comp.lang.ada
 help / color / mirror / Atom feed
* Creating several types from a base type and conversion
@ 2020-01-18  7:32 Ken Roberts
  2020-01-18 12:16 ` Simon Wright
                   ` (5 more replies)
  0 siblings, 6 replies; 49+ messages in thread
From: Ken Roberts @ 2020-01-18  7:32 UTC (permalink / raw)


New to ada with a question about types.

The theory is creating an old computer emulator. Memory is an array of BaseWord's (Older computer had 32K of memory, so making an array on a modern computer is peanuts).

Creating a base type that is stored into the memory array

<code>

    type BaseWord is array (00 .. 29) of Boolean;
    pragma pack (BaseWord);

</code>

Creating derived type(s) that can be returned from the memory section

<code>

    type DataWord is new BaseWord with record
        Upper : array 00 .. 8#77777# of Boolean;
        Lower : array 00 .. 8#77777# of Boolean;
    end record;

    for DataWord use record
        for Upper use 15 .. 29;
        for Lower use 00 .. 14;
    end record;
    pragma pack (DataWord);

    type InstructionWord is new BaseWord with record;
        (define field parameters)
    end record;
    for InstructionWord use record
        (define BaseWord fields bit locations)
    end record;

</code>

Defining how characters for display are packed into memory

<code>
    type CharByte is array (0 .. 5) of Boolean;  -- Character define
    type CharWord is record
        CharArray : array (0 .. 4) of CharByte;
    end record;
    for CharWord use record
        for CharArray (0) use 24 .. 29;
        for CharArray (1) use 18 .. 23;
        for CharArray (2) use 12 .. 17;
        for CharArray (3) use 6 .. 11;
        for CharArray (4) use 0 .. 5;
    end record;

</code>

So the next question is how to convert between CharWord/BaseWord?

For most stuff, memory will be returning either DataWord or InstructionWord for each memory access, but I'm also looking at an easier way to manage characters for text display on the emulated monitors.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-18  7:32 Creating several types from a base type and conversion Ken Roberts
@ 2020-01-18 12:16 ` Simon Wright
  2020-01-18 12:49   ` Ken Roberts
  2020-01-18 14:17   ` Optikos
  2020-01-18 17:57 ` Niklas Holsti
                   ` (4 subsequent siblings)
  5 siblings, 2 replies; 49+ messages in thread
From: Simon Wright @ 2020-01-18 12:16 UTC (permalink / raw)


Ken Roberts <alisonken1@gmail.com> writes:

> So the next question is how to convert between CharWord/BaseWord?

Possibly using Ada.Unchecked_Conversion. Hidden inside a function with a
helpful-in-six-months name.

It takes very little time to compile code nowadays, I'd strongly suggest
you start with the basics (e.g. BaseWord) and build up from there,
checking as you go (there are a _lot_ of syntax errors in your example
code).

You can't say

    type DataWord is new BaseWord with record
        Upper : array 00 .. 8#77777# of Boolean;
        Lower : array 00 .. 8#77777# of Boolean;
    end record;

because BaseWord isn't a tagged type, and even if it was you don't want
to add extra information, you're looking for an alternative view.
And that's not the syntax for an array declaration.
And you can't declare an anonymous array in a record.
And that's two large arrays (I think you're aiming for representations).

----------

I was wondering whether an Unchecked Union (ARM B.3.3[1]) might serve,
but (30) says "The use of an unchecked union to obtain the effect of an
unchecked conversion results in erroneous execution".

[1] http://www.ada-auth.org/standards/rm12_w_tc1/html/RM-B-3-3.html

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-18 12:16 ` Simon Wright
@ 2020-01-18 12:49   ` Ken Roberts
  2020-01-18 14:56     ` Bill Findlay
                       ` (3 more replies)
  2020-01-18 14:17   ` Optikos
  1 sibling, 4 replies; 49+ messages in thread
From: Ken Roberts @ 2020-01-18 12:49 UTC (permalink / raw)


On Saturday, January 18, 2020 at 4:16:20 AM UTC-8, Simon Wright wrote:
> Ken Roberts <snip> writes:
> 
> > So the next question is how to convert between CharWord/BaseWord?
> 
> Possibly using Ada.Unchecked_Conversion. Hidden inside a function with a
> helpful-in-six-months name.
> 
> It takes very little time to compile code nowadays, I'd strongly suggest
> you start with the basics (e.g. BaseWord) and build up from there,
> checking as you go (there are a _lot_ of syntax errors in your example
> code).
> 
> You can't say
> 
>     type DataWord is new BaseWord with record
>         Upper : array 00 .. 8#77777# of Boolean;
>         Lower : array 00 .. 8#77777# of Boolean;
>     end record;
> 
> because BaseWord isn't a tagged type, and even if it was you don't want
> to add extra information, you're looking for an alternative view.
> And that's not the syntax for an array declaration.
> And you can't declare an anonymous array in a record.
> And that's two large arrays (I think you're aiming for representations).
> 
> ----------
> 
> I was wondering whether an Unchecked Union (ARM B.3.3[1]) might serve,
> but (30) says "The use of an unchecked union to obtain the effect of an
> unchecked conversion results in erroneous execution".
> 
> [1] http://www.ada-auth.org/standards/rm12_w_tc1/html/RM-B-3-3.html

Don't know about the syntax errors - gnatmake seems to like it. But
then, the example is in a package spec with no body yet. Still working
on the basics first. Just now getting serious about Ada, I'm sure I'll
run into a lot of learning curve on these little details.

The concept is emulating a 30-bit computer from olden days.

It was my understanding that a boolean array would be better than an
integer in order to do some of the bit manipulations that the old
computer was designed for.

One example:

ADD LP : L[Y*(Q)]+(A) -> A

Take the logical product of Y and Q register, then add A register,
place results in A register.

(LP being boolean AND of 2 registers)

I think I tried doing tagged records and subtypes, but kept getting
errors like 'Bits already mapped' when trying to extend the BaseWord
(30 bit) into a data word ( 2 separate 15-bit fields) and instruction
word (5 separate bit-mapped fields) while still being able to easily
convert between BaseWord and others (think pulling next instruction from memory array, then pulling data from arbitrary location in memory array).

Functionally, it would be relatively easy to just ignore the hardware
aspect of the emulation, but I'm trying to set it up so I can emulate
the hardware later as a learning tool to how this old computer
actually did things (like 1's complement subtractive addition). The
real fun will be programming the timing (1MHz clock split into 4
phases, with interesting interrupt handling).

I know - _very_ ambitious project for a beginner in the language (not
to programming), but I figure might as well have an interesting
project to work on while learning rather than the basic "Hello World"
style that seems to be prevalent.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-18 12:16 ` Simon Wright
  2020-01-18 12:49   ` Ken Roberts
@ 2020-01-18 14:17   ` Optikos
  1 sibling, 0 replies; 49+ messages in thread
From: Optikos @ 2020-01-18 14:17 UTC (permalink / raw)


On Saturday, January 18, 2020 at 6:16:20 AM UTC-6, Simon Wright wrote:
> Ken Roberts writes:
> 
> > So the next question is how to convert between CharWord/BaseWord?
> 
> Possibly using Ada.Unchecked_Conversion. Hidden inside a function with a
> helpful-in-six-months name.
> 
> It takes very little time to compile code nowadays, I'd strongly suggest
> you start with the basics (e.g. BaseWord) and build up from there,
> checking as you go (there are a _lot_ of syntax errors in your example
> code).
> 
> You can't say
> 
>     type DataWord is new BaseWord with record
>         Upper : array 00 .. 8#77777# of Boolean;
>         Lower : array 00 .. 8#77777# of Boolean;
>     end record;
> 
> because BaseWord isn't a tagged type, and even if it was you don't want
> to add extra information, you're looking for an alternative view.
> And that's not the syntax for an array declaration.
> And you can't declare an anonymous array in a record.
> And that's two large arrays (I think you're aiming for representations).
> 
> ----------
> 
> I was wondering whether an Unchecked Union (ARM B.3.3[1]) might serve,
> but (30) says "The use of an unchecked union to obtain the effect of an
> unchecked conversion results in erroneous execution".
> 
> [1] http://www.ada-auth.org/standards/rm12_w_tc1/html/RM-B-3-3.html

The Unchecked_ series are better focused on the situation that BaseWord, DataWord, InstructionWord, and CharWord are originating in another language, say, C, but that is ostensibly not the case here.

More proper in Ada, would be to have multiple conversion functions that take the already-extant FooWord as a parameter and return an extracted/converted BarWord as return value (or a procedure that takes the already-extant FooWord as an in parameter and the to-be-extracted/to-be-converted BarWord as an out parameter).  Then that conversion/extraction subroutine is merely performing garden-variety safe/not-unchecked reading of FooWord and is merely performing garden-variety safe/not-unchecked writing of BarWord.

You would have a plethora of such FooWord to BarWord conversion/extraction subroutines—one for each origin-to-destination pairing in your app-domain, such as {ExtractCharWordFromDataWord, EmbedCharWordIntoDataWord, ExtractDataWordFromInstructionWordOpcode, EmbedDataWordIntoInstructionWordOpcode, ExtractDataWordFromInstructionWordImmediate, EmbedDataWordIntoInstructionWordImmediate, …} (assuming that immediates in the machine code are located at the same offset-from-beginning-of-instruction in all machine instructions, otherwise ExtractDataWordFromImmediateForSuchandsuchOpcode and EmbedDataWordIntoImmediateForSuchandsuchOpcode for each different format of suchandsuch opcodes in the instruction-set) and so forth.

In modern Ada, for efficiency and perhaps succinct/readable beauty, you might want to consider the extended return statement if choosing the design above that is based on functions (instead of the out parameter to a procedure, which is available in all eras of Ada, but perhaps naturally leading to slightly more verbose/less-lucid invocations at points of usage, due to being statements instead of expressions).

https://www.adaic.org/resources/add_content/standards/05rm/html/RM-6-5.html#S0170


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-18 12:49   ` Ken Roberts
@ 2020-01-18 14:56     ` Bill Findlay
  2020-01-18 16:13       ` Jeffrey R. Carter
  2020-01-18 22:20       ` Ken Roberts
  2020-01-18 15:09     ` Simon Wright
                       ` (2 subsequent siblings)
  3 siblings, 2 replies; 49+ messages in thread
From: Bill Findlay @ 2020-01-18 14:56 UTC (permalink / raw)


On 18 Jan 2020, Ken Roberts wrote
(in article<4b0649b3-aed2-44fd-822f-d6665b9352dd@googlegroups.com>):

> The concept is emulating a 30-bit computer from olden days.
>
> It was my understanding that a boolean array would be better than an
> integer in order to do some of the bit manipulations that the old
> computer was designed for.

Use the type:

mod 2**30

which provides all the bit handling you need.
Override the arithmetic operations with your own 1's complement versions.
Roberto es su tio.

For an Ada 2012 emulator of a 48-bit machine, see:

<http://www.findlayw.plus.com/KDF9/emulation/emulator.html>

-- 
Bill Findlay


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-18 12:49   ` Ken Roberts
  2020-01-18 14:56     ` Bill Findlay
@ 2020-01-18 15:09     ` Simon Wright
  2020-01-18 22:16       ` Ken Roberts
  2020-01-18 15:47     ` Simon Wright
  2020-01-21 21:35     ` Shark8
  3 siblings, 1 reply; 49+ messages in thread
From: Simon Wright @ 2020-01-18 15:09 UTC (permalink / raw)


Ken Roberts <alisonken1@gmail.com> writes:

> Don't know about the syntax errors - gnatmake seems to like it.

Then the code you're compiling isn't the code you're showing to us!

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-18 12:49   ` Ken Roberts
  2020-01-18 14:56     ` Bill Findlay
  2020-01-18 15:09     ` Simon Wright
@ 2020-01-18 15:47     ` Simon Wright
  2020-01-21 21:35     ` Shark8
  3 siblings, 0 replies; 49+ messages in thread
From: Simon Wright @ 2020-01-18 15:47 UTC (permalink / raw)


Ken Roberts <alisonken1@gmail.com> writes:

> I know - _very_ ambitious project for a beginner in the language (not
> to programming), but I figure might as well have an interesting
> project to work on while learning rather than the basic "Hello World"
> style that seems to be prevalent.

Good stuff!

My first processor emulator was in VAX Fortran, later migrated to
C. There were Ada and C++ bindings for adding peripherals as
required. Someone used the C++ bindings & threads to emulate a
CRT/lightpen on an X Windows touchscreen. Occasional threading problems,
surprise :-)

Nowadays I'd start from Ada, obvs

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-18 14:56     ` Bill Findlay
@ 2020-01-18 16:13       ` Jeffrey R. Carter
  2020-01-18 18:20         ` Bill Findlay
  2020-01-18 22:20       ` Ken Roberts
  1 sibling, 1 reply; 49+ messages in thread
From: Jeffrey R. Carter @ 2020-01-18 16:13 UTC (permalink / raw)


On 1/18/20 3:56 PM, Bill Findlay wrote:
> 
> Use the type:
> 
> mod 2**30
> 
> which provides all the bit handling you need.

Except for shifts and rotations, which most instruction sets provide. Shifts can 
be replaced by multiplication and division by powers of two. Rotations are a 
little more complex.

-- 
Jeff Carter
"He didn't get that nose from playing ping-pong."
Never Give a Sucker an Even Break
110

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-18  7:32 Creating several types from a base type and conversion Ken Roberts
  2020-01-18 12:16 ` Simon Wright
@ 2020-01-18 17:57 ` Niklas Holsti
  2020-01-18 22:59   ` Ken Roberts
  2020-01-19  3:37 ` Ken Roberts
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 49+ messages in thread
From: Niklas Holsti @ 2020-01-18 17:57 UTC (permalink / raw)


On 2020-01-18 9:32, Ken Roberts wrote:
> New to ada with a question about types.

Welcome to the language!

What did you program in before? Sometimes it is easier to give advice if 
one knows the background of the person asking for it.

> The theory is creating an old computer emulator. Memory is an array
> of BaseWord's (Older computer had 32K of memory, so making an array
> on a modern computer is peanuts).

Yes, but note that in some Ada compilers, the predefined Integer is only 
16 bits, so some memory address computations (offsets) in a 32K range 
might overflow... better define your own "memory address" type with the 
computational range you need (which may be more than 32K, or should 
perhaps use modular arithmetic).

> Creating a base type that is stored into the memory array
> 
> <code>
> 
>      type BaseWord is array (00 .. 29) of Boolean;
>      pragma pack (BaseWord);
> 
> </code>

Note that such a definition does not ensure that the Boolean with index 
0 is the least significant bit, or the most significant bit -- different 
compilers can use different conventions. And there is no way in current 
Ada to specify the indexing order.

