Hogwarts Wand Docs: ../sw/wand.asm

Wand Sourcecode: ../sw/wand.asm

;
; wand.asm - main wand code
;
; 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@ This is the main wand program

;###########################################################################
;################################ KERNEL STUFF #############################
;###########################################################################

#define HACK1       0
#define DBPRINT     1

#define SHOW_LED_DURING_MOTION  0

#define USE_CONST_MAGSQ_TH      1
#define QUIT_WHEN_SAMPLE_FULL   0

#define ENAB_BLINKS             0
#define ENAB_ISR_TAP            0
#define ENAB_OLD_TAP_HANDLE     0
#define ENAB_W_PARSE            0
#define ENAB_OLD_MOTION_SPELLS  0
#define ENAB_FMTB_MOTION_SPELLS 1
#define ENAB_TESTS              0

#define DBG_SPELL_MSG_IS_SPELL_NAME 0

;
; this enables extra org directives to keep code from shifting around
;
#define DBORG       0

#include        "chip.inc"
#include        "kernel.inc"


    radix       dec
    expand


;###########################################################################
;################################ ACCELEROMETER VALUES #####################
;###########################################################################

;
; Still values 
;
;  x min = 0x4e
;  x mid = 0x55 or 0x56
;  x max = 0x5c
;
;  y min = 0x4e 
;  x mid = 0x55 or 0x56
;  y max = 0x5c
;
; small movements
;
;  x = 51 - 5a
;  y = 4e - 51
;
;
; tap
;  see TAPS under TIMING CHART
;  fast rise time
;  mag = 60 = 0x3c
;  mag*mag = 0xe10
;
;

;
; Accelerometer threshhold defaults
;
ACC_THESH_X_AVG     equ 0x55
ACC_THESH_X_MIN     equ 0x4e
ACC_THESH_X_MAX     equ 0x5c

ACC_THESH_Y_AVG     equ 0x4f
ACC_THESH_Y_MIN     equ 0x4e
ACC_THESH_Y_MAX     equ 0x5c

;
; initial threshhold values
;
ACC_INIT_AVGX           equ 0x55    ; average x value (initial)
ACC_INIT_AVGY           equ 0x4f    ; average y value (initial)

;
; Time to ignore false-taps after a tap (in sample times - 640usec)
;
ACC_TAP_MASK_TIME       equ     312 ; counts to mask sampler after a tap

#if 1
ACC_INIT_TH_MAGSQ_TAP   equ (0x20*0x20) ; min mag squared for tap
ACC_INIT_TH_MAGSQ_Z     equ (7*7)-1 ; min mag squared to start sampling
;ACC_INIT_TH_MAGSQ_S    equ (5*6)-1 ; max mag squared to stop sampling
ACC_INIT_TH_MAGSQ_S     equ (2*2)-1 ; max mag squared to stop sampling

ACC_TH_MAGSQ_WAKE       equ (19*19) ; min magsq to wake up

;
; threshholds for saving an angle state or a pause state
;  TH_IMP  is impulse threshhold
;  TH_TIME is time threshhold in sample-times (640us per sample)
;  TH_MAG  is magnitude threshhold
;
ACC_TH_TIME_END         equ 0x0927  ; end when still for this long (1.5sec)
ACC_TH_TIME_RETIRE      equ 0x009c  ; max time to keep cur state w/out seeing it
ACC_TH_TIME_PAUSE       equ 0x009c  ; min time    to register a pause state
ACC_TH_TIME_RETIRE_TAP  equ 0x0005  ; max time to keep tap state w/out seeing it
;ACC_TH_TIME_RETIRE_TAP equ 0x009c  ; max time to keep tap state w/out seeing it

;
; to save a sample one of these conditions must be met
;   - magnitude > ACC_TH_MAG_TAP
;   - time and impulse exceed threshholds
;   - time and magnitude exceed threshholds
;
;ACC_TH_TIME_SAVE       equ 0x0004  ; min time    to register a new state
ACC_TH_TIME_SAVE        equ 0x0004  ; min time    to register a new state
;ACC_TH_IMP_SAVE        equ 0x0020  ; min impulse to register a new state
ACC_TH_IMP_SAVE         equ 0x0080  ; min impulse to register a new state
ACC_TH_MAG_SAVE         equ 0x0015  ; min impulse to register a new state
ACC_TH_MAG_TAP          equ 0x0020  ; mag to consider as a tap (for rythm)
ACC_TH_MAG_DRAW         equ 0x0006  ; min delta mag for draw enable


; ACC_TH_MAGSQ_STILL    - max magsq to consider still (for ending spell cast)
ACC_TH_MAGSQ_STILL      equ     (18*18)

#else

;
; special values for debug taps only
;
ACC_INIT_TH_MAGSQ_TAP   equ (0x20*0x20) ; min mag squared for tap
ACC_INIT_TH_MAGSQ_Z     equ (0x20*0x20)-1   ; min mag squared to start sampling
ACC_INIT_TH_MAGSQ_S     equ (0x20*0x20)-1   ; max mag squared to stop sampling
ACC_TH_TIME_END         equ 0x0927  ; end when still for this long (1.5sec)
ACC_TH_TIME_RETIRE      equ 0x0001  ; max time to keep cur state w/out seeing it
ACC_TH_TIME_PAUSE       equ 0x0001  ; min time    to register a pause state
ACC_TH_TIME_RETIRE_TAP  equ 0x0001  ; max time to keep tap state w/out seeing it
ACC_TH_TIME_SAVE        equ 0x0001  ; min time    to register a new state
ACC_TH_IMP_SAVE         equ 0x0080  ; min impulse to register a new state
ACC_TH_MAG_SAVE         equ 0x0015  ; min impulse to register a new state

ACC_TH_MAG_TAP          equ 0x0020  ; mag to consider as a tap (for rythm)


#endif


;###########################################################################
;################################ REGISTER SETTINGS ########################
;###########################################################################

    ;
    ; ADCON0_OFF = 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
    ;
    ;
    ;  Acc-X:    (A) AN0   0000 0001
    ;  Acc-Y:    (D) AN1   0000 0101
    ;  Acc-Xmin: (C) AN3   0000 1101
    ;  Acc-Xmax: (B) AN2   0000 1001
    ;  Acc-Ymin: (F) AN4   0001 0001
    ;  Acc-Ymax: (E) AN12  0011 0001
    ;
ADCON0_OFF      equ 0x00
ADCON0_ACCX     equ 0x01
ADCON0_ACCY     equ 0x05
ADCON0_ACCXMIN  equ 0x0d
ADCON0_ACCXMAX  equ 0x09
ADCON0_ACCYMIN  equ 0x11
ADCON0_ACCYMAX  equ 0x31
#define b_adgo  ADCON0,1

    ;
    ; 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
    ;
REGVAL_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)
    ;
    ;   Tad = 2usec
    ;   4  Tad for acquire
    ;   11 Tad for convert
    ;   15 Tad total = 30usec per convert
    ;
REGVAL_ADCON2      equ 0x11


    ;
    ; T3CON = 1011 1001
    ;  7  RD16     = 1  16 bit read/write mode
    ;  6  T3CCP2   = 0  Timer1 is the clock source for CCP1
    ; 5-4 RD16     = 11 1:8 prescaler
    ;  3  T3CCP1   = 1  Timer3 is the clock source for CCP2
    ;  2  _T3SYNC  = 0  unused in Fosc/4 mode
    ;  1  TMR3CS   = 0  use Fosc/4 oscillator
    ;  0  TMR3ON   = 1  Tim
    ;
    ; Used to count up to the a/d conversion.  CCP2 clears the counter.
    ;
REGVAL_T3CON_OFF    equ 0x00
REGVAL_T3CON_AD     equ 0xb9

    ;
    ; CCP resets Timer3 & starts A/D conversion evey 640usec
    ;   Fosc/4        = 1usec period
    ;   1:8 prescale  = 8usec period
    ;   640usec/8usec = 80 clocks
    ;
REGVAL_CCPRL2H      equ 0
REGVAL_CCPRL2L      equ 80

    ;
    ; CCP2CON = 0000 1011
    ; 7-6 reserved = 00
    ; 5-4 DC2B1-0  = 00   unused in compare mode
    ; 3-0 CCP2M3-0 = 1011 mode (compare mode + special event trigger)
    ;
    ; Used to start a/d conersion of accx when timer3 reaches the trigger val
    ;
REGVAL_CCP2CON      equ 0x0b



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

;
; Macros
;
; SOUT1   - print string in w_str_base1 block of strings
; SOUT1CR - print string followed by CR
;
;  MACROS FOR ADDING TEXT TO WAND OUTPUT
;  -------------------------------------
;
; WCLR    - clear wand
; WCOUT   - wand print character in w
; WSOUT0  - wand print string in kki_str_base0 block of strings
; WSOUT1  - wand print string in w_str_base1 block of strings
; WSOUT   - wand print string
;

;
; SOUT1 - print string from w_str_base1 block of strings
;
SOUT1   macro   str_addr
    movlw   str_addr-w_str_base1
    rcall   w_sout_base1
    endm
SOUT1L   macro   str_addr
    movlw   str_addr-w_str_base1
    call    w_sout_base1
    endm

SOUT1CR   macro   str_addr
    movlw   str_addr-w_str_base1
    rcall   w_sout_base1_cr
    endm
SOUT1CRL  macro   str_addr
    movlw   str_addr-w_str_base1
    call    w_sout_base1_cr
    endm

;
; WCLR - clear wand
;
WCLR    macro
    rcall   w_wclear
    endm
WCLRL   macro
    call    w_wclear
    endm

;
; WCOUT - wand print character in w
;
WCOUT   macro   char
    movlw   char
    rcall   w_wcout
    endm
WCOUTL  macro   char
    movlw   char
    call    w_wcout
    endm

;
; WSOUT - print string
;
WSOUT   macro   str_addr
    movlw   LOW str_addr
    movwf   v_strl
    movlw   HIGH str_addr
    rcall   w_wsout_wh
    endm
WSOUTL  macro   str_addr
    movlw   LOW str_addr
    movwf   v_strl
    movlw   HIGH str_addr
    call    w_wsout_wh
    endm

;
; WSOUT0 - print string from str_base0 string table
;
WSOUT0  macro   str_addr
    movlw   str_addr-kki_str_base0
    rcall   w_wsout_base0
    endm
WSOUT0L macro   str_addr
    movlw   str_addr-kki_str_base0
    call    w_wsout_base0
    endm

;
; WSOUT1 - print string from w_str_base1 block of strings
;
WSOUT1   macro   str_addr
    movlw   str_addr-w_str_base1
    rcall   w_wsout_base1
    endm
WSOUT1L   macro   str_addr
    movlw   str_addr-w_str_base1
    call    w_wsout_base1
    endm


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

ERROR_B1_ISR_OVERRUN        equ     0xb1    ; ISR took too long
ERROR_B2_DRAW2_AD_OVERRUN   equ     0xb2    ; A/D not done when expected

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

;
; v_*     - wand specific
;
; 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
;

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

; EEPROM_END  - first free eeprom address


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

;
; These variables are preserved across warm boot.
;


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

v_bits1             equ vaddr+0
v_bits2             equ vaddr+1
v_bits3             equ vaddr+2
v_tmp               equ vaddr+3
vaddr+=4

#define b_do_accx       v_bits1,0   ; next A/D conversion is x
#define b_accbuf_sample v_bits1,1   ; enable sampling into FSR2 buffer
#define b_parse_done    v_bits1,2   ; set when no more accbuf entries for parse
#define b_parse_wag     v_bits1,3   ; set when wagging back & forth
#if ENAB_BLINKS
#define b_blink         v_bits1,4   ; set when v_blink_cnt reaches 0
#endif
#if ENAB_ISR_TAP
#define b_tap_mask      v_bits1,5   ; ignore tap while set (after a tap)
#define b_tap           v_bits1,6   ; set when a tap occurs
#define b_tap_enable    v_bits1,7   ; taps are enabled when set
#endif

#define b_draw_ok       v_bits2,0   ; draw enabled (w_draw2)
#define b_draw_go       v_bits2,1   ; draw going   (w_draw2)
#define b_draw_scroll   v_bits2,2   ; scroll the text
#define b_d2_maybe_tap  v_bits2,3   ; tap might have occurred (w_draw2)
#define b_d2_tap        v_bits2,4   ; tap occurred (w_draw2)
#define b_d2_sleep      v_bits2,5   ; time to sleep (w_draw2)
#define b_got_spell     v_bits2,6   ; set if spell was recognized

#define b_spell_yes_no  v_bits3,0   ; current string is a yes/no question
#define b_spell_abort   v_bits3,1   ; abort spell cast
#define b_qsectmr0_done v_bits3,2   ; quarter-second-timer 0 expired if set
#define b_qsectmr1_done v_bits3,3   ; quarter-second-timer 1 expired if set
#define b_acc_motion    v_bits3,4   ; set in acc isr when motion detected while
                                    ;   sampling
#define b_acc_finish    v_bits3,5   ; if set isr will stop sampling
#define b_show_time     v_bits3,6   ; set to show time

#if ENAB_ISR_TAP
v_tap_bits          equ vaddr+0
vaddr+=1
    ;
    ; These bits are set for 1 loop (from w_main_run until next w_main_run)
    ;
#define b_tap_up            v_tap_bits,0    ; set when a up tap occurs
#define b_tap_right         v_tap_bits,1    ; set when a right tap occurs
#define b_tap_down          v_tap_bits,2    ; set when a down tap occurs
#define b_tap_left          v_tap_bits,3    ; set when a left tap occurs
#endif


v_accx              equ vaddr+0     ; recent x value
v_accy              equ vaddr+1     ; recent x value
v_accx_min          equ vaddr+2     ; min x value
v_accx_max          equ vaddr+3     ; min x value
v_accy_min          equ vaddr+4     ; min x value
v_accy_max          equ vaddr+5     ; min x value
vaddr+=6

;===========================================================================
; acc sampling
;===========================================================================

v_absx          equ vaddr+0 ; abs value of v_accx + v_avgx_neg
v_absy          equ vaddr+1 ; abs value of v_accy + v_avgy_neg
vaddr+=2

v_angle         equ vaddr+0 ; angle measured
v_mag           equ vaddr+1 ; magnitude (sqrt of magsq)
v_magsql        equ vaddr+2 ; magnitude squared (low byte)
v_magsqh        equ vaddr+3 ; magnitude squared (high byte)
vaddr+=4

#if ENAB_ISR_TAP
v_tap_angle         equ vaddr+0 ; angle of most recent tap
v_tap_mask_acntl    equ vaddr+1 ; time left to mask acc (low)
v_tap_mask_acnth    equ vaddr+2 ; time left to mask acc (high)
v_th_magsqh_tap     equ vaddr+3 ; threshhold for v_magsqh to be a tap
vaddr+=4
#endif

v_avgx_neg      equ vaddr+0 ; negative of average x value (-0x4f = 0xb1)
v_avgy_neg      equ vaddr+1 ; negative of average y value (-0x55 = 0xab)
v_th_magsqh     equ vaddr+2 ; threshhold for v_magsql to sample
v_th_magsql     equ vaddr+3 ; threshhold for v_magsql to sample
                            ;                      (v_th_magsql_z or s)
v_th_retimeh    equ vaddr+4 ; max time to hold state without seeing it
v_th_retimel    equ vaddr+5
vaddr+=6

#if !USE_CONST_MAGSQ_TH
v_th_magsql_z   equ vaddr+0 ; v_th_magsql when prev == zero (-8*8 = -64 = 0xc0)
v_th_magsql_s   equ vaddr+1 ; v_th_magsql when prev != zero (-7*7 = -49 = 0xcf)
vaddr+=2
#endif

#if HACK1
; debug
v_hack1_magsql      equ vaddr+0
v_hack1_magsqh      equ vaddr+1
v_hack1_mag         equ vaddr+2
v_hack1_angle       equ vaddr+3
v_hack1_a           equ vaddr+4
v_hack1_b           equ vaddr+5
v_hack1_c           equ vaddr+6
v_hack1_d           equ vaddr+7
vaddr+=8
#endif


;===========================================================================
; function parameters
;===========================================================================
v_strh          equ vaddr+0 ; string high pointer
v_strl          equ vaddr+1 ; string low  pointer
vaddr+=2

;===========================================================================
; drawing
;===========================================================================
v_sleep_wait        equ vaddr+0 ; how long until we sleep
v_draw_cnt          equ vaddr+1 ; time before next column of pixels
v_draw_mask         equ vaddr+2 ; which ixel to draw
v_tap_wait          equ vaddr+3 ; wait between draw and tap
vaddr+=4

v_drawbuf_startl    equ vaddr+0
v_drawbuf_starth    equ vaddr+1
v_drawbuf_endl      equ vaddr+2
v_drawbuf_endh      equ vaddr+3
vaddr+=4

#define SCROLL_SPEED    1
#if SCROLL_SPEED
v_scrollspd         equ vaddr+0
vaddr+=1
#endif

#if ENAB_BLINKS
;===========================================================================
; blink counter
;===========================================================================
v_blink_cnt         equ vaddr+0 ; countdown to next blink (640 usec counter)
v_blink_ptr         equ vaddr+1 ; where we are in blink sequence (0=off)
vaddr+=2
#endif

;===========================================================================
; time temporaries - see w_pause_w and w_timer_set_fsr0
;===========================================================================
v_th            equ vaddr+0 ; time
v_tl            equ vaddr+1 ;
v_t0l           equ vaddr+2 ; time of s<mins crossing
v_t0h           equ vaddr+3 ;
v_dtl           equ vaddr+4 ; deltaT = (t0-t1)/4
v_dth           equ vaddr+5 ;
v_qsectmr0      equ vaddr+6 ; quarter second timer (see w_qsectmr0_set)
v_qsectmr1      equ vaddr+7 ; quarter second timer (see w_qsectmr1_set)
vaddr+=8


;===========================================================================
; spell casting
;===========================================================================
v_spell_ptrh        equ vaddr+0 ; spellinfo for cast spell
v_spell_ptrl        equ vaddr+1 ;
v_spell_yesh        equ vaddr+2 ; spellinfo for yes
v_spell_yesl        equ vaddr+3 ;
v_spell_noh         equ vaddr+4 ; spellinfo for no
v_spell_nol         equ vaddr+5 ;
vaddr+=6

#if ENAB_W_PARSE
v_spell_len         equ vaddr+0 ; # of bytes in cast spell
vaddr+=1
#endif

;===========================================================================
; Buffers
;===========================================================================
RAMBUF_DRAW     equ 0x300   ; pixels to draw (LEN=0x400)
RAMBUF_DRAW_END equ 0xf00   ; end of pixel draw buffer
RAMBUF_RYTHM    equ 0x300   ; rythm buffer (LEN=0x100)
RAMBUF_MOTION   equ 0x300   ; motion buffer (LEN=0x100)
RAMBUF_ACC      equ 0x500   ; acceleration sample buffer (LEN=0x700)
RAMBUF_ACC_END  equ 0xf00   ; end of acceleration sample buffer

;===========================================================================
; MEMORY MAP
;===========================================================================
; 
; 0x0000 - 0x007f - access bank RAM
; 0x0080 - 0x00ff - bank 0 RAM (currently accessed through BSR register)
;
; 0x0100 - 0x0dff - buffer mem (see below)
;
; 0x0e00 - 0x0eff - ACC buf (coulod be used for BSR space if needed)
; 0x0f00 - 0x0f7f - persistant wand RAM (if needed - unused for now)
; 0x0f80 - 0x0fff - Special Function Registers (access bank addressable)
;
; BUFFERS
; 0x0100 - 0x01ff - TX
; 0x0200 - 0x02ff - RX
; 0x0300 - 0x03ff - DRAW     M2LIST  RYTHM
; 0x0400 - 0x04ff - DRAW     M2LIST
; 0x0500 - 0x05ff - DRAW ACC M2LIST
; 0x0600 - 0x06ff - DRAW ACC M2LIST
; 0x0700 - 0x07ff - DRAW ACC M2LIST  STBL2
; 0x0800 - 0x08ff - DRAW ACC  M3LIST STBL1
; 0x0900 - 0x09ff - DRAW ACC  M3LIST SPELLDATA
; 0x0a00 - 0x0aff - DRAW ACC  M3LIST MMATRIX
; 0x0b00 - 0x0bff - DRAW ACC  M3LIST STOKENS
; 0x0c00 - 0x0cff - DRAW ACC  M3LIST MTOKENS
; 0x0d00 - 0x0dff - DRAW ACC
; 0x0e00 - 0x0eff - DRAW ACC
; 0x0f00 - 0x0f7f - persistant ram (unused)
;
;
;

;===========================================================================
; TIMING CHART
;===========================================================================
; 
; CLOCKS
;      1 us/cycle - program clock in fast mode
;    122 us/cycle - program clock in slow mode
;    250 ms       - bk_qsec set every 1/4 sec (from main_run to main_run)
;      1 min      - bk_minute set evry minute (from main_run to main_run)
;      1 hour     - bk_hour set evry hour     (from main_run to main_run)
;    244 usec     - TMR1L increments with this period
;     62 msec     - TMR1H increments with this period (TMR1L overflow)
;     16 sec      - TMR1H overflows every 16 seconds
;
; A/D
;     30 usec - time to do one A/D conversion (fast mode) (CALCULATED)
;                 (NOTE: measured time indicated definitely 48usec or less)
;    640 usec - time between kicking off X A/D conversion (for ISR mode)
;      5 msec - time to settle after turning Accelerometer on
;    700 usec - time to settle after turning A/D converter on
;    3.5 msec - time to settle after turning LEDs on/off
;                (NOTE: LEDs can change value by  3 units (steady state))
;                (NOTE: LEDs can change value by 15 units (dynamic))
;      2 usec - Tad (a/d time) 4 + 11 = 15 Tad per conversion (4 for acquire)
;
; TAPS
;    Good taps jump 70+ units   - peak reached in under 1 ms.
;    Cusion taps jump 70+ units - peak reached in under 10 ms.
;    Non-taps jump up to 50 units peak reached in over 30 ms.
; 
; DRAW
;      9 usec - time each LED is on (1 at a time)
;     52 usec - time to turn on & off all 5 LEDs once
;   1456 usec - column time
;   8736 usec - character time (incl 1-col gap)
; 
; 


;###########################################################################
;###########################################################################
;################################ CODE #####################################
;###########################################################################
;###########################################################################


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


    org     kk_app_main
    goto    w_main

    org     kk_app_isr_low
    return

    org     kk_app_isr_high
    rcall   isr_ad
    retfie  1

;###########################################################################
;################################ INTERRUPTS ###############################
;###########################################################################


;
; ISR timing:
;
;  conversion takes 30usec
;
; time in usec==cycles
;
;  isr time section
;  -------- ------------
;  17       top of x
;  3        bottom of x
;  19       top of y (all y if no sampling)
;  <=47     top of astate
;  ?        top of sample
;  <=31     calc angle
;  ?        calc mag (sqrt)
;  ?        sample
;
;
;  <start x convert>
;  30       x convert time
;  45       time until y convert starts
;  30       y convert time
;  169      time to handle x
;  ---
;  274      total time
;
;
;  <start x> occurs every 640 usec
;


;===========================================================================
; A/D interrupt
;===========================================================================
isr_ad:
    bcf     b_adif

#if ENAB_BLINKS
    ;
    ; blink timer - set b_blink when v_blink_cnt reaches 0
    ;
    btfss   b_blink
    dcfsnz  v_blink_cnt,f
    bsf     b_blink
#endif


    btg     b_do_accx
    btfsc   b_do_accx
    bra     isr_ad_y

isr_ad_x:
    movlw   ADCON0_ACCY
    movwf   ADCON0
    movf    ADRESH,w
    movwf   v_accx
    movwf   v_absx
    cpfsgt  v_accx_max      ; remember x, maxx, minx
    movwf   v_accx_max
    cpfslt  v_accx_min
    movwf   v_accx_min


isr_ad_x_starty:
    bsf     b_adgo          ; start y conversion
    return

;
; here we are ready to read y result
;
isr_ad_y:
    movlw   ADCON0_ACCX
    movwf   ADCON0
    movf    ADRESH,w
    cpfsgt  v_accy_max
    movwf   v_accy_max
    cpfslt  v_accy_min
    movwf   v_accy_min
    movwf   v_accy
    movwf   v_absy

#if ENAB_ISR_TAP
    ;
    ; decrement tap mask counter
    ;   if a tap occurred this will clr tapmask when zero
    ;   if a tap did not occur this does nothing
    ;
    decfsz  v_tap_mask_acntl,f
    bra     isr_ad_astate
    dcfsnz  v_tap_mask_acnth,f
    bcf     b_tap_mask          ; timer ran out - start looking for taps again
#endif

    ;===========================================================================
    ;
    ; calculate astate (magnitude and angle)
    ;
    ; v_angle  - angle measured
    ; v_mag    - magnitude (sqrt of v_magsq)
    ; v_magsql   - magnitude squared (low byte)
    ; v_magsqh   - magnitude squared (high byte)
    ; v_absx   - abs value of v_accx + v_avgx_neg
    ; v_absy   - abs value of v_accy + v_avgy_neg
    ;
    ; astate values
    ;   0x00 - 0x1f     angle 0-31
    ;   0x82            zero magnitude
    ;   0x84            scrambled
    ;
    ; v_avgx_neg        - negative of average x value (-0x4f = 0xb1)
    ; v_avgy_neg        - negative of average y value (-0x55 = 0xab)
    ; v_th_magsql       - threshhold for v_magsql to sample (v_th_magsql_z or s)
    ; v_th_magsql_z     - v_th_magsql when prev == zero
    ; v_th_magsql_s     - v_th_magsql when prev != zero
#if ENAB_ISR_TAP
    ; v_th_magsqh_tap   - threshhold for v_magsqh to be a tap (-0xe = 0xf2)
#endif
    ;

isr_ad_astate:
    clrf    v_angle

    ;
    ; calc abs x
    ;
isr_ad_absx:
    movf    v_avgx_neg,w
    addwf   v_absx,f
    bnn     isr_ad_absy

    negf    v_absx
    movlw   -31
    movwf   v_angle

    ;
    ; calc abs y
    ;
isr_ad_absy:
    movf    v_avgy_neg,w
    addwf   v_absy,f
    bnn     isr_ad_mag

    negf    v_absy
    movlw   15
    addwf   v_angle,f
    negf    v_angle

    ;
    ; calculate magnitude
    ;
isr_ad_mag:
    setf    v_mag       ; assume max magnitude
    movf    v_absx,w
    mulwf   v_absx
    
    movff   PRODL,v_magsql
    movff   PRODH,v_magsqh

    movf    v_absy,w
    mulwf   v_absy

    movf    PRODL,w
    addwf   v_magsql,f
    movf    PRODH,w
    addwfc  v_magsqh,f
    bnc     isr_ad_mag2

    setf    v_magsql    ; clamp to max magnitude
    setf    v_magsqh

isr_ad_mag2:
#if HACK1
    movff   v_magsql,v_hack1_magsql
    movff   v_magsqh,v_hack1_magsqh
    clrf    v_hack1_mag
    clrf    v_hack1_angle
#endif

    ;
    ; request to stop sampling?
    ;
    btfsc   b_acc_finish
    bra     isr_ad_sample_zero

    ;
    ; is magnitude big enough to register?
    ;
    movf    v_magsql,w
    addwf   v_th_magsql,w
    movf    v_magsqh,w
    addwfc  v_th_magsqh,w
    bnc     isr_ad_sample_zero  ; small magnitude - call it 0
    

    ;
    ; if mag <  ACC_TH_MAGSQ_STILL then skip shrink
    ; if mag >= ACC_TH_MAGSQ_STILL then set b_accbuf_motion
    ;
    movlw   LOW  (-ACC_TH_MAGSQ_STILL)
    addwf   v_magsql,w
    movlw   HIGH (-ACC_TH_MAGSQ_STILL)
    addwfc  v_magsqh,w
    bnc     isr_ad_shrink_done          ; small mag - no shrink needed

    bsf     b_acc_motion                ; big mag - set motion bit




    ;
    ; ensure absx and absy are less than 0x40
    ; (divide both by 2 or 4 if necessary)
    ;
