1org 0x7C00
  2
  3; Entry point: BIOS loads this at 0x7C00 and jumps here
  4start:
  5    ; Set video mode to 13h: 320x200 pixels, 256 colors
  6    mov ax, 0x0013
  7    int 0x10
  8
  9    ; ES = 0xA000 is the segment for VGA memory
 10    push 0xA000
 11    pop es
 12
 13    ; Clear AX and set Data Segment to 0
 14    xor ax, ax
 15    mov ds, ax
 16
 17    ; Initialize game variables
 18    mov word [player_x], 155
 19    mov word [enemy_x], 100
 20    mov word [enemy_y], 0
 21    mov byte [bullet_active], 0
 22    mov word [score], 0
 23
 24game_loop:
 25    ; Use BIOS timer (ticks at 18.2Hz) for consistent game speed
 26    mov ah, 0x00
 27    int 0x1A
 28    mov bx, dx
 29
 30timer_wait:
 31    int 0x1A
 32    cmp dx, bx
 33    je timer_wait
 34
 35    ; Wait for vertical retrace to prevent screen flickering
 36    mov dx, 0x3DA
 37
 38retrace_wait:
 39    in al, dx
 40    test al, 8
 41    jz retrace_wait
 42
 43    ; Clear the entire screen (64000 pixels) with color 0 (black)
 44    xor di, di
 45    xor al, al
 46    mov cx, 64000
 47    rep stosb
 48
 49    ; Move cursor to (0,0) and print the current score
 50    xor dx, dx
 51    mov ah, 0x02
 52    xor bh, bh
 53    int 0x10
 54    mov ax, [score]
 55    call print_num
 56
 57    ; Check if a key is waiting in the keyboard buffer
 58    mov ah, 0x01
 59    int 0x16
 60    jz update_physics
 61
 62    ; Get the actual key pressed
 63    mov ah, 0x00
 64    int 0x16
 65
 66    ; Handle Arrow keys (scan codes) and Space bar
 67    cmp ah, 0x4B        ; Left Arrow
 68    je move_left
 69    cmp ah, 0x4D        ; Right Arrow
 70    je move_right
 71    cmp ah, 0x39        ; Space Bar
 72    je fire_bullet
 73    jmp update_physics
 74
 75move_left: 
 76    sub word [player_x], 10
 77    jnc update_physics
 78    mov word [player_x], 0
 79    jmp update_physics
 80
 81move_right: 
 82    add word [player_x], 10
 83    cmp word [player_x], 310
 84    jbe update_physics
 85    mov word [player_x], 310
 86    jmp update_physics
 87
 88fire_bullet:
 89    ; Allow only one bullet on screen at a time
 90    cmp byte [bullet_active], 0
 91    jnz update_physics
 92    mov byte [bullet_active], 1
 93    mov ax, [player_x]
 94    add ax, 4           ; Center bullet relative to player ship
 95    mov [bullet_x], ax
 96    mov word [bullet_y], 175
 97
 98update_physics:
 99    ; Process bullet movement if active
100    cmp byte [bullet_active], 0
101    jz update_enemy
102    sub word [bullet_y], 10
103    jnc bullet_in_bounds
104    mov byte [bullet_active], 0
105    jmp update_enemy
106
107bullet_in_bounds:
108    ; Collision check: Bullet vs Enemy (Bounding box)
109    mov ax, [bullet_y]
110    sub ax, [enemy_y]
111    add ax, 4
112    cmp ax, 19
113    ja update_enemy
114    mov ax, [bullet_x]
115    sub ax, [enemy_x]
116    add ax, 2
117    cmp ax, 17
118    ja update_enemy
119
120    ; If hit: Kill bullet, increase score, and respawn enemy
121    mov byte [bullet_active], 0
122    inc word [score]
123    jmp respawn_enemy
124
125update_enemy:
126    ; Move enemy down by 2 pixels
127    add word [enemy_y], 2
128    cmp word [enemy_y], 200
129    jae respawn_enemy
130
131    ; Collision check: Player vs Enemy
132    mov ax, [enemy_y]
133    add ax, 15          ; Enemy height
134    cmp ax, 185         ; Player Y start
135    jb draw_game
136
137    ; Bounding box check for X axis
138    mov ax, [player_x]
139    sub ax, [enemy_x]
140    add ax, 10
141    cmp ax, 25
142    jbe game_over
143
144respawn_enemy:
145    ; Reset enemy to top at a "random" X position based on system clock
146    mov word [enemy_y], 0
147    xor ax, ax
148    int 0x1A
149    mov ax, dx
150    xor dx, dx
151    mov bx, 305
152    div bx              ; Divide clock ticks by 305 to get X in screen range
153    mov [enemy_x], dx
154
155draw_game:
156    ; Render Player (10x5 Blue rectangle)
157    mov ax, 185
158    mov bx, [player_x]
159    mov si, 10
160    mov dx, 5
161    mov cl, 1           ; Color Blue
162    call rect
163
164    ; Render Bullet (2x4 Yellow rectangle) if active
165    cmp byte [bullet_active], 0
166    jz skip_bullet_draw
167    mov ax, [bullet_y]
168    mov bx, [bullet_x]
169    mov si, 2
170    mov dx, 4
171    mov cl, 14          ; Color Yellow
172    call rect
173
174skip_bullet_draw:
175    ; Render Enemy (15x15 Red rectangle)
176    mov ax, [enemy_y]
177    mov bx, [enemy_x]
178    mov si, 15
179    mov dx, 15
180    mov cl, 4           ; Color Red
181    call rect
182    jmp game_loop
183
184game_over:
185    ; Display Game Over message in middle of screen
186    mov dx, 0x0C0E      ; Position: Row 12, Column 14
187    mov ah, 0x02
188    xor bh, bh
189    int 0x10
190    mov si, msg_gameover
191
192print_msg:
193    lodsb
194    test al, al
195    jz wait_key
196    mov ah, 0x0E
197    int 0x10
198    jmp print_msg
199
200wait_key:
201    ; Wait for any key press to restart the game
202    mov ah, 0x00
203    int 0x16
204    jmp start
205
206; Subroutine: Convert number in AX to decimal and print to screen
207print_num:
208    mov bx, 10
209    xor cx, cx
210
211digit_loop:
212    xor dx, dx
213    div bx
214    push dx
215    inc cx
216    test ax, ax
217    jnz digit_loop
218
219char_loop:
220    pop ax
221    add al, '0'
222    mov ah, 0x0E
223    int 0x10
224    loop char_loop
225    ret
226
227; Subroutine: Draw a rectangle
228; Inputs: AX=Y, BX=X, SI=Width, DX=Height, CL=Color
229rect:
230    mov bp, dx
231    mov dx, 320
232    mul dx
233    add ax, bx
234    mov di, ax          ; Destination offset in VGA memory
235    mov al, cl
236
237draw_rect_loop:
238    push di
239    mov cx, si
240    rep stosb           ; Fill row with color
241    pop di
242    add di, 320         ; Move to next pixel row
243    dec bp
244    jnz draw_rect_loop
245    ret
246
247; Game variables and strings
248player_x      dw 0
249enemy_x       dw 0
250enemy_y       dw 0
251bullet_active db 0
252bullet_x      dw 0
253bullet_y      dw 0
254score         dw 0
255msg_gameover  db 'GAME OVER', 0
256
257; Pad file to 510 bytes and add the 0xAA55 boot signature
258times 510-($-$$) db 0
259dw 0xAA55