; ATmega328.V3.asm Nov 05, 2021

; cd ~/Desktop/freddieneil.com/kitsandparts.com/tutorials/ATmega328/

; avra ATmega328.V3.asm

; avrdude -P usb -p m328p -c avrispv2 -U ATmega328.V3.hex
; avrdude -P usb -p m328p -c avrispv2 -U flash:w:ATmega328.V3.hex -U eeprom:w:ATmega328.V3.eep.hex

; avrdude -P usb -p m328p -c avrispv2 -U eeprom:r:eeprom:i

; avrdude -P usb -p m328p -c avrispv2 -U lfuse:w:0x62:m  -U hfuse:w:0xD9:m  -U efuse:w:0xFF:m

.include "../include/m328Pdef.inc"

.equ  encoder_type  = 1   ; Declares decoder. 0=none, 1=mechanical, 2=optical

.equ LCD_DRS=	PC0
.equ LCD_CP	=	PC1
.equ LCD_E	=	PC2
.equ LED		=	PC3

.equ  button    = PD1
.equ  output_a  = PD2
.equ  output_b  = PD3

.equ PHASE = PD4

.def mx0 = r2
.def mx1 = r3
.def mx2 = r4
.def mx3 = r5
.def mz0 = r6
.def mz1 = r7
.def mz2 = r8
.def mz3 = r9
.def mz4 = r10
.def mz5 = r11
.def mz6 = r12
.def mz7 = r13
.def press = r14
.def encoder =r15
.def temp1 =r16
.def temp2 =r17
.def temp3 =r18
.def temp4 =r19
.def temp5 =r20
.def delay =r21
.def my0 = r22
.def my1 = r23
.def my2 = r24
.def my3 = r25
.def XL = r26
.def XH = r27
.def YL = r28
.def YH = r29
.def ZL	= r30
.def ZH	= r31

.eseg
.org 0
frequency_memory_0: .db "12345678"
frequency_memory_1: .db "12345678"
frequency_memory_2: .db "12345678"
frequency_memory_3: .db "12345678"
frequency_memory_4: .db "12345678"
frequency_memory_5: .db "12345678"
frequency_memory_6: .db "12345678"
frequency_memory_7: .db "12345678"
frequency_memory_8: .db "12345678"
frequency_memory_9: .db "12345678"
cursor_number_eeprom: .db $05

.dseg
.org SRAM_START
test_buffer: .byte 8; buffer for displaying memory data
frequency: .byte 8; ascii displayed frequency
freq_hex: .byte 4 ; 32 bit Hex frequency
cursor_number_sram: .byte 1

.cseg
.org 0
  jmp RESET
  jmp EXT_INT0 ; External Interrupt Request 0
  jmp EXT_INT1 ; External Interrupt Request 1
  jmp NOT_USED	; Pin Change Interrupt Request 0
  jmp NOT_USED	; Pin Change Interrupt Request 1
  jmp NOT_USED	; Pin Change Interrupt Request 2
  jmp NOT_USED	; Watchdog Time-out Interrupt
  jmp NOT_USED	; Timer/Counter2 Compare Match A
  jmp NOT_USED	; Timer/Counter2 Compare Match A
  jmp NOT_USED	; Timer/Counter2 Overflow
  jmp NOT_USED	; Timer/Counter1 Capture Event
  jmp NOT_USED	; Timer/Counter1 Compare Match A
  jmp NOT_USED	; Timer/Counter1 Compare Match B
  jmp NOT_USED	; Timer/Counter1 Overflow
  jmp NOT_USED	; TimerCounter0 Compare Match A
  jmp NOT_USED	; TimerCounter0 Compare Match B
  jmp OVF0      ; Timer/Counter0 Overflow
  jmp NOT_USED	; SPI Serial Transfer Complete
  jmp NOT_USED	; USART Rx Complete
  jmp NOT_USED	; USART, Data Register Empty
  jmp NOT_USED	; USART Tx Complete
  jmp NOT_USED	; ADC Conversion Complete
  jmp NOT_USED	; EEPROM Ready
  jmp NOT_USED	; ;Analog Comparator
  jmp NOT_USED	; Two-wire Serial Interface
  jmp NOT_USED	; Store Program Memory Read
NOT_USED: 
  reti

RESET:
	ldi		temp1,  low(RAMEND)
	out   SPL,    temp1			    ; Set stack pointer to last internal RAM location
	ldi		temp1,  high(RAMEND)
	out		SPH,    temp1
	