isr_ad_shrink:
    movf    v_absx,w
    iorwf   v_absy,w
    andlw   0xc0
    bz      isr_ad_shrink_done
    rrncf   v_absx,f
    rrncf   v_absy,f
    bcf     v_absx,7
    bcf     v_absy,7
    bra     isr_ad_shrink
isr_ad_shrink_done:


#if ENAB_ISR_TAP
    ;
    ; big enough to be a tap?
    ;
    movf    v_magsqh,w
    addwf   v_th_magsqh_tap,w
    bnc     isr_ad_sample       ; not big enough to be a tap?

    ;
    ; mag is big enough to count as a tap
    ;
isr_ad_tap:
    btfsc   b_tap_enable    ; skip tap if not enabled
    btfsc   b_tap_mask      ; skip tap if still masked from previous tap
    bra     isr_ad_sample

    ;
    ; New tap!!
    ;
    movlw   (HIGH ACC_TAP_MASK_TIME)+1
    movwf   v_tap_mask_acnth
    movlw   (LOW ACC_TAP_MASK_TIME)+1
    movwf   v_tap_mask_acntl
    bsf     b_tap_mask
    bsf     b_tap

    rcall   isr_calc_angle      ; calc angle
    movf    v_angle,w
    movwf   v_tap_angle

    btfss   b_accbuf_sample
    bra     isr_ad_done
    bra     isr_ad_sample2
#else
    bra     isr_ad_sample
#endif

;===========================================================================
;
; SAMPLING VARIABLES
;

;
; input variables
;
;  v_mag   - current magnitude
;  v_angle - current angle (garbage if v_mag is 0)
;

;
; prev zero sample (before current sample)
;
v_smplpz_cnth   equ vaddr+0 ; # of zero samples in a row
v_smplpz_cntl   equ vaddr+1 ; 
vaddr+=2

;
; current sample
;
v_smplc_imph        equ vaddr+0 ; total impulse
v_smplc_impl        equ vaddr+1
v_smplc_cnth        equ vaddr+2 ; total time
v_smplc_cntl        equ vaddr+3
v_smplc_angle       equ vaddr+4 ; original angle
v_smplc_min_mag     equ vaddr+5 ; min magnitude
v_smplc_max_mag     equ vaddr+6 ; max magnitude
v_smplc_best_angle  equ vaddr+7 ; angle of max magnitude
v_smplc_flags       equ vaddr+8 ; flags
vaddr+=9

#define SMPLC_FLAGS_INIT        0x80
#define b_smplc_zero    v_smplc_flags,0 ; includes zero samples

;
; next zero sample (since current sample)
;
v_smplnz_cnth   equ vaddr+0 ; # of zero samples in a row
v_smplnz_cntl   equ vaddr+1 ; 
vaddr+=2

;
; new sample (since current sample)
;
v_smpln_imph        equ vaddr+0 ; total impulse
v_smpln_impl        equ vaddr+1
v_smpln_cnth        equ vaddr+2 ; total time
v_smpln_cntl        equ vaddr+3
v_smpln_angle       equ vaddr+4 ; original angle
v_smpln_min_mag     equ vaddr+5 ; min magnitude
v_smpln_max_mag     equ vaddr+6 ; max magnitude
v_smpln_best_angle  equ vaddr+7 ; angle of max magnitude
vaddr+=8

;===========================================================================
    ;
    ; mag is too small to register - use mag==0
    ;
isr_ad_sample_zero:
    clrf    v_mag

    ;
    ; use v_th_magsql_z (larger threshhold) for v_th_magsql
    ;
#if USE_CONST_MAGSQ_TH
    movlw   HIGH (-ACC_INIT_TH_MAGSQ_Z)
    movwf   v_th_magsqh
    movlw   LOW (-ACC_INIT_TH_MAGSQ_Z)
    movwf   v_th_magsql
#else
    movff   v_th_magsql_z,v_th_magsql
#endif

    btfss   b_accbuf_sample
    bra     isr_ad_done

    ;
    ; increment v_smplnz_cntl,h
    ;
    incf    v_smplnz_cntl,f
    bnc     isr_ad_sample_zero2
    incf    v_smplnz_cnth,f
    bnc     isr_ad_sample_zero2

    setf    v_smplnz_cntl           ; clamp to 21 sec max
    setf    v_smplnz_cnth

isr_ad_sample_zero2:

#if SHOW_LED_DURING_MOTION
    bsf     bk_led2
#endif

    ;
    ; request to finish up?
    ;
    btfsc   b_acc_finish
    bra     isr_ad_sample_finish

    movlw   LOW (-ACC_TH_TIME_END)
    addwf   v_smplnz_cntl,w
    movlw   HIGH (-ACC_TH_TIME_END)
    addwfc  v_smplnz_cnth,w
    bnc     isr_ad_sample_zero_done

    ;
    ; still for >ACC_TH_TIME_END
    ; ensure we have at least 4 samples
    ;
    movlw   LOW (-(RAMBUF_ACC+12))
    addwf   FSR2L,w
    movlw   HIGH (-(RAMBUF_ACC+12))
    addwfc  FSR2H,w
    bnc     isr_ad_sample_zero_done
    
isr_ad_sample_finish:
    ;
    ; we have some samples and have been still
    ;   - quit sampling
    ;   - send any pending sample
    ;
    bcf     b_accbuf_sample
    bra     isr_ad_sample_sendresult

isr_ad_sample_zero_done:
    bra     isr_ad_done

;===========================================================================

    ;
    ; calc & record angle & magnitude
    ;
isr_ad_sample:
    btfss   b_accbuf_sample
    bra     isr_ad_done

    rcall   isr_calc_angle      ; calc angle
isr_ad_sample2:
    rcall   isr_calc_mag        ; calc magnitude

#if HACK1
    movff   v_mag,v_hack1_mag
    movff   v_angle,v_hack1_angle
#endif

#if SHOW_LED_DURING_MOTION
    bcf     bk_led2
#endif

    ;
    ; use v_th_magsql_s (smaller threshhold) for v_th_magsql
    ;
#if USE_CONST_MAGSQ_TH
    movlw   HIGH (-ACC_INIT_TH_MAGSQ_S)
    movwf   v_th_magsqh
    movlw   LOW (-ACC_INIT_TH_MAGSQ_S)
    movwf   v_th_magsql
#else
    movff   v_th_magsql_s,v_th_magsql
#endif

    ;
    ; do we have a current angle? (v_smplc_angle >= 0)
    ;
    btfsc   v_smplc_angle,7
    bra     isr_ad_sample_check_new ; no - check for new state

    ;
    ; has it been a while since we saw a current angle?
    ;  (compare smpln_cnt+smplnz_cnt to v_th_retime)
    ;
    movf    v_th_retimel,w
    addwf   v_smplnz_cntl,w
    btfss   STATUS,C
    addwf   v_smpln_cntl,w
    movf    v_th_retimeh,w
    addwfc  v_smplnz_cnth,w
    bc      isr_ad_sample_check_new
    addwf   v_smpln_cnth,w
    bc      isr_ad_sample_check_new

isr_ad_sample_check_current:
    ;
    ; are we within +/-1 of current angle?
    ;
    movf    v_angle,w
    subwf   v_smplc_angle,w
    bz      isr_ad_sample_current
    btfsc   WREG,7
    negf    WREG
    addlw   -1
    bz      isr_ad_sample_current
    addlw   -0x1f+1
    bz      isr_ad_sample_current

isr_ad_sample_check_new:
    ;
    ; do we even have a new angle?
    ;
    btfsc   v_smpln_angle,7
    bra     isr_ad_sample_newnew    ; no - check for new state

    ;
    ; are we within +/-1 of new angle?
    ;
    movf    v_angle,w
    subwf   v_smpln_angle,w
    bz      isr_ad_sample_samenew
    btfsc   WREG,7
    negf    WREG
    addlw   -1
    bz      isr_ad_sample_samenew
    addlw   -0x1f+1
    bz      isr_ad_sample_samenew

    ;
    ; we are seeing a brand new new angle
    ;
isr_ad_sample_newnew:
    ;
    ; count any new time as pause time
    ;
    movf    v_smpln_cntl,w
    addwf   v_smplnz_cntl,f
    movf    v_smpln_cnth,w
    addwfc  v_smplnz_cnth,f
    bnc     isr_ad_sample_newnew2

    setf    v_smplnz_cntl   ; clamp to max cnt
    setf    v_smplnz_cnth

isr_ad_sample_newnew2:
    movf    v_angle,w
    movwf   v_smpln_angle
    movwf   v_smpln_best_angle
    movf    v_mag,w
    movwf   v_smpln_max_mag
    movwf   v_smpln_min_mag
    movwf   v_smpln_impl
    clrf    v_smpln_imph
    movlw   1
    movwf   v_smpln_cntl
    clrf    v_smpln_cnth

    bra     isr_ad_done

;===========================================================================
; we are still seeing the same angle
;
isr_ad_sample_current:
    ;
    ; integrate impulse
    ;
    movf    v_mag,w
    addwf   v_smplc_impl,f
    clrf    WREG
    addwfc  v_smplc_imph,f
    bnc     isr_ad_sample_current2

    setf    v_smplc_imph            ; clamp to max impulse
    setf    v_smplc_impl

isr_ad_sample_current2:

    ;
    ; are we including some zero samples?
    ;
    movf    v_smplnz_cntl,w
    iorwf   v_smplnz_cnth,w
    btfss   STATUS,Z
    bsf     b_smplc_zero        ; yes

    ;
    ; increment sample time
    ; add in smplnz_cnt (and clear it)
    ; add in smpln_cnt (and clear it)
    ; Also add in any zcnt time that may exist (and clear it)
    ;
    bsf     STATUS,C
    movf    v_smplnz_cntl,w
    addwfc  v_smplc_cntl,f
    movf    v_smplnz_cnth,w
    addwfc  v_smplc_cnth,f
    bc      isr_ad_sample_current_maxtime

    movf    v_smpln_cntl,w
    addwf   v_smplc_cntl,f
    movf    v_smpln_cnth,w
    addwfc  v_smplc_cnth,f
    bnc     isr_ad_sample_current3

isr_ad_sample_current_maxtime:
    setf    v_smplc_cnth            ; clamp to 21 sec max
    setf    v_smplc_cntl

isr_ad_sample_current3:
    ;
    ; clear next and next_zero
    ;
    clrf    v_smplnz_cntl
    clrf    v_smplnz_cnth
    clrf    v_smpln_cntl
    clrf    v_smpln_cnth
    setf    v_smpln_angle           ; invalidate next angle
    
    ;
    ; is this the min/max magnitude of the sample so far?
    ;
    movf    v_mag,w
    cpfslt  v_smplc_min_mag
    movwf   v_smplc_min_mag
    cpfsgt  v_smplc_max_mag
    bra     isr_ad_sample_current_newmaxmag
    bra     isr_ad_done

isr_ad_sample_current_newmaxmag:
    movwf   v_smplc_max_mag
    movf    v_angle,w
    movwf   v_smplc_best_angle

    ;
    ; is this a tap?
    ;
    movlw   -ACC_TH_MAG_TAP
    addwf   v_smplc_max_mag,w
    bnc     isr_ad_done     ; no - quit

    ;
    ; is a tap - use tap retire threshhold
    ;
    movlw   HIGH (-ACC_TH_TIME_RETIRE_TAP)
    movwf   v_th_retimeh
    movlw   LOW (-ACC_TH_TIME_RETIRE_TAP)
    movwf   v_th_retimel

    bra     isr_ad_done


;===========================================================================
; we are seeing the same new angle
;
isr_ad_sample_samenew:

    ;
    ; integrate new impulse
    ;
    movf    v_mag,w
    addwf   v_smpln_impl,f
    clrf    WREG
    addwfc  v_smpln_imph,f
    bnc     isr_ad_sample_samenew2

    setf    v_smpln_imph            ; clamp to max impulse
    setf    v_smpln_impl

isr_ad_sample_samenew2:
    ;
    ; increment new time
    ;
    incf    v_smpln_cntl,f
    clrf    WREG
    addwfc  v_smpln_cnth,f
    bnc     isr_ad_sample_samenew3

    setf    v_smpln_cntl            ; clamp to max cnt
    setf    v_smpln_cnth

isr_ad_sample_samenew3:
    ;
    ; is this the min/max magnitude of the sample so far?
    ;
    movf    v_mag,w
    cpfslt  v_smpln_min_mag
    movwf   v_smpln_min_mag
    cpfsgt  v_smpln_max_mag
    bra     isr_ad_sample_samenew3a
    bra     isr_ad_sample_samenew4

isr_ad_sample_samenew3a:
    movwf   v_smpln_max_mag
    movf    v_angle,w
    movwf   v_smpln_best_angle

isr_ad_sample_samenew4:
    
    ;
    ; max magnitude big enough to be a tap?
    ; (if so save even if no time)
    ;
    movlw   -ACC_TH_MAG_TAP
    addwf   v_smpln_max_mag,w
    bc      isr_ad_sample_sendresult

    ;
    ; seen state for a while?
    ;
    movlw   LOW (-ACC_TH_TIME_SAVE)
    addwf   v_smpln_cntl,w
    movlw   HIGH (-ACC_TH_TIME_SAVE)
    addwfc  v_smpln_cnth,w
    bnc     isr_ad_done

    ;
    ; Have we seen enough impulse or magnitude?
    ;
    movlw   LOW (-ACC_TH_IMP_SAVE)
    addwf   v_smpln_impl,w
    movlw   HIGH (-ACC_TH_IMP_SAVE)
    addwfc  v_smpln_imph,w
    bc      isr_ad_sample_sendresult

    ;
    ; Have we seen enough magnitude?
    ;
    movlw   -ACC_TH_MAG_SAVE
    addwf   v_smpln_max_mag,w
    bc      isr_ad_sample_sendresult

    ;
    ; have not seen enough impulse to count this angle yet
    ;
    bra     isr_ad_done

    ;
    ; We have seen the new angle long enough & enough impulse.
    ; Time to call it the current angle.
    ;
isr_ad_sample_sendresult:
    bsf     b_acc_motion

    ;
    ; is there a current angle to send?
    ;
    btfsc   v_smplc_angle,7
    bra     isr_ad_sample_newcurrent    ; nope

    ;
    ; is there a previous zero-time to send?
    ;
    movf    v_smplpz_cnth,w
    iorwf   v_smplpz_cntl,w
    bz      isr_ad_sample_sendc         ; nope

;
; send pause
;    0  time (h)
;    1  time (l)
;    2  unused (0xff)
;    3  angle (0xff indicates pause)
;
isr_ad_sample_sendz:
    rcall   isr_ad_sample_pre_save

    movf    v_smplpz_cnth,w
    movwf   POSTINC2        ; time (h)
    movf    v_smplpz_cntl,w
    movwf   POSTINC2        ; time (l)
    setf    POSTINC2        ; unused (0xff)
    setf    POSTINC2        ; angle = 0xff

    ;
    ; pause always followed by actual angle - fall thru to send angle
    ;

;
; send current
;    0  time (h)
;    1  time (l)
;    2  max mag
;    3  angle (0xff indicates pause)
;    4  impulse (h)
;    5  impulse (l)
;    6  min mag
;    7  flags (see below)
;
; valid values for angle
;    0x00 - 0x1f  = angle
;    0xff         = pause
;    0xfe         = end of buffer
;
; flag bits
;    0   set if any zero samples occurred
;   1-6  unused (0)
;    7   must be set (required by isr_ad_sample_pre_save)
;
isr_ad_sample_sendc:

    rcall   isr_ad_sample_pre_save

    movf    v_smplc_cnth,w
    movwf   POSTINC2        ; time (h)
    movf    v_smplc_cntl,w
    movwf   POSTINC2        ; time (l)
    movf    v_smplc_max_mag,w
    movwf   POSTINC2        ; max mag
    movf    v_smplc_best_angle,w
    movwf   POSTINC2        ; angle
    movf    v_smplc_imph,w
    movwf   POSTINC2        ; impulse (h)
    movf    v_smplc_impl,w
    movwf   POSTINC2        ; impulse (l)
    movf    v_smplc_min_mag,w
    movwf   POSTINC2        ; min mag
    movf    v_smplc_flags,w
    movwf   POSTINC2        ; flags

;===========================================================================
; copy smplnz and smpln to smplpz and smplc
;
isr_ad_sample_newcurrent:

    ;
    ; init flags
    ;
    movlw   SMPLC_FLAGS_INIT
    movwf   v_smplc_flags

    ;
    ; reset retire time threshhold
    ;
    movlw   HIGH (-ACC_TH_TIME_RETIRE)
    movwf   v_th_retimeh
    movlw   LOW (-ACC_TH_TIME_RETIRE)
    movwf   v_th_retimel

    ;
    ; is smplnz_cnt big enough to count as a pause?
    ;
    movlw   LOW (-ACC_TH_TIME_PAUSE)
    addwf   v_smplnz_cntl,w
    movlw   HIGH (-ACC_TH_TIME_PAUSE)
    addwfc  v_smplnz_cnth,w
    bc      isr_ad_sample_newcurrent2

;
; no pause - incorporate smplnz time into smpln
;
    ;
    ; are we including some zero samples?
    ;
    movf    v_smplnz_cntl,w
    iorwf   v_smplnz_cnth,w
    btfss   STATUS,Z
    bsf     b_smplc_zero        ; yes

    ;
    ; incorporate smplnz time into smpln
    ;
    movf    v_smplnz_cntl,w
    addwf   v_smpln_cntl,f
    movf    v_smplnz_cnth,w
    clrf    v_smplnz_cnth
    clrf    v_smplnz_cntl
    addwfc  v_smpln_cnth,f
    bnc     isr_ad_sample_newcurrent2

    setf    v_smpln_cnth        ; clamp to max
    setf    v_smpln_cntl

isr_ad_sample_newcurrent2:
    movff   v_smplnz_cntl,v_smplpz_cntl
    movff   v_smplnz_cnth,v_smplpz_cnth

    movff   v_smpln_cnth,v_smplc_cnth
    movff   v_smpln_cntl,v_smplc_cntl
    movff   v_smpln_imph,v_smplc_imph
    movff   v_smpln_impl,v_smplc_impl
    movff   v_smpln_angle,v_smplc_angle
    movff   v_smpln_best_angle,v_smplc_best_angle
    movff   v_smpln_min_mag,v_smplc_min_mag
    movff   v_smpln_max_mag,v_smplc_max_mag

    clrf    v_smplnz_cntl
    clrf    v_smplnz_cnth
    clrf    v_smpln_cntl
    clrf    v_smpln_cnth
    setf    v_smpln_angle

isr_ad_done:
    btfss       b_adif
    return

    ERR         ERROR_B1_ISR_OVERRUN

    return

;===========================================================================
; Ensure there are (at least) 8 entries (32 bytes) in the sample buffer
;
isr_ad_sample_pre_save:
    ;
    ; check for end of sample buffer
    ;
    movlw   LOW (-(RAMBUF_ACC_END-32))
    addwf   FSR2L,w
    movlw   HIGH (-(RAMBUF_ACC_END-32))
    addwfc  FSR2H,w
    bnc     isr_ad_sample_pre_save_ok

    ;
    ; reached end of buffer - set FSR2 and disable sampling
    ;
    lfsr    FSR2,(RAMBUF_ACC_END-32-1)

    ;
    ; if last entry is first half of an angle entry then change it to a pause
    ; High bit is only clear if it is an angle.
    ; Could also be a flags entry, but that always has high bit set.
    ;
    btfss   INDF2,7
    setf    INDF2

    movf    POSTINC2,w  ; point to 4th to last entry

    ;
    ; disable sampling?
    ;
#if QUIT_WHEN_SAMPLE_FULL
    bcf     b_accbuf_sample
#endif

isr_ad_sample_pre_save_ok:
    return


;===========================================================================
; subroutine to calculate v_mag  = sqrt(v_magsq)
; At this point
;    v_magsql, v_magsqh = magnitude squared
;
; Result is v_mag
;
;
; v_mag is the exponent of the magnitude
; v_magsqh,l is the input
;
;
isr_calc_mag:
    clrf    v_mag
    nop

    movf    v_magsqh,w
    bnz     isr_calc_mag_4

    ; high byte is 0 - shift left 8 bits (sqrt shift 4 bits)

    movf    v_magsql,w
    bz      isr_calc_mag_done   ; sq==0 so v_mag=0

    movwf   v_magsqh
    bsf     v_mag,2     ; v_mag = 4
    clrf    v_magsql

isr_calc_mag_4:
    movf    v_magsqh,w
    andlw   0xf0
    bnz     isr_calc_mag_2

    ; high nibble is 0 - shift left 4 bits (sqrt shift 2 bits)

    incf    v_mag,f
    incf    v_mag,f
    swapf   v_magsqh,w
    andlw   0xf0
    movwf   v_magsqh
    swapf   v_magsql,w
    andlw   0x0f
    iorwf   v_magsqh,f
    swapf   v_magsql,w  ; low 4 bits are garbage now

isr_calc_mag_2:
    movf    v_magsqh,w
    andlw   0xc0
    bnz     isr_calc_mag_1

    ; high 2 bits are 0 - shift left 2 bits (sqrt shift 1 bit)

    incf    v_mag,f
    rlcf    v_magsql,f
    rlcf    v_magsqh,f
    rlcf    v_magsql,f  ; v_magsql is mostly garbage now
    rlcf    v_magsqh,f

isr_calc_mag_1:
    
    incf    v_mag,f     ; loop needs cnt + 1

    ;
    ; round up (+0x02)
    ; decrement high 2 bits (-0x40)
    ;  (High 2 bits will always be 01, 10, or 11)
    ;
    movlw   0x02-0x40
    addwf   v_magsqh,f

    rrncf   v_magsqh,w
    rrncf   WREG,w
    andlw   0x3f

    ;
    ; save TBLPTR & TABLAT regs in v_magsq and v_isr_save_tablat
    ;
v_isr_save_tablat   equ vaddr+0
vaddr+=1
    movff   TBLPTRL,v_magsql
    movff   TBLPTRH,v_magsqh
    movff   TABLAT,v_isr_save_tablat
    
    addlw   LOW (w_sqrt_table)
    movwf   TBLPTRL
    movlw   HIGH (w_sqrt_table)
    addwfc  TBLPTRU,w       ; add 0 + c  (TBLPTRU is always 0)
    movwf   TBLPTRH
    tblrd*
    movf    TABLAT,w
    bnz     isr_calc_mag_loop_start

    ;
    ; special case when table lookup is 0 (actual value should be 0x100)
    ;
    movlw   0x80
    movwf   TABLAT
    decfsz  v_mag,f
    bra     isr_calc_mag_loop_start

    ;
    ; special case when maxed out - just set result to 0xff
    ;
    setf    v_mag
    return
    
isr_calc_mag_loop:
    bcf     TABLAT,0
    rrncf   TABLAT,f
isr_calc_mag_loop_start:
    decfsz  v_mag,f
    bra     isr_calc_mag_loop

    movff   TABLAT,v_mag

    ;
    ; restore TBLPTR & TABLAT regs from v_magsq and v_isr_save_tablat
    ;
    movff   v_magsql,TBLPTRL
    movff   v_magsqh,TBLPTRH
    movff   v_isr_save_tablat,TABLAT 
isr_calc_mag_done:
    return

    ;
    ; sqrt table, calculated by my_sqrt16.c
    ;
w_sqrt_table:
    db      0x80, 0x83, 0x87, 0x8b, 0x8f, 0x92, 0x96, 0x99
    db      0x9c, 0xa0, 0xa3, 0xa6, 0xa9, 0xac, 0xaf, 0xb2
    db      0xb5, 0xb7, 0xba, 0xbd, 0xc0, 0xc2, 0xc5, 0xc7
    db      0xca, 0xcc, 0xcf, 0xd1, 0xd4, 0xd6, 0xd9, 0xdb
    db      0xdd, 0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xeb, 0xed
    db      0xef, 0xf1, 0xf3, 0xf5, 0xf7, 0xf9, 0xfb, 0xfe
    db      0x00


;===========================================================================
; subroutine to calculate angle
; At this point
;    v_absx = abs val of x
;    v_absy = abs val of y
;    v_angle =
;         0   if x >= 0 && y >= 0
;       -15   if x >= 0 && y <  0
;       -31   if x <  0 && y >= 0
;        16   if x <  0 && y <  0
;
isr_calc_angle:
    movf    v_absx,w
    subwf   v_absy,w
    bnn     isr_ad_angle2   ; if absx <= absy skip this

    ;
    ; absx > absy
    ;
    movlw   7               ; angle = -(angle+7)
    addwf   v_angle,f
    negf    v_angle
    movf    v_absy,w        ; swap absx <-> absy
    movff   v_absx,v_absy
    movwf   v_absx

isr_ad_angle2:
    movf    v_absx,w
    addwf   v_absx,w        ; w = absx * 2
    movwf   v_absx
    addwf   v_absx,f        ; v_absx = absx * 4

    ;
    ; compare 2*absx >? absy
    ;
    subwf   v_absy,w
    bnn     isr_ad_angle3   ; if 2*absx <= absy skip this

    ;
    ; 2*absx > absy
    ;
    movlw   3               ; angle = -(angle+3)
    addwf   v_angle,f
    negf    v_angle
    movf    v_absy,w        ; w = 3*absy
    addwf   v_absy,w
    addwf   v_absy,w
    movff   v_absx,v_absy   ; swap 4*absx and 3*absy
    movwf   v_absx

isr_ad_angle3:
    ;
    ; do one of these comparisons:
    ;     4*absx >? 1*absy
    ;     3*absy >? 4*absx
    ;
    movf    v_absx,w        ; 4*absx or 3*absy
    subwf   v_absy,w        ; absy   or 4*absx
    btfsc   STATUS,N
    incf    v_angle,f       ; v_absx > v_absy - increment angle

    ;
    ; absolute value is resulting angle
    ;
    btfsc   v_angle,7
    negf    v_angle

    ;
    ; compensate for 45 degree rotation of sensor
    ;
    movf    v_angle,w
    addlw   -4
    andlw   0x1f
    movwf   v_angle

    return


#if DBORG
    org     0x0f00
#endif

;###########################################################################
;################################ DEBUG FUNCTIONS ##########################
;###########################################################################

;
; Macros:
;
; DB_BYTE ch,reg   print char and register (save & restore w & other regs)
; DB_FLUSH         flush tx buffer
;

DB_BYTE macro   ch,reg
    movwf   v_dbg_save_w
    movff   reg,v_dbg_low
    movlw   ch
    call    w_dbg_byte
    endm

DB_FLUSH macro
    call    w_dbg_flush
    endm

v_dbg_low           equ vaddr+0 ;
v_dbg_ch            equ vaddr+1 ;
v_dbg_save_w        equ vaddr+2 ;
v_dbg_save_fsr0l    equ vaddr+3 ;
v_dbg_save_fsr0h    equ vaddr+4 ;
vaddr+=5

;
; Functions:
;
; w_dbg_byte   - print a byte with a char
; w_dbg_flush  - flush tx buffer
;


;
; w_dbg_byte - print a byte with a char
;   v_dbg_low - byte
;   v_dbg_char - character
;
w_dbg_byte:
    btfss   RCSTA,SPEN      ; do not bother to print if serial disabled
    return

    movff   FSR0L,v_dbg_save_fsr0l
    movff   FSR0H,v_dbg_save_fsr0h
    call    kk_cout
    COUTL   '='
    movf    v_dbg_low,w
    call    kk_outbyte
    COUTL   ' '

w_dbg_return:
    movff   v_dbg_save_fsr0l,FSR0L
    movff   v_dbg_save_fsr0h,FSR0H
    movf    v_dbg_save_w,w
    return

w_dbg_flush:
    btfsc   RCSTA,SPEN      ; do not bother to print if serial disabled
    btfsc   bk_rs_halt
    return
    call    w_main_run
    movf    vk_tx_cnt,w
    bnz     w_dbg_flush
    return

;###########################################################################
;################################ A/D FUNCTIONS ############################
;###########################################################################

