Memory layout of a data structure

From Rosetta Code
Revision as of 23:00, 25 June 2010 by rosettacode>Cferri (Added a solution for MATLAB)
Task
Memory layout of a data structure
You are encouraged to solve this task according to the task description, using any language you may know.

It is often useful to control the memory layout of fields in a data structure to match an interface control definition, or to interface with hardware. Define a data structure matching the RS-232 Plug Definition. Use the 9-pin definition for brevity.

Pin Settings for Plug
(Reverse order for socket.)
__________________________________________
1  2  3  4  5  6  7  8  9  10 11 12 13
14 15 16 17 18 19 20 21 22 23 24 25
_________________
1  2  3  4  5
6  7  8  9
25 pin                        9 pin
1 - PG   Protective ground
2 - TD   Transmitted data     3
3 - RD   Received data        2
4 - RTS  Request to send      7
5 - CTS  Clear to send        8
6 - DSR  Data set ready       6
7 - SG   Signal ground        5
8 - CD   Carrier detect       1
9 - + voltage (testing)
10 - - voltage (testing)
11 -
12 - SCD  Secondary CD
13 - SCS  Secondary CTS
14 - STD  Secondary TD
15 - TC   Transmit clock
16 - SRD  Secondary RD
17 - RC   Receiver clock
18 -
19 - SRS  Secondary RTS            
20 - DTR  Data terminal ready      4
21 - SQD  Signal quality detector
22 - RI   Ring indicator           9
23 - DRS  Data rate select
24 - XTC  External clock
25 -

Ada

<lang ada>type Bit is mod 2; type Rs_232_Layout is record

  Carrier_Detect      : Bit;
  Received_Data       : Bit;
  Transmitted_Data    : Bit;
  Data_Terminal_ready : Bit;
  Signal_Ground       : Bit;
  Data_Set_Ready      : Bit;
  Request_To_Send     : Bit;
  Clear_To_Send       : Bit;
  Ring_Indicator      : Bit;

end record;

for Rs_232_Layout use record

  Carrier_Detect      at 0 range 0..0;
  Received_Data       at 0 range 1..1;
  Transmitted_Data    at 0 range 2..2;
  Data_Terminal_Ready at 0 range 3..3;
  Signal_Ground       at 0 range 4..4;
  Data_Set_Ready      at 0 range 5..5;
  Request_To_Send     at 0 range 6..6;
  Clear_To_Send       at 0 range 7..7;
  Ring_Indicator      at 0 range 8..8;

end record;</lang>

ALGOL 68

Works with: ALGOL 68 version Standard - no extensions to language used
Works with: ALGOL 68G version Any - tested with release mk15-0.8b.fc9.i386

<lang algol68>MODE RSTWOTHREETWO = BITS; INT

  lwb rs232           = 1,
  carrier detect      = 1,
  received data       = 2,
  transmitted data    = 3,
  data terminal ready = 4,
  signal ground       = 5,
  data set ready      = 6,
  request to send     = 7,
  clear to send       = 8,
  ring indicator      = 9,
  upb rs232           = 9;

RSTWOTHREETWO rs232 bits := 2r01000000000000000000000000000000; # up to bits width, OR # rs232 bits := bits pack((FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE));

print(("received data: ",received data ELEM rs232bits))</lang> Output: <lang algol68>received data: T</lang>

C/C++

Note: The order of the fields is implementation-defined (i.e. the first bit might be the least-significant one or the most-significant one). On GCC and MSVC++, the first bit is the least-significant one. <lang c>struct RS232_data {

 unsigned carrier_detect        : 1;
 unsigned received_data         : 1;
 unsigned transmitted_data      : 1;
 unsigned data_terminal_ready   : 1;
 unsigned signal_ground         : 1;
 unsigned data_set_ready        : 1;
 unsigned request_to_send       : 1;
 unsigned clear_to_send         : 1;
 unsigned ring_indicator        : 1;

}; </lang> The ":1" gives the number of allocated bits. For unused bits (e.g. pin 11 in the 25-pin version above) the field name can be omitted.

