chiark / gitweb /
undo broken deletion
[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         endm
83         endif
84
85         ifdef   SLOW_VERSION
86 debug macro debugvalue
87         mov_lw  debugvalue
88         call    debug_serial_transmit
89         endm
90         endif
91
92 debug_serial_transmit
93         mov_wfa TXREG           ; move contents of W (i.e. debugvalue)
94                                 ;       to TXREG for transmission
95 waitfortsr
96         bt_fa_if0 TXSTA,1
97         bra     waitfortsr
98         return
99
100 ;****************************************************************************
101
102 serial_init @
103 ; serial set-up
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
108         bsc_txsta_brgh
109
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
114
115 ; set SPBRG to get correct baud rate according to table top right p186
116 ; (Tosc = 20MHz, desired baud rate = 9600)
117         movlw_movwf_spbrg
118
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
123
124         return
125
126 ;----------------------------------------------------------------------------
127
128 nmra_init @
129 ; timer 0 set-up
130 ; timer0 initial config for NMRA timer
131
132         ifdef   SLOW_VERSION
133         bc_fa     T0CON,6         ; p107 Timer0 -> 16bit mode (testing)
134         endif
135
136         ifndef  SLOW_VERSION
137         bs_fa     T0CON,6         ; p107 Timer0 -> 8bit mode (not-testing)
138         endif
139
140         bc_fa     T0CON,5         ; timer0 use internal clock
141
142         ifndef  SLOW_VERSION
143         mov_fw  T0CON
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)
148         endif
149
150         ifdef   SLOW_VERSION
151         bc_fa     T0CON,3         ; use prescaler
152         bs_fa     T0CON,2         ; }
153         bc_fa     T0CON,1         ; } prescale value 1:16
154         bc_fa     T0CON,0         ; } (testing)
155         endif
156
157         debug   'b'     ; write 'b' to serial port
158 ;----------------------------------------------------------------------------
159
160 ; initialise buffers (for bank 5, for nmra from-serial/to-track buffers)
161
162         mov_lw  bufferpage
163         mov_wf  FSR2H
164
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
168                                 ; all in access bank
169         clr_f   acknmra
170
171         debug   'c'     ; write 'c' to serial port
172 ;----------------------------------------------------------------------------
173
174
175 ; initialise next action/transmit bit
176         bs_fa   nmractrl,nextaction
177         bs_fa   nmractrl,transmitbit
178
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.
183         mov_lw  0x0f
184         and_wff totrack
185
186 ; initialise totrackbit bitmask
187         mov_lw  0x80
188         mov_wfa totrackbit      ; make bit mask be 1000 0000
189
190 ; initialise booster direction
191         bc_fa   TRISC,0         ; make pin 0 (booster direction) output
192         bc_fa   PORTC,0         ; set low initially
193
194 ; set booster pwm high
195         bs_fa   PORTC,1         ; booster pwm high
196         bc_fa   TRISC,1         ; make pin 1 (booster pwm) output
197
198         debug   'd'     ; write 'd' to serial port
199 ;----------------------------------------------------------------------------
200
201 ; interrupt set-up
202
203 ; interrupt set-up for timer0 interrupt p79/80
204         bs_fa   INTCON2,2       ; timer0 overflow high priority
205         bs_fa   INTCON,5        ; enable timer0 interrupts
206
207
208         debug   'e'     ; write 'e' to serial port
209
210         return
211
212 ;****************************************************************************
213
214 nmra_sendwatchdog_start @
215         mov_lw  0x40    ; if we were in a message it's going to get
216         mov_wf  totrack ; garbled; oh well.
217         return
218
219 nmra_sendwatchdog_stop @
220         bt_f_if0 totrack, 6
221         return ; not transmitting watchdog
222         mov_fw  fromserial
223         and_lw  0x30
224         mov_wf  totrack
225         return
226
227 ;****************************************************************************
228
229 nmra_serialrx_intrl @
230         bt_f_if0 PIR1,RCIF      ; check whether serial recv interrupt bit set
231         return
232
233         ; serial_receive:
234
235 ;       debug   'h'     ; write 'h' to serial port
236         bt_fa_if1 RCSTA,FERR            ; if FERR set (= framing error), then panic
237         goto    panic_ferr
238         bt_fa_if1 RCSTA,OERR            ; if OERR set (= overrun error), then panic
239         goto    panic_oerr
240
241         mov_ff  fromserial,FSR0L        ; set low byte of INDF0 pointer
242         mov_lw  bufferpage
243         mov_wfa FSR0H                   ; set high byte of INDF0 pointer
244 ;       debug   '1'
245         mov_fw  RCREG                   ; get received byte
246         mov_wf  INDF0                   ; copy to received register
247
248 ; check whether bit 7 is clear.
249 ; If so, move to next buffer (or process other kind of message)
250         bra_nn  end_message             ; (STATUS N still from mov_fw RCREG)
251 ;       debug   '3'
252
253 ; If not, move to next byte
254         inc_fa  fromserial              ; advance fromserial pointer by 1 byte
255 ;       debug   '4'
256
257         mov_lw  0x0f
258         and_wfw fromserial
259         bra_z   receive_message_too_long
260 serial_receive_done
261         intrl_handled_nostack
262
263 receive_too_much_nmra           panic   morse_HN
264 receive_message_too_long        panic   morse_HW
265
266 ; *** I *think* the interrupt bit is cleared by reading out of RCREG
267 ; but this may be something to try in debugging if stuff doesn't work
268
269 end_message
270 ; so what's the first byte then ?
271         mov_lw  0xf0
272         and_wff FSR0L
273         and_wff fromserial              ; fromserial := 00BB0000
274         com_fw  INDF0                   ;  where BB is this buffer
275         bra_nz  not_nmra_message
276
277 ; so, first byte is FF (since complement of it is zero)
278 ; so, move to next buffer
279 advance_write_buffer
280
281 ; increment top 4 bits of fromserial (already cleared low 4 bits)
282         debug   '5'
283         mov_fw  fromserial              ; W =           00BB0000
284         add_lw  0x10                    ; W =           0?CC0000
285         bc_w    6                       ; W =           00CC0000
286         mov_wf  t                       ; t =           00CC0000
287                                         ;  where BB is this buffer
288                                         ;   and CC is next buffer
289
290         xor_wfw totrack                 ; W =           0WDD????
291         and_lw  0x70                    ;               .WDD....
292         bra_z   receive_too_much_nmra   ; where DD is (fromserial buffer)
293                                         ;  xor (totrack buffer)
294                                         ; and W is 1 iff xmitting watchog
295
296         mov_fw  t                       ; W =           00CC0000
297         mov_wf  fromserial
298
299         bra     serial_receive_done
300
301 not_nmra_message
302         ; we've already set FSR0 and fromserial to point
303         ;  back to beginning of the same buffer
304         call    serialrx_generalmsg
305         bra     serial_receive_done
306
307 ;****************************************************************************
308
309 near_interrupt_high code
310
311 master_interrupt_high_notnmra
312         panic   morse_IH
313
314 master_interrupt_high @
315         bt_f_if0 INTCON,TMR0IF  ; check whether timer0 interrupt set
316         bra     master_interrupt_high_notnmra
317         ; timer0 interrupt
318
319         debug   ','     ; write 'j' to serial port
320         bc_fa   INTCON,TMR0IF   ; clear interrupt-set bit
321
322         ifdef   SLOW_VERSION
323         mov_lw  0x01    ; (testing)
324         endif
325
326         ifndef  SLOW_VERSION
327         mov_lw  nmra_master_t0init
328         endif
329
330         mov_wfa TMR0L           ; set timer0 to appropriate value (so interrupt takes 58us)
331
332
333         debug   'k'     ; write 'k' to serial port
334 ; check next action - if 0, change to 1 and return
335         bt_fa_if1 nmractrl,nextaction
336         bra     toggle_output
337         bs_fa   nmractrl,nextaction
338         debug   '1'     ; write 'k' to serial port
339         retfie_r
340
341
342 ; if next action = 1, then toggle output
343
344 toggle_output
345         debug   'l'     ; write 'l' to serial port
346         btg_fa  PORTC,0         ; toggle booster output pin
347         bt_fa_if0 PORTC,0
348         bra     decide_next_bit
349         ; we're in mid-bit:
350 ; if transition was 0->1 then we are mid-bit, so copy transmitbit to
351 ; nextaction in preparation for 2nd half of bit and then return
352
353         debug   'm'     ; write 'm' to serial port
354         bc_fa   nmractrl,nextaction
355         bt_fa_if1 nmractrl,transmitbit
356         bs_fa   nmractrl,nextaction
357         debug   '2'     ; write 'k' to serial port
358         retfie_r
359
360
361 decide_next_bit
362
363         debug   'n'     ; write 'n' to serial port
364 ; check whether current to-track buffer = current from-serial buffer
365 ; if yes, transmit 1s (set transmitbit=1, nextaction=1 and return)
366
367         mov_fw  fromserial
368         xor_wfwa totrack
369         and_lw  0x70
370         bra_nz  read_from_buffer
371         bs_fa   nmractrl,transmitbit
372         bs_fa   nmractrl,nextaction
373         debug   '3'
374         retfie_r
375
376
377 read_from_buffer
378         debug   'o'
379
380 ; if currently on bit 7, want to skip to bit 6
381 ;*** wouldn't it be easier to start on bit 6 ? :-)  -iwj
382
383         bt_fa_if1 totrackbit,7
384         rr_fa   totrackbit              ; rotate mask right
385
386 ; if not on bit 7 ,
387 ; set na=cb=bit value, advance bit (i.e. rotate transmitbit right),
388 ; check if bit7, if so, advance byte, return
389
390         debug   'p'
391         mov_fw  INDF2
392         and_wfwa totrackbit             ; select bit to be transmitted
393
394         bra_z   zero_bit_to_track
395         bra     one_bit_to_track
396
397 zero_bit_to_track
398         debug   '_'     ; write 'q' to serial port
399         debug   '0'     ; write 'q' to serial port
400         debug   '_'     ; write 'q' to serial port
401         bc_fa   nmractrl,transmitbit
402         bc_fa   nmractrl,nextaction
403         bra     advance_bit
404
405 one_bit_to_track
406         debug   '_'     ; write 'q' to serial port
407         debug   '1'     ; write 'r' to serial port
408         debug   '_'     ; write 'q' to serial port
409         bs_fa   nmractrl,transmitbit
410         bs_fa   nmractrl,nextaction
411         bra     advance_bit
412
413
414
415 advance_bit
416 ; rotate transmitbit to next position
417
418         debug   's'     ; write 's' to serial port
419         rr_fa   totrackbit              ; rotate mask right
420 ;*** surely rrnc (`rotate right not through carry' I assume)
421 ;*** will leave a copy of the top bit in the N flag ?  Then you
422 ;*** can use branch if negative.  -iwj
423         bt_fa_if1 totrackbit,7
424         rcall   advance_pointer
425         debug   '5'     ; write 's' to serial port
426
427         retfie_r
428
429
430
431 advance_pointer
432
433         debug   't'     ; write 't' to serial port
434
435 ; currently on bit 7 of the byte, after having read rest of byte to
436 ; track; check whether it is 1 or 0
437
438 ; if set, move to next buffer
439         bt_fa_if0 INDF2,7
440         rcall   advance_read_buffer
441
442 ; if not set, move to next byte of samebuffer (increment totrack)
443 ; (will be on bit 7 at this point anyway so no need to change totrackbit)
444         bt_fa_if1 INDF2,7
445         inc_fa  totrack
446         return
447
448
449 advance_read_buffer
450
451 ; move pointer to next buffer
452 ; clear low 4 bits of totrack and increment top 4 bits
453 ; aaaabbbb -> bbbbaaaa -> bbbb(aaaa+1) -> 0000(aaaa+1) -> (aaaa+1)0000
454         bt_f_if1 totrack, 6 ; were we doing watchdog broadcasts ?
455         bra     advance_read_buffer_ifwatchdog
456         mov_fw  totrack
457         add_lw  0x10
458         and_lw  0x30
459         mov_wf  totrack
460         debug   '9'     ; write 'v' to serial port
461
462         inc_f   acknmra
463         bs_f    PIE1, TXIE      ; ensure we tell host to send us more
464         bt_f_if0 acknmra, 2
465         return
466         ; oops:
467         panic   morse_HM
468
469 advance_read_buffer_ifwatchdog
470         ; just loop round the buffer
471         mov_lw  0x40
472         mov_wf  totrack
473         return
474
475 ;****************************************************************************
476
477 panic_oerr      panic   morse_HO
478 panic_ferr      panic   morse_HF
479
480 ;****************************************************************************
481
482   include final.inc