Need help with MMC5 and WRAM

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

Post Reply
fracturedsoul
Posts: 3
Joined: Thu Jul 10, 2014 3:58 pm

Need help with MMC5 and WRAM

Post by fracturedsoul »

I started to implement MMC5 on a emulator, castlevania work with graphical glitches (the background show garbled graphics), now i'm trying to add support to other games like metal slader glory, but i don't know exactly what this WRAM is, some places say is cartridge RAM, but other say it's supposed to be written to 0x6000, I tried writting the values suplied to the mapper to 0x6000 but the game still not working....

Thanks! :) And sorry for my bad english.
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: Need help with MMC5 and WRAM

Post by koitsu »

Yeah, I don't particularly like the inconsistencies in naming conventions used throughout documentation and individuals who can't seem to make up their mind (WRAM vs. SRAM vs. PRG RAM).

Basically as I understand it what you're asking about is the MMC5's WRAM support where an 8KByte region of WRAM (i.e. PRG RAM) can be mapped to $6000-7fff (the docs say also to $8000-dfff but I don't see how). The physical WRAM chip on the board can be up to 64KBytes in size. The register that controls all this is $5113. Basically I treat this as SRAM, as in "up to 64KBytes of RAM that is a physical chip on the cart (and possibly battery-backed?)".

The ExRAM stuff is separate from this. ExRAM is a 1KByte region of memory that's within the MMC5 itself (the actual mapper chip itself contains the RAM), and it can be used for a multitude of things (see "Overview" or "Disch's Notes" sections in the MMC5 page below -- actually reading the entire "Disch's Notes" section would be worthwhile).

As for your glitching graphics in Castlevania 3 -- I've seen this happen on numerous emulators (either when the actual game starts, or especially when panning). I didn't know if the game actually used ExRAM though, so I ran FCEUX and looking for any writes to $5104 or $5105. When powering on:

Code: Select all

 0F:E0DF:A9 00     LDA #$00
 0F:E0E1:8D 10 50  STA $5010 = #$E0
 0F:E0E4:8D 04 51  STA $5104 = #$E0
 0F:E0E7:A9 44     LDA #$44
 0F:E0E9:85 25     STA $0025 = #$E4
 0F:E0EB:8D 05 51  STA $5105 = #$E0
To me, this means use ExRAM as an extra nametable (%00 written to $5104) and vertical mirroring is selected ($44 to $5105).

However, I noticed that while the actual game is running, it's constantly running these two pieces of code (since I have a breakpoint on $5105):

Code: Select all

 0F:E05B:A5 25     LDA $0025 = #$E4
 0F:E05D:8D 05 51  STA $5105 = #$E4
...
 01:9FCB:A9 44     LDA #$44
 01:9FCD:8D 05 51  STA $5105 = #$44
Note the value $e4 (binary %11100100) in zero page $25. I had to read the "Disch's Notes" stuff to understand what the actual values here controlled. The %11 written to bits 7,6 would indicate use of ExRAM for the bottom-right nametable. So that may help you in your quest, or maybe it won't. Best I could do my first time around looking at CV3 / dealing with MMC5. :-)

Reference: http://wiki.nesdev.com/w/index.php/MMC5
Reference: http://wiki.nesdev.com/w/index.php/MMC5 ... .245113.29

P.S. -- Which emulator are you adding MMC5 support for? I'm curious if it's listed in the never-ending Emulators - Under Development page.
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Need help with MMC5 and WRAM

Post by tepples »

koitsu wrote:Yeah, I don't particularly like the inconsistencies in naming conventions used throughout documentation and individuals who can't seem to make up their mind (WRAM vs. SRAM vs. PRG RAM).
SRAM denotes the memory technology (as opposed to DRAM), and WRAM or PRG RAM denotes which bus it's connected to (as opposed to CHR RAM).
Basically as I understand it what you're asking about is the MMC5's WRAM support where an 8KByte region of WRAM (i.e. PRG RAM) can be mapped to $6000-7fff (the docs say also to $8000-dfff but I don't see how).
Let me guess how: the MMC5 decodes the WRAM's enable if the corresponding PRG bank register is set to use SRAM.
fracturedsoul
Posts: 3
Joined: Thu Jul 10, 2014 3:58 pm

Re: Need help with MMC5 and WRAM

Post by fracturedsoul »

Thanks for the quick reply, i managed to fix castlevania 3 graphics with some tweaks on MMC5 IRQ, the IRQ was incorret i guess... Stilla a few garbled graphics, probably related to ExRAM like you said.... But i still don't understand one thing. The doc says:

"Note that no commercial games rely on this mirroring -- therefore you can take the easy way out and simply give
all MMC5 games 64k PRG-RAM."

How can i give 64k of ram to the game? How the game would access this 64kb of ram? I really only want to run metal slader glory, and the game seems to use this thing, it write to 0x5117 and the value says it should use PRG-RAM.

The doc also says:

"$5114-5117: [RPPP PPPP]
R = ROM select (0=select RAM, 1=select ROM) **unused in $5117**
P = PRG page"

So i can do value AND 0x80 to get the R to know if the game use PRG-RAM, and value AND 0x7F to get the page. The page would indicate the cart ram? And would start on offset 0? Or is the NES Ram starting at offset 0x6000? But then it would only have 32kb (0x6000 ~ 0xDFFF).

The game just show a black screen when i run it.
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: Need help with MMC5 and WRAM

Post by koitsu »

tepples wrote:SRAM denotes the memory technology (as opposed to DRAM), and WRAM or PRG RAM denotes which bus it's connected to (as opposed to CHR RAM).
SRAM vs. DRAM -- sure, fine, that's totally valid. But the term SRAM is a common abbreviation for "Save RAM", as in "memory that can be written to and is retained even after the system is powered off**".

I still don't understand the delineation between WRAM and PRG RAM.

WRAM to me means Work RAM, as in some memory you can read/write to or execute code from. Is that not the case?

PRG RAM to me has always been a strange term because it seems to imply something identical to PRG ROM (i.e. ROM data in the mask ROM that can get swapped in/out by a mapper), yet the memory contents aren't ROM so writes to that memory region would actually change the contents (e.g. self-modifying code within that memory region would be completely valid). So what makes PRG RAM any different than normal RAM? Is PRG RAM somehow "swappable" with a mapper? (Everything I've found in mapper docs don't seem to imply that it's swappable, just that it's RAM that's physically mapped to some particularly memory range within the NES, ex. $6000-7ffff or something).

** - Yes I'm aware there can be RAM there that isn't battery-backed, hence RAM that loses its contents after the system is shut off.
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Need help with MMC5 and WRAM

Post by tepples »

PRG RAM to me has always been a strange term because it seems to imply something identical to PRG ROM (i.e. ROM data in the mask ROM that can get swapped in/out by a mapper), yet the memory contents aren't ROM so writes to that memory region would actually change the contents (e.g. self-modifying code within that memory region would be completely valid).
Plenty of games copy code from PRG ROM to WRAM. The Legend of Zelda is among them.
Is PRG RAM somehow "swappable" with a mapper?
Very few games have bank switched PRG RAM. The only Nintendo boards I know of that include this functionality are SOROM and SXROM (MMC1) and ETROM and EWROM (MMC5). Sunsoft's FME-7/5A/5B IC almost certainly supports this, but no Sunsoft board actually uses it. You can find them by searching NesCartDB for WRAM more than or equal to 16. All but two of the results are by Koei.
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: Need help with MMC5 and WRAM

Post by koitsu »

fracturedsoul wrote:Thanks for the quick reply, i managed to fix castlevania 3 graphics with some tweaks on MMC5 IRQ, the IRQ was incorret i guess... Stilla a few garbled graphics, probably related to ExRAM like you said.... But i still don't understand one thing. The doc says:

"Note that no commercial games rely on this mirroring -- therefore you can take the easy way out and simply give
all MMC5 games 64k PRG-RAM."

How can i give 64k of ram to the game? How the game would access this 64kb of ram?
You wouldn't "give 64KBytes of RAM to the game". Instead, inside of the emulator you'd just preallocate 64KB of memory somewhere, and that would be used for ExRAM. The game itself (physically on the cart) might only have 8KB (one 8KB chip), or 16KB (2x8KB chips), or 32KB (1x32KB chip), but could also potentially have 64KB (2x32KB chips). So an emulator can simply take the easy way out by preallocating 64KB of memory for ExRAM and not actually have to emulate different sizes (8KB vs. 16KB vs. 32KB vs. 64KB).

The answer of how a game could address (access) all this memory is describe in register $5113.

Since the memory we're talking about is accessible via address range $6000-7fff (I still don't understand the $8000-dfff claim), and that range is only 8KBytes in size, for chips which are larger than 8KBytes (or multiple chips) there has to be a way to select what "page" (or region) of that chip's memory you want mapped to $6000-7fff.

