; PIC 16F688 code for running a PWM board
;
; ### license
;
; VERSION 2: to stay within the sub-cycle timing requirements (see below),
;   this version unrolls some loops and combines PORT pin management.
;
; NOTE: each cycle is 4.1 ms, or 8192 instructions. This cycle is broken
;   down into 256 sub-cycles to manage the required duty per cycle. Thus,
;   each sub-cycle is 16 µs, or 32 instructions.
;
; NOTE: all variables are placed into Bank 2, where we have lots of space
;   and can also access TMR0, INTCON, PORTA, and PORTC.
;
; If the symbol "SDA" is defined, it specifies a PORTC pin to monitor. If
; that pin goes LOW, then the PWM is halted (with all outputs OFF), and
; the run_PWM function returns.
;

        list p = 16f688
        include p16f688.inc
        include ../include/handy.inc


        ; The target channel values go into the shared bank.
.gprnobnk udata
; Target PWM ratios, set externally.
T_CHAN1 res     1               ; aka: 1a on PWMBoard
T_CHAN2 res     1               ; aka: 1b
T_CHAN3 res     1               ; aka: 1c
T_CHAN4 res     1               ; aka: 2a
T_CHAN5 res     1               ; aka: 2b
T_CHAN6 res     1               ; aka: 2c
T_CHAN7 res     1               ; aka: 3a
T_CHAN8 res     1               ; aka: 3b
T_CHAN9 res     1               ; aka: 3c

; External code is allowed to adjust the T_CHAN* values. Publish the addresses.
 global T_CHAN1, T_CHAN2, T_CHAN3, T_CHAN4, T_CHAN5
 global T_CHAN6, T_CHAN7, T_CHAN8, T_CHAN9

; The PORT bits for each channel (RC4/TX, RC5/RX, RA3 are unused)
C1BIT_C equ     3
C2BIT_C equ     2
C3BIT_A equ     4
C4BIT_A equ     5
C5BIT_A equ     0
C6BIT_A equ     1
C7BIT_A equ     2
C8BIT_C equ     0
C9BIT_C equ     1

; ALL other variables go into Bank 2.
.gpr2   udata

; TMR0 counts each sub-cycle, until it rolls over at the 4.1 ms marker to
; start a new cycle of the display freqeuency. For the first N steps of a
; display cycle, we keep mask bytes that will DISABLE the output pins of
; PORTA and PORTC for the given sub-cycle position in the overall display
; cycle.
;
; The mask bytes below correspond to each sub-cycle point.
;
; Duty cycles [0,16] occur at each level. Then, we switch to every TWO
; possible duty cycles values between [16, 32].
SET_A_0         res     1       ; which PORTA pins to ENABLE at cycle start
SET_C_0         res     1       ; which PORTC pins to ENABLE at cycle start

MASK_A_1        res     1       ; mask to DISABLE PORTA/C pins at sub-cycle 1
MASK_C_1        res     1       ;   leading edge

MASK_A_2        res     1       ; mask for leading edge of sub-cycle 2
MASK_C_2        res     1

MASK_A_3        res     1
MASK_C_3        res     1
MASK_A_4        res     1
MASK_C_4        res     1
MASK_A_5        res     1
MASK_C_5        res     1
MASK_A_6        res     1
MASK_C_6        res     1
MASK_A_7        res     1
MASK_C_7        res     1
MASK_A_8        res     1
MASK_C_8        res     1
MASK_A_9        res     1
MASK_C_9        res     1
MASK_A_10       res     1
MASK_C_10       res     1
MASK_A_11       res     1
MASK_C_11       res     1
MASK_A_12       res     1
MASK_C_12       res     1
MASK_A_13       res     1
MASK_C_13       res     1
MASK_A_14       res     1
MASK_C_14       res     1
MASK_A_15       res     1
MASK_C_15       res     1
MASK_A_16       res     1
MASK_C_16       res     1

; Switch to every TWO duty cycle values.
MASK_A_18       res     1
MASK_C_18       res     1
MASK_A_20       res     1
MASK_C_20       res     1
MASK_A_22       res     1
MASK_C_22       res     1
MASK_A_24       res     1
MASK_C_24       res     1
MASK_A_26       res     1
MASK_C_26       res     1
MASK_A_28       res     1
MASK_C_28       res     1
MASK_A_30       res     1
MASK_C_30       res     1
MASK_A_32       res     1
MASK_C_32       res     1

; Computed mask values. Used for duty cycle [35,255].
MASK_A          res     1
MASK_C          res     1

