diff --git a/bsnes/nes/cartridge/cartridge.cpp b/bsnes/nes/cartridge/cartridge.cpp index d752293e..d8d5cb3f 100755 --- a/bsnes/nes/cartridge/cartridge.cpp +++ b/bsnes/nes/cartridge/cartridge.cpp @@ -2,9 +2,36 @@ namespace NES { +namespace Mapper { + unsigned Mapper::mirror(unsigned addr, unsigned size) const { + unsigned base = 0; + if(size) { + unsigned mask = 1 << 23; + while(addr >= size) { + while(!(addr & mask)) mask >>= 1; + addr -= mask; + if(size > mask) { + size -= mask; + base += mask; + } + mask >>= 1; + } + base += addr; + } + return base; + } + + #include "none/none.cpp" + #include "mmc1/mmc1.cpp" +} + Cartridge cartridge; void Cartridge::load(const string &xml, const uint8_t *data, unsigned size) { + rom_size = size - 16; + rom_data = new uint8[rom_size]; + memcpy(rom_data, data + 16, size - 16); + prg_size = data[4] * 0x4000; chr_size = data[5] * 0x2000; @@ -21,40 +48,53 @@ void Cartridge::load(const string &xml, const uint8_t *data, unsigned size) { chr_data = new uint8[chr_size](); } + mirroring = ((data[6] & 0x08) >> 2) | (data[6] & 0x01); + + uint8 mapperNumber = ((data[7] >> 4) << 4) | (data[6] >> 4); + switch(mapperNumber) { + default: mapper = &mapperNone; break; + case 0x01: mapper = &mapperMMC1; break; + } + loaded = true; } void Cartridge::unload() { if(loaded == false) return; + delete[] rom_data; delete[] prg_data; delete[] chr_data; loaded = false; } +void Cartridge::power() { + mapper->power(); +} + +void Cartridge::reset() { + mapper->reset(); +} + Cartridge::Cartridge() { loaded = false; } uint8 Cartridge::prg_read(uint16 addr) { - if(addr >= 0x8000 && addr <= 0xffff) { - addr &= 0x7fff; - if(addr >= prg_size) addr &= prg_size - 1; - return prg_data[addr]; - } + return mapper->prg_read(addr); } void Cartridge::prg_write(uint16 addr, uint8 data) { + return mapper->prg_write(addr, data); } uint8 Cartridge::chr_read(uint16 addr) { - return chr_data[addr & 0x1fff]; + return mapper->chr_read(addr); } void Cartridge::chr_write(uint16 addr, uint8 data) { - if(chr_ram == false) return; - chr_data[addr & 0x1fff] = data; + return mapper->chr_write(addr, data); } } diff --git a/bsnes/nes/cartridge/cartridge.hpp b/bsnes/nes/cartridge/cartridge.hpp index 2015403e..0b34002d 100755 --- a/bsnes/nes/cartridge/cartridge.hpp +++ b/bsnes/nes/cartridge/cartridge.hpp @@ -1,17 +1,46 @@ +namespace Mapper { + struct Mapper { + unsigned mirror(unsigned addr, unsigned size) const; + + virtual uint8 prg_read(uint16 addr) = 0; + virtual void prg_write(uint16 addr, uint8 data) = 0; + + virtual uint8 chr_read(uint16 addr) = 0; + virtual void chr_write(uint16 addr, uint8 data) = 0; + + virtual void power() = 0; + virtual void reset() = 0; + }; + + #include "none/none.hpp" + #include "mmc1/mmc1.hpp" +} + struct Cartridge : property { void load(const string &xml, const uint8_t *data, unsigned size); void unload(); + void power(); + void reset(); + readonly loaded; Cartridge(); +//privileged: + Mapper::Mapper *mapper; + Mapper::None mapperNone; + Mapper::MMC1 mapperMMC1; + uint8 prg_read(uint16 addr); void prg_write(uint16 addr, uint8 data); uint8 chr_read(uint16 addr); void chr_write(uint16 addr, uint8 data); + uint8 *rom_data; + unsigned rom_size; + uint8 *prg_data; unsigned prg_size; @@ -19,6 +48,7 @@ struct Cartridge : property { unsigned chr_size; bool chr_ram; + unsigned mirroring; }; extern Cartridge cartridge; diff --git a/bsnes/nes/cartridge/mmc1/mmc1.cpp b/bsnes/nes/cartridge/mmc1/mmc1.cpp new file mode 100755 index 00000000..34008887 --- /dev/null +++ b/bsnes/nes/cartridge/mmc1/mmc1.cpp @@ -0,0 +1,172 @@ +unsigned MMC1::ciram_addr(unsigned addr) const { + switch(r[0] & 0x03) { + case 0: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first) + case 1: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second) + case 2: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring + case 3: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring + } +} + +bool MMC1::prg_mode() const { + return r[0] & 0x04; +} + +unsigned MMC1::prg_addr() const { + if(r[0] & 0x08) return prg_bank() * 0x4000; + return (prg_bank() & ~1) * 0x8000; +} + +bool MMC1::chr_mode() const { + return r[0] & 0x10; +} + +unsigned MMC1::chr_banklo() const { + return r[1]; +} + +unsigned MMC1::chr_bankhi() const { + return r[2]; +} + +unsigned MMC1::prg_bank() const { + return r[3] & 0x0f; +} + +bool MMC1::prg_ram_disable() const { + return r[3] & 0x10; +} + +// + +uint8& MMC1::prg_data(unsigned addr) { + return cartridge.prg_data[mirror(addr, cartridge.prg_size)]; +} + +uint8& MMC1::chr_data(unsigned addr) { + return cartridge.chr_data[mirror(addr, cartridge.chr_size)]; +} + +// + +uint8 MMC1::prg_read(uint16 readaddr) { + unsigned addr = readaddr; + + if(addr >= 0x6000 && addr <= 0x7fff) { + if(prg_ram_disable() == false) return prg_ram[addr & 0x1fff]; + return 0x00; + } + + if(addr >= 0x8000 && addr <= 0xffff) { + if(prg_mode() == 0) { + if(addr >= 0x8000 && addr <= 0xbfff) { + addr = (addr & 0x3fff); + return prg_data(addr); + } + if(addr >= 0xc000 && addr <= 0xffff) { + addr = prg_addr() | (addr & 0x3fff); + return prg_data(addr); + } + } + + if(prg_mode() == 1) { + if(addr >= 0x8000 && addr <= 0xbfff) { + addr = prg_addr() | (addr & 0x3fff); + return prg_data(addr); + } + if(addr >= 0xc000 && addr <= 0xffff) { + addr = 15 * 0x4000 | (addr & 0x3fff); + return prg_data(addr); + } + } + } +} + +void MMC1::prg_write(uint16 addr, uint8 data) { + if(addr >= 0x6000 && addr <= 0x7fff) { + if(prg_ram_disable() == false) prg_ram[addr & 0x1fff] = data; + return; + } + + if(addr >= 0x8000 && addr <= 0xffff) { + if(data & 0x80) { + shiftaddr = 0; + r[0] |= 0x0c; + } else { + shiftdata >>= 1; + shiftdata |= (data & 1) << 4; + if(++shiftaddr == 5) { + shiftaddr = 0; + r[(addr >> 13) & 3] = shiftdata; + } + } + return; + } +} + +// + +uint8 MMC1::chr_read(uint16 readaddr) { + unsigned addr = readaddr; + + if(addr <= 0x0fff) { + if(chr_mode() == 0) addr = chr_banklo() * 0x2000 + (addr & 0x1fff); + if(chr_mode() == 1) addr = chr_banklo() * 0x1000 + (addr & 0x0fff); + return chr_data(addr); + } + + if(addr <= 0x1fff) { + if(chr_mode() == 0) addr = chr_banklo() * 0x2000 + (addr & 0x1fff); + if(chr_mode() == 1) addr = chr_bankhi() * 0x1000 + (addr & 0x0fff); + return chr_data(addr); + } + + if(addr <= 0x3eff) { + addr = ciram_addr(addr); + return ppu.ciram_read(addr); + } + + return ppu.cgram_read(addr); +} + +void MMC1::chr_write(uint16 readaddr, uint8 data) { + unsigned addr = readaddr; + + if(addr <= 0x0fff) { + if(cartridge.chr_ram == false) return; + if(chr_mode() == 0) addr = chr_banklo() * 0x2000 + (addr & 0x1fff); + if(chr_mode() == 1) addr = chr_banklo() * 0x1000 + (addr & 0x0fff); + chr_data(addr) = data; + return; + } + + if(addr <= 0x1fff) { + if(cartridge.chr_ram == false) return; + if(chr_mode() == 0) addr = chr_banklo() * 0x2000 + (addr & 0x1fff); + if(chr_mode() == 1) addr = chr_bankhi() * 0x1000 + (addr & 0x0fff); + chr_data(addr) = data; + return; + } + + if(addr <= 0x3eff) { + addr = ciram_addr(addr); + return ppu.ciram_write(addr, data); + } + + return ppu.cgram_write(addr, data); +} + +// + +void MMC1::power() { + reset(); +} + +void MMC1::reset() { + shiftaddr = 0; + shiftdata = 0; + + r[0] = 0x0c; + r[1] = 0x00; + r[2] = 0x01; + r[3] = 0x0f; +} diff --git a/bsnes/nes/cartridge/mmc1/mmc1.hpp b/bsnes/nes/cartridge/mmc1/mmc1.hpp new file mode 100755 index 00000000..20ade84c --- /dev/null +++ b/bsnes/nes/cartridge/mmc1/mmc1.hpp @@ -0,0 +1,28 @@ +struct MMC1 : Mapper { + uint8 prg_read(uint16 addr); + void prg_write(uint16 addr, uint8 data); + + uint8 chr_read(uint16 addr); + void chr_write(uint16 addr, uint8 data); + + void power(); + void reset(); + +private: + uint8 prg_ram[8192]; + uint8 r[4]; + unsigned shiftaddr; + unsigned shiftdata; + + unsigned ciram_addr(unsigned addr) const; + bool prg_mode() const; + unsigned prg_addr() const; + bool chr_mode() const; + unsigned chr_banklo() const; + unsigned chr_bankhi() const; + unsigned prg_bank() const; + bool prg_ram_disable() const; + + uint8& prg_data(unsigned addr); + uint8& chr_data(unsigned addr); +}; diff --git a/bsnes/nes/cartridge/none/none.cpp b/bsnes/nes/cartridge/none/none.cpp new file mode 100755 index 00000000..5215e8e4 --- /dev/null +++ b/bsnes/nes/cartridge/none/none.cpp @@ -0,0 +1,43 @@ +uint8 None::prg_read(uint16 addr) { + if(addr >= 0x8000 && addr <= 0xffff) { + addr &= 0x7fff; + if(addr >= cartridge.prg_size) addr &= cartridge.prg_size - 1; + return cartridge.prg_data[addr]; + } +} + +void None::prg_write(uint16 addr, uint8 data) { +} + +uint8 None::chr_read(uint16 addr) { + if(addr <= 0x1fff) { + return cartridge.chr_data[addr & 0x1fff]; + } + + if(addr <= 0x3eff) { + if(cartridge.mirroring == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff); + return ppu.ciram_read(addr & 0x07ff); + } + + return ppu.cgram_read(addr & 0x001f); +} + +void None::chr_write(uint16 addr, uint8 data) { + if(addr <= 0x1fff) { + if(cartridge.chr_ram == false) return; + cartridge.chr_data[addr & 0x1fff] = data; + } + + if(addr <= 0x3eff) { + if(cartridge.mirroring == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff); + return ppu.ciram_write(addr & 0x07ff, data); + } + + return ppu.cgram_write(addr & 0x001f, data); +} + +void None::power() { +} + +void None::reset() { +} diff --git a/bsnes/nes/cartridge/none/none.hpp b/bsnes/nes/cartridge/none/none.hpp new file mode 100755 index 00000000..1b7e2e3d --- /dev/null +++ b/bsnes/nes/cartridge/none/none.hpp @@ -0,0 +1,10 @@ +struct None : Mapper { + uint8 prg_read(uint16 addr); + void prg_write(uint16 addr, uint8 data); + + uint8 chr_read(uint16 addr); + void chr_write(uint16 addr, uint8 data); + + void power(); + void reset(); +}; diff --git a/bsnes/nes/cpu/core/core.hpp b/bsnes/nes/cpu/core/core.hpp index d9599876..2aad005b 100755 --- a/bsnes/nes/cpu/core/core.hpp +++ b/bsnes/nes/cpu/core/core.hpp @@ -1,13 +1,12 @@ struct Flags { - bool n, v, p, b, d, i, z, c; + bool n, v, d, i, z, c; inline operator unsigned() { - return (n << 7) | (v << 6) | (p << 5) | (b << 4) - | (d << 3) | (i << 2) | (z << 1) | (c << 0); + return (n << 7) | (v << 6) | (d << 3) | (i << 2) | (z << 1) | (c << 0); } inline Flags& operator=(uint8 data) { - n = data & 0x80; v = data & 0x40; p = data & 0x20; b = data & 0x10; + n = data & 0x80; v = data & 0x40; d = data & 0x08; i = data & 0x04; z = data & 0x02; c = data & 0x01; return *this; } @@ -95,6 +94,13 @@ void op_plp(); void op_rti(); void op_rts(); +void opill_nop_absolute(); +void opill_nop_absolute_x(); +void opill_nop_immediate(); +void opill_nop_implied(); +void opill_nop_zero_page(); +void opill_nop_zero_page_x(); + //exec.cpp void op_exec(); diff --git a/bsnes/nes/cpu/core/disassembler.cpp b/bsnes/nes/cpu/core/disassembler.cpp index 6c9093c8..83fa8b68 100755 --- a/bsnes/nes/cpu/core/disassembler.cpp +++ b/bsnes/nes/cpu/core/disassembler.cpp @@ -185,8 +185,8 @@ string CPU::disassemble() { output.append( "A:", hex<2>(regs.a), " X:", hex<2>(regs.x), " Y:", hex<2>(regs.y), " S:", hex<2>(regs.s), " ", - regs.p.n ? "N" : "n", regs.p.v ? "V" : "v", regs.p.p ? "P" : "p", regs.p.b ? "B" : "b", - regs.p.d ? "D" : "d", regs.p.i ? "I" : "i", regs.p.z ? "Z" : "z", regs.p.c ? "C" : "c" + regs.p.n ? "N" : "n", regs.p.v ? "V" : "v", regs.p.d ? "D" : "d", + regs.p.i ? "I" : "i", regs.p.z ? "Z" : "z", regs.p.c ? "C" : "c" ); return output; diff --git a/bsnes/nes/cpu/core/exec.cpp b/bsnes/nes/cpu/core/exec.cpp index 5ea21117..16d2b7bc 100755 --- a/bsnes/nes/cpu/core/exec.cpp +++ b/bsnes/nes/cpu/core/exec.cpp @@ -1,21 +1,29 @@ +//I = illegal instruction (only used as a visual indicator) +#define I + void CPU::op_exec() { uint8 opcode = op_readpci(); switch(opcode) { case 0x00: return op_brk(); case 0x01: return opi_read_indirect_zero_page_x<&CPU::opf_ora>(); + I case 0x04: return opill_nop_zero_page(); case 0x05: return opi_read_zero_page<&CPU::opf_ora>(); case 0x06: return opi_rmw_zero_page<&CPU::opf_asl>(); case 0x08: return op_php(); case 0x09: return opi_read_immediate<&CPU::opf_ora>(); case 0x0a: return opi_shift<&CPU::opf_sla>(); + I case 0x0c: return opill_nop_absolute(); case 0x0d: return opi_read_absolute<&CPU::opf_ora>(); case 0x0e: return opi_rmw_absolute<&CPU::opf_asl>(); case 0x10: return opi_branch(regs.p.n == 0); case 0x11: return opi_read_indirect_zero_page_y<&CPU::opf_ora>(); + I case 0x14: return opill_nop_zero_page_x(); case 0x15: return opi_read_zero_page_x<&CPU::opf_ora>(); case 0x16: return opi_rmw_zero_page_x<&CPU::opf_asl>(); case 0x18: return opi_clear_flag(regs.p.c); case 0x19: return opi_read_absolute_y<&CPU::opf_ora>(); + I case 0x1a: return opill_nop_implied(); + I case 0x1c: return opill_nop_absolute_x(); case 0x1d: return opi_read_absolute_x<&CPU::opf_ora>(); case 0x1e: return opi_rmw_absolute_x<&CPU::opf_asl>(); case 0x20: return op_jsr_absolute(); @@ -31,14 +39,18 @@ void CPU::op_exec() { case 0x2e: return opi_rmw_absolute<&CPU::opf_rol>(); case 0x30: return opi_branch(regs.p.n == 1); case 0x31: return opi_read_indirect_zero_page_y<&CPU::opf_and>(); + I case 0x34: return opill_nop_zero_page_x(); case 0x35: return opi_read_zero_page_x<&CPU::opf_and>(); case 0x36: return opi_rmw_zero_page_x<&CPU::opf_rol>(); case 0x38: return opi_set_flag(regs.p.c); case 0x39: return opi_read_absolute_y<&CPU::opf_and>(); + I case 0x3a: return opill_nop_implied(); + I case 0x3c: return opill_nop_absolute_x(); case 0x3d: return opi_read_absolute_x<&CPU::opf_and>(); case 0x3e: return opi_rmw_absolute_x<&CPU::opf_rol>(); case 0x40: return op_rti(); case 0x41: return opi_read_indirect_zero_page_x<&CPU::opf_eor>(); + I case 0x44: return opill_nop_zero_page(); case 0x45: return opi_read_zero_page<&CPU::opf_eor>(); case 0x46: return opi_rmw_zero_page<&CPU::opf_lsr>(); case 0x48: return opi_push(regs.a); @@ -49,15 +61,18 @@ void CPU::op_exec() { case 0x4e: return opi_rmw_absolute<&CPU::opf_lsr>(); case 0x50: return opi_branch(regs.p.v == 0); case 0x51: return opi_read_indirect_zero_page_y<&CPU::opf_eor>(); + I case 0x54: return opill_nop_zero_page_x(); case 0x55: return opi_read_zero_page_x<&CPU::opf_eor>(); case 0x56: return opi_rmw_zero_page_x<&CPU::opf_lsr>(); case 0x58: return opi_clear_flag(regs.p.i); case 0x59: return opi_read_absolute_y<&CPU::opf_eor>(); - case 0x5a: return opi_push(regs.y); + I case 0x5a: return opill_nop_implied(); + I case 0x5c: return opill_nop_absolute_x(); case 0x5d: return opi_read_absolute_x<&CPU::opf_eor>(); case 0x5e: return opi_rmw_absolute_x<&CPU::opf_lsr>(); case 0x60: return op_rts(); case 0x61: return opi_read_indirect_zero_page_x<&CPU::opf_adc>(); + I case 0x64: return opill_nop_zero_page(); case 0x65: return opi_read_zero_page<&CPU::opf_adc>(); case 0x66: return opi_rmw_zero_page<&CPU::opf_ror>(); case 0x68: return opi_pull(regs.a); @@ -67,19 +82,24 @@ void CPU::op_exec() { case 0x6d: return opi_read_absolute<&CPU::opf_adc>(); case 0x6e: return opi_rmw_absolute<&CPU::opf_ror>(); case 0x70: return opi_branch(regs.p.v == 1); + I case 0x74: return opill_nop_zero_page_x(); case 0x71: return opi_read_indirect_zero_page_y<&CPU::opf_adc>(); case 0x75: return opi_read_zero_page_x<&CPU::opf_adc>(); case 0x76: return opi_rmw_zero_page_x<&CPU::opf_ror>(); case 0x78: return opi_set_flag(regs.p.i); case 0x79: return opi_read_absolute_y<&CPU::opf_adc>(); - case 0x7a: return opi_pull(regs.y); + I case 0x7a: return opill_nop_implied(); + I case 0x7c: return opill_nop_absolute_x(); case 0x7d: return opi_read_absolute_x<&CPU::opf_adc>(); case 0x7e: return opi_rmw_absolute_x<&CPU::opf_ror>(); + I case 0x80: return opill_nop_absolute(); case 0x81: return opi_store_indirect_zero_page_x(regs.a); + I case 0x82: return opill_nop_immediate(); case 0x84: return opi_store_zero_page(regs.y); case 0x85: return opi_store_zero_page(regs.a); case 0x86: return opi_store_zero_page(regs.x); case 0x88: return opi_decrement(regs.y); + I case 0x89: return opill_nop_immediate(); case 0x8a: return opi_transfer(regs.x, regs.a, 1); case 0x8c: return opi_store_absolute(regs.y); case 0x8d: return opi_store_absolute(regs.a); @@ -118,6 +138,7 @@ void CPU::op_exec() { case 0xbe: return opi_read_absolute_y<&CPU::opf_ldx>(); case 0xc0: return opi_read_immediate<&CPU::opf_cpy>(); case 0xc1: return opi_read_indirect_zero_page_x<&CPU::opf_cmp>(); + I case 0xc2: return opill_nop_immediate(); case 0xc4: return opi_read_zero_page<&CPU::opf_cpy>(); case 0xc5: return opi_read_zero_page<&CPU::opf_cmp>(); case 0xc6: return opi_rmw_zero_page<&CPU::opf_dec>(); @@ -129,15 +150,18 @@ void CPU::op_exec() { case 0xce: return opi_rmw_absolute<&CPU::opf_dec>(); case 0xd0: return opi_branch(regs.p.z == 0); case 0xd1: return opi_read_indirect_zero_page_y<&CPU::opf_cmp>(); + I case 0xd4: return opill_nop_zero_page_x(); case 0xd5: return opi_read_zero_page_x<&CPU::opf_cmp>(); case 0xd6: return opi_rmw_zero_page_x<&CPU::opf_dec>(); case 0xd8: return opi_clear_flag(regs.p.d); case 0xd9: return opi_read_absolute_y<&CPU::opf_cmp>(); - case 0xda: return opi_push(regs.x); + I case 0xda: return opill_nop_implied(); + I case 0xdc: return opill_nop_absolute_x(); case 0xdd: return opi_read_absolute_x<&CPU::opf_cmp>(); case 0xde: return opi_rmw_absolute_x<&CPU::opf_dec>(); case 0xe0: return opi_read_immediate<&CPU::opf_cpx>(); case 0xe1: return opi_read_indirect_zero_page_x<&CPU::opf_sbc>(); + I case 0xe2: return opill_nop_immediate(); case 0xe4: return opi_read_zero_page<&CPU::opf_cpx>(); case 0xe5: return opi_read_zero_page<&CPU::opf_sbc>(); case 0xe6: return opi_rmw_zero_page<&CPU::opf_inc>(); @@ -149,19 +173,23 @@ void CPU::op_exec() { case 0xee: return opi_rmw_absolute<&CPU::opf_inc>(); case 0xf0: return opi_branch(regs.p.z == 1); case 0xf1: return opi_read_indirect_zero_page_y<&CPU::opf_sbc>(); + I case 0xf4: return opill_nop_zero_page_x(); case 0xf5: return opi_read_zero_page_x<&CPU::opf_sbc>(); case 0xf6: return opi_rmw_zero_page_x<&CPU::opf_inc>(); case 0xf8: return opi_set_flag(regs.p.d); case 0xf9: return opi_read_absolute_y<&CPU::opf_sbc>(); - case 0xfa: return opi_pull(regs.x); + I case 0xfa: return opill_nop_implied(); + I case 0xfc: return opill_nop_absolute_x(); case 0xfd: return opi_read_absolute_x<&CPU::opf_sbc>(); case 0xfe: return opi_rmw_absolute_x<&CPU::opf_inc>(); } - return op_nop(); +//return op_nop(); regs.pc--; print("Unimplemented opcode: ", hex<4>(regs.pc), " = ", hex<2>(bus.read(regs.pc)), "\n"); print("Counter = ", opcodeCounter, "\n"); while(true) scheduler.exit(); } + +#undef I diff --git a/bsnes/nes/cpu/core/opcodes.cpp b/bsnes/nes/cpu/core/opcodes.cpp index 6498c70d..afca5a02 100755 --- a/bsnes/nes/cpu/core/opcodes.cpp +++ b/bsnes/nes/cpu/core/opcodes.cpp @@ -188,12 +188,15 @@ L op_readpc(); } void CPU::opi_pull(uint8 &r) { + op_readpc(); + op_readpc(); L r = op_readsp(); regs.p.n = (r & 0x80); regs.p.z = (r == 0); } void CPU::opi_push(uint8 &r) { + op_readpc(); L op_writesp(r); } @@ -277,6 +280,7 @@ void CPU::opi_rmw_absolute() { abs.l = op_readpci(); abs.h = op_readpci(); rd = op_read(abs.w); + op_readpc(); call(op); L op_write(abs.w, rd); } @@ -285,8 +289,9 @@ template void CPU::opi_rmw_absolute_x() { abs.l = op_readpci(); abs.h = op_readpci(); - op_page(abs.w, abs.w + regs.x); + op_readpc(); rd = op_read(abs.w + regs.x); + op_readpc(); call(op); L op_write(abs.w + regs.x, rd); } @@ -387,7 +392,14 @@ L op_readpc(); void CPU::op_brk() { op_readpci(); - interrupt(0xfffe); + op_writesp(regs.pc >> 8); + op_writesp(regs.pc >> 0); + op_writesp(regs.p | 0x30); + abs.l = op_read(0xfffe); + regs.p.i = 1; + regs.p.d = 0; + abs.h = op_read(0xffff); + regs.pc = abs.w; } void CPU::op_jmp_absolute() { @@ -407,6 +419,7 @@ L iabs.h = op_read(abs.w); abs.l++; void CPU::op_jsr_absolute() { abs.l = op_readpci(); abs.h = op_readpci(); + op_readpc(); regs.pc--; op_writesp(regs.pc >> 8); L op_writesp(regs.pc >> 0); @@ -418,17 +431,20 @@ L op_readpc(); } void CPU::op_php() { -L op_writesp(regs.p); + op_readpc(); +L op_writesp(regs.p | 0x30); } void CPU::op_plp() { -L regs.p = op_readsp() | 0x30; + op_readpc(); + op_readpc(); +L regs.p = op_readsp(); } void CPU::op_rti() { op_readpc(); op_readpc(); - regs.p = op_readsp() | 0x30; + regs.p = op_readsp(); abs.l = op_readsp(); L abs.h = op_readsp(); regs.pc = abs.w; @@ -443,5 +459,40 @@ L op_readpc(); regs.pc = ++abs.w; } +//illegal opcodes +//=============== + +void CPU::opill_nop_absolute() { + abs.l = op_readpci(); + abs.h = op_readpci(); +L op_readpc(); +} + +void CPU::opill_nop_absolute_x() { + abs.l = op_readpci(); + abs.h = op_readpci(); + op_page(abs.w, abs.w + regs.x); +L op_readpc(); +} + +void CPU::opill_nop_immediate() { +L rd = op_readpc(); +} + +void CPU::opill_nop_implied() { +L op_readpc(); +} + +void CPU::opill_nop_zero_page() { + zp = op_readpci(); +L op_readpc(); +} + +void CPU::opill_nop_zero_page_x() { + zp = op_readpci(); + op_readpc(); +L op_readpc(); +} + #undef call #undef L diff --git a/bsnes/nes/cpu/cpu.cpp b/bsnes/nes/cpu/cpu.cpp index ba3cf649..f50e62ce 100755 --- a/bsnes/nes/cpu/cpu.cpp +++ b/bsnes/nes/cpu/cpu.cpp @@ -29,8 +29,10 @@ void CPU::main() { continue; } -// if(lpc != regs.pc) { print(disassemble(), "\n"); } lpc = regs.pc; -// if(lpc != regs.pc) { fprintf(fp, "%s\n", (const char*)disassemble()); fflush(fp); } lpc = regs.pc; + if(trace) { + if(lpc != regs.pc) { print(disassemble(), "\n"); } lpc = regs.pc; + //if(lpc != regs.pc) { fprintf(fp, "%s\n", (const char*)disassemble()); fflush(fp); } lpc = regs.pc; + } op_exec(); opcodeCounter++; @@ -45,7 +47,7 @@ void CPU::add_clocks(unsigned clocks) { void CPU::interrupt(uint16 vector) { op_writesp(regs.pc >> 8); op_writesp(regs.pc >> 0); - op_writesp(regs.p); + op_writesp(regs.p | 0x20); abs.l = op_read(vector + 0); regs.p.i = 1; regs.p.d = 0; @@ -58,7 +60,7 @@ void CPU::power() { regs.x = 0x00; regs.y = 0x00; regs.s = 0x00; - regs.p = 0x34; + regs.p = 0x04; for(unsigned addr = 0; addr < 0x0800; addr++) ram[addr] = 0xff; ram[0x0008] = 0xf7; @@ -120,7 +122,7 @@ void CPU::write(uint16 addr, uint8 data) { } void CPU::oam_dma(uint16 addr) { -// op_readpc(); + op_readpc(); for(unsigned n = 0; n < 256; n++) { uint8 data = bus.read(addr + n); op_write(0x2004, data); diff --git a/bsnes/nes/cpu/cpu.hpp b/bsnes/nes/cpu/cpu.hpp index 80f63041..07de3a40 100755 --- a/bsnes/nes/cpu/cpu.hpp +++ b/bsnes/nes/cpu/cpu.hpp @@ -27,6 +27,8 @@ struct CPU : Processor { void write(uint16 addr, uint8 data); void oam_dma(uint16 addr); + +bool trace; }; extern CPU cpu; diff --git a/bsnes/nes/nes.hpp b/bsnes/nes/nes.hpp index d136ae53..365c492a 100755 --- a/bsnes/nes/nes.hpp +++ b/bsnes/nes/nes.hpp @@ -4,7 +4,7 @@ namespace NES { namespace Info { static const char Name[] = "bnes"; - static const char Version[] = "000.01"; + static const char Version[] = "000.02"; } } diff --git a/bsnes/nes/ppu/ppu.cpp b/bsnes/nes/ppu/ppu.cpp index f8e5df19..3ece9580 100755 --- a/bsnes/nes/ppu/ppu.cpp +++ b/bsnes/nes/ppu/ppu.cpp @@ -122,9 +122,9 @@ void PPU::reset() { memset(buffer, 0, sizeof buffer); - memset(nram, 0, sizeof nram); - memset(pram, 0, sizeof pram); - memset(sram, 0, sizeof sram); + memset(ciram, 0, sizeof ciram); + memset(cgram, 0, sizeof cgram); + memset(oam, 0, sizeof oam); } uint8 PPU::read(uint16 addr) { @@ -140,14 +140,14 @@ uint8 PPU::read(uint16 addr) { status.address_latch = 0; break; case 4: //OAMDATA - result = sram[status.oam_addr++]; + result = oam[status.oam_addr++]; break; case 7: //PPUDATA if(addr >= 0x3f00) { - result = bus_read(status.vaddr); + result = cartridge.chr_read(status.vaddr); } else { result = status.bus_data; - status.bus_data = bus_read(status.vaddr); + status.bus_data = cartridge.chr_read(status.vaddr); } status.vaddr += status.vram_increment; break; @@ -185,7 +185,7 @@ void PPU::write(uint16 addr, uint8 data) { status.oam_addr = data; return; case 4: //OAMDATA - sram[status.oam_addr++] = data; + oam[status.oam_addr++] = data; return; case 5: //PPUSCROLL if(status.address_latch == 0) { @@ -206,37 +206,28 @@ void PPU::write(uint16 addr, uint8 data) { status.address_latch ^= 1; return; case 7: //PPUDATA - bus_write(status.vaddr, data); + cartridge.chr_write(status.vaddr, data); status.vaddr += status.vram_increment; return; } } -uint8 PPU::bus_read(uint16 addr) { - if(addr <= 0x1fff) return cartridge.chr_read(addr); - if(addr <= 0x3eff) return nram[addr & 0x07ff]; - if(addr <= 0x3fff) return pram[addr & 0x001f]; +uint8 PPU::ciram_read(uint16 addr) { + return ciram[addr & 0x07ff]; } -void PPU::bus_write(uint16 addr, uint8 data) { - if(addr <= 0x1fff) { - return cartridge.chr_write(addr, data); - } +void PPU::ciram_write(uint16 addr, uint8 data) { + ciram[addr & 0x07ff] = data; +} - if(addr <= 0x3eff) { - nram[addr & 0x07ff] = data; - return; - } +uint8 PPU::cgram_read(uint16 addr) { + if((addr & 0x13) == 0x10) addr &= ~0x10; + return cgram[addr & 0x1f]; +} - if(addr <= 0x3fff) { - addr &= 0x1f; - if(addr == 0x10) addr = 0x00; - if(addr == 0x14) addr = 0x04; - if(addr == 0x18) addr = 0x08; - if(addr == 0x1c) addr = 0x0c; - pram[addr] = data; - return; - } +void PPU::cgram_write(uint16 addr, uint8 data) { + if((addr & 0x13) == 0x10) addr &= ~0x10; + cgram[addr & 0x1f] = data; } bool PPU::raster_enable() const { @@ -258,24 +249,26 @@ unsigned PPU::scrolly() const { void PPU::render_scanline() { uint32 *line = buffer + status.ly * 256; - uint8 oam[8][5]; + uint8 ioam[8][5]; unsigned oamc = 0; unsigned sprite_height = status.sprite_size ? 16 : 8; for(unsigned n = 0; n < 64; n++) { - unsigned y = sram[(n * 4) + 0] + 1; + unsigned y = oam[(n * 4) + 0] + 1; if(status.ly < y || status.ly >= y + sprite_height) continue; - oam[oamc][0] = y; - oam[oamc][1] = sram[(n * 4) + 1]; - oam[oamc][2] = sram[(n * 4) + 2]; - oam[oamc][3] = sram[(n * 4) + 3]; - oam[oamc][4] = n; + ioam[oamc][0] = y; + ioam[oamc][1] = oam[(n * 4) + 1]; + ioam[oamc][2] = oam[(n * 4) + 2]; + ioam[oamc][3] = oam[(n * 4) + 3]; + ioam[oamc][4] = n; if(++oamc >= 8) break; } for(unsigned x = 0; x < 256; x++) { unsigned offsetx = x + scrollx(); - unsigned offsety = status.ly + scrolly(); + unsigned offsety = status.ly + (scrolly() < 240 ? scrolly() : 240 - scrolly()); + + //if(x==0&&status.ly==64) print("Y=", offsety, "\n"); bool screenx = offsetx & 256; bool screeny = offsety & 256; @@ -285,8 +278,8 @@ void PPU::render_scanline() { unsigned base = nametable_addr() + (screenx * 0x0400) + (screeny * 0x0800); - uint8 tile = bus_read(base + (offsety / 8) * 32 + (offsetx / 8)); - uint8 attr = bus_read(base + 0x03c0 + (offsety / 32) * 8 + (offsetx / 32)); + uint8 tile = cartridge.chr_read(base + (offsety / 8) * 32 + (offsetx / 8)); + uint8 attr = cartridge.chr_read(base + 0x03c0 + (offsety / 32) * 8 + (offsetx / 32)); if((offsety / 16) & 1) attr >>= 4; if((offsetx / 16) & 1) attr >>= 2; @@ -294,8 +287,8 @@ void PPU::render_scanline() { unsigned tilex = offsetx & 7; unsigned tiley = offsety & 7; - uint8 tdlo = bus_read(status.bg_addr + tile * 16 + 0 + tiley); - uint8 tdhi = bus_read(status.bg_addr + tile * 16 + 8 + tiley); + uint8 tdlo = cartridge.chr_read(status.bg_addr + tile * 16 + 0 + tiley); + uint8 tdhi = cartridge.chr_read(status.bg_addr + tile * 16 + 8 + tiley); unsigned mask = 0x80 >> tilex; unsigned palette = 0; @@ -308,40 +301,39 @@ void PPU::render_scanline() { } for(unsigned n = 0; n < oamc; n++) { - if(x < oam[n][3] || x >= oam[n][3] + 8) continue; + if(x < ioam[n][3] || x >= ioam[n][3] + 8) continue; if(status.sprite_enable == false) continue; if(status.sprite_edge_enable == false && x < 8) continue; - unsigned spritex = x - oam[n][3]; - unsigned spritey = status.ly - oam[n][0]; + unsigned spritex = x - ioam[n][3]; + unsigned spritey = status.ly - ioam[n][0]; unsigned addr = (sprite_height == 8) - ? status.sprite_addr + oam[n][1] * 16 - : ((oam[n][1] & ~1) * 16) + ((oam[n][1] & 1) * 0x1000); + ? status.sprite_addr + ioam[n][1] * 16 + : ((ioam[n][1] & ~1) * 16) + ((ioam[n][1] & 1) * 0x1000); - if(oam[n][2] & 0x80) spritey ^= (sprite_height - 1); - if(oam[n][2] & 0x40) spritex ^= 7; + if(ioam[n][2] & 0x80) spritey ^= (sprite_height - 1); + if(ioam[n][2] & 0x40) spritex ^= 7; if(spritey & 8) spritey += 8; - tdlo = bus_read(addr + 0 + spritey); - tdhi = bus_read(addr + 8 + spritey); + tdlo = cartridge.chr_read(addr + 0 + spritey); + tdhi = cartridge.chr_read(addr + 8 + spritey); mask = 0x80 >> spritex; unsigned sprite_palette = 0; sprite_palette |= (tdlo & mask) ? 1 : 0; sprite_palette |= (tdhi & mask) ? 2 : 0; if(sprite_palette == 0) continue; - sprite_palette |= (oam[n][2] & 3) << 2; - bool priority = oam[n][2] & 0x20; - if(priority == 0 || palette == 0) { - if(oam[n][4] == 0) status.sprite_zero_hit = 1; - palette = 16 + sprite_palette; - } + if(ioam[n][4] == 0) status.sprite_zero_hit = 1; + sprite_palette |= (ioam[n][2] & 3) << 2; + + bool priority = ioam[n][2] & 0x20; + if(priority == 0 || palette == 0) palette = 16 + sprite_palette; break; } - *line++ = paletteRGB[pram[palette]]; + *line++ = paletteRGB[cgram[palette]]; } } diff --git a/bsnes/nes/ppu/ppu.hpp b/bsnes/nes/ppu/ppu.hpp index d2ae10a0..7094862a 100755 --- a/bsnes/nes/ppu/ppu.hpp +++ b/bsnes/nes/ppu/ppu.hpp @@ -9,8 +9,11 @@ struct PPU : Processor { uint8 read(uint16 addr); void write(uint16 addr, uint8 data); - uint8 bus_read(uint16 addr); - void bus_write(uint16 addr, uint8 data); + uint8 ciram_read(uint16 addr); + void ciram_write(uint16 addr, uint8 data); + + uint8 cgram_read(uint16 addr); + void cgram_write(uint16 addr, uint8 data); void render_scanline(); @@ -62,9 +65,9 @@ struct PPU : Processor { uint32 buffer[256 * 240]; uint32 paletteRGB[64]; - uint8 nram[2048]; - uint8 pram[32]; - uint8 sram[256]; + uint8 ciram[2048]; + uint8 cgram[32]; + uint8 oam[256]; }; extern PPU ppu; diff --git a/bsnes/nes/system/system.cpp b/bsnes/nes/system/system.cpp index 83395c9f..342e955a 100755 --- a/bsnes/nes/system/system.cpp +++ b/bsnes/nes/system/system.cpp @@ -9,12 +9,14 @@ void System::run() { } void System::power() { + cartridge.power(); cpu.power(); ppu.power(); scheduler.power(); } void System::reset() { + cartridge.reset(); cpu.reset(); ppu.reset(); scheduler.reset(); diff --git a/bsnes/snes/snes.hpp b/bsnes/snes/snes.hpp index a405b627..e1ee6c39 100755 --- a/bsnes/snes/snes.hpp +++ b/bsnes/snes/snes.hpp @@ -4,7 +4,7 @@ namespace SNES { namespace Info { static const char Name[] = "bsnes"; - static const char Version[] = "082.05"; + static const char Version[] = "082.06"; static const unsigned SerializerVersion = 21; } } diff --git a/bsnes/ui/general/file-browser.cpp b/bsnes/ui/general/file-browser.cpp index 04698cc8..55ef66e4 100755 --- a/bsnes/ui/general/file-browser.cpp +++ b/bsnes/ui/general/file-browser.cpp @@ -40,6 +40,7 @@ FileBrowser::FileBrowser() { char path[PATH_MAX]; auto unused = getcwd(path); + strcpy(path, "/media/sdb1/root/nes_images/"); setPath(path); } diff --git a/bsnes/ui/general/main-window.cpp b/bsnes/ui/general/main-window.cpp index 3479af2e..df280700 100755 --- a/bsnes/ui/general/main-window.cpp +++ b/bsnes/ui/general/main-window.cpp @@ -33,6 +33,7 @@ MainWindow::MainWindow() { toolsMenu.setText("Tools"); toolsShrinkWindow.setText("Shrink Window"); + toolsTest.setText("Test"); helpMenu.setText("Help"); helpAbout.setText("About ..."); @@ -66,6 +67,7 @@ MainWindow::MainWindow() { append(toolsMenu); toolsMenu.append(toolsShrinkWindow); + toolsMenu.append(toolsTest); append(helpMenu); helpMenu.append(helpAbout); @@ -126,6 +128,10 @@ MainWindow::MainWindow() { toolsShrinkWindow.onTick = [&] { utility->resizeMainWindow(true); }; + toolsTest.onTick = [&] { + NES::cpu.trace = toolsTest.checked(); + }; + helpAbout.onTick = [&] { MessageWindow::information(*this, { application->title, "\n\n", diff --git a/bsnes/ui/general/main-window.hpp b/bsnes/ui/general/main-window.hpp index a1ece516..28e97b47 100755 --- a/bsnes/ui/general/main-window.hpp +++ b/bsnes/ui/general/main-window.hpp @@ -31,6 +31,7 @@ struct MainWindow : Window { Menu toolsMenu; Item toolsShrinkWindow; + CheckItem toolsTest; Menu helpMenu; Item helpAbout; diff --git a/bsnes/ui/utility/utility.cpp b/bsnes/ui/utility/utility.cpp index d608f993..5e9de0bf 100755 --- a/bsnes/ui/utility/utility.cpp +++ b/bsnes/ui/utility/utility.cpp @@ -5,14 +5,12 @@ void Utility::setMode(Interface::Mode mode) { video.clear(); audio.clear(); - mainWindow->cartridgeMenu.setVisible(false); mainWindow->nesMenu.setVisible(false); mainWindow->snesMenu.setVisible(false); mainWindow->gameBoyMenu.setVisible(false); if(mode == Interface::Mode::None) { mainWindow->setTitle(application->title); - mainWindow->cartridgeMenu.setVisible(true); mainWindow->setStatusText("No cartridge loaded"); }