#
# Program:	cont_s.asm - Creates a seconds and tenths of seconds counter
#	in software.
# Author:	       Ney Calazans
# Date:		       28/11/2021
# Last changed:    06/04/2022 
# Function: 	This program produces a decimal counter with precision
#	of tenths of seconds. The counter is expected be used in real time
#	as a peripheral that runs under control of the processor executing
#	this software. The counter starts at 000 seconds 0 tenths and 
#	automatically stops as it reaches the cont 999 seconds 9 tenths.
#	The peripheral attached to the processor contains four 4-bit 
#	registers, each keeping a decimal digit. The write enable of each
#	register is produced by sensing a write operation to the software
#	register address, as depicted in the software data segment.
#	The counter value is kept in four of the processor registers,
#	$s0 (tenths), $s1 (seconds units), $s2 (seconds tens), $s3 (seconds hundreds).	
#
		.text			# Add what follows to the text segment of the program
		.globl	main		# Declare the label main to be a global one
main:		
		xor	$s0,$s0,$s0	# Start the counter at 000,0
		xor	$s1,$s1,$s1
		xor	$s2,$s2,$s2
		xor	$s3,$s3,$s3

		la	$s4,out_dec_s	# get tenths address address
		lw	$s4,0($s4)	# get tenths address 
		la	$s5,out_un	# get seconds units address address
		lw	$s5,0($s5)	# get units address 
		la	$s6,out_dz	# get seconds tens address address
		lw	$s6,0($s6)	# get tens address 
		la	$s7,out_ct	# get seconds hundreds address address
		lw	$s7,0($s7)	# get hundreds address
		
mainloop:
#		jal	sim_print_count	# This line is for MARS simulation only, 
#					# comment it for running the program in hardware
		sb	$s0,0($s4)	# write tenths current value
		sb	$s1,0($s5)	# write seconds units current value
		sb	$s2,0($s6)	# write seconds tens current value
		sb	$s3,0($s7)	# write seconds hundreds current value
		jal	wait_to_1tenth	# call subroutine to wait for almost 1 tenth of second
		addiu	$a0,$zero,9	# get largest count of any digit (subroutine parameter)
		jal	update_digits	# call subroutine to update counter
					# now, logically compute if reached 999,9
		and	$t0,$s0,$s1	# create AND of tenths and units
		and	$t1,$s2,$s3	# create AND of tens and hundreds
		and	$t0,$t0,$t1	# create AND of all 4 digits
		beq	$a0,$t0,fim	# if the AND of all 4 digits is exactly 9, end processing
		j	mainloop	# else, jump back
		# now, enter infinite loop
fim:
		sb	$s0,0($s4)	# write tenths current value
		sb	$s1,0($s5)	# write seconds units current value
		sb	$s2,0($s6)	# write seconds tens current value
		sb	$s3,0($s7)	# write seconds hundreds current value
#		jal	sim_print_count	# This line is for MARS simulation only, 
#					# comment it for running the program in hardware
aqui:		j	aqui

# Subroutine:	wait_to_1tenth - Spends the time needed to complete (together with 
#	its calling software loop execution time) the passage of exactly 1 tenth of second. 
#	Assumptions: 
#		1) MIPS_S processor is running this routine, which accounts for the
#		exact number of clock cycles taken
#		2) The running clock cycle of the MIPS processor is exactly 100MHz,
#		that of the native clock input of the Digilent Nexys A7 platform
# 
# Author:	Ney Calazans
# Date:		28/11/2021
wait_to_1tenth:	li	$t0,249998	# Initialize counter with pre-computed value
		# The best integer value to minimize the error is 178.570, with 11 NOPs
		# Another reasonable value is 249.998, with 7 NOPs, the one chosen here
		# See the spreadsheet in annex for details.
time_loop:	beq	$t0,$zero,fim_wait
		addiu	$t0,$t0,-1	# decrement counter
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		j	time_loop
fim_wait:	jr	$ra		# for now, just return
		
# Subroutine:	update_digits - assuming 1 tenth has passed, increments the 4-digit
#		decimal counter stored in registers $s3 $s2 $s1 , $s0. Propagates
#		the decimal carry whenever needed. 
#	Assumptions: 
#		1) All digits are stored in BCD form and thus never exceed the
#	0-9 (0000 - 1001). 
#		2) A single input parameter is passed through $a0, and brings the
#	maximum value of each digit (in this case, is always 9)
# 
# Author:	Ney Calazans
# Date:		28/11/2021
update_digits: 	beq	$s0,$a0,tenths9	# here, treats tenths
		addiu	$s0,$s0,1	# did not reach 9, just increment tenths
		jr	$ra		# and return
tenths9:	xor	$s0,$s0,$s0	# put tenths at 0
		beq	$s1,$a0,units9	# here, treats units
		addiu	$s1,$s1,1	# did not reach 9, just increment units
		jr	$ra		# and return
units9:		xor	$s1,$s1,$s1	# put units at 0
		beq	$s2,$a0,tens9	# here, treats tens
		addiu	$s2,$s2,1	# did not reach 9, just increment tenths
		jr	$ra		# and return
tens9:		xor	$s2,$s2,$s2	# put tens at 0
		addiu	$s3,$s3,1	# just increment hundreds
		jr	$ra		# and return
# Warning: The treatment of hundreds is a little different from the treatment of tenths
#	It is assumed that when the counter is at 999,9 update_digits is not called anymore.
#	Thus, there is no need to test if hundreds is at 9.
		
# Subroutine:	sim_print_count - Just prints the counter value on the screen when called
#	Assumptions: 
#		1) All 4 digits of the counter are stored in BCD form in registers
#	 $s3 $s2 $s1 , $s0.
# 
# Author:	Ney Calazans
# Date:		28/11/2021
sim_print_count:
		move	$a0,$s3			# print hundreds
		li	$v0,1
		syscall
		move	$a0,$s2			# print tens
		li	$v0,1
		syscall
		move	$a0,$s1			# print units
		li	$v0,1
		syscall
		la	$a0,comma		# print comma
		li	$v0,4
		syscall
		move	$a0,$s0			# print tenths
		li	$v0,1
		syscall
		la	$a0,newline		# print comma
		li	$v0,4
		syscall
		jr	$ra

		.data			
out_dec_s:	.word	0x10008000		# address of the tenths counter
out_un:		.word	0x10008001		# address of the second units counter
out_dz:		.word	0x10008002		# address of the second tens counter
out_ct:		.word	0x10008003		# address of the second hundreds counter
comma:		.asciiz	","
newline:	.asciiz	"\n"



