v0.2 - First attempt at responding to the Anyone command
authorWerner Johansson <wj@xnk.nu>
Sun, 2 Mar 2003 10:15:39 +0000 (02:15 -0800)
committerWerner Johansson <wj@xnk.nu>
Sun, 17 Oct 2010 00:31:45 +0000 (17:31 -0700)
Signed-off-by: Werner Johansson <wj@xnk.nu>

wj-uni.asm

index 5f47338..130df18 100644 (file)
@@ -1,6 +1,6 @@
-       title   "PIC16F870 Unilink Interface by Werner Johansson (c) 2003"\r
+       title   "PIC16F870 Unilink(R) Interface by Werner Johansson (c) 2003"\r
        subtitl "Definitions"\r
-       list    c=132,P=16F870,R=DEC,F=inhx8m\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
       __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
+;  BUSON OUT control isn't implemented\r
+;  No checksum checking is done on incoming packets\r
+;  Investigate whether we 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 Bus re-initialize command\r
+;  Implement lots of other Unilink commands\r
+\r
+;----------------------------------------------------------------\r
+;  HISTORY\r
 ;----------------------------------------------------------------\r
 ;  Version\r
 ;\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
+\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\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
+#define RS232_RI_BIT   PORTC,5\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\r
-DataStore      equ     34h\r
-\r
-IRQW           equ     7fh\r
-IRQSTATUS      equ     7eh\r
-IRQPCLATH      equ     7dh\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
+\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
        subtitl "Startup"\r
        page\r
 ;----------------------------------------------------------------\r
 ;  Power up/Reset starting point [den rulerar]\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
+;  In order to save one instruction cycle we put the actual code here directly instead of a goto instruction\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
+       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 us Bank0 all the time\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
+; 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 INT (CLK)\r
-       goto    IRQNotINT               ; Nope\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) 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
@@ -95,34 +142,56 @@ IRQPCLATH  equ     7dh
 ; 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