;
; w_ad_init          - turn on  the accelerometer for non-ISR sampling
; w_ad_start         - turn on  the accelerometer
; w_ad_stop          - turn off the accelerometer
; w_ad_sample_finish - turn off accelerometer and flush sample output
; w_sample_start     - start sampling into buffer (call after w_ad_start)
; w_sample_clear     - clear sample buffer to 0
;
;
#if ENAB_ISR_TAP
; w_tap_enable    - clear and enable taps      (call after w_ad_start)
#endif

;===========================================================================
; w_ad_start - turn on the accelerometer & start ISR
;===========================================================================
w_ad_start:
    ;
    ; initialize accelerometer & A/D conversion
    ;
    rcall   w_ad_init

    ;
    ; setup auto-sampling and ISR
    ;
    clrf    CCPR2H
    movlw   REGVAL_CCPRL2L
    movwf   CCPR2L
    movlw   REGVAL_CCP2CON
    movwf   CCP2CON
    bsf     b_adip          ; a/d interrupt is high priority
    bsf     b_adie          ; a/d interrupt is enabled
    movlw   REGVAL_T3CON_AD
    movwf   T3CON           ; start timer3
    return


;===========================================================================
; w_ad_init - turn on the accelerometer (no ISR)
;===========================================================================
w_ad_init:
    ;
    ; disable isr while we set things up
    ;
    bcf     b_gieh          ; disable high priority interrupts

    ;
    ; stop ongoing conversion (if any)
    ;
    rcall   w_ad_stop

    ;
    ; clear timer3
    ;
    clrf    TMR3H
    clrf    TMR3L

    ;
    ; wait until accelerometer settles
    ;
    call    w_pause_ad_settle

    ;
    ; disable sampling
    ;
    bcf     b_accbuf_sample

    ;
    ; setup min/max values
    ;
    setf    v_accx_min
    setf    v_accy_min
    clrf    v_accx_max
    clrf    v_accy_max
    clrf    v_accx
    clrf    v_accy

    ;
    ; init astate
    ;
    clrf    v_angle
    clrf    v_mag

#if ENAB_ISR_TAP
    ;
    ; disable taps
    ;
    bcf     b_tap
    bcf     b_tap_mask
    bcf     b_tap_enable
#endif

    ;
    ; point FSR2 at acceleration buffer
    ;
    lfsr    FSR2,RAMBUF_ACC

    ;
    ; turn on accelerometer
    ;
    bcf     b_acc_disable

    ;
    ; set up timing
    ;
    movlw   KERNVAL_ADCON2
    movwf   ADCON2

    ;
    ; set up to sample x axis
    ;
    movlw   ADCON0_ACCX
    movwf   ADCON0
    bsf     b_do_accx       ; start with x axis

    ;
    ; disable Capture Compare (auto-triggers accelerometer)
    ;
    clrf    CCP2CON

    ;
    ; disable a/d interrupt
    ;
    bcf     b_adie          ; a/d interrupt is disabled
    bcf     b_adif          ; clear any old pending interrupt

    ;
    ; re-enable high priority interrupts
    ;
    bsf     b_gieh          ; enable high priority interrupts

    return

;===========================================================================
; w_ad_sample_finish - turn off accelerometer and flush sample output
;===========================================================================
w_ad_sample_finish:
    rcall   w_ad_stop
    rcall   isr_ad_sample_sendresult
    rcall   isr_ad_sample_sendresult

    ;
    ; mark end of buffer with special sample
    ; (save 2 copies in case first is 2nd half of angle sample)
    ;
    rcall   isr_ad_sample_pre_save

    ;
    ; long pause
    ;
    clrf    POSTINC2        ; time(h) = 0
    setf    POSTINC2        ; time(l) = 0xff
    setf    POSTINC2        ; unused
    setf    POSTINC2        ; angle = 0xff (pause)

    ;
    ; mark end of buffer
    ;
    clrf    POSTINC2
    setf    POSTINC2
    setf    POSTINC2
    movlw   0xfe
    movwf   POSTINC2        ; 0xfe in angle marks end of samples

    return


;===========================================================================
; w_ad_stop - turn off the accelerometer
;===========================================================================
w_ad_stop:
    bcf     b_adie          ; a/d interrupt is disabled
    clrf    T3CON           ; stop timer3
    btfsc   b_adgo
    bra     w_ad_stop       ; wait until any a/d conversion completes

    clrf    ADCON0          ; a/d unit off
    bsf     b_acc_disable   ; disable accelerometer power
    return
    

#if ENAB_ISR_TAP
;===========================================================================
; w_tap_enable - clear and enable taps
;===========================================================================
w_tap_enable:
    bcf     b_tap
    bcf     b_tap_mask
    bsf     b_tap_enable
    clrf    v_tap_bits
    return
#endif


;===========================================================================
; w_sample_start - begin storing samples into buffer
; (call w_ad_start first)
;===========================================================================
w_sample_start:
    bcf     b_accbuf_sample     ; disable sampling
    bcf     b_acc_finish        ; disable finish request
    lfsr    FSR2, RAMBUF_ACC


    ;
    ; init astate buffering
    ;
    clrf    v_angle
    clrf    v_mag
#if USE_CONST_MAGSQ_TH
    movlw   HIGH (-ACC_INIT_TH_MAGSQ_Z)
    movwf   v_th_magsqh
    movlw   LOW (-ACC_INIT_TH_MAGSQ_Z)
    movwf   v_th_magsql
#else
    movff   v_th_magsql_z,v_th_magsql
#endif
    movlw   HIGH (-ACC_TH_TIME_RETIRE)
    movwf   v_th_retimeh
    movlw   LOW (-ACC_TH_TIME_RETIRE)
    movwf   v_th_retimel

    ;
    ; init sampling state vars
    ;
    setf    v_smplc_angle       ; we have no sample angle
    setf    v_smpln_angle       ; we have no new sample angle
    clrf    v_smpln_cntl        ; no new sample time
    clrf    v_smpln_cnth
    clrf    v_smplnz_cntl       ; no new zero sample time
    clrf    v_smplnz_cnth


#if ENAB_W_PARSE
    ;
    ; store initial sample in buffer
    ;
    rcall   w_sample_store_zero
#endif

    bsf     b_accbuf_sample     ; enable sampling
    return

#if ENAB_W_PARSE
;===========================================================================
; w_sample_store_zero - store a zero-entry in acc buffer
; call with b_accbuf_sample not set
;===========================================================================
w_sample_store_zero:
    rcall   isr_ad_sample_pre_save

    setf    POSTINC2        ; time (l) max
    setf    POSTINC2        ; time (h) max
    setf    POSTINC2        ; time (h) max
    setf    POSTINC2        ; angle - none

    return
#endif

;===========================================================================
; w_sample_clear - clear sample & motion buffer to 0
;===========================================================================
w_sample_clear:
    lfsr    FSR0,RAMBUF_ACC
w_sample_clear_loop:
    clrf    POSTINC0

    movlw   LOW RAMBUF_ACC_END
    subwf   FSR0L,w
    movlw   HIGH RAMBUF_ACC_END
    subwfb  FSR0H,w
    bnc     w_sample_clear_loop
    return

    
    
#if DBORG
    org     0x1100
#endif

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

#include "wstrings.inc"

w_sout_base1:
    addlw   LOW w_str_base1
    movwf   TBLPTRL
    movlw   HIGH w_str_base1
    addwfc  TBLPTRU,w           ; add 0 + c  (TBLPTRU is always 0)

    ; fall thru

w_sout_wh:
    btfss   RCSTA,SPEN      ; do not bother to print if serial disabled
    return
    goto    kk_sout_wh

w_outbyte:
    btfss   RCSTA,SPEN      ; do not bother to print if serial disabled
    return
    goto    kk_outbyte

w_outbyte_sp:
    btfss   RCSTA,SPEN      ; do not bother to print if serial disabled
    return
    call    kk_outbyte
    movlw   ' '
    goto    kk_cout

w_sout_base1_cr:
    btfss   RCSTA,SPEN      ; do not bother to print if serial disabled
    return
    rcall   w_sout_base1

    ; fall thru

w_outcr:
    btfss   RCSTA,SPEN      ; do not bother to print if serial disabled
    return
    call    kk_outcr
    bra     w_dbg_flush

    


;###########################################################################
;################################ LED BLINKING #############################
;###########################################################################

#if ENAB_BLINKS
;
; START_BLINK macro begins a blink sequence
;
START_BLINK macro   addr
    movlw   addr-w_blinkpatterns+1
    movwf   v_blink_ptr
    bsf     b_blink
    endm

w_blinkpatterns:
w_blinkpat_left:    ; (down-up)
    db      0xfb, 0x04
    db      0xff, 0x5f
    db      0xf7, 0x04
    db      0xff, 0x5f
    db      0xef, 0x04
    db      0xff, 0x5f
    db      0xdf, 0x04
    db      0xff, 0x5f
    db      0xbf, 0x04
    db      0xff, 0x5f
w_blinkpat_up:
    db      0x7f, 0x04
    db      0xff, 0x5f
    db      0xbf, 0x04
    db      0xff, 0x5f
    db      0xdf, 0x04
    db      0xff, 0x5f
    db      0xef, 0x04
    db      0xff, 0x5f
    db      0xf7, 0x04
    db      0xff, 0x5f
    db      0xfb, 0x04
    db      0x00, 0x00

w_blinkpat_right:   ; (up-down)
    db      0x7f, 0x04
    db      0xff, 0x5f
    db      0xbf, 0x04
    db      0xff, 0x5f
    db      0xdf, 0x04
    db      0xff, 0x5f
    db      0xef, 0x04
    db      0xff, 0x5f
    db      0xf7, 0x04
    db      0xff, 0x5f
w_blinkpat_down:
    db      0xfb, 0x04
    db      0xff, 0x5f
    db      0xf7, 0x04
    db      0xff, 0x5f
    db      0xef, 0x04
    db      0xff, 0x5f
    db      0xdf, 0x04
    db      0xff, 0x5f
    db      0xbf, 0x04
    db      0xff, 0x5f
    db      0x7f, 0x04
    db      0x00, 0x00


w_blinkpat_countdown:
    db      0xff, 0x01
    db      0x03, 0x04
    db      0xff, 0xff
    db      0x07, 0x04
    db      0xff, 0xff
    db      0x0f, 0x04
    db      0xff, 0xff
    db      0x1f, 0x04
    db      0xff, 0xff
    db      0x3f, 0x04
    db      0xff, 0xff
    db      0x7f, 0x04
    db      0x00, 0x00

w_blinkpat_tip:
    db      0xff, 0xff
    db      0xfb, 0x04
    db      0x00, 0x00


;
; Call w_blink every main loop to handle blinking.
; Aceleration interrupt must be on (it implements the timer)
;
w_blink:
    movf    v_blink_ptr,w       ; skip if no sequence is going
    bz      w_blink_end

    addlw   LOW (w_blinkpatterns-1)
    movwf   TBLPTRL
    movlw   HIGH (w_blinkpatterns-1)
    addwfc  TBLPTRU,w       ; add 0 + c  (TBLPTRU is always 0)
    movwf   TBLPTRH
    
    movlw   KERNVAL_TRISB_LED_MASK
    iorwf   TRISB,f

    incf    v_blink_ptr,f   
    incf    v_blink_ptr,f   

    tblrd*+
    movf    TABLAT,w
    bz      w_blink_finish

    andwf   TRISB,f

    tblrd*+
    movff   TABLAT,v_blink_cnt

w_blink_end:
    bcf     b_blink
    return

w_blink_finish:
    clrf    v_blink_ptr
    bra     w_blink_end

#endif

;###########################################################################
;################################ HANDLE TAPS ##############################
;###########################################################################

#if ENAB_OLD_TAP_HANDLE
;
; Handle taps (call when b_tap is set)
;
w_tap:

#if DBPRINT
    ;
    ; debug - print tap
    ;
    COUTL   'T'
    COUTL   'a'
    COUTL   'p'
    movf    v_tap_angle,w
    call    kk_outbyte
    call    kk_outcr
#endif

    ;
    ; clear tap bit
    ;
    bcf     b_tap

    ;
    ; round tap angle to quadrant
    ;
    movf    v_tap_angle,w
    addlw   4
    rrncf   WREG,w
    rrncf   WREG,w
    rrncf   WREG,w
    andlw   0x03

;
; up tap?
;
w_tap_up:
    bnz     w_tap_right

#if ENAB_BLINKS
    START_BLINK     w_blinkpat_up
#endif
    bsf     b_tap_up

    return

;
; right tap?
;
w_tap_right:
    decfsz  WREG,w
    bra     w_tap_down

#if 0
#if ENAB_BLINKS
    START_BLINK     w_blinkpat_right
#endif
#endif
    bsf     b_tap_right

    return

;
; down tap?
;
w_tap_down:
    decfsz  WREG,w
    bra     w_tap_left

#if ENAB_BLINKS
    START_BLINK     w_blinkpat_down
#endif
    bsf     b_tap_down

    return

;
; left tap?
;
w_tap_left:

#if 0
#if ENAB_BLINKS
    START_BLINK     w_blinkpat_left
#endif
#endif
    bsf     b_tap_left

    return

#endif

;###########################################################################
;################################ DRAW CHARACTERS ##########################
;###########################################################################

#if DBORG
    org     0x1300
#endif

;
; DB_DRAW_ABCD   - print a,b,c,d when state transition occurs
; DB_DRAW_LED    - indicate state on LEDs
; DB_DRAW_TAP    - print when tap occurs
; DB_DRAW_SLEEP  - print when we need to sleep
; DB_DRAW_STOP   - print when stopping
; DB_DRAW_TIME   - print min/max time
; DB_DRAW_WHERE  - record where we are in program
;
#define DB_DRAW_ABCD        0
#define DB_DRAW_LED         0
#define DB_DRAW_TAP         1
#define DB_DRAW_SLEEP       1
#define DB_DRAW_STOP        1
#define DB_DRAW_TIME        0
#define DB_DRAW_WHERE       0


#if DB_DRAW_WHERE
v_draw_where1   equ vaddr+0
v_draw_where2   equ vaddr+1
vaddr+=2
DB_DWHERE1  macro   val
    movlw       val
    movwf       v_draw_where1
    endm
DB_DWHERE2  macro   val
    movlw       val
    movwf       v_draw_where2
    endm
#else
DB_DWHERE1  macro   val
    endm
DB_DWHERE2  macro   val
    endm
#endif


v_draw_val      equ vaddr+0 ; pixel value for current column
v_accs          equ vaddr+1 ; v_accx - v_accy
v_maxs          equ vaddr+2 ; max value of v_accs (or threshhold)
v_mins          equ vaddr+3 ; min value of v_accs (or threshhold)
v_last_maxs     equ vaddr+4 ; previous max value of v_accs
v_last_mins     equ vaddr+5 ; previous min value of v_accs
vaddr+=6

v_tdrawl        equ vaddr+0 ; time when drawing should commence
v_tdrawh        equ vaddr+1 ;
v_tendl         equ vaddr+2 ; time when drawing should commence
v_tendh         equ vaddr+3 ;
vaddr+=4

v_zcnt          equ vaddr+0 ; how long since magnitude was near 0 (for tap)
vaddr+=1

v_tapx          equ vaddr+0 ; x and y from tap
v_tapy          equ vaddr+1 ;
vaddr+=2


;
; all LEDs off
; accelerometer on
;
#if HW_VERSION>=2
DRAW2_TRISB_OFF_VAL equ     KERNVAL_TRISB_LED_MASK
#else
DRAW2_TRISB_OFF_VAL equ     0xfd
#endif

;
; begin draw sequence
;
w_draw2:
    ;
    ; check for scrolling (long message)
    ;
    rcall   w_wscrollcheck

    call    w_ad_init
    clrf    v_accx
    clrf    v_accy

    movlw   DRAW2_TRISB_OFF_VAL
    movwf   v_draw_val

    ;
    ; enable brightest LEDS
    ;
    bcf     b_maxbright_disable
    bcf     b_medbright_disable

    ;
    ; wait before sleeping
    ; allow taps immediately
    ;
    movlw   DRAW2_SLEEP_WAIT    
    movwf   v_sleep_wait
    setf    v_tap_wait

    ;
    ; tap stuff
    ;
    setf    v_zcnt
    bcf     b_d2_tap
    bcf     b_d2_sleep
    bcf     b_d2_maybe_tap

    ;
    ; wait for ACC & A/D to warm up
    ;
    rcall   w_pause_ad_settle

    ;
    ; read acc
    ;
    rcall   w_d2_column

    movf    v_accs,w
    movwf   v_last_maxs
    movwf   v_last_mins

    ;
    ; init v_maxs, v_mins
    ;
    rcall   w_d2_calcmax
    rcall   w_d2_calcmin

    ;
    ; init time
    ;
    rcall   w_d2_trans_min
    rcall   w_d2_trans_min

;==============================
; transition to lookmax state
;==============================
w_d2_lookmax_trans:
    DB_DWHERE1  0x11
    rcall   w_d2_trans_max
w_d2_lookmax_drawstop:
    DB_DWHERE1  0x12
    rcall   w_d2_draw_stop

#if DB_DRAW_ABCD
    COUTL   'a'
    call    kk_outcr
#endif
#if DB_DRAW_LED
    bcf     bk_led4
    bsf     bk_led3
    bsf     bk_led2
    bsf     bk_led1
#endif

;==============================
; w_d2_lookmax STATE
;==============================
w_d2_lookmax:
    DB_DWHERE1  0x21
    rcall   w_d2_column

    DB_DWHERE1  0x22
    rcall   w_main_run
    btfsc   bk_rs_halt
    bra     w_d2_stop
    DB_DWHERE1  0x23

    ;
    ; every 1/4 sec inc mins to ensure we dont get stuck
    ;
    btfsc   bk_qsec
    rcall   w_d2_qsec_lookmax

    DB_DWHERE1  0x24
    rcall   w_d2_column

    btfsc   b_d2_tap
    bra     w_d2_tap
    btfsc   b_d2_sleep
    bra     w_d2_sleep

    movf    v_accs,w
    cpfsgt  v_maxs
    movwf   v_maxs

    cpfsgt  v_mins
    bra     w_d2_lookmax


;==============================
; transition to lookmin state
;==============================
w_d2_lookmin_trans:
    DB_DWHERE1  0x31
    rcall   w_d2_draw_stop
    DB_DWHERE1  0x32
    rcall   w_d2_trans_min

#if DB_DRAW_ABCD
    COUTL   'b'
    call    kk_outcr
#endif
#if DB_DRAW_LED
    bsf     bk_led4
    bcf     bk_led3
    bsf     bk_led2
    bsf     bk_led1
#endif

;==============================
; w_d2_lookmin STATE
;==============================
w_d2_lookmin:
    DB_DWHERE1  0x41
    rcall   w_d2_column

    DB_DWHERE1  0x42
    rcall   w_main_run
    btfsc   bk_rs_halt
    bra     w_d2_stop
    DB_DWHERE1  0x43

#if 0
    ;
    ; test rx overflow
    ;
    COUTL   'X'
    call    kk_outcr
    bra     w_d2_lookmin
#endif

#if DB_DRAW_ABCD
    btfss   bk_qsec
    bra     w_d2_lookmin_noprint

    COUTL   'N'
    COUTL   ' '
    movf    v_last_mins,w
    call    kk_outbyte
    COUTL   ' '
    movf    v_mins,w
    call    kk_outbyte
    COUTL   ' '
    movf    v_maxs,w
    call    kk_outbyte
    COUTL   ' '
    movf    v_last_maxs,w
    call    kk_outbyte

    call    kk_outcr

w_d2_lookmin_noprint:
#endif

    ;
    ; every 1/4 sec dec maxs to ensure we dont get stuck
    ;
    btfsc   bk_qsec
    rcall   w_d2_qsec_lookmin

    DB_DWHERE1  0x44
    rcall   w_d2_column

    btfsc   b_d2_tap
    bra     w_d2_tap
    btfsc   b_d2_sleep
    bra     w_d2_sleep

    movf    v_accs,w
    cpfslt  v_mins
    movwf   v_mins

    cpfsgt  v_maxs
    bra     w_d2_lookmax_trans

    btfss   b_draw_ok
    bra     w_d2_lookmin

    ;
    ; start drawing if it is time
    ;
    movf    v_tl,w
    subwf   v_tdrawl,w
    movf    v_th,w
    subwfb  v_tdrawh,w
    bnn     w_d2_lookmin

    DB_DWHERE1  0x45
    rcall   w_d2_column

    ;
    ; scroll text
    ;
#if SCROLL_SPEED
    rcall   w_wscroll_inc
    rcall   w_d2_column
    rcall   w_wscroll_inc
    btfss   v_scrollspd,0
    bra     w_d2_draw_start

    rcall   w_d2_column
    rcall   w_wscroll_inc
    btfss   v_scrollspd,1
    bra     w_d2_draw_start

    rcall   w_d2_column
    rcall   w_wscroll_inc
    btfss   v_scrollspd,2
    bra     w_d2_draw_start

    rcall   w_d2_column
    rcall   w_wscroll_inc
    btfss   v_scrollspd,3
    bra     w_d2_draw_start

    rcall   w_d2_column
    rcall   w_wscroll_inc
    rcall   w_d2_column
    rcall   w_wscroll_inc
    btfss   v_scrollspd,4
    bra     w_d2_draw_start

    rcall   w_d2_column
    rcall   w_wscroll_inc
    rcall   w_d2_column
    rcall   w_wscroll_inc
    btfss   v_scrollspd,5
    bra     w_d2_draw_start

    rcall   w_d2_column
    rcall   w_wscroll_inc
    rcall   w_d2_column
    rcall   w_wscroll_inc
    btfss   v_scrollspd,6
    bra     w_d2_draw_start

    rcall   w_d2_column
    rcall   w_wscroll_inc
    rcall   w_d2_column
    rcall   w_wscroll_inc
    btfss   v_scrollspd,7
    bra     w_d2_draw_start

    rcall   w_d2_column
    rcall   w_wscroll_inc
    rcall   w_d2_column
    rcall   w_wscroll_inc

    bra     w_d2_draw_start

#else
    rcall   w_wscroll_inc
    rcall   w_d2_column
    rcall   w_wscroll_inc
    rcall   w_d2_column
    rcall   w_wscroll_inc

    bra     w_d2_draw_start
#endif

;==============================
; restart drawing
;==============================
w_d2_draw_restart:
    movwf   v_mins

;==============================
; start drawing 
;==============================
w_d2_draw_start:
    DB_DWHERE1  0x51
    rcall   w_d2_column

    rcall   w_main_run
    btfsc   bk_rs_halt
    bra     w_d2_stop

    ;
    ; setting v_sleep_wait indicates we see motion (do not sleep)
    ; setting v_tap_wait   indicates we see motion (ignore tap)
    ;
    movlw   DRAW2_SLEEP_WAIT    
    movwf   v_sleep_wait
    movlw   DRAW2_TAP_WAIT  
    movwf   v_tap_wait

    movff   v_drawbuf_starth,FSR0H
    movff   v_drawbuf_startl,FSR0L
    movlw   1
    movwf   v_draw_cnt

    bsf     b_draw_go

#if DB_DRAW_ABCD
    COUTL   'c'
    call    kk_outcr
#endif
#if DB_DRAW_LED
    bsf     bk_led4
    bsf     bk_led3
    bcf     bk_led2
    bsf     bk_led1
#endif

;==============================
; w_d2_drawmin STATE
;==============================
w_d2_drawmin:
    DB_DWHERE1  0x61
    rcall   w_d2_column

    DB_DWHERE1  0x62
    ;
    ; stop drawing if it is time
    ;
    movf    v_tl,w
    subwf   v_tendl,w
    movf    v_th,w
    subwfb  v_tendh,w
    bn      w_d2_drawmin_done

    DB_DWHERE1  0x63
    ;
    ; if s<mins then restart draw
    ;
    movf    v_accs,w
    cpfslt  v_mins
    bra     w_d2_draw_restart

    DB_DWHERE1  0x64
    ;
    ; if s>maxs then transition to drawmax state
    ;
    cpfsgt  v_maxs
    bra     w_d2_drawmax_trans

    DB_DWHERE1  0x65
    rcall   w_d2_column


    btfsc   b_draw_go
    bra     w_d2_drawmin

    ;
    ; Done drawing
    ;
w_d2_drawmin_done:
    rcall   w_d2_draw_stop
    bra     w_d2_lookmin
    


;==============================
; transition to drawmax state
;==============================
w_d2_drawmax_trans:
    rcall   w_d2_trans_max

#if DB_DRAW_ABCD
    COUTL   'd'
    call    kk_outcr
#endif
#if DB_DRAW_LED
    bsf     bk_led4
    bsf     bk_led3
    bsf     bk_led2
    bcf     bk_led1
#endif

;==============================
; w_d2_drawmax STATE
;==============================
w_d2_drawmax:
    rcall   w_d2_column

    ;
    ; if s<mins then stop draw
    ;
    movf    v_accs,w
    cpfslt  v_mins
    bra     w_d2_lookmin_trans

    ;
    ; keep track of maxs
    ;
    cpfsgt  v_maxs
    movwf   v_maxs

    rcall   w_d2_column

    ;
    ; stop drawing if it is time
    ;
    movf    v_tl,w
    subwf   v_tendl,w
    movf    v_th,w
    subwfb  v_tendh,w
    bn      w_d2_drawax_done


    btfsc   b_draw_go
    bra     w_d2_drawmax

    ;
    ; Done drawing
    ;
w_d2_drawax_done:
    rcall   w_d2_draw_stop
    bra     w_d2_lookmax_drawstop
    
    
;===========================================================================
; tap occurred (single down tap)
;===========================================================================
w_d2_tap:
    DB_DWHERE1  0x71

#if DB_DRAW_TAP
    SOUT1   w_str_d2tap
    movf    v_tapx,w
    call    w_outbyte_sp
    movf    v_tapy,w
    call    w_outbyte_sp
    COUTL   ' '
    movf    v_absx,w
    call    w_outbyte_sp
    movf    v_absy,w
    call    w_outbyte_sp
    movf    v_absx,w
    mulwf   v_absy
    movf    PRODH,w
    call    w_outbyte
    movf    PRODL,w
    call    w_outbyte
    call    w_outcr
#endif

    bcf     b_d2_sleep  ; no sleeping if tap occurred

    bra     w_d2_stop

;===========================================================================
; time to sleep
;===========================================================================
w_d2_sleep:
    DB_DWHERE1  0x74

#if DB_DRAW_SLEEP
    SOUT1CR w_str_d2sleep
#endif

    ;bra        w_d2_stop

    ; fall through to w_d2_stop

;===========================================================================
; return from draw sequence
;===========================================================================
w_d2_stop:
    DB_DWHERE1  0x72
#if DB_DRAW_STOP
    SOUT1CR w_str_d2stop
#endif

    ;
    ; disable bright LEDS
    ;
    bsf     b_maxbright_disable
    bsf     b_medbright_disable

    return

;===========================================================================
; error occurred
;===========================================================================
w_d2_error:
    DB_DWHERE1  0x73
    ERRL        ERROR_B2_DRAW2_AD_OVERRUN

;===========================================================================
; call this every quarter second
;===========================================================================
DRAW2_SLEEP_WAIT    equ 64  ; # of quarter seconds of motionless before sleep
;DRAW2_SLEEP_WAIT   equ 128 ; # of quarter seconds of motionless before sleep
;DRAW2_SLEEP_WAIT   equ 20  ; # of quarter seconds of motionless before sleep
DRAW2_TAP_WAIT      equ 2   ; # of quarter seconds of motionless before tap ok

w_d2_qsec_lookmin:
w_d2_qsec_lookmax:

#if DB_DRAW_TIME
v_dth_dbmin     equ vaddr+0
v_dtl_dbmin     equ vaddr+1
v_dth_dbmax     equ vaddr+2
v_dtl_dbmax     equ vaddr+3
v_dt_dbcnt      equ vaddr+4
v_dt_dbpcnt     equ vaddr+5
vaddr+=6

