aboutsummaryrefslogtreecommitdiff
path: root/level_maps.txt
blob: b114485a285fc410a467a90f4a682837257e5af0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
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.