Compare commits

...
3 Commits
Author SHA1 Message Date
byuu 7dec0b2a3c Update to bsnes v014 release.
This version adds speed regulation, greatly improves PPU rendering, and increases speed by ~30% over the previous version.
Changelog:
    - Rewrote offset-per-tile mode emulation, should be correct now. Fixes Chrono Trigger, Contra III, Tetris Attack, etc.
    - Fixed a bug with HDMA occuring during interrupts. Fixes Tales of Phantasia souond test screen
    - Updated compiler to Visual Studio 2005, and enabled profile guided optimizations
    - Added conditional compilation of debugging functions (faster without them)
    - Added conditional compilation of core classes as pointers (allowing polymorphism) or objects (allowing inlining). The latter results in a speed increase
    - Small fixes to BG and OAM rendering routines
    - Corrected sprite tile bounds wrapping
    - Corrected sprite rendering in hires video modes
    - Rewrote color add/sub routines, should be correct now. Fixes Illusion of Gaia menu, etc.
    - Optimized video blitting routines, will temporarilly break mixed video mode screenshots
    - Prevented selecting menu options via return key from being recognized as keypresses by the emulator
    - Added system speed regulation (60hz/NTSC or 50hz/PAL)! Many thanks to kode54, GIGO, and Richard Bannister for their assistance
I disabled the debugger and polymorphism, and enabled profile guided optimizations for this build, to maximize speed. The debugger and polymorphism can be re-enabled via uncommenting the respective #defines in src/base.h and recompiling, or bsnes v0.013 can be used. I may start releasing two separate builds in the future... not sure yet.
2005-11-12 16:49:26 +00:00
byuu f288280ceb Update to bsnes v013r02 release.
[No changelog available]
2005-10-25 23:25:28 +00:00
byuu c6c5f4669c 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.
2005-10-23 23:32:30 +00:00
90 changed files with 2601 additions and 2487 deletions

No files matched your search

