; Specify Device.
.include "8515def.inc"

;
; registers used 
;

.DEF EDIT_STATE1=r16; status of editing for PWM value1
.DEF EDIT_STATE2=r17; status of editing for PWM value2
.DEF SEG7       =r20; Register for seven segment LEDs
.DEF PWM_VALUE  =R22; Register that will contain value for PWM
.DEF list_inc   =r23; 
.DEF rgen1      =r24; general register 1
.DEF rgen2      =R25; general register 2

.DEF EEdrd      =r0		;result data byte
.DEF EEdwr      =r26		;data byte to write to EEPROM
.DEF EEawr      =r27		;address low byte to write to
.DEF EEawrh     =r28		;address high byte to write to
.DEF EEard      =r27		;address low byte to write to
.DEF EEardh     =r28		;address high byte to write to

; Constants

.EQU DIGIT      =PortA; Controls the seven segment LEDs
.EQU LEDS_OUT   =PortC; handles two LEDs for the switches
.EQU PWM_OUT    =PortD; recieves the PWM digital settings
.EQU SW1_PIN    =0; 
.EQU SW2_PIN    =1; 
.EQU IN_PORT    =PINB; Various push buttons
.EQU EE_addr1    =$10;
.EQU EE_addrh1   =$00;
.EQU EE_addr2    =$20;
.EQU EE_addrh2   =$00;

;
; Code segment start
;
.CSEG

	rjmp Start; 
	rjmp Start;
	rjmp Start;
	
;
; Subroutines here

EEWrite:
	sbic EECR,EEWE	;if EEWE not clear
	rjmp EEwrite		;    wait more
	out EEARH,EEawrh	;output address high for 8515
	out	EEARL,EEawr	;output address low for 8515
	out	EEDR,EEdwr	;output data
	sbi EECR,EEMWE	;set master write enable
	sbi	EECR,EEWE	;set EEPROM Write strobe
	ret

EERead:
	sbic EECR,EEWE	;if EEWE not clear
	rjmp EERead		;    wait more
	out EEARH,EEardh	;output address high for 8515
	out	EEARL,EEard	;output address low for 8515
	sbi	EECR,EERE	;set EEPROM Read strobe
	in	EEdrd,EEDR	;get data
	ret

delay20ms:
    ldi rgen1, 60
outer_loop:
    ldi rgen2, 255
inner_loop:
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    dec rgen2
    brne inner_loop
    dec rgen1
    brne outer_loop
    ret

delaysec:
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    rcall delay20ms          ;call a debounce delay routine
    ret
	

get_PWM1_value:
	ldi	EEardh,EE_addrh1
	ldi	EEard, EE_addr1
	rcall	EERead
    mov list_inc, EEdrd
    ret	

get_PWM2_value:
	ldi	EEardh,EE_addrh2
	ldi	EEard, EE_addr2
	rcall	EERead
    mov list_inc, EEdrd
    ret

save_PWM1_value:
	mov	EEdwr,list_inc
	ldi	EEawrh,EE_addrh1
	ldi	EEawr, EE_addr1
	rcall	EEWrite	
    ret

save_PWM2_value:
	mov	EEdwr,list_inc
	ldi	EEawrh,EE_addrh2
	ldi	EEawr, EE_addr2
	rcall	EEWrite	
    ret
	
;; the point where list_inc is used to load registers for SEG7 and PWM_VALUE.
;; function goes into the list containing various values for digits and pwm values.
;; it uses registers: r0, SEG7, and PWM_VALUE. It also uses list_inc, which counts
;; its way into pwm_list.

fix_list_len:
    dec list_inc
	
get_PWM_settings:
    ldi ZH,high(pwm_list*2)
    ldi ZL,low(pwm_list*2)
    lpm
    mov rgen1, list_inc; dont destroy list_inc
	
loop1: 
    adiw ZL, 4
	lpm
	mov SEG7, r0
	cpi SEG7, 0xff    ;test for 0xff to see if at the end
	breq fix_list_len
    dec rgen1
    cpi rgen1, 0
    brne loop1

    mov SEG7, r0      ;load on a value and shift it over four, this is "tens"
	lsl SEG7
	lsl SEG7
	lsl SEG7
	lsl SEG7

    adiw ZL, 1        ;load on the ones
	lpm
    add SEG7, r0
	
    adiw ZL, 1
	lpm
    mov PWM_VALUE, r0	

    ret
	
adjust_settings:

    in rgen1, IN_PORT        ;read input buffer
    cbr rgen1, 0b11110011    ;wipe all bits except the 4th and 3rd to last
    cpi rgen1, 0b00000100    ;check if one is pressed
    brne not_3               ;if not check more

its_3:   
    rcall delay20ms          ;call a debounce delay routine
waitfor_rel3:                ;now wait for the switch to be
    in rgen1, IN_PORT        ;read input buffer
    cbr rgen1, 0b11110011    ;wipe all bits except the 4th and 3rd to last
    cpi rgen1, 0b00001100    ;perform test for release
    brne waitfor_rel3

    rcall delay20ms          ; debounce after release
	inc list_inc
	rjmp _end
	
