EXTRN  BLUE:byte,GREEN:byte,CYAN:byte,RED:byte,PURPLE:byte
EXTRN  BROWN:byte,WHITE:byte,GRAY:byte,LIGHT_BLUE:byte
EXTRN  LIGHT_GREEN:byte,LIGHT_CYAN:byte,LIGHT_RED:byte,
EXTRN  LIGHT_MAGENTA:byte,YELLOW:byte, INT_WHITE:byte
EXTRN  buildings, buildings_p, n_buildings:byte
EXTRN  buildings_hit:byte, score, end_game_code

PUBLIC  key_flag
PUBLIC  scan_code
PUBLIC  timer_flag

EXTRN   kbd_int:NEAR
EXTRN   setup_int:NEAR
EXTRN   timer_tick:NEAR

PUBLIC Game
PUBLIC Score2dec

        .MODEL  SMALL
        .STACK  200h
;-----------------------------------------------------------------------

INCLUDE GRAPHS.LIB           ; Includes file with graph-mode macros
INCLUDE IMAGES.LIB           ; Includes the routines that will draw
INCLUDE MY_MACRO.LIB
                             ; the images.
;------DATA SEGMENT----------------------------------------------------
        .DATA
        interval        DB      0        ; Movements counter.
        max_interval    EQU     15       ; How many movements of the
                                         ; plane is needed for next
                                         ; bomb to be dropped.

        max_bombs       EQU     6        ; Number of bombs in the air at the
                                         ; same time.
        esc_key         EQU     1        ; ESCAPE
        left_arrow      EQU     75       ; LEFT-ARROW
        right_arrow     EQU     77       ; RIGHT-ARROW
        up_arrow        EQU     72       ; UP-ARROW

        gun_y           EQU     400      ; Y-position of the gun
        gun_x           DW      50       ; Starting X-position of the gun
        gun_speed       DW      8        ; Increment (velocity) of the gun.
        size_of_gun     DW      40       ; Gun's length

        size_of_plane   DW      80       ; Length of the plane
        plane_speed     DW      5        ; Velocity of the plane
        plane_x         DW      10       ; X-coordinate of the plane
        plane_y         DW      50       ; Y-coordinate of the plane

        bomb_speed      DW      5        ; Bomb's velocity
        number_bombs    DB      0        ; Number of bombs in the air.
        bomb_x          DW      max_bombs dup (0)    ;  Arrays with bombs'
        bomb_y          DW      max_bombs dup (0)    ;  XY coordinates. 
        size_of_bomb    DW      8                    ; Length of a bomb   

        size_of_building DW     40                   ; Length of the building

        new_timer_vec   DW      ?,?     ; Interrupts' vectors.
        old_timer_vec   DW      ?,?     ;
        new_key_vec     DW      ?,?     ;
        old_key_vec     DW      ?,?     ;

        scan_code       DB      0       ; Scan-Code of the key pressed.
        key_flag        DB      0       ; 1 if any key was pressed
        timer_flag      DB      0       ; Tick of the tmer.

        misl_x         DW      0       ; Missile X-position
        misl_y         DW      0       ; Missile y_position
        misl_way       DW      0       ; Will hold the length bullet's way.
;-----------------------------------------------------------------------
        .CODE
GAME  PROC  
        push    ax
        push    bx
        push    cx
        push    dx
        push    ds
        mov     ax,@DATA
        mov     ds,ax

        mov     new_timer_vec,offset timer_tick     ; Setting timer's
        mov     new_timer_vec + 2,cs                ; interrupt
        mov     al,1ch                              ;
        lea     di,old_timer_vec                    ;
        lea     si,new_timer_vec                    ;
        call    setup_int                           ;

        mov     new_key_vec,offset kbd_int          ; Setting keyboard's
        mov     new_key_vec + 2,cs                  ; interrupt
        mov     al,09h                              ;
        lea     di,old_key_vec                      ;
        lea     si,new_key_vec                      ;
        call    setup_int                           ;

        call    update_score                        ; Initialization    

test_key:                                           ; Main cycle
        cmp     key_flag,1                          ; IF any_key was pressed    
        je      key_pressed                         ; THEN check which one
        jmp     test_timer                          ; ELSE check timer_tick