; Working/temporary register(s)
LEVEL           res     1
T_LEVEL         res     1

; Current PWM ratio values, for each channel
C_CHAN1         res     1
C_CHAN2         res     1
C_CHAN3         res     1
C_CHAN4         res     1
C_CHAN5         res     1
C_CHAN6         res     1
C_CHAN7         res     1
C_CHAN8         res     1
C_CHAN9         res     1

; Next PWM ratio values, for each channel
N_CHAN1         res     1
N_CHAN2         res     1
N_CHAN3         res     1
N_CHAN4         res     1
N_CHAN5         res     1
N_CHAN6         res     1
N_CHAN7         res     1
N_CHAN8         res     1
N_CHAN9         res     1

; ====== End of Bank 2 registers.


; Macros to wait for Bit 0 or 1, to become HIGH or LOW
;
; EXPECT: Bank 0 or Bank 2 (for TMR0)
wait_TMR0_0_HIGH:       macro
 ifndef SDA
        btfss   TMR0, 0
        goto    $ - 1
 else
        btfss   PORTC, SDA
        goto    I2C_started
        btfss   TMR0, 0
        goto    $ - 3
 endif
        endm
wait_TMR0_0_LOW:        macro
 ifndef SDA
        btfsc   TMR0, 0
        goto    $ - 1
 else
        btfss   PORTC, SDA
        goto    I2C_started
        btfsc   TMR0, 0
        goto    $ - 3
 endif
        endm
wait_TMR0_1_HIGH:       macro
 ifndef SDA
        btfss   TMR0, 1
        goto    $ - 1
 else
        btfss   PORTC, SDA
        goto    I2C_started
        btfss   TMR0, 1
        goto    $ - 3
 endif
        endm
wait_TMR0_1_LOW:        macro
 ifndef SDA
        btfsc   TMR0, 1
        goto    $ - 1
 else
        btfss   PORTC, SDA
        goto    I2C_started
        btfsc   TMR0, 1
        goto    $ - 3
 endif
        endm


; Apply masks to PORTA/C in order to DISABLE certain pins
;
; Note: 4 instruction cycles.
;
; EXPECT: Bank 2.
apply_mask:     macro MASK_A, MASK_C
        movfw   MASK_A
        andwf   PORTA, F
        movfw   MASK_C
        andwf   PORTC, F
        endm

; Prepare a bit in MASK, to DISABLE a pin when CHANNEL says it should be
; disabled before/at the value in register LEVEL.
;
; Note: 3 instruction cycles.
;
; EXPECT: Bank 2.
prepare_mask_bit:  macro CHANNEL, MASK
        movfw   CHANNEL
        subwf   LEVEL, W        ; W = LEVEL - CHANNEL
        ; C = 0: CHANNEL <= LEVEL ... disable this pin at this LEVEL
        ; C = 1: CHANNEL > LEVEL ... do not mask/disable this pin (yet)
        rlf     MASK, F
        endm

; Run the next wait/mask sequence.
;
; Note: 49 minimum instruction cycles, plus a wait loop.
;       with I2C: 51 minimum cycles
;
; EXPECT: Bank 2.
perform_step:   macro DUTY_LEVEL
        movlw   DUTY_LEVEL
        call    next_step
 ifdef SDA
        btfss   PORTC, SDA
        ; SDA dropped low -- I2C START has occurred.
        goto    I2C_started
 endif
        endm

; Compute PORTA and PORTC masks for when Timer0 reaches T_LEVEL, relative
; to the channel levels stored in N_CHAN[1-9]. Store the results into
; $MASK_A and $MASK_C.
;
; Note: 34 + 6 instruction cycles.
;
; EXPECT: Bank 2.
compute_mask:   macro T_LEVEL, LEVEL_MASK_A, LEVEL_MASK_C
        movlw   T_LEVEL
        call    next_masks
        movwf   LEVEL_MASK_A
        movfw   MASK_C
        movwf   LEVEL_MASK_C
        endm

; Move a channel towards its target value.
;
; Note: 6 + 20 instruction cycles
;
; EXPECT: Bank 2.
bump_channel:   macro CUR_CHAN, NEW_CHAN, TARGET
        copy_reg TARGET, T_LEVEL
        movfw   CUR_CHAN
        call    adjust_channel
        movwf   NEW_CHAN
        endm

copy_reg:       macro REG_FROM, REG_TO
        movfw   REG_FROM
        movwf   REG_TO
        endm


