rev |
line source |
pascal@180
|
1 bits 16 ; tell NASM this is 16 bit code
|
pascal@180
|
2 cpu 8086
|
pascal@180
|
3
|
pascal@180
|
4 %define CURRENT_LEVEL 0x7E00
|
pascal@180
|
5 %define CURRENT_LEVEL_4 0x7E04
|
pascal@180
|
6 %define SCREEN_DS 0xb800
|
pascal@180
|
7
|
pascal@182
|
8 %define MOVE_COUNT
|
pascal@182
|
9
|
pascal@180
|
10 boot:
|
pascal@180
|
11 ; clear screen (re-set text mode)
|
pascal@182
|
12 mov ax, 0x0001 ; text mode 40x25 16 colours
|
pascal@182
|
13 %define COLS 40
|
pascal@182
|
14 %define LINES 25
|
pascal@180
|
15 int 0x10
|
pascal@180
|
16
|
pascal@180
|
17 ; disable cursor
|
pascal@180
|
18 mov ah, 0x01
|
pascal@180
|
19 mov ch, 0x3f
|
pascal@180
|
20 int 0x10
|
pascal@180
|
21
|
pascal@180
|
22 ; set up stack
|
pascal@180
|
23 push cs
|
pascal@180
|
24 pop ss
|
pascal@180
|
25 xor sp, sp
|
pascal@180
|
26
|
pascal@180
|
27 push cs
|
pascal@180
|
28 pop ds
|
pascal@180
|
29 push cs
|
pascal@180
|
30 pop es
|
pascal@180
|
31
|
pascal@180
|
32 call get_data
|
pascal@180
|
33
|
pascal@180
|
34 ; data section:
|
pascal@180
|
35
|
pascal@180
|
36 ; 0000 0000 EMPTY
|
pascal@180
|
37 ; 0000 0001 SPOT
|
pascal@180
|
38 ; 0000 0010 BRICK
|
pascal@180
|
39 ; 0000 0011 BRICK ON SPOT
|
pascal@180
|
40 ; 0000 0100 WALL
|
pascal@180
|
41 ; 0000 1000 PLAYER
|
pascal@180
|
42 ; 0000 1001 PLAYER ON SPOT
|
pascal@180
|
43 test_level:
|
pascal@180
|
44
|
pascal@182
|
45 %define p(a,b,c) ((c&7)+((b&7)*6)+((a&7)*6*6))
|
pascal@182
|
46 %if 1
|
pascal@182
|
47 db 15, 14 ;width, height
|
pascal@182
|
48 dw 15*7+13 ;playerxy
|
pascal@182
|
49 %define PLAYER 9
|
pascal@182
|
50 db p(4,4,4), p(4,5,5), p(5,5,5), p(4,4,4), p(4,5,5)
|
pascal@182
|
51 db p(4,0,0), p(4,4,4), p(4,4,4), p(4,1,1), p(4,4,4)
|
pascal@182
|
52 db p(4,0,2), p(0,2,0), p(2,0,0), p(4,1,1), p(1,1,4)
|
pascal@182
|
53 db p(4,0,2), p(0,0,0), p(2,2,0), p(4,3,3), p(3,1,4)
|
pascal@182
|
54 db p(4,0,2), p(0,2,0), p(2,0,0), p(4,1,1), p(3,1,4)
|
pascal@182
|
55 db p(4,0,0), p(2,0,2), p(0,2,0), p(4,3,1), p(3,1,4)
|
pascal@182
|
56 db p(4,4,0), p(2,0,2), p(0,2,0), p(1,3,1), p(3,1,4)
|
pascal@182
|
57 db p(4,0,0), p(2,0,2), p(0,2,0), p(1,3,1), p(3,PLAYER,4)
|
pascal@182
|
58 db p(4,0,0), p(2,0,2), p(0,2,0), p(4,3,1), p(3,1,4)
|
pascal@182
|
59 db p(4,0,2), p(0,2,0), p(2,0,0), p(4,1,1), p(3,1,4)
|
pascal@182
|
60 db p(4,0,2), p(0,0,0), p(2,2,0), p(4,3,3), p(3,1,4)
|
pascal@182
|
61 db p(4,0,2), p(0,2,0), p(2,0,0), p(4,1,1), p(1,1,4)
|
pascal@182
|
62 db p(4,0,0), p(4,4,4), p(4,4,4), p(4,1,1), p(4,4,4)
|
pascal@182
|
63 db p(4,4,4), p(4,5,5), p(5,5,5), p(4,4,4), p(4,5,5)
|
pascal@182
|
64 %else
|
pascal@182
|
65 db 21, 18 ;width, height
|
pascal@182
|
66 dw 21+8 ;playerxy
|
pascal@182
|
67 %define PLAYER 8
|
pascal@182
|
68 db p(5,5,5), p(5,5,5), p(5,4,4), p(4,5,5), p(5,5,5), p(5,5,5), p(5,5,5)
|
pascal@182
|
69 db p(5,5,4), p(4,4,4), p(4,0,8), p(0,4,4), p(4,4,4), p(4,4,5), p(5,5,5)
|
pascal@182
|
70 db p(5,5,4), p(0,0,0), p(4,0,2), p(0,4,0), p(0,0,0), p(0,4,4), p(5,5,5)
|
pascal@182
|
71 db p(5,4,4), p(0,2,0), p(4,4,0), p(0,4,2), p(0,0,0), p(0,0,4), p(4,5,5)
|
pascal@182
|
72 db p(5,4,0), p(2,0,2), p(0,4,0), p(0,4,0), p(2,4,4), p(0,2,0), p(4,5,5)
|
pascal@182
|
73 db p(4,4,0), p(3,1,1), p(0,4,4), p(0,4,2), p(0,4,4), p(2,0,0), p(4,5,5)
|
pascal@182
|
74 db p(4,0,2), p(1,1,3), p(0,0,4), p(0,4,0), p(2,0,0), p(0,0,4), p(4,5,5)
|
pascal@182
|
75 db p(4,0,3), p(2,4,2), p(3,0,4), p(2,0,0), p(0,2,0), p(0,4,4), p(5,5,5)
|
pascal@182
|
76 db p(4,1,1), p(0,4,0), p(1,1,4), p(0,0,4), p(4,4,2), p(4,4,5), p(5,5,5)
|
pascal@182
|
77 db p(4,0,4), p(4,4,4), p(4,0,4), p(0,2,0), p(0,2,0), p(0,4,4), p(5,5,5)
|
pascal@182
|
78 db p(4,0,2), p(0,4,0), p(2,0,0), p(2,0,2), p(0,2,0), p(2,0,4), p(4,5,5)
|
pascal@182
|
79 db p(4,0,1), p(1,4,1), p(1,0,2), p(0,4,0), p(0,0,2), p(0,2,0), p(4,5,5)
|
pascal@182
|
80 db p(4,0,1), p(1,2,1), p(1,0,4), p(0,4,0), p(2,4,4), p(0,0,0), p(4,5,5)
|
pascal@182
|
81 db p(4,0,1), p(1,1,1), p(1,2,4), p(0,4,0), p(0,0,0), p(0,2,0), p(4,5,5)
|
pascal@182
|
82 db p(4,0,1), p(1,1,1), p(1,0,4), p(0,4,2), p(2,2,0), p(2,0,4), p(4,5,5)
|
pascal@182
|
83 db p(4,0,1), p(1,1,1), p(1,2,4), p(0,4,0), p(0,0,0), p(0,4,4), p(5,5,5)
|
pascal@182
|
84 db p(4,0,1), p(1,1,1), p(1,0,4), p(0,4,4), p(4,4,4), p(4,4,5), p(5,5,5)
|
pascal@182
|
85 db p(5,4,4), p(4,4,4), p(4,4,5), p(4,5,5), p(5,5,5), p(5,5,5), p(5,5,5)
|
pascal@182
|
86 %endif
|
pascal@180
|
87
|
pascal@180
|
88
|
pascal@182
|
89 display_chars: db 0, 0x07 ; blank
|
pascal@182
|
90 db 249, 0x07 ; spot
|
pascal@182
|
91 db 0xFE, 0x0A ; brick
|
pascal@182
|
92 db 0xFE, 0x0C ; brick on spot
|
pascal@182
|
93 db 176, 0x71 ; wall
|
pascal@182
|
94 db 0, 0x07 ; out blank
|
pascal@182
|
95 db "6", 0x07 ; (no 6)
|
pascal@182
|
96 db "7", 0x07 ; (no 7)
|
pascal@182
|
97 db 1, 0x0F ; player
|
pascal@182
|
98 db 1, 0x0F ; player on spot
|
pascal@180
|
99
|
pascal@180
|
100 str_you_win: db 'YOU WIN! ',1,1,1,0
|
pascal@180
|
101
|
pascal@180
|
102 get_data:
|
pascal@180
|
103 pop bp
|
pascal@180
|
104
|
pascal@180
|
105 ; set current level to test level by copying
|
pascal@180
|
106 mov si, bp
|
pascal@180
|
107
|
pascal@180
|
108 ; get width and height and multiply by each other
|
pascal@182
|
109 lodsw
|
pascal@182
|
110
|
pascal@182
|
111 mov di, CURRENT_LEVEL ; next address to copy to
|
pascal@182
|
112
|
pascal@182
|
113 ; copy map size and player position ("uncompressed")
|
pascal@182
|
114 stosw
|
pascal@180
|
115 mul ah
|
pascal@180
|
116
|
pascal@180
|
117 ; set multiplied width and height + 4 as counter
|
pascal@180
|
118 mov cx, ax
|
pascal@180
|
119 ;add cx, 4
|
pascal@180
|
120
|
pascal@182
|
121 lodsw
|
pascal@182
|
122 stosw
|
pascal@180
|
123
|
pascal@182
|
124 xchg ax,bx
|
pascal@182
|
125 add bx,di ;save player location
|
pascal@182
|
126 mov dx,36*256+6
|
pascal@180
|
127
|
pascal@180
|
128 .copy_level_loop:
|
pascal@182
|
129 ; load one "compressed" byte and store 3 "uncompressed" bytes
|
pascal@182
|
130 lodsb
|
pascal@182
|
131 mov ah,0
|
pascal@182
|
132 div dh
|
pascal@182
|
133 stosb ; p/6/6
|
pascal@182
|
134 mov al,ah
|
pascal@182
|
135 cbw
|
pascal@182
|
136 div dl
|
pascal@182
|
137 stosw ; p/6%6 p%6
|
pascal@180
|
138
|
pascal@180
|
139 loop .copy_level_loop
|
pascal@182
|
140 mov byte [bx],PLAYER
|
pascal@180
|
141
|
pascal@180
|
142 call draw_current_level
|
pascal@180
|
143
|
pascal@180
|
144 .mainloop:
|
pascal@180
|
145 ; read key
|
pascal@180
|
146 xor ax, ax
|
pascal@180
|
147 int 0x16
|
pascal@180
|
148
|
pascal@180
|
149 mov al, byte [CURRENT_LEVEL] ; (width of current level) to the right = 1 down
|
pascal@180
|
150 dec ah ; esc
|
pascal@180
|
151 jne .not_boot
|
pascal@180
|
152 .exit:
|
pascal@182
|
153 mov al, 0x03 ; text mode 80x25 16 colours
|
pascal@180
|
154 int 0x10
|
pascal@180
|
155 int 0x20
|
pascal@182
|
156 int 0x19
|
pascal@180
|
157
|
pascal@180
|
158 .not_boot:
|
pascal@180
|
159 cmp ah, 0x50-1 ; down arrow
|
pascal@180
|
160 je .try_move_down
|
pascal@180
|
161
|
pascal@180
|
162 cmp ah, 0x48-1 ; up arrow
|
pascal@180
|
163 je .try_move_up
|
pascal@180
|
164
|
pascal@180
|
165 mov al, 1
|
pascal@180
|
166 cmp ah, 0x4d-1 ; right arrow
|
pascal@180
|
167 je .try_move_right
|
pascal@180
|
168
|
pascal@180
|
169 cmp ah, 0x4b-1 ; left arrow
|
pascal@180
|
170 jne .redraw
|
pascal@180
|
171
|
pascal@180
|
172 .try_move_left:
|
pascal@180
|
173 .try_move_up:
|
pascal@180
|
174 neg al
|
pascal@180
|
175 .try_move_right:
|
pascal@180
|
176 .try_move_down:
|
pascal@182
|
177 %ifdef MOVE_COUNT
|
pascal@180
|
178 push ax
|
pascal@180
|
179 push ds
|
pascal@180
|
180 mov bx, SCREEN_DS
|
pascal@180
|
181 mov ds, bx
|
pascal@182
|
182 mov bx, COLS + 2 + (LINES-1)*COLS*2
|
pascal@180
|
183
|
pascal@180
|
184 .chk_score:
|
pascal@180
|
185 dec bx
|
pascal@180
|
186 dec bx
|
pascal@180
|
187 mov al, '0'
|
pascal@180
|
188 xchg [bx], al
|
pascal@180
|
189 or al, 0x30
|
pascal@180
|
190 cmp al, '9'
|
pascal@180
|
191 je .chk_score
|
pascal@180
|
192 inc ax
|
pascal@180
|
193 mov [bx], al
|
pascal@180
|
194 pop ds
|
pascal@180
|
195 pop ax
|
pascal@180
|
196 %endif
|
pascal@180
|
197 call try_move
|
pascal@180
|
198
|
pascal@180
|
199 .redraw:
|
pascal@180
|
200 call draw_current_level
|
pascal@180
|
201
|
pascal@180
|
202 .check_win:
|
pascal@180
|
203
|
pascal@180
|
204 ; get width and height
|
pascal@180
|
205 mov ax, [CURRENT_LEVEL] ; al = width; ah = height
|
pascal@180
|
206 mul ah
|
pascal@180
|
207 mov cx, ax ; cx = size of map
|
pascal@180
|
208
|
pascal@180
|
209 xor bx, bx ; bx = number of bricks-NOT-on-a-spot
|
pascal@180
|
210
|
pascal@180
|
211 mov si, CURRENT_LEVEL_4
|
pascal@180
|
212 .check_win_loop:
|
pascal@180
|
213 lodsb
|
pascal@180
|
214 cmp al, 2
|
pascal@180
|
215 jne .not_a_brick
|
pascal@180
|
216 inc bx
|
pascal@180
|
217 .not_a_brick:
|
pascal@180
|
218 loop .check_win_loop
|
pascal@180
|
219
|
pascal@180
|
220 ; so, did we win? is the number of spotless bricks == 0??
|
pascal@180
|
221 cmp bx, 0
|
pascal@180
|
222 je win
|
pascal@180
|
223 jmp .mainloop
|
pascal@180
|
224
|
pascal@180
|
225
|
pascal@180
|
226 win:
|
pascal@180
|
227 ; print a nice win message to the middle of the screen
|
pascal@180
|
228 lea si, [bp+str_you_win-test_level]
|
pascal@180
|
229
|
pascal@180
|
230 ; destination position on screen
|
pascal@180
|
231 mov ax, SCREEN_DS
|
pascal@180
|
232 mov es, ax
|
pascal@182
|
233 mov di, (COLS * 12 + COLS/2 - 6) * 2
|
pascal@180
|
234
|
pascal@180
|
235 mov ah, 0x0F
|
pascal@180
|
236 .loop:
|
pascal@180
|
237 cs lodsb
|
pascal@180
|
238
|
pascal@180
|
239 cmp al, 0
|
pascal@180
|
240 je wait_for_esc
|
pascal@180
|
241
|
pascal@180
|
242 stosw
|
pascal@180
|
243 jmp .loop
|
pascal@180
|
244
|
pascal@180
|
245 wait_for_esc:
|
pascal@180
|
246 ; read key
|
pascal@180
|
247 xor ax, ax
|
pascal@180
|
248 int 0x16
|
pascal@180
|
249
|
pascal@180
|
250 dec ah ; esc
|
pascal@180
|
251 je get_data.exit
|
pascal@182
|
252 jmp boot
|
pascal@180
|
253 ; halt:
|
pascal@180
|
254 ; cli ; clear interrupt flag
|
pascal@180
|
255 ; hlt ; halt execution
|
pascal@180
|
256
|
pascal@180
|
257
|
pascal@180
|
258 ;; functions:
|
pascal@180
|
259
|
pascal@180
|
260 draw_current_level:
|
pascal@180
|
261 ; get width and height
|
pascal@180
|
262 mov cx, [CURRENT_LEVEL] ; cl = width; ch = height
|
pascal@180
|
263 push cx ; put it in the stack for later reuse
|
pascal@180
|
264
|
pascal@180
|
265 ; print in the middle and not in the corner
|
pascal@182
|
266 mov di, 25*COLS; middle of screen
|
pascal@180
|
267
|
pascal@180
|
268 ; offset by half of width
|
pascal@180
|
269 mov bx, 0x00FE
|
pascal@180
|
270 and bl, cl
|
pascal@180
|
271 sub di, bx
|
pascal@180
|
272
|
pascal@180
|
273 ; offset by half of height
|
pascal@180
|
274 mov cl, ch
|
pascal@180
|
275 and cx, 0x00FE
|
pascal@182
|
276 mov ax, COLS
|
pascal@180
|
277 mul cx
|
pascal@180
|
278 sub di, ax
|
pascal@180
|
279
|
pascal@180
|
280
|
pascal@180
|
281 mov si, CURRENT_LEVEL_4 ; source byte
|
pascal@180
|
282
|
pascal@180
|
283 ; screen memory in text mode
|
pascal@180
|
284 mov ax, SCREEN_DS
|
pascal@180
|
285 mov es, ax
|
pascal@180
|
286
|
pascal@180
|
287 .loop:
|
pascal@180
|
288 push si
|
pascal@180
|
289 lodsb
|
pascal@180
|
290 cbw
|
pascal@180
|
291 xchg ax, si
|
pascal@180
|
292 add si, si
|
pascal@182
|
293 mov ax, [bp+si+display_chars-test_level]
|
pascal@180
|
294 pop si
|
pascal@180
|
295
|
pascal@180
|
296 stosw
|
pascal@180
|
297
|
pascal@180
|
298 inc si
|
pascal@180
|
299 pop cx ; get counters
|
pascal@180
|
300 dec cl ; subtract 1 from X axis counter
|
pascal@180
|
301 jz .nextrow
|
pascal@180
|
302 push cx
|
pascal@180
|
303 jmp .loop
|
pascal@180
|
304
|
pascal@180
|
305 .nextrow:
|
pascal@180
|
306 dec ch ; subtract 1 from Y axis counter
|
pascal@180
|
307 jz .finished
|
pascal@180
|
308 mov cl, [CURRENT_LEVEL]
|
pascal@180
|
309 push cx
|
pascal@180
|
310
|
pascal@180
|
311 ; jump to next row down
|
pascal@180
|
312 xor ch, ch
|
pascal@180
|
313 neg cx
|
pascal@182
|
314 add cx, COLS
|
pascal@180
|
315 add cx, cx
|
pascal@180
|
316 add di, cx
|
pascal@180
|
317
|
pascal@180
|
318 jmp .loop
|
pascal@180
|
319
|
pascal@180
|
320 .finished:
|
pascal@180
|
321 ret
|
pascal@180
|
322
|
pascal@180
|
323 try_move:
|
pascal@180
|
324 ; try to move the player
|
pascal@180
|
325 ; al = offset of how much to move by
|
pascal@180
|
326
|
pascal@180
|
327 ; extend al into ax (signed)
|
pascal@180
|
328 test al, al ; check if negative
|
pascal@180
|
329 js .negative_al
|
pascal@180
|
330 xor ah, ah
|
pascal@180
|
331 jmp .after_al
|
pascal@180
|
332 .negative_al:
|
pascal@180
|
333 mov ah, 0xFF
|
pascal@180
|
334 .after_al:
|
pascal@180
|
335 push ax
|
pascal@180
|
336
|
pascal@180
|
337 mov di,CURRENT_LEVEL_4
|
pascal@180
|
338
|
pascal@180
|
339 ; calculate total level size
|
pascal@180
|
340 mov ax, [CURRENT_LEVEL]
|
pascal@180
|
341 mul ah
|
pascal@180
|
342
|
pascal@180
|
343 ; calculate requested destination position
|
pascal@180
|
344 pop bx
|
pascal@180
|
345 push bx
|
pascal@180
|
346 mov dx, [di - 2]
|
pascal@180
|
347 add bx, dx
|
pascal@180
|
348
|
pascal@180
|
349 ; check if in bounds
|
pascal@180
|
350 cmp bx, 0
|
pascal@180
|
351 jl .finished
|
pascal@180
|
352 cmp bx, ax
|
pascal@180
|
353 jg .finished
|
pascal@180
|
354
|
pascal@180
|
355 ; get value at destination position
|
pascal@180
|
356 mov cl, [bx + di]
|
pascal@180
|
357 cmp cl, 4
|
pascal@180
|
358 je .cant_push ; it's a wall
|
pascal@180
|
359 test cl, 0x02
|
pascal@180
|
360 jz .dont_push ; it's not a brick (on spot, or not), so don't try pushing
|
pascal@180
|
361
|
pascal@180
|
362 ; try pushing brick
|
pascal@180
|
363 pop cx ; get move offset
|
pascal@180
|
364 push bx ; store player's destination position (brick's current position)
|
pascal@180
|
365
|
pascal@180
|
366 mov dx, bx ; dx = current brick position
|
pascal@180
|
367 add bx, cx ; bx = next brick position
|
pascal@180
|
368
|
pascal@180
|
369 ; check bounds
|
pascal@180
|
370 cmp bx, 0
|
pascal@180
|
371 jl .cant_push
|
pascal@180
|
372 cmp bx, ax
|
pascal@180
|
373 jg .cant_push
|
pascal@180
|
374
|
pascal@180
|
375 ; get value at destination position
|
pascal@180
|
376 mov ch, [bx + di]
|
pascal@180
|
377 test ch, 0x0E ; test if the destination is occupied at all by ANDing with 0000 1110
|
pascal@180
|
378 jnz .cant_push
|
pascal@180
|
379
|
pascal@180
|
380 ; all checks passed! push the brick
|
pascal@180
|
381
|
pascal@180
|
382 ; add new brick to screen
|
pascal@180
|
383 or ch, 0x02 ; add brick bit, by ORing with 0000 0010
|
pascal@180
|
384 mov [bx + di], ch
|
pascal@180
|
385
|
pascal@180
|
386 ; remove old brick from screen
|
pascal@180
|
387 add di, dx
|
pascal@180
|
388 mov cl, [di]
|
pascal@180
|
389 and cl, 0xFD ; remove brick bit, by ANDing with 1111 1101
|
pascal@180
|
390 mov [di], cl
|
pascal@180
|
391 sub di, dx
|
pascal@180
|
392
|
pascal@180
|
393 mov dx, [di - 2] ; dx = current player position
|
pascal@180
|
394 pop bx ; bx = next player position
|
pascal@180
|
395 jmp .redraw_player
|
pascal@180
|
396
|
pascal@180
|
397 .cant_push:
|
pascal@180
|
398 pop bx
|
pascal@180
|
399 jmp .finished
|
pascal@180
|
400
|
pascal@180
|
401 .dont_push:
|
pascal@180
|
402 pop cx ; don't need to have this offset in the stack anymore
|
pascal@180
|
403
|
pascal@180
|
404 .redraw_player:
|
pascal@180
|
405 ; remove old player from screen
|
pascal@180
|
406 add di, dx
|
pascal@180
|
407 mov cl, [di]
|
pascal@180
|
408 and cl, 0xF7 ; remove player bit, by ANDing with 1111 0111
|
pascal@180
|
409 mov [di], cl
|
pascal@180
|
410 sub di, dx
|
pascal@180
|
411
|
pascal@180
|
412 ; add new player to screen
|
pascal@180
|
413 mov ch, [bx + di]
|
pascal@180
|
414 or ch, 0x08 ; add player bit, by ORing with 0000 1000
|
pascal@180
|
415 mov [bx + di], ch
|
pascal@180
|
416
|
pascal@180
|
417 ; update player position in memory
|
pascal@180
|
418 mov [di - 2], bx
|
pascal@180
|
419
|
pascal@180
|
420 .finished:
|
pascal@180
|
421 ret
|
pascal@180
|
422
|
pascal@180
|
423
|
pascal@180
|
424 times 510 - ($-$$) db 0 ; pad remaining 510 bytes with zeroes
|
pascal@180
|
425 dw 0xaa55 ; magic bootloader magic - marks this 512 byte sector bootable!
|