I'm sorry if this sounds dense, but would you mind explaining the algorithm a bit? You are obviously making some optimizations but I'm not sure I understand them. In particular, why the alternation between SUBR/ADDR for R3? Also, why use "ADDI #-100000 AND $FFFF" instead of "NEGR" once outside the loop and then just using ADDR with the 1000 in another register? Does NEGR not set the carry bit when an overflow occurs in the negation?
I'm trying to implement my own bin2dec routine to display score, more as a learning exercise, but I can't seem to understand exactly the mechanisms that make yours work.
-dZ.
I haven't looked into the carry bit operation much, but think I can explain the alternating subtracts and adds.
Basically, we are doing a divide by doing subtracts and counting the number of subtracts we do. Imagine we are working on the hundred's digit. So we have a number "num" that is less than 1000. The code in C would look like this:
digit = 0;
while (num >= 100)
{
num = num - 100;
digit = digit +1;
}
// use digit to set a display char
digit = 0;
while (num >= 10)
{
num = num - 10;
digit = digit +1;
}
// use digit to set a display char
In assembly, this would look like
clrr r3
mvii #100, r1 ; Power of 10.
BCDLoop3:
cmp r1, r0 ; is num lt 100
blt done3 ; yep, done
subr r1, r0
incr r3
b BCDLoop3
done3:
; output r3
clrr r3
mvii #10, r1 ; Power of 10.
BCDLoop4:
cmp r1, r0 ; is num lt 10
blt done4
subr r1, r0
incr r3
b BCDLoop3
done4:
If we study this code, it seems inefficient. The
cmp r1, r0
instruction subtracts r1 from r0, sets the flags, and then discards the result of the subtraction. Then the subtract instruction does the same subtract again. To fix this, we could do the subtract every time to set the flags. But this means we would be subtracting 100 one too many times from num, so we have to add back 100 to fix up num.
digit = 0;
while (1)
{
num = num - 100;
if (num <0) break;
digit = digit +1;
}
num = num + 100; // we did one too many subtracts, so fix num
// use digit to set a display char
digit = 0;
while (1)
{
num = num - 10;
if(num<0) break;
digit = digit +1;
}
num = num + 10; // we did one too many subtracts, so fix num
// use digit to set a display char
In assembly, this would look like:
clrr r3
mvii #100, r1 ; Power of 10.
BCDLoop3:
subr r1, r0
blt done3
incr r3
bc BCDLoop3
done3:
add r1, r0 ; fix up r0
; output r3
clrr r3
mvii #10, r1 ; Power of 10.
BCDLoop4:
subr r1, r0
blt done4
incr r3
bc BCDLoop3
done4:
add r1, r0 ; fix up r0
; output r3
As you can see, the loops are smaller. We could optimize this a bit more by initializing r3 to "-1" and then always doing the increment:
mvii #-1, r3
mvii #100, r1 ; Power of 10.
BCDLoop3:
incr r3
subr r1, r0
bge BCDLoop3
done3:
add r1, r0 ; fix up r0
; output r3
mvii #-1, r3
mvii #10, r1 ; Power of 10.
BCDLoop4:
incr r3
subr r1, r0
bge BCDLoop44
done4:
add r1, r0 ; fix up r0
; output r3
But, is there some way to avoid doing the fixups? This is where the alternating subtracts and adds comes in. In C
digit = -1;
while (1)
{
digit = digit +1;
num = num - 100;
if (num <0) break;
}
// num is now negative number (or zero)
// use digit to set a display char
digit = 10;
while (1)
{
digit = digit -1;
num = num + 10;
if (num>= 0 ) break;
}
// num is positive
// use digit to output the character
In assembly:
mvii #-1, r3
mvii #100, r1 ; Power of 10.
BCDLoop3:
incr r3
subr r1, r0
bge BCDLoop3
done3:
; output r3
mvii #10, r3
mvii #10, r1 ; Power of 10.
BCDLoop4:
decr r3
addr r1, r0
blt BCDLoop44
done4:
; output r3
Does this make sense?