;***********************************************************************
;* Code Utilities: Copyright © 2002 Alpha Omega Media, Inc. All Rights Reserved.
;***********************************************************************
; registers R26:R27 (X), R28:29 (Y) and R30:31 (Z) reserved as mem pointers
.equ RemainderInit = 0x40000000 ; initial remainder for Root()
.equ DividendInit = 0x60000000 ; initial dividend for Ratio()
; SQ[] holds the square from the AC volt readings, and the divisor for ohms
.def SQ0 = R10 ; Low Byte, Word 0 of square
.def SQ1 = R11 ; Hi Byte, Word 0 of square
.def SQ2 = R12 ; Low Byte, Word 1 of square
.def SQ3 = R13 ; Hi Byte, Word 1 of square
; General variables - Temp0 and Temp1 are maintained across interrupts
.def Temp0 = R16 ; Temporary storage register; Global
.def Temp1 = R17 ; General Purpose Global variable
.def Temp2 = R18 ; Used in Sqrt
.def Temp3 = R19 ; Used in Sqrt
;***********************************************************************
;* Ratio: - Divides 32-bit Temp[] (dividend) by 32-bit variable in SQ[] (divisor)
;* DD = dividend, VR = divisor, RM = remainder. SQ[] (divisor) is stored in
;* SRAM and recalled as needed. SQ[] is then used as initial dividend.
;* result is shifted into the dividend SQ[], which returns the final answer.
;* mem1, mem2 are pointers to 4-byte memory words.
;* Author: Frank Raffaeli – AOM, Inc. Copyright 2002
;***********************************************************************
Ratio: ; start here for arbitrary dividend
ldi XL, low(mem1) ; load pointer low byte mem1
ldi XH, high(mem1) ; load pointer high byte mem1
rcall SQ_2_Mem ; store SQ[] (input) into mem1
movw SQ0, Temp0 ; move low word of DD into SQ[]
movw SQ2, Temp2 ; move hi word of DD into SQ[]
clr Temp0 ; init Temp[] with low byte: RM = 0
clr Temp1 ; init Temp[] with 2nd byte: RM = 0
clr Temp2 ; init Temp[] with 3rd byte: RM = 0
sub Temp3, Temp3 ; init 4th byte: RM=0 & clear carry
ldi EE_Addr, 33 ; moose use of EE_Addr as counter
divide_loop1:
rol SQ0 ; shift dividend left
rol SQ1 ; carry into byte 2 of dividend
rol SQ2 ; carry into byte 3 of dividend
rol SQ3 ; carry into byte 4 of dividend
dec EE_Addr ; decrement moose counter
breq ExitRatio ; bug out when moose says
rol Temp0 ; shift dividend into remainder
rol Temp1 ; shift RM byte 2
rol Temp2 ; shift RM byte 3
rol Temp3 ; shift RM byte 4
ldi XL, low(mem2) ; load pointer low byte mem2
ldi XH, high(mem2) ; load pointer high byte mem2
rcall SQ_2_Mem ; store SQ[] (DD/result) to mem2
ldi XL, low(mem1) ; load pointer low byte mem1
ldi XH, high(mem1) ; load pointer high byte mem1
rcall LoadSQ ; get divisor from mem1 into SQ[]
sub Temp0, SQ0 ; RM = RM - VR (low byte)
sbc Temp1, SQ1 ; RM = RM - VR (carry 2nd byte)
sbc Temp2, SQ2 ; RM = RM - VR (carry 3rd byte)
sbc Temp3, SQ3 ; RM = RM - VR (carry 4th byte)
brcs add_back ; if carry set, add back to RM
ldi XL, low(mem2) ; load pointer low byte mem2
ldi XH, high(mem2) ; load pointer high byte mem2
rcall LoadSQ ; get DD/result from mem2 into SQ[]
sec ; set carry, shift '1' into DD
rjmp divide_loop1
add_back:
add Temp0, SQ0 ; RM = RM + VR (restoreremainder)
adc Temp1, SQ1 ; RM = RM + VR (carry 2nd byte)
adc Temp2, SQ2 ; RM = RM + VR (carry 3rd byte)
adc Temp3, SQ3 ; RM = RM + VR (carry 4th byte)
ldi XL, low(mem2) ; load pointer low byte mem2
ldi XH, high(mem2) ; load pointer high byte mem2
rcall LoadSQ ; get DD/result from mem2 into SQ[]
clc ; clear carry, shift '0' into DD
rjmp divide_loop1 ; back to start
ExitRatio:
; rcall RatioFilter ; smooth the data in SQ[] (optional)
; sbr Status, (1<<DataReady) ; data ready for CalcDisplayOhms()
ret
;***********************************************************************
;*
;* Root: - Takes the square root of a 32-bit integer
;*
;* -- This is a square root algorithm for unsigned 32 bit integers that uses
;* -- addition, subtraction, and shifts only. Result is truncated: Root(62) = 7
;* -- For a description of the algorithm, see the German computer magazine
;* -- c't, January 1990, page 300 ff. (Otto Peter, "Prozessor zieht Wurzeln")
;*
;* Author: Frank Raffaeli August 2002, AOM, Inc.
;*
;* Root : Unsigned_32 := 0; * Square root
;* RM : Unsigned_32 := 16#4000_0000#; * Remainder
;* X1 : Unsigned_32 := X; * Input, Passed in from SQ[]
;* X2 : Unsigned_32; * Interim 32-bit variable
;*
;* begin
;* loop
;* X2 := Root + RM;
;* Root := Shift_Right (Root, 1);
;* if X2 <= X1 then
;* X1 := X1 - X2;
;* Root := Root + RM;
;* end if;
;* RM := Shift_Right (RM, 2);
;* exit when RM = 0;
;* end loop;
;* return Root;
;* end Sqrt;
;*
;* In order to save on register usage, the functions below are used:
;*
;* Temp_2_Mem -> Saves contents of 32-bit Temp[] register to SRAM
;* SQ_2_Mem -> Saves contents of 32-bit SQ[] register to SRAM
;* LoadTemp -> Loads 32-bit Temp[] register with contents of SRAM
;* LoadSQ -> Loads 32-bit SQ[] register with contents of SRAM
;*
;* The modified algorithm is:
;*
;* Store X1 (SQ[]) into Mem1 * Save input data in Mem1
;* Load immediate constant 0x40000000 into Temp[] * Remainder init
;* clear SQ[] * Root register (for now)
;* Store Temp[] into Mem2 * Save remainder in Mem2
;*
;* root_loop:
;* Temp[] (RM) <- Temp + SQ[] * Temp[] = X2
;* SQ[] <- (SQ >> 1) * Shift Root right
;* Store SQ[] into Mem3 * Save root in Mem3
;* Load X1 from Mem1 into SQ[] * Ld X1 into SQ[] fm Mem1
;* Compare SQ[] (X1) to Temp[] (X2) * is X1 >= X2 ?
;*
;* brlo get_RM (else) * conditional branch
;* X1 (SQ[]) <- X1 - X2 (Temp[]) * subtract X2 from X1
;* Store X1 (SQ[]) into Mem1 * Store X1 into Mem1
;* Load Root into SQ[] from Mem3 * SQ[] <- Root from Mem3
;* Load Remainder into Temp[] from Mem2 * Temp <- RM from Mem2
;* SQ[] (Root) = Root + Temp[] (RM) * Root = Root+RM
;* rjmp update_RM * skip over to update
;*
;* get_RM:
;* Load Temp[] with RM from Mem2 * get remainder from Mem2
;* Load SQ[] with Root from Mem3 * get Root from Mem3
;*
;* update_RM:
;* Temp[] <- Temp[] >> 2 * RM >> 2
;* Store Temp[] into Mem2 * Save remainder in Mem2
;* brne root_loop * if RM !=0, go back
;*
;***********************************************************************
Root:
cbr Status, (1<<ACC_Ready) ; clear accumulator ready flag
ldi XL, low(mem1) ; load pointer low byte mem1
ldi XH, high(mem1) ; load pointer high byte mem1
rcall SQ_2_Mem ; store SQ[] (input) into mem1
ldi Temp0, low(RemainderInit) ; initialize low byte of 32-bit RM
ldi Temp1, high(RemainderInit) ; initialize 2nd byte of 32-bit RM
ldi Temp2, byte3(RemainderInit) ; initialize 3rd byte of 32-bit RM
ldi Temp3, byte4(RemainderInit) ; initialize 4th byte of 32-bit RM
clr SQ0
clr SQ1
movw SQ2, SQ0 ; shortcut clear of SQ[]
ldi XL, low(mem2) ; load pointer low byte mem2
ldi XH, high(mem2) ; load pointer high byte mem2
rcall Temp_2_Mem ; store Temp[] (RM) into mem2
root_loop:
add Temp0, SQ0 ; add low bytes
adc Temp1, SQ1 ; add 2nd byte with carry
adc Temp2, SQ2 ; add 3rd byte with carry
adc Temp3, SQ3 ; finished: Temp[] = Temp[] + SQ[]
lsr SQ3 ; begin at 4th byte, SQ[] >> 1
ror SQ2 ; 3rd byte >> 1 with carry
ror SQ1 ; 2nd byte >> 1 with carry
ror SQ0 ; finished SQ[] (Root) >> 1
ldi XL, low(mem3) ; load pointer low byte mem3
ldi XH, high(mem3) ; load pointer high byte mem3
rcall SQ_2_Mem ; store SQ[] (root) into mem3
ldi XL, low(mem1) ; load pointer low byte mem1
ldi XH, high(mem1) ; load pointer high byte mem1
rcall LoadSQ ; mem1 (input) -> SQ[]
cp SQ0, Temp0 ; compare low byte X1 to X2
cpc SQ1, Temp1 ; compare 2nd byte with carry
cpc SQ2, Temp2 ; compare 3rd byte with carry
cpc SQ3, Temp3 ; is SQ[] (X1) >= Temp[] (X2) ?
brlo get_RM ; if not, get remainder, else cont
sub SQ0, Temp0 ; subtract low bytes (X1 = X1 - X2)
sbc SQ1, Temp1 ; subtract 2nd byte with carry
sbc SQ2, Temp2 ; subtract 3rd byte with carry
sbc SQ3, Temp3 ; finished: SQ[] = SQ[] - Temp[]
ldi XL, low(mem1) ; load pointer low byte mem1
ldi XH, high(mem1) ; load pointer high byte mem1
rcall SQ_2_Mem ; store SQ[] (X1) into mem1
ldi XL, low(mem3) ; load pointer low byte mem3
ldi XH, high(mem3) ; load pointer high byte mem3
rcall LoadSQ ; mem3 (root) -> SQ[]
ldi XL, low(mem2) ; load pointer low byte mem2
ldi XH, high(mem2) ; load pointer high byte mem2
rcall LoadTemp ; mem2 (remainder : RM) -> Temp[]
add SQ0, Temp0 ; add low bytes Root = Root + RM
adc SQ1, Temp1 ; add 2nd byte with carry
adc SQ2, Temp2 ; add 3rd byte with carry
adc SQ3, Temp3 ; finished: SQ[] = SQ[] + Temp[]
rjmp update_RM ; skip over RM retreival, it's done
get_RM:
ldi XL, low(mem2) ; load pointer low byte mem2
ldi XH, high(mem2) ; load pointer high byte mem2
rcall LoadTemp ; mem2 (remainder : RM) -> Temp[]
ldi XL, low(mem3) ; load pointer low byte mem3
ldi XH, high(mem3) ; load pointer high byte mem3
rcall LoadSQ ; mem3 (root) -> SQ[]
update_RM:
lsr Temp3 ; begin at 4th byte, Temp[] >> 1
ror Temp2 ; 3rd byte >> 1 with carry
ror Temp1 ; 2nd byte >> 1 with carry
ror Temp0 ; Temp (Remainder, RM) >> 1
lsr Temp3 ; do it again, Temp[] >> 1
ror Temp2 ; 3rd byte >> 1 with carry
ror Temp1 ; 2nd byte >> 1 with carry
ror Temp0 ; Temp (Remainder, RM) >> 2
ldi XL, low(mem2) ; load pointer low byte mem2
ldi XH, high(mem2) ; load pointer high byte mem2
rcall Temp_2_Mem ; store Temp[] (RM) into mem2
tst Temp3 ; is it zero?
brne root_loop ; if not, go back to start
tst Temp2 ; is it zero?
brne root_loop ; if not, go back to start
tst Temp1 ; is it zero?
brne root_loop ; if not, go back to start
tst Temp0 ; is it zero?
brne root_loop ; if not, go back to start
root_done:
sbr Status, (1<<DataReady) ; set data ready for CalcDisplayAC()
ret
;***********************************************************************
;* LoadSQ -> SRAM at X[] address loaded into 32-bit SQ[] array
;* Uses register X[] to index SRAM
;***********************************************************************
LoadSQ:
ld SQ0, X+ ; load 1st SRAM byte into SQ0
ld SQ1, X+ ; load 2nd SRAM byte into SQ1
ld SQ2, X+ ; load 3rd SRAM byte into SQ2
ld SQ3, X+ ; load 4th SRAM byte into SQ3
sbiw XL, width_32 ; return X[] pointer to where it was
ret
;***********************************************************************
;* SQ_2_Mem -> Contents of 32-bit SQ[] -> SRAM at X[] address
;* Uses register X[] to index SRAM
;***********************************************************************
SQ_2_Mem:
st X+, SQ0 ; store SQ0 in 1st byte of SRAM
st X+, SQ1 ; store SQ1 in 2nd byte of SRAM
st X+, SQ2 ; store SQ2 in 3rd byte of SRAM
st X+, SQ3 ; store SQ3 in 4th byte of SRAM
sbiw XL, width_32 ; return X[] pointer to where it was
ret
;***********************************************************************
;* Temp_2_Mem -> Contents of 32-bit Temp[] -> SRAM at X[] address
;* Uses register X[] to index SRAM
;***********************************************************************
Temp_2_Mem:
st X+, Temp0 ; store Temp0 in 1st byte of SRAM
st X+, Temp1 ; store Temp1 in 2nd byte of SRAM
st X+, Temp2 ; store Temp2 in 3rd byte of SRAM
st X+, Temp3 ; store Temp3 in 4th byte of SRAM
sbiw XL, width_32 ; return X[] pointer to where it was
ret
;***********************************************************************
;* LoadTemp -> SRAM at X[] address loaded into 32-bit Temp[] array
;* Uses register X[] to index SRAM
;***********************************************************************
LoadTemp:
ld Temp0, X+ ; load 1st SRAM byte into Temp0
ld Temp1, X+ ; load 2nd SRAM byte into Temp1
ld Temp2, X+ ; load 3rd SRAM byte into Temp2
ld Temp3, X+ ; load 4th SRAM byte into Temp3
sbiw XL, width_32 ; return X[] pointer to where it was
ret