5f4733809102620ab702d56261fd9682054bdb81
[wj-unilink.git] / wj-uni.asm
1         title   "PIC16F870 Unilink Interface by Werner Johansson (c) 2003"\r
2         subtitl "Definitions"\r
3         list    c=132,P=16F870,R=DEC,F=inhx8m\r
4         include "p16f870.inc"            ; Standard equates & Macros\r
5         ERRORLEVEL 1,-302               ; Get rid of those annoying 302 msgs!\r
6 \r
7 \r
8 ;----------------------------------------------------------------\r
9 ;  The Configuration Word\r
10       __CONFIG _HS_OSC&_WDT_OFF&_PWRTE_ON&_BODEN_ON&_LVP_OFF&_CPD_OFF&_WRT_ENABLE_ON&_DEBUG_OFF&_CP_OFF\r
11 \r
12 ;----------------------------------------------------------------\r
13 ;       HISTORY\r
14 ;----------------------------------------------------------------\r
15 ;  Version\r
16 ;\r
17 ;  0.1  Receives Unilink data OK, relays it to serial\r
18 ;  0.0  Very first "Fucking No Work!" version\r
19 ;\r
20 ;----------------------------------------------------------------\r
21 \r
22 ; Unilink BUSON IN (blue) connected to RC2/CCP1\r
23 ; Unilink DATA (green) connected to RC3\r
24 ; Unilink BUSON OUT (blue) connected to RC4 (this is for daisy-chaining)\r
25 ; Unilink CLK (yellow) connected to RB0/INT (Interrupt pin)\r
26 ; Unilink RST (lilac) connected to RA4\r
27 ; LCD RS connected to pin RB1\r
28 ; LCD RW connected to pin RB2\r
29 ; LCD E connected to pin RB3\r
30 ; LCD DB4-DB7 connected to RB4-RB7\r
31 ; RS-232 TX from computer connected to RC7/RX\r
32 ; RS-232 RX to computer connected to RC6/TX\r
33 ; RS-232 RI to computer connected to RC5\r
34 \r
35 ; This leaves RC0, RC1 and the analog inputs (AN0-AN4) free for now...\r
36 \r
37 #define LCD_RS_BIT      PORTB,1\r
38 #define LCD_RW_BIT      PORTB,2\r
39 #define LCD_E_BIT       PORTB,3\r
40 #define LCD_DB4_BIT     PORTB,4\r
41 #define LCD_DB5_BIT     PORTB,5\r
42 #define LCD_DB6_BIT     PORTB,6\r
43 #define LCD_DB7_BIT     PORTB,7\r
44 \r
45 ;----------------------------------------------------------------\r
46 ;       File register usage\r
47 \r
48 Dcount  equ     20h\r
49 e_LEN   equ     21h\r
50 Icount  equ   2Dh   ; Offset of string to print\r
51 TxTemp  equ   2Eh   ; blahblah\r
52 TxTemp2 equ   2Fh   ; Blahblah2\r
53 \r
54 LCDWTmp equ     30h\r
55 Dcount2 equ     31h\r
56 temp    equ     32h\r
57 \r
58 DataCount       equ     33h\r
59 DataStore       equ     34h\r
60 \r
61 IRQW            equ     7fh\r
62 IRQSTATUS       equ     7eh\r
63 IRQPCLATH       equ     7dh\r
64 \r
65         subtitl "Startup"\r
66         page\r
67 ;----------------------------------------------------------------\r
68 ;  Power up/Reset starting point [den rulerar]\r
69 \r
70         org     0\r
71         call    Bootstrap       ; Call Flash Load routine\r
72         call    LCDInit         ; Initialize LCD I/F\r
73         call    IRQInit         ; Set up and start the IRQ handler\r
74         goto    Main            ; Run the main program loop (skip the IRQ handler)\r
75 \r
76         subtitl "IRQ Handler"\r
77 ;----------------------------------------------------------------\r
78 ;  Interrupt handler always starts at addr 4\r
79 \r
80         org     4               ; Must be on Address 4!\r
81         movwf   IRQW            ; Save W\r
82         swapf   STATUS,w        ; Get the status register into w\r
83         clrf    STATUS          ; Zero out the status reg, gives us Bank0 all the time\r
84         movwf   IRQSTATUS\r
85         movf    PCLATH,w\r
86         movwf   IRQPCLATH\r
87         clrf    PCLATH\r
88 \r
89         btfss   INTCON,INTF             ; Check if it's INT (CLK)\r
90         goto    IRQNotINT               ; Nope\r
91 \r
92 ; If there's activity on the clock line (the clock goes high) we stay in here until we have clocked eight bits\r
93 ; - this saves us a lot of context switching (and it's just a few hundred cpu cycles after all (20us*8 bits=\r
94 ; 160us=800 instruction cycles (5 MIPS @ 20MHz), not even a problem for serial input if we're not getting more than\r
95 ; 6250 bytes per second from the UART, and the 2-byte FIFO somehow fills up (this should be impossible even @ 115200\r
96 ; as we're only calling this blocking INT handler a maximum of 1000 times per second, halting INT's for 1/6250 of a second,\r
97 ; this gives the CPU ample of time to deal with all bytes from the USART. I'm checking the OERR (Serial Overrun) bit\r
98 ; to catch this though..\r
99 \r
100         movlw   8                       ; Loop this many times\r
101         movwf   DataCount\r
102 \r
103 CLKWaitHigh\r
104         btfss   PORTC,2                 ; Check for BUSON\r
105         goto    IRQAfterINT\r
106         btfss   PORTB,0                 ; Wait for clock to go high\r
107         goto    CLKWaitHigh\r
108 \r
109 CLKWaitLow\r
110         btfss   PORTC,2                 ; Check for BUSON\r
111         goto    IRQAfterINT\r
112         btfsc   PORTB,0                 ; Wait for clock to go low\r
113         goto    CLKWaitLow\r
114 \r
115         clrc                            ; Clear carry (this way the DataStore byte doesn't have to be cleared before)\r
116         btfss   PORTC,3                 ; Test DATA\r
117         setc                            ; Set carry if data is LOW (data is inverted!)\r
118         rlf     DataStore,f             ; Shift it into our accumulator\r
119 \r
120         decfsz  DataCount,f             ; Loop once more perhaps?\r
121         goto    CLKWaitHigh\r
122 \r
123 ; Successfully received a byte here, run it through a state machine to figure out what to do\r
124 ; (several possibilites exists here:\r
125 ; If more than 1.1ms has passed since last receive, reset receive counter to zero\r
126 ; If receive counter is zero and the received byte is a zero byte, discard it\r
127 ; Otherwise store the byte in our receive buffer and increment receive counter\r
128 ; If the receive counter is 3 check the two upper bits of recv'd byte (CMD1) - this tells us the length of the packet\r
129 ;   00 = short 6 byte packet\r
130 ;   10 = medium 11 byte packet\r
131 ;   11 = long 16 byte packet\r
132 ; Update the receive length byte accordingly\r
133 ; Check whether receive length and receive count are equal, that means that we're finished, flag this by setting \r
134 ;  the high bit of receive length\r
135 \r
136 IRQAfterINT\r
137 \r
138         bcf     INTCON,INTF             ; Clear our IRQ\r
139 \r
140 IRQNotINT\r
141 \r
142         movf    IRQPCLATH,w\r
143         movwf   PCLATH          ; Restore PCLATH\r
144         swapf   IRQSTATUS,w\r
145         movwf   STATUS          ; Restore STATUS\r
146         swapf   IRQW,f\r
147         swapf   IRQW,w          ; Restore W\r
148         retfie                  ; Interrupt return\r
149 \r
150 \r
151 \r
152         subtitl "Main loop"\r
153         page\r
154 \r
155 ;----------------------------------------------------------------\r
156 ;  Data can be stored between here and 100h...\r
157 \r
158 StartUpText1\r
159         DT      "-WJ UniLink I/F-"\r
160 StartUpText2\r
161         DT      "Code and design:"\r
162 StartUpText3\r
163         DT      "**TCC of Yodel**"\r
164 \r
165                \r
166 LookUp  movwf   PCL             ; Go to it\r
167 \r
168 ;----------------------------------------------------------------\r
169 ;  Main program begins here. [Called after bootloader, lcdinit and irqinit...]\r
170 \r
171         org     100h\r
172 Main\r
173 \r
174         bsf     STATUS,RP0\r
175         bsf     TXSTA,TXEN              ; Enable UART TX\r
176         bcf     STATUS,RP0              ; Back to bank 0\r
177 \r
178         bsf     RCSTA,SPEN              ; Enable serial port\r
179         bsf     RCSTA,CREN              ; Enable UART RX\r
180 \r
181 retry\r
182         \r
183 ;       movlw   8                       ; Loop this many times\r
184 ;       movwf   DataCount\r
185 \r
186 ;CLKWaitHigh\r
187 ;       btfsc   PORTA,4                 ; Check for RST\r
188 ;       goto    retry\r
189 ;       btfss   PORTC,2                 ; Check for BUSON\r
190 ;       goto    retry\r
191 ;       btfss   PORTB,0                 ; Wait for clock to go high\r
192 ;       goto    CLKWaitHigh\r
193 ;CLKWaitLow\r
194 ;       btfsc   PORTA,4                 ; Check for RST\r
195 ;       goto    retry\r
196 ;       btfss   PORTC,2                 ; Check for BUSON\r
197 ;       goto    retry\r
198 ;       btfsc   PORTB,0                 ; Wait for clock to go low\r
199 ;       goto    CLKWaitLow\r
200 \r
201 ;       clrc                            ; Clear carry\r
202 ;       btfss   PORTC,3                 ; Test DATA\r
203 ;       setc                            ; Set carry if data is LOW (data is inverted!)\r
204 ;       rlf     DataStore,f             ; Shift it into our accumulator\r
205 \r
206 ;       decfsz  DataCount,f             ; Loop once more perhaps?\r
207 ;       goto    CLKWaitHigh\r
208 \r
209         bcf     LCD_RS_BIT      ;Command mode\r
210         movlw   80h             ;DisplayRam 0\r
211         call    TxLCDB\r
212         bsf     LCD_RS_BIT\r
213 \r
214         movlw   '0'\r
215         btfsc   PORTA,4         ; Test RST\r
216         movlw   'R'\r
217         call    TxLCDB\r
218 \r
219         movlw   '0'\r
220         btfsc   PORTB,0         ; Test CLK\r
221         movlw   'C'\r
222         call    TxLCDB\r
223 \r
224         movlw   '0'\r
225         btfsc   PORTC,2         ; Test BUSON-IN\r
226         movlw   'B'\r
227         call    TxLCDB\r
228 \r
229         movlw   '0'\r
230         btfsc   PORTC,3         ; Test DATA\r
231         movlw   'D'\r
232         call    TxLCDB\r
233 \r
234         movf    DataCount,w             ; Load bit counter (if 0 then byte is available)\r
235         skpz\r
236         goto    retry\r
237 \r
238         movf    DataStore,w             ; Get the result\r
239         decf    DataCount,f             ; Set it non-zero\r
240 \r
241         call    BootTXB                 ; Send to terminal\r
242 \r
243         goto    retry\r
244 \r
245 \r
246 \r
247         movlw   StartUpText1\r
248         call    TxLCD16B\r
249         call    LongDelay\r
250 \r
251         bsf     PORTA,4         ; turn off LED\r
252 \r
253         movlw   StartUpText2\r
254         call    TxLCD16B\r
255         call    LongDelay\r
256 \r
257         bcf     PORTA,4         ; turn on LED\r
258 \r
259         movlw   StartUpText3\r
260         call    TxLCD16B\r
261         call    LongDelay\r
262 \r
263         goto    retry\r
264 \r
265 \r
266 ;----------------------------------------------------------------\r
267 ; IRQInit - Sets up the IRQ Handler\r
268 \r
269 IRQInit\r
270         bsf     STATUS,RP0              ; Reg bank 1\r
271 ;       bcf     OPTION_REG,INTEDG       ; We want RB0 to give us an IRQ on the falling edge\r
272         bsf     INTCON,INTE             ; Enable the RB0/INT\r
273         bsf     INTCON,GIE              ; Enable global interrupts\r
274         bcf     STATUS,RP0              ; Back to bank 0\r
275         return\r
276 \r
277 ;----------------------------------------------------------------\r
278 ;  Initialize LCD Controller...\r
279 \r
280 LCDInit\r
281         clrf    PORTB\r
282         bsf     STATUS,RP0      ; Hi Bank\r
283         movlw   0cfh            ; RC4 & RC5 should be outputs...\r
284         movwf   TRISC           ; Yep.\r
285         movlw   001h            ; All but RB0 are outputs.\r
286         movwf   TRISB           ; Yep\r
287         bcf     OPTION_REG,NOT_RBPU     ; Turn on port B pull-up\r
288         bcf     STATUS,RP0      ; Restore Lo Bank\r
289 \r
290 ;       bcf     PORTA,4         ; turn on LED\r
291 \r
292 ;       movlw   44              ; Should be 16ms delay\r
293         movlw   255             ; Should be 16ms delay\r
294         call    DelayW\r
295 \r
296         movlw   3               ; Write 3 to the LCD\r
297         call    TxLCD           ; Send to LCD\r
298 ;       movlw   12              ; Should be 5ms delay\r
299         movlw   255             ; Should be 16ms delay\r
300         call    DelayW\r
301 \r
302         movlw   3               ; Write 3 to the LCD\r
303         call    TxLCD\r
304 ;       movlw   12              ; Should be 16ms delay\r
305         movlw   255             ; Should be 16ms delay\r
306         call    DelayW\r
307 \r
308         movlw   3               ; Write 3 to the LCD\r
309         call    TxLCD\r
310 ;       movlw   44\r
311         movlw   255             ; Should be 16ms delay\r
312         call    DelayW\r
313 \r
314         movlw   2               ;\\r
315         call    TxLCD           ; | 4-bit interface\r
316 ;       movlw   55              ; | After this we are ready to ROCK!\r
317         movlw   255             ; Should be 16ms delay\r
318         call    DelayW          ;/\r
319 \r
320 ;       bsf     PORTA,4         ; turn off LED\r
321 \r
322         movlw 28h               ; Some random commands :)))\r
323         call TxLCDB\r
324 \r
325         movlw 0ch               ; hmmm\r
326         call TxLCDB\r
327 \r
328         movlw 01h               ; hmmm\r
329         call TxLCDB\r
330 \r
331         movlw 06h               ; hmmm\r
332         call TxLCDB\r
333         \r
334         return\r
335    \r
336 ;----------------------------------------------------------------\r
337 ; LongDelay - Well, guess that for yourself...\r
338 \r
339 LongDelay\r
340 ;   btfss PORTB,6        ; Talk to da PC?\r
341 ;   goto PCTalk          ; Oh yeah...\r
342 \r
343    movlw 255\r
344    call DelayW\r
345    movlw 255\r
346    call DelayW\r
347    movlw 255\r
348    call DelayW\r
349    movlw 255\r
350    call DelayW\r
351    movlw 255\r
352    call DelayW\r
353    movlw 255\r
354    call DelayW\r
355    movlw 255\r
356    call DelayW\r
357    movlw 255\r
358    call DelayW\r
359    movlw 255\r
360    call DelayW\r
361    movlw 255\r
362    call DelayW\r
363    movlw 255\r
364    call DelayW\r
365    movlw 255\r
366    call DelayW\r
367    movlw 255\r
368    call DelayW\r
369    movlw 255\r
370    call DelayW\r
371    movlw 255\r
372    call DelayW\r
373    movlw 255\r
374    call DelayW\r
375    return\r
376 \r
377 ;----------------------------------------------------------------\r
378 ;  TxLCD16B\r
379 ;  Send a string to the LCD.\r
380 \r
381 TxLCD16B\r
382         movwf   Icount\r
383         bcf     LCD_RS_BIT\r
384         movlw   80h             ;DisplayRam 0\r
385         call    TxLCDB\r
386         bsf     LCD_RS_BIT\r
387         call    TxLCD8B\r
388         bcf     LCD_RS_BIT\r
389         movlw   80h+40          ;DisplayRam 40 (row 2)\r
390         call    TxLCDB\r
391         bsf     LCD_RS_BIT\r
392         call    TxLCD8B\r
393         return\r
394 \r
395 ;----------------------------------------------------------------\r
396 ;  TxLCD8B\r
397 ;  Send a string to the LCD.\r
398 \r
399 TxLCD8B\r
400 ;       movwf   Icount          ; Icount = W\r
401         movlw   8\r
402         movwf   e_LEN           ; Move to e_LEN\r
403 \r
404 Txm_lp  movf    Icount,w        ; get the byte\r
405         call    LookUp\r
406         incf    Icount,f        ; ...else ++Icount (table index)\r
407         call    TxLCDB          ; Send out the byte\r
408         decfsz  e_LEN,f\r
409         goto    Txm_lp\r
410         return\r
411 \r
412 ;----------------------------------------------------------------\r
413 ; TxLCDB - send a byte to the LCD\r
414 \r
415 TxLCDB\r
416         movwf   TxTemp          ; Store byte to send for a while...\r
417 \r
418         bcf     temp,0          ; Clear my temp bit\r
419         btfss   LCD_RS_BIT      ; Check if we try the correct reg\r
420         goto    RxNoProb\r
421         bcf     LCD_RS_BIT\r
422         bsf     temp,0          ; Indicate RS change\r
423 RxNoProb\r
424 \r
425 NotReady\r
426         call    RxLCDB          ; Receive byte from LCD, status reg\r
427         andlw   80h\r
428         btfss   STATUS,Z        ; If the bit was set, the zero flag is not\r
429         goto    NotReady\r
430 \r
431         btfsc   temp,0          ; If we had to clear RS reset it now\r
432         bsf     LCD_RS_BIT\r
433 \r
434         swapf   TxTemp,w        ; Hi nibble of data to send in lo w bits\r
435         call    TxLCD           ; Send them first...\r
436         movf    TxTemp,w        ; Then we have the low nibble in low w bits\r
437         call    TxLCD           ; And send that one as well\r
438 \r
439         return\r
440 ;----------------------------------------------------------------\r
441 ; RxLCDB - recv a byte from the LCD\r
442 \r
443 RxLCDB\r
444         call    RxLCD           ; Receive the high nibble\r
445         movwf   LCDWTmp\r
446         swapf   LCDWTmp,f       ; Swap it back to file\r
447         call    RxLCD           ; Receive the low nibble\r
448         addwf   LCDWTmp,w       ; Put the nibbles together and return in W\r
449 \r
450         return\r
451 \r
452 ;----------------------------------------------------------------\r
453 ; TxLCD - send a nibble to the LCD\r
454 \r
455 TxLCD\r
456         movwf   LCDWTmp         ; Write nibble to tmp\r
457         bcf     LCD_DB4_BIT     ; Clear previous data\r
458         bcf     LCD_DB5_BIT     ; \r
459         bcf     LCD_DB6_BIT     ;\r
460         bcf     LCD_DB7_BIT     ;\r
461 \r
462         btfsc   LCDWTmp,0       ; Test bit 0, transfer a set bit to LCD PORT\r
463         bsf     LCD_DB4_BIT\r
464         btfsc   LCDWTmp,1       ; Test bit 1, transfer a set bit to LCD PORT\r
465         bsf     LCD_DB5_BIT\r
466         btfsc   LCDWTmp,2       ; Test bit 2, transfer a set bit to LCD PORT\r
467         bsf     LCD_DB6_BIT\r
468         btfsc   LCDWTmp,3       ; Test bit 3, transfer a set bit to LCD PORT\r
469         bsf     LCD_DB7_BIT\r
470 \r
471         bsf     LCD_E_BIT       ; And set E to clock the data into the LCD module\r
472         nop                     ; Let it settle\r
473         bcf     LCD_E_BIT       ; And clear the Enable again.\r
474         return                  ; Returns without modifying W\r
475 \r
476 ;----------------------------------------------------------------\r
477 ; RxLCD - recv a nibble from the LCD\r
478 \r
479 RxLCD\r
480         clrw                    ; Clear W register, return data in lower 4 bits\r
481 \r
482         bsf     STATUS,RP0      ; Select 2nd reg bank, now TRIS regs can be accessed\r
483         \r
484         bsf     LCD_DB4_BIT     ; This sets the port bit as an input\r
485         bsf     LCD_DB5_BIT     \r
486         bsf     LCD_DB6_BIT     \r
487         bsf     LCD_DB7_BIT\r
488         bcf     STATUS,RP0      ; Back at reg bank 0    \r
489 \r
490         bsf     LCD_RW_BIT      ; Set Read mode for the LCD\r
491         bsf     LCD_E_BIT       ; And set E to clock the data out of the LCD module\r
492         nop                     ; Let the bus settle\r
493         btfsc   LCD_DB4_BIT     ; Transfer a set port bit into W\r
494         addlw   1\r
495         btfsc   LCD_DB5_BIT     ; Transfer a set port bit into W\r
496         addlw   2\r
497         btfsc   LCD_DB6_BIT     ; Transfer a set port bit into W\r
498         addlw   4\r
499         btfsc   LCD_DB7_BIT     ; Transfer a set port bit into W\r
500         addlw   8\r
501         bcf     LCD_E_BIT       ; And clear the Enable again.\r
502         bcf     LCD_RW_BIT      ; Set Write mode for the LCD\r
503 \r
504         bsf     STATUS,RP0      ; Select 2nd reg bank, now TRIS regs can be accessed\r
505         bcf     LCD_DB4_BIT     ; Set the port as an output again\r
506         bcf     LCD_DB5_BIT     ; \r
507         bcf     LCD_DB6_BIT     ;\r
508         bcf     LCD_DB7_BIT     ;\r
509         bcf     STATUS,RP0      ; Back at reg bank 0    \r
510 \r
511         return                  ; Returns with data in W\r
512 \r
513 ;----------------------------------------------------------------------\r
514 ; Delay routines       (one iteration=3 cycles. That is 0.366211ms @32kHz)\r
515 ; 2.73* # of ms is good...\r
516 \r
517 DelayW  movwf   Dcount          ; Set delay counter\r
518         clrf    Dcount2\r
519         decf    Dcount2,f\r
520 DelayLp decfsz  Dcount,f\r
521         goto    DelayIn\r
522         retlw   0\r
523 DelayIn decfsz  Dcount2,f\r
524         goto    DelayIn2\r
525         decf    Dcount2,f\r
526         goto    DelayLp\r
527 DelayIn2        goto    $+1\r
528         goto    $+1\r
529         goto    $+1\r
530         goto    DelayIn\r
531 \r
532         subtitl "Bootstrap/Bootloader code"\r
533         page\r
534 \r
535 ;----------------------------------------------------------------------\r
536 ; Bootstrap code - Allows PIC to flash itself with data from async port\r
537 ; Startup @9600bps\r
538 \r
539         org     700h                    ; Place the boot code at the top of memory\r
540 \r
541 BootRXState     equ     7fh             ; What are we waiting for @RX\r
542 BootBits        equ     7eh             ; bit0 1=write 0=read, bit1 1=PGM 0=EE, bit2 0=normal 1=no-op when prog\r
543 BootAddrL       equ     7dh\r
544 BootAddrH       equ     7ch\r
545 BootDataL       equ     7bh\r
546 BootDataH       equ     7ah\r
547 BootTimerL      equ     79h\r
548 BootTimerM      equ     78h\r
549 BootTimerH      equ     77h\r
550 BootNumBytes    equ     76h\r
551 BootDataVL      equ     75h\r
552 BootDataVH      equ     74h\r
553 BootHEXTemp     equ     73h\r
554 BootStrTemp     equ     72h\r
555 \r
556 Bootstrap\r
557         movlw   7\r
558         movwf   PCLATH\r
559 \r
560         bsf     STATUS,RP0              ; Access bank 1\r
561         bsf     TXSTA,TXEN              ; Enable UART TX\r
562         movlw   31                      ; Divisor for 9k6 @ 20MHz Fosc\r
563         movwf   SPBRG                   ; Store\r
564         bcf     STATUS,RP0              ; Back to bank 0\r
565 \r
566         bsf     RCSTA,SPEN              ; Enable serial port\r
567         bsf     RCSTA,CREN              ; Enable UART RX\r
568 \r
569 ;       clrf    BootRXState             ; Waiting for command\r
570 \r
571         movlw   BootStartText\r
572         call    BootTXStr\r
573 \r
574         movlw   0e8h\r
575         movwf   BootTimerL\r
576         movwf   BootTimerM\r
577         movwf   BootTimerH\r
578 \r
579 BootTimeout\r
580         incf    BootTimerL,f            ; A 24-bit counter\r
581         skpnz\r
582         incf    BootTimerM,f\r
583         skpnz\r
584         incf    BootTimerH,f\r
585         skpnz                           ; When overflowing here..\r
586         goto    BootReturn              ; ..Exit boot loader, no keypress within timeout period, resume program\r
587         btfss   PIR1,RCIF               ; Wait for RX to complete\r
588         goto    BootTimeout\r
589         call    BootRXB\r
590         xorlw   27                      ; ESC\r
591         btfss   STATUS,Z\r
592         goto    BootTimeout             ; If it wasn't space, wait for another key\r
593 \r
594 BootFlash\r
595         movlw   BootFlashText\r
596         call    BootTXStr\r
597 \r
598         bsf     BootBits,1\r
599         clrf    BootAddrL\r
600         clrf    BootAddrH\r
601 \r
602 BootLoop\r
603         call    BootRXB                 ; First find the ':'\r
604         xorlw   ':'\r
605         skpz\r
606         goto    BootLoop                ; Loop until we find it!\r
607 \r
608         call    BootRXHEX               ; Get one ASCII encoded byte (two chars)\r
609         movwf   BootNumBytes            ; This is the number of bytes to be programmed on the line\r
610 ; Maybe clear cary here?\r
611         rrf     BootNumBytes,f          ; Right shift because we're double addressing this 8-bit format\r
612 \r
613 ; Note carry should be clear here as there cannot be odd number of bytes in this format\r
614 \r
615         call    BootRXHEX               ; Receive AddrH\r
616         movwf   BootAddrH\r
617         call    BootRXHEX               ; Receive AddrL\r
618         movwf   BootAddrL\r
619         rrf     BootAddrH,f             ; Fix the addressing again\r
620         rrf     BootAddrL,f\r
621 \r
622         bcf     BootBits,2              ; Assume we should program\r
623         bsf     BootBits,1              ; And assume we should program flash not ee\r
624 \r
625         movf    BootAddrH,w\r
626         xorlw   020h                    ; Check if it's configuration, which we can't program\r
627         skpnz                           ; Skip the bit set if it was false alarm\r
628         bsf     BootBits,2              ; No programming for this line\r
629 \r
630         xorlw   001h                    ; Also check if it's EEPROM memory (first xor 20h then 1 =21h)\r
631         skpnz                           ; Skip the bit set instr if not EE data address\r
632         bcf     BootBits,1              ; We should program EE, will ignore the AddrH\r
633 \r
634         call    BootRXHEX               ; Receive Record Type (must be 0 for real records)\r
635         skpz                            ; Check if zero\r
636         goto    BootFlashComplete\r
637 \r
638 BootLineLoop\r
639         call    BootRXHEX               ; Receive low-byte of data word\r
640         movwf   BootDataVL\r
641         call    BootRXHEX               ; Receive high-byte of data word\r
642         movwf   BootDataVH\r
643         \r
644         btfsc   BootBits,2              ; Check whether this line should be programmed at all\r
645         goto    BootWriteSkip\r
646 \r
647         bcf     BootBits,0              ; Read mode first, verify if we actually have to write\r
648         call    BootEE\r
649         movf    BootDataVL,w\r
650         xorwf   BootDataL,f             ; Compare and destroy DataL\r
651         movwf   BootDataL               ; Write new data to DataL\r
652         skpz                            ; Skip if no difference, have to check high byte as well\r
653         goto    BootWrite               ; Jump directly to write\r
654 \r
655         movf    BootDataVH,w\r
656         xorwf   BootDataH,f             ; Compare\r
657         skpnz                           ; Skip if no difference, no programming necessary\r
658         goto    BootWriteSkip\r
659 \r
660 BootWrite\r
661         movf    BootDataVH,w\r
662         movwf   BootDataH               ; Have to put the new H byte data in as well\r
663 \r
664 ;       movlw   '+'\r
665 ;       call    BootTXB                 ; Send progword indicator\r
666 \r
667         bsf     BootBits,0\r
668         call    BootEE                  ; Write directly into program mem\r
669 \r
670 ; Here a verify can take place, the read-back results are now in DataL/H\r
671 \r
672         movlw   '+'\r
673         goto    BootWriteDone\r
674 \r
675 BootWriteSkip\r
676         movlw   '-'\r
677 BootWriteDone\r
678         call    BootTXB\r
679 \r
680         incf    BootAddrL,f             ; Advance counter to next addr\r
681         skpnz\r
682         incf    BootAddrH,f             ; And add to high byte if needed\r
683 \r
684         decfsz  BootNumBytes,f\r
685         goto    BootLineLoop\r
686 \r
687         movlw   13                      ; Progress\r
688         call    BootTXB\r
689         movlw   10                      ; Progress\r
690         call    BootTXB\r
691 \r
692         goto    BootLoop\r
693 \r
694 BootFlashComplete\r
695         \r
696 BootReturn\r
697         movlw   BootRunText\r
698         call    BootTXStr\r
699 \r
700         bsf     STATUS,RP0\r
701 BootReturnWait\r
702         btfss   TXSTA,TRMT              ; Wait for last things to flush\r
703         goto    BootReturnWait\r
704         bcf     TXSTA,TXEN              ; Disable UART TX\r
705         bcf     STATUS,RP0              ; Back to bank 0\r
706 \r
707         bcf     RCSTA,SPEN              ; Enable serial port\r
708         bcf     RCSTA,CREN              ; Enable UART RX\r
709 \r
710         clrf    PCLATH\r
711         return                          ; Return to code        \r
712 \r
713 ;----------------------------------------------------------------------\r
714 ; BootTXB - Sends one byte to the UART, waits for transmitter to become\r
715 ;  free before sending\r
716 \r
717 BootTXB\r
718 BootTXW1\r
719         btfss   PIR1,TXIF               ; Wait for TX to empty\r
720         goto    BootTXW1\r
721         movwf   TXREG                   ; Send the byte\r
722         return\r
723 \r
724 ;----------------------------------------------------------------------\r
725 ; BootTXStr - Sends ASCII string pointed to by W, zero terminated\r
726 \r
727 BootTXStr\r
728         movwf   BootStrTemp             ; Store offset\r
729         call    BootLookup              ; Lookup char\r
730         addlw   0\r
731         skpnz\r
732         return\r
733         call    BootTXB                 ; Send char\r
734         incf    BootStrTemp,w           ; Retrieve\r
735         goto    BootTXStr\r
736 \r
737 ;----------------------------------------------------------------------\r
738 ; BootRXB - Receives one byte from the UART, waits if nothing available\r
739 \r
740 BootRXB\r
741 BootRXW1\r
742         btfss   PIR1,RCIF               ; Wait for RX to complete\r
743         goto    BootRXW1\r
744         movf    RCREG,w                 ; Get the recvd byte\r
745         return\r
746 \r
747 ;----------------------------------------------------------------------\r
748 ; BootRXHEXNibble - Receives one byte and converts it from ASCII HEX to binary\r
749 \r
750 BootRXHEXNibble\r
751         call    BootRXB                 ; Receive nibble\r
752 \r
753         addlw   -'A'                    ; Convert from BCD to binary nibble\r
754         skpc                            ; Test if if was 0-9 or A-F, skip if A-F\r
755         addlw  'A' - 10 - '0'           ; It was numeric '0'\r
756         addlw   10                      ; Add 10 (A get to be 0ah etc.)\r
757         return\r
758 \r
759 ;----------------------------------------------------------------------\r
760 ; BootRXHEX - Receives two bytes from the UART, waits if nothing available\r
761 ;  Decodes the bytes as ASCII hex and returns a single byte in W\r
762 \r
763 BootRXHEX\r
764         call    BootRXHEXNibble\r
765         movwf   BootHEXTemp\r
766         swapf   BootHEXTemp,f           ; Swap it up to the high nibble\r
767 \r
768         call    BootRXHEXNibble\r
769         addwf   BootHEXTemp,w           ; And add the two nibbles together\r
770         return\r
771 \r
772 ;----------------------------------------------------------------------\r
773 ; BootEE - Reads or writes EE or Flash memory, BootBits specify the\r
774 ;  exact action to take. BootAddrL and BootAddrH has to be initialized\r
775 ;  to the address of choice (0000-003fh for EE and 0000h-07ffh for flash\r
776 ;  The data to be written has to be put in BootDataL and BootDataH, and\r
777 ;  data will be to the same place when read back\r
778 \r
779 BootEE\r
780         bsf     STATUS,RP1              ; Select bank 2 (RP0 must be 0)\r
781 \r
782         movf    BootAddrH,w             ; Load desired address\r
783         movwf   EEADRH\r
784         movf    BootAddrL,w\r
785         movwf   EEADR\r
786         movf    BootDataH,w             ; And load the data (only used when writing)\r
787         movwf   EEDATH\r
788         movf    BootDataL,w\r
789         movwf   EEDATA\r
790 \r
791         bsf     STATUS,RP0              ; Go to bank 3\r
792 \r
793         bsf     EECON1,EEPGD            ; Point to Program Flash mem\r
794         btfss   BootBits,1              ; Test if that was correct or if we have to clear again\r
795         bcf     EECON1,EEPGD            ; Point to EE DATA mem\r
796 \r
797         btfss   BootBits,0              ; Check from read or write\r
798         goto    BootEERD                ; Skip the WR if we were going for a read\r
799 \r
800         bsf     EECON1,WREN             ; Enable writes\r
801         movlw   55h\r
802         movwf   EECON2\r
803         movlw   0AAh\r
804         movwf   EECON2                  ; Unlock write operation\r
805         bsf     EECON1,WR               ; And start a write cycle\r
806 BootWRLoop\r
807         btfsc   EECON1,WR               ; This executes for EE only not flash, waits for WR to finish\r
808         goto    BootWRLoop              ; These two instructions gets NOPed when flashing\r
809 \r
810         bcf     EECON1,WREN             ; Finally disable writes again\r
811                                         ; Here we read the data back again, can be used as verify\r
812 BootEERD\r
813         bsf     EECON1,RD               ; Start a read cycle\r
814         nop                             ; Only necessary for flash read, same thing as when writing above\r
815         nop                             ; Except I could use the two words for something useful there.. :)\r
816 \r
817 BootEEX\r
818         bcf     STATUS,RP0              ; Back to bank 2\r
819         movf    EEDATA,w                ; Store our EE-data\r
820         movwf   BootDataL\r
821         movf    EEDATH,w\r
822         movwf   BootDataH\r
823         bcf     STATUS,RP1              ; And finally back to bank 0\r
824 \r
825         return\r
826 \r
827 BootStartText\r
828         DT      "WJBoot - press ESC to flash",0\r
829 BootFlashText\r
830         DT      13,10,"Send INHX8 file now...",13,10,0\r
831 BootRunText\r
832         DT      13,10,"Exiting loader",13,10,0\r
833 \r
834 BootLookup\r
835         movwf   PCL                     ; Go fetch the char data\r
836 \r
837 ;----------------------------------------------------------------------\r
838 ; EE Data (64 bytes), located at 2100h\r
839 \r
840         org 2100h\r
841 ;        data 0f2h, 099h, 000h, 000h, 018h, 0a5h, 090h, 084h\r
842 \r
843         END\r