Everett M. Greene said:
Alexei A. Frounze said:
Everett M. Greene said:
Another use is testing code from a cross-compiler for a
processor that doesn't exist yet. With this application,
you also get to deal with the vagaries of the documentation.
It teaches you that some people have unusual ideas as to
what constitutes a good processor design ["you don't need
no steenkin' signed arithmetic"].
I beg your pardon, what's so bad about signed arithmetics?
Not a thing. Just try doing signed comparisons without
any signed operations.
No problem. Consider the most common case of 2's complemented representation
of signed integers and a pair of integers of the same bit size. You subtract
them the same way as you'd do with unsigned ones. If the result is 0,
they're equal. Otherwise, they're not and you need to determine which one is
smaller/bigger than the other. To find out which one is bigger/smaller, you
only need the sign of the result (the most significant bit). But since the
overflow can occur (e.g. when subtracting 16-bit integer 1 from 16-bit
integer -32768 you want to get -32769 but that can't fit into just 16 bits
anymore and you get 32767 and overflow), you must take that into account.
So, you take the most significant bit of the result and treat it as the
sign, which is subject to further correction. You can then simply XOR that
MSBit with the overflow flag of the CPU (I haven't seen a CPU w/o that flag
yet, so I consider that to be available) and the outcome of the XOR
operation is the true sign of the result, whether there was overflow or not.
This is my 16-bit comparison routine for i8051 8-bit micro controller:
; R3(MSB) R2(LSB) is comared to R5(MSB) R4(LSB)
CMP16:
CLR C ; clear carry/borrow flag before 1st subtraction
with borrow (there's no subtraction w/o borrow on this CPU)
MOV A, R4
SUBB A, R2
MOV B, A
MOV A, R5
SUBB A, R3
JNB ACC.7, CMP16_1 ; jump if MSBit of accumulator is 0, if the
jump is taken the overflow (OV) flag is equal to true sign of the result of
subtraction; otherwise OV is the inverse of the true sign
CPL OV ; invert OV
CMP16_1: ; got subtraction result's sign in OV!
ORL A, B ; now, find out if the subtraction gives us 0 (i.e.
equal numbers), this doesn't affect carry/borrow and OV
RET
Now, this routine can be used to compare both signed and unsigned numbers.
To differentiate between the two you use different conditional jumps after
calling this routine:
For unsigned numbers:
R5R4 = R3R2, if A = 0 (JZ)
R5R4 <> R3R2, if A <> 0 (JNZ)
R5R4 < R3R2, if CY = 1 (JC)
R5R4 > R3R2, if CY = 0 and A <> 0
R5R4 >= R3R2, if CY = 0 (JNC)
For signed numbers:
R5R4 = R3R2, if A = 0 (JZ)
R5R4 <> R3R2, if A <> 0 (JNZ)
R5R4 < R3R2, if OV = 1 (JO)
R5R4 > R3R2, if OV = 0 and A <> 0
R5R4 >= R3R2, if OV = 0 (JNO)
There's no special zero flag on this CPU and JZ/JNZ compares the whole
accumulator to 0 to make a decision on whether or not the jump is taken.
While I agree there's a bit more work involved in comparing signed numbers,
it's not that big. In this case, just 2 extra instructions (JNB ACC.7,...
and CPL OV). It will be different on a different CPU (if you can't test
MSBit of a register directly or flag inversion is absent for OV) but it's
doable. And if your signed numbers are small (and their subtraction can't
give you the overflow), just to compare them you could add a positive bias
to the two before doing subtraction to get away with effectively unsigned
comparison. Or you could sign extend the numbers before subtraction to get
rid of the overflow altogether. Depends on your CPU and maybe application.
The next more complicated signed operation to implement would probably be
division, especially if you want to get the remainder and both overflows (it
can be that there's no overflow in the quotient, but there's in remainder!).
2's complemented multiplication is actually implemented easily with unsigned
multiplication and even w/o finding the absolute values of the
multiplicands.
And you don't probably need signed numbers everywhere...
Alex