7. Source-level debugging¶
A program built from C, assembly or BASIC source can be debugged by file:line
instead of by address: a breakpoint set on a line, a step that moves a
statement at a time, a Source tab that follows the program counter. JNEXT does
this from files the toolchain already writes (an sjasmplus SLD, a z88dk map and
its listings, a NextBuild Memory.txt). It reads them into two stores (and the user's own labels and comments into a third, 7.4.6; a z88dk map's C variables ride inside the source map, 7.9) and
answers every question about them from those stores plus one fact only the
running machine knows: which memory supplies the instruction at an address
right now.
This chapter is the maintainer's description of that machinery. The user's side, with the procedures (loading a map, setting a line breakpoint, stepping by statement), is chapter 10 of the user guide, Source-level debugging. Nothing here repeats a procedure; where a page needs one, it says so and points there.
The files on the left are written by other people's tools. An adapter reads each into a store; the stores belong to the backend; the machine contributes only the memory identity of the address being asked about. Lifetime (7.6) decides what happens to the stores when the machine is replaced.
The three layers¶
Adapters know a file format and nothing else. Each parses a whole file
before it touches a store, and each hands over compiler-neutral data
(7.3, 7.4). They live in src/debug/:
sld_loader.*, z88dk_source_map.*, z88dk_lis.*, z88dk_space.* and the
loaders of symbol_table.*.
Stores know no format. SourceMap (source_map.*) holds ranges of bytes
tagged with the memory they came from (7.2);
SymbolTable (symbol_table.*) holds labels keyed by that memory and an
address. Both are owned by Debugger::Impl and replaced only by a complete,
successful load. The user's own labels and comments (UserAnnotations,
user_annotations.*) are a third store, beside the SymbolTable and winning over it, that no toolchain
fills (7.4.6).
Consumers ask a question and get an answer about the machine as it is now:
the location of an address, the addresses of a line, the name of a label, the
lines a run has executed. They go through the facade (debug/debugger.h);
none holds a store.
What ties the layers together is MemSpace (debug/mem_space.h), the type
that names which memory a byte lives in. A source range, a label and a
breakpoint are all qualified by it, and 7.1 is the page
that explains why: on a Next the same logical address can be a RAM page, a
ROM, the DivMMC's memory or the Multiface's, and a line number is only
meaningful for one of them.
The pages¶
| Page | Answers |
|---|---|
| 7.1 Which memory supplied the instruction | What MemSpace and FetchSource mean, how the machine predicts and records the one that supplies a fetch, how a space is spelled (the long address and its stable tags), and how coverage is keyed by it |
| 7.2 The source map | The range store: the primary location, lookup_all(), resolve(), aliases, embedded texts, and the program identity |
| 7.3 Loading | The adapter contract, content dispatch, the sidecar search, and who owns what |
| 7.4 File formats and records | One page per input: sjasmplus SLD and --sym/--exp, the z88dk map and listings, Memory.txt, and the user's .jdp annotations file; each with the producer's code, the grammar, the rules and a real excerpt |
| 7.5 Breakpoints, steps, history and call frames | Source-line breakpoints, source_step(), Back and Reverse Continue, and the call stack they lean on |
| 7.6 Lifetime | What happens to the stores on a reset, a snapshot, a rewind, an RZX, a tape load or a dot command |
| 7.7 The frontends | Who reads the stores: the Qt Source tab in full (refresh, file search, highlighter and schemes, gutter, selection sync with the Disassembly), the long address in the views, and the GDB, ZRCP, DZRP, script and command-line surfaces |
| 7.8 Tests, fixtures and adding a producer | The suites, where the fixtures come from, and the checklist for supporting another toolchain |
| 7.10 C frames and values | The C frames at a stop (the frame base of each compiler, tracked and fallback walks, the floor of a frame), the function extents they stand on, and who keeps the call-stack tracker running |
| 7.9 C variables | The variable store a z88dk map carries: the __CDBINFO__ records joined to the map's symbols by module, the storage of each variable, each function's scope table, and its lifetime |
Where the code lives¶
| Area | Files |
|---|---|
| Stores and adapters | src/debug/source_map.*, sld_loader.*, z88dk_space.*, z88dk_source_map.*, z88dk_lis.*, symbol_table.*, source_file.*, source_search.* |
| Memory identity | src/debug/mem_space.h, long_address.*, mem_coverage.*; the arbiter in src/memory/mmu.h; Emulator::predict_fetch() in src/core/emulator.cpp |
| The backend's side | src/debug/debugger_source.cpp (stores, sidecars, lifetime, source_step()), debugger_impl.h, debugger_events.cpp and debugger_control.cpp (line breakpoints, run to line), debugger_inspect.cpp (coverage), trace.*, call_stack.* |
| The loop owners' side | src/platform/emulator_boot.h (--source-map), and the three load_program_sidecars() callers in headless_app.cpp, sdl_app.cpp and gui/qt_app.cpp |
| The Qt frontend | src/debugger/source_panel.*, source_highlighter.*, breakpoint_model.*, debugger_manager.*, memory_text.h |
Who reads the stores today¶
7.7 is the map from a frontend to the part of the chapter it depends on, with the
Qt Source tab described in full. In short: the Qt debugger draws the source; GDB prints it
(monitor src); ZRCP's smartload reaches the sidecars; scripts subscribe to FILE:LINE; the loop
owners attach --source-map; DZRP has no source surface at all (DeZog reads the SLD or map itself).
The facade verbs themselves (load_map(), source_map(), source_location(),
source_step(), subscribe_source_line()) are listed in
3.9.1.