;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 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 ret welcome endp set_speed proc save_regs ;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 ;macro call ret set_speed endp clear_screen proc save_regs 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 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 mov bh, 0 ;set cursor position mov ah, 2 int 10h restore_regs ret setpos endp draw_screen proc ;this procedure will draw the screen for the game save_regs 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 ret draw_screen endp drawborder proc ;this procedure will draw the borders for the screen save_regs 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 ret drawborder endp drawtab proc ;this procedure draws the top and bottom of the screen save_regs 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 ret drawtab endp init_cursor proc ;this procedure starts the cursor off in the bottom, middle of the screen save_regs 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 ret init_cursor endp get_key proc ;this procedure gets the key that was pressed save_regs 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 ret get_key endp which_arrow? proc ;this procedure will decipher what arrow keys are pressed ;input ah = scan code save_regs 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 ret which_arrow? endp execute proc save_regs 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 ret box_in_bot_right endp exit proc save_regs 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 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 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 ret cursor_up endp cursor_down proc save_regs 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 ret cursor_down endp cursor_right proc save_regs 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 ret cursor_right endp cursor_left proc save_regs 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 ret cursor_left endp paint proc ;this procedure will paint the characters for the boxes save_regs mov bh, 0 ;page 0 mov cx, 1 ;1 character mov ah, 9 ;subfunction to print int 10h restore_regs 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 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 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 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 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 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 ret check_bound endp greenize proc ;make the box green save_regs 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 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 ;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 ret Ball_Bounce endp Show_Ball proc save_regs 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 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 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 ret check_spot endp is_key proc ;is there any input from keyboard? save_regs mov ah, 0bh int 21h mov yesss, al ;0 if key 1 if not restore_regs 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 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 ret delete_box endp delay proc save_regs mov bx, speed big: mov ax, 0ffffh lp: sub ax, 1 jnz lp sub bx, 1 jnz big restore_regs ret delay endp gameover proc save_regs 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 ret gameover endp float proc save_regs 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 ret float endp save proc save_regs mov bh, 0 mov ah, 8 int 10h mov screen_attr, ah mov screen_char, al restore_regs ret save endp restore_it proc save_regs mov al, screen_char mov bl, screen_attr call paint restore_regs ret restore_it endp scoring proc ;output score save_regs 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 ret scoring endp killing proc ;output current pop_dead save_regs 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 ret killing endp wchar proc save_regs sub bh, bh mov ah, 14 int 10h restore_regs ret wchar endp utxt1 proc save_regs 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 ret utxt1 endp winner proc save_regs 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 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