comp.lang.ada
 help / color / mirror / Atom feed
* Is there a better (Ada) way?
@ 2003-10-22 18:56 Mike Silva
  2003-10-22 19:33 ` sk
                   ` (3 more replies)
  0 siblings, 4 replies; 10+ messages in thread
From: Mike Silva @ 2003-10-22 18:56 UTC (permalink / raw)


I've started writing a little CPU simulator, just for fun and to be
doing *something* in Ada, and I want to make sure I'm not writing "C
in Ada" when it comes to all the bit, bitfield and register
manipulations.

Below is a typical question.  I need to add a 5-bit signed offset to a
16-bit register.  The way I came up with is shown on the last line of
the example code, but I want to find out if there's a more Ada-ish way
to do what I'm doing.  Any comments about any part of the example code
are welcome.

Mike

-------- example code --------

procedure test is
   type Reg16_t is mod 2**16;             -- 16 bit register
   for Reg16_t'Size use 16;
   
   type Offset5_t is range -2**4..2**4-1; -- 5 bit signed offset
   for Offset5_t'Size use 5;
   
   type Dummy_t is mod 2**3;              -- just take up extra 3 bits
in byte, for this example
   for Dummy_t'Size use 3;
   
   type Postbyte_t is                   -- typical opcode postbyte
      record
         dummy   : Dummy_t;
         offset5 : Offset5_t;
      end record;
   
   for Postbyte_t use
      record
         dummy     at 0 range 5..7;
         offset5   at 0 range 0..4;
      end record;
      
   for Postbyte_t'Size use 8;

   R16 : Reg16_t;
   PB    : Postbyte_t;