;init ports

	ldi		temp1,$0F
	out		DDRC,temp1		;make PC0,PC1,PC2,PC3 pins an output
	cbi		PORTC,LCD_E		;set LCD_E low
 	cbi		PORTC,LCD_CP	;set LCD_CP low
	sbi		PORTC,LED			;turn LED off	

  ldi   temp1, 0
  out   DDRD, temp1   ;all of portd is input	

;init misc

  ; set System Clock Prescaler - system default clock is 8.0 MHz
  ldi   temp1,  0b10000000  ; init for prescaler change
  sts   CLKPR,  temp1       ; else out instruction to CLKPS will not take effect
; ldi   temp1,  0b00000000  ; prescaler / 1 - ( 8.0 MHz / 1) = 8.000 MHz = 125 nanoseconds
; ldi   temp1,  0b00000001  ; prescaler / 2 - ( 8.0 MHz / 2) = 4.000 MHz = 250 nanoseconds
; ldi   temp1,  0b00000010  ; prescaler / 4 - ( 8.0 MHz / 4) = 2.000 MHz = 500 nanoseconds
  ldi   temp1,  0b00000011  ; prescaler / 8 - factory default; set by Fuse Low Byte ( 8.0 MHz / 8) = 1.000 MHz = 1.000 microsecond
; ldi   temp1,  0b00000100  ; prescaler / 16 - ( 8.0 MHz / 16) = 500 KHz = 2.000 microseconds
; the setting below disable reprogramming the uP - must use "-B 500" with AVRDUDE to fix
; ldi   temp1,  0b00000101  ; prescaler / 32 - ( 8.0 MHz / 32) = 250 KHz = 4.000 microseconds
; ldi   temp1,  0b00000110  ; prescaler / 64 - ( 8.0 MHz / 64) = 125 KHz = 8.000 microseconds
; ldi   temp1,  0b00000111  ; prescaler / 128 - ( 8.0 MHz / 128) = 62.5 KHz = 16.000 microseconds
; ldi   temp1,  0b00001000  ; prescaler / 256 - ( 8.0 MHz / 256) = 31250 Hz = 32.000 microseconds
  sts   CLKPR,  temp1

; with prescaler set to 8:
; ldi   temp1,  0b00000001  ; no prescaling
  ldi   temp1,  0b00000010  ; CLK 1/8 from prescaler (8.000 uS)
; ldi   temp1,  0b00000011  ; CLK 1/64
; ldi   temp1,  0b00000100  ; CLK 1/256
; ldi   temp1,  0b00000101  ; CLK 1/1024
  out   TCCR0B, temp1	      ; set timer prescaler

	ldi   temp1,  0b00000001  ; overflow interrupt enable bit
	sts		TIMSK0,temp1	;enable TIMER0 overflow interrupts
	
	ldi		temp1,0b00000101	; Any change of INT0 & INT1 generate an interrupt request
	sts		EICRA,temp1
	sbi		EIMSK,0 ;enable INT0 interrupts
  sbi		EIMSK,1 ;enable INT1 interrupts

  clr		encoder				;clear encoder interrupt counts
	sei   ;global all interrupt enable

 ldi YH,high(cursor_number_eeprom)
 ldi YL,low(cursor_number_eeprom)
 rcall EEPROM_READ
 tst temp1
 breq MOVE_DEFAULT_CURSOR_TO_EEPROM
 cpi temp1,$09
 brlo CURSOR_NUMBER_IN_EEPROM_OK
MOVE_DEFAULT_CURSOR_TO_EEPROM:
 ldi temp1,3
 rcall EEPROM_WRITE
CURSOR_NUMBER_IN_EEPROM_OK:
 ld temp1,Y
 ldi XH,high(cursor_number_sram)
 ldi XL,low(cursor_number_sram)
 st X,temp1
  
MSG_PORK:
;   "1234567890123456"
.db "  04 Oct 2021   ",0,0
MSG_CMD:
;   "1234567890123456"
.db "Porkalyzer.328v0",0,0
PORK:	
 rcall INIT_LCD
 ldi ZH,high(2*MSG_PORK)
 ldi ZL,low(2*MSG_PORK)
 ldi temp2,$80				;display on line 1
 rcall LCDCMD
 rcall DISPLAY_LINE
 ldi temp1,3 ;blink LED 3x to show system is working
 rcall BLINK_LED ; has effect of a delay 1.5 seconds
 rcall load_last_entered_frequency
 rcall ShowFreq
 call ASCII8_HEX4
 
