;Alison Bowman 
;OUTBREAK! 
;------------------------------------------------------------------------
comment! 
THE SCENARIO: You are on a mission from the United States Government to  
try and track down the ebola virus that has been infecting the population
and killing thousands. The ebola virus was brought to the United States
by way of a group of monkeys taken from Africa, where they contracted the
ebola. 
The United States Government is mostly concerned about protecting its 
people from this deadly disease. If the virus is allowed to get loose, it 
has the strength to erase all of mankind from the face of the Earth. You
must stop it before it does.

OBJECT OF THE GAME: Using the arrow keys, you must create walls (using the
population that remains on earth) and 'contain' the ebola. The only way to
contain the ebola is to make a box with the arrow keys, and trap the moving
virus inside your box. The ebola virus is represented on the screen by a 
yellow ball that bounces all over. Every time the virus bounces off a border
(which is your population remaining) it 'kills' another one of your remain-
ing people. Also, every time it bounces off the walls you are in the process 
of creating so you can contain it, it kills another person as well. You 
initially have a population of 100 people, and you must try to contain the 
ebola before it kills off your entire population. You 'score' points by 
first containing the virus, and once you have contained it in a now green
box, you have to try and catch it more times within that box. If you create 
a box that does NOT contain the ebola virus, it does not turn green, you are
not awarded any points, and the box disappears from the screen. The smaller 
the box you contain it in, the more points you are awarded. For boxes of 
large sizes, no points are awarded at all. If you score 10 points before your
'population dead' is equal to 100 (everyone is dead) then you win the game.
If you win, 'Camptown Races' plays and if you lose, you will hear 'Taps'.

DETAILS/RULES: The cursor begins at the bottom middle of the screen. When you 
are  at a border (touching the population) you cannot move into the borders 
or the game will terminate. These are considered 'hot zones'. So you must take
caution when manipulating the arrow keys. They must be pressed one at a time
and they cannot go beyond the borders (the population). The boxes you draw 
(which also consist of your 'population') must also be drawn carefully. In 
order to complete a box, you must 'use'one of the borders to complete the box
in which you contain the virus. Therefore, there are eight different ways you
can make a type of box: one that takes up each of the four corners of the
screen, and one that is on touched one of the four sides of the screen.
If you draw a box in the middle of the screen without 'using' one of the
borders (your population) you cannot contain the virus, the box will disappear
and your score will not increase. In addition, you must draw boxes that are
geometrically correct in that they have completely straight lines. Once you
are making a box of one type, you cannot change your mind in the middle of
making it and switch direction. Therefore, a box cannot have jagged edges or
stray corners if it is to contain the ebola virus. 

Esc will exit you from the game at any time during the game.
After the GAMOVER sign comes across the screen, you must hit Esc to
return to DOS.


The following procedures were borrowed from meltdown program
               restore_it
               save
               float 
               parts of gameover
               delay
The following procedures were borrowed from game program
               paint
               setpos
               tune
end comment! 
;------------------------------------------------------------------------
 
.model small
.stack 100h
.data

        border1         equ     02h             ;character for sides
        border2         equ     02h             ;character for top and bottom
        box             equ     01h             ;smilie face to use as box char
                                                ;represents the peoples
        bluecol         equ     17h             ;blue box's color
        greencol        equ     20h             ;green box's color
        scrLength       equ     79              ;length of screen
        scrHeight       equ     24              ;height of screen
        maxcol          equ     80
        ballpict        equ     09h             ;ascii ball char
        ballcolor       equ     0Eh             ;yellow on black
loser_song    dw 54,523,6,0
              dw 36,523,6,0
              dw 63,720,6,0
              dw 54,523,6,0
              dw 36,720,6,0
              dw 63,880,6,0
              dw 36,523,6,0
              dw 36,721,6,0
              dw 54,880,6,0
              dw 36,523,6,0
              dw 36,721,6,0
              dw 54,880,6,0
              dw 36,523,6,0
              dw 36,721,6,0
              dw 54,880,6,0
              dw 54,721,6,0
              dw 36,880,6,0
              dw 63,1050,6,0
              dw 36,1050,6,0
              dw 36,880,6,0
              dw 54,523,6,0
              dw 54,523,6,0
              dw 36,523,6,0
              dw 63,721,6,0
              dw 0
winner_song dw 36, 784, 6, 0
            dw 36, 784, 6, 0
            dw 36, 784, 6, 0
            dw 36, 659, 6, 0
            dw 36, 784, 6, 0
            dw 36, 880, 6, 0
            dw 36, 784, 6, 0
            dw 54, 659, 6, 0
            dw 36, 659, 6, 0
            dw 63, 587, 6, 0
            dw 36, 659, 6, 0
            dw 54, 587, 6, 0        
            dw 36, 784, 6, 0        
            dw 36, 784, 6, 0         
            dw 36, 784, 6, 0
            dw 36, 659, 6, 0 
            dw 36, 784, 6, 0
            dw 36, 880, 6, 0
            dw 36, 784, 6, 0
            dw 54, 659, 6, 0         
            dw 45, 587, 6, 0
            dw 18, 587, 6, 0
            dw 36, 659, 6, 0        
            dw 36, 587, 6, 0        
            dw 63, 523, 6, 0
            dw 0 

