# mp1.S - missile-command version

.data
	
    # Constants for accessing the fields of a struct missile, 
    # struct missile is defined in rtc.h and mp1.h

	NEXT 	  = 0
	X	  = 4
	Y	  = 8
	VX	  = 12
	VY	  = 16
	DEST_X	  = 20
	DEST_Y	  = 24
	EXPLODED  = 28
	C	  = 32

    # Character to draw for an explosion - '@'

	EXPLOSION = 64


# Data shared between this file and rtc.c helper functions

# This '.globl' directive makes these symbols visible externally
.globl mp1_missile_list, base_alive, mp1_score

mp1_missile_list:	.long 0x0	# Head of list pointer
base_alive:		.long 0x0	# Array of 3 bytes, plus a padding byte
mp1_score:		.long 0x0	# Player's current score


# Data private to this  file

base_pic:     	    .string "/^^^\\"	# Picture of a live base
dead_base_pic: 	    .string "xxxxx"	# Picture of a dead base
crosshairs_x:		.long 0x0	# X-position of the crosshairs
crosshairs_y:		.long 0x0	# Y-position of the crosshairs

.text

# void mp1_poke(void);
# You can use this function to write to video memory.
#
# Interface: Register-based arguments (not C-style)
#    Inputs: %cl  - The byte you wish to write
#            %eax - Offset from the start of video memory that you wish
#                   to write to
#   Outputs: Text-mode video screen is written to at location %eax with
#            the byte in %cl
# Registers: Clobbers EDX

mp1_poke:
	movl    vmem_base_addr(,1),%edx
	movb    %cl,(%edx,%eax,1)
ret

#   ----------------- Exported functions ---------------------

# void mp1_rtc_tasklet(unsigned long garbage);
# Performs three tasks:
#	(1) updates the list of missiles (implement this in update_missiles,
#           below, and call it from here).
#	(2) Redraw the bases - they may have been overwritten with missiles
#	(3) Redraw the crosshairs - it may have been overwritten with missiles
# Inputs   : none
# Outputs  : none
# Registers: Standard C calling convention

.globl mp1_rtc_tasklet
mp1_rtc_tasklet:
	# callee save
	pushl %ebp
	movl %esp, %ebp
	
	# make the three calls
	pushl %edx
	call update_missiles
	popl %edx

	pushl %edx
	call mp1_rtc_tasklet_redraw_bases
	popl %edx
	
	pushl %edx
	call mp1_rtc_tasklet_redraw_xhair
	popl %edx

	# callee teardown
	leave
ret

mp1_rtc_tasklet_redraw_bases:
	# ESI - Counter for outer loop = 3
	# EDI - Counter for inner loop = 5
	# EBX - Hold ptr to base
	# callee save
	pushl %ebp
	movl %esp, %ebp
	pushl %ebx
	pushl %esi
	pushl %edi

	# redraw bases
	# initialize variables for the 2 loops
	movl $3876, %eax
	movl $5, %edi
	movl $3, %esi
	movl $base_alive, %edx

	# loop to print 5 characters
	mp1_rtc_tasklet_redraw_bases_outter:
		cmpl $0, %esi
		jle mp1_rtc_tasklet_redraw_bases_outter_exit
		cmpb $0, (%edx)
		je  mp1_rtc_tasklet_redraw_bases_dead
		movl $base_pic, %ebx
		jmp mp1_rtc_tasklet_redraw_bases_inner

		mp1_rtc_tasklet_redraw_bases_dead:
		movl $dead_base_pic, %ebx

		mp1_rtc_tasklet_redraw_bases_inner:
			cmpl $0, %edi
			jle mp1_rtc_tasklet_redraw_bases_inner_exit
			movb (%ebx), %cl 
			pushl %edx
			call mp1_poke
			popl %edx
			addl $1, %ebx
			addl $2, %eax
			decl %edi
			jmp mp1_rtc_tasklet_redraw_bases_inner
		mp1_rtc_tasklet_redraw_bases_inner_exit:
		addl $30, %eax
		# movl $base_pic, %ebx
		movl $5, %edi
		decl %esi
		addl $1, %edx
		jmp mp1_rtc_tasklet_redraw_bases_outter
	mp1_rtc_tasklet_redraw_bases_outter_exit:

	# callee teardown
	popl %edi
	popl %esi
	popl %ebx
	leave
