; ATtiny.105.asm
; version 14 Oct 2021
; author: W8DIZ

; i2c tutorial via youtube: https://www.youtube.com/watch?v=7CgNF78pYQM
; i2c bus sec: https://kitsandparts.com/tutorials/learn.i2c/I2C_bus_spec.pdf

; cut and paste the desired AVRA or AVRDUDE commands to the command line on the LINUX TERMINAL PROGRAM.

; Set up the working directory for avra ==> cd ~/Desktop/freddieneil.com/kitsandparts.com/tutorials/ATtiny/105/ ***********************************************************

; Assemble the code ==> avra ATtiny.105.asm  ***********************************************************

; To enables PB5 and disable External Reset:
; for ATtiny13, clear bit 0 in Fuse High Byte.
; for ATtiny25/45/85, clear bit 7 in Fuse High Byte.
; Must use high voltage programmer like the dragon_hvsp if PB5 enabled

;  avrispv2 * avrispv2 * avrispv2 * avrispv2 * avrispv2 * avrispv2 * avrispv2 * avrispv2 * avrispv2 * avrispv2 * avrispv2 * 

; avrdude -P usb -p t13 -c avrispv2 -U ATtiny.105.hex
; avrdude -P usb -p t13 -c avrispv2 -U lfuse:w:0x6A:m -U hfuse:w:0xFF:m
; avrdude -P usb -p t13 -c avrispv2 -U ATtiny.105.hex -U lfuse:w:0x6A:m -U hfuse:w:0xFF:m

; avrdude -P usb -p t45 -c avrispv2 -U ATtiny.105.hex
; avrdude -P usb -p t45 -c avrispv2 -U lfuse:w:0x62:m -U hfuse:w:0xDF:m -U efuse:w:0xFF:m
; avrdude -P usb -p t45 -c avrispv2 -U ATtiny.105.hex -U lfuse:w:0x62:m -U hfuse:w:0xDF:m -U efuse:w:0xFF:m

; avrdude -P usb -p t85 -c avrispv2 -U ATtiny.105.hex -U eeprom:w:ATtiny.105.eep.hex ***********************************************************
; avrdude -P usb -p t85 -c avrispv2 -U lfuse:w:0x62:m -U hfuse:w:0xDF:m -U efuse:w:0xFF:m
; avrdude -P usb -p t85 -c avrispv2 -U ATtiny.105.hex -U lfuse:w:0x62:m -U hfuse:w:0xDF:m -U efuse:w:0xFF:m

; dragon_hvsp * dragon_hvsp * dragon_hvsp * dragon_hvsp * dragon_hvsp * dragon_hvsp * dragon_hvsp * dragon_hvsp * dragon_hvsp

; avrdude -P usb -p t13 -c dragon_hvsp -U ATtiny.105.hex
; avrdude -P usb -p t13 -c dragon_hvsp -U lfuse:w:0x6A:m -U hfuse:w:0xFF:m
; avrdude -P usb -p t13 -c dragon_hvsp -U ATtiny.105.hex -U lfuse:w:0x6A:m

; avrdude -P usb -p t45 -c dragon_hvsp -U ATtiny.105.hex
; avrdude -P usb -p t45 -c dragon_hvsp -U lfuse:w:0x62:m -U hfuse:w:0xDF:m -U efuse:w:0xFF:m
; avrdude -P usb -p t45 -c dragon_hvsp -U ATtiny.105.hex -U lfuse:w:0x62:m -U hfuse:w:0xDF:m -U efuse:w:0xFF:m

; avrdude -P usb -p t85 -c dragon_hvsp -U ATtiny.105.hex
; avrdude -P usb -p t85 -c dragon_hvsp -U lfuse:w:0x62:m -U hfuse:w:0xDF:m -U efuse:w:0xFF:m
; avrdude -P usb -p t85 -c dragon_hvsp -U ATtiny.105.hex -U lfuse:w:0x62:m -U hfuse:w:0xDF:m -U efuse:w:0xFF:m


; define ATtiny version

