chiark / gitweb /
reserve /^ [^ ;]/ for statements inserted for debugging purposes
[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  ;
36  ; fromserial and totrack are pointers into the buffer page
37  ;
38  ; fromserial points to the first empty byte, where we will put new
39  ; data provided by the host; this means that (fromserial & 0xf0) is
40  ; the first buffer which fails to contain a complete nmra message.
41  ; fromserial is updated only by the (low priority) serial receive
42  ; interrupt handler.  Furthermore, it always maintains a correct
43  ; and valid value for use by the high-priority nmra readout ISR.
44  ;
45  ; totrack points to the first remaining full byte, which is currently
46  ; being transmitted; if nothing is currently being transmitted
47  ; because there is no nmra data at all then totrack points to the
48  ; start of the same buffer as fromserial
49  ;
50  ; We maintain the following properties, notionally:
51  ;
52  ;  totrack <= fromserial < totrack
53  ;
54  ;            \            \
55  ;             \            `- equal would mean overrun
56  ;              \
57  ;               `- equal if we have nothing but
58  ;                   idle to trasmit
59  ;
60
61 bufferpage      equ     5
62
63 buffer_section udata bufferpage << 8
64 buffer0 res 16
65 buffer1 res 16
66 buffer2 res 16
67 buffer3 res 16
68
69   code
70
71 ;****************************************************************************
72
73 macros
74
75 ; macro to call subroutine to transmit over serial port for debugging
76 ; takes 8-bit value, puts in W, invokes debug_serial_transmit
77
78         ifndef  SLOW_VERSION
79 debug macro debugvalue
80         endm
81         endif
82
83         ifdef   SLOW_VERSION
84 debug macro debugvalue
85         mov_lw  debugvalue
86         call    debug_serial_transmit
87         endm
88         endif
89
90 debug_serial_transmit
91         mov_wfa TXREG           ; move contents of W (i.e. debugvalue)
92                                 ;       to TXREG for transmission
93 waitfortsr
94         bt_fa_if0 TXSTA,1
95         bra     waitfortsr
96         return
97
98 ;****************************************************************************
99
100 serial_init
101 ; serial set-up
102 ; initial config - TXSTA register p181
103         bc_fa     TXSTA,6       ; p181, set 8-bit mode
104         bs_fa   TXSTA,5         ; transmit enable
105         bc_fa   TXSTA,4         ; asynchronous mode
106         bsc_txsta_brgh
107
108 ; initial config - RCSTA register p182
109         bs_fa   RCSTA,7         ; serial port enable (p182)
110         bc_fa   RCSTA,6         ; 8-bit reception
111         bs_fa   RCSTA,4         ; enable continuous receive
112
113 ; set SPBRG to get correct baud rate according to table top right p186
114 ; (Tosc = 20MHz, desired baud rate = 9600)
115         movlw_movwf_spbrg
116
117         mov_lw  ~((1<<RCIP) | (1<<TXIP))
118         and_wff IPR1            ; serial interrupts: low priority
119         mov_lw  (1<<RCIE) | (1<<TXIE)
120         ior_wff PIE1            ; serial interrupt: interrupt enable
121
122         return
123
124 ;----------------------------------------------------------------------------
125
126 nmra_init
127 ; timer 0 set-up
128 ; timer0 initial config for NMRA timer
129
130         ifdef   SLOW_VERSION
131         bc_fa     T0CON,6         ; p107 Timer0 -> 16bit mode (testing)
132         endif
133
134         ifndef  SLOW_VERSION
135         bs_fa     T0CON,6         ; p107 Timer0 -> 8bit mode (not-testing)
136         endif
137
138         bc_fa     T0CON,5         ; timer0 use internal clock
139         bc_fa     T0CON,3         ; use prescaler
140
141         ifndef  SLOW_VERSION
142         bc_fa     T0CON,2         ; }
143         bc_fa     T0CON,1         ; } prescale value 1:2
144         bc_fa     T0CON,0         ; } (not-testing)
145         endif
146
147         ifdef   SLOW_VERSION
148         bs_fa     T0CON,2         ; }
149         bc_fa     T0CON,1         ; } prescale value 1:16
150         bc_fa     T0CON,0         ; } (testing)
151         endif
152
153         debug   'b'     ; write 'b' to serial port
154 ;----------------------------------------------------------------------------
155
156 ; initialise buffers (for bank 5, for nmra from-serial/to-track buffers)
157
158         mov_lw  bufferpage
159         mov_wf  FSR2H
160
161         clr_fa  nmractrl        ; for bits relevant to control of nmra stream
162         clr_fa  fromserial      ; for loc'n of write-from-usart ptr in buffers
163         clr_fa  totrack         ; for loc'n of send-to-track ptr in buffers
164                                 ; all in access bank
165         clr_f   acknmra
166
167         debug   'c'     ; write 'c' to serial port
168 ;----------------------------------------------------------------------------
169
170
171 ; initialise next action/transmit bit
172         bs_fa   nmractrl,nextaction
173         bs_fa   nmractrl,transmitbit
174
175 nmra_restartmessage     ; Entrypoint from power_polarising_tick, at end
176                         ;  of settle time.  Goes back to beginning of
177                         ;  current message (if any) and retransmits it.
178                         ;  Also, enables booster PWM and Timer 0 interrupts.
179         mov_lw  0x0f
180         and_wff totrack
181
182 ; initialise totrackbit bitmask
183         mov_lw  0x80
184         mov_wfa totrackbit      ; make bit mask be 1000 0000
185
186 ; initialise booster direction
187         bc_fa   TRISC,0         ; make pin 0 (booster direction) output
188         bc_fa   PORTC,0         ; set low initially
189
190 ; set booster pwm high
191         bs_fa   PORTC,1         ; booster pwm high
192         bc_fa   TRISC,1         ; make pin 1 (booster pwm) output
193
194         debug   'd'     ; write 'd' to serial port
195 ;----------------------------------------------------------------------------
196
197 ; interrupt set-up
198
199 ; interrupt set-up for timer0 interrupt p79/80
200         bs_fa   INTCON2,2       ; timer0 overflow high priority
201         bs_fa   INTCON,5        ; enable timer0 interrupts
202
203
204         debug   'e'     ; write 'e' to serial port
205
206         return
207
208 ;****************************************************************************
209
210 nmra_serialrx_intrl
211         bt_f_if0 PIR1,RCIF      ; check whether serial recv interrupt bit set
212         return
213
214         ; serial_receive:
215
216 ;       debug   'h'     ; write 'h' to serial port
217         bt_fa_if1 RCSTA,FERR            ; if FERR set (= framing error), then panic
218         goto    panic_ferr
219         bt_fa_if1 RCSTA,OERR            ; if OERR set (= overrun error), then panic
220         goto    panic_oerr
221
222         mov_ff  fromserial,FSR0L        ; set low byte of INDF0 pointer
223         mov_lw  bufferpage
224         mov_wfa FSR0H                   ; set high byte of INDF0 pointer
225 ;       debug   '1'
226         mov_fw  RCREG                   ; get received byte
227         mov_wf  INDF0                   ; copy to received register
228
229 ; check whether bit 7 is clear.
230 ; If so, move to next buffer (or process other kind of message)
231         bra_nn  end_message             ; (STATUS N still from mov_fw RCREG)
232 ;       debug   '3'
233
234 ; If not, move to next byte
235         inc_fa  fromserial              ; advance fromserial pointer by 1 byte
236 ;       debug   '4'
237
238         mov_lw  0x0f
239         and_wfw fromserial
240         bra_z   receive_message_too_long
241 serial_receive_done
242         intrl_handled_nostack
243
244 receive_too_much_nmra           panic   morse_HN
245 receive_message_too_long        panic   morse_HW
246
247 ; *** I *think* the interrupt bit is cleared by reading out of RCREG
248 ; but this may be something to try in debugging if stuff doesn't work
249
250 end_message
251 ; so what's the first byte then ?
252         mov_lw  0xf0
253         and_wff FSR0L
254         and_wff fromserial              ; fromserial := 00BB0000
255         com_fw  INDF0                   ;  where BB is this buffer
256         bra_nz  not_nmra_message
257
258 ; so, first byte is FF (since complement of it is zero)
259 ; so, move to next buffer
260 advance_write_buffer
261
262 ; increment top 4 bits of fromserial (already cleared low 4 bits)
263         debug   '5'
264         mov_fw  fromserial              ; W =           00BB0000
265         add_lw  0x10                    ; W =           0?CC0000
266         mov_wf  t                       ; t =           0?CC0000
267                                         ;  where BB is this buffer
268                                         ;   and CC is next buffer
269
270         xor_wfw totrack                 ; W =           0?DD????
271         and_lw  0x30                    ;               ..DD....
272         bra_z   receive_too_much_nmra   ; where DD is (fromserial buffer)
273                                         ;  xor (totrack buffer)
274
275         mov_wf  t                       ; W =           0?CC0000
276         bc_w    6                       ; W =           00CC0000
277         mov_fw  fromserial
278
279         bra     serial_receive_done
280
281 not_nmra_message
282         ; we've already set FSR0 and fromserial to point
283         ;  back to beginning of the same buffer
284         call    serialrx_generalmsg
285         bra     serial_receive_done
286
287 ;****************************************************************************
288
289 near_interrupt_high code
290
291 master_interrupt_high_notnmra
292         panic   morse_IH
293
294 master_interrupt_high
295         bt_f_if0 INTCON,TMR0IF  ; check whether timer0 interrupt set
296         bra     master_interrupt_high_notnmra
297         ; timer0 interrupt
298
299         debug   ','     ; write 'j' to serial port
300         bc_fa   INTCON,TMR0IF   ; clear interrupt-set bit
301
302         ifdef   SLOW_VERSION
303         mov_lw  0x01    ; (testing)
304         endif
305
306         ifndef  SLOW_VERSION
307         mov_lw  0x73    ; (not-testing)
308         endif
309
310         mov_wfa TMR0L           ; set timer0 to appropriate value (so interrupt takes 58us)
311
312
313         debug   'k'     ; write 'k' to serial port
314 ; check next action - if 0, change to 1 and return
315         bt_fa_if1 nmractrl,nextaction
316         bra     toggle_output
317         bs_fa   nmractrl,nextaction
318         debug   '1'     ; write 'k' to serial port
319         retfie_r
320
321
322 ; if next action = 1, then toggle output
323
324 toggle_output
325         debug   'l'     ; write 'l' to serial port
326         btg_fa  PORTC,0         ; toggle booster output pin
327         bt_fa_if0 PORTC,0
328         bra     decide_next_bit
329         ; we're in mid-bit:
330 ; if transition was 0->1 then we are mid-bit, so copy transmitbit to
331 ; nextaction in preparation for 2nd half of bit and then return
332
333         debug   'm'     ; write 'm' to serial port
334         bc_fa   nmractrl,nextaction
335         bt_fa_if1 nmractrl,transmitbit
336         bs_fa   nmractrl,nextaction
337         debug   '2'     ; write 'k' to serial port
338         retfie_r
339
340
341 decide_next_bit
342
343         debug   'n'     ; write 'n' to serial port
344 ; check whether current to-track buffer = current from-serial buffer
345 ; if yes, transmit 1s (set transmitbit=1, nextaction=1 and return)
346
347         mov_fw  fromserial
348         xor_wfwa totrack
349         and_lw  0x30
350         bra_nz  read_from_buffer
351         bs_fa   nmractrl,transmitbit
352         bs_fa   nmractrl,nextaction
353         debug   '3'
354         bs_fa   TRISB,0         ; user fault Z (red)
355         retfie_r
356
357
358 read_from_buffer
359         bc_fa   TRISB,0         ; user fault L (purple)
360         debug   'o'
361
362 ; if currently on bit 7, want to skip to bit 6
363 ;*** wouldn't it be easier to start on bit 6 ? :-)  -iwj
364
365         bt_fa_if1 totrackbit,7
366         rr_fa   totrackbit              ; rotate mask right
367
368 ; if not on bit 7 ,
369 ; set na=cb=bit value, advance bit (i.e. rotate transmitbit right),
370 ; check if bit7, if so, advance byte, return
371
372         debug   'p'
373         mov_fw  INDF2
374         and_wfwa totrackbit             ; select bit to be transmitted
375
376         bra_z   zero_bit_to_track
377         bra     one_bit_to_track
378
379 zero_bit_to_track
380         debug   '_'     ; write 'q' to serial port
381         debug   '0'     ; write 'q' to serial port
382         debug   '_'     ; write 'q' to serial port
383         bc_fa   nmractrl,transmitbit
384         bc_fa   nmractrl,nextaction
385         bra     advance_bit
386
387 one_bit_to_track
388         debug   '_'     ; write 'q' to serial port
389         debug   '1'     ; write 'r' to serial port
390         debug   '_'     ; write 'q' to serial port
391         bs_fa   nmractrl,transmitbit
392         bs_fa   nmractrl,nextaction
393         bra     advance_bit
394
395
396
397 advance_bit
398 ; rotate transmitbit to next position
399
400         debug   's'     ; write 's' to serial port
401         rr_fa   totrackbit              ; rotate mask right
402 ;*** surely rrnc (`rotate right not through carry' I assume)
403 ;*** will leave a copy of the top bit in the N flag ?  Then you
404 ;*** can use branch if negative.  -iwj
405         bt_fa_if1 totrackbit,7
406         rcall   advance_pointer
407         debug   '5'     ; write 's' to serial port
408
409         retfie_r
410
411
412
413 advance_pointer
414
415         debug   't'     ; write 't' to serial port
416
417 ; currently on bit 7 of the byte, after having read rest of byte to
418 ; track; check whether it is 1 or 0
419
420 ; if set, move to next buffer
421         bt_fa_if1 INDF2,7
422         rcall   advance_read_buffer
423
424 ; if not set, move to next byte of samebuffer (increment totrack)
425 ; (will be on bit 7 at this point anyway so no need to change totrackbit)
426         bt_fa_if0 INDF2,7
427         inc_fa  totrack
428         return
429
430
431 advance_read_buffer
432
433 ; move pointer to next buffer
434 ; clear low 4 bits of totrack and increment top 4 bits
435 ; aaaabbbb -> bbbbaaaa -> bbbb(aaaa+1) -> 0000(aaaa+1) -> (aaaa+1)0000
436         debug   '7'     ; write 'v' to serial port
437         swap_fa totrack
438         inc_fa  totrack
439         mov_lw  0xF
440         and_wffa totrack
441         swap_fa totrack
442         debug   '8'     ; write 'v' to serial port
443
444 ; clear bit 6 (will set back to buffer 0 if has overflowed to 4)
445         bc_fa   totrack,6
446         debug   '9'     ; write 'v' to serial port
447
448         inc_f   acknmra
449         bs_f    PIE1, TXIE      ; ensure we tell host to send us more
450         bt_f_if0 acknmra, 2
451         return
452         ; oops:
453         panic   morse_HM
454
455
456
457 ;****************************************************************************
458
459 panic_oerr      panic   morse_HO
460 panic_ferr      panic   morse_HF
461
462 ;****************************************************************************
463
464   include final.inc