diff --git a/bsnes.cfg b/bsnes.cfg index 8bb84202..48be699b 100644 --- a/bsnes.cfg +++ b/bsnes.cfg @@ -35,7 +35,7 @@ video.vblank = false # Show framerate in window title # (default = true) -gui.show_fps = false +gui.show_fps = true # Joypad1 up # (default = 0x26) @@ -85,3 +85,51 @@ input.joypad1.select = 0x10 # (default = 0xd) input.joypad1.start = 0xd +# Joypad2 up +# (default = 0x54) +input.joypad2.up = 0x54 + +# Joypad2 down +# (default = 0x47) +input.joypad2.down = 0x47 + +# Joypad2 left +# (default = 0x46) +input.joypad2.left = 0x46 + +# Joypad2 right +# (default = 0x48) +input.joypad2.right = 0x48 + +# Joypad2 A +# (default = 0x4b) +input.joypad2.a = 0x4b + +# Joypad2 B +# (default = 0x4a) +input.joypad2.b = 0x4a + +# Joypad2 X +# (default = 0x49) +input.joypad2.x = 0x49 + +# Joypad2 Y +# (default = 0x55) +input.joypad2.y = 0x55 + +# Joypad2 L +# (default = 0x4f) +input.joypad2.l = 0x4f + +# Joypad2 R +# (default = 0x4c) +input.joypad2.r = 0x4c + +# Joypad2 select +# (default = 0x5b) +input.joypad2.select = 0x5b + +# Joypad2 start +# (default = 0x5d) +input.joypad2.start = 0x5d + diff --git a/bsnes.exe b/bsnes.exe index 2a1747ec..e74488f3 100644 Binary files a/bsnes.exe and b/bsnes.exe differ diff --git a/license.txt b/license.txt index 23d55685..c9c93e74 100644 --- a/license.txt +++ b/license.txt @@ -19,3 +19,8 @@ http://www.libsdl.org/ This library is distributed under the terms of the GNU LGPL: http://www.gnu.org/copyleft/lesser.html + +Licensing Exemptions: +--------------------- +Richard Bannister has asked for and received my permission to distribute +a binary-only port of bsnes on the Mac OS X platform. diff --git a/src/apu/apuregs.h b/src/apu/apuregs.h index 3d96dd6f..25d46793 100644 --- a/src/apu/apuregs.h +++ b/src/apu/apuregs.h @@ -13,7 +13,7 @@ private: inline bool operator ^= (bool i) { if(i)_b ^= B; return (_b & B); } }; public: - union { +union { uint8 _b; bit<0x80> n; bit<0x40> v; @@ -23,7 +23,7 @@ public: bit<0x04> i; bit<0x02> z; bit<0x01> c; - }; +}; APURegFlags() { _b = 0; } inline operator uint8() { return _b; } @@ -38,10 +38,11 @@ public: uint16 pc; union { uint16 ya; -//not endian-safe - struct { - uint8 a, y; - }; +#ifdef ARCH_LSB + struct { uint8 a, y; }; +#else + struct { uint8 y, a; }; +#endif }; uint8 x, sp; APURegFlags p; diff --git a/src/apu/bapu/bapu_op_pc.cpp b/src/apu/bapu/bapu_op_pc.cpp index 98f4e788..0fbe0b5a 100644 --- a/src/apu/bapu/bapu_op_pc.cpp +++ b/src/apu/bapu/bapu_op_pc.cpp @@ -510,7 +510,7 @@ void bAPU::op_cbne_dp() { rd = op_read(); break; case 3: - sp = op_read(OPMODE_DP, dp + 0); + sp = op_read(OPMODE_DP, dp); break; case 4: if(regs.a == sp)status.cycle_pos = 0; @@ -533,14 +533,16 @@ void bAPU::op_cbne_dpx() { rd = op_read(); break; case 3: - sp = op_read(OPMODE_DP, dp + regs.x); break; case 4: - if(regs.a == sp)status.cycle_pos = 0; + sp = op_read(OPMODE_DP, dp + regs.x); break; case 5: + if(regs.a == sp)status.cycle_pos = 0; break; case 6: + break; + case 7: regs.pc += (int8)rd; status.cycle_pos = 0; break; diff --git a/src/apu/bapu/op_pc.b b/src/apu/bapu/op_pc.b index d6e0b989..a13b1147 100644 --- a/src/apu/bapu/op_pc.b +++ b/src/apu/bapu/op_pc.b @@ -37,16 +37,25 @@ bbc7(0xf3, 0x80, ==) { 6:regs.pc += (int8)rd; } -cbne_dp(0x2e, 0), -cbne_dpx(0xde, regs.x) { +cbne_dp(0x2e) { 1:dp = op_read(); 2:rd = op_read(); -3:sp = op_read(OPMODE_DP, dp + $1); +3:sp = op_read(OPMODE_DP, dp); 4:if(regs.a == sp)end; 5: 6:regs.pc += (int8)rd; } +cbne_dpx(0xde) { +1:dp = op_read(); +2:rd = op_read(); +3: +4:sp = op_read(OPMODE_DP, dp + regs.x); +5:if(regs.a == sp)end; +6: +7:regs.pc += (int8)rd; +} + dbnz_dp(0x6e) { 1:dp = op_read(); 2:rd = op_read(); diff --git a/src/base.h b/src/base.h index 783fa03f..3141c3a0 100644 --- a/src/base.h +++ b/src/base.h @@ -1,9 +1,27 @@ +//this should be declared in the port-specific makefiles +//#define ARCH_LSB +//#define ARCH_MSB + +#ifndef ARCH_LSB + #ifndef ARCH_MSB + #define ARCH_LSB + #endif +#endif + #include #include "lib/libbase.h" #include "lib/libvector.h" #include "lib/libstring.h" #include "lib/libconfig.h" +inline uint16 read16(uint8 *addr, uint pos) { +#ifdef ARCH_LSB + return *((uint16*)(addr + pos)); +#else + return (addr[pos]) | (addr[pos + 1] << 8); +#endif +} + #if defined(_WIN32) #define _WIN32_ #undef _UNIX_ diff --git a/src/cpu/bcpu/bcpu.cpp b/src/cpu/bcpu/bcpu.cpp index 295b1f2e..2ce1cc9c 100644 --- a/src/cpu/bcpu/bcpu.cpp +++ b/src/cpu/bcpu/bcpu.cpp @@ -18,54 +18,72 @@ uint8 bCPU::pio_status() { return status.pio; } void bCPU::run() { - switch(status.cpu_state) { - case CPUSTATE_DMA: - dma_run(); - break; - case CPUSTATE_WAI: - case CPUSTATE_RUN: - if(status.cycle_pos == 0) { - //interrupts only trigger on opcode edges - if(time.nmi_pending == true) { - time.nmi_pending = false; - irq(0xffea); - break; - } - if(time.irq_pending == true) { - time.irq_pending = false; - irq(0xffee); - break; - } - } - //fallthrough - case CPUSTATE_STP: - exec_cycle(); - break; + if(run_state.hdma) { + exec_hdma(); + return; } - cycle_edge(); + if(run_state.dma) { + exec_dma(); + return; + } + + if(run_state.irq) { + irq_run(); + return; + } + + if(run_state.stp) { + exec_cycle(); + return; + } + + if(status.cycle_pos == 0) { + //interrupts only trigger on opcode edges + if(time.nmi_pending == true) { + time.nmi_pending = false; + aa.w = 0xffea; + run_state.irq = true; + return; + } + if(time.irq_pending == true) { + time.irq_pending = false; + aa.w = 0xffee; + run_state.irq = true; + return; + } + } + + exec_cycle(); } void bCPU::scanline() { - status.hdma_triggered = false; + time.hdma_triggered = false; if(vcounter() == 225 && status.auto_joypad_poll == true) { - snes->poll_input(); + snes->poll_input(SNES::DEV_JOYPAD1); + snes->poll_input(SNES::DEV_JOYPAD2); //When the SNES auto-polls the joypads, it writes 1, then 0 to //$4016, then reads from each 16 times to get the joypad state //information. As a result, the joypad read positions are set //to 16 after such a poll. Position 16 is the controller //connected status bit. status.joypad1_read_pos = 16; + status.joypad2_read_pos = 16; } } void bCPU::frame() { - hdma_initialize(); - time.nmi_read = 1; time.nmi_line = 1; time.nmi_transition = 0; + + if(cpu_version == 2) { + time.hdmainit_trigger_pos = 12 + dma_counter(); + } else { + time.hdmainit_trigger_pos = 12 + 8 - dma_counter(); + } + time.hdmainit_triggered = false; } void bCPU::power() { @@ -94,21 +112,27 @@ void bCPU::reset() { mmio_reset(); dma_reset(); - status.cpu_state = CPUSTATE_RUN; - status.dma_state = DMASTATE_STOP; + run_state.hdma = false; + run_state.dma = false; + run_state.irq = false; + run_state.wai = false; + run_state.stp = false; status.cycle_pos = 0; status.cycle_count = 0; status.cycles_executed = 0; - status.hdma_triggered = false; - apu_port[0] = 0x00; apu_port[1] = 0x00; apu_port[2] = 0x00; apu_port[3] = 0x00; frame(); + +//initial latch values for $213c/$213d +//[x]0035 : [y]0000 (53.0 -> 212) [lda $2137] +//[x]0038 : [y]0000 (56.5 -> 226) [nop : lda $2137] + add_cycles(186); } uint8 bCPU::port_read(uint8 port) { @@ -141,20 +165,18 @@ void bCPU::cpu_c6(uint16 addr) { * cpu_io is an I/O cycle, and always 6 clock cycles long. * mem_read / mem_write indicate memory access bus cycle, * they are either 6, 8, or 12 bus cycles long, depending - * both on location and the $420d.1 FastROM enable bit. + * both on location and the $420d.d0 FastROM enable bit. */ void bCPU::cpu_io() { - if(status.is_last_cycle)last_cycle_exec(); - status.cycle_count = 6; + pre_exec_cycle(); add_cycles(6); } uint8 bCPU::mem_read(uint32 addr) { - if(status.is_last_cycle)last_cycle_exec(); - status.cycle_count = mem_bus->speed(addr); + pre_exec_cycle(); add_cycles(status.cycle_count - 4); regs.mdr = mem_bus->read(addr); add_cycles(4); @@ -162,9 +184,8 @@ uint8 bCPU::mem_read(uint32 addr) { } void bCPU::mem_write(uint32 addr, uint8 value) { - if(status.is_last_cycle)last_cycle_exec(); - status.cycle_count = mem_bus->speed(addr); + pre_exec_cycle(); add_cycles(status.cycle_count); mem_bus->write(addr, value); } diff --git a/src/cpu/bcpu/bcpu.h b/src/cpu/bcpu/bcpu.h index d593b288..9217a564 100644 --- a/src/cpu/bcpu/bcpu.h +++ b/src/cpu/bcpu/bcpu.h @@ -32,32 +32,39 @@ CPUReg24 aa, rd; uint8 dp, sp; enum { - CPUSTATE_RUN = 0, - CPUSTATE_WAI, - CPUSTATE_STP, - CPUSTATE_DMA -}; - -enum { - DMASTATE_STOP = 0, DMASTATE_DMASYNC, DMASTATE_DMASYNC2, + DMASTATE_DMASYNC3, DMASTATE_RUN, DMASTATE_CPUSYNC, - DMASTATE_CPUSYNC2 + + HDMASTATE_IDMASYNC, + HDMASTATE_IDMASYNC2, + HDMASTATE_IDMASYNC3, + HDMASTATE_ICPUSYNC, + + HDMASTATE_DMASYNC, + HDMASTATE_DMASYNC2, + HDMASTATE_DMASYNC3, + HDMASTATE_RUN, + HDMASTATE_CPUSYNC }; struct { - uint8 cpu_state, cycle_pos, cycle_count; + bool hdma; + bool dma; + bool irq; + bool stp; + bool wai; +} run_state; + +struct { + uint8 cycle_pos, cycle_count; uint8 opcode; uint32 cycles_executed; -//set by last_cycle(), cleared by last_cycle_exec() - bool is_last_cycle; - - uint8 dma_state; - uint32 dma_cycle_count; - bool hdma_triggered; + uint8 dma_state, hdma_state; + uint32 dma_cycle_count, hdma_cycle_count; //$4207-$420a uint16 virq_trigger, hirq_trigger; @@ -65,9 +72,9 @@ struct { //$2181-$2183 uint32 wram_addr; -//$4016 - uint8 joypad1_strobe_value; - uint8 joypad1_read_pos; +//$4016-$4017 + uint8 joypad1_strobe_value, joypad2_strobe_value; + uint8 joypad1_read_pos, joypad2_read_pos; //$4200 bool nmi_enabled; @@ -92,6 +99,12 @@ struct { uint16 r4216; } status; +//$43x0.d7 +enum { + DMA_CPUTOMMIO = 0, + DMA_MMIOTOCPU = 1 +}; + struct { uint32 read_index; //set to 0 at beginning of DMA/HDMA @@ -113,7 +126,10 @@ struct { //$43x4 uint8 srcbank; //$43x5-$43x6 - uint16 xfersize; + union { + uint16 xfersize; + uint16 hdma_iaddr; + }; //$43x7 uint8 hdma_ibank; //$43x8-$43x9 @@ -124,15 +140,11 @@ struct { uint8 hdma_unknown; //hdma-specific - bool hdma_first_line; - bool hdma_repeat; - uint16 hdma_iaddr; bool hdma_active; + bool hdma_do_transfer; } channel[8]; inline bool hdma_test(); - - inline void irq(uint16 addr); inline bool nmi_test(); inline bool irq_test(); @@ -144,17 +156,27 @@ struct { inline void power(); inline void reset(); + inline void irq_run(); + //dma commands + inline void dma_add_cycles(uint32 cycles); + inline void hdma_add_cycles(uint32 cycles); inline void dma_run(); inline void hdma_run(); - inline void hdma_initialize(); + inline void hdma_update(uint8 i); + inline uint8 hdma_enabled_channels(); + inline uint8 hdma_active_channels(); + inline void hdmainit_activate(); + inline void hdma_activate(); inline void dma_cputommio(uint8 i, uint8 index); inline void dma_mmiotocpu(uint8 i, uint8 index); inline void dma_write(uint8 i, uint8 index); inline uint32 dma_addr(uint8 i); inline uint32 hdma_addr(uint8 i); inline uint32 hdma_iaddr(uint8 i); - inline void hdma_write(uint8 i, uint8 l, uint8 x); + inline uint16 hdma_mmio(uint8 i); + inline uint8 hdma_read(uint8 i); + inline void hdma_write(uint8 i, uint8 x); inline void dma_reset(); //mmio commands @@ -172,6 +194,8 @@ struct { uint8 mmio_r4217(); uint8 mmio_r4218(); uint8 mmio_r4219(); + uint8 mmio_r421a(); + uint8 mmio_r421b(); uint8 mmio_r43x0(uint8 i); uint8 mmio_r43x1(uint8 i); uint8 mmio_r43x2(uint8 i); @@ -189,6 +213,7 @@ struct { void mmio_w2182(uint8 value); void mmio_w2183(uint8 value); void mmio_w4016(uint8 value); + void mmio_w4017(uint8 value); void mmio_w4200(uint8 value); void mmio_w4201(uint8 value); void mmio_w4202(uint8 value); @@ -217,10 +242,11 @@ struct { void mmio_w43xb(uint8 value, uint8 i); enum { CYCLE_OPREAD = 0, CYCLE_READ, CYCLE_WRITE, CYCLE_IO }; + inline void pre_exec_cycle(); + inline void exec_hdma(); + inline void exec_dma(); inline void exec_cycle(); inline void last_cycle(); - inline void last_cycle_exec(); - inline void cycle_edge(); inline bool in_opcode(); //cpu extra-cycle conditions diff --git a/src/cpu/bcpu/bcpu_dma.cpp b/src/cpu/bcpu/bcpu_dma.cpp index 61a6b12f..dc9ccbc0 100644 --- a/src/cpu/bcpu/bcpu_dma.cpp +++ b/src/cpu/bcpu/bcpu_dma.cpp @@ -1,7 +1,22 @@ +void bCPU::dma_add_cycles(uint32 cycles) { + status.dma_cycle_count += cycles; +} + +void bCPU::hdma_add_cycles(uint32 cycles) { + if(run_state.dma) { + status.dma_cycle_count += cycles; + } + status.hdma_cycle_count += cycles; +} + uint32 bCPU::dma_addr(uint8 i) { uint32 r; - r = channel[i].srcaddr; - r |= (channel[i].srcbank << 16); + r = (channel[i].srcbank << 16) | (channel[i].srcaddr); + + if(channel[i].fixedxfer == false) { + channel[i].srcaddr += channel[i].incmode; + } + return r; } @@ -15,10 +30,6 @@ uint8 x; mem_bus->write(0x2100 | ((channel[i].destaddr + index) & 0xff), x); - if(channel[i].fixedxfer == false) { - channel[i].srcaddr += channel[i].incmode; - } - add_cycles(8); channel[i].xfersize--; } @@ -28,10 +39,6 @@ uint8 x; x = mem_bus->read(0x2100 | ((channel[i].destaddr + index) & 0xff)); mem_bus->write(dma_addr(i), x); - if(channel[i].fixedxfer == false) { - channel[i].srcaddr += channel[i].incmode; - } - add_cycles(8); channel[i].xfersize--; } @@ -51,7 +58,8 @@ int i; //first byte transferred? if(channel[i].read_index == 0) { - sdd1->dma_begin(i, dma_addr(i), channel[i].xfersize); + sdd1->dma_begin(i, (channel[i].srcbank << 16) | (channel[i].srcaddr), + channel[i].xfersize); } switch(channel[i].xfermode) { @@ -66,6 +74,7 @@ int i; } channel[i].read_index++; + dma_add_cycles(8); if(channel[i].xfersize == 0) { channel[i].active = false; @@ -78,22 +87,15 @@ int i; } uint32 bCPU::hdma_addr(uint8 i) { -uint32 r; - r = channel[i].hdma_addr; - r |= (channel[i].srcbank << 16); - channel[i].hdma_addr++; - return r; + return (channel[i].srcbank << 16) | (channel[i].hdma_addr++); } uint32 bCPU::hdma_iaddr(uint8 i) { -uint32 r; - r = channel[i].hdma_iaddr; - r |= (channel[i].hdma_ibank << 16); - channel[i].hdma_iaddr++; - return r; + return (channel[i].hdma_ibank << 16) | (channel[i].hdma_iaddr++); } -void bCPU::hdma_write(uint8 i, uint8 l, uint8 x) { +uint16 bCPU::hdma_mmio(uint8 i) { +uint8 l = channel[i].read_index; uint16 index; switch(channel[i].xfermode) { case 0:index = 0; break; //0 @@ -106,90 +108,118 @@ uint16 index; case 7:index = (l >> 1) & 1;break; //0,0,1,1 [3] } - index = 0x2100 | ((channel[i].destaddr + index) & 0xff); - mem_bus->write(index, x); + return (0x2100 | ((channel[i].destaddr + index) & 0xff)); +} + +uint8 bCPU::hdma_read(uint8 i) { + if(channel[i].direction == DMA_MMIOTOCPU) { + return mem_bus->read(hdma_mmio(i)); + } else if(!channel[i].hdma_indirect) { + return mem_bus->read(hdma_addr(i)); + } else { + return mem_bus->read(hdma_iaddr(i)); + } +} + +void bCPU::hdma_write(uint8 i, uint8 x) { + if(channel[i].direction == DMA_CPUTOMMIO) { + mem_bus->write(hdma_mmio(i), x); + } else if(!channel[i].hdma_indirect) { + mem_bus->write(hdma_addr(i), x); + } else { + mem_bus->write(hdma_iaddr(i), x); + } + + add_cycles(8); + hdma_add_cycles(8); +} + +void bCPU::hdma_update(uint8 i) { + channel[i].hdma_line_counter = mem_bus->read(hdma_addr(i)); + add_cycles(8); + hdma_add_cycles(8); + + if(channel[i].hdma_indirect) { + channel[i].hdma_iaddr = mem_bus->read(hdma_addr(i)) << 8; + add_cycles(8); + hdma_add_cycles(8); + } + + if(channel[i].hdma_line_counter == 0) { + channel[i].hdma_active = false; + channel[i].hdma_do_transfer = false; + return; + } + + channel[i].hdma_do_transfer = true; + + if(channel[i].hdma_indirect) { + channel[i].hdma_iaddr >>= 8; + channel[i].hdma_iaddr |= mem_bus->read(hdma_addr(i)) << 8; + add_cycles(8); + hdma_add_cycles(8); + } } void bCPU::hdma_run() { -int l, xferlen; -uint8 x, active_channels = 0; static uint8 hdma_xferlen[8] = { 1, 2, 2, 4, 4, 4, 2, 4 }; for(int i=0;i<8;i++) { - if(channel[i].hdma_active == false)continue; + if(!channel[i].hdma_enabled || !channel[i].hdma_active)continue; - add_cycles(8); - active_channels++; - - if(channel[i].hdma_line_counter == 0) { - channel[i].hdma_line_counter = mem_bus->read(hdma_addr(i)); - - if(channel[i].hdma_line_counter == 0) { - channel[i].hdma_active = false; - continue; - } - - if(channel[i].hdma_line_counter > 0x80) { - channel[i].hdma_repeat = true; - channel[i].hdma_line_counter -= 0x80; - } else { - channel[i].hdma_repeat = false; - } - - channel[i].hdma_first_line = true; - - if(channel[i].hdma_indirect == true) { - channel[i].hdma_iaddr = mem_bus->read(hdma_addr(i)); - channel[i].hdma_iaddr |= mem_bus->read(hdma_addr(i)) << 8; - add_cycles(16); + if(channel[i].hdma_do_transfer) { + int xferlen = hdma_xferlen[channel[i].xfermode]; + for(channel[i].read_index=0;channel[i].read_indexread(hdma_addr(i)); - } else { - x = mem_bus->read(hdma_iaddr(i)); - } - - hdma_write(i, l, x); - add_cycles(8); + channel[i].hdma_do_transfer = !!(channel[i].hdma_line_counter & 0x80); + if((channel[i].hdma_line_counter & 0x7f) == 0) { + hdma_update(i); } } - - if(active_channels != 0) { - add_cycles(18); - } } -void bCPU::hdma_initialize() { -uint8 active_channels = 0; +uint8 bCPU::hdma_enabled_channels() { +int r = 0; for(int i=0;i<8;i++) { - //does this happen when $420c channel bit is clear? - channel[i].hdma_addr = channel[i].srcaddr; - channel[i].hdma_line_counter = 0x00; + if(channel[i].hdma_enabled)r++; + } + return r; +} - if(channel[i].hdma_enabled == false) { - channel[i].hdma_active = false; - continue; - } +uint8 bCPU::hdma_active_channels() { +int r = 0; + for(int i=0;i<8;i++) { + if(channel[i].hdma_enabled && channel[i].hdma_active)r++; + } + return r; +} - channel[i].hdma_active = true; - active_channels++; +/* hdmainit_activate() + * hdma_activate() + * + * Functions are called by CPU timing routine + * when an HDMA event (init or run) occurs. + */ - if(channel[i].hdma_indirect == false) { - add_cycles(8); - } else { - add_cycles(24); - } +void bCPU::hdmainit_activate() { + for(int i=0;i<8;i++) { + channel[i].hdma_active = false; + channel[i].hdma_do_transfer = false; } - if(active_channels != 0) { - add_cycles(18); + if(hdma_enabled_channels() != 0) { + status.hdma_state = HDMASTATE_IDMASYNC; + run_state.hdma = true; + } +} + +void bCPU::hdma_activate() { + if(hdma_active_channels() != 0) { + status.hdma_state = HDMASTATE_DMASYNC; + run_state.hdma = true; } } @@ -206,15 +236,15 @@ void bCPU::dma_reset() { channel[i].xfermode = 0; channel[i].destaddr = 0; channel[i].srcaddr = 0; - channel[i].xfersize = 0; + channel[i].xfersize = 0x0000; + //xfersize and hdma_iaddr are of union { uint16 }; + //channel[i].hdma_iaddr = 0x0000; channel[i].hdma_ibank = 0; channel[i].hdma_addr = 0x0000; channel[i].hdma_line_counter = 0x00; channel[i].hdma_unknown = 0x00; channel[i].hdma_active = false; - channel[i].hdma_first_line = false; - channel[i].hdma_repeat = false; - channel[i].hdma_iaddr = 0x0000; + channel[i].hdma_do_transfer = false; } } diff --git a/src/cpu/bcpu/bcpu_exec.cpp b/src/cpu/bcpu/bcpu_exec.cpp index 0cef3fae..8a242245 100644 --- a/src/cpu/bcpu/bcpu_exec.cpp +++ b/src/cpu/bcpu/bcpu_exec.cpp @@ -1,51 +1,145 @@ void bCPU::last_cycle() { - status.is_last_cycle = true; -} - -void bCPU::last_cycle_exec() { - status.is_last_cycle = false; - time.nmi_pending = nmi_test(); time.irq_pending = irq_test(); } -void bCPU::cycle_edge() { -int c, n, z; - if(status.dma_state != DMASTATE_STOP) { +void bCPU::pre_exec_cycle() { + if(!run_state.dma && !run_state.hdma)return; + +int c, z; + if(run_state.hdma) { + switch(status.hdma_state) { + case HDMASTATE_ICPUSYNC: + case HDMASTATE_CPUSYNC: + c = status.cycle_count; + z = c - (status.hdma_cycle_count % c); + if(!z)z = c; + add_cycles(z); + run_state.hdma = false; + break; + } + } + + if(run_state.dma) { switch(status.dma_state) { - case DMASTATE_DMASYNC: - status.dma_state = DMASTATE_DMASYNC2; - break; - case DMASTATE_DMASYNC2: - n = 8 - dma_counter() + 8; - add_cycles(n); - status.dma_cycle_count = n; - for(z=0;z<8;z++) { - if(channel[z].active == false)continue; - add_cycles(8); - status.dma_cycle_count += 8; - } - status.cpu_state = CPUSTATE_DMA; - status.dma_state = DMASTATE_RUN; - break; - case DMASTATE_RUN: - status.dma_cycle_count += 8; - break; case DMASTATE_CPUSYNC: - status.cpu_state = CPUSTATE_RUN; - status.dma_state = DMASTATE_CPUSYNC2; - break; - case DMASTATE_CPUSYNC2: c = status.cycle_count; z = c - (status.dma_cycle_count % c); if(!z)z = c; add_cycles(z); - status.dma_state = DMASTATE_STOP; + run_state.dma = false; break; } } } +void bCPU::exec_hdma() { +int n; +static int z; + switch(status.hdma_state) { + case HDMASTATE_IDMASYNC: + status.hdma_cycle_count = 0; + z = 0; + if(!run_state.dma) { + exec_cycle(); + status.hdma_state = HDMASTATE_IDMASYNC2; + } else { + status.hdma_state = HDMASTATE_IDMASYNC3; + } + break; + case HDMASTATE_IDMASYNC2: + n = 8 - dma_counter() + 8; + add_cycles(n); + status.hdma_cycle_count += n; + status.hdma_state = HDMASTATE_IDMASYNC3; + break; + case HDMASTATE_IDMASYNC3: + channel[z].hdma_active = channel[z].hdma_enabled; + if(channel[z].hdma_enabled) { + channel[z].hdma_addr = channel[z].srcaddr; + hdma_update(z); //updates status.hdma_cycle_count + } + if(++z < 8)break; + if(!run_state.dma) { + status.hdma_state = HDMASTATE_ICPUSYNC; + } else { + run_state.hdma = false; + } + break; + case HDMASTATE_ICPUSYNC: + exec_cycle(); + break; + + case HDMASTATE_DMASYNC: + status.hdma_cycle_count = 0; + z = 0; + if(!run_state.dma) { + exec_cycle(); + status.hdma_state = HDMASTATE_DMASYNC2; + } else { + status.hdma_state = HDMASTATE_DMASYNC3; + } + break; + case HDMASTATE_DMASYNC2: + n = 8 - dma_counter() + 8; + add_cycles(n); + status.hdma_cycle_count += n; + status.hdma_state = HDMASTATE_DMASYNC3; + break; + case HDMASTATE_DMASYNC3: + if(channel[z].hdma_active) { + add_cycles(8); + status.hdma_cycle_count += 8; + } + if(++z < 8)break; + status.hdma_state = HDMASTATE_RUN; + break; + case HDMASTATE_RUN: + hdma_run(); //updates status.hdma_cycle_count + if(!run_state.dma) { + status.hdma_state = HDMASTATE_CPUSYNC; + } else { + run_state.hdma = false; + } + break; + case HDMASTATE_CPUSYNC: + exec_cycle(); + break; + } +} + +void bCPU::exec_dma() { +int n; +static int z; + switch(status.dma_state) { + case DMASTATE_DMASYNC: + exec_cycle(); + status.dma_state = DMASTATE_DMASYNC2; + break; + case DMASTATE_DMASYNC2: + n = 8 - dma_counter() + 8; + add_cycles(n); + status.dma_cycle_count = n; + z = 0; + status.dma_state = DMASTATE_DMASYNC3; + break; + case DMASTATE_DMASYNC3: + if(channel[z].active == true) { + add_cycles(8); + status.dma_cycle_count += 8; + } + if(++z < 8)break; + status.dma_state = DMASTATE_RUN; + break; + case DMASTATE_RUN: + dma_run(); //updates status.dma_cycle_count + break; + case DMASTATE_CPUSYNC: + exec_cycle(); + break; + } +} + void bCPU::exec_cycle() { //on first cycle? if(status.cycle_pos == 0) { diff --git a/src/cpu/bcpu/bcpu_int.cpp b/src/cpu/bcpu/bcpu_int.cpp index 5a4eb1a8..9f47c88d 100644 --- a/src/cpu/bcpu/bcpu_int.cpp +++ b/src/cpu/bcpu/bcpu_int.cpp @@ -1,43 +1,43 @@ /* [IRQ cycles] - [1] pbr,pc ; opcode - [2] pbr,pc ; io - [3] 0,s ; pbr - [4] 0,s-1 ; pch - [5] 0,s-2 ; pcl - [6] 0,s-3 ; p - [7] 0,va ; aavl - [8] 0,va+1 ; aavh + [0] pbr,pc ; opcode + [1] pbr,pc ; io + [2] 0,s ; pbr + [3] 0,s-1 ; pch + [4] 0,s-2 ; pcl + [5] 0,s-3 ; p + [6] 0,va ; aavl + [7] 0,va+1 ; aavh */ -void bCPU::irq(uint16 addr) { +void bCPU::irq_run() { //WDC documentation is incorrect, first cycle //is a memory read fetch from PBR:PC - add_cycles(mem_bus->speed(regs.pc.d)); - add_cycles(6); - stack_write(regs.pc.b); - stack_write(regs.pc.h); - stack_write(regs.pc.l); - stack_write(regs.p); - rd.l = op_read(OPMODE_ADDR, addr); - rd.h = op_read(OPMODE_ADDR, addr + 1); - - regs.pc.b = 0x00; - regs.pc.w = rd.w; - regs.p.i = 1; - regs.p.d = 0; - -//let debugger know the new IRQ opcode address - snes->notify(SNES::CPU_EXEC_OPCODE_END); + switch(status.cycle_pos++) { + case 0: add_cycles(mem_bus->speed(regs.pc.d)); break; + case 1: add_cycles(6); break; + case 2: stack_write(regs.pc.b); break; + case 3: stack_write(regs.pc.h); break; + case 4: stack_write(regs.pc.l); break; + case 5: stack_write(regs.p); break; + case 6: rd.l = op_read(OPMODE_ADDR, aa.w); break; + case 7: rd.h = op_read(OPMODE_ADDR, aa.w + 1); + regs.pc.b = 0x00; + regs.pc.w = rd.w; + regs.p.i = 1; + regs.p.d = 0; + //let debugger know the new IRQ opcode address + snes->notify(SNES::CPU_EXEC_OPCODE_END); + status.cycle_pos = 0; + run_state.irq = false; + break; + } } bool bCPU::nmi_test() { if(time.nmi_transition == 0)return false; time.nmi_transition = 0; - if(status.cpu_state == CPUSTATE_WAI) { - status.cpu_state = CPUSTATE_RUN; - } - + run_state.wai = false; return true; } @@ -61,10 +61,7 @@ bool bCPU::irq_test() { _true: time.irq_transition = 0; - if(status.cpu_state == CPUSTATE_WAI) { - status.cpu_state = CPUSTATE_RUN; - } - + run_state.wai = false; if(regs.p.i)return false; return true; } diff --git a/src/cpu/bcpu/bcpu_mmio.cpp b/src/cpu/bcpu/bcpu_mmio.cpp index fa23536a..fbcb0349 100644 --- a/src/cpu/bcpu/bcpu_mmio.cpp +++ b/src/cpu/bcpu/bcpu_mmio.cpp @@ -2,9 +2,11 @@ void bCPU::mmio_reset() { //$2181-$2183 status.wram_addr = 0x000000; -//$4016 +//$4016-$4017 status.joypad1_strobe_value = 0x00; + status.joypad2_strobe_value = 0x00; status.joypad1_read_pos = 0; + status.joypad2_read_pos = 0; //$4200 status.nmi_enabled = false; @@ -91,6 +93,28 @@ uint8 r; r = regs.mdr & 0xe0; r |= 0x1c; + if(status.joypad2_strobe_value == 1) { + r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_B); + } else { + switch(status.joypad2_read_pos) { + case 0:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_B); break; + case 1:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_Y); break; + case 2:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_SELECT);break; + case 3:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_START); break; + case 4:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_UP); break; + case 5:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_DOWN); break; + case 6:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_LEFT); break; + case 7:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_RIGHT); break; + case 8:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_A); break; + case 9:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_X); break; + case 10:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_L); break; + case 11:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_R); break; + case 16:r |= 1;break; //joypad connected bit + default:r |= 1;break; //after 16th read, all subsequent reads return 1 + } + if(++status.joypad2_read_pos > 17)status.joypad2_read_pos = 17; + } + return r; } @@ -206,6 +230,36 @@ uint16 v = vcounter(); return r; } +//JOY2L +uint8 bCPU::mmio_r421a() { +uint8 r = 0x00; +uint16 v = vcounter(); + if(status.auto_joypad_poll == false)return 0x00; //can't read joypad if auto polling not enabled +//if(v >= 225 && v <= 227)return 0x00; //can't read joypad while SNES is polling input + r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_A) << 7; + r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_X) << 6; + r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_L) << 5; + r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_R) << 4; + return r; +} + +//JOY2H +uint8 bCPU::mmio_r421b() { +uint8 r = 0x00; +uint16 v = vcounter(); + if(status.auto_joypad_poll == false)return 0x00; //can't read joypad if auto polling not enabled +//if(v >= 225 && v <= 227)return 0x00; //can't read joypad while SNES is polling input + r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_B) << 7; + r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_Y) << 6; + r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_SELECT) << 5; + r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_START) << 4; + r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_UP) << 3; + r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_DOWN) << 2; + r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_LEFT) << 1; + r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_RIGHT); + return r; +} + //DMAPx uint8 bCPU::mmio_r43x0(uint8 i) { return channel[i].dmap; @@ -310,9 +364,9 @@ uint i; case 0x4217:return cpu->mmio_r4217(); //RDMPYH case 0x4218:return cpu->mmio_r4218(); //JOY1L case 0x4219:return cpu->mmio_r4219(); //JOY1H - case 0x421a:case 0x421b:case 0x421c:case 0x421d:case 0x421e:case 0x421f:return 0x00; -case 0x4000:dprintf("* 4000 read at %3d,%4d <%d>", cpu->time.v, cpu->time.hc, cpu->status.cycle_count);break; -case 0x4200:dprintf("* 4200 read at %3d,%4d", cpu->time.v, cpu->time.hc);break; + case 0x421a:return cpu->mmio_r421a(); //JOY2L + case 0x421b:return cpu->mmio_r421b(); //JOY2H + case 0x421c:case 0x421d:case 0x421e:case 0x421f:return 0x00; } return cpu->regs.mdr; @@ -345,13 +399,22 @@ void bCPU::mmio_w2183(uint8 value) { //JOYSER0 void bCPU::mmio_w4016(uint8 value) { - status.joypad1_strobe_value = (value & 1); + status.joypad1_strobe_value = !!(value & 1); if(value == 1) { - snes->poll_input(); + snes->poll_input(SNES::DEV_JOYPAD1); status.joypad1_read_pos = 0; } } +//JOYSER1 +void bCPU::mmio_w4017(uint8 value) { + status.joypad2_strobe_value = !!(value & 1); + if(value == 1) { + snes->poll_input(SNES::DEV_JOYPAD2); + status.joypad2_read_pos = 0; + } +} + //NMITIMEN void bCPU::mmio_w4200(uint8 value) { status.nmi_enabled = !!(value & 0x80); @@ -438,6 +501,7 @@ void bCPU::mmio_w420a(uint8 value) { void bCPU::mmio_w420b(uint8 value) { int len; if(value != 0x00) { + run_state.dma = true; status.dma_state = DMASTATE_DMASYNC; } @@ -566,6 +630,7 @@ uint8 i; case 0x2182:cpu->mmio_w2182(value);return; //WMADDM case 0x2183:cpu->mmio_w2183(value);return; //WMADDH case 0x4016:cpu->mmio_w4016(value);return; //JOYSER0 + case 0x4017:cpu->mmio_w4017(value);return; //JOYSER1 case 0x4200:cpu->mmio_w4200(value);return; //NMITIMEN case 0x4201:cpu->mmio_w4201(value);return; //WRIO case 0x4202:cpu->mmio_w4202(value);return; //WRMPYA @@ -580,7 +645,6 @@ uint8 i; case 0x420b:cpu->mmio_w420b(value);return; //DMAEN case 0x420c:cpu->mmio_w420c(value);return; //HDMAEN case 0x420d:cpu->mmio_w420d(value);return; //MEMSEL -case 0x4000:dprintf("* 4000 write at %3d,%4d", cpu->time.v, cpu->time.hc);break; } } diff --git a/src/cpu/bcpu/bcpu_op_misc.cpp b/src/cpu/bcpu/bcpu_op_misc.cpp index ff08ae50..0de20b45 100644 --- a/src/cpu/bcpu/bcpu_op_misc.cpp +++ b/src/cpu/bcpu/bcpu_op_misc.cpp @@ -164,7 +164,7 @@ void bCPU::op_stp() { switch(status.cycle_pos++) { case 1: cpu_io(); - status.cpu_state = CPUSTATE_STP; + run_state.stp = true; break; case 2: last_cycle(); @@ -179,12 +179,13 @@ void bCPU::op_wai() { switch(status.cycle_pos++) { case 1: cpu_io(); - status.cpu_state = CPUSTATE_WAI; + run_state.wai = true; break; case 2: last_cycle(); cpu_io(); - if(status.cpu_state == CPUSTATE_WAI) { + if(run_state.wai) { + //this can be cleared within last_cycle() regs.pc.w--; } status.cycle_pos = 0; diff --git a/src/cpu/bcpu/bcpu_timing.cpp b/src/cpu/bcpu/bcpu_timing.cpp index e25535c3..93eed40e 100644 --- a/src/cpu/bcpu/bcpu_timing.cpp +++ b/src/cpu/bcpu/bcpu_timing.cpp @@ -207,7 +207,7 @@ void bCPU::inc_vcounter() { } } - time.dma_counter = time.line_cycles & 6; + time.dma_counter += time.line_cycles; if(time.v == 240 && time.interlace == false && time.interlace_field == 1) { time.line_cycles = 1360; } else { @@ -259,21 +259,14 @@ void bCPU::add_cycles(int cycles) { scanline(); ppu->scanline(); -// ppu->render_scanline(); snes->scanline(); time.line_rendered = false; } - if(time.line_rendered == false) { - //rendering should start at H=22, but due to inaccurate - //timing, and due to using a scanline-based renderer, use - //a higher value to allow more games to run properly... - //H=48 fixes off-by-one HDMA effects with FF6's battles - if(time.hc + cycles >= (48 * 4)) { - cycles = (time.hc + cycles) - (48 * 4); - time.hc = (48 * 4); - time.line_rendered = true; - ppu->render_scanline(); + if(time.hdmainit_triggered == false) { + if(time.hc + cycles >= time.hdmainit_trigger_pos || time.v) { + time.hdmainit_triggered = true; + hdmainit_activate(); } } @@ -296,14 +289,22 @@ void bCPU::add_cycles(int cycles) { } } - if(status.hdma_triggered == false) { - //vcounter range verified on hardware + if(time.line_rendered == false) { + //rendering should start at H=18 (+256=274), but since the + //current PPU emulation renders the entire scanline at once, + //PPU register changes mid-scanline do not show up. + //therefore, wait a few dots before rendering the scanline + if(time.hc + cycles >= (48 * 4)) { + time.line_rendered = true; + ppu->render_scanline(); + } + } + + if(time.hdma_triggered == false) { if(time.v <= (overscan() ? 239 : 224)) { - if(time.hc + cycles >= 1112) { //278 * 4 = 1112 - cycles = (time.hc + cycles) - 1112; - time.hc = 1112; - status.hdma_triggered = true; - hdma_run(); + if(time.hc + cycles >= 1106) { + time.hdma_triggered = true; + hdma_activate(); } } } @@ -312,11 +313,8 @@ void bCPU::add_cycles(int cycles) { } void bCPU::time_reset() { -//initial latch values for $213c/$213d -//[x]0035 : [y]0000 (53.0 -> 212) [lda $2137] -//[x]0038 : [y]0000 (56.5 -> 226) [nop : lda $2137] time.v = 0; - time.hc = 186; + time.hc = 0; //upon SNES reset, start at scanline 0 non-interlace time.interlace = false; @@ -329,6 +327,12 @@ void bCPU::time_reset() { time.dma_counter = 0; +//set at V=0,H=0 + time.hdmainit_trigger_pos = 0; + time.hdmainit_triggered = true; + + time.hdma_triggered = false; + time.nmi_pending = false; time.irq_pending = false; time.nmi_line = time.nmi_read = 1; diff --git a/src/cpu/bcpu/bcpu_timing.h b/src/cpu/bcpu/bcpu_timing.h index 42a15158..bb70a3de 100644 --- a/src/cpu/bcpu/bcpu_timing.h +++ b/src/cpu/bcpu/bcpu_timing.h @@ -11,6 +11,11 @@ struct { uint8 dma_counter; + uint16 hdmainit_trigger_pos; + bool hdmainit_triggered; + + bool hdma_triggered; + uint16 region_scanlines; //nmi_pending, irq_pending are used by last_cycle() diff --git a/src/cpu/bcpu/op_misc.b b/src/cpu/bcpu/op_misc.b index 847c832e..7e8e07e3 100644 --- a/src/cpu/bcpu/op_misc.b +++ b/src/cpu/bcpu/op_misc.b @@ -53,7 +53,7 @@ cop(0x02, 0xfff4, 0xfff5, 0xffe4, 0xffe5) { stp(0xdb) { 1:cpu_io(); - status.cpu_state = CPUSTATE_STP; + run_state.stp = true; 2:last_cycle(); cpu_io(); regs.pc.w--; @@ -61,10 +61,11 @@ stp(0xdb) { wai(0xcb) { 1:cpu_io(); - status.cpu_state = CPUSTATE_WAI; + run_state.wai = true; 2:last_cycle(); cpu_io(); - if(status.cpu_state == CPUSTATE_WAI) { + if(run_state.wai) { + //this can be cleared within last_cycle() regs.pc.w--; } } diff --git a/src/cpu/cpu.cpp b/src/cpu/cpu.cpp index 2bec94aa..cf6b70fa 100644 --- a/src/cpu/cpu.cpp +++ b/src/cpu/cpu.cpp @@ -2,6 +2,6 @@ #include "dcpu.cpp" CPU::CPU() { - cpu_version = 2; + cpu_version = 1; mmio = &mmio_unmapped; } diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h index f249338c..77e60098 100644 --- a/src/cpu/cpu.h +++ b/src/cpu/cpu.h @@ -15,6 +15,7 @@ uint8 cpu_version; virtual bool interlace() = 0; virtual bool interlace_field() = 0; virtual bool overscan() = 0; + virtual uint16 region_scanlines() = 0; virtual void set_interlace(bool r) = 0; virtual void set_overscan (bool r) = 0; diff --git a/src/cpu/cpuregs.h b/src/cpu/cpuregs.h index 7fa4c078..8d6447bd 100644 --- a/src/cpu/cpuregs.h +++ b/src/cpu/cpuregs.h @@ -13,7 +13,7 @@ private: inline bool operator ^= (bool i) { if(i)_b ^= B; return (_b & B); } }; public: - union { +union { uint8 _b; bit<0x80> n; bit<0x40> v; @@ -23,7 +23,7 @@ public: bit<0x04> i; bit<0x02> z; bit<0x01> c; - }; +}; CPURegFlags() { _b = 0; } inline operator uint8() { return _b; } @@ -35,10 +35,14 @@ public: class CPUReg16 { public: - union { +union { uint16 w; +#ifdef ARCH_LSB struct { uint8 l, h; }; - }; +#else + struct { uint8 h, l; }; +#endif; +}; CPUReg16() { w = 0; } inline operator uint16() { return w; } @@ -56,11 +60,16 @@ public: class CPUReg24 { public: - union { - uint16 w; +union { uint32 d; - struct { uint8 l, h, b; }; - }; +#ifdef ARCH_LSB + struct { uint16 w, null_w; }; + struct { uint8 l, h, b, null_b; }; +#else + struct { uint16 null_w, w; }; + struct { uint8 null_b, b, h, l; }; +#endif +}; CPUReg24() { d = 0; } inline operator uint32() { return (d & 0xffffff); } diff --git a/src/dsp/bdsp/bdsp.cpp b/src/dsp/bdsp/bdsp.cpp index abd1e944..d973ce81 100644 --- a/src/dsp/bdsp/bdsp.cpp +++ b/src/dsp/bdsp/bdsp.cpp @@ -145,7 +145,7 @@ int i, v, n; case 0x4c: status.KON = data; - status.kon = data; +// status.kon = data; status.key_flag = true; break; @@ -221,7 +221,7 @@ int v; status.KOFF = 0x00; status.FLG |= 0xe0; - status.kon = 0x00; +//status.kon = 0x00; status.key_flag = false; status.noise_ctr = 0; @@ -290,17 +290,20 @@ int32 fir_samplel, fir_sampler; if(!(dsp_counter++ & 1) && status.key_flag) { for(v=0;v<8;v++) { + uint8 mask = 1 << v; if(status.soft_reset()) { if(voice[v].env_state != SILENCE) { voice[v].env_state = SILENCE; voice[v].AdjustEnvelope(); } - } else if(status.KOFF & (1 << v)) { + } else if(status.KOFF & mask) { if(voice[v].env_state != SILENCE && voice[v].env_state != RELEASE) { voice[v].env_state = RELEASE; voice[v].AdjustEnvelope(); } - } else if(status.kon & (1 << v)) { + } else if(status.KON & mask) { //status.kon + status.KON &= ~mask; //new code + status.ENDX &= ~mask; //new code voice[v].brr_ptr = read_16((status.DIR << 8) + (voice[v].SRCN << 2)); voice[v].brr_index = -9; voice[v].brr_looped = false; @@ -313,8 +316,8 @@ int32 fir_samplel, fir_sampler; voice[v].AdjustEnvelope(); } } - status.ENDX &= ~status.kon; - status.kon = 0; +// status.ENDX &= ~status.kon; +// status.kon = 0; status.key_flag = false; } diff --git a/src/dsp/bdsp/bdsp.h b/src/dsp/bdsp/bdsp.h index 40eb6849..dfed7bac 100644 --- a/src/dsp/bdsp/bdsp.h +++ b/src/dsp/bdsp/bdsp.h @@ -63,7 +63,7 @@ struct Status { int8 FIR[8]; //internal variables - uint8 kon; +//uint8 kon; bool key_flag; int16 noise_ctr, noise_rate; diff --git a/src/interface.h b/src/interface.h index 829a5f6f..e25ba73f 100644 --- a/src/interface.h +++ b/src/interface.h @@ -1,4 +1,4 @@ -#define BSNES_VERSION "0.012" +#define BSNES_VERSION "0.013" #define BSNES_TITLE "bsnes v" BSNES_VERSION #include "reader/reader.h" diff --git a/src/ppu/bppu/bppu.cpp b/src/ppu/bppu/bppu.cpp index cc276a06..cc0f86ee 100644 --- a/src/ppu/bppu/bppu.cpp +++ b/src/ppu/bppu/bppu.cpp @@ -32,35 +32,18 @@ void bPPU::scanline() { } void bPPU::render_scanline() { -//only allow frameskip setting to ignore actual rendering; not RTO, etc. - if(settings.frameskip_pos != 0)return; + if(status.render_output == false)return; if(_y > 0 && _y < (cpu->overscan() ? 239 : 224)) { render_line(); } } -bool bPPU::render_frame() { return (settings.frameskip_pos == 0); } - void bPPU::frame() { - if(settings.frameskip_changed == true) { - settings.frameskip_changed = false; - settings.frameskip_pos = 0; - } else { - settings.frameskip_pos++; - settings.frameskip_pos %= (settings.frameskip + 1); - } - - if(settings.frameskip_pos != 0)return; - + PPU::frame(); snes->notify(SNES::RENDER_FRAME); } -void bPPU::set_frameskip(int fs) { - settings.frameskip = fs; - settings.frameskip_changed = true; -} - void bPPU::power() { memset(vram, 0, 65536); memset(oam, 0, 544); @@ -162,30 +145,33 @@ void bPPU::reset() { regs.cgram_addr = 0x0000; //$2123-$2125 - regs.bg_window1_enabled[BG1] = false; - regs.bg_window1_enabled[BG2] = false; - regs.bg_window1_enabled[BG3] = false; - regs.bg_window1_enabled[BG4] = false; - regs.bg_window1_enabled[OAM] = false; - regs.bg_window1_invert [BG1] = false; - regs.bg_window1_invert [BG2] = false; - regs.bg_window1_invert [BG3] = false; - regs.bg_window1_invert [BG4] = false; - regs.bg_window1_invert [OAM] = false; - regs.bg_window2_enabled[BG1] = false; - regs.bg_window2_enabled[BG2] = false; - regs.bg_window2_enabled[BG3] = false; - regs.bg_window2_enabled[BG4] = false; - regs.bg_window2_enabled[OAM] = false; - regs.bg_window2_invert [BG1] = false; - regs.bg_window2_invert [BG2] = false; - regs.bg_window2_invert [BG3] = false; - regs.bg_window2_invert [BG4] = false; - regs.bg_window2_invert [OAM] = false; - regs.color_window1_enabled = false; - regs.color_window1_invert = false; - regs.color_window2_enabled = false; - regs.color_window2_invert = false; + regs.window1_enabled[BG1] = false; + regs.window1_enabled[BG2] = false; + regs.window1_enabled[BG3] = false; + regs.window1_enabled[BG4] = false; + regs.window1_enabled[OAM] = false; + regs.window1_enabled[COL] = false; + + regs.window1_invert [BG1] = false; + regs.window1_invert [BG2] = false; + regs.window1_invert [BG3] = false; + regs.window1_invert [BG4] = false; + regs.window1_invert [OAM] = false; + regs.window1_invert [COL] = false; + + regs.window2_enabled[BG1] = false; + regs.window2_enabled[BG2] = false; + regs.window2_enabled[BG3] = false; + regs.window2_enabled[BG4] = false; + regs.window2_enabled[OAM] = false; + regs.window2_enabled[COL] = false; + + regs.window2_invert [BG1] = false; + regs.window2_invert [BG2] = false; + regs.window2_invert [BG3] = false; + regs.window2_invert [BG4] = false; + regs.window2_invert [OAM] = false; + regs.window2_invert [COL] = false; //$2126-$2129 regs.window1_left = 0; @@ -194,12 +180,12 @@ void bPPU::reset() { regs.window2_right = 0; //$212a-$212b - regs.bg_window_mask[BG1] = 0; - regs.bg_window_mask[BG2] = 0; - regs.bg_window_mask[BG3] = 0; - regs.bg_window_mask[BG4] = 0; - regs.bg_window_mask[OAM] = 0; - regs.color_window_mask = 0; + regs.window_mask[BG1] = 0; + regs.window_mask[BG2] = 0; + regs.window_mask[BG3] = 0; + regs.window_mask[BG4] = 0; + regs.window_mask[OAM] = 0; + regs.window_mask[COL] = 0; //$212c-$212d regs.bg_enabled[BG1] = false; @@ -214,16 +200,16 @@ void bPPU::reset() { regs.bgsub_enabled[OAM] = false; //$212e-$212f - regs.bg_window_enabled[BG1] = false; - regs.bg_window_enabled[BG2] = false; - regs.bg_window_enabled[BG3] = false; - regs.bg_window_enabled[BG4] = false; - regs.bg_window_enabled[OAM] = false; - regs.bgsub_window_enabled[BG1] = false; - regs.bgsub_window_enabled[BG2] = false; - regs.bgsub_window_enabled[BG3] = false; - regs.bgsub_window_enabled[BG4] = false; - regs.bgsub_window_enabled[OAM] = false; + regs.window_enabled[BG1] = false; + regs.window_enabled[BG2] = false; + regs.window_enabled[BG3] = false; + regs.window_enabled[BG4] = false; + regs.window_enabled[OAM] = false; + regs.sub_window_enabled[BG1] = false; + regs.sub_window_enabled[BG2] = false; + regs.sub_window_enabled[BG3] = false; + regs.sub_window_enabled[BG4] = false; + regs.sub_window_enabled[OAM] = false; //$2130 regs.color_mask = 0; @@ -231,14 +217,14 @@ void bPPU::reset() { regs.addsub_mode = 0; //$2131 - regs.color_mode = 0; - regs.color_halve = false; - regs.bg_color_enabled[BACK] = false; - regs.bg_color_enabled[OAM] = false; - regs.bg_color_enabled[BG4] = false; - regs.bg_color_enabled[BG3] = false; - regs.bg_color_enabled[BG2] = false; - regs.bg_color_enabled[BG1] = false; + regs.color_mode = 0; + regs.color_halve = false; + regs.color_enabled[BACK] = false; + regs.color_enabled[OAM] = false; + regs.color_enabled[BG4] = false; + regs.color_enabled[BG3] = false; + regs.color_enabled[BG2] = false; + regs.color_enabled[BG1] = false; //$2132 regs.color_r = 0x00; @@ -266,8 +252,10 @@ void bPPU::reset() { regs.time_over = false; regs.range_over = false; + _screen_width = 256; //needed for clear_window_cache() update_sprite_list_sizes(); clear_tiledata_cache(); + clear_window_cache(); } uint8 bPPU::vram_read(uint16 addr) { @@ -318,10 +306,6 @@ void bPPU::cgram_write(uint16 addr, uint8 value) { } bPPU::bPPU() { - settings.frameskip = 0; - settings.frameskip_pos = 0; - settings.frameskip_changed = false; - mmio = new bPPUMMIO(this); vram = (uint8*)malloc(65536); diff --git a/src/ppu/bppu/bppu.h b/src/ppu/bppu/bppu.h index c403df54..406fd8a6 100644 --- a/src/ppu/bppu/bppu.h +++ b/src/ppu/bppu/bppu.h @@ -14,7 +14,7 @@ uint8 *vram, *oam, *cgram; uint8 region; enum { NTSC = 0, PAL = 1 }; -enum { BG1 = 0, BG2 = 1, BG3 = 2, BG4 = 3, OAM = 4, BACK = 5 }; +enum { BG1 = 0, BG2 = 1, BG3 = 2, BG4 = 3, OAM = 4, BACK = 5, COL = 5 }; enum { SC_32x32 = 0, SC_32x64 = 1, SC_64x32 = 2, SC_64x64 = 3 }; struct sprite_item { @@ -27,11 +27,6 @@ struct sprite_item { uint8 priority; } sprite_list[128]; -struct { - int32 frameskip, frameskip_pos; - bool frameskip_changed; -} settings; - struct { //open bus support uint8 ppu1_mdr, ppu2_mdr; @@ -95,26 +90,23 @@ struct { uint16 cgram_addr; //$2123-$2125 - bool bg_window1_enabled[5]; - bool bg_window1_invert [5]; - bool bg_window2_enabled[5]; - bool bg_window2_invert [5]; - bool color_window1_enabled, color_window1_invert; - bool color_window2_enabled, color_window2_invert; + 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 bg_window_mask[5]; - uint8 color_window_mask; + uint8 window_mask[6]; //$212c-$212d bool bg_enabled[5], bgsub_enabled[5]; //$212e-$212f - bool bg_window_enabled[5], bgsub_window_enabled[5]; + bool window_enabled[5], sub_window_enabled[5]; //$2130 uint8 color_mask, colorsub_mask; @@ -123,7 +115,7 @@ struct { //$2131 bool color_mode, color_halve; - bool bg_color_enabled[6]; + bool color_enabled[6]; //$2132 uint8 color_r, color_g, color_b; @@ -158,6 +150,8 @@ struct { void update_sprite_list(uint16 addr); void update_sprite_list_sizes(); uint16 get_vram_address(); + bool vram_can_read(); + bool vram_can_write(uint8 &value); void mmio_w2100(uint8 value); //INIDISP void mmio_w2101(uint8 value); //OBSEL @@ -242,8 +236,6 @@ uint16 *mosaic_table[16]; void frame(); void power(); void reset(); - void set_frameskip(int fs); - bool render_frame(); bool scanline_is_hires() { return (regs.bg_mode == 5 || regs.bg_mode == 6); } diff --git a/src/ppu/bppu/bppu_mmio.cpp b/src/ppu/bppu/bppu_mmio.cpp index 916e465a..0bb916bd 100644 --- a/src/ppu/bppu/bppu_mmio.cpp +++ b/src/ppu/bppu/bppu_mmio.cpp @@ -16,6 +16,55 @@ uint16 addr; return (addr << 1); } +bool bPPU::vram_can_read() { + if(regs.display_disabled == true) { + return true; + } + +uint16 v = cpu->vcounter(); +uint16 hc = cpu->hcycles(); +uint16 ls; + if(cpu->interlace() && !cpu->interlace_field()) { + ls = cpu->region_scanlines(); + } else { + ls = cpu->region_scanlines() - 1; + } + + if(v == ls && hc == 1362)return false; + + if(v < (cpu->overscan() ? 239 : 224))return false; + + if(v == (cpu->overscan() ? 239 : 224)) { + if(hc == 1362)return true; + return false; + } + + return true; +} + +bool bPPU::vram_can_write(uint8 &value) { + if(regs.display_disabled == true) { + return true; + } + +uint16 v = cpu->vcounter(); +uint16 hc = cpu->hcycles(); + if(v == 0) { + if(hc <= 4)return true; + if(hc == 6) { value = cpu->regs.mdr; return true; } + return false; + } + + if(v < (cpu->overscan() ? 240 : 225))return false; + + if(v == (cpu->overscan() ? 240 : 225)) { + if(hc <= 4)return false; + return true; + } + + return true; +} + //INIDISP void bPPU::mmio_w2100(uint8 value) { regs.display_disabled = !!(value & 0x80); @@ -68,6 +117,13 @@ void bPPU::mmio_w2105(uint8 value) { regs.bg_tilesize[BG1] = !!(value & 0x10); regs.bg3_priority = !!(value & 0x08); regs.bg_mode = (value & 7); + + window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true; + window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true; + window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true; + window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true; + window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true; + window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true; } //MOSAIC @@ -185,40 +241,54 @@ void bPPU::mmio_w2115(uint8 value) { void bPPU::mmio_w2116(uint8 value) { regs.vram_addr = (regs.vram_addr & 0xff00) | value; uint16 addr = get_vram_address(); - regs.vram_readbuffer = vram_read(addr); - regs.vram_readbuffer |= vram_read(addr + 1) << 8; + if(vram_can_read()) { + regs.vram_readbuffer = vram_read(addr); + regs.vram_readbuffer |= vram_read(addr + 1) << 8; + } else { + regs.vram_readbuffer = 0x0000; + } } //VMADDH void bPPU::mmio_w2117(uint8 value) { regs.vram_addr = (value << 8) | (regs.vram_addr & 0x00ff); uint16 addr = get_vram_address(); - regs.vram_readbuffer = vram_read(addr); - regs.vram_readbuffer |= vram_read(addr + 1) << 8; + if(vram_can_read()) { + regs.vram_readbuffer = vram_read(addr); + regs.vram_readbuffer |= vram_read(addr + 1) << 8; + } else { + regs.vram_readbuffer = 0x0000; + } } //VMDATAL void bPPU::mmio_w2118(uint8 value) { uint16 addr = get_vram_address(); - vram_write(addr, value); + if(vram_can_write(value)) { + vram_write(addr, value); + bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1; + bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1; + bg_tiledata_state[TILE_8BIT][(addr >> 6)] = 1; + } + if(regs.vram_incmode == 0) { regs.vram_addr += regs.vram_incsize; } - bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1; - bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1; - bg_tiledata_state[TILE_8BIT][(addr >> 6)] = 1; } //VMDATAH void bPPU::mmio_w2119(uint8 value) { uint16 addr = get_vram_address() + 1; - vram_write(addr, value); + if(vram_can_write(value)) { + vram_write(addr, value); + bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1; + bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1; + bg_tiledata_state[TILE_8BIT][(addr >> 6)] = 1; + } + if(regs.vram_incmode == 1) { regs.vram_addr += regs.vram_incsize; } - bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1; - bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1; - bg_tiledata_state[TILE_8BIT][(addr >> 6)] = 1; } //M7SEL @@ -284,72 +354,117 @@ void bPPU::mmio_w2122(uint8 value) { //W12SEL void bPPU::mmio_w2123(uint8 value) { - regs.bg_window2_enabled[BG2] = !!(value & 0x80); - regs.bg_window2_invert [BG2] = !!(value & 0x40); - regs.bg_window1_enabled[BG2] = !!(value & 0x20); - regs.bg_window1_invert [BG2] = !!(value & 0x10); - regs.bg_window2_enabled[BG1] = !!(value & 0x08); - regs.bg_window2_invert [BG1] = !!(value & 0x04); - regs.bg_window1_enabled[BG1] = !!(value & 0x02); - regs.bg_window1_invert [BG1] = !!(value & 0x01); + regs.window2_enabled[BG2] = !!(value & 0x80); + regs.window2_invert [BG2] = !!(value & 0x40); + regs.window1_enabled[BG2] = !!(value & 0x20); + regs.window1_invert [BG2] = !!(value & 0x10); + regs.window2_enabled[BG1] = !!(value & 0x08); + regs.window2_invert [BG1] = !!(value & 0x04); + regs.window1_enabled[BG1] = !!(value & 0x02); + regs.window1_invert [BG1] = !!(value & 0x01); + + window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true; + window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true; } //W34SEL void bPPU::mmio_w2124(uint8 value) { - regs.bg_window2_enabled[BG4] = !!(value & 0x80); - regs.bg_window2_invert [BG4] = !!(value & 0x40); - regs.bg_window1_enabled[BG4] = !!(value & 0x20); - regs.bg_window1_invert [BG4] = !!(value & 0x10); - regs.bg_window2_enabled[BG3] = !!(value & 0x08); - regs.bg_window2_invert [BG3] = !!(value & 0x04); - regs.bg_window1_enabled[BG3] = !!(value & 0x02); - regs.bg_window1_invert [BG3] = !!(value & 0x01); + regs.window2_enabled[BG4] = !!(value & 0x80); + regs.window2_invert [BG4] = !!(value & 0x40); + regs.window1_enabled[BG4] = !!(value & 0x20); + regs.window1_invert [BG4] = !!(value & 0x10); + regs.window2_enabled[BG3] = !!(value & 0x08); + regs.window2_invert [BG3] = !!(value & 0x04); + regs.window1_enabled[BG3] = !!(value & 0x02); + regs.window1_invert [BG3] = !!(value & 0x01); + + window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true; + window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true; } //WOBJSEL void bPPU::mmio_w2125(uint8 value) { - regs.color_window2_enabled = !!(value & 0x80); - regs.color_window2_invert = !!(value & 0x40); - regs.color_window1_enabled = !!(value & 0x20); - regs.color_window1_invert = !!(value & 0x10); - regs.bg_window2_enabled[OAM] = !!(value & 0x08); - regs.bg_window2_invert [OAM] = !!(value & 0x04); - regs.bg_window1_enabled[OAM] = !!(value & 0x02); - regs.bg_window1_invert [OAM] = !!(value & 0x01); + regs.window2_enabled[COL] = !!(value & 0x80); + regs.window2_invert [COL] = !!(value & 0x40); + regs.window1_enabled[COL] = !!(value & 0x20); + regs.window1_invert [COL] = !!(value & 0x10); + regs.window2_enabled[OAM] = !!(value & 0x08); + regs.window2_invert [OAM] = !!(value & 0x04); + regs.window1_enabled[OAM] = !!(value & 0x02); + regs.window1_invert [OAM] = !!(value & 0x01); + + window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true; + window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true; } //WH0 void bPPU::mmio_w2126(uint8 value) { regs.window1_left = value; + + window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true; + window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true; + window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true; + window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true; + window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true; + window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true; } //WH1 void bPPU::mmio_w2127(uint8 value) { regs.window1_right = value; + + window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true; + window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true; + window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true; + window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true; + window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true; + window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true; } //WH2 void bPPU::mmio_w2128(uint8 value) { regs.window2_left = value; + + window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true; + window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true; + window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true; + window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true; + window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true; + window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true; } //WH3 void bPPU::mmio_w2129(uint8 value) { regs.window2_right = value; + + window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true; + window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true; + window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true; + window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true; + window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true; + window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true; } //WBGLOG void bPPU::mmio_w212a(uint8 value) { - regs.bg_window_mask[BG4] = (value >> 6) & 3; - regs.bg_window_mask[BG3] = (value >> 4) & 3; - regs.bg_window_mask[BG2] = (value >> 2) & 3; - regs.bg_window_mask[BG1] = (value ) & 3; + regs.window_mask[BG4] = (value >> 6) & 3; + regs.window_mask[BG3] = (value >> 4) & 3; + regs.window_mask[BG2] = (value >> 2) & 3; + regs.window_mask[BG1] = (value ) & 3; + + window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true; + window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true; + window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true; + window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true; } //WOBJLOG void bPPU::mmio_w212b(uint8 value) { - regs.color_window_mask = (value >> 2) & 3; - regs.bg_window_mask[OAM] = (value ) & 3; + regs.window_mask[COL] = (value >> 2) & 3; + regs.window_mask[OAM] = (value ) & 3; + + window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true; + window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true; } //TM @@ -372,20 +487,32 @@ void bPPU::mmio_w212d(uint8 value) { //TMW void bPPU::mmio_w212e(uint8 value) { - regs.bg_window_enabled[OAM] = !!(value & 0x10); - regs.bg_window_enabled[BG4] = !!(value & 0x08); - regs.bg_window_enabled[BG3] = !!(value & 0x04); - regs.bg_window_enabled[BG2] = !!(value & 0x02); - regs.bg_window_enabled[BG1] = !!(value & 0x01); + regs.window_enabled[OAM] = !!(value & 0x10); + regs.window_enabled[BG4] = !!(value & 0x08); + regs.window_enabled[BG3] = !!(value & 0x04); + regs.window_enabled[BG2] = !!(value & 0x02); + regs.window_enabled[BG1] = !!(value & 0x01); + + window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true; + window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true; + window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true; + window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true; + window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true; } //TSW void bPPU::mmio_w212f(uint8 value) { - regs.bgsub_window_enabled[OAM] = !!(value & 0x10); - regs.bgsub_window_enabled[BG4] = !!(value & 0x08); - regs.bgsub_window_enabled[BG3] = !!(value & 0x04); - regs.bgsub_window_enabled[BG2] = !!(value & 0x02); - regs.bgsub_window_enabled[BG1] = !!(value & 0x01); + regs.sub_window_enabled[OAM] = !!(value & 0x10); + regs.sub_window_enabled[BG4] = !!(value & 0x08); + regs.sub_window_enabled[BG3] = !!(value & 0x04); + regs.sub_window_enabled[BG2] = !!(value & 0x02); + regs.sub_window_enabled[BG1] = !!(value & 0x01); + + window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true; + window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true; + window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true; + window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true; + window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true; } //CGWSEL @@ -394,18 +521,20 @@ void bPPU::mmio_w2130(uint8 value) { regs.colorsub_mask = (value >> 4) & 3; regs.addsub_mode = !!(value & 0x02); regs.direct_color = !!(value & 0x01); + + window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true; } //CGADDSUB void bPPU::mmio_w2131(uint8 value) { - regs.color_mode = !!(value & 0x80); - regs.color_halve = !!(value & 0x40); - regs.bg_color_enabled[BACK] = !!(value & 0x20); - regs.bg_color_enabled[OAM] = !!(value & 0x10); - regs.bg_color_enabled[BG4] = !!(value & 0x08); - regs.bg_color_enabled[BG3] = !!(value & 0x04); - regs.bg_color_enabled[BG2] = !!(value & 0x02); - regs.bg_color_enabled[BG1] = !!(value & 0x01); + regs.color_mode = !!(value & 0x80); + regs.color_halve = !!(value & 0x40); + regs.color_enabled[BACK] = !!(value & 0x20); + regs.color_enabled[OAM] = !!(value & 0x10); + regs.color_enabled[BG4] = !!(value & 0x08); + regs.color_enabled[BG3] = !!(value & 0x04); + regs.color_enabled[BG2] = !!(value & 0x02); + regs.color_enabled[BG1] = !!(value & 0x01); } //COLDATA @@ -476,8 +605,12 @@ uint16 addr = get_vram_address(); regs.ppu1_mdr = regs.vram_readbuffer; if(regs.vram_incmode == 0) { addr &= 0xfffe; - regs.vram_readbuffer = vram_read(addr); - regs.vram_readbuffer |= vram_read(addr + 1) << 8; + if(vram_can_read()) { + regs.vram_readbuffer = vram_read(addr); + regs.vram_readbuffer |= vram_read(addr + 1) << 8; + } else { + regs.vram_readbuffer = 0x0000; + } regs.vram_addr += regs.vram_incsize; } return regs.ppu1_mdr; @@ -489,8 +622,12 @@ uint16 addr = get_vram_address() + 1; regs.ppu1_mdr = regs.vram_readbuffer >> 8; if(regs.vram_incmode == 1) { addr &= 0xfffe; - regs.vram_readbuffer = vram_read(addr); - regs.vram_readbuffer |= vram_read(addr + 1) << 8; + if(vram_can_read()) { + regs.vram_readbuffer = vram_read(addr); + regs.vram_readbuffer |= vram_read(addr + 1) << 8; + } else { + regs.vram_readbuffer = 0x0000; + } regs.vram_addr += regs.vram_incsize; } return regs.ppu1_mdr; diff --git a/src/ppu/bppu/bppu_render.cpp b/src/ppu/bppu/bppu_render.cpp index 131d8d59..9a156c02 100644 --- a/src/ppu/bppu/bppu_render.cpp +++ b/src/ppu/bppu/bppu_render.cpp @@ -126,7 +126,7 @@ void bPPU::render_line() { } clear_pixel_cache(); - build_color_window_tables(); + build_window_tables(COL); switch(regs.bg_mode) { case 0:render_line_mode0();break; diff --git a/src/ppu/bppu/bppu_render.h b/src/ppu/bppu/bppu_render.h index cc6f4229..13be3f0c 100644 --- a/src/ppu/bppu/bppu_render.h +++ b/src/ppu/bppu/bppu_render.h @@ -35,17 +35,18 @@ uint8 *bg_tiledata_state[3]; void render_bg_tile(uint8 color_depth, uint16 tile_num); inline void clear_pixel_cache(); -void init_tiledata_cache(); -void clear_tiledata_cache(); +inline void init_tiledata_cache(); +inline void clear_tiledata_cache(); //bppu_render_windows.cpp -uint8 main_windowtable[5][512], sub_windowtable[5][512], - main_colorwindowtable[512], sub_colorwindowtable[512]; +struct _window { + bool main_dirty, sub_dirty; + uint8 main[512], sub[512]; +} window_cache[6]; -void build_window_table(uint8 bg, uint8 *wtbl, bool mainscreen); +void build_window_table(uint8 bg, bool mainscreen); void build_window_tables(uint8 bg); -void build_color_window_table(uint8 *wtbl, uint8 mask); -void build_color_window_tables(); +inline void clear_window_cache(); //bppu_render_bg.cpp void render_line_bg(uint8 bg, uint8 color_depth, uint8 pri0_pos, uint8 pri1_pos); diff --git a/src/ppu/bppu/bppu_render_bg.cpp b/src/ppu/bppu/bppu_render_bg.cpp index 7379d277..bd29d0e4 100644 --- a/src/ppu/bppu/bppu_render_bg.cpp +++ b/src/ppu/bppu/bppu_render_bg.cpp @@ -118,8 +118,8 @@ int xpos, ypos; uint16 map_index, hoffset, voffset, col; build_window_tables(bg); -uint8 *wt_main = main_windowtable[bg]; -uint8 *wt_sub = sub_windowtable[bg]; +uint8 *wt_main = window_cache[bg].main; +uint8 *wt_sub = window_cache[bg].sub; screen_x = 0; do { //for(screen_x=0;screen_x<_screen_width;screen_x++) { @@ -141,7 +141,7 @@ uint8 *wt_sub = sub_windowtable[bg]; if(regs.bg_mode == 4) { pos = regs.bg_scaddr[BG3] + tile_x; - t = *((uint16*)vram + (pos >> 1)); + t = read16(vram, pos); if(t & opt_valid_bit) { if(!(t & 0x8000)) { hoffset = ((t & 0x1ff8) | (hscroll & 7)) & screen_width_mask; @@ -151,12 +151,12 @@ uint8 *wt_sub = sub_windowtable[bg]; } } else { pos = regs.bg_scaddr[BG3] + tile_x; - t = *((uint16*)vram + (pos >> 1)); + t = read16(vram, pos); if(t & opt_valid_bit) { hoffset = ((t & 0x1ff8) | (hscroll & 7)) & screen_width_mask; } pos = regs.bg_scaddr[BG3] + 64 + tile_x; - t = *((uint16*)vram + (pos >> 1)); + t = read16(vram, pos); if(t & opt_valid_bit) { voffset = (t & 0x1fff) & screen_height_mask; } @@ -186,7 +186,7 @@ uint8 *wt_sub = sub_windowtable[bg]; base_xpos = ((mosaic_x >> 3) & 31); base_pos = (((mosaic_y >> tile_height) & 31) << 5) + ((mosaic_x >> tile_width) & 31); pos = _scaddr + map_index + (base_pos << 1); - t = *((uint16*)vram + (pos >> 1)); + t = read16(vram, pos); mirror_y = !!(t & 0x8000); mirror_x = !!(t & 0x4000); @@ -228,7 +228,7 @@ int _pri; if(mirror_x) { xpos = (7 - (mosaic_x & 7)); } else { xpos = ( (mosaic_x & 7)); } col = *(tile_ptr + xpos); - if(col && main_colorwindowtable[screen_x]) { + if(col && window_cache[COL].main[screen_x]) { if(regs.direct_color == true && bg == BG1 && (regs.bg_mode == 3 || regs.bg_mode == 4)) { col = get_direct_color(pal_num, col); } else { diff --git a/src/ppu/bppu/bppu_render_cache.cpp b/src/ppu/bppu/bppu_render_cache.cpp index 1cf45204..28bc5ae9 100644 --- a/src/ppu/bppu/bppu_render_cache.cpp +++ b/src/ppu/bppu/bppu_render_cache.cpp @@ -117,3 +117,17 @@ void bPPU::clear_tiledata_cache() { memset(bg_tiledata_state[TILE_4BIT], 0, 2048); memset(bg_tiledata_state[TILE_8BIT], 0, 1024); } + +void bPPU::clear_window_cache() { + for(int i=0;i<6;i++) { + window_cache[i].main_dirty = true; + window_cache[i].sub_dirty = true; + } + + build_window_tables(BG1); + build_window_tables(BG2); + build_window_tables(BG3); + build_window_tables(BG4); + build_window_tables(OAM); + build_window_tables(COL); +} diff --git a/src/ppu/bppu/bppu_render_line.cpp b/src/ppu/bppu/bppu_render_line.cpp index 7b657c07..ac8565d5 100644 --- a/src/ppu/bppu/bppu_render_line.cpp +++ b/src/ppu/bppu/bppu_render_line.cpp @@ -1,21 +1,21 @@ inline uint16 bPPU::get_palette(uint8 index) { - return *((uint16*)cgram + index); + return read16(cgram, index << 1); } inline uint16 bPPU::get_direct_color(uint8 p, uint8 t) { -//p = 00000bgr -//t = BBGGGRRR -//r = 0BBb00GGGg0RRRr0 +//p = 00000bgr +//t = BBGGGRRR +//r = 0BBb00GGGg0RRRr0 return ((t & 7) << 2) | ((p & 1) << 1) | - (((t >> 3) & 7) << 7) | (((p >> 1) & 1) << 6) | - ((t >> 6) << 13) | ((p >> 2) << 12); + (((t >> 3) & 7) << 7) | (((p >> 1) & 1) << 6) | + ((t >> 6) << 13) | ((p >> 2) << 12); } inline uint16 bPPU::get_pixel(int x) { _pixel *p = &pixel_cache[x]; uint16 _r, src_back = get_palette(0); if(p->bg_main && p->bg_sub) { - if(p->color_exempt == false && regs.bg_color_enabled[p->bg_main & 0x7f] && sub_colorwindowtable[x]) { + if(p->color_exempt == false && regs.color_enabled[p->bg_main & 0x7f] && window_cache[COL].sub[x]) { if(regs.addsub_mode) { _r = addsub_pixels(p->src_main, p->src_sub); } else { @@ -25,14 +25,14 @@ uint16 _r, src_back = get_palette(0); _r = p->src_main; } } else if(p->bg_main) { - if(p->color_exempt == false && regs.bg_color_enabled[p->bg_main & 0x7f] && sub_colorwindowtable[x]) { + if(p->color_exempt == false && regs.color_enabled[p->bg_main & 0x7f] && window_cache[COL].sub[x]) { _r = addsub_pixel(p->src_main); } else { _r = p->src_main; } } else if(p->bg_sub) { - if(regs.bg_color_enabled[BACK]) { - if(sub_colorwindowtable[x]) { + if(regs.color_enabled[BACK]) { + if(window_cache[COL].sub[x]) { if(regs.addsub_mode) { _r = addsub_pixels(src_back, p->src_sub); } else { @@ -42,11 +42,11 @@ uint16 _r, src_back = get_palette(0); _r = src_back; } } else { - _r = 0x0000; + _r = src_back; //was 0x0000 -- possibly another condition here? } } else { - if(main_colorwindowtable[x]) { - if(regs.bg_color_enabled[BACK] && sub_colorwindowtable[x]) { + if(window_cache[COL].main[x]) { + if(regs.color_enabled[BACK] && window_cache[COL].sub[x]) { _r = addsub_pixel(src_back); } else { _r = src_back; diff --git a/src/ppu/bppu/bppu_render_mode7.cpp b/src/ppu/bppu/bppu_render_mode7.cpp index 1671f6c5..85e06c1c 100644 --- a/src/ppu/bppu/bppu_render_mode7.cpp +++ b/src/ppu/bppu/bppu_render_mode7.cpp @@ -31,15 +31,16 @@ int32 tx, ty, tile, palette; cx = (int32(regs.m7x) << 19) >> 19; cy = (int32(regs.m7y) << 19) >> 19; hofs = (int32(regs.m7_hofs) << 19) >> 19; - vofs = (int32(regs.m7_vofs + 1) << 19) >> 19; +//+1 breaks FF5 title screen mirror alignment... + vofs = (int32(regs.m7_vofs + 0) << 19) >> 19; int _pri, _x; bool _bg_enabled = regs.bg_enabled[bg]; bool _bgsub_enabled = regs.bgsub_enabled[bg]; build_window_tables(bg); -uint8 *wt_main = main_windowtable[bg]; -uint8 *wt_sub = sub_windowtable[bg]; +uint8 *wt_main = window_cache[bg].main; +uint8 *wt_sub = window_cache[bg].sub; if(regs.mode7_vflip == true) { y = 255 - _y; @@ -119,7 +120,7 @@ int32 psy = ((c * CLIP(hofs - cx)) & ~63) + ((d * CLIP(vofs - cy)) & ~63) + ((d _x = x; } - if(main_colorwindowtable[_x]) { + if(window_cache[COL].main[_x]) { uint32 col; if(regs.direct_color == true && bg == BG1) { //direct color mode does not apply to bg2, as it is only 128 colors... diff --git a/src/ppu/bppu/bppu_render_oam.cpp b/src/ppu/bppu/bppu_render_oam.cpp index 54d7f1a8..e1ba3771 100644 --- a/src/ppu/bppu/bppu_render_oam.cpp +++ b/src/ppu/bppu/bppu_render_oam.cpp @@ -207,9 +207,9 @@ int s, x; bool _bg_enabled = regs.bg_enabled[OAM]; bool _bgsub_enabled = regs.bgsub_enabled[OAM]; -uint8 *wt_main = main_windowtable[OAM]; -uint8 *wt_sub = sub_windowtable[OAM]; build_window_tables(OAM); +uint8 *wt_main = window_cache[OAM].main; +uint8 *wt_sub = window_cache[OAM].sub; regs.oam_itemcount = 0; regs.oam_tilecount = 0; @@ -240,6 +240,7 @@ uint8 *wt_sub = sub_windowtable[OAM]; regs.range_over |= (regs.oam_itemcount > 32); if(_bg_enabled == false && _bgsub_enabled == false)return; + int _pri; for(x=0;x<_screen_width;x++) { if(oam_line_pri[x] == OAM_PRI_NONE)continue; @@ -251,7 +252,7 @@ int _pri; case 3:_pri = pri3_pos;break; } - if(main_colorwindowtable[x]) { + if(window_cache[COL].main[x]) { if(_bg_enabled == true && !wt_main[x]) { if(pixel_cache[x].pri_main < _pri) { pixel_cache[x].pri_main = _pri; diff --git a/src/ppu/bppu/bppu_render_windows.cpp b/src/ppu/bppu/bppu_render_windows.cpp index 1d74b67b..4f3becf9 100644 --- a/src/ppu/bppu/bppu_render_windows.cpp +++ b/src/ppu/bppu/bppu_render_windows.cpp @@ -1,11 +1,50 @@ -void bPPU::build_window_table(uint8 bg, uint8 *wtbl, bool mainscreen) { - if(mainscreen == true && regs.bg_window_enabled[bg] == false) { - memset(wtbl, 0, _screen_width); - return; +void bPPU::build_window_table(uint8 bg, bool mainscreen) { +uint8 set = true, clr = false; +uint8 *wtbl; + if(mainscreen == true) { + wtbl = (uint8*)window_cache[bg].main; + } else { + wtbl = (uint8*)window_cache[bg].sub; } - if(mainscreen == false && regs.bgsub_window_enabled[bg] == false) { - memset(wtbl, 0, _screen_width); - return; + + if(bg != COL) { + if(mainscreen == true && regs.window_enabled[bg] == false) { + memset(wtbl, 0, _screen_width); + return; + } + if(mainscreen == false && regs.sub_window_enabled[bg] == false) { + memset(wtbl, 0, _screen_width); + return; + } + } else { + uint8 mask; + if(mainscreen == true) { + mask = regs.color_mask; + } else { + mask = regs.colorsub_mask; + } + + if(mask == 0) { + //always + memset(wtbl, 1, _screen_width); + return; + } + + if(mask == 3) { + //never + memset(wtbl, 0, _screen_width); + return; + } + + if(mask == 1) { + //inside window only + set = 1; + clr = 0; + } else { //mask == 2 + //outside window only + set = 0; + clr = 1; + } } uint16 window1_left, window1_right, window2_left, window2_right; @@ -24,54 +63,54 @@ bool r; window2_right <<= 1; } - if(regs.bg_window1_enabled[bg] == false && regs.bg_window2_enabled[bg] == false) { - memset(wtbl, 0, _screen_width); - } else if(regs.bg_window1_enabled[bg] == true && regs.bg_window2_enabled[bg] == false) { - if(regs.bg_window1_invert[bg] == false) { + if(regs.window1_enabled[bg] == false && regs.window2_enabled[bg] == false) { + memset(wtbl, clr, _screen_width); + } else if(regs.window1_enabled[bg] == true && regs.window2_enabled[bg] == false) { + if(regs.window1_invert[bg] == false) { for(x=0;x<_screen_width;x++) { - wtbl[x] = (x >= window1_left && x <= window1_right)?true:false; + wtbl[x] = (x >= window1_left && x <= window1_right) ? set : clr; } } else { for(x=0;x<_screen_width;x++) { - wtbl[x] = (x < window1_left || x > window1_right)?true:false; + wtbl[x] = (x < window1_left || x > window1_right) ? set : clr; } } - } else if(regs.bg_window1_enabled[bg] == false && regs.bg_window2_enabled[bg] == true) { - if(regs.bg_window2_invert[bg] == false) { + } else if(regs.window1_enabled[bg] == false && regs.window2_enabled[bg] == true) { + if(regs.window2_invert[bg] == false) { for(x=0;x<_screen_width;x++) { - wtbl[x] = (x >= window2_left && x <= window2_right)?true:false; + wtbl[x] = (x >= window2_left && x <= window2_right) ? set : clr; } } else { for(x=0;x<_screen_width;x++) { - wtbl[x] = (x < window2_left || x > window2_right)?true:false; + wtbl[x] = (x < window2_left || x > window2_right) ? set : clr; } } - } else { //if(regs.bg_window1_enabled[bg] == true && regs.bg_window2_enabled[bg] == true) { + } else { //if(regs.window1_enabled[bg] == true && regs.window2_enabled[bg] == true) { for(x=0;x<_screen_width;x++) { - if(regs.bg_window1_invert[bg] == false) { + if(regs.window1_invert[bg] == false) { w1_mask = (x >= window1_left && x <= window1_right); } else { w1_mask = (x < window1_left || x > window1_right); } - if(regs.bg_window2_invert[bg] == false) { + if(regs.window2_invert[bg] == false) { w2_mask = (x >= window2_left && x <= window2_right); } else { w2_mask = (x < window2_left || x > window2_right); } - switch(regs.bg_window_mask[bg]) { + switch(regs.window_mask[bg]) { case 0: //WINDOWMASK_OR: - wtbl[x] = ((w1_mask | w2_mask) == 1)?true:false; + wtbl[x] = ((w1_mask | w2_mask) == 1) ? set : clr; break; case 1: //WINDOWMASK_AND: - wtbl[x] = ((w1_mask & w2_mask) == 1)?true:false; + wtbl[x] = ((w1_mask & w2_mask) == 1) ? set : clr; break; case 2: //WINDOWMASK_XOR: - wtbl[x] = ((w1_mask ^ w2_mask) == 1)?true:false; + wtbl[x] = ((w1_mask ^ w2_mask) == 1) ? set : clr; break; case 3: //WINDOWMASK_XNOR: - wtbl[x] = ((w1_mask ^ w2_mask) == 0)?true:false; + wtbl[x] = ((w1_mask ^ w2_mask) == 0) ? set : clr; break; } } @@ -79,105 +118,13 @@ bool r; } void bPPU::build_window_tables(uint8 bg) { - build_window_table(bg, main_windowtable[bg], true); - build_window_table(bg, sub_windowtable[bg], false); -} - -void bPPU::build_color_window_table(uint8 *wtbl, uint8 mask) { - if(mask == 0) { - //always - memset(wtbl, 1, _screen_width); - return; + if(window_cache[bg].main_dirty == true) { + window_cache[bg].main_dirty = false; + build_window_table(bg, true); } - if(mask == 3) { - //never - memset(wtbl, 0, _screen_width); - return; - } - -int _true, _false; - if(mask == 1) { - //inside window only - _true = 1; - _false = 0; - } else { //mask == 2 - //outside window only - _true = 0; - _false = 1; - } - -uint16 window1_left, window1_right, window2_left, window2_right; -int w1_mask, w2_mask; //1 = masked, 0 = not masked -int x; -bool r; - window1_left = regs.window1_left; - window1_right = regs.window1_right; - window2_left = regs.window2_left; - window2_right = regs.window2_right; - - if(_screen_width == 512) { - window1_left <<= 1; - window1_right <<= 1; - window2_left <<= 1; - window2_right <<= 1; - } - - if(regs.color_window1_enabled == false && regs.color_window2_enabled == false) { - memset(wtbl, _false, _screen_width); - } else if(regs.color_window1_enabled == true && regs.color_window2_enabled == false) { - if(regs.color_window1_invert == false) { - for(x=0;x<_screen_width;x++) { - wtbl[x] = (x >= window1_left && x <= window1_right)?_true:_false; - } - } else { - for(x=0;x<_screen_width;x++) { - wtbl[x] = (x < window1_left || x > window1_right)?_true:_false; - } - } - } else if(regs.color_window1_enabled == false && regs.color_window2_enabled == true) { - if(regs.color_window2_invert == false) { - for(x=0;x<_screen_width;x++) { - wtbl[x] = (x >= window2_left && x <= window2_right)?_true:_false; - } - } else { - for(x=0;x<_screen_width;x++) { - wtbl[x] = (x < window2_left || x > window2_right)?_true:_false; - } - } - } else { //if(regs.color_window1_enabled == true && regs.color_window2_enabled == true) { - for(x=0;x<_screen_width;x++) { - if(regs.color_window1_invert == false) { - w1_mask = (x >= window1_left && x <= window1_right); - } else { - w1_mask = (x < window1_left || x > window1_right); - } - - if(regs.color_window2_invert == false) { - w2_mask = (x >= window2_left && x <= window2_right); - } else { - w2_mask = (x < window2_left || x > window2_right); - } - - switch(regs.color_window_mask) { - case 0: //WINDOWMASK_OR: - wtbl[x] = ((w1_mask | w2_mask) == 1)?_true:_false; - break; - case 1: //WINDOWMASK_AND: - wtbl[x] = ((w1_mask & w2_mask) == 1)?_true:_false; - break; - case 2: //WINDOWMASK_XOR: - wtbl[x] = ((w1_mask ^ w2_mask) == 1)?_true:_false; - break; - case 3: //WINDOWMASK_XNOR: - wtbl[x] = ((w1_mask ^ w2_mask) == 0)?_true:_false; - break; - } - } + if(window_cache[bg].sub_dirty == true) { + window_cache[bg].sub_dirty = false; + build_window_table(bg, false); } } - -void bPPU::build_color_window_tables() { - build_color_window_table(main_colorwindowtable, regs.color_mask); - build_color_window_table(sub_colorwindowtable, regs.colorsub_mask); -} diff --git a/src/ppu/bppu/snes9x_render_mode7.cpp b/src/ppu/bppu/snes9x_render_mode7.cpp deleted file mode 100644 index 572a414f..00000000 --- a/src/ppu/bppu/snes9x_render_mode7.cpp +++ /dev/null @@ -1,148 +0,0 @@ -#define CLIP_10BIT_SIGNED(x) \ - ((x) & ((1 << 10) - 1)) + (((((x) & (1 << 13)) ^ (1 << 13)) - (1 << 13)) >> 3) - -#define CAST_WORDTOINT(x) \ - (int32)((((x) & 0x8000) ? ((x) | 0xffff0000) : ((x) & 0x00007fff))) - -void bPPU::render_line_mode7(uint8 bg, uint8 pri0_pos, uint8 pri1_pos) { -int32 x; -int32 step_m7a, step_m7c, m7a, m7b, m7c, m7d; -int32 hoffset, voffset; -int32 centerx, centery; -int32 xx, yy; -int32 px, py; -int32 tx, ty, tile, palette; -uint8 layer_pos; - if(regs.bg_enabled[bg] == false && regs.bgsub_enabled[bg] == false)return; - -uint16 *mtable_x, *mtable_y; - 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 - //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]; - mtable_y = (uint16*)mosaic_table[(regs.mosaic_enabled[BG1] == true) ? regs.mosaic_size : 0]; - } - - hoffset = ((int32)regs.m7_hofs << 19) >> 19; - voffset = ((int32)regs.m7_vofs << 19) >> 19; - - centerx = ((int32)regs.m7x << 19) >> 19; - centery = ((int32)regs.m7y << 19) >> 19; - - if(regs.mode7_vflip == true) { - yy = 255 - _y; - } else { - yy = _y; - } - yy = mtable_y[yy]; - yy += CLIP_10BIT_SIGNED(voffset - centery); - - m7b = CAST_WORDTOINT(regs.m7b) * yy + (centerx << 8); - m7d = CAST_WORDTOINT(regs.m7d) * yy + (centery << 8); - - step_m7a = CAST_WORDTOINT(regs.m7a); - step_m7c = CAST_WORDTOINT(regs.m7c); - - xx = CLIP_10BIT_SIGNED(hoffset - centerx); - - m7a = CAST_WORDTOINT(regs.m7a) * xx; - m7c = CAST_WORDTOINT(regs.m7c) * xx; - -int _pri, _x; -bool _bg_enabled, _bgsub_enabled; - _bg_enabled = regs.bg_enabled[bg]; - _bgsub_enabled = regs.bgsub_enabled[bg]; - - build_window_tables(bg); -uint8 *wt_main = main_windowtable[bg]; -uint8 *wt_sub = sub_windowtable[bg]; - - for(x=0;x<256;x++) { - px = ((m7a + step_m7a * mtable_x[x] + m7b) >> 8); - py = ((m7c + step_m7c * mtable_x[x] + m7d) >> 8); - - switch(regs.mode7_repeat) { - case 0: //screen repitition outside of screen area - case 1: //same as case 0 - px &= 1023; - py &= 1023; - tx = ((px >> 3) & 127); - ty = ((py >> 3) & 127); - tile = vram[(ty * 128 + tx) << 1]; - palette = vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; - break; - case 2: //palette color 0 outside of screen area - if(px < 0 || px > 1023 || py < 0 || py > 1023) { - palette = 0; - } else { - px &= 1023; - py &= 1023; - tx = ((px >> 3) & 127); - ty = ((py >> 3) & 127); - tile = vram[(ty * 128 + tx) << 1]; - palette = vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; - } - break; - case 3: //character 0 repetition outside of screen area - if(px < 0 || px > 1023 || py < 0 || py > 1023) { - tx = 0; - ty = 0; - } else { - px &= 1023; - py &= 1023; - tx = ((px >> 3) & 127); - ty = ((py >> 3) & 127); - } - tile = vram[(ty * 128 + tx) << 1]; - palette = vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; - break; - } - - if(bg == BG1) { - _pri = pri0_pos; - } else { - _pri = (palette >> 7) ? pri1_pos : pri0_pos; - palette &= 0x7f; - } - - if(!palette)continue; - - if(regs.mode7_hflip == true) { - _x = 255 - x; - } else { - _x = x; - } - - if(main_colorwindowtable[_x]) { - uint32 col; - if(regs.direct_color == true && bg == BG1) { - //direct color mode does not apply to bg2 as it is only 128 colors... - col = get_direct_color(0, palette); - } else { - col = get_palette(palette); - } - - if(regs.bg_enabled[bg] == true && !wt_main[_x]) { - if(pixel_cache[_x].pri_main < _pri) { - pixel_cache[_x].pri_main = _pri; - pixel_cache[_x].bg_main = 0x80 | bg; - pixel_cache[_x].src_main = col; - pixel_cache[_x].color_exempt = false; - } - } - if(regs.bgsub_enabled[bg] == true && !wt_sub[_x]) { - if(pixel_cache[_x].pri_sub < _pri) { - pixel_cache[_x].pri_sub = _pri; - pixel_cache[_x].bg_sub = 0x80 | bg; - pixel_cache[_x].src_sub = col; - } - } - } - } -} - -#undef CLIP_10BIT_SIGNED -#undef CAST_WORDTOINT diff --git a/src/ppu/ppu.cpp b/src/ppu/ppu.cpp index 224bd621..f93b0fbf 100644 --- a/src/ppu/ppu.cpp +++ b/src/ppu/ppu.cpp @@ -5,12 +5,33 @@ void PPU::get_scanline_info(scanline_info *info) { info->interlace = cpu->interlace(); } -void PPU::set_frameskip(int fs) {} -bool PPU::render_frame() { return true; } +void PPU::enable_renderer(bool r) { status.render_output = r; } +bool PPU::renderer_enabled() { return status.render_output; } + +void PPU::frame() { +static fr = 0, fe = 0; +static time_t prev, curr; + fe++; + if(status.render_output)fr++; + + time(&curr); + if(curr != prev) { + status.frames_updated = true; + status.frames_rendered = fr; + status.frames_executed = fe; + fr = fe = 0; + } + prev = curr; +} PPU::PPU() { + status.render_output = true; + status.frames_updated = false; + status.frames_rendered = 0; + status.frames_executed = 0; + ppu1_version = 1; - ppu2_version = 3; + ppu2_version = 1; mmio = &mmio_unmapped; } diff --git a/src/ppu/ppu.h b/src/ppu/ppu.h index f2997aea..6cdc6843 100644 --- a/src/ppu/ppu.h +++ b/src/ppu/ppu.h @@ -1,5 +1,16 @@ class PPU { public: + +//this struct should be read-only to +//functions outside of this class +struct { + bool render_output; + + bool frames_updated; + uint32 frames_rendered; + uint32 frames_executed; +} status; + //PPU1 version number //* 1 is known //* reported by $213e @@ -32,11 +43,11 @@ struct scanline_info { virtual void run() = 0; virtual void scanline() = 0; virtual void render_scanline() = 0; - virtual void frame() = 0; + virtual void frame(); virtual void power() = 0; virtual void reset() = 0; - virtual void set_frameskip(int fs); - virtual bool render_frame(); + virtual void enable_renderer(bool r); + virtual bool renderer_enabled(); PPU(); ~PPU(); diff --git a/src/sdl/Makefile b/src/sdl/Makefile index 0275a371..16b9d601 100644 --- a/src/sdl/Makefile +++ b/src/sdl/Makefile @@ -1,5 +1,5 @@ CC = c++ -CFLAGS = -O2 +CFLAGS = -O3 -fomit-frame-pointer -ffast-math OBJS = sdlmain.o \ libstring.o libconfig.o \ reader.o \ diff --git a/src/sdl/Makefile.win32 b/src/sdl/Makefile.win32 index fb3b12fb..1bc07ca4 100644 --- a/src/sdl/Makefile.win32 +++ b/src/sdl/Makefile.win32 @@ -1,5 +1,5 @@ CC = cl -CFLAGS = /nologo /O2 /Ogityb2 /Gr /Gs +CFLAGS = /nologo /O2 /Ogityb2 /Gr /Gs /DARCH_LSB OBJS = sdlmain.obj \ libstring.obj libconfig.obj \ reader.obj \ diff --git a/src/sdl/bsnes.cpp b/src/sdl/bsnes.cpp index 054b3243..1c2482b0 100644 --- a/src/sdl/bsnes.cpp +++ b/src/sdl/bsnes.cpp @@ -17,17 +17,33 @@ void bSNES::run() { } } -void bSNES::video_run() { render(); } +void bSNES::video_run() { + if(ppu->status.frames_updated) { + char s[512], t[512]; + ppu->status.frames_updated = false; +// if((bool)config::gui.show_fps == true) { + sprintf(s, "%s : %d fps", BSNES_TITLE, ppu->status.frames_executed); +// if(w_main->frameskip != 0) { +// sprintf(t, " (%d frames)", ppu->status.frames_rendered); +// strcat(s, t); +// } + SDL_WM_SetCaption(s, 0); +// } + } + + render(); +} + void bSNES::sound_run() {} /*********************** *** Input functions *** ***********************/ -//It would appear that keystate does not need to be free'd +//It would appear that keystate does not need to be released //after calling SDL_GetKeyState... doing so causes libSDL //to throw error messages about a bad free call to stdout... -void bSNES::poll_input() { +void bSNES::poll_input(uint8 type) { uint8 *keystate = SDL_GetKeyState(0); joypad1.up = keystate[(int)config::input.joypad1.up]; joypad1.down = keystate[(int)config::input.joypad1.down]; diff --git a/src/sdl/bsnes.h b/src/sdl/bsnes.h index dfefc6e1..de5af083 100644 --- a/src/sdl/bsnes.h +++ b/src/sdl/bsnes.h @@ -22,7 +22,7 @@ enum { STOP = 0, RUN }; uint32 get_status(); //input functions - void poll_input(); + void poll_input(uint8 type); bool get_input_status(uint8 device, uint8 button); void notify(uint32 message, uint32 param1 = 0, uint32 param2 = 0); diff --git a/src/sdl/bsnes_sdl.cfg b/src/sdl/bsnes_sdl.cfg new file mode 100644 index 00000000..5a6ac037 --- /dev/null +++ b/src/sdl/bsnes_sdl.cfg @@ -0,0 +1,86 @@ +# Applies contrast adjust filter to video output when enabled +# Works by lowering the brightness of darker colors, +# while leaving brighter colors alone; thus reducing saturation +# (default = true) +snes.video_color_curve = true + +# Selects color adjustment filter for video output +# 0 = Normal (no filter, rgb555) +# 1 = Grayscale mode (l5) +# 2 = VGA mode (rgb332) +# 3 = Genesis mode (rgb333) +# (default = 0) +snes.video_color_adjust_mode = 0 + +# Mutes SNES audio output when enabled +# (default = true) +snes.mute = true + +# Enable fullscreen mode at startup +# (default = false) +video.fullscreen = false + +# Window / Fullscreen width +# (default = 320) +video.display_width = 320 + +# Window / Fullscreen height +# (default = 240) +video.display_height = 240 + +# SNES video output width +# (default = 256) +video.output_width = 256 + +# SNES video output height +# (default = 223) +video.output_height = 223 + +# Joypad1 up +# (default = 0x111) +input.joypad1.up = 0x111 + +# Joypad1 down +# (default = 0x112) +input.joypad1.down = 0x112 + +# Joypad1 left +# (default = 0x114) +input.joypad1.left = 0x114 + +# Joypad1 right +# (default = 0x113) +input.joypad1.right = 0x113 + +# Joypad1 A +# (default = 0x78) +input.joypad1.a = 0x78 + +# Joypad1 B +# (default = 0x7a) +input.joypad1.b = 0x7a + +# Joypad1 X +# (default = 0x73) +input.joypad1.x = 0x73 + +# Joypad1 Y +# (default = 0x61) +input.joypad1.y = 0x61 + +# Joypad1 L +# (default = 0x64) +input.joypad1.l = 0x64 + +# Joypad1 R +# (default = 0x63) +input.joypad1.r = 0x63 + +# Joypad1 select +# (default = 0x12f) +input.joypad1.select = 0x12f + +# Joypad1 start +# (default = 0xd) +input.joypad1.start = 0xd + diff --git a/src/snes/snes.cpp b/src/snes/snes.cpp index b9564c7e..9370b905 100644 --- a/src/snes/snes.cpp +++ b/src/snes/snes.cpp @@ -55,7 +55,6 @@ int i; for(i=0x2140;i<=0x217f;i++)mem_bus->set_mmio_mapper(i, cpu->mmio); for(i=0x2180;i<=0x2183;i++)mem_bus->set_mmio_mapper(i, cpu->mmio); //input -mem_bus->set_mmio_mapper(0x4000, cpu->mmio); //test -- remove this mem_bus->set_mmio_mapper(0x4016, cpu->mmio); mem_bus->set_mmio_mapper(0x4017, cpu->mmio); for(i=0x4200;i<=0x421f;i++)mem_bus->set_mmio_mapper(i, cpu->mmio); diff --git a/src/snes/snes_input.h b/src/snes/snes_input.h index 73fe859f..26a4940c 100644 --- a/src/snes/snes_input.h +++ b/src/snes/snes_input.h @@ -14,7 +14,7 @@ enum { //The CPU calls poll_input() when the main interface should check the //status of all joypad buttons and cache the results... - virtual void poll_input() = 0; + virtual void poll_input(uint8 type) = 0; //...and then the CPU calls get_input_status() whenever it needs one //of the cached button values to be returned for emulation purposes. diff --git a/src/snes/snes_video.cpp b/src/snes/snes_video.cpp index b273bf6a..eb79e5f6 100644 --- a/src/snes/snes_video.cpp +++ b/src/snes/snes_video.cpp @@ -10,108 +10,66 @@ const uint8 SNES::color_curve_table[32] = { void SNES::update_color_lookup_table() { int i, l, r, g, b; double lr = 0.2126, lg = 0.7152, lb = 0.0722; //luminance - switch(video.depth) { - case 15: //rgb565 - for(i=0;i<32768;i++) { - r = (i ) & 31; - g = (i >> 5) & 31; - b = (i >> 10) & 31; +uint32 col; - if((bool)config::snes.video_color_curve == true) { - r = color_curve_table[r] >> 3; - g = color_curve_table[g] >> 3; - b = color_curve_table[b] >> 3; - } + for(i=0;i<32768;i++) { + //bgr555->rgb888 + col = ((i & 0x001f) << 19) | ((i & 0x001c) << 14) | + ((i & 0x03e0) << 6) | ((i & 0x0380) << 1) | + ((i & 0x7c00) >> 7) | ((i & 0x7000) >> 12); - if((int)config::snes.video_color_adjust_mode == VCA_GRAYSCALE) { - r = (r << 3) | (r >> 2); - g = (g << 3) | (g >> 2); - b = (b << 3) | (b >> 2); + r = (col >> 16) & 0xff; + g = (col >> 8) & 0xff; + b = (col ) & 0xff; - l = int((double)r * lr) + ((double)g * lg) + ((double)b * lb); - if(l < 0)l = 0; - if(l > 255)l = 255; - r = g = b = l; + if((bool)config::snes.video_color_curve == true) { + r = color_curve_table[r >> 3]; + g = color_curve_table[g >> 3]; + b = color_curve_table[b >> 3]; + } - r >>= 3; - g >>= 3; - b >>= 3; - } else if((int)config::snes.video_color_adjust_mode == VCA_VGA) { - //rgb555->rgb332 - r >>= 2; - g >>= 2; - b >>= 3; - - r = (r << 2) | (r >> 1); - g = (g << 2) | (g >> 1); - b = (b << 3) | (b << 1) | (b >> 1); - } else if((int)config::snes.video_color_adjust_mode == VCA_GENESIS) { - //rgb555->rgb333 - r >>= 2; - g >>= 2; - b >>= 2; - - r = (r << 2) | (r >> 1); - g = (g << 2) | (g >> 1); - b = (b << 2) | (b >> 1); - } + if((int)config::snes.video_color_adjust_mode == VCA_GRAYSCALE) { + l = int((double)r * lr) + ((double)g * lg) + ((double)b * lb); + if(l < 0)l = 0; + if(l > 255)l = 255; + r = g = b = l; + } else if((int)config::snes.video_color_adjust_mode == VCA_VGA) { + r &= 0xe0; + g &= 0xe0; + b &= 0xc0; + r |= (r >> 3) | (r >> 6); + g |= (g >> 3) | (g >> 6); + b |= (b >> 2) | (b >> 4) | (b >> 6); + } else if((int)config::snes.video_color_adjust_mode == VCA_GENESIS) { + r &= 0xe0; + g &= 0xe0; + b &= 0xe0; + r |= (r >> 3) | (r >> 6); + g |= (g >> 3) | (g >> 6); + b |= (b >> 3) | (b >> 6); + } + switch(video.depth) { + case 15: + r >>= 3; + g >>= 3; + b >>= 3; color_lookup_table[i] = (r << 10) | (g << 5) | (b); - } - break; - case 16: //rgb565 - for(i=0;i<32768;i++) { - r = (i ) & 31; - g = (i >> 5) & 31; - b = (i >> 10) & 31; - - if((bool)config::snes.video_color_curve == true) { - r = color_curve_table[r] >> 3; - g = color_curve_table[g] >> 2; - b = color_curve_table[b] >> 3; - } else { - g = (g << 1) | (g >> 4); - } - - if((int)config::snes.video_color_adjust_mode == VCA_GRAYSCALE) { - r = (r << 3) | (r >> 2); - g = (g << 2) | (g >> 4); - b = (b << 3) | (b >> 2); - - l = int((double)r * lr) + ((double)g * lg) + ((double)b * lb); - if(l < 0)l = 0; - if(l > 255)l = 255; - r = g = b = l; - - r >>= 3; - g >>= 2; - b >>= 3; - } else if((int)config::snes.video_color_adjust_mode == VCA_VGA) { - //rgb565->rgb332 - r >>= 2; - g >>= 3; - b >>= 3; - - r = (r << 2) | (r >> 1); - g = (g << 3) | (g); - b = (b << 3) | (b << 1) | (b >> 1); - } else if((int)config::snes.video_color_adjust_mode == VCA_GENESIS) { - //rgb565->rgb333 - r >>= 2; - g >>= 3; - b >>= 2; - - r = (r << 2) | (r >> 1); - g = (g << 3) | (g); - b = (b << 2) | (b >> 1); - } - + break; + case 16: + r >>= 3; + g >>= 2; + b >>= 3; color_lookup_table[i] = (r << 11) | (g << 5) | (b); + break; + case 24: + case 32: + color_lookup_table[i] = (r << 16) | (g << 8) | (b); + break; + default: + alert("Error: SNES::update_color_lookup_table() -- color depth %d not supported", video.depth); + break; } - break; - default: - alert("Error: SNES::update_color_lookup_table() -- color depth %d not supported", video.depth); - break; } } @@ -305,32 +263,32 @@ bool field = !cpu->interlace_field(); } void SNES::video_update() { - if(!ppu->render_frame())return; - - if(video.format_changed == true) { - update_video_format(); - } - -uint16 *src = (uint16*)video.ppu_data + ((int(cpu->overscan()) << 3) * 1024); - switch(video.mode) { - case VM_256x224:video_update_256x224(src);break; - case VM_512x224:video_update_512x224(src);break; - case VM_256x448:video_update_256x448(src);break; - case VM_512x448:video_update_512x448(src);break; - case VM_VARIABLE: - switch(int(video.frame_hires) | (int(video.frame_interlace) << 1)) { - case 0:video_update_256x224(src);break; - case 1:video_update_512x224(src);break; - case 2:video_update_256x448(src);break; - case 3:video_update_512x448(src);break; + if(ppu->renderer_enabled()) { + if(video.format_changed == true) { + update_video_format(); } - break; - } -//SNES::capture_screenshot() was called by emulation interface - if(flag_output_screenshot == true) { - output_screenshot(); - flag_output_screenshot = false; + uint16 *src = (uint16*)video.ppu_data + ((int(cpu->overscan()) << 3) * 1024); + switch(video.mode) { + case VM_256x224:video_update_256x224(src);break; + case VM_512x224:video_update_512x224(src);break; + case VM_256x448:video_update_256x448(src);break; + case VM_512x448:video_update_512x448(src);break; + case VM_VARIABLE: + switch(int(video.frame_hires) | (int(video.frame_interlace) << 1)) { + case 0:video_update_256x224(src);break; + case 1:video_update_512x224(src);break; + case 2:video_update_256x448(src);break; + case 3:video_update_512x448(src);break; + } + break; + } + + //SNES::capture_screenshot() was called by emulation interface + if(flag_output_screenshot == true) { + output_screenshot(); + flag_output_screenshot = false; + } } video_run(); diff --git a/src/win/Makefile b/src/win/Makefile index c0dc7955..e4e01a22 100644 --- a/src/win/Makefile +++ b/src/win/Makefile @@ -1,5 +1,5 @@ CC = cl -CFLAGS = /nologo /O2 /Ogityb2 /Gr /Gs +CFLAGS = /nologo /O2 /Ogityb2 /Gr /Gs /DARCH_LSB OBJS = winmain.obj \ libstring.obj libconfig.obj \ reader.obj \ diff --git a/src/win/bsnes.cpp b/src/win/bsnes.cpp index 9bddbd74..18c54999 100644 --- a/src/win/bsnes.cpp +++ b/src/win/bsnes.cpp @@ -114,7 +114,23 @@ void bSNES::run() { } void bSNES::video_run() { - dd_renderer->update(); + if(ppu->status.frames_updated) { + char s[512], t[512]; + ppu->status.frames_updated = false; + if((bool)config::gui.show_fps == true) { + sprintf(s, "%s : %d fps", BSNES_TITLE, ppu->status.frames_executed); + if(w_main->frameskip != 0) { + sprintf(t, " (%d frames)", ppu->status.frames_rendered); + strcat(s, t); + } + SetWindowText(w_main->hwnd, s); + } + } + + w_main->frameskip_pos++; + w_main->frameskip_pos %= (w_main->frameskip + 1); + if(ppu->renderer_enabled())dd_renderer->update(); + ppu->enable_renderer(w_main->frameskip_pos == 0); } void bSNES::sound_run() { @@ -124,30 +140,58 @@ void bSNES::sound_run() { /*********************** *** Input functions *** ***********************/ -void bSNES::poll_input() { +void bSNES::poll_input(uint8 type) { //only capture input when main window has focus if(GetForegroundWindow() == w_main->hwnd) { - joypad1.up = KeyDown(config::input.joypad1.up); - joypad1.down = KeyDown(config::input.joypad1.down); - joypad1.left = KeyDown(config::input.joypad1.left); - joypad1.right = KeyDown(config::input.joypad1.right); - joypad1.select = KeyDown(config::input.joypad1.select); - joypad1.start = KeyDown(config::input.joypad1.start); - joypad1.y = KeyDown(config::input.joypad1.y); - joypad1.b = KeyDown(config::input.joypad1.b); - joypad1.x = KeyDown(config::input.joypad1.x); - joypad1.a = KeyDown(config::input.joypad1.a); - joypad1.l = KeyDown(config::input.joypad1.l); - joypad1.r = KeyDown(config::input.joypad1.r); + switch(type) { + case SNES::DEV_JOYPAD1: + joypad1.up = KeyDown(config::input.joypad1.up); + joypad1.down = KeyDown(config::input.joypad1.down); + joypad1.left = KeyDown(config::input.joypad1.left); + joypad1.right = KeyDown(config::input.joypad1.right); + joypad1.select = KeyDown(config::input.joypad1.select); + joypad1.start = KeyDown(config::input.joypad1.start); + joypad1.y = KeyDown(config::input.joypad1.y); + joypad1.b = KeyDown(config::input.joypad1.b); + joypad1.x = KeyDown(config::input.joypad1.x); + joypad1.a = KeyDown(config::input.joypad1.a); + joypad1.l = KeyDown(config::input.joypad1.l); + joypad1.r = KeyDown(config::input.joypad1.r); + break; + case SNES::DEV_JOYPAD2: + joypad2.up = KeyDown(config::input.joypad2.up); + joypad2.down = KeyDown(config::input.joypad2.down); + joypad2.left = KeyDown(config::input.joypad2.left); + joypad2.right = KeyDown(config::input.joypad2.right); + joypad2.select = KeyDown(config::input.joypad2.select); + joypad2.start = KeyDown(config::input.joypad2.start); + joypad2.y = KeyDown(config::input.joypad2.y); + joypad2.b = KeyDown(config::input.joypad2.b); + joypad2.x = KeyDown(config::input.joypad2.x); + joypad2.a = KeyDown(config::input.joypad2.a); + joypad2.l = KeyDown(config::input.joypad2.l); + joypad2.r = KeyDown(config::input.joypad2.r); + break; + } } else { - joypad1.up = joypad1.down = joypad1.left = joypad1.right = - joypad1.select = joypad1.start = - joypad1.y = joypad1.b = joypad1.x = joypad1.a = - joypad1.l = joypad1.r = 0; + switch(type) { + case SNES::DEV_JOYPAD1: + joypad1.up = joypad1.down = joypad1.left = joypad1.right = + joypad1.select = joypad1.start = + joypad1.y = joypad1.b = joypad1.x = joypad1.a = + joypad1.l = joypad1.r = 0; + break; + case SNES::DEV_JOYPAD2: + joypad2.up = joypad2.down = joypad2.left = joypad2.right = + joypad2.select = joypad2.start = + joypad2.y = joypad2.b = joypad2.x = joypad2.a = + joypad1.l = joypad2.r = 0; + break; + } } //check for debugger-based key locks - if(is_debugger_enabled == true) { + if(is_debugger_enabled == true && type == SNES::DEV_JOYPAD1) { if(w_console->joypad_lock.up )joypad1.up = true; if(w_console->joypad_lock.down )joypad1.down = true; if(w_console->joypad_lock.left )joypad1.left = true; @@ -167,6 +211,9 @@ void bSNES::poll_input() { //this to happen causes glitches in many SNES games. if(joypad1.up) joypad1.down = 0; if(joypad1.left)joypad1.right = 0; + + if(joypad2.up) joypad2.down = 0; + if(joypad2.left)joypad2.right = 0; } bool bSNES::get_input_status(uint8 device, uint8 button) { @@ -187,7 +234,24 @@ bool bSNES::get_input_status(uint8 device, uint8 button) { case JOYPAD_START: return joypad1.start; } break; + case DEV_JOYPAD2: + switch(button) { + case JOYPAD_UP: return joypad2.up; + case JOYPAD_DOWN: return joypad2.down; + case JOYPAD_LEFT: return joypad2.left; + case JOYPAD_RIGHT: return joypad2.right; + case JOYPAD_A: return joypad2.a; + case JOYPAD_B: return joypad2.b; + case JOYPAD_X: return joypad2.x; + case JOYPAD_Y: return joypad2.y; + case JOYPAD_L: return joypad2.l; + case JOYPAD_R: return joypad2.r; + case JOYPAD_SELECT:return joypad2.select; + case JOYPAD_START: return joypad2.start; + } + break; } + return 0; } diff --git a/src/win/bsnes.h b/src/win/bsnes.h index 41165c76..d0f3340a 100644 --- a/src/win/bsnes.h +++ b/src/win/bsnes.h @@ -43,7 +43,7 @@ enum { DRAM = 0, SPCRAM = 1, VRAM = 2, OAM = 3, CGRAM = 4 }; uint32 get_status(); //input functions - void poll_input(); + void poll_input(uint8 type); bool get_input_status(uint8 device, uint8 button); //debugging functions diff --git a/src/win/bsnes.res b/src/win/bsnes.res new file mode 100644 index 00000000..412d6082 Binary files /dev/null and b/src/win/bsnes.res differ diff --git a/src/win/config.cpp b/src/win/config.cpp index 8f2427ca..c0a92860 100644 --- a/src/win/config.cpp +++ b/src/win/config.cpp @@ -20,21 +20,36 @@ struct GUI { Setting GUI::show_fps(&config_file, "gui.show_fps", "Show framerate in window title", true, Setting::TRUE_FALSE); struct Input { - struct Joypad { + struct Joypad1 { static Setting up, down, left, right, a, b, x, y, l, r, select, start; } joypad1; + struct Joypad2 { + static Setting up, down, left, right, a, b, x, y, l, r, select, start; + } joypad2; } input; -Setting Input::Joypad::up (&config_file, "input.joypad1.up", "Joypad1 up", VK_UP, Setting::HEX); -Setting Input::Joypad::down (&config_file, "input.joypad1.down", "Joypad1 down", VK_DOWN, Setting::HEX); -Setting Input::Joypad::left (&config_file, "input.joypad1.left", "Joypad1 left", VK_LEFT, Setting::HEX); -Setting Input::Joypad::right (&config_file, "input.joypad1.right", "Joypad1 right", VK_RIGHT, Setting::HEX); -Setting Input::Joypad::a (&config_file, "input.joypad1.a", "Joypad1 A", 'X', Setting::HEX); -Setting Input::Joypad::b (&config_file, "input.joypad1.b", "Joypad1 B", 'Z', Setting::HEX); -Setting Input::Joypad::x (&config_file, "input.joypad1.x", "Joypad1 X", 'S', Setting::HEX); -Setting Input::Joypad::y (&config_file, "input.joypad1.y", "Joypad1 Y", 'A', Setting::HEX); -Setting Input::Joypad::l (&config_file, "input.joypad1.l", "Joypad1 L", 'D', Setting::HEX); -Setting Input::Joypad::r (&config_file, "input.joypad1.r", "Joypad1 R", 'C', Setting::HEX); -Setting Input::Joypad::select(&config_file, "input.joypad1.select", "Joypad1 select", VK_SHIFT, Setting::HEX); -Setting Input::Joypad::start (&config_file, "input.joypad1.start", "Joypad1 start", VK_RETURN, Setting::HEX); +Setting Input::Joypad1::up (&config_file, "input.joypad1.up", "Joypad1 up", VK_UP, Setting::HEX); +Setting Input::Joypad1::down (&config_file, "input.joypad1.down", "Joypad1 down", VK_DOWN, Setting::HEX); +Setting Input::Joypad1::left (&config_file, "input.joypad1.left", "Joypad1 left", VK_LEFT, Setting::HEX); +Setting Input::Joypad1::right (&config_file, "input.joypad1.right", "Joypad1 right", VK_RIGHT, Setting::HEX); +Setting Input::Joypad1::a (&config_file, "input.joypad1.a", "Joypad1 A", 'X', Setting::HEX); +Setting Input::Joypad1::b (&config_file, "input.joypad1.b", "Joypad1 B", 'Z', Setting::HEX); +Setting Input::Joypad1::x (&config_file, "input.joypad1.x", "Joypad1 X", 'S', Setting::HEX); +Setting Input::Joypad1::y (&config_file, "input.joypad1.y", "Joypad1 Y", 'A', Setting::HEX); +Setting Input::Joypad1::l (&config_file, "input.joypad1.l", "Joypad1 L", 'D', Setting::HEX); +Setting Input::Joypad1::r (&config_file, "input.joypad1.r", "Joypad1 R", 'C', Setting::HEX); +Setting Input::Joypad1::select(&config_file, "input.joypad1.select", "Joypad1 select", VK_SHIFT, Setting::HEX); +Setting Input::Joypad1::start (&config_file, "input.joypad1.start", "Joypad1 start", VK_RETURN, Setting::HEX); +Setting Input::Joypad2::up (&config_file, "input.joypad2.up", "Joypad2 up", 'T', Setting::HEX); +Setting Input::Joypad2::down (&config_file, "input.joypad2.down", "Joypad2 down", 'G', Setting::HEX); +Setting Input::Joypad2::left (&config_file, "input.joypad2.left", "Joypad2 left", 'F', Setting::HEX); +Setting Input::Joypad2::right (&config_file, "input.joypad2.right", "Joypad2 right", 'H', Setting::HEX); +Setting Input::Joypad2::a (&config_file, "input.joypad2.a", "Joypad2 A", 'K', Setting::HEX); +Setting Input::Joypad2::b (&config_file, "input.joypad2.b", "Joypad2 B", 'J', Setting::HEX); +Setting Input::Joypad2::x (&config_file, "input.joypad2.x", "Joypad2 X", 'I', Setting::HEX); +Setting Input::Joypad2::y (&config_file, "input.joypad2.y", "Joypad2 Y", 'U', Setting::HEX); +Setting Input::Joypad2::l (&config_file, "input.joypad2.l", "Joypad2 L", 'O', Setting::HEX); +Setting Input::Joypad2::r (&config_file, "input.joypad2.r", "Joypad2 R", 'L', Setting::HEX); +Setting Input::Joypad2::select(&config_file, "input.joypad2.select", "Joypad2 select", '[', Setting::HEX); +Setting Input::Joypad2::start (&config_file, "input.joypad2.start", "Joypad2 start", ']', Setting::HEX); }; diff --git a/src/win/dd_renderer.cpp b/src/win/dd_renderer.cpp index 0e6d88ec..47fae633 100644 --- a/src/win/dd_renderer.cpp +++ b/src/win/dd_renderer.cpp @@ -164,33 +164,16 @@ int rx, ry; break; } } + if(config::video.vblank) { lpdd->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, 0); } + hr = lpdds->Blt(&rd, lpddsb, &lpddrc, DDBLT_WAIT, 0); if(hr == DDERR_SURFACELOST) { lpdds->Restore(); lpddsb->Restore(); } - - if((int)config::gui.show_fps == false || bsnes->get_status() == bSNES::STOP)return; -uint32 fps; -char s[256], t[256]; - fps_timer->tick(); - if(fps_timer->second_passed() == true) { - sprintf(s, BSNES_TITLE); - if(rom_image->loaded() == true) { - fps = fps_timer->get_ticks(); - if(w_main->frameskip == 0) { - sprintf(t, " : %d fps", fps); - } else { - sprintf(t, " : %d fps [fs: %d]", fps * (1 + w_main->frameskip), w_main->frameskip); - } - strcat(s, t); - } - SetWindowText(w_main->hwnd, s); - fps_timer->reset(); - } } #include "dd_renderer16.cpp" diff --git a/src/win/timer.cpp b/src/win/timer.cpp deleted file mode 100644 index 36e973fc..00000000 --- a/src/win/timer.cpp +++ /dev/null @@ -1,43 +0,0 @@ -class fpstimer { -private: -SYSTEMTIME st; -int second, ticks; -public: - void start(); - void tick(); - int get_ticks(); - void reset(); - bool second_passed(); - - fpstimer(); -}; - -void fpstimer::start() { - GetSystemTime(&st); - second = st.wSecond; -} - -void fpstimer::tick() { - ticks++; -} - -int fpstimer::get_ticks() { - return ticks; -} - -void fpstimer::reset() { - ticks = 0; -} - -bool fpstimer::second_passed() { - GetSystemTime(&st); - if(st.wSecond != second) { - second = st.wSecond; - return true; - } - return false; -} - -fpstimer::fpstimer() { - second = ticks = 0; -} diff --git a/src/win/ui.h b/src/win/ui.h index 0ece67b8..9619a420 100644 --- a/src/win/ui.h +++ b/src/win/ui.h @@ -39,6 +39,7 @@ enum { MENU_SETTINGS_SHOWFPS, MENU_SETTINGS_MUTE, MENU_SETTINGS_INPUTCFG_JOYPAD1, + MENU_SETTINGS_INPUTCFG_JOYPAD2, MENU_SETTINGS_DEBUGGER, MENU_MISC_SCREENSHOT, MENU_MISC_LOGAUDIO, @@ -164,7 +165,7 @@ struct { int width, height; }window; class MainWindow : public Window { public: -uint8 frameskip; +uint8 frameskip, frameskip_pos; int width, height; void create(); void to_fullscreen(); @@ -256,7 +257,8 @@ bool auto_update; //update memory window whenever visible value is written to class InputConfig : public Window { public: enum { - JOYPAD1 = 0 + JOYPAD1 = 0, + JOYPAD2 }; enum { ID_COMMAND = 100 diff --git a/src/win/ui_inputconfig.cpp b/src/win/ui_inputconfig.cpp index 258603b6..05af8d69 100644 --- a/src/win/ui_inputconfig.cpp +++ b/src/win/ui_inputconfig.cpp @@ -53,18 +53,40 @@ bool end_config = false; case 12:end_config = true;break; } config_pos++; + break; + case JOYPAD2: + switch(config_pos) { + case 0:config::input.joypad2.up = key;break; + case 1:config::input.joypad2.down = key;break; + case 2:config::input.joypad2.left = key;break; + case 3:config::input.joypad2.right = key;break; + case 4:config::input.joypad2.select = key;break; + case 5:config::input.joypad2.start = key;break; + case 6:config::input.joypad2.x = key;break; + case 7:config::input.joypad2.y = key;break; + case 8:config::input.joypad2.a = key;break; + case 9:config::input.joypad2.b = key;break; + case 10:config::input.joypad2.l = key;break; + case 11:config::input.joypad2.r = key;break; + case 12:end_config = true;break; + } + config_pos++; + break; } + if(end_config == true) { polling = false; hide(); return; } + update_command(); } void InputConfig::update_command() { switch(config_type) { case JOYPAD1: + case JOYPAD2: switch(config_pos) { case 0:set_text("Press key for Up..."); break; case 1:set_text("Press key for Down..."); break; @@ -90,17 +112,23 @@ void InputConfig::begin_config(int type) { SetFocus(hwnd); return; } + config_type = type; config_pos = 0; switch(config_type) { case JOYPAD1: - SetWindowText(hwnd, "bsnes Input Configuration : Joypad1"); + SetWindowText(hwnd, "bsnes Input Configuration (Joypad1)"); + break; + case JOYPAD2: + SetWindowText(hwnd, "bsnes Input Configuration (Joypad2)"); break; default: return; } + polling = true; update_command(); + center(); show(); } diff --git a/src/win/ui_main.cpp b/src/win/ui_main.cpp index 2b2c863b..0ad3aca6 100644 --- a/src/win/ui_main.cpp +++ b/src/win/ui_main.cpp @@ -11,8 +11,8 @@ void MainWindow::set_frameskip(uint8 fs) { CheckMenuItem(w_main->hmenu, MENU_SETTINGS_FRAMESKIP_9, MF_UNCHECKED); CheckMenuItem(w_main->hmenu, MENU_SETTINGS_FRAMESKIP_OFF + fs, MF_CHECKED); - ppu->set_frameskip(fs); - w_main->frameskip = fs; + w_main->frameskip = fs; + w_main->frameskip_pos = 0; } void MainWindow::to_fullscreen() { @@ -239,6 +239,9 @@ int i; case MENU_SETTINGS_INPUTCFG_JOYPAD1: w_inputconfig->begin_config(InputConfig::JOYPAD1); break; + case MENU_SETTINGS_INPUTCFG_JOYPAD2: + w_inputconfig->begin_config(InputConfig::JOYPAD2); + break; case MENU_SETTINGS_DEBUGGER: if(bsnes->debugger_enabled() == true) { bsnes->debugger_disable(); @@ -350,6 +353,7 @@ HMENU hsubmenu, hbranchmenu; hbranchmenu = CreatePopupMenu(); AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_INPUTCFG_JOYPAD1, "Joypad 1"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_INPUTCFG_JOYPAD2, "Joypad 2"); AppendMenu(hsubmenu, MF_STRING | MF_POPUP, (unsigned int)hbranchmenu, "&Configure Input Devices"); AppendMenu(hsubmenu, MF_SEPARATOR, 0, ""); diff --git a/src/win/winmain.cpp b/src/win/winmain.cpp index 9ae050a4..7778e009 100644 --- a/src/win/winmain.cpp +++ b/src/win/winmain.cpp @@ -15,9 +15,6 @@ #include "dd_renderer.h" #include "ds_sound.h" -#include "timer.cpp" -fpstimer *fps_timer; - #include "lib.cpp" #include "rom.cpp" @@ -27,7 +24,6 @@ fpstimer *fps_timer; void init_snes() { mem_bus = new bMemBus(); cpu = new bCPU(); -cpu->cpu_version = 1; apu = new bAPU(); dsp = new bDSP(); ppu = new bPPU(); @@ -82,8 +78,6 @@ string cfg_fn; get_config_fn(cfg_fn); config_file.load(cfg_fn); meminit(); - fps_timer = new fpstimer(); - fps_timer->start(); rom_image = new ROMImage(); init_ui0();