bsnes/sfc/ppu/mmio/mmio.cpp

888 lines
25 KiB
C++
Raw Normal View History

auto PPU::interlace() const -> bool {
return display.interlace;
}
auto PPU::overscan() const -> bool {
return display.overscan;
}
auto PPU::hires() const -> bool {
return true;
}
auto PPU::latch_counters() -> void {
cpu.synchronizePPU();
regs.hcounter = hdot();
regs.vcounter = vcounter();
regs.counters_latched = true;
}
auto PPU::get_vram_address() -> uint16 {
uint16 addr = regs.vram_addr;
switch(regs.vram_mapping) {
case 0: break; //direct mapping
case 1: addr = (addr & 0xff00) | ((addr & 0x001f) << 3) | ((addr >> 5) & 7); break;
case 2: addr = (addr & 0xfe00) | ((addr & 0x003f) << 3) | ((addr >> 6) & 7); break;
case 3: addr = (addr & 0xfc00) | ((addr & 0x007f) << 3) | ((addr >> 7) & 7); break;
}
return (addr << 1);
}
auto PPU::vram_read(uint addr) -> uint8 {
Update to v094r05 release. byuu says: Commands can be prefixed with: (cpu|smp|ppu|dsp|apu|vram|oam|cgram)/ to set their source. Eg "vram/hex 0800" or "smp/breakpoints.append execute ffc0"; default is cpu. These overlap a little bit in odd ways, but that's just the way the SNES works: it's not a very orthogonal system. CPU is both a processor and the main bus (ROM, RAM, WRAM, etc), APU is the shared memory by the SMP+DSP (eg use it to catch writes from either chip); PPU probably won't ever be used since it's broken down into three separate buses (VRAM, OAM, CGRAM), but DSP could be useful for tracking bugs like we found in Koushien 2 with the DSP echo buffer corrupting SMP opcodes. Technically the PPU memory pools are only ever tripped by the CPU poking at them, as the PPU doesn't ever write. I now have run.for, run.to, step.for, step.to. The difference is that run only prints the next instruction after running, whereas step prints all of the instructions along the way as well. run.to acts the same as "step over" here. Although it's not quite as nice, since you have to specify the address of the next instruction. Logging the Field/Vcounter/Hcounter on instruction listings now, good for timing information. Added in the tracer mask, as well as memory export, as well as VRAM/OAM/CGRAM/SMP read/write/execute breakpoints, as well as an APU usage map (it tracks DSP reads/writes separately, although I don't currently have debugger callbacks on DSP accesses just yet.) Have not hooked up actual SMP debugging just yet, but I plan to soon. Still thinking about how I want to allow / block interleaving of instructions (terminal output and tracing.) So ... remaining tasks at this point: - full SMP debugging - CPU+SMP interleave support - aliases - hotkeys - save states (will be kind of tricky ... will have to suppress breakpoints during synchronization, or abort a save in a break event.) - keep track of window geometry between runs
2014-02-05 11:30:08 +00:00
uint8 data = 0x00;
Update to v068r12 release. (there was no r11 release posted to the WIP thread) byuu says: This took ten hours of mind boggling insanity to pull off. It upgrades the S-PPU dot-based renderer to fetch one tile, and then output all of its pixels before fetching again. It sounds easy enough, but it's insanely difficult. I ended up taking one small shortcut, in that rather than fetch at -7, I fetch at the first instance where a tile is needed to plot to x=0. So if you have {-3 to +4 } as a tile, it fetches at -3. That won't work so well on hardware, if two BGs fetch at the same X offset, they won't have time. I have had no luck staggering the reads at BG1=-7, BG3=-5, etc. While I can shift and fetch just fine, what happens is that when a new tile is fetched in, that gives a new palette, priority, etc; and this ends up happening between two tiles which results in the right-most edges of the screen ending up with the wrong colors and such. Offset-per-tile is cheap as always. Although looking at it, I'm not sure how BG3 could pre-fetch, especially with the way one or two OPT modes can fetch two tiles. There's no magic in Hoffset caching yet, so the SMW1 pixel issue is still there. Mode 7 got a bugfix, it was off-by-one horizontally from the mosaic code. After re-designing the BG mosaic, I ended up needing a separate mosaic for Mode7, and in the process I fixed that bug. The obvious change is that the Chrono Trigger Mode7->Mode2 transition doesn't cause the pendulum to jump anymore. Windows were simplified just a tad. The range testing is shared for all modes now. Ironically, it's a bit slower, but I'll take less code over more speed for the accuracy core. Speaking of speed, because there's so much less calculations per pixel for BGs, performance for the entire emulator has gone up by 30% in the accuracy core. Pretty neat overall, I can maintain 60fps in all but, yeah you can guess can't you?
2010-09-04 03:36:03 +00:00
if(regs.display_disable || vcounter() >= (!regs.overscan ? 225 : 240)) {
Update to v094r05 release. byuu says: Commands can be prefixed with: (cpu|smp|ppu|dsp|apu|vram|oam|cgram)/ to set their source. Eg "vram/hex 0800" or "smp/breakpoints.append execute ffc0"; default is cpu. These overlap a little bit in odd ways, but that's just the way the SNES works: it's not a very orthogonal system. CPU is both a processor and the main bus (ROM, RAM, WRAM, etc), APU is the shared memory by the SMP+DSP (eg use it to catch writes from either chip); PPU probably won't ever be used since it's broken down into three separate buses (VRAM, OAM, CGRAM), but DSP could be useful for tracking bugs like we found in Koushien 2 with the DSP echo buffer corrupting SMP opcodes. Technically the PPU memory pools are only ever tripped by the CPU poking at them, as the PPU doesn't ever write. I now have run.for, run.to, step.for, step.to. The difference is that run only prints the next instruction after running, whereas step prints all of the instructions along the way as well. run.to acts the same as "step over" here. Although it's not quite as nice, since you have to specify the address of the next instruction. Logging the Field/Vcounter/Hcounter on instruction listings now, good for timing information. Added in the tracer mask, as well as memory export, as well as VRAM/OAM/CGRAM/SMP read/write/execute breakpoints, as well as an APU usage map (it tracks DSP reads/writes separately, although I don't currently have debugger callbacks on DSP accesses just yet.) Have not hooked up actual SMP debugging just yet, but I plan to soon. Still thinking about how I want to allow / block interleaving of instructions (terminal output and tracing.) So ... remaining tasks at this point: - full SMP debugging - CPU+SMP interleave support - aliases - hotkeys - save states (will be kind of tricky ... will have to suppress breakpoints during synchronization, or abort a save in a break event.) - keep track of window geometry between runs
2014-02-05 11:30:08 +00:00
data = vram[addr];
debugger.vram_read(addr, data);
}
Update to v094r05 release. byuu says: Commands can be prefixed with: (cpu|smp|ppu|dsp|apu|vram|oam|cgram)/ to set their source. Eg "vram/hex 0800" or "smp/breakpoints.append execute ffc0"; default is cpu. These overlap a little bit in odd ways, but that's just the way the SNES works: it's not a very orthogonal system. CPU is both a processor and the main bus (ROM, RAM, WRAM, etc), APU is the shared memory by the SMP+DSP (eg use it to catch writes from either chip); PPU probably won't ever be used since it's broken down into three separate buses (VRAM, OAM, CGRAM), but DSP could be useful for tracking bugs like we found in Koushien 2 with the DSP echo buffer corrupting SMP opcodes. Technically the PPU memory pools are only ever tripped by the CPU poking at them, as the PPU doesn't ever write. I now have run.for, run.to, step.for, step.to. The difference is that run only prints the next instruction after running, whereas step prints all of the instructions along the way as well. run.to acts the same as "step over" here. Although it's not quite as nice, since you have to specify the address of the next instruction. Logging the Field/Vcounter/Hcounter on instruction listings now, good for timing information. Added in the tracer mask, as well as memory export, as well as VRAM/OAM/CGRAM/SMP read/write/execute breakpoints, as well as an APU usage map (it tracks DSP reads/writes separately, although I don't currently have debugger callbacks on DSP accesses just yet.) Have not hooked up actual SMP debugging just yet, but I plan to soon. Still thinking about how I want to allow / block interleaving of instructions (terminal output and tracing.) So ... remaining tasks at this point: - full SMP debugging - CPU+SMP interleave support - aliases - hotkeys - save states (will be kind of tricky ... will have to suppress breakpoints during synchronization, or abort a save in a break event.) - keep track of window geometry between runs
2014-02-05 11:30:08 +00:00
return data;
}
auto PPU::vram_write(uint addr, uint8 data) -> void {
Update to v068r12 release. (there was no r11 release posted to the WIP thread) byuu says: This took ten hours of mind boggling insanity to pull off. It upgrades the S-PPU dot-based renderer to fetch one tile, and then output all of its pixels before fetching again. It sounds easy enough, but it's insanely difficult. I ended up taking one small shortcut, in that rather than fetch at -7, I fetch at the first instance where a tile is needed to plot to x=0. So if you have {-3 to +4 } as a tile, it fetches at -3. That won't work so well on hardware, if two BGs fetch at the same X offset, they won't have time. I have had no luck staggering the reads at BG1=-7, BG3=-5, etc. While I can shift and fetch just fine, what happens is that when a new tile is fetched in, that gives a new palette, priority, etc; and this ends up happening between two tiles which results in the right-most edges of the screen ending up with the wrong colors and such. Offset-per-tile is cheap as always. Although looking at it, I'm not sure how BG3 could pre-fetch, especially with the way one or two OPT modes can fetch two tiles. There's no magic in Hoffset caching yet, so the SMW1 pixel issue is still there. Mode 7 got a bugfix, it was off-by-one horizontally from the mosaic code. After re-designing the BG mosaic, I ended up needing a separate mosaic for Mode7, and in the process I fixed that bug. The obvious change is that the Chrono Trigger Mode7->Mode2 transition doesn't cause the pendulum to jump anymore. Windows were simplified just a tad. The range testing is shared for all modes now. Ironically, it's a bit slower, but I'll take less code over more speed for the accuracy core. Speaking of speed, because there's so much less calculations per pixel for BGs, performance for the entire emulator has gone up by 30% in the accuracy core. Pretty neat overall, I can maintain 60fps in all but, yeah you can guess can't you?
2010-09-04 03:36:03 +00:00
if(regs.display_disable || vcounter() >= (!regs.overscan ? 225 : 240)) {
vram[addr] = data;
Update to v094r05 release. byuu says: Commands can be prefixed with: (cpu|smp|ppu|dsp|apu|vram|oam|cgram)/ to set their source. Eg "vram/hex 0800" or "smp/breakpoints.append execute ffc0"; default is cpu. These overlap a little bit in odd ways, but that's just the way the SNES works: it's not a very orthogonal system. CPU is both a processor and the main bus (ROM, RAM, WRAM, etc), APU is the shared memory by the SMP+DSP (eg use it to catch writes from either chip); PPU probably won't ever be used since it's broken down into three separate buses (VRAM, OAM, CGRAM), but DSP could be useful for tracking bugs like we found in Koushien 2 with the DSP echo buffer corrupting SMP opcodes. Technically the PPU memory pools are only ever tripped by the CPU poking at them, as the PPU doesn't ever write. I now have run.for, run.to, step.for, step.to. The difference is that run only prints the next instruction after running, whereas step prints all of the instructions along the way as well. run.to acts the same as "step over" here. Although it's not quite as nice, since you have to specify the address of the next instruction. Logging the Field/Vcounter/Hcounter on instruction listings now, good for timing information. Added in the tracer mask, as well as memory export, as well as VRAM/OAM/CGRAM/SMP read/write/execute breakpoints, as well as an APU usage map (it tracks DSP reads/writes separately, although I don't currently have debugger callbacks on DSP accesses just yet.) Have not hooked up actual SMP debugging just yet, but I plan to soon. Still thinking about how I want to allow / block interleaving of instructions (terminal output and tracing.) So ... remaining tasks at this point: - full SMP debugging - CPU+SMP interleave support - aliases - hotkeys - save states (will be kind of tricky ... will have to suppress breakpoints during synchronization, or abort a save in a break event.) - keep track of window geometry between runs
2014-02-05 11:30:08 +00:00
debugger.vram_write(addr, data);
}
}
auto PPU::oam_read(uint addr) -> uint8 {
Update to v094r05 release. byuu says: Commands can be prefixed with: (cpu|smp|ppu|dsp|apu|vram|oam|cgram)/ to set their source. Eg "vram/hex 0800" or "smp/breakpoints.append execute ffc0"; default is cpu. These overlap a little bit in odd ways, but that's just the way the SNES works: it's not a very orthogonal system. CPU is both a processor and the main bus (ROM, RAM, WRAM, etc), APU is the shared memory by the SMP+DSP (eg use it to catch writes from either chip); PPU probably won't ever be used since it's broken down into three separate buses (VRAM, OAM, CGRAM), but DSP could be useful for tracking bugs like we found in Koushien 2 with the DSP echo buffer corrupting SMP opcodes. Technically the PPU memory pools are only ever tripped by the CPU poking at them, as the PPU doesn't ever write. I now have run.for, run.to, step.for, step.to. The difference is that run only prints the next instruction after running, whereas step prints all of the instructions along the way as well. run.to acts the same as "step over" here. Although it's not quite as nice, since you have to specify the address of the next instruction. Logging the Field/Vcounter/Hcounter on instruction listings now, good for timing information. Added in the tracer mask, as well as memory export, as well as VRAM/OAM/CGRAM/SMP read/write/execute breakpoints, as well as an APU usage map (it tracks DSP reads/writes separately, although I don't currently have debugger callbacks on DSP accesses just yet.) Have not hooked up actual SMP debugging just yet, but I plan to soon. Still thinking about how I want to allow / block interleaving of instructions (terminal output and tracing.) So ... remaining tasks at this point: - full SMP debugging - CPU+SMP interleave support - aliases - hotkeys - save states (will be kind of tricky ... will have to suppress breakpoints during synchronization, or abort a save in a break event.) - keep track of window geometry between runs
2014-02-05 11:30:08 +00:00
uint8 data = oam[addr];
debugger.oam_read(addr, data);
return data;
Update to v085r08 release. byuu says: Changelog: - follow the Laevateinn topic to get most of it - also added NMI, IRQ step buttons to CPU debugger - also added trace masking + trace mask reset - also added memory export - cartridge loading is entirely folder-based now FitzRoy, I'll go ahead and make a second compromise with you for v086: I'll match the following: /path/to/SNES.sfc/*.sfc /path/to/NES.fc/*.prg, *.chr (split format) /path/to/NES.fc/*.fc (merged format) /path/to/GB.gb/*.gb /path/to/GBC.gbc/*.gbc Condition will be that there can only be one of each file. If there's more than one, it'll abort. That lets me name my ROMs as "Game.fc/Game.fc", and you can name yours as "Game.fc/cartridge.prg, cartridge.chr". Or whatever you want. We'll just go with that, see what fares out as the most popular, and then restrict it back to that method. The folder must have the .fc, etc extension though. That will be how we avoid false-positive folder matches. [Editor's note - the Laevateinn topic mentions these changes for v085r08: Added SMP/PPU breakpoints, SMP debugger, SMP stepping / tracing, memory editing on APU-bus / VRAM / OAM / CGRAM, save state menu, WRAM mirroring on breakpoints, protected MMIO memory regions (otherwise, viewing $002100 could crash your game.) Major missing components: - trace mask - trace mask clear / usage map clear - window geometry caching / sizing improvements - VRAM viewer - properties viewer - working memory export button The rest will most likely appear after v086 is released. ]
2012-02-12 05:35:40 +00:00
}
auto PPU::oam_write(uint addr, uint8 data) -> void {
Update to v085r08 release. byuu says: Changelog: - follow the Laevateinn topic to get most of it - also added NMI, IRQ step buttons to CPU debugger - also added trace masking + trace mask reset - also added memory export - cartridge loading is entirely folder-based now FitzRoy, I'll go ahead and make a second compromise with you for v086: I'll match the following: /path/to/SNES.sfc/*.sfc /path/to/NES.fc/*.prg, *.chr (split format) /path/to/NES.fc/*.fc (merged format) /path/to/GB.gb/*.gb /path/to/GBC.gbc/*.gbc Condition will be that there can only be one of each file. If there's more than one, it'll abort. That lets me name my ROMs as "Game.fc/Game.fc", and you can name yours as "Game.fc/cartridge.prg, cartridge.chr". Or whatever you want. We'll just go with that, see what fares out as the most popular, and then restrict it back to that method. The folder must have the .fc, etc extension though. That will be how we avoid false-positive folder matches. [Editor's note - the Laevateinn topic mentions these changes for v085r08: Added SMP/PPU breakpoints, SMP debugger, SMP stepping / tracing, memory editing on APU-bus / VRAM / OAM / CGRAM, save state menu, WRAM mirroring on breakpoints, protected MMIO memory regions (otherwise, viewing $002100 could crash your game.) Major missing components: - trace mask - trace mask clear / usage map clear - window geometry caching / sizing improvements - VRAM viewer - properties viewer - working memory export button The rest will most likely appear after v086 is released. ]
2012-02-12 05:35:40 +00:00
oam[addr] = data;
sprite.update(addr, data);
Update to v094r05 release. byuu says: Commands can be prefixed with: (cpu|smp|ppu|dsp|apu|vram|oam|cgram)/ to set their source. Eg "vram/hex 0800" or "smp/breakpoints.append execute ffc0"; default is cpu. These overlap a little bit in odd ways, but that's just the way the SNES works: it's not a very orthogonal system. CPU is both a processor and the main bus (ROM, RAM, WRAM, etc), APU is the shared memory by the SMP+DSP (eg use it to catch writes from either chip); PPU probably won't ever be used since it's broken down into three separate buses (VRAM, OAM, CGRAM), but DSP could be useful for tracking bugs like we found in Koushien 2 with the DSP echo buffer corrupting SMP opcodes. Technically the PPU memory pools are only ever tripped by the CPU poking at them, as the PPU doesn't ever write. I now have run.for, run.to, step.for, step.to. The difference is that run only prints the next instruction after running, whereas step prints all of the instructions along the way as well. run.to acts the same as "step over" here. Although it's not quite as nice, since you have to specify the address of the next instruction. Logging the Field/Vcounter/Hcounter on instruction listings now, good for timing information. Added in the tracer mask, as well as memory export, as well as VRAM/OAM/CGRAM/SMP read/write/execute breakpoints, as well as an APU usage map (it tracks DSP reads/writes separately, although I don't currently have debugger callbacks on DSP accesses just yet.) Have not hooked up actual SMP debugging just yet, but I plan to soon. Still thinking about how I want to allow / block interleaving of instructions (terminal output and tracing.) So ... remaining tasks at this point: - full SMP debugging - CPU+SMP interleave support - aliases - hotkeys - save states (will be kind of tricky ... will have to suppress breakpoints during synchronization, or abort a save in a break event.) - keep track of window geometry between runs
2014-02-05 11:30:08 +00:00
debugger.oam_write(addr, data);
Update to v085r08 release. byuu says: Changelog: - follow the Laevateinn topic to get most of it - also added NMI, IRQ step buttons to CPU debugger - also added trace masking + trace mask reset - also added memory export - cartridge loading is entirely folder-based now FitzRoy, I'll go ahead and make a second compromise with you for v086: I'll match the following: /path/to/SNES.sfc/*.sfc /path/to/NES.fc/*.prg, *.chr (split format) /path/to/NES.fc/*.fc (merged format) /path/to/GB.gb/*.gb /path/to/GBC.gbc/*.gbc Condition will be that there can only be one of each file. If there's more than one, it'll abort. That lets me name my ROMs as "Game.fc/Game.fc", and you can name yours as "Game.fc/cartridge.prg, cartridge.chr". Or whatever you want. We'll just go with that, see what fares out as the most popular, and then restrict it back to that method. The folder must have the .fc, etc extension though. That will be how we avoid false-positive folder matches. [Editor's note - the Laevateinn topic mentions these changes for v085r08: Added SMP/PPU breakpoints, SMP debugger, SMP stepping / tracing, memory editing on APU-bus / VRAM / OAM / CGRAM, save state menu, WRAM mirroring on breakpoints, protected MMIO memory regions (otherwise, viewing $002100 could crash your game.) Major missing components: - trace mask - trace mask clear / usage map clear - window geometry caching / sizing improvements - VRAM viewer - properties viewer - working memory export button The rest will most likely appear after v086 is released. ]
2012-02-12 05:35:40 +00:00
}
auto PPU::cgram_read(uint addr) -> uint8 {
Update to v094r05 release. byuu says: Commands can be prefixed with: (cpu|smp|ppu|dsp|apu|vram|oam|cgram)/ to set their source. Eg "vram/hex 0800" or "smp/breakpoints.append execute ffc0"; default is cpu. These overlap a little bit in odd ways, but that's just the way the SNES works: it's not a very orthogonal system. CPU is both a processor and the main bus (ROM, RAM, WRAM, etc), APU is the shared memory by the SMP+DSP (eg use it to catch writes from either chip); PPU probably won't ever be used since it's broken down into three separate buses (VRAM, OAM, CGRAM), but DSP could be useful for tracking bugs like we found in Koushien 2 with the DSP echo buffer corrupting SMP opcodes. Technically the PPU memory pools are only ever tripped by the CPU poking at them, as the PPU doesn't ever write. I now have run.for, run.to, step.for, step.to. The difference is that run only prints the next instruction after running, whereas step prints all of the instructions along the way as well. run.to acts the same as "step over" here. Although it's not quite as nice, since you have to specify the address of the next instruction. Logging the Field/Vcounter/Hcounter on instruction listings now, good for timing information. Added in the tracer mask, as well as memory export, as well as VRAM/OAM/CGRAM/SMP read/write/execute breakpoints, as well as an APU usage map (it tracks DSP reads/writes separately, although I don't currently have debugger callbacks on DSP accesses just yet.) Have not hooked up actual SMP debugging just yet, but I plan to soon. Still thinking about how I want to allow / block interleaving of instructions (terminal output and tracing.) So ... remaining tasks at this point: - full SMP debugging - CPU+SMP interleave support - aliases - hotkeys - save states (will be kind of tricky ... will have to suppress breakpoints during synchronization, or abort a save in a break event.) - keep track of window geometry between runs
2014-02-05 11:30:08 +00:00
uint8 data = cgram[addr];
debugger.cgram_read(addr, data);
return data;
Update to v085r08 release. byuu says: Changelog: - follow the Laevateinn topic to get most of it - also added NMI, IRQ step buttons to CPU debugger - also added trace masking + trace mask reset - also added memory export - cartridge loading is entirely folder-based now FitzRoy, I'll go ahead and make a second compromise with you for v086: I'll match the following: /path/to/SNES.sfc/*.sfc /path/to/NES.fc/*.prg, *.chr (split format) /path/to/NES.fc/*.fc (merged format) /path/to/GB.gb/*.gb /path/to/GBC.gbc/*.gbc Condition will be that there can only be one of each file. If there's more than one, it'll abort. That lets me name my ROMs as "Game.fc/Game.fc", and you can name yours as "Game.fc/cartridge.prg, cartridge.chr". Or whatever you want. We'll just go with that, see what fares out as the most popular, and then restrict it back to that method. The folder must have the .fc, etc extension though. That will be how we avoid false-positive folder matches. [Editor's note - the Laevateinn topic mentions these changes for v085r08: Added SMP/PPU breakpoints, SMP debugger, SMP stepping / tracing, memory editing on APU-bus / VRAM / OAM / CGRAM, save state menu, WRAM mirroring on breakpoints, protected MMIO memory regions (otherwise, viewing $002100 could crash your game.) Major missing components: - trace mask - trace mask clear / usage map clear - window geometry caching / sizing improvements - VRAM viewer - properties viewer - working memory export button The rest will most likely appear after v086 is released. ]
2012-02-12 05:35:40 +00:00
}
auto PPU::cgram_write(uint addr, uint8 data) -> void {
Update to v085r08 release. byuu says: Changelog: - follow the Laevateinn topic to get most of it - also added NMI, IRQ step buttons to CPU debugger - also added trace masking + trace mask reset - also added memory export - cartridge loading is entirely folder-based now FitzRoy, I'll go ahead and make a second compromise with you for v086: I'll match the following: /path/to/SNES.sfc/*.sfc /path/to/NES.fc/*.prg, *.chr (split format) /path/to/NES.fc/*.fc (merged format) /path/to/GB.gb/*.gb /path/to/GBC.gbc/*.gbc Condition will be that there can only be one of each file. If there's more than one, it'll abort. That lets me name my ROMs as "Game.fc/Game.fc", and you can name yours as "Game.fc/cartridge.prg, cartridge.chr". Or whatever you want. We'll just go with that, see what fares out as the most popular, and then restrict it back to that method. The folder must have the .fc, etc extension though. That will be how we avoid false-positive folder matches. [Editor's note - the Laevateinn topic mentions these changes for v085r08: Added SMP/PPU breakpoints, SMP debugger, SMP stepping / tracing, memory editing on APU-bus / VRAM / OAM / CGRAM, save state menu, WRAM mirroring on breakpoints, protected MMIO memory regions (otherwise, viewing $002100 could crash your game.) Major missing components: - trace mask - trace mask clear / usage map clear - window geometry caching / sizing improvements - VRAM viewer - properties viewer - working memory export button The rest will most likely appear after v086 is released. ]
2012-02-12 05:35:40 +00:00
cgram[addr] = data;
Update to v094r05 release. byuu says: Commands can be prefixed with: (cpu|smp|ppu|dsp|apu|vram|oam|cgram)/ to set their source. Eg "vram/hex 0800" or "smp/breakpoints.append execute ffc0"; default is cpu. These overlap a little bit in odd ways, but that's just the way the SNES works: it's not a very orthogonal system. CPU is both a processor and the main bus (ROM, RAM, WRAM, etc), APU is the shared memory by the SMP+DSP (eg use it to catch writes from either chip); PPU probably won't ever be used since it's broken down into three separate buses (VRAM, OAM, CGRAM), but DSP could be useful for tracking bugs like we found in Koushien 2 with the DSP echo buffer corrupting SMP opcodes. Technically the PPU memory pools are only ever tripped by the CPU poking at them, as the PPU doesn't ever write. I now have run.for, run.to, step.for, step.to. The difference is that run only prints the next instruction after running, whereas step prints all of the instructions along the way as well. run.to acts the same as "step over" here. Although it's not quite as nice, since you have to specify the address of the next instruction. Logging the Field/Vcounter/Hcounter on instruction listings now, good for timing information. Added in the tracer mask, as well as memory export, as well as VRAM/OAM/CGRAM/SMP read/write/execute breakpoints, as well as an APU usage map (it tracks DSP reads/writes separately, although I don't currently have debugger callbacks on DSP accesses just yet.) Have not hooked up actual SMP debugging just yet, but I plan to soon. Still thinking about how I want to allow / block interleaving of instructions (terminal output and tracing.) So ... remaining tasks at this point: - full SMP debugging - CPU+SMP interleave support - aliases - hotkeys - save states (will be kind of tricky ... will have to suppress breakpoints during synchronization, or abort a save in a break event.) - keep track of window geometry between runs
2014-02-05 11:30:08 +00:00
debugger.cgram_write(addr, data);
Update to v085r08 release. byuu says: Changelog: - follow the Laevateinn topic to get most of it - also added NMI, IRQ step buttons to CPU debugger - also added trace masking + trace mask reset - also added memory export - cartridge loading is entirely folder-based now FitzRoy, I'll go ahead and make a second compromise with you for v086: I'll match the following: /path/to/SNES.sfc/*.sfc /path/to/NES.fc/*.prg, *.chr (split format) /path/to/NES.fc/*.fc (merged format) /path/to/GB.gb/*.gb /path/to/GBC.gbc/*.gbc Condition will be that there can only be one of each file. If there's more than one, it'll abort. That lets me name my ROMs as "Game.fc/Game.fc", and you can name yours as "Game.fc/cartridge.prg, cartridge.chr". Or whatever you want. We'll just go with that, see what fares out as the most popular, and then restrict it back to that method. The folder must have the .fc, etc extension though. That will be how we avoid false-positive folder matches. [Editor's note - the Laevateinn topic mentions these changes for v085r08: Added SMP/PPU breakpoints, SMP debugger, SMP stepping / tracing, memory editing on APU-bus / VRAM / OAM / CGRAM, save state menu, WRAM mirroring on breakpoints, protected MMIO memory regions (otherwise, viewing $002100 could crash your game.) Major missing components: - trace mask - trace mask clear / usage map clear - window geometry caching / sizing improvements - VRAM viewer - properties viewer - working memory export button The rest will most likely appear after v086 is released. ]
2012-02-12 05:35:40 +00:00
}
auto PPU::mmio_update_video_mode() -> void {
switch(regs.bgmode) {
case 0:
bg1.regs.mode = Background::Mode::BPP2; bg1.regs.priority0 = 8; bg1.regs.priority1 = 11;
bg2.regs.mode = Background::Mode::BPP2; bg2.regs.priority0 = 7; bg2.regs.priority1 = 10;
bg3.regs.mode = Background::Mode::BPP2; bg3.regs.priority0 = 2; bg3.regs.priority1 = 5;
bg4.regs.mode = Background::Mode::BPP2; bg4.regs.priority0 = 1; bg4.regs.priority1 = 4;
sprite.regs.priority0 = 3; sprite.regs.priority1 = 6; sprite.regs.priority2 = 9; sprite.regs.priority3 = 12;
break;
case 1:
bg1.regs.mode = Background::Mode::BPP4;
bg2.regs.mode = Background::Mode::BPP4;
bg3.regs.mode = Background::Mode::BPP2;
bg4.regs.mode = Background::Mode::Inactive;
if(regs.bg3_priority) {
bg1.regs.priority0 = 5; bg1.regs.priority1 = 8;
bg2.regs.priority0 = 4; bg2.regs.priority1 = 7;
bg3.regs.priority0 = 1; bg3.regs.priority1 = 10;
sprite.regs.priority0 = 2; sprite.regs.priority1 = 3; sprite.regs.priority2 = 6; sprite.regs.priority3 = 9;
} else {
bg1.regs.priority0 = 6; bg1.regs.priority1 = 9;
bg2.regs.priority0 = 5; bg2.regs.priority1 = 8;
bg3.regs.priority0 = 1; bg3.regs.priority1 = 3;
sprite.regs.priority0 = 2; sprite.regs.priority1 = 4; sprite.regs.priority2 = 7; sprite.regs.priority3 = 10;
}
break;
case 2:
bg1.regs.mode = Background::Mode::BPP4;
bg2.regs.mode = Background::Mode::BPP4;
bg3.regs.mode = Background::Mode::Inactive;
bg4.regs.mode = Background::Mode::Inactive;
bg1.regs.priority0 = 3; bg1.regs.priority1 = 7;
bg2.regs.priority0 = 1; bg2.regs.priority1 = 5;
sprite.regs.priority0 = 2; sprite.regs.priority1 = 4; sprite.regs.priority2 = 6; sprite.regs.priority3 = 8;
break;
case 3:
bg1.regs.mode = Background::Mode::BPP8;
bg2.regs.mode = Background::Mode::BPP4;
bg3.regs.mode = Background::Mode::Inactive;
bg4.regs.mode = Background::Mode::Inactive;
bg1.regs.priority0 = 3; bg1.regs.priority1 = 7;
bg2.regs.priority0 = 1; bg2.regs.priority1 = 5;
sprite.regs.priority0 = 2; sprite.regs.priority1 = 4; sprite.regs.priority2 = 6; sprite.regs.priority3 = 8;
break;
case 4:
bg1.regs.mode = Background::Mode::BPP8;
bg2.regs.mode = Background::Mode::BPP2;
bg3.regs.mode = Background::Mode::Inactive;
bg4.regs.mode = Background::Mode::Inactive;
bg1.regs.priority0 = 3; bg1.regs.priority1 = 7;
bg2.regs.priority0 = 1; bg2.regs.priority1 = 5;
sprite.regs.priority0 = 2; sprite.regs.priority1 = 4; sprite.regs.priority2 = 6; sprite.regs.priority3 = 8;
break;
case 5:
bg1.regs.mode = Background::Mode::BPP4;
bg2.regs.mode = Background::Mode::BPP2;
bg3.regs.mode = Background::Mode::Inactive;
bg4.regs.mode = Background::Mode::Inactive;
bg1.regs.priority0 = 3; bg1.regs.priority1 = 7;
bg2.regs.priority0 = 1; bg2.regs.priority1 = 5;
sprite.regs.priority0 = 2; sprite.regs.priority1 = 4; sprite.regs.priority2 = 6; sprite.regs.priority3 = 8;
break;
case 6:
bg1.regs.mode = Background::Mode::BPP4;
bg2.regs.mode = Background::Mode::Inactive;
bg3.regs.mode = Background::Mode::Inactive;
bg4.regs.mode = Background::Mode::Inactive;
bg1.regs.priority0 = 2; bg1.regs.priority1 = 5;
sprite.regs.priority0 = 1; sprite.regs.priority1 = 3; sprite.regs.priority2 = 4; sprite.regs.priority3 = 6;
break;
case 7:
if(regs.mode7_extbg == false) {
bg1.regs.mode = Background::Mode::Mode7;
bg2.regs.mode = Background::Mode::Inactive;
bg3.regs.mode = Background::Mode::Inactive;
bg4.regs.mode = Background::Mode::Inactive;
bg1.regs.priority0 = 2; bg1.regs.priority1 = 2;
sprite.regs.priority0 = 1; sprite.regs.priority1 = 3; sprite.regs.priority2 = 4; sprite.regs.priority3 = 5;
} else {
bg1.regs.mode = Background::Mode::Mode7;
bg2.regs.mode = Background::Mode::Mode7;
bg3.regs.mode = Background::Mode::Inactive;
bg4.regs.mode = Background::Mode::Inactive;
bg1.regs.priority0 = 3; bg1.regs.priority1 = 3;
bg2.regs.priority0 = 1; bg2.regs.priority1 = 5;
sprite.regs.priority0 = 2; sprite.regs.priority1 = 4; sprite.regs.priority2 = 6; sprite.regs.priority3 = 7;
}
break;
}
}
//INIDISP
auto PPU::mmio_w2100(uint8 data) -> void {
if(regs.display_disable && vcounter() == (!regs.overscan ? 225 : 240)) sprite.address_reset();
Update to v068r12 release. (there was no r11 release posted to the WIP thread) byuu says: This took ten hours of mind boggling insanity to pull off. It upgrades the S-PPU dot-based renderer to fetch one tile, and then output all of its pixels before fetching again. It sounds easy enough, but it's insanely difficult. I ended up taking one small shortcut, in that rather than fetch at -7, I fetch at the first instance where a tile is needed to plot to x=0. So if you have {-3 to +4 } as a tile, it fetches at -3. That won't work so well on hardware, if two BGs fetch at the same X offset, they won't have time. I have had no luck staggering the reads at BG1=-7, BG3=-5, etc. While I can shift and fetch just fine, what happens is that when a new tile is fetched in, that gives a new palette, priority, etc; and this ends up happening between two tiles which results in the right-most edges of the screen ending up with the wrong colors and such. Offset-per-tile is cheap as always. Although looking at it, I'm not sure how BG3 could pre-fetch, especially with the way one or two OPT modes can fetch two tiles. There's no magic in Hoffset caching yet, so the SMW1 pixel issue is still there. Mode 7 got a bugfix, it was off-by-one horizontally from the mosaic code. After re-designing the BG mosaic, I ended up needing a separate mosaic for Mode7, and in the process I fixed that bug. The obvious change is that the Chrono Trigger Mode7->Mode2 transition doesn't cause the pendulum to jump anymore. Windows were simplified just a tad. The range testing is shared for all modes now. Ironically, it's a bit slower, but I'll take less code over more speed for the accuracy core. Speaking of speed, because there's so much less calculations per pixel for BGs, performance for the entire emulator has gone up by 30% in the accuracy core. Pretty neat overall, I can maintain 60fps in all but, yeah you can guess can't you?
2010-09-04 03:36:03 +00:00
regs.display_disable = data & 0x80;
regs.display_brightness = data & 0x0f;
}
//OBSEL
auto PPU::mmio_w2101(uint8 data) -> void {
sprite.regs.base_size = (data >> 5) & 7;
sprite.regs.nameselect = (data >> 3) & 3;
sprite.regs.tiledata_addr = (data & 3) << 14;
}
//OAMADDL
auto PPU::mmio_w2102(uint8 data) -> void {
regs.oam_baseaddr = (regs.oam_baseaddr & 0x0200) | (data << 1);
sprite.address_reset();
}
//OAMADDH
auto PPU::mmio_w2103(uint8 data) -> void {
regs.oam_priority = data & 0x80;
regs.oam_baseaddr = ((data & 0x01) << 9) | (regs.oam_baseaddr & 0x01fe);
sprite.address_reset();
}
//OAMDATA
auto PPU::mmio_w2104(uint8 data) -> void {
bool latch = regs.oam_addr & 1;
uint10 addr = regs.oam_addr++;
if(regs.display_disable == false && vcounter() < (!regs.overscan ? 225 : 240)) addr = regs.oam_iaddr;
if(addr & 0x0200) addr &= 0x021f;
if(latch == 0) regs.oam_latchdata = data;
if(addr & 0x0200) {
Update to v085r08 release. byuu says: Changelog: - follow the Laevateinn topic to get most of it - also added NMI, IRQ step buttons to CPU debugger - also added trace masking + trace mask reset - also added memory export - cartridge loading is entirely folder-based now FitzRoy, I'll go ahead and make a second compromise with you for v086: I'll match the following: /path/to/SNES.sfc/*.sfc /path/to/NES.fc/*.prg, *.chr (split format) /path/to/NES.fc/*.fc (merged format) /path/to/GB.gb/*.gb /path/to/GBC.gbc/*.gbc Condition will be that there can only be one of each file. If there's more than one, it'll abort. That lets me name my ROMs as "Game.fc/Game.fc", and you can name yours as "Game.fc/cartridge.prg, cartridge.chr". Or whatever you want. We'll just go with that, see what fares out as the most popular, and then restrict it back to that method. The folder must have the .fc, etc extension though. That will be how we avoid false-positive folder matches. [Editor's note - the Laevateinn topic mentions these changes for v085r08: Added SMP/PPU breakpoints, SMP debugger, SMP stepping / tracing, memory editing on APU-bus / VRAM / OAM / CGRAM, save state menu, WRAM mirroring on breakpoints, protected MMIO memory regions (otherwise, viewing $002100 could crash your game.) Major missing components: - trace mask - trace mask clear / usage map clear - window geometry caching / sizing improvements - VRAM viewer - properties viewer - working memory export button The rest will most likely appear after v086 is released. ]
2012-02-12 05:35:40 +00:00
oam_write(addr, data);
} else if(latch == 1) {
Update to v085r08 release. byuu says: Changelog: - follow the Laevateinn topic to get most of it - also added NMI, IRQ step buttons to CPU debugger - also added trace masking + trace mask reset - also added memory export - cartridge loading is entirely folder-based now FitzRoy, I'll go ahead and make a second compromise with you for v086: I'll match the following: /path/to/SNES.sfc/*.sfc /path/to/NES.fc/*.prg, *.chr (split format) /path/to/NES.fc/*.fc (merged format) /path/to/GB.gb/*.gb /path/to/GBC.gbc/*.gbc Condition will be that there can only be one of each file. If there's more than one, it'll abort. That lets me name my ROMs as "Game.fc/Game.fc", and you can name yours as "Game.fc/cartridge.prg, cartridge.chr". Or whatever you want. We'll just go with that, see what fares out as the most popular, and then restrict it back to that method. The folder must have the .fc, etc extension though. That will be how we avoid false-positive folder matches. [Editor's note - the Laevateinn topic mentions these changes for v085r08: Added SMP/PPU breakpoints, SMP debugger, SMP stepping / tracing, memory editing on APU-bus / VRAM / OAM / CGRAM, save state menu, WRAM mirroring on breakpoints, protected MMIO memory regions (otherwise, viewing $002100 could crash your game.) Major missing components: - trace mask - trace mask clear / usage map clear - window geometry caching / sizing improvements - VRAM viewer - properties viewer - working memory export button The rest will most likely appear after v086 is released. ]
2012-02-12 05:35:40 +00:00
oam_write((addr & ~1) + 0, regs.oam_latchdata);
oam_write((addr & ~1) + 1, data);
}
sprite.set_first_sprite();
}
//BGMODE
auto PPU::mmio_w2105(uint8 data) -> void {
bg4.regs.tile_size = (data & 0x80);
bg3.regs.tile_size = (data & 0x40);
bg2.regs.tile_size = (data & 0x20);
bg1.regs.tile_size = (data & 0x10);
regs.bg3_priority = (data & 0x08);
regs.bgmode = (data & 0x07);
mmio_update_video_mode();
}
//MOSAIC
auto PPU::mmio_w2106(uint8 data) -> void {
unsigned mosaic_size = (data >> 4) & 15;
bg4.regs.mosaic = (data & 0x08 ? mosaic_size : 0);
bg3.regs.mosaic = (data & 0x04 ? mosaic_size : 0);
bg2.regs.mosaic = (data & 0x02 ? mosaic_size : 0);
bg1.regs.mosaic = (data & 0x01 ? mosaic_size : 0);
}
//BG1SC
auto PPU::mmio_w2107(uint8 data) -> void {
bg1.regs.screen_addr = (data & 0x7c) << 9;
bg1.regs.screen_size = data & 3;
}
//BG2SC
auto PPU::mmio_w2108(uint8 data) -> void {
bg2.regs.screen_addr = (data & 0x7c) << 9;
bg2.regs.screen_size = data & 3;
}
//BG3SC
auto PPU::mmio_w2109(uint8 data) -> void {
bg3.regs.screen_addr = (data & 0x7c) << 9;
bg3.regs.screen_size = data & 3;
}
//BG4SC
auto PPU::mmio_w210a(uint8 data) -> void {
bg4.regs.screen_addr = (data & 0x7c) << 9;
bg4.regs.screen_size = data & 3;
}
//BG12NBA
auto PPU::mmio_w210b(uint8 data) -> void {
bg1.regs.tiledata_addr = (data & 0x07) << 13;
bg2.regs.tiledata_addr = (data & 0x70) << 9;
}
//BG34NBA
auto PPU::mmio_w210c(uint8 data) -> void {
bg3.regs.tiledata_addr = (data & 0x07) << 13;
bg4.regs.tiledata_addr = (data & 0x70) << 9;
}
//BG1HOFS
auto PPU::mmio_w210d(uint8 data) -> void {
regs.mode7_hoffset = (data << 8) | regs.mode7_latchdata;
regs.mode7_latchdata = data;
bg1.regs.hoffset = (data << 8) | (regs.bgofs_latchdata & ~7) | ((bg1.regs.hoffset >> 8) & 7);
regs.bgofs_latchdata = data;
}
//BG1VOFS
auto PPU::mmio_w210e(uint8 data) -> void {
regs.mode7_voffset = (data << 8) | regs.mode7_latchdata;
regs.mode7_latchdata = data;
bg1.regs.voffset = (data << 8) | regs.bgofs_latchdata;
regs.bgofs_latchdata = data;
}
//BG2HOFS
auto PPU::mmio_w210f(uint8 data) -> void {
bg2.regs.hoffset = (data << 8) | (regs.bgofs_latchdata & ~7) | ((bg2.regs.hoffset >> 8) & 7);
regs.bgofs_latchdata = data;
}
//BG2VOFS
auto PPU::mmio_w2110(uint8 data) -> void {
bg2.regs.voffset = (data << 8) | regs.bgofs_latchdata;
regs.bgofs_latchdata = data;
}
//BG3HOFS
auto PPU::mmio_w2111(uint8 data) -> void {
bg3.regs.hoffset = (data << 8) | (regs.bgofs_latchdata & ~7) | ((bg3.regs.hoffset >> 8) & 7);
regs.bgofs_latchdata = data;
}
//BG3VOFS
auto PPU::mmio_w2112(uint8 data) -> void {
bg3.regs.voffset = (data << 8) | regs.bgofs_latchdata;
regs.bgofs_latchdata = data;
}
//BG4HOFS
auto PPU::mmio_w2113(uint8 data) -> void {
bg4.regs.hoffset = (data << 8) | (regs.bgofs_latchdata & ~7) | ((bg4.regs.hoffset >> 8) & 7);
regs.bgofs_latchdata = data;
}
//BG4VOFS
auto PPU::mmio_w2114(uint8 data) -> void {
bg4.regs.voffset = (data << 8) | regs.bgofs_latchdata;
regs.bgofs_latchdata = data;
}
//VMAIN
auto PPU::mmio_w2115(uint8 data) -> void {
regs.vram_incmode = data & 0x80;
regs.vram_mapping = (data >> 2) & 3;
switch(data & 3) {
case 0: regs.vram_incsize = 1; break;
case 1: regs.vram_incsize = 32; break;
case 2: regs.vram_incsize = 128; break;
case 3: regs.vram_incsize = 128; break;
}
}
//VMADDL
auto PPU::mmio_w2116(uint8 data) -> void {
regs.vram_addr &= 0xff00;
regs.vram_addr |= (data << 0);
uint16 addr = get_vram_address();
regs.vram_readbuffer = vram_read(addr + 0) << 0;
regs.vram_readbuffer |= vram_read(addr + 1) << 8;
}
//VMADDH
auto PPU::mmio_w2117(uint8 data) -> void {
regs.vram_addr &= 0x00ff;
regs.vram_addr |= (data << 8);
uint16 addr = get_vram_address();
regs.vram_readbuffer = vram_read(addr + 0) << 0;
regs.vram_readbuffer |= vram_read(addr + 1) << 8;
}
//VMDATAL
auto PPU::mmio_w2118(uint8 data) -> void {
uint16 addr = get_vram_address() + 0;
vram_write(addr, data);
if(regs.vram_incmode == 0) regs.vram_addr += regs.vram_incsize;
}
//VMDATAH
auto PPU::mmio_w2119(uint8 data) -> void {
uint16 addr = get_vram_address() + 1;
vram_write(addr, data);
if(regs.vram_incmode == 1) regs.vram_addr += regs.vram_incsize;
}
//M7SEL
auto PPU::mmio_w211a(uint8 data) -> void {
regs.mode7_repeat = (data >> 6) & 3;
regs.mode7_vflip = data & 0x02;
regs.mode7_hflip = data & 0x01;
}
//M7A
auto PPU::mmio_w211b(uint8 data) -> void {
regs.m7a = (data << 8) | regs.mode7_latchdata;
regs.mode7_latchdata = data;
}
//M7B
auto PPU::mmio_w211c(uint8 data) -> void {
regs.m7b = (data << 8) | regs.mode7_latchdata;
regs.mode7_latchdata = data;
}
//M7C
auto PPU::mmio_w211d(uint8 data) -> void {
regs.m7c = (data << 8) | regs.mode7_latchdata;
regs.mode7_latchdata = data;
}
//M7D
auto PPU::mmio_w211e(uint8 data) -> void {
regs.m7d = (data << 8) | regs.mode7_latchdata;
regs.mode7_latchdata = data;
}
//M7X
auto PPU::mmio_w211f(uint8 data) -> void {
regs.m7x = (data << 8) | regs.mode7_latchdata;
regs.mode7_latchdata = data;
}
//M7Y
auto PPU::mmio_w2120(uint8 data) -> void {
regs.m7y = (data << 8) | regs.mode7_latchdata;
regs.mode7_latchdata = data;
}
//CGADD
auto PPU::mmio_w2121(uint8 data) -> void {
regs.cgram_addr = data << 1;
}
//CGDATA
auto PPU::mmio_w2122(uint8 data) -> void {
bool latch = regs.cgram_addr & 1;
uint9 addr = regs.cgram_addr++;
if(regs.display_disable == false
&& vcounter() > 0 && vcounter() < (!regs.overscan ? 225 : 240)
&& hcounter() >= 88 && hcounter() < 1096
) addr = regs.cgram_iaddr;
if(latch == 0) {
regs.cgram_latchdata = data;
} else {
Update to v085r08 release. byuu says: Changelog: - follow the Laevateinn topic to get most of it - also added NMI, IRQ step buttons to CPU debugger - also added trace masking + trace mask reset - also added memory export - cartridge loading is entirely folder-based now FitzRoy, I'll go ahead and make a second compromise with you for v086: I'll match the following: /path/to/SNES.sfc/*.sfc /path/to/NES.fc/*.prg, *.chr (split format) /path/to/NES.fc/*.fc (merged format) /path/to/GB.gb/*.gb /path/to/GBC.gbc/*.gbc Condition will be that there can only be one of each file. If there's more than one, it'll abort. That lets me name my ROMs as "Game.fc/Game.fc", and you can name yours as "Game.fc/cartridge.prg, cartridge.chr". Or whatever you want. We'll just go with that, see what fares out as the most popular, and then restrict it back to that method. The folder must have the .fc, etc extension though. That will be how we avoid false-positive folder matches. [Editor's note - the Laevateinn topic mentions these changes for v085r08: Added SMP/PPU breakpoints, SMP debugger, SMP stepping / tracing, memory editing on APU-bus / VRAM / OAM / CGRAM, save state menu, WRAM mirroring on breakpoints, protected MMIO memory regions (otherwise, viewing $002100 could crash your game.) Major missing components: - trace mask - trace mask clear / usage map clear - window geometry caching / sizing improvements - VRAM viewer - properties viewer - working memory export button The rest will most likely appear after v086 is released. ]
2012-02-12 05:35:40 +00:00
cgram_write((addr & ~1) + 0, regs.cgram_latchdata);
cgram_write((addr & ~1) + 1, data & 0x7f);
}
}
//W12SEL
auto PPU::mmio_w2123(uint8 data) -> void {
window.regs.bg2_two_enable = data & 0x80;
window.regs.bg2_two_invert = data & 0x40;
window.regs.bg2_one_enable = data & 0x20;
window.regs.bg2_one_invert = data & 0x10;
window.regs.bg1_two_enable = data & 0x08;
window.regs.bg1_two_invert = data & 0x04;
window.regs.bg1_one_enable = data & 0x02;
window.regs.bg1_one_invert = data & 0x01;
}
//W34SEL
auto PPU::mmio_w2124(uint8 data) -> void {
window.regs.bg4_two_enable = data & 0x80;
window.regs.bg4_two_invert = data & 0x40;
window.regs.bg4_one_enable = data & 0x20;
window.regs.bg4_one_invert = data & 0x10;
window.regs.bg3_two_enable = data & 0x08;
window.regs.bg3_two_invert = data & 0x04;
window.regs.bg3_one_enable = data & 0x02;
window.regs.bg3_one_invert = data & 0x01;
}
//WOBJSEL
auto PPU::mmio_w2125(uint8 data) -> void {
window.regs.col_two_enable = data & 0x80;
window.regs.col_two_invert = data & 0x40;
window.regs.col_one_enable = data & 0x20;
window.regs.col_one_invert = data & 0x10;
window.regs.oam_two_enable = data & 0x08;
window.regs.oam_two_invert = data & 0x04;
window.regs.oam_one_enable = data & 0x02;
window.regs.oam_one_invert = data & 0x01;
}
//WH0
auto PPU::mmio_w2126(uint8 data) -> void {
window.regs.one_left = data;
}
//WH1
auto PPU::mmio_w2127(uint8 data) -> void {
window.regs.one_right = data;
}
//WH2
auto PPU::mmio_w2128(uint8 data) -> void {
window.regs.two_left = data;
}
//WH3
auto PPU::mmio_w2129(uint8 data) -> void {
window.regs.two_right = data;
}
//WBGLOG
auto PPU::mmio_w212a(uint8 data) -> void {
window.regs.bg4_mask = (data >> 6) & 3;
window.regs.bg3_mask = (data >> 4) & 3;
window.regs.bg2_mask = (data >> 2) & 3;
window.regs.bg1_mask = (data >> 0) & 3;
}
//WOBJLOG
auto PPU::mmio_w212b(uint8 data) -> void {
window.regs.col_mask = (data >> 2) & 3;
window.regs.oam_mask = (data >> 0) & 3;
}
//TM
auto PPU::mmio_w212c(uint8 data) -> void {
sprite.regs.main_enable = data & 0x10;
Update to v068r12 release. (there was no r11 release posted to the WIP thread) byuu says: This took ten hours of mind boggling insanity to pull off. It upgrades the S-PPU dot-based renderer to fetch one tile, and then output all of its pixels before fetching again. It sounds easy enough, but it's insanely difficult. I ended up taking one small shortcut, in that rather than fetch at -7, I fetch at the first instance where a tile is needed to plot to x=0. So if you have {-3 to +4 } as a tile, it fetches at -3. That won't work so well on hardware, if two BGs fetch at the same X offset, they won't have time. I have had no luck staggering the reads at BG1=-7, BG3=-5, etc. While I can shift and fetch just fine, what happens is that when a new tile is fetched in, that gives a new palette, priority, etc; and this ends up happening between two tiles which results in the right-most edges of the screen ending up with the wrong colors and such. Offset-per-tile is cheap as always. Although looking at it, I'm not sure how BG3 could pre-fetch, especially with the way one or two OPT modes can fetch two tiles. There's no magic in Hoffset caching yet, so the SMW1 pixel issue is still there. Mode 7 got a bugfix, it was off-by-one horizontally from the mosaic code. After re-designing the BG mosaic, I ended up needing a separate mosaic for Mode7, and in the process I fixed that bug. The obvious change is that the Chrono Trigger Mode7->Mode2 transition doesn't cause the pendulum to jump anymore. Windows were simplified just a tad. The range testing is shared for all modes now. Ironically, it's a bit slower, but I'll take less code over more speed for the accuracy core. Speaking of speed, because there's so much less calculations per pixel for BGs, performance for the entire emulator has gone up by 30% in the accuracy core. Pretty neat overall, I can maintain 60fps in all but, yeah you can guess can't you?
2010-09-04 03:36:03 +00:00
bg4.regs.main_enable = data & 0x08;
bg3.regs.main_enable = data & 0x04;
bg2.regs.main_enable = data & 0x02;
bg1.regs.main_enable = data & 0x01;
}
//TS
auto PPU::mmio_w212d(uint8 data) -> void {
sprite.regs.sub_enable = data & 0x10;
Update to v068r12 release. (there was no r11 release posted to the WIP thread) byuu says: This took ten hours of mind boggling insanity to pull off. It upgrades the S-PPU dot-based renderer to fetch one tile, and then output all of its pixels before fetching again. It sounds easy enough, but it's insanely difficult. I ended up taking one small shortcut, in that rather than fetch at -7, I fetch at the first instance where a tile is needed to plot to x=0. So if you have {-3 to +4 } as a tile, it fetches at -3. That won't work so well on hardware, if two BGs fetch at the same X offset, they won't have time. I have had no luck staggering the reads at BG1=-7, BG3=-5, etc. While I can shift and fetch just fine, what happens is that when a new tile is fetched in, that gives a new palette, priority, etc; and this ends up happening between two tiles which results in the right-most edges of the screen ending up with the wrong colors and such. Offset-per-tile is cheap as always. Although looking at it, I'm not sure how BG3 could pre-fetch, especially with the way one or two OPT modes can fetch two tiles. There's no magic in Hoffset caching yet, so the SMW1 pixel issue is still there. Mode 7 got a bugfix, it was off-by-one horizontally from the mosaic code. After re-designing the BG mosaic, I ended up needing a separate mosaic for Mode7, and in the process I fixed that bug. The obvious change is that the Chrono Trigger Mode7->Mode2 transition doesn't cause the pendulum to jump anymore. Windows were simplified just a tad. The range testing is shared for all modes now. Ironically, it's a bit slower, but I'll take less code over more speed for the accuracy core. Speaking of speed, because there's so much less calculations per pixel for BGs, performance for the entire emulator has gone up by 30% in the accuracy core. Pretty neat overall, I can maintain 60fps in all but, yeah you can guess can't you?
2010-09-04 03:36:03 +00:00
bg4.regs.sub_enable = data & 0x08;
bg3.regs.sub_enable = data & 0x04;
bg2.regs.sub_enable = data & 0x02;
bg1.regs.sub_enable = data & 0x01;
}
//TMW
auto PPU::mmio_w212e(uint8 data) -> void {
window.regs.oam_main_enable = data & 0x10;
window.regs.bg4_main_enable = data & 0x08;
window.regs.bg3_main_enable = data & 0x04;
window.regs.bg2_main_enable = data & 0x02;
window.regs.bg1_main_enable = data & 0x01;
}
//TSW
auto PPU::mmio_w212f(uint8 data) -> void {
window.regs.oam_sub_enable = data & 0x10;
window.regs.bg4_sub_enable = data & 0x08;
window.regs.bg3_sub_enable = data & 0x04;
window.regs.bg2_sub_enable = data & 0x02;
window.regs.bg1_sub_enable = data & 0x01;
}
//CGWSEL
auto PPU::mmio_w2130(uint8 data) -> void {
window.regs.col_main_mask = (data >> 6) & 3;
window.regs.col_sub_mask = (data >> 4) & 3;
screen.regs.addsub_mode = data & 0x02;
screen.regs.direct_color = data & 0x01;
}
//CGADDSUB
auto PPU::mmio_w2131(uint8 data) -> void {
screen.regs.color_mode = data & 0x80;
screen.regs.color_halve = data & 0x40;
screen.regs.back_color_enable = data & 0x20;
screen.regs.oam_color_enable = data & 0x10;
screen.regs.bg4_color_enable = data & 0x08;
screen.regs.bg3_color_enable = data & 0x04;
screen.regs.bg2_color_enable = data & 0x02;
screen.regs.bg1_color_enable = data & 0x01;
}
//COLDATA
auto PPU::mmio_w2132(uint8 data) -> void {
if(data & 0x80) screen.regs.color_b = data & 0x1f;
if(data & 0x40) screen.regs.color_g = data & 0x1f;
if(data & 0x20) screen.regs.color_r = data & 0x1f;
}
//SETINI
auto PPU::mmio_w2133(uint8 data) -> void {
regs.mode7_extbg = data & 0x40;
regs.pseudo_hires = data & 0x08;
regs.overscan = data & 0x04;
sprite.regs.interlace = data & 0x02;
regs.interlace = data & 0x01;
mmio_update_video_mode();
}
//MPYL
auto PPU::mmio_r2134() -> uint8 {
unsigned result = ((int16)regs.m7a * (int8)(regs.m7b >> 8));
regs.ppu1_mdr = (result >> 0);
return regs.ppu1_mdr;
}
//MPYM
auto PPU::mmio_r2135() -> uint8 {
unsigned result = ((int16)regs.m7a * (int8)(regs.m7b >> 8));
regs.ppu1_mdr = (result >> 8);
return regs.ppu1_mdr;
}
//MPYH
auto PPU::mmio_r2136() -> uint8 {
unsigned result = ((int16)regs.m7a * (int8)(regs.m7b >> 8));
regs.ppu1_mdr = (result >> 16);
return regs.ppu1_mdr;
}
//SLHV
auto PPU::mmio_r2137() -> uint8 {
if(cpu.pio() & 0x80) latch_counters();
return cpu.regs.mdr;
}
//OAMDATAREAD
auto PPU::mmio_r2138() -> uint8 {
uint10 addr = regs.oam_addr++;
if(regs.display_disable == false && vcounter() < (!regs.overscan ? 225 : 240)) addr = regs.oam_iaddr;
if(addr & 0x0200) addr &= 0x021f;
Update to v085r08 release. byuu says: Changelog: - follow the Laevateinn topic to get most of it - also added NMI, IRQ step buttons to CPU debugger - also added trace masking + trace mask reset - also added memory export - cartridge loading is entirely folder-based now FitzRoy, I'll go ahead and make a second compromise with you for v086: I'll match the following: /path/to/SNES.sfc/*.sfc /path/to/NES.fc/*.prg, *.chr (split format) /path/to/NES.fc/*.fc (merged format) /path/to/GB.gb/*.gb /path/to/GBC.gbc/*.gbc Condition will be that there can only be one of each file. If there's more than one, it'll abort. That lets me name my ROMs as "Game.fc/Game.fc", and you can name yours as "Game.fc/cartridge.prg, cartridge.chr". Or whatever you want. We'll just go with that, see what fares out as the most popular, and then restrict it back to that method. The folder must have the .fc, etc extension though. That will be how we avoid false-positive folder matches. [Editor's note - the Laevateinn topic mentions these changes for v085r08: Added SMP/PPU breakpoints, SMP debugger, SMP stepping / tracing, memory editing on APU-bus / VRAM / OAM / CGRAM, save state menu, WRAM mirroring on breakpoints, protected MMIO memory regions (otherwise, viewing $002100 could crash your game.) Major missing components: - trace mask - trace mask clear / usage map clear - window geometry caching / sizing improvements - VRAM viewer - properties viewer - working memory export button The rest will most likely appear after v086 is released. ]
2012-02-12 05:35:40 +00:00
regs.ppu1_mdr = oam_read(addr);
sprite.set_first_sprite();
return regs.ppu1_mdr;
}
//VMDATALREAD
auto PPU::mmio_r2139() -> uint8 {
uint16 addr = get_vram_address() + 0;
regs.ppu1_mdr = regs.vram_readbuffer >> 0;
if(regs.vram_incmode == 0) {
addr &= ~1;
regs.vram_readbuffer = vram_read(addr + 0) << 0;
regs.vram_readbuffer |= vram_read(addr + 1) << 8;
regs.vram_addr += regs.vram_incsize;
}
return regs.ppu1_mdr;
}
//VMDATAHREAD
auto PPU::mmio_r213a() -> uint8 {
uint16 addr = get_vram_address() + 1;
regs.ppu1_mdr = regs.vram_readbuffer >> 8;
if(regs.vram_incmode == 1) {
addr &= ~1;
regs.vram_readbuffer = vram_read(addr + 0) << 0;
regs.vram_readbuffer |= vram_read(addr + 1) << 8;
regs.vram_addr += regs.vram_incsize;
}
return regs.ppu1_mdr;
}
//CGDATAREAD
auto PPU::mmio_r213b() -> uint8 {
bool latch = regs.cgram_addr & 1;
uint9 addr = regs.cgram_addr++;
if(regs.display_disable == false
&& vcounter() > 0 && vcounter() < (!regs.overscan ? 225 : 240)
&& hcounter() >= 88 && hcounter() < 1096
) addr = regs.cgram_iaddr;
if(latch == 0) {
Update to v085r08 release. byuu says: Changelog: - follow the Laevateinn topic to get most of it - also added NMI, IRQ step buttons to CPU debugger - also added trace masking + trace mask reset - also added memory export - cartridge loading is entirely folder-based now FitzRoy, I'll go ahead and make a second compromise with you for v086: I'll match the following: /path/to/SNES.sfc/*.sfc /path/to/NES.fc/*.prg, *.chr (split format) /path/to/NES.fc/*.fc (merged format) /path/to/GB.gb/*.gb /path/to/GBC.gbc/*.gbc Condition will be that there can only be one of each file. If there's more than one, it'll abort. That lets me name my ROMs as "Game.fc/Game.fc", and you can name yours as "Game.fc/cartridge.prg, cartridge.chr". Or whatever you want. We'll just go with that, see what fares out as the most popular, and then restrict it back to that method. The folder must have the .fc, etc extension though. That will be how we avoid false-positive folder matches. [Editor's note - the Laevateinn topic mentions these changes for v085r08: Added SMP/PPU breakpoints, SMP debugger, SMP stepping / tracing, memory editing on APU-bus / VRAM / OAM / CGRAM, save state menu, WRAM mirroring on breakpoints, protected MMIO memory regions (otherwise, viewing $002100 could crash your game.) Major missing components: - trace mask - trace mask clear / usage map clear - window geometry caching / sizing improvements - VRAM viewer - properties viewer - working memory export button The rest will most likely appear after v086 is released. ]
2012-02-12 05:35:40 +00:00
regs.ppu2_mdr = cgram_read(addr);
} else {
regs.ppu2_mdr &= 0x80;
Update to v085r08 release. byuu says: Changelog: - follow the Laevateinn topic to get most of it - also added NMI, IRQ step buttons to CPU debugger - also added trace masking + trace mask reset - also added memory export - cartridge loading is entirely folder-based now FitzRoy, I'll go ahead and make a second compromise with you for v086: I'll match the following: /path/to/SNES.sfc/*.sfc /path/to/NES.fc/*.prg, *.chr (split format) /path/to/NES.fc/*.fc (merged format) /path/to/GB.gb/*.gb /path/to/GBC.gbc/*.gbc Condition will be that there can only be one of each file. If there's more than one, it'll abort. That lets me name my ROMs as "Game.fc/Game.fc", and you can name yours as "Game.fc/cartridge.prg, cartridge.chr". Or whatever you want. We'll just go with that, see what fares out as the most popular, and then restrict it back to that method. The folder must have the .fc, etc extension though. That will be how we avoid false-positive folder matches. [Editor's note - the Laevateinn topic mentions these changes for v085r08: Added SMP/PPU breakpoints, SMP debugger, SMP stepping / tracing, memory editing on APU-bus / VRAM / OAM / CGRAM, save state menu, WRAM mirroring on breakpoints, protected MMIO memory regions (otherwise, viewing $002100 could crash your game.) Major missing components: - trace mask - trace mask clear / usage map clear - window geometry caching / sizing improvements - VRAM viewer - properties viewer - working memory export button The rest will most likely appear after v086 is released. ]
2012-02-12 05:35:40 +00:00
regs.ppu2_mdr |= cgram_read(addr);
}
return regs.ppu2_mdr;
}
//OPHCT
auto PPU::mmio_r213c() -> uint8 {
if(regs.latch_hcounter == 0) {
regs.ppu2_mdr = (regs.hcounter >> 0);
} else {
regs.ppu2_mdr &= 0xfe;
regs.ppu2_mdr |= (regs.hcounter >> 8) & 1;
}
regs.latch_hcounter ^= 1;
return regs.ppu2_mdr;
}
//OPVCT
auto PPU::mmio_r213d() -> uint8 {
if(regs.latch_vcounter == 0) {
regs.ppu2_mdr = (regs.vcounter >> 0);
} else {
regs.ppu2_mdr &= 0xfe;
regs.ppu2_mdr |= (regs.vcounter >> 8) & 1;
}
regs.latch_vcounter ^= 1;
return regs.ppu2_mdr;
}
//STAT77
auto PPU::mmio_r213e() -> uint8 {
regs.ppu1_mdr &= 0x10;
regs.ppu1_mdr |= sprite.regs.time_over << 7;
regs.ppu1_mdr |= sprite.regs.range_over << 6;
regs.ppu1_mdr |= ppu1_version & 0x0f;
return regs.ppu1_mdr;
}
//STAT78
auto PPU::mmio_r213f() -> uint8 {
regs.latch_hcounter = 0;
regs.latch_vcounter = 0;
regs.ppu2_mdr &= 0x20;
regs.ppu2_mdr |= field() << 7;
if((cpu.pio() & 0x80) == 0) {
regs.ppu2_mdr |= 0x40;
} else if(regs.counters_latched) {
regs.ppu2_mdr |= 0x40;
regs.counters_latched = false;
}
regs.ppu2_mdr |= (system.region() == System::Region::NTSC ? 0 : 1) << 4;
regs.ppu2_mdr |= ppu2_version & 0x0f;
return regs.ppu2_mdr;
}
auto PPU::mmio_reset() -> void {
regs.ppu1_mdr = random(0xff);
regs.ppu2_mdr = random(0xff);
regs.vram_readbuffer = random(0x0000);
regs.oam_latchdata = random(0x00);
regs.cgram_latchdata = random(0x00);
regs.bgofs_latchdata = random(0x00);
regs.mode7_latchdata = random(0x00);
regs.counters_latched = false;
regs.latch_hcounter = 0;
regs.latch_vcounter = 0;
regs.oam_iaddr = 0x0000;
regs.cgram_iaddr = 0x00;
//$2100 INIDISP
Update to v068r12 release. (there was no r11 release posted to the WIP thread) byuu says: This took ten hours of mind boggling insanity to pull off. It upgrades the S-PPU dot-based renderer to fetch one tile, and then output all of its pixels before fetching again. It sounds easy enough, but it's insanely difficult. I ended up taking one small shortcut, in that rather than fetch at -7, I fetch at the first instance where a tile is needed to plot to x=0. So if you have {-3 to +4 } as a tile, it fetches at -3. That won't work so well on hardware, if two BGs fetch at the same X offset, they won't have time. I have had no luck staggering the reads at BG1=-7, BG3=-5, etc. While I can shift and fetch just fine, what happens is that when a new tile is fetched in, that gives a new palette, priority, etc; and this ends up happening between two tiles which results in the right-most edges of the screen ending up with the wrong colors and such. Offset-per-tile is cheap as always. Although looking at it, I'm not sure how BG3 could pre-fetch, especially with the way one or two OPT modes can fetch two tiles. There's no magic in Hoffset caching yet, so the SMW1 pixel issue is still there. Mode 7 got a bugfix, it was off-by-one horizontally from the mosaic code. After re-designing the BG mosaic, I ended up needing a separate mosaic for Mode7, and in the process I fixed that bug. The obvious change is that the Chrono Trigger Mode7->Mode2 transition doesn't cause the pendulum to jump anymore. Windows were simplified just a tad. The range testing is shared for all modes now. Ironically, it's a bit slower, but I'll take less code over more speed for the accuracy core. Speaking of speed, because there's so much less calculations per pixel for BGs, performance for the entire emulator has gone up by 30% in the accuracy core. Pretty neat overall, I can maintain 60fps in all but, yeah you can guess can't you?
2010-09-04 03:36:03 +00:00
regs.display_disable = true;
regs.display_brightness = 0;
//$2102 OAMADDL
//$2103 OAMADDH
regs.oam_baseaddr = random(0x0000);
regs.oam_addr = random(0x0000);
regs.oam_priority = random(false);
//$2105 BGMODE
regs.bg3_priority = false;
regs.bgmode = 0;
//$210d BG1HOFS
regs.mode7_hoffset = random(0x0000);
//$210e BG1VOFS
regs.mode7_voffset = random(0x0000);
//$2115 VMAIN
regs.vram_incmode = random(1);
regs.vram_mapping = random(0);
regs.vram_incsize = 1;
//$2116 VMADDL
//$2117 VMADDH
regs.vram_addr = random(0x0000);
//$211a M7SEL
regs.mode7_repeat = random(0);
regs.mode7_vflip = random(false);
regs.mode7_hflip = random(false);
//$211b M7A
regs.m7a = random(0x0000);
//$211c M7B
regs.m7b = random(0x0000);
//$211d M7C
regs.m7c = random(0x0000);
//$211e M7D
regs.m7d = random(0x0000);
//$211f M7X
regs.m7x = random(0x0000);
//$2120 M7Y
regs.m7y = random(0x0000);
//$2121 CGADD
regs.cgram_addr = random(0x0000);
//$2133 SETINI
regs.mode7_extbg = random(false);
regs.pseudo_hires = random(false);
regs.overscan = false;
regs.interlace = false;
//$213c OPHCT
regs.hcounter = 0;
//$213d OPVCT
regs.vcounter = 0;
}
auto PPU::mmio_read(uint addr) -> uint8 {
cpu.synchronizePPU();
switch(addr & 0xffff) {
case 0x2104:
case 0x2105:
case 0x2106:
case 0x2108:
case 0x2109:
case 0x210a:
case 0x2114:
case 0x2115:
case 0x2116:
case 0x2118:
case 0x2119:
case 0x211a:
case 0x2124:
case 0x2125:
case 0x2126:
case 0x2128:
case 0x2129:
case 0x212a: return regs.ppu1_mdr;
case 0x2134: return mmio_r2134(); //MPYL
case 0x2135: return mmio_r2135(); //MPYM
case 0x2136: return mmio_r2136(); //MYPH
case 0x2137: return mmio_r2137(); //SLHV
case 0x2138: return mmio_r2138(); //OAMDATAREAD
case 0x2139: return mmio_r2139(); //VMDATALREAD
case 0x213a: return mmio_r213a(); //VMDATAHREAD
case 0x213b: return mmio_r213b(); //CGDATAREAD
case 0x213c: return mmio_r213c(); //OPHCT
case 0x213d: return mmio_r213d(); //OPVCT
case 0x213e: return mmio_r213e(); //STAT77
case 0x213f: return mmio_r213f(); //STAT78
}
return cpu.regs.mdr;
}
auto PPU::mmio_write(uint addr, uint8 data) -> void {
cpu.synchronizePPU();
switch(addr & 0xffff) {
case 0x2100: return mmio_w2100(data); //INIDISP
case 0x2101: return mmio_w2101(data); //OBSEL
case 0x2102: return mmio_w2102(data); //OAMADDL
case 0x2103: return mmio_w2103(data); //OAMADDH
case 0x2104: return mmio_w2104(data); //OAMDATA
case 0x2105: return mmio_w2105(data); //BGMODE
case 0x2106: return mmio_w2106(data); //MOSAIC
case 0x2107: return mmio_w2107(data); //BG1SC
case 0x2108: return mmio_w2108(data); //BG2SC
case 0x2109: return mmio_w2109(data); //BG3SC
case 0x210a: return mmio_w210a(data); //BG4SC
case 0x210b: return mmio_w210b(data); //BG12NBA
case 0x210c: return mmio_w210c(data); //BG34NBA
case 0x210d: return mmio_w210d(data); //BG1HOFS
case 0x210e: return mmio_w210e(data); //BG1VOFS
case 0x210f: return mmio_w210f(data); //BG2HOFS
case 0x2110: return mmio_w2110(data); //BG2VOFS
case 0x2111: return mmio_w2111(data); //BG3HOFS
case 0x2112: return mmio_w2112(data); //BG3VOFS
case 0x2113: return mmio_w2113(data); //BG4HOFS
case 0x2114: return mmio_w2114(data); //BG4VOFS
case 0x2115: return mmio_w2115(data); //VMAIN
case 0x2116: return mmio_w2116(data); //VMADDL
case 0x2117: return mmio_w2117(data); //VMADDH
case 0x2118: return mmio_w2118(data); //VMDATAL
case 0x2119: return mmio_w2119(data); //VMDATAH
case 0x211a: return mmio_w211a(data); //M7SEL
case 0x211b: return mmio_w211b(data); //M7A
case 0x211c: return mmio_w211c(data); //M7B
case 0x211d: return mmio_w211d(data); //M7C
case 0x211e: return mmio_w211e(data); //M7D
case 0x211f: return mmio_w211f(data); //M7X
case 0x2120: return mmio_w2120(data); //M7Y
case 0x2121: return mmio_w2121(data); //CGADD
case 0x2122: return mmio_w2122(data); //CGDATA
case 0x2123: return mmio_w2123(data); //W12SEL
case 0x2124: return mmio_w2124(data); //W34SEL
case 0x2125: return mmio_w2125(data); //WOBJSEL
case 0x2126: return mmio_w2126(data); //WH0
case 0x2127: return mmio_w2127(data); //WH1
case 0x2128: return mmio_w2128(data); //WH2
case 0x2129: return mmio_w2129(data); //WH3
case 0x212a: return mmio_w212a(data); //WBGLOG
case 0x212b: return mmio_w212b(data); //WOBJLOG
case 0x212c: return mmio_w212c(data); //TM
case 0x212d: return mmio_w212d(data); //TS
case 0x212e: return mmio_w212e(data); //TMW
case 0x212f: return mmio_w212f(data); //TSW
case 0x2130: return mmio_w2130(data); //CGWSEL
case 0x2131: return mmio_w2131(data); //CGADDSUB
case 0x2132: return mmio_w2132(data); //COLDATA
case 0x2133: return mmio_w2133(data); //SETINI
}
}