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