MENU:
 ldi		ZH,high(2*MSG_CMD)
 ldi		ZL,low(2*MSG_CMD)
 ldi		temp2,$C0				;display on line 2
 rcall	LCDCMD
 rcall	DISPLAY_LINE
GET_CMD:
CMD025:
 tst encoder
 breq CMD049
 jmp process_encoder
CMD049:

rjmp GET_CMD 
 


MSG_CURSOR:
;    1234567890123456789012345678901234567890
.db " Cursor Loc: 1  ",0,0

set_default_cursor:
 ldi		ZH,high(2*MSG_CURSOR)
 ldi		ZL,low(2*MSG_CURSOR)
 ldi		temp2,$C0				;display on line 1
 rcall	LCDCMD
 rcall	DISPLAY_LINE
 ldi XH,high(cursor_number_sram)
 ldi XL,low(cursor_number_sram)
 ld temp3,X ; default cursor value
 ori temp3,$30
SC103: 
 ldi temp2,$CD ; set CURSOR
 rcall LCDCMD
SC105:
 rcall get_keypad ; return character in temp1
 tst temp1
 breq SC105
 cpi temp1,'0' ; '0' is illegal
 breq SC105
 cpi temp1,'#' ; enter key
 breq SC120 ; all done with keypad entry
 cpi temp1,'*' ; enter key
 breq SC999 ; Escape key (*)
 cpi temp1,$39 ; disregard keys 9,A,B,C,D
 brge SC105
 mov temp3,temp1
 rcall LCDCHR
 rjmp SC103
SC120:
 ldi YH,high(cursor_number_eeprom)
 ldi YL,low(cursor_number_eeprom)
 subi temp3,$30
 st X,temp3
 mov temp1,temp3
 rcall EEPROM_WRITE
SC999:
 rjmp MENU
  

 rjmp MENU
 
MSG_LM:
;    1234567890123456789012345678901234567890
.db "Load from Mem: 1",0,0

load_from_memory:
 ldi		ZH,high(2*MSG_LM)
 ldi		ZL,low(2*MSG_LM)
 ldi		temp2,$C0				;display on line 1
 rcall	LCDCMD
 rcall	DISPLAY_LINE
 ldi temp3,$31 ; default save memory number
LM103: 
 ldi temp2,$CF ; set CURSOR
 rcall LCDCMD
LM105:
 rcall get_keypad ; return character in temp1
 tst temp1
 breq LM105
 cpi temp1,'#' ; enter key
 breq LM120 ; all done with keypad entry
 cpi temp1,'*' ; escape key
 breq LM999 ; Escape key (*)
 cpi temp1,$3A ; disregard keys A,B,C
 brge LM105
 mov temp3,temp1
 rcall LCDCHR
 rjmp LM103
LM120:
 ldi ZH,high(frequency)
 ldi ZL,low(frequency)
 ldi YH,high(frequency_memory_0)
 ldi YL,low(frequency_memory_0)
 subi temp3,$30
 breq LM140
LM130:
 adiw YL,8 ; move EEPROM memory pointer to next frequency memory
 dec temp3
 brne LM130
LM140:
 ldi my3,8
LM150:
 rcall EEPROM_READ
 st Z+,temp1
 adiw YL,1 ; increment EEPROM memory pointer
 dec my3
 brne LM150
 call ASCII8_HEX4
LM999:
 rjmp MENU

load_last_entered_frequency:
 ldi ZH,high(frequency)
 ldi ZL,low(frequency)
 ldi YH,high(frequency_memory_0)
 ldi YL,low(frequency_memory_0)
 ldi my3,8
LLEF5:
 rcall EEPROM_READ
 st Z+,temp1
 adiw YL,1 ; increment EEPROM memory pointer
 dec my3
 brne LLEF5
 ret
 
MSG_SM:
;   "1234567890123456"
.db " Save to Mem: 1 ",0,0

save_to_memory:
 ldi		ZH,high(2*MSG_SM)
 ldi		ZL,low(2*MSG_SM)
 ldi		temp2,$C0				;display on line 1
 rcall	LCDCMD
 rcall	DISPLAY_LINE
 ldi temp3,$31 ; default save memory number
SM103: 
 ldi temp2,$CE ; set CURSOR
 rcall LCDCMD
SM105:
 rcall get_keypad ; return character in temp1
 tst temp1
 breq SM105
 cpi temp1,'#' ; enter key
 breq SM120 ; all done with keypad entry
 cpi temp1,'*' ; enter key
 breq SM999 ; Escape key (*)
 cpi temp1,$3A ; disregard keys A,B,C
 brge SM105
 mov temp3,temp1
 rcall LCDCHR
 rjmp SM103