v_mins_dbmin        equ vaddr+0
v_mins_dbmax        equ vaddr+1
v_maxs_dbmin        equ vaddr+2
v_maxs_dbmax        equ vaddr+3
vaddr+=4

    decf    v_dt_dbpcnt,f
    bnn     w_d2_qsec_skiptime

    movlw   10
    movwf   v_dt_dbpcnt

    movf    v_dth_dbmin,w
    call    kk_outbyte
    movf    v_dtl_dbmin,w
    call    kk_outbyte
    COUTL   ' '
    movf    v_dth_dbmax,w
    call    kk_outbyte
    movf    v_dtl_dbmax,w
    call    kk_outbyte
    COUTL   ' '
    COUTL   ' '
    movf    v_dt_dbcnt,w
    call    kk_outbyte
#if DB_DRAW_WHERE
    COUTL   ' '
    movf    v_draw_where1,w
    call    kk_outbyte
#endif
    COUTL   ' '
    COUTL   ' '
    movf    v_mins_dbmin,w
    call    kk_outbyte
    COUTL   ' '
    movf    v_mins,w
    call    kk_outbyte
    COUTL   ' '
    movf    v_mins_dbmax,w
    call    kk_outbyte
    COUTL   ' '
    COUTL   ' '
    movf    v_maxs_dbmin,w
    call    kk_outbyte
    COUTL   ' '
    movf    v_maxs,w
    call    kk_outbyte
    COUTL   ' '
    movf    v_maxs_dbmax,w
    call    kk_outbyte
    call    kk_outcr

    DB_FLUSH

    setf    v_dth_dbmin
    setf    v_dtl_dbmin
    clrf    v_dth_dbmax
    clrf    v_dtl_dbmax
    clrf    v_dt_dbcnt

w_d2_qsec_skiptime:
#endif


w_d2_qsec_common:
    ;
    ; still long enough to sleep?
    ;
    dcfsnz  v_sleep_wait,f
    bsf     b_d2_sleep

    ;
    ; still long enough to allow taps?
    ;
    btfss   v_tap_wait,7    ; clamp to -1 (0xff)
    decf    v_tap_wait,f

    return

;===========================================================================
; stop drawing
;===========================================================================
w_d2_draw_stop:
    bcf     b_draw_go
    bcf     b_draw_ok
    movlw   DRAW2_TRISB_OFF_VAL
    movwf   v_draw_val
    return

;===========================================================================
; transition from lookmax to lookmin
;===========================================================================
w_d2_trans_min:
    rcall   w_d2_column

    movf    v_maxs,w
    movwf   v_last_maxs

#if DB_DRAW_TIME
    cpfsgt  v_maxs_dbmax
    movwf   v_maxs_dbmax
    cpfslt  v_maxs_dbmin
    movwf   v_maxs_dbmin
#endif

    rcall   w_d2_calcmax

    rcall   w_d2_column

    ;
    ; disable current draw (should be 0 already)
    ; enable next draw
    ;
    bcf     b_draw_go
    bsf     b_draw_ok

    ;
    ; disable drawing if last_maxs-last_mins < threshhold
    ;
    movf    v_last_mins,w
    subwf   v_last_maxs,w
    addlw   -ACC_TH_MAG_DRAW
    btfss   STATUS,C
    bcf     b_draw_ok

    rcall   w_d2_column

    ;
    ; copy old time to v_dt
    ;
    movf    v_t0l,w
    movwf   v_dtl
    movf    v_t0h,w
    movwf   v_dth

    rcall   w_d2_column

    ;
    ; calc v_dt
    ;
    movf    v_tl,w
    movwf   v_t0l
    movwf   v_tdrawl
    movwf   v_tendl
    bsf     STATUS,C
    subfwb  v_dtl,f
    movf    v_th,w
    movwf   v_t0h
    movwf   v_tdrawh
    movwf   v_tendh
    subfwb  v_dth,f

    rcall   w_d2_column

    ;
    ; disable drawing if time >= 1sec
    ;
    movf    v_dth,w
    andlw   0xf0
    btfss   STATUS,Z
    bcf     b_draw_ok


#if DB_DRAW_TIME
    movf    v_dtl,w
    subwf   v_dtl_dbmin,w
    movf    v_dth,w
    subwfb  v_dth_dbmin,w
    btfsc   STATUS,C
    movff   v_dtl,v_dtl_dbmin
    btfsc   STATUS,C
    movff   v_dth,v_dth_dbmin

    movf    v_dtl,w
    subwf   v_dtl_dbmax,w
    movf    v_dth,w
    subwfb  v_dth_dbmax,w
    btfss   STATUS,C
    movff   v_dtl,v_dtl_dbmax
    btfss   STATUS,C
    movff   v_dth,v_dth_dbmax

    infsnz  v_dt_dbcnt,f
    setf    v_dt_dbcnt
#endif

    ;
    ; calc v_tdraw & v_tend
    ;
    bcf     STATUS,C
    rrcf    v_dth,f
    rrcf    v_dtl,f

    movf    v_dtl,w
    addwf   v_tendl,f
    movf    v_dth,w
    addwfc  v_tendh,f

    rcall   w_d2_column

    bcf     STATUS,C
    rrcf    v_dth,f
    rrcf    v_dtl,f

#if 0
    movf    v_dtl,w
    addwf   v_tendl,f
    movf    v_dth,w
    addwfc  v_tendh,f
#else
    nop
    nop
    nop
    nop
#endif

    movf    v_dtl,w
    addwf   v_tdrawl,f
    movf    v_dth,w
    addwfc  v_tdrawh,f

    rcall   w_d2_column

#if 1
    bcf     STATUS,C
    rrcf    v_dth,f
    rrcf    v_dtl,f

    movf    v_dtl,w
    addwf   v_tendl,f
    movf    v_dth,w
    addwfc  v_tendh,f

    rcall   w_d2_column
#endif

    return

;===========================================================================
; transition from lookmin to lookmax
;===========================================================================
w_d2_trans_max:
    movf    v_mins,w
    movwf   v_last_mins

#if DB_DRAW_TIME
    cpfsgt  v_mins_dbmax
    movwf   v_mins_dbmax
    cpfslt  v_mins_dbmin
    movwf   v_mins_dbmin
#endif

    rcall   w_d2_column

    ; fall thru to w_d2_calcmin

;===========================================================================
; calc max (threshhold) from lastmax & lastmin
;===========================================================================
w_d2_calcmin:
    ;
    ; use mins = (3*last_mins + last_maxs)/4
    ;
    rrncf   v_last_mins,w
    andlw   0x7e
    movwf   v_mins
    rrncf   WREG,w
    addwf   v_mins,f
    rrncf   v_last_maxs,w
    rrncf   WREG,w
    andlw   0x3f
    addwf   v_mins,f

#if 1
    ;
    ; clamp to max 0x70 (stability)
    ;
    movlw   0x70
    cpfsgt  v_mins
    movwf   v_mins
#endif

    return

;===========================================================================
; calc max (threshhold) from lastmax & lastmin
;===========================================================================
w_d2_calcmax:
    rrncf   v_last_maxs,w
    andlw   0x7e
    movwf   v_maxs
    rrncf   WREG,w
    addwf   v_maxs,f
    rrncf   v_last_mins,w
    rrncf   WREG,w
    andlw   0x3f
    addwf   v_maxs,f
    return


;===========================================================================
; clear pixels (off) and draw column
;===========================================================================
w_d2_column_clr:
    movlw   DRAW2_TRISB_OFF_VAL
    movwf   v_draw_val

    ; fall thru to w_d2_column

;===========================================================================
; draw 1 column of leds (several times)
;===========================================================================
; also read accelerometer and detect taps
; b_d2_tap is set if tap detected
;
w_d2_column:
    bsf     b_adgo

    ;
    ; read time
    ;
    rcall   w_read_time

    rcall   w_d2_pcol       ; draw column pass 1

    ;
    ; a/d conversion should be done by now
    ;
    btfsc   b_adgo
    bra     w_d2_error

    ;
    ; read X acceleration
    ;
    movf    ADRESH,w
    movwf   v_accx
    movwf   v_accs
    addwf   v_avgx_neg,w
    movwf   v_absx
    movlw   ADCON0_ACCY
    movwf   ADCON0

    rcall   w_d2_pcol       ; draw column pass 2

    bsf     b_adgo

    ;
    ; check for tap (A)
    ;
    btfsc   v_tap_wait,7
    bsf     b_d2_maybe_tap

    rcall   w_d2_pcol       ; draw column pass 3

    ;
    ; a/d conversion should be done by now
    ;
    btfsc   b_adgo
    bra     w_d2_error

    ;
    ; read Y acceleration
    ;
    movf    ADRESH,w
    movwf   v_accy
    subwf   v_accs,f
    addwf   v_avgy_neg,w
    movwf   v_absy
    movlw   ADCON0_ACCX
    movwf   ADCON0

    rcall   w_d2_pcol       ; draw column pass 4

    ;
    ; check for tap (B)
    ;
    rrcf    v_accy,w
    bsf     WREG,7
    movwf   v_accs
    rrcf    v_accx,w
    andlw   0x7f
    subwf   v_accs,f

    rcall   w_d2_pcol       ; draw column pass 5

    ;
    ; check for tap (C)
    ;
    movf    v_absx,w
    addwf   v_absy,w
    btfss   WREG,7
    bcf     b_d2_maybe_tap

    rcall   w_d2_pcol       ; draw column pass 6

    ;
    ; check for tap (D)
    ; find magsq
    ;
    btfsc   v_absx,7
    negf    v_absx
    btfsc   v_absy,7
    negf    v_absy
    movf    v_absx,w
    mulwf   v_absy

    rcall   w_d2_pcol       ; draw column pass 7
    
    ;
    ; check for tap (E)
    ;
    movf    PRODH,w
    addlw   -0x03           ; TAP MAGSQH THRESH
    btfss   STATUS,C
    bcf     b_d2_maybe_tap

    rcall   w_d2_pcol       ; draw column pass 8
    
    ;
    ; check for tap (F)
    ;
    movf    PRODH,w
    addlw   -0x01
    movlw   5               ; SLOPE THRESH: smaller = steeper slope required
    btfss   STATUS,C
    movwf   v_zcnt

    rcall   w_d2_pcol       ; draw column pass 9
    
    ;
    ; check for tap (G)
    ;
    btfss   v_zcnt,7
    decf    v_zcnt,f

    btfsc   v_zcnt,7
    bcf     b_d2_maybe_tap

    btfss   b_d2_tap        ; only record first tap
    btfss   b_d2_maybe_tap
    return

    ;
    ; tap occurred! 
    ;
    movff   v_accx,v_tapx
    movff   v_accy,v_tapy
    bsf     b_d2_tap

#if 0
    COUTL   'T'
    COUTL   'a'
    COUTL   'p'
    COUTL   ' '
    movf    v_accx,w
    call    kk_outbyte
    COUTL   ' '
    movf    v_accy,w
    call    kk_outbyte
    COUTL   ' '
    COUTL   ' '
    movf    v_absx,w
    call    kk_outbyte
    COUTL   ' '
    movf    v_absy,w
    call    kk_outbyte
    COUTL   ' '
    movf    PRODH,w
    call    kk_outbyte
    movf    PRODL,w
    call    kk_outbyte

    COUTL   ' '
    COUTL   ' '
    movf    v_zcnt,w
    call    kk_outbyte
    

    call    kk_outcr

    DB_FLUSH
#endif

    return

;===========================================================================
; light each LED in column (if needed)
;===========================================================================
w_d2_pcol:
    movlw   0x7f
    movwf   v_draw_mask

w_d2_pcol_loop:
    movf    v_draw_mask,w
    iorwf   v_draw_val,w
    bcf     WREG,1      ; accelerometer enabled
    bsf     WREG,0      ; input E in tristate 
#if DB_DRAW_LED
    nop
#else
    movwf   TRISB
#endif

    rrncf   v_draw_mask,f
    btfsc   v_draw_mask,2   ; loop til mask has enabled each led
    bra     w_d2_pcol_loop

    movlw   DRAW2_TRISB_OFF_VAL
#if DB_DRAW_LED
    nop
#else
    movwf   TRISB
#endif

    decfsz  v_draw_cnt,f
    return

    movlw   20          ; Character width
    movwf   v_draw_cnt

    btfss   b_draw_go
    return

    ;
    ; fetch next column value (off if disabled)
    ;
    movf    POSTINC0,w
    bz      w_d2_pcol_end
    movwf   v_draw_val
    return

w_d2_pcol_end:
    movlw   DRAW2_TRISB_OFF_VAL
    movwf   v_draw_val

    lfsr    FSR0,RAMBUF_DRAW+1  ; start at beginning of message

    btfsc   b_draw_scroll
    return

    bcf     b_draw_go
    bcf     b_draw_ok
    return

;###########################################################################
;################################ WAND PRINTF ##############################
;###########################################################################

#if DBORG
    org     0x1780
#endif


;===========================================================================
; fill in buffer with characters
;
; w_wclear  clear wand outbuffer
; w_wcout   output ascii char to wand draw buffer
; w_woutidx output char corresponding to index
; w_wpixels  output w as a vertical row of pixels - bit7 is base, bit2 is tip
; w_scroll_faster  make scrolling faster
; w_scroll_slower  make scrolling slower
;

;
; Clear the draw buffer
;
w_wclear:
    movlw   HIGH RAMBUF_DRAW
    movwf   FSR0H
    movwf   v_drawbuf_starth
    movwf   v_drawbuf_endh
    
    clrf    FSR0L
    movlw   1
    movwf   v_drawbuf_startl
    movwf   v_drawbuf_endl

    clrf    POSTINC0
    
w_wclear_loop:
#if (RAMBUF_DRAW_END-RAMBUF_DRAW) != 0xc00
    error   "Hardcoded to len=0xc00"
endif
    setf    POSTINC0
    setf    POSTINC0
    setf    POSTINC0
    setf    POSTINC0
    setf    POSTINC0
    setf    POSTINC0
    setf    POSTINC0
    setf    POSTINC0
    setf    POSTINC0
    setf    POSTINC0
    setf    POSTINC0
    setf    POSTINC0
    incfsz  v_drawbuf_endl,f
    bra     w_wclear_loop

    setf    POSTINC0
    setf    POSTINC0

    clrf    POSTINC0
    incf    v_drawbuf_endl,f

    ; fall thru to w_woutsp

;
; output space
;
w_woutsp:
    movlw   0
    bra     w_woutidx

;
; output space or exclamation point
;
w_woutexclamation:
    movlw   1
    bra     w_woutidx

;
; output nibble
;
w_woutnib:
    andlw   0x0f
    addlw   '0'
    cpfsgt  vki_ten
    addlw   'A'-'0'-10

    ; fall thru to w_wcout

;
; Add char to draw buffer
;
w_wcout:
    andlw   0x5f
    btfsc   WREG,6
    addlw   -0x20
    addlw   -1
    bz      w_woutexclamation
    addlw   1-0x60
    bnn     w_woutsp
    addlw   0x60-0x0b
    bn      w_woutsp
    addlw   2

w_woutidx:
    ;
    ; multiply w times 5 and add w_char_first
    ;
    clrf    TBLPTRH
    movwf   TBLPTRL
    rlncf   TBLPTRL,f
    rlncf   TBLPTRL,f
    addwf   TBLPTRL,f
    btfsc   STATUS,C
    incf    TBLPTRH,f
    movlw   LOW w_char_first
    addwf   TBLPTRL,f
    movlw   HIGH w_char_first
    addwfc  TBLPTRH,f

    rcall   w_wpixels_tblrd
    rcall   w_wpixels_tblrd
    rcall   w_wpixels_tblrd
    rcall   w_wpixels_tblrd
    rcall   w_wpixels_tblrd
    setf    TABLAT
    rcall   w_wpixels_tablat
    bra     w_wpixels_tablat


w_wpixels_tblrd:
    tblrd*+
w_wpixels_tablat:
    movf    TABLAT,w
w_wpixels:
    movff   v_drawbuf_endh,FSR0H
    movff   v_drawbuf_endl,FSR0L
    movwf   POSTINC0

    movf    INDF0,w
    bz      w_wpixels_end   ; end of buffer - do not inc pointer

    movff   FSR0H,v_drawbuf_endh
    movff   FSR0L,v_drawbuf_endl

w_wpixels_end:
    return

;
; w_scroll_faster  make scrolling faster
;
w_scroll_faster:
    bsf     STATUS,C
    rlcf    v_scrollspd,f
    return

;
; w_scroll_slower  make scrolling slower
;
w_scroll_slower:
    bcf     STATUS,C
    rrcf    v_scrollspd,f
    return


;===========================================================================
; w_wscrollcheck  - check for scrolling
;===========================================================================
;
; check for scrolling and add spaces at end
;
#define MIN_SCROLL_WIDTH    (7*7)
w_wscrollcheck:
    bcf     b_draw_scroll
    movff   v_drawbuf_endh,FSR0H
    movff   v_drawbuf_endl,FSR0L

    ;
    ; search for one that is not 0xff
    ;
w_wscrollcheck_loop:
    infsnz  POSTDEC0,w
    bra     w_wscrollcheck_loop

    movf    POSTINC0,w
    movf    POSTINC0,w

    ;
    ; turn scrolling on if 0x23 columns or more
    ;
    movlw   LOW (-(RAMBUF_DRAW+MIN_SCROLL_WIDTH))
    addwf   FSR0L,w
    movlw   HIGH (-(RAMBUF_DRAW+MIN_SCROLL_WIDTH))
    addwfc  FSR0H,w
    btfss   STATUS,C
    return              ; scrolling off

    ;
    ; scrolling on
    ;
    bsf     b_draw_scroll

    movf    FSR0H,w
    movwf   v_drawbuf_endh
    movwf   v_drawbuf_starth
    movf    FSR0L,w
    movwf   v_drawbuf_endl
    movwf   v_drawbuf_startl

    ;
    ; add 3 spaces
    ;
    rcall   w_woutsp
    rcall   w_woutsp

    ;
    ; mark end with a 0x00
    ;
    clrf    TABLAT
    rcall   w_wpixels_tablat

    ;
    ; back up pointer to just before 0x00 and save it
    ;
    movf    POSTDEC0,w
    movf    POSTDEC0,w

    movff   FSR0H,v_drawbuf_endh
    movff   FSR0L,v_drawbuf_endl

w_wscrollcheck_done:
    return

;===========================================================================
; w_wscroll_inc - scroll 1 column
;===========================================================================
w_wscroll_inc:
    movff   v_drawbuf_starth,FSR0H
    movff   v_drawbuf_startl,FSR0L

    btfss   b_draw_scroll
    return

    movf    POSTINC0,w
    bz      w_wscroll_inc_wrap

    movff   FSR0H,v_drawbuf_starth
    movff   FSR0L,v_drawbuf_startl
    return

w_wscroll_inc_wrap:
    movlw   HIGH RAMBUF_DRAW
    movwf   v_drawbuf_starth
    movlw   1
    movwf   v_drawbuf_startl
    return

;===========================================================================
; output strings from tblptrl,h
;===========================================================================
    ;
    ; w_wsout   prints string at v_strl,h
    ; w_wsout_wh is just like w_wsout, but w is first copied to v_strh
    ; w_wsout_base0 adds w to kki_str_base0 and prints that string
    ; w_wsout_base1 adds w to w_str_base1 and prints that string
    ;
w_wsout_base0:
    addlw   LOW kki_str_base0
    movwf   v_strl
    movlw   HIGH kki_str_base0
    addwfc  TBLPTRU,w           ; add 0 + c  (TBLPTRU is always 0)
    bra     w_wsout_wh

w_wsout_base1:
    addlw   LOW w_str_base1
    movwf   v_strl
    movlw   HIGH w_str_base1
    addwfc  TBLPTRU,w           ; add 0 + c  (TBLPTRU is always 0)

    ; fall through to w_wsout_wh
    
w_wsout_wh:
    movwf   v_strh
w_wsout:
    bra     w_wsout_start

w_wsout_loop:
    rcall   w_wcout
w_wsout_start:
    movff   v_strl,TBLPTRL
    movff   v_strh,TBLPTRH
    tblrd*+
    movff   TBLPTRL,v_strl
    movff   TBLPTRH,v_strh
    movf    TABLAT,w
    bnz     w_wsout_loop
    return



w_char_first:
#include        "wchars.inc"

;###########################################################################
;################################ PAUSE ####################################
;###########################################################################

;
; w_read_time       - read TMR1 into v_th,v_tl
; w_pause_ad_settle - pause long enough for accelerometer & A/D to settle
;                        (long enough for turn-on and after LED turn on/off)
; w_pause_qsec      - pause for 1/4 sec
; w_pause_5ms       - pause for 5 ms
; w_pause_w         - pause for WREG * 244 usec
; w_pause_t0h_w     - pause for v_t0h,w * 244 usec
;
; w_timer_set_fsr0_w   - set timer to expire at now + w*244usec
; w_timer_set_fsr0     - set timer to expire at certain time
; w_timer_check_fsr0   - check if timer expired. N set if expired, else N clr.
;
; w_qsectmr0_set      - set quarter second timer #0 to expire in w seconds
; w_qsectmr1_set      - set quarter second timer #1 to expire in w seconds
;                           (up to 255 = 63.75 sec)
;                           b_qsectmr{0,1}_done is cleared until time expires
;
;
; Note: TMR1L increments every 244 usec
;

;
; pause for 1/4 sec
;
w_pause_qsec:
    movlw   HIGH 1025
    movwf   v_t0h
    movlw   LOW 1025
    bra     w_pause_t0h_w

;
; pause for 5 msec
;
w_pause_ad_settle:
w_pause_5ms:
    movlw   20

;
; pause for w * 244usec
;
w_pause_w:
    clrf    v_t0h
;
; pause for v_t0h,w * 244usec
;
w_pause_t0h_w:
    movwf   v_t0l
;
; pause for v_t0h,l * 244usec
;
    rcall   w_read_time
    movf    v_tl,w
    addwf   v_t0l,f
    movf    v_th,w
    addwfc  v_t0h,f

    rcall   w_main_run

w_pause_loop:
    rcall   w_read_time
    
    movf    v_tl,w
    subwf   v_t0l,w
    movf    v_th,w
    subwfb  v_t0h,w

    bnn     w_pause_loop
    return

;
; Read time into v_th,l
;
w_read_time:
    movf    TMR1H,w
    movwf   v_th
    movff   TMR1L,v_tl
    subwf   TMR1H,w
    bnz     w_read_time
    return


;
; Add the current time to w and place the result in the registers pointer to 
; by FSR0.  FSR0 should point to the low value.  If the result is 0 it will be
; incremented so the final result is never 0.
;
; time units are 244usec
;
w_timer_set_fsr0_w:
    movwf   POSTINC0
    clrf    POSTDEC0

;
; Add the current time to the time set in the registers pointer to 
; by FSR0.  FSR0 should point to the low value.  If the result is 0 it will be
; incremented so the final result is never 0.
;
; time units are 244usec
;
w_timer_set_fsr0:
    rcall   w_read_time
    movf    v_tl,w
    addwf   POSTINC0,f
    movf    v_th,w
    addwfc  POSTDEC0,f
    btfss   STATUS,Z
    return
    movf    INDF0,w
    btfsc   STATUS,Z
    incf    INDF0,f
    return
    
    
;
; check the current time against the time in the resigsters pointer to by FSR0.
; (low value first).  If the time has expired then N will be set.  If the
; time is not expired then N will be clear.
; Also, if time has expired then the registers are cleared to 0.  If the
; registers are already 0 then the time is always considered expired.
;
; time units are 244usec
;
w_timer_check_fsr0:
    movf    POSTINC0,w
    iorwf   POSTDEC0,w
    bz      w_timer_check_fsr0_expired
    rcall   w_read_time
    movf    v_tl,w
    subwf   POSTINC0,w
    movf    v_th,w
    subwfb  POSTDEC0,w
    btfss   STATUS,N
    return

w_timer_check_fsr0_expired:
    clrf    POSTINC0
    clrf    POSTDEC0
    bsf     STATUS,N
    return

;   
; when this is called b_qsectmr0_done is cleared.
; after w seconds b_qsectmr0_done gets set.
;
w_qsectmr0_set:
    movwf   v_qsectmr0 
    bcf     b_qsectmr0_done
    return

;
; when this is called b_qsectmr1_done is cleared.
; after w seconds b_qsectmr1_done gets set.
;
w_qsectmr1_set:
    movwf   v_qsectmr1
    bcf     b_qsectmr1_done
    return


;###########################################################################
;################################ MAIN RUN #################################
;###########################################################################

#if DBORG
    org     0x1b00
#endif

; call this once every main loop to do periodic chores

w_main_run:

#if ENAB_BLINKS
    ;
    ; handle blink sequence when b_blink is set
    ;
    btfsc   b_blink
    rcall   w_blink
#endif

#if ENAB_OLD_TAP_HANDLE
    ;
    ; handle taps
    ;
    clrf    v_tap_bits
    btfsc   b_tap
    rcall   w_tap
#endif

    btfss   bk_qsec
    bra     w_mr_not_qsec

    ;
    ; handle quarter second timers
    ;
    dcfsnz  v_qsectmr0,f
    bsf     b_qsectmr0_done
    dcfsnz  v_qsectmr1,f
    bsf     b_qsectmr1_done
w_mr_not_qsec:

    ;
    ; check serial connect/disconnect (unless going to sleep)
    ;
    btfss   b_d2_sleep
    rcall   w_check_serial

    goto    kk_main_run     ; kernel main loop tasks

;###########################################################################
;################################ SERIAL ###################################
;###########################################################################

v_serial_dis_cnt    equ vaddr+0
vaddr+=1

;
; check if serial is connected/disconnected
;
w_check_serial
    btfss   RCSTA,SPEN
    bra     w_check_serial_reconnect

    ;
    ; serial port enabled - check for disconnect
    ;
    btfsc   bki_serial_detect
    bra     w_check_serial_connected    ; connected

    ;
    ; may be disconnected 
    ;
    decfsz  v_serial_dis_cnt,f
    return

    ;
    ; disconnected for 256 loops - disable
    ;

    goto    kk_serial_disable

w_check_serial_connected:
    clrf    v_serial_dis_cnt
    return

;
; check for reconnection
;
w_check_serial_reconnect:
    btfss   bki_serial_detect
    return

    clrf    v_serial_dis_cnt

    ;
    ; serial got reconnected - enable it!
    ;
    goto    kk_serial_enable

;###########################################################################
;################################ MAIN INIT ################################
;###########################################################################

w_main:
w_main_init:

    ;
    ; init threshholds
    ;
    movlw   (-ACC_INIT_AVGX)&0xff
    movwf   v_avgx_neg

    movlw   (-ACC_INIT_AVGY)&0xff
    movwf   v_avgy_neg

#if ENAB_ISR_TAP
    movlw   HIGH (-ACC_INIT_TH_MAGSQ_TAP)
    movwf   v_th_magsqh_tap
#endif

#if !USE_CONST_MAGSQ_TH
    movlw   -ACC_INIT_TH_MAGSQ_Z
    movwf   v_th_magsql_z

    movlw   -ACC_INIT_TH_MAGSQ_S
    movwf   v_th_magsql_s
#endif


    ;
    ; enable this code to erase the eeprom to 0xff
    ;
#define ERASE_EEPROM    0
#if     ERASE_EEPROM
    movlw           0xff-EEPROM_END
    movwf           v_tmp
    EEPROM_BEGIN    EEPROM_END
w_main_erase_eeprom_loop:
    movlw   0xff
    call    kk_eeprom_write
    decfsz  v_tmp,f
    bra     w_main_erase_eeprom_loop
#endif

    ;
    ; init variables
    ;
    clrf    v_bits1
    clrf    v_bits2
    clrf    v_bits3

    ;
    ; init scroll speed
    ;
    movlw   0x07
    movwf   v_scrollspd

#if ENAB_ISR_TAP
    clrf    v_tap_bits
#endif

#if ENAB_BLINKS
    clrf    v_blink_ptr
#endif

    ;
    ; choose wakeup phrase
    ;
    rcall   w_wakeup_phrase

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

w_main_loop:


#define RUN_TEST    0
#if RUN_TEST
    ;bra        w_test1_print       ; print acc data (max/min)
    ;bra        w_test2_text        ; show text (w_draw)
    ;bra        w_test3_sample      ; sample a bunch of data
    bra         w_test4_parse       ; cast spells
    ;bra        w_test5_textspell   ; show text + cast spell (broken)
    ;bra        w_test6_calibrate   ; calibration
    ;bra        w_test7_text2       ; draw text (w_draw2)
    ;bra        w_test8_serial      ; show serial connectivity on leds

#else

#define TEST_PAUSE      0
#if TEST_PAUSE
    bcf     bk_led1
    rcall   w_pause_qsec
    bsf     bk_led1
    rcall   w_pause_qsec
    bcf     bk_led1
    rcall   w_pause_qsec
    bsf     bk_led1
    rcall   w_pause_qsec
    bcf     bk_led1
    rcall   w_pause_qsec
    bsf     bk_led1
#endif

    call    w_draw2

    btfsc   bk_rs_halt
    bra     w_main_stop

    btfsc   b_d2_tap
    rcall   w_main_tap

    btfsc   bk_rs_halt
    bra     w_main_stop

    btfsc   b_d2_sleep
    rcall   w_main_sleep

    btfsc   bk_rs_halt
    bra     w_main_stop

    bra     w_main_loop

#endif

    ;
    ; turn things off before jumping back to kernel
    ;
w_main_stop:
    call    w_ad_stop
    goto    kk_main_loop

;###########################################################################
;################################ TAP ######################################
;###########################################################################

;
; call here when a tap occurred during draw
;
w_main_tap:
    ;
    ; look for the spell
    ;
    call    w_cast_spell

    btfsc   bk_rs_halt
    return

    btfsc   b_spell_abort
    bra     w_mt_end

    ;
    ; start LED scanning
    ;
    rcall   w_ledscan_start

    SOUT1CRL    w_str_check_rythm

    call    w_check_rythm_spell

    SOUT1CRL    w_str_check_motion

    rcall   w_ledscan

#if 1
    btfss   RCSTA,SPEN
    bra     w_main_tap_dump1_skip

    ;
    ; dump accbuf
    ;
    lfsr    FSR1,RAMBUF_ACC
w_main_tap_dump1:
    COUTL   'a'
    movf    POSTINC1,w
    movwf   v_tmp
    call    kk_outbyte
    movf    POSTINC1,w
    iorwf   v_tmp,f
    call    kk_outbyte
    movf    POSTINC1,w
    iorwf   v_tmp,f
    call    kk_outbyte
    movf    POSTINC1,w
    iorwf   v_tmp,f
    call    kk_outbyte
    movf    POSTINC1,w
    iorwf   v_tmp,f
    call    kk_outbyte
    movf    POSTINC1,w
    iorwf   v_tmp,f
    call    kk_outbyte
    movf    POSTINC1,w
    iorwf   v_tmp,f
    call    kk_outbyte
    movf    POSTINC1,w
    iorwf   v_tmp,f
    call    kk_outbyte
    call    kk_outcr
    DB_FLUSH

    movf    v_tmp,w
    bnz     w_main_tap_dump1

    rcall   w_ledscan
w_main_tap_dump1_skip:
#endif

#if 0
    btfsc   b_got_spell
    bra     w_got_spell
#endif

    call    w_check_motion_spell


    SOUT1CRL    w_str_check_done

    btfsc   b_got_spell
    bra     w_got_spell
    

#if 0
    ;
    ; dump motion tokens
    ;
    lfsr    FSR1,RAMBUF_MTOKENS
    bra     w_main_tap_dump2_start
w_main_tap_dump2:
    COUTL   'm'
    movf    POSTINC1,w
    call    kk_outbyte
    call    kk_outcr
    DB_FLUSH

w_main_tap_dump2_start:
    movf    INDF1,w
    addlw   -MTOKEN_END
    bnz     w_main_tap_dump2
#endif

w_mt_end:
    ;
    ; turn LEDs off
    ;
    movlw   KERNVAL_TRISB_LED_MASK
    iorwf   TRISB,f

    ;
    ; Unrecognized spell
    ;
    bra     w_unrecognized_spell

;###########################################################################
;################################ SCAN_LEDS ################################
;###########################################################################

v_ledscan       equ vaddr+0
vaddr+=1


;
; start scanning led back and forth (starts with LED3)
;
w_ledscan_start:
    clrf    v_ledscan

;
; scan led to next location
;
w_ledscan:
    incf    v_ledscan,f
    movf    v_ledscan,w

    setf    v_tmp
    bcf     v_tmp,3

    btfsc   v_ledscan,2
    negf    WREG
    andlw   7
    bz      w_ledscan_disp
w_ledscan_loop:
    rlncf   v_tmp,f
    decfsz  WREG,w
    bra     w_ledscan_loop

w_ledscan_disp:
    movlw   KERNVAL_TRISB_LED_MASK
    iorwf   TRISB,f
    movf    v_tmp,w
    iorlw   ~KERNVAL_TRISB_LED_MASK
    andwf   TRISB,f
    return

;###########################################################################
;################################ SLEEP ####################################
;###########################################################################


;
; call here when it is time to sleep
;
w_main_sleep:
    SOUT1CRL    w_str_sleep
    COUTL   ' '
    COUTL   ' '
    DB_FLUSH


v_save_portc        equ vaddr+0 ; value of PORTC when sleeping
vaddr+=1

    movff   PORTC,v_save_portc

    ;
    ; prepare to sleep
    ;
    call    w_ad_stop
    call    kk_serial_disable
    bcf     bki_rx_new
    bcf     b_rcif
    bcf     b_rcif

    ;
    ; slow down the clock
    ;
    call    kk_set_osc_32khz

    ;
    ; get reference value for accelerometer
    ;
    rcall   w_pause_qsec
    rcall   w_sleep_check_motion
    movff   v_accx,v_avgx_neg
    movff   v_accy,v_avgy_neg
    negf    v_avgx_neg
    negf    v_avgy_neg

    ;
    ; sleep loop
    ;
w_sleep_loop2:
    call    kk_main_run     ; kernel main loop tasks
    btfsc   bk_rs_halt      ; pointless, but we check anyway for consistancy
    bra     w_sleep_wake

    ;
    ; wait for 100ms
    ;
SLEEP_MOTION_DT     equ     0x199       ; 0x199 * 244us = 100ms
    lfsr    FSR0,v_t0l
    movlw   HIGH SLEEP_MOTION_DT
    movwf   v_t0h
    movlw   LOW SLEEP_MOTION_DT
    movwf   v_t0l
    call    w_timer_set_fsr0

w_sleep_loop1:
    ;
    ; wake if serial port toggled
    ;
    movf    PORTC,w
    xorwf   v_save_portc,w
    btfsc   WREG,RX
    bra     w_sleep_wake

    ;
    ; time to check for motion?
    ;
    lfsr    FSR0,v_t0l
    call    w_timer_check_fsr0
    bnn     w_sleep_loop1

    ;
    ; check for motion
    ;
    rcall   w_sleep_check_motion

    ;
    ; sleep until b_d2_sleep is cleared by w_sleep_check_motion
    ;
    btfsc   b_d2_sleep
    bra     w_sleep_loop2


;===========================================================================
; wake up
;===========================================================================
w_sleep_wake:

    call    kk_set_osc_8mhz

    btfsc   bki_serial_detect
    call    kk_serial_enable

    bcf     b_d2_sleep
    
    WCLR
    WSOUT1  w_str_sleepy

    movlw   DRAW2_SLEEP_WAIT
    movwf   v_sleep_wait

    SOUT1CRL    w_str_wake

    return

;===========================================================================
; check for motion
;===========================================================================
; (clear b_d2_sleep if motion occurrs)
;
w_sleep_check_motion:

    call    w_ad_init

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

    movlw   SLEEP_ADCON2
    movwf   ADCON2

    bsf     b_adgo
w_sleep_check_motion1:
    btfss   b_adgo
    bra     w_sleep_check_motion1

    ;
    ; read X acceleration
    ;
    movlw   ADCON0_ACCY
    movwf   ADCON0

    movf    ADRESH,w

    movwf   v_accx          ; save off accx value
    addwf   v_avgx_neg,w
    btfsc   WREG,7
    negf    WREG
    mulwf   WREG
    movff   PRODL,v_magsql
    movff   PRODH,v_magsqh

    bsf     b_adgo

w_sleep_check_motion2:
    btfss   b_adgo
    bra     w_sleep_check_motion2

    ;
    ; read Y acceleration
    ;
    movf    ADRESH,w
    movwf   v_accy          ; save off accy value
    addwf   v_avgy_neg,w
    btfsc   WREG,7
    negf    WREG
    mulwf   WREG

    call    w_ad_stop

    movf    PRODL,w
    addwf   v_magsql,f
    movf    PRODH,w
    addwfc  v_magsqh,f

    movlw   LOW (-ACC_TH_MAGSQ_WAKE)
    addwf   v_magsql,w
    movlw   HIGH (-ACC_TH_MAGSQ_WAKE)
    addwfc  v_magsqh,w

    btfsc   STATUS,C
    bcf     b_d2_sleep

    return


;###########################################################################
;################################ GOT SPELL ################################
;###########################################################################

w_spellinfo_default     equ w_str_unrecognized

#if DBG_SPELL_MSG_IS_SPELL_NAME
;
; spell info - for now just strings
;
;w_spellinfo_shave      equ w_str_shave
w_spellinfo_row         equ w_str_row
;w_spellinfo_anthem     equ w_str_anthem
;w_spellinfo_twinkle        equ w_str_twinkle
w_spellinfo_jingle      equ w_str_jingle
w_spellinfo_mary        equ w_str_mary
w_spellinfo_brady       equ w_str_brady
w_spellinfo_gilligan    equ w_str_gilligan
w_spellinfo_flint       equ w_str_flint
w_spellinfo_birthday    equ w_str_birthday
#endif

w_spellinfo_jeff1       equ w_str_jeff1
w_spellinfo_quarters    equ w_str_quarters
w_spellinfo_star        equ w_str_star

;
; SPELLINFO STRUCTURE
;
; if low bit of spellinfo is set then it points to following structure:
;
; byte   description
; ----   -----------
;    0   hour      hour   that this message expires
;    1   minute
;         bit 0-5: minute that this message expires
;         bit 6:   contains yes/no question (if set)
;         bit 7:   last spellinfo for this spell (if set)
;    2   string high addr
;    3   string low  addr
;
; byte 4-7 only if byte1 bit6 is set
;
;    4   yes spellinfo (h)
;    5   yes spellinfo (l)
;    6   no  spellinfo (h)
;    7   no  spellinfo (l)
;
SPELLINFO_SIZE_YN   equ 8   ; size of each spellinfo structure with yes/no
SPELLINFO_SIZE_0    equ 4   ; size of each spellinfo structure w/o yes/no

#if 0
w_spellinfo_shave1:
w_spellinfo_shave   equ w_spellinfo_shave1+1
    db  0x00, 0x01, HIGH w_str_shavea, LOW w_str_shavea
    db  0x09, 0x01, HIGH w_str_shaveb, LOW w_str_shaveb
    db  0xff, 0xbf, HIGH w_str_shavec, LOW w_str_shavec


w_spellinfo_twinkle1:
w_spellinfo_twinkle equ w_spellinfo_twinkle1+1
    db  0xff, 0xff, HIGH w_str_twinkle, LOW w_str_twinkle
    db  HIGH w_str_star_yes, LOW w_str_star_yes 
    db  HIGH w_str_star_no,  LOW w_str_star_no 
#endif

;
; got a spell
;
w_got_spell:

    ;
    ; turn LEDs off
    ;
    movlw   KERNVAL_TRISB_LED_MASK
    iorwf   TRISB,f


#if DBPRINT
    btfss   RCSTA,SPEN
    bra     w_got_spell_skipprint

    SOUT1L  w_str_spell
    movf    v_spell_ptrh,w
    call    kk_outbyte
    movf    v_spell_ptrl,w
    call    kk_outbyte
    COUTL   ' '
    COUTL   'T'
    COUTL   '='
    movf    vk_hour,w
    call    kk_outbyte
    COUTL   ':'
    movf    vk_minute,w
    call    kk_outbyte
    COUTL   ':'
    rrncf   vk_qsec,w
    rrncf   WREG,w
    andlw   0x3f
    call    kk_outbyte
    call    kk_outcr
    DB_FLUSH
w_got_spell_skipprint:
#endif

    ;
    ; assume it is not a yes/no spell
    ;
    bcf     b_spell_yes_no

    ;
    ; check for faster
    ;
    movf    v_spell_ptrl,w
    addlw   -(LOW w_spellinfo_fast)
    bnz     v_gs_notfast
    movf    v_spell_ptrh,w
    addlw   -(HIGH w_spellinfo_fast)
    bnz     v_gs_notfast
    call    w_scroll_faster
v_gs_notfast:

    ;
    ; check for slower
    ;
    movf    v_spell_ptrl,w
    addlw   -(LOW w_spellinfo_slow)
    bnz     v_gs_notslow
    movf    v_spell_ptrh,w
    addlw   -(HIGH w_spellinfo_slow)
    bnz     v_gs_notslow
    call    w_scroll_slower
v_gs_notslow:

    ;
    ; check for twinkle (time)
    ;
    movf    v_spell_ptrl,w
    addlw   -(LOW w_spellinfo_twinkle)
    bnz     v_gs_nottime
    movf    v_spell_ptrh,w
    addlw   -(HIGH w_spellinfo_twinkle)
    bnz     v_gs_nottime
    bsf     b_show_time
v_gs_nottime:

    ;
    ; is this a spellinfo structure?
    ;
    btfss   v_spell_ptrl,0
    bra     w_gs_string ; no - just a string
    
    ;
    ; yes - spellinfo structure
    ;
    movf    v_spell_ptrl,w
    andlw   0xfe
    movwf   TBLPTRL
    movf    v_spell_ptrh,w
    movwf   TBLPTRH


    ;
    ; read hour and minute
    ;
w_gs_loop:
    tblrd*+
    movf    TABLAT,w
    tblrd*+
    
    ;
    ; are we before specified time?
    ;
    subwf   vk_hour,w
    bnc     w_gs_found  ; yes - use this one
    bnz     w_gs_next   ; no  - try next spellinfo (if any)

    ;
    ; same hour - check the minute
    ;
    movf    TABLAT,w
    andlw   0x3f        ; ignore 2 high bits
    bsf     STATUS,C
    subfwb  vk_minute,w
    bc      w_gs_found  ; yes - use this one

;
; did not match - try next spellinfo
; At this point:
;    TABLAT contains the minute
;    TBLPTR points just after the minute
;
w_gs_next:
    btfsc   TABLAT,7
    bra     w_disabled_spell
    movlw   SPELLINFO_SIZE_0-2
    btfsc   TABLAT,6
    movlw   SPELLINFO_SIZE_YN-2
    addwf   TBLPTRL,f
    movlw   0
    addwfc  TBLPTRH,f
    bra     w_gs_loop

;
; matched time - use this spellinfo
; At this point:
;    TABLAT contains the minute
;    TBLPTR points just after the minute
;
w_gs_found:
    movf    TABLAT,w            ; byte 2
    tblrd*+
    movff   TABLAT,v_spell_ptrh
    tblrd*+
    movff   TABLAT,v_spell_ptrl

    ;
    ; have yes/no?
    ;
    btfss   WREG,6
    bra     w_got_spell ; no - re-parse spell pointer

    ;
    ; yes/no spell - save yes/no values
    ;
    tblrd*+
    movff   TABLAT,v_spell_yesh
    tblrd*+
    movff   TABLAT,v_spell_yesl
    tblrd*+
    movff   TABLAT,v_spell_noh
    tblrd*+
    movff   TABLAT,v_spell_nol

    bsf     b_spell_yes_no

    ;
    ; loop in case this is in turn a spellinfo structure
    ;
    bra     w_got_spell

;
; v_spell_ptrh,l now contains a string pointer
;
w_gs_string:
    WCLR

    movf    v_spell_ptrl,w
    movwf   v_strl
    movf    v_spell_ptrh,w
    rcall   w_wsout_wh

#if DBPRINT
    movf    v_spell_ptrl,w
    movwf   TBLPTRL
    movf    v_spell_ptrh,w
    call    w_sout_wh
    call    kk_outcr
#endif

    btfss   b_show_time
    return

    bcf     b_show_time

    swapf   vk_hour,w
    call    w_woutnib
    movf    vk_hour,w
    call    w_woutnib

    movlw   ':'
    call    w_wcout

    swapf   vk_minute,w
    call    w_woutnib
    movf    vk_minute,w
    call    w_woutnib

    movlw   ':'
    call    w_wcout

    swapf   vk_qsec,w
    call    w_woutnib
    movf    vk_qsec,w
    call    w_woutnib

    return


;###########################################################################
;################################ UNRECOGNIZED SPELL #######################
;###########################################################################
;
; Unrecognized or disabled spell
;
w_disabled_spell:
w_unrecognized_spell:
    WCLR
    WSOUT1  w_str_unrecognized

#if 1
    bcf     b_d2_sleep      ; make extra sure we do not sleep

    SOUT1CRL    w_str_unrecognized
    call    kk_outcr

#else

    bcf     b_d2_sleep      ; make extra sure we do not sleep

    ;
    ; print spell that was cast
    ;
    COUTL   'M'
    COUTL   'o'
    COUTL   't'
    COUTL   'i'
    ;COUTL  'o'
    ;COUTL  'n'
    ;COUTL  '='

    call    kk_outcr
    call    kk_outcr
    call    kk_outcr

    DB_FLUSH
#endif

    return
    
;###########################################################################
;################################ CHOOSE PHRASES ###########################
;###########################################################################

;
; w_wakeup_phrase  - choose random wakeup phrase
w_wakeup_phrase:
    WCLR
    WSOUT1  w_str_hello
    return
    

;###########################################################################
;################################ SAMPLE TEST ##############################
;###########################################################################

#if ENAB_TESTS

#if DBORG
    org     0x1f00
#endif

w_tst3_stopwait:

w_tst3_stop:
    call    w_ad_stop
    goto    kk_main_loop

w_test3_sample:
    COUTL   'A'
    call    w_ad_stop
    COUTL   'B'


    ;
    ; wait for buffer to empty
    ;
w_tst3_mt_loop:
    rcall   w_main_run
    btfsc   bk_rs_halt
    bra     w_tst3_stop

    movf    vk_tx_cnt,w
    bnz     w_tst3_mt_loop

    ;
    ; turn on 9 bit xfers
    ;
    ;bsf        TXSTA,TX9D
    ;bsf        TXSTA,TX9


    COUTL   'C'
    
    call    w_sample_clear
    call    w_ad_start
    call    w_sample_start


w_tst3_loop:
    rcall   w_main_run
    btfsc   bk_rs_halt
    bra     w_tst3_stop

    ;
    ; indicate fullness with LEDs
    ;
    movlw   KERNVAL_TRISB_LED_MASK
    iorwf   TRISB,f
    comf    FSR2L,w
    iorlw   (~KERNVAL_TRISB_LED_MASK)&0xff
    andwf   TRISB,f
    

    btfsc   b_accbuf_sample     ; loop until full
    bra     w_tst3_loop




    ;
    ; here the buffer has been filled
    ;
    call    w_ad_stop

    ;
    ; turn LEDs off
    ;
    movlw   KERNVAL_TRISB_LED_MASK
    iorwf   TRISB,f

    bra     w_tst3_aaaa
w_tst3_str:
    db  "data is 0x400-0x800: tt tt xx yy\0"

w_tst3_aaaa:

    SOUTL   w_tst3_str
    call    kk_outcr

    

w_tst3_end:
    call    w_ad_stop
    call    kk_outcr
    COUTL   'e'
    COUTL   'n'
    COUTL   'd'
    call    kk_outcr



w_tst3_end_loop:
    rcall   w_main_run
    btfsc   bk_rs_halt
    bra     w_tst3_stop

    bra     w_tst3_end_loop


;###########################################################################
;################################ PRINT ACC TEST ###########################
;###########################################################################

#if DBORG
    org     0x2000
#endif

;================================================

v_tmra  equ vaddr+0
vaddr+=1


w_tst1_stop:
    call    w_ad_stop
    nop
    nop
    goto    kk_main_loop

w_test1_print:

    call    w_sample_clear
    call    w_ad_start
    call    w_sample_start
    movlw   5
    movwf   v_tmra

w_tst1_loop:
    rcall   w_main_run
    btfsc   bk_rs_halt
    bra     w_tst1_stop

    nop

#if 0
#if ENAB_BLINKS
    ;
    ; test blinking
    ;

v_blink_dbg     equ vaddr+0
vaddr+=1


    ;
    ; blink leds every 1/4 sec
    ;
    ;movf   v_blink_ptr,w
    ;bnz        w_tst1_print

    btfss   bk_qsec
    bra     w_tst1_print

    incf    v_blink_dbg,f
    movf    v_blink_dbg,w
    addlw   -10
    bnc     w_tst1_print

    clrf    v_blink_dbg


    ;START_BLINK    w_blinkpat_down
    ;START_BLINK    w_blinkpat_up
    ;START_BLINK    w_blinkpat_right
    START_BLINK     w_blinkpat_left
    
#endif
#endif



w_tst1_print:

#if 0
    btfss   bk_qsec
    bra     w_tst1_loop
#else
    nop
    nop
#endif

    movf    vk_tx_cnt,w
    bnz     w_tst1_loop


#if 0
    decfsz  v_tmra,f
    bra     w_tst1_print2

    movlw   50
    movwf   v_tmra

#if 0
#if ENAB_BLINKS
    START_BLINK     w_blinkpat_countdown
#endif
#else
    btg     bk_led2
    btg     bk_led1
    ;nop
#endif

w_tst1_print2:
#endif

    SOUT1L  w_str_sec

    movf    vk_qsec,w
    rrncf   WREG,w
    rrncf   WREG,w
    andlw   0x3f

    call    kk_outbyte

v_pnt_x     equ vaddr+0
v_pnt_y     equ vaddr+1
v_pnt_m     equ vaddr+2
v_pnt_a     equ vaddr+3
v_pnt_ml    equ vaddr+6
v_pnt_mh    equ vaddr+7
v_pnt_h1a   equ vaddr+8
v_pnt_h1b   equ vaddr+9
v_pnt_h1c   equ vaddr+10
v_pnt_h1d   equ vaddr+11
vaddr+=12


    movff   v_accx,v_pnt_x
    movff   v_accy,v_pnt_y
    movff   v_mag,v_pnt_m
    movff   v_angle,v_pnt_a
#if HACK1
    movff   v_hack1_magsql,v_pnt_ml
    movff   v_hack1_magsqh,v_pnt_mh
    movff   v_hack1_mag,v_pnt_m
    movff   v_hack1_angle,v_pnt_a
    movff   v_smpln_angle,v_pnt_h1a
    movff   v_smpln_cntl,v_pnt_h1b
    movff   v_smpln_imph,v_pnt_h1c
    movff   v_smpln_impl,v_pnt_h1d
#endif

    COUTL   ' '
    COUTL   ' '
    COUTL   ' '
    movf    v_accx_min,w
    call    kk_outbyte
    COUTL   ' '
    movf    v_pnt_x,w
    call    kk_outbyte
    COUTL   ' '
    movf    v_accx_max,w
    call    kk_outbyte

    COUTL   ' '
    COUTL   ' '
    COUTL   ' '
    movf    v_accy_min,w
    call    kk_outbyte
    COUTL   ' '
    movf    v_pnt_y,w
    call    kk_outbyte
    COUTL   ' '
    movf    v_accy_max,w
    call    kk_outbyte

    COUTL   ' '
    COUTL   ' '
    COUTL   'a'
    movf    v_pnt_a,w
    call    kk_outbyte
    COUTL   ' '
    COUTL   'm'
    movf    v_pnt_m,w
    call    kk_outbyte

#if HACK1
    COUTL   ' '
    movf    v_pnt_mh,w
    call    kk_outbyte
    movf    v_pnt_ml,w
    call    kk_outbyte

    COUTL   ' '
    movf    v_pnt_h1a,w
    call    kk_outbyte
    COUTL   ' '
    movf    v_pnt_h1b,w
    call    kk_outbyte
    COUTL   ' '
    movf    v_pnt_h1c,w
    call    kk_outbyte
    COUTL   ' '
    movf    v_pnt_h1d,w
    call    kk_outbyte
#endif

#if ENAB_ISR_TAP
    COUTL   ' '
    COUTL   ' '
    movf    v_tap_mask_acnth,w
    call    kk_outbyte
#endif

    COUTL   ' '
    COUTL   ' '
    COUTL   ' '
    movf    FSR2H,w
    call    kk_outbyte
    movf    FSR2L,w
    call    kk_outbyte


#if 0
    COUTL   ' '
    COUTL   'b'
    movf    LATB,w
    call    kk_outbyte
    movf    TRISB,w
    call    kk_outbyte
#endif


    call    kk_outcr
    
    clrf    v_accx_max
    clrf    v_accy_max
    setf    v_accx_min
    setf    v_accy_min


w_tst1_noprint:
    bra     w_tst1_loop

;###########################################################################
;################################ PARSE TEST ###############################
;###########################################################################

#if ENAB_ISR_TAP
#if DBORG
    org     0x2200
#endif

w_tst4_stop:
    call    w_ad_stop
    goto    kk_main_loop

w_test4_parse:

    call    w_sample_clear
    call    w_ad_start
#if ENAB_ISR_TAP
    call    w_tap_enable
#endif

w_tst4_loop1:
    rcall   w_main_run
    btfsc   bk_rs_halt
    bra     w_tst4_stop


    btfss   b_tap_down
    bra     w_tst4_loop1    ; down tap begins wait-for-still


    ;
    ; look for the spell
    ;
    rcall   w_cast_spell

    btfsc   bk_rs_halt
    bra     w_tst4_stop

    ;
    ; print spell that was cast
    ;
    COUTL   'M'
    COUTL   'o'
    COUTL   't'
    COUTL   'i'
    COUTL   'o'
    COUTL   'n'
    COUTL   '='

    call    kk_outcr
    rcall   w_check_rythm_spell
    
#if ENAB_W_PARSE
    lfsr    FSR1,RAMBUF_MOTION
w_tst4_loop4:
    movf    POSTINC1,w
    bn      w_tst4_restart
    call    kk_outbyte
    COUTL   ' '
    bra     w_tst4_loop4
#endif

w_tst4_restart:
    call    kk_outcr
    call    w_ad_start
    call    w_tap_enable
    bra     w_tst4_loop1

#endif

;###########################################################################
;################################ CALIBRATION TEST #########################
;###########################################################################

#if DBORG
    org     0x2400
#endif

v_tst6_cnt      equ vaddr+0
vaddr+=1

w_tst6_stop:
    call    w_ad_stop
    goto    kk_main_loop

w_test6_calibrate:
    call    w_sample_clear
    call    w_ad_init
    lfsr    FSR2, RAMBUF_ACC

    ;
    ; setup steady state - all LEDS off
    ;
    movlw   KERNVAL_TRISB_LED_MASK
    iorwf   TRISB,f
    
    movlw   (~KERNVAL_TRISB_LED_MASK)&0xff
    andwf   TRISB,f

#if 0
    ;
    ; turn off accelerometer
    ;
    bsf     b_acc_disable
#endif
#if 0
    ;
    ; turn off A/D
    ;
    clrf    ADCON0
#endif



    ;
    ; wait a while (about 3 sec)
    ;
    movlw   10
    movwf   v_tst6_cnt
w_tst6_w1:
    call    w_main_run
    btfsc   bk_rs_halt
    bra     w_tst6_stop
    
    btfss   bk_qsec
    bra     w_tst6_w1


    decfsz  v_tst6_cnt,f
    bra     w_tst6_w1

#if 0
    ;
    ; turn on accelerometer
    ;
    bcf     b_acc_disable
#endif

#if 0
    ;
    ; set up to sample x axis
    ;
    movlw   ADCON0_ACCX
    movwf   ADCON0

    nop
    nop
    nop
#endif

    ;
    ; start sampling
    ;
    bsf     b_adgo          ; start first conversion


