v0.5 - Issues slave breaks seemingly without hickups (!)
[wj-unilink.git] / wj-uni.asm
index 5f47338..08ddc24 100644 (file)
@@ -1,7 +1,7 @@
-       title   "PIC16F870 Unilink Interface by Werner Johansson (c) 2003"\r
+       title   "PIC16F870 Unilink(R) Interface by Werner Johansson (c) 2002-2003"\r
        subtitl "Definitions"\r
-       list    c=132,P=16F870,R=DEC,F=inhx8m\r
-        include "p16f870.inc"            ; Standard equates & Macros\r
+       list    c=150,P=16F870,R=DEC,F=inhx8m\r
+        include "p16f870.inc"           ; Standard equates & Macros\r
         ERRORLEVEL 1,-302               ; Get rid of those annoying 302 msgs!\r
 \r
 \r
       __CONFIG _HS_OSC&_WDT_OFF&_PWRTE_ON&_BODEN_ON&_LVP_OFF&_CPD_OFF&_WRT_ENABLE_ON&_DEBUG_OFF&_CP_OFF\r
 \r
 ;----------------------------------------------------------------\r
-;      HISTORY\r
+;  TODO\r
+;----------------------------------------------------------------\r
+;  Fix the DelayW routine so it actually delays W/10 ms...\r
+;  No checksum checking is done on incoming packets\r
+;  Investigate whether I actually have to save PCLATH in ISH, maybe save FSR?\r
+;  Move RS232 code into ISH\r
+;  Check Overrun errors from the UART\r
+;  Implement lots of other Unilink commands (Text display, time display etc.)\r
+;  Implement the Watchdog Timer (might be useful even though I haven't seen it hang yet..)\r
+\r
+;----------------------------------------------------------------\r
+;  HISTORY\r
 ;----------------------------------------------------------------\r
 ;  Version\r
 ;\r
+;  0.5  Issues slave breaks seemingly without hickups (!)\r
+;  0.4  Some fixups in the bootstrap code (I actually had to put the PIC in my PICSTART Plus programmer again :))\r
+;  0.3  Implementing more Unilink commands and RingIndicator control (to wake the computer from sleep)\r
+;  0.2  First attempt at responding to the Anyone command\r
 ;  0.1  Receives Unilink data OK, relays it to serial\r
-;  0.0  Very first "Fucking No Work!" version\r
-;\r
+;  0.0  Very first "F**king No Work!" version\r
+\r
+;----------------------------------------------------------------\r
+;  I/O LAYOUT\r
 ;----------------------------------------------------------------\r
+;  Unilink BUSON IN (blue) connected to RC2/CCP1\r
+;  Unilink DATA (green) connected to RC3\r
+;  Unilink BUSON OUT (blue) connected to RC4 (this is for daisy-chaining)\r
+;  Unilink CLK (yellow) connected to RB0/INT (Interrupt pin)\r
+;  Unilink RST (lilac) connected to RA4\r
+;  LCD RS connected to pin RB1 (The LCD is a standard 16x1 char HD44780 compatible unit)\r
+;  LCD RW connected to pin RB2\r
+;  LCD E connected to pin RB3\r
+;  LCD DB4-DB7 connected to RB4-RB7\r
+;  RS-232 TX from computer connected to RC7/RX\r
+;  RS-232 RX to computer connected to RC6/TX\r
+;  RS-232 RI to computer connected to RC5\r
+;\r
+;  This leaves RC0, RC1 and the analog inputs (AN0-AN4) free for now...\r
 \r
-; Unilink BUSON IN (blue) connected to RC2/CCP1\r
-; Unilink DATA (green) connected to RC3\r
-; Unilink BUSON OUT (blue) connected to RC4 (this is for daisy-chaining)\r
-; Unilink CLK (yellow) connected to RB0/INT (Interrupt pin)\r
-; Unilink RST (lilac) connected to RA4\r
-; LCD RS connected to pin RB1\r
-; LCD RW connected to pin RB2\r
-; LCD E connected to pin RB3\r
-; LCD DB4-DB7 connected to RB4-RB7\r
-; RS-232 TX from computer connected to RC7/RX\r
-; RS-232 RX to computer connected to RC6/TX\r
-; RS-232 RI to computer connected to RC5\r
-\r
-; This leaves RC0, RC1 and the analog inputs (AN0-AN4) free for now...\r
+#define BUSON_IN_BIT   PORTC,2\r
+#define DATA_BIT       PORTC,3\r
+#define BUSON_OUT_BIT  PORTC,4\r
+#define CLK_BIT                PORTB,0\r
+#define RST_BIT                PORTA,4\r
 \r
 #define        LCD_RS_BIT      PORTB,1\r
 #define        LCD_RW_BIT      PORTB,2\r
 #define LCD_DB6_BIT    PORTB,6\r
 #define LCD_DB7_BIT    PORTB,7\r
 \r
-;----------------------------------------------------------------\r
-;      File register usage\r
-\r
-Dcount equ     20h\r
-e_LEN  equ     21h\r
-Icount equ   2Dh   ; Offset of string to print\r
-TxTemp equ   2Eh   ; blahblah\r
-TxTemp2        equ   2Fh   ; Blahblah2\r
+#define RS232_RI_BIT   PORTC,5\r
 \r
-LCDWTmp equ    30h\r
-Dcount2        equ     31h\r
-temp   equ     32h\r
-\r
-DataCount      equ     33h\r
-DataStore      equ     34h\r
-\r
-IRQW           equ     7fh\r
-IRQSTATUS      equ     7eh\r
-IRQPCLATH      equ     7dh\r
+;----------------------------------------------------------------\r
+;  FILE REGISTER USAGE\r
+;----------------------------------------------------------------\r
+TrackName00    equ     20h             ; Buffer for TrackName\r
+TrackName01    equ     21h\r
+TrackName02    equ     22h\r
+TrackName03    equ     23h\r
+TrackName04    equ     24h\r
+TrackName05    equ     25h\r
+TrackName06    equ     26h\r
+TrackName07    equ     27h\r
+TrackName08    equ     28h\r
+TrackName09    equ     29h\r
+TrackName0a    equ     2ah\r
+TrackName0b    equ     2bh\r
+TrackName0c    equ     2ch\r
+TrackName0d    equ     2dh\r
+TrackName0e    equ     2eh\r
+TrackName0f    equ     2fh\r
+TrackName10    equ     30h\r
+TrackName11    equ     31h\r
+TrackName12    equ     32h\r
+TrackName13    equ     33h\r
+TrackName14    equ     34h\r
+TrackName15    equ     35h\r
+TrackName16    equ     36h\r
+TrackName17    equ     37h\r
+TrackName18    equ     38h\r
+TrackName19    equ     39h\r
+TrackName1a    equ     3ah\r
+TrackName1b    equ     3bh\r
+TrackName1c    equ     3ch\r
+TrackName1d    equ     3dh\r
+TrackName1e    equ     3eh\r
+TrackName1f    equ     3fh\r
+TrackName20    equ     40h\r
+TrackName21    equ     41h\r
+TrackName22    equ     42h\r
+TrackName23    equ     43h\r
+TrackName24    equ     44h\r
+TrackName25    equ     45h\r
+TrackName26    equ     46h\r
+TrackName27    equ     47h\r
+TrackName28    equ     48h\r
+TrackName29    equ     49h\r
+TrackName2a    equ     4ah\r
+TrackName2b    equ     4bh\r
+TrackName2c    equ     4ch\r
+TrackName2d    equ     4dh\r
+TrackName2e    equ     4eh\r
+TrackName2f    equ     4fh\r
+\r
+UnilinkRAD     equ     50h             ; Beginning of Unilink packet - the Receiving Address\r
+UnilinkTAD     equ     51h             ; Transmitter address\r
+UnilinkCMD1    equ     52h             ; CMD1 byte\r
+UnilinkCMD2    equ     53h             ; CMD2 byte\r
+UnilinkParity1 equ     54h             ; First or only parity byte for short packets (6 bytes)\r
+UnilinkData1   equ     55h             ; Extra data for medium/large packets, or zero for short packets\r
+UnilinkData2   equ     56h             ;\r
+UnilinkData3   equ     57h             ;\r
+UnilinkData4   equ     58h             ;\r
+UnilinkData5   equ     59h             ; Data5 if this is a large packet\r
+UnilinkParity2M        equ     59h             ; Parity2 shares the same byte if it's a medium sized packet\r
+UnilinkData6   equ     5ah             ; Extra data for large packets, or zero for medium packets\r
+UnilinkData7   equ     5bh             ;\r
+UnilinkData8   equ     5ch             ;\r
+UnilinkData9   equ     5dh             ;\r
+UnilinkParity2 equ     5eh             ; Parity byte for large packets\r
+UnilinkZero    equ     5fh             ; Should always be zero (possibly used to signal corrupt packets from slave to master?)\r
+\r
+UnilinkTimeout equ     60h             ; Counts up every 0.5ms to "age out" faulty bytes clocked in\r
+UnilinkSelected        equ     61h             ; High bit is set when selected\r
+UnilinkBit     equ     62h             ; This is my "bitmask" to be used for requests\r
+UnilinkID      equ     63h             ; This is my Bus ID\r
+UnilinkCmdLen  equ     64h             ; This gets updated with the actual packet length after CMD1 has been received\r
+UnilinkTXRX    equ     65h             ; This is a pointer to the Unilink packet above, used with indirect addressing\r
+SlaveBreakState        equ     66h             ; Hold state and time-out information about slave break, indicates when it can happen\r
+DisplayStatus  equ     67h             ; What information will be put on the display next, bit 7 cleared if nothing\r
+Icount         equ     68h             ; Offset of string to print\r
+TxTemp         equ     69h             ; blahblah\r
+TxTemp2                equ     6ah             ; Blahblah2\r
+LCDWTmp        equ     6bh\r
+Dcount2                equ     6ch\r
+temp           equ     6dh\r
+Dcount         equ     6eh\r
+e_LEN          equ     6fh\r
+\r
+\r
+Counter                equ     70h\r
+DataCount      equ     71h             ; Temp storage for the bit counter used during bit shifts (Unilink TX/RX)\r
+DataStore      equ     72h             ; This is a kludge\r
+\r
+IRQPCLATH      equ     7dh             ; ISH storage\r
+IRQSTATUS      equ     7eh             ; Needs to be located in a shared area accessible from all register banks\r
+IRQW           equ     7fh             ; \r
+\r
+DiscName00     equ     0a0h            ; Buffer for DiscName\r
+DiscName01     equ     0a1h\r
+DiscName02     equ     0a2h\r
+DiscName03     equ     0a3h\r
+DiscName04     equ     0a4h\r
+DiscName05     equ     0a5h\r
+DiscName06     equ     0a6h\r
+DiscName07     equ     0a7h\r
+DiscName08     equ     0a8h\r
+DiscName09     equ     0a9h\r
+DiscName0a     equ     0aah\r
+DiscName0b     equ     0abh\r
+DiscName0c     equ     0ach\r
+DiscName0d     equ     0adh\r
+DiscName0e     equ     0aeh\r
+DiscName0f     equ     0afh\r
+DiscName10     equ     0b0h\r
+DiscName11     equ     0b1h\r
+DiscName12     equ     0b2h\r
+DiscName13     equ     0b3h\r
+DiscName14     equ     0b4h\r
+DiscName15     equ     0b5h\r
+DiscName16     equ     0b6h\r
+DiscName17     equ     0b7h\r
+DiscName18     equ     0b8h\r
+DiscName19     equ     0b9h\r
+DiscName1a     equ     0bah\r
+DiscName1b     equ     0bbh\r
+DiscName1c     equ     0bch\r
+DiscName1d     equ     0bdh\r
+DiscName1e     equ     0beh\r
+DiscName1f     equ     0bfh\r
 \r
        subtitl "Startup"\r
        page\r
 ;----------------------------------------------------------------\r