SM120:
 ldi ZH,high(frequency)
 ldi ZL,low(frequency)
 ldi YH,high(frequency_memory_0)
 ldi YL,low(frequency_memory_0)
 subi temp3,$30
 breq SM140
SM130:
 adiw YL,8 ; move EEPROM memory pointer to next frequency memory
 dec temp3
 brne SM130
SM140:
 ldi my3,8
SM150:
 ld temp1,Z+
 rcall EEPROM_WRITE
 adiw YL,1 ; increment EEPROM memory pointer
 dec my3
 brne SM150
SM999:
 rjmp MENU

MSG_EF:
;    1234567890123456789012345678901234567890
.db "Freq:",0

enter_frequency:
 ldi ZH,high(2*MSG_EF)
 ldi ZL,low(2*MSG_EF)
 ldi temp2,$C0				;display on line 1
 rcall LCDCMD
 rcall DISPLAY_LINE
 ldi temp1,' ' ; space
 ldi ZH,high(frequency)
 ldi ZL,low(frequency) ; clear frequency buffer
 ldi temp1,' '
 st Z+,temp1
 ldi temp1,' '
 st Z+,temp1
 ldi temp1,' '
 st Z+,temp1
 ldi temp1,' '
 st Z+,temp1
 ldi temp1,' '
 st Z+,temp1
 ldi temp1,' '
 st Z+,temp1
 ldi temp1,' '
 st Z+,temp1
 ldi temp1,' '
 st Z,temp1
EF100:
 ldi temp2,$C5 ; set CURSOR
 rcall LCDCMD 
 rcall display_freq_buffer
EF103: 
 ldi temp2,$CE ; set CURSOR
 rcall LCDCMD
EF105:
 rcall get_keypad ; return character in temp1
 tst temp1
 breq EF105
 cpi temp1,'*' ; backspace key
 brne EF108
; *** start update_freq_buffer_right ***
 ldi ZH,high(frequency+7) 
 ldi ZL,low(frequency+7)
 ld temp2,Z+
 cpi temp2,' '
 brne EF106
 rjmp MENU
EF106:
 st Z,temp2
 sbiw ZL,2
 ld temp2,Z+
 st Z,temp2
 sbiw ZL,2
 ld temp2,Z+
 st Z,temp2
 sbiw ZL,2
 ld temp2,Z+
 st Z,temp2
 sbiw ZL,2
 ld temp2,Z+
 st Z,temp2
 sbiw ZL,2
 ld temp2,Z+
 st Z,temp2
 sbiw ZL,2
 ld temp2,Z+
 st Z,temp2
 sbiw ZL,2
 ld temp2,Z+
 st Z,temp2
 sbiw ZL,1
 ldi temp2,' '
 st Z,temp2
; *** end of update_freq_buffer_right ***
 ldi temp2,$C5 ; set CURSOR
 rcall LCDCMD 
 rcall display_freq_buffer
 rjmp EF103
EF108:
 cpi temp1,'#' ; enter key
 breq EF115 ; all done with keypad entry
 cpi temp1,$3A ; disregard keys A,B,C,D
 brge EF103
 rcall update_freq_buffer_left
 ldi temp2,$C5 ; set CURSOR
 rcall LCDCMD 
 rcall display_freq_buffer
 rjmp EF103
EF115:
 rcall ASCII8_HEX4
 rcall save_last_entered_frequency
 rjmp MENU
 
ASCII8_HEX4: ; convert ascii freq to 4 byte hex
 ldi ZH,high(freq_hex)
 ldi ZL,low(freq_hex)
 clr temp1
 st Z+,temp1 ; clear freq_hex
 st Z+,temp1
 st Z+,temp1
 st Z,temp1
 ldi temp2,8 ; convert 8 ascii freq bytes to 4 hex bytes
 ldi ZH,high(frequency)
 ldi ZL,low(frequency)
EF122:
 ld temp1,Z+
 cpi temp1,' '
 breq EF123
 subi temp1,$30
 rcall multiply_freq_hex_by_10_and_add
EF123:
 dec temp2
 brne EF122
 rcall ShowFreq
 ret

save_last_entered_frequency:
 ldi ZH,high(frequency)
 ldi ZL,low(frequency)
 ldi YH,high(frequency_memory_0)
 ldi YL,low(frequency_memory_0)
 ldi my3,8
