Hogwarts Wand Docs: ../sw/kernel.inc

Wand Sourcecode: ../sw/kernel.inc

;
; kernel.inc - main kernel sourcecode
;
;
; Copyright (C) 2006  Nathan (Acorn) Pooley
; 
; This program is free software; you can redistribute it and/or
; modify it under the terms of the GNU General Public License
; version 2 as published by the Free Software Foundation.
; 
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
; GNU General Public License (gpl.txt) for more details. 
; 
; You should have received a copy of the GNU General Public License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
; 
; You can contact the author, Nathan (Acorn) Pooley, by writing
; to Nathan (Acorn) Pooley, 949 Buckeye Drive, Sunnyvale, CA  94086, USA
; or through the form at http://www.rawbw.com/~acorn/wand/feedback.html
; 

;#@DOC@ main kernel sourcecode - compiled into kernel and wand programs


#define     SIM             0
#define     KK_DEBUG        1

#define     KK_VERSION      28

#ifndef     KK_CHIP
;#define    KK_CHIP     18252
;#define    KK_CHIP     182525
#define     KK_CHIP     182620
;#define    KK_CHIP     182680
#endif

#if (KK_CHIP==18252)
#include        "d18252.inc"
#endif

#if (KK_CHIP==182525)
#include        "d182525.inc"
#endif

#if (KK_CHIP==182620)
#include        "d182620.inc"
#endif

#if (KK_CHIP==182680)
#include        "d182680.inc"
#endif

    radix       dec
    expand


#define KK_DEBUG_ISRSAVE    0

;###########################################################################
;################################ CONFIG SETTINGS ##########################
;###########################################################################

    ;
    ;   NOTE:
    ;      1 = set to 1
    ;      0 = set to 0
    ;      . = not sure; default is 0
    ;      ; = not sure; default is 1
    ;      - = unimplemented (set to 1)
    ;
    ; 0x300000 = ?
    ;
    ; 0x300001 = CONFIG1H = 00-- 1000
    ;  7  IESO     = 0     two speed startup disabled
    ;  6  FCMEN    = 0     disable fail-safe clock monitor
    ; 5-4 reserved = --
    ; 3-0 FOSC3-0  = 1000  internal oscillator (RA6, RA7 are port bits)
    ;
    __CONFIG    _CONFIG1H,0x38

    ;
    ; 0x300002 = CONFIG2L = ---1 1001
    ; 7-5 reserved = ---
    ; 4-3 BORV1-0  = 11    brown out voltage min value
    ; 2-1 BOREN1-0 = 01    brown out under SW control
    ;  0  /PWRTEN  = 1     power up timer disbled (?)
    ;
#if KK_VERSION>=12
    __CONFIG    _CONFIG2L,0xfb
#else
    __CONFIG    _CONFIG2L,0xf9
#endif

    ;
    ; 0x300003 = CONFIG2H = ---1 1110 = 0xfe
    ; 7-5 reserved = ---
    ; 4-1 WDTPS3-0 = 1111  watchdog prescale 1:32768
    ;  0  WDTEN    = 0     watchdog disabled
    ;
    __CONFIG    _CONFIG2H,0xfe

    ;
    ; 0x300004 = ?
    ;
    ; 0x300005 = CONFIG3H = 1--- -011 = 0xfb
    ;  7  MCLRE    = 1     MCLR enabled
    ; 6-3 reserved = ----
    ;  2  LPT1OSC  = 0     T1 osc high power mode (try low power too?)
    ;  1  PBADEN   = 1     PortB<4:0> are analog inputs on reset
    ;  0  CC2P2MX  = 1     CCP2 bit is multiplexed with RC1
    ;
    __CONFIG    _CONFIG3H,0xfb

    ;
    ; 0x300006 = CONFIG4L = 10-- -0-1 = 0xbb
    ;  7  /DEBUG   = 1     debug disabled
    ;  6  XINST    = 0     extended instruction set disabled
    ; 5-3 reserved = ---
    ;  2  LVP      = 0     single-supply ICSP (programming) disabled
    ;  1  reserved = -
    ;  0  STVREN   = 1     stack under/over flow causes reset
    ;
    __CONFIG    _CONFIG4L,0xbb

    ;
    ; 0x300007 = ?
    ;
    ; 0x300008 = CONFIG5L = ---- 0000 = 0xf0
    ; 7-4 reserved = ----
    ; 3-0 CP3-0    = 0000   FLASH protected (no external r/w)
    ;
;   __CONFIG    _CONFIG5L,0xf0
    __CONFIG    _CONFIG5L,0xff

    ;
    ; 0x300009 = CONFIG5H = 00-- ---- = 0x3f
    ;  7  CPD      = 0      EEPROM proteced (no external r/w)
    ;  6  CPB      = 0      boot flash proteced (no external r/w)
    ; 5-0 reserved = ------
    ;
;   __CONFIG    _CONFIG5H,0x3f
    __CONFIG    _CONFIG5H,0xff


    ;
    ; 0x30000a = CONFIG6L = ---- 1111 = 0xff
    ; 7-4 reserved = ----
    ; 3-0 WRT3-0   = 1111   enable table writes to flash
    ;
    __CONFIG    _CONFIG6L,0xff

    ;
    ; 0x30000b = CONFIG6H = 111- ---- = 0xff
    ;  7  WRTD     = 1      enable table writes to eeprom
    ;  6  WRTB     = 1      enable table writes to boot flash
    ;  5  WRTC     = 1      enable table writes to config
    ; 4-0 reserved = -----
    ;
    __CONFIG    _CONFIG6H,0xff

    ;
    ; 0x30000c = CONFIG7L = ---- 1111 = 0xff
    ; 7-4 reserved = ----
    ; 3-0 EBTR3-0  = 1111   enable table read from any block
    ;
    __CONFIG    _CONFIG7L,0xff

    ;
    ; 0x30000d = CONFIG7H = -1-- ---- = 0xff
    ;  7  reserved = -
    ;  6  EBTRB    = 1      enable table read from any block
    ; 5-0 reserved = ------
    ;
    __CONFIG    _CONFIG7H,0xff

;###########################################################################
;################################ PORT SETTINGS ############################
;###########################################################################


#ifndef     KERNVAL_PORTA
    error " KERNVAL_PORTA required: default PORTA setting"
#endif
#ifndef     KERNVAL_TRISA
    error " KERNVAL_TRISA required: default TRISA setting"
#endif
#ifndef     KERNVAL_PORTB
    error " KERNVAL_PORTB required: default PORTB setting"
#endif
#ifndef     KERNVAL_TRISB
    error " KERNVAL_TRISB required: default TRISB setting"
#endif
#ifndef     KERNVAL_PORTC
    error " KERNVAL_PORTC required: default PORTC setting"
#endif
#ifndef     KERNVAL_TRISC
    error " KERNVAL_TRISC required: default TRISC setting"
#endif
#ifndef     KERNVAL_TRISB_LED_MASK
    error " KERNVAL_TRISB_LED_MASK required: set bits which are LED outputs"
#endif
#ifndef     bki_alive
    error " bki_alive required: bit to toggle to show its running"
#endif
#ifndef     bki_led_alive
    error " bki_led_alive required: bit to toggle to show its working"
#endif
#ifndef     bki_led_serial
    error " bki_led_serial required: bit to toggle when serial i/o occurs"
#endif


#define bki_serial_detect   PORTC,RX


;###########################################################################
;################################ BOOT SETTINGS ############################
;###########################################################################

    ;
    ; INTCON2 = 1111 0000
    ;  7  /RBPU    = 1  disable portB pullup resistors
    ;  6  INTEDG0  = 1  rising edge for intr0
    ;  5  INTEDG1  = 1  rising edge for intr1
    ;  4  INTEDG2  = 1  rising edge for intr2
    ;  3  reserved = 0
    ;  2  TMR0IP   = 0  T0 isr is low priority
    ;  1  reserved = 0
    ;  0  RBIP     = 0  portB change interrupt is low priority
    ;
KERNVAL_INTCON2     equ 0xf0

    ;
    ; INTCON3 = 0000 0000
    ;  7  INT2IP   = 0  intr2 is low priority
    ;  6  INT1IP   = 0  intr1 is low priority
    ;  5  reserved = 0  
    ;  4  INT2IE   = 0  intr2 disabled
    ;  3  INT1IE   = 0  intr1 disabled
    ;  2  reserved = 0  
    ;  1  INT2IF   = 0  intr2 flag cleared
    ;  0  INT1IF   = 0  intr1 flag cleared
    ;
KERNVAL_INTCON3     equ 0x00

    ;
    ; internal oscillator control
    ;
    ; OSCTUNE = 1000 0000
    ;
    ;  7  INTSRC   = 1     31kHz clock from 8MHz internal clock
    ;  6  PLLEN    = 0     PLL off
    ;  5  reserved = 0
    ; 4-0 TUN4-0   = 00000 tune to center frequency
    ;
KERNVAL_OSCTUNE     equ 0x80

    ;
    ; OSCCON = 1110 0000
    ;
    ;  7  IDLEN   = 1   entire IDLE on SLEEP instruction
    ; 6-4 IRCF2-0 = 110 4MHz clock
    ;  3  OSTS    = 0   (read-only)
    ;  2  IOFS    = 0   (read-only)
    ; 1-0 SCS1-0  = 00  Primary oscillator (from config bits)
    ;
KERNVAL_OSCCON      equ 0xe0
    
    ;
    ; T0CON = 0000 1000
    ;
    ;  7   TMR0ON    = 0   T0 off (not running)
    ;  6   T08BIT    = 0   T0 is 16 bit counter
    ;  5   T0CS      = 0   clock source=FOSC
    ;  4   T0SE      = 0   increment on low-to-hi
    ;  3   PSA       = 1   prescaler not used
    ; 2-0  T0PS2-0   = 000 1:2 prescale (not used)
    ;
KERNVAL_T0CON       equ 0x08

    ;
    ; T1CON = 0111 1111
    ;
    ;  7   RD16      = 0   8 bit reads/writes
    ;  6   T1RUN     = 1   use t1 Oscillator
    ; 5-4  T1CKPS1-0 = 11  1:8 prescale
    ;  3   T1OSCEN   = 1   t1 osc enabled
    ;  2   /T1SYNC   = 1   do not syncronize external clock input
    ;  1   TMR1CS    = 1   external clock
    ;  0   TMR1ON    = 1   t1 on (running)
    ;
    ; 32768 Hz / 8 = 4096 HZ
    ; TMR1L wraps 4096Hz/256 = 16Hz = every 0.0625 sec
    ; TMR1H wraps 16Hz/256 = every 16 seconds
    ;
    ;   TMR1H = ssss qqff
    ;      ssss = seconds
    ;      qq   = quarter seconds
    ;      ff   = fractions of quarter seconds
    ;
KERNVAL_T1CON       equ 0x7f

    ;
    ; RCON = 1101 1111
    ;  7  IPEN     = 1  enable intr priority levels
    ;  6  SBOREN   = 1  enable brown-out reset
    ;  5  reserved = 0
    ;  4  /RI      = 1  set to 0 on reset instruction
    ;  3  /TO      = 1  read-only (set to 0 if WDT times out)
    ;  2  /PD      = 1  read-only (set to 0 if MCLR or WDT)
    ;  1  /POR     = 1  this is set to 0 on power up
    ;  0  /BOR     = 1  this is set to 0 on brown-out or power up
    ;
KERNVAL_RCON        equ 0xdf

    ;
    ; ADCON0 = 0000 0000
    ; 7-6 reserved 00
    ; 5-2 CHS3-0   0000 select channel 0
    ;  1  GO/DONE  0    do not start conversion
    ;  0  ADON     0    A/D unit off
    ; 
KERNVAL_ADCON0      equ 0x00

    ;
    ; ADCON1 = 0000 0000
    ; 7-6 reserved 00
    ;  5  VCFG1    0    Vref- = Vss
    ;  4  VCFG0    0    Vref+ = Vdd
    ; 3-0 PCFG3-0  0000 all available pins set as analog inputs
    ; 
KERNVAL_ADCON1      equ 0x00

    ;
    ; ADCON2 = 0001 0001
    ;  7  ADFM     = 0   left justified formt)
    ;  6  reserved = 0
    ; 5-3 ACQT2-0  = 010 4 Tad cycles for acquisition
    ; 2-0 ADCS2-0  = 001 A/D conversion clock = Fosc/8 (Tad = 2usec)
    ; 
