1 ;======================================================================
2 ; NMRA DATA TO TRACK - also, initially deals with all incoming RS232
4 ;======================================================================
7 ; can be tested with liberator's mic input:
8 ; 1k1, 110k resistor divider from track gives 1/100 track
11 ; sox -r 96000 -u -b -t ossdsp /dev/dsp t.dat
12 ; perl -pe '$_= m/\-/ ? "_" : "~"' <t.dat | fold -290 | perl -pe 's/$/\n\n\n/' >u
13 ; in an xterm showing font "tiny" and then resized to eg 295x54
16 ;======================================================================
18 clock equ mclock ; this part runs really only on master
24 nmractrl res 1 ; state relevant to NMRA control
25 transmitbit equ 7 ; bit 7: 0/1 bit currently being transmitted
26 nextaction equ 6 ; bit 6: change polarity on next interrupt y/n
27 ; nmractrl is a bitmask, and only the bits above are used
29 fromserial res 1 ; from-serial buffer location (in buffer bank)
30 totrack equ FSR2L ; to-track buffer location (in buffer bank)
31 totrackbit res 1 ; bit location of pointer within byte
33 ; buffers are each 16 bytes (this is hardwired)
34 ; and there are four of them starting at 0x500, 0x510, 0x520, 0x530.
35 ; plus a copy of the watchdog `stop everything' command at 0x540.
37 ; fromserial and totrack are pointers into the buffer page
39 ; fromserial points to the first empty byte, where we will put new
40 ; data provided by the host; this means that (fromserial & 0xf0) is
41 ; the first buffer which fails to contain a complete nmra message.
42 ; fromserial is updated only by the (low priority) serial receive
43 ; interrupt handler. Furthermore, it always maintains a correct
44 ; and valid value for use by the high-priority nmra readout ISR.
46 ; totrack points to the first remaining full byte, which is currently
47 ; being transmitted; if nothing is currently being transmitted
48 ; because there is no nmra data at all then totrack points to the
49 ; start of the same buffer as fromserial
51 ; We maintain the following properties, notionally:
53 ; totrack <= fromserial < totrack
56 ; \ `- equal would mean overrun
58 ; `- equal if we have nothing but
64 buffer_section udata bufferpage << 8
73 ;****************************************************************************
77 ; macro to call subroutine to transmit over serial port for debugging
78 ; takes 8-bit value, puts in W, invokes debug_serial_transmit
81 debug macro debugvalue
82 ; uncomment this to get copious stuff in the debug buffer
83 ; NB that strictly this does not work because debugbyte messes with
84 ; FSR0[LH] and if we interrupt debugbyte we will trash FSR0
90 debug macro debugvalue
92 call debug_serial_transmit
96 mov_wfa TXREG ; move contents of W (i.e. debugvalue)
98 ; to TXREG for transmission
104 ;****************************************************************************
108 ; initial config - TXSTA register p181
109 bc_fa TXSTA,6 ; p181, set 8-bit mode
110 bs_fa TXSTA,5 ; transmit enable
111 bc_fa TXSTA,4 ; asynchronous mode
114 ; initial config - RCSTA register p182
115 bs_fa RCSTA,7 ; serial port enable (p182)
116 bc_fa RCSTA,6 ; 8-bit reception
117 bs_fa RCSTA,4 ; enable continuous receive
119 ; set SPBRG to get correct baud rate according to table top right p186
120 ; (Tosc = 20MHz, desired baud rate = 9600)
123 mov_lw ~((1<<RCIP) | (1<<TXIP))
124 and_wff IPR1 ; serial interrupts: low priority
125 mov_lw (1<<RCIE) | (1<<TXIE)
126 ior_wff PIE1 ; serial interrupt: interrupt enable
130 ;----------------------------------------------------------------------------
134 ; timer0 initial config for NMRA timer
137 bc_fa T0CON,6 ; p107 Timer0 -> 16bit mode (testing)
141 bs_fa T0CON,6 ; p107 Timer0 -> 8bit mode (not-testing)
144 bc_fa T0CON,5 ; timer0 use internal clock
148 and_lw ~((1<<PSA)|(1<<T0PS2)|(1<<T0PS1)|(1<<T0PS0))
149 ior_lw nmra_master_t0scale
150 mov_wf T0CON ; prescale value calculated from
151 ; program.clocks (not-testing)
155 bc_fa T0CON,3 ; use prescaler
157 bc_fa T0CON,1 ; } prescale value 1:16
158 bc_fa T0CON,0 ; } (testing)
161 debug 'b' ; write 'b' to serial port
162 ;----------------------------------------------------------------------------
164 ; initialise buffers (for bank 5, for nmra from-serial/to-track buffers)
169 clr_fa nmractrl ; for bits relevant to control of nmra stream
170 clr_fa fromserial ; for loc'n of write-from-usart ptr in buffers
171 clr_fa totrack ; for loc'n of send-to-track ptr in buffers
175 debug 'c' ; write 'c' to serial port
176 ;----------------------------------------------------------------------------
179 ; initialise next action/transmit bit
180 bs_fa nmractrl,nextaction
181 bs_fa nmractrl,transmitbit
183 nmra_restartmessage @ ; Entrypoint from power_polarising_tick, at end
184 ; of settle time. Goes back to beginning of
185 ; current message (if any) and retransmits it.
186 ; Also, enables booster PWM and Timer 0 interrupts.
190 ; initialise totrackbit bitmask
192 mov_wfa totrackbit ; make bit mask be 1000 0000
194 ; initialise booster direction
195 bc_fa TRISC,0 ; make pin 0 (booster direction) output
196 bc_fa PORTC,0 ; set low initially
198 ; set booster pwm high
199 bs_fa PORTC,1 ; booster pwm high
200 bc_fa TRISC,1 ; make pin 1 (booster pwm) output
202 debug 'd' ; write 'd' to serial port
203 ;----------------------------------------------------------------------------
207 ; interrupt set-up for timer0 interrupt p79/80
208 bs_fa INTCON2,2 ; timer0 overflow high priority
210 bs_fa INTCON,5 ; enable timer0 interrupts
211 ; ^ to disable NMRA output and get DC onto the track,
212 ; comment out the line above
215 debug 'e' ; write 'e' to serial port
219 ;****************************************************************************
221 nmra_sendwatchdog_start @
222 mov_lw 0x40 ; if we were in a message it's going to get
223 mov_wf totrack ; garbled; oh well.
226 nmra_sendwatchdog_stop @
228 return ; not transmitting watchdog
234 ;****************************************************************************
236 nmra_serialrx_intrl @
237 bt_f_if0 PIR1,RCIF ; check whether serial recv interrupt bit set
242 debug 'h' ; write 'h' to serial port
246 bt_fa_if1 RCSTA,FERR ; if FERR set (= framing error), then panic
248 bt_fa_if1 RCSTA,OERR ; if OERR set (= overrun error), then panic
251 mov_ff fromserial,FSR0L ; set low byte of INDF0 pointer
253 mov_wfa FSR0H ; set high byte of INDF0 pointer
255 mov_fw RCREG ; get received byte
256 mov_wf INDF0 ; copy to received register
258 ; check whether bit 7 is clear.
259 ; If so, move to next buffer (or process other kind of message)
260 bra_nn end_message ; (STATUS N still from mov_fw RCREG)
263 ; If not, move to next byte
264 inc_fa fromserial ; advance fromserial pointer by 1 byte
269 bra_z receive_message_too_long
271 intrl_handled_nostack
273 receive_too_much_nmra panic morse_HN
274 receive_message_too_long panic morse_HW
276 ; *** I *think* the interrupt bit is cleared by reading out of RCREG
277 ; but this may be something to try in debugging if stuff doesn't work
280 ; so what's the first byte then ?
283 and_wff fromserial ; fromserial := 00BB0000
284 com_fw INDF0 ; where BB is this buffer
285 bra_nz not_nmra_message
287 ; so, first byte is FF (since complement of it is zero)
288 ; so, move to next buffer
291 ; increment top 4 bits of fromserial (already cleared low 4 bits)
293 mov_fw fromserial ; W = 00BB0000
294 add_lw 0x10 ; W = 0?CC0000
295 bc_w 6 ; W = 00CC0000
296 mov_wf t ; t = 00CC0000
297 ; where BB is this buffer
298 ; and CC is next buffer
300 xor_wfw totrack ; W = 0WDD????
301 and_lw 0x70 ; .WDD....
302 bra_z receive_too_much_nmra ; where DD is (fromserial buffer)
303 ; xor (totrack buffer)
304 ; and W is 1 iff xmitting watchog
306 mov_fw t ; W = 00CC0000
309 bra serial_receive_done
312 ; we've already set FSR0 and fromserial to point
313 ; back to beginning of the same buffer
314 call serialrx_generalmsg
315 bra serial_receive_done
317 ;****************************************************************************
319 near_interrupt_high code
321 master_interrupt_high_notnmra
324 master_interrupt_high @
325 bt_f_if0 INTCON,TMR0IF ; check whether timer0 interrupt set
326 bra master_interrupt_high_notnmra
329 debug ',' ; write ',' to serial port
334 bc_fa INTCON,TMR0IF ; clear interrupt-set bit
337 mov_lw 0x01 ; (testing)
341 mov_lw nmra_master_t0init
344 mov_wfa TMR0L ; set timer0 to appropriate value (so interrupt takes 58us)
347 debug 'k' ; write 'k' to serial port
348 ; check next action - if 0, change to 1 and return
349 bt_fa_if1 nmractrl,nextaction
351 bs_fa nmractrl,nextaction
352 debug '1' ; write 'k' to serial port
356 ; if next action = 1, then toggle output
359 debug 'l' ; write 'l' to serial port
360 btg_fa LATC,0 ; toggle booster output pin
364 ; if transition was 0->1 then we are mid-bit, so copy transmitbit to
365 ; nextaction in preparation for 2nd half of bit and then return
367 debug 'm' ; write 'm' to serial port
368 bc_fa nmractrl,nextaction
369 bt_fa_if1 nmractrl,transmitbit
370 bs_fa nmractrl,nextaction
371 debug '2' ; write 'k' to serial port
377 debug 'n' ; write 'n' to serial port
378 ; check whether current to-track buffer = current from-serial buffer
379 ; if yes, transmit 1s (set transmitbit=1, nextaction=1 and return)
384 bra_nz read_from_buffer
385 bs_fa nmractrl,transmitbit
386 bs_fa nmractrl,nextaction
394 ; if currently on bit 7, want to skip to bit 6
395 ;*** wouldn't it be easier to start on bit 6 ? :-) -iwj
397 bt_fa_if1 totrackbit,7
398 rr_fa totrackbit ; rotate mask right
401 ; set na=cb=bit value, advance bit (i.e. rotate transmitbit right),
402 ; check if bit7, if so, advance byte, return
406 and_wfwa totrackbit ; select bit to be transmitted
408 bra_z zero_bit_to_track
412 debug '_' ; write 'q' to serial port
413 debug '0' ; write 'q' to serial port
414 debug '_' ; write 'q' to serial port
415 bc_fa nmractrl,transmitbit
416 bc_fa nmractrl,nextaction
420 debug '_' ; write 'q' to serial port
421 debug '1' ; write 'r' to serial port
422 debug '_' ; write 'q' to serial port
423 bs_fa nmractrl,transmitbit
424 bs_fa nmractrl,nextaction
430 ; rotate transmitbit to next position
432 debug 's' ; write 's' to serial port
433 rr_fa totrackbit ; rotate mask right
434 ;*** surely rrnc (`rotate right not through carry' I assume)
435 ;*** will leave a copy of the top bit in the N flag ? Then you
436 ;*** can use branch if negative. -iwj
437 bt_fa_if1 totrackbit,7
438 rcall advance_pointer
439 debug '5' ; write 's' to serial port
447 debug 't' ; write 't' to serial port
449 ; currently on bit 7 of the byte, after having read rest of byte to
450 ; track; check whether it is 1 or 0
452 ; if clear, move to next buffer
454 bra advance_read_buffer
456 ; if set, move to next byte of samebuffer (increment totrack)
457 ; (will be on bit 7 at this point anyway so no need to change totrackbit)
465 ; move pointer to next buffer
466 ; clear low 4 bits of totrack and increment top 4 bits
467 ; aaaabbbb -> bbbbaaaa -> bbbb(aaaa+1) -> 0000(aaaa+1) -> (aaaa+1)0000
468 bt_f_if1 totrack, 6 ; were we doing watchdog broadcasts ?
469 bra advance_read_buffer_ifwatchdog
475 debug '9' ; write '9' to serial port
478 bs_f PIE1, TXIE ; ensure we tell host to send us more
484 advance_read_buffer_ifwatchdog
485 ; just loop round the buffer
490 ;****************************************************************************
492 panic_oerr panic morse_HO
493 panic_ferr panic morse_HF
495 ;****************************************************************************