;array of the screen
        rows            db      2000 dup (0)    ;row
        box_array       dw      100  dup (0)    ;drawing box coordinates
        won_prompt      db      '**  YOU WON!!!! **',0
        box_length      db      0               ;length of box you're drawing
        ind             dw      0               ;index for box_array
        colnum          db      0               ;column number
        rownum          db      0               ;row number
        indexnum        dw      0               ;index for rows array
        key_scan        db      ?               ;scan code
        array_attrib    db      ?               ;codes of boxes
        array_index     dw      ?               ;
        direction       db      ?               ;number (1-4) depending on direction
        olddirection    db      0               ;previous direction
        made_box        db      ?               ;flag, 0 no box made, 1 box made
        top_r_corn_col  db     -1               ;initialized with sentinel
        top_l_corn_col  db     -1
        top_r_corn_row  db     -1
        top_l_corn_row  db     -1
        bot_r_corn_col  db     -1
        bot_l_corn_col  db     -1
        bot_r_corn_row  db     -1
        bot_l_corn_row  db     -1
        oldrow          db      ?               ;old row coordinate
        oldcol          db      ?               ;old column coordinate
        ballrow         db      ?               ;current position of ball
        ballcol         db      ?
        bvertdir        db      ?               ;ball direction -1 up, 1 down
        bcoldir         db      ?               ;               -1 left, 1 right
        old_ballc       db      ?               ;previous ball column
        old_ballr       db      ?               ;previous ball row
        endgame         db      0               ;0 = no, 1 = yes
        pop_dead        dw      0
        player_row      db      ?               ;
        player_col      db      ?
        spotok          db      0               ;flag if you can move to that location
        yesss           db      ?               ;
        speed           dw      ?               ;used for delay proc
        cantmove        db      0               ;flag so you cant move a box you've drawn pos
        msg             db      'G a m e   O v e r !!!',0
        end_col         db      ?                
        screen_char     db      ?
        screen_attr     db      ?
        score           db      0               
        last_col        db      ?
        last_row        db      ?
        boxes           dw      0               ;score
        u_dead          dw      0               ;flag that you lost
.code                   

if1
	include mac.asm
endif

main:
     mov ax, @data              ;initialize data segment
     mov ds, ax
     lea si, rows               ;initialize array pointer
     call clear_screen
     call welcome
     call clear_screen
     call set_speed
     call clear_screen
     call draw_screen
     call init_cursor
     mov di, 0                  ;initialize array pointer (box_array)
     call initialize_ball
play: call delay
     call Show_Ball
     cmp pop_dead, 100          ;is the entire population dead?
     je end_pop
     call is_key
     cmp yesss, 0ffh            ;a key was not hit on keyboard
     jne play                   ;continue displaying ball
     call get_key  
     cmp endgame, 1             ;was game ended?
     jne play                   ;no, continue playing
     jmp the_end
end_pop:
     mov u_dead, 1              ;yes, everyone is dead
the_end:     
     call quitting              ;yes, quit
;end of main

welcome proc
        save_regs <dx, ax>
        new_line                ;macro call
        new_line
        new_line
        disp_str 'Welcome to the game of OUTBREAK!'  ;macro call
        new_line
        disp_str 'Please hit any key to play'
;get key to begin
        mov ah, 1
        int 21h
        restore_regs <ax, dx>
        ret
welcome endp

set_speed proc
        save_regs <dx, ax>      ;macro call
        disp_str '1 = slow, 2 = Medium, 3 = FAST! '   
        new_line
        disp_str 'Please pick a speed: '
getkey: mov ah, 1               ;get key
        int 21h
        cmp al, 31h             ;was a number pressed?
        jb getkey
        cmp al, 33h             ;was the number between 1 and 3?
        ja getkey
        cmp al, 31h             ;was the number 1?
        je setslow
        cmp al, 32h             ;was the number 2?
        je setmed
        cmp al, 33h             ;was the number 3?
        je setfast              
setslow:
        mov speed, 13           ;speed is used in delay
        jmp speedset
setmed:
        mov speed, 9
        jmp speedset
setfast:
        mov speed, 6
speedset:
        restore_regs <ax, dx>    ;macro call
        ret
set_speed endp

clear_screen proc
        save_regs <ax, bx, cx, dx>
        mov ah, 6                 ;scroll up function
        xor al, al                ;clear whole screen
        xor cx, cx                ;upper left corner (0,0)
        mov dx, 184fh             ;lower right corner is (18, 4f)
        mov bh, 7                 ;normal video attribute
        int 10h                   ;clear screen
        restore_regs <dx, cx, bx, ax>
        ret
clear_screen endp

initialize_ball proc
     mov bvertdir, -1        ;start direction up
     mov bcoldir, 1          ;start direction right
     mov ballcol, 10
     mov ballrow, 7          ;start at position 1,1
     mov old_ballr, 7        ;put some number in old row
     mov old_ballc, 10
     ret
initialize_ball endp

setpos proc
;procedure to set position of the cursor
;input  dh = line       dl = column                                          

        save_regs <ax, bx>            
        mov bh, 0            ;set cursor position
        mov ah, 2
        int 10h
        restore_regs <bx, ax>
        ret
setpos endp

draw_screen proc
;this procedure will draw the screen for the game
        save_regs <bx,cx,dx>
        mov dx, 0            ;set line/column to 0
        mov cx, 1            ;print 1 character
        mov bl, bluecol      ;black on blue 
        xor bh, bh           ;page 0
        call drawborder      ;draw the sides
        mov dl, 1            ;start at row 1
        call drawtab         ;draw top and bottom
        restore_regs <dx, cx, bx>
        ret
draw_screen endp

drawborder proc
;this procedure will draw the borders for the screen
        save_regs <ax, dx>
        mov al, border1      ;character to paint
again:  call setpos
        mov ah, 9            ;subroutine 9
        int 10h              ;paint character
        mov array_attrib, 3  ;border code
        call in_array
        inc dh
        cmp dh, 25           ;is dh at end?
        jne again
        mov dh, 0            ;reset rows 
        cmp dl, 79           ;did we do both sides?
        je done
        mov dl, 79           ;no, so do right side
        jmp again
done:   restore_regs <dx, ax>
        ret

drawborder endp

drawtab proc
;this procedure draws the top and bottom of the screen
        save_regs <ax, dx>
        mov al, border2
nxtrow: call setpos          ;set position
        mov ah, 9            ;draw character function
        int 10h
        mov array_attrib, 3  ;border code
        call in_array        ;put character in array
        inc dl               ;move to left
        cmp dl, 79           ;end of row?
        jne nxtrow              
        mov dl, 1            ;start at 1
        cmp dh, 24           ;did we all ready do bottom of screen?
        je done1             ;yes, then we're finished
        mov dh, 24           ;no then start bottom of screen
        jmp nxtrow           ;do for dl = 1 to 79
done1:  restore_regs <dx, ax>
        ret
drawtab endp