KERNVAL_ADCON2      equ 0x11

    ;
    ; TXSTA
    ;
    ;  7  CSRC  = 0  dont care
    ;  6  TX9   = 0  use 8 bit data
    ;  5  TXEN  = 0  transmit disabled
    ;  4  SYNC  = 0  Async mode
    ;  3  SENDB = 0  do not send sync break
    ;  2  BRGH  = 1
    ;  1  TRMT  = 0  read-only
    ;  0  TX9D  = 0  dont care
    ;
KERNVAL_TXSTA       equ 0x24

    ;
    ; RCSTA
    ;
    ;  7  SPEN  = 1  enable serial port
    ;  6  RX9   = 0  use 8 bit data
    ;  5  SREN  = 0  dont care
    ;  4  CREN  = 1  receive enable 
    ;  3  ADDEN = 0  dont care
    ;  2  FERR  = 0  read-only
    ;  1  OERR  = 0  read-only 
    ;  0  RX9D  = 0  read-only
    ;
KERNVAL_RCSTA       equ 0x90

    ;
    ; BAUDCON
    ;
    ;  7  ABDOVF   = 0  dont care
    ;  6  RCIDL    = 0  read-only
    ;  5  reserved = 0  
    ;  4  SCKP     = 0  dont care
    ;  3  BRG16    = 0
    ;  2  reserved = 0  
    ;  1  WUE      = 0  disable wakeup interrupt
    ;  0  ABDEN    = 0  disable auto-baud rate detect
    ;
KERNVAL_BAUDCON     equ 0x00

    ;
    ; SPBRG 
    ;          9600 baud: 1ms per byte
    ;        186000 baud: 53 usec per byte  (too fast)
    ; B
    ; R B
    ; G R
    ; 1 G
    ; 6 H
    ; - -
    ; 0 0    rate = 4MHz/(64(n+1)) = 9600
    ;        n = 4MHz/(64*9600) - 1 = 5.5
    ;        (n=5) rate = 10416
    ;        (n=6) rate =  8928
    ;
    ; 0 1    rate = 4MHz/(16(n+1)) = 9600
    ; or     n = 4MHz/(16*9600) - 1 = 25.04
    ; 1 0    (n=25) rate = 9615
    ;        (n=26) rate = 9259
    ;
    ; 1 1    rate = 4MHz/(4(n+1)) = 9600
    ;        n = 4MHz/(4*9600) - 1 = 103.17
    ;        (n=103) rate = 9615
    ;        (n=104) rate = 9523
    ;
    ; USE
    ;    BRG16  = 0
    ;    BRGH   = 1
    ;    SPBRG  = 25
    ;    SPBRGH = 0 (dont care)
    ;
KERNVAL_SPBRG       equ 25


    ;       CMCON
    ;  7  C2OUT   0    read-only
    ;  6  C1OUT   0    read-only
    ;  5  C2INV   0    not inverted
    ;  4  C1INV   0    not inverted
    ;  3  CIS     0    dont care
    ; 2-0 CM2-CM0 111  comparitors off
    ;
KERNVAL_CMCON   equ 0x07    ; This is the reset value


;###########################################################################
;###########################################################################
;################################ MACROS ###################################
;###########################################################################
;###########################################################################

;
; Macros
;
; COUT   - print character in w
; COUTCR - print character in w followed by CR
; SOUT   - print string
; SOUT0  - print string from str_base0 string table
; ERR    - error occurred
; ERRL   - error occurred
; BRKPT  - cause a breakpoint
;
; SET_LED      - set DEBUG LED value
; DB_WHERE     - Remember where in the code we are (DEBUG)
; DB_WHERE2    - Remember where in the code we are (DEBUG)
; DB_IWHERE    - Remember where in the code we are (DEBUG)
; EEPROM_BEGIN - prepare to read/write eeprom at offset
; EEPROM_READ1 - read byte from eeprom into w
; SET_RUNSTATE - set current runstate to vki_runstate_*
;

;
; COUT - print character in w
;
COUT    macro   char
    movlw   char
    rcall   kk_cout
    endm
COUTL   macro   char
    movlw   char
    call    kk_cout
    endm

;
; COUTCR - print character in w
;
COUTCR  macro   char
    movlw   char
    rcall   kk_cout
    rcall   kk_outcr
    endm
COUTCRL macro   char
    movlw   char
    call    kk_cout
    call    kk_outcr
    endm

;
; SOUT - print string
;
SOUT    macro   str_addr
    movlw   LOW str_addr
    movwf   TBLPTRL
    movlw   HIGH str_addr
    rcall   kk_sout_wh
    endm
SOUTL   macro   str_addr
    movlw   LOW str_addr
    movwf   TBLPTRL
    movlw   HIGH str_addr
    call    kk_sout_wh
    endm

;
; SOUT - print string from str_base0 string table
;
SOUT0   macro   str_addr
    movlw   str_addr-kki_str_base0
    rcall   kki_sout_base0
    endm
SOUT0L  macro   str_addr
    movlw   str_addr-kki_str_base0
    call    kki_sout_base0
    endm

;
; ERR - error occurred
;
ERR     macro   errnum
    movlw   errnum
    bra     kk_doerror
    endm
ERRL    macro   errnum
    movlw   errnum
    goto    kk_doerror
    endm
    
;
; BRKPT - cause a breakpoint
;
BRKPT   macro   brknum
    movwf   vki_breakpt_w
    movlw   brknum
    rcall   kk_dobreak
    endm
BRKPTL  macro   brknum
    movwf   vki_breakpt_w
    movlw   brknum
    call    kk_dobreak
    endm

;
; SET_LED - set DEBUG LED value
;
SET_LED macro   trisb_val
    movlw       trisb_val
    rcall       kki_led_set
    endm

;
; DB_WHERE - Remember where in the code we are (DEBUG)
;
DB_WHERE macro  wh
    movlw       wh
    movwf       vk_where
    endm

;
; DB_WHERE2 - Remember where in the code we are (DEBUG)
;
DB_WHERE2 macro wh
    movlw       wh
    movwf       vk_where2
    endm

;
; DB_IWHERE - Remember where in the code we are (DEBUG)
;
DB_IWHERE macro wh
    movlw       wh
    movwf       vk_iwhere
    endm

;
; EEPROM_BEGIN - prepare to read/write eeprom at offset
;
EEPROM_BEGIN    macro   offset
    movlw   offset-1    ; kk_eeprom_read pre-increments
    movwf   EEADR
    endm

;
; EEPROM_READ1 - read 1 byte from eeprom into w
;
EEPROM_READ1    macro   offset
    EEPROM_BEGIN    offset
    rcall           kk_eeprom_read  ; read eeprom into w
    endm

;
; SET_RUNSTATE - set current runstate to kki_runstate_*
;
SET_RUNSTATE    macro   runstate
    movlw       runstate-kki_runstate_table
    rcall       kki_set_runstate
    endm


;###########################################################################
;################################ ERRORS & BREAKPOINTS #####################
;###########################################################################

ERROR_A1_TIME4SEC       equ     0xa1    ; kk_main_run not called for >4 sec
ERROR_A2_STACK          equ     0xa2    ; stack over/under flow
ERROR_A3_RUNSTATE       equ     0xa3    ; bad runstate offset
ERROR_A4_LOADER         equ     0xa4    ; kernel loader failed
ERROR_A5_TX             equ     0xa5    ; error in tx registers

;###########################################################################
;###########################################################################
;################################ VARIABLES ################################
;###########################################################################
;###########################################################################

;
; kk_*    - kernel public label
; vk_*    - kernel public byte variables
; bk_*    - kernel public bit  variables
;
; kki_*   - kernel private label
; vki_*   - kernel private byte variables
; bki_*   - kernel private bit  variables
;
; vaddr   - next free RAM byte
;

    variable vaddr=0


;###########################################################################
;################################ SFR REGISTER NOTES #######################
;###########################################################################

; FSR0 is used as a general purpose FSR register.  The main program may use it
; but must assume it may be scrambled when kk_main_run or other kernel
; functions are called.
;
; FSR1 is used by the kernel, but its value is always saved.  Therefore it
;      cannot be used in ISR rotines, but otherwise can be assumed to maintain
;      its value.  It is also used as a paramter in kk_eeprom_write.
;
; FSR2 is not used by the kernel at all
;

;###########################################################################
;################################ EEPROM LAYOUT ############################
;###########################################################################

EEPROM_A5           equ 0x0000  ; set to 0xa5 if valid
EEPROM_VERSION      equ 0x0001  ; set to EEPROM_VERSION_VALUE if valid
EEPROM_RUNSTATE     equ 0x0002  ; run state - see vki_runstate_bits
EEPROM_HOUR         equ 0x0003  ; last recorded hour
EEPROM_MINUTE       equ 0x0004  ; last recorded minute
EEPROM_END          equ 0x0005  ; first unused eeprom location



EEPROM_VERSION_VALUE    equ KK_VERSION



;###########################################################################
;################################ PERSISTANT GLOBAL VARIABLES ##############
;###########################################################################

;
; These variables are preserved across warm boot.
;
;===========================================================================
; These variables have specific addesses (DO NOT CHANGE THEM!!)
;===========================================================================
;
; The loader & debugger access these variables, so they should always stay
; at the same address.
;
vki_runstate_bits   equ 0x0000      ; kernel runstate bits
vk_error            equ 0x0001      ; current error code (0 if not in err mode)
vk_breakpt          equ 0x0002      ; current or most recent breakpoint code
vaddr=3

;===========================================================================
; RUNSTATE
;===========================================================================
vki_runstate_prompt     equ vaddr+0
vaddr+=1

;
; bits in vki_runstate_bits
;
#define bk_rs_debug     vki_runstate_bits,0 ; debug (always set in halt)
#define bk_rs_halt      vki_runstate_bits,1 ; halted (only run boot code)
#define bki_rs_breakpt  vki_runstate_bits,2 ; set if breakpoint occurred
#define bki_rs_error    vki_runstate_bits,3 ; set if error occurred

#define bki_rs_warmboot vki_runstate_bits,5 ; set to do a warm boot restart

;
; valid vki_runstate_bits values
;
RUNSTATE_BITS_BREAK     equ 0x07    ; xxxx x1xx breakpoint (debug)
RUNSTATE_BITS_ERROR     equ 0x0b    ; xxxx 10xx error occurred (debug)
                                    ; xx10 00xx warmboot
RUNSTATE_BITS_HALT      equ 0x03    ; xx00 001x halted (debug)
RUNSTATE_BITS_DEBUG     equ 0x01    ; xx00 0001 running in debug mode
RUNSTATE_BITS_RUN       equ 0x00    ; xx00 0000 running in non-debug mode

;===========================================================================
; state variables
;===========================================================================
vk_persisth         equ vaddr+0 ; # of bytes at end of memory to save on reset
vk_persistl         equ vaddr+1 ; # of bytes at end of memory to save on reset
vaddr+=2

;===========================================================================
; time variables
;===========================================================================
vk_hour             equ vaddr+0 ; hour   - 0-23=day0 24-47=day1 etc.  clamp 255
vk_minute           equ vaddr+1 ; minute - 0-59
vk_qsec             equ vaddr+2 ; quarter second - 0-239
vaddr+=3

;===========================================================================
; debugging variables
;===========================================================================
#if KK_VERSION<19
vk_breakpt1         equ vaddr+0     ; current or most recent breakpoint code
vk_breakpt2         equ vaddr+1     ; current or most recent breakpoint code
vk_breakpt3         equ vaddr+2     ; current or most recent breakpoint code
vk_breakpt4         equ vaddr+3     ; current or most recent breakpoint code
vk_breakpt5         equ vaddr+4     ; current or most recent breakpoint code
vaddr+=5
#endif

#if KK_VERSION>=19
#define bk_boot_serial_enabled  vk_boot0_bbits,0
#define bk_boot_first           vk_boot0_bbits,1
#define bk_boot_cold            vk_boot0_bbits,2
#define bk_boot_warm            vk_boot0_bbits,3
#define bk_boot_common          vk_boot0_bbits,4

vk_boot0_bbits      equ vaddr+0     ; boot bits (for debugging)
vk_boot0_runstate   equ vaddr+1     ; runstate at boot (from EEPROM)
vaddr+=2
#endif

vk_boot0_error      equ vaddr+0     ; vk_error at last boot
vk_boot1_error      equ vaddr+1     ; vk_error at previous boot
vk_boot2_error      equ vaddr+2     ; vk_error 2 boots ago
vk_boot0_rcon       equ vaddr+3     ; RCON at last boot
vk_boot1_rcon       equ vaddr+4     ; RCON at last boot
vk_boot2_rcon       equ vaddr+5     ; RCON at last boot
vk_boot3_rcon       equ vaddr+6     ; RCON at last boot
vk_boot0_stkptr     equ vaddr+7     ; STKPTR at last boot
vaddr+=8


