SamForth disassembled
Description of the page content
Disassembling of SamForth.
Project developed from 2012-12-22 to 2013-06.
SamForth is a Forth for the SAM Coupé, written by John A. Avis in 1991. SamForth is freeware but unfortunately its source is lost.
Goal
The goal of this disassembling project was to reuse the code of SamForth as the base of a new Forth for the SAM Coupé, focused on cross development with SimCoupe. That was the unfinished project ForthCoupe.
Final status
Done:
- Word headers.
- SamForth internal variables.
- Data zones: data stack, return stack, TIB and pad.
- RST calls and their vectors.
- Main routines.
- Addon routines.
To do:
- Investigate and label the remaining routines.
- Document the memory paging.
- Document the SAM BASIC file interface.
The dissasambled code can be assembled back with both pyz80 and Pasmo.
Tools
Among several Z80 disassemblers available for GNU/Linux, I choosed z80dasm (version 1.1.3).
In order to automate the creation of the blocks and symbols files for z80dasm, a pre-processing tool was written in Gforth: SamForth2z80dasm.
Unfortunately, z80dasm doesn't provide a way to mark defm
and defs
blocks. It also creates one comment and two bound symbols for every defined block, what sometimes is inconvenient. The assembler also converts literal numbers to address labels when their values are equal. That had to be fixed. Beside those issues, other specific changes were required in the final source. In order to solve all issues get a tidy and definitive Z80 source, a post-processing tool was written in the Vim language: z80dasm2tidySamForth.
A shell wrapper is provided in the download section. It calls all tools step by step with the requried arguments.
SamForth[-A] and SamForth-B
There are two versions of SamForth:
- SamForth runs at 0000h and pages the ROM in when needed.
- SamForth-B runs at 4000h with the ROM paged in.
In this project, for clarity I call them SamForth-A and SamForth-B respectively, while the name SamForth is used only when applicable to both versions.
There are more details about both SamForth versions in the SamForth documentation.
Screenshots
Start screens of SamForth-A and SamForth-B, showing partial lists of their words:
Reverse engineered information
SamForth word headers
- 2 bytes: Name Link Address: the address of previous word name in the dictionary.
- 1 byte: bits 0-5 are the word name length (so the maximum word length is 63 characters); bit 6 is the precedence bit; bit 7 is the compile-only bit.
- n bytes: the word name, pointed by the name link address of the next word in the dictionary.
- Code Field Address: the Z80 code, finished by
ret
or a jump to a subroutine.
SamForth engine
SamForth is a STC (Subroutine Threaded Code) Forth (see a detailed description on threading techniques by Brad Rodriguez). Thus the Z80 return stack is used as Forth return stack. This has the advantage the inner Forth interpreter is much simpler and Z80 code can be more easily mixed with Forth code.
The data stack is managed with routines (SamForth-A uses rst
calls). In both SamForth variants the data stack code is identical and at first sight it's quite heavy, because a copy of SP is restored just to do the task and then saved back. A more usual aproach would be to use a Z80 register as data stack pointer, but some timings revealed that the John Avis' original code is already the fastest option.
The stacks
SamForth uses the Z80 return stack as Forth return stack.
The Forth data stack's bounds and pointer are kept in the SamForth variables: STACK
, STKEND
and SP
. The data stack is manipulated with the Z80 stack commands, but first the Z80's SP
is copied into the SamForth's RSTACK
variable, and restored at the end.
Z80 registers
The YI
register points to SamForth's FLAGS
variables. HL
and DE
are used to pop from and push to the data stack.
Variables
There's a variables list included in the documentation, but some descriptions are vague.
For example, the FLAGS
variable holds several control bits. In SamForth-A bits 1, 5 and 7 of FLAGS
are used; in SamForth-B only bits 1 and 7 are used. The meaning of the bits will be finally discovered when the last routines will be understood.
Addon routines
Both BASIC loaders poke two short routines at 50000 (0c350h) and 50020 (c364h). The first one is used to start SamForth from BASIC. The last one is identical in both versions
SamForth-A also loads a third routine from disk, at 50040.
Downloads
Tools:
- dasm_samforth.sh (3.41 KiB), a simple wrapper for the whole process.
- samforth2z80dasm.fs (48.10 KiB), the SamForth2z80dasm tool.
- samforth2z80dasm.fs.gz (12.17 KiB), the SamForth2z80dasm tool, gzipped.
- z80dasm2tidysamforth.vim (34.25 KiB), the z80dasm2tidySamForth tool.
- samforth-a.bin (16.00 KiB), the SamForth-A binary code.
- samforth-a.bin_blocks.txt (24.40 KiB), its blocks file, required by z80dasm and created by SamForth2z80dasm.
- samforth-a.bin_symbols.z80s (25.59 KiB), its symbols file, required by z80dasm and created by SamForth2z80dasm.
- samforth-a.z80s.gz (15.91 KiB), the SamForth-A disassembled code.
- samforth-a_50000.z80s (687 B), the SamForth-A routines at 50000 and 50020, disassembled.
- samforth-a_50040.z80s (3.25 KiB), the SamForth-A routine at 50040, disassembled.
- samforth-b.bin (16.00 KiB), the SamForth-B binary code.
- samforth-b.bin_blocks.txt (26.52 KiB), its blocks file, required by z80dasm and created by SamForth2z80dasm.
- samforth-b.bin_symbols.z80s (25.79 KiB), its symbols file, required by z80dasm and created by SamForth2z80dasm.
- samforth-b.z80s.gz (15.81 KiB), the SamForth-B disassembled code.
- samforth-b_50000.z80s (657 B), the SamForth-B routines at 50000 and 50020, disassembled.
Original SamForth by John Avis (1991), ready to be used on SimCoupe:
- SamForth.mgt.gz (29.09 KiB), SamForth disk image.
- SamForthDocs.mgt.gz (26.90 KiB), disk image with the original SamForth documentation.