init_cursor proc
;this procedure starts the cursor off in the bottom, middle of the screen
        save_regs <dx>
        mov dl, 39           ;middle of screen
        mov dh, 24           ;bottom row
        call setpos         
        mov player_row, dh   ;save the coordinates
        mov player_col, dl
        restore_regs <dx>
        ret
init_cursor endp

get_key proc
;this procedure gets the key that was pressed
        save_regs <ax>
        mov ah, 0
        int 16h           
        mov key_scan, ah     ;scan code for the key pressed
        cmp al, 1bh          ;esc?
        je ending
        cmp al, 0            ;al = 0?
        jne ending2          ;no then character key
        call which_arrow?   
        jmp ending2
ending:
        mov endgame, 1
ending2:
        restore_regs <ax>
        ret
get_key endp

which_arrow? proc
;this procedure will decipher what arrow keys are pressed
;input ah = scan code
        save_regs <ax, bx>
        mov ah, 3            ;get position 
        mov bh, 0            ;on page 0
        int 10h              ;dh = row, dl = col
        mov ah, key_scan     ;get scan code
        cmp ah, 72           ;up arrow?
        je get_cursor_up     ;yes
        cmp ah, 75           ;left arrow?
        je get_cursor_left   ;yes
        cmp ah, 77           ;right arrow?
        je get_cursor_right  ;yes
        cmp ah, 80           ;down arrow?
        je get_cursor_down   ;yes
        jmp arrow_done       ;not an arrow key
get_cursor_up:
     call cursor_up
     jmp arrow_done
get_cursor_down:
     call cursor_down
     jmp arrow_done
get_cursor_left:
     call cursor_left
     jmp arrow_done
get_cursor_right:
     call cursor_right
arrow_done:
     mov cantmove, 0         ;reset
     restore_regs <bx, ax>
     ret
which_arrow? endp
     
execute proc
     save_regs <ax, bx, dx>
     call setpos             ;set the cursor position
     mov array_attrib, 1     ;drawing box code
     call in_array
     mov box_array [di], dx  ;keep track of box you're drawing
     add di, 2               ;array is words
     add box_length, 1       ;accumulator 
     cmp box_length, 1       ;is this the first box?
     jne savedpos            ;no, then position all ready saved
     mov last_col, dl        ;save the first position for use later
     mov last_row, dh
savedpos:     
     mov al, box             ;print box character
     mov bl, 17h             ;blue on 
     call paint              ;paint the character
     restore_regs <dx, bx, ax>
     ret
execute endp

comment!
Here's some high-level thinking for this algorithm:
There are eight different cases for containing the ebola virus inside a
box you have drawn:
      2:   if it's a box & has a top_right hand corner 
           (a box in lower left corner of the screen)            
              check ball's position :
              if top_r_corn_row < ballrow AND top_r_cor_col > ballcol                then  VIRUS CONTAINED
                VIRUS CONTAINED
           else delete the box
I'm going to use less description in my high-level pseudocode for the
rest of these because they're pretty much all the same idea:

     4:   if box and top_left hand corner
             if top_l_corn_row < ballrow AND top_l_corn_col < ballcol
             VIRUS CONTAINED
     6:   if box and bot_right hand corner
             if bot_r_corn_row > ballrow AND bot_r_corn_col > ballcol
             VIRUS CONTAINED
     8:   if box and bot_left hand corner
             if bot_l_corn_row > ballrow AND bot_l_corn_col < ballcol
             VIRUS CONTAINED

Other cases:
     1:   if top_left hand corner AND top_right hand corner
           if corn_row < ballrow AND top_l_corn_col < ballcol 
           AND top_r_corn_col > ballcol
        VIRUS CONTAINED
     5:   if top_left hand corner and bot_left hand corner
           if corn_col < ballcol AND top_l_corn_row < ballrow
           AND bot_l_corn_row > ballrow
        VIRUS CONTAINED

     3:   if top_right hand corner AND bot_right hand corner
           if corn_col > ballcol AND top_r_corn_row < ballrow
           AND bot_r_corn_row > ballrow
        VIRUS CONTAINED

     7:   if bot_right hand corner AND bot_left hand corner
           if corn_row < ballrow AND bot_l_corn_col < ballcol
           AND bot_r_corn_col > ballcol
        VIRUS CONTAINED
end comment!

check_virus_contained proc
;we know we have made a box, now we check to see if it has the virus        

        save_regs <ax>
        mov al, -1              ;check to see if there is a corner
        cmp al, top_r_corn_col  ;checking for case #2
        je try_nudda            ;we get rid of possibility of cases 1,2,3
        cmp al, top_l_corn_col  ;check for case #1 
        jne case_1              ;we have case #1!
        cmp al, bot_r_corn_col  ;checking for case #3 now
        je case_2               ;well, at least we have case #2
        call box_on_left        ;we have case #3 
try_nudda:
        cmp al, top_l_corn_col  ;check for case #4
        je and_anudda           ;can't be case 4 or 5
        cmp al, bot_l_corn_col  ;checking for case #5
        jne case_5              ;we found case#5
        call box_in_bot_right   ;we have case #4
and_anudda:   
        cmp al, bot_r_corn_col  ;check for case #6
        je case_8               ;must be case #8
        cmp al, bot_l_corn_col      ;checking for case #7
        jne case_7              ;here's case #7
        call box_in_top_left    ;its gotta be case #6
        jmp exit_this   
case_1: 
        call box_on_bot
        jmp exit_this
case_2:
        call box_in_bot_left
        jmp exit_this
case_5:
        call box_on_right
        jmp exit_this
case_7:
        call box_on_top
        jmp exit_this
case_8:
        call box_in_top_right
        jmp exit_this

exit_this:     mov di, 0      ;reset box_array pointer
     mov box_length, 0        ;reset length of box
     restore_regs <ax>
     ret
check_virus_contained endp

box_on_top proc
;case #7
;if bot_r_corn_row < ballrow AND bot_l_corn_col < ballcol        
;AND bot_r_corn_col > ballcol then
;VIRUS CONTAINED
        save_regs <ax, bx>
        mov al, ballrow
        cmp al, bot_r_corn_row
        jb nopes7
        mov bl, ballcol
        cmp bl, bot_l_corn_col
        jb nopes7
        cmp bl, bot_r_corn_col
        ja nopes7
        call greenize
        jmp all_done7
