v0.5 - Issues slave breaks seemingly without hickups (!)
[wj-unilink.git] / wj-uni.asm
index f2a186d..08ddc24 100644 (file)
@@ -1,4 +1,4 @@
-       title   "PIC16F870 Unilink(R) Interface by Werner Johansson (c) 2003"\r
+       title   "PIC16F870 Unilink(R) Interface by Werner Johansson (c) 2002-2003"\r
        subtitl "Definitions"\r
        list    c=150,P=16F870,R=DEC,F=inhx8m\r
         include "p16f870.inc"           ; Standard equates & Macros\r
@@ -25,6 +25,8 @@
 ;----------------------------------------------------------------\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
 ;----------------------------------------------------------------\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
-\r
-LCDWTmp        equ     30h\r
-Dcount2                equ     31h\r
-temp           equ     32h\r
-\r
-DataCount      equ     33h             ; Temp storage for the bit counter used during bit shifts (Unilink TX/RX)\r
-DataStore      equ     34h             ; This is a kludge\r
-\r
-UnilinkSelected        equ     3bh\r
-UnilinkBit     equ     3ch             ; This is my "bitmask" to be used for requests\r
-UnilinkID      equ     3dh             ; This is my Bus ID\r
-UnilinkCmdLen  equ     3eh             ; This gets updated with the actual packet length after CMD1 has been received\r
-UnilinkTXRX    equ     3fh             ; This is a pointer to the Unilink packet below, used with indirect addressing\r
-\r
-UnilinkRAD     equ     40h             ; Beginning of Unilink packet - the Receiving Address\r
-UnilinkTAD     equ     41h             ; Transmitter address\r
-UnilinkCMD1    equ     42h             ; CMD1 byte\r
-UnilinkCMD2    equ     43h             ; CMD2 byte\r
-UnilinkParity1 equ     44h             ; First or only parity byte for short packets (6 bytes)\r
-UnilinkData1   equ     45h             ; Extra data for medium/large packets, or zero for short packets\r
-UnilinkData2   equ     46h             ;\r
-UnilinkData3   equ     47h             ;\r
-UnilinkData4   equ     48h             ;\r
-UnilinkData5   equ     49h             ; Data5 if this is a large packet\r
-UnilinkParity2M        equ     49h             ; Parity2 shares the same byte if it's a medium sized packet\r
-UnilinkData6   equ     4ah             ; Extra data for large packets, or zero for medium packets\r
-UnilinkData7   equ     4bh             ;\r
-UnilinkData8   equ     4ch             ;\r
-UnilinkData9   equ     4dh             ;\r
-UnilinkParity2 equ     4eh             ; Parity byte for large packets\r
-UnilinkZero    equ     4fh             ; Should always be zero (possibly used to signal corrupt packets from slave to master?)\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
@@ -126,16 +213,17 @@ IRQW              equ     7fh             ;
 ;  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.\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
-       movf    PCLATH,w                ; Get the PCLATH reg\r
-       movwf   IRQPCLATH               ; And store it\r
-       clrf    PCLATH                  ; Go to low memory\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
@@ -208,6 +296,7 @@ IRQINTCLKWaitHigh
 ; 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
@@ -235,7 +324,7 @@ IRQINTRecvNotCMD1
        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 a packet, should check the checksum(s) as well, but I don't care right now\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
@@ -249,13 +338,11 @@ IRQINTRecvNotCMD1
 \r
 ; Check for 01 00 (Bus Re-Initialization)\r
        movf    UnilinkCMD2,w\r
-;      xorlw   00h\r
        bnz     IRQINTParseNot0100\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
+       call    ClearUnilinkStatus      ; Clear everything Unilink (ID, BUSON_OUT)\r
 \r
-       goto    IRQINTParseComplete     ; Don't send any reply to this\r
+       goto    IRQINTParseComplete     ; Don't send any reply to this (clear the packet buffer though)\r
 \r
 IRQINTParseNot0100\r
 \r
@@ -267,27 +354,39 @@ IRQINTParseNot0100
        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   00h                     ; 00??\r
+       movlw   10h                     ; 00??\r
+       addwf   UnilinkParity1,f\r
        movwf   UnilinkCMD2\r