w_tst6_loop:
    rcall   w_tst6_sample

    movlw   KERNVAL_TRISB_LED_MASK
    btfsc   FSR2L,4
    iorwf   TRISB,f     ; turn LED on after 4 samples
    
    btfsc   FSR2H,2     ; loop for 256 samples
    bra     w_tst6_loop


    ;
    ; print result
    ;
    call    w_ad_stop

    movlw   0
    movwf   v_tst6_cnt
    lfsr    FSR2, RAMBUF_ACC

w_tst6_r1:
    movf    FSR2H,w
    call    kk_outbyte
    movf    FSR2L,w
    call    kk_outbyte

    COUTL   ':'
    movf    POSTINC2,w
    call    kk_outbyte
    movf    POSTINC2,w
    call    kk_outbyte
    COUTL   ':'

    movf    POSTINC2,w
    call    kk_outbyte
    movf    POSTINC2,w
    call    kk_outbyte
    call    kk_outcr

    DB_FLUSH

    decfsz  v_tst6_cnt,f
    bra     w_tst6_r1

    ;
    ; LEDS off
    ;
    movlw   KERNVAL_TRISB_LED_MASK
    iorwf   TRISB,f

    ;
    ; wait for  halt
    ;
w_tst6_endwait:
    call    w_main_run
    btfsc   bk_rs_halt
    bra     w_tst6_stop
    bra     w_tst6_endwait



;
; sample subroutine
;
w_tst6_sample:
    btfss   b_adif
    return

    bcf     b_adif

    movf    ADRESH,w
    movwf   POSTINC2
    movf    ADRESL,w
    movwf   POSTINC2

    bsf     b_adgo          ; start next conversion

    movf    TMR1H,w
    movwf   POSTINC2
    movf    TMR1L,w
    movwf   POSTINC2

    return

;###########################################################################
;################################ TEXT TEST 2 ##############################
;###########################################################################

w_tst7_stop:
    call    w_ad_stop
    goto    kk_main_loop

w_test7_text2:
    WCLRL
    WSOUT1L w_str_quick



w_tst7_loop:
    call    w_draw2

    call    w_main_run
    btfsc   bk_rs_halt
    bra     w_tst7_stop

    btfsc   b_d2_tap
    bra     w_tst7_loop


    ; handle tap here


    bcf     b_d2_tap
    bra     w_tst7_loop


;###########################################################################
;################################ SERIAL TEST 8 ############################
;###########################################################################

w_tst8_stop:
    goto    kk_main_loop

w_test8_serial:

w_tst8_loop:
    call    w_main_run
    btfsc   bk_rs_halt
    bra     w_tst8_stop

    bsf     bk_led4
    btfss   bki_serial_detect
    bcf     bk_led4
    
    bra     w_tst8_loop


#endif ; ENAB_TESTS

;###########################################################################
;################################ CAST SPELL ###############################
;###########################################################################

#if DBORG
    org     0x2700
#endif


;
; wait for still, then cast spell
; accelerometer & A/D are OFF on return
;
w_cast_spell:
    bcf     b_spell_abort

    rcall   w_still_wait

#if ENAB_ISR_TAP
    ;
    ; disable taps
    ;
    bcf     b_tap_enable
    bcf     b_tap
#endif

    btfsc   b_spell_abort
    bra     w_cs_end

    ;
    ; led0 is on while casting spell
    ;
    bsf     bk_led0
    bcf     bk_led4
    call    w_pause_ad_settle

    ;
    ; begin sampling & parsing
    ;
    bcf     b_accbuf_sample
    call    w_sample_clear
    call    w_sample_start

#if ENAB_W_PARSE
    rcall   w_parse_init
#endif

#if ENAB_ISR_TAP
    ;
    ; abort if tap occured (might occur in w_still_wait)
    ;
    movf    v_tap_bits,f
    bnz     w_cs_done
#endif

    ;
    ; set timeout timer for stillness
    ;
SPELL_STILL_TIME_INIT   equ 15*4    ; 15 sec
SPELL_STILL_TIME        equ 6       ; 1.5 sec
    movlw   SPELL_STILL_TIME_INIT
    call    w_qsectmr0_set
    bcf     b_acc_motion

w_cs_loop:
    call    w_main_run
    btfsc   bk_rs_halt
    bra     w_cs_end

#if ENAB_W_PARSE
    rcall   w_parse
#endif


#if ENAB_W_PARSE
    ;
    ; exit loop when wand is wagging left-right
    ;
    btfsc   b_parse_wag
    bra     w_cs_done
#endif

#if 0 && DBPRINT
v_hack_fsr2h    equ vaddr+0
v_hack_fsr2l    equ vaddr+1
v_hack_tmp      equ vaddr+3
vaddr+=3

    movf    vk_tx_cnt,w
    bnz     w_cs_skipprint

    movf    FSR2H,w
    subwf   v_hack_fsr2h,w
    movwf   v_hack_tmp
    movf    FSR2L,w
    subwf   v_hack_fsr2l,w
    iorwf   v_hack_tmp,w
    bz      w_cs_skipprint

    movf    FSR2L,w
    movwf   v_hack_fsr2l
    movf    FSR2H,w
    movwf   v_hack_fsr2h
    call    kk_outbyte
    movf    v_hack_fsr2l,w
    call    kk_outbyte
    call    kk_outcr
w_cs_skipprint:
#endif

    ;
    ; check for stillness
    ;
    btfss   b_acc_motion
    bra     w_cs_check_still

    movlw   SPELL_STILL_TIME
    call    w_qsectmr0_set
    bcf     b_acc_motion
    bra     w_cs_not_still

w_cs_check_still:
    btfsc   b_qsectmr0_done
    bsf     b_acc_finish            ; tell isr to stop sampling
    
w_cs_not_still:

    ;
    ; exit loop when accbuf is full or done sampling
    ; TODO: wrap accbuf
    ;
    btfsc   b_accbuf_sample
    bra     w_cs_loop

    ;
    ; finish parsing what we got
    ;
w_cs_done:

    ;
    ; Turn off A/D and accelerometer
    ; flush last motions to buffer
    ;
    call    w_ad_sample_finish

    bcf     b_accbuf_sample
#if SHOW_LED_DURING_MOTION
    bsf     bk_led2
#endif
    bsf     bk_led4


#if ENAB_W_PARSE
    rcall   w_parse_finish

    ;
    ; remove last 8 entries and mark end with 0xff
    ;
    lfsr    FSR0,RAMBUF_MOTION
    movf    v_spell_len,w
    addlw   -8
    btfss   STATUS,C    ; less than 8 entries?
    clrf    WREG        ; yes - empty spell
    movwf   v_spell_len
    movwf   FSR0L
    setf    INDF0
#endif

w_cs_end:
    movlw   KERNVAL_TRISB_LED_MASK
    iorwf   TRISB,f
    call    w_ad_stop
    return


;###########################################################################
;################################ STILL WAIT ###############################
;###########################################################################

#define USE_NEW_STILLWAIT   1
#if USE_NEW_STILLWAIT
    ;
    ; NEW STILLNESS TEST - (seems to work fairly well)
    ;
;
; Wait for wand to be still
;
;  when done:
;    v_avgx_neg has been calculated
;    v_avgy_neg has been calculated
;    wand has been still for 1.5 sec
;    wand interrupt is enabled
;    wand sampling is disabled
;    led 5 is on 
;
;  OR:
;    wand has NOT been still for many seconds
;    wand interrupt is enabled
;    wand sampling is disabled
;    v_avgx_neg unchanged
;    v_avgy_neg unchanged
;    led 5 is on 
;
;  OR:
;    bk_rs_halt is set
;    wand interrupt is enabled
;    wand sampling is disabled
;    v_avgx_neg unchanged
;    v_avgy_neg unchanged
;    led 5 is on 
;
w_still_wait:
    call    w_ad_start

v_sw_leds   equ vaddr+0 ; current led values
vaddr+=1

;
; SHORT_TIMEOUT is time to quit unless getting close to still
; LONG_TIMEOUT  is max time before giving up
;   time is in quarter second increments
;
STILLWAIT_SHORT_TIMEOUT equ 4*4     ;  4 sec
STILLWAIT_LONG_TIMEOUT  equ 15*4    ; 15 sec

    movlw    STILLWAIT_LONG_TIMEOUT
    call    w_qsectmr0_set
    movlw    STILLWAIT_SHORT_TIMEOUT
    call    w_qsectmr1_set


w_sw_err3:
    movlw   0x07
    movwf   v_sw_leds
    bra     w_sw_set_leds

w_sw_err2:
    movlw   0x0f
    andwf   v_sw_leds,f
    bra     w_sw_set_leds

w_sw_err1:
    movf    STILLWAIT_SHORT_TIMEOUT,w
    call    w_qsectmr1_set
    bcf     v_sw_leds,5
    bcf     v_sw_leds,6

    ;
    ; set new led values
    ;
w_sw_set_leds:
    movf    v_sw_leds,w
    andlw   KERNVAL_TRISB_LED_MASK
    iorwf   TRISB,f
    iorlw   ~KERNVAL_TRISB_LED_MASK
    andwf   TRISB,f

    ;
    ; wait while leds settle
    ;
    call    w_pause_ad_settle
    call    w_pause_ad_settle
    call    w_pause_ad_settle
    call    w_pause_ad_settle

    ;
    ; set timer
    ;
STILLWAIT_DT    equ 0x0801  ; time per led = 244us * ?


    lfsr    FSR0,v_t0l
    movlw   HIGH STILLWAIT_DT
    movwf   v_t0h
    movlw   LOW STILLWAIT_DT
    movwf   v_t0l

#if 1
    ;
    ; if high leds are set then allow them to go off quickly
    ;
    btfss   v_sw_leds,5
    clrf    v_t0h
#endif

    call    w_timer_set_fsr0

    ;
    ; reset min/max
    ;
    setf    v_accx_min
    setf    v_accy_min
    clrf    v_accx_max
    clrf    v_accy_max

    ;
    ; check for timeout
    ;
    btfsc   b_qsectmr0_done
    bra     w_sw_abort
    btfsc   b_qsectmr1_done
    bra     w_sw_abort


w_sw_loop1:
    call    w_main_run
    btfsc   bk_rs_halt
    bra     w_sw_abort

    movf    v_accy_max,w
    bz      w_sw_loop1
    movf    v_accx_max,w
    bz      w_sw_loop1

    ;
    ; calc (ABS(maxx - miny) + ABS(maxy - minx))
    ; Trying to get it close to 0
    ;
    movf    v_accy_min,w
    subwf   v_accx_max,w
    btfss   STATUS,C
    negf    WREG
    movwf   v_tmp

    movf    v_accx_min,w
    subwf   v_accy_max,w
    btfss   STATUS,C
    negf    WREG
    addwf   v_tmp,w

    ;
    ; how big is the error?
    ;
    addlw   -7
    bc      w_sw_err3   ; err >= 7
    infsnz  WREG,w
    bra     w_sw_err2   ; err == 6
    infsnz  WREG,w
    bra     w_sw_err2   ; err == 5
    infsnz  WREG,w
    bra     w_sw_err1   ; err == 4
    infsnz  WREG,w
    bra     w_sw_err1   ; err == 3

    ;
    ; check time
    ;
    lfsr    FSR0,v_t0l
    call    w_timer_check_fsr0
    bnn     w_sw_loop1

    ;
    ; timer expired - turn 1 led off
    ;
    bsf     STATUS,C
    rlcf    v_sw_leds,f
    incfsz  v_sw_leds,w     ; done if all set
    bra     w_sw_set_leds

    ;
    ; still - calc average accx and accy values
    ;
    rcall   w_calc_avg

    ;
    ; turn on led0 0 only
    ;
    bsf     bk_led0
    bcf     bk_led4

    ;
    ; done
    ;
    return

w_sw_abort:
    movlw   KERNVAL_TRISB_LED_MASK
    iorwf   TRISB,f

    bsf     b_spell_abort
    return

#else
    ;
    ; OLD STILLNESS TEST - WORKING & TESTED
    ;
;
; Wait for wand to be still
;
;  when done:
;    v_avgx_neg has been calculated
;    v_avgy_neg has been calculated
;    wand has been still for 1.5 sec
;    wand interrupt is enabled
;    wand sampling is disabled
;    led 5 is on 
;
#if ENAB_ISR_TAP
;  OR:
;    v_tap_bits is nonzero
;    wand has NOT been still
;    wand interrupt is enabled
;    wand sampling is disabled
;    v_avgx_neg unchanged
;    v_avgy_neg unchanged
;    led 5 is on 
#endif
;
;  OR:
;    bk_rs_halt is set
;    wand interrupt is enabled
;    wand sampling is disabled
;    v_avgx_neg unchanged
;    v_avgy_neg unchanged
;    led 5 is on 
;
w_still_wait:
    call    w_ad_start
#if ENAB_ISR_TAP
    call    w_tap_enable
#endif
    bra     w_sw_restart

w_sw_restart_still:
    bcf     bk_led2     ; led3 indicates stillness
w_sw_restart_level:
    bcf     bk_led1     ; led4 indicates level


v_wait_cnt  equ vaddr+0
vaddr+=1

w_sw_restart:
    ;
    ; wait for this many quarter seconds of stillness
    ;
    movlw   6
    movwf   v_wait_cnt

    ;
    ; reset min/max
    ;
    setf    v_accx_min
    setf    v_accy_min
    clrf    v_accx_max
    clrf    v_accy_max

w_sw_loop:
    call    w_main_run
    btfsc   bk_rs_halt
    bra     w_sw_abort

#if ENAB_ISR_TAP
    ;
    ; quit if tap occurred
    ;
    movf    v_tap_bits,f
    bnz     w_sw_abort
#endif

#if ENAB_BLINKS
    ;
    ; wait until tap blink sequence is done
    ;
    movf    v_blink_ptr,f
    bnz     w_sw_restart
#endif

    ;
    ; led5 is on while waiting for still
    ;
    bcf     bk_led0

    ;
    ; restart if maxx - minx > 2
    ;
    movf    v_accx_min,w
    subwf   v_accx_max,w
    addlw   -3
    bc      w_sw_restart_still

    ;
    ; restart if maxy - miny > 2
    ;
    movf    v_accy_min,w
    subwf   v_accy_max,w
    addlw   -3
    bc      w_sw_restart_still

    ;
    ; turn led3 off when wand is still
    ;
    btfss   bk_led2
    bra     w_sw_is_still

    ;
    ; NOTE: this just waits until x==y
    ; This could indicate right-side-up or upside-down.
    ; Actually indicates 45 degree angle for sensor.
    ;
    ; restart if |v_accx - v_accy| > 1
    ;
    movf    v_accy,w
    subwf   v_accx,w
    btfsc   WREG,7
    negf    WREG
    addlw   -2
    bc      w_sw_restart_level

    ;
    ; turn led4 off when wand is level & still
    ;
    btfss   bk_led1
    bra     w_sw_is_level

#if 0
#define STILL_WAIT_45_CW    1
#if STILL_WAIT_45_CW
    ;
    ; this accepts 45 degrees cw from upright
    ; needs relative and approximate values only
    ;

    ;
    ; restart if |v_accx - v_accy| > 1
    ;
    movf    v_accy,w
    subwf   v_accx,w
    btfsc   WREG,7
    negf    WREG
    addlw   -2
    bc      w_sw_restart

    ;
    ; restart if v_accx > 0x55
    ;
    movf    v_accy,w
    addlw   -0x55
    bnn     w_sw_restart
#else
    ;
    ; this accepts upright
    ; relies on 0g = 0x55
    ;

    ;
    ; restart if v_accy >= 0x52
    ;
    movf    v_accy,w
    addlw   -0x52
    bnn     w_sw_restart

    ;
    ; restart if |v_accx-0x55| > 1
    ;
    movf    v_accx,w
    addlw   -0x55
    btfsc   WREG,7
    negf    WREG
    addlw   -2
    bc      w_sw_restart
#endif
#endif


    ;
    ; loop for 5/4 - 6/4 sec of stillness
    ;
    btfss   bk_qsec
    bra     w_sw_loop

    decfsz  v_wait_cnt,f
    bra     w_sw_loop

    ;
    ; calc average of min,max for x & y
    ;
    rcall   w_calc_avg

w_sw_abort:
    bsf     bk_led1
    bsf     bk_led2
    return

w_sw_is_level:
    bsf     bk_led1     ; turn LED1 off
w_sw_is_still:
    bsf     bk_led2     ; turn LED2 off
    call    w_pause_ad_settle
    bra     w_sw_restart

#endif

;###########################################################################
;################################ CALC AVG #################################
;###########################################################################

;
; calc average acc val 
;
w_calc_avg:
    movf    v_accy_max,w
    addwf   v_accy_min,w
    rrncf   WREG,f
    andlw   0x7f
    negf    WREG
    movwf   v_avgy_neg

    movf    v_accx_max,w
    addwf   v_accx_min,w
    rrncf   WREG,f
    andlw   0x7f
    negf    WREG
    movwf   v_avgx_neg

#if 0
    setf    v_accx_min
    setf    v_accy_min
    clrf    v_accx_max
    clrf    v_accy_max
#endif

#if DBPRINT
    COUTL   'A'
    COUTL   'V'
    COUTL   'G'
    COUTL   ' '
    movf    v_accx_min,w
    call    kk_outbyte
    COUTL   ' '
    movf    v_avgx_neg,w
    negf    WREG
    call    kk_outbyte
    COUTL   ' '
    movf    v_accx_max,w
    call    kk_outbyte
    COUTL   ' '
    COUTL   ' '
    movf    v_accy_min,w
    call    kk_outbyte
    COUTL   ' '
    movf    v_avgy_neg,w
    negf    WREG
    call    kk_outbyte
    COUTL   ' '
    movf    v_accy_max,w
    call    kk_outbyte
    call    kk_outcr
#endif

    return

;###########################################################################
;################################ MOTION SPELLS ############################
;###########################################################################

#if DBORG
    org     0x2900
#endif

#if ENAB_FMTB_MOTION_SPELLS
#include        "wparse.inc"
#endif


#if ENAB_OLD_MOTION_SPELLS
;;;
;;;;
;;;; Call this to check for a motion spell
;;;;
;;;; REQUIRED INPUT CONDITIONS
;;;;  - sampling disabled (b_accbuf_sample=0)
;;;;  - RAMBUF_ACC contains samples terminated by 8 consecutive 0xfe entries
;;;;
;;;; RESOURCES USED
;;;;
;;;; OUTPUT CONDITIONS
;;;;  if spell is recognized:
;;;;    - b_got_spell is set
;;;;    - v_spell_ptrh,l = pointer to spell info (points to string for now)
;;;;  if spell is NOT recognized:
;;;;    - b_got_spell is clear
;;;;
;;;w_check_motion_spell:
;;; bcf     b_got_spell ; no spell recognized (yet)
;;; 
;;; lfsr    FSR2,RAMBUF_ACC
;;; lfsr    FSR1,RAMBUF_MOTION
;;;
;;; setf    v_mo1_s_angle
;;; bsf     v_mo1_s_prev_pause
;;;
;;; ;
;;; ; start collecting data for a new shape
;;; ;
;;; ;
;;; ; store each shape as follows
;;; ;
;;; ; 0 - angle
;;; ; 1 - impulseh
;;; ; 2 - impulsel
;;; ; 3 - maxmag
;;; ; 4 - shape
;;; ;
;;;v_mo1_begin_shape:
;;; swapf   v_mo1_s_flags,w
;;; andlw   MFLG_PN_FLAGS
;;; iorlw   MFLG_INIT_FLAGS
;;; movwf   v_mo1_s_flags       ; init flags (copy next pause to prev pause)
;;;
;;; clrf    v_mo1_s_maxstep
;;; clrf    v_mo1_s_asum
;;; clrf    v_mo1_s_impl
;;; clrf    v_mo1_s_imph
;;; clrf    v_mo1_s_maxdmag
;;;
;;;
;;; bsf     b_mo1_restart
;;;v_mo1_pause_begin:
;;; bsf     
;;;v_mo1_pause_loop:
;;; rcall   w_mo1_read
;;; btfsc   v_mo1_angle,7
;;; bra     v_mo1_start
;;; 
;;;
;;;
;;;
;;;;
;;;; read next entry
;;;;   input:
;;;;     v_mo1_angle = last angle
;;;;     b_mo1_pause = previous was pause
;;;;
;;;;   output:
;;;;     v_mo1_prev_angle = last angle
;;;;     v_mo1_angle = angle
;;;;     v_mo1_impl  = impulse lo
;;;;     v_mo1_imph  = impulse hi
;;;;     v_mo1_flags = flags
;;;;     v_mo1_step  = ABS(angle - prev_angle)
;;;;
;;;;     b_mo1_pause = 1 if pause
;;;;     b_mo1_dir   = cw(1) or ccw(0)
;;;;     b_mo1_end   = last 
;;;;
;;;;     b_mo1_restart - set if any of these conditions occur:
;;;;
;;;
;;;
;;;
;;;
;;;
;;;
;;;
;;;
;;;
;;; ;
;;; ; read & parse next motion  entry
;;; ;
;;;v_mo1_read:
;;;;
;;;; parse packets
;;;;
;;;; send pause
;;;;    1  time (h)
;;;;    0  time (l)
;;;;    2  unused (0xff)
;;;;    3  angle (0xff indicates pause)
;;;;
;;;;
;;;; send current
;;;;    0  time (h)
;;;;    1  time (l)
;;;;    2  max mag
;;;;    3  angle (0xff indicates pause)
;;;;    4  impulse (h)
;;;;    5  impulse (l)
;;;;    6  min mag
;;;;    7  flags (see below)
;;;;
;;;; valid values for angle
;;;;    0x00 - 0x1f  = angle
;;;;    0xff         = pause
;;;;    0xfe         = end of buffer
;;;;
;;;; flag bits
;;;;    0   set if any zero samples occurred
;;;;   1-6  unused (0)
;;;;    7   must be set (required by isr_ad_sample_pre_save)
;;;;
;;; movf    POSTINC2,w          ; time high (ignore)
;;; movf    POSTINC2,w          ; time low (ignore)
;;; movf    POSTINC2,w
;;; movwf   v_mo1_maxmag        ; max mag
;;; movf    POSTINC2,w
;;; movwf   v_mo1_angle         ; angle
;;;
;;; bnn     w_mo1_read_angle    ; it is an angle
;;;
;;; ;
;;; ; this is a pause
;;; ;
;;; btfsc   b_mo1_s_started
;;; bra     w_mo1_check_done
;;;
;;; bsf     b_mo1_s_next_pause
;;; ;
;;; ; Handle pause following motion
;;; ;
;;; bra     w_mo1_store
;;;
;;;
;;;;
;;;; this is an angle - read the rest of it
;;;;
;;;w_mo1_read_angle:
;;; movff   v_mo1_angle,v_mo1_prev_angle
;;;
;;; ;
;;; ; angle - read the rest of the fields
;;; ;
;;; movf    POSTINC2,w      ; impulse h
;;; movf    POSTDEC2,w      ; impulse l
;;; addwf   v_mo1_s_impl,f
;;; movf    POSTINC2,w      ; impulse h
;;; addwfc  v_mo1_s_imph,f
;;; btfsc   STATUS,C
;;; setf    v_mo1_s_imph    ; clamp to high value
;;; movf    POSTINC2,w      ; impulse l
;;; movf    POSTINC2,w      ; min mag 
;;; subwf   v_mo1_maxmag,w  ; delta = max-min
;;;
;;; btfsc   INDF2,0         ; ZERO flag
;;; movf    v_mo1_maxmag,w  ; delta = max (if ZERO flag is set)
;;; 
;;; cpfsgt  v_mo1_s_maxdmag ; max magnitude delta
;;; movwf   v_mo1_s_maxdmag
;;;
;;; movf    POSTINC2,w      ; flags
;;; andlw   MFLG_ZERO       ; only care about zero flag
;;; iorwf   v_mo1_s_flags,f
;;;
;;; movf    v_mo1_prev_angle,w  ; calc step
;;; subwf   v_mo1_angle,w
;;; rcall   w_adiff
;;; bcf     b_mo1_s_dir
;;; btfsc   WREG,7
;;; bsf     b_mo1_s_dir
;;; btfsc   WREG,7
;;; negf    WREG
;;; 
;;; ; here w is step
;;;
;;; cpfsgt  v_mo1_s_maxstep
;;; movwf   v_mo1_s_maxstep
;;; addwf   v_mo1_s_asum,f
;;;
;;; swapf   v_mo1_s_flags,w ; xor prev & next dir
;;; xorwf   v_mo1_s_flags,w
;;; btfsc   WREG,1          ; dir changed? 
;;; bcf     b_mo1_s_mono    ; yes - not monotonic
;;;
;;; movf    v_mo1_prev_angle,w
;;; rcall   a2oct
;;; movwf   v_tmp
;;; movf    v_mo1_angle,w
;;; rcall   a2oct
;;; subwf   v_tmp,w
;;; bnz     w_mo1_newoct
;;;
;;;
;;;w_mo1_newoct:
;;; 
;;; 
;;; 
;;;
;;;
;;;
;;;
;;; ;
;;; ; re-reading previous?
;;; ;
;;; btfsc   b_mo1_s_started
;;; bra     w_mo1_read_angle_started
;;;
;;; ;
;;; ; first time - reading preceding record
;;; ;
;;; bsf     b_mo1_s_started
;;;
;;;
;;;
;;;
;;; ; get the next one
;;; bra     v_mo1_read
;;;
;;;w_mo1_read_angle_started:
;;;
;;;
;;;
;;;
;;; ;
;;; ; get next one
;;; ;
;;; bra     v_mo1_read
;;;
;;;;
;;;; something is different.  Store this one, then re-read next.
;;;;
;;;w_mo1_store_angle:
;;;
;;; ;
;;; ; cause next record to be re-read
;;; ;
;;; movlw   8
;;; subwf   FSR2L,f
;;; movlw   0
;;; subwfb  FSR2H,f
;;;
;;;w_mo1_store:
;;; ;
;;; ; store preceding collection of pulses
;;; ;
;;; ; 0 - angle
;;; ; 1 - impulseh
;;; ; 2 - impulsel
;;; ; 3 - maxmag
;;; ; 4 - shape
;;; ;
;;; ; bits stored in shape:
;;;#define MSHF_PLUS            0x01    ; (part of shape)
;;;#define MSHF_PULSE           0x04    ; (part of shape)
;;;#define MSHF_CLEAN           0x02    ; (part of shape)
;;;#define MSHF_SHP_MASK        0x07    ; (all bits of shape)
;;;#define MSHF_GAP         0x08
;;;#define MSHF_PAUSE           0x80    ; set if preceded by pause
;;;#define MSHF_MONO            0x40
;;;#define MSHF_DIR             0x20    ; dir (set if cw (negative))
;;;#define MSHF_MONO_PAUSE  0xc0
;;;
;;;
;;;;define MSHP_PAUSE                   not used
;;;#define MSHP_DIRTY_CIRCLE            0x00
;;;#define MSHP_DIRTY_CIRCLE_PLUS       0x01
;;;#define MSHP_CLEAN_CIRCLE            0x02
;;;#define MSHP_CLEAN_CIRCLE_PLUS       0x03
;;;#define MSHP_PULSE                   0x04
;;;#define MSHP_PULSE_PLUS              0x05
;;;#define MSHP_CIRCLE_GAP              0x0e
;;;
;;; ;
;;; ; bits stored in v_mo1_s_flags
;;; ;
;;;#define MFLG_PREV_PAUSE      0x80    ; b_mo1_s_prev_pause
;;;#define MFLG_NEXT_PAUSE      0x08    ; b_mo1_s_next_pause
;;;#define MFLG_PREV_DIR        0x20    ; b_mo1_s_prev_dir
;;;#define MFLG_NEXT_DIR        0x02    ; b_mo1_s_next_dir
;;;#define MFLG_MONO            0x40    ; b_mo1_s_mono
;;;#define MFLG_ZERO            0x01    ; b_mo1_s_zero
;;;#define MFLG_STARTED     0x04    ; b_mo1_s_started
;;;
;;;#define MFLG_SHAPE_FLAGS 0xe0    ; copy these flags to shape
;;;#define MFLG_PN_FLAGS        0xa0    ; copy these flags to from lo to hi nibble
;;;#define MFLG_INIT_FLAGS      0x40    ; copy these flags to from lo to hi nibble
;;;
;;;#define b_mo1_s_prev_pause       v_mo1_s_flags,7
;;;#define b_mo1_s_next_pause       v_mo1_s_flags,3
;;;#define b_mo1_s_prev_dir     v_mo1_s_flags,5
;;;#define b_mo1_s_next_dir     v_mo1_s_flags,1
;;;#define b_mo1_s_mono         v_mo1_s_flags,6
;;;#define b_mo1_s_zero         v_mo1_s_flags,0
;;;#define b_mo1_s_started          v_mo1_s_flags,2
;;;
;;;
;;; ;
;;; ;
;;; movwf   POSTINC1    ; angle
;;; movf    v_mo1_s_impulseh
;;; movwf   POSTINC1    ; impulse h
;;; movf    v_mo1_s_impulsel
;;; movwf   POSTINC1    ; impulse l
;;; movf    v_mo1_s_maxmag
;;; movwf   POSTINC1    ; max magnitude
;;;
;;; btfss   b_mo1_s_mono
;;; clrf    v_mo1_s_asum
;;;
;;; movlw   MSHP_PULSE
;;; movwf   INDF1               ; set shape to pulse
;;;
;;; btfss   b_mo1_s_mono
;;; bra     v_mo1_store_shape   ; pulse if not MONO
;;;
;;; btfss   b_mo1_s_next_pause
;;; btfsc   b_mo1_s_prev_pause
;;; bra     v_mo1_store_shape   ; pulse if preceded or followed by pause
;;;
;;; movf    v_mo1_s_asum,w
;;; addlw   -4
;;; bnc     v_mo1_store_shape   ; pulse if asum<4
;;;
;;; movf    v_mo1_s_maxstep,w
;;; addlw   -6
;;; bc      v_mo1_store_shape   ; pulse if maxstep>=6
;;;
;;; bcf     INDF1,2             ; not a pulse - set shape to dirty circle
;;;
;;; addlw   6-4
;;; bnn     v_mo1_store_shape   ; dirty circle if maxstep>=4
;;;
;;; btfsc   b_mo1_s_zero
;;; bra     v_mo1_store_shape   ; dirty circle if ZERO is set
;;;
;;; movf    v_mo1_maxdmag,w
;;; addlw   -8
;;; bc      v_mo1_store_shape   ; dirty circle if maxdmag >= 8
;;;
;;; bsf     INDF1,1             ; clean circle!
;;;
;;;!! todo: if next is pause or end then come back and make this a pulse
;;;
;;; ;
;;; ; store shape & flags (actually already stored at this point)
;;; ;
;;;v_mo1_store_shape:
;;; movf    v_mo1_s_flags,w
;;; andlw   MFLG_SHAPE_FLAGS
;;; iorwf   POSTINC1,f          ; store flags & direction
;;;
;;;
;;;w_mo1_check_done:
;;; movf    v_mo1_angle,w
;;; addlw   -0xfe
;;; bnz     v_mo1_begin_shape
;;;
;;; setf    POSTDEC1            ; mark end with 0xff
;;;
;;; ;
;;; ; done - make last shape into a pulse
;;; ;
;;; movf    INDF1,w
;;; andlw   MSHF_MONO_PAUSE     ; preserve MONO & PAUSE flags
;;; iorlw   MSHP_PULSE
;;; movwf   INDF1
;;;
;;;DONE HERE!!!!!!!!!!!!
;;;
;;;
;;;CRAP FOLLOWS
;;; btf
;;;
;;;
;;;
;;; clrf    v_mo1_s_maxstep
;;; movf    v_mo1_step,w
;;; movwf   v_mo1_s_asum
;;; movf    v_mo1_impl,w
;;; movwf   v_mo1_s_impl
;;; movf    v_mo1_imph,w
;;; movwf   v_mo1_s_imph
;;;
;;;
;;;
;;;
;;;
;;;
;;; addlw   -0xfe
;;; bz      w_mo1_read_loop_done
;;;
;;; ;
;;; ; did we have
;;; ;
;;; addlw   0xfe-0xff
;;; bnz     w_mo1_read_loop_notime  ; not a pause - ignore it
;;;
;;; ;
;;; ; PAUSE
;;; ;   - clear got-tap (been long enough)
;;; ;   - include time and get next record
;;; ;
;;; bcf     b_ry_got_tap
;;; bra     w_crs_read_loop
;;;
;;;xxw_mo1_read_angle:
;;; ;
;;; ; angle - read the rest of the fields
;;; ;
;;; movf    POSTINC2,w
;;; movwf   v_ry_ih         ; impulse high
;;; movf    POSTINC2,w
;;; movwf   v_ry_il         ; impulse low
;;; movf    POSTINC2,w
;;; movwf   v_ry_magn       ; min mag
;;; movf    POSTINC2,w
;;; movwf   v_ry_magx       ; max mag
;;;
;;; ;
;;; ; is force up? (angle = 0x0a - 0x15)
;;; ;
;;; movf    v_ry_a,w
;;; addlw   -0x0a
;;; addlw   -(0x16-0x0a)
;;; bnc     w_crs_read_loop_is_up
;;; 
;;; ;
;;; ; direction is wrong for a tap - clear got_tap and continue
;;; ;
;;; bcf     b_ry_got_tap
;;; bra     w_crs_read_loop
;;;
;;; 
;;;
;;; return
;;;
#endif

