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
;---------------------------------------------------

While pursuing a Computer Science degree, I founded DaniWeb.com, an online community for developers and IT professionals. I coded the backend platform from the ground up and I also do all of the advertising sales and SEO. I'm a super-geeky programmer with a passion for Internet marketing.

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

Hi..........
I am abeginner to assembly language and we are using 8051 assembly language..Can anyone plz help me to convert space invaders program into 8051 assembly language so that it will be helpful to me.........I need to understand the logic too...
Thank you

Gah, it looks like I can't play this. It keeps crashing everytime I try to start the game. I haven't played this in 5+ years so there must be some type of incompatibility introduced on my current architecture.

The article starter has earned a lot of community kudos, and such articles offer a bounty for quality replies.