pwm:    code                    ; relocatable

; main entry point: start cycling the outputs
;
; INPUTS: none
; ASSUME: Bank 0
; EXIT:   Bank 2
;
        global  run_PWM         ; Publish this entrypoint
run_PWM:

        ; Disable comparators. We're going to use all RA* as OUTPUTS.
        movlw   0x07
        movwf   CMCON0

        ; Latch all pins to OFF, before we define them as OUTPUTS.
        clrf    PORTA
        movlw   0x30
        andwf   PORTC, F

        bank1s

        ; With an 8 MHz clock, an instruction cycle is 0.5 µs. Target
        ; operation is clocking the PWM at about 200 Hz frequency, or
        ; about 5000 µs per cycle, or 10k instructions.
        ;
        ; Timer 0 wraps at 256 instructions. Setting the prescaler to 1:32
        ; will wrap at 8192 instructions, or 4.1 ms, or 244 Hz.
        movlw   0x84            ; pull-ups disabled, T0CS to internal
                                ;   instruction clock (timer mode), prescaler
                                ;   assigned to Timer 0, rate set at 1:32.
        movwf   OPTION_REG

        ; All pins are digital OUTPUTS, so clear all AN* selectors
        clrf    ANSEL

        ; All RA* pins (but RA3) are OUTPUTS.
        clrf    TRISA

        ; Mask off bits in TRISC to set RC0/1/2/3 as OUTPUTS.
        movlw   0x30
        andwf   TRISC, F

        ; NOTE: Bank 2, to access our various masks (and a few special regs)
        bank2

