mirror of https://github.com/bsnes-emu/bsnes.git
Update to bsnes v049 release.
This is a maintenance release, but it offers a lot of bug-fixes and speed-ups, so it should be well worth the update. The debugger is not finished yet, so use it at your own risk. It is disabled in the binary release because breakpoint testing impacts performance. Once it is ready, I will release a separate binary with the debugger enabled. Changelog: - Optimized S-PPU emulation, provides a ~10-15% speedup in normal games - Cleaned up cheat editor user interface - Added save state and export data path selections - Added workaround for a strange issue that caused PAL games to run at 60 fps sometimes - Fixed sprite caching issue; fixes SD F-1 Grand Prix - Fixed PPUcounter reset issue; fixes Bishoujo Janshi Suchie-Pai [Jonas Quinn] - Fixed scaling on scanline, Scale2x, LQ2x and HQ2x filters on hires and interlace screens - Fixed sizeof(bool) serialization issue for PowerPC architecture [Richard Bannister] - Fixed cheat code sort ordering - Fixed a bug with centering in fullscreen mode - Fixed an audio pitch bug when changing frequency - Fixed a volume adjust bug when frequency was exactly 32000hz - Fixed X-video RGB rendering bugs [thanks to tukuyomi for testing] - Fixed a file open dialog issue on Linux when using QGtkStyle [jensbw] - Fixed a memory corruption issue involving QApplication::main() [giovannibajo] - Added a preliminary debugger (disabled in binary releases due to associated speed hit) - Added S-CPU and S-SMP stepping and tracing support - Added read/write/execute breakpoint support - Added memory editor (currently it can only view memory) - Added screenshot capture support [kode54] - Save state archives are now ~60% smaller than before - Various code cleanup work, as usual (note: the debugger code is messy, as it is in-progress)
This commit is contained in:
parent
c26f9d912a
commit
59b86cd3a8
|
@ -167,7 +167,7 @@ obj/sdsp.o: dsp/sdsp/sdsp.cpp dsp/sdsp/*
|
|||
###########
|
||||
|
||||
obj/ppu.o : ppu/ppu.cpp ppu/*
|
||||
obj/bppu.o: ppu/bppu/bppu.cpp ppu/bppu/*
|
||||
obj/bppu.o: ppu/bppu/bppu.cpp $(call rwildcard,ppu/bppu/)
|
||||
|
||||
##############
|
||||
### system ###
|
||||
|
|
12
src/base.hpp
12
src/base.hpp
|
@ -1,11 +1,6 @@
|
|||
static const char bsnesVersion[] = "0.048";
|
||||
static const char bsnesVersion[] = "0.049";
|
||||
static const char bsnesTitle[] = "bsnes";
|
||||
|
||||
#define BUSCORE sBus
|
||||
#define CPUCORE sCPU
|
||||
#define SMPCORE sSMP
|
||||
#define DSPCORE sDSP
|
||||
#define PPUCORE bPPU
|
||||
static const unsigned bsnesSaveStateVersion = 2;
|
||||
|
||||
//S-DSP can be encapsulated into a state machine using #define magic
|
||||
//this avoids ~2.048m co_switch() calls per second (~5% speedup)
|
||||
|
@ -14,6 +9,9 @@ static const char bsnesTitle[] = "bsnes";
|
|||
//game genie + pro action replay code support (~2% speed hit)
|
||||
#define CHEAT_SYSTEM
|
||||
|
||||
//enable debugging extensions (~15% speed hit)
|
||||
//#define DEBUGGER
|
||||
|
||||
#include <libco/libco.h>
|
||||
|
||||
#include <nall/algorithm.hpp>
|
||||
|
|
|
@ -71,6 +71,7 @@ void Cartridge::load(Mode cartridge_mode) {
|
|||
set(crc32, ~checksum);
|
||||
|
||||
bus.load_cart();
|
||||
system.serialize_init();
|
||||
set(loaded, true);
|
||||
}
|
||||
|
||||
|
|
|
@ -127,274 +127,274 @@ void CPUcore::disassemble_opcode(char *output) {
|
|||
#define x8 (regs.e || regs.p.x)
|
||||
|
||||
switch(op) {
|
||||
case 0x00: sprintf(t, "brk #$%.2x ", op8); break;
|
||||
case 0x01: sprintf(t, "ora ($%.2x,x) [$%.6x]", op8, decode(OPTYPE_IDPX, op8)); break;
|
||||
case 0x02: sprintf(t, "cop #$%.2x ", op8); break;
|
||||
case 0x03: sprintf(t, "ora $%.2x,s [$%.6x]", op8, decode(OPTYPE_SR, op8)); break;
|
||||
case 0x04: sprintf(t, "tsb $%.2x [$%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x05: sprintf(t, "ora $%.2x [$%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x06: sprintf(t, "asl $%.2x [$%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x07: sprintf(t, "ora [$%.2x] [$%.6x]", op8, decode(OPTYPE_ILDP, op8)); break;
|
||||
case 0x08: sprintf(t, "php "); break;
|
||||
case 0x09: if(a8)sprintf(t, "ora #$%.2x ", op8);
|
||||
else sprintf(t, "ora #$%.4x ", op16); break;
|
||||
case 0x0a: sprintf(t, "asl a "); break;
|
||||
case 0x0b: sprintf(t, "phd "); break;
|
||||
case 0x0c: sprintf(t, "tsb $%.4x [$%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x0d: sprintf(t, "ora $%.4x [$%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x0e: sprintf(t, "asl $%.4x [$%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x0f: sprintf(t, "ora $%.6x [$%.6x]", op24, decode(OPTYPE_LONG, op24)); break;
|
||||
case 0x10: sprintf(t, "bpl $%.4x [$%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break;
|
||||
case 0x11: sprintf(t, "ora ($%.2x),y [$%.6x]", op8, decode(OPTYPE_IDPY, op8)); break;
|
||||
case 0x12: sprintf(t, "ora ($%.2x) [$%.6x]", op8, decode(OPTYPE_IDP, op8)); break;
|
||||
case 0x13: sprintf(t, "ora ($%.2x,s),y [$%.6x]", op8, decode(OPTYPE_ISRY, op8)); break;
|
||||
case 0x14: sprintf(t, "trb $%.2x [$%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x15: sprintf(t, "ora $%.2x,x [$%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0x16: sprintf(t, "asl $%.2x,x [$%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0x17: sprintf(t, "ora [$%.2x],y [$%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break;
|
||||
case 0x18: sprintf(t, "clc "); break;
|
||||
case 0x19: sprintf(t, "ora $%.4x,y [$%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break;
|
||||
case 0x1a: sprintf(t, "inc "); break;
|
||||
case 0x1b: sprintf(t, "tcs "); break;
|
||||
case 0x1c: sprintf(t, "trb $%.4x [$%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x1d: sprintf(t, "ora $%.4x,x [$%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0x1e: sprintf(t, "asl $%.4x,x [$%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0x1f: sprintf(t, "ora $%.6x,x [$%.6x]", op24, decode(OPTYPE_LONGX, op24)); break;
|
||||
case 0x20: sprintf(t, "jsr $%.4x [$%.6x]", op16, decode(OPTYPE_ADDR_PC, op16)); break;
|
||||
case 0x21: sprintf(t, "and ($%.2x,x) [$%.6x]", op8, decode(OPTYPE_IDPX, op8)); break;
|
||||
case 0x22: sprintf(t, "jsl $%.6x [$%.6x]", op24, decode(OPTYPE_LONG, op24)); break;
|
||||
case 0x23: sprintf(t, "and $%.2x,s [$%.6x]", op8, decode(OPTYPE_SR, op8)); break;
|
||||
case 0x24: sprintf(t, "bit $%.2x [$%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x25: sprintf(t, "and $%.2x [$%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x26: sprintf(t, "rol $%.2x [$%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x27: sprintf(t, "and [$%.2x] [$%.6x]", op8, decode(OPTYPE_ILDP, op8)); break;
|
||||
case 0x28: sprintf(t, "plp "); break;
|
||||
case 0x29: if(a8)sprintf(t, "and #$%.2x ", op8);
|
||||
else sprintf(t, "and #$%.4x ", op16); break;
|
||||
case 0x2a: sprintf(t, "rol a "); break;
|
||||
case 0x2b: sprintf(t, "pld "); break;
|
||||
case 0x2c: sprintf(t, "bit $%.4x [$%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x2d: sprintf(t, "and $%.4x [$%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x2e: sprintf(t, "rol $%.4x [$%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x2f: sprintf(t, "and $%.6x [$%.6x]", op24, decode(OPTYPE_LONG, op24)); break;
|
||||
case 0x30: sprintf(t, "bmi $%.4x [$%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break;
|
||||
case 0x31: sprintf(t, "and ($%.2x),y [$%.6x]", op8, decode(OPTYPE_IDPY, op8)); break;
|
||||
case 0x32: sprintf(t, "and ($%.2x) [$%.6x]", op8, decode(OPTYPE_IDP, op8)); break;
|
||||
case 0x33: sprintf(t, "and ($%.2x,s),y [$%.6x]", op8, decode(OPTYPE_ISRY, op8)); break;
|
||||
case 0x34: sprintf(t, "bit $%.2x,x [$%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0x35: sprintf(t, "and $%.2x,x [$%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0x36: sprintf(t, "rol $%.2x,x [$%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0x37: sprintf(t, "and [$%.2x],y [$%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break;
|
||||
case 0x38: sprintf(t, "sec "); break;
|
||||
case 0x39: sprintf(t, "and $%.4x,y [$%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break;
|
||||
case 0x3a: sprintf(t, "dec "); break;
|
||||
case 0x3b: sprintf(t, "tsc "); break;
|
||||
case 0x3c: sprintf(t, "bit $%.4x,x [$%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0x3d: sprintf(t, "and $%.4x,x [$%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0x3e: sprintf(t, "rol $%.4x,x [$%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0x3f: sprintf(t, "and $%.6x,x [$%.6x]", op24, decode(OPTYPE_LONGX, op24)); break;
|
||||
case 0x40: sprintf(t, "rti "); break;
|
||||
case 0x41: sprintf(t, "eor ($%.2x,x) [$%.6x]", op8, decode(OPTYPE_IDPX, op8)); break;
|
||||
case 0x42: sprintf(t, "wdm "); break;
|
||||
case 0x43: sprintf(t, "eor $%.2x,s [$%.6x]", op8, decode(OPTYPE_SR, op8)); break;
|
||||
case 0x44: sprintf(t, "mvp $%.2x,$%.2x ", op1, op8); break;
|
||||
case 0x45: sprintf(t, "eor $%.2x [$%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x46: sprintf(t, "lsr $%.2x [$%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x47: sprintf(t, "eor [$%.2x] [$%.6x]", op8, decode(OPTYPE_ILDP, op8)); break;
|
||||
case 0x48: sprintf(t, "pha "); break;
|
||||
case 0x49: if(a8)sprintf(t, "eor #$%.2x ", op8);
|
||||
else sprintf(t, "eor #$%.4x ", op16); break;
|
||||
case 0x4a: sprintf(t, "lsr a "); break;
|
||||
case 0x4b: sprintf(t, "phk "); break;
|
||||
case 0x4c: sprintf(t, "jmp $%.4x [$%.6x]", op16, decode(OPTYPE_ADDR_PC, op16)); break;
|
||||
case 0x4d: sprintf(t, "eor $%.4x [$%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x4e: sprintf(t, "lsr $%.4x [$%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x4f: sprintf(t, "eor $%.6x [$%.6x]", op24, decode(OPTYPE_LONG, op24)); break;
|
||||
case 0x50: sprintf(t, "bvc $%.4x [$%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break;
|
||||
case 0x51: sprintf(t, "eor ($%.2x),y [$%.6x]", op8, decode(OPTYPE_IDPY, op8)); break;
|
||||
case 0x52: sprintf(t, "eor ($%.2x) [$%.6x]", op8, decode(OPTYPE_IDP, op8)); break;
|
||||
case 0x53: sprintf(t, "eor ($%.2x,s),y [$%.6x]", op8, decode(OPTYPE_ISRY, op8)); break;
|
||||
case 0x54: sprintf(t, "mvn $%.2x,$%.2x ", op1, op8); break;
|
||||
case 0x55: sprintf(t, "eor $%.2x,x [$%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0x56: sprintf(t, "lsr $%.2x,x [$%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0x57: sprintf(t, "eor [$%.2x],y [$%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break;
|
||||
case 0x58: sprintf(t, "cli "); break;
|
||||
case 0x59: sprintf(t, "eor $%.4x,y [$%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break;
|
||||
case 0x5a: sprintf(t, "phy "); break;
|
||||
case 0x5b: sprintf(t, "tcd "); break;
|
||||
case 0x5c: sprintf(t, "jml $%.6x [$%.6x]", op24, decode(OPTYPE_LONG, op24)); break;
|
||||
case 0x5d: sprintf(t, "eor $%.4x,x [$%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0x5e: sprintf(t, "lsr $%.4x,x [$%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0x5f: sprintf(t, "eor $%.6x,x [$%.6x]", op24, decode(OPTYPE_LONGX, op24)); break;
|
||||
case 0x60: sprintf(t, "rts "); break;
|
||||
case 0x61: sprintf(t, "adc ($%.2x,x) [$%.6x]", op8, decode(OPTYPE_IDPX, op8)); break;
|
||||
case 0x62: sprintf(t, "per $%.4x [$%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x63: sprintf(t, "adc $%.2x,s [$%.6x]", op8, decode(OPTYPE_SR, op8)); break;
|
||||
case 0x64: sprintf(t, "stz $%.2x [$%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x65: sprintf(t, "adc $%.2x [$%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x66: sprintf(t, "ror $%.2x [$%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x67: sprintf(t, "adc [$%.2x] [$%.6x]", op8, decode(OPTYPE_ILDP, op8)); break;
|
||||
case 0x68: sprintf(t, "pla "); break;
|
||||
case 0x69: if(a8)sprintf(t, "adc #$%.2x ", op8);
|
||||
else sprintf(t, "adc #$%.4x ", op16); break;
|
||||
case 0x6a: sprintf(t, "ror a "); break;
|
||||
case 0x6b: sprintf(t, "rtl "); break;
|
||||
case 0x6c: sprintf(t, "jmp ($%.4x) [$%.6x]", op16, decode(OPTYPE_IADDR_PC, op16)); break;
|
||||
case 0x6d: sprintf(t, "adc $%.4x [$%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x6e: sprintf(t, "ror $%.4x [$%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x6f: sprintf(t, "adc $%.6x [$%.6x]", op24, decode(OPTYPE_LONG, op24)); break;
|
||||
case 0x70: sprintf(t, "bvs $%.4x [$%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break;
|
||||
case 0x71: sprintf(t, "adc ($%.2x),y [$%.6x]", op8, decode(OPTYPE_IDPY, op8)); break;
|
||||
case 0x72: sprintf(t, "adc ($%.2x) [$%.6x]", op8, decode(OPTYPE_IDP, op8)); break;
|
||||
case 0x73: sprintf(t, "adc ($%.2x,s),y [$%.6x]", op8, decode(OPTYPE_ISRY, op8)); break;
|
||||
case 0x74: sprintf(t, "stz $%.2x,x [$%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0x75: sprintf(t, "adc $%.2x,x [$%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0x76: sprintf(t, "ror $%.2x,x [$%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0x77: sprintf(t, "adc [$%.2x],y [$%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break;
|
||||
case 0x78: sprintf(t, "sei "); break;
|
||||
case 0x79: sprintf(t, "adc $%.4x,y [$%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break;
|
||||
case 0x7a: sprintf(t, "ply "); break;
|
||||
case 0x7b: sprintf(t, "tdc "); break;
|
||||
case 0x7c: sprintf(t, "jmp ($%.4x,x) [$%.6x]", op16, decode(OPTYPE_IADDRX, op16)); break;
|
||||
case 0x7d: sprintf(t, "adc $%.4x,x [$%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0x7e: sprintf(t, "ror $%.4x,x [$%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0x7f: sprintf(t, "adc $%.6x,x [$%.6x]", op24, decode(OPTYPE_LONGX, op24)); break;
|
||||
case 0x80: sprintf(t, "bra $%.4x [$%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break;
|
||||
case 0x81: sprintf(t, "sta ($%.2x,x) [$%.6x]", op8, decode(OPTYPE_IDPX, op8)); break;
|
||||
case 0x82: sprintf(t, "brl $%.4x [$%.6x]", uint16(decode(OPTYPE_RELW, op16)), decode(OPTYPE_RELW, op16)); break;
|
||||
case 0x83: sprintf(t, "sta $%.2x,s [$%.6x]", op8, decode(OPTYPE_SR, op8)); break;
|
||||
case 0x84: sprintf(t, "sty $%.2x [$%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x85: sprintf(t, "sta $%.2x [$%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x86: sprintf(t, "stx $%.2x [$%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x87: sprintf(t, "sta [$%.2x] [$%.6x]", op8, decode(OPTYPE_ILDP, op8)); break;
|
||||
case 0x88: sprintf(t, "dey "); break;
|
||||
case 0x89: if(a8)sprintf(t, "bit #$%.2x ", op8);
|
||||
else sprintf(t, "bit #$%.4x ", op16); break;
|
||||
case 0x8a: sprintf(t, "txa "); break;
|
||||
case 0x8b: sprintf(t, "phb "); break;
|
||||
case 0x8c: sprintf(t, "sty $%.4x [$%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x8d: sprintf(t, "sta $%.4x [$%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x8e: sprintf(t, "stx $%.4x [$%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x8f: sprintf(t, "sta $%.6x [$%.6x]", op24, decode(OPTYPE_LONG, op24)); break;
|
||||
case 0x90: sprintf(t, "bcc $%.4x [$%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break;
|
||||
case 0x91: sprintf(t, "sta ($%.2x),y [$%.6x]", op8, decode(OPTYPE_IDPY, op8)); break;
|
||||
case 0x92: sprintf(t, "sta ($%.2x) [$%.6x]", op8, decode(OPTYPE_IDP, op8)); break;
|
||||
case 0x93: sprintf(t, "sta ($%.2x,s),y [$%.6x]", op8, decode(OPTYPE_ISRY, op8)); break;
|
||||
case 0x94: sprintf(t, "sty $%.2x,x [$%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0x95: sprintf(t, "sta $%.2x,x [$%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0x96: sprintf(t, "stx $%.2x,y [$%.6x]", op8, decode(OPTYPE_DPY, op8)); break;
|
||||
case 0x97: sprintf(t, "sta [$%.2x],y [$%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break;
|
||||
case 0x98: sprintf(t, "tya "); break;
|
||||
case 0x99: sprintf(t, "sta $%.4x,y [$%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break;
|
||||
case 0x9a: sprintf(t, "txs "); break;
|
||||
case 0x9b: sprintf(t, "txy "); break;
|
||||
case 0x9c: sprintf(t, "stz $%.4x [$%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x9d: sprintf(t, "sta $%.4x,x [$%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0x9e: sprintf(t, "stz $%.4x,x [$%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0x9f: sprintf(t, "sta $%.6x,x [$%.6x]", op24, decode(OPTYPE_LONGX, op24)); break;
|
||||
case 0xa0: if(x8)sprintf(t, "ldy #$%.2x ", op8);
|
||||
else sprintf(t, "ldy #$%.4x ", op16); break;
|
||||
case 0xa1: sprintf(t, "lda ($%.2x,x) [$%.6x]", op8, decode(OPTYPE_IDPX, op8)); break;
|
||||
case 0xa2: if(x8)sprintf(t, "ldx #$%.2x ", op8);
|
||||
else sprintf(t, "ldx #$%.4x ", op16); break;
|
||||
case 0xa3: sprintf(t, "lda $%.2x,s [$%.6x]", op8, decode(OPTYPE_SR, op8)); break;
|
||||
case 0xa4: sprintf(t, "ldy $%.2x [$%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0xa5: sprintf(t, "lda $%.2x [$%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0xa6: sprintf(t, "ldx $%.2x [$%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0xa7: sprintf(t, "lda [$%.2x] [$%.6x]", op8, decode(OPTYPE_ILDP, op8)); break;
|
||||
case 0xa8: sprintf(t, "tay "); break;
|
||||
case 0xa9: if(a8)sprintf(t, "lda #$%.2x ", op8);
|
||||
else sprintf(t, "lda #$%.4x ", op16); break;
|
||||
case 0xaa: sprintf(t, "tax "); break;
|
||||
case 0xab: sprintf(t, "plb "); break;
|
||||
case 0xac: sprintf(t, "ldy $%.4x [$%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0xad: sprintf(t, "lda $%.4x [$%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0xae: sprintf(t, "ldx $%.4x [$%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0xaf: sprintf(t, "lda $%.6x [$%.6x]", op24, decode(OPTYPE_LONG, op24)); break;
|
||||
case 0xb0: sprintf(t, "bcs $%.4x [$%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break;
|
||||
case 0xb1: sprintf(t, "lda ($%.2x),y [$%.6x]", op8, decode(OPTYPE_IDPY, op8)); break;
|
||||
case 0xb2: sprintf(t, "lda ($%.2x) [$%.6x]", op8, decode(OPTYPE_IDP, op8)); break;
|
||||
case 0xb3: sprintf(t, "lda ($%.2x,s),y [$%.6x]", op8, decode(OPTYPE_ISRY, op8)); break;
|
||||
case 0xb4: sprintf(t, "ldy $%.2x,x [$%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0xb5: sprintf(t, "lda $%.2x,x [$%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0xb6: sprintf(t, "ldx $%.2x,y [$%.6x]", op8, decode(OPTYPE_DPY, op8)); break;
|
||||
case 0xb7: sprintf(t, "lda [$%.2x],y [$%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break;
|
||||
case 0xb8: sprintf(t, "clv "); break;
|
||||
case 0xb9: sprintf(t, "lda $%.4x,y [$%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break;
|
||||
case 0xba: sprintf(t, "tsx "); break;
|
||||
case 0xbb: sprintf(t, "tyx "); break;
|
||||
case 0xbc: sprintf(t, "ldy $%.4x,x [$%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0xbd: sprintf(t, "lda $%.4x,x [$%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0xbe: sprintf(t, "ldx $%.4x,y [$%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break;
|
||||
case 0xbf: sprintf(t, "lda $%.6x,x [$%.6x]", op24, decode(OPTYPE_LONGX, op24)); break;
|
||||
case 0xc0: if(x8)sprintf(t, "cpy #$%.2x ", op8);
|
||||
else sprintf(t, "cpy #$%.4x ", op16); break;
|
||||
case 0xc1: sprintf(t, "cmp ($%.2x,x) [$%.6x]", op8, decode(OPTYPE_IDPX, op8)); break;
|
||||
case 0xc2: sprintf(t, "rep #$%.2x ", op8); break;
|
||||
case 0xc3: sprintf(t, "cmp $%.2x,s [$%.6x]", op8, decode(OPTYPE_SR, op8)); break;
|
||||
case 0xc4: sprintf(t, "cpy $%.2x [$%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0xc5: sprintf(t, "cmp $%.2x [$%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0xc6: sprintf(t, "dec $%.2x [$%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0xc7: sprintf(t, "cmp [$%.2x] [$%.6x]", op8, decode(OPTYPE_ILDP, op8)); break;
|
||||
case 0xc8: sprintf(t, "iny "); break;
|
||||
case 0xc9: if(a8)sprintf(t, "cmp #$%.2x ", op8);
|
||||
else sprintf(t, "cmp #$%.4x ", op16); break;
|
||||
case 0xca: sprintf(t, "dex "); break;
|
||||
case 0xcb: sprintf(t, "wai "); break;
|
||||
case 0xcc: sprintf(t, "cpy $%.4x [$%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0xcd: sprintf(t, "cmp $%.4x [$%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0xce: sprintf(t, "dec $%.4x [$%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0xcf: sprintf(t, "cmp $%.6x [$%.6x]", op24, decode(OPTYPE_LONG, op24)); break;
|
||||
case 0xd0: sprintf(t, "bne $%.4x [$%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break;
|
||||
case 0xd1: sprintf(t, "cmp ($%.2x),y [$%.6x]", op8, decode(OPTYPE_IDPY, op8)); break;
|
||||
case 0xd2: sprintf(t, "cmp ($%.2x) [$%.6x]", op8, decode(OPTYPE_IDP, op8)); break;
|
||||
case 0xd3: sprintf(t, "cmp ($%.2x,s),y [$%.6x]", op8, decode(OPTYPE_ISRY, op8)); break;
|
||||
case 0xd4: sprintf(t, "pei ($%.2x) [$%.6x]", op8, decode(OPTYPE_IDP, op8)); break;
|
||||
case 0xd5: sprintf(t, "cmp $%.2x,x [$%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0xd6: sprintf(t, "dec $%.2x,x [$%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0xd7: sprintf(t, "cmp [$%.2x],y [$%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break;
|
||||
case 0xd8: sprintf(t, "cld "); break;
|
||||
case 0xd9: sprintf(t, "cmp $%.4x,y [$%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break;
|
||||
case 0xda: sprintf(t, "phx "); break;
|
||||
case 0xdb: sprintf(t, "stp "); break;
|
||||
case 0xdc: sprintf(t, "jmp [$%.4x] [$%.6x]", op16, decode(OPTYPE_ILADDR, op16)); break;
|
||||
case 0xdd: sprintf(t, "cmp $%.4x,x [$%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0xde: sprintf(t, "dec $%.4x,x [$%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0xdf: sprintf(t, "cmp $%.6x,x [$%.6x]", op24, decode(OPTYPE_LONGX, op24)); break;
|
||||
case 0xe0: if(x8)sprintf(t, "cpx #$%.2x ", op8);
|
||||
else sprintf(t, "cpx #$%.4x ", op16); break;
|
||||
case 0xe1: sprintf(t, "sbc ($%.2x,x) [$%.6x]", op8, decode(OPTYPE_IDPX, op8)); break;
|
||||
case 0xe2: sprintf(t, "sep #$%.2x ", op8); break;
|
||||
case 0xe3: sprintf(t, "sbc $%.2x,s [$%.6x]", op8, decode(OPTYPE_SR, op8)); break;
|
||||
case 0xe4: sprintf(t, "cpx $%.2x [$%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0xe5: sprintf(t, "sbc $%.2x [$%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0xe6: sprintf(t, "inc $%.2x [$%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0xe7: sprintf(t, "sbc [$%.2x] [$%.6x]", op8, decode(OPTYPE_ILDP, op8)); break;
|
||||
case 0xe8: sprintf(t, "inx "); break;
|
||||
case 0xe9: if(a8)sprintf(t, "sbc #$%.2x ", op8);
|
||||
else sprintf(t, "sbc #$%.4x ", op16); break;
|
||||
case 0xea: sprintf(t, "nop "); break;
|
||||
case 0xeb: sprintf(t, "xba "); break;
|
||||
case 0xec: sprintf(t, "cpx $%.4x [$%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0xed: sprintf(t, "sbc $%.4x [$%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0xee: sprintf(t, "inc $%.4x [$%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0xef: sprintf(t, "sbc $%.6x [$%.6x]", op24, decode(OPTYPE_LONG, op24)); break;
|
||||
case 0xf0: sprintf(t, "beq $%.4x [$%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break;
|
||||
case 0xf1: sprintf(t, "sbc ($%.2x),y [$%.6x]", op8, decode(OPTYPE_IDPY, op8)); break;
|
||||
case 0xf2: sprintf(t, "sbc ($%.2x) [$%.6x]", op8, decode(OPTYPE_IDP, op8)); break;
|
||||
case 0xf3: sprintf(t, "sbc ($%.2x,s),y [$%.6x]", op8, decode(OPTYPE_ISRY, op8)); break;
|
||||
case 0xf4: sprintf(t, "pea $%.4x [$%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0xf5: sprintf(t, "sbc $%.2x,x [$%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0xf6: sprintf(t, "inc $%.2x,x [$%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0xf7: sprintf(t, "sbc [$%.2x],y [$%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break;
|
||||
case 0xf8: sprintf(t, "sed "); break;
|
||||
case 0xf9: sprintf(t, "sbc $%.4x,y [$%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break;
|
||||
case 0xfa: sprintf(t, "plx "); break;
|
||||
case 0xfb: sprintf(t, "xce "); break;
|
||||
case 0xfc: sprintf(t, "jsr ($%.4x,x) [$%.6x]", op16, decode(OPTYPE_IADDRX, op16)); break;
|
||||
case 0xfd: sprintf(t, "sbc $%.4x,x [$%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0xfe: sprintf(t, "inc $%.4x,x [$%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0xff: sprintf(t, "sbc $%.6x,x [$%.6x]", op24, decode(OPTYPE_LONGX, op24)); break;
|
||||
case 0x00: sprintf(t, "brk #$%.2x ", op8); break;
|
||||
case 0x01: sprintf(t, "ora ($%.2x,x) [%.6x]", op8, decode(OPTYPE_IDPX, op8)); break;
|
||||
case 0x02: sprintf(t, "cop #$%.2x ", op8); break;
|
||||
case 0x03: sprintf(t, "ora $%.2x,s [%.6x]", op8, decode(OPTYPE_SR, op8)); break;
|
||||
case 0x04: sprintf(t, "tsb $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x05: sprintf(t, "ora $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x06: sprintf(t, "asl $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x07: sprintf(t, "ora [$%.2x] [%.6x]", op8, decode(OPTYPE_ILDP, op8)); break;
|
||||
case 0x08: sprintf(t, "php "); break;
|
||||
case 0x09: if(a8)sprintf(t, "ora #$%.2x ", op8);
|
||||
else sprintf(t, "ora #$%.4x ", op16); break;
|
||||
case 0x0a: sprintf(t, "asl a "); break;
|
||||
case 0x0b: sprintf(t, "phd "); break;
|
||||
case 0x0c: sprintf(t, "tsb $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x0d: sprintf(t, "ora $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x0e: sprintf(t, "asl $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x0f: sprintf(t, "ora $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break;
|
||||
case 0x10: sprintf(t, "bpl $%.4x [%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break;
|
||||
case 0x11: sprintf(t, "ora ($%.2x),y [%.6x]", op8, decode(OPTYPE_IDPY, op8)); break;
|
||||
case 0x12: sprintf(t, "ora ($%.2x) [%.6x]", op8, decode(OPTYPE_IDP, op8)); break;
|
||||
case 0x13: sprintf(t, "ora ($%.2x,s),y [%.6x]", op8, decode(OPTYPE_ISRY, op8)); break;
|
||||
case 0x14: sprintf(t, "trb $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x15: sprintf(t, "ora $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0x16: sprintf(t, "asl $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0x17: sprintf(t, "ora [$%.2x],y [%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break;
|
||||
case 0x18: sprintf(t, "clc "); break;
|
||||
case 0x19: sprintf(t, "ora $%.4x,y [%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break;
|
||||
case 0x1a: sprintf(t, "inc "); break;
|
||||
case 0x1b: sprintf(t, "tcs "); break;
|
||||
case 0x1c: sprintf(t, "trb $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x1d: sprintf(t, "ora $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0x1e: sprintf(t, "asl $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0x1f: sprintf(t, "ora $%.6x,x [%.6x]", op24, decode(OPTYPE_LONGX, op24)); break;
|
||||
case 0x20: sprintf(t, "jsr $%.4x [%.6x]", op16, decode(OPTYPE_ADDR_PC, op16)); break;
|
||||
case 0x21: sprintf(t, "and ($%.2x,x) [%.6x]", op8, decode(OPTYPE_IDPX, op8)); break;
|
||||
case 0x22: sprintf(t, "jsl $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break;
|
||||
case 0x23: sprintf(t, "and $%.2x,s [%.6x]", op8, decode(OPTYPE_SR, op8)); break;
|
||||
case 0x24: sprintf(t, "bit $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x25: sprintf(t, "and $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x26: sprintf(t, "rol $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x27: sprintf(t, "and [$%.2x] [%.6x]", op8, decode(OPTYPE_ILDP, op8)); break;
|
||||
case 0x28: sprintf(t, "plp "); break;
|
||||
case 0x29: if(a8)sprintf(t, "and #$%.2x ", op8);
|
||||
else sprintf(t, "and #$%.4x ", op16); break;
|
||||
case 0x2a: sprintf(t, "rol a "); break;
|
||||
case 0x2b: sprintf(t, "pld "); break;
|
||||
case 0x2c: sprintf(t, "bit $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x2d: sprintf(t, "and $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x2e: sprintf(t, "rol $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x2f: sprintf(t, "and $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break;
|
||||
case 0x30: sprintf(t, "bmi $%.4x [%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break;
|
||||
case 0x31: sprintf(t, "and ($%.2x),y [%.6x]", op8, decode(OPTYPE_IDPY, op8)); break;
|
||||
case 0x32: sprintf(t, "and ($%.2x) [%.6x]", op8, decode(OPTYPE_IDP, op8)); break;
|
||||
case 0x33: sprintf(t, "and ($%.2x,s),y [%.6x]", op8, decode(OPTYPE_ISRY, op8)); break;
|
||||
case 0x34: sprintf(t, "bit $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0x35: sprintf(t, "and $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0x36: sprintf(t, "rol $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0x37: sprintf(t, "and [$%.2x],y [%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break;
|
||||
case 0x38: sprintf(t, "sec "); break;
|
||||
case 0x39: sprintf(t, "and $%.4x,y [%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break;
|
||||
case 0x3a: sprintf(t, "dec "); break;
|
||||
case 0x3b: sprintf(t, "tsc "); break;
|
||||
case 0x3c: sprintf(t, "bit $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0x3d: sprintf(t, "and $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0x3e: sprintf(t, "rol $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0x3f: sprintf(t, "and $%.6x,x [%.6x]", op24, decode(OPTYPE_LONGX, op24)); break;
|
||||
case 0x40: sprintf(t, "rti "); break;
|
||||
case 0x41: sprintf(t, "eor ($%.2x,x) [%.6x]", op8, decode(OPTYPE_IDPX, op8)); break;
|
||||
case 0x42: sprintf(t, "wdm "); break;
|
||||
case 0x43: sprintf(t, "eor $%.2x,s [%.6x]", op8, decode(OPTYPE_SR, op8)); break;
|
||||
case 0x44: sprintf(t, "mvp $%.2x,$%.2x ", op1, op8); break;
|
||||
case 0x45: sprintf(t, "eor $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x46: sprintf(t, "lsr $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x47: sprintf(t, "eor [$%.2x] [%.6x]", op8, decode(OPTYPE_ILDP, op8)); break;
|
||||
case 0x48: sprintf(t, "pha "); break;
|
||||
case 0x49: if(a8)sprintf(t, "eor #$%.2x ", op8);
|
||||
else sprintf(t, "eor #$%.4x ", op16); break;
|
||||
case 0x4a: sprintf(t, "lsr a "); break;
|
||||
case 0x4b: sprintf(t, "phk "); break;
|
||||
case 0x4c: sprintf(t, "jmp $%.4x [%.6x]", op16, decode(OPTYPE_ADDR_PC, op16)); break;
|
||||
case 0x4d: sprintf(t, "eor $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x4e: sprintf(t, "lsr $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x4f: sprintf(t, "eor $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break;
|
||||
case 0x50: sprintf(t, "bvc $%.4x [%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break;
|
||||
case 0x51: sprintf(t, "eor ($%.2x),y [%.6x]", op8, decode(OPTYPE_IDPY, op8)); break;
|
||||
case 0x52: sprintf(t, "eor ($%.2x) [%.6x]", op8, decode(OPTYPE_IDP, op8)); break;
|
||||
case 0x53: sprintf(t, "eor ($%.2x,s),y [%.6x]", op8, decode(OPTYPE_ISRY, op8)); break;
|
||||
case 0x54: sprintf(t, "mvn $%.2x,$%.2x ", op1, op8); break;
|
||||
case 0x55: sprintf(t, "eor $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0x56: sprintf(t, "lsr $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0x57: sprintf(t, "eor [$%.2x],y [%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break;
|
||||
case 0x58: sprintf(t, "cli "); break;
|
||||
case 0x59: sprintf(t, "eor $%.4x,y [%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break;
|
||||
case 0x5a: sprintf(t, "phy "); break;
|
||||
case 0x5b: sprintf(t, "tcd "); break;
|
||||
case 0x5c: sprintf(t, "jml $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break;
|
||||
case 0x5d: sprintf(t, "eor $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0x5e: sprintf(t, "lsr $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0x5f: sprintf(t, "eor $%.6x,x [%.6x]", op24, decode(OPTYPE_LONGX, op24)); break;
|
||||
case 0x60: sprintf(t, "rts "); break;
|
||||
case 0x61: sprintf(t, "adc ($%.2x,x) [%.6x]", op8, decode(OPTYPE_IDPX, op8)); break;
|
||||
case 0x62: sprintf(t, "per $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x63: sprintf(t, "adc $%.2x,s [%.6x]", op8, decode(OPTYPE_SR, op8)); break;
|
||||
case 0x64: sprintf(t, "stz $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x65: sprintf(t, "adc $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x66: sprintf(t, "ror $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x67: sprintf(t, "adc [$%.2x] [%.6x]", op8, decode(OPTYPE_ILDP, op8)); break;
|
||||
case 0x68: sprintf(t, "pla "); break;
|
||||
case 0x69: if(a8)sprintf(t, "adc #$%.2x ", op8);
|
||||
else sprintf(t, "adc #$%.4x ", op16); break;
|
||||
case 0x6a: sprintf(t, "ror a "); break;
|
||||
case 0x6b: sprintf(t, "rtl "); break;
|
||||
case 0x6c: sprintf(t, "jmp ($%.4x) [%.6x]", op16, decode(OPTYPE_IADDR_PC, op16)); break;
|
||||
case 0x6d: sprintf(t, "adc $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x6e: sprintf(t, "ror $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x6f: sprintf(t, "adc $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break;
|
||||
case 0x70: sprintf(t, "bvs $%.4x [%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break;
|
||||
case 0x71: sprintf(t, "adc ($%.2x),y [%.6x]", op8, decode(OPTYPE_IDPY, op8)); break;
|
||||
case 0x72: sprintf(t, "adc ($%.2x) [%.6x]", op8, decode(OPTYPE_IDP, op8)); break;
|
||||
case 0x73: sprintf(t, "adc ($%.2x,s),y [%.6x]", op8, decode(OPTYPE_ISRY, op8)); break;
|
||||
case 0x74: sprintf(t, "stz $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0x75: sprintf(t, "adc $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0x76: sprintf(t, "ror $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0x77: sprintf(t, "adc [$%.2x],y [%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break;
|
||||
case 0x78: sprintf(t, "sei "); break;
|
||||
case 0x79: sprintf(t, "adc $%.4x,y [%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break;
|
||||
case 0x7a: sprintf(t, "ply "); break;
|
||||
case 0x7b: sprintf(t, "tdc "); break;
|
||||
case 0x7c: sprintf(t, "jmp ($%.4x,x) [%.6x]", op16, decode(OPTYPE_IADDRX, op16)); break;
|
||||
case 0x7d: sprintf(t, "adc $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0x7e: sprintf(t, "ror $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0x7f: sprintf(t, "adc $%.6x,x [%.6x]", op24, decode(OPTYPE_LONGX, op24)); break;
|
||||
case 0x80: sprintf(t, "bra $%.4x [%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break;
|
||||
case 0x81: sprintf(t, "sta ($%.2x,x) [%.6x]", op8, decode(OPTYPE_IDPX, op8)); break;
|
||||
case 0x82: sprintf(t, "brl $%.4x [%.6x]", uint16(decode(OPTYPE_RELW, op16)), decode(OPTYPE_RELW, op16)); break;
|
||||
case 0x83: sprintf(t, "sta $%.2x,s [%.6x]", op8, decode(OPTYPE_SR, op8)); break;
|
||||
case 0x84: sprintf(t, "sty $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x85: sprintf(t, "sta $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x86: sprintf(t, "stx $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0x87: sprintf(t, "sta [$%.2x] [%.6x]", op8, decode(OPTYPE_ILDP, op8)); break;
|
||||
case 0x88: sprintf(t, "dey "); break;
|
||||
case 0x89: if(a8)sprintf(t, "bit #$%.2x ", op8);
|
||||
else sprintf(t, "bit #$%.4x ", op16); break;
|
||||
case 0x8a: sprintf(t, "txa "); break;
|
||||
case 0x8b: sprintf(t, "phb "); break;
|
||||
case 0x8c: sprintf(t, "sty $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x8d: sprintf(t, "sta $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x8e: sprintf(t, "stx $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x8f: sprintf(t, "sta $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break;
|
||||
case 0x90: sprintf(t, "bcc $%.4x [%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break;
|
||||
case 0x91: sprintf(t, "sta ($%.2x),y [%.6x]", op8, decode(OPTYPE_IDPY, op8)); break;
|
||||
case 0x92: sprintf(t, "sta ($%.2x) [%.6x]", op8, decode(OPTYPE_IDP, op8)); break;
|
||||
case 0x93: sprintf(t, "sta ($%.2x,s),y [%.6x]", op8, decode(OPTYPE_ISRY, op8)); break;
|
||||
case 0x94: sprintf(t, "sty $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0x95: sprintf(t, "sta $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0x96: sprintf(t, "stx $%.2x,y [%.6x]", op8, decode(OPTYPE_DPY, op8)); break;
|
||||
case 0x97: sprintf(t, "sta [$%.2x],y [%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break;
|
||||
case 0x98: sprintf(t, "tya "); break;
|
||||
case 0x99: sprintf(t, "sta $%.4x,y [%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break;
|
||||
case 0x9a: sprintf(t, "txs "); break;
|
||||
case 0x9b: sprintf(t, "txy "); break;
|
||||
case 0x9c: sprintf(t, "stz $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0x9d: sprintf(t, "sta $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0x9e: sprintf(t, "stz $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0x9f: sprintf(t, "sta $%.6x,x [%.6x]", op24, decode(OPTYPE_LONGX, op24)); break;
|
||||
case 0xa0: if(x8)sprintf(t, "ldy #$%.2x ", op8);
|
||||
else sprintf(t, "ldy #$%.4x ", op16); break;
|
||||
case 0xa1: sprintf(t, "lda ($%.2x,x) [%.6x]", op8, decode(OPTYPE_IDPX, op8)); break;
|
||||
case 0xa2: if(x8)sprintf(t, "ldx #$%.2x ", op8);
|
||||
else sprintf(t, "ldx #$%.4x ", op16); break;
|
||||
case 0xa3: sprintf(t, "lda $%.2x,s [%.6x]", op8, decode(OPTYPE_SR, op8)); break;
|
||||
case 0xa4: sprintf(t, "ldy $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0xa5: sprintf(t, "lda $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0xa6: sprintf(t, "ldx $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0xa7: sprintf(t, "lda [$%.2x] [%.6x]", op8, decode(OPTYPE_ILDP, op8)); break;
|
||||
case 0xa8: sprintf(t, "tay "); break;
|
||||
case 0xa9: if(a8)sprintf(t, "lda #$%.2x ", op8);
|
||||
else sprintf(t, "lda #$%.4x ", op16); break;
|
||||
case 0xaa: sprintf(t, "tax "); break;
|
||||
case 0xab: sprintf(t, "plb "); break;
|
||||
case 0xac: sprintf(t, "ldy $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0xad: sprintf(t, "lda $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0xae: sprintf(t, "ldx $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0xaf: sprintf(t, "lda $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break;
|
||||
case 0xb0: sprintf(t, "bcs $%.4x [%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break;
|
||||
case 0xb1: sprintf(t, "lda ($%.2x),y [%.6x]", op8, decode(OPTYPE_IDPY, op8)); break;
|
||||
case 0xb2: sprintf(t, "lda ($%.2x) [%.6x]", op8, decode(OPTYPE_IDP, op8)); break;
|
||||
case 0xb3: sprintf(t, "lda ($%.2x,s),y [%.6x]", op8, decode(OPTYPE_ISRY, op8)); break;
|
||||
case 0xb4: sprintf(t, "ldy $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0xb5: sprintf(t, "lda $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0xb6: sprintf(t, "ldx $%.2x,y [%.6x]", op8, decode(OPTYPE_DPY, op8)); break;
|
||||
case 0xb7: sprintf(t, "lda [$%.2x],y [%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break;
|
||||
case 0xb8: sprintf(t, "clv "); break;
|
||||
case 0xb9: sprintf(t, "lda $%.4x,y [%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break;
|
||||
case 0xba: sprintf(t, "tsx "); break;
|
||||
case 0xbb: sprintf(t, "tyx "); break;
|
||||
case 0xbc: sprintf(t, "ldy $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0xbd: sprintf(t, "lda $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0xbe: sprintf(t, "ldx $%.4x,y [%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break;
|
||||
case 0xbf: sprintf(t, "lda $%.6x,x [%.6x]", op24, decode(OPTYPE_LONGX, op24)); break;
|
||||
case 0xc0: if(x8)sprintf(t, "cpy #$%.2x ", op8);
|
||||
else sprintf(t, "cpy #$%.4x ", op16); break;
|
||||
case 0xc1: sprintf(t, "cmp ($%.2x,x) [%.6x]", op8, decode(OPTYPE_IDPX, op8)); break;
|
||||
case 0xc2: sprintf(t, "rep #$%.2x ", op8); break;
|
||||
case 0xc3: sprintf(t, "cmp $%.2x,s [%.6x]", op8, decode(OPTYPE_SR, op8)); break;
|
||||
case 0xc4: sprintf(t, "cpy $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0xc5: sprintf(t, "cmp $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0xc6: sprintf(t, "dec $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0xc7: sprintf(t, "cmp [$%.2x] [%.6x]", op8, decode(OPTYPE_ILDP, op8)); break;
|
||||
case 0xc8: sprintf(t, "iny "); break;
|
||||
case 0xc9: if(a8)sprintf(t, "cmp #$%.2x ", op8);
|
||||
else sprintf(t, "cmp #$%.4x ", op16); break;
|
||||
case 0xca: sprintf(t, "dex "); break;
|
||||
case 0xcb: sprintf(t, "wai "); break;
|
||||
case 0xcc: sprintf(t, "cpy $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0xcd: sprintf(t, "cmp $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0xce: sprintf(t, "dec $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0xcf: sprintf(t, "cmp $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break;
|
||||
case 0xd0: sprintf(t, "bne $%.4x [%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break;
|
||||
case 0xd1: sprintf(t, "cmp ($%.2x),y [%.6x]", op8, decode(OPTYPE_IDPY, op8)); break;
|
||||
case 0xd2: sprintf(t, "cmp ($%.2x) [%.6x]", op8, decode(OPTYPE_IDP, op8)); break;
|
||||
case 0xd3: sprintf(t, "cmp ($%.2x,s),y [%.6x]", op8, decode(OPTYPE_ISRY, op8)); break;
|
||||
case 0xd4: sprintf(t, "pei ($%.2x) [%.6x]", op8, decode(OPTYPE_IDP, op8)); break;
|
||||
case 0xd5: sprintf(t, "cmp $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0xd6: sprintf(t, "dec $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0xd7: sprintf(t, "cmp [$%.2x],y [%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break;
|
||||
case 0xd8: sprintf(t, "cld "); break;
|
||||
case 0xd9: sprintf(t, "cmp $%.4x,y [%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break;
|
||||
case 0xda: sprintf(t, "phx "); break;
|
||||
case 0xdb: sprintf(t, "stp "); break;
|
||||
case 0xdc: sprintf(t, "jmp [$%.4x] [%.6x]", op16, decode(OPTYPE_ILADDR, op16)); break;
|
||||
case 0xdd: sprintf(t, "cmp $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0xde: sprintf(t, "dec $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0xdf: sprintf(t, "cmp $%.6x,x [%.6x]", op24, decode(OPTYPE_LONGX, op24)); break;
|
||||
case 0xe0: if(x8)sprintf(t, "cpx #$%.2x ", op8);
|
||||
else sprintf(t, "cpx #$%.4x ", op16); break;
|
||||
case 0xe1: sprintf(t, "sbc ($%.2x,x) [%.6x]", op8, decode(OPTYPE_IDPX, op8)); break;
|
||||
case 0xe2: sprintf(t, "sep #$%.2x ", op8); break;
|
||||
case 0xe3: sprintf(t, "sbc $%.2x,s [%.6x]", op8, decode(OPTYPE_SR, op8)); break;
|
||||
case 0xe4: sprintf(t, "cpx $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0xe5: sprintf(t, "sbc $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0xe6: sprintf(t, "inc $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break;
|
||||
case 0xe7: sprintf(t, "sbc [$%.2x] [%.6x]", op8, decode(OPTYPE_ILDP, op8)); break;
|
||||
case 0xe8: sprintf(t, "inx "); break;
|
||||
case 0xe9: if(a8)sprintf(t, "sbc #$%.2x ", op8);
|
||||
else sprintf(t, "sbc #$%.4x ", op16); break;
|
||||
case 0xea: sprintf(t, "nop "); break;
|
||||
case 0xeb: sprintf(t, "xba "); break;
|
||||
case 0xec: sprintf(t, "cpx $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0xed: sprintf(t, "sbc $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0xee: sprintf(t, "inc $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0xef: sprintf(t, "sbc $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break;
|
||||
case 0xf0: sprintf(t, "beq $%.4x [%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break;
|
||||
case 0xf1: sprintf(t, "sbc ($%.2x),y [%.6x]", op8, decode(OPTYPE_IDPY, op8)); break;
|
||||
case 0xf2: sprintf(t, "sbc ($%.2x) [%.6x]", op8, decode(OPTYPE_IDP, op8)); break;
|
||||
case 0xf3: sprintf(t, "sbc ($%.2x,s),y [%.6x]", op8, decode(OPTYPE_ISRY, op8)); break;
|
||||
case 0xf4: sprintf(t, "pea $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break;
|
||||
case 0xf5: sprintf(t, "sbc $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0xf6: sprintf(t, "inc $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break;
|
||||
case 0xf7: sprintf(t, "sbc [$%.2x],y [%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break;
|
||||
case 0xf8: sprintf(t, "sed "); break;
|
||||
case 0xf9: sprintf(t, "sbc $%.4x,y [%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break;
|
||||
case 0xfa: sprintf(t, "plx "); break;
|
||||
case 0xfb: sprintf(t, "xce "); break;
|
||||
case 0xfc: sprintf(t, "jsr ($%.4x,x) [%.6x]", op16, decode(OPTYPE_IADDRX, op16)); break;
|
||||
case 0xfd: sprintf(t, "sbc $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0xfe: sprintf(t, "inc $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break;
|
||||
case 0xff: sprintf(t, "sbc $%.6x,x [%.6x]", op24, decode(OPTYPE_LONGX, op24)); break;
|
||||
}
|
||||
|
||||
#undef op8
|
||||
|
@ -427,7 +427,7 @@ void CPUcore::disassemble_opcode(char *output) {
|
|||
strcat(s, t);
|
||||
strcat(s, " ");
|
||||
|
||||
sprintf(t, "V:%3d H:%4d", ppu.vcounter(), ppu.hcounter());
|
||||
sprintf(t, "V:%3d H:%4d", cpu.vcounter(), cpu.hcounter());
|
||||
strcat(s, t);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
#ifdef SCPU_CPP
|
||||
|
||||
void sCPUdebug::op_step() {
|
||||
bool break_event = false;
|
||||
|
||||
if(debugger.step_cpu) {
|
||||
debugger.break_event = Debugger::CPUStep;
|
||||
break_event = true;
|
||||
}
|
||||
|
||||
for(unsigned i = 0; i < Debugger::Breakpoints; i++) {
|
||||
if(debugger.breakpoint[i].enabled == false) continue;
|
||||
if(debugger.breakpoint[i].addr != regs.pc) continue;
|
||||
if(debugger.breakpoint[i].mode != Debugger::Breakpoint::Exec) continue;
|
||||
if(debugger.breakpoint[i].source != Debugger::Breakpoint::CPUBus) continue;
|
||||
|
||||
debugger.breakpoint[i].counter++;
|
||||
debugger.breakpoint_hit = i;
|
||||
debugger.break_event = Debugger::BreakpointHit;
|
||||
break_event = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(break_event) scheduler.exit();
|
||||
|
||||
if(debugger.trace_cpu) {
|
||||
char t[256];
|
||||
disassemble_opcode(t);
|
||||
debugger.tracefile.print(string() << t << "\n");
|
||||
}
|
||||
|
||||
sCPU::op_step();
|
||||
scheduler.sync_cpusmp();
|
||||
}
|
||||
|
||||
uint8 sCPUdebug::op_read(uint32 addr) {
|
||||
uint8 data = sCPU::op_read(addr);
|
||||
|
||||
for(unsigned i = 0; i < Debugger::Breakpoints; i++) {
|
||||
if(debugger.breakpoint[i].enabled == false) continue;
|
||||
if(debugger.breakpoint[i].addr != addr) continue;
|
||||
if(debugger.breakpoint[i].data != -1 && debugger.breakpoint[i].data != data) continue;
|
||||
if(debugger.breakpoint[i].mode != Debugger::Breakpoint::Read) continue;
|
||||
if(debugger.breakpoint[i].source != Debugger::Breakpoint::CPUBus) continue;
|
||||
|
||||
debugger.breakpoint[i].counter++;
|
||||
debugger.breakpoint_hit = i;
|
||||
debugger.break_event = Debugger::BreakpointHit;
|
||||
scheduler.exit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void sCPUdebug::op_write(uint32 addr, uint8 data) {
|
||||
sCPU::op_write(addr, data);
|
||||
|
||||
for(unsigned i = 0; i < Debugger::Breakpoints; i++) {
|
||||
if(debugger.breakpoint[i].enabled == false) continue;
|
||||
if(debugger.breakpoint[i].addr != addr) continue;
|
||||
if(debugger.breakpoint[i].data != -1 && debugger.breakpoint[i].data != data) continue;
|
||||
if(debugger.breakpoint[i].mode != Debugger::Breakpoint::Write) continue;
|
||||
if(debugger.breakpoint[i].source != Debugger::Breakpoint::CPUBus) continue;
|
||||
|
||||
debugger.breakpoint[i].counter++;
|
||||
debugger.breakpoint_hit = i;
|
||||
debugger.break_event = Debugger::BreakpointHit;
|
||||
scheduler.exit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,6 @@
|
|||
class sCPUdebug : public sCPU {
|
||||
public:
|
||||
void op_step();
|
||||
uint8 op_read(uint32 addr);
|
||||
void op_write(uint32 addr, uint8 data);
|
||||
};
|
|
@ -11,6 +11,6 @@ void port_write(uint8 port, uint8 data) { apu_port[port & 3] = data; }
|
|||
//======================
|
||||
|
||||
void op_io();
|
||||
uint8 op_read(uint32 addr);
|
||||
void op_write(uint32 addr, uint8 data);
|
||||
debugvirtual uint8 op_read(uint32 addr);
|
||||
debugvirtual void op_write(uint32 addr, uint8 data);
|
||||
alwaysinline unsigned speed(unsigned addr) const;
|
||||
|
|
|
@ -3,8 +3,14 @@
|
|||
#define SCPU_CPP
|
||||
namespace SNES {
|
||||
|
||||
#include "serialization.cpp"
|
||||
#if defined(DEBUGGER)
|
||||
#include "debugger/debugger.cpp"
|
||||
sCPUdebug cpu;
|
||||
#else
|
||||
sCPU cpu;
|
||||
#endif
|
||||
|
||||
#include "serialization.cpp"
|
||||
#include "dma/dma.cpp"
|
||||
#include "memory/memory.cpp"
|
||||
#include "mmio/mmio.cpp"
|
||||
|
@ -22,18 +28,27 @@ void sCPU::enter() {
|
|||
if(status.nmi_pending) {
|
||||
status.nmi_pending = false;
|
||||
status.interrupt_vector = (regs.e == false ? 0xffea : 0xfffa);
|
||||
op_irq();
|
||||
} else if(status.irq_pending) {
|
||||
status.irq_pending = false;
|
||||
status.interrupt_vector = (regs.e == false ? 0xffee : 0xfffe);
|
||||
op_irq();
|
||||
} else if(status.reset_pending) {
|
||||
status.reset_pending = false;
|
||||
add_clocks(186);
|
||||
regs.pc.l = bus.read(0xfffc);
|
||||
regs.pc.h = bus.read(0xfffd);
|
||||
}
|
||||
op_irq();
|
||||
}
|
||||
|
||||
tracer.trace_cpuop(); //traces CPU opcode (only if tracer is enabled)
|
||||
(this->*opcode_table[op_readpc()])();
|
||||
op_step();
|
||||
}
|
||||
}
|
||||
|
||||
void sCPU::op_step() {
|
||||
(this->*opcode_table[op_readpc()])();
|
||||
}
|
||||
|
||||
void sCPU::op_irq() {
|
||||
op_read(regs.pc.d);
|
||||
op_io();
|
||||
|
@ -78,9 +93,6 @@ void sCPU::reset() {
|
|||
regs.wai = false;
|
||||
update_table();
|
||||
|
||||
status.interrupt_pending = false;
|
||||
status.interrupt_vector = 0xfffc; //reset vector address
|
||||
|
||||
mmio_reset();
|
||||
dma_reset();
|
||||
timing_reset();
|
||||
|
@ -89,9 +101,6 @@ void sCPU::reset() {
|
|||
apu_port[1] = 0x00;
|
||||
apu_port[2] = 0x00;
|
||||
apu_port[3] = 0x00;
|
||||
|
||||
regs.pc.l = bus.read(0xfffc);
|
||||
regs.pc.h = bus.read(0xfffd);
|
||||
}
|
||||
|
||||
sCPU::sCPU() : event(512, bind(&sCPU::queue_event, this)) {
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
class sCPUdebug;
|
||||
|
||||
class sCPU : public CPU, public CPUcore {
|
||||
public:
|
||||
void enter();
|
||||
debugvirtual void op_step();
|
||||
void op_irq();
|
||||
bool interrupt_pending() { return status.interrupt_pending; }
|
||||
|
||||
|
@ -35,6 +38,8 @@ public:
|
|||
bool irq_pending;
|
||||
bool irq_hold;
|
||||
|
||||
bool reset_pending;
|
||||
|
||||
//DMA
|
||||
bool dma_active;
|
||||
unsigned dma_counter;
|
||||
|
@ -91,4 +96,13 @@ public:
|
|||
void serialize(serializer&);
|
||||
sCPU();
|
||||
~sCPU();
|
||||
|
||||
friend class sCPUdebug;
|
||||
};
|
||||
|
||||
#if defined(DEBUGGER)
|
||||
#include "debugger/debugger.hpp"
|
||||
extern sCPUdebug cpu;
|
||||
#else
|
||||
extern sCPU cpu;
|
||||
#endif
|
||||
|
|
|
@ -28,6 +28,8 @@ void sCPU::serialize(serializer &s) {
|
|||
s.integer(status.irq_pending);
|
||||
s.integer(status.irq_hold);
|
||||
|
||||
s.integer(status.reset_pending);
|
||||
|
||||
s.integer(status.dma_active);
|
||||
s.integer(status.dma_counter);
|
||||
s.integer(status.dma_clocks);
|
||||
|
|
|
@ -155,6 +155,10 @@ void sCPU::timing_reset() {
|
|||
status.irq_pending = false;
|
||||
status.irq_hold = false;
|
||||
|
||||
status.reset_pending = true;
|
||||
status.interrupt_pending = true;
|
||||
status.interrupt_vector = 0xfffc; //reset vector address
|
||||
|
||||
status.dma_active = false;
|
||||
status.dma_counter = 0;
|
||||
status.dma_clocks = 0;
|
||||
|
|
|
@ -77,7 +77,6 @@ software which is ordinarily not compatible with this license, such as the GPL.
|
|||
<tr><td>ST-0010 emulator</td><td></td><td>Feather, John Weidman, Kris Bleakley, Matthew Kendora</td></tr>
|
||||
|
||||
<tr><td>Qt toolkit</td><td>LGPL 2.1</td><td>Nokia</td></tr>
|
||||
<tr><td>HQ2x filter</td><td>LGPL 2.1</td><td>MaxST</td></tr>
|
||||
<tr><td>JMA decompressor</td><td>GPL 2</td><td>NSRT team</td></tr>
|
||||
<tr><td>NTSC filter</td><td>LGPL 2.1</td><td>Shay Green</td></tr>
|
||||
<tr><td>zlib decompressor</td><td>zlib license</td><td>zlib team</td></tr>
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#define ADSP_CPP
|
||||
namespaec SNES {
|
||||
|
||||
aDSP dsp;
|
||||
|
||||
#include "adsp_tables.cpp"
|
||||
|
||||
void aDSP::enter() { loop:
|
||||
|
|
|
@ -170,3 +170,5 @@ public:
|
|||
aDSP();
|
||||
~aDSP();
|
||||
};
|
||||
|
||||
extern aDSP dsp;
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#define SDSP_CPP
|
||||
namespace SNES {
|
||||
|
||||
sDSP dsp;
|
||||
|
||||
#include "serialization.cpp"
|
||||
|
||||
#define REG(n) state.regs[r_##n]
|
||||
|
|
|
@ -165,3 +165,5 @@ private:
|
|||
void echo_29();
|
||||
void echo_30();
|
||||
};
|
||||
|
||||
extern sDSP dsp;
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
#ifdef DEBUGGER
|
||||
#define debugvirtual virtual
|
||||
#else
|
||||
#define debugvirtual
|
||||
#endif
|
||||
|
||||
namespace SNES {
|
||||
#include "memory/memory.hpp"
|
||||
#include "memory/smemory/smemory.hpp"
|
||||
|
@ -16,12 +22,6 @@ namespace SNES {
|
|||
#include "dsp/dsp.hpp"
|
||||
#include "dsp/sdsp/sdsp.hpp"
|
||||
|
||||
extern BUSCORE bus;
|
||||
extern CPUCORE cpu;
|
||||
extern SMPCORE smp;
|
||||
extern PPUCORE ppu;
|
||||
extern DSPCORE dsp;
|
||||
|
||||
#include "system/system.hpp"
|
||||
#include "chip/chip.hpp"
|
||||
#include "cartridge/cartridge.hpp"
|
||||
|
@ -31,3 +31,5 @@ namespace SNES {
|
|||
#include "ppu/ppu-inline.hpp"
|
||||
#include "cheat/cheat-inline.hpp"
|
||||
};
|
||||
|
||||
#undef debugvirtual
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
//HQ2x filter
|
||||
//authors: byuu and blargg
|
||||
//license: public domain
|
||||
//
|
||||
//note: this is a clean reimplementation of the original HQ2x filter, which was
|
||||
//written by Maxim Stepin (MaxSt). it is not 100% identical, but very similar.
|
||||
|
||||
HQ2xFilter filter_hq2x;
|
||||
|
||||
const uint8_t HQ2xFilter::hqTable[256] = {
|
||||
|
@ -87,8 +94,13 @@ alwaysinline uint16_t HQ2xFilter::blend(unsigned rule, uint16_t E, uint16_t A, u
|
|||
}
|
||||
|
||||
void HQ2xFilter::size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height) {
|
||||
outwidth = width * 2;
|
||||
outheight = height * 2;
|
||||
outwidth = width;
|
||||
outheight = height;
|
||||
|
||||
if(width <= 256 && height <= 240) {
|
||||
outwidth *= 2;
|
||||
outheight *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
void HQ2xFilter::render(
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
LQ2xFilter filter_lq2x;
|
||||
|
||||
void LQ2xFilter::size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height) {
|
||||
outwidth = width * 2;
|
||||
outheight = height * 2;
|
||||
outwidth = width;
|
||||
outheight = height;
|
||||
|
||||
if(width <= 256 && height <= 240) {
|
||||
outwidth *= 2;
|
||||
outheight *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
void LQ2xFilter::render(
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
Scale2xFilter filter_scale2x;
|
||||
|
||||
void Scale2xFilter::size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height) {
|
||||
outwidth = width * 2;
|
||||
outheight = height * 2;
|
||||
outwidth = width;
|
||||
outheight = height;
|
||||
|
||||
if(width <= 256 && height <= 240) {
|
||||
outwidth *= 2;
|
||||
outheight *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
void Scale2xFilter::render(
|
||||
|
|
|
@ -2,7 +2,7 @@ ScanlineFilter filter_scanline;
|
|||
|
||||
void ScanlineFilter::size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height) {
|
||||
outwidth = width;
|
||||
outheight = height * 2;
|
||||
outheight = height > 240 ? height : height * 2;
|
||||
}
|
||||
|
||||
void ScanlineFilter::render(
|
||||
|
|
|
@ -34,11 +34,14 @@ namespace nall {
|
|||
}
|
||||
|
||||
template<typename T> void integer(T &value) {
|
||||
if(save) {
|
||||
for(unsigned n = 0; n < sizeof(T); n++) idata[isize++] = value >> (n << 3);
|
||||
} else {
|
||||
enum { size = is_bool<T>::value ? 1 : sizeof(T) };
|
||||
if(mode == Save) {
|
||||
for(unsigned n = 0; n < size; n++) idata[isize++] = value >> (n << 3);
|
||||
} else if(mode == Load) {
|
||||
value = 0;
|
||||
for(unsigned n = 0; n < sizeof(T); n++) value |= idata[isize++] << (n << 3);
|
||||
for(unsigned n = 0; n < size; n++) value |= idata[isize++] << (n << 3);
|
||||
} else if(mode == Size) {
|
||||
isize += size;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,7 +57,7 @@ namespace nall {
|
|||
serializer& operator=(const serializer &s) {
|
||||
if(idata) delete[] idata;
|
||||
|
||||
save = s.save;
|
||||
mode = s.mode;
|
||||
idata = new uint8_t[s.icapacity];
|
||||
isize = s.isize;
|
||||
icapacity = s.icapacity;
|
||||
|
@ -67,15 +70,21 @@ namespace nall {
|
|||
operator=(s);
|
||||
}
|
||||
|
||||
serializer() {
|
||||
mode = Size;
|
||||
idata = 0;
|
||||
isize = 0;
|
||||
}
|
||||
|
||||
serializer(unsigned capacity) {
|
||||
save = true;
|
||||
mode = Save;
|
||||
idata = new(zeromemory) uint8_t[capacity];
|
||||
isize = 0;
|
||||
icapacity = capacity;
|
||||
}
|
||||
|
||||
serializer(const uint8_t *data, unsigned capacity) {
|
||||
save = false;
|
||||
mode = Load;
|
||||
idata = new uint8_t[capacity];
|
||||
isize = 0;
|
||||
icapacity = capacity;
|
||||
|
@ -83,11 +92,11 @@ namespace nall {
|
|||
}
|
||||
|
||||
~serializer() {
|
||||
delete[] idata;
|
||||
if(idata) delete[] idata;
|
||||
}
|
||||
|
||||
private:
|
||||
bool save;
|
||||
enum mode_t { Load, Save, Size } mode;
|
||||
uint8_t *idata;
|
||||
unsigned isize;
|
||||
unsigned icapacity;
|
||||
|
|
|
@ -26,6 +26,9 @@ namespace nall {
|
|||
template<> struct is_floating_point<double> { enum { value = true }; };
|
||||
template<> struct is_floating_point<long double> { enum { value = true }; };
|
||||
|
||||
template<typename T> struct is_bool { enum { value = false }; };
|
||||
template<> struct is_bool<bool> { enum { value = true }; };
|
||||
|
||||
template<typename T> struct is_void { enum { value = false }; };
|
||||
template<> struct is_void<void> { enum { value = true }; };
|
||||
|
||||
|
|
|
@ -224,7 +224,7 @@ public:
|
|||
}
|
||||
|
||||
if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) {
|
||||
if(format[i].type == XvRGB && format[i].bits_per_pixel == 16) {
|
||||
if(format[i].type == XvRGB && format[i].bits_per_pixel <= 16 && format[i].red_mask == 0xf800) {
|
||||
device.format = XvFormatRGB16;
|
||||
device.fourcc = format[i].id;
|
||||
break;
|
||||
|
@ -232,7 +232,7 @@ public:
|
|||
}
|
||||
|
||||
if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) {
|
||||
if(format[i].type == XvRGB && format[i].bits_per_pixel == 15) {
|
||||
if(format[i].type == XvRGB && format[i].bits_per_pixel <= 16 && format[i].red_mask == 0x7c00) {
|
||||
device.format = XvFormatRGB15;
|
||||
device.fourcc = format[i].id;
|
||||
break;
|
||||
|
@ -316,8 +316,8 @@ public:
|
|||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
memcpy(output, input, width * 4);
|
||||
input += 1024 - width;
|
||||
output += 1024 - width;
|
||||
input += 1024;
|
||||
output += 1024;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,14 @@ void MappedRAM::map(uint8 *source, unsigned length) {
|
|||
size_ = data_ && length > 0 ? length : -1U;
|
||||
}
|
||||
|
||||
void MappedRAM::copy(uint8 *data, unsigned size) {
|
||||
if(!data_) {
|
||||
size_ = (size & ~255) + ((bool)(size & 255) << 8);
|
||||
data_ = new(zeromemory) uint8[size_];
|
||||
}
|
||||
memcpy(data_, data, min(size_, size));
|
||||
}
|
||||
|
||||
void MappedRAM::write_protect(bool status) { write_protect_ = status; }
|
||||
uint8* MappedRAM::data() { return data_; }
|
||||
unsigned MappedRAM::size() const { return size_; }
|
||||
|
|
|
@ -39,7 +39,8 @@ private:
|
|||
|
||||
struct MappedRAM : Memory {
|
||||
inline void reset();
|
||||
inline void map(uint8 *source, unsigned length);
|
||||
inline void map(uint8*, unsigned);
|
||||
inline void copy(uint8*, unsigned);
|
||||
|
||||
inline void write_protect(bool status);
|
||||
inline uint8* data();
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#define SMEMORY_CPP
|
||||
namespace SNES {
|
||||
|
||||
sBus bus;
|
||||
|
||||
#include "system.cpp"
|
||||
#include "generic.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
|
|
@ -16,3 +16,5 @@ private:
|
|||
void map_generic();
|
||||
void map_generic_sram();
|
||||
};
|
||||
|
||||
extern sBus bus;
|
||||
|
|
|
@ -3,9 +3,12 @@
|
|||
#define BPPU_CPP
|
||||
namespace SNES {
|
||||
|
||||
bPPU ppu;
|
||||
|
||||
#include "memory/memory.cpp"
|
||||
#include "mmio/mmio.cpp"
|
||||
#include "render/render.cpp"
|
||||
#include "serialization.cpp"
|
||||
#include "bppu_mmio.cpp"
|
||||
#include "bppu_render.cpp"
|
||||
|
||||
void bPPU::enter() {
|
||||
while(true) {
|
||||
|
@ -31,9 +34,12 @@ void bPPU::enter() {
|
|||
add_clocks(640);
|
||||
|
||||
//H = 1152 (cache OBSEL)
|
||||
cache.oam_basesize = regs.oam_basesize;
|
||||
if(cache.oam_basesize != regs.oam_basesize) {
|
||||
cache.oam_basesize = regs.oam_basesize;
|
||||
sprite_list_valid = false;
|
||||
}
|
||||
cache.oam_nameselect = regs.oam_nameselect;
|
||||
cache.oam_tdaddr = regs.oam_tdaddr;
|
||||
cache.oam_tdaddr = regs.oam_tdaddr;
|
||||
add_clocks(lineclocks() - 1152); //seek to start of next scanline
|
||||
}
|
||||
}
|
||||
|
@ -99,7 +105,7 @@ void bPPU::power() {
|
|||
region = (system.region() == System::NTSC ? 0 : 1); //0 = NTSC, 1 = PAL
|
||||
|
||||
//$2100
|
||||
regs.display_disabled = 1;
|
||||
regs.display_disabled = true;
|
||||
regs.display_brightness = 15;
|
||||
|
||||
//$2101
|
||||
|
@ -306,11 +312,15 @@ void bPPU::reset() {
|
|||
PPU::reset();
|
||||
PPU::frame();
|
||||
|
||||
//$2100
|
||||
regs.display_disabled = true;
|
||||
|
||||
display.interlace = false;
|
||||
display.overscan = false;
|
||||
regs.scanlines = 224;
|
||||
|
||||
memset(sprite_list, 0, sizeof(sprite_list));
|
||||
sprite_list_valid = false;
|
||||
|
||||
//open bus support
|
||||
regs.ppu1_mdr = 0xff;
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
class bPPU : public PPU {
|
||||
public:
|
||||
#include "memory/memory.hpp"
|
||||
#include "mmio/mmio.hpp"
|
||||
#include "render/render.hpp"
|
||||
|
||||
void enter();
|
||||
void add_clocks(unsigned clocks);
|
||||
|
||||
|
@ -15,134 +19,6 @@ public:
|
|||
bool overscan;
|
||||
} display;
|
||||
|
||||
struct {
|
||||
//open bus support
|
||||
uint8 ppu1_mdr, ppu2_mdr;
|
||||
|
||||
//bg line counters
|
||||
uint16 bg_y[4];
|
||||
|
||||
//$2100
|
||||
bool display_disabled;
|
||||
uint8 display_brightness;
|
||||
|
||||
//$2101
|
||||
uint8 oam_basesize;
|
||||
uint8 oam_nameselect;
|
||||
uint16 oam_tdaddr;
|
||||
|
||||
//$2102-$2103
|
||||
uint16 oam_baseaddr;
|
||||
uint16 oam_addr;
|
||||
bool oam_priority;
|
||||
uint8 oam_firstsprite;
|
||||
|
||||
//$2104
|
||||
uint8 oam_latchdata;
|
||||
|
||||
//$2105
|
||||
bool bg_tilesize[4];
|
||||
bool bg3_priority;
|
||||
uint8 bg_mode;
|
||||
|
||||
//$2106
|
||||
uint8 mosaic_size;
|
||||
bool mosaic_enabled[4];
|
||||
uint16 mosaic_countdown;
|
||||
|
||||
//$2107-$210a
|
||||
uint16 bg_scaddr[4];
|
||||
uint8 bg_scsize[4];
|
||||
|
||||
//$210b-$210c
|
||||
uint16 bg_tdaddr[4];
|
||||
|
||||
//$210d-$2114
|
||||
uint8 bg_ofslatch;
|
||||
uint16 m7_hofs, m7_vofs;
|
||||
uint16 bg_hofs[4];
|
||||
uint16 bg_vofs[4];
|
||||
|
||||
//$2115
|
||||
bool vram_incmode;
|
||||
uint8 vram_mapping;
|
||||
uint8 vram_incsize;
|
||||
|
||||
//$2116-$2117
|
||||
uint16 vram_addr;
|
||||
|
||||
//$211a
|
||||
uint8 mode7_repeat;
|
||||
bool mode7_vflip;
|
||||
bool mode7_hflip;
|
||||
|
||||
//$211b-$2120
|
||||
uint8 m7_latch;
|
||||
uint16 m7a, m7b, m7c, m7d, m7x, m7y;
|
||||
|
||||
//$2121
|
||||
uint16 cgram_addr;
|
||||
|
||||
//$2122
|
||||
uint8 cgram_latchdata;
|
||||
|
||||
//$2123-$2125
|
||||
bool window1_enabled[6];
|
||||
bool window1_invert [6];
|
||||
bool window2_enabled[6];
|
||||
bool window2_invert [6];
|
||||
|
||||
//$2126-$2129
|
||||
uint8 window1_left, window1_right;
|
||||
uint8 window2_left, window2_right;
|
||||
|
||||
//$212a-$212b
|
||||
uint8 window_mask[6];
|
||||
|
||||
//$212c-$212d
|
||||
bool bg_enabled[5], bgsub_enabled[5];
|
||||
|
||||
//$212e-$212f
|
||||
bool window_enabled[5], sub_window_enabled[5];
|
||||
|
||||
//$2130
|
||||
uint8 color_mask, colorsub_mask;
|
||||
bool addsub_mode;
|
||||
bool direct_color;
|
||||
|
||||
//$2131
|
||||
bool color_mode, color_halve;
|
||||
bool color_enabled[6];
|
||||
|
||||
//$2132
|
||||
uint8 color_r, color_g, color_b;
|
||||
uint16 color_rgb;
|
||||
|
||||
//$2133
|
||||
//overscan and interlace are checked once per frame to
|
||||
//determine if entire frame should be interlaced/non-interlace
|
||||
//and overscan adjusted. therefore, the variables act sort of
|
||||
//like a buffer, but they do still affect internal rendering
|
||||
bool mode7_extbg;
|
||||
bool pseudo_hires;
|
||||
bool overscan;
|
||||
uint16 scanlines;
|
||||
bool oam_interlace;
|
||||
bool interlace;
|
||||
|
||||
//$2137
|
||||
uint16 hcounter, vcounter;
|
||||
bool latch_hcounter, latch_vcounter;
|
||||
bool counters_latched;
|
||||
|
||||
//$2139-$213a
|
||||
uint16 vram_readbuffer;
|
||||
|
||||
//$213e
|
||||
bool time_over, range_over;
|
||||
uint16 oam_itemcount, oam_tilecount;
|
||||
} regs;
|
||||
|
||||
struct {
|
||||
//$2101
|
||||
uint8 oam_basesize;
|
||||
|
@ -154,87 +30,6 @@ public:
|
|||
alwaysinline bool overscan() const { return display.overscan; }
|
||||
alwaysinline bool hires() const { return (regs.pseudo_hires || regs.bg_mode == 5 || regs.bg_mode == 6); }
|
||||
|
||||
uint16 get_vram_address();
|
||||
uint8 vram_mmio_read (uint16 addr);
|
||||
void vram_mmio_write (uint16 addr, uint8 data);
|
||||
uint8 oam_mmio_read (uint16 addr);
|
||||
void oam_mmio_write (uint16 addr, uint8 data);
|
||||
uint8 cgram_mmio_read (uint16 addr);
|
||||
void cgram_mmio_write(uint16 addr, uint8 data);
|
||||
|
||||
void mmio_w2100(uint8 value); //INIDISP
|
||||
void mmio_w2101(uint8 value); //OBSEL
|
||||
void mmio_w2102(uint8 value); //OAMADDL
|
||||
void mmio_w2103(uint8 value); //OAMADDH
|
||||
void mmio_w2104(uint8 value); //OAMDATA
|
||||
void mmio_w2105(uint8 value); //BGMODE
|
||||
void mmio_w2106(uint8 value); //MOSAIC
|
||||
void mmio_w2107(uint8 value); //BG1SC
|
||||
void mmio_w2108(uint8 value); //BG2SC
|
||||
void mmio_w2109(uint8 value); //BG3SC
|
||||
void mmio_w210a(uint8 value); //BG4SC
|
||||
void mmio_w210b(uint8 value); //BG12NBA
|
||||
void mmio_w210c(uint8 value); //BG34NBA
|
||||
void mmio_w210d(uint8 value); //BG1HOFS
|
||||
void mmio_w210e(uint8 value); //BG1VOFS
|
||||
void mmio_w210f(uint8 value); //BG2HOFS
|
||||
void mmio_w2110(uint8 value); //BG2VOFS
|
||||
void mmio_w2111(uint8 value); //BG3HOFS
|
||||
void mmio_w2112(uint8 value); //BG3VOFS
|
||||
void mmio_w2113(uint8 value); //BG4HOFS
|
||||
void mmio_w2114(uint8 value); //BG4VOFS
|
||||
void mmio_w2115(uint8 value); //VMAIN
|
||||
void mmio_w2116(uint8 value); //VMADDL
|
||||
void mmio_w2117(uint8 value); //VMADDH
|
||||
void mmio_w2118(uint8 value); //VMDATAL
|
||||
void mmio_w2119(uint8 value); //VMDATAH
|
||||
void mmio_w211a(uint8 value); //M7SEL
|
||||
void mmio_w211b(uint8 value); //M7A
|
||||
void mmio_w211c(uint8 value); //M7B
|
||||
void mmio_w211d(uint8 value); //M7C
|
||||
void mmio_w211e(uint8 value); //M7D
|
||||
void mmio_w211f(uint8 value); //M7X
|
||||
void mmio_w2120(uint8 value); //M7Y
|
||||
void mmio_w2121(uint8 value); //CGADD
|
||||
void mmio_w2122(uint8 value); //CGDATA
|
||||
void mmio_w2123(uint8 value); //W12SEL
|
||||
void mmio_w2124(uint8 value); //W34SEL
|
||||
void mmio_w2125(uint8 value); //WOBJSEL
|
||||
void mmio_w2126(uint8 value); //WH0
|
||||
void mmio_w2127(uint8 value); //WH1
|
||||
void mmio_w2128(uint8 value); //WH2
|
||||
void mmio_w2129(uint8 value); //WH3
|
||||
void mmio_w212a(uint8 value); //WBGLOG
|
||||
void mmio_w212b(uint8 value); //WOBJLOG
|
||||
void mmio_w212c(uint8 value); //TM
|
||||
void mmio_w212d(uint8 value); //TS
|
||||
void mmio_w212e(uint8 value); //TMW
|
||||
void mmio_w212f(uint8 value); //TSW
|
||||
void mmio_w2130(uint8 value); //CGWSEL
|
||||
void mmio_w2131(uint8 value); //CGADDSUB
|
||||
void mmio_w2132(uint8 value); //COLDATA
|
||||
void mmio_w2133(uint8 value); //SETINI
|
||||
uint8 mmio_r2134(); //MPYL
|
||||
uint8 mmio_r2135(); //MPYM
|
||||
uint8 mmio_r2136(); //MPYH
|
||||
uint8 mmio_r2137(); //SLHV
|
||||
uint8 mmio_r2138(); //OAMDATAREAD
|
||||
uint8 mmio_r2139(); //VMDATALREAD
|
||||
uint8 mmio_r213a(); //VMDATAHREAD
|
||||
uint8 mmio_r213b(); //CGDATAREAD
|
||||
uint8 mmio_r213c(); //OPHCT
|
||||
uint8 mmio_r213d(); //OPVCT
|
||||
uint8 mmio_r213e(); //STAT77
|
||||
uint8 mmio_r213f(); //STAT78
|
||||
|
||||
uint8 mmio_read(unsigned addr);
|
||||
void mmio_write(unsigned addr, uint8 data);
|
||||
|
||||
void latch_counters();
|
||||
|
||||
//PPU render functions
|
||||
#include "bppu_render.hpp"
|
||||
|
||||
uint16 light_table_b[16][32];
|
||||
uint16 light_table_gr[16][32 * 32];
|
||||
uint16 mosaic_table[16][4096];
|
||||
|
@ -253,3 +48,5 @@ public:
|
|||
bPPU();
|
||||
~bPPU();
|
||||
};
|
||||
|
||||
extern bPPU ppu;
|
||||
|
|
|
@ -1,150 +0,0 @@
|
|||
#ifdef BPPU_CPP
|
||||
|
||||
#include "bppu_render_cache.cpp"
|
||||
#include "bppu_render_windows.cpp"
|
||||
#include "bppu_render_bg.cpp"
|
||||
#include "bppu_render_oam.cpp"
|
||||
#include "bppu_render_mode7.cpp"
|
||||
#include "bppu_render_addsub.cpp"
|
||||
#include "bppu_render_line.cpp"
|
||||
|
||||
//this function can be used to disable BG / OAM layer rendering (currently unused)
|
||||
bool bPPU::render_enabled(uint8 bg, uint8 pri) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
Mode 0: ->
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
|
||||
BG4B, BG3B, OAM0, BG4A, BG3A, OAM1, BG2B, BG1B, OAM2, BG2A, BG1A, OAM3
|
||||
*/
|
||||
void bPPU::render_line_mode0() {
|
||||
render_line_bg(BG1, COLORDEPTH_4, 8, 11);
|
||||
render_line_bg(BG2, COLORDEPTH_4, 7, 10);
|
||||
render_line_bg(BG3, COLORDEPTH_4, 2, 5);
|
||||
render_line_bg(BG4, COLORDEPTH_4, 1, 4);
|
||||
render_line_oam(3, 6, 9, 12);
|
||||
}
|
||||
|
||||
/*
|
||||
Mode 1 (pri=1): ->
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
|
||||
BG3B, OAM0, OAM1, BG2B, BG1B, OAM2, BG2A, BG1A, OAM3, BG3A
|
||||
|
||||
Mode 1 (pri=0): ->
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
|
||||
BG3B, OAM0, BG3A, OAM1, BG2B, BG1B, OAM2, BG2A, BG1A, OAM3
|
||||
*/
|
||||
void bPPU::render_line_mode1() {
|
||||
if(regs.bg3_priority) {
|
||||
render_line_bg(BG1, COLORDEPTH_16, 5, 8);
|
||||
render_line_bg(BG2, COLORDEPTH_16, 4, 7);
|
||||
render_line_bg(BG3, COLORDEPTH_4, 1, 10);
|
||||
render_line_oam(2, 3, 6, 9);
|
||||
} else {
|
||||
render_line_bg(BG1, COLORDEPTH_16, 6, 9);
|
||||
render_line_bg(BG2, COLORDEPTH_16, 5, 8);
|
||||
render_line_bg(BG3, COLORDEPTH_4, 1, 3);
|
||||
render_line_oam(2, 4, 7, 10);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Mode 2: ->
|
||||
1, 2, 3, 4, 5, 6, 7, 8
|
||||
BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3
|
||||
*/
|
||||
void bPPU::render_line_mode2() {
|
||||
render_line_bg(BG1, COLORDEPTH_16, 3, 7);
|
||||
render_line_bg(BG2, COLORDEPTH_16, 1, 5);
|
||||
render_line_oam(2, 4, 6, 8);
|
||||
}
|
||||
|
||||
/*
|
||||
Mode 3: ->
|
||||
1, 2, 3, 4, 5, 6, 7, 8
|
||||
BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3
|
||||
*/
|
||||
void bPPU::render_line_mode3() {
|
||||
render_line_bg(BG1, COLORDEPTH_256, 3, 7);
|
||||
render_line_bg(BG2, COLORDEPTH_16, 1, 5);
|
||||
render_line_oam(2, 4, 6, 8);
|
||||
}
|
||||
|
||||
/*
|
||||
Mode 4: ->
|
||||
1, 2, 3, 4, 5, 6, 7, 8
|
||||
BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3
|
||||
*/
|
||||
void bPPU::render_line_mode4() {
|
||||
render_line_bg(BG1, COLORDEPTH_256, 3, 7);
|
||||
render_line_bg(BG2, COLORDEPTH_4, 1, 5);
|
||||
render_line_oam(2, 4, 6, 8);
|
||||
}
|
||||
|
||||
/*
|
||||
Mode 5: ->
|
||||
1, 2, 3, 4, 5, 6, 7, 8
|
||||
BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3
|
||||
*/
|
||||
void bPPU::render_line_mode5() {
|
||||
render_line_bg(BG1, COLORDEPTH_16, 3, 7);
|
||||
render_line_bg(BG2, COLORDEPTH_4, 1, 5);
|
||||
render_line_oam(2, 4, 6, 8);
|
||||
}
|
||||
|
||||
/*
|
||||
Mode 6: ->
|
||||
1, 2, 3, 4, 5, 6
|
||||
OAM0, BG1B, OAM1, OAM2, BG1A, OAM3
|
||||
*/
|
||||
void bPPU::render_line_mode6() {
|
||||
render_line_bg(BG1, COLORDEPTH_16, 2, 5);
|
||||
render_line_oam(1, 3, 4, 6);
|
||||
}
|
||||
|
||||
/*
|
||||
Mode7: ->
|
||||
1, 2, 3, 4, 5
|
||||
OAM0, BG1n, OAM1, OAM2, OAM3
|
||||
|
||||
Mode 7 EXTBG: ->
|
||||
1, 2, 3, 4, 5, 6, 7
|
||||
BG2B, OAM0, BG1n, OAM1, BG2A, OAM2, OAM3
|
||||
*/
|
||||
void bPPU::render_line_mode7() {
|
||||
if(regs.mode7_extbg == false) {
|
||||
render_line_mode7(BG1, 2, 2);
|
||||
render_line_oam(1, 3, 4, 5);
|
||||
} else {
|
||||
render_line_mode7(BG1, 3, 3);
|
||||
render_line_mode7(BG2, 1, 5);
|
||||
render_line_oam(2, 4, 6, 7);
|
||||
}
|
||||
}
|
||||
|
||||
void bPPU::render_line() {
|
||||
if(regs.display_disabled == true) {
|
||||
render_line_clear();
|
||||
return;
|
||||
}
|
||||
|
||||
flush_pixel_cache();
|
||||
build_window_tables(COL);
|
||||
update_bg_info();
|
||||
|
||||
switch(regs.bg_mode) {
|
||||
case 0: render_line_mode0(); break;
|
||||
case 1: render_line_mode1(); break;
|
||||
case 2: render_line_mode2(); break;
|
||||
case 3: render_line_mode3(); break;
|
||||
case 4: render_line_mode4(); break;
|
||||
case 5: render_line_mode5(); break;
|
||||
case 6: render_line_mode6(); break;
|
||||
case 7: render_line_mode7(); break;
|
||||
}
|
||||
|
||||
render_line_output();
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,151 +0,0 @@
|
|||
#ifdef BPPU_CPP
|
||||
|
||||
#define render_bg_tile_line_2bpp(mask) \
|
||||
col = !!(d0 & mask) << 0; \
|
||||
col += !!(d1 & mask) << 1; \
|
||||
*dest++ = col
|
||||
|
||||
#define render_bg_tile_line_4bpp(mask) \
|
||||
col = !!(d0 & mask) << 0; \
|
||||
col += !!(d1 & mask) << 1; \
|
||||
col += !!(d2 & mask) << 2; \
|
||||
col += !!(d3 & mask) << 3; \
|
||||
*dest++ = col
|
||||
|
||||
#define render_bg_tile_line_8bpp(mask) \
|
||||
col = !!(d0 & mask) << 0; \
|
||||
col += !!(d1 & mask) << 1; \
|
||||
col += !!(d2 & mask) << 2; \
|
||||
col += !!(d3 & mask) << 3; \
|
||||
col += !!(d4 & mask) << 4; \
|
||||
col += !!(d5 & mask) << 5; \
|
||||
col += !!(d6 & mask) << 6; \
|
||||
col += !!(d7 & mask) << 7; \
|
||||
*dest++ = col
|
||||
|
||||
void bPPU::render_bg_tile(uint8 color_depth, uint16 tile_num) {
|
||||
uint8 mask, d0, d1, d2, d3, d4, d5, d6, d7, col;
|
||||
int x, y;
|
||||
uint32 pos;
|
||||
uint8 *dest;
|
||||
|
||||
switch(color_depth) {
|
||||
case COLORDEPTH_4: {
|
||||
dest = (uint8*)bg_tiledata[TILE_2BIT] + tile_num * 64;
|
||||
pos = tile_num * 16;
|
||||
y = 8;
|
||||
while(y--) {
|
||||
d0 = memory::vram[pos ];
|
||||
d1 = memory::vram[pos + 1];
|
||||
render_bg_tile_line_2bpp(0x80);
|
||||
render_bg_tile_line_2bpp(0x40);
|
||||
render_bg_tile_line_2bpp(0x20);
|
||||
render_bg_tile_line_2bpp(0x10);
|
||||
render_bg_tile_line_2bpp(0x08);
|
||||
render_bg_tile_line_2bpp(0x04);
|
||||
render_bg_tile_line_2bpp(0x02);
|
||||
render_bg_tile_line_2bpp(0x01);
|
||||
pos += 2;
|
||||
}
|
||||
bg_tiledata_state[TILE_2BIT][tile_num] = 0;
|
||||
} break;
|
||||
|
||||
case COLORDEPTH_16: {
|
||||
dest = (uint8*)bg_tiledata[TILE_4BIT] + tile_num * 64;
|
||||
pos = tile_num * 32;
|
||||
y = 8;
|
||||
while(y--) {
|
||||
d0 = memory::vram[pos ];
|
||||
d1 = memory::vram[pos + 1];
|
||||
d2 = memory::vram[pos + 16];
|
||||
d3 = memory::vram[pos + 17];
|
||||
render_bg_tile_line_4bpp(0x80);
|
||||
render_bg_tile_line_4bpp(0x40);
|
||||
render_bg_tile_line_4bpp(0x20);
|
||||
render_bg_tile_line_4bpp(0x10);
|
||||
render_bg_tile_line_4bpp(0x08);
|
||||
render_bg_tile_line_4bpp(0x04);
|
||||
render_bg_tile_line_4bpp(0x02);
|
||||
render_bg_tile_line_4bpp(0x01);
|
||||
pos += 2;
|
||||
}
|
||||
bg_tiledata_state[TILE_4BIT][tile_num] = 0;
|
||||
} break;
|
||||
|
||||
case COLORDEPTH_256: {
|
||||
dest = (uint8*)bg_tiledata[TILE_8BIT] + tile_num * 64;
|
||||
pos = tile_num * 64;
|
||||
y = 8;
|
||||
while(y--) {
|
||||
d0 = memory::vram[pos ];
|
||||
d1 = memory::vram[pos + 1];
|
||||
d2 = memory::vram[pos + 16];
|
||||
d3 = memory::vram[pos + 17];
|
||||
d4 = memory::vram[pos + 32];
|
||||
d5 = memory::vram[pos + 33];
|
||||
d6 = memory::vram[pos + 48];
|
||||
d7 = memory::vram[pos + 49];
|
||||
render_bg_tile_line_8bpp(0x80);
|
||||
render_bg_tile_line_8bpp(0x40);
|
||||
render_bg_tile_line_8bpp(0x20);
|
||||
render_bg_tile_line_8bpp(0x10);
|
||||
render_bg_tile_line_8bpp(0x08);
|
||||
render_bg_tile_line_8bpp(0x04);
|
||||
render_bg_tile_line_8bpp(0x02);
|
||||
render_bg_tile_line_8bpp(0x01);
|
||||
pos += 2;
|
||||
}
|
||||
bg_tiledata_state[TILE_8BIT][tile_num] = 0;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
#undef render_bg_tile_line_2bpp
|
||||
#undef render_bg_tile_line_4bpp
|
||||
#undef render_bg_tile_line_8bpp
|
||||
|
||||
void bPPU::flush_pixel_cache() {
|
||||
uint16 main = get_palette(0);
|
||||
uint16 sub = (regs.pseudo_hires || regs.bg_mode == 5 || regs.bg_mode == 6)
|
||||
? main
|
||||
: regs.color_rgb;
|
||||
|
||||
unsigned i = 255;
|
||||
do {
|
||||
pixel_cache[i].src_main = main;
|
||||
pixel_cache[i].src_sub = sub;
|
||||
pixel_cache[i].bg_main = BACK;
|
||||
pixel_cache[i].bg_sub = BACK;
|
||||
pixel_cache[i].ce_main = false;
|
||||
pixel_cache[i].ce_sub = false;
|
||||
pixel_cache[i].pri_main = 0;
|
||||
pixel_cache[i].pri_sub = 0;
|
||||
} while(i--);
|
||||
}
|
||||
|
||||
void bPPU::alloc_tiledata_cache() {
|
||||
bg_tiledata[TILE_2BIT] = new(zeromemory) uint8_t[262144];
|
||||
bg_tiledata[TILE_4BIT] = new(zeromemory) uint8_t[131072];
|
||||
bg_tiledata[TILE_8BIT] = new(zeromemory) uint8_t[ 65536];
|
||||
bg_tiledata_state[TILE_2BIT] = new(zeromemory) uint8_t[ 4096];
|
||||
bg_tiledata_state[TILE_4BIT] = new(zeromemory) uint8_t[ 2048];
|
||||
bg_tiledata_state[TILE_8BIT] = new(zeromemory) uint8_t[ 1024];
|
||||
}
|
||||
|
||||
//marks all tiledata cache entries as dirty
|
||||
void bPPU::flush_tiledata_cache() {
|
||||
for(unsigned i = 0; i < 4096; i++) bg_tiledata_state[TILE_2BIT][i] = 1;
|
||||
for(unsigned i = 0; i < 2048; i++) bg_tiledata_state[TILE_4BIT][i] = 1;
|
||||
for(unsigned i = 0; i < 1024; i++) bg_tiledata_state[TILE_8BIT][i] = 1;
|
||||
}
|
||||
|
||||
void bPPU::free_tiledata_cache() {
|
||||
delete[] bg_tiledata[TILE_2BIT];
|
||||
delete[] bg_tiledata[TILE_4BIT];
|
||||
delete[] bg_tiledata[TILE_8BIT];
|
||||
delete[] bg_tiledata_state[TILE_2BIT];
|
||||
delete[] bg_tiledata_state[TILE_4BIT];
|
||||
delete[] bg_tiledata_state[TILE_8BIT];
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,99 +0,0 @@
|
|||
#ifdef BPPU_CPP
|
||||
|
||||
void bPPU::build_window_table(uint8 bg, bool mainscreen) {
|
||||
uint8 set = 1, clr = 0;
|
||||
uint8 *wtbl = (mainscreen == true) ? window[bg].main : window[bg].sub;
|
||||
|
||||
if(bg != COL) {
|
||||
if(mainscreen == true && regs.window_enabled[bg] == false) {
|
||||
memset(wtbl, 0, 256);
|
||||
return;
|
||||
}
|
||||
if(mainscreen == false && regs.sub_window_enabled[bg] == false) {
|
||||
memset(wtbl, 0, 256);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
switch((mainscreen == true) ? regs.color_mask : regs.colorsub_mask) {
|
||||
case 0: { //always
|
||||
memset(wtbl, 1, 256);
|
||||
} return;
|
||||
|
||||
case 3: { //never
|
||||
memset(wtbl, 0, 256);
|
||||
} return;
|
||||
|
||||
case 1: { //inside window only
|
||||
set = 1;
|
||||
clr = 0;
|
||||
} break;
|
||||
|
||||
case 2: { //outside window only
|
||||
set = 0;
|
||||
clr = 1;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
uint16 window1_left = regs.window1_left;
|
||||
uint16 window1_right = regs.window1_right;
|
||||
uint16 window2_left = regs.window2_left;
|
||||
uint16 window2_right = regs.window2_right;
|
||||
|
||||
if(regs.window1_enabled[bg] == false && regs.window2_enabled[bg] == false) {
|
||||
memset(wtbl, clr, 256);
|
||||
return;
|
||||
}
|
||||
|
||||
if(regs.window1_enabled[bg] == true && regs.window2_enabled[bg] == false) {
|
||||
if(regs.window1_invert[bg] == true)swap(set, clr);
|
||||
for(int x = 0; x < 256; x++) {
|
||||
wtbl[x] = (x >= window1_left && x <= window1_right) ? set : clr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(regs.window1_enabled[bg] == false && regs.window2_enabled[bg] == true) {
|
||||
if(regs.window2_invert[bg] == true)swap(set, clr);
|
||||
for(int x = 0; x < 256; x++) {
|
||||
wtbl[x] = (x >= window2_left && x <= window2_right) ? set : clr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//(regs.window1_enabled[bg] == true && regs.window2_enabled[bg] == true)
|
||||
int w1_mask, w2_mask; //1 = masked, 0 = not masked
|
||||
|
||||
for(int x = 0; x < 256; x++) {
|
||||
w1_mask = (x >= window1_left && x <= window1_right);
|
||||
if(regs.window1_invert[bg] == true)w1_mask = !w1_mask;
|
||||
|
||||
w2_mask = (x >= window2_left && x <= window2_right);
|
||||
if(regs.window2_invert[bg] == true)w2_mask = !w2_mask;
|
||||
|
||||
switch(regs.window_mask[bg]) {
|
||||
case 0: { //WINDOWMASK_OR:
|
||||
wtbl[x] = ((w1_mask | w2_mask) == 1) ? set : clr;
|
||||
} break;
|
||||
|
||||
case 1: { //WINDOWMASK_AND:
|
||||
wtbl[x] = ((w1_mask & w2_mask) == 1) ? set : clr;
|
||||
} break;
|
||||
|
||||
case 2: { //WINDOWMASK_XOR:
|
||||
wtbl[x] = ((w1_mask ^ w2_mask) == 1) ? set : clr;
|
||||
} break;
|
||||
|
||||
case 3: { //WINDOWMASK_XNOR:
|
||||
wtbl[x] = ((w1_mask ^ w2_mask) == 0) ? set : clr;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bPPU::build_window_tables(uint8 bg) {
|
||||
build_window_table(bg, true);
|
||||
build_window_table(bg, false);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,173 @@
|
|||
#ifdef BPPU_CPP
|
||||
|
||||
void bPPU::latch_counters() {
|
||||
regs.hcounter = cpu.hdot();
|
||||
regs.vcounter = cpu.vcounter();
|
||||
regs.counters_latched = true;
|
||||
}
|
||||
|
||||
uint16 bPPU::get_vram_address() {
|
||||
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);
|
||||
}
|
||||
|
||||
//NOTE: all VRAM writes during active display are invalid. Unlike OAM and CGRAM, they will
|
||||
//not be written anywhere at all. The below address ranges for where writes are invalid have
|
||||
//been validated on hardware, as has the edge case where the S-CPU MDR can be written if the
|
||||
//write occurs during the very last clock cycle of vblank.
|
||||
|
||||
uint8 bPPU::vram_mmio_read(uint16 addr) {
|
||||
uint8 data;
|
||||
|
||||
if(regs.display_disabled == true) {
|
||||
data = memory::vram[addr];
|
||||
} else {
|
||||
uint16 v = cpu.vcounter();
|
||||
uint16 h = cpu.hcounter();
|
||||
uint16 ls = ((system.region() == System::NTSC ? 525 : 625) >> 1) - 1;
|
||||
if(interlace() && !cpu.field()) ls++;
|
||||
|
||||
if(v == ls && h == 1362) {
|
||||
data = 0x00;
|
||||
} else if(v < (!overscan() ? 224 : 239)) {
|
||||
data = 0x00;
|
||||
} else if(v == (!overscan() ? 224 : 239)) {
|
||||
if(h == 1362) {
|
||||
data = memory::vram[addr];
|
||||
} else {
|
||||
data = 0x00;
|
||||
}
|
||||
} else {
|
||||
data = memory::vram[addr];
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void bPPU::vram_mmio_write(uint16 addr, uint8 data) {
|
||||
if(regs.display_disabled == true) {
|
||||
memory::vram[addr] = data;
|
||||
} else {
|
||||
uint16 v = cpu.vcounter();
|
||||
uint16 h = cpu.hcounter();
|
||||
if(v == 0) {
|
||||
if(h <= 4) {
|
||||
memory::vram[addr] = data;
|
||||
} else if(h == 6) {
|
||||
memory::vram[addr] = cpu.regs.mdr;
|
||||
} else {
|
||||
//no write
|
||||
}
|
||||
} else if(v < (!overscan() ? 225 : 240)) {
|
||||
//no write
|
||||
} else if(v == (!overscan() ? 225 : 240)) {
|
||||
if(h <= 4) {
|
||||
//no write
|
||||
} else {
|
||||
memory::vram[addr] = data;
|
||||
}
|
||||
} else {
|
||||
memory::vram[addr] = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//NOTE: OAM accesses during active display are rerouted to 0x0218 ... this can be considered
|
||||
//a hack. The actual address varies during rendering, as the S-PPU reads in data itself for
|
||||
//processing. Unfortunately, we have yet to determine how this works. The algorithm cannot be
|
||||
//reverse engineered using a scanline renderer such as this, and at this time, there does not
|
||||
//exist a more accurate SNES PPU emulator to work from. The only known game to actually access
|
||||
//OAM during active display is Uniracers. It expects accesses to map to offset 0x0218.
|
||||
//It was decided by public consensus to map writes to this address to match Uniracers, primarily
|
||||
//because it is the only game observed to do this, but also because mapping to this address does
|
||||
//not contradict any of our findings, because we have no findings whatsoever on this behavior.
|
||||
//Think of this what you will, I openly admit that this is a hack. But it is more accurate than
|
||||
//writing to the 'expected' address set by $2102,$2103, and will catch problems in software that
|
||||
//accidentally accesses OAM during active display by virtue of not returning the expected data.
|
||||
|
||||
uint8 bPPU::oam_mmio_read(uint16 addr) {
|
||||
addr &= 0x03ff;
|
||||
if(addr & 0x0200) addr &= 0x021f;
|
||||
uint8 data;
|
||||
|
||||
if(regs.display_disabled == true) {
|
||||
data = memory::oam[addr];
|
||||
} else {
|
||||
if(cpu.vcounter() < (!overscan() ? 225 : 240)) {
|
||||
data = memory::oam[0x0218];
|
||||
} else {
|
||||
data = memory::oam[addr];
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void bPPU::oam_mmio_write(uint16 addr, uint8 data) {
|
||||
addr &= 0x03ff;
|
||||
if(addr & 0x0200) addr &= 0x021f;
|
||||
|
||||
sprite_list_valid = false;
|
||||
|
||||
if(regs.display_disabled == true) {
|
||||
memory::oam[addr] = data;
|
||||
} else {
|
||||
if(cpu.vcounter() < (!overscan() ? 225 : 240)) {
|
||||
memory::oam[0x0218] = data;
|
||||
} else {
|
||||
memory::oam[addr] = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//NOTE: CGRAM writes during hblank are valid. During active display, the actual address the
|
||||
//data is written to varies, as the S-PPU itself changes the address. Like OAM, we do not know
|
||||
//the exact algorithm used, but we have zero known examples of any commercial software that
|
||||
//attempts to do this. Therefore, the addresses are mapped to 0x01ff. There is nothing special
|
||||
//about this address, it is simply more accurate to invalidate the 'expected' address than not.
|
||||
|
||||
uint8 bPPU::cgram_mmio_read(uint16 addr) {
|
||||
addr &= 0x01ff;
|
||||
uint8 data;
|
||||
|
||||
if(regs.display_disabled == true) {
|
||||
data = memory::cgram[addr];
|
||||
} else {
|
||||
uint16 v = cpu.vcounter();
|
||||
uint16 h = cpu.hcounter();
|
||||
if(v < (!overscan() ? 225 : 240) && h >= 128 && h < 1096) {
|
||||
data = memory::cgram[0x01ff] & 0x7f;
|
||||
} else {
|
||||
data = memory::cgram[addr];
|
||||
}
|
||||
}
|
||||
|
||||
if(addr & 1) data &= 0x7f;
|
||||
return data;
|
||||
}
|
||||
|
||||
void bPPU::cgram_mmio_write(uint16 addr, uint8 data) {
|
||||
addr &= 0x01ff;
|
||||
if(addr & 1) data &= 0x7f;
|
||||
|
||||
if(regs.display_disabled == true) {
|
||||
memory::cgram[addr] = data;
|
||||
} else {
|
||||
uint16 v = cpu.vcounter();
|
||||
uint16 h = cpu.hcounter();
|
||||
if(v < (!overscan() ? 225 : 240) && h >= 128 && h < 1096) {
|
||||
memory::cgram[0x01ff] = data & 0x7f;
|
||||
} else {
|
||||
memory::cgram[addr] = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,7 @@
|
|||
uint16 get_vram_address();
|
||||
uint8 vram_mmio_read(uint16 addr);
|
||||
void vram_mmio_write(uint16 addr, uint8 data);
|
||||
uint8 oam_mmio_read(uint16 addr);
|
||||
void oam_mmio_write(uint16 addr, uint8 data);
|
||||
uint8 cgram_mmio_read(uint16 addr);
|
||||
void cgram_mmio_write(uint16 addr, uint8 data);
|
|
@ -1,173 +1,5 @@
|
|||
#ifdef BPPU_CPP
|
||||
|
||||
void bPPU::latch_counters() {
|
||||
regs.hcounter = cpu.hdot();
|
||||
regs.vcounter = cpu.vcounter();
|
||||
regs.counters_latched = true;
|
||||
}
|
||||
|
||||
uint16 bPPU::get_vram_address() {
|
||||
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);
|
||||
}
|
||||
|
||||
//NOTE: all VRAM writes during active display are invalid. Unlike OAM and CGRAM, they will
|
||||
//not be written anywhere at all. The below address ranges for where writes are invalid have
|
||||
//been validated on hardware, as has the edge case where the S-CPU MDR can be written if the
|
||||
//write occurs during the very last clock cycle of vblank.
|
||||
|
||||
uint8 bPPU::vram_mmio_read(uint16 addr) {
|
||||
uint8 data;
|
||||
|
||||
if(regs.display_disabled == true) {
|
||||
data = memory::vram[addr];
|
||||
} else {
|
||||
uint16 v = cpu.vcounter();
|
||||
uint16 h = cpu.hcounter();
|
||||
uint16 ls = ((system.region() == System::NTSC ? 525 : 625) >> 1) - 1;
|
||||
if(interlace() && !cpu.field()) ls++;
|
||||
|
||||
if(v == ls && h == 1362) {
|
||||
data = 0x00;
|
||||
} else if(v < (!overscan() ? 224 : 239)) {
|
||||
data = 0x00;
|
||||
} else if(v == (!overscan() ? 224 : 239)) {
|
||||
if(h == 1362) {
|
||||
data = memory::vram[addr];
|
||||
} else {
|
||||
data = 0x00;
|
||||
}
|
||||
} else {
|
||||
data = memory::vram[addr];
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void bPPU::vram_mmio_write(uint16 addr, uint8 data) {
|
||||
if(regs.display_disabled == true) {
|
||||
memory::vram[addr] = data;
|
||||
} else {
|
||||
uint16 v = cpu.vcounter();
|
||||
uint16 h = cpu.hcounter();
|
||||
if(v == 0) {
|
||||
if(h <= 4) {
|
||||
memory::vram[addr] = data;
|
||||
} else if(h == 6) {
|
||||
memory::vram[addr] = cpu.regs.mdr;
|
||||
} else {
|
||||
//no write
|
||||
}
|
||||
} else if(v < (!overscan() ? 225 : 240)) {
|
||||
//no write
|
||||
} else if(v == (!overscan() ? 225 : 240)) {
|
||||
if(h <= 4) {
|
||||
//no write
|
||||
} else {
|
||||
memory::vram[addr] = data;
|
||||
}
|
||||
} else {
|
||||
memory::vram[addr] = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//NOTE: OAM accesses during active display are rerouted to 0x0218 ... this can be considered
|
||||
//a hack. The actual address varies during rendering, as the S-PPU reads in data itself for
|
||||
//processing. Unfortunately, we have yet to determine how this works. The algorithm cannot be
|
||||
//reverse engineered using a scanline renderer such as this, and at this time, there does not
|
||||
//exist a more accurate SNES PPU emulator to work from. The only known game to actually access
|
||||
//OAM during active display is Uniracers. It expects accesses to map to offset 0x0218.
|
||||
//It was decided by public consensus to map writes to this address to match Uniracers, primarily
|
||||
//because it is the only game observed to do this, but also because mapping to this address does
|
||||
//not contradict any of our findings, because we have no findings whatsoever on this behavior.
|
||||
//Think of this what you will, I openly admit that this is a hack. But it is more accurate than
|
||||
//writing to the 'expected' address set by $2102,$2103, and will catch problems in software that
|
||||
//accidentally accesses OAM during active display by virtue of not returning the expected data.
|
||||
|
||||
uint8 bPPU::oam_mmio_read(uint16 addr) {
|
||||
addr &= 0x03ff;
|
||||
if(addr & 0x0200) addr &= 0x021f;
|
||||
uint8 data;
|
||||
|
||||
if(regs.display_disabled == true) {
|
||||
data = memory::oam[addr];
|
||||
} else {
|
||||
if(cpu.vcounter() < (!overscan() ? 225 : 240)) {
|
||||
data = memory::oam[0x0218];
|
||||
} else {
|
||||
data = memory::oam[addr];
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void bPPU::oam_mmio_write(uint16 addr, uint8 data) {
|
||||
addr &= 0x03ff;
|
||||
if(addr & 0x0200) addr &= 0x021f;
|
||||
|
||||
if(regs.display_disabled == true) {
|
||||
memory::oam[addr] = data;
|
||||
} else {
|
||||
if(cpu.vcounter() < (!overscan() ? 225 : 240)) {
|
||||
memory::oam[0x0218] = data;
|
||||
} else {
|
||||
memory::oam[addr] = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//NOTE: CGRAM writes during hblank are valid. During active display, the actual address the
|
||||
//data is written to varies, as the S-PPU itself changes the address. Like OAM, we do not know
|
||||
//the exact algorithm used, but we have zero known examples of any commercial software that
|
||||
//attempts to do this. Therefore, the addresses are mapped to 0x01ff. There is nothing special
|
||||
//about this address, it is simply more accurate to invalidate the 'expected' address than not.
|
||||
|
||||
uint8 bPPU::cgram_mmio_read(uint16 addr) {
|
||||
addr &= 0x01ff;
|
||||
uint8 data;
|
||||
|
||||
if(regs.display_disabled == true) {
|
||||
data = memory::cgram[addr];
|
||||
} else {
|
||||
uint16 v = cpu.vcounter();
|
||||
uint16 h = cpu.hcounter();
|
||||
if(v < (!overscan() ? 225 : 240) && h >= 128 && h < 1096) {
|
||||
data = memory::cgram[0x01ff] & 0x7f;
|
||||
} else {
|
||||
data = memory::cgram[addr];
|
||||
}
|
||||
}
|
||||
|
||||
if(addr & 1) data &= 0x7f;
|
||||
return data;
|
||||
}
|
||||
|
||||
void bPPU::cgram_mmio_write(uint16 addr, uint8 data) {
|
||||
addr &= 0x01ff;
|
||||
if(addr & 1) data &= 0x7f;
|
||||
|
||||
if(regs.display_disabled == true) {
|
||||
memory::cgram[addr] = data;
|
||||
} else {
|
||||
uint16 v = cpu.vcounter();
|
||||
uint16 h = cpu.hcounter();
|
||||
if(v < (!overscan() ? 225 : 240) && h >= 128 && h < 1096) {
|
||||
memory::cgram[0x01ff] = data & 0x7f;
|
||||
} else {
|
||||
memory::cgram[addr] = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//INIDISP
|
||||
void bPPU::mmio_w2100(uint8 value) {
|
||||
if(regs.display_disabled == true && cpu.vcounter() == (!overscan() ? 225 : 240)) {
|
||||
|
@ -595,6 +427,7 @@ void bPPU::mmio_w2133(uint8 value) {
|
|||
regs.interlace = !!(value & 0x01);
|
||||
|
||||
display.overscan = regs.overscan;
|
||||
sprite_list_valid = false;
|
||||
}
|
||||
|
||||
//MPYL
|
|
@ -0,0 +1,198 @@
|
|||
struct {
|
||||
//open bus support
|
||||
uint8 ppu1_mdr, ppu2_mdr;
|
||||
|
||||
//bg line counters
|
||||
uint16 bg_y[4];
|
||||
|
||||
//$2100
|
||||
bool display_disabled;
|
||||
uint8 display_brightness;
|
||||
|
||||
//$2101
|
||||
uint8 oam_basesize;
|
||||
uint8 oam_nameselect;
|
||||
uint16 oam_tdaddr;
|
||||
|
||||
//$2102-$2103
|
||||
uint16 oam_baseaddr;
|
||||
uint16 oam_addr;
|
||||
bool oam_priority;
|
||||
uint8 oam_firstsprite;
|
||||
|
||||
//$2104
|
||||
uint8 oam_latchdata;
|
||||
|
||||
//$2105
|
||||
bool bg_tilesize[4];
|
||||
bool bg3_priority;
|
||||
uint8 bg_mode;
|
||||
|
||||
//$2106
|
||||
uint8 mosaic_size;
|
||||
bool mosaic_enabled[4];
|
||||
uint16 mosaic_countdown;
|
||||
|
||||
//$2107-$210a
|
||||
uint16 bg_scaddr[4];
|
||||
uint8 bg_scsize[4];
|
||||
|
||||
//$210b-$210c
|
||||
uint16 bg_tdaddr[4];
|
||||
|
||||
//$210d-$2114
|
||||
uint8 bg_ofslatch;
|
||||
uint16 m7_hofs, m7_vofs;
|
||||
uint16 bg_hofs[4];
|
||||
uint16 bg_vofs[4];
|
||||
|
||||
//$2115
|
||||
bool vram_incmode;
|
||||
uint8 vram_mapping;
|
||||
uint8 vram_incsize;
|
||||
|
||||
//$2116-$2117
|
||||
uint16 vram_addr;
|
||||
|
||||
//$211a
|
||||
uint8 mode7_repeat;
|
||||
bool mode7_vflip;
|
||||
bool mode7_hflip;
|
||||
|
||||
//$211b-$2120
|
||||
uint8 m7_latch;
|
||||
uint16 m7a, m7b, m7c, m7d, m7x, m7y;
|
||||
|
||||
//$2121
|
||||
uint16 cgram_addr;
|
||||
|
||||
//$2122
|
||||
uint8 cgram_latchdata;
|
||||
|
||||
//$2123-$2125
|
||||
bool window1_enabled[6];
|
||||
bool window1_invert [6];
|
||||
bool window2_enabled[6];
|
||||
bool window2_invert [6];
|
||||
|
||||
//$2126-$2129
|
||||
uint8 window1_left, window1_right;
|
||||
uint8 window2_left, window2_right;
|
||||
|
||||
//$212a-$212b
|
||||
uint8 window_mask[6];
|
||||
|
||||
//$212c-$212d
|
||||
bool bg_enabled[5], bgsub_enabled[5];
|
||||
|
||||
//$212e-$212f
|
||||
bool window_enabled[5], sub_window_enabled[5];
|
||||
|
||||
//$2130
|
||||
uint8 color_mask, colorsub_mask;
|
||||
bool addsub_mode;
|
||||
bool direct_color;
|
||||
|
||||
//$2131
|
||||
bool color_mode, color_halve;
|
||||
bool color_enabled[6];
|
||||
|
||||
//$2132
|
||||
uint8 color_r, color_g, color_b;
|
||||
uint16 color_rgb;
|
||||
|
||||
//$2133
|
||||
//overscan and interlace are checked once per frame to
|
||||
//determine if entire frame should be interlaced/non-interlace
|
||||
//and overscan adjusted. therefore, the variables act sort of
|
||||
//like a buffer, but they do still affect internal rendering
|
||||
bool mode7_extbg;
|
||||
bool pseudo_hires;
|
||||
bool overscan;
|
||||
uint16 scanlines;
|
||||
bool oam_interlace;
|
||||
bool interlace;
|
||||
|
||||
//$2137
|
||||
uint16 hcounter, vcounter;
|
||||
bool latch_hcounter, latch_vcounter;
|
||||
bool counters_latched;
|
||||
|
||||
//$2139-$213a
|
||||
uint16 vram_readbuffer;
|
||||
|
||||
//$213e
|
||||
bool time_over, range_over;
|
||||
uint16 oam_itemcount, oam_tilecount;
|
||||
} regs;
|
||||
|
||||
void mmio_w2100(uint8 value); //INIDISP
|
||||
void mmio_w2101(uint8 value); //OBSEL
|
||||
void mmio_w2102(uint8 value); //OAMADDL
|
||||
void mmio_w2103(uint8 value); //OAMADDH
|
||||
void mmio_w2104(uint8 value); //OAMDATA
|
||||
void mmio_w2105(uint8 value); //BGMODE
|
||||
void mmio_w2106(uint8 value); //MOSAIC
|
||||
void mmio_w2107(uint8 value); //BG1SC
|
||||
void mmio_w2108(uint8 value); //BG2SC
|
||||
void mmio_w2109(uint8 value); //BG3SC
|
||||
void mmio_w210a(uint8 value); //BG4SC
|
||||
void mmio_w210b(uint8 value); //BG12NBA
|
||||
void mmio_w210c(uint8 value); //BG34NBA
|
||||
void mmio_w210d(uint8 value); //BG1HOFS
|
||||
void mmio_w210e(uint8 value); //BG1VOFS
|
||||
void mmio_w210f(uint8 value); //BG2HOFS
|
||||
void mmio_w2110(uint8 value); //BG2VOFS
|
||||
void mmio_w2111(uint8 value); //BG3HOFS
|
||||
void mmio_w2112(uint8 value); //BG3VOFS
|
||||
void mmio_w2113(uint8 value); //BG4HOFS
|
||||
void mmio_w2114(uint8 value); //BG4VOFS
|
||||
void mmio_w2115(uint8 value); //VMAIN
|
||||
void mmio_w2116(uint8 value); //VMADDL
|
||||
void mmio_w2117(uint8 value); //VMADDH
|
||||
void mmio_w2118(uint8 value); //VMDATAL
|
||||
void mmio_w2119(uint8 value); //VMDATAH
|
||||
void mmio_w211a(uint8 value); //M7SEL
|
||||
void mmio_w211b(uint8 value); //M7A
|
||||
void mmio_w211c(uint8 value); //M7B
|
||||
void mmio_w211d(uint8 value); //M7C
|
||||
void mmio_w211e(uint8 value); //M7D
|
||||
void mmio_w211f(uint8 value); //M7X
|
||||
void mmio_w2120(uint8 value); //M7Y
|
||||
void mmio_w2121(uint8 value); //CGADD
|
||||
void mmio_w2122(uint8 value); //CGDATA
|
||||
void mmio_w2123(uint8 value); //W12SEL
|
||||
void mmio_w2124(uint8 value); //W34SEL
|
||||
void mmio_w2125(uint8 value); //WOBJSEL
|
||||
void mmio_w2126(uint8 value); //WH0
|
||||
void mmio_w2127(uint8 value); //WH1
|
||||
void mmio_w2128(uint8 value); //WH2
|
||||
void mmio_w2129(uint8 value); //WH3
|
||||
void mmio_w212a(uint8 value); //WBGLOG
|
||||
void mmio_w212b(uint8 value); //WOBJLOG
|
||||
void mmio_w212c(uint8 value); //TM
|
||||
void mmio_w212d(uint8 value); //TS
|
||||
void mmio_w212e(uint8 value); //TMW
|
||||
void mmio_w212f(uint8 value); //TSW
|
||||
void mmio_w2130(uint8 value); //CGWSEL
|
||||
void mmio_w2131(uint8 value); //CGADDSUB
|
||||
void mmio_w2132(uint8 value); //COLDATA
|
||||
void mmio_w2133(uint8 value); //SETINI
|
||||
|
||||
uint8 mmio_r2134(); //MPYL
|
||||
uint8 mmio_r2135(); //MPYM
|
||||
uint8 mmio_r2136(); //MPYH
|
||||
uint8 mmio_r2137(); //SLHV
|
||||
uint8 mmio_r2138(); //OAMDATAREAD
|
||||
uint8 mmio_r2139(); //VMDATALREAD
|
||||
uint8 mmio_r213a(); //VMDATAHREAD
|
||||
uint8 mmio_r213b(); //CGDATAREAD
|
||||
uint8 mmio_r213c(); //OPHCT
|
||||
uint8 mmio_r213d(); //OPVCT
|
||||
uint8 mmio_r213e(); //STAT77
|
||||
uint8 mmio_r213f(); //STAT78
|
||||
|
||||
uint8 mmio_read(unsigned addr);
|
||||
void mmio_write(unsigned addr, uint8 data);
|
||||
|
||||
void latch_counters();
|
|
@ -5,14 +5,14 @@
|
|||
inline uint16 bPPU::addsub(uint32 x, uint32 y, bool halve) {
|
||||
if(!regs.color_mode) {
|
||||
if(!halve) {
|
||||
unsigned sum = x + y;
|
||||
unsigned sum = x + y;
|
||||
unsigned carry = (sum - ((x ^ y) & 0x0421)) & 0x8420;
|
||||
return (sum - carry) | (carry - (carry >> 5));
|
||||
} else {
|
||||
return (x + y - ((x ^ y) & 0x0421)) >> 1;
|
||||
}
|
||||
} else {
|
||||
unsigned diff = x - y + 0x8420;
|
||||
unsigned diff = x - y + 0x8420;
|
||||
unsigned borrow = (diff - ((x ^ y) & 0x8420)) & 0x8420;
|
||||
if(!halve) {
|
||||
return (diff - borrow) & (borrow - (borrow >> 5));
|
|
@ -22,7 +22,8 @@ void bPPU::update_bg_info() {
|
|||
}
|
||||
}
|
||||
|
||||
uint16 bPPU::bg_get_tile(uint8 bg, uint16 x, uint16 y) {
|
||||
template<unsigned bg>
|
||||
uint16 bPPU::bg_get_tile(uint16 x, uint16 y) {
|
||||
x = (x & bg_info[bg].mx) >> bg_info[bg].tw;
|
||||
y = (y & bg_info[bg].my) >> bg_info[bg].th;
|
||||
|
||||
|
@ -50,22 +51,15 @@ uint16 bPPU::bg_get_tile(uint8 bg, uint16 x, uint16 y) {
|
|||
pixel_cache[x].ce_sub = false; \
|
||||
}
|
||||
|
||||
void bPPU::render_line_bg(uint8 bg, uint8 color_depth, uint8 pri0_pos, uint8 pri1_pos) {
|
||||
if(regs.bg_enabled[bg] == false && regs.bgsub_enabled[bg] == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
//are layers disabled by user?
|
||||
if(render_enabled(bg, 0) == false) pri0_pos = 0;
|
||||
if(render_enabled(bg, 1) == false) pri1_pos = 0;
|
||||
//nothing to render?
|
||||
if(!pri0_pos && !pri1_pos) return;
|
||||
template<unsigned mode, unsigned bg, unsigned color_depth>
|
||||
void bPPU::render_line_bg(uint8 pri0_pos, uint8 pri1_pos) {
|
||||
if(regs.bg_enabled[bg] == false && regs.bgsub_enabled[bg] == false) return;
|
||||
|
||||
const bool bg_enabled = regs.bg_enabled[bg];
|
||||
const bool bgsub_enabled = regs.bgsub_enabled[bg];
|
||||
|
||||
const uint16 opt_valid_bit = (bg == BG1) ? 0x2000 : (bg == BG2) ? 0x4000 : 0x0000;
|
||||
const uint8 bgpal_index = (regs.bg_mode == 0 ? (bg << 5) : 0);
|
||||
const uint8 bgpal_index = (mode == 0 ? (bg << 5) : 0);
|
||||
|
||||
const uint8 pal_size = 2 << color_depth; //<<2 (*4), <<4 (*16), <<8 (*256)
|
||||
const uint16 tile_mask = 0x0fff >> color_depth; //0x0fff, 0x07ff, 0x03ff
|
||||
|
@ -85,7 +79,7 @@ void bPPU::render_line_bg(uint8 bg, uint8 color_depth, uint8 pri0_pos, uint8 pri
|
|||
uint16 hscroll = regs.bg_hofs[bg];
|
||||
uint16 vscroll = regs.bg_vofs[bg];
|
||||
|
||||
const unsigned hires = (regs.bg_mode == 5 || regs.bg_mode == 6);
|
||||
const unsigned hires = (mode == 5 || mode == 6);
|
||||
const unsigned width = (!hires ? 256 : 512);
|
||||
|
||||
if(hires) {
|
||||
|
@ -101,8 +95,8 @@ void bPPU::render_line_bg(uint8 bg, uint8 color_depth, uint8 pri0_pos, uint8 pri
|
|||
|
||||
const uint8 *tile_ptr;
|
||||
const uint16 *mtable = mosaic_table[regs.mosaic_enabled[bg] ? regs.mosaic_size : 0];
|
||||
const bool is_opt_mode = (regs.bg_mode == 2 || regs.bg_mode == 4 || regs.bg_mode == 6);
|
||||
const bool is_direct_color_mode = (regs.direct_color == true && bg == BG1 && (regs.bg_mode == 3 || regs.bg_mode == 4));
|
||||
const bool is_opt_mode = (mode == 2 || mode == 4 || mode == 6);
|
||||
const bool is_direct_color_mode = (regs.direct_color == true && bg == BG1 && (mode == 3 || mode == 4));
|
||||
|
||||
build_window_tables(bg);
|
||||
const uint8 *wt_main = window[bg].main;
|
||||
|
@ -122,13 +116,13 @@ void bPPU::render_line_bg(uint8 bg, uint8 color_depth, uint8 pri0_pos, uint8 pri
|
|||
if((opt_x >> 3) != (prev_optx >> 3)) {
|
||||
prev_optx = opt_x;
|
||||
|
||||
hval = bg_get_tile(BG3, (opt_x - 8) + (regs.bg_hofs[BG3] & ~7), regs.bg_vofs[BG3]);
|
||||
if(regs.bg_mode != 4) {
|
||||
vval = bg_get_tile(BG3, (opt_x - 8) + (regs.bg_hofs[BG3] & ~7), regs.bg_vofs[BG3] + 8);
|
||||
hval = bg_get_tile<BG3>((opt_x - 8) + (regs.bg_hofs[BG3] & ~7), regs.bg_vofs[BG3]);
|
||||
if(mode != 4) {
|
||||
vval = bg_get_tile<BG3>((opt_x - 8) + (regs.bg_hofs[BG3] & ~7), regs.bg_vofs[BG3] + 8);
|
||||
}
|
||||
}
|
||||
|
||||
if(regs.bg_mode == 4) {
|
||||
if(mode == 4) {
|
||||
if(hval & opt_valid_bit) {
|
||||
if(!(hval & 0x8000)) {
|
||||
hoffset = opt_x + (hval & ~7);
|
||||
|
@ -154,7 +148,7 @@ void bPPU::render_line_bg(uint8 bg, uint8 color_depth, uint8 pri0_pos, uint8 pri
|
|||
prev_x = (hoffset >> 3);
|
||||
prev_y = (voffset >> 3);
|
||||
|
||||
tile_num = bg_get_tile(bg, hoffset, voffset); //format = vhopppcc cccccccc
|
||||
tile_num = bg_get_tile<bg>(hoffset, voffset); //format = vhopppcc cccccccc
|
||||
mirror_y = (tile_num & 0x8000);
|
||||
mirror_x = (tile_num & 0x4000);
|
||||
tile_pri = (tile_num & 0x2000) ? pri1_pos : pri0_pos;
|
||||
|
@ -174,7 +168,7 @@ void bPPU::render_line_bg(uint8 bg, uint8 color_depth, uint8 pri0_pos, uint8 pri
|
|||
tile_num &= tile_mask;
|
||||
|
||||
if(bg_td_state[tile_num] == 1) {
|
||||
render_bg_tile(color_depth, tile_num);
|
||||
render_bg_tile<color_depth>(tile_num);
|
||||
}
|
||||
|
||||
if(mirror_y) voffset ^= 7; //invert y tile pos
|
|
@ -0,0 +1,147 @@
|
|||
#ifdef BPPU_CPP
|
||||
|
||||
#define render_bg_tile_line_2bpp(mask) \
|
||||
col = !!(d0 & mask) << 0; \
|
||||
col += !!(d1 & mask) << 1; \
|
||||
*dest++ = col
|
||||
|
||||
#define render_bg_tile_line_4bpp(mask) \
|
||||
col = !!(d0 & mask) << 0; \
|
||||
col += !!(d1 & mask) << 1; \
|
||||
col += !!(d2 & mask) << 2; \
|
||||
col += !!(d3 & mask) << 3; \
|
||||
*dest++ = col
|
||||
|
||||
#define render_bg_tile_line_8bpp(mask) \
|
||||
col = !!(d0 & mask) << 0; \
|
||||
col += !!(d1 & mask) << 1; \
|
||||
col += !!(d2 & mask) << 2; \
|
||||
col += !!(d3 & mask) << 3; \
|
||||
col += !!(d4 & mask) << 4; \
|
||||
col += !!(d5 & mask) << 5; \
|
||||
col += !!(d6 & mask) << 6; \
|
||||
col += !!(d7 & mask) << 7; \
|
||||
*dest++ = col
|
||||
|
||||
template<unsigned color_depth>
|
||||
void bPPU::render_bg_tile(uint16 tile_num) {
|
||||
uint8 col, d0, d1, d2, d3, d4, d5, d6, d7;
|
||||
|
||||
if(color_depth == COLORDEPTH_4) {
|
||||
uint8 *dest = (uint8*)bg_tiledata[TILE_2BIT] + tile_num * 64;
|
||||
unsigned pos = tile_num * 16;
|
||||
unsigned y = 8;
|
||||
while(y--) {
|
||||
d0 = memory::vram[pos ];
|
||||
d1 = memory::vram[pos + 1];
|
||||
render_bg_tile_line_2bpp(0x80);
|
||||
render_bg_tile_line_2bpp(0x40);
|
||||
render_bg_tile_line_2bpp(0x20);
|
||||
render_bg_tile_line_2bpp(0x10);
|
||||
render_bg_tile_line_2bpp(0x08);
|
||||
render_bg_tile_line_2bpp(0x04);
|
||||
render_bg_tile_line_2bpp(0x02);
|
||||
render_bg_tile_line_2bpp(0x01);
|
||||
pos += 2;
|
||||
}
|
||||
bg_tiledata_state[TILE_2BIT][tile_num] = 0;
|
||||
}
|
||||
|
||||
if(color_depth == COLORDEPTH_16) {
|
||||
uint8 *dest = (uint8*)bg_tiledata[TILE_4BIT] + tile_num * 64;
|
||||
unsigned pos = tile_num * 32;
|
||||
unsigned y = 8;
|
||||
while(y--) {
|
||||
d0 = memory::vram[pos ];
|
||||
d1 = memory::vram[pos + 1];
|
||||
d2 = memory::vram[pos + 16];
|
||||
d3 = memory::vram[pos + 17];
|
||||
render_bg_tile_line_4bpp(0x80);
|
||||
render_bg_tile_line_4bpp(0x40);
|
||||
render_bg_tile_line_4bpp(0x20);
|
||||
render_bg_tile_line_4bpp(0x10);
|
||||
render_bg_tile_line_4bpp(0x08);
|
||||
render_bg_tile_line_4bpp(0x04);
|
||||
render_bg_tile_line_4bpp(0x02);
|
||||
render_bg_tile_line_4bpp(0x01);
|
||||
pos += 2;
|
||||
}
|
||||
bg_tiledata_state[TILE_4BIT][tile_num] = 0;
|
||||
}
|
||||
|
||||
if(color_depth == COLORDEPTH_256) {
|
||||
uint8 *dest = (uint8*)bg_tiledata[TILE_8BIT] + tile_num * 64;
|
||||
unsigned pos = tile_num * 64;
|
||||
unsigned y = 8;
|
||||
while(y--) {
|
||||
d0 = memory::vram[pos ];
|
||||
d1 = memory::vram[pos + 1];
|
||||
d2 = memory::vram[pos + 16];
|
||||
d3 = memory::vram[pos + 17];
|
||||
d4 = memory::vram[pos + 32];
|
||||
d5 = memory::vram[pos + 33];
|
||||
d6 = memory::vram[pos + 48];
|
||||
d7 = memory::vram[pos + 49];
|
||||
render_bg_tile_line_8bpp(0x80);
|
||||
render_bg_tile_line_8bpp(0x40);
|
||||
render_bg_tile_line_8bpp(0x20);
|
||||
render_bg_tile_line_8bpp(0x10);
|
||||
render_bg_tile_line_8bpp(0x08);
|
||||
render_bg_tile_line_8bpp(0x04);
|
||||
render_bg_tile_line_8bpp(0x02);
|
||||
render_bg_tile_line_8bpp(0x01);
|
||||
pos += 2;
|
||||
}
|
||||
bg_tiledata_state[TILE_8BIT][tile_num] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#undef render_bg_tile_line_2bpp
|
||||
#undef render_bg_tile_line_4bpp
|
||||
#undef render_bg_tile_line_8bpp
|
||||
|
||||
void bPPU::flush_pixel_cache() {
|
||||
uint16 main = get_palette(0);
|
||||
uint16 sub = (regs.pseudo_hires || regs.bg_mode == 5 || regs.bg_mode == 6)
|
||||
? main
|
||||
: regs.color_rgb;
|
||||
|
||||
unsigned i = 255;
|
||||
do {
|
||||
pixel_cache[i].src_main = main;
|
||||
pixel_cache[i].src_sub = sub;
|
||||
pixel_cache[i].bg_main = BACK;
|
||||
pixel_cache[i].bg_sub = BACK;
|
||||
pixel_cache[i].ce_main = false;
|
||||
pixel_cache[i].ce_sub = false;
|
||||
pixel_cache[i].pri_main = 0;
|
||||
pixel_cache[i].pri_sub = 0;
|
||||
} while(i--);
|
||||
}
|
||||
|
||||
void bPPU::alloc_tiledata_cache() {
|
||||
bg_tiledata[TILE_2BIT] = new(zeromemory) uint8_t[262144];
|
||||
bg_tiledata[TILE_4BIT] = new(zeromemory) uint8_t[131072];
|
||||
bg_tiledata[TILE_8BIT] = new(zeromemory) uint8_t[ 65536];
|
||||
bg_tiledata_state[TILE_2BIT] = new(zeromemory) uint8_t[ 4096];
|
||||
bg_tiledata_state[TILE_4BIT] = new(zeromemory) uint8_t[ 2048];
|
||||
bg_tiledata_state[TILE_8BIT] = new(zeromemory) uint8_t[ 1024];
|
||||
}
|
||||
|
||||
//marks all tiledata cache entries as dirty
|
||||
void bPPU::flush_tiledata_cache() {
|
||||
for(unsigned i = 0; i < 4096; i++) bg_tiledata_state[TILE_2BIT][i] = 1;
|
||||
for(unsigned i = 0; i < 2048; i++) bg_tiledata_state[TILE_4BIT][i] = 1;
|
||||
for(unsigned i = 0; i < 1024; i++) bg_tiledata_state[TILE_8BIT][i] = 1;
|
||||
}
|
||||
|
||||
void bPPU::free_tiledata_cache() {
|
||||
delete[] bg_tiledata[TILE_2BIT];
|
||||
delete[] bg_tiledata[TILE_4BIT];
|
||||
delete[] bg_tiledata[TILE_8BIT];
|
||||
delete[] bg_tiledata_state[TILE_2BIT];
|
||||
delete[] bg_tiledata_state[TILE_4BIT];
|
||||
delete[] bg_tiledata_state[TILE_8BIT];
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,31 +1,31 @@
|
|||
#ifdef BPPU_CPP
|
||||
|
||||
inline uint16 bPPU::get_palette(uint8 index) {
|
||||
unsigned addr = index << 1;
|
||||
const unsigned addr = index << 1;
|
||||
return memory::cgram[addr] + (memory::cgram[addr + 1] << 8);
|
||||
}
|
||||
|
||||
//p = 00000bgr <palette data>
|
||||
//t = BBGGGRRR <tilemap data>
|
||||
//r = 0BBb00GGGg0RRRr0 <return data>
|
||||
inline uint16 bPPU::get_direct_color(uint8 p, uint8 t) {
|
||||
//p = 00000bgr <palette data>
|
||||
//t = BBGGGRRR <tilemap data>
|
||||
//r = 0BBb00GGGg0RRRr0 <return data>
|
||||
return ((t & 7) << 2) | ((p & 1) << 1) |
|
||||
(((t >> 3) & 7) << 7) | (((p >> 1) & 1) << 6) |
|
||||
((t >> 6) << 13) | ((p >> 2) << 12);
|
||||
}
|
||||
|
||||
inline uint16 bPPU::get_pixel_normal(uint32 x) {
|
||||
_pixel *p = &pixel_cache[x];
|
||||
pixel_t &p = pixel_cache[x];
|
||||
uint16 src_main, src_sub;
|
||||
uint8 bg_sub;
|
||||
src_main = p->src_main;
|
||||
src_main = p.src_main;
|
||||
|
||||
if(!regs.addsub_mode) {
|
||||
bg_sub = BACK;
|
||||
src_sub = regs.color_rgb;
|
||||
} else {
|
||||
bg_sub = p->bg_sub;
|
||||
src_sub = p->src_sub;
|
||||
bg_sub = p.bg_sub;
|
||||
src_sub = p.src_sub;
|
||||
}
|
||||
|
||||
if(!window[COL].main[x]) {
|
||||
|
@ -35,7 +35,7 @@ inline uint16 bPPU::get_pixel_normal(uint32 x) {
|
|||
src_main = 0x0000;
|
||||
}
|
||||
|
||||
if(!p->ce_main && regs.color_enabled[p->bg_main] && window[COL].sub[x]) {
|
||||
if(!p.ce_main && regs.color_enabled[p.bg_main] && window[COL].sub[x]) {
|
||||
bool halve = false;
|
||||
if(regs.color_halve && window[COL].main[x]) {
|
||||
if(regs.addsub_mode && bg_sub == BACK);
|
||||
|
@ -50,17 +50,17 @@ inline uint16 bPPU::get_pixel_normal(uint32 x) {
|
|||
}
|
||||
|
||||
inline uint16 bPPU::get_pixel_swap(uint32 x) {
|
||||
_pixel *p = &pixel_cache[x];
|
||||
pixel_t &p = pixel_cache[x];
|
||||
uint16 src_main, src_sub;
|
||||
uint8 bg_sub;
|
||||
src_main = p->src_sub;
|
||||
src_main = p.src_sub;
|
||||
|
||||
if(!regs.addsub_mode) {
|
||||
bg_sub = BACK;
|
||||
src_sub = regs.color_rgb;
|
||||
} else {
|
||||
bg_sub = p->bg_main;
|
||||
src_sub = p->src_main;
|
||||
bg_sub = p.bg_main;
|
||||
src_sub = p.src_main;
|
||||
}
|
||||
|
||||
if(!window[COL].main[x]) {
|
||||
|
@ -70,7 +70,7 @@ inline uint16 bPPU::get_pixel_swap(uint32 x) {
|
|||
src_main = 0x0000;
|
||||
}
|
||||
|
||||
if(!p->ce_sub && regs.color_enabled[p->bg_sub] && window[COL].sub[x]) {
|
||||
if(!p.ce_sub && regs.color_enabled[p.bg_sub] && window[COL].sub[x]) {
|
||||
bool halve = false;
|
||||
if(regs.color_halve && window[COL].main[x]) {
|
||||
if(regs.addsub_mode && bg_sub == BACK);
|
||||
|
@ -93,18 +93,18 @@ inline void bPPU::render_line_output() {
|
|||
|
||||
if(!regs.pseudo_hires && regs.bg_mode != 5 && regs.bg_mode != 6) {
|
||||
if(regs.display_brightness == 15) {
|
||||
for(int x = 0; x < 256; x++) {
|
||||
for(unsigned x = 0; x < 256; x++) {
|
||||
*ptr++ = get_pixel_normal(x);
|
||||
}
|
||||
} else {
|
||||
for(int x = 0; x < 256; x++) {
|
||||
for(unsigned x = 0; x < 256; x++) {
|
||||
curr = get_pixel_normal(x);
|
||||
*ptr++ = luma_b[curr >> 10] + luma_gr[curr & 0x3ff];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(regs.display_brightness == 15) {
|
||||
for(int x = 0, prev = 0; x < 256; x++) {
|
||||
for(unsigned x = 0, prev = 0; x < 256; x++) {
|
||||
curr = get_pixel_swap(x);
|
||||
*ptr++ = (prev + curr - ((prev ^ curr) & 0x0421)) >> 1;
|
||||
prev = curr;
|
||||
|
@ -115,7 +115,7 @@ inline void bPPU::render_line_output() {
|
|||
|
||||
}
|
||||
} else {
|
||||
for(int x = 0, prev = 0; x < 256; x++) {
|
||||
for(unsigned x = 0, prev = 0; x < 256; x++) {
|
||||
curr = get_pixel_swap(x);
|
||||
curr = luma_b[curr >> 10] + luma_gr[curr & 0x3ff];
|
||||
*ptr++ = (prev + curr - ((prev ^ curr) & 0x0421)) >> 1;
|
||||
|
@ -131,8 +131,7 @@ inline void bPPU::render_line_output() {
|
|||
}
|
||||
|
||||
inline void bPPU::render_line_clear() {
|
||||
uint16 *ptr = (uint16*)output + (line * 1024) +
|
||||
((interlace() && field()) ? 512 : 0);
|
||||
uint16 *ptr = (uint16*)output + (line * 1024) + ((interlace() && field()) ? 512 : 0);
|
||||
uint16 width = (!regs.pseudo_hires && regs.bg_mode != 5 && regs.bg_mode != 6) ? 256 : 512;
|
||||
memset(ptr, 0, width * 2 * sizeof(uint16));
|
||||
}
|
|
@ -1,30 +1,21 @@
|
|||
#ifdef BPPU_CPP
|
||||
|
||||
/*****
|
||||
* bsnes mode7 renderer
|
||||
*
|
||||
* base algorithm written by anomie
|
||||
* bsnes implementation written by byuu
|
||||
*
|
||||
* supports mode 7 + extbg + rotate + zoom +
|
||||
* direct color + scrolling + m7sel + windowing + mosaic
|
||||
* interlace and pseudo-hires support are automatic via main rendering routine
|
||||
*****/
|
||||
//bsnes mode7 renderer
|
||||
//
|
||||
//base algorithm written by anomie
|
||||
//bsnes implementation written by byuu
|
||||
//
|
||||
//supports mode 7 + extbg + rotate + zoom + direct color + scrolling + m7sel + windowing + mosaic
|
||||
//interlace and pseudo-hires support are automatic via main rendering routine
|
||||
|
||||
//13-bit sign extend
|
||||
//--s---vvvvvvvvvv -> ssssssvvvvvvvvvv
|
||||
#define CLIP(x) ( ((x) & 0x2000) ? ( (x) | ~0x03ff) : ((x) & 0x03ff) )
|
||||
|
||||
void bPPU::render_line_mode7(uint8 bg, uint8 pri0_pos, uint8 pri1_pos) {
|
||||
template<unsigned bg>
|
||||
void bPPU::render_line_mode7(uint8 pri0_pos, uint8 pri1_pos) {
|
||||
if(regs.bg_enabled[bg] == false && regs.bgsub_enabled[bg] == false) return;
|
||||
|
||||
//are layers disabled by user?
|
||||
if(render_enabled(bg, 0) == false) pri0_pos = 0;
|
||||
if(render_enabled(bg, 1) == false) pri1_pos = 0;
|
||||
|
||||
//nothing to render?
|
||||
if(!pri0_pos && !pri1_pos) return;
|
||||
|
||||
int32 px, py;
|
||||
int32 tx, ty, tile, palette;
|
||||
|
||||
|
@ -52,7 +43,7 @@ void bPPU::render_line_mode7(uint8 bg, uint8 pri0_pos, uint8 pri1_pos) {
|
|||
if(bg == BG1) {
|
||||
mtable_x = (uint16*)mosaic_table[(regs.mosaic_enabled[BG1] == true) ? regs.mosaic_size : 0];
|
||||
mtable_y = (uint16*)mosaic_table[(regs.mosaic_enabled[BG1] == true) ? regs.mosaic_size : 0];
|
||||
} else { //bg == BG2
|
||||
} else { //bg == BG2
|
||||
//Mode7 EXTBG BG2 uses BG1 mosaic enable to control vertical mosaic,
|
||||
//and BG2 mosaic enable to control horizontal mosaic...
|
||||
mtable_x = (uint16*)mosaic_table[(regs.mosaic_enabled[BG2] == true) ? regs.mosaic_size : 0];
|
||||
|
@ -70,8 +61,8 @@ void bPPU::render_line_mode7(uint8 bg, uint8 pri0_pos, uint8 pri1_pos) {
|
|||
py >>= 8;
|
||||
|
||||
switch(regs.mode7_repeat) {
|
||||
case 0: //screen repetition outside of screen area
|
||||
case 1: { //same as case 0
|
||||
case 0: //screen repetition outside of screen area
|
||||
case 1: { //same as case 0
|
||||
px &= 1023;
|
||||
py &= 1023;
|
||||
tx = ((px >> 3) & 127);
|
||||
|
@ -79,7 +70,7 @@ void bPPU::render_line_mode7(uint8 bg, uint8 pri0_pos, uint8 pri1_pos) {
|
|||
tile = memory::vram[(ty * 128 + tx) << 1];
|
||||
palette = memory::vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1];
|
||||
} break;
|
||||
case 2: { //palette color 0 outside of screen area
|
||||
case 2: { //palette color 0 outside of screen area
|
||||
if(px < 0 || px > 1023 || py < 0 || py > 1023) {
|
||||
palette = 0;
|
||||
} else {
|
||||
|
@ -91,7 +82,7 @@ void bPPU::render_line_mode7(uint8 bg, uint8 pri0_pos, uint8 pri1_pos) {
|
|||
palette = memory::vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1];
|
||||
}
|
||||
} break;
|
||||
case 3: { //character 0 repetition outside of screen area
|
||||
case 3: { //character 0 repetition outside of screen area
|
||||
if(px < 0 || px > 1023 || py < 0 || py > 1023) {
|
||||
tile = 0;
|
||||
} else {
|
|
@ -1,12 +1,15 @@
|
|||
#ifdef BPPU_CPP
|
||||
|
||||
void bPPU::build_sprite_list() {
|
||||
uint8 *tableA = memory::oam.data();
|
||||
uint8 *tableB = memory::oam.data() + 512;
|
||||
if(sprite_list_valid == true) return;
|
||||
sprite_list_valid = true;
|
||||
|
||||
const uint8 *tableA = memory::oam.data();
|
||||
const uint8 *tableB = memory::oam.data() + 512;
|
||||
|
||||
for(unsigned i = 0; i < 128; i++) {
|
||||
unsigned x = !!(*tableB & (1 << ((i & 3) << 1))); //0x01, 0x04, 0x10, 0x40
|
||||
bool size = !!(*tableB & (2 << ((i & 3) << 1))); //0x02, 0x08, 0x20, 0x80
|
||||
const bool x = *tableB & (1 << ((i & 3) << 1)); //0x01, 0x04, 0x10, 0x40
|
||||
const bool size = *tableB & (2 << ((i & 3) << 1)); //0x02, 0x08, 0x20, 0x80
|
||||
|
||||
switch(cache.oam_basesize) {
|
||||
case 0: sprite_list[i].width = (!size) ? 8 : 16;
|
||||
|
@ -41,8 +44,8 @@ void bPPU::build_sprite_list() {
|
|||
sprite_list[i].x = (x << 8) + tableA[0];
|
||||
sprite_list[i].y = (tableA[1] + 1) & 0xff;
|
||||
sprite_list[i].character = tableA[2];
|
||||
sprite_list[i].vflip = !!(tableA[3] & 0x80);
|
||||
sprite_list[i].hflip = !!(tableA[3] & 0x40);
|
||||
sprite_list[i].vflip = tableA[3] & 0x80;
|
||||
sprite_list[i].hflip = tableA[3] & 0x40;
|
||||
sprite_list[i].priority = (tableA[3] >> 4) & 3;
|
||||
sprite_list[i].palette = (tableA[3] >> 1) & 7;
|
||||
sprite_list[i].use_nameselect = tableA[3] & 1;
|
||||
|
@ -123,7 +126,7 @@ void bPPU::render_oam_tile(int tile_num) {
|
|||
uint8 *oam_td_state = (uint8*)bg_tiledata_state[COLORDEPTH_16];
|
||||
|
||||
if(oam_td_state[t->tile] == 1) {
|
||||
render_bg_tile(COLORDEPTH_16, t->tile);
|
||||
render_bg_tile<COLORDEPTH_16>(t->tile);
|
||||
}
|
||||
|
||||
unsigned sx = t->x;
|
||||
|
@ -168,25 +171,6 @@ void bPPU::render_line_oam_rto() {
|
|||
regs.range_over |= (regs.oam_itemcount > 32);
|
||||
}
|
||||
|
||||
void bPPU::render_line_oam(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos) {
|
||||
if(regs.bg_enabled[OAM] == false && regs.bgsub_enabled[OAM] == false) return;
|
||||
|
||||
//are layers disabled by user?
|
||||
if(render_enabled(OAM, 0) == false) pri0_pos = 0;
|
||||
if(render_enabled(OAM, 1) == false) pri1_pos = 0;
|
||||
if(render_enabled(OAM, 2) == false) pri2_pos = 0;
|
||||
if(render_enabled(OAM, 3) == false) pri3_pos = 0;
|
||||
//nothing to render?
|
||||
if(!pri0_pos && !pri1_pos && !pri2_pos && !pri3_pos) return;
|
||||
|
||||
for(int s = 0; s < 34; s++) {
|
||||
if(oam_tilelist[s].tile == 0xffff) continue;
|
||||
render_oam_tile(s);
|
||||
}
|
||||
|
||||
render_line_oam_lores(pri0_pos, pri1_pos, pri2_pos, pri3_pos);
|
||||
}
|
||||
|
||||
#define setpixel_main(x) \
|
||||
if(pixel_cache[x].pri_main < pri) { \
|
||||
pixel_cache[x].pri_main = pri; \
|
||||
|
@ -202,7 +186,14 @@ void bPPU::render_line_oam(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8
|
|||
pixel_cache[x].ce_sub = (oam_line_pal[x] < 192); \
|
||||
}
|
||||
|
||||
void bPPU::render_line_oam_lores(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos) {
|
||||
void bPPU::render_line_oam(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos) {
|
||||
if(regs.bg_enabled[OAM] == false && regs.bgsub_enabled[OAM] == false) return;
|
||||
|
||||
for(unsigned s = 0; s < 34; s++) {
|
||||
if(oam_tilelist[s].tile == 0xffff) continue;
|
||||
render_oam_tile(s);
|
||||
}
|
||||
|
||||
bool bg_enabled = regs.bg_enabled[OAM];
|
||||
bool bgsub_enabled = regs.bgsub_enabled[OAM];
|
||||
|
||||
|
@ -210,11 +201,11 @@ void bPPU::render_line_oam_lores(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos,
|
|||
uint8 *wt_main = window[OAM].main;
|
||||
uint8 *wt_sub = window[OAM].sub;
|
||||
|
||||
int pri_tbl[4] = { pri0_pos, pri1_pos, pri2_pos, pri3_pos };
|
||||
unsigned pri_tbl[4] = { pri0_pos, pri1_pos, pri2_pos, pri3_pos };
|
||||
for(int x = 0; x < 256; x++) {
|
||||
if(oam_line_pri[x] == OAM_PRI_NONE) continue;
|
||||
|
||||
int pri = pri_tbl[oam_line_pri[x]];
|
||||
unsigned pri = pri_tbl[oam_line_pri[x]];
|
||||
if(bg_enabled == true && !wt_main[x]) { setpixel_main(x); }
|
||||
if(bgsub_enabled == true && !wt_sub[x]) { setpixel_sub(x); }
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
#ifdef BPPU_CPP
|
||||
|
||||
#include "cache.cpp"
|
||||
#include "windows.cpp"
|
||||
#include "bg.cpp"
|
||||
#include "oam.cpp"
|
||||
#include "mode7.cpp"
|
||||
#include "addsub.cpp"
|
||||
#include "line.cpp"
|
||||
|
||||
//Mode 0: ->
|
||||
// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
|
||||
// BG4B, BG3B, OAM0, BG4A, BG3A, OAM1, BG2B, BG1B, OAM2, BG2A, BG1A, OAM3
|
||||
void bPPU::render_line_mode0() {
|
||||
render_line_bg<0, BG1, COLORDEPTH_4>(8, 11);
|
||||
render_line_bg<0, BG2, COLORDEPTH_4>(7, 10);
|
||||
render_line_bg<0, BG3, COLORDEPTH_4>(2, 5);
|
||||
render_line_bg<0, BG4, COLORDEPTH_4>(1, 4);
|
||||
render_line_oam(3, 6, 9, 12);
|
||||
}
|
||||
|
||||
//Mode 1 (pri=1): ->
|
||||
// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
|
||||
// BG3B, OAM0, OAM1, BG2B, BG1B, OAM2, BG2A, BG1A, OAM3, BG3A
|
||||
//
|
||||
//Mode 1 (pri=0): ->
|
||||
// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
|
||||
// BG3B, OAM0, BG3A, OAM1, BG2B, BG1B, OAM2, BG2A, BG1A, OAM3
|
||||
void bPPU::render_line_mode1() {
|
||||
if(regs.bg3_priority) {
|
||||
render_line_bg<1, BG1, COLORDEPTH_16>(5, 8);
|
||||
render_line_bg<1, BG2, COLORDEPTH_16>(4, 7);
|
||||
render_line_bg<1, BG3, COLORDEPTH_4 >( 1, 10);
|
||||
render_line_oam(2, 3, 6, 9);
|
||||
} else {
|
||||
render_line_bg<1, BG1, COLORDEPTH_16>(6, 9);
|
||||
render_line_bg<1, BG2, COLORDEPTH_16>(5, 8);
|
||||
render_line_bg<1, BG3, COLORDEPTH_4 >( 1, 3);
|
||||
render_line_oam(2, 4, 7, 10);
|
||||
}
|
||||
}
|
||||
|
||||
//Mode 2: ->
|
||||
// 1, 2, 3, 4, 5, 6, 7, 8
|
||||
// BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3
|
||||
void bPPU::render_line_mode2() {
|
||||
render_line_bg<2, BG1, COLORDEPTH_16>(3, 7);
|
||||
render_line_bg<2, BG2, COLORDEPTH_16>(1, 5);
|
||||
render_line_oam(2, 4, 6, 8);
|
||||
}
|
||||
|
||||
//Mode 3: ->
|
||||
// 1, 2, 3, 4, 5, 6, 7, 8
|
||||
// BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3
|
||||
void bPPU::render_line_mode3() {
|
||||
render_line_bg<3, BG1, COLORDEPTH_256>(3, 7);
|
||||
render_line_bg<3, BG2, COLORDEPTH_16 >(1, 5);
|
||||
render_line_oam(2, 4, 6, 8);
|
||||
}
|
||||
|
||||
//Mode 4: ->
|
||||
// 1, 2, 3, 4, 5, 6, 7, 8
|
||||
// BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3
|
||||
void bPPU::render_line_mode4() {
|
||||
render_line_bg<4, BG1, COLORDEPTH_256>(3, 7);
|
||||
render_line_bg<4, BG2, COLORDEPTH_4 >(1, 5);
|
||||
render_line_oam(2, 4, 6, 8);
|
||||
}
|
||||
|
||||
//Mode 5: ->
|
||||
// 1, 2, 3, 4, 5, 6, 7, 8
|
||||
// BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3
|
||||
void bPPU::render_line_mode5() {
|
||||
render_line_bg<5, BG1, COLORDEPTH_16>(3, 7);
|
||||
render_line_bg<5, BG2, COLORDEPTH_4 >(1, 5);
|
||||
render_line_oam(2, 4, 6, 8);
|
||||
}
|
||||
|
||||
//Mode 6: ->
|
||||
// 1, 2, 3, 4, 5, 6
|
||||
// OAM0, BG1B, OAM1, OAM2, BG1A, OAM3
|
||||
void bPPU::render_line_mode6() {
|
||||
render_line_bg<6, BG1, COLORDEPTH_16>(2, 5);
|
||||
render_line_oam(1, 3, 4, 6);
|
||||
}
|
||||
|
||||
//Mode7: ->
|
||||
// 1, 2, 3, 4, 5
|
||||
// OAM0, BG1n, OAM1, OAM2, OAM3
|
||||
|
||||
//Mode 7 EXTBG: ->
|
||||
// 1, 2, 3, 4, 5, 6, 7
|
||||
// BG2B, OAM0, BG1n, OAM1, BG2A, OAM2, OAM3
|
||||
void bPPU::render_line_mode7() {
|
||||
if(regs.mode7_extbg == false) {
|
||||
render_line_mode7<BG1>(2, 2);
|
||||
render_line_oam(1, 3, 4, 5);
|
||||
} else {
|
||||
render_line_mode7<BG1>(3, 3);
|
||||
render_line_mode7<BG2>(1, 5);
|
||||
render_line_oam(2, 4, 6, 7);
|
||||
}
|
||||
}
|
||||
|
||||
void bPPU::render_line() {
|
||||
if(regs.display_disabled == true) {
|
||||
render_line_clear();
|
||||
return;
|
||||
}
|
||||
|
||||
flush_pixel_cache();
|
||||
build_window_tables(COL);
|
||||
update_bg_info();
|
||||
|
||||
switch(regs.bg_mode) {
|
||||
case 0: render_line_mode0(); break;
|
||||
case 1: render_line_mode1(); break;
|
||||
case 2: render_line_mode2(); break;
|
||||
case 3: render_line_mode3(); break;
|
||||
case 4: render_line_mode4(); break;
|
||||
case 5: render_line_mode5(); break;
|
||||
case 6: render_line_mode6(); break;
|
||||
case 7: render_line_mode7(); break;
|
||||
}
|
||||
|
||||
render_line_output();
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,5 +1,4 @@
|
|||
//bppu_render.cpp
|
||||
inline bool render_enabled(uint8 bg, uint8 pri);
|
||||
//render.cpp
|
||||
inline void render_line_mode0();
|
||||
inline void render_line_mode1();
|
||||
inline void render_line_mode2();
|
||||
|
@ -9,11 +8,11 @@ inline void render_line_mode5();
|
|||
inline void render_line_mode6();
|
||||
inline void render_line_mode7();
|
||||
|
||||
//bppu_render_cache.cpp
|
||||
//cache.cpp
|
||||
enum { COLORDEPTH_4 = 0, COLORDEPTH_16 = 1, COLORDEPTH_256 = 2 };
|
||||
enum { TILE_2BIT = 0, TILE_4BIT = 1, TILE_8BIT = 2 };
|
||||
|
||||
struct _pixel {
|
||||
struct pixel_t {
|
||||
//bgr555 color data for main/subscreen pixels: 0x0000 = transparent / use palette color # 0
|
||||
//needs to be bgr555 instead of palette index for direct color mode ($2130 bit 0) to work
|
||||
uint16 src_main, src_sub;
|
||||
|
@ -29,32 +28,32 @@ struct _pixel {
|
|||
uint8 *bg_tiledata[3];
|
||||
uint8 *bg_tiledata_state[3]; //0 = valid, 1 = dirty
|
||||
|
||||
void render_bg_tile(uint8 color_depth, uint16 tile_num);
|
||||
template<unsigned color_depth> void render_bg_tile(uint16 tile_num);
|
||||
inline void flush_pixel_cache();
|
||||
void alloc_tiledata_cache();
|
||||
void flush_tiledata_cache();
|
||||
void free_tiledata_cache();
|
||||
|
||||
//bppu_render_windows.cpp
|
||||
struct _window {
|
||||
//windows.cpp
|
||||
struct window_t {
|
||||
uint8 main[256], sub[256];
|
||||
} window[6];
|
||||
|
||||
void build_window_table(uint8 bg, bool mainscreen);
|
||||
void build_window_tables(uint8 bg);
|
||||
|
||||
//bppu_render_bg.cpp
|
||||
//bg.cpp
|
||||
struct {
|
||||
uint16 tw, th; //tile width, height
|
||||
uint16 mx, my; //screen mask x, y
|
||||
uint16 scx, scy; //sc index offsets
|
||||
} bg_info[4];
|
||||
void update_bg_info();
|
||||
|
||||
void update_bg_info();
|
||||
uint16 bg_get_tile(uint8 bg, uint16 x, uint16 y);
|
||||
void render_line_bg(uint8 bg, uint8 color_depth, uint8 pri0_pos, uint8 pri1_pos);
|
||||
template<unsigned bg> uint16 bg_get_tile(uint16 x, uint16 y);
|
||||
template<unsigned mode, unsigned bg, unsigned color_depth> void render_line_bg(uint8 pri0_pos, uint8 pri1_pos);
|
||||
|
||||
//bppu_render_oam.cpp
|
||||
//oam.cpp
|
||||
struct sprite_item {
|
||||
uint8 width, height;
|
||||
uint16 x, y;
|
||||
|
@ -64,6 +63,7 @@ struct sprite_item {
|
|||
uint8 palette;
|
||||
uint8 priority;
|
||||
} sprite_list[128];
|
||||
bool sprite_list_valid;
|
||||
unsigned active_sprite;
|
||||
|
||||
uint8 oam_itemlist[32];
|
||||
|
@ -81,15 +81,14 @@ void load_oam_tiles();
|
|||
void render_oam_tile(int tile_num);
|
||||
void render_line_oam_rto();
|
||||
void render_line_oam(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos);
|
||||
void render_line_oam_lores(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos);
|
||||
|
||||
//bppu_render_mode7.cpp
|
||||
void render_line_mode7(uint8 bg, uint8 pri0_pos, uint8 pri1_pos);
|
||||
//mode7.cpp
|
||||
template<unsigned bg> void render_line_mode7(uint8 pri0_pos, uint8 pri1_pos);
|
||||
|
||||
//bppu_render_addsub.cpp
|
||||
//addsub.cpp
|
||||
inline uint16 addsub(uint32 x, uint32 y, bool halve);
|
||||
|
||||
//bppu_render_line.cpp
|
||||
//line.cpp
|
||||
inline uint16 get_palette(uint8 index);
|
||||
inline uint16 get_direct_color(uint8 p, uint8 t);
|
||||
inline uint16 get_pixel_normal(uint32 x);
|
|
@ -0,0 +1,70 @@
|
|||
#ifdef BPPU_CPP
|
||||
|
||||
//screen: 0 = main, 1 = sub
|
||||
void bPPU::build_window_table(uint8 bg, bool screen) {
|
||||
bool set = 1, clr = 0;
|
||||
uint8 *table = (screen == 0 ? window[bg].main : window[bg].sub);
|
||||
|
||||
if(bg != COL) {
|
||||
if(screen == 0 && regs.window_enabled[bg] == false) {
|
||||
memset(table, 0, 256);
|
||||
return;
|
||||
}
|
||||
if(screen == 1 && regs.sub_window_enabled[bg] == false) {
|
||||
memset(table, 0, 256);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
switch(screen == 0 ? regs.color_mask : regs.colorsub_mask) {
|
||||
case 0: memset(table, 1, 256); return; //always
|
||||
case 3: memset(table, 0, 256); return; //never
|
||||
case 1: set = 1, clr = 0; break; //inside window only
|
||||
case 2: set = 0, clr = 1; break; //outside window only
|
||||
}
|
||||
}
|
||||
|
||||
const uint16 window1_left = regs.window1_left;
|
||||
const uint16 window1_right = regs.window1_right;
|
||||
const uint16 window2_left = regs.window2_left;
|
||||
const uint16 window2_right = regs.window2_right;
|
||||
|
||||
if(regs.window1_enabled[bg] == false && regs.window2_enabled[bg] == false) {
|
||||
memset(table, clr, 256);
|
||||
return;
|
||||
}
|
||||
|
||||
if(regs.window1_enabled[bg] == true && regs.window2_enabled[bg] == false) {
|
||||
if(regs.window1_invert[bg] == true) set ^= clr ^= set ^= clr;
|
||||
for(unsigned x = 0; x < 256; x++) {
|
||||
table[x] = (x >= window1_left && x <= window1_right) ? set : clr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(regs.window1_enabled[bg] == false && regs.window2_enabled[bg] == true) {
|
||||
if(regs.window2_invert[bg] == true) set ^= clr ^= set ^= clr;
|
||||
for(unsigned x = 0; x < 256; x++) {
|
||||
table[x] = (x >= window2_left && x <= window2_right) ? set : clr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for(unsigned x = 0; x < 256; x++) {
|
||||
bool w1_mask = (x >= window1_left && x <= window1_right) ^ regs.window1_invert[bg];
|
||||
bool w2_mask = (x >= window2_left && x <= window2_right) ^ regs.window2_invert[bg];
|
||||
|
||||
switch(regs.window_mask[bg]) {
|
||||
case 0: table[x] = (w1_mask | w2_mask) == 1 ? set : clr; break; //or
|
||||
case 1: table[x] = (w1_mask & w2_mask) == 1 ? set : clr; break; //and
|
||||
case 2: table[x] = (w1_mask ^ w2_mask) == 1 ? set : clr; break; //xor
|
||||
case 3: table[x] = (w1_mask ^ w2_mask) == 0 ? set : clr; break; //xnor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bPPU::build_window_tables(uint8 bg) {
|
||||
build_window_table(bg, 0);
|
||||
build_window_table(bg, 1);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -9,6 +9,10 @@ void bPPU::serialize(serializer &s) {
|
|||
s.integer(display.interlace);
|
||||
s.integer(display.overscan);
|
||||
|
||||
s.integer(cache.oam_basesize);
|
||||
s.integer(cache.oam_nameselect);
|
||||
s.integer(cache.oam_tdaddr);
|
||||
|
||||
s.integer(regs.ppu1_mdr);
|
||||
s.integer(regs.ppu2_mdr);
|
||||
for(unsigned n = 0; n < 4; n++) s.integer(regs.bg_y[n]);
|
||||
|
@ -118,10 +122,6 @@ void bPPU::serialize(serializer &s) {
|
|||
s.integer(regs.oam_itemcount);
|
||||
s.integer(regs.oam_tilecount);
|
||||
|
||||
s.integer(cache.oam_basesize);
|
||||
s.integer(cache.oam_nameselect);
|
||||
s.integer(cache.oam_tdaddr);
|
||||
|
||||
for(unsigned n = 0; n < 256; n++) {
|
||||
s.integer(pixel_cache[n].src_main);
|
||||
s.integer(pixel_cache[n].src_sub);
|
||||
|
@ -162,6 +162,7 @@ void bPPU::serialize(serializer &s) {
|
|||
s.integer(sprite_list[n].palette);
|
||||
s.integer(sprite_list[n].priority);
|
||||
}
|
||||
s.integer(sprite_list_valid);
|
||||
s.integer(active_sprite);
|
||||
|
||||
s.array(oam_itemlist, 32);
|
||||
|
|
|
@ -74,7 +74,7 @@ void PPUcounter::reset() {
|
|||
status.interlace = false;
|
||||
status.field = 0;
|
||||
status.vcounter = 0;
|
||||
status.hcounter = 186;
|
||||
status.hcounter = 0;
|
||||
history.index = 0;
|
||||
|
||||
for(unsigned i = 0; i < 2048; i++) {
|
||||
|
|
|
@ -21,7 +21,7 @@ void SMPcore::disassemble_opcode(char *output) {
|
|||
opdp0 = ((unsigned)regs.p.p << 8) + op0;
|
||||
opdp1 = ((unsigned)regs.p.p << 8) + op1;
|
||||
|
||||
strcpy(t, " ");
|
||||
strcpy(t, " ");
|
||||
|
||||
switch(op) {
|
||||
case 0x00: sprintf(t, "nop"); break;
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
#ifdef SSMP_CPP
|
||||
|
||||
void sSMPdebug::op_step() {
|
||||
bool break_event = false;
|
||||
|
||||
if(debugger.step_smp) {
|
||||
debugger.break_event = Debugger::SMPStep;
|
||||
break_event = true;
|
||||
}
|
||||
|
||||
for(unsigned i = 0; i < Debugger::Breakpoints; i++) {
|
||||
if(debugger.breakpoint[i].enabled == false) continue;
|
||||
if(debugger.breakpoint[i].addr != regs.pc) continue;
|
||||
if(debugger.breakpoint[i].mode != Debugger::Breakpoint::Exec) continue;
|
||||
if(debugger.breakpoint[i].source != Debugger::Breakpoint::APURAM) continue;
|
||||
|
||||
debugger.breakpoint[i].counter++;
|
||||
debugger.breakpoint_hit = i;
|
||||
debugger.break_event = Debugger::BreakpointHit;
|
||||
break_event = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(break_event) scheduler.exit();
|
||||
|
||||
if(debugger.trace_smp) {
|
||||
char t[256];
|
||||
disassemble_opcode(t);
|
||||
debugger.tracefile.print(string() << t << "\n");
|
||||
}
|
||||
|
||||
sSMP::op_step();
|
||||
scheduler.sync_smpcpu();
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,4 @@
|
|||
class sSMPdebug : public sSMP {
|
||||
public:
|
||||
void op_step();
|
||||
};
|
|
@ -3,6 +3,13 @@
|
|||
#define SSMP_CPP
|
||||
namespace SNES {
|
||||
|
||||
#if defined(DEBUGGER)
|
||||
#include "debugger/debugger.cpp"
|
||||
sSMPdebug smp;
|
||||
#else
|
||||
sSMP smp;
|
||||
#endif
|
||||
|
||||
#include "serialization.cpp"
|
||||
#include "memory/memory.cpp"
|
||||
#include "timing/timing.cpp"
|
||||
|
@ -11,9 +18,7 @@ void sSMP::enter() {
|
|||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SyncAll) scheduler.exit();
|
||||
|
||||
tracer.trace_smpop(); //traces SMP opcode (only if tracer is enabled)
|
||||
|
||||
(this->*opcode_table[op_readpc()])();
|
||||
op_step();
|
||||
|
||||
//forcefully sync S-CPU and S-SMP, in case chips are not communicating
|
||||
if(++instruction_counter >= 128) {
|
||||
|
@ -23,6 +28,10 @@ void sSMP::enter() {
|
|||
}
|
||||
}
|
||||
|
||||
void sSMP::op_step() {
|
||||
(this->*opcode_table[op_readpc()])();
|
||||
}
|
||||
|
||||
void sSMP::power() {
|
||||
//targets not initialized/changed upon reset
|
||||
t0.target = 0;
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
class sSMPdebug;
|
||||
|
||||
class sSMP : public SMP, public SMPcore {
|
||||
public:
|
||||
void enter();
|
||||
debugvirtual void op_step();
|
||||
|
||||
#include "memory/memory.hpp"
|
||||
#include "timing/timing.hpp"
|
||||
|
@ -38,4 +41,13 @@ public:
|
|||
void serialize(serializer&);
|
||||
sSMP();
|
||||
~sSMP();
|
||||
|
||||
friend class sSMPdebug;
|
||||
};
|
||||
|
||||
#if defined(DEBUGGER)
|
||||
#include "debugger/debugger.hpp"
|
||||
extern sSMPdebug smp;
|
||||
#else
|
||||
extern sSMP smp;
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
#ifdef SYSTEM_CPP
|
||||
|
||||
Debugger debugger;
|
||||
|
||||
uint8 Debugger::read(Debugger::MemorySource source, unsigned addr) {
|
||||
switch(source) {
|
||||
case CPUBus: {
|
||||
//do not read from memory-mapped registers that could affect program behavior
|
||||
if(((addr - 0x2000) & 0x40c000) == 0x000000) break; //$00-3f:2000-5fff MMIO
|
||||
return bus.read(addr & 0xffffff);
|
||||
} break;
|
||||
|
||||
case APURAM: {
|
||||
return memory::apuram.read(addr & 0xffff);
|
||||
} break;
|
||||
|
||||
case VRAM: {
|
||||
return memory::vram.read(addr & 0xffff);
|
||||
} break;
|
||||
|
||||
case OAM: {
|
||||
if(addr & 0x0200) return memory::oam.read(0x0200 + (addr & 0x1f));
|
||||
return memory::oam.read(addr & 0x01ff);
|
||||
} break;
|
||||
|
||||
case CGRAM: {
|
||||
return memory::cgram.read(addr & 0x01ff);
|
||||
} break;
|
||||
}
|
||||
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
Debugger::Debugger() {
|
||||
break_event = None;
|
||||
|
||||
for(unsigned n = 0; n < Breakpoints; n++) {
|
||||
breakpoint[n].enabled = false;
|
||||
breakpoint[n].addr = 0;
|
||||
breakpoint[n].data = -1;
|
||||
breakpoint[n].mode = Breakpoint::Exec;
|
||||
breakpoint[n].source = Breakpoint::CPUBus;
|
||||
breakpoint[n].counter = 0;
|
||||
}
|
||||
breakpoint_hit = 0;
|
||||
|
||||
step_cpu = false;
|
||||
step_smp = false;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,34 @@
|
|||
class Debugger {
|
||||
public:
|
||||
enum BreakEvent {
|
||||
None,
|
||||
BreakpointHit,
|
||||
CPUStep,
|
||||
SMPStep,
|
||||
} break_event;
|
||||
|
||||
enum { Breakpoints = 8 };
|
||||
struct Breakpoint {
|
||||
bool enabled;
|
||||
unsigned addr;
|
||||
signed data; //-1 = unused
|
||||
enum Mode { Exec, Read, Write } mode;
|
||||
enum Source { CPUBus, APURAM, VRAM, OAM, CGRAM } source;
|
||||
unsigned counter; //number of times breakpoint has been hit since being set
|
||||
} breakpoint[Breakpoints];
|
||||
unsigned breakpoint_hit;
|
||||
|
||||
bool step_cpu;
|
||||
bool step_smp;
|
||||
|
||||
file tracefile;
|
||||
bool trace_cpu;
|
||||
bool trace_smp;
|
||||
|
||||
enum MemorySource { CPUBus, APURAM, VRAM, OAM, CGRAM };
|
||||
uint8 read(MemorySource, unsigned addr);
|
||||
|
||||
Debugger();
|
||||
};
|
||||
|
||||
extern Debugger debugger;
|
|
@ -1,9 +1,9 @@
|
|||
#ifdef SYSTEM_CPP
|
||||
|
||||
serializer System::serialize() {
|
||||
serializer s(1024 * 1024);
|
||||
serializer s(serialize_size);
|
||||
|
||||
unsigned signature = 0x31545342, version = 1, crc32 = cartridge.crc32();
|
||||
unsigned signature = 0x31545342, version = bsnesSaveStateVersion, crc32 = cartridge.crc32();
|
||||
s.integer(signature);
|
||||
s.integer(version);
|
||||
s.integer(crc32);
|
||||
|
@ -19,7 +19,7 @@ bool System::unserialize(serializer &s) {
|
|||
s.integer(crc32);
|
||||
|
||||
if(signature != 0x31545342) return false;
|
||||
if(version != 1) return false;
|
||||
if(version != bsnesSaveStateVersion) return false;
|
||||
if(crc32 != cartridge.crc32()) return false;
|
||||
scheduler.init();
|
||||
|
||||
|
@ -55,4 +55,19 @@ void System::serialize_all(serializer &s) {
|
|||
if(cartridge.has_obc1()) obc1.serialize(s);
|
||||
}
|
||||
|
||||
//called once upon cartridge load event: perform dry-run state save.
|
||||
//determines exactly how many bytes are needed to save state for this cartridge,
|
||||
//as amount varies per game (eg different RAM sizes, special chips, etc.)
|
||||
void System::serialize_init() {
|
||||
serializer s;
|
||||
|
||||
unsigned signature = 0, version = 0, crc32 = 0;
|
||||
s.integer(signature);
|
||||
s.integer(version);
|
||||
s.integer(crc32);
|
||||
|
||||
serialize_all(s);
|
||||
serialize_size = s.size();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -52,10 +52,10 @@ serializer StateManager::load(const char *filename, uint8 slot) {
|
|||
file fp;
|
||||
if(fp.open(filename, file::mode_read) == false) throw;
|
||||
|
||||
fp.seek(HeaderSize + StateSize * index);
|
||||
uint8 *data = new uint8[StateSize];
|
||||
fp.read(data, StateSize);
|
||||
serializer s(data, StateSize);
|
||||
fp.seek(HeaderSize + system.serialize_size * index);
|
||||
uint8 *data = new uint8[system.serialize_size];
|
||||
fp.read(data, system.serialize_size);
|
||||
serializer s(data, system.serialize_size);
|
||||
delete[] data;
|
||||
fp.close();
|
||||
return s;
|
||||
|
@ -98,9 +98,9 @@ bool StateManager::save(const char *filename, uint8 slot, serializer &s, const c
|
|||
fp.seek(DescIndex + index * DescriptionSize);
|
||||
fp.write((uint8*)&desc[0], DescriptionSize);
|
||||
|
||||
fp.seek(HeaderSize + index * StateSize);
|
||||
fp.seek(HeaderSize + index * system.serialize_size);
|
||||
fp.write(s.data(), s.size());
|
||||
for(unsigned n = 0; n < StateSize - s.size(); n++) fp.write(0x00);
|
||||
for(unsigned n = 0; n < system.serialize_size - s.size(); n++) fp.write(0x00);
|
||||
|
||||
fp.close();
|
||||
return true;
|
||||
|
@ -126,12 +126,12 @@ bool StateManager::erase(const char *filename, uint8 slot) {
|
|||
fp.seek(DescIndex + index * DescriptionSize);
|
||||
fp.write((uint8*)&info.description[lastslot * DescriptionSize], DescriptionSize);
|
||||
|
||||
fp.seek(HeaderSize + StateSize * lastslot);
|
||||
uint8 *data = new uint8[StateSize];
|
||||
fp.read(data, StateSize);
|
||||
fp.seek(HeaderSize + system.serialize_size * lastslot);
|
||||
uint8 *data = new uint8[system.serialize_size];
|
||||
fp.read(data, system.serialize_size);
|
||||
|
||||
fp.seek(HeaderSize + StateSize * index);
|
||||
fp.write(data, StateSize);
|
||||
fp.seek(HeaderSize + system.serialize_size * index);
|
||||
fp.write(data, system.serialize_size);
|
||||
delete[] data;
|
||||
|
||||
//decrement all IDs after the deleted one (removes empty slot ID from deletion)
|
||||
|
@ -143,7 +143,7 @@ bool StateManager::erase(const char *filename, uint8 slot) {
|
|||
fp.write(info.slot, 256);
|
||||
|
||||
unsigned size = fp.size();
|
||||
fp.truncate(size - StateSize);
|
||||
fp.truncate(size - system.serialize_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -154,19 +154,19 @@ bool StateManager::load(const char *filename) {
|
|||
if(filesize < HeaderSize) return false;
|
||||
fp.seek(0);
|
||||
if(fp.readl(4) != 0x31415342) return false;
|
||||
if(fp.readl(4) != 1) return false;
|
||||
if(fp.readl(4) != bsnesSaveStateVersion) return false;
|
||||
fp.read((uint8*)&info.slot[0], 256);
|
||||
fp.read((uint8*)&info.datetime[0], 256 * DateTimeSize);
|
||||
fp.read((uint8*)&info.description[0], 256 * DescriptionSize);
|
||||
info.slotcount = (filesize - HeaderSize) / StateSize;
|
||||
info.slotcount = (filesize - HeaderSize) / system.serialize_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StateManager::create(const char *filename) const {
|
||||
file fp;
|
||||
if(fp.open(filename, file::mode_write) == false) return false;
|
||||
fp.writel(0x31415342, 4); //signature ('BSA1')
|
||||
fp.writel(1, 4); //version
|
||||
fp.writel(0x31415342, 4); //signature ('BSA1')
|
||||
fp.writel(bsnesSaveStateVersion, 4); //version
|
||||
for(unsigned i = 0; i < 256 * SlotSize; i++) fp.write(SlotInvalid); //slot index
|
||||
for(unsigned i = 0; i < 256 * DateTimeSize; i++) fp.write(0x20); //date / time
|
||||
for(unsigned i = 0; i < 256 * DescriptionSize; i++) fp.write(0x00); //description
|
||||
|
|
|
@ -6,7 +6,6 @@ public:
|
|||
SlotSize = 1,
|
||||
DateTimeSize = 19,
|
||||
DescriptionSize = 512,
|
||||
StateSize = 1024 * 1024,
|
||||
|
||||
HeaderSize = 8 + (256 * SlotSize) + (256 * DateTimeSize) + (256 * DescriptionSize),
|
||||
SlotIndex = 8,
|
||||
|
|
|
@ -3,18 +3,13 @@
|
|||
#define SYSTEM_CPP
|
||||
namespace SNES {
|
||||
|
||||
System system;
|
||||
BUSCORE bus;
|
||||
CPUCORE cpu;
|
||||
SMPCORE smp;
|
||||
DSPCORE dsp;
|
||||
PPUCORE ppu;
|
||||
System system;
|
||||
|
||||
#include "config/config.cpp"
|
||||
#include "debugger/debugger.cpp"
|
||||
#include "serialization.cpp"
|
||||
#include "scheduler/scheduler.cpp"
|
||||
#include "statemanager/statemanager.cpp"
|
||||
#include "tracer/tracer.cpp"
|
||||
|
||||
#include "video/video.cpp"
|
||||
#include "audio/audio.cpp"
|
||||
|
@ -32,9 +27,6 @@ void System::coprocessor_enter() {
|
|||
}
|
||||
|
||||
void System::run() {
|
||||
}
|
||||
|
||||
void System::runtoframe() {
|
||||
scheduler.sync = Scheduler::SyncNone;
|
||||
|
||||
scheduler.enter();
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#include "config/config.hpp"
|
||||
#include "debugger/debugger.hpp"
|
||||
#include "interface/interface.hpp"
|
||||
#include "scheduler/scheduler.hpp"
|
||||
#include "statemanager/statemanager.hpp"
|
||||
#include "tracer/tracer.hpp"
|
||||
|
||||
#include "video/video.hpp"
|
||||
#include "audio/audio.hpp"
|
||||
|
@ -18,7 +18,6 @@ public:
|
|||
|
||||
//system functions
|
||||
virtual void run();
|
||||
virtual void runtoframe();
|
||||
virtual void runtosave();
|
||||
|
||||
virtual void init(Interface*);
|
||||
|
@ -41,16 +40,20 @@ public:
|
|||
virtual ~System() {}
|
||||
|
||||
private:
|
||||
unsigned serialize_size;
|
||||
void serialize(serializer&);
|
||||
void serialize_all(serializer&);
|
||||
void serialize_init();
|
||||
|
||||
Interface *interface;
|
||||
unsigned snes_region;
|
||||
unsigned snes_expansion;
|
||||
|
||||
friend class Cartridge;
|
||||
friend class Video;
|
||||
friend class Audio;
|
||||
friend class Input;
|
||||
friend class StateManager;
|
||||
};
|
||||
|
||||
extern System system;
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
#ifdef SYSTEM_CPP
|
||||
|
||||
Tracer tracer;
|
||||
|
||||
void tprintf(const char *s, ...) {
|
||||
if(tracer.enabled() == false) return;
|
||||
|
||||
char str[4096];
|
||||
va_list args;
|
||||
va_start(args, s);
|
||||
vsprintf(str, s, args);
|
||||
va_end(args);
|
||||
fprintf(tracer.fp, "%s\r\n", str);
|
||||
}
|
||||
|
||||
void Tracer::trace_cpuop() {
|
||||
if(enabled() == false) return;
|
||||
if(cpuop_enabled() == false) return;
|
||||
|
||||
if(cpuopmask_enabled() == true) {
|
||||
unsigned addr = cpu.regs.pc.d;
|
||||
if(settings.cpuopmasktbl[addr >> 3] & 0x80 >> (addr & 7)) return;
|
||||
settings.cpuopmasktbl[addr >> 3] |= 0x80 >> (addr & 7);
|
||||
}
|
||||
|
||||
char t[1024];
|
||||
cpu.disassemble_opcode(t);
|
||||
fprintf(fp, "%s\r\n", t);
|
||||
}
|
||||
|
||||
void Tracer::trace_smpop() {
|
||||
if(enabled() == false) return;
|
||||
if(smpop_enabled() == false) return;
|
||||
|
||||
if(smpopmask_enabled() == true) {
|
||||
unsigned addr = smp.regs.pc;
|
||||
if(settings.smpopmasktbl[addr >> 3] & 0x80 >> (addr & 7)) return;
|
||||
settings.smpopmasktbl[addr >> 3] |= 0x80 >> (addr & 7);
|
||||
}
|
||||
|
||||
char t[1024];
|
||||
smp.disassemble_opcode(t);
|
||||
fprintf(fp, "%s\r\n", t);
|
||||
}
|
||||
|
||||
void Tracer::enable(bool en) {
|
||||
if(en == true && enabled() == false) {
|
||||
fp = fopen("trace.log", "wb");
|
||||
} else if(en == false && enabled() == true) {
|
||||
fclose(fp);
|
||||
fp = 0;
|
||||
}
|
||||
|
||||
settings.enabled = en;
|
||||
}
|
||||
|
||||
void Tracer::cpuopmask_enable(bool en) {
|
||||
if(en == true && cpuopmask_enabled() == false) {
|
||||
settings.cpuopmasktbl = new(zeromemory) uint8_t[0x200000];
|
||||
} else if(en == false && cpuopmask_enabled() == true) {
|
||||
delete[] settings.cpuopmasktbl;
|
||||
}
|
||||
|
||||
settings.cpuopmask = en;
|
||||
}
|
||||
|
||||
void Tracer::smpopmask_enable(bool en) {
|
||||
if(en == true && smpopmask_enabled() == false) {
|
||||
settings.smpopmasktbl = new(zeromemory) uint8_t[0x2000];
|
||||
} else if(en == false && smpopmask_enabled() == true) {
|
||||
delete[] settings.smpopmasktbl;
|
||||
}
|
||||
|
||||
settings.smpopmask = en;
|
||||
}
|
||||
|
||||
Tracer::Tracer() {
|
||||
fp = 0;
|
||||
|
||||
settings.cpuop = false;
|
||||
settings.cpuopmask = false;
|
||||
settings.cpuopmasktbl = 0;
|
||||
|
||||
settings.smpop = false;
|
||||
settings.smpopmask = false;
|
||||
settings.smpopmasktbl = 0;
|
||||
}
|
||||
|
||||
Tracer::~Tracer() {
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,45 +0,0 @@
|
|||
void tprintf(const char *s, ...);
|
||||
|
||||
class Tracer {
|
||||
private:
|
||||
|
||||
FILE *fp;
|
||||
|
||||
struct {
|
||||
bool enabled;
|
||||
|
||||
bool cpuop;
|
||||
bool cpuopmask;
|
||||
uint8 *cpuopmasktbl;
|
||||
|
||||
bool smpop;
|
||||
bool smpopmask;
|
||||
uint8 *smpopmasktbl;
|
||||
} settings;
|
||||
|
||||
public:
|
||||
void enable(bool en);
|
||||
bool enabled() { return settings.enabled; }
|
||||
|
||||
void cpuop_enable(bool en) { settings.cpuop = en; }
|
||||
bool cpuop_enabled() { return settings.cpuop; }
|
||||
|
||||
void cpuopmask_enable(bool en);
|
||||
bool cpuopmask_enabled() { return settings.cpuopmask; }
|
||||
|
||||
void smpop_enable(bool en) { settings.smpop = en; }
|
||||
bool smpop_enabled() { return settings.smpop; }
|
||||
|
||||
void smpopmask_enable(bool en);
|
||||
bool smpopmask_enabled() { return settings.smpopmask; }
|
||||
|
||||
void trace_cpuop();
|
||||
void trace_smpop();
|
||||
|
||||
Tracer();
|
||||
~Tracer();
|
||||
|
||||
friend void tprintf(const char *s, ...);
|
||||
};
|
||||
|
||||
extern Tracer tracer;
|
|
@ -46,7 +46,7 @@ void Application::locateFile(string &filename, bool createDataDirectory) {
|
|||
filename = temp;
|
||||
}
|
||||
|
||||
int Application::main(int argc, char **argv) {
|
||||
int Application::main(int &argc, char **argv) {
|
||||
app = new App(argc, argv);
|
||||
#if !defined(PLATFORM_WIN)
|
||||
//Windows port uses 256x256 icon from resource file
|
||||
|
@ -109,7 +109,10 @@ void Application::run() {
|
|||
}
|
||||
|
||||
if(SNES::cartridge.loaded() && !pause && !autopause) {
|
||||
SNES::system.runtoframe();
|
||||
if(SNES::debugger.break_event == SNES::Debugger::None) {
|
||||
SNES::system.run();
|
||||
if(SNES::debugger.break_event != SNES::Debugger::None) debugger->event();
|
||||
}
|
||||
} else {
|
||||
usleep(20 * 1000);
|
||||
}
|
||||
|
@ -153,4 +156,3 @@ Application::~Application() {
|
|||
//deleting (QApplication)app will segfault the application upon exit
|
||||
//delete app;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ public:
|
|||
bool winEventFilter(MSG *msg, long *result);
|
||||
#endif
|
||||
|
||||
App(int argc, char **argv) : QApplication(argc, argv) {}
|
||||
App(int &argc, char **argv) : QApplication(argc, argv) {}
|
||||
} *app;
|
||||
|
||||
QTimer *timer;
|
||||
|
@ -25,7 +25,7 @@ public:
|
|||
string configFilename;
|
||||
string styleSheetFilename;
|
||||
|
||||
int main(int argc, char **argv);
|
||||
int main(int &argc, char **argv);
|
||||
void processEvents();
|
||||
void locateFile(string &filename, bool createDataDirectory = false);
|
||||
void initPaths(const char *basename);
|
||||
|
@ -37,4 +37,3 @@ public:
|
|||
public slots:
|
||||
void run();
|
||||
} application;
|
||||
|
||||
|
|
|
@ -57,6 +57,9 @@ void Application::init() {
|
|||
utility.updateFullscreenState();
|
||||
application.processEvents();
|
||||
|
||||
debugger = new Debugger;
|
||||
debugger->setup();
|
||||
|
||||
settingsWindow = new SettingsWindow;
|
||||
settingsWindow->setup();
|
||||
|
||||
|
|
|
@ -10,96 +10,96 @@ void MainWindow::setup() {
|
|||
#endif
|
||||
|
||||
system = window->menu->addMenu("System");
|
||||
system_load = system->addAction("&Load Cartridge ...");
|
||||
system_load = system->addAction("Load Cartridge ...");
|
||||
system->addSeparator();
|
||||
system_power = system->addMenu("&Power");
|
||||
system_power = system->addMenu("Power");
|
||||
system_power_on = system_power->addAction("On");
|
||||
system_power_on->setCheckable(true);
|
||||
system_power_off = system_power->addAction("Off");
|
||||
system_power_off->setCheckable(true);
|
||||
system_reset = system->addAction("&Reset");
|
||||
system_reset = system->addAction("Reset");
|
||||
system->addSeparator();
|
||||
system_port1 = system->addMenu("Controller Port &1");
|
||||
system_port1_none = system_port1->addAction("&None");
|
||||
system_port1 = system->addMenu("Controller Port 1");
|
||||
system_port1_none = system_port1->addAction("None");
|
||||
system_port1_none->setCheckable(true);
|
||||
system_port1_joypad = system_port1->addAction("&Joypad");
|
||||
system_port1_joypad = system_port1->addAction("Joypad");
|
||||
system_port1_joypad->setCheckable(true);
|
||||
system_port1_multitap = system_port1->addAction("&Multitap");
|
||||
system_port1_multitap = system_port1->addAction("Multitap");
|
||||
system_port1_multitap->setCheckable(true);
|
||||
system_port1_mouse = system_port1->addAction("M&ouse");
|
||||
system_port1_mouse = system_port1->addAction("Mouse");
|
||||
system_port1_mouse->setCheckable(true);
|
||||
system_port2 = system->addMenu("Controller Port &2");
|
||||
system_port2_none = system_port2->addAction("&None");
|
||||
system_port2 = system->addMenu("Controller Port 2");
|
||||
system_port2_none = system_port2->addAction("None");
|
||||
system_port2_none->setCheckable(true);
|
||||
system_port2_joypad = system_port2->addAction("&Joypad");
|
||||
system_port2_joypad = system_port2->addAction("Joypad");
|
||||
system_port2_joypad->setCheckable(true);
|
||||
system_port2_multitap = system_port2->addAction("&Multitap");
|
||||
system_port2_multitap = system_port2->addAction("Multitap");
|
||||
system_port2_multitap->setCheckable(true);
|
||||
system_port2_mouse = system_port2->addAction("M&ouse");
|
||||
system_port2_mouse = system_port2->addAction("Mouse");
|
||||
system_port2_mouse->setCheckable(true);
|
||||
system_port2_superscope = system_port2->addAction("&Super Scope");
|
||||
system_port2_superscope = system_port2->addAction("Super Scope");
|
||||
system_port2_superscope->setCheckable(true);
|
||||
system_port2_justifier = system_port2->addAction("&Justifier");
|
||||
system_port2_justifier = system_port2->addAction("Justifier");
|
||||
system_port2_justifier->setCheckable(true);
|
||||
system_port2_justifiers = system_port2->addAction("&Two Justifiers");
|
||||
system_port2_justifiers = system_port2->addAction("Two Justifiers");
|
||||
system_port2_justifiers->setCheckable(true);
|
||||
#if !defined(PLATFORM_OSX)
|
||||
system->addSeparator();
|
||||
#endif
|
||||
system_exit = system->addAction("E&xit");
|
||||
system_exit = system->addAction("Exit");
|
||||
system_exit->setMenuRole(QAction::QuitRole);
|
||||
|
||||
settings = window->menu->addMenu("Settings");
|
||||
settings_videoMode = settings->addMenu("&Video Mode");
|
||||
settings_videoMode_1x = settings_videoMode->addAction("Scale &1x");
|
||||
settings_videoMode = settings->addMenu("Video Mode");
|
||||
settings_videoMode_1x = settings_videoMode->addAction("Scale 1x");
|
||||
settings_videoMode_1x->setCheckable(true);
|
||||
settings_videoMode_2x = settings_videoMode->addAction("Scale &2x");
|
||||
settings_videoMode_2x = settings_videoMode->addAction("Scale 2x");
|
||||
settings_videoMode_2x->setCheckable(true);
|
||||
settings_videoMode_3x = settings_videoMode->addAction("Scale &3x");
|
||||
settings_videoMode_3x = settings_videoMode->addAction("Scale 3x");
|
||||
settings_videoMode_3x->setCheckable(true);
|
||||
settings_videoMode_4x = settings_videoMode->addAction("Scale &4x");
|
||||
settings_videoMode_4x = settings_videoMode->addAction("Scale 4x");
|
||||
settings_videoMode_4x->setCheckable(true);
|
||||
settings_videoMode_max = settings_videoMode->addAction("Scale &Max");
|
||||
settings_videoMode_max = settings_videoMode->addAction("Scale Max");
|
||||
settings_videoMode_max->setCheckable(true);
|
||||
settings_videoMode_max->setStatusTip("Scale video output to fill as much of the screen as possible");
|
||||
settings_videoMode->addSeparator();
|
||||
settings_videoMode_correctAspectRatio = settings_videoMode->addAction("&Correct Aspect Ratio");
|
||||
settings_videoMode_correctAspectRatio = settings_videoMode->addAction("Correct Aspect Ratio");
|
||||
settings_videoMode_correctAspectRatio->setStatusTip("Match pixel width-to-height ratio of TV");
|
||||
settings_videoMode_correctAspectRatio->setCheckable(true);
|
||||
settings_videoMode_fullscreen = settings_videoMode->addAction("&Fullscreen");
|
||||
settings_videoMode_fullscreen = settings_videoMode->addAction("Fullscreen");
|
||||
settings_videoMode_fullscreen->setCheckable(true);
|
||||
settings_videoMode->addSeparator();
|
||||
settings_videoMode_ntsc = settings_videoMode->addAction("&NTSC");
|
||||
settings_videoMode_ntsc = settings_videoMode->addAction("NTSC");
|
||||
settings_videoMode_ntsc->setCheckable(true);
|
||||
settings_videoMode_ntsc->setStatusTip("Size video output window to match NTSC TV spec");
|
||||
settings_videoMode_pal = settings_videoMode->addAction("&PAL");
|
||||
settings_videoMode_pal = settings_videoMode->addAction("PAL");
|
||||
settings_videoMode_pal->setCheckable(true);
|
||||
settings_videoMode_pal->setStatusTip("Size video output window to match PAL TV spec");
|
||||
settings_videoFilter = settings->addMenu("Video &Filter");
|
||||
settings_videoFilter_point = settings_videoFilter->addAction("&Point");
|
||||
settings_videoFilter = settings->addMenu("Video Filter");
|
||||
settings_videoFilter_point = settings_videoFilter->addAction("Point");
|
||||
settings_videoFilter_point->setCheckable(true);
|
||||
settings_videoFilter_point->setStatusTip("Use pixellated hardware video scaling");
|
||||
settings_videoFilter_linear = settings_videoFilter->addAction("&Linear");
|
||||
settings_videoFilter_linear = settings_videoFilter->addAction("Linear");
|
||||
settings_videoFilter_linear->setCheckable(true);
|
||||
settings_videoFilter_linear->setStatusTip("Use smoothed hardware video scaling");
|
||||
settings_videoFilter->addSeparator();
|
||||
settings_videoFilter_none = settings_videoFilter->addAction("&None");
|
||||
settings_videoFilter_none = settings_videoFilter->addAction("None");
|
||||
settings_videoFilter_none->setCheckable(true);
|
||||
settings_videoFilter_scanline = settings_videoFilter->addAction("&Scanline");
|
||||
settings_videoFilter_scanline = settings_videoFilter->addAction("Scanline");
|
||||
settings_videoFilter_scanline->setCheckable(true);
|
||||
settings_videoFilter_scale2x = settings_videoFilter->addAction("S&cale2x");
|
||||
settings_videoFilter_scale2x = settings_videoFilter->addAction("Scale2x");
|
||||
settings_videoFilter_scale2x->setCheckable(true);
|
||||
settings_videoFilter_lq2x = settings_videoFilter->addAction("&LQ2x");
|
||||
settings_videoFilter_lq2x = settings_videoFilter->addAction("LQ2x");
|
||||
settings_videoFilter_lq2x->setCheckable(true);
|
||||
settings_videoFilter_hq2x = settings_videoFilter->addAction("&HQ2x");
|
||||
settings_videoFilter_hq2x = settings_videoFilter->addAction("HQ2x");
|
||||
settings_videoFilter_hq2x->setCheckable(true);
|
||||
settings_videoFilter_ntsc = settings_videoFilter->addAction("N&TSC");
|
||||
settings_videoFilter_ntsc = settings_videoFilter->addAction("NTSC");
|
||||
settings_videoFilter_ntsc->setCheckable(true);
|
||||
settings->addSeparator();
|
||||
settings_muteAudio = settings->addAction("&Mute Audio Output");
|
||||
settings_muteAudio = settings->addAction("Mute Audio Output");
|
||||
settings_muteAudio->setCheckable(true);
|
||||
settings->addSeparator();
|
||||
settings_emulationSpeed = settings->addMenu("&Emulation Speed");
|
||||
settings_emulationSpeed = settings->addMenu("Emulation Speed");
|
||||
settings_emulationSpeed_slowest = settings_emulationSpeed->addAction("50%");
|
||||
settings_emulationSpeed_slowest->setCheckable(true);
|
||||
settings_emulationSpeed_slow = settings_emulationSpeed->addAction("75%");
|
||||
|
@ -111,26 +111,33 @@ void MainWindow::setup() {
|
|||
settings_emulationSpeed_fastest = settings_emulationSpeed->addAction("200%");
|
||||
settings_emulationSpeed_fastest->setCheckable(true);
|
||||
settings_emulationSpeed->addSeparator();
|
||||
settings_emulationSpeed_syncVideo = settings_emulationSpeed->addAction("Sync &Video");
|
||||
settings_emulationSpeed_syncVideo = settings_emulationSpeed->addAction("Sync Video");
|
||||
settings_emulationSpeed_syncVideo->setCheckable(true);
|
||||
settings_emulationSpeed_syncVideo->setStatusTip("Sync video output to vertical refresh rate");
|
||||
settings_emulationSpeed_syncAudio = settings_emulationSpeed->addAction("Sync &Audio");
|
||||
settings_emulationSpeed_syncAudio = settings_emulationSpeed->addAction("Sync Audio");
|
||||
settings_emulationSpeed_syncAudio->setCheckable(true);
|
||||
settings_emulationSpeed_syncAudio->setStatusTip("Sync audio output to sound card output rate");
|
||||
settings_configuration = settings->addAction("&Configuration ...");
|
||||
settings_configuration = settings->addAction("Configuration ...");
|
||||
settings_configuration->setMenuRole(QAction::PreferencesRole);
|
||||
|
||||
tools = window->menu->addMenu("Tools");
|
||||
tools_cheatEditor = tools->addAction("&Cheat Editor ...");
|
||||
tools_stateManager = tools->addAction("&State Manager ...");
|
||||
tools_cheatEditor = tools->addAction("Cheat Editor ...");
|
||||
tools_stateManager = tools->addAction("State Manager ...");
|
||||
#if defined(DEBUGGER)
|
||||
tools->addSeparator();
|
||||
#endif
|
||||
tools_debugger = tools->addAction("Debugger ...");
|
||||
#if !defined(DEBUGGER)
|
||||
tools_debugger->setVisible(false);
|
||||
#endif
|
||||
|
||||
help = window->menu->addMenu("Help");
|
||||
help_documentation = help->addAction("&Documentation ...");
|
||||
help_license = help->addAction("&License ...");
|
||||
help_documentation = help->addAction("Documentation ...");
|
||||
help_license = help->addAction("License ...");
|
||||
#if !defined(PLATFORM_OSX)
|
||||
help->addSeparator();
|
||||
#endif
|
||||
help_about = help->addAction("&About ...");
|
||||
help_about = help->addAction("About ...");
|
||||
help_about->setMenuRole(QAction::AboutRole);
|
||||
|
||||
canvasContainer = new CanvasObject;
|
||||
|
@ -206,6 +213,7 @@ void MainWindow::setup() {
|
|||
connect(settings_configuration, SIGNAL(triggered()), this, SLOT(showConfigWindow()));
|
||||
connect(tools_cheatEditor, SIGNAL(triggered()), this, SLOT(showCheatEditor()));
|
||||
connect(tools_stateManager, SIGNAL(triggered()), this, SLOT(showStateManager()));
|
||||
connect(tools_debugger, SIGNAL(triggered()), this, SLOT(showDebugger()));
|
||||
connect(help_documentation, SIGNAL(triggered()), this, SLOT(showDocumentation()));
|
||||
connect(help_license, SIGNAL(triggered()), this, SLOT(showLicense()));
|
||||
connect(help_about, SIGNAL(triggered()), this, SLOT(showAbout()));
|
||||
|
@ -330,6 +338,7 @@ void MainWindow::showConfigWindow() {
|
|||
|
||||
void MainWindow::showCheatEditor() { toolsWindow->showCheatEditor(); }
|
||||
void MainWindow::showStateManager() { toolsWindow->showStateManager(); }
|
||||
void MainWindow::showDebugger() { debugger->show(); }
|
||||
|
||||
void MainWindow::showDocumentation() {
|
||||
QFile file(":/documentation.html");
|
||||
|
|
|
@ -73,6 +73,7 @@ public:
|
|||
QMenu *tools;
|
||||
QAction *tools_cheatEditor;
|
||||
QAction *tools_stateManager;
|
||||
QAction *tools_debugger;
|
||||
QMenu *help;
|
||||
QAction *help_documentation;
|
||||
QAction *help_license;
|
||||
|
@ -131,6 +132,7 @@ public slots:
|
|||
void showConfigWindow();
|
||||
void showCheatEditor();
|
||||
void showStateManager();
|
||||
void showDebugger();
|
||||
void showDocumentation();
|
||||
void showLicense();
|
||||
void showAbout();
|
||||
|
|
|
@ -62,6 +62,7 @@ Configuration::Configuration() {
|
|||
|
||||
attach(path.rom = "", "path.rom");
|
||||
attach(path.save = "", "path.save");
|
||||
attach(path.state = "", "path.state");
|
||||
attach(path.patch = "", "path.patch");
|
||||
attach(path.cheat = "", "path.cheat");
|
||||
attach(path.data = "", "path.data");
|
||||
|
@ -166,7 +167,9 @@ Configuration::Configuration() {
|
|||
attach(input.uiGeneral.resetSystem = "none", "input.uiGeneral.resetSystem");
|
||||
attach(input.uiGeneral.powerCycleSystem = "none", "input.uiGeneral.powerCycleSystem");
|
||||
|
||||
attach(input.uiGeneral.saveScreenshot = "none", "input.uiGeneral.saveScreenshot");
|
||||
attach(input.uiGeneral.showStateManager = "keyboard00.f3", "input.uiGeneral.showStateManager");
|
||||
|
||||
attach(input.uiGeneral.quickLoad1 = "keyboard00.f4", "input.uiGeneral.quickLoad1");
|
||||
attach(input.uiGeneral.quickLoad2 = "none", "input.uiGeneral.quickLoad2");
|
||||
attach(input.uiGeneral.quickLoad3 = "none", "input.uiGeneral.quickLoad3");
|
||||
|
|
|
@ -17,7 +17,7 @@ public:
|
|||
string base; //binary path
|
||||
string user; //user profile path (bsnes.cfg, ...)
|
||||
string current; //current working directory (path to currently loaded cartridge)
|
||||
string rom, save, patch, cheat, data;
|
||||
string rom, save, state, patch, cheat, data;
|
||||
string bsx, st, sgb;
|
||||
} path;
|
||||
|
||||
|
@ -70,6 +70,7 @@ public:
|
|||
string pauseEmulation;
|
||||
string resetSystem;
|
||||
string powerCycleSystem;
|
||||
string saveScreenshot;
|
||||
string showStateManager;
|
||||
string quickLoad1;
|
||||
string quickLoad2;
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
BreakpointItem::BreakpointItem(unsigned id_) : id(id_) {
|
||||
layout = new QHBoxLayout;
|
||||
layout->setMargin(0);
|
||||
layout->setSpacing(Style::WidgetSpacing);
|
||||
setLayout(layout);
|
||||
|
||||
enabled = new QCheckBox;
|
||||
layout->addWidget(enabled);
|
||||
|
||||
addr = new QLineEdit;
|
||||
addr->setMinimumWidth(80);
|
||||
layout->addWidget(addr);
|
||||
|
||||
data = new QLineEdit;
|
||||
data->setMinimumWidth(40);
|
||||
layout->addWidget(data);
|
||||
|
||||
mode = new QComboBox;
|
||||
mode->addItem("Exec");
|
||||
mode->addItem("Read");
|
||||
mode->addItem("Write");
|
||||
layout->addWidget(mode);
|
||||
|
||||
source = new QComboBox;
|
||||
source->addItem("S-CPU bus");
|
||||
source->addItem("S-SMP bus");
|
||||
source->addItem("S-PPU VRAM");
|
||||
source->addItem("S-PPU OAM");
|
||||
source->addItem("S-PPU CGRAM");
|
||||
layout->addWidget(source);
|
||||
|
||||
connect(enabled, SIGNAL(stateChanged(int)), this, SLOT(toggle()));
|
||||
}
|
||||
|
||||
void BreakpointItem::toggle() {
|
||||
bool state = enabled->isChecked();
|
||||
|
||||
if(state) {
|
||||
SNES::debugger.breakpoint[id].enabled = true;
|
||||
SNES::debugger.breakpoint[id].addr = strhex(addr->text().toUtf8().data()) & 0xffffff;
|
||||
SNES::debugger.breakpoint[id].data = strhex(data->text().toUtf8().data()) & 0xff;
|
||||
if(data->text().length() == 0) SNES::debugger.breakpoint[id].data = -1;
|
||||
SNES::debugger.breakpoint[id].mode = (SNES::Debugger::Breakpoint::Mode)mode->currentIndex();
|
||||
SNES::debugger.breakpoint[id].source = (SNES::Debugger::Breakpoint::Source)source->currentIndex();
|
||||
SNES::debugger.breakpoint[id].counter = 0;
|
||||
|
||||
addr->setEnabled(false);
|
||||
data->setEnabled(false);
|
||||
mode->setEnabled(false);
|
||||
source->setEnabled(false);
|
||||
} else {
|
||||
SNES::debugger.breakpoint[id].enabled = false;
|
||||
|
||||
addr->setEnabled(true);
|
||||
data->setEnabled(true);
|
||||
mode->setEnabled(true);
|
||||
source->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void BreakpointEditor::setup() {
|
||||
window = new QWidget;
|
||||
window->setObjectName("breakpoint-editor");
|
||||
window->setWindowTitle("Breakpoint Editor");
|
||||
|
||||
layout = new QVBoxLayout;
|
||||
layout->setMargin(Style::WindowMargin);
|
||||
layout->setSpacing(Style::WidgetSpacing);
|
||||
window->setLayout(layout);
|
||||
|
||||
for(unsigned n = 0; n < SNES::Debugger::Breakpoints; n++) {
|
||||
breakpoint[n] = new BreakpointItem(n);
|
||||
layout->addWidget(breakpoint[n]);
|
||||
}
|
||||
|
||||
window->resize(0, 0);
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
class BreakpointItem : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QHBoxLayout *layout;
|
||||
QCheckBox *enabled;
|
||||
QLineEdit *addr;
|
||||
QLineEdit *data;
|
||||
QComboBox *mode;
|
||||
QComboBox *source;
|
||||
BreakpointItem(unsigned id);
|
||||
|
||||
public slots:
|
||||
void toggle();
|
||||
|
||||
private:
|
||||
const unsigned id;
|
||||
};
|
||||
|
||||
class BreakpointEditor : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QWidget *window;
|
||||
QVBoxLayout *layout;
|
||||
BreakpointItem *breakpoint[SNES::Debugger::Breakpoints];
|
||||
|
||||
void setup();
|
||||
|
||||
public slots:
|
||||
} *breakpointEditor;
|
|
@ -0,0 +1,180 @@
|
|||
#include "breakpoint.cpp"
|
||||
#include "memory.cpp"
|
||||
|
||||
void Debugger::setup() {
|
||||
window = new QWidget;
|
||||
window->setObjectName("debugger");
|
||||
window->setWindowTitle("Debugger");
|
||||
|
||||
layout = new QVBoxLayout;
|
||||
layout->setMargin(Style::WindowMargin);
|
||||
layout->setSpacing(Style::WidgetSpacing);
|
||||
window->setLayout(layout);
|
||||
|
||||
menu = new QMenuBar;
|
||||
layout->setMenuBar(menu);
|
||||
|
||||
tools = menu->addMenu("Tools");
|
||||
tools_breakpoint = tools->addAction("Breakpoint Editor");
|
||||
tools_memory = tools->addAction("Memory Editor");
|
||||
|
||||
options = menu->addMenu("Options");
|
||||
options->setEnabled(false);
|
||||
|
||||
console = new QTextEdit;
|
||||
console->setFont(QFont(Style::Monospace));
|
||||
console->setReadOnly(true);
|
||||
layout->addWidget(console);
|
||||
|
||||
command = new QLineEdit;
|
||||
command->setFont(QFont(Style::Monospace));
|
||||
layout->addWidget(command);
|
||||
|
||||
window->setMinimumSize(725, 425);
|
||||
window->resize(725, 425);
|
||||
connect(command, SIGNAL(returnPressed()), this, SLOT(execCommand()));
|
||||
connect(tools_breakpoint, SIGNAL(triggered()), this, SLOT(showBreakpointEditor()));
|
||||
connect(tools_memory, SIGNAL(triggered()), this, SLOT(showMemoryEditor()));
|
||||
|
||||
breakpointEditor = new BreakpointEditor;
|
||||
breakpointEditor->setup();
|
||||
|
||||
memoryEditor = new MemoryEditor;
|
||||
memoryEditor->setup();
|
||||
|
||||
syncUi();
|
||||
}
|
||||
|
||||
void Debugger::syncUi() {
|
||||
memoryEditor->syncUi();
|
||||
}
|
||||
|
||||
void Debugger::show() {
|
||||
utility.showCentered(window);
|
||||
command->setFocus();
|
||||
}
|
||||
|
||||
void Debugger::echo(const char *message) {
|
||||
console->moveCursor(QTextCursor::End);
|
||||
console->insertPlainText(message);
|
||||
}
|
||||
|
||||
void Debugger::clear() {
|
||||
console->setHtml("");
|
||||
}
|
||||
|
||||
void Debugger::execCommand() {
|
||||
string s = command->text().toUtf8().data();
|
||||
command->setText("");
|
||||
|
||||
if(s == "clear") {
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "run") {
|
||||
SNES::debugger.break_event = SNES::Debugger::None;
|
||||
SNES::debugger.step_cpu = false;
|
||||
SNES::debugger.step_smp = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "step cpu") {
|
||||
SNES::debugger.step_cpu = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "step smp") {
|
||||
SNES::debugger.step_smp = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "trace on") {
|
||||
SNES::debugger.tracefile.open("trace.log", file::mode_write);
|
||||
echo("Tracing enabled.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "trace off") {
|
||||
SNES::debugger.trace_cpu = false;
|
||||
SNES::debugger.trace_smp = false;
|
||||
SNES::debugger.tracefile.close();
|
||||
echo("Tracing disabled.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "trace cpu on") {
|
||||
if(SNES::debugger.tracefile.open() == false) return;
|
||||
SNES::debugger.trace_cpu = true;
|
||||
echo("S-CPU tracing enabled.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "trace cpu off") {
|
||||
SNES::debugger.trace_cpu = false;
|
||||
echo("S-CPU tracing disabled.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "trace smp on") {
|
||||
if(SNES::debugger.tracefile.open() == false) return;
|
||||
SNES::debugger.trace_smp = true;
|
||||
echo("S-SMP tracing enabled.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "trace smp off") {
|
||||
SNES::debugger.trace_smp = false;
|
||||
echo("S-SMP tracing disabled.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(SNES::cartridge.loaded() == false) return;
|
||||
|
||||
if(s == "") {
|
||||
SNES::debugger.break_event = SNES::Debugger::None;
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::showBreakpointEditor() {
|
||||
utility.showCentered(breakpointEditor->window);
|
||||
}
|
||||
|
||||
void Debugger::showMemoryEditor() {
|
||||
utility.showCentered(memoryEditor->window);
|
||||
}
|
||||
|
||||
void Debugger::event() {
|
||||
char t[256];
|
||||
|
||||
switch(SNES::debugger.break_event) {
|
||||
case SNES::Debugger::BreakpointHit: {
|
||||
unsigned n = SNES::debugger.breakpoint_hit;
|
||||
echo(utf8() << "Breakpoint " << n << " hit (" << SNES::debugger.breakpoint[n].counter << ").\n");
|
||||
|
||||
if(SNES::debugger.breakpoint[n].mode == SNES::Debugger::Breakpoint::Exec) {
|
||||
if(SNES::debugger.breakpoint[n].source == SNES::Debugger::Breakpoint::CPUBus) {
|
||||
SNES::debugger.step_cpu = true;
|
||||
SNES::cpu.disassemble_opcode(t);
|
||||
echo(utf8() << t << "\n");
|
||||
}
|
||||
|
||||
if(SNES::debugger.breakpoint[n].source == SNES::Debugger::Breakpoint::APURAM) {
|
||||
SNES::debugger.step_smp = true;
|
||||
SNES::smp.disassemble_opcode(t);
|
||||
echo(utf8() << t << "\n");
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case SNES::Debugger::CPUStep: {
|
||||
SNES::cpu.disassemble_opcode(t);
|
||||
echo(utf8() << t << "\n");
|
||||
} break;
|
||||
|
||||
case SNES::Debugger::SMPStep: {
|
||||
SNES::smp.disassemble_opcode(t);
|
||||
echo(utf8() << t << "\n");
|
||||
} break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
#include "breakpoint.moc"
|
||||
#include "memory.moc"
|
||||
|
||||
class Debugger : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QWidget *window;
|
||||
QMenuBar *menu;
|
||||
QMenu *tools;
|
||||
QAction *tools_breakpoint;
|
||||
QAction *tools_memory;
|
||||
QMenu *options;
|
||||
QVBoxLayout *layout;
|
||||
QTextEdit *console;
|
||||
QLineEdit *command;
|
||||
|
||||
void setup();
|
||||
void syncUi();
|
||||
void show();
|
||||
void clear();
|
||||
void echo(const char *message);
|
||||
void event();
|
||||
|
||||
public slots:
|
||||
void execCommand();
|
||||
void showBreakpointEditor();
|
||||
void showMemoryEditor();
|
||||
} *debugger;
|
|
@ -0,0 +1,75 @@
|
|||
void MemoryEditor::setup() {
|
||||
window = new QWidget;
|
||||
window->setObjectName("memory-editor");
|
||||
window->setWindowTitle("Memory Editor");
|
||||
|
||||
layout = new QVBoxLayout;
|
||||
layout->setMargin(Style::WindowMargin);
|
||||
layout->setSpacing(Style::WidgetSpacing);
|
||||
window->setLayout(layout);
|
||||
|
||||
controls = new QHBoxLayout;
|
||||
controls->setSpacing(Style::WidgetSpacing);
|
||||
layout->addLayout(controls);
|
||||
|
||||
spacer = new QWidget;
|
||||
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
controls->addWidget(spacer);
|
||||
|
||||
addr = new QLineEdit;
|
||||
addr->setFixedWidth(80);
|
||||
controls->addWidget(addr);
|
||||
|
||||
source = new QComboBox;
|
||||
source->addItem("S-CPU bus");
|
||||
source->addItem("S-APU bus");
|
||||
source->addItem("S-PPU VRAM");
|
||||
source->addItem("S-PPU OAM");
|
||||
source->addItem("S-PPU CGRAM");
|
||||
controls->addWidget(source);
|
||||
|
||||
refresh = new QPushButton("Refresh");
|
||||
controls->addWidget(refresh);
|
||||
|
||||
autoUpdate = new QCheckBox("Auto update");
|
||||
autoUpdate->setEnabled(false);
|
||||
controls->addWidget(autoUpdate);
|
||||
|
||||
editor = new QTextEdit;
|
||||
editor->setFont(QFont(Style::Monospace));
|
||||
layout->addWidget(editor);
|
||||
|
||||
window->setMinimumSize(425, 325);
|
||||
window->resize(0, 0);
|
||||
connect(refresh, SIGNAL(released()), this, SLOT(refreshView()));
|
||||
}
|
||||
|
||||
void MemoryEditor::syncUi() {
|
||||
if(SNES::cartridge.loaded() == false) {
|
||||
editor->setPlainText("");
|
||||
refresh->setEnabled(false);
|
||||
} else {
|
||||
refresh->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryEditor::refreshView() {
|
||||
string s;
|
||||
unsigned offset = strhex(addr->text().toUtf8().data());
|
||||
unsigned mode = source->currentIndex();
|
||||
|
||||
for(unsigned y = 0; y < 16; y++) {
|
||||
char t[16];
|
||||
sprintf(t, "%.6x ", offset & 0xffffff);
|
||||
s << t;
|
||||
for(unsigned x = 0; x < 16; x++) {
|
||||
uint8 data = SNES::debugger.read((SNES::Debugger::MemorySource)mode, offset++);
|
||||
sprintf(t, "%.2x", data);
|
||||
s << t;
|
||||
if(x != 15) s << " ";
|
||||
}
|
||||
if(y != 15) s << "\n";
|
||||
}
|
||||
|
||||
editor->setPlainText(utf8() << s);
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
class MemoryEditor : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QWidget *window;
|
||||
QVBoxLayout *layout;
|
||||
QHBoxLayout *controls;
|
||||
QWidget *spacer;
|
||||
QLineEdit *addr;
|
||||
QComboBox *source;
|
||||
QPushButton *refresh;
|
||||
QCheckBox *autoUpdate;
|
||||
QTextEdit *editor;
|
||||
|
||||
void setup();
|
||||
void syncUi();
|
||||
|
||||
public slots:
|
||||
void refreshView();
|
||||
} *memoryEditor;
|
|
@ -32,6 +32,7 @@ pauseEmulation(InputObject::Button, "Pause emulation", config.input.uiGeneral.pa
|
|||
resetSystem(InputObject::Button, "Reset system", config.input.uiGeneral.resetSystem),
|
||||
powerCycleSystem(InputObject::Button, "Power cycle system", config.input.uiGeneral.powerCycleSystem),
|
||||
|
||||
saveScreenshot(InputObject::Button, "Save screenshot", config.input.uiGeneral.saveScreenshot),
|
||||
showStateManager(InputObject::Button, "Show state manager window", config.input.uiGeneral.showStateManager),
|
||||
quickLoad1(InputObject::Button, "Load from temporary state 1", config.input.uiGeneral.quickLoad1),
|
||||
quickLoad2(InputObject::Button, "Load from temporary state 2", config.input.uiGeneral.quickLoad2),
|
||||
|
@ -52,6 +53,7 @@ exitEmulator(InputObject::Button, "Exit emulator", config.input.uiGeneral.exitEm
|
|||
attach(resetSystem);
|
||||
attach(powerCycleSystem);
|
||||
|
||||
attach(saveScreenshot);
|
||||
attach(showStateManager);
|
||||
attach(quickLoad1);
|
||||
attach(quickLoad2);
|
||||
|
|
|
@ -3,6 +3,7 @@ struct InputUiGeneral : public InputGroup {
|
|||
InputObject pauseEmulation;
|
||||
InputObject resetSystem;
|
||||
InputObject powerCycleSystem;
|
||||
InputObject saveScreenshot;
|
||||
InputObject showStateManager;
|
||||
InputObject quickLoad1;
|
||||
InputObject quickLoad2;
|
||||
|
|
|
@ -7,6 +7,7 @@ void Interface::video_refresh(uint16_t *data, unsigned pitch, unsigned *line, un
|
|||
libfilter::filter.render(output, outpitch, data, pitch, line, width, height);
|
||||
video.unlock();
|
||||
video.refresh();
|
||||
if(saveScreenshot == true) captureScreenshot(output, outpitch, outwidth, outheight);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,3 +23,27 @@ void Interface::input_poll() {
|
|||
int16_t Interface::input_poll(unsigned deviceid, unsigned id) {
|
||||
return inputManager.getStatus(deviceid, id);
|
||||
}
|
||||
|
||||
void Interface::captureScreenshot(uint32_t *data, unsigned pitch, unsigned width, unsigned height) {
|
||||
saveScreenshot = false;
|
||||
QImage image((const unsigned char*)data, width, height, pitch, QImage::Format_RGB32);
|
||||
|
||||
string filename = "screenshot-";
|
||||
time_t systemTime = time(0);
|
||||
tm *currentTime = localtime(&systemTime);
|
||||
char t[512];
|
||||
sprintf(t, "%.4u%.2u%.2u-%.2u%.2u%.2u",
|
||||
1900 + currentTime->tm_year, 1 + currentTime->tm_mon, currentTime->tm_mday,
|
||||
currentTime->tm_hour, currentTime->tm_min, currentTime->tm_sec
|
||||
);
|
||||
filename << t << ".png";
|
||||
|
||||
string path = config.path.data;
|
||||
if(path == "") path = config.path.current;
|
||||
image.save(utf8() << path << filename);
|
||||
utility.showMessage("Screenshot saved.");
|
||||
}
|
||||
|
||||
Interface::Interface() {
|
||||
saveScreenshot = false;
|
||||
}
|
||||
|
|
|
@ -4,4 +4,8 @@ public:
|
|||
void audio_sample(uint16_t left, uint16_t right);
|
||||
void input_poll();
|
||||
int16_t input_poll(unsigned deviceid, unsigned id);
|
||||
|
||||
Interface();
|
||||
void captureScreenshot(uint32_t*, unsigned, unsigned, unsigned);
|
||||
bool saveScreenshot;
|
||||
} interface;
|
||||
|
|
|
@ -41,6 +41,7 @@ const char defaultStylesheet[] =
|
|||
"\n";
|
||||
|
||||
#include "application/application.cpp"
|
||||
#include "debugger/debugger.cpp"
|
||||
#include "input/input.cpp"
|
||||
#include "utility/utility.cpp"
|
||||
|
||||
|
|
|
@ -36,13 +36,24 @@ using namespace ruby;
|
|||
#include "config.hpp"
|
||||
#include "interface.hpp"
|
||||
#include "application/application.moc"
|
||||
#include "debugger/debugger.moc"
|
||||
#include "input/input.hpp"
|
||||
#include "utility/utility.hpp"
|
||||
|
||||
struct Style {
|
||||
static const char Monospace[];
|
||||
|
||||
enum {
|
||||
WindowMargin = 5,
|
||||
WidgetSpacing = 5,
|
||||
SeparatorSpacing = 5,
|
||||
};
|
||||
};
|
||||
|
||||
#if defined(PLATFORM_X)
|
||||
const char Style::Monospace[] = "Monospace";
|
||||
#elif defined(PLATFORM_WIN)
|
||||
const char Style::Monospace[] = "Lucida Console";
|
||||
#else
|
||||
const char Style::Monospace[] = "Courier New";
|
||||
#endif
|
||||
|
|
|
@ -108,6 +108,7 @@ void AudioSettingsWindow::frequencyChange(int value) {
|
|||
case 3: config.audio.outputFrequency = 96000; break;
|
||||
}
|
||||
audio.set(Audio::Frequency, config.audio.outputFrequency);
|
||||
utility.updateEmulationSpeed();
|
||||
}
|
||||
|
||||
void AudioSettingsWindow::latencyChange(int value) {
|
||||
|
|
|
@ -49,6 +49,26 @@ void PathSettingsWindow::setup() {
|
|||
layout->addLayout(saves);
|
||||
layout->addSpacing(Style::WidgetSpacing);
|
||||
|
||||
stateLabel = new QLabel("Save states:");
|
||||
layout->addWidget(stateLabel);
|
||||
|
||||
states = new QHBoxLayout; {
|
||||
states->setMargin(0);
|
||||
|
||||
statePath = new QLineEdit;
|
||||
statePath->setReadOnly(true);
|
||||
states->addWidget(statePath);
|
||||
|
||||
stateSelect = new QPushButton("Select ...");
|
||||
states->addWidget(stateSelect);
|
||||
|
||||
stateDefault = new QPushButton("Default");
|
||||
states->addWidget(stateDefault);
|
||||
}
|
||||
states->setSpacing(Style::WidgetSpacing);
|
||||
layout->addLayout(states);
|
||||
layout->addSpacing(Style::WidgetSpacing);
|
||||
|
||||
patchLabel = new QLabel("UPS patches:");
|
||||
layout->addWidget(patchLabel);
|
||||
|
||||
|
@ -89,23 +109,24 @@ void PathSettingsWindow::setup() {
|
|||
layout->addLayout(cheats);
|
||||
layout->addSpacing(Style::WidgetSpacing);
|
||||
|
||||
dataLabel = new QLabel("Export data:");
|
||||
dataLabel = new QLabel("Exported data:");
|
||||
layout->addWidget(dataLabel);
|
||||
|
||||
data = new QHBoxLayout;
|
||||
data->setMargin(0);
|
||||
data->setSpacing(Style::WidgetSpacing); {
|
||||
datum = new QHBoxLayout; {
|
||||
datum->setMargin(0);
|
||||
datum->setSpacing(Style::WidgetSpacing);
|
||||
|
||||
dataPath = new QLineEdit;
|
||||
dataPath->setReadOnly(true);
|
||||
data->addWidget(dataPath);
|
||||
datum->addWidget(dataPath);
|
||||
|
||||
dataSelect = new QPushButton("Select ...");
|
||||
data->addWidget(dataSelect);
|
||||
datum->addWidget(dataSelect);
|
||||
|
||||
dataDefault = new QPushButton("Default");
|
||||
data->addWidget(dataDefault);
|
||||
datum->addWidget(dataDefault);
|
||||
}
|
||||
layout->addLayout(data);
|
||||
layout->addLayout(datum);
|
||||
|
||||
spacer = new QWidget;
|
||||
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
|
@ -118,6 +139,8 @@ void PathSettingsWindow::setup() {
|
|||
connect(gameDefault, SIGNAL(released()), this, SLOT(defaultGamePath()));
|
||||
connect(saveSelect, SIGNAL(released()), this, SLOT(selectSavePath()));
|
||||
connect(saveDefault, SIGNAL(released()), this, SLOT(defaultSavePath()));
|
||||
connect(stateSelect, SIGNAL(released()), this, SLOT(selectStatePath()));
|
||||
connect(stateDefault, SIGNAL(released()), this, SLOT(defaultStatePath()));
|
||||
connect(patchSelect, SIGNAL(released()), this, SLOT(selectPatchPath()));
|
||||
connect(patchDefault, SIGNAL(released()), this, SLOT(defaultPatchPath()));
|
||||
connect(cheatSelect, SIGNAL(released()), this, SLOT(selectCheatPath()));
|
||||
|
@ -127,11 +150,22 @@ void PathSettingsWindow::setup() {
|
|||
}
|
||||
|
||||
void PathSettingsWindow::syncUi() {
|
||||
gamePath->setText (config.path.rom == "" ? "<startup path>" : (const char*)config.path.rom);
|
||||
savePath->setText (config.path.save == "" ? "<same folder as loaded game>" : (const char*)config.path.save);
|
||||
patchPath->setText(config.path.patch == "" ? "<same folder as loaded game>" : (const char*)config.path.patch);
|
||||
cheatPath->setText(config.path.cheat == "" ? "<same folder as loaded game>" : (const char*)config.path.cheat);
|
||||
dataPath->setText (config.path.data == "" ? "<same folder as loaded game>" : (const char*)config.path.data);
|
||||
syncPath(gamePath, config.path.rom, "Startup path");
|
||||
syncPath(savePath, config.path.save, "Same folder as loaded game");
|
||||
syncPath(statePath, config.path.state, "Same folder as loaded game");
|
||||
syncPath(patchPath, config.path.patch, "Same folder as loaded game");
|
||||
syncPath(cheatPath, config.path.cheat, "Same folder as loaded game");
|
||||
syncPath(dataPath, config.path.data, "Same folder as loaded game");
|
||||
}
|
||||
|
||||
void PathSettingsWindow::syncPath(QLineEdit *control, const string &path, const char *caption) {
|
||||
if(path == "") {
|
||||
control->setStyleSheet("color: #808080");
|
||||
control->setText(caption);
|
||||
} else {
|
||||
control->setStyleSheet("color: #000000");
|
||||
control->setText((const char*)path);
|
||||
}
|
||||
}
|
||||
|
||||
void PathSettingsWindow::selectGamePath() {
|
||||
|
@ -160,6 +194,19 @@ void PathSettingsWindow::defaultSavePath() {
|
|||
syncUi();
|
||||
}
|
||||
|
||||
void PathSettingsWindow::selectStatePath() {
|
||||
string path = utility.selectFolder("Default Save State Path");
|
||||
if(path.length() > 0) {
|
||||
config.path.state = path;
|
||||
syncUi();
|
||||
}
|
||||
}
|
||||
|
||||
void PathSettingsWindow::defaultStatePath() {
|
||||
config.path.state = "";
|
||||
syncUi();
|
||||
}
|
||||
|
||||
void PathSettingsWindow::selectPatchPath() {
|
||||
string path = utility.selectFolder("Default UPS Patch Path");
|
||||
if(path.length() > 0) {
|
||||
|
@ -187,7 +234,7 @@ void PathSettingsWindow::defaultCheatPath() {
|
|||
}
|
||||
|
||||
void PathSettingsWindow::selectDataPath() {
|
||||
string path = utility.selectFolder("Default Export Data Path");
|
||||
string path = utility.selectFolder("Default Exported Data Path");
|
||||
if(path.length() > 0) {
|
||||
config.path.data = path;
|
||||
syncUi();
|
||||
|
|
|
@ -15,6 +15,11 @@ public:
|
|||
QLineEdit *savePath;
|
||||
QPushButton *saveSelect;
|
||||
QPushButton *saveDefault;
|
||||
QLabel *stateLabel;
|
||||
QHBoxLayout *states;
|
||||
QLineEdit *statePath;
|
||||
QPushButton *stateSelect;
|
||||
QPushButton *stateDefault;
|
||||
QLabel *patchLabel;
|
||||
QHBoxLayout *patches;
|
||||
QLineEdit *patchPath;
|
||||
|
@ -26,7 +31,7 @@ public:
|
|||
QPushButton *cheatSelect;
|
||||
QPushButton *cheatDefault;
|
||||
QLabel *dataLabel;
|
||||
QHBoxLayout *data;
|
||||
QHBoxLayout *datum;
|
||||
QLineEdit *dataPath;
|
||||
QPushButton *dataSelect;
|
||||
QPushButton *dataDefault;
|
||||
|
@ -34,12 +39,15 @@ public:
|
|||
|
||||
void setup();
|
||||
void syncUi();
|
||||
void syncPath(QLineEdit *control, const string &path, const char *caption);
|
||||
|
||||
public slots:
|
||||
void selectGamePath();
|
||||
void defaultGamePath();
|
||||
void selectSavePath();
|
||||
void defaultSavePath();
|
||||
void selectStatePath();
|
||||
void defaultStatePath();
|
||||
void selectPatchPath();
|
||||
void defaultPatchPath();
|
||||
void selectCheatPath();
|
||||
|
|
|
@ -17,7 +17,6 @@ void SettingsWindow::setup() {
|
|||
list->addItem(paths = new QListWidgetItem("Paths"));
|
||||
list->addItem(advanced = new QListWidgetItem("Advanced"));
|
||||
list->setCurrentItem(input); //select most frequently used panel by default
|
||||
list->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
|
||||
list->setFixedWidth(135);
|
||||
|
||||
panel = new QWidget;
|
||||
|
|
|
@ -11,10 +11,10 @@ void CheatEditorWindow::setup() {
|
|||
layout->addWidget(title);
|
||||
|
||||
list = new QTreeWidget;
|
||||
list->setColumnCount(4);
|
||||
list->setHeaderLabels(QStringList() << "" << "Slot" << "Code" << "Description");
|
||||
list->setColumnCount(3);
|
||||
list->setHeaderLabels(QStringList() << "Slot" << "Code" << "Description");
|
||||
list->setAllColumnsShowFocus(true);
|
||||
list->sortByColumn(1, Qt::AscendingOrder);
|
||||
list->sortByColumn(0, Qt::AscendingOrder);
|
||||
list->setRootIsDecorated(false);
|
||||
list->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
layout->addWidget(list);
|
||||
|
@ -43,10 +43,10 @@ void CheatEditorWindow::setup() {
|
|||
buttonLayout->setSpacing(Style::WidgetSpacing);
|
||||
layout->addLayout(buttonLayout);
|
||||
|
||||
addCode = new QPushButton("Add");
|
||||
addCode = new QPushButton("Add Slot");
|
||||
buttonLayout->addWidget(addCode);
|
||||
|
||||
deleteCode = new QPushButton("Delete");
|
||||
deleteCode = new QPushButton("Delete Slot");
|
||||
buttonLayout->addWidget(deleteCode);
|
||||
|
||||
reloadList();
|
||||
|
@ -98,33 +98,27 @@ void CheatEditorWindow::updateItem(QTreeWidgetItem *item) {
|
|||
SNES::Cheat::cheat_t code, temp;
|
||||
SNES::cheat.get(n, code);
|
||||
|
||||
QFont font = item->font(2);
|
||||
if(SNES::cheat.decode(code.code, temp) == false) {
|
||||
font.setBold(true);
|
||||
font.setItalic(false);
|
||||
item->setForeground(2, QBrush(QColor(224, 0, 0)));
|
||||
item->setForeground(1, QBrush(QColor(224, 0, 0)));
|
||||
} else {
|
||||
font.setBold(false);
|
||||
lstring part;
|
||||
part.split("+", code.code);
|
||||
if(part.size() > 1) {
|
||||
font.setItalic(true);
|
||||
item->setForeground(2, QBrush(QColor(0, 128, 0)));
|
||||
item->setForeground(1, QBrush(QColor(0, 128, 0)));
|
||||
} else {
|
||||
font.setItalic(false);
|
||||
item->setForeground(2, QBrush(QColor(0, 0, 0)));
|
||||
item->setForeground(1, QBrush(QColor(0, 0, 128)));
|
||||
}
|
||||
}
|
||||
item->setFont(2, font);
|
||||
|
||||
string scode = code.code;
|
||||
scode.replace(" ", "");
|
||||
lstring lcode;
|
||||
lcode.split("+", scode);
|
||||
if(lcode.size() > 1) lcode[0] << "+" << lcode.size() - 1;
|
||||
|
||||
item->setCheckState(0, code.enabled ? Qt::Checked : Qt::Unchecked);
|
||||
item->setText(2, utf8() << lcode[0]);
|
||||
item->setText(3, utf8() << code.desc);
|
||||
item->setCheckState(1, code.enabled ? Qt::Checked : Qt::Unchecked);
|
||||
item->setText(1, utf8() << lcode[0]);
|
||||
item->setText(2, utf8() << code.desc);
|
||||
}
|
||||
|
||||
void CheatEditorWindow::popupMenu(const QPoint &point) {
|
||||
|
@ -144,7 +138,9 @@ void CheatEditorWindow::reloadList() {
|
|||
for(unsigned n = 0; n < SNES::cheat.count(); n++) {
|
||||
QTreeWidgetItem *item = new QTreeWidgetItem(list);
|
||||
item->setData(0, Qt::UserRole, QVariant(n));
|
||||
item->setText(1, utf8() << n + 1);
|
||||
char slot[16];
|
||||
sprintf(slot, "%3u", n + 1);
|
||||
item->setText(0, utf8() << slot);
|
||||
updateItem(item);
|
||||
}
|
||||
}
|
||||
|
@ -194,7 +190,7 @@ void CheatEditorWindow::updateCodeStatus() {
|
|||
if(items.count() > 0) {
|
||||
QTreeWidgetItem *item = items[0];
|
||||
unsigned n = item->data(0, Qt::UserRole).toUInt();
|
||||
item->checkState(0) == Qt::Checked ? SNES::cheat.enable(n) : SNES::cheat.disable(n);
|
||||
item->checkState(1) == Qt::Checked ? SNES::cheat.enable(n) : SNES::cheat.disable(n);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -205,7 +201,7 @@ void CheatEditorWindow::toggleCodeStatus() {
|
|||
QTreeWidgetItem *item = items[0];
|
||||
unsigned n = item->data(0, Qt::UserRole).toUInt();
|
||||
SNES::cheat.enabled(n) == false ? SNES::cheat.enable(n) : SNES::cheat.disable(n);
|
||||
item->setCheckState(0, SNES::cheat.enabled(n) ? Qt::Checked : Qt::Unchecked);
|
||||
item->setCheckState(1, SNES::cheat.enabled(n) ? Qt::Checked : Qt::Unchecked);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ void ToolsWindow::setup() {
|
|||
list->addItem(cheatEditor = new QListWidgetItem("Cheat Editor"));
|
||||
list->addItem(stateManager = new QListWidgetItem("State Manager"));
|
||||
list->setCurrentItem(cheatEditor);
|
||||
list->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
|
||||
list->setFixedWidth(135);
|
||||
|
||||
panel = new QWidget;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
string Utility::selectCartridge() {
|
||||
audio.clear();
|
||||
application.timer->stop();
|
||||
QString filename = QFileDialog::getOpenFileName(0,
|
||||
"Load Cartridge",
|
||||
utf8() << (config.path.rom != "" ? config.path.rom : config.path.current),
|
||||
|
@ -13,6 +14,7 @@ string Utility::selectCartridge() {
|
|||
");;"
|
||||
"All files (*)"
|
||||
);
|
||||
application.timer->start(0);
|
||||
|
||||
string name = filename.toUtf8().constData();
|
||||
if(strlen(name) > 0) config.path.current = basepath(name);
|
||||
|
@ -21,12 +23,15 @@ string Utility::selectCartridge() {
|
|||
|
||||
string Utility::selectFolder(const char *title) {
|
||||
audio.clear();
|
||||
application.timer->stop();
|
||||
QString pathname = QFileDialog::getExistingDirectory(0,
|
||||
title, utf8() << config.path.current,
|
||||
QFileDialog::ShowDirsOnly);
|
||||
application.timer->start(0);
|
||||
|
||||
string path = pathname.toUtf8().constData();
|
||||
strtr(path, "\\", "/");
|
||||
if(strend(path, "/") == false) path << "/";
|
||||
if(path.length() > 0 && strend(path, "/") == false) path << "/";
|
||||
return path;
|
||||
}
|
||||
|
||||
|
@ -188,6 +193,7 @@ void Utility::modifySystemState(system_state_t state) {
|
|||
<< "Loaded " << cartridge.name
|
||||
<< (cartridge.patchApplied ? ", and applied UPS patch." : "."));
|
||||
mainWindow->window->setWindowTitle(utf8() << bsnesTitle << " v" << bsnesVersion << " - " << cartridge.name);
|
||||
debugger->echo(utf8() << "Loaded " << cartridge.name << ".\n");
|
||||
} break;
|
||||
|
||||
case UnloadCartridge: {
|
||||
|
@ -243,10 +249,10 @@ void Utility::modifySystemState(system_state_t state) {
|
|||
}
|
||||
|
||||
mainWindow->syncUi();
|
||||
debugger->syncUi();
|
||||
cheatEditorWindow->reloadList();
|
||||
stateManagerWindow->reloadList();
|
||||
}
|
||||
//
|
||||
|
||||
bool Utility::loadCartridge(const char *filename, SNES::MappedRAM &memory) {
|
||||
if(file::exists(filename) == false) return false;
|
||||
|
@ -330,7 +336,8 @@ bool Utility::loadCartridge(const char *filename, SNES::MappedRAM &memory) {
|
|||
}
|
||||
}
|
||||
|
||||
memory.map(data, size);
|
||||
memory.copy(data, size);
|
||||
delete[] data;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -349,7 +356,7 @@ bool Utility::loadMemory(const char *filename, const char *extension, SNES::Mapp
|
|||
fp.read(data, size);
|
||||
fp.close();
|
||||
|
||||
memcpy(memory.data(), data, min(size, memory.size()));
|
||||
memory.copy(data, size);
|
||||
delete[] data;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ void Utility::quickLoad(uint8 slot) {
|
|||
}
|
||||
|
||||
string name;
|
||||
name << filepath(basename(cartridge.baseName), config.path.save);
|
||||
name << filepath(basename(cartridge.baseName), config.path.state);
|
||||
name << "-" << (unsigned)(slot + 1) << ".bst";
|
||||
|
||||
file fp;
|
||||
|
@ -61,7 +61,7 @@ void Utility::quickSave(uint8 slot) {
|
|||
SNES::system.runtosave();
|
||||
|
||||
string name;
|
||||
name << filepath(basename(cartridge.baseName), config.path.save);
|
||||
name << filepath(basename(cartridge.baseName), config.path.state);
|
||||
name << "-" << (unsigned)(slot + 1) << ".bst";
|
||||
|
||||
serializer state = SNES::system.serialize();
|
||||
|
@ -78,20 +78,20 @@ void Utility::quickSave(uint8 slot) {
|
|||
|
||||
void Utility::loadStateInfo(lstring &info) {
|
||||
info.reset();
|
||||
string name = string() << filepath(basename(cartridge.baseName), config.path.save) << ".bsa";
|
||||
string name = string() << filepath(basename(cartridge.baseName), config.path.state) << ".bsa";
|
||||
if(SNES::statemanager.load(name) == false) return;
|
||||
SNES::statemanager.list(info);
|
||||
}
|
||||
|
||||
void Utility::setStateDescription(uint8 slot, const char *description) {
|
||||
if(SNES::cartridge.loaded() == false) return;
|
||||
string name = string() << filepath(basename(cartridge.baseName), config.path.save) << ".bsa";
|
||||
string name = string() << filepath(basename(cartridge.baseName), config.path.state) << ".bsa";
|
||||
SNES::statemanager.set_description(name, slot, description);
|
||||
}
|
||||
|
||||
void Utility::loadState(uint8 slot) {
|
||||
if(SNES::cartridge.loaded() == false || application.power == false) return;
|
||||
string name = string() << filepath(basename(cartridge.baseName), config.path.save) << ".bsa";
|
||||
string name = string() << filepath(basename(cartridge.baseName), config.path.state) << ".bsa";
|
||||
|
||||
try {
|
||||
serializer state = SNES::statemanager.load(name, slot);
|
||||
|
@ -112,7 +112,7 @@ void Utility::saveState(uint8 slot, const char *description) {
|
|||
SNES::system.runtosave();
|
||||
|
||||
serializer state = SNES::system.serialize();
|
||||
string name = string() << filepath(basename(cartridge.baseName), config.path.save) << ".bsa";
|
||||
string name = string() << filepath(basename(cartridge.baseName), config.path.state) << ".bsa";
|
||||
if(SNES::statemanager.save(name, slot, state, description) == true) {
|
||||
showMessage(utf8() << "State saved.");
|
||||
} else {
|
||||
|
@ -122,7 +122,7 @@ void Utility::saveState(uint8 slot, const char *description) {
|
|||
|
||||
void Utility::deleteState(uint8 slot) {
|
||||
if(SNES::cartridge.loaded() == false) return;
|
||||
string name = string() << filepath(basename(cartridge.baseName), config.path.save) << ".bsa";
|
||||
string name = string() << filepath(basename(cartridge.baseName), config.path.state) << ".bsa";
|
||||
if(SNES::statemanager.erase(name, slot) == true) {
|
||||
showMessage(utf8() << "State deleted.");
|
||||
} else {
|
||||
|
|
|
@ -77,6 +77,11 @@ void Utility::inputEvent(uint16_t code) {
|
|||
modifySystemState(PowerCycle);
|
||||
}
|
||||
|
||||
if(isButtonDown(code, inputUiGeneral.saveScreenshot)) {
|
||||
//tell SNES::Interface to save a screenshot at the next video_refresh() event
|
||||
interface.saveScreenshot = true;
|
||||
}
|
||||
|
||||
if(isButtonDown(code, inputUiGeneral.showStateManager)) {
|
||||
toolsWindow->showStateManager();
|
||||
}
|
||||
|
@ -229,7 +234,7 @@ void Utility::updateEmulationSpeed() {
|
|||
unsigned outfreq = config.audio.outputFrequency;
|
||||
unsigned infreq = config.audio.inputFrequency * scale[config.system.speed] + 0.5;
|
||||
|
||||
audio.set(Audio::Resample, outfreq != infreq); //only resample when necessary
|
||||
audio.set(Audio::Resample, true); //always resample (required for volume adjust + frequency scaler)
|
||||
audio.set(Audio::ResampleRatio, (double)infreq / (double)outfreq);
|
||||
}
|
||||
|
||||
|
|
|
@ -139,17 +139,22 @@ void Utility::resizeMainWindow() {
|
|||
|
||||
//center canvas onscreen; ensure it is not larger than viewable area
|
||||
mainWindow->canvas->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
mainWindow->canvas->setMaximumSize(width, height);
|
||||
mainWindow->canvas->setFixedSize(width, height);
|
||||
mainWindow->canvas->setMinimumSize(0, 0);
|
||||
}
|
||||
|
||||
application.processEvents();
|
||||
usleep(2000);
|
||||
}
|
||||
|
||||
//work around for Qt/Xlib bug:
|
||||
//workaround for Qt/Xlib bug:
|
||||
//if window resize occurs with cursor over it, Qt shows Qt::Size*DiagCursor;
|
||||
//so force it to show Qt::ArrowCursor, as expected
|
||||
mainWindow->window->setCursor(Qt::ArrowCursor);
|
||||
mainWindow->canvasContainer->setCursor(Qt::ArrowCursor);
|
||||
mainWindow->canvas->setCursor(Qt::ArrowCursor);
|
||||
|
||||
//workaround for DirectSound(?) bug:
|
||||
//window resizing sometimes breaks audio sync, this call re-initializes it
|
||||
updateAvSync();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue