When used for division, an arithmetic right shift will round the result down, towards negative infinity, not towards zero.
If you try to divide -3 by 2, the correct result is -1.5, but in two's complement that gets rounded down to -2. The same happens when you divide -1 by 2, which should be -0.5, but that gets rounded down to -1 and you're stuck with -1 no matter how much you keep shifting... there will never be negative 0.
This instruction doesn't exist on the 6502. There is "arithmetic shift left" (ASL) and "logical shift right" (LSR), both of which are the same as rotate (ROL/ROR) if carry was zero.tokumaru wrote:Yeah, an arithmetic right shift will shift the most significant bit right, but also preserve it as the most significant bit.
There is no sign preserving right shift, AFAIK, you have to manually move the sign to carry before starting with ROR. Samophlange's LDA/CMP is a valid way to load carry with the sign. Could also LDA/ROL for the same result in one less byte.
To round up, add N-1 to the value before shifting, where N is the amount you're dividing by.
I didn't say it did, I was just explaining how the operation he implemented in several 6502 instructions worked.rainwarrior wrote:This instruction doesn't exist on the 6502.
Just to fully clarify, ARR appears to be:pubby wrote:illegal opcode ARR #$FF is pretty close to being a sign-preserving right shift. At least when doing multiple shifts it is.
1. Overflow (V) is set as if an ADC of: (A & #immediate) + #immediate ?
2. Result is: AND A with #immediate, followed by ROR
3. Carry (C) is set from bit 6 of result.
4. Zero/Negative (Z/N) from result.
So you still need to load the carry with sign before starting the shifts, but if you're doing multiple shifts on a single byte ARR #-1 can keep putting the sign back into the carry after each shift.
Apparently only helps if you need to shift more than once. Looks like this:
Code: Select all
; value to be shifted already in A CMP #$80 ; load sign into carry ARR #$FF ; signed /2 ARR #$FF ; signed /4 ARR #$FF ; signed /8 ROR ; signed /16 ; note that the last shift in the chain can be optimized as a ROR to save a byte.
Is there a clever/compact way to see if two variables have the same sign? The only way I can think of involves ANDing with %10000000 into two temporaries and comparing those, but it seems like there should be some way to use the bit 7 "negative flag".
Comparing value in A to variable "v":
Code: Select all
EOR v ; sign bit will be 0 if the signs match, 1 if they differ AND v ; sign bit will be set if both signs are negative ORA v ; sign bit will be clear if both signs are positive
Bit 6 is also convenient just for the BIT instruction, which can test it directly and puts the result in the overflow flag V... which is weird and special to that one instruction, but pretty handy especially for testing some hardware registers.
You don't need to isolate the bits or use any temporaries for this. Just EOR one value with the other directly and the N flag of the result will give you the answer. The result of EOR is 0 if both bits are equal, or 1 if they differ. So you can simply do this:samophlange wrote:Is there a clever/compact way to see if two variables have the same sign? The only way I can think of involves ANDing with %10000000 into two temporaries and comparing those, but it seems like there should be some way to use the bit 7 "negative flag".
Code: Select all
lda Variable0 eor Variable1 bmi DifferentSigns SameSign: ;(...) DifferentSigns: ;(...)