SLEF5:
 ld temp1,Z+
 rcall EEPROM_WRITE
 adiw YL,1 ; increment EEPROM memory pointer
 dec my3
 brne SLEF5
 ret

;**********************************
;* ShowFreq
;* uses temp1 & temp2
;***********************************
ShowFreq: ;display freq on LCD
 ldi temp2,$80
 rcall LCDCMD
 ldi temp1,' '
 rcall LCDCHR
 ldi ZH,high(frequency)
 ldi ZL,low(frequency)
 ld temp1,Z+
 rcall LCDCHR
 ld temp1,Z+
 rcall LCDCHR
 cpi temp1,' '
 breq ShowFreq3
 ldi temp1,','
ShowFreq3:
 rcall LCDCHR
 ld temp1,Z+
 rcall LCDCHR
 ld temp1,Z+
 rcall LCDCHR
 ld temp1,Z+
 rcall LCDCHR
 cpi temp1,' '
 breq ShowFreq5
 ldi temp1,','
ShowFreq5:
 rcall LCDCHR
 ld temp1,Z+
 rcall LCDCHR
 ld temp1,Z+
 rcall LCDCHR
 ld temp1,Z+
 rcall LCDCHR
 ldi temp1,' '
 rcall LCDCHR
 ldi temp1,'H'
 rcall LCDCHR
 ldi temp1,'z'
 rcall LCDCHR
 ldi temp1,' '
 rcall LCDCHR
 ret


multiply_freq_hex_by_10_and_add:
 push ZL
 push ZH

 push temp1
 ldi ZH,high(freq_hex)
 ldi ZL,low(freq_hex)
 ld mx3,Z+
 mov my3,mx3
 ld mx2,Z+
 mov my2,mx2
 ld mx1,Z+
 mov my1,mx1
 ld mx0,Z+
 mov my0,mx0

 lsl mx0
 rol mx1
 rol mx2
 rol mx3
 
 lsl mx0
 rol mx1
 rol mx2
 rol mx3 
 
 lsl mx0
 rol mx1
 rol mx2
 rol mx3

 add mx0,my0
 adc mx1,my1
 adc mx2,my2
 adc mx3,my3
 
 add mx0,my0
 adc mx1,my1
 adc mx2,my2
 adc mx3,my3

 pop temp1
 clr my0
 add mx0,temp1
 st -Z,mx0
 adc mx1,my0
 st -Z,mx1
 adc mx2,my0
 st -Z,mx2
 adc mx3,my0
 st -Z,mx3

 pop ZH
 pop ZL
 ret

update_freq_buffer_left:
 ldi ZH,high(frequency+1)
 ldi ZL,low(frequency+1)
 ld temp2,Z
 st -Z,temp2 
 adiw ZL,2
 ld temp2,Z
 st -Z,temp2 
 adiw ZL,2
 ld temp2,Z
 st -Z,temp2 
 adiw ZL,2
 ld temp2,Z
 st -Z,temp2 
 adiw ZL,2
 ld temp2,Z
 st -Z,temp2 
 adiw ZL,2
 ld temp2,Z
 st -Z,temp2 
 adiw ZL,2
 ld temp2,Z
 st -Z,temp2 
 adiw ZL,1
 st Z,temp1
 ret

display_freq_buffer:
 ldi ZH,high(frequency)
 ldi ZL,low(frequency)
 ld temp1,Z+
 rcall LCDCHR
 ld temp1,Z+
 rcall LCDCHR
 cpi temp1,' '
 breq dfb10
 ldi temp1,','
dfb10:
 rcall LCDCHR
 ld temp1,Z+
 rcall LCDCHR
 ld temp1,Z+
 rcall LCDCHR
 ld temp1,Z+
 rcall LCDCHR
 cpi temp1,' '
 breq dfb20
 ldi temp1,','
dfb20:
 rcall LCDCHR
 ld temp1,Z+
 rcall LCDCHR
 ld temp1,Z+
 rcall LCDCHR
 ld temp1,Z
 mov temp3,temp1 ; save last character
 rcall LCDCHR
 ret
 
