Topic: tech dcc pc src prev next
tech dcc pc src > packet.c
#include "packet.h"
#define DCC_REGISTER_PAGE 0b101
#define DCC_REGISTER_D0 0b000
#define DCC_REGISTER_D1 0b001
#define DCC_REGISTER_D2 0b010
#define DCC_REGISTER_D3 0b011
packet Packet_CreateEmpty()
{
packet p = { .length = 0, .service_mode = false };
return p;
}
packet Packet_CreateReset()
{
return Packet_CreateServiceMode2(0, 0);
}
packet Packet_CreatePagePreset()
{
return Packet_CreateServiceMode2(0b01111101, 0b00000001);
}
packet Packet_CreateIdle()
{
return Packet_CreateServiceMode2(0b11111111, 0b00000000);
}
packet Packet_CreateSpeed128(
cfg_address_type address,
bool forward,
unsigned char speed
)
{
if(speed > 0)
++speed;
return Packet_Create3(
(unsigned char)address,
0b00111111,
(unsigned char)((forward ? 0b10000000 : 0) | (speed & 0b01111111))
);
}
packet Packet_CreateSpeed28(
cfg_address_type address,
bool forward,
unsigned char speed
)
{
if(speed > 0)
speed = (unsigned char)(speed + 3);
unsigned char data = 0b01000000;
if(forward)
data |= 0b00100000;
if(speed & 0b00000001)
data |= 0b00010000;
data = (unsigned char)(data | ((speed >> 1) & 0x0f));
return Packet_Create2((unsigned char)address, data);
}
packet Packet_CreatePomAccWrite(
cfg_address_type target_address,
cfg_address_type cv_no,
unsigned char cv_data
)
{
// Move the CV no. to the 0-1023 address space.
--cv_no;
return Packet_Create5(
// First byte: 10AAAAAA.
(unsigned char)((target_address & 0b00111111) | 0b10000000),
// Second byte: 1AAACDDD. When CDDD == 0000, the CV written refers
// to the entire decoder. The three upper address bytes in one's
// complement.
(unsigned char)(
((~(target_address & 0b111000000) >> 2) & 0b01110000) |
0b10000000
),
// Third byte: 1110CCVV. When CC == 11, this is a 'write byte'
// packet. VV is the two highest bits of the CV number.
(unsigned char)(0b11101100 | ((cv_no >> 8) & 0b00000011)),
// Fourth byte: eight lower bits of the CV number.
(unsigned char)(cv_no & 0b11111111),
// Fifth byte: CV data.
cv_data
);
}
packet Packet_CreatePomLocoWrite(
cfg_address_type target_address,
cfg_address_type cv_no,
unsigned char cv_data
)
{
// Move the CV no. to the 0-1023 address space.
--cv_no;
return Packet_Create4(
(unsigned char)(target_address),
(unsigned char)(0b11101100 | ((cv_no >> 8) & 0b00000011)),
(unsigned char)(cv_no & 0b11111111),
cv_data
);
}
packet Packet_CreateAccessoryBasic(
cfg_address_type address,
unsigned char output,
bool state
)
{
// Address MSB bytes (second byte):
// "By convention these bits (bits 4-6 of
// the second data byte) are in ones complement."
return Packet_Create2(
(unsigned char)((address & 0b00111111) | 0b10000000),
(unsigned char)(
((~(address & 0b111000000) >> 2) & 0b01110000) |
(state ? 0b00001000 : 0) | 0b10000000 |
(output & 0b111)
)
);
}
packet Packet_Create2(unsigned char byte1, unsigned char byte2)
{
packet out;
out.length = 3;
out.service_mode = false;
out.data[0] = byte1;
out.data[1] = byte2;
out.data[2] = byte1 ^ byte2;
return out;
}
packet Packet_Create3(unsigned char byte1, unsigned char byte2, unsigned char byte3)
{
packet out;
out.length = 4;
out.service_mode = false;
out.data[0] = byte1;
out.data[1] = byte2;
out.data[2] = byte3;
out.data[3] = byte1 ^ byte2 ^ byte3;
return out;
}
packet Packet_Create4(
unsigned char byte1,
unsigned char byte2,
unsigned char byte3,
unsigned char byte4
)
{
packet out;
out.length = 5;
out.service_mode = false;
out.data[0] = byte1;
out.data[1] = byte2;
out.data[2] = byte3;
out.data[3] = byte4;
out.data[4] = byte1 ^ byte2 ^ byte3 ^ byte4;
return out;
}
packet Packet_Create5(
unsigned char byte1,
unsigned char byte2,
unsigned char byte3,
unsigned char byte4,
unsigned char byte5
)
{
packet out;
out.length = 6;
out.service_mode = false;
out.data[0] = byte1;
out.data[1] = byte2;
out.data[2] = byte3;
out.data[3] = byte4;
out.data[4] = byte5;
out.data[5] = byte1 ^ byte2 ^ byte3 ^ byte4 ^ byte5;
return out;
}
packet Packet_CreateServiceMode2(unsigned char byte1, unsigned char byte2)
{
packet out;
out.length = 3;
out.service_mode = true;
out.data[0] = byte1;
out.data[1] = byte2;
out.data[2] = byte1 ^ byte2;
return out;
}
packet Packet_CreateServiceMode3(unsigned char byte1, unsigned char byte2, unsigned char byte3)
{
packet out;
out.length = 4;
out.service_mode = true;
out.data[0] = byte1;
out.data[1] = byte2;
out.data[2] = byte3;
out.data[3] = byte1 ^ byte2 ^ byte3;
return out;
}
bool Packet_AppendByte(packet *p, unsigned char b)
{
if(p->length >= PACKET_MAX_LENGTH)
return false;
p->data[p->length] = b;
p->length++;
return true;
}
bool Packet_EccValid(const packet *p)
{
unsigned char ecc = 0;
for(int i = 0; i < p->length; ++i)
ecc ^= p->data[i];
return (ecc == 0);
}
unsigned char Packet_RawLength(const packet *p)
{
return (unsigned char)(
(
p->service_mode ?
PACKET_PROGRAMMING_PREAMBLE_LENGTH : PACKET_NORMAL_PREAMBLE_LENGTH
) + p->length * 9 + 1
);
}
void Packet_RawData(unsigned char *out, const packet *p)
{
const unsigned char len = Packet_RawLength(p);
const unsigned char pre =
p->service_mode ?
PACKET_PROGRAMMING_PREAMBLE_LENGTH : PACKET_NORMAL_PREAMBLE_LENGTH;
// Each output bit.
for(unsigned char i = 0; i < len; ++i)
{
// Position to write to in 'out'.
const unsigned char out_byte = i / 8;
const unsigned char out_shift = 7 - (i % 8);
if(i < pre)
{
// Preamble bit.
out[out_byte] = (unsigned char)(out[out_byte] | (1 << out_shift));
}
else
{
// i >= pre
// Byte and bit to take from p->data.
const unsigned char in_byte = (unsigned char)((i - pre) / 9);
const unsigned char in_shift = (unsigned char)(8 - ((i - pre) % 9));
if(in_byte < p->length)
{
if(in_shift == 8)
{
// Byte start bit.
out[out_byte] =
(unsigned char)(out[out_byte] & ~(1 << out_shift));
}
else
{
// Data bit.
if(p->data[in_byte] & (1 << in_shift))
out[out_byte] =
(unsigned char)(out[out_byte] | (1 << out_shift));
else
out[out_byte] =
(unsigned char)(out[out_byte] & ~(1 << out_shift));
}
}
else
{
// Packet end bit.
out[out_byte] =
(unsigned char)(out[out_byte] | (1 << out_shift));
}
}
}
}
bool Packet_Matches(const packet *a, const packet *b, const packet *mask)
{
if(a->length != b->length || a->length != mask->length)
return false;
// Do not check the checksum byte.
for(int i = 0; i < a->length - 1; ++i)
if((a->data[i] & mask->data[i]) != (b->data[i] & mask->data[i]))
return false;
return true;
}
bool Packet_IsReset(const packet *p)
{
return p->length == 3 && p->data[0] == 0 && p->data[1] == 0;
}
bool Packet_IsServiceMode(const packet *p)
{
return (p->length >= 1 && p->data[0] >= 112 && p->data[0] <= 127);
}
bool Packet_Is28Step(const packet *p)
{
packet cmp = Packet_Create2(0, 0b01000000);
packet mask = Packet_Create2(0b10000000, 0b11000000);
return Packet_Matches(p, &cmp, &mask);
}
cfg_address_type Packet_28StepAddress(const packet *p)
{
return p->data[0] & 0b01111111;
}
unsigned char Packet_28StepSpeed(const packet *p)
{
unsigned char s =
(unsigned char)(
((p->data[1] & 0b00001111) << 1) | ((p->data[1] & 0b00010000) >> 4)
);
if(s >= 1)
s = (unsigned char)(s - 1);
return s;
}
bool Packet_28StepFwd(const packet *p)
{
return p->data[1] & 0b00100000;
}
bool Packet_Is128Step(const packet *p)
{
packet cmp = Packet_Create3(0, 0b00111111, 0);
packet mask = Packet_Create3(0b10000000, 0b11111111, 0);
return Packet_Matches(p, &cmp, &mask);
}
unsigned char Packet_128StepSpeed(const packet *p)
{
unsigned char s = p->data[2] & 0b01111111;
if(s >= 1)
s = (unsigned char)(s - 1);
return s;
}
bool Packet_128StepFwd(const packet *p)
{
return p->data[2] & 0b10000000;
}
cfg_address_type Packet_AdvAddress(const packet *p)
{
if(p->length < 1)
return 0;
return p->data[0] & 0b01111111;
}
bool Packet_IsDirectProgramming(const packet *p)
{
packet cmp = Packet_Create3(0b01111100, 0, 0);
packet mask = Packet_Create3(0b01111100, 0, 0);
return Packet_Matches(p, &cmp, &mask);
}
/*bool Packet_IsRegisterProgramming(const packet *p)*/
/*{*/
/*packet cmp = Packet_Create2(0b01110000, 0);*/
/*packet mask = Packet_Create2(0b11110000, 0);*/
/*return Packet_Matches(p, &cmp, &mask);*/
/*}*/
unsigned short Packet_DirectCv(const packet *p)
{
return (unsigned short)((((p->data[0] & 0b11) << 8) | p->data[1]) + 1);
}
unsigned char Packet_DirectValue(const packet *p)
{
return p->data[2];
}
bool Packet_IsRegisterProgramming(const packet *p)
{
packet cmp = Packet_Create2(0b01111000, 0);
packet mask = Packet_Create2(0b11111000, 0);
return Packet_Matches(p, &cmp, &mask);
}
unsigned char Packet_Register(const packet *p)
{
return p->data[0] & 0b00000111;
}
unsigned char Packet_RegisterValue(const packet *p)
{
return p->data[1];
}
bool Packet_IsLocoPomWrite(const packet *p)
{
packet cmp = Packet_Create4(0, 0b11101100, 0, 0);
packet mask = Packet_Create4(0, 0b11111100, 0, 0);
return Packet_Matches(p, &cmp, &mask);
}
bool Packet_IsPomAccelerationWrite(const packet *p)
{
packet cmp = Packet_Create3(0, 0b11110010, 0);
packet mask = Packet_Create3(0, 0xff, 0);
return Packet_Matches(p, &cmp, &mask);
}
bool Packet_IsPomDecelerationWrite(const packet *p)
{
packet cmp = Packet_Create3(0, 0b11110011, 0);
packet mask = Packet_Create3(0, 0xff, 0);
return Packet_Matches(p, &cmp, &mask);
}
unsigned short Packet_LocoPomCv(const packet *p)
{
return (unsigned short)((((p->data[1] & 0b11) << 8) | p->data[2]) + 1);
}
unsigned char Packet_LocoPomValue(const packet *p)
{
return p->data[3];
}
bool Packet_IsBasicAccessory(const packet *p)
{
// Optimisation: return early if the packet is of the wrong length.
if(p->length != 3)
return false;
packet cmp = Packet_Create2(0b10000000, 0b10000000);
packet mask = Packet_Create2(0b11000000, 0b10000000);
return Packet_Matches(p, &cmp, &mask);
}
cfg_address_type Packet_BasicAccessoryAddress(const packet *p)
{
if(p->length != 3)
return 0;
// Bits 6, 5, 4 of byte 1 in one's complement and bits 5-0 of byte 0.
return (cfg_address_type)(
(((p->data[1] & 0b01110000) << 2) ^ 0b111000000) |
(p->data[0] & 0b00111111)
);
}
bool Packet_BasicAccessoryState(const packet *p)
{
if(p->length != 3)
return false;
return (p->data[1] & 0b00001000);
}
unsigned char Packet_BasicAccessoryOutput(const packet *p)
{
if(p->length != 3)
return false;
return (p->data[1] & 0b00000111);
}
bool Packet_IsFunction(const packet *p)
{
{
packet cmp = Packet_Create2(0, 0b10000000);
packet mask = Packet_Create2(0, 0b11100000);
if(Packet_Matches(p, &cmp, &mask))
return true;
}
{
packet cmp = Packet_Create2(0, 0b10110000);
packet mask = Packet_Create2(0, 0b11110000);
if(Packet_Matches(p, &cmp, &mask))
return true;
}
{
packet cmp = Packet_Create2(0, 0b10100000);
packet mask = Packet_Create2(0, 0b11110000);
if(Packet_Matches(p, &cmp, &mask))
return true;
}
{
packet cmp = Packet_Create3(0, 0b11011110, 0);
packet mask = Packet_Create3(0, 0b11111111, 0);
if(Packet_Matches(p, &cmp, &mask))
return true;
}
{
packet cmp = Packet_Create3(0, 0b11011111, 0);
packet mask = Packet_Create3(0, 0b11111111, 0);
if(Packet_Matches(p, &cmp, &mask))
return true;
}
return false;
}
packet Packet_CreateFunction0_4(cfg_address_type address, unsigned char data)
{
return Packet_Create2(
(unsigned char)address,
(unsigned char)(
0b10000000 |
((data >> 1) & 0b00001111) |
((data << 4) & 0b00010000))
);
}
packet Packet_CreateFunction5_8(cfg_address_type address, unsigned char data)
{
return Packet_Create2(
(unsigned char)address,
(unsigned char)(0b10110000 | (data & 0b00001111))
);
}
packet Packet_CreateFunction9_12(cfg_address_type address, unsigned char data)
{
return Packet_Create2(
(unsigned char)address,
(unsigned char)(0b10100000 | (data & 0b00001111))
);
}
packet Packet_CreateFunction13_20(cfg_address_type address, unsigned char data)
{
return Packet_Create3((unsigned char)address, 0b11011110, data);
}
packet Packet_CreateFunction21_28(cfg_address_type address, unsigned char data)
{
return Packet_Create3((unsigned char)address, 0b11011111, data);
}
bool Packet_IsAccPomWrite(const packet *p)
{
// Optimisation: return early if the packet is of the wrong length.
/*if(p->length != 6)*/
/*return false;*/
packet cmp = Packet_Create5(
// Lower address bits.
0b10000000,
// 1AAACDDD - upper bits of address, CDDD == 0000 to access a CV
// for the decoder rather than an output.
0b10000000,
// Write byte instruction.
0b11101100,
// Lower eight bits of CV number.
0,
// CV data.
0
);
packet mask = Packet_Create5(0b11000000, /* accept packets addressed to any input */ 0b10000000, 0b11111100, 0, 0);
return Packet_Matches(p, &cmp, &mask);
}
cfg_address_type Packet_AccPomAddress(const packet *p)
{
// Bits 6, 5, 4 of byte 1 in one's complement and bits 5-0 of byte 0.
return (cfg_address_type)(
(((p->data[1] & 0b01110000) << 2) ^ 0b111000000) |
(p->data[0] & 0b111111)
);
}
cfg_address_type Packet_AccPomCv(const packet *p)
{
return (cfg_address_type)((((p->data[2] & 0b11) << 8) | p->data[3]) + 1);
}
unsigned char Packet_AccPomValue(const packet *p)
{
return p->data[4];
}