The way the game does that is through bits 1-0 of MMC5's $5113 register. With only 2 bits to select which "page" you want, that means you're limited to 4 pages (page 0, page 1, page 2, or page 3). Thus the total amount of addressable RAM that could be accessed is 4*8192 = 32768 (32KB).

But don't forget bit 3 of $5113, which lets you pick "which PRG RAM chip". Imagine the game has two (2) 32KB chips on it; now you have pages 0,1,2,3 for chip 0, and pages 0,1,2,3 for chip 1, giving you 2*4*8192 = 65536 (64KB) of possible addressable RAM.

Whether or not the game itself actually has that much memory on the cart is irrelevant in this case -- the game code isn't going to magically change on you, i.e. if the game came with 2x8KB (16KB) chips, then the game code is only going to be using up to that much. But you can safely preallocate 64KBytes in the emulator without any repercussions -- just that the last 48KBytes would never be used.

Make more sense?

If you want me to give you an actual 6502 code example of how you'd go about accessing the different pages/area and chips, I can write it up for you, just as a demonstration.

I have absolutely no idea how much WRAM/PRG-RAM Metal Slader Glory included in the cart, but like I said it doesn't matter from an emulator's perspective -- just preallocate 64KB and don't worry about the "actual size used by the cart". Just implement the MMC5 register emulation and you're good to go.

