MUST BE IN PLP* You calculator should accept full mathematical expressions with
ID: 3666062 • Letter: M
Question
MUST BE IN PLP*
You calculator should accept full mathematical expressions with 2 numbers and an operator (e.g. “1+2=”) and print the result to the UART using the provided module. It should handle the operators to add (+), subtract (-), and multiply (*), but it does not need to handle division. It should accept multiple expressions and generate the appropriate outputs for each expression without manually resetting the simulation (e.g. the input “5-4=1*2=” should output “1” followed by “2”).
The module contains a print function, project_3_output_number, that uses 2 registers as input arguments, $a0 and $a1. The register, $a0, is the value to be displayed on the UART and register $a0 is used to indicate the error message. If $a1 is non-zero an error message will be displayed regardless of the value in $a0. The table below indicates the mapping between $a1 and the UART output.
The only error your program needs to check for is invalid characters ($a1 = 1), which are when a character is not a number character or one of the 3 operators (you can also allow space characters, but this is not required). In order to use the output function you PLP program needs to have initialized the stack pointer ($sp). You should typically initialize the stack pointer to 0x10fffffc (the last address of RAM). The output is called using the following instruction (the line following this instruction must be a valid PLP instruction otherwise you will most likely get an error during simulation): call project3_output_number
After this function call, the value in $a0 (unless $a1 is set to something other than 0) will be displayed on the UART and then the program will return to the instruction immediately following the call instruction.
Below is the code that needs to be weeved with your code:
Explanation / Answer
bcd_storage_p3:
.space 10 #creates 10 words here
invalid_char_p3:
.asciiz "ERROR: Invalid character "
invalid_expression_p3:
.asciiz "ERROR: Invalid expression "
input_overflow_p3:
.asciiz "ERROR: Input overflow "
output_overflow_p3:
.asciiz "ERROR: Output overflow "
unknown_error_p3:
.asciiz "ERROR: Unknown error code "
project3_output_number:
push $ra
# Check for error
beq $a1, $0, UART_no_error_p3
nop
addiu $t0, $0, 1
li $a0, invalid_char_p3
beq $a1, $t0, display_error_message_p3
nop
addiu $t0, $t0, 1
li $a0, invalid_expression_p3
beq $a1, $t0, display_error_message_p3
nop
addiu $t0, $t0, 1
li $a0, input_overflow_p3
beq $a1, $t0, display_error_message_p3
nop
addiu $t0, $t0, 1
li $a0, output_overflow_p3
beq $a1, $t0, display_error_message_p3
nop
li $a0, unknown_error_p3
display_error_message_p3:
jal libplp_uart_write_string_p3
nop
pop $ra
return
UART_no_error_p3:
#INITIALIZATIONS
# Saved Values
lui $s0, 0xF000 #UART
li $s1, 0x1 #mask for bit 0, used by put_char
li $s2, 0x2 #mask for bit 1, used by get_char
li $s3, 10 #used by decimal_to_binary
li $s4, 48 #subtract from ascii for decimal, used by decimal_to_binary
# Temporary Values
li $t0, 0 #counter in subroutines
li $t1, 0 #x: used for UART character reads and writes
# check if 0
bne $a0, $0, non-zero_input_p3
nop
addiu $t1, $0, '0' # set UART output value to '0'
jal put_char_p3
nop
addiu $t1, $0, 10 # set UART output value to ' '
jal put_char_p3
nop
pop $ra
return
non-zero_input_p3:
# Get sign bit
move $t2, $a0
srl $t2, $t2, 31
beq $t2, $0, non_negative_output_p3
nop
jal handle_negative_p3
nop
non_negative_output_p3:
jal to_bcd_p3
nop
jal display_bcd_p3
nop
pop $ra
return
#=======================================FUNCTIONS=======================================
#Description: places number result ($s7) into the space at the label bcd_storage
#Resources: #uses $t0, t2, $t3, $t4
to_bcd_p3:
push $ra
li $s5, bcd_storage_p3
#set 10's place followed call subtract function to store bcd
li $s6, 1000000000 #10
jal base10_subtract
nop
li $s6, 100000000 #9
jal base10_subtract
nop
li $s6, 10000000 #8
jal base10_subtract
nop
li $s6, 1000000 #7
jal base10_subtract
nop
li $s6, 100000 #6
jal base10_subtract
nop
li $s6, 10000 #5
jal base10_subtract
nop
li $s6, 1000 #4
jal base10_subtract
nop
li $s6, 100 #3
jal base10_subtract
nop
li $s6, 10 #2
jal base10_subtract
nop
li $s6, 1 #1
jal base10_subtract
nop
pop $ra
jr $ra
nop
#Description: repeats $s7 - $s6 as many times as possilbe, where s6 is a multiple of 10
#Resources: #uses $t0, t2, $t3, $t4
base10_subtract:
li $t5, 0 # bcd place value
sltu $t6, $a0, $s6 # determine if number less than decimal place
subtract_loop_p3:
bne $t6, $0, exit_sub_loop_p3
nop
subu $a0, $a0, $s6
addiu $t5, $t5, 1 #incriment bcd
sltu $t6, $a0, $s6 # determine if number less than decimal place
j subtract_loop_p3
nop
exit_sub_loop_p3:
sw $t5, 0($s5) # store bcd value
addiu $s5, $s5, 4 # increment BCD pointer to next word
jr $ra
nop
#Description: places number result ($s7) into the space at the label bcd_storage
#Resources: #uses $t0, t2, $t3, $t4
display_bcd_p3:
push $ra
li $t3, bcd_storage_p3
li $t2, 10 #count down for prints
# for 10 starting at bcd_storage, add 48 and put char
lw $t1, 0($t3)
remove_preceeding_zeros_p3:
bne $t1, $0, display_bcd_loop_p3 # branch if non-zero
nop
addiu $t3, $t3, 4 # increment BCD pointer
subu $t2, $t2, $s1 # decrement $t2
lw $t1, 0($t3)
j remove_preceeding_zeros_p3
nop
display_bcd_loop_p3:
# convert to ascii and print
addu $t1, $t1, $s4
jal put_char_p3
nop
addiu $t3, $t3, 4 # increment BCD pointer
subu $t2, $t2, $s1 # decrement $t2
lw $t1, 0($t3)
bne $t2, $0, display_bcd_loop_p3
nop
addiu $t1, $0, 10 # set UART output value to ' '
jal put_char_p3
nop
pop $ra
jr $ra
nop
#Description: outputs negative sign and converts from 2's compliment
# Resources: $s0 = UART, $s1 = 1, $a0 = number to convert, $t1 = character to print
handle_negative_p3:
push $ra # save return address
addiu $t1, $0, '-' # set UART output value to '-'
jal put_char_p3
nop
pop $ra # restore return address
# 2's compliment conversion
nor $a0, $a0, $a0
addu $a0, $a0, $s1
jr $ra
nop
#Description: Writes $t1 to UART
#Resources: $s0 = UART, $s1 = 1, $t0 = temp
put_char_p3:
lw $t0, 4($s0) # load status register
and $t0, $t0, $s1 # mask for clear to send
bne $t0, $s1, put_char_p3
nop
sw $t1, 12($s0) # store in send buffer
sw $s1, 0($s0) # command register: send
jr $ra
nop
# From PLP UART Library
libplp_uart_write_p3:
lui $t0, 0xf000 #uart base address
libplp_uart_write_loop_p3:
lw $t1, 4($t0) #get the uart status
andi $t1, $t1, 0x01 #mask for the cts bit
beq $t1, $zero, libplp_uart_write_loop_p3
nop
sw $a0, 12($t0) #write the data to the output buffer
sw $t1, 0($t0) #send the data!
jr $31
nop
libplp_uart_write_string_p3: #we have a pointer to the string in a0, just loop and increment until we see a
move $t9, $31 #save the return address
move $t8, $a0 #save the argument
libplp_uart_write_string_multi_word_p3:
lw $a0, 0($t8) #first 1-4 characters
ori $t0, $zero, 0x00ff #reverse the word to make it big endian
and $t1, $t0, $a0 #least significant byte
sll $t1, $t1, 24
srl $a0, $a0, 8
and $t2, $t0, $a0 #second byte
sll $t2, $t2, 16
srl $a0, $a0, 8
and $t3, $t0, $a0 #third byte
sll $t3, $t3, 8
srl $a0, $a0, 8 #last byte in a0
or $a0, $t1, $a0
or $a0, $t2, $a0
or $a0, $t3, $a0
beq $a0, $zero, libplp_uart_write_string_done_p3
nop
ori $t7, $zero, 4
libplp_uart_write_string_loop_p3:
jal libplp_uart_write_p3 #write this byte
addiu $t7, $t7, -1
srl $a0, $a0, 8
bne $a0, $zero, libplp_uart_write_string_loop_p3
nop
beq $t7, $zero, libplp_uart_write_string_multi_word_p3
addiu $t8, $t8, 4 #increment for the next word
libplp_uart_write_string_done_p3:
jr $t9 #go home
nop
arithmetic.asm
arithmetic:
li $t0 0x2a # asterisk
beq $s4 $t0 multiplication
nop
li $t0 0x2b # plus sign
beq $s4 $t0 addition
nop
li $t0 0x2d # hyphen
beq $s4 $t0 subtraction
nop
_arithmetic_end:
jr $ra
nop
# Multiplication
multiplication:
li $v0 0
beq $s2 $zero _arithmetic_end
nop
beq $s3 $zero _arithmetic_end
nop
li $t0 1
beq $t0 $s2 _one_left_multiplication # Short circuit trivial multiplication.
nop
beq $t0 $s3 _one_right_multiplication # Short circuit trivial multiplication.
nop
li $t0 -1
beq $t0 $s2 _neg_one_left_multiplication # Short circuit trivial multiplication.
nop
beq $t0 $s3 _neg_one_right_multiplication # Short circuit trivial multiplication.
nop
push $ra
mullo $t0 $s2 $s3
move $a0 $t0
move $a1 $s3
jal signed_division
nop
bne $s2 $v0 output_overflow_error
nop
move $v0 $t0
pop $ra
j _arithmetic_end
nop
_one_left_multiplication:
move $v0 $s3
j _arithmetic_end
nop
_one_right_multiplication:
move $v0 $s2
j _arithmetic_end
nop
_neg_one_left_multiplication:
li $t1 -2147483648
beq $t1 $s3 output_overflow_error
nop
mullo $v0 $s3 $t0
j _arithmetic_end
nop
_neg_one_right_multiplication:
li $t1 -2147483648
beq $t1 $s2 output_overflow_error
nop
mullo $v0 $s2 $t0
j _arithmetic_end
nop
# Addition and subtraction
addition:
bne $s0 $s1 _addition_diff_sign
nop
li $t0 1
_addition_anchor1:
addu $v0 $s2 $s3
bne $t0 $zero _addition_same_sign
nop
_addition_anchor2:
j _arithmetic_end
nop
_addition_diff_sign:
li $t0 0
j _addition_anchor1
nop
_addition_same_sign:
srl $t2 $v0 31
bne $s0 $t2 output_overflow_error
nop
j _addition_anchor2
subtraction:
beq $s0 $s1 _subtraction_same_sign
nop
li $t0 1
_subtraction_anchor1:
subu $v0 $s2 $s3
bne $t0 $zero _subtraction_diff_sign
nop
_subtraction_anchor2:
j _arithmetic_end
nop
_subtraction_same_sign:
li $t0 0
j _subtraction_anchor1
nop
_subtraction_diff_sign:
srl $t2 $v0 31
bne $s0 $t2 output_overflow_error
nop
j _subtraction_anchor2
nop
errors.asm
# errors
invalid_character_error:
li $a1 1
call project3_output_number
nop
jal exhaust_bits
nop
j main
nop
invalid_expression_error:
li $a1 2
call project3_output_number
nop
jal exhaust_bits
nop
j main
nop
input_overflow_error:
li $a1 3
call project3_output_number
nop
jal exhaust_bits
nop
j main
nop
output_overflow_error:
li $a1 4
call project3_output_number
nop
jal exhaust_bits
nop
j main
nop
unknown_error:
li $a1 5
call project3_output_number
nop
jal exhaust_bits
nop
j main
nop
states.asm
# Implementations of the project 3 state machine.
# $s0 Left operand negative flag.
# $s1 Right operand negative flag.
# $s2 Left operand.
# $s3 Right operand.
# $s4 Operator
# $s5 Temp accumulator for building an operand
state_0:
jal get_uart
nop
move $a0 $v0
li $t0 45 # Hyphen
beq $a0 $t0 state_1b
nop
jal is_operator
nop
bne $v0 $zero invalid_expression_error
nop
li $t0 61 # Equals sign
beq $a0 $t0 invalid_expression_error
nop
jal is_numeric
nop
beq $v0 $zero invalid_character_error
nop
jal append_to_left_value
nop
j state_1a
nop
state_1b:
li $s0 1
jal get_uart
nop
move $a0 $v0
jal is_operator
nop
bne $v0 $zero invalid_expression_error
nop
li $t0 61 # Equals sign
beq $a0 $t0 invalid_expression_error
nop
jal is_numeric
nop
beq $v0 $zero invalid_character_error
nop
jal append_to_left_value
nop
j state_1a
nop
state_1a:
jal get_uart
nop
move $a0 $v0
jal is_operator
nop
bne $zero $v0 state_2_transition
nop
li $t0 61 # Equals sign
beq $a0 $t0 invalid_expression_error
nop
jal is_numeric
nop
beq $v0 $zero invalid_character_error
nop
jal append_to_left_value
nop
j state_1a
nop
state_2_transition:
move $s4 $a0
move $s2 $s5
move $s5 $zero
j state_2
nop
state_2:
jal get_uart
nop
move $a0 $v0
li $t0 45 # Hyphen
beq $a0 $t0 state_3a
nop
jal is_operator
nop
bne $v0 $zero invalid_expression_error
nop
li $t0 61 # Equals sign
beq $a0 $t0 invalid_expression_error
nop
jal is_numeric
nop
beq $v0 $zero invalid_character_error
nop
jal append_to_right_value
nop
j state_3b
nop
state_3a:
li $s1 1
jal get_uart
nop
move $a0 $v0
jal is_operator
nop
bne $v0 $zero invalid_expression_error
nop
li $t0 61 # Equals sign
beq $a0 $t0 invalid_expression_error
nop
jal is_numeric
nop
beq $v0 $zero invalid_character_error
nop
jal append_to_right_value
nop
j state_3b
nop
state_3b:
jal get_uart
nop
move $a0 $v0
jal is_operator
nop
bne $v0 $zero compound_eval_transition
nop
li $t0 61 # Equals sign
beq $a0 $t0 final_eval_transition
nop
jal is_numeric
nop
beq $v0 $zero invalid_character_error
nop
jal append_to_right_value
nop
j state_3b
nop
compound_eval_transition:
push $a0
move $s3 $s5
jal arithmetic
nop
move $s2 $v0
srl $s0 $s2 31
pop $a0
move $s4 $a0
move $s3 $zero
move $s1 $zero
move $s5 $zero
j state_2
nop
final_eval_transition:
move $s3 $s5
jal arithmetic
nop
move $a0 $v0
li $a1 0
call project3_output_number
nop
j main
nop
consultants.asm
# Consultants for project 3.
#
# Questions:
# is_numeric
# is_operator
is_numeric:
push $t0
push $t1
push $t2
li $t2 47
sltu $t0 $t2 $a0 # (47 < val) ? 1 : 0
sltiu $t1 $a0 58 # (val < 58) ? 1 : 0
and $v0 $t0 $t1
pop $t2
pop $t1
pop $t0
jr $ra
nop
is_operator:
# * 42
# + 43
# - 45
push $t0
move $v0 $zero
li $t0 42
beq $a0 $t0 _is_operator_set_true # *
nop
li $t0 43
beq $a0 $t0 _is_operator_set_true # +
nop
li $t0 45
beq $a0 $t0 _is_operator_set_true # -
nop
_is_operator_return:
pop $t0
jr $ra
nop
_is_operator_set_true:
li $v0 1
j _is_operator_return
nop
will_overflow:
# Takes is $a0 to indicate desired signage.
# 0 - Positive
# 1 - Negative
#
# Predicts whether or not enlarging the current accumulator
# by multiplying it by 10 will cause an overflow.
push $t0
beq $a0 $zero _positive_prediction
nop
bne $a0 $zero _negative_prediction
nop
_positive_prediction:
li $t0 214748364
sltu $v0 $t0 $s5
j _return_prediction
nop
_negative_prediction:
li $t0 -214748364
slt $v0 $s5 $t0
_return_prediction:
pop $t0
jr $ra
no
division.asm
# $a0 / $a1
unsigned_division:
push $ra
push $t0
push $t1
push $t2
push $t3
move $v0 $zero
beq $a1 $zero _zero_division
nop
beq $a0 $a1 _equal_operands
nop
move $t0 $a0 # $t0 dividend
move $t1 $a1 # $t1 divisor
li $t2 1 # mask
align:
sltu $t3 $t1 $t0
bne $t3 $zero _shift_left
nop
_do:
sltu $t3 $t1 $t0 # divisor < dividend
bne $t3 $zero _math
nop
subu $t3 $t1 $t0
beq $t3 $zero _math
nop
_while:
srl $t1 $t1 1
srl $t2 $t2 1
bne $t2 $zero _do
nop
pop $t3
pop $t2
pop $t1
pop $t0
pop $ra
jr $ra
nop
signed_division:
push $ra
push $t0
push $t1
push $t2
push $t3
push $t4
push $t5
#move $t6 $ra # Save the original ra
move $t0 $a0 # Dividend
move $t1 $a1 # Divisor
move $t2 $zero # Sign
_check_negatives:
slti $t3 $t0 0 # if (a < 0)
bne $t3 $zero _negate_dividend
nop
_continue_negative_check:
slti $t3 $t1 0 # if (a < 0)
bne $t3 $zero _negate_divisor
nop
_end_negative_check:
move $a0 $t0
move $a1 $t1
jal unsigned_division
nop
bne $t2 $zero _negate_result
nop
_done_with_division:
pop $t5
pop $t4
pop $t3
pop $t2
pop $t1
pop $t0
pop $ra
jr $ra
nop
_negate_dividend:
jal _toggle_sign
li $t3 0xFFFFFFFF
mulhi $t4 $t0 $t3
mullo $t5 $t0 $t3
addu $t0 $t4 $t5
j _continue_negative_check
nop
_negate_divisor:
jal _toggle_sign
li $t3 0xFFFFFFFF
mulhi $t4 $t1 $t3
mullo $t5 $t1 $t3
addu $t1 $t4 $t5
j _end_negative_check
nop
_negate_result:
nor $v0 $v0 $zero
addiu $v0 $v0 1
j _done_with_division
nop
_toggle_sign:
nor $t2 $t2 $zero
jr $ra
nop
_math:
subu $t0 $t0 $t1
addu $v0 $v0 $t2
j _while
nop
_shift_left:
sll $t1 $t1 1
sll $t2 $t2 1
j align
nop
_zero_division:
li $v0 0
jr $ra
nop
_equal_operands:
li $v0 1
jr $ra
nop
main.asm
# main source file
.org 0x10000000
li $sp 0x10FFFFFC
main:
move $s0 $zero # Left operand negative flag.
move $s1 $zero # Right operand negative flag.
move $s2 $zero # Left operand.
move $s3 $zero # Right operand.
move $s4 $zero # Operator
move $s5 $zero # Temp accumulator for building an operand
j state_0
nop
tools.asm
#
# Includes:
# append_to_left_value
# append_to_right_value
# get_uart
append_to_left_value:
push $t0
push $a0
move $t0 $ra
move $a0 $s0
jal will_overflow
nop
bne $v0 $zero input_overflow_error
nop
jal _enlarge_accumulator
nop
move $ra $t0
pop $a0
pop $t0
andi $a0 $a0 0b1111 # Save ASCII to $a0
bne $s0 $zero _append_value_negative
nop
beq $s0 $zero _append_value_positive
nop
append_to_right_value:
push $t0
push $a0
move $t0 $ra
move $a0 $s1
jal will_overflow
nop
bne $v0 $zero input_overflow_error
nop
jal _enlarge_accumulator
nop
move $ra $t0
pop $a0
pop $t0
andi $a0 $a0 0b1111 # Save ASCII to $a0
bne $s1 $zero _append_value_negative
nop
beq $s1 $zero _append_value_positive
nop
_append_value_negative:
subu $s5 $s5 $a0
push $t0
li $t0 0x80000000
and $t0 $t0 $s5
beq $t0 $zero input_overflow_error
nop
pop $t0
jr $ra
nop
_append_value_positive:
addu $s5 $s5 $a0
push $t0
li $t0 0x80000000
and $t0 $t0 $s5
bne $t0 $zero input_overflow_error
nop
pop $t0
jr $ra
nop
_enlarge_accumulator:
push $ra
push $t0
push $t1
push $t2
push $t3
move $t0 $s5 # Copy current accumulator
li $t1 10
mulhi $t2 $t0 $t1 # Mulhi copy of accumulator
mullo $t3 $t0 $t1 # Mullo copy of accumulator
beq $a0 $zero _concatenate_positive
nop
bne $a0 $zero _concatenate_negative
nop
_return_enlarge:
move $s5 $t0
pop $t3
pop $t2
pop $t1
pop $t0
pop $ra
jr $ra
nop
_concatenate_positive:
or $t0 $t2 $t3
j _return_enlarge
nop
_concatenate_negative:
and $t0 $t2 $t3
j _return_enlarge
nop
get_uart:
push $t0
push $t1
# UART Location.
li $t0 0xF0000000
li $t1 0b10
_wait_for_input:
# Check if we have any input, wait until otherwise.
lw $t2 4($t0)
and $t2 $t2 $t1
bne $t1 $t2 _wait_for_input
nop
# Retrieve value.
lw $v0 8($t0)
# Reset ready bit
lw $t1 0($t0)
ori $t1 $t1 0b10
sw $t1 0($t0)
pop $t1
pop $t0
jr $ra
nop
consume_bad_expression:
# UART Location.
push $t0
push $t1
push $t2
li $t0 0xF0000000
_consume_expression:
# Check if we have any input.
# If not, then we are at the end of the expression.
lw $t0 4($t0)
andi $t0 $t0 0b10
bne $t0 $t1 _consumed_expression
# Retrieve value.
lw $v0 8($t0)
# Reset ready bit
lw $t1 0($t0)
ori $t1 $t1 0b10
sw $t1 0($t0)
li $t2 61 # = symbol
bne $t1 $t2 _consume_expression
nop
_consumed_expression:
pop $t2
pop $t1
pop $t0
jr $ra
nop
exhaust_bits:
li $t1 0xF0000000
lw $t0 4($t1)
andi $t0 $t0 0x0002 # mask ready bit
beq $t0 $zero _exhaust_bits_return # if ready bit == 0
nop
li $t0 0x0002
sw $t0 0($t1) # write 1 to clear status bit
j exhaust_bits
nop
_exhaust_bits_return:
jr $ra
nop
Related Questions
drjack9650@gmail.com
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.