get_keypad:
 sbi DDRB,0 ; make PB0 an output
 cbi PORTB,0 ; Clear Bit in I/O Register
 nop ; at least one 'nop' required for keypad operation
 nop 
 ldi temp1,'1'
 sbis PIND,6 ; Skip if Bit in I/O Register is Set
 rjmp grk_exit
 ldi temp1,'2'
 sbis PIND,7 ; Skip if Bit in I/O Register is Set
 rjmp grk_exit
 ldi temp1,'3'
 sbis PINB,4 ; Skip if Bit in I/O Register is Set
 rjmp grk_exit
 ;ldi temp1,'A'
 ;sbis PINB,5 ; Skip if Bit in I/O Register is Set
 ;rjmp grk_exit
 sbi PORTB,0 ; Set Bit in I/O Register
 cbi DDRB,0 ; make PB0 an input
 
 sbi DDRB,1 ; make PB1 an output
 cbi PORTB,1 ; Clear Bit in I/O Register
 nop ; at least one 'nop' required for keypad operation
 nop 
 ldi temp1,'4'
 sbis PIND,6 ; Skip if Bit in I/O Register is Set
 rjmp grk_exit
 ldi temp1,'5'
 sbis PIND,7 ; Skip if Bit in I/O Register is Set
 rjmp grk_exit
 ldi temp1,'6'
 sbis PINB,4 ; Skip if Bit in I/O Register is Set
 rjmp grk_exit
 ;ldi temp1,'B'
 ;sbis PINB,5 ; Skip if Bit in I/O Register is Set
 ;rjmp grk_exit
 sbi PORTB,1 ; Set Bit in I/O Register
 cbi DDRB,1 ; make PB1 an input
 
 sbi DDRB,2 ; make PB2 an output
 cbi PORTB,2 ; Clear Bit in I/O Register
 nop ; at least one 'nop' required for keypad operation
 nop
 ldi temp1,'7'
 sbis PIND,6 ; Skip if Bit in I/O Register is Set
 rjmp grk_exit
 ldi temp1,'8'
 sbis PIND,7 ; Skip if Bit in I/O Register is Set
 rjmp grk_exit
 ldi temp1,'9'
 sbis PINB,4 ; Skip if Bit in I/O Register is Set
 rjmp grk_exit
 ;ldi temp1,'C'
 ;sbis PINB,5 ; Skip if Bit in I/O Register is Set
 ;rjmp grk_exit
 sbi PORTB,2 ; Set Bit in I/O Register
 cbi DDRB,2 ; make PB2 an input
 
 sbi DDRB,3 ; make PB3 an output
 cbi PORTB,3 ; Clear Bit in I/O Register
 nop ; at least one 'nop' required for keypad operation
 nop 
 ldi temp1,'*'
 sbis PIND,6 ; Skip if Bit in I/O Register is Set
 rjmp grk_exit
 ldi temp1,'0'
 sbis PIND,7 ; Skip if Bit in I/O Register is Set
 rjmp grk_exit
 ldi temp1,'#'
 sbis PINB,4 ; Skip if Bit in I/O Register is Set
 rjmp grk_exit
 ;ldi temp1,'D'
 ;sbis PINB,5 ; Skip if Bit in I/O Register is Set
 ;rjmp grk_exit
 sbi PORTB,3 ; Set Bit in I/O Register
 cbi DDRB,3 ; make PB3 an input
 
 clr temp1
 rjmp grk_exit2
grk_exit:
 ldi delay,250 ; wait 250 milliseconds to remove bounce
 rcall WAIT
grk_exit2:
 sbi PORTB,0 ; Set Bit in I/O Register
 sbi PORTB,1 ; Set Bit in I/O Register
 sbi PORTB,2 ; Set Bit in I/O Register
 sbi PORTB,3 ; Set Bit in I/O Register
 cbi DDRB,0 ; make PB0 an input
 cbi DDRB,1 ; make PB1 an input
 cbi DDRB,2 ; make PB2 an input
 cbi DDRB,3 ; make PB3 an input
 ret
	
DISPLAY_LINE:
	lpm		temp1,Z+
	tst		temp1
	breq	DISPLAY_LINE_FINIS
	rcall	LCDCHR
	rjmp	DISPLAY_LINE
DISPLAY_LINE_FINIS:
 ldi XH,high(cursor_number_sram)
 ldi XL,low(cursor_number_sram)
 ld temp2,X
 cpi temp2,4
 brlo cursor9
 inc temp2
 cpi temp2,8
 brlo cursor9
 inc temp2
cursor9:
 ldi temp1,$8B
 sub temp1,temp2
 mov temp2,temp1
 rcall LCDCMD
 ret
 
;************************************
;* WAIT
;* temp1 = WAIT time in milliseconds
;************************************
WAIT:
	tst		delay
	brne	WAIT
	ret

