CPU writes to PPU Memory.

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

Post Reply
urbanspr1nter
Posts: 39
Joined: Thu Aug 16, 2012 7:55 pm

CPU writes to PPU Memory.

Post by urbanspr1nter »

I am aware that the PPU exposes various registers to the CPU (PPUCTRL/2000, PPUSTATUS/2002, PPUADDR/2006, PPUDATA/2007, etc), from a CPU perspective, is this a unidirectional way of communication to the PPU? For example something like this:

Code: Select all

       PPU Request
[CPU] -------------> [PPU]
In emulator development, if a CPU makes a memory-mapped read/write on a specific PPU register, does that make direct access to the PPU RAM right away? Or is it held in the PPU registers and then written to PPU Memory by the PPU. Stupid question, I know... but getting a bit confused when reading the wiki docs. :oops:
User avatar
Memblers
Site Admin
Posts: 4044
Joined: Mon Sep 20, 2004 6:04 am
Location: Indianapolis
Contact:

Re: CPU writes to PPU Memory.

Post by Memblers »

Conceptually, I think of it as being immediate. But I would assume it's in fact quantized to the next PPU cycle (though I don't really know exactly). Since the PPU/CPU are running at different rates and not in sync. Someone else would know or explain better than me. But it might help if you can think of the particular situation you have in mind.

The $2007 register can be read by the CPU, but it's actually buffered. So you read $2007 once it fills the buffer, then the next $2007 read actually returns the buffer (and fills the buffer with the next byte etc.). However, this doesn't happen when you read the palette memory (which is RAM internal to the PPU). Maybe This kind of implies that palette memory access is immediate? But the rest would go through the PPU's normal process for accessing external memory.

I'm not an emu dev, so hopefully this doesn't hurt more than it helps, haha.
urbanspr1nter
Posts: 39
Joined: Thu Aug 16, 2012 7:55 pm

Re: CPU writes to PPU Memory.

Post by urbanspr1nter »

Hmm yeah the Wiki page here (http://wiki.nesdev.com/w/index.php/PPU_ ... rs#PPUADDR) says:
Because the CPU and the PPU are on separate buses, neither has direct access to the other's memory. The CPU writes to VRAM through a pair of registers on the PPU. First it loads an address into PPUADDR, and then it writes repeatedly to PPUDATA to fill VRAM.
So, I am wondering if when we run our PPU cycles after running the current CPU instructions in our execution loop, do we read the registers and perform the memory operations?

Code: Select all

executionLoop() {
      executeCpuInstruction(); // potentially a PPU register access here.
      executePpuCycles(); // do we handle the writes to VRAM here based off the registers?
}
OR does the documentation mean that these "registers" can be seen as directly mapped to PPU RAM as the only "path" to the PPU memory since both CPU and PPU buses are separate (which I assume it means they don't share memory, which I know.)
Hangin10
Posts: 37
Joined: Thu Jun 04, 2009 9:07 am

Re: CPU writes to PPU Memory.

Post by Hangin10 »

That would not work for some instructions as it doesn't represent the two happening at the same time. For example, read-modify-write instructions (from nesdev.com/6502_cpu.txt):

Read-Modify-Write instructions (ASL, LSR, ROL, ROR, INC, DEC,
SLO, SRE, RLA, RRA, ISB, DCP)

# address R/W description
--- ------- --- ------------------------------------------
1 PC R fetch opcode, increment PC
2 PC R fetch address, increment PC
3 address R read from effective address
4 address W write the value back to effective address,
and do the operation on it
5 address W write the new value to effective address
Between CPU cycles 3 and 4, atleast 3 PPU cycles (3.2 on PAL, I think) have happened, so even if reads/writes to $2007 (for example) only happened on PPU clocks, it wouldn't matter to the cpu at all. What will matter is executing 3 PPU cycles for each of those cpu cycles, as the $2007 pointer (loopy_v? is there a better name?) would get incremented 3 times as the instruction does 3 accesses to the address.
urbanspr1nter
Posts: 39
Joined: Thu Aug 16, 2012 7:55 pm

Re: CPU writes to PPU Memory.

Post by urbanspr1nter »

If then for this case -- for emulator development purposes, would it then make sense to create some sort of buffer for these specific registers? Something like a queue that holds a struct of memory reads and writes to the PPU? Then we can read the queue and process these memory operations to "catch up" to the CPU?
Hangin10
Posts: 37
Joined: Thu Jun 04, 2009 9:07 am

Re: CPU writes to PPU Memory.

Post by Hangin10 »

That's one option. It takes being careful about when various things trigger catch up, like sprite-0-hit. In my case, I just chose to write my cpu as NES-specific as it has calls to ppu_cycles(3) throughout instructions after emulating each cycle, although sometimes that's not necessary as not every cycle of every instruction has a memory access that's important to the ppu like reading the absolute address operand (eg. could jump to PC=$2000, but the chances of there being useful code from that (bus conflict?) are slim to none) or none at all (eg. nop, branches, etc) just end that switch-case with ppu_cycles(3*whatever cycles). (my emu was NTSC only too :beer:
urbanspr1nter
Posts: 39
Joined: Thu Aug 16, 2012 7:55 pm

Re: CPU writes to PPU Memory.

Post by urbanspr1nter »

Haha, ok.. Let me try that. I will plan for some of the cases you had mentioned.... Right now my goal is to just get the Donkey Kong title screen to appear. >.<

I think my plan is that since I have counted all CPU for each instruction, after the execution for the CPU instruction, I will take the CPU cycles * 3 to get the PPU cycles to be executed. The plan is to use that to know how many cycles the PPU should executed. When I run my PPU , I will decrement that value for the specific operations involved such as memory access (2 cycles?) until it is <= 0.

I have been looking at this document to get an idea of how I should execute my PPU cycles: https://wiki.nesdev.com/w/index.php/PPU_rendering.

I would assume that when my PPU state is at scanline 241, I would then just trigger the NMI and Vblank and after the PPU has finished execution, my loop will just check for that NMI flag raised and execute the NMI routine from there...

Also, I just had a realization on how should a CPU read from PPU Memory work? Say an LDA from $2007... That would be buffered into the queue too, right?

Not looking for exact accuracy, but I am not sure of any considerations I should take with this approach... What are your thoughts?
Post Reply