.equ  ATtiny13  = 0   ; Declares the symbol ATtiny13
.equ  ATtiny25  = 0   ; Declares the symbol ATtiny25
.equ  ATtiny45  = 0   ; Declares the symbol ATtiny45
.equ  ATtiny85  = 1   ; Declares the symbol ATtiny85

; 1-Rst--------Vcc-8
; 2-PB3--------PB2-7
; 3-PB4--------PB1-6
; 4-Gnd--------PB0-5

.nolist

; conditional assembly: .define .undef .if .ifdef .ifndef .else .elif .endif .error .warning .message 
.if    ATtiny13  ==  1
  .include  "../../include/tn13def.inc"
  .message  "tn13def.inc selected"
.elif  ATtiny25  ==  1
  .include  "../../include/tn25def.inc"
  .message  "tn25def.inc selected"
.elif  ATtiny45  ==  1
  .include  "../../include/tn45def.inc"
  .message  "tn45def.inc selected"
.elif  ATtiny85  ==  1
  .include  "../../include/tn85def.inc"
  .message  "tn85def.inc selected"
.else
  .error  "include file not specified"
.endif

.list
 
.equ  PINx      = PINB
.equ  DDRx      = DDRB
.equ  PORTx     = PORTB
.equ  sda_bit   = portb0
.equ  scl_bit   = portb1
.equ  lcd_e     = portb2
.equ  tune_bit  = portb3
.equ  led_bit   = portb4
.equ  not_used  = portb5
.equ  si5351_i2cAddr  = 0xC0  ; si5351A address = 0x60 but needs to shift left 1 bit

; Define register names
; r0 is used by Z-registor
; NOTE: registers r0-r15 are not available for all instructions
.def  lastPot = r1

.def  temp29  = r2  ; r29_VCOa P1[15:8]
.def  temp30  = r3  ; r30_VCOa P1[7:0]
.def  temp31  = r4  ; r31_VCOa P3[19:16]:P2[19:16]
.def  temp32  = r5  ; r32_VCOa P2[15:8]
.def  temp33  = r6  ; r33_VCOa P2[7:0]
.def  temp34  = r7  ; extra byte for finer inc/dec resolution

.def  step0   = r8  ; high byte add/sub for inc/dec
.def  step1   = r9  ; next byte add/sub for inc/dec
.def  step2   = r10 ; next byte add/sub for inc/dec
.def  step3   = r11 ; next byte add/sub for inc/dec
.def  step4   = r12 ; low byte add/sub for inc/dec

.def  temp1   = r16
.def  temp2   = r17
.def  temp3   = r18
.def  temp4   = r19
.def  delay   = r20
.def  i2cData = r21
.def	si5351_regNum = r22
.def	si5351_regVal = r23
; r24-r31 used as 16 bit register pointers W, X, Y ,Z

.equ  PARAMS  = 71    ; Declares frequency PARAMS options: 71, 72, 10 - MHz

.eseg
.org 0
.if PARAMS == 10
; 100 Hz sets at 10 MHz, A=24, D=60, XTAL=25 MHz
REGS2933_EE: .db $0A,$00,$F0,$00,$00,$00  ;  (.db = numbers from 0..255, an ASCII-character like 'c', a string like 'abcde' or a combination like 1,2,3,'abc'.)
CR44EE: .db $00
CR45EE: .db $1C
STEP_EE:  .db $00,$7D,$D4,$39,$58
.elif PARAMS == 71
; 100 Hz sets at 7 MHz, A=35, D=100, XTAL=20 MHz
REGS2933_EE: .db $0F,$80,$F0,$00,$00,$00  ;  (.db = numbers from 0..255, an ASCII-character like 'c', a string like 'abcde' or a combination like 1,2,3,'abc'.)
CR44EE: .db $00
CR45EE: .db $30
STEP_EE:  .db $01,$06,$24,$BC,$6A
.elif PARAMS == 72
; 100 Hz sets at 7 MHz, A=28, D=100, XTAL=25 MHz
REGS2933_EE: .db $0C,$00,$F0,$00,$00,$00  ;  (.db = numbers from 0..255, an ASCII-character like 'c', a string like 'abcde' or a combination like 1,2,3,'abc'.)
CR44EE: .db $00
CR45EE: .db $30
STEP_EE:  .db $00,$D1,$B6,$E9,$79
.else
.endif


