a MMC1 simple problem

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

Post Reply
Boolean
Posts: 87
Joined: Thu Jan 02, 2014 7:58 am

a MMC1 simple problem

Post by Boolean »

Hello, everyone.

1. My emulator executed 43319 instructions after launched AD&D Hillsfar (J), whether my MMC1 implementation manner was wrong?
following is stdout

Write addr 9000 data 1e
Write addr b000 data 0
Write addr ff00 data 0
Write addr ff00 data 1a
Write addr ff00 data 12
执行未文档化的操作码 $92
执行指令总数 43319


执行未文档化的操作码 means CPU executed one bad opcode, maybe CPU not switched
PRG-ROM Bank properly.
执行指令总数 means CPU already executed instructions

2. When my emulator run Double Dragon, a strange thing occured.
When I killed the boss of mission 3 the mission didn't over, I went ahead and wraped to mission 3's start.

Here is my MMC1 implementation using C++:

Code: Select all

#ifndef __MAPPER001__H__
#define __MAPPER001__H__

/*********************************************
 *
 * 2013年12月12日22:46:58
 *
 * Mapper001 (MMC1)
 *
 * Example :
 * Double Dragon 1
 *********************************************/

#include <iostream>
using namespace std;

#include "Mapper.h"
#include "ROMParser.h"

// CPU Memory
extern BYTE *PROM[4];

// PPU Memory
extern BYTE *VROM[8];

class Mapper001 : public Mapper
{
public:
	Mapper001(ROMParser *parser):Mapper(parser){

		// PROM BANK 16KB
		// VROM BANK 4KB

		// 开始的16KB
		for(int i = 0;i < 2;i++){
			::PROM[i] = Mapper::PROM + i * 0x2000;
		}
		
		// 结尾的16KB
		for(int i = 0;i < 2;i++){
			::PROM[i + 2] = Mapper::PROM + (np - 1) * 0x4000 + i * 0x2000;
		}

		for(int i = 0;i < 8;i++){
			::VROM[i] = Mapper::VROM + i * 0x400;
		}

		// 清空寄存器
		memset(this->reg, 0, sizeof(reg));
		
		// 清空临时寄存器
		Reg = 0;

		// 写偏移置为0
		Pos = 0;
	}

	void SetData(WORD addr, BYTE data){
		if(data & 0x80){ // 复位
			Pos = 0;
			reg[0] |= 0x0C;
		}else{
			// 想写入的数据
			BYTE b = data & 0x01;
			b ? Reg |= (1 << Pos) : Reg &= ~(1 << Pos);
			Pos++;

			//cout<<"This is "<<hex<<(int)Pos<<"th write addr\t$"<<addr<<"\tdata\t$"<<(int)data<<endl;

			if(Pos == 5){ // full

				Reg &= 0x1F;
				cout<<"Write addr\t"<<hex<<(int)addr<<"\tdata\t"<<(int)Reg<<endl;

				if(addr < 0xA000){			// 设置镜像 固定BANK
					reg[0] = Reg;
					this->Write0();

				}else if(addr < 0xC000){	// 设置VROM $0000 8K 或 4K
					reg[1] = Reg;
					this->Write1();

				}else if(addr < 0xE000){	// 设置VROM $1000 4K
					reg[2] = Reg;
					this->Write2();

				}else{						// 设置PROM
					reg[3] = Reg;
					this->Write3();
				}
				Pos = 0;
			}
		}
	}
	
	void  Load(BYTE* /*data*/, int /*len*/){

		cout<<"Mapper未实现加载状态"<<endl;
		//system("pause");

	}
	BYTE* Save(int * /*len*/){

		cout<<"Mapper未实现保存状态"<<endl;
		//system("pause");
		return NULL;
	}
private:
	BYTE reg[4];
	BYTE Reg; // 临时寄存器
	BYTE Pos; // 0 ~ 4

	void Write0(){
		int mirror;
		int which = (0x03 & reg[0]);
		switch(which){
			case 0:
				mirror = 2;
				break;
			case 1:
				mirror = 3;
				break;
			case 2:
				mirror = 1;
				break;
			default:
				mirror = 0;
		}
		ppu->SetMirrorManner( mirror );

		
		// 固定BANK
		switch((reg[0] >> 2) & 0x03){
			case 2: // fix first bank at $8000
				for(int i = 0;i < 2;i++){
					::PROM[i] = Mapper::PROM + i * 0x2000;
				}
				break;
			case 3: // fix last bank at $C000
				for(int i = 0;i < 2;i++){
					::PROM[i + 2] = Mapper::PROM + (Mapper::np - 1) * 0x4000 + i * 0x2000;
				}
				break;
		}
	}