begin_cycle:
        ; Latch some specific outputs to ON.
        ;
        ; NOTE: enable these before we start TMR0 operation, to guarantee
        ;   they are enabled for at least one full sub-cycle (the human eye
        ;   is very sensitive to differences at this low duty cycle, so we
        ;   need to be as precise as possible).
        movfw   SET_A_0
        iorwf   PORTA, F
        movfw   SET_C_0
        iorwf   PORTC, F

        ; Ensure Timer 0 is reset
        clrf    TMR0

        ; NOTE: from here on, we must alter pin outputs within 32 instruction
        ;   periods for each sub-cyle.

        ; Wait for TMR0, bit 0, to become HIGH (ie. TMR0 == 1)
        wait_TMR0_0_HIGH

        ; Turn off appropriate PORTA/PORTC pins
        apply_mask MASK_A_1, MASK_C_1

        ; Wait for TMR0.0 to become LOW (ie. TMR0 == 2), then mask the pins
        wait_TMR0_0_LOW
        apply_mask MASK_A_2, MASK_C_2

        ; Additional waits and masking
        wait_TMR0_0_HIGH
        apply_mask MASK_A_3, MASK_C_3
        wait_TMR0_0_LOW
        apply_mask MASK_A_4, MASK_C_4
        wait_TMR0_0_HIGH
        apply_mask MASK_A_5, MASK_C_5
        wait_TMR0_0_LOW
        apply_mask MASK_A_6, MASK_C_6
        wait_TMR0_0_HIGH
        apply_mask MASK_A_7, MASK_C_7
        wait_TMR0_0_LOW
        apply_mask MASK_A_8, MASK_C_8
        wait_TMR0_0_HIGH
        apply_mask MASK_A_9, MASK_C_9
        wait_TMR0_0_LOW
        apply_mask MASK_A_10, MASK_C_10
        wait_TMR0_0_HIGH
        apply_mask MASK_A_11, MASK_C_11
        wait_TMR0_0_LOW
        apply_mask MASK_A_12, MASK_C_12
        wait_TMR0_0_HIGH
        apply_mask MASK_A_13, MASK_C_13
        wait_TMR0_0_LOW
        apply_mask MASK_A_14, MASK_C_14
        wait_TMR0_0_HIGH
        apply_mask MASK_A_15, MASK_C_15
        wait_TMR0_0_LOW
        apply_mask MASK_A_16, MASK_C_16

        ; TMR == 16

        ; NOTE: from here on, we must alter pin outputs within 64 instruction
        ;   periods for each sub-cyle.

        ; We will wait on Bit 1 changes, or every other duty cycle value.
        wait_TMR0_1_HIGH
        apply_mask MASK_A_18, MASK_C_18
        wait_TMR0_1_LOW
        apply_mask MASK_A_20, MASK_C_20
        wait_TMR0_1_HIGH
        apply_mask MASK_A_22, MASK_C_22
        wait_TMR0_1_LOW
        apply_mask MASK_A_24, MASK_C_24
        wait_TMR0_1_HIGH
        apply_mask MASK_A_26, MASK_C_26
        wait_TMR0_1_LOW
        apply_mask MASK_A_28, MASK_C_28
        wait_TMR0_1_HIGH
        apply_mask MASK_A_30, MASK_C_30
        wait_TMR0_1_LOW
        apply_mask MASK_A_32, MASK_C_32

        ; TMR == 32

        ; NOTE: from here on, we increment the duty cycle by a minimum of
        ;   (3) sub-cycles, so we have 96 instructions (or more) to compute
        ;   mask values and alter the pin outputs.

        ; perform_step requires 49 instructions (plus the busy-wait loop) to
        ; construct the PORTA/C masks, wait, then to apply those masks.

        ; 47 instructions available (96-49)
        perform_step 0x23
        ; 47 instructions available (96-49)
        perform_step 0x26
        ; 47 instructions available (96-49)
        perform_step 0x29
        ; 47 instructions available (96-49)
        perform_step 0x2c
        ; 47 instructions available (96-49)
        perform_step 0x2f
        ; 47 instructions available (96-49)
        perform_step 0x32
        ; 79 instructions available (128-49)
        perform_step 0x36
        ; 79 instructions available (128-49)
        perform_step 0x3a
        ; 79 instructions available (128-49)
        perform_step 0x3e
        ; 79 instructions available (128-49)
        perform_step 0x42
        ; 111 instructions available (160-49)
        perform_step 0x47
        ; 111 instructions available (160-49)
        perform_step 0x4c
        ; 111 instructions available (160-49)
        perform_step 0x51
        ; 143 instructions available (192-49)
        perform_step 0x57
        ; 143 instructions available (192-49)
        perform_step 0x5d
        ; 143 instructions available (192-49)
        perform_step 0x63
        ; 175 instructions available (224-49)
        perform_step 0x6a
        ; 175 instructions available (224-49)
        perform_step 0x71
        ; 207 instructions available (256-49)
        perform_step 0x79
        ; 207 instructions available (256-49)
        perform_step 0x81
        ; 239 instructions available (288-49)
        perform_step 0x8a
        ; 239 instructions available (288-49)
        perform_step 0x93
        ; 271 instructions available (320-49)
        perform_step 0x9d
        ; 271 instructions available (320-49)
        perform_step 0xa7
        ; 303 instructions available (352-49)
        perform_step 0xb2
        ; 335 instructions available (384-49)
        perform_step 0xbe
        ; 335 instructions available (384-49)

        ; 9 * 26 == 234 instructions
        bump_channel C_CHAN1, N_CHAN1, T_CHAN1
        bump_channel C_CHAN2, N_CHAN2, T_CHAN2
        bump_channel C_CHAN3, N_CHAN3, T_CHAN3
        bump_channel C_CHAN4, N_CHAN4, T_CHAN4
        bump_channel C_CHAN5, N_CHAN5, T_CHAN5
        bump_channel C_CHAN6, N_CHAN6, T_CHAN6
        bump_channel C_CHAN7, N_CHAN7, T_CHAN7
        bump_channel C_CHAN8, N_CHAN8, T_CHAN8
        bump_channel C_CHAN9, N_CHAN9, T_CHAN9

        perform_step 0xca
        ; 367 instructions available (416-49)

        ; 5 * 40 == 200 instructions
        compute_mask 0x01, MASK_A_1, MASK_C_1
        compute_mask 0x02, MASK_A_2, MASK_C_2
        compute_mask 0x03, MASK_A_3, MASK_C_3
        compute_mask 0x04, MASK_A_4, MASK_C_4
        compute_mask 0x05, MASK_A_5, MASK_C_5

        perform_step 0xd7
        ; 399 instructions available (448-49)

        ; 9 * 40 == 360 instructions
        compute_mask 0x06, MASK_A_6, MASK_C_6
        compute_mask 0x07, MASK_A_7, MASK_C_7
        compute_mask 0x08, MASK_A_8, MASK_C_8
        compute_mask 0x09, MASK_A_9, MASK_C_9
        compute_mask 0x0a, MASK_A_10, MASK_C_10
        compute_mask 0x0b, MASK_A_11, MASK_C_11
        compute_mask 0x0c, MASK_A_12, MASK_C_12
        compute_mask 0x0d, MASK_A_13, MASK_C_13
        compute_mask 0x0e, MASK_A_14, MASK_C_14

        perform_step 0xe5
        ; 431 instructions available (480-49)

        ; 10 * 40 == 400 instructions
        compute_mask 0x0f, MASK_A_15, MASK_C_15
        compute_mask 0x10, MASK_A_16, MASK_C_16
        compute_mask 0x12, MASK_A_18, MASK_C_18
        compute_mask 0x14, MASK_A_20, MASK_C_20
        compute_mask 0x16, MASK_A_22, MASK_C_22
        compute_mask 0x18, MASK_A_24, MASK_C_24
        compute_mask 0x1a, MASK_A_26, MASK_C_26
        compute_mask 0x1c, MASK_A_28, MASK_C_28
        compute_mask 0x1e, MASK_A_30, MASK_C_30
        compute_mask 0x20, MASK_A_32, MASK_C_32

        perform_step 0xf4

        ; TMR0 == 244.
        ; All channels will remain in their current state until TMR0 rollover.

        ; ### adjust channel values towards targets. adjust masks.
        ; ### about 384 instructions (12 remaining TMR0 values * 32).

        ; Copy new channel values into the current values, for the next
        ; round of PWM.
        ;
        ; Note: 9 * 2 instruction cycles.
        copy_reg        N_CHAN1, C_CHAN1
        copy_reg        N_CHAN2, C_CHAN2
        copy_reg        N_CHAN3, C_CHAN3
        copy_reg        N_CHAN4, C_CHAN4
        copy_reg        N_CHAN5, C_CHAN5
        copy_reg        N_CHAN6, C_CHAN6
        copy_reg        N_CHAN7, C_CHAN7
        copy_reg        N_CHAN8, C_CHAN8
        copy_reg        N_CHAN9, C_CHAN9

        ; Wait for the overflow, and restart the display cycle.
