; Calculator.asm -- Travis Collins
; By submitting this program, I agree that I have followed the Virginia Tech
; Honor Code, and that all work is my own.
;
; Basic functionality: Input a digit, then an operation, then a digit
;			The program will then perform the operation using
;			the digits, then output the result

; In general, R7 is used to store return addresses. Arguments used as registers 
; then generally count down from R5, so R5 is the first arg, R4 is the second, etc.
; In this way, the branches to labels act more like functions than gotos

; Labeling is generally used in a form consistent with function naming
; For instance, if you had a label 'FXN' then the loop of that label would be 'LFXN'
; and the quitting sequence of that function would be 'QFXN'

		.ORIG x3000
; -----		BEGIN MAIN CODE SECTION		-----
		LEA R0,PROMPT 
		PUTS 	     ; Output our prompt
		GETC
		OUT	     ; Print the input they entered for their benefit
		ADD R5,R0,#0 ; Put the value of R0 (the input) in R5
		LD  R3,ASCIIZERO
		NOT R3,R3
		ADD R3,R3,#1 ; R3 now has the value required to convert from ASCII to integers
		ADD R5,R5,R3 ; R5 now contains the integer that was input as the first parameter
		
		GETC
		OUT	     ; Print the input they entered for their benefit
		ADD R2,R0,#0 ; Move value of R0 (the input) into R2 for usage later
		
		GETC
		OUT	     ; Print the input they entered for their benefit
		ADD R4,R0,#0 ; Put the value of R0 (the input) in R4
		ADD R4,R4,R3 ; R4 now contains the integer that was input as the third parameter
		
		LEA R0,EQUALMSG 
		PUTS 	     ; Output our equal sign and spaces
		
		; R2 contains the ASCII value of the operation value to be done, we can easily 'convert' it
		; By subtracting the value of '+', we will have either n, z, or p set to tell us our operation
		; (repeat the process for telling between division/subtraction)
		; n = *
		; z = +
		; p = - or /
		LD R3,ASCIIPLUS
		NOT R3,R3
		ADD R3,R3,#1 ; R3 now has the value required to convert the operations to nzp flags
		ADD R2,R2,R3 ; flags now set
		BRn BRMULT
		BRz BRADD
		ADD R2,R2,#-2; for determining between - and /
		BRz BRSUB
		BRp BRDIV
				
BRADD		JSR DOADD
		JSR PRINTP
		JSR QUIT  ; Terminate execution with extreme prejudice
			
BRSUB		JSR DOSUB
		BRn BRPRINTN
		JSR PRINTP
		JSR QUIT  ; Terminate execution with extreme prejudice

BRMULT		JSR DOMULT
		JSR PRINTP
		JSR QUIT  ; Terminate execution with extreme prejudice
			
BRDIV		JSR DODIV
		JSR PRINTP
		JSR QUIT  ; Terminate execution with extreme prejudice
		
BRPRINTN	JSR PRINTN 
		JSR QUIT  ; Terminate execution with extreme prejudice

; -----		BEGIN FUNCTION SECTION		-----

; function SHL - used for shifting bytes to the left by multiplying by 2^8 (256)
SHL		ST  R7,TEMP6 ; We have to modify R7 with a function call, so store it
		ADD R6,R4,#0 ; Store the previous value of R4 in our "junk register"
		LD  R4,LOBIT ; Put 255 (x00FF) in R4
		ADD R4,R4,#1 ; Put 256 in R4 so we can multiply R5 by it
		JSR DOMULT   ; Multiply the value by 256, which is the same as shifting two bits to the left
		ADD R4,R6,#0 ; Restore the value of R4
		LD  R7,TEMP6 ; restore the value of R7
		RET	     ; Jump to the return address given

; function DOADD - used for adding numbers
DOADD		ADD R5,R5,R4
		RET	     ; return execution to caller [ RETURNS ANSWER IN R5 ]

; function DOSUB - Used for subtracting numbers			
DOSUB		NOT R4,R4    ; Not the second argument so that we can add the negative version of it
		ADD R4,R4,#1 ; Complete the operation that makes the second arg negative
		ADD R5,R5,R4 ; Add the first arg and the negative version of the second
		RET	     ; Return execution to the caller [ RETURNS ANSWER IN R5 ]

; function DOMULT - Used for multiplying numbers			
DOMULT		ST  R3,TEMP1 ; Store the value of R3 so we can restore it back later 
		ADD R3,R5,#0 ; Store the value of the first argument for later use in adding
		AND R5,R5,#0 ; Zero out R5 so the result is correct
LDOMULT 	ADD R4,R4,#-1; Decrement the second argument and check if its less than 0
		BRn QDOMULT
		ADD R5,R5,R3 ; If its not, add the first argument to the running total
		BR  LDOMULT  ; Jump back to the start of the loop
QDOMULT		LD  R3,TEMP1 ; Restore the original register values (note that this resets nzp, 
			     ; 		but not important in this application)
		RET	     ; Return execution to the caller [ RETURNS ANSWER IN R5 ]
			