I think the first thing you should consider, and decide, is whether you 
want your emulator to be fully portable to all standard Ada 
implementations, or to the commonly available Ada compilers. The main 
issues are the endianness and the word length of the computer you want 
to emulate:

- Can the computer to be emulated address only full 30-bit words, or can 
it address also, say, characters (bytes) within a word?

- Is the computer you intend to emulate big-endian or little-endian? 
There are two aspects to this question: first, conventions for numbering 
bits (is bit 0 most significant or least significant?) and second, 
addressing sub-word-units like characters (if possible) within a word 
(does the lowest sub-word address access the most significant end of the 
addressed word, or the least-significant end?).

I'll sketch an approach that applies to the commonly available Ada 
compilers and makes no a-priori assumptions on the endianness of the 
computer to be emulated.

I recommend starting from the type Interfaces.Unsigned_32. This is a 
32-bit, unsigned, modular integer type, and has predefined shift and 
rotate operations as well as bit-wise Boolean operations. The type is 
not required to exist in all Ada compilers (for example, the computer 
you are emulating would probably instead have had a type 
Interfaces.Unsigned_30) but does exist in GNAT and other Ada compilers 
for most modern computers which tend to have 32-bit machine integers.

The 30-bit unsigned integer type is then

    subtype Base_Word is Interfaces.Unsigned_32 range 0 .. 2**30 - 1;

and the emulated memory is an array of 2**15 Base_Word elements, indexed 
by the memory-address type.

