The idea is that the functionality of any mapper can be implemented in the form of a script, used by the emulator. Of course, you'd be able to come up with some wildly unrealistic mappers, but that's aside the point.
Anyway, we should play around with this idea to see if anything might come of it.
I had the idea to set up breakpoint-like behavior. It seemed like it was efficient enough for code, but modular enough to not be hardcoded into the emulator (which is the whole point of this in the first place ).
For NROM,
PRG_Space is just the collection of bytes the CPU can have access to at any time, from any collection of ROMs on the cart. Basically, it's just a way to access the PRG section of the iNES rom.
CHR_Space is the same thing, but in this case, it can either be a ROM or RAM.
I didn't specify what happens for PPU accesses to 2000-2FFF. In that case, the emulator should just assume everything is completely hardwired and determine the behavior based on the appropriate iNES header flags. Additionally, the same could be done if you don't specify CPU 8000-FFFF accesses, and PPU 0000-1FFF accesses.
Let's try CNROM:
Now let's try AxROM, which has mapper-controlled mirroring, and PRG bankswitching.
There's a couple of kinks that would need to be ironed out, but I think this would be usable for describing mappers.
Anyway, we should play around with this idea to see if anything might come of it.
I had the idea to set up breakpoint-like behavior. It seemed like it was efficient enough for code, but modular enough to not be hardcoded into the emulator (which is the whole point of this in the first place ).
For NROM,
Code:
on CPU_Read:8000,FFFF {
PRG_Data = PRG_Space[CPU_Addr];
}
on PPU_Read:0000,1FFF {
CHR_Data = CHR_Space[PPU_Addr];
}
on PPU_Write:0000,1FFF {
CHR_Space[PPU_Addr] = CHR_Data; // This does nothing if it's ROM.
}
PRG_Data = PRG_Space[CPU_Addr];
}
on PPU_Read:0000,1FFF {
CHR_Data = CHR_Space[PPU_Addr];
}
on PPU_Write:0000,1FFF {
CHR_Space[PPU_Addr] = CHR_Data; // This does nothing if it's ROM.
}
PRG_Space is just the collection of bytes the CPU can have access to at any time, from any collection of ROMs on the cart. Basically, it's just a way to access the PRG section of the iNES rom.
CHR_Space is the same thing, but in this case, it can either be a ROM or RAM.
I didn't specify what happens for PPU accesses to 2000-2FFF. In that case, the emulator should just assume everything is completely hardwired and determine the behavior based on the appropriate iNES header flags. Additionally, the same could be done if you don't specify CPU 8000-FFFF accesses, and PPU 0000-1FFF accesses.
Let's try CNROM:
Code:
var bank [= initial value on reset];
on CPU_Write:8000,FFFF {
bank = PRG_Data << 13; // %bb..... ........
}
on CPU_Read:8000,FFFF {
PRG_Data = PRG_Space[CPU_Addr];
}
on PPU_Read:0000,1FFF {
CHR_Data = CHR_Space[bank | PPU_Addr]; // bbppppp ppppppp
}
on CPU_Write:8000,FFFF {
bank = PRG_Data << 13; // %bb..... ........
}
on CPU_Read:8000,FFFF {
PRG_Data = PRG_Space[CPU_Addr];
}
on PPU_Read:0000,1FFF {
CHR_Data = CHR_Space[bank | PPU_Addr]; // bbppppp ppppppp
}
Now let's try AxROM, which has mapper-controlled mirroring, and PRG bankswitching.
Code:
var bank;
var nametable;
on CPU_Write:8000,FFFF {
bank = (PRG_Data & 7) << 15; // %bb b....... ........
nametable = (PRG_Data & 0x10) << 6; // %n.. ........
}
on CPU_Read:8000,FFFF {
PRG_Data = PRG_Space[bank | CPU_Addr]; // %bb bccccccc cccccccc
}
on PPU_Read:0000,1FFF {
PPU_Data = PPU_Space[PPU_Addr];
}
on PPU_Write:0000,1FFF {
PPU_Space[PPU_Addr] = PPU_Data;
}
on PPU_Read:2000,2FFF {
PPU_Data = CIRAM[nametable | (PPU_Addr % 0x3FF)];
}
on PPU_Write:2000,2FFF {
CIRAM[nametable | (PPU_Addr % 0x3FF)] = PPU_Data;
}
var nametable;
on CPU_Write:8000,FFFF {
bank = (PRG_Data & 7) << 15; // %bb b....... ........
nametable = (PRG_Data & 0x10) << 6; // %n.. ........
}
on CPU_Read:8000,FFFF {
PRG_Data = PRG_Space[bank | CPU_Addr]; // %bb bccccccc cccccccc
}
on PPU_Read:0000,1FFF {
PPU_Data = PPU_Space[PPU_Addr];
}
on PPU_Write:0000,1FFF {
PPU_Space[PPU_Addr] = PPU_Data;
}
on PPU_Read:2000,2FFF {
PPU_Data = CIRAM[nametable | (PPU_Addr % 0x3FF)];
}
on PPU_Write:2000,2FFF {
CIRAM[nametable | (PPU_Addr % 0x3FF)] = PPU_Data;
}
There's a couple of kinks that would need to be ironed out, but I think this would be usable for describing mappers.