blob: 3fbf757812437d02e8504efd33319dcb389077f4 (
plain) (
tree)
|
|
# CONVENTION:
# Return value for functions : in register A
# Arguments for functions : registers A, B, C, D
# All registers are caller-saved, except SP which is preserved by function calls
# Labels beginning with '_' are not meant to be used outside of the function where
# they are declared.
.text
# PROCEDURE: main loop
# ROLE: starts with the CPU then runs continuously
jal run_unit_tests
li A msghello
jal ser_out_str
li A 1994
li B var_year
sw A 0(B)
li A 2
li B var_month
sb A 0(B)
li A 26
li B var_day
sb A 0(B)
_main_loop:
# Process serial input
jal check_input
jz A _end_process_input
jal run_cmd
_end_process_input:
# Process clock ticking
li B _clock
lw A 0(B)
jz A _main_loop
jal incr_clock
jal disp_clock_d7
j _main_loop
# PROCEDURE: incr_clock
# ROLE: take into account seconds increment
# ARGUMENTS: number of seconds to add, in register A
incr_clock:
push RA
jz A _incr_clock_ret
lb B var_sec
add B B A
li C 60
divu A B C
move B E
sb B var_sec
jz A _incr_clock_ret
lb B var_min
add B B A
divu A B C
move B E
sb B var_min
jz A _incr_clock_ret
lb B var_hour
add B B A
li C 24
divu A B C
move B E
sb B var_hour
jz A _incr_clock_ret
lb B var_day
add B B A
jal get_dom
divu A B A
move B E
sb B var_day
jz A _incr_clock_ret
lb B var_month
add B B A
li C 12
divu A B C
move B E
sb B var_month
jz A _incr_clock_ret
lw B var_year
add B B A
sw B var_year
_incr_clock_ret:
pop RA
jr RA
# PROCEDURE: get_dom
# ROLE: how many days in current month ?
# RETURN VALUE: the result, in register A.
# PRESERVES REGISTERS: B
get_dom:
lb C var_month
li D days_in_month
add D D C
lb A 0(D)
jr RA
# PROCEDURE: disp_clock_d7
# ROLE: display current time (H M S) to D7 display
# ARGUMENTS: none
disp_clock_d7:
li D 10
lb A var_sec
divu A A D
move B E
push B
push A
push D
lb A var_min
divu A A D
move B E
push B
push A
push D
lb A var_hour
divu A A D
move B E
push B
push A
li D d7_digits
li C 8
li B 0x4200
_disp_d7_loop:
pop A
add A A D
lb A 0(A)
sb A 0(B)
incri B 1
incri C -1
jnz C _disp_d7_loop
jr RA
# PROCEDURE: disp_clock_ser
# ROLE: display current time to serial output
# ARGUMENTS: none
disp_clock_ser:
push RA
lw A var_year
jal ser_out_int_dec
li A '-'
li E _output
sb A 0(E)
lb A var_month
incri A 1
jal ser_out_int_dec_two_digit
li A '-'
li E _output
sb A 0(E)
lb A var_day
incri A 1
jal ser_out_int_dec_two_digit
li A ' '
li E _output
sb A 0(E)
lb A var_hour
jal ser_out_int_dec_two_digit
li A ':'
li E _output
sb A 0(E)
lb A var_min
jal ser_out_int_dec_two_digit
li A ':'
li E _output
sb A 0(E)
lb A var_sec
jal ser_out_int_dec_two_digit
li A '\n'
li E _output
sb A 0(E)
pop RA
jr RA
# PROCEDURE: run_cmd
# ROLE: execute and clear command stored in cmdline
# ARGUMENTS: none
# COMMAND FORMAT:[hmsYMD][0-9]+
run_cmd:
push RA
li A prompt
jal ser_out_str
li A cmdline
jal ser_out_str
li A endl
jal ser_out_str
li A cmdline
incri A 1
li C 0
_decode_digit_loop:
lb B 0(A)
jz B _decode_digit_end
li D 10
mulu C C D
li D '0'
sub B B D
add C C B
incri A 1
j _decode_digit_loop
_decode_digit_end:
li A cmdline
lb A 0(A)
sei B A 's'
jz B _test_min
li D 60
sleu D D C
jnz D _error_too_big
sb C var_sec
j _return_run_cmd
_test_min:
sei B A 'm'
jz B _test_hour
li D 60
sleu D D C
jnz D _error_too_big
sb C var_min
j _return_run_cmd
_test_hour:
sei B A 'h'
jz B _test_day
li D 24
sleu D D C
jnz D _error_too_big
sb C var_hour
j _return_run_cmd
_test_day:
sei B A 'D'
jz B _test_month
move B C
incri B -1
jal get_dom
sleu D A B
jnz D _error_too_big
sb B var_day
j _return_run_cmd
_test_month:
sei B A 'M'
jz B _test_year
li D 12
sleu D D C
jnz D _error_too_big
incri C -1
sb C var_month
j _return_run_cmd
_test_year:
sei B A 'Y'
jz B _test_anything_else
sb C var_year
j _return_run_cmd
_test_anything_else:
jz A _return_run_cmd
j _error_no_such_var
_error_too_big:
li A error_too_big
jal ser_out_str
j _return_run_cmd
_error_no_such_var:
li A error_no_such_var
jal ser_out_str
_return_run_cmd:
sw Z cmdline_used
jal disp_clock_ser
pop RA
jr RA
# PROCEDURE: ser_out_int_dec
# ROLE: write a decimal integer to serial output
# ARGUMENTS: integer to write in register A
ser_out_int_dec:
jz A _display_zero
push Z
li B 10
_divide_int_by_ten:
jz A _show_dec
divu A A B
move C E
addi C C '0'
push C
j _divide_int_by_ten
_show_dec:
li C _output
pop A
jz A _return_out_dec
sb A 0(C)
j _show_dec
_return_out_dec:
jr RA
_display_zero:
li C _output
li D '0'
sb D 0(C)
jr RA
# PROCEDURE: ser_out_two_digit
# ROLE: write only the two lower digits of a decimal integer to serial output
# ARGUMENTS: integer to write in register A
ser_out_int_dec_two_digit:
li B 10
divu A A B
move C E
divu A A B
move D E
li B _output
addi D D '0'
addi C C '0'
sw D 0(B)
sw C 0(B)
jr RA
# PROCEDURE: ser_out_str
# ROLE: write null-terminated string to serial output
# ARGUMENTS: address of string in register A
ser_out_str:
li C _output
_ser_out_str_loop:
lb B 0(A)
jz B _ser_out_str_ret
sb B 0(C)
incri A 1
j _ser_out_str_loop
_ser_out_str_ret:
jr RA
# PROCEDURE: check_input
# ROLE: check if an input byte is available. if it is, and is different from '\n' (10), add it to cmdline
# ARGUMENTS: none
# RETURN VALUE: 1 if read byte was '\n', 0 otherwise
# WARNING: no buffer overflow check.
check_input:
li A _input
lb A 0(A)
jz A _check_input_ret
move B A
sei A A '\n'
jz A _ci_add_b_to_string
move B Z
_ci_add_b_to_string:
push A
li A cmdline
li D cmdline_used
lw C 0(D)
add A A C
sb B 0(A)
incri C 1
sw C 0(D)
pop A
jz A check_input
_check_input_ret:
jr RA
# PROCEDURE: run_unit_tests
# ROLE: check that CPU features work correctly ; displays message to serial output
# ARGUMENTS: none
run_unit_tests:
push RA
li A testbegin
jal ser_out_str
move B Z
_unit_test_begin:
li D testlist
add D D B
lw D 0(D)
jz D _unit_tests_done
push B
push D
li A teststr
add A A B
lw A 0(A)
jal ser_out_str
pop D
jalr D
li A testfail
jz B _unit_test_failed
li A testok
_unit_test_failed:
jal ser_out_str
pop B
addi B B 2
j _unit_test_begin
_unit_tests_done:
pop RA
jr RA
unit_test_0: # Addition / substraction
li B 1
li C 12
li D 44
add C C D
sei A C 56
and B B A
li C -7
li D 7
add C C D
se A C Z
and B B A
li C 32767
li D 32767
add C C D
li D 2
sub D Z D
se A C D
and B B A
jr RA
unit_test_1: # Unsigned multiplication
li B 1
li C 12
li D 44
mulu C C D
move D E
sei A C 528
and B B A
se A D Z
and B B A
li C 744
li D 1244
mulu C C D
move D E
sei A C 8032
and B B A
sei A D 14
and B B A
jr RA
unit_test_2: # Unsigned division
li B 1
li C 61
li D 5
divu C C D
move D E
sei A C 12
and B B A
sei A D 1
and B B A
li C 54200
li D 4203
divu C C D
move D E
sei A C 12
and B B A
sei A D 3764
and B B A
jr RA
unit_test_3: # Signed multiplication/division
li B 0
jr RA
# READ-ONLY PROGRAM DATA
# General strings
msghello:
ascii "\nHello, world!\n"
msgtick:
ascii " ..."
prompt:
ascii "\n$ "
endl:
ascii "\n"
error_too_big:
ascii "That number is too big!\n"
error_no_such_var:
ascii "No such variable...\n"
# For unit-tests
testlist:
word unit_test_0 unit_test_1 unit_test_2 unit_test_3 0
teststr:
word test0 test1 test2 test3 0
testbegin:
ascii "Runing CPU unit tests...\n"
testok:
ascii "OK\n"
testfail:
ascii "FAIL\n"
test0:
ascii "Addition/substraction.......... "
test1:
ascii "Unsigned multiplication........ "
test2:
ascii "Unsigned division.............. "
test3:
ascii "Signed multiplication/division. "
days_in_month:
byte 31 28 31 30 31 30 31 31 30 31 30 31
d7_digits:
byte 0b1110111 0b0100100 0b1011101 0b1101101 0b0101110
byte 0b1101011 0b1111011 0b0100101 0b1111111 0b1101111
byte 0
.data
# Space where command-line is buffered from serial input (256 bytes)
cmdline:
byte 256
# Number of bytes used in the command-line buffer (1 word)
cmdline_used:
word 1
var_sec:
byte 1
var_min:
byte 1
var_hour:
byte 1
var_day:
byte 1
var_month:
byte 1
var_year:
word 1
|