not_3:
    cpi rgen1, 0b00001000    ;check if one is pressed
    brne not_4               ;if not check more

its_4:   
    rcall delay20ms          ;call a debounce delay routine
waitfor_rel4:                ;now wait for the switch to be
    in rgen1, IN_PORT        ;read input buffer
    cbr rgen1, 0b11110011    ;wipe all bits except the 4th and 3rd to last
    cpi rgen1, 0b00001100    ;perform test for release
    brne waitfor_rel4

    rcall delay20ms          ; debounce after release
    dec list_inc
	rjmp func_end

not_4: ; means that both or none are pressed.
    ; rcall delay20ms          ;call a debounce delay routine   
    ; do stuff here

func_end:
	ret
	
;	
; Start of program
;
Start:
;
; Setup the stack for the use of subroutines
;
	ldi rgen1,HIGH(RAMEND); Stack setting to highest RAM adress
	out SPH,rgen1
	ldi rgen1,LOW(RAMEND)
	out SPL,rgen1

    ldi rgen1, 0b11111111     ;load register with all 1's
    out DDRA, rgen1           ;configure PORT A for all outputs
	
    ; two of Port B's inputs are not used.
    ldi rgen1, 0b00000000     ;load register with all 0's
    out DDRB, rgen1           ;configure PORT B for all inputs

    ldi rgen1, 0b11111111     ;load register with all 1's
    out DDRC, rgen1           ;configure PORT C for all outputs
	
	ldi rgen1, 0b11111111     ;load register with all 1's
    out DDRD, rgen1           ;configure PORT D for all outputs
	
    cbi LEDS_OUT, SW1_PIN     ; turn em on.
    cbi LEDS_OUT, SW2_PIN

	ldi EDIT_STATE1, 0
	ldi EDIT_STATE2, 0

    ; stabilize...
    rcall delaysec

    sbi LEDS_OUT, SW1_PIN    ;make sure they're both off
    sbi LEDS_OUT, SW2_PIN


main_loop:
; test if the edit switch is on
    in rgen1, IN_PORT        ;read input buffer
    cbr rgen1, 0b11101111    ;wipe all bits except the fifth
    cpi rgen1, 0b00000000    ;check if one is pressed
    brne edit_switch_off     

edit_switch_on:   
    rcall delay20ms          ;call a debounce delay routine
    cpi EDIT_STATE1, 1       ;check if EDIT_STATE is already set. 
    breq allow_edits         ;if it is, dont perform other tests
    cpi EDIT_STATE2, 1       ;check if EDIT_STATE2 is already set. 
    breq allow_edits

edit_state_not_set:
    sbi LEDS_OUT, SW1_PIN    ; turn off the LED for switch 1
    sbi LEDS_OUT, SW2_PIN    ; turn off the LED for switch 2
    in rgen1, IN_PORT        ;read input buffer
    cbr rgen1, 0b11111100    ;wipe all bits except the last two
    cpi rgen1, 0b00000010    ;check if one is pressed
    brne not_1               ;if not check more

edit_request1:   
    rcall delay20ms          ;call a debounce delay routine
    ldi EDIT_STATE1, 1       ;flip this to ON
    rcall get_PWM1_value    ;this retreives the value from program memory
    cbi LEDS_OUT, SW1_PIN    ; turn on the LED for switch 1
    sbi LEDS_OUT, SW2_PIN    ; turn off the LED for switch 2
	rjmp edit_end
	
not_1:
    cpi rgen1, 0b00000001    ;check if one is pressed
    brne not_2               ;if not check more

edit_request2:   
    rcall delay20ms          ;call a debounce delay routine
    ldi EDIT_STATE2, 1       ;flip this to ON
    rcall get_PWM2_value    ;this retreives the value from program memory
    sbi LEDS_OUT, SW1_PIN    ; turn off the LED for switch 1
    cbi LEDS_OUT, SW2_PIN    ; turn on the LED for switch 2
	rjmp edit_end

not_2: ; means that both or none are pressed.
    rcall delay20ms          ;call a debounce delay routine   
	rjmp edit_end

allow_edits:
    rcall get_PWM_settings
    rcall adjust_settings	

edit_end:
    ; set the PWM to a low setting here.
    ldi PWM_VALUE, 0b00000011
    out PWM_OUT, PWM_VALUE
	rjmp _end
	
; the edit switch is not on.
; This is the "show" state -- when user has edit switch off and
; the driver is displaying its stored values and driving the PWM
; settings. 

;First we handle the transition when there was an edit
; happening, and we want to store the edited values.
; Remember: only 100,000 or so writes can be made to the eeprom.
edit_switch_off:
	rcall delay20ms          ;call a debounce delay routine   
    cpi EDIT_STATE1, 0       ;check EDIT_STATE1 status. 
    breq edit1_not_on        ;if it is off, jump

edit1_is_on:
	ldi EDIT_STATE1, 0       ;turn it off
	rcall save_PWM1_value
	rjmp edit_test_end
	
edit1_not_on:
    cpi EDIT_STATE2, 0       ;check if EDIT_STATE2 status. 
    breq edit_test_end       ;if it is off, jump

