From e8c456321e0241f6086620f6978a36dccdae8244 Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Sun, 26 Jan 2014 00:25:48 -0500 Subject: [PATCH 1/1] Add square root regression test --- tests/Makefile.am | 6 +- tests/div32u.asm | 250 +++++++++++++++++++++++++++++++ tests/sqroot.asm | 374 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 628 insertions(+), 2 deletions(-) create mode 100644 tests/div32u.asm create mode 100644 tests/sqroot.asm diff --git a/tests/Makefile.am b/tests/Makefile.am index 0fec7ea..2dbcf9b 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -11,9 +11,10 @@ SUFFIXES = .hex .asm .sh ASM_SRC = \ add.asm \ mul1.asm mul2.asm \ - div.asm \ + div.asm div32u.asm \ orl.asm anl.asm \ mov.asm \ + sqroot.asm \ timer0.asm timer1.asm timer2.asm if RUN_TESTS @@ -21,9 +22,10 @@ if RUN_TESTS TESTS = \ add.sh \ mul1.sh mul2.sh \ - div.sh \ + div.sh div32u.sh \ orl.sh anl.sh \ mov.sh \ + sqroot.sh \ timer0.sh timer1.sh timer2.sh # Tell make how to generate a .sh file after a .hex file is generated: diff --git a/tests/div32u.asm b/tests/div32u.asm new file mode 100644 index 0000000..c101536 --- /dev/null +++ b/tests/div32u.asm @@ -0,0 +1,250 @@ +; Test program to verify correct emu8051 operation +; Taken from http://www.vzsite.us/8051/ +; +; Test desc: 32-bit division +; Test output: PC = $FFF0 +; Test output: SP = $60 +; Test output: R0 = $E6 +; Test output: R1 = $55 +; Test output: R2 = $00 +; Test output: R3 = $00 +; Test output: R4 = $20 +; Test output: R5 = $1B +; Test output: PSW = $05 + +;26 Oct 00 added code to zero remainder when dividend is zero +;19 Dec 99 corrected comments, removed unnecessary instruction +;16 May 99 8051 source code +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +;DIV32U is called to divide (unsigned) a 32-bit dividend using a +; 16-bit divisor. +; +;DIV32U solves for quotient and remainder the equation: +; +; dividend = divisor*quotient + remainder +; +;Call: +; r7,r6,r5,r4 = dividend +; r1,r0 = divisor +; lcall DIV32U +; +;Return: +; r5,r4 = quotient +; r7,r6 = remainder +; c flag set to 1 if overflow occured +; All registers, acc, b and two caller-assigned direct memory bytes +; (q0 and q1)have been changed. +; Data pointer has not been disturbed +; +;Note: +; (1)Overflow is a divide by zero or any value that will cause +; the quotient to be greater than 16 bits. +; (2)Most significant (ms) register always listed first when comma separates +; two in a comment. Example: r3,r2 (r3 contains the ms bits) +; (3) The algorithm used in this code borrows heavily from work posted +; by John C. Wren who said he got it from a C complier. +; +;Original author: John Veazey, Ridgecrest, CA, 18 APR 99 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +q0 EQU 70h +q1 EQU 71h +TOS EQU 60h ; Adresse du dessus de la pile. + + ORG 0000h ; Reset vector + MOV SP,#TOS ; Init stack pointer + + ;; Set dividend + MOV R7,#012h + MOV R6,#034h + MOV R5,#056h + MOV R4,#078h + + ;; Set divisor + MOV R1,#0ABh + MOV R0,#0CDh + + LCALL DIV32U + LJMP 0FFF0h + +DIV32U: +; +;Clear the working quotient +; + clr a + mov q1,a + mov q0,a +; +;Clear the msb's of a 32-bit working divisor (r3,r2,r1,r0) +; + mov r3,a + mov r2,a +; +;b counts the number of places+1 the divisor was initially +; shifted left to align its ms bit set with the ms bit set +; in the dividend +; + mov b,#1 +; +;Make an error return if trying to divide by zero +; + mov a,r1 + orl a,r0 + jnz du100 + ljmp du920 ;Make the error return +; +;Just return with quotient and remainder zero if dividend is zero +; +du100: + mov a,r7 + orl a,r6 + orl a,r5 + orl a,r4 + jnz du200 + mov r7,a + mov r6,a + ljmp du910 ;Make a normal return +; +;Align the msb set in the demoninator with the msb set in the +; numerator. Increment the shift count in b each time a shift left +; is performed. +; +du200: + mov a,r3 ;Stop if msb set + clr c + rlc a + jc du600 + subb a,r7 ;Compare r3 & r7, (c clear) + jz du210 ; jump if r3=r7 + jnc du600 ; jump if r3>r7 + sjmp du240 ; r3r6 + jnz du240 ; jump if r2r5 + jnz du240 ; jump if r1r4 +du240: + clr c ;Now shift the denominator + mov a,r0 ; left 1 bit position + rlc a + mov r0,a + mov a,r1 + rlc a + mov r1,a + mov a,r2 + rlc a + mov r2,a + mov a,r3 + rlc a + mov r3,a + inc b ;Increment b counter and + sjmp du200 ; continue +; +;Compare the shifted divisor with the remainder (what's +; left of the dividend) +; +du600: + mov a,r7 + clr c + subb a,r3 + jc du720 ;jump if r3>r7 + jnz du700 ;jump if r3r6 + jnz du700 ;jump if r2r5 + jnz du700 ;jump if r1r4 +; +;Divisor is equal or smaller, so subtract it off and +; get a 1 for the quotient +; +du700: + mov a,r4 + clr c + subb a,r0 + mov r4,a + mov a,r5 + subb a,r1 + mov r5,a + mov a,r6 + subb a,r2 + mov r6,a + mov a,r7 + subb a,r3 + mov r7,a + clr c + cpl c ;Get a 1 for the quotient + sjmp du730 +; +;Divisor is greater, get a 0 for the quotient +; +du720: + clr c +; +;Shift 0 or 1 into quotient +; +du730: + mov a,q0 + rlc a + mov q0,a + mov a,q1 + rlc a + mov q1,a + jc du920 ;overflow - make the error return +; +;Now shift the denominator right 1, decrement the counter +; in b until b = 0 +; +du740: + clr c + mov a,r3 + rrc a + mov r3,a + mov a,r2 + rrc a + mov r2,a + mov a,r1 + rrc a + mov r1,a + mov a,r0 + rrc a + mov r0,a + djnz b,du600 +; +;Move quotient and remainder +; + mov a,r5 + mov r7,a + mov a,r4 + mov r6,a + mov a,q1 + mov r5,a + mov a,q0 + mov r4,a +; +;Make the normal return +; +du910: + clr c + ret +; +;Make the error return +; +du920: + clr c + cpl c + ret diff --git a/tests/sqroot.asm b/tests/sqroot.asm new file mode 100644 index 0000000..eac6a20 --- /dev/null +++ b/tests/sqroot.asm @@ -0,0 +1,374 @@ +; Test program to verify correct emu8051 operation +; Taken from http://www.vzsite.us/8051/ +; +; Test desc: 32-bit division +; Test output: PC = $FFF0 +; Test output: SP = $30 +; Test output: PSW = $04 +; Test output: A = $44 +; Test output: B = $44 + +;17 Jan 00 re-written for consistency with assembler/compiler byte order +;21 May 99 8051 source code +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +;SQROOT1 is called to calculate the square root of a 32-bit number. +; +;Call: +; +; r0 => MSB of 32-bit input +; lcall SQROOT1 +; +;Return: +; +; computed square root is in acc(LSB's) and b(MSB's) +; (root is also in est(MSB's) and est+1(LSB's)) +; +;SQROOT1 uses the formula +; +; estimate = (last_estimate + input/last_estimate)/2 +; +;Method is described as Newton's, Newton-Raphson, and Babylonian method. +;DIV32U is called to do the 32/16-bit division. +;SQROOT1 perfoms a fixed number of iterations to converge to the root. +; +;SQROOT1 will destroy all registers except r0, dptr. +; +;Original Author: John Veazey, Ridgecrest, CA, 18 Apr 99 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +q0 EQU 70h +q1 EQU 71h +est EQU 72h ; 2 bytes: The estimated value being developed +sqrcnt EQU 74h ; Counts iterations +square EQU 75h +TOS EQU 30h ; Adresse du dessus de la pile. + + ORG 0000h ; Reset vector + MOV SP,#TOS ; Init stack pointer + + ;; Set 32-bit input value + MOV square+0,#012h + MOV square+1,#034h + MOV square+2,#032h + MOV square+3,#010h + + ;; Set pointer to square + mov r0,#square + + LCALL SQROOT + LJMP 0FFF0h + +SQROOT: +; +;Save r0 on the stack +; + mov a,r0 + push acc +; +;Initialize the iteration counter +; + mov sqrcnt,#7 ;There will be seven calculations +; +;Find the MSB set in input, call it n as in 2**n. Calculate +; r = ceiling(n+1)/2, then set up the first estimate to +; be (2**r)-1. +; + mov a,@r0 ;Is MS byte of input not-zero? + jz sqr112 + mov r1,#33 ; Yes, set r1 to the bit number+2 + sjmp sqr140 +sqr112: + inc r0 ; No, + mov a,@r0 ;Is 2nd MS byte of input not-zero? + jz sqr114 + mov r1,#25 ; Yes + sjmp sqr140 +sqr114: + inc r0 + mov a,@r0 ;Is 3rd MS byte of input not-zero? + jz sqr116 + mov r1,#17 ; Yes + sjmp sqr140 +sqr116: + inc r0 + mov est,#0 + mov a,@r0 ;Is LS byte of input zero? + jnz sqr122 + mov est+1,#0 ; Yes, return with a zero + ljmp sqr900 ; because input is zero +sqr122: + mov est+1,#1 + dec a ;Is LS byte of input = 1? + jz sqr900 ; Yes, return with a one + inc a ; No + mov r1,#9 +sqr140: + dec r1 + rlc a + jnc sqr140 + mov a,r1 ;Form ceiling[(n+1)/2] + clr c + rrc a ; (divide by 2, add remainder) + jnc sqr142 + inc a +sqr142: + mov r1,a + clr a + mov est,a +sqr144: + setb c ;Get the 1 to shift in + rlc a + mov b,a + mov a,est + rlc a + mov est,a + mov a,b + dec r1 + cjne r1,#0,sqr144 + mov est+1,a +; +;Load input into DIV32U dividend register +; +sqr200: + pop acc + push acc + mov r0,a + mov a,@r0 ;Set MSB's + mov r7,a + inc r0 + mov a,@r0 + mov r6,a + inc r0 + mov a,@r0 + mov r5,a + inc r0 + mov a,@r0 + mov r4,a +; +;Load last estimate into DIV32U divisor registers +; + mov a,est+1 ;Set LSB's + mov r0,a + mov a,est ;Set MSB's + mov r1,a +; +;Call DIV32U to do the 32/16-bit division (input/est) +; + lcall DIV32U ;(r7,r6,r5,r4)/(r1,r0) = (r5,r4) + jnc sqr310 ;If overflow, set to maximum number + mov r4,#-1 + mov r5,#-1 +; +;Add the last estimate to the quotient +; +sqr310: + mov a,r4 + add a,est+1 ;sets c + mov est+1,a + mov a,est + addc a,r5 ;sets c +; +;Divide sum by 2 and save as new estimate +; + rrc a ;sets c + mov est,a + mov a,est+1 + rrc a ;c discarded + mov est+1,a +; +;Decrement the iteration counter and repeat if not zero +; + djnz sqrcnt,sqr200 +; +;Make the normal return +; +sqr900: + pop acc ;Restore caller's pointer + mov r0,a + mov b,est ;Get answer in (b,a) + mov a,est+1 + ret + + +DIV32U: +; +;Clear the working quotient +; + clr a + mov q1,a + mov q0,a +; +;Clear the msb's of a 32-bit working divisor (r3,r2,r1,r0) +; + mov r3,a + mov r2,a +; +;b counts the number of places+1 the divisor was initially +; shifted left to align its ms bit set with the ms bit set +; in the dividend +; + mov b,#1 +; +;Make an error return if trying to divide by zero +; + mov a,r1 + orl a,r0 + jnz du100 + ljmp du920 ;Make the error return +; +;Just return with quotient and remainder zero if dividend is zero +; +du100: + mov a,r7 + orl a,r6 + orl a,r5 + orl a,r4 + jnz du200 + mov r7,a + mov r6,a + ljmp du910 ;Make a normal return +; +;Align the msb set in the demoninator with the msb set in the +; numerator. Increment the shift count in b each time a shift left +; is performed. +; +du200: + mov a,r3 ;Stop if msb set + clr c + rlc a + jc du600 + subb a,r7 ;Compare r3 & r7, (c clear) + jz du210 ; jump if r3=r7 + jnc du600 ; jump if r3>r7 + sjmp du240 ; r3r6 + jnz du240 ; jump if r2r5 + jnz du240 ; jump if r1r4 +du240: + clr c ;Now shift the denominator + mov a,r0 ; left 1 bit position + rlc a + mov r0,a + mov a,r1 + rlc a + mov r1,a + mov a,r2 + rlc a + mov r2,a + mov a,r3 + rlc a + mov r3,a + inc b ;Increment b counter and + sjmp du200 ; continue +; +;Compare the shifted divisor with the remainder (what's +; left of the dividend) +; +du600: + mov a,r7 + clr c + subb a,r3 + jc du720 ;jump if r3>r7 + jnz du700 ;jump if r3r6 + jnz du700 ;jump if r2r5 + jnz du700 ;jump if r1r4 +; +;Divisor is equal or smaller, so subtract it off and +; get a 1 for the quotient +; +du700: + mov a,r4 + clr c + subb a,r0 + mov r4,a + mov a,r5 + subb a,r1 + mov r5,a + mov a,r6 + subb a,r2 + mov r6,a + mov a,r7 + subb a,r3 + mov r7,a + clr c + cpl c ;Get a 1 for the quotient + sjmp du730 +; +;Divisor is greater, get a 0 for the quotient +; +du720: + clr c +; +;Shift 0 or 1 into quotient +; +du730: + mov a,q0 + rlc a + mov q0,a + mov a,q1 + rlc a + mov q1,a + jc du920 ;overflow - make the error return +; +;Now shift the denominator right 1, decrement the counter +; in b until b = 0 +; +du740: + clr c + mov a,r3 + rrc a + mov r3,a + mov a,r2 + rrc a + mov r2,a + mov a,r1 + rrc a + mov r1,a + mov a,r0 + rrc a + mov r0,a + djnz b,du600 +; +;Move quotient and remainder +; + mov a,r5 + mov r7,a + mov a,r4 + mov r6,a + mov a,q1 + mov r5,a + mov a,q0 + mov r4,a +; +;Make the normal return +; +du910: + clr c + ret +; +;Make the error return +; +du920: + clr c + cpl c + ret + -- 2.20.1