nopes7:
        call delete_box
all_done7:
        restore_regs <bx, ax>
        ret
box_on_top endp

box_on_bot proc
;case #1
;if top_l_corn_row < ballrow AND top_l_corn_col < ballcol        
;AND top_r_corn_col > ballcol then
;VIRUS CONTAINED
        save_regs <ax, bx>
        mov al, ballrow
        cmp al, top_l_corn_row
        jb nopes
        mov bl, ballcol
        cmp bl, top_l_corn_col
        jb nopes
        cmp bl, top_r_corn_col
        ja nopes
        call greenize
        jmp all_done
nopes:
        call delete_box
all_done:
        restore_regs <bx, ax>
        ret
box_on_bot endp

box_on_left proc
;case #3
;if top_r_corn_col > ballcol AND top_r_corn_row < ballrow        
;AND bot_r_corn_row > ballrow then
;VIRUS CONTAINED
        save_regs <ax, bx>
        mov al, ballcol
        cmp al, top_r_corn_row        ;checking if ball is within
        ja nopes3                     ;the boundaries of the box
        mov bl, ballrow
        cmp bl, top_r_corn_row
        jb nopes3
        cmp bl, bot_r_corn_row
        ja nopes3
        call greenize                 ;fits all requirements
        jmp all_done3
nopes3:
        call delete_box
all_done3:
        restore_regs <bx, ax>
        ret
box_on_left endp

box_on_right proc
;case #5
;if top_l_corn_col < ballcol AND top_l_corn_row < ballrow        
;AND bot_l_corn_row > ballrow then
;VIRUS CONTAINED
        save_regs <ax, bx>
        mov al, ballcol
        cmp al, top_l_corn_col       ;checking if ball is within
        jb nopes5                     ;the boundaries of the box
        mov bl, ballrow
        cmp bl, top_l_corn_row
        jb nopes5
        cmp bl, bot_l_corn_row
        ja nopes5
        call greenize               ;fits all requirements
        jmp all_done5
nopes5:
        call delete_box
all_done5:
        restore_regs <bx, ax>
        ret
box_on_right endp

box_in_top_left proc
;case #6
;if bot_r_corn_row > ballrow AND bot_r_corn_col > ballcol then
;VIRUS CONTAINED
        save_regs <ax, bx>
        mov al, bot_r_corn_row
        cmp al, ballrow
        jb nopes6
        mov bl, bot_r_corn_col 
        cmp bl, ballcol
        jb nopes6
        call greenize
        jmp all_done6
nopes6:
        call delete_box
all_done6:
        restore_regs <ax>
        ret
box_in_top_left endp

box_in_top_right proc
;case #8
;if bot_l_corn_row > ballrow AND bot_l_corn_col < ballcol then
;VIRUS CONTAINED
        save_regs <ax, bx>
        mov al, bot_l_corn_row
        cmp al, ballrow
        jb nopes8
        mov bl, bot_l_corn_col 
        cmp bl, ballcol
        ja nopes8
        call greenize
        jmp all_done8
nopes8:
        call delete_box
all_done8:
        restore_regs <ax>
        ret
box_in_top_right endp

box_in_bot_left proc
;case #2
;if top_r_corn_row < ballrow AND top_r_corn_col > ballcol then
;VIRUS CONTAINED
        save_regs <ax, bx>
        mov al, top_r_corn_row
        cmp al, ballrow
        ja nopes2
        mov bl, top_r_corn_col
        cmp bl, ballcol
        jb nopes2
        call greenize
        jmp all_done2
nopes2:
        call delete_box
all_done2:
        restore_regs <bx, ax>
        ret
box_in_bot_left endp

box_in_bot_right proc
;case #4
;if top_l_corn_row < ballrow AND top_l_corn_col < ballcol then
;VIRUS CONTAINED
        save_regs <ax, bx>
        mov al, top_l_corn_row
        cmp al, ballrow
        ja nopes4
        mov bl, top_l_corn_col
        cmp bl, ballcol
        ja nopes4
        call greenize
        jmp all_done4
nopes4:
        call delete_box
all_done4:
        restore_regs <ax>
        ret
box_in_bot_right endp

exit proc
     save_regs <bx, dx>
     mov bl, direction       ;want to save current direction as old direction
     mov olddirection, bl    ;so we have it for next key pressed
     mov player_row, dh      ;save location
     mov player_col, dl
     restore_regs <dx, bx>
     ret
exit endp

;the following procedures all do the same thing, just it depends what
;cursor key is pressed.  Cursor_up is commented, but the same comments
;apply for all 4 procedures

cursor_up proc
;this procedure is called when the up cursor key is pressed
     save_regs <ax, bx, cx, dx>
     mov direction, 1        ;code for moving up
     mov al, 2               ;preparing to see if you were just going down (code for own = 2)
     cmp al, olddirection       
     je end_cursor_up        ;if we were just going down, can't go up
     
     MOV BL, 3               ;code for moving left
     CMP BL, OLDDIRECTION    ;see if we were coming from the right
     JE BOT_LEFT_CORNER      ;we have a bottom-left-corner
     MOV CL, 4               ;code for moving right
     CMP CL, OLDDIRECTION    ;see if we were just coming from the left
     JE BOT_RIGHT_CORNER     ;we have a bottom-left-corner
     JMP KEEP_IT_GOIN
BOT_LEFT_CORNER:
        MOV BOT_L_CORN_ROW, DH  ;save these corner's coordinates
        MOV BOT_L_CORN_COL, DL  
        JMP KEEP_IT_GOIN
BOT_RIGHT_CORNER:
        MOV BOT_R_CORN_ROW, DH
        MOV BOT_R_CORN_COL, DL

KEEP_IT_GOIN:
     call check_bound        ;check if we made a box
     cmp made_box, 1         ;yes we made a box
     je box_cursor_up
     cmp cantmove, 1         ;if we didnt make a box can we move there?
     je end_cursor_up
     cmp dh, 0               ;row 0?
     je end_cursor_up        ;yes, get out of this procedure and get next key
     dec dh                  ;row = row -1
     cmp dh, 0               ;are we on the border?
     ja execute_cursor_up    ;no then we can move to that position
     ;call setpos            ;yes, cant draw on border
     call exit
     jmp end_cursor_up