vk_where            equ vaddr+0     ; where in program we are (top level)
vk_where2           equ vaddr+1     ; where in kernel subroutines we are
vk_iwhere           equ vaddr+2     ; where in isr we are (0=not in isr)
vaddr+=3

;===========================================================================
; persistant local variables
;===========================================================================
vki_last_tmr1h      equ vaddr+0 ; last checked TMR1H value
vki_tmp1            equ vaddr+1 ; temporary variable
vki_tmp2            equ vaddr+2 ; temporary variable
vki_tmp3            equ vaddr+3 ; temporary variable
vki_ten             equ vaddr+4 ; const: '9'+1
vaddr+=5

KERN_RAM_PERSIST    equ vaddr

;###########################################################################
;################################ GLOBAL VARIABLES #########################
;###########################################################################

;===========================================================================
; global bit variables
;===========================================================================

#define bk_qsec     vk_time_bits,0  ; set one main-loop every quarter sec
#define bk_minute   vk_time_bits,1  ; set one main-loop every minute
#define bk_hour     vk_time_bits,2  ; set one main-loop every hour

;===========================================================================
; parameter passing
;===========================================================================
vk_w                equ vaddr+0 ; w is restored from here in kk_jump
vaddr+=1

;###########################################################################
;################################ LOCAL VARIABLES ##########################
;###########################################################################


;===========================================================================
; generic variables
;===========================================================================
vki_lbits0          equ vaddr+0 ; bit variables
vaddr+=1

;===========================================================================
; saving values
;===========================================================================
vki_breakpt_w       equ vaddr+0 ; WREG when breakpt occurred
vaddr+=1


vki_fsr1l_save      equ vaddr+0 ; save FSR1 (in kki_serial_rx)
vki_fsr1h_save      equ vaddr+1 ; save FSR1 (in kki_serial_rx)
vaddr+=2

vki_led_val         equ vaddr+0 ; halt LED value
vaddr+=1

vki_isrl_save_s         equ vaddr+0 ; save s in isrl
vki_isrl_save_w         equ vaddr+1 ; save w in isrl
vki_isrl_save_fsr0l     equ vaddr+2 ; save FSR0L in isrl
vki_isrl_save_fsr0h     equ vaddr+3 ; save FSR0H in isrl
vaddr+=4

;===========================================================================
; time variables
;===========================================================================
vk_time_bits        equ vaddr+0 ; contains time bit variables. Cleared each loop
vaddr+=1

;===========================================================================
; serial tx variables
;===========================================================================
vk_tx_cnt           equ vaddr+0 ; # of chars in tx buffer
vki_tx_head         equ vaddr+1 ; where next char will go
vki_tx_tail         equ vaddr+2 ; next char to send
vki_tx_ctime        equ vaddr+3 ; TMR1H when last char was sent
vki_tx_cksum        equ vaddr+4 ; sum of chars sent on line
vaddr+=5

#if KK_VERSION>=17
vki_tx_gap_cnt      equ vaddr+0 ; chars left before gap insert
vaddr+=1
#endif

#define bki_tx_overflow vki_lbits0,0    ; set on overflow, clr on buf empty
#define bki_tx_docksum  vki_lbits0,1    ; set to send checksum each line

;===========================================================================
; serial rx variables
;===========================================================================

#define bki_rx_err_oflow    vki_rx_error,0      ; bit 0: overflow buffer
#define bki_rx_err_orun     vki_rx_error,OERR   ; bit 1: overrun error
#define bki_rx_err_frame    vki_rx_error,FERR   ; bit 2: frame error
#define bki_rx_err_badchar  vki_rx_error,3      ; bit 3: bad char
#define bki_rx_err_txfull   vki_rx_error,4      ; bit 4: tx buffer full

#define bki_rx_new          vki_rx_state,0      ; new char available
#define bki_rx_synerr       vki_rx_state,1      ; syntax error parsing command
#define bki_rx_write        vki_rx_state,2      ; write command
#define bki_rx_rw_ram       vki_rx_state,3      ; r/w ram command
#define bki_rx_rw_flash     vki_rx_state,4      ; r/w flash command
#define bki_rx_rw_eeprom    vki_rx_state,5      ; r/w eeprom command
#if KK_VERSION>=27
#define bki_rx_fdump        vki_rx_state,6      ; fast flash dump command
#define bki_tx_flushing     vki_rx_state,7      ; flushing tx buffer
#endif

KERNVAL_RX_STATE_READ   equ 0x38    ; all rw bits

vki_rx_error            equ vaddr+0 ; rx error bits
vki_rx_head             equ vaddr+1 ; next buffer byte to write
vki_rx_echo             equ vaddr+2
vki_rx_tail             equ vaddr+3 ; next byte to echo

vki_rx_state            equ vaddr+4 ; command state bits
vki_rx_cnt              equ vaddr+5 ; # data bytes in command
vki_rx_cmd              equ vaddr+6 ; current command (ASCII)
vki_rx_rw_ptrh          equ vaddr+7 ; r/w command data pointer (low)
vki_rx_rw_ptrl          equ vaddr+8 ; r/w command data pointer (high)
vki_rx_err_badchar      equ vaddr+9 ; bad rx char
vaddr+=10

;===========================================================================
; buffers
;===========================================================================
RAMBUF_TX       equ 0x100   ; serial transmit buffer (256 bytes)
RAMBUF_RX       equ 0x200   ; command byte buffer    (256 bytes)

;###########################################################################
;################################ RESET CODE ###############################
;###########################################################################

    org     0x0000
    bra     kk_boot

#if SIM
kk_breakpt:
    return
kk_error:
    return
#endif

;###########################################################################
;################################ ISR CODE #################################
;###########################################################################

    ;
    ; high priority isr
    ;
    org     0x0008
    goto    kk_app_isr_high

    ;
    ;  Data record at 0x0010
    ;
    ;  addr     value
    ;  ------   ----------
    ;  0x0010   0xa5
    ;  0x0011   0x5a
    ;  0x0012   KK_VERSION
    ;  0x0013   <unused>
    ;  0x0014   high byte of kk_app_main
    ;  0x0015   low  byte of kk_app_main
    ;  0x0016   <unused>
    ;  0x0017   <unused>
    ;
    org     0x0010
    db      0xa5, 0x5a
    db      KK_VERSION, 0
    db      HIGH kk_app_main, LOW kk_app_main
    db      0,0

    ;
    ; low priority isr
    ;
    org     0x0018
    movff   STATUS,vki_isrl_save_s
    movwf   vki_isrl_save_w
    movff   FSR0L,vki_isrl_save_fsr0l
    movff   FSR0H,vki_isrl_save_fsr0h
    DB_IWHERE   1

    bra     kki_isrl_begin

    org     0x40    ; 2nd 64-byte flash block

    ;
    ; If we reach here it means that the first 64 bytes are hosed (probably
    ; erased to 0xff which is a nop).  This probably means we are running
    ; the loader and it failed after clearing the first block of memory.
    ; Jump to the loader address to continue loading.
    ;
    goto    KERNVAL_LOADER_BEGIN

kki_isrl_begin:

#if KK_DEBUG_ISRSAVE
vki_sv_intcon       equ vaddr+0
vki_sv_intcon2      equ vaddr+1
vki_sv_intcon3      equ vaddr+2
vki_sv_PIE1         equ vaddr+3
vki_sv_PIE2         equ vaddr+4
vki_sv_PIR1         equ vaddr+5
vki_sv_PIR2         equ vaddr+6
vki_sv_IPR1         equ vaddr+7
vki_sv_IPR2         equ vaddr+8
vki_sv_RCON         equ vaddr+9
vki_sv_RCSTA        equ vaddr+10
vki_sv_TXSTA        equ vaddr+11
vki_sv_STKPTR       equ vaddr+12

vki_sv2_PIE1        equ vaddr+13
vki_sv2_PIR1        equ vaddr+14
vaddr+=15

    movff   INTCON,vki_sv_intcon
    movff   INTCON2,vki_sv_intcon2
    movff   INTCON3,vki_sv_intcon3
    movff   PIE1,vki_sv_PIE1
    movff   PIE2,vki_sv_PIE2
    movff   PIR1,vki_sv_PIR1
    movff   PIR2,vki_sv_PIR2
    movff   IPR1,vki_sv_IPR1
    movff   IPR2,vki_sv_IPR2
    movff   RCON,vki_sv_RCON
    movff   RCSTA,vki_sv_RCSTA
    movff   TXSTA,vki_sv_TXSTA
    movff   STKPTR,vki_sv_STKPTR
#endif

    ;
    ; jump to application low priority isr (unless halted)
    ;
kki_isrl_user:
    btfss   bk_rs_halt
    call    kk_app_isr_low

    ;
    ; serial RX isr
    ;
kki_isrl_rx:
    DB_IWHERE   2
#if SIM
    bcf     b_rcie

    ;
    ; SIM RX - put bytes to read at 0x300 (0 terminate)
    ;
vki_sim_rcreg   equ vaddr+0
vaddr+=1
    btfss   b_int1if
    bra     kki_isrl_return

    bcf     b_int1if
    lfsr    FSR0,0x300
    movf    POSTINC0,w
    bz      kki_isrl_return
    movwf   vki_sim_rcreg

vki_isrl_rx_sim_loop:
    movf    POSTDEC0,w
    movwf   POSTINC0
    movf    POSTINC0,w
    bnz     vki_isrl_rx_sim_loop


#endif
    ;
    ; got an RX character!
    ;
    ;  SPECIAL RX CHARACTERS:
    ;     any negative char: error
    ;     CR (0x0d):         end of command
    ;     LF (0x0a):         end of command
    ;     ^C (0x03):         ignore preceding command
    ;     ^X (24):           ignore preceding command
    ;
    ;  SPECIAL CHARS IN BUFFER:
    ;    0xff:              end of command
    ;    0xfe:              ignore preceding command
    ;    0xfd:              error
    ;    other negative:    error
    ;
    movf    vki_rx_error,w
    bnz     kki_isrl_rx_err_end     ; error - ignore incoming chars

    DB_IWHERE   3
    lfsr    FSR0,RAMBUF_RX
    movff   vki_rx_head,FSR0L   
    bsf     bki_rx_new
    btg     bki_led_serial
    movf    RCSTA,w
    andlw   0x06        ; FERR or OERR bits set?
    bnz     kki_isrl_rx_fault
#if KK_VERSION<18
    btfsc   bki_tx_overflow
    bra     kki_isrl_rx_txfull
#endif

    DB_IWHERE   4
#if SIM
    movf    vki_sim_rcreg,w
#else
    btfss   b_rcif              ; PIR1,5
    bra     kki_isrl_return

    movf    RCREG,w     ; get RX byte and clear RCIF
#endif

    bn      kki_isrl_rx_badchar
    bz      kki_isrl_rx_badchar
kki_isrl_rx_store:
    movwf   INDF0
    addlw   -0x0a
    bz      kki_isrl_rx_cr          ; got LF? store a 0xff
    addlw   0x0a-0x0d
    bz      kki_isrl_rx_cr          ; got CR? store a 0xff
    addlw   0x0d-3                  ; ctrl-C? store a 0xfe
    bz      kki_isrl_rx_kill
    addlw   3-24                    ; ctrl-X? store a 0xfe
    bz      kki_isrl_rx_kill

    DB_IWHERE   5
kki_isrl_rx_next:
    incf    vki_rx_head,f           ; next buffer loc
    incf    vki_rx_head,w           ; are we right before tail?
    subwf   vki_rx_tail,w
    bnz     kki_isrl_return

    ;
    ; overflowed buffer here (1 spot left)
    ; store error code in extra spot
    ;
    DB_IWHERE   6
    bsf     bki_rx_err_oflow
    movlw   0xfd
    movwf   INDF0
    bra     kki_isrl_rx_err_end

    ;
    ; end of line conditions:
    ;    rx error (store 0xfd)
    ;    badchar  (store 0xfd)
    ;    kill     (store 0xfe)
    ;    cr       (store 0xff)
    ;
kki_isrl_rx_fault:
    iorwf   vki_rx_error,f      ; set error bit 0x2 and/or 0x4
    bra     kki_isrl_rx_err

kki_isrl_rx_badchar:
    movwf   vki_rx_err_badchar
    bsf     bki_rx_err_badchar

