-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.asm
714 lines (630 loc) · 21 KB
/
main.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
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
.MODEL SMALL
.STACK 64
.DATA
COM1 EQU 03F8H
SCREEN_WIDTH EQU 320 - 1
SCREEN_HEIGHT EQU 200 - 1
BLACK EQU 0
DARK_BLUE EQU 1
GREEN EQU 2
LIGHT_BLUE EQU 3
RED EQU 4
PINK EQU 5
LIGHT_BROWN EQU 6
WHITE EQU 7
LIGHT_BLACK EQU 8
PURPLE EQU 9
LIGHT_GREEN EQU 10
CYAN EQU 11
ORANGE EQU 12
YELLOW EQU 14
TOP_MARGIN EQU 10
BOTTOM_MARGIN EQU SCREEN_HEIGHT
ROW DW 120
COLUMN DW 100
DOT_COLOR DW 0
BIRD_SIZE EQU 10
ROW_BIRD_START DW 20
ROW_BIRD_END DW 20 + BIRD_SIZE
COLUMN_BIRD_START DW 100
COLUMN_BIRD_END DW 100 + BIRD_SIZE
VELOCITY_BIRD DW 0
ACCELERATION_BIRD DW 1
FLY_VELOCITY_BIRD DW 0FFF9H
ROW_START DW 10
COLUMN_START DW 5
ROW_END DW 20
COLUMN_END DW 80
IS_BIRD_FLY DB 0
IS_GAMEOVER DB 0
IS_POINT_IN_WALL DB 0
; ROW_START, COLUMN_START, ROW_END, COLUMN_END
WALL DW 130,300,BOTTOM_MARGIN-1,300+20,150,420,BOTTOM_MARGIN-1,420+20,170,540,BOTTOM_MARGIN-1,540+20
WALL_INDEX DB 0
WALL_MAX_WIDTH DW 5
SCORE DW 0
SCORE_LENGTH DW 3
; 5 characters for score. 1 character for null terminator.
NUMBER_STRING DB 6 DUP (' ')
TEMP DB 'B'
; 1 EASY - 2 MEDIUM - 3 HARD
MODE DB ?
FLAG DB 0
MODE_STRING DB 'Choose the mode 1 EASY,2 MEDIUM,3 HARD: ','$'
.CODE
MAIN PROC FAR
MOV AX,@DATA
MOV DS,AX
MOV AX,0A000H
MOV ES,AX
CALL INIT_UART
CALL WELCOME_SCREEN
CALL CLEAR_SCREEN
CALL DRAW_MARGIN
MOV AX,SCORE
CALL PRINT_SCORE
CALL TEST_CODE
MOV AH,4CH ; exit program
INT 21H
MAIN ENDP
WELCOME_SCREEN PROC NEAR
MOV AH,09H ; Option 09 writes a string of data to stdout.
LEA DX,MODE_STRING ; Loads character input message into DX.
INT 21H
MOV AH,01H ; Option 01 reads one character from stdin.
INT 21H
SUB AL,'0' ; Converts ASCII number to binary number by subtracting the offset.
MOV MODE,AL
CMP AL,3
JE HARD
RET
HARD:
MOV ACCELERATION_BIRD,2
MOV FLY_VELOCITY_BIRD,0FFF5H
MOV WALL_MAX_WIDTH,1
RET
WELCOME_SCREEN ENDP
; The main logic of the game
; This routine gets no arguments and returns if the game is over
TEST_CODE PROC NEAR
DR:
; Wait here
CALL DELAY
; Moving and draw wall
MOV AL,0
LOOP_WALL1:
PUSH AX
CALL MOVE_WALL
POP AX
INC AL
CMP AL,3
JB LOOP_WALL1
;Moving bird
MOV DOT_COLOR,BLACK
CALL DRAW_BIRD
CALL MOVE_BIRD
MOV DOT_COLOR,WHITE
CALL DRAW_BIRD
; Check IS_GAMEOVER
CALL MARGIN_COLLISION
CALL WALL_COLLISION
CMP IS_GAMEOVER,0
JZ CONTINUE_GAME
RET
CONTINUE_GAME:
; Handle changing the score
CALL CLEAR_SCORE
INC SCORE
CALL PRINT_SCORE
CALL SEND_SCORE_UART
; For simulation, change the IS_BIRD_FLY bit
CALL CHECK_KEY_PRESS
CMP BX,1
JE KEY_IS_PRESSED
CALL CHECK_UART_CONTROLLER_KEY
CMP BX,0
JE KEY_NOT_PRESSED
KEY_IS_PRESSED:
MOV DX,FLY_VELOCITY_BIRD
MOV VELOCITY_BIRD,DX
KEY_NOT_PRESSED:
JMP DR
RET
TEST_CODE ENDP
; This routine makes a delay by busy waiting
DELAY PROC NEAR
CMP MODE,1
JE EASY_DELAY
CMP MODE,2
JE MEDIUM_DELAY
MOV CX,10000
BUSY_WAIT1:
LOOP BUSY_WAIT1
RET
EASY_DELAY:
MOV CX,0FFFFH
BUSY_WAIT2:
LOOP BUSY_WAIT2
MOV CX,0FFFFH
BUSY_WAIT3:
LOOP BUSY_WAIT3
RET
MEDIUM_DELAY:
MOV CX,60000
BUSY_WAIT4:
LOOP BUSY_WAIT4
DELAY ENDP
; This routine generates the offset of pixel in memory segment
; This routine gets 2 arguments. (ROW, COLUMN)
; Return BX as OFFSET
GET_OFFSET PROC NEAR
MOV AX,320
MUL ROW
ADD AX,COLUMN
MOV BX,AX
RET
GET_OFFSET ENDP
; This routine sets the video mode to 320x200
CLEAR_SCREEN PROC NEAR
MOV AH,0 ; set graphic mode
MOV AL,13H ; 320x200
INT 10H
RET
CLEAR_SCREEN ENDP
; This routine gets 3 arguments. (ROW, COLUMN, DOT_COLOR)
DRAW_DOT PROC NEAR
CALL GET_OFFSET
MOV DX,DOT_COLOR
MOV ES:[BX],DX
RET
DRAW_DOT ENDP
; This routine gets 5 arguments. (ROW_START, COLUMN_START, ROW_END, COLUMN_END, DOT_COLOR)
DRAW_SQUARE_FILL PROC NEAR
MOV DX,ROW_START
LOOP1:
MOV ROW,DX
PUSH DX
CALL DRAW_HORIZONT_LINE
POP DX
INC DX
CMP DX,ROW_END
JBE LOOP1
RET
DRAW_SQUARE_FILL ENDP
; This routine gets 4 arguments. (ROW_START, ROW_END, COLUMN, DOT_COLOR)
DRAW_VERTICAL_LINE PROC NEAR
MOV DX,ROW_START
LOOP1:
MOV ROW,DX
PUSH DX
CALL DRAW_DOT
POP DX
INC DX
CMP DX,ROW_END
JBE LOOP1
RET
DRAW_VERTICAL_LINE ENDP
; This routine gets 4 arguments. (COLUMN_START, COLUMN_END, ROW, DOT_COLOR)
DRAW_HORIZONT_LINE PROC NEAR
MOV DX,COLUMN_START
LOOP1:
MOV COLUMN,DX
PUSH DX
CALL DRAW_DOT
POP DX
INC DX
CMP DX,COLUMN_END
JBE LOOP1
RET
DRAW_HORIZONT_LINE ENDP
; This routine gets 5 arguments. (ROW_START, COLUMN_START, ROW_END, COLUMN_END, DOT_COLOR)
DRAW_SQUARE_OUTLINE PROC NEAR
;Draw upper line
MOV DX,ROW_START
MOV ROW,DX
CALL DRAW_HORIZONT_LINE
;Draw down line
MOV DX,ROW_END
MOV ROW,DX
CALL DRAW_HORIZONT_LINE
;Draw right line
MOV DX,COLUMN_END
MOV COLUMN,DX
CALL DRAW_VERTICAL_LINE
;Draw left line
MOV DX,COLUMN_START
MOV COLUMN,DX
CALL DRAW_VERTICAL_LINE
RET
DRAW_SQUARE_OUTLINE ENDP
; This routine gets 5 arguments. (ROW_BIRD_START, COLUMN_BIRD_START, ROW_BIRD_END, COLUMN_BIRD_END, DOT_COLOR)
DRAW_BIRD PROC NEAR
MOV DX,ROW_BIRD_START
MOV AX,COLUMN_BIRD_START
MOV ROW_START,DX
MOV COLUMN_START,AX
MOV DX,ROW_BIRD_END
MOV AX,COLUMN_BIRD_END
MOV ROW_END,DX
MOV COLUMN_END,AX
CALL DRAW_SQUARE_OUTLINE
RET
DRAW_BIRD ENDP
; This routine initializes the SI register to point to the WALL array element
; Gets argument (AL as WALL_INDEX)
INIT_WALL_INDEX PROC NEAR
LEA SI,WALL
; OFFSET = 4 * 2 * WALL_INDEX
MOV AH,8
MUL AH ; AX = 8 * WALL_INDEX
ADD SI,AX
RET
INIT_WALL_INDEX ENDP
; (DOT_COLOR, SI as WALL_INDEX)
DRAW_WALL PROC NEAR
; Read array elements
MOV DX,[SI]
MOV ROW_START,DX
MOV DX,[SI]+2
MOV COLUMN_START,DX
MOV DX,[SI]+4
MOV ROW_END,DX
MOV DX,[SI]+6
MOV COLUMN_END,DX
CALL DRAW_SQUARE_OUTLINE
RET
DRAW_WALL ENDP
; (WALL, SI as WALL_INDEX)
; This routine does not clear the whole wall. Just deletes the left and right lines of wall to make it ready for moving.
; Has more perfoemance than clearing whole wall and then draw a new shifted wall
DELETE_WALL PROC NEAR
;Remove the left line
MOV DX,[SI]
MOV ROW_START,DX
MOV DX,[SI]+2
MOV COLUMN,DX
MOV DX,[SI]+4
MOV ROW_END,DX
MOV DOT_COLOR,BLACK
CALL DRAW_VERTICAL_LINE
;Remove the left line
MOV DX,[SI]+6
MOV COLUMN,DX
CALL DRAW_VERTICAL_LINE
RET
DELETE_WALL ENDP
; A top level routine that is called in each game loop cycle
; This routine deletes wall and shift it to the left and draw a new wall
; If the wall is out of the screen, it will be removed and a new wall will be generated
; Gets (AL as WALL_INDEX)
MOVE_WALL PROC NEAR
CALL INIT_WALL_INDEX
; Check the wall reached to the left margin or not
MOV DX,[SI]+2
CMP DX,1
JA SHIFT_AND_DRAW
; Delete whole wall
MOV DOT_COLOR,BLACK
CALL DRAW_WALL
; Change the wall position
; Find the last wall column in WALL array
MOV AL,WALL_INDEX
ADD AL,2 ; go to the last wall
INC WALL_INDEX
; Get the remainder of AL/3. because we have 3 walls
MOV AH,0
MOV BL,3
DIV BL ; remainder in AH
MOV DI,SI ; copy the current wall index to DI
MOV AL,AH ; copy wall index to AL
CALL INIT_WALL_INDEX ; now SI points to the last wall
; Generate a random number for width of wall
MOV BX,WALL_MAX_WIDTH
CALL RANDOM_NUMBER ; Get a random number between 0 and 4 in DX
MOV AL,10
MUL DL ; result in AX
; set new columns value
MOV DX,[SI]+6 ; get the last wall column_end
ADD DX,100
MOV [DI]+2,DX ; set the new column_start
ADD DX,AX
MOV [DI]+6,DX ; set the new column_end
; Generate a random number for height of wall
MOV BX,10
CALL RANDOM_NUMBER ; Get a random number between 0 and 9 in DX
INC DX ; DX is between 1 and 10
MOV AL,10
MUL DL ; result in AX
MOV SI,AX ; Copy the height to SI
; Generate a random number for position of wall (down margin or up margin)
MOV BX,2
CALL RANDOM_NUMBER ; Get a random number between 0 and 1 in DX
CMP DX,0
JNZ WALL_UP
MOV DX,BOTTOM_MARGIN-1
MOV [DI]+4,DX
SUB DX,SI
MOV [DI],DX ; set the new row_start
RET
WALL_UP:
MOV DX,TOP_MARGIN+1
MOV [DI],DX
ADD DX,SI
MOV [DI]+4,DX
RET
SHIFT_AND_DRAW:
; Check the wall is in the screen or not
MOV DX,[SI]+6
CMP DX,SCREEN_WIDTH
JAE JUST_MOVE_IT
CALL DELETE_WALL
; Shift the wall to the left
MOV DX,[SI]+2
DEC DX
MOV [SI]+2,DX
MOV DX,[SI]+6
DEC DX
MOV [SI]+6,DX
MOV DOT_COLOR,GREEN
CALL DRAW_WALL
RET
JUST_MOVE_IT:
MOV DX,[SI]+2
DEC DX
MOV [SI]+2,DX
MOV DX,[SI]+6
DEC DX
MOV [SI]+6,DX
RET
MOVE_WALL ENDP
; Check the keyboard buffer for a key press. If a key is pressed, ZeroFlag will set 0.
; Return BX as is a key press
CHECK_KEY_PRESS PROC NEAR
MOV AH,01H ; check keyboard buffer is empty or not
INT 16H
JZ NO_KEY_PRESSED
MOV AH,0 ; read key from keyboard buffer (clear buffer)
INT 16H
MOV BX,1
RET
NO_KEY_PRESSED:
MOV BX,0
RET
CHECK_KEY_PRESS ENDP
; Return BX as is a key press
CHECK_UART_CONTROLLER_KEY PROC NEAR
MOV DX,COM1
IN AL,DX
CMP AL,0FFH
JNE NO_KEY_PRESSED
MOV BX,1
RET
NO_KEY_PRESSED:
MOV BX,0
RET
CHECK_UART_CONTROLLER_KEY ENDP
; This routine prints the score on the screen
PRINT_SCORE PROC NEAR
MOV AX,SCORE
CALL NUMBER_TO_STRING
LEA SI,NUMBER_STRING
MOV CX,SCORE_LENGTH
; Print the characters
LOOP1:
MOV AL,[SI]
MOV AH,0EH
INT 10H
INC SI
LOOP LOOP1
RET
PRINT_SCORE ENDP
; This routine converts AX number to string
; (AX as number)
NUMBER_TO_STRING PROC NEAR
MOV CX,0
MOV BX,10
LEA SI,NUMBER_STRING
DIVIDE:
; word/word division - DX: remainder, AX: quotient
MOV DX,0
DIV BX
INC CX
PUSH DX ; Push the remainder to stack
CMP AX,0
JNZ DIVIDE
MOV SCORE_LENGTH,CX
CONVERT:
POP DX
; Print the digit
ADD DL,30H
MOV [SI],DL
INC SI
LOOP CONVERT
RET
NUMBER_TO_STRING ENDP
; Clears the score characters by writing back-space character
; (SCORE_LENGTH)
CLEAR_SCORE PROC NEAR
MOV CX,SCORE_LENGTH
LOOP1:
MOV AH,0EH
MOV AL,08H
INT 10H
LOOP LOOP1
RET
CLEAR_SCORE ENDP
; (TOP_MARGIN, SCREEN_WIDTH, BOTTOM_MARGIN)
DRAW_MARGIN PROC NEAR
MOV DOT_COLOR,ORANGE
MOV ROW,TOP_MARGIN
MOV COLUMN_START,0
MOV COLUMN_END,SCREEN_WIDTH
CALL DRAW_HORIZONT_LINE
MOV ROW,BOTTOM_MARGIN
CALL DRAW_HORIZONT_LINE
RET
DRAW_MARGIN ENDP
; Checks weather the bird is beyond margins or not
; Return IS_GAMEOVER
MARGIN_COLLISION PROC NEAR
CMP ROW_BIRD_START,TOP_MARGIN
JBE GAMEOVER
CMP ROW_BIRD_END,BOTTOM_MARGIN
JAE GAMEOVER
RET
GAMEOVER:
MOV IS_GAMEOVER,1
RET
MARGIN_COLLISION ENDP
; Checks if any point of bird is in wall or not
WALL_COLLISION PROC NEAR
MOV AL,0
WALL_LOOP:
; right down corner
MOV DX,ROW_BIRD_END
MOV ROW,DX
MOV DX,COLUMN_BIRD_END
MOV COLUMN,DX
PUSH AX
CALL POINT_IN_WALL
POP AX
CMP IS_POINT_IN_WALL,1
JE GAMEOVER
; left down corner
MOV DX,ROW_BIRD_END
MOV ROW,DX
MOV DX,COLUMN_BIRD_START
MOV COLUMN,DX
PUSH AX
CALL POINT_IN_WALL
POP AX
CMP IS_POINT_IN_WALL,1
JE GAMEOVER
; left up corner
MOV DX,ROW_BIRD_START
MOV ROW,DX
MOV DX,COLUMN_BIRD_START
MOV COLUMN,DX
PUSH AX
CALL POINT_IN_WALL
POP AX
CMP IS_POINT_IN_WALL,1
JE GAMEOVER
; right up corner
MOV DX,ROW_BIRD_START
MOV ROW,DX
MOV DX,COLUMN_BIRD_END
MOV COLUMN,DX
PUSH AX
CALL POINT_IN_WALL
POP AX
CMP IS_POINT_IN_WALL,1
JE GAMEOVER
; Next wall
INC AL
CMP AL,3
JB WALL_LOOP
RET
GAMEOVER:
MOV IS_GAMEOVER,1
RET
WALL_COLLISION ENDP
; Gets (AL as WALL_INDEX)
; Checks is (ROW,COLUMN) in a wall or not. This routine will be called 4 times for each corner of the bird.
; The WALL_COLLISION routine will call this routine 4 times.
; Return IS_POINT_IN_WALL
POINT_IN_WALL PROC NEAR
CALL INIT_WALL_INDEX
MOV DX,ROW
MOV AX,COLUMN
CMP DX,[SI]
JB NOT_IN_WALL
CMP DX,[SI]+4
JA NOT_IN_WALL
CMP AX,[SI]+2
JB NOT_IN_WALL
CMP AX,[SI]+6
JA NOT_IN_WALL
MOV IS_POINT_IN_WALL,1
RET
NOT_IN_WALL:
MOV IS_POINT_IN_WALL,0
RET
POINT_IN_WALL ENDP
; Calculate the new position of the bird with Makan-Zaman equation
; new_position = old_position + (old_velocity * time) + (0.5 * acceleration * time * time)
; new_velocity = old_velocity + (acceleration * time)
; The time value is always 1 because the time unit is 1 cycle of the game loop.
; (ROW_BIRD, COLUMN_BIRD, VELOCITY_BIRD, ACCELERATION_BIRD)
MOVE_BIRD PROC NEAR
CMP MODE,3
JNE CHANGE_POSITION
CMP FLAG,2
JAE CHANGE_POSITION
INC FLAG
RET
CHANGE_POSITION:
MOV FLAG,0
; Calculate new ROW_BIRD_START
MOV AX,ACCELERATION_BIRD
MOV BX,2
MOV DX,0
DIV BX ; Result is in AX
MOV DX,VELOCITY_BIRD
ADD ROW_BIRD_START,DX
ADD ROW_BIRD_START,AX
; Calculate new ROW_BIRD_END
ADD ROW_BIRD_END,DX
ADD ROW_BIRD_END,AX
; Calculate new VELOCITY_BIRD
ADD DX,ACCELERATION_BIRD
MOV VELOCITY_BIRD,DX
RET
MOVE_BIRD ENDP
; Gets 1 argument (BX as maximum number)
; Return a random number between 0 and BX in DX
RANDOM_NUMBER PROC NEAR
MOV AX,0 ; Get clock ticks
INT 1AH
; Byte/Byte division - DX: remainder, AX: quotient
MOV AX,DX
MOV DX,0
DIV BX
RET
RANDOM_NUMBER ENDP
INIT_UART PROC NEAR
MOV AH,0
MOV AL,10100011B ; baudrate = 2400, 8 bit data, 1 stop bit, no parity
MOV DX,0 ; serial1
INT 14H
RET
INIT_UART ENDP
; Sends the NUMBER_STRING to COM1
SEND_SCORE_UART PROC NEAR
MOV AX,SCORE
MOV DX,0
MOV BX,10
DIV BX ; remainder in DX
CMP DX,0
JZ SEND
RET
SEND:
; send first byte of score
MOV DX,COM1
MOV CX,SCORE
MOV AL,CL
OUT DX,AL
WAIT_ACK1:
IN AL,DX
CMP AL,1
JNE WAIT_ACK1
; send second byte of score
MOV AL,CH
OUT DX,AL
WAIT_ACK2:
IN AL,DX
CMP AL,2
JNE WAIT_ACK2
RET
SEND_SCORE_UART ENDP
END MAIN