# Railroad Crossing 2 for RVController - User Interface # A product of Scheiße & Broken Signalling Systems, a Cheapie Systems company # This is free and unencumbered software released into the public domain. # See http://unlicense.org/ for more information # UI Pages # Page 0: Idle/Active Screen # Page 1: Main Menu # Page 2: Country Selection # Page 3: Timing Menu # Page 4: Gate Drop Time Adjustment # Page 5: Initial Active Time Adjustment # Page 6: Extension Time Adjustment # Page 7: Invalid Input Message for Time Adjustments # Page 8: Diagnostics Menu # Page 9: Output Test Menu # Page 10: Road Signal Test # Page 11: Rail Signal Test # Page 12: Bell Test # Page 13: Gate Test # Page 14: Statistics # Page 15: About 1 # Page 16: About 2 # Page 17: About 3 # Page 18: About 4 mainloop: la t0,uipage lbu t0,0(t0) # Read the desired page number addi t2,t0,1 # Landing pad label will be page number + 1 (to avoid 0) slli t2,t2,12 la t1,uidrawpointers sh2add t1,t0,t1 # Calculate the location of the pointer to the desired function lw t1,0(t1) # Load the pointer from memory jalr ra,t1,0 # And call it 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 t0,uipage lbu t0,0(t0) # Read the desired page number addi t2,t0,1 # Landing pad label will be page number + 1 (to avoid 0) slli t2,t2,12 la t1,uihandlepointers sh2add t1,t0,t1 # Calculate the location of the pointer to the desired function lw t1,0(t1) # Load the pointer from memory jalr ra,t1,0 # And call it 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 .align 2 uidrawidle: lpad 1 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 .align 2 uihandleidle: lpad 1 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 .align 2 uidrawmainmenu: lpad 2 la a0,uimainmenu li a7,4 ecall ret .align 2 uihandlemainmenu: lpad 2 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,'3' bne a0,t0,1f la t1,uipage li t2,8 sb t2,0(t1) # User entered '3', go to diagnostics (page 8) 1: li t0,'4' bne a0,t0,1f la t1,uipage li t2,15 sb t2,0(t1) # User entered '4', go to about 1 (page 15) 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 .align 2 uidrawcountryselect: lpad 3 la a0,uicountryselect li a7,4 ecall ret .align 2 uihandlecountryselect: lpad 3 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 .align 2 uidrawadjusttiming: lpad 4 la a0,uiadjusttiming li a7,4 ecall ret .align 2 uihandleadjusttiming: lpad 4 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 .align 2 uidrawgatedropdelay: lpad 5 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 .align 2 uihandlegatedropdelay: lpad 5 # 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 .align 2 uidrawinitialtime: lpad 6 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 .align 2 uihandleinitialtime: lpad 6 # 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 .align 2 uidrawextensiontime: lpad 7 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 .align 2 uihandleextensiontime: lpad 7 # 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 .align 2 uidrawinvalidinput: lpad 8 la a0,uiinvalidinput li a7,4 ecall ret .align 2 uihandleinvalidinput: lpad 8 la t0,uipage li t1,3 # Go back to timing adjustment (page 3) sb t1,0(t0) ret .align 2 uidrawdiagnostics: lpad 9 la a0,uidiagnostics li a7,4 ecall ret .align 2 uihandlediagnostics: lpad 9 li a7,132 ecall # Read one character of input li t0,'1' bne a0,t0,1f la t1,uipage li t0,9 sb t0,0(t1) # User entered '1', go to output test (page 9) 1: li t0,'2' bne a0,t0,1f la t1,uipage li t0,14 sb t0,0(t1) # User entered '1', go to statistics (page 14) 1: li t0,'0' bne a0,t0,1f la t1,uipage li t0,1 sb t0,0(t1) # User entered '0', go to main menu (page 1) 1: ret .align 2 uidrawoutputtest: lpad 10 la a0,uioutputtest li a7,4 ecall ret .align 2 uihandleoutputtest: lpad 10 li a7,132 ecall # Read one character of input li t0,'1' bne a0,t0,1f la t1,uipage li t0,10 sb t0,0(t1) # User entered '1', go to light test (page 10) 1: li t0,'2' bne a0,t0,1f la t1,uipage li t0,11 sb t0,0(t1) # User entered '2', go to signal test (page 11) 1: li t0,'3' bne a0,t0,1f la t1,uipage li t0,12 sb t0,0(t1) # User entered '3', go to bell test (page 12) 1: li t0,'4' bne a0,t0,1f la t1,uipage li t0,13 sb t0,0(t1) # User entered '4', go to gate test (page 13) 1: li t0,'0' bne a0,t0,1f la t1,uipage li t0,8 sb t0,0(t1) # User entered '0', go to diagnostics (page 8) 1: ret .align 2 uidrawlighttest: lpad 11 la a0,uilighttest li a7,4 ecall ret .align 2 uihandlelighttest: lpad 11 li a7,132 ecall # Read one character of input li t0,'1' bne a0,t0,1f la a0,lightchannel la a1,lightoffmsg li a7,129 ecall # User entered '1', command light to off 1: li t0,'2' bne a0,t0,1f la a0,lightchannel la a1,lightredmsg li a7,129 ecall # User entered '2', command light to red 1: li t0,'3' bne a0,t0,1f la a0,lightchannel la a1,lightyellowmsg li a7,129 ecall # User entered '3', command light to yellow 1: li t0,'4' bne a0,t0,1f la a0,lightchannel la a1,lightgreenmsg li a7,129 ecall # User entered '4', command light to green 1: li t0,'5' bne a0,t0,1f la a0,lightchannel la a1,lightflashredmsg li a7,129 ecall # User entered '5', command light to flashing red 1: li t0,'6' bne a0,t0,1f la a0,lightchannel la a1,lightflashyellowmsg li a7,129 ecall # User entered '6', command light to flashing yellow 1: li t0,'0' bne a0,t0,1f la t1,uipage li t0,9 sb t0,0(t1) # User entered '0', go to output test (page 9) 1: ret .align 2 uidrawsignaltest: lpad 12 la a0,uisignaltest li a7,4 ecall ret .align 2 uihandlesignaltest: lpad 12 li a7,132 ecall # Read one character of input li t0,'1' bne a0,t0,1f la a0,signalchannel la a1,lightoffmsg li a7,129 ecall # User entered '1', command signal to off 1: li t0,'2' bne a0,t0,1f la a0,signalchannel la a1,lightredmsg li a7,129 ecall # User entered '2', command signal to red 1: li t0,'3' bne a0,t0,1f la a0,signalchannel la a1,lightyellowmsg li a7,129 ecall # User entered '3', command signal to yellow 1: li t0,'4' bne a0,t0,1f la a0,signalchannel la a1,lightgreenmsg li a7,129 ecall # User entered '4', command signal to green 1: li t0,'5' bne a0,t0,1f la a0,signalchannel la a1,lightflashredmsg li a7,129 ecall # User entered '5', command signal to flashing red 1: li t0,'6' bne a0,t0,1f la a0,signalchannel la a1,lightflashyellowmsg li a7,129 ecall # User entered '6', command signal to flashing yellow 1: li t0,'0' bne a0,t0,1f la t1,uipage li t0,9 sb t0,0(t1) # User entered '0', go to output test (page 9) 1: ret .align 2 uidrawbelltest: lpad 13 la a0,uibelltest li a7,4 ecall ret .align 2 uihandlebelltest: lpad 13 li a7,132 ecall # Read one character of input li t0,'1' bne a0,t0,1f la a0,bellchannel la a1,belloffmsg li a7,129 ecall # User entered '1', command bell to off 1: li t0,'2' bne a0,t0,1f la a0,bellchannel la a1,bellonmsg li a7,129 ecall # User entered '2', command bell to on 1: li t0,'0' bne a0,t0,1f la t1,uipage li t0,9 sb t0,0(t1) # User entered '0', go to output test (page 9) 1: ret .align 2 uidrawgatetest: lpad 14 la a0,uigatetest li a7,4 ecall ret .align 2 uihandlegatetest: lpad 14 li a7,132 ecall # Read one character of input li t0,'1' bne a0,t0,1f la a0,gatechannel la a1,gateupmsg li a7,129 ecall # User entered '1', command gate to up 1: li t0,'2' bne a0,t0,1f la a0,gatechannel la a1,gatedownmsg li a7,129 ecall # User entered '2', command gate to down 1: li t0,'0' bne a0,t0,1f la t1,uipage li t0,9 sb t0,0(t1) # User entered '0', go to output test (page 9) 1: ret .align 2 uidrawstatistics: lpad 15 cm.push {ra},-16 la a0,uistatistics+21 rdcycle a1 call hexdump la a0,uistatistics+39 rdtime a1 call hexdump la a0,uistatistics+57 rdinstret a1 call hexdump la a0,uistatistics li a7,4 ecall cm.popret {ra},16 .align 2 uihandlestatistics: lpad 15 li a7,132 ecall # Read one character of input li t0,'0' bne a0,t0,1f la t1,uipage li t0,8 sb t0,0(t1) # User entered '0', go to diagnostics (page 8) 1: ret .align 2 uidrawabout1: lpad 16 la a0,uiabout1 li a7,4 ecall ret .align 2 uihandleabout1: lpad 16 li a7,132 ecall # Read one character of input li t0,'1' bne a0,t0,1f la t1,uipage li t0,16 sb t0,0(t1) # User entered '1', go to about 2 (page 16) 1: li t0,'0' bne a0,t0,1f la t1,uipage li t0,1 sb t0,0(t1) # User entered '0', go to main menu (page 1) 1: ret .align 2 uidrawabout2: lpad 17 la a0,uiabout2 li a7,4 ecall ret .align 2 uihandleabout2: lpad 17 li a7,132 ecall # Read one character of input li t0,'1' bne a0,t0,1f la t1,uipage li t0,17 sb t0,0(t1) # User entered '1', go to about 3 (page 17) 1: li t0,'0' bne a0,t0,1f la t1,uipage li t0,1 sb t0,0(t1) # User entered '0', go to main menu (page 1) 1: ret .align 2 uidrawabout3: lpad 18 la a0,uiabout3 li a7,4 ecall ret .align 2 uihandleabout3: lpad 18 li a7,132 ecall # Read one character of input li t0,'1' bne a0,t0,1f la t1,uipage li t0,18 sb t0,0(t1) # User entered '1', go to about 4 (page 18) 1: li t0,'0' bne a0,t0,1f la t1,uipage li t0,1 sb t0,0(t1) # User entered '0', go to main menu (page 1) 1: ret .align 2 uidrawabout4: lpad 19 la a0,uiabout4 li a7,4 ecall ret .align 2 uihandleabout4: lpad 19 li a7,132 ecall # Read one character of input li t0,'0' bne a0,t0,1f la t1,uipage li t0,1 sb t0,0(t1) # User entered '0', go to main menu (page 1) 1: ret uidrawpointers: .word uidrawidle,uidrawmainmenu,uidrawcountryselect,uidrawadjusttiming,uidrawgatedropdelay,uidrawinitialtime,uidrawextensiontime,uidrawinvalidinput,uidrawdiagnostics .word uidrawoutputtest,uidrawlighttest,uidrawsignaltest,uidrawbelltest,uidrawgatetest,uidrawstatistics,uidrawabout1,uidrawabout2,uidrawabout3,uidrawabout4 uihandlepointers: .word uihandleidle,uihandlemainmenu,uihandlecountryselect,uihandleadjusttiming,uihandlegatedropdelay,uihandleinitialtime,uihandleextensiontime,uihandleinvalidinput,uihandlediagnostics .word uihandleoutputtest,uihandlelighttest,uihandlesignaltest,uihandlebelltest,uihandlegatetest,uihandlestatistics,uihandleabout1,uihandleabout2,uihandleabout3,uihandleabout4 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\n3: Diagnostics\n4: About\n0: Exit" 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: Back" 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" uidiagnostics: .asciz "\nDIAGNOSTICS\n1: Output Test\n2: Statistics\n\n\n0: Back" uioutputtest: .asciz "\nOUTPUT TEST\n1: Road Signal\n2: Rail Signal\n3: Bell\n4: Gate\n0: Back" uilighttest: .asciz "\nROAD SIGNAL TEST\n1: Off | 2: Red\n3: Yellow | 4: Green\n5: Flash Red\n6: Flash Yellow\n0: Back" uisignaltest: .asciz "\nRAIL SIGNAL TEST\n1: Off | 2: Red\n3: Yellow | 4: Green\n5: Flash Red\n6: Flash Yellow\n0: Back" uibelltest: .asciz "\nBELL TEST\n1: Off\n2: On\n\n\n0: Back" uigatetest: .asciz "\nGATE TEST\n1: Up\n2: Down\n\n\n0: Back" uistatistics: .asciz "\nSTATISTICS\n cycle: XXXXXXXX\n time: XXXXXXXX\ninstret: XXXXXXXX\n\n0: Back" uiabout1: .asciz "\nABOUT\nRailroad Crossing 2\nfor RVController\n\n\n1: Next | 0: Exit" uiabout2: .asciz "\nA product of\nScheiße & Broken\nSignalling Systems,\na Cheapie Systems\ncompany\n1: Next | 0: Exit" uiabout3: .asciz "\nThis is free and\nunencumbered\nsoftware released\ninto the public\ndomain\n1: Next | 0: Exit" uiabout4: .asciz "\nFor full license\ntext, see the\nincluded LICENSE\nfile, or visit\nunlicense.org\n0: Exit" uide: .asciz "DE" uiuk: .asciz "UK" uius: .asciz "US"