+54 -2
View File
@@ -14,7 +14,11 @@ snes.video_color_adjust_mode = 0
# Mutes SNES audio output when enabled
# (default = true)
snes.mute = true
snes.mute = false
# Regulate speed to 60hz (NTSC) / 50hz (PAL)
# (default = true)
system.regulate_speed = true
# Video mode
# 0 = 256x224w
@@ -35,7 +39,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 +89,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
View File
Binary file not shown.
+5
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.
+7 -6
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;
+13 -7
View File
@@ -24,13 +24,13 @@ uint8 r;
break;
case 0xf3: //DSPDATA
//0x80-0xff is a read-only mirror of 0x00-0x7f
r = dsp->read(status.dsp_addr & 0x7f);
r = r_dsp->read(status.dsp_addr & 0x7f);
break;
case 0xf4: //CPUIO0
case 0xf5: //CPUIO1
case 0xf6: //CPUIO2
case 0xf7: //CPUIO3
r = cpu->port_read(addr & 3);
r = r_cpu->port_read(addr & 3);
break;
case 0xf8: //???
case 0xf9: //??? -- Mapped to SPCRAM
@@ -63,7 +63,10 @@ uint8 r;
r = spcram[addr];
}
}
#ifdef DEBUGGER
snes->notify(SNES::SPCRAM_READ, addr, r);
#endif
return r;
}
@@ -78,12 +81,12 @@ void bAPU::spcram_write(uint16 addr, uint8 value) {
//one-time clearing of APU port read registers,
//emulated by simulating CPU writes of 0x00
if(value & 0x20) {
cpu->port_write(2, 0x00);
cpu->port_write(3, 0x00);
r_cpu->port_write(2, 0x00);
r_cpu->port_write(3, 0x00);
}
if(value & 0x10) {
cpu->port_write(0, 0x00);
cpu->port_write(1, 0x00);
r_cpu->port_write(0, 0x00);
r_cpu->port_write(1, 0x00);
}
//0->1 transistion resets timers
@@ -111,7 +114,7 @@ void bAPU::spcram_write(uint16 addr, uint8 value) {
case 0xf3: //DSPDATA
//0x80-0xff is a read-only mirror of 0x00-0x7f
if(status.dsp_addr < 0x80) {
dsp->write(status.dsp_addr & 0x7f, value);
r_dsp->write(status.dsp_addr & 0x7f, value);
}
break;
case 0xf4: //CPUIO0
@@ -143,7 +146,10 @@ void bAPU::spcram_write(uint16 addr, uint8 value) {
//even if the iplrom is enabled.
spcram[addr] = value;
}
#ifdef DEBUGGER
snes->notify(SNES::SPCRAM_WRITE, addr, value);
#endif
}
uint8 bAPU::port_read(uint8 port) {
+12 -8
View File
@@ -1,18 +1,22 @@
void bAPU::exec_cycle() {
uint8 op;
if(status.cycle_pos == 0) {
op = spcram_read(regs.pc);
snes->notify(SNES::APU_EXEC_OPCODE_BEGIN);
status.opcode = op_read();
status.cycle_pos = 1;
add_cycles(1);
} else {
if(status.cycle_pos) {
(this->*optbl[status.opcode])();
add_cycles(1);
if(status.cycle_pos == 0) {
#ifdef DEBUGGER
snes->notify(SNES::APU_EXEC_OPCODE_END);
#endif
}
return;
}
//on first cycle?
#ifdef DEBUGGER
snes->notify(SNES::APU_EXEC_OPCODE_BEGIN);
#endif
status.opcode = op_read();
status.cycle_pos = 1;
add_cycles(1);
}
//only return true when we are on an opcode edge
+5 -3
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;
+12 -3
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();
-171
View File
@@ -1,171 +0,0 @@
#include "../../base.h"
uint8 bAPUSkip::spcram_read (uint16 addr) { return 0xff; }
void bAPUSkip::spcram_write(uint16 addr, uint8 value) {}
/*
This routine is very serious. It will eat holes through
the ROM to skip APU test conditions. Or in other words,
it will disable and/or force branches when neccesary.
It can very easily break or corrupt a game and prevent it
from being playable until the ROM is reloaded (ROM writes
are only performed in memory, of course).
However, this kind of brute force approach is required to
get many games playable without proper SPC700 emulation.
*/
uint8 bAPUSkip::port_read(uint8 port) {
port &= 3;
_apu_port *p = &apu_port[port];
int i, x, y, z, t;
uint32 addr;
addr = cpu->regs.pc.d;
p->read_addr[p->read_pos & 31] = addr;
//- lda $214x
// cmp $214x
// bne -
// cmp ???
// beq/bne -
__test1:
//look for an lda/cmp read pattern
if(addr == p->read_addr[(p->read_pos - 1) & 31])goto __test2;
if(addr != p->read_addr[(p->read_pos - 2) & 31])goto __test2;
if(addr == p->read_addr[(p->read_pos - 3) & 31])goto __test2;
if(addr != p->read_addr[(p->read_pos - 4) & 31])goto __test2;
if(p->read_addr[(p->read_pos - 1) & 31] != p->read_addr[(p->read_pos - 3) & 31])goto __test2;
//try and find compare opcode
for(i=0;i<24;i++) {
x = mem_bus->read(addr + i);
if(x == OP_CMP_CONST || x == OP_CPX_CONST || x == OP_CPY_CONST)break;
if(x == OP_CMP_ADDR || x == OP_CPX_ADDR || x == OP_CPY_ADDR) break;
if(x == OP_CMP_LONG)break;
}
if(i == 24)goto __test2;
//seek to next opcode
if(x == OP_CMP_CONST) {
i += (cpu->regs.p.m)?2:3;
} else if(x == OP_CPX_CONST || x == OP_CPY_CONST) {
i += (cpu->regs.p.x)?2:3;
} else if(x == OP_CMP_ADDR || x == OP_CPX_ADDR || x == OP_CPY_ADDR) {
i += 3;
} else { //(x == OP_CMP_LONG) {
i += 4;
}
x = mem_bus->read(addr + i);
if(x == OP_BNE) {
mem_bus->cart->write_protect(false);
mem_bus->write(addr + i, OP_NOP);
mem_bus->write(addr + i + 1, OP_NOP);
mem_bus->cart->write_protect(true);
} else if(x == OP_BEQ) {
mem_bus->cart->write_protect(false);
mem_bus->write(addr + i, OP_BRA);
mem_bus->cart->write_protect(true);
} else goto __test2;
goto __pass;
//- lda $214x
// cmp ???
// beq/bne -
__test2:
//look for a repeated read pattern
if(addr != p->read_addr[(p->read_pos - 1) & 31])goto __test3;
if(addr != p->read_addr[(p->read_pos - 2) & 31])goto __test3;
if(addr != p->read_addr[(p->read_pos - 3) & 31])goto __test3;
if(addr != p->read_addr[(p->read_pos - 4) & 31])goto __test3;
if(addr != p->read_addr[(p->read_pos - 5) & 31])goto __test3;
if(addr != p->read_addr[(p->read_pos - 6) & 31])goto __test3;
if(addr != p->read_addr[(p->read_pos - 7) & 31])goto __test3;
if(addr != p->read_addr[(p->read_pos - 8) & 31])goto __test3;
//try and find compare opcode
for(i=0;i<24;i++) {
x = mem_bus->read(addr + i);
if(x == OP_CMP_CONST || x == OP_CPX_CONST || x == OP_CPY_CONST)break;
if(x == OP_CMP_ADDR || x == OP_CPX_ADDR || x == OP_CPY_ADDR) break;
if(x == OP_CMP_LONG)break;
}
if(i == 24)goto __test3;
//seek to next opcode
if(x == OP_CMP_CONST) {
i += (cpu->regs.p.m)?2:3;
} else if(x == OP_CPX_CONST || x == OP_CPY_CONST) {
i += (cpu->regs.p.x)?2:3;
} else if(x == OP_CMP_ADDR || x == OP_CPX_ADDR || x == OP_CPY_ADDR) {
i += 3;
} else if(x == OP_CMP_LONG) {
i += 4;
}
x = mem_bus->read(addr + i);
if(x == OP_BNE) {
mem_bus->cart->write_protect(false);
mem_bus->write(addr + i, OP_NOP);
mem_bus->write(addr + i + 1, OP_NOP);
mem_bus->cart->write_protect(true);
} else if(x == OP_BEQ) {
mem_bus->cart->write_protect(false);
mem_bus->write(addr + i, OP_BRA);
mem_bus->cart->write_protect(true);
} else goto __test3;
goto __pass;
//fallback
__test3:
if(p->pos < 4) {
if(!(port & 1)) {
p->value = cpu->regs.a.l;
} else {
p->value = cpu->regs.a.h;
}
} else if(p->pos < 8) {
if(!(port & 1)) {
p->value = cpu->regs.x.l;
} else {
p->value = cpu->regs.x.h;
}
} else if(p->pos < 12) {
if(!(port & 1)) {
p->value = cpu->regs.y.l;
} else {
p->value = cpu->regs.y.h;
}
} else if(p->pos < 16) {
p->value = rand();
}
if(++p->pos == 16)p->pos = 0;
__pass:
p->read_pos++;
p->read_pos &= 31;
return p->value;
}
void bAPUSkip::port_write(uint8 port, uint8 value) {
port &= 3;
apu_port[port].value = value;
}
void bAPUSkip::run() {
snes->notify(SNES::APU_EXEC_OPCODE_BEGIN);
snes->notify(SNES::APU_EXEC_OPCODE_END);
}
void bAPUSkip::power() {
reset();
}
void bAPUSkip::reset() {
regs.a = 0x00;
regs.x = 0x00;
regs.y = 0x00;
regs.sp = 0x00;
regs.p = 0x02;
regs.pc = 0xffc0;
memset(&apu_port[0], 0, sizeof(_apu_port));
memset(&apu_port[1], 0, sizeof(_apu_port));
memset(&apu_port[2], 0, sizeof(_apu_port));
memset(&apu_port[3], 0, sizeof(_apu_port));
}
-37
View File
@@ -1,37 +0,0 @@
class bAPUSkip : public APU {
private:
uint8 spcram[65536];
public:
struct _apu_port {
uint8 value;
uint8 step, pos;
uint32 read_addr[32], read_pos;
}apu_port[4];
enum {
OP_CMP_CONST = 0xc9,
OP_CPX_CONST = 0xe0,
OP_CPY_CONST = 0xc0,
OP_CMP_ADDR = 0xcd,
OP_CPX_ADDR = 0xec,
OP_CPY_ADDR = 0xcc,
OP_CMP_LONG = 0xcf,
OP_BNE = 0xd0,
OP_BEQ = 0xf0,
OP_BRA = 0x80,
OP_NOP = 0xea
};
uint8 spcram_read (uint16 addr);
void spcram_write(uint16 addr, uint8 value);
uint8 port_read (uint8 port);
void port_write (uint8 port, uint8 value);
uint8 *get_spcram_handle() { return spcram; }
void run();
uint32 cycles_executed() { return 12 * 24; }
void power();
void reset();
};
+21
View File
@@ -1,9 +1,30 @@
//#define DEBUGGER
//#define POLYMORPHISM
//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_
+2 -2
View File
@@ -6,7 +6,7 @@ void SDD1::init() {
void SDD1::enable() {
for(int i=0x4800;i<=0x4807;i++) {
mem_bus->set_mmio_mapper(i, mmio);
r_mem->set_mmio_mapper(i, mmio);
}
}
@@ -47,7 +47,7 @@ uint8 SDD1::mmio_read(uint16 addr) {
case 0x4807:return (sdd1.index[3] >> 20) & 7;
}
return cpu->regs.mdr;
return r_cpu->regs.mdr;
}
void SDD1::mmio_write(uint16 addr, uint8 data) {
+1 -1
View File
@@ -28,7 +28,7 @@ understood.
************************************************************************/
#define SDD1_read(__addr) (mem_bus->read(__addr))
#define SDD1_read(__addr) (r_mem->read(__addr))
////////////////////////////////////////////////////
+3 -3
View File
@@ -78,8 +78,8 @@ void SRTC::init() {
}
void SRTC::enable() {
mem_bus->set_mmio_mapper(0x2800, mmio);
mem_bus->set_mmio_mapper(0x2801, mmio);
r_mem->set_mmio_mapper(0x2800, mmio);
r_mem->set_mmio_mapper(0x2801, mmio);
}
void SRTC::power() {
@@ -176,7 +176,7 @@ uint8 SRTCMMIO::read(uint32 addr) {
case 0x2800:return srtc->read();
}
return cpu->regs.mdr;
return r_cpu->regs.mdr;
}
void SRTCMMIO::write(uint32 addr, uint8 value) {
+57 -44
View File
@@ -18,54 +18,61 @@
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(status.cycle_pos == 0) {
//interrupts only trigger on opcode edges
if(!run_state.irq && !run_state.stp) {
if(time.nmi_pending == true) {
time.nmi_pending = false;
aa.w = 0xffea;
run_state.irq = true;
} else if(time.irq_pending == true) {
time.irq_pending = false;
aa.w = 0xffee;
run_state.irq = true;
}
}
}
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() {
@@ -78,7 +85,7 @@ void bCPU::power() {
void bCPU::reset() {
//reset vector location
regs.pc = mem_bus->read(0xfffc) | (mem_bus->read(0xfffd) << 8);
regs.pc = r_mem->read(0xfffc) | (r_mem->read(0xfffd) << 8);
//registers are not fully reset by SNES
regs.x.h = 0x00;
@@ -94,21 +101,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,32 +154,32 @@ 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);
cycle_edge();
}
uint8 bCPU::mem_read(uint32 addr) {
if(status.is_last_cycle)last_cycle_exec();
status.cycle_count = mem_bus->speed(addr);
status.cycle_count = r_mem->speed(addr);
pre_exec_cycle();
add_cycles(status.cycle_count - 4);
regs.mdr = mem_bus->read(addr);
regs.mdr = r_mem->read(addr);
add_cycles(4);
cycle_edge();
return regs.mdr;
}
void bCPU::mem_write(uint32 addr, uint8 value) {
if(status.is_last_cycle)last_cycle_exec();
status.cycle_count = mem_bus->speed(addr);
status.cycle_count = r_mem->speed(addr);
pre_exec_cycle();
add_cycles(status.cycle_count);
mem_bus->write(addr, value);
r_mem->write(addr, value);
cycle_edge();
}
uint32 bCPU::op_addr(uint8 mode, uint32 addr) {
+55 -29
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
+124 -94
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;
}
@@ -10,14 +25,10 @@ uint8 x;
if(sdd1->dma_active() == true) {
x = sdd1->dma_read();
} else {
x = mem_bus->read(dma_addr(i));
x = r_mem->read(dma_addr(i));
}
mem_bus->write(0x2100 | ((channel[i].destaddr + index) & 0xff), x);
if(channel[i].fixedxfer == false) {
channel[i].srcaddr += channel[i].incmode;
}
r_mem->write(0x2100 | ((channel[i].destaddr + index) & 0xff), x);
add_cycles(8);
channel[i].xfersize--;
@@ -25,12 +36,8 @@ uint8 x;
void bCPU::dma_mmiotocpu(uint8 i, uint8 index) {
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;
}
x = r_mem->read(0x2100 | ((channel[i].destaddr + index) & 0xff));
r_mem->write(dma_addr(i), x);
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 r_mem->read(hdma_mmio(i));
} else if(!channel[i].hdma_indirect) {
return r_mem->read(hdma_addr(i));
} else {
return r_mem->read(hdma_iaddr(i));
}
}
void bCPU::hdma_write(uint8 i, uint8 x) {
if(channel[i].direction == DMA_CPUTOMMIO) {
r_mem->write(hdma_mmio(i), x);
} else if(!channel[i].hdma_indirect) {
r_mem->write(hdma_addr(i), x);
} else {
r_mem->write(hdma_iaddr(i), x);
}
add_cycles(8);
hdma_add_cycles(8);
}
void bCPU::hdma_update(uint8 i) {
channel[i].hdma_line_counter = r_mem->read(hdma_addr(i));
add_cycles(8);
hdma_add_cycles(8);
if(channel[i].hdma_indirect) {
channel[i].hdma_iaddr = r_mem->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 |= r_mem->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;
}
}
+145 -40
View File
@@ -1,64 +1,169 @@
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
cycle_edge();
break;
case DMASTATE_CPUSYNC:
exec_cycle();
break;
}
}
void bCPU::exec_cycle() {
//on first cycle?
if(status.cycle_pos == 0) {
snes->notify(SNES::CPU_EXEC_OPCODE_BEGIN);
status.opcode = op_read();
status.cycle_pos = 1;
//irq active? run one bus cycle of the irq event and return
if(run_state.irq) {
irq_run();
return;
}
(this->*optbl[status.opcode])();
if(status.cycle_pos == 0) {
snes->notify(SNES::CPU_EXEC_OPCODE_END);
if(status.cycle_pos) {
(this->*optbl[status.opcode])();
#ifdef DEBUGGER
if(status.cycle_pos == 0) {
snes->notify(SNES::CPU_EXEC_OPCODE_END);
}
#endif
return;
}
//on first cycle?
#ifdef DEBUGGER
snes->notify(SNES::CPU_EXEC_OPCODE_BEGIN);
#endif
status.opcode = op_read();
status.cycle_pos = 1;
}
//only return true when we are on an opcode edge
+32 -33
View File
@@ -1,43 +1,45 @@
/*
[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(r_mem->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;
#ifdef DEBUGGER
//let debugger know the new IRQ opcode address
snes->notify(SNES::CPU_EXEC_OPCODE_END);
#endif
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 +63,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;
}
+76 -12
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;
@@ -35,7 +37,7 @@ void bCPU::mmio_reset() {
//WMDATA
uint8 bCPU::mmio_r2180() {
uint8 r;
r = mem_bus->read(0x7e0000 | status.wram_addr);
r = r_mem->read(0x7e0000 | status.wram_addr);
status.wram_addr++;
status.wram_addr &= 0x01ffff;
return r;
@@ -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;
@@ -270,7 +324,7 @@ uint8 bCPUMMIO::read(uint32 addr) {
uint i;
//APU
if(addr >= 0x2140 && addr <= 0x217f) {
return apu->port_read(addr & 3);
return r_apu->port_read(addr & 3);
}
//HDMA
@@ -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;
@@ -320,7 +374,7 @@ case 0x4200:dprintf("* 4200 read at %3d,%4d", cpu->time.v, cpu->time.hc);break;
//WMDATA
void bCPU::mmio_w2180(uint8 value) {
mem_bus->write(0x7e0000 | status.wram_addr, value);
r_mem->write(0x7e0000 | status.wram_addr, value);
status.wram_addr++;
status.wram_addr &= 0x01ffff;
}
@@ -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);
@@ -378,7 +441,7 @@ void bCPU::mmio_w4200(uint8 value) {
//WRIO
void bCPU::mmio_w4201(uint8 value) {
if((status.pio & 0x80) && !(value & 0x80)) {
ppu->latch_counters();
r_ppu->latch_counters();
}
status.pio = value;
}
@@ -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;
}
@@ -461,7 +525,7 @@ void bCPU::mmio_w420c(uint8 value) {
//MEMSEL
void bCPU::mmio_w420d(uint8 value) {
mem_bus->set_speed(value & 1);
r_mem->set_speed(value & 1);
}
//DMAPx
@@ -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;
}
}
+4 -3
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;
+36 -30
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 {
@@ -239,6 +239,24 @@ uint32 r = status.cycles_executed;
return r;
}
void bCPU::cycle_edge() {
if(time.hdmainit_triggered == false) {
if(time.hc >= time.hdmainit_trigger_pos || time.v) {
time.hdmainit_triggered = true;
hdmainit_activate();
}
}
if(time.hdma_triggered == false) {
if(time.v <= (overscan() ? 239 : 224)) {
if(time.hc >= 1106) {
time.hdma_triggered = true;
hdma_activate();
}
}
}
}
void bCPU::add_cycles(int cycles) {
status.cycles_executed += cycles;
@@ -253,30 +271,16 @@ void bCPU::add_cycles(int cycles) {
if(time.v == 0) {
frame();
ppu->frame();
r_ppu->frame();
snes->frame();
}
scanline();
ppu->scanline();
// ppu->render_scanline();
r_ppu->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.dram_refreshed == false) {
if(time.hc + cycles >= time.dram_refresh_pos) {
time.dram_refreshed = true;
@@ -296,15 +300,14 @@ void bCPU::add_cycles(int cycles) {
}
}
if(status.hdma_triggered == false) {
//vcounter range verified on hardware
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.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;
r_ppu->render_scanline();
}
}
@@ -312,11 +315,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 +329,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;
+6
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()
@@ -64,5 +69,6 @@ inline uint8 dma_counter();
inline void inc_vcounter();
inline uint16 get_hcounter();
inline void cycle_edge();
inline void add_cycles(int cycles);
inline void time_reset();
+4 -3
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--;
}
}
+1 -1
View File
@@ -2,6 +2,6 @@
#include "dcpu.cpp"
CPU::CPU() {
cpu_version = 2;
cpu_version = 1;
mmio = &mmio_unmapped;
}
+1
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;
+17 -8
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); }
+11 -11
View File
@@ -38,23 +38,23 @@ uint32 r = 0;
break;
case OPTYPE_IDP:
addr = (regs.d + (addr & 0xffff)) & 0xffff;
r = (regs.db << 16) + mem_bus->read_word(addr);
r = (regs.db << 16) + r_mem->read_word(addr);
break;
case OPTYPE_IDPX:
addr = (regs.d + regs.x + (addr & 0xffff)) & 0xffff;
r = (regs.db << 16) + mem_bus->read_word(addr);
r = (regs.db << 16) + r_mem->read_word(addr);
break;
case OPTYPE_IDPY:
addr = (regs.d + (addr & 0xffff)) & 0xffff;
r = (regs.db << 16) + mem_bus->read_word(addr) + regs.y;
r = (regs.db << 16) + r_mem->read_word(addr) + regs.y;
break;
case OPTYPE_ILDP:
addr = (regs.d + (addr & 0xffff)) & 0xffff;
r = mem_bus->read_long(addr);
r = r_mem->read_long(addr);
break;
case OPTYPE_ILDPY:
addr = (regs.d + (addr & 0xffff)) & 0xffff;
r = mem_bus->read_long(addr) + regs.y;
r = r_mem->read_long(addr) + regs.y;
break;
case OPTYPE_ADDR:
r = (regs.db << 16) + (addr & 0xffff);
@@ -88,7 +88,7 @@ uint32 r = 0;
break;
case OPTYPE_ISRY:
addr = (regs.s + (addr & 0xff)) & 0xffff;
r = (regs.db << 16) + mem_bus->read_word(addr) + regs.y;
r = (regs.db << 16) + r_mem->read_word(addr) + regs.y;
break;
}
return r;
@@ -109,10 +109,10 @@ static CPUReg24 pc;
pc.d = regs.pc.d;
sprintf(s, "%0.6x ", pc.d);
op = mem_bus->read(pc.d); pc.w++;
op0 = mem_bus->read(pc.d); pc.w++;
op1 = mem_bus->read(pc.d); pc.w++;
op2 = mem_bus->read(pc.d);
op = r_mem->read(pc.d); pc.w++;
op0 = r_mem->read(pc.d); pc.w++;
op1 = r_mem->read(pc.d); pc.w++;
op2 = r_mem->read(pc.d);
switch(op) {
case 0x00:sprintf(t, "brk #$%0.2x ", op0);break;
@@ -457,7 +457,7 @@ static uint8 op_len_tbl[256] = {
return 0;
}
op = mem_bus->read(regs.pc.d);
op = r_mem->read(regs.pc.d);
len = op_len_tbl[op];
if(len == 5)return (regs.p.m)?2:3;
if(len == 6)return (regs.p.x)?2:3;
+10 -7
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;
@@ -183,7 +183,7 @@ int i, v, n;
void bDSP::power() {
int v;
spcram = apu->get_spcram_handle();
spcram = r_apu->get_spcram_handle();
memset(dspram, 0x00, 128);
for(v=0;v<8;v++) {
@@ -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;
}
+1 -1
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;
+50 -18
View File
@@ -1,49 +1,81 @@
#define BSNES_VERSION "0.012"
#define BSNES_VERSION "0.014"
#define BSNES_TITLE "bsnes v" BSNES_VERSION
#ifdef POLYMORPHISM
#define ref(x) x
#else
#define ref(x) (&x)
#endif
#define deref(x) __##x
#define r_mem ref(deref(mem))
#define r_cpu ref(deref(cpu))
#define r_apu ref(deref(apu))
#define r_dsp ref(deref(dsp))
#define r_ppu ref(deref(ppu))
#include "reader/reader.h"
#include "memory/memory.h"
#include "memory/bmemory/bmemory.h"
extern MMIO mmio_unmapped;
extern MemBus *mem_bus;
#include "cpu/cpu.h"
#include "cpu/bcpu/bcpu.h"
extern CPU *cpu;
#include "apu/apu.h"
#include "apu/bapu/bapu.h"
#include "apu/bapuskip/bapuskip.h"
extern APU *apu;
#include "dsp/dsp.h"
#include "dsp/bdsp/bdsp.h"
extern DSP *dsp;
#include "ppu/ppu.h"
#include "ppu/bppu/bppu.h"
extern PPU *ppu;
#include "snes/snes.h"
extern SNES *snes;
#include "chip/srtc/srtc.h"
#include "chip/sdd1/sdd1.h"
extern MMIO mmio_unmapped;
#ifdef POLYMORPHISM
extern MemBus *deref(mem);
extern CPU *deref(cpu);
extern APU *deref(apu);
extern DSP *deref(dsp);
extern PPU *deref(ppu);
#else
extern bMemBus deref(mem);
extern bCPU deref(cpu);
extern bAPU deref(apu);
extern bDSP deref(dsp);
extern bPPU deref(ppu);
#endif
extern SNES *snes;
extern SRTC *srtc;
extern SDD1 *sdd1;
#include "config/config.h"
#ifdef INTERFACE_MAIN
#include "config/config.cpp"
MemBus *mem_bus;
CPU *cpu;
APU *apu;
DSP *dsp;
PPU *ppu;
SNES *snes;
SRTC *srtc;
SDD1 *sdd1;
#include "config/config.cpp"
#ifdef POLYMORPHISM
MemBus *deref(mem);
CPU *deref(cpu);
APU *deref(apu);
DSP *deref(dsp);
PPU *deref(ppu);
#else
bMemBus deref(mem);
bCPU deref(cpu);
bAPU deref(apu);
bDSP deref(dsp);
bPPU deref(ppu);
#endif
SNES *snes;
SRTC *srtc;
SDD1 *sdd1;
#endif
-1
View File
@@ -11,7 +11,6 @@ uint Setting::get() {
}
void Setting::set(uint _data) {
printf("%s %d\n", name, _data);
data = _data;
if(type != DEC && type != HEX) {
+6 -2
View File
@@ -15,7 +15,7 @@ uint8 mapper, region;
rom_size = rf->size();
if(rom_size < 32768) {
free(rom_image);
zerofree(rom_image);
return false;
}
@@ -77,7 +77,7 @@ end:
region = rom[index + 0x19];
dprintf("* Image Name : \"%s\"", cart_title);
dprintf("* Region : %s", (region <= 1)?"NTSC":"PAL");
dprintf("* Region : %s", (region <= 1) ? "NTSC" : "PAL");
dprintf("* MAD : %0.2x", mapper);
dprintf("* SRAM Size : %dkb", sram_size / 1024);
dprintf("* Reset:%0.4x NMI:%0.4x IRQ:%0.4x BRK[n]:%0.4x COP[n]:%0.4x BRK[e]:%0.4x COP[e]:%0.4x",
@@ -206,7 +206,9 @@ static uint32 r;
break;
}
#ifdef DEBUGGER
snes->notify(SNES::MEM_READ, addr, r);
#endif
return r;
}
@@ -245,7 +247,9 @@ void bMemBus::write(uint32 addr, uint8 value) {
break;
}
#ifdef DEBUGGER
snes->notify(SNES::MEM_WRITE, addr, value);
#endif
}
void bMemBus::power() {
+1 -5
View File
@@ -79,7 +79,7 @@ void Memory::write_long(uint32 addr, uint32 data, uint wrap) {
}
MMIO mmio_unmapped;
uint8 MMIO::read (uint32 addr) { return cpu->regs.mdr; }
uint8 MMIO::read (uint32 addr) { return r_cpu->regs.mdr; }
void MMIO::write(uint32 addr, uint8 value) {}
uint8 MemBus::calc_speed(uint32 addr, bool fast) {
@@ -92,10 +92,6 @@ uint8 MemBus::calc_speed(uint32 addr, bool fast) {
return 8;
}
uint8 MemBus::speed(uint32 addr) {
return speed_table[addr >> 9];
}
void MemBus::set_speed(bool fast) {
fastROM = fast;
+4 -2
View File
@@ -43,8 +43,10 @@ uint8 *speed_table,
speed_table_fastrom[32768];
inline uint8 calc_speed(uint32 addr, bool fast);
public:
uint8 speed(uint32 addr);
void set_speed(bool fast);
inline uint8 speed(uint32 addr) {
return speed_table[addr >> 9];
}
void set_speed(bool fast);
virtual bool load_cart(Reader *rf) = 0;
virtual bool load_sram(Reader *rf) = 0;
@@ -0,0 +1,64 @@
inline uint16 bPPU::addsub_pixels(uint32 cdest, uint32 csrc) {
if(!regs.color_mode) {
//add
cdest = ((cdest << 16) | cdest) & 0x03e07c1f;
csrc = ((csrc << 16) | csrc) & 0x03e07c1f;
cdest += csrc;
if(regs.color_halve) {
cdest >>= 1;
} else {
if(cdest & 0x04000000)cdest |= 0x03e00000;
if(cdest & 0x00008000)cdest |= 0x00007c00;
if(cdest & 0x00000020)cdest |= 0x0000001f;
}
cdest &= 0x03e07c1f;
return (cdest >> 16) | cdest;
} else {
//subtract
if((cdest & 0x7c00) < (csrc & 0x7c00))cdest &= ~0x7c00;
else cdest -= (csrc & 0x7c00);
if((cdest & 0x03e0) < (csrc & 0x03e0))cdest &= ~0x03e0;
else cdest -= (csrc & 0x03e0);
if((cdest & 0x001f) < (csrc & 0x001f))cdest &= ~0x001f;
else cdest -= (csrc & 0x001f);
return (!regs.color_halve) ? cdest : ((cdest & 0x7bde) >> 1);
}
}
inline uint16 bPPU::addsub_pixel(uint32 cdest) {
uint32 csrc = regs.color_rgb;
if(!regs.color_mode) {
//add
cdest = ((cdest << 16) | cdest) & 0x03e07c1f;
csrc = ((csrc << 16) | csrc) & 0x03e07c1f;
cdest += csrc;
if(regs.color_halve && !regs.addsub_mode) {
cdest >>= 1;
} else {
if(cdest & 0x04000000)cdest |= 0x03e00000;
if(cdest & 0x00008000)cdest |= 0x00007c00;
if(cdest & 0x00000020)cdest |= 0x0000001f;
}
cdest &= 0x03e07c1f;
return (cdest >> 16) | cdest;
} else {
//subtract
if((cdest & 0x7c00) < (csrc & 0x7c00))cdest &= ~0x7c00;
else cdest -= (csrc & 0x7c00);
if((cdest & 0x03e0) < (csrc & 0x03e0))cdest &= ~0x03e0;
else cdest -= (csrc & 0x03e0);
if((cdest & 0x001f) < (csrc & 0x001f))cdest &= ~0x001f;
else cdest -= (csrc & 0x001f);
if(regs.color_halve && !regs.addsub_mode) {
return (cdest & 0x7bde) >> 1;
}
return cdest;
}
}
+82
View File
@@ -0,0 +1,82 @@
inline uint16 bPPU::get_palette(uint8 index) {
return read16(cgram, index << 1);
}
inline uint16 bPPU::get_direct_color(uint8 p, uint8 t) {
//p = 00000bgr <palette data>
//t = BBGGGRRR <tilemap data>
//r = 0BBb00GGGg0RRRr0 <return data>
return ((t & 7) << 2) | ((p & 1) << 1) |
(((t >> 3) & 7) << 7) | (((p >> 1) & 1) << 6) |
((t >> 6) << 13) | ((p >> 2) << 12);
}
inline uint16 bPPU::get_pixel(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 && 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 {
r = addsub_pixel(p->src_main);
}
} else {
r = p->src_main;
}
} else if(p->bg_main) {
if(!p->color_exempt && 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.color_enabled[BACK]) {
if(window_cache[COL].sub[x]) {
if(regs.addsub_mode) {
r = addsub_pixels(src_back, p->src_sub);
} else {
r = addsub_pixel(src_back);
}
} else {
r = src_back;
}
} else {
r = src_back; //was 0x0000 -- possibly another condition here?
}
} else {
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;
}
} else {
if(regs.color_enabled[BACK] && window_cache[COL].sub[x]) {
r = (!regs.color_mode) ? regs.color_rgb : 0x0000;
} else {
r = 0x0000;
}
}
}
return r;
}
inline void bPPU::render_line_output() {
uint16 r, x;
uint16 *ptr = (uint16*)output + (line.y * 1024) +
((line.interlace && line.interlace_field) ? 512 : 0);
uint16 *ltable = (uint16*)light_table + (regs.display_brightness << 15);
if(line.width == 256) {
for(x=0;x<256;x++) {
r = get_pixel(x);
*ptr++ = *(ltable + r);
}
} else {
for(x=0;x<512;x++) {
r = get_pixel(x);
*ptr++ = *(ltable + r);
}
}
}
+83 -83
View File
@@ -5,18 +5,19 @@
void bPPU::run() {}
void bPPU::scanline() {
_y = cpu->vcounter();
_screen_width = (regs.bg_mode == 5 || regs.bg_mode == 6) ? 512 : 256;
_interlace = cpu->interlace();
_interlace_field = cpu->interlace_field();
line.y = r_cpu->vcounter();
line.width = (regs.bg_mode == 5 || regs.bg_mode == 6) ? 512 : 256;
line.hires = (regs.bg_mode == 5 || regs.bg_mode == 6);
line.interlace = r_cpu->interlace();
line.interlace_field = r_cpu->interlace_field();
if(_y == 0) {
if(line.y == 0) {
//RTO flag reset
regs.time_over = false;
regs.range_over = false;
}
if(_y == 1) {
if(line.y == 1) {
//OAM FirstSprite priority set
if(regs.oam_priority == true) {
regs.oam_firstsprite = (regs.oam_addr & 0xfe) >> 1;
@@ -25,43 +26,27 @@ void bPPU::scanline() {
}
}
if(_y == (cpu->overscan() ? 239 : 224) && regs.display_disabled == false) {
if(line.y == (r_cpu->overscan() ? 239 : 224) && regs.display_disabled == false) {
//OAM address reset
regs.oam_addr = ((regs.oam_addrh << 8) | regs.oam_addrl) << 1;
}
}
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)) {
if(line.y > 0 && line.y < (r_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;
snes->notify(SNES::RENDER_FRAME);
}
void bPPU::set_frameskip(int fs) {
settings.frameskip = fs;
settings.frameskip_changed = true;
PPU::frame();
}
void bPPU::power() {
PPU::power();
memset(vram, 0, 65536);
memset(oam, 0, 544);
memset(cgram, 0, 512);
@@ -72,6 +57,7 @@ void bPPU::power() {
}
void bPPU::reset() {
PPU::reset();
frame();
memset(sprite_list, 0, sizeof(sprite_list));
@@ -162,30 +148,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 +183,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 +203,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,19 +220,20 @@ 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;
regs.color_g = 0x00;
regs.color_b = 0x00;
regs.color_r = 0x00;
regs.color_g = 0x00;
regs.color_b = 0x00;
regs.color_rgb = 0x0000;
//$2133
regs.mode7_extbg = false;
@@ -266,27 +256,35 @@ void bPPU::reset() {
regs.time_over = false;
regs.range_over = false;
line.width = 256; //needed for clear_window_cache()
update_sprite_list_sizes();
clear_tiledata_cache();
clear_window_cache();
}
uint8 bPPU::vram_read(uint16 addr) {
uint8 r;
r = vram[addr];
#ifdef DEBUGGER
snes->notify(SNES::VRAM_READ, addr, r);
#endif
return r;
}
void bPPU::vram_write(uint16 addr, uint8 value) {
vram[addr] = value;
#ifdef DEBUGGER
snes->notify(SNES::VRAM_WRITE, addr, value);
#endif
}
uint8 bPPU::oam_read(uint16 addr) {
uint8 r;
if(addr >= 0x0200)addr = 0x0200 | (addr & 31);
r = oam[addr];
#ifdef DEBUGGER
snes->notify(SNES::OAM_READ, addr, r);
#endif
return r;
}
@@ -294,7 +292,9 @@ void bPPU::oam_write(uint16 addr, uint8 value) {
if(addr >= 0x0200)addr = 0x0200 | (addr & 31);
oam[addr] = value;
update_sprite_list(addr);
#ifdef DEBUGGER
snes->notify(SNES::OAM_WRITE, addr, value);
#endif
}
uint8 bPPU::cgram_read(uint16 addr) {
@@ -304,7 +304,9 @@ uint8 r;
if(addr & 1) {
r &= 0x7f;
}
#ifdef DEBUGGER
snes->notify(SNES::CGRAM_READ, addr, r);
#endif
return r;
}
@@ -314,14 +316,12 @@ void bPPU::cgram_write(uint16 addr, uint8 value) {
value &= 0x7f;
}
cgram[addr] = value;
#ifdef DEBUGGER
snes->notify(SNES::CGRAM_WRITE, addr, value);
#endif
}
bPPU::bPPU() {
settings.frameskip = 0;
settings.frameskip_pos = 0;
settings.frameskip_changed = false;
mmio = new bPPUMMIO(this);
vram = (uint8*)malloc(65536);
+20 -19
View File
@@ -14,8 +14,16 @@ 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 { SC_32x32 = 0, SC_32x64 = 1, SC_64x32 = 2, SC_64x64 = 3 };
enum { BG1 = 0, BG2 = 1, BG3 = 2, BG4 = 3, OAM = 4, BACK = 5, COL = 5 };
enum { SC_32x32 = 0, SC_64x32 = 1, SC_32x64 = 2, SC_64x64 = 3 };
struct {
uint32 y;
uint32 width;
bool hires;
bool interlace;
bool interlace_field;
} line;
struct sprite_item {
uint8 width, height;
@@ -27,11 +35,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 +98,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,10 +123,11 @@ struct {
//$2131
bool color_mode, color_halve;
bool bg_color_enabled[6];
bool color_enabled[6];
//$2132
uint8 color_r, color_g, color_b;
uint16 color_rgb;
//$2133
bool mode7_extbg;
@@ -158,6 +159,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 +245,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); }
+212 -73
View File
@@ -1,6 +1,6 @@
void bPPU::latch_counters() {
regs.hcounter = cpu->hcounter();
regs.vcounter = cpu->vcounter();
regs.hcounter = r_cpu->hcounter();
regs.vcounter = r_cpu->vcounter();
regs.counters_latched = true;
}
@@ -16,6 +16,55 @@ uint16 addr;
return (addr << 1);
}
bool bPPU::vram_can_read() {
if(regs.display_disabled == true) {
return true;
}
uint16 v = r_cpu->vcounter();
uint16 hc = r_cpu->hcycles();
uint16 ls;
if(r_cpu->interlace() && !r_cpu->interlace_field()) {
ls = r_cpu->region_scanlines();
} else {
ls = r_cpu->region_scanlines() - 1;
}
if(v == ls && hc == 1362)return false;
if(v < (r_cpu->overscan() ? 239 : 224))return false;
if(v == (r_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 = r_cpu->vcounter();
uint16 hc = r_cpu->hcycles();
if(v == 0) {
if(hc <= 4)return true;
if(hc == 6) { value = r_cpu->regs.mdr; return true; }
return false;
}
if(v < (r_cpu->overscan() ? 240 : 225))return false;
if(v == (r_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
@@ -413,6 +542,8 @@ void bPPU::mmio_w2132(uint8 value) {
if(value & 0x80)regs.color_b = value & 0x1f;
if(value & 0x40)regs.color_g = value & 0x1f;
if(value & 0x20)regs.color_r = value & 0x1f;
regs.color_rgb = (regs.color_r) | (regs.color_g << 5) | (regs.color_b << 10);
}
//SETINI
@@ -423,8 +554,8 @@ void bPPU::mmio_w2133(uint8 value) {
regs.oam_halve = !!(value & 0x02);
regs.interlace = !!(value & 0x01);
cpu->set_overscan(regs.overscan);
cpu->set_interlace(regs.interlace);
r_cpu->set_overscan(regs.overscan);
r_cpu->set_interlace(regs.interlace);
}
//MPYL
@@ -453,10 +584,10 @@ uint32 r;
//SLHV
uint8 bPPU::mmio_r2137() {
if(cpu->pio_status() & 0x80) {
if(r_cpu->pio_status() & 0x80) {
latch_counters();
}
return cpu->regs.mdr;
return r_cpu->regs.mdr;
}
//OAMDATAREAD
@@ -476,8 +607,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 +624,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;
@@ -552,8 +691,8 @@ uint8 r = 0x00;
regs.latch_hcounter = 0;
regs.latch_vcounter = 0;
r |= cpu->interlace_field() << 7;
if(!(cpu->pio_status() & 0x80)) {
r |= r_cpu->interlace_field() << 7;
if(!(r_cpu->pio_status() & 0x80)) {
r |= 0x40;
} else if(regs.counters_latched == true) {
r |= 0x40;
@@ -601,7 +740,7 @@ uint8 bPPUMMIO::read(uint32 addr) {
case 0x213f:return ppu->mmio_r213f(); //STAT78
}
return cpu->regs.mdr;
return r_cpu->regs.mdr;
}
void bPPUMMIO::write(uint32 addr, uint8 value) {
+13 -15
View File
@@ -20,28 +20,25 @@ inline void bPPU::render_line_mode0() {
}
/*
Mode 1 (pri=0): ->
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
BG3B, OAM0, BG3A, OAM1, BG2B, BG1B, OAM2, BG2A, BG1A, OAM3
Mode 1 (pri=1): ->
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
BG3B, OAM0, OAM1, BG2B, BG1B, OAM2, BG2A, BG1A, OAM3, BG3A
Mode 1 (pri=0): ->
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
BG3B, OAM0, BG3A, OAM1, BG2B, BG1B, OAM2, BG2A, BG1A, OAM3
*/
inline void bPPU::render_line_mode1() {
switch(regs.bg3_priority) {
case 0:
render_line_bg(BG1, COLORDEPTH_16, 6, 9);
render_line_bg(BG2, COLORDEPTH_16, 5, 8);
render_line_bg(BG3, COLORDEPTH_4, 1, 3);
render_line_oam(2, 4, 7, 10);
break;
case 1:
if(regs.bg3_priority) {
render_line_bg(BG1, COLORDEPTH_16, 5, 8);
render_line_bg(BG2, COLORDEPTH_16, 4, 7);
render_line_bg(BG3, COLORDEPTH_4, 1, 10);
render_line_oam(2, 3, 6, 9);
break;
} else {
render_line_bg(BG1, COLORDEPTH_16, 6, 9);
render_line_bg(BG2, COLORDEPTH_16, 5, 8);
render_line_bg(BG3, COLORDEPTH_4, 1, 3);
render_line_oam(2, 4, 7, 10);
}
}
@@ -121,12 +118,13 @@ inline void bPPU::render_line_mode7() {
void bPPU::render_line() {
if(regs.display_disabled == true) {
memset(snes->get_ppu_output_handle(), 0, 1024);
memset(output + (line.y * 1024), 0, 1024);
return;
}
clear_pixel_cache();
build_color_window_tables();
build_window_tables(COL);
update_bg_info();
switch(regs.bg_mode) {
case 0:render_line_mode0();break;
+20 -15
View File
@@ -1,8 +1,4 @@
//bppu_render.cpp
int _screen_width;
bool _interlace;
int _interlace_field;
inline void render_line_mode0();
inline void render_line_mode1();
inline void render_line_mode2();
@@ -35,19 +31,27 @@ 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
struct {
uint16 tw, th; //tile width, height
uint16 mx, my; //screen mask x, y
} bg_info[4];
inline void update_bg_info();
inline uint16 bg_get_tile(uint8 bg, uint16 x, uint16 y);
void render_line_bg(uint8 bg, uint8 color_depth, uint8 pri0_pos, uint8 pri1_pos);
//bppu_render_oam.cpp
@@ -60,24 +64,25 @@ struct oam_tileitem {
} oam_tilelist[34];
enum { OAM_PRI_NONE = 4 };
uint8 oam_line_pal[512], oam_line_pri[512];
uint8 oam_line_pal[256], oam_line_pri[256];
bool is_sprite_on_scanline();
void load_oam_tiles();
void render_oam_tile(int tile_num);
void render_line_oam(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos);
void render_line_oam_lores(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos);
void render_line_oam_hires(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos);
//bppu_render_mode7.cpp
void render_line_mode7(uint8 bg, uint8 pri0_pos, uint8 pri1_pos);
//bppu_render_addsub.cpp
inline uint16 addsub_pixels(uint32 cdest, uint32 csrc);
inline uint16 addsub_pixel (uint32 cdest);
inline uint16 addsub_pixels(uint32 x);
//bppu_render_line.cpp
enum { BLENDTYPE_BACK = 0, BLENDTYPE_MAIN = 1, BLENDTYPE_SUB = 2, BLENDTYPE_COMBINE = 3 };
inline uint16 get_palette(uint8 index);
inline uint16 get_direct_color(uint8 p, uint8 t);
inline uint16 get_pixel(int x);
inline uint16 get_pixel(uint32 x);
inline void render_line_output();
+29 -82
View File
@@ -1,92 +1,39 @@
inline uint16 bPPU::addsub_pixels(uint32 cdest, uint32 csrc) {
int r, g, b;
uint16 res;
switch(regs.color_mode) {
case 0: //COLORMODE_ADD:
if(regs.color_halve == true) {
cdest = ((cdest << 16) | cdest) & 0x03e07c1f;
csrc = ((csrc << 16) | csrc) & 0x03e07c1f;
cdest += csrc;
cdest >>= 1;
cdest &= 0x03e07c1f;
return (cdest >> 16) | cdest;
} else {
cdest = ((cdest << 16) | cdest) & 0x03e07c1f;
csrc = ((csrc << 16) | csrc) & 0x03e07c1f;
cdest += csrc;
if(cdest & 0x04000000)cdest |= 0x03e00000;
if(cdest & 0x00008000)cdest |= 0x00007c00;
if(cdest & 0x00000020)cdest |= 0x0000001f;
cdest &= 0x03e07c1f;
return (cdest >> 16) | cdest;
inline uint16 bPPU::addsub_pixels(uint32 x) {
uint32 cdest = pixel_cache[x].src_main,
csrc = pixel_cache[x].src_sub;
bool halve = false;
if(regs.color_halve && window_cache[COL].main[x]) {
if(regs.addsub_mode && pixel_cache[x].bg_sub == BACK);
else {
halve = true;
}
break;
case 1: //COLORMODE_SUB:
if(regs.color_halve == true) {
if((cdest & 0x7c00) < (csrc & 0x7c00))cdest &= ~0x7c00;
else cdest -= (csrc & 0x7c00);
if((cdest & 0x03e0) < (csrc & 0x03e0))cdest &= ~0x03e0;
else cdest -= (csrc & 0x03e0);
if((cdest & 0x001f) < (csrc & 0x001f))cdest &= ~0x001f;
else cdest -= (csrc & 0x001f);
return (cdest & 0x7bde) >> 1;
} else {
if((cdest & 0x7c00) < (csrc & 0x7c00))cdest &= ~0x7c00;
else cdest -= (csrc & 0x7c00);
if((cdest & 0x03e0) < (csrc & 0x03e0))cdest &= ~0x03e0;
else cdest -= (csrc & 0x03e0);
if((cdest & 0x001f) < (csrc & 0x001f))cdest &= ~0x001f;
else cdest -= (csrc & 0x001f);
return cdest;
}
break;
}
return 0x0000; //prevent annoying warning message
}
inline uint16 bPPU::addsub_pixel(uint32 cdest) {
int r, g, b;
uint32 csrc = (regs.color_r) | (regs.color_g << 5) | (regs.color_b << 10);
uint16 res;
switch(regs.color_mode) {
case 0: //COLORMODE_ADD:
if(regs.color_halve == true && regs.addsub_mode == 0) {
cdest = ((cdest << 16) | cdest) & 0x03e07c1f;
csrc = ((csrc << 16) | csrc) & 0x03e07c1f;
cdest += csrc;
cdest >>= 1;
cdest &= 0x03e07c1f;
return (cdest >> 16) | cdest;
} else {
cdest = ((cdest << 16) | cdest) & 0x03e07c1f;
csrc = ((csrc << 16) | csrc) & 0x03e07c1f;
cdest += csrc;
if(!regs.color_mode) {
//add
cdest = ((cdest << 16) | cdest) & 0x03e07c1f;
csrc = ((csrc << 16) | csrc) & 0x03e07c1f;
cdest += csrc;
if(!halve) {
if(cdest & 0x04000000)cdest |= 0x03e00000;
if(cdest & 0x00008000)cdest |= 0x00007c00;
if(cdest & 0x00000020)cdest |= 0x0000001f;
cdest &= 0x03e07c1f;
return (cdest >> 16) | cdest;
}
break;
case 1: //COLORMODE_SUB:
if(regs.color_halve == true && regs.addsub_mode == 0) {
if((cdest & 0x7c00) < (csrc & 0x7c00))cdest &= ~0x7c00;
else cdest -= (csrc & 0x7c00);
if((cdest & 0x03e0) < (csrc & 0x03e0))cdest &= ~0x03e0;
else cdest -= (csrc & 0x03e0);
if((cdest & 0x001f) < (csrc & 0x001f))cdest &= ~0x001f;
else cdest -= (csrc & 0x001f);
return (cdest & 0x7bde) >> 1;
} else {
if((cdest & 0x7c00) < (csrc & 0x7c00))cdest &= ~0x7c00;
else cdest -= (csrc & 0x7c00);
if((cdest & 0x03e0) < (csrc & 0x03e0))cdest &= ~0x03e0;
else cdest -= (csrc & 0x03e0);
if((cdest & 0x001f) < (csrc & 0x001f))cdest &= ~0x001f;
else cdest -= (csrc & 0x001f);
return cdest;
cdest >>= 1;
}
break;
cdest &= 0x03e07c1f;
return (cdest >> 16) | cdest;
} else {
//subtract
if((cdest & 0x7c00) < (csrc & 0x7c00))cdest &= ~0x7c00;
else cdest -= (csrc & 0x7c00);
if((cdest & 0x03e0) < (csrc & 0x03e0))cdest &= ~0x03e0;
else cdest -= (csrc & 0x03e0);
if((cdest & 0x001f) < (csrc & 0x001f))cdest &= ~0x001f;
else cdest -= (csrc & 0x001f);
return (!halve) ? cdest : ((cdest & 0x7bde) >> 1);
}
return 0x0000; //prevent annoying warning message
}
+152 -224
View File
@@ -1,263 +1,191 @@
//called once at the start of every rendered scanline
void bPPU::update_bg_info() {
for(int bg=0;bg<4;bg++) {
bg_info[bg].th = (regs.bg_tilesize[bg]) ? 4 : 3;
bg_info[bg].tw = (line.hires) ? 4 : bg_info[bg].th;
bg_info[bg].mx = (bg_info[bg].th == 4) ? (line.width << 1) : line.width;
bg_info[bg].my = bg_info[bg].mx;
if(regs.bg_scsize[bg] & 0x01)bg_info[bg].mx <<= 1;
if(regs.bg_scsize[bg] & 0x02)bg_info[bg].my <<= 1;
bg_info[bg].mx--;
bg_info[bg].my--;
}
}
uint16 bPPU::bg_get_tile(uint8 bg, uint16 x, uint16 y) {
uint16 map_index, pos;
x &= bg_info[bg].mx;
y &= bg_info[bg].my;
//32 = SC width, height; tilemap data is stored as uint16
//width(32) * height(32) * sizeof(uint16) = 2048
switch(regs.bg_scsize[bg]) {
case SC_32x32:map_index = 0; break;
case SC_64x32:map_index = ((x >> bg_info[bg].th) / 32) * 2048; break;
case SC_32x64:map_index = ((y >> bg_info[bg].th) / 32) * 2048; break;
case SC_64x64:map_index = ((x >> bg_info[bg].th) / 32) * 2048 +
((y >> bg_info[bg].th) / 32) * 4096; break;
}
pos = ((((y >> bg_info[bg].th) & 31) * 32) + ((x >> bg_info[bg].tw) & 31)) << 1;
return read16(vram, regs.bg_scaddr[bg] + map_index + pos);
}
void bPPU::render_line_bg(uint8 bg, uint8 color_depth, uint8 pri0_pos, uint8 pri1_pos) {
if(regs.bg_enabled[bg] == false && regs.bgsub_enabled[bg] == false) {
return;
}
int x;
int _scaddr = regs.bg_scaddr[bg];
int _tdaddr = regs.bg_tdaddr[bg];
bool _bg_enabled = regs.bg_enabled[bg];
bool _bgsub_enabled = regs.bgsub_enabled[bg];
bool bg_enabled = regs.bg_enabled[bg];
bool bgsub_enabled = regs.bgsub_enabled[bg];
uint16 opt_valid_bit; //offset-per-tile valid flag bit
if(bg == BG1) {
opt_valid_bit = 0x2000;
} else if(bg == BG2) {
opt_valid_bit = 0x4000;
} else {
opt_valid_bit = 0x0000;
uint16 opt_valid_bit = (bg == BG1) ? 0x2000 : ((bg == BG2) ? 0x4000 : 0x0000);
uint8 bgpal_index = (regs.bg_mode == 0) ? (bg << 5) : 0;
uint8 pal_size = (color_depth == 2) ? 256 : ((color_depth == 1) ? 16 : 4);
uint16 tile_mask = (color_depth == 2) ? 0x03ff : ((color_depth == 1) ? 0x07ff : 0x0fff);
//4 + color_depth = >>(4-6) -- / {16, 32, 64 } bytes/tile
//index is a tile number count to add to base tile number
uint tiledata_index = regs.bg_tdaddr[bg] >> (4 + color_depth);
uint8 *bg_td = (uint8*)bg_tiledata[color_depth];
uint8 *bg_td_state = (uint8*)bg_tiledata_state[color_depth];
uint8 tile_width = bg_info[bg].tw;
uint8 tile_height = bg_info[bg].th;
uint16 mask_x = bg_info[bg].mx; //screen width mask
uint16 mask_y = bg_info[bg].my; //screen height mask
uint x = 0;
uint y = line.y;
if(line.interlace && line.hires) {
y = (y << 1) + line.interlace_field;
}
//Mode 0 uses a special palette-indexing mode.
//Since there are 8 selectable palettes per tile,
//and there are 4 colors per palette on all four
//BGs for Mode 0, each BG has a unique palette
//entry point. This allows all 256 palette colors
//to be used, instead of just the first 32.
//entry = bg * 32, where 32 is from 8 * 4
uint8 bgpal_index = (regs.bg_mode == 0) ? (bg << 5) : 0;
uint16 hscroll = (line.hires) ? (regs.bg_hofs[bg] << 1) : regs.bg_hofs[bg];
uint16 vscroll = (line.interlace && line.hires) ? (regs.bg_vofs[bg] << 1) : regs.bg_vofs[bg];
hscroll &= mask_x;
vscroll &= mask_y;
uint8 pal_size, tiledata_size;
switch(color_depth) {
case COLORDEPTH_4:
pal_size = 4;
tiledata_size = 4; //<<4=*16
break;
case COLORDEPTH_16:
pal_size = 16;
tiledata_size = 5; //<<5=*32
break;
case COLORDEPTH_256:
pal_size = 256;
tiledata_size = 6; //<<6=*64
break;
}
uint16 *mtable = (uint16*)mosaic_table[(regs.mosaic_enabled[bg]) ? regs.mosaic_size : 0];
uint16 mosaic_x;
uint16 mosaic_y;
uint8 *bg_td, *bg_td_state;
bg_td = (uint8*)bg_tiledata[color_depth];
bg_td_state = (uint8*)bg_tiledata_state[color_depth];
uint16 screen_width, screen_height;
screen_width = _screen_width;
screen_height = _screen_width; //this is correct -- ppu tilemap is a perfect square
uint8 tile_size, tile_width, tile_height;
tile_size = (regs.bg_tilesize[bg])?4:3; //<<4=*16, <<3=*8
tile_width = tile_size;
tile_height = tile_size;
int screen_x, screen_y;
if(_interlace == true && _screen_width == 512) {
screen_y = (_y << 1) + _interlace_field;
} else {
screen_y = _y;
}
//Modes 5 and 6 seem to force 16-width tiles due to having twice the resolution.
//The tile size attribute in $2105 has no effect on tile width.
if(_screen_width == 512) {
tile_width = 4; //<<4=*16
}
if(tile_size == 4) { //16x16 tile size
screen_width <<= 1;
screen_height <<= 1;
}
if(regs.bg_scsize[bg] & 0x01)screen_width <<= 1;
if(regs.bg_scsize[bg] & 0x02)screen_height <<= 1;
uint16 screen_width_mask, screen_height_mask;
screen_width_mask = screen_width - 1;
screen_height_mask = screen_height - 1;
int bg_x, bg_y;
uint16 vscroll, hscroll;
if(_screen_width == 512) {
hscroll = (regs.bg_hofs[bg] << 1) & screen_width_mask;
} else {
hscroll = regs.bg_hofs[bg] & screen_width_mask;
}
bg_x = hscroll;
if(_screen_width == 512 && _interlace == true) {
vscroll = (regs.bg_vofs[bg] << 1) & screen_height_mask;
} else {
vscroll = regs.bg_vofs[bg] & screen_height_mask;
}
bg_y = (screen_y + vscroll) & screen_height_mask;
uint16 *mtable;
int mosaic_x, mosaic_y;
if(regs.mosaic_enabled[bg] == true) {
mtable = (uint16*)mosaic_table[regs.mosaic_size];
} else {
mtable = (uint16*)mosaic_table[0];
}
mosaic_x = mtable[bg_x];
mosaic_y = mtable[bg_y];
uint8 tile_x;
uint16 t, base_xpos, base_pos, pos;
uint16 tile_num;
int mirror_x, mirror_y;
uint16 hval, vval;
uint16 t, tile_pri, tile_num;
uint8 pal_index, pal_num;
uint8 *tile_ptr;
int xpos, ypos;
uint16 map_index, hoffset, voffset, col;
uint xpos, ypos;
uint16 hoffset, voffset, opt_x, col;
bool mirror_x, mirror_y;
bool is_opt_mode = (regs.bg_mode == 2 || regs.bg_mode == 4 || regs.bg_mode == 6);
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++) {
if(regs.bg_mode == 2 || regs.bg_mode == 4 || regs.bg_mode == 6) {
if(regs.bg_mode == 6) {
//hires adjust
tile_x = (mtable[screen_x + (hscroll & 15)] >> 4);
} else {
tile_x = (mtable[screen_x + (hscroll & 7)] >> 3);
}
int32 prev_x = -1, prev_y = -1;
for(x=0;x<line.width;x++) {
hoffset = x + hscroll;
voffset = y + vscroll;
hoffset = hscroll;
voffset = vscroll;
if(is_opt_mode) {
opt_x = (x + (hscroll & 7));
//tile 0 is unaffected by OPT mode...
if(tile_x != 0) {
//multiply by two to index into 16-bit table entries
tile_x = ((tile_x - 1) & 31) << 1;
if(opt_x >= 8) {
hval = bg_get_tile(BG3,
(opt_x - 8) + (regs.bg_hofs[BG3] & ~7),
regs.bg_vofs[BG3]);
vval = bg_get_tile(BG3,
(opt_x - 8) + (regs.bg_hofs[BG3] & ~7),
regs.bg_vofs[BG3] + 8);
if(regs.bg_mode == 4) {
pos = regs.bg_scaddr[BG3] + tile_x;
t = *((uint16*)vram + (pos >> 1));
if(t & opt_valid_bit) {
if(!(t & 0x8000)) {
hoffset = ((t & 0x1ff8) | (hscroll & 7)) & screen_width_mask;
if(hval & opt_valid_bit) {
if(!(hval & 0x8000)) {
hoffset = opt_x + (hval & ~7);
} else {
voffset = (t & 0x1fff) & screen_height_mask;
voffset = y + hval;
}
}
} else {
pos = regs.bg_scaddr[BG3] + tile_x;
t = *((uint16*)vram + (pos >> 1));
if(t & opt_valid_bit) {
hoffset = ((t & 0x1ff8) | (hscroll & 7)) & screen_width_mask;
if(hval & opt_valid_bit) {
hoffset = opt_x + (hval & ~7);
}
pos = regs.bg_scaddr[BG3] + 64 + tile_x;
t = *((uint16*)vram + (pos >> 1));
if(t & opt_valid_bit) {
voffset = (t & 0x1fff) & screen_height_mask;
if(vval & opt_valid_bit) {
voffset = y + vval;
}
}
}
mosaic_x = mtable[(screen_x + hoffset) & screen_width_mask ];
mosaic_y = mtable[(screen_y + voffset) & screen_height_mask];
}
switch(regs.bg_scsize[bg]) {
case 0:
map_index = 0;
break;
case 1:
map_index = ((mosaic_x >> tile_size) >> 5) << 11;
break;
case 2:
map_index = ((mosaic_y >> tile_size) >> 5) << 11;
break;
case 3:
map_index = ((mosaic_x >> tile_size) >> 5) << 11 |
((mosaic_y >> tile_size) >> 5) << 12;
break;
}
mosaic_x = mtable[hoffset & mask_x];
mosaic_y = mtable[voffset & mask_y];
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));
mirror_y = !!(t & 0x8000);
mirror_x = !!(t & 0x4000);
if((mosaic_x >> 3) != prev_x || (mosaic_y >> 3) != prev_y) {
prev_x = (mosaic_x >> 3);
prev_y = (mosaic_y >> 3);
int _pri;
_pri = (t & 0x2000) ? pri1_pos : pri0_pos;
t = bg_get_tile(bg, mosaic_x, mosaic_y);
tile_num = t & 0x03ff;
mirror_y = !!(t & 0x8000);
mirror_x = !!(t & 0x4000);
//16x16 horizontal tile mirroring
if(tile_width == 4) {
if(((mosaic_x & 15) >= 8 && !mirror_x) ||
((mosaic_x & 15) < 8 && mirror_x))tile_num++;
tile_num &= 0x03ff;
}
tile_pri = (t & 0x2000) ? pri1_pos : pri0_pos;
tile_num = t;
//16x16 vertical tile mirroring
if(tile_height == 4) {
if(((mosaic_y & 15) >= 8 && !mirror_y) ||
((mosaic_y & 15) < 8 && mirror_y))tile_num += 16;
tile_num &= 0x03ff;
}
tile_num += (_tdaddr >> tiledata_size);
if(bg_td_state[tile_num] == 1) {
render_bg_tile(color_depth, tile_num);
}
pal_num = ((t >> 10) & 7);
pal_index = pal_num * pal_size + bgpal_index;
if(mirror_y) { ypos = (7 - (mosaic_y & 7)); }
else { ypos = ( (mosaic_y & 7)); }
//loop while we are rendering from the same tile, as there's no need to do all of the above work
//unless we have rendered all of the visible tile, taking mosaic into account.
tile_ptr = (uint8*)bg_td + (tile_num << 6) + (ypos << 3);
do {
if(mirror_x) { xpos = (7 - (mosaic_x & 7)); }
else { xpos = ( (mosaic_x & 7)); }
col = *(tile_ptr + xpos);
if(col && main_colorwindowtable[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 {
col = get_palette(col + pal_index);
}
if(_bg_enabled == true && !wt_main[screen_x]) {
if(pixel_cache[screen_x].pri_main < _pri) {
pixel_cache[screen_x].pri_main = _pri;
pixel_cache[screen_x].bg_main = 0x80 | bg;
pixel_cache[screen_x].src_main = col;
pixel_cache[screen_x].color_exempt = false;
}
}
if(_bgsub_enabled == true && !wt_sub[screen_x]) {
if(pixel_cache[screen_x].pri_sub < _pri) {
pixel_cache[screen_x].pri_sub = _pri;
pixel_cache[screen_x].bg_sub = 0x80 | bg;
pixel_cache[screen_x].src_sub = col;
}
}
if(tile_width == 4) { //16x16 horizontal tile mirroring
if(!!(mosaic_x & 8) != mirror_x)tile_num++;
}
bg_x++;
bg_x &= screen_width_mask;
mosaic_x = mtable[bg_x];
if(tile_height == 4) { //16x16 vertical tile mirroring
if(!!(mosaic_y & 8) != mirror_y)tile_num += 16;
}
if(base_xpos != ((mosaic_x >> 3) & 31))break;
if(++screen_x >= _screen_width)break;
} while(1);
} while(++screen_x < _screen_width);
tile_num &= 0x03ff;
tile_num += tiledata_index;
tile_num &= tile_mask;
if(bg_td_state[tile_num] == 1) {
render_bg_tile(color_depth, tile_num);
}
pal_num = ((t >> 10) & 7);
pal_index = (pal_num * pal_size) + bgpal_index;
ypos = mosaic_y & 7;
if(mirror_y)ypos ^= 7; //invert y tile pos
tile_ptr = (uint8*)bg_td + (tile_num * 64) + (ypos * 8);
}
xpos = mosaic_x & 7;
if(mirror_x)xpos ^= 7; //invert x tile pos
col = *(tile_ptr + xpos);
if(col) {
if(regs.direct_color == true && bg == BG1 && (regs.bg_mode == 3 || regs.bg_mode == 4)) {
col = get_direct_color(pal_num, col);
} else {
col = get_palette(col + pal_index);
}
if(bg_enabled == true && !wt_main[x]) {
if(pixel_cache[x].pri_main < tile_pri) {
pixel_cache[x].pri_main = tile_pri;
pixel_cache[x].bg_main = 0x80 | bg;
pixel_cache[x].src_main = col;
pixel_cache[x].color_exempt = false;
}
}
if(bgsub_enabled == true && !wt_sub[x]) {
if(pixel_cache[x].pri_sub < tile_pri) {
pixel_cache[x].pri_sub = tile_pri;
pixel_cache[x].bg_sub = 0x80 | bg;
pixel_cache[x].src_sub = col;
}
}
}
}
}
+21 -1
View File
@@ -3,6 +3,7 @@
if(d0 & __m)col += 1; \
if(d1 & __m)col += 2; \
*dest++ = col
#define render_bg_tile_line_16(__m) \
col = 0; \
if(d0 & __m)col += 1; \
@@ -10,6 +11,7 @@
if(d2 & __m)col += 4; \
if(d3 & __m)col += 8; \
*dest++ = col
#define render_bg_tile_line_256(__m) \
col = 0; \
if(d0 & __m)col += 1; \
@@ -96,6 +98,10 @@ uint8 *dest;
}
}
#undef render_bg_tile_line_4
#undef render_bg_tile_line_16
#undef render_bg_tile_line_256
inline void bPPU::clear_pixel_cache() {
memset(pixel_cache, 0, sizeof(pixel_cache));
}
@@ -112,8 +118,22 @@ void bPPU::init_tiledata_cache() {
void bPPU::clear_tiledata_cache() {
memset(bg_tiledata[TILE_2BIT], 0, 262144);
memset(bg_tiledata[TILE_4BIT], 0, 131072);
memset(bg_tiledata[TILE_4BIT], 0, 65536);
memset(bg_tiledata[TILE_8BIT], 0, 65536);
memset(bg_tiledata_state[TILE_2BIT], 0, 4096);
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);
}
+48 -60
View File
@@ -1,81 +1,69 @@
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) {
inline uint16 bPPU::get_pixel(uint32 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(regs.addsub_mode) {
_r = addsub_pixels(p->src_main, p->src_sub);
} else {
_r = addsub_pixel(p->src_main);
}
} else {
_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]) {
_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.addsub_mode) {
_r = addsub_pixels(src_back, p->src_sub);
} else {
_r = addsub_pixel(src_back);
}
} else {
_r = src_back;
}
} else {
_r = 0x0000;
}
if(!p->bg_main) {
p->bg_main = BACK;
p->src_main = get_palette(0);
p->color_exempt = false;
} else {
if(main_colorwindowtable[x]) {
if(regs.bg_color_enabled[BACK] && sub_colorwindowtable[x]) {
_r = addsub_pixel(src_back);
} else {
_r = src_back;
}
} else {
_r = 0x0000;
}
p->bg_main &= 0x7f;
}
return _r;
if(!p->bg_sub) {
p->bg_sub = BACK;
p->src_sub = regs.color_rgb;
} else {
p->bg_sub &= 0x7f;
}
if(!regs.addsub_mode) {
p->bg_sub = BACK;
p->src_sub = regs.color_rgb;
}
if(!window_cache[COL].main[x]) {
if(!window_cache[COL].sub[x]) {
return 0x0000;
}
//p->bg_main remains the same, even when the main color window
//masks out the color. this is needed for regs.color_enabled[p->bg_main]
//test below. illusion of gaia relies on this behavior for its load menu.
p->src_main = 0x0000;
}
if(!p->color_exempt && regs.color_enabled[p->bg_main] && window_cache[COL].sub[x]) {
return addsub_pixels(x);
}
return p->src_main;
}
inline void bPPU::render_line_output() {
int x;
uint16 _r;
uint16 *ptr;
ptr = (uint16*)snes->get_ppu_output_handle();
uint16 r, x;
uint16 *ptr = (uint16*)output + (line.y * 1024) +
((line.interlace && line.interlace_field) ? 512 : 0);
uint16 *ltable = (uint16*)light_table + (regs.display_brightness << 15);
uint16 *ltable;
ltable = (uint16*)light_table + (regs.display_brightness << 15);
if(_screen_width == 256) {
if(line.width == 256) {
for(x=0;x<256;x++) {
_r = get_pixel(x);
*ptr++ = *(ltable + _r);
r = get_pixel(x);
*ptr++ = *(ltable + r);
}
} else {
for(x=0;x<512;x++) {
_r = get_pixel(x);
*ptr++ = *(ltable + _r);
r = get_pixel(x);
*ptr++ = *(ltable + r);
}
}
}
+25 -26
View File
@@ -31,20 +31,21 @@ 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;
y = 255 - line.y;
} else {
y = _y;
y = line.y;
}
uint16 *mtable_x, *mtable_y;
@@ -119,29 +120,27 @@ int32 psy = ((c * CLIP(hofs - cx)) & ~63) + ((d * CLIP(vofs - cy)) & ~63) + ((d
_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);
}
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.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;
}
}
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;
}
}
}
+130 -79
View File
@@ -63,10 +63,10 @@ uint16 width, height;
addr &= 0x001f;
z = oam[0x0200 + addr];
i = addr << 2;
sprite_list[i ].x = ((z & 0x01)?0x0100:0x0000) + (sprite_list[i ].x & 0xff);
sprite_list[i + 1].x = ((z & 0x04)?0x0100:0x0000) + (sprite_list[i + 1].x & 0xff);
sprite_list[i + 2].x = ((z & 0x10)?0x0100:0x0000) + (sprite_list[i + 2].x & 0xff);
sprite_list[i + 3].x = ((z & 0x40)?0x0100:0x0000) + (sprite_list[i + 3].x & 0xff);
sprite_list[i ].x = ((z & 0x01) ? 256 : 0) + (sprite_list[i ].x & 255);
sprite_list[i + 1].x = ((z & 0x04) ? 256 : 0) + (sprite_list[i + 1].x & 255);
sprite_list[i + 2].x = ((z & 0x10) ? 256 : 0) + (sprite_list[i + 2].x & 255);
sprite_list[i + 3].x = ((z & 0x40) ? 256 : 0) + (sprite_list[i + 3].x & 255);
get_sprite_size(i, !!(z & 0x02));
get_sprite_size(i + 1, !!(z & 0x08));
get_sprite_size(i + 2, !!(z & 0x20));
@@ -90,18 +90,18 @@ uint16 addr = 0x0200;
bool bPPU::is_sprite_on_scanline() {
//if sprite is entirely offscreen and doesn't wrap around to the left side of the screen,
//then it is not counted. 256 is correct, and not 255 -- as one might first expect
if(spr->x > 256 && (spr->x + spr->width) < 512 && _screen_width != 512)return false;
if(spr->x > 256 && (spr->x + spr->width) < 512)return false;
if(regs.oam_halve == false) {
if(_y >= spr->y && _y < (spr->y + spr->height)) {
if(line.y >= spr->y && line.y < (spr->y + spr->height)) {
return true;
} else if((spr->y + spr->height) >= 256 && _y < ((spr->y + spr->height) & 255)) {
} else if((spr->y + spr->height) >= 256 && line.y < ((spr->y + spr->height) & 255)) {
return true;
}
} else {
if(_y >= spr->y && _y < (spr->y + (spr->height >> 1))) {
if(line.y >= spr->y && line.y < (spr->y + (spr->height >> 1))) {
return true;
} else if((spr->y + (spr->height >> 1)) >= 256 && _y < ((spr->y + (spr->height >> 1)) & 255)) {
} else if((spr->y + (spr->height >> 1)) >= 256 && line.y < ((spr->y + (spr->height >> 1)) & 255)) {
return true;
}
}
@@ -110,62 +110,53 @@ bool bPPU::is_sprite_on_scanline() {
}
void bPPU::load_oam_tiles() {
uint16 tile_width;
tile_width = spr->width >> 3;
uint16 tile_width = spr->width >> 3;
int x = spr->x;
int y = (spr->vflip) ? ((spr->height - 1) - (line.y - spr->y)) : (line.y - spr->y);
int x, y, chr, nameselect_index;
x = spr->x;
if(_screen_width == 512)x <<= 1;
x &= 511;
if(spr->vflip) {
y = ((spr->height - 1) - (_y - spr->y));
} else {
y = (_y - spr->y);
}
//todo: double-check code below. seems that interlace_field
//should be added to hires 512x448 sprites as well, and not
//just when oam_halve is enabled...
if(regs.oam_halve == true) {
y <<= 1;
if(_interlace == true && _screen_width == 512) {
y += _interlace_field;
if(line.interlace && line.width == 512) {
y += line.interlace_field;
}
}
x &= 511;
y &= 255;
chr = spr->character;
uint16 tdaddr = regs.oam_tdaddr;
uint16 chrx = (spr->character ) & 15;
uint16 chry = (spr->character >> 4) & 15;
if(spr->use_nameselect == true) {
chr += 256;
nameselect_index = regs.oam_nameselect << 13;
} else {
nameselect_index = 0x0000;
tdaddr += (256 * 32) + (regs.oam_nameselect << 13);
}
chr += (y >> 3) << 4;
chry += (y >> 3);
chry &= 15;
chry <<= 4;
int i, n, mx, pos, z;
int i, n, sx, mx, pos;
for(i=0;i<tile_width;i++) {
z = x;
z += (i << ((_screen_width == 512)?4:3));
z &= 511;
sx = x;
sx += i << 3;
sx &= 511;
//ignore sprites that are offscreen
//sprites at 256 are still counted, even though they aren't visible onscreen
if(z >= 257 && (z + 7) < 512 && _screen_width != 512)continue;
if(sx >= 257 && (sx + 7) < 512)continue;
if(regs.oam_tilecount++ > 34)break;
n = regs.oam_tilecount - 1;
oam_tilelist[n].x = z;
oam_tilelist[n].x = sx;
oam_tilelist[n].y = y;
oam_tilelist[n].pri = spr->priority;
oam_tilelist[n].pal = (spr->palette << 4) + 128;
oam_tilelist[n].hflip = spr->hflip;
if(oam_tilelist[n].hflip) {
mx = (tile_width - 1) - i;
} else {
mx = i;
}
pos = regs.oam_tdaddr + ((chr + mx) << 5) + ((y & 7) << 1) + nameselect_index;
mx = (oam_tilelist[n].hflip) ? ((tile_width - 1) - i) : i;
pos = tdaddr + ((chry + ((chrx + mx) & 15)) << 5);
oam_tilelist[n].tile = (pos >> 5) & 0x07ff;
}
}
@@ -182,38 +173,26 @@ oam_tileitem *t = &oam_tilelist[tile_num];
int x, sx, col;
sx = t->x;
//tile_ptr = tiledata + (tile * (8_width * 8_height)) + ((y & 7_height_mask) * 8_width);
tile_ptr = (uint8*)oam_td + (t->tile << 6) + ((t->y & 7) << 3);
for(x=0;x<8;x++) {
sx &= 511;
if(sx < _screen_width) {
col = *(tile_ptr + ((t->hflip)?7-x:x));
if(sx < 256) {
col = *(tile_ptr + ((t->hflip) ? (7 - x) : x));
if(col) {
col += t->pal;
oam_line_pal[sx] = col;
oam_line_pri[sx] = t->pri;
if(_screen_width == 512) {
oam_line_pal[sx + 1] = col;
oam_line_pri[sx + 1] = t->pri;
}
}
}
sx += (_screen_width == 512)?2:1;
sx++;
}
}
void bPPU::render_line_oam(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos) {
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);
int s;
regs.oam_itemcount = 0;
regs.oam_tilecount = 0;
memset(oam_line_pri, OAM_PRI_NONE, 512);
memset(oam_line_pri, OAM_PRI_NONE, 256);
memset(oam_itemlist, 0xff, 32);
for(s=0;s<128;s++) {
@@ -239,33 +218,105 @@ uint8 *wt_sub = sub_windowtable[OAM];
regs.time_over |= (regs.oam_tilecount > 34);
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(regs.bg_enabled[OAM] == false && regs.bgsub_enabled[OAM] == false)return;
if(line.width == 256) {
render_line_oam_lores(pri0_pos, pri1_pos, pri2_pos, pri3_pos);
} else {
render_line_oam_hires(pri0_pos, pri1_pos, pri2_pos, pri3_pos);
}
}
void bPPU::render_line_oam_lores(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos) {
bool bg_enabled = regs.bg_enabled[OAM];
bool bgsub_enabled = regs.bgsub_enabled[OAM];
build_window_tables(OAM);
uint8 *wt_main = window_cache[OAM].main;
uint8 *wt_sub = window_cache[OAM].sub;
int pri;
for(int x=0;x<256;x++) {
if(oam_line_pri[x] == OAM_PRI_NONE)continue;
switch(oam_line_pri[x]) {
case 0:_pri = pri0_pos;break;
case 1:_pri = pri1_pos;break;
case 2:_pri = pri2_pos;break;
case 3:_pri = pri3_pos;break;
case 0:pri = pri0_pos;break;
case 1:pri = pri1_pos;break;
case 2:pri = pri2_pos;break;
case 3:pri = pri3_pos;break;
}
if(main_colorwindowtable[x]) {
if(_bg_enabled == true && !wt_main[x]) {
if(pixel_cache[x].pri_main < _pri) {
pixel_cache[x].pri_main = _pri;
pixel_cache[x].bg_main = PC_OAM;
pixel_cache[x].src_main = get_palette(oam_line_pal[x]);
pixel_cache[x].color_exempt = (oam_line_pal[x] < 192);
}
if(bg_enabled == true && !wt_main[x]) {
if(pixel_cache[x].pri_main < pri) {
pixel_cache[x].pri_main = pri;
pixel_cache[x].bg_main = PC_OAM;
pixel_cache[x].src_main = get_palette(oam_line_pal[x]);
pixel_cache[x].color_exempt = (oam_line_pal[x] < 192);
}
if(_bgsub_enabled == true && !wt_sub[x]) {
if(pixel_cache[x].pri_sub < _pri) {
pixel_cache[x].pri_sub = _pri;
pixel_cache[x].bg_sub = PC_OAM;
pixel_cache[x].src_sub = get_palette(oam_line_pal[x]);
}
}
if(bgsub_enabled == true && !wt_sub[x]) {
if(pixel_cache[x].pri_sub < pri) {
pixel_cache[x].pri_sub = pri;
pixel_cache[x].bg_sub = PC_OAM;
pixel_cache[x].src_sub = get_palette(oam_line_pal[x]);
}
}
}
}
void bPPU::render_line_oam_hires(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos) {
bool bg_enabled = regs.bg_enabled[OAM];
bool bgsub_enabled = regs.bgsub_enabled[OAM];
build_window_tables(OAM);
uint8 *wt_main = window_cache[OAM].main;
uint8 *wt_sub = window_cache[OAM].sub;
int pri, sx;
for(int x=0;x<256;x++) {
if(oam_line_pri[x] == OAM_PRI_NONE)continue;
switch(oam_line_pri[x]) {
case 0:pri = pri0_pos;break;
case 1:pri = pri1_pos;break;
case 2:pri = pri2_pos;break;
case 3:pri = pri3_pos;break;
}
sx = x << 1;
if(bg_enabled == true && !wt_main[sx]) {
if(pixel_cache[sx].pri_main < pri) {
pixel_cache[sx].pri_main = pri;
pixel_cache[sx].bg_main = PC_OAM;
pixel_cache[sx].src_main = get_palette(oam_line_pal[x]);
pixel_cache[sx].color_exempt = (oam_line_pal[x] < 192);
}
}
if(bgsub_enabled == true && !wt_sub[sx]) {
if(pixel_cache[sx].pri_sub < pri) {
pixel_cache[sx].pri_sub = pri;
pixel_cache[sx].bg_sub = PC_OAM;
pixel_cache[sx].src_sub = get_palette(oam_line_pal[x]);
}
}
sx++;
if(bg_enabled == true && !wt_main[sx]) {
if(pixel_cache[sx].pri_main < pri) {
pixel_cache[sx].pri_main = pri;
pixel_cache[sx].bg_main = PC_OAM;
pixel_cache[sx].src_main = get_palette(oam_line_pal[x]);
pixel_cache[sx].color_exempt = (oam_line_pal[x] < 192);
}
}
if(bgsub_enabled == true && !wt_sub[sx]) {
if(pixel_cache[sx].pri_sub < pri) {
pixel_cache[sx].pri_sub = pri;
pixel_cache[sx].bg_sub = PC_OAM;
pixel_cache[sx].src_sub = get_palette(oam_line_pal[x]);
}
}
}
+76 -129
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, line.width);
return;
}
if(mainscreen == false && regs.sub_window_enabled[bg] == false) {
memset(wtbl, 0, line.width);
return;
}
} else {
uint8 mask;
if(mainscreen == true) {
mask = regs.color_mask;
} else {
mask = regs.colorsub_mask;
}
if(mask == 0) {
//always
memset(wtbl, 1, line.width);
return;
}
if(mask == 3) {
//never
memset(wtbl, 0, line.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;
@@ -17,61 +56,61 @@ bool r;
window2_left = regs.window2_left;
window2_right = regs.window2_right;
if(_screen_width == 512) {
if(line.width == 512) {
window1_left <<= 1;
window1_right <<= 1;
window2_left <<= 1;
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) {
for(x=0;x<_screen_width;x++) {
wtbl[x] = (x >= window1_left && x <= window1_right)?true:false;
if(regs.window1_enabled[bg] == false && regs.window2_enabled[bg] == false) {
memset(wtbl, clr, line.width);
} else if(regs.window1_enabled[bg] == true && regs.window2_enabled[bg] == false) {
if(regs.window1_invert[bg] == false) {
for(x=0;x<line.width;x++) {
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;
for(x=0;x<line.width;x++) {
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) {
for(x=0;x<_screen_width;x++) {
wtbl[x] = (x >= window2_left && x <= window2_right)?true:false;
} else if(regs.window1_enabled[bg] == false && regs.window2_enabled[bg] == true) {
if(regs.window2_invert[bg] == false) {
for(x=0;x<line.width;x++) {
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;
for(x=0;x<line.width;x++) {
wtbl[x] = (x < window2_left || x > window2_right) ? set : clr;
}
}
} else { //if(regs.bg_window1_enabled[bg] == true && regs.bg_window2_enabled[bg] == true) {
for(x=0;x<_screen_width;x++) {
if(regs.bg_window1_invert[bg] == false) {
} else { //if(regs.window1_enabled[bg] == true && regs.window2_enabled[bg] == true) {
for(x=0;x<line.width;x++) {
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);
}
-148
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
+39 -5
View File
@@ -2,16 +2,50 @@
void PPU::get_scanline_info(scanline_info *info) {
info->hires = scanline_is_hires();
info->interlace = cpu->interlace();
info->interlace = r_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() {
status.frame_executed = true;
static int32 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;
}
void PPU::power() {}
void PPU::reset() {
memset(output, 0, 512 * 480 * sizeof(uint16));
}
PPU::PPU() {
output = (uint16*)malloc(512 * 480 * sizeof(uint16));
memset(output, 0, 512 * 480 * sizeof(uint16));
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;
}
PPU::~PPU() {}
PPU::~PPU() {
zerofree(output);
}
+18 -6
View File
@@ -1,5 +1,18 @@
class PPU {
public:
uint16 *output;
//this struct should be read-only to
//functions outside of this class
struct {
bool render_output;
bool frame_executed;
bool frames_updated;
uint32 frames_rendered;
uint32 frames_executed;
} status;
//PPU1 version number
//* 1 is known
//* reported by $213e
@@ -10,7 +23,6 @@ uint8 ppu1_version;
//* reported by $213f
uint8 ppu2_version;
int _y;
MMIO *mmio;
struct scanline_info {
@@ -32,11 +44,11 @@ struct scanline_info {
virtual void run() = 0;
virtual void scanline() = 0;
virtual void render_scanline() = 0;
virtual void frame() = 0;
virtual void power() = 0;
virtual void reset() = 0;
virtual void set_frameskip(int fs);
virtual bool render_frame();
virtual void frame();
virtual void power();
virtual void reset();
virtual void enable_renderer(bool r);
virtual bool renderer_enabled();
PPU();
~PPU();
+2 -5
View File
@@ -1,11 +1,11 @@
CC = c++
CFLAGS = -O2
CFLAGS = -O3 -fomit-frame-pointer -ffast-math
OBJS = sdlmain.o \
libstring.o libconfig.o \
reader.o \
memory.o bmemory.o \
cpu.o bcpu.o \
apu.o bapu.o bapuskip.o \
apu.o bapu.o \
bdsp.o \
ppu.o bppu.o \
snes.o \
@@ -58,9 +58,6 @@ apu.o: ../apu/*
bapu.o: ../apu/bapu/*
$(CC) $(CFLAGS) -c ../apu/bapu/bapu.cpp
bapuskip.o: ../apu/bapuskip/*
$(CC) $(CFLAGS) -c ../apu/bapuskip/bapuskip.cpp
###########
### dsp ###
###########
+2 -5
View File
@@ -1,11 +1,11 @@
CC = cl
CFLAGS = /nologo /O2 /Ogityb2 /Gr /Gs
CFLAGS = /nologo /O2 /wd4996
OBJS = sdlmain.obj \
libstring.obj libconfig.obj \
reader.obj \
memory.obj bmemory.obj \
cpu.obj bcpu.obj \
apu.obj bapu.obj bapuskip.obj \
apu.obj bapu.obj \
bdsp.obj \
ppu.obj bppu.obj \
snes.obj \
@@ -59,9 +59,6 @@ apu.obj: ../apu/*
bapu.obj: ../apu/bapu/*
$(CC) $(CFLAGS) /c ../apu/bapu/bapu.cpp
bapuskip.obj: ../apu/bapuskip/*
$(CC) $(CFLAGS) /c ../apu/bapuskip/bapuskip.cpp
###########
### dsp ###
###########
+44 -18
View File
@@ -6,28 +6,61 @@ void bSNES::run() {
switch(run_status) {
case RUN:
while(update_frame == false) {
SNES::run();
}
update_frame = false;
render();
SNES::runtoframe();
return;
case STOP:
break;
}
}
void bSNES::video_run() { render(); }
void bSNES::sound_run() {}
void bSNES::video_run() {
if(r_ppu->status.frames_updated) {
char s[512], t[512];
r_ppu->status.frames_updated = false;
// if((bool)config::gui.show_fps == true) {
sprintf(s, "%s : %d fps", BSNES_TITLE, r_ppu->status.frames_executed);
// if(w_main->frameskip != 0) {
// sprintf(t, " (%d frames)", r_ppu->status.frames_rendered);
// strcat(s, t);
// }
SDL_WM_SetCaption(s, 0);
// }
}
render();
}
void bSNES::sound_run(uint32 data) {}
uint16 *bSNES::video_lock(uint32 &pitch) {
if(SDL_MUSTLOCK(screen)) {
SDL_LockSurface(screen);
}
if(SDL_MUSTLOCK(backbuffer)) {
SDL_LockSurface(backbuffer);
}
pitch = backbuffer->pitch;
return (uint16*)backbuffer->pixels;
}
void bSNES::video_unlock() {
if(SDL_MUSTLOCK(backbuffer)) {
SDL_UnlockSurface(backbuffer);
}
if(SDL_MUSTLOCK(screen)) {
SDL_UnlockSurface(screen);
}
}
/***********************
*** 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];
@@ -77,15 +110,8 @@ bJoypad::bJoypad() {
l = r = select = start = false;
}
void bSNES::notify(uint32 message, uint32 param1, uint32 param2) {
switch(message) {
case RENDER_FRAME:
update_frame = true;
break;
}
}
void bSNES::notify(uint32 message, uint32 param1, uint32 param2) {}
bSNES::bSNES() {
run_status = STOP;
update_frame = false;
run_status = STOP;
}
+9 -7
View File
@@ -9,20 +9,22 @@ bool l, r, select, start;
class bSNES : public SNES {
private:
uint32 run_status;
bool update_frame;
bJoypad joypad1, joypad2;
public:
enum { STOP = 0, RUN };
void run();
void video_run();
void sound_run();
void run();
void video_run();
void sound_run(uint32 data);
void set_status(uint32 new_status);
uint32 get_status();
uint16 *video_lock(uint32 &data);
void video_unlock();
void set_status(uint32 new_status);
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
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
+2
View File
@@ -1 +1,3 @@
@nmake /NOLOGO /f Makefile.win32 clean
@del output.wav
@del bsnes_sdl.exe
+1 -56
View File
@@ -1,59 +1,11 @@
void render16() {
uint16 *dest, *src;
uint32 pitch;
int x, y;
void render() {
SNES::video_info vi;
snes->get_video_info(&vi);
pitch = (backbuffer->pitch >> 1);
dest = (uint16*)backbuffer->pixels;
src = (uint16*)vi.data;
if(vi.width == 256 && vi.height == 224) {
for(y=0;y<224;y++) {
memcpy(dest, src, 512);
dest += pitch;
src += 256;
}
} else if(vi.width == 512 && vi.height == 224) {
for(y=0;y<224;y++) {
memcpy(dest, src, 1024);
dest += pitch;
src += 512;
}
} else if(vi.width == 256 && vi.height == 448) {
for(y=0;y<448;y++) {
memcpy(dest, src, 512);
dest += pitch;
src += 256;
}
} else if(vi.width == 512 && vi.height == 448) {
for(y=0;y<448;y++) {
memcpy(dest, src, 1024);
dest += pitch;
src += 512;
}
}
screen_info.rs.x = 0;
screen_info.rs.y = (vi.height == 224) ? 1 : 2;
screen_info.rs.w = vi.width;
screen_info.rs.h = (vi.height == 224) ? 223 : 446;
}
void render32() {}
void render() {
if(SDL_MUSTLOCK(screen)) {
SDL_LockSurface(screen);
}
if(SDL_MUSTLOCK(backbuffer)) {
SDL_LockSurface(backbuffer);
}
render16();
//documentation says not to use this, but it's rather ridiculous that a graphics
//library wouldn't support simple image scaling... so let's use it anyway and see
@@ -61,12 +13,5 @@ void render() {
SDL_SoftStretch(backbuffer, &screen_info.rs, screen, &screen_info.rd);
//SDL_BlitSurface(backbuffer, &screen_info.rs, screen, &screen_info.rd);
if(SDL_MUSTLOCK(backbuffer)) {
SDL_UnlockSurface(backbuffer);
}
if(SDL_MUSTLOCK(screen)) {
SDL_UnlockSurface(screen);
}
SDL_UpdateRect(screen, screen_info.rd.x, screen_info.rd.y, screen_info.rd.w, screen_info.rd.h);
}
+7 -7
View File
@@ -27,14 +27,14 @@ FileReader *rf = new FileReader();
alert("Error loading image file [%s]!", rom_fn);
return false;
}
mem_bus->load_cart(static_cast<Reader*>(rf));
r_mem->load_cart(static_cast<Reader*>(rf));
rf->close();
CartInfo ci;
mem_bus->get_cartinfo(&ci);
r_mem->get_cartinfo(&ci);
if(ci.sram_size != 0) {
rf->open(sram_fn);
mem_bus->load_sram(static_cast<Reader*>(rf));
r_mem->load_sram(static_cast<Reader*>(rf));
rf->close();
}
@@ -49,25 +49,25 @@ void ROMImage::unload() {
FileWriter *wf;
CartInfo ci;
mem_bus->get_cartinfo(&ci);
r_mem->get_cartinfo(&ci);
if(ci.sram_size != 0) {
wf = new FileWriter();
wf->open(sram_fn);
mem_bus->save_sram(static_cast<Writer*>(wf));
r_mem->save_sram(static_cast<Writer*>(wf));
wf->close();
delete(wf);
}
file_loaded = false;
mem_bus->unload_cart();
r_mem->unload_cart();
}
void ROMImage::select(char *fn) {
int i;
if(file_loaded == true)return;
/* Remove quotes */
//remove quotes
if(fn[0] == '\"') {
strcpy(rom_fn, fn + 1);
rom_fn[strlen(rom_fn) - 1] = 0;
+19 -17
View File
@@ -44,13 +44,15 @@ va_list args;
}
void init_snes() {
mem_bus = new bMemBus();
cpu = new bCPU();
apu = new bAPU();
dsp = new bDSP();
ppu = new bPPU();
snes = new bSNES();
bsnes = static_cast<bSNES*>(snes);
#ifdef POLYMORPHISM
deref(mem) = new bMemBus();
deref(cpu) = new bCPU();
deref(apu) = new bAPU();
deref(dsp) = new bDSP();
deref(ppu) = new bPPU();
#endif
snes = new bSNES();
bsnes = static_cast<bSNES*>(snes);
snes->init();
@@ -59,18 +61,18 @@ void init_snes() {
//play audio in real-time while sound output
//isn't available.
snes->log_audio_enable("output.wav");
snes->set_playback_buffer_size(2000);
}
void term_snes() {
snes->term();
if(mem_bus) { delete(static_cast<bMemBus*>(mem_bus)); mem_bus = 0; }
if(cpu) { delete(static_cast<bCPU*> (cpu)); cpu = 0; }
if(apu) { delete(static_cast<bAPU*> (apu)); apu = 0; }
if(ppu) { delete(static_cast<bPPU*> (ppu)); ppu = 0; }
if(snes) { delete(static_cast<bSNES*> (snes)); snes = 0; }
#ifdef POLYMORPHISM
if(deref(mem)) { delete static_cast<bMemBus*>(deref(mem)); deref(mem) = 0; }
if(deref(cpu)) { delete static_cast<bCPU*> (deref(cpu)); deref(cpu) = 0; }
if(deref(apu)) { delete static_cast<bAPU*> (deref(apu)); deref(apu) = 0; }
if(deref(dsp)) { delete static_cast<bDSP*> (deref(dsp)); deref(dsp) = 0; }
if(deref(ppu)) { delete static_cast<bPPU*> (deref(ppu)); deref(ppu) = 0; }
#endif
if(snes) { delete(static_cast<bSNES*>(snes)); snes = 0; }
}
void center_window() {
@@ -133,7 +135,7 @@ SDL_Event event;
atexit(SDL_Quit);
set_window_info();
screen = SDL_SetVideoMode(config::video.display_width, config::video.display_height, 16,
SDL_SWSURFACE | ((config::video.fullscreen)?SDL_FULLSCREEN:0));
SDL_SWSURFACE | ((config::video.fullscreen) ? SDL_FULLSCREEN : 0));
if(!screen) { alert("Failed to initialize SDL"); goto _end; }
backbuffer = SDL_CreateRGBSurface(SDL_SWSURFACE, 512, 448, 16, 0xf800, 0x07e0, 0x001f, 0x0000);
if(!backbuffer) { alert("Failed to initialize SDL"); goto _end; }
@@ -160,7 +162,7 @@ int cursor_status;
snes->capture_screenshot();
break;
case SDLK_F10: //toggle cursor display
cursor_status = (SDL_ShowCursor(SDL_QUERY) == SDL_ENABLE)?SDL_DISABLE:SDL_ENABLE;
cursor_status = (SDL_ShowCursor(SDL_QUERY) == SDL_ENABLE) ? SDL_DISABLE : SDL_ENABLE;
SDL_ShowCursor(cursor_status);
break;
case SDLK_F11: //only supported on X11
+30 -31
View File
@@ -5,13 +5,12 @@
#include "snes_input.cpp"
void SNES::run() {
uint32 cycles, r;
if(apusync.cycles < 0) {
cpu->run();
apusync.cycles += apusync.apu_multbl[cpu->cycles_executed()];
r_cpu->run();
apusync.cycles += apusync.apu_multbl[r_cpu->cycles_executed()];
} else {
apu->run();
cycles = apu->cycles_executed();
r_apu->run();
uint32 cycles = r_apu->cycles_executed();
apusync.dsp += cycles;
apusync.cycles -= apusync.cpu_multbl[cycles];
@@ -19,11 +18,18 @@ uint32 cycles, r;
//24576000(Sound clock crystal) / 32000(DSP) = 768crystal/dsp ticks
while(apusync.dsp >= 768) {
apusync.dsp -= 768;
audio_update(dsp->run());
audio_update(r_dsp->run());
}
}
}
void SNES::runtoframe() {
while(r_ppu->status.frame_executed == false) {
SNES::run();
}
r_ppu->status.frame_executed = false;
}
void SNES::init() {
srtc = new SRTC();
sdd1 = new SDD1();
@@ -40,49 +46,44 @@ void SNES::term() {
}
void SNES::power() {
cpu->power();
apu->power();
dsp->power();
ppu->power();
mem_bus->power();
r_cpu->power();
r_apu->power();
r_dsp->power();
r_ppu->power();
r_mem->power();
srtc->power();
sdd1->power();
int i;
mem_bus->flush_mmio_mappers();
for(i=0x2100;i<=0x213f;i++)mem_bus->set_mmio_mapper(i, ppu->mmio);
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);
r_mem->flush_mmio_mappers();
for(i=0x2100;i<=0x213f;i++)r_mem->set_mmio_mapper(i, r_ppu->mmio);
for(i=0x2140;i<=0x217f;i++)r_mem->set_mmio_mapper(i, r_cpu->mmio);
for(i=0x2180;i<=0x2183;i++)r_mem->set_mmio_mapper(i, r_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);
for(i=0x4300;i<=0x437f;i++)mem_bus->set_mmio_mapper(i, cpu->mmio);
r_mem->set_mmio_mapper(0x4016, r_cpu->mmio);
r_mem->set_mmio_mapper(0x4017, r_cpu->mmio);
for(i=0x4200;i<=0x421f;i++)r_mem->set_mmio_mapper(i, r_cpu->mmio);
for(i=0x4300;i<=0x437f;i++)r_mem->set_mmio_mapper(i, r_cpu->mmio);
srtc->enable();
sdd1->enable();
memset(video.data, 0, 512 * 448 * sizeof(uint32));
memset(video.ppu_data, 0, 512 * 480 * sizeof(uint16));
video_update();
}
void SNES::reset() {
apusync.cycles = -apusync.cpu_multbl[32];
cpu->reset();
apu->reset();
dsp->reset();
ppu->reset();
mem_bus->reset();
r_cpu->reset();
r_apu->reset();
r_dsp->reset();
r_ppu->reset();
r_mem->reset();
srtc->reset();
sdd1->reset();
memset(video.data, 0, 512 * 448 * sizeof(uint32));
memset(video.ppu_data, 0, 512 * 480 * sizeof(uint16));
video_update();
}
@@ -155,6 +156,4 @@ SNES::SNES() {
snes_region = NTSC;
update_timing();
dsp_buffer.data = 0;
}
+2 -2
View File
@@ -19,7 +19,8 @@ public:
enum { NTSC = 0, PAL = 1 };
//system functions
virtual void run();
virtual inline void run();
virtual inline void runtoframe();
virtual void init();
virtual void term();
virtual void power();
@@ -39,7 +40,6 @@ enum { NTSC = 0, PAL = 1 };
//debugging functions
enum {
NO_ACTION = 0,
RENDER_FRAME,
CPU_EXEC_OPCODE_BEGIN, CPU_EXEC_OPCODE_END,
APU_EXEC_OPCODE_BEGIN, APU_EXEC_OPCODE_END,
MEM_READ, MEM_WRITE,
+1 -27
View File
@@ -1,25 +1,3 @@
void SNES::set_playback_buffer_size(uint32 buffer_size) {
if(dsp_buffer.data) {
free(dsp_buffer.data);
dsp_buffer.data = 0;
}
//* 2 is for left/right channel data
dsp_buffer.data = (uint16*)malloc(buffer_size * sizeof(uint16) * 2);
memset(dsp_buffer.data, 0, buffer_size * sizeof(uint16) * 2);
dsp_buffer.size = buffer_size;
dsp_buffer.pos = 0;
}
uint32 SNES::get_playback_buffer_pos() {
return dsp_buffer.pos;
}
uint16 *SNES::get_playback_buffer() {
return dsp_buffer.data;
}
void SNES::audio_update(uint32 data) {
if(pcmfp) {
fputc(data, pcmfp);
@@ -30,11 +8,7 @@ void SNES::audio_update(uint32 data) {
if((bool)config::snes.mute == true)data = 0x0000;
dsp_buffer.data[dsp_buffer.pos++] = (data) & 0xffff;
dsp_buffer.data[dsp_buffer.pos++] = (data >> 16) & 0xffff;
dsp_buffer.pos %= dsp_buffer.size;
sound_run();
sound_run(data);
}
void SNES::log_audio_enable(const char *fn) {
+1 -11
View File
@@ -1,15 +1,5 @@
struct {
uint16 *data;
uint32 size, pos;
} dsp_buffer;
FILE *pcmfp;
//buffer_size is in samples
void set_playback_buffer_size(uint32 buffer_size);
uint32 get_playback_buffer_pos();
uint16 *get_playback_buffer();
//if a filename is not specified, one will be generated
//automatically ("audio%0.3d.wav")
void log_audio_enable(const char *fn = 0);
@@ -19,4 +9,4 @@ FILE *pcmfp;
void audio_init();
void audio_term();
virtual void sound_run() = 0;
virtual void sound_run(uint32 data) = 0;
+1 -1
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.
+139 -165
View File
@@ -8,110 +8,68 @@ const uint8 SNES::color_curve_table[32] = {
};
void SNES::update_color_lookup_table() {
int i, l, r, g, b;
int32 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 = int32(((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;
}
}
@@ -134,7 +92,6 @@ void SNES::update_video_format() {
}
void SNES::get_video_info(video_info *info) {
info->data = video.data;
info->mode = video.mode;
switch(video.mode) {
@@ -171,10 +128,10 @@ void SNES::get_video_info(video_info *info) {
}
}
void SNES::video_update_256x224(uint16 *src) {
void SNES::video_update_256x224() {
int x, y;
uint16 *dest;
dest = video.data;
uint16 *src = video.ppu_data;
uint16 *dest = video.data;
for(y=0;y<224;y++) {
if(video_frame[y].hires == false) {
@@ -189,13 +146,14 @@ uint16 *dest;
}
src += 512;
}
dest += video.pitch - 256;
}
}
void SNES::video_update_512x224(uint16 *src) {
void SNES::video_update_512x224() {
int x, y;
uint16 *dest;
dest = video.data;
uint16 *src = video.ppu_data;
uint16 *dest = video.data;
for(y=0;y<224;y++) {
if(video_frame[y].hires == false) {
@@ -210,31 +168,41 @@ uint16 *dest;
}
src += 512;
}
dest += video.pitch - 512;
}
}
void SNES::video_update_256x448(uint16 *src) {
void SNES::video_update_256x448() {
int x, y;
uint16 *dest;
bool field = !cpu->interlace_field();
dest = video.data;
uint16 *src = video.ppu_data;
uint16 *dest = video.data;
bool field = !r_cpu->interlace_field();
for(y=0;y<224;y++) {
if(video_frame[y].interlace == false) {
if(video_frame[y].hires == false) {
for(x=0;x<512;x++) {
*dest++ = color_lookup_table[*(src + (uint8)x)];
for(x=0;x<256;x++) {
*dest++ = color_lookup_table[*(src + x)];
}
src += 1024;
dest += video.pitch - 256;
for(x=0;x<256;x++) {
*dest++ = color_lookup_table[*(src + x)];
}
dest += video.pitch - 256;
} else {
for(x=0;x<512;x++) {
*dest++ = color_lookup_table[*(src + ((x & 255) << 1))];
for(x=0;x<256;x++) {
*dest++ = color_lookup_table[*(src + (x << 1))];
}
src += 1024;
dest += video.pitch - 256;
for(x=0;x<256;x++) {
*dest++ = color_lookup_table[*(src + (x << 1))];
}
dest += video.pitch - 256;
}
src += 1024;
} else {
if(field) {
dest += 256;
dest += video.pitch;
src += 512;
}
@@ -249,38 +217,49 @@ bool field = !cpu->interlace_field();
src += 2;
}
}
dest += video.pitch - 256;
if(!field) {
dest += 256;
dest += video.pitch;
src += 512;
}
}
}
}
void SNES::video_update_512x448(uint16 *src) {
void SNES::video_update_512x448() {
int x, y;
uint16 *dest;
bool field = !cpu->interlace_field();
dest = video.data;
uint16 *src = video.ppu_data;
uint16 *dest = video.data;
bool field = !r_cpu->interlace_field();
for(y=0;y<224;y++) {
if(video_frame[y].interlace == false) {
if(video_frame[y].hires == false) {
for(x=0;x<512;x++) {
*dest++ = color_lookup_table[*(src + (uint8)x)];
*dest++ = color_lookup_table[*(src + (uint8)x)];
for(x=0;x<256;x++) {
*dest++ = color_lookup_table[*(src + x)];
*dest++ = color_lookup_table[*(src + x)];
}
src += 1024;
dest += video.pitch - 512;
for(x=0;x<256;x++) {
*dest++ = color_lookup_table[*(src + x)];
*dest++ = color_lookup_table[*(src + x)];
}
dest += video.pitch - 512;
} else {
for(x=0;x<1024;x++) {
*dest++ = color_lookup_table[*(src + (x & 511))];
for(x=0;x<512;x++) {
*dest++ = color_lookup_table[*(src + x)];
}
src += 1024;
dest += video.pitch - 512;
for(x=0;x<512;x++) {
*dest++ = color_lookup_table[*(src + x)];
}
dest += video.pitch - 512;
}
src += 1024;
} else {
if(field) {
dest += 512;
dest += video.pitch;
src += 512;
}
@@ -295,9 +274,10 @@ bool field = !cpu->interlace_field();
*dest++ = color_lookup_table[*src++];
}
}
dest += video.pitch - 512;
if(!field) {
dest += 512;
dest += video.pitch;
src += 512;
}
}
@@ -305,32 +285,37 @@ 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(r_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;
video.data = (uint16*)video_lock(video.pitch);
video.ppu_data = (uint16*)r_ppu->output + (int(r_cpu->overscan()) << 13);
video.pitch >>= 1;
if(video.data) {
switch(video.mode) {
case VM_256x224:video_update_256x224();break;
case VM_512x224:video_update_512x224();break;
case VM_256x448:video_update_256x448();break;
case VM_512x448:video_update_512x448();break;
case VM_VARIABLE:
switch(int(video.frame_hires) | (int(video.frame_interlace) << 1)) {
case 0:video_update_256x224();break;
case 1:video_update_512x224();break;
case 2:video_update_256x448();break;
case 3:video_update_512x448();break;
}
break;
}
video_unlock();
}
//SNES::capture_screenshot() was called by emulation interface
if(flag_output_screenshot == true) {
output_screenshot();
flag_output_screenshot = false;
}
}
video_run();
@@ -340,13 +325,13 @@ uint16 *src = (uint16*)video.ppu_data + ((int(cpu->overscan()) << 3) * 1024);
}
void SNES::video_scanline() {
int y = cpu->vcounter();
int o = int(cpu->overscan()) << 3;
int y = r_cpu->vcounter();
int o = int(r_cpu->overscan()) << 3;
if(y <= (0 + o) || y >= (224 + o))return;
y -= o;
PPU::scanline_info si;
ppu->get_scanline_info(&si);
r_ppu->get_scanline_info(&si);
video_frame[y].hires = si.hires;
video_frame[y].interlace = si.interlace;
@@ -355,21 +340,10 @@ PPU::scanline_info si;
video.frame_interlace |= si.interlace;
}
uint16 *SNES::get_ppu_output_handle() {
return (uint16*)(video.ppu_data +
(cpu->vcounter() * 1024) +
((cpu->interlace() && cpu->interlace_field())?512:0));
}
void SNES::video_init() {
int i, c;
video.format_changed = false;
video.data = (uint16*)malloc(512 * 448 * sizeof(uint32));
video.ppu_data = (uint16*)malloc(512 * 480 * sizeof(uint16));
memset(video.data, 0, 512 * 448 * sizeof(uint32));
memset(video.ppu_data, 0, 512 * 480 * sizeof(uint16));
for(i=0;i<224;i++) {
video_frame[i].hires = false;
video_frame[i].interlace = false;
+11 -11
View File
@@ -29,6 +29,7 @@ struct {
uint16 *data, *ppu_data;
uint8 mode;
uint8 depth;
uint32 pitch;
bool frame_hires, frame_interlace;
@@ -40,21 +41,20 @@ struct {
} video_frame[224];
struct video_info {
uint16 *data;
uint8 mode;
uint32 width, height;
uint32 mode, width, height;
};
//public functions
void capture_screenshot();
void update_color_lookup_table();
virtual void set_video_format(uint8 mode, uint8 depth);
virtual void get_video_info(video_info *info);
virtual void video_run() = 0;
virtual void set_video_format(uint8 mode, uint8 depth);
virtual void get_video_info(video_info *info);
virtual void video_run() = 0;
virtual uint16 *video_lock(uint32 &pitch) = 0;
virtual void video_unlock() = 0;
//private functions
uint16 *get_ppu_output_handle(); //used by PPU only
private:
//when a screenshot is requested, wait until the frame
//has finished rendering, so we can tell the image size
@@ -62,10 +62,10 @@ bool flag_output_screenshot;
uint16 to_rgb555(uint32 color);
void output_screenshot();
void update_video_format();
void video_update_256x224(uint16 *src);
void video_update_512x224(uint16 *src);
void video_update_256x448(uint16 *src);
void video_update_512x448(uint16 *src);
void video_update_256x224();
void video_update_512x224();
void video_update_256x448();
void video_update_512x448();
void video_update();
void video_scanline();
void video_init();
+6 -18
View File
@@ -5,25 +5,13 @@ void SNES::capture_screenshot() {
//used to convert pixel data to write to rgb555 format
//bitmap image via SNES::output_screenshot() function
uint16 SNES::to_rgb555(uint32 color) {
if(video.depth == 15) {
//rgb555
return color & 0x7fff;
}
if(video.depth == 16) {
//rgb565->rgb555
return ((color >> 1) & 0x7fe0) | (color & 0x001f);
}
if(video.depth == 24 || video.depth == 32) {
//rgb888->rgb555
return ((color >> 9) & 0x7c00) | ((color >> 6) & 0x03e0) | ((color >> 3) & 0x001f);
}
//unsupported color depth
return color;
//bgr555->rgb555
return ((color & 0x7c00) >> 10) | (color & 0x03e0) | ((color & 0x001f) << 10);
}
//this routine isn't perfect... it will fail if the video frame
//mixes resolutions (e.g. the top half is 256x224, and the bottom
//half is 512x224, etc.)
void SNES::output_screenshot() {
FILE *fp;
char fn[256];
@@ -95,7 +83,7 @@ int x, y;
uint16 c;
for(y=height;y>=1;y--) {
for(x=0;x<width;x++) {
c = to_rgb555(video.data[y * width + x]);
c = to_rgb555(video.ppu_data[y * 1024 + x]);
fputc(c, fp);
fputc(c >> 8, fp);
}
+5 -5
View File
@@ -1,11 +1,11 @@
CC = cl
CFLAGS = /nologo /O2 /Ogityb2 /Gr /Gs
CFLAGS = /nologo /O2 /wd4996
OBJS = winmain.obj \
libstring.obj libconfig.obj \
reader.obj \
memory.obj bmemory.obj \
cpu.obj bcpu.obj \
apu.obj bapu.obj bapuskip.obj \
apu.obj bapu.obj \
bdsp.obj \
ppu.obj bppu.obj \
snes.obj \
@@ -15,9 +15,12 @@ LIBS = kernel32.lib user32.lib gdi32.lib comdlg32.lib ddraw.lib dsound.lib dxgui
all: $(OBJS)
rc /r /fobsnes.res bsnes.rc
$(CC) /Febsnes.exe $(CFLAGS) $(OBJS) bsnes.res $(LIBS)
#/link /PGD:bsnes.pgd /LTCG:PGOPTIMIZE
clean:
del *.obj
del *.pgd
del *.pgc
######################
### win32-specific ###
@@ -60,9 +63,6 @@ apu.obj: ../apu/*
bapu.obj: ../apu/bapu/*
$(CC) $(CFLAGS) /c ../apu/bapu/bapu.cpp
bapuskip.obj: ../apu/bapuskip/*
$(CC) $(CFLAGS) /c ../apu/bapuskip/bapuskip.cpp
###########
### dsp ###
###########
+143 -55
View File
@@ -1,3 +1,12 @@
void bSNES::power() {
ds_sound->init();
SNES::power();
}
void bSNES::reset() {
SNES::reset();
}
void bSNES::set_status(uint32 new_status) {
uint8 cpu_op;
run_status = new_status;
@@ -12,7 +21,7 @@ uint8 cpu_op;
status.cpu_ran = false;
break;
case RUNTOCPUPROCEED:
cpu_op = mem_bus->read(cpu->regs.pc.d);
cpu_op = r_mem->read(r_cpu->regs.pc.d);
if(cpu_op == 0x10 || //bpl rel
cpu_op == 0x30 || //bmi rel
@@ -27,7 +36,7 @@ uint8 cpu_op;
cpu_op == 0xfc //jsr (addr,x)
) {
w_console->is_running(true);
status.cpu_stop_pos = (cpu->regs.pc.b << 16) | ((cpu->regs.pc.d + cpu->opcode_length()) & 0xffff);
status.cpu_stop_pos = (r_cpu->regs.pc.b << 16) | ((r_cpu->regs.pc.d + r_cpu->opcode_length()) & 0xffff);
} else {
status.cpu_ran = false;
run_status = RUNTOCPUSTEP;
@@ -48,10 +57,7 @@ void bSNES::run() {
switch(run_status) {
case RUN:
while(update_frame == false) {
SNES::run();
}
update_frame = false;
SNES::runtoframe();
return;
case STOP:
break;
@@ -68,8 +74,8 @@ void bSNES::run() {
break;
case RUNTOFRAME:
SNES::run();
if(update_frame == true) {
update_frame = false;
if(r_ppu->status.frame_executed == true) {
r_ppu->status.frame_executed = false;
set_status(STOP);
disassemble_apu_op();
disassemble_cpu_op();
@@ -89,7 +95,7 @@ void bSNES::run() {
break;
case RUNTOCPUPROCEED:
SNES::run();
if(cpu->in_opcode() == false && status.cpu_stop_pos == cpu->regs.pc.d) {
if(r_cpu->in_opcode() == false && status.cpu_stop_pos == r_cpu->regs.pc.d) {
set_status(STOP);
disassemble_cpu_op();
} else if(w_bp->hit() == true) {
@@ -114,40 +120,110 @@ void bSNES::run() {
}
void bSNES::video_run() {
dd_renderer->update();
if(r_ppu->status.frames_updated) {
char s[512], t[512];
r_ppu->status.frames_updated = false;
if((bool)config::gui.show_fps == true) {
sprintf(s, "%s : %d fps", BSNES_TITLE, r_ppu->status.frames_executed);
if(w_main->frameskip != 0) {
sprintf(t, " (%d frames)", r_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(r_ppu->renderer_enabled())dd_renderer->update();
r_ppu->enable_renderer(w_main->frameskip_pos == 0);
}
void bSNES::sound_run() {
ds_sound->run();
void bSNES::sound_run(uint32 data) {
ds_sound->run(data);
}
/***********************
*** Video functions ***
***********************/
uint16 *bSNES::video_lock(uint32 &pitch) {
return dd_renderer->lock(pitch);
}
void bSNES::video_unlock() {
dd_renderer->unlock();
}
/***********************
*** Input functions ***
***********************/
void bSNES::poll_input() {
void bSNES::clear_input() {
joypad1.up = joypad2.up =
joypad1.down = joypad2.down =
joypad1.left = joypad2.left =
joypad1.right = joypad2.right =
joypad1.a = joypad2.a =
joypad1.b = joypad2.b =
joypad1.x = joypad2.x =
joypad1.y = joypad2.y =
joypad1.l = joypad2.l =
joypad1.r = joypad2.r =
joypad1.select = joypad2.select =
joypad1.start = joypad2.start = 0;
}
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 +243,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 +266,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;
}
@@ -215,27 +311,27 @@ uint8 r = 0x00;
if(a >= 0x2000 && a <= 0x5fff) {
r = 0x00;
} else {
r = mem_bus->read(addr);
r = r_mem->read(addr);
}
} else {
r = mem_bus->read(addr);
r = r_mem->read(addr);
}
break;
case SPCRAM:
addr &= 0xffff;
r = apu->spcram_read(addr);
r = r_apu->spcram_read(addr);
break;
case VRAM:
addr &= 0xffff;
r = ppu->vram_read(addr);
r = r_ppu->vram_read(addr);
break;
case OAM:
addr &= 0x03ff;
r = ppu->oam_read(addr);
r = r_ppu->oam_read(addr);
break;
case CGRAM:
addr &= 0x01ff;
r = ppu->cgram_read(addr);
r = r_ppu->cgram_read(addr);
break;
}
debug_command = false;
@@ -247,38 +343,31 @@ void bSNES::write(uint8 type, uint32 addr, uint8 value) {
switch(type) {
case DRAM:
addr &= 0xffffff;
mem_bus->cart->write_protect(false);
mem_bus->write(addr, value);
mem_bus->cart->write_protect(true);
r_mem->cart->write_protect(false);
r_mem->write(addr, value);
r_mem->cart->write_protect(true);
break;
case SPCRAM:
addr &= 0xffff;
apu->spcram_write(addr, value);
r_apu->spcram_write(addr, value);
break;
case VRAM:
addr &= 0xffff;
ppu->vram_write(addr, value);
r_ppu->vram_write(addr, value);
break;
case OAM:
addr &= 0x03ff;
ppu->oam_write(addr, value);
r_ppu->oam_write(addr, value);
break;
case CGRAM:
addr &= 0x01ff;
ppu->cgram_write(addr, value);
r_ppu->cgram_write(addr, value);
break;
}
debug_command = false;
}
void bSNES::notify(uint32 message, uint32 param1, uint32 param2) {
//system messages
switch(message) {
case RENDER_FRAME:
update_frame = true;
break;
}
//debugging messages
if(is_debugger_enabled == false)return;
@@ -289,14 +378,14 @@ void bSNES::notify(uint32 message, uint32 param1, uint32 param2) {
status.cpu_ran = true;
status.cpu_trace_pos++;
//test next opcode for breakpoint
w_bp->test(message, cpu->regs.pc.d, 0);
w_bp->test(message, r_cpu->regs.pc.d, 0);
disassemble_cpu_op();
break;
case APU_EXEC_OPCODE_BEGIN:
break;
case APU_EXEC_OPCODE_END:
status.apu_ran = true;
w_bp->test(message, apu->regs.pc, 0);
w_bp->test(message, r_apu->regs.pc, 0);
disassemble_apu_op();
break;
case MEM_READ:
@@ -331,7 +420,7 @@ char t[512];
//don't disassemble opcodes that won't be printed to console/traced to log anyway
if(!w_console->can_write(Console::CPU_MESSAGE) && !w_console->tracing_enabled)return;
cpu->disassemble_opcode(t);
r_cpu->disassemble_opcode(t);
w_console->write(t, Console::CPU_MESSAGE);
}
@@ -341,7 +430,7 @@ char t[512];
if(!w_console->can_write(Console::APU_MESSAGE) && !w_console->tracing_enabled)return;
apu->disassemble_opcode(t);
r_apu->disassemble_opcode(t);
w_console->write(t, Console::APU_MESSAGE);
}
@@ -476,7 +565,6 @@ uint32 i, style;
bSNES::bSNES() {
run_status = STOP;
debug_command = false;
update_frame = false;
debugger_disable();
}
+10 -4
View File
@@ -18,8 +18,6 @@ bool is_debugger_activated;
bool debug_command;
uint32 run_status;
bool update_frame;
bJoypad joypad1, joypad2;
public:
@@ -35,15 +33,23 @@ enum {
RUNTOAPUSTEP
};
enum { DRAM = 0, SPCRAM = 1, VRAM = 2, OAM = 3, CGRAM = 4 };
void power();
void reset();
void run();
void video_run();
void sound_run();
void sound_run(uint32 data);
void set_status(uint32 new_status);
uint32 get_status();
//video functions
uint16 *video_lock(uint32 &pitch);
void video_unlock();
//input functions
void poll_input();
void clear_input();
void poll_input(uint8 type);
bool get_input_status(uint8 device, uint8 button);
//debugging functions
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+33 -13
View File
@@ -1,5 +1,10 @@
namespace config {
struct System {
static Setting regulate_speed;
} system;
Setting System::regulate_speed(&config_file, "system.regulate_speed", "Regulate speed to 60hz (NTSC) / 50hz (PAL)", true, Setting::TRUE_FALSE);
struct Video {
static Setting mode, use_vram, vblank;
} video;
@@ -20,21 +25,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);
};
+9 -73
View File
@@ -164,95 +164,31 @@ 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"
void DDRenderer::update16() {
HRESULT hr;
hr = lpddsb->Lock(0, &ddsd, DDLOCK_WAIT, 0);
if(hr != DD_OK)return;
set_source_window();
if (vi.width == 256 && vi.height == 224) {
update16_256x224();
} else if(vi.width == 512 && vi.height == 224) {
update16_512x224();
} else if(vi.width == 256 && vi.height == 448) {
update16_256x448();
} else if(vi.width == 512 && vi.height == 448) {
update16_512x448();
}
uint16 *DDRenderer::lock(uint32 &pitch) {
if(lpddsb->Lock(0, &ddsd, DDLOCK_WAIT, 0) != DD_OK)return 0;
pitch = ddsd.lPitch;
return (uint16*)ddsd.lpSurface;
}
void DDRenderer::unlock() {
lpddsb->Unlock(0);
}
//#include "dd_renderer32.cpp"
/*
void DDRenderer::update32() {
HRESULT hr;
hr = lpddsb->Lock(0, &ddsd, DDLOCK_WAIT, 0);
if(hr != DD_OK)return;
set_source_window();
if(ppu->output->hires == false) {
if(ppu->output->interlace == false) {
update32_256x224();
} else {
update32_256x448();
}
} else {
if(ppu->output->interlace == false) {
update32_512x224();
} else {
update32_512x448();
}
}
lpddsb->Unlock(0);
}
*/
void DDRenderer::update() {
snes->get_video_info(&vi);
switch(color_depth) {
case 15:
case 16:
update16();
break;
case 32:
//update32();
break;
}
set_source_window();
redraw();
}
+3 -10
View File
@@ -14,22 +14,15 @@ bool fullscreen;
int width, height; //used for fullscreen mode clipping only
uint8 color_depth;
SNES::video_info vi; //initialized each frame at start of update()
uint16 *lock(uint32 &pitch);
void unlock();
void set_window(HWND hwnd_handle);
void create_backbuffer();
void to_windowed();
void to_fullscreen(int _width, int _height);
void set_source_window();
void redraw();
inline void update16_256x224();
inline void update16_512x224();
inline void update16_256x448();
inline void update16_512x448();
void update16();
//inline void update32_256x224();
//inline void update32_512x224();
//inline void update32_256x448();
//inline void update32_512x448();
//void update32();
void update();
void destroy();
-79
View File
@@ -1,79 +0,0 @@
inline void DDRenderer::update16_256x224() {
uint16 *src, *dest;
uint32 pitch;
int x, y;
dest = (uint16*)ddsd.lpSurface;
src = (uint16*)vi.data;
#ifdef USE_X86_ASM
pitch = (ddsd.lPitch) - 512;
__asm {
mov edi,dest
mov esi,src
mov edx,224
ly:
mov ecx,32
lx:
movsd
movsd
movsd
movsd
loopnz lx
add edi,pitch
dec edx
jnz ly
}
#else
pitch = (ddsd.lPitch >> 1);
for(y=0;y<224;y++) {
memcpy(dest, src, 512);
dest += pitch;
src += 256;
}
#endif
}
inline void DDRenderer::update16_512x224() {
uint16 *src, *dest;
uint32 pitch;
int x, y;
pitch = (ddsd.lPitch >> 1);
dest = (uint16*)ddsd.lpSurface;
src = (uint16*)vi.data;
for(y=0;y<224;y++) {
memcpy(dest, src, 1024);
dest += pitch;
src += 512;
}
}
inline void DDRenderer::update16_256x448() {
uint16 *src, *dest;
uint32 pitch;
int x, y;
pitch = (ddsd.lPitch >> 1);
dest = (uint16*)ddsd.lpSurface;
src = (uint16*)vi.data;
for(y=0;y<448;y++) {
memcpy(dest, src, 512);
dest += pitch;
src += 256;
}
}
inline void DDRenderer::update16_512x448() {
uint16 *src, *dest;
uint32 pitch;
int x, y;
pitch = (ddsd.lPitch >> 1);
dest = (uint16*)ddsd.lpSurface;
src = (uint16*)vi.data;
for(y=0;y<448;y++) {
memcpy(dest, src, 1024);
dest += pitch;
src += 512;
}
}
-123
View File
@@ -1,123 +0,0 @@
inline void DDRenderer::update32_256x224() {
uint16 *src;
uint32 *dest;
uint32 pitch;
int x, y;
src = (uint16*)ppu->output->buffer + (1 << 10);
dest = (uint32*)ddsd.lpSurface;
pitch = (ddsd.lPitch >> 2) - 256;
int overscan_adjust = 0;
if(cpu->overscan() == true) {
src += 7 << 10;
overscan_adjust = 7;
}
for(y=1+overscan_adjust;y<224+overscan_adjust;y++) {
x = 256;
while(x--) {
*dest++ = color_lookup_table[*src];
src += 2;
}
dest += pitch;
src += 512;
}
}
inline void DDRenderer::update32_256x448() {
uint16 *src;
uint32 *dest;
uint32 pitch;
int x, y;
src = (uint16*)ppu->output->buffer + (1 << 10);
dest = (uint32*)ddsd.lpSurface;
pitch = (ddsd.lPitch >> 2) - 256;
int overscan_adjust = 0;
if(cpu->overscan() == true) {
src += 7 << 10;
overscan_adjust = 14;
}
for(y=2+overscan_adjust;y<448+overscan_adjust;y++) {
x = 256;
while(x--) {
*dest++ = color_lookup_table[*src];
src += 2;
}
dest += pitch;
if(ppu->output->line[y >> 1].interlace == false) {
src += (y & 1)?512:-512;
}
}
}
inline void DDRenderer::update32_512x224() {
uint16 *src;
uint32 *dest;
uint32 pitch;
int x, y;
src = (uint16*)ppu->output->buffer + (1 << 10);
dest = (uint32*)ddsd.lpSurface;
pitch = (ddsd.lPitch >> 2) - 512;
int overscan_adjust = 0;
if(cpu->overscan() == true) {
src += 7 << 10;
overscan_adjust = 7;
}
for(y=1+overscan_adjust;y<224+overscan_adjust;y++) {
if(ppu->output->line[y].hires == true) {
x = 512;
while(x--) {
*dest++ = color_lookup_table[*src++];
}
} else {
x = 256;
while(x--) {
*dest++ = color_lookup_table[*src];
*dest++ = color_lookup_table[*src];
src += 2;
}
}
dest += pitch;
src += 512;
}
}
inline void DDRenderer::update32_512x448() {
uint16 *src;
uint32 *dest;
uint32 pitch;
int x, y;
src = (uint16*)ppu->output->buffer + (1 << 10);
dest = (uint32*)ddsd.lpSurface;
pitch = (ddsd.lPitch >> 2) - 512;
int overscan_adjust = 0;
if(cpu->overscan() == true) {
src += 7 << 10;
overscan_adjust = 14;
}
for(y=2+overscan_adjust;y<448+overscan_adjust;y++) {
if(ppu->output->line[y >> 1].hires == true) {
x = 512;
while(x--) {
*dest++ = color_lookup_table[*src++];
}
} else {
x = 256;
while(x--) {
*dest++ = color_lookup_table[*src];
*dest++ = color_lookup_table[*src];
src += 2;
}
}
dest += pitch;
if(ppu->output->line[y >> 1].interlace == false) {
src += (y & 1)?512:-512;
}
}
}
+89 -50
View File
@@ -1,77 +1,116 @@
DSSound::DSSound() {
data.buffer = 0;
data.lpos = data.lsample = data.lbuffer = 0;
}
void DSSound::run(uint32 sample) {
data.buffer[data.buffer_pos++] = sample;
void DSSound::run() {
if(snes->get_playback_buffer_pos() != 0)return;
if(data.buffer_pos >= data.samples_per_frame) {
uint32 pos, size;
void *buffer;
if((bool)config::system.regulate_speed == true) {
do {
dsb_b->GetCurrentPosition(&pos, 0);
data.read_buffer = pos / data.buffer_size;
} while(data.read_buffer == data.prev_buffer);
}
uint32 pos, status;
data.prev_buffer = data.read_buffer;
data.read_buffer++;
data.read_buffer &= 7;
//dsb_b[0]->SetFrequency(22050);
pos = (data.read_buffer + 1) & 7;
//do {
// dsb_b[0]->GetStatus(&status);
//} while(status & DSBSTATUS_PLAYING);
if(dsb_b->Lock(pos * data.buffer_size,
data.buffer_size, &buffer, &size, 0, 0, 0) == DS_OK) {
memcpy(buffer, data.buffer, data.buffer_size);
dsb_b->Unlock(buffer, size, 0, 0);
}
dsb_b[0]->Lock(0, DSP_BUFFER_SIZE * 4, &dslb, &dslbs, 0, 0, 0);
memcpy(dslb, snes->get_playback_buffer(), DSP_BUFFER_SIZE * 4);
dsb_b[0]->Unlock(dslb, dslbs, 0, 0);
dsb_b[0]->SetCurrentPosition(0);
//has the buffer stopped (possibly due to running too fast)?
dsb_b[0]->GetStatus(&status);
if(!(status & DSBSTATUS_PLAYING)) {
dsb_b[0]->Play(0, 0, 0);
data.buffer_pos = 0;
}
}
void DSSound::clear() {
data.read_buffer = 0;
data.prev_buffer = 0;
data.buffer_pos = 0;
memset(data.buffer, 0, 2048 * 4);
if(!dsb_b)return;
dsb_b->Stop();
dsb_b->SetCurrentPosition(0);
uint32 size;
void *buffer;
dsb_b->Lock(0, data.buffer_size * 8, &buffer, &size, 0, 0, 0);
memset(buffer, 0, data.buffer_size * 8);
dsb_b->Unlock(buffer, size, 0, 0);
dsb_b->Play(0, 0, DSBPLAY_LOOPING);
}
void DSSound::init() {
clear();
term();
data.read_buffer = 0;
data.prev_buffer = 0;
data.buffer_pos = 0;
data.samples_per_frame = DSP_FREQ / ((snes->region() == SNES::NTSC) ? 60 : 50);
data.buffer_size = data.samples_per_frame * 4;
DirectSoundCreate(0, &ds, 0);
ds->SetCooperativeLevel(w_main->hwnd, DSSCL_PRIORITY);
memset(&dsbd, 0, sizeof(dsbd));
dsbd.dwSize = sizeof(dsbd);
dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
dsbd.dwSize = sizeof(dsbd);
dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
dsbd.dwBufferBytes = 0;
dsbd.lpwfxFormat = 0;
dsbd.lpwfxFormat = 0;
ds->CreateSoundBuffer(&dsbd, &dsb_p, 0);
memset(&wfx, 0, sizeof(wfx));
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = 2;
wfx.nSamplesPerSec = 32000;
wfx.wBitsPerSample = 16;
wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels;
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = 2;
wfx.nSamplesPerSec = DSP_FREQ;
wfx.wBitsPerSample = 16;
wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels;
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
dsb_p->SetFormat(&wfx);
sample_size = (16 / 8) * 2;
buffer_size = DSP_BUFFER_SIZE * sample_size;
buffer_pos = 0;
dsb_b = new LPDIRECTSOUNDBUFFER[1];
memset(&dsbd, 0, sizeof(dsbd));
dsbd.dwSize = sizeof(dsbd);
dsbd.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_GLOBALFOCUS;
dsbd.dwBufferBytes = buffer_size;
dsbd.dwSize = sizeof(dsbd);
dsbd.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLFREQUENCY |
DSBCAPS_GLOBALFOCUS | DSBCAPS_LOCSOFTWARE;
dsbd.dwBufferBytes = data.buffer_size * 8;
dsbd.guid3DAlgorithm = GUID_NULL;
dsbd.lpwfxFormat = &wfx;
ds->CreateSoundBuffer(&dsbd, &dsb_b[0], 0);
ds->CreateSoundBuffer(&dsbd, &dsb_b[1], 0);
dsb_b[0]->SetFrequency(32000);
dsb_b[1]->SetFrequency(32000);
dsbd.lpwfxFormat = &wfx;
ds->CreateSoundBuffer(&dsbd, &dsb_b, 0);
dsb_b->SetFrequency(DSP_FREQ);
dsb_b->SetCurrentPosition(0);
dsb_b[0]->Lock(0, buffer_size, &dslb, &dslbs, 0, 0, 0);
memset(dslb, 0, buffer_size);
dsb_b[0]->Unlock(dslb, dslbs, 0, 0);
uint32 size;
void *buffer;
dsb_b->Lock(0, data.buffer_size * 8, &buffer, &size, 0, 0, 0);
memset(buffer, 0, data.buffer_size * 8);
dsb_b->Unlock(buffer, size, 0, 0);
dsb_b[1]->Lock(0, buffer_size, &dslb, &dslbs, 0, 0, 0);
memset(dslb, 0, buffer_size);
dsb_b[1]->Unlock(dslb, dslbs, 0, 0);
data.read_buffer = 0;
dsb_b->Play(0, 0, DSBPLAY_LOOPING);
}
dsb_b[0]->Play(0, 0, 0);
void DSSound::term() {
if(dsb_b) {
dsb_b->Stop();
dsb_b->Release();
dsb_b = 0;
}
buffer_pos = 0;
if(dsb_p) {
dsb_p->Release();
dsb_p = 0;
}
if(ds) {
ds->Release();
ds = 0;
}
}
+11 -13
View File
@@ -1,30 +1,28 @@
#include <dsound.h>
#define DSP_FREQ 32000
class DSSound {
public:
LPDIRECTSOUND ds;
LPDIRECTSOUNDBUFFER dsb_p, *dsb_b;
LPDIRECTSOUNDBUFFER dsb_p, dsb_b;
DSBUFFERDESC dsbd;
WAVEFORMATEX wfx;
uint32 sample_size;
uint32 buffer_size;
uint32 buffer_pos;
void *dslb;
uint32 dslbs;
public:
void run();
void run(uint32 sample);
void clear();
void init();
DSSound();
void term();
};
struct {
uint32 *buffer;
uint32 lpos;
uint32 lsample;
bool lbuffer;
uint32 buffer[2048];
uint8 read_buffer, prev_buffer;
uint32 buffer_pos, buffer_size;
uint32 samples_per_frame;
} data;
DSSound *ds_sound;
+6 -6
View File
@@ -27,14 +27,14 @@ FileReader *rf = new FileReader();
alert("Error loading image file [%s]!", rom_fn);
return false;
}
mem_bus->load_cart(static_cast<Reader*>(rf));
r_mem->load_cart(static_cast<Reader*>(rf));
rf->close();
CartInfo ci;
mem_bus->get_cartinfo(&ci);
r_mem->get_cartinfo(&ci);
if(ci.sram_size != 0) {
rf->open(sram_fn);
mem_bus->load_sram(static_cast<Reader*>(rf));
r_mem->load_sram(static_cast<Reader*>(rf));
rf->close();
}
@@ -50,11 +50,11 @@ void ROMImage::unload() {
FileWriter *wf;
CartInfo ci;
mem_bus->get_cartinfo(&ci);
r_mem->get_cartinfo(&ci);
if(ci.sram_size != 0) {
wf = new FileWriter();
wf->open(sram_fn);
mem_bus->save_sram(static_cast<Writer*>(wf));
r_mem->save_sram(static_cast<Writer*>(wf));
wf->close();
delete(wf);
}
@@ -62,7 +62,7 @@ CartInfo ci;
file_loaded = false;
bsnes->debugger_deactivate();
mem_bus->unload_cart();
r_mem->unload_cart();
}
void ROMImage::select(char *fn) {
-43
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;
}
-1
View File
@@ -54,7 +54,6 @@ void init_ui1() {
SetFocus(w_main->hwnd);
dd_renderer->set_window(w_main->hwnd);
dd_renderer->to_windowed();
ds_sound->init();
w_main->show_menu();
w_main->set_video_mode(config::video.mode);
+6 -3
View File
@@ -14,6 +14,7 @@ enum {
MENU_FILE_RESET,
MENU_FILE_POWER,
MENU_FILE_EXIT,
MENU_SETTINGS_REGULATE_SPEED,
MENU_SETTINGS_FRAMESKIP_OFF,
MENU_SETTINGS_FRAMESKIP_1,
MENU_SETTINGS_FRAMESKIP_2,
@@ -39,6 +40,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 +166,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();
@@ -250,13 +252,14 @@ bool auto_update; //update memory window whenever visible value is written to
uint8 read_byte(uint32 addr);
void write_byte(uint32 addr, uint8 value);
void refresh(uint32 type = null, uint32 addr = 0);
void export(uint32 type);
void export_data(uint32 type);
}*w_memory = 0;
class InputConfig : public Window {
public:
enum {
JOYPAD1 = 0
JOYPAD1 = 0,
JOYPAD2
};
enum {
ID_COMMAND = 100
+25 -23
View File
@@ -29,7 +29,7 @@ HDC hdc;
break;
case CONSOLE_CPUPROCEED:
if(bsnes->get_status() == bSNES::STOP) {
if(cpu->in_opcode() == true) {
if(r_cpu->in_opcode() == true) {
dprintf("* CPU within opcode, proceed aborted");
} else {
bsnes->set_status(bSNES::RUNTOCPUPROCEED);
@@ -38,10 +38,10 @@ HDC hdc;
break;
case CONSOLE_CPUSKIP:
if(bsnes->get_status() == bSNES::STOP) {
if(cpu->in_opcode() == true) {
if(r_cpu->in_opcode() == true) {
dprintf("* CPU within opcode, skip aborted");
} else {
cpu->regs.pc.w += cpu->opcode_length();
r_cpu->regs.pc.w += r_cpu->opcode_length();
bsnes->disassemble_cpu_op();
}
}
@@ -57,15 +57,15 @@ HDC hdc;
break;
case CONSOLE_CPUDISABLE:
if(bsnes->get_status() == bSNES::STOP) {
if(cpu->in_opcode() == true) {
if(r_cpu->in_opcode() == true) {
dprintf("* CPU within opcode, disable aborted");
} else {
addr = cpu->regs.pc.d;
len = cpu->opcode_length();
addr = r_cpu->regs.pc.d;
len = r_cpu->opcode_length();
for(i=0;i<len;i++) {
bsnes->write(bSNES::DRAM, (addr & 0xff0000) | ((addr + i) & 0xffff), 0xea);
}
//cpu->regs.pc.w += len;
//r_cpu->regs.pc.w += len;
bsnes->disassemble_cpu_op();
}
}
@@ -123,23 +123,23 @@ HDC hdc;
value = strhex(t);
pos = SendDlgItemMessage(hwnd, CONSOLE_CFGREGTYPE, CB_GETCURSEL, 0, 0);
if(pos == 0) { //Set CPU register
if(cpu->in_opcode() == true) {
if(r_cpu->in_opcode() == true) {
dprintf("* CPU within opcode, register set aborted");
} else {
pos = SendDlgItemMessage(hwnd, CONSOLE_CFGREGNUM, CB_GETCURSEL, 0, 0);
switch(pos) {
case 0:cpu->regs.a.w = value;break;
case 1:cpu->regs.x.w = value;break;
case 2:cpu->regs.y.w = value;break;
case 3:cpu->regs.s.w = value;break;
case 4:cpu->regs.d.w = value;break;
case 5:cpu->regs.db = value;break;
case 6:cpu->regs.p = value;break;
case 7:cpu->regs.e = value;break;
case 8:cpu->regs.pc.d = value;break;
case 0:r_cpu->regs.a.w = value;break;
case 1:r_cpu->regs.x.w = value;break;
case 2:r_cpu->regs.y.w = value;break;
case 3:r_cpu->regs.s.w = value;break;
case 4:r_cpu->regs.d.w = value;break;
case 5:r_cpu->regs.db = value;break;
case 6:r_cpu->regs.p = value;break;
case 7:r_cpu->regs.e = value;break;
case 8:r_cpu->regs.pc.d = value;break;
}
//these bits can never be clear in emulation mode
if(cpu->regs.e)cpu->regs.p |= 0x30;
if(r_cpu->regs.e)r_cpu->regs.p |= 0x30;
bsnes->disassemble_cpu_op();
}
} else { //Set APU register
@@ -229,10 +229,12 @@ int sl = strlen(s);
memset(t + sl, 0x20, 80 - sl);
}
t[80] = 0;
//only allow ascii characters. other characters will force the
//font to change to one that supports non-ascii characters,
//which will break the line highlighting and alignment of text
for(int i=0;i<80;i++) {
int i;
for(i=0;i<80;i++) {
if(t[i] & 0x80)t[i] = '?';
}
@@ -281,13 +283,13 @@ static uint8 linecol[4] = { 1, 2, 3 };
strcpy(s, "");
sprintf(t, "V:%3d H:%3d HC:%4d I:%d IF:%d O:%d",
cpu->vcounter(), cpu->hcounter(), cpu->hcycles(),
cpu->interlace(), cpu->interlace_field(), cpu->overscan());
r_cpu->vcounter(), r_cpu->hcounter(), r_cpu->hcycles(),
r_cpu->interlace(), r_cpu->interlace_field(), r_cpu->overscan());
strcat(s, t);
if(1) { //config::apu.enabled
sprintf(t, " -- CPU[$%0.2x,$%0.2x,$%0.2x,$%0.2x]<>APU[$%0.2x,$%0.2x,$%0.2x,$%0.2x]",
cpu->port_read(0), cpu->port_read(1), cpu->port_read(2), cpu->port_read(3),
apu->port_read(0), apu->port_read(1), apu->port_read(2), apu->port_read(3)
r_cpu->port_read(0), r_cpu->port_read(1), r_cpu->port_read(2), r_cpu->port_read(3),
r_apu->port_read(0), r_apu->port_read(1), r_apu->port_read(2), r_apu->port_read(3)
);
strcat(s, t);
}
+29 -1
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();
}
+26 -9
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() {
@@ -135,6 +135,16 @@ long __stdcall wndproc_main(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
time_t timeout;
int i;
switch(msg) {
case WM_ENTERMENULOOP:
ds_sound->clear();
break;
case WM_EXITMENULOOP:
timeout = time(0);
while(difftime(time(0), timeout) < 3) {
if(!KeyDown(VK_RETURN))break;
}
bsnes->clear_input();
break;
case WM_KEYDOWN:
if(wparam == VK_ESCAPE) {
if(GetMenu(w_main->hwnd) == NULL) {
@@ -146,13 +156,6 @@ int i;
}
break;
case WM_COMMAND:
//below code fails because it is triggered after snes->poll_input()...
//unsure how to fix this...
// timeout = time(NULL);
// while(difftime(time(NULL), timeout) < 5) {
// if(!KeyDown(VK_RETURN))break;
// }
switch(LOWORD(wparam)) {
case MENU_FILE_LOAD:
w_main->menu_load();
@@ -175,6 +178,11 @@ int i;
case MENU_FILE_EXIT:
PostQuitMessage(0);
break;
case MENU_SETTINGS_REGULATE_SPEED:
config::system.regulate_speed.toggle();
CheckMenuItem(w_main->hmenu, MENU_SETTINGS_REGULATE_SPEED,
(config::system.regulate_speed)?MF_CHECKED:MF_UNCHECKED);
break;
case MENU_SETTINGS_FRAMESKIP_OFF:
case MENU_SETTINGS_FRAMESKIP_1:
case MENU_SETTINGS_FRAMESKIP_2:
@@ -239,6 +247,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();
@@ -311,6 +322,7 @@ HMENU hsubmenu, hbranchmenu;
AppendMenu(hmenu, MF_STRING | MF_POPUP, (unsigned int)hsubmenu, "&File");
hsubmenu = CreatePopupMenu();
AppendMenu(hsubmenu, MF_STRING, MENU_SETTINGS_REGULATE_SPEED, "&Regulate Speed");
hbranchmenu = CreatePopupMenu();
AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_OFF, "Off");
@@ -350,10 +362,14 @@ 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");
#ifdef DEBUGGER
AppendMenu(hsubmenu, MF_SEPARATOR, 0, "");
AppendMenu(hsubmenu, MF_STRING, MENU_SETTINGS_DEBUGGER, "&Debugger");
#endif
AppendMenu(hmenu, MF_STRING | MF_POPUP, (unsigned int)hsubmenu, "&Settings");
hsubmenu = CreatePopupMenu();
@@ -363,6 +379,7 @@ HMENU hsubmenu, hbranchmenu;
AppendMenu(hsubmenu, MF_STRING, MENU_MISC_ABOUT, "&About...");
AppendMenu(hmenu, MF_STRING | MF_POPUP, (unsigned int)hsubmenu, "&Misc");
CheckMenuItem(hmenu, MENU_SETTINGS_REGULATE_SPEED, (config::system.regulate_speed)?MF_CHECKED:MF_UNCHECKED);
CheckMenuItem(hmenu, MENU_SETTINGS_USEVRAM, (config::video.use_vram)?MF_CHECKED:MF_UNCHECKED);
CheckMenuItem(hmenu, MENU_SETTINGS_VBLANK, (config::video.vblank)?MF_CHECKED:MF_UNCHECKED);
CheckMenuItem(hmenu, MENU_SETTINGS_COLORADJUST_COLORCURVE, (config::snes.video_color_curve)?MF_CHECKED:MF_UNCHECKED);
+8 -8
View File
@@ -154,7 +154,7 @@ HDC hdc;
break;
case MEMORYEDITOR_FEXPORT:
pos = SendDlgItemMessage(hwnd, MEMORYEDITOR_FSOURCE, CB_GETCURSEL, 0, 0);
w_memory->export(pos);
w_memory->export_data(pos);
break;
case MEMORYEDITOR_AUTOUPDATE:
if(w_memory->auto_update == false) {
@@ -186,7 +186,7 @@ HDC hdc;
return DefWindowProc(hwnd, msg, wparam, lparam);
}
void MemoryEditor::export(uint32 type) {
void MemoryEditor::export_data(uint32 type) {
FILE *fp;
int i, x;
if(type == 0) { //DRAM
@@ -233,12 +233,12 @@ int i, x;
}
fclose(fp);
} else if(type == 6) { //All
export(0);
export(1);
export(2);
export(3);
export(4);
export(5);
export_data(0);
export_data(1);
export_data(2);
export_data(3);
export_data(4);
export_data(5);
}
}
+27 -35
View File
@@ -1,23 +1,15 @@
#define INTERFACE_MAIN
//requires visual c++
#define USE_X86_ASM
#include "winmain.h"
#include "../base.h"
#include "config.cpp"
#define DSP_BUFFER_SIZE 8000
#include "bsnes.h"
#include "ui.h"
#include "dd_renderer.h"
#include "ds_sound.h"
#include "timer.cpp"
fpstimer *fps_timer;
#include "lib.cpp"
#include "rom.cpp"
@@ -25,45 +17,47 @@ fpstimer *fps_timer;
#include "ui.cpp"
void init_snes() {
mem_bus = new bMemBus();
cpu = new bCPU();
cpu->cpu_version = 1;
apu = new bAPU();
dsp = new bDSP();
ppu = new bPPU();
snes = new bSNES();
bsnes = static_cast<bSNES*>(snes);
#ifdef POLYMORPHISM
deref(mem) = new bMemBus();
deref(cpu) = new bCPU();
deref(apu) = new bAPU();
deref(dsp) = new bDSP();
deref(ppu) = new bPPU();
#endif
snes = new bSNES();
bsnes = static_cast<bSNES*>(snes);
snes->init();
snes->set_playback_buffer_size(DSP_BUFFER_SIZE);
}
void term_snes() {
snes->term();
#ifdef POLYMORPHISM
//static casting is neccesary to call derived class deconstructor...
if(mem_bus) {
delete(static_cast<bMemBus*>(mem_bus));
mem_bus = 0;
if(deref(mem)) {
delete static_cast<bMemBus*>(deref(mem));
deref(mem) = 0;
}
if(cpu) {
delete(static_cast<bCPU*>(cpu));
cpu = 0;
if(deref(cpu)) {
delete static_cast<bCPU*>(deref(cpu));
deref(cpu) = 0;
}
if(apu) {
delete(static_cast<bAPU*>(apu));
apu = 0;
if(deref(apu)) {
delete static_cast<bAPU*>(deref(apu));
deref(apu) = 0;
}
if(dsp) {
delete(static_cast<bDSP*>(dsp));
dsp = 0;
if(deref(dsp)) {
delete static_cast<bDSP*>(deref(dsp));
deref(dsp) = 0;
}
if(ppu) {
delete(static_cast<bPPU*>(ppu));
ppu = 0;
if(deref(ppu)) {
delete static_cast<bPPU*>(deref(ppu));
deref(ppu) = 0;
}
#endif
if(snes) {
delete(static_cast<bSNES*>(snes));
delete static_cast<bSNES*>(snes);
snes = 0;
}
}
@@ -82,8 +76,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();