#if KK_VERSION<18
    bra     kki_isrl_rx_err
kki_isrl_rx_txfull:
    bsf     bki_rx_err_txfull
#endif

kki_isrl_rx_err:
    movlw   0xff                ; 0xff -1 -1 = 0xfd = error
kki_isrl_rx_kill:
    decf    WREG,w              ; store 0xfe (kill) into buf
kki_isrl_rx_cr:
    decf    WREG,w              ; store 0xff (end-of-command)
    bra     kki_isrl_rx_store   

kki_isrl_rx_err_end:
    movf    RCREG,w             ; clear interrupt flag
    bcf     b_rcie              ; disable receive interrupts

kki_isrl_return:
#if KK_DEBUG_ISRSAVE
    movff   PIE1,vki_sv2_PIE1
    movff   PIR1,vki_sv2_PIR1
#endif

    DB_IWHERE   0
    movff   vki_isrl_save_fsr0h,FSR0H
    movff   vki_isrl_save_fsr0l,FSR0L
    movf    vki_isrl_save_w,w
    movff   vki_isrl_save_s,STATUS
    retfie  0


;###########################################################################
;################################ STRINGS ##################################
;###########################################################################

kki_str_base0:
kki_str_rx_syntax_err:
    db                  "Syntax\0"
kki_str_rx_badcmd_err:
    db                  "Bad Cmd\0"
kki_str_rx_illcmd_err:
    db                  "Illegal Cmd\0"
kki_str_ram:
    db                  "ram 0x\0"
kki_str_eeprom:
    db                  "eeprom 0x\0"
kki_str_flash:
    db                  "flash 0x\0"
kki_str_prompt_run:
    db                  "Run> \0"
kki_str_prompt_debug:
    db                  "Debug> \0"
kki_str_prompt_halt:
    db                  "Halt> \0"
kki_str_prompt_error:
    db                  "Error> \0"
kki_str_prompt_break:
    db                  "Break> \0"
kki_str_hello:
    db                  "Hello! Kernel 0x\0"
kki_str_error:
    db                  "Error: \0"
kki_str_error_rx:
    db                  "RX=\0"
kki_str_rarrow:
    db                  " -> \0"
kki_str_written:
    db                  "Written\0"
kki_str_tx_overflow:
    db                  "...tx err...\0"
kki_str_boot:
    db                  "==Boot==\0"
kki_str_errboot:
    db                  "==ErrBoot==\0"
kki_str_tick:
    db                  "tick\0"
kki_str_kill:
    db                  "<kill>\0"
kki_str_00:
    db                  "00:\0"

kki_str_help:
    db                  "H Halt \n"
    db                  "R Run\n"
    db                  "D Debug\n"
    db                  "G Go (brk/err) \n"
    db                  "J Jump \n"
    db                  "P Prog \n"
    db                  "E Eeprom \n"
    db                  "V Var\n"
#if KK_VERSION>=27
    db                  "F Fast dump\n"
#endif
    db                  "C Restart\n"
    db                  "M Warm Reset \n"
    db                  "B Cold Reset \n"
    db                  "S Cksum\n"
    db                  "I instructions \n\0"

;###########################################################################
;################################ SUBROUTINES ##############################
;###########################################################################

;
; kki_led_set   - set LED state to w
; kki_led_check - set LEDs to saved value
;
kki_led_set:
    movwf   vki_led_val
kki_led_check:
    movlw   KERNVAL_TRISB_LED_MASK
    iorwf   TRISB,f
    movf    vki_led_val,w
    iorlw   (~KERNVAL_TRISB_LED_MASK)&0xff
    andwf   TRISB,f
    return

;###########################################################################
;################################ BOOT CODE ################################
;###########################################################################

;===========================================================================
; kk_boot - reset entry point
;===========================================================================
kk_boot:
    movlw   KERNVAL_T1CON
    movwf   T1CON

kk_boot2:
#if (KK_CHIP!=18252)
    movlw   KERNVAL_OSCTUNE
    movwf   OSCTUNE
#endif

    movlw   KERNVAL_OSCCON
    movwf   OSCCON

    movlw   KERNVAL_T0CON
    movwf   T0CON

    ; avoid setting TMR1L,TMR1H
    ;  cold reset - arbitrary value
    ;  warm reset - continue with old value

    ;
    ; The following registers default values are fine
    ;
    ; WDTCON set to 0 by default (watchdog off)
    ; T2CON defaults to T2 disabled
    ; T3CON defaults to T3 disabled
    ; CCP1CON disabled by default
    ; CCP2CON disabled by default
    ; PIE1 all interrupts disabled by default
    ; PIE2 all interrupts disabled by default
    ; CMCON has all comparitors disabled by defalt
    ;

    ;
    ; all interrupts off
    ;
    clrf    INTCON

    movlw   KERNVAL_PORTA
    movwf   PORTA
    movlw   KERNVAL_TRISA
    movwf   TRISA

    movlw   KERNVAL_PORTB
    movwf   PORTB
    movlw   KERNVAL_TRISB
    movwf   TRISB

#if KK_VERSION<24
    movlw   KERNVAL_ADCON1
    movwf   PORTA
#endif

    movlw   KERNVAL_PORTC
    movwf   PORTC
    movlw   KERNVAL_TRISC
    movwf   TRISC

    movlw   KERNVAL_ADCON0
    movwf   ADCON0
    movlw   KERNVAL_ADCON1
    movwf   ADCON1
#if (KK_CHIP!=18252)
    movlw   KERNVAL_ADCON2
    movwf   ADCON2
#endif

    ;
    ; save first 0x100 bytes of ram to 0x500 (DEBUG)
    ;
    movlw   0x00
    lfsr    FSR0,0x0000
    lfsr    FSR1,0x0500
kki_zpdump_loop:
    movff   POSTINC0,POSTINC1
    decfsz  WREG,w
    bra     kki_zpdump_loop

    ;
    ; save some boot values
    ;
    movff   vk_boot2_rcon,vk_boot3_rcon
    movff   vk_boot1_rcon,vk_boot2_rcon
    movff   vk_boot0_rcon,vk_boot1_rcon
    movff   RCON,vk_boot0_rcon
    movff   STKPTR,vk_boot0_stkptr
#if KK_VERSION>=19
    clrf    vk_boot0_bbits
#endif

#if KK_DEBUG
    ;
    ; save and clear stack
    ;
    movlw   32
    lfsr    FSR0,0x400
kki_stkdump_loop:
    clrf    POSTINC0
    movff   TOSU,POSTINC0
    movff   TOSH,POSTINC0
    movff   TOSL,POSTINC0
    clrf    TOSU
    clrf    TOSH
    clrf    TOSL
    incf    STKPTR,f
    decfsz  WREG,w
    bra     kki_stkdump_loop
#endif

    clrf    STKPTR

kki_constants:
    ;
    ; always keep TBLPTRU, PCLATU, and EEADRH clear
    ;
    clrf    TBLPTRU
    clrf    PCLATU
#if (KK_CHIP!=18252)
    clrf    EEADRH
#endif

    DB_WHERE    1

#if KK_VERSION>=19
    ;
    ; serial enabled at boot time? (detect low = not attached)
    ;
    btfsc   bki_serial_detect
    bsf     bk_boot_serial_enabled
#endif

kki_eeprom_check:
    setf    EEADR       ; EEADR=0xff (next read will come from ++EEADR == 0)

    ;
    ; first byte should be 0xA5
    ;
    rcall   kk_eeprom_read
    addlw   -0xa5
    bnz     kki_boot_first

    ;
    ; second byte should match EEPROM_VERSION_VALUE
    ;
    rcall   kk_eeprom_read
    addlw   -EEPROM_VERSION_VALUE
    bnz     kki_boot_first

#if KK_VERSION>=19
    ;
    ; read runstate from eeprom
    ;
    rcall   kk_eeprom_read
    movwf   vk_boot0_runstate
#endif

    ;
    ; check for cause of reset
    ;
#if KK_VERSION>=19
    btfss   vk_boot0_rcon,POR
#else
    btfss   RCON,POR
#endif
    bra     kki_boot_cold   ; powerup reset

    ;
    ; user-requested warm boot?
    ;
    btfsc   bki_rs_warmboot
    bra     kki_boot_warm


    ;
    ; All other resets are error conditions:
    ;
    ; reset instruction  (RCON,RI is clear)
    ; stack overflow (STKPTR,STKFUL is set)
    ; stack underflow (STKPTR,STKUNF is set)
    ; watchdog reset (RCON,TO is clear)
    ; brown out reset (RCON,BOR is clear)
    ; MCLR reset (if none of the above are true)
    ;


;===========================================================================
; kki_boot_error - jump here if an error caused the reset
;===========================================================================

#if KK_DEBUG

kki_boot_error:
    DB_WHERE    2

    ;
    ; save old errors
    ;
    movff   vk_boot1_error,vk_boot2_error
    movff   vk_boot0_error,vk_boot1_error
    movff   vk_error,vk_boot0_error

    ;
    ; always run error code if not in RUNSTATE_RUN
    ;
    btfsc   bk_rs_debug
    bra     kki_boot_error2 ; debug mode - run error code

    ;
    ; In RUNSTATE_RUN most resets should continue, but reset button
    ; should jump to error code if serial port is connected.
    ; Figure out what kind of reset this is and do the right thing.
    ;

    ;
    ; if any of these bits in RCON are clear then cold boot:
    ;    0x10 RI   - reset instruction if 0
    ;    0x08 TO   - watch dog timeout if 0
    ;    0x02 POR  - power on reset if 0 (this is caught above)
    ;    0x01 BOR  - brown out reset if 0
    ;
    comf    vk_boot0_rcon,w     ; invert bits of RCON boot value
    andlw   0x1b                ; check RI, TO, POR, and BOR
    bnz     kki_boot_cold

    ;
    ; If these bits in STKPTR are set then cold boot:
    ;    0x80 STKFUL  - stack full
    ;    0x40 STKUNF  - stack undeflow
    ;
    movf    vk_boot0_stkptr,w
    andlw   0xc0                ; check STKFUL, and STKUNF
    bnz     kki_boot_cold

    ;
    ; Otherwise a mclr reset occurred - 
    ;


;
; Here one of these two situations has occurred:
;      - the reset button was pressed
;      - an error occurred while we were not in RUNSTATE_RUN
;
kki_boot_error2:
    ;
    ; If serial is low (not attached) then jump to coldboot
    ;
#if !SIM
    btfss   bki_serial_detect
    bra     kki_boot_cold
#endif

    ;
    ; Error occurred. Could be
    ;   reset instruction (RCON,RI is clear)
    ;   stack overflow    (STKPTR,STKFUL set)
    ;   stack underflow   (STKPTR,STKUNF set)
    ;   vk_error contains the error code
    ;

    SET_RUNSTATE    kki_runstate_error

    rcall   kk_serial_enable
    SOUT0   kki_str_errboot
    rcall   kki_cr_prompt

kki_boot_error_loop:
    rcall   kk_main_run
    rcall   kki_led_check

    ;
    ; loop in error until cleared
    ;
    btfsc   bki_rs_error
    bra     kki_boot_error_loop

    DB_WHERE    3

    SET_RUNSTATE    kki_runstate_halt

    clrf    vk_error

#endif

    bra     kki_boot_cold


;===========================================================================
; kki_boot_first - jump here on first kk_boot ever (or eeprom scrambled)
;===========================================================================
kki_boot_first:
    ;
    ; first time booting with this eeprom version
    ;

    ;
    ; init eeprom
    ;
    DB_WHERE    4
    EEPROM_BEGIN    EEPROM_A5       ; start writing at EEPROM_A5
    movlw   0xa5
    rcall   kk_eeprom_write         ; EEPROM_A5
    movlw   EEPROM_VERSION_VALUE
    rcall   kk_eeprom_write         ; EEPROM_VERSION
    movlw   kki_runstate_halt-kki_runstate_table
    rcall   kk_eeprom_write         ; EEPROM_RUNSTATE
    movlw   0x00
    rcall   kk_eeprom_write         ; EEPROM_HOUR
    movlw   0x00
    rcall   kk_eeprom_write         ; EEPROM_MINUTE

#if KK_VERSION>=19
    bsf     bk_boot_first
#endif

    ; fall through to kki_boot_cold

;===========================================================================
; kki_boot_cold - jump here for power-on resets
;===========================================================================
kki_boot_cold:

    ;
    ; save booted rcon val in TABLAT
    ; save boot bits in PRODH
    ;
    movff   vk_boot0_rcon,TABLAT
