#
# One Instruction Computer Simulator.
#

#
# Hand assembled OIC program.
#
# This program simulates an one-instruction computer, with the instruction
#  subz A,B,C
# which has the semantics:
#  mem[A] = mem[A] - mem[B]
#  if mem[A] == 0 then PC = C
#  else PC = PC + 1
# The mem[] array contains words which, when interpreted as instructions,
# hold three memory addresses.  Thus, in our simulated code, we use 3
# MIPS words per OIC word, and the OIC subtraction operation is a 96-bit
# unsigned subtraction.
#
# The 96-bit subtraction is implemented by viewing the three 32-bit words
# as base 2^32 digits; we use the unsigned mod 2^32 subtraction provided
# by the underlying "hardware" (simulated by spim) to implement modulo
# 2^96 subtraction:  We first subtract the least significant digit, test
# to see if the result is larger than the subtrahend -- if so, the minuend
# must have been larger than the subtrahend and caused a wraparound, and
# we need to borrow from the next digit.  We do the same thing with the
# middle digit, but additionally also subtract the borrow flag from the
# low order digit subtraction.  If either of these subtractions caused a
# borrow, we borrow a 1 from the high order digit.
#
# The OIC emulator maintains the OIC PC as a MIPS pointer into the
# simulated OIC memory; whenever a branch occurs, the index C is immediately
# translated into a MIPS pointer.
#
# Termination of the simulation occurs if an instruction's execution causes
# a branch back to itself.
#

	.data
OICmem:
# first test case -- add macro
#	.word 6,6,1		#   mem[0]		subz scr,scr,next
#	.word 7,7,2		# 	1		subz sum,sum,next
#	.word 6,8,3		#	2		subz scr,A,next
#	.word 6,9,4		#	3		subz scr,B,next
#	.word 7,6,5		#	4		subz sum,scr,next
#	.word 6,6,5		#	5	done:	subz scr,scr,done
#	.word 0,0,0		#	6 scr
#	.word 0,0,0		#	7 sum
#	.word 0,0,5		#	8 A
#	.word 0,0,6		#	9 B

# mult code
	.word 13,13,1	#	mem[0]		subz prod,prod,next
	.word 16,16,2	#	mem[1]		subz scr,scr,next
	.word 16,11,10	#	mem[2]		subz scr,x,zero_mult
	.word 15,15,4	#	mem[3]		subz index,index,next
	.word 15,16,5	#	mem[4]		subz index,scr,next
	.word 16,16,6	#	mem[5]		subz scr,scr,next

			# top:	# loop invariant:  (index * y) - scr = x * y
			#	# hold here...
	.word 16,12,7	#	mem[6]		subz scr,y,next
	.word 15,14,9	#	mem[7]		subz index,const1,done
	.word 13,13,6	#	mem[8]		subz prod,prod,top

			# done:
	.word 13,16,10	#	mem[9]		subz prod,scr,next

			#	mem[10]	zero_mult:
	.word 16,16,10	#			subz scr,scr,zero_mult
			#	mem[11]	x:      .space  1 # user input, >= 0
	.word 0,0,0x10
#	 .word 0,0,10	# other test cases for x
#	 .word 0,0,3	#

			#	mem[12]	y:      .space  1
			#		 # user input, no value restriction
	.word 0x60006000,0x60000000,0x60000000

	.word 0,0,0	#	mem[13]	prod:   .space  1
	.word 0,0,1	#	mem[14]	const1: .word   1
	.word 0,0,0	#	mem[15]	index:  .space  1
	.word 0,0,0	#	mem[16]	scr:    .space  1
OICmemEnd:

#
# Since this is main, we assume that the caller (the operating system)
# do not require that the s registers be saved.  $s0-$s4 are trashed.
#
	.text
main:	la $s0,OICmem
	move $s1,$s0		# pc, translated into address
	la $s3,OICmemEnd

loop:	move $s4,$s1		# old_pc gets pc
	lw $a0,0($s1)		# A -- of subz A,B,C
	sll $a2,$a0,3		# 8*A
	sll $a0,$a0,2		# 4*A
	add $a0,$a0,$a2		# 12*A
	add $a0,$s0,$a0		# OICmem+12*A
	bgeu $a0,$s3,error0

	lw $a1,4($s1)		# B -- of subz A,B,C
	sll $a2,$a1,3		# 8*B
	sll $a1,$a1,2		# 4*B
	add $a1,$a1,$a2		# 12*B
	add $a1,$s0,$a1		# OICmem+12*B
	bgeu $a1,$s3,error1

	lw $t0,0($a0)		# $t0,$t1,$t2 are the digits from mem[A]
	lw $t1,4($a0)
	lw $t2,8($a0)

	lw $t3,0($a1)		# $t3,$t4,$t5 are the digits from mem[B]
	lw $t4,4($a1)
	lw $t5,8($a1)

	subu $t8,$t2,$t5	# least sig digit
	sltu $t9,$t2,$t8	# set to 1 if borrow occurred

	subu $s2,$t1,$t4
	sltu $a3,$t1,$s2
	subu $t7,$s2,$t9	# subtract borrow flag
	sltu $t9,$s2,$t7
	or $t9,$t9,$a3		# if borrow occured in either case

	subu $t6,$t0,$t3	# most sig digit, do not care if borrow
	subu $t6,$t6,$t9	# occurs here

	sw $t6,0($a0)		# mem[A] gets result of subtraction
	sw $t7,4($a0)
	sw $t8,8($a0)

	or $t6,$t6,$t7		# could have been 3 cond branches, but can not
	or $t6,$t6,$t8		# fill those delay slots
	beq $t6,$zero,branch
	add $s1,12		# this goes into either beq delay slot
	j loop			#  or the j loop delay slot.  It can go into
				#  the beq slot (if assembler is really smart
				#  and notices) because the branch: code
				#  do not use $s1; it just gets a new value,
				#  so incrementing the old value does not hurt.
				# if we wanted the add to go into the beq
				#  delay slot, we could have the move $s4,$s1
				#  in the j loop's delay slot and get rid of
				#  it from the top of the loop (but we need
				#  one instance of it before we enter the
				#  loop)

branch:
	lw $a2,8($s1)		# C -- of subz A,B,C
	sll $t0,$a2,3
	sll $a2,$a2,2
	add $a2,$a2,$t0
	add $s1,$s0,$a2
	bgeu $s1,$s3,error2

	bne $s4,$s1,loop	# if new PC != old PC, we keep going

	.data
adios:	.asciiz "Simulation ends\n"
	.text
done:	la $a0,adios
	li $v0,4
	syscall
	jr $ra

	.data
error0_str:
	.asciiz "Simulator detected out-of-bounds error for A\n"
	.text
error0:	la $a0,error0_str
	li $v0,4
	syscall
	jr $ra

	.data
error1_str:
	.asciiz "Simulator detected out-of-bounds error for B\n"
	.text
error1:	la $a0,error1_str
	li $v0,4
	syscall
	jr $ra

	.data
error2_str:
	.asciiz "Simulator detected out-of-bounds error for C\n"
	.text
error2:	la $a0,error2_str
	li $v0,4
	syscall
	jr $ra
