PPU memory read buffer is not the open bus?

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

Post Reply
User avatar
Haruki_nyan
Posts: 4
Joined: Fri Aug 02, 2019 11:23 am
Location: United States
Contact:

PPU memory read buffer is not the open bus?

Post by Haruki_nyan » Sun Dec 06, 2020 9:46 pm

I am currently modifying the VirtuaNES emulator by "Norix" to improve accuracy, add new features, etc.. I was running the test ROM test_ppu_read_buffer.nes, and received this error:

Code: Select all

TEST:test_ppu_read_buffer
-----------------------------
Testing basic PPU memory I/O.
 BRK?
BRK executed where should not!
PC=1D20,AXYSP=00/D7/00/F7/36
Test failed.

-----------------------------
Failed tests: 20 19
Untested: 7-11 21-79
-----------------------------
PPU memory read buffer is not
the open bus. Reading the bus
should repeat the last value
that was transferred, not
disclose the buffered byte.

Failed #19
I am not sure what this means exactly, so I don't know how to fix it. Any explanation is welcome.

tepples
Posts: 22275
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: PPU memory read buffer is not the open bus?

Post by tepples » Sun Dec 06, 2020 10:40 pm

There are three separate 8-bit registers

CPU Open Bus - Returned when nothing is decoded for reading at a particular CPU address
PPU I/O Data Bus - Returned whenever reading any PPU register
PPU Memory Read Buffer - Used only for reading $2007

Reading a write-only PPU register, or reading unused bits of PPU status ($2002), returns the last value written to or read from a PPU register. Even writes to read-only PPU registers will set this value. You can, say, write a value to $2002 and read it back from $2001. This value is stored in PPU I/O Data Bus, called "PPUGenLatch" in the FCEUX source code and "io_db" in Visual 2C02. Physically, this is an octal dynamic latch arising from the capacitance of relatively long metal wires inside the PPU. When reading an actual readable register, that is, $2002, $2004, or $2007, the circuit responsible for that register drives voltages onto this latch. See "Riding the open bus" for discussion and a test ROM.

The PPU Memory Read Buffer is separate from both the buses. Every read from $2007 copies the PPU Memory Read Buffer to the PPU I/O Data Bus and then refills the PPU Memory Read Buffer. In turn, every read from this or any other PPU register drives the PPU I/O Data Bus onto the pins that connect to the CPU data bus.

On the Super NES, each of the two PPUs has its own I/O Data Bus. This is where you get "PPU1 Open Bus" and "PPU2 Open Bus", as one PPU does sprites and the other does backgrounds, palettes, and color math.

User avatar
Quietust
Posts: 1682
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: PPU memory read buffer is not the open bus?

Post by Quietust » Mon Dec 07, 2020 6:05 am

tepples wrote:
Sun Dec 06, 2020 10:40 pm
There are three separate 8-bit registers

CPU Open Bus - Returned when nothing is decoded for reading at a particular CPU address
PPU I/O Data Bus - Returned whenever reading any PPU register
PPU Memory Read Buffer - Used only for reading $2007
Of special note is that writing to $2007 does not affect the PPU Memory Read Buffer - instead, the value sits on the PPU I/O Data Bus for several cycles until it's ready to be written to VRAM.
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.

User avatar
Haruki_nyan
Posts: 4
Joined: Fri Aug 02, 2019 11:23 am
Location: United States
Contact:

Re: PPU memory read buffer is not the open bus?

Post by Haruki_nyan » Mon Dec 07, 2020 11:49 am

I am truly at a loss of how to fix this. `PPU7_Temp`(PPU Memory Read Buffer) is only ever written to in the `PPUDATA` reading code. It is never set to open bus, nor is it set when writing or reading any other register. It is set to 0xFF in the PPU::Reset function, but I doubt that's where the problem is.
Below is the entirety of the code (C++) for reading PPUDATA/$2007.

Code: Select all

#define PPUDATA 0x2007
#define PPU_INC32_BIT 0x04
BYTE PPU::Read(WORD addr){
	static BYTE data = 0x00;
	switch(addr){
		/*...*/
		case(PPUDATA):{
			// VRAM I/O Register(RW)
			WORD addr = loopy_v & 0x3FFF;
			data = PPU7_Temp;
			if(PPUREG[0] & PPU_INC32_BIT){
				loopy_v += 32;
			}
			else{
				loopy_v++;
			}
			if(addr >= 0x3000){
				if(addr >= 0x3F00){
					// data &= 0x3F;
					if(!(addr & 0x0010)){
						// return BGPAL[addr & 0x000F];
						data = BGPAL[addr & 0x000F];
					}
					else{
						// return SPPAL[addr & 0x000F];
						data = SPPAL[addr & 0x000F];
					}
				}
				addr &= 0xEFFF;
			}
			PPU7_Temp = PPU_MEM_BANK[addr >> 10][addr & 0x03FF];
		}
	}

	return data;
}
Does anyone have a suggestion for a solution, because I can't see it. :(

User avatar
Quietust
Posts: 1682
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: PPU memory read buffer is not the open bus?

Post by Quietust » Mon Dec 07, 2020 1:40 pm

Haruki_nyan wrote:
Mon Dec 07, 2020 11:49 am
I am truly at a loss of how to fix this. `PPU7_Temp`(PPU Memory Read Buffer) is only ever written to in the `PPUDATA` reading code. It is never set to open bus, nor is it set when writing or reading any other register. It is set to 0xFF in the PPU::Reset function, but I doubt that's where the problem is.
The source code for the "TEST_PPU_OPENBUS_MUST_NOT_COPY_READBUFFER" test (i.e. #19) is as follows:

Code: Select all

	; It does not matter which values we receive. It can be anything.
	; Still, use the 2D00-2DFF region because at least we know
	; that we have _initialized_ it.
	set_ppuaddr $2D00
	ldx #0
:	ldy PPUDATA
	cpy PPUCTRL
	bne @failed
@continue_loop:
	inx
	bne :-
	rts
@failed:
	; If it failed, figure out whether it failed because the data
	; came from read buffer.
	lda PPUCTRL
	sta temp2	; open bus
	lda PPUDATA
	cmp temp2	; did the bus really disclose the next byte?
	bne @alternative_diagnosis
	jmp fail_current_test
@alternative_diagnosis = @continue_loop
	; Not nagging about this failure for now.
The test runs 256 times, and each time reads first from $2007 and then from $2000 and bails out if the two values were different.

Exactly why the test is failing would require seeing what your PPU logic for reading from $2000 looks like - if it's doing "data = PPU7_Temp", then you need to remove that and let it just return "data" as-is, but you'll also need to take your "static BYTE data = 0x00;" and move it outside of your PPU::Read routine so that it's also used by PPU::Write (since the same buffer needs to be used for both directions).
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.

User avatar
Haruki_nyan
Posts: 4
Joined: Fri Aug 02, 2019 11:23 am
Location: United States
Contact:

Re: PPU memory read buffer is not the open bus?

Post by Haruki_nyan » Mon Dec 07, 2020 2:30 pm

The code for reading PPUCTRL/$2000 (and the other write-only registers) is as follows:

Code: Select all

	switch(addr){
		case(PPUCTRL):
		case(PPUMASK):
		case(OAMADDR):
		case(PPUSCROLL):
		case(PPUADDR):{
			data = openBus;
			break;
		}
		/*...*/
	}
The `static BYTE data = 0x00;` is just to prevent `data` being reallocated every function call, and to keep it local to PPU::Read(). I use the variable `openBus` to preserve the read/write buffer/open bus.

lidnariq
Posts: 10236
Joined: Sun Apr 13, 2008 11:12 am
Location: Seattle

Re: PPU memory read buffer is not the open bus?

Post by lidnariq » Mon Dec 07, 2020 2:44 pm

Is that "openBus" the system-wide one or the PPU's own internal one?

User avatar
Quietust
Posts: 1682
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: PPU memory read buffer is not the open bus?

Post by Quietust » Mon Dec 07, 2020 2:46 pm

Haruki_nyan wrote:
Mon Dec 07, 2020 2:30 pm
The code for reading PPUCTRL/$2000 (and the other write-only registers) is as follows:

Code: Select all

	switch(addr){
		case(PPUCTRL):
		case(PPUMASK):
		case(OAMADDR):
		case(PPUSCROLL):
		case(PPUADDR):{
			data = openBus;
			break;
		}
		/*...*/
	}
Then that's your problem - when you read from $2007 (or from $2002 or $2004), you must also set "openBus = data;"
Haruki_nyan wrote:
Mon Dec 07, 2020 2:30 pm
The `static BYTE data = 0x00;` is just to prevent `data` being reallocated every function call, and to keep it local to PPU::Read(). I use the variable `openBus` to preserve the read/write buffer/open bus.
Declaring a local variable as "static" will actually reduce performance because it turns what would otherwise be a local stack variable (for which "allocation" is completely free as long as they don't all take up too much space) into a global variable and inserts special logic into the function to make it initialize that variable the first time the function is called.
Last edited by Quietust on Mon Dec 07, 2020 2:57 pm, edited 1 time in total.
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.

User avatar
Haruki_nyan
Posts: 4
Joined: Fri Aug 02, 2019 11:23 am
Location: United States
Contact:

Re: PPU memory read buffer is not the open bus?

Post by Haruki_nyan » Mon Dec 07, 2020 2:57 pm

Quietust wrote:
Mon Dec 07, 2020 2:46 pm
Then that's your problem - when you read from $2007 (or from $2002 or $2004), you must also set "openBus = data;"
That makes sense, and it works; thank you. (*yay*)
Also, the problem of data being declared static has been noted and changed.
lidnariq wrote:
Mon Dec 07, 2020 2:44 pm
Is that "openBus" the system-wide one or the PPU's own internal one?
It's just the PPU's internal one. It's actually PPU::openBus, meaning it's a member of the PPU class.

Post Reply