130df184effb4335f816a1a185b2bf0899af7937
[wj-unilink.git] / wj-uni.asm
1         title   "PIC16F870 Unilink(R) Interface by Werner Johansson (c) 2003"\r
2         subtitl "Definitions"\r
3         list    c=150,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 ;  TODO\r
14 ;----------------------------------------------------------------\r
15 ;  BUSON OUT control isn't implemented\r
16 ;  No checksum checking is done on incoming packets\r
17 ;  Investigate whether we actually have to save PCLATH in ISH, maybe save FSR?\r
18 ;  Move RS232 code into ISH\r
19 ;  Check Overrun errors from the UART\r
20 ;  Implement Bus re-initialize command\r
21 ;  Implement lots of other Unilink commands\r
22 \r
23 ;----------------------------------------------------------------\r
24 ;  HISTORY\r
25 ;----------------------------------------------------------------\r
26 ;  Version\r
27 ;\r
28 ;  0.2  First attempt at responding to the Anyone command\r
29 ;  0.1  Receives Unilink data OK, relays it to serial\r
30 ;  0.0  Very first "Fucking No Work!" version\r
31 \r
32 ;----------------------------------------------------------------\r
33 ;  I/O LAYOUT\r
34 ;----------------------------------------------------------------\r
35 ;  Unilink BUSON IN (blue) connected to RC2/CCP1\r
36 ;  Unilink DATA (green) connected to RC3\r
37 ;  Unilink BUSON OUT (blue) connected to RC4 (this is for daisy-chaining)\r
38 ;  Unilink CLK (yellow) connected to RB0/INT (Interrupt pin)\r
39 ;  Unilink RST (lilac) connected to RA4\r
40 ;  LCD RS connected to pin RB1\r
41 ;  LCD RW connected to pin RB2\r
42 ;  LCD E connected to pin RB3\r
43 ;  LCD DB4-DB7 connected to RB4-RB7\r
44 ;  RS-232 TX from computer connected to RC7/RX\r
45 ;  RS-232 RX to computer connected to RC6/TX\r
46 ;  RS-232 RI to computer connected to RC5\r
47 ;\r
48 ;  This leaves RC0, RC1 and the analog inputs (AN0-AN4) free for now...\r
49 \r
50 #define BUSON_IN_BIT    PORTC,2\r
51 #define DATA_BIT        PORTC,3\r
52 #define BUSON_OUT_BIT   PORTC,4\r
53 #define CLK_BIT         PORTB,0\r
54 #define RST_BIT         PORTA,4\r
55 \r
56 #define LCD_RS_BIT      PORTB,1\r
57 #define LCD_RW_BIT      PORTB,2\r
58 #define LCD_E_BIT       PORTB,3\r
59 #define LCD_DB4_BIT     PORTB,4\r
60 #define LCD_DB5_BIT     PORTB,5\r
61 #define LCD_DB6_BIT     PORTB,6\r
62 #define LCD_DB7_BIT     PORTB,7\r
63 \r
64 #define RS232_RI_BIT    PORTC,5\r
65 \r
66 ;----------------------------------------------------------------\r
67 ;  FILE REGISTER USAGE\r
68 ;----------------------------------------------------------------\r
69 Dcount          equ     20h\r
70 e_LEN           equ     21h\r
71 Icount          equ     2Dh             ; Offset of string to print\r
72 TxTemp          equ     2Eh             ; blahblah\r
73 TxTemp2         equ     2Fh             ; Blahblah2\r
74 \r
75 LCDWTmp         equ     30h\r
76 Dcount2         equ     31h\r
77 temp            equ     32h\r
78 \r
79 DataCount       equ     33h             ; Temp storage for the bit counter used during bit shifts (Unilink TX/RX)\r
80 DataStore       equ     34h             ; This is a kludge\r
81 \r
82 UnilinkSelected equ     3bh\r
83 UnilinkBit      equ     3ch             ; This is my "bitmask" to be used for requests\r
84 UnilinkID       equ     3dh             ; This is my Bus ID\r
85 UnilinkCmdLen   equ     3eh             ; This gets updated with the actual packet length after CMD1 has been received\r
86 UnilinkTXRX     equ     3fh             ; This is a pointer to the Unilink packet below, used with indirect addressing\r
87 \r
88 UnilinkRAD      equ     40h             ; Beginning of Unilink packet - the Receiving Address\r
89 UnilinkTAD      equ     41h             ; Transmitter address\r
90 UnilinkCMD1     equ     42h             ; CMD1 byte\r
91 UnilinkCMD2     equ     43h             ; CMD2 byte\r
92 UnilinkParity1  equ     44h             ; First or only parity byte for short packets (6 bytes)\r
93 UnilinkData1    equ     45h             ; Extra data for medium/large packets, or zero for short packets\r
94 UnilinkData2    equ     46h             ;\r
95 UnilinkData3    equ     47h             ;\r
96 UnilinkData4    equ     48h             ;\r
97 UnilinkData5    equ     49h             ; Data5 if this is a large packet\r
98 UnilinkParity2M equ     49h             ; Parity2 shares the same byte if it's a medium sized packet\r
99 UnilinkData6    equ     4ah             ; Extra data for large packets, or zero for medium packets\r
100 UnilinkData7    equ     4bh             ;\r
101 UnilinkData8    equ     4ch             ;\r
102 UnilinkData9    equ     4dh             ;\r
103 UnilinkParity2  equ     4eh             ; Parity byte for large packets\r
104 UnilinkZero     equ     4fh             ; Should always be zero (possibly used to signal corrupt packets from slave to master?)\r
105 \r
106 IRQPCLATH       equ     7dh             ; ISH storage\r
107 IRQSTATUS       equ     7eh             ; Needs to be located in a shared area accessible from all register banks\r
108 IRQW            equ     7fh             ; \r
109 \r
110         subtitl "Startup"\r
111         page\r
112 ;----------------------------------------------------------------\r
113 ;  Power up/Reset starting point [den rulerar]\r
114 \r
115         org     0                       ; Start at the beginning of memory (the reset vector)\r
116         call    Bootstrap               ; Call Flash Load routine\r
117         call    LCDInit                 ; Initialize LCD I/F\r
118         call    IRQInit                 ; Set up and start the IRQ handler\r
119         goto    Main                    ; Run the main program loop (skip the IRQ handler)\r
120 \r
121         subtitl "IRQ Handler"\r
122 ;----------------------------------------------------------------\r
123 ;  Interrupt handler always starts at addr 4\r
124 ;  In order to save one instruction cycle we put the actual code here directly instead of a goto instruction\r
125 \r
126         org     4                       ; ISR vector is at address 4\r
127         movwf   IRQW                    ; Save W\r
128         swapf   STATUS,w                ; Get the status register into w\r
129         clrf    STATUS                  ; Zero out the status reg, gives us Bank0 all the time\r
130         movwf   IRQSTATUS               ; Store the STATUS reg\r
131         movf    PCLATH,w                ; Get the PCLATH reg\r
132         movwf   IRQPCLATH               ; And store it\r
133         clrf    PCLATH                  ; Go to low memory\r
134 ; Maybe save FSR here as well (if there's a need for it in the non-ISR code)\r
135 \r
136         btfss   INTCON,INTF             ; Check if it's the INT edge interrupt (Unilink CLK)\r
137         goto    IRQNotINT               ; No it's not, check the other sources\r
138 \r
139 ; If there's activity on the clock line (the clock goes high) we stay in here until we have clocked eight bits\r
140 ; - this saves us a lot of context switching (and it's just a few hundred cpu cycles after all (20us*8 bits=\r
141 ; 160us=800 instruction cycles (5 MIPS @ 20MHz), not even a problem for serial input if we're not getting more than\r
142 ; 6250 bytes per second from the UART, and the 2-byte FIFO somehow fills up (this should be impossible even @ 115200\r
143 ; 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
144 ; this gives the CPU ample of time to deal with all bytes from the USART. I'm checking the OERR (Serial Overrun) bit\r
145 ; to catch this though.. Note that this piece of code does both TX and RX at the same time (in order to receive packets\r
146 ; one has to make sure that the packet buffer is zeroed out before entering here, otherwise collisions will occur..\r
147 ; According to my logic analyzer this implementation is pretty decent when it comes to timing, even though it's a\r
148 ; interrupt driven software based USART - by trigging the interrupt on the rising edge we buy us some extra time here\r
149 ; (the clock goes high 10us before the master clocks the bit in (on the falling edge), that should be plenty of time..)\r
150 \r
151         movlw   8                       ; Loop through the 8 bits\r
152         movwf   DataCount\r
153         movf    UnilinkTXRX,w           ; Get the pointer\r
154         movwf   FSR                     ; Store it to make use of indirect addressing\r
155 \r
156 IRQINTBitSet\r
157         btfss   INDF,7                  ; Test high bit of data (that's the first bit to be clocked out)\r
158         goto    IRQINTTristate          ; Bit is low, we should tristate bit\r
159         bcf     PORTC,3                 ; Otherwise set DATA bit low\r
160         bsf     STATUS,RP0              ; Select high regs\r
161         bcf     TRISC,3                 ; And pull low (now it's an output)\r
162         bcf     STATUS,RP0              ; Back to regbank 0\r
163         goto    IRQINTCLKWaitLow        ; Wait for master to actually clock this bit in\r
164 \r
165 IRQINTTristate\r
166         bsf     STATUS,RP0              ; Select high regs\r
167         bsf     TRISC,3                 ; Force the bit to be tristated\r
168         bcf     STATUS,RP0              ; Back to regbank 0\r
169 \r
170 IRQINTCLKWaitLow\r
171         btfss   PORTC,2                 ; Check for BUSON\r
172         goto    IRQAfterINT\r
173         btfsc   PORTB,0                 ; Wait for clock to go low\r
174         goto    IRQINTCLKWaitLow\r
175 \r
176         clrc                            ; Clear carry (this way the DataStore byte doesn't have to be cleared before)\r
177         btfss   PORTC,3                 ; Test DATA\r
178         setc                            ; Set carry if data is LOW (data is inverted!)\r
179         rlf     INDF,f                  ; Shift it into our accumulator\r
180 \r
181         decfsz  DataCount,f             ; Loop once more perhaps?\r
182         goto    IRQINTCLKWaitHigh       ; Yes, again!\r
183         goto    IRQINTRecvDone          ; No we're done, don't check for clock to go high again\r
184 \r
185 IRQINTCLKWaitHigh\r
186         btfss   PORTC,2                 ; Check for BUSON\r
187         goto    IRQAfterINT\r
188         btfss   PORTB,0                 ; Wait for clock to go high\r
189         goto    IRQINTCLKWaitHigh\r
190         goto    IRQINTBitSet            ; Loop again\r
191 \r
192 ; Successfully received a byte here, run it through a state machine to figure out what to do\r
193 ; (several possibilites exists here:\r
194 ;;;;;; If more than 1.1ms has passed since last receive, reset receive counter to zero\r
195 ; If receive counter is zero and the received byte is a zero byte, discard it\r
196 ; Otherwise store the byte in our receive buffer and increment receive counter\r
197 ; 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
198 ;   00 = short 6 byte packet\r
199 ;   10 = medium 11 byte packet\r
200 ;   11 = long 16 byte packet\r
201 ; Update the receive length byte accordingly\r
202 ; Check whether receive length and receive count are equal, that means that we're finished and we can carry on parsing\r
203 ; the packet and take appropriate action.\r
204 \r
205 IRQINTRecvDone\r
206         movf    UnilinkTXRX,w           ; Find out which byte we got\r
207         andlw   0fh                     ; Mask\r
208         bnz     IRQINTRecvNotFirst      ; Not the first byte\r
209         movf    UnilinkRAD,w            ; Get the first byte received\r
210         bz      IRQINTRecvNullByte      ; Null byte received, ignore this, don't increment counter\r
211 IRQINTRecvNotFirst\r
212         incf    UnilinkTXRX,f           ; Increment address\r
213 \r
214         movf    UnilinkTXRX,w           ; Get the byte position again\r
215         andlw   0fh                     ; Only lower 4 bits of interest\r
216         xorlw   03h                     ; Well, is it the third byte? (CMD1, telling us the length of the packet)\r
217         bnz     IRQINTRecvNotCMD1       ; No, skip the length code for now\r
218         movlw   6                       ; Assume it's a short packet\r
219         btfss   INDF,7                  ; INDF still points to received byte, test high bit for medium/long\r
220         goto    IRQINTRecvShort         ; Nope, it's a short packet\r
221         addlw   5                       ; OK, it's long or medium at least\r
222         btfsc   INDF,6                  ; Test for long\r
223         addlw   5                       ; Yep, it's a long packet\r
224 IRQINTRecvShort\r
225         movwf   UnilinkCmdLen           ; Store the length\r
226 \r
227 IRQINTRecvNotCMD1\r
228         movf    UnilinkTXRX,w           ; Get the byte position\r
229         xorwf   UnilinkCmdLen,w         ; XOR with the calculated command length\r
230         andlw   0fh                     ; and mask - this results in a zero result when finished receiving\r
231         bnz     IRQINTRecvIncomplete    ; Packet not ready yet\r
232 \r
233 ; Here we actually have received a packet, should check the checksum(s) as well, but I don't care right now\r
234 ; (I need music in my car! :))\r
235 ; This is inefficient, I know, I'll improve it later... (Not that it matters, we have plenty of time here\r
236 ; (there can't be any more communication for another 4.8ms))\r
237 \r
238 ; Unilink command parser:\r
239 \r
240 ; Check for CMD1 = 01h (System bus commands)\r
241         movf    UnilinkCMD1,w\r
242         xorlw   01h\r
243         bnz     IRQINTParseNot01\r
244 \r
245 ; Check for 01 02 (Anyone)\r
246         movf    UnilinkCMD2,w\r
247         xorlw   02h\r
248         bnz     IRQINTParseNot0102\r
249 \r
250         movf    UnilinkID,w             ; Do I have an ID already?\r
251         bnz     IRQINTParseNot0102      ; Yep, I don't want another one!\r
252 \r
253         movlw   10h                     ; Sending to Master\r
254         movwf   UnilinkRAD\r
255         movlw   0d0h                    ; I'm in the MD changer group\r
256         movwf   UnilinkTAD\r
257         movlw   8ch                     ; Device discovery command reply\r
258         movwf   UnilinkCMD1\r
259         movlw   00h                     ; 00??\r
260         movwf   UnilinkCMD2\r
261         movlw   6ch                     ; Hard coded parity (!)\r
262         movwf   UnilinkParity1\r
263         movlw   24h                     ; My internal MD sends 25 here first time, and then 24 when appointed!??\r
264         movwf   UnilinkData1\r
265         movlw   2ch                     ; 2c??\r
266         movwf   UnilinkData2\r
267         movlw   22h                     ; 22??\r
268         movwf   UnilinkData3\r
269         movlw   00h                     ; 00??\r
270         movwf   UnilinkData4\r
271         movlw   0deh                    ; Hard coded parity 2 (!)\r
272         movwf   UnilinkData5\r
273         clrf    UnilinkData6\r
274         goto    IRQINTParseBypassClear  ; We don't want to clear the data, we want to send what's in the buffer next time\r
275 \r
276 IRQINTParseNot0102\r
277 \r
278 ; Check for 01 12 (Time poll)\r
279         movf    UnilinkCMD2,w\r
280         xorlw   12h\r
281         bnz     IRQINTParseNot0112\r
282 \r
283         movf    UnilinkRAD,w\r
284         xorwf   UnilinkID,w             ; Is it for us?\r
285         bnz     IRQINTParseNot0112      ; nope\r
286 \r
287         clrf    UnilinkParity1\r
288         movlw   10h                     ; Sending to Master\r
289         addwf   UnilinkParity1,f\r
290         movwf   UnilinkRAD\r
291         movf    UnilinkID,w             ; This is my ID\r
292         addwf   UnilinkParity1,f\r
293         movwf   UnilinkTAD\r
294         movlw   00h\r
295         addwf   UnilinkParity1,f\r
296         movwf   UnilinkCMD1\r
297 \r
298         movlw   80h                     ; We're idle unless selected\r
299         btfsc   UnilinkSelected,7       \r
300         clrw\r
301         \r
302         addwf   UnilinkParity1,f\r
303         movwf   UnilinkCMD2\r
304         clrf    UnilinkData6\r
305         goto    IRQINTParseBypassClear  ; We don't want to clear the data, we want to send!\r
306 \r
307 IRQINTParseNot0112\r
308 \r
309 IRQINTParseNot01\r
310 \r
311 ; Check for CMD1 = 02h (Appoint)\r
312         movf    UnilinkCMD1,w\r
313         xorlw   02h\r
314         bnz     IRQINTParseNot02\r
315 \r
316         movf    UnilinkRAD,w            ; Get the ID the master has given us\r
317         movwf   UnilinkID               ; Store my id\r
318         movf    UnilinkCMD2,w           ; Get the bitmask\r
319         movwf   UnilinkBit              ; And store it (this is needed when doing slave breaks and actually responding)\r
320 \r
321         clrf    UnilinkParity1\r
322         movlw   10h                     ; Sending to Master\r
323         addwf   UnilinkParity1,f\r
324         movwf   UnilinkRAD\r
325         movf    UnilinkID,w             ; This is my ID\r
326         addwf   UnilinkParity1,f\r
327         movwf   UnilinkTAD\r
328         movlw   8ch                     ; Device discovery command again\r
329         addwf   UnilinkParity1,f\r
330         movwf   UnilinkCMD1\r
331         movlw   00h\r
332         addwf   UnilinkParity1,f\r
333         movwf   UnilinkCMD2\r
334 \r
335         movf    UnilinkParity1,w\r
336         movwf   UnilinkParity2M         ; That's the parity when sending medium messages\r
337 \r
338         movlw   24h\r
339         addwf   UnilinkParity2M,f\r
340         movwf   UnilinkData1\r
341         movlw   2ch                     ; My internal MD sends 1c here... (external/internal or 1/10 disc difference?)\r
342         addwf   UnilinkParity2M,f\r
343         movwf   UnilinkData2\r
344         movlw   22h\r
345         addwf   UnilinkParity2M,f\r
346         movwf   UnilinkData3\r
347         movlw   00h\r
348         addwf   UnilinkParity2M,f\r
349         movwf   UnilinkData4\r
350 \r
351         clrf    UnilinkData6\r
352         goto    IRQINTParseBypassClear  ; We don't want to clear the data, we want to send!\r
353 \r
354 IRQINTParseNot02\r
355 \r
356 ; Check for CMD1 = f0h (Source Select)\r
357         movf    UnilinkCMD1,w\r
358         xorlw   0f0h\r
359         bnz     IRQINTParseNotF0\r
360 \r
361         movf    UnilinkCMD2,w\r
362         xorwf   UnilinkID,w             ; Check if it's selecting us\r
363         bnz     IRQINTParseF0Deselect\r
364 \r
365         bsf     UnilinkSelected,7       ; Now we're selected\r
366         goto    IRQINTParseNotF0\r
367 \r
368 IRQINTParseF0Deselect\r
369 \r
370         bcf     UnilinkSelected,7       ; Now we're de-selected\r
371         goto    IRQINTParseNotF0\r
372 \r
373 IRQINTParseNotF0\r
374 \r
375 ; We end up here when parsing is complete and we're not interested in sending any reply back to the master\r
376 ; (that's why we clear out all the packet buffer bytes)\r
377 ; TODO: Replace this with an FSR access to save space and make the code neater\r
378 \r
379         clrf    UnilinkRAD\r
380         clrf    UnilinkTAD\r
381         clrf    UnilinkCMD1\r
382         clrf    UnilinkCMD2\r
383         clrf    UnilinkParity1\r
384         clrf    UnilinkData1\r
385         clrf    UnilinkData2\r
386         clrf    UnilinkData3\r
387         clrf    UnilinkData4\r
388         clrf    UnilinkData5\r
389         clrf    UnilinkData6\r
390         clrf    UnilinkData7\r
391         clrf    UnilinkData8\r
392         clrf    UnilinkData9\r
393         clrf    UnilinkParity2\r
394         clrf    UnilinkZero\r
395 \r
396 IRQINTParseBypassClear\r
397 \r
398         movlw   UnilinkRAD              ; Get the pointer to the first byte in the receive buffer\r
399         movwf   UnilinkTXRX             ; Store it - this way the next byte that gets received goes into RAD\r
400 \r
401         clrf    UnilinkCmdLen           ; No command length as we're waiting for a new packet\r
402 \r
403         \r
404 IRQINTRecvIncomplete\r
405 \r
406 IRQINTRecvNullByte\r
407         movf    INDF,w\r
408         movwf   DataStore               ; Store it so our non-irq code can snoop\r
409 \r
410 IRQAfterINT\r
411         bcf     INTCON,INTF             ; Clear our IRQ source bit so we can receive new bits again\r
412 \r
413 IRQNotINT\r
414 \r
415 ; Finally restore CPU state and return from the ISR\r
416         movf    IRQPCLATH,w\r
417         movwf   PCLATH          ; Restore PCLATH\r
418         swapf   IRQSTATUS,w\r
419         movwf   STATUS          ; Restore STATUS\r
420         swapf   IRQW,f\r
421         swapf   IRQW,w          ; Restore W\r
422         retfie                  ; Interrupt return\r
423 \r
424 \r
425         subtitl "Main loop"\r
426         page\r
427 \r
428 ;----------------------------------------------------------------\r
429 ;  Data can be stored between here and 100h...\r
430 \r
431 StartUpText1\r
432         DT      "----- WJ UniLink"\r
433                \r
434 LookUp  movwf   PCL             ; Go to it\r
435 \r
436 ;----------------------------------------------------------------\r
437 ;  Main program begins here. [Called after bootloader, lcdinit and irqinit...]\r
438 \r
439         org     100h\r
440 Main\r
441 \r
442         bsf     RS232_RI_BIT    ; We want RI to be high (inverted logic, not set)\r
443         bcf     BUSON_OUT_BIT   ; But we don't want BUSON_OUT on just yet, we need to be appointed first\r
444 \r
445         bsf     STATUS,RP0      ; Select bank 1\r
446 \r
447         bcf     RS232_RI_BIT    ; Both bits should be outputs at least\r
448         bcf     BUSON_OUT_BIT   ;\r
449 \r
450 ;       bcf     STATUS,RP0\r
451 ;       bsf     STATUS,RP0\r
452 \r
453         bsf     TXSTA,TXEN              ; Enable UART TX\r
454         bcf     STATUS,RP0              ; Back to bank 0\r
455 \r
456         bsf     RCSTA,SPEN              ; Enable serial port\r
457         bsf     RCSTA,CREN              ; Enable UART RX\r
458 \r
459 ; Replace this with an FSR access\r
460         clrf    UnilinkSelected\r
461         clrf    UnilinkID\r
462         clrf    UnilinkBit\r
463         clrf    UnilinkCmdLen\r
464         clrf    UnilinkRAD\r
465         clrf    UnilinkTAD\r
466         clrf    UnilinkCMD1\r
467         clrf    UnilinkCMD2\r
468         clrf    UnilinkParity1\r
469         clrf    UnilinkData1\r
470         clrf    UnilinkData2\r
471         clrf    UnilinkData3\r
472         clrf    UnilinkData4\r
473         clrf    UnilinkData5\r
474         clrf    UnilinkData6\r
475         clrf    UnilinkData7\r
476         clrf    UnilinkData8\r
477         clrf    UnilinkData9\r
478         clrf    UnilinkParity2\r
479         clrf    UnilinkZero\r
480 \r
481         clrf    DataStore\r
482         movlw   UnilinkRAD      ; Get the pointer to the first byte in the receive buffer\r
483         movwf   UnilinkTXRX     ; Store it\r
484 \r
485         movlw   StartUpText1\r
486         call    TxLCD16B\r
487 retry\r
488         \r
489         bcf     LCD_RS_BIT      ;Command mode\r
490         movlw   80h             ;DisplayRam 0\r
491         call    TxLCDB\r
492         bsf     LCD_RS_BIT\r
493 \r
494         movlw   '0'\r
495         btfsc   PORTA,4         ; Test RST\r
496         movlw   'R'\r
497         call    TxLCDB\r
498 \r
499         movlw   '0'\r
500         btfsc   PORTB,0         ; Test CLK\r
501         movlw   'C'\r
502         call    TxLCDB\r
503 \r
504         movlw   '0'\r
505         btfsc   PORTC,2         ; Test BUSON-IN\r
506         movlw   'B'\r
507         call    TxLCDB\r
508 \r
509         movlw   '0'\r
510         btfsc   PORTC,3         ; Test DATA\r
511         movlw   'D'\r
512         call    TxLCDB\r
513 \r
514         movf    UnilinkCmdLen,w\r
515         bz      DontPrintCmd\r
516         addlw   '0'\r
517         call    TxLCDB\r
518 DontPrintCmd\r
519 \r
520         movf    DataCount,w             ; Load bit counter (if 0 then byte is available)\r
521         skpz\r
522         goto    retry\r
523 \r
524         decf    DataCount,f             ; Set it non-zero\r
525 \r
526         movf    DataStore,w\r
527         call    BootTXB                 ; Send to terminal\r
528         goto    retry\r
529 \r
530 \r
531 \r
532 ;       movlw   StartUpText1\r
533 ;       call    TxLCD16B\r
534 ;       call    LongDelay\r
535 \r
536 ;       bsf     PORTA,4         ; turn off LED\r
537 \r
538 ;       movlw   StartUpText2\r
539 ;       call    TxLCD16B\r
540 ;       call    LongDelay\r
541 \r
542 ;       bcf     PORTA,4         ; turn on LED\r
543 \r
544 ;       movlw   StartUpText3\r
545 ;       call    TxLCD16B\r
546 ;       call    LongDelay\r
547 \r
548 ;       goto    retry\r
549 \r
550 \r
551 ;----------------------------------------------------------------\r
552 ; IRQInit - Sets up the IRQ Handler\r
553 \r
554 IRQInit\r
555         bsf     STATUS,RP0              ; Reg bank 1\r
556 ;       bcf     OPTION_REG,INTEDG       ; We want RB0 to give us an IRQ on the falling edge\r
557         bsf     INTCON,INTE             ; Enable the RB0/INT\r
558         bsf     INTCON,GIE              ; Enable global interrupts\r
559         bcf     STATUS,RP0              ; Back to bank 0\r
560         return\r
561 \r
562 ;----------------------------------------------------------------\r
563 ;  Initialize LCD Controller...\r
564 \r
565 LCDInit\r
566         clrf    PORTB\r
567         bsf     STATUS,RP0      ; Hi Bank\r
568         movlw   001h            ; All but RB0 are outputs.\r
569         movwf   TRISB           ; Yep\r
570         bcf     OPTION_REG,NOT_RBPU     ; Turn on port B pull-up\r
571         bcf     STATUS,RP0      ; Restore Lo Bank\r
572 \r
573 ;       bcf     PORTA,4         ; turn on LED\r
574 \r
575 ;       movlw   44              ; Should be 16ms delay\r
576         movlw   255             ; Should be 16ms delay\r
577         call    DelayW\r
578 \r
579         movlw   3               ; Write 3 to the LCD\r
580         call    TxLCD           ; Send to LCD\r
581 ;       movlw   12              ; Should be 5ms delay\r
582         movlw   255             ; Should be 16ms delay\r
583         call    DelayW\r
584 \r
585         movlw   3               ; Write 3 to the LCD\r
586         call    TxLCD\r
587 ;       movlw   12              ; Should be 16ms delay\r
588         movlw   255             ; Should be 16ms delay\r
589         call    DelayW\r
590 \r
591         movlw   3               ; Write 3 to the LCD\r
592         call    TxLCD\r
593 ;       movlw   44\r
594         movlw   255             ; Should be 16ms delay\r
595         call    DelayW\r
596 \r
597         movlw   2               ;\\r
598         call    TxLCD           ; | 4-bit interface\r
599 ;       movlw   55              ; | After this we are ready to ROCK!\r
600         movlw   255             ; Should be 16ms delay\r
601         call    DelayW          ;/\r
602 \r
603 ;       bsf     PORTA,4         ; turn off LED\r
604 \r
605         movlw 28h               ; Some random commands :)))\r
606         call TxLCDB\r
607 \r
608         movlw 0ch               ; hmmm\r
609         call TxLCDB\r
610 \r
611         movlw 01h               ; hmmm\r
612         call TxLCDB\r
613 \r
614         movlw 06h               ; hmmm\r
615         call TxLCDB\r
616         \r
617         return\r
618    \r
619 ;----------------------------------------------------------------\r
620 ; LongDelay - Well, guess that for yourself...\r
621 \r
622 LongDelay\r
623 ;   btfss PORTB,6        ; Talk to da PC?\r
624 ;   goto PCTalk          ; Oh yeah...\r
625 \r
626    movlw 255\r
627    call DelayW\r
628    movlw 255\r
629    call DelayW\r
630    movlw 255\r
631    call DelayW\r
632    movlw 255\r
633    call DelayW\r
634    movlw 255\r
635    call DelayW\r
636    movlw 255\r
637    call DelayW\r
638    movlw 255\r
639    call DelayW\r
640    movlw 255\r
641    call DelayW\r
642    movlw 255\r
643    call DelayW\r
644    movlw 255\r
645    call DelayW\r
646    movlw 255\r
647    call DelayW\r
648    movlw 255\r
649    call DelayW\r
650    movlw 255\r
651    call DelayW\r
652    movlw 255\r
653    call DelayW\r
654    movlw 255\r
655    call DelayW\r
656    movlw 255\r
657    call DelayW\r
658    return\r
659 \r
660 ;----------------------------------------------------------------\r
661 ;  TxLCD16B\r
662 ;  Send a string to the LCD.\r
663 \r
664 TxLCD16B\r
665         movwf   Icount\r
666         bcf     LCD_RS_BIT\r
667         movlw   80h             ;DisplayRam 0\r
668         call    TxLCDB\r
669         bsf     LCD_RS_BIT\r
670         call    TxLCD8B\r
671         bcf     LCD_RS_BIT\r
672         movlw   80h+40          ;DisplayRam 40 (row 2)\r
673         call    TxLCDB\r
674         bsf     LCD_RS_BIT\r
675         call    TxLCD8B\r
676         return\r
677 \r
678 ;----------------------------------------------------------------\r
679 ;  TxLCD8B\r
680 ;  Send a string to the LCD.\r
681 \r
682 TxLCD8B\r
683 ;       movwf   Icount          ; Icount = W\r
684         movlw   8\r
685         movwf   e_LEN           ; Move to e_LEN\r
686 \r
687 Txm_lp  movf    Icount,w        ; get the byte\r
688         call    LookUp\r
689         incf    Icount,f        ; ...else ++Icount (table index)\r
690         call    TxLCDB          ; Send out the byte\r
691         decfsz  e_LEN,f\r
692         goto    Txm_lp\r
693         return\r
694 \r
695 ;----------------------------------------------------------------\r
696 ; TxLCDB - send a byte to the LCD\r
697 \r
698 TxLCDB\r
699         movwf   TxTemp          ; Store byte to send for a while...\r
700 \r
701         bcf     temp,0          ; Clear my temp bit\r
702         btfss   LCD_RS_BIT      ; Check if we try the correct reg\r
703         goto    RxNoProb\r
704         bcf     LCD_RS_BIT\r
705         bsf     temp,0          ; Indicate RS change\r
706 RxNoProb\r
707 \r
708 NotReady\r
709         call    RxLCDB          ; Receive byte from LCD, status reg\r
710         andlw   80h\r
711         btfss   STATUS,Z        ; If the bit was set, the zero flag is not\r
712         goto    NotReady\r
713 \r
714         btfsc   temp,0          ; If we had to clear RS reset it now\r
715         bsf     LCD_RS_BIT\r
716 \r
717         swapf   TxTemp,w        ; Hi nibble of data to send in lo w bits\r
718         call    TxLCD           ; Send them first...\r
719         movf    TxTemp,w        ; Then we have the low nibble in low w bits\r
720         call    TxLCD           ; And send that one as well\r
721 \r
722         return\r
723 ;----------------------------------------------------------------\r
724 ; RxLCDB - recv a byte from the LCD\r
725 \r
726 RxLCDB\r
727         call    RxLCD           ; Receive the high nibble\r
728         movwf   LCDWTmp\r
729         swapf   LCDWTmp,f       ; Swap it back to file\r
730         call    RxLCD           ; Receive the low nibble\r
731         addwf   LCDWTmp,w       ; Put the nibbles together and return in W\r
732 \r
733         return\r
734 \r
735 ;----------------------------------------------------------------\r
736 ; TxLCD - send a nibble to the LCD\r
737 \r
738 TxLCD\r
739         movwf   LCDWTmp         ; Write nibble to tmp\r
740         bcf     LCD_DB4_BIT     ; Clear previous data\r
741         bcf     LCD_DB5_BIT     ; \r
742         bcf     LCD_DB6_BIT     ;\r
743         bcf     LCD_DB7_BIT     ;\r
744 \r
745         btfsc   LCDWTmp,0       ; Test bit 0, transfer a set bit to LCD PORT\r
746         bsf     LCD_DB4_BIT\r
747         btfsc   LCDWTmp,1       ; Test bit 1, transfer a set bit to LCD PORT\r
748         bsf     LCD_DB5_BIT\r
749         btfsc   LCDWTmp,2       ; Test bit 2, transfer a set bit to LCD PORT\r
750         bsf     LCD_DB6_BIT\r
751         btfsc   LCDWTmp,3       ; Test bit 3, transfer a set bit to LCD PORT\r
752         bsf     LCD_DB7_BIT\r
753 \r
754         bsf     LCD_E_BIT       ; And set E to clock the data into the LCD module\r
755         nop                     ; Let it settle\r
756         bcf     LCD_E_BIT       ; And clear the Enable again.\r
757         return                  ; Returns without modifying W\r
758 \r
759 ;----------------------------------------------------------------\r
760 ; RxLCD - recv a nibble from the LCD\r
761 \r
762 RxLCD\r
763         clrw                    ; Clear W register, return data in lower 4 bits\r
764 \r
765         bsf     STATUS,RP0      ; Select 2nd reg bank, now TRIS regs can be accessed\r
766         \r
767         bsf     LCD_DB4_BIT     ; This sets the port bit as an input\r
768         bsf     LCD_DB5_BIT     \r
769         bsf     LCD_DB6_BIT     \r
770         bsf     LCD_DB7_BIT\r
771         bcf     STATUS,RP0      ; Back at reg bank 0    \r
772 \r
773         bsf     LCD_RW_BIT      ; Set Read mode for the LCD\r
774         bsf     LCD_E_BIT       ; And set E to clock the data out of the LCD module\r
775         nop                     ; Let the bus settle\r
776         btfsc   LCD_DB4_BIT     ; Transfer a set port bit into W\r
777         addlw   1\r
778         btfsc   LCD_DB5_BIT     ; Transfer a set port bit into W\r
779         addlw   2\r
780         btfsc   LCD_DB6_BIT     ; Transfer a set port bit into W\r
781         addlw   4\r
782         btfsc   LCD_DB7_BIT     ; Transfer a set port bit into W\r
783         addlw   8\r
784         bcf     LCD_E_BIT       ; And clear the Enable again.\r
785         bcf     LCD_RW_BIT      ; Set Write mode for the LCD\r
786 \r
787         bsf     STATUS,RP0      ; Select 2nd reg bank, now TRIS regs can be accessed\r
788         bcf     LCD_DB4_BIT     ; Set the port as an output again\r
789         bcf     LCD_DB5_BIT     ; \r
790         bcf     LCD_DB6_BIT     ;\r
791         bcf     LCD_DB7_BIT     ;\r
792         bcf     STATUS,RP0      ; Back at reg bank 0    \r
793 \r
794         return                  ; Returns with data in W\r
795 \r
796 ;----------------------------------------------------------------------\r
797 ; Delay routines       (one iteration=3 cycles. That is 0.366211ms @32kHz)\r
798 ; 2.73* # of ms is good...\r
799 \r
800 DelayW  movwf   Dcount          ; Set delay counter\r
801         clrf    Dcount2\r
802         decf    Dcount2,f\r
803 DelayLp decfsz  Dcount,f\r
804         goto    DelayIn\r
805         retlw   0\r
806 DelayIn decfsz  Dcount2,f\r
807         goto    DelayIn2\r
808         decf    Dcount2,f\r
809         goto    DelayLp\r
810 DelayIn2        goto    $+1\r
811         goto    $+1\r
812         goto    $+1\r
813         goto    DelayIn\r
814 \r
815         subtitl "Bootstrap/Bootloader code"\r
816         page\r
817 \r
818 ;----------------------------------------------------------------------\r
819 ; Bootstrap code - Allows PIC to flash itself with data from async port\r
820 ; Startup @9600bps\r
821 \r
822         org     700h                    ; Place the boot code at the top of memory\r
823 \r
824 BootRXState     equ     7fh             ; What are we waiting for @RX\r
825 BootBits        equ     7eh             ; bit0 1=write 0=read, bit1 1=PGM 0=EE, bit2 0=normal 1=no-op when prog\r
826 BootAddrL       equ     7dh\r
827 BootAddrH       equ     7ch\r
828 BootDataL       equ     7bh\r
829 BootDataH       equ     7ah\r
830 BootTimerL      equ     79h\r
831 BootTimerM      equ     78h\r
832 BootTimerH      equ     77h\r
833 BootNumBytes    equ     76h\r
834 BootDataVL      equ     75h\r
835 BootDataVH      equ     74h\r
836 BootHEXTemp     equ     73h\r
837 BootStrTemp     equ     72h\r
838 \r
839 Bootstrap\r
840         movlw   7\r
841         movwf   PCLATH\r
842 \r
843         bsf     STATUS,RP0              ; Access bank 1\r
844         bsf     TXSTA,TXEN              ; Enable UART TX\r
845         movlw   31                      ; Divisor for 9k6 @ 20MHz Fosc\r
846         movwf   SPBRG                   ; Store\r
847         bcf     STATUS,RP0              ; Back to bank 0\r
848 \r
849         bsf     RCSTA,SPEN              ; Enable serial port\r
850         bsf     RCSTA,CREN              ; Enable UART RX\r
851 \r
852 ;       clrf    BootRXState             ; Waiting for command\r
853 \r
854         movlw   BootStartText\r
855         call    BootTXStr\r
856 \r
857         movlw   0e8h\r
858         movwf   BootTimerL\r
859         movwf   BootTimerM\r
860         movwf   BootTimerH\r
861 \r
862 BootTimeout\r
863         incf    BootTimerL,f            ; A 24-bit counter\r
864         skpnz\r
865         incf    BootTimerM,f\r
866         skpnz\r
867         incf    BootTimerH,f\r
868         skpnz                           ; When overflowing here..\r
869         goto    BootReturn              ; ..Exit boot loader, no keypress within timeout period, resume program\r
870         btfss   PIR1,RCIF               ; Wait for RX to complete\r
871         goto    BootTimeout\r
872         call    BootRXB\r
873         xorlw   27                      ; ESC\r
874         btfss   STATUS,Z\r
875         goto    BootTimeout             ; If it wasn't space, wait for another key\r
876 \r
877 BootFlash\r
878         movlw   BootFlashText\r
879         call    BootTXStr\r
880 \r
881         bsf     BootBits,1\r
882         clrf    BootAddrL\r
883         clrf    BootAddrH\r
884 \r
885 BootLoop\r
886         call    BootRXB                 ; First find the ':'\r
887         xorlw   ':'\r
888         skpz\r
889         goto    BootLoop                ; Loop until we find it!\r
890 \r
891         call    BootRXHEX               ; Get one ASCII encoded byte (two chars)\r
892         movwf   BootNumBytes            ; This is the number of bytes to be programmed on the line\r
893 ; Maybe clear cary here?\r
894         rrf     BootNumBytes,f          ; Right shift because we're double addressing this 8-bit format\r
895 \r
896 ; Note carry should be clear here as there cannot be odd number of bytes in this format\r
897 \r
898         call    BootRXHEX               ; Receive AddrH\r
899         movwf   BootAddrH\r
900         call    BootRXHEX               ; Receive AddrL\r
901         movwf   BootAddrL\r
902         rrf     BootAddrH,f             ; Fix the addressing again\r
903         rrf     BootAddrL,f\r
904 \r
905         bcf     BootBits,2              ; Assume we should program\r
906         bsf     BootBits,1              ; And assume we should program flash not ee\r
907 \r
908         movf    BootAddrH,w\r
909         xorlw   020h                    ; Check if it's configuration, which we can't program\r
910         skpnz                           ; Skip the bit set if it was false alarm\r
911         bsf     BootBits,2              ; No programming for this line\r
912 \r
913         xorlw   001h                    ; Also check if it's EEPROM memory (first xor 20h then 1 =21h)\r
914         skpnz                           ; Skip the bit set instr if not EE data address\r
915         bcf     BootBits,1              ; We should program EE, will ignore the AddrH\r
916 \r
917         call    BootRXHEX               ; Receive Record Type (must be 0 for real records)\r
918         skpz                            ; Check if zero\r
919         goto    BootFlashComplete\r
920 \r
921 BootLineLoop\r
922         call    BootRXHEX               ; Receive low-byte of data word\r
923         movwf   BootDataVL\r
924         call    BootRXHEX               ; Receive high-byte of data word\r
925         movwf   BootDataVH\r
926         \r
927         btfsc   BootBits,2              ; Check whether this line should be programmed at all\r
928         goto    BootWriteSkip\r
929 \r
930         bcf     BootBits,0              ; Read mode first, verify if we actually have to write\r
931         call    BootEE\r
932         movf    BootDataVL,w\r
933         xorwf   BootDataL,f             ; Compare and destroy DataL\r
934         movwf   BootDataL               ; Write new data to DataL\r
935         skpz                            ; Skip if no difference, have to check high byte as well\r
936         goto    BootWrite               ; Jump directly to write\r
937 \r
938         movf    BootDataVH,w\r
939         xorwf   BootDataH,f             ; Compare\r
940         skpnz                           ; Skip if no difference, no programming necessary\r
941         goto    BootWriteSkip\r
942 \r
943 BootWrite\r
944         movf    BootDataVH,w\r
945         movwf   BootDataH               ; Have to put the new H byte data in as well\r
946 \r
947 ;       movlw   '+'\r
948 ;       call    BootTXB                 ; Send progword indicator\r
949 \r
950         bsf     BootBits,0\r
951         call    BootEE                  ; Write directly into program mem\r
952 \r
953 ; Here a verify can take place, the read-back results are now in DataL/H\r
954 \r
955         movlw   '+'\r
956         goto    BootWriteDone\r
957 \r
958 BootWriteSkip\r
959         movlw   '-'\r
960 BootWriteDone\r
961         call    BootTXB\r
962 \r
963         incf    BootAddrL,f             ; Advance counter to next addr\r
964         skpnz\r
965         incf    BootAddrH,f             ; And add to high byte if needed\r
966 \r
967         decfsz  BootNumBytes,f\r
968         goto    BootLineLoop\r
969 \r
970         movlw   13                      ; Progress\r
971         call    BootTXB\r
972         movlw   10                      ; Progress\r
973         call    BootTXB\r
974 \r
975         goto    BootLoop\r
976 \r
977 BootFlashComplete\r
978         \r
979 BootReturn\r
980         movlw   BootRunText\r
981         call    BootTXStr\r
982 \r
983         bsf     STATUS,RP0\r
984 BootReturnWait\r
985         btfss   TXSTA,TRMT              ; Wait for last things to flush\r
986         goto    BootReturnWait\r
987         bcf     TXSTA,TXEN              ; Disable UART TX\r
988         bcf     STATUS,RP0              ; Back to bank 0\r
989 \r
990         bcf     RCSTA,SPEN              ; Enable serial port\r
991         bcf     RCSTA,CREN              ; Enable UART RX\r
992 \r
993         clrf    PCLATH\r
994         return                          ; Return to code        \r
995 \r
996 ;----------------------------------------------------------------------\r
997 ; BootTXB - Sends one byte to the UART, waits for transmitter to become\r
998 ;  free before sending\r
999 \r
1000 BootTXB\r
1001 BootTXW1\r
1002         btfss   PIR1,TXIF               ; Wait for TX to empty\r
1003         goto    BootTXW1\r
1004         movwf   TXREG                   ; Send the byte\r
1005         return\r
1006 \r
1007 ;----------------------------------------------------------------------\r
1008 ; BootTXStr - Sends ASCII string pointed to by W, zero terminated\r
1009 \r
1010 BootTXStr\r
1011         movwf   BootStrTemp             ; Store offset\r
1012         call    BootLookup              ; Lookup char\r
1013         addlw   0\r
1014         skpnz\r
1015         return\r
1016         call    BootTXB                 ; Send char\r
1017         incf    BootStrTemp,w           ; Retrieve\r
1018         goto    BootTXStr\r
1019 \r
1020 ;----------------------------------------------------------------------\r
1021 ; BootRXB - Receives one byte from the UART, waits if nothing available\r
1022 \r
1023 BootRXB\r
1024 BootRXW1\r
1025         btfss   PIR1,RCIF               ; Wait for RX to complete\r
1026         goto    BootRXW1\r
1027         movf    RCREG,w                 ; Get the recvd byte\r
1028         return\r
1029 \r
1030 ;----------------------------------------------------------------------\r
1031 ; BootRXHEXNibble - Receives one byte and converts it from ASCII HEX to binary\r
1032 \r
1033 BootRXHEXNibble\r
1034         call    BootRXB                 ; Receive nibble\r
1035 \r
1036         addlw   -'A'                    ; Convert from BCD to binary nibble\r
1037         skpc                            ; Test if if was 0-9 or A-F, skip if A-F\r
1038         addlw  'A' - 10 - '0'           ; It was numeric '0'\r
1039         addlw   10                      ; Add 10 (A get to be 0ah etc.)\r
1040         return\r
1041 \r
1042 ;----------------------------------------------------------------------\r
1043 ; BootRXHEX - Receives two bytes from the UART, waits if nothing available\r
1044 ;  Decodes the bytes as ASCII hex and returns a single byte in W\r
1045 \r
1046 BootRXHEX\r
1047         call    BootRXHEXNibble\r
1048         movwf   BootHEXTemp\r
1049         swapf   BootHEXTemp,f           ; Swap it up to the high nibble\r
1050 \r
1051         call    BootRXHEXNibble\r
1052         addwf   BootHEXTemp,w           ; And add the two nibbles together\r
1053         return\r
1054 \r
1055 ;----------------------------------------------------------------------\r
1056 ; BootEE - Reads or writes EE or Flash memory, BootBits specify the\r
1057 ;  exact action to take. BootAddrL and BootAddrH has to be initialized\r
1058 ;  to the address of choice (0000-003fh for EE and 0000h-07ffh for flash\r
1059 ;  The data to be written has to be put in BootDataL and BootDataH, and\r
1060 ;  data will be to the same place when read back\r
1061 \r
1062 BootEE\r
1063         bsf     STATUS,RP1              ; Select bank 2 (RP0 must be 0)\r
1064 \r
1065         movf    BootAddrH,w             ; Load desired address\r
1066         movwf   EEADRH\r
1067         movf    BootAddrL,w\r
1068         movwf   EEADR\r
1069         movf    BootDataH,w             ; And load the data (only used when writing)\r
1070         movwf   EEDATH\r
1071         movf    BootDataL,w\r
1072         movwf   EEDATA\r
1073 \r
1074         bsf     STATUS,RP0              ; Go to bank 3\r
1075 \r
1076         bsf     EECON1,EEPGD            ; Point to Program Flash mem\r
1077         btfss   BootBits,1              ; Test if that was correct or if we have to clear again\r
1078         bcf     EECON1,EEPGD            ; Point to EE DATA mem\r
1079 \r
1080         btfss   BootBits,0              ; Check from read or write\r
1081         goto    BootEERD                ; Skip the WR if we were going for a read\r
1082 \r
1083         bsf     EECON1,WREN             ; Enable writes\r
1084         movlw   55h\r
1085         movwf   EECON2\r
1086         movlw   0AAh\r
1087         movwf   EECON2                  ; Unlock write operation\r
1088         bsf     EECON1,WR               ; And start a write cycle\r
1089 BootWRLoop\r
1090         btfsc   EECON1,WR               ; This executes for EE only not flash, waits for WR to finish\r
1091         goto    BootWRLoop              ; These two instructions gets NOPed when flashing\r
1092 \r
1093         bcf     EECON1,WREN             ; Finally disable writes again\r
1094                                         ; Here we read the data back again, can be used as verify\r
1095 BootEERD\r
1096         bsf     EECON1,RD               ; Start a read cycle\r
1097         nop                             ; Only necessary for flash read, same thing as when writing above\r
1098         nop                             ; Except I could use the two words for something useful there.. :)\r
1099 \r
1100 BootEEX\r
1101         bcf     STATUS,RP0              ; Back to bank 2\r
1102         movf    EEDATA,w                ; Store our EE-data\r
1103         movwf   BootDataL\r
1104         movf    EEDATH,w\r
1105         movwf   BootDataH\r
1106         bcf     STATUS,RP1              ; And finally back to bank 0\r
1107 \r
1108         return\r
1109 \r
1110 BootStartText\r
1111         DT      "WJBoot - press ESC to flash",0\r
1112 BootFlashText\r
1113         DT      13,10,"Send INHX8 file now...",13,10,0\r
1114 BootRunText\r
1115         DT      13,10,"Exiting loader",13,10,0\r
1116 \r
1117 BootLookup\r
1118         movwf   PCL                     ; Go fetch the char data\r
1119 \r
1120 ;----------------------------------------------------------------------\r
1121 ; EE Data (64 bytes), located at 2100h\r
1122 \r
1123         org 2100h\r
1124 ;        data 0f2h, 099h, 000h, 000h, 018h, 0a5h, 090h, 084h\r
1125 \r
1126         END\r