Page 1 of 1

Graphics routine

Posted: Fri Mar 12, 2010 11:37 am
by ehguacho
so here i'm again reading all the documents i have about PPU,VBlank, NMI, IRQ, etc... and still doesn't understand how the NES shows graphics, so i think i'm get drunk and then hang my neck on a rope... ok i'm joking, not gonna suicide... but there's too many things and speaking english can be so complicated sometimes (remember i'm from Argentina... yeah yeah, Maradona's place XD) :(
i'm loosing all my faith on this thing about the PPU, so please Tepples, Disch, MottZilla and all the NESDEV crew have mercy with me and help me :(

Posted: Fri Mar 12, 2010 12:59 pm
by ehguacho
Just to be clear, I'm not asking for code or anything like that. All I'm asking for is the algorithm which NES uses to show its graphics.


[Spelling Fairy was here]

Posted: Fri Mar 12, 2010 1:00 pm
by MottZilla
Well there is no one way to emulate the graphics for NES. You can achieve graphics emulation on a basic level pretty easily. The very first thing you need to do is implement the PPU's registers ($2000 - $2007). You will need to keep track of the CPU and PPU to figure out when certain important events happen which early on the main one is $2002 will need to know when VBlank occurs. This will also be in relation to when to trigger NMI interrupts. Based on register $2000 the game can choose if NMI interrupts occur on VBLANK, which happens after the last scanline is rendered.

You'll probably write and rewrite your graphics emulation code in the future so for now if you just want quick results here is a basic idea. Have a counter that counts the amount of CPU cycles executed. Each time a CPU opcode is executed you add the cycle time to this counter. When the counter exceeds 27280 (thats 113.6 the amount of cpu cycles per scanline for NTSC) you've had 240 scanlines drawn. You should mark a flag for this loop related to a check that checks after each opcode if 240 lines have passed by. If the flag isn't set, then you set it and set the VBLANK flag in $2002 as well as check $2000 if you need to trigger a NMI interrupt. Then when 262 lines have passed by, you reset that flag, and subtract 29780 from the counter which is approx 262 scanlines. This will have you setup on a loop to trigger VBLANK flags, NMIs, and games will execute more of their program code than before.

Then you could add some video emulation just by drawing the NameTable to the screen at the same time you are setting the VBL flag and checking to see if you need to trigger a NMI. After drawing the NameTable to the screen you could add drawing the sprites, add drawing the nametables with scrolling in effect.

Now if you have a specific sticking point on what you don't understand I'm sure you can get an answer.

Posted: Fri Mar 12, 2010 1:09 pm
by MottZilla
ehguacho wrote:just to be clear, all i'm asking for is the algorithm wich NES uses to shows their graphics. i'm [not] asking for code!
There is no algorithm or magic piece of code that does it. I can explain how graphics rendering works on NES but there are enough documents on it already. Again, if you have a specific question, we can help. But it's not possible to just post graphics code you can snap into your emulator. If you want to see complete NES graphics emulation code you could download the source to many open source NES emulators. But I don't think that will help you much. It's better to understand it and write your own code than to try to plug in someone else's code.

Posted: Fri Mar 12, 2010 1:10 pm
by tepples
To draw each scanline of a background, do this 33 times:
  1. Fetch a nametable entry.
  2. Fetch the corresponding attribute table entry.
  3. Fetch the low-order byte of the pattern table sliver.
  4. Fetch the high-order byte of the pattern table sliver.
  5. Turn the attribute data and the pattern table data into palette indices, and store them in an array for combination with sprite data.
Which one of the five steps do you want explained in detail first?

Posted: Fri Mar 12, 2010 1:58 pm
by Jeroen
Tepples: I have to say...that is a probably the clearest I've ever een anyone say it. (not that i've really been researching it though)

Posted: Fri Mar 12, 2010 6:36 pm
by ehguacho
tepples wrote:To draw each scanline of a background, do this 33 times:
  1. Fetch a nametable entry.
  2. Fetch the corresponding attribute table entry.
  3. Fetch the low-order byte of the pattern table sliver.
  4. Fetch the high-order byte of the pattern table sliver.
  5. Turn the attribute data and the pattern table data into palette indices, and store them in an array for combination with sprite data.
Which one of the five steps do you want explained in detail first?
ok, i think this algorithm you wrote may be helpful.

let's start with step 1: fetch a name table entry.

first of all, i have to read wich is written in $2000 to fetch the information about the address of the name table wich is gonna be used (bits 0 and 1) and the sprite pattern table and background pattern table wich are gonna be used (bits 3 and 4, respectively). that's easy. but what do you mean with "name table entry"? is it a write to $2004 or $2006 or any write to an address between the name table space?
also i have another question about this. as i read, $2003 is used to write the 8-bit address in SPR-RAM to access via $2004. i.e.:

(extracted from GbaGuy demo)

lda #$00 ; these lines tell $2003
sta $2003 ; to tell
lda #$00 ; $2004 to start
sta $2003 ; at $0000.

when i'm handling a routine like that, do i have to store the first write to $2003 (the first "sta $2003") in some variable in order to later assemble it with the value written in the second write? because if i don't, at the second write i'll miss the value written in first time. in that case, why its says it's a 8-bit address if it's a 16-bits address (2 bytes written to $2003 = 16-bits address)?
does all the addresses in a name table work like this? (i.e.: $2014 -> the address register, $2015 -> I/O register)

Posted: Sat Mar 13, 2010 5:46 am
by tepples
ehguacho wrote:let's start with step 1: fetch a name table entry.

first of all, i have to read wich is written in $2000 to fetch the information about the address of the name table wich is gonna be used (bits 0 and 1)
At this point, please read Loopy's document "The skinny on NES scrolling" available from the main page.
but what do you mean with "name table entry"?
A byte in PPU $2000-$23BF, $2400-$27BF, $2800-$2BBF, or $2C00-$2FBF.
also i have another question about this. as i read, $2003 is used to write the 8-bit address in SPR-RAM to access via $2004. i.e.:

(extracted from GbaGuy demo)
GbaGuy's demos have severe defects. Don't use them when developing your emulator.
lda #$00 ; these lines tell $2003
sta $2003 ; to tell
lda #$00 ; $2004 to start
sta $2003 ; at $0000.
The first write to $2003 is IGNORED because it's only an 8-bit register. It DOES NOT use the latch that $2005 and $2006 use.

Posted: Sat Mar 13, 2010 4:24 pm
by ehguacho
ok thanks again tepples. i'll keep on trying.
this is an important project to me, also i'm thinking about to use it as the final project for third year in college. classics never dies man... :wink:

Posted: Fri May 21, 2010 11:11 am
by ehguacho
tepples wrote:To draw each scanline of a background, do this 33 times:
  1. Fetch a nametable entry.
  2. Fetch the corresponding attribute table entry.
  3. Fetch the low-order byte of the pattern table sliver.
  4. Fetch the high-order byte of the pattern table sliver.
  5. Turn the attribute data and the pattern table data into palette indices, and store them in an array for combination with sprite data.
Which one of the five steps do you want explained in detail first?
look at this simply code:

Code: Select all

static inline void RefreshScreen(void)
{
    static unsigned char NameTableEntry,AttributeTableEntry;

    NameTableEntry = VRAM[Reg2000_NameTable];
    AttributeTableEntry = VRAM[Reg2000_NameTable + 0x3c0];

    return;
}
now the question is: how do i fetch the corresponding pattern table entry?

Posted: Fri May 21, 2010 1:19 pm
by tepples
PatternBits0 = VRAM[Reg2000_PatternTableBase * 4096 + NameTableEntry * 16 + (Y % 8)];
PatternBits1 = VRAM[Reg2000_PatternTableBase * 4096 + NameTableEntry * 16 + (Y % 8) + 8];

Posted: Fri May 21, 2010 4:27 pm
by ehguacho
tepples wrote:PatternBits0 = VRAM[Reg2000_PatternTableBase * 4096 + NameTableEntry * 16 + (Y % 8)];
PatternBits1 = VRAM[Reg2000_PatternTableBase * 4096 + NameTableEntry * 16 + (Y % 8) + 8];
thanks for your answer tepples, but what that "Y" stands for?
i'm assuming that "Reg2000_PatternTableBase" holds the value of the bit of Register 2000 wich indicates the pattern table address (sprite or background). is that right?

Posted: Fri May 21, 2010 8:09 pm
by tepples
ehguacho wrote:what that "Y" stands for?
Every tile has eight lines in it. For background tiles, the line number is bits 14 through 12 of the VRAM address.
i'm assuming that "Reg2000_PatternTableBase" holds the value of the bit of Register 2000 wich indicates the pattern table address (sprite or background). is that right?
Yes.

Posted: Fri May 21, 2010 9:49 pm
by ehguacho
thank you very much tepples!