04 game code and runtime - HoraceAndTheSpider/New-Zealand-Story-Editor GitHub Wiki

Game Code and Runtime — original 68000 source trace

v0.12 — Carrier-family image trace: original source matched against savestate

Binary evidence (new): actor-type table RAM $058D2 + 12×$26 contains the type $26 sequence pointer RAM $0B006. Its bytes begin 00 5E 00 6C 00 6D 80 48...; decompressing from Disk $8AF90 with the original $95 value countMinusOne RLE reconstructs the first 64 bytes identically to the saved RAM. The native renderer at RAM $0BF80 applies SUBQ.W #1,D1, so script $005E draws bitmap $05D (bird). This is a newly recovered animation source pointer, not arbitrary atlas adjacency.

Important corrections to older P18 interpretation: type $11 animation stream at RAM $08164 / Disk $880DA begins image word $0040, so it draws bitmap $03F (purple actor). Type $21 first script at RAM $0B04E / Disk $8AFD8 begins $0055 (bitmap $054). Its later $005D means bitmap $05C, not bird bitmap $05D. Type $2B script at RAM $0AFFA / Disk $8AF84 starts $005F and displays bitmap $05E, also not the bird. References to $5D without a script word/bitmap index distinction are insufficient to infer transport attachment. The original 289-image directory remains zero-based and unchanged.

Composition not code verified: actor $11 and actor $26 are separate entries in the native type table. We have not traced a parent/child reference linking them, their creation timing, or their relative pixel offsets. The editor's +4px companion-only Y shift is a transparent experimental overlay adjustable in the Entities panel, not a corrected native coordinate or animation state. The native actor creation and generator scheduler still require investigation. The real type $21 default graphic is no longer replaced with a bird simply because its script includes word $005D.

Original actor frames and source conversions, plus explicitly experimental composite

v0.11 — $8048 generator command disassembled (14 October 2026)

The original 20-entry animation-command dispatcher maps control word $8048 to RAM $0B62E / Disk $8B58E. The twelve opcode bytes are identical in the owner's savestate and disk: 42 6D 00 20 42 6D 00 1E 54 6D 00 22. They decode as:

CLR.W  $0020(A5)      ; clear this actor's field +$20
CLR.W  $001E(A5)      ; clear this actor's field +$1E
ADDQ.W #2,$0022(A5)  ; advance the animation stream cursor

Crucial consequence: $8048 is a state/counter reset, not a literal delay opcode. The two original generator image runs straddle this reset; a dwell on bitmap $A1 inserted by the v0.10 editor was unsupported. Native script image $A1 resolves to bitmap $A0 after the $8048 handler. The v0.11 editor holds bitmap $A0 briefly only as an illustrative viewing choice; native duration/activation is not yet simulated. Negative control opcodes, timed scheduling, actor slot occupancy and conditions still need an executing 68000 interpreter trace to establish actual generator chronology.

This trace was verified by testing both Disk.1 and RAM from Stage 1_1.uss; app/nzs-native-frame-resolver.js::validateGeneratorCommand provides a reproducible source guard. See source-derived generator comparison and the current Objects and Entities section.

P19 — Original draw routine proves all native frame references are one-based (14 October 2026)

The 68000 engine's original drawing entry contains these exact bytes in both source files:

Native address Source offset Bytes Meaning
RAM $0BF80 Disk $8BEE0 53 41 SUBQ.W #1,D1; one-based script word → zero-based bitmap index
RAM $0BF82 Disk $8BEE2 4B FA 1C BE LEA using PC-relative displacement
RAM $0BF86 Disk $8BEE6 61 00 00 0C Branch into drawing routine at RAM $0BF94
RAM $0BF94 Disk $8BEF4 41 F9 00 02 2A A6 Base of bitmap payload
RAM $0BF9A Disk $8BEFA 43 F9 00 03 9A A6 Base of 16-byte bitmap descriptors

The script interpreter and graphics directory do not use the same indexing convention. The P12/P13 physical image atlas remains exactly 289 zero-based frames $000..$120, and the P14 native animation streams contain one-based positive image words. The conversion happens at the rendering call, not by modifying the data, relocating images or subtracting 1 from high-bit animation control opcodes $8000..$804C. The $0000 script entry must not wrap to image $0FF; its control/termination semantics require context. Native generator type $03 explicitly demonstrates the difference: source A6 resolves to bitmap A5 (a generator), while bitmap A6 independently depicts an enemy.

This is a verified instruction-and-source match, but does NOT prove per-actor starting pose, facing, sprite screen hotspot, original animation timing or the completion of generator request chains. The $1F and $20 table content follows the same one-based image references whenever supplied to this renderer. Earlier Wiki interpretations that label raw $BD script word as bitmap $BD, raw $CA as bitmap $CA, or raw $5D as bitmap $5D are superseded by this trace.

Source renderer indexing cross-check

P18 — State changes, generator chains and transport source references

Native animation state is not equivalent to orientation at level start. The interpreter's control word $8040 is dispatched by the original 20-entry jump table at RAM $0B430 to RAM $0B610, source Disk $8B570. Bytes 46 2D 00 10 are NOT.B $0010(A5); the following 54 6D 00 22 advances the actor's script index. This toggles an actor state byte (+$10), and many actor routines access that field. Establish the consumer of this byte and its initial value before labelling it left/right or claiming all enemies face right. The $8044 command dispatches at RAM $0B61C, Disk $8B57C, and uses the following pair of words to write an actor field via indexed addressing. Both 68000 source windows byte-match the savestate. A command such as $8044,$0030,$0001 configures an actor field; it is not itself a proof of first visible sprite.

Generator activation and repeat requests: native scanner at RAM $05FD2 (Disk $85F49) uses absolute differences against the descriptor's first two words. Those verified thresholds produce a symmetric rectangle for inspection. Descriptor stream Disk $85C07..$85DB6 expands to 432 bytes at RAM $05C90. Every variant is parsable to source request triplets (type, two factory-consumed bytes) interleaved with $FC/$FD/$FE/$FF; several variants have multiple, sometimes mixed, requests. See the complete source-generated figure in Entities and app/nzs-native-generators.js. The interpreter's control flow/loop conditions are not simulated, so lexical order and $FE delays do not prove actual in-game spawn sequence or facing.

Flying transport native source chain: original 68000 type table RAM $058D2, twelve bytes per entry, includes types $21–$27; the separately source-verified animation data for $21,$22,$23,$25,$27 each contains image $005D. Native type $11's first script contains $0040. These are distinct actor script references, not conclusive parent/child compositing. Native bitmap directory and frame offsets confirm the actual artwork at IDs $03F,$040,$05D. Further investigate pointers/child allocation and actual rendered state before asserting the rider is always attached to $5D or that $25 is an arena trigger.

Ordinary warp hyperlink proof: the existing P09 source decoder remains unchanged: table RAM $04B02 / Disk $84B14..$84B7F, 20 rows, 12 configured. RAM $0423A installs a source/target into live warp state; RAM $04C36..$04C42 copies destination stage and coordinates into restart state. P18 only supplies navigation for these verified links. It does not infer boss-area closures, extra invisible portals, rescued-kiwi transitions or live camera positions from empty rows.

Map dimensions: each native 128-byte map header's big-endian +$02/+$04 fields are width/height, while the following grid always occupies 4096 stored bytes. For 1-1 (Disk $18C00), bytes 30 31 00 40 00 0E declare width 64 and height 14. The header declarations do not alone prove how many tile rows the game actually renders on the display. P18 retains these native values, including 4-4b's height 48, and flags the owner's 17-row observation for a camera trace rather than overwriting source.

Boss and bullet trace boundaries: type $02 appears at all five _4 boss stages, while type $25 has zero direct source placements. This excludes only direct placement, not dynamic creation, script-generated actors or an unrelated $25 marker. The type $06 source assigns a secondary $8C–$8F animation; it does not authorise arbitrary projectile placement records. Source editing remains locked pending allocator/capacity and full code-controlled actor instantiation proof.

Generator descriptor evidence

Flying transport source image comparison


P17 — Source animation words versus owner-verified display grouping

No original animation bytes have been changed. Decoding Disk.1 with the verified interpreter gives type $00 first script image $001, type $01/$02 first script image $DA, type $03 first image $A6, type $05 first $79 and type $20 first $CA. These are verifiable script references. They are not necessarily the artist's first sprite for a complete character or the static appearance of the placed entity. The owner has confirmed that $D9 depicts the caged kiwi, $DA starts the rescue/end animation, $C9 is an overlooked bonus-family image, $A6 should not be used as the first image of the reviewed $03 visual group (start at $A7), and type $05 includes movement, firing and death. Source-script groups remain separate from reviewed image groups so future analysis can cross-check both without falsifying the original disk.

Original masked bitmap records $028 and $029 are a visually coherent two-frame warp animation, with native dimensions 48×37 for both and directory hotspots (-16,-18). The editor alternates these at original ordinary outgoing warp source locations, using the independently proven RLE table at Disk $84B14..$84B7F. This establishes source artwork and table coordinates but not the actual game runtime warp animation period or exact positioning relative to its trigger.

Native proof relevant to this pass is reproducible in tests/test-p17-display-frames.js: the raw $03 script still begins $A6, type $01 still first references $DA, and type $20 still first references $CA even though the review views start at $A7, $D9, $C9 respectively. See Game Objects and Entities for the revised grouping diagram.

Native tile classification source and exact four-byte expansion

The entire original tile-classification stream is now recovered directly from Disk.1 at source $8CA5E..$8CFE3 inclusive, using $95 <value> <count-minus-one> escape RLE. It expands to exactly $06F2 = 1,778 bytes in native memory RAM $0CBA0..$0D291, with a complete 1,778/1,778-byte identity match against the supplied Stage 1_1.uss (RAM begins at USS file offset $098C). The next disk byte $8CFE4 begins the first entity-list preface; no guessed padding or repack is permitted. Source $8CA5E immediately follows code ending 4E75 at $8CA5C. The original stage catalogue at RAM $05E40 contains 20 × 16-byte stage records; the first longword points to the relevant native tile-class table. The special 4-4b table at RAM $0CFC2 must be inserted as a separate 21st editor stage rather than indexed through the ordinary 20-stage catalogue.