#if KK_VERSION>=19
    movff   vk_boot0_bbits,PRODH
#endif

    ;
    ; clear persistant memory
    ;
    lfsr    FSR0,0x000
    movlw   KERN_RAM_PERSIST
kki_boot_pmemclr:
    clrf    POSTINC0
    decfsz  WREG,w
    bra     kki_boot_pmemclr

    DB_WHERE    5

    ;
    ; Restore saved RCON from TABLAT
    ; Restore saved boot bits from PRODH
    ;
    movff   TABLAT,vk_boot0_rcon
#if KK_VERSION>=19
    movff   PRODH,vk_boot0_bbits
    bsf     bk_boot_cold
#endif

    ;
    ; initialize values
    ;
    clrf    TMR1H
    clrf    TMR1L
    clrf    vk_persistl
    clrf    vk_persisth

    ;
    ; read values from eeprom
    ;
    EEPROM_BEGIN    EEPROM_HOUR ; read starting from EEPROM_HOUR
    rcall   kk_eeprom_read          ; EEPROM_HOUR
    movwf   vk_hour
    rcall   kk_eeprom_read          ; EEPROM_MINUTE
    movwf   vk_minute

    ; fall through to kki_boot_warm

;===========================================================================
; kki_boot_warm - jump here for non-power-on-resets
;===========================================================================
kki_boot_warm:

#if KK_VERSION>=25
    bsf     bk_boot_warm
#endif

    bcf     bki_rs_warmboot
    ; fall through to kki_boot_common

;===========================================================================
; kki_boot_common - all kk_boot versions converge here
;===========================================================================
kki_boot_common:

#if KK_VERSION>=25
    bsf     bk_boot_common
#endif

    ;
    ; erase entire RAM except for persistant variables
    ;
    ; start at KERN_RAM_PERSIST (end of persistant memory)
    ; erase count is:
    ;    RAM size (0xf80)
    ;    - KERN_RAM_PERSIST     (persistant at start of mem)
    ;    - vk_persisth,l            (persistant at end of mem)
    ;
    DB_WHERE    6
    lfsr    FSR0,KERN_RAM_PERSIST

    comf    vk_persistl,w
    addlw   LOW (0xf80-KERN_RAM_PERSIST+1)
    movwf   vki_tmp2

    movlw   (HIGH (0xf80-KERN_RAM_PERSIST+1))+1
    subfwb  vk_persisth,w
    movwf   vki_tmp1

kki_boot_memclr:
    clrf    POSTINC0
    decfsz  vki_tmp2,f
    bra     kki_boot_memclr
    decfsz  vki_tmp1,f
    bra     kki_boot_memclr

    ;
    ; set runstate from eeprom
    ;
    EEPROM_READ1    EEPROM_RUNSTATE
#if KK_VERSION>=19
    movwf   vk_boot0_runstate
#endif
    rcall   kki_set_runstate

    ;
    ; enable serial communications
    ;
    DB_WHERE    7
    rcall   kk_serial_enable
    SOUT0   kki_str_boot
    rcall   kki_cr_prompt

    ;
    ; clear stack
    ;
    clrf    STKPTR

    ;
    ; clear error condition
    ;
    clrf    vk_error

    ; fall through to main loop

;###########################################################################
;################################ MAIN LOOP ################################
;###########################################################################

;
; An app can return to the main loop by jumping to kk_main_loop or by just
; returning.  In the return case control returns here and kk_main_loop is
; called again.
;
kki_main_loop2:
    rcall   kk_main_loop
    bra     kki_main_loop2

;===========================================================================
; kk_main_loop
;===========================================================================
kk_main_loop:
    DB_WHERE    8
    rcall   kk_main_run

    ;
    ; clear non-persistant mem & restart if bki_rs_warmboot is set
    ;
    btfsc   bki_rs_warmboot
    bra     kk_boot2

    ;
    ; jump to main app function.
    ; Main app function can be either:
    ;  1) an infinite loop with calls to kk_main_run, or
    ;  2) branch back to kk_main_loop repeatedly
    ;
    DB_WHERE    9
    btfss   bk_rs_halt
    bra     kk_app_main     ; call out to application
    DB_WHERE    10

    rcall   kki_led_check

    bra     kk_main_loop

;===========================================================================
; kk_main_run - do internal kk_main_loop tasks
;===========================================================================
kk_main_run:
    DB_WHERE2   1

#if SIM
    ;
    ; simulate time by incrementing T1 timer
    ;
    incf    TMR1L,f
    btfsc   STATUS,C
    incf    TMR1H,f

#if 1
    ;
    ; simulate rx
    ;
kki_sim_rx:
    bsf     b_int1ie
    lfsr    FSR0,0x300
    movf    POSTINC0,w
    bz      kki_sim_rx_none

    bsf     b_int1if


#if 1
    ;
    ; send all available chars at once
    ;
    bra     kki_sim_rx
#endif

kki_sim_rx_none:
#endif
#endif


    ;
    ; new chars arrived?
    ;
    DB_WHERE2   2
#if KK_VERSION<18
    btfsc   bki_tx_overflow
    bra     kki_main_run_time
#endif
#if KK_VERSION>=18
    bsf     b_rcie              ; enable serial interrupt ; PIE1,5
#endif
    btfsc   bki_rx_new
    rcall   kki_serial_rx_new
    bsf     b_rcie              ; enable serial interrupt ; PIE1,5

    ;
    ; check for time change
    ;
kki_main_run_time:
    DB_WHERE2   3
    clrf    vk_time_bits
    movf    vki_last_tmr1h,w
    subwf   TMR1H,w
    andlw   0xfc
    bnz     kki_time_update

kki_main_run_endtime:

    ;
    ; transmit next byte?
    ;
    DB_WHERE2   4
    btfsc   b_txif          ; PIR1,4
    rcall   kki_serial_tx   ; transmit next byte
    DB_WHERE2   5

    ;
    ; if not halted then turn rx interrupts off again
    ;
    btfss   bk_rs_halt
    bcf     b_rcie          ; PIE1,5

    DB_WHERE2   0
    return

;###########################################################################
;################################ TIME FUNCTION ############################
;###########################################################################

    ;
    ; called approx every 1/4 sec.
    ; w contains ((number of quarter seconds since last update) * 4)
    ; (always a multiple of 4)
    ;
    ; kk_main_run Must be called at least once every 4 seconds or this
    ; code will fail with an error.
    ;
    ; (Note: time will be silently lost if kk_main_run is not called at least
    ;        every 16 sec)
    ;
    ; DO NOT RETURN - jump back to kki_main_run_endtime
    ;
kki_time_update:
    bsf     bk_qsec

    ;
    ; blink the alive LED (PORTC alive)
    ;
    btg     bki_alive

    ;
    ; in halt modes blink the alive LED (PORTB alive)
    ;
    btg     bki_led_alive

    ;
    ; add the time in.  Divide by 4 to get quarter-seconds
    ; (low 2 bits of w are never 0 here)
    ;
    addwf   vki_last_tmr1h,f
    rrncf   WREG,w
    rrncf   WREG,w
    addwf   vk_qsec,f

    ;
    ; did vk_qsec overflow by 4 or more seconds?
    ;
    bc      kki_time_4sec_wrap

    ;
    ; check for vk_qsec overflow
    ;
    movlw   -240
    addwf   vk_qsec,w
    bnc     kki_main_run_endtime
    movwf   vk_qsec
    
    ;
    ; vk_qsec overflowed - increment minute
    ;
    bsf     bk_minute
    incf    vk_minute,f

    ;
    ; check for minute overflow
    ;
    movlw   -60
    addwf   vk_minute,w
#if KK_VERSION>=26
    bnz     kki_time_save
#else
    bnz     kki_main_run_endtime
#endif
    movwf   vk_minute

    ;
    ; increment hour
    ;
    bsf     bk_hour

#if KK_VERSION>=28
    incf    vk_hour,f
    movlw   0x7f
    btfsc   vk_hour,7
    movwf   vk_hour     ; clamp to 0x7f
#else
    infsnz  vk_hour,f
    setf    vk_hour     ; clamp to 0xff
#endif

kki_time_save:
    btfss   vk_hour,7           ; do not save time way after game is over
    rcall   kki_save_time
    bra     kki_main_run_endtime


    ;
    ; ERROR: waited too long (over 4 sec) between calls to kk_main_run
    ;
kki_time_4sec_wrap:
    ERR     ERROR_A1_TIME4SEC


;###########################################################################
;################################ SERIAL FUNCTIONS #########################
;###########################################################################

    ;
    ; Convert ascii hex digit (in w) to binary nibble value (in w).
    ; Set bki_rx_synerr if not hex digit
    ;
kki_serial_rx_hexdig:
    addlw   -(1+'9')
    bc      kki_serial_rx_hexadig
    addlw   (1+'9')-'0'
    btfss   STATUS,C
kki_serial_rx_hexdig_err:
    bsf     bki_rx_synerr
    return

kki_serial_rx_hexadig:
    andlw   0xdf        ; toupper
    addlw   (1+'9')-(1+'F')
    bc      kki_serial_rx_hexdig_err
    addlw   (1+'F')-'A'
    bnc     kki_serial_rx_hexdig_err
    addlw   10
    return
    


    ;
    ; kki_serial_rx_new loops here to echo chars
    ;
kki_serial_rx_echo:
    ;
    ; echo character at vki_rx_echo
    ;
    lfsr    FSR0,RAMBUF_RX
    movwf   FSR0L
    movf    INDF0,w
    bn      kki_serial_rx_eoc   ; end of command

    ;
    ; replace ctrl chars with question marks
    ;
    addlw   -0x20
    btfsc   STATUS,N
    movlw   '?'-0x20
    addlw   0x20

    rcall   kk_cout
kki_serial_rx_next:
    incf    vki_rx_echo,f

;===========================================================================
; kki_serial_rx_new - process newly arrived chars (SERIAL ENTRY POINT)
;===========================================================================
kki_serial_rx_new:
    DB_WHERE2   0x10
    bcf     bki_rx_new
    movf    vki_rx_echo,w
    cpfseq  vki_rx_head
    bra     kki_serial_rx_echo
    return

#if KK_VERSION>=27
kki_end_flush:
    bsf     bki_rx_new
    bcf     bki_tx_flushing
    return
#endif

;
; got end-of-command:
;   w==0xff   command 
;   w==0xfe   kill command
;   else      error
;
kki_serial_rx_eoc:
    
#if KK_VERSION>=27
    ;
    ; if flushing then just cancel flush and return
    ;
    btfsc   bki_tx_flushing
    bra     kki_end_flush
#endif

    infsnz  WREG,w
    bra     kki_serial_rx_cmd   ; 0xff = got command
    infsnz  WREG,w
    bra     kki_serial_rx_kill  ; 0xfe = got kill

kki_serial_rx_error:
    DB_WHERE2   0x11
    SOUT0   kki_str_error
    SOUT0   kki_str_error_rx
    movf    vki_rx_error,w
    rcall   kk_outbyte
    COUT    ' '
    movf    vki_rx_err_badchar,w
    rcall   kk_outbyte
    bra     kki_rx_init

kki_serial_rx_kill:
    DB_WHERE2   0x12
    SOUT0   kki_str_kill
    bra     kki_serial_rx_cmd_end2

    ;
    ; null command - continue printing or do nothing
    ;
kki_serial_rx_cmd_null:
    DB_WHERE2   0x13
    bcf     bki_rx_write            ; do not write
    movf    vki_rx_state,w
    andlw   KERNVAL_RX_STATE_READ   ; any r/w bits set?
    bz      kki_serial_rx_cmd_end
    movlw   16
    movwf   vki_rx_cnt              ; show 16 bytes
    btfss   bki_rx_rw_ram
kki_rx_rw_entry1:
    bra     kki_rx_rw_entry

    ;
    ; Ram - do not read into SFR (0x0f80 and above)
    ;
    DB_WHERE2   0x14
    movf    vki_rx_rw_ptrh,w
    addlw   -0x0f
    bnz     kki_rx_rw_entry1
    movf    vki_rx_rw_ptrl,w
    addlw   0x90
    bnc     kki_rx_rw_entry1
    clrf    vki_rx_state            ; in sfr - cancel read command
    bra     kki_serial_rx_cmd_end