Since as stated before the order of bits can't be assured but it could be important if we need to interact with hardware, the best way is to define bit masks; of course actual writing/reading to/from an hardware "register" greater than a single byte must be done taking care of endianness.

D

Implementation uses tango's BitArray structure.

<lang D> module controlFieldsInStruct;

import tango.core.BitArray; import tango.io.Stdout; import tango.text.convert.Integer;

class RS232Wrapper(int Length = 9) {

   static assert(Length == 9 || Length == 25, "ERROR, wrong type");
   BitArray ba; 
   static uint[char[]] _map;

   public:
   static if (Length == 9) {
       static this() {
           _map = [ cast(char[])
               "CD"  : 1, "RD"  : 2, "TD"  : 3, "DTR" : 4, "SG"  : 5,
               "DSR" : 6, "RTS" : 7, "CTS" : 8, "RI"  : 9 
           ];  
       }   
   } else {
       static this() {
           _map = [ cast(char[])
               "PG"  : 1u, "TD"  :  2, "RD"  :  3, "RTS" :  4, "CTS" :  5,  
               "DSR" :  6, "SG"  :  7, "CD"  :  8, "+"   :  9, "-"   : 10, 
               "SCD" : 12, "SCS" : 13, "STD" : 14, "TC"  : 15, "SRD" : 16, 
               "RC"  : 17, "SRS" : 19, "DTR" : 20, "SQD" : 21, "RI"  : 22, 
               "DRS" : 23, "XTC" : 24
           ];  
       }   
   }   


   this() {
       ba.length = Length;
   }   
   bool opIndex(uint pos) { return ba[pos]; }
   bool opIndexAssign(bool b, uint pos) { return (ba[pos] = b); }
   bool opIndex(char[] name) {
       assert (name in _map, "don't know that plug: " ~ name);
       return opIndex(_map[name]);
   }   
   bool opIndexAssign(bool b, char[] name) {
       assert (name in _map, "don't know that plug: " ~ name);
       return opIndexAssign(b, _map[name]);
   }   
   void opSliceAssign(bool b) { foreach (ref r; ba) r = b; }
   char[] toString() {
       char[] ret = "[";
       foreach (name, value; _map)
           ret ~= name ~ ":" ~ (ba[value]?"1":"0") ~", ";
       ret ~= "]";
       return ret;
   }   

}

int main(char[][] args) {

   auto ba = new RS232Wrapper!(25);
   // set all bits
   ba[] = 1;
   ba["RD"] = 0;
   ba[5] = 0;
   Stdout (ba).newline;
   return 0;

} </lang>

Output:

[RD:0, RI:1, DSR:1, SG:1, DTR:1, TC:1, TD:1, CD:1, SQD:1, +:1, -:1, SRD:1, RTS:1, SRS:1, STD:1, PG:1, SCD:1, CTS:0, DRS:1, SCS:1, XTC:1, RC:1 ]

Forth

Low level hardware control is a typical use of Forth. None of this is standard, however, since hardware I/O mechanisms differ on different systems. Forth does not have a structure mechanism, much less bitfields. These would be represented instead via bitmask constants if doing real serial port control.

: masks ( n -- ) 0 do 1 i lshift constant loop ;

9 masks DCD RxD TxD DTR SG DSR RTS CTS RI

Example usage, assuming I/O primitives in and out:

hex
3fd constant com1-ctrl
decimal

: wait-ready
  begin
    com1-ctrl in
    CTS and
  until ;
: wait-rx
  begin
    com1-ctrl in
    CTS and 0=
  until ;

: send-byte ( b -- )   \ send assuming N81 (no parity, 8 bits data, 1 bit frame)
  255 and
  9 0 do
    RTS com1-ctrl out
    wait-ready
    dup 1 and if TxD else 0 then com1-ctrl out
    wait-rx
    2/
  loop drop ;
  

Of course, this is a very simplified view of the full RS-232 protocol. Also, although this represents the order of the pins in a D-9 connector, this would not necessarily be the same as the order of the bits in a control register.

J

