*= $4000;
        !to "test.o"
    !cpu 6502
;    !set simulator = 1

!ifdef simulator {
sensor1    =    $c061    ;for simulation on mac use simulated sw0 input
sensor2 =       $c062   ;for simulation on mac use simulated sw1 inputA
}else{
sensor    =    $c0c0    ;card plugged in slot 4
}
keyboard = $c000    ;read from keyboard

frstsensor =    $80
secsensor =    $40
sensmask =    $3f
frstopensecclos = frstsensor OR sensmask
frstclossecopen = secsensor OR sensmask
bothclos = sensmask
bothopen = secsensor OR frstsensor OR sensmask

variables = $fa     ; used for onerr pointers by applesoft
variable1 = $eb     ;
variable2 = $ce     ;
msfrstopen =    variables + $0
lsfrstopen =    variables + $1
esfrstcls =    variables + $2
msfrstcls =    variables + $3
lsfrstcls =    variables + $4
mssecopen =    variable1 + $0
lssecopen =    variable1 + $1
esseccls =    variable1 + $2
msseccls =    variable1 + $3
lsseccls =    variable1 + $4
errortn =    variable2 + $0    ; error returned
state  =    variable2 + $1    ; current state
scratch =    $18    ; scratchpad

;
; error codes
;
err_timeout = 1
err_pg1_fail = 2
err_pg2_fail = 3
err_pg3_fail = 4


;
; define commonly used macros
;
;
;  increment time (ls byte in x, ms byte in y) - takes 9 cycles
;
    !macro inc_time .wrap_exit{
        inx            ; 2
        bne    .no_ls_wrap    ; 2/3
        iny            ; 2
        bne    .no_ms_wrap    ; 2/3
        jmp    .wrap_exit        ; 4
.no_ls_wrap
        nop            ; 2
        nop            ; 2
.no_ms_wrap
    }
;
;  save time (ls byte in x, ms byte in y) - takes 10 cycles
;
    !macro sv_time_1_opn {
        tya            ; 2
        sta    msfrstopen    ; 3
        txa            ; 2
        sta    lsfrstopen    ; 3
    }
;
;  save time (ls byte in x, ms byte in y) - takes 10 cycles
;
    !macro sv_time_1_cls {
        tya            ; 2
        sta    msfrstcls    ; 3
        txa            ; 2
        sta    lsfrstcls    ; 3
    }
;
;  save time (ls byte in x, ms byte in y) - takes 10 cycles
;
    !macro sv_time_2_opn {
        tya            ; 2
        sta    mssecopen    ; 3
        txa            ; 2
        sta    lssecopen    ; 3
    }
;
;  save time (ls byte in x, ms byte in y) - takes 10 cycles
;
    !macro sv_time_2_cls {
        tya            ; 2
        sta    msseccls    ; 3
        txa            ; 2
        sta    lsseccls    ; 3
    }
!ifdef simulator {
;
; following code for mac simulator - use mouse buttons
;
;
;  read shutter HW  and  put results into acc
;    18 cycles
;
    !macro read_sensor {
        lda    sensor2        ; 4  read sensor2
        lsr             ; 2 shift right one bit
        sta    scratch        ; 3 save temporary
        lda    sensor1        ; 4 read sensor1
        ora    #sensmask    ; 2 add  in unused bits
        ora    scratch        ; 3 merge with other sensor
    }
} else {
    !macro read_sensor {
        lda sensor        ; 4 read sensor
    }
}
!ifdef simulator {
;
;  read shutter HW  and wait for change
;    0 cycles setup
;    32 per loop
;    -1 final iteration
;
    !macro wait_change  .current_state, .timeout {
.compare
        +inc_time .timeout    ; 9 cycles
        +read_sensor        ; 18
        cmp    .current_state    ; 3 cycles (zero page)
        beq    .compare    ; 2 fall through/3 branch
    }
} else {
;
;  read shutter HW  and wait for change -  
;        3 cycles setup (assuming .current_state is zero page)
;        16 per loop
;        -1 final iteration
;
    !macro wait_change  .current_state, .timeout{
        lda    .current_state  ; 3 cycles (zero page)
.compare
        +inc_time .timeout    ; 9 cycles
        cmp    sensor        ; 4 cycles
        beq    .compare    ; 2 fall through/3 branch
    }
}

    .code