key_pressed:                                        ; Key was pressed    
        mov     key_flag,0                          ; Set flag.
        cmp     scan_code,esc_key                   ; IF <ESCAPE> pressed
        jne     mov_left                            ; THEN EXIT
        mov     end_game_code,1
        jmp     lGame_over                          ; 
mov_left:                                           ; IF <left-arrow> key was
        cmp     scan_code,left_arrow                ; pressed, then
        jne     mov_right                           ; set direction flag in CL
        mov     cl,0                                ; move the gun and
        call    move_gun                            ; check timer_tick
        jmp     test_timer
mov_right:                                          ; IF <right-arrow> key was
        cmp     scan_code,right_arrow               ; pressed, then
        jne     test_shot                           ; set direction flag in CL
        mov     cl,1                                ; move the gun and 
        call    move_gun                            ; check timer_tick.
        jmp     test_timer                          ;
test_shot:
        cmp     scan_code, up_arrow    ; Did we draw the shot ?
        jne     test_timer             ; if no, go check time_tick, else
        mov     misl_way,350           ; set initial length of bullet's way
                                       ; which will hit the sky (heaven ?).

        mov     cx,gun_x               ; Use CX as input = gun's X-position. 
        call    destroy_bomb           ; Check if any of the bombs was shot

        cmp     al,0                   ; If NO
        je      test_plane             ; May be we shot the plane ? 
                                       ; Otherwise :
                                       ; AL holds bomb's number
        mov     cx,380                 ; Top gun's pixel
        sub     cx,dx                  ; Calculate the length
        mov     misl_way,cx            ; of the shot into  misl_way.
                                       ; Optimizing the bombs array
        mov     cl,al                  ; by shifhting to the left
        call    del_bomb               ; up to position AL
        jmp     real_shot              ; Show shot.
test_plane:                     
        mov     cx,gun_x               ; Place Gun's X-position into CX 
        call    destroy_plane          ; check if plane is hit.
        cmp     al,-1                  ; If ALL planes
        jne     some_planes_left       ; the game is over.
        jmp     lGame_Over             ; But it could be just one of them
some_planes_left:                      ; 
        cmp     al,0                   ; If not so,
        je      real_shot              ; just hit the sky
                                       ; ELSE
        mov     cx,380                 ; adjust the length
        sub     cx,40                  ; of bullet way
        mov     misl_way,cx            ; and put it into misl_way
real_shot:
        movw    misl_x,gun_x          ; Show the shot as two lines going from
        movw    misl_y,gun_y          ; top 2 pixels of the gun.
        call    shot                   ; Do it.
test_timer:                            ;
        cmp     timer_flag,1           ; IF timer_tick occured
        je      start_move_plane       ; THEN move all the objects
        jmp     test_key               ; ELSE go handle key_pressing.

start_move_plane:                      ; Perform the next step of game
        mov     timer_flag,0           ; Clear timer_tick 

        call    move_plane             ; Plane is moved one position to right.
        inc     interval               ; Counter increased. 
                                       ; Check if it is the time
        cmp     interval,max_interval  ; to drop the bomb,
        jne     no_new_bomb            ; else, do not drop new bomb. 

        mov     interval,0             ; Clear the counter
        cmp     number_bombs,max_bombs ; IF allowed amount of bombs is in the
        je      no_new_bomb            ; air, do not drop more.
        call    add_bomb               ; ELSE new bomb is dropped.
no_new_bomb:                           ;         
        xor bx,bx                      ;
        mov cx,1                       ; Counter of falling bombs in CX
cont_bomb:                             ; Cycle to move down all
        cmp cl,number_bombs            ; avaible bombs. 
        jbe drop                       ; There are some !
        jmp test_key                   ; No, all objects were moved, quitting
                                       ; the cycle         