The original property-grid expander at RAM $0C83C / Disk $8C709 takes the current tile ID from the level map and compares it with the word count determined by the native $AAAA terminator. If the tile ID is outside the classification table, it uses entry 0 (fallback), not missing data or the tile sprite ID. It then rotates the 16-bit property word by four bits four times, writes four zero-extended nibble values as four bytes at offsets 0, 1, $80, $81 in a destination grid of stride $80. In logical spatial order:

Native 16-bit tile word $ABCD      Destination 2×2 property bytes
    nibble A | nibble B              $0A  |  $0B
    nibble C | nibble D              $0C  |  $0D

These bytes are numeric property codes (0..15), not four 8-bit mask bitfields. Do not automatically name $0 empty, $1 solid, $F spike etc. Their actual collision, destructibility, pass-through, damage and any special effects depend on downstream game-code consumers which remain to be traced. The editor's TILE MASK inspector shows both the source big-endian 16-bit word and four expanded raw bytes, with a fallback indication for source tile indices outside the active classification table. This is read-only and sourced exclusively from the loaded disk.

Complete stage table offsets in native RAM (index 16 is the extra area):

1-1 CBA4 | 1-2 CBC4 | 1-3 CBE8 | 1-4 CC22
2-1 CC80 | 2-2 CC80 | 2-3 CC80 | 2-4 CC80
3-1 CD22 | 3-2 CD90 | 3-3 CE24 | 3-4 CEBE
4-1 CF30 | 4-2 CF30 | 4-3 CF30 | 4-4 CF98 | 4-4b CFC2
5-1 D038 | 5-2 D0AA | 5-3 D198 | 5-4 D20C

Counts for each table in that exact order: 13, 15, 26, 44, 72, 72, 72, 72, 52, 71, 74, 50, 49, 49, 49, 20, 56, 54, 116, 55, 66. Every terminator was validated during source decoding. Tests: tests/test-p16-original-source.js (compare all source bytes to RAM, 21 tables, 5,376 tile lookups). See original property/graphics comparison.

Source-matched generator descriptor stream and first spawned actor request

The original generator descriptor and script block decompresses from Disk $85C07..$85DB6, $95 RLE, to $01B0 = 432 bytes at RAM $05C90..$05E3F. This source exactly matches the 432 bytes in the savestate; $85DB7 begins the separate 20-stage catalogue. The native table at RAM $05C90 is u16 MAX_OFFSET=$0026 followed by 19 signed relative 16-bit descriptor offsets, relative to each offset word's own address. For each variant v:

entryRAM = $05C92 + 2*v
rel = int16BE(RAM[entryRAM])
descriptorRAM = entryRAM + rel
activationX = uint16BE(descriptorRAM+0)
activationY = uint16BE(descriptorRAM+2)
assert RAM[descriptorRAM+4] == $FC
firstActorRequest = RAM[descriptorRAM+5]

The $FC byte changes control state, and its subsequent nonnegative actor-request byte is handled by the scheduler's existing common actor factory (RAM $05872). Original first request tokens for variants $00..$12 are respectively $0A,$11,$17,$0C,$16,$0D,$04,$06,$14,$0B,$08,$13,$12,$15,$0E,$0F,$10,$08,$17. Variant $12 aliases the same descriptor source as $02 and is unused by the 298 original generators. All 298 original $03 placement variant values resolve successfully. This identifies a native candidate child actor type, but the creator also consumes actor-specific data from the script; a generator can spawn later types and movement/appearance is dynamic. This does not prove a particular child pose exists exactly at the fixed door coordinate. The editor renders candidate original child graphics at the generator anchor with reduced opacity, a dedicated toggle, and clear provisional labels, while permitting per-instance pose review. This is a display projection only; game actor slots or Disk.1 are not changed.

See the original artwork projected at 2-2 source entities (generator candidates labelled Gxx) and direct actor previews on 1-1. The thumbnails themselves are decoded from the original image source $A099A, using original native descriptor hotspots at $B42CF.

Source-matched original animation command interpreter (P14, 14 October 2026)

The native interpreter starts at RAM $0B3F0 / Disk $8B358. After fetching a big-endian sequence word from the actor's script pointer (+$14(A5)) using its byte offset (+$22(A5)), it clears the high-bit flag in register D1 and dispatches 20 distinct command opcodes by jumping through the 20 × 4-byte BRA.W table RAM $0B430..$0B47F / original Disk $8B398..$8B3E7. All 80 original source bytes of that jump table and the interpreter's first 120 bytes match the supplied Stage 1_1.uss RAM. A normal positive image ID is stored in actor field +$12(A5) at RAM $0B480. A command is not an image even if a later parameter happens to be below image count 289.

For read-only sequential decoding, the command operand lengths, counted in additional 16-bit words after the opcode, are:

Extra words Commands Runtime detail / limitation
0 $8000,$8024,$803C,$8040,$8048 $8000 resets the sequence byte cursor to zero; others update existing actor state.
1 $8004,$8008,$800C,$8010,$8014,$8018,$8020,$8028,$802C,$8030,$8034,$8038 Consume another word for coordinates, delay, condition or other control; $802C–$8038 may select a path dynamically.
2 $801C,$8044,$804C Consume two more words. $801C replaces a script pointer; $804C invokes a native callback; do not follow either blindly.

Source/RAM verification: 39 of the 46 native actor-type animation pointers can be mapped to original Disk.1 source streams. For each, the first 64 decompressed bytes match exactly the corresponding loaded sequence in the supplied Stage 1_1.uss, with the pointer read directly from the 46 × 12-byte RAM actor descriptor table at $058D2 (sequence pointer at row +4). The seven unresolved source pointers are types $08,$1B,$1C,$1D,$1E,$24,$26; they may lie at starts obscured by RLE boundaries or require other loader handling, and P14 does not classify them as source-verified animation groups. The interpreter's first 120 bytes and all 80 bytes of the 20-command dispatch table also match the original disk byte-for-byte. Reproduction: tests/verify_native_animation_trace_p14.py, which reads both originals and checks the address mapping from the shipped JS source. This verifies source bytes and command dispatch but does not prove the first linear segment covers all runtime branches.

The browser decoder in app/nzs-animation-catalogue.js expands the native $95 value count-minus-one RLE at source-matched animation addresses, distinguishes frame IDs from commands/operands, and stops at the first reset, branch or ambiguous word. This is deliberately the first linear segment, not a complete engine simulator. Source addresses are reference metadata, not bundled scene content, bitmap pixels or cached animation IDs.

Source actor sequence Disk source address Native image IDs in first linear segment (unique, original order) Segment termination
Direct $04 $8804E $06F,$070,$071,$072,$073 $8000 reset
Direct $05 $88D50 $079,$07A,$07B,$07C,$07D $8000 reset
Direct $06 $882A2 $082,$083,$084 $8000 reset
Direct $0E $88236 $038,$039,$03A $8000 reset
Collectibles $1F $874CE First 17 words: $B0,$B1,$B2,$B3,$B4,$B5,$B6,$B7,$BD,$B9,$BA,$BF,$BC,$B8,$BB,$C0,$BE; later script entries and other frames exist First reset follows two additional image-like words; source data need branch-aware treatment
Letter bonuses $20 $87518 $CA,$CB,$CC,$CD,$CE,$CF $8000 reset

P15 refinement: IDs $07E–$081 do not occur in the first $05 script segment; however, the source-matched full bounded stream does reference them. This is not evidence that all those images belong to the same visual creature. $81 is the owner-identified leading frame for type $06 (see P15 full stream trace below). The native $1F item table is also non-sequential: $BD is table slot 8, not $B0 + 13. This is evidence for binary image-table order, not confirmed per-placement item selection.

Original source animation frames recovered from Disk.1 for direct types $04, $05 and $06 Source $1F item-slot order from the original disk, including $BD at slot 8

Actor $04/$05/$06 full native source envelopes and image $8D — P15

The owner recognised three leading frames not found in the first straight-line segment of their actor scripts: type $04 begins with image $6E, type $05 with $78, and type $06 with $81. These leading bitmaps exist in the original source sprite library. The identification of them as initial/standing poses is based on the owner's visual observation, not yet on a native code write that selects these three particular frame IDs. The earlier P14 first-segment results remain byte-accurate, but the editor must not omit the owner-identified first pose from its creature galleries.

Native memory provides hard boundaries between these three scripts and adjacent code or a separate table. Unlike walking only the first segment, the P15 decodeBoundedType() reader decodes all raw image references from these finite source bytes, using the same verified 20-command operand lengths; it does not execute conditional branches, perform sprite state simulation or equate all references to one species:

Type Script RAM address Matching Disk source Source-data end RAM (exclusive) Total script bytes Distinct nonzero image IDs referenced within envelope
$04 $080D8 $8804E $08120 (next script) $48 $6F,$70,$71,$72,$73,$74,$75
$05 $08DD8 $88D50 $08E2C (native code) $54 $79,$7A,$7B,$7C,$7D,$7E,$7F,$80,$81
$06 $0832A $882A2 $08398 (native code) $6E $82,$83,$84,$85,$86,$87,$88,$89,$8A,$8B,$8C,$8D,$8E,$8F

A bounded script is not a species atlas. In type $05, graphics $7E–$80 are not demonstrated to be the same creature as $78–$7D, and $81 is identified by the owner as the initial image of type $06. Similarly, the $04 graphics $75–$77 are potentially associated with a separate fired weapon (only $75 is found in the source-bounded $04 stream). P15 intentionally separates owner-proposed action/art families from native script references and exposes both for inspection. A literal $0000 that appears after a reset/branch in a bounded region is treated as ambiguous control/data, not automatically shown as image zero/Tiki.

Source-backed answer to image $8D usage

This is not just a nearby sprite-directory index or an unqualified data scan. The native actor $06 behaviour pointer resolves to RAM $087F2, whose code eventually prepares a secondary A4 object context. The following source-matched words explicitly install a callback and an animation pointer in that secondary object:

