Breaking NES apart (WARNING: traffic)
Moderator: Moderators
Breaking NES apart (WARNING: traffic)
Hi.
I'm going to reverse engineer NES chips, including 2A03 (CPU) and 2C02 (PPU), both revision G and write its truly cycle-accurate emulator.
Die photos are available around (visual6502, quietust site). I do chip tracing by my own.
You can find my 6502-related topic on 6502.org board: http://forum.6502.org/viewtopic.php?f=8&t=2208
My current progress on APU:
PPU:
I'm going to reverse engineer NES chips, including 2A03 (CPU) and 2C02 (PPU), both revision G and write its truly cycle-accurate emulator.
Die photos are available around (visual6502, quietust site). I do chip tracing by my own.
You can find my 6502-related topic on 6502.org board: http://forum.6502.org/viewtopic.php?f=8&t=2208
My current progress on APU:
PPU:
- cpow
- NESICIDE developer
- Posts: 1097
- Joined: Mon Oct 13, 2008 7:55 pm
- Location: Minneapolis, MN
- Contact:
Re: Breaking NES apart (WARNING: traffic)
Yay. There'll finally be an emulator with worse performance than mine!
Re: Breaking NES apart (WARNING: traffic)
Todays speccy: APU address pin
Address pin use tri-state logic to disconnect address bus, while NC/A line is high.
Note: I will use 6502-like bus and control lines notation. For example, "1/A0" mean "put 1 on A0 line". And A0 mean simply "bit 0 of address bus".
cpow, performance doesn't matter on modern PC's
Address pin use tri-state logic to disconnect address bus, while NC/A line is high.
Note: I will use 6502-like bus and control lines notation. For example, "1/A0" mean "put 1 on A0 line". And A0 mean simply "bit 0 of address bus".
cpow, performance doesn't matter on modern PC's
Re: Breaking NES apart (WARNING: traffic)
You might consider trying to get a hold of Quietust, who's done a lot of inspecting the CPU's die before, and took the visual6502 code to make visual2a03. He's apparently also been responsible for some back-end changes in Visual6502 to make setting up simulations of non-cpus like the 2c02 easier and more sensical.
- rainwarrior
- Posts: 8734
- Joined: Sun Jan 22, 2012 12:03 pm
- Location: Canada
- Contact:
Re: Breaking NES apart (WARNING: traffic)
Also one page up, he has a nice collection of layer and label images: http://www.qmtpro.com/~nes/chipimages/
Re: Breaking NES apart (WARNING: traffic)
I seriously need to work on my skills... How do you determine anything from the plain layered die photo? Is image processing being employed? (Or are you actually compositing the layers?)
Re: Breaking NES apart (WARNING: traffic)
rainwarrior, I've seen those layers and images but when I tried to compose them in photoshop, it doesn't look suitable. So I do by my own.
kyuusaku, search on youtube "Tracing 6502". I captured real-time video, while tracing 6502. This is quite simple
kyuusaku, search on youtube "Tracing 6502". I captured real-time video, while tracing 6502. This is quite simple
Re: Breaking NES apart (WARNING: traffic)
Ahh, I thought you were working only from the die photo... Still impressive!
I took the easy way out and extracted the node and transistor list.
I took the easy way out and extracted the node and transistor list.
Re: Breaking NES apart (WARNING: traffic)
Todays speccy: PPU OAM row decoder.
Input: row group number. Output selects one of 9 OAM row groups.
OAM layout (see attached pic):
Top part: 34 rows by 32 cells
Bottom part: 32 rows by 32 cells
Rows are aranged in groups, 9 groups total.
Number of rows in each group : 8, 8, 5, 8, 8, 8, 5, 8, 8
This gives 1792 + 320 = 2112 bits (264 bytes)
Code: Select all
1 2 3 4 abcdefghi
--------------------
0 0 0 0 | 000000010
0 0 0 1 | 000000100
0 0 1 0 | 000001000
0 0 1 1 | 000010000
0 1 0 0 | 000100000
0 1 0 1 | 001000000
0 1 1 0 | 010000000
0 1 1 1 | 100000000
1 x x x | 000000001
OAM layout (see attached pic):
Top part: 34 rows by 32 cells
Bottom part: 32 rows by 32 cells
Rows are aranged in groups, 9 groups total.
Number of rows in each group : 8, 8, 5, 8, 8, 8, 5, 8, 8
This gives 1792 + 320 = 2112 bits (264 bytes)
Re: Breaking NES apart (WARNING: traffic)
Todays speccy: PPU data bus (D0...D7)
This is CPU-side data pin.
Each pin has 3 connects: RD, WR and DBn.
RD and WR controls read and write tri-state logic.
When RD is high write logic goes to "not connected" state and otherwise.
RD and WR cannot be in combinations 0/0. Both RD and WR are high, when /CE = 1 ("chip not enabled")
DBn wire transfers actual data in both directions.
This is CPU-side data pin.
Each pin has 3 connects: RD, WR and DBn.
RD and WR controls read and write tri-state logic.
When RD is high write logic goes to "not connected" state and otherwise.
RD and WR cannot be in combinations 0/0. Both RD and WR are high, when /CE = 1 ("chip not enabled")
DBn wire transfers actual data in both directions.
Re: Breaking NES apart (WARNING: traffic)
Bonus: PPU R/W decode logic
Output RD and WR, based on R/W and /CE lines:
RD = ~R/W
WR = R/W
RD = WR = 1, when /CE = 1.
From Brad Taylor's "NTSC 2C02 technical reference":
Output RD and WR, based on R/W and /CE lines:
RD = ~R/W
WR = R/W
RD = WR = 1, when /CE = 1.
From Brad Taylor's "NTSC 2C02 technical reference":
/CS is mentioned as /DBE (NOT data bus enabled) in PPU US patent 4824106.R/W, D0-D7, A2-A0, /CS: these are the PPU's control bus signals responsible
for programming the 2C02's internal registers. R/W controls data direction
(write data into PPU reg on zero), A0-A2 selects the internal PPU register
to read/write, and while /CS is set to zero, D0-D7 is used to transfer the
data bits to/from the selected register (if /CS=1, D0-D7 float).
Last edited by org on Fri Aug 10, 2012 6:42 am, edited 2 times in total.
Re: Breaking NES apart (WARNING: traffic)
APU reset pin
Simply inverted /RST input, no any latches or something.
RST is connected to NC/A address bus input. This mean address bus is float during reset.
Simply inverted /RST input, no any latches or something.
RST is connected to NC/A address bus input. This mean address bus is float during reset.
Re: Breaking NES apart (WARNING: traffic)
Todays speccy: PPU register select PLA.
I partially decoded obscured area of register select PLA :
As you can see diffusion layer is totally hidden under metall crosspassings.
I took original picture, maxed contrast and removed saturation:
Noticed the difference between red and green circles? Humain brain is a miracle
Although I didn't managed to recover top 3 lines of PLA, I still happy Now I can find PPU registers.
PLA feeds A0-A3 and R/W lines and connect specific register with data bus (D0...D7), when condition met (actually it output "disconnect" drive signals, leaving only single register to be connected)
If condition is not met, then neither register is not connected to the data bus.
A0-A2 lines are mentioned as RS0-2 (register select) in PPU US patent 4824106.
Enjoy)
I partially decoded obscured area of register select PLA :
As you can see diffusion layer is totally hidden under metall crosspassings.
I took original picture, maxed contrast and removed saturation:
Noticed the difference between red and green circles? Humain brain is a miracle
Although I didn't managed to recover top 3 lines of PLA, I still happy Now I can find PPU registers.
PLA feeds A0-A3 and R/W lines and connect specific register with data bus (D0...D7), when condition met (actually it output "disconnect" drive signals, leaving only single register to be connected)
If condition is not met, then neither register is not connected to the data bus.
A0-A2 lines are mentioned as RS0-2 (register select) in PPU US patent 4824106.
Enjoy)
Re: Breaking NES apart (WARNING: traffic)
Todays speccy: APU register decode
First take a look on oveview:
Register address decode is done in 3 steps:
- predecode: determine whenever address is belongs to register memory area (4000 ... 401F)
- R/W decode: determine what we gonna do - read or write
- PLA: connects appropriate register with data bus
Predecode:
It simply grounds output, if address is not "0x00 0000 000x xxxx" (where "x" mean any bit)
Two PDSEL outpus are identical to each other.
Ricoh missed two pullups here, so PDSEL is just "not connected", instead of high level, if address is match.
R/W decode:
Feeds R/W line from CPU and PDSEL (which is grounded or just not-connected)
2 outputs goes to PLA.
Register write is performed when R/W is low and PDSEL is not connected.
Register read is performed when R/W is high and PDSEL is not connected.
PLA:
Most interesting part.
This scheme has a lot of output control wires, which connect different registers with data bus.
Write PLA outputs are grounded during 6502 PHI1 phase.
I found undocumented registers : 4018, 4019 (both read only) and 401A (read/write).
Access to these registers is controlled by yet unkown (UNK) control line.
EDIT: UNK line is actually DEBUG input (pin 30). And 4018-401A are debug readback registers.
Some links from Quietust:
http://wiki.nesdev.com/w/index.php/Talk ... escription
http://wiki.nesdev.com/w/index.php/File:Apu_address.jpg
First take a look on oveview:
Register address decode is done in 3 steps:
- predecode: determine whenever address is belongs to register memory area (4000 ... 401F)
- R/W decode: determine what we gonna do - read or write
- PLA: connects appropriate register with data bus
Predecode:
It simply grounds output, if address is not "0x00 0000 000x xxxx" (where "x" mean any bit)
Two PDSEL outpus are identical to each other.
Ricoh missed two pullups here, so PDSEL is just "not connected", instead of high level, if address is match.
R/W decode:
Feeds R/W line from CPU and PDSEL (which is grounded or just not-connected)
2 outputs goes to PLA.
Register write is performed when R/W is low and PDSEL is not connected.
Register read is performed when R/W is high and PDSEL is not connected.
PLA:
Most interesting part.
This scheme has a lot of output control wires, which connect different registers with data bus.
Write PLA outputs are grounded during 6502 PHI1 phase.
I found undocumented registers : 4018, 4019 (both read only) and 401A (read/write).
Access to these registers is controlled by yet unkown (UNK) control line.
EDIT: UNK line is actually DEBUG input (pin 30). And 4018-401A are debug readback registers.
Some links from Quietust:
http://wiki.nesdev.com/w/index.php/Talk ... escription
http://wiki.nesdev.com/w/index.php/File:Apu_address.jpg
Last edited by org on Mon Aug 13, 2012 7:09 am, edited 1 time in total.
Re: Breaking NES apart (WARNING: traffic)
Todays LSD trip: Master clock divider.
CLK input pin:
Not sure if output is inverted or not I do not understand how does it work, when diffusion cross poly
So I assume its not inverted.
6-stage Johnson counter:
I placed two images: one for color and another is just trans-level.
You can find some artifacts on color schematics, I believe this is parts of PAL-version of 2A03.
Input CLK is divided by 12.
Additional duty compensation is need to make high level of PHI2 more wider. Without such compensation output PHI2 would be only 50% duty.
Also I wrote small program on C, to test Johnson counter:
[spoiler]
[/spoiler]
Admins: please allow to attach txt/c files, or add spoiler bb-code.
EDIT: found some errors, now fixed )
CLK input pin:
Not sure if output is inverted or not I do not understand how does it work, when diffusion cross poly
So I assume its not inverted.
6-stage Johnson counter:
I placed two images: one for color and another is just trans-level.
You can find some artifacts on color schematics, I believe this is parts of PAL-version of 2A03.
Input CLK is divided by 12.
Additional duty compensation is need to make high level of PHI2 more wider. Without such compensation output PHI2 would be only 50% duty.
Also I wrote small program on C, to test Johnson counter:
[spoiler]
Code: Select all
// 2A03 master clock divider simulation.
#include <stdio.h>
int CounterOut;
int InLatch[6], OutLatch[6];
int DutyOut;
void step_count (int CLK)
{
int i;
CounterOut = OutLatch[5];
for (i=0; i<6; i++) {
if ((CLK & 1) == 0) InLatch[i] = CounterOut & 1;
if ((CLK & 1) == 1) OutLatch[i] = ~InLatch[i] & 1;
if ( i < 4) CounterOut = ~OutLatch[i] & ~(~OutLatch[5] & OutLatch[4]);
else CounterOut = ~OutLatch[i];
}
CounterOut &= 1;
DutyOut = ~InLatch[4] & 1;
// Dump counter
#if 0
printf ( "Counter (CLK: %i): ", CLK);
for (i=0; i<6; i++) printf ( " %i", ~OutLatch[i] & 1 );
printf ( "\n" );
#endif
}
main ()
{
int i, CLK;
// initial conditions
CounterOut = 0;
for (i=0; i<6; i++) InLatch[i] = OutLatch[i] = 0;
// Display master CLK steps
CLK = 0;
printf ( "CLK : " );
for (i=0; i<50; i++) {
printf ("%i", CLK );
CLK ^= 1;
}
printf ( "\n" );
// Display iterations of PHI2 output
CLK = 0;
printf ( "PHI2: " );
for (i=0; i<50; i++) {
step_count (CLK);
printf ("%i", ~(CounterOut & ~DutyOut) & 1 );
CLK ^= 1;
}
printf ( "\n" );
}
CLK : 01010101010101010101010101010101010101010101010101
PHI2: 00000000111111111111111000000000111111111111111000
Admins: please allow to attach txt/c files, or add spoiler bb-code.
EDIT: found some errors, now fixed )