GDB Stub
Spectranext cartridges support the GDB Remote Serial Protocol (RSP), enabling external tools to automate file operations, device control, and debugging tasks. This protocol allows you to build custom tools that interact with the cartridge's onboard XFS filesystem without requiring a web browser or manual file transfers.
Overview
The GDB RSP implementation in Spectranext provides:
- File operations via
vFileprotocol extension - Device-specific operations via
vSpectranextprotocol extension - Standard GDB debugging capabilities (register access, memory read/write, breakpoints)
- Connection via USB CDC or TCP socket (port 1337, for emulators)
Protocol Basics
Packet Format
GDB RSP uses a simple packet-based protocol:
$<data>#<checksum>
Where:
$- Packet start delimiter<data>- Packet payload (can contain any bytes, including null bytes)#- Checksum delimiter<checksum>- Two hex digits representing the 8-bit checksum of all bytes in<data>
Checksum Calculation
The checksum is the sum of all bytes in the data payload, modulo 256:
checksum = sum(ord(byte) for byte in data) & 0xFF
Acknowledgments
- ACK (
+): Sent when a packet is received correctly - NAK (
-): Sent when a packet checksum is invalid or packet is malformed
Interrupt Character
The interrupt character (\x03, Ctrl+C) can be sent at any time to halt execution. It is only processed when the deframer is in idle state (not receiving a packet).
Connection Methods
USB CDC (Serial)
The device appears as a USB CDC serial port (e.g., /dev/ttyACM0 on Linux, COM28 on Windows).
TCP Socket (Emulators)
Connect via TCP socket:
- Host:
localhost(or remote host if GDB server is running remotely) - Port:
1337(default)
The TCP connection is useful when using the Fuse emulator's GDB server.
Protocol Detection
To detect if a device supports Spectranext extensions, send a qSupported query:
$qSupported#<checksum>
The response will include vSpectranext+ if supported:
$PacketSize=4000;qXfer:features:read+;qXfer:auxv:read+;vSpectranext+#<checksum>
Live Spectrum screen
Send vSpectranext:screen:binary to request a complete snapshot with one
command. Over USB, the device sends one response:
F<frame-id-8>,<write-count-8>,<size-4>,<fnv1a-32-8>,<border-2>:<RSP-escaped screen bytes>
Over Wi-Fi, it sends these responses in order, without waiting for another request:
S<frame-id-8>,<write-count-8>,<size-4>,<chunk-size-4>,<chunk-count-2>,<border-2>
B<frame-id-8>,<part-2>,<offset-4>:<RSP-escaped screen bytes>
... (one B response per part)
E<frame-id-8>,<fnv1a-32-8>
Numeric fields are hexadecimal. Parts are numbered from zero. border is
the most recently observed colour from writes to port $xxFE,
in the range 0–7. It is sampled when the screen snapshot is made, so rapid
border colour changes within a video frame are not represented. Older firmware
omits this field.
Raw screen bytes $, #, *, and } are escaped as } followed by the byte XOR 0x20.
The RSP packet checksum covers the escaped payload. The final
checksum is FNV-1a (32 bit, initial value 2166136261) over the 6,912 raw
screen bytes. Accept a frame only when its ID, offsets, lengths, part count,
and final checksum all match. Each B response fits the transport's
advertised PacketSize using five portions of up to 1,400 bytes over Wi-Fi.
The complete escaped USB frame fits in its 16,384-byte RSP packet limit.
vSpectranext:screen:stream uses the same header and end response, with D
responses containing ASCII hexadecimal screen bytes. It remains available for
clients that cannot read binary RSP payloads.
The older two-command read interface remains available:
The vSpectranext:screen:begin request copies the current 6,912-byte screen shadow to a readable snapshot. The response is S followed by an eight-digit hexadecimal count of captured screen writes.
Read the snapshot with vSpectranext:screen:read:<offset>,<length>. Offset and
length are hexadecimal; each response contains exactly twice length ASCII
hexadecimal digits. A read may contain at most 4,096 bytes, and the client must
also respect PacketSize from qSupported. Read offsets 0 through 6,143 for the
Spectrum bitmap and 6,144 through 6,911 for the colour attributes. Call
screen:begin again for the next frame. Invalid ranges return E22.
The shadow currently observes writes to the normal $4000–$5AFF display memory. It starts empty at controller boot and fills as the Spectrum writes the screen; the alternate 128K display bank is not captured yet.
vFile Protocol
The vFile protocol extension provides file operations on the cartridge's local XFS filesystem. All paths and data are encoded as ASCII-hex (two hex characters per byte). Writes go to the RAM layer; use vFile:commit to copy a file or directory into the overlayed flash layer.
Packet Format
$vFile:<subcommand>:<args>#<checksum>
Commands
vFile:open
Open a file for reading or writing.
Request:
$vFile:open:<fd>,<flags>,<mode>,<path>#<checksum>
<fd>: File descriptor (hex, ignored, use1)<flags>: Open flags (hex):0x0000= O_RDONLY (read-only)0x0001= O_WRONLY (write-only)0x0002= O_RDWR (read-write)0x0008= O_APPEND (append mode)0x0100= O_CREAT (create if not exists)0x0200= O_CREAT | O_TRUNC (create and truncate)
<mode>: File mode (hex, ignored)<path>: File path (ASCII-hex encoded)
Response:
- Success:
F<fd>where<fd>is the file descriptor (hex, typically1) - Error:
F-1,<errno>where<errno>is the error number (hex)
Example:
Request: $vFile:open:1,2,0,2f746573742e747874#<checksum>
(opens "/test.txt" for read-write)
Response: F1
vFile:close
Close an open file.
Request:
$vFile:close:<fd>#<checksum>
<fd>: File descriptor (hex)
Response:
- Success:
F0 - Error:
F-1,<errno>
Example:
Request: $vFile:close:1#<checksum>
Response: F0
vFile:pread
Read data from an open file (sequential read from current position).
Request:
$vFile:pread:<fd>,<count>#<checksum>
<fd>: File descriptor (hex)<count>: Number of bytes to read (hex)
Response:
- Success:
<hex-data>(hex-encoded binary data, no prefix) - Error:
F-1,<errno>
Example:
Request: $vFile:pread:1,100#<checksum>
(read 256 bytes)
Response: 48656c6c6f20576f726c64...
(hex-encoded "Hello World...")
vFile:pwrite
Write data to an open file (sequential write at current position).
Request:
$vFile:pwrite:<fd>,<hex-data>#<checksum>
<fd>: File descriptor (hex)<hex-data>: Binary data encoded as ASCII-hex (two hex chars per byte)
Response:
- Success:
F<bytes-written>where<bytes-written>is the number of bytes written (hex) - Error:
F-1,<errno>
Example:
Request: $vFile:pwrite:1,48656c6c6f#<checksum>
(write "Hello" = 0x48 0x65 0x6c 0x6c 0x6f)
Response: F5
(5 bytes written)
vFile:size
Get the size of a file.
Request:
$vFile:size:<path>#<checksum>
<path>: File path (ASCII-hex encoded)
Response:
- Success:
F<size>where<size>is the file size in bytes (hex) - Error:
F-1,<errno>
Example:
Request: $vFile:size:2f746573742e747874#<checksum>
(get size of "/test.txt")
Response: F1000
(file is 4096 bytes)
vFile:exists
Check if a file exists.
Request:
$vFile:exists:<path>#<checksum>
<path>: File path (ASCII-hex encoded)
Response:
- Exists:
F,1 - Not exists:
F,0 - Error:
F-1,<errno>
Example:
Request: $vFile:exists:2f746573742e747874#<checksum>
Response: F,1
vFile:unlink
Delete a file.
Request:
$vFile:unlink:<path>#<checksum>
<path>: File path (ASCII-hex encoded)
Response:
- Success:
F0 - Error:
F-1,<errno>
Example:
Request: $vFile:unlink:2f746573742e747874#<checksum>
Response: F0
vFile:commit
Commit a file or directory from RAM to flash.
Request:
$vFile:commit:<path>#<checksum>
<path>: File or directory path (ASCII-hex encoded)
Response:
- Success:
F0 - Error:
F-1,<errno>
Example:
Request: $vFile:commit:2f746573742e747874#<checksum>
(commit "/test.txt" to flash)
Response: F0
vSpectranext Protocol
The vSpectranext protocol extension provides Spectranext-specific operations for device control and directory operations.
Packet Format
$vSpectranext:<subcommand>:<args>#<checksum>
Commands
vSpectranext:reboot
Trigger a ZX Spectrum reboot.
Request:
$vSpectranext:reboot#<checksum>
Response:
- Success:
OK(sent before reboot)
Example:
Request: $vSpectranext:reboot#<checksum>
Response: OK
(device reboots immediately after)
vSpectranext:autoboot
Configure autoboot from xfs://ram/ and reboot the device.
Request:
$vSpectranext:autoboot#<checksum>
Response:
- Success:
OK(sent before reboot) - Error:
E<errno>
Example:
Request: $vSpectranext:autoboot#<checksum>
Response: OK
(device configures autoboot and reboots)
vSpectranext:opendir
Open a directory for reading.
Request:
$vSpectranext:opendir:<path>#<checksum>
<path>: Directory path (ASCII-hex encoded)
Response:
- Success:
OK - Error:
E<errno>
Example:
Request: $vSpectranext:opendir:2f#<checksum>
(open root directory "/")
Response: OK
vSpectranext:readdir
Read the next directory entry.
Request:
$vSpectranext:readdir#<checksum>
Response:
- Success:
FOK,<name>,<type>,<size>,<storage>where:<name>: File/directory name (ASCII-hex encoded)<type>:Ffor file,Dfor directory<size>: File size in bytes (hex, ignored for directories)<storage>: Storage layer flag (0= RAM,1= flash/committed)
- End of directory: `` (empty string)
- Error:
E<errno>
Example:
Request: $vSpectranext:readdir#<checksum>
Response: FOK,746573742e747874,46,1000,1
(file "test.txt", type file, size 4096 bytes, committed to flash)
Request: $vSpectranext:readdir#<checksum>
Response:
(end of directory)
vSpectranext:closedir
Close an open directory.
Request:
$vSpectranext:closedir#<checksum>
Response:
- Success:
OK - Error:
E<errno>
Example:
Request: $vSpectranext:closedir#<checksum>
Response: OK
vSpectranext:mv
Move/rename a file or directory.
Request:
$vSpectranext:mv:<old-path>,<new-path>#<checksum>
<old-path>: Current path (ASCII-hex encoded)<new-path>: New path (ASCII-hex encoded)
Response:
- Success:
OK - Error:
E<errno>
Example:
Request: $vSpectranext:mv:2f6f6c64,2f6e6577#<checksum>
(rename "/old" to "/new")
Response: OK
vSpectranext:mkdir
Create a directory.
Request:
$vSpectranext:mkdir:<path>#<checksum>
<path>: Directory path (ASCII-hex encoded)
Response:
- Success:
OK - Error:
E<errno>
Example:
Request: $vSpectranext:mkdir:2f6d79646972#<checksum>
(create "/mydir")
Response: OK
vSpectranext:rmdir
Remove an empty directory.
Request:
$vSpectranext:rmdir:<path>#<checksum>
<path>: Directory path (ASCII-hex encoded)
Response:
- Success:
OK - Error:
E<errno>
Example:
Request: $vSpectranext:rmdir:2f6d79646972#<checksum>
Response: OK
Error Codes
Error responses use standard errno values:
0= Success (no error)2= ENOENT (No such file or directory)5= EIO (Input/output error)9= EBADF (Bad file descriptor)12= ENOMEM (Out of memory)17= EEXIST (File exists)20= ENOTDIR (Not a directory)21= EISDIR (Is a directory)22= EINVAL (Invalid argument)27= EFBIG (File too large)28= ENOSPC (No space left on device)39= ENOTEMPTY (Directory not empty)
Implementation Notes
Sequential I/O
vFile:pread and vFile:pwrite perform sequential I/O from the current file position. The position advances automatically after each read or write operation.
Path Encoding
All paths must be encoded as ASCII-hex (two hex characters per byte). For example:
/→2f/test.txt→2f746573742e747874/my dir/file.bin→2f6d79206469722f66696c652e62696e
Binary Data Encoding
Binary data in vFile:pwrite requests must be hex-encoded. Binary data in vFile:pread responses is returned as hex-encoded.
Packet Size Limits
The maximum packet size is as advertised in qSupported.
For large file transfers, split data into multiple vFile:pwrite operations.
Reference Implementation
The spx command-line tool (sdk/bin/spx.py) provides a complete reference implementation of the GDB RSP protocol for Spectranext. You can use it as a reference for building your own tools.
See Also
- Syncing with Computer - Using the
spxtool for file operations - XFS Filesystem - Understanding the onboard filesystem
- GDB Remote Serial Protocol Specification - Official GDB RSP documentation