-       movlw   6ch                     ; Hard coded parity (!)\r
-       movwf   UnilinkParity1\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   2ch                     ; 2c??\r
+       movlw   0a8h                    ; 2c??\r
+       addwf   UnilinkParity2M,f\r
        movwf   UnilinkData2\r
-       movlw   22h                     ; 22??\r
+       movlw   17h                     ; 22??\r
+       addwf   UnilinkParity2M,f\r
        movwf   UnilinkData3\r
-       movlw   00h                     ; 00??\r
+       movlw   0a0h                    ; 00?? 0a0=10 disc?\r
+       addwf   UnilinkParity2M,f\r
        movwf   UnilinkData4\r
-       movlw   0deh                    ; Hard coded parity 2 (!)\r
-       movwf   UnilinkData5\r
-        clrf   UnilinkData6\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
@@ -301,7 +400,8 @@ IRQINTParseNot0102
        xorwf   UnilinkID,w             ; Is it for me?\r
        bnz     IRQINTParseNot0112      ; Nope\r
 \r
-       clrf    UnilinkParity1\r
+;      clrf    UnilinkParity1\r
+       call    ClearUnilinkBuffer\r
        movlw   10h                     ; Sending to Master\r
        addwf   UnilinkParity1,f\r
        movwf   UnilinkRAD\r
@@ -318,11 +418,146 @@ IRQINTParseNot0102
        \r
        addwf   UnilinkParity1,f\r
        movwf   UnilinkCMD2\r
-        clrf   UnilinkData6\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
+       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
@@ -337,7 +572,8 @@ IRQINTParseNot01
        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
+;      clrf    UnilinkParity1\r
+       call    ClearUnilinkBuffer\r
        movlw   10h                     ; Sending to Master\r
        addwf   UnilinkParity1,f\r
        movwf   UnilinkRAD\r
@@ -347,7 +583,7 @@ IRQINTParseNot01
        movlw   8ch                     ; Device discovery command again\r
        addwf   UnilinkParity1,f\r
        movwf   UnilinkCMD1\r
-       movlw   00h\r
+       movlw   10h\r
        addwf   UnilinkParity1,f\r
        movwf   UnilinkCMD2\r
 \r
@@ -357,17 +593,17 @@ IRQINTParseNot01
        movlw   24h\r
        addwf   UnilinkParity2M,f\r
        movwf   UnilinkData1\r
-       movlw   2ch                     ; My internal MD sends 1c here... (external/internal difference)\r
+       movlw   0a8h                    ; My internal MD sends 1c here... (external/internal difference)\r
        addwf   UnilinkParity2M,f\r
        movwf   UnilinkData2\r
-       movlw   22h\r
+       movlw   17h\r
        addwf   UnilinkParity2M,f\r
        movwf   UnilinkData3\r
-       movlw   00h\r
+       movlw   0a0h                    ; 0a0=10disc\r
        addwf   UnilinkParity2M,f\r
        movwf   UnilinkData4\r
 \r
-        clrf   UnilinkData6\r
+;        clrf  UnilinkData6\r
        goto    IRQINTParseBypassClear  ; Don't clear the data, the buffer will be sent as the next packet\r
 \r
 IRQINTParseNot02\r
@@ -400,37 +636,23 @@ IRQINTParseNot87
        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 CPU ends up here when parsing is complete and it's not interested in sending any reply back to the master\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
-; TODO: Replace this with an FSR access to save space and make the code neater\r
 \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
+       call    ClearUnilinkBuffer\r
 \r
 IRQINTParseBypassClear\r
 \r
@@ -451,37 +673,164 @@ IRQAfterINT
 \r
 IRQNotINT\r
 \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
+;      movf    IRQPCLATH,w\r
+;      movwf   PCLATH                  ; Restore PCLATH\r
        swapf   IRQSTATUS,w\r
        movwf   STATUS                  ; Restore STATUS\r
        swapf   IRQW,f\r
        swapf   IRQW,w                  ; Restore W\r
        retfie                          ; Interrupt return\r
 \r
+;----------------------------------------------------------------\r
+; ClearUnilinkStatus - Zeroes out the Unilink state (used when initializing)\r
 \r
-       subtitl "Main loop"\r
-       page\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
+       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
-StartUpText1\r
-       DT      "----- WJ UniLink"\r
-               \r
-LookUp  movwf   PCL                    ; Go to it (this assumes PCLATH == 00h)\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
+       return\r
+\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                    ; Maybe not force this to a specific address later\r
 Main\r
