Skip to content

Latest commit

 

History

History
208 lines (162 loc) · 8.5 KB

File metadata and controls

208 lines (162 loc) · 8.5 KB

cdi_merlin.stb — the linker's symbol file, shipped

Ten sectors at LBA 129,342, 19,752 bytes, written at 13:57:18 on 31 January 1995 — one second before the executable and two before the volume descriptor.

Nothing on the disc names it. The executable never opens it. It is the Microware linker's global symbol table, and it went onto the glass master with everything else.

python tools/cdistb.py stats
python tools/cdistb.py list
python tools/cdistb.py groups
module      cdi_merlin.stb
module size 19752 bytes
symbols     887
sections    {65535: 308, 0: 83, 1: 444, 2: 52}

887 symbols with their source-level names. For comparison, Link carried 325 function names as loose NUL-terminated strings between the functions; Soccer and Origami carried none. This is a real symbol table, in its own module, with addresses attached.

The file is itself an OS-9 module

It starts 4A FC, and os9mod.py validates it: type Data, name cdi_merlin.stb at the offset in M$Name. That is the linker's doing — Microware's tools wrap the symbol file as a loadable Data module so the debugger can modload it next to the program.

Record layout

After the module name, a flat array of 16-byte records runs to offset 8,950:

u16  hi         high word of the symbol's 32-bit value
u16  value      low word
u16  flags
u32  nameoff    byte offset of a NUL-terminated name, module-relative

The name offsets climb monotonically and each delta equals the previous name's length plus one, which is what identifies the layout in the first place. Names live in one block from offset 8,950 to the end.

The two halves are one 32-bit number

addr = (hi << 16) | value

Three independent checks against cdi_merlin settle it:

symbol computed address what it must be matches
btext 0x00000000 the module's first byte yes
etext 0x0002196a M$Size, 137,578 exactly
_cstart 0x00000054 M$Exec, 84 exactly

etext landing on the module size to the byte is the one that cannot be coincidence. And hi == 0xffff simply means a negative number — an A6-relative offset, which is where an OS-9/68000 C program keeps its globals. The first instruction at _cstart writes to -32752(A6), and the symbol at −32,752 is _totmem. See 03-executable.md.

Cross-checking the other direction: of the 576 symbols in the three code sections, 452 begin with a C-compiler prologue at the address the table gives —

4e55   LINK A5,#d      167
2f00   MOVE.L D0,-(SP) 145
48e7   MOVEM.L         140

— and the rest are data labels, section bounds and library entry points that begin with a jump or a load.

Flags

The flag word takes seven values and correlates with the section:

section 0xffff (globals)   0x0000 84   0x0001 57   0x2000 132   0x2001 35
sections 0/1/2 (code)      0x0004      0x2004      0x3004

0x0004 is set on every symbol in a code section and clear on every global, so it marks the section kind. 0x2000 is set on 167 of 308 globals and 218 of 576 code symbols, in a pattern consistent with an export/visibility bit. 0x1000 appears only in combination, on 149 symbols. This is read off the data rather than from documentation; treat the last two as unconfirmed.

The four sections

The link is laid out in three code sections plus the globals, and each has a distinct character. This is the whole build, visible at once.

section base symbols what it is
0 0x00000 83 the C startup and the game
1 0x10000 444 BOLT, plus the Microware C library
2 0x20000 52 the OS-9 system-call library
0xffff A6-relative 308 globals

Section 0 — the game, 83 symbols

_cstart, _stkcheck, stacksiz, freemem, trapinit, then main at 0x2ea, and after that nothing but Merlin's Apprentice: the challenge engines, the BOSS_* shell, the NVRAM pack/unpack, the menu and token screens. Read in address order it is the game's architecture, and it is set out in 08-game-architecture.md.

Section 1 — BOLT and the C library, 444 symbols

The largest section, and it interleaves two things. The first two thirds are the engine:

0x10000  MM_TermScreen, ChallDirMenuBar, ... PresentationEngine
0x1096a  TC_*        the Traffic Cop: jukebox, animation, TOC, memory, display
0x13f78  FH_*        screens, buttons, save data, NVRAM, cursor, input, sound
0x1497c  SM_*        plane setup, button rendering, hit testing
0x16ab4  InitBOLT, TermBOLT, InitGraphics, ... graphics and MCD212
0x1892c  InitBOLTC, StopBOLTPlay, DoBOLTSeek, OpenNextFile, LibRead ...
0x19c06  OpenBOLTLib, GetBOLTGroup, GetBOLTMember, MemberAddr, MemberSize ...
0x1abec  Decompress, DrawPic, RLEDrawPic, StartFade, DoFadeInt, DoCycleInt
0x1c542  InitInLine, CopyMem, FillLong, DrawLine, FillBox, ReadPix, WritePix

and then, from 0x1d452 to the end of the section, the Microware C library with every name intact: __mulsi3, __udivsi3, _T$LMul, _T$LDiv, os9exec, modloadp, printf, fwrite, strcmp, malloc, memcpy, toupper, open, read, write, lseek.

Two details from that list are worth a sentence each.

memmove and memcpy are both at 0x1f98c. One implementation, two names, aliased in the library.

A libgcc-to-Microware thunk table. Five symbols sit at 0x1d452, four bytes apart, and every one of them is a single BRA.W:

1d452  60 00 27 08   __mulsi3    -> 1fb5c  _T$UMul / _T$LMul
1d456  60 00 27 76   __udivsi3   -> 1fbce  _T$UDiv
1d45a  60 00 27 38   __divsi3    -> 1fb94  _T$LDiv
1d45e  60 00 27 66   __umodsi3   -> 1fbc6  _T$UMod
1d462  60 00 27 5a   __modsi3    -> 1fbbe  _T$LMod

Every branch target is exactly the symbol the name calls for. __mulsi3 and friends are GCC's libgcc integer-helper names; _T$UMul, _T$LDiv and so on are Microware's. Something in this build was compiled by a GCC-family compiler that emitted calls to libgcc, and somebody wrote a twenty-byte shim to land them on the Microware runtime instead of linking libgcc. Note that _T$UMul and _T$LMul are the same address — signed and unsigned 32-bit multiply are one routine, correctly.

Section 2 — the OS-9 system library, 52 symbols

pause, crc, prerr, sleep, tsleep, _parsepath, _mkdata_module, _get_module_dir, _get_process_table, _get_process_desc, modlink, modload, munlink, ebrk, ibrk, sbrk, kill, wait, setpr, chain, os9fork, getpid, intercept, exit, abort, _utinit, _dumprof, etext.

_dumprof is the profiler's dump entry point, linked in.

Globals, 308 symbols

All A6-relative and all negative, from _mtop at −32,764 to end at −23,208 — 9,556 bytes, which is M$Mem = 9,560 rounded. The full list is in notes/symbol-inventory.md. What they name:

  • The BOLT runtime's state: BOLTChain, CurGroupPtr, CurMemberPtr, CurLibPtr, LibraryChain, BOLTMemPool, MemHighWaterMark, AsyncInProgress, AsyncSectors, ErrorRetry, Encrypt, XORMask.
  • The MCD212: FCTA, FCTB, LCTA1, LCTB1, LCTA2, LCTB2, LCTALINK, LCTBLINK, CurLCTA, VisibleLCTA, plane_order, PlaneAMem, PlaneBMem, CDIScreenMem, Interlace, PALFlag.
  • Fifteen named colours four bytes apart: BLACK, HALF_BLUE, HALF_GREEN, HALF_CYAN, HALF_RED, HALF_MAGENTA, HALF_YELLOW, HALF_WHITE, FULL_BLUEWHITE.
  • A named random number generator: R250_Index and R250_Buffer. The gap from R250_Buffer to the next global is 500 bytes — 250 sixteen-bit words. R250 is the Kirkpatrick–Stoll lagged-Fibonacci generator, and the symbol table gives both its name and its state size. srand, rand, InitRand, Random and Rand are all in section 1 beside it.
  • TitleSafeRect, and ScreenDim, FontRect, CursorRect.
  • _chcodes, the Microware C character-class table — 129 bytes, named.

Why this matters

Merlin's Apprentice has no published source. What this file gives, for free, is the name of every global and every function in a 137 KB program, in link order, with addresses that resolve. Everything in 05-bolt-library.md, 06-realtime-files.md and 08-game-architecture.md was found by reading it and then confirming the reading against the bytes.

Scan for a .stb on every CD-i disc. It costs one directory listing, and on this one it was worth more than everything else put together.