;************************************
;* BLINK_LED
;* temp1 = number of times LED BLINKS
;************************************
BLINK_LED:
	tst		temp1
	breq	BLINK_LED_EXIT
BLINK_LED2:
	cbi		PORTC,LED				;turn LED on
	ldi		delay,250				;250 mSec
	rcall WAIT
	sbi		PORTC,LED				;turn LED off		
	ldi		delay,250				;250 mSec
	rcall	WAIT
	dec   temp1
	rjmp	BLINK_LED
BLINK_LED_EXIT:
	ret
	
;************************************
;* LCDCMD - Send a command to the LCD
;* temp2 = cmd
;*****************************************
;* LCDCHR - Display a character on the LCD
;* temp1 = data
;************************************
LCDCMD:
  push temp1
  mov temp1,temp2
	rcall	SENDBYTE
	cbi		PORTC,LCD_DRS			;set RS low
	rjmp	LCDEXEC
LCDCHR:
  push temp1
	rcall	SENDBYTE
	sbi		PORTC,LCD_DRS			;set RS High
LCDEXEC:
	sbi		PORTC,LCD_E				;E clocks on rising edge
	ldi		delay,2				;wait 2 milliseconds
	cbi		PORTC,LCD_E				;set E low
	rcall	WAIT
	pop temp1
	ret
	
;**********************************
;* SENDBYTE clock byte into 74hc164
;* temp1 = data
;* temp2 = bitcounter
;***********************************
SENDBYTE:
  push temp1
	push	temp2
	ldi		temp2,8					;bit counter
ShiftBits:
	cbi		PORTC,LCD_DRS   ;set LCD Data bit to 0
	lsl		temp1						;shift bits in AReg into carry
	brcc	ClockBit				;if zero, clock it out
	sbi		PORTC,LCD_DRS   ;if 1, set LCD Data bit to 1
ClockBit: 
	sbi		PORTC,LCD_CP		;bit clocks on rising edge
	cbi		PORTC,LCD_CP		;bring clock back to 0
	dec		temp2						;shift out 8 bits
	brne	ShiftBits
	pop		temp2
	pop temp1
	ret

.if encoder_type  ==  2

EXT_INT0: ; logic change on INT0 (PD2, output_a)
	push	temp1           ;save temp1 register
	in		temp1,SREG      ;save the status register
	push	temp1
  in    temp1,  PIND   ;
  andi  temp1,  $0C
  breq  INT0_1          ; if both bits are clear decrement encoder
  cpi   temp1,  $0C
  breq  INT0_1          ; if both bits are set decrement encoder
  inc		encoder         ; else increment encoder
  rjmp	INT0_EXIT
INT0_1:
  dec		encoder
INT0_EXIT:
	pop		temp1
	out		SREG,temp1      ;restore the status register
	pop		temp1           ;restore temp1 register
  reti
  
EXT_INT1: ; logic change on INT1 (PD3, output_b)
	push	temp1           ;save temp1 register
	in		temp1,SREG      ;save the status register
	push	temp1
  in    temp1,  PIND    ;
  andi  temp1,  0b00001100
  breq  INT1_1          ; if both bits are clear decrement encoder
  cpi   temp1,  0b00001100
  breq  INT1_1          ; if both bits are set decrement encoder
  dec		encoder         ; else increment encoder
  rjmp	INT1_EXIT
INT1_1:
  inc		encoder
INT1_EXIT:
	pop		temp1
	out		SREG,temp1      ;restore the status register
	pop		temp1           ;restore temp1 register
  reti

.elif encoder_type  ==  1

EXT_INT0:
	reti
	
EXT_INT1:
	push	temp1 ;save temp1 register
	in		temp1,SREG ;save the status register
	push	temp1
	lds		temp1,EICRA
	cpi		temp1,0b00001000	; test falling edge
	breq	int15
	ldi		temp1,0b00001000	; set int1 on falling edge
	sts		EICRA,temp1
	sbis	PIND,PHASE ; test PHASE
	rjmp	int11
	dec		encoder
	rjmp	int19
int11:
	inc		encoder
	rjmp	int19
int15:
	ldi		temp1,0b00001100	; set int1 for rising edge
	sts		EICRA,temp1
	sbis	PIND,PHASE ; test PHASE
	rjmp	int16
	inc		encoder
	rjmp	int19
int16:
	dec		encoder
int19:
	pop		temp1
	out		SREG,temp1 ;restore the status register
	pop		temp1   ;restore temp1 register
	reti

.else

EXT_INT1:
	reti

.endif
  