One thing I will mention: Metal Slader Glory (the ROM) is apparently 1MByte in size, so it's possible that the MMC5 emulation code in whatever emulator you're improving doesn't actually support that properly; possibly it only works with up to 512KBytes thus it has the wrong ROM page mapped into memory space when the game starts and thus crashes. I really don't know -- without you actually giving some details (e.g. where the game crashes (show actual 6502 code), what leads up to the crash, or how we can reproduce it) I don't think we can help too much, all we can do is speculate.
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Need help with MMC5 and WRAM

Post by tepples »

koitsu wrote:Whether or not the game itself actually has that much memory on the cart is irrelevant in this case -- the game code isn't going to magically change on you, i.e. if the game came with 2x8KB (16KB) chips, then the game code is only going to be using up to that much.
Unless a game relies on mirroring behavior, where writes to banks 1-3 appear in bank 0 and writes to banks 5-7 appear in bank 4.
I have absolutely no idea how much WRAM/PRG-RAM Metal Slader Glory included in the cart
Apparently Metal Slader Glory isn't in NesCartDB, unlike most other Famicom games.
, but like I said it doesn't matter from an emulator's perspective -- just preallocate 64KB and don't worry about the "actual size used by the cart". Just implement the MMC5 register emulation and you're good to go.
I don't know; did any NES games do this if the RAM is too big? A lot of Super NES games did.

Image
Wrong WRAM size error from Tetris Attack


There are two solutions to the RAM size mess. One is NES 2.0, an extension to the iNES header that specifies the size of all memories. The other is to assume that carts without a battery are 8K (e.g. Castlevania III) and those with a battery are 16K (e.g. all those Koei games) and use a database of PRG ROM hashes to detect those few games using 32K.
lidnariq
Posts: 11429
Joined: Sun Apr 13, 2008 11:12 am