Note that Base_Word does not wrap around at 30 bits (as the type "mod 
2**30" would) but at 32 bits. But you can emulate 30-bit wrap-around by 
clearing the two high bits after every arithmetic operation.

I would then define a function to extract any desired bit-field from a 
Base_Word, for example

    function Field (Word : Base_Word; Low_Bit, High_Bit : Bit_Number)
    return Base_Word

where Bit_Number is range 0 .. 29.

The easiest way to implement such a function is by the left-shift and 
right-shift operations for Base_Word. In this implementation, you can 
decide if you want Bit_Number to work in a little-endian or big-endian way.

I would also define a procedure Set_Field similarly:

    procedure Set_Field (
       Word              : in out Base_Word;
       Low_Bit, High_Bit : in     Bit_Number;
       New_Value         : in     Base_Word);

Again, this can be done with the shift operations (or rotate operations) 
and some Boolean operations on Base_Word.

If the computer to be emulated can access sub-word data, for example 
bytes with a byte address, you can now use the above subprograms to 
implement operations to read or write such sub-word data, and again you 
can implement either little-endian sub-word order or big-endian sub-word 
order.

These tools should let you decode instructions and their bit-fields and 
emulate all "logical" (Boolean) instructions and load/store instructions.

If you prefer to implement the bit-field lay-outs of instruction words 
by means of record representation clauses, that is fine too. However, 
note that the endianness of the bit numbering in such clauses may be 
different in different Ada compilers, so you should also use the 
Bit_Order aspect, together with the representation clause, to define the 
order you want to use (which is probably the same as in the 
instruction-set manuals for the emulated computer). Also, you then have 
to use Unchecked_Conversion to convert between Base_Word and the 
instruction record types, but that is good and safe as long as you have 
taken care of the bit order.

For the arithmetic instructions, you must of course first consider how 
the emulated computer represents negative numbers: two's complement, 
one's complement, or sign-magnitude?

The answer will tell you how to convert, using ordinary type conversions 
and ordinary arithmetic (not Unchecked_Conversion) from Base_Word to the 
corresponding signed type (for use as an operand in the emulation of an 
arithmetic instruction) and from the signed type (as the result of the 
arithmetic instruction) back to Base_Type for storing in an emulated 
register or in the emulated memory.

Remember that you may have to use wider types for the results of 
arithmetic operations so as to handle and detect overflows. Unsigned_32 
already gives you two more high-end bits, so it can be used for addition 
and subtraction, but for multiplication you may want to use a 64-bit 
type, for example.

(I agree with others that there are some errors in the declarations you 
gave (elided here) which should have made GNAT reject the code.)
  > For most stuff, memory will be returning either DataWord or
> InstructionWord for each memory access, but I'm also looking at an 
> easier way to manage characters for text display on the emulated 
> monitors.
I would assume that in the original system, the diplay of characters on 
the monitors was implemented in the SW programs that ran on the emulated 
processor. Why should the processor emulator do something special for 
this? Did the monitors display data directly from "display buffers" in 
the 32K memory, using DMA?

-- 
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
       .      @       .

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-18 16:13       ` Jeffrey R. Carter
@ 2020-01-18 18:20         ` Bill Findlay
  2020-01-18 18:32           ` Jeffrey R. Carter
  0 siblings, 1 reply; 49+ messages in thread
From: Bill Findlay @ 2020-01-18 18:20 UTC (permalink / raw)


On 18 Jan 2020, Jeffrey R. Carter wrote (in 
article<qvvaqs$k9p$1@dont-email.me>):

> On 1/18/20 3:56 PM, Bill Findlay wrote:
> >
> > Use the type:
> >
> > mod 2**30
> >
> > which provides all the bit handling you need.
>
> Except for shifts and rotations, which most instruction sets provide.
> Shifts can be replaced by multiplication and division by powers of two.
> Rotations are a little more complex.

Or, using GNAT, e.g.:

type word is mod 2**30;

subtype word_shift_length is Natural range 0..30;

function shift_word_left (W : word; amount : word_shift_length)
return KDF9.word;

function shift_word_right (W : word; amount : word_shift_length)
return KDF9.word;

function rotate_word_left (W : word; amount : word_shift_length)
return KDF9.word;

function rotate_word_right (W : word; amount : word_shift_length)
return KDF9.word;

All done.

-- 
Bill Findlay



^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-18 18:20         ` Bill Findlay
@ 2020-01-18 18:32           ` Jeffrey R. Carter
  2020-01-18 20:34             ` Simon Wright
  0 siblings, 1 reply; 49+ messages in thread
From: Jeffrey R. Carter @ 2020-01-18 18:32 UTC (permalink / raw)


On 1/18/20 7:20 PM, Bill Findlay wrote:
> 
> Or, using GNAT, e.g.:
> 
> type word is mod 2**30;
> 
> subtype word_shift_length is Natural range 0..30;
> 
> function shift_word_left (W : word; amount : word_shift_length)
> return KDF9.word;
> 
> function shift_word_right (W : word; amount : word_shift_length)
> return KDF9.word;
> 
> function rotate_word_left (W : word; amount : word_shift_length)
> return KDF9.word;
> 
> function rotate_word_right (W : word; amount : word_shift_length)
> return KDF9.word;
> 
> All done.

I don't know what KDF9.Word is, but surely you have to implement these. Or are 
you saying GNAT magically implements them?

-- 
Jeff Carter
"He didn't get that nose from playing ping-pong."
Never Give a Sucker an Even Break
110

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-18 18:32           ` Jeffrey R. Carter
@ 2020-01-18 20:34             ` Simon Wright
  2020-01-20 16:38               ` Bill Findlay
  0 siblings, 1 reply; 49+ messages in thread
From: Simon Wright @ 2020-01-18 20:34 UTC (permalink / raw)


"Jeffrey R. Carter" <spam.jrcarter.not@spam.not.acm.org> writes:

> On 1/18/20 7:20 PM, Bill Findlay wrote:

>> type word is mod 2**30;
>>
>> subtype word_shift_length is Natural range 0..30;
>>
>> function shift_word_left (W : word; amount : word_shift_length)
>> return KDF9.word;

> I don't know what KDF9.Word is, but surely you have to implement
> these. Or are you saying GNAT magically implements them?

It doesn't; 'with Import, convention => Intrinsic' requires the first
argument to have size 8, 16, 32, or 64, and the second argument must be
Natural (not a subtype of it, even if unconstrained).

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-18 15:09     ` Simon Wright
@ 2020-01-18 22:16       ` Ken Roberts
  2020-01-18 22:35         ` Simon Wright
  2020-01-19  0:07         ` Niklas Holsti
  0 siblings, 2 replies; 49+ messages in thread
From: Ken Roberts @ 2020-01-18 22:16 UTC (permalink / raw)


On Saturday, January 18, 2020 at 7:10:00 AM UTC-8, Simon Wright wrote:
> Ken Roberts <snip> writes:
> 
> > Don't know about the syntax errors - gnatmake seems to like it.
> 
> Then the code you're compiling isn't the code you're showing to us!


ken/ntds/usq20/playground $ gnatmake core
gcc -c core.ads
ken/ntds/usq20/playground $ ls -al
drwxrwxr-x. 2 ken ken 4096 Jan 18 14:10 .
drwxrwxr-x. 8 ken ken 4096 Jan 16 03:34 ..
-rw-rw-r--. 1 ken ken 1201 Jan 16 03:49 core.ads
-rw-rw-r--. 1 ken ken  280 Jan 18 14:10 core.ali
-rw-rw-r--. 1 ken ken  968 Jan 18 14:10 core.o
ken/ntds/usq20/playground $ 

<code>

package core is

    --
    -- 30-bit word size (CP642/A/B)
    --      Data : 0 .. 2**29
    -- Half-word
    --      Upper : at 0 range 15 .. 29
    --      Lower : at 0 range 00 .. 14
    -- Instruction word (normal)
    --      f   : at 0 range 24 .. 29   Op code (instruction)
    --      j   : at 0 range 21 .. 23   Skip condition
    --      k   : at 0 range 18 .. 20   Read/Write/Replace type
    --      b   : at 0 ragne 15 .. 17   Index register
    --      y   : at 0 range 00 .. 14   Operand/constant
    -- Instruction word (I/O) (f => 13 | 17 | 62 | 63 | 66 | 67 | 73 .. 76
    --      f   : at 0 range 24 .. 29   Op code (instruction)
    --      j^  : at 0 range 20 .. 23   I/O channel (C^n)
    --      k^  : at 0 range 18 .. 19   Buffer control address modifier
    --      b   : at 0 range 15 .. 17   Index register
    --      y   : at 0 range 00 .. 14   Operand/buffer address

    WORD_BITS       : constant := 30;
    WORD_MASK       : constant := 8#77777_77777#;
    HALF_WORD_BITS  : constant := 15;
    HALF_WORD_MASK  : constant := 8#77777#;
    CHAR_BITS       : constant := 6;
    CHAR_MASK       : constant := 8#77#;  -- Used for pulling out 6-bit character code from full word

    type BaseWord is array (0 .. (WORD_BITS - 1)) of Boolean;

    -- Although we use constants here for definition, use hard numbers for
    -- record components for now
    type DataWord is new BaseWord with record
        Upper : array (0 .. (HALF_WORD_BITS - 1)) of Boolean;
        Lower : array (0 .. (HALF_WORD_BITS - 1)) of Boolean;
    end record;
    for DataWord use record
        for Upper use HALF_WORD_BITS .. (WORD_BITS - 1);
        for Lower use 00 .. (HALF_WORD_BITS - 1);
    end record;
    pragma pack (DataWord);

    -- Type 1 instructions are normal instructions
    type Instruction1 is new BaseWord with record
        F : array 0 .. 8#77# of Boolean;
        J : array 0 .. 8#7# of Boolean;
        K : array 0 .. 8#7# of Boolean;
        B : array 0 .. 8#7# of Boolean;
        Y : array 0 .. 8#77777# of Boolean;
    end record;
    for Instruction1 use record
        for F use 24 .. 29;
        for J use 21 .. 23;
        for K use 18 .. 20;
        for B use 15 .. 17;
        for Y use 00 ,, 14;
    end record;
    pragma pack (Instruction1);

    -- Type 2 instructions are I/O related instructions - J/K fields become J^/K^ fields
    -- where J^ steals one bit from K^ to specify in/out channel
    type Instruction2 is new BaseWord with record
        F : array 0 .. 8#77# of Boolean;
        J : array 0 .. 8#17# of Boolean;
        K : array 0 .. 8#3# of Boolean;
        B : array 0 .. 8#7# of Boolean;
        Y : array 0 .. 8#77777# of Boolean;
    end record;
    for Instruction2 use record
        for F use 24 .. 29;
        for J use 20 .. 23;
        for K use 18 .. 19;
        for B use 15 .. 17;
        for Y use 00 .. 14;
    end record;
    pragma pack (Instruction2);


    type CharByte is array (0 .. 5) of Boolean;  -- Character define
    type CharWord is record
        CharArray : array (0 .. 4) of CharByte;
    end record;
    for CharWord use record
        for CharArray (0) use 24 .. 29;
        for CharArray (1) use 18 .. 23;
        for CharArray (2) use 12 .. 17;
        for CharArray (3) use 6 .. 11;
        for CharArray (4) use 0 .. 5;
    end record;
    pragma pack (CharWord)

end core;

</code>


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-18 14:56     ` Bill Findlay
  2020-01-18 16:13       ` Jeffrey R. Carter
@ 2020-01-18 22:20       ` Ken Roberts
  1 sibling, 0 replies; 49+ messages in thread
From: Ken Roberts @ 2020-01-18 22:20 UTC (permalink / raw)


On Saturday, January 18, 2020 at 6:56:24 AM UTC-8, Bill Findlay wrote:
> On 18 Jan 2020, Ken Roberts wrote
> (in article<4b0649b3-aed2-44fd-822f-d6665b9352dd@googlegroups.com>):
> 
> > The concept is emulating a 30-bit computer from olden days.
> >
> > It was my understanding that a boolean array would be better than an
> > integer in order to do some of the bit manipulations that the old
> > computer was designed for.
> 
> Use the type:
> 
> mod 2**30
> 
> which provides all the bit handling you need.
> Override the arithmetic operations with your own 1's complement versions.
> Roberto es su tio.
> 
> For an Ada 2012 emulator of a 48-bit machine, see:
> 
> <http://www.findlayw.plus.com/KDF9/emulation/emulator.html>
> 
> -- 
> Bill Findlay

Thanks for the link - will look into it.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-18 22:16       ` Ken Roberts
@ 2020-01-18 22:35         ` Simon Wright
  2020-01-18 23:03           ` Ken Roberts
  2020-01-19  0:07         ` Niklas Holsti
  1 sibling, 1 reply; 49+ messages in thread
From: Simon Wright @ 2020-01-18 22:35 UTC (permalink / raw)


Ken Roberts <alisonken1@gmail.com> writes:

> On Saturday, January 18, 2020 at 7:10:00 AM UTC-8, Simon Wright wrote:
>> Ken Roberts <snip> writes:
>> 
>> > Don't know about the syntax errors - gnatmake seems to like it.
>> 
>> Then the code you're compiling isn't the code you're showing to us!
>
>
> ken/ntds/usq20/playground $ gnatmake core
> gcc -c core.ads
> ken/ntds/usq20/playground $ ls -al
> drwxrwxr-x. 2 ken ken 4096 Jan 18 14:10 .
> drwxrwxr-x. 8 ken ken 4096 Jan 16 03:34 ..
> -rw-rw-r--. 1 ken ken 1201 Jan 16 03:49 core.ads
> -rw-rw-r--. 1 ken ken  280 Jan 18 14:10 core.ali
> -rw-rw-r--. 1 ken ken  968 Jan 18 14:10 core.o
> ken/ntds/usq20/playground 

Well, the core.ads you showed is 3 times bigger!!!!

lockheed:cla simon$ ls -l core.*
-rw-r--r--  1 simon  staff  3377 18 Jan 22:23 core.ads
lockheed:cla simon$ gnatmake core.ads
gcc -c core.ads
core.ads:34:17: anonymous arrays not allowed as components
core.ads:35:17: anonymous arrays not allowed as components
core.ads:37:28: missing ";"
core.ads:38:09: missing "end record;" for "record" at line 37
core.ads:39:23: invalid representation clause
core.ads:40:05: no "record" for this "end record"
core.ads:45:13: anonymous arrays not allowed as components
core.ads:46:13: anonymous arrays not allowed as components
core.ads:47:13: anonymous arrays not allowed as components
core.ads:48:13: anonymous arrays not allowed as components
core.ads:49:13: anonymous arrays not allowed as components
core.ads:51:32: missing ";"
core.ads:52:09: missing "end record;" for "record" at line 51
core.ads:53:19: invalid representation clause
core.ads:54:19: invalid representation clause
core.ads:55:19: invalid representation clause
core.ads:56:19: invalid representation clause
core.ads:57:05: no "record" for this "end record"
core.ads:63:13: anonymous arrays not allowed as components
core.ads:64:13: anonymous arrays not allowed as components
core.ads:65:13: anonymous arrays not allowed as components
core.ads:66:13: anonymous arrays not allowed as components
core.ads:67:13: anonymous arrays not allowed as components
core.ads:69:32: missing ";"
core.ads:70:09: missing "end record;" for "record" at line 69
core.ads:71:19: invalid representation clause
core.ads:72:19: invalid representation clause
core.ads:73:19: invalid representation clause
core.ads:74:19: invalid representation clause
core.ads:75:05: no "record" for this "end record"
core.ads:81:21: anonymous arrays not allowed as components
core.ads:83:28: missing ";"
core.ads:84:09: missing "end record;" for "record" at line 83
core.ads:85:23: missing "use"
core.ads:86:23: missing "use"
core.ads:87:23: missing "use"
core.ads:88:23: missing "use"
core.ads:89:05: no "record" for this "end record"
core.ads:90:27: missing ";"
gnatmake: "core.ads" compilation error
lockheed:cla simon$ 

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-18 17:57 ` Niklas Holsti
@ 2020-01-18 22:59   ` Ken Roberts
  2020-01-19  0:30     ` Niklas Holsti
  0 siblings, 1 reply; 49+ messages in thread
From: Ken Roberts @ 2020-01-18 22:59 UTC (permalink / raw)


On Saturday, January 18, 2020 at 9:57:08 AM UTC-8, Niklas Holsti wrote:
> On 2020-01-18 9:32, Ken Roberts wrote:
> > New to ada with a question about types.
> 
> Welcome to the language!
> 
> What did you program in before? Sometimes it is easier to give advice if 
> one knows the background of the person asking for it.

I touched on C way back when (sometime in the 80's) but my day job was not programming and didn't really dig into it.

For the last few years I've been programming Python (again, hobby, not day job) working on a PJLink control for network-connected projectors in an open source church project.

> > The theory is creating an old computer emulator. Memory is an array
> > of BaseWord's (Older computer had 32K of memory, so making an array
> > on a modern computer is peanuts).
> 
> Yes, but note that in some Ada compilers, the predefined Integer is only 
> 16 bits, so some memory address computations (offsets) in a 32K range 
> might overflow... better define your own "memory address" type with the 
> computational range you need (which may be more than 32K, or should 
> perhaps use modular arithmetic).
> 
> > Creating a base type that is stored into the memory array
> > 
> > <code>
> > 
> >      type BaseWord is array (00 .. 29) of Boolean;
> >      pragma pack (BaseWord);
> > 
> > </code>
> 
> Note that such a definition does not ensure that the Boolean with index 
> 0 is the least significant bit, or the most significant bit -- different 
> compilers can use different conventions. And there is no way in current 
> Ada to specify the indexing order.
> 
> I think the first thing you should consider, and decide, is whether you 
> want your emulator to be fully portable to all standard Ada 
> implementations, or to the commonly available Ada compilers. The main 
> issues are the endianness and the word length of the computer you want 
> to emulate:
> 
> - Can the computer to be emulated address only full 30-bit words, or can 
> it address also, say, characters (bytes) within a word?

The computer is 30-bit and the memory is 30-bit magnetic core. The memory control can return either a 30-bit word or 15-bit half-word from upper or lower half of memory address depending on one of the instruction modifiers.

> 
> - Is the computer you intend to emulate big-endian or little-endian? 
> There are two aspects to this question: first, conventions for numbering 
> bits (is bit 0 most significant or least significant?) and second, 
> addressing sub-word-units like characters (if possible) within a word 
> (does the lowest sub-word address access the most significant end of the 
> addressed word, or the least-significant end?).

The memory/computer model is 30-bit words both in computer and memory. No endianness.

32K of directly addressable internal memory. Depending on a modifier field in the instruction word, either a 30-bit word or 15-bit half word (from either upper 15 bits or lower 15 bits) was returned from memory for operatnds.

The addition of 144K of extended memory was provided by an external memory unit approximately the same physical size, and the memory control chassis on the computer was modified at a later date to allow access. Unfortunately, I can't find the documentation on how the external memory was accessed, so I'll have to guess until I can find out.

The computer was actually built before semiconductors became the norm, so the whole computer was built using discreet components on 15-pin circuit cards.


> 
> I'll sketch an approach that applies to the commonly available Ada 
> compilers and makes no a-priori assumptions on the endianness of the 
> computer to be emulated.
> 
> I recommend starting from the type Interfaces.Unsigned_32. This is a 
> 32-bit, unsigned, modular integer type, and has predefined shift and 
> rotate operations as well as bit-wise Boolean operations. The type is 
> not required to exist in all Ada compilers (for example, the computer 
> you are emulating would probably instead have had a type 
> Interfaces.Unsigned_30) but does exist in GNAT and other Ada compilers 
> for most modern computers which tend to have 32-bit machine integers.
> 
> The 30-bit unsigned integer type is then
> 
>     subtype Base_Word is Interfaces.Unsigned_32 range 0 .. 2**30 - 1;
> 
> and the emulated memory is an array of 2**15 Base_Word elements, indexed 
> by the memory-address type.
> 
> Note that Base_Word does not wrap around at 30 bits (as the type "mod 
> 2**30" would) but at 32 bits. But you can emulate 30-bit wrap-around by 
> clearing the two high bits after every arithmetic operation.
> 
> I would then define a function to extract any desired bit-field from a 
> Base_Word, for example
> 
>     function Field (Word : Base_Word; Low_Bit, High_Bit : Bit_Number)
>     return Base_Word
> 
> where Bit_Number is range 0 .. 29.
> 
> The easiest way to implement such a function is by the left-shift and 
> right-shift operations for Base_Word. In this implementation, you can 
> decide if you want Bit_Number to work in a little-endian or big-endian way.
> 
> I would also define a procedure Set_Field similarly:
> 
>     procedure Set_Field (
>        Word              : in out Base_Word;
>        Low_Bit, High_Bit : in     Bit_Number;
>        New_Value         : in     Base_Word);
> 
> Again, this can be done with the shift operations (or rotate operations) 
> and some Boolean operations on Base_Word.
> 
> If the computer to be emulated can access sub-word data, for example 
> bytes with a byte address, you can now use the above subprograms to 
> implement operations to read or write such sub-word data, and again you 
> can implement either little-endian sub-word order or big-endian sub-word 
> order.
> 

As noted above, this was before semiconductors and bytes became a thing.

The only memory operations were 30-bit word and upper/lower 15-bit half-word get/put operations.

Program accessible registers were 30-bit (with one exception - some operations treated 2 registers as one 60-bit register, but that was for a few selected math functions).

> These tools should let you decode instructions and their bit-fields and 
> emulate all "logical" (Boolean) instructions and load/store instructions.
> 
> If you prefer to implement the bit-field lay-outs of instruction words 
> by means of record representation clauses, that is fine too. However, 
> note that the endianness of the bit numbering in such clauses may be 
> different in different Ada compilers, so you should also use the 
> Bit_Order aspect, together with the representation clause, to define the 
> order you want to use (which is probably the same as in the 
> instruction-set manuals for the emulated computer). Also, you then have 
> to use Unchecked_Conversion to convert between Base_Word and the 
> instruction record types, but that is good and safe as long as you have 
> taken care of the bit order.
> 

See reply to Simon about my program compile operations - it included the field breakdown notes.

> For the arithmetic instructions, you must of course first consider how 
> the emulated computer represents negative numbers: two's complement, 
> one's complement, or sign-magnitude?
> 

The computer is 1's complement subtractive addition, and some instruction modifiers allow for circular shift, shift with sign, and sign extension (for half word operations).


> The answer will tell you how to convert, using ordinary type conversions 
> and ordinary arithmetic (not Unchecked_Conversion) from Base_Word to the 
> corresponding signed type (for use as an operand in the emulation of an 
> arithmetic instruction) and from the signed type (as the result of the 
> arithmetic instruction) back to Base_Type for storing in an emulated 
> register or in the emulated memory.
> 
> Remember that you may have to use wider types for the results of 
> arithmetic operations so as to handle and detect overflows. Unsigned_32 
> already gives you two more high-end bits, so it can be used for addition 
> and subtraction, but for multiplication you may want to use a 64-bit 
> type, for example.

True - noted above there are a couple of multiply/divide instructions that treat the 30-bit A and Q registers as one 60-bit register. I was thinking about that and concluded that for those specific instructions it would be better to treat the 60-bit AQ register combination in a private variable since the final operation will still end up being two 30-bit registers.

> 
> (I agree with others that there are some errors in the declarations you 
> gave (elided here) which should have made GNAT reject the code.)
>   > For most stuff, memory will be returning either DataWord or
> > InstructionWord for each memory access, but I'm also looking at an 
> > easier way to manage characters for text display on the emulated 
> > monitors.
> I would assume that in the original system, the diplay of characters on 
> the monitors was implemented in the SW programs that ran on the emulated 
> processor. Why should the processor emulator do something special for 
> this? Did the monitors display data directly from "display buffers" in 
> the 32K memory, using DMA?

Nope. There was no "display" on the computer. The closest thing to a display was a bunch of neon buttons that reflected the contents of the registers on the front panel. The "display" part was either an old-school teletype machine (using 5-bit characters) or a CRT that was a combination radar repeater/computer symbology display unit with attached 15 line X 65 character text display using a really interesting 30-bit layout.

(reaaalllly long URL from google reposted via bit.ly)

http://bit.ly/368sGgl  Picture of computer I'm trying to emulate.

Worked on it for 15 years in the Navy.

> 
> -- 
> Niklas Holsti
> Tidorum Ltd
> niklas holsti tidorum fi
>        .      @       .


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-18 22:35         ` Simon Wright
@ 2020-01-18 23:03           ` Ken Roberts
  2020-01-18 23:38             ` Simon Wright
  0 siblings, 1 reply; 49+ messages in thread
From: Ken Roberts @ 2020-01-18 23:03 UTC (permalink / raw)


On Saturday, January 18, 2020 at 2:35:52 PM UTC-8, Simon Wright wrote:
> Ken Roberts <snip> writes:
> 
> > On Saturday, January 18, 2020 at 7:10:00 AM UTC-8, Simon Wright wrote:
> >> Ken Roberts <snip> writes:
> >> 
> >> > Don't know about the syntax errors - gnatmake seems to like it.
> >> 
> >> Then the code you're compiling isn't the code you're showing to us!
> >

<snip>

Interesting. Might be the difference in compilers then - I'm using the ada compiler collection available in the Fedora 30 branch.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-18 23:03           ` Ken Roberts
@ 2020-01-18 23:38             ` Simon Wright
  2020-01-19  0:12               ` Ken Roberts
  2020-01-19  0:33               ` Ken Roberts
  0 siblings, 2 replies; 49+ messages in thread
From: Simon Wright @ 2020-01-18 23:38 UTC (permalink / raw)


Ken Roberts <alisonken1@gmail.com> writes:

> On Saturday, January 18, 2020 at 2:35:52 PM UTC-8, Simon Wright wrote:
>> Ken Roberts <snip> writes:
>> 
>> > On Saturday, January 18, 2020 at 7:10:00 AM UTC-8, Simon Wright wrote:
>> >> Ken Roberts <snip> writes:
>> >> 
>> >> > Don't know about the syntax errors - gnatmake seems to like it.
>> >> 
>> >> Then the code you're compiling isn't the code you're showing to us!
>> >
>
> <snip>
>
> Interesting. Might be the difference in compilers then - I'm using the
> ada compiler collection available in the Fedora 30 branch.

This is what you posted

> ken/ntds/usq20/playground $ gnatmake core
> gcc -c core.ads
> ken/ntds/usq20/playground $ ls -al
> drwxrwxr-x. 2 ken ken 4096 Jan 18 14:10 .
> drwxrwxr-x. 8 ken ken 4096 Jan 16 03:34 ..
> -rw-rw-r--. 1 ken ken 1201 Jan 16 03:49 core.ads     <<<<<< 1201 bytes
> -rw-rw-r--. 1 ken ken  280 Jan 18 14:10 core.ali
> -rw-rw-r--. 1 ken ken  968 Jan 18 14:10 core.o
> ken/ntds/usq20/playground 

and this is what I got from copying & pasting the code you'd posted

lockheed:cla simon$ ls -l core.*
-rw-r--r--  1 simon  staff  3377 18 Jan 22:23 core.ads  <<<<<< 3377 bytes

I can 100% assure you that the difference in results IS NOT caused by a
difference in compilers: it IS caused by the fact that the code you sent
us is not the same as the code you compiled.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-18 22:16       ` Ken Roberts
  2020-01-18 22:35         ` Simon Wright
@ 2020-01-19  0:07         ` Niklas Holsti
  1 sibling, 0 replies; 49+ messages in thread
From: Niklas Holsti @ 2020-01-19  0:07 UTC (permalink / raw)


On 2020-01-19 0:16, Ken Roberts wrote:
> <code>
> 
> package core is
> 
>      --
>      -- 30-bit word size (CP642/A/B)
>      --      Data : 0 .. 2**29
>      -- Half-word
>      --      Upper : at 0 range 15 .. 29
>      --      Lower : at 0 range 00 .. 14

This means that you (and/or the documentation of the original computer) 
are using little-endian bit numbering, in which the less-significant 15 
bits are numbered 0 .. 14, and the more significant are numbered 15 .. 29.

Note, again, that the bit-numbering scheme can be different in different 
Ada compilers or for different target computers, which means that if you 
Unchecked_Convert a record type with a representation clause from or to 
an integer or modular type (wuth the same size) you must use the 
Bit_Order aspect to ensure portability across Ada compilers.

>      WORD_BITS       : constant := 30;
>      WORD_MASK       : constant := 8#77777_77777#;
>      HALF_WORD_BITS  : constant := 15;
>      HALF_WORD_MASK  : constant := 8#77777#;

Most Ada programmers do not use all-caps for constants (or named 
numbers, as here), because they are not preprocessor macros as in C, but 
are more like ordinary objects or variables in Ada. You can of course 
define your own style, this is just a remark.

>      for DataWord use record
>          for Upper use HALF_WORD_BITS .. (WORD_BITS - 1);
>          for Lower use 00 .. (HALF_WORD_BITS - 1);
>      end record;
>      pragma pack (DataWord);

Pragma Pack has two uses: first, to reduce the amount of memory used for 
data, which is probably not relevant for your emulator; second, to 
squeeze records like this into a "word" of a certain size, usually to 
make it possible to Unchecked_Convert it to/from some other type of the 
same size, and this is probably your goal here. But note that pragma 
Pack, even when accepted by the compiler, does _not_ mean that the 
compiler uses a size that is the sum of the widths of the bit-ranges in 
the representation clause; the compiler could, for whatever reason, add 
some space. Therefore it is good practice, IMO, to always add a Size 
clause if you want a certain size. Therefore I would add, to every 
pragma Pack that is meant to produce 30 bits, the clause "for 
the_type'Size use 30;".

(But the errors in the type declarations must first be corrected, of 
course.)

-- 
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
       .      @       .


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-18 23:38             ` Simon Wright
@ 2020-01-19  0:12               ` Ken Roberts
  2020-01-19  9:37                 ` Simon Wright
  2020-01-19  0:33               ` Ken Roberts
  1 sibling, 1 reply; 49+ messages in thread
From: Ken Roberts @ 2020-01-19  0:12 UTC (permalink / raw)


On Saturday, January 18, 2020 at 3:38:27 PM UTC-8, Simon Wright wrote:
> Ken Roberts <snip> writes:
> 
> > On Saturday, January 18, 2020 at 2:35:52 PM UTC-8, Simon Wright wrote:
> >> Ken Roberts <snip> writes:
> >> 
> >> > On Saturday, January 18, 2020 at 7:10:00 AM UTC-8, Simon Wright wrote:
> >> >> Ken Roberts <snip> writes:
> >> >> 
> >> >> > Don't know about the syntax errors - gnatmake seems to like it.
> >> >> 
> >> >> Then the code you're compiling isn't the code you're showing to us!
> >> >
> >
> > <snip>
> >
> > Interesting. Might be the difference in compilers then - I'm using the
> > ada compiler collection available in the Fedora 30 branch.
> 
> This is what you posted
> 
> > ken/ntds/usq20/playground $ gnatmake core
> > gcc -c core.ads
> > ken/ntds/usq20/playground $ ls -al
> > drwxrwxr-x. 2 ken ken 4096 Jan 18 14:10 .
> > drwxrwxr-x. 8 ken ken 4096 Jan 16 03:34 ..
> > -rw-rw-r--. 1 ken ken 1201 Jan 16 03:49 core.ads     <<<<<< 1201 bytes
> > -rw-rw-r--. 1 ken ken  280 Jan 18 14:10 core.ali
> > -rw-rw-r--. 1 ken ken  968 Jan 18 14:10 core.o
> > ken/ntds/usq20/playground 
> 
> and this is what I got from copying & pasting the code you'd posted
> 
> lockheed:cla simon$ ls -l core.*
> -rw-r--r--  1 simon  staff  3377 18 Jan 22:23 core.ads  <<<<<< 3377 bytes
> 
> I can 100% assure you that the difference in results IS NOT caused by a
> difference in compilers: it IS caused by the fact that the code you sent
> us is not the same as the code you compiled.

https://drive.google.com/file/d/1jZv-GcGL3PdqYqi60eIYZnRIndudZqic/view?usp=sharing

Here's a google drive link to check out - no changes and no copy/paste


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-18 22:59   ` Ken Roberts
@ 2020-01-19  0:30     ` Niklas Holsti
  2020-01-19  1:07       ` Ken Roberts
  0 siblings, 1 reply; 49+ messages in thread
From: Niklas Holsti @ 2020-01-19  0:30 UTC (permalink / raw)


On 2020-01-19 0:59, Ken Roberts wrote:
> On Saturday, January 18, 2020 at 9:57:08 AM UTC-8, Niklas Holsti wrote:
>> On 2020-01-18 9:32, Ken Roberts wrote:
>>> New to ada with a question about types.
>>
>> Welcome to the language!
>>
>> What did you program in before? Sometimes it is easier to give advice if
>> one knows the background of the person asking for it.
> 
> I touched on C way back when (sometime in the 80's) but my day job
> was not programming and didn't really dig into it.
> 
> For the last few years I've been programming Python (again, hobby,
> not day job) working on a PJLink control for network-connected
> projectors in an open source church project.

Ok, thanks, this is helpful.

>> - Can the computer to be emulated address only full 30-bit words, or can
>> it address also, say, characters (bytes) within a word?
> 
> The computer is 30-bit and the memory is 30-bit magnetic core. The
> memory control can return either a 30-bit word or 15-bit half-word
> from upper or lower half of memory address depending on one of the
> instruction modifiers.

Is it the AN/USQ-20? https://en.wikipedia.org/wiki/AN/USQ-20

>> - Is the computer you intend to emulate big-endian or little-endian?
>> There are two aspects to this question: first, conventions for numbering
>> bits (is bit 0 most significant or least significant?) and second,
>> addressing sub-word-units like characters (if possible) within a word
>> (does the lowest sub-word address access the most significant end of the
>> addressed word, or the least-significant end?).
> 
> The memory/computer model is 30-bit words both in computer and
> memory. No endianness.
> 
> 32K of directly addressable internal memory. Depending on a modifier 
> field in the instruction word, either a 30-bit word or 15-bit half 
> word (from either upper 15 bits or lower 15 bits) was returned from 
> memory for operatnds.

Ok, so there are no "half-word addresses" in the HW, so no half-word 
endianness.

Your other posts suggest that the manuals used little-endian bit 
numbering, so I assume you will use the same bit-numbering order in the 
record-representation clauses. You should then specify the Bit_Order 
aspect as System.Low_Order_First for those record types.

 From your other posts, it seems the practice was to store five 6-bit 
characters in a 30-bit word. There must be some "endianness" convention 
here: is the first of these five characters stored in the high bits of 
the word, or in the low bits? If the HW does not support character 
addressing, this endianness convention was defined and implemented by 
the SW running on the computer, and so does not have to be considered in 
the emulator per se.

>>> For most stuff, memory will be returning either DataWord or
>>> InstructionWord for each memory access, but I'm also looking at an
>>> easier way to manage characters for text display on the emulated
>>> monitors.
>> I would assume that in the original system, the diplay of characters on
>> the monitors was implemented in the SW programs that ran on the emulated
>> processor. Why should the processor emulator do something special for
>> this? Did the monitors display data directly from "display buffers" in
>> the 32K memory, using DMA?

> The "display" part was either an old-school teletype machine (using
> 5-bit characters)

I assume that the SW in the machine was responsible for sending the 
characters to the teletype, one by one, so the method for storing 
characters in words was a SW convention, right?

> or a CRT that was a combination radar repeater/computer symbology
> display unit with attached 15 line X 65 character text display using
> a really interesting 30-bit layout.
That sounds like possibly involving a HW-defined lay-out of characters 
in words. If you emulate this CRT, you may have to implement that in the 
emulator.

> (reaaalllly long URL from google reposted via bit.ly)
> 
> http://bit.ly/368sGgl  Picture of computer I'm trying to emulate.

Looks very much like the AN/USQ-20. And the acronym "NTDS" matches 
"Naval Tactical Data System", check.

-- 
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
       .      @       .

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-18 23:38             ` Simon Wright
  2020-01-19  0:12               ` Ken Roberts
@ 2020-01-19  0:33               ` Ken Roberts
  1 sibling, 0 replies; 49+ messages in thread
From: Ken Roberts @ 2020-01-19  0:33 UTC (permalink / raw)


On Saturday, January 18, 2020 at 3:38:27 PM UTC-8, Simon Wright wrote:

<snip>

Anyway - the posted code is an example of what I'm trying to start with for building the rest. ATM, this is just an example and for purpose of this discussion I'm just trying to figure out the basics of:

- Basic type to be able to easily put common 30-bit value into memory
- Basic type that when getting from memory, can easily be converted to a data word/instruction word/packed character set

Note: the packed character set as described is the way the computer was originally programmed since the output would have to be converted to either:
- send text to 5-bit teletype
- Send text to PPI (plan position indicator - aka radar repeater) (30-bit word)
- Send text to auxiliary CRT (15 lines X 65 character text only) (30-bit word)

The PPI and auxiliary CRT both actually use the same channel - the only difference is the 30-bit word format.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-19  0:30     ` Niklas Holsti
@ 2020-01-19  1:07       ` Ken Roberts
  0 siblings, 0 replies; 49+ messages in thread
From: Ken Roberts @ 2020-01-19  1:07 UTC (permalink / raw)


On Saturday, January 18, 2020 at 4:30:15 PM UTC-8, Niklas Holsti wrote:

> Is it the AN/USQ-20? https://en.wikipedia.org/wiki/AN/USQ-20

Yep. although the systems I worked on were the 642A/B variants - the USQ-20 link posted showed the original 642 as designed by Sperry/Univac for the evaluation phase of the NTDS project.


> Ok, so there are no "half-word addresses" in the HW, so no half-word 
> endianness.
> 

The half-word addressing was to/from memory only - the actual hardware memory controller had an S register (30-bit) that was used for actual memory in/out transfers between memory and the rest of the machine, with Zu and Zl (both 15-bit) registers that would then be taken from/placed into the S register for the actual memory transfer.

> Your other posts suggest that the manuals used little-endian bit 
> numbering, so I assume you will use the same bit-numbering order in the 
> record-representation clauses. You should then specify the Bit_Order 
> aspect as System.Low_Order_First for those record types.
> 
>  From your other posts, it seems the practice was to store five 6-bit 
> characters in a 30-bit word. There must be some "endianness" convention 
> here: is the first of these five characters stored in the high bits of 
> the word, or in the low bits? If the HW does not support character 
> addressing, this endianness convention was defined and implemented by 
> the SW running on the computer, and so does not have to be considered in 
> the emulator per se.
> 

The original program spec was to use CMS1 programming language later updated to CMS2 language. I didn't have access to the CMS compiler or even a system that could compile, so I worked directly with the 642 via the teletype.

I actually had to dig into the microfiche (it was rather nicely laid out - 1 microfiche slide per module, each formatted with CMS code immediately followed by the 642 machine code). there was a separate routine that was invoked to pull the characters out of the machine word and format for the intended device.

IIRC - the routine basically did:

- RJump to unpack routine
- Read next address into A
- Loop until end  -- Word loop
-   loop 5  -- Character loop
-     Circular left shift A 6
-     Logical AND A & 8#77#
-     Convert A to appropriate character for output device
-     Shift A to place character into correct bit position for output device
-     Masked OR with A and output buffer word being assembled
-     Store A to buffer
-     end loop -- Character 
-   Read next address & check for end of output
-   End loop  -- Word
- Return jump to (RJump + length of output) for next instruction

Note the output could either be 

- 5-bits (1 character) per word for teletype
- 30-bits (1 character plus position location bits) per word for PPI
- 30-bits (3 character) per word for ACRO (Auxiliary Crt Read Out)

> >>> For most stuff, memory will be returning either DataWord or
> >>> InstructionWord for each memory access, but I'm also looking at an
> >>> easier way to manage characters for text display on the emulated
> >>> monitors.
> >> I would assume that in the original system, the diplay of characters on
> >> the monitors was implemented in the SW programs that ran on the emulated
> >> processor. Why should the processor emulator do something special for
> >> this? Did the monitors display data directly from "display buffers" in
> >> the 32K memory, using DMA?
> 
> > The "display" part was either an old-school teletype machine (using
> > 5-bit characters)
> 
> I assume that the SW in the machine was responsible for sending the 
> characters to the teletype, one by one, so the method for storing 
> characters in words was a SW convention, right?
> 

Correct - see above for the approximate routine that was used. The formatted output was placed into an output buffer, then output was initiated with I/O instruction that used the output buffer start/end memory addresses.

The NTDS computer used a memory address for buffer control that was initiated with an I/O instruction and formatted as:

(I/O base address + channel number) : Mu == current output address, Ml == final output address. When memory detected Mu == Ml, then depending on I/O instruction used, would terminate the buffer, or terminate buffer and generate an interrupt to the computer.

I/O was handled asynchronously and the memory controller had interrupt priority for what section of the computer received the next memory access. The hardware was rather interesting in how memory was setup and controlled - it appears to have a lot more control than the current memory schemes, but with core memory (mini rings of magnets with wires running through them), timing and control was rather interesting.


> > or a CRT that was a combination radar repeater/computer symbology
> > display unit with attached 15 line X 65 character text display using
> > a really interesting 30-bit layout.
> That sounds like possibly involving a HW-defined lay-out of characters 
> in words. If you emulate this CRT, you may have to implement that in the 
> emulator.
> 
> > (reaaalllly long URL from google reposted via bit.ly)
> > 
> > http://bit.ly/368sGgl  Picture of computer I'm trying to emulate.
> 
> Looks very much like the AN/USQ-20. And the acronym "NTDS" matches 
> "Naval Tactical Data System", check.

Yep.

There was also a secondary 18-bit computer in use that the 642 talked to, but that's a different project.

> 
> -- 
> Niklas Holsti
> Tidorum Ltd
> niklas holsti tidorum fi
>        .      @       .

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-18  7:32 Creating several types from a base type and conversion Ken Roberts
  2020-01-18 12:16 ` Simon Wright
  2020-01-18 17:57 ` Niklas Holsti
@ 2020-01-19  3:37 ` Ken Roberts
  2020-01-23 21:39 ` Optikos
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 49+ messages in thread
From: Ken Roberts @ 2020-01-19  3:37 UTC (permalink / raw)


If anyone is interested - here's the gitlab link for what I'm working on

https://gitlab.com/ada5/usq20/

Mainly has the documentation I've been looking at and some markdown files I've added.

NOTE: it does not include playground/ directory where previous code examples reside since that's my personal playground to test Ada concepts.

Also - some of the code examples that might be in the src/ directory I know do not work at this time - still playing.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-19  0:12               ` Ken Roberts
@ 2020-01-19  9:37                 ` Simon Wright
  2020-01-19 11:48                   ` AdaMagica
  0 siblings, 1 reply; 49+ messages in thread
From: Simon Wright @ 2020-01-19  9:37 UTC (permalink / raw)


Ken Roberts <alisonken1@gmail.com> writes:

> On Saturday, January 18, 2020 at 3:38:27 PM UTC-8, Simon Wright wrote:
>> Ken Roberts <snip> writes:
>> 
>> > On Saturday, January 18, 2020 at 2:35:52 PM UTC-8, Simon Wright wrote:

>> I can 100% assure you that the difference in results IS NOT caused by
>> a difference in compilers: it IS caused by the fact that the code you
>> sent us is not the same as the code you compiled.
>
> https://drive.google.com/file/d/1jZv-GcGL3PdqYqi60eIYZnRIndudZqic/view?usp=sharing
>
> Here's a google drive link to check out - no changes and no copy/paste

Great, so that's a 31-line file, whereas the source you posted has 92
lines.

https://groups.google.com/d/msg/comp.lang.ada/2gqi1zxDk4A/pUO5HBQgCQAJ

I took "here is the code I'm working on" to mean "here is the code I'm
working on" rather than "this is a sketch of the sort of thing I'd like
to do, haven't tried to compile it yet".

Anyway, you've got lots of good hints from others here, so I'll shut up,
except for saying that I'd probably go with Bill Findlay's view:
representation clauses where endianness is involved are a lot more
confusing than shifts and masks.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-19  9:37                 ` Simon Wright
@ 2020-01-19 11:48                   ` AdaMagica
  2020-01-19 14:51                     ` Simon Wright
  2020-01-19 16:11                     ` Optikos
  0 siblings, 2 replies; 49+ messages in thread
From: AdaMagica @ 2020-01-19 11:48 UTC (permalink / raw)


Am Sonntag, 19. Januar 2020 10:37:21 UTC+1 schrieb Simon Wright:
> representation clauses where endianness is involved are a lot more
> confusing than shifts and masks.

See https://en.wikibooks.org/wiki/Ada_Programming/Attributes/%27Bit_Order

This is a complete description of how far we can get with endian independence in current Ada. Ada 202x won't change anything in this respect.

CKWG


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-19 11:48                   ` AdaMagica
@ 2020-01-19 14:51                     ` Simon Wright
  2020-01-19 15:24                       ` Niklas Holsti
  2020-01-19 16:11                     ` Optikos
  1 sibling, 1 reply; 49+ messages in thread
From: Simon Wright @ 2020-01-19 14:51 UTC (permalink / raw)


AdaMagica <christ-usch.grein@t-online.de> writes:

> Am Sonntag, 19. Januar 2020 10:37:21 UTC+1 schrieb Simon Wright:
>> representation clauses where endianness is involved are a lot more
>> confusing than shifts and masks.
>
> See https://en.wikibooks.org/wiki/Ada_Programming/Attributes/%27Bit_Order
>
> This is a complete description of how far we can get with endian
> independence in current Ada. Ada 202x won't change anything in this
> respect.

The description is still, I feel, confusing.

That said, I found this incantation (for an SNTP packet, which is
transmitted in network byte order) works a treat on this LE machine. I
don't have a BE machine to check it on (but the original code was used
on both BE and LE machines).

   end record
     with
       Bit_Order => System.High_Order_First,
       Scalar_Storage_Order => System.High_Order_First,
       Size => 48 * 8;


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-19 14:51                     ` Simon Wright
@ 2020-01-19 15:24                       ` Niklas Holsti
  0 siblings, 0 replies; 49+ messages in thread
From: Niklas Holsti @ 2020-01-19 15:24 UTC (permalink / raw)


On 2020-01-19 16:51, Simon Wright wrote:
> AdaMagica <christ-usch.grein@t-online.de> writes:
> 
>> Am Sonntag, 19. Januar 2020 10:37:21 UTC+1 schrieb Simon Wright:
>>> representation clauses where endianness is involved are a lot more
>>> confusing than shifts and masks.

In my experience, record-representation clauses with non-default 
Bit_Order work well as long as you don't exceed the size of the largest 
machine scalar (so 32 bits are ok for most systems, 64 bits for some) 
and as long as you don't use record components whose types are 
themselves records with representation clauses (if Bit_Order is not the 
default, GNAT insists on widening the component types to the nearest 
larger-or-equal machine scalar, so a 5-bit record, say, is widened to an 
8-bit record and GNAT then insists on using at least 8 bits for any 
component of that type, which usually breaks the lay-out of the 
higher-level record).

>> See https://en.wikibooks.org/wiki/Ada_Programming/Attributes/%27Bit_Order
>>
>> This is a complete description of how far we can get with endian
>> independence in current Ada. Ada 202x won't change anything in this
>> respect.
> 
> The description is still, I feel, confusing.
> 
> That said, I found this incantation (for an SNTP packet, which is
> transmitted in network byte order) works a treat on this LE machine. I
> don't have a BE machine to check it on (but the original code was used
> on both BE and LE machines).
> 
>     end record
>       with
>         Bit_Order => System.High_Order_First,
>         Scalar_Storage_Order => System.High_Order_First,

Note that Scalar_Storage_Order is GNAT-specific and not Ada standard.

>         Size => 48 * 8;
>

-- 
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
       .      @       .

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-19 11:48                   ` AdaMagica
  2020-01-19 14:51                     ` Simon Wright
@ 2020-01-19 16:11                     ` Optikos
  1 sibling, 0 replies; 49+ messages in thread
From: Optikos @ 2020-01-19 16:11 UTC (permalink / raw)


On Sunday, January 19, 2020 at 5:48:20 AM UTC-6, AdaMagica wrote:
> Am Sonntag, 19. Januar 2020 10:37:21 UTC+1 schrieb Simon Wright:
> > representation clauses where endianness is involved are a lot more
> > confusing than shifts and masks.
> 
> See https://en.wikibooks.org/wiki/Ada_Programming/Attributes/%27Bit_Order
> 
> This is a complete description of how far we can get with endian independence in current Ada. Ada 202x won't change anything in this respect.
> 
> CKWG

No, it is not entirely complete—not entirely capturing the full industrial practice on this little-endian/big-endian topic.  A complete treatment of the topic would include utilizing the build system to swap in either of 2 child packages containing same-named identifiers, such as my aforementioned {ExtractCharWordFromDataWord, EmbedCharWordIntoDataWord, ExtractDataWordFromInstructionWordOpcode, EmbedDataWordIntoInstructionWordOpcode, ExtractDataWordFromInstructionWordImmediate, EmbedDataWordIntoInstructionWordImmediate,  ExtractDataWordFromImmediateForSuchandsuchOpcode, EmbedDataWordIntoImmediateForSuchandsuchOpcode} where
1) one child package has the •little-endian• shifting & masking (via either declarative/representation-clauses or bit-twiddling imperative/OR-&-AND-expressions-&-assignments) variant for each of those identifier names
and
2) the other child package has the •big-endian• shifting & masking (…) variant for each of those identifier names.

