; skeleton of a PIC 16C84 electronic speed control program
; see http://www.math.niu.edu/~behr/RC/speed-ctl.html for details
;
; WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! 
;   This code is NOT complete and lacks many safety features.
;   It is a "proof of concept", provided for informational
;   purposes only. Do NOT use "as-is" without adding missing
;   pulse shutdown, de-glitching, and startup code matching
;   parameters to the input signal.
;
; remember that GOTO, CALL, RETLW, RETURN all use 2 cycles
; remember that conditionals use 2 cycles _if true_
; port change flag cannot be cleared unless the port is read
;    or written to

flags     equ      0x0F    ; our flags
pulse     equ      0x12    ; measured pulse
out_on    equ      0x13    ; processed output high
out_off   equ      0x14    ; processed output low
cnt1      equ      0x17    ; counter for delays

#define   cmpfudge .3      ; compute takes this many
#define   maxcnt   .18     ; max value of the main loop counter
#define   downshift  .38   ; pulse value adjustment
#define   maxhalf  .9      ; exact half of the main loop counter
#define   mincmp   .7      ; just under half of the max counter
#define   output   porta,0
#define   led      porta,1 ; indicator
#define   testout  porta,3 ; test output
#define   input    portb,4
#define   pulse_ready flags,1 ; we just had a new pulse
#define   stay_off    flags,2 ; don't turn output on
#define   stay_on     flags,3 ; don't turn output off
; --------------------------------------------
; normal entry point
  org    0x00
  goto   main

; --------------------------------------------
; initialize
init
  bcf     intcon,gie
  bcf     intcon,t0ie
  movf    portb,f         ; have to read port before
  bcf     intcon,rbif     ; clearing the rbint flag
  bcf     intcon,rbie
  clrf    porta
  clrf    portb
  bsf     status,rp0       ; select bank 1
  movlw   0x0              ; A0-A4 out
  movwf   porta
  movlw   0x10             ; RB4 in
  movwf   portb
  bcf     option,t0cs      ; internal tmr0 clock
  bcf     option,ps0       ; set prescaler to 1:8
  bsf     option,ps1
  bcf     option,ps2
  bcf     option,psa
  bcf     status,rp0       ; select bank 0
  clrf    flags
  clrf    pulse
  movlw   maxcnt
  movwf   out_off
  clrf    out_on
  retlw   0

; --------------------------------------------
; compute the output length from pulse [N*14 + 1 cycles w/call]
; can be at most (maxhalf-isrfudge)*(main loop cycles) + 1
compute
  btfsc   pulse_ready
  goto    do_compute
  decf    cnt1         ; wasted only one loop iteration
  nop                  ; nothing to do!
  nop
  nop
  nop
  nop
  nop
  nop
  nop
  return               ; [15 with call and return]

do_compute             ; [5] up to here
  bcf     pulse_ready  ; so it won't go off again
; decrement cnt1 by cmpfudge, that's how many iteration
; periods we will waste here
  movlw   cmpfudge
  subwf   cnt1,f
  movf    pulse,w      ; between 125 and 250 or so
  movwf   out_on
  bcf     status,c
  rrf     out_on,f
  bcf     status,c
  rrf     out_on,f     ; between 32 and 64
; adjust between 0 and 32
  movlw   downshift
  subwf   out_on,w
  btfss   status,c
  movlw   .0
  movwf   out_on
  sublw   maxcnt       ; [20] up to here
  btfss   status,c
  movlw   .0
  movwf   out_off
; set stay_on and stay_off flags
; maxcnt in _both_ timers if either flag set
  bcf     stay_off
  bcf     stay_on
; put in a check for tmr0 overflow here
  movf    out_on,f           ; [26]
  btfss   status,z
  goto    check_off
do_off
  bsf     stay_off
  movlw   maxcnt
  movwf   out_on
  movwf   out_off
  nop
  nop
  nop
  nop
  nop
  nop
  nop
  nop
  nop
  return                     ; [43]
check_off
  movf    out_off,f
  btfss   status,z
  goto    cmp_out
  bsf     stay_on
  movlw   maxcnt
  movwf   out_on
  movwf   out_off
  nop
  nop
  nop
  nop
  nop
  return                     ; [43]

cmp_out
  nop
  nop
  nop
  nop
  nop
  nop
  nop
  nop
  return

; --------------------------------------------
; main routine
main
  call    init
  movlw   maxcnt
  movwf   out_off
  bsf     stay_off

work
  decfsz  cnt1,f
  goto    normal

; handle transition here
  btfsc   output
  goto    turnoff
turnon
  btfss   stay_off
  bsf     output
  movf    out_on,w
  goto    trans_done
turnoff
  btfss   stay_on
  bcf     output
  movf    out_off,w
  nop
trans_done
  movwf   cnt1
  nop
  nop
  goto    work            ; [14]

normal
  btfss   intcon,rbif
  goto    no_edge
; input edge
  btfsc   input
  goto    wenthigh
wentlow                   ; this is the critical branch
  movf    tmr0,w          ; could we clear this elsewhere
  movwf   pulse           ; and save a cycle or two?
  movf    portb,f         ; have to read port before
  bcf     intcon,rbif     ; clearing the rbint flag
  bsf     pulse_ready
  goto    work            ; [14]
wenthigh
  clrf    tmr0
; clear the rising rbif here
  movf    portb,f         ; have to read port before
  bcf     intcon,rbif     ; clearing the rbint flag
; some delay here?
  nop
  goto    work            ; [14]

no_edge
; if in long and pulse ready, call compute
  movlw   mincmp          ; do we have enough time?
  subwf   cnt1,w
  btfsc   status,c
  call    compute         ; should use N*14 + 1
  nop
  nop
  goto    work            ; [14]


end