.dseg
.org SRAM_START
CR_EE: .byte 5; buffer for working registers 29-33 (Reserves one or more bytes space in the data segment; only used to produce correct labels, does not insert any values!)
 
.cseg
.org 0

.if ATtiny13  ==  1
  rjmp   RESET        ; Reset Handler
  reti  ; EXT_INT0    ; IRQ0 Handler
  reti  ; PCINT0      ; PCINT0 Handler
  rjmp  TIMER0_OVF    ; Timer0 Overflow
  reti  ; EE_RDY      ; EEPROM Ready Handler
  reti  ; ANA_COMP    ; Analog Comparator Handler
  reti  ; TIM0_COMPA  ; Timer0 CompareA Handler
  reti  ; TIM0_COMPB  ; Timer0 CompareB Handler
  reti  ; WATCHDOG    ; Watchdog Interrupt Handler
  reti  ;	ADC         ; ADC Conversion Handler
.else
  rjmp  RESET
  reti  ; EXT_INT0    ; External Interrupt 0
  reti  ; PCI0        ; Pin change Interrupt Request 0
  reti  ; OC1A        ; Timer/Counter1 Compare Match 1A
  reti  ; TIMER1_OVF  ; Timer/Counter1 Overflow
  rjmp  TIMER0_OVF    ; Timer/Counter0 Overflow
  reti  ; EE_RDY      ; EEPROM Ready
  reti  ; ANA_COMP    ; Analog comparator
  reti  ; ADCC        ; ADC Conversion ready
  reti  ; OC1B        ; Timer/Counter1 Compare Match B
  reti  ; OC0A        ; Timer/Counter0 Compare Match A
  reti  ; OC0B        ; Timer/Counter0 Compare Match B
  reti  ; WATCHDOG    ; Watchdog Interrupt Handler
  reti  ; USI_START   ; USI START
  reti  ; USI_OVF     ; USI Overflow
.endif

RESET:  ; init stack pointer
  ldi   temp1,  low(RAMEND)
  out   SPL,    temp1 ; Set stack pointer to last internal RAM location
.if ATtiny13  ==  1
.elif  ATtiny25  ==  1
.else
  ldi   temp1,  high(RAMEND)
  out   SPH,    temp1
.endif

adc_init:
 ldi temp1,0b00100000 ; use ADC channel 0, left adjusted, vref = Vcc
 out ADMUX, temp1
 ldi temp1, 0b11100111 ;from left to right: ADC Enable, Start Conversion, Free-Running Mode, write zero to ADC Int flag
 out ADCSRA, temp1 ; disable int, prescaler: 111 for XTAL/128

led_init:
  sbi   PORTx,  led_bit   ; set led_bit to turn off LED
  sbi   DDRx,   led_bit   ; set led_bit to enable output for LED control

i2c_init:
	cbi   DDRx,   sda_bit ; make sda_bit an input - release Si5351A sda_bit i2c sda line - float high via pull up resistor
	cbi   DDRx,   scl_bit ; make scl_bit an input - release Si5351A scl_bit i2c scl line - float high via pull up resistor
	cbi   PORTx,  sda_bit ; set sda_bit to logic "0" (when DDRx(sda_bit) changes to an output)
	cbi   PORTx,  scl_bit ; set scl_bit to logic "0" (when DDRx(scl_bit) changes to an output)