drop:                                  ;
        mov bl,cl                      ; Calculate position in bomb's
        dec bl                         ; array.
        shl bl,1                       ; BL points to current bomb.

        inc cl                         ; Increase counter of falling bombs

        call move_bomb                ; Move curennt bomb

        mov dx,bomb_y[bx]             ; Place it's Y-position in DX
        cmp dx,gun_y                  ; Bomb is higher the gun (and buildings)
        jbe cont_bomb                 ; So, move the next one (CL is set).
        sub dx,10                     ; Check lower bound of gun
        cmp dx,gun_y                  ; If bomb passed the gun, then
        jae below_gun                 ; go check buildings
        push cx                       ;  Saving CX
        mov cx,bomb_x[bx]             ;  Putting bombs X-position in it.
        call destroy_gun              ;  check if gun is hit
                                      ;  (we'll check for buildings later)  
        cmp al,1                      ;  Gun is it.
        jne gun_not_hit               ;
        pop cx                        ;  restoring CX,
        jmp lGame_over                ;  but the game is over!

; Since building are located below the gun we check them after checking of
; gun.

gun_not_hit:                          ; Restoring CX and recalling
        pop cx                        ; that DX still holds Y-position of the
below_gun:                            ; Bomb has passed the gun
        mov dx,bomb_y[bx]
        cmp dx,476              ; bomb. Compare it to 476 - buildngs level. 
        jbe cont_bomb                 ; Building is not hit - go on bombing.

        push cx                       ; CX is used as input.
        mov cx,bomb_x[bx]             ; Building is hit.
        call destr_build              ; Destroy it.
        pop cx                        ; restore CX

        cmp     al,-1                 ; IF all the  buildings were destroyed
        je      lGame_over            ; THEN game is over  
                                      ; ELSE
        dec cl                        ; Bomb does't exist any longer
        call del_bomb                 ; remove it from the array
        inc cl                        ; and shift next bomb to the left.
        jmp cont_bomb                 ; Go on bombing.
                                        
lGame_over:                             ; Game Over
         lea    di,new_timer_vec        ; Restoring 
         lea    si,old_timer_vec        ;      original 
         mov    al,1ch                  ;        interrup handlers
         call   setup_int               ;             for 
                                        ;             timer
         lea    di,new_key_vec          ;   and
         lea    si,old_key_vec          ;             keyboard
         mov    al,09h                  ;
         call   setup_int               ;

        pop     ds                      ;
        pop     dx                      ;
        pop     cx                      ; Restoring registers
        pop     bx                      ;
        pop     ax                      ;

        ret                           ; Return to Main module  
GAME    ENDP                          ; End of Game procedure
;-----------------------------------------------------------------------
;Below are stored the procedures we use to handle all the events of the game.
;-----------------------------------------------------------------------
Move_Gun        PROC
;Procedure move_gun moves gun when one of the two appropriate keys
;was pressed.
;Input  CL - direction  0 - left, 1 - right
;Output - none
        push    ax
        push    bx
        push    cx
        push    dx
        push    si
        push    di

        draw_gun        gun_x,gun_y,0,0,0,0     ; Hide the gun
                                                ; on its original position.
        mov     ax,gun_x                        ; X-position in AX
if_left:
        cmp     cl,0                            ; Moving to the left
        jne     if_right
        cmp     ax,gun_speed                    ; check if we hit the left
        jb      done_gun                        ; boundary of the screen
        sub     ax,gun_speed                    ; if not
        mov     gun_x,ax                        ; mov the gun to the left
        jmp     done_gun                        ; amd exit

if_right:                                       ; Moving to the right
        mov     ax,gun_x                        ; Checking if
        add     ax,size_of_gun                  ; the right boundary of the
        add     ax,gun_speed                    ; screen, where
        cmp     ax,640                          ; 640 = Resolution.
        ja      done_gun                        ; If not so,
        sub     ax,size_of_gun                  ; move the gun and
        mov     gun_x,ax                        ; exit.
done_gun:               ; Drawing the gun on new position
        draw_gun        gun_x,gun_y,INT_WHITE,GREEN,BROWN,0
        pop     di
        pop     si
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
ENDP
;-----------------------------------------------------------------------
SHOT    PROC
;Input - Variable misl_way is set in calling procedure.
;Output - None
        cmp     misl_way,0                      ; If bomb is immideately
        jg      show_shot                       ; above the gun then
        mov     misl_way,20                     ; No shot will be shown
show_shot:
        mov     ax,misl_way
        add     misl_x,19                       ;Gun's middle pixel
        sub     misl_y,ax                       ;Draw line from above to
        sub     misl_y,20                       ;gun's top pixel
        v_line  misl_x,misl_y,misl_way,RED,0    ;Draw first line
        inc     misl_x                          ;Increase X-position
        v_line  misl_x,misl_y,misl_way,RED,0    ;Draw second line
        v_line  misl_x,misl_y,misl_way,0,0      ;Hide old second line
        dec     misl_x                          ;Decrease X-Position
        v_line  misl_x,misl_y,misl_way,0,0      ;Hide old first line
        ret                                     ;Return 
ENDP
;-----------------------------------------------------------------------
MOVE_PLANE PROC
;Input - Variables plane_x and plane_y are set in calling procedure
;Output - None
        push ax                ;Save    Ax
        draw_plane      plane_x,plane_y,0,0,0,0 ;Hide plane
        mov     ax,plane_x                      ;Increase X-position of
        add     ax,size_of_plane                ;the plane to make
        add     ax,plane_speed                  ;sure it does not hit
        cmp     ax,640                          ;640 - Resolution of the 
        jb      cont_cycle                      ;screen. If so,
        mov     plane_x,0                       ;make it zero,
        jmp     redraw                          ;otherwise
cont_cycle:                                     ;
        sub     ax,size_of_plane                ;Restore original X-position
        mov     plane_x,ax                      ;put it into plane_x
redraw:                                         ;and
                                                ;draw the plane
        draw_plane      plane_x, plane_y,LIGHT_RED,YELLOW,LIGHT_GREEN,0
        pop ax          ;restore AX
        ret             ;Return
ENDP
;-----------------------------------------------------------------------
MOVE_BOMB  PROC
;Input BX - position of the bomb in bombs array
        push ax                               ; Save registers
        push bx                               ;
        mov     ax,bomb_speed                 ; Y - incremwnt is put in AX 
        bomb    bomb_x[bx],bomb_y[bx],0,0     ; Hide bomb
        add     word ptr bomb_y[bx],ax        ; Increase Y_position
        bomb    bomb_x[bx],bomb_y[bx],BROWN,0 ; Draw bomb.
        pop bx                                ; Restore registers
        pop ax                                ;
        ret                                   ;Return
ENDP

;----------------------------------------------------------------------
ADD_BOMB PROC
        push ax                         ;Save registers
        push bx                         ;
        inc     number_bombs            ;Number of bombs in the air increases
        mov     bl,number_bombs         ;Put this number in BL
        dec     bl                      ;Calculate new bomb's position in
        shl     bl,1                    ;bombs array
        xor     bh,bh                   ;
        mov     ax,plane_x              ;Assign plane's coordinates to
        mov     word ptr bomb_x[bx],ax  ;the bomb
        add     word ptr bomb_x[bx],60  ;and 
        mov     ax,plane_y              ;adjust them
        mov     word ptr bomb_y[bx],ax  ;accordingly.
        add     word ptr bomb_y[bx],10  ;
        pop     bx                      ;Restore registers
        pop     ax                      ;
        ret                             ;Return
ENDP
;------------------------------------------------------------------------
DEL_BOMB PROC
; This procedure optimazes the bombs array so it does not have empty 
; positions inside. 
;Input CL - index of bomb that was shot bu the gun or hit the surface etc.

   push         ax                       ;Save registers
   push         bx                       ;
   push         cx                       ;
   push         dx                       ;
   xor          bh,bh                    ; Clear BH
transfer:   
   cmp  cl,number_bombs                  ; If not available bombs were checked
   je   finish                           ; then
   mov  bl,cl                            ; get bomb position in
   shl  bl,1                             ; bomb_x_y array
   mov  ax,word ptr bomb_x[bx]           ; Take next bomb's
   mov  dx,word ptr bomb_y[bx]           ; coordinates
   sub  bl,2                             ; and put them
   mov  word ptr bomb_x[bx],ax           ; into previous
   mov  word ptr bomb_y[bx],dx           ; position.
   inc  cl                               ; Get to next bomb
   jmp  transfer                         ; and check it.
finish:
   dec  number_bombs                     ; Decreasing number of bombs
   pop  dx                               ; that are in the air in the same
   pop  cx                               ; time,
   pop  bx                               ; restoring registers,
   pop  ax                               ;
   ret                                   ; quitting.
DEL_BOMB ENDP
;-----------------------------------------------------------------------
Destr_Build PROC
;Input  CX - Bomb's X_position
;Output AL =  index of destroyed building OR
;       AL =  0 IF no buildings were destroyed
;       AL = - 1 if ALL buildings were destroyed and game is over.
   push  bx             ;
   push  cx             ; Saving registers
   push  dx             ;
   xor   al,al                    ; Clearing AL    (default)
   xor   bh,bh                    ; and BH
   mov   ah,1                     ; AH = index of building
begin_check:
   cmp  ah,n_buildings   ; Check if all buildings are checked.
   jbe  not_all                   ; If so, exit with AL = 0
   jmp  exit_destr_build          ; otherwise
not_all:                          ;
   mov  bl,ah                     ; Use AH
   dec  bl                        ; to get building's index into BX
   shl  bl,1                      ; bx=2*(ah-1)
   inc  ah                                ;Increase AH
   cmp  word ptr buildings_p[bx],0        ;If presence byte is 0, 
   je   begin_check                       ;check next building, else 
   mov  dx,buildings[bx]                  ;DX contains X-position of building.
   add  cx,size_of_bomb                   ; Check if building is hit.
   cmp  cx,dx                    ;IF it's to the left from current building
   ja   if_hit                   ;THEN exit, because all left buildings were
   jmp  exit_destr_build         ;checked before.
if_hit:                          ;Else
   sub  cx,size_of_bomb          ;If it's not to the rigth from the building
   add  dx,size_of_building      ;it's a hit!
   cmp  cx,dx                    ;Otherwise, 
   jae  begin_check              ;Check next building available.
   mov  word ptr buildings_p[bx],0                ;Put 0 into presense byte
   draw_building buildings[bx],466,0,0,0,0      ;and hide the building
   mov  al,ah                    ; AL contains number of erased building
   dec  al                       ; Return AL.
   inc  buildings_hit            ; Increase counter of buildings hit
   call update_score
   mov  bl,buildings_hit         ; We can use BX by this moment.
   cmp  bl,n_buildings           ; IF all of them were destroyed
   jb   exit_destr_build         ; THEN change AL to return -1
   mov  al,-1                    ; to indicate that the game is over.
exit_destr_build:                ;
   pop  dx                       ;
   pop  cx                       ; Restoring registers
   pop  bx                       ;
   ret                           ; Return
destr_build ENDP
;-----------------------------------------------------------------------
DESTROY_BOMB PROC
;Input CX - X_position of gun   
;Output AL = index of the bomb which was destroyed or
;       AL = 0 no bombs were destroyed
;       DX = Y_position of the destroyed bomb
    add         cx,20                        ; Shot is drawn from the middle 
    push        bx                           ; of the gun
    xor         al,al                        ; Clearing      (0 - no bombs)
    xor         bh,bh                        ; registers.
    mov         ah,1                         ; Beginning with 1st Bomb
scan_bombs:
    cmp         ah,number_bombs              ;IF all bombs are checked
    jbe         erasing_bomb                 ;THEN exit
    jmp         exit_destroy_bomb            ;ELSE
erasing_bomb:
    mov         bl,ah                        ; BL=2*(AH-1) - this moves
    dec         bl                           ; BL in bombs array.
    shl         bl,1                         ;
    inc         ah                           ;(Move AX toward next bomb)
    mov dx,word ptr bomb_x[bx]               ;DX = X_position of the bomb
    cmp         cx,dx            ;Compare positions of the bomb and gun
    jb          scan_bombs       ;If to_the_left of the bomb - check next one.
    add         dx,size_of_bomb  ; Check 
    cmp         cx,dx            ;IF to_the_ right of the bomb-check next one
    ja          scan_bombs       ;ELSE
    add         score,25
    call        update_score
    dec         ah               ;There is a hit!
    mov         al,ah            ;AL contains number of destroyed bomb
    mov         dx,word ptr bomb_y[bx]         ; Define the misl_way   
    bomb        bomb_x[bx],bomb_y[bx],0,0      ; Hiding the bomb
exit_destroy_bomb:
    pop         bx               ; Restoring registers 
    ret                          ; Return
ENDP
;-----------------------------------------------------------------------
DESTROY_PLANE PROC
;Input cx - X_position of gun   
; Output AL=1  if plane was destroyed
;        AL=0  if plane was not destroyed

    push        bx                 ; Save BX 

    add         cx,20              ;Adjust gun's position to the middle.
    xor         al,al              ;Set AL to default value - 0 .
    mov         dx,plane_x         ;DX contains current X_position of plane
    cmp         cx,dx              ;Checking if the plane was hit
    jae         s_inside           ;
    jmp         exit_destroy_plane ;  To_the_left
s_inside:                          ;
    add         dx,size_of_plane   ;
    cmp         cx,dx              ;
    jbe         s1_inside          ;
    jmp         exit_destroy_plane ; To_the_right
s1_inside:                         ; 
    or          al,1               ; HIT!
    add         score,10
    call update_score              ;     
    draw_plane plane_x,plane_y,0,0,0,0  ; Hiding the plane on it's position
    mov         plane_x,0          ; Setting the plane at 0-position
exit_destroy_plane:                ; return
    pop  bx
    ret
ENDP
;-----------------------------------------------------------------------
DESTROY_GUN PROC
;Input CX - X_position of bomb   
;Output AL=1 if gun was destroyed
;       AL=0 if gun was not destroyed
    push        cx                    ; Saving registers
    push        dx                    ;
    add         cx,size_of_bomb       ; Set CX to right edge of the bomb
    xor         al,al                 ; Set AL to default
    mov         dx,gun_x              ; Set DX to gun's X_Position
    cmp         cx,dx                 ; Compare
    jae         s3_inside             ; IF the possibility still extis
    jmp         exit_destroy_gun      ; Check it, else EXIT
s3_inside:                            ;
    sub         cx,size_of_bomb       ; Set CX to left edge of the bomb
    add         dx,size_of_gun        ; Set DX to the right edge of the gun
    cmp         cx,dx                 ; Compare
    jbe         gun_is_hit            ; IF CX < DX there is a hit
    jmp         exit_destroy_gun      ; ELSE exit
gun_is_hit:                           ;
    or          al,1                  ; Set AL
    draw_gun gun_x,gun_y,0,0,0,0      ; Hide the gun
    mov         end_game_code,2       ;   
exit_destroy_gun:                     ;
    pop         dx                    ; Restorimg registers
    pop         cx                    ;
    ret                               ; return
ENDP
;-----------------------------------------------------------------------
Update_score Proc
; Procedure updates number of buldings and planes left
        push    ax
        push    dx

        move_cursor  1,15,0                      ; Set cursor
        mov     dl,n_buildings          ; Get number of hits
        sub     dl,buildings_hit        ; Get number of buildings
        add     dl,30h                           ; left, convert to ASCII
        mov     ah,02                            ; Print
        int     21h                              ;
        move_cursor 1,69,0                       ; Set Cursor
        call score2dec                           ; Print score
        pop     dx                               ;
        pop     ax                               ;
        ret                                      ;
EndP
;------------------------------------------------------------------------
Score2Dec Proc
; (The method used is taken from the text-book)
; Input - in variable SCORE
; Output - prints SCORE in ASCII on the sreen
  push ax
  push bx
  push cx                                       ; Saving Registers
  push dx
          mov  ax,score                         ;ax contains score
          xor  cx,cx                            ;cx counts decimal digits
          mov  bx,10                            ;bx has divisor
repeat1:
          xor  dx,dx                         ;prepare high word of dividend
          div  bx                            ;ax=quotient, dx=remainder
          push dx                               ;save remainder on stack
          inc  cx                               ;count=count+1
          or   ax,ax                            ;quotient=0?
          jne  repeat1                          ;no,keep going
          mov  ah,2                             ;print char function
print_loop:
          pop  dx                               ;digit in dl
          or   dl,30h                           ;convert to character
          int  21h                              ;print digit
          loop print_loop
  pop  dx
  pop  cx                                        ;Restoring Registers.
  pop  bx
  pop  ax
  ret                                           ; Return to Update_Score
Endp    
;-----------------------------------------------------------------------
END
