SNES Test Program

Strictly for discussing ZSNES development and for submitting code. You can also join us on IRC at irc.libera.chat in #zsnes.
Please, no requests here.

Moderator: ZSNES Mods

Post Reply
byuu

SNES Test Program

Post by byuu »

Ok, I've got all tests passing except $6f and $73. Working with the first one... this is the code:

Code: Select all

0093FD LDX #$80               A:0000 X:0021 Y:0010 S:01FD DB:00 D:0000 P:17 e
0093FF STX $2115     [002115] A:0000 X:0080 Y:0010 S:01FD DB:00 D:0000 P:95 e
009402 REP #$10               A:0000 X:0080 Y:0010 S:01FD DB:00 D:0000 P:95 e
009404 LDA #$0000             A:0000 X:0080 Y:0010 S:01FD DB:00 D:0000 P:85 e
009407 LDX #$0200             A:0000 X:0080 Y:0010 S:01FD DB:00 D:0000 P:07 e
00940A STX $40       [000040] A:0000 X:0200 Y:0010 S:01FD DB:00 D:0000 P:05 e
00940C STA $2116     [002116] A:0000 X:0200 Y:0010 S:01FD DB:00 D:0000 P:05 e
00940F LDX $2139     [002139] A:0000 X:0200 Y:0010 S:01FD DB:00 D:0000 P:05 e
009412 CPX $40       [000040] A:0000 X:0200 Y:0010 S:01FD DB:00 D:0000 P:05 e
009414 BNE $9435     [009435] A:0000 X:0200 Y:0010 S:01FD DB:00 D:0000 P:07 e
009416 INC $40       [000040] A:0000 X:0200 Y:0010 S:01FD DB:00 D:0000 P:07 e
009418 INC A                  A:0000 X:0200 Y:0010 S:01FD DB:00 D:0000 P:05 e
009419 INC A                  A:0001 X:0200 Y:0010 S:01FD DB:00 D:0000 P:05 e
00941A INC A                  A:0002 X:0200 Y:0010 S:01FD DB:00 D:0000 P:05 e
00941B INC A                  A:0003 X:0200 Y:0010 S:01FD DB:00 D:0000 P:05 e
00941C INC A                  A:0004 X:0200 Y:0010 S:01FD DB:00 D:0000 P:05 e
00941D INC A                  A:0005 X:0200 Y:0010 S:01FD DB:00 D:0000 P:05 e
00941E INC A                  A:0006 X:0200 Y:0010 S:01FD DB:00 D:0000 P:05 e
00941F INC A                  A:0007 X:0200 Y:0010 S:01FD DB:00 D:0000 P:05 e
009420 CMP #$0100             A:0008 X:0200 Y:0010 S:01FD DB:00 D:0000 P:05 e
009423 BNE $940C     [00940C] A:0008 X:0200 Y:0010 S:01FD DB:00 D:0000 P:84 e
Ok, so its setting $2115 to standard addressing and inc on $2119/$213a access.

This is a VRAM dump:

Code: Select all

00022002555555555555555555555555
0102AAAAAAAAAAAAAAAAAAAAAAAAAAAA
0202FFFFFFFFFFFFFFFFFFFFFFFFFFFF
03020000000000000000000000000000
...
ZSNES has the exact same data in VRAM at this point and it passes the test.

With the inc #8, it's setting the VRAM pointer to $00n0 (after multiplying by two) each test. It then tries to read the value from $2139, however: I fail this test because I don't immediately return the value read from VRAM, or basically, I treat the first read as a dummy read and return the previous read instead. My code looks like this:

Code: Select all

//VMDATALREAD
uint8 bPPU::mmio_r2139() {
uint16 addr = get_vram_address();
uint8 r = regs.vram_readbuffer;
  if(regs.vram_incmode == 0) {
    addr &= 0xfffe;
    regs.vram_readbuffer  = vram_read(addr);
    regs.vram_readbuffer |= vram_read(addr + 1) << 8;
    regs.vram_addr += regs.vram_incsize;
  }
  return r;
}

//VMDATAHREAD
uint8 bPPU::mmio_r213a() {
uint16 addr = get_vram_address() + 1;
uint8 r = regs.vram_readbuffer >> 8;
  if(regs.vram_incmode == 1) {
    addr &= 0xfffe;
    regs.vram_readbuffer  = vram_read(addr);
    regs.vram_readbuffer |= vram_read(addr + 1) << 8;
    regs.vram_addr += regs.vram_incsize;
  }
  return r;
}
Which is pretty much exactly as anomie described it, and modifying this code in any way breaks tons of games.
get_vram_address() returns the absolute 64k address, not the word version thats written to $2116. It also adjusts for address remapping/etc. which isn't used in this test.

Basically, ZSNES and the SNES test program expect the VRAM read to return the full 16-bit value immediately after the first $2139/$213a read... can someone please explain what I'm doing wrong? :/

$73 is, AFAIK, supposed to be the RTO test, right? Probably be a while before I add support for that one...
Overload
Hazed
Posts: 70
Joined: Sat Sep 18, 2004 12:47 am
Location: Australia
Contact:

Re: SNES Test Program

Post by Overload »

byuusan wrote:Which is pretty much exactly as anomie described it, and modifying this code in any way breaks tons of games.
get_vram_address() returns the absolute 64k address, not the word version thats written to $2116. It also adjusts for address remapping/etc. which isn't used in this test.

Basically, ZSNES and the SNES test program expect the VRAM read to return the full 16-bit value immediately after the first $2139/$213a read... can someone please explain what I'm doing wrong? :/
When you set $2116/17 the VRAM at that address is buffered. The SNES actually has two 32K SRAMs for VRAM. 2118/39 accesses the first chip and 2119/3a access the second chip. This way the ppu is able to read 16-bits in one cycle instead of two.
byuu

Post by byuu »