+; to catch this though.. Note that this piece of code does both TX and RX at the same time (in order to receive packets\r
+; one has to make sure that the packet buffer is zeroed out before entering 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 a\r
+; interrupt driven software based USART - by trigging the interrupt on the rising edge we buy us some extra time 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 this many times\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
        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 our accumulator\r
 \r
        decfsz  DataCount,f             ; Loop once more perhaps?\r
-       goto    CLKWaitHigh\r
+       goto    IRQINTCLKWaitHigh       ; Yes, again!\r
+       goto    IRQINTRecvDone          ; No we're 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
+;;;;;; 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,15 +199,220 @@ 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
+       movf    UnilinkTXRX,w           ; Find out which byte we got\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 we actually have received a packet, 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, we have plenty of time here\r
+; (there can't be any more communication for 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 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
+       movlw   10h                     ; Sending to Master\r
+       movwf   UnilinkRAD\r
+       movlw   0d0h                    ; I'm in the MD changer group\r
+       movwf   UnilinkTAD\r
+       movlw   8ch                     ; Device discovery command reply\r
+       movwf   UnilinkCMD1\r
+       movlw   00h                     ; 00??\r
+       movwf   UnilinkCMD2\r
+       movlw   6ch                     ; Hard coded parity (!)\r
+       movwf   UnilinkParity1\r
+       movlw   24h                     ; My internal MD sends 25 here first time, and then 24 when appointed!??\r
+       movwf   UnilinkData1\r
+       movlw   2ch                     ; 2c??\r
+       movwf   UnilinkData2\r
+       movlw   22h                     ; 22??\r
+       movwf   UnilinkData3\r
+       movlw   00h                     ; 00??\r
+       movwf   UnilinkData4\r
+       movlw   0deh                    ; Hard coded parity 2 (!)\r
+       movwf   UnilinkData5\r
+        clrf   UnilinkData6\r
+       goto    IRQINTParseBypassClear  ; We don't want to clear the data, we want to send what's in the buffer next time\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 us?\r
+       bnz     IRQINTParseNot0112      ; nope\r
+\r
+       clrf    UnilinkParity1\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                     ; We're idle unless selected\r
+       btfsc   UnilinkSelected,7       \r
+       clrw\r
+       \r
+       addwf   UnilinkParity1,f\r
+       movwf   UnilinkCMD2\r
+        clrf   UnilinkData6\r
+       goto    IRQINTParseBypassClear  ; We don't want to clear the data, we want to send!\r
+\r
+IRQINTParseNot0112\r
+\r
+IRQINTParseNot01\r
+\r
+; Check for CMD1 = 02h (Appoint)\r
+       movf    UnilinkCMD1,w\r
+       xorlw   02h\r
+       bnz     IRQINTParseNot02\r
+\r
+       movf    UnilinkRAD,w            ; Get the ID the master has given us\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
+       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   00h\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   2ch                     ; My internal MD sends 1c here... (external/internal or 1/10 disc difference?)\r
+       addwf   UnilinkParity2M,f\r
+       movwf   UnilinkData2\r
+       movlw   22h\r
+       addwf   UnilinkParity2M,f\r
+       movwf   UnilinkData3\r
+       movlw   00h\r
+       addwf   UnilinkParity2M,f\r
+       movwf   UnilinkData4\r
+\r
+        clrf   UnilinkData6\r
+       goto    IRQINTParseBypassClear  ; We don't want to clear the data, we want to send!\r
+\r
+IRQINTParseNot02\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 us\r
+       bnz     IRQINTParseF0Deselect\r
+\r
+       bsf     UnilinkSelected,7       ; Now we're selected\r
+       goto    IRQINTParseNotF0\r
+\r
+IRQINTParseF0Deselect\r
+\r
+       bcf     UnilinkSelected,7       ; Now we're de-selected\r
+       goto    IRQINTParseNotF0\r
+\r
+IRQINTParseNotF0\r
+\r
+; We end up here when parsing is complete and we're 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
+\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 as we're waiting for a new packet\r
 \r
-IRQAfterINT\r
+       \r
+IRQINTRecvIncomplete\r
 \r
-       bcf     INTCON,INTF             ; Clear our IRQ\r
+IRQINTRecvNullByte\r
+       movf    INDF,w\r
+       movwf   DataStore               ; Store it so our non-irq code can snoop\r
+\r
+IRQAfterINT\r
+       bcf     INTCON,INTF             ; Clear our IRQ source bit so we can receive new bits again\r
 \r
 IRQNotINT\r
 \r
+; Finally restore CPU state and return from the ISR\r
        movf    IRQPCLATH,w\r
        movwf   PCLATH          ; Restore PCLATH\r
        swapf   IRQSTATUS,w\r
@@ -148,7 +422,6 @@ IRQNotINT
        retfie                  ; Interrupt return\r
 \r
 \r
-\r
        subtitl "Main loop"\r
        page\r
 \r
@@ -156,12 +429,7 @@ IRQNotINT
 ;  Data can be stored between here and 100h...\r
 \r
 StartUpText1\r
-       DT      "-WJ UniLink I/F-"\r
-StartUpText2\r
-       DT      "Code and design:"\r
-StartUpText3\r
-       DT      "**TCC of Yodel**"\r
-\r
+       DT      "----- WJ UniLink"\r
                \r
 LookUp  movwf   PCL             ; Go to it\r
 \r
@@ -171,41 +439,53 @@ LookUp  movwf   PCL             ; Go to it
        org     100h\r
 Main\r
 \r
-       bsf     STATUS,RP0\r
+       bsf     RS232_RI_BIT    ; We want RI to be high (inverted logic, not set)\r
+       bcf     BUSON_OUT_BIT   ; But we don't want BUSON_OUT on just yet, we need to be appointed first\r
+\r
+       bsf     STATUS,RP0      ; Select bank 1\r
+\r
+       bcf     RS232_RI_BIT    ; Both bits should be outputs at least\r
+       bcf     BUSON_OUT_BIT   ;\r
+\r
+;      bcf     STATUS,RP0\r
+;      bsf     STATUS,RP0\r
+\r
        bsf     TXSTA,TXEN              ; Enable UART TX\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
+; Replace this with an FSR access\r
+       clrf    UnilinkSelected\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
+       movlw   StartUpText1\r
+       call    TxLCD16B\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
        call    TxLCDB\r
@@ -231,36 +511,41 @@ retry
        movlw   'D'\r
        call    TxLCDB\r
 \r
+       movf    UnilinkCmdLen,w\r
+       bz      DontPrintCmd\r
+       addlw   '0'\r
+       call    TxLCDB\r
+DontPrintCmd\r
+\r
        movf    DataCount,w             ; Load bit counter (if 0 then byte is available)\r
        skpz\r
        goto    retry\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
 \r
 \r
-       movlw   StartUpText1\r
-       call    TxLCD16B\r
-       call    LongDelay\r
+;      movlw   StartUpText1\r
+;      call    TxLCD16B\r
+;      call    LongDelay\r
 \r
-       bsf     PORTA,4         ; turn off LED\r
+;      bsf     PORTA,4         ; turn off LED\r
 \r
-       movlw   StartUpText2\r
-       call    TxLCD16B\r
-       call    LongDelay\r
+;      movlw   StartUpText2\r
+;      call    TxLCD16B\r
+;      call    LongDelay\r
 \r
-       bcf     PORTA,4         ; turn on LED\r
+;      bcf     PORTA,4         ; turn on LED\r
 \r
-       movlw   StartUpText3\r
-       call    TxLCD16B\r
-       call    LongDelay\r
+;      movlw   StartUpText3\r
+;      call    TxLCD16B\r
+;      call    LongDelay\r
 \r
-       goto    retry\r
+;      goto    retry\r
 \r
 \r
 ;----------------------------------------------------------------\r
@@ -280,8 +565,6 @@ IRQInit
 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