Initial v0.0 commit
authorWerner Johansson <wj@xnk.nu>
Thu, 20 Feb 2003 01:37:48 +0000 (17:37 -0800)
committerWerner Johansson <wj@xnk.nu>
Sun, 17 Oct 2010 00:05:58 +0000 (17:05 -0700)
Signed-off-by: Werner Johansson <wj@xnk.nu>

wj-uni.asm [new file with mode: 0644]

diff --git a/wj-uni.asm b/wj-uni.asm
new file mode 100644 (file)
index 0000000..ecb6d6a
--- /dev/null
@@ -0,0 +1,824 @@
+       title   "PIC16F870 Unilink Interface by Werner Johansson (c) 2003"\r
+       subtitl "Definitions"\r
+       list    c=132,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
+;----------------------------------------------------------------\r
+;  The Configuration Word\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
+;----------------------------------------------------------------\r
+;  Version\r
+;\r
+;  0.0  Very first "Fucking No Work!" version\r
+;\r
+;----------------------------------------------------------------\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
+#define        LCD_RS_BIT      PORTB,1\r
+#define        LCD_RW_BIT      PORTB,2\r
+#define        LCD_E_BIT       PORTB,3\r
+#define LCD_DB4_BIT    PORTB,4\r
+#define LCD_DB5_BIT    PORTB,5\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
+\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
+       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
+\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
+       movlw   8                       ; Loop this many times\r
+       movwf   DataCount\r
+\r
+CLKWaitHigh\r
+       btfsc   PORTA,4                 ; Check for RST\r
+       goto    IRQAfterINT\r
+       btfss   PORTC,2                 ; Check for BUSON\r
+       goto    IRQAfterINT\r
+       btfss   PORTB,0                 ; Wait for clock to go high\r
+       goto    CLKWaitHigh\r
+CLKWaitLow\r
+       btfsc   PORTA,4                 ; Check for RST\r
+       goto    IRQAfterINT\r
+       btfss   PORTC,2                 ; Check for BUSON\r
+       goto    IRQAfterINT\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
+IRQAfterINT\r
+\r
+       bcf     INTCON,INTF             ; Clear our IRQ\r
+\r
+IRQNotINT\r
+\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
+\r
+       subtitl "Main loop"\r
+       page\r
+\r
+;----------------------------------------------------------------\r
+;  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
+               \r
+LookUp  movwf   PCL             ; Go to it\r
+\r
+;----------------------------------------------------------------\r
+;  Main program begins here. [Called after bootloader, lcdinit and irqinit...]\r
+\r
+       org     100h\r
+Main\r
+\r
+       bsf     STATUS,RP0\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
+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
+        bsf     LCD_RS_BIT\r
+\r
+       movlw   '0'\r
+       btfsc   PORTA,4         ; Test RST\r
+       movlw   'R'\r
+       call    TxLCDB\r
+\r
+       movlw   '0'\r
+       btfsc   PORTB,0         ; Test CLK\r
+       movlw   'C'\r
+       call    TxLCDB\r
+\r
+       movlw   '0'\r
+       btfsc   PORTC,2         ; Test BUSON-IN\r
+       movlw   'B'\r
+       call    TxLCDB\r
+\r
+       movlw   '0'\r
+       btfsc   PORTC,3         ; Test DATA\r
+       movlw   'D'\r
+       call    TxLCDB\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
+       call    BootTXB                 ; Send to terminal\r
+\r
+       goto    retry\r
+\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
+       bcf     PORTA,4         ; turn on LED\r
+\r
+       movlw   StartUpText3\r
+       call    TxLCD16B\r
+       call    LongDelay\r
+\r
+       goto    retry\r
+\r
+\r
+;----------------------------------------------------------------\r
+; IRQInit - Sets up the IRQ Handler\r
+\r
+IRQInit\r
+       bsf     STATUS,RP0              ; Reg bank 1\r
+;      bcf     OPTION_REG,INTEDG       ; We want RB0 to give us an IRQ on the falling edge\r
+       bsf     INTCON,INTE             ; Enable the RB0/INT\r
+       bsf     INTCON,GIE              ; Enable global interrupts\r
+       bcf     STATUS,RP0              ; Back to bank 0\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
+       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
+       call    DelayW\r
+\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
+       call    DelayW\r
+\r
+       movlw   3               ; Write 3 to the LCD\r
+       call    TxLCD\r
+;      movlw   44\r
+       movlw   255             ; Should be 16ms 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
+\r
+;      bsf     PORTA,4         ; turn off LED\r
+\r
+       movlw 28h               ; Some random commands :)))\r
+       call TxLCDB\r
+\r
+       movlw 0ch               ; hmmm\r
+       call TxLCDB\r
+\r
+       movlw 01h               ; hmmm\r
+       call TxLCDB\r
+\r
+       movlw 06h               ; hmmm\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
+       call    TxLCDB\r
+        bsf     LCD_RS_BIT\r
+       call    TxLCD8B\r
+        bcf     LCD_RS_BIT\r
+       movlw   80h+40          ;DisplayRam 40 (row 2)\r
+       call    TxLCDB\r
+        bsf     LCD_RS_BIT\r
+       call    TxLCD8B\r
+       return\r
+\r
+;----------------------------------------------------------------\r
+;  TxLCD8B\r
+;  Send a string to the LCD.\r
+\r
+TxLCD8B\r
+;      movwf   Icount          ; Icount = W\r
+       movlw   8\r
+       movwf   e_LEN           ; Move to e_LEN\r
+\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
+       decfsz  e_LEN,f\r
+       goto    Txm_lp\r
+       return\r
+\r
+;----------------------------------------------------------------\r
+; TxLCDB - send a byte to the LCD\r
+\r
+TxLCDB\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
+       goto    RxNoProb\r
+       bcf     LCD_RS_BIT\r
+       bsf     temp,0          ; Indicate RS change\r
+RxNoProb\r
+\r
+NotReady\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
+       goto    NotReady\r
+\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
+\r
+       return\r
+;----------------------------------------------------------------\r
+; RxLCDB - recv a byte from the LCD\r
+\r
+RxLCDB\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
+\r
+       return\r
+\r
+;----------------------------------------------------------------\r
+; 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
+\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
+       bsf     LCD_DB5_BIT\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
+       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
+\r
+;----------------------------------------------------------------\r
+; RxLCD - recv a nibble from the LCD\r
+\r
+RxLCD\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
+       \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
+       addlw   1\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
+       addlw   4\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
+\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
+\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
+\r
+       subtitl "Bootstrap/Bootloader code"\r
+       page\r
+\r
+;----------------------------------------------------------------------\r
+; Bootstrap code - Allows PIC to flash itself with data from async port\r
+; Startup @9600bps\r
+\r
+       org     700h                    ; Place the boot code at the top of memory\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
+BootDataL      equ     7bh\r
+BootDataH      equ     7ah\r
+BootTimerL     equ     79h\r
+BootTimerM     equ     78h\r
+BootTimerH     equ     77h\r
+BootNumBytes   equ     76h\r
+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
+\r
+       bsf     STATUS,RP0              ; Access bank 1\r
+       bsf     TXSTA,TXEN              ; Enable UART TX\r
+       movlw   31                      ; Divisor for 9k6 @ 20MHz Fosc\r
+       movwf   SPBRG                   ; Store\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
+;      clrf    BootRXState             ; Waiting for command\r
+\r
+       movlw   BootStartText\r
+       call    BootTXStr\r
+\r
+       movlw   0e8h\r
+       movwf   BootTimerL\r
+       movwf   BootTimerM\r
+       movwf   BootTimerH\r
+\r
+BootTimeout\r
+       incf    BootTimerL,f            ; A 24-bit counter\r
+       skpnz\r
+       incf    BootTimerM,f\r
+       skpnz\r
+       incf    BootTimerH,f\r
+       skpnz                           ; When overflowing here..\r
+       goto    BootReturn              ; ..Exit boot loader, no keypress within timeout period, resume program\r
+       btfss   PIR1,RCIF               ; Wait for RX to complete\r
+       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
+\r
+BootFlash\r
+       movlw   BootFlashText\r
+       call    BootTXStr\r
+\r
+       bsf     BootBits,1\r
+       clrf    BootAddrL\r
+       clrf    BootAddrH\r
+\r
+BootLoop\r
+       call    BootRXB                 ; First find the ':'\r
+       xorlw   ':'\r
+       skpz\r
+       goto    BootLoop                ; Loop until we find it!\r
+\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
+\r
+; Note carry should be clear here as there cannot be odd number of bytes in this format\r
+\r
+       call    BootRXHEX               ; Receive AddrH\r
+       movwf   BootAddrH\r
+       call    BootRXHEX               ; Receive AddrL\r
+       movwf   BootAddrL\r
+       rrf     BootAddrH,f             ; Fix the addressing again\r
+       rrf     BootAddrL,f\r
+\r
+       bcf     BootBits,2              ; Assume we should program\r
+       bsf     BootBits,1              ; And assume we should program flash not ee\r
+\r
+       movf    BootAddrH,w\r
+       xorlw   020h                    ; Check if it's configuration, which we can't program\r
+       skpnz                           ; Skip the bit set if it was false alarm\r
+       bsf     BootBits,2              ; No programming for this line\r
+\r
+       xorlw   001h                    ; Also check if it's EEPROM memory (first xor 20h then 1 =21h)\r
+       skpnz                           ; Skip the bit set instr if not EE data address\r
+       bcf     BootBits,1              ; We should program EE, will ignore the AddrH\r
+\r
+       call    BootRXHEX               ; Receive Record Type (must be 0 for real records)\r
+       skpz                            ; Check if zero\r
+       goto    BootFlashComplete\r
+\r
+BootLineLoop\r
+       call    BootRXHEX               ; Receive low-byte of data word\r
+       movwf   BootDataVL\r
+       call    BootRXHEX               ; Receive high-byte of data word\r
+       movwf   BootDataVH\r
+       \r
+       btfsc   BootBits,2              ; Check whether this line should be programmed at all\r
+       goto    BootWriteSkip\r
+\r
+       bcf     BootBits,0              ; Read mode first, verify if we actually have to write\r
+       call    BootEE\r
+       movf    BootDataVL,w\r
+       xorwf   BootDataL,f             ; Compare and destroy DataL\r
+       movwf   BootDataL               ; Write new data to DataL\r
+       skpz                            ; Skip if no difference, have to check high byte as well\r
+       goto    BootWrite               ; Jump directly to write\r
+\r
+       movf    BootDataVH,w\r
+       xorwf   BootDataH,f             ; Compare\r
+       skpnz                           ; Skip if no difference, no programming necessary\r
+       goto    BootWriteSkip\r
+\r
+BootWrite\r
+       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
+       incf    BootAddrH,f             ; And add to high byte if needed\r
+\r
+       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
+       call    BootTXStr\r
+\r
+       bsf     STATUS,RP0\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
+\r
+       clrf    PCLATH\r
+       return                          ; Return to code        \r
+\r
+;----------------------------------------------------------------------\r
+; BootTXB - Sends one byte to the UART, waits for transmitter to become\r
+;  free before sending\r
+\r
+BootTXB\r
+BootTXW1\r
+       btfss   PIR1,TXIF               ; Wait for TX to empty\r
+       goto    BootTXW1\r
+       movwf   TXREG                   ; Send the byte\r
+       return\r
+\r
+;----------------------------------------------------------------------\r
+; 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
+       return\r
+       call    BootTXB                 ; Send char\r
+       incf    BootStrTemp,w           ; Retrieve\r
+       goto    BootTXStr\r
+\r
+;----------------------------------------------------------------------\r
+; BootRXB - Receives one byte from the UART, waits if nothing available\r
+\r
+BootRXB\r
+BootRXW1\r
+       btfss   PIR1,RCIF               ; Wait for RX to complete\r
+       goto    BootRXW1\r
+       movf    RCREG,w                 ; Get the recvd byte\r
+       return\r
+\r
+;----------------------------------------------------------------------\r
+; BootRXHEXNibble - Receives one byte and converts it from ASCII HEX to binary\r
+\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
+       addlw   10                      ; Add 10 (A get to be 0ah etc.)\r
+       return\r
+\r
+;----------------------------------------------------------------------\r
+; BootRXHEX - Receives two bytes from the UART, waits if nothing available\r
+;  Decodes the bytes as ASCII hex and returns a single byte in W\r
+\r
+BootRXHEX\r
+       call    BootRXHEXNibble\r
+       movwf   BootHEXTemp\r
+       swapf   BootHEXTemp,f           ; Swap it up to the high nibble\r
+\r
+       call    BootRXHEXNibble\r
+       addwf   BootHEXTemp,w           ; And add the two nibbles together\r
+       return\r
+\r
+;----------------------------------------------------------------------\r
+; BootEE - Reads or writes EE or Flash memory, BootBits specify the\r
+;  exact action to take. BootAddrL and BootAddrH has to be initialized\r
+;  to the address of choice (0000-003fh for EE and 0000h-07ffh for flash\r
+;  The data to be written has to be put in BootDataL and BootDataH, and\r
+;  data will be to the same place when read back\r
+\r
+BootEE\r
+       bsf     STATUS,RP1              ; Select bank 2 (RP0 must be 0)\r
+\r
+       movf    BootAddrH,w             ; Load desired address\r
+       movwf   EEADRH\r
+       movf    BootAddrL,w\r
+       movwf   EEADR\r
+       movf    BootDataH,w             ; And load the data (only used when writing)\r
+       movwf   EEDATH\r
+       movf    BootDataL,w\r
+       movwf   EEDATA\r
+\r
+       bsf     STATUS,RP0              ; Go to bank 3\r
+\r
+       bsf     EECON1,EEPGD            ; Point to Program Flash mem\r
+       btfss   BootBits,1              ; Test if that was correct or if we have to clear again\r
+       bcf     EECON1,EEPGD            ; Point to EE DATA mem\r
+\r
+       btfss   BootBits,0              ; Check from read or write\r
+       goto    BootEERD                ; Skip the WR if we were going for a read\r
+\r
+       bsf     EECON1,WREN             ; Enable writes\r
+       movlw   55h\r
+       movwf   EECON2\r
+       movlw   0AAh\r
+       movwf   EECON2                  ; Unlock write operation\r
+       bsf     EECON1,WR               ; And start a write cycle\r
+BootWRLoop\r
+       btfsc   EECON1,WR               ; This executes for EE only not flash, waits for WR to finish\r
+       goto    BootWRLoop              ; These two instructions gets NOPed when flashing\r
+\r
+       bcf     EECON1,WREN             ; Finally disable writes again\r
+                                       ; Here we read the data back again, can be used as verify\r
+BootEERD\r
+       bsf     EECON1,RD               ; Start a read cycle\r
+       nop                             ; Only necessary for flash read, same thing as when writing above\r
+       nop                             ; Except I could use the two words for something useful there.. :)\r
+\r
+BootEEX\r
+       bcf     STATUS,RP0              ; Back to bank 2\r
+       movf    EEDATA,w                ; Store our EE-data\r
+       movwf   BootDataL\r
+       movf    EEDATH,w\r
+       movwf   BootDataH\r
+       bcf     STATUS,RP1              ; And finally back to bank 0\r
+\r
+       return\r
+\r
+BootStartText\r
+       DT      "WJBoot - press ESC to flash",0\r
+BootFlashText\r
+       DT      13,10,"Send INHX8 file now...",13,10,0\r
+BootRunText\r
+       DT      13,10,"Exiting loader",13,10,0\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
+\r
+       END\r