wait_cycle_end:
 ifdef SDA
        btfss   PORTC, SDA
        ; SDA dropped low -- I2C START has occurred.
        goto    I2C_started
 endif
        btfss   INTCON, T0IF
        goto    wait_cycle_end

        ; Clear the Timer 0 overflow notification
        bcf     INTCON, T0IF

        ; An I2C interrupt could modify the CHAN* registers. If something
        ; will be turned ON, then keep running the PWM.
        movfw   SET_A_0
        iorwf   SET_C_0, W
        bnz     begin_cycle

        ; Nothing is ON, so stop running any PWM.
        return

; Prepare the current port masks, then wait, then apply them.
;
; Note: 3 + 32 + 5 + 4 + 2 == 46 minimum instruction cycles, plus a wait loop.
;       with I2C: 48 instruction cycles
;
; ENTRY: W = duty-cycle level; wait for TMR0 to reach this level
; EXPECT: Bank 2.
next_step:
        movwf   LEVEL
        call    prepare_masks

        ; Wait until TMR0 >= LEVEL, then mask out some bits.
wait_for_level:
        movfw   TMR0
        addlw   1
        subwf   LEVEL, W        ; W = LEVEL - (TMR0+1)
        ; C = 0: TMR0+1 <= LEVEL ... aka TMR0 < LEVEL
        ; C = 1: TMR0+1 > LEVEL  ... aka TMR0 >= LEVEL
 ifdef SDA
        btfss   PORTC, SDA
        ; SDA dropped low -- I2C START has occurred.
        return
 endif
        skpc
        goto    wait_for_level

        apply_mask MASK_A, MASK_C
        return

; Set up MASK_A and MASK_C with appropriate bits based on C_CHAN* and LEVEL.
;
; Note: 32 instruction cycles.
;
; ENTRY: LEVEL contains value to derive PORTA/C masks
; EXPECT: Bank 2.
prepare_masks:
        ; NOTE: we do not need to initialize MASK_A, since we rotate all
        ;       six PORTA bits into the mask. The upper two bits do not
        ;       matter since they are not implemented in the PORTA register.
        ;
        ; NOTE: we don't care about the RA3 mask since we cannot write/mask
        ;       that bit in PORTA.
        prepare_mask_bit C_CHAN4, MASK_A ; RA5
        prepare_mask_bit C_CHAN3, MASK_A ; RA4
        rlf              MASK_A, F       ; skip RA3. bit value: don't care.
        prepare_mask_bit C_CHAN7, MASK_A ; RA2
        prepare_mask_bit C_CHAN6, MASK_A ; RA1
        prepare_mask_bit C_CHAN5, MASK_A ; RA0

        movlw   0x03            ; will be rotated to 0x30
        movwf   MASK_C
        prepare_mask_bit C_CHAN1, MASK_C ; RC3
        prepare_mask_bit C_CHAN2, MASK_C ; RC2
        prepare_mask_bit C_CHAN9, MASK_C ; RC1
        prepare_mask_bit C_CHAN8, MASK_C ; RC0

        return

