-
Notifications
You must be signed in to change notification settings - Fork 3
/
snake.asm
614 lines (536 loc) · 11 KB
/
snake.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
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
; Yehonatan Mizrachi
; 8086 masm DOSBox
; Micro processors and assembly language - bar ilan university
.model small
.stack 200h
.data
; init
backgroud_color equ 60h
player_score_color equ 2Bh
screen_width equ 80d
screen_hight equ 25d
; player
player_score_label_offset equ (screen_hight*screen_width-1d)*2d
player_score db ?
player_win_score equ 0FFh
; snake
; len X 2
snake_len dw ?
snake_body dw player_win_score + 3h dup(?)
; for repairing the backgroud(the snake will never start at 25d*80d*2d)
snake_previous_last_cell dw ?
; snake movement
; 4D/4B/48/50 - r/l/u/d. defulte - right
RIGHT equ 4Dh
LEFT equ 4Bh
UP equ 48h
DOWN equ 50h
snake_direction db ?
; food
food_location dw ?
food_color equ 4Dh
food_icon equ 01h
food_bounders equ 2d*screen_width*2d
; start and exit
EXIT db 0h
START_AGAIN db 0h
; 39h = bios code for the space key
START_AGAIN_KEY equ 39h
END_GAME_KEY equ 01h
; messeges
msg_game_over db 'GAME OVER!:( YOU ANYWAY SHOULD GET BACK TO WORK ON EX5Q3;) PRESS Esc TO EXIT ' ,0Ah , 0Dh , '$'
msg_game_over2 db ' PRESS SPACE TO START AGAIN',0Ah , 0Dh , '$'
msg_game_win db 'YOU HAVE WON THE GAME BADASS!!:) PRESS ANY KEY TO EXIT' ,0Ah , 0Dh , '$'
msg_start_game db 'WELCOME TO SNAKEV2! PRESS THE Esc KEY TO EXIT THE GAME. enjoy:)',0Ah , 0Dh , '$'
;Ex2q3 write register content
;initializing ascii array with every possible combenation of ?? 0-F
ascii db 16 dup ('0')
db 16 dup ('1')
db 16 dup ('2')
db 16 dup ('3')
db 16 dup ('4')
db 16 dup ('5')
db 16 dup ('6')
db 16 dup ('7')
db 16 dup ('8')
db 16 dup ('9')
db 16 dup ('A')
db 16 dup ('B')
db 16 dup ('C')
db 16 dup ('D')
db 16 dup ('E')
db 16 dup ('F')
db 16 dup ('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F')
.code
MAIN:
; data segment initializion
mov ax, @data
mov ds, ax
call INIT_GAME
; infinit loop with escape key(esc) and game over or win game options
MAIN_LOOP:
;next frame
call MOVE_SNAKE
call PRINT_SNAKE
call CHECK_SNAKE_AET_FOOD
call CHECK_SNAKE_IN_BORDERS
call CHECK_SNAKE_NOOSE
call GET_DIRECTION_BY_KEY
call MAIN_LOOP_FRAME_RATE
; if exit is on, end the game and return to OS
cmp [EXIT],1h
jnz MAIN_LOOP
; start again
cmp [START_AGAIN],1h
jz MAIN
call INIT_SCREEN_BACK_TO_OS
; return to OS
mov ah,4ch
int 21h
INIT_GAME proc near
mov byte ptr [player_score],0h
mov byte ptr [snake_direction],RIGHT
mov word ptr [snake_previous_last_cell],screen_width*screen_hight*2d
mov word ptr [food_location],8d*screen_width*2d + 10d*2d
mov byte ptr [EXIT],0h
mov byte ptr [START_AGAIN],0h
call INIT_SCREEN
call INIT_SNAKE_BODY
ret
INIT_GAME endp
; if it is, it's GAME OVER. the snake is noose if the head has the same location as one of its body cells
CHECK_SNAKE_NOOSE proc near
push si
push ax
mov ax,snake_body[0h]
mov si,2h
CHECK_SNAKE_NOOSE_LOOP:
; if ax == snake body[si] its game over
cmp ax,snake_body[si]
jz CHECK_SNAKE_NOOSE_GAME_OVER
; next iteration
add si,2h
cmp si,snake_len
jnz CHECK_SNAKE_NOOSE_LOOP
jmp END_CHECK_SNAKE_NOOSE
CHECK_SNAKE_NOOSE_GAME_OVER:
call GAME_OVER
END_CHECK_SNAKE_NOOSE:
pop ax
pop si
ret
CHECK_SNAKE_NOOSE endp
; for now, N and S(E and W is fine)
CHECK_SNAKE_IN_BORDERS proc near
push ax
mov ax,snake_body[0h]
;S
cmp ax,screen_width*screen_hight*2h
jb CHECK_SNAKE_IN_BORDERS_VALID
call GAME_OVER
CHECK_SNAKE_IN_BORDERS_VALID:
pop ax
ret
CHECK_SNAKE_IN_BORDERS endp
CHECK_SNAKE_AET_FOOD proc near
push ax
push si
mov ax, snake_body[0h]
cmp ax,food_location
jnz END_CHECK_SNAKE_AET_FOOD
; gemerate new food location
call GENERATE_RANDOM_FOOD_LOCATION
; print it to the screen
mov si,[food_location]
mov al,food_icon
mov ah,food_color
mov es:[si],ax
; make the snake bigger
mov ax,[snake_previous_last_cell]
mov si,[snake_len]
mov snake_body[si],ax
add [snake_len],2d
; add score
inc byte ptr [player_score]
call PRINT_PLAYER_SCORE
cmp byte ptr [player_score],player_win_score
jnz END_CHECK_SNAKE_AET_FOOD
call WIN_GAME
END_CHECK_SNAKE_AET_FOOD:
pop si
pop ax
ret
CHECK_SNAKE_AET_FOOD endp
GENERATE_RANDOM_FOOD_LOCATION proc near
push ax
push dx
push si
push bx
GENERATE_RANDOM_FOOD_LOCATIPN_AGAIN:
; update its location
; cx:dx number of clock ticks since midnight
mov ah,0h
INT 1Ah
mov ax,dx
mov dx,cx
add dx,[snake_len]
add dx,[snake_len]
; div 16-bit dx:ax/operant -> dx = mod, ax = result
mov cx, screen_width*screen_hight*2h - food_bounders
div cx
;get rid of the last bit
and dx,0FFFEh
add dx, food_bounders/2d
;check if the food is on the snake
mov si,0d
GENERATE_RANDOM_FOOD_LOCATION_AGAIN_LOOP:
mov ax,snake_body[si]
;if the new location is on the snake, start over the whole function
cmp dx,ax
jz GENERATE_RANDOM_FOOD_LOCATIPN_AGAIN
add si,2d
cmp si,[snake_len]
jnz GENERATE_RANDOM_FOOD_LOCATION_AGAIN_LOOP
;update food location
mov [food_location], dx
pop bx
pop si
pop dx
pop ax
ret
GENERATE_RANDOM_FOOD_LOCATION endp
MAIN_LOOP_FRAME_RATE proc near
push ax
push cx
push dx
push bx
;make the game faster
mov bx,0h
mov bl,[player_score]
mov cl,4d
shr bx,cl
;delay cx:dx micro sec (10^-6)
mov al,0
mov ah,86h
mov cx,0000h
mov dx,0FFFFh
sub dx,bx
int 15h
pop bx
pop dx
pop cx
pop dx
ret
MAIN_LOOP_FRAME_RATE endp
WIN_GAME proc near
push dx
push ax
mov dx, offset msg_game_win
mov ah, 9h
int 21h
; get key delay
mov ax, 0h
mov ah,0h
int 16h
; clear key buffer
mov ah,0Ch
int 21h
mov byte ptr [EXIT],1h
pop ax
pop dx
ret
WIN_GAME endp
GAME_OVER proc near
push dx
push ax
push bx
; print game over msg
mov dx, offset msg_game_over
mov ah, 9h
int 21h
; add blink to the msg2 line
mov bx,0h
GAME_OVER_BLINK_LABEL:
mov ax,0h
mov ah,backgroud_color
or ah,10000000b
mov es:[bx + 3*screen_width*2d], ax
; next iteration
add bx,2h
cmp bx, screen_width*2d
jnz GAME_OVER_BLINK_LABEL
; print game over msg2
mov dx, offset msg_game_over2
mov ah, 9h
int 21h
GAME_OVER_GET_OTHER_KEY:
; clear key buffer
mov ah,0Ch
int 21h
; get key
mov ax,0h
mov ah,0h
int 16h
cmp ah, END_GAME_KEY
jz END_GAME_OVER
cmp ah, START_AGAIN_KEY
jz GAME_OVER_START_AGAIN
jmp GAME_OVER_GET_OTHER_KEY
GAME_OVER_START_AGAIN:
mov [START_AGAIN],1h
END_GAME_OVER:
; clear key buffer
mov ah,0Ch
int 21h
mov byte ptr [EXIT],1h
pop bx
pop ax
pop dx
ret
GAME_OVER endp
MOVE_SNAKE proc near
push ax
push bx
; save snake_previous_last_cell(for backgroud repairing)
mov bx,snake_len
mov ax,snake_body[bx - 2d]
mov [snake_previous_last_cell],ax
mov ax,snake_body[0h]
call SHR_ARRAY
; RIGHT
cmp byte ptr [snake_direction],RIGHT
jz MOVE_RIGHT
; LEFT
cmp byte ptr [snake_direction],LEFT
jz MOVE_LEFT
; UP
cmp byte ptr [snake_direction],UP
jz MOVE_UP
; DOWN
cmp byte ptr [snake_direction],DOWN
jz MOVE_DOWN
MOVE_RIGHT:
add ax,2d
jmp MOVE_TO_DIRECTION
MOVE_LEFT:
sub ax, 2d
jmp MOVE_TO_DIRECTION
MOVE_UP:
sub ax, screen_width*2d
jmp MOVE_TO_DIRECTION
MOVE_DOWN:
add ax, screen_width*2d
jmp MOVE_TO_DIRECTION
MOVE_TO_DIRECTION:
;add the new head cell
mov snake_body[0h],ax
pop bx
pop ax
ret
MOVE_SNAKE endp
PRINT_SNAKE proc near
push ax
push si
push bx
;clear the screen
; mov ax, 03h
; int 10h
;repair the backgroud
mov bx,[snake_previous_last_cell]
mov al,0h
mov ah,backgroud_color
mov es:[bx],ax
;print head
mov al,'o'
mov ah, 10h
mov bx, snake_body[0d]
mov es:[bx], ax
;if the snake has no body(only head) - jump to the end of the function
cmp snake_len,2h
jz END_PRINT_SNAKE
;print the rest if the snake
;snake color(body)
mov al,0h
mov ah, 10h
mov si,2h
PRINT_SNAKE_LOOP:
mov bx, snake_body[si]
mov es:[bx], ax
;next iteration
add si,2h
cmp si, [snake_len]
jnz PRINT_SNAKE_LOOP
END_PRINT_SNAKE:
pop bx
pop si
pop ax
ret
PRINT_SNAKE endp
PRINT_PLAYER_SCORE proc near
push ax
push bx
mov ah,player_score_color
mov bx,0h
mov bl,[player_score]
; low
mov al, ascii[bx + 256d]
mov es:[player_score_label_offset],ax
; height
mov al, ascii[bx]
mov es:[player_score_label_offset-2d],ax
; label
mov al,':'
mov es:[player_score_label_offset-4d],ax
mov al,'E'
mov es:[player_score_label_offset-6d],ax
mov al,'R'
mov es:[player_score_label_offset-8d],ax
mov al,'O'
mov es:[player_score_label_offset-10d],ax
mov al,'C'
mov es:[player_score_label_offset-12d],ax
mov al,'S'
mov es:[player_score_label_offset-14d],ax
pop bx
pop ax
ret
PRINT_PLAYER_SCORE endp
INIT_SCREEN proc near
push ax
push cx
push si
; graphics mode
mov ah,00h
mov al,13h
int 10h
; set screen segment
mov ax, 0b800h
mov es, ax
; clear the screen
mov ax, 03h
int 10h
call WRITE_SCREEN_BACKGROUND
call PRINT_PLAYER_SCORE
; write the first food
mov si, [food_location]
mov al,food_icon
mov ah,food_color
mov es:[si],ax
; print start game msg
mov dx, offset msg_start_game
mov ah, 9h
int 21h
; --hide text-cusor--
pop si
pop cx
pop ax
ret
INIT_SCREEN endp
WRITE_SCREEN_BACKGROUND proc near
push si
push ax
; set backgroud
mov al,0h
mov ah,backgroud_color
mov si,0
INIT_BACKGROUND_LOOP:
mov es:[si],ax
add si,2d
cmp si,25d*80d*2d
jnz INIT_BACKGROUND_LOOP
pop ax
pop si
ret
WRITE_SCREEN_BACKGROUND endp
INIT_SCREEN_BACK_TO_OS proc near
push ax
push cx
;clear the screen
mov ax, 03h
int 10h
; normal text mode
mov ah,03h
mov al,13h
int 10h
pop cx
pop ax
ret
INIT_SCREEN_BACK_TO_OS endp
INIT_SNAKE_BODY proc near
; init snake_body
mov word ptr snake_body[6d],4d + 3d*screen_width*2d
mov word ptr snake_body[4d],6d + 3d*screen_width*2d
mov word ptr snake_body[2d],8d + 3d*screen_width*2d
mov word ptr snake_body[0d],10d + 3d*screen_width*2d
; sizeX2
mov word ptr [snake_len],8d
ret
INIT_SNAKE_BODY endp
; update [direction] accordingly. if there is no new key-event direction will stay the same.
; ecs will quit the game
GET_DIRECTION_BY_KEY proc near
; check for a key storke
push ax
push bx
mov ax, 0h
mov ah,01h
int 16h
; zero flag is on if there was no event
jz END_GET_DIRECTION_BY_KEY
; esc key
cmp ah,END_GAME_KEY
jz GET_DIRECTION_BY_KEY_EXIT_GAME_IS_ON
;if |new direction - old direction| == 3d or 5d it's a valid move(the snake cant turn backward)
mov bh,ah
mov bl,[snake_direction]
sub bh,bl
cmp bh,3d
jz GET_DIRECTION_BY_KEY_VALID_MOVE
cmp bh,5d
jz GET_DIRECTION_BY_KEY_VALID_MOVE
neg bh
cmp bh,3d
jz GET_DIRECTION_BY_KEY_VALID_MOVE
cmp bh,5d
jz GET_DIRECTION_BY_KEY_VALID_MOVE
; invalid move:
; clear key buffer
mov ah,0Ch
int 21h
jmp END_GET_DIRECTION_BY_KEY
GET_DIRECTION_BY_KEY_VALID_MOVE:
mov [snake_direction], ah
; clear key buffer
mov ah,0Ch
int 21h
jmp END_GET_DIRECTION_BY_KEY
GET_DIRECTION_BY_KEY_EXIT_GAME_IS_ON:
mov byte ptr [EXIT], 1h
; clear key buffer
mov ah,0Ch
int 21h
END_GET_DIRECTION_BY_KEY:
pop bx
pop ax
ret
GET_DIRECTION_BY_KEY endp
; the last cell overrided
SHR_ARRAY proc near
push bx
push ax
push si
mov si,[snake_len]
sub si,2h
L1:
mov ax,snake_body[si - 2h]
mov snake_body[si], ax
;next iteration
sub si,2h
cmp si,0h
jnz L1
pop si
pop ax
pop bx
ret
SHR_ARRAY endp
end MAIN