;
; got command
;   starts at vki_rx_tail
;   ends at   vki_rx_echo
;
kki_serial_rx_cmd:
    DB_WHERE2   0x15
    movff   FSR1L,vki_fsr1l_save
    movff   FSR1H,vki_fsr1h_save

    ;
    ; got complete command - echo CR
    ;
    rcall   kk_outcr

    ;
    ; FSR0 = FSR1 = RAMBUF_RX + tail
    ;
    movlw   HIGH RAMBUF_RX
    movwf   FSR0H
    movwf   FSR1H
    movf    vki_rx_tail,w
    movwf   FSR0L
    movwf   FSR1L

    movf    INDF0,w
    bn      kki_serial_rx_cmd_null
    movwf   vki_rx_cmd              ; save cmd char
    clrf    vki_rx_state
    
    ;
    ; convert rest of command from hex
    ;
kki_serial_rx_cvt:
    DB_WHERE2   0x16
    incf    FSR0L,f
    movf    INDF0,w
    bn      kki_serial_rx_cmd_parse


    addlw   -0x20
    bz      kki_serial_rx_cvt       ; skip spaces
    addlw   0x20-':'
    bz      kki_serial_rx_cvt       ; skip colons
    addlw   ':'
    rcall   kki_serial_rx_hexdig

    swapf   WREG,w
    movwf   INDF1

    incf    FSR0L,f
    movf    INDF0,w
    rcall   kki_serial_rx_hexdig
    iorwf   INDF1,f
    incf    FSR1L,f

    btfss   bki_rx_synerr       ; set by kki_serial_rx_hexdig if non hex digit
    bra     kki_serial_rx_cvt

    bra     kki_rx_cmd_err_syntax

kki_rx_outerr:
    movwf   vki_tmp2
    DB_WHERE2   0x13    
    rcall   kk_outcr
    SOUT0   kki_str_error
    movf    vki_tmp2,w
    rcall   kki_sout_base0
    bra     kki_serial_rx_cmd_end

    ;
    ; subroutine: get next data byte
    ;
kki_fsr1_nextbyte:
    movf    INDF1,w
    incf    FSR1L,f
    return

;
; call here after command is processed
;
kki_serial_rx_cmd_end:
    movff   vki_fsr1l_save,FSR1L
    movff   vki_fsr1h_save,FSR1H

;
; set tail pointer to next command buffer location
;
kki_serial_rx_cmd_end2:
    DB_WHERE2   0x17
    incf    vki_rx_echo,w
    movwf   vki_rx_echo         ; echo++
    movwf   vki_rx_tail         ; echo --> tail
    bsf     bki_rx_new
    DB_WHERE2   0
    bra     kki_cr_prompt

kki_serial_rx_cmd_parse:
    ;
    ; Command table
    ;      <addr>  = 2 byte address in hex (0-9, a-z)
    ;      <data>  = 1 byte of data in hex (0-9, a-z)
    ;      <pdata> = 64 bytes of program data in hex (0-9, a-z)
    ;
    ; In non-debug mode ONLY the  <addr>H  is accepted.
    ;
    ; ^C              - immediate abort line
    ; ^X              - immediate abort line
    ; Hbeefb0550      - halt with password
    ; H               - halt
    ; R               - run in non-debug mode
    ; D               - debug - run in debug mode
    ; J<addr>         - jump to subroutine at <addr>
    ; P<addr>         - list 64 bytes of program memory
    ; P<addr>:<pdata> - write 64 bytes to <addr> - bytes follow <addr>
    ; E<addr>         - show eeprom byte
    ; E<addr>:<data>  - write eeprom byte
    ; V<addr>         - show variable byte
    ; V<addr>:<data>  - write variable byte
    ; F<start><end>   - fast dump of prog mem.  <start> and <end> are hi byte.
    ; C               - clear non-persistant memory and restart (keeps time)
    ; M               - warm reset chip (will gain/lose less than 1sec)
    ; B               - boot (cold reset) chip (lose up to 1 minute)
    ; I               - instructions
    ; S               - toggle checksum mode
    ;
    DB_WHERE2   0x18
    movf    FSR1L,w
    movwf   vki_rx_cnt
    movf    vki_rx_tail,w
    subwf   vki_rx_cnt,f            ; # of data bytes
    movwf   FSR1L                   ; point to first data byte
    movf    vki_rx_cmd,w            ; this is the command byte

    addlw   -'H'
    bz      kki_rx_cmd_halt
    addlw   'H'-'I'
    bz      kki_rx_cmd_help

    btfss   bk_rs_debug
    bra     kki_rx_cmd_err_badcmd   ; only halt command allowed if non-debug
    
    addlw   'I'-'R'
    bz      kki_rx_cmd_run
    addlw   'R'-'D'
    bz      kki_rx_cmd_debug
    addlw   'D'-'J'
    bz      kki_rx_cmd_jump
    addlw   'J'-'P'
    bz      kki_rx_cmd_prog
    addlw   'P'-'E'
    bz      kki_rx_cmd_eeprom
    addlw   'E'-'V'
    bz      kki_rx_cmd_varable
    addlw   'V'-'M'
    bz      kki_rx_cmd_reset
    addlw   'M'-'B'
    bz      kki_rx_cmd_boot
    addlw   'B'-'C'
    bz      kki_rx_cmd_clear
    addlw   'C'-'S'
    bz      kki_rx_cmd_cktoggle
#if KK_VERSION>=27
    addlw   'S'-'F'
    bz      kki_rx_cmd_fastdump1
#endif

    ; fall through to badcmd

kki_rx_cmd_err_badcmd:
    movlw   kki_str_rx_badcmd_err-kki_str_base0
    bra     kki_rx_outerr

kki_rx_cmd_err_syntax:
    movlw   kki_str_rx_syntax_err-kki_str_base0
    bra     kki_rx_outerr

kki_rx_cmd_cktoggle:
    btg     bki_tx_docksum
    bra     kki_serial_rx_cmd_end

kki_rx_cmd_help:
    SOUT0   kki_str_help
    bra     kki_serial_rx_cmd_end

kki_rx_cmd_run:
    SET_RUNSTATE    kki_runstate_run
    bra     kki_serial_rx_cmd_end

kki_rx_cmd_debug:
    SET_RUNSTATE    kki_runstate_debug
    bra     kki_serial_rx_cmd_end

kki_rx_cmd_halt:
    btfsc   bk_rs_debug
    bra     kki_rx_cmd_dbhalt

    ;
    ; check for password: 0xbeefb055
    ;
    movf    vki_rx_cnt,w    ; # of bytes of data
    addlw   -4              ; need 4 bytes
    bnz     kki_rx_cmd_err_illcmd

    rcall   kki_fsr1_nextbyte
    addlw   -0xbe
    bnz     kki_rx_cmd_err_syntax
    rcall   kki_fsr1_nextbyte
    addlw   -0xef
    bnz     kki_rx_cmd_err_syntax
    rcall   kki_fsr1_nextbyte
    addlw   -0xb0
    bnz     kki_rx_cmd_err_syntax
    rcall   kki_fsr1_nextbyte
    addlw   -0x55
    bnz     kki_rx_cmd_err_syntax
    
    ;
    ; password success - transition to RUNSTATE_HALT
    ;
kki_rx_cmd_dbhalt:
    rcall   kk_clear_interrupts
    SET_RUNSTATE    kki_runstate_halt
    bra     kki_serial_rx_cmd_end

kki_rx_cmd_jump:
    movf    vki_rx_cnt,w    ; # bytes
    addlw   -2
    bnz     kki_rx_cmd_err_syntax

    rcall   kki_rx_cmd_jump2
    bra     kki_serial_rx_cmd_end

kki_rx_cmd_jump2:
    rcall   kki_fsr1_nextbyte
    movwf   PCLATH
    rcall   kki_fsr1_nextbyte
    movwf   PCL         ; this jumps to <addr>
                        ; return will come back to rcall above
    
kki_rx_cmd_clear:
    bsf     bki_rs_warmboot ; do a warm restart at end of main loop
    bra     kki_serial_rx_cmd_end

kki_rx_cmd_boot:
    clrf    RCON,POR    ; simulate a cold boot
kki_rx_cmd_reset:
    bsf     bki_rs_warmboot ; do a warm reset
    reset

    ;
    ; illegal command
    ;
kki_rx_cmd_err_illcmd:
    movlw   kki_str_rx_illcmd_err-kki_str_base0
    bra     kki_rx_outerr

#if KK_VERSION>=27
    ;
    ; dump blocks of flash mem
    ;
kki_rx_cmd_fastdump1:
    bsf     bki_rx_fdump
    bra     kki_rx_rw_cnt2
#endif

    ;
    ; read/write 64 bytes of flash mem
    ;
kki_rx_cmd_prog:
    bsf     bki_rx_rw_flash
    movlw   66              ; prog cmd is 66 bytes
    bra     kki_rx_rw

    ;
    ; read/write an eeprom value
    ;
kki_rx_cmd_eeprom:
    bsf     bki_rx_rw_eeprom
    bra     kki_rx_rw3

    ;
    ; read/write ram
    ;
kki_rx_cmd_varable:
    bsf     bki_rx_rw_ram

kki_rx_rw3:
    movlw   3       ; eeprom/ram write is 3 bytes

kki_rx_rw:
    btfss   bk_rs_halt      ; only allow write if halted
    bra     kki_rx_rw_cnt2  

    ;
    ; check vki_rx_cnt
    ;   ==2  read
    ;   ==w  write (3 for eeprom/var  66 for prog)
    ;
    bsf     bki_rx_write        ; write command?
    subwf   vki_rx_cnt,w
    bz      kki_rx_rw_goodcnt

kki_rx_rw_cnt2:
    bcf     bki_rx_write        ; read command?
    movf    vki_rx_cnt,w
    addlw   -2
    bnz     kki_rx_cmd_err_syntax

kki_rx_rw_goodcnt:
    DB_WHERE2   0x19
    rcall   kki_fsr1_nextbyte
    movwf   vki_rx_rw_ptrh
    rcall   kki_fsr1_nextbyte
    movwf   vki_rx_rw_ptrl
    movlw   1
    movwf   vki_rx_cnt          ; # bytes to read

#if KK_VERSION>=27
    btfsc   bki_rx_fdump
    bra     kki_rx_cmd_fastdump
#endif

kki_rx_rw_entry:
    ; print string for type of mem
    movlw   kki_str_ram-kki_str_base0
    btfsc   bki_rx_rw_eeprom
    movlw   kki_str_eeprom-kki_str_base0
    btfsc   bki_rx_rw_flash
    movlw   kki_str_flash-kki_str_base0
    rcall   kki_sout_base0

    ; print address
    movf    vki_rx_rw_ptrh,w
    rcall   kk_outbyte
    movf    vki_rx_rw_ptrl,w
    rcall   kk_outbyte
    COUT    ':'

    ;
    ; if reading, or if writing anything other than flash, then show byte
    ;
kki_rx_rw_loop:
    rcall   kki_rx_readbyte         ; read byte
    btfsc   bki_rx_write
    btfss   bki_rx_rw_flash
    rcall   kk_outbyte              ; display byte

    btfsc   bki_rx_write
    bra     kki_rx_write        ; do write

    ;
    ; loop read, or done
    ;
    COUT    0x20
    infsnz  vki_rx_rw_ptrl,f    ; point to next byte
    incf    vki_rx_rw_ptrh,f
    decfsz  vki_rx_cnt,f        ; check cnt
    bra     kki_rx_rw_loop
    bra     kki_serial_rx_cmd_end   ; done

    ;
    ; write byte(s)
    ;
kki_rx_write:
    DB_WHERE2   0x1a
    SOUT0   kki_str_rarrow
    movf    INDF1,w

kki_rx_write_ram:
    btfss   bki_rx_rw_ram
    bra     kki_rx_write_eeprom

    movff   vki_rx_rw_ptrl,FSR0L
    movff   vki_rx_rw_ptrh,FSR0H
    movwf   INDF0
    rcall   kk_outbyte
    bra     kki_serial_rx_cmd_end

kki_rx_write_eeprom:
    btfss   bki_rx_rw_eeprom
    bra     kki_rx_write_flash

    decf    EEADR,f
    rcall   kk_eeprom_write

    movf    INDF1,w
    rcall   kk_outbyte
    bra     kki_serial_rx_cmd_end