.if ATtiny13  ==  1
  ; set System Clock Prescaler - system default clock is 9.6 MHz - can be changed to 4.8 MHz by Fuse Low Byte
  ldi   temp1,  0b10000000  ; init for prescaler change
  out   CLKPR,  temp1       ; else out instruction to CLKPR will not take effect
  ; ldi   temp1,  0b00000000  ; prescaler / 1
  ; ldi   temp1,  0b00000001  ; prescaler / 2
  ; ldi   temp1,  0b00000010  ; prescaler / 4 - ( 9.6 MHz / 4) = 2.400 MHz = 417 nanoseconds
  ldi   temp1,  0b00000011  ; prescaler / 8 - default set by Fuse Low Byte ( 9.6 MHz / 8) = 1.200 MHz = 833 nanoseconds
  ; ldi   temp1,  0b00000100  ; prescaler / 16 - ( 9.6 MHz / 16) = 600 KHz = 1.667 microseconds
  ; the setting below disable reprogramming the uP - must use "-B 500" with AVRDUDE to fix
  ; ldi   temp1,  0b00000101  ; prescaler / 32 - ( 9.6 MHz / 32) = 300 KHz = 3.333 microseconds
  ; ldi   temp1,  0b00000110  ; prescaler / 64 - ( 9.6 MHz / 64) = 150 KHz = 6.667 microseconds
  ; ldi   temp1,  0b00000111  ; prescaler / 128 - ( 9.6 MHz / 128) = 75 KHz = 13.333 microseconds
  ; ldi   temp1,  0b00001000  ; prescaler / 256 - ( 9.6 MHz / 256) = 37500 Hz = 26.667 microseconds
  out   CLKPR,  temp1
.else
  ; set System Clock Prescaler - system default clock is 8.0 MHz - can be changed to 6.4 MHz by Fuse Low Byte
  ldi   temp1,  0b10000000  ; init for prescaler change
  out   CLKPR,  temp1       ; else out instruction to CLKPR 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
  out   CLKPR,  temp1
.endif 

; with prescaler set to 8:
; ATtiny13 - set Timer/Counter Prescaler to System Clock Source
; ATtinyx5 - set Timer/Counter Prescaler to System Clock Source
; ldi   temp1,  0b00000001  ; no prescaling
  ldi   temp1,  0b00000010  ; CLK 1/8 from prescaler (6.667 uS fo ATtiny13, 8.000 uS for ATtinyx5)
; 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,  0b00000010  ; overflow interrupt enable bit
.if ATtiny13  ==  1
  out   TIMSK0, temp1       ; enable timer/counter0 overflow interrupt
.else    
  out   TIMSK,  temp1       ; enable TIMER0 overflow interrupts
.endif

  ldi YH, high(REGS2933_EE)
  ldi YL, low(REGS2933_EE)
  rcall   EEPROM_READ ; get EEPROM value to register temp1
  mov temp29, temp1
  rcall   EEPROM_READ ; get EEPROM value to register temp1
  mov temp30, temp1
  rcall   EEPROM_READ ; get EEPROM value to register temp1
  mov temp31, temp1
  rcall   EEPROM_READ ; get EEPROM value to register temp1
  mov temp32, temp1
  rcall   EEPROM_READ ; get EEPROM value to register temp1
  mov temp33, temp1
  rcall   EEPROM_READ ; get EEPROM value to register temp1
  mov temp34, temp1   ; temp34 is a dummy for better INC/DEC resolution

  ldi YH, high(STEP_EE)
  ldi YL, low(STEP_EE)
  rcall   EEPROM_READ ; get EEPROM value to register temp1
  mov step0,  temp1
  rcall   EEPROM_READ ; get EEPROM value to register temp1
  mov step1,  temp1
  rcall   EEPROM_READ ; get EEPROM value to register temp1
  mov step2,  temp1
  rcall   EEPROM_READ ; get EEPROM value to register temp1
  mov step3,  temp1
  
  ldi   temp1,  $0F
  mov   lastPot,  temp1
    
  sei   ; global all interrupt enable
 
  ldi   delay,  10
  rcall WAIT
  
  rcall Si5351
  
main:   ; start of main program

.if ATtiny13  ==  1
  ldi  delay,  250   ; 250 * 1 mS = 250 mS for ATtiny13