Re: Need help with MMC5 and WRAM

Post by lidnariq »

koitsu wrote:(I still don't understand the $8000-dfff claim)
Warning: tangent.

The original NROM games took the lazy way out, and just tell the PRG ROM to emit data whenever the CPU is accessing $8000-$FFFF.
This is because the signals coming from the NES already combined the two positive logic signals—M2 NAND A15—to make the signal needed by the ROM in the cartridge.
This shortcut is what causes bus conflicts for the slightly-more-advanced discrete logic mappers: the ROM needs the opposite of the signal the 6502 provides for whether to read or write.
More advanced mappers, even ones as simple as the MMC1and VRC1, add the extra logic necessary to combine the signal from the NES (/ROMSEL) with R/W so that the ROM only emits data when the CPU is reading from that memory space.
The convention for RAM across NES games assumed it would be easier to just map ROM over the entire upper 32 KiB of address space, but that's a convention, not an intrinsic limitation. (As an example, every other 6502-using machine)
So it'd be easy for a mapper, such as the MMC5, to include the same bits used to switch between banks to decide whether to bank RAM or ROM in any given address space.

It'd be trivial to extend any mapper in this way by adding a 74'139, very similar to how TQROM extends the MMC3 to allow CHR RAM and ROM at the same time.
fracturedsoul
Posts: 3
Joined: Thu Jul 10, 2014 3:58 pm

Re: Need help with MMC5 and WRAM

Post by fracturedsoul »

Metal Slader Glory working! :D

I writted a NES emulator from scratch in vb.net, i writted CPU, PPU, added support to a lot of mappers, everything, but was never able to emulate any mmc5 game other than castlevania 3, for years.... But now, thanks to all your guys and excellent docs at nesdev wiki, i could run Metal Slader Glory, could sound stupid, but this bring tears to my eyes.... (will port this code to my original emulator later)

This WRAM had nothing to do with the game not working, it was using ExRAM in mode 2 for storing and reading data, as 1kb of extra ram, but i didn't implemented the reads. Now the game is working, just some garbled graphics, but hell, is working!

Here is the code to emulate MMC5:
[spoiler]

Code: Select all

/* Mirroring especial */
extern int mirror[3]; //O MMC5 tem um Mirroring especial
extern unsigned char nt[3][0x3FF]; //O MMC5 pode usar ExRam como NameTable

/* RAM Extra */
unsigned char ex_ram[1023]; //RAM Extra dentro do MAPPER de 1KB
unsigned char mmc5_wram[65535]; //64kb de RAM extra em alguns cartuchos

unsigned char mmc5_wram_page;
unsigned char mmc5_wram_chip;

/* PRG/CHR */
unsigned char mmc5_prgsize;
unsigned char mmc5_chrsize;
unsigned char mmc5_gfx_mode;

unsigned char mmc5_chr_page_sprite[7];
unsigned char mmc5_chr_page_background[3];

/* IRQ */
unsigned char mmc5_irq_clear;
unsigned int mmc5_irq_scanline;
unsigned int mmc5_irq_line;
unsigned char mmc5_irq_status;
unsigned char mmc5_irq_enable;

int maskaddr(unsigned char bank) {
    if (bank >= PRG * 2) {
        unsigned char i = 0xFF;
        while ((bank & i) >= PRG * 2) {
            i /= 2;
        }
        return bank & i;
    } else {
        return bank;
    }
}

void copynt(int nt_num) {
    int i;
    for (i = 0; i < 1024; i++) {
        nt[nt_num][i] = ex_ram[i];
    }
}

void mmc5_switch_prg(int address, int bank, int prg_size) {
	memcpy(memory + address, romcache + 16 + (maskaddr(bank)  * 0x2000), prg_size);
}

void mmc5_switch_chr(int bank, int page, int chr_size) {
	int prg_size;

	int chr_start;

	unsigned int address;

	address = page * 0x400;

	prg_size = 16384;
	chr_start = prg_size * PRG;

	memcpy(ppu_memory + address, romcache + 16 + chr_start + (bank * 0x400), chr_size);
}

void mmc5_access(unsigned int address,unsigned char data) {
    switch (address) {
        case 0x5100: mmc5_prgsize = data & 3; break;
        case 0x5101: mmc5_chrsize = data & 3; break;
        case 0x5104: mmc5_gfx_mode = data & 3; printf("gfx mode -> ", mmc5_gfx_mode); break; //OBS: Apenas um modo suportado
        case 0x5105: //Mirroring do MMC5
            mirror[0] = data & 1;
            mirror[1] = (data & 4) >> 2;
            mirror[2] = (data & 0x10) >> 4;
            mirror[3] = (data & 0x40) >> 6;

            if (mmc5_gfx_mode == 1) { //Modo de usar ExRam como NameTable
                if (data & 2) {copynt(0);}
                if (data & 8) {copynt(1);}
                if (data & 0x20) {copynt(2);}
                if (data & 0x80) {copynt(3);}
            }
        break;
        case 0x5113:
            mmc5_wram_page = data & 3;
            mmc5_wram_chip = (data & 4) >> 2;
        break;
        case 0x5114:
        case 0x5115:
        case 0x5116:
        case 0x5117:
            if (data & 0x80) {
                switch(address & 7) {
                    case 4: if (mmc5_prgsize == 3) mmc5_switch_prg(0x8000, data & 0x7F, 8192); break;
                    case 5:
                        if ((mmc5_prgsize == 1) || (mmc5_prgsize == 2)) { //16k 8
                            mmc5_switch_prg(0x8000, data & 0x7F, 16384);
                        } else { //8k A
                            mmc5_switch_prg(0xA000, data & 0x7F, 8192);
                        }
                    break;
                    case 6: if ((mmc5_prgsize == 2) || (mmc5_prgsize == 3)) mmc5_switch_prg(0xC000, data & 0x7F, 8192); break;
                    case 7:
                        switch (mmc5_prgsize) {
                            case 0: mmc5_switch_prg(0x8000, data & 0x7F, 32768); break;
                            case 1: mmc5_switch_prg(0xC000, data & 0x7F, 16384); break;
                            case 2: mmc5_switch_prg(0xE000, data & 0x7F, 8192); break;
                        }
                    break;
                }
            } else { //Usa SRAM como... PRG-RAM?
                switch(address & 7) {
                    case 4: break;
                    case 5:
                        if ((mmc5_prgsize == 1) || (mmc5_prgsize == 2)) {
                            //???
                        } else { //METAL SLADER GLORY ESCREVE AQUI !!!!!!!!!!!!
                            //???
                            //printf("mmc5 wram\n");
                            memcpy(mmc5_wram + (mmc5_wram_page * 8192) + (mmc5_wram_chip * 8192), romcache + 16 + (maskaddr(data & 0x7F)  * 0x2000), 8192);
                            //mmc5_switch_prg(0x8000, data & 0x7F, 16384); break;
                        }
                    break;
                    case 6: break;
                }
            }
        break;
        case 0x5120: //CHR Sprite
        case 0x5121:
        case 0x5122:
        case 0x5123:
        case 0x5124:
        case 0x5125:
        case 0x5126:
        case 0x5127:
            mmc5_chr_page_sprite[address & 7] = data;

            switch(mmc5_chrsize) {
                case 0: mmc5_switch_chr(mmc5_chr_page_sprite[7], 0, 8192); break;
                case 1:
                    mmc5_switch_chr(mmc5_chr_page_sprite[3], 0, 4096);
                    mmc5_switch_chr(mmc5_chr_page_sprite[7], 4, 4096);
                break;
                case 2:
                    mmc5_switch_chr(mmc5_chr_page_sprite[1], 0, 2048);
                    mmc5_switch_chr(mmc5_chr_page_sprite[3], 2, 2048);
                    mmc5_switch_chr(mmc5_chr_page_sprite[5], 4, 2048);
                    mmc5_switch_chr(mmc5_chr_page_sprite[7], 6, 2048);
                break;
                case 3:
                    mmc5_switch_chr(mmc5_chr_page_sprite[0], 0, 1024);
                    mmc5_switch_chr(mmc5_chr_page_sprite[1], 1, 1024);
                    mmc5_switch_chr(mmc5_chr_page_sprite[2], 2, 1024);
                    mmc5_switch_chr(mmc5_chr_page_sprite[3], 3, 1024);
                    mmc5_switch_chr(mmc5_chr_page_sprite[4], 4, 1024);
                    mmc5_switch_chr(mmc5_chr_page_sprite[5], 5, 1024);
                    mmc5_switch_chr(mmc5_chr_page_sprite[6], 6, 1024);
                    mmc5_switch_chr(mmc5_chr_page_sprite[7], 7, 1024);
                break;
            }
        break;
        case 0x5128: //CHR Background
        case 0x5129:
        case 0x512A:
        case 0x512B:
            mmc5_chr_page_background[address & 3] = data;

            switch(mmc5_chrsize) {
                case 1: mmc5_switch_chr(mmc5_chr_page_background[3], 0, 8192); break;
                case 3:
                    mmc5_switch_chr(mmc5_chr_page_background[0], 4, 1024);
                    mmc5_switch_chr(mmc5_chr_page_background[1], 5, 1024);
                    mmc5_switch_chr(mmc5_chr_page_background[2], 6, 1024);
                    mmc5_switch_chr(mmc5_chr_page_background[3], 7, 1024);
                break;
            }
        break;
        case 0x5203: mmc5_irq_line = data; break;
        case 0x5204: mmc5_irq_enable = data; break;
    }

    if (address >= 0x5C00 && address <= 0x5FFF) {ex_ram[address - 0x5C00] = data;}
}

unsigned char mmc5_read(unsigned int address) {
    unsigned char tmp = 0;
    switch (address) {
        case 0x5204:
            tmp = mmc5_irq_status;
            mmc5_irq_status = 0;
            mmc5_irq_status &= 0x80;
            return tmp;
        break;
    }

    if (address >= 0x5C00 && address <= 0x5FFF) {return ex_ram[address - 0x5C00];}
}

void mmc5_hblank(int scanline) {
    if (scanline < 240) {
        mmc5_irq_scanline++;
        mmc5_irq_status |= 0x40;
        mmc5_irq_clear = 0;
    }

    if (mmc5_irq_scanline == mmc5_irq_line) {
        mmc5_irq_status |= 0x80;
    }
    if (++mmc5_irq_clear > 2) {
        mmc5_irq_scanline = 0;
        mmc5_irq_status &= ~0x80;
        mmc5_irq_status &= ~0x40;
    }

    if ((mmc5_irq_enable & 0x80) && (mmc5_irq_status & 0x80) && (mmc5_irq_status & 0x40)) IRQ();
}
[/spoiler]

Next step - fix graphical glitches, but this should be easy.... I hope....
zzo38
Posts: 1096
Joined: Mon Feb 07, 2011 12:46 pm

Re: Need help with MMC5 and WRAM

Post by zzo38 »

tepples wrote:There are two solutions to the RAM size mess. One is NES 2.0, an extension to the iNES header that specifies the size of all memories. The other is to assume that carts without a battery are 8K (e.g. Castlevania III) and those with a battery are 16K (e.g. all those Koei games) and use a database of PRG ROM hashes to detect those few games using 32K.
The documentation says no known games rely on mirroring, so if the NES 2.0 is not present then you should assume 64K (this also means it may work with new homebrew games requiring 64K but that don't have a NES 2.0 header or the emulator doesn't support NES 2.0 headers). If you want to use a PRG hash database, you can use that to make the RAM smaller than 64K if it is recognized game.
(Free Hero Mesh - FOSS puzzle game engine)
Post Reply