Page 1 of 2

NES cartridge dumping

Posted: Sat Jan 06, 2007 11:08 pm
by timmeh87
Hello all

I am a university student in computer engineering, and i am making a circuit to dump NES cartridges. why? just for fun & learning. i came into this project with absolutely no knowledge of how the NES works, but through much time spent reading the wonderful articles on your sister site, i have *almost* successfully built this piece of hardware

but only almost.

before i get to my problem, a brief layout of what i put together and how it operates:

a PIC 16F877 hooked up to two 74HC595 (8 bit) shift registers in series. the shift registers are hooked up to address lines PRG a0 to PRG a14. the PRG data lines are hooked up directly to port C. PRG /CE and PRG R/W are both pulled low.

right now i am only concerned with dumping the PRG rom, 30 wires is already getting a little crazy.

the microcontroller shifts out a 15 bit address, and then reads the byte off of port C. it then sends the byte over a RS-232 serial connection to my PC.

now. it all *seemingly* works fine and dandy. i shift out addresses starting at 0x0000 (even though the ROM starts at 0x8000 in the CPU address space, it is my understanding that pulling PRG /CE low is equivalent to setting the MSB of the address to 1. so 0x0000 + CE = 0x8000 ?). My hyperterminal window fills with hex. the only problem is that the hex code is completely different than the SMB ROM that i downloaded off of the internet. _except for the first byte_

now. there are three possible scenarios here.

one, the ROM i am using for reference is not the same game that i am dumping. I am positive that the game i am using is the original SMB w/ duck hunt, so thats the ROM i got. i have compared 4 different versions of the ROM, the first kilobyte or more is the same across the board. but nowhere near what I'm reading off of this cartridge.

two, amongst the mess of wires flying around my breadboard, i got some address or data lines crossed. ive already checked once, but ill double check again later (im taking a bit of a break, ive been at this for hours...). either that, or i still do not understand exactly how to hook things up properly, and it just isn't working and never would. this still means i have to explain the first byte being the same as a "large coincidence"

three, my code is messed up. Ive already chased down a few bugs, but at this point no matter what i do i still get the same data out of it.


so basically, i was wondering if anyone had and suggestions or ideas. am i going about this right? I'm really hoping I'm just missing a wire or something simple like that. if you want i can provide my code here too, its written in C. i know that theres a mapper in this cart i should be worrying about, but i assumed that in its default state, id be reading from the beginning of the ROM. can anyone explain how the mapper works? i think i have to write a byte to some random address, but i cant find out exactly where or how.

finally, id like to end this post with the first 256 bytes that i am reading, in the hope that maybe someone can identify what i am seeing (assuming it isnt just garbage)

Code: Select all

78 69 42 23 07 cf a2 06 27 20 23 e6 4a 20 00 d0
c9 cf 07 01 03 09 33 0a 45 02 c7 f0 0a c9 8d 2e
69 07 0a 20 4a 00 18 85 85 09 31 c9 89 1c 38 00
c9 cf 58 20 29 03 ed a9 1b 6d 4a 02 0c 4b 18 14
a2 d9 25 c9 f0 49 00 16 c8 05 8b a4 4a 16 0e f4
bc 95 0e 02 0d 1e a9 18 16 1f 87 09 d8 e3 ee 84
8d 07 c7 04 13 08 ad 15 a9 a0 8d 0a 60 95 46 84
70 c0 42 b2 e8 90 b5 f9 06 cf b6 07 ff 03 d5 00
00 10 0e f0 8d 00 07 ad f7 60 0d 02 06 60 cf d0
07 6e 13 c8 93 69 29 cb 26 10 40 c1 b1 d0 a9 34
ad 04 0d aa 07 02 c9 aa f0 1e 00 09 a9 20 53 04
98 95 22 bd 99 ad 20 a9 30 2c e3 fc 0f c9 20 5c
20 07 e9 95 f0 b5 01 a2 23 7a b3 29 09 33 d0 12
85 8a 38 a9 07 85 33 d0 1e 99 06 18 c9 20 8d 04
bd cf 02 c6 aa 02 d0 60 86 bf 0f 4a bc 9d ee 00
24 d3 f8 dc 30 80 d0 a4 9d 3c 30 4a 10 6d a9 09

Posted: Sun Jan 07, 2007 12:45 am
by gannon
With the rom that you have, are you comparing the incoming data directly with the rom data, or are you starting at the prg data in the rom?
If you want to split the file, you can do it manually after reading the header, or use something like ucon64

Posted: Sun Jan 07, 2007 12:48 am
by timmeh87
im starting at 0x00...0010 in the reference ROM file i have, it is my understanding that that is where PRG data starts.

im just viewing it in a hex viewer.

Posted: Sun Jan 07, 2007 5:18 am
by blargg
Your data are good, it's just that the address lines are connected in reverse, and starting at bit 14. That is, bit 0 of your address is wired to PRG bit 14, bit 1 to PRG bit 13, etc. The data lines are connected correctly. For comparison with an iNES file, skip the first 16 bites in the file. For SMB, the PRG is 32Kbytes.

Posted: Sun Jan 07, 2007 11:39 am
by timmeh87
omg! you are totally right. they arent even hooked up 'backwards', because thats how i wanted to put them. my shift register subroutine just pushes them out backwards.

ill change it around and report back

Posted: Sun Jan 07, 2007 7:04 pm
by timmeh87
ok, so i really feel like an idiot now. thats what i get for reusing old code i guess. or something.


anyways, it comes out perfectly now. im getting about 1 kbytes / second, i dont know how that compares to a copynes. i would be curious if any could tell me.

the next phase is to get mappers sorted out... im going to do some research, but if anyone wanted to you know... explain it to me, well that would be great :D

Posted: Sun Jan 07, 2007 7:26 pm
by tepples
To speed up sequential reads, add a sequencer at the other end of the cable. Send the address, and have a 12-bit binary counter (like three 161s in succession) increment after each data read. This way you don't have to keep sending addresses; you can just set the counter to $8000, read 4096 bytes, set the counter to $9000, etc.

Posted: Sun Jan 07, 2007 7:59 pm
by timmeh87
indeed tepples, i had that idea. but i had these shift regs lying around and i cant afford to put in a digikey order right now. anyways. at less than a minute per 32k block, im not complaining.

Posted: Sun Jan 07, 2007 8:12 pm
by kyuusaku
I don't know about CopyNES (there must be a lot of overhead), but my dumper is entirely logic (latches for addressing)/parallel port controlled; I've read out 256K games in <5 seconds (using 4-bit i/o) which I think is only limited by the parallel port library I use. I would recommend using latches/flip flops with your design for random addressing (also allows for fast bankswitching)

Posted: Sun Jan 07, 2007 9:33 pm
by timmeh87
ok. so to bankswitch with this mapper, its my understanding that i have to write to an address between 0x8000 and 0xFFFF, with the last byte specifying the action.

so to get to PRG bank 0, i would write to 0x8000, and to get to PRG bank 1 i would write to 0x8010. is this correct?

in that case, how does one 'write' to the address. i have tried setting the address to 0x8010 and then bringing RW high for a time, but that doesn't seem to work, i just end up reading bank 0 twice...

any insight? theres a lot of half-information out there that doesn't really help.

edit: i had the polarity on the RW pin backwards, but it makes no difference. i also now think it depends on the value on the data bus, not the address, but that also doesnt seem to work

Posted: Sun Jan 07, 2007 11:20 pm
by tepples
First you have to find a byte somewhere in $8000-$FFFF whose low-order bits already contain the bank number you're looking for; then you have to write that same number back. It's not based on the address; it's based on the data.

Posted: Mon Jan 08, 2007 6:57 am
by blargg
It's not based on the address; it's based on the data.
Based on the data that the ROM chip puts on the data bus (yes, even during a supposed write cycle).

EDIT: removed suggestion to float data bus when writing.

Posted: Mon Jan 08, 2007 11:23 am
by tepples
If you put highZ on the data bus for all discrete mapper writes, you'll run into problems when you dump ANROM, one of the few discrete mappers that has the anti-bus-conflict circuit.

Posted: Mon Jan 08, 2007 8:38 pm
by timmeh87
well. i have been messing with it for some hours, and i finally got the bankswitching to work.

but i dont fully understand WHY it works. it actually didnt work at all when i had the CE line pulled down permanently. so i connected it to a pin on my uC instead, so i could mess with it to see what happens. it turns out that even if the first thing i do is pull CE down, i get the second bank out! but of course, i was unable to switch out of the second bank :roll:

so just on a hunch, i threw in a line of code to pulse the CE line high for one cycle every time i try to bankswitch - after i write the byte and before i bring RW high (read mode) again.

and of course, now it works great.

so whats up with that guys?

Posted: Mon Jan 08, 2007 8:56 pm
by Quietust
That's how the CPU bus works - in order to perform a memory access, you first set the address bits A0-A14, the state of R/W, and the data bits (if it's a write), then you pull M2 high (and /CE low if it's in $8000-$FFFF, since /CE is implemented as /[A15 & M2]) to indicate a valid memory access (at which point you check the data pins if you're doing a read).