7.4.1 sjasmplus SLD¶
SLD, "Source Level Debugging data", is the only format here that carries a machine's memory model, one record per instruction and the labels in one file. It is the format JNEXT likes best, because it names the page of every instruction.
1. The producer¶
sjasmplus, option --sld[=FILE]. The fixtures were made with sjasmplus v1.23.1
(z00m128/sjasmplus commit 46aae3fa784cae903345b87bd6b9db16a77c4bea):
sjasmplus --sld=equlabels.sld equlabels.asm
Without a file name the default is the first source's name with the extension
sld.txt (OpenSld_buildDefaultNameIfNeeded(), sjasmplus sjasm/sjio.cpp:1605),
which is why the sidecar search tries both <stem>.sld and <stem>.sld.txt
(7.3). Records are written only for code assembled inside a
DEVICE (IsSldExportActive() needs a device id; the sjasmplus manual,
docs/documentation.xml chapter c_sld_data, line 5479).
2. The writer¶
All in sjasmplus:
sjasm/sjio.cpp:1587writes the first line,|SLD.data.version|1. An optional||K|KEYWORDS|...line follows (sjasm/sjio.cpp:1588-1597).sjasm/sjio.cpp:1629-1687,WriteToSldFile(), writes every record. Its own comment (sjasm/sjio.cpp:1630-1666) documents the layout; thesnprintfis atsjasm/sjio.cpp:1683:"%s|%s|%s|%s|%d|%d|%c|%s\n". The position isline[:colBegin[:colEnd]](WriteToSldFile_TextFilePos(),sjasm/sjio.cpp:1573-1578).- The device record:
sjasm/devices.cpp:275-291buildspages.size:%d,pages.count:%d,slots.count:%d,slots.adr:A,B,...and writes it as aZrecord with page and value-1. - The
Lrecord's data,module,main,local[,+trait...], is built byExportLabelToSld()(sjasm/tables.cpp:135-185); the module markersmodule,,,+moduleandmodule,,,+endmodbyExportModuleToSld()(sjasm/tables.cpp:187). The traits written are+local,+equ,+macro,+smc,+reloc,+reloc_high,+used,+struct_def,+struct_dataand+sizeof(sjasm/tables.cpp:174-183).
3. The grammar¶
A line is either the version line, a comment, or a record of eight
|-separated fields:
|SLD.data.version|1
||<anything>
<file>|<line>[:<col>[:<endcol>]]|<deffile>|<defline>|<page>|<value>|<type>|<data>
| Field | Meaning |
|---|---|
file |
Source file, never empty in a record |
line |
The source position: the line, then optionally the start and end column |
deffile, defline |
Where the instruction was defined, when that differs (inside a macro); 0 otherwise |
page |
The device page of value; -1 where the value is not an address in a page |
value |
A 16-bit address, or a full 32-bit value for an EQU |
type |
T instruction trace (data empty), D an EQU symbol, F a function label, L a label, Z a device (memory model) change |
data |
By type: empty, the name, the name, module,main,local[,+traits], pages.size:... |
4. The parser's rules¶
load_sld() in src/debug/sld_loader.cpp applies these, in file order.
- Header. Line 1 must equal
|SLD.data.version|1(a trailing CR is dropped), else"missing SLD.data.version 1 header"and the whole file is refused. ||lines are comments, except||program.name,||program.sha256,||program.organd||program.size, which are the identity (below). A||line without a:is ignored.- Eight fields. Any other line is split on
|and must have exactly 8 fields, else"malformed SLD record at line N". Z, the device. Page and value must be-1.parse_device()requirespages.size:,,pages.count:,,slots.count:,,slots.adr:in that order; a page size of exactly 8192; 1 to 224 pages (the Next's 224 8 KB RAM pages) and at least one slot; as many slot addresses asslots.count, each in 0..0xFFFF, strictly ascending, none that would run past0x10000, none overlapping. Exactly one device record is allowed ("SLD contains multiple device records","SLD must contain one device record"). Every device error refuses the whole file.T, a trace (source half). A trace before the device record is an error; so is an empty file field, a non-empty data field, a position that is notline[:col[:endcol]]with each number at least 1 (parse_position()), apageoutside[0, pages.count)or above0xFF, or an address outside0..0xFFFF. The line and the start column are kept; the end column and the definition fields (deffile,defline) are ignored, so a macro's instructions map to the line that invoked it. The range is{Page{page}, address, hi, {file, line, column}}.L, a label (symbol half, only if a label table was given).parse_label(): the data is split on,; at least three parts and a non-emptymainare needed (so module markers name nothing and are skipped). The name ismodule.main.localwith the empty parts and their dots left out, as sjasmplus--symspells it. The address must be 0..0xFFFF. A record whose traits include+struct_defis a struct's size or a field offset, not an address: it goes to the name-only values (add_value()).+sizeofonly says thatSIZEOF(label)was used somewhere, and the record is an ordinary label (sjasmplussjasm/tables.hLABEL_HAS_SIZE). A page of-1makes an unpaged label; any other page must be in[0, pages.count)(and at most0xFF), else the record is skipped.- Failures of the source half. Without a label table the first bad record
returns an error at once. With one, the scan goes on and the first error is kept
(
source_error) so the labels still load: the source half is refused (count == -1,labels >= 0), the label half is not. The device and identity errors are never recoverable this way. - No traces. With no trace record and no earlier error:
"SLD contains no source traces".
5. Space and extent¶
Space. A trace in page p belongs to MemSpace::page(p): the SLD page
number is the device's 8 KB page number, which for DEVICE ZXSPECTRUMNEXT is the
NR 0x50-0x57 number, so a record is exactly the memory it names (7.1).
value is the CPU's logical address and is kept as the range's lo.
Extent. A trace has a start and no end, so the adapter gives it one
(sld_loader.cpp, near the end of load_sld()): the range covers its
instruction up to the next distinct start in the same page, and never past
the end of the 8 KB window it starts in, (lo & 0xE000) + 0x2000. Records that
share a start share the extent. A zero-width range cannot result.
6. Identity¶
sjasmplus writes no program identity. ||program.name, ||program.sha256,
||program.org and ||program.size are a jnext convention that a build script
appends (introduced with the SLD identity check); grep -rn
program.sha256 ~/src/spectrum/sjasmplus finds nothing. They are all or none:
- the SHA-256 must be exactly 64 lowercase hex digits (
valid_sha256()), else"invalid SHA-256 in SLD program identity"; - all four must be present,
program.namenon-empty,orgat most0xFFFF,sizeat most0x10000andorg + sizeat most0x10000, else"invalid or incomplete SLD program identity".
The identity then is SourceProgramIdentity { sha256, org, size }; the name is
only checked. It is the hash form of 7.2: the SHA-256 of
size bytes of logical memory from org, as they were right after the load. A
bad identity refuses the whole file, labels included.
7. What is skipped¶
D (EQU symbols) and F (function labels) records are well-formed records the
loader does not use (the same names arrive as L records). The ||K|KEYWORDS|
line is a || line. A +struct_def record, a module marker, a label whose value
is past 0xFFFF or negative (an EQU of 0x10000 or -1), and a label in a page the device
does not have are skipped as labels. The definition position is ignored (rule 5).
8. Excerpt¶
From test/fixtures/sjasmequ/equlabels.sld, produced by sjasmplus v1.23.1 from
equlabels.asm (unmodified, 50 lines; lines 1-4 and 13-22 shown):
|SLD.data.version|1
equlabels.asm|3||0|-1|-1|Z|pages.size:8192,pages.count:224,slots.count:8,slots.adr:0,8192,16384,24576,32768,40960,49152,57344
equlabels.asm|5||0|-1|16384|D|SCREEN
equlabels.asm|5||0|-1|16384|L|,SCREEN,,+equ,+used
equlabels.asm|18||0|-1|2|L|,point,,+struct_def
equlabels.asm|18||0|-1|0|L|,point.x,,+struct_def
equlabels.asm|18||0|-1|1|L|,point.y,,+struct_def
equlabels.asm|21||0|4|32768|F|main
equlabels.asm|21||0|4|32768|L|,main,
equlabels.asm|21||0|4|32768|T|
equlabels.asm|22||0|4|32771|T|
equlabels.asm|23||0|4|32774|F|main.loop
equlabels.asm|23||0|4|32774|L|,main,loop,+local,+used
equlabels.asm|23||0|4|32774|T|
main at 32768 ($8000) is page 4. The two T records at 32768 and 32771
become the ranges Page{4}:[$8000, $8003) for line 21 and [$8003, $8006) for
line 22 (the next distinct start in the page is 32774, which is line 23's).
point, point.x and point.y are name-only values (sizes and offsets), and
SCREEN is unpaged (page -1).
The identity records are synthetic: no public producer writes them, and
there is no committed fixture. This is the SLD that write_text builds in code in
test/debug/source_map_test.cpp:144-154, to exercise the identity check; kNextDevice is
the Z record's tail (kNextDevice, source_map_test.cpp:85-87):
|SLD.data.version|1
||program.name:game.bin
||program.sha256:9f64a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a
||program.org:32768
||program.size:4
main.bas|6||0|-1|-1|Z|pages.size:8192,pages.count:224,slots.count:8,slots.adr:0,8192,16384,24576,32768,40960,49152,57344
main.bas|6||0|4|32790|T|
...
The hash is of the four bytes 01 02 03 04 at $8000.
9. The rows¶
source_map_test:SLD-*(the parser's refusals and acceptances),SLDL-*(labels, withequlabels.symas the oracle for names and values),SRCRNG-*(extents and resolution),SRCMAP-*,SRCPATH-*,SYMPG-*.source_debug_test:SRCDBG-SIDE-*(the sidecar),SRCDBG-SYM-*,SRCLIFE-*(7.6).- Functional:
sld-labels-funcandsource-map-func. test/fixtures/sjasmequ/README.mdrecords howequlabels.sldandsizeof.sldwere made.