.else
  ldi  delay,  250   ; 250 * 2 mS = 500 mS for ATtinyx5
.endif
  
read_potentiometer:
  ldi   temp1,    0b00100011 ; select ADC 03 = Tunning
  out   ADMUX,    temp1 ; AVcc, left adjust( we need just ADCH only), ADC0
  sbi   ADCSRA,   ADSC  ; start ADC conversion
keep_pooling:
  sbis  ADCSRA,   ADIF  ; look for ADC conversion finish flag ADIF
  rjmp  keep_pooling
  sbi   ADCSRA,   ADIF   
  in    temp1,    ADCH  ; get pot value

  lsr   temp1
  lsr   temp1
  lsr   temp1
  
  cp    temp1,    lastPot
  breq  exit_potentiometer
  
  mov   lastpot,  temp1
  
  cpi   temp1,  $0F
  brlo  DN_STEP
  rcall STEP_UP
  rjmp  UPDATE
DN_STEP:
  rcall STEP_DN
UPDATE:
  rcall UPDATE_2930313233
  sbi   PINx,     led_bit ; toggle LED on/off
exit_potentiometer:
  rjmp main

STEP_UP:
  clr   temp1
  add   temp34, step3
  adc   temp33, step2
  adc   temp32, step1
  adc   temp31, step0
  adc   temp30, temp1
  adc   temp29, temp1
  ldi   temp1,  $F0
  or    temp31, temp1
  ret

STEP_DN:
  clr   temp1
  ldi   temp1,  $0F
  and   temp31, temp1
  clr   temp1
  sub   temp34, step3
  sbc   temp33, step2
  sbc   temp32, step1
  sbc   temp31, step0
  sbc   temp30, temp1
  sbc   temp29, temp1
  ldi   temp1,  $F0
  or    temp31, temp1
  ret

WAIT:
  tst   delay
  brne  WAIT
  ret

Si5351:  ; demo freq set for 7.000 MHz
  ldi si5351_regNum, 3
  ldi si5351_regVal, 0xFF
  rcall i2cSend   ; disable all outputs

  ldi   si5351_regNum, 16
  ldi   si5351_regVal, 0x80
  rcall i2cSend   ; power down clock0

  ldi   si5351_regNum, 17
  ldi   si5351_regVal, 0x80
  rcall i2cSend   ; power down clock1

  ldi   si5351_regNum, 18
  ldi   si5351_regVal, 0x80
  rcall i2cSend   ; power down clock2

  ;ldi   si5351_regNum, 15
  ;ldi   si5351_regVal, 0x00   ; default is 0x00  
  ;rcall i2cSend   ; select PLLa,b source and use xtal input

  ;ldi   si5351_regNum, 24
  ;ldi   si5351_regVal, 0x00   ; default is 0x00  
  ;rcall i2cSend   ; clock disable state

  ldi si5351_regNum, 149
  ldi si5351_regVal, 0    ; disable spread spectrum on PLLa
  rcall i2cSend   ; reset both PLLs
  
; Output Multisynth0, e=0, f=1, MS0_P2 and MS0_P3
  ldi   si5351_regNum, 42
  ldi   si5351_regVal, 0
  rcall i2cSend   ; set MS0_P3[15:8]

  ldi   si5351_regNum, 43
  ldi   si5351_regVal, 1
  rcall i2cSend   ; set MS0_P3[7:0]
  
  ldi   si5351_regNum, 47
  ldi   si5351_regVal, 0
  rcall i2cSend   ; set MS0_P3[19:16] | MS0_P2[19:16]

  ldi   si5351_regNum, 48
  ldi   si5351_regVal, 0
  rcall i2cSend   ; set MS0_P2[15:8]

  ldi   si5351_regNum, 49
  ldi   si5351_regVal, 0
  rcall i2cSend   ; set MS0_P2:[7:0]
  
  ldi   si5351_regNum, 16
  ldi   si5351_regVal, 0x4C
  rcall i2cSend   ; power up clock0, MS0 in integer mode,  PLLa and 2 mA

  ;ldi si5351_regNum, 183
  ;ldi si5351_regVal, 0x52 ;$D2=default 10pF, $92=8pF, $52=6pF
  ;rcall i2cSend   ; reset both PLLs

  ldi si5351_regNum, 177
  ldi si5351_regVal, 0xA0
  rcall i2cSend   ; reset both PLLs
  
  ldi si5351_regNum, 3
  ldi si5351_regVal, 0xFE
  rcall i2cSend   ; enable clock0 output only 
  