-;  Power up/Reset starting point [den rulerar]\r
+;  Power up/Reset starting point\r
 \r
-       org     0\r
-       call    Bootstrap       ; Call Flash Load routine\r
-       call    LCDInit         ; Initialize LCD I/F\r
-       call    IRQInit         ; Set up and start the IRQ handler\r
-       goto    Main            ; Run the main program loop (skip the IRQ handler)\r
+       org     0                       ; Start at the beginning of memory (the reset vector)\r
+       call    Bootstrap               ; Call Flash Load routine\r
+       call    LCDInit                 ; Initialize LCD I/F\r
+       call    IRQInit                 ; Set up and start the IRQ handler\r
+       goto    Main                    ; Run the main program loop (skip the IRQ handler)\r
 \r
        subtitl "IRQ Handler"\r
 ;----------------------------------------------------------------\r
 ;  Interrupt handler always starts at addr 4\r
-\r
-       org     4               ; Must be on Address 4!\r
-       movwf   IRQW            ; Save W\r
-       swapf   STATUS,w        ; Get the status register into w\r
-       clrf    STATUS          ; Zero out the status reg, gives us Bank0 all the time\r
-       movwf   IRQSTATUS\r
-       movf    PCLATH,w\r
-       movwf   IRQPCLATH\r
-       clrf    PCLATH\r
-\r
-       btfss   INTCON,INTF             ; Check if it's INT (CLK)\r
-       goto    IRQNotINT               ; Nope\r
-\r
-; If there's activity on the clock line (the clock goes high) we stay in here until we have clocked eight bits\r
-; - this saves us a lot of context switching (and it's just a few hundred cpu cycles after all (20us*8 bits=\r
-; 160us=800 instruction cycles (5 MIPS @ 20MHz), not even a problem for serial input if we're not getting more than\r
-; 6250 bytes per second from the UART, and the 2-byte FIFO somehow fills up (this should be impossible even @ 115200\r
-; 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
-; this gives the CPU ample of time to deal with all bytes from the USART. I'm checking the OERR (Serial Overrun) bit\r
-; to catch this though..\r
-\r
-       movlw   8                       ; Loop this many times\r
+;  In order to reduce the INT latency the actual code is put here directly instead of using a goto instruction.\r
+;  Also because of the real-time requirements for clocking data onto the Unilink bus the first check in the ISR\r
+;  is to see whether the Unilink clock rise was the reason for the interrupt. This results in a "clock rise to\r
+;  bit ready" time of less than 30 instruction cycles, should be plenty of spare time waiting for clock to go low\r
+;  again after that. Other interrupts might introduce latencies, but let's see how this works..\r
+\r
+       org     4                       ; ISR vector is at address 4\r
+       movwf   IRQW                    ; Save W\r
+       swapf   STATUS,w                ; Get the status register into w\r
+       clrf    STATUS                  ; Zero out the status reg, gives Reg Bank0\r
+       movwf   IRQSTATUS               ; Store the STATUS reg\r
+; Not using PCLATH for anything in the ISR right now\r
+;      movf    PCLATH,w                ; Get the PCLATH reg\r
+;      movwf   IRQPCLATH               ; And store it\r
+;      clrf    PCLATH                  ; Go to low memory\r
+; Maybe save FSR here as well (if there's a need for it in the non-ISR code)\r
+\r
+       btfss   INTCON,INTF             ; Check if it's the INT edge interrupt (Unilink CLK)\r
+       goto    IRQNotINT               ; No it's not, check the other sources\r
+\r
+; If there's activity on the clock line (the clock goes high) the CPU will stay in here until eight bits have been clocked in\r
+; - this reduces context switching (and it's just a few hundred cpu cycles after all (20us*8 bits=160us=800 instruction\r
+; cycles (5 MIPS @ 20MHz), not even a problem for serial input if it's not receiving more than 6250 bytes per second, and the\r
+; 2-byte FIFO somehow fills up (this should be impossible even @ 115200 as this blocking INT handler only runs a maximum of\r
+; 1000 times per second, halting INT's for 1/6250 of a second - this gives the CPU ample of time to deal with all bytes from\r
+; the USART. I should check the OERR (Serial Overrun) bit to catch this though.. Note that this piece of code does both TX\r
+; and RX at the same time (in order to receive packets one has to make sure that the packet buffer is zeroed out before entering\r
+; here, otherwise collisions will occur..\r
+; According to my logic analyzer this implementation is pretty decent when it comes to timing, even though it's an\r
+; interrupt driven "USART" implemented in software - by trigging the interrupt on the rising edge there's some extra margin here\r
+; (the clock goes high 10us before the master clocks the bit in (on the falling edge), that should be plenty of time..)\r
+\r
+       movlw   8                       ; Loop through the 8 bits\r
        movwf   DataCount\r
-\r
-CLKWaitHigh\r
-       btfss   PORTC,2                 ; Check for BUSON\r
-       goto    IRQAfterINT\r
-       btfss   PORTB,0                 ; Wait for clock to go high\r
-       goto    CLKWaitHigh\r
-\r
-CLKWaitLow\r
+       movf    UnilinkTXRX,w           ; Get the pointer\r
+       movwf   FSR                     ; Store it to make use of indirect addressing\r
+\r
+IRQINTBitSet\r
+       btfss   INDF,7                  ; Test high bit of data (that's the first bit to be clocked out)\r
+       goto    IRQINTTristate          ; Bit is low, we should tristate bit\r
+       bcf     PORTC,3                 ; Otherwise set DATA bit low\r
+       bsf     STATUS,RP0              ; Select high regs\r
+       bcf     TRISC,3                 ; And pull low (now it's an output)\r
+       bcf     STATUS,RP0              ; Back to regbank 0\r
+       goto    IRQINTCLKWaitLow        ; Wait for master to actually clock this bit in\r
+\r
+IRQINTTristate\r
+       bsf     STATUS,RP0              ; Select high regs\r
+       bsf     TRISC,3                 ; Force the bit to be tristated\r
+       bcf     STATUS,RP0              ; Back to regbank 0\r
+\r
+IRQINTCLKWaitLow\r
        btfss   PORTC,2                 ; Check for BUSON\r
        goto    IRQAfterINT\r
        btfsc   PORTB,0                 ; Wait for clock to go low\r
-       goto    CLKWaitLow\r
+       goto    IRQINTCLKWaitLow\r
 \r
-       clrc                            ; Clear carry (this way the DataStore byte doesn't have to be cleared before)\r
+       clrc                            ; Clear carry\r
        btfss   PORTC,3                 ; Test DATA\r
        setc                            ; Set carry if data is LOW (data is inverted!)\r
-       rlf     DataStore,f             ; Shift it into our accumulator\r
+       rlf     INDF,f                  ; Shift it into the "accumulator"\r
 \r
        decfsz  DataCount,f             ; Loop once more perhaps?\r
-       goto    CLKWaitHigh\r
+       goto    IRQINTCLKWaitHigh       ; Yes, again!\r
+       goto    IRQINTRecvDone          ; No it's done, don't check for clock to go high again\r
+\r
+IRQINTCLKWaitHigh\r
+       btfss   PORTC,2                 ; Check for BUSON\r
+       goto    IRQAfterINT\r
+       btfss   PORTB,0                 ; Wait for clock to go high\r
+       goto    IRQINTCLKWaitHigh\r
+       goto    IRQINTBitSet            ; Loop again\r
 \r
 ; Successfully received a byte here, run it through a state machine to figure out what to do\r
-; (several possibilites exists here:\r
-; If more than 1.1ms has passed since last receive, reset receive counter to zero\r
+; (several possibilites exists here):\r
+;;;;;; If more than 1.1ms has passed since last receive, reset receive counter to zero\r
 ; If receive counter is zero and the received byte is a zero byte, discard it\r
 ; Otherwise store the byte in our receive buffer and increment receive counter\r
 ; 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
@@ -130,265 +292,733 @@ CLKWaitLow
 ;   10 = medium 11 byte packet\r
 ;   11 = long 16 byte packet\r
 ; Update the receive length byte accordingly\r
-; Check whether receive length and receive count are equal, that means that we're finished, flag this by setting \r
-;  the high bit of receive length\r
+; Check whether receive length and receive count are equal, that means that we're finished and we can carry on parsing\r
+; the packet and take appropriate action.\r
+\r
+IRQINTRecvDone\r
+       clrf    SlaveBreakState         ; First of all, clear the break state - this got in the way, restart detection..\r
+       movf    UnilinkTXRX,w           ; Find out which byte # that was received\r
+       andlw   0fh                     ; Mask\r
+       bnz     IRQINTRecvNotFirst      ; Not the first byte\r
+       movf    UnilinkRAD,w            ; Get the first byte received\r
+       bz      IRQINTRecvNullByte      ; Null byte received, ignore this, don't increment counter\r
+IRQINTRecvNotFirst\r
+       incf    UnilinkTXRX,f           ; Increment address\r
+\r
+       movf    UnilinkTXRX,w           ; Get the byte position again\r
+       andlw   0fh                     ; Only lower 4 bits of interest\r
+       xorlw   03h                     ; Well, is it the third byte? (CMD1, telling us the length of the packet)\r
+       bnz     IRQINTRecvNotCMD1       ; No, skip the length code for now\r
+       movlw   6                       ; Assume it's a short packet\r
+       btfss   INDF,7                  ; INDF still points to received byte, test high bit for medium/long\r
+       goto    IRQINTRecvShort         ; Nope, it's a short packet\r
+       addlw   5                       ; OK, it's long or medium at least\r
+       btfsc   INDF,6                  ; Test for long\r
+       addlw   5                       ; Yep, it's a long packet\r
+IRQINTRecvShort\r
+       movwf   UnilinkCmdLen           ; Store the length\r
+\r
+IRQINTRecvNotCMD1\r
+       movf    UnilinkTXRX,w           ; Get the byte position\r
+       xorwf   UnilinkCmdLen,w         ; XOR with the calculated command length\r
+       andlw   0fh                     ; and mask - this results in a zero result when finished receiving\r
+       bnz     IRQINTRecvIncomplete    ; Packet not ready yet\r
+\r
+; Here a packet is actually received, should check the checksum(s) as well, but I don't care right now\r
+; (I need music in my car! :))\r
+; This is inefficient, I know, I'll improve it later... (Not that it matters, there's plenty of time here\r
+; (there won't be any more communication for at least another 4.8ms))\r
+\r
+; Unilink command parser:\r
+\r
+; Check for CMD1 = 01h (System bus commands)\r
+       movf    UnilinkCMD1,w\r
+       xorlw   01h\r
+       bnz     IRQINTParseNot01\r
+\r
+; Check for 01 00 (Bus Re-Initialization)\r
+       movf    UnilinkCMD2,w\r
+       bnz     IRQINTParseNot0100\r
+\r
+       call    ClearUnilinkStatus      ; Clear everything Unilink (ID, BUSON_OUT)\r
+\r
+       goto    IRQINTParseComplete     ; Don't send any reply to this (clear the packet buffer though)\r
+\r
+IRQINTParseNot0100\r
+\r
+; Check for 01 02 (Anyone)\r
+       movf    UnilinkCMD2,w\r
+       xorlw   02h\r
+       bnz     IRQINTParseNot0102\r
+\r
+       movf    UnilinkID,w             ; Do I have an ID already?\r
+       bnz     IRQINTParseNot0102      ; Yep, I don't want another one!\r
+\r
+;      clrf    UnilinkParity1\r
+       call    ClearUnilinkBuffer      ; Zero it out completely\r
+\r
+       movlw   10h                     ; Sending to Master\r
+       addwf   UnilinkParity1,f\r
+       movwf   UnilinkRAD\r
+       movlw   0d0h                    ; I'm in the MD changer group\r
+       addwf   UnilinkParity1,f\r
+       movwf   UnilinkTAD\r
+       movlw   8ch                     ; Device discovery command reply\r
+       addwf   UnilinkParity1,f\r
+       movwf   UnilinkCMD1\r
+       movlw   10h                     ; 00??\r
+       addwf   UnilinkParity1,f\r
+       movwf   UnilinkCMD2\r
+\r
+       movf    UnilinkParity1,w\r
+       movwf   UnilinkParity2M\r
+\r
+       movlw   24h                     ; My internal MD sends 25 here first time, and then 24 when appointed!??\r
+       addwf   UnilinkParity2M,f\r
+       movwf   UnilinkData1\r
+       movlw   0a8h                    ; 2c??\r
+       addwf   UnilinkParity2M,f\r
+       movwf   UnilinkData2\r
+       movlw   17h                     ; 22??\r
+       addwf   UnilinkParity2M,f\r
+       movwf   UnilinkData3\r
+       movlw   0a0h                    ; 00?? 0a0=10 disc?\r
+       addwf   UnilinkParity2M,f\r
+       movwf   UnilinkData4\r
+\r
+;        clrf  UnilinkData6\r
+       goto    IRQINTParseBypassClear  ; Don't clear the data, the buffer will be sent as the next packet\r
+\r
+IRQINTParseNot0102\r
+\r
+; Check for 01 12 (Time poll)\r
+       movf    UnilinkCMD2,w\r
+       xorlw   12h\r
+       bnz     IRQINTParseNot0112\r
+\r
+       movf    UnilinkRAD,w\r
+       xorwf   UnilinkID,w             ; Is it for me?\r
+       bnz     IRQINTParseNot0112      ; Nope\r
+\r
+;      clrf    UnilinkParity1\r
+       call    ClearUnilinkBuffer\r
+       movlw   10h                     ; Sending to Master\r
+       addwf   UnilinkParity1,f\r
+       movwf   UnilinkRAD\r
+       movf    UnilinkID,w             ; This is my ID\r
+       addwf   UnilinkParity1,f\r
+       movwf   UnilinkTAD\r
+       movlw   00h\r
+       addwf   UnilinkParity1,f\r
+       movwf   UnilinkCMD1\r
+\r
+       movlw   80h                     ; Idle unless selected\r
+       btfsc   UnilinkSelected,7       \r
+       clrw\r
+       \r
+       addwf   UnilinkParity1,f\r
+       movwf   UnilinkCMD2\r
+;        clrf  UnilinkData1\r
+       goto    IRQINTParseBypassClear  ; Don't clear the data, the buffer will be sent as the next packet\r
+\r
+IRQINTParseNot0112\r
+\r
+; Check for 01 13 (Request Time poll)\r
+       movf    UnilinkCMD2,w\r
+       xorlw   13h\r
+       bnz     IRQINTParseNot0113\r
+\r
+       movf    UnilinkRAD,w\r
+       xorwf   UnilinkID,w             ; Is it for me?\r
+       bnz     IRQINTParseNot0113      ; Nope\r
+\r
+       btfss   DisplayStatus,7         ; If not displaying, skip this\r
+       goto    IRQINTParseComplete\r
+\r
+;      clrf    UnilinkParity1\r
+       call    ClearUnilinkBuffer\r
+       movlw   70h                     ; Sending to Display Group\r
+       addwf   UnilinkParity1,f\r
+       movwf   UnilinkRAD\r
+       movf    UnilinkID,w             ; This is my ID\r
+       addwf   UnilinkParity1,f\r
+       movwf   UnilinkTAD\r
+       movlw   90h\r
+       addwf   UnilinkParity1,f\r
+       movwf   UnilinkCMD1\r
+       movlw   50h\r
+       addwf   UnilinkParity1,f\r
+       movwf   UnilinkCMD2\r
+\r
+       movf    UnilinkParity1,w        ; Carry the parity forward\r
+       movwf   UnilinkParity2M\r
+\r
+;      movlw   01h\r
+       movf    DisplayStatus,w\r
+       addwf   UnilinkParity2M,f\r
+       movwf   UnilinkData1\r
+       movlw   00h\r
+       addwf   UnilinkParity2M,f\r
+       movwf   UnilinkData2\r
+       movlw   01h\r
+       addwf   UnilinkParity2M,f\r
+       movwf   UnilinkData3\r
+;      movlw   0c0h\r
+       movf    DisplayStatus,w\r
+       andlw   0f0h\r
+       addwf   UnilinkParity2M,f\r
+       movwf   UnilinkData4\r
+       \r
+;        clrf  UnilinkData6\r
 \r
-IRQAfterINT\r
+       incf    DisplayStatus,f         ; Temporary debug info\r
+       bsf     DisplayStatus,7\r
+\r
+       goto    IRQINTParseBypassClear  ; Don't clear the data, the buffer will be sent as the next packet\r
+\r
+IRQINTParseNot0113\r
+\r
+; Check for 01 15 (Who sent the slave break?)\r
+       movf    UnilinkCMD2,w\r
+       xorlw   15h\r
+       bnz     IRQINTParseNot0115\r
+\r
+       btfss   DisplayStatus,7         ; First of all check if there should be anything displayed\r
+       goto    IRQINTParseComplete     ; No, not at this time\r
+       \r
+;      clrf    UnilinkParity1\r
+       call    ClearUnilinkBuffer\r
+       movlw   10h                     ; Sending to Master\r
+       addwf   UnilinkParity1,f\r
+       movwf   UnilinkRAD\r
+       movlw   18h                     ; Broadcast address sending in this special case\r
+       addwf   UnilinkParity1,f\r
+       movwf   UnilinkTAD\r
+       movlw   82h                     ; Who wants to talk reply command\r
+       addwf   UnilinkParity1,f\r
+       movwf   UnilinkCMD1\r
+\r
+       clrw\r
+       call    Bit_Frig\r
+       addwf   UnilinkParity1,f\r
+       movwf   UnilinkCMD2\r
+\r
+       movf    UnilinkParity1,w        ; Carry the parity forward\r
+       movwf   UnilinkParity2M\r
+\r
+       movlw   20h\r
+       call    Bit_Frig\r
+       addwf   UnilinkParity2M,f\r
+       movwf   UnilinkData1\r
+       movlw   40h\r
+       call    Bit_Frig\r
+       addwf   UnilinkParity2M,f\r
+       movwf   UnilinkData2\r
+       movlw   60h\r
+       call    Bit_Frig\r
+       addwf   UnilinkParity2M,f\r
+       movwf   UnilinkData3\r
+       movlw   80h\r
+       call    Bit_Frig\r
+       addwf   UnilinkParity2M,f\r
+       movwf   UnilinkData4\r
+\r
+;      clrf    UnilinkData6\r
+\r
+       goto    IRQINTParseBypassClear  ; Don't clear the data, the buffer will be sent as the next packet\r
+\r
+;******************************************************************************\r
+; Bit frig - works out which bit to set in the response to Master Poll\r
+\r
+; W register is input of which stage you are on (0x00, 0x20, 0x30, 0x40 etc)\r
+; and is returned with the byte to write (0x00 if wrong stage).\r
+\r
+Bit_Frig:\r
+       xorwf   UnilinkBit, 0\r
+       andlw   0xe0                            ; Strip off low bits\r
+\r
+       btfsc   STATUS, Z                       ; Do we have a hit?\r
+       goto    Bit_Frig_Hit\r
+\r
+       movlw   0x00\r
+       return\r
+\r
+Bit_Frig_Hit:\r
+       btfss   UnilinkBit, 4                   ; Do we need to swap nybbles?\r
+       goto    Bit_Frig_Swap\r
+\r
+       movf    UnilinkBit, 0\r
+       andlw   0x0F\r
+       return\r
+\r
+Bit_Frig_Swap:\r
+       swapf   UnilinkBit, 0\r
+       andlw   0xF0\r
+       return\r
+\r
+IRQINTParseNot0115\r
+\r
+IRQINTParseNot01\r
+\r
+; Check for CMD1 = 02h (Appoint)\r
+       movf    UnilinkCMD1,w\r
+       xorlw   02h\r
+       bnz     IRQINTParseNot02\r
+\r
+       bsf     BUSON_OUT_BIT           ; Now activate the cascade BUSON pin, to allow others to be discovered\r
+\r
+       movf    UnilinkRAD,w            ; Get the ID the master has given me\r
+       movwf   UnilinkID               ; Store my id\r
+       movf    UnilinkCMD2,w           ; Get the bitmask\r
+       movwf   UnilinkBit              ; And store it (this is needed when doing slave breaks and actually responding)\r
+\r
+;      clrf    UnilinkParity1\r
+       call    ClearUnilinkBuffer\r
+       movlw   10h                     ; Sending to Master\r
+       addwf   UnilinkParity1,f\r
+       movwf   UnilinkRAD\r
+       movf    UnilinkID,w             ; This is my ID\r
+       addwf   UnilinkParity1,f\r
+       movwf   UnilinkTAD\r
+       movlw   8ch                     ; Device discovery command again\r
+       addwf   UnilinkParity1,f\r
+       movwf   UnilinkCMD1\r
+       movlw   10h\r
+       addwf   UnilinkParity1,f\r
+       movwf   UnilinkCMD2\r
+\r
+       movf    UnilinkParity1,w\r
+       movwf   UnilinkParity2M         ; That's the parity when sending medium messages\r
+\r
+       movlw   24h\r
+       addwf   UnilinkParity2M,f\r
+       movwf   UnilinkData1\r
+       movlw   0a8h                    ; My internal MD sends 1c here... (external/internal difference)\r
+       addwf   UnilinkParity2M,f\r
+       movwf   UnilinkData2\r
+       movlw   17h\r
+       addwf   UnilinkParity2M,f\r
+       movwf   UnilinkData3\r
+       movlw   0a0h                    ; 0a0=10disc\r
+       addwf   UnilinkParity2M,f\r
+       movwf   UnilinkData4\r
 \r
-       bcf     INTCON,INTF             ; Clear our IRQ\r
+;        clrf  UnilinkData6\r
+       goto    IRQINTParseBypassClear  ; Don't clear the data, the buffer will be sent as the next packet\r
+\r
+IRQINTParseNot02\r
+\r
+; Check for CMD1 = 87h (Power control)\r
+       movf    UnilinkCMD1,w\r
+       xorlw   087h\r
+       bnz     IRQINTParseNot87\r
+\r
+; Test for power-on bit (it seems like bit 3 (0x08h) of CMD2 is set when the power is on)\r
+       btfsc   UnilinkCMD2,3\r
+       goto    IRQINTParse87PowerOn\r
+\r
+       bsf     RS232_RI_BIT            ; Set this to make RI pin go low (after RS-232 levels)\r
+       goto    IRQINTParseComplete\r
+\r
+IRQINTParse87PowerOn\r
+       bcf     RS232_RI_BIT            ; Clear this to make RI pin go high (waking the computer)\r
+       goto    IRQINTParseComplete\r
+\r
+IRQINTParseNot87\r
+\r
+; Check for CMD1 = f0h (Source Select)\r
+       movf    UnilinkCMD1,w\r
+       xorlw   0f0h\r
+       bnz     IRQINTParseNotF0\r
+\r
+       movf    UnilinkCMD2,w\r
+       xorwf   UnilinkID,w             ; Check if it's selecting me\r
+       bnz     IRQINTParseF0Deselect\r
+\r
+       bsf     UnilinkSelected,7       ; Now we're selected\r
+       bsf     DisplayStatus,7\r
+       goto    IRQINTParseComplete\r
+\r
+IRQINTParseF0Deselect\r
+\r
+       bcf     UnilinkSelected,7       ; Now we're de-selected\r
+       bcf     DisplayStatus,7\r
+       goto    IRQINTParseComplete\r
+\r
+IRQINTParseNotF0\r
+\r
+IRQINTParseComplete\r
+\r
+; The code ends up here when parsing is complete and it's not interested in sending any reply back to the master\r
+; (that's why we clear out all the packet buffer bytes)\r
+\r
+       call    ClearUnilinkBuffer\r
+\r
+IRQINTParseBypassClear\r
+\r
+       movlw   UnilinkRAD              ; Get the pointer to the first byte in the receive buffer\r
+       movwf   UnilinkTXRX             ; Store it - this way the next byte that gets received goes into RAD\r
+\r
+       clrf    UnilinkCmdLen           ; No command length while waiting for a new packet\r
+\r
+       \r
+IRQINTRecvIncomplete\r
+\r
+IRQINTRecvNullByte\r
+       movf    INDF,w\r
+       movwf   DataStore               ; Store it so the non-irq code can snoop\r
+\r
+IRQAfterINT\r
+       bcf     INTCON,INTF             ; Clear the IRQ source bit to re-enable INT interrupts again\r
 \r
 IRQNotINT\r
 \r
-       movf    IRQPCLATH,w\r
-       movwf   PCLATH          ; Restore PCLATH\r
+       btfss   PIR1,TMR2IF             ; Check if it's the TMR2 interrupt (0.5ms timing)\r
+       goto    IRQNotTMR2              ; No it's not, check the other sources\r
+\r
+; Slave break opportunity detection here - the logic works as follows:\r
+; Look for a data low period of at least 5 ms (10 loops)\r
+; Look for a data high period of at least 2 ms (4 loops)\r
+; If the Slave Break request bit has been set, issue a slave break by holding the data line low for 4ms (8 loops)\r
+; If a bit would be received (CLK activates) the packet handler automatically clears out the SlaveBreakState, which means start all over\r
+\r
+;      incf    Counter,f               ; Increment the general purpose counter\r
+\r
+       btfsc   SlaveBreakState,5       ; Check if already pulling the data line low\r
+       goto    IRQTMR2SlaveBreak\r
+\r
+       btfsc   SlaveBreakState,7       ; Looking for low or high data\r
+       goto    IRQTMR2HighData\r
+       btfss   DATA_BIT                ; Looking for a low data line, if it's low, increment state, if it's high, reset state\r
+       goto    IRQTMR2LowDataOK\r
+       clrf    SlaveBreakState         ; Got a high data line while waiting for a low one, reset state\r
+       goto    IRQAfterTMR2            ; Leave ISR\r
+\r
+IRQTMR2HighData\r
+       btfsc   DATA_BIT                ; Looking for a high data line, if it's high - increment state, otherwise wait\r
+       goto    IRQTMR2HighDataOK\r
+       movlw   080h\r
+       btfsc   SlaveBreakState,6       ; Test the "first time around" bit\r
+       clrw                            ; Not the beginning of the state, have to restart the entire thing now, not just this state\r
+       andwf   SlaveBreakState,f       ; Mask out the 1 upper control bits and restart this state\r
+       goto    IRQAfterTMR2\r
+\r
+IRQTMR2HighDataOK\r
+IRQTMR2LowDataOK\r
+       bsf     SlaveBreakState,6       ; Set the "first time around" bit\r
+       \r
+       movf    SlaveBreakState,w\r
+       andlw   1fh\r
+\r
+       btfss   SlaveBreakState,7       ; Checking whether it's low or high\r
+       goto    IRQTMR2FoundLow\r
+\r
+       xorlw   4                       ; It's high now, and if 4 periods have passed we can activate slave break\r
+       skpz\r
+       goto    IRQAfterTMR2\r
+\r
+; Issue slave break here\r
+\r
+       clrf    SlaveBreakState\r
+\r
+       incf    Counter,f\r
+\r
+       btfss   DisplayStatus,7         ; Only do this if high bit is set\r
+       goto    IRQAfterTMR2\r
+\r
+       movlw   20h\r
+       movwf   SlaveBreakState\r
+       bcf     DATA_BIT\r
+       bsf     STATUS,RP0\r
+       bcf     DATA_BIT\r
+       bcf     STATUS,RP0\r
+       goto    IRQAfterTMR2\r
+\r
+IRQTMR2FoundLow\r
+       xorlw   10\r
+       skpz\r
+       goto    IRQAfterTMR2\r
+       movlw   80h                     ; Prepare for state 2, looking for data line high\r
+       movwf   SlaveBreakState\r
+       goto    IRQAfterTMR2\r
+       \r
+IRQTMR2SlaveBreak\r
+       movf    SlaveBreakState,w\r
+       andlw   01fh\r
+       xorlw   8\r
+       skpz\r
+       goto    IRQAfterTMR2\r
+       bsf     STATUS,RP0\r
+       bsf     DATA_BIT\r
+       bcf     STATUS,RP0\r
+       clrf    SlaveBreakState\r
+\r
+IRQAfterTMR2\r
+       btfss   SlaveBreakState,4       ; Only increment to 0x10\r
+       incf    SlaveBreakState,f\r
+       bcf     PIR1,TMR2IF             ; Clear the IRQ source bit to re-enable TMR2 interrupts again\r
+\r
+IRQNotTMR2\r
+\r
+; Finally restore CPU state and return from the ISR\r
+\r
+; If I have to save the FSR in the beginning I also need to restore it here...\r
+\r
+;      movf    IRQPCLATH,w\r
+;      movwf   PCLATH                  ; Restore PCLATH\r
        swapf   IRQSTATUS,w\r
-       movwf   STATUS          ; Restore STATUS\r
+       movwf   STATUS                  ; Restore STATUS\r
        swapf   IRQW,f\r
-       swapf   IRQW,w          ; Restore W\r
-       retfie                  ; Interrupt return\r
+       swapf   IRQW,w                  ; Restore W\r
+       retfie                          ; Interrupt return\r
+\r
+;----------------------------------------------------------------\r
+; ClearUnilinkStatus - Zeroes out the Unilink state (used when initializing)\r
 \r
+ClearUnilinkStatus\r
 \r
+       clrf    UnilinkID               ; Clear the existing Unilink ID, if any\r
+       bcf     BUSON_OUT_BIT           ; Clear the cascade BUSON pin, not activated again until we have a new ID\r
+       clrf    DisplayStatus           ; No crazy display updates when resetting.. :)\r
+       clrf    UnilinkSelected         ; We're not selected anymore\r
 \r
-       subtitl "Main loop"\r
-       page\r
+       bsf     STATUS,RP0              ; Reg bank 1\r
+       bsf     DATA_BIT                ; Make sure data is tristated\r
+       bcf     STATUS,RP0              ; Reg bank 0\r
+\r
+       movlw   UnilinkRAD              ; Get the pointer to the first byte in the receive buffer\r
+       movwf   UnilinkTXRX             ; Store it - this way the next byte that gets received goes into RAD\r
+\r
+       clrf    UnilinkCmdLen           ; No command length while waiting for a new packet\r
 \r
+       return\r
+       \r
 ;----------------------------------------------------------------\r
-;  Data can be stored between here and 100h...\r
+; ClearUnilinkBuffer - Zeroes out the Unilink packet buffer\r
+\r
+ClearUnilinkBuffer\r
+\r
+; TODO: Replace this with an FSR access to save space and make the code neater\r
+       clrf    UnilinkRAD\r
+       clrf    UnilinkTAD\r
+       clrf    UnilinkCMD1\r
+       clrf    UnilinkCMD2\r
+       clrf    UnilinkParity1\r
+       clrf    UnilinkData1\r
+       clrf    UnilinkData2\r
+       clrf    UnilinkData3\r
+       clrf    UnilinkData4\r
+       clrf    UnilinkData5\r
+       clrf    UnilinkData6\r
+       clrf    UnilinkData7\r
+       clrf    UnilinkData8\r
+       clrf    UnilinkData9\r
+       clrf    UnilinkParity2\r
+       clrf    UnilinkZero\r
 \r
-StartUpText1\r
-       DT      "-WJ UniLink I/F-"\r
-StartUpText2\r
-       DT      "Code and design:"\r
-StartUpText3\r
-       DT      "**TCC of Yodel**"\r
+       return\r
 \r
-               \r
-LookUp  movwf   PCL             ; Go to it\r
+\r
+       subtitl "Main loop"\r
+       page\r
 \r
 ;----------------------------------------------------------------\r
-;  Main program begins here. [Called after bootloader, lcdinit and irqinit...]\r
+; Main program begins here. [Called after bootloader, lcdinit and irqinit...]\r
+; Here all other house keeping tasks are performed, like displaying info on the LCD.. \r
 \r
-       org     100h\r
 Main\r
+       movlw   high LookUp\r
+       movwf   PCLATH\r
 \r
-       bsf     STATUS,RP0\r
-       bsf     TXSTA,TXEN              ; Enable UART TX\r
-       bcf     STATUS,RP0              ; Back to bank 0\r
+       movlw   low StartUpText1        ; Show something on the LCD\r
+       call    TxLCD16B\r
 \r
-       bsf     RCSTA,SPEN              ; Enable serial port\r
-       bsf     RCSTA,CREN              ; Enable UART RX\r
+MainLoop\r
 \r
-retry\r
-       \r
-;      movlw   8                       ; Loop this many times\r
-;      movwf   DataCount\r
-\r
-;CLKWaitHigh\r
-;      btfsc   PORTA,4                 ; Check for RST\r
-;      goto    retry\r
-;      btfss   PORTC,2                 ; Check for BUSON\r
-;      goto    retry\r
-;      btfss   PORTB,0                 ; Wait for clock to go high\r
-;      goto    CLKWaitHigh\r
-;CLKWaitLow\r
-;      btfsc   PORTA,4                 ; Check for RST\r
-;      goto    retry\r
-;      btfss   PORTC,2                 ; Check for BUSON\r
-;      goto    retry\r
-;      btfsc   PORTB,0                 ; Wait for clock to go low\r
-;      goto    CLKWaitLow\r
-\r
-;      clrc                            ; Clear carry\r
-;      btfss   PORTC,3                 ; Test DATA\r
-;      setc                            ; Set carry if data is LOW (data is inverted!)\r
-;      rlf     DataStore,f             ; Shift it into our accumulator\r
-\r
-;      decfsz  DataCount,f             ; Loop once more perhaps?\r
-;      goto    CLKWaitHigh\r
-\r
-        bcf     LCD_RS_BIT     ;Command mode\r
-       movlw   80h             ;DisplayRam 0\r
+       bcf     LCD_RS_BIT              ; LCD Command mode\r
+       movlw   80h                     ; DisplayRam 0\r
        call    TxLCDB\r
-        bsf     LCD_RS_BIT\r
+       bsf     LCD_RS_BIT\r
 \r
-       movlw   '0'\r
-       btfsc   PORTA,4         ; Test RST\r
+;      movlw   '0'\r
+       movf    Counter,w               ; Debug timer\r
+       btfsc   PORTA,4                 ; Test RST\r
        movlw   'R'\r
        call    TxLCDB\r
 \r
-       movlw   '0'\r
-       btfsc   PORTB,0         ; Test CLK\r
+;      movlw   '0'\r
+       movf    SlaveBreakState,w\r
+       andlw   80h\r
+       btfsc   PORTB,0                 ; Test CLK\r
        movlw   'C'\r
        call    TxLCDB\r
 \r
        movlw   '0'\r
-       btfsc   PORTC,2         ; Test BUSON-IN\r
+       btfsc   PORTC,2                 ; Test BUSON-IN\r
        movlw   'B'\r
        call    TxLCDB\r
 \r
        movlw   '0'\r
-       btfsc   PORTC,3         ; Test DATA\r
+       btfsc   PORTC,3                 ; Test DATA\r
        movlw   'D'\r
        call    TxLCDB\r
 \r
+       movf    UnilinkCmdLen,w\r
+       bz      MainDontPrintCmd\r
+       addlw   '0'\r
+       call    TxLCDB\r
+\r
+MainDontPrintCmd\r
+\r
        movf    DataCount,w             ; Load bit counter (if 0 then byte is available)\r
        skpz\r
-       goto    retry\r
+       goto    MainLoop\r
 \r
-       movf    DataStore,w             ; Get the result\r
        decf    DataCount,f             ; Set it non-zero\r
 \r
+       movf    DataStore,w\r
        call    BootTXB                 ; Send to terminal\r
-\r
-       goto    retry\r
-\r
+       goto    MainLoop\r
 \r
 \r
-       movlw   StartUpText1\r
-       call    TxLCD16B\r
-       call    LongDelay\r
-\r
-       bsf     PORTA,4         ; turn off LED\r
-\r
-       movlw   StartUpText2\r
-       call    TxLCD16B\r
-       call    LongDelay\r
+;----------------------------------------------------------------\r
+; IRQInit - Sets up the IRQ Handler\r
+; Set up Timer2 to generate 2000 interrupts per second, used for timing - 1/16 prescaler and a PR2 reg of 156 (0x9c) is set\r
 \r
-       bcf     PORTA,4         ; turn on LED\r
+IRQInit\r
 \r
-       movlw   StartUpText3\r
-       call    TxLCD16B\r
-       call    LongDelay\r
+; Start with clearing the Unilink packet buffer before enabling any interrupts, otherwise the first packet might become corrupt\r
+; TODO: Replace this with FSR access\r
+       clrf    UnilinkSelected\r
+       clrf    DisplayStatus\r
+       clrf    UnilinkID\r
+       clrf    UnilinkBit\r
+       clrf    UnilinkCmdLen\r
+       clrf    UnilinkRAD\r
+       clrf    UnilinkTAD\r
+       clrf    UnilinkCMD1\r
+       clrf    UnilinkCMD2\r
+       clrf    UnilinkParity1\r
+       clrf    UnilinkData1\r
+       clrf    UnilinkData2\r
+       clrf    UnilinkData3\r
+       clrf    UnilinkData4\r
+       clrf    UnilinkData5\r
+       clrf    UnilinkData6\r
+       clrf    UnilinkData7\r
+       clrf    UnilinkData8\r
+       clrf    UnilinkData9\r
+       clrf    UnilinkParity2\r
+       clrf    UnilinkZero\r
+\r
+       clrf    DataStore\r
+       movlw   UnilinkRAD              ; Get the pointer to the first byte in the receive buffer\r
+       movwf   UnilinkTXRX             ; Store it\r
+\r
+       clrf    SlaveBreakState         ; Zero out the status, we're starting from the beginning\r
+\r
+; Fix the output state of RI and BUSON_OUT to a safe default\r
+\r
+       bsf     RS232_RI_BIT            ; RS232 RI should be inactive (inverted logic, a set bit here gives a negative output)\r
+       bcf     BUSON_OUT_BIT           ; BUSON_OUT should be disabled for now, must be appointed first\r
+\r
+       movlw   06h                     ; Timer2 enabled + 1/16 prescaler\r
+       movwf   T2CON\r
 \r
-       goto    retry\r
+       bsf     STATUS,RP0              ; Reg bank 1\r
 \r
+       movlw   09ch                    ; Timer PR2 reg giving 2000 interrupts per second\r
+       movwf   PR2\r
 \r
-;----------------------------------------------------------------\r
-; IRQInit - Sets up the IRQ Handler\r
+       bcf     RS232_RI_BIT            ; Both bits should be outputs\r
+       bcf     BUSON_OUT_BIT           ;\r
 \r
-IRQInit\r
-       bsf     STATUS,RP0              ; Reg bank 1\r
+; The default behavior of RB0/INT is to interrupt on the rising edge, that's what we use...\r
 ;      bcf     OPTION_REG,INTEDG       ; We want RB0 to give us an IRQ on the falling edge\r
+\r
        bsf     INTCON,INTE             ; Enable the RB0/INT\r
+       bsf     INTCON,PEIE             ; Enable the peripheral interrupts\r
+       bsf     PIE1,TMR2IE             ; Enable the Timer2 peripheral interrupt\r
        bsf     INTCON,GIE              ; Enable global interrupts\r
+\r
+       bsf     TXSTA,TXEN              ; Enable UART TX\r
+\r
        bcf     STATUS,RP0              ; Back to bank 0\r
+\r
+       bsf     RCSTA,SPEN              ; Enable serial port\r
+       bsf     RCSTA,CREN              ; Enable UART RX\r
+\r
        return\r
 \r
 ;----------------------------------------------------------------\r
 ;  Initialize LCD Controller...\r
 \r
 LCDInit\r
-        clrf   PORTB\r
-        bsf     STATUS,RP0      ; Hi Bank\r
-        movlw   0cfh            ; RC4 & RC5 should be outputs...\r
-        movwf   TRISC           ; Yep.\r
-        movlw   001h            ; All but RB0 are outputs.\r
-        movwf   TRISB           ; Yep\r
-        bcf     OPTION_REG,NOT_RBPU     ; Turn on port B pull-up\r
-        bcf     STATUS,RP0      ; Restore Lo Bank\r
-\r
-;      bcf     PORTA,4         ; turn on LED\r
-\r
-;      movlw   44              ; Should be 16ms delay\r
-       movlw   255             ; Should be 16ms delay\r
+       clrf    PORTB                   ; First clear PortB data register\r
+       bsf     STATUS,RP0              ; Reg bank 1\r
+       movlw   001h                    ; All but RB0 are outputs.\r
+       movwf   TRISB                   ;\r
+\r
+       bcf     OPTION_REG,NOT_RBPU     ; Turn on port B pull-up\r
+       bcf     STATUS,RP0              ; Restore Reg bank 0\r
+\r
+; This is a standard reset sequence for the LCD controller\r
+\r
+       movlw   160                     ; Need to delay for at least 15ms, let's go for 16ms delay\r
        call    DelayW\r
 \r
-       movlw   3               ; Write 3 to the LCD\r
-       call    TxLCD           ; Send to LCD\r
-;      movlw   12              ; Should be 5ms delay\r
-       movlw   255             ; Should be 16ms delay\r
+       movlw   3                       ; Write 3 to the LCD\r
+       call    TxLCD                   ; Send to LCD\r
+       movlw   50                      ; Need to delay for at least 4.1ms, let's go for 5ms delay\r
        call    DelayW\r
 \r
-       movlw   3               ; Write 3 to the LCD\r
+       movlw   3                       ; Write 3 to the LCD\r
        call    TxLCD\r
-;      movlw   12              ; Should be 16ms delay\r
-       movlw   255             ; Should be 16ms delay\r
+       movlw   10                      ; Need to delay for at least 100us, let's go for 1ms delay\r
        call    DelayW\r
 \r
-       movlw   3               ; Write 3 to the LCD\r
+       movlw   3                       ; Write 3 to the LCD\r
        call    TxLCD\r
-;      movlw   44\r
-       movlw   255             ; Should be 16ms delay\r
+       movlw   10                      ; Need to delay for at least 40us, let's go for 1ms delay\r
        call    DelayW\r
 \r
-       movlw   2               ;\\r
-       call    TxLCD           ; | 4-bit interface\r
-;      movlw   55              ; | After this we are ready to ROCK!\r
-       movlw   255             ; Should be 16ms delay\r
-       call    DelayW          ;/\r
+       movlw   2                       ; 4-bit interface requested\r
+       call    TxLCD                   ;\r
+       movlw   10                      ; Need to delay for at least 40us, let's go for 1ms delay\r
+       call    DelayW                  ;\r
 \r
-;      bsf     PORTA,4         ; turn off LED\r
+; Reset sequence ends here\r
+; From this point no delays are needed, now the BUSY bit is valid and the bus I/F is 4 bits\r
 \r
-       movlw 28h               ; Some random commands :)))\r
-       call TxLCDB\r
+       movlw   28h                     ; Function Select + 4-bit bus + 2-line display\r
+       call    TxLCDB\r
 \r
-       movlw 0ch               ; hmmm\r
-       call TxLCDB\r
+       movlw   0ch                     ; Display Control + LCD On (No cursor)\r
+       call    TxLCDB\r
 \r
-       movlw 01h               ; hmmm\r
-       call TxLCDB\r
+       movlw   01h                     ; Clear Display\r
+       call    TxLCDB\r
 \r
-       movlw 06h               ; hmmm\r
-       call TxLCDB\r
+       movlw   06h                     ; Auto Increment cursor position\r
+       call    TxLCDB\r
        \r
        return\r
    \r
 ;----------------------------------------------------------------\r
-; LongDelay - Well, guess that for yourself...\r
-\r
-LongDelay\r
-;   btfss PORTB,6        ; Talk to da PC?\r
-;   goto PCTalk          ; Oh yeah...\r
-\r
-   movlw 255\r
-   call DelayW\r
-   movlw 255\r
-   call DelayW\r
-   movlw 255\r
-   call DelayW\r
-   movlw 255\r
-   call DelayW\r
-   movlw 255\r
-   call DelayW\r
-   movlw 255\r
-   call DelayW\r
-   movlw 255\r
-   call DelayW\r
-   movlw 255\r
-   call DelayW\r
-   movlw 255\r
-   call DelayW\r
-   movlw 255\r
-   call DelayW\r
-   movlw 255\r
-   call DelayW\r
-   movlw 255\r
-   call DelayW\r
-   movlw 255\r
-   call DelayW\r
-   movlw 255\r
-   call DelayW\r
-   movlw 255\r
-   call DelayW\r
-   movlw 255\r
-   call DelayW\r
-   return\r
-\r
-;----------------------------------------------------------------\r
 ;  TxLCD16B\r
 ;  Send a string to the LCD.\r
 \r
 TxLCD16B\r
        movwf   Icount\r
-        bcf     LCD_RS_BIT\r
-       movlw   80h             ;DisplayRam 0\r
+       bcf     LCD_RS_BIT\r
+       movlw   80h                     ; DisplayRam 0\r
        call    TxLCDB\r
-        bsf     LCD_RS_BIT\r
+       bsf     LCD_RS_BIT\r
        call    TxLCD8B\r
-        bcf     LCD_RS_BIT\r
-       movlw   80h+40          ;DisplayRam 40 (row 2)\r
+       bcf     LCD_RS_BIT\r
+       movlw   80h+40                  ; DisplayRam 40 (row 2)\r
        call    TxLCDB\r
-        bsf     LCD_RS_BIT\r
+       bsf     LCD_RS_BIT\r
        call    TxLCD8B\r
        return\r
 \r
@@ -397,14 +1027,14 @@ TxLCD16B
 ;  Send a string to the LCD.\r
 \r
 TxLCD8B\r
-;      movwf   Icount          ; Icount = W\r
+;      movwf   Icount                  ; Icount = W\r
        movlw   8\r
-       movwf   e_LEN           ; Move to e_LEN\r
+       movwf   e_LEN                   ; Move to e_LEN\r
 \r
-Txm_lp movf    Icount,w        ; get the byte\r
+Txm_lp movf    Icount,w                ; get the byte\r
        call    LookUp\r
-       incf    Icount,f        ; ...else ++Icount (table index)\r
-       call    TxLCDB          ; Send out the byte\r
+       incf    Icount,f                ; ...else ++Icount (table index)\r
+       call    TxLCDB                  ; Send out the byte\r
        decfsz  e_LEN,f\r
        goto    Txm_lp\r
        return\r
@@ -413,39 +1043,39 @@ Txm_lp   movf    Icount,w        ; get the byte
 ; TxLCDB - send a byte to the LCD\r
 \r
 TxLCDB\r
-       movwf   TxTemp          ; Store byte to send for a while...\r
+       movwf   TxTemp                  ; Store byte to send for a while...\r
 \r
-       bcf     temp,0          ; Clear my temp bit\r
-       btfss   LCD_RS_BIT      ; Check if we try the correct reg\r
+       bcf     temp,0                  ; Clear my temp bit\r
+       btfss   LCD_RS_BIT              ; Check if we try the correct reg\r
        goto    RxNoProb\r
        bcf     LCD_RS_BIT\r
-       bsf     temp,0          ; Indicate RS change\r
+       bsf     temp,0                  ; Indicate RS change\r
 RxNoProb\r
 \r
 NotReady\r
-       call    RxLCDB          ; Receive byte from LCD, status reg\r
+       call    RxLCDB                  ; Receive byte from LCD, status reg\r
        andlw   80h\r
-       btfss   STATUS,Z        ; If the bit was set, the zero flag is not\r
+       skpz                            ; If the bit was set, the zero flag is not\r
        goto    NotReady\r
 \r
-       btfsc   temp,0          ; If we had to clear RS reset it now\r
+       btfsc   temp,0                  ; If we had to clear RS reset it now\r
        bsf     LCD_RS_BIT\r
 \r
-       swapf   TxTemp,w        ; Hi nibble of data to send in lo w bits\r
-       call    TxLCD           ; Send them first...\r
-       movf    TxTemp,w        ; Then we have the low nibble in low w bits\r
-       call    TxLCD           ; And send that one as well\r
+       swapf   TxTemp,w                ; Hi nibble of data to send in lo w bits\r
+       call    TxLCD                   ; Send them first...\r
+       movf    TxTemp,w                ; Then we have the low nibble in low w bits\r
+       call    TxLCD                   ; And send that one as well\r
 \r
        return\r
 ;----------------------------------------------------------------\r
 ; RxLCDB - recv a byte from the LCD\r
 \r
 RxLCDB\r
-       call    RxLCD           ; Receive the high nibble\r
+       call    RxLCD                   ; Receive the high nibble\r
        movwf   LCDWTmp\r
-       swapf   LCDWTmp,f       ; Swap it back to file\r
-       call    RxLCD           ; Receive the low nibble\r
-       addwf   LCDWTmp,w       ; Put the nibbles together and return in W\r
+       swapf   LCDWTmp,f               ; Swap it back to file\r
+       call    RxLCD                   ; Receive the low nibble\r
+       addwf   LCDWTmp,w               ; Put the nibbles together and return in W\r
 \r
        return\r
 \r
@@ -453,92 +1083,113 @@ RxLCDB
 ; TxLCD - send a nibble to the LCD\r
 \r
 TxLCD\r
-       movwf   LCDWTmp         ; Write nibble to tmp\r
-       bcf     LCD_DB4_BIT     ; Clear previous data\r
-       bcf     LCD_DB5_BIT     ; \r
-       bcf     LCD_DB6_BIT     ;\r
-       bcf     LCD_DB7_BIT     ;\r
+       movwf   LCDWTmp                 ; Write nibble to tmp\r
+       bcf     LCD_DB4_BIT             ; Clear previous data\r
+       bcf     LCD_DB5_BIT             ; \r
+       bcf     LCD_DB6_BIT             ;\r
+       bcf     LCD_DB7_BIT             ;\r
 \r
-       btfsc   LCDWTmp,0       ; Test bit 0, transfer a set bit to LCD PORT\r
+       btfsc   LCDWTmp,0               ; Test bit 0, transfer a set bit to LCD PORT\r
        bsf     LCD_DB4_BIT\r
-       btfsc   LCDWTmp,1       ; Test bit 1, transfer a set bit to LCD PORT\r
+       btfsc   LCDWTmp,1               ; Test bit 1, transfer a set bit to LCD PORT\r
        bsf     LCD_DB5_BIT\r
-       btfsc   LCDWTmp,2       ; Test bit 2, transfer a set bit to LCD PORT\r
+       btfsc   LCDWTmp,2               ; Test bit 2, transfer a set bit to LCD PORT\r
        bsf     LCD_DB6_BIT\r
-       btfsc   LCDWTmp,3       ; Test bit 3, transfer a set bit to LCD PORT\r
+       btfsc   LCDWTmp,3               ; Test bit 3, transfer a set bit to LCD PORT\r
        bsf     LCD_DB7_BIT\r
 \r
-       bsf     LCD_E_BIT       ; And set E to clock the data into the LCD module\r
-       nop                     ; Let it settle\r
-        bcf     LCD_E_BIT       ; And clear the Enable again.\r
-       return                  ; Returns without modifying W\r
+       bsf     LCD_E_BIT               ; And set E to clock the data into the LCD module\r
+       nop                             ; Let it settle\r
+        bcf    LCD_E_BIT               ; And clear the Enable again.\r
+       return                          ; Returns without modifying W\r
 \r
 ;----------------------------------------------------------------\r
 ; RxLCD - recv a nibble from the LCD\r
 \r
 RxLCD\r
-       clrw                    ; Clear W register, return data in lower 4 bits\r
+       clrw                            ; Clear W register, return data in lower 4 bits\r
 \r
-        bsf     STATUS,RP0     ; Select 2nd reg bank, now TRIS regs can be accessed\r
+       bsf     STATUS,RP0              ; Select 2nd reg bank, now TRIS regs can be accessed\r
        \r
-       bsf     LCD_DB4_BIT     ; This sets the port bit as an input\r
+       bsf     LCD_DB4_BIT             ; This sets the port bit as an input\r
        bsf     LCD_DB5_BIT     \r
        bsf     LCD_DB6_BIT     \r
        bsf     LCD_DB7_BIT\r
-       bcf     STATUS,RP0      ; Back at reg bank 0    \r
 \r
-       bsf     LCD_RW_BIT      ; Set Read mode for the LCD\r
-       bsf     LCD_E_BIT       ; And set E to clock the data out of the LCD module\r
-       nop                     ; Let the bus settle\r
-       btfsc   LCD_DB4_BIT     ; Transfer a set port bit into W\r
+       bcf     STATUS,RP0              ; Back at reg bank 0    \r
+\r
+       bsf     LCD_RW_BIT              ; Set Read mode for the LCD\r
+       bsf     LCD_E_BIT               ; And set E to clock the data out of the LCD module\r
+       nop                             ; Let the bus settle\r
+       btfsc   LCD_DB4_BIT             ; Transfer a set port bit into W\r
        addlw   1\r
-       btfsc   LCD_DB5_BIT     ; Transfer a set port bit into W\r
+       btfsc   LCD_DB5_BIT             ; Transfer a set port bit into W\r
        addlw   2\r
-       btfsc   LCD_DB6_BIT     ; Transfer a set port bit into W\r
+       btfsc   LCD_DB6_BIT             ; Transfer a set port bit into W\r
        addlw   4\r
-       btfsc   LCD_DB7_BIT     ; Transfer a set port bit into W\r
+       btfsc   LCD_DB7_BIT             ; Transfer a set port bit into W\r
        addlw   8\r
-        bcf     LCD_E_BIT       ; And clear the Enable again.\r
-       bcf     LCD_RW_BIT      ; Set Write mode for the LCD\r
+        bcf    LCD_E_BIT               ; And clear the Enable again.\r
+       bcf     LCD_RW_BIT              ; Set Write mode for the LCD\r
+\r
+       bsf     STATUS,RP0              ; Select 2nd reg bank, now TRIS regs can be accessed\r
+\r
+       bcf     LCD_DB4_BIT             ; Set the port as an output again\r
+       bcf     LCD_DB5_BIT             ; \r
+       bcf     LCD_DB6_BIT             ;\r
+       bcf     LCD_DB7_BIT             ;\r
 \r
-        bsf     STATUS,RP0     ; Select 2nd reg bank, now TRIS regs can be accessed\r
-       bcf     LCD_DB4_BIT     ; Set the port as an output again\r
-       bcf     LCD_DB5_BIT     ; \r
-       bcf     LCD_DB6_BIT     ;\r
-       bcf     LCD_DB7_BIT     ;\r
-       bcf     STATUS,RP0      ; Back at reg bank 0    \r
+       bcf     STATUS,RP0              ; Back at reg bank 0    \r
 \r
-       return                  ; Returns with data in W\r
+       return                          ; Returns with data in W\r
 \r
 ;----------------------------------------------------------------------\r
-; Delay routines       (one iteration=3 cycles. That is 0.366211ms @32kHz)\r
-; 2.73* # of ms is good...\r
-\r
-DelayW movwf   Dcount          ; Set delay counter\r
-       clrf    Dcount2\r
-       decf    Dcount2,f\r
-DelayLp        decfsz  Dcount,f\r
-       goto    DelayIn\r
-       retlw   0\r
-DelayIn        decfsz  Dcount2,f\r
-       goto    DelayIn2\r
-       decf    Dcount2,f\r
-       goto    DelayLp\r
-DelayIn2       goto    $+1\r
-       goto    $+1\r
-       goto    $+1\r
-       goto    DelayIn\r
+; Delay routines (non-interrupt based, therefore not even close to reliable)\r
+; W=10 gives ~ 1ms of delay\r
+; 1ms=5000 instructions wasted, 100us=500 cycles\r
+; Maximum time waited will be 256*100us=25.6ms\r
+\r
+DelayW\r
+       movwf   Dcount                  ; Set delay counter, number of 100us periods to wait\r
+\r
+DelayOuter\r
+       movlw   0a5h                    ; This gives 165 iterations of the inner loop, wastes 495 cycles + these two + one more\r
+       movwf   Dcount2                 ; exiting the loop + 3 more for the outer loop = 501 cycles for every Dcount\r
+DelayInner\r
+       decfsz  Dcount2,f               ; 1 cycle (or two when exiting the loop)\r
+       goto    DelayInner              ; 2 cycles\r
+       decfsz  Dcount,f                ; Now decrement number of 100us periods and loop again\r
+       goto    DelayOuter\r
+       return\r
+\r
+\r
+;----------------------------------------------------------------\r
+;  Data can be stored between 600 and 6ffh...\r
+\r
+       org     600h\r
+StartUpText1\r
+       DT      "----- WJ UniLink"\r
+\r
+InfoText1\r
+       DT      "WJ UniLink      "               \r
+LookUp movwf   PCL                     ; Go to it (this assumes PCLATH == 06h)\r
+\r
 \r
        subtitl "Bootstrap/Bootloader code"\r
        page\r
 \r
 ;----------------------------------------------------------------------\r
-; Bootstrap code - Allows PIC to flash itself with data from async port\r
+; Bootstrap code - Allows PIC to flash itself with data from the async port.\r
+; Accepts a standard INHX8 encoded file as input, the only caveat is that the code is slow when writing to memory\r
+; (we have to wait for the flash to complete), and therefore care has to be taken not to overflow the RS232 receiver\r
+; (one good way of solving that is to wait for the echo from the PIC before sending anything else)\r
+; Both program memory and Data EEPROM memory can be programmed, but due to hardware contraints the configuration\r
+; register can't be programmed. That means that any references to the config register in the hex file will be ignored.\r
+;\r
 ; Startup @9600bps\r
 \r
-       org     700h                    ; Place the boot code at the top of memory\r
+; RAM usage for the bootstrap code\r
 \r
-BootRXState    equ     7fh             ; What are we waiting for @RX\r
 BootBits       equ     7eh             ; bit0 1=write 0=read, bit1 1=PGM 0=EE, bit2 0=normal 1=no-op when prog\r
 BootAddrL      equ     7dh\r
 BootAddrH      equ     7ch\r
@@ -551,12 +1202,10 @@ BootNumBytes     equ     76h
 BootDataVL     equ     75h\r
 BootDataVH     equ     74h\r
 BootHEXTemp    equ     73h\r
-BootStrTemp    equ     72h\r
 \r
-Bootstrap\r
-       movlw   7\r
-       movwf   PCLATH\r
+       org     738h                    ; Place the boot code at the top of memory (currently the loader is exactly 200 bytes)\r
 \r
+Bootstrap\r
        bsf     STATUS,RP0              ; Access bank 1\r
        bsf     TXSTA,TXEN              ; Enable UART TX\r
        movlw   31                      ; Divisor for 9k6 @ 20MHz Fosc\r
@@ -566,12 +1215,10 @@ Bootstrap
        bsf     RCSTA,SPEN              ; Enable serial port\r
        bsf     RCSTA,CREN              ; Enable UART RX\r
 \r
-;      clrf    BootRXState             ; Waiting for command\r
-\r
-       movlw   BootStartText\r
+       movlw   low BootStartText       ; Send boot banner to the serial port\r
        call    BootTXStr\r
 \r
-       movlw   0e8h\r
+       movlw   0e8h                    ; Initialize timeout timer\r
        movwf   BootTimerL\r
        movwf   BootTimerM\r
        movwf   BootTimerH\r
@@ -588,11 +1235,11 @@ BootTimeout
        goto    BootTimeout\r
        call    BootRXB\r
        xorlw   27                      ; ESC\r
-       btfss   STATUS,Z\r
-       goto    BootTimeout             ; If it wasn't space, wait for another key\r
+       skpz\r
+       goto    BootTimeout             ; If it wasn't ESC, wait for another key\r
 \r
 BootFlash\r
-       movlw   BootFlashText\r
+       movlw   low BootFlashText       ; OK, flashing it is, send "start" text to serial port\r
        call    BootTXStr\r
 \r
        bsf     BootBits,1\r
@@ -661,21 +1308,12 @@ BootWrite
        movf    BootDataVH,w\r
        movwf   BootDataH               ; Have to put the new H byte data in as well\r
 \r
-;      movlw   '+'\r
-;      call    BootTXB                 ; Send progword indicator\r
-\r
        bsf     BootBits,0\r
        call    BootEE                  ; Write directly into program mem\r
 \r
 ; Here a verify can take place, the read-back results are now in DataL/H\r
 \r
-       movlw   '+'\r
-       goto    BootWriteDone\r
-\r
 BootWriteSkip\r
-       movlw   '-'\r
-BootWriteDone\r
-       call    BootTXB\r
 \r
        incf    BootAddrL,f             ; Advance counter to next addr\r
        skpnz\r
@@ -684,30 +1322,24 @@ BootWriteDone
        decfsz  BootNumBytes,f\r
        goto    BootLineLoop\r
 \r
-       movlw   13                      ; Progress\r
-       call    BootTXB\r
-       movlw   10                      ; Progress\r
-       call    BootTXB\r
-\r
        goto    BootLoop\r
 \r
 BootFlashComplete\r
        \r
 BootReturn\r
-       movlw   BootRunText\r
+       movlw   low BootRunText\r
        call    BootTXStr\r
 \r
-       bsf     STATUS,RP0\r
+       bsf     STATUS,RP0              ; Reg bank 1\r
 BootReturnWait\r
        btfss   TXSTA,TRMT              ; Wait for last things to flush\r
        goto    BootReturnWait\r
        bcf     TXSTA,TXEN              ; Disable UART TX\r
        bcf     STATUS,RP0              ; Back to bank 0\r
 \r
-       bcf     RCSTA,SPEN              ; Enable serial port\r
-       bcf     RCSTA,CREN              ; Enable UART RX\r
+       bcf     RCSTA,SPEN              ; Disable serial port\r
+       bcf     RCSTA,CREN              ; Disable UART RX\r
 \r
-       clrf    PCLATH\r
        return                          ; Return to code        \r
 \r
 ;----------------------------------------------------------------------\r
@@ -725,14 +1357,23 @@ BootTXW1
 ; BootTXStr - Sends ASCII string pointed to by W, zero terminated\r
 \r
 BootTXStr\r
-       movwf   BootStrTemp             ; Store offset\r
-       call    BootLookup              ; Lookup char\r
-       addlw   0\r
-       skpnz\r
+       movwf   BootAddrL               ; Store LSB of text pointer\r
+       movlw   07h                     ; MSB of pointer to the text (0700h in this boot loader)\r
+       movwf   BootAddrH\r
+       movlw   02h                     ; Select "Read Program Memory" operation\r
+       movwf   BootBits        \r
+BootTXStrLoop\r
+       call    BootEE                  ; Lookup char (actually two packed into one word)\r
+       rlf     BootDataL,w             ; Shift the MSB out into carry (that's the 2nd char LSB)\r
+       rlf     BootDataH,w             ; Shift it into 2nd char\r
+       call    BootTXB                 ; Send the high byte first\r
+       movf    BootDataL,w             ; Get the low byte\r
+       andlw   07fh                    ; Mask of the highest bit\r
+       skpnz                           ; Stop if zero\r
        return\r
        call    BootTXB                 ; Send char\r
-       incf    BootStrTemp,w           ; Retrieve\r
-       goto    BootTXStr\r
+       incf    BootAddrL,f             ; Increment pointer\r
+       goto    BootTXStrLoop\r
 \r
 ;----------------------------------------------------------------------\r
 ; BootRXB - Receives one byte from the UART, waits if nothing available\r
@@ -742,6 +1383,7 @@ BootRXW1
        btfss   PIR1,RCIF               ; Wait for RX to complete\r
        goto    BootRXW1\r
        movf    RCREG,w                 ; Get the recvd byte\r
+       call    BootTXB                 ; Echo to terminal\r
        return\r
 \r
 ;----------------------------------------------------------------------\r
@@ -749,7 +1391,6 @@ BootRXW1
 \r
 BootRXHEXNibble\r
        call    BootRXB                 ; Receive nibble\r
-\r
        addlw   -'A'                    ; Convert from BCD to binary nibble\r
        skpc                            ; Test if if was 0-9 or A-F, skip if A-F\r
        addlw  'A' - 10 - '0'           ; It was numeric '0'\r
@@ -824,20 +1465,22 @@ BootEEX
 \r
        return\r
 \r
+; To produce compact code the end zero byte has to be in the LSB (that means an even number of chars in every string)\r
 BootStartText\r
-       DT      "WJBoot - press ESC to flash",0\r
+       DW      0x2bca,0x216f,0x37f4,0x102d,0x1070,0x3965,0x39f3,0x1045,0x29c3,0x1074,0x37a0,0x336c,0x30f3,0x3400\r
+;      DE      "WJBoot - press ESC to flash\x00"\r
 BootFlashText\r
-       DT      13,10,"Send INHX8 file now...",13,10,0\r
+       DW      0x068a,0x29e5,0x3764,0x1049,0x2748,0x2c38,0x1066,0x34ec,0x32a0,0x376f,0x3bae,0x172e,0x0680\r
+;      DE      "\r\nSend INHX8 file now...\r\x00"\r
 BootRunText\r
-       DT      13,10,"Exiting loader",13,10,0\r
+       DW      0x068a,0x22f8,0x34f4,0x34ee,0x33a0,0x366f,0x30e4,0x32f2,0x0680\r
+;      DE      "\r\nExiting loader\r\x00"\r
 \r
-BootLookup\r
-       movwf   PCL                     ; Go fetch the char data\r
 \r
 ;----------------------------------------------------------------------\r
 ; EE Data (64 bytes), located at 2100h\r
 \r
         org 2100h\r
-;        data 0f2h, 099h, 000h, 000h, 018h, 0a5h, 090h, 084h\r
+;      de      0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh\r
 \r
        END\r