; Both original physical Disk.1 and owner savestate contain the same bytes:
; RAM $08958 / Disk $888D0: 297C 0000 8986 0018
MOVE.L #$00008986,$0018(A4)    ; callback/routine pointer
; RAM $08960 / Disk $888D8: 297C 0000 838E 0014
MOVE.L #$0000838E,$0014(A4)    ; secondary animation stream pointer

; RAM $0838E / Disk $88306, original source 16-bit animation words:
DC.W $008C,$008D,$008E,$008F,$8000

Image $8D is a 23×10, hotspot (-12,-2) original indexed image (bitmap source Disk $A099A RLE and directory source $B42CF); it is visually a small red/orange curved shape. Its occurrence in the code-backed secondary animation stream means it should not be labelled unused artwork, and it is not in the $1F 17-slot item table or the $20 six bonus-letter IDs. A separate stage object of type $8D is not implied: $8D is an image identifier, whereas stage records use a different one-byte actor type. Original type $06 does occur on 1-1 (four times) and 15 times overall. Whether this path is exercised in stock gameplay, when it activates, what the shape represents exactly and where it appears are still untested. This is a static source-code association, not a recorded live Amiga occurrence.

Original frame/animation artwork, including the three preceding first poses Image $8D native sequence, source opcodes and hotspot

Reproduce: tests/test-native-images-p15.js reads the 289 images and three bounded scripts from Disk.1, tests 15 $06 records and their four 1-1 coordinates, verifies no mutation. tests/verify_p15_native_source.py checks the two code writes plus stream bytes byte-for-byte against both original Disk.1 and Stage 1_1.uss. tests/make_p15_wiki_figures.py regenerates the three Wiki figures directly from Disk.1. No cached sprites are shipped as editor content.

Native source sprite graphics — original Disk.1 decoder (13 October 2026)

P13 supersedes P12's RAM-only graphics limitation. The complete 289-image stock sprite library is now regenerated directly from the physical Disk.1 bytes; the savestate is no longer an application input. Source compressed bitmap stream starts Disk $A099A, decodes to exactly $17000 = 94,208 bytes, and consumes disk bytes through $B42CE inclusive. Source 16-byte-per-image directory begins at Disk $B42CF and decodes at least 290 rows, with a zero terminator after 289 frames. The same RLE grammar applies to both blocks: an ordinary byte is literal; $95 <value> <count-minus-one> expands count+1 copies of value. These blocks are separate from the 16 compressed background-tile banks.

Exact original source layout and runtime transformation

Source image record: u16 BE widthInWords, u16 BE height, 5×widthInWords×2×height bytes. Source body order is row → 16-pixel word → plane 0 mask, plane 1 colour bit 0, plane 2 bit 1, plane 3 bit 2, plane 4 bit 3 (five sequential big-endian words for each 16-pixel word, then next word or row). An image's full width is taken from the 16-byte directory, not inferred as a full multiple of 16. Source directory entry [u32 BE poolOffset, s16 BE dx, s16 BE dy, u16 BE width, u16 BE height, u32 BE zero]. On loading, the game transforms row/word-major source graphics into five contiguous plane-major bitmap bands in RAM; the last four descriptor bytes in RAM become a repeat of signed dx/dy, whereas the original source has all four zero. This explains why the P12 5-plane-major RAM decoder cannot be used directly on the compressed disk record without this transformation.

