Disassemblies in skoolkit and Ghidra¶
jnext can hand what the debugger knows (its labels, your comments, and which bytes the program really executed, read or wrote) to two reverse-engineering tools, and read labels and comments back from them: SkoolKit (a Z80 disassembler driven by control files) and Ghidra (through its ImportSymbolsScript.py symbol import).
The workflow¶
- Run the program with
--coverage(or Debug ▸ Coverage ▸ Enable Coverage, see Memory coverage), so jnext learns which bytes are code and which are data. - Export: Map ▸ Export ▸ skoolkit Control Files… or Ghidra Symbols…, which asks for a
directory; or
--export-skoolkit DIR/--export-ghidra DIRon the command line, written when jnext exits. The directory is created. - Open the result in the tool, and, if you want to keep the names you gave there, bring them back with
Map ▸ Import ▸ … or
--import-skoolkit FILE/--import-ghidra FILE.
An export writes what the Disassembly shows: your own labels over the loaded files' labels, your comments, and (for skoolkit) the code and data map. Without coverage the control files still carry the labels and comments; each window is then one code block.
A report box tells how many files, labels and comments were written, and anything you should know (a name skipped, a memory whose bytes cannot be read). A directory you exported to before has its own earlier files replaced, and nothing else in it is ever touched. A file of the same name that is not jnext's makes the export fail before it writes anything.
One name, one address. A name is written once in an export. z88dk gives the labels
of a library routine the same name in several routines (end_x at $80CA and at $8103), but SkoolKit
refuses a label defined twice (skool2asm.py: Duplicate label) and jnext's own import and labels keep one
address per name. Of the labels that share a name, the export keeps the first, in this order: the labels
that belong to no memory, then the memories (RAM pages, ROMs, ...) in the debugger's order, then the
lowest address. Each of the others is left out and named in the report box, with the one that kept the
name (label 'end_x' at P04:$8103 skipped: the name is already used at P04:$80CA). Give the one you want
to keep a name of its own first (Your own labels and comments) if the
wrong one would win. The Ghidra view.ghidra.txt is made unique in itself, the same way.
SkoolKit¶
The Next has 224 pages of 8 KB, and a control file describes one 64 KB view. So the export writes one control file and one image per memory window:
| File | Holds |
|---|---|
C000-P10.ctl |
the labels, comments and code/data map of the page P10 (RAM page 0x10), seen at $C000 |
C000-P10.bin |
that window's bytes, as the debugger reads them now |
0000-ROM0.ctl / .bin |
ROM 0, a 16 KB window |
0000-0CPU.ctl |
the labels that belong to no particular memory (a --map file's, for example) |
The names are the debugger's (Which memory): P0A, ROM1, DIVRAM2…
The first line of each file, # jnext space P10, is a comment skoolkit ignores; jnext reads it back.
- One page:
sna2skool.py -o 0xC000 C000-P10.binfindsC000-P10.ctlby itself. - A 48K or 128K snapshot (
--delayed-snapshot):sna2skool.py -c DIR snapshot.snareads every control file of the directory, in name order, which is address order. Pass only the memories the snapshot holds: on a 48K machine they are all of them, on a 128K machine the pages that were mapped.
In the code/data map, bytes the program executed are code (c), bytes it only read or wrote are data
(b) and the rest is unused (u). skoolkit knows only the plain Z80: the Next's own instructions
(NEXTREG, MUL, PUSH nn…) are written as DEFB, so the code after them stays aligned. A label
that falls inside an instruction or a data run gets a split of its own, so skoolkit does not drop it
without a word (it does that to any label that is not at the start of a line).
A page you labelled in two windows (for example $4000 and $C000) gets a file for each; the
labels of one window do not appear in the other's.
A label that belongs to no particular memory is written once, in 0000-0CPU.ctl. Every exported window
that covers its address also gets a split there, which is what lets skoolkit keep the label. At an address
that no exported window covers (a page with no labels and no coverage there) it has no window to split in,
and sna2skool -c DIR leaves it out.
Ghidra¶
Ghidra's ImportSymbolsScript.py reads lines of NAME ADDRESS l. Since Ghidra 12.1 the script is
part of the Jython extension, which is not installed by default: install it first (File ▸ Install
Extensions…, tick Jython), then run the script from the Script Manager and pick the file.
| File | Lines | Use |
|---|---|---|
view.ghidra.txt |
main 0x8000 l |
the labels as the debugger shows them now; for a flat 64 KB image, no setup |
P04.ghidra.txt |
main P04:0x8000 l |
the labels of one memory; needs an overlay named P04 |
CPU.ghidra.txt |
rst8 0x0008 l |
the labels that belong to no particular memory |
To use a per-memory file, create in the program's memory map an overlay block named exactly as the
memory (P04) over the range the page was mapped at, then import its file. The script stops at the
first line whose overlay does not exist, which is why there is a file per memory and not one big file:
import only the files whose overlays you made. An overlay covers the window you made; a page you
labelled in two windows has both addresses in its one file.
Ghidra's symbol import carries no comments and no code/data map.
Import¶
Map ▸ Import ▸ skoolkit Control Files… (several files at once) or Ghidra Symbols… read labels and comments into the same store as your own labels (Your own labels and comments), so Save Annotations keeps them. An imported label replaces your label at the same address in the same memory; the same for a comment. All the files are checked first: if any of them is wrong, the message names the file and line and nothing is imported.
- From skoolkit jnext takes the
@ ADDRESS label=NAMElines and the comment text of the instruction sub-blocks (B,C,S,T,W, with their.lines). The block titles (sna2skool makes its own), the descriptions and every other directive are counted in the report and not imported. A control file thatskool2ctl.py -k -hmade from your edited disassembly imports fine. - Which memory. The file's
# jnext spaceline, else its name (P04.ctl,8000-P04.ctl); a Ghidra line may carryP04:. A file with none of these (a control file of your own, say) puts each entry in the memory that supplies that address at that moment, which with--import-skoolkiton the command line is before the program is loaded. - Strict reading. jnext refuses what skoolkit would only warn about and then ignore: an address
that is not decimal,
$hexor%binary, or is above 65535; a parameter skoolkit cannot read; an unknown directive; a label that is not a name; one name at two addresses.
Command line¶
| Option | |
|---|---|
--export-skoolkit DIR / --export-ghidra DIR |
write at exit; a failure exits with status 1 |
--import-skoolkit FILE / --import-ghidra FILE |
read at start, after --annotations; repeat for several files; a bad file exits with status 1 |
--coverage |
record the code/data map |
See also the command line reference.