SNES Programing Help 2

Discussion of hardware and software development for Super NES and Super Famicom. See the SNESdev wiki for more information.

Moderator: Moderators

Forum rules
  • For making cartridges of your Super NES games, see Reproduction.
93143
Posts: 1875
Joined: Fri Jul 04, 2014 9:31 pm

Re: SNES Programing Help 2

Post by 93143 »

Espozo wrote:By the way, what is the opcode "bit" do?
It's the same as and, only it doesn't change the accumulator; it just sets the status flags.

EDIT: Not quite; see below. That's what I get for trying to impart knowledge I haven't used myself yet...
Last edited by 93143 on Mon Mar 09, 2015 9:47 pm, edited 1 time in total.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: SNES Programing Help 2

Post by Drew Sebastino »

93143 wrote:status flags.
What? Sorry... :oops:
93143
Posts: 1875
Joined: Fri Jul 04, 2014 9:31 pm

Re: SNES Programing Help 2

Post by 93143 »

In the processor status register.
Eyes and Lichty (1992) wrote: BIT sets the P status register flags based on the result of two different operations, making it a dual-purpose instruction:

First, it sets or clears the n flag to reflect the value of the high bit of the data located at the effective address specified by the operand, and sets or clears the v flag to reflect the contents of the next-to-highest bit of the data addressed.

Second, it logically ANDs the data located at the effective address with the contents of the accumulator; it changes neither value, but sets the z flag if the result is zero, or clears it if the result is non-zero.

[...] When the BIT instruction is used with the immediate addressing mode, the n and v flags are unaffected.
So it's not exactly the same as and...
User avatar
Khaz
Posts: 314
Joined: Thu Dec 25, 2014 10:26 pm
Location: Canada

Re: SNES Programing Help 2

Post by Khaz »

Huh, I forgot about the effect that BIT had on the N and V flags. Sounds handy, you can test three conditions with a single instruction...

... except that it's setting Z based on the accumulator, but V and N based on the value at the operand address, which makes it confusing and weird to me... I can see it being handy if it worked in immediate mode, such that every BIT instruction automatically tests the highest two bits of the accumulator and stores them in the V and N flags so you effectively get three BITs in one, but I can't really understand the use of its actual function.
Sik
Posts: 1589
Joined: Thu Aug 12, 2010 3:43 am

Re: SNES Programing Help 2

Post by Sik »

