chiark / gitweb /
correct points which were all noted backwards
[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
91 debug_serial_transmit
92         mov_wfa TXREG           ; move contents of W (i.e. debugvalue)
93         endif
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
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
209
210
211         debug   'e'     ; write 'e' to serial port
212
213         return
214
215 ;****************************************************************************
216
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.
220         return
221
222 nmra_sendwatchdog_stop @
223         bt_f_if0 totrack, 6
224         return ; not transmitting watchdog
225         mov_fw  fromserial
226         and_lw  0x30
227         mov_wf  totrack
228         return
229
230 ;****************************************************************************
231
232 nmra_serialrx_intrl @
233         bt_f_if0 PIR1,RCIF      ; check whether serial recv interrupt bit set
234         return
235
236         ; serial_receive:
237
238 ;       debug   'h'     ; write 'h' to serial port
239         bt_fa_if1 RCSTA,FERR            ; if FERR set (= framing error), then panic
240         goto    panic_ferr
241         bt_fa_if1 RCSTA,OERR            ; if OERR set (= overrun error), then panic
242         goto    panic_oerr
243
244         mov_ff  fromserial,FSR0L        ; set low byte of INDF0 pointer
245         mov_lw  bufferpage
246         mov_wfa FSR0H                   ; set high byte of INDF0 pointer
247 ;       debug   '1'
248         mov_fw  RCREG                   ; get received byte
249         mov_wf  INDF0                   ; copy to received register
250
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)
254 ;       debug   '3'
255
256 ; If not, move to next byte
257         inc_fa  fromserial              ; advance fromserial pointer by 1 byte
258 ;       debug   '4'
259
260         mov_lw  0x0f
261         and_wfw fromserial
262         bra_z   receive_message_too_long
263 serial_receive_done
264         intrl_handled_nostack
265
266 receive_too_much_nmra           panic   morse_HN
267 receive_message_too_long        panic   morse_HW
268
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
271
272 end_message
273 ; so what's the first byte then ?
274         mov_lw  0xf0
275         and_wff FSR0L
276         and_wff fromserial              ; fromserial := 00BB0000
277         com_fw  INDF0                   ;  where BB is this buffer
278         bra_nz  not_nmra_message
279
280 ; so, first byte is FF (since complement of it is zero)
281 ; so, move to next buffer
282 advance_write_buffer
283
284 ; increment top 4 bits of fromserial (already cleared low 4 bits)
285         debug   '5'
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
292
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
298
299         mov_fw  t                       ; W =           00CC0000
300         mov_wf  fromserial
301
302         bra     serial_receive_done
303
304 not_nmra_message
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
309
310 ;****************************************************************************
311
312 near_interrupt_high code
313
314 master_interrupt_high_notnmra
315         panic   morse_IH
316
317 master_interrupt_high @
318         bt_f_if0 INTCON,TMR0IF  ; check whether timer0 interrupt set
319         bra     master_interrupt_high_notnmra
320         ; timer0 interrupt
321
322         debug   ','     ; write 'j' to serial port
323         bc_fa   INTCON,TMR0IF   ; clear interrupt-set bit
324
325         ifdef   SLOW_VERSION
326         mov_lw  0x01    ; (testing)
327         endif
328
329         ifndef  SLOW_VERSION
330         mov_lw  nmra_master_t0init
331         endif
332
333         mov_wfa TMR0L           ; set timer0 to appropriate value (so interrupt takes 58us)
334
335
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
339         bra     toggle_output
340         bs_fa   nmractrl,nextaction
341         debug   '1'     ; write 'k' to serial port
342         retfie_r
343
344
345 ; if next action = 1, then toggle output
346
347 toggle_output
348         debug   'l'     ; write 'l' to serial port
349         btg_fa  PORTC,0         ; toggle booster output pin
350         bt_fa_if0 PORTC,0
351         bra     decide_next_bit
352         ; we're in mid-bit:
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
355
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
361         retfie_r
362
363
364 decide_next_bit
365
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)
369
370         mov_fw  fromserial
371         xor_wfwa totrack
372         and_lw  0x70
373         bra_nz  read_from_buffer
374         bs_fa   nmractrl,transmitbit
375         bs_fa   nmractrl,nextaction
376         debug   '3'
377         retfie_r
378
379
380 read_from_buffer
381         debug   'o'
382
383 ; if currently on bit 7, want to skip to bit 6
384 ;*** wouldn't it be easier to start on bit 6 ? :-)  -iwj
385
386         bt_fa_if1 totrackbit,7
387         rr_fa   totrackbit              ; rotate mask right
388
389 ; if not on bit 7 ,
390 ; set na=cb=bit value, advance bit (i.e. rotate transmitbit right),
391 ; check if bit7, if so, advance byte, return
392
393         debug   'p'
394         mov_fw  INDF2
395         and_wfwa totrackbit             ; select bit to be transmitted
396
397         bra_z   zero_bit_to_track
398         bra     one_bit_to_track
399
400 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
406         bra     advance_bit
407
408 one_bit_to_track
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
414         bra     advance_bit
415
416
417
418 advance_bit
419 ; rotate transmitbit to next position
420
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
429
430         retfie_r
431
432
433
434 advance_pointer
435
436         debug   't'     ; write 't' to serial port
437
438 ; currently on bit 7 of the byte, after having read rest of byte to
439 ; track; check whether it is 1 or 0
440
441 ; if set, move to next buffer
442         bt_fa_if0 INDF2,7
443         rcall   advance_read_buffer
444
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)
447         bt_fa_if1 INDF2,7
448         inc_fa  totrack
449         return
450
451
452 advance_read_buffer
453
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
459         mov_fw  totrack
460         add_lw  0x10
461         and_lw  0x30
462         mov_wf  totrack
463         debug   '9'     ; write 'v' to serial port
464
465         inc_f   acknmra
466         bs_f    PIE1, TXIE      ; ensure we tell host to send us more
467         bt_f_if0 acknmra, 2
468         return
469         ; oops:
470         panic   morse_HM
471
472 advance_read_buffer_ifwatchdog
473         ; just loop round the buffer
474         mov_lw  0x40
475         mov_wf  totrack
476         return
477
478 ;****************************************************************************
479
480 panic_oerr      panic   morse_HO
481 panic_ferr      panic   morse_HF
482
483 ;****************************************************************************
484
485   include final.inc