kki_rx_write_flash:
    ;
    ; cannot write flash in breakpt or error modes or if not halted
    ;
    btfss   bk_rs_halt
    bra     kki_rx_cmd_err_illcmd
    btfsc   bki_rs_error
    bra     kki_rx_cmd_err_illcmd
    btfsc   bki_rs_breakpt
    bra     kki_rx_cmd_err_illcmd

    ;
    ; check for unaligned write
    ;
    movf    vki_rx_rw_ptrl,w
    movwf   TBLPTRL
    andlw   0x3f
    bnz     kki_rx_cmd_err_illcmd   ; error: not 64 byte aligned

    movf    vki_rx_rw_ptrh,w
    movwf   TBLPTRH

    ;
    ; check for clobbering boot code
    ;
    movlw   LOW  (-kk_app_main)
    addwf   TBLPTRL,w
    movlw   ((-kk_app_main)>>8)&0xff
    addwfc  TBLPTRH,w
    bnc     kki_rx_cmd_err_illcmd   ; error: overwriting boot area

    ;
    ; write 64 bytes from FSR1 to TBLPTRH,L
    ;
    rcall   kk_flash_write

    SOUT0   kki_str_written
    bra     kki_serial_rx_cmd_end


;
; Functions to read & display bytes of memory
;
kki_rx_readbyte:
    movf    vki_rx_rw_ptrh,w
    btfsc   bki_rx_rw_eeprom
    bra     kki_rx_readbyte_eeprom
    btfsc   bki_rx_rw_flash
    bra     kki_rx_readbyte_flash

kki_rx_readbyte_ram:
    movwf   FSR0H
    movff   vki_rx_rw_ptrl,FSR0L
    movf    INDF0,w
    return

kki_rx_readbyte_eeprom:
#if (KK_CHIP!=18252)
    movwf   EEADRH
#endif
    decf    vki_rx_rw_ptrl,w
    movwf   EEADR
    rcall   kk_eeprom_read
#if (KK_CHIP!=18252)
    clrf    EEADRH
#endif
    return

kki_rx_readbyte_flash:
    movwf   TBLPTRH
    movff   vki_rx_rw_ptrl,TBLPTRL
    tblrd*
    movf    TABLAT,w
    return

#if KK_VERSION>=27
;
; Fast dump command
;
kki_rx_cmd_fastdump:
    bsf     bki_tx_flushing

    movf    vki_rx_rw_ptrh,w
    rcall   kk_outbyte
    SOUT0   kki_str_00

    clrf    TBLPTRL
    rcall   kki_rx_cmd_fastdump_go

    movlw   0x40
    movwf   TBLPTRL
    rcall   kki_rx_cmd_fastdump_go

    movlw   0x80
    movwf   TBLPTRL
    rcall   kki_rx_cmd_fastdump_go

    movlw   0xc0
    movwf   TBLPTRL
    rcall   kki_rx_cmd_fastdump_go

    rcall   kk_outcr

    incf    vki_rx_rw_ptrh,f
    movf    vki_rx_rw_ptrh,w
    subwf   vki_rx_rw_ptrl,w
    btfsc   bki_tx_flushing
    bnz     kki_rx_cmd_fastdump

    ;
    ; done - finished or cancelled by new command arriving
    ;
    bcf     bki_rx_fdump
    bcf     bki_tx_flushing
    bra     kki_serial_rx_cmd_end   

kki_rx_cmd_fastdump_go:
    movf    vki_rx_rw_ptrh,w
    movwf   TBLPTRH
kki_rx_cmd_fastdump_go_loop:
    tblrd*+
    movf    TABLAT,w
    rcall   kk_outbyte

    movf    TBLPTRL,w
    andlw   0x3f
    bnz     kki_rx_cmd_fastdump_go_loop
    bra     kki_tx_flush_sub

#endif

;===========================================================================
; kki_prompt - print the prompt
;===========================================================================
kki_cr_prompt:
    rcall   kk_outcr

kki_prompt:
    ;
    ; error prompt includes error number
    ;
    movf    vk_error,w
    btfsc   bki_rs_error
    bra     kki_prompt_byte

    ;
    ; breakpoint prompt includes breakpoint number
    ;
    movf    vk_breakpt,w
    btfss   bki_rs_breakpt
    bra     kki_prompt_string

kki_prompt_byte:
    rcall   kk_outbyte      ; print the byte
    COUT    ' '             ; space following byte

kki_prompt_string:
    movf    vki_runstate_prompt,w
    bra     kki_sout_base0

;===========================================================================
; kk_serial_enable - enable & initialize serial communication
;===========================================================================
kk_serial_enable:
    DB_WHERE2   0x1b
    rcall   kk_clear_interrupts ;TODO: is this needed?

    rcall   kk_set_osc_8mhz     ; setup to work in fast mode
    clrf    RCSTA               ; reset serial port
    bsf     TRISC,TX

#if 1
    ;
    ; delay - let serial port cool off
    ;
    clrf    vki_tmp1
kk_serial_enable_delay:
    decfsz  vki_tmp1,f
    bra     kk_serial_enable_delay
#endif

    movlw   KERNVAL_TXSTA
    movwf   TXSTA
#if (KK_CHIP!=18252)
    movlw   KERNVAL_BAUDCON
    movwf   BAUDCON
    clrf    SPBRGH
#endif
    movlw   KERNVAL_SPBRG
    movwf   SPBRG


    ;
    ; constant vki_ten = '9'+1
    ;
    movlw   '9'+1
    movwf   vki_ten

    ;
    ; setup tx buffer
    ;
    clrf    vk_tx_cnt
    clrf    vki_tx_head
    clrf    vki_tx_tail
    clrf    vki_tx_cksum
    bcf     bki_tx_overflow
    bsf     bki_tx_docksum
    rcall   kk_outcr
    SOUT0   kki_str_hello

#if KK_VERSION>=14
    movlw   KK_VERSION
    rcall   kk_outbyte
#endif


    ;
    ; setup rx variables
    ;
kki_rx_init:
    DB_WHERE2   0x1c
    bcf     b_rcie          ; disable interrupts ; PIE1,5
    bcf     b_cren          ; clear overrun error (if any)
    movlw   KERNVAL_RCSTA
    movwf   RCSTA       ; clear errors & enable serial port

    clrf    vki_rx_rw_ptrl
    clrf    vki_rx_rw_ptrh
    clrf    vki_rx_cnt
    clrf    vki_rx_state

    clrf    vki_rx_tail
    clrf    vki_rx_echo
    clrf    vki_rx_head
    clrf    vki_rx_error
    movlw   ' '
    movwf   vki_rx_err_badchar
    
    ;
    ; print prompt, setup rx logic, and return
    ;
    bra     kki_cr_prompt


;===========================================================================
; kk_serial_disable - disable serial communication
;===========================================================================
kk_serial_disable:
    DB_WHERE2   0x1d
    bcf     RCSTA,SPEN      ; disable serial port
    bcf     TRISC,TX        ; output high on TX pin
#if KK_VERSION>=18
    bsf     bki_tx_overflow ; avoid adding chars to buffer
    clrf    vk_tx_cnt       ; make buffer look empty
#else
    setf    vk_tx_cnt       ; keep cout from enabling b_txen
#endif
    bcf     b_txen          ; disable b_txif bit ; TXSTA,5
    return

;===========================================================================
; kki_serial_tx - transmit a character
;===========================================================================
;
; 32768 Hz / 8 = 4096 HZ
; TMR1L wraps 4096Hz/256 = 16Hz
; TMR1H wraps 16Hz/256 = every 16 seconds
;
; 9600 baud:
;  104.166 usec per bit
; 1041.66  usec per byte (10 bits)
;
;
kki_serial_tx:
    DB_WHERE2   0x20

#if 1
    ;
    ; sanity check tx registers
    ;
    movf    vki_tx_tail,w
    subwf   vki_tx_head,w
    subwf   vk_tx_cnt,w
    bz      kki_serial_tx_sane1
    ERR     ERROR_A5_TX
kki_serial_tx_sane1:
#endif  

    ;
    ; chars left to send?
    ;
    movf    vk_tx_cnt,w
    bz      kki_serial_tx_done  

    ;
    ; point FSR0 at buffer
    ;
    lfsr    FSR0,RAMBUF_TX
    movff   vki_tx_tail,FSR0L

#if KK_VERSION>=17
    ;
    ; pause every so often to avoid messed up transmission
    ;
    dcfsnz  vki_tx_gap_cnt,f
    bra     kki_serial_tx_mt
#endif

    ;
    ; get the char
    ;
    movf    INDF0,w

    ;
    ; if tx is empty then jump to kki_serial_tx_mt
    ;
    btfsc   b_trmt              ; TXSTA,1
    bra     kki_serial_tx_mt

kki_serial_tx_xmit:
    movwf   TXREG
    incf    vki_tx_tail,f

    ;
    ; remember when the last char was sent (16ths of a sec)
    ;
    movff   TMR1H,vki_tx_ctime

    decfsz  vk_tx_cnt,f
    return

    ;
    ; just sent last char in buffer - check & clear overflow
    ;
    btfss   bki_tx_overflow
    return

    ;
    ; clear overflow condition & send overflow string
    ;
    DB_WHERE2   0x21
    bcf     bki_tx_overflow
    rcall   kk_outcr
    SOUT0   kki_str_tx_overflow
    bra     kk_outcr

    ;
    ; empty - wait at least 1/16 sec between chars that are not adjacent
    ; (This fixes timing bug)
    ;
kki_serial_tx_mt:
    DB_WHERE2   0x22

#if KK_VERSION>=17
    ;
    ; allow 40 more chars before next forced gap
    ;
    movlw   40
    movwf   vki_tx_gap_cnt
#endif

    movf    vki_tx_ctime,w
    subwf   TMR1H,w
    andlw   0xfe
    bz      kki_serial_tx_return    ; have not waited long enough

    ;
    ; waited long enough - send the next char
    ;
    movf    INDF0,w
    bra     kki_serial_tx_xmit

    ;
    ; no more characters to send
    ; turn off xmit (but only after empty for at least 1/16 sec)
    ;
kki_serial_tx_done:
    DB_WHERE2   0x23
    btfss   b_trmt  ; TXSTA,1
    return          ; not empty - leave it on

    movf    vki_tx_ctime,w
    subwf   TMR1H,w
    andlw   0xfe
    bz      kki_serial_tx_return    ; have not waited long enough

    ;
    ; waited long enough - turn xmit off
    ;
    bcf     b_txen      ; TXSTA,5

kki_serial_tx_return:
    DB_WHERE2   0
    return

;===========================================================================
; kk_outcr - output linefeed
;===========================================================================
kk_outcr:
    btfss   bki_tx_docksum
    bra     kki_outcr2
    movlw   '@'
    rcall   kki_cout2
    movf    vki_tx_cksum,w
    rcall   kk_outbyte
kki_outcr2:
    movlw   '\n'
    rcall   kki_cout2
    clrf    vki_tx_cksum
    return

;===========================================================================
; kk_outbyte - output byte in w
;===========================================================================
kk_outbyte:
    movwf   vki_tmp1
    swapf   WREG,w
    rcall   kki_outnib
    movf    vki_tmp1,w

    ; fall through to kk_outnib

kki_outnib:
    andlw   0x0f
    addlw   '0'
    cpfsgt  vki_ten
    addlw   'A'-'0'-10

    ; fall through to kk_outc

;===========================================================================
; kk_cout - output character in w (does not modify W)
;===========================================================================
kk_cout:
    addlw   -0x0a
    bz      kk_outcr
    addlw   0x0a
    addwf   vki_tx_cksum,f          ; add into checksum

kki_cout2:
#if KK_VERSION>=18
    btfsc   bki_tx_overflow
    return
#endif
    incf    vk_tx_cnt,f
    bz      kki_cout_overflow       ; buffer full?

    ;
    ; store char into buffer
    ;
    lfsr    FSR0,RAMBUF_TX
    movff   vki_tx_head,FSR0L
    movwf   INDF0
    incf    vki_tx_head,f
    btfss   b_txen
    bsf     b_txen              ; enable kki_serial_tx TXSTA,5
    return

kki_cout_overflow:
    decf    vk_tx_cnt,f
    bsf     bki_tx_overflow
    return

#if KK_VERSION>=27
;===========================================================================
; kk_tx_flush - flush tx buffer
;===========================================================================
;
; flush output buffer
;
kk_flush:
    bsf     bki_tx_flushing
    rcall   kki_tx_flush_sub
    bcf     bki_tx_flushing
    return

;
; flush sub
;
kki_tx_flush_sub:
    movf    vk_tx_cnt,w
    andlw   0xfc
    btfsc   bki_tx_flushing
    btfsc   STATUS,Z
    return
    rcall   kk_main_run
    bra     kki_tx_flush_sub

#endif

;===========================================================================
; kk_sout - output string in tblptrl,h
;===========================================================================
    ;
    ; kk_sout   prints string at TBLPTRH,L
    ; kk_sout_wh is just like kk_sout, but w is first copied to TBLPTRH
    ; kki_sout_base0 adds w to kki_str_base0 and prints that string
    ;