box_cursor_up:               ;we made a box, now check for virus
     call check_virus_contained
     call exit
     jmp end_cursor_up
execute_cursor_up:
     call execute
     call exit
end_cursor_up:
     restore_regs <dx, cx, bx, ax>
     ret
cursor_up endp


cursor_down proc
     save_regs <ax, bx, cx, dx>
     mov direction, 2        ;code for moving down
     mov al, 1
     cmp al, olddirection    ;can't move down if we were just moving up
     je end_cursor_down
     
     MOV BL, 3                 ;code for moving left
     CMP BL, OLDDIRECTION      ;see if we were coming from the right
     JE TOP_LEFT_CORNER2       ;we have a top-left-corner
     MOV CL, 4                 ;code for moving right
     CMP CL, OLDDIRECTION      ;see if we were just coming from the left
     JE TOP_RIGHT_CORNER2      ;we have a top-left-corner
     JMP KEEP_IT_GOIN2
TOP_LEFT_CORNER2:
        MOV TOP_L_CORN_ROW, DH ;save these corner's coordinates
        MOV TOP_L_CORN_COL, DL  
        JMP KEEP_IT_GOIN2
TOP_RIGHT_CORNER2:
        MOV TOP_R_CORN_ROW, DH
        MOV TOP_R_CORN_COL, DL

KEEP_IT_GOIN2:
     call check_bound
     cmp made_box, 1           ;yes we made a box
     je box_cursor_down
     cmp cantmove, 1
     je end_cursor_down
     cmp dh, 24                ;last row?
     je end_cursor_down        ;yes, get next key
     inc dh                    ;no, row = row + 1
     cmp dh, 24
     jb execute_cursor_down
     ;call setpos
     call exit        
     jmp end_cursor_down
box_cursor_down:               ;we made a box, now check for contained virus
     call check_virus_contained
     call exit
     jmp end_cursor_down
execute_cursor_down:
     call execute
     call exit
end_cursor_down:
     restore_regs <dx, cx, bx, ax>
     ret
cursor_down endp        


cursor_right proc
     save_regs <ax, bx, cx, dx> 
     mov direction, 4
     mov al, 3                 ;cant move right if we were just moving left
     cmp al, olddirection
     je end_cursor_right
     
     MOV BL, 1                 ;code for moving up
     CMP BL, OLDDIRECTION      ;see if we were just coming from the bottom 
     JE TOP_LEFT_CORNER3       ;we have a top-left-corner
     MOV CL, 2                 ;code for moving down
     CMP CL, OLDDIRECTION      ;see if we were just coming from the top
     JE BOT_LEFT_CORNER3       ;we have a bottom-left-corner
     JMP KEEP_IT_GOIN3
TOP_LEFT_CORNER3:
        MOV TOP_L_CORN_ROW, DH  ;save these corner's coordinates
        MOV TOP_L_CORN_COL, DL  
        JMP KEEP_IT_GOIN3
BOT_LEFT_CORNER3:
        MOV BOT_L_CORN_ROW, DH
        MOV BOT_L_CORN_COL, DL

KEEP_IT_GOIN3:
     call check_bound
     cmp made_box, 1
     je box_cursor_right
     cmp cantmove, 1
     je end_cursor_right
     cmp dl, 79                ;last column?
     je end_cursor_right       ;yes, get next key
     inc dl                    ;col = col + 1
     cmp dl, 79
     jb execute_cursor_right   ;end, get another key
     ;call setpos
     call exit
     jmp end_cursor_right
box_cursor_right:              ;we made a box, now check for virus inside
     call check_virus_contained
     call exit
     jmp end_cursor_right
execute_cursor_right:
     call execute
     call exit
end_cursor_right:
     restore_regs <dx, cx, bx, ax>
     ret
cursor_right endp

cursor_left proc
     save_regs <ax, bx, cx, dx>
     mov direction, 3
     mov al, 4                 ;cant move left if we were just moving right
     cmp al, olddirection
     je end_cursor_left
     
     MOV BL, 1                 ;code for moving up
     CMP BL, OLDDIRECTION      ;see if we were just coming from the bottom 
     JE TOP_RIGHT_CORNER4      ;we have a top-right-corner
     MOV CL, 2                 ;code for moving down
     CMP CL, OLDDIRECTION      ;see if we were just coming from the top
     JE BOT_RIGHT_CORNER4      ;we have a bottom-right-corner
     JMP KEEP_IT_GOIN4        
TOP_RIGHT_CORNER4:
        MOV TOP_R_CORN_ROW, DH ;save these corner's coordinates
        MOV TOP_R_CORN_COL, DL  
        JMP KEEP_IT_GOIN4
BOT_RIGHT_CORNER4:
        MOV BOT_R_CORN_ROW, DH
        MOV BOT_R_CORN_COL, DL

KEEP_IT_GOIN4:
     

     call check_bound
     cmp made_box, 1          ;YES WE MADE A BOX
     je box_cursor_left
     cmp cantmove, 1
     je end_cursor_left
     cmp dl, 0                ;column 0?
     je end_cursor_left       ;yes, get next key
     dec dl                   ;col = col - 1
     cmp dl, 0
     ja execute_cursor_left
     ;call setpos
     call exit
     jmp end_cursor_left
box_cursor_left:              ;we made a box now check for virus inside it
     call check_virus_contained
     call exit
     jmp end_cursor_left
execute_cursor_left:
     call execute
     call exit
end_cursor_left:
     restore_regs <dx, cx, bx, ax>
     ret
cursor_left endp

paint proc
;this procedure will paint the characters for the boxes       
   save_regs <ax, bx, cx>
   mov bh, 0                 ;page 0
   mov cx, 1                 ;1 character
   mov ah, 9                 ;subfunction to print
   int 10h
   restore_regs <cx, bx, ax>
   ret
paint endp


quitting proc
;returns to dos
        call its_sound
        call gameover
        call clear_screen
        mov ah, 4ch            ;return to dos
        int 21h
