;----------------------------------------------------------------\r
; Version\r
;\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
Dcount equ 20h\r
e_LEN equ 21h\r
+\r
+Counter equ 22h\r
+SlaveBreakState equ 23h ; Hold state and time-out information about slave break, indicates when it can happen\r
Icount equ 2Dh ; Offset of string to print\r
TxTemp equ 2Eh ; blahblah\r
TxTemp2 equ 2Fh ; Blahblah2\r
; Also because of the real-time requirements for clocking data onto the Unilink bus the first check in the ISR\r
; is to see whether the Unilink clock rise was the reason for the interrupt. This results in a "clock rise to\r
; bit ready" time of less than 30 instruction cycles, should be plenty of spare time waiting for clock to go low\r
-; again after that.\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
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
+; 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
; 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
\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 5ms (10 loops)\r
+; If a packet would be received 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
+ btfss 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
+ btfss SlaveBreakState,4 ; Only increment to 0x10\r
+ incf SlaveBreakState,f\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
+ incf Counter,f\r
+; clrf SlaveBreakState\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 7\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 4\r
+ skpz\r
+ goto IRQAfterTMR2\r
+ bsf STATUS,RP0\r
+ bsf DATA_BIT\r
+ bcf STATUS,RP0\r
+ clrf SlaveBreakState\r
+\r
+IRQAfterTMR2\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
page\r
\r
;----------------------------------------------------------------\r
-; Data can be stored between here and 100h...\r
-\r
-StartUpText1\r
- DT "----- WJ UniLink"\r
- \r
-LookUp movwf PCL ; Go to it (this assumes PCLATH == 00h)\r
-\r
-;----------------------------------------------------------------\r
; Main program begins here. [Called after bootloader, lcdinit and irqinit...]\r
\r
- org 100h ; Maybe not force this to a specific address later\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
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
btfsc PORTB,0 ; Test CLK\r
movlw 'C'\r
call TxLCDB\r
\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
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
; 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
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
+LookUp movwf PCL ; Go to it (this assumes PCLATH == 06h)\r
+\r
+\r
subtitl "Bootstrap/Bootloader code"\r
page\r
\r
\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
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
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
\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
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
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
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
; 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
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
\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
\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