; Load PLLa Feedback Multisynth 26,27,28,29,30,31,32,33

  ldi   si5351_regNum,  26
  ldi   si5351_regVal,  0xff
  rcall i2cSend   ; set MSNA_P3[15:8]

  ldi   si5351_regNum,  27
  ldi   si5351_regVal,  0xff
  rcall i2cSend   ; set MSNA_P3[7:0]

  ldi   si5351_regNum,  28
  ldi   si5351_regVal,  0
  rcall i2cSend   ; set MSNA_P1[17:16]
  
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  rcall UPDATE_2930313233
   
; Load Output Multysynth with d (e and f already set during init and never changes) 44,45,46

  ldi   si5351_regNum,  44
  ldi   si5351_regVal,  0
  rcall i2cSend   ; set MS0_P1[17:16]

  ldi   si5351_regNum,  45
  ldi   si5351_regVal,  0x30
  rcall i2cSend   ; set MS0_P1[15:8]

  ldi   si5351_regNum,  46
  ldi   si5351_regVal,  0
  rcall i2cSend   ; set MS0_P1[7:0]

  ldi si5351_regNum, 177
  ldi si5351_regVal, 0x20
  rcall i2cSend   ; reset PLLa only
  ret
  
UPDATE_2930313233:
  ldi   si5351_regNum,  29
  mov   si5351_regVal,  temp29
  rcall i2cSend   ; set MSNA_P1[15:8]
  
  ldi   si5351_regNum,  30
  mov   si5351_regVal,  temp30
  rcall i2cSend   ; set MSNA_P1[7:0]
  
  ldi   si5351_regNum,  31
  mov   si5351_regVal,  temp31
  rcall i2cSend   ; set MSNA_P3[19:16] | MSNA_P2[19:16]
  
  ldi   si5351_regNum,  32
  mov   si5351_regVal,  temp32
  rcall i2cSend   ; set MSNA_P2[15:8]
  
  ldi   si5351_regNum,  33
  mov   si5351_regVal,  temp33
  rcall i2cSend   ; set MSNA_P2[7:0]
  ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
i2cSend:  ; (uses si5351_regNum, si5351_regVal)
	rcall	i2c_start				  ; Send start condition and address
	MOV		i2cdata,  si5351_regNum	; Write word address (0x00)
	rcall	i2c_write_8bits 	  ; Execute transfer
	MOV		i2cdata,  si5351_regVal	; Set write data to 01010101b
	rcall	i2c_write_8bits 	  ; Execute transfer
	rcall	i2c_stop				  ; Send stop condition
	ret

i2c_start:
  ldi   i2cdata,  si5351_i2cAddr     ; load i2c device address to transmit register
  sbi   DDRx,     sda_bit   ; force data line low
  rcall i2cDelay_50
  sbi   DDRx,     scl_bit   ; force clock line low
  rcall i2cDelay_50
i2c_write_8bits:
  sec                       ; set carry flag
  rol   i2cdata             ; shift in carry bit and out bit into carry bit
i2c_write_bit:
  breq  i2c_get_ack
  brcc  i2c_bit_write_low
clock_it_high:
  cbi   DDRx,   sda_bit     ; release sda_bit - float sda high
  rjmp  clock_it
i2c_bit_write_low:
  sbi   DDRx,   sda_bit     ; force sda line low
clock_it:
  rcall i2cDelay_25
  cbi   DDRx,   scl_bit     ; release scl_bit - float scl high
  rcall i2cDelay_25
  sbi   DDRx,   scl_bit     ; force scl_bit low
  rcall i2cDelay_25