kki_sout_base0:
    addlw   LOW kki_str_base0
    movwf   TBLPTRL
    movlw   HIGH kki_str_base0
    addwfc  TBLPTRU,w           ; add 0 + c  (TBLPTRU is always 0)

    ; fall through to kk_sout_wh
    
kk_sout_wh:
    movwf   TBLPTRH
    bra     kk_sout

kki_sout_loop:
    rcall   kk_cout
kk_sout:
    tblrd*+
    movf    TABLAT,w
    bnz     kki_sout_loop
    return

;###########################################################################
;################################ VITAL STATE ##############################
;###########################################################################

;
; Set runstate & save value in EEPROM
;

;
; Runstate table
; Each entry has 4 bytes:
;
;   0 = vki_runstate_bits       ; bitmask
;   1 = runstate LEDs           ; led values
;   2 = vki_runstate_prompt     ; prompt string
;   3 = runstate offset         ; offset to store in eeprom EEPROM_RUNSTATE
;
kki_runstate_table:
kki_runstate_run:
    db  RUNSTATE_BITS_RUN,KERNVAL_TRISB_LED_RUN
    db  kki_str_prompt_run-kki_str_base0,kki_runstate_run-kki_runstate_table
kki_runstate_debug:
    db  RUNSTATE_BITS_DEBUG,KERNVAL_TRISB_LED_DEBUG
    db  kki_str_prompt_debug-kki_str_base0,kki_runstate_debug-kki_runstate_table
kki_runstate_halt:
    db  RUNSTATE_BITS_HALT,KERNVAL_TRISB_LED_HALT
    db  kki_str_prompt_halt-kki_str_base0,kki_runstate_halt-kki_runstate_table
kki_runstate_error:
    db  RUNSTATE_BITS_ERROR,KERNVAL_TRISB_LED_ERROR
    db  kki_str_prompt_error-kki_str_base0,kki_runstate_halt-kki_runstate_table
kki_runstate_break:
    db  RUNSTATE_BITS_BREAK,KERNVAL_TRISB_LED_BREAK
    db  kki_str_prompt_break-kki_str_base0,kki_runstate_halt-kki_runstate_table

kki_set_runstate:
    movwf   TBLPTRL
    addlw   -(kki_runstate_break-kki_runstate_table)
    bc      kki_set_rs_error
    andlw   0x03
    bnz     kki_set_rs_error

    movlw   LOW kki_runstate_table
    addwf   TBLPTRL,f
    movlw   HIGH kki_runstate_table
    addwfc  TBLPTRU,w           ; add 0 + c  (TBLPTRU is always 0)
    movwf   TBLPTRH

    tblrd*+
    movff   TABLAT,vki_runstate_bits
    tblrd*+
    movf    TABLAT,w
    rcall   kki_led_set
    tblrd*+
    movff   TABLAT,vki_runstate_prompt
    tblrd*+
#if KK_VERSION>=19
    EEPROM_BEGIN    EEPROM_RUNSTATE ; write starting from EEPROM_RUNSTATE
#endif
    movf    TABLAT,w
    bra     kk_eeprom_write_wait        ; write runstate offset to eeprom

;
; error: bad runstate offset
;
kki_set_rs_error:
    ERR     ERROR_A3_RUNSTATE
    

;
; Save time to eeprom
;
kki_save_time:
    EEPROM_BEGIN    EEPROM_HOUR ; write starting from EEPROM_HOUR
    movf    vk_hour,w
    rcall   kk_eeprom_write             ; write hour to eeprom
    movf    vk_minute,w
    bra     kk_eeprom_write_wait        ; write minute to eeprom
    

;###########################################################################
;################################ UTILITY FUNCTIONS ########################
;###########################################################################

;
; kk_set_osc_32khz - use 32.768 khz oscillator
; kk_set_osc_8mhz  - use 8 MHz oscillator
; kk_clear_interrupts - turn all isrs off, then enable globally
;
kk_set_osc_32khz:
    btfss   RCSTA,SPEN      ; serial port enabled?
    bsf     OSCCON,0        ; run slowly (but not if serial enabled)
    return

kk_set_osc_8mhz:
    bcf     OSCCON,0        ; run quickly
    return

kk_clear_interrupts:
    clrf    INTCON

    ; IPR1 - set all interrupt priorities to LOW
    ; IPR2 - set all interrupt priorities to LOW
    clrf    IPR1
    clrf    IPR2

    movlw   KERNVAL_INTCON2
    movwf   INTCON2

    movlw   KERNVAL_INTCON3
    movwf   INTCON3

    clrf    PIE1
    clrf    PIE2

    ;
    ; setup RCON so we can detect different types of reset
    ;
    movlw   KERNVAL_RCON
    movwf   RCON

    ;
    ; enable high & low priority interrupts (global)
    ;
    movlw   0xc0
    iorwf   INTCON,f

    return

;###########################################################################
;################################ ERROR & DEBUG ############################
;###########################################################################

;===========================================================================
; error
;===========================================================================
kk_doerror:
    movwf   vk_error

#if SIM
    call    kk_error
#endif

    reset

;===========================================================================
; breakpoint
;===========================================================================
kk_dobreakpt:
    movwf   vk_breakpt

#if SIM
    call    kk_breakpt
#endif

    movf    vki_breakpt_w,w

    ;
    ; return if serial disabled or debug not enabled
    ;
    btfss   RCSTA,SPEN      ; serial port disabled?
    btfss   bk_rs_debug
    return

    SET_RUNSTATE    kki_runstate_break

kki_dobreakpt_loop:
    rcall   kk_main_run

    ;
    ; loop until breakpoint bit cleared
    ;
    btfsc   bki_rs_breakpt
    bra     kki_dobreakpt_loop

    SET_RUNSTATE    kki_runstate_debug

    movf    vki_breakpt_w,w
    return

;###########################################################################
;################################ JUMP TABLE FUNCTIONS #####################
;###########################################################################

;
; implements a jump table
; jump here after setting W to the jump offset
; before the jump W is reloaded from vk_w
;
kk_jump:
    addwf   TOSL,f
    btfsc   STATUS,C
    incf    TOSH,f
    addwf   TOSL,f
    btfsc   STATUS,C
    incf    TOSH,f
    movlw   2
    addwf   TOSL,f
    btfsc   STATUS,C
    incf    TOSH,f
    movf    vk_w,w
    return

#if 0
;
; EXAMPLE CODE - use this to implement a jump table
;
    movwf   vk_w                ; vk_w will be put back into w before jumping
    movf    v_table_index,w     ; This should be the 0-based index into table

    ; Code here should ensure that w < sizeof table

    push
    bra     kk_jump

    bra     func0           ; this happens if v_table_index == 0
    bra     func1           ; this happens if v_table_index == 1
    bra     func2           ; this happens if v_table_index == 2
    bra     func3           ; this happens if v_table_index == 3
    bra     func4           ; this happens if v_table_index == 4
    ;...
#endif




;###########################################################################
;################################ MEMORY FUNCTIONS #########################
;###########################################################################

    ;
    ; EECON1
    ;  7  EEPGD    = 0  0=eeprom        1=flash
    ;  6  CFGS     = 0  0=eeprom/flash  1=config
    ;  5  reserved = 0  
    ;  4  FREE     = 0  1 = erash flash block
    ;  3  WRERR    = 0  1 = reset or write error
    ;  2  WREN     = x  1 = enable writes
    ;  1  WR       = 0  set to initiate a write/erase
    ;  0  RD       = 0  set to initiate a read
    ;
    ; RD_EE    EECON1 = 0000 0000 = 0x00 (set bit 0 (RD) to initiate read)
    ; WR_EE    EECON1 = 0000 0100 = 0x04 (set bit 1 (WR) to initiate write)
    ; RD_FLASH EECON1 = **** **** = 0x** (dont care)
    ; ER_FLASH EECON1 = 1001 0100 = 0x00 (set bit 1 (WR) to initiate erase)
    ; WR_FLASH EECON1 = 1000 0100 = 0x00 (set bit 1 (WR) to initiate write)
    ;

;===========================================================================
; Common memory functions
;===========================================================================
;
; kki_table_write - common FLASH/EEPROM write/erase sequence
;
kki_table_write:

    ;
    ; clear interrupts during write
    ;
    movff   INTCON,vki_tmp1
    movlw   0x3f
    andwf   INTCON,f

    ;
    ; enable writes
    ;
    bsf     EECON1,WREN

    ;
    ; following sequence must remain exactly the same
    ;
    movlw   0x55
    movwf   EECON2
    movlw   0xaa
    movwf   EECON2
    bsf     EECON1,WR

    ;
    ; disable writes; restore interrupts
    ;
    bcf     EECON1,WREN
    movff   vki_tmp1,INTCON
    return


;===========================================================================
; Flash memory functions
;===========================================================================
;
; kk_flash_write    - erase & write 64 bytes from FSR1 to TBLPTRH,L
; kk_tblptr_addw    - add w to TBLPTRL,H
;
kk_flash_write:

    ;
    ; wait for any preceding eeprom read/write & clear EECON1 reister
    ;
    rcall   kki_eeprom_setup

    ;
    ; erase flash at TBLPTR
    ;
    bsf     EECON1,EEPGD    ; flash memory
    bsf     EECON1,FREE     ; erase (free) block (cleared automatically)
    rcall   kki_table_write ; erase (stall here for ~2ms)
    
    ;
    ; write 64 bytes
    ;
    movlw   64
    movwf   vki_tmp1
    TBLRD*-                 ; decrement TBLPTR

kk_flash_write_loop:
    movf    INDF1,w
    incf    FSR1L,f
    movwf   TABLAT
    tblwt+*                 ; write with preinc
    decfsz  vki_tmp1,f
    bra     kk_flash_write_loop

    ;
    ; do the write and we are done!
    ;
    bsf     EECON1,EEPGD    ; flash memory
    bra     kki_table_write


;
; add w to TBLPTRL,H
;
kk_tblptr_addw:
    addwf   TBLPTRL,f
    btfsc   STATUS,C
    incf    TBLPTRH,f
    return

;===========================================================================
; eeprom memory functions
;===========================================================================
;
; kki_eeprom_setup     - Setup for eeprom access by
;                         1) waiting for any pending writes to complete
;                         2) set EECON1 for eeprom reads
;                         3) increment EEADR
;
; kk_eeprom_read       - read eeprom[EEADRH,++EEADR] -> w
; kk_eeprom_write      - write w -> eeprom[EEADRH,++EEADR]
; kk_eeprom_write_wait - write and wait for write to complete
;
kk_eeprom_read:
    rcall   kki_eeprom_setup
    bsf     EECON1,RD
    movf    EEDATA,w
    return

kk_eeprom_write:
kk_eeprom_write_wait:

    movwf   vki_tmp1
kki_eeprom_write_confirm:
    rcall   kk_eeprom_read      ; what is there now?
    subwf   vki_tmp1,w          ; same thing?
    btfsc   STATUS,Z
    return                      ; same - we are done!

    movff   vki_tmp1,EEDATA
    bra     kki_table_write
    
    decf    EEADR,f
    bra     kki_eeprom_write_confirm


kki_eeprom_setup:
    btfsc   EECON1,WR
    bra     kki_eeprom_setup
    clrf    EECON1
    incf    EEADR,f
    return

;###########################################################################
;################################ MAIN PROGRAM VECTORS #####################
;###########################################################################

;
; kk_app_main     - kernel jumps here to run application
; kk_app_isr_low  - low  priority ISR starts here
; kk_app_isr_high - high priority ISR starts here 
;


#if KK_VERSION >= 9
kk_app_main     equ 0xa00
#endif


kk_app_isr_low  equ kk_app_main+0x4
kk_app_isr_high equ kk_app_main+0x30


;
; loader values - these should never change
;
;
;
KERNVAL_HEADER              equ 0x0010  ; location of header
KERNVAL_LOADER_BEGIN        equ 0x3000  ; where the loader starts
KERNVAL_LOADER_NEW_KERNEL   equ 0x4000  ; where the new kernel is loaded

;###########################################################################
;################################ END ######################################
;###########################################################################


This file Copyright (C) 2006 by Nathan (Acorn) Pooley
Go to TOP Wand page
Go to Acorn's personal webpage
Go to Hogwarts website: www.gotohogwarts.com
Snout: www.snout.org/game
Gadgets of Justice Unlimited
Snout GC (Wiki)
Snout Wiki
File created by do_doc at Wed May 30 03:22:36 PDT 2007