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:
byuu 2009-08-22 12:09:19 +00:00
parent c26f9d912a
commit 59b86cd3a8
91 changed files with 2155 additions and 1494 deletions

View File

@ -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 ###

View File

@ -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>

View File

@ -71,6 +71,7 @@ void Cartridge::load(Mode cartridge_mode) {
set(crc32, ~checksum);
bus.load_cart();
system.serialize_init();
set(loaded, true);
}

View File

@ -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);
}

View File

@ -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

View File

@ -0,0 +1,6 @@
class sCPUdebug : public sCPU {
public:
void op_step();
uint8 op_read(uint32 addr);
void op_write(uint32 addr, uint8 data);
};

View File

@ -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;

View File

@ -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)) {

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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>

View File

@ -3,6 +3,8 @@
#define ADSP_CPP
namespaec SNES {
aDSP dsp;
#include "adsp_tables.cpp"
void aDSP::enter() { loop:

View File

@ -170,3 +170,5 @@ public:
aDSP();
~aDSP();
};
extern aDSP dsp;

View File

@ -7,6 +7,8 @@
#define SDSP_CPP
namespace SNES {
sDSP dsp;
#include "serialization.cpp"
#define REG(n) state.regs[r_##n]

View File

@ -165,3 +165,5 @@ private:
void echo_29();
void echo_30();
};
extern sDSP dsp;

View File

@ -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

View File

@ -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(

View File

@ -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(

View File

@ -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(

View File

@ -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(

View File

@ -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;

View File

@ -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 }; };

View File

@ -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;
}
}

View File

@ -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_; }

View File

@ -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();

View File

@ -3,6 +3,8 @@
#define SMEMORY_CPP
namespace SNES {
sBus bus;
#include "system.cpp"
#include "generic.cpp"
#include "serialization.cpp"

View File

@ -16,3 +16,5 @@ private:
void map_generic();
void map_generic_sram();
};
extern sBus bus;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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

198
src/ppu/bppu/mmio/mmio.hpp Normal file
View File

@ -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();

View File

@ -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));

View File

@ -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

View File

@ -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

View File

@ -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));
}

View File

@ -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 {

View File

@ -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); }
}

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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++) {

View File

@ -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;

View File

@ -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

View File

@ -0,0 +1,4 @@
class sSMPdebug : public sSMP {
public:
void op_step();
};

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -6,7 +6,6 @@ public:
SlotSize = 1,
DateTimeSize = 19,
DescriptionSize = 512,
StateSize = 1024 * 1024,
HeaderSize = 8 + (256 * SlotSize) + (256 * DateTimeSize) + (256 * DescriptionSize),
SlotIndex = 8,

View File

@ -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();

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -57,6 +57,9 @@ void Application::init() {
utility.updateFullscreenState();
application.processEvents();
debugger = new Debugger;
debugger->setup();
settingsWindow = new SettingsWindow;
settingsWindow->setup();

View File

@ -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");

View File

@ -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();

View File

@ -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");

View File

@ -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;

View File

@ -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);
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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;

View File

@ -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);

View File

@ -3,6 +3,7 @@ struct InputUiGeneral : public InputGroup {
InputObject pauseEmulation;
InputObject resetSystem;
InputObject powerCycleSystem;
InputObject saveScreenshot;
InputObject showStateManager;
InputObject quickLoad1;
InputObject quickLoad2;

View File

@ -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;
}

View File

@ -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;

View File

@ -41,6 +41,7 @@ const char defaultStylesheet[] =
"\n";
#include "application/application.cpp"
#include "debugger/debugger.cpp"
#include "input/input.cpp"
#include "utility/utility.cpp"

View File

@ -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

View File

@ -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) {

View File

@ -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();

View File

@ -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();

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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 {

View File

@ -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);
}

View File

@ -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();
}