;###########################################################################
;################################ RYTHM SPELLS #############################
;###########################################################################

#if DBORG
    org     0x3400
#endif


;
; need at least this many taps to be a rythm
;
#define MIN_RYTM_TAPS       5

;
; temp variables used in w_check_rythm_spell
;
v_ry_bits       equ vaddr+0     ;
v_ry_tap_cnt    equ vaddr+1     ; # of taps
v_ry_cnth       equ vaddr+2     ; accumulated time for next tap
v_ry_cntl       equ vaddr+3     ;
v_ry_minh       equ vaddr+4     ; min tap time
v_ry_minl       equ vaddr+5     ;
v_ry_ch         equ vaddr+6     ; sample time
v_ry_cl         equ vaddr+7     ;
v_ry_a          equ vaddr+8     ; sample angle
v_ry_ih         equ vaddr+9     ; sample impulse
v_ry_il         equ vaddr+10    ;
v_ry_magn       equ vaddr+11    ; sample min mag
v_ry_magx       equ vaddr+12    ; sample max mag
vaddr+=13

RYTHM_MIN_TAP_TIME  equ     100 ; taps must be at least this many counts apart

#define b_ry_got_tap        v_ry_bits,0 ; 1 if found a tap
#define b_ry_got_first_tap  v_ry_bits,1 ; set after first tap is found
#define b_ry_save_mul       v_ry_bits,2 ; set to store multiplier in buffer
#define b_ry_got_zero       v_ry_bits,3 ; got at least 1 zero
#define b_ry_run_on         v_ry_bits,4 ; ok if extra notes follow


v_ry_loopcnt1       equ vaddr+0     ; 
v_ry_loopcnt2       equ vaddr+1     ; 
v_ry_best_minh      equ vaddr+2     ; best beat time
v_ry_best_minl      equ vaddr+3     ; 
v_ry_best_scoreh    equ vaddr+4     ; score for best beat time
v_ry_best_scorel    equ vaddr+5     ; 
v_ry_scoreh         equ vaddr+6     ; score for current beat time
v_ry_scorel         equ vaddr+7     ; 
v_ry_err0h          equ vaddr+8     ; error0
v_ry_err0l          equ vaddr+9     ; 
v_ry_err1h          equ vaddr+10    ; error1
v_ry_err1l          equ vaddr+11    ; 
vaddr+=12

;
; Call this to check for a rythm spell
;
; REQUIRED INPUT CONDITIONS
;  - sampling disabled (b_accbuf_sample=0)
;  - RAMBUF_ACC contains samples terminated by entry with angle=0xfe
;
; RESOURCES USED
;  - uses RAMBUF_RYTHM for scratch buffer
;
; OUTPUT CONDITIONS
;  if spell is recognized:
;    - b_got_spell is set
;    - v_spell_ptrh,l = pointer to spell info (points to string for now)
;  if spell is NOT recognized:
;    - b_got_spell is clear
;
w_check_rythm_spell:
    bcf     b_got_spell ; no spell recognized (yet)
    
    lfsr    FSR2,RAMBUF_ACC
    lfsr    FSR1,RAMBUF_RYTHM

    setf    v_ry_cnth
    setf    v_ry_cntl
    clrf    v_ry_bits
    clrf    v_ry_ch
    clrf    v_ry_cl

#define DBG_RYTHM_TTIME 1
#if DBG_RYTHM_TTIME
    ;
    ; v_t0l,h are for debug - total time
    ;
    clrf    v_t0h
    clrf    v_t0l
#endif

    setf    v_ry_minh
    setf    v_ry_minl

    ;
    ; find each tap in buffer and record time
    ;
w_crs_read_loop:
#if DBG_RYTHM_TTIME
    movf    v_ry_cl,w
    addwf   v_t0l,f
    movf    v_ry_ch,w
    addwfc  v_t0h,f     ; let it wrap
#endif
    ;
    ; add time from previous entry
    ;
    movf    v_ry_cl,w
    addwf   v_ry_cntl,f
    movf    v_ry_ch,w
    addwfc  v_ry_cnth,f
    bnc     w_crs_read_loop_notime

    setf    v_ry_cnth   ; clamp to max
    setf    v_ry_cntl   ; clamp to max

w_crs_read_loop_notime:

    ;
    ; read next entry
    ;
    movf    POSTINC2,w
    movwf   v_ry_ch         ; time high
    movf    POSTINC2,w
    movwf   v_ry_cl         ; time low
    movf    POSTINC2,w
    movwf   v_ry_magx       ; max mag
    movf    POSTINC2,w
    movwf   v_ry_a          ; angle

    bnn     w_crs_read_loop_angle   ; it is an angle

    addlw   -0xfe
    bz      w_crs_read_loop_done

    ;
    ; if (angle == 0xff) then this is a PAUSE
    ;
    addlw   0xfe-0xff
    bnz     w_crs_read_loop_notime  ; not a pause - ignore it

    ;
    ; PAUSE
    ;   - clear got-tap (been long enough)
    ;   - include time and get next record
    ;
    bcf     b_ry_got_tap
    bra     w_crs_read_loop

w_crs_read_loop_angle:
    ;
    ; angle - read the rest of the fields
    ;
    movf    POSTINC2,w      ; impulse high
    movwf   v_ry_ih
    movf    POSTINC2,w      ; impulse low
    movwf   v_ry_il
    movf    POSTINC2,w      ; min mag
    movwf   v_ry_magn
    movf    POSTINC2,w      ; flags

    ;
    ; is force up? (angle = 0x0a - 0x15)
    ;
    movf    v_ry_a,w
    addlw   -0x0a
    addlw   -(0x16-0x0a)
    bnc     w_crs_read_loop_is_up
    
    ;
    ; direction is wrong for a tap - clear got_tap and continue
    ;
    bcf     b_ry_got_tap
    bra     w_crs_read_loop

w_crs_read_loop_is_up:

    ;
    ; direction is right for a tap - is magnitude big enough?
    ;
    movlw   -ACC_TH_MAG_TAP
    addwf   v_ry_magx,w
    bnc     w_crs_read_loop ; nope - continue

    ;
    ; is this just part of an earlier tap?
    ;
    btfsc   b_ry_got_tap
    bra     w_crs_read_loop

    ;
    ; has it been long enough since last tap?
    ;
    movlw   LOW  RYTHM_MIN_TAP_TIME
    subwf   v_ry_cntl,w
    movlw   HIGH RYTHM_MIN_TAP_TIME
    subwfb  v_ry_cnth,w
    bnc     w_crs_read_loop
    

w_crs_read_loop_tap:
    btfss   b_ry_got_first_tap
    bra     w_crs_read_loop_tap_next

    ;
    ; no time accumulated (should not happen)
    ;
    movf    v_ry_cnth,w
    iorwf   v_ry_cntl,w
    bz      w_crs_read_loop

#if 1 && DBPRINT
#if DBG_RYTHM_TTIME
    movf    v_t0h,w
    call    kk_outbyte
    movf    v_t0l,w
    call    kk_outbyte
    COUTL   ' '
#endif
#endif
    ;
    ; save tap time 
    ;
    movf    v_ry_cnth,w
    movwf   POSTINC1
#if 1 && DBPRINT
    call    kk_outbyte
#endif
    movf    v_ry_cntl,w
    movwf   POSTINC1

#if 1 && DBPRINT
    call    kk_outbyte
    call    kk_outcr
    DB_FLUSH
#endif

    ;
    ; ignore extra taps (only RSPELL_MAX_TAPS count)
    ;
    movf    FSR1L,w
    addlw   -((RSPELL_MAX_TAPS+1)*2)
    bc      w_crs_read_loop_done

    ;
    ; only look for min time in first RSPELL_LATEST_ONE taps
    ;
    movf    FSR1L,w
    addlw   -((RSPELL_LATEST_ONE+1)*2)
    bc      w_crs_read_loop_tap_next

    ;
    ; get min tap time
    ;
    movf    v_ry_minl,w
    subwf   v_ry_cntl,w
    movf    v_ry_minh,w
    subwfb  v_ry_cnth,w
    bc      w_crs_read_loop_tap_next

    ;
    ; new min time
    ;
    movf    v_ry_cntl,w
    movwf   v_ry_minl
    movf    v_ry_cnth,w
    movwf   v_ry_minh


w_crs_read_loop_tap_next:
    bsf     b_ry_got_first_tap
    bsf     b_ry_got_tap

    ;
    ; clear tap time
    ;
    clrf    v_ry_cntl
    clrf    v_ry_cnth

    bra     w_crs_read_loop

w_crs_read_loop_done:

    ;
    ; scan LED
    ;
    call    w_ledscan

    ;
    ; v_ry_tap_cnt = # taps in buffer
    ; mark end of buffer with 00
    ;
    rrncf   FSR1L,w
    movwf   v_ry_tap_cnt

    ;
    ; too few taps?
    ;
    addlw   -RSPELL_MIN_TAPS
    btfss   STATUS,C
    bra     w_crs_done


    clrf    POSTINC1
    clrf    POSTINC1


#if DBPRINT
    ;
    ; debug printf
    ;
    call    kk_outcr

    COUTL   'T'
    movf    v_ry_tap_cnt,w
    call    kk_outbyte

    COUTL   ' '
    COUTL   'N'
    movf    v_ry_minh,w
    call    kk_outbyte
    movf    v_ry_minl,w
    call    kk_outbyte

    call    kk_outcr
#endif

    ;
    ; abort if min time is too big (>2.6 sec)
    ;
    movf    v_ry_minh,w
    andlw   0xf0
    bnz     w_crs_done

    ;
    ; prepare to loop over all times between min-0x80 and min+0x80
    ; (stepping by 1)
    ;
    movlw   LOW (-0x81)
    addwf   v_ry_minl,f
    movlw   HIGH (-0x81)
    addwfc  v_ry_minh,f
    bc      w_crs_prep1

    clrf    v_ry_minl   ; clamp
    clrf    v_ry_minh

w_crs_prep1:

    clrf    v_ry_loopcnt1
    clrf    v_ry_best_minh
    clrf    v_ry_best_minl
    setf    v_ry_best_scoreh
    setf    v_ry_best_scorel

    ;
    ; loop over all times between min-0x80 and min+0x80
    ; (if min<=0x80 then loop from 0x01 - 0x100)
    ; (stepping by 1)
    ;
w_crs_time_loop:
    ;
    ; increment time
    ;
    infsnz  v_ry_minl,f
    incf    v_ry_minh,f

    ;
    ; score this time value (do not save multipliers)
    ;
    bcf     b_ry_save_mul
    rcall   w_crs_score

#if 0 && DBPRINT
    DB_FLUSH

    COUTL   'M'
    movf    v_ry_minh,w
    call    kk_outbyte
    movf    v_ry_minl,w
    call    kk_outbyte
    COUTL   ' '
    COUTL   'S'
    movf    v_ry_scoreh,w
    call    kk_outbyte
    movf    v_ry_scorel,w
    call    kk_outbyte

    COUTL   ' '
    COUTL   'B'
    movf    v_ry_bits,w
    call    kk_outbyte
    call    kk_outcr
    DB_FLUSH
#endif

    btfss   b_ry_got_zero
    bra     w_crs_time_loop_end     ; no zeros - probably mul is too big


    ;
    ; save smallest score with multiplier that caused it
    ;
    movf    v_ry_best_scorel,w
    subwf   v_ry_scorel,w
    movf    v_ry_best_scoreh,w
    subwfb  v_ry_scoreh,w
    
    bc      w_crs_time_loop_end ; new score is bigger - try next mul

    ;
    ; new score is smaller - save new mul
    ;
    movff   v_ry_scorel,v_ry_best_scorel
    movff   v_ry_scoreh,v_ry_best_scoreh
    movff   v_ry_minl,v_ry_best_minl
    movff   v_ry_minh,v_ry_best_minh
    
    ;
    ; end loop over times
    ;
w_crs_time_loop_end:
    decfsz  v_ry_loopcnt1,f
    bra     w_crs_time_loop
    

#if DBPRINT
    DB_FLUSH
    COUTL   'S'
    COUTL   'C'
    movf    v_ry_best_scoreh,w
    call    kk_outbyte
    movf    v_ry_best_scorel,w
    call    kk_outbyte
    call    kk_outcr


    COUTL   'B'
    COUTL   'C'
    movf    v_ry_best_minh,w
    movwf   v_ry_minh
    call    kk_outbyte
    movf    v_ry_best_minl,w
    movwf   v_ry_minl
    call    kk_outbyte
    call    kk_outcr
    DB_FLUSH
#endif

    ;
    ; scan LED
    ;
    call    w_ledscan

    ;
    ; use the best cnt value and break the rythm into 0-7 numbers
    ;
    lfsr    FSR2,RAMBUF_RYTHM
    bsf     b_ry_save_mul
    rcall   w_crs_score

#if DBPRINT
    call    kk_outcr
#endif

    ;
    ; scan LED
    ;
    call    w_ledscan

    rcall   w_crs_check_spells
w_crs_done:
    return


;===========================================================================
; loop over each tap to calculate score & multiple
;===========================================================================
w_crs_score:
    bcf     b_ry_got_zero
    clrf    v_ry_scorel
    clrf    v_ry_scoreh
    lfsr    FSR1,RAMBUF_RYTHM

w_crs_tap_loop:

    movf    POSTINC1,w
    movwf   v_ry_ch
    movf    POSTINC1,w
    movwf   v_ry_cl

    iorwf   v_ry_ch,w   ; reached the last one?
    btfsc   STATUS,Z
    return
    
    movlw   8
    movwf   v_ry_loopcnt2
    clrf    v_ry_cnth
    clrf    v_ry_cntl
    setf    v_ry_err0l
    setf    v_ry_err0h

    ;
    ; loop over 1-8 times minl,h time
    ;
w_crs_mul_loop:
    movf    v_ry_minl,w
    addwf   v_ry_cntl,f
    movf    v_ry_minh,w
    addwfc  v_ry_cnth,f

    movf    v_ry_cntl,w
    subwf   v_ry_cl,w
    movwf   v_ry_err1l
    movf    v_ry_cnth,w
    subwfb  v_ry_ch,w
    movwf   v_ry_err1h

    bnc     w_crs_mul_loop_cnt_bigger

    movwf   v_ry_err0h
    movff   v_ry_err1l,v_ry_err0l
    
    ;
    ; loop over 8 possibilities
    ;
    decfsz  v_ry_loopcnt2,f
    bra     w_crs_mul_loop
    
    ;
    ; tap time is longer than 8x - just use 8x
    ;


    ;
    ; save multiplier if b_ry_save_mul is set
    ;
w_crs_mul_save:
    comf    v_ry_loopcnt2,w
    addlw   8

    btfsc   STATUS,Z
    bsf     b_ry_got_zero

    incf    WREG,w

#if 0 && DBPRINT
    movwf   v_ry_loopcnt2
    COUTL   '.'
    movf    v_ry_loopcnt2,w
    call    kk_outbyte
    COUTL   ' '
    movf    v_ry_loopcnt2,w
#endif

    btfss   b_ry_save_mul
    bra     w_crs_mul_score

    ;
    ; turn 7s into 8s (there are no 7s, and 8s are hard to get sometimes)
    ;
    addlw   -7
    btfsc   STATUS,Z
    incf    WREG,w
    addlw   7

#if DBPRINT
    movwf   v_ry_loopcnt2
    call    kk_outbyte
    COUTL   ' '
    movf    v_ry_loopcnt2,w
#endif

    iorwf   POSTINC2,f      ; store in low nibble of byte
    btfss   FSR1L,1
    bra     w_crs_mul_score

    clrf    POSTDEC2        ; set last one to 0x00
    swapf   WREG,w
    movwf   INDF2           ; store in high nibble of byte
                            ; (next one will go in low nibble, same byte)


    ;
    ; add error to score
    ;
w_crs_mul_score:
#if 0 && DBPRINT
    COUTL   'e'
    movf    v_ry_err0h,w
    call    kk_outbyte
    movf    v_ry_err0l,w
    call    kk_outbyte
    COUTL   ' '
#endif

    movf    v_ry_err0l,w
    addwf   v_ry_scorel,f
    movf    v_ry_err0h,w
    addwfc  v_ry_scoreh,f
    bnc     w_crs_tap_loop

    ;
    ; abort if score overflows
    ;
    setf    v_ry_scorel     ; clamp
    setf    v_ry_scoreh
    return

    ;
    ; here err0 is positive and err1 is negative
    ; if err1 is smaller then cp to err0 and decrement v_ry_loopcnt2
    ;
w_crs_mul_loop_cnt_bigger:
    movf    v_ry_err0l,w
    addwf   v_ry_err1l,w
    movf    v_ry_err0h,w
    addwfc  v_ry_err1h,w

    bnc     w_crs_mul_save  ; err0 is smaller
    
    ;
    ; err1 is smaller
    ;
    decf    v_ry_loopcnt2,f

    comf    v_ry_err1l,w
    movwf   v_ry_err0l
    comf    v_ry_err1h,w
    movwf   v_ry_err0h
    infsnz  v_ry_err0l,f
    incf    v_ry_err0h,f

    bra     w_crs_mul_save

;===========================================================================
; compare result against spell (exact match only)
; TBLPTR should point to spell - 1
;
w_crs_spell_cmp:
    tblrd*-
    lfsr    FSR1,RAMBUF_RYTHM
    
w_crs_spell_cmp_loop:
    tblrd*+
    movf    TABLAT,w
    btfsc   b_ry_run_on
    bz      w_crs_spell_cmp_good
    subwf   POSTINC1,w
    bnz     w_crs_spell_cmp_bad
    movf    TABLAT,w
    bnz     w_crs_spell_cmp_loop

w_crs_spell_cmp_good:
    bsf     b_got_spell
w_crs_spell_cmp_bad:
    movf    TABLAT,w
    btfsc   STATUS,Z
    return

    ;
    ; find end of spell 0x00
    ;
    tblrd*+
    bra     w_crs_spell_cmp_bad

;===========================================================================
;
; compare result againste each spell
;
w_crs_check_spells:

    ;
    ; prepare to recognize spells
    ;
    bcf     b_ry_run_on
    movlw   HIGH w_rspell1
    movwf   TBLPTRH
    movlw   LOW w_rspell1
    movwf   TBLPTRL

w_crs_check_loop:
    tblrd*+
    movf    TABLAT,w
    bz      w_crs_check_loop    ; ignore extra 0x00

    ;
    ; 0xff = done
    ; 0xfe = partial spell
    ; 0xfd = alternate version of same spell
    ; else = check
    ;
    infsnz  TABLAT,f
    return              ; no rythm matched
    
    infsnz  TABLAT,f
    bra     w_crs_check_partial
    
    ;
    ; check spell
    ;
    rcall   w_crs_spell_cmp

    ;
    ; read spell info
    ;
    tblrd*+
    movff   TABLAT,v_spell_ptrh
    tblrd*+
    movff   TABLAT,v_spell_ptrl

    ;
    ; check next if no match
    ;
    btfss   b_got_spell
    bra     w_crs_check_loop

    SOUT1L  w_str_gotrythm
    movf    v_spell_ptrh,w
    call    w_outbyte
    movf    v_spell_ptrl,w
    call    w_outbyte
    call    w_outcr
    DB_FLUSH

    ;
    ; Recognized a spell!!
    ;
    return

w_crs_check_partial:
    bsf     b_ry_run_on     ; start partial spells
    bra     w_crs_check_loop


#if DBORG
    org     0x3700
#endif

;
; special numbers in spells
;
SPELL_END       equ 0x00    ; terminate spell - ignored elsewhere
SPELL_LAST      equ 0xff    ; follows last spell
SPELL_PARTIAL   equ 0xfe    ; begin partial spells

#include        "rspells.inc"


;###########################################################################
;################################ PARSING MOTIONS ##########################
;###########################################################################

#if ENAB_W_PARSE

#if DBORG
    org     0x3500
#endif

;
; Acceleration Parser
;
; FUNCTIONS
; ---------
;  w_parse_init   - initialize parsing
;  w_parse        - call to parse next accbuf entry (if any)
;  w_parse_finish - finish parsing and filling RAMBUF_MOTION
;  w_parse_flush  - call w_parse until no more accbuf entries available
;
; OVERVIEW
; --------
; Read output of RAMBUF_ACC and write parsed motion bytes
;
;  OUTPUT FORMAT
;    0daa cccc
;
;    cccc = count - how many half cirles were made.  0=end (not a circle)
;    aa   = angle  00=up 01=right 10=down 11=left
;    d    = direction 0=cw 1=ccw
;
;  if cccc==0: aa = angle of largest magnitude
;               d = was actual angle cw or ccw of indicated angle
;
;  if cccc!=0: aa = average of start angle and end angle.
;               d = direction of circles
;

;
; Global state vars
;
; v_spell_len  - ptr to dst buffer & # bytes in spell


;
; state vars
;
v_ap_srcl           equ vaddr+0 ; pointer to source data
v_ap_srch           equ vaddr+1
v_ap_max_mag        equ vaddr+2 ; max magnitude
v_ap_max_mag_angle  equ vaddr+3 ; angle of max magnitude
v_ap_prev_angle     equ vaddr+4 ; previous angle
v_ap_cnt            equ vaddr+5 ; number of circles so far
v_ap_dir            equ vaddr+6 ; bit7: 1=ccw 0=cw
v_ap_wag_cnt        equ vaddr+7 ; 8 - # of left-right wags in a row
v_ap_angle_pos      equ vaddr+8 ; current angle position in circle
v_ap_angle_pos_min  equ vaddr+9 ; min     angle position in circle
v_ap_angle_pos_max  equ vaddr+10 ; max     angle position in circle
v_ap_max_angle      equ vaddr+11 ; v_ap_angle at angle_pos_max
vaddr+=12

;
; tmp vars
;
v_ap_delta_angle_abs    equ vaddr+0 ; abs value of delta angle
v_ap_delta_angle        equ vaddr+1 ; delte angle
v_ap_result             equ vaddr+2 ; result
v_ap_mag                equ vaddr+3 ; current mag
v_ap_angle              equ vaddr+4 ; current angle
v_ap_angle_tmp1         equ vaddr+5 ; temporary to save value during result
vaddr+=6
    
;===========================================================================
; DEBUG CODE
;===========================================================================
#define WP_DEBUG    0
#define WP_DEBUG2   1
#define WP_DEBUG3   1
#if WP_DEBUG
WPARSE_DBG  macro   whr
    movlw       whr
    movwf       v_tmp
    rcall       w_parse_dbg
    endm
#else
WPARSE_DBG  macro   whr
    endm
#endif

;===========================================================================
; initialize parsing
;===========================================================================
w_parse_init:
    movlw   LOW RAMBUF_ACC
    movwf   v_ap_srcl
    movlw   HIGH RAMBUF_ACC
    movwf   v_ap_srch
    movlw   ACC_STATE_INVALID
    clrf    v_spell_len
    clrf    v_ap_max_mag
    bcf     b_parse_wag
    bcf     b_parse_done
    movlw   8
    movwf   v_ap_wag_cnt
    return