ret

mp1_rtc_tasklet_redraw_xhair:
	# callee save
	pushl %ebp
	movl %esp, %ebp
	pushl %ebx
	pushl %esi
	pushl %edi

	# redraw xhair
	movl crosshairs_y, %eax
	imull $160, %eax 				# 80 x 2
	movl crosshairs_x, %ebx
	imull $2, %ebx
	addl %ebx, %eax
	movb $88, %cl
	call mp1_poke

	# callee teardown
	popl %edi
	popl %esi
	popl %ebx
	leave
ret

# int mp1_ioctl(unsigned long arg, unsigned int cmd)
# The dispatch function for the MP1 ioctls - should use the cmd argument
# and a jumptable to execute one of the specific ioctls implemented below.
# Inputs   : unsigned long arg - parameter to the mp1_ioctl_....
#          : unsigned int cmd  - specifies which mp1_ioctl_... function 
#          :                     to execute
# Outputs  : Returns an integer - depends on which ioctl() function is called
# Registers: Standard C calling convention

.globl mp1_ioctl
mp1_ioctl:
	# get cmd in eax
	movl 8(%esp), %eax

	# compare agar > 4
	cmpl $4, %eax
	jg mp1_ioctl_invalid

	# compare agar < 0
	cmpl $0, %eax
	jl mp1_ioctl_invalid
	
	# then jump to jump table
	jmp *jump_table(, %eax, 4)

	# agar invalid
	mp1_ioctl_invalid:
	movl $-1, %eax
ret

# ----------------- Functions private to this file -------------------

update_missiles:
	# callee save
	pushl %ebp
	movl %esp, %ebp
	pushl %ebx
	pushl %esi
	pushl %edi

	# render the missiles

	# ebx is current_head
	movl mp1_missile_list, %ebx

	update_missiles_loop:
	cmpl $0, %ebx
	je update_missiles_loop_end
	
	movl %ebx, %edi		# temp gets curr
	movl (%edi), %edi	# temp gets temp next

	# start render
	cmpl $0, EXPLODED(%ebx)
	
	# NOT EXPLODING
	je update_missiles_not_exploding

	# EXPLODING
	pushl %edi
	pushl %ebx
	call update_missiles_exploding
	popl %ebx
	popl %edi

	update_missiles_not_exploding_return:

	movl %edi, %ebx		# curr gets temp

	jmp update_missiles_loop
	update_missiles_loop_end:

	# callee teardown
	popl %edi
	popl %esi
	popl %ebx
	leave
ret

update_missiles_not_exploding:
	pushl %edi
	pushl %ebx
	pushl %esi

	# call draw with ' ' and struct ptr
	pushl $32
	pushl %ebx
	call update_missiles_draw
	popl %ebx
	addl $4, %esp

	# add delta
	movl X(%ebx), %edi
	movl Y(%ebx), %esi
	addl VX(%ebx), %edi
	addl VY(%ebx), %esi

	movl %edi, X(%ebx)
	movl %esi, Y(%ebx)

	# get high 16 bits
	shrl $16, %edi
	shrl $16, %esi

	# check agar inside screen
	cmpl $79, %edi
	jg update_missiles_not_exploding_outside_screen

	cmpl $0, %edi
	jl update_missiles_not_exploding_outside_screen

	cmpl $24, %esi
	jg update_missiles_not_exploding_outside_screen

	cmpl $0, %esi
	jl update_missiles_not_exploding_outside_screen

	jmp update_missiles_not_exploding_inside_screen

	update_missiles_not_exploding_internal_return:
	popl %esi
	popl %ebx
	popl %edi