; See prepare_masks for overall info.
; Changes:
;   ** Use N_CHAN* values
;   ** On entry, W is the mask level to compare against channel levels
;   ** On exit, the mask for PORTA is loaded into W
;
; Note: 34 instruction cycles
;
; EXPECT: Bank 2.
next_masks:
        movwf   LEVEL
        prepare_mask_bit N_CHAN4, MASK_A ; RA5
        prepare_mask_bit N_CHAN3, MASK_A ; RA4
        rlf              MASK_A, F       ; skip RA3. bit value: don't care.
        prepare_mask_bit N_CHAN7, MASK_A ; RA2
        prepare_mask_bit N_CHAN6, MASK_A ; RA1
        prepare_mask_bit N_CHAN5, MASK_A ; RA0
        movlw   0x03
        movwf   MASK_C
        prepare_mask_bit N_CHAN1, MASK_C ; RC3
        prepare_mask_bit N_CHAN2, MASK_C ; RC2
        prepare_mask_bit N_CHAN9, MASK_C ; RC1
        prepare_mask_bit N_CHAN8, MASK_C ; RC0
        movfw   MASK_A
        return


; Adjust a channel value towards its target value. The adjustment is
; specified as:
;   ADJUST = (CUR_VALUE / 16) + 1
; This value is added/subtracted to reach the target value.
;
; ENTRY: W contains CUR_VALUE
;        T_LEVEL contains target value
; EXIT: W contains NEW_VALUE
; EXPECT: Bank 2.
;
; Note: 20 instruction cycles
;
;   EQUAL: 7 instruction cycles
;   DOWNWARDS, PINNED: 20 instruction cycles
;   DOWNWARDS, NO PIN: 18 instruction cycles
;   UPWARDS, PINNED: 18 instruction cycles
;   UPWARDS, NO PIN: 16 instruction cycles
;
adjust_channel:
        movwf   LEVEL           ; LEVEL = CUR_VALUE
        subwf   T_LEVEL, W      ; W = T_LEVEL - CUR_VALUE
        skpz
        goto    adjust_unequal
        ; Values are equal. No change needed.
        movfw   LEVEL
        return
adjust_unequal:
        ; Note: 5 instruction cycles to here
        skpc
        goto    adjust_downwards ; CUR_VALUE > T_LEVEL (generated CARRY)
        ; CUR_VALUE < T_LEVEL. Adjust upwards.
        swapf   LEVEL, W        ; W = CUR_VALUE / 16 (and bits in upper nibble)
        andlw   0x0f
        addlw   1
        addwf   LEVEL, F        ; LEVEL = CUR_VALUE + (CUR_VALUE / 16) + 1
        movfw   LEVEL
        subwf   T_LEVEL, W      ; W = T_LEVEL - LEVEL
        ; Note: 13 instruction cycles to here
        skpc
        ; LEVEL <= T_LEVEL
        return
pin_level:
        ; LEVEL > T_LEVEL. Pin new value to T_LEVEL.
        movfw   T_LEVEL
        return
adjust_downwards:
        ; Note: 8 instruction cycles to here
        swapf   LEVEL, W
        andlw   0x0f
        addlw   1               ; W = (CUR_VALUE / 16) + 1
        subwf   LEVEL, F        ; LEVEL = CUR_VALUE - (CUR_VALUE / 16) - 1
        movfw   LEVEL
        subwf   T_LEVEL, W      ; W = T_LEVEL - LEVEL
        ; Note: 14 instruction cycles to here
        skpc
        ; LEVEL <= T_LEVEL. Pin new value to T_LEVEL
        goto    pin_level
        ; LEVEL > T_LEVEL
        return


; I2C START condition has been identified. Turn off all outputs.
;
; NOTE: we turn OFF the outputs. At very low levels, keeping them ON for
;       even a little extra time will be noticeable to the human eye.
;       However, at higher levels, turning OFF a bit sooner will not
;       be noticed.
;
; Note: this function does a "return", intended to return from run_PWM.
I2C_started:
        clrf    PORTA
        clrf    PORTC
        return


; stupid assemblers don't realize EOF means *end*
        end