Think of it in reverse: the mask is in A and the address contains the value you want to test (the zero flag is set based on the result after the AND so it's technically neither operand).
tomaitheous
Posts: 592
Joined: Thu Aug 28, 2008 1:17 am
Contact:

Re: SNES Programing Help 2

Post by tomaitheous »

93143 wrote:In the processor status register.
[...] When the BIT instruction is used with the immediate addressing mode, the n and v flags are unaffected.
??? Is this specific to the 65816, or the C/CS models as well? Because it's not like that for the 6280. The immediate mode sets the same n/v flags as with the other addressing modes. Then again, it has TST #$nn , EA instruction so maybe the logic is redundant/shared (neither write to Acc).
__________________________
http://pcedev.wordpress.com
93143
Posts: 1875
Joined: Fri Jul 04, 2014 9:31 pm

Re: SNES Programing Help 2

Post by 93143 »

That was a direct quote from "Programming the 65816" from WDC, and from context it seems to apply to the 65C02 and 65802 as well (6502 doesn't have immediate mode for this opcode). I haven't had occasion to try it, but that's what the man said.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: SNES Programing Help 2

Post by Drew Sebastino »

Sorry about this, but I was going to hold off this question so I didn't sound stupid, but I don't care. The bit instruction can be used as an "and" and a "cmp", so you only use one instruction instead of two? It seems like that's what's happening in psychopathicteen's code.
tomaitheous
Posts: 592
Joined: Thu Aug 28, 2008 1:17 am
Contact:

Re: SNES Programing Help 2

Post by tomaitheous »

Espozo wrote:Sorry about this, but I was going to hold off this question so I didn't sound stupid, but I don't care. The bit instruction can be used as an "and" and a "cmp", so you only use one instruction instead of two? It seems like that's what's happening in psychopathicteen's code.

I didn't look at his code, but just think about what the instruction does for a sec. CMP tests values, BIT tests 'bits'. You can test one bit, or more than one bit. You don't need a compare instruction, because you're only testing if 1 or more bits are zero or not zero. And those bits don't need to be next to each other either, etc. You can do this without BIT, but you would have to AND with Acc first, which means you've destroyed the original contents of Acc. A byte (or word) might have both data and flags in the value. This is one way to test and branch as needed. Or a byte/word might just be a series of state flags, and you test them in a sequential fashion or whatever (i.e. you don't want to keep loading isolating bits to test with AND).

The N/V flags as a different aspect of it though. You can use the BIT instruction to basically test these two bits of a byte in memory, without loading anything into a register. It saves some cycles. If you use an indexing mode, you can exploit this further (you have fast/instance access to whether the data/struct of an array is valid/applicable or not, using an auxiliary list/array/table/etc).

93143: IIRC, there are technically two versions of the 65C02 from WDC. One is the original processor, and the other is the 65C02 MCU they later developed. The later MCU is supposed to have taken the new instructions that Rockwell added to their own R65C02S (sometimes listed as 65CS02, which is the branch the 6280 is from).
Last edited by tomaitheous on Wed Mar 11, 2015 2:51 pm, edited 1 time in total.
__________________________
http://pcedev.wordpress.com
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: SNES Programing Help 2

Post by koitsu »

Espozo wrote:Sorry about this, but I was going to hold off this question so I didn't sound stupid, but I don't care. The bit instruction can be used as an "and" and a "cmp", so you only use one instruction instead of two? It seems like that's what's happening in psychopathicteen's code.
No -- unlike and, bit DOES NOT modify the accumulator. 93143 covered how it works by quoting the manual: viewtopic.php?p=142705#p142705

It's easy to skim over/miss the key point there (in the last line). Rephrased: the "Flags Affected" section is the most useful part because it becomes very clear what addressing mode used affects what flags. So here it is from the manual. To anyone who doesn't understand the opcode: PLEASE READ THIS SECTION SLOWLY. DO NOT SKIM.

Code: Select all

Test Memory Bits against Accumulator

   BIT sets the P status register flags based on the result of two different operations,
making it a dual-purpose instruction:

   First, it sets or clears the n flag to reflect the value of the high bit of the data
located at the effective address specified by the operand, and sets or clears the v
flag to reflect the contents of the next-to-highest bit of the data addressed.

   Second, it logically ANDs the data located at the effective address with the con-
tents of the accumulator; it changes neither value, but sets the z flag if the result is
zero, or clears it if the result is non-zero.

  BIT is usually used immediately preceding a conditional branch instruction: to
test a memory value’s highest or next-to-highest bits; with a mask in the accumula-
tor, to test any bits of the memory operand; or with a constant as the mask (using
immediate addressing) or a mask in memory, to test any bits in the accumulator.
All of these tests are non-destructive of the data in the accumulator or in memory.
When the BIT instruction is used with the immediate addressing mode, the n and v
flags are unaffected.

   8-bit accumulator/memory (all processors): Data in memory is eight-bit; bit 7 is
moved into the n flag; bit 6 is moved into the v flag.

   16-bit accumulator/memory (65802/65816 only, m = 0): Data in memory is
sixteen-bit: the low-order eight bits are located at the effective address; the high-
order eight bits are located at the effective address plus one. Bit 15 is moved into
the n flag; bit 14 is moved into the v flag.

Flags Affected: n v - - - - z - (Other than immediate addressing)
                - - - - - - z - (Immediate addressing only)
                  n   Takes value of most significant bit of memory data.
                  v   Takes value of next-to-highest bit of memory data.
                  z   Set if logical AND of memory and accumulator is zero; else cleared.
Addressing modes supported by BIT:
  • Immediate (opcode $89) -- BIT #$22
  • Absolute (opcode $2C)--. BIT $1234
  • Direct Page (DP) (opcode $24) -- BIT $16
  • Absolute Indexed,X (opcode $3C) -- BIT $1234,X
  • DP Indexed,X (opcode $34) -- BIT $16,X
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: SNES Programing Help 2

Post by Drew Sebastino »

Oh wait, I just looked through the comments and I noticed something...
Khaz wrote:I have no clue what his plan was with the x-position and size bits for the second OAM table
That is what is "unofficially" called the "Soft OBC-1" technique. Because oam on the SNES is a pain in that there is a second sprite table for the 9th x bit and the sprite size bit, (see the relation?) psychopathicteen (unless he isn't actually the one) thought of the idea of creating a second sprite table that is just like the actual second sprite table in oam, except that this one is 512 bytes just like the first sprite table, so when you increment the pointer for the first sprite table, you are also incrementing the new one by the same amount. Later in the code, the contents from the new sprite table are dumped into the old 32 byte one. At this point, you have both x and y free, so it is much faster and easier. See this: (made by psychopathicteen) http://wiki.superfamicom.org/snes/show/ ... ay+sprites

You know, I remember at one point, you said something about looking directly at the object table for object information in the metasprite routine. Because I was using both x and y in the routine, I figured that I wouldn't be able to directly pull information from the object table, so I had it to where I dumped metasprite related object stuff into different registers that I wouldn't have to index because they don't move. Can you please explain?
User avatar
Khaz
Posts: 314
Joined: Thu Dec 25, 2014 10:26 pm
Location: Canada

Re: SNES Programing Help 2

Post by Khaz »

Espozo wrote:That is what is "unofficially" called the "Soft OBC-1" technique. Because oam on the SNES is a pain in that there is a second sprite table for the 9th x bit and the sprite size bit, (see the relation?) psychopathicteen (unless he isn't actually the one) thought of the idea of creating a second sprite table that is just like the actual second sprite table in oam, except that this one is 512 bytes just like the first sprite table, so when you increment the pointer for the first sprite table, you are also incrementing the new one by the same amount. Later in the code, the contents from the new sprite table are dumped into the old 32 byte one. At this point, you have both x and y free, so it is much faster and easier. See this: (made by psychopathicteen) http://wiki.superfamicom.org/snes/show/ ... ay+sprites
I suspected there would be something like that. It just seemed like a bizzare way to go about it to me, if nothing else just for tying up those 512 bytes in prime direct-page WRAM real estate. Especially since I'm cramming my whole object list in that same $2000.

I just rotated each bit into a temporary register once I'd isolated them, and stored the register to the OAM2 table every time it fills up.
Espozo wrote:You know, I remember at one point, you said something about looking directly at the object table for object information in the metasprite routine. Because I was using both x and y in the routine, I figured that I wouldn't be able to directly pull information from the object table, so I had it to where I dumped metasprite related object stuff into different registers that I wouldn't have to index because they don't move. Can you please explain?
What I meant is using direct page, so that you can just relocate your base address and then run the exact same code again to process another object. Like, say...

Code: Select all

.EQU objectXVelocity    $00
.EQU objectYVelocity    $02

;at the start of each object as you loop through the list
lda AddressOfCurrentObject    ;make sure you're in 16-bit A mode
tcd                           ;set direct page (D) register to address of current object slot

;assuming you do your build-metasprite routine as you process each object...
;your direct page register will be set to the current object slot, so you just...

lda.b objectXVelocity    ;this will read from the address in the D register
lda.b objectYVelocity    ;this will read from the address in the D register plus $02
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: SNES Programing Help 2

Post by Drew Sebastino »

Khaz wrote:I suspected there would be something like that. It just seemed like a bizzare way to go about it to me, if nothing else just for tying up those 512 bytes in prime direct-page WRAM real estate. Especially since I'm cramming my whole object list in that same $2000.I just rotated each bit into a temporary register once I'd isolated them, and stored the register to the OAM2 table every time it fills up.
That would be ever so slightly slower. It seems like with the SNES, if you can do just one less instruction even if you use 10 extra registers, than you do that, because the SNES has just about as much ram as you could ever possibly want, but not necessarily processing power.

Wait, oh yeah, so the direct page is kind of like an index register, even though it isn't at the end of the operand like x and y? why didn't they just do "lda Register,d"?
User avatar
Khaz
Posts: 314
Joined: Thu Dec 25, 2014 10:26 pm
Location: Canada

Re: SNES Programing Help 2

Post by Khaz »

Espozo wrote:Wait, oh yeah, so the direct page is kind of like an index register, even though you don't mess with it directly?
As far as I can recall off the top of my head, the only ways you interact with the D register are PHD/PLD and TCD/TDC, to set it or read it.

The effect of the D register is that every time you use a "Direct Page" instruction (generally speaking, an instruction with a one-byte operand like lda $10), the address in the D register is added to the operand (So if D is $1500 that lda reads from $1510).

It's simpler than it sounds (until you get into indirect addressing modes).
psycopathicteen
Posts: 3197
Joined: Wed May 19, 2010 6:12 pm

Re: SNES Programing Help 2

Post by psycopathicteen »

I just thought of a more efficient way of storing coordinates. "9.7" format. 9 bit X and Y coordinates, with 7 bits of subpixel accuracy. This would require a scrolling level map.
Post Reply