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