Yes, I realize that the build system is (unfortunately!) not standardized in Ada, but (nearly?) all Ada compilers since time began have permitted some technique of choosing which packages to include or exclude from the build so that the linker choses this variant or that variant of same-named identifiers to actually be linked into the executable or dynamically-linked library, preferably on a per-ISA basis or on a conditional-compilation basis.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-18 20:34             ` Simon Wright
@ 2020-01-20 16:38               ` Bill Findlay
  0 siblings, 0 replies; 49+ messages in thread
From: Bill Findlay @ 2020-01-20 16:38 UTC (permalink / raw)


On 18 Jan 2020, Simon Wright wrote
(in article <lyftgcy059.fsf@pushface.org>):

> "Jeffrey R. Carter"<spam.jrcarter.not@spam.not.acm.org>  writes:
>
> > On 1/18/20 7:20 PM, Bill Findlay wrote:
>
> > > type word is mod 2**30;
> > >
> > > subtype word_shift_length is Natural range 0..30;
> > >
> > > function shift_word_left (W : word; amount : word_shift_length)
> > > return KDF9.word;
>
> > I don't know what KDF9.Word is, but surely you have to implement
> > these. Or are you saying GNAT magically implements them?
>
> It doesn't; 'with Import, convention =>  Intrinsic' requires the first
> argument to have size 8, 16, 32, or 64, and the second argument must be
> Natural (not a subtype of it, even if unconstrained).

