-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathpillman.asm
387 lines (359 loc) · 12.8 KB
/
pillman.asm
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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
;
; Pillman
;
; by Oscar Toledo G.
; http://nanochess.org/
;
; (c) Copyright 2019 Oscar Toledo G.
;
; Creation date: Jun/11/2019.
; Revision date: Jun/12/2019. Draws level.
; Revision date: Jun/13/2019. Pillman can move.
; Revision date: Jun/14/2019. Now ghosts don't get stuck. Ghost are
; transparent. Pillman doesn't leave
; trash.
; Revision date: Jun/15/2019. Ghosts can catch pillman. Optimized.
; 509 bytes.
; Revision date: Jul/09/2019. Self-modifying code, move subroutine,
; cache routine address (Peter Ferrie).
; 504 bytes.
; Revision date: Jul/22/2019. Added Esc key to exit.
;
cpu 8086
%ifndef com_file ; If not defined create a boot sector
com_file: equ 0
%endif
base: equ 0xf9fe ; Memory base (same segment as video)
intended_dir: equ base+0x00 ; Next direction for player
frame: equ base+0x01 ; Current video frame
x_player: equ base+0x02 ; Saved X-coordinate of player
y_player: equ ms6+0x01 ; Saved Y-coordinate of player
old_time: equ base+0x06 ; Old time
;
; Maze should start at x,y coordinate multiple of 8
;
BASE_MAZE: equ 16*X_OFFSET+32
pos1: equ BASE_MAZE+21*8*X_OFFSET
X_OFFSET: equ 0x0140
MAZE_COLOR: equ 0x37 ; No color should be higher or equal value
PILL_COLOR: equ 0x02 ; Color for pill
PLAYER_COLOR: equ 0x0e ; Should be unique
;
; XOR combination of these plus PILL_COLOR shouldn't
; result in PLAYER_COLOR
;
GHOST1_COLOR: equ 0x21 ; Ghost 1 color
GHOST2_COLOR: equ 0x2e ; Ghost 2 color
GHOST3_COLOR: equ 0x28 ; Ghost 3 color
GHOST4_COLOR: equ 0x34 ; Ghost 4 color
%if com_file
org 0x0100 ; Start address for COM file
%else
org 0x7c00 ; Start address for boot sector
%endif
restart:
mov ax,0x0013 ; Set mode 0x13 (320x200x256 VGA)
int 0x10 ; Call BIOS
cld
mov ax,0xa000 ; Video segment
mov ds,ax ; Use as source data segment
mov es,ax ; Use as target data segment
;
; Draw the maze
;
mov si,maze ; SI = Address of maze data
mov di,BASE_MAZE ; DI = Address for drawing maze
draw_maze_row:
cs lodsw ; Load one word of data from Code Segment
xchg ax,cx ; Put into AX
mov bx,30*8 ; Offset of mirror position
draw_maze_col:
shl cx,1 ; Extract one tile of maze
mov ax,MAZE_COLOR*0x0100+0x18 ; Carry = 0 = Wall
jnc dm1 ; If bit was zero, jump to dm1
mov ax,PILL_COLOR*0x0100+0x38 ; Carry = 1 = Pill
dm1: call draw_sprite ; Draw tile
add di,bx ; Go to mirror position
sub bx,16 ; Mirror finished?
jc dm2 ; Yes, jump
call draw_sprite ; Draw tile
sub di,bx ; Restore position
sub di,8 ; Advance tile
jmp draw_maze_col ; Repeat until finished
;
; Move ghost
; bh = color
;
move_ghost:
lodsw ; Load screen position
xchg ax,di
lodsw ; Load direction
test ah,ah
xchg ax,bx ; Color now in ah
mov al,0x30
push ax
mov byte [si-1],0x02 ; Remove first time setup flag
call move_sprite3
pop ax
;
; Draw the sprite/tile
;
; ah = sprite color
; al = sprite (x8)
; di = Target address
draw_sprite:
push ax
push bx
push cx
push di
ds0: push ax
mov bx,bitmaps-8
cs xlat ; Extract one byte from bitmap
xchg ax,bx
mov cx,8
ds1: mov al,bh
shl bl,1 ; Extract one bit
jc ds2
xor ax,ax ; Background color
ds2:
cmp bh,0x10 ; Color < 0x10
jc ds4 ; Yes, jump
cmp byte [di],PLAYER_COLOR ; "Eats" player?
je restart ; Yes, it should crash after several hundred games
ds3:
xor al,[di] ; XOR ghost again pixel
ds4:
stosb
loop ds1
add di,X_OFFSET-8 ; Go to next video line
pop ax
inc ax ; Next bitmap byte
test al,7 ; Sprite complete?
jne ds0 ; No, jump
pop di
pop cx
pop bx
pop ax
ret
dm2:
add di,X_OFFSET*8-15*8 ; Go to next row
cmp si,setup_data ; Maze completed?
jne draw_maze_row ; No, jump
;
; Setup characters
;
; CX is zero at this point
; DI is equal to pos1 at this point
;mov di,pos1
mov cl,5 ; 5 elements (player + ghosts)
mov ax,2 ; Going to right
dm3:
cs movsw ; Copy position from Code Segment
stosw ; Store desired direction
loop dm3 ; Loop
;
; Main game loop
;
game_loop:
mov ah,0x00
int 0x1a ; BIOS clock read
cmp dx,[old_time] ; Wait for time change
je game_loop
mov [old_time],dx ; Save new time
mov ah,0x01 ; BIOS Key available
int 0x16
mov ah,0x00 ; BIOS Read Key
je no_key
int 0x16
no_key:
mov al,ah
cmp al,0x01 ; Esc key
jne no_esc
int 0x20
no_esc:
sub al,0x48 ; Code for arrow up?
jc no_key2 ; Out of range, jump.
cmp al,0x09 ; Farther than arrow down?
jnc no_key2 ; Out of range, jump.
mov bx,dirs
cs xlat ; Translate direction to internal code
mov [intended_dir],al ; Save as desired direction
no_key2:
mov si,pos1 ; SI points to data for player
lodsw ; Load screen position
xchg ax,di
lodsw ; Load direction/type
xchg ax,bx
xor ax,ax ; Delete pillman
call move_sprite2 ; Move
xor byte [frame],0x80 ; Alternate frame
mov ax,0x0e28 ; Closed mouth
js close_mouth ; Jump if sign set.
mov al,[pos1+2] ; Using current direction
mov cl,3 ; Multiply by 8
shl al,cl ; Show open mouth
close_mouth:
call draw_sprite ; Draw
;
; Move ghosts
;
mov bp, move_ghost
mov bh,GHOST1_COLOR
call bp
mov bh,GHOST2_COLOR
call bp
mov bh,GHOST3_COLOR
call bp
mov bh,GHOST4_COLOR
call bp
jmp game_loop
;
; DI = address on the screen
; BL = wanted direction
;
move_sprite3:
je move_sprite ; If zero, won't remove first time
move_sprite2:
call draw_sprite ; Remove ghost
move_sprite:
mov ax,di ; Prepare to extract pixel row/column
xor dx,dx
mov cx,X_OFFSET
div cx
; Now AX = Row, DX = Column
mov ah,dl
or ah,al
and ah,7 ; Both aligned at 8 pixels?
jne ms0 ; No, jump because cannot change direction.
; AH is zero already
;mov ah,0
;
; Get available directions
;
mov ch,MAZE_COLOR
cmp [di-0x0001],ch ; Left
adc ah,ah ; AH = 0000 000L
cmp [di+X_OFFSET*8],ch ; Down
adc ah,ah ; AH = 0000 00LD
cmp [di+0x0008],ch ; Right
adc ah,ah ; AH = 0000 0LDR
cmp [di-X_OFFSET],ch ; Up
adc ah,ah ; AH = 0000 LDRU
test bh,bh ; Is it pillman?
je ms4 ; Yes, jump
;
; Ghost
;
test bl,0x05 ; Test BL for .... .D.U
je ms6 ; No, jump
; Current direction is up/down
cmp dx,[x_player] ; Compare X coordinate with player
mov al,0x02 ; Go right
jc ms8 ; Jump if X ghost < X player
mov al,0x08 ; Go left
jmp ms8
; Current direction is left/right
ms6: cmp al,0x00 ; (SMC) Compare Y coordinate with player
mov al,0x04 ; Go down
jc ms8 ; Jump if Y ghost < Y player
mov al,0x01 ; Go up
ms8:
test ah,al ; Can it go in intended direction?
jne ms1 ; Yes, go in direction
mov al,bl
ms9: test ah,al ; Can it go in current direction?
jne ms1 ; Yes, jump
shr al,1 ; Try another direction
jne ms9
mov al,0x08 ; Cycle direction
jmp ms9
;
; Pillman
;
ms4:
mov [x_player],dx ; Save current X coordinate
cs mov [y_player],al ; Save current Y coordinate
mov al,[intended_dir]
test ah,al ; Can it go in intended direction?
jne ms1 ; Yes, go in that direction
ms5: and ah,bl ; Can it go in current direction?
je ms2 ; No, stops
ms0: mov al,bl
ms1: mov [si-2],al ; Save new direction
test al,5 ; If going up/down...
mov bx,-X_OFFSET*2 ; ...bx = vertical movement
jne ms3
mov bx,1*2 ; ...bx = horizontal movement
ms3:
test al,12
je ms7
neg bx ; Reverse direction
ms7:
add di,bx ; Do move
mov [si-4],di ; Save the new screen position
ms2:
ret
;
; Game bitmaps
;
bitmaps:
db 0x00,0x42,0xe7,0xe7,0xff,0xff,0x7e,0x3c ; dir = 1
db 0x3c,0x7e,0xfc,0xf0,0xf0,0xfc,0x7e,0x3c ; dir = 2
db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff ; Maze
db 0x3c,0x7e,0xff,0xff,0xe7,0xe7,0x42,0x00 ; dir = 4
db 0x3c,0x7e,0xff,0xff,0xff,0xff,0x7e,0x3c ; Closed mouth
db 0x3c,0x7e,0xdb,0xdb,0xff,0xff,0xff,0xa5 ; Ghost
db 0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00 ; Pill
db 0x3c,0x7e,0x3f,0x0f,0x0f,0x3f,0x7e,0x3c ; dir = 8
;
; Maze shape
;
maze:
dw 0b0000_0000_0000_0000
dw 0b0111_1111_1111_1110
dw 0b0100_0010_0000_0010
dw 0b0100_0010_0000_0010
dw 0b0111_1111_1111_1111
dw 0b0100_0010_0100_0000
dw 0b0111_1110_0111_1110
dw 0b0000_0010_0000_0010
dw 0b0000_0010_0111_1111
dw 0b0000_0011_1100_0000
dw 0b0000_0010_0100_0000
dw 0b0000_0010_0111_1111
dw 0b0000_0010_0100_0000
dw 0b0111_1111_1111_1110
dw 0b0100_0010_0000_0010
dw 0b0111_1011_1111_1111
dw 0b0000_1010_0100_0000
dw 0b0111_1110_0111_1110
dw 0b0100_0000_0000_0010
dw 0b0111_1111_1111_1111
dw 0b0000_0000_0000_0000
;
; Starting positions
;
setup_data:
dw BASE_MAZE+0x78*X_OFFSET+0x78
dw BASE_MAZE+0x30*X_OFFSET+0x70
dw BASE_MAZE+0x40*X_OFFSET+0x78
dw BASE_MAZE+0x20*X_OFFSET+0x80
dw BASE_MAZE+0x30*X_OFFSET+0x88
;
; Convert arrow codes to internal directions
;
dirs:
db 0x01 ; 0x48 = Up arrow
db 0x00
db 0x00
db 0x08 ; 0x4b = Left arrow
db 0x00
db 0x02 ; 0x4d = Right arrow
db 0x00
db 0x00
db 0x04 ; 0x50 = Down arrow
%if com_file
%else
times 510-($-$$) db 0x4f
db 0x55,0xaa ; Make it a bootable sector
%endif