6502 ASM trick
Moderator: Moderators
Re: 6502 ASM trick
Flipping the MSB seems the best way to deal with comparing to a constant (pre flipped). But otherwise I would refer to the link tepples posted a few post back. It has a method using N and V flags that avoids using a temp variable.
- Jarhmander
- Formerly ~J-@D!~
- Posts: 521
- Joined: Sun Mar 12, 2006 12:36 am
- Location: Rive nord de Montréal
Re: 6502 ASM trick
I revive this old thread with some recent thinkings about the use of the identity table by tokumaru (1st post in thread, also see here).
Basically, you can do these operations:
cst is an unsigned constant that you want to add/substract to X or Y;
[OP A] is some optional ALU operation with A; obviously, if OP is cmp, A isn't affected.
The restriction is that the addition/substraction of X/Y with the constant must not overflow/underflow; otherwise the result is undefined. If one want (((X or Y) ± cst) & 0xFF) and never have undefined result, they'll need an identity table twice the size, or it could be done in hardware with a non-inverting tri-state buffer that connects A0-A8 to D0-D8, output would be enabled with reads into a memory area of interest.
As you might have guessed, these pseudo-instructions use absolute indexed addressing, and the arithmetics are constant offsets added to the identity table address. Undefined results happen simply when the accesses goes out of the table. Assuming the table is page-aligned, when a constant is added to X or Y, it takes 4 cycles, whereas with the substractive case, it takes 5 cycles because of the page crossing.
Some examples of use: (idt is the identity table)
It's possible that the HideSprite routine above is the fastest one without using unofficial opcodes and without total unrolling (and without clearing a bit in $2001, you fools!). There's very little unrolling here, just what's necessary so it works.
EDIT: fixed (enhanced) code.
Basically, you can do these operations:
Code: Select all
((X or Y) ± cst) [OP A] → A
(X ± cst) → Y
(Y ± cst) → X[OP A] is some optional ALU operation with A; obviously, if OP is cmp, A isn't affected.
The restriction is that the addition/substraction of X/Y with the constant must not overflow/underflow; otherwise the result is undefined. If one want (((X or Y) ± cst) & 0xFF) and never have undefined result, they'll need an identity table twice the size, or it could be done in hardware with a non-inverting tri-state buffer that connects A0-A8 to D0-D8, output would be enabled with reads into a memory area of interest.
As you might have guessed, these pseudo-instructions use absolute indexed addressing, and the arithmetics are constant offsets added to the identity table address. Undefined results happen simply when the accesses goes out of the table. Assuming the table is page-aligned, when a constant is added to X or Y, it takes 4 cycles, whereas with the substractive case, it takes 5 cycles because of the page crossing.
Some examples of use: (idt is the identity table)
Code: Select all
ldy idt+4,X ; Y = X + 4
cmp idt-1,Y ; Compare A with Y - 1, Y is != 0
adc idx+3,X ; ADC A with X+3Code: Select all
HideSprites: ; Make all sprites in OAM invisible (in next sprite DMA)
; Could be modified to hide some sprites only
lda #$FF
ldx #0
: ldy idt+4,x
sta OAMbuff,x
ldx idt+4,y
sta OAMbuff,y
cpy #$FC
bne :-
rtsEDIT: fixed (enhanced) code.
Last edited by Jarhmander on Sat Mar 22, 2014 6:29 pm, edited 1 time in total.
((λ (x) (x x)) (λ (x) (x x)))
Re: 6502 ASM trick
Who said total unrolling was required ?[...]the fastest one without using unofficial opcodes and without total unrolling
What you're doing is 2 loop iteration in one, in which you gain roughly 50% of the speed you gain in total unrolling.
If you make 4 loop iteration in one, you gain 75% of the speed you gain in total unrolling.
There's really no point in going anything further than that...
Code: Select all
[...] blah blah initialization
_loop
sta $200,Y
sta $240,Y
sta $280,Y
sta $2c0,Y
dey
dey
dey
dey
bne _loop
Re: 6502 ASM trick
Jarhmander's code can start the clear at any X position, not just 0, which allows using it to clear the rest of the sprites after the ones that are already displayed.
- Jarhmander
- Formerly ~J-@D!~
- Posts: 521
- Joined: Sun Mar 12, 2006 12:36 am
- Location: Rive nord de Montréal
Re: 6502 ASM trick
I mentioned unrolled loops because obviously it would be faster (and of limited utility). Also, I tried to showcase a useful example, but it turns out that even when getting rid of the last cpy (by putting a 0 byte after the identity table, a bit of a hack) it is as fast as the following with the same number of stores in the loop:
32 iterations, 21 cycles for the loop. Bad example, then. 
Code: Select all
ldx #0
clc
: lda #$FF
sta OAMbuff,x
sta OAMbuff+4,x
txa
acd #8
tax
bcc :-((λ (x) (x x)) (λ (x) (x x)))
Re: 6502 ASM trick
This will probably sound trivial to some, but here it goes: Testing for equality without affecting the carry flag
Yesterday I caught myself in a situation where I needed to verify if a variable contained a specific value, but the carry flag was holding the result of a previous operation that I would like to keep for a future decision. This meant I couldn't use CMP, since that would modify the carry flag. Then, I realized I could use EOR for this:
EOR is a drop-in replacement for CMP in this case (unless you need the value in the accumulator to be preserved, of course), there's no need to change anything else. This works because EOR turns bits that are the same in both bytes into 0s, and the bits that are different into 1s, so the only way to get all 0s is if all bits in both bytes are the same.
Again, I'm sure this is known to some people, but since I had never thought of this trick before (I sure hope it wasn't mentioned in this thread already!), I thought it was a good idea to share it here so everyone knows that there's another way to compare numbers for equality that might be useful in a few special cases.
Yesterday I caught myself in a situation where I needed to verify if a variable contained a specific value, but the carry flag was holding the result of a previous operation that I would like to keep for a future decision. This meant I couldn't use CMP, since that would modify the carry flag. Then, I realized I could use EOR for this:
Code: Select all
lda Variable
eor #VALUE
bne NotEqualAgain, I'm sure this is known to some people, but since I had never thought of this trick before (I sure hope it wasn't mentioned in this thread already!), I thought it was a good idea to share it here so everyone knows that there's another way to compare numbers for equality that might be useful in a few special cases.
Re: 6502 ASM trick
I've been doing something similar for a while, but I use DEC and INC to flip my flags between false ($00) to true ($FF).zeroone wrote:Woz's Optimized Flags Code for 6502
Re: 6502 ASM trick
Which is fine until you try to clear to false a flag that is already false. Then the next time you try to set it to true, it'll still be false.
To clear a flag, as in the article:
To set a flag, beating the article by one byte but adding two cycles:
To clear a flag, as in the article:
Code: Select all
lsr flagCode: Select all
sec
ror flagRe: 6502 ASM trick
True, but I only use INC/DEC when the state of the flag is known, which is most of the time in my programs so far. In the few cases when I don't know the value, I indeed have to set the new value the old LDA + STA way.tepples wrote:Which is fine until you try to clear to false a flag that is already false.
Re: 6502 ASM trick
As well as not modifying any registers!tepples wrote: To set a flag, beating the article by one byte but adding two cycles:Code: Select all
sec ror flag