	void Write1(){

		reg[1] %= 2 * Mapper::nv;

		if(0x10 & reg[0]){ // 切换4KB VROM in $0000

			for(int i = 0;i < 4;i++){
				::VROM[i] = Mapper::VROM + (0x1F & reg[1]) * 0x1000 + 0x400 * i;
			}

		}else{ // 切换8KB VROM

			for(int i = 0;i < 8;i++){
				::VROM[i] = Mapper::VROM + (0x1F & reg[1]) * 0x1000 + 0x400 * i;
			}

		}
	}
	void Write2(){
		if(0x10 & reg[0]){

			reg[2] %= 2 * Mapper::nv;

			for(int i = 0;i < 4;i++){ // 切换4KB VROM in $1000
				::VROM[4 + i] = Mapper::VROM + (0x1F & reg[2]) * 0x1000 + 0x400 * i ;
			}
		}

	}
	void Write3(){
		if(!(0x08 & reg[0])){ // 切换32KB PROM

			//reg[3] %= Mapper::np / 2;
			for(int i = 0;i < 2;i++){
				::PROM[i] = Mapper::PROM + ((0x0F & reg[3]) >> 1) * 0x8000 + i * 0x2000;
				::PROM[i + 2] = Mapper::PROM + ((0x0F & reg[3]) >> 1) * 0x8000 + i * 0x2000 + 0x4000;
			}

		}else{ // 切换16KB PROM

			//reg[3] %= Mapper::np;
			if((0x0c & reg[0]) == 0x0c){ // in $8000

				for(int i = 0;i < 2;i++){
					::PROM[i] = Mapper::PROM + (0x0F & reg[3]) * 0x4000 + i * 0x2000;
				}

			}else if((0x08 & reg[0]) == 0x08){ // in $C000

				for(int i = 0;i < 2;i++){
					::PROM[i + 2] = Mapper::PROM + (0x0F & reg[3]) * 0x4000 + i * 0x2000;
				}
				
			}else{
				cout<<"16KB 切换出错"<<endl;
				system("pause");
			}
		}
	}

};

#endif

WedNESday
Posts: 1231
Joined: Thu Sep 15, 2005 9:23 am
Location: Berlin, Germany
Contact:

Re: a MMC1 simple problem

Post by WedNESday »

AD&D Hillsfar won't run unless you allow only 1 MMC1 write per opcode.

The game immediately performs a RMW Absolute on a memory location which most emulators that have cycle-accurate CPU consider 2 writes instead of just 1. Bill & Ted and other games require this behaviour.
Boolean
Posts: 87
Joined: Thu Jan 02, 2014 7:58 am

Re: a MMC1 simple problem

Post by Boolean »

http://wiki.nesdev.com/w/index.php/Tric ... late_games Tricky-to-emulate games page lists Bill & Ted only.
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: a MMC1 simple problem

Post by tepples »

I reworded the example on that page not to imply that it is exhaustive. Expect to see a few other games that INC $FFFD (or the like) to reset the mapper.
WedNESday
Posts: 1231
Joined: Thu Sep 15, 2005 9:23 am
Location: Berlin, Germany
Contact:

Re: a MMC1 simple problem

Post by WedNESday »

Boolean wrote:http://wiki.nesdev.com/w/index.php/Tric ... late_games Tricky-to-emulate games page lists Bill & Ted only.
Trust me, Bill & Ted isn't the only one.
LocalH
Posts: 180
Joined: Thu Mar 02, 2006 12:30 pm

Re: a MMC1 simple problem

Post by LocalH »

Boolean wrote: 2. When my emulator run Double Dragon, a strange thing occured.
When I killed the boss of mission 3 the mission didn't over, I went ahead and wraped to mission 3's start.
This is normal behavior if you fight the two Abobos and then walk offscreen to the right. To advance in the game, you have to enter one of the two holes that the Abobos leave in the wall.
Boolean
Posts: 87
Joined: Thu Jan 02, 2014 7:58 am

Re: a MMC1 simple problem

Post by Boolean »

LocalH wrote:
Boolean wrote: 2. When my emulator run Double Dragon, a strange thing occured.
When I killed the boss of mission 3 the mission didn't over, I went ahead and wraped to mission 3's start.
This is normal behavior if you fight the two Abobos and then walk offscreen to the right. To advance in the game, you have to enter one of the two holes that the Abobos leave in the wall.
Thank you.
The two Abobos aren't real boss. :lol:
Post Reply