Produced by tools/hunk.py and
tools/modmap.py; raw output in
notes/cosm-hunks.txt and
notes/source-modules.txt.
/cosm was linked with its debug information intact. It carries 78
HUNK_DEBUG blocks: one index and 77 line-number tables, one per assembler
source file. Between them they name every module of the game, its directory,
the first source line that produced code, and where in the binary that code
starts.
The index block is a Devpac/HiSoft-style header:
ULONG 0
char "HEADDBGV01"
...
ULONG 77 number of following blocks
ULONG offset[77] file offset of each HUNK_DEBUG that follows
Each of the 77 blocks is an HCLN line table:
ULONG 0
char "HCLN"
ULONG name length in longwords
char name[] NUL-padded, e.g. "CDCODE/CDXLINT.S"
ULONG count number of line-table entries
UBYTE first_line first source line that emitted code
UBYTE 0
UWORD start | 1 start offset, low bit set as an end marker;
if this word reads 0, a ULONG start | 1 follows
(UBYTE line_delta, UBYTE byte_delta) x count
The variable-width start offset is the only awkward part: small offsets are
stored as a word with bit 0 set, large ones as a longword with the same
convention and a zero word in front of them. The decoding was checked against
the disassembly rather than assumed — MAIN/NVL.S lands exactly on the
nonvolatile.library calls, FUNCTIONS/READJOYPORT.S exactly on the
lowlevel.library ReadJoyPort call, and DECRUNCH/DECRUNCHER.S exactly on
the routine the CPIC handler branches to.
The (line_delta, byte_delta) stream decodes cleanly for most modules and
desynchronises for a handful, so this document does not quote per-module code
sizes. The module names, their entry counts and their first code offsets
are solid; the byte totals are not, and the honest form of that is to leave them
out. The "gap to next" column in notes/source-modules.txt is the distance to
the next module's first byte, which is an upper bound and not a size.
Fourteen directories. Reconstructed from the 77 names:
cosm.s
MAIN/ GLOBALGFX NVL LOOKATCD GLOBALAUDIO PT_RBFINTERRUPT LOADLEVEL
PLINK/ READDATA
LEVELS/ FIRSTDEATH DEATHPLSTUB DEATH1..5 EOLBDIE1..5
EOLBTWEEN1..5 EOLBKILLYOU1..5 WSTO1..5 CORELEVEL GAMEOVER
CDCODE/ INITCD CDXLINT DOOROPENINT CDALIASES FILELIST
TASKS/ CDTASK CONSOLETASK DECRUNCHTASK DECRAM
DECRUNCH/ DECRUNCHER CLEARBORDER
FRAMEHANDLERS/ INTERACTIVE256 NONINTERACTIVE32 NONINTERACTIVE64
NONINTERACTIVE128 NONINTERACTIVE256
COPPER/ BBITCOPPERLIST CBITCOPPERLIST DBITCOPPERLIST EBITCOPPERLIST
DOUBLECBITCOPPERLIST DOUBLEDBITCOPPERLIST DOUBLEEBITCOPPERLIST
COLOURCOPPERLIST LOGOCOPPERLIST
INTERRUPTS/ GLOBALVBLINT GLOBALAUDIOINT
VBLHOOKS/ GLOBALVBLCOMMANDHANDLER
FUNCTIONS/ WAIT GRABA5 MAKETASK GETSIGNAL INITMSGPORT DRAWSPRITE
READJOYPORT
AUDIO/ VBLAUDIO
DEBUG/ PRINT NUM DEBUGCOPPERLIST
SYSTEM/ SYSTEM
The whole game is one program with five level scripts. LEVELS/ holds five
parallel families — DEATHn, EOLBDIEn, EOLBTWEENn, EOLBKILLYOUn, WSTOn
— one per level, all five present, all five with real code in the binary
including the 4 variants whose end-of-level boss was never built
(03). WSTO is the waystation, the hub the game
returns to between levels; waystat.code is the overlay that goes with it.
The five FRAMEHANDLERS/ modules are the five video modes, and they line
up one-for-one with the copper lists:
| frame handler | copper list | bitplanes | colours |
|---|---|---|---|
NONINTERACTIVE32.S |
BBITCOPPERLIST.S |
5 | 32 |
NONINTERACTIVE64.S |
CBITCOPPERLIST.S (and DOUBLE) |
6 | 64 |
NONINTERACTIVE128.S |
DBITCOPPERLIST.S (and DOUBLE) |
7 | 128 |
NONINTERACTIVE256.S |
EBITCOPPERLIST.S (and DOUBLE) |
8 | 256 |
INTERACTIVE256.S |
DOUBLEEBITCOPPERLIST.S |
8 | 256 |
The B/C/D/E naming is a plane count in a private notation and the
mapping was read out of the copper lists themselves, not guessed
(08). The single INTERACTIVE handler is the one used
while the player is flying; everything else on the disc — logos, briefings, the
theatre, the boss cut-scenes — runs through a non-interactive handler.
DECRUNCH/ is the video codec, not a file cruncher. Nothing on this disc is
compressed in the ordinary sense (05 covers the
census). DECRUNCH/DECRUNCHER.S starts at hunk offset 0x0a3b0, and the CPIC
chunk handler at 0x979e branches straight to 0x0a3b8. TASKS/DECRUNCHTASK.S
is the task that drives it and TASKS/DECRAM.S manages its buffers.
DECRUNCH/CLEARBORDER.S blanks the frame around the 320 x 144 picture.
CDCODE/FILELIST.S is generated and it is the largest module in the
program. 3,444 line-table entries, first byte at hunk offset 0x0b420, and it
runs from there to the end of the 106,232-byte code hunk — 60,120 bytes,
56.6 % of all the code in cosm. It is not code. It is a table of 48-byte
records describing contiguous segments of /cbmbuild:
ULONG offset into cbmbuild
ULONG length of the segment
ULONG a per-segment size
ULONG x 9 the largest chunk sizes in the segment, for buffer sizing
and the offsets chain exactly — 0x1a20ec + 0x212fe0 = 0x3b50cc, the next
record's offset, and so on. The nine trailing longwords in the first record are
33f8 2e40 31dc 36f4 3bb4 3cec 4238 4568 4970 4910, which are the first frame
sizes at the head of cbmbuild itself. A 3,444-line generated assembler source
whose only job is to tell the loader how big to make its buffers.
The two eleven-byte files /filelist.i and /filelist.s in the disc's root are
the ghosts of the generator that produced it (11).
PLINK/ is a directory of one file, READDATA.S, fifteen line entries. The
name is presumably a Psygnosis in-house tool or library; nothing else on the
disc mentions it.
AUDIO/VBLAUDIO.S is the biggest hand-written module, 1,231 line entries,
followed by TASKS/DECRUNCHTASK.S at 995 and DECRUNCH/DECRUNCHER.S at 620.
The music replayer is larger than the video decoder.
MAIN/PT_RBFINTERRUPT.S. PT is ProTracker and RBF is the serial
receive-buffer-full interrupt — the standard Amiga trick of driving a music
replayer from the serial port's interrupt instead of the vertical blank, so the
tempo does not tie itself to the display (09).
The complete table, with first-byte offsets and line-entry counts for all 77,
is in notes/source-modules.txt.