next_i2c_byte:
  lsl i2cdata
  rjmp  i2c_write_bit
  
i2c_get_ack:
  cbi   DDRx,   sda_bit     ; release sda_bit - float sda high
  rcall i2cDelay_25
  cbi   DDRx,   scl_bit     ; release scl_bit - float scl high
  rcall i2cDelay_25
i2c_get_ack_wait:
  sbis  PINx,   scl_bit     ; wait sda_bit high
  rjmp  i2c_get_ack_wait
  sbi   DDRx,   sda_bit     ; force sda_bit low
  rcall i2cDelay_25
  sbi   DDRx,   scl_bit     ; force scl_bit low
  rcall i2cDelay_25
  clc                       ; clear carry flag
  sbic  PINx,   sda_bit     ; if sda_bit is high
  sec                       ; set carry flag
  rcall i2cDelay_50
  ret  

i2c_stop:
  sbi   DDRx,   scl_bit     ; force scl_bit low
  sbi   DDRx,   sda_bit     ; force sda_bit low
  rcall i2cDelay_25
  cbi   DDRx,   scl_bit     ; release scl_bit
  rcall i2cDelay_25
  cbi   DDRx,   sda_bit     ; release sda_bit
  rcall i2cDelay_25
  ret
  
i2cDelay_50:
  ldi temp1, 3 ; total delay = 40(cycles) * 125 nS = 5.0 uS
  rjmp  i2cDelay_loop
i2cDelay_25:
  ldi temp1, 2  ; total delay = 20(cycles) * 125 nS = 2.5 uS
i2cDelay_loop:
  in    temp2,  SREG
  push  temp2
i2cDelay_loop2:
; with System Clock Prescaler set to /1
; 104 nS per clock for ATtiny13
;	125 nS per clock for ATtinyx5
; rcall instruction takes 2 clock cycles
; ret instruction takes 4 clock cycles
;	6 cycles * 0.104 uS per clock for ATtiny13 = 624 nS
;	6 cycles * 0.125 uS per clock for ATtinyx5 = 750 nS
; ldi & brne instructions take 1 clock cycle each
  dec temp1
  brne  i2cDelay_loop2
  pop   temp2
  out   SREG,   temp2
  ret
	
; Data transfer is initiated with a start condition (S) signaled by sda_bit being pulled low while SCL stays high.
  
TIMER0_OVF:
; with System Clock Prescaler set to /8
; and Timer/Counter Prescaler set to /8
; and TCNT0 loaded with 150 or 250 (see below)
  push  temp1
  push  temp2
  in    temp1,  SREG
  push  temp1
.if ATtiny13  ==  1
  ldi   temp1,  256-150 ;	1 mS for ATtiny13 ; 6.667 uS per count
.else
  ldi   temp1,  256-250 ;	2 mS for ATtinyx5 ; 8.000 uS per count
.endif
  out   TCNT0,  temp1   ;	set for next overflow
  tst   delay
  breq  TIM0_exit
  dec   delay
TIM0_exit:
  pop   temp1
  out   SREG,   temp1
  pop   temp2
  pop   temp1
  reti

EEPROM_READ: ; read one eeprom data byte into temp1, pointed to YH,YL
 cli
 sbic EECR,EEPE
 rjmp EEPROM_READ ; Wait for completion of previous write
 out EEARH,YH ; Set up eeprom address register high byte
 out EEARL,YL ; Set up eeprom address register low byte
 sbi EECR,EERE ; Start eeprom read by writing EERE
 in temp1,EEDR ; Read data from data register
 adiw YL,1 ; increment EEPROM memory pointer for possible next read
 sei
 ret

EEPROM_WRITE: ; write one byte from temp1 to eeprom data pointed by YH,YL
 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 eeprom address register high byte
 out EEARL,YL ; Set up eeprom address register low byte
 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
 adiw YL,1 ; increment EEPROM memory pointer for possible next write
 sei
 ret

