--- /dev/null
+ 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