\ -*- forth -*-


\ Boot code for PIC18 chips.


\ Misc  stuff to remember when configuring chip

\ - disable watchdog timer if the code does not use CLWDT
\ - set power up timer (or use capacitor + reset enabled)
\ - disable MCLR pin, or use a reset switch with pullup resistor



\ Data memory

\ Note that the extended 18f instruction set allows the use of indexed
\ addressing relative to FSR2, which can enable cheap structs/objects. 
\ however, it disables global (access bank) variables for the region
\ 0x000-0x05F, which makes that region better suited for stack memory
\ if the extensions are used, so the contiguous space after 0x80 could
\ be used as object memory, leaving the space 0x60-7F for global
\ variables.

\ in short, indexed addressing?
\ no  -> stacks in 0x80 - 0xFF, globals in 0x00 - 0x7F
\ yes -> stacks in 0x00 - 0x5F, globals in 0x60 - 0x7F

\ you'll have to configure this in a config seed, values are used below

\ note that bank selecting is not used. it sucks, one can do without


\ * INDF0 the data stack
\ * INDF1 the control/temp stack (x)
\ * STKPTR the hardware return stack (r)

\ redefine these:

\ : stack-data    #x80 ;
\ : stack-control #x90 ;


\ stack init code
macro
: init-rs
    0 movlw STKPTR movwf ;      \ RS  
: init-ds
    stack-data    1 - 0 lfsr ;  \ DS
: init-xs    
    stack-control 1 - 1 lfsr ;  \ CS
: init-stacks
    init-ds
    init-xs
    init-rs ;
forth

\ util
macro
: kHz  1000 * ;
: MHz  kHz kHz ;
forth

\ macros for chip setup. this needs to be cleaned up a bit.. but maybe
\ this approach is better even? all the setup code is defined in
\ macros, and you simply instantiate the macro in your boot code. all
\ nice and explicit.
  
\ these default to all output, which is good enough for testing.

macro

\ will be redefined when the correct machine is loaded
\ : TRISA undefined ;
\ : TRISB undefined ;
\ : TRISC undefined ;
\ : TRISD undefined ;
\ : TRISE undefined ;

: 2-output 0 TRISA ! 0 TRISB ! ;
: 3-output 2-output 0 TRISC ! ;
: 5-output 3-output 0 TRISD ! 0 TRISE ! ;
forth


\ The main idea of a boot loader during development is that it is
\ robust: it should be impossible to mess up the boot process while
\ poking around; it should always be possible to gain access to the
\ device through the built-in interpreter.

\ On the other hand, d\uring deployment, it should be possible to
\ erase or fuse the boot block to disable all access to the
\ interpreter.

\ The boot block should be universal enough so that it has to be
\ flashed only once. All unnecessary features need to be moved to
\ the application (or os kernel) source.

\ These requirements lead to the following solutions:

\ - booting never bypasses the debug check (serial idle / break)
\ - ISRs are passed to the block following the boot block
\ - first 64 K are clean of core code (to enable fusing)
\ - boot code is strictly read-only

  
\ Jump to boot loader, and install ISR patched to block at app-boot

app-boot org        : application
app-boot #x08 + org : isr-high
app-boot #x18 + org : isr-low
    
#x0000 org  : boot boot-40 ;
#x0008 org  : boot-isr-high isr-high ;
#x0018 org  : boot-isr-low  isr-low ;
  
\ It is possible to install a custom boot loader by overwriting block
\ 0.  For robustness, it is allowed for block 0 (the first 64 bytes ==
\ first flash erase unit) to be empty, meaning it contains only #xFFFF
\ NOP instructions. the code will fall into this word, at the start of
\ block 1.
    

#x0040 org    
: boot-40 warm ;

\ Boot loader code starts here.
    

  
\ Set the ram allocation pointer.
\ allot-ram  dict ram