jmp update_missiles_not_exploding_return

update_missiles_not_exploding_inside_screen:
	# is at destination?
	cmpl DEST_X(%ebx), %edi
	jne update_missiles_not_exploding_inside_screen_not_at_dest

	cmpl DEST_Y(%ebx), %esi
	jne update_missiles_not_exploding_inside_screen_not_at_dest

	pushl %ebx
	call update_missiles_exploding
	popl %ebx

	jmp update_missiles_not_exploding_internal_return

	update_missiles_not_exploding_inside_screen_not_at_dest:
	pushl C(%ebx)
	pushl %ebx
	call update_missiles_draw
	popl %ebx
	addl $4, %esp

jmp update_missiles_not_exploding_internal_return

update_missiles_not_exploding_outside_screen:
	# delete from list
	pushl %ebx
	call update_missiles_delete
	popl %ebx

jmp update_missiles_not_exploding_internal_return

update_missiles_exploding:
	# callee save
	pushl %ebp
	movl %esp, %ebp
	pushl %ebx
	pushl %esi
	pushl %edi

	# ebx gets current ptr - ask
	movl 8(%ebp), %ebx

	# call missile_explode - ask
	pushl %ebx
	call missile_explode
	popl %ebx

	# call user notifyy
	cmpl $0, %eax
	jne call_notify_user
	return_from_call_notify_user:
	
	# decrement EXPLODED
	decl EXPLODED(%ebx)

	# check if 0 (just 0)
	cmpl $0, EXPLODED(%ebx)
	je update_missiles_exploding_e_0
	jmp update_missiles_exploding_e_not_0

	update_missiles_exploding_e_0:
	# call draw with ' ' and struct ptr
	pushl $32
	pushl %ebx
	call update_missiles_draw
	popl %ebx
	addl $4, %esp
	# call delete with struct ptr
	pushl %ebx
	call update_missiles_delete
	popl %ebx
	jmp update_missiles_exploding_end

	update_missiles_exploding_e_not_0:
	# call draw with @ and struct ptr
	pushl $EXPLOSION
	pushl %ebx
	call update_missiles_draw
	popl %ebx
	addl $4, %esp
	
	update_missiles_exploding_end:
	popl %edi
	popl %esi
	popl %ebx
	leave
ret

call_notify_user:
	call mp1_notify_user
jmp return_from_call_notify_user

update_missiles_draw:
	# callee save
	pushl %ebp
	movl %esp, %ebp
	pushl %ebx
	pushl %esi
	pushl %edi

	movl 8(%ebp), %ebx		# ebx gets ptr
	movl X(%ebx), %esi		# esi gets X
	movl Y(%ebx), %edi		# edi gets Y

	shrl $16, %esi			# get high 16 bits from X and Y
	shrl $16, %edi

	imull $160, %edi, %eax	# 80 x 2
	imull $2, %esi			# X = X x 2
	addl %esi, %eax			# eax = 160 * Y + X * 2
	movb 12(%ebp), %cl		# cl gets char 
	
	pushl %edx
	call mp1_poke
	popl %edx

	popl %edi
	popl %esi
	popl %ebx
	leave
ret

update_missiles_delete:
	# callee save
	pushl %ebp
	movl %esp, %ebp
	pushl %ebx
	pushl %esi
	pushl %edi

	# get the ptr to delete
	movl 8(%ebp), %ebx

	# get the head ptr
	movl mp1_missile_list, %esi

	# see if head == ptr
	cmpl %ebx, %esi
	je update_missiles_delete_change_head

	update_missiles_delete_loop:
	cmpl $0, %esi
	je update_missiles_delete_end
	
	cmpl (%esi), %ebx
	je update_missiles_delete_remove
	movl (%esi), %esi
	jmp update_missiles_delete_loop
	update_missiles_delete_end:

	popl %edi
	popl %esi
	popl %ebx
	leave
