On XC8 it's pretty straightforward but you do need to work round some quirks.
Compile your main code with the codeoffset linker setting as required.
Here's an example of the vectors in my bootloader for an 16F1823 - this only enters the bootloader on a power-on reset. (One option to enter your main code is to just force a watchdog reset)
void bootvecs(void) @0x00
{
#asm
btfss 3,4 // status, /TO
goto 0x200 // watchdog timeout - go to app code
goto _main
nop
MOVLP 2
goto 0x204 // int code fixup
#endasm
}
The only issue is forcing the compiler to put the code where you want it - you need to check the assembler listing, as it sometimes ignores the @xxx directive.
You need to put the snippet above as the first procedure in the source file, otherwise it will put other code there and ignore the @ directive
You also need to put a call to the code to prevent the compiler from optimising it out - just set up a call that the compiler can't figure out will never get called.
e.g.
void main(void) @0x08
{
..your bootloader
} while(1);
bootvecs(); // force inclusion of bootvecs - optimised out otherwise
}
One thing I've never managed to achieve for various obscure reasons is have the main code and bootloader in the same source project. One issue is the @ operator overrides codeoffset but it can get itself confused.