I have created two separate programs for my AT89C51ED2: bootloader (0x0000) and normal program (0x2000). I use an absolute memory addressed variable to distinguish between the two modes of operation so that interrupts either execute code in the bootloader or call the appropriate ISRs in the program code (above 0x2000). The following is an example of the code used for interrupts in program code, in order to eliminate the usual pushes, pops and reti done by the bootloader ISR.
/** Timer 1 ISR. */ void interruptTimer1(void) interrupt 3 { #pragma asm lcall interruptTimer1Mirror ret #pragma endasm } /** Timer 1 interrupt working function. This routine is used for delays. */ void interruptTimer1Mirror() { timer1Interrupted = TRUE; }
I have successfully accomplished the exact thing that you desire. The trick is to actually use 3 programs. The first contains the interrupt vectors located at address 0. All interrupts are handled. Each interrupt handler interrogates a specific bit (I either use F0 or 20^0) to determine whether the interrupt is for the BootLoader or the real application. It then jumps to the corresponding interrupt +0x200 if in the bootloader or +0x2000 if in the application. The normal startup at location 0 points to startup code in the BootLoader (Program at address 0x200). The bootloader sets the "BootLoader" flag, determines whether the application is loaded (via checksum info saved in an unused interrupt vector address) and if so, clears the "BootLoader" flag and jumps to address 0x2000. If the application is not loaded it simply waits to do the download. When I load the bootloader initially, I merge the hex for the interrupt vector program with the bootloader hex to create a valid operating program. The application has it's interrupt vector set to 0x2000. The startup code is modified to ORG at 0x2000 also. Other than that, the app is just a normal program.
Thanks for the advice. I'll give it a try...