sourceImage, imageNext = expand_escape95(Disk.1, $A099A, $17000)
assert imageNext == $B42CF
sourceDirectory, ... = expand_escape95(Disk.1, $B42CF, 290*16)
for id in 0..288:
    dir = sourceDirectory[id*16 : id*16+16]
    offset, dx, dy, width, height = BE_fields(dir)
    words = ceil(width/16)
    assert sourceImage[offset:offset+4] == BE16(words) + BE16(height)
    for y in 0..height-1, x in 0..width-1:
        band0bit = bit(sourceImage[offset+4 + y*words*10 + (x//16)*10 + 0*2 + ((x%16)//8)])
        colour_bit_p = bit(sourceImage[offset+4 + y*words*10 + (x//16)*10 + p*2 + ((x%16)//8)])
        alpha = 255 if band0bit else 0
        colour_index = colour_bit_1 + 2*colour_bit_2 + 4*colour_bit_3 + 8*colour_bit_4

The source's final directory row after the 289 records is zero. Frame $00 dimensions 16×22, hotspot (-7,-22), pool offset $0000; frame $BD is 18×16 (no assumption that images are all 16×16); frame $120 is 39×24. Both source and savestate give 289/289 matching descriptor entries for their first 12 bytes. Raw bitmap/source vs saved RAM matches on 288/289 frames after interleave transformation. Frame $024 alone has a runtime-edited mask in the savestate (rows 1–5 have FF FF FF FF FC 00 where the source uses shape-conforming mask bytes); the editor faithfully renders Disk.1, not this runtime-modified state. Do not silently patch the original source mask to match the savestate. Reproducer: tests/verify_disk_sprites_p13.py; pure JS runtime decoder app/nzs-sprite-disk.js; Node regression tests/test-disk-only-p13.js.

Pixels, hotspot coordinates and dimensions are now directly source-derived, not shipped PNGs. Native palette is re-decoded from the opened disk's first native scenery graphics bank. The existing 16 source colours match the earlier Amiga colour registers for the captured state, but dynamic gameplay copper/palette changes remain an independent visual verification task. The original 68000 loader's precise instruction-by-instruction trace and a safe re-encoder remain open; source addressing and read-only decoder are already independently proved.

Correct catalogue coverage and interpretation

The editor now defaults to All 289 original image IDs and groups them by evidence rather than incorrectly implying that a single contiguous ID span defines one enemy species. Actor/factory type $05 explicitly references $79..$81 across several animation branches: $79..$7C are an initial set, while $7D..$81 are listed separately as additional references with identity not asserted. Type $1F and $20 are collectibles in UI, not creature actor categories. The $1F stream at Disk $874CE includes $B0..$C9 before the type $20 family begins at $CA, including less common $BD and previously excluded $BE..$C9. Type $20 references six $CA..$CF tokens; parameter→frame mapping is a viewer hypothesis, not an accepted write/identity rule. Generator $03 does not itself carry an invented single enemy sprite: its child identity requires full script argument interpretation.

Complete source-derived frame catalogue, first hundred Source-derived frames 100–199 Source-derived frames 200–288 Expanded native collectible catalogue, including B–C family $BD Script-related type 05 images, grouped separately without assuming one species

Moving-graphics pipeline — verified original RAM renderer and image catalogue (13 October 2026)

Resolved: the authentic Amiga game-frame data format, indexed image directory, sprite dimensions, signed drawing hotspots, bitmap mask, four colour planes and actor-sequence-to-image families. This supersedes the older P04/P10 graphics-probe warnings for the specific native descriptor-based extraction described here. Now resolved P13: original compressed bitmap/directory source and disk-to-image layout, documented above. The original decoder routine implementation, editable compression and runtime authoring remain open. P12 art was extracted from the owner's original Stage 1_1.uss live RAM, not fabricated; P13 validates and reproduces it independently from Disk.1 and independently cross-checked against a new original disk-only sprite decoder. The relevant original 68000 reader instructions are byte-identical to Disk.1.

Actor animation reader → image identifier

RAM $0B3F0 = Disk.1 $8B358 (32-byte source match). The native interpreter starts with 20 6D 00 14 (MOVEA.L $14(A5),A0), fetches a stream position 30 2D 00 22 (MOVE.W $22(A5),D0), tests indexed stream word 4A 70 00 00, and then reads the frame/control word (including 32 30 00 00 at RAM $0B418). 08 81 00 0F branches for high-bit control words. Ordinary non-control image indices are written via 3B 41 00 12 (MOVE.W D1,$12(A5)) at RAM $0B480 / Disk $8B3E8. The native error text ANIM ERROR is visible in the same saved code region at $0B4E0. Do not confuse this with the separate $8AB32 movement-vector/countdown script reader.

Known actor-family stream entries, independently source-matched with Disk: $04 RAM $080D8 / Disk $8804E refers to images $6F..; $05 $08DD8 / $88D50 refers to $79..; $06 $0832A / $882A2 refers to $82..; direct $1F $07558 / $874CE refers to $B0–$C9; direct $20 $075A6 / $87518 refers to $CA–$CF. Original player/Tiki sequence references include image $00 and associated player images.

Image directory → native BOB bitmap address

Native drawing routine RAM $0BF94 / Disk $8BEF4, first 12 bytes exactly 41 F9 00 02 2A A6 43 F9 00 03 9A A6. These are two absolute LEA instructions: A0 ← $022AA6 (loaded bitmap store), A1 ← $039AA6 (loaded image directory). The renderer multiplies the frame index by $10 to select a 16-byte descriptor. This is the native graphics lookup that was missing through P11.

For frame i (0..288, all 289 valid in the supplied stock savestate), descriptorRAM = $039AA6 + 16*i. In big-endian bytes:

Offset Width Native field
+$00 u32 Offset into loaded bitmap store $022AA6
+$04 s16 Display-offset/hotspot X (dx)
+$06 s16 Display-offset/hotspot Y (dy)
+$08 u16 Width in pixels, w
+$0A u16 Height in pixels, h
+$0C s16 Repeated X drawing offset; matches +$04 across all 289 stock records
+$0E s16 Repeated Y drawing offset; matches +$06 across all 289 stock records

At bitmapRecordRAM = $022AA6 + offset, the 4-byte bitmap header stores u16 widthInWords=ceil(w/16), u16 h, followed by five plane-major contiguous bitmaps. One band occupies widthInWords * 2 * h bytes. Band 0 is the opaque-pixel mask (alpha), bands 1–4 are colour-index bits 0–3. All bands are row-major within the plane, with Amiga big-endian 16-bit words, highest bit first. Non-multiple-of-16 image widths use the source word stride but crop to descriptor w.

q = RAM[$039AA6 + 16 * frame : ... +16]
bitmap = $022AA6 + read_be_u32(q+0)
words = ceil(read_be_u16(q+8)/16)
height = read_be_u16(q+10)
assert read_be_u16(bitmap+0) == words
assert read_be_u16(bitmap+2) == height
planeBytes = words * 2 * height
for each x in [0,width), y in [0,height):
    bit(plane) = (RAM[bitmap+4 + plane*planeBytes + y*words*2 + x//8] >> (7-(x%8))) & 1
    alpha = 255 if bit(0) else 0
    colourIndex = bit(1) + 2*bit(2) + 4*bit(3) + 8*bit(4)

Palette used for verified stock preview is the 16 native bank words 0000 0307 0111 0521 0640 0430 0030 0555 0300 0007 0700 0157 0740 0662 0070 0777; convert three 3-bit channels to 8-bit by component * 34. These sixteen words match independently captured original Amiga colour registers for the stock state (see Wiki 02). Actual live sprite-specific colour changes outside this state, if any, remain to be traced. Applying this indexed palette to the properly masked five-band images yields recognisable yellow Tiki, enemies and items. The old fixed $80 stride, row-interleaved probes yielded wrong striped art: do not revive them.

Exact source-quality checks: stock Disk.1 SHA256 fec7ce66934497b65745aa658599bdc697cf0ea0111d77d211fce3f48e27b416, Stage 1_1.uss SHA256 4656b4af4322ce946cd8cf0decf843ffb5a8cf7e80fbf153df5c0a2fe317cadb; original code windows 32-byte-exact at the three listed RAM/Disk pairs; 289/289 descriptor records sane; all 289 bitmap record widthInWords and height headers equal directory dimensions; original bitmap boundaries are non-overlapping. Frame $00: directory RAM $039AA6, bitmap record RAM $022AA6, colour pixels start $022AAA, hotspot (-7,-22), 16×22. Next bitmap record starts $022B86. These are loaded RAM addresses, not Disk offsets. tests/extract_native_sprites_p12.py reproduces all 289 RGBA PNGs and the app/nzs-sprite-data.js browser index using the exact attached pair; no third-party sprite images or guessed offsets.

Rendered evidence (inline, directly maintained in wiki/images/)

Tiki — native sprite images $00–$17, frame dimensions and bitmap RAM addresses

Enemy / creature art referenced by directly placed actor types $04, $05 and $06

Type $1F real original fruit and other pickup graphics, frames $B0–$BD

Type $20 letter/bonus graphics, six frames $CA–$CF

Remaining loader investigation: precise 68000 code path that transforms the now-identified compressed source to runtime RAM and a size-safe recompression/repointing contract. P13 source-decoded images have independently validated disk addresses, but enabling sprite writes remains unsafe. Confirm renderer hotspot/BOB placement with actual moving gameplay and note that the owner now thinks the existing v0.3 entity placement visually appears correct; do not impose guessed ±16-pixel shifts.

New primary discovery: a 20 × 8 stage-transition table at loaded RAM $04B02, original compressed disk stream $84B14..$84B7F, is processed by distinct setup, activation, and stage-commit routines. Original Disk.1 and the provided Stage 1_1.uss saved 68000 code/data were independently byte-matched. See research/P09_trace_warps.py, P09_verified_findings.json, and P09_verification_run.txt for fully repeatable assertions. The old “warp records not found” notes below this P09 section are historical only.

A — stage setup selects one transition row

RAM $0422E..$0425B, source match begins Disk $84246 (RAM $0422C). Significant Motorola 68000 opcodes:

0422E   30 2E 00 08       MOVE.W   $0008(A6),D0        ; stock stage index × $10
04232   41 FA 08 CE       LEA      $04B02(PC),A0      ; start of 20 × 8 rows
04236   E2 40             ASR.W    #1,D0              ; 16-byte stage index -> 8-byte entry offset
04238   D0 C0             ADDA.W   D0,A0
0423A   43 FA 08 BC       LEA      $04AF8(PC),A1      ; live 10-byte control record
0423E   32 90             MOVE.W   (A0),(A1)          ; copy source x/y bytes
04240   13 7C 00 05 00 02 MOVE.B #5,$0002(A1)       ; arm a native gate/countdown
04246   33 68 00 02 00 04 MOVE.W $0002(A0),$0004(A1) ; target stage
0424C   23 68 00 04 00 06 MOVE.L $0004(A0),$0006(A1) ; two destination coordinates
04252   4A 6E 00 26       TST.W    $0026(A6)        ; special area?
04256   67 04             BEQ.S    $0425C
04258   42 29 00 02       CLR.B    $0002(A1)        ; suppress ordinary gate in special mode

Important decoder interpretation: $08(A6) is a byte offset into the 20 × 16 ordinary stage catalogue, not a plain integer 0..19. Converting it using ASR.W #1 indexes this 8-byte table exactly. E.g. stage 1-4 = stage number 3, $08(A6)=$30, table offset $18, so row RAM $04B1A. This is direct code proof, not an assumption about sequential level order.

B — location check and transition object callbacks

RAM $04A66..$04AEF, Disk source at $84A78 (at least 140 exact source-matched bytes). Routine walks up to 32 pointer entries originating at +$02C8(A6); the precise identity of every pointer is still being traced. For a populated entry, it reads coordinates from the pointed-to structure, shifts both values right four ($04A80 E8 40, $04A86 E8 41) and compares with the first two bytes of the active transition record:

04A78   43 F9 00 00 4A F8 LEA     $00004AF8,A1
04A88   4A 29 00 02       TST.B   $0002(A1)          ; active gate
04A8C   B0 11             CMP.B   (A1),D0            ; first coordinate
04A90   B2 29 00 01       CMP.B   $0001(A1),D1       ; second coordinate

On the matching branch, the native executable installs object callbacks $04BA2 and $04C72 at +$18/+14 of the pointed-to structure ($04ABE..$04ACD), and manipulates its current position/state. This proves the source coordinate is consumed for an actionable event; it is not a decorative icon. The per-pointer object class and event requirements remain under investigation, so a table-defined transition must not automatically be described as an always-available walking-through-a-tile warp.

C — native destination finally changes stage and position

RAM $04C32, byte-matched at Disk $84C10:

04C32  41 FA FE C4         LEA      $04AF8(PC),A0
04C36  3D 68 00 04 00 08   MOVE.W   $0004(A0),$0008(A6)   ; destination stage catalogue offset
04C3C  2D 68 00 06 02 7C   MOVE.L   $0006(A0),$027C(A6)   ; 2 raw destination coordinates
04C42  3D 7C FF FF 02 80   MOVE.W   #$FFFF,$0280(A6)
04C48  61 00 5C 34         BSR.W    $0A87E                 ; intermediate call; role unresolved
04C4C  4E F9 00 00 40 A2   JMP      $000040A2              ; stage reinitialisation

The exact instruction transfer from callback $04C72 to $04C32 uses a native callback/message sequence and still needs a full execution trace. What is directly proven is that these two fields are written as new stage index and position/state during restart.

D — World-group progression dispatcher is a separate mechanism

RAM $0ADFE..$0AE2D, source Disk $8AD88..$8ADB7; compares stage state to $00F0 (world 4, stage 4) and indexes five branch destinations by ASR.W #4 followed by ANDI.W #$FFFC, then JMP (PC,D0.W):

World Branch RAM Result RAM Interpretation
1 $0AE1A $0A760 World-group completion handler, detailed semantics pending
2 $0AE1E $0A520 Separate world-group handler
3 $0AE22 $09F5C Separate world-group handler
4 $0AE26 $045D0 Sets a callback to $045DE, clearing A5 +$20
5 $0AE2A $0A25E Separate world-group handler

This world dispatcher is not the same system as the 20-row table. The world-4 branch and exact $00F0 test are relevant to the user's report that rescuing the kiwi on 4-4 leads to 4-4b instead of a normal boss. P08 proved the special-area flag setter at $04632, its persistence branch $04636 -> $040BE, and special list/classification overrides. The still-missing link is an executed control-flow trace connecting the rescued-kiwi event to $04632 and locating its landing position. Do not assert that $0AE26 alone establishes that whole chain.

E — Original and loaded representation are not interchangeable

The 20-row RAM image is 160 bytes, but the original source stream $84B14..$84B7F is 108 bytes. The first 64 decoded bytes match literally; the rest contains four $95 escape runs ($95,value,countMinusOne), here all zero-valued:

Disk offset Escaped input Output Decoded offset
$84B54 95 00 07 eight zero bytes +$40
$84B5F 95 00 0F sixteen zero bytes +$50
$84B6A 95 00 0F sixteen zero bytes +$68
$84B7D 95 00 17 twenty-four zero bytes +$88

Immediately after the compressed data at original Disk $84B80 is the native executable that becomes RAM $04BA2 and is byte-matched separately. A naive Disk[$84B14 + 8*stage] works only for the first eight records, and overwriting 160 bytes from disk offset $84B14 would destroy code. Never use this addressing shortcut in an editor.

Verification notes and remaining semantics

  • Nine original source windows have been checked, covering all transition stages and the separate special-mode/world-completion paths; table decoding checks all 160 resulting bytes against emulator RAM.
  • The 20 table entries provide 12 nonzero stage transition records, including four known World-1 departure levels. Zero records represent absence only from this system.
  • The exact native scaling of the destination longword, sign/overflow handling, game object type touching the activation area and availability conditions require real WHDLoad replay. Map PNG destinations are labelled approximate. The source coordinate comparison definitely applies a right shift of four.
  • No new edits or extracted sprite identities are validated by this research pass. See Further Investigations and App Editing and Export.

P08 — New original code traces: special-area selection, actor slots and type-index dispatch (13 October 2026)

Evidence: original owner Disk.1, SHA-256 fec7ce66934497b65745aa658599bdc697cf0ea0111d77d211fce3f48e27b416; owner UAE save state, SHA-256 4656b4af4322ce946cd8cf0decf843ffb5a8cf7e80fbf153df5c0a2fe317cadb. RAM comes from Stage 1_1.uss offset $098C, length $80000. The listed exact source windows are independently asserted in research/P08_research_and_visuals.py (run output in P08_verification_run.txt). Addresses do not share one universal RAM-to-disk offset.

A. Source-verified 4-4b switch, proven native alternate path

Function RAM Disk source Native evidence
Reset special-area word $040B8 $840D2 42 6E 00 26 CLR.W $26(A6)
Runtime callback installed $045EC $84606 23 FC 00 00 45 FE 00 06 45 9A writes callback $045FE to $06459A
Load special-area data $04624 $8463E 70 45 before loader sequence; exact resource meaning unresolved
Set alternate-area flag $04632 $8464C 50 EE 00 26: ST.B $26(A6)
Preserve flag and restart $04636 $84650 60 00 FA 86: branch to $040BE, skipping reset
Select 4-4b object list $0421E / $04224 $84238 / $8423E TST.W $26(A6); BEQ +6; LEA $DA3A,A2
Select same list elsewhere $0AE70 / $0AE74 $8ADFA / $8ADFE independent TST.W/BEQ/LEA $DA3A,A2
Select special tile classification $0C7E2 / $0C7E6 $8C6AF / $8C6B3 TST.W/BEQ/LEA $CFC2,A2

The first and later placement selectors use the same $0DA3A pointer, byte-matched to the physically additional 4-4b object list (Disk.1 $8D78C). This is direct proof of the alternate-area entity and classification mode. The original trigger leading to the callback and exact warp arrival coordinates remain unresolved, and no other hidden transitions are asserted. The labelled flow image is visuals_P08/05_special_area_native_code_flow.png.

B. Separate concurrent actor pool reached from native type $03 generator scheduler

The scheduler at RAM $06010 calls allocator RAM $06050, source disk $85FC9 (44 consecutive bytes byte-identical). This allocator performs exactly five trials (MOVEQ #4,D0 plus DBF) starting at RAM $65598, with a stride $3A (58 bytes). TST.L $18(A3) tests slot occupation. If no free entry is found, it returns D0=0; a free entry is cleared over 58 bytes and returns D0=-1. Five is the maximum available from this particular scanner, not proven to be the universal actor cap. P07's separate 32-door slot array is still valid; distinct capacities must not be mixed.

C. Common actor factory resolves a type through 12-byte records

P07 established that direct and generator-created actors converge on RAM $05872 / disk $8583A. P08 establishes the next level of indirection in that same routine:

05882  CC FC 00 0C   MULU.W  #$000C,D6             ; actor type × 12
05886  22 7B 60 4A   MOVEA.L $4A(PC,D6.W),A1       ; first long field, from RAM $58D2
0588A  20 7B 60 4A   MOVEA.L $4A(PC,D6.W),A0       ; second long field, from RAM $58D6
0588E  34 3B 60 4A   MOVE.W  $4A(PC,D6.W),D2       ; first word, from RAM $58DA
05892  3F 3B 60 48   MOVE.W  $48(PC,D6.W),-(A7)    ; second word, from RAM $58DC

Each table row therefore consists of {long A, long B, word A, word B}, exactly 12 bytes, indexed by type number. research/P08_native_actor_dispatch_00_2D.csv records all 46 rows for stock-observed IDs $00..$2D, their RAM addresses and raw fields. visuals_P08/08_actor_dispatch_table_1.png and _2.png provide a mobile-oriented 46-entry table. The two longs are pointer-like native fields, not confirmed graphics addresses, and their later consumers must be traced to name sprites or behavioural callbacks. The valid ceiling above $2D has not been established; do not add new types beyond stock usage.

Critical difference between file bytes and runtime table: RAM row $058D2 begins 00 00 93 1E 00 00 AF 5C 00 00 00 00, whereas the physically corresponding original disk area at $8589A starts 00 02 93 1E 00 00 AF 5C 95 00 05 AE. P08 confirms this mismatch programmatically. This is consistent with the game's loader representation/relocation processing; it is not a proof of the exact source encoder. Never write runtime table entries directly into disk source or assume this area is a flat 12-byte on-disk table.

C2. Two separate native tile classification tables for 4-4 and 4-4b

At normal RAM $0CF98 the original 4-4 tile-classification lookup holds 20 words then native $AAAA sentinel RAM $0CFC0. At alternate pointer $0CFC2, 4-4b's list holds 56 words and sentinel $0D032. The game code at $0C7E6 swaps to that independent second table when special flag is set. Original disk $8CD47 matches the full first 42 bytes, and disk $8CD71 matches the first 54 bytes of the special table before source $95 bytes diverge from RAM, indicating source material requiring decoding/fix-up rather than a directly writable raw table. The four nibbles in each word feed the previously traced property-grid expansion; their physical class meanings are still unknown. Evidence CSV and two PNG comparison sheets are in P08 research/visuals.

D. Warp evidence status and required next trace

The only new travel-related code evidence is the 4-4b alternate mode. It is a genuine area-selection mechanism, but the triggering tile/object, origin transition point and exact arrival are not proved. The native $00 first placement, $01/$02 goal and header +$0E/+10 words remain distinct byte sources. The new later-game overlays are intended for visual cross-checking, not the identification of unspecified warps. To find other warp in/out points: start with original callback installer RAM $045EC and branch/call predecessors to $045FE; trace what modifies the player-position fields during a warp rather than searching graphics pixels or guessing from native record IDs. A runtime capture immediately before/after one user-known warp would be decisive; no desktop interaction is needed to inspect the current source in the meantime.

E. Edit feasibility status

Source entity lists physically abut one another and the final terminator is immediately followed by non-padding $95. Variable-length editing needs relocation support and an on-disk fix-up strategy, including the twenty stage table pointers, the two $DA3A immediate operands in alternate-mode routines, loader/disk sizing and any unlocated consumers. The 32-generator capacity, 5-generated-actor allocator and variant-index limits are distinct runtime restrictions. No game bytes are modified by P08.


P07 — Runtime implementation of $03 doors and generated actors

Native 68000 code is the evidence; the script $03 is now understood beyond its selector table. P06 already located the object-list dispatcher and 19-entry descriptor table. P07 traces through the allocator, 32-door scheduler, actor instantiation and script interpreter. Addresses below are from the owner's saved RAM image, matched to the owner's original Disk.1 at the specific listed windows (not a universal RAM-to-disk offset):

Native role RAM Disk source Original evidence
Direct actor factory / coordinate setup $05872 $8583A Called from normal and door-generated paths
Type $03 table lookup and door descriptor attach $05C32 $85BA9 Fourth byte selects 19 descriptors at $05C90
Door scanner / proximity check $05FD2 $85F49 Nonzero slot +$08, compare x/y distances to +$04/+$06
Actor allocation and door-script pointer copy $06010 $85F87 Set actor type $03, script to actor +$36, actor backpointer in door +$0C
Actor script interpreter $060B4 $8602D Read +(A2), signed-byte dispatch, positive actor tokens
Script countdown callback $06194 $8610B SUBQ.W #1,$30(A5); return to script callback when expired
Door X/Y unpack and 32-slot allocator $0B71C $8B67C shift X/Y to pixels; MOVE.W #$1F,D7; free if slot +$08==0
Door-allocation message $0B742 around $8B6A2 NOT ENOUGH DOORS! (snapshot string)

Code-level proof of the shared actor factory: normal placements execute 61 00 FC 14 at RAM $05C5C; their BSR extension at $05C5E resolves to $05872. Door scripts execute 61 00 F7 A2 at RAM $060CE; its BSR extension at $060D0 also resolves to $05872. The factory reads further bytes from A2. This demonstrates that one set of actor-type dispatch routines handles direct records and scripted door outputs.

Door slot evidence: $0B72A LEA $007C(A6),A5; $0B72E MOVE.W #$001F,D7; $0B732 TST.L $0008(A5); $0B738 LEA $0010(A5),A5; $0B73C DBF D7,$0B732. That is a maximum of 32 fixed 16-byte door slots. At $0B71C two source bytes are turned into a packed X/Y long (X=(Xcell<<4)+4, Y=Ycell<<4). At $0B754..$0B764, an acquired slot is zero-initialised and receives that position. Original source data contains 298 $03 placements overall but no stage exceeds 22 simultaneous native door allocations.

Proximity: RAM $05FF0 computes absolute X difference, compares to door +$04; RAM $05FF8..$0600E does likewise for Y and door +$06. If both comparisons pass and an active child is not already attached, RAM $06010..$06040 allocates a live sprite/actor and copies its script pointer into actor +$36. The two original descriptor header words (e.g. variant 0 0020 00A0) are therefore activation-distance thresholds (32, 160), rather than static sprite data.

Script control: at RAM $060B4 instruction 24 6D 00 36 is MOVEA.L $36(A5),A2; at $060BA 1C 1A reads a byte into D6; $060BC branches on a negative byte. $FF executes aligned pointer-reload/restart logic; $FE consumes a literal next byte into timer $30(A5) and installs callback $06194; $FD/$FC set distinct callbacks and control-state words; $FB disables the active callback; nonnegative tokens use the actor factory $05872. Do not split every apparently positive byte into distinct actors: the actor factory may advance A2 for actor-specific parameters.

Source-vs-memory relocation: the code windows are byte-matched, including 224 consecutive bytes from script interpreter RAM $060B4 ↔ Disk $8602D. Some descriptor tail bytes differ after loading (FF95 0002 on disk → FF00 0000 in the RAM example), so in-memory pointer formatting is not automatically a disk-writing contract. Reproducer: research/P07_verify_generators.py and manifest door_script_variants_P07.csv.

Warp distinction: Native door system here means actor generation, not proven level-teleportation. The first record $00 and level-header coordinate pair remain different source fields. No additional warp entrances or multi-arrival destination table have been established. The new validation maps intentionally use independent overlays.


P06 — Verified 68000 object-list reader and $03 variant dispatcher (13 October 2026)

This section supersedes the P05 statement that the original object-list consumer had not yet been located. The record list was already recovered in P05; P06 traces the native 68000 instructions that select the list, consume its first entry, iterate typed records, and treat $03 as a specialised variant class. The supplied save-state RAM is not assumed to be identical to the disk without byte-window checks. All proven instruction windows below match the supplied Disk.1 at the addresses stated and can be re-tested using research/native_parser_code_evidence.json.

Role RAM (snapshot) Disk.1 Evidence
Stage object pointer from stage+$04 $04216 $84230 41 FA 1C 28 24 70 00 04: LEA $05E40,A0; MOVEA.L 4(A0,D0.W),A2
Consume two-byte stage preface $05B5E $85AD5 30 1A: MOVE.W (A2)+,D0
First entry's leading byte skipped $05B7E $85AF5 52 8A: ADDQ.L #1,A2
Read/convert first entry's X/Y $05872 $8583A twice MOVE.B (A2)+,D1, with shifts/swaps
Typed-record dispatch loop $05C1C $85B93 1C 1A 6B 6C: MOVE.B (A2)+,D6; BMI to end for $FF sentinel
Special case $01/$02 $05C20 $85B97 CMP.B #$01,D6 and CMP.W #$02,D6
Special case $03 $05C32 $85BA9 BC 3C 00 03; 66 24: CMP.B #$03,D6; BNE
$03 X/Y extraction $0B71C $8B67C MOVE.B (A2)+ two times, shifts/swaps, preserving coordinate pair in D1
$03 parameter (4th byte) $05C3C..$05C46 $85BB3..$85BBD MOVEQ #0,D0; MOVE.B (A2)+,D0; ADD.W D0,D0; LEA variant table
Variant table bound and relative branches $05C90 $85C07 first word $0026 = 38 byte maximum; 19 × 16-bit offsets for indices 0–18
Other type X/Y path $05C5C $85BD3 BSR $05872, then reads 4th byte at $05C70

Native parser — what it establishes

  1. List framing is now code-backed. The stage table's second longword leads to an actual native object-list consumer. A separate two-byte preface is consumed, then the first four-byte record's leading byte is skipped and two coordinates are decoded. In every one of the 21 source lists, that first byte is type $00, strongly supporting it being the ordinary stage-entry/initialisation record. The routine does not explicitly compare this first byte to zero in the inspected path, so do not allow deletion or reordering.
  2. The remaining records are explicitly type dispatched by the original game code, and an $FF type byte terminates iteration. The branch for $01/$02 and the separate $03 branch are observed in native opcodes. This markedly strengthens the earlier classification of $01 and $02 as specialised stage objectives, although actual gameplay names still require identifying the called handlers.
  3. Type $03 uses the fourth byte differently. For these records a special native routine consumes X and Y, then the fourth byte is read as a zero-extended index. The code doubles that index and consults a table beginning at RAM $05C90 (Disk $85C07). This is a native 19-slot variant descriptor selector, not a generic uninterpreted flags field. All 298 original $03 records use indices 0–17, every one of those 18 indices appears at least once; index 18 exists as an extra table slot but occurs zero times in the stock placement lists. Slot 18 resolves to the same descriptor RAM $05CD6 as slot 2. Do not assert these are specific creature species until the later descriptor data/rendering is traced.
  4. The value of the two-byte preface is still unclassified. A separate later native routine at RAM $0AE7A (Disk $8AE04) also consumes (A2)+ as a word and uses it in arithmetic. This is strong evidence it is a real field rather than padding; its unit (timer/score/other) is unknown.

Full indexed table — direct RAM evidence

The table first word $0026 is the byte-range check. From RAM $05C92, the 19 signed word offsets map to variant descriptors (the source contains equivalent table words at Disk $85C09):

$03 variant Original placements RAM descriptor Disk table word First two descriptor words
00 55 $05CB6 $85C09 002000A0
01 53 $05CC6 $85C0B 00200030
02 10 $05CD6 $85C0D 00A00014
03 21 $05CE8 $85C0F 00320064
04 17 $05D0C $85C11 00640064
05 12 $05D24 $85C13 00640064
06 4 $05D34 $85C15 00640064
07 9 $05D56 $85C17 00640064
08 9 $05D6C $85C19 00100020
09 33 $05D7E $85C1B 00640064
10 12 $05D94 $85C1D 00500064
11 17 $05DAA $85C1F 00B40032
12 5 $05DC2 $85C21 00500096
13 2 $05DD2 $85C23 00500096
14 15 $05DE4 $85C25 00500096
15 14 $05E0A $85C27 00640096
16 6 $05E1C $85C29 00640096
17 4 $05E2E $85C2B 00500064
18 0 $05CD6 $85C2D 00A00014

Some pointer fields inside the descriptor data are relocation-adjusted between disk and snapshot; the table-entry words themselves have verified direct source addresses. Descriptor payloads contain further native control sequences, not yet an approved decoded sprite atlas. No claim of actual actor artwork, sprite palette or warp linkage follows from this table alone.

Diagnostic deliverables: visuals/12_native_placement_code_trace.png gives the branch flow; visuals/13_type03_variants_and_descriptors.png and 14_type03_variants_by_map.png show variant coverage. research/native_parser_code_evidence.json carries byte-for-byte comparisons, with a companion subtype CSV. No game bytes were changed.


P05 — Original 68000 game-code results (supersedes P04 'unidentified code' stage)

The relevant game routines are now traceable back to their original Disk.1 bytes, using 64–300+ consecutive matching bytes from the supplied emulator's RAM. Previous research had only RAM addresses; P05 identifies corresponding disk locations and verifies the level pointer table and entity-data stream in both files.

(A) Native header dimension reads during stage setup

At RAM $0C7F0 (matching original source Disk $8C6BD for this window; enclosing routine begins at RAM $0C7D4 / Disk $8C6A1):

MOVE.W  $00044AA8,D1     ; bytes 32 39 00 04 4A A8: width = $0040
MOVE.W  $00044AAA,D2     ; bytes 34 39 00 04 4A AA: height = $000E in 1-1

These are exact accesses to the RAM 1-1 header at $44AA6, proving that the original game code reads the header dimensions, not just that their values look geometrically sensible. The same code selects an index into the 20-entry runtime stage catalogue, which begins at RAM $05E40 / Disk $85DB7. Around RAM $0C7DA, 41 FA 96 64 is LEA -$699C(PC),A0; the address calculation lands in the $05E40 pointer table. The following 24 70 00 00 is an indexed MOVEA.L (A0,D0.W),A2. The control field feeding the index and each catalogue word's purpose still require a full trace.

(B) Native tile-map byte → 2×2 nibble field expansion

At RAM $0C83C, identical for at least 130 bytes to Disk $8C709, game code obtains the stage's map address LEA $00044B26,A0, then uses a table of 16-bit words terminated by a $AAAA sentinel to turn map tile IDs into four 4-bit cell values each. This is a strong candidate for a tile collision / property map, not a confirmed spawn list. Directly observed Motorola 68000 instruction sequence (opcodes given so it can be re-disassembled independently):

0C83E  41 F9 00 04 4B 26  LEA       $00044B26,A0    ; byte map
0C844  D0 EE 00 0C        ADDA.W    $000C(A6),A0
0C848  2F 0A              MOVE.L    A2,-(A7)
0C84A  70 00              MOVEQ     #0,D0
0C84C  0C 5A AA AA        CMPI.W    #$AAAA,(A2)+   ; lookup table terminator
0C850  67 04              BEQ       ...
0C852  52 40              ADDQ.W    #1,D0          ; number of word records
0C854  60 F6              BRA       ...
0C856  3C 00              MOVE.W    D0,D6
...
0C864  70 00              MOVEQ     #0,D0
0C866  10 18              MOVE.B    (A0)+,D0      ; next tile ID
0C868  B0 46              CMP.W     D6,D0
0C86C  70 00              MOVEQ     #0,D0         ; fallback for out-of-table tile ID
0C870  D0 00              ADD.B     D0,D0         ; word-index ×2
0C872  30 32 00 00        MOVE.W    0(A2,D0.W),D0
0C876  E9 58              ROL.W     #4,D0
0C878  12 00              MOVE.B    D0,D1
0C87A  02 01 00 0F        ANDI.B    #$0F,D1
0C87E  12 81              MOVE.B    D1,(A1)
0C880  E9 58              ROL.W     #4,D0
0C882  12 00              MOVE.B    D0,D1
0C884  02 01 00 0F        ANDI.B    #$0F,D1
0C888  13 41 00 01        MOVE.B    D1,$0001(A1)
0C88C  E9 58              ROL.W     #4,D0
0C88E  12 00              MOVE.B    D0,D1
0C890  02 01 00 0F        ANDI.B    #$0F,D1
0C894  13 41 00 80        MOVE.B    D1,$0080(A1)
0C898  E9 58              ROL.W     #4,D0
0C89A  12 00              MOVE.B    D0,D1
0C89C  02 01 00 0F        ANDI.B    #$0F,D1
0C8A0  13 41 00 81        MOVE.B    D1,$0081(A1)

Writes to (A1), 1(A1), $80(A1), $81(A1) create a 2×2 field per source tile in a 128-byte destination row stride. The loop setup MOVE.W #$3F,D7 / SWAP D7 / MOVE.W #$3F,D7 and nested DBF loops correspond to the 64×64 source map. P05 has established an original, concrete collision/property-grid conversion pipeline; only the meaning of each nibble requires additional consumer-code tracing. Its table comes from the stage catalogue first pointer (e.g. 1-1 at RAM $0CBA4).

(C) Separate object placement stream, not map glyphs

The second longword of each 20-record stage catalogue entry is a valid RAM pointer to a well-formed object list. At RAM $0D292 (1-1), the first 56 bytes are identical to Disk $8CFE4 and consist of one 16-bit preface, thirteen records and $FF 00 terminator. All twenty stage pointers and the 4-4b extra list are source-matched, together providing 599 list records, as documented in Game Objects and Entities. Actual code that steps the type/X/Y/flag records is the next target; the stage-table/record-layout evidence is strong but the precise record-consumer routine and type handlers have not yet been isolated.

(D) Moving graphics unresolved but now diagnostically bounded

The earlier P04 16×16 / 4 consecutive planes / +$80 stride contact sheets cannot reliably identify sprites. Several RAM motifs repeat with a $FE-byte separation ($50E0A↔$50F08 with 158 identical bytes, $51000↔$510FE with 116 identical bytes), so $80 cannot be assumed as a universal frame size or record step. The wrong colours or apparent scanline streaks reported by the user are evidence that palette/layout/frame boundaries are not established. P05 visual 06_graphics_motif_stride_FE_vs_80.png deliberately compares these layouts without claiming character identity. Follow the graphics creation code and Blitter mask/plane arrangements before offering a sprite atlas in the editor.


Game Code and Runtime

P02 — 12 October 2026. The 68000 loader, render and object code remain to be disassembled. The native tile file-format decoder and embedded palette-word extraction are now byte-accurate, derived from the disk stream itself and cross-checked against the supplied emulator save state (see Maps and Tiles).

Proven behavioural boundaries

  • Disk image header begins DOS\0; this is not evidence of ordinary file loading.
  • Game maps are stored bottom-up, 64×64 raw bytes, with 21 observed offsets. Browser and game-coordinate conversion is explicit in nzs-core.js.
  • There are 16 bank-specific RLE escape streams. Per-stream records and tile pixel format are documented in the Maps and Tiles reference. No 68000 routine addresses are assigned yet: the JavaScript decoder is a reconstruction from reproducible binary evidence, not copied game code.
  • Stage 1_1.uss holds a valid native unpacked 1_1 tile run in its CRAM payload. This is live-state corroboration only; no unverified runtime address mapping has been inferred.

Remaining native routine traces

Find references to compressed bank headers/readers and recover the original 68000 decompress loop, level selection/map pointers, native RGB word expansion and palette writes (Amiga $DFF180–$DFF1BE colour registers where applicable), static tile collision rules and separate moving sprite graphics. Investigate player starts, exits, monster spawns, objects and warps individually, with reproducible code addresses and in-game confirmation. Keep these questions in the single Further Investigations page.

P03 — executable and sprite-graphics reconnaissance

A byte scan of the original disk detects many 68000 return opcodes (4E75) and JSR-style opcodes in the approximate region $07C000..$08C000, unlike the map/tile-bank regions. This is a potential code location only; opcode density is not a disassembly and no runtime control flow has been proven. Source-backed investigation should first establish the level-header consumer and the bank unpacker, then trace entity creation and graphics render calls. The installed WHDLoad package also has assembly source (NewZealandStoryHD.s) available in the official install archive; this may help relate disk data, loader handling and live Amiga addresses, but the slave patches are not a substitute for tracing retail game code.

A raw 16×16 four-bitplane scan of the original disk beyond the background banks, and a similar scan of the user's Stage 1_1.uss chip RAM, shows graphics-like patterns but has not isolated a trustworthy hero or enemy animation frame family. Animated actors may use differing dimensions, interleaving, blitter masks or transformations. No arbitrary contact-sheet cell is yet labelled "hero" or "monster". The pre-map interstitial $580 records likewise remain unclassified. Sprite extraction must be validated by clear recognisable frames and their consuming runtime code before being integrated into production.

P04 — proved game-memory map and evidence for native map consumers

The 1_1 header, tile grid and $580 trailing bytes at Disk.1 $18C00..$1A1FF are a byte-identical 5,632-byte range at Chip RAM $44AA6..$460A5 in the supplied UAE save state. The 1_1 map begins in RAM at $44B26. This is stronger than relying on visual screenshots alone and supplies an in-memory address for the specific captured game state.

Two occurrences in the save-state 68000 machine-code region refer directly to the RAM map address. The following instruction bytes are verified from snapshot memory and interpret as the normal M68K LEA absolute-long,A0 instruction:

; In memory at $0000BE5A (not Disk.1 file offset)
LEA     $00044B26,A0     ; bytes 41 F9 00 04 4B 26
; Nearby code manipulates offsets derived from other registers.

; In memory at $0000C83C
LEA     $00044B26,A0     ; bytes 41 F9 00 04 4B 26
; Nearby code reads indexed map data; precise operation still unclassified.

The existence of these literal map-address operands in executable-looking RAM is verified. Their role as map users is highly likely, but whether they represent collision, rendering, spawning or another table lookup still requires instruction-by-instruction tracing and live behaviour. No guessed on-disk code offset is assigned. The code path responsible for the 1_1 header candidate X/Y fields has not yet been proved.

At RAM $04172 / $04628, LEA $00044AA6,A0 points instead to the header. These occur in code that also uses RAM $3CAA6 (graphics-related) and branches to the apparent disk-read routine. Treat these as loading/setup references; they do not prove either coordinate field's gameplay meaning.

P04 — possible actor graphics layout located in Chip RAM (not yet disk-resolved)

The save-state CRAM block is present at file offset $0980; the first saved RAM byte is at file $098C. A scan of RAM at approximately $51000..$56FFF found reproducibly visible individual figure-/face-like shapes and groups of changed poses when decoded as 16×16, four-bitplane, plane-major images (different from native background tiles' interleaved scanline layout). The trial decoder interprets 128 consecutive bytes as four 32-byte planes, with row-major words within each plane:

RAM frame base B; x,y in 0..15; plane p in 0..3
byte_address = B + (p * 32) + (y * 2) + floor(x / 8)
index(x,y) = Σ_p ( (RAM[byte_address] >> (7 - (x & 7))) & 1 ) << p

The PNG sheets supplied for review start at RAM $51030, $53030 and $55030, stepping forward $80 bytes per rendered trial image. Different boundary phases (+$00, +$30, +$50 relative to $51000) produce different appearances; the correct record start is not yet proven. Some alignments show distinct colour-indexed, face-like drawings rather than the invalid noise of a row-interleaved decoder. This is promising for locating decoded moving graphics but it is not yet a demonstrated sprite resource format: some frames may come from a different graphics resource, stored frame lengths may vary, data may include masks, and the palette currently used is the tile-bank RGB palette, not a verified sprite palette.

Do not claim that $51030 or any other candidate is an on-disk sprite offset, hero identity or enemy ID. A reverse mapping to the Disk.1 source stream and the native code using it is still required. Contact sheets 03_ram_graphics_A_51030.png, 04_ram_graphics_B_53030.png, 05_ram_graphics_C_55030.png and 06_compare_RAM_frame_alignments.png are for user visual identification, not production game-asset exports.

The exact $1600 1_1 disk↔RAM match is separately shown in 07_level_1_1_disk_to_ram.png. Source SHA-256 (Disk.1): fec7ce66934497b65745aa658599bdc697cf0ea0111d77d211fce3f48e27b416; save state: 4656b4af4322ce946cd8cf0decf843ffb5a8cf7e80fbf153df5c0a2fe317cadb.


P10 — Native actor behaviour, pre-placed bonus candidate and animation streams (13 October 2026)

Confirmed independent source checks

The P10 script research/P10_trace_actor_art_and_items.py independently reads the original 901,120-byte Disk.1 and 0x80000 bytes of live RAM from Stage 1_1.uss at file offset $98C. Ten selected windows were matched bit-for-bit against their disk source; the source checks include the actor $1F/$20 handlers, five actor-sequence streams, $03 generator stream interpreter and shared numeric routine. Exact RAM/disk positions, lengths and first bytes are in research/P10_evidence_report.json.

Real native actor table consumer

RAM $05880..$05895 contains the actual factory code:

05880  36 06             MOVE.W D6,D3
05882  CC FC 00 0C       MULU.W #$000C,D6       ; actor ID × 12
05886  22 7B 60 4A       MOVEA.L (PC,D6.W),A1  ; one actor descriptor pointer
0588A  20 7B 60 4A       MOVEA.L (PC,D6.W),A0  ; second descriptor pointer
0588E  34 3B 60 4A       MOVE.W (PC,D6.W),D2  ; descriptor word
05892  3F 3B 60 48       MOVE.W (PC,D6.W),-(A7) ; other descriptor word

The loaded descriptor table begins at RAM $058D2, with 46 documented stock ID rows $00..$2D. Its source representation is not a trivial 12-byte-per-type verbatim disk block because pointers are subject to loader fix-up. A behaviour pointer and a word-stream pointer are consistent with the observed values, but a full native renderer/animation-interpreter trace is still required to name every field. See research/P10_46_actor_dispatch_and_animations.csv for all 46 rows.

Type $20 — specific contact and numeric-update path

Native type $20 points from actor descriptor $05A52 to handler RAM $075D8, matching Disk $8754A for 60 bytes. The beginning of the handler is:

075D8  42 6D 00 20       CLR.W $20(A5)
075DC  61 00 F1 0E       BSR.W $066EC        ; compare actor/player occupancy/proximity
075E0  67 50             BEQ.S $07632        ; skip reward path
075E2  30 3C 03 E8       MOVE.W #$03E8,D0   ; decimal 1000
075E6  61 00 DE 6A       BSR.W $05452        ; common numeric-update path
075EA  42 AD 00 18       CLR.L $18(A5)      ; clear an actor callback/state pointer

The $066EC function examines fields on (A5) and compared active object context, and is also called by other collisions/actor handlers. The $05452 common routine is independently matched to Disk $8541B; additional callers supply immediate values 50, 100, 150, 1000, 20000, 30000 and 50000 or calculated input (see sharedRoutineCallers in JSON). This strongly suggests a scoring/bonus mechanism, not just generic movement. The exact semantics and visual presentation of the resulting amount still need native runtime confirmation; $20 is a high-confidence pre-placed bonus/pickup candidate, not a conclusively named collectible.

Further in the $20 handler the routine reads $22(A5) and performs bit operations against state on (A6); this is a source-backed reason not to assume the fourth placement byte is an unconstrained picture index. In stock placements it takes exactly six values $00–$05. Its relationship to a collection set/bonus chain is an investigation hypothesis only.

Type $1F is distinct

Loaded descriptor $05A46 refers to handler RAM $0767A / source Disk $875EC, and sequence $07558 / Disk $874CE. Its first instruction subtracts one from $38(A5) and it calls the shared proximity helper $066EC, but does not implement the identical $20 immediate-1000 sequence at its entry. Its 29 direct stock records carry seven different parameters (some with bit $40 set). Do not merge types $1F and $20 under a generic pickup category until their branching/state changes and image sequences are decoded.

Disk-to-actor-animation record anchors

Source function RAM Matching Disk offset Native excerpt
Actor $04 sequence $080D8 $8804E $8028,$0002,$8020,$000A,$006F,$0070...
Actor $05 sequence $08DD8 $88D50 $8028,$0002,$8020,$000A,$0079,$007A...
Actor $06 sequence $0832A $882A2 $8044,$0030,$0000,$8020,$0003,$0082...
Actor $1F sequence $07558 $874CE $00B0,$00B1,$00B2,$00B3,$00B4...
Actor $20 sequence $075A6 $87518 $00CA,$00CB,$00CC,$00CD,$00CE,$00CF...
Actor $1F handler $0767A $875EC $536D,$0038,$673C...
Actor $20 handler $075D8 $8754A $426D,$0020,$6100,$F10E...
Common numeric updater $05452 $8541B $0280,$0000,$FFFF...

In all cases the relevant 32–60 source bytes match exactly. Animation/control streams may include embedded command arguments and follow-on code; the first N words are printed as raw memory, not labelled completed animation frames.

Generator type $03 → first actor request (code-backed but partial)

The source-backed type $03 descriptors begin with activation thresholds followed by command $FC. Their first token following $FC is a native actor request. This yields a limited but genuine bridge from placement variant to candidate child actor ID, tabulated below and in research/P10_19_generator_actor_links.csv. It does not prove child sprite identity or cover the full temporal script.

Variant Stock generators Descriptor RAM Proximity X/Y First $FC actor token
$00 55 $05CB6 32 / 160 $0A
$01 53 $05CC6 32 / 48 $11
$02 10 $05CD6 160 / 20 $17
$03 21 $05CE8 50 / 100 $0C
$04 17 $05D0C 100 / 100 $16
$05 12 $05D24 100 / 100 $0D
$06 4 $05D34 100 / 100 $04
$07 9 $05D56 100 / 100 $06
$08 9 $05D6C 16 / 32 $14
$09 33 $05D7E 100 / 100 $0B
$0A 12 $05D94 80 / 100 $08
$0B 17 $05DAA 180 / 50 $13
$0C 5 $05DC2 80 / 150 $12
$0D 2 $05DD2 80 / 150 $15
$0E 15 $05DE4 80 / 150 $0E
$0F 14 $05E0A 100 / 150 $0F
$10 6 $05E1C 100 / 150 $10
$11 4 $05E2E 80 / 100 $08
$12 0 $05CD6 160 / 20 $17

Graphics uncertainty — why not repeat the earlier misleading sheets

Historical P10 state, now superseded by the first section of this page: P10 could not yet map image indices to bitmap addresses, so it tried arbitrary $51030 16×16 memory probes. Those striped/provisional PNGs remain rejected and must never be used as art. P12 subsequently traced the native bitmap image directory $039AA6 and pool $022AA6, recovering 289 authentic frames. Original packed disk source was then unresolved; P13 located $A099A and $B42CF and independently decoded the same 289 frames.

Acceptance required to call a sprite confirmed: trace the code consuming a sequence index, identify per-frame width/height/mask/plane organisation and palette selection, reproduce a recognisable image from source Disk bytes, and compare at least one frame with a live Amiga in-game capture. Do not use a guessed fixed 128-byte stride as an exporter.

Native actor/generator placement coordinate unpack (savestate-confirmed)

Verified against both supplied originals, 13 October 2026: Disk.1 SHA-256 fec7ce66934497b65745aa658599bdc697cf0ea0111d77d211fce3f48e27b416; Stage 1_1.uss SHA-256 4656b4af4322ce946cd8cf0decf843ffb5a8cf7e80fbf153df5c0a2fe317cadb. The save contains exactly $80000 bytes of mapped game RAM beginning at USS file offset $098C. Original object factory and the separate generator-position consumer execute the same 14 bytes:

Purpose Savestate RAM Disk source Matched original bytes
Direct-record / actor-factory coordinates $05872 $8583A 72 00 12 1A E9 41 58 41 48 41 12 1A E9 41
Type $03 generator-position coordinates $0B71C $8B67C 72 00 12 1A E9 41 58 41 48 41 12 1A E9 41
MOVEQ    #0,D1
MOVE.B   (A2)+,D1      ; first source tile coordinate = X
LSL.W    #4,D1         ; X*16
ADDQ.W   #4,D1         ; explicit horizontal +4 pixels
SWAP     D1            ; X in upper word
MOVE.B   (A2)+,D1      ; second source tile coordinate = Y
LSL.W    #4,D1         ; Y*16 in lower word

Thus the packed native pair is (Xtile×16+4, Ytile×16), without the previous browser marker's assumed tile-centre +8,+8. In the editor's fixed 1,024×1,024 canvas, a stage-local top-down Y must also be placed after the inactive 64-row map prefix: canvasY=(64−declaredHeight)×16 + Ytile×16. This projection follows the record and map storage and does not determine the visible drawn top-left or feet of a variable-size BOB. The renderer/BOB hotspot, masks and exact alignment remain unknown; the user's suspected whole-tile X and/or Y visual mismatch cannot yet be resolved by moving disk coordinates.

P11.1 leaves both dimensions in independent, read-only ±16px display comparisons. White + shows the code-proven native anchor; cyan shows the user-adjustable marker projection. One visible overlay might align art better, but that is not code proof of the sprite origin. Ordinary transition source and incoming positions use different native coordinate consumers; these X/Y options intentionally apply only to placement records, not to warps.

Original code-derived placement and nine display X/Y options, 1-2

Original code-derived placement and nine display X/Y options, 3-4

Original movement/control stream — native decoder at Disk $8AB32 (P11; 13 October 2026)

Source: original 901,120-byte Disk.1, SHA-256 fec7ce66934497b65745aa658599bdc697cf0ea0111d77d211fce3f48e27b416. A check against nearby consumers, rather than only the positive record parser, establishes that this is a movement/control stream, not a proven sprite-frame index decoder.

Disk $8AB04  53 6D 00 30          SUBQ.W   #1,$30(A5)          ; movement countdown
Disk $8AB08  67 28                BEQ.S    $8AB32              ; zero -> read next record
Disk $8AB0A  30 2D 00 32          MOVE.W   $32(A5),D0          ; horizontal increment
Disk $8AB0E  D1 6E 00 42          ADD.W    D0,$42(A6)          ; update native X field
Disk $8AB12  30 2D 00 34          MOVE.W   $34(A5),D0          ; vertical increment
Disk $8AB16  D1 6E 00 44          ADD.W    D0,$44(A6)          ; update native Y field
Disk $8AB1A  4E 75                RTS
Disk $8AB1C  20 50                MOVEA.L  (A0),A0            ; separate pointer path
Disk $8AB1E  2B 48 00 36          MOVE.L   A0,$36(A5)
Disk $8AB22  2B 7C 00 00 AC B0 00 18   MOVE.L #$ACB0,$18(A5)
Disk $8AB2A  3B 7C 00 02 00 22   MOVE.W   #$0002,$22(A5)
Disk $8AB30  4E 75                RTS
Disk $8AB32  20 6D 00 36          MOVEA.L  $36(A5),A0       ; stream pointer
Disk $8AB36  30 18                MOVE.W   (A0)+,D0          ; next 16-bit value
Disk $8AB38  6B E2                BMI.S    $8AB1C          ; negative -> alternate path
Disk $8AB3A  3B 40 00 30          MOVE.W   D0,$30(A5)        ; duration / counter
Disk $8AB3E  3B 58 00 32          MOVE.W   (A0)+,$32(A5)     ; signed X increment candidate
Disk $8AB42  3B 58 00 34          MOVE.W   (A0)+,$34(A5)     ; signed Y increment candidate
Disk $8AB46  2B 48 00 36          MOVE.L   A0,$36(A5)        ; next stream position
Disk $8AB4A  3B 7C 00 C8 00 2E   MOVE.W   #$00C8,$2E(A5)
Disk $8AB50  4E 75                RTS

The proven data contract for this routine is [counter, X increment, Y increment] for non-negative first words, with a separate high-bit/negative path. The exact timing/scaling of the two increments is not yet fully explained, but their consumer adds the stored words to X/Y context positions; consequently $30(A5) here cannot be a graphical frame ID. This discovery invalidates a prior provisional P11 'frame identifier at +$30' interpretation. It also means that earlier P10 notation calling $B0.. and $CA.. sprite frames was not independently proved; those were raw positive control words in the sequences. Directly associating them with graphics would be unjustified without a separate renderer consumer. The distinct native actor-sequence anchors $04 Disk $8804E, $05 $88D50, $06 $882A2, $1F $874CE, $20 $87518 remain byte-verified; an execution link between those actor sequences and this movement routine is still to be proven, rather than assumed from common-looking words.

P11 historical conclusion — superseded by P12 above. The original image index/bitmap directory, dimensions, mask, and four colour planes are now verified and 289 loaded native stock graphics have been extracted, including recognisable Tiki, creatures and items. Historically open at P11, subsequently narrowed by P13: disk source is now identified; exact 68000 loader implementation, dynamic palette and per-instance poses/hotspots remain open. Do not mistake this obsolete P11 observation for the current graphics status.

Native countdown, signed position increments and separate high-bit path: code-backed, not sprite art

Canonical illustrated evidence (P11 handover curation)

The graphics references below concern code and original records, not verified moving bitmap frames.

native generator code flow native 4-4b code flow actor dispatch descriptors first half actor dispatch descriptors second half native transition code P10 actor animation stream references first half P10 actor animation stream references second half P10 still-missing art source link diagram P10 type $20 native behaviour

⚠️ **GitHub.com Fallback** ⚠️