# exceptions.s:
#
# This file contains the exception handler code for the I/O Lab.

######################################################################
# EXCEPTION HANDLER
######################################################################

	.ktext 0x80000180	# Forces interrupt routine below to be
				# located at address 0x80000180.

intrp:
	addiu	$sp,$sp,-32	# Save registers. 
	.set noat		# Tell assembler to stop using $at...
	sw	$at,28($sp)	# so we can use it.
	.set at			# Now give back $at to the assembler.
	# Save registers.  Remember, this is an interrupt routine
	# so it has to save the $t registers.
	sw	$ra,24($sp)
	sw	$a0,20($sp)
	sw	$t4,16($sp)
	sw	$t3,12($sp)
	sw	$t2,8($sp)
	sw	$t1,4($sp)
	sw	$t0,0($sp)

	li	$a0,'J'
	jal	trace

# First attend to input
	lui $t1,0xffff          # get base address of device
	lw $t3,0($t1)           # get status word for input
	andi $t3,$t3,0x1        # mask ready bit
	beq $t3,$0,indone       # if no char, all done

	li $a0,'R'
	jal trace

	lb $t3,4($t1)           # else get the char

	lw $t0,rcvInput         # Fetch current input index, and
				# compute new input index after we
	addiu $t1,$t0,1         # store the next character.
	andi $t1,$t1,15         # Wrap around from 16 back to 0.
	lw $t2,rcvOutput        # Is buffer full? (i.e. is rcvInput
				# just before rcvOutput?)
	bne $t2,$t1,insertChar  # If not, add new char to buffer.
	lw $t2,dropCount	# If so, tally dropped char.
	addi $t2,$t2,1
	sw $t2,dropCount
	j indone

insertChar:
	la $t4,rcvBuf
	add $t4,$t4,$t0
	sb $t3,0($t4)           # Space in buffer: store character.
	sw $t1,rcvInput         # Update index.

# Next attend to output.

indone:
	li $a0,'X'
	jal trace

	lw	$t0,nextOut	# Is buffer empty?
	lw	$t1,nextIn	# I.e. is nextOut equal to nextIn?
	bne	$t0,$t1,notEmpty
### LAB: Exercise
	lui	$t3,0xffff	# $t3 points to base of I/O register
	sw	$0,8($t3)	# If so, disable terminal interrupts.
				# What happens if we don't disable 
				# interrupts?
	j	intDone

	.globl	notEmpty

notEmpty:
	li $a0,'I'
	jal trace

	lui $t1,0xffff		# get base address of device registers.
	lw $t2,8($t1)		# Get status word for output
	andi $t2,$t2,0x1	# mask out all but ready bit
	beq $t2,$0,intDone	# return if not ready

	la $t2,buffer		# get buffer base address
	addu $t3,$t2,$t0	# add offset
	lb $t2,0($t3)		# get the char from the buffer 
	sb $t2,12($t1)		# Output character to term
	addiu $t1,$t0,1		# increment index
	andi $t1,$t1,31		# Wrap around from 32 back to 0.
	sw $t1,nextOut		

	.globl	intDone
intDone:
	## Clear Cause register
	mfc0	$t0,$13		# get Cause register, then clear it
	mtc0	$0, $13

	## restore registers
	lw	$t0,0($sp)
	lw	$t1,4($sp) 
	lw	$t2,8($sp) 
	lw	$t3,12($sp) 
	lw	$t4,16($sp) 
	lw	$a0,20($sp)
	lw	$ra,24($sp)
	.set noat 
	lw $at,28($sp) 
	.set at 
	addiu	$sp,$sp,32
	
	eret 

# Make note of the execution of the various i/o handling routines
# by storing characters in the circular array named callSeq.

	.globl trace
trace:
	addiu $sp,$sp,-8
	sw $s0,0($sp)
	sw $s1,4($sp)
	la $s0,callSeq
	lw $s1,callSeqIndex
	add $s0,$s0,$s1
	sb $a0,0($s0)
	addi $s1,$s1,1
	andi $s1,$s1,31
	sw $s1,callSeqIndex
	lw $s1,4($sp)
	lw $s0,0($sp)
	addiu $sp,$sp,8
	jr $ra

# Standard startup code.  Invoke the routine "main" with arguments:
#       main(argc, argv, envp)
#
	.ktext
	.globl __start
__start:
	lw $a0 0($sp)           # argc
	addiu $a1 $sp 4         # argv
	addiu $a2 $a1 4         # envp
	sll $v0 $a0 2
	addu $a2 $a2 $v0
	jal main
	nop

	li $v0 10
	syscall                 # syscall 10 (exit)

	.globl __eoth
__eoth:
	.kdata
	.globl	dropCount
	.globl	callSeq
	.globl	callSeqIndex
dropCount:
	.word	0
callSeq:
	.space	32
callSeqIndex:
	.word	0
