summaryrefslogblamecommitdiff
path: root/cpu/os.asm
blob: 210f3a0e4e40885626ea1fb3afb33f2bd56503ac (plain) (tree)
1
2
3
4
5
6
7
8
             




                                                                                        
 
     


                                                  

                       
                 










                   
 
           

                          
                           
               
                   

                           
               

                   
 
                  
                     
                
 



                                                    
           


























                        

               

















                        
          

         













                                                  
                 








































                                             











































                                 


                                                   
                               



               
                   
                
                   
             
                   














                          
    





























































                            
                   
                     
 






                          




          













































                                                                             

                                                     
            
                
                  
             
                         

             

                       

         







                                                                                                         
                         

                
                            
            
                    



                     
             
                     
                    
                 
         




                                                                                  


                  
                   


            
                 


                 
                         





                
                   


                 
                          
               
                  
                   


              
                      
 
                 


          
 
                                         






















              

                                       
























                                       
           




              
             

             







                




                                                    
 


                        

                 
         
                             
        




                



                                     
 
 

                
                                                          
        
                                  
          
                                      
       
                
         
                  
      
                                            
      
                                            
      
                                            
      
                                            

 


                                            




                                                          

     
                                                                    

            
                                                          


             











          



 
# 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:
    li D cmdline
    lw C cmdline_used
    add D D C
    sb B 0(D)
    incri C 1
    sw C cmdline_used
    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