Excellent! 20 of 21 tests now pass. Thank you, Overload. I'm familiar with the two 32k Sony chips, but I didn't realize setting the VRAM address would update the buffer. That essentially makes the second read the dummy instead of the first (well, the second is still valid, but it's one behind...), ah well.

I went ahead and gave RTO a shot, not as bad as I thought it would be at least. I have the game setting $2103 bit 7 but not using it (shouldn't matter for RTO flags), and I have the renderer stopping after 32 sprites/34 tiles.

But the flags aren't passing the last SNES test. anomie's document is very vague with when these flags are set. Are they set every scanline, or once per frame? Are they set immediately when $213e is read, at the start of the scanline, at the start of hblank on the current scanline, at the end of the scanline, at the end of the frame? Are they set and cleared at the start and end of vblank? Are they set once per frame if ANY scanlines have >32 sprites or >34 tiles?

Right now, I have it setting the flags immediately at V=any, H=0. If V isn't onscreen (scanline 0 or 224+/239+), they're cleared to zero. Reads from $213e return the results but don't set/clear the conditions for subsequent reads. I made a small test program that validates I have the code working right, but it's obviously incorrect as the SNES test program still chokes.

Also, I assume the value last written to $2102-$2103 is reloaded at V=224/239/H=0, and FirstSprite set at V=0/H=0, set as FirstSprite = ((oam_addr << 1) & 0xfe) >>1, and not ((oam_addr) & 0xfe) >> 1? (oam_addr = $2102 | $2103 << 8) The latter makes the star thing skip by two each time in the test program.
Right now, I have to OAM renderer run through i=0-127, and test sprite (i + FirstSprite) & 127, and each time if any line of the sprite equals the current scanline being rendered, it adds 1 to the rangeover counter, and (tile_width / 8) to the timeover counter. Even if that sprite/tile ends up not being rendered, or even if OAM is disabled, or even if another sprite before it already covered all pixels that this sprite uses.
This makes the SNES test program's stars blink left to right, but then it cuts off sprites in the next test (with the big princess sprite), and the cursors in FF6...

[Later:]
The princess sprite clipping was because I didn't ignore sprites out of x bounds. anomie's doc says "Only those sprites with -size < X <= 256 (not <= 255) are considered on the scanline."
I don't follow what -size < x does. So I'm using this:
if(current_sprite.x > 256 && ((current_sprite.x + current_sprite.width) & 511) > 256)continue;
Hopefully that's what he meant.

The cursor in FF6 battles is still invisible, though. Here's a little log I made of that frame:
http://byuu.org/files/ff6log.txt
Sprites 115-123 are all blank tiles, but have x set to the same position as the cursor, and are within both the x and y ranges needed to count as valid sprites. I take it my implementation of the tile count is what's incorrect...

[Later:]
Added in code to ignore tiles that aren't visible on a sprite on a line, ala snes9x gfx.cpp. Below code happens once per valid sprite within x/y boundaries.

Code: Select all

  regs.oam_itemcount++;
//add all visible tiles to regs.oam_tilecount
  if(current_sprite.x > 256) {
  //if sprite clips on left edge of the screen
    regs.oam_tilecount += ((current_sprite.x + current_sprite.width + 7) & 511) >> 3;
  } else if(current_sprite.x + current_sprite.width >= 257) {
  //if sprite clips on right edge of screen
    regs.oam_tilecount += (257 - current_sprite.x + 7) >> 3;
  } else {
  //if entire sprite is visible
    regs.oam_tilecount += current_sprite.width >> 3;
  }
Doesn't do much since the tiles that are breaking things in FF6 are all within range though...
byuu

Post by byuu »

Hooray!

I believe that makes bsnes the fourth emulator to pass this test without using hacks; after snes9x, sneese, and super sleuth. Not that it matters...

Now all I need to do is fix that darned FF6 cursor, and I'm golden.

Almost forgot: I pass the RTO test by setting range/time over to zero at V=0/H=0, and each line I bitwise OR the range/time over results of each scanline. So if scanline 2 sets range over, then every subsequent scanline has $213e bit 6 set until V=0/H=0 again. That's what it looks like snes9x is doing, and it works, so... cool.
Last edited by byuu on Sat Jul 25, 2009 9:13 pm, edited 1 time in total.
Nightcrawler
Romhacking God
Posts: 922
Joined: Wed Jul 28, 2004 11:27 pm
Contact:

Post by Nightcrawler »

Congratulations Byuu! You've come a long way in a short amount of time. Hopefully you don't loose interest in bsnes and continue to develop it. It's really starting to turn out nice. Passing the electronics test is certainly an accomplishment.
[url=http://transcorp.romhacking.net]TransCorp[/url] - Home of the Dual Orb 2, Cho Mahou Tairyku Wozz, and Emerald Dragon SFC/SNES translations.
[url=http://www.romhacking.net]ROMhacking.net[/url] - The central hub of the ROM hacking community.
anomie
Lurker
Posts: 151
Joined: Tue Dec 07, 2004 1:40 am

Post by anomie »

byuusan wrote:But the flags aren't passing the last SNES test. anomie's document is very vague with when these flags are set.
That's because we don't actually know exactly when they're set. But as you've surmised, they seem to be set at some point in the vicinity of the first scanline with a RO or a TO, and not cleared until the start of the new frame. Best guess is that they're set sometime during the scanline before the RTO scanline is displayed (RO during the scanline and TO at the very end of the H-Blank), since that's when we guess OBJ processing for the next line is occurring. Figuring out RO especially will be fun, since it probably varies depending on which particular sprite is ROing (i.e. 32 would set earlier than if it gets all the way to 127).
Also, I assume the value last written to $2102-$2103 is reloaded at V=224/239/H=0,
Another thing we don't really know, that's as good a guess as any. Remember this doesn't occur during force blank.
I don't follow what -size < x does.
You can consider the values X=0x101 through 0x1ff as -255 through -1. I think your code snippet is about right.
The cursor in FF6 battles is still invisible, though. Here's a little log I made of that frame:
http://byuu.org/files/ff6log.txt
Sprites 115-123 are all blank tiles, but have x set to the same position as the cursor, and are within both the x and y ranges needed to count as valid sprites. I take it my implementation of the tile count is what's incorrect...
Remember, Range picks the first 32 sprites on the line, but then Time starts from the LAST one and works back to the first. IIRC, that cursor is the highest-numbered sprite on the line, so Time processing loads it first.

IOW, in setting up sprites like this:

Code: Select all

0 1 2 3 4 5 6 7 8 9 A B C D E F G H I J K L M N O P Q R S TT UU VV W
Range will pick up sprites 0-V; W will be dropped. Time will pick both V tiles, both U tiles, both T tiles, and S-1; 0 will be dropped.
Starman Ghost
Trooper
Posts: 535
Joined: Wed Jul 28, 2004 3:26 am

Post by Starman Ghost »

byuusan wrote: I believe that makes bsnes the fourth emulator to pass this test without using hacks; after snes9x, sneese, and super sleuth. Not that it matters...
You mean zsnes doesn't pass this without hacks?
[code]<Guo_Si> Hey, you know what sucks?
<TheXPhial> vaccuums
<Guo_Si> Hey, you know what sucks in a metaphorical sense?
<TheXPhial> black holes
<Guo_Si> Hey, you know what just isn't cool?
<TheXPhial> lava?[/code]
adventure_of_link
Locksmith of Hyrule
Posts: 3634
Joined: Sun Aug 08, 2004 7:49 am
Location: 255.255.255.255
Contact:

Post by adventure_of_link »

No. ZSnes doesn't pass it at all.
PS: I was using the 7-10-2k5 windows WIP build to test this.
Image
<Nach> so why don't the two of you get your own room and leave us alone with this stupidity of yours?
NSRT here.
byuu

Post by byuu »

That's because we don't actually know exactly when they're set.
That would be a pretty good reason.
Another thing we don't really know, that's as good a guess as any. Remember this doesn't occur during force blank.
Right, I don't reload it when $2100 bit 7 is set.
You can consider the values X=0x101 through 0x1ff as -255 through -1. I think your code snippet is about right.
That seems to complicate things a bit more having to use a signed 9-bit number where 0x0100 is still positive. It seems to work either way, but your explanation makes more sense on why xpos = 510 will put a sprite on the left-hand side of the screen.
Remember, Range picks the first 32 sprites on the line, but then Time starts from the LAST one and works back to the first.
o_O... utter madness. That fixed it, but I had to totally rewrite my OAM renderer. Thanks again for the help. I can't imagine where I'd be without all of the help.
I have the game run through sprites (0-127+FirstSprite)&127, pick out the first 32. If there's a 33rd, set RO. Then run from selected sprites 31-0 and extract tile/palette/x/y/flip/priority info into an array. Reads in 34, sets TO on the 35th tile. It selects tiles from left to right after flipping, so if a 16x16 tile has tiles 34 and 35, the right half of the sprite will not render. Then I run from tiles 0-33 and draw them. I draw subsequent tiles on TOP of other tiles, since going through the sprite list backwards reverses the tile priority order, so I just write on top of all the old tiles until I get to the last tile in the list. All pixel writes go to a sprite buffer, so at the end I run through the entire scanline and write all pixels set in the sprite line buffer when the priority is high enough. Fun, fun, fun. Looks like adding an option to make the PPU (not $2137) ignore RTO will be a bit trickier to add now ;)
That seems to fix every game I have. Only slight issue is the middle star isn't the one that's invisible when interlace is enabled on the character test, it's the second to last one or so. I'll worry about that later, though.
You mean zsnes doesn't pass this without hacks?
It fails tests 14, 15, and 20 as of the latest WIP (there are 21 of them). I'm unaware of any emulator that uses hacks to pass this, but you never know. Besides being easy, it would be an incredibly lame thing to do :P
14 is a latch timing test to see if v/hblank flags are set at the right time and if NMI triggers at the right time.
15 is some sort of DMA/NMI timing/flag test, or something. I really can't tell from looking at it right now.
20 is the RTO flag test.
14 is probably the only one ZSNES would have a lot of trouble fixing if they really wanted to. It looks like ZSNES already supports RTO with screen rendering, though its very buggy.

None of these tests really matter much, as very few games rely on this stuff. I just like to fix stuff like this in hopes that it fixes other things (and it did, DKC sprites weren't working because I wasn't performing OAM address reset before).

Oh, sneese seems to fail the test too, actually. No idea why since it lacks a debugger though :(
<_<
Rookie
Posts: 24
Joined: Sat May 07, 2005 12:29 am
Location: A boat.
Contact:

Post by <_< »

byuusan wrote:anomie's doc says "Only those sprites with -size < X <= 256 (not <= 255) are considered on the scanline."
I don't follow what -size < x does.
If (Size * -1) is less than X, and X is less than/equal to 256, then it's on the line. 'Tis my interpretation, anyway.
It's-a me, Mario! Wait, no it's not.
Post Reply