+       movlw   high LookUp\r
+       movwf   PCLATH\r
 \r
-       movlw   StartUpText1            ; Show something on the LCD\r
+       movlw   low StartUpText1        ; Show something on the LCD\r
        call    TxLCD16B\r
 \r
 MainLoop\r
@@ -491,12 +840,15 @@ MainLoop
        call    TxLCDB\r
        bsf     LCD_RS_BIT\r
 \r
-       movlw   '0'\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
+;      movlw   '0'\r
+       movf    SlaveBreakState,w\r
+       andlw   80h\r
        btfsc   PORTB,0                 ; Test CLK\r
        movlw   'C'\r
        call    TxLCDB\r
@@ -531,12 +883,14 @@ MainDontPrintCmd
 \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
 IRQInit\r
 \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
@@ -561,13 +915,21 @@ IRQInit
        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
        bsf     STATUS,RP0              ; Reg bank 1\r
 \r
+       movlw   09ch                    ; Timer PR2 reg giving 2000 interrupts per second\r
+       movwf   PR2\r
+\r
        bcf     RS232_RI_BIT            ; Both bits should be outputs\r
        bcf     BUSON_OUT_BIT           ;\r
 \r
@@ -575,6 +937,8 @@ IRQInit
 ;      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
@@ -799,6 +1163,18 @@ DelayInner
        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
@@ -814,7 +1190,6 @@ DelayInner
 \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
@@ -827,14 +1202,10 @@ BootNumBytes     equ     76h
 BootDataVL     equ     75h\r
 BootDataVH     equ     74h\r
 BootHEXTemp    equ     73h\r
-BootStrTemp    equ     72h\r
 \r
-       org     700h                    ; Place the boot code at the top of memory\r
+       org     738h                    ; Place the boot code at the top of memory (currently the loader is exactly 200 bytes)\r
 \r
 Bootstrap\r
-       movlw   7\r
-       movwf   PCLATH                  ; Make sure the lookup code runs with the high PC bits set!\r
-\r
        bsf     STATUS,RP0              ; Access bank 1\r
        bsf     TXSTA,TXEN              ; Enable UART TX\r
        movlw   31                      ; Divisor for 9k6 @ 20MHz Fosc\r
@@ -844,8 +1215,6 @@ Bootstrap
        bsf     RCSTA,SPEN              ; Enable serial port\r
        bsf     RCSTA,CREN              ; Enable UART RX\r
 \r
-;      clrf    BootRXState             ; Waiting for command\r
-\r
        movlw   low BootStartText       ; Send boot banner to the serial port\r
        call    BootTXStr\r
 \r
@@ -879,12 +1248,11 @@ BootFlash
 \r
 BootLoop\r
        call    BootRXB                 ; First find the ':'\r
-       call    BootTXB                 ; Echo to terminal\r
        xorlw   ':'\r
        skpz\r
        goto    BootLoop                ; Loop until we find it!\r
 \r
-       call    BootRXHEX               ; Get one ASCII encoded byte (two chars) (this echoes automatically)\r
+       call    BootRXHEX               ; Get one ASCII encoded byte (two chars)\r
        movwf   BootNumBytes            ; This is the number of bytes to be programmed on the line\r
 ; Maybe clear cary here?\r
        rrf     BootNumBytes,f          ; Right shift because we're double addressing this 8-bit format\r
@@ -940,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
@@ -963,11 +1322,6 @@ 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
@@ -986,7 +1340,6 @@ BootReturnWait
        bcf     RCSTA,SPEN              ; Disable serial port\r
        bcf     RCSTA,CREN              ; Disable UART RX\r
 \r
-       clrf    PCLATH                  ; Go back to memory bank 0\r
        return                          ; Return to code        \r
 \r
 ;----------------------------------------------------------------------\r
@@ -1004,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
@@ -1021,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
@@ -1028,7 +1391,6 @@ BootRXW1
 \r
 BootRXHEXNibble\r
        call    BootRXB                 ; Receive nibble\r
-       call    BootTXB                 ; Echo to terminal\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
@@ -1103,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 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh\r
+;      de      0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh\r
 \r
        END\r