D'oh! (Looks around for lost marbles - they are here somewhere.)

Here are extracts from theactual code for KDF9's 48-bit shift operations,
showing how they are built up from the intrinsic 64-bit shifts.
Converting this to 30-bit shifts using 32-bit intrinsics should be simple:

-- 
-- The fundamental emulated storage unit is the 48-bit word.
-- 

type word is mod 2**48;

-- 
-- This is the emulation host's 64-bit unsigned integer type.
-- 

type u_64 is mod 2**64 with Size =>  64;

word_mask : constant := 8#7777777777777777#;

function as_word (u : u_64)
return word
is (word(u and word_mask));

-- This GNAT-specific pragma declares all the 64-bit shift operations.

pragma Provide_Shift_Operators (u_64);

-- 
-- These are the 48-bit primitive, fixed-direction, shift operations.
-- 

subtype word_shift_length is Natural range 0..48;

function shift_word_left (W : word; amount : word_shift_length)
return word
is (as_word(shift_left(u_64(W), amount)));

function shift_word_right (W : word; amount : word_shift_length)
return word
is (word(shift_right(u_64(W), amount)));

function rotate_word_left (W : word; amount : word_shift_length)
return word
is (shift_word_left(W, amount) or shift_word_right(W, 48-amount));

function rotate_word_right (W : word; amount : word_shift_length)
return word
is (shift_word_right(W, amount) or shift_word_left(W, 48-amount));

-- 
-- KDF9 shift operations actually had a signed shift amount; e.g.:
-- 

function shift_logical (W : word; L : signed_Q_part)
return word
is
(
if abs L >  47 then 0
elsif L <  0 then shift_word_right(W, Natural(-L))
else shift_word_left (W, Natural(+L))
);

... etc ...

Sorry for the temporary brain malfunction!

-- 
Bill Findlay

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-18 12:49   ` Ken Roberts
                       ` (2 preceding siblings ...)
  2020-01-18 15:47     ` Simon Wright
@ 2020-01-21 21:35     ` Shark8
  2020-01-21 23:06       ` Niklas Holsti
  2020-01-22  8:37       ` Simon Wright
  3 siblings, 2 replies; 49+ messages in thread
From: Shark8 @ 2020-01-21 21:35 UTC (permalink / raw)


On Saturday, January 18, 2020 at 5:49:17 AM UTC-7, Ken Roberts wrote:
> 
> The concept is emulating a 30-bit computer from olden days.
Then allow me to suggest you do that, instead:

   Type Word is range 0..2**30-1
    with Size => 30;

this allows you to also do something like this:
  Function Convert( Data : Integer ) return Word
    with Inline, Pre => Data in Integer(Word'First)..Integer(Word'Last);
  Function Convert( Data : Word ) return Integer
    with Inline;
  -- ...
  Function Convert( Data : Integer ) return Word is  ( Word(Data)    );
  Function Convert( Data : Word ) return Integer is  ( Integer(Data) );

> 
> It was my understanding that a boolean array would be better than an
> integer in order to do some of the bit manipulations that the old
> computer was designed for.
That might be true; though If you're going that route you may want to number the array indices 1..30.

> 
> One example:
> 
> ADD LP : L[Y*(Q)]+(A) -> A
> 
> Take the logical product of Y and Q register, then add A register,
> place results in A register.
> 
> (LP being boolean AND of 2 registers)
> 
> I think I tried doing tagged records and subtypes, but kept getting
> errors like 'Bits already mapped' when trying to extend the BaseWord
> (30 bit) into a data word ( 2 separate 15-bit fields) and instruction
> word (5 separate bit-mapped fields) while still being able to easily
> convert between BaseWord and others (think pulling next instruction from memory array, then pulling data from arbitrary location in memory array).
Hm, you *could* make the programs a stream and use the stream read/write attributes. -- Though this *might* be a bit of a bear to debug, it does have the advantage that you could "dump the bits to the stream" and let the Read attribute/function take care of interpreting the data.

