rev |
line source |
pascal@180
|
1 ;
|
pascal@180
|
2 ; Pillman
|
pascal@180
|
3 ;
|
pascal@180
|
4 ; by Oscar Toledo G.
|
pascal@180
|
5 ; http://nanochess.org/
|
pascal@180
|
6 ;
|
pascal@180
|
7 ; (c) Copyright 2019 Oscar Toledo G.
|
pascal@180
|
8 ;
|
pascal@180
|
9 ; Creation date: Jun/11/2019.
|
pascal@180
|
10 ; Revision date: Jun/12/2019. Draws level.
|
pascal@180
|
11 ; Revision date: Jun/13/2019. Pillman can move.
|
pascal@180
|
12 ; Revision date: Jun/14/2019. Now ghosts don't get stuck. Ghost are
|
pascal@180
|
13 ; transparent. Pillman doesn't leave
|
pascal@180
|
14 ; trash.
|
pascal@180
|
15 ; Revision date: Jun/15/2019. Ghosts can catch pillman. Optimized.
|
pascal@180
|
16 ; 509 bytes.
|
pascal@180
|
17 ; Revision date: Jul/09/2019. Self-modifying code, move subroutine,
|
pascal@180
|
18 ; cache routine address (Peter Ferrie).
|
pascal@180
|
19 ; 504 bytes.
|
pascal@180
|
20 ; Revision date: Jul/22/2019. Added Esc key to exit.
|
pascal@180
|
21 ;
|
pascal@180
|
22 ; Revision date: Sep/11/2023. Remove screen garbage, setup stack,
|
pascal@180
|
23 ; position independant code, reset screen
|
pascal@180
|
24 ; at exit, slow down Pillman blink.
|
pascal@180
|
25 ;
|
pascal@180
|
26
|
pascal@180
|
27 cpu 8086
|
pascal@180
|
28
|
pascal@180
|
29 base: equ 0xfa00 ; Memory base (same segment as video)
|
pascal@180
|
30 pos1: equ base
|
pascal@180
|
31 intended_dir: equ pos1+20 ; Next direction for player
|
pascal@180
|
32
|
pascal@180
|
33 X_OFFSET: equ 0x0140
|
pascal@180
|
34
|
pascal@180
|
35 ;
|
pascal@180
|
36 ; Maze should start at x,y coordinate multiple of 8
|
pascal@180
|
37 ;
|
pascal@180
|
38 BASE_MAZE: equ 16*X_OFFSET+32
|
pascal@180
|
39
|
pascal@180
|
40 MAZE_COLOR: equ 0x37 ; No color should be higher or equal value
|
pascal@180
|
41 PILL_COLOR: equ 0x02 ; Color for pill
|
pascal@180
|
42 PLAYER_COLOR: equ 0x0e ; Should be unique
|
pascal@180
|
43
|
pascal@180
|
44 ;
|
pascal@180
|
45 ; XOR combination of these plus PILL_COLOR shouldn't
|
pascal@180
|
46 ; result in PLAYER_COLOR
|
pascal@180
|
47 ;
|
pascal@180
|
48 GHOST1_COLOR: equ 0x21 ; Ghost 1 color
|
pascal@180
|
49 GHOST2_COLOR: equ 0x2e ; Ghost 2 color
|
pascal@180
|
50 GHOST3_COLOR: equ 0x28 ; Ghost 3 color
|
pascal@180
|
51 GHOST4_COLOR: equ 0x34 ; Ghost 4 color
|
pascal@180
|
52
|
pascal@180
|
53 old_time:
|
pascal@183
|
54 sti
|
pascal@180
|
55 cld
|
pascal@183
|
56 frame:
|
pascal@180
|
57 push cs
|
pascal@180
|
58 pop ss
|
pascal@180
|
59 restart:
|
pascal@183
|
60 mov sp,0xa000 ; Video segment
|
pascal@183
|
61 mov ds,sp ; Use as source data segment
|
pascal@183
|
62 mov es,sp ; Use as target data segment
|
pascal@180
|
63
|
pascal@180
|
64 mov ax,0x0013 ; Set mode 0x13 (320x200x256 VGA)
|
pascal@180
|
65 int 0x10 ; Call BIOS
|
pascal@180
|
66
|
pascal@180
|
67 call get_tables
|
pascal@180
|
68 tables:
|
pascal@180
|
69 ;
|
pascal@180
|
70 ; Ghost colors
|
pascal@180
|
71 ;
|
pascal@180
|
72 ghost_colors:
|
pascal@180
|
73 db GHOST4_COLOR,GHOST3_COLOR,GHOST2_COLOR,GHOST1_COLOR
|
pascal@180
|
74
|
pascal@180
|
75 ;
|
pascal@180
|
76 ; Maze shape
|
pascal@180
|
77 ;
|
pascal@180
|
78 maze:
|
pascal@180
|
79 dw 0b0000_0000_0000_0000
|
pascal@180
|
80 dw 0b0111_1111_1111_1110
|
pascal@180
|
81 dw 0b0100_0010_0000_0010
|
pascal@180
|
82 dw 0b0100_0010_0000_0010
|
pascal@180
|
83 dw 0b0111_1111_1111_1111
|
pascal@180
|
84 dw 0b0100_0010_0100_0000
|
pascal@180
|
85 dw 0b0111_1110_0111_1110
|
pascal@180
|
86 dw 0b0000_0010_0000_0010
|
pascal@182
|
87 dw 0b0111_1110_0111_1111
|
pascal@182
|
88 dw 0b0100_0011_1100_0000
|
pascal@182
|
89 dw 0b0100_0010_0100_0000
|
pascal@182
|
90 dw 0b0111_1110_0111_1111
|
pascal@180
|
91 dw 0b0000_0010_0100_0000
|
pascal@180
|
92 dw 0b0111_1111_1111_1110
|
pascal@180
|
93 dw 0b0100_0010_0000_0010
|
pascal@180
|
94 dw 0b0111_1011_1111_1111
|
pascal@180
|
95 dw 0b0000_1010_0100_0000
|
pascal@180
|
96 dw 0b0111_1110_0111_1110
|
pascal@180
|
97 dw 0b0100_0000_0000_0010
|
pascal@180
|
98 dw 0b0111_1111_1111_1111
|
pascal@180
|
99 dw 0b0000_0000_0000_0000
|
pascal@180
|
100
|
pascal@180
|
101 ;
|
pascal@180
|
102 ; Starting positions
|
pascal@180
|
103 ;
|
pascal@180
|
104 setup_data:
|
pascal@180
|
105 dw BASE_MAZE+0x78*X_OFFSET+0x78
|
pascal@180
|
106 dw BASE_MAZE+0x30*X_OFFSET+0x70
|
pascal@180
|
107 dw BASE_MAZE+0x40*X_OFFSET+0x78
|
pascal@180
|
108 dw BASE_MAZE+0x20*X_OFFSET+0x80
|
pascal@180
|
109 dw BASE_MAZE+0x30*X_OFFSET+0x88
|
pascal@180
|
110
|
pascal@180
|
111 ;
|
pascal@180
|
112 ; Convert arrow codes to internal directions
|
pascal@180
|
113 ;
|
pascal@180
|
114 dirs:
|
pascal@180
|
115 db 0x01 ; 0x48 = Up arrow
|
pascal@180
|
116 go_restart:
|
pascal@180
|
117 jmp restart ; 0x49 = Page Up ; 0x4a = Keypad -
|
pascal@180
|
118 db 0x04 ; 0x4b = Left arrow
|
pascal@180
|
119 db 0x00 ; 0x4c = Keypad 5
|
pascal@180
|
120 db 0x08 ; 0x4d = Right arrow
|
pascal@180
|
121 x_player:
|
pascal@180
|
122 dw 0x00 ; 0x4e = Keypad + ; 0x4f = End
|
pascal@180
|
123 db 0x02 ; 0x50 = Down arrow
|
pascal@180
|
124
|
pascal@180
|
125 ;
|
pascal@180
|
126 ; Game bitmaps
|
pascal@180
|
127 ;
|
pascal@180
|
128 bitmaps:
|
pascal@180
|
129 db 0x00,0x42,0xe7,0xe7,0xff,0xff,0x7e,0x3c ; dir = 1
|
pascal@180
|
130 db 0x3c,0x7e,0xff,0xff,0xe7,0xe7,0x42,0x00 ; dir = 2
|
pascal@180
|
131 db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff ; Maze
|
pascal@180
|
132 db 0x3c,0x7e,0x3f,0x0f,0x0f,0x3f,0x7e,0x3c ; dir = 4
|
pascal@180
|
133 db 0x3c,0x7e,0xff,0xff,0xff,0xff,0x7e,0x3c ; Closed mouth
|
pascal@180
|
134 db 0x3c,0x7e,0xdb,0xdb,0xff,0xff,0xff,0xa5 ; Ghost
|
pascal@180
|
135 db 0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00 ; Pill
|
pascal@180
|
136 db 0x3c,0x7e,0xfc,0xf0,0xf0,0xfc,0x7e,0x3c ; dir = 8
|
pascal@180
|
137
|
pascal@180
|
138 ;
|
pascal@180
|
139 ; Move ghost
|
pascal@180
|
140 ; bh = color
|
pascal@180
|
141 ;
|
pascal@180
|
142 move_ghost:
|
pascal@180
|
143 lodsw ; Load screen position
|
pascal@180
|
144 xchg ax,di
|
pascal@180
|
145 lodsw ; Load direction
|
pascal@180
|
146 test ah,ah
|
pascal@180
|
147 xchg ax,bx ; Color now in ah
|
pascal@180
|
148 mov al,0x30
|
pascal@180
|
149 push ax
|
pascal@180
|
150 mov byte [si-1],0x02 ; Remove first time setup flag
|
pascal@180
|
151 call move_sprite3
|
pascal@180
|
152 pop ax
|
pascal@180
|
153 ;
|
pascal@180
|
154 ; Draw the sprite/tile
|
pascal@180
|
155 ;
|
pascal@180
|
156 ; ah = sprite color
|
pascal@180
|
157 ; al = sprite (x8)
|
pascal@180
|
158 ; di = Target address
|
pascal@180
|
159 draw_sprite:
|
pascal@180
|
160 push ax
|
pascal@180
|
161 push bx
|
pascal@180
|
162 push cx
|
pascal@180
|
163 push di
|
pascal@180
|
164 ds0: push ax
|
pascal@180
|
165 lea bx,[bp+bitmaps_-8]
|
pascal@180
|
166 cs xlat ; Extract one byte from bitmap
|
pascal@180
|
167 xchg ax,bx
|
pascal@180
|
168 mov cx,8
|
pascal@180
|
169 ds1: mov al,bh
|
pascal@180
|
170 shl bl,1 ; Extract one bit
|
pascal@180
|
171 jc ds2
|
pascal@180
|
172 xor ax,ax ; Background color
|
pascal@180
|
173 ds2: cmp bh,0x10 ; Color < 0x10
|
pascal@180
|
174 jc ds3 ; Yes, jump
|
pascal@180
|
175 cmp byte [di],PLAYER_COLOR ; "Eats" player?
|
pascal@180
|
176 je go_restart ; No, it should not crash after several hundred games
|
pascal@180
|
177 xor al,[di] ; XOR ghost again pixel
|
pascal@180
|
178 ds3: stosb
|
pascal@180
|
179 loop ds1
|
pascal@180
|
180 add di,X_OFFSET-8 ; Go to next video line
|
pascal@180
|
181 pop ax
|
pascal@180
|
182 inc ax ; Next bitmap byte
|
pascal@180
|
183 test al,7 ; Sprite complete?
|
pascal@180
|
184 jne ds0 ; No, jump
|
pascal@180
|
185 pop di
|
pascal@180
|
186 pop cx
|
pascal@180
|
187 pop bx
|
pascal@180
|
188 pop ax
|
pascal@180
|
189 ret
|
pascal@180
|
190
|
pascal@180
|
191 get_tables:
|
pascal@180
|
192 pop bp ; SS:BP = tables
|
pascal@180
|
193
|
pascal@180
|
194 ghost_colors_ equ 0
|
pascal@180
|
195 bitmaps_ equ bitmaps-ghost_colors ; Game bitmaps
|
pascal@180
|
196 maze_ equ maze-ghost_colors ; Maze shape
|
pascal@180
|
197 setup_data_ equ setup_data-ghost_colors ; Starting positions
|
pascal@180
|
198 dirs_ equ dirs-ghost_colors ; Convert arrow codes to internal directions
|
pascal@180
|
199 frame_ equ frame-ghost_colors ; Current video frame
|
pascal@180
|
200 x_player_ equ x_player-ghost_colors ; Saved X-coordinate of player
|
pascal@180
|
201 y_player_ equ y_player_loc-ghost_colors ; Saved Y-coordinate of player
|
pascal@180
|
202 old_time_ equ old_time-ghost_colors ; Old time
|
pascal@180
|
203 ;
|
pascal@180
|
204 ; Draw the maze
|
pascal@180
|
205 ;
|
pascal@180
|
206 lea si,[bp+maze_] ; SI = Address of maze data
|
pascal@180
|
207 mov di,BASE_MAZE ; DI = Address for drawing maze
|
pascal@180
|
208 draw_maze_row:
|
pascal@180
|
209 cs lodsw ; Load one word of data from Code Segment
|
pascal@180
|
210 xchg ax,cx ; Put into AX
|
pascal@180
|
211 mov bx,30*8 ; Offset of mirror position
|
pascal@180
|
212 draw_maze_col:
|
pascal@180
|
213 shl cx,1 ; Extract one tile of maze
|
pascal@180
|
214 mov ax,MAZE_COLOR*0x0100+0x18 ; Carry = 0 = Wall
|
pascal@180
|
215 jnc dm1 ; If bit was zero, jump to dm1
|
pascal@180
|
216 mov ax,PILL_COLOR*0x0100+0x38 ; Carry = 1 = Pill
|
pascal@180
|
217 dm1: call draw_sprite ; Draw tile
|
pascal@180
|
218 add di,bx ; Go to mirror position
|
pascal@180
|
219 sub bx,16 ; Mirror finished?
|
pascal@180
|
220 jc dm2 ; Yes, jump
|
pascal@180
|
221 call draw_sprite ; Draw tile
|
pascal@180
|
222 sub di,bx ; Restore position
|
pascal@180
|
223 sub di,8 ; Advance tile
|
pascal@180
|
224 jmp draw_maze_col ; Repeat until finished
|
pascal@180
|
225
|
pascal@180
|
226 dm2:
|
pascal@180
|
227 add di,X_OFFSET*8-15*8 ; Go to next row
|
pascal@180
|
228 lea ax,[bp+setup_data_]
|
pascal@180
|
229 sub ax,si ; Maze completed?
|
pascal@180
|
230 jne draw_maze_row ; No, jump
|
pascal@180
|
231
|
pascal@180
|
232 ;
|
pascal@180
|
233 ; Setup characters
|
pascal@180
|
234 ;
|
pascal@180
|
235 ; CX is zero at this point
|
pascal@180
|
236 ; DI is equal to pos1 at this point
|
pascal@180
|
237 %if pos1 != BASE_MAZE+21*8*X_OFFSET
|
pascal@180
|
238 mov di,pos1
|
pascal@180
|
239 %endif
|
pascal@180
|
240 mov cl,5 ; 5 elements (player + ghosts)
|
pascal@180
|
241 mov al,8 ; Going to right
|
pascal@180
|
242 dm3:
|
pascal@180
|
243 cs movsw ; Copy position from Code Segment
|
pascal@180
|
244 stosw ; Store desired direction
|
pascal@180
|
245 loop dm3 ; Loop
|
pascal@180
|
246
|
pascal@180
|
247 ;
|
pascal@180
|
248 ; Main game loop
|
pascal@180
|
249 ;
|
pascal@180
|
250 game_loop:
|
pascal@182
|
251 hlt ; Wait for clock interrupt
|
pascal@180
|
252 mov ah,0x00
|
pascal@180
|
253 int 0x1a ; BIOS clock read
|
pascal@180
|
254 cmp dx,[bp+old_time_] ; Wait for time change
|
pascal@180
|
255 je game_loop
|
pascal@180
|
256 mov [bp+old_time_],dx ; Save new time
|
pascal@180
|
257
|
pascal@180
|
258 mov ah,0x01 ; BIOS Key available
|
pascal@180
|
259 int 0x16
|
pascal@180
|
260 cbw ; BIOS Read Key
|
pascal@180
|
261 je no_key
|
pascal@180
|
262 int 0x16
|
pascal@182
|
263 dec ah
|
pascal@180
|
264 mov al,ah
|
pascal@180
|
265 jne no_esc
|
pascal@182
|
266 mov al,0x03 ; Restore text mode
|
pascal@180
|
267 int 0x10
|
pascal@180
|
268 int 0x20
|
pascal@182
|
269 int 0x19
|
pascal@180
|
270 no_esc:
|
pascal@182
|
271 sub al,0x48-1 ; Code for arrow up?
|
pascal@182
|
272 jc no_key ; Out of range, jump.
|
pascal@180
|
273 cmp al,0x09 ; Farther than arrow down?
|
pascal@182
|
274 jnc no_key ; Out of range, jump.
|
pascal@180
|
275 lea bx,[bp+dirs_]
|
pascal@180
|
276 cs xlat ; Translate direction to internal code
|
pascal@180
|
277 mov [intended_dir],al ; Save as desired direction
|
pascal@182
|
278 no_key:
|
pascal@180
|
279 mov si,pos1 ; SI points to data for player
|
pascal@180
|
280 lodsw ; Load screen position
|
pascal@180
|
281 xchg ax,di
|
pascal@180
|
282 lodsw ; Load direction/type
|
pascal@180
|
283 xchg ax,bx
|
pascal@180
|
284 xor ax,ax ; Delete pillman
|
pascal@180
|
285 call move_sprite2 ; Move
|
pascal@180
|
286 mov ax,0x0e28 ; Closed mouth
|
pascal@180
|
287 add byte [bp+frame_],al ; Alternate frame
|
pascal@180
|
288 js close_mouth ; Jump if sign set.
|
pascal@180
|
289 mov al,[pos1+2] ; Using current direction
|
pascal@180
|
290 mov cl,3 ; Multiply by 8
|
pascal@180
|
291 shl al,cl ; Show open mouth
|
pascal@180
|
292 close_mouth:
|
pascal@180
|
293 call draw_sprite ; Draw
|
pascal@180
|
294 ;
|
pascal@180
|
295 ; Move ghosts
|
pascal@180
|
296 ;
|
pascal@180
|
297 mov di,3 ; ghost_colors+3
|
pascal@180
|
298 close_mouth_lp:
|
pascal@180
|
299 mov bh,[bp+di]
|
pascal@180
|
300 push di
|
pascal@180
|
301 call move_ghost
|
pascal@180
|
302 pop di
|
pascal@180
|
303 dec di
|
pascal@180
|
304 jns close_mouth_lp
|
pascal@180
|
305 jmp game_loop
|
pascal@180
|
306
|
pascal@180
|
307 ;
|
pascal@180
|
308 ; DI = address on the screen
|
pascal@180
|
309 ; BL = wanted direction
|
pascal@180
|
310 ;
|
pascal@180
|
311 move_sprite3:
|
pascal@180
|
312 je move_sprite ; If zero, won't remove first time
|
pascal@180
|
313 move_sprite2:
|
pascal@180
|
314 call draw_sprite ; Remove ghost
|
pascal@180
|
315 move_sprite:
|
pascal@180
|
316 cwd ; ah = GHOST[1234]_COLOR
|
pascal@180
|
317 ; xor dx,dx
|
pascal@180
|
318 mov ax,di ; Prepare to extract pixel row/column
|
pascal@180
|
319 mov cx,X_OFFSET
|
pascal@180
|
320 div cx
|
pascal@180
|
321 ; Now AX = Row, DX = Column
|
pascal@180
|
322 mov ah,dl
|
pascal@180
|
323 or ah,al
|
pascal@180
|
324 and ah,7 ; Both aligned at 8 pixels?
|
pascal@180
|
325 jne ms0 ; No, jump because cannot change direction.
|
pascal@180
|
326 ; AH is zero already
|
pascal@180
|
327 ;mov ah,0
|
pascal@180
|
328 ;
|
pascal@180
|
329 ; Get available directions
|
pascal@180
|
330 ;
|
pascal@180
|
331 mov ch,MAZE_COLOR
|
pascal@180
|
332 cmp [di+0x0008],ch ; Right
|
pascal@180
|
333 adc ah,ah ; AH = 0000 000R
|
pascal@180
|
334 cmp [di-0x0001],ch ; Left
|
pascal@180
|
335 adc ah,ah ; AH = 0000 00RL
|
pascal@180
|
336 cmp [di+X_OFFSET*8],ch ; Down
|
pascal@180
|
337 adc ah,ah ; AH = 0000 0RLD
|
pascal@180
|
338 cmp [di-X_OFFSET],ch ; Up
|
pascal@180
|
339 adc ah,ah ; AH = 0000 RLDU
|
pascal@180
|
340
|
pascal@180
|
341 test bh,bh ; Is it pillman?
|
pascal@180
|
342 je ms4 ; Yes, jump
|
pascal@180
|
343
|
pascal@180
|
344 ;
|
pascal@180
|
345 ; Ghost
|
pascal@180
|
346 ;
|
pascal@180
|
347 test bl,0x03 ; Test BL for .... ..DU
|
pascal@180
|
348 je ms6 ; No, jump
|
pascal@180
|
349 ; Current direction is up/down
|
pascal@180
|
350 cmp dx,[bp+x_player_] ; Compare X coordinate with player
|
pascal@180
|
351 mov al,0x88 ; Go right if X ghost < X player
|
pascal@180
|
352 jmp ms10
|
pascal@180
|
353
|
pascal@180
|
354 ; Current direction is left/right
|
pascal@180
|
355 ms6: cmp al,0x00 ; (SMC) Compare Y coordinate with player
|
pascal@180
|
356 y_player_loc equ $-1
|
pascal@180
|
357 mov al,0x22 ; Go down
|
pascal@180
|
358 ms10:
|
pascal@180
|
359 jc ms8 ; Jump if Y ghost < Y player
|
pascal@180
|
360 shr al,1 ; Go up or right
|
pascal@180
|
361 ms8:
|
pascal@180
|
362 test ah,al ; Can it go in intended direction?
|
pascal@180
|
363 jne ms1 ; Yes, go in direction
|
pascal@180
|
364
|
pascal@180
|
365 mov al,bl
|
pascal@180
|
366 ms9: test ah,al ; Can it go in current direction?
|
pascal@180
|
367 jne ms1 ; Yes, jump
|
pascal@180
|
368 ror al,1 ; Try another direction
|
pascal@180
|
369 jmp ms9
|
pascal@180
|
370
|
pascal@180
|
371 ;
|
pascal@180
|
372 ; Pillman
|
pascal@180
|
373 ;
|
pascal@180
|
374 ms4:
|
pascal@180
|
375 mov [bp+x_player_],dx ; Save current X coordinate
|
pascal@180
|
376 mov [bp+y_player_],al ; Save current Y coordinate
|
pascal@180
|
377
|
pascal@180
|
378 mov al,[intended_dir]
|
pascal@180
|
379 test ah,al ; Can it go in intended direction?
|
pascal@180
|
380 jne ms1 ; Yes, go in that direction
|
pascal@180
|
381
|
pascal@180
|
382 ms5: and ah,bl ; Can it go in current direction?
|
pascal@180
|
383 je ms2 ; No, stops
|
pascal@180
|
384
|
pascal@180
|
385 ms0: mov al,bl
|
pascal@180
|
386
|
pascal@180
|
387 ms1: mov [si-2],al ; Save new direction
|
pascal@180
|
388 test al,3 ; If going up/down...
|
pascal@180
|
389 mov bx,-X_OFFSET*2 ; ...bx = vertical movement
|
pascal@180
|
390 jne ms3
|
pascal@180
|
391 mov bx,1*2 ; ...bx = horizontal movement
|
pascal@180
|
392 ms3:
|
pascal@180
|
393 test al,6 ; If going left/down...
|
pascal@180
|
394 je ms7
|
pascal@180
|
395 neg bx ; ...reverse direction
|
pascal@180
|
396 ms7:
|
pascal@180
|
397 add di,bx ; Do move
|
pascal@180
|
398 mov [si-4],di ; Save the new screen position
|
pascal@180
|
399 ms2:
|
pascal@180
|
400 ret
|
pascal@180
|
401
|
pascal@180
|
402 times 510-($-$$) db 0x4f
|
pascal@180
|
403 db 0x55,0xaa ; Make it a bootable sector
|