chiark / gitweb /
hostside: more length for bavarian
[trains.git] / detpic / nmra-stream.asm
1 ;======================================================================
2 ; NMRA DATA TO TRACK - also, initially deals with all incoming RS232
3 ;
4 ;======================================================================
5 ; TESTING INSTRUCTIONS
6 ;
7 ; can be tested with liberator's mic input:
8 ;   1k1, 110k resistor divider from track gives 1/100 track
9 ;   voltage to mic input
10 ; then
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
14 ;   less u
15 ;
16 ;======================================================================
17
18 clock equ mclock ; this part runs really only on master
19
20   include common.inc
21
22   udata_acs
23
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
28
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
32   ;
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.
36   ;
37   ; fromserial and totrack are pointers into the buffer page
38   ;
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.
45   ;
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
50   ;
51   ; We maintain the following properties, notionally:
52   ;
53   ;  totrack <= fromserial < totrack
54   ;
55   ;            \            \
56   ;             \            `- equal would mean overrun
57   ;              \
58   ;               `- equal if we have nothing but
59   ;                   idle to trasmit
60   ;
61
62 bufferpage      equ     5
63
64 buffer_section udata bufferpage << 8
65 buffer0 res 16
66 buffer1 res 16
67 buffer2 res 16
68 buffer3 res 16
69 bufferw res 16
70
71   code
72
73 ;****************************************************************************
74
75 macros
76
77 ; macro to call subroutine to transmit over serial port for debugging
78 ; takes 8-bit value, puts in W, invokes debug_serial_transmit
79
80         ifndef  SLOW_VERSION
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
85  ;      Dl      debugvalue
86         endm
87         endif
88
89         ifdef   SLOW_VERSION
90 debug macro debugvalue
91         mov_lw  debugvalue
92         call    debug_serial_transmit
93         endm
94
95 debug_serial_transmit
96         mov_wfa TXREG           ; move contents of W (i.e. debugvalue)
97         endif
98                                 ;       to TXREG for transmission
99 waitfortsr
100         bt_fa_if0 TXSTA,1
101         bra     waitfortsr
102         return
103
104 ;****************************************************************************
105
106 serial_init @
107 ; serial set-up
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
112         bsc_txsta_brgh
113
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
118
119 ; set SPBRG to get correct baud rate according to table top right p186
120 ; (Tosc = 20MHz, desired baud rate = 9600)
121         movlw_movwf_spbrg
122
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
127
128         return
129
130 ;----------------------------------------------------------------------------
131
132 nmra_init @
133 ; timer 0 set-up
134 ; timer0 initial config for NMRA timer
135
136         ifdef   SLOW_VERSION
137         bc_fa     T0CON,6         ; p107 Timer0 -> 16bit mode (testing)
138         endif
139
140         ifndef  SLOW_VERSION
141         bs_fa     T0CON,6         ; p107 Timer0 -> 8bit mode (not-testing)
142         endif
143
144         bc_fa     T0CON,5         ; timer0 use internal clock
145
146         ifndef  SLOW_VERSION
147         mov_fw  T0CON
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)
152         endif
153
154         ifdef   SLOW_VERSION
155         bc_fa     T0CON,3         ; use prescaler
156         bs_fa     T0CON,2         ; }
157         bc_fa     T0CON,1         ; } prescale value 1:16
158         bc_fa     T0CON,0         ; } (testing)
159         endif
160
161         debug   'b'     ; write 'b' to serial port
162 ;----------------------------------------------------------------------------
163
164 ; initialise buffers (for bank 5, for nmra from-serial/to-track buffers)
165
166         mov_lw  bufferpage
167         mov_wf  FSR2H
168
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
172                                 ; all in access bank
173         clr_f   acknmra
174
175         debug   'c'     ; write 'c' to serial port
176 ;----------------------------------------------------------------------------
177
178
179 ; initialise next action/transmit bit
180         bs_fa   nmractrl,nextaction
181         bs_fa   nmractrl,transmitbit
182
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.
187         mov_lw  0xf0
188         and_wff totrack
189
190 ; initialise totrackbit bitmask
191         mov_lw  0x80
192         mov_wfa totrackbit      ; make bit mask be 1000 0000
193
194 ; initialise booster direction
195         bc_fa   TRISC,0         ; make pin 0 (booster direction) output
196         bc_fa   PORTC,0         ; set low initially
197
198 ; set booster pwm high
199         bs_fa   PORTC,1         ; booster pwm high
200         bc_fa   TRISC,1         ; make pin 1 (booster pwm) output
201
202         debug   'd'     ; write 'd' to serial port
203 ;----------------------------------------------------------------------------
204
205 ; interrupt set-up
206
207 ; interrupt set-up for timer0 interrupt p79/80
208         bs_fa   INTCON2,2       ; timer0 overflow high priority
209
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
213
214
215         debug   'e'     ; write 'e' to serial port
216
217         return
218
219 ;****************************************************************************
220
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.
224         return
225
226 nmra_sendwatchdog_stop @
227         bt_f_if0 totrack, 6
228         return ; not transmitting watchdog
229         mov_fw  fromserial
230         and_lw  0x30
231         mov_wf  totrack
232         return
233
234 ;****************************************************************************
235
236 nmra_serialrx_intrl @
237         bt_f_if0 PIR1,RCIF      ; check whether serial recv interrupt bit set
238         return
239
240         ; serial_receive:
241
242         debug   'h'     ; write 'h' to serial port
243         Df      fromserial
244         Df      totrack
245
246         bt_fa_if1 RCSTA,FERR            ; if FERR set (= framing error), then panic
247         goto    panic_ferr
248         bt_fa_if1 RCSTA,OERR            ; if OERR set (= overrun error), then panic
249         goto    panic_oerr
250
251         mov_ff  fromserial,FSR0L        ; set low byte of INDF0 pointer
252         mov_lw  bufferpage
253         mov_wfa FSR0H                   ; set high byte of INDF0 pointer
254         debug   '1'
255         mov_fw  RCREG                   ; get received byte
256         mov_wf  INDF0                   ; copy to received register
257
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)
261         debug   '3'
262
263 ; If not, move to next byte
264         inc_fa  fromserial              ; advance fromserial pointer by 1 byte
265         debug   '4'
266
267         mov_lw  0x0f
268         and_wfw fromserial
269         bra_z   receive_message_too_long
270 serial_receive_done
271         intrl_handled_nostack
272
273 receive_too_much_nmra           panic   morse_HN
274 receive_message_too_long        panic   morse_HW
275
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
278
279 end_message
280 ; so what's the first byte then ?
281         mov_lw  0xf0
282         and_wff FSR0L
283         and_wff fromserial              ; fromserial := 00BB0000
284         com_fw  INDF0                   ;  where BB is this buffer
285         bra_nz  not_nmra_message
286
287 ; so, first byte is FF (since complement of it is zero)
288 ; so, move to next buffer
289 advance_write_buffer
290
291 ; increment top 4 bits of fromserial (already cleared low 4 bits)
292         debug   '5'
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
299
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
305
306         mov_fw  t                       ; W =           00CC0000
307         mov_wf  fromserial
308
309         bra     serial_receive_done
310
311 not_nmra_message
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
316
317 ;****************************************************************************
318
319 near_interrupt_high code
320
321 master_interrupt_high_notnmra
322         panic   morse_IH
323
324 master_interrupt_high @
325         bt_f_if0 INTCON,TMR0IF  ; check whether timer0 interrupt set
326         bra     master_interrupt_high_notnmra
327         ; timer0 interrupt
328
329         debug   ','     ; write ',' to serial port
330         Df      fromserial
331         Df      totrack
332         Df      nmractrl
333
334         bc_fa   INTCON,TMR0IF   ; clear interrupt-set bit
335
336         ifdef   SLOW_VERSION
337         mov_lw  0x01    ; (testing)
338         endif
339
340         ifndef  SLOW_VERSION
341         mov_lw  nmra_master_t0init
342         endif
343
344         mov_wfa TMR0L           ; set timer0 to appropriate value (so interrupt takes 58us)
345
346
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
350         bra     toggle_output
351         bs_fa   nmractrl,nextaction
352         debug   '1'     ; write 'k' to serial port
353         retfie_r
354
355
356 ; if next action = 1, then toggle output
357
358 toggle_output
359         debug   'l'     ; write 'l' to serial port
360         btg_fa  LATC,0          ; toggle booster output pin
361         bt_fa_if0 LATC,0
362         bra     decide_next_bit
363         ; we're in mid-bit:
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
366
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
372         retfie_r
373
374
375 decide_next_bit
376
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)
380
381         mov_fw  fromserial
382         xor_wfwa totrack
383         and_lw  0x70
384         bra_nz  read_from_buffer
385         bs_fa   nmractrl,transmitbit
386         bs_fa   nmractrl,nextaction
387         debug   '3'
388         retfie_r
389
390
391 read_from_buffer
392         debug   'o'
393
394 ; if currently on bit 7, want to skip to bit 6
395 ;*** wouldn't it be easier to start on bit 6 ? :-)  -iwj
396
397         bt_fa_if1 totrackbit,7
398         rr_fa   totrackbit              ; rotate mask right
399
400 ; if not on bit 7 ,
401 ; set na=cb=bit value, advance bit (i.e. rotate transmitbit right),
402 ; check if bit7, if so, advance byte, return
403
404         debug   'p'
405         mov_fw  INDF2
406         and_wfwa totrackbit             ; select bit to be transmitted
407
408         bra_z   zero_bit_to_track
409         bra     one_bit_to_track
410
411 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
417         bra     advance_bit
418
419 one_bit_to_track
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
425         bra     advance_bit
426
427
428
429 advance_bit
430 ; rotate transmitbit to next position
431
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
440
441         retfie_r
442
443
444
445 advance_pointer
446
447         debug   't'     ; write 't' to serial port
448
449 ; currently on bit 7 of the byte, after having read rest of byte to
450 ; track; check whether it is 1 or 0
451
452 ; if clear, move to next buffer
453         bt_fa_if0 INDF2,7
454         bra     advance_read_buffer
455
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)
458         bt_fa_if1 INDF2,7
459         inc_fa  totrack
460         return
461
462
463 advance_read_buffer
464
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
470
471         mov_fw  totrack
472         add_lw  0x10
473         and_lw  0x30
474         mov_wf  totrack
475         debug   '9'     ; write '9' to serial port
476
477         inc_f   acknmra
478         bs_f    PIE1, TXIE      ; ensure we tell host to send us more
479         bt_f_if0 acknmra, 2
480         return
481         ; oops:
482         panic   morse_HM
483
484 advance_read_buffer_ifwatchdog
485         ; just loop round the buffer
486         mov_lw  0x40
487         mov_wf  totrack
488         return
489
490 ;****************************************************************************
491
492 panic_oerr      panic   morse_HO
493 panic_ferr      panic   morse_HF
494
495 ;****************************************************************************
496
497   include final.inc