;**********************************
;* Timer 0 Overflow
;* gets here every 1 millisecond
;* decrements the DELAY counter
;***********************************     	
OVF0:
	push	temp1
	in		temp1,  SREG
	push	temp1
	ldi		temp1,  256-125   ; 1 ms using default clock values
	out		TCNT0,  temp1			; set for next overflow
	tst		delay
	breq	OVF0_EXIT
	dec		delay
OVF0_EXIT:
	pop		temp1
	out		SREG, temp1
	pop		temp1
	reti

;**********************************
;* SHOWHEX_LINE
;* display HEX bytes from non-program memory on line 2
;* byte count in temp2
;*********************************** 
SHOWHEX_LINE:
	ldi		temp2,8			;display 8 hex bytes
SHOWHEX_LINE_2:
	ld		temp1,Z+		;get display byte
;preserves temp1 and temp2 values
	push	temp1
	push	temp2
	push	temp1
	swap	temp1
	andi	temp1,$0F
	ori		temp1,$30
	cpi		temp1,$3A
	brlo	SHOWHEX2
	ldi		temp2,$07
	add		temp1,temp2
SHOWHEX2:
	rcall	LCDCHR
	pop		temp1
	andi	temp1,$0F
	ori		temp1,$30
	cpi		temp1,$3A
	brlo	SHOWHEX3
	ldi		temp2,$07
	add		temp1,temp2
SHOWHEX3:
	rcall	LCDCHR
	pop		temp2
	pop		temp1
	dec		temp2
	brne	SHOWHEX_LINE_2
	ret

 
EEPROM_READ:
 cli
 sbic EECR,EEPE
 rjmp EEPROM_READ ; Wait for completion of previous write
 out EEARH,YH ; Set up address register
 out EEARL,YL
 sbi EECR,EERE ; Start eeprom read by writing EERE
 in temp1,EEDR ; Read data from data register
 sei
 ret

EEPROM_WRITE:
 cli
 sbic EECR,EEPE
 rjmp EEPROM_WRITE; Wait for completion of previous write
 ldi temp2,0 ; Set Programming mode
 out EECR,temp2
 out EEARH,YH ; Set up address register
 out EEARL,YL
 out EEDR,temp1 ; Write data temp1 to data register
 sbi EECR,EEMPE ; Write logical one to EEMPE
 sbi EECR,EEPE ; Start eeprom write by setting EEPE
 sei
 ret

;************************************
INIT_LCD:
;* uses temp2 
;************************************
	ldi		delay,20			;wait 20 milliseconds for system to stabilize
	rcall	WAIT
	ldi		temp2,$30
	rcall LCDCMD
	ldi		delay,4				;wait 4 milliseconds
	rcall	WAIT	
	ldi		temp2,$38			;set for 8 bits, 2 lines, default font
	rcall LCDCMD
	ldi		temp2,$01			;Clear screen, CURSOR home
	rcall LCDCMD
	ldi		temp2,$0E			;Display on, CURSOR on, blink off
	rcall LCDCMD
	ret

process_encoder:
 ldi XH,high(cursor_number_sram)
 ldi XL,low(cursor_number_sram)
 sbic PIND,button ; Skip if Bit in I/O Register is Cleared (button is proessed)
 rjmp PE050
 ld temp2,X
 tst encoder
 brpl cursor_right
 inc temp2
 cpi temp2,9
 brlo cursor_exit
 ldi temp2,1
 rjmp cursor_exit
cursor_right:
 dec temp2
 brne cursor_exit
 ldi temp2,8
cursor_exit:
 st X,temp2
 clr encoder
 rcall ShowFreq
 rjmp MENU 
PE050:
 ldi ZH,high(frequency+8)
 ldi ZL,low(frequency+8)
 ld temp1,X ; get cursor
 ldi temp2,8
 sub temp2,temp1
PE100:
 sbiw ZL,1
 dec temp1
 brne PE100
 tst encoder
 brpl freq_positive
freq_negative:
 ld temp1,Z
 dec temp1
 cpi temp1,$30
 brsh SP200
 ldi temp1,$39
 st Z,temp1
 sbiw ZL,1
 rjmp freq_negative
 rjmp SP200
freq_positive:
 ld temp1,Z
 inc temp1
 cpi temp1,$3A
 brlo SP200
 ldi temp1,$30
 st Z,temp1
 sbiw ZL,1
 rjmp freq_positive
SP200:
 st Z,temp1
 call ASCII8_HEX4
PE999:
 clr encoder
 jmp MENU
 