ret

update_missiles_delete_remove:
	movl (%ebx), %edi
	movl %edi, (%esi)
	pushl %ebx
	call mp1_free
	popl %ebx
	movl %edi, %eax
jmp update_missiles_delete_end

update_missiles_delete_change_head:
	movl (%esi), %esi
	movl %esi, mp1_missile_list
	pushl %ebx
	call mp1_free
	popl %ebx
	movl %esi, %eax
jmp update_missiles_delete_end

mp1_ioctl_startgame:
	# callee save
	pushl %ebp
	movl %esp, %ebp
	pushl %ebx
	pushl %esi
	pushl %edi

	# movl $0, mp1_missile_list
	movl $0, mp1_score
	movl $40, crosshairs_x
	movl $12, crosshairs_y
	
	movb $1, base_alive
	movb $1, base_alive + 1
	movb $1, base_alive + 2

	popl %edi
	popl %esi
	popl %ebx
	leave
ret

mp1_ioctl_addmissile:
	# eax - return value
	# ebx - ptr to user space struct
	# edi - ptr to alloted memory for the struct in kernel
	# esi - temp var to help add to linked list

	# callee save
	pushl %ebp
	movl %esp, %ebp
	pushl %ebx
	pushl %esi
	pushl %edi

	# dynamic memory allocation malloc(36) - 36 bytes (size of struct)
	pushl $36
	call mp1_malloc
	addl $4, %esp

	# check for malloc error
	cmpl $0, %eax
	je mp1_ioctl_addmissile_malloc_error
	movl %eax, %edi

	# get user space ptr to ebx and check validity
	movl 8(%ebp), %ebx # getting argument
	cmpl $0, %ebx
	je mp1_ioctl_addmissile_copy_error

	# copy to kernel
	pushl $36
	pushl %ebx
	pushl %edi
	call mp1_copy_from_user
	popl %edi
	popl %ebx
	addl $4, %esp

	# check for copy error
	cmpl $0, %eax
	jne mp1_ioctl_addmissile_copy_error

	# add to the linked list
	movl mp1_missile_list, %esi # temp gets head
	movl %esi, (%edi)           
	movl %edi, mp1_missile_list # head gets new ptr
	jmp mp1_ioctl_addmissile_tear_down

	# free if copy error
	mp1_ioctl_addmissile_copy_error:
	pushl %edi
	call mp1_free
	addl $4, %esp

	# return -1
	mp1_ioctl_addmissile_malloc_error:
	movl $-1, %eax
	jmp mp1_ioctl_addmissile_tear_down_with_fail

	# callee teardown
	mp1_ioctl_addmissile_tear_down:
	movl $0, %eax

	mp1_ioctl_addmissile_tear_down_with_fail:
	popl %edi
	popl %esi
	popl %ebx
	leave
ret

mp1_ioctl_movexhairs:
	# crosshairs_x
	# crosshairs_y
	# ebx - new x position
	# edx - new y positon
	# callee save
	pushl %ebp
	movl %esp, %ebp
	pushl %ebx
	pushl %esi
	pushl %edi

	# check agar delta is 0
	cmpl $0, 8(%ebp)
	je mp1_ioctl_movexhairs_return_0

	# remove from current position
	movl crosshairs_y, %eax
	imull $160, %eax                     # 80 x 2
	movl crosshairs_x, %ebx
	imull $2, %ebx
	addl %ebx, %eax
	movb $32, %cl
	call mp1_poke

	# caculate correct new position
	movl $0, %ebx
	movl $0, %edx
	
	addl crosshairs_x, %ebx
	addw 8(%ebp), %bx

	addl crosshairs_y, %edx
	addw 10(%ebp), %dx

	cmpl $79, %ebx
	jbe mp1_ioctl_movexhairs_x_up_is_ok
	subw 8(%ebp), %bx

	mp1_ioctl_movexhairs_x_up_is_ok:
	cmpl $0, %ebx
	jae mp1_ioctl_movexhairs_x_down_is_ok
	subw 8(%ebp), %bx

	mp1_ioctl_movexhairs_x_down_is_ok:
	cmpl $24, %edx
	jbe mp1_ioctl_movexhairs_y_up_is_ok
	subw 10(%ebp), %dx

	mp1_ioctl_movexhairs_y_up_is_ok:
	cmpl $0, %edx
	jae mp1_ioctl_movexhairs_y_down_is_ok
	subw 10(%ebp), %dx

	mp1_ioctl_movexhairs_y_down_is_ok:
	jmp mp1_ioctl_movexhairs_update_current_xhair

	mp1_ioctl_movexhairs_update_current_xhair:
	movl %ebx, crosshairs_x
	movl %edx, crosshairs_y

	mp1_ioctl_movexhairs_return_0:
	movl $0, %eax

	# callee teardown
	popl %edi
	popl %esi
	popl %ebx
	leave