J does not support "structures", nor "fields in a structure". Instead, J supports arrays. And, of course, J could have labels corresponding to the elements of an array representing the state (voltage, current, logical bit value, whatever) of each pin of a 9-pin RS-232 plug: <lang j>labels=: <;._2]0 :0 CD Carrier detect RD Received data TD Transmitted data DTR Data terminal ready SG Signal ground DSR Data set ready RTS Request to send CTS Clear to send RI Ring indicator )</lang>

MATLAB

Defining structs in MATLAB is kind of bulky, making a class definition might be cleaner for this purpose. If you need to enumerate each pin rather than set the state of the pin using the name of the pin, you can use struct2cell() on the rs232 struct, which will return a cell array whose entries are the value of each of the structs fields in the order in which they were defined.

<lang MATLAB>>> rs232 = struct('carrier_detect', logical(1),... 'received_data' , logical(1), ... 'transmitted_data', logical(1),... 'data_terminal_ready', logical(1),... 'signal_ground', logical(1),... 'data_set_ready', logical(1),... 'request_to_send', logical(1),... 'clear_to_send', logical(1),... 'ring_indicator', logical(1))

rs232 =

        carrier_detect: 1
         received_data: 1
      transmitted_data: 1
   data_terminal_ready: 1
         signal_ground: 1
        data_set_ready: 1
       request_to_send: 1
         clear_to_send: 1
        ring_indicator: 1

>> struct2cell(rs232)

ans =

   [1]
   [1]
   [1]
   [1]
   [1]
   [1]
   [1]
   [1]
   [1]</lang>


OCaml

Library: extlib <lang ocaml> open ExtLib class rs232_data = object

 val d = BitSet.create 9
 method carrier_detect      = BitSet.is_set d 0
 method received_data       = BitSet.is_set d 1
 method transmitted_data    = BitSet.is_set d 2
 method data_terminal_ready = BitSet.is_set d 3
 method signal_ground       = BitSet.is_set d 4
 method data_set_ready      = BitSet.is_set d 5
 method request_to_send     = BitSet.is_set d 6
 method clear_to_send       = BitSet.is_set d 7
 method ring_indicator      = BitSet.is_set d 8
 method set_carrier_detect      b = (if b then BitSet.set else BitSet.unset) d 0
 method set_received_data       b = (if b then BitSet.set else BitSet.unset) d 1
 method set_transmitted_data    b = (if b then BitSet.set else BitSet.unset) d 2
 method set_data_terminal_ready b = (if b then BitSet.set else BitSet.unset) d 3
 method set_signal_ground       b = (if b then BitSet.set else BitSet.unset) d 4
 method set_data_set_ready      b = (if b then BitSet.set else BitSet.unset) d 5
 method set_request_to_send     b = (if b then BitSet.set else BitSet.unset) d 6
 method set_clear_to_send       b = (if b then BitSet.set else BitSet.unset) d 7
 method set_ring_indicator      b = (if b then BitSet.set else BitSet.unset) d 8

end

</lang>

Perl

<lang perl> use Bit::Vector::Minimal qw(); my $vec = Bit::Vector::Minimal->new(size => 24);

my %rs232 = reverse (

    1 => 'PG   Protective ground',
    2 => 'TD   Transmitted data',
    3 => 'RD   Received data',
    4 => 'RTS  Request to send',
    5 => 'CTS  Clear to send',
    6 => 'DSR  Data set ready',
    7 => 'SG   Signal ground',
    8 => 'CD   Carrier detect',
    9 => '+ voltage (testing)',
   10 => '- voltage (testing)',
   12 => 'SCD  Secondary CD',
   13 => 'SCS  Secondary CTS',
   14 => 'STD  Secondary TD',
   15 => 'TC   Transmit clock',
   16 => 'SRD  Secondary RD',
   17 => 'RC   Receiver clock',
   19 => 'SRS  Secondary RTS',
   20 => 'DTR  Data terminal ready',
   21 => 'SQD  Signal quality detector',
   22 => 'RI   Ring indicator',
   23 => 'DRS  Data rate select',
   24 => 'XTC  External clock',

);