quitting endp


in_array proc
;this procedure puts the box in an array
;row = bx
;column = si
;input: array_attrib (1 = red box, 2 = blue, 3 = border)
        save_regs <ax, bx, cx, dx>
        mov ch, array_attrib       ;need to save this in array
        call find_array_index      ;will give us location
        mov si, array_index  
        mov rows [si], ch          ;save attribute
        restore_regs <dx, cx, bx, ax>
        ret
in_array endp

find_array_index proc
;this procedure find the array index.
;the index is the row * 80 + column
;output: array_index
        save_regs <ax, bx, cx, dx>
        mov colnum, dl            ;dl = column number
        mov dl, dh                ;dh = row number - put it in lower bit
        xor dh, dh                ;clear out higher bit
        mov al, maxcol                  
        mul dl                    ;row * 80
        xor bh, bh                ;clear out bh
        mov bl, colnum            ;bx = column number
        add ax, bx                ;ax = row * 80 + column
        mov array_index, ax       ;si is index
        ;mov rows [si], ch
        restore_regs <dx, cx, bx, ax>
        ret
find_array_index endp

check_bound proc
;this procedure checks to see if a wall has been hit. 
;returns in made_box 0 if no box was made, 1 if a box was made        
        save_regs <ax, bx, cx, dx>
        cmp direction, 2          ;2 = down
        je dn                     ;down
        jb upp                    ;up
        cmp direction, 4          ;4 = right
        je rght                   ;right
        jb lft                    ;left
dn:     inc dh                    ;go to next spot down
        call find_array_index     ;get index for this spot
        mov si, array_index
        mov al, rows [si]         ;get whats in this spot
        cmp al, 0                 ;0 = nothing's there
        je nobox
        cmp al, 1                 ;1 = blue/red box
        je cannotmove             ;cant move onto a blue/red box
        mov made_box, 1           ;if we get here we made a box!
        mov cantmove, 1    
        jmp finihere
upp:    dec dh                    ;go to next spot up
        call find_array_index     ;get index
        mov si, array_index
        mov al, rows [si]         ;get whats there
        cmp al, 0
        je nobox                  ;0 = nothing
        cmp al, 1                 ;1 = blue/smile box
        je cannotmove             ;cant move on a blue/smile box
        mov made_box, 1           ;get here must've made a box
        mov cantmove, 1            
        jmp finihere
rght:   inc dl                    ;go to next position to the right
        call find_array_index     ;get index
        mov si, array_index
        mov al, rows [si]         ;find out whats there
        cmp al, 0                 ;0 = nothing
        je nobox
        cmp al, 1                 ;1 = blue/smile box
        je cannotmove             ;cant move on a blue/smile box
        mov cantmove, 1            
        mov made_box, 1           ;must've made a box
        jmp finihere
lft:    dec dl                    ;go to next position to left
        call find_array_index     ;get index
        mov si, array_index
        mov al, rows [si]         ;get whats there
        cmp al, 0                 ;0 = nothing
        je nobox
        cmp al, 1                 ;1 = blue/smile box
        je cannotmove             ;cant move on blue/smile box
        mov made_box, 1
        mov cantmove, 1            
        jmp finihere
nobox:  mov made_box, 0           ;set flag
        jmp finihere
cannotmove:
        mov cantmove, 1           ;set flag
finihere:  restore_regs <dx, cx, bx, ax>
           ret
check_bound endp

greenize proc
;make the box green
        save_regs <ax, bx, cx, dx>
        lea di, box_array
        mov cl, box_length       ;counter
        cmp cl, 0                ;did you press same arrow key?
        je nogood                ;cant fill in a box of length 0
        cmp box_length, 50       ;if box length < 50 get 4 points
        ja onepoint
        inc boxes
        inc boxes
        inc boxes
        inc boxes
        jmp here
onepoint:
        cmp box_length, 80       ;box length > 80  get no points
        ja here
        inc boxes
here:   call scoring
        mov di, 0                ;start at beginning of box
another: 
        mov dx, box_array [di]   ;get coordinates of box
        call find_array_index    ;find index for that spot in rows array
        mov si, array_index
        mov rows [si], 2         ;green box code
        call setpos
        mov al, box              ;character to make boxes
        mov bl, greencol         ;color for boxes
        call paint           
        add di, 2                ;array is of word length
        sub cl, 1                ;dec counter
        cmp cl, 0                ;are we done?
        jne another
        cmp boxes, 10            ;you need a score of 10 points to win game
        jb nogood
        call winner
nogood: restore_regs <dx, cx, bx, ax>
        ret
greenize endp

Ball_Bounce proc
;this procedure will check to see if the ball has hit a boundary.
;if it has, it will 'bounce' the ball off the boundary
;input:
;output:  ballcol - column to display the ball
;         ballrow - row to diplay the ball

        save_regs <ax, bx, cx, dx>
;handle vertical first
checkagain:   
        mov dl, ballcol
        mov dh, ballrow         ;current position
        add dh, bvertdir        ;try to move vertically
        call check_spot
        cmp spotok, 0           ;0 = ok, 1 = spot full
        je checkTAB             ;check top and bottom
        inc pop_dead
        cmp pop_dead, 100
        ;call killing
        je good_bye
        call killing
        neg bvertdir            ;spot not ok, change vertical direction
        jmp checkagain          ;spot is full
checkTAB:
        mov dh, ballrow         ;to cancel out the old bvertdir from above
        add dl, bcoldir         ;try to mve horizontally
        call check_spot
        cmp spotok, 0
        je checkdiag
        inc pop_dead
        cmp pop_dead, 100
        je good_bye
        call killing
        neg bcoldir             ;spot not ok, change horizontal direction
        jmp checkagain
checkdiag:                      ;checks diagnol position
        add dh, bvertdir        ;dx now contains actual location to move
        call check_spot
        cmp spotok, 0
        je continue
        inc pop_dead
        cmp pop_dead, 100
        je good_bye
        call killing
        dec ballcol             ;change direction if position is no good
continue:       
        mov dh, ballrow         ;ensure dx holds correct position
        add dh, bvertdir
        mov dl, ballcol
        add dl, bcoldir
        mov ballrow, dh         ;save this position
        mov ballcol, dl
        jmp bounce_more
