| | |
Space Invaders
Please support our Assembly advertiser: Programming Forums - DaniWeb Sister Site
This is a program I wrote for my x86 assembly class which is basically the Spade Invaders game in all its glory. It uses Irvine32.inc which came with the textbook.
title SpaceInvaders (spacegame.asm) ; Dani Horowitz ; CSC111 x86 Assembly Programming INCLUDE Irvine32.inc .data ; begin data segment use_default byte 0 ; don't use user variables? ; ##### USER VARIABLES ##### ; ## used if use_default=1 # ; ########################## dodge_bullets byte 1 ; boolean for whether invaders can dodge bullets invader_count dword 20 ; number of invaders wanted (min 14) bottom_row byte 20 ; screen height (console vs fullscreen) ; ########################## ; ########################## invader_char = 2 ; character symbol to represent player_char = 1 ; character symbol to represent bullet_char = 30 ; character symbol to represent invader_color = red player_color = lightCyan bullet_color = yellow default_color = white ; ########################## invader_steps byte 1 ; number of rows and columns invaders move at a time game_running byte 0 ; boolean value for whether game is running or not ; ########################## bullet_row byte 0 ; current location of bullet (dh) bullet_col byte 0 ; current location of bullet (dl) invader_col byte 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70 invader_row byte 2, 5, 8, 5, 2, 5, 8, 5, 2, 5, 8, 5, 2, 5 current_loc byte ? ; player's current column (dl) location direction byte ? ; player's left/right direction inv_direction byte 0 ; current direction of invaders ; ########################## time dword 0 ; iterations of the game loop (loop counter) delay_time dword 70 ; length of pause time points dword 0 ; invaders killed counter dword 0 ; invaders that escaped number_invaders dword 14 ; current number of invaders icounter dword 0 ; invader loop counter score sdword 0 ; ########################## msg_bottom byte 21 ; bottom location for message (dh) msg_left = 2 ; left location for message (dl) msg_right = 65 ; right location for score (dl) ; ########################## stdInHandle dword ? ; keyboard input handler buffer byte 80 DUP(?) ; keyboard input buffer bug_fix_counter byte 0 ; ################################################## .code ; begin code segment ; ################################################## ; ################################################## INIT_KEYBOARD PROC ; get handle to standard keyboard input ; populate the stdInHandle variable ; ################################################## ; abstraction is a wonderful, glorious thing! INVOKE GetStdHandle, STD_INPUT_HANDLE mov stdInHandle, eax ret ; ################################################## INIT_KEYBOARD ENDP ; ################################################## ; ################################################## KYBD_HANDLER PROC ; set console flags to appropriate mode to read a single character ; determine if a new character is waiting in the buffer ; if yes, read it in and call the KEY_LISTENER procedure ; reset console flags to their previous values ; bug: all keypresses are executed twice ?? ; ################################################## .data saveFlags dword ? ; default console flags bytesRead dword ? ; number of bytes in buffer .code INVOKE GetConsoleMode, stdInhandle, saveFlags INVOKE SetConsoleMode, stdInHandle, 0 ; ---------- ; peek in buffer and see if anything new is there INVOKE PeekConsoleInput, stdInHandle, ADDR buffer, 1, ADDR bytesRead ; if no, skip to the end cmp bytesRead, 1 jne Done ; ---------- ; if a keypress is waiting in the buffer, ; put its value into al and call KEY_LISTENER ; to do something with it INVOKE ReadConsoleInput, stdInHandle, ADDR buffer, 1, ADDR bytesRead xor eax, eax mov esi, OFFSET buffer add esi, 14 mov al, [esi] call KEY_LISTENER Done: INVOKE SetConsoleMode, stdInHandle, saveFlags ret ; ################################################## KYBD_HANDLER ENDP ; ################################################## ; ################################################## PRINT_BLANK PROC ; assume dh and dl have already been set ; position cursor ; print a blank space to the screen ; ################################################## mov al, ' ' call Gotoxy call WriteChar ret ; ################################################## PRINT_BLANK ENDP ; ################################################## ; ################################################## PRINT_INVADER PROC ; assume dh and dl have already been set ; position cursor ; set cursor color to the invader color ; print the invader character to the screen ; reset cursor color to the default color ; ################################################## call Gotoxy mov eax, invader_color call SetTextColor mov al, invader_char call WriteChar mov eax, default_color call SetTextColor ret ; ################################################## PRINT_INVADER ENDP ; ################################################## ; ################################################## PRINT_PLAYER PROC ; assume dh and dl have already been set ; position cursor ; set cursor color to the player color ; print the player character to the screen ; reset cursor color to the default color ; ################################################## call Gotoxy mov eax, player_color call SetTextColor mov al, player_char call WriteChar mov eax, default_color call SetTextColor ret ; ################################################## PRINT_PLAYER ENDP ; ################################################## ; ################################################## PRINT_BULLET PROC ; assume dh and dl have already been set ; position cursor ; set cursor color to the bullet color ; print the bullet character to the screen ; reset cursor color to the default color ; ################################################## call Gotoxy mov eax, bullet_color call SetTextColor mov al, bullet_char call WriteChar mov eax, default_color call SetTextColor ret ; ################################################## PRINT_BULLET ENDP ; ################################################## ; ################################################## INIT_GAME PROC ; determine if using default variables ; if not using defaults, call INIT_VARS procedure to prompt for them ; screen print that the game is starting ; call INIT_KEYBOARD procedure to set up the keyboard console input handler ; call CREATE_OBJS procedure to print the player and invaders on the screen ; call RUN_GAME procedure to run the game loop ; return to main when finished ; ################################################## .data msg byte "Game starting ...", 0 msg_howto_left byte "F to move left ", 0 msg_howto_right byte "J to move right", 0 msg_howto_fire byte "B to fire ", 0 msg_howto_quit byte "E to quit game ", 0 .code mov eax, default_color ; set cursor to use default text color call SetTextColor .IF use_default == 0 ; use user-defined variables? call INIT_VARS ; initialize global variables call DumpRegs ; pretty print out what is in the registers .ENDIF mov edx, OFFSET msg call WriteString ; print "Game starting" call Crlf ; give user directions how to play mov edx, OFFSET msg_howto_left call WriteString call Crlf mov edx, OFFSET msg_howto_right call WriteString call Crlf mov edx, OFFSET msg_howto_fire call WriteString call Crlf mov edx, OFFSET msg_howto_quit call WriteString call Crlf mov eax, 500 call Delay ; pause call WaitMsg call Clrscr call INIT_KEYBOARD ; set up keyboard handler to get input call CREATE_OBJS ; create visual objects call RUN_GAME ; run the game ret ; game over ; ################################################## INIT_GAME ENDP ; ################################################## ; ################################################## INIT_VARS PROC ; this is only invoked if use_default=0 ; prompt for and populate user-defined value for dodge_bullets ; prompt for and populate user-defined value for invader_count ; prompt for and populate user-defined value for bottom_row ; use bottom_row value to also set the msg_bottom variable ; return to INIT_GAME when finished ; ################################################## .data promptA byte "Allow invaders to dodge bullets? (0=no 1=yes) ", 0 promptB byte "Maximum number of invaders? (minimum 14) ", 0 promptC byte "How many screen rows for your monitor? (20 or 45) ", 0 .code PromptForA: mov edx, OFFSET promptA ; prompt for dodge_bullets call WriteString call ReadInt ; assume user enters a small value mov dodge_bullets, al ; and prey this doesn't break .IF al!=0 && al!=1 jmp PromptForA .ENDIF ; ---------- PromptForB: mov edx, OFFSET promptB ; prompt for # of invaders call WriteString call ReadInt mov invader_count, eax cmp eax, 14 jl PromptForB ; ---------- PromptForC: mov edx, OFFSET promptC ; prompt for screen resolution call WriteString call ReadInt mov bottom_row, al .IF al!=20 && al!=45 jmp PromptForC .ENDIF inc al mov msg_bottom, al ret ; ################################################## INIT_VARS ENDP ; ################################################## ; ################################################## RUN_GAME PROC ; expects a bunch of stuff to already be populated ; continuously runs game loop until all invaders accounted for ; by either having been killed or escaped ; call KYBD_HANDLER procedure to check for a key press ; move existing bullet up ; move a random invader down ; when appropriate, call the END_GAME procedure ; return to INIT_GAME when finished ; ################################################## .data msg_run_game byte "Game started ", 0 .code ; ############################## mov dh, msg_bottom mov dl, msg_left call Gotoxy mov edx, OFFSET msg_run_game call WriteString ; ############################## call Randomize ; randomize seed mov game_running, 1 ; set game_running flag on Step: mov eax, delay_time ; pause .IF delay_time<20 mov delay_time, 20 .ENDIF call Delay mov dh, 0 mov dl, 0 call Gotoxy ; position cursor at top left of screen mov eax, counter add eax, points cmp eax, number_invaders ; loop if not all invaders accounted for jge GameOver call KYBD_HANDLER ; continously run call MOVE_BULLET ; step bullet, if exists, up call MOVE_INVADERS ; step invaders down call PRINT_SCORE inc time ; increment game time cmp game_running, 0 ; game over if game_running flag switched to off je GameOver jmp Step GameOver: call END_GAME ; end game ret ; ################################################## RUN_GAME ENDP ; ################################################## ; ################################################## END_GAME PROC ; assumes points has been populated with # of invaders killed ; assumes counter has been populated with # of invaders escaped ; assumes time has been populated with # of game loop iterations ; clears the screen ; pretty prints points, counter, time variables to the screen ; return to end of RUN_GAME when finished ; ################################################## .data msg_points byte "Invader Death Counter: ",0 msg_counter byte "Invaders That Escaped: ",0 msg_time byte "Game length: ", 0 .code call Clrscr ; ---------- mov dh, 10 mov dl, 10 call Gotoxy mov edx, OFFSET msg_points ; print points call WriteString mov eax, points call WriteDec ; ---------- mov dh, 15 mov dl, 10 call Gotoxy mov edx, OFFSET msg_counter ; print counter call WriteString mov eax, counter call WriteDec ; ---------- mov dh, 20 mov dl, 10 call Gotoxy mov edx, OFFSET msg_time ; print timer call WriteString mov eax, time call WriteDec ; ---------- mov dh, 30 mov dl, 10 call Gotoxy ret ; ################################################## END_GAME ENDP ; ################################################## ; ################################################## PRINT_SCORE PROC ; pretty print current game score at bottom right of screen ; ################################################## .data msg_score byte "Score: ", 0 msg_speed byte "Game Speed: ", 0 .code mov dh, msg_bottom mov dl, msg_right call Gotoxy ; position cursor mov edx, OFFSET msg_score call WriteString ; write "Score: " mov eax, score call WriteInt ; write the value ; ---------- mov dh, msg_bottom inc dh mov dl, msg_left call Gotoxy ; position cursor mov edx, OFFSET msg_speed ; write "Game Speed: " call WriteString mov eax, 70 sub eax, delay_time call WriteDec ; write the value ret ; ################################################## PRINT_SCORE ENDP ; ################################################## ; ################################################## CREATE_OBJS PROC ; self-explanatory ; return to INIT_GAME when finished ; ################################################## call Clrscr call CREATE_PLAYER call CREATE_INVADERS ret ; ################################################## CREATE_OBJS ENDP ; ################################################## ; ################################################## CREATE_PLAYER PROC ; draw player at bottom center screen location ; populate player coordinate to correct value ; return to CREATE_OBJS when finished ; ################################################## mov dh, bottom_row ; row current_bottom dec dh mov dl, 40 ; column 40 call PRINT_PLAYER mov current_loc, 40 ; set current_loc variable ret ; ################################################## CREATE_PLAYER ENDP ; ################################################## ; ################################################## CREATE_INVADERS PROC ; traverse a loop to draw 14 invaders at starting locations ; starting locations are read from byte arrays ; return to CREATE_OBJS when finished ; ################################################## mov icounter, 0 ; initialize loop PrintInvader: mov esi, OFFSET invader_col ; get col add esi, icounter mov dl, [esi] ; set column value mov esi, OFFSET invader_row ; get row add esi, icounter mov dh, [esi] ; set row value call PRINT_INVADER ; print invader to the screen inc icounter ; increment loop counter cmp icounter, 14 ; did we do all 14 invaders? jl PrintInvader ; if not, do next invader ret ; ################################################## CREATE_INVADERS ENDP ; ################################################## ; ################################################## MOVE_PLAYER PROC ; invoked from within KEY_LISTENER ; assumes a keyboard char to move the player has been pressed ; prints empty character over player's current location ; moves player's current location variable one space left or right ; prints player character to screen at the new location ; doesn't allow player to move out of bounds ; return to KEY_LISTENER when finished ; ################################################## .data temp_dl byte ? temp_dh byte ? msg_move_player_left byte "Moving left ", 0 msg_move_player_right byte "Moving right ", 0 .code inc bug_fix_counter ; bug workaround!! .IF bug_fix_counter==2 mov bug_fix_counter, 0 jmp Done .ENDIF ; end bug fix :( mov dh, bottom_row ; erase from old location dec dh mov dl, current_loc call PRINT_BLANK mov temp_dh, dh mov temp_dl, dl ; ---------- .IF direction == 'j' ; determine direction cmp dl, 70 jge RightBound ; handle out of bounds inc temp_dl ; increment dl ; ############################## mov dh, msg_bottom mov dl, msg_left call Gotoxy mov edx, OFFSET msg_move_player_right call WriteString ; ############################## .ELSE cmp dl, 5 jle LeftBound ; handle out of bounds dec temp_dl ; decrement dl ; ############################## mov dh, msg_bottom mov dl, msg_left call Gotoxy mov edx, OFFSET msg_move_player_left call WriteString ; ############################## .ENDIF jmp Move LeftBound: mov dl, 5 jmp Move RightBound: mov dl, 70 Move: mov dl, temp_dl mov dh, temp_dh mov current_loc, dl ; position cursor at new location call PRINT_PLAYER ; print the player all pretty-like Done: ret ; ################################################## MOVE_PLAYER ENDP ; ################################################## ; ################################################## KEY_LISTENER PROC ; assumes al is populated with a character pressed from the keyboard ; initiates FIRE procedure if 'b' key pressed ; sets game_running flag to off if 'e' key is pressed ; otherwise, initiates MOVE_PLAYER procedure ; returns to KYBD_HANDLER when finished ; ################################################## mov direction, al ; stick it in direction .IF direction == 'b' ; if entered 'b' fire bullet call FIRE .ELSEIF direction == 'e' mov game_running, 0 .ELSE call MOVE_PLAYER ; else, move player .ENDIF Done: ret ; ################################################## KEY_LISTENER ENDP ; ################################################## ; ################################################## FIRE PROC ; assumes 'b' key has been pressed to fire a bullet ; determine starting location of bullet ; draw bullet to screen if another bullet isn't in progress ; return to KEY_LISTENER when finished ; ################################################## .data msg_fire byte "Bullet fired ", 0 msg_fire_inprogress byte "Cannot Fire ", 0 .code ; ############################## mov dh, msg_bottom mov dl, msg_left call Gotoxy mov edx, OFFSET msg_fire call WriteString ; ############################## cmp bullet_col, 0 ; is a bullet currently in progress? jne ExistingBullet ; if yes, skip down and do nothing mov dh, bottom_row sub dh, 2 mov bullet_row, dh ; position bullet's starting location mov al, current_loc ; from player's current location mov bullet_col, al mov dh, bullet_row ; set bullet location mov dl, bullet_col call PRINT_BULLET ; print bullet to screen sub score, 50 jmp Done ExistingBullet: ; ############################## mov dh, msg_bottom ; only one bullet allowed in the air at a time mov dl, msg_left call Gotoxy mov edx, OFFSET msg_fire_inprogress call WriteString ; ############################## Done: ret ; ################################################## FIRE ENDP ; ################################################## ; ################################################## MOVE_BULLET PROC ; determine if there are any bullets in progress ; if yes, move bullet up ; update bullet location ; erase bullet from old location and draw to new location ; initiate procedure to determine if bullet hit an invader ; if bullet reached top of screen, update variables to ; indicate no bullets are in progress ; return to loop in RUN_GAME when finished ; ################################################## cmp bullet_row, 0 ; determine if bullet is in progress je NoBullets ; if no, there is nothing to move mov dh, bullet_row mov dl, bullet_col call PRINT_BLANK ; write empty string in current location mov al, invader_steps ; move bullet_row up sub bullet_row, al mov dh, bullet_row call PRINT_BULLET call HIT_INVADER ; see if we hit any invaders .IF dodge_bullets==1 call SHIFT_INVADERS ; shift invaders to the side .ENDIF ; ---------- cmp bullet_row, 1 ; see if we reached the top of the screen jle HideBullet jmp NoBullets HideBullet: mov bullet_row, 0 ; set value to default (no bullets in progress) mov bullet_col, 0 ; set value to default (no bullets in progress) sub score, 100 NoBullets: ret ; ################################################## MOVE_BULLET ENDP ; ################################################## ; ################################################## HIT_INVADER PROC ; traverse through each column an invader is in ; determine if bullet is in the same column ; if yes, check to see if rows match too ; if everything matches, invoke KILL_INVADER procedure ; return to MOVE_BULLET when finished ; ################################################## mov icounter, 0 CheckCol: mov esi, OFFSET invader_col ; find the invader column array add esi, icounter mov dl, [esi] ; get column location for invader cmp bullet_col, dl ; compare to column location for bullet je CheckRow ; if a match, check the row inc icounter cmp icounter, 14 ; loop for all invaders jl CheckCol jmp Done CheckRow: mov esi, OFFSET invader_row ; find the invader row array add esi, icounter mov dh, [esi] ; get row location for invader mov dl, bullet_row ; get row location for bullet sub dl, dh ; difference in location .IF dl<=invader_steps && dl>=0 && dh!=bottom_row call KILL_INVADER ; if we got here, he's dead .ENDIF Done: ret ; ################################################## HIT_INVADER ENDP ; ################################################## ; ################################################## KILL_INVADER PROC ; delete bullet from screen ; draw an X where invader was killed ; pause, and then draw a blank space ; (need to handle this separately in case going at different speeds) ; move bullet location to default (none in progress) ; set invader row to bottom_row ; if we want another invader, create it! ; increase points value ; return to HIT_INVADER when finished ; ################################################## .data msg_kill_invader byte "Invader Killed ", 0 .code ; ############################## mov dh, msg_bottom mov dl, msg_left call Gotoxy mov edx, OFFSET msg_kill_invader call WriteString ; ############################## add score, 500 mov dh, bullet_row mov dl, bullet_col call PRINT_BLANK ; delete bullet from screen ; ---------- mov esi, OFFSET invader_row ; find the invader row array add esi, icounter mov dh, [esi] mov esi, OFFSET invader_col ; find the invader col array add esi, icounter mov dl, [esi] ; ---------- mov al, 'X' ; X marks the spot call Gotoxy call WriteChar ; draw an X really quickly mov eax, 100 call Delay call PRINT_BLANK ; pause, and then draw a blank space over the X ; ---------- mov bullet_col, 0 ; move bullet column location to default mov bullet_row, 0 ; move bullet row location to default mov esi, OFFSET invader_row ; find the invader row array add esi, icounter mov dh, bottom_row mov eax, invader_count .IF number_invaders < eax ; do we want more invaders? mov dh, 2 ; if yes, create a new one inc number_invaders .ENDIF mov [esi], dh ; set invader row to bottom_row inc points ; increase points value sub delay_time, 2 ; game runs faster as it progresses ret ; ################################################## KILL_INVADER ENDP ; ################################################## ; ################################################## MOVE_INVADERS PROC ; find a random invader ; if the invader is already gone, choose another one ; move the chosen invader down ; if invader reached the bottom, delete the invader ; if we want another invader, create it! ; return to loop in RUN_GAME when finished ; ################################################## .data prevent_loop byte 0 ; just in case ... .code mov prevent_loop, 0 ChooseInvader: ; prevent an infinite loop if inc prevent_loop ; no invaders are available cmp prevent_loop, 14 jg Done ; ---------- mov eax, 14 ; generate random number between 0-13 call RandomRange mov icounter, eax ; put random number into icounter PrintInvader: mov esi, OFFSET invader_col ; get invader row array add esi, icounter mov dl, [esi] ; set dl to current column of icounter invader mov esi, OFFSET invader_row ; get invader column array add esi, icounter mov dh, [esi] ; set dh to current row of icounter invader ; ---------- cmp dh, bottom_row ; if invader is gone already, choose another invader jge ChooseInvader ; ---------- call PRINT_BLANK ; delete invader from current location ; ---------- add dh, invader_steps mov al, bottom_row dec al cmp dh, al ; did we reach bottom? jge HideInvader ; if yes, delete invader call PRINT_INVADER Continue: mov [esi], dh ; update invader array jmp Done HideInvader: sub score, 100 mov dh, bottom_row ; position below player inc counter ; add to invaders that escaped counter call PRINT_BLANK mov eax, invader_count .IF number_invaders < eax ; do we want more invaders? mov dh, 2 ; if yes, create a new one inc number_invaders .ENDIF jmp Continue Done: ret ; ################################################## MOVE_INVADERS ENDP ; ################################################## ; ################################################## SHIFT_INVADERS PROC ; only when a bullet is in the air: ; every few iterations, move all invaders right or left ; don't let invaders go out of bounds ; return to MOVE_BULLET when finished ; ################################################## mov icounter, 0 ShiftInvaders: mov esi, OFFSET invader_row ; find the invader row array add esi, icounter mov dh, [esi] ; get row location for invader mov esi, OFFSET invader_col ; find the invader column array add esi, icounter mov dl, [esi] ; get column location for invader cmp dh, bottom_row jge NextInvader ; ---------- call PRINT_BLANK ; write a blank space to current location ; ---------- .IF inv_direction == 5 ; wobble back and forth add dl, invader_steps .ELSEIF inv_direction == 10 sub dl, invader_steps .ENDIF .IF dl < 5 ; don't let invaders go out of bounds mov dl, 5 .ELSEIF dl > 70 mov dl, 70 .ENDIF mov [esi], dl ; ---------- mov esi, OFFSET invader_row ; get invader row array add esi, icounter mov dh, [esi] ; set dh to current row of icounter invader mov esi, OFFSET invader_col ; get invader column array add esi, icounter mov dl, [esi] ; set dh to current column of icounter invader call PRINT_INVADER ; ---------- NextInvader: inc icounter cmp icounter, 14 ; loop for all invaders jl ShiftInvaders .IF inv_direction < 15 ; don't wobble every time inc inv_direction .ELSE mov inv_direction, 0 .ENDIF ret ; ################################################## SHIFT_INVADERS ENDP ; ################################################## ;--------------------------------------------------- main proc ; our glorious starting point ;--------------------------------------------------- call Clrscr ; clear the screen call INIT_GAME ; initialize game variables and start game call WaitMsg exit ; game over! main endp end main ;---------------------------------------------------
0
•
•
•
•
Looks nice , but ..I cannot translate nor test
1/ What is that Irvine 32 ?
2/ What is the assembler ?
I personally use RosAsm,It is very user friendly (when past the querks) (original : It contains its source in the PE file)
I would like , as an exercise , to translate to RosAsm
I have plenty stuff in RosAsm , simple to complicated . (including up to FFT's )
gsbr
1/ What is that Irvine 32 ?
2/ What is the assembler ?
I personally use RosAsm,It is very user friendly (when past the querks) (original : It contains its source in the PE file)
I would like , as an exercise , to translate to RosAsm
I have plenty stuff in RosAsm , simple to complicated . (including up to FFT's )
gsbr
Similar Threads
- Space Invaders (Assembly)
- Starting Game Dev With Space Invaders (Game Development)
- Space Invaders game (C# & OpenGL) (Game Development)
- Space Invaders - Game (C++)
| Thread Tools | Search this Thread |
Tag cloud for Assembly
3d 68hc11 6811 80386 adress array asm assembler assembly boot bootloader buffer compression cursor debug directory division docs dos draw emulator endtask error exceptions file int10h integer intel interrupt interrupts language loop nohau osdevelopment print program range read remainder shape string text theory tsr x86