edit2_is_on:
	ldi EDIT_STATE2, 0       ;turn it off
	rcall save_PWM2_value
	
edit_test_end:

; now check state of input pin requesting PWM1 or PWM2 setting

    in rgen1, IN_PORT        ;read input buffer
    cbr rgen1, 0b11011111    ;wipe all bits except the sixth
    cpi rgen1, 0b00100000    ;check its on
    brne setting_is_lo       ;if not check more

    rcall get_PWM1_value    ;this retreives the value from program memory
    cbi LEDS_OUT, SW1_PIN    ; turn on the LED for switch 1
    sbi LEDS_OUT, SW2_PIN    ; turn off the LED for switch 2
    rjmp put_PWM_on_port
	
setting_is_lo:
    sbi LEDS_OUT, SW1_PIN    ; turn off the LED for switch 1
    cbi LEDS_OUT, SW2_PIN    ; turn on the LED for switch 2
    rcall get_PWM2_value     ;this retreives the value from program memory

put_PWM_on_port:
    rcall get_PWM_settings   ;list_inc has been set, call this function to load PWM_VALUE
    out PWM_OUT, PWM_VALUE   ; show PWM_VALUE to the world

; in this state user may request what the PWM value is for either
;  of the PWM settings. This is done by pressing the PWM switches
;  without the edit switch set to on. notice that this just pushes
;  new values on the SEG7 for display, but the status of PWM_OUT 
;  stays the same.

    mov rgen2, list_inc      ;save this guy
    in rgen1, IN_PORT        ;read input buffer
    cbr rgen1, 0b11111110    ;wipe all bits except 2nd
    cpi rgen1, 0b00000000    ;check if sw1 is pressed
    brne not_show_PWM1       ;if not check more

show_PWM1:   
    rcall delay20ms          ;call a debounce delay routine
    rcall get_PWM1_value     ;this retreives the value from program memory
    rcall get_PWM_settings   ;list_inc has been set, call this function to load SEG7
    mov list_inc, rgen2      ;restore this so it doesnt get used in put_PWM_on_port
    cbi LEDS_OUT, SW1_PIN    ; turn on the LED for switch 1
    sbi LEDS_OUT, SW2_PIN    ; turn off the LED for switch 2
    out DIGIT, SEG7

	; loop until user releases sw1
wait_for_rel_of_showPWM1:
    in rgen1, IN_PORT        ;read input buffer
    cbr rgen1, 0b11111110    ;wipe all bits except the 2nd
    cpi rgen1, 0b00000001    ;perform test for release
    brne wait_for_rel_of_showPWM1
    rcall delay20ms          ; debounce after release
	rjmp _end
	
not_show_PWM1:
    in rgen1, IN_PORT        ;read input buffer
    cbr rgen1, 0b11111101    ;wipe all bits except last
    cpi rgen1, 0b00000000    ;check if two is pressed
    brne _end                ;if here, neither button pressed

show_PWM2:   
    rcall delay20ms          ;call a debounce delay routine
    rcall get_PWM2_value     ;this retreives the value from program memory
    rcall get_PWM_settings   ;list_inc has been set, call this function to load SEG7
    mov list_inc, rgen2      ;restore this so it doesnt get used in put_PWM_on_port
    sbi LEDS_OUT, SW1_PIN    ; turn off the LED for switch 1
    cbi LEDS_OUT, SW2_PIN    ; turn on the LED for switch 2
    out DIGIT, SEG7
	
	; loop until user releases sw2
wait_for_rel_of_showPWM2:
    in rgen1, IN_PORT        ;read input buffer
    cbr rgen1, 0b11111101    ;wipe all bits except the 1st
    cpi rgen1, 0b00000010    ;perform test for release
    brne wait_for_rel_of_showPWM2
    rcall delay20ms          ; debounce after release
	

_end:

out DIGIT, SEG7

rjmp main_loop

pwm_list: 
.db 0,2,0,0x21
.db 0,6,1,0x21
.db 0,9,2,0x21
.db 1,2,3,0x21
.db 1,5,4,0x21
.db 1,8,5,0x21
.db 2,1,6,0x21
.db 2,4,7,0x21
.db 2,7,8,0x21
.db 3,1,9,0x21
.db 3,4,10,0x21
.db 3,7,11,0x21
.db 4,0,12,0x21
.db 4,3,13,0x21
.db 4,6,14,0x21
.db 4,9,15,0x21
.db 5,2,16,0x21
.db 5,6,17,0x21
.db 5,9,18,0x21
.db 6,2,19,0x21
.db 6,5,20,0x21
.db 6,8,21,0x21
.db 7,1,22,0x21
.db 7,4,23,0x21
.db 7,7,24,0x21
.db 8,1,25,0x21
.db 8,4,26,0x21
.db 8,7,27,0x21
.db 9,0,28,0x21
.db 9,3,29,0x21
.db 9,6,30,0x21
.db 9,9,31,0x21
.db 0xff,0xff,0xff,0xff  ;test for 0xff to see if at the end