Confidence Building Projects

Pages: 123
Yes. It was also fun but significantly harder.

EDIT... in fact... I think I still have it around... lemme see if I can dropbox it.


EDIT 2: here's the dropbox link:

https://www.dropbox.com/s/t7yff47lh8tsv3r/schuper.zip

dunno if I still have the source. I can look for it and post it if interested. It's very old and probably sloppy.
Last edited on
I'm a bit interested in the SNES emu source.
I am not very good at taking the big image and then breaking it down to the smaller components. 17 years and I still have trouble with that, but like Disch pointed out, I don't challenge myself that often so I've never really had a chance to build that skill. May be an interesting challenge regardless.
maeriden wrote:
I'm a bit interested in the SNES emu source.


Here is the source I could find. I have no idea if it's the same version as the binary I uploaded.

https://www.dropbox.com/s/l8i4cq2u3twpgeq/Schupersrc.zip

EDIT: holy crap I'm looking at this for the first time in years and I don't remember how any of it works. good luck. lol /EDIT

BHXSpecter wrote:
I am not very good at taking the big image and then breaking it down to the smaller components.


It's definitely an important skill.

Anyway, I definitely think the NES emu would be a good project for you. But it's your call. =)
Last edited on
I think the thing that makes me hesitate is that I don't know where to even start. My interest is peaked in it except for the fact of starting it. Looking at the links you provided and goolging to get an idea of where to start.
Last edited on by closed account z6A9GNh0
The CPU. Without it you can do nothing.
First steps should be a CPU (6502) emulator:

--) make a generic 'read' function which reads from a give 16-bit address and returns an 8-bit value... and a 'write' function which writes an 8-bit value to a 16-bit address.

--) leave the functions blank, and just use them as you'd expect

--) in a loop, read() a byte from the current PC. This byte is your opcode. Run it through a big switch to determine what opcode it is and simulate whatever instruction it does. It may involve more reading/writing or just other register manipulation

--) run the loop a few hundred times (eventually you'll want to make it so each instruction takes the appropriate number of cycles, and then run for X cycles)

--) once you've got that loop and big switch set up... make the reads/writes actually GO somewhere (ie: read from ROM, read+write RAM -- don't worry about special reg addresses yet)

--) get it to where you can actually load up a basic "mapper 0" ROM. Load the ROM up so that it can be read by the CPU.



At this point you should have a mostly functioning CPU. You won't be able to get very far in commercial games, as they'll quickly do an infinite loop while waiting for the PPU to respond... but you should be able to run some test ROMs to make sure your program is working. specifically "nestest" when run in "auto" mode, will stress your CPU and expose logic errors.

Throw in a tracer so you can log what your CPU is doing and you can tweak it and make changes.


Once it's fully functional you can start working on PPU basics.
Last edited on
The first thing seems easy. The rest seems complicated af.
Disch wrote:
make a generic 'read' function which reads from a give 16-bit address and returns an 8-bit value
And a read function which reads from a 16-bit address and returns a 16-bit value!
fucking little endian

Also, I notice that several memory regions in the 6502 are mirrored and such, and program data starts from 0x8000 onwards. Is it important to mirror everything to specification? Or will it end up not mattering?

EDIT:
And implementing a memory mapper (for roms with more than 32kb of data and can't fit all at once in the 6502's memory) is still confusing to me. How would the mapper know when to switch out memory regions?
Last edited on
Thumper wrote:
And a read function which reads from a 16-bit address and returns a 16-bit value!
fucking little endian


Yup that's definitely an approach. ;)

Also little endian is superior. In the case of the 6502 it literally speeds up the CPU.

Also, I notice that several memory regions in the 6502 are mirrored and such, and program data starts from 0x8000 onwards. Is it important to mirror everything to specification? Or will it end up not mattering?


For most games it won't matter... but every once in a while you'll run into a weird one.

Mirroring isn't difficult to do, though. What I typically do is have reads/writes to each of the $X000 pages go to a different function. For example... $0000 and $1000 pages would read system RAM... but since there is only 2K system ram and not 8K.. you just mask out the low bits to mirror it:

1
2
3
4
u8 read(u16 a)
{
    return ram[a & 0x07FF];  // <- handles all your mirroring
}


Likewise, the 8 ppu regs that are mirrored across $2xxx and $3xxx pages:

1
2
3
4
5
6
7
8
u8 read(u16 a)
{
    switch(a & 7)
    {
    case 0:  // $2000
      //...
    case 1:  // $2001
      // etc 


And implementing a memory mapper (for roms with more than 32kb of data and can't fit all at once in the 6502's memory) is still confusing to me. How would the mapper know when to switch out memory regions?


bankswitching is usually (99% of the time) triggered by a register write. Most mappers put their registers in ROM space (between $8000-FFFF) since ROM can't be written to anyway.

Mapper 2 is probably the easiest.

$8000-BFFF is the swappable bank
$C000-FFFF is fixed at the last bank

The game swaps out the $8000 page by writing anywhere in $8000-FFFF. The value written determines the 16K page number to swap in.


I wrote a ton of mapper docs a long while back which outlined the details. The wiki has more up-to-date and accurate info on many of them.... but personally I find the format of my docs easier to understand.

http://www.romhacking.net/download/documents/362/



But all of that said... I wouldn't worry about mappers until you get basic "mapper 0" games running.
Last edited on
Thanks Disch, that really helps.

One more (probably obvious) question:
The program counter should start at the address stored in the reset vector (0xFFFC/D) correct?
Yes. =)
Topic archived. No new replies allowed.
Pages: 123