mirror of
https://github.com/ares-emulator/ares.git
synced 2026-11-10 09:21:41 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6c5f4669c |
No files matched your search
@@ -35,7 +35,7 @@ video.vblank = false
|
||||
|
||||
# Show framerate in window title
|
||||
# (default = true)
|
||||
gui.show_fps = false
|
||||
gui.show_fps = true
|
||||
|
||||
# Joypad1 up
|
||||
# (default = 0x26)
|
||||
@@ -85,3 +85,51 @@ input.joypad1.select = 0x10
|
||||
# (default = 0xd)
|
||||
input.joypad1.start = 0xd
|
||||
|
||||
# Joypad2 up
|
||||
# (default = 0x54)
|
||||
input.joypad2.up = 0x54
|
||||
|
||||
# Joypad2 down
|
||||
# (default = 0x47)
|
||||
input.joypad2.down = 0x47
|
||||
|
||||
# Joypad2 left
|
||||
# (default = 0x46)
|
||||
input.joypad2.left = 0x46
|
||||
|
||||
# Joypad2 right
|
||||
# (default = 0x48)
|
||||
input.joypad2.right = 0x48
|
||||
|
||||
# Joypad2 A
|
||||
# (default = 0x4b)
|
||||
input.joypad2.a = 0x4b
|
||||
|
||||
# Joypad2 B
|
||||
# (default = 0x4a)
|
||||
input.joypad2.b = 0x4a
|
||||
|
||||
# Joypad2 X
|
||||
# (default = 0x49)
|
||||
input.joypad2.x = 0x49
|
||||
|
||||
# Joypad2 Y
|
||||
# (default = 0x55)
|
||||
input.joypad2.y = 0x55
|
||||
|
||||
# Joypad2 L
|
||||
# (default = 0x4f)
|
||||
input.joypad2.l = 0x4f
|
||||
|
||||
# Joypad2 R
|
||||
# (default = 0x4c)
|
||||
input.joypad2.r = 0x4c
|
||||
|
||||
# Joypad2 select
|
||||
# (default = 0x5b)
|
||||
input.joypad2.select = 0x5b
|
||||
|
||||
# Joypad2 start
|
||||
# (default = 0x5d)
|
||||
input.joypad2.start = 0x5d
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
+18
@@ -1,9 +1,27 @@
|
||||
//this should be declared in the port-specific makefiles
|
||||
//#define ARCH_LSB
|
||||
//#define ARCH_MSB
|
||||
|
||||
#ifndef ARCH_LSB
|
||||
#ifndef ARCH_MSB
|
||||
#define ARCH_LSB
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <time.h>
|
||||
#include "lib/libbase.h"
|
||||
#include "lib/libvector.h"
|
||||
#include "lib/libstring.h"
|
||||
#include "lib/libconfig.h"
|
||||
|
||||
inline uint16 read16(uint8 *addr, uint pos) {
|
||||
#ifdef ARCH_LSB
|
||||
return *((uint16*)(addr + pos));
|
||||
#else
|
||||
return (addr[pos]) | (addr[pos + 1] << 8);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define _WIN32_
|
||||
#undef _UNIX_
|
||||
|
||||
+60
-39
@@ -18,54 +18,72 @@
|
||||
uint8 bCPU::pio_status() { return status.pio; }
|
||||
|
||||
void bCPU::run() {
|
||||
switch(status.cpu_state) {
|
||||
case CPUSTATE_DMA:
|
||||
dma_run();
|
||||
break;
|
||||
case CPUSTATE_WAI:
|
||||
case CPUSTATE_RUN:
|
||||
if(status.cycle_pos == 0) {
|
||||
//interrupts only trigger on opcode edges
|
||||
if(time.nmi_pending == true) {
|
||||
time.nmi_pending = false;
|
||||
irq(0xffea);
|
||||
break;
|
||||
}
|
||||
if(time.irq_pending == true) {
|
||||
time.irq_pending = false;
|
||||
irq(0xffee);
|
||||
break;
|
||||
}
|
||||
}
|
||||
//fallthrough
|
||||
case CPUSTATE_STP:
|
||||
exec_cycle();
|
||||
break;
|
||||
if(run_state.hdma) {
|
||||
exec_hdma();
|
||||
return;
|
||||
}
|
||||
|
||||
cycle_edge();
|
||||
if(run_state.dma) {
|
||||
exec_dma();
|
||||
return;
|
||||
}
|
||||
|
||||
if(run_state.irq) {
|
||||
irq_run();
|
||||
return;
|
||||
}
|
||||
|
||||
if(run_state.stp) {
|
||||
exec_cycle();
|
||||
return;
|
||||
}
|
||||
|
||||
if(status.cycle_pos == 0) {
|
||||
//interrupts only trigger on opcode edges
|
||||
if(time.nmi_pending == true) {
|
||||
time.nmi_pending = false;
|
||||
aa.w = 0xffea;
|
||||
run_state.irq = true;
|
||||
return;
|
||||
}
|
||||
if(time.irq_pending == true) {
|
||||
time.irq_pending = false;
|
||||
aa.w = 0xffee;
|
||||
run_state.irq = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
exec_cycle();
|
||||
}
|
||||
|
||||
void bCPU::scanline() {
|
||||
status.hdma_triggered = false;
|
||||
time.hdma_triggered = false;
|
||||
|
||||
if(vcounter() == 225 && status.auto_joypad_poll == true) {
|
||||
snes->poll_input();
|
||||
snes->poll_input(SNES::DEV_JOYPAD1);
|
||||
snes->poll_input(SNES::DEV_JOYPAD2);
|
||||
//When the SNES auto-polls the joypads, it writes 1, then 0 to
|
||||
//$4016, then reads from each 16 times to get the joypad state
|
||||
//information. As a result, the joypad read positions are set
|
||||
//to 16 after such a poll. Position 16 is the controller
|
||||
//connected status bit.
|
||||
status.joypad1_read_pos = 16;
|
||||
status.joypad2_read_pos = 16;
|
||||
}
|
||||
}
|
||||
|
||||
void bCPU::frame() {
|
||||
hdma_initialize();
|
||||
|
||||
time.nmi_read = 1;
|
||||
time.nmi_line = 1;
|
||||
time.nmi_transition = 0;
|
||||
|
||||
if(cpu_version == 2) {
|
||||
time.hdmainit_trigger_pos = 12 + dma_counter();
|
||||
} else {
|
||||
time.hdmainit_trigger_pos = 12 + 8 - dma_counter();
|
||||
}
|
||||
time.hdmainit_triggered = false;
|
||||
}
|
||||
|
||||
void bCPU::power() {
|
||||
@@ -94,21 +112,27 @@ void bCPU::reset() {
|
||||
mmio_reset();
|
||||
dma_reset();
|
||||
|
||||
status.cpu_state = CPUSTATE_RUN;
|
||||
status.dma_state = DMASTATE_STOP;
|
||||
run_state.hdma = false;
|
||||
run_state.dma = false;
|
||||
run_state.irq = false;
|
||||
run_state.wai = false;
|
||||
run_state.stp = false;
|
||||
|
||||
status.cycle_pos = 0;
|
||||
status.cycle_count = 0;
|
||||
status.cycles_executed = 0;
|
||||
|
||||
status.hdma_triggered = false;
|
||||
|
||||
apu_port[0] = 0x00;
|
||||
apu_port[1] = 0x00;
|
||||
apu_port[2] = 0x00;
|
||||
apu_port[3] = 0x00;
|
||||
|
||||
frame();
|
||||
|
||||
//initial latch values for $213c/$213d
|
||||
//[x]0035 : [y]0000 (53.0 -> 212) [lda $2137]
|
||||
//[x]0038 : [y]0000 (56.5 -> 226) [nop : lda $2137]
|
||||
add_cycles(186);
|
||||
}
|
||||
|
||||
uint8 bCPU::port_read(uint8 port) {
|
||||
@@ -141,20 +165,18 @@ void bCPU::cpu_c6(uint16 addr) {
|
||||
* cpu_io is an I/O cycle, and always 6 clock cycles long.
|
||||
* mem_read / mem_write indicate memory access bus cycle,
|
||||
* they are either 6, 8, or 12 bus cycles long, depending
|
||||
* both on location and the $420d.1 FastROM enable bit.
|
||||
* both on location and the $420d.d0 FastROM enable bit.
|
||||
*/
|
||||
|
||||
void bCPU::cpu_io() {
|
||||
if(status.is_last_cycle)last_cycle_exec();
|
||||
|
||||
status.cycle_count = 6;
|
||||
pre_exec_cycle();
|
||||
add_cycles(6);
|
||||
}
|
||||
|
||||
uint8 bCPU::mem_read(uint32 addr) {
|
||||
if(status.is_last_cycle)last_cycle_exec();
|
||||
|
||||
status.cycle_count = mem_bus->speed(addr);
|
||||
pre_exec_cycle();
|
||||
add_cycles(status.cycle_count - 4);
|
||||
regs.mdr = mem_bus->read(addr);
|
||||
add_cycles(4);
|
||||
@@ -162,9 +184,8 @@ uint8 bCPU::mem_read(uint32 addr) {
|
||||
}
|
||||
|
||||
void bCPU::mem_write(uint32 addr, uint8 value) {
|
||||
if(status.is_last_cycle)last_cycle_exec();
|
||||
|
||||
status.cycle_count = mem_bus->speed(addr);
|
||||
pre_exec_cycle();
|
||||
add_cycles(status.cycle_count);
|
||||
mem_bus->write(addr, value);
|
||||
}
|
||||
|
||||
+55
-29
@@ -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
|
||||
|
||||
+120
-90
@@ -1,7 +1,22 @@
|
||||
void bCPU::dma_add_cycles(uint32 cycles) {
|
||||
status.dma_cycle_count += cycles;
|
||||
}
|
||||
|
||||
void bCPU::hdma_add_cycles(uint32 cycles) {
|
||||
if(run_state.dma) {
|
||||
status.dma_cycle_count += cycles;
|
||||
}
|
||||
status.hdma_cycle_count += cycles;
|
||||
}
|
||||
|
||||
uint32 bCPU::dma_addr(uint8 i) {
|
||||
uint32 r;
|
||||
r = channel[i].srcaddr;
|
||||
r |= (channel[i].srcbank << 16);
|
||||
r = (channel[i].srcbank << 16) | (channel[i].srcaddr);
|
||||
|
||||
if(channel[i].fixedxfer == false) {
|
||||
channel[i].srcaddr += channel[i].incmode;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -15,10 +30,6 @@ uint8 x;
|
||||
|
||||
mem_bus->write(0x2100 | ((channel[i].destaddr + index) & 0xff), x);
|
||||
|
||||
if(channel[i].fixedxfer == false) {
|
||||
channel[i].srcaddr += channel[i].incmode;
|
||||
}
|
||||
|
||||
add_cycles(8);
|
||||
channel[i].xfersize--;
|
||||
}
|
||||
@@ -28,10 +39,6 @@ uint8 x;
|
||||
x = mem_bus->read(0x2100 | ((channel[i].destaddr + index) & 0xff));
|
||||
mem_bus->write(dma_addr(i), x);
|
||||
|
||||
if(channel[i].fixedxfer == false) {
|
||||
channel[i].srcaddr += channel[i].incmode;
|
||||
}
|
||||
|
||||
add_cycles(8);
|
||||
channel[i].xfersize--;
|
||||
}
|
||||
@@ -51,7 +58,8 @@ int i;
|
||||
|
||||
//first byte transferred?
|
||||
if(channel[i].read_index == 0) {
|
||||
sdd1->dma_begin(i, dma_addr(i), channel[i].xfersize);
|
||||
sdd1->dma_begin(i, (channel[i].srcbank << 16) | (channel[i].srcaddr),
|
||||
channel[i].xfersize);
|
||||
}
|
||||
|
||||
switch(channel[i].xfermode) {
|
||||
@@ -66,6 +74,7 @@ int i;
|
||||
}
|
||||
|
||||
channel[i].read_index++;
|
||||
dma_add_cycles(8);
|
||||
|
||||
if(channel[i].xfersize == 0) {
|
||||
channel[i].active = false;
|
||||
@@ -78,22 +87,15 @@ int i;
|
||||
}
|
||||
|
||||
uint32 bCPU::hdma_addr(uint8 i) {
|
||||
uint32 r;
|
||||
r = channel[i].hdma_addr;
|
||||
r |= (channel[i].srcbank << 16);
|
||||
channel[i].hdma_addr++;
|
||||
return r;
|
||||
return (channel[i].srcbank << 16) | (channel[i].hdma_addr++);
|
||||
}
|
||||
|
||||
uint32 bCPU::hdma_iaddr(uint8 i) {
|
||||
uint32 r;
|
||||
r = channel[i].hdma_iaddr;
|
||||
r |= (channel[i].hdma_ibank << 16);
|
||||
channel[i].hdma_iaddr++;
|
||||
return r;
|
||||
return (channel[i].hdma_ibank << 16) | (channel[i].hdma_iaddr++);
|
||||
}
|
||||
|
||||
void bCPU::hdma_write(uint8 i, uint8 l, uint8 x) {
|
||||
uint16 bCPU::hdma_mmio(uint8 i) {
|
||||
uint8 l = channel[i].read_index;
|
||||
uint16 index;
|
||||
switch(channel[i].xfermode) {
|
||||
case 0:index = 0; break; //0
|
||||
@@ -106,90 +108,118 @@ uint16 index;
|
||||
case 7:index = (l >> 1) & 1;break; //0,0,1,1 [3]
|
||||
}
|
||||
|
||||
index = 0x2100 | ((channel[i].destaddr + index) & 0xff);
|
||||
mem_bus->write(index, x);
|
||||
return (0x2100 | ((channel[i].destaddr + index) & 0xff));
|
||||
}
|
||||
|
||||
uint8 bCPU::hdma_read(uint8 i) {
|
||||
if(channel[i].direction == DMA_MMIOTOCPU) {
|
||||
return mem_bus->read(hdma_mmio(i));
|
||||
} else if(!channel[i].hdma_indirect) {
|
||||
return mem_bus->read(hdma_addr(i));
|
||||
} else {
|
||||
return mem_bus->read(hdma_iaddr(i));
|
||||
}
|
||||
}
|
||||
|
||||
void bCPU::hdma_write(uint8 i, uint8 x) {
|
||||
if(channel[i].direction == DMA_CPUTOMMIO) {
|
||||
mem_bus->write(hdma_mmio(i), x);
|
||||
} else if(!channel[i].hdma_indirect) {
|
||||
mem_bus->write(hdma_addr(i), x);
|
||||
} else {
|
||||
mem_bus->write(hdma_iaddr(i), x);
|
||||
}
|
||||
|
||||
add_cycles(8);
|
||||
hdma_add_cycles(8);
|
||||
}
|
||||
|
||||
void bCPU::hdma_update(uint8 i) {
|
||||
channel[i].hdma_line_counter = mem_bus->read(hdma_addr(i));
|
||||
add_cycles(8);
|
||||
hdma_add_cycles(8);
|
||||
|
||||
if(channel[i].hdma_indirect) {
|
||||
channel[i].hdma_iaddr = mem_bus->read(hdma_addr(i)) << 8;
|
||||
add_cycles(8);
|
||||
hdma_add_cycles(8);
|
||||
}
|
||||
|
||||
if(channel[i].hdma_line_counter == 0) {
|
||||
channel[i].hdma_active = false;
|
||||
channel[i].hdma_do_transfer = false;
|
||||
return;
|
||||
}
|
||||
|
||||
channel[i].hdma_do_transfer = true;
|
||||
|
||||
if(channel[i].hdma_indirect) {
|
||||
channel[i].hdma_iaddr >>= 8;
|
||||
channel[i].hdma_iaddr |= mem_bus->read(hdma_addr(i)) << 8;
|
||||
add_cycles(8);
|
||||
hdma_add_cycles(8);
|
||||
}
|
||||
}
|
||||
|
||||
void bCPU::hdma_run() {
|
||||
int l, xferlen;
|
||||
uint8 x, active_channels = 0;
|
||||
static uint8 hdma_xferlen[8] = { 1, 2, 2, 4, 4, 4, 2, 4 };
|
||||
for(int i=0;i<8;i++) {
|
||||
if(channel[i].hdma_active == false)continue;
|
||||
if(!channel[i].hdma_enabled || !channel[i].hdma_active)continue;
|
||||
|
||||
add_cycles(8);
|
||||
active_channels++;
|
||||
|
||||
if(channel[i].hdma_line_counter == 0) {
|
||||
channel[i].hdma_line_counter = mem_bus->read(hdma_addr(i));
|
||||
|
||||
if(channel[i].hdma_line_counter == 0) {
|
||||
channel[i].hdma_active = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(channel[i].hdma_line_counter > 0x80) {
|
||||
channel[i].hdma_repeat = true;
|
||||
channel[i].hdma_line_counter -= 0x80;
|
||||
} else {
|
||||
channel[i].hdma_repeat = false;
|
||||
}
|
||||
|
||||
channel[i].hdma_first_line = true;
|
||||
|
||||
if(channel[i].hdma_indirect == true) {
|
||||
channel[i].hdma_iaddr = mem_bus->read(hdma_addr(i));
|
||||
channel[i].hdma_iaddr |= mem_bus->read(hdma_addr(i)) << 8;
|
||||
add_cycles(16);
|
||||
if(channel[i].hdma_do_transfer) {
|
||||
int xferlen = hdma_xferlen[channel[i].xfermode];
|
||||
for(channel[i].read_index=0;channel[i].read_index<xferlen;channel[i].read_index++) {
|
||||
hdma_write(i, hdma_read(i));
|
||||
}
|
||||
}
|
||||
|
||||
channel[i].hdma_line_counter--;
|
||||
if(channel[i].hdma_first_line == false && channel[i].hdma_repeat == false)continue;
|
||||
channel[i].hdma_first_line = false;
|
||||
|
||||
xferlen = hdma_xferlen[channel[i].xfermode];
|
||||
for(l=0;l<xferlen;l++) {
|
||||
if(channel[i].hdma_indirect == false) {
|
||||
x = mem_bus->read(hdma_addr(i));
|
||||
} else {
|
||||
x = mem_bus->read(hdma_iaddr(i));
|
||||
}
|
||||
|
||||
hdma_write(i, l, x);
|
||||
add_cycles(8);
|
||||
channel[i].hdma_do_transfer = !!(channel[i].hdma_line_counter & 0x80);
|
||||
if((channel[i].hdma_line_counter & 0x7f) == 0) {
|
||||
hdma_update(i);
|
||||
}
|
||||
}
|
||||
|
||||
if(active_channels != 0) {
|
||||
add_cycles(18);
|
||||
}
|
||||
}
|
||||
|
||||
void bCPU::hdma_initialize() {
|
||||
uint8 active_channels = 0;
|
||||
uint8 bCPU::hdma_enabled_channels() {
|
||||
int r = 0;
|
||||
for(int i=0;i<8;i++) {
|
||||
//does this happen when $420c channel bit is clear?
|
||||
channel[i].hdma_addr = channel[i].srcaddr;
|
||||
channel[i].hdma_line_counter = 0x00;
|
||||
if(channel[i].hdma_enabled)r++;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
if(channel[i].hdma_enabled == false) {
|
||||
channel[i].hdma_active = false;
|
||||
continue;
|
||||
}
|
||||
uint8 bCPU::hdma_active_channels() {
|
||||
int r = 0;
|
||||
for(int i=0;i<8;i++) {
|
||||
if(channel[i].hdma_enabled && channel[i].hdma_active)r++;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
channel[i].hdma_active = true;
|
||||
active_channels++;
|
||||
/* hdmainit_activate()
|
||||
* hdma_activate()
|
||||
*
|
||||
* Functions are called by CPU timing routine
|
||||
* when an HDMA event (init or run) occurs.
|
||||
*/
|
||||
|
||||
if(channel[i].hdma_indirect == false) {
|
||||
add_cycles(8);
|
||||
} else {
|
||||
add_cycles(24);
|
||||
}
|
||||
void bCPU::hdmainit_activate() {
|
||||
for(int i=0;i<8;i++) {
|
||||
channel[i].hdma_active = false;
|
||||
channel[i].hdma_do_transfer = false;
|
||||
}
|
||||
|
||||
if(active_channels != 0) {
|
||||
add_cycles(18);
|
||||
if(hdma_enabled_channels() != 0) {
|
||||
status.hdma_state = HDMASTATE_IDMASYNC;
|
||||
run_state.hdma = true;
|
||||
}
|
||||
}
|
||||
|
||||
void bCPU::hdma_activate() {
|
||||
if(hdma_active_channels() != 0) {
|
||||
status.hdma_state = HDMASTATE_DMASYNC;
|
||||
run_state.hdma = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,15 +236,15 @@ void bCPU::dma_reset() {
|
||||
channel[i].xfermode = 0;
|
||||
channel[i].destaddr = 0;
|
||||
channel[i].srcaddr = 0;
|
||||
channel[i].xfersize = 0;
|
||||
channel[i].xfersize = 0x0000;
|
||||
//xfersize and hdma_iaddr are of union { uint16 };
|
||||
//channel[i].hdma_iaddr = 0x0000;
|
||||
channel[i].hdma_ibank = 0;
|
||||
channel[i].hdma_addr = 0x0000;
|
||||
channel[i].hdma_line_counter = 0x00;
|
||||
channel[i].hdma_unknown = 0x00;
|
||||
|
||||
channel[i].hdma_active = false;
|
||||
channel[i].hdma_first_line = false;
|
||||
channel[i].hdma_repeat = false;
|
||||
channel[i].hdma_iaddr = 0x0000;
|
||||
channel[i].hdma_do_transfer = false;
|
||||
}
|
||||
}
|
||||
+126
-32
@@ -1,51 +1,145 @@
|
||||
void bCPU::last_cycle() {
|
||||
status.is_last_cycle = true;
|
||||
}
|
||||
|
||||
void bCPU::last_cycle_exec() {
|
||||
status.is_last_cycle = false;
|
||||
|
||||
time.nmi_pending = nmi_test();
|
||||
time.irq_pending = irq_test();
|
||||
}
|
||||
|
||||
void bCPU::cycle_edge() {
|
||||
int c, n, z;
|
||||
if(status.dma_state != DMASTATE_STOP) {
|
||||
void bCPU::pre_exec_cycle() {
|
||||
if(!run_state.dma && !run_state.hdma)return;
|
||||
|
||||
int c, z;
|
||||
if(run_state.hdma) {
|
||||
switch(status.hdma_state) {
|
||||
case HDMASTATE_ICPUSYNC:
|
||||
case HDMASTATE_CPUSYNC:
|
||||
c = status.cycle_count;
|
||||
z = c - (status.hdma_cycle_count % c);
|
||||
if(!z)z = c;
|
||||
add_cycles(z);
|
||||
run_state.hdma = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(run_state.dma) {
|
||||
switch(status.dma_state) {
|
||||
case DMASTATE_DMASYNC:
|
||||
status.dma_state = DMASTATE_DMASYNC2;
|
||||
break;
|
||||
case DMASTATE_DMASYNC2:
|
||||
n = 8 - dma_counter() + 8;
|
||||
add_cycles(n);
|
||||
status.dma_cycle_count = n;
|
||||
for(z=0;z<8;z++) {
|
||||
if(channel[z].active == false)continue;
|
||||
add_cycles(8);
|
||||
status.dma_cycle_count += 8;
|
||||
}
|
||||
status.cpu_state = CPUSTATE_DMA;
|
||||
status.dma_state = DMASTATE_RUN;
|
||||
break;
|
||||
case DMASTATE_RUN:
|
||||
status.dma_cycle_count += 8;
|
||||
break;
|
||||
case DMASTATE_CPUSYNC:
|
||||
status.cpu_state = CPUSTATE_RUN;
|
||||
status.dma_state = DMASTATE_CPUSYNC2;
|
||||
break;
|
||||
case DMASTATE_CPUSYNC2:
|
||||
c = status.cycle_count;
|
||||
z = c - (status.dma_cycle_count % c);
|
||||
if(!z)z = c;
|
||||
add_cycles(z);
|
||||
status.dma_state = DMASTATE_STOP;
|
||||
run_state.dma = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bCPU::exec_hdma() {
|
||||
int n;
|
||||
static int z;
|
||||
switch(status.hdma_state) {
|
||||
case HDMASTATE_IDMASYNC:
|
||||
status.hdma_cycle_count = 0;
|
||||
z = 0;
|
||||
if(!run_state.dma) {
|
||||
exec_cycle();
|
||||
status.hdma_state = HDMASTATE_IDMASYNC2;
|
||||
} else {
|
||||
status.hdma_state = HDMASTATE_IDMASYNC3;
|
||||
}
|
||||
break;
|
||||
case HDMASTATE_IDMASYNC2:
|
||||
n = 8 - dma_counter() + 8;
|
||||
add_cycles(n);
|
||||
status.hdma_cycle_count += n;
|
||||
status.hdma_state = HDMASTATE_IDMASYNC3;
|
||||
break;
|
||||
case HDMASTATE_IDMASYNC3:
|
||||
channel[z].hdma_active = channel[z].hdma_enabled;
|
||||
if(channel[z].hdma_enabled) {
|
||||
channel[z].hdma_addr = channel[z].srcaddr;
|
||||
hdma_update(z); //updates status.hdma_cycle_count
|
||||
}
|
||||
if(++z < 8)break;
|
||||
if(!run_state.dma) {
|
||||
status.hdma_state = HDMASTATE_ICPUSYNC;
|
||||
} else {
|
||||
run_state.hdma = false;
|
||||
}
|
||||
break;
|
||||
case HDMASTATE_ICPUSYNC:
|
||||
exec_cycle();
|
||||
break;
|
||||
|
||||
case HDMASTATE_DMASYNC:
|
||||
status.hdma_cycle_count = 0;
|
||||
z = 0;
|
||||
if(!run_state.dma) {
|
||||
exec_cycle();
|
||||
status.hdma_state = HDMASTATE_DMASYNC2;
|
||||
} else {
|
||||
status.hdma_state = HDMASTATE_DMASYNC3;
|
||||
}
|
||||
break;
|
||||
case HDMASTATE_DMASYNC2:
|
||||
n = 8 - dma_counter() + 8;
|
||||
add_cycles(n);
|
||||
status.hdma_cycle_count += n;
|
||||
status.hdma_state = HDMASTATE_DMASYNC3;
|
||||
break;
|
||||
case HDMASTATE_DMASYNC3:
|
||||
if(channel[z].hdma_active) {
|
||||
add_cycles(8);
|
||||
status.hdma_cycle_count += 8;
|
||||
}
|
||||
if(++z < 8)break;
|
||||
status.hdma_state = HDMASTATE_RUN;
|
||||
break;
|
||||
case HDMASTATE_RUN:
|
||||
hdma_run(); //updates status.hdma_cycle_count
|
||||
if(!run_state.dma) {
|
||||
status.hdma_state = HDMASTATE_CPUSYNC;
|
||||
} else {
|
||||
run_state.hdma = false;
|
||||
}
|
||||
break;
|
||||
case HDMASTATE_CPUSYNC:
|
||||
exec_cycle();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void bCPU::exec_dma() {
|
||||
int n;
|
||||
static int z;
|
||||
switch(status.dma_state) {
|
||||
case DMASTATE_DMASYNC:
|
||||
exec_cycle();
|
||||
status.dma_state = DMASTATE_DMASYNC2;
|
||||
break;
|
||||
case DMASTATE_DMASYNC2:
|
||||
n = 8 - dma_counter() + 8;
|
||||
add_cycles(n);
|
||||
status.dma_cycle_count = n;
|
||||
z = 0;
|
||||
status.dma_state = DMASTATE_DMASYNC3;
|
||||
break;
|
||||
case DMASTATE_DMASYNC3:
|
||||
if(channel[z].active == true) {
|
||||
add_cycles(8);
|
||||
status.dma_cycle_count += 8;
|
||||
}
|
||||
if(++z < 8)break;
|
||||
status.dma_state = DMASTATE_RUN;
|
||||
break;
|
||||
case DMASTATE_RUN:
|
||||
dma_run(); //updates status.dma_cycle_count
|
||||
break;
|
||||
case DMASTATE_CPUSYNC:
|
||||
exec_cycle();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void bCPU::exec_cycle() {
|
||||
//on first cycle?
|
||||
if(status.cycle_pos == 0) {
|
||||
|
||||
+30
-33
@@ -1,43 +1,43 @@
|
||||
/*
|
||||
[IRQ cycles]
|
||||
[1] pbr,pc ; opcode
|
||||
[2] pbr,pc ; io
|
||||
[3] 0,s ; pbr
|
||||
[4] 0,s-1 ; pch
|
||||
[5] 0,s-2 ; pcl
|
||||
[6] 0,s-3 ; p
|
||||
[7] 0,va ; aavl
|
||||
[8] 0,va+1 ; aavh
|
||||
[0] pbr,pc ; opcode
|
||||
[1] pbr,pc ; io
|
||||
[2] 0,s ; pbr
|
||||
[3] 0,s-1 ; pch
|
||||
[4] 0,s-2 ; pcl
|
||||
[5] 0,s-3 ; p
|
||||
[6] 0,va ; aavl
|
||||
[7] 0,va+1 ; aavh
|
||||
*/
|
||||
void bCPU::irq(uint16 addr) {
|
||||
void bCPU::irq_run() {
|
||||
//WDC documentation is incorrect, first cycle
|
||||
//is a memory read fetch from PBR:PC
|
||||
add_cycles(mem_bus->speed(regs.pc.d));
|
||||
add_cycles(6);
|
||||
stack_write(regs.pc.b);
|
||||
stack_write(regs.pc.h);
|
||||
stack_write(regs.pc.l);
|
||||
stack_write(regs.p);
|
||||
rd.l = op_read(OPMODE_ADDR, addr);
|
||||
rd.h = op_read(OPMODE_ADDR, addr + 1);
|
||||
|
||||
regs.pc.b = 0x00;
|
||||
regs.pc.w = rd.w;
|
||||
regs.p.i = 1;
|
||||
regs.p.d = 0;
|
||||
|
||||
//let debugger know the new IRQ opcode address
|
||||
snes->notify(SNES::CPU_EXEC_OPCODE_END);
|
||||
switch(status.cycle_pos++) {
|
||||
case 0: add_cycles(mem_bus->speed(regs.pc.d)); break;
|
||||
case 1: add_cycles(6); break;
|
||||
case 2: stack_write(regs.pc.b); break;
|
||||
case 3: stack_write(regs.pc.h); break;
|
||||
case 4: stack_write(regs.pc.l); break;
|
||||
case 5: stack_write(regs.p); break;
|
||||
case 6: rd.l = op_read(OPMODE_ADDR, aa.w); break;
|
||||
case 7: rd.h = op_read(OPMODE_ADDR, aa.w + 1);
|
||||
regs.pc.b = 0x00;
|
||||
regs.pc.w = rd.w;
|
||||
regs.p.i = 1;
|
||||
regs.p.d = 0;
|
||||
//let debugger know the new IRQ opcode address
|
||||
snes->notify(SNES::CPU_EXEC_OPCODE_END);
|
||||
status.cycle_pos = 0;
|
||||
run_state.irq = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool bCPU::nmi_test() {
|
||||
if(time.nmi_transition == 0)return false;
|
||||
time.nmi_transition = 0;
|
||||
|
||||
if(status.cpu_state == CPUSTATE_WAI) {
|
||||
status.cpu_state = CPUSTATE_RUN;
|
||||
}
|
||||
|
||||
run_state.wai = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -61,10 +61,7 @@ bool bCPU::irq_test() {
|
||||
_true:
|
||||
time.irq_transition = 0;
|
||||
|
||||
if(status.cpu_state == CPUSTATE_WAI) {
|
||||
status.cpu_state = CPUSTATE_RUN;
|
||||
}
|
||||
|
||||
run_state.wai = false;
|
||||
if(regs.p.i)return false;
|
||||
return true;
|
||||
}
|
||||
@@ -2,9 +2,11 @@ void bCPU::mmio_reset() {
|
||||
//$2181-$2183
|
||||
status.wram_addr = 0x000000;
|
||||
|
||||
//$4016
|
||||
//$4016-$4017
|
||||
status.joypad1_strobe_value = 0x00;
|
||||
status.joypad2_strobe_value = 0x00;
|
||||
status.joypad1_read_pos = 0;
|
||||
status.joypad2_read_pos = 0;
|
||||
|
||||
//$4200
|
||||
status.nmi_enabled = false;
|
||||
@@ -91,6 +93,28 @@ uint8 r;
|
||||
r = regs.mdr & 0xe0;
|
||||
r |= 0x1c;
|
||||
|
||||
if(status.joypad2_strobe_value == 1) {
|
||||
r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_B);
|
||||
} else {
|
||||
switch(status.joypad2_read_pos) {
|
||||
case 0:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_B); break;
|
||||
case 1:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_Y); break;
|
||||
case 2:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_SELECT);break;
|
||||
case 3:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_START); break;
|
||||
case 4:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_UP); break;
|
||||
case 5:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_DOWN); break;
|
||||
case 6:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_LEFT); break;
|
||||
case 7:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_RIGHT); break;
|
||||
case 8:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_A); break;
|
||||
case 9:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_X); break;
|
||||
case 10:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_L); break;
|
||||
case 11:r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_R); break;
|
||||
case 16:r |= 1;break; //joypad connected bit
|
||||
default:r |= 1;break; //after 16th read, all subsequent reads return 1
|
||||
}
|
||||
if(++status.joypad2_read_pos > 17)status.joypad2_read_pos = 17;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -206,6 +230,36 @@ uint16 v = vcounter();
|
||||
return r;
|
||||
}
|
||||
|
||||
//JOY2L
|
||||
uint8 bCPU::mmio_r421a() {
|
||||
uint8 r = 0x00;
|
||||
uint16 v = vcounter();
|
||||
if(status.auto_joypad_poll == false)return 0x00; //can't read joypad if auto polling not enabled
|
||||
//if(v >= 225 && v <= 227)return 0x00; //can't read joypad while SNES is polling input
|
||||
r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_A) << 7;
|
||||
r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_X) << 6;
|
||||
r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_L) << 5;
|
||||
r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_R) << 4;
|
||||
return r;
|
||||
}
|
||||
|
||||
//JOY2H
|
||||
uint8 bCPU::mmio_r421b() {
|
||||
uint8 r = 0x00;
|
||||
uint16 v = vcounter();
|
||||
if(status.auto_joypad_poll == false)return 0x00; //can't read joypad if auto polling not enabled
|
||||
//if(v >= 225 && v <= 227)return 0x00; //can't read joypad while SNES is polling input
|
||||
r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_B) << 7;
|
||||
r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_Y) << 6;
|
||||
r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_SELECT) << 5;
|
||||
r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_START) << 4;
|
||||
r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_UP) << 3;
|
||||
r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_DOWN) << 2;
|
||||
r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_LEFT) << 1;
|
||||
r |= (uint8)snes->get_input_status(SNES::DEV_JOYPAD2, SNES::JOYPAD_RIGHT);
|
||||
return r;
|
||||
}
|
||||
|
||||
//DMAPx
|
||||
uint8 bCPU::mmio_r43x0(uint8 i) {
|
||||
return channel[i].dmap;
|
||||
@@ -310,9 +364,9 @@ uint i;
|
||||
case 0x4217:return cpu->mmio_r4217(); //RDMPYH
|
||||
case 0x4218:return cpu->mmio_r4218(); //JOY1L
|
||||
case 0x4219:return cpu->mmio_r4219(); //JOY1H
|
||||
case 0x421a:case 0x421b:case 0x421c:case 0x421d:case 0x421e:case 0x421f:return 0x00;
|
||||
case 0x4000:dprintf("* 4000 read at %3d,%4d <%d>", cpu->time.v, cpu->time.hc, cpu->status.cycle_count);break;
|
||||
case 0x4200:dprintf("* 4200 read at %3d,%4d", cpu->time.v, cpu->time.hc);break;
|
||||
case 0x421a:return cpu->mmio_r421a(); //JOY2L
|
||||
case 0x421b:return cpu->mmio_r421b(); //JOY2H
|
||||
case 0x421c:case 0x421d:case 0x421e:case 0x421f:return 0x00;
|
||||
}
|
||||
|
||||
return cpu->regs.mdr;
|
||||
@@ -345,13 +399,22 @@ void bCPU::mmio_w2183(uint8 value) {
|
||||
|
||||
//JOYSER0
|
||||
void bCPU::mmio_w4016(uint8 value) {
|
||||
status.joypad1_strobe_value = (value & 1);
|
||||
status.joypad1_strobe_value = !!(value & 1);
|
||||
if(value == 1) {
|
||||
snes->poll_input();
|
||||
snes->poll_input(SNES::DEV_JOYPAD1);
|
||||
status.joypad1_read_pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//JOYSER1
|
||||
void bCPU::mmio_w4017(uint8 value) {
|
||||
status.joypad2_strobe_value = !!(value & 1);
|
||||
if(value == 1) {
|
||||
snes->poll_input(SNES::DEV_JOYPAD2);
|
||||
status.joypad2_read_pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//NMITIMEN
|
||||
void bCPU::mmio_w4200(uint8 value) {
|
||||
status.nmi_enabled = !!(value & 0x80);
|
||||
@@ -438,6 +501,7 @@ void bCPU::mmio_w420a(uint8 value) {
|
||||
void bCPU::mmio_w420b(uint8 value) {
|
||||
int len;
|
||||
if(value != 0x00) {
|
||||
run_state.dma = true;
|
||||
status.dma_state = DMASTATE_DMASYNC;
|
||||
}
|
||||
|
||||
@@ -566,6 +630,7 @@ uint8 i;
|
||||
case 0x2182:cpu->mmio_w2182(value);return; //WMADDM
|
||||
case 0x2183:cpu->mmio_w2183(value);return; //WMADDH
|
||||
case 0x4016:cpu->mmio_w4016(value);return; //JOYSER0
|
||||
case 0x4017:cpu->mmio_w4017(value);return; //JOYSER1
|
||||
case 0x4200:cpu->mmio_w4200(value);return; //NMITIMEN
|
||||
case 0x4201:cpu->mmio_w4201(value);return; //WRIO
|
||||
case 0x4202:cpu->mmio_w4202(value);return; //WRMPYA
|
||||
@@ -580,7 +645,6 @@ uint8 i;
|
||||
case 0x420b:cpu->mmio_w420b(value);return; //DMAEN
|
||||
case 0x420c:cpu->mmio_w420c(value);return; //HDMAEN
|
||||
case 0x420d:cpu->mmio_w420d(value);return; //MEMSEL
|
||||
case 0x4000:dprintf("* 4000 write at %3d,%4d", cpu->time.v, cpu->time.hc);break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -207,7 +207,7 @@ void bCPU::inc_vcounter() {
|
||||
}
|
||||
}
|
||||
|
||||
time.dma_counter = time.line_cycles & 6;
|
||||
time.dma_counter += time.line_cycles;
|
||||
if(time.v == 240 && time.interlace == false && time.interlace_field == 1) {
|
||||
time.line_cycles = 1360;
|
||||
} else {
|
||||
@@ -259,21 +259,14 @@ void bCPU::add_cycles(int cycles) {
|
||||
|
||||
scanline();
|
||||
ppu->scanline();
|
||||
// ppu->render_scanline();
|
||||
snes->scanline();
|
||||
time.line_rendered = false;
|
||||
}
|
||||
|
||||
if(time.line_rendered == false) {
|
||||
//rendering should start at H=22, but due to inaccurate
|
||||
//timing, and due to using a scanline-based renderer, use
|
||||
//a higher value to allow more games to run properly...
|
||||
//H=48 fixes off-by-one HDMA effects with FF6's battles
|
||||
if(time.hc + cycles >= (48 * 4)) {
|
||||
cycles = (time.hc + cycles) - (48 * 4);
|
||||
time.hc = (48 * 4);
|
||||
time.line_rendered = true;
|
||||
ppu->render_scanline();
|
||||
if(time.hdmainit_triggered == false) {
|
||||
if(time.hc + cycles >= time.hdmainit_trigger_pos || time.v) {
|
||||
time.hdmainit_triggered = true;
|
||||
hdmainit_activate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,14 +289,22 @@ void bCPU::add_cycles(int cycles) {
|
||||
}
|
||||
}
|
||||
|
||||
if(status.hdma_triggered == false) {
|
||||
//vcounter range verified on hardware
|
||||
if(time.line_rendered == false) {
|
||||
//rendering should start at H=18 (+256=274), but since the
|
||||
//current PPU emulation renders the entire scanline at once,
|
||||
//PPU register changes mid-scanline do not show up.
|
||||
//therefore, wait a few dots before rendering the scanline
|
||||
if(time.hc + cycles >= (48 * 4)) {
|
||||
time.line_rendered = true;
|
||||
ppu->render_scanline();
|
||||
}
|
||||
}
|
||||
|
||||
if(time.hdma_triggered == false) {
|
||||
if(time.v <= (overscan() ? 239 : 224)) {
|
||||
if(time.hc + cycles >= 1112) { //278 * 4 = 1112
|
||||
cycles = (time.hc + cycles) - 1112;
|
||||
time.hc = 1112;
|
||||
status.hdma_triggered = true;
|
||||
hdma_run();
|
||||
if(time.hc + cycles >= 1106) {
|
||||
time.hdma_triggered = true;
|
||||
hdma_activate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -312,11 +313,8 @@ void bCPU::add_cycles(int cycles) {
|
||||
}
|
||||
|
||||
void bCPU::time_reset() {
|
||||
//initial latch values for $213c/$213d
|
||||
//[x]0035 : [y]0000 (53.0 -> 212) [lda $2137]
|
||||
//[x]0038 : [y]0000 (56.5 -> 226) [nop : lda $2137]
|
||||
time.v = 0;
|
||||
time.hc = 186;
|
||||
time.hc = 0;
|
||||
|
||||
//upon SNES reset, start at scanline 0 non-interlace
|
||||
time.interlace = false;
|
||||
@@ -329,6 +327,12 @@ void bCPU::time_reset() {
|
||||
|
||||
time.dma_counter = 0;
|
||||
|
||||
//set at V=0,H=0
|
||||
time.hdmainit_trigger_pos = 0;
|
||||
time.hdmainit_triggered = true;
|
||||
|
||||
time.hdma_triggered = false;
|
||||
|
||||
time.nmi_pending = false;
|
||||
time.irq_pending = false;
|
||||
time.nmi_line = time.nmi_read = 1;
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
@@ -2,6 +2,6 @@
|
||||
#include "dcpu.cpp"
|
||||
|
||||
CPU::CPU() {
|
||||
cpu_version = 2;
|
||||
cpu_version = 1;
|
||||
mmio = &mmio_unmapped;
|
||||
}
|
||||
@@ -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
@@ -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); }
|
||||
|
||||
@@ -145,7 +145,7 @@ int i, v, n;
|
||||
|
||||
case 0x4c:
|
||||
status.KON = data;
|
||||
status.kon = data;
|
||||
// status.kon = data;
|
||||
status.key_flag = true;
|
||||
break;
|
||||
|
||||
@@ -221,7 +221,7 @@ int v;
|
||||
status.KOFF = 0x00;
|
||||
status.FLG |= 0xe0;
|
||||
|
||||
status.kon = 0x00;
|
||||
//status.kon = 0x00;
|
||||
status.key_flag = false;
|
||||
|
||||
status.noise_ctr = 0;
|
||||
@@ -290,17 +290,20 @@ int32 fir_samplel, fir_sampler;
|
||||
|
||||
if(!(dsp_counter++ & 1) && status.key_flag) {
|
||||
for(v=0;v<8;v++) {
|
||||
uint8 mask = 1 << v;
|
||||
if(status.soft_reset()) {
|
||||
if(voice[v].env_state != SILENCE) {
|
||||
voice[v].env_state = SILENCE;
|
||||
voice[v].AdjustEnvelope();
|
||||
}
|
||||
} else if(status.KOFF & (1 << v)) {
|
||||
} else if(status.KOFF & mask) {
|
||||
if(voice[v].env_state != SILENCE && voice[v].env_state != RELEASE) {
|
||||
voice[v].env_state = RELEASE;
|
||||
voice[v].AdjustEnvelope();
|
||||
}
|
||||
} else if(status.kon & (1 << v)) {
|
||||
} else if(status.KON & mask) { //status.kon
|
||||
status.KON &= ~mask; //new code
|
||||
status.ENDX &= ~mask; //new code
|
||||
voice[v].brr_ptr = read_16((status.DIR << 8) + (voice[v].SRCN << 2));
|
||||
voice[v].brr_index = -9;
|
||||
voice[v].brr_looped = false;
|
||||
@@ -313,8 +316,8 @@ int32 fir_samplel, fir_sampler;
|
||||
voice[v].AdjustEnvelope();
|
||||
}
|
||||
}
|
||||
status.ENDX &= ~status.kon;
|
||||
status.kon = 0;
|
||||
// status.ENDX &= ~status.kon;
|
||||
// status.kon = 0;
|
||||
status.key_flag = false;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ struct Status {
|
||||
int8 FIR[8];
|
||||
|
||||
//internal variables
|
||||
uint8 kon;
|
||||
//uint8 kon;
|
||||
bool key_flag;
|
||||
|
||||
int16 noise_ctr, noise_rate;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#define BSNES_VERSION "0.012"
|
||||
#define BSNES_VERSION "0.013"
|
||||
#define BSNES_TITLE "bsnes v" BSNES_VERSION
|
||||
|
||||
#include "reader/reader.h"
|
||||
|
||||
+55
-71
@@ -32,35 +32,18 @@ void bPPU::scanline() {
|
||||
}
|
||||
|
||||
void bPPU::render_scanline() {
|
||||
//only allow frameskip setting to ignore actual rendering; not RTO, etc.
|
||||
if(settings.frameskip_pos != 0)return;
|
||||
if(status.render_output == false)return;
|
||||
|
||||
if(_y > 0 && _y < (cpu->overscan() ? 239 : 224)) {
|
||||
render_line();
|
||||
}
|
||||
}
|
||||
|
||||
bool bPPU::render_frame() { return (settings.frameskip_pos == 0); }
|
||||
|
||||
void bPPU::frame() {
|
||||
if(settings.frameskip_changed == true) {
|
||||
settings.frameskip_changed = false;
|
||||
settings.frameskip_pos = 0;
|
||||
} else {
|
||||
settings.frameskip_pos++;
|
||||
settings.frameskip_pos %= (settings.frameskip + 1);
|
||||
}
|
||||
|
||||
if(settings.frameskip_pos != 0)return;
|
||||
|
||||
PPU::frame();
|
||||
snes->notify(SNES::RENDER_FRAME);
|
||||
}
|
||||
|
||||
void bPPU::set_frameskip(int fs) {
|
||||
settings.frameskip = fs;
|
||||
settings.frameskip_changed = true;
|
||||
}
|
||||
|
||||
void bPPU::power() {
|
||||
memset(vram, 0, 65536);
|
||||
memset(oam, 0, 544);
|
||||
@@ -162,30 +145,33 @@ void bPPU::reset() {
|
||||
regs.cgram_addr = 0x0000;
|
||||
|
||||
//$2123-$2125
|
||||
regs.bg_window1_enabled[BG1] = false;
|
||||
regs.bg_window1_enabled[BG2] = false;
|
||||
regs.bg_window1_enabled[BG3] = false;
|
||||
regs.bg_window1_enabled[BG4] = false;
|
||||
regs.bg_window1_enabled[OAM] = false;
|
||||
regs.bg_window1_invert [BG1] = false;
|
||||
regs.bg_window1_invert [BG2] = false;
|
||||
regs.bg_window1_invert [BG3] = false;
|
||||
regs.bg_window1_invert [BG4] = false;
|
||||
regs.bg_window1_invert [OAM] = false;
|
||||
regs.bg_window2_enabled[BG1] = false;
|
||||
regs.bg_window2_enabled[BG2] = false;
|
||||
regs.bg_window2_enabled[BG3] = false;
|
||||
regs.bg_window2_enabled[BG4] = false;
|
||||
regs.bg_window2_enabled[OAM] = false;
|
||||
regs.bg_window2_invert [BG1] = false;
|
||||
regs.bg_window2_invert [BG2] = false;
|
||||
regs.bg_window2_invert [BG3] = false;
|
||||
regs.bg_window2_invert [BG4] = false;
|
||||
regs.bg_window2_invert [OAM] = false;
|
||||
regs.color_window1_enabled = false;
|
||||
regs.color_window1_invert = false;
|
||||
regs.color_window2_enabled = false;
|
||||
regs.color_window2_invert = false;
|
||||
regs.window1_enabled[BG1] = false;
|
||||
regs.window1_enabled[BG2] = false;
|
||||
regs.window1_enabled[BG3] = false;
|
||||
regs.window1_enabled[BG4] = false;
|
||||
regs.window1_enabled[OAM] = false;
|
||||
regs.window1_enabled[COL] = false;
|
||||
|
||||
regs.window1_invert [BG1] = false;
|
||||
regs.window1_invert [BG2] = false;
|
||||
regs.window1_invert [BG3] = false;
|
||||
regs.window1_invert [BG4] = false;
|
||||
regs.window1_invert [OAM] = false;
|
||||
regs.window1_invert [COL] = false;
|
||||
|
||||
regs.window2_enabled[BG1] = false;
|
||||
regs.window2_enabled[BG2] = false;
|
||||
regs.window2_enabled[BG3] = false;
|
||||
regs.window2_enabled[BG4] = false;
|
||||
regs.window2_enabled[OAM] = false;
|
||||
regs.window2_enabled[COL] = false;
|
||||
|
||||
regs.window2_invert [BG1] = false;
|
||||
regs.window2_invert [BG2] = false;
|
||||
regs.window2_invert [BG3] = false;
|
||||
regs.window2_invert [BG4] = false;
|
||||
regs.window2_invert [OAM] = false;
|
||||
regs.window2_invert [COL] = false;
|
||||
|
||||
//$2126-$2129
|
||||
regs.window1_left = 0;
|
||||
@@ -194,12 +180,12 @@ void bPPU::reset() {
|
||||
regs.window2_right = 0;
|
||||
|
||||
//$212a-$212b
|
||||
regs.bg_window_mask[BG1] = 0;
|
||||
regs.bg_window_mask[BG2] = 0;
|
||||
regs.bg_window_mask[BG3] = 0;
|
||||
regs.bg_window_mask[BG4] = 0;
|
||||
regs.bg_window_mask[OAM] = 0;
|
||||
regs.color_window_mask = 0;
|
||||
regs.window_mask[BG1] = 0;
|
||||
regs.window_mask[BG2] = 0;
|
||||
regs.window_mask[BG3] = 0;
|
||||
regs.window_mask[BG4] = 0;
|
||||
regs.window_mask[OAM] = 0;
|
||||
regs.window_mask[COL] = 0;
|
||||
|
||||
//$212c-$212d
|
||||
regs.bg_enabled[BG1] = false;
|
||||
@@ -214,16 +200,16 @@ void bPPU::reset() {
|
||||
regs.bgsub_enabled[OAM] = false;
|
||||
|
||||
//$212e-$212f
|
||||
regs.bg_window_enabled[BG1] = false;
|
||||
regs.bg_window_enabled[BG2] = false;
|
||||
regs.bg_window_enabled[BG3] = false;
|
||||
regs.bg_window_enabled[BG4] = false;
|
||||
regs.bg_window_enabled[OAM] = false;
|
||||
regs.bgsub_window_enabled[BG1] = false;
|
||||
regs.bgsub_window_enabled[BG2] = false;
|
||||
regs.bgsub_window_enabled[BG3] = false;
|
||||
regs.bgsub_window_enabled[BG4] = false;
|
||||
regs.bgsub_window_enabled[OAM] = false;
|
||||
regs.window_enabled[BG1] = false;
|
||||
regs.window_enabled[BG2] = false;
|
||||
regs.window_enabled[BG3] = false;
|
||||
regs.window_enabled[BG4] = false;
|
||||
regs.window_enabled[OAM] = false;
|
||||
regs.sub_window_enabled[BG1] = false;
|
||||
regs.sub_window_enabled[BG2] = false;
|
||||
regs.sub_window_enabled[BG3] = false;
|
||||
regs.sub_window_enabled[BG4] = false;
|
||||
regs.sub_window_enabled[OAM] = false;
|
||||
|
||||
//$2130
|
||||
regs.color_mask = 0;
|
||||
@@ -231,14 +217,14 @@ void bPPU::reset() {
|
||||
regs.addsub_mode = 0;
|
||||
|
||||
//$2131
|
||||
regs.color_mode = 0;
|
||||
regs.color_halve = false;
|
||||
regs.bg_color_enabled[BACK] = false;
|
||||
regs.bg_color_enabled[OAM] = false;
|
||||
regs.bg_color_enabled[BG4] = false;
|
||||
regs.bg_color_enabled[BG3] = false;
|
||||
regs.bg_color_enabled[BG2] = false;
|
||||
regs.bg_color_enabled[BG1] = false;
|
||||
regs.color_mode = 0;
|
||||
regs.color_halve = false;
|
||||
regs.color_enabled[BACK] = false;
|
||||
regs.color_enabled[OAM] = false;
|
||||
regs.color_enabled[BG4] = false;
|
||||
regs.color_enabled[BG3] = false;
|
||||
regs.color_enabled[BG2] = false;
|
||||
regs.color_enabled[BG1] = false;
|
||||
|
||||
//$2132
|
||||
regs.color_r = 0x00;
|
||||
@@ -266,8 +252,10 @@ void bPPU::reset() {
|
||||
regs.time_over = false;
|
||||
regs.range_over = false;
|
||||
|
||||
_screen_width = 256; //needed for clear_window_cache()
|
||||
update_sprite_list_sizes();
|
||||
clear_tiledata_cache();
|
||||
clear_window_cache();
|
||||
}
|
||||
|
||||
uint8 bPPU::vram_read(uint16 addr) {
|
||||
@@ -318,10 +306,6 @@ void bPPU::cgram_write(uint16 addr, uint8 value) {
|
||||
}
|
||||
|
||||
bPPU::bPPU() {
|
||||
settings.frameskip = 0;
|
||||
settings.frameskip_pos = 0;
|
||||
settings.frameskip_changed = false;
|
||||
|
||||
mmio = new bPPUMMIO(this);
|
||||
|
||||
vram = (uint8*)malloc(65536);
|
||||
|
||||
+10
-18
@@ -14,7 +14,7 @@ uint8 *vram, *oam, *cgram;
|
||||
uint8 region;
|
||||
|
||||
enum { NTSC = 0, PAL = 1 };
|
||||
enum { BG1 = 0, BG2 = 1, BG3 = 2, BG4 = 3, OAM = 4, BACK = 5 };
|
||||
enum { BG1 = 0, BG2 = 1, BG3 = 2, BG4 = 3, OAM = 4, BACK = 5, COL = 5 };
|
||||
enum { SC_32x32 = 0, SC_32x64 = 1, SC_64x32 = 2, SC_64x64 = 3 };
|
||||
|
||||
struct sprite_item {
|
||||
@@ -27,11 +27,6 @@ struct sprite_item {
|
||||
uint8 priority;
|
||||
} sprite_list[128];
|
||||
|
||||
struct {
|
||||
int32 frameskip, frameskip_pos;
|
||||
bool frameskip_changed;
|
||||
} settings;
|
||||
|
||||
struct {
|
||||
//open bus support
|
||||
uint8 ppu1_mdr, ppu2_mdr;
|
||||
@@ -95,26 +90,23 @@ struct {
|
||||
uint16 cgram_addr;
|
||||
|
||||
//$2123-$2125
|
||||
bool bg_window1_enabled[5];
|
||||
bool bg_window1_invert [5];
|
||||
bool bg_window2_enabled[5];
|
||||
bool bg_window2_invert [5];
|
||||
bool color_window1_enabled, color_window1_invert;
|
||||
bool color_window2_enabled, color_window2_invert;
|
||||
bool window1_enabled[6];
|
||||
bool window1_invert [6];
|
||||
bool window2_enabled[6];
|
||||
bool window2_invert [6];
|
||||
|
||||
//$2126-$2129
|
||||
uint8 window1_left, window1_right;
|
||||
uint8 window2_left, window2_right;
|
||||
|
||||
//$212a-$212b
|
||||
uint8 bg_window_mask[5];
|
||||
uint8 color_window_mask;
|
||||
uint8 window_mask[6];
|
||||
|
||||
//$212c-$212d
|
||||
bool bg_enabled[5], bgsub_enabled[5];
|
||||
|
||||
//$212e-$212f
|
||||
bool bg_window_enabled[5], bgsub_window_enabled[5];
|
||||
bool window_enabled[5], sub_window_enabled[5];
|
||||
|
||||
//$2130
|
||||
uint8 color_mask, colorsub_mask;
|
||||
@@ -123,7 +115,7 @@ struct {
|
||||
|
||||
//$2131
|
||||
bool color_mode, color_halve;
|
||||
bool bg_color_enabled[6];
|
||||
bool color_enabled[6];
|
||||
|
||||
//$2132
|
||||
uint8 color_r, color_g, color_b;
|
||||
@@ -158,6 +150,8 @@ struct {
|
||||
void update_sprite_list(uint16 addr);
|
||||
void update_sprite_list_sizes();
|
||||
uint16 get_vram_address();
|
||||
bool vram_can_read();
|
||||
bool vram_can_write(uint8 &value);
|
||||
|
||||
void mmio_w2100(uint8 value); //INIDISP
|
||||
void mmio_w2101(uint8 value); //OBSEL
|
||||
@@ -242,8 +236,6 @@ uint16 *mosaic_table[16];
|
||||
void frame();
|
||||
void power();
|
||||
void reset();
|
||||
void set_frameskip(int fs);
|
||||
bool render_frame();
|
||||
|
||||
bool scanline_is_hires() { return (regs.bg_mode == 5 || regs.bg_mode == 6); }
|
||||
|
||||
|
||||
+201
-64
@@ -16,6 +16,55 @@ uint16 addr;
|
||||
return (addr << 1);
|
||||
}
|
||||
|
||||
bool bPPU::vram_can_read() {
|
||||
if(regs.display_disabled == true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16 v = cpu->vcounter();
|
||||
uint16 hc = cpu->hcycles();
|
||||
uint16 ls;
|
||||
if(cpu->interlace() && !cpu->interlace_field()) {
|
||||
ls = cpu->region_scanlines();
|
||||
} else {
|
||||
ls = cpu->region_scanlines() - 1;
|
||||
}
|
||||
|
||||
if(v == ls && hc == 1362)return false;
|
||||
|
||||
if(v < (cpu->overscan() ? 239 : 224))return false;
|
||||
|
||||
if(v == (cpu->overscan() ? 239 : 224)) {
|
||||
if(hc == 1362)return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bPPU::vram_can_write(uint8 &value) {
|
||||
if(regs.display_disabled == true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16 v = cpu->vcounter();
|
||||
uint16 hc = cpu->hcycles();
|
||||
if(v == 0) {
|
||||
if(hc <= 4)return true;
|
||||
if(hc == 6) { value = cpu->regs.mdr; return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
if(v < (cpu->overscan() ? 240 : 225))return false;
|
||||
|
||||
if(v == (cpu->overscan() ? 240 : 225)) {
|
||||
if(hc <= 4)return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//INIDISP
|
||||
void bPPU::mmio_w2100(uint8 value) {
|
||||
regs.display_disabled = !!(value & 0x80);
|
||||
@@ -68,6 +117,13 @@ void bPPU::mmio_w2105(uint8 value) {
|
||||
regs.bg_tilesize[BG1] = !!(value & 0x10);
|
||||
regs.bg3_priority = !!(value & 0x08);
|
||||
regs.bg_mode = (value & 7);
|
||||
|
||||
window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true;
|
||||
window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true;
|
||||
window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true;
|
||||
window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true;
|
||||
window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true;
|
||||
window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true;
|
||||
}
|
||||
|
||||
//MOSAIC
|
||||
@@ -185,40 +241,54 @@ void bPPU::mmio_w2115(uint8 value) {
|
||||
void bPPU::mmio_w2116(uint8 value) {
|
||||
regs.vram_addr = (regs.vram_addr & 0xff00) | value;
|
||||
uint16 addr = get_vram_address();
|
||||
regs.vram_readbuffer = vram_read(addr);
|
||||
regs.vram_readbuffer |= vram_read(addr + 1) << 8;
|
||||
if(vram_can_read()) {
|
||||
regs.vram_readbuffer = vram_read(addr);
|
||||
regs.vram_readbuffer |= vram_read(addr + 1) << 8;
|
||||
} else {
|
||||
regs.vram_readbuffer = 0x0000;
|
||||
}
|
||||
}
|
||||
|
||||
//VMADDH
|
||||
void bPPU::mmio_w2117(uint8 value) {
|
||||
regs.vram_addr = (value << 8) | (regs.vram_addr & 0x00ff);
|
||||
uint16 addr = get_vram_address();
|
||||
regs.vram_readbuffer = vram_read(addr);
|
||||
regs.vram_readbuffer |= vram_read(addr + 1) << 8;
|
||||
if(vram_can_read()) {
|
||||
regs.vram_readbuffer = vram_read(addr);
|
||||
regs.vram_readbuffer |= vram_read(addr + 1) << 8;
|
||||
} else {
|
||||
regs.vram_readbuffer = 0x0000;
|
||||
}
|
||||
}
|
||||
|
||||
//VMDATAL
|
||||
void bPPU::mmio_w2118(uint8 value) {
|
||||
uint16 addr = get_vram_address();
|
||||
vram_write(addr, value);
|
||||
if(vram_can_write(value)) {
|
||||
vram_write(addr, value);
|
||||
bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1;
|
||||
bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1;
|
||||
bg_tiledata_state[TILE_8BIT][(addr >> 6)] = 1;
|
||||
}
|
||||
|
||||
if(regs.vram_incmode == 0) {
|
||||
regs.vram_addr += regs.vram_incsize;
|
||||
}
|
||||
bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1;
|
||||
bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1;
|
||||
bg_tiledata_state[TILE_8BIT][(addr >> 6)] = 1;
|
||||
}
|
||||
|
||||
//VMDATAH
|
||||
void bPPU::mmio_w2119(uint8 value) {
|
||||
uint16 addr = get_vram_address() + 1;
|
||||
vram_write(addr, value);
|
||||
if(vram_can_write(value)) {
|
||||
vram_write(addr, value);
|
||||
bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1;
|
||||
bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1;
|
||||
bg_tiledata_state[TILE_8BIT][(addr >> 6)] = 1;
|
||||
}
|
||||
|
||||
if(regs.vram_incmode == 1) {
|
||||
regs.vram_addr += regs.vram_incsize;
|
||||
}
|
||||
bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1;
|
||||
bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1;
|
||||
bg_tiledata_state[TILE_8BIT][(addr >> 6)] = 1;
|
||||
}
|
||||
|
||||
//M7SEL
|
||||
@@ -284,72 +354,117 @@ void bPPU::mmio_w2122(uint8 value) {
|
||||
|
||||
//W12SEL
|
||||
void bPPU::mmio_w2123(uint8 value) {
|
||||
regs.bg_window2_enabled[BG2] = !!(value & 0x80);
|
||||
regs.bg_window2_invert [BG2] = !!(value & 0x40);
|
||||
regs.bg_window1_enabled[BG2] = !!(value & 0x20);
|
||||
regs.bg_window1_invert [BG2] = !!(value & 0x10);
|
||||
regs.bg_window2_enabled[BG1] = !!(value & 0x08);
|
||||
regs.bg_window2_invert [BG1] = !!(value & 0x04);
|
||||
regs.bg_window1_enabled[BG1] = !!(value & 0x02);
|
||||
regs.bg_window1_invert [BG1] = !!(value & 0x01);
|
||||
regs.window2_enabled[BG2] = !!(value & 0x80);
|
||||
regs.window2_invert [BG2] = !!(value & 0x40);
|
||||
regs.window1_enabled[BG2] = !!(value & 0x20);
|
||||
regs.window1_invert [BG2] = !!(value & 0x10);
|
||||
regs.window2_enabled[BG1] = !!(value & 0x08);
|
||||
regs.window2_invert [BG1] = !!(value & 0x04);
|
||||
regs.window1_enabled[BG1] = !!(value & 0x02);
|
||||
regs.window1_invert [BG1] = !!(value & 0x01);
|
||||
|
||||
window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true;
|
||||
window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true;
|
||||
}
|
||||
|
||||
//W34SEL
|
||||
void bPPU::mmio_w2124(uint8 value) {
|
||||
regs.bg_window2_enabled[BG4] = !!(value & 0x80);
|
||||
regs.bg_window2_invert [BG4] = !!(value & 0x40);
|
||||
regs.bg_window1_enabled[BG4] = !!(value & 0x20);
|
||||
regs.bg_window1_invert [BG4] = !!(value & 0x10);
|
||||
regs.bg_window2_enabled[BG3] = !!(value & 0x08);
|
||||
regs.bg_window2_invert [BG3] = !!(value & 0x04);
|
||||
regs.bg_window1_enabled[BG3] = !!(value & 0x02);
|
||||
regs.bg_window1_invert [BG3] = !!(value & 0x01);
|
||||
regs.window2_enabled[BG4] = !!(value & 0x80);
|
||||
regs.window2_invert [BG4] = !!(value & 0x40);
|
||||
regs.window1_enabled[BG4] = !!(value & 0x20);
|
||||
regs.window1_invert [BG4] = !!(value & 0x10);
|
||||
regs.window2_enabled[BG3] = !!(value & 0x08);
|
||||
regs.window2_invert [BG3] = !!(value & 0x04);
|
||||
regs.window1_enabled[BG3] = !!(value & 0x02);
|
||||
regs.window1_invert [BG3] = !!(value & 0x01);
|
||||
|
||||
window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true;
|
||||
window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true;
|
||||
}
|
||||
|
||||
//WOBJSEL
|
||||
void bPPU::mmio_w2125(uint8 value) {
|
||||
regs.color_window2_enabled = !!(value & 0x80);
|
||||
regs.color_window2_invert = !!(value & 0x40);
|
||||
regs.color_window1_enabled = !!(value & 0x20);
|
||||
regs.color_window1_invert = !!(value & 0x10);
|
||||
regs.bg_window2_enabled[OAM] = !!(value & 0x08);
|
||||
regs.bg_window2_invert [OAM] = !!(value & 0x04);
|
||||
regs.bg_window1_enabled[OAM] = !!(value & 0x02);
|
||||
regs.bg_window1_invert [OAM] = !!(value & 0x01);
|
||||
regs.window2_enabled[COL] = !!(value & 0x80);
|
||||
regs.window2_invert [COL] = !!(value & 0x40);
|
||||
regs.window1_enabled[COL] = !!(value & 0x20);
|
||||
regs.window1_invert [COL] = !!(value & 0x10);
|
||||
regs.window2_enabled[OAM] = !!(value & 0x08);
|
||||
regs.window2_invert [OAM] = !!(value & 0x04);
|
||||
regs.window1_enabled[OAM] = !!(value & 0x02);
|
||||
regs.window1_invert [OAM] = !!(value & 0x01);
|
||||
|
||||
window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true;
|
||||
window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true;
|
||||
}
|
||||
|
||||
//WH0
|
||||
void bPPU::mmio_w2126(uint8 value) {
|
||||
regs.window1_left = value;
|
||||
|
||||
window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true;
|
||||
window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true;
|
||||
window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true;
|
||||
window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true;
|
||||
window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true;
|
||||
window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true;
|
||||
}
|
||||
|
||||
//WH1
|
||||
void bPPU::mmio_w2127(uint8 value) {
|
||||
regs.window1_right = value;
|
||||
|
||||
window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true;
|
||||
window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true;
|
||||
window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true;
|
||||
window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true;
|
||||
window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true;
|
||||
window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true;
|
||||
}
|
||||
|
||||
//WH2
|
||||
void bPPU::mmio_w2128(uint8 value) {
|
||||
regs.window2_left = value;
|
||||
|
||||
window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true;
|
||||
window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true;
|
||||
window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true;
|
||||
window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true;
|
||||
window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true;
|
||||
window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true;
|
||||
}
|
||||
|
||||
//WH3
|
||||
void bPPU::mmio_w2129(uint8 value) {
|
||||
regs.window2_right = value;
|
||||
|
||||
window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true;
|
||||
window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true;
|
||||
window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true;
|
||||
window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true;
|
||||
window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true;
|
||||
window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true;
|
||||
}
|
||||
|
||||
//WBGLOG
|
||||
void bPPU::mmio_w212a(uint8 value) {
|
||||
regs.bg_window_mask[BG4] = (value >> 6) & 3;
|
||||
regs.bg_window_mask[BG3] = (value >> 4) & 3;
|
||||
regs.bg_window_mask[BG2] = (value >> 2) & 3;
|
||||
regs.bg_window_mask[BG1] = (value ) & 3;
|
||||
regs.window_mask[BG4] = (value >> 6) & 3;
|
||||
regs.window_mask[BG3] = (value >> 4) & 3;
|
||||
regs.window_mask[BG2] = (value >> 2) & 3;
|
||||
regs.window_mask[BG1] = (value ) & 3;
|
||||
|
||||
window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true;
|
||||
window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true;
|
||||
window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true;
|
||||
window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true;
|
||||
}
|
||||
|
||||
//WOBJLOG
|
||||
void bPPU::mmio_w212b(uint8 value) {
|
||||
regs.color_window_mask = (value >> 2) & 3;
|
||||
regs.bg_window_mask[OAM] = (value ) & 3;
|
||||
regs.window_mask[COL] = (value >> 2) & 3;
|
||||
regs.window_mask[OAM] = (value ) & 3;
|
||||
|
||||
window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true;
|
||||
window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true;
|
||||
}
|
||||
|
||||
//TM
|
||||
@@ -372,20 +487,32 @@ void bPPU::mmio_w212d(uint8 value) {
|
||||
|
||||
//TMW
|
||||
void bPPU::mmio_w212e(uint8 value) {
|
||||
regs.bg_window_enabled[OAM] = !!(value & 0x10);
|
||||
regs.bg_window_enabled[BG4] = !!(value & 0x08);
|
||||
regs.bg_window_enabled[BG3] = !!(value & 0x04);
|
||||
regs.bg_window_enabled[BG2] = !!(value & 0x02);
|
||||
regs.bg_window_enabled[BG1] = !!(value & 0x01);
|
||||
regs.window_enabled[OAM] = !!(value & 0x10);
|
||||
regs.window_enabled[BG4] = !!(value & 0x08);
|
||||
regs.window_enabled[BG3] = !!(value & 0x04);
|
||||
regs.window_enabled[BG2] = !!(value & 0x02);
|
||||
regs.window_enabled[BG1] = !!(value & 0x01);
|
||||
|
||||
window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true;
|
||||
window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true;
|
||||
window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true;
|
||||
window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true;
|
||||
window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true;
|
||||
}
|
||||
|
||||
//TSW
|
||||
void bPPU::mmio_w212f(uint8 value) {
|
||||
regs.bgsub_window_enabled[OAM] = !!(value & 0x10);
|
||||
regs.bgsub_window_enabled[BG4] = !!(value & 0x08);
|
||||
regs.bgsub_window_enabled[BG3] = !!(value & 0x04);
|
||||
regs.bgsub_window_enabled[BG2] = !!(value & 0x02);
|
||||
regs.bgsub_window_enabled[BG1] = !!(value & 0x01);
|
||||
regs.sub_window_enabled[OAM] = !!(value & 0x10);
|
||||
regs.sub_window_enabled[BG4] = !!(value & 0x08);
|
||||
regs.sub_window_enabled[BG3] = !!(value & 0x04);
|
||||
regs.sub_window_enabled[BG2] = !!(value & 0x02);
|
||||
regs.sub_window_enabled[BG1] = !!(value & 0x01);
|
||||
|
||||
window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true;
|
||||
window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true;
|
||||
window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true;
|
||||
window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true;
|
||||
window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true;
|
||||
}
|
||||
|
||||
//CGWSEL
|
||||
@@ -394,18 +521,20 @@ void bPPU::mmio_w2130(uint8 value) {
|
||||
regs.colorsub_mask = (value >> 4) & 3;
|
||||
regs.addsub_mode = !!(value & 0x02);
|
||||
regs.direct_color = !!(value & 0x01);
|
||||
|
||||
window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true;
|
||||
}
|
||||
|
||||
//CGADDSUB
|
||||
void bPPU::mmio_w2131(uint8 value) {
|
||||
regs.color_mode = !!(value & 0x80);
|
||||
regs.color_halve = !!(value & 0x40);
|
||||
regs.bg_color_enabled[BACK] = !!(value & 0x20);
|
||||
regs.bg_color_enabled[OAM] = !!(value & 0x10);
|
||||
regs.bg_color_enabled[BG4] = !!(value & 0x08);
|
||||
regs.bg_color_enabled[BG3] = !!(value & 0x04);
|
||||
regs.bg_color_enabled[BG2] = !!(value & 0x02);
|
||||
regs.bg_color_enabled[BG1] = !!(value & 0x01);
|
||||
regs.color_mode = !!(value & 0x80);
|
||||
regs.color_halve = !!(value & 0x40);
|
||||
regs.color_enabled[BACK] = !!(value & 0x20);
|
||||
regs.color_enabled[OAM] = !!(value & 0x10);
|
||||
regs.color_enabled[BG4] = !!(value & 0x08);
|
||||
regs.color_enabled[BG3] = !!(value & 0x04);
|
||||
regs.color_enabled[BG2] = !!(value & 0x02);
|
||||
regs.color_enabled[BG1] = !!(value & 0x01);
|
||||
}
|
||||
|
||||
//COLDATA
|
||||
@@ -476,8 +605,12 @@ uint16 addr = get_vram_address();
|
||||
regs.ppu1_mdr = regs.vram_readbuffer;
|
||||
if(regs.vram_incmode == 0) {
|
||||
addr &= 0xfffe;
|
||||
regs.vram_readbuffer = vram_read(addr);
|
||||
regs.vram_readbuffer |= vram_read(addr + 1) << 8;
|
||||
if(vram_can_read()) {
|
||||
regs.vram_readbuffer = vram_read(addr);
|
||||
regs.vram_readbuffer |= vram_read(addr + 1) << 8;
|
||||
} else {
|
||||
regs.vram_readbuffer = 0x0000;
|
||||
}
|
||||
regs.vram_addr += regs.vram_incsize;
|
||||
}
|
||||
return regs.ppu1_mdr;
|
||||
@@ -489,8 +622,12 @@ uint16 addr = get_vram_address() + 1;
|
||||
regs.ppu1_mdr = regs.vram_readbuffer >> 8;
|
||||
if(regs.vram_incmode == 1) {
|
||||
addr &= 0xfffe;
|
||||
regs.vram_readbuffer = vram_read(addr);
|
||||
regs.vram_readbuffer |= vram_read(addr + 1) << 8;
|
||||
if(vram_can_read()) {
|
||||
regs.vram_readbuffer = vram_read(addr);
|
||||
regs.vram_readbuffer |= vram_read(addr + 1) << 8;
|
||||
} else {
|
||||
regs.vram_readbuffer = 0x0000;
|
||||
}
|
||||
regs.vram_addr += regs.vram_incsize;
|
||||
}
|
||||
return regs.ppu1_mdr;
|
||||
|
||||
@@ -126,7 +126,7 @@ void bPPU::render_line() {
|
||||
}
|
||||
|
||||
clear_pixel_cache();
|
||||
build_color_window_tables();
|
||||
build_window_tables(COL);
|
||||
|
||||
switch(regs.bg_mode) {
|
||||
case 0:render_line_mode0();break;
|
||||
|
||||
@@ -35,17 +35,18 @@ uint8 *bg_tiledata_state[3];
|
||||
|
||||
void render_bg_tile(uint8 color_depth, uint16 tile_num);
|
||||
inline void clear_pixel_cache();
|
||||
void init_tiledata_cache();
|
||||
void clear_tiledata_cache();
|
||||
inline void init_tiledata_cache();
|
||||
inline void clear_tiledata_cache();
|
||||
|
||||
//bppu_render_windows.cpp
|
||||
uint8 main_windowtable[5][512], sub_windowtable[5][512],
|
||||
main_colorwindowtable[512], sub_colorwindowtable[512];
|
||||
struct _window {
|
||||
bool main_dirty, sub_dirty;
|
||||
uint8 main[512], sub[512];
|
||||
} window_cache[6];
|
||||
|
||||
void build_window_table(uint8 bg, uint8 *wtbl, bool mainscreen);
|
||||
void build_window_table(uint8 bg, bool mainscreen);
|
||||
void build_window_tables(uint8 bg);
|
||||
void build_color_window_table(uint8 *wtbl, uint8 mask);
|
||||
void build_color_window_tables();
|
||||
inline void clear_window_cache();
|
||||
|
||||
//bppu_render_bg.cpp
|
||||
void render_line_bg(uint8 bg, uint8 color_depth, uint8 pri0_pos, uint8 pri1_pos);
|
||||
|
||||
@@ -118,8 +118,8 @@ int xpos, ypos;
|
||||
uint16 map_index, hoffset, voffset, col;
|
||||
|
||||
build_window_tables(bg);
|
||||
uint8 *wt_main = main_windowtable[bg];
|
||||
uint8 *wt_sub = sub_windowtable[bg];
|
||||
uint8 *wt_main = window_cache[bg].main;
|
||||
uint8 *wt_sub = window_cache[bg].sub;
|
||||
|
||||
screen_x = 0;
|
||||
do { //for(screen_x=0;screen_x<_screen_width;screen_x++) {
|
||||
@@ -141,7 +141,7 @@ uint8 *wt_sub = sub_windowtable[bg];
|
||||
|
||||
if(regs.bg_mode == 4) {
|
||||
pos = regs.bg_scaddr[BG3] + tile_x;
|
||||
t = *((uint16*)vram + (pos >> 1));
|
||||
t = read16(vram, pos);
|
||||
if(t & opt_valid_bit) {
|
||||
if(!(t & 0x8000)) {
|
||||
hoffset = ((t & 0x1ff8) | (hscroll & 7)) & screen_width_mask;
|
||||
@@ -151,12 +151,12 @@ uint8 *wt_sub = sub_windowtable[bg];
|
||||
}
|
||||
} else {
|
||||
pos = regs.bg_scaddr[BG3] + tile_x;
|
||||
t = *((uint16*)vram + (pos >> 1));
|
||||
t = read16(vram, pos);
|
||||
if(t & opt_valid_bit) {
|
||||
hoffset = ((t & 0x1ff8) | (hscroll & 7)) & screen_width_mask;
|
||||
}
|
||||
pos = regs.bg_scaddr[BG3] + 64 + tile_x;
|
||||
t = *((uint16*)vram + (pos >> 1));
|
||||
t = read16(vram, pos);
|
||||
if(t & opt_valid_bit) {
|
||||
voffset = (t & 0x1fff) & screen_height_mask;
|
||||
}
|
||||
@@ -186,7 +186,7 @@ uint8 *wt_sub = sub_windowtable[bg];
|
||||
base_xpos = ((mosaic_x >> 3) & 31);
|
||||
base_pos = (((mosaic_y >> tile_height) & 31) << 5) + ((mosaic_x >> tile_width) & 31);
|
||||
pos = _scaddr + map_index + (base_pos << 1);
|
||||
t = *((uint16*)vram + (pos >> 1));
|
||||
t = read16(vram, pos);
|
||||
mirror_y = !!(t & 0x8000);
|
||||
mirror_x = !!(t & 0x4000);
|
||||
|
||||
@@ -228,7 +228,7 @@ int _pri;
|
||||
if(mirror_x) { xpos = (7 - (mosaic_x & 7)); }
|
||||
else { xpos = ( (mosaic_x & 7)); }
|
||||
col = *(tile_ptr + xpos);
|
||||
if(col && main_colorwindowtable[screen_x]) {
|
||||
if(col && window_cache[COL].main[screen_x]) {
|
||||
if(regs.direct_color == true && bg == BG1 && (regs.bg_mode == 3 || regs.bg_mode == 4)) {
|
||||
col = get_direct_color(pal_num, col);
|
||||
} else {
|
||||
|
||||
@@ -117,3 +117,17 @@ void bPPU::clear_tiledata_cache() {
|
||||
memset(bg_tiledata_state[TILE_4BIT], 0, 2048);
|
||||
memset(bg_tiledata_state[TILE_8BIT], 0, 1024);
|
||||
}
|
||||
|
||||
void bPPU::clear_window_cache() {
|
||||
for(int i=0;i<6;i++) {
|
||||
window_cache[i].main_dirty = true;
|
||||
window_cache[i].sub_dirty = true;
|
||||
}
|
||||
|
||||
build_window_tables(BG1);
|
||||
build_window_tables(BG2);
|
||||
build_window_tables(BG3);
|
||||
build_window_tables(BG4);
|
||||
build_window_tables(OAM);
|
||||
build_window_tables(COL);
|
||||
}
|
||||
@@ -1,21 +1,21 @@
|
||||
inline uint16 bPPU::get_palette(uint8 index) {
|
||||
return *((uint16*)cgram + index);
|
||||
return read16(cgram, index << 1);
|
||||
}
|
||||
|
||||
inline uint16 bPPU::get_direct_color(uint8 p, uint8 t) {
|
||||
//p = 00000bgr
|
||||
//t = BBGGGRRR
|
||||
//r = 0BBb00GGGg0RRRr0
|
||||
//p = 00000bgr <palette data>
|
||||
//t = BBGGGRRR <tilemap data>
|
||||
//r = 0BBb00GGGg0RRRr0 <return data>
|
||||
return ((t & 7) << 2) | ((p & 1) << 1) |
|
||||
(((t >> 3) & 7) << 7) | (((p >> 1) & 1) << 6) |
|
||||
((t >> 6) << 13) | ((p >> 2) << 12);
|
||||
(((t >> 3) & 7) << 7) | (((p >> 1) & 1) << 6) |
|
||||
((t >> 6) << 13) | ((p >> 2) << 12);
|
||||
}
|
||||
|
||||
inline uint16 bPPU::get_pixel(int x) {
|
||||
_pixel *p = &pixel_cache[x];
|
||||
uint16 _r, src_back = get_palette(0);
|
||||
if(p->bg_main && p->bg_sub) {
|
||||
if(p->color_exempt == false && regs.bg_color_enabled[p->bg_main & 0x7f] && sub_colorwindowtable[x]) {
|
||||
if(p->color_exempt == false && regs.color_enabled[p->bg_main & 0x7f] && window_cache[COL].sub[x]) {
|
||||
if(regs.addsub_mode) {
|
||||
_r = addsub_pixels(p->src_main, p->src_sub);
|
||||
} else {
|
||||
@@ -25,14 +25,14 @@ uint16 _r, src_back = get_palette(0);
|
||||
_r = p->src_main;
|
||||
}
|
||||
} else if(p->bg_main) {
|
||||
if(p->color_exempt == false && regs.bg_color_enabled[p->bg_main & 0x7f] && sub_colorwindowtable[x]) {
|
||||
if(p->color_exempt == false && regs.color_enabled[p->bg_main & 0x7f] && window_cache[COL].sub[x]) {
|
||||
_r = addsub_pixel(p->src_main);
|
||||
} else {
|
||||
_r = p->src_main;
|
||||
}
|
||||
} else if(p->bg_sub) {
|
||||
if(regs.bg_color_enabled[BACK]) {
|
||||
if(sub_colorwindowtable[x]) {
|
||||
if(regs.color_enabled[BACK]) {
|
||||
if(window_cache[COL].sub[x]) {
|
||||
if(regs.addsub_mode) {
|
||||
_r = addsub_pixels(src_back, p->src_sub);
|
||||
} else {
|
||||
@@ -42,11 +42,11 @@ uint16 _r, src_back = get_palette(0);
|
||||
_r = src_back;
|
||||
}
|
||||
} else {
|
||||
_r = 0x0000;
|
||||
_r = src_back; //was 0x0000 -- possibly another condition here?
|
||||
}
|
||||
} else {
|
||||
if(main_colorwindowtable[x]) {
|
||||
if(regs.bg_color_enabled[BACK] && sub_colorwindowtable[x]) {
|
||||
if(window_cache[COL].main[x]) {
|
||||
if(regs.color_enabled[BACK] && window_cache[COL].sub[x]) {
|
||||
_r = addsub_pixel(src_back);
|
||||
} else {
|
||||
_r = src_back;
|
||||
|
||||
@@ -31,15 +31,16 @@ int32 tx, ty, tile, palette;
|
||||
cx = (int32(regs.m7x) << 19) >> 19;
|
||||
cy = (int32(regs.m7y) << 19) >> 19;
|
||||
hofs = (int32(regs.m7_hofs) << 19) >> 19;
|
||||
vofs = (int32(regs.m7_vofs + 1) << 19) >> 19;
|
||||
//+1 breaks FF5 title screen mirror alignment...
|
||||
vofs = (int32(regs.m7_vofs + 0) << 19) >> 19;
|
||||
|
||||
int _pri, _x;
|
||||
bool _bg_enabled = regs.bg_enabled[bg];
|
||||
bool _bgsub_enabled = regs.bgsub_enabled[bg];
|
||||
|
||||
build_window_tables(bg);
|
||||
uint8 *wt_main = main_windowtable[bg];
|
||||
uint8 *wt_sub = sub_windowtable[bg];
|
||||
uint8 *wt_main = window_cache[bg].main;
|
||||
uint8 *wt_sub = window_cache[bg].sub;
|
||||
|
||||
if(regs.mode7_vflip == true) {
|
||||
y = 255 - _y;
|
||||
@@ -119,7 +120,7 @@ int32 psy = ((c * CLIP(hofs - cx)) & ~63) + ((d * CLIP(vofs - cy)) & ~63) + ((d
|
||||
_x = x;
|
||||
}
|
||||
|
||||
if(main_colorwindowtable[_x]) {
|
||||
if(window_cache[COL].main[_x]) {
|
||||
uint32 col;
|
||||
if(regs.direct_color == true && bg == BG1) {
|
||||
//direct color mode does not apply to bg2, as it is only 128 colors...
|
||||
|
||||
@@ -207,9 +207,9 @@ int s, x;
|
||||
bool _bg_enabled = regs.bg_enabled[OAM];
|
||||
bool _bgsub_enabled = regs.bgsub_enabled[OAM];
|
||||
|
||||
uint8 *wt_main = main_windowtable[OAM];
|
||||
uint8 *wt_sub = sub_windowtable[OAM];
|
||||
build_window_tables(OAM);
|
||||
uint8 *wt_main = window_cache[OAM].main;
|
||||
uint8 *wt_sub = window_cache[OAM].sub;
|
||||
|
||||
regs.oam_itemcount = 0;
|
||||
regs.oam_tilecount = 0;
|
||||
@@ -240,6 +240,7 @@ uint8 *wt_sub = sub_windowtable[OAM];
|
||||
regs.range_over |= (regs.oam_itemcount > 32);
|
||||
|
||||
if(_bg_enabled == false && _bgsub_enabled == false)return;
|
||||
|
||||
int _pri;
|
||||
for(x=0;x<_screen_width;x++) {
|
||||
if(oam_line_pri[x] == OAM_PRI_NONE)continue;
|
||||
@@ -251,7 +252,7 @@ int _pri;
|
||||
case 3:_pri = pri3_pos;break;
|
||||
}
|
||||
|
||||
if(main_colorwindowtable[x]) {
|
||||
if(window_cache[COL].main[x]) {
|
||||
if(_bg_enabled == true && !wt_main[x]) {
|
||||
if(pixel_cache[x].pri_main < _pri) {
|
||||
pixel_cache[x].pri_main = _pri;
|
||||
|
||||
@@ -1,11 +1,50 @@
|
||||
void bPPU::build_window_table(uint8 bg, uint8 *wtbl, bool mainscreen) {
|
||||
if(mainscreen == true && regs.bg_window_enabled[bg] == false) {
|
||||
memset(wtbl, 0, _screen_width);
|
||||
return;
|
||||
void bPPU::build_window_table(uint8 bg, bool mainscreen) {
|
||||
uint8 set = true, clr = false;
|
||||
uint8 *wtbl;
|
||||
if(mainscreen == true) {
|
||||
wtbl = (uint8*)window_cache[bg].main;
|
||||
} else {
|
||||
wtbl = (uint8*)window_cache[bg].sub;
|
||||
}
|
||||
if(mainscreen == false && regs.bgsub_window_enabled[bg] == false) {
|
||||
memset(wtbl, 0, _screen_width);
|
||||
return;
|
||||
|
||||
if(bg != COL) {
|
||||
if(mainscreen == true && regs.window_enabled[bg] == false) {
|
||||
memset(wtbl, 0, _screen_width);
|
||||
return;
|
||||
}
|
||||
if(mainscreen == false && regs.sub_window_enabled[bg] == false) {
|
||||
memset(wtbl, 0, _screen_width);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
uint8 mask;
|
||||
if(mainscreen == true) {
|
||||
mask = regs.color_mask;
|
||||
} else {
|
||||
mask = regs.colorsub_mask;
|
||||
}
|
||||
|
||||
if(mask == 0) {
|
||||
//always
|
||||
memset(wtbl, 1, _screen_width);
|
||||
return;
|
||||
}
|
||||
|
||||
if(mask == 3) {
|
||||
//never
|
||||
memset(wtbl, 0, _screen_width);
|
||||
return;
|
||||
}
|
||||
|
||||
if(mask == 1) {
|
||||
//inside window only
|
||||
set = 1;
|
||||
clr = 0;
|
||||
} else { //mask == 2
|
||||
//outside window only
|
||||
set = 0;
|
||||
clr = 1;
|
||||
}
|
||||
}
|
||||
|
||||
uint16 window1_left, window1_right, window2_left, window2_right;
|
||||
@@ -24,54 +63,54 @@ bool r;
|
||||
window2_right <<= 1;
|
||||
}
|
||||
|
||||
if(regs.bg_window1_enabled[bg] == false && regs.bg_window2_enabled[bg] == false) {
|
||||
memset(wtbl, 0, _screen_width);
|
||||
} else if(regs.bg_window1_enabled[bg] == true && regs.bg_window2_enabled[bg] == false) {
|
||||
if(regs.bg_window1_invert[bg] == false) {
|
||||
if(regs.window1_enabled[bg] == false && regs.window2_enabled[bg] == false) {
|
||||
memset(wtbl, clr, _screen_width);
|
||||
} else if(regs.window1_enabled[bg] == true && regs.window2_enabled[bg] == false) {
|
||||
if(regs.window1_invert[bg] == false) {
|
||||
for(x=0;x<_screen_width;x++) {
|
||||
wtbl[x] = (x >= window1_left && x <= window1_right)?true:false;
|
||||
wtbl[x] = (x >= window1_left && x <= window1_right) ? set : clr;
|
||||
}
|
||||
} else {
|
||||
for(x=0;x<_screen_width;x++) {
|
||||
wtbl[x] = (x < window1_left || x > window1_right)?true:false;
|
||||
wtbl[x] = (x < window1_left || x > window1_right) ? set : clr;
|
||||
}
|
||||
}
|
||||
} else if(regs.bg_window1_enabled[bg] == false && regs.bg_window2_enabled[bg] == true) {
|
||||
if(regs.bg_window2_invert[bg] == false) {
|
||||
} else if(regs.window1_enabled[bg] == false && regs.window2_enabled[bg] == true) {
|
||||
if(regs.window2_invert[bg] == false) {
|
||||
for(x=0;x<_screen_width;x++) {
|
||||
wtbl[x] = (x >= window2_left && x <= window2_right)?true:false;
|
||||
wtbl[x] = (x >= window2_left && x <= window2_right) ? set : clr;
|
||||
}
|
||||
} else {
|
||||
for(x=0;x<_screen_width;x++) {
|
||||
wtbl[x] = (x < window2_left || x > window2_right)?true:false;
|
||||
wtbl[x] = (x < window2_left || x > window2_right) ? set : clr;
|
||||
}
|
||||
}
|
||||
} else { //if(regs.bg_window1_enabled[bg] == true && regs.bg_window2_enabled[bg] == true) {
|
||||
} else { //if(regs.window1_enabled[bg] == true && regs.window2_enabled[bg] == true) {
|
||||
for(x=0;x<_screen_width;x++) {
|
||||
if(regs.bg_window1_invert[bg] == false) {
|
||||
if(regs.window1_invert[bg] == false) {
|
||||
w1_mask = (x >= window1_left && x <= window1_right);
|
||||
} else {
|
||||
w1_mask = (x < window1_left || x > window1_right);
|
||||
}
|
||||
|
||||
if(regs.bg_window2_invert[bg] == false) {
|
||||
if(regs.window2_invert[bg] == false) {
|
||||
w2_mask = (x >= window2_left && x <= window2_right);
|
||||
} else {
|
||||
w2_mask = (x < window2_left || x > window2_right);
|
||||
}
|
||||
|
||||
switch(regs.bg_window_mask[bg]) {
|
||||
switch(regs.window_mask[bg]) {
|
||||
case 0: //WINDOWMASK_OR:
|
||||
wtbl[x] = ((w1_mask | w2_mask) == 1)?true:false;
|
||||
wtbl[x] = ((w1_mask | w2_mask) == 1) ? set : clr;
|
||||
break;
|
||||
case 1: //WINDOWMASK_AND:
|
||||
wtbl[x] = ((w1_mask & w2_mask) == 1)?true:false;
|
||||
wtbl[x] = ((w1_mask & w2_mask) == 1) ? set : clr;
|
||||
break;
|
||||
case 2: //WINDOWMASK_XOR:
|
||||
wtbl[x] = ((w1_mask ^ w2_mask) == 1)?true:false;
|
||||
wtbl[x] = ((w1_mask ^ w2_mask) == 1) ? set : clr;
|
||||
break;
|
||||
case 3: //WINDOWMASK_XNOR:
|
||||
wtbl[x] = ((w1_mask ^ w2_mask) == 0)?true:false;
|
||||
wtbl[x] = ((w1_mask ^ w2_mask) == 0) ? set : clr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -79,105 +118,13 @@ bool r;
|
||||
}
|
||||
|
||||
void bPPU::build_window_tables(uint8 bg) {
|
||||
build_window_table(bg, main_windowtable[bg], true);
|
||||
build_window_table(bg, sub_windowtable[bg], false);
|
||||
}
|
||||
|
||||
void bPPU::build_color_window_table(uint8 *wtbl, uint8 mask) {
|
||||
if(mask == 0) {
|
||||
//always
|
||||
memset(wtbl, 1, _screen_width);
|
||||
return;
|
||||
if(window_cache[bg].main_dirty == true) {
|
||||
window_cache[bg].main_dirty = false;
|
||||
build_window_table(bg, true);
|
||||
}
|
||||
|
||||
if(mask == 3) {
|
||||
//never
|
||||
memset(wtbl, 0, _screen_width);
|
||||
return;
|
||||
}
|
||||
|
||||
int _true, _false;
|
||||
if(mask == 1) {
|
||||
//inside window only
|
||||
_true = 1;
|
||||
_false = 0;
|
||||
} else { //mask == 2
|
||||
//outside window only
|
||||
_true = 0;
|
||||
_false = 1;
|
||||
}
|
||||
|
||||
uint16 window1_left, window1_right, window2_left, window2_right;
|
||||
int w1_mask, w2_mask; //1 = masked, 0 = not masked
|
||||
int x;
|
||||
bool r;
|
||||
window1_left = regs.window1_left;
|
||||
window1_right = regs.window1_right;
|
||||
window2_left = regs.window2_left;
|
||||
window2_right = regs.window2_right;
|
||||
|
||||
if(_screen_width == 512) {
|
||||
window1_left <<= 1;
|
||||
window1_right <<= 1;
|
||||
window2_left <<= 1;
|
||||
window2_right <<= 1;
|
||||
}
|
||||
|
||||
if(regs.color_window1_enabled == false && regs.color_window2_enabled == false) {
|
||||
memset(wtbl, _false, _screen_width);
|
||||
} else if(regs.color_window1_enabled == true && regs.color_window2_enabled == false) {
|
||||
if(regs.color_window1_invert == false) {
|
||||
for(x=0;x<_screen_width;x++) {
|
||||
wtbl[x] = (x >= window1_left && x <= window1_right)?_true:_false;
|
||||
}
|
||||
} else {
|
||||
for(x=0;x<_screen_width;x++) {
|
||||
wtbl[x] = (x < window1_left || x > window1_right)?_true:_false;
|
||||
}
|
||||
}
|
||||
} else if(regs.color_window1_enabled == false && regs.color_window2_enabled == true) {
|
||||
if(regs.color_window2_invert == false) {
|
||||
for(x=0;x<_screen_width;x++) {
|
||||
wtbl[x] = (x >= window2_left && x <= window2_right)?_true:_false;
|
||||
}
|
||||
} else {
|
||||
for(x=0;x<_screen_width;x++) {
|
||||
wtbl[x] = (x < window2_left || x > window2_right)?_true:_false;
|
||||
}
|
||||
}
|
||||
} else { //if(regs.color_window1_enabled == true && regs.color_window2_enabled == true) {
|
||||
for(x=0;x<_screen_width;x++) {
|
||||
if(regs.color_window1_invert == false) {
|
||||
w1_mask = (x >= window1_left && x <= window1_right);
|
||||
} else {
|
||||
w1_mask = (x < window1_left || x > window1_right);
|
||||
}
|
||||
|
||||
if(regs.color_window2_invert == false) {
|
||||
w2_mask = (x >= window2_left && x <= window2_right);
|
||||
} else {
|
||||
w2_mask = (x < window2_left || x > window2_right);
|
||||
}
|
||||
|
||||
switch(regs.color_window_mask) {
|
||||
case 0: //WINDOWMASK_OR:
|
||||
wtbl[x] = ((w1_mask | w2_mask) == 1)?_true:_false;
|
||||
break;
|
||||
case 1: //WINDOWMASK_AND:
|
||||
wtbl[x] = ((w1_mask & w2_mask) == 1)?_true:_false;
|
||||
break;
|
||||
case 2: //WINDOWMASK_XOR:
|
||||
wtbl[x] = ((w1_mask ^ w2_mask) == 1)?_true:_false;
|
||||
break;
|
||||
case 3: //WINDOWMASK_XNOR:
|
||||
wtbl[x] = ((w1_mask ^ w2_mask) == 0)?_true:_false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(window_cache[bg].sub_dirty == true) {
|
||||
window_cache[bg].sub_dirty = false;
|
||||
build_window_table(bg, false);
|
||||
}
|
||||
}
|
||||
|
||||
void bPPU::build_color_window_tables() {
|
||||
build_color_window_table(main_colorwindowtable, regs.color_mask);
|
||||
build_color_window_table(sub_colorwindowtable, regs.colorsub_mask);
|
||||
}
|
||||
@@ -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
|
||||
+24
-3
@@ -5,12 +5,33 @@ void PPU::get_scanline_info(scanline_info *info) {
|
||||
info->interlace = cpu->interlace();
|
||||
}
|
||||
|
||||
void PPU::set_frameskip(int fs) {}
|
||||
bool PPU::render_frame() { return true; }
|
||||
void PPU::enable_renderer(bool r) { status.render_output = r; }
|
||||
bool PPU::renderer_enabled() { return status.render_output; }
|
||||
|
||||
void PPU::frame() {
|
||||
static fr = 0, fe = 0;
|
||||
static time_t prev, curr;
|
||||
fe++;
|
||||
if(status.render_output)fr++;
|
||||
|
||||
time(&curr);
|
||||
if(curr != prev) {
|
||||
status.frames_updated = true;
|
||||
status.frames_rendered = fr;
|
||||
status.frames_executed = fe;
|
||||
fr = fe = 0;
|
||||
}
|
||||
prev = curr;
|
||||
}
|
||||
|
||||
PPU::PPU() {
|
||||
status.render_output = true;
|
||||
status.frames_updated = false;
|
||||
status.frames_rendered = 0;
|
||||
status.frames_executed = 0;
|
||||
|
||||
ppu1_version = 1;
|
||||
ppu2_version = 3;
|
||||
ppu2_version = 1;
|
||||
mmio = &mmio_unmapped;
|
||||
}
|
||||
|
||||
|
||||
+14
-3
@@ -1,5 +1,16 @@
|
||||
class PPU {
|
||||
public:
|
||||
|
||||
//this struct should be read-only to
|
||||
//functions outside of this class
|
||||
struct {
|
||||
bool render_output;
|
||||
|
||||
bool frames_updated;
|
||||
uint32 frames_rendered;
|
||||
uint32 frames_executed;
|
||||
} status;
|
||||
|
||||
//PPU1 version number
|
||||
//* 1 is known
|
||||
//* reported by $213e
|
||||
@@ -32,11 +43,11 @@ struct scanline_info {
|
||||
virtual void run() = 0;
|
||||
virtual void scanline() = 0;
|
||||
virtual void render_scanline() = 0;
|
||||
virtual void frame() = 0;
|
||||
virtual void frame();
|
||||
virtual void power() = 0;
|
||||
virtual void reset() = 0;
|
||||
virtual void set_frameskip(int fs);
|
||||
virtual bool render_frame();
|
||||
virtual void enable_renderer(bool r);
|
||||
virtual bool renderer_enabled();
|
||||
|
||||
PPU();
|
||||
~PPU();
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
CC = c++
|
||||
CFLAGS = -O2
|
||||
CFLAGS = -O3 -fomit-frame-pointer -ffast-math
|
||||
OBJS = sdlmain.o \
|
||||
libstring.o libconfig.o \
|
||||
reader.o \
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
CC = cl
|
||||
CFLAGS = /nologo /O2 /Ogityb2 /Gr /Gs
|
||||
CFLAGS = /nologo /O2 /Ogityb2 /Gr /Gs /DARCH_LSB
|
||||
OBJS = sdlmain.obj \
|
||||
libstring.obj libconfig.obj \
|
||||
reader.obj \
|
||||
|
||||
+19
-3
@@ -17,17 +17,33 @@ void bSNES::run() {
|
||||
}
|
||||
}
|
||||
|
||||
void bSNES::video_run() { render(); }
|
||||
void bSNES::video_run() {
|
||||
if(ppu->status.frames_updated) {
|
||||
char s[512], t[512];
|
||||
ppu->status.frames_updated = false;
|
||||
// if((bool)config::gui.show_fps == true) {
|
||||
sprintf(s, "%s : %d fps", BSNES_TITLE, ppu->status.frames_executed);
|
||||
// if(w_main->frameskip != 0) {
|
||||
// sprintf(t, " (%d frames)", ppu->status.frames_rendered);
|
||||
// strcat(s, t);
|
||||
// }
|
||||
SDL_WM_SetCaption(s, 0);
|
||||
// }
|
||||
}
|
||||
|
||||
render();
|
||||
}
|
||||
|
||||
void bSNES::sound_run() {}
|
||||
|
||||
/***********************
|
||||
*** Input functions ***
|
||||
***********************/
|
||||
|
||||
//It would appear that keystate does not need to be free'd
|
||||
//It would appear that keystate does not need to be released
|
||||
//after calling SDL_GetKeyState... doing so causes libSDL
|
||||
//to throw error messages about a bad free call to stdout...
|
||||
void bSNES::poll_input() {
|
||||
void bSNES::poll_input(uint8 type) {
|
||||
uint8 *keystate = SDL_GetKeyState(0);
|
||||
joypad1.up = keystate[(int)config::input.joypad1.up];
|
||||
joypad1.down = keystate[(int)config::input.joypad1.down];
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ enum { STOP = 0, RUN };
|
||||
uint32 get_status();
|
||||
|
||||
//input functions
|
||||
void poll_input();
|
||||
void poll_input(uint8 type);
|
||||
bool get_input_status(uint8 device, uint8 button);
|
||||
|
||||
void notify(uint32 message, uint32 param1 = 0, uint32 param2 = 0);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -55,7 +55,6 @@ int i;
|
||||
for(i=0x2140;i<=0x217f;i++)mem_bus->set_mmio_mapper(i, cpu->mmio);
|
||||
for(i=0x2180;i<=0x2183;i++)mem_bus->set_mmio_mapper(i, cpu->mmio);
|
||||
//input
|
||||
mem_bus->set_mmio_mapper(0x4000, cpu->mmio); //test -- remove this
|
||||
mem_bus->set_mmio_mapper(0x4016, cpu->mmio);
|
||||
mem_bus->set_mmio_mapper(0x4017, cpu->mmio);
|
||||
for(i=0x4200;i<=0x421f;i++)mem_bus->set_mmio_mapper(i, cpu->mmio);
|
||||
|
||||
@@ -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.
|
||||
|
||||
+76
-118
@@ -10,108 +10,66 @@ const uint8 SNES::color_curve_table[32] = {
|
||||
void SNES::update_color_lookup_table() {
|
||||
int i, l, r, g, b;
|
||||
double lr = 0.2126, lg = 0.7152, lb = 0.0722; //luminance
|
||||
switch(video.depth) {
|
||||
case 15: //rgb565
|
||||
for(i=0;i<32768;i++) {
|
||||
r = (i ) & 31;
|
||||
g = (i >> 5) & 31;
|
||||
b = (i >> 10) & 31;
|
||||
uint32 col;
|
||||
|
||||
if((bool)config::snes.video_color_curve == true) {
|
||||
r = color_curve_table[r] >> 3;
|
||||
g = color_curve_table[g] >> 3;
|
||||
b = color_curve_table[b] >> 3;
|
||||
}
|
||||
for(i=0;i<32768;i++) {
|
||||
//bgr555->rgb888
|
||||
col = ((i & 0x001f) << 19) | ((i & 0x001c) << 14) |
|
||||
((i & 0x03e0) << 6) | ((i & 0x0380) << 1) |
|
||||
((i & 0x7c00) >> 7) | ((i & 0x7000) >> 12);
|
||||
|
||||
if((int)config::snes.video_color_adjust_mode == VCA_GRAYSCALE) {
|
||||
r = (r << 3) | (r >> 2);
|
||||
g = (g << 3) | (g >> 2);
|
||||
b = (b << 3) | (b >> 2);
|
||||
r = (col >> 16) & 0xff;
|
||||
g = (col >> 8) & 0xff;
|
||||
b = (col ) & 0xff;
|
||||
|
||||
l = int((double)r * lr) + ((double)g * lg) + ((double)b * lb);
|
||||
if(l < 0)l = 0;
|
||||
if(l > 255)l = 255;
|
||||
r = g = b = l;
|
||||
if((bool)config::snes.video_color_curve == true) {
|
||||
r = color_curve_table[r >> 3];
|
||||
g = color_curve_table[g >> 3];
|
||||
b = color_curve_table[b >> 3];
|
||||
}
|
||||
|
||||
r >>= 3;
|
||||
g >>= 3;
|
||||
b >>= 3;
|
||||
} else if((int)config::snes.video_color_adjust_mode == VCA_VGA) {
|
||||
//rgb555->rgb332
|
||||
r >>= 2;
|
||||
g >>= 2;
|
||||
b >>= 3;
|
||||
|
||||
r = (r << 2) | (r >> 1);
|
||||
g = (g << 2) | (g >> 1);
|
||||
b = (b << 3) | (b << 1) | (b >> 1);
|
||||
} else if((int)config::snes.video_color_adjust_mode == VCA_GENESIS) {
|
||||
//rgb555->rgb333
|
||||
r >>= 2;
|
||||
g >>= 2;
|
||||
b >>= 2;
|
||||
|
||||
r = (r << 2) | (r >> 1);
|
||||
g = (g << 2) | (g >> 1);
|
||||
b = (b << 2) | (b >> 1);
|
||||
}
|
||||
if((int)config::snes.video_color_adjust_mode == VCA_GRAYSCALE) {
|
||||
l = int((double)r * lr) + ((double)g * lg) + ((double)b * lb);
|
||||
if(l < 0)l = 0;
|
||||
if(l > 255)l = 255;
|
||||
r = g = b = l;
|
||||
} else if((int)config::snes.video_color_adjust_mode == VCA_VGA) {
|
||||
r &= 0xe0;
|
||||
g &= 0xe0;
|
||||
b &= 0xc0;
|
||||
r |= (r >> 3) | (r >> 6);
|
||||
g |= (g >> 3) | (g >> 6);
|
||||
b |= (b >> 2) | (b >> 4) | (b >> 6);
|
||||
} else if((int)config::snes.video_color_adjust_mode == VCA_GENESIS) {
|
||||
r &= 0xe0;
|
||||
g &= 0xe0;
|
||||
b &= 0xe0;
|
||||
r |= (r >> 3) | (r >> 6);
|
||||
g |= (g >> 3) | (g >> 6);
|
||||
b |= (b >> 3) | (b >> 6);
|
||||
}
|
||||
|
||||
switch(video.depth) {
|
||||
case 15:
|
||||
r >>= 3;
|
||||
g >>= 3;
|
||||
b >>= 3;
|
||||
color_lookup_table[i] = (r << 10) | (g << 5) | (b);
|
||||
}
|
||||
break;
|
||||
case 16: //rgb565
|
||||
for(i=0;i<32768;i++) {
|
||||
r = (i ) & 31;
|
||||
g = (i >> 5) & 31;
|
||||
b = (i >> 10) & 31;
|
||||
|
||||
if((bool)config::snes.video_color_curve == true) {
|
||||
r = color_curve_table[r] >> 3;
|
||||
g = color_curve_table[g] >> 2;
|
||||
b = color_curve_table[b] >> 3;
|
||||
} else {
|
||||
g = (g << 1) | (g >> 4);
|
||||
}
|
||||
|
||||
if((int)config::snes.video_color_adjust_mode == VCA_GRAYSCALE) {
|
||||
r = (r << 3) | (r >> 2);
|
||||
g = (g << 2) | (g >> 4);
|
||||
b = (b << 3) | (b >> 2);
|
||||
|
||||
l = int((double)r * lr) + ((double)g * lg) + ((double)b * lb);
|
||||
if(l < 0)l = 0;
|
||||
if(l > 255)l = 255;
|
||||
r = g = b = l;
|
||||
|
||||
r >>= 3;
|
||||
g >>= 2;
|
||||
b >>= 3;
|
||||
} else if((int)config::snes.video_color_adjust_mode == VCA_VGA) {
|
||||
//rgb565->rgb332
|
||||
r >>= 2;
|
||||
g >>= 3;
|
||||
b >>= 3;
|
||||
|
||||
r = (r << 2) | (r >> 1);
|
||||
g = (g << 3) | (g);
|
||||
b = (b << 3) | (b << 1) | (b >> 1);
|
||||
} else if((int)config::snes.video_color_adjust_mode == VCA_GENESIS) {
|
||||
//rgb565->rgb333
|
||||
r >>= 2;
|
||||
g >>= 3;
|
||||
b >>= 2;
|
||||
|
||||
r = (r << 2) | (r >> 1);
|
||||
g = (g << 3) | (g);
|
||||
b = (b << 2) | (b >> 1);
|
||||
}
|
||||
|
||||
break;
|
||||
case 16:
|
||||
r >>= 3;
|
||||
g >>= 2;
|
||||
b >>= 3;
|
||||
color_lookup_table[i] = (r << 11) | (g << 5) | (b);
|
||||
break;
|
||||
case 24:
|
||||
case 32:
|
||||
color_lookup_table[i] = (r << 16) | (g << 8) | (b);
|
||||
break;
|
||||
default:
|
||||
alert("Error: SNES::update_color_lookup_table() -- color depth %d not supported", video.depth);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
alert("Error: SNES::update_color_lookup_table() -- color depth %d not supported", video.depth);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,32 +263,32 @@ bool field = !cpu->interlace_field();
|
||||
}
|
||||
|
||||
void SNES::video_update() {
|
||||
if(!ppu->render_frame())return;
|
||||
|
||||
if(video.format_changed == true) {
|
||||
update_video_format();
|
||||
}
|
||||
|
||||
uint16 *src = (uint16*)video.ppu_data + ((int(cpu->overscan()) << 3) * 1024);
|
||||
switch(video.mode) {
|
||||
case VM_256x224:video_update_256x224(src);break;
|
||||
case VM_512x224:video_update_512x224(src);break;
|
||||
case VM_256x448:video_update_256x448(src);break;
|
||||
case VM_512x448:video_update_512x448(src);break;
|
||||
case VM_VARIABLE:
|
||||
switch(int(video.frame_hires) | (int(video.frame_interlace) << 1)) {
|
||||
case 0:video_update_256x224(src);break;
|
||||
case 1:video_update_512x224(src);break;
|
||||
case 2:video_update_256x448(src);break;
|
||||
case 3:video_update_512x448(src);break;
|
||||
if(ppu->renderer_enabled()) {
|
||||
if(video.format_changed == true) {
|
||||
update_video_format();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//SNES::capture_screenshot() was called by emulation interface
|
||||
if(flag_output_screenshot == true) {
|
||||
output_screenshot();
|
||||
flag_output_screenshot = false;
|
||||
uint16 *src = (uint16*)video.ppu_data + ((int(cpu->overscan()) << 3) * 1024);
|
||||
switch(video.mode) {
|
||||
case VM_256x224:video_update_256x224(src);break;
|
||||
case VM_512x224:video_update_512x224(src);break;
|
||||
case VM_256x448:video_update_256x448(src);break;
|
||||
case VM_512x448:video_update_512x448(src);break;
|
||||
case VM_VARIABLE:
|
||||
switch(int(video.frame_hires) | (int(video.frame_interlace) << 1)) {
|
||||
case 0:video_update_256x224(src);break;
|
||||
case 1:video_update_512x224(src);break;
|
||||
case 2:video_update_256x448(src);break;
|
||||
case 3:video_update_512x448(src);break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//SNES::capture_screenshot() was called by emulation interface
|
||||
if(flag_output_screenshot == true) {
|
||||
output_screenshot();
|
||||
flag_output_screenshot = false;
|
||||
}
|
||||
}
|
||||
|
||||
video_run();
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
CC = cl
|
||||
CFLAGS = /nologo /O2 /Ogityb2 /Gr /Gs
|
||||
CFLAGS = /nologo /O2 /Ogityb2 /Gr /Gs /DARCH_LSB
|
||||
OBJS = winmain.obj \
|
||||
libstring.obj libconfig.obj \
|
||||
reader.obj \
|
||||
|
||||
+83
-19
@@ -114,7 +114,23 @@ void bSNES::run() {
|
||||
}
|
||||
|
||||
void bSNES::video_run() {
|
||||
dd_renderer->update();
|
||||
if(ppu->status.frames_updated) {
|
||||
char s[512], t[512];
|
||||
ppu->status.frames_updated = false;
|
||||
if((bool)config::gui.show_fps == true) {
|
||||
sprintf(s, "%s : %d fps", BSNES_TITLE, ppu->status.frames_executed);
|
||||
if(w_main->frameskip != 0) {
|
||||
sprintf(t, " (%d frames)", ppu->status.frames_rendered);
|
||||
strcat(s, t);
|
||||
}
|
||||
SetWindowText(w_main->hwnd, s);
|
||||
}
|
||||
}
|
||||
|
||||
w_main->frameskip_pos++;
|
||||
w_main->frameskip_pos %= (w_main->frameskip + 1);
|
||||
if(ppu->renderer_enabled())dd_renderer->update();
|
||||
ppu->enable_renderer(w_main->frameskip_pos == 0);
|
||||
}
|
||||
|
||||
void bSNES::sound_run() {
|
||||
@@ -124,30 +140,58 @@ void bSNES::sound_run() {
|
||||
/***********************
|
||||
*** Input functions ***
|
||||
***********************/
|
||||
void bSNES::poll_input() {
|
||||
void bSNES::poll_input(uint8 type) {
|
||||
//only capture input when main window has focus
|
||||
if(GetForegroundWindow() == w_main->hwnd) {
|
||||
joypad1.up = KeyDown(config::input.joypad1.up);
|
||||
joypad1.down = KeyDown(config::input.joypad1.down);
|
||||
joypad1.left = KeyDown(config::input.joypad1.left);
|
||||
joypad1.right = KeyDown(config::input.joypad1.right);
|
||||
joypad1.select = KeyDown(config::input.joypad1.select);
|
||||
joypad1.start = KeyDown(config::input.joypad1.start);
|
||||
joypad1.y = KeyDown(config::input.joypad1.y);
|
||||
joypad1.b = KeyDown(config::input.joypad1.b);
|
||||
joypad1.x = KeyDown(config::input.joypad1.x);
|
||||
joypad1.a = KeyDown(config::input.joypad1.a);
|
||||
joypad1.l = KeyDown(config::input.joypad1.l);
|
||||
joypad1.r = KeyDown(config::input.joypad1.r);
|
||||
switch(type) {
|
||||
case SNES::DEV_JOYPAD1:
|
||||
joypad1.up = KeyDown(config::input.joypad1.up);
|
||||
joypad1.down = KeyDown(config::input.joypad1.down);
|
||||
joypad1.left = KeyDown(config::input.joypad1.left);
|
||||
joypad1.right = KeyDown(config::input.joypad1.right);
|
||||
joypad1.select = KeyDown(config::input.joypad1.select);
|
||||
joypad1.start = KeyDown(config::input.joypad1.start);
|
||||
joypad1.y = KeyDown(config::input.joypad1.y);
|
||||
joypad1.b = KeyDown(config::input.joypad1.b);
|
||||
joypad1.x = KeyDown(config::input.joypad1.x);
|
||||
joypad1.a = KeyDown(config::input.joypad1.a);
|
||||
joypad1.l = KeyDown(config::input.joypad1.l);
|
||||
joypad1.r = KeyDown(config::input.joypad1.r);
|
||||
break;
|
||||
case SNES::DEV_JOYPAD2:
|
||||
joypad2.up = KeyDown(config::input.joypad2.up);
|
||||
joypad2.down = KeyDown(config::input.joypad2.down);
|
||||
joypad2.left = KeyDown(config::input.joypad2.left);
|
||||
joypad2.right = KeyDown(config::input.joypad2.right);
|
||||
joypad2.select = KeyDown(config::input.joypad2.select);
|
||||
joypad2.start = KeyDown(config::input.joypad2.start);
|
||||
joypad2.y = KeyDown(config::input.joypad2.y);
|
||||
joypad2.b = KeyDown(config::input.joypad2.b);
|
||||
joypad2.x = KeyDown(config::input.joypad2.x);
|
||||
joypad2.a = KeyDown(config::input.joypad2.a);
|
||||
joypad2.l = KeyDown(config::input.joypad2.l);
|
||||
joypad2.r = KeyDown(config::input.joypad2.r);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
joypad1.up = joypad1.down = joypad1.left = joypad1.right =
|
||||
joypad1.select = joypad1.start =
|
||||
joypad1.y = joypad1.b = joypad1.x = joypad1.a =
|
||||
joypad1.l = joypad1.r = 0;
|
||||
switch(type) {
|
||||
case SNES::DEV_JOYPAD1:
|
||||
joypad1.up = joypad1.down = joypad1.left = joypad1.right =
|
||||
joypad1.select = joypad1.start =
|
||||
joypad1.y = joypad1.b = joypad1.x = joypad1.a =
|
||||
joypad1.l = joypad1.r = 0;
|
||||
break;
|
||||
case SNES::DEV_JOYPAD2:
|
||||
joypad2.up = joypad2.down = joypad2.left = joypad2.right =
|
||||
joypad2.select = joypad2.start =
|
||||
joypad2.y = joypad2.b = joypad2.x = joypad2.a =
|
||||
joypad1.l = joypad2.r = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//check for debugger-based key locks
|
||||
if(is_debugger_enabled == true) {
|
||||
if(is_debugger_enabled == true && type == SNES::DEV_JOYPAD1) {
|
||||
if(w_console->joypad_lock.up )joypad1.up = true;
|
||||
if(w_console->joypad_lock.down )joypad1.down = true;
|
||||
if(w_console->joypad_lock.left )joypad1.left = true;
|
||||
@@ -167,6 +211,9 @@ void bSNES::poll_input() {
|
||||
//this to happen causes glitches in many SNES games.
|
||||
if(joypad1.up) joypad1.down = 0;
|
||||
if(joypad1.left)joypad1.right = 0;
|
||||
|
||||
if(joypad2.up) joypad2.down = 0;
|
||||
if(joypad2.left)joypad2.right = 0;
|
||||
}
|
||||
|
||||
bool bSNES::get_input_status(uint8 device, uint8 button) {
|
||||
@@ -187,7 +234,24 @@ bool bSNES::get_input_status(uint8 device, uint8 button) {
|
||||
case JOYPAD_START: return joypad1.start;
|
||||
}
|
||||
break;
|
||||
case DEV_JOYPAD2:
|
||||
switch(button) {
|
||||
case JOYPAD_UP: return joypad2.up;
|
||||
case JOYPAD_DOWN: return joypad2.down;
|
||||
case JOYPAD_LEFT: return joypad2.left;
|
||||
case JOYPAD_RIGHT: return joypad2.right;
|
||||
case JOYPAD_A: return joypad2.a;
|
||||
case JOYPAD_B: return joypad2.b;
|
||||
case JOYPAD_X: return joypad2.x;
|
||||
case JOYPAD_Y: return joypad2.y;
|
||||
case JOYPAD_L: return joypad2.l;
|
||||
case JOYPAD_R: return joypad2.r;
|
||||
case JOYPAD_SELECT:return joypad2.select;
|
||||
case JOYPAD_START: return joypad2.start;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ enum { DRAM = 0, SPCRAM = 1, VRAM = 2, OAM = 3, CGRAM = 4 };
|
||||
uint32 get_status();
|
||||
|
||||
//input functions
|
||||
void poll_input();
|
||||
void poll_input(uint8 type);
|
||||
bool get_input_status(uint8 device, uint8 button);
|
||||
|
||||
//debugging functions
|
||||
|
||||
Binary file not shown.
+28
-13
@@ -20,21 +20,36 @@ struct GUI {
|
||||
Setting GUI::show_fps(&config_file, "gui.show_fps", "Show framerate in window title", true, Setting::TRUE_FALSE);
|
||||
|
||||
struct Input {
|
||||
struct Joypad {
|
||||
struct Joypad1 {
|
||||
static Setting up, down, left, right, a, b, x, y, l, r, select, start;
|
||||
} joypad1;
|
||||
struct Joypad2 {
|
||||
static Setting up, down, left, right, a, b, x, y, l, r, select, start;
|
||||
} joypad2;
|
||||
} input;
|
||||
Setting Input::Joypad::up (&config_file, "input.joypad1.up", "Joypad1 up", VK_UP, Setting::HEX);
|
||||
Setting Input::Joypad::down (&config_file, "input.joypad1.down", "Joypad1 down", VK_DOWN, Setting::HEX);
|
||||
Setting Input::Joypad::left (&config_file, "input.joypad1.left", "Joypad1 left", VK_LEFT, Setting::HEX);
|
||||
Setting Input::Joypad::right (&config_file, "input.joypad1.right", "Joypad1 right", VK_RIGHT, Setting::HEX);
|
||||
Setting Input::Joypad::a (&config_file, "input.joypad1.a", "Joypad1 A", 'X', Setting::HEX);
|
||||
Setting Input::Joypad::b (&config_file, "input.joypad1.b", "Joypad1 B", 'Z', Setting::HEX);
|
||||
Setting Input::Joypad::x (&config_file, "input.joypad1.x", "Joypad1 X", 'S', Setting::HEX);
|
||||
Setting Input::Joypad::y (&config_file, "input.joypad1.y", "Joypad1 Y", 'A', Setting::HEX);
|
||||
Setting Input::Joypad::l (&config_file, "input.joypad1.l", "Joypad1 L", 'D', Setting::HEX);
|
||||
Setting Input::Joypad::r (&config_file, "input.joypad1.r", "Joypad1 R", 'C', Setting::HEX);
|
||||
Setting Input::Joypad::select(&config_file, "input.joypad1.select", "Joypad1 select", VK_SHIFT, Setting::HEX);
|
||||
Setting Input::Joypad::start (&config_file, "input.joypad1.start", "Joypad1 start", VK_RETURN, Setting::HEX);
|
||||
Setting Input::Joypad1::up (&config_file, "input.joypad1.up", "Joypad1 up", VK_UP, Setting::HEX);
|
||||
Setting Input::Joypad1::down (&config_file, "input.joypad1.down", "Joypad1 down", VK_DOWN, Setting::HEX);
|
||||
Setting Input::Joypad1::left (&config_file, "input.joypad1.left", "Joypad1 left", VK_LEFT, Setting::HEX);
|
||||
Setting Input::Joypad1::right (&config_file, "input.joypad1.right", "Joypad1 right", VK_RIGHT, Setting::HEX);
|
||||
Setting Input::Joypad1::a (&config_file, "input.joypad1.a", "Joypad1 A", 'X', Setting::HEX);
|
||||
Setting Input::Joypad1::b (&config_file, "input.joypad1.b", "Joypad1 B", 'Z', Setting::HEX);
|
||||
Setting Input::Joypad1::x (&config_file, "input.joypad1.x", "Joypad1 X", 'S', Setting::HEX);
|
||||
Setting Input::Joypad1::y (&config_file, "input.joypad1.y", "Joypad1 Y", 'A', Setting::HEX);
|
||||
Setting Input::Joypad1::l (&config_file, "input.joypad1.l", "Joypad1 L", 'D', Setting::HEX);
|
||||
Setting Input::Joypad1::r (&config_file, "input.joypad1.r", "Joypad1 R", 'C', Setting::HEX);
|
||||
Setting Input::Joypad1::select(&config_file, "input.joypad1.select", "Joypad1 select", VK_SHIFT, Setting::HEX);
|
||||
Setting Input::Joypad1::start (&config_file, "input.joypad1.start", "Joypad1 start", VK_RETURN, Setting::HEX);
|
||||
|
||||
Setting Input::Joypad2::up (&config_file, "input.joypad2.up", "Joypad2 up", 'T', Setting::HEX);
|
||||
Setting Input::Joypad2::down (&config_file, "input.joypad2.down", "Joypad2 down", 'G', Setting::HEX);
|
||||
Setting Input::Joypad2::left (&config_file, "input.joypad2.left", "Joypad2 left", 'F', Setting::HEX);
|
||||
Setting Input::Joypad2::right (&config_file, "input.joypad2.right", "Joypad2 right", 'H', Setting::HEX);
|
||||
Setting Input::Joypad2::a (&config_file, "input.joypad2.a", "Joypad2 A", 'K', Setting::HEX);
|
||||
Setting Input::Joypad2::b (&config_file, "input.joypad2.b", "Joypad2 B", 'J', Setting::HEX);
|
||||
Setting Input::Joypad2::x (&config_file, "input.joypad2.x", "Joypad2 X", 'I', Setting::HEX);
|
||||
Setting Input::Joypad2::y (&config_file, "input.joypad2.y", "Joypad2 Y", 'U', Setting::HEX);
|
||||
Setting Input::Joypad2::l (&config_file, "input.joypad2.l", "Joypad2 L", 'O', Setting::HEX);
|
||||
Setting Input::Joypad2::r (&config_file, "input.joypad2.r", "Joypad2 R", 'L', Setting::HEX);
|
||||
Setting Input::Joypad2::select(&config_file, "input.joypad2.select", "Joypad2 select", '[', Setting::HEX);
|
||||
Setting Input::Joypad2::start (&config_file, "input.joypad2.start", "Joypad2 start", ']', Setting::HEX);
|
||||
};
|
||||
+2
-19
@@ -164,33 +164,16 @@ int rx, ry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(config::video.vblank) {
|
||||
lpdd->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, 0);
|
||||
}
|
||||
|
||||
hr = lpdds->Blt(&rd, lpddsb, &lpddrc, DDBLT_WAIT, 0);
|
||||
if(hr == DDERR_SURFACELOST) {
|
||||
lpdds->Restore();
|
||||
lpddsb->Restore();
|
||||
}
|
||||
|
||||
if((int)config::gui.show_fps == false || bsnes->get_status() == bSNES::STOP)return;
|
||||
uint32 fps;
|
||||
char s[256], t[256];
|
||||
fps_timer->tick();
|
||||
if(fps_timer->second_passed() == true) {
|
||||
sprintf(s, BSNES_TITLE);
|
||||
if(rom_image->loaded() == true) {
|
||||
fps = fps_timer->get_ticks();
|
||||
if(w_main->frameskip == 0) {
|
||||
sprintf(t, " : %d fps", fps);
|
||||
} else {
|
||||
sprintf(t, " : %d fps [fs: %d]", fps * (1 + w_main->frameskip), w_main->frameskip);
|
||||
}
|
||||
strcat(s, t);
|
||||
}
|
||||
SetWindowText(w_main->hwnd, s);
|
||||
fps_timer->reset();
|
||||
}
|
||||
}
|
||||
|
||||
#include "dd_renderer16.cpp"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
+4
-2
@@ -39,6 +39,7 @@ enum {
|
||||
MENU_SETTINGS_SHOWFPS,
|
||||
MENU_SETTINGS_MUTE,
|
||||
MENU_SETTINGS_INPUTCFG_JOYPAD1,
|
||||
MENU_SETTINGS_INPUTCFG_JOYPAD2,
|
||||
MENU_SETTINGS_DEBUGGER,
|
||||
MENU_MISC_SCREENSHOT,
|
||||
MENU_MISC_LOGAUDIO,
|
||||
@@ -164,7 +165,7 @@ struct { int width, height; }window;
|
||||
|
||||
class MainWindow : public Window {
|
||||
public:
|
||||
uint8 frameskip;
|
||||
uint8 frameskip, frameskip_pos;
|
||||
int width, height;
|
||||
void create();
|
||||
void to_fullscreen();
|
||||
@@ -256,7 +257,8 @@ bool auto_update; //update memory window whenever visible value is written to
|
||||
class InputConfig : public Window {
|
||||
public:
|
||||
enum {
|
||||
JOYPAD1 = 0
|
||||
JOYPAD1 = 0,
|
||||
JOYPAD2
|
||||
};
|
||||
enum {
|
||||
ID_COMMAND = 100
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
+6
-2
@@ -11,8 +11,8 @@ void MainWindow::set_frameskip(uint8 fs) {
|
||||
CheckMenuItem(w_main->hmenu, MENU_SETTINGS_FRAMESKIP_9, MF_UNCHECKED);
|
||||
|
||||
CheckMenuItem(w_main->hmenu, MENU_SETTINGS_FRAMESKIP_OFF + fs, MF_CHECKED);
|
||||
ppu->set_frameskip(fs);
|
||||
w_main->frameskip = fs;
|
||||
w_main->frameskip = fs;
|
||||
w_main->frameskip_pos = 0;
|
||||
}
|
||||
|
||||
void MainWindow::to_fullscreen() {
|
||||
@@ -239,6 +239,9 @@ int i;
|
||||
case MENU_SETTINGS_INPUTCFG_JOYPAD1:
|
||||
w_inputconfig->begin_config(InputConfig::JOYPAD1);
|
||||
break;
|
||||
case MENU_SETTINGS_INPUTCFG_JOYPAD2:
|
||||
w_inputconfig->begin_config(InputConfig::JOYPAD2);
|
||||
break;
|
||||
case MENU_SETTINGS_DEBUGGER:
|
||||
if(bsnes->debugger_enabled() == true) {
|
||||
bsnes->debugger_disable();
|
||||
@@ -350,6 +353,7 @@ HMENU hsubmenu, hbranchmenu;
|
||||
|
||||
hbranchmenu = CreatePopupMenu();
|
||||
AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_INPUTCFG_JOYPAD1, "Joypad 1");
|
||||
AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_INPUTCFG_JOYPAD2, "Joypad 2");
|
||||
AppendMenu(hsubmenu, MF_STRING | MF_POPUP, (unsigned int)hbranchmenu, "&Configure Input Devices");
|
||||
|
||||
AppendMenu(hsubmenu, MF_SEPARATOR, 0, "");
|
||||
|
||||
@@ -15,9 +15,6 @@
|
||||
#include "dd_renderer.h"
|
||||
#include "ds_sound.h"
|
||||
|
||||
#include "timer.cpp"
|
||||
fpstimer *fps_timer;
|
||||
|
||||
#include "lib.cpp"
|
||||
#include "rom.cpp"
|
||||
|
||||
@@ -27,7 +24,6 @@ fpstimer *fps_timer;
|
||||
void init_snes() {
|
||||
mem_bus = new bMemBus();
|
||||
cpu = new bCPU();
|
||||
cpu->cpu_version = 1;
|
||||
apu = new bAPU();
|
||||
dsp = new bDSP();
|
||||
ppu = new bPPU();
|
||||
@@ -82,8 +78,6 @@ string cfg_fn;
|
||||
get_config_fn(cfg_fn);
|
||||
config_file.load(cfg_fn);
|
||||
meminit();
|
||||
fps_timer = new fpstimer();
|
||||
fps_timer->start();
|
||||
rom_image = new ROMImage();
|
||||
|
||||
init_ui0();
|
||||
|
||||
Reference in new issue
Block a user