.macro div2_16bit(address) {
lda address + 1 // load the msb
asl // copy the sign bit into c
ror address + 1 // and back into the msb
ror address + 0 // rotate the lsb as normal
}
This works well until I try to shift a number with the value -1 as I still get -1...
My question is, is there a fast way to test to see if I have tried to "divide by 2" the value "-1" and if so, set the value to 0?
cheers,
Paul
"The plastic veneer of civilization is easily melted in the heat of the moment" - Paul Nicholls
Simple integer division like you're describing can often result in silent truncation toward negative infinity. In your case the carry flag on exit will tell you if truncation occurred, and you can use it with the sign of the argument to "round-toward-zero", assuming that's the behavior you seek. If you just want to force -1/2 to zero as a special case, you can try the following:
.macro div2_16bit(address) {
ldx address + 1 // load the msb
cpx #$80 // copy the sign bit into c
ror address + 1 // and back into the msb
ror address + 0 // rotate the lsb as normal
inx
bne done // see if orig. argument was -1,
ldx address + 0
inx
bne done
stx address + 1 // ... and force result to 0 if it was
stx address + 0
done:
}
Wow, there must be a more efficient way than that, but it's past my bed time again and my fuel gauge is on "E", so I'll leave you in the capable hands of the other members ...
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!
That seems to work ok if the value is -1 before the macro, but if the value is -2 before the macro then the result becomes 0 right away instead of -1 as I would have expected.
cheers,
Paul
"The plastic veneer of civilization is easily melted in the heat of the moment" - Paul Nicholls
.macro div2_16bit(address) {
// is the value -1 at the start?
lda address + 1
cmp #255
bne divBy2
lda address + 0
cmp #255
bne divBy2
// yes, so set to zero and exit
lda #0
sta address + 1
sta address + 0
jmp done
divBy2:
lda address + 1 // load the msb
asl // copy the sign bit into c
ror address + 1 // and back into the msb
ror address + 0 // rotate the lsb as normal
done:
}
Something shorter if possible would be nice
"The plastic veneer of civilization is easily melted in the heat of the moment" - Paul Nicholls
Oh, yeah, a bcc done before the first inx would probably correct the bug in my original attempt, at the expense of two more bytes. Here's a fresh attempt that seems to be a bit more (space) efficient (thanks to Martin A for the seed of the idea):
.macro div2_16bit(address) {
lda address + 1 // load the msb
cmp #$80 // copy the sign bit into c
ror address + 1 // rotate sign into the msb
and address + 0 // check for special case -1
ror address + 0 // rotate the lsb as normal
eor #$ff // complete the -1 detection
bne done
sta address + 1 // force result to 0 only if
sta address + 0 // original input was -1
done:
}
I always default to optimizing for size rather than speed.
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!
Oh, yeah, a bcc done before the first inx would probably correct the bug in my original attempt, at the expense of two more bytes. Here's a fresh attempt that seems to be a bit more (space) efficient (thanks to Martin A for the seed of the idea):
.macro div2_16bit(address) {
lda address + 1 // load the msb
cmp #$80 // copy the sign bit into c
ror address + 1 // rotate sign into the msb
and address + 0 // check for special case -1
ror address + 0 // rotate the lsb as normal
eor #$ff // complete the -1 detection
bne done
sta address + 1 // force result to 0 only if
sta address + 0 // original input was -1
done:
}
I always default to optimizing for size rather than speed.
As was already suggested, you can test for -1 before the division.
My question is, does the -1 result cause a problem with your program? In other words, does your program require symetrical integer division or can you use floored integer division?
With floored integer division, if the real result of the division lies between two consecutive integers, the lower value is returned. Since -1/2 is between 0 and -1, -1 is the correct result of -1 divided by 2.
.macro div2_16bit(address) {
LDA address + 1 // load the msb
ASL // copy the sign bit into c
ROR address + 1 // and back into the msb
ROR address + 0 // rotate the lsb as normal
LDA address + 1 // load the msb
BPL skip // if negative
LDA address + 0 // add the carry
ADC #$0 // back to the result
STA address + 0 // to force symetric division
LDA address + 1
ADC #$0
STA address + 1
skip:
}
I am away from my desktop at the moment, which has the c64 simulator, so I can't fully test this code. I did a brief check on easy 6502.
I like Jim's symmetrical routine better than just special-casing -1/2, but I think I can make it shorter and possibly even faster if we don't mind getting register x involved (untested):
As was already suggested, you can test for -1 before the division.
My question is, does the -1 result cause a problem with your program? In other words, does your program require symetrical integer division or can you use floored integer division?
With floored integer division, if the real result of the division lies between two consecutive integers, the lower value is returned. Since -1/2 is between 0 and -1, -1 is the correct result of -1 divided by 2.
Cheers,
Jim
Hmm...good question @JimBoyd...I might have been hasty as I do want floored division...I ultimately want to convert a 16-bit x value to its column value using the equivalent of column = floor(x/8.0).
I thought I could call my 16-bit signed shift routine 3 times to do this.
Thanks for all the great answers and help all!
"The plastic veneer of civilization is easily melted in the heat of the moment" - Paul Nicholls
I like Jim's symmetrical routine better than just special-casing -1/2, but I think I can make it shorter and possibly even faster if we don't mind getting register x involved (untested):
I like the short version, however if the x register needs preserved because it is used for something like a data stack pointer , then the y register could be used in place of the x register.
I like Jim's symmetrical routine better than just special-casing -1/2, but I think I can make it shorter and possibly even faster if we don't mind getting register x involved (untested):
I like the short version, however if the x register needs preserved because it is used for something like a data stack pointer , then the y register could be used in place of the x register.
Ah, well done, dmsc! You saved us a bpl, and it looks like you trimmed it about as lean as it's gonna get (heck, you even got rid of those flabby comments) ... unless someone like dclxvi or bogax flops a little gem onto the table from low Earth orbit ...
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!