$vec->set($rs232{'RD Received data'}, 1); $vec->get($rs232{'TC Transmit clock'}); </lang>

PicoLisp

PicoLisp can handle bit fields or bit structures only as bignums. They can be manipulated with '&', '|' and 'x|', or tested with 'bit?'. <lang PicoLisp># Define bit constants (for (N . Mask) '(CD RD TD DTR SG DSR RTS CTS RI)

  (def Mask (>> (- 1 N) 1)) )
  1. Test if Clear to send

(when (bit? CTS Data)

  ... )</lang>

Python

The ctypes module allows for the creation of Structures that can map between the structures of C and python datatypes. Within Structures, bit fields can be created.

<lang python> from ctypes import Structure, c_int

rs232_9pin = "_0 CD RD TD DTR SG DSR RTS CTS RI".split() rs232_25pin = ( "_0 PG TD RD RTS CTS DSR SG CD pos neg" +

               "_11 SCD SCS STD TC  SRD RC" +
               "_18 SRS DTR SQD RI DRS XTC" ).split()

class RS232_9pin(Structure):

   _fields_ = [(__, c_int, 1) for __ in rs232_9pin]


class RS232_25pin(Structure):

   _fields_ = [(__, c_int, 1) for __ in rs232_25pin]

</lang>

Ruby

Uses the BitStruct module, which is handy but awkward to instantiate objects. <lang ruby>require 'bit-struct'

class RS232_9 < BitStruct

 unsigned :cd,  1, "Carrier detect"       #1
 unsigned :rd,  1, "Received data"        #2
 unsigned :td,  1, "Transmitted data"     #3
 unsigned :dtr, 1, "Data terminal ready"  #4
 unsigned :sg,  1, "Signal ground"        #5
 unsigned :dsr, 1, "Data set ready"       #6
 unsigned :rts, 1, "Request to send"      #7
 unsigned :cts, 1, "Clear to send"        #8
 unsigned :ri,  1, "Ring indicator"       #9
 
 def self.new_with_int(value)
   data = {}
   fields.each_with_index {|f, i| data[f.name] = value[i]}
   new(data)
 end

end

num = rand(2**9 - 1) puts "num = #{num}"

sample1 = RS232_9.new([("%09d" % num.to_s(2)).reverse].pack("B*")) puts sample1.inspect_detailed

sample2 = RS232_9.new_with_int(num) puts sample2.inspect_detailed

puts "CD is #{sample2.cd == 1 ? 'on' : 'off'}"</lang>

num = 37
RS232_9:
                Carrier detect = 1
                 Received data = 0
              Transmitted data = 1
           Data terminal ready = 0
                 Signal ground = 0
                Data set ready = 1
               Request to send = 0
                 Clear to send = 0
                Ring indicator = 0
RS232_9:
                Carrier detect = 1
                 Received data = 0
              Transmitted data = 1
           Data terminal ready = 0
                 Signal ground = 0
                Data set ready = 1
               Request to send = 0
                 Clear to send = 0
                Ring indicator = 0
CD is on

Tcl

This Tcl implementation represents the fields as bits in an integer. It provides two functions to get from symbolic pin names to the integer, and vice versa. <lang Tcl> set rs232_bits {CD RD TD DTR SG DSR RTS CTS RI}

proc rs232_encode args {

   set res 0
   foreach arg $args {
       set pos [lsearch $::rs232_bits $arg]
       if {$pos >=0} {set res [expr {$res | 1<<$pos}]}
   }
   return $res

} proc rs232_decode int {

   set res {}
   set i -1
   foreach bit $::rs232_bits {
       incr i
       if {$int & 1<<$i} {lappend res $bit}
   }
   return $res

}

  1. ------------------------------ Test suite

foreach {test => expected} {

   {rs232_encode CD} -> 1
   {rs232_decode 1} -> CD
   {rs232_encode CD RD TD} -> 7
   {rs232_decode 7} -> {CD RD TD}

} {

   catch $test res
   if {$res ne $expected} {puts "$test -> $res, expected $expected"}

} </lang>