Debugging Spectrum Programs
Spectranext includes a powerful integrated debugger that allows you to debug ZX Spectrum programs using GDB or console commands. The debugger supports breakpoints, memory inspection, register access, and stack traces.
Overview
The debugger provides:
- GDB Stub: Connect GDB for full debugging support
- Hardware Breakpoints: Break on specific memory addresses
- Software Breakpoints: Break on code locations
- Memory Inspection: Read/write memory
- Register Access: View and modify CPU registers
- Stack Traces: Inspect call stack
- Source-level debugging: Debug C programs with z88dk-gdb
- All with real hardware!
Compiling with Debug Symbols
To enable source-level (C) debugging, compile your program with the -debug flag:
zcc +zx -debug main.c -create-app -m
This generates debugging symbols that allow GDB to map addresses to source code locations, enabling features like:
- Setting breakpoints by function name or line number
- Viewing variable values
- Stepping through source code line by line
- Viewing source code context
The -debug flag includes debugging information in the output files, which can be used with z88dk-gdb or standard GDB.
Assembly-Level Debugging
You can debug any program at the assembly level, even without debug symbols. This is useful for:
- Assembly programs: Programs written directly in Z80 assembly
- Low-level debugging: Understanding what the CPU is actually executing
- Optimization: Analyzing generated code from compilers
- Reverse engineering: Understanding existing programs
Debugging Without Debug Symbols
When debugging without debug symbols, you work directly with memory addresses and assembly instructions:
Set breakpoints by address:
(gdb) break *0x8000
(gdb) break *main
Disassemble code:
(gdb) disassemble 0x8000
(gdb) disassemble main
(gdb) disassemble $pc
View assembly at current location:
(gdb) x/10i $pc # Show 10 instructions from PC
(gdb) x/10i $pc-10 # Show 10 instructions before PC
Step by instruction:
(gdb) stepi # Step one instruction (step into)
(gdb) nexti # Step one instruction (step over)
(gdb) stepi 5 # Step 5 instructions
Inspect registers:
(gdb) info registers
(gdb) print/x $pc # Program counter in hex
(gdb) print/x $sp # Stack pointer in hex
(gdb) print/x $af # AF register pair
(gdb) print/x $bc # BC register pair
(gdb) print/x $de # DE register pair
(gdb) print/x $hl # HL register pair
View memory as assembly:
(gdb) x/20i 0x8000 # Disassemble 20 instructions from 0x8000
(gdb) x/20i $pc # Disassemble 20 instructions from PC
Assembly Debugging Workflow
- Load your program: Load the binary or TAP file
- Set breakpoint: Set breakpoint at entry point or specific address
(gdb) break *0x8000 - Run to breakpoint: Start execution
(gdb) continue - Disassemble current code: See what's executing
(gdb) x/20i $pc - Step through instructions: Execute one instruction at a time
(gdb) stepi - Inspect registers and memory: Check CPU state
(gdb) info registers
(gdb) x/16x $sp # View stack - Continue or modify: Resume execution or change values
(gdb) set $hl = 0x1234
(gdb) continue
Useful Assembly Debugging Commands
View memory in different formats:
(gdb) x/16x 0x8000 # Hex dump
(gdb) x/16i 0x8000 # Disassemble as instructions
(gdb) x/16c 0x8000 # Character dump
(gdb) x/16d 0x8000 # Decimal dump
View stack:
(gdb) stack # View stack contents
Example: Debugging Assembly Code
# Set breakpoint at program start
(gdb) break $8000
# Run program
(gdb) continue
# When breakpoint hits, disassemble
(gdb) dis
0x8000: ld hl, 0x1234
0x8003: ld de, 0x5678
0x8006: add hl, de
...
# Step through instructions
(gdb) si
(gdb) print/x $hl # Check HL register
$1 = 0x1234
Assembly-level debugging gives you complete control over the CPU state and allows you to debug any program, regardless of whether it was compiled with debug symbols.
Debugger Architecture
When the debugger traps (breakpoint hit or stop command), Spectranext remaps memory:
0x0000-0x0FFF - Debugger 0-page code
0x1000-0x3FFF - Unmapped
0x4000-0xFFFF - Spectrum RAM
The debugger code keeps the Z80 running in a loop until a command is received from the debugger client.
GDB Integration
Spectranext's debugger can be accessed via GDB in several ways:
Connection Options
USB Connection (Recommended)
For debugging on physical Spectranext hardware over USB-C:
The GDB debugger uses the third CDC interface (CDC interface 2). On macOS, the device appears as /dev/cu.usbmodem* (e.g., /dev/cu.usbmodem00015). On Linux, it's typically /dev/ttyACM2 (the third device, 0-indexed).
Using z88dk-gdb:
z88dk-gdb -d /dev/cu.usbmodem00015 -x your_program.sym
Note: Spectranext exposes three CDC interfaces:
- Interface 0: Terminal/Console
- Interface 1: USBFS (file transfer)
- Interface 2: GDB Server (this one)
Network Connection (WiFi/TCP)
For debugging on physical Spectranext hardware over WiFi:
z88dk-gdb -h <spectranext_ip> -p 1337 -x your_program.sym
The debugger listens on port 1337 for GDB connections over the network.
Using z88dk-gdb
z88dk-gdb is a specialized GDB wrapper designed for Z80 debugging. Connect to the debugger:
# USB connection (recommended)
z88dk-gdb -d <serial_device> -x <debug_symbols>
# Network connection
z88dk-gdb -h <host> -p <port> -x <debug_symbols>
Parameters:
-d <device>: Serial device path for USB connections (e.g.,/dev/cu.usbmodem00015on macOS,/dev/ttyACM2on Linux - the third CDC interface)-h <host>: Hostname or IP address for network connections (e.g.,192.168.1.100)-p <port>: Port number (only required for network connections)-x <debug_symbols>: Path to debug symbols file generated during compilation
IDE Integration (CLion)
You can debug Spectrum programs directly from CLion for a full-featured debugging experience with breakpoints, variable inspection, and step-through debugging.
Setup:
- Install z88dk: Ensure you have the latest z88dk installed (part of Spectranext SDK)
- Configure CMake: Use z88dk's CMake toolchain (see example below)
- Create Debug Configuration: Configure CLion to use
z88dk-gdbas the debugger
CLion Debug Configuration:
-
Go to Run > Edit Configurations...
-
Create a new GDB Remote Debug configuration
-
Set the following:
- Name:
Spectranext USB(orSpectranext WiFi) - GDB: Path to
z88dk-gdbexecutable - Target: Your compiled binary
- Symbol file: Your
.mapfile (generated with-debugflag) - 'target remote' args:
- For USB:
/dev/cu.usbmodem00015(or your device path) - For WiFi:
<spectranext_ip>:1337
- For USB:
- GDB startup commands: Leave empty or add custom initialization
- Name:
-
Set breakpoints in your source code
-
Run the debug configuration -
z88dk-gdbwill connect and upload the binary -
The debugger will stop at your breakpoints
Example Project:
See the z88dk-gdb IDE test repository for a complete working example with CLion and VSCode configurations.
Emulators
For debugging in emulators, use the emulator's GDB server:
MAME Emulator:
# Start MAME with GDB stub
mame spectrum -window -nomaximize -resolution0 768x576 -debug -debugger gdbstub -debugger_port 1337
# Connect z88dk-gdb
z88dk-gdb -h localhost -p 1337 -x your_program.sym
Fuse Emulator (Windows):
- Start Fuse
- Select "GDBServer..." from the menu
- Select "Enabled"
- Connect z88dk-gdb to the default port
Fuse Emulator (Mac):
- Start Fuse
- Go to "Preferences" > "Debugger"
- Check "Enable GDBServer"
- Connect z88dk-gdb to the default port
IDE Integration
z88dk-gdb can be integrated with modern IDEs for a better debugging experience. See the IDE Integration (CLion) section above for detailed setup instructions.
Visual Studio Code:
- Configure launch.json to use
z88dk-gdb - Set breakpoints directly in the editor
- View variables and call stack in the IDE
- See the z88dk-gdb IDE test repository for VSCode configuration examples
CLion:
- Configure remote debugger to use
z88dk-gdb - Full debugging support with breakpoints and variable inspection
- See the IDE Integration (CLion) section above for detailed setup instructions
z88dk-gdb Commands
z88dk-gdb provides a rich set of debugging commands. Here are the most commonly used commands:
Execution Control
continue/cont/c- Continue executionstep/s- Step one source line (including into calls)stepi/si- Step one instruction (including into calls)next/n- Step one source line (over calls)nexti/ni- Step one instruction (over calls)finish- Exit current function and print result if any
Breakpoints
break/b [address/label]- Set breakpoint at address or labelbreak delete [index]- Delete breakpoint by indexbreak disable [index]- Disable breakpointbreak enable [index]- Enable breakpointbreak memory8 [address] [value]- Break when memory address equals value (8-bit)break memory16 [address] [value]- Break when memory address equals value (16-bit)break register [register] [value]- Break when register equals valuedel_break/d [index]- Delete breakpoint
Memory and Register Inspection
examine/x [address]- Examine memory at addressprint/p <expression>- Print expression valueregisters/reg- Display all registersinfo locals- Show local variables in current frameinfo registers- Show register values
Stack and Frames
backtrace/bt- Show execution stackstack [size]- Examine stack (optionally specify size)frame [num]- Set or view current frameup- Go one frame updown- Go one frame down
Disassembly
disassemble/dis [address] [size]- Disassemble from address (or PC) for size bytes
Source Code
list [address]- List source code around address or current location
Advanced Features
restore <file> [address]- Restore memory from filerestore_pc <file> [address]- Restore memory and set PCset <register/address> = <value>- Set register or memory value
Example z88dk-gdb Session
# Connect to debugger over USB (recommended - third CDC interface)
z88dk-gdb -d /dev/cu.usbmodem00015 -x program.sym
# Or connect over WiFi/TCP
# z88dk-gdb -h <spectranext_ip> -p 1337 -x program.sym
# Set breakpoint
(gdb) break main
# Continue to breakpoint
(gdb) continue
# When stopped, inspect state
(gdb) info registers
(gdb) print variable_name
(gdb) x/16x 0x8000
# Step through code
(gdb) step
(gdb) next
# View stack
(gdb) backtrace
(gdb) stack 32
# Disassemble
(gdb) disassemble $pc
(gdb) disassemble 0x8000 64
# Set watchpoint
(gdb) watch write 0x8000
# Continue
(gdb) continue
IDE Integration
You can debug Spectrum programs directly from modern IDEs like Visual Studio Code or CLion, providing a full-featured debugging experience with breakpoints, variable inspection, and step-through debugging.
See https://github.com/speccytools/z88dk-gdb-ide-test
Breakpoints
Hardware Breakpoints
Hardware breakpoints are set in hardware and trigger immediately:
- Limited count: Only 1 hardware breakpoint available
- Fast: No performance impact
- Reliable: Always triggers when address matches
Software Breakpoints
Software breakpoints modify code to insert trap instructions:
- More available: 4 software breakpoints
- Code modification: Replaces instruction with RST 28h
- Restorable: Original instruction is saved
Example Debugging Session
Complete Workflow
-
Compile with debug symbols:
zcc +zx -debug main.c -create-app -m -
Start debugger target:
- Physical hardware: Ensure Spectranext is running and accessible
- Connect Spectranext to your computer over USB-C or have an active Wi-Fi
-
Connect GDB:
# USB connection (recommended - third CDC interface)
z88dk-gdb -d /dev/cu.usbmodem00015 -x main.symFor WiFi/TCP connection:
z88dk-gdb -h <spectranext_ip> -p 1337 -x main.sym -
Set breakpoints:
(gdb) b main
(gdb) b main.c:20 -
Start/continue execution:
(gdb) continue
(gdb) c -
Inspect state when breakpoint hits:
(gdb) reg -
Step through code:
(gdb) step # Step into function calls
(gdb) next # Step over function calls
(gdb) finish # Finish current function -
Continue debugging or exit:
(gdb) continue
(gdb) quit
Limitations
- Limited breakpoints: Only 1 hardware, 4 software breakpoints
- Debug symbols: Source-level debugging requires compiling with
-debugflag, which disables certain program size optimizations.
Additional Resources
- z88dk-gdb Documentation - Complete guide to using z88dk-gdb
- z88dk Debugging Guide - General debugging information for z88dk
- Memory Architecture - Understanding memory layout
- Socket APIs - Network debugging
- Check debugger source code for advanced features