Level Maps ---------- level00_map: .byte $FE,$33,$9C,$FD,$04,$00,$44,$05 ; A300 FE 33 9C FD 04 00 44 05 .3....D. .byte $06,$04,$15,$0A,$74,$15,$0A,$24 ; A308 06 04 15 0A 74 15 0A 24 ....t..$ .byte $22,$02,$74,$22,$02,$24,$25,$16 ; A310 22 02 74 22 02 24 25 16 ".t".$%. .byte $04,$45,$04,$44,$45,$06,$8C,$45 ; A318 04 45 04 44 45 06 8C 45 .E.DE..E .byte $04,$04,$55,$08,$34,$55,$0E,$7C ; A320 04 04 55 08 34 55 0E 7C ..U.4U.| .byte $55,$08,$FD,$04,$FF,$34,$09,$04 ; A328 55 08 FD 04 FF 34 09 04 U....4.. .byte $5C,$44,$0A,$FD,$04,$01,$5C,$06 ; A330 5C 44 0A FD 04 01 5C 06 \D....\. .byte $04,$1C,$3B,$0A,$FE,$5F,$9C,$FD ; A338 04 1C 3B 0A FE 5F 9C FD ..;.._.. .byte $00,$04,$0C,$41,$05,$8C,$41,$05 ; A340 00 04 0C 41 05 8C 41 05 ...A..A. .byte $24,$01,$05,$74,$01,$05,$4C,$01 ; A348 24 01 05 74 01 05 4C 01 $..t..L. .byte $15,$FE,$C9,$9C,$06,$18,$0A,$99 ; A350 15 FE C9 9C 06 18 0A 99 ........ .byte $18,$0A,$FE,$DA,$9C,$1D,$38,$06 ; A358 18 0A FE DA 9C 1D 38 06 ......8. .byte $81,$38,$06,$FE,$B3,$9C ; A360 81 38 06 FE B3 9C .8.... A300: FE 33 9C selects a shape (in this case, a girder) A303: FD 04 00 sets the X and Y delta (4 pixels X, 0 lines Y) A306: 44 is the X starting position A307: 05 is the Y starting position A308: 06 is the number of copies to draw (length of line) A309: 04 is another X position (same shape type, still drawing girders) A30A: 15 is another Y position (same shape type) A30B: 0A is the number of copies to draw ...more x/y/length tuples here, all drawn with dx=4, dy=0, aka horizontally from left to right. A32A: $FD,$04,$FF sets a new X/Y delta. now we're drawing left and down, for the diagonal girders (aka ramps) that run SW to NE. A333: $FD,$04,$01 is X/Y delta, drawing left and up, for the ramps that run NW to SE (tilted the other way from the previous set). A33C: $FE,$5F,$9C selects a different shape (ladders) so eventually, I need ca65 macros that resemble instructions that assemble into map data. Something like: set_gfx $9Cee set_delta $04,$00 draw_gfx $44,$05,$06 draw_gfx $04,$15,$0A ...notionally the drawing engine is a little CPU with 3 16-bit registers: a program counter (stored at $C0), gfx (the graphic object it draws) and delta (2 8-bit signed ints). It has 5 opcodes: $FC - gfx_jump (operand = 16-bit target address) $FD - gfx_delta (operand = 2 8-bit signed ints) $FE - gfx_shape (operand = 16-bit address of shape, see below) $FF - gfx_end (done drawing) Anything else - gfx_draw (opcode is X start, next 2 bytes are Y and length) All the opcodes other than gfx_end are 3 bytes. Write macros for ca65 and a disassembler in perl. There are 13 primary maps (one per level, plus the WELL DONE screen). Some (all?) levels also have secondary map data, which I think is used for changing parts of the map during the level (e.g. the disappearing girders on level 1). Shapes ------ Actual graphics data (girder sections, ladders, etc) is stored less efficiently. I'm not going to call these "sprites" because they don't move, and I'm not going to call them "tiles" because they aren't (they can be placed at arbitrary X/Y positions, not limited to a character map style grid). So I'll refer them as a "shape". Each shape is at least one pixel wide and at least 1 pixel tall. Pixels are 2 bits wide (4-color GR.7 mode), and are stored unpacked (2 bits per byte). Here's what the girder section looks like: girder: .byte $04,$00,$00,$01,$01,$01,$01,$04 ; 9C33 04 00 00 01 01 01 01 04 ........ .byte $00,$01,$01,$00,$01,$00,$04,$00 ; 9C3B 00 01 01 00 01 00 04 00 ........ .byte $02,$01,$01,$01,$01,$FF,$xx,$xx ; 9C43 02 01 01 01 01 FF xx xx ........ That's from the disassembly, the xx's are "don't care" because they're actually part of the next shape in the table. Here's the same thing, laid out in a more human-readable way [*] girder: girder_row0: girder_row0_width: .byte $04 girder_row0_x_offset: .byte $00 girder_row0_y_offset: .byte $00 girder_row0_pixels: .byte $01,$01,$01,$01 girder_row1: girder_row1_width: .byte $04 girder_row1_x_offset: .byte $00 girder_row1_y_offset: .byte $01 girder_row1_pixels: .byte $01,$00,$01,$00 girder_row2: girder_row2_width: .byte $04 girder_row2_x_offset: .byte $00 girder_row2_y_offset: .byte $02 girder_row2_pixels: .byte $01,$01,$01,$01 girder_end: .byte $FF [*] Getting da65 to emit this would be possible but unwieldy, it'll have to wait until I've mapped out all the 'unknown' code/data sections and can start manually editing the disassembly to turn it into proper source. This makes a shape like this: XXXX X X XXXX ...which you'll recognize as a girder segment, if you squint a little. Notice the use of $FF as an end marker. If it occurs in place of a width, the dm_draw_gfx routine knows it's done, and exits immediately. Here's the next shape in the table: blank: blank_row0: blank_row0_width: .byte $04 blank_row0_x_offset: .byte $00 blank_row0_y_offset: .byte $00 blank_row0_pixels: .byte $00,$00,$00,$00 blank_row1: blank_row1_width: .byte $04 blank_row1_x_offset: .byte $00 blank_row1_y_offset: .byte $01 blank_row1_pixels: .byte $00,$00,$00,$00 blank_row2: blank_row2_width: .byte $04 blank_row2_x_offset: .byte $00 blank_row2_y_offset: .byte $02 blank_row2_pixels: .byte $00,$00,$00,$00 blank_end: .byte $FF This is a 4x4 block of empty pixels. It's probably used to erase parts of the level (e.g. the disappearing platforms on level 1). Next: xxx: xxx_row0: xxx_row0_width: .byte $02 xxx_row0_x_offset: .byte $00 xxx_row0_y_offset: .byte $00 xxx_row0_pixels: .byte $02,$02 xxx_row1_width: .byte $02 xxx_row1_x_offset: .byte $06 xxx_row1_y_offset: .byte $00 xxx_row1_pixels: .byte $02,$02 xxx_row2_width: .byte $02 xxx_row2_x_offset: .byte $00 xxx_row2_y_offset: .byte $01 xxx_row2_pixels: .byte $02,$02 xxx_row3_width: .byte $02 xxx_row3_x_offset: .byte $06 xxx_row3_y_offset: .byte $01 xxx_row3_pixels: .byte $02,$02 xxx_row4_width: .byte $08 xxx_row4_x_offset: .byte $00 xxx_row4_y_offset: .byte $02 xxx_row4_pixels: .byte $02,$02,$02,$02,$02,$02,$02,$02 xxx_row5_width: .byte $02 xxx_row5_x_offset: .byte $00 xxx_row5_y_offset: .byte $03 xxx_row5_pixels: .byte $02,$02 xxx_row6_width: .byte $02 xxx_row6_x_offset: .byte $06 xxx_row6_y_offset: .byte $03 xxx_row6_pixels: .byte $02,$02 xxx_end: .byte $FF In this one, all the pixels are $02. Most of the shapes will be like this, only using one color.