mainloop: la ra,uidrawend # Will call a function to draw the current page, but it will return to the end instead of here la t0,uipage lbu t0,0(t0) beqz t0,uidrawidle # Page 0: Idle screen li t1,1 beq t0,t1,uidrawmainmenu # Page 1: Main menu li t1,2 beq t0,t1,uidrawcountryselect # Page 2: Country selection li t1,3 beq t0,t1,uidrawadjusttiming # Page 3: Timing adjustments li t1,4 beq t0,t1,uidrawgatedropdelay # Page 4: Gate drop delay adjustment li t1,5 beq t0,t1,uidrawinitialtime # Page 5: Initial active time adjustment li t1,6 beq t0,t1,uidrawextensiontime # Page 6: Extension time adjustment li t1,7 beq t0,t1,uidrawinvalidinput # Page 7: Invalid (time) input j uidrawunknown uidrawend: wfi # Program is entirely interrupt-driven, so wait for something to happen j mainloop handler_console: li t0,262144 # 2^18, value of MCIP bit csrc mip,t0 la ra,uihandlingend # Will call a function to handle input for the current page, but it will return to the end instead of here la t0,uipage lbu t0,0(t0) beqz t0,uihandleidle # Page 0: Idle screen li t1,1 beq t0,t1,uihandlemainmenu # Page 1: Main menu li t1,2 beq t0,t1,uihandlecountryselect # Page 2: Country selection li t1,3 beq t0,t1,uihandleadjusttiming # Page 3: Timing adjustments li t1,4 beq t0,t1,uihandlegatedropdelay # Page 4: Gate drop delay adjustment li t1,5 beq t0,t1,uihandleinitialtime # Page 5: Initial active time adjustment li t1,6 beq t0,t1,uihandleextensiontime # Page 6: Extension time adjustment li t1,7 beq t0,t1,uihandleinvalidinput # Page 7: Invalid (time) input uihandlingend: li a7,131 ecall # Drop anything else the user may have typed j handler_epilogue copystring: # Destination pointer in a0, source pointer in a1, copies until null is reached (but doesn't copy the null), not very optimized lb t0,0(a1) beqz t0,1f sb t0,0(a0) addi a0,a0,1 addi a1,a1,1 j copystring 1: ret uidrawidle: cm.push {ra},-16 la a0,uiidle+44 la a3,uiidle la t0,currstate # Check if currently running or not lbu t0,0(t0) andi t0,t0,0xf beqz t0,1f la a0,uiactive+44 # If so, then show the active message instead la a3,uiactive 1: la a1,uide la t0,country lb t0,0(t0) li t1,1 bne t0,t1,1f la a1,uiuk 1: li t1,2 bne t0,t1,1f la a1,uius 1: call copystring mv a0,a3 li a7,4 ecall cm.popret {ra},16 uihandleidle: cm.push {ra},-16 li a7,132 ecall # Read one character of input li t0,'m' bne a0,t0,1f # If it was 'm'... la t0,uipage li t1,1 sb t1,0(t0) # ...go to the main menu (page 1) 1: li t0,'a' bne a0,t0,1f # If it was 'a', then start the sequence now la t0,currstate lbu t0,0(t0) andi t0,t0,0xf bnez t0,1f # Unless it's already running, that is la t1,country lb t1,0(t1) # Read current country setting slli t1,t1,4 # Multiply by 16 to get the offset into the state table addi t1,t1,1 # And add 1 to get to the first non-idle state la t0,currstate sb t1,0(t0) # Update state in RAM to the first state in the current country la t0,states sh2add t0,t1,t0 # Calculate the proper offset in the list of pointers (state number * 4 + base address) lw t0,0(t0) # Load the pointer jalr ra,t0,0 # And call that function 1: cm.popret {ra},16 ret uidrawmainmenu: la a0,uimainmenu li a7,4 ecall ret uihandlemainmenu: li a7,132 ecall # Read one character of input li t0,'1' bne a0,t0,1f la t1,uipage li t2,2 sb t2,0(t1) # User entered '1', go to country selection (page 2) 1: li t0,'2' bne a0,t0,1f la t1,uipage li t2,3 sb t2,0(t1) # User entered '2', go to timing adjustment (page 3) 1: li t0,'0' bne a0,t0,1f la t1,uipage sb x0,0(t1) # User entered '0', go to idle screen (page 0) 1: ret uidrawcountryselect: la a0,uicountryselect li a7,4 ecall ret uihandlecountryselect: li a7,132 ecall # Read one character of input li t0,'1' bne a0,t0,1f la t1,country sb x0,0(t1) # User entered '1', change country to DE (0) la t1,uipage li t2,1 sb t2,0(t1) # Return to main menu (page 1) 1: li t0,'2' bne a0,t0,1f la t1,country li t2,1 sb t2,0(t1) # User entered '2', change country to UK (1) la t1,uipage li t2,1 sb t2,0(t1) # Return to main menu (page 1) 1: li t0,'3' bne a0,t0,1f la t1,country li t2,2 sb t2,0(t1) # User entered '3', change country to US (2) la t1,uipage li t2,1 sb t2,0(t1) # Return to main menu (page 1) 1: li t0,'0' bne a0,t0,1f la t1,uipage li t2,1 sb t2,0(t1) # User entered '0', return to main menu (page 1) without changing anything 1: ret uidrawadjusttiming: la a0,uiadjusttiming li a7,4 ecall ret uihandleadjusttiming: li a7,132 ecall # Read one character of input li t0,'1' bne a0,t0,1f la t1,uipage li t2,4 sb t2,0(t1) # User entered '1', go to gate drop time setting (page 4) 1: li t0,'2' bne a0,t0,1f la t1,uipage li t2,5 sb t2,0(t1) # User entered '2', go to initial active time setting (page 5) 1: li t0,'3' bne a0,t0,1f la t1,uipage li t2,6 sb t2,0(t1) # User entered '3', go to extension time setting (page 6) 1: li t0,'0' bne a0,t0,1f la t1,uipage li t2,1 sb t2,0(t1) # User entered '0', go to main menu (page 1) 1: ret ret uidrawgatedropdelay: la a0,uigatedropdelay la t0,gatedroptime lbu t0,0(t0) # Load the current setting li t1,10 div t2,t0,t1 # Split it into tens and ones places rem t3,t0,t1 addi t2,t2,0x30 # Convert to ASCII addi t3,t3,0x30 sb t2,27(a0) # Insert the digits into the string sb t3,28(a0) li a7,4 ecall # And print it ret uihandlegatedropdelay: # Read first digit li a7,132 ecall # Check if the user wanted to cancel li t0,'c' bne a0,t0,1f la t0,uipage li t1,3 # User chose to cancel, go back to timing adjustment (page 3) sb t1,0(t0) ret 1: # Validate and process first digit addi s0,a0,-0x30 # Convert the first digit to ASCII li t0,10 bltu s0,t0,1f # Inputs less than 0x30 become negative in the addi above, this branch is unsigned so those become large positive numbers la t0,uipage # ^ Thank the RISC-V manual for that trick li t1,7 sb t1,0(t0) # First digit was not valid, go to invalid input error (page 7) ret 1: mul s0,s0,t0 # Multiply the first digit by 10 # Read second digit li a7,132 ecall bnez a0,1f div s0,s0,t0 # Second digit was null, so the first digit was really the ones place li a0,0x30 # Pretend this digit was 0 so validation still passes but the addition doesn't change anything 1: addi a0,a0,-0x30 bltu a0,t0,1f # Inputs less than 0x30 become negative in the addi above la t0,uipage li t1,7 sb t1,0(t0) # Second digit was not valid, go to invalid input error (page 7) ret 1: add s0,s0,a0 # Sum the two digits together bnez s0,1f la t0,uipage li t1,7 sb t1,0(t0) # Input was 0, go to invalid input error (page 7) ret 1: la t0,gatedroptime sb s0,0(t0) # Write out the new setting la t0,uipage li t1,3 # Go back to timing adjustment (page 3) sb t1,0(t0) ret uidrawinitialtime: la a0,uiinitialtime la t0,activetime lbu t0,0(t0) # Load the current setting li t1,10 div t2,t0,t1 # Split it into tens and ones places rem t3,t0,t1 addi t2,t2,0x30 # Convert to ASCII addi t3,t3,0x30 sb t2,29(a0) # Insert the digits into the string sb t3,30(a0) li a7,4 ecall # And print it ret uihandleinitialtime: # Read first digit li a7,132 ecall # Check if the user wanted to cancel li t0,'c' bne a0,t0,1f la t0,uipage li t1,3 # User chose to cancel, go back to timing adjustment (page 3) sb t1,0(t0) ret 1: # Validate and process first digit addi s0,a0,-0x30 # Convert the first digit to ASCII li t0,10 bltu s0,t0,1f # Inputs less than 0x30 become negative in the addi above, this branch is unsigned so those become large positive numbers la t0,uipage # ^ Thank the RISC-V manual for that trick li t1,7 sb t1,0(t0) # First digit was not valid, go to invalid input error (page 7) ret 1: mul s0,s0,t0 # Multiply the first digit by 10 # Read second digit li a7,132 ecall bnez a0,1f div s0,s0,t0 # Second digit was null, so the first digit was really the ones place li a0,0x30 # Pretend this digit was 0 so validation still passes but the addition doesn't change anything 1: addi a0,a0,-0x30 bltu a0,t0,1f # Inputs less than 0x30 become negative in the addi above la t0,uipage li t1,7 sb t1,0(t0) # Second digit was not valid, go to invalid input error (page 7) ret 1: add s0,s0,a0 # Sum the two digits together bnez s0,1f la t0,uipage li t1,7 sb t1,0(t0) # Input was 0, go to invalid input error (page 7) ret 1: la t0,activetime sb s0,0(t0) # Write out the new setting la t0,uipage li t1,3 # Go back to timing adjustment (page 3) sb t1,0(t0) ret uidrawextensiontime: la a0,uiextensiontime la t0,extensiontime lbu t0,0(t0) # Load the current setting li t1,10 div t2,t0,t1 # Split it into tens and ones places rem t3,t0,t1 addi t2,t2,0x30 # Convert to ASCII addi t3,t3,0x30 sb t2,26(a0) # Insert the digits into the string sb t3,27(a0) li a7,4 ecall # And print it ret uihandleextensiontime: # Read first digit li a7,132 ecall # Check if the user wanted to cancel li t0,'c' bne a0,t0,1f la t0,uipage li t1,3 # User chose to cancel, go back to timing adjustment (page 3) sb t1,0(t0) ret 1: # Validate and process first digit addi s0,a0,-0x30 # Convert the first digit to ASCII li t0,10 bltu s0,t0,1f # Inputs less than 0x30 become negative in the addi above, this branch is unsigned so those become large positive numbers la t0,uipage # ^ Thank the RISC-V manual for that trick li t1,7 sb t1,0(t0) # First digit was not valid, go to invalid input error (page 7) ret 1: mul s0,s0,t0 # Multiply the first digit by 10 # Read second digit li a7,132 ecall bnez a0,1f div s0,s0,t0 # Second digit was null, so the first digit was really the ones place li a0,0x30 # Pretend this digit was 0 so validation still passes but the addition doesn't change anything 1: addi a0,a0,-0x30 bltu a0,t0,1f # Inputs less than 0x30 become negative in the addi above la t0,uipage li t1,7 sb t1,0(t0) # Second digit was not valid, go to invalid input error (page 7) ret 1: add s0,s0,a0 # Sum the two digits together bnez s0,1f la t0,uipage li t1,7 sb t1,0(t0) # Input was 0, go to invalid input error (page 7) ret 1: la t0,extensiontime sb s0,0(t0) # Write out the new setting la t0,uipage li t1,3 # Go back to timing adjustment (page 3) sb t1,0(t0) ret uidrawinvalidinput: la a0,uiinvalidinput li a7,4 ecall ret uihandleinvalidinput: la t0,uipage li t1,3 # Go back to timing adjustment (page 3) sb t1,0(t0) ret uidrawunknown: la a0,uiunknown li a7,4 ecall ret uipage: .byte 0 uiidle: .asciz "\nRailroad Crossing 2\nfor RVController\nMode: ??\n\nPress 'm' for menu\n'a' to activate now" uiactive: .asciz "\nRailroad Crossing 2\nfor RVController\nMode: ??\nCrossing is active\n\nPress 'm' for menu" uimainmenu: .asciz "\nMAIN MENU\n1: Change Country\n2: Adjust Timing\n\n\n0: Exit" uiunknown: .asciz "\nInternal error\n\nUnknown UI page\n\n\n" uicountryselect: .asciz "\nCHANGE COUNTRY\n1: Germany\n2: United Kingdom\n3: United States\n\n0: Cancel" uiadjusttiming: .asciz "\nADJUST TIMING\n1: Gate Drop Delay\n2: Init. Active Time\n3: Extension Time\n\n0: Cancel" uigatedropdelay: .asciz "\nGATE DROP DELAY\n\nCurrent: XX sec\n\nEnter new time or\n'c' to cancel:" uiinitialtime: .asciz "\nINIT. ACTIVE TIME\n\nCurrent: XX sec\n\nEnter new time or\n'c' to cancel:" uiextensiontime: .asciz "\nEXTENSION TIME\n\nCurrent: XX sec\n\nEnter new time or\n'c' to cancel:" uiinvalidinput: .asciz "\nInvalid entry\n\nPress any key\n\n\n" uide: .asciz "DE" uiuk: .asciz "UK" uius: .asciz "US"