> 
> Functionally, it would be relatively easy to just ignore the hardware
> aspect of the emulation, but I'm trying to set it up so I can emulate
> the hardware later as a learning tool to how this old computer
> actually did things (like 1's complement subtractive addition). The
> real fun will be programming the timing (1MHz clock split into 4
> phases, with interesting interrupt handling).
Hm, this is interesting... it sounds like it would make a really good Ada/VHDL combined-language project. (But I have no idea how hard that would be compared to just simulating things pure-Ada.)

> 
> I know - _very_ ambitious project for a beginner in the language (not
> to programming), but I figure might as well have an interesting
> project to work on while learning rather than the basic "Hello World"
> style that seems to be prevalent.
Yes. I would tend to agree.
Allow me to suggest that you could leverage the power of enumerations for the instruction-set.

Then you could build your instruction-stream off that; eg:
  -- Ultra-simplified:
  Type OP_CODE is (ADD, DIV, EXMPL); -- SUB, MUL, WTVR.
  
  Type Register_Code is (R1, R2, AD, SB)
   with Size => 4; -- Four bit register-code; single-trace active.
  For  Register_Code use (
   R1 => 2#0001#,
   R2 => 2#0010#,
   AD => 2#0100#,
   SB => 2#1000#
  );
  
  Type Instruction( Op : Opcode ) is record
   case Op is
    When ADD   => Val_1, Val_2 : Word; -- two Word-size operands.
    When DIV   => Val_1, Val_2 : Word;
    When EXMPL => Register  : Register_Code;
    -- other operations
   end case;
  end record
  with Static_Predicate => -- NOTE: YOU CAN ENFORCE INSTRUCTION VALIDITY.
   (Case Instruction.Op is
     When Add => True, -- All values are good.
     When Div => Instruction.Val_2 /= 0 or else raise Program_Error with "Invalid bitstream",
     When EXMPL => Instruction.Register /= AD
   );

The above is a simplified version of something I played around with a few years ago: https://github.com/OneWingedShark/Lamman/blob/master/src/ghost_cpu.ads

IOW, don't be afraid to logically model things a bit more than the bit-twiddling would suggest. Also Bill Findlay's KDF9 emulator is probably a really good resource in using Ada to emulate old hardware. (I think there was an Ada implementation of the WWII Enigma machine or something similar someone built, but I can't remember *who*.)

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-21 21:35     ` Shark8
@ 2020-01-21 23:06       ` Niklas Holsti
  2020-01-22  1:08         ` Ken Roberts
  2020-01-22  8:37       ` Simon Wright
  1 sibling, 1 reply; 49+ messages in thread
From: Niklas Holsti @ 2020-01-21 23:06 UTC (permalink / raw)


On 2020-01-21 23:35, Shark8 wrote:
> On Saturday, January 18, 2020 at 5:49:17 AM UTC-7, Ken Roberts wrote:
>>
>> The concept is emulating a 30-bit computer from olden days.
> Then allow me to suggest you do that, instead:
> 
>     Type Word is range 0..2**30-1
>      with Size => 30;
> 
> this allows you to also do something like this:
>    Function Convert( Data : Integer ) return Word
>      with Inline, Pre => Data in Integer(Word'First)..Integer(Word'Last);

This won't work in Janus/Ada or other Ada compilers (if there are 
any...) with a 16-bit Integer. Better use a user-defined type with a 
sufficient range.

>> It was my understanding that a boolean array would be better than an
>> integer in order to do some of the bit manipulations that the old
>> computer was designed for.

> That might be true; though If you're going that route you may want to
> number the array indices 1..30.

Why? Bit numbers normally start at zero, in either bit-number order, and 
in all computer manuals and instruction sets I have ever seen.

(The rest of my comments are addressed mainly to Ken Roberts.)

>> One example:
>>
>> ADD LP : L[Y*(Q)]+(A) -> A
>>
>> Take the logical product of Y and Q register, then add A register,
>> place results in A register.
>>
>> (LP being boolean AND of 2 registers)

Modular types, including Interfaces.Unsigned_32, have built-in bit-wise 
boolean operators, so there is little advantage in using arrays of 
booleans instead of modular types.

>> I think I tried doing tagged records and subtypes, but kept getting
>> errors like 'Bits already mapped' when trying to extend the BaseWord
>> (30 bit) into a data word ( 2 separate 15-bit fields) and instruction
>> word (5 separate bit-mapped fields) while still being able to easily
>> convert between BaseWord and others (think pulling next instruction
>> from memory array, then pulling data from arbitrary location in
>> memory array).

Perhaps you (Ken) did not understand that when you extend a tagged 
record type, you are *adding* components to the components in the parent 
type. The parent type's components are still there, and still occupy the 
bit ranges specified in the representation clause for the parent type. 
This is not a "remapping" or "reinterpretation" of the bits of the 
parent type -- that is what the Unchecked_Conversion function is for.

> Hm, you *could* make the programs a stream and use the stream > read/write attributes. -- Though this *might* be a bit of a
> bear to debug, it does have the advantage that you could "dump > the bits to the stream" and let the Read attribute/function take
> care of interpreting the data.

That's a bad idea, IMO. The emulated RAM should be randomly accessible, 
word by word, which is cumbersome and slow (if possible) for streams.

>> Functionally, it would be relatively easy to just ignore the hardware
>> aspect of the emulation, but I'm trying to set it up so I can emulate
>> the hardware later as a learning tool to how this old computer
>> actually did things (like 1's complement subtractive addition). The
>> real fun will be programming the timing (1MHz clock split into 4
>> phases, with interesting interrupt handling).

It seems to me that emulating the instruction semantics (functional 
emulation) is a reasonable goal for someone new to Ada. Emulating the 
cycle-by-cycle operation of the HW is much more ambitious, especially 
when various HW units are running in parallel, and I do not advise 
starting there. Better to start from a functional emulation, and tackle 
the cycle-accurate emulation later.

Your github documents suggest that you intend use network messages to 
emulate data transmitted at clock-rate between the different HW units of 
the emulated computer. I suspect that this, together with the 
cycle/phase-specific emulation, will not let you reach your target of 
running the emulation at the 1 MHz rate of the original computer.

-- 
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
       .      @       .

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-21 23:06       ` Niklas Holsti
@ 2020-01-22  1:08         ` Ken Roberts
  2020-01-22 14:18           ` Ken Roberts
  0 siblings, 1 reply; 49+ messages in thread
From: Ken Roberts @ 2020-01-22  1:08 UTC (permalink / raw)


On Tuesday, January 21, 2020 at 3:06:55 PM UTC-8, Niklas Holsti wrote:
> On 2020-01-21 23:35, Shark8 wrote:
> > On Saturday, January 18, 2020 at 5:49:17 AM UTC-7, Ken Roberts wrote:

<snip>

> >> One example:
> >>
> >> ADD LP : L[Y*(Q)]+(A) -> A
> >>
> >> Take the logical product of Y and Q register, then add A register,
> >> place results in A register.
> >>
> >> (LP being boolean AND of 2 registers)
> 
> Modular types, including Interfaces.Unsigned_32, have built-in bit-wise 
> boolean operators, so there is little advantage in using arrays of 
> booleans instead of modular types.
> 
> >> I think I tried doing tagged records and subtypes, but kept getting
> >> errors like 'Bits already mapped' when trying to extend the BaseWord
> >> (30 bit) into a data word ( 2 separate 15-bit fields) and instruction
> >> word (5 separate bit-mapped fields) while still being able to easily
> >> convert between BaseWord and others (think pulling next instruction
> >> from memory array, then pulling data from arbitrary location in
> >> memory array).
> 
> Perhaps you (Ken) did not understand that when you extend a tagged 
> record type, you are *adding* components to the components in the parent 
> type. The parent type's components are still there, and still occupy the 
> bit ranges specified in the representation clause for the parent type. 
> This is not a "remapping" or "reinterpretation" of the bits of the 
> parent type -- that is what the Unchecked_Conversion function is for.
>

I kinda understood it was not a remapping but adding components, but was kinda hoping to be able to have:

var.data = 30 bits
var.upper = upper 15 bits of data
var.lower = lower 15 bits of data

 
> > Hm, you *could* make the programs a stream and use the stream > read/write attributes. -- Though this *might* be a bit of a
> > bear to debug, it does have the advantage that you could "dump > the bits to the stream" and let the Read attribute/function take
> > care of interpreting the data.
> 
> That's a bad idea, IMO. The emulated RAM should be randomly accessible, 
> word by word, which is cumbersome and slow (if possible) for streams.
> 

Correct - I was planning on using

Memory : array (0 .. 32768) of Word;

As I noted, with modern computers this size of array would be pretty much a corner of ram.


> >> Functionally, it would be relatively easy to just ignore the hardware
> >> aspect of the emulation, but I'm trying to set it up so I can emulate
> >> the hardware later as a learning tool to how this old computer
> >> actually did things (like 1's complement subtractive addition). The
> >> real fun will be programming the timing (1MHz clock split into 4
> >> phases, with interesting interrupt handling).
> 
> It seems to me that emulating the instruction semantics (functional 
> emulation) is a reasonable goal for someone new to Ada. Emulating the 
> cycle-by-cycle operation of the HW is much more ambitious, especially 
> when various HW units are running in parallel, and I do not advise 
> starting there. Better to start from a functional emulation, and tackle 
> the cycle-accurate emulation later.
> 

The original plan was to emulate the software, but by careful planning should be able to reuse the instruction interpreter functions/procedures when it comes time to actually emulate the hardware - note that the main emulation parts are moving data between registers and bit manipulation within the registers.

> Your github documents suggest that you intend use network messages to 
> emulate data transmitted at clock-rate between the different HW units of 
> the emulated computer. I suspect that this, together with the 
> cycle/phase-specific emulation, will not let you reach your target of 
> running the emulation at the 1 MHz rate of the original computer.
> 

The network emulation is not for messaging between computer sections - it's for emulating the cabling between the computer and peripheral equipment.

The timing is a consideration both in emulating the software as well as emulating the timing between the peripheral equipment.

The long-term project goal is to emulate the USQ-20 suite - so the message passing via network would simulate the cabling between the nodes. Node examples:

Node 1 : CP642 (unit A)
Node 2 : CP642 (unit B)
Node 3 : CP642 (unit C)
Node 4 : RD231 (Magnetic tape unit)
Node 5 : UGC13 (Teletypewriter)
Node 6 : SYA4  (PPI display - radar repeaters w/symbology)

> -- 
> Niklas Holsti
> Tidorum Ltd
> niklas holsti tidorum fi
>        .      @       .

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-21 21:35     ` Shark8
  2020-01-21 23:06       ` Niklas Holsti
@ 2020-01-22  8:37       ` Simon Wright
  2020-01-22 14:32         ` Shark8
  1 sibling, 1 reply; 49+ messages in thread
From: Simon Wright @ 2020-01-22  8:37 UTC (permalink / raw)


Shark8 <onewingedshark@gmail.com> writes:

>    Type Word is range 0..2**30-1
>     with Size => 30;
>
> this allows you to also do something like this:
>   Function Convert( Data : Integer ) return Word
>     with Inline, Pre => Data in Integer(Word'First)..Integer(Word'Last);

Not sure what advantage this would give over just Word (<integer>)

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-22  1:08         ` Ken Roberts
@ 2020-01-22 14:18           ` Ken Roberts
  0 siblings, 0 replies; 49+ messages in thread
From: Ken Roberts @ 2020-01-22 14:18 UTC (permalink / raw)


On Tuesday, January 21, 2020 at 5:08:25 PM UTC-8, Ken Roberts wrote:
> On Tuesday, January 21, 2020 at 3:06:55 PM UTC-8, Niklas Holsti wrote:
> > On 2020-01-21 23:35, Shark8 wrote:
> > > On Saturday, January 18, 2020 at 5:49:17 AM UTC-7, Ken Roberts wrote:

<snip> 
> Node 1 : CP642 (unit A)
> Node 2 : CP642 (unit B)
> Node 3 : CP642 (unit C)
> Node 4 : RD231 (Magnetic tape unit)
> Node 5 : UGC13 (Teletypewriter)
> Node 6 : SYA4  (PPI display - radar repeaters w/symbology)

Oops - been a while.

Node 4 : RD243 (Magnetic tape unit)
Node 5 : RD231/UGC13 (Combined paper tape punch-reader/teletypewriter)

And to expand on SYA4:

Computer I/O channel -> RMU (refresh memory unit) -> up to 15 PPI display units

The original SYA4 PPI units could only display symbology as it was sent from the computer. The RMU was added so the computer could load symbols that were to be displayed long term into the RMU, then the RMU did the actual refresh of the symbols without computer intervention.

The upgraded display suite was UYA4 and each console had it's own symbology refresh memory (with a few enhancements) that utilized mostly the same symbology as the SYA4 suite.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-22  8:37       ` Simon Wright
@ 2020-01-22 14:32         ` Shark8
  2020-01-22 15:40           ` Simon Wright
  0 siblings, 1 reply; 49+ messages in thread
From: Shark8 @ 2020-01-22 14:32 UTC (permalink / raw)


On Wednesday, January 22, 2020 at 1:38:02 AM UTC-7, Simon Wright wrote:
> Shark8 writes:
> 
> >    Type Word is range 0..2**30-1
> >     with Size => 30;
> >
> > this allows you to also do something like this:
> >   Function Convert( Data : Integer ) return Word
> >     with Inline, Pre => Data in Integer(Word'First)..Integer(Word'Last);
> 
> Not sure what advantage this would give over just Word (<integer>)

SPARK Verification, mostly.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-22 14:32         ` Shark8
@ 2020-01-22 15:40           ` Simon Wright
  0 siblings, 0 replies; 49+ messages in thread
From: Simon Wright @ 2020-01-22 15:40 UTC (permalink / raw)


Shark8 <onewingedshark@gmail.com> writes:

> On Wednesday, January 22, 2020 at 1:38:02 AM UTC-7, Simon Wright wrote:
>> Shark8 writes:
>> 
>> >    Type Word is range 0..2**30-1
>> >     with Size => 30;
>> >
>> > this allows you to also do something like this:
>> >   Function Convert( Data : Integer ) return Word
>> >     with Inline, Pre => Data in Integer(Word'First)..Integer(Word'Last);
>> 
>> Not sure what advantage this would give over just Word (<integer>)
>
> SPARK Verification, mostly.

Fairly sure that SPARK knows about data ranges.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-18  7:32 Creating several types from a base type and conversion Ken Roberts
                   ` (2 preceding siblings ...)
  2020-01-19  3:37 ` Ken Roberts
@ 2020-01-23 21:39 ` Optikos
  2020-01-24  9:35   ` Ken Roberts
  2020-01-24 15:54   ` Simon Wright
  2020-01-25 10:37 ` Ken Roberts
  2020-01-27 14:10 ` Ken Roberts
  5 siblings, 2 replies; 49+ messages in thread
From: Optikos @ 2020-01-23 21:39 UTC (permalink / raw)


On Saturday, January 18, 2020 at 1:32:54 AM UTC-6, Ken Roberts wrote:
> New to ada with a question about types.
> 
> The theory is creating an old computer emulator. Memory is an array of BaseWord's (Older computer had
> 32K of memory, so making an array on a modern computer is peanuts).
> 
> Creating a base type that is stored into the memory array
> 
> <code>
> 
>     type BaseWord is array (00 .. 29) of Boolean;
>     pragma pack (BaseWord);
> 
> </code>
> 
> Creating derived type(s) that can be returned from the memory section
> 
> <code>
> 
>     type DataWord is new BaseWord with record
>         Upper : array 00 .. 8#77777# of Boolean;
>         Lower : array 00 .. 8#77777# of Boolean;
>     end record;
> 
>     for DataWord use record
>         for Upper use 15 .. 29;
>         for Lower use 00 .. 14;
>     end record;
>     pragma pack (DataWord);
> 
>     type InstructionWord is new BaseWord with record;
>         (define field parameters)
>     end record;
>     for InstructionWord use record
>         (define BaseWord fields bit locations)
>     end record;
> 
> </code>
> 
> Defining how characters for display are packed into memory
> 
> <code>
>     type CharByte is array (0 .. 5) of Boolean;  -- Character define
>     type CharWord is record
>         CharArray : array (0 .. 4) of CharByte;
>     end record;
>     for CharWord use record
>         for CharArray (0) use 24 .. 29;
>         for CharArray (1) use 18 .. 23;
>         for CharArray (2) use 12 .. 17;
>         for CharArray (3) use 6 .. 11;
>         for CharArray (4) use 0 .. 5;
>     end record;
> 
> </code>
> 
> So the next question is how to convert between CharWord/BaseWord?
> 
> For most stuff, memory will be returning either DataWord or InstructionWord for each memory access,
> but I'm also looking at an easier way to manage characters for text display on the emulated monitors.

Many replies to this posting focused on details of using standard Ada itself or GNAT-specific extensions of Ada itself as the total solution to portability, such as via record-representation clauses (which aren't always portable to Janus/Ada) and/or Scalar_Storage_Order (which is GNAT-only, absent in all other Ada compilers).

Instead of depending on a big-language solution of a very-feature-rich compiler, there is a common industrial practice in both Ada and other languages that I mentioned in my 2 replies:  use 2 different packages with same-named identifiers (especially functions or procedures) to link in either the little-endian implementation or big-endian implementation (or any other nonportability that is peculiar to a target ISA or target OS).  This solution is at the heart of VIPER and MVVM software architectures for taming the portability beast quite well (going so far as to tame even radical philosophical departures among the very divergent GUIs/window-managers:  Gnome/GTK, Windows UWP, Win32, KDE/Qt, Tizen).

The key idea is to utilize the build system on a per-target basis, especially the same build system for all targets (instead of the easier case of a different build system per target OS/ISA/GUI/whatever).  The Project Extension portion of gprbuild is how FSF or AdaCore GNAT would link in the little-endian package of same-named identifiers versus the big-endian package of same-named identifiers (or overcoming any other seemingly troublesome lack of portability within the Ada language itself or of your own ossified source code that oops inadvertently/short-sightedly got modeled in the past for only one of the now-needed targets-of-portability).  Find a layer of identifiers (especially functions or procedures) that can now be a facade of sorts behind/within which you swap in this versus that nonportable implementation peculiar to each target at link-time (and perhaps just don't even compile the other package that is not needed for the current target-of-interest).

The kind of per-target switcheroo just described above is mentioned in the webpage below:
“… Another use case is a large software system with multiple implementations of a common interface; in Ada terms, multiple versions of a package body for the same spec, or perhaps different versions of a package spec that have the same visible part but different private parts. For example, one package might be safe for use in tasking programs, while another might be used only in sequential applications. …”

https://docs.adacore.com/gprbuild-docs/html/gprbuild_ug/gnat_project_manager.html#project-extension


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-23 21:39 ` Optikos
@ 2020-01-24  9:35   ` Ken Roberts
  2020-01-24 10:04     ` AdaMagica
  2020-01-24 12:38     ` Optikos
  2020-01-24 15:54   ` Simon Wright
  1 sibling, 2 replies; 49+ messages in thread
From: Ken Roberts @ 2020-01-24  9:35 UTC (permalink / raw)


On Thursday, January 23, 2020 at 1:39:13 PM UTC-8, Optikos wrote:

<snip>

> Instead of depending on a big-language solution of a very-feature-rich compiler, there is a common industrial practice in both Ada and other languages that I mentioned in my 2 replies:  use 2 different packages with same-named identifiers (especially functions or procedures) to link in either the little-endian implementation or big-endian implementation (or any other nonportability that is peculiar to a target ISA or target OS).  This solution is at the heart of VIPER and MVVM software architectures for taming the portability beast quite well (going so far as to tame even radical philosophical departures among the very divergent GUIs/window-managers:  Gnome/GTK, Windows UWP, Win32, KDE/Qt, Tizen).

<snip>

It was my understanding that big/little-endian representations were mainly how data was accessed from memory that used 8-bit bytes for physical memory layout.

Using a 16-bit register as an example:

register: [first byte][second byte]

In big-endian format the memory usage would be:

adx 0: [first byte]
adx 1: [second byte]
adx 2: [first byte]
adx 3: [second byte]

In little-endian format the memory usage would be:

adx 0: [second byte]
adx 1: [first byte]
adx 2: [second byte]
adx 3: [first byte]

Is my understanding off?


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-24  9:35   ` Ken Roberts
@ 2020-01-24 10:04     ` AdaMagica
  2020-01-24 12:38     ` Optikos
  1 sibling, 0 replies; 49+ messages in thread
From: AdaMagica @ 2020-01-24 10:04 UTC (permalink / raw)


Am Freitag, 24. Januar 2020 10:35:04 UTC+1 schrieb Ken Roberts:
> It was my understanding that big/little-endian representations were mainly how
> data was accessed from memory that used 8-bit bytes for physical memory layout.
> 
> Using a 16-bit register as an example:
> 
> register: [first byte][second byte]

Assuming the register is interpreted as a signless whole number, first byte is the MSB, second byte the LSB.
> 
> In big-endian format the memory usage would be:
> 
> adx 0: [first byte]     MSB
> adx 1: [second byte]    LSB
> adx 2: [first byte]
> adx 3: [second byte]
> 
> In little-endian format the memory usage would be:
> 
> adx 0: [second byte]    LSB
> adx 1: [first byte]     MSB
> adx 2: [second byte]
> adx 3: [first byte]
> 
> Is my understanding off?

So it's OK. See
https://en.wikibooks.org/wiki/Ada_Programming/Attributes/%27Bit_Order


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-24  9:35   ` Ken Roberts
  2020-01-24 10:04     ` AdaMagica
@ 2020-01-24 12:38     ` Optikos
  2020-01-24 15:01       ` Ken Roberts
  1 sibling, 1 reply; 49+ messages in thread
From: Optikos @ 2020-01-24 12:38 UTC (permalink / raw)


On Friday, January 24, 2020 at 3:35:04 AM UTC-6, Ken Roberts wrote:
> On Thursday, January 23, 2020 at 1:39:13 PM UTC-8, Optikos wrote:
> 
> <snip>
> 
> > Instead of depending on a big-language solution of a very-feature-rich compiler, there is a common industrial practice in both Ada and other languages that I mentioned in my 2 replies:  use 2 different packages with same-named identifiers (especially functions or procedures) to link in either the little-endian implementation or big-endian implementation (or any other nonportability that is peculiar to a target ISA or target OS).  This solution is at the heart of VIPER and MVVM software architectures for taming the portability beast quite well (going so far as to tame even radical philosophical departures among the very divergent GUIs/window-managers:  Gnome/GTK, Windows UWP, Win32, KDE/Qt, Tizen).
> 
> <snip>
> 
> It was my understanding that big/little-endian representations were mainly how data was accessed from memory that used 8-bit bytes for physical memory layout.
> 
> Using a 16-bit register as an example:
> 
> register: [first byte][second byte]
> 
> In big-endian format the memory usage would be:
> 
> adx 0: [first byte]
> adx 1: [second byte]
> adx 2: [first byte]
> adx 3: [second byte]
> 
> In little-endian format the memory usage would be:
> 
> adx 0: [second byte]
> adx 1: [first byte]
> adx 2: [second byte]
> adx 3: [first byte]
> 
> Is my understanding off?

Yes, you are correct sort of, depending on how you define “first” and “second”.  If first is low-valued powers of 2 increasing ascendingly up to 16 (or 32), then you have it backwards.  If first is high-valued powers of 2 decreasing descendingly down to 0, then you have it correct.  As AdaMagica also nudged in the better-thinking direction, the terms most-significant byte and least-significant byte and most-significant (16-bit-)word and least-significant word are clearer thinking because they overtly refer to the powers of 2 mathematically.  Ada's bit_order refers to whether the ISA refers to the bits (independent of the bytes and words) in power-of-2 order mathematically where 2⁰ is the origin and usually called bit zero (instead of bit one) or opposite of power-of-2 order anti-mathematically/lexicaly where 2¹⁶ is the origin often called bit one (or more rarely bit zero) in 16-bit words and where 2³² is the origin often called bit one (or more rarely bit zero) in 32-bit words.

But I am focusing on a different debate.  Should a) an all-knowing omniscient compiler feature be the basis of your portability, deferring the responsibility to the language, or b) overt source code that you write in a structured way be the basis of your portability, taking the responsibility on yourself?  The omnsiscent compiler feature needs to present in all compilers across all targets and nonbuggy (or bug-for-bug compatibility) across all targets, whereas the overt source code architecture depends only on having a way to substitute one Ada package for another with same-named identifiers on this target versus that target.  In the overt source-code architecture there are little-endian versions of certain functions (or procedures) and big-endian versions of those same-named functions (or procedures).  Indeed, the this versus that package switcheroo at build-time can be utilized for more portability challenges than mere endianness, but also for this OS versus that OS (e.g., POSIX versus Windows) and this GUI framework versus that GUI framework (e.g., Gnome/Gtk versus Windows UWP versus Win32 versus Carbon versus Cocoa versus CocoaTouch versus Tizen) all of which is far far far beyond the capabilities of any omniscient compiler to hide.  VIPER takes this build-time switcheroo to the Nth degree (but you might not need all of VIPER); MVVM less so than VIPER, but both as a valid stern criticism of MVC's excessive interconnectedness that causes MVC to ruin this degree of portability.  All 3 of VIPER, MVVM, and MVC is a criticism of the omniscient compiler feature trying to accomplish the portability for you intrinsically behind the scenes.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-24 12:38     ` Optikos
@ 2020-01-24 15:01       ` Ken Roberts
  2020-01-24 15:22         ` Simon Wright
  0 siblings, 1 reply; 49+ messages in thread
From: Ken Roberts @ 2020-01-24 15:01 UTC (permalink / raw)


On Friday, January 24, 2020 at 4:38:53 AM UTC-8, Optikos wrote:

<snip>

OK - here's what I'm looking at.

Based upon the hardware I worked on (and documentation for the 642 system), apparently it's BE based:

Register representation on panel: R bits 29 .. 0

where MSB is on the left and LSB on the right.

Memory is 30 bits (just like registers) and is physically spread over 5 chassis with 6 bits of each word stored on each chassis:

chassis 1 : bits 5 .. 0
chassis 2 : bits 11 .. 6
chassis 3 : bits 17 .. 12
chassis 4 : bits 23 .. 18
chassis 5 : bits 29 .. 24

If you look up to my third post you can see the basic setup of how a word is interpreted for instructions, with all calculations based on the msb .. lsb concept.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-24 15:01       ` Ken Roberts
@ 2020-01-24 15:22         ` Simon Wright
  2020-01-24 15:40           ` Ken Roberts
  0 siblings, 1 reply; 49+ messages in thread
From: Simon Wright @ 2020-01-24 15:22 UTC (permalink / raw)


Ken Roberts <alisonken1@gmail.com> writes:

> Based upon the hardware I worked on (and documentation for the 642
> system), apparently it's BE based:
>
> Register representation on panel: R bits 29 .. 0
>
> where MSB is on the left and LSB on the right.

Looks LE to me.

If the panel shows

bit #   ...  5 4 3 2 1 0
lit     ...  0 0 0 1 0 1

and the value represented is 5 then it's little-endian; bit 0 is the
least significant bit.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-24 15:22         ` Simon Wright
@ 2020-01-24 15:40           ` Ken Roberts
  0 siblings, 0 replies; 49+ messages in thread
From: Ken Roberts @ 2020-01-24 15:40 UTC (permalink / raw)


On Friday, January 24, 2020 at 7:22:10 AM UTC-8, Simon Wright wrote:
> Ken Roberts <snip> writes:
> 
> > Based upon the hardware I worked on (and documentation for the 642
> > system), apparently it's BE based:
> >
> > Register representation on panel: R bits 29 .. 0
> >
> > where MSB is on the left and LSB on the right.
> 
> Looks LE to me.
> 
> If the panel shows
> 
> bit #   ...  5 4 3 2 1 0
> lit     ...  0 0 0 1 0 1
> 
> and the value represented is 5 then it's little-endian; bit 0 is the
> least significant bit.

My concern would be in bit shifting operations, that if a bit is shifted left 20 bits then:

start: 000000000000000000000000000001

end  : 000000000100000000000000000000

In one instruction for the 642, it combines two registers to make one 60-bit register for some operations, so representing both 30-bit and 60-bit register operations should be the same (assuming a 32-bit computer that should have both 32-bit and 64-bit operations).

At least that's what will have to be accomplished in the emulation.

As noted earlier, the first pass of what I'm working on is the software emulation, but the long-term goal is to fully emulate the 642 computer using register manipulations rather than just emulating the instructions.

At least for now, the target hardware for compiling the system will probably be a minimum 32-bit hardware (like the current intel 32/64 bit chipsets).

One of the USQ20 system is also an 18-bit computer (CP789) - but I don't plan on emulating that for a while yet (if at all - since the 789 was used to manage the KCMX, which was the interface to the older analog fire control systems and not really needed for what I'm initially targeting the project for).

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-23 21:39 ` Optikos
  2020-01-24  9:35   ` Ken Roberts
@ 2020-01-24 15:54   ` Simon Wright
  1 sibling, 0 replies; 49+ messages in thread
From: Simon Wright @ 2020-01-24 15:54 UTC (permalink / raw)


Optikos <ZUERCHER_Andreas@outlook.com> writes:

> Instead of depending on a big-language solution of a very-feature-rich
> compiler, there is a common industrial practice in both Ada and other
> languages that I mentioned in my 2 replies: use 2 different packages
> with same-named identifiers (especially functions or procedures) to
> link in either the little-endian implementation or big-endian
> implementation

An alternative approach (which I've now abandoned in favour of the GNAT
Scalar_Storage_Order) was, given e.g.

   type SNTP_Timestamp is delta 2.0 ** (-32) range -2.0 ** 31 .. 2.0 ** 31;
   for SNTP_Timestamp'Size use 64;

   subtype Timestamp_Slice is Ada.Streams.Stream_Element_Array (1 .. 8);

to provide conversion functions like

   function To_Timestamp_Slice
     (T : SNTP_Timestamp) return Timestamp_Slice is
      function Convert
      is new Ada.Unchecked_Conversion (SNTP_Timestamp,
                                       Timestamp_Slice);
      Tmp : constant Timestamp_Slice := Convert (T);
   begin
      if Big_Endian then
         return Tmp;
      else
         return (1 => Tmp (8),
                 2 => Tmp (7),
                 3 => Tmp (6),
                 4 => Tmp (5),
                 5 => Tmp (4),
                 6 => Tmp (3),
                 7 => Tmp (2),
                 8 => Tmp (1));
      end if;
   end To_Timestamp_Slice;

(Big_Endian was a compile-time constant; GNAT is clever enough not to
generate any object code for the "other" branch).

It got a bit more interesting where there were bit fields involved; the
two branches can declare appropriately-represented derived types and use
type conversion between base and derived type to get the compiler to do
the hard work. See e.g. [1] starting at line 43.

[1] https://sourceforge.net/p/coldframe/adasntp/code/ci/Rel_20070311/tree/SNTP.impl/sntp_support.adb


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-18  7:32 Creating several types from a base type and conversion Ken Roberts
                   ` (3 preceding siblings ...)
  2020-01-23 21:39 ` Optikos
@ 2020-01-25 10:37 ` Ken Roberts
  2020-01-25 10:44   ` Ken Roberts
  2020-01-25 20:26   ` Shark8
  2020-01-27 14:10 ` Ken Roberts
  5 siblings, 2 replies; 49+ messages in thread
From: Ken Roberts @ 2020-01-25 10:37 UTC (permalink / raw)


It might be that I can only really concentrate on learning Ada on my weekends (thursday night -> saturday) subject to $HoneyDoList.

Needless to say, I'm not always the brightest nowadays between age and free time without some extra help, so sorry for seeming a little dense right now.

Here's the computer representation as expressed in documentation and how I'm used to working on the hardware:

30-bit words (both memory storage and hardware registers)

Bit Number : 29 28 .. 01 00
MSBit on left, LSBit on right

30-bit words using half-word (memory access only)

Bit number:  29 28 .. 16 15 14 13 .. 01 00
Half word : |    Upper     |    Lower     |

Instruction format:

Bit number: | 29 .. 24 | 23 .. 21 | 20 .. 18 | 17 .. 15 | 14 .. 0 |
Field     : | Op code  |    J     |    K     |    B     |    Y    |

Op code : Instruction
j       : Skip next instruction modification
k       : Source or destination of data
b       : Index register to use to modify Y
y       : Constant or memory address


Sample program 1 (all numbering is octal notation):

<code>

| Address | Contents    | Description
| 00600   | 12130 00601 | f = 12  : Opcode (Load A register)
|         |             | j = 1   : 1 = always skip next instruction
|         |             | k = 3   : y is memory address, 30 bits
|         |             | y = 601 : Data
| 00601   | 00005 00001 | Data
|         |             | A register initial: 00005 00001
| 00602   | 06000 00017 | f = 06  : Opcode (Shift A left circular)
|         |             | j = 0   : Execute next instruction
|         |             | k = 0   : Shift count is y
|         |             | y = 17  : Data (17 octal, 15 decimal)
|         |             | A register final: 00001 00005

</code>

Sample program using 60-bit register instruction:

60-bit register
Bit number     : | 59 .. 30 | 29 .. 00 |
Registers used : | A reg    | Q reg    |

<code>

| Address | Contents    | Description
| 00600   | 10000 00001 | Load Q register
|         |             | Q register = 00000 00001
| 00601   | 11000 00005 | Load A register
|         |             | A register = 00000 00005
| 00602   | 07000 00036 | Left shift AQ register circular
|         |             | Shift count (36 octal, 30 decimal)
|         |             |
|         |             | AQ initial : 00000 00005 00000 00001
|         |             | A register initial = 00000 00005
|         |             | Q register initial = 00000 00001
|         |             |
|         |             | AQ final   : 00000 00001 00000 00005
|         |             | A register final = 00000 00001
|         |             | Q register final = 00000 00005

</code>

My programming environment is:

Intel-based system
64-bit Fedora based KDE desktop system
Fedora repository has the GNAT compiler suite (GCC Ada - AdaCore?)

With these considerations in mind, I'm trying to figure out the best way to represent the computer hardware (register and memory).

The register and memory base word would be a 30-bit quantity (Interfaces.unsigned_32 possibly?) with 2**30 bit limit, so all register/memory representations would be easily convertible.

Memory representation will be an array (0 .. #8#77777# or 32,768 words) of 30-bit words.

The 60-bit instructions (there's only 2 shift instructions, multiply, which puts answer in QA register(s)) and divide, which uses combined QA register for data, and puts answer in Q and remainder in A))

Based on Niklas Holsti's recommendation, it looks like the following would be what I want to do:

<code>

package core is

with Interfaces;

subtype Word is Interfaces.Unsigned_32 with range 0 .. 2**30 - 1;

subtype D_Word is Interfaces.Unsigned_64 with record
    Ru : Word;
    Rl : Word;
end record

for D_Word use record
    Ru : at 0 use 59 .. 30;
    Rl : at 0 use 29 .. 0;
end record

-- NOTE: I_Word is basically read-only
--       The only use is to make it easier to break down Word into
--       the fields to interpret the instruction and will not change
--       during the instruction execution cycle

I_Word is Word with record
    -- Not sure how to define this part yet
    -- See below for breakdown of bits-to-fields
end record

for I_Word use record
    f : at 0 range 29 .. 24;
    j : at 0 range 23 .. 21;
    k : at 0 range 20 .. 18;
    b : at 0 range 17 .. 15;
    y : at 0 range 14 .. 00;
end record

end package core;

</code>

With the above representation, I can use a D_Word for shifting
and be able to ignore unused 32/64 bits in hardware

<code>

-- Circular left shift register
procedure shift_left_logical (R     : <> Word'Class, 
                              Count : in Integer) is
    private
        D : D_Word;

        -- NOTE: By putting the same contents into both Ru and Rl
        --       I don't have to worry about extra bits from Unsigned_32
        D.Ru := R;
        D.Rl := R;

        -- NOTE: Only bits 5 .. 0 are used, the rest are ignored
        -- Add option to limit actual shifting to mod 30 since anything
        -- over 30 would just wrap around again
        -- ex: Count == 31  is equivalent to Count == 1
        --     on a 30-bit register
        if Count > 8#77# then
           C : Integer (Count and 8#77#);
        else
           C : Integer (Count);
        end if Count;

    end private;

    begin
        D.Shift_Left(C);

        -- Set the register to new value
        R := D.Ru;

    end shift_left_logical;

</code>

For the double-register shift

<code>

-- Circular left shift double register
procedure double_shift_left_logical (R1 : <> Word'Class, 
                                     R2 : <> Word'Class,
                                     Count : in Integer) is
    private
        D1 : D_Word;
        D2 : D_Word;

        -- Normal register order so lower register bits go to upper register
        D1.Ru := R1;  -- A register
        D1.Rl := R2;  -- Q register

        -- Reverse the register order so high register bits go to low register
        D2.Ru := R2;  -- Q register
        D2.Rl := R1;  -- A register

        -- NOTE: Only bits 5 .. 0 are used, the rest are ignored
        -- Add option to limit actual shifting to mod 60 since anything
        -- over 60 would just wrap around again
        -- ex: Count == 61  is equivalent to Count == 1
        --     on a 60-bit register
        if Count > 8#77# then
           C : Integer (Count and 8#77#);
        else
           C : Integer (Count);
        end if Count;

    end private;

    begin
        D1.Shift_Left(C);  -- AQ register pair
        D2.Shift_Left(C);  -- QA register pair

        -- Set the registers to new values
        R1 := D1.Ru;  -- A register
        R2 := D2.Ru;  -- Q Register

    end shift_left_logical;

</code>

This is from my limited understanding so far.

Thanks for all of your help - I wish I could catch on a little quicker, but age and $DayJob (plus $HoneyDoList) make learning a new language a little challenging lately.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-25 10:37 ` Ken Roberts
@ 2020-01-25 10:44   ` Ken Roberts
  2020-01-25 20:26   ` Shark8
  1 sibling, 0 replies; 49+ messages in thread
From: Ken Roberts @ 2020-01-25 10:44 UTC (permalink / raw)


On Saturday, January 25, 2020 at 2:37:17 AM UTC-8, Ken Roberts wrote:
<snip>

Before I get nitpicked:
> 
> For the double-register shift
> 
> <code>
> 
> -- Circular left shift double register
> procedure double_shift_left_logical (R1 : <> Word'Class, 
>                                      R2 : <> Word'Class,
>                                      Count : in Integer) is

<snip>

>     end shift_left_logical;

> 
> </code>

Yes - this should be

     end double_shift_left_logical;

C&P w/memory fart


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-25 10:37 ` Ken Roberts
  2020-01-25 10:44   ` Ken Roberts
@ 2020-01-25 20:26   ` Shark8
  1 sibling, 0 replies; 49+ messages in thread
From: Shark8 @ 2020-01-25 20:26 UTC (permalink / raw)


On Saturday, January 25, 2020 at 3:37:17 AM UTC-7, Ken Roberts wrote:
> It might be that I can only really concentrate on learning Ada on my weekends (thursday night -> saturday) subject to $HoneyDoList.
> 
> Needless to say, I'm not always the brightest nowadays between age and free time without some extra help, so sorry for seeming a little dense right now.
That's fine; we all know life happens.

> 
> Here's the computer representation as expressed in documentation and how I'm used to working on the hardware:
> 
> 30-bit words (both memory storage and hardware registers)
> 
> Bit Number : 29 28 .. 01 00
> MSBit on left, LSBit on right
Isn't MSB on left, rather arbitrary?
(eg "Your left or mine?")

What really matters is that you're consistent.

> Instruction format:
> 
> Bit number: | 29 .. 24 | 23 .. 21 | 20 .. 18 | 17 .. 15 | 14 .. 0 |
> Field     : | Op code  |    J     |    K     |    B     |    Y    |
> 
> Op code : Instruction
> j       : Skip next instruction modification
> k       : Source or destination of data
> b       : Index register to use to modify Y
> y       : Constant or memory address

I would recommend that you use a proper enumeration for your op-codes, and a full record for your instruction. The enumeration can help by making use of the required case-coverage (absent an OTHERS option) for case-statements and discriminated-records; the record is an excellent way to "keep things together" and organize things. — If you *do* use a record for the instruction-type, consider a record discriminated on the op-code enumeration.

> 
> Based on Niklas Holsti's recommendation, it looks like the following would be what I want to do:
> 
> <code>
> 
> with Interfaces;
> package core is
> 
> subtype Word is Interfaces.Unsigned_32 with range 0 .. 2**30 - 1;
> 
> subtype D_Word is Interfaces.Unsigned_64 with record
>     Ru : Word;
>     Rl : Word;
> end record
> 
> for D_Word use record
>     Ru : at 0 use 59 .. 30;
>     Rl : at 0 use 29 .. 0;
> end record
> 
> -- NOTE: I_Word is basically read-only
> --       The only use is to make it easier to break down Word into
> --       the fields to interpret the instruction and will not change
> --       during the instruction execution cycle
> 
> I_Word is Word with record
>     -- Not sure how to define this part yet
>     -- See below for breakdown of bits-to-fields
> end record
> 
> for I_Word use record
>     f : at 0 range 29 .. 24;
>     j : at 0 range 23 .. 21;
>     k : at 0 range 20 .. 18;
>     b : at 0 range 17 .. 15;
>     y : at 0 range 14 .. 00;
> end record
> 
> end package core;
> 
> </code>
I_Word's syntax is off you can only do "Type X is new Y with record ..." for tagged types. — in order to make it work as you have it laid out you're going to need the appropriate sized types to occupy those locations.

> -- Circular left shift register
> procedure shift_left_logical (R     : <> Word'Class, 
>                               Count : in Integer) is
You can only use S'Class on tagged types; while you /could/ have your word-types as tagged-types, it's probably a really bad idea considering you indicate you want to model the bits. (Tagged-types have a tag which would throw off your record's bit-layouts.)

> 
> Thanks for all of your help - I wish I could catch on a little quicker, but age and $DayJob (plus $HoneyDoList) make learning a new language a little challenging lately.
Again, it's fine.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Creating several types from a base type and conversion
  2020-01-18  7:32 Creating several types from a base type and conversion Ken Roberts
                   ` (4 preceding siblings ...)
  2020-01-25 10:37 ` Ken Roberts
@ 2020-01-27 14:10 ` Ken Roberts
  5 siblings, 0 replies; 49+ messages in thread
From: Ken Roberts @ 2020-01-27 14:10 UTC (permalink / raw)


After some more reflection (nice thing about having a boring job), I think I've been overthinking the problem.

The registers do not need to be a subtype of Unsigned_32 (or Word30), since they're going to be containers rather than actual variables. As long as the __data__ they get/return is a subtype of Unsigned_32, the conversion is automatic (from my understanding).

So, for what I'm looking at, I_Word[1-2] is only used by one register (the Instruction register), so they can be worked on using private variables; once the instruction is loaded into the register, the upper half is pretty much read-only with the lower half being the only part that _may_ be returned. The other registers pretty much just use Unsigned_30 as is. For the ones that use half-word (15-bit) parts, easy enough to work with as well since they'll be using subtypes of Unsigned_30.

Goes to show what happens when you over-think the problem.

Thanks for all of your suggestions and help.


^ permalink raw reply	[flat|nested] 49+ messages in thread

end of thread, other threads:[~2020-01-27 14:10 UTC | newest]

Thread overview: 49+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-01-18  7:32 Creating several types from a base type and conversion Ken Roberts
2020-01-18 12:16 ` Simon Wright
2020-01-18 12:49   ` Ken Roberts
2020-01-18 14:56     ` Bill Findlay
2020-01-18 16:13       ` Jeffrey R. Carter
2020-01-18 18:20         ` Bill Findlay
2020-01-18 18:32           ` Jeffrey R. Carter
2020-01-18 20:34             ` Simon Wright
2020-01-20 16:38               ` Bill Findlay
2020-01-18 22:20       ` Ken Roberts
2020-01-18 15:09     ` Simon Wright
2020-01-18 22:16       ` Ken Roberts
2020-01-18 22:35         ` Simon Wright
2020-01-18 23:03           ` Ken Roberts
2020-01-18 23:38             ` Simon Wright
2020-01-19  0:12               ` Ken Roberts
2020-01-19  9:37                 ` Simon Wright
2020-01-19 11:48                   ` AdaMagica
2020-01-19 14:51                     ` Simon Wright
2020-01-19 15:24                       ` Niklas Holsti
2020-01-19 16:11                     ` Optikos
2020-01-19  0:33               ` Ken Roberts
2020-01-19  0:07         ` Niklas Holsti
2020-01-18 15:47     ` Simon Wright
2020-01-21 21:35     ` Shark8
2020-01-21 23:06       ` Niklas Holsti
2020-01-22  1:08         ` Ken Roberts
2020-01-22 14:18           ` Ken Roberts
2020-01-22  8:37       ` Simon Wright
2020-01-22 14:32         ` Shark8
2020-01-22 15:40           ` Simon Wright
2020-01-18 14:17   ` Optikos
2020-01-18 17:57 ` Niklas Holsti
2020-01-18 22:59   ` Ken Roberts
2020-01-19  0:30     ` Niklas Holsti
2020-01-19  1:07       ` Ken Roberts
2020-01-19  3:37 ` Ken Roberts
2020-01-23 21:39 ` Optikos
2020-01-24  9:35   ` Ken Roberts
2020-01-24 10:04     ` AdaMagica
2020-01-24 12:38     ` Optikos
2020-01-24 15:01       ` Ken Roberts
2020-01-24 15:22         ` Simon Wright
2020-01-24 15:40           ` Ken Roberts
2020-01-24 15:54   ` Simon Wright
2020-01-25 10:37 ` Ken Roberts
2020-01-25 10:44   ` Ken Roberts
2020-01-25 20:26   ` Shark8
2020-01-27 14:10 ` Ken Roberts

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox