nesdev.com
http://forums.nesdev.com/

Confused about VRAM Address Increment
http://forums.nesdev.com/viewtopic.php?f=3&t=18656
Page 1 of 1

Author:  urbanspr1nter [ Sun Mar 31, 2019 9:01 am ]
Post subject:  Confused about VRAM Address Increment

Hi, I am reading about how the PPU registers work from the Wiki. Going through the pages, I am having a bit of confusion with understanding the access to the $2007 PPUDATA register.

It says here:

Quote:
VRAM read/write data register. After access, the video memory address will increment by an amount determined by bit 2 of $2000.


IIRC, the VRAM address is obtained through $2006, where it has been written twice to memory by the CPU. Is my understanding on how the full 16-bit VRAM address is obtained, correct?

1. CPU writes to $2006.
-> The address latch gets filled with this byte.
2. CPU writes to $2006
-> The VRAM is constructed from (pseudo-code)
Code:
vramAddr = (latch.value << 8) | mem.get(0x2006)

3. CPU writes to $2007
4. PPU uses the
Code:
vramAddr
and writes to PPU memory with data found in $2007.

Now, here is where I am stuck... Is the increment happening on vramAddr? Or, is the increment happening in $2006. I am confused about this because These values are only 8-bits and addresses are be 16-bits, right? Where do I write the low/high bytes in this case?

Author:  tokumaru [ Sun Mar 31, 2019 9:41 am ]
Post subject:  Re: Confused about VRAM Address Increment

$2006 is just a port for communicating with the CPU, it doesn't actually hold any values. Internally, the PPU has 2 address registers: a temporary one, and an effective one. Writing to $2006 changes the value of the temporary register, first the high byte, then the low byte. After the low byte is written, the value of the temporary register is automatically copied to the effective register, which is the one the PPU for actual VRAM access.

The auto-increment is just something the PPU does to speed up access to VRAM. After each access to VRAM (writing or reading), the PPU automatically increments the value of the effective address register, by either 1 or 32. Incrementing the address by 32 is useful for accessing columns of tiles in the name tables. Since name tables are 32 tiles wide, adding 32 to the address causes it to point to the tile immediately below.

Author:  urbanspr1nter [ Sun Mar 31, 2019 9:54 am ]
Post subject:  Re: Confused about VRAM Address Increment

I see, so if my understanding is correct, then my PPU would have something like this for variables:

Code:
// initially:
vramAddressToggle = false
loVramAddressByte: Byte = 0
hiVramAddressByte: Byte
effectiveVramAddress: DoubleByte = 0

// then:
if(!vramAddressToggle) {
     loVramAddressByte = mem.get($2006);
     vramAddressToggle = true;
}
else {
     hiVramAddressByte = mem.get($2006);
     vramAddressToggle = false;

     effectiveVramAddress = merge(loVramAddressByte, hiVramAddressByte);
}


// Then on accesses:
ppuMem.get(effectiveVramAddress)

// or..
ppuMem.set(effectiveVramAddress, mem.get($2007));
incrementAddress(effectiveVramAddress); // either 8, or 32?


Also, if the above is true, does that mean we don't reset the vramAddressToggle until we read PPUSTATUS? When does that get reset?

Author:  lidnariq [ Sun Mar 31, 2019 10:03 am ]
Post subject:  Re: Confused about VRAM Address Increment

vramAddressToggle toggles after every write to $2005 or $2006.

Author:  tokumaru [ Sun Mar 31, 2019 10:04 am ]
Post subject:  Re: Confused about VRAM Address Increment

Let me try to answer your questions more directly:

urbanspr1nter wrote:
IIRC, the VRAM address is obtained through $2006, where it has been written twice to memory by the CPU. Is my understanding on how the full 16-bit VRAM address is obtained, correct?

Yes

Quote:
1. CPU writes to $2006.
-> The address latch gets filled with this byte.
2. CPU writes to $2006
-> The VRAM is constructed from (pseudo-code)
Code:
vramAddr = (latch.value << 8) | mem.get(0x2006)

Not really. What you call a "latch" is actually the temporary VRAM address register, commonly referred simply as "t" around these parts. It's a 16-bit register (actually 15), and it's value comes from the values written to PPU port $2006. $2006 really is just a port: the PPU "listens" to the address and data buses waiting for this address (and it's mirrors) to be written to, and when that happens, it takes the value in the data bus and updates t accordingly.

Which bits of t are updated depends on the state of the toggle that selects between the first and second writes to $2006. The first write updates the high byte of t (except for the top 2 bits, which get cleared!), and the second write updates the low byte, and copies the full value to the effective VRAM address register, commonly called "v".

The toggle that selects between the first and second writes is shared between ports $2006 and $2005, where the PPU used it to tell whether to update the X scroll or the Y scroll.

Quote:
3. CPU writes to $2007
4. PPU uses the
Code:
vramAddr
and writes to PPU memory with data found in $2007.

Yes, but again, there's no "data found in $2007", $2007 is just a port, when the PPU detects a write to this address, it immediately intercepts the write and captures the value being written.

Quote:
Now, here is where I am stuck... Is the increment happening on vramAddr?

Yeah.

Quote:
Or, is the increment happening in $2006.

Assuming you mean the temporary VRAM address register (t), then no, t remains unchanged until $2005, $2006 or $2000 are written to. These 3 ports affect t in some way.

Quote:
I am confused about this because These values are only 8-bits and addresses are be 16-bits, right?

This is why the PPU needs a toggle to select between first and second writes, so it can get 16 bits of data through a single 8-bit port.

Author:  tokumaru [ Sun Mar 31, 2019 10:19 am ]
Post subject:  Re: Confused about VRAM Address Increment

urbanspr1nter wrote:
Code:
// initially:
vramAddressHiToggle = false
loVramAddressByte: Byte = 0
hiVramAddressByte: Byte
effectiveVramAddress: DoubleByte = 0

That's one way to do it.

Quote:
Code:
if(!vramAddressToggle) {
     loVramAddressByte = mem.get($2006);
     vramAddressToggle = true;
}
else {
     hiVramAddressByte = mem.get($2006);
     vramAddressAccessCount++;

     effectiveVramAddress = merge(loVramAddressByte, hiVramAddressByte);
}

Like I said before, $2006 isn't memory, so you probably shouldn't be using mem.get($2006) to get the value. The correct way to do it would be to intercept the write operation at the exact moment it happens and copy the value then.

Other than that, I don't know what you're using vramAddressAccessCount for, and you forgot to set the toggle back to false on the second write. Oh, and the high/low order is inverted here... The high byte is updated on the first write, the low byte on the second. And don't forget to clear the top 2 bits when updating the high byte.

Quote:
Code:
/ Then on accesses:
ppuMem.get(effectiveVramAddress)

You need to do the auto-increment after reads too. And don't forget that VRAM reads are buffered: on each read, the value in the buffer is returned to the CPU, and the value from VRAM is copied to the buffer. Except when reading the palette, palette data is returned immediately.

Quote:
Code:
ppuMem.set(effectiveVramAddress, mem.get($2007));
incrementAddress(effectiveVramAddress); // either 8, or 32?

Again, $2007 is a port, not memory. While treating it as memory might seem to work at first, I'm sure something will go wrong really soon.

Quote:
Also, if the above is true, does that mean we don't reset the vramAddressToggle until we read PPUSTATUS? When does that get reset?

It toggles on every write to $2006 or $2005, and resets when PPUSTATUS is read.

Author:  urbanspr1nter [ Sun Mar 31, 2019 10:24 am ]
Post subject:  Re: Confused about VRAM Address Increment

Quote:
Yes, but again, there's no "data found in $2007", $2007 is just a port, when the PPU detects a write to this address, it immediately intercepts the write and captures the value being written.


Oh, then in my implementation, it is fundamentally wrong. I would then need to call a handler for the PPU when my CPU accesses these addresses. Thanks for the clarification on that piece.

Quote:
Like I said before, $2006 isn't memory, so you probably shouldn't be using mem.get($2006) to get the value. The correct way to do it would be to intercept the write operation at the exact moment it happens and copy the value then.

Other than that, I don't know what you're using vramAddressAccessCount for, and you forgot to set the toggle back to false on the second write. Oh, and the high/low order is inverted here... The high byte is updated on the first write, the low byte on the second. And don't forget to clear the top 2 bits when updating the high byte.


Oops, that was a typo. But yes definitely understand about the $2006 access now.

Quote:
Assuming you mean the temporary VRAM address register (t), then no, t remains unchanged until $2005, $2006 or $2000 are written to. These 3 ports affect t in some way.


In what way of $2000? The only thing I came across is $2002 in the wiki :-\

Author:  lidnariq [ Sun Mar 31, 2019 10:25 am ]
Post subject:  Re: Confused about VRAM Address Increment

Obvious wikipedia article: https://en.wikipedia.org/wiki/Memory-mapped_I/O

Author:  tokumaru [ Sun Mar 31, 2019 10:31 am ]
Post subject:  Re: Confused about VRAM Address Increment

$2000 updates the name table bits in the VRAM address. When accessing name tables, the address register has the following format: 0010NNYY YYYXXXXX

$2006 writes affect bits 0 through 13 (and clear bits 14 and 15), $2005 writes change the X and Y bits, and $2000 writes change the NN bits.

One weird aspect of the PPU is that it uses its addresses registers when rendering the image, which is why the scroll settings (set via $2000 and $2005) affect the address registers. Fir rendering purposes, the PPU address registers (t and v have the following format:

0yyyNNYY YYYXXXXX

XXXXX: coarse X scroll;
YYYYY: coarse Y scroll;
NN: name table;
yyy: fine Y scroll;
(the fine X scroll is kept elsewhere)

When the frame starts, the PPU will automatically copy the value from t to v and use that to determine what part of the background to render, as specified by the bits above.

Page 1 of 1 All times are UTC - 7 hours
Powered by phpBB® Forum Software © phpBB Group
http://www.phpbb.com/