-
Notifications
You must be signed in to change notification settings - Fork 0
/
pongmbr.nasm
312 lines (271 loc) · 7.42 KB
/
pongmbr.nasm
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
BITS 16
org 7c00h
; configurable constants
; Because different computers are different speeds the throttle should be changed
; to suit the computer's speed and desired game speed.
%define THROTTLE 0x2f22
%define SCREEN_TOP 0
%define BALL_HEIGHT 8
%define BAT_HEIGHT 32
; The code cannot handle a score past 9 so make sure GAME_POINT is between 1 and 9 inclusive
%define GAME_POINT 9
%define SCORE_1_POS 30
%define SCORE_2_POS 0
; semi-configurable constants
; these constants can be safely changed but may not have
; the changes one would expect
; SCREEN_LEFT and SCREEN_RIGHT are only useful for the ball start point
%define SCREEN_LEFT 0
%define SCREEN_RIGHT 640
; LINE_Y can be moved anywhere on the field and will still be the bottom
; of the field but changing this value may lead to poor aesthetics.
; If you must change the field height it is suggested you alter SCREEN_TOP
; instead.
%define LINE_Y 455
; non-configurable constants
; one way or another these each rely on something hardcoded
%define LEFT_BAT_COLUMN 4
%define RIGHT_BAT_COLUMN 76
%define LEFT_BAT_SURFACE 40
%define RIGHT_BAT_SURFACE 607
%define BALL_WIDTH 8
jmp short start
ballx:
dw (SCREEN_RIGHT - SCREEN_LEFT) / 2 + SCREEN_LEFT ; start at half point
bally:
dw (LINE_Y - SCREEN_TOP) / 2 + SCREEN_TOP ; start at half point
ballxvel:
dw -1 ; start moving toward left
ballyvel:
dw 0 ; start at no y velocity
bat1y:
dw (LINE_Y - SCREEN_TOP) / 2 + SCREEN_TOP ; start at half point
bat2y:
dw (LINE_Y - SCREEN_TOP) / 2 + SCREEN_TOP ; start at half point
bat1dir: ; bat1 direction choices: -1, 0, 1. up, none, down, respectively
db 0x0 ; initialise to immobile
bat2dir: ; bat2 direction choices: -1, 0, 1. up, none, down respectively
db 0x0 ; initialise to immobile
score1:
db 0x0 ; start at no score
score2:
db 0x0 ; start at no score
throttle:
dw THROTTLE ; throttle the game to manageable speeds
; adjust as necessary for a given cpu
start:
mov ax,0x12
int 0x10
mov ax,0xa000
mov es,ax ; ES points to video memory
mov dx,0x3c4 ; dx = index register
mov ax,0xF02 ; INDEX = MASK MAP
out dx,ax ; write all the bitplanes
; draw a line across the screen to section off scores
mov al, 0xff
mov di,(80 * LINE_Y)
mov cx, 80
.lineloop:
stosb
loop .lineloop, cx
.gameloop: ; main game loop
dec word [throttle]
cmp word [throttle],0
jne .gameloop
mov [throttle],word THROTTLE ; reset throttle counter
in al, 0x60 ; check for keyboard input and set bat directions accordingly
mov bl, 0x11
mov dl, 0x1f
mov si, bat1dir
push ax
call testinput
mov bl, 0x18
mov dl, 0x26
mov si, bat2dir
pop ax
call testinput
; move and redraw bats
mov al,[bat1dir]
mov bx,bat1y
call movebat
mov al,[bat2dir]
mov bx,bat2y
call movebat
mov di,LEFT_BAT_COLUMN
mov ax,[bat1y]
call drawbat
mov di,RIGHT_BAT_COLUMN
mov ax,[bat2y]
call drawbat
.moveball:
call cleanball ; clear out where the ball currently is
mov ax,[ballxvel]
add [ballx],ax ; move ball horizontal
mov ax,[ballyvel]
add [bally],ax ; move ball vertical
.checkycollision:
.ballattop:
cmp [bally], word SCREEN_TOP ; ball position counts from top left of ball
jg .ballatbottom
mov dx, SCREEN_TOP
jmp short .y_collide
.ballatbottom:
cmp [bally], word LINE_Y - BALL_HEIGHT ; ball position counts from top left of ball
jl .checkxcollision
mov dx, LINE_Y - BALL_HEIGHT
.y_collide:
mov [bally], dx
neg word [ballyvel] ; bounce y
.checkxcollision:
cmp [ballx], word LEFT_BAT_SURFACE ; ball position counts from top left of ball
jg .check_right_x
mov ax,[bat1y]
mov bx,score2
jmp short .checkbaty
.check_right_x:
cmp [ballx], word RIGHT_BAT_SURFACE - BALL_WIDTH ; ball position counts from top left of ball
jl .redraw
mov ax,[bat2y]
mov bx,score1
.checkbaty:
mov cx,[bally]
add cx,BAT_HEIGHT / 2 + BALL_HEIGHT ; if any part of the ball touches the bat it bounces
sub cx,ax
cmp cx,BAT_HEIGHT + BALL_HEIGHT
jna .bounce
; somebody scored! increment score and reset ball
inc byte [bx]
call cleanball
mov [ballx], word (SCREEN_RIGHT - SCREEN_LEFT) / 2
mov [bally], word (LINE_Y - SCREEN_TOP) / 2
mov [ballyvel], word 0 ; reset the y velocity. Don't touch the x so it will go
; away from the player who just scored
jmp short .redraw
.bounce:
mov ax,cx ; ax will be between 0 and 40
sub ax,(BAT_HEIGHT + BALL_HEIGHT) / 2 ; ax will be between -20 and 20
mov cl,(BAT_HEIGHT + BALL_HEIGHT) / 8 ; we want 8 segments on the bat
idiv cl
cbw
mov [ballyvel],ax
neg word [ballxvel] ; reverse
.redraw:
mov bl, 0xff
call drawball ; redraw ball in new position
.printscores:
mov dl, SCORE_1_POS
mov al, [score1]
call printscore
mov dl, SCORE_2_POS
mov al, [score2]
call printscore
.checkscore1:
cmp [score1], byte GAME_POINT
jl .checkscore2
jmp short end
.checkscore2:
cmp [score2], byte GAME_POINT
jl .gameloop
jmp short end
jmp .gameloop
end:
jmp short end
drawball:
xor dx,dx ; div needs dx zeroed out
mov ax,[ballx]
mov cx,8
div cx ; convert bit count to byte count
push dx
mov di,ax
mov ax,[bally]
mov cx,80
mul cx ; there are 80 columns per row
add di,ax
pop cx
mov al,0x80 ; initialise al to 0b10000000
sar al,cl ; and shift to set a 1 for every pixel in this byte
mov cx,BALL_HEIGHT ; ball is 8 tall
.balltop:
not al ; the left column needs lower bits
and al,bl
stosb
not al ; the right column needs upper bits
and al, bl
stosb
add di,78
loop .balltop,cx
ret
cleanball:
mov bl, 0x00
call drawball
ret
drawbat:
sub ax,BAT_HEIGHT / 2 + 2 ; stored positions are midpoints, advance back two further to clear above
mov cx,80
mul cx
add di,ax
mov cx, BAT_HEIGHT
mov ax, 0x00
stosb
add di,79
stosb
add di,79
mov al, 0xff ; bats are byte-aligned and one byte wide so just set all pixels in the byte
.battop:
stosb
add di,79
loop .battop, cx
mov ax, 0x00 ; clear the pixel below the bat as well
stosb
add di,79
stosb
ret
movebat:
.batup:
cmp al, byte 0xff
jne .batdown
.movebatup:
cmp [bx],word (BAT_HEIGHT / 2) + 3 + SCREEN_TOP ; bat is counted from the middle
jle .batup_end
sub [bx],word 2
.batup_end:
ret
.batdown:
cmp al, byte 0x01
jne .donemove
.movebatdown:
cmp [bx],word LINE_Y - (BAT_HEIGHT / 2 + 3) ; bat is counted from the middle
jge .batdown_end
add [bx],word 2
.batdown_end:
.donemove:
ret
printscore:
mov dh, 80
mov bx, 0x10f
mov ah, 0x2
int 0x10
mov cx, 0x1
add ax, 0x0830
int 0x10
ret
testinput:
mov cl, 0xff
.checkloop:
.check_up:
cmp al, bl
jne .check_down
mov [si], cl
.check_down:
neg cl ; -1 -> 1; 0 -> 0
cmp al, dl
jne .donecheck
mov [si], cl
.donecheck:
and cl, 0x0 ; keyups set dir to 0
xor al, 0x80 ; key up = key down + 0x80
test al, 0x80 ; if the 0x80 bit was already unset there's no point in looping
jz .checkloop
ret
times 510-($-$$) DB 0
DW 0xAA55