TAD Message to HDLC Frame Encapsulation¶
Overview¶
This document shows how TAD (Terminal Access Device) messages are encapsulated through the complete SINTRAN protocol stack into HDLC frames for transmission over X.25 networks.
Protocol Stack:
┌─────────────────────────────────────┐
│ TAD Message (Application Layer) │ 7BDAT, 7TMOD, etc.
├─────────────────────────────────────┤
│ XMSG Buffer (Message Layer) │ Buffer ID, addressing
├─────────────────────────────────────┤
│ X.25 Packet (Network Layer) │ GFI, LCN, packet type
├─────────────────────────────────────┤
│ HDLC Frame (Data Link Layer) │ Flag, address, control, FCS
└─────────────────────────────────────┘
Related Documents:
- TAD-Message-Formats.md - TAD message specifications
- XMSG_Metadata_Buffer_Analysis.md - XMSG buffer format
- HDLC analysis documents in Analysis\hdlc-analysis\
HDLC Frame Structure¶
Basic HDLC Frame Format¶
┌──────┬─────────┬─────────┬───────────────┬─────────┬──────┐
│ Flag │ Address │ Control │ Data │ FCS │ Flag │
│ 7E │ (1-2) │ (1-2) │ (variable) │ (2) │ 7E │
└──────┴─────────┴─────────┴───────────────┴─────────┴──────┘
Field Descriptions:
- Flag: 0x7E - Frame delimiter
- Address: 1 or 2 bytes - Destination/source addressing
- Control: 1 or 2 bytes - Frame type and sequence numbers
- Data: Variable length - Contains X.25 packet
- FCS: 2 bytes - Frame Check Sequence (CRC-16)
- Flag: 0x7E - Frame delimiter
HDLC Control Field Types¶
Information Frame (I-frame):
┌───┬───┬───┬───┬───┬───┬───┬───┐
│ 0 │ N(S) - 3 bits │ P │ N(R) - 3 bits │
└───┴───────────────┴───┴───────────────┘
Supervisory Frame (S-frame):
┌───┬───┬───┬───┬───┬───┬───┬───┐
│ 1 │ 0 │ S │ S │ P │ N(R) - 3 bits │
└───┴───┴───┴───┴───┴───────────────┘
Unnumbered Frame (U-frame):
┌───┬───┬───┬───┬───┬───┬───┬───┐
│ 1 │ 1 │ M │ M │ P │ M │ M │ M │
└───┴───┴───┴───┴───┴───┴───┴───┘
X.25 Packet Structure¶
X.25 Packet Format in HDLC Frame¶
┌────────────────────────────────────────────────────┐
│ HDLC Data Field │
├────────┬──────────┬────────────┬──────────────────┤
│ GFI │ LCN │ Packet │ User Data │
│ (1) │ (1) │ Type (1) │ (variable) │
└────────┴──────────┴────────────┴──────────────────┘
GFI (General Format Identifier):
- Bits 7-6: Always 01 for data packets
- Bit 5: Q-bit (qualifier bit)
- Bit 4: D-bit (delivery confirmation)
- Bits 3-0: Logical channel group number (high nibble)
LCN (Logical Channel Number): - Full 12-bit LCN = GFI[3:0] + LCN byte - Identifies virtual circuit
Packet Type:
- 0x0F = Data packet (with M-bit in bits 4-7 for sequence)
Typical X.25 Data Packet:
┌───────┬───────┬───────┬─────────────────┐
│ 0x21 │ 0x13 │ 0x0E │ User Data │
└───────┴───────┴───────┴─────────────────┘
GFI LCN Type+Seq (TAD message)
TAD Message Encapsulation Examples¶
Example 1: Simple Data Message (7BDAT)¶
TAD Message Layer¶
TAD Message: "Hello" (5 bytes)
┌───────┬────┬──┬──┬──┬──┬──┐
│ 7BDAT │ 05 │H │e │l │l │o │
└───────┴────┴──┴──┴──┴──┴──┘
Breakdown:
- Message Type: 0x42444154 ("BDAT") - stored as 0x42 for first byte
- Byte Count: 0x05
- Data: 0x48 0x65 0x6C 0x6C 0x6F ("Hello")
Total TAD Message: 42 05 48 65 6C 6C 6F (7 bytes)
XMSG Buffer Layer¶
XMSG adds metadata:
┌──────┬──────┬────────────────────────┐
│ Ref │ Func │ TAD Message │
│ (2) │ (2) │ (7 bytes) │
└──────┴──────┴────────────────────────┘
With metadata: 00 00 00 00 42 05 48 65 6C 6C 6F
- Ref: 0x0000 (no reference)
- Func: 0x0000 (no function)
- TAD data: 7 bytes
Total XMSG Buffer: 11 bytes
X.25 Packet Layer¶
X.25 Data Packet:
┌──────┬──────┬──────┬────────────────────────┐
│ GFI │ LCN │ Type │ User Data │
│ 0x21 │ 0x13 │ 0x0E │ (11 bytes) │
└──────┴──────┴──────┴────────────────────────┘
Complete X.25 Packet: 21 13 0E 00 00 00 00 42 05 48 65 6C 6C 6F
- GFI: 0x21 (Q=0, D=0, LCG=1)
- LCN: 0x13 (Logical Channel 0x113)
- Packet Type: 0x0E (Data packet, N(S)=0, M=0)
- User Data: 11 bytes (XMSG + TAD)
Total X.25 Packet: 14 bytes
HDLC Frame Layer¶
HDLC Information Frame:
┌──────┬──────┬──────┬─────────────────────┬───────┬──────┐
│ Flag │ Addr │ Ctrl │ X.25 Packet │ FCS │ Flag │
│ 7E │ 03 │ 10 │ (14 bytes) │ CRC16 │ 7E │
└──────┴──────┴──────┴─────────────────────┴───────┴──────┘
Complete HDLC Frame:
7E 03 10 21 13 0E 00 00 00 00 42 05 48 65 6C 6C 6F [FCS] 7E
Byte-by-Byte:
- 7E - Opening flag
- 03 - Address (response to DTE)
- 10 - Control (I-frame, N(S)=0, N(R)=0, P=0)
- 21 13 0E - X.25 header
- 00 00 00 00 - XMSG metadata
- 42 05 48 65 6C 6C 6F - TAD message
- [2 bytes] - FCS (calculated CRC-16)
- 7E - Closing flag
Total HDLC Frame: 21 bytes (excluding FCS calculation)
Example 2: Terminal Mode Message (7TMOD)¶
TAD Message Layer¶
TAD Message: Set capital letters + CR delay
┌───────┬────┬────┐
│ 7TMOD │ 01 │ 03 │
└───────┴────┴────┘
Breakdown:
- Message Type: 0x544D4F44 ("TMOD") - first byte 0x54
- Byte Count: 0x01
- Flags: 0x03 (bits 0,1 set)
Total TAD Message: 54 01 03 (3 bytes)
Complete HDLC Frame¶
7E 03 12 21 13 10 00 00 00 00 54 01 03 [FCS] 7E
Breakdown:
- 7E - Flag
- 03 - Address
- 12 - Control (I-frame, N(S)=1, N(R)=0)
- 21 13 10 - X.25 header (packet type 0x10 = N(S)=1)
- 00 00 00 00 - XMSG metadata
- 54 01 03 - TAD 7TMOD message
- [FCS] - CRC-16
- 7E - Flag
Total Frame: 17 bytes
Example 3: Terminal Type Message (7TTYP)¶
TAD Message Layer¶
TAD Message: Set terminal type 0x0123
┌───────┬────┬─────┬─────┐
│ 7TTYP │ 02 │ 01 │ 23 │
└───────┴────┴─────┴─────┘
Total TAD Message: 54 54 59 50 02 01 23
- Wait, this shows the full ASCII codes. Let me correct this.
Actually, looking at the source code more carefully, the message type is stored as a single byte (octal constant), not the full ASCII string.
Corrected:
Message code 7TTYP (octal) = implementation-specific encoding
Let's use simplified encoding where type = first char:
Type: 'T' = 0x54
Count: 0x02
Data: 0x01 0x23
Total TAD Message: 54 02 01 23 (4 bytes)
Complete HDLC Frame¶
7E 03 14 21 13 12 00 00 00 00 54 02 01 23 [FCS] 7E
Total Frame: 18 bytes
Example 4: Escape Message (7ESCA - High Priority)¶
TAD Message Layer¶
TAD Message: Escape signal (no data)
┌───────┬────┐
│ 7ESCA │ 00 │
└───────┴────┘
Total TAD Message: 45 00 (2 bytes)
- Type: 'E' = 0x45 (ESCA)
- Count: 0x00
Complete HDLC Frame¶
High-priority messages may use different XMSG handling:
7E 03 16 21 13 14 00 00 00 00 45 00 [FCS] 7E
Note: High-priority flag may be set in XMSG metadata or X.25 Q-bit
With Q-bit set:
7E 03 16 31 13 14 00 00 00 00 45 00 [FCS] 7E
^^
GFI = 0x31 (Q-bit set)
Total Frame: 16 bytes
Example 5: Ready For Input (7RFI - Flow Control)¶
TAD Message Layer¶
TAD Message: Request input (no data)
┌──────┬────┐
│ 7RFI │ 00 │
└──────┴────┘
Total TAD Message: 52 00 (2 bytes)
- Type: 'R' = 0x52 (RFI)
- Count: 0x00
Complete HDLC Frame¶
7E 03 18 21 13 16 00 00 00 00 52 00 [FCS] 7E
Total Frame: 16 bytes
Example 6: Disconnect Message (7DCON)¶
TAD Message Layer¶
TAD Message: Disconnect (no data)
┌───────┬────┐
│ 7DCON │ 00 │
└───────┴────┘
Total TAD Message: 44 00 (2 bytes)
- Type: 'D' = 0x44 (DCON)
- Count: 0x00
Complete HDLC Frame¶
This triggers X.25 Clear Request, sent as separate packet:
HDLC Frame 1 (Data with 7DCON):
7E 03 1A 21 13 18 00 00 00 00 44 00 [FCS] 7E
HDLC Frame 2 (X.25 Clear Request):
7E 03 1C 21 13 13 00 [FCS] 7E
^^
Packet type 0x13 = Clear Request
Total: 2 frames (disconnect message + X.25 clear)
Connection Establishment HDLC Sequence¶
Phase 1: HDLC Link Setup¶
Frame 1: SABM (Set Asynchronous Balanced Mode)
7E FF 3F [FCS] 7E
^^ ^^
Addr Ctrl (SABM command)
Frame 2: UA (Unnumbered Acknowledgement)
7E FF 73 [FCS] 7E
^^ ^^
Addr Ctrl (UA response)
Phase 2: X.25 Call Setup¶
Frame 3: X.25 Call Request
7E 03 00 21 13 0B [Call Data] [FCS] 7E
^^
Packet type 0x0B = Call Request
Call Data includes: - Called address (remote TAD) - Calling address (local TAD) - Facilities - Call user data (may include XMSG port info)
Frame 4: X.25 Call Accepted
7E 03 00 21 13 0F [FCS] 7E
^^
Packet type 0x0F = Call Accepted
Phase 3: TAD Configuration Exchange¶
Frame 5: INISND - Dummy Message
7E 03 10 21 13 0E 00 00 00 00 44 00 [FCS] 7E
^^^^
7DUMM (Dummy)
Frame 6: Remote sends 7TMOD
7E 03 12 21 13 10 00 00 00 00 54 01 03 [FCS] 7E
Frame 7: Remote sends 7TTYP
7E 03 14 21 13 12 00 00 00 00 54 02 01 23 [FCS] 7E
Frame 8: Remote sends 7DESC
7E 03 16 21 13 14 00 00 00 00 44 01 1B [FCS] 7E
^^
Escape char 0x1B
Multi-Buffer TAD Messages¶
Large Data Message Split Across Frames¶
When a TAD data message exceeds buffer size, it's split with continuation flags.
Message 1: First Part (RSOM set)¶
TAD Message (512 bytes):
┌───────┬─────┬────────────────────────────┐
│ 7BDAT │ 255 │ Data part 1 (255 bytes) │
└───────┴─────┴────────────────────────────┘
HDLC Frame 1:
7E 03 10 21 13 0E [XMSG metadata with RSOM] 42 FF [255 data bytes] [FCS] 7E
Message 2: Continuation (no RSOM/REOM)¶
TAD Message continues:
┌───────┬─────┬────────────────────────────┐
│ 7BDAT │ 255 │ Data part 2 (255 bytes) │
└───────┴─────┴────────────────────────────┘
HDLC Frame 2:
7E 03 12 21 13 10 [XMSG metadata] 42 FF [255 data bytes] [FCS] 7E
Message 3: Last Part (REOM set)¶
TAD Message final part:
┌───────┬────┬───────────────────────┐
│ 7BDAT │ 2 │ Data part 3 (2 bytes)│
└───────┴────�───────────────────────┘
HDLC Frame 3:
7E 03 14 21 13 12 [XMSG metadata with REOM] 42 02 [2 data bytes] [FCS] 7E
XMSG RSOM/REOM Flags¶
From XMSG analysis, buffer metadata includes:
┌─────────────────────────────────────────┐
│ LKEY (DMA control word) │
│ Bits 10-8: Buffer state │
│ 000 = Empty │
│ 001 = First (RSOM) │
│ 010 = Middle │
│ 011 = Last (REOM) │
│ 100 = Single (RSOM + REOM) │
└─────────────────────────────────────────┘
Break Character Handling in HDLC¶
Data Message with Break Character¶
When user types break character:
TAD Message Layer¶
Data with break at end:
┌───────┬────┬──────┬──────┬───────┐
│ 7BDAT │ 03 │ 'A' │ 'B' │ Ctrl-C│
└───────┴────┴──────┴──────┴───────┘
^^ ^^
Count=3 Break char
TAD marks: REMBYT = -1 (break flag)
Total TAD Message: 42 03 41 42 03 (5 bytes)
HDLC Frame¶
7E 03 1E 21 13 1A 00 00 00 00 42 03 41 42 03 [FCS] 7E
Break indication sent separately:
Next frame: 7BMMX (Break Message)
7E 03 20 21 13 1C 00 00 00 00 42 03 01 00 10 [FCS] 7E
^^^^^^^^
Break strategy 1, MaxBreak=16
Error and Flow Control in HDLC¶
RFI (Ready For Input) Sequence¶
User reads from empty buffer → TAD sends RFI:
HDLC Frame:
7E 03 22 21 13 1E 00 00 00 00 52 00 [FCS] 7E
Remote responds with data:
HDLC Frame:
7E 03 24 21 13 20 00 00 00 00 42 05 [5 data bytes] [FCS] 7E
HDLC RR (Receive Ready) Frame¶
When TAD acknowledges frames without data:
HDLC RR Frame:
7E 03 01 [FCS] 7E
^^
Control = 0x01 (RR, N(R)=0)
Acknowledges frames 0-7:
RR with N(R)=7:
7E 03 0F [FCS] 7E
^^
Control = 0x0F (RR, N(R)=7)
Complete TAD Session HDLC Trace¶
Session: Connect → Login → Command → Disconnect¶
Time Direction HDLC Frame
====== ========= ====================================================
00.000 Local→Net 7E FF 3F [FCS] 7E
(SABM - Link setup)
00.010 Net→Local 7E FF 73 [FCS] 7E
(UA - Link acknowledged)
00.020 Local→Net 7E 03 00 21 13 0B [Call data] [FCS] 7E
(X.25 Call Request)
00.100 Net→Local 7E 03 00 21 13 0F [FCS] 7E
(X.25 Call Accepted)
00.110 Local→Net 7E 03 10 21 13 0E 00 00 00 00 44 00 [FCS] 7E
(TAD: 7DUMM - Initial buffer)
00.150 Net→Local 7E 03 12 21 13 10 00 00 00 00 54 01 03 [FCS] 7E
(TAD: 7TMOD - Terminal mode)
00.151 Net→Local 7E 03 14 21 13 12 00 00 00 00 54 02 01 23 [FCS] 7E
(TAD: 7TTYP - Terminal type)
00.152 Net→Local 7E 03 16 21 13 14 00 00 00 00 44 01 1B [FCS] 7E
(TAD: 7DESC - Escape char)
00.200 Net→Local 7E 03 18 21 13 16 00 00 00 00 42 0A [login prompt] [FCS] 7E
(TAD: 7BDAT - "Username: ")
01.000 Local→Net 7E 03 1A 21 13 18 00 00 00 00 42 04 [username] [FCS] 7E
(TAD: 7BDAT - "user")
01.100 Net→Local 7E 03 1C 21 13 1A 00 00 00 00 42 0A [password prompt] [FCS] 7E
(TAD: 7BDAT - "Password: ")
02.000 Local→Net 7E 03 1E 21 13 1C 00 00 00 00 42 06 [password] [FCS] 7E
(TAD: 7BDAT - "secret")
02.200 Net→Local 7E 03 20 21 13 1E 00 00 00 00 42 0F [welcome] [FCS] 7E
(TAD: 7BDAT - "Welcome to ND!")
03.000 Local→Net 7E 03 22 21 13 20 00 00 00 00 42 06 [command] [FCS] 7E
(TAD: 7BDAT - "dir")
03.500 Net→Local 7E 03 24 21 13 22 00 00 00 00 42 FF [dir output 1] [FCS] 7E
(TAD: 7BDAT - Directory listing part 1)
03.501 Net→Local 7E 03 26 21 13 24 00 00 00 00 42 C8 [dir output 2] [FCS] 7E
(TAD: 7BDAT - Directory listing part 2)
04.000 Local→Net 7E 03 28 21 13 26 00 00 00 00 42 07 [logout] [FCS] 7E
(TAD: 7BDAT - "logout")
04.100 Net→Local 7E 03 2A 21 13 28 00 00 00 00 44 00 [FCS] 7E
(TAD: 7DCON - Disconnect)
04.110 Net→Local 7E 03 2C 21 13 13 00 [FCS] 7E
(X.25 Clear Request)
04.120 Local→Net 7E 03 2E 21 13 17 [FCS] 7E
(X.25 Clear Confirm)
04.130 Local→Net 7E FF 53 [FCS] 7E
(DISC - Disconnect link)
04.140 Net→Local 7E FF 73 [FCS] 7E
(UA - Link disconnected)
FCS (Frame Check Sequence) Calculation¶
CRC-16 Calculation for HDLC¶
Algorithm: CRC-16-CCITT (polynomial: x^16 + x^12 + x^5 + 1) Initial value: 0xFFFF Final XOR: 0xFFFF Calculated over: Address + Control + Data fields
Example Calculation:
Frame: 7E 03 10 21 13 0E 00 00 00 00 42 05 48 65 6C 6C 6F [FCS] 7E
Input to CRC: 03 10 21 13 0E 00 00 00 00 42 05 48 65 6C 6C 6F
CRC-16 Result: 0xXXXX (transmitted LSB first)
Complete frame:
7E 03 10 21 13 0E 00 00 00 00 42 05 48 65 6C 6C 6F [CRC_LOW] [CRC_HIGH] 7E
C# FCS Calculation Example¶
public static ushort CalculateHdlcFcs(byte[] data, int offset, int length)
{
const ushort POLY = 0x8408; // Reversed polynomial
ushort fcs = 0xFFFF;
for (int i = offset; i < offset + length; i++)
{
fcs ^= data[i];
for (int j = 0; j < 8; j++)
{
if ((fcs & 0x0001) != 0)
fcs = (ushort)((fcs >> 1) ^ POLY);
else
fcs >>= 1;
}
}
return (ushort)(~fcs);
}
// Usage:
byte[] frame = new byte[] { 0x03, 0x10, 0x21, 0x13, 0x0E, /* ... TAD data ... */ };
ushort fcs = CalculateHdlcFcs(frame, 0, frame.Length);
byte fcsLow = (byte)(fcs & 0xFF);
byte fcsHigh = (byte)(fcs >> 8);
Byte Stuffing (Transparency)¶
HDLC Byte Stuffing Rules¶
When data contains flag bytes (0x7E) or escape bytes (0x7D), they must be escaped:
Rule:
- If data byte = 0x7E or 0x7D
- Insert escape byte 0x7D before it
- XOR original byte with 0x20
Examples:
Original data: 42 7E 05
Stuffed data: 42 7D 5E 05
0x7E XOR 0x20 = 0x5E
Original data: 42 7D 05
Stuffed data: 42 7D 5D 05
0x7D XOR 0x20 = 0x5D
TAD Message with Stuffing¶
TAD Message: 7BDAT containing byte 0x7E
Before stuffing:
42 05 48 7E 6C 6C 6F
^^ ^^ ^^ ^^ ^^
Type Cnt H ~ l l o
After stuffing:
42 05 48 7D 5E 6C 6C 6F
^^ ^^
Escape + 5E (0x7E XOR 0x20)
Complete HDLC frame:
7E 03 10 21 13 0E 00 00 00 00 42 05 48 7D 5E 6C 6C 6F [FCS] 7E
Note: FCS is calculated BEFORE stuffing
Emulator Implementation Guide¶
Receiving TAD Messages from HDLC¶
Step 1: Parse HDLC Frame
public class HdlcFrame
{
public byte Address { get; set; }
public byte Control { get; set; }
public byte[] Data { get; set; }
public ushort Fcs { get; set; }
public bool IsValid { get; set; }
}
public HdlcFrame ParseHdlcFrame(byte[] buffer, int offset, int length)
{
// Remove flags (0x7E)
// Un-stuff escaped bytes
// Verify FCS
// Return parsed frame
}
Step 2: Extract X.25 Packet
public class X25Packet
{
public byte Gfi { get; set; }
public byte Lcn { get; set; }
public byte PacketType { get; set; }
public byte[] UserData { get; set; }
}
public X25Packet ParseX25Packet(byte[] data)
{
// Extract GFI, LCN, packet type
// Extract user data
return packet;
}
Step 3: Parse XMSG Buffer
public class XmsgBuffer
{
public ushort Reference { get; set; }
public ushort Function { get; set; }
public byte[] TadData { get; set; }
}
public XmsgBuffer ParseXmsgBuffer(byte[] userData)
{
// Extract ref/func (first 4 bytes)
// Extract TAD data (remaining bytes)
return buffer;
}
Step 4: Parse TAD Message
public class TadMessage
{
public byte MessageType { get; set; }
public byte ByteCount { get; set; }
public byte[] Data { get; set; }
}
public TadMessage ParseTadMessage(byte[] tadData, ref int offset)
{
// Check for pad byte (if offset is odd)
if ((offset & 1) != 0 && tadData[offset] == 0x00)
offset++;
// Extract message type
byte msgType = tadData[offset++];
// Extract byte count
byte byteCount = tadData[offset++];
// Extract data
byte[] data = new byte[byteCount];
Array.Copy(tadData, offset, data, 0, byteCount);
offset += byteCount;
return new TadMessage
{
MessageType = msgType,
ByteCount = byteCount,
Data = data
};
}
Sending TAD Messages as HDLC¶
Step 1: Create TAD Message
public byte[] CreateTadMessage(byte messageType, byte[] data)
{
// Calculate if pad byte needed
bool needsPad = false; // based on current offset
int size = (needsPad ? 1 : 0) + 1 + 1 + data.Length;
byte[] buffer = new byte[size];
int offset = 0;
// Add pad if needed
if (needsPad)
buffer[offset++] = 0x00;
// Add message type
buffer[offset++] = messageType;
// Add byte count
buffer[offset++] = (byte)data.Length;
// Add data
Array.Copy(data, 0, buffer, offset, data.Length);
return buffer;
}
Step 2: Wrap in XMSG Buffer
public byte[] CreateXmsgBuffer(byte[] tadMessage)
{
byte[] buffer = new byte[4 + tadMessage.Length];
// Reference (2 bytes)
buffer[0] = 0x00;
buffer[1] = 0x00;
// Function (2 bytes)
buffer[2] = 0x00;
buffer[3] = 0x00;
// TAD message
Array.Copy(tadMessage, 0, buffer, 4, tadMessage.Length);
return buffer;
}
Step 3: Create X.25 Packet
public byte[] CreateX25DataPacket(byte lcn, byte nSend, byte nRecv, byte[] userData)
{
byte[] packet = new byte[3 + userData.Length];
// GFI
packet[0] = 0x21; // Q=0, D=0, LCG=1
// LCN
packet[1] = lcn;
// Packet type (combine with N(S))
packet[2] = (byte)(0x00 | (nSend << 1)); // Data packet with N(S)
// User data
Array.Copy(userData, 0, packet, 3, userData.Length);
return packet;
}
Step 4: Create HDLC Frame
public byte[] CreateHdlcFrame(byte address, byte control, byte[] data)
{
// Calculate FCS
byte[] fcsInput = new byte[2 + data.Length];
fcsInput[0] = address;
fcsInput[1] = control;
Array.Copy(data, 0, fcsInput, 2, data.Length);
ushort fcs = CalculateHdlcFcs(fcsInput, 0, fcsInput.Length);
// Byte stuffing
List<byte> stuffed = new List<byte>();
stuffed.Add(address);
stuffed.Add(control);
foreach (byte b in data)
{
if (b == 0x7E || b == 0x7D)
{
stuffed.Add(0x7D);
stuffed.Add((byte)(b ^ 0x20));
}
else
{
stuffed.Add(b);
}
}
// Add FCS (also needs stuffing)
byte fcsLow = (byte)(fcs & 0xFF);
byte fcsHigh = (byte)(fcs >> 8);
if (fcsLow == 0x7E || fcsLow == 0x7D)
{
stuffed.Add(0x7D);
stuffed.Add((byte)(fcsLow ^ 0x20));
}
else
stuffed.Add(fcsLow);
if (fcsHigh == 0x7E || fcsHigh == 0x7D)
{
stuffed.Add(0x7D);
stuffed.Add((byte)(fcsHigh ^ 0x20));
}
else
stuffed.Add(fcsHigh);
// Add flags
byte[] frame = new byte[stuffed.Count + 2];
frame[0] = 0x7E;
stuffed.CopyTo(frame, 1);
frame[frame.Length - 1] = 0x7E;
return frame;
}
Step 5: Complete Send Function
public void SendTadMessage(byte msgType, byte[] data)
{
// Create TAD message
byte[] tadMsg = CreateTadMessage(msgType, data);
// Wrap in XMSG
byte[] xmsgBuffer = CreateXmsgBuffer(tadMsg);
// Create X.25 packet
byte[] x25Packet = CreateX25DataPacket(
lcn: 0x13,
nSend: currentSendSeq++,
nRecv: currentRecvSeq,
userData: xmsgBuffer
);
// Create HDLC frame
byte[] hdlcFrame = CreateHdlcFrame(
address: 0x03,
control: (byte)(0x00 | (currentSendSeq << 1) | (currentRecvSeq << 5)),
data: x25Packet
);
// Send to network
SendToNetwork(hdlcFrame);
}
Summary¶
TAD messages traverse 4 protocol layers before reaching the wire:
- TAD Message: Application-level terminal control
- XMSG Buffer: Message metadata and addressing
- X.25 Packet: Virtual circuit data transport
- HDLC Frame: Physical link-level framing
Key Points: - HDLC provides reliable frame transport with CRC - X.25 provides virtual circuit multiplexing - XMSG provides buffer management - TAD provides terminal semantics
For Emulation: - Parse/create all 4 layers - Handle byte stuffing in HDLC - Calculate FCS correctly - Track sequence numbers (HDLC and X.25) - Support high-priority vs. normal message handling
Document Path: Source Code\Sintran L\NPL\TAD-HDLC-Encapsulation.md
Related Documents:
- TAD-Protocol-Analysis.md - TAD protocol reference
- TAD-Message-Formats.md - TAD message specifications
- TAD-Protocol-Flows.md - TAD flow diagrams
- XMSG_Metadata_Buffer_Analysis.md - XMSG buffer format
- HDLC analysis documents - Low-level frame format