;===========================================================================
; finish parsing & flush output
;===========================================================================
w_parse_finish:
#if WP_DEBUG
    COUTL   'x'
    call    kk_outcr
#endif

    bcf     b_accbuf_sample     ; disable sampling
    rcall   w_sample_store_zero ; store an extra zero sample in buffer
    rcall   w_parse_flush

#if WP_DEBUG
    COUTL   'y'
    call    kk_outcr
#endif

    ;
    ; store an entry which will force a jump in w_parse (flushes last val)
    ;
    rcall   w_sample_store_zero ; store an extra zero sample in buffer
    movff   FSR2H,FSR0H
    movff   FSR2L,FSR0L
    movf    POSTDEC0,f      ; point to mag
    movlw   1
    movwf   POSTDEC0        ; mag=1; point to angle
    movf    v_ap_prev_angle,w
    btg     WREG,4
    movwf   INDF0           ; angle = opposite of prev angle

#if WP_DEBUG
    COUTL   'w'
    movf    v_ap_prev_angle,w
    call    kk_outbyte
    call    kk_outcr
#endif
    
    rcall   w_sample_store_zero ; store an extra zero sample in buffer

    ;
    ; fall through to w_parse_flush
    ;

;===========================================================================
; handle all entries in the acc buffer
;===========================================================================
w_parse_flush:

    WPARSE_DBG  'z'

    rcall   w_parse
    btfss   b_parse_done
    bra     w_parse_flush
    return

;===========================================================================
; read one ACC buffer record (if available) and parse it into motions
;===========================================================================
w_parse:
    ;
    ; is next record available?
    ;
    bsf     b_parse_done    ; set if no more to read
    movf    v_ap_srch,w
    movwf   FSR0H
    subwf   FSR2H,w
    movwf   v_tmp
    movf    v_ap_srcl,w
    movwf   FSR0L
    subwf   FSR2L,w
    iorwf   v_tmp,w
    btfsc   STATUS,Z
    return              ; return if head==tail

    ;
    ; increment src pointer
    ;
    movlw   4
    addwf   v_ap_srcl,f
    btfsc   STATUS,C
    incf    v_ap_srch,f

    bcf     b_parse_done    ; still more to read (maybe)

    ;
    ; do nothing if spell is complete
    ;
    btfsc   b_parse_wag
    bra     w_parse_end

    ;
    ; read sample (currently pointing just AFTER sample)
    ;
    movf    POSTDEC0,w  ; garbage (now point to mag)
    movf    POSTDEC0,w  ; magnitude (now point to astate)
    movwf   v_ap_mag
    bz      w_parse_end ; ignore zero mag states
    movf    POSTINC0,w  ; astate (now point to mag)
    movwf   v_ap_angle

    ;
    ; check for special astates
    ;
    addlw   0xe0
    bnc     w_parse_check_first

    ;
    ;   TODO: for long zero states halt circle (send result)

    bra     w_parse_end     ; ignore this state


    ;
    ; is this the first angle?
    ;
w_parse_check_first:
    movf    v_ap_max_mag,f
    bnz     w_parse_cont

;
; start a new motion
;
w_parse_start:
    movf    v_ap_mag,w
    movwf   v_ap_max_mag
    movf    v_ap_angle,w
    movwf   v_ap_prev_angle
    movwf   v_ap_max_mag_angle
    movwf   v_ap_max_angle
    clrf    v_ap_cnt
    clrf    v_ap_dir
    movlw   0x80
    movwf   v_ap_angle_pos
    movwf   v_ap_angle_pos_max
    movwf   v_ap_angle_pos_min

    WPARSE_DBG  'd'
w_parse_end:
    return

;
; not the first motion
;
w_parse_cont:

    ;
    ; calc delta angle
    ;
    negf    WREG
    addwf   v_ap_prev_angle,w
    rcall   w_adiff
    movwf   v_ap_delta_angle
    movff   v_ap_angle,v_ap_prev_angle
    btfsc   WREG,7
    negf    WREG
    movwf   v_ap_delta_angle_abs
    addlw   -4
    bnc     w_parse_circle

;
; angle difference is >4
;
w_parse_jump:
    rcall   w_parse_result
    rcall   w_parse_start
    return

;
; delta angle is <4, so we might be circling
;
w_parse_circle:

;
; .01 sec - .2 sec is range of times per circle pulse
;
;
;
;
;
;

    ;
    ; if mag is bigger save max_mag and max_mag_angle
    ;
    movf    v_ap_mag,w          ; current mag
    cpfsgt  v_ap_max_mag        ; bigger than prev mag?
    movff   v_ap_angle,v_ap_max_mag_angle
    cpfsgt  v_ap_max_mag
    movwf   v_ap_max_mag

    ;
    ; moving same direction as before?
    ;
    movf    v_ap_delta_angle,w
    xorwf   v_ap_dir,w
    bn      w_parse_rev

;
; going same direction as circle direction
;
w_parse_same:

    ;
    ; calc current position
    ;
    movf    v_ap_delta_angle_abs,w
    addwf   v_ap_angle_pos,f

    ;
    ; increased max angle?
    ;
    movf    v_ap_angle_pos,w
    cpfslt  v_ap_angle_pos_max
    return                      ; no - do nothing

    ;
    ; biggest angle so far - save it
    ; also save the v_ap_angle corresponding to this angle
    ;
    movwf   v_ap_angle_pos_max
    movff   v_ap_angle,v_ap_max_angle

    ;
    ; Have we moved more than 2 circles?
    ;
    subwf   v_ap_angle_pos_min,w    ; NEGATIVE total movement
    addlw   0x40
    btfsc   STATUS,C
    return                          ; no - do nothing

    ;
    ; moved more than 2 circles - subtract off 1 circle
    ;
    movf    v_ap_angle_pos,w
    addlw   -0x20
    movwf   v_ap_angle_pos
    movwf   v_ap_angle_pos_max
    infsnz  v_ap_cnt,f
    setf    v_ap_cnt            ; clamp to 0xff
    return

;
; going opposite direction from circle direction
;
w_parse_rev:

    ;
    ; calc current position
    ;
    movf    v_ap_delta_angle_abs,w
    subwf   v_ap_angle_pos,f

    ;
    ; keep track of min angle_pos
    ;
    movf    v_ap_angle_pos,w
    cpfslt  v_ap_angle_pos_min
    movwf   v_ap_angle_pos_min

    ;
    ; how far backwards have we moved?
    ;
    subwf   v_ap_angle_pos_max,w
    movwf   v_ap_angle_tmp1
    addlw   -8                  ;   REVERSE THRESHHOLD
    btfss   STATUS,C
    return                      ; < REVERSE THRESHHOLD - do nothing

    ;
    ; we are reversing direction - send result if precvious circle big enough
    ;
    movf    v_ap_cnt,w
    bnz     w_parse_rev_result  ; at least a circle - send result

    movf    v_ap_angle_pos_min,w
    subwf   v_ap_angle_pos_max,w
    addlw   -24                 ;   MIN REV CIRCLE THRESHHOLD
    bnc     w_parse_rev_yes     ; < MIN REV CIRCLE THRESHHOLD - do not send

w_parse_rev_result:
    rcall   w_parse_result

;
; reverse direction of circles
;
w_parse_rev_yes:
    ;
    ; max - min
    ;
    movf    v_ap_angle_pos_min,w
    subwf   v_ap_angle_pos_max,w

    ;
    ; adjust angle at max (becomes angle at what was min)
    ;
    btfss   v_ap_dir,7
    subwf   v_ap_max_angle,f
    btfss   v_ap_dir,7
    addwf   v_ap_max_angle,f

    ;
    ; new max = 0x80 + old (max-min)
    ; new min = 0x80 + 0
    ;
    addlw   0x80
    movwf   v_ap_angle_pos_max
    movlw   0x80
    movwf   v_ap_angle_pos_min

    addwf   v_ap_angle_tmp1,w
    movwf   v_ap_angle_pos
    clrf    v_ap_cnt
    btg     v_ap_dir,7
    return


;
; w_parse_result - send result to motion buffer
; Must not modify any of:
;     v_ap_prev_angle
;     v_ap_angle_pos_max
;     v_ap_angle_pos_min
;
w_parse_result:
    movf    v_ap_angle_pos_min,w
    subwf   v_ap_angle_pos_max,w

    addlw   -24                 ; MIN CIRCLE THRESHHOLD
    bnc     w_parse_result_check    ; < MIN CIRCLE THRESHHOLD - no extra circle

w_parse_result_cnt_loop:
    infsnz  v_ap_cnt,f
    setf    v_ap_cnt        ; clamp to 0xff

    addlw   -32
    bc      w_parse_result_cnt_loop     ; another circle


w_parse_result_check:
    movf    v_ap_cnt,f
    bnz     w_parse_result_circle   ; at least 1 circle

w_parse_result_jerk:
    ;
    ; clamp to 4 directions - 0=up 1=right 2=down 3=left
    ;
    movf    v_ap_max_mag_angle,w
    addlw   4
    rlncf   WREG,w
    andlw   0x30
    movwf   v_ap_result
    bra     w_parse_result_send


w_parse_result_circle:

#if WP_DEBUG3
    DB_BYTE 'A',v_ap_max_angle
    DB_BYTE 'n',v_ap_angle_pos_min
    DB_BYTE 'p',v_ap_angle_pos
    DB_BYTE 'x',v_ap_angle_pos_max
    DB_BYTE '1',WREG
#endif



    ;
    ; calc start direction
    ; w contains max-min-24
    ;
    addlw   24
    andlw   0x1f

    ;
    ; Now w is (max - min) mod 32
    ;

    ;
    ; bias by 24 so that if w is 0-24 average will be between min...max and if
    ; 25-31 then average will be between max...min
    ;
    addlw   -24
    btfss   STATUS,N
    addlw   -0x20
    addlw   24

#if WP_DEBUG3
    DB_BYTE '2',WREG
#endif
    
    ;
    ; Here w is the delta from max to min in range -7 ... 24
    ;

    btfsc   v_ap_dir,7
    negf    WREG

    ; w now contains the signed delta between min and max angle mod 32
#if WP_DEBUG3
    DB_BYTE '3',WREG
#endif

    btfsc   WREG,7
    addlw   1
    bcf     WREG,0
    btfsc   WREG,7
    bsf     WREG,0
    rrncf   WREG,w

    ; w is now half the delta angle from min to max
#if WP_DEBUG3
    DB_BYTE '4',WREG
#endif

    ;
    ; add to the angle at max to get avg of start & end angle
    ;
    addwf   v_ap_max_angle,w

#if WP_DEBUG3
    DB_BYTE '5',WREG
#endif
    ;
    ; round to quadrant
    ;
    addlw   4
    rlncf   WREG,w
    andlw   0x30
    movwf   v_ap_result

#if WP_DEBUG3
    DB_BYTE 'a',WREG
#endif
    ;
    ; clamp cnt to 0x0f and store in result
    ;
    movf    v_ap_cnt,w
    andlw   0xf0
    btfss   STATUS,Z
    setf    v_ap_cnt
    movf    v_ap_cnt,w
    andlw   0x0f
    iorwf   v_ap_result,f

    ;
    ; store cw/ccw
    ;
    btfsc   v_ap_dir,7
    bsf     v_ap_result,6

#if WP_DEBUG3
    DB_BYTE 'r',v_ap_result
    call    kk_outcr
#endif


;
; send result to output buffer
;
w_parse_result_send:

    ;
    ; point to next dst and increment spell len (clamp to 0xff)
    ;
    lfsr    FSR0,RAMBUF_MOTION
    movf    v_spell_len,w
    movwf   FSR0L
    incfsz  WREG,w
    movwf   v_spell_len

    movf    v_ap_result,w
    movwf   INDF0           ; result to buffer

    ;
    ; check for v_ap_result == 0x10 or 0x30
    ;
    movf    v_ap_result,w
    addlw   -0x10
    bz      w_parse_result_wag
    addlw   0x10-0x30
    bz      w_parse_result_wag

    movlw   9
    movwf   v_ap_wag_cnt        ; reset wag count
    
w_parse_result_wag:
    dcfsnz  v_ap_wag_cnt,f
    bsf     b_parse_wag

    WPARSE_DBG  'c'

#if WP_DEBUG2
    COUTL   'r'
    movf    v_ap_result,w
    call    kk_outbyte
    call    kk_outcr
#endif

    return

;
; angle convert - convert a1-a2 to signed angle from -16 to +15
;   input: w=a1-a2
;   output w = signed angle -16 ... 15
;
w_adiff:
    andlw   0x1f
    btfss   WREG,4
    return
    addlw   -0x20
    return

;
; convert angle to octant
;   input:  angle  (0-31) in W
;   output: octant (0-7)  in W
;
w_a2oct:
    addlw   2
    rrncf   WREG,w
    rrncf   WREG,w
    andlw   7
    return

#if 0
;
; convert angle to octant
;   input:  angle  (0-31) in W
;   output: quad   (0-3)  in W
;
w_a2quad:
    addlw   4
    rrncf   WREG,w
    rrncf   WREG,w
    rrncf   WREG,w
    andlw   3
    return
#endif


#if WP_DEBUG
w_parse_dbg:
    movf    FSR0L,w
    andlw   0xfc
    addlw   2
    movwf   FSR0L
    movf    INDF0,w
    call    kk_outbyte
    COUTL   ' '
    movf    v_tmp,w
    call    kk_cout
    COUTL   ' '
    COUTL   'C'
    movf    v_ap_cnt,w
    call    kk_outbyte
    COUTL   ' '
    COUTL   'S'
    movf    v_ap_start_angle,w
    call    kk_outbyte
    COUTL   ' '
    COUTL   'P'
    movf    v_ap_prev_angle,w
    call    kk_outbyte
    COUTL   ' '
    COUTL   'D'
    movf    v_ap_delta_angle,w
    call    kk_outbyte
    COUTL   ' '
    COUTL   'M'
    movf    v_ap_max_mag,w
    call    kk_outbyte
    COUTL   ' '
    COUTL   'A'
    movf    v_ap_max_mag_angle,w
    call    kk_outbyte
    COUTL   ' '
    COUTL   'R'
    movf    v_ap_dir_save,w
    call    kk_outbyte
    COUTL   ' '
    COUTL   'X'
    movf    v_ap_dbg_spx,w
    call    kk_outbyte
    COUTL   ' '
    COUTL   'Y'
    movf    v_ap_dbg_spy,w
    call    kk_outbyte

    COUTL   ' '
    movf    v_ap_srch,w
    call    kk_outbyte
    movf    v_ap_srcl,w
    call    kk_outbyte

    COUTL   ' '
    movf    FSR2H,w
    call    kk_outbyte
    movf    FSR2L,w
    call    kk_outbyte

    COUTL   ' '
    COUTL   ' '
    movf    v_bits1,w
    call    kk_outbyte

    call    kk_outcr

w_parse_dbg_loop:
    call    w_main_run
    movf    vk_tx_cnt,w
    bnz     w_parse_dbg_loop

    return
#endif
    
#endif

#if vaddr>0xff
    error   "Too many variables!"
#endif

;###########################################################################
;################################ DATA #####################################
;###########################################################################




#if DBG_SPELL_MSG_IS_SPELL_NAME
w_spellinfo_puffthemagicdragon:
    db "Puff the magic_dragon"
#endif


#if DBORG
    org     0x3800
#endif
    
#include "wmspells.inc"

#include "wmspells2.inc"


#if DBORG
    org     0x3c00
#endif

#if !DBG_SPELL_MSG_IS_SPELL_NAME
#include "wspell_info.inc"
#include "wspell_str.inc"
#endif

#if !DBG_SPELL_MSG_IS_SPELL_NAME
w_spellinfo_wandy:
    db  "You cast wandy!  Next:  grecian ____ a living.    \0"

w_spellinfo_urn:
    db  "You cast urn/earn!  Next:  finger ____ of darkness.    \0"

w_spellinfo_prince:
    db  "You cast prints/prince!  Next:  hole in ____ the match.    \0"

w_spellinfo_one:
    db  "You cast one/won!  Next:  samurai ____ like an eagle.    \0"

#if 0
    ;db "You cast wandy!  Next:  grecian ____ a living.    \0"
    ;db "You cast urn/earn!  Next:  pad ____ ness.    \0"
    ;db "You cast lock/loch!  Next:  jewler's ____ de loop.    \0"
    ;db "You cast loupe/loop!  Next:  prickly ____ of aces.    \0"
    ;db "You cast pear/pair!  Next:  finger ____ of darkness.    \0"
    ;db "You cast prints/prince!  Next:  hole in ____ the match.    \0"
    ;db "You cast one/won!  Next:  samurai ____ like an eagle.    \0"
w_spellinfo_sword:
    db  "You cast sword/soared! Well done! Go make potions at Tsakopoulos Library Galleria.\0"
#endif
#endif

#if !DBG_SPELL_MSG_IS_SPELL_NAME
w_spellinfo_fast:
    db  "You cast fast!  Cast ;fast; again for even faster text.  Also try the ;slow; spell.\0"

w_spellinfo_slow:
    db  "You cast slow!  Cast ;slow; again for even slower text.  Also try the ;fast; spell.\0"

w_spellinfo_yay1:
w_spellinfo_yay     equ     w_spellinfo_yay1+1
    db  0x3c, 0x0, HIGH w_str_yayA, LOW w_str_yayA
    db  0xff, 0x80, HIGH w_str_yayB, LOW w_str_yayB
w_spellinfo_ankle1:
w_spellinfo_ankle       equ w_spellinfo_ankle1+1
    db  0x3c, 0x0, HIGH w_str_ankleA, LOW w_str_ankleA
    db  0xff, 0x80, HIGH w_str_ankleB, LOW w_str_ankleB
w_spellinfo_ask1:
w_spellinfo_ask     equ w_spellinfo_ask1+1
    db  0x3c, 0x0, HIGH w_str_askA, LOW w_str_askA
    db  0xff, 0x80, HIGH w_str_askB, LOW w_str_askB
w_spellinfo_dad1:
w_spellinfo_dad     equ w_spellinfo_dad1+1
    db  0x3c, 0x0, HIGH w_str_dadA, LOW w_str_dadA
    db  0xff, 0x80, HIGH w_str_dadB, LOW w_str_dadB
w_spellinfo_jaw1:
w_spellinfo_jaw     equ w_spellinfo_jaw1+1
    db  0x3c, 0x0, HIGH w_str_jawA, LOW w_str_jawA
    db  0xff, 0x80, HIGH w_str_jawB, LOW w_str_jawB
w_spellinfo_cheese1:
w_spellinfo_cheese      equ w_spellinfo_cheese1+1
    db  0x3c, 0x0, HIGH w_str_cheeseA, LOW w_str_cheeseA
    db  0x48, 0x00, HIGH w_str_cheeseB, LOW w_str_cheeseB
    db  0xff, 0x80, HIGH w_str_cheeseC, LOW w_str_cheeseC
w_spellinfo_hush1:
w_spellinfo_hush        equ w_spellinfo_hush1+1
    db  0x3c, 0x0, HIGH w_str_hushA, LOW w_str_hushA
    db  0xff, 0x80, HIGH w_str_hushB, LOW w_str_hushB
w_spellinfo_hello1:
w_spellinfo_hello       equ w_spellinfo_hello1+1
    db  0x3c, 0x0, HIGH w_str_helloA, LOW w_str_helloA
    db  0xff, 0x80, HIGH w_str_helloB, LOW w_str_helloB
w_spellinfo_poem1:
w_spellinfo_poem        equ w_spellinfo_poem1+1
    db  0x3c, 0x0, HIGH w_str_poemA, LOW w_str_poemA
    db  0xff, 0x80, HIGH w_str_poemB, LOW w_str_poemB
w_spellinfo_noel1:
w_spellinfo_noel        equ w_spellinfo_noel1+1
    db  0x3c, 0x0, HIGH w_str_noelA, LOW w_str_noelA
    db  0xff, 0x80, HIGH w_str_noelB, LOW w_str_noelB
w_spellinfo_chanters1:
w_spellinfo_chanters        equ w_spellinfo_chanters1+1
    db  0x3c, 0x0, HIGH w_str_chantersA, LOW w_str_chantersA
    db  0xff, 0x80, HIGH w_str_chantersB, LOW w_str_chantersB
w_spellinfo_draconis1:
w_spellinfo_draconis        equ w_spellinfo_draconis1+1
    db  0x3c, 0x0, HIGH w_str_draconisA, LOW w_str_draconisA
    db  0xff, 0x80, HIGH w_str_draconisB, LOW w_str_draconisB
w_spellinfo_gorilla1:
w_spellinfo_gorilla     equ w_spellinfo_gorilla1+1
    db  0x3c, 0x0, HIGH w_str_gorillaA, LOW w_str_gorillaA
    db  0xff, 0x80, HIGH w_str_gorillaB, LOW w_str_gorillaB


w_spellinfo_shave1:
w_spellinfo_shave       equ w_spellinfo_shave1+1
    db  0x3c, 0x0, HIGH w_str_shaveA, LOW w_str_shaveA
    db  0xff, 0x80, HIGH w_str_shaveB, LOW w_str_shaveB
w_spellinfo_row1:
w_spellinfo_row     equ w_spellinfo_row1+1
    db  0x3c, 0x0, HIGH w_str_rowA, LOW w_str_rowA
    db  0xff, 0x80, HIGH w_str_rowB, LOW w_str_rowB
w_spellinfo_jingle1:
w_spellinfo_jingle      equ w_spellinfo_jingle1+1
    db  0x3c, 0x0, HIGH w_str_jingleA, LOW w_str_jingleA
    db  0xff, 0x80, HIGH w_str_jingleB, LOW w_str_jingleB
w_spellinfo_mary1:
w_spellinfo_mary        equ w_spellinfo_mary1+1
    db  0x3c, 0x0, HIGH w_str_maryA, LOW w_str_maryA
    db  0xff, 0x80, HIGH w_str_maryB, LOW w_str_maryB
w_spellinfo_brady1:
w_spellinfo_brady       equ w_spellinfo_brady1+1
    db  0x3c, 0x0, HIGH w_str_bradyA, LOW w_str_bradyA
    db  0xff, 0x80, HIGH w_str_bradyB, LOW w_str_bradyB
w_spellinfo_gilligan1:
w_spellinfo_gilligan        equ w_spellinfo_gilligan1+1
    db  0x3c, 0x0, HIGH w_str_gilliganA, LOW w_str_gilliganA
    db  0xff, 0x80, HIGH w_str_gilliganB, LOW w_str_gilliganB
w_spellinfo_flint1:
w_spellinfo_flint       equ w_spellinfo_flint1+1
    db  0x3c, 0x0, HIGH w_str_flintA, LOW w_str_flintA
    db  0xff, 0x80, HIGH w_str_flintB, LOW w_str_flintB
w_spellinfo_twinkle1:
w_spellinfo_twinkle     equ w_spellinfo_twinkle1+1
    db  0x3c, 0x0, HIGH w_str_twinkleA, LOW w_str_twinkleA
    db  0xff, 0x80, HIGH w_str_twinkleB, LOW w_str_twinkleB
w_spellinfo_birthday1:
w_spellinfo_birthday        equ w_spellinfo_birthday1+1
    db  0x3c, 0x0, HIGH w_str_birthdayA, LOW w_str_birthdayA
    db  0xff, 0x80, HIGH w_str_birthdayB, LOW w_str_birthdayB


w_str_yayA:
    db  "you cast YAY!  Hurray for you!\0"
w_str_ankleA:
    db  "you cast ANKLE!  \0"
w_str_askA:
    db  "you cast ASK!  \0"
w_str_dadA:
    db  "you cast DAD!  \0"
w_str_jawA:
    db  "you cast JAW!  \0"
w_str_cheeseA:
    db  "you cast CHEESE!  \0"
w_str_hushA:
    db  "you cast HUSH!  Ssshhh!!!\0"
w_str_helloA:
    db  "you cast HELLO!  Try the 'fast' spell too!\0"
w_str_poemA:
    db  "you cast POEM!  \0"
w_str_noelA:
    db  "you cast NOEL!  Merry Christmas!\0"
w_str_chantersA:
    db  "you cast CHANTERS!  That is a password, not a spell!\0"
w_str_draconisA:
    db  "you cast DRACONIS!  You're scaring me!\0"
w_str_gorillaA:
    db  "you cast Gorilla!  \0"


w_str_yayB:
    db  "you cast YAY!  Thanks for playing 'Hogwarts the draconian prophecy!'  Tr casting 'Hello'\0"
w_str_ankleB:
    db  "you cast ANKLE!  Don't trip!\0"
w_str_askB:
    db  "you cast ASK!  Ask and you shall receive...       NOT!     \0"
w_str_dadB:
    db  "you cast DAD!  My father is Acorn.  http://www.rawbw.com/[acorn\0"
w_str_jawB:
    db  "you cast JAW!  We're going to need a bigger boat!\0"
w_str_cheeseB:
    db  "you cast CHEESE!  After Sunday check out the wand website at http://www.rawbw.com/[acorn/wand\0"
w_str_hushB:
    db  "you cast HUSH!  No need to be so quiet now that the game is over!\0"
w_str_helloB:
    db  "you cast HELLO!  Try tapping songs.  Twinkle twinkle.  Row your boat.  Mary had a little lamb.  Shave and a haircut.  Jingle Bells.  Brady Bunch.  Flintstones.  Gilligan.  Happy Birthday.    \0"
w_str_poemB:
    db  "you cast POEM!  Suzie was a chemist.  Suzie is no more.  For what she thought was h2o was h2so4!\0"
w_str_noelB:
    db  "you cast NOEL!  Happy hanukkah!\0"
w_str_chantersB:
    db  "you cast CHANTERS!  That is you portrait password!\0"
w_str_draconisB:
    db  "you cast DRACONIS!  You're scaring me!\0"
w_str_gorillaB:
    db  "you cast Gorilla!  \0"


w_str_cheeseC:
    db  "you cast CHEESE!  Check out the wand website at http://www.rawbw.com/[acorn/wand\0"


w_str_rowA:
    db  "you cast 'Row row row your boat'!  \0"
w_str_shaveA:
    db  "you cast 'Shave and a Haircut'!  \0"
w_str_jingleA:
    db  "you cast Jingle Bells!  \0"
w_str_maryA:
    db  "you cast 'Mary had a little lamb'!  \0"
w_str_bradyA:
    db  "you cast 'The Brady Bunch'!  \0"
w_str_gilliganA:
    db  "you cast 'Gilligan'!  \0"
w_str_flintA:
    db  "you cast 'The Flintstones'!  \0"
w_str_twinkleA:
    db  "you cast 'Twinkle'!   \0"
w_str_birthdayA:
    db  "you cast 'Happy Birthday'!\0"

w_str_rowB:
    db  "you cast 'Row row row your boat'!  Check out the website http://www.gotohogwarts.com\0"
w_str_shaveB:
    db  "you cast 'Shave and a Haircut'!  You know what happens when you can't stop laughing?   One of these days, you're gonna die laughing.\0"
w_str_jingleB:
    db  "you cast Jingle Bells!  Try casting 'Noel'\0"
w_str_maryB:
    db  "you cast 'Mary had a little lamb'!  ry had a little lamb.  her father shot it dead.  Now Mary takes her lamb to school between two hunks of bread.\0"
w_str_bradyB:
    db  "you cast 'The Brady Bunch'!  Sorry, no more than 6 people per team.\0"
w_str_gilliganB:
    db  "you cast 'Gilligan'!  Don't miss the boat!\0"
w_str_flintB:
    db  "you cast 'The Flintstones'!  Who would win in a fight: astronauts or cavemen? Discuss.\0"
w_str_twinkleB:
    db  "you cast 'Twinkle'!  Happy 40th anniversary, Star Trek!   \0"
w_str_birthdayB:
    db  "you cast 'Happy Birthday'!  Hogwats and the Draconian Prophecy.  September 10-11 2006.  http://www.gotohogwarts.com\0"

#endif

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

    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:21:41 PDT 2007