NES cartridge dumping

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
NES cartridge dumping
by on (#20975)
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:
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

by on (#20977)
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

by on (#20978)
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.

by on (#20981)
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.

by on (#20983)
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

by on (#20997)
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

by on (#20998)
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.

by on (#21000)
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.

by on (#21001)
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)

by on (#21004)
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

by on (#21007)
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.

by on (#21009)
Quote:
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.

by on (#21017)
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.

by on (#21025)
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?

by on (#21026)
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).

by on (#21029)
oh yeah, i actually remember reading that information somewhere. dur.

well i think that takes care of most of my questions for now. i think this little experiment is nearing its end. (next: dumping an n64 cart?)

thanks to all those who helped out, i appreciate it :D