Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl pack()ing a message?

Background

I have a device connected to my machine on a serial port that is message oriented, where each incoming command message is exactly eight 16-bit words. The most significant byte of each word is received first and the least significant byte received second. Each header word is a command identifier (ranging from 0 - 255). The format is defined like so:

XXXX    Header Word (Placeholder for 16-bit Hexadecimal value)
XXXX    Data Word
XXXX    Data Word
XXXX    Data Word
XXXX    Data Word
XXXX    Data Word
XXXX    Data Word
XXXX    Checksum Word (Result of Exclusive OR on header word and all six data words)

The format for the command I need to send, header 0x5D, is defined like so:

Word 1: Header = 005Dh
Word 2: Mode (values can be 2, 3, 6, 8, 9)
Words 3-7: Not significant
Word 8: 16 bit checksum 

Questions (all related)

I have this example from other source code that apparently sends this message correctly, but I'm not sure what is going on, even after reading perldoc on pack.

pack("v"x8, 0x00, 0x5D, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x5D);
  1. "v" means little endian, but I cannot find any reference on the x8 following it.
  2. And according to the format of the message, shouldn't the 0x5D value be right after the "v"x8, not after the 0x00?
  3. The message is supposed to have eight 16-bit words, so why are there 16 and not 8? Some are 0x00, and not 0x0? Does that make a difference?
like image 335
rem45acp Avatar asked Jun 08 '14 01:06

rem45acp


1 Answers

pack "v"x8 (which results in pack "vvvvvvvv", which can be written as pack "v8" for short) packs eight 16-bit numbers, yet you passed sixteen.

If you have sixteen bytes already, you want

pack 'C16', 0x00, 0x5D, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x5D

But I would expect you to have eight 16-bit words instead, so you'd use

pack 'n8', 0x005D, 0x0009, 0x0000, 0x0000, 0x0000, 0x0000, 0x0009, 0x005D

Note the use of n instead of v to get the desired byte order.

pack 'v', 0x1234      => "\x34\x12"   Most significant byte last
pack 'n', 0x1234      => "\x12\x34"   Most significant byte first

Proof that both methods are equivalent:

my $method1 =  pack 'C16',
    0x00, 0x5D, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x5D;

my $method2 = pack 'n8',
    0x005D, 0x0009, 0x0000, 0x0000, 0x0000, 0x0000, 0x0009, 0x005D;

print($method1 eq $method2 ? "yes" : "no", "\n");  # yes
like image 137
ikegami Avatar answered Sep 23 '22 02:09

ikegami