Update to bsnes v013 release.

- Greatly improved HDMA timing and accuracy with help from anomie and DMV27 -- fixes bugs in Energy Breaker and Street Fighter Alpha 2
    - Fixed a problem with color add/sub code -- fixes opening battle in Tales of Phantasia and clouds in Energy Breaker
    - Temporarily added DMV27's bugfix for the DSP KON register -- fixes sound in Der Langrisser, but this is not a hardware-accurate fix
    - Disabled VRAM writes outside of vblank -- fixes Hook, but breaks many PD ROMs and fan translations (Roto's BS Zelda hack, Gideon Zhi's Ys 4 translation, etc). I might add an option in the future to toggle this behavior, but for now these games will no longer work. Please keep in mind these games will not run properly on real SNES hardware, either.
    - Improved frameskipping code thanks to a suggestion from Richard Bannister
    - Misc. other code cleanups and improvements (notably in the color table generation code)
    - bsnes is now endian-safe and runs on Mac OS X
    - Added caching support for window clipping tables resulting in a slight speedup. Please let me know if you spot any errors as a result of this change.
This commit is contained in:
byuu 2005-10-23 23:32:30 +00:00
parent 397b9c4505
commit c6c5f4669c
56 changed files with 1393 additions and 987 deletions

View File

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

BIN
bsnes.exe

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

View File

@ -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 <time.h>
#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_

View File

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

View File

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

View File

@ -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_index<xferlen;channel[i].read_index++) {
hdma_write(i, hdma_read(i));
}
}
channel[i].hdma_line_counter--;
if(channel[i].hdma_first_line == false && channel[i].hdma_repeat == false)continue;
channel[i].hdma_first_line = false;
xferlen = hdma_xferlen[channel[i].xfermode];
for(l=0;l<xferlen;l++) {
if(channel[i].hdma_indirect == false) {
x = mem_bus->read(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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,6 +2,6 @@
#include "dcpu.cpp"
CPU::CPU() {
cpu_version = 2;
cpu_version = 1;
mmio = &mmio_unmapped;
}

View File

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

View File

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

View File

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

View File

@ -63,7 +63,7 @@ struct Status {
int8 FIR[8];
//internal variables
uint8 kon;
//uint8 kon;
bool key_flag;
int16 noise_ctr, noise_rate;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 <palette data>
//t = BBGGGRRR <tilemap data>
//r = 0BBb00GGGg0RRRr0 <return data>
return ((t & 7) << 2) | ((p & 1) << 1) |
(((t >> 3) & 7) << 7) | (((p >> 1) & 1) << 6) |
((t >> 6) << 13) | ((p >> 2) << 12);
(((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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
CC = c++
CFLAGS = -O2
CFLAGS = -O3 -fomit-frame-pointer -ffast-math
OBJS = sdlmain.o \
libstring.o libconfig.o \
reader.o \

View File

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

View File

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

View File

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

86
src/sdl/bsnes_sdl.cfg Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

BIN
src/win/bsnes.res Normal file

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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