Memory layout of a data structure
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.
You are encouraged to solve this task according to the task description, using any language you may know.
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
<lang algol68>MODE RSTWOTHREETWO = BITS; INT ofs = bits width - 9; INT
lwb rs232 = ofs + 1, carrier detect = ofs + 1, received data = ofs + 2, transmitted data = ofs + 3, data terminal ready = ofs + 4, signal ground = ofs + 5, data set ready = ofs + 6, request to send = ofs + 7, clear to send = ofs + 8, ring indicator = ofs + 9, upb rs232 = ofs + 9;
RSTWOTHREETWO rs232 bits := 2r10000000; # up to bits width, OR # print(("received data: ",received data ELEM rs232bits, new line));
rs232 bits := bits pack((FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)); print(("received data: ",received data ELEM rs232bits, new line))</lang> Output:
received data: T received data: T
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
Tango version
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 ]
Phobos version
Not tested. <lang d>import std.bitmanip;
struct RS232_data {
static if (std.system.endian == std.system.Endian.bigEndian) { mixin(bitfields!(bool, "carrier_detect", 1, bool, "received_data", 1, bool, "transmitted_data", 1, bool, "data_terminal_ready", 1, bool, "signal_ground", 1, bool, "data_set_ready", 1, bool, "request_to_send", 1, bool, "clear_to_send", 1, bool, "ring_indicator", 1, bool, "", 7)); } else { mixin(bitfields!(bool, "", 7, bool, "ring_indicator", 1, bool, "clear_to_send", 1, bool, "request_to_send", 1, bool, "data_set_ready", 1, bool, "signal_ground", 1, bool, "data_terminal_ready", 1, bool, "transmitted_data", 1, bool, "received_data", 1, bool, "carrier_detect", 1)); }
static assert(RS232_data.sizeof == 2);
}
void main() {}</lang>
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.
<lang forth> : masks ( n -- ) 0 do 1 i lshift constant loop ;
9 masks DCD RxD TxD DTR SG DSR RTS CTS RI</lang>
Example usage, assuming I/O primitives in and out:
<lang forth> 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 ;</lang>
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.
Go
Go does not have named bits as part of the type system. Instead, constants are typically defined as shown. For a word of bits with special meanings like this, a type would be defined though, as shown. Static typing rules then control assignments and comparisons at the word level. At the bit level, it helps to follow naming conventions so that, say, using a 9-pin constant on a 25-pin word would be an obvious error in the source code. <lang go>package main
import "fmt"
type rs232p9 uint16
const (
CD9 = 1 << iota // Carrier detect RD9 // Received data TD9 // Transmitted data DTR9 // Data terminal ready SG9 // signal ground DSR9 // Data set ready RTS9 // Request to send CTS9 // Clear to send RI9 // Ring indicator
)
func main() {
// set some nonsense bits just for example var p rs232p9 = RI9 | TD9 | CD9 fmt.Printf("%04x\n", p)
}</lang> Output:
0105
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 / Octave
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>
Mercury
Mercury does not have data types that mark down to individual bits. Instead it allows you to use symbolic names (as per the rs232_pin type definition below with its constructors like carrier_detect) and to easily map those names to a value if you so choose (as per the to_index/1 function below).
This is, admittedly, more verbose than the equivalent code would be in, say, C, but it permits things which are more difficult to pull off in such languages. First, the code here is perfectly type safe (despite the use of unsafe_set and unsafe_clear). It is literally impossible to set or clear bits outside of the bounds of the bit array used in the physical representation. It is as easy to set and clear individual bits in the bit array as it is to use bit notation in C, and it's easier to do than when using the safer "integer variable with bitwise operators" technique. Setting and clearing groups of bits is even easier via the rs232_set_bits/2 function: merely pass in a list of symbolic names. Changing the bitwise representation is a matter of changing the bit numbers in to_index/1 and possibly changing the size of the bit array in rs232_bits/1. Indeed the entire underlying representation and implementation can change without the interface changing at all.
Aiding in changing the underlying representation at will is the fact that the exposed type—rs232— is an opaque data type. Instead of exposing the fact of the bitmap implementation to the outside world, it is carefully concealed by the implementation. It is impossible for any code using this module to operate on the underlying bitmap with anything other than the API which has been exposed. This means that should it be deemed desirable to instead use an int as the underlying representation, this can be done without changing even one byte of client code.
rs232.m
<lang Mercury>
- - module rs232.
- - interface.
- - import_module bool, io, list, string.
- - type rs232_pin
---> carrier_detect ; received_data ; transmitted_data ; data_terminal_ready ; signal_ground ; data_set_ready ; request_to_send ; clear_to_send ; ring_indicator.
- - type rs232.
- - func rs232_bits = rs232.
- - func rs232_bits(bool) = rs232.
- - func rs232_set(rs232, rs232_pin) = rs232.
- - func rs232_clear(rs232, rs232_pin) = rs232.
- - pred rs232_is_set(rs232::in, rs232_pin::in) is semidet.
- - pred rs232_is_clear(rs232::in, rs232_pin::in) is semidet.
- - func rs232_set_bits(rs232, list(rs232_pin)) = rs232.
- - func rs232_clear_bits(rs232, list(rs232_pin)) = rs232.
- - func to_string(rs232) = string.
- - pred write_rs232(rs232::in, io::di, io::uo) is det.
- - implementation.
- - import_module bitmap.
- - type rs232 == bitmap.
rs232_bits = rs232_bits(no). rs232_bits(Default) = bitmap.init(9, Default).
rs232_set(A, Pin) = unsafe_set(A, to_index(Pin)). rs232_clear(A, Pin) = unsafe_clear(A, to_index(Pin)).
rs232_is_set(A, Pin) :- unsafe_is_set(A, to_index(Pin)). rs232_is_clear(A, Pin) :- unsafe_is_clear(A, to_index(Pin)).
rs232_set_bits(A, Pins) = foldl((func(Pin, B) = rs232_set(B, Pin)), Pins, A). rs232_clear_bits(A, Pins) = foldl((func(Pin, B) = rs232_clear(B, Pin)), Pins, A).
to_string(A) = bitmap.to_string(A).
write_rs232(A, !IO) :- write_bitmap(resize(A, 16, no), !IO).
% cannot write a bitmap that isn't byte-divisible
- - func to_index(rs232_pin) = bit_index.
to_index(carrier_detect) = 0. to_index(received_data) = 1. to_index(transmitted_data) = 2. to_index(data_terminal_ready) = 3. to_index(signal_ground) = 4. to_index(data_set_ready) = 5. to_index(request_to_send) = 6. to_index(clear_to_send) = 7. to_index(ring_indicator) = 8.
- - end_module rs232.
</lang>
rs232_main.m
<lang Mercury>
- - module rs232_main.
- - interface.
- - import_module io.
- - pred main(io::di, io::uo) is det.
- - implementation.
- - import_module bitmap, bool, list, rs232.
main(!IO) :-
Com1 = rs232_set_bits(rs232_bits, [data_terminal_ready, data_set_ready]), Com2 = rs232_clear_bits(rs232_bits(yes), [data_terminal_ready, data_set_ready]),
write_string("Com1 bits = ", !IO), write_string(to_string(Com1), !IO), nl(!IO),
write_string("Com2 bits = ", !IO), write_string(to_string(Com2), !IO), nl(!IO),
write_string("Com1 DTR is ", !IO), ( rs232_is_set(Com1, data_terminal_ready) -> write_string("set.", !IO), nl(!IO) ; write_string("clear.", !IO), nl(!IO) ),
write_string("Com2 DSR is ", !IO), ( rs232_is_clear(Com2, data_set_ready) -> write_string("clear.", !IO), nl(!IO) ; write_string("set.", !IO), nl(!IO) ).
- - end_module rs232_main.
</lang>
Usage and output
$ mmc --make rs232_main $ ./rs232_main Com1 bits = <9:1400> Com2 bits = <9:EB80> Com1 DTR is set. Com2 DSR is clear.
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>
Pascal
<lang pascal>program memoryLayout;
type
T_RS232 = ( carrier_detect, received_data, transmitted_data, data_terminal_ready, signal_ground, data_set_ready, request_to_send, clear_to_send, ring_indicator );
var
Signal: bitpacked array[T_RS232] of boolean;
begin
Signal[signal_ground] := true;
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)) )
- Test if Clear to send
(when (bit? CTS Data)
... )</lang>
PL/I
<lang PL/I> declare 1 RS232_layout,
2 Carrier_Detect Bit(1), 2 Received_Data Bit(1), 2 Transmitted_Data Bit(1), 2 Data_Terminal_ready Bit(1), 2 Signal_Ground Bit(1), 2 Data_Set_Ready Bit(1), 2 Request_To_Send Bit(1), 2 Clear_To_Send Bit(1), 2 Ring_Indicator Bit(1);
</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>
REXX
version 1
<lang rexx>/* REXX ***************************************************************
- Decode Memory structure of RS-232 Plug Definition
- Not sure if I understood it completely :-) Open for corrections
- You never stop learning (as long as you live)
- 03.08.2012 Walter Pachl
- /
Call decode 'ABC' Call decode 'XY' Exit
decode:
Parse Arg c cb=c2b(c) If length(cb)=24 Then Do Parse Var cb, /* 1 - PG */ Protective ground +1, /* 3 2 - TD */ Transmitted_data +1, /* 2 3 - RD */ Received_data +1, /* 7 4 - RTS */ Request_to_send +1, /* 8 5 - CTS */ Clear_to_send +1, /* 6 6 - DSR */ Data_set_ready +1, /* 5 7 - SG */ Signal_ground +1, /* 1 8 - CD */ Carrier_detect +1, /* 9 - + */ plus_voltage +1, /* 10 - - */ minus_voltage +1, /* 11 - */ . +1, /* 12 - SCD */ Secondary_CD +1, /* 13 - SCS */ Secondary_CTS +1, /* 14 - STD */ Secondary_TD +1, /* 15 - TC */ Transmit_clock +1, /* 16 - SRD */ Secondary_RD +1, /* 17 - RC */ Receiver_clock +1, /* 18 - */ . +1, /* 19 - SRS */ Secondary_RTS +1, /* 4 20 - DTR */ Data_terminal_ready +1, /* 21 - SQD */ Signal_quality_detector+1, /* 9 22 - RI */ Ring_indicator +1, /* 23 - DRS */ Data_rate_select +1, /* 24 - XTC */ External_clock +1 Say '24 bins:' cb Say ' 1 - PG Protective ground ='Protective ground Say ' 2 - TD Transmitted data ='Transmitted_data Say ' 3 - RD Received data ='Received_data Say ' 4 - RTS Request to send ='Request_to_send Say ' 5 - CTS Clear to send ='Clear_to_send Say ' 6 - DSR Data set ready ='Data_set_ready Say ' 7 - SG Signal ground ='Signal_ground Say ' 8 - CD Carrier detect ='Carrier_detect Say ' 9 - + plus voltage ='plus_voltage Say '10 - - minus voltage ='minus_voltage Say ' ' Say '12 - SCD Secondary CD ='Secondary_CD Say '13 - SCS Secondary CTS ='Secondary_CTS Say '14 - STD Secondary TD ='Secondary_TD Say '15 - TC Transmit clock ='Transmit_clock Say '16 - SRD Secondary RD ='Secondary_RD Say '17 - RC Receiver clock ='Receiver_clock Say ' ' Say '19 - SRS Secondary RTS ='Secondary_RTS Say '20 - DTR Data terminal ready ='Data_terminal_ready Say '21 - SQD Signal quality detector ='Signal_quality_detector Say '22 - RI Ring indicator ='Ring_indicator Say '23 - DRS Data rate select ='Data_rate_select Say '24 - XTC External hlock ='External_clock End Else Do Parse Var cb, /* 1 8 - CD */ Carrier_detect +1, /* 2 3 - RD */ Received_data +1, /* 3 2 - TD */ Transmitted_data +1, /* 4 20 - DTR */ Data_terminal_ready +1, /* 5 7 - SG */ Signal_ground +1, /* 6 6 - DSR */ Data_set_ready +1, /* 7 4 - RTS */ Request_to_send +1, /* 8 5 - CTS */ Clear_to_send +1, /* 9 22 - RI */ Ring_indicator +1 Say ' ' Say '9-bin:' left(cb,9) Say ' 1 CD Carrier detect ='Carrier_detect Say ' 2 RD Received data ='Received_data Say ' 3 TD Transmitted data ='Transmitted_data Say ' 4 DTR Data terminal ready ='Data_terminal_ready Say ' 5 SG Signal ground ='Signal_ground Say ' 6 DSR Data set ready ='Data_set_ready Say ' 7 RTS Request to send ='Request_to_send Say ' 8 CTS Clear to send ='Clear_to_send Say ' 9 RI Ring indicator ='Ring_indicator End Return
c2b: Procedure /* REXX ***************************************************************
- c2b Convert a character string to a bit string
- 03.08.2012 Walter Pachl
- /
Parse Arg c x=c2x(c) res= Do While x<>
Parse Var x hb +1 x Select When hb='0' Then bs='0000' When hb='1' Then bs='0001' When hb='2' Then bs='0010' When hb='3' Then bs='0011' When hb='4' Then bs='0100' When hb='5' Then bs='0101' When hb='6' Then bs='0110' When hb='7' Then bs='0111' When hb='8' Then bs='1000' When hb='9' Then bs='1001' When hb='A' Then bs='1010' When hb='B' Then bs='1011' When hb='C' Then bs='1100' When hb='D' Then bs='1101' When hb='E' Then bs='1110' When hb='F' Then bs='1111' End res=res||bs End
Return res</lang> Output EBCDIC:
24 bins: 110000011100001011000011 1 - PG Protective ground =1 2 - TD Transmitted data =1 3 - RD Received data =0 4 - RTS Request to send =0 5 - CTS Clear to send =0 6 - DSR Data set ready =0 7 - SG Signal ground =0 8 - CD Carrier detect =1 9 - + plus voltage =1 10 - - minus voltage =1 12 - SCD Secondary CD =0 13 - SCS Secondary CTS =0 14 - STD Secondary TD =0 15 - TC Transmit clock =1 16 - SRD Secondary RD =0 17 - RC Receiver clock =1 19 - SRS Secondary RTS =0 20 - DTR Data terminal ready =0 21 - SQD Signal quality detector =0 22 - RI Ring indicator =0 23 - DRS Data rate select =1 24 - XTC External hlock =1 9-bin: 111001111 1 CD Carrier detect =1 2 RD Received data =1 3 TD Transmitted data =1 4 DTR Data terminal ready =0 5 SG Signal ground =0 6 DSR Data set ready =1 7 RTS Request to send =1 8 CTS Clear to send =1 9 RI Ring indicator =1
version 2
Checks could be added to verify the number of pins selected and also verify if the data (pin readings) specified is valid. <lang rexx>/*REXX pgm displays which pins are active of a 9 or 24 pin RS-232 plug. */ call rs_232 24, 127 /*value for an RS-232 24 pin plug*/ call rs_232 24, '020304x' /*value for an RS-232 24 pin plug*/ call rs_232 9, '10100000b' /*value for an RS-232 9 pin plug*/ exit /*stick a fork in it, we're done.*/ /*──────────────────────────────────RS_232 subroutine───────────────────*/ rs_232: arg pins,x; parse arg ,ox /*x is uppercased when using ARG.*/ @. = '??? unassigned bit' /*assigned default for all bits. */ @.24.1 = 'PG protective ground' @.24.2 = 'TD transmitted data' ; @.9.3 = @.24.2 @.24.3 = 'RD received data' ; @.9.2 = @.24.3 @.24.4 = 'RTS request to send' ; @.9.7 = @.24.4 @.24.5 = 'CTS clear to send' ; @.9.8 = @.24.5 @.24.6 = 'DSR data set ready' ; @.9.6 = @.24.6 @.24.7 = 'SG signal ground' ; @.9.5 = @.24.7 @.24.8 = 'CD carrier detect' ; @.9.1 = @.24.8 @.24.9 = '+ positive voltage' @.24.10 = '- negative voltage' @.24.12 = 'SCD secondary CD' @.24.13 = 'SCS secondary CTS' @.24.14 = 'STD secondary td' @.24.15 = 'TC transmit clock' @.24.16 = 'SRD secondary RD' @.24.17 = 'RC receiver clock' @.24.19 = 'SRS secondary RTS' @.24.20 = 'DTR data terminal ready' ; @.9.4 = @.24.20 @.24.21 = 'SQD signal quality detector' @.24.22 = 'RI ring indicator' ; @.9.9 = @.24.22 @.24.23 = 'DRS data rate select' @.24.24 = 'XTC external clock'
select when right(x,1)=='B' then bits= strip(x,'T',"B") when right(x,1)=='X' then bits=x2b(strip(x,'T',"X")) otherwise bits=x2b( d2x(x)) end /*select*/
bits=right(bits,pins,0) /*right justify the pin readings.*/ say; say '───────── For a' pins "pin RS─232 plug, with a reading of: " ox say
do j=1 for pins; z=substr(bits,j,1); if z==0 then iterate say right(j,5) 'pin is "on": ' @.pins.j end /*j*/
return</lang> output when using the internal input:
───────── For a 24 pin RS─232 plug, with a reading of: 127 18 pin is "on": B18 unassigned bit 18 19 pin is "on": SRS secondary RTS 20 pin is "on": DTR data terminal ready 21 pin is "on": SQD signal quality detector 22 pin is "on": RI ring indicator 23 pin is "on": DRS data rate select 24 pin is "on": XTC external clock ───────── For a 24 pin RS─232 plug, with a reading of: 020304x 7 pin is "on": SG signal ground 15 pin is "on": TC transmit clock 16 pin is "on": SRD secondary RD 22 pin is "on": RI ring indicator ───────── For a 9 pin RS─232 plug, with a reading of: 10100000b 2 pin is "on": RD received data 4 pin is "on": DTR data terminal ready
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
}
- ------------------------------ 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>