ret

mp1_ioctl_getstatus:
	# esi - get param (the ptr to user space int)
	# edi - get my local int ptr
	# callee save
	pushl %ebp
	movl %esp, %ebp
	pushl %ebx
	pushl %esi
	pushl %edi

	movl 8(%ebp), %esi
	movl $0, %ebx
	movw mp1_score, %bx

	cmpb $0, base_alive
	jne mp1_ioctl_getstatus_set_base1_to_alive

	mp1_ioctl_getstatus_base1_done:
	cmpb $0, base_alive + 1
	jne mp1_ioctl_getstatus_set_base2_to_alive

	mp1_ioctl_getstatus_base2_done:
	cmpb $0, base_alive + 2
	jne mp1_ioctl_getstatus_set_base3_to_alive
	jmp mp1_ioctl_getstatus_base3_done

	mp1_ioctl_getstatus_set_base1_to_alive:
	orl $0x00010000, %ebx
	jmp mp1_ioctl_getstatus_base1_done

	mp1_ioctl_getstatus_set_base2_to_alive:
	orl $0x00020000, %ebx
	jmp mp1_ioctl_getstatus_base2_done

	mp1_ioctl_getstatus_set_base3_to_alive:
	orl $0x00040000, %ebx
	
	mp1_ioctl_getstatus_base3_done:
	pushl %ebx
	movl %esp, %edi # edi is ptr to int

	# copy to user
	pushl $4
	pushl %edi
	pushl %esi
	call mp1_copy_to_user
	popl %esi
	popl %edi
	addl $4, %esp

	# check for copy error
	cmpl $0, %eax
	jne mp1_ioctl_getstatus_failed

	# callee teardown
	popl %ebx
	popl %edi
	popl %esi
	popl %ebx
	movl $0, %eax
	leave
	ret

	# callee teardown failed
	mp1_ioctl_getstatus_failed:
	popl %ebx
	popl %edi
	popl %esi
	popl %ebx
	movl $-1, %eax
	leave
ret

mp1_ioctl_endgame:
	# ebx is temp
	# callee save
	pushl %ebp
	movl %esp, %ebp
	pushl %ebx
	pushl %esi
	pushl %edi

	mp1_ioctl_endgame_loop:
	cmpl $0, mp1_missile_list
	je mp1_ioctl_endgame_loop_end
	
	movl mp1_missile_list, %ebx # temp gets head
	movl mp1_missile_list, %esi
	movl (%esi), %esi # head gets head next
	movl %esi, mp1_missile_list
	
	pushl %ebx
	call mp1_free
	addl $4, %esp

	jmp mp1_ioctl_endgame_loop
	mp1_ioctl_endgame_loop_end:

	# callee teardown
	popl %edi
	popl %esi
	popl %ebx
	leave
ret

jump_table:
.long mp1_ioctl_startgame, mp1_ioctl_addmissile, mp1_ioctl_movexhairs, mp1_ioctl_getstatus, mp1_ioctl_endgame