; function DODIV - Used for doing integer division
DODIV		ST  R3,TEMP1 ; Store the value of R3 so we can restore it back later 
		AND R3,R3,#0 ; Initialize our counter
		NOT R4,R4
		ADD R4,R4,#1 ; R4 is now ready to be added for "subtraction"
LDODIV		ADD R3,R3,#1 ; Increment the counter
		ADD R5,R5,R4
		BRn LDODIVDONE; if its negative, we need to re-add 10 to get the digit
		BRz QDODIV   ; if its zero, we can skip that addition
		BR  LDODIV   ; Not done yet, loop again
LDODIVDONE 	ADD R4,R4,#10; R4 now has the value of the right-most digit, R3 has the left
		ADD R3,R3,#-1; R3 is only valid directly after the loop if the 'division' was even, since
			     ;  	it wasn't, decrement it
QDODIV		ADD R5,R3,#0 ; put the return value in R5
		LD  R3,TEMP1 ; Restore the original register values (note that this resets nzp,		  					 ; 		but not impo.rtant in this application)
		RET	     ; Return execution to the caller [ RETURNS ANSWER IN R5 ]

; function PRINTP - used for printing positive numbers, including two-digit numbers			
PRINTP		ST  R4,TEMP2 ; R4 is used to store bytes and check for two digit numbers
		ST  R3,TEMP3 ; R3 is used as a counter
		ST  R7,TEMP4 ; R7 is used for function calls
		ADD R0,R5,#0 ; Store the value of R5 in R0
		ADD R4,R0,#-9; Subtract 9 for determining if its two digits
		BRnz QPRINTP ; If its negative or zero then its one digit->skip to the printing;
    			     ;		the following code is for two digit numbers only
			
	; The following code works by subtracting 10 until the number is equal to or less than zero
	; It then re-adds 10 if the answer to the subtraction is not 10 and that gives us the right-most digit
	; While subtracting, it also increments a counter, giving us the left-most digit
	
		AND R3,R3,#0 ; Initialize our counter
		ADD R4,R0,#0 ; Initialize R4 with the full number
LPRINTP		ADD R3,R3,#1 ; Increment the counter
		ADD R4,R4,#-10
		BRn LPRINTPDONE; if its negative, we need to re-add 10 to get the digit
		BRz LPRINTPZERO; if its zero, we can skip that addition
		BR  LPRINTP  ; Not done yet, loop again
LPRINTPDONE 	ADD R4,R4,#10; R4 now has the value of the right-most digit, R3 has the left
		ADD R3,R3,#-1; R3 is only valid directly after the loop if the 'division' was even, since 
						 ; 		it wasn't, decrement it
LPRINTPZERO	AND R5,R5,#0 ; Clear out R5 so we can use it for a function call
		ADD R5,R4,#0 ; Put the right most digit in the low byte of R5 for passing to SHL
		LD  R4,ASCIIZERO
		ADD R5,R5,R4 ; Convert the digit to ASCII text
		JSR SHL	     ; "Call" SHL, overwrites R7
		ADD R0,R5,R3 ; Now R0's high byte is the right digit, low byte is the left digit
QPRINTP		LD  R4,ASCIIZERO
		ADD R0,R0,R4 ; Convert the left-most digit (low byte) to ASCII
		OUT          ; Print out R0, in the format <Low Byte><High Byte>
		LD  R7,TEMP4 ; Restore original value of R7
		LD  R3,TEMP3 ; Restore original value of R3
		LD  R4,TEMP2 ; Restore original value of R4
		RET 	     ; Return execution to the caller [ NO RETURN VALUE ]
			
; function PRINTN - used for printing negative numbers
PRINTN		ST  R7,TEMP5  ; We need to use R7 for calling PRINTP, so we store it past the temps
			      ; 		that PRINTP uses
		LD  R0,ASCIINEG; Put '-' in R0 for printing
		OUT
		NOT R5,R5
		ADD R5,R5,#1 ; R5 is now positive so we can call PRINTP with it
		JSR PRINTP   ; "Call" PRINTP, overwrites R7
		LD  R7,TEMP5 ; Restore the value of R7
		RET			 ; Return execution to the caller [ NO RETURN VALUE ]

; function QUIT - used to halt execution
QUIT		HALT
			
TEMP1		.FILL x0000 ; The TEMPx locations allow us to keep registers for restoring later
TEMP2		.FILL x0000
TEMP3		.FILL x0000
TEMP4		.FILL x0000
TEMP5		.FILL x0000
TEMP6		.FILL x0000
HIBIT		.FILL xFF00 ; Used for getting only the high byte
LOBIT		.FILL x00FF ; Used for getting only the low byte

ASCIIZERO	.FILL x0030 ; ASCII value of '0'
ASCIINEG	.FILL x002D ; ASCII value of '-'
ASCIIPLUS	.FILL x002B ; ASCII value of '+'
PROMPT		.STRINGZ "A _ B?: "
EQUALMSG	.STRINGZ " = "

		.END