good_bye:
        mov u_dead, 1           ;the entire population is DEAD
        call quitting
bounce_more:
        restore_regs <dx, cx, bx, ax>
        ret
Ball_Bounce endp

Show_Ball proc
        save_regs <ax, bx, dx>
        call Ball_Bounce
        mov dh, old_ballr       ;clear position of the ball character
        mov dl, old_ballc
        call setpos
        mov al, box             ;ascii code for 'face'
        mov bl, 00              ;black on black
        call paint
        mov dh, ballrow
        mov dl, ballcol
        call setpos
        mov al, ballpict        ;ascii code for ball
        mov bl, ballcolor       ;yellow on black
        call paint
        mov old_ballr, dh       ;save these positions
        mov old_ballc, dl
        call restore_pos        ;put player back at their position
        restore_regs <ax, bx, dx>
        ret
Show_Ball endp

restore_pos proc       
;restore the players position on the screen
        push dx
        mov dh, player_row
        mov dl, player_col
        call setpos
        pop dx
        ret
restore_pos  endp

check_spot proc
;check if this spot for ball is occupied
;input dx = location of spot
;output spotok = 0 if empty 1 if full
        save_regs <ax, dx>
        call find_array_index
        mov si, array_index
        mov al, rows [si]
        cmp al, 0               ;nothings in that spot
        je yep
        mov spotok, 1           ;flag that ball can't move there
        jmp fini
        mov dl, last_col
        mov dh, last_row
        call setpos
yep:    mov spotok, 0           ;can move there
fini:   restore_regs <dx, ax> 
        ret
check_spot endp

is_key proc
;is there any input from keyboard?
        save_regs <ax>
        mov ah, 0bh
        int 21h
        mov yesss, al           ;0 if key 1 if not
        restore_regs <ax>
        ret
is_key endp

delete_box proc
;this procedure removes the boxes if you created a box and that didn't
;turn green because you did not contain the virus OR you didn't follow the
;specific instructions for making a box
        save_regs <ax, bx, cx, dx>
        xor ch, ch
        lea di, box_array
        mov cl, box_length      ;counter
        cmp cl, 0               ;did you press same arrow key?
        mov di, 0               ;start at beginning of box
anotherminibox: 
        mov dx, box_array [di]  ;get coordinates of box
        call find_array_index   ;find index for that spot in rows array
        mov si, array_index
        mov rows [si], 0        ;clear that spot
        call setpos
        mov al, box             ;character to make boxes
        mov bl, 00              ;black on black
        call paint           
        add di, 2               ;array is of word length
        sub cl, 1               ;dec counter
        cmp cl, 0               ;are we done?
        jne anotherminibox
        mov box_length, 0       ;have to start over
        mov di, 0
nogood1: restore_regs <dx, cx, bx, ax>
        ret
delete_box endp

delay proc
        save_regs <ax, bx>
        mov bx, speed
big:    mov ax, 0ffffh
lp:     sub ax, 1
        jnz lp
        sub bx, 1
        jnz big
        restore_regs <bx, ax>
        ret
delay endp

gameover proc
        save_regs <ax, bx, dx>
        mov dh, 10           ;row to put message on
        mov dl, 30           ;column to put message on
        mov bl, 84h          ;blinking red on black
        mov si, offset msg

dalp:   cmp byte ptr [si], 0
        je over
        mov al, [si]
        call float
        inc si
        inc dl
        jmp dalp
over:   mov ah, 0            ;wait for a key to be pressed
        int 16h
        cmp al, 1bh
        je out1 
        jmp over
out1:   restore_regs <dx, bx, ax> 
        ret
gameover endp

float proc
        save_regs <dx>
        mov speed, 1
        mov end_col, dl
        mov dl, 79
draw:   call setpos
        call save
        call paint
        call delay
        call restore_it
        dec dl
        cmp dl, end_col
        jae draw
        call paint
;now show score and population dead in same way
        restore_regs <dx>
        ret
float endp

save proc
        save_regs <ax, bx>
        mov bh, 0
        mov ah, 8
        int 10h
        mov screen_attr, ah
        mov screen_char, al
        restore_regs <bx, ax>
        ret
save endp

restore_it proc
        save_regs <ax, bx>
        mov al, screen_char
        mov bl, screen_attr
        call paint
        restore_regs <bx, ax>
        ret
restore_it endp

scoring proc
;output score
    save_regs <ax, dx>
    mov dx, 0002h
    call setpos
    mov al, 'S'
    call wchar
    mov al, 'C'
    call wchar
    mov al, 'O'
    call wchar
    mov al, 'R'
    call wchar
    mov al, 'E'
    call wchar
    mov al, ':'
    call wchar
    mov al, ' '
    call wchar
    mov ax, boxes
    call utxt1
    restore_regs <dx, ax>
    ret
scoring endp

killing proc
;output current pop_dead
    save_regs <ax, dx>
    mov dx, 00031h
    call setpos
    mov al, 'P'
    call wchar
    mov al, 'O'
    call wchar
    mov al, 'P'
    call wchar
    mov al, 'U'
    call wchar
    mov al, 'L'
    call wchar
    mov al, 'A'
    call wchar
    mov al, 'T'
    call wchar
    mov al, 'I'
    call wchar
    mov al, 'O'
    call wchar
    mov al, 'N'
    call wchar
    mov al, ' '
    call wchar
    mov al, 'D'
    call wchar
    mov al, 'E'
    call wchar
    mov al, 'A'
    call wchar
    mov al, 'D'
    call wchar
    mov al, ':'
    call wchar
    mov al, ' '
    call wchar
    mov ax, pop_dead
    call utxt1
    restore_regs <dx, ax>
    ret         
killing endp

wchar   proc
    save_regs <ax, bx, bp>
    sub bh, bh
    mov ah, 14
    int 10h
    restore_regs <bp, bx, ax>
    ret
wchar endp

utxt1 proc
    save_regs <cx, dx>
    mov cx, 10
    sub dx, dx
    div cx
    or ax, ax
    jz utxt2
    call utxt1
