It is currently Mon Oct 23, 2017 5:41 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 74 posts ]  Go to page Previous  1, 2, 3, 4, 5  Next
Author Message
PostPosted: Fri Aug 23, 2013 5:05 pm 
Offline
User avatar

Joined: Mon Sep 27, 2004 8:33 am
Posts: 3715
Location: Central Texas, USA
If PC reads work, then zero-page reads should be working.


Top
 Profile  
 
PostPosted: Fri Aug 23, 2013 6:54 pm 
Offline
User avatar

Joined: Thu Sep 23, 2010 7:28 pm
Posts: 232
blargg wrote:
If PC reads work, then zero-page reads should be working.


Should be, but not if he reads the opcode from CPU memory then reads some operands from VRAM due to typos. It looks like he's specifying the address space to take the data from each time he pulls in a byte for anything.


Top
 Profile  
 
PostPosted: Fri Aug 23, 2013 8:38 pm 
Offline
User avatar

Joined: Sat Jan 03, 2009 3:28 pm
Posts: 59
Location: Oregon
Zeropage reads work correctly on nestest.nes, so I don't understand why they aren't here.

I pass the ppu to readRAM so that if $2002 is read I can clear the writeToggle or if $2007 is read I can use/increment the ppuAddress. I have a separate function for reading from VRAM creativity called readVRAM.

Edit: Somehow I screwed my cpu code up and it wasn't passing any of the tests except for basics. So I reverted back to an older file and everything is working again. I'm trying to figure out why my ROR instructions won't pass.


Top
 Profile  
 
PostPosted: Sat Aug 24, 2013 9:38 am 
Offline
User avatar

Joined: Thu Sep 23, 2010 7:28 pm
Posts: 232
Ah, okay that makes sense. My mistake.

For ROR, you should have a temp variable to store whether bit 0 of the operand is 0 or 1 before changing it. Now shift the value right by a bit. If the actual CPU carry flag is set, now set bit 7 of the operand. Then, replace the actual CPU carry flag with what you saved in the temp variable. Last, calculate the zero and negative flags. That should do it.


Top
 Profile  
 
PostPosted: Sun Aug 25, 2013 9:30 pm 
Offline
User avatar

Joined: Sat Jan 03, 2009 3:28 pm
Posts: 59
Location: Oregon
For some reason only tests 01-03 print out a pass/fail message. 04, 05, and 06 only output the missed opcodes. If no opcodes are missed, then nothing gets printed out. Is that supposed to happen?

Here is what I'm getting right now for 04:
Quote:
�ްa
P

�4
�
�
/usr/lib/x86_64-linux-gnu/libXdmcp.so.6.0.0
@


�4
�
�
/usr/lib/x86_64-linux-gnu/libXdmcp.so.6.0.0
DONE


Top
 Profile  
 
PostPosted: Mon Aug 26, 2013 1:09 am 
Offline
User avatar

Joined: Thu Sep 23, 2010 7:28 pm
Posts: 232
Yes, that's normal.


Top
 Profile  
 
PostPosted: Mon Aug 26, 2013 10:27 pm 
Offline
User avatar

Joined: Sat Jan 03, 2009 3:28 pm
Posts: 59
Location: Oregon
Well I'm happy to report that I passed every test except for 11-stack. I've checked everything that has to do with the stack, and I cannot find my error.

Test output:
Quote:
48 PHA
08 PHP
68 PLA
28 PLP
9A TXS
BA TSX

11-stack

Failed


I'm guessing that since I'm missing pretty much every instruction that effects the stack, it must be how I wrap around. These are my two stack functions:

Code:
const void cpu::pushStack(memory* memory, unsigned char &data, ppu* ppu)
{
   memory->writeRAM(SP, data, ppu);
   SP--;            //Decrememnt after writing memory.
   if(SP == 0x00FF) SP = 0x01FF;
}

const unsigned char cpu::popStack(memory* memory,ppu* ppu)
{
   SP++;
   if(SP == 0x2000) SP = 0x0100;
   return( memory->readRAM(SP, ppu) );
}


Top
 Profile  
 
PostPosted: Mon Aug 26, 2013 11:01 pm 
Offline
User avatar

Joined: Mon Sep 27, 2004 8:33 am
Posts: 3715
Location: Central Texas, USA
Quote:
if(SP == 0x2000) SP = 0x0100;

There's the problem, 0x2000 instead of 0x200.


Top
 Profile  
 
PostPosted: Wed Aug 28, 2013 1:16 am 
Offline
User avatar

Joined: Sat Jan 03, 2009 3:28 pm
Posts: 59
Location: Oregon
Of course it was a dumb mistake like that. I checked that function multiplie times and missed it. Now I passed, which means my CPU checks out.

After comparing my VRAM dump of Donkey Kong to the one posted earlier in this thread, I'm way off. I'm not exactly sure why, though. I went through and rechecked how all the registers worked and how nametable mirroring worked on the wiki to make sure my code looked ok. My function that writes to VRAM in my memory class looks ok, so I'm thinking that maybe my ppu is somehow at an incorrect address when I go to write data to VRAM. Maybe if I ask these few questions I'll be closer to figuring out why my VRAM isn't being filled correctly.

1. What is the ppu address supposed to be at startup? Currently I'm starting it up at $2000, but I have no idea if it matters or not.
2. Should the ppu be doing its normal rendering operations during the first few screens at startup? Right now, mine starts fetching data/shifting registers/outputting pixels from the instant the emulator starts. To me, this seems like it would be wrong since the game is going to be polling $2002 until Vblank happens - which means that nothing but the chr-rom will actually be filled. Could my ppu address be incorrect because I'm incrementing it from the get go?
3. The only way the game can fill up VRAM is by writing the address it wants to fill with data at $2006 and then the writing data to $2007, correct?

This is my code to fill VRAM:
Code:
void memory::writeVRAM(unsigned short address, unsigned char &data)
{
   //Address above 0x3FFF wrap around between the 0x0000 and 0x3FFF range.
   address &= 0x3FFF;

   if(address >= 0x2000 && address <= 0x2FFF)
   {
      if(horizontalMirror)
      {
         //$2000 = $2400 and $2800 = $2C00
         if(address < 0x2400)         //Address in first nametable
         {
            VRAM[address] = data;
            VRAM[address + 0x400] = data;
            VRAM[address + 0x1000] = data;
            VRAM[address + 0x1400] = data;   
         }
         else if(address < 0x2800)
         {
            VRAM[address] = data;
            VRAM[address - 0x400] = data;
            VRAM[address + 0xC00] = data;         //Mirror for $2000 range
            VRAM[address + 0x1000] = data;
         }
         else if(address < 0x2C00)
         {
            VRAM[address] = data;
            VRAM[address + 0x400] = data;
            VRAM[address + 0x1000] = data;
            if(address < 0x2B00) VRAM[address + 0x1400] = data;   //No mirror above 0x2EFF
         }
         else
         {
            VRAM[address] = data;
            VRAM[address - 0x400] = data;
            if(address < 0x2F00) VRAM[address + 0x1000] = data;   //No mirror above 0x2EFF
            VRAM[address + 0xC00] = data;
         }
      }
      else    //Vertical mirroring
      {
         //$2000 = $2800 and $2400 = $2C00
         if(address < 0x2400)         //Address in first nametable
         {
            VRAM[address] = data;
            VRAM[address + 0x800] = data;
            VRAM[address + 0x1000] = data;
            VRAM[address + 0x1800] = data;   
         }
         else if(address < 0x2800)      //$2400 range
         {
            VRAM[address] = data;
            VRAM[address + 0x800] = data;
            VRAM[address + 0x1000] = data;   
            if(address < 0x2700) VRAM[address + 0x1800] = data;
         }
         else if(address < 0x2C00)      //$2800 range
         {
            VRAM[address] = data;
            VRAM[address - 0x800] = data;
            VRAM[address + 0x800] = data;         //Mirror for $2000 range
            VRAM[address + 0x1000] = data;
         }
         else               //$2C00 range
         {
            VRAM[address] = data;
            VRAM[address - 0x800] = data;
            if(address < 0x2F00) VRAM[address + 0x1000] = data;   //No mirror above 0x2EFF
            VRAM[address + 0x800] = data;            //Mirror for $2400 range
         }
      }   
   }
   else   //Pallete write
   {
      //Pallete glitch where these areas of BG pallete are also copied to sprite pallete
      if(address == 0x3F00 || address == 0x3F04 || address == 0x3F08 || address == 0x3F0C)
      {
         //Sets and mirrors the data
         for(int i = address; i < 0x4000; i += 0x20)
            VRAM[i] = VRAM[i + 0x10] = data;
      }
      else
      {
         //Sets and mirrors the data
         for(int i = address; i < 0x4000; i += 0x20)
            VRAM[i] = data;
      }
   }
}


Top
 Profile  
 
PostPosted: Wed Aug 28, 2013 5:37 am 
Offline
User avatar

Joined: Fri Nov 19, 2004 7:35 pm
Posts: 3944
The PPU begins with NMIs disabled and sprites and background disabled. If that's not enough, the game will also likely disable NMIs and disable sprites and backgrounds immediately at boot. So you'll be drawing a bunch of gray pixels (background color) until the game has really started up. Also before the first frame has rendered completely, writes to 2000 and 2001 are ignored, so you can't enable NMIs or enable spites and backgrounds.
The Famicom is different in that the PPU is immediately ready after a system reset, so that's why games will disable NMIs and disable sprites and backgrounds. While it doesn't matter on the NES, it does on the Famicom.

Also, it sounds like you might be having problems with disabling rendering. When rendering is disabled (sprites and backgrounds turned off), the PPU doesn't do any memory fetches, and doesn't automatically increment its addresses. So at power on, it won't be incrementing the PPU address at all.

Initial VRAM address simply doesn't matter, nothing relies on it at all. But it's supposed to be 0000.

Also, remember that the first 2006 write sets the high byte, and the second 2006 write sets the low byte. It's the only Big Endian part of the entire system.

_________________
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!


Top
 Profile  
 
PostPosted: Wed Aug 28, 2013 5:53 am 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 3192
Location: Mountain View, CA, USA
Dartht33bagger wrote:
1. What is the ppu address supposed to be at startup? Currently I'm starting it up at $2000, but I have no idea if it matters or not.
2. Should the ppu be doing its normal rendering operations during the first few screens at startup? Right now, mine starts fetching data/shifting registers/outputting pixels from the instant the emulator starts. To me, this seems like it would be wrong since the game is going to be polling $2002 until Vblank happens - which means that nothing but the chr-rom will actually be filled. Could my ppu address be incorrect because I'm incrementing it from the get go?

Both questions answered in the Wiki: http://wiki.nesdev.com/w/index.php/PPU_power_up_state

Dwedit's comments on Famicom vs. NES apply as well, naturally.

Also, the above "VRAM writing code" (which is really just your code used to handle mirroring and certain PPU memory regions) sheds absolutely no light on how you're handling writes to $2000, $2005, $2006, and $2007.


Top
 Profile  
 
PostPosted: Wed Aug 28, 2013 6:34 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19122
Location: NE Indiana, USA (NTSC)
The way you're handling mirroring appears sort of suspect. You're executing each write more than once into a space larger than the actual memory. But most ASIC mappers can change the mirroring at runtime, and when it changes, games expect the existing data to get moved around. Say a program writes the following to the nametables while mirroring is set to vertical (not to scale):
Code:
Data wri  tten to
one name  table
stays th  ere when
nametabl  es move.

Data wri  tten to
one name  table
stays th  ere when
nametabl  es move.

If the program then proceeds to switch the mirroring to horizontal, it expects the data to instantly become logically rearranged as follows:
Code:
Data wri  Data wri
one name  one name
stays th  stays th
nametabl  nametabl

tten to   tten to
table     table
ere when  ere when
es move.  es move.

Rad Racer relies on this, as do a lot of games that use a status bar.

You'll especially notice this when you try to implement one-screen mirroring as used by mapper 7 and some MMC1 games.


Top
 Profile  
 
PostPosted: Wed Aug 28, 2013 9:06 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10068
Location: Rio de Janeiro - Brazil
tepples has a pretty good point. Mirroring doesn't mean that the data is written to multiple locations... it means that there's only one physical location, which is accessed through multiple addresses. In emulators this is often implemented with pointers, which can be changed to point anywhere you want (and like tepples said, many mappers do constantly change the mirroring settings as they run).


Top
 Profile  
 
PostPosted: Wed Aug 28, 2013 1:52 pm 
Offline
User avatar

Joined: Sat Jan 03, 2009 3:28 pm
Posts: 59
Location: Oregon
I completely forgot that I could use pointers to reference data pieces inside of an array. I'll come home and re-write my mirror functions accordingly later tonight with pointers.

Here is how I handle writes to registers for those who asked:

Code:
switch(address)
   {
      case 0x2000:
         ppu->ppuTempAddress &= ~0x0C00;         //Clears bits 10 and 11
         ppu->ppuTempAddress |= ((data & 0x03) << 10);   //Shifts the nametable select bits to bit 10 and 11 in the temp address
         break;
      case 0x2005:
         if(ppu->writeToggle)
         {
            ppu->ppuTempAddress &= 0x8C1F;            //Makes bits 5-9 and 12-14 zero
            ppu->ppuTempAddress |= (data & 0xF8) << 2;      //Shifts the data to fill bits 5-9
            ppu->ppuTempAddress |= (data & 0x07) << 12;      //Shifts the data to fill bits 12-14
            ppu->writeToggle = false;
            break;
         }
         else
         {
            ppu->ppuTempAddress &= ~0x001F;            //Makes the first 5 bits zero
            ppu->ppuTempAddress |= (data & 0xF8) >> 3;      //Gets the last 5 bits for the address
            ppu->fineXScroll = data & 0x07;            //Gets the first three bits
            ppu->writeToggle = true;
         }
         break;
      case 0x2006:
         if(ppu->writeToggle)
         {
            ppu->ppuTempAddress &= 0xFF00;            //Clears the lower 8 bits
            ppu->ppuTempAddress |= data;            //Lower byte of address
            ppu->ppuAddress = ppu->ppuTempAddress;         //Set after temp address is filled
            ppu->writeToggle = false;
         }
         else
         {
            ppu->ppuTempAddress &= 0x00FF;            //Clears upper 8 bits
            ppu->ppuTempAddress = (data & 0x3F) << 8;      //Upper piece of address
            ppu->writeToggle = true;
         }
         break;
      case 0x2007:
         writeVRAM(ppu->ppuAddress, data);
         if(RAM[0x2000] & 0x04) ppu->ppuAddress += 32;         //Checks increment bit
         else ppu->ppuAddress++;
         break;
   }


Top
 Profile  
 
PostPosted: Wed Aug 28, 2013 2:01 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 3192
Location: Mountain View, CA, USA
Comment in passing (have much to do today, sorry) -- thumbs up to using pointers for the mirroring. That's absolutely the proper way to do it, ditto with a large amount of mapper implementation (PRG/CHR page selection). If I see memcpy() I will stab. ;-)


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 74 posts ]  Go to page Previous  1, 2, 3, 4, 5  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 6 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group