begin
   ....
   R16 := R16 + Reg16_t( Integer( PB.offset5 ) mod Reg16_t'Modulus );
-- the above line works, but is it good Ada, or C-in-Ada?



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

* Re: Is there a better (Ada) way?
  2003-10-22 18:56 Is there a better (Ada) way? Mike Silva
@ 2003-10-22 19:33 ` sk
  2003-10-23 18:33   ` Mike Silva
  2003-10-22 20:50 ` Robert I. Eachus
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 10+ messages in thread
From: sk @ 2003-10-22 19:33 UTC (permalink / raw)
  To: comp.lang.ada

Mike Silva <snarflemike@yahoo.com>:
 > ... sure I'm not writing "C in Ada" ...

I was playing with an assembler written in Ada a while
ago. I also played with a small kernel.

After trying many awkward Ada ways, I came to the
conclusion that sometimes the "C" (masking, shifting
etc) way was a lot cleaner to look at, read and maintain
than hammering it into an "Ada way" of doing it.

So maybe, at the bit-twiddling level, C-in-Ada might
not be a bad way to go.


-- 
-------------------------------------------------
-- Merge vertically for real address
--
--     s n p @ t . o
--      k i e k c c m
-------------------------------------------------




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

* Re: Is there a better (Ada) way?
  2003-10-22 18:56 Is there a better (Ada) way? Mike Silva
  2003-10-22 19:33 ` sk
@ 2003-10-22 20:50 ` Robert I. Eachus
  2003-10-23 16:57   ` Mike Silva
  2003-10-23  3:12 ` Steve
  2003-10-24 22:20 ` Nick Roberts
  3 siblings, 1 reply; 10+ messages in thread
From: Robert I. Eachus @ 2003-10-22 20:50 UTC (permalink / raw)


Mike Silva wrote:
> I've started writing a little CPU simulator, just for fun and to be
> doing *something* in Ada, and I want to make sure I'm not writing "C
> in Ada" when it comes to all the bit, bitfield and register
> manipulations.
> 
> Below is a typical question.  I need to add a 5-bit signed offset to a
> 16-bit register.  The way I came up with is shown on the last line of
> the example code, but I want to find out if there's a more Ada-ish way
> to do what I'm doing.  Any comments about any part of the example code
> are welcome.

>    R16 : Reg16_t;
>    PB    : Postbyte_t;
> begin
>    ....
>    R16 := R16 + Reg16_t( Integer( PB.offset5 ) mod Reg16_t'Modulus );
> -- the above line works, but is it good Ada, or C-in-Ada?

Probably not.

    R16 := R16 + Reg16_t(PB.offset5);

Should do exactly the same thing--but it doesn't.  I would probably write:

    R16 := Reg16_t(Integer(R16) + Integer(PB.offset5));

However, you need to think about what you want to happen when you add a 
positive offset to R16'Last, or a negative offset to Reg16'First.  If 
you do want wraparound semantics, you can probably suppress 
Constraint_Error and use the first line I supplied.

And incidently, it is pretty common in Ada when doing things like this 
that you have to think about which exceptional cases you don't want 
detected, instead of which special cases you do want to handle.


-- 
                                                     Robert I. Eachus

"Quality is the Buddha. Quality is scientific reality. Quality is the 
goal of Art. It remains to work these concepts into a practical, 
down-to-earth context, and for this there is nothing more practical or 
down-to-earth than what I have been talking about all along...the repair 
of an old motorcycle."  -- from Zen and the Art of Motorcycle 
Maintenance by Robert Pirsig




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

* Re: Is there a better (Ada) way?
  2003-10-22 18:56 Is there a better (Ada) way? Mike Silva
  2003-10-22 19:33 ` sk
  2003-10-22 20:50 ` Robert I. Eachus
@ 2003-10-23  3:12 ` Steve
  2003-10-23 18:30   ` Mike Silva
  2003-10-24 22:20 ` Nick Roberts
  3 siblings, 1 reply; 10+ messages in thread
From: Steve @ 2003-10-23  3:12 UTC (permalink / raw)


I don't know if its really more "Ada like" but here is the approach I would
take.

Rougly following your conventions I would add a type definition:

  type SReg16_T is range -2**15..2**15-1;
  for SReg16_T'size use 16;

Then use a type conversion for a sign extend:
  SReg16_T( PB.Offset5 )

And an instance of unchecked conversion to convert to a SReg16_T to a
Reg16_T:

function To_Reg16_T is new Ada.Unchecked_Conversion( SReg16_T, Reg16_T );

Then the expression in your example would read:

R16 := R16 + To_Reg16_T( SReg16_T( PB.Offset5 ) );

Another approach would be to use a lookup table indexed by the offset giving
the amount to change the index.  Something like:

  type RegOff_T is array( Offset5_T ) of Reg16_T;
  Reg_Offset : constant RegOff_T :=
  (
    -16 =>   16#FFF0#,
    -15 =>   16#FFF1#,
    -14 =>   16#FFF2#,
    -13 =>   16#FFF3#,
    -12 =>   16#FFF4#,
    -11 =>   16#FFF5#,
    -10 =>   16#FFF6#,
    -9  =>   16#FFF7#,
    -8  =>   16#FFF8#,
    -7  =>   16#FFF9#,
    -6  =>   16#FFFA#,
    -5  =>   16#FFFB#,
    -4  =>   16#FFFC#,
    -3  =>   16#FFFD#,
    -2  =>   16#FFFE#,
    -1  =>   16#FFFF#,
     0  =>   16#0000#,
     1  =>   16#0001#,
     2  =>   16#0002#,
     3  =>   16#0003#,
     4  =>   16#0004#,
     5  =>   16#0005#,
     6  =>   16#0006#,
     7  =>   16#0007#,
     8  =>   16#0008#,
     9  =>   16#0009#,
     10 =>   16#000A#,
     11 =>   16#000B#,
     12 =>   16#000C#,
     13 =>   16#000D#,
     14 =>   16#000E#,
     15 =>   16#000F#
  );

Then the expression in your example would read:

R16 := R16 + Reg_Offset( PB.Offset5 );

If you actually want to try to run the simulator, I'd try a few approaches
and pick the one that is faster.

BTW:  There is a nice module built in to Ada95 called "Interfaces" it
defines stuff like Unsigned_16, etc.  I prefer to use that over defining my
own similar types.

Steve
(The Duck)


"Mike Silva" <snarflemike@yahoo.com> wrote in message
news:20619edc.0310221056.4c92d10c@posting.google.com...
> I've started writing a little CPU simulator, just for fun and to be
> doing *something* in Ada, and I want to make sure I'm not writing "C
> in Ada" when it comes to all the bit, bitfield and register
> manipulations.
>
[snip]
>    ....
>    R16 := R16 + Reg16_t( Integer( PB.offset5 ) mod Reg16_t'Modulus );
> -- the above line works, but is it good Ada, or C-in-Ada?





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

* Re: Is there a better (Ada) way?
  2003-10-22 20:50 ` Robert I. Eachus
@ 2003-10-23 16:57   ` Mike Silva
  2003-10-23 21:42     ` Robert I. Eachus
  0 siblings, 1 reply; 10+ messages in thread
From: Mike Silva @ 2003-10-23 16:57 UTC (permalink / raw)


"Robert I. Eachus" <rieachus@comcast.net> wrote in message news:<3F96ED50.2040009@comcast.net>...
> >    R16 := R16 + Reg16_t( Integer( PB.offset5 ) mod Reg16_t'Modulus );
> > -- the above line works, but is it good Ada, or C-in-Ada?
> 
>     R16 := R16 + Reg16_t(PB.offset5);
> 
> Should do exactly the same thing--but it doesn't.

Do you mean that the language should have been defined so that the
conversion in the 2nd line acts like the 1st, with negative numbers? 
Or ???

Mike



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

* Re: Is there a better (Ada) way?
  2003-10-23  3:12 ` Steve
@ 2003-10-23 18:30   ` Mike Silva
  0 siblings, 0 replies; 10+ messages in thread
From: Mike Silva @ 2003-10-23 18:30 UTC (permalink / raw)


"Steve" <nospam_steved94@comcast.net> wrote in message news:<%GHlb.1106$mZ5.9467@attbi_s54>...
> I don't know if its really more "Ada like" but here is the approach I would
> take.
> 
> Rougly following your conventions I would add a type definition:
> 
>   type SReg16_T is range -2**15..2**15-1;
>   for SReg16_T'size use 16;
> 
> Then use a type conversion for a sign extend:
>   SReg16_T( PB.Offset5 )
> 
> And an instance of unchecked conversion to convert to a SReg16_T to a
> Reg16_T:
> 
> function To_Reg16_T is new Ada.Unchecked_Conversion( SReg16_T, Reg16_T );
> 
> Then the expression in your example would read:
> 
> R16 := R16 + To_Reg16_T( SReg16_T( PB.Offset5 ) );
> 
Yes, I like this better -- in fact, I came up with the same thing
myself last night in trying various alternatives.  Thanks for
confirming my instincts!

(I also have to deal with signed 8-bit offsets, which pretty much puts
the array method out of the running for me...)

Mike



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

* Re: Is there a better (Ada) way?
  2003-10-22 19:33 ` sk
@ 2003-10-23 18:33   ` Mike Silva
  0 siblings, 0 replies; 10+ messages in thread
From: Mike Silva @ 2003-10-23 18:33 UTC (permalink / raw)


sk <noname@myob.com> wrote in message news:<mailman.179.1066850672.25614.comp.lang.ada@ada-france.org>...
> Mike Silva <snarflemike@yahoo.com>:
>  > ... sure I'm not writing "C in Ada" ...
> 
> I was playing with an assembler written in Ada a while
> ago. I also played with a small kernel.
> 
> After trying many awkward Ada ways, I came to the
> conclusion that sometimes the "C" (masking, shifting
> etc) way was a lot cleaner to look at, read and maintain
> than hammering it into an "Ada way" of doing it.
> 
> So maybe, at the bit-twiddling level, C-in-Ada might
> not be a bad way to go.
> 
I understand what you're saying, but I do want to force myself to do
this in the spirit of Ada for the learning value.  Maybe I'll learn
that you're right! :)



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

* Re: Is there a better (Ada) way?
  2003-10-23 16:57   ` Mike Silva
@ 2003-10-23 21:42     ` Robert I. Eachus
  2003-10-24  1:42       ` Mike Silva
  0 siblings, 1 reply; 10+ messages in thread
From: Robert I. Eachus @ 2003-10-23 21:42 UTC (permalink / raw)


Mike Silva wrote:
> "Robert I. Eachus" <rieachus@comcast.net> wrote in message news:<3F96ED50.2040009@comcast.net>...
> 
>>>   R16 := R16 + Reg16_t( Integer( PB.offset5 ) mod Reg16_t'Modulus );
>>>-- the above line works, but is it good Ada, or C-in-Ada?
>>
>>    R16 := R16 + Reg16_t(PB.offset5);
>>
>>Should do exactly the same thing--but it doesn't.
> 
> 
> Do you mean that the language should have been defined so that the
> conversion in the 2nd line acts like the 1st, with negative numbers? 
> Or ???
> 
> Mike

Yes.  I believe that this will be "fixed" in the next version of the 
standard, but for now you will find compilers that raise 
Constraint_Error when converting a negative value to a modular type. 
Suppressing Constraint_Error for this one line makes it all come out 
right, if there is nothing that should be a Constraint_Error.

It is one of those nasty complex glitches that make language definition 
so hard.  There may not be a valid representation for a negative value 
of an unsigned type, so it looks like the "right" rule.  But in reality, 
there must be a valid representation of the current value--the current 
one--and arithmetic works correctly after a possible sign extension for 
modular types with binary modulii.

Incidently I thought of what may be a better way to do it, but to do it 
efficiently is tricky.  Define a sign extention intrinsic operation, 
then do a normal addition.  This works great on most hardware.  Do a 
left shift that puts the bits in the high order position, then a sign 
extending right shift.  Not great on a Pentium 4, but most other 
processors have a barrel-shifter that can do the two instructions quickly.

And you can always write:

if PB.offset5 >= 0
then R16 := R16 + Reg16_t(PB.offset5);
else R16 := R16 - Reg16_t(-PB.offset5);
end if;

On some compilers you will get better performance than the version with 
the explicit mod operation, since even 16-bit division tends to be slow. 
   But as I said the best code is probably to use pragma 
Suppress(Range_Check, Reg16_t);  I can't imagine any case other than 
this where you might want the check to be made for a binary modular type 
like Reg16_t, so you can probably put the pragma with the type declaration.

Hmmm, thinking more about it, the compiler is not required to do the 
sign extension in that case, but the following should always work right:

function To_Reg16(Value: in Offset5_t) return Reg16_t is
   type Integer16 is range -2**15..2**15-1;
   for Integer16'Size use 16;
   function UC is new Ada.Unchecked_Conversion(Integer16, Reg16_t);
begin return UC(Integer16(Value)); end To_Reg16;
pragma Inline(To_Reg16);

A lot of work to eliminate one mod operation, but now you can write:

   R16 := To_Reg16(PB.offset5);

And be sure it is portable.  If you have a lot of these operations, it 
may be worth it.  Of course, it will take an awful lot of instances to 
justify all this analysis, but I think this is all covered under the 
learning experience heading.

The lesson that should be coming through is that Ada programmers care a 
lot about making the code look pretty and easy to read and understand. 
That doesn't mean that we don't care about efficiency.  We just go out 
of our way to hide the messy parts of the code in some function hidden 
in some package body.  Oh, and that Ada programmers tend to have trouble 
sleeping if they write code that is compiler dependent when it doesn't 
have to be. ;-)

-- 
                                           Robert I. Eachus

"Quality is the Buddha. Quality is scientific reality. Quality is the 
goal of Art. It remains to work these concepts into a practical, 
down-to-earth context, and for this there is nothing more practical or 
down-to-earth than what I have been talking about all along...the repair 
of an old motorcycle."  -- from Zen and the Art of Motorcycle 
Maintenance by Robert Pirsig




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

* Re: Is there a better (Ada) way?
  2003-10-23 21:42     ` Robert I. Eachus
@ 2003-10-24  1:42       ` Mike Silva
  0 siblings, 0 replies; 10+ messages in thread
From: Mike Silva @ 2003-10-24  1:42 UTC (permalink / raw)


"Robert I. Eachus" <rieachus@comcast.net> wrote in message news:<3F984AF5.6030703@comcast.net>...
> Mike Silva wrote:
> > "Robert I. Eachus" <rieachus@comcast.net> wrote in message news:<3F96ED50.2040009@comcast.net>...
> > 
> >>>   R16 := R16 + Reg16_t( Integer( PB.offset5 ) mod Reg16_t'Modulus );
> >>>-- the above line works, but is it good Ada, or C-in-Ada?
> >>
> >>    R16 := R16 + Reg16_t(PB.offset5);
> >>
> >>Should do exactly the same thing--but it doesn't.
> > 
> > 
> > Do you mean that the language should have been defined so that the
> > conversion in the 2nd line acts like the 1st, with negative numbers? 
> > Or ???
> > 
> > Mike
> 
> Yes.  I believe that this will be "fixed" in the next version of the 
> standard, but for now you will find compilers that raise 

<good stuff snipped>

Robert, thanks a bunch for your insights.  Although I'm doing this
mostly for my own amusement/edification, I also want to do this
cleanly enough that it "shows off" Ada to those who would never
imagine looking at Ada for bit-twiddling.  BTW, the processor I'm
simulating is my favorite 8-bitter, the ill-fated 6809.  Thanks again.

Mike



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

* Re: Is there a better (Ada) way?
  2003-10-22 18:56 Is there a better (Ada) way? Mike Silva
                   ` (2 preceding siblings ...)
  2003-10-23  3:12 ` Steve
@ 2003-10-24 22:20 ` Nick Roberts
  3 siblings, 0 replies; 10+ messages in thread
From: Nick Roberts @ 2003-10-24 22:20 UTC (permalink / raw)


Mike Silva wrote:

> I've started writing a little CPU simulator, just for fun and to be
> doing *something* in Ada, and I want to make sure I'm not writing "C
> in Ada" when it comes to all the bit, bitfield and register
> manipulations.
> 
> Below is a typical question.  I need to add a 5-bit signed offset to a
> 16-bit register.  The way I came up with is shown on the last line of
> the example code, but I want to find out if there's a more Ada-ish way
> to do what I'm doing.  Any comments about any part of the example code
> are welcome.
> 
> Mike
> 
> -------- example code --------
> 
> procedure test is
>    type Reg16_t is mod 2**16;             -- 16 bit register
>    for Reg16_t'Size use 16;
>    
>    type Offset5_t is range -2**4..2**4-1; -- 5 bit signed offset
>    for Offset5_t'Size use 5;
>    
>    type Dummy_t is mod 2**3;              -- just take up extra 3 bits
> in byte, for this example
>    for Dummy_t'Size use 3;
>    
>    type Postbyte_t is                   -- typical opcode postbyte
>       record
>          dummy   : Dummy_t;
>          offset5 : Offset5_t;
>       end record;
>    
>    for Postbyte_t use
>       record
>          dummy     at 0 range 5..7;
>          offset5   at 0 range 0..4;
>       end record;
>       
>    for Postbyte_t'Size use 8;
> 
>    R16 : Reg16_t;
>    PB    : Postbyte_t;
> begin
>    ....
>    R16 := R16 + Reg16_t( Integer( PB.offset5 ) mod Reg16_t'Modulus );
> -- the above line works, but is it good Ada, or C-in-Ada?

I would be inclined to create an explicit signed integer type of the 
appropriate size:

procedure test is

    type Unsigned_16 is mod 2**16;
    for Unsigned_16'Size use 16;

    type Offset_5 is range -2**4..2**4-1;
    for Offset_5'Size use 5;

    type Signed_16 is range -2**15..2**15-1;
    for Signed_16'Size use 16;

    type Jump_Postbyte is
       record
          Offset: Offset_5;
       end record;

    for Jump_Postbyte use
       record
          Offset at 0 range 0..4;
       end record;

    for Jump_Postbyte'Size use 8;

    R16 : Unsigned_16;
    PB  : Jump_Postbyte;

begin
    ....
    R16 := R16 + Unsigned_16( Signed_16(PB.Offset)+2**4 ) - 2**4;

This formulation gives wrap-around semantics, which is the likely choice 
for a hard implementation.

Ada 95 doesn't provide signed-to-modular integer addition. Perhaps the next 
revision should. (There's a thought ;-)

-- 
Nick Roberts




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

end of thread, other threads:[~2003-10-24 22:20 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2003-10-22 18:56 Is there a better (Ada) way? Mike Silva
2003-10-22 19:33 ` sk
2003-10-23 18:33   ` Mike Silva
2003-10-22 20:50 ` Robert I. Eachus
2003-10-23 16:57   ` Mike Silva
2003-10-23 21:42     ` Robert I. Eachus
2003-10-24  1:42       ` Mike Silva
2003-10-23  3:12 ` Steve
2003-10-23 18:30   ` Mike Silva
2003-10-24 22:20 ` Nick Roberts

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