1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
|
# Railroad Crossing 2 for RVController - Main File
# 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
j _start
.include "util.S"
_start:
li t0,0x400
csrs mseccfg,t0 # Turn on MLPE (enable Zicfilp)
li sp,0x10000 # Set up stack pointer
la t0,handler
csrw mtvec,t0 # Set up trap handler
li t0,0x400
csrc mstatush,t0 # Turn off MDT
li a7,131
ecall # Clear console input buffer
li t0,0x60000
csrw mie,t0 # Mask all interrupts except digilines and console
li gp,0xffff0000
csrw 0x801,gp # Map MMIO region at 0xffff0000
li t1,0xffffffff
mv t2,t1
sd t1,10(gp) # Set mtimecmp to 0xffffffffffffffff
csrw mip,zero # Clear any pending interrupts
csrsi mstatus,0x8 # Turn on MIE to enable interrupts
# Hardware initialization
digiline_send lightchannel,lightoffmsg
digiline_send gatechannel,gateupmsg
digiline_send bellchannel,belloffmsg
digiline_send signalchannel,lightredmsg
.include "ui.S"
.align 2
handler:
csrw mscratch,x31 # Free up x31 to hold the context address
la x31,hcontext # Using x register numbers here since trap handlers don't use the usual calling convention
sw x1,0(x31) # Save all the registers to the context
sd x2,4(x31) # Two at a time since we have Zilsd/Zclsd
sd x4,12(x31)
sd x6,20(x31)
sd x8,28(x31)
sd x10,36(x31)
sd x12,44(x31)
sd x14,52(x31)
sd x16,60(x31)
sd x18,68(x31)
sd x20,76(x31)
sd x22,84(x31)
sd x24,92(x31)
sd x26,100(x31)
sd x28,108(x31)
sw x30,116(x31)
csrr x30,mscratch # Get the old x31 value back into a register
sw x30,120(x31) # and write that back out too
csrr t0,mcause # Read why the trap occurred
li t1,0x80000011 # Interrupt 17 = digilines
beq t0,t1,handler_digilines
addi t1,t1,1 # Interrupt 18 = console
beq t0,t1,handler_console
li t1,0x80000007 # Interrupt 7 = timer
beq t0,t1,handler_timer
j handler_other # Something else
handler_digilines:
li t0,131072 # 2^17, value of MDIP bit
csrc mip,t0 # Acknowledge digilines interrupt
la a0,channelbuf
li a1,16
la a2,msgbuf
li a3,16
li a7,135
ecall # Read the next digilines message
la t0,channelbuf
ld t1,0(t0)
li t3,0x65746564 # 'dete'
bne t1,t3,1f # not the detector
li t3,0x00ffffff
and t2,t2,t3
li t3,0x00007463 # 'ct\0\0'
bne t2,t3,1f # not the detector
la t0,currstate
lb t0,0(t0) # Read current state from RAM
andi t0,t0,0xf # Mask out the country
bnez t0,2f
# State 0: start sequence
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
2: la t0,currstate
lb s0,0(t0) # Re-read current state from RAM
li t1,5
beq s0,t1,1f # State 5: reset timer
li t1,21
beq s0,t1,1f # State 21 (UK state 5): reset timer
li t1,35
beq s0,t1,1f # State 35 (US state 3): reset timer
j 2f # None of the above, skip over the reset
1: set_timer_to extensiontime
2: li a7,133
ecall # Check if there are more messages to process
bnez a0,handler_digilines
j handler_epilogue
handler_timer:
stop_timer # Stopping timer acknowledges the interrupt too
la t2,currstate
lb s0,0(t2) # Read current state from RAM
beqz s0,handler_epilogue # State 0: timer shouldn't have fired in the first place
li t3,16
beq s0,t3,handler_epilogue # State 16 (UK state 0): timer shouldn't have fired in the first place
li t3,32
beq s0,t3,handler_epilogue # State 32 (US state 0): timer shouldn't have fired in the first place
addi s0,s0,1 # Increment to get to next state
sb s0,0(t2) # Write new state to RAM
la t1,4
mul t0,s0,t1
la t1,states
add t0,t0,t1 # Calculate address of function address for new state
lw t1,0(t0) # Read what the function address should be
addi t2,s0,1 # Landing pad label is state number + 1 (to avoid having any of them be 0)
slli t2,t2,12
jalr ra,t1,0 # And call the proper function
# Fall through to epilogue
handler_epilogue:
csrr t0,mip # Check if there are any interrupts still pending
bexti t1,t0,7 # Bit 7 = MTIP (timer)
bnez t1,handler_timer
bexti t1,t0,17 # Bit 17 = MDIP (digilines)
bnez t1,handler_digilines
bexti t1,t0,18 # Bit 18 = MCIP (console)
bnez t1,handler_console
la x31,hcontext # x numbers here for the same reason as during the save
lw x1,0(x31) # Load all of the registers from the context
ld x2,4(x31) # Two at a time since we have Zilsd/Zclsd
ld x4,12(x31)
ld x6,20(x31)
ld x8,28(x31)
ld x10,36(x31)
ld x12,44(x31)
ld x14,52(x31)
ld x16,60(x31)
ld x18,68(x31)
ld x20,76(x31)
ld x22,84(x31)
ld x24,92(x31)
ld x26,100(x31)
ld x28,108(x31)
ld x30,116(x31)
mret # Return to the program
handler_other:
li a7,4
la a0,uiunimplementedtrap
ecall # Display unimplemented trap message
csrr a0,mcause # Read what the cause was
li a7,1
ecall # Tell the user
li a7,10
ecall # Then halt
states: .word destate0,destate1,destate2,destate3,destate4,destate5,destate6,0,0,0,0,0,0,0,0,0
.word ukstate0,ukstate1,ukstate2,ukstate3,ukstate4,ukstate5,ukstate6,ukstate7,0,0,0,0,0,0,0,0
.word usstate0,usstate1,usstate2,usstate3,usstate4,0,0,0,0,0,0,0,0,0,0,0
.align 2
destate0:
lpad 1
stop_timer
digiline_send lightchannel,lightoffmsg
digiline_send gatechannel,gateupmsg
digiline_send bellchannel,belloffmsg
digiline_send signalchannel,lightredmsg
ret
.align 2
destate1:
lpad 2
digiline_send lightchannel,lightyellowmsg
set_timeri 2
ret
.align 2
destate2:
lpad 3
digiline_send bellchannel,bellonmsg
set_timeri 3
ret
.align 2
destate3:
lpad 4
digiline_send lightchannel,lightredmsg
set_timer_to gatedroptime
ret
.align 2
destate4:
lpad 5
digiline_send gatechannel,gatedownmsg
set_timeri 3
ret
.align 2
destate5:
lpad 6
digiline_send signalchannel,lightgreenmsg
digiline_send bellchannel,belloffmsg
set_timer_to activetime
ret
.align 2
destate6:
lpad 7
digiline_send signalchannel,lightredmsg
digiline_send gatechannel,gateupmsg
set_timeri 2
la t0,currstate
li t1,-1
sb t1,0(t0) # Next state should be 0
ret
.align 2
ukstate0:
lpad 17
stop_timer
digiline_send lightchannel,lightoffmsg
digiline_send gatechannel,gateupmsg
digiline_send bellchannel,belloffmsg
digiline_send signalchannel,lightredmsg
ret
.align 2
ukstate1:
lpad 18
digiline_send lightchannel,lightyellowmsg
digiline_send bellchannel,bellonmsg
set_timeri 3
ret
.align 2
ukstate2:
lpad 19
digiline_send lightchannel,lightredmsg
set_timeri 1
ret
.align 2
ukstate3:
lpad 20
digiline_send lightchannel,lightflashredmsg
set_timer_to gatedroptime
ret
.align 2
ukstate4:
lpad 21
digiline_send gatechannel,gatedownmsg
set_timeri 3
ret
.align 2
ukstate5:
lpad 22
digiline_send signalchannel,lightgreenmsg
set_timer_to activetime
ret
.align 2
ukstate6:
lpad 23
digiline_send signalchannel,lightredmsg
digiline_send bellchannel,belloffmsg
set_timeri 1
ret
.align 2
ukstate7:
lpad 24
digiline_send gatechannel,gateupmsg
set_timeri 3
la t0,currstate
li t1,15
sb t1,0(t0) # Next state should be 16 (UK state 0)
ret
.align 2
usstate0:
lpad 33
stop_timer
digiline_send lightchannel,lightoffmsg
digiline_send gatechannel,gateupmsg
digiline_send bellchannel,belloffmsg
digiline_send signalchannel,lightredmsg
ret
.align 2
usstate1:
lpad 34
digiline_send lightchannel,lightflashredmsg
digiline_send bellchannel,bellonmsg
set_timer_to gatedroptime
ret
.align 2
usstate2:
lpad 35
digiline_send gatechannel,gatedownmsg
set_timeri 3
ret
.align 2
usstate3:
lpad 36
digiline_send bellchannel,belloffmsg
digiline_send signalchannel,lightgreenmsg
set_timer_to activetime
ret
.align 2
usstate4:
lpad 37
digiline_send signalchannel,lightredmsg
digiline_send gatechannel,gateupmsg
set_timeri 3
la t0,currstate
li t1,31
sb t1,0(t0) # Next state should be 32 (US state 0)
ret
hcontext: .skip 32*4
currstate: .byte 0
country: .byte 0 # 0 = DE, 1 = UK, 2 = US
gatedroptime: .byte 3
activetime: .byte 15
extensiontime: .byte 25
uiunimplementedtrap: .asciz "Unimplemented trap!\nmcause: "
channelbuf: .skip 16
msgbuf: .skip 16
lightchannel: .asciz "light"
bellchannel: .asciz "bell"
gatechannel: .asciz "gate"
signalchannel: .asciz "signal"
lightoffmsg: .asciz "OFF"
lightredmsg: .asciz "RED"
lightyellowmsg: .asciz "YELLOW"
lightgreenmsg: .asciz "GREEN"
lightflashredmsg: .asciz "FLASHRED"
lightflashyellowmsg: .asciz "FLASHYELLOW"
belloffmsg: .asciz "off"
bellonmsg: .asciz "on"
gateupmsg: .asciz "up"
gatedownmsg: .asciz "down"
|