But it crash on Dragon warrior 3 (USA) (invalid opcode 0x9f) .
It seems that I make some mistake on PRG bank switching. But I don't know where.
Here is my Mapper reading and writing code for mapper 1.
Yes I haven't support the PRG RAM yet (But this does not affect the crash bug).
Code: Select all
static void INesMapper1Write(INesInstance* instance, uint16_t addr, uint8_t data) {
assert(addr >= 0x6000 || addr < 0x2000);
assert(instance->mapper->number == (uint8_t)INesMapperTypeMMC1);
if (addr >= 0x8000) {
if ((data >> 7) == 1) {
instance->mapper->reg0 = 0x10;
instance->mapper->reg1 |= 0xc;
} else {
uint8_t complete = instance->mapper->reg0 & 1;
instance->mapper->reg0 >>= 1;
instance->mapper->reg0 |= (data & 1) << 4;
printf("write $%04x\n", addr);
if (complete) {
if (addr <= 0x9fff) {
// control
instance->mapper->reg1 = instance->mapper->reg0 & 0x1f;
uint8_t mirror = instance->mapper->reg1 & 3;
if (mirror <= 1) {
instance->mirror = INesInstanceMirrorOneScreen;
} else if (mirror == 2) {
instance->mirror = INesInstanceMirrorVertical;
} else {
assert(mirror == 3);
instance->mirror = INesInstanceMirrorHorizontal;
}
} else if (addr <= 0xbfff) {
// chr bank 0
printf("bank 0 = $%02x\n", instance->mapper->reg0);
instance->mapper->reg2 = instance->mapper->reg0 & 0x1f;
} else if (addr <= 0xdfff) {
// chr bank 1
instance->mapper->reg3 = instance->mapper->reg0 & 0x1f;
} else {
assert(addr <= 0xffff);
// prg bank
instance->mapper->reg4 = instance->mapper->reg0 & 0x0f;
}
instance->mapper->reg0 = 0x10;
}
}
} else if (addr >= 0x6000) {
instance->mem[addr] = data;
} else if (addr < 0x2000) {
// CHR
instance->ppu->mem[addr] = data;
}
}
static uint8_t INesMapper1Read(INesInstance* instance, uint16_t addr) {
assert(addr >= 0x6000 || addr < 0x2000);
if (addr >= 0x8000) {
size_t startOffset = 0;
size_t spaceSize = instance->file->PRGRomSize;
uint32_t bank = instance->mapper->reg4;
if (instance->file->PRGRomSize == 524288) {
if (instance->mapper->reg2 & 0x10) {
startOffset = 262144;
}
spaceSize = 262144;
}
uint8_t prgBankMode = (instance->mapper->reg1 >> 2) & 3;
if (prgBankMode <= 1) {
return instance->file->PRGRom[startOffset + ((bank & 0xe) << 14) + (uint32_t)(addr - 0x8000)];
} else if (prgBankMode == 2) {
if (addr < 0xc000) {
// fix first bank at $8000
return instance->file->PRGRom[startOffset + addr - 0x8000];
} else {
assert(addr >= 0xc000);
return instance->file->PRGRom[startOffset + (bank << 14) + (uint32_t)(addr - 0xc000)];
}
} else if (prgBankMode == 3) {
if (addr >= 0xc000) {
// fix last bank at $C000
return instance->file->PRGRom[startOffset + spaceSize - 0x4000 + ((uint32_t)addr - 0xc000)];
} else {
assert(addr < 0xc000);
return instance->file->PRGRom[startOffset + (bank << 14) + (uint32_t)(addr - 0x8000)];
}
}
} else if (addr >= 0x6000) {
return instance->mem[addr];
} else if (addr < 0x2000) {
if (instance->file->CHRRomSize == 0) {
return instance->ppu->mem[addr];
}
if ((instance->mapper->reg1 >> 4) & 1) {
// 4 kb mode, switch two separate 4 KB banks
if (addr < 0x1000) {
// lower bank
uint32_t cvtaddr = ((uint32_t)instance->mapper->reg2 << 12) + (uint32_t)addr;
if (cvtaddr >= instance->file->CHRRomSize) {
assert(!"chr size error!");
return 0;
}
return instance->file->CHRRom[cvtaddr];
} else {
// upper bank
uint32_t cvtaddr = ((uint32_t)instance->mapper->reg3 << 12) + ((uint32_t)addr - (uint32_t)0x1000);
if (cvtaddr >= instance->file->CHRRomSize) {
assert(!"chr size error!");
return 0;
}
return instance->file->CHRRom[cvtaddr];
}
} else {
// 8 kb mode, switch 8 KB at a time
uint32_t cvtaddr = ((uint32_t)(instance->mapper->reg2 & 0xfe) << 12) + (uint32_t)addr;
if (cvtaddr >= instance->file->CHRRomSize) {
assert(!"chr size error!");
return 0;
}
return instance->file->CHRRom[cvtaddr];
}
}
return 0;
}