;
; initialize variables and clock
;
start
    lda    #$0
    sta    msfrstopen    ;left shutter open time
    sta    lsfrstopen    ;
    sta    esfrstcls    ;left shutter close time
    sta    msfrstcls    ;
    sta    lsfrstcls    ;
    sta    lssecopen    ;right shutter open time
    sta    mssecopen    ;
    sta    esseccls    ;right shutter close time
    sta    lsseccls    ;
    sta    msseccls     ;
    sta    errortn        ;initially no error
    tax            ;initial time is zero
    tay            ;initial time (most significant) is also zero
.start
    lda    #bothclos    ;initial state
    sta    state        ;save it
;
; first ensure the shutter is totally closed
;
.wait_till_off
    lda    keyboard    ; has user typed something?
    bpl    .wait_off_cont    ; no continue
    rts            ; yes return to application
.wait_off_cont
    +read_sensor        ;
    cmp state        ; are both sensors off?
    bne .wait_till_off    ; no, wait
;
; align page 1 to ensure branches don't cross page boundries
;
    !align    255,0
;
; OK, we are ready to go, but don't start clock until something happens
;
.wait_4_change
    lda    keyboard    ; has user typed something?
    bpl    .wait_change_cont ; no continue
    rts            ; yes return to application
.wait_change_cont
    +read_sensor        ;
    cmp state        ; 3 are both sensors off?
    beq .wait_4_change    ; 2/3 yes, wait
;
; Ok, we detected a transition
;
; is is 1st, 2nd or both open?
    sta     state        ; 3 save current state
    cmp     #bothopen    ; 2 check for both open
    beq    .pg1_both_open  ; 2/3 yes, skip jump
    jmp    .pg1_chk_1st_open ; 3 branch (this leads to page 2)
;
; both shutters opened at same time
;    
.pg1_both_open
    +wait_change    state,t12_timeout ; 3(initial)/18(per loop)/7(exit)
    +read_sensor        ; 4
    sta state        ; 3
;
; which shutter closed?
;
    cmp #bothclos        ; 2 both?
    bne .pg1_chk_1st_closed; 2/3 no, try individual
    +sv_time_2_cls         ; log times for both channels
    +sv_time_1_cls         ;
    rts
;
; first shutter closed, second still open
;
.pg1_chk_1st_closed
    cmp     #frstclossecopen ; 2 just first
    bne    .pg1_chk_2nd_closed; 2/3 no, must be second
    +sv_time_1_cls         ; 10
.pg1_1st_cycled
;
; wait for 2nd to close, 1st already cycled
;
    +wait_change    state,t2_timeout ; 3(initial)/18(per loop)/7(exit)
    +read_sensor        ; 4
    cmp     #bothclos    ; 2 sanity check
    bne    .pg1_fail    ; 2/3 branch if error
    +sv_time_2_cls         ; 10
    rts    
;
; assume 2nd closed
;
.pg1_chk_2nd_closed
    +sv_time_2_cls         ; 10
.pg1_2nd_cycled
;
; wait for 1st to close, 2nd already cycled
;
    +wait_change    state,t1_timeout ; 3(initial)/18(per loop)/7(exit)
    +read_sensor        ; 4
    cmp     #bothclos    ; 2 sanity check
    bne    .pg1_fail    ; 2/3 branch if error
    +sv_time_1_cls         ; 10
    rts    
.pg1_fail
    lda    #err_pg1_fail
    sta    errortn
    rts

;
; align page 2 to ensure branches don't cross page boundries
;
    !align    255,0
;
;
.pg1_chk_1st_open
    cmp     #frstopensecclos; 2 just first open
    beq    .pg1_1st_open    ; 2/3 no, skip jmp if first
    jmp    .pg1_assume_2nd_open; 3  must be second - to page 3 listing
.pg1_1st_open
    +wait_change    state,timeout ; 3(initial)/18(per loop)/7(exit)
    +read_sensor        ; 4
    sta state        ; 3
;
; page 2 flowchart starts here
;
    cmp     #bothclos    ; check for 1st closed, 2nd still closed
    bne    .pg2_check_both_open ;
;
; 1st cycled, 2nd still closed
;
    +sv_time_1_cls         ; 10
    +wait_change    state,timeout ; 3(initial)/18(per loop)/7(exit)
    +read_sensor        ; 4
    sta state        ; 3
;
; has to be 2nd open, 1st closed, other is error
;
    cmp     #frstclossecopen ; 2
    bne    .pg2_fail    ; 2/3
.pg2_2nd_open
    +sv_time_2_opn         ; 10
    jmp    .pg1_1st_cycled ; 3
;
; check for both open
;
.pg2_check_both_open
    cmp     #bothopen    ; 2
    bne    .pg2_2nd_open_1st_cycled ;2/3
    +sv_time_2_opn         ; 10
    jmp    .pg1_both_open    ; 3
.pg2_2nd_open_1st_cycled
    +sv_time_1_cls         ; 10
    jmp     .pg2_2nd_open    ; 3    
;
; page 2 failures go here
;
.pg2_fail
    lda    #err_pg2_fail
    sta    errortn
    rts
;
; align page 3 to ensure branches don't cross page boundries
;
    !align    255,0
;
; 2nd sensor recorded light, first
;
.pg1_assume_2nd_open
    +wait_change    state,timeout ; 3(initial)/18(per loop)/7(exit)
    +read_sensor        ; 4
    sta state        ; 3
;
; page 3 listing starts here
;
    cmp     #bothclos    ; check for 2nd closed, 1st still closed
    bne    .pg3_check_both_open ;
;
; 2nd cycled, 1st still closed
;
    +sv_time_2_cls         ; 10
    +wait_change    state,timeout ; 3(initial)/18(per loop)/7(exit)
    +read_sensor        ; 4
    sta state        ; 3
;
; has to be 1st open, 2nd closed, other is error
;
    cmp     #frstopensecclos ; 2
    bne    .pg3_fail    ; 2/3
.pg3_1st_open
    +sv_time_1_opn         ; 10
    jmp    .pg1_2nd_cycled ; 3
;
; check for both open
;
.pg3_check_both_open
    cmp     #bothopen    ; 2
    bne    .pg3_1st_open_2nd_cycled ;2/3
    +sv_time_1_opn         ; 10
    jmp    .pg1_both_open    ; 3
.pg3_1st_open_2nd_cycled
    +sv_time_2_cls         ; 10
    jmp     .pg3_1st_open    ; 3    
;
; page 3 failures go here
;
.pg3_fail
    lda    #err_pg2_fail
    sta    errortn
    rts
;
; timeout with both shutters open, inc extended time and continue, in not wrappedu
;
t12_timeout
    inc     esfrstcls    ; 4 inc 1st extended close
    beq    timeout        ; 2, timeout, exitt
    inc     esseccls    ; 4 inc 2st extended close
    beq    timeout        ; 2 timeout, exitt
    jmp    .pg1_both_open  ; 3 continue
;
; timeout with only frst shutter open
;
t2_timeout
    inc     esseccls    ; 4 inc 1st extended close
    beq    timeout        ; 2 timeout, exit
    jmp    .pg1_1st_cycled    ; 3 continue
;
; timeout with only second shutter open
;
t1_timeout
    inc     esfrstcls    ; 4 inc 2nd extended close
    beq    timeout        ; 2, timeout, exit
    jmp     .pg1_2nd_cycled ; 3 continue
;
; clock wrapped
;
timeout
    lda    #err_timeout
    sta    errortn
    rts