utxt2:
    mov al, dl
    or al, '0'
    call wchar
    restore_regs <dx, cx>
    ret
utxt1  endp

winner proc
    save_regs <ax, bx, dx>
    mov u_dead, 0
    mov dh, 15   
    mov dl, 29
    mov bl, 90h
    mov si, offset won_prompt
alp:
    cmp byte ptr [si], 0
    je overhere
    mov al, [si]
    call float
    inc si
    inc dl
    jmp alp
overhere:
    call quitting
    restore_regs <dx, bx, ax>
    ret
winner endp
        
its_sound proc
        cmp u_dead, 1
        jne winner2
        lea  si, loser_song       ;you LOST! play  'Taps'
        call  tune
        jmp over7
winner2:        
        lea si, winner_song       ;you WON! play 'Camptown Races'
        call tune
over7:        ret
its_sound endp

;Routine to play tune on speaker
;
;        (SI)                Pointer to tune list
;        CALL TUNE
;
;  The tune list is a series of word pairs. The first word is the
;  duration in units of 1/100th of a second, and the second word
;  is the frequency. An entry with zero duration ends the tune list
;  and a zero frequency is a rest (period of silence).
;
TUNE     PROC
         PUSH  AX            ; save registers
         PUSH  DX
;
;  Loop through notes of the tune
;
TUN1:    LODSW               ; load duration
         OR    AX,AX         ; test zero duration ending the list
         JZ    TUN2          ; if so, end of tune
;
;  Play next note
;
         XCHG  AX,DX         ; put duration in DX
         LODSW               ; load frequency
         CALL  NOTE          ; play the note
         JMP   TUN1          ; and loop back
;
;  Here at end of tune
;
TUN2:    POP   DX            ; restore registers
         POP   AX
         RET                 ; return to caller
TUNE     ENDP
;
;  Routine to play note on speaker
;
;        (AX)            Frequency in Hz (32 - 32000)
;        (DX)            Duration in units of 1/100 second
;        CALL  NOTE
;
;  Note: a frequency of zero, means rest (silence) for the indicated
;  time, allowing this routine to be used simply as a timing delay.
;
;  Definitions for timer gate control
;
CTRL     EQU   61H           ; timer gate control port
TIMR     EQU   00000001B     ; bit to turn timer on
SPKR     EQU   00000010B     ; bit to turn speaker on
;
;  Definitions of input/output ports to access timer chip
;
TCTL     EQU   043H          ; port for timer control
TCTR     EQU   042H          ; port for timer count values
;
;  Definitions of timer control values (to send to control port)
;
TSQW     EQU   10110110B     ; timer 2, 2 bytes, sq wave, binary
LATCH    EQU   10000000B     ; latch timer 2
;
;  Define 32 bit value used to set timer frequency
;
FRHI     EQU   0012H         ; timer frequency high (1193180 / 256)
FRLO     EQU   34DCH         ; timer low (1193180 mod 256)
;
NOTE     PROC
         PUSH  AX            ; save registers
         PUSH  BX
         PUSH  CX
         PUSH  DX
         PUSH  SI
         MOV   BX,AX         ; save frequency in BX
         MOV   CX,DX         ; save duration in CX
;
;  We handle the rest (silence) case by using an arbitrary frequency to
;  program the clock so that the normal approach for getting the right
;  delay functions, but we will leave the speaker off in this case.
;
         MOV   SI,BX         ; copy frequency to BX
         OR    BX,BX         ; test zero frequency (rest)
         JNZ   NOT1          ; jump if not
         MOV   BX,256        ; else reset to arbitrary non-zero
;
;  Initialize timer and set desired frequency
;
NOT1:    MOV   AL,TSQW       ; set timer 2 in square wave mode
         OUT   TCTL,AL
         MOV   DX,FRHI       ; set DX:AX = 1193180 decimal
         MOV   AX,FRLO       ;   = clock frequency
         DIV   BX            ; divide by desired frequency
         OUT   TCTR,AL       ; output low order of divisor
         MOV   AL,AH         ; output high order of divisor
         OUT   TCTR,AL
;
;  Turn the timer on, and also the speaker (unless frequency 0 = rest)
;
         IN    AL,CTRL       ; read current contents of control port
         OR    AL,TIMR       ; turn timer on
         OR    SI,SI         ; test zero frequency
         JZ    NOT2          ; skip if so (leave speaker off)
         OR    AL,SPKR       ; else turn speaker on as well
;
;  Compute number of clock cycles required at this frequency
;
NOT2:    OUT   CTRL,AL       ; rewrite control port
         XCHG  AX,BX         ; frequency to AX
         MUL   CX            ; frequency times secs/100 to DX:AX
         MOV   CX,100        ; divide by 100 to get number of beats
         DIV   CX
         SHL   AX,1          ; times 2 because two clocks/beat
         XCHG  AX,CX         ; count of clock cycles to CX
;
;  Loop through clock cycles
;
NOT3:    CALL  RCTR          ; read initial count
;
;  Loop to wait for clock count to get reset. The count goes from the
;  value we set down to 0, and then is reset back to the set value
;
NOT4:    MOV   DX,AX         ; save previous count in DX
         CALL  RCTR          ; read count again
         CMP   AX,DX         ; compare new count : old count
         JB    NOT4          ; loop if new count is lower
         LOOP  NOT3          ; else reset, count down cycles
;
;  Wait is complete, so turn off clock and return
;
         IN    AL,CTRL       ; read current contents of port
         AND   AL,0FFH-TIMR-SPKR ; reset timer/speaker control bits
         OUT   CTRL,AL       ; rewrite control port
         POP   SI            ; restore registers
         POP   DX
         POP   CX
         POP   BX
         POP   AX
         RET                 ; return to caller
NOTE     ENDP
;
;  Routine to read count, returns current timer 2 count in AX
;
RCTR     PROC
         MOV   AL,LATCH      ; latch the counter
         OUT   TCTL,AL       ; latch counter
         IN    AL,TCTR       ; read lsb of count
         MOV   AH,AL
         IN    AL,TCTR       ; read msb of count
         XCHG  AH,AL         ; count is in AX
         RET                 ; return to caller
RCTR     ENDP


end main
