bsnes/sfc/smp/memory.cpp
Tim Allen 423a6c6bf8 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-09 17:05:58 +11:00

206 lines
4.6 KiB
C++

#ifdef SMP_CPP
alwaysinline uint8 SMP::ram_read(uint16 addr) {
if(addr >= 0xffc0 && status.iplrom_enable) return iplrom[addr & 0x3f];
if(status.ram_disable) return 0x5a; //0xff on mini-SNES
return apuram[addr];
}
alwaysinline void SMP::ram_write(uint16 addr, uint8 data) {
//writes to $ffc0-$ffff always go to apuram, even if the iplrom is enabled
if(status.ram_writable && !status.ram_disable) apuram[addr] = data;
}
uint8 SMP::port_read(uint2 port) const {
return apuram[0xf4 + port];
}
void SMP::port_write(uint2 port, uint8 data) {
apuram[0xf4 + port] = data;
}
uint8 SMP::op_busread(uint16 addr) {
unsigned result;
switch(addr) {
case 0xf0: //TEST -- write-only register
return 0x00;
case 0xf1: //CONTROL -- write-only register
return 0x00;
case 0xf2: //DSPADDR
return status.dsp_addr;
case 0xf3: //DSPDATA
//0x80-0xff are read-only mirrors of 0x00-0x7f
return dsp.read(status.dsp_addr & 0x7f);
case 0xf4: //CPUIO0
case 0xf5: //CPUIO1
case 0xf6: //CPUIO2
case 0xf7: //CPUIO3
synchronize_cpu();
return cpu.port_read(addr);
case 0xf8: //RAM0
return status.ram00f8;
case 0xf9: //RAM1
return status.ram00f9;
case 0xfa: //T0TARGET
case 0xfb: //T1TARGET
case 0xfc: //T2TARGET -- write-only registers
return 0x00;
case 0xfd: //T0OUT -- 4-bit counter value
result = timer0.stage3_ticks;
timer0.stage3_ticks = 0;
return result;
case 0xfe: //T1OUT -- 4-bit counter value
result = timer1.stage3_ticks;
timer1.stage3_ticks = 0;
return result;
case 0xff: //T2OUT -- 4-bit counter value
result = timer2.stage3_ticks;
timer2.stage3_ticks = 0;
return result;
}
return ram_read(addr);
}
void SMP::op_buswrite(uint16 addr, uint8 data) {
switch(addr) {
case 0xf0: //TEST
if(regs.p.p) break; //writes only valid when P flag is clear
status.clock_speed = (data >> 6) & 3;
status.timer_speed = (data >> 4) & 3;
status.timers_enable = data & 0x08;
status.ram_disable = data & 0x04;
status.ram_writable = data & 0x02;
status.timers_disable = data & 0x01;
status.timer_step = (1 << status.clock_speed) + (2 << status.timer_speed);
timer0.synchronize_stage1();
timer1.synchronize_stage1();
timer2.synchronize_stage1();
break;
case 0xf1: //CONTROL
status.iplrom_enable = data & 0x80;
if(data & 0x30) {
//one-time clearing of APU port read registers,
//emulated by simulating CPU writes of 0x00
synchronize_cpu();
if(data & 0x20) {
cpu.port_write(2, 0x00);
cpu.port_write(3, 0x00);
}
if(data & 0x10) {
cpu.port_write(0, 0x00);
cpu.port_write(1, 0x00);
}
}
//0->1 transistion resets timers
if(timer2.enable == false && (data & 0x04)) {
timer2.stage2_ticks = 0;
timer2.stage3_ticks = 0;
}
timer2.enable = data & 0x04;
if(timer1.enable == false && (data & 0x02)) {
timer1.stage2_ticks = 0;
timer1.stage3_ticks = 0;
}
timer1.enable = data & 0x02;
if(timer0.enable == false && (data & 0x01)) {
timer0.stage2_ticks = 0;
timer0.stage3_ticks = 0;
}
timer0.enable = data & 0x01;
break;
case 0xf2: //DSPADDR
status.dsp_addr = data;
break;
case 0xf3: //DSPDATA
if(status.dsp_addr & 0x80) break; //0x80-0xff are read-only mirrors of 0x00-0x7f
dsp.write(status.dsp_addr & 0x7f, data);
break;
case 0xf4: //CPUIO0
case 0xf5: //CPUIO1
case 0xf6: //CPUIO2
case 0xf7: //CPUIO3
synchronize_cpu();
port_write(addr, data);
break;
case 0xf8: //RAM0
status.ram00f8 = data;
break;
case 0xf9: //RAM1
status.ram00f9 = data;
break;
case 0xfa: //T0TARGET
timer0.target = data;
break;
case 0xfb: //T1TARGET
timer1.target = data;
break;
case 0xfc: //T2TARGET
timer2.target = data;
break;
case 0xfd: //T0OUT
case 0xfe: //T1OUT
case 0xff: //T2OUT -- read-only registers
break;
}
ram_write(addr, data); //all writes, even to MMIO registers, appear on bus
}
void SMP::op_io() {
add_clocks(24);
cycle_edge();
}
uint8 SMP::op_read(uint16 addr) {
add_clocks(12);
uint8 data = op_busread(addr);
add_clocks(12);
cycle_edge();
debugger.op_read(addr, data);
return data;
}
void SMP::op_write(uint16 addr, uint8 data) {
add_clocks(24);
op_buswrite(addr, data);
cycle_edge();
debugger.op_write(addr, data);
}
uint8 SMP::disassembler_read(uint16 addr) {
if((addr & 0xfff0) == 0x00f0) return 0x00;
if((addr & 0xffc0) == 0xffc0 && status.iplrom_enable) return iplrom[addr & 0x3f];
return apuram[addr];
}
#endif