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
86 debug macro debugvalue
88 call debug_serial_transmit
92 mov_wfa TXREG ; move contents of W (i.e. debugvalue)
94 ; to TXREG for transmission
100 ;****************************************************************************
104 ; initial config - TXSTA register p181
105 bc_fa TXSTA,6 ; p181, set 8-bit mode
106 bs_fa TXSTA,5 ; transmit enable
107 bc_fa TXSTA,4 ; asynchronous mode
110 ; initial config - RCSTA register p182
111 bs_fa RCSTA,7 ; serial port enable (p182)
112 bc_fa RCSTA,6 ; 8-bit reception
113 bs_fa RCSTA,4 ; enable continuous receive
115 ; set SPBRG to get correct baud rate according to table top right p186
116 ; (Tosc = 20MHz, desired baud rate = 9600)
119 mov_lw ~((1<<RCIP) | (1<<TXIP))
120 and_wff IPR1 ; serial interrupts: low priority
121 mov_lw (1<<RCIE) | (1<<TXIE)
122 ior_wff PIE1 ; serial interrupt: interrupt enable
126 ;----------------------------------------------------------------------------
130 ; timer0 initial config for NMRA timer
133 bc_fa T0CON,6 ; p107 Timer0 -> 16bit mode (testing)
137 bs_fa T0CON,6 ; p107 Timer0 -> 8bit mode (not-testing)
140 bc_fa T0CON,5 ; timer0 use internal clock
144 and_lw ~((1<<PSA)|(1<<T0PS2)|(1<<T0PS1)|(1<<T0PS0))
145 ior_lw nmra_master_t0scale
146 mov_wf T0CON ; prescale value calculated from
147 ; program.clocks (not-testing)
151 bc_fa T0CON,3 ; use prescaler
153 bc_fa T0CON,1 ; } prescale value 1:16
154 bc_fa T0CON,0 ; } (testing)
157 debug 'b' ; write 'b' to serial port
158 ;----------------------------------------------------------------------------
160 ; initialise buffers (for bank 5, for nmra from-serial/to-track buffers)
165 clr_fa nmractrl ; for bits relevant to control of nmra stream
166 clr_fa fromserial ; for loc'n of write-from-usart ptr in buffers
167 clr_fa totrack ; for loc'n of send-to-track ptr in buffers
171 debug 'c' ; write 'c' to serial port
172 ;----------------------------------------------------------------------------
175 ; initialise next action/transmit bit
176 bs_fa nmractrl,nextaction
177 bs_fa nmractrl,transmitbit
179 nmra_restartmessage @ ; Entrypoint from power_polarising_tick, at end
180 ; of settle time. Goes back to beginning of
181 ; current message (if any) and retransmits it.
182 ; Also, enables booster PWM and Timer 0 interrupts.
186 ; initialise totrackbit bitmask
188 mov_wfa totrackbit ; make bit mask be 1000 0000
190 ; initialise booster direction
191 bc_fa TRISC,0 ; make pin 0 (booster direction) output
192 bc_fa PORTC,0 ; set low initially
194 ; set booster pwm high
195 bs_fa PORTC,1 ; booster pwm high
196 bc_fa TRISC,1 ; make pin 1 (booster pwm) output
198 debug 'd' ; write 'd' to serial port
199 ;----------------------------------------------------------------------------
203 ; interrupt set-up for timer0 interrupt p79/80
204 bs_fa INTCON2,2 ; timer0 overflow high priority
206 bs_fa INTCON,5 ; enable timer0 interrupts
207 ; ^ to disable NMRA output and get DC onto the track,
208 ; comment out the line above
211 debug 'e' ; write 'e' to serial port
215 ;****************************************************************************
217 nmra_sendwatchdog_start @
218 mov_lw 0x40 ; if we were in a message it's going to get
219 mov_wf totrack ; garbled; oh well.
222 nmra_sendwatchdog_stop @
224 return ; not transmitting watchdog
230 ;****************************************************************************
232 nmra_serialrx_intrl @
233 bt_f_if0 PIR1,RCIF ; check whether serial recv interrupt bit set
238 ; debug 'h' ; write 'h' to serial port
239 bt_fa_if1 RCSTA,FERR ; if FERR set (= framing error), then panic
241 bt_fa_if1 RCSTA,OERR ; if OERR set (= overrun error), then panic
244 mov_ff fromserial,FSR0L ; set low byte of INDF0 pointer
246 mov_wfa FSR0H ; set high byte of INDF0 pointer
248 mov_fw RCREG ; get received byte
249 mov_wf INDF0 ; copy to received register
251 ; check whether bit 7 is clear.
252 ; If so, move to next buffer (or process other kind of message)
253 bra_nn end_message ; (STATUS N still from mov_fw RCREG)
256 ; If not, move to next byte
257 inc_fa fromserial ; advance fromserial pointer by 1 byte
262 bra_z receive_message_too_long
264 intrl_handled_nostack
266 receive_too_much_nmra panic morse_HN
267 receive_message_too_long panic morse_HW
269 ; *** I *think* the interrupt bit is cleared by reading out of RCREG
270 ; but this may be something to try in debugging if stuff doesn't work
273 ; so what's the first byte then ?
276 and_wff fromserial ; fromserial := 00BB0000
277 com_fw INDF0 ; where BB is this buffer
278 bra_nz not_nmra_message
280 ; so, first byte is FF (since complement of it is zero)
281 ; so, move to next buffer
284 ; increment top 4 bits of fromserial (already cleared low 4 bits)
286 mov_fw fromserial ; W = 00BB0000
287 add_lw 0x10 ; W = 0?CC0000
288 bc_w 6 ; W = 00CC0000
289 mov_wf t ; t = 00CC0000
290 ; where BB is this buffer
291 ; and CC is next buffer
293 xor_wfw totrack ; W = 0WDD????
294 and_lw 0x70 ; .WDD....
295 bra_z receive_too_much_nmra ; where DD is (fromserial buffer)
296 ; xor (totrack buffer)
297 ; and W is 1 iff xmitting watchog
299 mov_fw t ; W = 00CC0000
302 bra serial_receive_done
305 ; we've already set FSR0 and fromserial to point
306 ; back to beginning of the same buffer
307 call serialrx_generalmsg
308 bra serial_receive_done
310 ;****************************************************************************
312 near_interrupt_high code
314 master_interrupt_high_notnmra
317 master_interrupt_high @
318 bt_f_if0 INTCON,TMR0IF ; check whether timer0 interrupt set
319 bra master_interrupt_high_notnmra
322 debug ',' ; write 'j' to serial port
323 bc_fa INTCON,TMR0IF ; clear interrupt-set bit
326 mov_lw 0x01 ; (testing)
330 mov_lw nmra_master_t0init
333 mov_wfa TMR0L ; set timer0 to appropriate value (so interrupt takes 58us)
336 debug 'k' ; write 'k' to serial port
337 ; check next action - if 0, change to 1 and return
338 bt_fa_if1 nmractrl,nextaction
340 bs_fa nmractrl,nextaction
341 debug '1' ; write 'k' to serial port
345 ; if next action = 1, then toggle output
348 debug 'l' ; write 'l' to serial port
349 btg_fa PORTC,0 ; toggle booster output pin
353 ; if transition was 0->1 then we are mid-bit, so copy transmitbit to
354 ; nextaction in preparation for 2nd half of bit and then return
356 debug 'm' ; write 'm' to serial port
357 bc_fa nmractrl,nextaction
358 bt_fa_if1 nmractrl,transmitbit
359 bs_fa nmractrl,nextaction
360 debug '2' ; write 'k' to serial port
366 debug 'n' ; write 'n' to serial port
367 ; check whether current to-track buffer = current from-serial buffer
368 ; if yes, transmit 1s (set transmitbit=1, nextaction=1 and return)
373 bra_nz read_from_buffer
374 bs_fa nmractrl,transmitbit
375 bs_fa nmractrl,nextaction
383 ; if currently on bit 7, want to skip to bit 6
384 ;*** wouldn't it be easier to start on bit 6 ? :-) -iwj
386 bt_fa_if1 totrackbit,7
387 rr_fa totrackbit ; rotate mask right
390 ; set na=cb=bit value, advance bit (i.e. rotate transmitbit right),
391 ; check if bit7, if so, advance byte, return
395 and_wfwa totrackbit ; select bit to be transmitted
397 bra_z zero_bit_to_track
401 debug '_' ; write 'q' to serial port
402 debug '0' ; write 'q' to serial port
403 debug '_' ; write 'q' to serial port
404 bc_fa nmractrl,transmitbit
405 bc_fa nmractrl,nextaction
409 debug '_' ; write 'q' to serial port
410 debug '1' ; write 'r' to serial port
411 debug '_' ; write 'q' to serial port
412 bs_fa nmractrl,transmitbit
413 bs_fa nmractrl,nextaction
419 ; rotate transmitbit to next position
421 debug 's' ; write 's' to serial port
422 rr_fa totrackbit ; rotate mask right
423 ;*** surely rrnc (`rotate right not through carry' I assume)
424 ;*** will leave a copy of the top bit in the N flag ? Then you
425 ;*** can use branch if negative. -iwj
426 bt_fa_if1 totrackbit,7
427 rcall advance_pointer
428 debug '5' ; write 's' to serial port
436 debug 't' ; write 't' to serial port
438 ; currently on bit 7 of the byte, after having read rest of byte to
439 ; track; check whether it is 1 or 0
441 ; if set, move to next buffer
443 rcall advance_read_buffer
445 ; if not set, move to next byte of samebuffer (increment totrack)
446 ; (will be on bit 7 at this point anyway so no need to change totrackbit)
454 ; move pointer to next buffer
455 ; clear low 4 bits of totrack and increment top 4 bits
456 ; aaaabbbb -> bbbbaaaa -> bbbb(aaaa+1) -> 0000(aaaa+1) -> (aaaa+1)0000
457 bt_f_if1 totrack, 6 ; were we doing watchdog broadcasts ?
458 bra advance_read_buffer_ifwatchdog
463 debug '9' ; write 'v' to serial port
466 bs_f PIE1, TXIE ; ensure we tell host to send us more
472 advance_read_buffer_ifwatchdog
473 ; just loop round the buffer
478 ;****************************************************************************
480 panic_oerr panic morse_HO
481 panic_ferr panic morse_HF
483 ;****************************************************************************