Compare commits

..

2 Commits
v011 ... v013

Author SHA1 Message Date
byuu
c6c5f4669c Update to bsnes v013 release.
- Greatly improved HDMA timing and accuracy with help from anomie and DMV27 -- fixes bugs in Energy Breaker and Street Fighter Alpha 2
    - Fixed a problem with color add/sub code -- fixes opening battle in Tales of Phantasia and clouds in Energy Breaker
    - Temporarily added DMV27's bugfix for the DSP KON register -- fixes sound in Der Langrisser, but this is not a hardware-accurate fix
    - Disabled VRAM writes outside of vblank -- fixes Hook, but breaks many PD ROMs and fan translations (Roto's BS Zelda hack, Gideon Zhi's Ys 4 translation, etc). I might add an option in the future to toggle this behavior, but for now these games will no longer work. Please keep in mind these games will not run properly on real SNES hardware, either.
    - Improved frameskipping code thanks to a suggestion from Richard Bannister
    - Misc. other code cleanups and improvements (notably in the color table generation code)
    - bsnes is now endian-safe and runs on Mac OS X
    - Added caching support for window clipping tables resulting in a slight speedup. Please let me know if you spot any errors as a result of this change.
2005-10-23 23:32:30 +00:00
byuu
397b9c4505 Update to bsnes v012 release.
Changelog:
    - Added S-DSP emulation
    - Added sound output support via DirectSound -- no sound buffering though, so sound is muted by default
    - Added option to record raw sound output to WAV files
    - Added multiple color adjustment filters to the video output
    - Added mode3/4 direct color support
    - Added mode7 direct color and mosaic support
    - Greatly improved mode7 rendering algorithm thanks to anomie
    - Fixed mode7 screen repitition and EXTBG effects
    - Greatly increased accuracy of NMI and IRQ timing, and emulated many newly discovered hardware quirks involving the two
    - A few speed improvements courtesy of Nach for profiling the code for me
I'm now looking for assistance with sound buffering. Specifically, I need help modifying the DirectSound code to allow the emulator to be ran between 50%-400% normal speed, while keeping the sound output relatively good. If you have experience with this and can help, please get in touch with me (setsunakun0 at hotmail dot com).
2005-10-02 00:38:34 +00:00
112 changed files with 5264 additions and 2272 deletions

154
bsnes.cfg
View File

@@ -1,57 +1,135 @@
#[bsnes v0.009 configuration file]
# 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
#[apu enable]
apu.enabled = 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
#[video mode]
# 0: 256x224w
# 1: 512x448w
# 2: 960x720w
# 3: 640x480f
# 4: 1024x768f
# Mutes SNES audio output when enabled
# (default = true)
snes.mute = true
# Video mode
# 0 = 256x224w
# 1 = 512x448w
# 2 = 960x720w
# 3 = 640x480f
# 4 = 1024x768f
# (default = 1)
video.mode = 1
#[video memory type]
# true: video ram (VRAM)
# false: system ram (SRAM)
#
# VRAM results in the image being stretched in hardware,
# which is generally much faster, and automatically adds
# bilinear filtering (if the card supports it).
#
# However, some video cards end up taking a major speed
# loss when this option is enabled. It is also the only
# way to guarantee that the output image will not be
# filtered.
# Use Video RAM instead of System RAM
# (default = true)
video.use_vram = true
#[color curve]
# gives a more NTSC TV-style feel to the color palette
# by darkening the image contrast.
video.color_curve = enabled
#[show fps]
# true: show fps in titlebar
# false: do not show fps in titlebar
gui.show_fps = true
#[wait for vertical retrace]
# Wait for vertical retrace when updating screen
# (default = false)
video.vblank = false
#[joypad 1 configuration]
# Key numbers are standard windows VK_* keys.
# Unfortunately, I don't have a table of common
# key mappings to list here... use GUI joypad
# configuration utility to edit these.
# Show framerate in window title
# (default = true)
gui.show_fps = true
# Joypad1 up
# (default = 0x26)
input.joypad1.up = 0x26
# Joypad1 down
# (default = 0x28)
input.joypad1.down = 0x28
# Joypad1 left
# (default = 0x25)
input.joypad1.left = 0x25
# Joypad1 right
# (default = 0x27)
input.joypad1.right = 0x27
# Joypad1 A
# (default = 0x58)
input.joypad1.a = 0x58
# Joypad1 B
# (default = 0x5a)
input.joypad1.b = 0x5a
# Joypad1 X
# (default = 0x53)
input.joypad1.x = 0x53
# Joypad1 Y
# (default = 0x41)
input.joypad1.y = 0x41
# Joypad1 L
# (default = 0x44)
input.joypad1.l = 0x44
# Joypad1 R
# (default = 0x43)
input.joypad1.r = 0x43
# Joypad1 select
# (default = 0x10)
input.joypad1.select = 0x10
input.joypad1.start = 0x0d
# Joypad1 start
# (default = 0xd)
input.joypad1.start = 0xd
# Joypad2 up
# (default = 0x54)
input.joypad2.up = 0x54
# Joypad2 down
# (default = 0x47)
input.joypad2.down = 0x47
# Joypad2 left
# (default = 0x46)
input.joypad2.left = 0x46
# Joypad2 right
# (default = 0x48)
input.joypad2.right = 0x48
# Joypad2 A
# (default = 0x4b)
input.joypad2.a = 0x4b
# Joypad2 B
# (default = 0x4a)
input.joypad2.b = 0x4a
# Joypad2 X
# (default = 0x49)
input.joypad2.x = 0x49
# Joypad2 Y
# (default = 0x55)
input.joypad2.y = 0x55
# Joypad2 L
# (default = 0x4f)
input.joypad2.l = 0x4f
# Joypad2 R
# (default = 0x4c)
input.joypad2.r = 0x4c
# Joypad2 select
# (default = 0x5b)
input.joypad2.select = 0x5b
# Joypad2 start
# (default = 0x5d)
input.joypad2.start = 0x5d

BIN
bsnes.exe

Binary file not shown.

View File

@@ -17,5 +17,10 @@ emulators.
The Simple DirectMedia Layer library source code is available from:
http://www.libsdl.org/
This library is distributed under the terms of the GNU LGPL license:
This library is distributed under the terms of the GNU LGPL:
http://www.gnu.org/copyleft/lesser.html
Licensing Exemptions:
---------------------
Richard Bannister has asked for and received my permission to distribute
a binary-only port of bsnes on the Mac OS X platform.

View File

@@ -1,2 +1,3 @@
#include "../base.h"
#include "iplrom.h"
#include "dapu.cpp"

View File

@@ -1,10 +1,9 @@
#define _APU_IPLROM
#include "iplrom.h"
#include "apuregs.h"
class APU {
public:
APURegs regs;
static const uint8 iplrom[64];
enum {
FLAG_N = 0x80, FLAG_V = 0x40,
FLAG_P = 0x20, FLAG_B = 0x10,
@@ -18,6 +17,7 @@ APURegs regs;
virtual uint8 port_read (uint8 port) = 0;
virtual void port_write(uint8 port, uint8 value) = 0;
virtual uint8 *get_spcram_handle() = 0;
virtual void run() = 0;
virtual uint32 cycles_executed() = 0;
virtual void power() = 0;

View File

@@ -13,7 +13,7 @@ private:
inline bool operator ^= (bool i) { if(i)_b ^= B; return (_b & B); }
};
public:
union {
union {
uint8 _b;
bit<0x80> n;
bit<0x40> v;
@@ -23,7 +23,7 @@ public:
bit<0x04> i;
bit<0x02> z;
bit<0x01> c;
};
};
APURegFlags() { _b = 0; }
inline operator uint8() { return _b; }
@@ -38,10 +38,11 @@ public:
uint16 pc;
union {
uint16 ya;
//not endian-safe
struct {
uint8 a, y;
};
#ifdef ARCH_LSB
struct { uint8 a, y; };
#else
struct { uint8 y, a; };
#endif
};
uint8 x, sp;
APURegFlags p;

View File

@@ -24,7 +24,7 @@ uint8 r;
break;
case 0xf3: //DSPDATA
//0x80-0xff is a read-only mirror of 0x00-0x7f
r = dsp_regs[status.dsp_addr & 0x7f];
r = dsp->read(status.dsp_addr & 0x7f);
break;
case 0xf4: //CPUIO0
case 0xf5: //CPUIO1
@@ -111,7 +111,7 @@ void bAPU::spcram_write(uint16 addr, uint8 value) {
case 0xf3: //DSPDATA
//0x80-0xff is a read-only mirror of 0x00-0x7f
if(status.dsp_addr < 0x80) {
dsp_regs[status.dsp_addr & 0x7f] = value;
dsp->write(status.dsp_addr & 0x7f, value);
}
break;
case 0xf4: //CPUIO0
@@ -209,6 +209,14 @@ void bAPU::stack_write(uint8 value) {
regs.sp--;
}
uint8 *bAPU::get_spcram_handle() {
if(!spcram) {
alert("bAPU::get_spcram_handle() -- spcram uninitialized");
}
return spcram;
}
void bAPU::run() {
exec_cycle();
}
@@ -247,15 +255,12 @@ void bAPU::reset() {
t0.stage3_ticks = 0;
t1.stage3_ticks = 0;
t2.stage3_ticks = 0;
memset(dsp_regs, 0, 128);
}
bAPU::bAPU() {
init_op_table();
spcram = (uint8*)malloc(65536);
memcpy(iplrom, spc700_iplrom, 64);
t0.cycle_frequency = 128; //1.024mhz / 8khz = 128
t1.cycle_frequency = 128; //1.024mhz / 8khz = 128

View File

@@ -29,12 +29,13 @@ struct {
bAPUTimer t0, t1, t2;
uint8 *spcram, iplrom[64], dsp_regs[128];
uint8 *spcram;
inline uint8 spcram_read (uint16 addr);
inline void spcram_write(uint16 addr, uint8 value);
inline uint8 port_read (uint8 port);
inline void port_write(uint8 port, uint8 value);
inline uint8 *get_spcram_handle();
inline void run();
inline uint32 cycles_executed();
inline void power();

View File

@@ -510,7 +510,7 @@ void bAPU::op_cbne_dp() {
rd = op_read();
break;
case 3:
sp = op_read(OPMODE_DP, dp + 0);
sp = op_read(OPMODE_DP, dp);
break;
case 4:
if(regs.a == sp)status.cycle_pos = 0;
@@ -533,14 +533,16 @@ void bAPU::op_cbne_dpx() {
rd = op_read();
break;
case 3:
sp = op_read(OPMODE_DP, dp + regs.x);
break;
case 4:
if(regs.a == sp)status.cycle_pos = 0;
sp = op_read(OPMODE_DP, dp + regs.x);
break;
case 5:
if(regs.a == sp)status.cycle_pos = 0;
break;
case 6:
break;
case 7:
regs.pc += (int8)rd;
status.cycle_pos = 0;
break;

Binary file not shown.

View File

@@ -37,16 +37,25 @@ bbc7(0xf3, 0x80, ==) {
6:regs.pc += (int8)rd;
}
cbne_dp(0x2e, 0),
cbne_dpx(0xde, regs.x) {
cbne_dp(0x2e) {
1:dp = op_read();
2:rd = op_read();
3:sp = op_read(OPMODE_DP, dp + $1);
3:sp = op_read(OPMODE_DP, dp);
4:if(regs.a == sp)end;
5:
6:regs.pc += (int8)rd;
}
cbne_dpx(0xde) {
1:dp = op_read();
2:rd = op_read();
3:
4:sp = op_read(OPMODE_DP, dp + regs.x);
5:if(regs.a == sp)end;
6:
7:regs.pc += (int8)rd;
}
dbnz_dp(0x6e) {
1:dp = op_read();
2:rd = op_read();

View File

@@ -1,4 +1,7 @@
class bAPUSkip : public APU {
private:
uint8 spcram[65536];
public:
struct _apu_port {
@@ -26,6 +29,7 @@ enum {
uint8 port_read (uint8 port);
void port_write (uint8 port, uint8 value);
uint8 *get_spcram_handle() { return spcram; }
void run();
uint32 cycles_executed() { return 12 * 24; }
void power();

View File

@@ -3,12 +3,8 @@
//allow writing to the IPLROM, all writes are
//instead mapped to the extended SPC700 RAM region,
//accessible when $f1 bit 7 is clear.
//If you use this buffer directly, make sure not
//to write to it, as this will break other APU
//implementations that attempt to use this buffer.
#ifdef _APU_IPLROM
const uint8 spc700_iplrom[64] = {
const uint8 APU::iplrom[64] = {
/*ffc0*/ 0xcd, 0xef, //mov x,#$ef
/*ffc2*/ 0xbd, //mov sp,x
/*ffc3*/ 0xe8, 0x00, //mov a,#$00
@@ -43,6 +39,3 @@ const uint8 spc700_iplrom[64] = {
/*fffb*/ 0x1f, 0x00, 0x00, //jmp ($0000+x)
/*fffe*/ 0xc0, 0xff //---reset vector location ($ffc0)
};
#else
extern const uint8 spc700_iplrom[64];
#endif

View File

@@ -1,5 +1,26 @@
//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_
@@ -11,12 +32,6 @@
#error "unknown architecture"
#endif
//structs
typedef struct {
uint8 *data;
uint32 size;
}lfile;
//platform-specific global functions
void *memalloc(uint32 size, char *name = 0, ...);
void memfree(void *mem, char *name = 0, ...);

View File

@@ -1,6 +1,15 @@
#include "../../base.h"
#include "sdd1emu.cpp"
void SDD1::init() {
}
void SDD1::enable() {
for(int i=0x4800;i<=0x4807;i++) {
mem_bus->set_mmio_mapper(i, mmio);
}
}
void SDD1::power() {
reset();
}

View File

@@ -21,6 +21,7 @@ struct {
bool dma_active;
}sdd1;
void init();
void enable();
void power();
void reset();
uint32 offset(uint32 addr);

View File

@@ -74,6 +74,14 @@ tm *t;
srtc.data[12] = t->tm_wday;
}
void SRTC::init() {
}
void SRTC::enable() {
mem_bus->set_mmio_mapper(0x2800, mmio);
mem_bus->set_mmio_mapper(0x2801, mmio);
}
void SRTC::power() {
memset(&srtc, 0, sizeof(srtc));
reset();

View File

@@ -41,11 +41,13 @@ Index Description Range
12 Day of week 0-6 (0=Sunday, ...)
******************************/
struct {
int8 index;
uint8 mode;
uint8 data[MAX_SRTC_INDEX + 1];
}srtc;
int8 index;
uint8 mode;
uint8 data[MAX_SRTC_INDEX + 1];
} srtc;
void set_time();
void init();
void enable();
void power();
void reset();
void write(uint8 data);

28
src/config/config.cpp Normal file
View File

@@ -0,0 +1,28 @@
Config config_file;
namespace config {
SNES::VideoColorAdjust SNES::video_color_curve(&config_file, "snes.video_color_curve",
"Applies contrast adjust filter to video output when enabled\n"
"Works by lowering the brightness of darker colors,\n"
"while leaving brighter colors alone; thus reducing saturation",
true, Setting::TRUE_FALSE);
SNES::VideoColorAdjust SNES::video_color_adjust_mode(&config_file, "snes.video_color_adjust_mode",
"Selects color adjustment filter for video output\n"
" 0 = Normal (no filter, rgb555)\n"
" 1 = Grayscale mode (l5)\n"
" 2 = VGA mode (rgb332)\n"
" 3 = Genesis mode (rgb333)",
0, Setting::DEC);
void SNES::VideoColorAdjust::set(uint32 _data) {
data = _data;
::snes->update_color_lookup_table();
}
Setting SNES::mute(&config_file, "snes.mute",
"Mutes SNES audio output when enabled",
true, Setting::TRUE_FALSE);
};

15
src/config/config.h Normal file
View File

@@ -0,0 +1,15 @@
extern Config config_file;
namespace config {
extern struct SNES {
static class VideoColorAdjust : public Setting {
public:
void set(uint32 _data);
SettingOperators(VideoColorAdjust);
} video_color_curve, video_color_adjust_mode;
static Setting mute;
} snes;
};

View File

@@ -13,190 +13,77 @@
#include "bcpu_timing.cpp"
uint8 bCPU::pio_status() {
return status.pio;
}
#include "bcpu_int.cpp"
/***********
*** IRQ ***
***********
cycles:
[1] pbr,pc ; io/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
*/
void bCPU::irq(uint16 addr) {
if(status.cpu_state == CPUSTATE_WAI) {
status.cpu_state = CPUSTATE_RUN;
regs.pc.w++;
}
//GTE 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);
}
//vcounter range verified on real hardware,
//HDMA runs on the very first scanline of vblank
bool bCPU::hdma_test() {
if(status.hdma_triggered == false) {
if(vcounter() <= (overscan()?239:224) && hcounter() >= 278) {
status.hdma_triggered = true;
return true;
}
}
return false;
}
//NMI range: V==225/240,H>=12 ; V>225/240
bool bCPU::nmi_test() {
if(status.nmi_exec == true)return false;
//[status.cycle_count index]
// 6->12
// 8->14
int hc = status.cycle_count + 6;
if(vcounter() == ((overscan()?239:224) + 1) && hcycles() < hc) {
//dprintf("* miss at %3d,%4d,%3x : %d x=%0.4x", vcounter(), hcycles(), hcounter(), status.cycle_count, regs.x.w);
}
if(
(vcounter() == ((overscan()?239:224) + 1) && hcycles() >= hc) ||
(vcounter() > ((overscan()?239:224) + 1))
) {
//dprintf("* %3d,%4d,%3x : %d x=%0.4x", vcounter(), hcycles(), hcounter(), status.cycle_count, regs.x.w);
status.nmi_exec = true;
return status.nmi_enabled;
}
return false;
}
bool bCPU::irq_test() {
int vpos, hpos;
if(regs.p.i)return false; //no interrupt can occur with I flag set
if(status.irq_read == true)return false; //same as above
if(status.virq_enabled == false && status.hirq_enabled == false)return false;
//if irq_exec is true, then an IRQ occurred already.
//IRQs will continue to fire until $4211 is read from, or
//$4200 is written to, where irq_exec is set back to false.
if(status.irq_exec == true) {
return true;
}
//calculate V/H positions required for IRQ to trigger
vpos = status.virq_pos;
hpos = (status.hirq_enabled) ? status.hirq_pos : 0;
//positions that can never be latched
//region_scanlines() = 262/NTSC, 312/PAL
//PAL results are unverified on hardware
if(vpos == 240 && hpos == 339 && interlace() == false && interlace_field() == 1)return false;
if(vpos == (region_scanlines() - 1) && hpos == 339 && interlace() == false)return false;
if(vpos == region_scanlines() && interlace() == false)return false;
if(vpos == region_scanlines() && hpos == 339)return false;
if(vpos > region_scanlines())return false;
if(hpos > 339)return false;
if(hpos == 0) {
hpos = status.cycle_count + 14;
} else {
hpos <<= 2;
hpos += status.cycle_count + 18;
//it should be OK to use the current line cycles/frame lines,
//as the IRQ will only trigger on the correct scanline anyway...
if(hpos >= time.line_cycles) {
hpos -= time.line_cycles;
vpos++;
if(vpos >= time.frame_lines) {
vpos = 0;
}
}
}
if(status.virq_enabled == true && vcounter() != vpos)return false;
if(hcycles() >= hpos) {
status.irq_exec = true;
return true;
}
return false;
}
uint8 bCPU::pio_status() { return status.pio; }
void bCPU::run() {
switch(status.cpu_state) {
case CPUSTATE_DMA:
dma_run();
break;
case CPUSTATE_RUN:
case CPUSTATE_WAI:
if(status.cycle_pos == 0) {
//interrupts only trigger on opcode edges
if(nmi_test() == true) {
irq(0xffea);
break;
}
if(irq_test() == true) {
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;
}
if(status.virq_enabled == false) {
status.irq_read = false;
status.joypad2_read_pos = 16;
}
}
void bCPU::frame() {
hdma_initialize();
time.nmi_read = 1;
time.nmi_line = 1;
time.nmi_transition = 0;
status.nmi_read = false;
status.nmi_exec = false;
status.irq_read = false;
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() {
@@ -225,27 +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;
status.nmi_read = false;
status.nmi_exec = false;
status.irq_read = false;
status.irq_exec = 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) {
@@ -258,43 +145,49 @@ void bCPU::port_write(uint8 port, uint8 value) {
void bCPU::cpu_c2() {
if(regs.d.l != 0x00) {
status.cycle_count = 6;
add_cycles(6);
cpu_io();
}
}
void bCPU::cpu_c4(uint16 x, uint16 y) {
if(!regs.p.x && (x & 0xff00) != (y & 0xff00)) {
status.cycle_count = 6;
add_cycles(6);
cpu_io();
}
}
void bCPU::cpu_c6(uint16 addr) {
if(regs.e && (regs.pc.w & 0xff00) != (addr & 0xff00)) {
status.cycle_count = 6;
add_cycles(6);
cpu_io();
}
}
/* The next 3 functions control bus timing for the CPU.
* 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.d0 FastROM enable bit.
*/
void bCPU::cpu_io() {
status.cycle_count = 6;
pre_exec_cycle();
add_cycles(6);
}
uint8 bCPU::mem_read(uint32 addr) {
status.cycle_count = mem_bus->speed(addr);
add_cycles(2);
pre_exec_cycle();
add_cycles(status.cycle_count - 4);
regs.mdr = mem_bus->read(addr);
add_cycles(status.cycle_count - 2);
add_cycles(4);
return regs.mdr;
}
void bCPU::mem_write(uint32 addr, uint8 value) {
status.cycle_count = mem_bus->speed(addr);
add_cycles(6);
pre_exec_cycle();
add_cycles(status.cycle_count);
mem_bus->write(addr, value);
add_cycles(status.cycle_count - 6);
}
uint32 bCPU::op_addr(uint8 mode, uint32 addr) {
@@ -306,12 +199,11 @@ uint32 bCPU::op_addr(uint8 mode, uint32 addr) {
addr &= 0xffffff;
break;
case OPMODE_DBR:
addr &= 0xffffff;
addr = (regs.db << 16) + addr;
addr = ((regs.db << 16) + addr) & 0xffffff;
break;
case OPMODE_PBR:
addr &= 0xffff;
addr = (regs.pc.b << 16) | addr;
addr = (regs.pc.b << 16) + addr;
break;
case OPMODE_DP:
addr &= 0xffff;

View File

@@ -10,8 +10,7 @@ bCPU *cpu;
class bCPU : public CPU {
private:
typedef void (bCPU::*op)();
op optbl[256];
void (bCPU::*optbl[256])();
enum { NTSC = 0, PAL = 1 };
uint8 region;
@@ -33,49 +32,49 @@ 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;
uint8 dma_state;
uint32 dma_cycle_count;
bool hdma_triggered;
uint8 dma_state, hdma_state;
uint32 dma_cycle_count, hdma_cycle_count;
//used by $4210 read bit 7
bool nmi_read;
//used by NMI test, set when NMI executed this frame
bool nmi_exec;
//IRQ is level-sensitive, so $4211 must be read to
//prevent multiple interrupts from occurring
bool irq_read;
//this is used to return $4211 bit 7
bool irq_exec;
//$4207-$420a
uint16 virq_trigger, hirq_trigger;
//$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;
@@ -98,7 +97,13 @@ struct {
//$4214-$4216
uint16 r4214;
uint16 r4216;
}status;
} status;
//$43x0.d7
enum {
DMA_CPUTOMMIO = 0,
DMA_MMIOTOCPU = 1
};
struct {
uint32 read_index; //set to 0 at beginning of DMA/HDMA
@@ -121,7 +126,10 @@ struct {
//$43x4
uint8 srcbank;
//$43x5-$43x6
uint16 xfersize;
union {
uint16 xfersize;
uint16 hdma_iaddr;
};
//$43x7
uint8 hdma_ibank;
//$43x8-$43x9
@@ -132,16 +140,13 @@ struct {
uint8 hdma_unknown;
//hdma-specific
bool hdma_first_line;
bool hdma_repeat;
uint16 hdma_iaddr;
bool hdma_active;
}channel[8];
bool hdma_do_transfer;
} channel[8];
inline bool hdma_test();
inline bool nmi_test();
inline bool irq_test();
inline void irq(uint16 addr);
inline uint8 pio_status();
inline void run();
@@ -151,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
@@ -179,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);
@@ -196,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);
@@ -224,8 +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 cycle_edge();
inline void last_cycle();
inline bool in_opcode();
//cpu extra-cycle conditions

View File

@@ -1,7 +1,22 @@
void bCPU::dma_add_cycles(uint32 cycles) {
status.dma_cycle_count += cycles;
}
void bCPU::hdma_add_cycles(uint32 cycles) {
if(run_state.dma) {
status.dma_cycle_count += cycles;
}
status.hdma_cycle_count += cycles;
}
uint32 bCPU::dma_addr(uint8 i) {
uint32 r;
r = channel[i].srcaddr;
r |= (channel[i].srcbank << 16);
r = (channel[i].srcbank << 16) | (channel[i].srcaddr);
if(channel[i].fixedxfer == false) {
channel[i].srcaddr += channel[i].incmode;
}
return r;
}
@@ -15,10 +30,6 @@ uint8 x;
mem_bus->write(0x2100 | ((channel[i].destaddr + index) & 0xff), x);
if(channel[i].fixedxfer == false) {
channel[i].srcaddr += channel[i].incmode;
}
add_cycles(8);
channel[i].xfersize--;
}
@@ -28,10 +39,6 @@ uint8 x;
x = mem_bus->read(0x2100 | ((channel[i].destaddr + index) & 0xff));
mem_bus->write(dma_addr(i), x);
if(channel[i].fixedxfer == false) {
channel[i].srcaddr += channel[i].incmode;
}
add_cycles(8);
channel[i].xfersize--;
}
@@ -51,7 +58,8 @@ int i;
//first byte transferred?
if(channel[i].read_index == 0) {
sdd1->dma_begin(i, dma_addr(i), channel[i].xfersize);
sdd1->dma_begin(i, (channel[i].srcbank << 16) | (channel[i].srcaddr),
channel[i].xfersize);
}
switch(channel[i].xfermode) {
@@ -66,6 +74,7 @@ int i;
}
channel[i].read_index++;
dma_add_cycles(8);
if(channel[i].xfersize == 0) {
channel[i].active = false;
@@ -78,22 +87,15 @@ int i;
}
uint32 bCPU::hdma_addr(uint8 i) {
uint32 r;
r = channel[i].hdma_addr;
r |= (channel[i].srcbank << 16);
channel[i].hdma_addr++;
return r;
return (channel[i].srcbank << 16) | (channel[i].hdma_addr++);
}
uint32 bCPU::hdma_iaddr(uint8 i) {
uint32 r;
r = channel[i].hdma_iaddr;
r |= (channel[i].hdma_ibank << 16);
channel[i].hdma_iaddr++;
return r;
return (channel[i].hdma_ibank << 16) | (channel[i].hdma_iaddr++);
}
void bCPU::hdma_write(uint8 i, uint8 l, uint8 x) {
uint16 bCPU::hdma_mmio(uint8 i) {
uint8 l = channel[i].read_index;
uint16 index;
switch(channel[i].xfermode) {
case 0:index = 0; break; //0
@@ -106,90 +108,118 @@ uint16 index;
case 7:index = (l >> 1) & 1;break; //0,0,1,1 [3]
}
index = 0x2100 | ((channel[i].destaddr + index) & 0xff);
mem_bus->write(index, x);
return (0x2100 | ((channel[i].destaddr + index) & 0xff));
}
uint8 bCPU::hdma_read(uint8 i) {
if(channel[i].direction == DMA_MMIOTOCPU) {
return mem_bus->read(hdma_mmio(i));
} else if(!channel[i].hdma_indirect) {
return mem_bus->read(hdma_addr(i));
} else {
return mem_bus->read(hdma_iaddr(i));
}
}
void bCPU::hdma_write(uint8 i, uint8 x) {
if(channel[i].direction == DMA_CPUTOMMIO) {
mem_bus->write(hdma_mmio(i), x);
} else if(!channel[i].hdma_indirect) {
mem_bus->write(hdma_addr(i), x);
} else {
mem_bus->write(hdma_iaddr(i), x);
}
add_cycles(8);
hdma_add_cycles(8);
}
void bCPU::hdma_update(uint8 i) {
channel[i].hdma_line_counter = mem_bus->read(hdma_addr(i));
add_cycles(8);
hdma_add_cycles(8);
if(channel[i].hdma_indirect) {
channel[i].hdma_iaddr = mem_bus->read(hdma_addr(i)) << 8;
add_cycles(8);
hdma_add_cycles(8);
}
if(channel[i].hdma_line_counter == 0) {
channel[i].hdma_active = false;
channel[i].hdma_do_transfer = false;
return;
}
channel[i].hdma_do_transfer = true;
if(channel[i].hdma_indirect) {
channel[i].hdma_iaddr >>= 8;
channel[i].hdma_iaddr |= mem_bus->read(hdma_addr(i)) << 8;
add_cycles(8);
hdma_add_cycles(8);
}
}
void bCPU::hdma_run() {
int l, xferlen;
uint8 x, active_channels = 0;
static hdma_xferlen[8] = { 1, 2, 2, 4, 4, 4, 2, 4 };
static uint8 hdma_xferlen[8] = { 1, 2, 2, 4, 4, 4, 2, 4 };
for(int i=0;i<8;i++) {
if(channel[i].hdma_active == false)continue;
if(!channel[i].hdma_enabled || !channel[i].hdma_active)continue;
// add_cycles(8);
active_channels++;
if(channel[i].hdma_line_counter == 0) {
channel[i].hdma_line_counter = mem_bus->read(hdma_addr(i));
if(channel[i].hdma_line_counter == 0) {
channel[i].hdma_active = false;
continue;
}
if(channel[i].hdma_line_counter > 0x80) {
channel[i].hdma_repeat = true;
channel[i].hdma_line_counter -= 0x80;
} else {
channel[i].hdma_repeat = false;
}
channel[i].hdma_first_line = true;
if(channel[i].hdma_indirect == true) {
channel[i].hdma_iaddr = mem_bus->read(hdma_addr(i));
channel[i].hdma_iaddr |= mem_bus->read(hdma_addr(i)) << 8;
// add_cycles(16);
if(channel[i].hdma_do_transfer) {
int xferlen = hdma_xferlen[channel[i].xfermode];
for(channel[i].read_index=0;channel[i].read_index<xferlen;channel[i].read_index++) {
hdma_write(i, hdma_read(i));
}
}
channel[i].hdma_line_counter--;
if(channel[i].hdma_first_line == false && channel[i].hdma_repeat == false)continue;
channel[i].hdma_first_line = false;
xferlen = hdma_xferlen[channel[i].xfermode];
for(l=0;l<xferlen;l++) {
if(channel[i].hdma_indirect == false) {
x = mem_bus->read(hdma_addr(i));
} else {
x = mem_bus->read(hdma_iaddr(i));
}
hdma_write(i, l, x);
// add_cycles(8);
channel[i].hdma_do_transfer = !!(channel[i].hdma_line_counter & 0x80);
if((channel[i].hdma_line_counter & 0x7f) == 0) {
hdma_update(i);
}
}
if(active_channels != 0) {
// add_cycles(18);
}
}
void bCPU::hdma_initialize() {
uint8 active_channels = 0;
uint8 bCPU::hdma_enabled_channels() {
int r = 0;
for(int i=0;i<8;i++) {
//does this happen when $420c channel bit is clear?
channel[i].hdma_addr = channel[i].srcaddr;
channel[i].hdma_line_counter = 0x00;
if(channel[i].hdma_enabled)r++;
}
return r;
}
if(channel[i].hdma_enabled == false) {
channel[i].hdma_active = false;
continue;
}
uint8 bCPU::hdma_active_channels() {
int r = 0;
for(int i=0;i<8;i++) {
if(channel[i].hdma_enabled && channel[i].hdma_active)r++;
}
return r;
}
channel[i].hdma_active = true;
active_channels++;
/* hdmainit_activate()
* hdma_activate()
*
* Functions are called by CPU timing routine
* when an HDMA event (init or run) occurs.
*/
if(channel[i].hdma_indirect == false) {
add_cycles(8);
} else {
add_cycles(24);
}
void bCPU::hdmainit_activate() {
for(int i=0;i<8;i++) {
channel[i].hdma_active = false;
channel[i].hdma_do_transfer = false;
}
if(active_channels != 0) {
add_cycles(18);
if(hdma_enabled_channels() != 0) {
status.hdma_state = HDMASTATE_IDMASYNC;
run_state.hdma = true;
}
}
void bCPU::hdma_activate() {
if(hdma_active_channels() != 0) {
status.hdma_state = HDMASTATE_DMASYNC;
run_state.hdma = true;
}
}
@@ -206,15 +236,15 @@ void bCPU::dma_reset() {
channel[i].xfermode = 0;
channel[i].destaddr = 0;
channel[i].srcaddr = 0;
channel[i].xfersize = 0;
channel[i].xfersize = 0x0000;
//xfersize and hdma_iaddr are of union { uint16 };
//channel[i].hdma_iaddr = 0x0000;
channel[i].hdma_ibank = 0;
channel[i].hdma_addr = 0x0000;
channel[i].hdma_line_counter = 0x00;
channel[i].hdma_unknown = 0x00;
channel[i].hdma_active = false;
channel[i].hdma_first_line = false;
channel[i].hdma_repeat = false;
channel[i].hdma_iaddr = 0x0000;
channel[i].hdma_do_transfer = false;
}
}

View File

@@ -1,50 +1,157 @@
inline void bCPU::cycle_edge() {
int c, n, z;
if(status.dma_state != DMASTATE_STOP) {
void bCPU::last_cycle() {
time.nmi_pending = nmi_test();
time.irq_pending = irq_test();
}
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) {
snes->notify(SNES::CPU_EXEC_OPCODE_BEGIN);
status.opcode = op_read();
status.cycle_pos = 1;
} else {
(this->*optbl[status.opcode])();
if(status.cycle_pos == 0) {
snes->notify(SNES::CPU_EXEC_OPCODE_END);
}
return;
}
(this->*optbl[status.opcode])();
if(status.cycle_pos == 0) {
snes->notify(SNES::CPU_EXEC_OPCODE_END);
}
}

67
src/cpu/bcpu/bcpu_int.cpp Normal file
View File

@@ -0,0 +1,67 @@
/*
[IRQ cycles]
[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_run() {
//WDC documentation is incorrect, first cycle
//is a memory read fetch from PBR:PC
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;
run_state.wai = false;
return true;
}
bool bCPU::irq_test() {
if(time.irq_transition == 1)goto _true;
if(time.irq_read == 0) {
if(time.irq_line == 1 && (irq_trigger_pos_match(0) || irq_trigger_pos_match(2))) {
return false;
}
goto _true;
}
if(time.irq_line == 0) {
time.irq_line = 1;
goto _true;
}
return false;
_true:
time.irq_transition = 0;
run_state.wai = false;
if(regs.p.i)return false;
return true;
}

View File

@@ -2,9 +2,11 @@ void bCPU::mmio_reset() {
//$2181-$2183
status.wram_addr = 0x000000;
//$4016
//$4016-$4017
status.joypad1_strobe_value = 0x00;
status.joypad2_strobe_value = 0x00;
status.joypad1_read_pos = 0;
status.joypad2_read_pos = 0;
//$4200
status.nmi_enabled = false;
@@ -42,18 +44,17 @@ uint8 r;
}
//JOYSER0
/*
The joypad contains a small bit shifter that has 16 bits.
Reading from 4016 reads one bit from this buffer, then moves
the buffer left one, and adds a '1' to the rightmost bit.
Writing a one to $4016 will fill the buffer with the current
joypad button states, and lock the bit shifter at position
zero. All reads will be the first buffer state, or 'B'.
A zero must be written back to $4016 to unlock the buffer,
so that reads will increment the bit shifting position.
*/
//7-2 = MDR
//1-0 = Joypad serial data
/* The joypad contains a small bit shifter that has 16 bits.
* Reading from 4016 reads one bit from this buffer, then moves
* the buffer left one, and adds a '1' to the rightmost bit.
* Writing a one to $4016 will fill the buffer with the current
* joypad button states, and lock the bit shifter at position
* zero. All reads will be the first buffer state, or 'B'.
* A zero must be written back to $4016 to unlock the buffer,
* so that reads will increment the bit shifting position.
*/
uint8 bCPU::mmio_r4016() {
uint8 r;
r = regs.mdr & 0xfc;
@@ -92,48 +93,45 @@ 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;
}
//RDNMI
/* $4210 bit 7 (NMI triggered bit) is set at:
* V=225/240,HC>=2,
* V>225
* The bit is only set once per NMI trigger, so
* subsequent reads return this bit as being clear.
* There is but one exception: if the $4210 read
* occurs at *exactly* V=225/240,HC==2, then $4210
* bit 7 will be set, and the next read will also
* have this bit set.
*/
//7 = NMI acknowledge
//6-4 = MDR
//3-0 = CPU (5a22) version
uint8 bCPU::mmio_r4210() {
uint8 r;
uint16 v, h, hc, vs;
r = regs.mdr & 0x70;
uint8 r;
r = regs.mdr & 0x70;
r |= uint8(!time.nmi_read) << 7;
v = vcounter();
h = hcounter();
hc = hcycles();
vs = (overscan()?239:224);
if(status.nmi_read == false) {
if(
(v == (vs + 1) && hc >= 2) ||
(v > (vs + 1))
) {
r |= 0x80;
//test for special case where NMI read not raised
if(v != (vs + 1) || hc != 2) {
status.nmi_read = true;
}
}
if(!nmi_trigger_pos_match(0) && !nmi_trigger_pos_match(2)) {
time.nmi_read = 1;
}
r |= 0x02; //cpu version number
r |= (cpu_version & 0x0f);
return r;
}
@@ -142,10 +140,15 @@ uint16 v, h, hc, vs;
//6-0 = MDR
uint8 bCPU::mmio_r4211() {
uint8 r;
r = regs.mdr & 0x7f;
if(status.irq_exec == true)r |= 0x80;
status.irq_exec = false;
status.irq_read = true;
r = regs.mdr & 0x7f;
r |= uint8(!time.irq_read) << 7;
if(!irq_trigger_pos_match(0) && !irq_trigger_pos_match(2)) {
time.irq_read = 1;
time.irq_line = 1;
time.irq_transition = 0;
}
return r;
}
@@ -155,23 +158,20 @@ uint8 r;
//5-1 = MDR
//0 = joypad ready
uint8 bCPU::mmio_r4212() {
uint8 r;
uint16 v, h, hc, vs;
uint8 r;
r = regs.mdr & 0x3e;
v = vcounter();
h = hcounter();
hc = hcycles();
vs = (overscan()?239:224);
uint16 vs = overscan() ? 240 : 225;
//auto joypad polling
if(v >= (vs + 1) && v <= (vs + 3))r |= 0x01;
if(time.v >= vs && time.v <= (vs + 2))r |= 0x01;
//hblank
if(hc <= 2 || hc >= 1096)r |= 0x40;
if(time.hc <= 2 || time.hc >= 1096)r |= 0x40;
//vblank
if(v >= (vs + 1))r |= 0x80;
if(time.v >= vs)r |= 0x80;
return r;
}
@@ -230,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;
@@ -291,9 +321,7 @@ uint8 bCPU::mmio_r43xb(uint8 i) {
}
uint8 bCPUMMIO::read(uint32 addr) {
uint8 i;
//cpu->sync();
uint i;
//APU
if(addr >= 0x2140 && addr <= 0x217f) {
return apu->port_read(addr & 3);
@@ -336,7 +364,9 @@ uint8 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 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;
@@ -369,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);
@@ -383,14 +422,20 @@ void bCPU::mmio_w4200(uint8 value) {
status.hirq_enabled = !!(value & 0x10);
status.auto_joypad_poll = !!(value & 0x01);
if(status.nmi_enabled == false) {
status.nmi_read = false;
if(time.nmi_read == 0) {
if(time.nmi_line == 1 && !status.nmi_enabled == 0) {
time.nmi_transition = 1;
}
time.nmi_line = !status.nmi_enabled;
}
status.irq_exec = false;
if(status.virq_enabled == true || status.hirq_enabled == true) {
status.irq_read = false;
if(status.virq_enabled == false && status.hirq_enabled == false) {
time.irq_line = 1;
time.irq_read = 1;
time.irq_transition = 0;
}
update_interrupts();
}
//WRIO
@@ -424,38 +469,39 @@ void bCPU::mmio_w4205(uint8 value) {
//WRDIVB
void bCPU::mmio_w4206(uint8 value) {
status.div_b = value;
status.r4214 = (status.div_b)?status.div_a / status.div_b : 0xffff;
status.r4216 = (status.div_b)?status.div_a % status.div_b : status.div_a;
status.r4214 = (status.div_b) ? status.div_a / status.div_b : 0xffff;
status.r4216 = (status.div_b) ? status.div_a % status.div_b : status.div_a;
}
//HTIMEL
void bCPU::mmio_w4207(uint8 value) {
status.hirq_pos = (status.hirq_pos & 0xff00) | value;
status.irq_read = false;
update_interrupts();
}
//HTIMEH
void bCPU::mmio_w4208(uint8 value) {
status.hirq_pos = (status.hirq_pos & 0x00ff) | (value << 8);
status.irq_read = false;
update_interrupts();
}
//VTIMEL
void bCPU::mmio_w4209(uint8 value) {
status.virq_pos = (status.virq_pos & 0xff00) | value;
status.irq_read = false;
update_interrupts();
}
//VTIMEH
void bCPU::mmio_w420a(uint8 value) {
status.virq_pos = (status.virq_pos & 0x00ff) | (value << 8);
status.irq_read = false;
update_interrupts();
}
//DMAEN
void bCPU::mmio_w420b(uint8 value) {
int len;
if(value != 0x00) {
run_state.dma = true;
status.dma_state = DMASTATE_DMASYNC;
}
@@ -479,7 +525,7 @@ void bCPU::mmio_w420c(uint8 value) {
//MEMSEL
void bCPU::mmio_w420d(uint8 value) {
mem_bus->fastROM = value & 1;
mem_bus->set_speed(value & 1);
}
//DMAPx
@@ -549,8 +595,6 @@ void bCPU::mmio_w43xb(uint8 value, uint8 i) {
void bCPUMMIO::write(uint32 addr, uint8 value) {
uint8 i;
//cpu->sync();
//APU
if(addr >= 0x2140 && addr <= 0x217f) {
cpu->port_write(addr & 3, value);
@@ -586,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

View File

@@ -186,7 +186,6 @@ void op_sta_idpy();
void op_sta_ildpy();
void op_sta_sr();
void op_sta_isry();
void op_bra();
void op_bcc();
void op_bcs();
void op_bne();
@@ -195,6 +194,7 @@ void op_bpl();
void op_bmi();
void op_bvc();
void op_bvs();
void op_bra();
void op_brl();
void op_jmp_addr();
void op_jmp_long();

View File

@@ -1,6 +1,7 @@
void bCPU::op_nop() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
status.cycle_pos = 0;
break;
@@ -10,6 +11,7 @@ void bCPU::op_nop() {
void bCPU::op_wdm() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
op_read();
status.cycle_pos = 0;
break;
@@ -22,6 +24,7 @@ void bCPU::op_xba() {
cpu_io();
break;
case 2:
last_cycle();
cpu_io();
regs.a.l ^= regs.a.h;
regs.a.h ^= regs.a.l;
@@ -54,6 +57,7 @@ void bCPU::op_mvn() {
else { regs.x.w++; regs.y.w++; }
break;
case 6:
last_cycle();
cpu_io();
if(regs.a.w--)regs.pc.w -= 3;
status.cycle_pos = 0;
@@ -82,6 +86,7 @@ void bCPU::op_mvp() {
else { regs.x.w--; regs.y.w--; }
break;
case 6:
last_cycle();
cpu_io();
if(regs.a.w--)regs.pc.w -= 3;
status.cycle_pos = 0;
@@ -111,6 +116,7 @@ void bCPU::op_brk() {
rd.l = op_read(OPMODE_LONG, (regs.e)?0xfffe:0xffe6);
break;
case 7:
last_cycle();
rd.h = op_read(OPMODE_LONG, (regs.e)?0xffff:0xffe7);
regs.pc.b = 0x00;
regs.pc.w = rd.w;
@@ -143,6 +149,7 @@ void bCPU::op_cop() {
rd.l = op_read(OPMODE_LONG, (regs.e)?0xfff4:0xffe4);
break;
case 7:
last_cycle();
rd.h = op_read(OPMODE_LONG, (regs.e)?0xfff5:0xffe5);
regs.pc.b = 0x00;
regs.pc.w = rd.w;
@@ -157,9 +164,10 @@ 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();
cpu_io();
regs.pc.w--;
status.cycle_pos = 0;
@@ -171,11 +179,15 @@ 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();
regs.pc.w--;
if(run_state.wai) {
//this can be cleared within last_cycle()
regs.pc.w--;
}
status.cycle_pos = 0;
break;
}
@@ -184,6 +196,7 @@ void bCPU::op_wai() {
void bCPU::op_xce() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
bool c = regs.p.c;
regs.p.c = regs.e;
@@ -202,6 +215,7 @@ void bCPU::op_xce() {
void bCPU::op_clc() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
regs.p.c = 0;
status.cycle_pos = 0;
@@ -212,6 +226,7 @@ void bCPU::op_clc() {
void bCPU::op_cld() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
regs.p.d = 0;
status.cycle_pos = 0;
@@ -222,6 +237,7 @@ void bCPU::op_cld() {
void bCPU::op_cli() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
regs.p.i = 0;
status.cycle_pos = 0;
@@ -232,6 +248,7 @@ void bCPU::op_cli() {
void bCPU::op_clv() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
regs.p.v = 0;
status.cycle_pos = 0;
@@ -242,6 +259,7 @@ void bCPU::op_clv() {
void bCPU::op_sec() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
regs.p.c = 1;
status.cycle_pos = 0;
@@ -252,6 +270,7 @@ void bCPU::op_sec() {
void bCPU::op_sed() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
regs.p.d = 1;
status.cycle_pos = 0;
@@ -262,6 +281,7 @@ void bCPU::op_sed() {
void bCPU::op_sei() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
regs.p.i = 1;
status.cycle_pos = 0;
@@ -275,6 +295,7 @@ void bCPU::op_rep() {
rd.l = op_read();
break;
case 2:
last_cycle();
cpu_io();
regs.p &=~ rd.l;
if(regs.e)regs.p |= 0x30;
@@ -293,6 +314,7 @@ void bCPU::op_sep() {
rd.l = op_read();
break;
case 2:
last_cycle();
cpu_io();
regs.p |= rd.l;
if(regs.e)regs.p |= 0x30;
@@ -308,6 +330,7 @@ void bCPU::op_sep() {
void bCPU::op_tax() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
if(regs.p.x) {
regs.x.l = regs.a.l;
@@ -326,6 +349,7 @@ void bCPU::op_tax() {
void bCPU::op_tay() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
if(regs.p.x) {
regs.y.l = regs.a.l;
@@ -344,6 +368,7 @@ void bCPU::op_tay() {
void bCPU::op_txa() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
if(regs.p.m) {
regs.a.l = regs.x.l;
@@ -362,6 +387,7 @@ void bCPU::op_txa() {
void bCPU::op_txy() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
if(regs.p.x) {
regs.y.l = regs.x.l;
@@ -380,6 +406,7 @@ void bCPU::op_txy() {
void bCPU::op_tya() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
if(regs.p.m) {
regs.a.l = regs.y.l;
@@ -398,6 +425,7 @@ void bCPU::op_tya() {
void bCPU::op_tyx() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
if(regs.p.x) {
regs.x.l = regs.y.l;
@@ -416,6 +444,7 @@ void bCPU::op_tyx() {
void bCPU::op_tcd() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
regs.d.w = regs.a.w;
regs.p.n = !!(regs.d.w & 0x8000);
@@ -428,6 +457,7 @@ void bCPU::op_tcd() {
void bCPU::op_tcs() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
regs.s.w = regs.a.w;
if(regs.e)regs.s.h = 0x01;
@@ -439,6 +469,7 @@ void bCPU::op_tcs() {
void bCPU::op_tdc() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
regs.a.w = regs.d.w;
regs.p.n = !!(regs.a.w & 0x8000);
@@ -451,6 +482,7 @@ void bCPU::op_tdc() {
void bCPU::op_tsc() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
regs.a.w = regs.s.w;
if(regs.e) {
@@ -468,6 +500,7 @@ void bCPU::op_tsc() {
void bCPU::op_tsx() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
if(regs.p.x) {
regs.x.l = regs.s.l;
@@ -486,6 +519,7 @@ void bCPU::op_tsx() {
void bCPU::op_txs() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
if(regs.e) {
regs.s.l = regs.x.l;
@@ -511,6 +545,7 @@ void bCPU::op_pha() {
stack_write(regs.a.h);
break;
case 3:
last_cycle();
stack_write(regs.a.l);
status.cycle_pos = 0;
break;
@@ -527,6 +562,7 @@ void bCPU::op_phx() {
stack_write(regs.x.h);
break;
case 3:
last_cycle();
stack_write(regs.x.l);
status.cycle_pos = 0;
break;
@@ -543,6 +579,7 @@ void bCPU::op_phy() {
stack_write(regs.y.h);
break;
case 3:
last_cycle();
stack_write(regs.y.l);
status.cycle_pos = 0;
break;
@@ -559,6 +596,7 @@ void bCPU::op_phd() {
stack_write(regs. d.h);
break;
case 3:
last_cycle();
stack_write(regs. d.l);
status.cycle_pos = 0;
break;
@@ -571,6 +609,7 @@ void bCPU::op_phb() {
cpu_io();
break;
case 2:
last_cycle();
stack_write(regs.db);
status.cycle_pos = 0;
break;
@@ -583,6 +622,7 @@ void bCPU::op_phk() {
cpu_io();
break;
case 2:
last_cycle();
stack_write(regs.pc.b);
status.cycle_pos = 0;
break;
@@ -595,6 +635,7 @@ void bCPU::op_php() {
cpu_io();
break;
case 2:
last_cycle();
stack_write(regs.p);
status.cycle_pos = 0;
break;
@@ -610,6 +651,7 @@ void bCPU::op_pla() {
cpu_io();
break;
case 3:
if(regs.p.m)last_cycle();
regs.a.l = stack_read();
if(regs.p.m) {
regs.p.n = !!(regs.a.l & 0x80);
@@ -618,6 +660,7 @@ void bCPU::op_pla() {
}
break;
case 4:
last_cycle();
regs.a.h = stack_read();
regs.p.n = !!(regs.a.w & 0x8000);
regs.p.z = (regs.a.w == 0);
@@ -635,6 +678,7 @@ void bCPU::op_plx() {
cpu_io();
break;
case 3:
if(regs.p.x)last_cycle();
regs.x.l = stack_read();
if(regs.p.x) {
regs.p.n = !!(regs.x.l & 0x80);
@@ -643,6 +687,7 @@ void bCPU::op_plx() {
}
break;
case 4:
last_cycle();
regs.x.h = stack_read();
regs.p.n = !!(regs.x.w & 0x8000);
regs.p.z = (regs.x.w == 0);
@@ -660,6 +705,7 @@ void bCPU::op_ply() {
cpu_io();
break;
case 3:
if(regs.p.x)last_cycle();
regs.y.l = stack_read();
if(regs.p.x) {
regs.p.n = !!(regs.y.l & 0x80);
@@ -668,6 +714,7 @@ void bCPU::op_ply() {
}
break;
case 4:
last_cycle();
regs.y.h = stack_read();
regs.p.n = !!(regs.y.w & 0x8000);
regs.p.z = (regs.y.w == 0);
@@ -685,6 +732,7 @@ void bCPU::op_pld() {
cpu_io();
break;
case 3:
if(0)last_cycle();
regs. d.l = stack_read();
if(0) {
regs.p.n = !!(regs. d.l & 0x80);
@@ -693,6 +741,7 @@ void bCPU::op_pld() {
}
break;
case 4:
last_cycle();
regs. d.h = stack_read();
regs.p.n = !!(regs. d.w & 0x8000);
regs.p.z = (regs. d.w == 0);
@@ -710,6 +759,7 @@ void bCPU::op_plb() {
cpu_io();
break;
case 3:
last_cycle();
regs.db = stack_read();
regs.p.n = !!(regs.db & 0x80);
regs.p.z = (regs.db == 0);
@@ -727,6 +777,7 @@ void bCPU::op_plp() {
cpu_io();
break;
case 3:
last_cycle();
regs.p = stack_read();
if(regs.e)regs.p |= 0x30;
if(regs.p.x) {
@@ -750,6 +801,7 @@ void bCPU::op_pea() {
stack_write(aa.h);
break;
case 4:
last_cycle();
stack_write(aa.l);
status.cycle_pos = 0;
break;
@@ -774,6 +826,7 @@ void bCPU::op_pei() {
stack_write(aa.h);
break;
case 6:
last_cycle();
stack_write(aa.l);
status.cycle_pos = 0;
break;
@@ -796,6 +849,7 @@ void bCPU::op_per() {
stack_write(rd.h);
break;
case 5:
last_cycle();
stack_write(rd.l);
status.cycle_pos = 0;
break;

View File

@@ -1,27 +1,7 @@
void bCPU::op_bra() {
switch(status.cycle_pos++) {
case 1:
rd.l = op_read();
if(1) {
aa.w = regs.pc.d + (int8)rd.l;
regs.pc.w = aa.w;
} else {
status.cycle_pos = 0;
}
break;
case 2:
cpu_c6(aa.w);
break;
case 3:
cpu_io();
status.cycle_pos = 0;
break;
}
}
void bCPU::op_bcc() {
switch(status.cycle_pos++) {
case 1:
if(!!regs.p.c)last_cycle();
rd.l = op_read();
if(!regs.p.c) {
aa.w = regs.pc.d + (int8)rd.l;
@@ -34,6 +14,7 @@ void bCPU::op_bcc() {
cpu_c6(aa.w);
break;
case 3:
last_cycle();
cpu_io();
status.cycle_pos = 0;
break;
@@ -43,6 +24,7 @@ void bCPU::op_bcc() {
void bCPU::op_bcs() {
switch(status.cycle_pos++) {
case 1:
if(!regs.p.c)last_cycle();
rd.l = op_read();
if(regs.p.c) {
aa.w = regs.pc.d + (int8)rd.l;
@@ -55,6 +37,7 @@ void bCPU::op_bcs() {
cpu_c6(aa.w);
break;
case 3:
last_cycle();
cpu_io();
status.cycle_pos = 0;
break;
@@ -64,6 +47,7 @@ void bCPU::op_bcs() {
void bCPU::op_bne() {
switch(status.cycle_pos++) {
case 1:
if(!!regs.p.z)last_cycle();
rd.l = op_read();
if(!regs.p.z) {
aa.w = regs.pc.d + (int8)rd.l;
@@ -76,6 +60,7 @@ void bCPU::op_bne() {
cpu_c6(aa.w);
break;
case 3:
last_cycle();
cpu_io();
status.cycle_pos = 0;
break;
@@ -85,6 +70,7 @@ void bCPU::op_bne() {
void bCPU::op_beq() {
switch(status.cycle_pos++) {
case 1:
if(!regs.p.z)last_cycle();
rd.l = op_read();
if(regs.p.z) {
aa.w = regs.pc.d + (int8)rd.l;
@@ -97,6 +83,7 @@ void bCPU::op_beq() {
cpu_c6(aa.w);
break;
case 3:
last_cycle();
cpu_io();
status.cycle_pos = 0;
break;
@@ -106,6 +93,7 @@ void bCPU::op_beq() {
void bCPU::op_bpl() {
switch(status.cycle_pos++) {
case 1:
if(!!regs.p.n)last_cycle();
rd.l = op_read();
if(!regs.p.n) {
aa.w = regs.pc.d + (int8)rd.l;
@@ -118,6 +106,7 @@ void bCPU::op_bpl() {
cpu_c6(aa.w);
break;
case 3:
last_cycle();
cpu_io();
status.cycle_pos = 0;
break;
@@ -127,6 +116,7 @@ void bCPU::op_bpl() {
void bCPU::op_bmi() {
switch(status.cycle_pos++) {
case 1:
if(!regs.p.n)last_cycle();
rd.l = op_read();
if(regs.p.n) {
aa.w = regs.pc.d + (int8)rd.l;
@@ -139,6 +129,7 @@ void bCPU::op_bmi() {
cpu_c6(aa.w);
break;
case 3:
last_cycle();
cpu_io();
status.cycle_pos = 0;
break;
@@ -148,6 +139,7 @@ void bCPU::op_bmi() {
void bCPU::op_bvc() {
switch(status.cycle_pos++) {
case 1:
if(!!regs.p.v)last_cycle();
rd.l = op_read();
if(!regs.p.v) {
aa.w = regs.pc.d + (int8)rd.l;
@@ -160,6 +152,7 @@ void bCPU::op_bvc() {
cpu_c6(aa.w);
break;
case 3:
last_cycle();
cpu_io();
status.cycle_pos = 0;
break;
@@ -169,6 +162,7 @@ void bCPU::op_bvc() {
void bCPU::op_bvs() {
switch(status.cycle_pos++) {
case 1:
if(!regs.p.v)last_cycle();
rd.l = op_read();
if(regs.p.v) {
aa.w = regs.pc.d + (int8)rd.l;
@@ -181,6 +175,25 @@ void bCPU::op_bvs() {
cpu_c6(aa.w);
break;
case 3:
last_cycle();
cpu_io();
status.cycle_pos = 0;
break;
}
}
void bCPU::op_bra() {
switch(status.cycle_pos++) {
case 1:
rd.l = op_read();
aa.w = regs.pc.d + (int8)rd.l;
regs.pc.w = aa.w;
break;
case 2:
cpu_c6(aa.w);
break;
case 3:
last_cycle();
cpu_io();
status.cycle_pos = 0;
break;
@@ -196,6 +209,7 @@ void bCPU::op_brl() {
rd.h = op_read();
break;
case 3:
last_cycle();
cpu_io();
regs.pc.w = regs.pc.d + (int16)rd.w;
status.cycle_pos = 0;
@@ -209,6 +223,7 @@ void bCPU::op_jmp_addr() {
rd.l = op_read();
break;
case 2:
last_cycle();
rd.h = op_read();
regs.pc.w = rd.w;
status.cycle_pos = 0;
@@ -225,6 +240,7 @@ void bCPU::op_jmp_long() {
rd.h = op_read();
break;
case 3:
last_cycle();
rd.b = op_read();
regs.pc.d = rd.d & 0xffffff;
status.cycle_pos = 0;
@@ -244,6 +260,7 @@ void bCPU::op_jmp_iaddr() {
rd.l = op_read(OPMODE_ADDR, aa.w);
break;
case 4:
last_cycle();
rd.h = op_read(OPMODE_ADDR, aa.w + 1);
regs.pc.w = rd.w;
status.cycle_pos = 0;
@@ -266,6 +283,7 @@ void bCPU::op_jmp_iaddrx() {
rd.l = op_read(OPMODE_PBR, aa.w + regs.x.w);
break;
case 5:
last_cycle();
rd.h = op_read(OPMODE_PBR, aa.w + regs.x.w + 1);
regs.pc.w = rd.w;
status.cycle_pos = 0;
@@ -288,6 +306,7 @@ void bCPU::op_jmp_iladdr() {
rd.h = op_read(OPMODE_ADDR, aa.w + 1);
break;
case 5:
last_cycle();
rd.b = op_read(OPMODE_ADDR, aa.w + 2);
regs.pc.d = rd.d & 0xffffff;
status.cycle_pos = 0;
@@ -311,6 +330,7 @@ void bCPU::op_jsr_addr() {
stack_write(regs.pc.h);
break;
case 5:
last_cycle();
stack_write(regs.pc.l);
regs.pc.w = aa.w;
status.cycle_pos = 0;
@@ -340,6 +360,7 @@ void bCPU::op_jsr_long() {
stack_write(regs.pc.h);
break;
case 7:
last_cycle();
stack_write(regs.pc.l);
regs.pc.d = aa.d & 0xffffff;
status.cycle_pos = 0;
@@ -368,6 +389,7 @@ void bCPU::op_jsr_iaddrx() {
rd.l = op_read(OPMODE_PBR, aa.w + regs.x.w);
break;
case 7:
last_cycle();
rd.h = op_read(OPMODE_PBR, aa.w + regs.x.w + 1);
regs.pc.w = rd.w;
status.cycle_pos = 0;
@@ -402,6 +424,7 @@ void bCPU::op_rti() {
}
break;
case 6:
last_cycle();
rd.b = stack_read();
regs.pc.d = rd.d & 0xffffff;
status.cycle_pos = 0;
@@ -424,6 +447,7 @@ void bCPU::op_rts() {
rd.h = stack_read();
break;
case 5:
last_cycle();
cpu_io();
regs.pc.w = rd.w;
regs.pc.w++;
@@ -447,6 +471,7 @@ void bCPU::op_rtl() {
rd.h = stack_read();
break;
case 5:
last_cycle();
rd.b = stack_read();
regs.pc.d = rd.d & 0xffffff;
regs.pc.w++;

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
void bCPU::op_inc() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
if(regs.p.m) {
regs.a.l++;
@@ -19,6 +20,7 @@ void bCPU::op_inc() {
void bCPU::op_inx() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
if(regs.p.x) {
regs.x.l++;
@@ -37,6 +39,7 @@ void bCPU::op_inx() {
void bCPU::op_iny() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
if(regs.p.x) {
regs.y.l++;
@@ -55,6 +58,7 @@ void bCPU::op_iny() {
void bCPU::op_dec() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
if(regs.p.m) {
regs.a.l--;
@@ -73,6 +77,7 @@ void bCPU::op_dec() {
void bCPU::op_dex() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
if(regs.p.x) {
regs.x.l--;
@@ -91,6 +96,7 @@ void bCPU::op_dex() {
void bCPU::op_dey() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
if(regs.p.x) {
regs.y.l--;
@@ -109,6 +115,7 @@ void bCPU::op_dey() {
void bCPU::op_asl() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
if(regs.p.m) {
regs.p.c = !!(regs.a.l & 0x80);
@@ -129,6 +136,7 @@ void bCPU::op_asl() {
void bCPU::op_lsr() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
if(regs.p.m) {
regs.p.c = regs.a.l & 1;
@@ -149,6 +157,7 @@ void bCPU::op_lsr() {
void bCPU::op_rol() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
uint16 c = regs.p.c;
if(regs.p.m) {
@@ -172,6 +181,7 @@ void bCPU::op_rol() {
void bCPU::op_ror() {
switch(status.cycle_pos++) {
case 1:
last_cycle();
cpu_io();
uint16 c;
if(regs.p.m) {
@@ -218,6 +228,7 @@ void bCPU::op_inc_addr() {
op_write(OPMODE_DBR, aa.w + 1, rd.h);
break;
case 7:
last_cycle();
op_write(OPMODE_DBR, aa.w, rd.l);
status.cycle_pos = 0;
break;
@@ -248,6 +259,7 @@ void bCPU::op_dec_addr() {
op_write(OPMODE_DBR, aa.w + 1, rd.h);
break;
case 7:
last_cycle();
op_write(OPMODE_DBR, aa.w, rd.l);
status.cycle_pos = 0;
break;
@@ -278,6 +290,7 @@ void bCPU::op_asl_addr() {
op_write(OPMODE_DBR, aa.w + 1, rd.h);
break;
case 7:
last_cycle();
op_write(OPMODE_DBR, aa.w, rd.l);
status.cycle_pos = 0;
break;
@@ -308,6 +321,7 @@ void bCPU::op_lsr_addr() {
op_write(OPMODE_DBR, aa.w + 1, rd.h);
break;
case 7:
last_cycle();
op_write(OPMODE_DBR, aa.w, rd.l);
status.cycle_pos = 0;
break;
@@ -338,6 +352,7 @@ void bCPU::op_rol_addr() {
op_write(OPMODE_DBR, aa.w + 1, rd.h);
break;
case 7:
last_cycle();
op_write(OPMODE_DBR, aa.w, rd.l);
status.cycle_pos = 0;
break;
@@ -368,6 +383,7 @@ void bCPU::op_ror_addr() {
op_write(OPMODE_DBR, aa.w + 1, rd.h);
break;
case 7:
last_cycle();
op_write(OPMODE_DBR, aa.w, rd.l);
status.cycle_pos = 0;
break;
@@ -398,6 +414,7 @@ void bCPU::op_trb_addr() {
op_write(OPMODE_DBR, aa.w + 1, rd.h);
break;
case 7:
last_cycle();
op_write(OPMODE_DBR, aa.w, rd.l);
status.cycle_pos = 0;
break;
@@ -428,6 +445,7 @@ void bCPU::op_tsb_addr() {
op_write(OPMODE_DBR, aa.w + 1, rd.h);
break;
case 7:
last_cycle();
op_write(OPMODE_DBR, aa.w, rd.l);
status.cycle_pos = 0;
break;
@@ -461,6 +479,7 @@ void bCPU::op_inc_addrx() {
op_write(OPMODE_DBR, aa.w + regs.x.w + 1, rd.h);
break;
case 8:
last_cycle();
op_write(OPMODE_DBR, aa.w + regs.x.w, rd.l);
status.cycle_pos = 0;
break;
@@ -494,6 +513,7 @@ void bCPU::op_dec_addrx() {
op_write(OPMODE_DBR, aa.w + regs.x.w + 1, rd.h);
break;
case 8:
last_cycle();
op_write(OPMODE_DBR, aa.w + regs.x.w, rd.l);
status.cycle_pos = 0;
break;
@@ -527,6 +547,7 @@ void bCPU::op_asl_addrx() {
op_write(OPMODE_DBR, aa.w + regs.x.w + 1, rd.h);
break;
case 8:
last_cycle();
op_write(OPMODE_DBR, aa.w + regs.x.w, rd.l);
status.cycle_pos = 0;
break;
@@ -560,6 +581,7 @@ void bCPU::op_lsr_addrx() {
op_write(OPMODE_DBR, aa.w + regs.x.w + 1, rd.h);
break;
case 8:
last_cycle();
op_write(OPMODE_DBR, aa.w + regs.x.w, rd.l);
status.cycle_pos = 0;
break;
@@ -593,6 +615,7 @@ void bCPU::op_rol_addrx() {
op_write(OPMODE_DBR, aa.w + regs.x.w + 1, rd.h);
break;
case 8:
last_cycle();
op_write(OPMODE_DBR, aa.w + regs.x.w, rd.l);
status.cycle_pos = 0;
break;
@@ -626,6 +649,7 @@ void bCPU::op_ror_addrx() {
op_write(OPMODE_DBR, aa.w + regs.x.w + 1, rd.h);
break;
case 8:
last_cycle();
op_write(OPMODE_DBR, aa.w + regs.x.w, rd.l);
status.cycle_pos = 0;
break;
@@ -656,6 +680,7 @@ void bCPU::op_inc_dp() {
op_write(OPMODE_DP, dp + 1, rd.h);
break;
case 7:
last_cycle();
op_write(OPMODE_DP, dp, rd.l);
status.cycle_pos = 0;
break;
@@ -686,6 +711,7 @@ void bCPU::op_dec_dp() {
op_write(OPMODE_DP, dp + 1, rd.h);
break;
case 7:
last_cycle();
op_write(OPMODE_DP, dp, rd.l);
status.cycle_pos = 0;
break;
@@ -716,6 +742,7 @@ void bCPU::op_asl_dp() {
op_write(OPMODE_DP, dp + 1, rd.h);
break;
case 7:
last_cycle();
op_write(OPMODE_DP, dp, rd.l);
status.cycle_pos = 0;
break;
@@ -746,6 +773,7 @@ void bCPU::op_lsr_dp() {
op_write(OPMODE_DP, dp + 1, rd.h);
break;
case 7:
last_cycle();
op_write(OPMODE_DP, dp, rd.l);
status.cycle_pos = 0;
break;
@@ -776,6 +804,7 @@ void bCPU::op_rol_dp() {
op_write(OPMODE_DP, dp + 1, rd.h);
break;
case 7:
last_cycle();
op_write(OPMODE_DP, dp, rd.l);
status.cycle_pos = 0;
break;
@@ -806,6 +835,7 @@ void bCPU::op_ror_dp() {
op_write(OPMODE_DP, dp + 1, rd.h);
break;
case 7:
last_cycle();
op_write(OPMODE_DP, dp, rd.l);
status.cycle_pos = 0;
break;
@@ -836,6 +866,7 @@ void bCPU::op_trb_dp() {
op_write(OPMODE_DP, dp + 1, rd.h);
break;
case 7:
last_cycle();
op_write(OPMODE_DP, dp, rd.l);
status.cycle_pos = 0;
break;
@@ -866,6 +897,7 @@ void bCPU::op_tsb_dp() {
op_write(OPMODE_DP, dp + 1, rd.h);
break;
case 7:
last_cycle();
op_write(OPMODE_DP, dp, rd.l);
status.cycle_pos = 0;
break;
@@ -899,6 +931,7 @@ void bCPU::op_inc_dpx() {
op_write(OPMODE_DP, dp + regs.x.w + 1, rd.h);
break;
case 8:
last_cycle();
op_write(OPMODE_DP, dp + regs.x.w, rd.l);
status.cycle_pos = 0;
break;
@@ -932,6 +965,7 @@ void bCPU::op_dec_dpx() {
op_write(OPMODE_DP, dp + regs.x.w + 1, rd.h);
break;
case 8:
last_cycle();
op_write(OPMODE_DP, dp + regs.x.w, rd.l);
status.cycle_pos = 0;
break;
@@ -965,6 +999,7 @@ void bCPU::op_asl_dpx() {
op_write(OPMODE_DP, dp + regs.x.w + 1, rd.h);
break;
case 8:
last_cycle();
op_write(OPMODE_DP, dp + regs.x.w, rd.l);
status.cycle_pos = 0;
break;
@@ -998,6 +1033,7 @@ void bCPU::op_lsr_dpx() {
op_write(OPMODE_DP, dp + regs.x.w + 1, rd.h);
break;
case 8:
last_cycle();
op_write(OPMODE_DP, dp + regs.x.w, rd.l);
status.cycle_pos = 0;
break;
@@ -1031,6 +1067,7 @@ void bCPU::op_rol_dpx() {
op_write(OPMODE_DP, dp + regs.x.w + 1, rd.h);
break;
case 8:
last_cycle();
op_write(OPMODE_DP, dp + regs.x.w, rd.l);
status.cycle_pos = 0;
break;
@@ -1064,6 +1101,7 @@ void bCPU::op_ror_dpx() {
op_write(OPMODE_DP, dp + regs.x.w + 1, rd.h);
break;
case 8:
last_cycle();
op_write(OPMODE_DP, dp + regs.x.w, rd.l);
status.cycle_pos = 0;
break;

View File

@@ -7,10 +7,12 @@ void bCPU::op_sta_addr() {
aa.h = op_read();
break;
case 3:
if(regs.p.m)last_cycle();
op_write(OPMODE_DBR, aa.w, regs.a.w);
if(regs.p.m)status.cycle_pos = 0;
break;
case 4:
last_cycle();
op_write(OPMODE_DBR, aa.w + 1, regs.a.w >> 8);
status.cycle_pos = 0;
break;
@@ -26,10 +28,12 @@ void bCPU::op_stx_addr() {
aa.h = op_read();
break;
case 3:
if(regs.p.x)last_cycle();
op_write(OPMODE_DBR, aa.w, regs.x.w);
if(regs.p.x)status.cycle_pos = 0;
break;
case 4:
last_cycle();
op_write(OPMODE_DBR, aa.w + 1, regs.x.w >> 8);
status.cycle_pos = 0;
break;
@@ -45,10 +49,12 @@ void bCPU::op_sty_addr() {
aa.h = op_read();
break;
case 3:
if(regs.p.x)last_cycle();
op_write(OPMODE_DBR, aa.w, regs.y.w);
if(regs.p.x)status.cycle_pos = 0;
break;
case 4:
last_cycle();
op_write(OPMODE_DBR, aa.w + 1, regs.y.w >> 8);
status.cycle_pos = 0;
break;
@@ -64,10 +70,12 @@ void bCPU::op_stz_addr() {
aa.h = op_read();
break;
case 3:
if(regs.p.m)last_cycle();
op_write(OPMODE_DBR, aa.w, 0x0000);
if(regs.p.m)status.cycle_pos = 0;
break;
case 4:
last_cycle();
op_write(OPMODE_DBR, aa.w + 1, 0x0000 >> 8);
status.cycle_pos = 0;
break;
@@ -86,10 +94,12 @@ void bCPU::op_sta_addrx() {
cpu_c4(aa.w, aa.w + regs.x.w);
break;
case 4:
if(regs.p.m)last_cycle();
op_write(OPMODE_DBR, aa.w + regs.x.w, regs.a.w);
if(regs.p.m)status.cycle_pos = 0;
break;
case 5:
last_cycle();
op_write(OPMODE_DBR, aa.w + regs.x.w + 1, regs.a.w >> 8);
status.cycle_pos = 0;
break;
@@ -108,10 +118,12 @@ void bCPU::op_stz_addrx() {
cpu_c4(aa.w, aa.w + regs.x.w);
break;
case 4:
if(regs.p.m)last_cycle();
op_write(OPMODE_DBR, aa.w + regs.x.w, 0x0000);
if(regs.p.m)status.cycle_pos = 0;
break;
case 5:
last_cycle();
op_write(OPMODE_DBR, aa.w + regs.x.w + 1, 0x0000 >> 8);
status.cycle_pos = 0;
break;
@@ -130,10 +142,12 @@ void bCPU::op_sta_addry() {
cpu_c4(aa.w, aa.w + regs.y.w);
break;
case 4:
if(regs.p.m)last_cycle();
op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l);
if(regs.p.m)status.cycle_pos = 0;
break;
case 5:
last_cycle();
op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h);
status.cycle_pos = 0;
break;
@@ -152,10 +166,12 @@ void bCPU::op_sta_long() {
aa.b = op_read();
break;
case 4:
if(regs.p.m)last_cycle();
op_write(OPMODE_LONG, aa.d, regs.a.l);
if(regs.p.m)status.cycle_pos = 0;
break;
case 5:
last_cycle();
op_write(OPMODE_LONG, aa.d + 1, regs.a.h);
status.cycle_pos = 0;
break;
@@ -174,10 +190,12 @@ void bCPU::op_sta_longx() {
aa.b = op_read();
break;
case 4:
if(regs.p.m)last_cycle();
op_write(OPMODE_LONG, aa.d + regs.x.w, regs.a.l);
if(regs.p.m)status.cycle_pos = 0;
break;
case 5:
last_cycle();
op_write(OPMODE_LONG, aa.d + regs.x.w + 1, regs.a.h);
status.cycle_pos = 0;
break;
@@ -193,10 +211,12 @@ void bCPU::op_sta_dp() {
cpu_c2();
break;
case 3:
if(regs.p.m)last_cycle();
op_write(OPMODE_DP, dp, regs.a.w);
if(regs.p.m)status.cycle_pos = 0;
break;
case 4:
last_cycle();
op_write(OPMODE_DP, dp + 1, regs.a.w >> 8);
status.cycle_pos = 0;
break;
@@ -212,10 +232,12 @@ void bCPU::op_stx_dp() {
cpu_c2();
break;
case 3:
if(regs.p.x)last_cycle();
op_write(OPMODE_DP, dp, regs.x.w);
if(regs.p.x)status.cycle_pos = 0;
break;
case 4:
last_cycle();
op_write(OPMODE_DP, dp + 1, regs.x.w >> 8);
status.cycle_pos = 0;
break;
@@ -231,10 +253,12 @@ void bCPU::op_sty_dp() {
cpu_c2();
break;
case 3:
if(regs.p.x)last_cycle();
op_write(OPMODE_DP, dp, regs.y.w);
if(regs.p.x)status.cycle_pos = 0;
break;
case 4:
last_cycle();
op_write(OPMODE_DP, dp + 1, regs.y.w >> 8);
status.cycle_pos = 0;
break;
@@ -250,10 +274,12 @@ void bCPU::op_stz_dp() {
cpu_c2();
break;
case 3:
if(regs.p.m)last_cycle();
op_write(OPMODE_DP, dp, 0x0000);
if(regs.p.m)status.cycle_pos = 0;
break;
case 4:
last_cycle();
op_write(OPMODE_DP, dp + 1, 0x0000 >> 8);
status.cycle_pos = 0;
break;
@@ -272,10 +298,12 @@ void bCPU::op_sta_dpx() {
cpu_io();
break;
case 4:
if(regs.p.m)last_cycle();
op_write(OPMODE_DP, dp + regs.x.w, regs.a.w);
if(regs.p.m)status.cycle_pos = 0;
break;
case 5:
last_cycle();
op_write(OPMODE_DP, dp + regs.x.w + 1, regs.a.w >> 8);
status.cycle_pos = 0;
break;
@@ -294,10 +322,12 @@ void bCPU::op_sty_dpx() {
cpu_io();
break;
case 4:
if(regs.p.x)last_cycle();
op_write(OPMODE_DP, dp + regs.x.w, regs.y.w);
if(regs.p.x)status.cycle_pos = 0;
break;
case 5:
last_cycle();
op_write(OPMODE_DP, dp + regs.x.w + 1, regs.y.w >> 8);
status.cycle_pos = 0;
break;
@@ -316,10 +346,12 @@ void bCPU::op_stz_dpx() {
cpu_io();
break;
case 4:
if(regs.p.m)last_cycle();
op_write(OPMODE_DP, dp + regs.x.w, 0x0000);
if(regs.p.m)status.cycle_pos = 0;
break;
case 5:
last_cycle();
op_write(OPMODE_DP, dp + regs.x.w + 1, 0x0000 >> 8);
status.cycle_pos = 0;
break;
@@ -338,10 +370,12 @@ void bCPU::op_stx_dpy() {
cpu_io();
break;
case 4:
if(regs.p.x)last_cycle();
op_write(OPMODE_DP, dp + regs.y.w, regs.x.l);
if(regs.p.x)status.cycle_pos = 0;
break;
case 5:
last_cycle();
op_write(OPMODE_DP, dp + regs.y.w + 1, regs.x.h);
status.cycle_pos = 0;
break;
@@ -363,10 +397,12 @@ void bCPU::op_sta_idp() {
aa.h = op_read(OPMODE_DP, dp + 1);
break;
case 5:
if(regs.p.m)last_cycle();
op_write(OPMODE_DBR, aa.w, regs.a.l);
if(regs.p.m)status.cycle_pos = 0;
break;
case 6:
last_cycle();
op_write(OPMODE_DBR, aa.w + 1, regs.a.h);
status.cycle_pos = 0;
break;
@@ -391,10 +427,12 @@ void bCPU::op_sta_ildp() {
aa.b = op_read(OPMODE_DP, dp + 2);
break;
case 6:
if(regs.p.m)last_cycle();
op_write(OPMODE_LONG, aa.d, regs.a.l);
if(regs.p.m)status.cycle_pos = 0;
break;
case 7:
last_cycle();
op_write(OPMODE_LONG, aa.d + 1, regs.a.h);
status.cycle_pos = 0;
break;
@@ -419,10 +457,12 @@ void bCPU::op_sta_idpx() {
aa.h = op_read(OPMODE_DP, dp + regs.x.w + 1);
break;
case 6:
if(regs.p.m)last_cycle();
op_write(OPMODE_DBR, aa.w, regs.a.l);
if(regs.p.m)status.cycle_pos = 0;
break;
case 7:
last_cycle();
op_write(OPMODE_DBR, aa.w + 1, regs.a.h);
status.cycle_pos = 0;
break;
@@ -447,10 +487,12 @@ void bCPU::op_sta_idpy() {
cpu_c4(aa.w, aa.w + regs.y.w);
break;
case 6:
if(regs.p.m)last_cycle();
op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l);
if(regs.p.m)status.cycle_pos = 0;
break;
case 7:
last_cycle();
op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h);
status.cycle_pos = 0;
break;
@@ -475,10 +517,12 @@ void bCPU::op_sta_ildpy() {
aa.b = op_read(OPMODE_DP, dp + 2);
break;
case 6:
if(regs.p.m)last_cycle();
op_write(OPMODE_LONG, aa.d + regs.y.w, regs.a.l);
if(regs.p.m)status.cycle_pos = 0;
break;
case 7:
last_cycle();
op_write(OPMODE_LONG, aa.d + regs.y.w + 1, regs.a.h);
status.cycle_pos = 0;
break;
@@ -494,10 +538,12 @@ void bCPU::op_sta_sr() {
cpu_io();
break;
case 3:
if(regs.p.m)last_cycle();
op_write(OPMODE_SP, sp, regs.a.l);
if(regs.p.m)status.cycle_pos = 0;
break;
case 4:
last_cycle();
op_write(OPMODE_SP, sp + 1, regs.a.h);
status.cycle_pos = 0;
break;
@@ -522,10 +568,12 @@ void bCPU::op_sta_isry() {
cpu_io();
break;
case 6:
if(regs.p.m)last_cycle();
op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l);
if(regs.p.m)status.cycle_pos = 0;
break;
case 7:
last_cycle();
op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h);
status.cycle_pos = 0;
break;

View File

@@ -186,7 +186,6 @@ optbl[0x91] = &bCPU::op_sta_idpy;
optbl[0x97] = &bCPU::op_sta_ildpy;
optbl[0x83] = &bCPU::op_sta_sr;
optbl[0x93] = &bCPU::op_sta_isry;
optbl[0x80] = &bCPU::op_bra;
optbl[0x90] = &bCPU::op_bcc;
optbl[0xb0] = &bCPU::op_bcs;
optbl[0xd0] = &bCPU::op_bne;
@@ -195,6 +194,7 @@ optbl[0x10] = &bCPU::op_bpl;
optbl[0x30] = &bCPU::op_bmi;
optbl[0x50] = &bCPU::op_bvc;
optbl[0x70] = &bCPU::op_bvs;
optbl[0x80] = &bCPU::op_bra;
optbl[0x82] = &bCPU::op_brl;
optbl[0x4c] = &bCPU::op_jmp_addr;
optbl[0x5c] = &bCPU::op_jmp_long;

View File

@@ -25,19 +25,158 @@
* 4 cycles short?
*/
uint16 bCPU::vcounter() { return time.v; }
uint16 bCPU::hcounter() { return get_hcounter(); }
uint16 bCPU::hcycles() { return time.hc; }
uint16 bCPU::vcounter() { return time.v; }
uint16 bCPU::hcounter() { return get_hcounter(); }
uint16 bCPU::hcycles() { return time.hc; }
bool bCPU::interlace() { return time.interlace; }
bool bCPU::interlace_field() { return time.interlace_field; }
bool bCPU::overscan() { return time.overscan; }
uint16 bCPU::region_scanlines() { return time.region_scanlines; }
void bCPU::set_interlace(bool r) { time.interlace = r; }
void bCPU::set_overscan(bool r) { time.overscan = r; }
void bCPU::set_interlace(bool r) { time.interlace = r; update_interrupts(); }
void bCPU::set_overscan (bool r) { time.overscan = r; update_interrupts(); }
uint8 bCPU::dma_counter() { return (time.dma_counter + time.hc) & 6; }
bool bCPU::nmi_trigger_pos_match(uint32 offset) {
uint16 v = overscan() ? 240 : 225;
uint16 hc = 2 + offset;
return (time.v == v && time.hc == hc);
}
bool bCPU::irq_trigger_pos_match(uint32 offset) {
uint16 v = status.virq_pos;
uint16 hc = (status.hirq_enabled) ? status.hirq_pos : 0;
//positions that can never be latched
//region_scanlines() = 262/NTSC, 312/PAL
//PAL results are unverified on hardware
if(v == 240 && hc == 339 && interlace() == false && interlace_field() == 1)return false;
if(v == (region_scanlines() - 1) && hc == 339 && interlace() == false)return false;
if(v == region_scanlines() && interlace() == false)return false;
if(v == region_scanlines() && hc == 339)return false;
if(v > region_scanlines())return false;
if(hc > 339)return false;
hc = (hc != 0) ? ((hc << 2) + 14) : 10;
hc += offset;
if(hc >= time.line_cycles) {
hc -= time.line_cycles;
if(++v >= time.frame_lines) {
v = 0;
}
}
if((status.virq_enabled == true && time.v == v) || status.virq_enabled == false) {
return (time.hc == hc);
}
return false;
}
void bCPU::update_nmi() {
if(time.v == (overscan() ? 240 : 225)) {
time.nmi_read_trigger_pos = 2;
time.nmi_line_trigger_pos = 6;
} else {
time.nmi_read_trigger_pos = -64;
time.nmi_line_trigger_pos = -64;
}
}
void bCPU::update_irq() {
int vpos = status.virq_pos;
int hpos = (status.hirq_enabled) ? status.hirq_pos : 0;
//positions that can never be latched
//region_scanlines() = 262/NTSC, 312/PAL
//PAL results are unverified on hardware
if(vpos == 240 && hpos == 339 && interlace() == false && interlace_field() == 1)goto _nolatch;
if(vpos == (region_scanlines() - 1) && hpos == 339 && interlace() == false)goto _nolatch;
if(vpos == region_scanlines() && interlace() == false)goto _nolatch;
if(vpos == region_scanlines() && hpos == 339)goto _nolatch;
if(vpos > region_scanlines())goto _nolatch;
if(hpos > 339)goto _nolatch;
hpos = (hpos != 0) ? ((hpos << 2) + 14) : 10;
if(hpos >= time.line_cycles) {
hpos -= time.line_cycles;
if(++vpos >= time.frame_lines) {
vpos = 0;
}
}
if((status.virq_enabled == true && time.v == vpos) || status.virq_enabled == false) {
time.irq_read_trigger_pos = hpos;
} else {
time.irq_read_trigger_pos = -64;
}
hpos += 4;
if(hpos >= time.line_cycles) {
hpos -= time.line_cycles;
if(++vpos >= time.frame_lines) {
vpos = 0;
}
}
if((status.virq_enabled == true && time.v == vpos) || status.virq_enabled == false) {
time.irq_line_trigger_pos = hpos;
} else {
time.irq_line_trigger_pos = -64;
}
return;
_nolatch:
time.irq_read_trigger_pos = -64;
time.irq_line_trigger_pos = -64;
}
void bCPU::update_interrupts() {
update_nmi();
update_irq();
}
void bCPU::poll_interrupts(int cycles) {
int16 hc, hc_end;
if(time.hc == 0) {
hc = -1;
hc_end = cycles;
} else {
hc = time.hc;
hc_end = time.hc + cycles;
}
if(hc < time.nmi_read_trigger_pos && time.nmi_read_trigger_pos <= hc_end) {
//nmi_read can go low even with NMI interrupts disabled in $4200.d7
time.nmi_read = 0;
}
if(hc < time.nmi_line_trigger_pos && time.nmi_line_trigger_pos <= hc_end) {
if(status.nmi_enabled == true) {
if(time.nmi_line == 1) {
time.nmi_transition = 1;
}
time.nmi_line = 0;
}
}
if(hc < time.irq_read_trigger_pos && time.irq_read_trigger_pos <= hc_end) {
if(status.virq_enabled == true || status.hirq_enabled == true) {
time.irq_read = 0;
}
}
if(hc < time.irq_line_trigger_pos && time.irq_line_trigger_pos <= hc_end) {
if(status.virq_enabled == true || status.hirq_enabled == true) {
time.irq_line = 0;
time.irq_transition = 1;
}
}
}
//all scanlines are 1364 cycles long, except scanline 240
//on non-interlace odd-frames, which is 1360 cycles long.
//[NTSC]
@@ -68,14 +207,15 @@ void bCPU::inc_vcounter() {
}
}
time.hc = 0;
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 {
time.line_cycles = 1364;
}
time.dram_refreshed = false;
update_interrupts();
}
//all dots are 4 cycles long, except dots 323 and 327. dots 323 and 327
@@ -93,16 +233,6 @@ uint16 bCPU::get_hcounter() {
return (time.hc - ((time.hc > 1292) << 1) - ((time.hc > 1310) << 1)) >> 2;
}
void bCPU::dram_refresh() {
time.dram_refreshed = true;
add_cycles(40);
if(time.v != 240 || time.interlace != false || time.interlace_field != 1) {
//alternate between 534 and 538 every scanline except 240ni1
//in reality, this is probably based on frame cycle length...
time.dram_refresh_pos ^= 12;
}
}
uint32 bCPU::cycles_executed() {
uint32 r = status.cycles_executed;
status.cycles_executed = 0;
@@ -112,36 +242,79 @@ uint32 r = status.cycles_executed;
void bCPU::add_cycles(int cycles) {
status.cycles_executed += cycles;
cycles >>= 1;
while(cycles--) {
time.hc += 2;
poll_interrupts(cycles);
if(time.hc >= time.line_cycles) {
inc_vcounter();
if(time.v == 0) {
frame();
ppu->frame();
}
scanline();
ppu->scanline();
if(time.hc + cycles >= time.line_cycles) {
cycles = (time.hc + cycles) - time.line_cycles;
time.hc = 0;
inc_vcounter();
poll_interrupts(cycles);
if(time.v == 0) {
frame();
ppu->frame();
snes->frame();
}
if(time.dram_refreshed == false && time.hc >= time.dram_refresh_pos) {
dram_refresh();
}
scanline();
ppu->scanline();
snes->scanline();
time.line_rendered = false;
}
if(hdma_test() == true) {
hdma_run();
if(time.hdmainit_triggered == false) {
if(time.hc + cycles >= time.hdmainit_trigger_pos || time.v) {
time.hdmainit_triggered = true;
hdmainit_activate();
}
}
if(time.dram_refreshed == false) {
if(time.hc + cycles >= time.dram_refresh_pos) {
time.dram_refreshed = true;
status.cycles_executed += 40;
cycles = (time.hc + cycles) - time.dram_refresh_pos;
time.hc = time.dram_refresh_pos + 40;
if(cpu_version == 2) {
if(time.v != 240 || time.interlace != false || time.interlace_field != 1) {
if(time.dram_refresh_pos == 534) {
time.dram_refresh_pos = 538;
} else {
time.dram_refresh_pos = 534;
}
}
}
}
}
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 >= 1106) {
time.hdma_triggered = true;
hdma_activate();
}
}
}
time.hc += 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;
@@ -150,10 +323,26 @@ void bCPU::time_reset() {
time.line_cycles = 1364;
time.dram_refreshed = false;
time.dram_refresh_pos = 538;
time.dram_refresh_pos = (cpu_version == 2) ? 538 : 530;
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;
time.irq_line = time.irq_read = 1;
time.nmi_transition = 0;
time.irq_transition = 0;
update_interrupts();
switch(region) {
case NTSC:
time.region_scanlines = 262;

View File

@@ -4,13 +4,47 @@ struct {
bool interlace, interlace_field, overscan;
uint16 line_cycles, frame_lines;
bool line_rendered;
bool dram_refreshed;
uint16 dram_refresh_pos;
uint8 dma_counter;
uint16 hdmainit_trigger_pos;
bool hdmainit_triggered;
bool hdma_triggered;
uint16 region_scanlines;
}time;
//nmi_pending, irq_pending are used by last_cycle()
//nmi_line = /NMI, nmi_read = $4210.7
//irq_line = /IRQ, irq_read = $4211.7
bool nmi_pending, nmi_line, nmi_read;
bool irq_pending, irq_line, irq_read;
//NMI is edge-sensitive, meaning it triggers when /NMI
//transitions from high to low. This value is set to 1
//when that happens, and cleared after the nmi_test()
//routine acknowledges it and invokes the NMI interrupt
bool nmi_transition;
//IRQ is level-sensitive, so it does not need to keep
//track of transitions from high to low. IRQs will
//continue to fire as long as /IRQ stays low.
//However, if a write to $4200 forces IRQs high at the
//exact same clock cycle that /IRQ goes low, the /IRQ
//will still occur. Hence the need for this variable.
bool irq_transition;
//position is relative to time.hc, set at start of each scanline
//-64 means no trigger point on this scanline
//$4210/$4211 status bits get set before /NMI and /IRQ go low,
//hence the need for two variables for each.
int32 nmi_read_trigger_pos, nmi_line_trigger_pos;
int32 irq_read_trigger_pos, irq_line_trigger_pos;
} time;
inline uint16 vcounter();
inline uint16 hcounter();
@@ -20,6 +54,14 @@ inline bool interlace_field();
inline bool overscan();
inline uint16 region_scanlines();
inline bool nmi_trigger_pos_match(uint32 offset);
inline bool irq_trigger_pos_match(uint32 offset);
inline void update_nmi();
inline void update_irq();
inline void update_interrupts();
inline void poll_interrupts(int cycles);
inline void set_interlace(bool r);
inline void set_overscan (bool r);
@@ -27,6 +69,5 @@ inline uint8 dma_counter();
inline void inc_vcounter();
inline uint16 get_hcounter();
inline void dram_refresh();
inline void add_cycles(int cycles);
inline void time_reset();

View File

@@ -53,7 +53,6 @@ char t[4096];
void update_line(int i, int n) {
char t[4096];
sprintf(t, "goto l%d;", n + 2);
replace(line[i], "end;", "status.cycle_pos = 0;");
replace(line[i], "skip;", "status.cycle_pos++;");
}
@@ -88,7 +87,11 @@ char t[4096];
i++;
}
if(!strcmp(line[i], "}"))strcat(output_op, " status.cycle_pos = 0;\r\n");
if(!strcmp(line[i], "}")) {
strcat(output_op, " status.cycle_pos = 0;\r\n");
}
strcat(output_op, " break;\r\n");
}
strcat(output_op, " }\r\n}");

Binary file not shown.

View File

@@ -1,14 +1,17 @@
nop(0xea) {
1:cpu_io();
1:last_cycle();
cpu_io();
}
wdm(0x42) {
1:op_read();
1:last_cycle();
op_read();
}
xba(0xeb) {
1:cpu_io();
2:cpu_io();
2:last_cycle();
cpu_io();
regs.a.l ^= regs.a.h;
regs.a.h ^= regs.a.l;
regs.a.l ^= regs.a.h;
@@ -26,7 +29,8 @@ mvp(0x44, --) {
5:cpu_io();
if(regs.p.x) { regs.x.l$1; regs.y.l$1; }
else { regs.x.w$1; regs.y.w$1; }
6:cpu_io();
6:last_cycle();
cpu_io();
if(regs.a.w--)regs.pc.w -= 3;
}
@@ -39,7 +43,8 @@ cop(0x02, 0xfff4, 0xfff5, 0xffe4, 0xffe5) {
4:stack_write(regs.pc.l);
5:stack_write(regs.p);
6:rd.l = op_read(OPMODE_LONG, (regs.e)?$1:$3);
7:rd.h = op_read(OPMODE_LONG, (regs.e)?$2:$4);
7:last_cycle();
rd.h = op_read(OPMODE_LONG, (regs.e)?$2:$4);
regs.pc.b = 0x00;
regs.pc.w = rd.w;
regs.p.i = 1;
@@ -48,20 +53,26 @@ cop(0x02, 0xfff4, 0xfff5, 0xffe4, 0xffe5) {
stp(0xdb) {
1:cpu_io();
status.cpu_state = CPUSTATE_STP;
2:cpu_io();
run_state.stp = true;
2:last_cycle();
cpu_io();
regs.pc.w--;
}
wai(0xcb) {
1:cpu_io();
status.cpu_state = CPUSTATE_WAI;
2:cpu_io();
regs.pc.w--;
run_state.wai = true;
2:last_cycle();
cpu_io();
if(run_state.wai) {
//this can be cleared within last_cycle()
regs.pc.w--;
}
}
xce(0xfb) {
1:cpu_io();
1:last_cycle();
cpu_io();
bool c = regs.p.c;
regs.p.c = regs.e;
regs.e = c;
@@ -80,14 +91,16 @@ clv(0xb8, regs.p.v = 0),
sec(0x38, regs.p.c = 1),
sed(0xf8, regs.p.d = 1),
sei(0x78, regs.p.i = 1) {
1:cpu_io();
1:last_cycle();
cpu_io();
$1;
}
rep(0xc2, &=~),
sep(0xe2, |=) {
1:rd.l = op_read();
2:cpu_io();
2:last_cycle();
cpu_io();
regs.p $1 rd.l;
if(regs.e)regs.p |= 0x30;
if(regs.p.x) {
@@ -102,7 +115,8 @@ txa(0x8a, regs.p.m, a, x),
txy(0x9b, regs.p.x, y, x),
tya(0x98, regs.p.m, a, y),
tyx(0xbb, regs.p.x, x, y) {
1:cpu_io();
1:last_cycle();
cpu_io();
if($1) {
regs.$2.l = regs.$3.l;
regs.p.n = !!(regs.$2.l & 0x80);
@@ -115,27 +129,31 @@ tyx(0xbb, regs.p.x, x, y) {
}
tcd(0x5b) {
1:cpu_io();
1:last_cycle();
cpu_io();
regs.d.w = regs.a.w;
regs.p.n = !!(regs.d.w & 0x8000);
regs.p.z = (regs.d.w == 0);
}
tcs(0x1b) {
1:cpu_io();
1:last_cycle();
cpu_io();
regs.s.w = regs.a.w;
if(regs.e)regs.s.h = 0x01;
}
tdc(0x7b) {
1:cpu_io();
1:last_cycle();
cpu_io();
regs.a.w = regs.d.w;
regs.p.n = !!(regs.a.w & 0x8000);
regs.p.z = (regs.a.w == 0);
}
tsc(0x3b) {
1:cpu_io();
1:last_cycle();
cpu_io();
regs.a.w = regs.s.w;
if(regs.e) {
regs.p.n = !!(regs.a.l & 0x80);
@@ -147,7 +165,8 @@ tsc(0x3b) {
}
tsx(0xba) {
1:cpu_io();
1:last_cycle();
cpu_io();
if(regs.p.x) {
regs.x.l = regs.s.l;
regs.p.n = !!(regs.x.l & 0x80);
@@ -160,7 +179,8 @@ tsx(0xba) {
}
txs(0x9a) {
1:cpu_io();
1:last_cycle();
cpu_io();
if(regs.e) {
regs.s.l = regs.x.l;
regs.p.n = !!(regs.s.l & 0x80);
@@ -179,14 +199,16 @@ phd(0x0b, 0, d) {
1:cpu_io();
if($1)skip;
2:stack_write(regs.$2.h);
3:stack_write(regs.$2.l);
3:last_cycle();
stack_write(regs.$2.l);
}
phb(0x8b, regs.db),
phk(0x4b, regs.pc.b),
php(0x08, regs.p) {
1:cpu_io();
2:stack_write($1);
2:last_cycle();
stack_write($1);
}
pla(0x68, regs.p.m, a),
@@ -195,13 +217,15 @@ ply(0x7a, regs.p.x, y),
pld(0x2b, 0, d) {
1:cpu_io();
2:cpu_io();
3:regs.$2.l = stack_read();
3:if($1)last_cycle();
regs.$2.l = stack_read();
if($1) {
regs.p.n = !!(regs.$2.l & 0x80);
regs.p.z = (regs.$2.l == 0);
end;
}
4:regs.$2.h = stack_read();
4:last_cycle();
regs.$2.h = stack_read();
regs.p.n = !!(regs.$2.w & 0x8000);
regs.p.z = (regs.$2.w == 0);
}
@@ -209,7 +233,8 @@ pld(0x2b, 0, d) {
plb(0xab) {
1:cpu_io();
2:cpu_io();
3:regs.db = stack_read();
3:last_cycle();
regs.db = stack_read();
regs.p.n = !!(regs.db & 0x80);
regs.p.z = (regs.db == 0);
}
@@ -217,7 +242,8 @@ plb(0xab) {
plp(0x28) {
1:cpu_io();
2:cpu_io();
3:regs.p = stack_read();
3:last_cycle();
regs.p = stack_read();
if(regs.e)regs.p |= 0x30;
if(regs.p.x) {
regs.x.h = 0x00;
@@ -229,7 +255,8 @@ pea(0xf4) {
1:aa.l = op_read();
2:aa.h = op_read();
3:stack_write(aa.h);
4:stack_write(aa.l);
4:last_cycle();
stack_write(aa.l);
}
pei(0xd4) {
@@ -238,7 +265,8 @@ pei(0xd4) {
3:aa.l = op_read(OPMODE_DP, dp);
4:aa.h = op_read(OPMODE_DP, dp + 1);
5:stack_write(aa.h);
6:stack_write(aa.l);
6:last_cycle();
stack_write(aa.l);
}
per(0x62) {
@@ -247,5 +275,6 @@ per(0x62) {
3:cpu_io();
rd.w = regs.pc.d + (int16)aa.w;
4:stack_write(rd.h);
5:stack_write(rd.l);
5:last_cycle();
stack_write(rd.l);
}

View File

@@ -1,4 +1,3 @@
bra(0x80, 1),
bcc(0x90, !regs.p.c),
bcs(0xb0, regs.p.c),
bne(0xd0, !regs.p.z),
@@ -7,7 +6,8 @@ bpl(0x10, !regs.p.n),
bmi(0x30, regs.p.n),
bvc(0x50, !regs.p.v),
bvs(0x70, regs.p.v) {
1:rd.l = op_read();
1:if(!$1)last_cycle();
rd.l = op_read();
if($1) {
aa.w = regs.pc.d + (int8)rd.l;
regs.pc.w = aa.w;
@@ -15,26 +15,39 @@ bvs(0x70, regs.p.v) {
end;
}
2:cpu_c6(aa.w);
3:cpu_io();
3:last_cycle();
cpu_io();
}
bra(0x80) {
1:rd.l = op_read();
aa.w = regs.pc.d + (int8)rd.l;
regs.pc.w = aa.w;
2:cpu_c6(aa.w);
3:last_cycle();
cpu_io();
}
brl(0x82) {
1:rd.l = op_read();
2:rd.h = op_read();
3:cpu_io();
3:last_cycle();
cpu_io();
regs.pc.w = regs.pc.d + (int16)rd.w;
}
jmp_addr(0x4c) {
1:rd.l = op_read();
2:rd.h = op_read();
2:last_cycle();
rd.h = op_read();
regs.pc.w = rd.w;
}
jmp_long(0x5c) {
1:rd.l = op_read();
2:rd.h = op_read();
3:rd.b = op_read();
3:last_cycle();
rd.b = op_read();
regs.pc.d = rd.d & 0xffffff;
}
@@ -42,7 +55,8 @@ jmp_iaddr(0x6c) {
1:aa.l = op_read();
2:aa.h = op_read();
3:rd.l = op_read(OPMODE_ADDR, aa.w);
4:rd.h = op_read(OPMODE_ADDR, aa.w + 1);
4:last_cycle();
rd.h = op_read(OPMODE_ADDR, aa.w + 1);
regs.pc.w = rd.w;
}
@@ -51,7 +65,8 @@ jmp_iaddrx(0x7c) {
2:aa.h = op_read();
3:cpu_io();
4:rd.l = op_read(OPMODE_PBR, aa.w + regs.x.w);
5:rd.h = op_read(OPMODE_PBR, aa.w + regs.x.w + 1);
5:last_cycle();
rd.h = op_read(OPMODE_PBR, aa.w + regs.x.w + 1);
regs.pc.w = rd.w;
}
@@ -60,7 +75,8 @@ jmp_iladdr(0xdc) {
2:aa.h = op_read();
3:rd.l = op_read(OPMODE_ADDR, aa.w);
4:rd.h = op_read(OPMODE_ADDR, aa.w + 1);
5:rd.b = op_read(OPMODE_ADDR, aa.w + 2);
5:last_cycle();
rd.b = op_read(OPMODE_ADDR, aa.w + 2);
regs.pc.d = rd.d & 0xffffff;
}
@@ -70,7 +86,8 @@ jsr_addr(0x20) {
3:cpu_io();
4:regs.pc.w--;
stack_write(regs.pc.h);
5:stack_write(regs.pc.l);
5:last_cycle();
stack_write(regs.pc.l);
regs.pc.w = aa.w;
}
@@ -82,7 +99,8 @@ jsr_long(0x22) {
5:aa.b = op_read();
6:regs.pc.w--;
stack_write(regs.pc.h);
7:stack_write(regs.pc.l);
7:last_cycle();
stack_write(regs.pc.l);
regs.pc.d = aa.d & 0xffffff;
}
@@ -93,7 +111,8 @@ jsr_iaddrx(0xfc) {
4:aa.h = op_read();
5:cpu_io();
6:rd.l = op_read(OPMODE_PBR, aa.w + regs.x.w);
7:rd.h = op_read(OPMODE_PBR, aa.w + regs.x.w + 1);
7:last_cycle();
rd.h = op_read(OPMODE_PBR, aa.w + regs.x.w + 1);
regs.pc.w = rd.w;
}
@@ -112,7 +131,8 @@ rti(0x40) {
regs.pc.w = rd.w;
end;
}
6:rd.b = stack_read();
6:last_cycle();
rd.b = stack_read();
regs.pc.d = rd.d & 0xffffff;
}
@@ -121,7 +141,8 @@ rts(0x60) {
2:cpu_io();
3:rd.l = stack_read();
4:rd.h = stack_read();
5:cpu_io();
5:last_cycle();
cpu_io();
regs.pc.w = rd.w;
regs.pc.w++;
}
@@ -131,7 +152,8 @@ rtl(0x6b) {
2:cpu_io();
3:rd.l = stack_read();
4:rd.h = stack_read();
5:rd.b = stack_read();
5:last_cycle();
rd.b = stack_read();
regs.pc.d = rd.d & 0xffffff;
regs.pc.w++;
}

View File

@@ -9,9 +9,11 @@ ldx_const(0xa2, ldx, regs.p.x),
ldy_const(0xa0, ldy, regs.p.x),
ora_const(0x09, ora, regs.p.m),
sbc_const(0xe9, sbc, regs.p.m) {
1:rd.l = op_read();
1:if($2)last_cycle();
rd.l = op_read();
if($2) { op_$1_b(); end; }
2:rd.h = op_read();
2:last_cycle();
rd.h = op_read();
op_$1_w();
}
@@ -29,9 +31,11 @@ ora_addr(0x0d, ora, regs.p.m),
sbc_addr(0xed, sbc, regs.p.m) {
1:aa.l = op_read();
2:aa.h = op_read();
3:rd.l = op_read(OPMODE_DBR, aa.w);
3:if($2)last_cycle();
rd.l = op_read(OPMODE_DBR, aa.w);
if($2) { op_$1_b(); end; }
4:rd.h = op_read(OPMODE_DBR, aa.w + 1);
4:last_cycle();
rd.h = op_read(OPMODE_DBR, aa.w + 1);
op_$1_w();
}
@@ -47,9 +51,11 @@ sbc_addrx(0xfd, sbc, regs.p.m) {
1:aa.l = op_read();
2:aa.h = op_read();
3:cpu_c4(aa.w, aa.w + regs.x.w);
4:rd.l = op_read(OPMODE_DBR, aa.w + regs.x.w);
4:if($2)last_cycle();
rd.l = op_read(OPMODE_DBR, aa.w + regs.x.w);
if($2) { op_$1_b(); end; }
5:rd.h = op_read(OPMODE_DBR, aa.w + regs.x.w + 1);
5:last_cycle();
rd.h = op_read(OPMODE_DBR, aa.w + regs.x.w + 1);
op_$1_w();
}
@@ -64,9 +70,11 @@ sbc_addry(0xf9, sbc, regs.p.m) {
1:aa.l = op_read();
2:aa.h = op_read();
3:cpu_c4(aa.w, aa.w + regs.y.w);
4:rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w);
4:if($2)last_cycle();
rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w);
if($2) { op_$1_b(); end; }
5:rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1);
5:last_cycle();
rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1);
op_$1_w();
}
@@ -80,9 +88,11 @@ sbc_long(0xef, sbc, regs.p.m) {
1:aa.l = op_read();
2:aa.h = op_read();
3:aa.b = op_read();
4:rd.l = op_read(OPMODE_LONG, aa.d);
4:if($2)last_cycle();
rd.l = op_read(OPMODE_LONG, aa.d);
if($2) { op_$1_b(); end; }
5:rd.h = op_read(OPMODE_LONG, aa.d + 1);
5:last_cycle();
rd.h = op_read(OPMODE_LONG, aa.d + 1);
op_$1_w();
}
@@ -96,9 +106,11 @@ sbc_longx(0xff, sbc, regs.p.m) {
1:aa.l = op_read();
2:aa.h = op_read();
3:aa.b = op_read();
4:rd.l = op_read(OPMODE_LONG, aa.d + regs.x.w);
4:if($2)last_cycle();
rd.l = op_read(OPMODE_LONG, aa.d + regs.x.w);
if($2) { op_$1_b(); end; }
5:rd.h = op_read(OPMODE_LONG, aa.d + regs.x.w + 1);
5:last_cycle();
rd.h = op_read(OPMODE_LONG, aa.d + regs.x.w + 1);
op_$1_w();
}
@@ -116,9 +128,11 @@ ora_dp(0x05, ora, regs.p.m),
sbc_dp(0xe5, sbc, regs.p.m) {
1:dp = op_read();
2:cpu_c2();
3:rd.l = op_read(OPMODE_DP, dp);
3:if($2)last_cycle();
rd.l = op_read(OPMODE_DP, dp);
if($2) { op_$1_b(); end; }
4:rd.h = op_read(OPMODE_DP, dp + 1);
4:last_cycle();
rd.h = op_read(OPMODE_DP, dp + 1);
op_$1_w();
}
@@ -134,9 +148,11 @@ sbc_dpx(0xf5, sbc, regs.p.m) {
1:dp = op_read();
2:cpu_c2();
3:cpu_io();
4:rd.l = op_read(OPMODE_DP, dp + regs.x.w);
4:if($2)last_cycle();
rd.l = op_read(OPMODE_DP, dp + regs.x.w);
if($2) { op_$1_b(); end; }
5:rd.h = op_read(OPMODE_DP, dp + regs.x.w + 1);
5:last_cycle();
rd.h = op_read(OPMODE_DP, dp + regs.x.w + 1);
op_$1_w();
}
@@ -144,9 +160,11 @@ ldx_dpy(0xb6, ldx, regs.p.x) {
1:dp = op_read();
2:cpu_c2();
3:cpu_io();
4:rd.l = op_read(OPMODE_DP, dp + regs.y.w);
4:if($2)last_cycle();
rd.l = op_read(OPMODE_DP, dp + regs.y.w);
if($2) { op_$1_b(); end; }
5:rd.h = op_read(OPMODE_DP, dp + regs.y.w + 1);
5:last_cycle();
rd.h = op_read(OPMODE_DP, dp + regs.y.w + 1);
op_$1_w();
}
@@ -161,9 +179,11 @@ sbc_idp(0xf2, sbc, regs.p.m) {
2:cpu_c2();
3:aa.l = op_read(OPMODE_DP, dp);
4:aa.h = op_read(OPMODE_DP, dp + 1);
5:rd.l = op_read(OPMODE_DBR, aa.w);
5:if($2)last_cycle();
rd.l = op_read(OPMODE_DBR, aa.w);
if($2) { op_$1_b(); end; }
6:rd.h = op_read(OPMODE_DBR, aa.w + 1);
6:last_cycle();
rd.h = op_read(OPMODE_DBR, aa.w + 1);
op_$1_w();
}
@@ -179,9 +199,11 @@ sbc_idpx(0xe1, sbc, regs.p.m) {
3:cpu_io();
4:aa.l = op_read(OPMODE_DP, dp + regs.x.w);
5:aa.h = op_read(OPMODE_DP, dp + regs.x.w + 1);
6:rd.l = op_read(OPMODE_DBR, aa.w);
6:if($2)last_cycle();
rd.l = op_read(OPMODE_DBR, aa.w);
if($2) { op_$1_b(); end; }
7:rd.h = op_read(OPMODE_DBR, aa.w + 1);
7:last_cycle();
rd.h = op_read(OPMODE_DBR, aa.w + 1);
op_$1_w();
}
@@ -197,9 +219,11 @@ sbc_idpy(0xf1, sbc, regs.p.m) {
3:aa.l = op_read(OPMODE_DP, dp);
4:aa.h = op_read(OPMODE_DP, dp + 1);
5:cpu_c4(aa.w, aa.w + regs.y.w);
6:rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w);
6:if($2)last_cycle();
rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w);
if($2) { op_$1_b(); end; }
7:rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1);
7:last_cycle();
rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1);
op_$1_w();
}
@@ -215,9 +239,11 @@ sbc_ildp(0xe7, sbc, regs.p.m) {
3:aa.l = op_read(OPMODE_DP, dp);
4:aa.h = op_read(OPMODE_DP, dp + 1);
5:aa.b = op_read(OPMODE_DP, dp + 2);
6:rd.l = op_read(OPMODE_LONG, aa.d);
6:if($2)last_cycle();
rd.l = op_read(OPMODE_LONG, aa.d);
if($2) { op_$1_b(); end; }
7:rd.h = op_read(OPMODE_LONG, aa.d + 1);
7:last_cycle();
rd.h = op_read(OPMODE_LONG, aa.d + 1);
op_$1_w();
}
@@ -233,9 +259,11 @@ sbc_ildpy(0xf7, sbc, regs.p.m) {
3:aa.l = op_read(OPMODE_DP, dp);
4:aa.h = op_read(OPMODE_DP, dp + 1);
5:aa.b = op_read(OPMODE_DP, dp + 2);
6:rd.l = op_read(OPMODE_LONG, aa.d + regs.y.w);
6:if($2)last_cycle();
rd.l = op_read(OPMODE_LONG, aa.d + regs.y.w);
if($2) { op_$1_b(); end; }
7:rd.h = op_read(OPMODE_LONG, aa.d + regs.y.w + 1);
7:last_cycle();
rd.h = op_read(OPMODE_LONG, aa.d + regs.y.w + 1);
op_$1_w();
}
@@ -248,9 +276,11 @@ ora_sr(0x03, ora, regs.p.m),
sbc_sr(0xe3, sbc, regs.p.m) {
1:sp = op_read();
2:cpu_io();
3:rd.l = op_read(OPMODE_SP, sp);
3:if($2)last_cycle();
rd.l = op_read(OPMODE_SP, sp);
if($2) { op_$1_b(); end; }
4:rd.h = op_read(OPMODE_SP, sp + 1);
4:last_cycle();
rd.h = op_read(OPMODE_SP, sp + 1);
op_$1_w();
}
@@ -266,18 +296,22 @@ sbc_isry(0xf3, sbc) {
3:aa.l = op_read(OPMODE_SP, sp);
4:aa.h = op_read(OPMODE_SP, sp + 1);
5:cpu_io();
6:rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w);
6:if(regs.p.m)last_cycle();
rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w);
if(regs.p.m) { op_$1_b(); end; }
7:rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1);
7:last_cycle();
rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1);
op_$1_w();
}
bit_const(0x89) {
1:rd.l = op_read();
1:if(regs.p.m)last_cycle();
rd.l = op_read();
if(regs.p.m) {
regs.p.z = ((rd.l & regs.a.l) == 0);
end;
}
2:rd.h = op_read();
2:last_cycle();
rd.h = op_read();
regs.p.z = ((rd.w & regs.a.w) == 0);
}

View File

@@ -1,7 +1,8 @@
inc(0x1a, regs.p.m, a),
inx(0xe8, regs.p.x, x),
iny(0xc8, regs.p.x, y) {
1:cpu_io();
1:last_cycle();
cpu_io();
if($1) {
regs.$2.l++;
regs.p.n = !!(regs.$2.l & 0x80);
@@ -16,7 +17,8 @@ iny(0xc8, regs.p.x, y) {
dec(0x3a, regs.p.m, a),
dex(0xca, regs.p.x, x),
dey(0x88, regs.p.x, y) {
1:cpu_io();
1:last_cycle();
cpu_io();
if($1) {
regs.$2.l--;
regs.p.n = !!(regs.$2.l & 0x80);
@@ -29,7 +31,8 @@ dey(0x88, regs.p.x, y) {
}
asl(0x0a) {
1:cpu_io();
1:last_cycle();
cpu_io();
if(regs.p.m) {
regs.p.c = !!(regs.a.l & 0x80);
regs.a.l <<= 1;
@@ -44,7 +47,8 @@ asl(0x0a) {
}
lsr(0x4a) {
1:cpu_io();
1:last_cycle();
cpu_io();
if(regs.p.m) {
regs.p.c = regs.a.l & 1;
regs.a.l >>= 1;
@@ -59,7 +63,8 @@ lsr(0x4a) {
}
rol(0x2a) {
1:cpu_io();
1:last_cycle();
cpu_io();
uint16 c = regs.p.c;
if(regs.p.m) {
regs.p.c = !!(regs.a.l & 0x80);
@@ -77,7 +82,8 @@ rol(0x2a) {
}
ror(0x6a) {
1:cpu_io();
1:last_cycle();
cpu_io();
uint16 c;
if(regs.p.m) {
c = (regs.p.c)?0x80:0;
@@ -113,7 +119,8 @@ tsb_addr(0x0c, tsb) {
if(regs.p.m) { op_$1_b(); skip; }
else op_$1_w();
6:op_write(OPMODE_DBR, aa.w + 1, rd.h);
7:op_write(OPMODE_DBR, aa.w, rd.l);
7:last_cycle();
op_write(OPMODE_DBR, aa.w, rd.l);
}
inc_addrx(0xfe, inc),
@@ -132,7 +139,8 @@ ror_addrx(0x7e, ror) {
if(regs.p.m) { op_$1_b(); skip; }
else op_$1_w();
7:op_write(OPMODE_DBR, aa.w + regs.x.w + 1, rd.h);
8:op_write(OPMODE_DBR, aa.w + regs.x.w, rd.l);
8:last_cycle();
op_write(OPMODE_DBR, aa.w + regs.x.w, rd.l);
}
inc_dp(0xe6, inc),
@@ -152,7 +160,8 @@ tsb_dp(0x04, tsb) {
if(regs.p.m) { op_$1_b(); skip; }
else op_$1_w();
6:op_write(OPMODE_DP, dp + 1, rd.h);
7:op_write(OPMODE_DP, dp, rd.l);
7:last_cycle();
op_write(OPMODE_DP, dp, rd.l);
}
inc_dpx(0xf6, inc),
@@ -171,5 +180,6 @@ ror_dpx(0x76, ror) {
if(regs.p.m) { op_$1_b(); skip; }
else op_$1_w();
7:op_write(OPMODE_DP, dp + regs.x.w + 1, rd.h);
8:op_write(OPMODE_DP, dp + regs.x.w, rd.l);
8:last_cycle();
op_write(OPMODE_DP, dp + regs.x.w, rd.l);
}

View File

@@ -4,9 +4,11 @@ sty_addr(0x8c, regs.p.x, regs.y.w),
stz_addr(0x9c, regs.p.m, 0x0000) {
1:aa.l = op_read();
2:aa.h = op_read();
3:op_write(OPMODE_DBR, aa.w, $2);
3:if($1)last_cycle();
op_write(OPMODE_DBR, aa.w, $2);
if($1)end;
4:op_write(OPMODE_DBR, aa.w + 1, $2 >> 8);
4:last_cycle();
op_write(OPMODE_DBR, aa.w + 1, $2 >> 8);
}
sta_addrx(0x9d, regs.p.m, regs.a.w),
@@ -14,36 +16,44 @@ stz_addrx(0x9e, regs.p.m, 0x0000) {
1:aa.l = op_read();
2:aa.h = op_read();
3:cpu_c4(aa.w, aa.w + regs.x.w);
4:op_write(OPMODE_DBR, aa.w + regs.x.w, $2);
4:if($1)last_cycle();
op_write(OPMODE_DBR, aa.w + regs.x.w, $2);
if($1)end;
5:op_write(OPMODE_DBR, aa.w + regs.x.w + 1, $2 >> 8);
5:last_cycle();
op_write(OPMODE_DBR, aa.w + regs.x.w + 1, $2 >> 8);
}
sta_addry(0x99) {
1:aa.l = op_read();
2:aa.h = op_read();
3:cpu_c4(aa.w, aa.w + regs.y.w);
4:op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l);
4:if(regs.p.m)last_cycle();
op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l);
if(regs.p.m)end;
5:op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h);
5:last_cycle();
op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h);
}
sta_long(0x8f) {
1:aa.l = op_read();
2:aa.h = op_read();
3:aa.b = op_read();
4:op_write(OPMODE_LONG, aa.d, regs.a.l);
4:if(regs.p.m)last_cycle();
op_write(OPMODE_LONG, aa.d, regs.a.l);
if(regs.p.m)end;
5:op_write(OPMODE_LONG, aa.d + 1, regs.a.h);
5:last_cycle();
op_write(OPMODE_LONG, aa.d + 1, regs.a.h);
}
sta_longx(0x9f) {
1:aa.l = op_read();
2:aa.h = op_read();
3:aa.b = op_read();
4:op_write(OPMODE_LONG, aa.d + regs.x.w, regs.a.l);
4:if(regs.p.m)last_cycle();
op_write(OPMODE_LONG, aa.d + regs.x.w, regs.a.l);
if(regs.p.m)end;
5:op_write(OPMODE_LONG, aa.d + regs.x.w + 1, regs.a.h);
5:last_cycle();
op_write(OPMODE_LONG, aa.d + regs.x.w + 1, regs.a.h);
}
sta_dp(0x85, regs.p.m, regs.a.w),
@@ -52,9 +62,11 @@ sty_dp(0x84, regs.p.x, regs.y.w),
stz_dp(0x64, regs.p.m, 0x0000) {
1:dp = op_read();
2:cpu_c2();
3:op_write(OPMODE_DP, dp, $2);
3:if($1)last_cycle();
op_write(OPMODE_DP, dp, $2);
if($1)end;
4:op_write(OPMODE_DP, dp + 1, $2 >> 8);
4:last_cycle();
op_write(OPMODE_DP, dp + 1, $2 >> 8);
}
sta_dpx(0x95, regs.p.m, regs.a.w),
@@ -63,18 +75,22 @@ stz_dpx(0x74, regs.p.m, 0x0000) {
1:dp = op_read();
2:cpu_c2();
3:cpu_io();
4:op_write(OPMODE_DP, dp + regs.x.w, $2);
4:if($1)last_cycle();
op_write(OPMODE_DP, dp + regs.x.w, $2);
if($1)end;
5:op_write(OPMODE_DP, dp + regs.x.w + 1, $2 >> 8);
5:last_cycle();
op_write(OPMODE_DP, dp + regs.x.w + 1, $2 >> 8);
}
stx_dpy(0x96) {
1:dp = op_read();
2:cpu_c2();
3:cpu_io();
4:op_write(OPMODE_DP, dp + regs.y.w, regs.x.l);
4:if(regs.p.x)last_cycle();
op_write(OPMODE_DP, dp + regs.y.w, regs.x.l);
if(regs.p.x)end;
5:op_write(OPMODE_DP, dp + regs.y.w + 1, regs.x.h);
5:last_cycle();
op_write(OPMODE_DP, dp + regs.y.w + 1, regs.x.h);
}
sta_idp(0x92) {
@@ -82,9 +98,11 @@ sta_idp(0x92) {
2:cpu_c2();
3:aa.l = op_read(OPMODE_DP, dp);
4:aa.h = op_read(OPMODE_DP, dp + 1);
5:op_write(OPMODE_DBR, aa.w, regs.a.l);
5:if(regs.p.m)last_cycle();
op_write(OPMODE_DBR, aa.w, regs.a.l);
if(regs.p.m)end;
6:op_write(OPMODE_DBR, aa.w + 1, regs.a.h);
6:last_cycle();
op_write(OPMODE_DBR, aa.w + 1, regs.a.h);
}
sta_ildp(0x87) {
@@ -93,9 +111,11 @@ sta_ildp(0x87) {
3:aa.l = op_read(OPMODE_DP, dp);
4:aa.h = op_read(OPMODE_DP, dp + 1);
5:aa.b = op_read(OPMODE_DP, dp + 2);
6:op_write(OPMODE_LONG, aa.d, regs.a.l);
6:if(regs.p.m)last_cycle();
op_write(OPMODE_LONG, aa.d, regs.a.l);
if(regs.p.m)end;
7:op_write(OPMODE_LONG, aa.d + 1, regs.a.h);
7:last_cycle();
op_write(OPMODE_LONG, aa.d + 1, regs.a.h);
}
sta_idpx(0x81) {
@@ -104,9 +124,11 @@ sta_idpx(0x81) {
3:cpu_io();
4:aa.l = op_read(OPMODE_DP, dp + regs.x.w);
5:aa.h = op_read(OPMODE_DP, dp + regs.x.w + 1);
6:op_write(OPMODE_DBR, aa.w, regs.a.l);
6:if(regs.p.m)last_cycle();
op_write(OPMODE_DBR, aa.w, regs.a.l);
if(regs.p.m)end;
7:op_write(OPMODE_DBR, aa.w + 1, regs.a.h);
7:last_cycle();
op_write(OPMODE_DBR, aa.w + 1, regs.a.h);
}
sta_idpy(0x91) {
@@ -115,9 +137,11 @@ sta_idpy(0x91) {
3:aa.l = op_read(OPMODE_DP, dp);
4:aa.h = op_read(OPMODE_DP, dp + 1);
5:cpu_c4(aa.w, aa.w + regs.y.w);
6:op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l);
6:if(regs.p.m)last_cycle();
op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l);
if(regs.p.m)end;
7:op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h);
7:last_cycle();
op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h);
}
sta_ildpy(0x97) {
@@ -126,17 +150,21 @@ sta_ildpy(0x97) {
3:aa.l = op_read(OPMODE_DP, dp);
4:aa.h = op_read(OPMODE_DP, dp + 1);
5:aa.b = op_read(OPMODE_DP, dp + 2);
6:op_write(OPMODE_LONG, aa.d + regs.y.w, regs.a.l);
6:if(regs.p.m)last_cycle();
op_write(OPMODE_LONG, aa.d + regs.y.w, regs.a.l);
if(regs.p.m)end;
7:op_write(OPMODE_LONG, aa.d + regs.y.w + 1, regs.a.h);
7:last_cycle();
op_write(OPMODE_LONG, aa.d + regs.y.w + 1, regs.a.h);
}
sta_sr(0x83) {
1:sp = op_read();
2:cpu_io();
3:op_write(OPMODE_SP, sp, regs.a.l);
3:if(regs.p.m)last_cycle();
op_write(OPMODE_SP, sp, regs.a.l);
if(regs.p.m)end;
4:op_write(OPMODE_SP, sp + 1, regs.a.h);
4:last_cycle();
op_write(OPMODE_SP, sp + 1, regs.a.h);
}
sta_isry(0x93) {
@@ -145,7 +173,9 @@ sta_isry(0x93) {
3:aa.l = op_read(OPMODE_SP, sp);
4:aa.h = op_read(OPMODE_SP, sp + 1);
5:cpu_io();
6:op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l);
6:if(regs.p.m)last_cycle();
op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l);
if(regs.p.m)end;
7:op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h);
7:last_cycle();
op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h);
}

View File

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

View File

@@ -2,6 +2,12 @@
class CPU {
public:
//CPU version number
//* 1 and 2 are known
//* reported by $4210
//* affects DRAM refresh behavior
uint8 cpu_version;
//timing
virtual uint16 vcounter() = 0;
virtual uint16 hcounter() = 0;
@@ -9,7 +15,7 @@ public:
virtual bool interlace() = 0;
virtual bool interlace_field() = 0;
virtual bool overscan() = 0;
virtual uint16 region_scanlines() = 0;
virtual void set_interlace(bool r) = 0;
virtual void set_overscan (bool r) = 0;

View File

@@ -13,7 +13,7 @@ private:
inline bool operator ^= (bool i) { if(i)_b ^= B; return (_b & B); }
};
public:
union {
union {
uint8 _b;
bit<0x80> n;
bit<0x40> v;
@@ -23,7 +23,7 @@ public:
bit<0x04> i;
bit<0x02> z;
bit<0x01> c;
};
};
CPURegFlags() { _b = 0; }
inline operator uint8() { return _b; }
@@ -35,10 +35,14 @@ public:
class CPUReg16 {
public:
union {
union {
uint16 w;
#ifdef ARCH_LSB
struct { uint8 l, h; };
};
#else
struct { uint8 h, l; };
#endif;
};
CPUReg16() { w = 0; }
inline operator uint16() { return w; }
@@ -56,11 +60,16 @@ public:
class CPUReg24 {
public:
union {
uint16 w;
union {
uint32 d;
struct { uint8 l, h, b; };
};
#ifdef ARCH_LSB
struct { uint16 w, null_w; };
struct { uint8 l, h, b, null_b; };
#else
struct { uint16 null_w, w; };
struct { uint8 null_b, b, h, l; };
#endif
};
CPUReg24() { d = 0; }
inline operator uint32() { return (d & 0xffffff); }

586
src/dsp/bdsp/bdsp.cpp Normal file
View File

@@ -0,0 +1,586 @@
#include "../../base.h"
#include "bdsp_tables.cpp"
uint8 bDSP::read_8 (uint16 addr) { return (spcram[addr]); }
uint16 bDSP::read_16 (uint16 addr) { return (spcram[(addr + 1) & 0xffff] << 8) + (spcram[addr]); }
void bDSP::write_8 (uint16 addr, uint8 data) { spcram[addr] = data; }
void bDSP::write_16(uint16 addr, uint16 data) { spcram[addr++] = data; spcram[addr] = data >> 8; }
uint8 bDSP::read(uint8 addr) {
int i, v, n;
addr &= 127;
v = addr >> 4;
n = addr & 15;
switch(addr) {
case 0x00:case 0x10:case 0x20:case 0x30:
case 0x40:case 0x50:case 0x60:case 0x70:
return voice[v].VOLL;
case 0x01:case 0x11:case 0x21:case 0x31:
case 0x41:case 0x51:case 0x61:case 0x71:
return voice[v].VOLR;
case 0x02:case 0x12:case 0x22:case 0x32:
case 0x42:case 0x52:case 0x62:case 0x72:
return voice[v].PITCH >> 8;
case 0x03:case 0x13:case 0x23:case 0x33:
case 0x43:case 0x53:case 0x63:case 0x73:
return voice[v].PITCH;
case 0x04:case 0x14:case 0x24:case 0x34:
case 0x44:case 0x54:case 0x64:case 0x74:
return voice[v].SRCN;
case 0x05:case 0x15:case 0x25:case 0x35:
case 0x45:case 0x55:case 0x65:case 0x75:
return voice[v].ADSR1;
case 0x06:case 0x16:case 0x26:case 0x36:
case 0x46:case 0x56:case 0x66:case 0x76:
return voice[v].ADSR2;
case 0x07:case 0x17:case 0x27:case 0x37:
case 0x47:case 0x57:case 0x67:case 0x77:
return voice[v].GAIN;
case 0x08:case 0x18:case 0x28:case 0x38:
case 0x48:case 0x58:case 0x68:case 0x78:
return voice[v].ENVX;
case 0x09:case 0x19:case 0x29:case 0x39:
case 0x49:case 0x59:case 0x69:case 0x79:
return voice[v].OUTX;
case 0x0f:case 0x1f:case 0x2f:case 0x3f:
case 0x4f:case 0x5f:case 0x6f:case 0x7f:
return status.FIR[v];
case 0x0c:return status.MVOLL;
case 0x1c:return status.MVOLR;
case 0x2c:return status.EVOLL;
case 0x3c:return status.EVOLR;
case 0x4c:return status.KON;
case 0x5c:return status.KOFF;
case 0x6c:return status.FLG;
case 0x7c:return status.ENDX;
case 0x0d:return status.EFB;
case 0x2d:return status.PMON;
case 0x3d:return status.NON;
case 0x4d:return status.EON;
case 0x5d:return status.DIR;
case 0x6d:return status.ESA;
case 0x7d:return status.EDL;
}
return dspram[addr];
}
void bDSP::write(uint8 addr, uint8 data) {
int i, v, n;
//0x80-0xff is a read-only mirror of 0x00-0x7f
if(addr & 0x80)return;
v = addr >> 4;
n = addr & 15;
switch(addr) {
case 0x00:case 0x10:case 0x20:case 0x30:
case 0x40:case 0x50:case 0x60:case 0x70:
voice[v].VOLL = data;
break;
case 0x01:case 0x11:case 0x21:case 0x31:
case 0x41:case 0x51:case 0x61:case 0x71:
voice[v].VOLR = data;
break;
case 0x02:case 0x12:case 0x22:case 0x32:
case 0x42:case 0x52:case 0x62:case 0x72:
voice[v].PITCH &= 0xff00;
voice[v].PITCH |= data;
break;
case 0x03:case 0x13:case 0x23:case 0x33:
case 0x43:case 0x53:case 0x63:case 0x73:
voice[v].PITCH &= 0x00ff;
voice[v].PITCH |= data << 8;
break;
case 0x04:case 0x14:case 0x24:case 0x34:
case 0x44:case 0x54:case 0x64:case 0x74:
//voice[v].SRCN = data;
//below is anomie's code, but TRAC says writing SRCN doesn't affect anything until a
//BRR-with-end block is encountered, where it loads the loop address from the new SRCN
if(voice[v].SRCN != data) {
voice[v].SRCN = data;
voice[v].brr_ptr = read_16((status.DIR << 8) + (voice[v].SRCN << 2) + ((voice[v].brr_looped)?2:0));
voice[v].brr_index = 0;
}
break;
case 0x05:case 0x15:case 0x25:case 0x35:
case 0x45:case 0x55:case 0x65:case 0x75:
voice[v].ADSR1 = data;
voice[v].AdjustEnvelope();
break;
case 0x06:case 0x16:case 0x26:case 0x36:
case 0x46:case 0x56:case 0x66:case 0x76:
voice[v].ADSR2 = data;
//sustain_level = 0-7, 7 is a special case handled by ATTACK envx mode
voice[v].env_sustain = (voice[v].ADSR_sus_level() + 1) << 8;
voice[v].AdjustEnvelope();
break;
case 0x07:case 0x17:case 0x27:case 0x37:
case 0x47:case 0x57:case 0x67:case 0x77:
voice[v].GAIN = data;
voice[v].AdjustEnvelope();
break;
case 0x08:case 0x18:case 0x28:case 0x38:
case 0x48:case 0x58:case 0x68:case 0x78:
voice[v].ENVX = data;
break;
case 0x09:case 0x19:case 0x29:case 0x39:
case 0x49:case 0x59:case 0x69:case 0x79:
voice[v].OUTX = data;
break;
case 0x0f:case 0x1f:case 0x2f:case 0x3f:
case 0x4f:case 0x5f:case 0x6f:case 0x7f:
status.FIR[v] = data;
break;
case 0x0c:status.MVOLL = data;break;
case 0x1c:status.MVOLR = data;break;
case 0x2c:status.EVOLL = data;break;
case 0x3c:status.EVOLR = data;break;
case 0x4c:
status.KON = data;
// status.kon = data;
status.key_flag = true;
break;
case 0x5c:
status.KOFF = data;
status.key_flag = true;
break;
case 0x6c:
status.FLG = data;
status.key_flag = true;
status.noise_rate = RateTable[data & 0x1f];
break;
case 0x7c:
//read-only register, writes clear all bits of ENDX
status.ENDX = 0;
break;
case 0x0d:status.EFB = data;break;
case 0x2d:status.PMON = data;break;
case 0x3d:status.NON = data;break;
case 0x4d:status.EON = data;break;
case 0x5d:status.DIR = data;break;
case 0x6d:status.ESA = data;break;
case 0x7d:
status.EDL = data;
status.echo_size = (data & 0x0f) << 11;
break;
}
dspram[addr] = data;
}
void bDSP::power() {
int v;
spcram = apu->get_spcram_handle();
memset(dspram, 0x00, 128);
for(v=0;v<8;v++) {
voice[v].VOLL = 0;
voice[v].VOLR = 0;
voice[v].PITCH = 0;
voice[v].SRCN = 0;
voice[v].ADSR1 = 0;
voice[v].ADSR2 = 0;
voice[v].GAIN = 0;
status.FIR[v] = 0;
}
status.MVOLL = status.MVOLR = 0;
status.EVOLL = status.EVOLR = 0;
status.ENDX = 0;
status.EFB = 0;
status.PMON = 0;
status.NON = 0;
status.EON = 0;
status.DIR = 0;
status.ESA = 0;
status.EDL = 0;
status.echo_size = 0;
status.echo_target = 0;
reset();
}
void bDSP::reset() {
int v;
status.KON = 0x00;
status.KOFF = 0x00;
status.FLG |= 0xe0;
//status.kon = 0x00;
status.key_flag = false;
status.noise_ctr = 0;
status.noise_rate = 0;
status.noise_sample = 0x4000;
status.echo_index = 0;
status.fir_buffer_index = 0;
for(v=0;v<8;v++) {
voice[v].ENVX = 0;
voice[v].OUTX = 0;
voice[v].pitch_ctr = 0;
voice[v].brr_index = 0;
voice[v].brr_ptr = read_16((status.DIR << 8) + (voice[v].SRCN << 2));
voice[v].brr_looped = false;
voice[v].brr_data[0] = 0;
voice[v].brr_data[1] = 0;
voice[v].brr_data[2] = 0;
voice[v].brr_data[3] = 0;
voice[v].brr_data_index = 0;
voice[v].envx = 0;
voice[v].env_ctr = 0;
voice[v].env_rate = 0;
voice[v].env_state = SILENCE;
voice[v].env_mode = DIRECT;
status.fir_buffer[0][v] = 0;
status.fir_buffer[1][v] = 0;
}
dsp_counter = 0;
}
int32 bDSP::clamp(int32 bits, int32 x) {
int32 b = 1 << (bits - 1);
if(x > (b - 1)) {
return (b - 1);
} else if(x < -b) {
return -b;
} else {
return x;
}
}
int32 bDSP::clip(int32 bits, int32 x) {
int32 b = 1 << (bits - 1);
if(x & b) {
return x | ~(b - 1);
} else {
return x & (b - 1);
}
}
uint32 bDSP::run() {
int v, d;
uint8 pmon;
int32 sample;
int32 msamplel, msampler;
int32 esamplel, esampler;
int32 fir_samplel, fir_sampler;
pmon = status.PMON & ~status.NON & ~1;
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 & mask) {
if(voice[v].env_state != SILENCE && voice[v].env_state != RELEASE) {
voice[v].env_state = RELEASE;
voice[v].AdjustEnvelope();
}
} 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;
voice[v].brr_data[0] = 0;
voice[v].brr_data[1] = 0;
voice[v].brr_data[2] = 0;
voice[v].brr_data[3] = 0;
voice[v].envx = 0;
voice[v].env_state = ATTACK;
voice[v].AdjustEnvelope();
}
}
// status.ENDX &= ~status.kon;
// status.kon = 0;
status.key_flag = false;
}
//update noise
status.noise_ctr += status.noise_rate;
if(status.noise_ctr >= 0x7800) {
status.noise_ctr -= 0x7800;
status.noise_sample = (status.noise_sample >> 1) |
(((status.noise_sample << 14) ^ (status.noise_sample << 13)) & 0x4000);
}
msamplel = msampler = 0;
esamplel = esampler = 0;
for(v=0;v<8;v++) {
if(voice[v].brr_index < -1) {
voice[v].brr_index++;
voice[v].OUTX = voice[v].outx = 0;
voice[v].ENVX = 0;
continue;
}
if(voice[v].brr_index >= 0) {
if(pmon & (1 << v)) {
voice[v].pitch_ctr += (voice[v].pitch_rate() * (voice[v - 1].outx + 0x8000)) >> 15;
} else {
voice[v].pitch_ctr += voice[v].pitch_rate();
}
} else {
voice[v].pitch_ctr = 0x3000;
voice[v].brr_index = 0;
}
//decode BRR samples
while(voice[v].pitch_ctr >= 0) {
voice[v].pitch_ctr -= 0x1000;
voice[v].brr_data_index++;
voice[v].brr_data_index &= 3;
if(voice[v].brr_index == 0) {
voice[v].brr_header = read_8(voice[v].brr_ptr);
if(voice[v].brr_header_flags() & BRR_END) {
status.ENDX |= (1 << v);
}
if(voice[v].brr_header_flags() == BRR_END) {
voice[v].env_state = SILENCE;
voice[v].AdjustEnvelope();
}
}
#define S(x) voice[v].brr_data[(voice[v].brr_data_index + (x)) & 3]
if(voice[v].env_state != SILENCE) {
sample = read_8(voice[v].brr_ptr + 1 + (voice[v].brr_index >> 1));
if(voice[v].brr_index & 1) {
sample = clip(4, sample);
} else {
sample = clip(4, sample >> 4);
}
if(voice[v].brr_header_shift() <= 12) {
sample = (sample << voice[v].brr_header_shift() >> 1);
} else {
sample &= ~0x7ff;
}
switch(voice[v].brr_header_filter()) {
case 0: //direct
break;
case 1: //15/16
sample += S(-1) + ((-S(-1)) >> 4);
break;
case 2: //61/32 - 15/16
sample += (S(-1) << 1) + ((-((S(-1) << 1) + S(-1))) >> 5)
- S(-2) + (S(-2) >> 4);
break;
case 3: //115/64 - 13/16
sample += (S(-1) << 1) + ((-(S(-1) + (S(-1) << 2) + (S(-1) << 3))) >> 6)
- S(-2) + (((S(-2) << 1) + S(-2)) >> 4);
break;
}
S(0) = sample = clip(15, clamp(16, sample));
} else {
S(0) = sample = 0;
}
if(++voice[v].brr_index > 15) {
voice[v].brr_index = 0;
if(voice[v].brr_header_flags() & BRR_END) {
voice[v].brr_ptr = read_16((status.DIR << 8) + (voice[v].SRCN << 2) + 2);
voice[v].brr_looped = true;
} else {
voice[v].brr_ptr += 9;
}
}
}
//volume envelope adjust
voice[v].env_ctr += voice[v].env_rate;
if(voice[v].env_ctr >= 0x7800) {
voice[v].env_ctr -= 0x7800;
switch(voice[v].env_mode) {
case DIRECT:
voice[v].env_rate = 0;
break;
case LINEAR_DEC:
voice[v].envx -= 32;
if(voice[v].envx <= 0) {
voice[v].envx = 0;
voice[v].env_rate = 0;
voice[v].env_mode = DIRECT;
}
break;
case LINEAR_INC:
voice[v].envx += 32;
if(voice[v].envx >= 0x7ff) {
voice[v].envx = 0x7ff;
voice[v].env_rate = 0;
voice[v].env_mode = DIRECT;
if(voice[v].ADSR_enabled() && voice[v].env_state == ATTACK) {
voice[v].env_state = ((voice[v].env_sustain == 0x800) ? SUSTAIN : DECAY);
voice[v].AdjustEnvelope();
}
}
break;
case EXP_DEC:
//multiply by 255/256ths
voice[v].envx -= ((voice[v].envx - 1) >> 8) + 1;
if(voice[v].ADSR_enabled() && voice[v].env_state == DECAY && voice[v].envx <= voice[v].env_sustain) {
voice[v].env_state = SUSTAIN;
voice[v].AdjustEnvelope();
} else if(voice[v].envx <= 0) {
voice[v].envx = 0;
voice[v].env_rate = 0;
voice[v].env_mode = DIRECT;
}
break;
case BENT_INC:
if(voice[v].envx < 0x600) {
voice[v].envx += 32;
} else {
voice[v].envx += 8;
if(voice[v].envx >= 0x7ff) {
voice[v].envx = 0x7ff;
voice[v].env_rate = 0;
voice[v].env_mode = DIRECT;
}
}
break;
case FAST_ATTACK:
voice[v].envx += 0x400;
if(voice[v].envx >= 0x7ff) {
voice[v].envx = 0x7ff;
//attack raises to max. envx, if sustain is also set to max. envx, skip decay phase
voice[v].env_state = ((voice[v].env_sustain == 0x800) ? SUSTAIN : DECAY);
voice[v].AdjustEnvelope();
}
break;
case RELEASE_DEC:
voice[v].envx -= 8;
if(voice[v].envx <= 0) {
voice[v].env_state = SILENCE;
voice[v].AdjustEnvelope();
}
break;
}
}
voice[v].ENVX = voice[v].envx >> 4;
//gaussian interpolation / noise
if(status.NON & (1 << v)) {
sample = status.noise_sample;
} else {
d = voice[v].pitch_ctr >> 4; //-256 <= sample <= -1
sample = ((GaussTable[ -1-d] * S(-3)) >> 11);
sample += ((GaussTable[255-d] * S(-2)) >> 11);
sample += ((GaussTable[512+d] * S(-1)) >> 11);
sample = clip (15, sample);
sample += ((GaussTable[256+d] * S( 0)) >> 11);
sample = clamp(15, sample);
}
#undef S
//envelope / volume adjust
sample = (sample * voice[v].envx) >> 11;
voice[v].outx = sample << 1;
voice[v].OUTX = sample >> 7;
if(!status.mute()) {
msamplel += ((sample * voice[v].VOLL) >> 7) << 1;
msampler += ((sample * voice[v].VOLR) >> 7) << 1;
}
if((status.EON & (1 << v)) && status.echo_write()) {
esamplel += ((sample * voice[v].VOLL) >> 7) << 1;
esampler += ((sample * voice[v].VOLR) >> 7) << 1;
}
}
//echo (FIR) adjust
#define F(c,x) status.fir_buffer[c][(status.fir_buffer_index+(x)) & 7]
status.fir_buffer_index++;
F(0,0) = read_16((status.ESA << 8) + status.echo_index);
F(1,0) = read_16((status.ESA << 8) + status.echo_index + 2);
fir_samplel = (F(0,-0) * status.FIR[7] +
F(0,-1) * status.FIR[6] +
F(0,-2) * status.FIR[5] +
F(0,-3) * status.FIR[4] +
F(0,-4) * status.FIR[3] +
F(0,-5) * status.FIR[2] +
F(0,-6) * status.FIR[1] +
F(0,-7) * status.FIR[0]);
fir_sampler = (F(1,-0) * status.FIR[7] +
F(1,-1) * status.FIR[6] +
F(1,-2) * status.FIR[5] +
F(1,-3) * status.FIR[4] +
F(1,-4) * status.FIR[3] +
F(1,-5) * status.FIR[2] +
F(1,-6) * status.FIR[1] +
F(1,-7) * status.FIR[0]);
#undef F
//update echo buffer
if(status.echo_write()) {
esamplel += (fir_samplel * status.EFB) >> 14;
esampler += (fir_sampler * status.EFB) >> 14;
esamplel = clamp(16, esamplel);
esampler = clamp(16, esampler);
write_16((status.ESA << 8) + status.echo_index, esamplel);
write_16((status.ESA << 8) + status.echo_index + 2, esampler);
}
status.echo_index += 4;
if(status.echo_index >= status.echo_target) {
status.echo_index = 0;
status.echo_target = status.echo_size;
}
//main output adjust
if(!status.mute()) {
msamplel = (msamplel * status.MVOLL) >> 7;
msampler = (msampler * status.MVOLR) >> 7;
msamplel += (fir_samplel * status.EVOLL) >> 14;
msampler += (fir_sampler * status.EVOLR) >> 14;
msamplel = clamp(16, msamplel);
msampler = clamp(16, msampler);
}
return (uint32)(((uint16)msamplel) | ((uint16)msampler << 16));
}
bDSP::bDSP() {}
bDSP::~bDSP() {}

176
src/dsp/bdsp/bdsp.h Normal file
View File

@@ -0,0 +1,176 @@
class bDSP : public DSP {
private:
uint8 dspram[128];
uint8 *spcram;
uint32 dsp_counter;
enum {
BRR_END = 1,
BRR_LOOP = 2
};
uint8 read_8 (uint16 addr);
uint16 read_16 (uint16 addr);
void write_8 (uint16 addr, uint8 data);
void write_16(uint16 addr, uint16 data);
public:
static const uint16 RateTable[32];
static const int16 GaussTable[512];
enum EnvelopeStates {
ATTACK,
DECAY,
SUSTAIN,
RELEASE,
SILENCE
};
enum EnvelopeModes {
DIRECT,
LINEAR_DEC,
EXP_DEC,
LINEAR_INC,
BENT_INC,
FAST_ATTACK,
RELEASE_DEC
};
private:
struct Status {
//$0c,$1c
int8 MVOLL, MVOLR;
//$2c,$3c
int8 EVOLL, EVOLR;
//$4c,$5c
uint8 KON, KOFF;
//$6c
uint8 FLG;
//$7c
uint8 ENDX;
//$0d
int8 EFB;
//$2d,$3d,$4d
uint8 PMON, NON, EON;
//$5d
uint8 DIR;
//$6d,$7d
uint8 ESA, EDL;
//$xf
int8 FIR[8];
//internal variables
//uint8 kon;
bool key_flag;
int16 noise_ctr, noise_rate;
uint16 noise_sample;
uint16 echo_index, echo_size, echo_target;
int16 fir_buffer[2][8];
uint8 fir_buffer_index;
//functions
bool soft_reset() { return !!(FLG & 0x80); }
bool mute() { return !!(FLG & 0x40); }
bool echo_write() { return !(FLG & 0x20); }
} status;
struct Voice {
//$x0-$x1
int8 VOLL, VOLR;
//$x2-$x3
int16 PITCH;
//$x4
uint8 SRCN;
//$x5-$x7
uint8 ADSR1, ADSR2, GAIN;
//$x8-$x9
uint8 ENVX, OUTX;
//internal variables
int16 pitch_ctr;
int8 brr_index;
uint16 brr_ptr;
uint8 brr_header;
bool brr_looped;
int16 brr_data[4];
uint8 brr_data_index;
int16 envx;
uint16 env_ctr, env_rate, env_sustain;
enum EnvelopeStates env_state;
enum EnvelopeModes env_mode;
int16 outx;
//functions
int16 pitch_rate() { return PITCH & 0x3fff; }
uint8 brr_header_shift() { return brr_header >> 4; }
uint8 brr_header_filter() { return (brr_header >> 2) & 3; }
uint8 brr_header_flags() { return brr_header & 3; }
bool ADSR_enabled() { return !!(ADSR1 & 0x80); }
uint8 ADSR_decay() { return (ADSR1 >> 4) & 7; }
uint8 ADSR_attack() { return ADSR1 & 15; }
uint8 ADSR_sus_level() { return ADSR2 >> 5; }
uint8 ADSR_sus_rate() { return ADSR2 & 31; }
void AdjustEnvelope() {
if(env_state == SILENCE) {
env_mode = DIRECT;
env_rate = 0;
envx = 0;
} else if(env_state == RELEASE) {
env_mode = RELEASE_DEC;
env_rate = 0x7800;
} else if(ADSR_enabled()) {
switch(env_state) {
case ATTACK:
env_rate = RateTable[(ADSR_attack() << 1) + 1];
env_mode = (env_rate == 0x7800) ? FAST_ATTACK : LINEAR_INC;
break;
case DECAY:
env_rate = RateTable[(ADSR_decay() << 1) + 0x10];
env_mode = EXP_DEC;
break;
case SUSTAIN:
env_rate = RateTable[ADSR_sus_rate()];
env_mode = (env_rate == 0) ? DIRECT : EXP_DEC;
break;
}
} else if(GAIN & 0x80) {
switch(GAIN & 0x60) {
case 0x00:env_mode = LINEAR_DEC; break;
case 0x20:env_mode = EXP_DEC; break;
case 0x40:env_mode = LINEAR_INC; break;
case 0x60:env_mode = BENT_INC; break;
}
env_rate = RateTable[GAIN & 0x1f];
} else {
env_mode = DIRECT;
env_rate = 0;
envx = (GAIN & 0x7f) << 4;
}
}
} voice[8];
int32 clamp(int32 bits, int32 x);
int32 clip (int32 bits, int32 x);
public:
uint8 read (uint8 addr);
void write(uint8 addr, uint8 data);
void power();
void reset();
uint32 run();
bDSP();
~bDSP();
};

View File

@@ -0,0 +1,73 @@
const uint16 bDSP::RateTable[32] = {
0x0000, 0x000F, 0x0014, 0x0018, 0x001E, 0x0028, 0x0030, 0x003C,
0x0050, 0x0060, 0x0078, 0x00A0, 0x00C0, 0x00F0, 0x0140, 0x0180,
0x01E0, 0x0280, 0x0300, 0x03C0, 0x0500, 0x0600, 0x0780, 0x0A00,
0x0C00, 0x0F00, 0x1400, 0x1800, 0x1E00, 0x2800, 0x3C00, 0x7800
};
const int16 bDSP::GaussTable[512] = {
0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001,
0x001, 0x001, 0x001, 0x002, 0x002, 0x002, 0x002, 0x002,
0x002, 0x002, 0x003, 0x003, 0x003, 0x003, 0x003, 0x004,
0x004, 0x004, 0x004, 0x004, 0x005, 0x005, 0x005, 0x005,
0x006, 0x006, 0x006, 0x006, 0x007, 0x007, 0x007, 0x008,
0x008, 0x008, 0x009, 0x009, 0x009, 0x00A, 0x00A, 0x00A,
0x00B, 0x00B, 0x00B, 0x00C, 0x00C, 0x00D, 0x00D, 0x00E,
0x00E, 0x00F, 0x00F, 0x00F, 0x010, 0x010, 0x011, 0x011,
0x012, 0x013, 0x013, 0x014, 0x014, 0x015, 0x015, 0x016,
0x017, 0x017, 0x018, 0x018, 0x019, 0x01A, 0x01B, 0x01B,
0x01C, 0x01D, 0x01D, 0x01E, 0x01F, 0x020, 0x020, 0x021,
0x022, 0x023, 0x024, 0x024, 0x025, 0x026, 0x027, 0x028,
0x029, 0x02A, 0x02B, 0x02C, 0x02D, 0x02E, 0x02F, 0x030,
0x031, 0x032, 0x033, 0x034, 0x035, 0x036, 0x037, 0x038,
0x03A, 0x03B, 0x03C, 0x03D, 0x03E, 0x040, 0x041, 0x042,
0x043, 0x045, 0x046, 0x047, 0x049, 0x04A, 0x04C, 0x04D,
0x04E, 0x050, 0x051, 0x053, 0x054, 0x056, 0x057, 0x059,
0x05A, 0x05C, 0x05E, 0x05F, 0x061, 0x063, 0x064, 0x066,
0x068, 0x06A, 0x06B, 0x06D, 0x06F, 0x071, 0x073, 0x075,
0x076, 0x078, 0x07A, 0x07C, 0x07E, 0x080, 0x082, 0x084,
0x086, 0x089, 0x08B, 0x08D, 0x08F, 0x091, 0x093, 0x096,
0x098, 0x09A, 0x09C, 0x09F, 0x0A1, 0x0A3, 0x0A6, 0x0A8,
0x0AB, 0x0AD, 0x0AF, 0x0B2, 0x0B4, 0x0B7, 0x0BA, 0x0BC,
0x0BF, 0x0C1, 0x0C4, 0x0C7, 0x0C9, 0x0CC, 0x0CF, 0x0D2,
0x0D4, 0x0D7, 0x0DA, 0x0DD, 0x0E0, 0x0E3, 0x0E6, 0x0E9,
0x0EC, 0x0EF, 0x0F2, 0x0F5, 0x0F8, 0x0FB, 0x0FE, 0x101,
0x104, 0x107, 0x10B, 0x10E, 0x111, 0x114, 0x118, 0x11B,
0x11E, 0x122, 0x125, 0x129, 0x12C, 0x130, 0x133, 0x137,
0x13A, 0x13E, 0x141, 0x145, 0x148, 0x14C, 0x150, 0x153,
0x157, 0x15B, 0x15F, 0x162, 0x166, 0x16A, 0x16E, 0x172,
0x176, 0x17A, 0x17D, 0x181, 0x185, 0x189, 0x18D, 0x191,
0x195, 0x19A, 0x19E, 0x1A2, 0x1A6, 0x1AA, 0x1AE, 0x1B2,
0x1B7, 0x1BB, 0x1BF, 0x1C3, 0x1C8, 0x1CC, 0x1D0, 0x1D5,
0x1D9, 0x1DD, 0x1E2, 0x1E6, 0x1EB, 0x1EF, 0x1F3, 0x1F8,
0x1FC, 0x201, 0x205, 0x20A, 0x20F, 0x213, 0x218, 0x21C,
0x221, 0x226, 0x22A, 0x22F, 0x233, 0x238, 0x23D, 0x241,
0x246, 0x24B, 0x250, 0x254, 0x259, 0x25E, 0x263, 0x267,
0x26C, 0x271, 0x276, 0x27B, 0x280, 0x284, 0x289, 0x28E,
0x293, 0x298, 0x29D, 0x2A2, 0x2A6, 0x2AB, 0x2B0, 0x2B5,
0x2BA, 0x2BF, 0x2C4, 0x2C9, 0x2CE, 0x2D3, 0x2D8, 0x2DC,
0x2E1, 0x2E6, 0x2EB, 0x2F0, 0x2F5, 0x2FA, 0x2FF, 0x304,
0x309, 0x30E, 0x313, 0x318, 0x31D, 0x322, 0x326, 0x32B,
0x330, 0x335, 0x33A, 0x33F, 0x344, 0x349, 0x34E, 0x353,
0x357, 0x35C, 0x361, 0x366, 0x36B, 0x370, 0x374, 0x379,
0x37E, 0x383, 0x388, 0x38C, 0x391, 0x396, 0x39B, 0x39F,
0x3A4, 0x3A9, 0x3AD, 0x3B2, 0x3B7, 0x3BB, 0x3C0, 0x3C5,
0x3C9, 0x3CE, 0x3D2, 0x3D7, 0x3DC, 0x3E0, 0x3E5, 0x3E9,
0x3ED, 0x3F2, 0x3F6, 0x3FB, 0x3FF, 0x403, 0x408, 0x40C,
0x410, 0x415, 0x419, 0x41D, 0x421, 0x425, 0x42A, 0x42E,
0x432, 0x436, 0x43A, 0x43E, 0x442, 0x446, 0x44A, 0x44E,
0x452, 0x455, 0x459, 0x45D, 0x461, 0x465, 0x468, 0x46C,
0x470, 0x473, 0x477, 0x47A, 0x47E, 0x481, 0x485, 0x488,
0x48C, 0x48F, 0x492, 0x496, 0x499, 0x49C, 0x49F, 0x4A2,
0x4A6, 0x4A9, 0x4AC, 0x4AF, 0x4B2, 0x4B5, 0x4B7, 0x4BA,
0x4BD, 0x4C0, 0x4C3, 0x4C5, 0x4C8, 0x4CB, 0x4CD, 0x4D0,
0x4D2, 0x4D5, 0x4D7, 0x4D9, 0x4DC, 0x4DE, 0x4E0, 0x4E3,
0x4E5, 0x4E7, 0x4E9, 0x4EB, 0x4ED, 0x4EF, 0x4F1, 0x4F3,
0x4F5, 0x4F6, 0x4F8, 0x4FA, 0x4FB, 0x4FD, 0x4FF, 0x500,
0x502, 0x503, 0x504, 0x506, 0x507, 0x508, 0x50A, 0x50B,
0x50C, 0x50D, 0x50E, 0x50F, 0x510, 0x511, 0x511, 0x512,
0x513, 0x514, 0x514, 0x515, 0x516, 0x516, 0x517, 0x517,
0x517, 0x518, 0x518, 0x518, 0x518, 0x518, 0x519, 0x519
};

9
src/dsp/dsp.h Normal file
View File

@@ -0,0 +1,9 @@
class DSP {
public:
virtual uint8 read (uint8 addr) = 0;
virtual void write(uint8 addr, uint8 data) = 0;
virtual void power() = 0;
virtual void reset() = 0;
virtual uint32 run() = 0;
};

View File

@@ -1,3 +1,6 @@
#define BSNES_VERSION "0.013"
#define BSNES_TITLE "bsnes v" BSNES_VERSION
#include "reader/reader.h"
#include "memory/memory.h"
@@ -14,6 +17,10 @@ extern CPU *cpu;
#include "apu/bapuskip/bapuskip.h"
extern APU *apu;
#include "dsp/dsp.h"
#include "dsp/bdsp/bdsp.h"
extern DSP *dsp;
#include "ppu/ppu.h"
#include "ppu/bppu/bppu.h"
extern PPU *ppu;
@@ -26,10 +33,14 @@ extern SNES *snes;
extern SRTC *srtc;
extern SDD1 *sdd1;
#include "config/config.h"
#ifdef INTERFACE_MAIN
#include "config/config.cpp"
MemBus *mem_bus;
CPU *cpu;
APU *apu;
DSP *dsp;
PPU *ppu;
SNES *snes;

View File

@@ -18,6 +18,8 @@
#define FALSE 0
#endif
#define zerofree(__n) if(__n) { free(__n); __n = 0; }
typedef unsigned int uint;
typedef unsigned char byte;

View File

@@ -1,273 +1,161 @@
#include "libbase.h"
#include "libconfig.h"
config_item::config_item() {
strcpy(name, "");
strcpy(strdef, "");
is_string = false;
source = 0;
strsource = 0;
def = 0;
type = 0;
void Setting::toggle() {
data ^= 1;
set(data);
}
void config::add(uint32 *variable, char *name, uint32 def, uint32 type) {
int n;
if(item_count >= 4096)return;
n = item_count;
item[n] = new config_item();
strcpy(item[n]->name, name);
item[n]->is_string = false;
item[n]->def = def;
item[n]->source = variable;
*item[n]->source = item[n]->def;
item[n]->type = type;
item_count++;
uint Setting::get() {
return data;
}
void config::add(string *variable, char *name, char *def, uint32 type) {
int n;
if(item_count >= 4096)return;
n = item_count;
void Setting::set(uint _data) {
printf("%s %d\n", name, _data);
data = _data;
item[n] = new config_item();
strcpy(item[n]->name, name);
item[n]->is_string = true;
strcpy(item[n]->strdef, def);
item[n]->strsource = variable;
strcpy(*item[n]->strsource, item[n]->strdef);
item[n]->type = type;
item_count++;
}
uint32 config::find(char *name) {
for(int i=0;i<item_count;i++) {
if(!strcmp(item[i]->name, name)) {
return i;
}
if(type != DEC && type != HEX) {
//data is a boolean type
data &= 1;
}
return null;
}
void config::load(char *fn) {
Setting::Setting(Config *_parent, char *_name, char *_desc, uint _data, uint _type) {
int s;
if(_parent) {
_parent->add(this);
}
s = strlen(_name);
name = (char*)malloc(s + 1);
strcpy(name, _name);
if(_desc) {
s = strlen(_desc);
desc = (char*)malloc(s + 1);
strcpy(desc, _desc);
} else {
desc = (char*)malloc(1);
*desc = 0;
}
data = _data;
def = _data;
type = _type;
}
void Config::add(Setting *setting) {
list[list_count++] = setting;
}
uint Config::string_to_uint(uint type, char *input) {
if(!strcmp(input, "true") ||
!strcmp(input, "enabled") ||
!strcmp(input, "on") ||
!strcmp(input, "yes")
) {
return (uint)true;
}
if(!strcmp(input, "false") ||
!strcmp(input, "disabled") ||
!strcmp(input, "off") ||
!strcmp(input, "no")
) {
return (uint)false;
}
if(!strbegin(input, "0x")) {
return strhex(input + 2);
}
return strdec(input);
}
char *Config::uint_to_string(uint type, uint input) {
static char output[512];
switch(type) {
case Setting::TRUE_FALSE:
sprintf(output, "%s", (input & 1)?"true":"false");
break;
case Setting::ENABLED_DISABLED:
sprintf(output, "%s", (input & 1)?"enabled":"disabled");
break;
case Setting::ON_OFF:
sprintf(output, "%s", (input & 1)?"on":"off");
break;
case Setting::YES_NO:
sprintf(output, "%s", (input & 1)?"yes":"no");
break;
case Setting::DEC:
sprintf(output, "%d", input);
break;
case Setting::HEX:
sprintf(output, "0x%x", input);
break;
}
return output;
}
bool Config::load(char *fn) {
FILE *fp;
char *buffer;
int i, fsize;
uint32 l;
fp = fopen(fn, "rb");
//file doesn't exist yet, do nothing
if(!fp)return;
if(!fp)return false;
//load the config file into memory
fseek(fp, 0, SEEK_END);
fsize = ftell(fp);
int fsize = ftell(fp);
fseek(fp, 0, SEEK_SET);
//blank file, do nothing
if(fsize == 0) {
fclose(fp);
return;
}
buffer = (char*)malloc(fsize + 1);
char *buffer = (char*)malloc(fsize + 1);
fread(buffer, 1, fsize, fp);
fclose(fp);
buffer[fsize] = 0;
*(buffer + fsize) = 0;
strcpy(data, buffer);
free(buffer);
replace(data, "\r\n", "\n");
qreplace(data, "\t", " ");
//split the file into lines
replace(data, "\r\n", "\n");
qreplace(data, "\t", "");
qreplace(data, " ", "");
split(line, "\n", data);
for(i=0;i<count(line);i++) {
qreplace(line[i], " ", "");
//remove comment, if it exists
if(qstrpos(line[i], "#") != null) {
strset(line[i], qstrpos(line[i], "#"), 0);
}
//ignore blank lines
for(int i=0;i<count(line);i++) {
if(strlen(line[i]) == 0)continue;
if(*strptr(line[i]) == '#')continue;
qsplit(part, "=", line[i]);
l = find(strptr(part[0]));
if(l != null) {
//if the config item name is valid... update item value
if(item[l]->is_string == true) {
strunquote(part[1]);
strcpy(*item[l]->strsource, part[1]);
} else {
if(!strcmp(part[1], "true") || !strcmp(part[1], "yes") ||
!strcmp(part[1], "on") || !strcmp(part[1], "enabled")) {
*item[l]->source = 1;
} else if(!strcmp(part[1], "false") || !strcmp(part[1], "no") ||
!strcmp(part[1], "off") || !strcmp(part[1], "disabled")) {
*item[l]->source = 0;
} else if(item[l]->type == HEX) {
*item[l]->source = strhex(strptr(part[1]) + 2); //skip 0x prefix
} else { /* fall back on DEC */
*item[l]->source = strdec(part[1]);
}
split(part, "=", line[i]);
for(int l=0;l<list_count;l++) {
if(!strcmp(list[l]->name, part[0])) {
list[l]->set(string_to_uint(list[l]->type, strptr(part[1])));
}
}
}
}
void config::load(substring &fn) { load(strptr(fn)); }
//create a text string from config item[i] to be output via config->save()
void config::set_newline(int i) {
char t[16];
if(item[i]->is_string == true) {
strcpy(newline, item[i]->name);
strcat(newline, " = \"");
strcat(newline, *item[i]->strsource);
strcat(newline, "\"");
} else {
strcpy(newline, item[i]->name);
strcat(newline, " = ");
switch(item[i]->type) {
case TRUEFALSE:
strcat(newline, (*item[i]->source)?"true":"false");
break;
case YESNO:
strcat(newline, (*item[i]->source)?"yes":"no");
break;
case ONOFF:
strcat(newline, (*item[i]->source)?"on":"off");
break;
case ENABLED:
strcat(newline, (*item[i]->source)?"enabled":"disabled");
break;
case HEX:
sprintf(t, "0x%0.2x", *item[i]->source);
strcat(newline, t);
break;
case DEC:
default:
sprintf(t, "%d", *item[i]->source);
strcat(newline, t);
break;
}
}
return true;
}
bool Config::load(substring &fn) { return load(strptr(fn)); }
void config::save(char *fn) {
bool Config::save(char *fn) {
FILE *fp;
int i, fsize;
uint32 l;
char *buffer;
uint8 set[4096];
bool blank = false;
fp = fopen(fn, "rb");
if(!fp) {
blank = true;
} else {
fseek(fp, 0, SEEK_END);
fsize = ftell(fp);
fseek(fp, 0, SEEK_SET);
if(fsize == 0) {
blank = true;
} else {
buffer = (char*)malloc(fsize + 1);
fread(buffer, 1, fsize, fp);
buffer[fsize] = 0;
strcpy(data, buffer);
free(buffer);
}
fclose(fp);
}
fp = fopen(fn, "wb");
//no write access?
if(!fp)return;
if(!fp)return false;
//list of config items. if the item is set in the
//existing config file, then don't test it to see
//if it needs to be written again later on
memset(set, 0, item_count);
if(blank == false) {
for(int i=0;i<list_count;i++) {
strcpy(data, list[i]->desc);
replace(data, "\r\n", "\n");
qreplace(data, "\t", " ");
split(line, "\n", data);
split(oldline, "\n", data);
for(i=0;i<count(line);i++) {
qreplace(line[i], " ", "");
if(qstrpos(line[i], "#") != null) {
strset(line[i], qstrpos(line[i], "#"), 0);
}
//this line is empty, restore the old line and continue
if(strlen(line[i]) == 0) {
strcpy(line[i], oldline[i]);
continue;
}
qsplit(part, "=", line[i]);
l = find(strptr(part[0]));
if(l == null) {
//invalid item name, restore the old line and continue
strcpy(line[i], oldline[i]);
continue;
}
set[l] = 1;
set_newline(l);
//copy the user comment from the old config file, if it exists
if(qstrpos(oldline[i], "#") != null) {
strcat(newline, " ");
strcat(newline, strptr(oldline[i]) + qstrpos(oldline[i], "#"));
}
strcpy(line[i], newline);
}
//write out the old config file + changes first
for(i=0;i<count(line);) {
fprintf(fp, "%s", strptr(line[i]));
//write a line feed on all lines but the last.
//keeps the file from growing everytime it is saved
if(++i < count(line))fprintf(fp, "\r\n");
split(line, "\n", data);
for(int l=0;l<count(line);l++) {
fprintf(fp, "# %s\r\n", strptr(line[l]));
}
fprintf(fp, "# (default = %s)\r\n", uint_to_string(list[i]->type, list[i]->def));
fprintf(fp, "%s = %s\r\n\r\n", list[i]->name, uint_to_string(list[i]->type, list[i]->data));
}
int lines_written;
for(i=lines_written=0;i<item_count;i++) {
//if the item was written to the file above...
if(set[i] == 1)continue;
set_newline(i);
//prevent a newline from appearing at the top of the file
//when the config file is created for the first time
if(lines_written == 0 && blank == false)fprintf(fp, "\r\n");
fprintf(fp, "%s\r\n", strptr(newline));
lines_written++;
}
fclose(fp);
return true;
}
void config::save(substring &fn) { save(strptr(fn)); }
bool Config::save(substring &fn) { return save(strptr(fn)); }
config::config() {
item_count = 0;
}
config::~config() {
for(int i=0;i<item_count;i++) {
if(item[i])delete(item[i]);
}
Config::Config() {
list_count = 0;
}

View File

@@ -1,5 +1,5 @@
/*
libconfig : version 0.03 ~byuu (08/20/05)
libconfig : version 0.05 ~byuu (09/13/05)
*/
#ifndef __LIBCONFIG
@@ -7,41 +7,92 @@
#include "libstring.h"
class config_item {
public:
uint32 *source, def, type;
string *strsource, strdef;
bool is_string;
string name;
config_item();
};
class Config;
//operator= is the /only/ overloaded operator that isn't inherited by derived classes.
//similarly, base constructor/destructors with arguments are not inherited.
//the inclusion of the overloaded virtual function 'toggle' is to allow toggle to call
//the overloaded set() function, if it exists. for some reason, Setting::toggle() calls
//Setting::set() no matter what, even if the derived class defines set()...
//the below macro is a quick way to take care of all of these issues.
//usage example:
// class T : public Setting { public: SettingOperators(T); } t;
// t = 0; // -> t.set(0);
#define SettingOperators(__name) \
inline __name &operator=(const bool _data) { set((uint)_data); return *this; } \
inline __name &operator=(const uint _data) { set(_data); return *this; } \
inline __name &operator=(const uint8 _data) { set(_data); return *this; } \
inline __name &operator=(const uint16 _data) { set(_data); return *this; } \
inline __name &operator=(const uint32 _data) { set(_data); return *this; } \
inline __name &operator=(const int _data) { set(_data); return *this; } \
inline __name &operator=(const int8 _data) { set(_data); return *this; } \
inline __name &operator=(const int16 _data) { set(_data); return *this; } \
inline __name &operator=(const int32 _data) { set(_data); return *this; } \
void toggle() { data ^= 1; set(data); } \
__name(Config *_parent, char *_name, char *_desc = 0, uint _data = 0, uint _type = Setting::DEC) : \
Setting(_parent, _name, _desc, _data, _type) {}
class Setting {
friend class Config;
protected:
uint data, type, def;
class config {
private:
uint32 item_count;
config_item *item[4096];
string data, line, oldline, newline, part;
public:
enum {
TRUEFALSE = 0,
YESNO = 1,
ONOFF = 2,
ENABLED = 3,
DEC = 4,
HEX = 5,
STR = 6
TRUE_FALSE,
ENABLED_DISABLED,
ON_OFF,
YES_NO,
BOOL,
DEC,
HEX
};
void add(uint32 *variable, char *name, uint32 def, uint32 type = DEC);
void add(string *variable, char *name, char *def, uint32 type = STR);
uint32 find(char *name);
void load(char *fn);
void load(substring &fn);
void save(char *fn);
void save(substring &fn);
void set_newline(int i);
char *name, *desc;
virtual void toggle();
virtual uint get();
virtual void set(uint _data);
config();
~config();
Setting(Config *_parent, char *_name, char *_desc = 0, uint _data = 0, uint _type = DEC);
inline operator bool() { return (bool)get(); }
inline operator uint() { return get(); }
inline operator uint8() { return get(); }
inline operator uint16() { return get(); }
inline operator uint32() { return get(); }
inline operator int() { return get(); }
inline operator int8() { return get(); }
inline operator int16() { return get(); }
inline operator int32() { return get(); }
inline Setting &operator=(const bool _data) { set((uint)_data); return *this; }
inline Setting &operator=(const uint _data) { set(_data); return *this; }
inline Setting &operator=(const uint8 _data) { set(_data); return *this; }
inline Setting &operator=(const uint16 _data) { set(_data); return *this; }
inline Setting &operator=(const uint32 _data) { set(_data); return *this; }
inline Setting &operator=(const int _data) { set(_data); return *this; }
inline Setting &operator=(const int8 _data) { set(_data); return *this; }
inline Setting &operator=(const int16 _data) { set(_data); return *this; }
inline Setting &operator=(const int32 _data) { set(_data); return *this; }
};
class Config {
protected:
vector<Setting*> list;
uint list_count;
string data, line, part, subpart;
uint string_to_uint(uint type, char *input);
char *uint_to_string(uint type, uint input);
public:
void add(Setting *setting);
bool load(char *fn);
bool load(substring &fn);
bool save(char *fn);
bool save(substring &fn);
Config();
};
#endif

View File

@@ -6,7 +6,7 @@ public:
uint8 *rom, *sram;
uint32 rom_size, sram_size;
uint8 read (uint32 addr);
void write(uint32 addr, byte value);
void write(uint32 addr, uint8 value);
void write_protect(bool r);
void set_cartinfo(CartInfo *ci);

View File

@@ -6,7 +6,7 @@ public:
uint8 *rom, *sram;
uint32 rom_size, sram_size;
uint8 read (uint32 addr);
void write(uint32 addr, byte value);
void write(uint32 addr, uint8 value);
void write_protect(bool r);
void set_cartinfo(CartInfo *ci);

View File

@@ -7,7 +7,7 @@ public:
uint8 *rom, *sram;
uint32 rom_size, sram_size;
uint8 read (uint32 addr);
void write(uint32 addr, byte value);
void write(uint32 addr, uint8 value);
void write_protect(bool r);
void set_cartinfo(CartInfo *ci);

View File

@@ -7,7 +7,7 @@ public:
uint8 *rom, *sram;
uint32 rom_size, sram_size;
uint8 read (uint32 addr);
void write(uint32 addr, byte value);
void write(uint32 addr, uint8 value);
void write_protect(bool r);
void set_cartinfo(CartInfo *ci);

View File

@@ -171,33 +171,39 @@ void bMemBus::get_cartinfo(CartInfo *ci) {
***************************************/
uint8 bMemBus::read(uint32 addr) {
uint32 b, w, r;
addr &= 0xffffff;
b = (addr >> 16);
w = (addr & 0xffff);
static uint32 r;
switch(addr & 0xc00000) {
case 0x400000:
if((addr & 0xfe0000) == 0x7e0000) {
r = wram[addr & 0x01ffff];
break;
}
//fallthrough
case 0xc00000:
r = cart->read(addr);
break;
if(b <= 0x3f) {
if(w <= 0x1fff) {
r = wram[w];
} else if(w <= 0x5fff) {
r = mmio[w - 0x2000]->read(w);
} else {
//case 0x000000:
//case 0x800000:
default:
switch(addr & 0x00e000) {
case 0x0000:
r = wram[addr & 0x1fff];
break;
case 0x2000:
case 0x4000:
r = mmio[(addr - 0x2000) & 0x3fff]->read(addr & 0x7fff);
break;
// case 0x6000:
// case 0x8000:
// case 0xa000:
// case 0xc000:
// case 0xe000:
default:
r = cart->read(addr);
break;
}
} else if(b <= 0x7d) {
r = cart->read(addr);
} else if(b <= 0x7f) {
r = wram[addr & 0x01ffff];
} else if(b <= 0xbf) {
if(w <= 0x1fff) {
r = wram[w];
} else if(w <= 0x5fff) {
r = mmio[w - 0x2000]->read(w);
} else {
r = cart->read(addr);
}
} else {
r = cart->read(addr);
break;
}
snes->notify(SNES::MEM_READ, addr, r);
@@ -205,33 +211,38 @@ uint32 b, w, r;
}
void bMemBus::write(uint32 addr, uint8 value) {
uint32 b, w;
addr &= 0xffffff;
b = (addr >> 16);
w = (addr & 0xffff);
switch(addr & 0xc00000) {
case 0x400000:
if((addr & 0xfe0000) == 0x7e0000) {
wram[addr & 0x01ffff] = value;
break;
}
//fallthrough
case 0xc00000:
cart->write(addr, value);
break;
if(b <= 0x3f) {
if(w <= 0x1fff) {
wram[w] = value;
} else if(w <= 0x5fff) {
mmio[w - 0x2000]->write(w, value);
} else {
//case 0x000000:
//case 0x800000:
default:
switch(addr & 0x00e000) {
case 0x0000:
wram[addr & 0x1fff] = value;
break;
case 0x2000:
case 0x4000:
mmio[(addr - 0x2000) & 0x3fff]->write(addr & 0x7fff, value);
break;
// case 0x6000:
// case 0x8000:
// case 0xa000:
// case 0xc000:
// case 0xe000:
default:
cart->write(addr, value);
break;
}
} else if(b <= 0x7d) {
cart->write(addr, value);
} else if(b <= 0x7f) {
wram[addr & 0x01ffff] = value;
} else if(b <= 0xbf) {
if(w <= 0x1fff) {
wram[w] = value;
} else if(w <= 0x5fff) {
mmio[w - 0x2000]->write(w, value);
} else {
cart->write(addr, value);
}
} else {
cart->write(addr, value);
break;
}
snes->notify(SNES::MEM_WRITE, addr, value);
@@ -243,7 +254,7 @@ void bMemBus::power() {
}
void bMemBus::reset() {
fastROM = false;
set_speed(false);
}
bMemBus::bMemBus() {
@@ -253,5 +264,5 @@ bMemBus::bMemBus() {
}
bMemBus::~bMemBus() {
if(wram)free(wram);
zerofree(wram);
}

View File

@@ -18,13 +18,13 @@ bool rom_loaded;
enum { LOROM = 0x20, HIROM = 0x21, EXLOROM = 0x22, EXHIROM = 0x25 };
uint8 read (uint32 addr);
void write(uint32 addr, byte value);
void write(uint32 addr, uint8 value);
bool load_cart(Reader *rf);
bool load_sram(Reader *rf);
bool save_sram(Writer *wf);
void unload_cart();
void get_cartinfo(CartInfo *ci);
bool load_cart(Reader *rf);
bool load_sram(Reader *rf);
bool save_sram(Writer *wf);
void unload_cart();
void get_cartinfo(CartInfo *ci);
void power();
void reset();

View File

@@ -1,6 +1,6 @@
#include "../base.h"
uint16 Memory::read_word(uint32 addr, uint8 wrap) {
uint16 Memory::read_word(uint32 addr, uint wrap) {
uint16 r;
switch(wrap) {
case WRAP_NONE:
@@ -19,24 +19,24 @@ uint16 r;
return r;
}
void Memory::write_word(uint32 addr, uint16 value, uint8 wrap) {
void Memory::write_word(uint32 addr, uint16 data, uint wrap) {
switch(wrap) {
case WRAP_NONE:
write(addr, value);
write(addr + 1, value >> 8);
write(addr, data);
write(addr + 1, data >> 8);
return;
case WRAP_BANK:
write(addr, value);
write((addr & 0xff0000) | ((addr + 1) & 0xffff), value >> 8);
write(addr, data);
write((addr & 0xff0000) | ((addr + 1) & 0xffff), data >> 8);
return;
case WRAP_PAGE:
write(addr, value);
write((addr & 0xffff00) | ((addr + 1) & 0xff), value >> 8);
write(addr, data);
write((addr & 0xffff00) | ((addr + 1) & 0xff), data >> 8);
return;
}
}
uint32 Memory::read_long(uint32 addr, uint8 wrap) {
uint32 Memory::read_long(uint32 addr, uint wrap) {
uint32 r;
switch(wrap) {
case WRAP_NONE:
@@ -58,22 +58,22 @@ uint32 r;
return r;
}
void Memory::write_long(uint32 addr, uint32 value, uint8 wrap) {
void Memory::write_long(uint32 addr, uint32 data, uint wrap) {
switch(wrap) {
case WRAP_NONE:
write(addr, value);
write(addr + 1, value >> 8);
write(addr + 2, value >> 16);
write(addr, data);
write(addr + 1, data >> 8);
write(addr + 2, data >> 16);
return;
case WRAP_BANK:
write(addr, value);
write((addr & 0xff0000) | ((addr + 1) & 0xffff), value >> 8);
write((addr & 0xff0000) | ((addr + 2) & 0xffff), value >> 16);
write(addr, data);
write((addr & 0xff0000) | ((addr + 1) & 0xffff), data >> 8);
write((addr & 0xff0000) | ((addr + 2) & 0xffff), data >> 16);
return;
case WRAP_PAGE:
write(addr, value);
write((addr & 0xffff00) | ((addr + 1) & 0xff), value >> 8);
write((addr & 0xffff00) | ((addr + 2) & 0xff), value >> 16);
write(addr, data);
write((addr & 0xffff00) | ((addr + 1) & 0xff), data >> 8);
write((addr & 0xffff00) | ((addr + 2) & 0xff), data >> 16);
return;
}
}
@@ -82,16 +82,30 @@ MMIO mmio_unmapped;
uint8 MMIO::read (uint32 addr) { return cpu->regs.mdr; }
void MMIO::write(uint32 addr, uint8 value) {}
uint8 MemBus::speed(uint32 addr) {
uint8 MemBus::calc_speed(uint32 addr, bool fast) {
if((addr & 0xc00000) == 0x400000)return 8;
if((addr & 0x808000) == 0x808000)return fastROM?6:8;
if((addr & 0xc00000) == 0xc00000)return fastROM?6:8;
if((addr & 0x808000) == 0x808000)return fast?6:8;
if((addr & 0xc00000) == 0xc00000)return fast?6:8;
if((addr & 0xe000) == 0x2000)return 6;
if((addr & 0xfe00) == 0x4000)return 12;
if((addr & 0xe000) == 0x4000)return 6;
return 8;
}
uint8 MemBus::speed(uint32 addr) {
return speed_table[addr >> 9];
}
void MemBus::set_speed(bool fast) {
fastROM = fast;
if(fastROM) {
speed_table = (uint8*)speed_table_fastrom;
} else {
speed_table = (uint8*)speed_table_slowrom;
}
}
void MemBus::flush_mmio_mappers() {
for(int i=0;i<0x4000;i++) {
mmio[i] = &mmio_unmapped;
@@ -99,7 +113,7 @@ void MemBus::flush_mmio_mappers() {
}
bool MemBus::set_mmio_mapper(uint16 addr, MMIO *mapper) {
/* out of range? */
//out of range?
if(addr < 0x2000 || addr >= 0x6000)return false;
mmio[(addr - 0x2000) & 0x3fff] = mapper;
@@ -110,6 +124,12 @@ MemBus::MemBus() {
int i;
fastROM = false;
flush_mmio_mappers();
for(i=0;i<32768;i++) {
speed_table_slowrom[i] = calc_speed(i << 9, false);
speed_table_fastrom[i] = calc_speed(i << 9, true);
}
speed_table = (uint8*)speed_table_slowrom;
}
MemBus::~MemBus() {}

View File

@@ -1,18 +1,18 @@
class Memory {
public:
enum { WRAP_NONE = 0, WRAP_BANK = 1, WRAP_PAGE = 2 };
virtual uint8 read (uint32 addr) = 0;
virtual void write(uint32 addr, uint8 value) = 0;
virtual uint16 read_word (uint32 addr, uint8 wrap = WRAP_NONE);
virtual void write_word(uint32 addr, uint16 value, uint8 wrap = WRAP_NONE);
virtual uint32 read_long (uint32 addr, uint8 wrap = WRAP_NONE);
virtual void write_long(uint32 addr, uint32 value, uint8 wrap = WRAP_NONE);
virtual uint8 read (uint32 addr) = 0;
virtual void write (uint32 addr, uint8 data) = 0;
virtual uint16 read_word (uint32 addr, uint wrap = WRAP_NONE);
virtual void write_word(uint32 addr, uint16 data, uint wrap = WRAP_NONE);
virtual uint32 read_long (uint32 addr, uint wrap = WRAP_NONE);
virtual void write_long(uint32 addr, uint32 data, uint wrap = WRAP_NONE);
};
typedef struct {
uint8 *rom, *sram;
uint32 rom_size, sram_size;
}CartInfo;
uint8 *rom, *sram;
uint32 rom_size, sram_size;
} CartInfo;
class Cart : public Memory {
public:
@@ -31,15 +31,26 @@ public:
Cart *cart;
MMIO *mmio[0x4000];
bool fastROM;
virtual void flush_mmio_mappers();
virtual bool set_mmio_mapper(uint16 addr, MMIO *mapper);
virtual uint8 speed(uint32 addr);
void flush_mmio_mappers();
bool set_mmio_mapper(uint16 addr, MMIO *mapper);
virtual bool load_cart(Reader *rf) = 0;
virtual bool load_sram(Reader *rf) = 0;
virtual bool save_sram(Writer *wf) = 0;
virtual void unload_cart() = 0;
virtual void get_cartinfo(CartInfo *ci) = 0;
private:
//0x1000000 / 512 = 32768
//512 = 0x200, smallest block of a different-speed memory range
//ex. $4000-$41ff = 512
uint8 *speed_table,
speed_table_slowrom[32768],
speed_table_fastrom[32768];
inline uint8 calc_speed(uint32 addr, bool fast);
public:
uint8 speed(uint32 addr);
void set_speed(bool fast);
virtual bool load_cart(Reader *rf) = 0;
virtual bool load_sram(Reader *rf) = 0;
virtual bool save_sram(Writer *wf) = 0;
virtual void unload_cart() = 0;
virtual void get_cartinfo(CartInfo *ci) = 0;
virtual void power() = 0;
virtual void reset() = 0;

View File

@@ -6,7 +6,7 @@ void bPPU::run() {}
void bPPU::scanline() {
_y = cpu->vcounter();
_screen_width = (regs.bg_mode == 5 || regs.bg_mode == 6)?512:256;
_screen_width = (regs.bg_mode == 5 || regs.bg_mode == 6) ? 512 : 256;
_interlace = cpu->interlace();
_interlace_field = cpu->interlace_field();
@@ -25,50 +25,23 @@ void bPPU::scanline() {
}
}
if(_y == (cpu->overscan()?239:224) && regs.display_disabled == false) {
if(_y == (cpu->overscan() ? 239 : 224) && regs.display_disabled == false) {
//OAM address reset
regs.oam_addr = ((regs.oam_addrh << 8) | regs.oam_addrl) << 1;
}
}
//only allow frameskip setting to ignore actual rendering; not RTO, etc.
if(settings.frameskip_pos != 0)return;
void bPPU::render_scanline() {
if(status.render_output == false)return;
if(_y > 0 && _y < (cpu->overscan()?239:224)) {
if(regs.bg_mode == 5 || regs.bg_mode == 6) {
output->hires = true;
output->line[_y].hires = true;
}
if(_interlace == true) {
output->interlace = true;
output->line[_y].interlace = true;
}
if(_y > 0 && _y < (cpu->overscan() ? 239 : 224)) {
render_line();
}
}
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);
output->hires = false;
output->interlace = false;
for(int i=0;i<239;i++) {
output->line[i].hires = false;
output->line[i].interlace = false;
}
}
void bPPU::set_frameskip(int fs) {
settings.frameskip = fs;
settings.frameskip_changed = true;
}
void bPPU::power() {
@@ -82,7 +55,6 @@ void bPPU::power() {
}
void bPPU::reset() {
memset(output->buffer, 0, 512 * 478 * 2);
frame();
memset(sprite_list, 0, sizeof(sprite_list));
@@ -173,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;
@@ -205,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;
@@ -225,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;
@@ -242,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;
@@ -277,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) {
@@ -329,15 +306,11 @@ 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*)memalloc(65536, "bPPU::vram");
oam = (uint8*)memalloc( 544, "bPPU::oam");
cgram = (uint8*)memalloc( 512, "bPPU::cgram");
vram = (uint8*)malloc(65536);
oam = (uint8*)malloc( 544);
cgram = (uint8*)malloc( 512);
memset(vram, 0, 65536);
memset(oam, 0, 544);
memset(cgram, 0, 512);
@@ -346,7 +319,7 @@ bPPU::bPPU() {
int i, l;
uint8 r, g, b;
float m;
double m;
uint16 *ptr;
for(l=0;l<16;l++) {
mosaic_table[l] = (uint16*)malloc(4096 * 2);
@@ -358,7 +331,7 @@ uint16 *ptr;
light_table = (uint16*)malloc(16 * 32768 * 2);
ptr = (uint16*)light_table;
for(l=0;l<16;l++) {
m = (float)l / 15.0;
m = (double)l / 15.0;
for(i=0;i<32768;i++) {
r = (i ) & 31;
g = (i >> 5) & 31;
@@ -366,9 +339,9 @@ uint16 *ptr;
if(l == 0) { r = g = b = 0; }
else if(l == 15);
else {
r = (uint8)((float)r * m);
g = (uint8)((float)g * m);
b = (uint8)((float)b * m);
r = (uint8)((double)r * m);
g = (uint8)((double)g * m);
b = (uint8)((double)b * m);
}
*ptr++ = (r) | (g << 5) | (b << 10);
}
@@ -378,30 +351,15 @@ uint16 *ptr;
bPPU::~bPPU() {
delete(mmio);
if(vram) {
free(vram);
vram = 0;
}
if(oam) {
free(oam);
oam = 0;
}
if(cgram) {
free(cgram);
cgram = 0;
}
zerofree(vram);
zerofree(oam);
zerofree(cgram);
for(int i=0;i<16;i++) {
if(mosaic_table[i]) {
free(mosaic_table[i]);
mosaic_table[i] = 0;
}
zerofree(mosaic_table[i]);
}
if(light_table) {
memfree(light_table);
light_table = 0;
}
zerofree(light_table);
}
bPPUMMIO::bPPUMMIO(bPPU *_ppu) {

View File

@@ -14,7 +14,7 @@ uint8 *vram, *oam, *cgram;
uint8 region;
enum { NTSC = 0, PAL = 1 };
enum { BG1 = 0, BG2 = 1, BG3 = 2, BG4 = 3, OAM = 4, BACK = 5 };
enum { BG1 = 0, BG2 = 1, BG3 = 2, BG4 = 3, OAM = 4, BACK = 5, COL = 5 };
enum { SC_32x32 = 0, SC_32x64 = 1, SC_64x32 = 2, SC_64x64 = 3 };
struct sprite_item {
@@ -25,12 +25,7 @@ struct sprite_item {
bool vflip, hflip;
uint8 palette;
uint8 priority;
}sprite_list[128];
struct {
int32 frameskip, frameskip_pos;
bool frameskip_changed;
}settings;
} sprite_list[128];
struct {
//open bus support
@@ -95,34 +90,32 @@ 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;
bool addsub_mode;
bool direct_color;
//$2131
bool color_mode, color_halve;
bool bg_color_enabled[6];
bool color_enabled[6];
//$2132
uint8 color_r, color_g, color_b;
@@ -145,7 +138,7 @@ struct {
//$213e
bool time_over, range_over;
uint16 oam_itemcount, oam_tilecount;
}regs;
} regs;
uint8 vram_read (uint16 addr);
void vram_write (uint16 addr, uint8 value);
uint8 oam_read (uint16 addr);
@@ -157,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
@@ -225,7 +220,7 @@ struct {
void latch_counters();
/* PPU render functions */
//PPU render functions
#include "bppu_render.h"
@@ -234,13 +229,15 @@ uint16 *mosaic_table[16];
void render_line();
void update_oam_status();
/* Required functions */
//required functions
void run();
void scanline();
void render_scanline();
void frame();
void power();
void reset();
void set_frameskip(int fs);
bool scanline_is_hires() { return (regs.bg_mode == 5 || regs.bg_mode == 6); }
bPPU();
~bPPU();

View File

@@ -16,6 +16,55 @@ uint16 addr;
return (addr << 1);
}
bool bPPU::vram_can_read() {
if(regs.display_disabled == true) {
return true;
}
uint16 v = cpu->vcounter();
uint16 hc = cpu->hcycles();
uint16 ls;
if(cpu->interlace() && !cpu->interlace_field()) {
ls = cpu->region_scanlines();
} else {
ls = cpu->region_scanlines() - 1;
}
if(v == ls && hc == 1362)return false;
if(v < (cpu->overscan() ? 239 : 224))return false;
if(v == (cpu->overscan() ? 239 : 224)) {
if(hc == 1362)return true;
return false;
}
return true;
}
bool bPPU::vram_can_write(uint8 &value) {
if(regs.display_disabled == true) {
return true;
}
uint16 v = cpu->vcounter();
uint16 hc = cpu->hcycles();
if(v == 0) {
if(hc <= 4)return true;
if(hc == 6) { value = cpu->regs.mdr; return true; }
return false;
}
if(v < (cpu->overscan() ? 240 : 225))return false;
if(v == (cpu->overscan() ? 240 : 225)) {
if(hc <= 4)return false;
return true;
}
return true;
}
//INIDISP
void bPPU::mmio_w2100(uint8 value) {
regs.display_disabled = !!(value & 0x80);
@@ -68,6 +117,13 @@ void bPPU::mmio_w2105(uint8 value) {
regs.bg_tilesize[BG1] = !!(value & 0x10);
regs.bg3_priority = !!(value & 0x08);
regs.bg_mode = (value & 7);
window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true;
window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true;
window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true;
window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true;
window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true;
window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true;
}
//MOSAIC
@@ -185,40 +241,54 @@ void bPPU::mmio_w2115(uint8 value) {
void bPPU::mmio_w2116(uint8 value) {
regs.vram_addr = (regs.vram_addr & 0xff00) | value;
uint16 addr = get_vram_address();
regs.vram_readbuffer = vram_read(addr);
regs.vram_readbuffer |= vram_read(addr + 1) << 8;
if(vram_can_read()) {
regs.vram_readbuffer = vram_read(addr);
regs.vram_readbuffer |= vram_read(addr + 1) << 8;
} else {
regs.vram_readbuffer = 0x0000;
}
}
//VMADDH
void bPPU::mmio_w2117(uint8 value) {
regs.vram_addr = (value << 8) | (regs.vram_addr & 0x00ff);
uint16 addr = get_vram_address();
regs.vram_readbuffer = vram_read(addr);
regs.vram_readbuffer |= vram_read(addr + 1) << 8;
if(vram_can_read()) {
regs.vram_readbuffer = vram_read(addr);
regs.vram_readbuffer |= vram_read(addr + 1) << 8;
} else {
regs.vram_readbuffer = 0x0000;
}
}
//VMDATAL
void bPPU::mmio_w2118(uint8 value) {
uint16 addr = get_vram_address();
vram_write(addr, value);
if(vram_can_write(value)) {
vram_write(addr, value);
bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1;
bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1;
bg_tiledata_state[TILE_8BIT][(addr >> 6)] = 1;
}
if(regs.vram_incmode == 0) {
regs.vram_addr += regs.vram_incsize;
}
bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1;
bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1;
bg_tiledata_state[TILE_8BIT][(addr >> 6)] = 1;
}
//VMDATAH
void bPPU::mmio_w2119(uint8 value) {
uint16 addr = get_vram_address() + 1;
vram_write(addr, value);
if(vram_can_write(value)) {
vram_write(addr, value);
bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1;
bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1;
bg_tiledata_state[TILE_8BIT][(addr >> 6)] = 1;
}
if(regs.vram_incmode == 1) {
regs.vram_addr += regs.vram_incsize;
}
bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1;
bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1;
bg_tiledata_state[TILE_8BIT][(addr >> 6)] = 1;
}
//M7SEL
@@ -284,72 +354,117 @@ void bPPU::mmio_w2122(uint8 value) {
//W12SEL
void bPPU::mmio_w2123(uint8 value) {
regs.bg_window2_enabled[BG2] = !!(value & 0x80);
regs.bg_window2_invert [BG2] = !!(value & 0x40);
regs.bg_window1_enabled[BG2] = !!(value & 0x20);
regs.bg_window1_invert [BG2] = !!(value & 0x10);
regs.bg_window2_enabled[BG1] = !!(value & 0x08);
regs.bg_window2_invert [BG1] = !!(value & 0x04);
regs.bg_window1_enabled[BG1] = !!(value & 0x02);
regs.bg_window1_invert [BG1] = !!(value & 0x01);
regs.window2_enabled[BG2] = !!(value & 0x80);
regs.window2_invert [BG2] = !!(value & 0x40);
regs.window1_enabled[BG2] = !!(value & 0x20);
regs.window1_invert [BG2] = !!(value & 0x10);
regs.window2_enabled[BG1] = !!(value & 0x08);
regs.window2_invert [BG1] = !!(value & 0x04);
regs.window1_enabled[BG1] = !!(value & 0x02);
regs.window1_invert [BG1] = !!(value & 0x01);
window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true;
window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true;
}
//W34SEL
void bPPU::mmio_w2124(uint8 value) {
regs.bg_window2_enabled[BG4] = !!(value & 0x80);
regs.bg_window2_invert [BG4] = !!(value & 0x40);
regs.bg_window1_enabled[BG4] = !!(value & 0x20);
regs.bg_window1_invert [BG4] = !!(value & 0x10);
regs.bg_window2_enabled[BG3] = !!(value & 0x08);
regs.bg_window2_invert [BG3] = !!(value & 0x04);
regs.bg_window1_enabled[BG3] = !!(value & 0x02);
regs.bg_window1_invert [BG3] = !!(value & 0x01);
regs.window2_enabled[BG4] = !!(value & 0x80);
regs.window2_invert [BG4] = !!(value & 0x40);
regs.window1_enabled[BG4] = !!(value & 0x20);
regs.window1_invert [BG4] = !!(value & 0x10);
regs.window2_enabled[BG3] = !!(value & 0x08);
regs.window2_invert [BG3] = !!(value & 0x04);
regs.window1_enabled[BG3] = !!(value & 0x02);
regs.window1_invert [BG3] = !!(value & 0x01);
window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true;
window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true;
}
//WOBJSEL
void bPPU::mmio_w2125(uint8 value) {
regs.color_window2_enabled = !!(value & 0x80);
regs.color_window2_invert = !!(value & 0x40);
regs.color_window1_enabled = !!(value & 0x20);
regs.color_window1_invert = !!(value & 0x10);
regs.bg_window2_enabled[OAM] = !!(value & 0x08);
regs.bg_window2_invert [OAM] = !!(value & 0x04);
regs.bg_window1_enabled[OAM] = !!(value & 0x02);
regs.bg_window1_invert [OAM] = !!(value & 0x01);
regs.window2_enabled[COL] = !!(value & 0x80);
regs.window2_invert [COL] = !!(value & 0x40);
regs.window1_enabled[COL] = !!(value & 0x20);
regs.window1_invert [COL] = !!(value & 0x10);
regs.window2_enabled[OAM] = !!(value & 0x08);
regs.window2_invert [OAM] = !!(value & 0x04);
regs.window1_enabled[OAM] = !!(value & 0x02);
regs.window1_invert [OAM] = !!(value & 0x01);
window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true;
window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true;
}
//WH0
void bPPU::mmio_w2126(uint8 value) {
regs.window1_left = value;
window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true;
window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true;
window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true;
window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true;
window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true;
window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true;
}
//WH1
void bPPU::mmio_w2127(uint8 value) {
regs.window1_right = value;
window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true;
window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true;
window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true;
window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true;
window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true;
window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true;
}
//WH2
void bPPU::mmio_w2128(uint8 value) {
regs.window2_left = value;
window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true;
window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true;
window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true;
window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true;
window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true;
window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true;
}
//WH3
void bPPU::mmio_w2129(uint8 value) {
regs.window2_right = value;
window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true;
window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true;
window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true;
window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true;
window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true;
window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true;
}
//WBGLOG
void bPPU::mmio_w212a(uint8 value) {
regs.bg_window_mask[BG4] = (value >> 6) & 3;
regs.bg_window_mask[BG3] = (value >> 4) & 3;
regs.bg_window_mask[BG2] = (value >> 2) & 3;
regs.bg_window_mask[BG1] = (value ) & 3;
regs.window_mask[BG4] = (value >> 6) & 3;
regs.window_mask[BG3] = (value >> 4) & 3;
regs.window_mask[BG2] = (value >> 2) & 3;
regs.window_mask[BG1] = (value ) & 3;
window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true;
window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true;
window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true;
window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true;
}
//WOBJLOG
void bPPU::mmio_w212b(uint8 value) {
regs.color_window_mask = (value >> 2) & 3;
regs.bg_window_mask[OAM] = (value ) & 3;
regs.window_mask[COL] = (value >> 2) & 3;
regs.window_mask[OAM] = (value ) & 3;
window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true;
window_cache[COL].main_dirty = window_cache[COL].sub_dirty = true;
}
//TM
@@ -372,20 +487,32 @@ void bPPU::mmio_w212d(uint8 value) {
//TMW
void bPPU::mmio_w212e(uint8 value) {
regs.bg_window_enabled[OAM] = !!(value & 0x10);
regs.bg_window_enabled[BG4] = !!(value & 0x08);
regs.bg_window_enabled[BG3] = !!(value & 0x04);
regs.bg_window_enabled[BG2] = !!(value & 0x02);
regs.bg_window_enabled[BG1] = !!(value & 0x01);
regs.window_enabled[OAM] = !!(value & 0x10);
regs.window_enabled[BG4] = !!(value & 0x08);
regs.window_enabled[BG3] = !!(value & 0x04);
regs.window_enabled[BG2] = !!(value & 0x02);
regs.window_enabled[BG1] = !!(value & 0x01);
window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true;
window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true;
window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true;
window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true;
window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true;
}
//TSW
void bPPU::mmio_w212f(uint8 value) {
regs.bgsub_window_enabled[OAM] = !!(value & 0x10);
regs.bgsub_window_enabled[BG4] = !!(value & 0x08);
regs.bgsub_window_enabled[BG3] = !!(value & 0x04);
regs.bgsub_window_enabled[BG2] = !!(value & 0x02);
regs.bgsub_window_enabled[BG1] = !!(value & 0x01);
regs.sub_window_enabled[OAM] = !!(value & 0x10);
regs.sub_window_enabled[BG4] = !!(value & 0x08);
regs.sub_window_enabled[BG3] = !!(value & 0x04);
regs.sub_window_enabled[BG2] = !!(value & 0x02);
regs.sub_window_enabled[BG1] = !!(value & 0x01);
window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true;
window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true;
window_cache[BG3].main_dirty = window_cache[BG3].sub_dirty = true;
window_cache[BG4].main_dirty = window_cache[BG4].sub_dirty = true;
window_cache[OAM].main_dirty = window_cache[OAM].sub_dirty = true;
}
//CGWSEL
@@ -393,18 +520,21 @@ void bPPU::mmio_w2130(uint8 value) {
regs.color_mask = (value >> 6) & 3;
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
@@ -475,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;
@@ -488,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;
@@ -538,9 +676,9 @@ uint8 bPPU::mmio_r213d() {
//STAT77
uint8 bPPU::mmio_r213e() {
uint8 r = 0x00;
r |= (regs.time_over) ?0x80:0x00;
r |= (regs.range_over)?0x40:0x00;
r |= 0x01; //PPU1 version number
r |= (regs.time_over) ? 0x80 : 0x00;
r |= (regs.range_over) ? 0x40 : 0x00;
r |= (ppu1_version & 0x0f);
regs.ppu1_mdr = r;
return regs.ppu1_mdr;
}
@@ -560,7 +698,7 @@ uint8 r = 0x00;
}
r |= (regs.ppu2_mdr & 0x20);
r |= (region << 4); //0 = NTSC, 1 = PAL
r |= 0x03; //PPU2 version number
r |= (ppu2_version & 0x0f);
regs.ppu2_mdr = r;
return regs.ppu2_mdr;
}

View File

@@ -104,32 +104,30 @@ Mode7: ->
1, 2, 3, 4, 5
OAM0, BG1n, OAM1, OAM2, OAM3
***
This appears to be incorrect, possibly should be...
Mode 7 EXTBG: ->
1, 2, 3, 4, 5, 6, 7
BG2B, OAM0, BG1n, OAM1, BG2A, OAM2, OAM3
***
Mode 7 (extbg): ->
1, 2, 3, 4, 5, 6
BG2B, OAM0, OAM1, BG2A, OAM2, OAM3
*/
inline void bPPU::render_line_mode7() {
if(regs.mode7_extbg == false) {
render_line_mode7(2, 0, 0); //bg2 priorities are ignored
render_line_mode7(BG1, 2, 2);
render_line_oam(1, 3, 4, 5);
} else {
render_line_mode7(0, 1, 4); //bg1 priority is ignored
render_line_oam(2, 3, 5, 6);
render_line_mode7(BG1, 3, 3);
render_line_mode7(BG2, 1, 5);
render_line_oam(2, 4, 6, 7);
}
}
void bPPU::render_line() {
if(regs.display_disabled == true) {
memset(output->buffer + (_y << 10), 0, 2048);
memset(snes->get_ppu_output_handle(), 0, 1024);
return;
}
clear_pixel_cache();
build_color_window_tables();
build_window_tables(COL);
switch(regs.bg_mode) {
case 0:render_line_mode0();break;
case 1:render_line_mode1();break;
@@ -140,5 +138,6 @@ void bPPU::render_line() {
case 6:render_line_mode6();break;
case 7:render_line_mode7();break;
}
render_line_output();
}

View File

@@ -18,32 +18,35 @@ enum { TILE_2BIT = 0, TILE_4BIT = 1, TILE_8BIT = 2 };
enum { PC_BG1 = 0x80, PC_BG2 = 0x81, PC_BG3 = 0x82, PC_BG4 = 0x83, PC_OAM = 0x84, PC_BACK = 0x00 };
struct _pixel {
//palette # index for main/subscreen pixels
//0 = transparent / use palette color # 0
uint8 src_main, src_sub;
//bgr555 color data for main/subscreen pixels: 0x0000 = transparent / use palette color # 0
//needs to be bgr555 instead of palette index for direct color mode ($2130 bit 0) to work
uint16 src_main, src_sub;
//indicates source of palette # for main/subscreen (BG1-4, OAM, or back)
uint8 bg_main, bg_sub;
uint8 bg_main, bg_sub;
//true when bg_main == OAM && palette index >= 192, disables color add/sub effects
uint8 color_exempt;
//priority level of src_n. to set src_n,
//the priority of the pixel must be >pri_n
uint8 pri_main, pri_sub;
}pixel_cache[512];
uint8 pri_main, pri_sub;
} pixel_cache[512];
uint8 *bg_tiledata[3];
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);
@@ -55,7 +58,7 @@ uint8 oam_itemlist[32];
struct oam_tileitem {
uint16 x, y, pri, pal, tile;
bool hflip;
}oam_tilelist[34];
} oam_tilelist[34];
enum { OAM_PRI_NONE = 4 };
uint8 oam_line_pal[512], oam_line_pri[512];
@@ -66,15 +69,16 @@ void render_oam_tile(int tile_num);
void render_line_oam(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos);
//bppu_render_mode7.cpp
void render_line_mode7(uint8 bg1_pri, uint8 bg2b_pri, uint8 bg2a_pri);
void render_line_mode7(uint8 bg, uint8 pri0_pos, uint8 pri1_pos);
//bppu_render_addsub.cpp
inline uint16 addsub_pixels(int cdest_index, int cdest_bg, int csrc_index, int csrc_bg);
inline uint16 addsub_pixel(int cdest_index, int cdest_bg);
inline uint16 addsub_pixels(uint32 cdest, uint32 csrc);
inline uint16 addsub_pixel (uint32 cdest);
//bppu_render_line.cpp
enum { BLENDTYPE_BACK = 0, BLENDTYPE_MAIN = 1, BLENDTYPE_SUB = 2, BLENDTYPE_COMBINE = 3 };
inline uint16 get_palette(int index);
inline uint16 get_palette(uint8 index);
inline uint16 get_direct_color(uint8 p, uint8 t);
inline uint16 get_pixel(int x);
inline void render_line_output();

View File

@@ -1,15 +1,6 @@
inline uint16 bPPU::addsub_pixels(int cdest_index, int cdest_bg, int csrc_index, int csrc_bg) {
inline uint16 bPPU::addsub_pixels(uint32 cdest, uint32 csrc) {
int r, g, b;
uint32 cdest = get_palette(cdest_index);
uint32 csrc = get_palette(csrc_index);
uint16 res;
//oam palettes 0-3 are not affected by color add/sub
if(cdest_bg == OAM) {
if(cdest_index < 192) {
return cdest;
}
}
switch(regs.color_mode) {
case 0: //COLORMODE_ADD:
if(regs.color_halve == true) {
@@ -53,18 +44,10 @@ uint16 res;
return 0x0000; //prevent annoying warning message
}
inline uint16 bPPU::addsub_pixel(int cdest_index, int cdest_bg) {
inline uint16 bPPU::addsub_pixel(uint32 cdest) {
int r, g, b;
uint32 cdest = get_palette(cdest_index);
uint32 csrc = (regs.color_r) | (regs.color_g << 5) | (regs.color_b << 10);
uint16 res;
//only oam palettes 4-7 are affected by color add/sub
if(cdest_bg == OAM) {
if(cdest_index < 192) {
return cdest;
}
}
switch(regs.color_mode) {
case 0: //COLORMODE_ADD:
if(regs.color_halve == true && regs.addsub_mode == 0) {

View File

@@ -3,7 +3,7 @@ void bPPU::render_line_bg(uint8 bg, uint8 color_depth, uint8 pri0_pos, uint8 pri
return;
}
int x;
int x;
int _scaddr = regs.bg_scaddr[bg];
int _tdaddr = regs.bg_tdaddr[bg];
bool _bg_enabled = regs.bg_enabled[bg];
@@ -25,17 +25,7 @@ uint16 opt_valid_bit; //offset-per-tile valid flag bit
//entry point. This allows all 256 palette colors
//to be used, instead of just the first 32.
//entry = bg * 32, where 32 is from 8 * 4
uint8 bgpal_index;
if(regs.bg_mode == 0) {
switch(bg) {
case BG1:bgpal_index = 0;break;
case BG2:bgpal_index = 32;break;
case BG3:bgpal_index = 64;break;
case BG4:bgpal_index = 96;break;
}
} else {
bgpal_index = 0;
}
uint8 bgpal_index = (regs.bg_mode == 0) ? (bg << 5) : 0;
uint8 pal_size, tiledata_size;
switch(color_depth) {
@@ -114,37 +104,44 @@ int mosaic_x, mosaic_y;
} else {
mtable = (uint16*)mosaic_table[0];
}
mosaic_x = mtable[bg_x];
mosaic_y = mtable[bg_y];
uint8 tile_x;
uint16 t, base_xpos, base_pos, pos;
uint16 tile_num;
int mirror_x, mirror_y;
uint8 pal_index;
int mirror_x, mirror_y;
uint8 pal_index, pal_num;
uint8 *tile_ptr;
int xpos, ypos;
uint16 map_index, hoffset, voffset, col;
uint8 *wt_main = main_windowtable[bg];
uint8 *wt_sub = sub_windowtable[bg];
build_window_tables(bg);
for(screen_x=0;screen_x<_screen_width;screen_x++) {
//offset-per-tile mode. horizontal OPT is buggy, so it is disabled
//vertical OPT seems to be working OK...
uint8 *wt_main = window_cache[bg].main;
uint8 *wt_sub = window_cache[bg].sub;
screen_x = 0;
do { //for(screen_x=0;screen_x<_screen_width;screen_x++) {
if(regs.bg_mode == 2 || regs.bg_mode == 4 || regs.bg_mode == 6) {
if(regs.bg_mode == 6) {
//hires adjust
tile_x = (mtable[screen_x + (hscroll & 15)] >> 4);
} else {
tile_x = (mtable[screen_x + (hscroll & 7)] >> 3);
}
hoffset = hscroll;
voffset = vscroll;
//tile 0 is unaffected by OPT mode...
if(tile_x != 0) {
tile_x = (tile_x - 1) & 31;
//multiply by two to index into 16-bit table entries
tile_x = ((tile_x - 1) & 31) << 1;
if(regs.bg_mode == 4) {
pos = regs.bg_scaddr[BG3] + (tile_x << 1);
t = *((uint16*)vram + (pos >> 1));
pos = regs.bg_scaddr[BG3] + tile_x;
t = read16(vram, pos);
if(t & opt_valid_bit) {
if(!(t & 0x8000)) {
hoffset = ((t & 0x1ff8) | (hscroll & 7)) & screen_width_mask;
@@ -153,18 +150,19 @@ uint8 *wt_sub = sub_windowtable[bg];
}
}
} else {
pos = regs.bg_scaddr[BG3] + (tile_x << 1);
t = *((uint16*)vram + (pos >> 1));
pos = regs.bg_scaddr[BG3] + tile_x;
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 << 1);
t = *((uint16*)vram + (pos >> 1));
pos = regs.bg_scaddr[BG3] + 64 + tile_x;
t = read16(vram, pos);
if(t & opt_valid_bit) {
voffset = (t & 0x1fff) & screen_height_mask;
}
}
}
mosaic_x = mtable[(screen_x + hoffset) & screen_width_mask ];
mosaic_y = mtable[(screen_y + voffset) & screen_height_mask];
}
@@ -188,31 +186,37 @@ 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));
mirror_y = (t & 0x8000)?1:0;
mirror_x = (t & 0x4000)?1:0;
t = read16(vram, pos);
mirror_y = !!(t & 0x8000);
mirror_x = !!(t & 0x4000);
int _pri;
_pri = (t & 0x2000) ? pri1_pos : pri0_pos;
tile_num = t & 0x03ff;
//16x16 horizontal tile mirroring
if(tile_width == 4) {
if(((mosaic_x & 15) >= 8 && !mirror_x) ||
((mosaic_x & 15) < 8 && mirror_x))tile_num++;
tile_num &= 0x03ff;
}
//16x16 vertical tile mirroring
if(tile_height == 4) {
if(((mosaic_y & 15) >= 8 && !mirror_y) ||
((mosaic_y & 15) < 8 && mirror_y))tile_num += 16;
tile_num &= 0x03ff;
}
tile_num += (_tdaddr >> tiledata_size);
if(bg_td_state[tile_num] == 1) {
render_bg_tile(color_depth, tile_num);
}
pal_index = ((t >> 10) & 7) * pal_size + bgpal_index;
pal_num = ((t >> 10) & 7);
pal_index = pal_num * pal_size + bgpal_index;
if(mirror_y) { ypos = (7 - (mosaic_y & 7)); }
else { ypos = ( (mosaic_y & 7)); }
@@ -220,23 +224,30 @@ int _pri;
//loop while we are rendering from the same tile, as there's no need to do all of the above work
//unless we have rendered all of the visible tile, taking mosaic into account.
tile_ptr = (uint8*)bg_td + (tile_num << 6) + (ypos << 3);
while(1) {
do {
if(mirror_x) { xpos = (7 - (mosaic_x & 7)); }
else { xpos = ( (mosaic_x & 7)); }
col = *(tile_ptr + xpos);
if(col && main_colorwindowtable[screen_x]) {
if(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 {
col = get_palette(col + pal_index);
}
if(_bg_enabled == true && !wt_main[screen_x]) {
if(pixel_cache[screen_x].pri_main < _pri) {
pixel_cache[screen_x].pri_main = _pri;
pixel_cache[screen_x].bg_main = 0x80 | bg;
pixel_cache[screen_x].src_main = col + pal_index;
pixel_cache[screen_x].src_main = col;
pixel_cache[screen_x].color_exempt = false;
}
}
if(_bgsub_enabled == true && !wt_sub[screen_x]) {
if(pixel_cache[screen_x].pri_sub < _pri) {
pixel_cache[screen_x].pri_sub = _pri;
pixel_cache[screen_x].bg_sub = 0x80 | bg;
pixel_cache[screen_x].src_sub = col + pal_index;
pixel_cache[screen_x].src_sub = col;
}
}
}
@@ -246,8 +257,7 @@ int _pri;
mosaic_x = mtable[bg_x];
if(base_xpos != ((mosaic_x >> 3) & 31))break;
screen_x++;
if(screen_x >= _screen_width)break;
}
}
if(++screen_x >= _screen_width)break;
} while(1);
} while(++screen_x < _screen_width);
}

View File

@@ -117,3 +117,17 @@ void bPPU::clear_tiledata_cache() {
memset(bg_tiledata_state[TILE_4BIT], 0, 2048);
memset(bg_tiledata_state[TILE_8BIT], 0, 1024);
}
void bPPU::clear_window_cache() {
for(int i=0;i<6;i++) {
window_cache[i].main_dirty = true;
window_cache[i].sub_dirty = true;
}
build_window_tables(BG1);
build_window_tables(BG2);
build_window_tables(BG3);
build_window_tables(BG4);
build_window_tables(OAM);
build_window_tables(COL);
}

View File

@@ -1,46 +1,55 @@
inline uint16 bPPU::get_palette(int index) {
return *((uint16*)cgram + index);
inline uint16 bPPU::get_palette(uint8 index) {
return read16(cgram, index << 1);
}
inline uint16 bPPU::get_direct_color(uint8 p, uint8 t) {
//p = 00000bgr <palette data>
//t = BBGGGRRR <tilemap data>
//r = 0BBb00GGGg0RRRr0 <return data>
return ((t & 7) << 2) | ((p & 1) << 1) |
(((t >> 3) & 7) << 7) | (((p >> 1) & 1) << 6) |
((t >> 6) << 13) | ((p >> 2) << 12);
}
inline uint16 bPPU::get_pixel(int x) {
_pixel *p = &pixel_cache[x];
uint16 _r;
uint16 _r, src_back = get_palette(0);
if(p->bg_main && p->bg_sub) {
if(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->bg_main & 0x7f, p->src_sub, p->bg_sub & 0x7f);
_r = addsub_pixels(p->src_main, p->src_sub);
} else {
_r = addsub_pixel(p->src_main, p->bg_main & 0x7f);
_r = addsub_pixel(p->src_main);
}
} else {
_r = get_palette(p->src_main);
_r = p->src_main;
}
} else if(p->bg_main) {
if(regs.bg_color_enabled[p->bg_main & 0x7f] && sub_colorwindowtable[x]) {
_r = addsub_pixel(p->src_main, p->bg_main & 0x7f);
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 = get_palette(p->src_main);
_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(0, BACK, p->src_sub, p->bg_sub & 0x7f);
_r = addsub_pixels(src_back, p->src_sub);
} else {
_r = addsub_pixel(0, BACK);
_r = addsub_pixel(src_back);
}
} else {
_r = 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]) {
_r = addsub_pixel(0, BACK);
if(window_cache[COL].main[x]) {
if(regs.color_enabled[BACK] && window_cache[COL].sub[x]) {
_r = addsub_pixel(src_back);
} else {
_r = get_palette(0);
_r = src_back;
}
} else {
_r = 0x0000;
@@ -53,10 +62,7 @@ inline void bPPU::render_line_output() {
int x;
uint16 _r;
uint16 *ptr;
ptr = (uint16*)output->buffer + (_y << 10);
if(_interlace == true) {
ptr += _interlace_field << 9;
}
ptr = (uint16*)snes->get_ppu_output_handle();
uint16 *ltable;
ltable = (uint16*)light_table + (regs.display_brightness << 15);
@@ -64,8 +70,7 @@ uint16 *ltable;
if(_screen_width == 256) {
for(x=0;x<256;x++) {
_r = get_pixel(x);
*ptr = *(ltable + _r);
ptr += 2;
*ptr++ = *(ltable + _r);
}
} else {
for(x=0;x<512;x++) {

View File

@@ -1,62 +1,73 @@
#define CLIP_10BIT_SIGNED(x) \
((x) & ((1 << 10) - 1)) + (((((x) & (1 << 13)) ^ (1 << 13)) - (1 << 13)) >> 3)
/*
bsnes mode7 renderer
#define CAST_WORDTOINT(x) \
(int32)(((x & 0x8000) ? (x | 0xffff0000) : (x & 0x00007fff)))
base algorithm written by anomie
bsnes implementation written by byuu
supports mode 7 + extbg + rotate + zoom + direct color + scrolling + m7sel + windowing + mosaic
does not support pseudo-hires
interlace support is automatic via main rendering routine
*/
//13-bit sign extend
//--s---vvvvvvvvvv -> ssssssvvvvvvvvvv
#define CLIP(x) ( ((x) & 0x2000) ? ( (x) | ~0x03ff) : ((x) & 0x03ff) )
//#define CLIP(x) ( ((x) & 0x03ff) | (((x) & 0x2000) ? -0x0400 : 0) )
void bPPU::render_line_mode7(uint8 bg, uint8 pri0_pos, uint8 pri1_pos) {
if(regs.bg_enabled[bg] == false && regs.bgsub_enabled[bg] == false)return;
int32 x, y;
int32 a, b, c, d, cx, cy;
int32 hofs, vofs;
void bPPU::render_line_mode7(uint8 bg1_pri, uint8 bg2b_pri, uint8 bg2a_pri) {
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;
hoffset = ((int32)regs.m7_hofs << 19) >> 19;
voffset = ((int32)regs.m7_vofs << 19) >> 19;
a = int32(int16(regs.m7a));
b = int32(int16(regs.m7b));
c = int32(int16(regs.m7c));
d = int32(int16(regs.m7d));
centerx = ((int32)regs.m7x << 19) >> 19;
centery = ((int32)regs.m7y << 19) >> 19;
cx = (int32(regs.m7x) << 19) >> 19;
cy = (int32(regs.m7y) << 19) >> 19;
hofs = (int32(regs.m7_hofs) << 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 = window_cache[bg].main;
uint8 *wt_sub = window_cache[bg].sub;
if(regs.mode7_vflip == true) {
yy = 255 - _y;
y = 255 - _y;
} else {
yy = _y;
}
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, _bg;
bool _bg_enabled, _bgsub_enabled;
if(regs.mode7_extbg == false) {
_pri = bg1_pri;
_bg = BG1;
_bg_enabled = regs.bg_enabled[BG1];
_bgsub_enabled = regs.bgsub_enabled[BG1];
} else {
_bg = BG2;
_bg_enabled = regs.bg_enabled[BG2];
_bgsub_enabled = regs.bgsub_enabled[BG2];
y = _y;
}
uint8 *wt_main = main_windowtable[_bg];
uint8 *wt_sub = sub_windowtable[_bg];
build_window_tables(_bg);
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];
}
int32 psx = ((a * CLIP(hofs - cx)) & ~63) + ((b * CLIP(vofs - cy)) & ~63) + ((b * mtable_y[y]) & ~63) + (cx << 8);
int32 psy = ((c * CLIP(hofs - cx)) & ~63) + ((d * CLIP(vofs - cy)) & ~63) + ((d * mtable_y[y]) & ~63) + (cy << 8);
for(x=0;x<256;x++) {
px = ((m7a + m7b) >> 8);
py = ((m7c + m7d) >> 8);
px = psx + (a * mtable_x[x]);
py = psy + (c * mtable_x[x]);
//mask floating-point bits (low 8 bits)
px >>= 8;
py >>= 8;
switch(regs.mode7_repeat) {
case 0: //screen repitition outside of screen area
@@ -68,20 +79,7 @@ uint8 *wt_sub = sub_windowtable[_bg];
tile = vram[(ty * 128 + tx) << 1];
palette = vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1];
break;
case 2: //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;
case 3: //palette color 0 outside of screen area
case 2: //palette color 0 outside of screen area
if(px < 0 || px > 1023 || py < 0 || py > 1023) {
palette = 0;
} else {
@@ -93,46 +91,61 @@ uint8 *wt_sub = sub_windowtable[_bg];
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) {
tile = 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(!palette)goto _end_setpixel;
if(regs.mode7_extbg == false) {
//_pri set at top of function, as it is static
if(regs.mode7_hflip == true) {
_x = 255 - x;
} else {
_x = x;
}
if(bg == BG1) {
_pri = pri0_pos;
} else {
_pri = (palette >> 7) ? bg2a_pri : bg2b_pri;
_pri = (palette >> 7) ? pri1_pos : pri0_pos;
palette &= 0x7f;
if(regs.mode7_hflip == true) {
_x = 255 - x;
} else {
_x = x;
}
}
if(main_colorwindowtable[_x]) {
if(_bg_enabled == true && !wt_main[_x]) {
if(!palette)continue;
if(regs.mode7_hflip == true) {
_x = 255 - x;
} else {
_x = 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...
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 = palette;
pixel_cache[_x].bg_main = 0x80 | bg;
pixel_cache[_x].src_main = col;
pixel_cache[_x].color_exempt = false;
}
}
if(_bgsub_enabled == true && !wt_sub[_x]) {
if(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 = palette;
pixel_cache[_x].bg_sub = 0x80 | bg;
pixel_cache[_x].src_sub = col;
}
}
}
_end_setpixel:
m7a += step_m7a;
m7c += step_m7c;
}
}
#undef CLIP

View File

@@ -207,9 +207,9 @@ int s, x;
bool _bg_enabled = regs.bg_enabled[OAM];
bool _bgsub_enabled = regs.bgsub_enabled[OAM];
uint8 *wt_main = main_windowtable[OAM];
uint8 *wt_sub = sub_windowtable[OAM];
build_window_tables(OAM);
uint8 *wt_main = window_cache[OAM].main;
uint8 *wt_sub = window_cache[OAM].sub;
regs.oam_itemcount = 0;
regs.oam_tilecount = 0;
@@ -240,6 +240,7 @@ uint8 *wt_sub = sub_windowtable[OAM];
regs.range_over |= (regs.oam_itemcount > 32);
if(_bg_enabled == false && _bgsub_enabled == false)return;
int _pri;
for(x=0;x<_screen_width;x++) {
if(oam_line_pri[x] == OAM_PRI_NONE)continue;
@@ -251,19 +252,20 @@ 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;
pixel_cache[x].bg_main = PC_OAM;
pixel_cache[x].src_main = oam_line_pal[x];
pixel_cache[x].src_main = get_palette(oam_line_pal[x]);
pixel_cache[x].color_exempt = (oam_line_pal[x] < 192);
}
}
if(_bgsub_enabled == true && !wt_sub[x]) {
if(pixel_cache[x].pri_sub < _pri) {
pixel_cache[x].pri_sub = _pri;
pixel_cache[x].bg_sub = PC_OAM;
pixel_cache[x].src_sub = oam_line_pal[x];
pixel_cache[x].src_sub = get_palette(oam_line_pal[x]);
}
}
}

View File

@@ -1,11 +1,50 @@
void bPPU::build_window_table(uint8 bg, uint8 *wtbl, bool mainscreen) {
if(mainscreen == true && regs.bg_window_enabled[bg] == false) {
memset(wtbl, 0, _screen_width);
return;
void bPPU::build_window_table(uint8 bg, bool mainscreen) {
uint8 set = true, clr = false;
uint8 *wtbl;
if(mainscreen == true) {
wtbl = (uint8*)window_cache[bg].main;
} else {
wtbl = (uint8*)window_cache[bg].sub;
}
if(mainscreen == false && regs.bgsub_window_enabled[bg] == false) {
memset(wtbl, 0, _screen_width);
return;
if(bg != COL) {
if(mainscreen == true && regs.window_enabled[bg] == false) {
memset(wtbl, 0, _screen_width);
return;
}
if(mainscreen == false && regs.sub_window_enabled[bg] == false) {
memset(wtbl, 0, _screen_width);
return;
}
} else {
uint8 mask;
if(mainscreen == true) {
mask = regs.color_mask;
} else {
mask = regs.colorsub_mask;
}
if(mask == 0) {
//always
memset(wtbl, 1, _screen_width);
return;
}
if(mask == 3) {
//never
memset(wtbl, 0, _screen_width);
return;
}
if(mask == 1) {
//inside window only
set = 1;
clr = 0;
} else { //mask == 2
//outside window only
set = 0;
clr = 1;
}
}
uint16 window1_left, window1_right, window2_left, window2_right;
@@ -24,54 +63,54 @@ bool r;
window2_right <<= 1;
}
if(regs.bg_window1_enabled[bg] == false && regs.bg_window2_enabled[bg] == false) {
memset(wtbl, 0, _screen_width);
} else if(regs.bg_window1_enabled[bg] == true && regs.bg_window2_enabled[bg] == false) {
if(regs.bg_window1_invert[bg] == false) {
if(regs.window1_enabled[bg] == false && regs.window2_enabled[bg] == false) {
memset(wtbl, clr, _screen_width);
} else if(regs.window1_enabled[bg] == true && regs.window2_enabled[bg] == false) {
if(regs.window1_invert[bg] == false) {
for(x=0;x<_screen_width;x++) {
wtbl[x] = (x >= window1_left && x <= window1_right)?true:false;
wtbl[x] = (x >= window1_left && x <= window1_right) ? set : clr;
}
} else {
for(x=0;x<_screen_width;x++) {
wtbl[x] = (x < window1_left || x > window1_right)?true:false;
wtbl[x] = (x < window1_left || x > window1_right) ? set : clr;
}
}
} else if(regs.bg_window1_enabled[bg] == false && regs.bg_window2_enabled[bg] == true) {
if(regs.bg_window2_invert[bg] == false) {
} else if(regs.window1_enabled[bg] == false && regs.window2_enabled[bg] == true) {
if(regs.window2_invert[bg] == false) {
for(x=0;x<_screen_width;x++) {
wtbl[x] = (x >= window2_left && x <= window2_right)?true:false;
wtbl[x] = (x >= window2_left && x <= window2_right) ? set : clr;
}
} else {
for(x=0;x<_screen_width;x++) {
wtbl[x] = (x < window2_left || x > window2_right)?true:false;
wtbl[x] = (x < window2_left || x > window2_right) ? set : clr;
}
}
} else { //if(regs.bg_window1_enabled[bg] == true && regs.bg_window2_enabled[bg] == true) {
} else { //if(regs.window1_enabled[bg] == true && regs.window2_enabled[bg] == true) {
for(x=0;x<_screen_width;x++) {
if(regs.bg_window1_invert[bg] == false) {
if(regs.window1_invert[bg] == false) {
w1_mask = (x >= window1_left && x <= window1_right);
} else {
w1_mask = (x < window1_left || x > window1_right);
}
if(regs.bg_window2_invert[bg] == false) {
if(regs.window2_invert[bg] == false) {
w2_mask = (x >= window2_left && x <= window2_right);
} else {
w2_mask = (x < window2_left || x > window2_right);
}
switch(regs.bg_window_mask[bg]) {
switch(regs.window_mask[bg]) {
case 0: //WINDOWMASK_OR:
wtbl[x] = ((w1_mask | w2_mask) == 1)?true:false;
wtbl[x] = ((w1_mask | w2_mask) == 1) ? set : clr;
break;
case 1: //WINDOWMASK_AND:
wtbl[x] = ((w1_mask & w2_mask) == 1)?true:false;
wtbl[x] = ((w1_mask & w2_mask) == 1) ? set : clr;
break;
case 2: //WINDOWMASK_XOR:
wtbl[x] = ((w1_mask ^ w2_mask) == 1)?true:false;
wtbl[x] = ((w1_mask ^ w2_mask) == 1) ? set : clr;
break;
case 3: //WINDOWMASK_XNOR:
wtbl[x] = ((w1_mask ^ w2_mask) == 0)?true:false;
wtbl[x] = ((w1_mask ^ w2_mask) == 0) ? set : clr;
break;
}
}
@@ -79,105 +118,13 @@ bool r;
}
void bPPU::build_window_tables(uint8 bg) {
build_window_table(bg, main_windowtable[bg], true);
build_window_table(bg, sub_windowtable[bg], false);
}
void bPPU::build_color_window_table(uint8 *wtbl, uint8 mask) {
if(mask == 0) {
//always
memset(wtbl, 1, _screen_width);
return;
if(window_cache[bg].main_dirty == true) {
window_cache[bg].main_dirty = false;
build_window_table(bg, true);
}
if(mask == 3) {
//never
memset(wtbl, 0, _screen_width);
return;
}
int _true, _false;
if(mask == 1) {
//inside window only
_true = 1;
_false = 0;
} else { //mask == 2
//outside window only
_true = 0;
_false = 1;
}
uint16 window1_left, window1_right, window2_left, window2_right;
int w1_mask, w2_mask; //1 = masked, 0 = not masked
int x;
bool r;
window1_left = regs.window1_left;
window1_right = regs.window1_right;
window2_left = regs.window2_left;
window2_right = regs.window2_right;
if(_screen_width == 512) {
window1_left <<= 1;
window1_right <<= 1;
window2_left <<= 1;
window2_right <<= 1;
}
if(regs.color_window1_enabled == false && regs.color_window2_enabled == false) {
memset(wtbl, _false, _screen_width);
} else if(regs.color_window1_enabled == true && regs.color_window2_enabled == false) {
if(regs.color_window1_invert == false) {
for(x=0;x<_screen_width;x++) {
wtbl[x] = (x >= window1_left && x <= window1_right)?_true:_false;
}
} else {
for(x=0;x<_screen_width;x++) {
wtbl[x] = (x < window1_left || x > window1_right)?_true:_false;
}
}
} else if(regs.color_window1_enabled == false && regs.color_window2_enabled == true) {
if(regs.color_window2_invert == false) {
for(x=0;x<_screen_width;x++) {
wtbl[x] = (x >= window2_left && x <= window2_right)?_true:_false;
}
} else {
for(x=0;x<_screen_width;x++) {
wtbl[x] = (x < window2_left || x > window2_right)?_true:_false;
}
}
} else { //if(regs.color_window1_enabled == true && regs.color_window2_enabled == true) {
for(x=0;x<_screen_width;x++) {
if(regs.color_window1_invert == false) {
w1_mask = (x >= window1_left && x <= window1_right);
} else {
w1_mask = (x < window1_left || x > window1_right);
}
if(regs.color_window2_invert == false) {
w2_mask = (x >= window2_left && x <= window2_right);
} else {
w2_mask = (x < window2_left || x > window2_right);
}
switch(regs.color_window_mask) {
case 0: //WINDOWMASK_OR:
wtbl[x] = ((w1_mask | w2_mask) == 1)?_true:_false;
break;
case 1: //WINDOWMASK_AND:
wtbl[x] = ((w1_mask & w2_mask) == 1)?_true:_false;
break;
case 2: //WINDOWMASK_XOR:
wtbl[x] = ((w1_mask ^ w2_mask) == 1)?_true:_false;
break;
case 3: //WINDOWMASK_XNOR:
wtbl[x] = ((w1_mask ^ w2_mask) == 0)?_true:_false;
break;
}
}
if(window_cache[bg].sub_dirty == true) {
window_cache[bg].sub_dirty = false;
build_window_table(bg, false);
}
}
void bPPU::build_color_window_tables() {
build_color_window_table(main_colorwindowtable, regs.color_mask);
build_color_window_table(sub_colorwindowtable, regs.colorsub_mask);
}

View File

@@ -1,27 +1,38 @@
#include "../base.h"
PPUOutput::PPUOutput() {
buffer = (uint16*)memalloc(512 * 478 * 2, "PPUOutput::buffer");
memset(buffer, 0, 512 * 478 * 2);
hires = false;
interlace = false;
for(int i=0;i<239;i++) {
line[i].hires = false;
line[i].interlace = false;
void PPU::get_scanline_info(scanline_info *info) {
info->hires = scanline_is_hires();
info->interlace = cpu->interlace();
}
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;
}
PPUOutput::~PPUOutput() {
if(buffer)memfree(buffer, "PPUOutput::buffer");
}
void PPU::set_frameskip(int fs) {}
PPU::PPU() {
status.render_output = true;
status.frames_updated = false;
status.frames_rendered = 0;
status.frames_executed = 0;
ppu1_version = 1;
ppu2_version = 1;
mmio = &mmio_unmapped;
output = new PPUOutput();
}
PPU::~PPU() {
if(output)delete(output);
}
PPU::~PPU() {}

View File

@@ -1,19 +1,36 @@
class PPUOutput {
public:
bool hires, interlace;
struct {
bool hires, interlace;
}line[239];
uint16 *buffer;
PPUOutput();
~PPUOutput();
};
class PPU {
public:
int _y;
PPUOutput *output;
MMIO *mmio;
//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
uint8 ppu1_version;
//PPU2 version number
//* 1 and 3 are known
//* reported by $213f
uint8 ppu2_version;
int _y;
MMIO *mmio;
struct scanline_info {
bool hires;
bool interlace;
};
virtual bool scanline_is_hires() = 0;
virtual void get_scanline_info(scanline_info *info);
virtual uint8 vram_read (uint16 addr) = 0;
virtual void vram_write (uint16 addr, uint8 value) = 0;
virtual uint8 oam_read (uint16 addr) = 0;
@@ -25,10 +42,12 @@ MMIO *mmio;
virtual void run() = 0;
virtual void scanline() = 0;
virtual void frame() = 0;
virtual void render_scanline() = 0;
virtual void frame();
virtual void power() = 0;
virtual void reset() = 0;
virtual void set_frameskip(int fs);
virtual void enable_renderer(bool r);
virtual bool renderer_enabled();
PPU();
~PPU();

View File

@@ -1,11 +1,12 @@
CC = c++
CFLAGS = -O2
CFLAGS = -O3 -fomit-frame-pointer -ffast-math
OBJS = sdlmain.o \
libstring.o libconfig.o \
reader.o \
memory.o bmemory.o \
cpu.o bcpu.o \
apu.o bapu.o bapuskip.o \
bdsp.o \
ppu.o bppu.o \
snes.o \
srtc.o sdd1.o
@@ -60,6 +61,12 @@ bapu.o: ../apu/bapu/*
bapuskip.o: ../apu/bapuskip/*
$(CC) $(CFLAGS) -c ../apu/bapuskip/bapuskip.cpp
###########
### dsp ###
###########
bdsp.o: ../dsp/bdsp/*
$(CC) $(CFLAGS) -c ../dsp/bdsp/bdsp.cpp
###########
### ppu ###
###########

View File

@@ -1,11 +1,12 @@
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 \
memory.obj bmemory.obj \
cpu.obj bcpu.obj \
apu.obj bapu.obj bapuskip.obj \
bdsp.obj \
ppu.obj bppu.obj \
snes.obj \
srtc.obj sdd1.obj
@@ -61,6 +62,12 @@ bapu.obj: ../apu/bapu/*
bapuskip.obj: ../apu/bapuskip/*
$(CC) $(CFLAGS) /c ../apu/bapuskip/bapuskip.cpp
###########
### dsp ###
###########
bdsp.obj: ../dsp/bdsp/*
$(CC) $(CFLAGS) /c ../dsp/bdsp/bdsp.cpp
###########
### ppu ###
###########

View File

@@ -1,13 +1,13 @@
void bSNES::set_status(uint32 new_status) { run_status = new_status; }
uint32 bSNES::get_status() { return run_status; }
void bSNES::snes_run() {
void bSNES::run() {
if(!rom_image->loaded())return;
switch(run_status) {
case RUN:
while(update_frame == false) {
run();
SNES::run();
}
update_frame = false;
render();
@@ -17,29 +17,46 @@ void bSNES::snes_run() {
}
}
void bSNES::render_frame() {}
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[cfg.input.joypad1.up];
joypad1.down = keystate[cfg.input.joypad1.down];
joypad1.left = keystate[cfg.input.joypad1.left];
joypad1.right = keystate[cfg.input.joypad1.right];
joypad1.select = keystate[cfg.input.joypad1.select];
joypad1.start = keystate[cfg.input.joypad1.start];
joypad1.y = keystate[cfg.input.joypad1.y];
joypad1.b = keystate[cfg.input.joypad1.b];
joypad1.x = keystate[cfg.input.joypad1.x];
joypad1.a = keystate[cfg.input.joypad1.a];
joypad1.l = keystate[cfg.input.joypad1.l];
joypad1.r = keystate[cfg.input.joypad1.r];
joypad1.up = keystate[(int)config::input.joypad1.up];
joypad1.down = keystate[(int)config::input.joypad1.down];
joypad1.left = keystate[(int)config::input.joypad1.left];
joypad1.right = keystate[(int)config::input.joypad1.right];
joypad1.select = keystate[(int)config::input.joypad1.select];
joypad1.start = keystate[(int)config::input.joypad1.start];
joypad1.y = keystate[(int)config::input.joypad1.y];
joypad1.b = keystate[(int)config::input.joypad1.b];
joypad1.x = keystate[(int)config::input.joypad1.x];
joypad1.a = keystate[(int)config::input.joypad1.a];
joypad1.l = keystate[(int)config::input.joypad1.l];
joypad1.r = keystate[(int)config::input.joypad1.r];
//It's impossible to hold both up+down, or left+right down
//at the same time on a directional pad; and besides, allowing

View File

@@ -14,13 +14,15 @@ bJoypad joypad1, joypad2;
public:
enum { STOP = 0, RUN };
void run();
void video_run();
void sound_run();
void set_status(uint32 new_status);
uint32 get_status();
void snes_run();
void render_frame();
//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);

View File

@@ -1,18 +1,86 @@
apu.enabled = true
# 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
video.display_width = 256
video.display_height = 223
# 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
input.joypad1.start = 0x0d
# Joypad1 start
# (default = 0xd)
input.joypad1.start = 0xd

View File

@@ -1,3 +1,2 @@
@nmake /NOLOGO /f Makefile.win32
@move bsnes_sdl.exe ../../bsnes_sdl.exe>nul
@pause

View File

@@ -1,47 +1,32 @@
#define __config_add(name, def, type) add(&name, #name, def, type)
namespace config {
class Config : public config {
public:
struct Video {
static Setting fullscreen;
static Setting display_width, display_height;
static Setting output_width, output_height;
} video;
Setting Video::fullscreen(&config_file, "video.fullscreen", "Enable fullscreen mode at startup", false, Setting::TRUE_FALSE);
Setting Video::display_width (&config_file, "video.display_width", "Window / Fullscreen width", 320, Setting::DEC);
Setting Video::display_height(&config_file, "video.display_height", "Window / Fullscreen height", 240, Setting::DEC);
Setting Video::output_width (&config_file, "video.output_width", "SNES video output width", 256, Setting::DEC);
Setting Video::output_height (&config_file, "video.output_height", "SNES video output height", 223, Setting::DEC);
struct {
uint32 enabled;
}apu;
struct Input {
struct Joypad {
static Setting up, down, left, right, a, b, x, y, l, r, select, start;
} joypad1;
} input;
Setting Input::Joypad::up (&config_file, "input.joypad1.up", "Joypad1 up", SDLK_UP, Setting::HEX);
Setting Input::Joypad::down (&config_file, "input.joypad1.down", "Joypad1 down", SDLK_DOWN, Setting::HEX);
Setting Input::Joypad::left (&config_file, "input.joypad1.left", "Joypad1 left", SDLK_LEFT, Setting::HEX);
Setting Input::Joypad::right (&config_file, "input.joypad1.right", "Joypad1 right", SDLK_RIGHT, Setting::HEX);
Setting Input::Joypad::a (&config_file, "input.joypad1.a", "Joypad1 A", SDLK_x, Setting::HEX);
Setting Input::Joypad::b (&config_file, "input.joypad1.b", "Joypad1 B", SDLK_z, Setting::HEX);
Setting Input::Joypad::x (&config_file, "input.joypad1.x", "Joypad1 X", SDLK_s, Setting::HEX);
Setting Input::Joypad::y (&config_file, "input.joypad1.y", "Joypad1 Y", SDLK_a, Setting::HEX);
Setting Input::Joypad::l (&config_file, "input.joypad1.l", "Joypad1 L", SDLK_d, Setting::HEX);
Setting Input::Joypad::r (&config_file, "input.joypad1.r", "Joypad1 R", SDLK_c, Setting::HEX);
Setting Input::Joypad::select(&config_file, "input.joypad1.select", "Joypad1 select", SDLK_RSHIFT, Setting::HEX);
Setting Input::Joypad::start (&config_file, "input.joypad1.start", "Joypad1 start", SDLK_RETURN, Setting::HEX);
struct {
uint32 fullscreen;
uint32 display_width, display_height;
uint32 output_width, output_height;
}video;
struct {
struct {
uint32 up, down, left, right;
uint32 a, b, x, y, l, r;
uint32 select, start;
}joypad1;
}input;
Config() {
__config_add(apu.enabled, true, TRUEFALSE);
__config_add(video.fullscreen, false, TRUEFALSE);
__config_add(video.display_width, 256, DEC);
__config_add(video.display_height, 223, DEC);
__config_add(video.output_width, 256, DEC);
__config_add(video.output_height, 223, DEC);
__config_add(input.joypad1.up, SDLK_UP, HEX);
__config_add(input.joypad1.down, SDLK_DOWN, HEX);
__config_add(input.joypad1.left, SDLK_LEFT, HEX);
__config_add(input.joypad1.right, SDLK_RIGHT, HEX);
__config_add(input.joypad1.a, SDLK_x, HEX);
__config_add(input.joypad1.b, SDLK_z, HEX);
__config_add(input.joypad1.x, SDLK_s, HEX);
__config_add(input.joypad1.y, SDLK_a, HEX);
__config_add(input.joypad1.l, SDLK_d, HEX);
__config_add(input.joypad1.r, SDLK_c, HEX);
__config_add(input.joypad1.select, SDLK_RSHIFT, HEX);
__config_add(input.joypad1.start, SDLK_RETURN, HEX);
}
}cfg;
};

View File

@@ -1,87 +1,49 @@
uint8 color_curve_table[32];
uint32 color_lookup_table[65536];
void update_color_lookup_table() {
int i, r, g, b, c;
for(i=0,c=0;i<16;i++) {
color_curve_table[i] = c;
c = c + i + 1;
}
for(;i<31;i++) {
color_curve_table[i] = c;
c += 8;
}
color_curve_table[i] = 0xff;
int color_depth = 16;
if(color_depth == 15) {
for(i=0;i<65536;i++) {
r = (i ) & 31;
g = (i >> 5) & 31;
b = (i >> 10) & 31;
r = color_curve_table[r] >> 3;
g = color_curve_table[g] >> 3;
b = color_curve_table[b] >> 3;
color_lookup_table[i] = (r << 10) | (g << 5) | (b);
}
} else if(color_depth == 16) {
for(i=0;i<65536;i++) {
r = (i ) & 31;
g = (i >> 5) & 31;
b = (i >> 10) & 31;
r = color_curve_table[r] >> 3;
g = color_curve_table[g] >> 2;
b = color_curve_table[b] >> 3;
color_lookup_table[i] = (r << 11) | (g << 5) | (b);
}
} else if(color_depth == 32) {
for(i=0;i<65536;i++) {
r = (i ) & 31;
g = (i >> 5) & 31;
b = (i >> 10) & 31;
r = color_curve_table[r];
g = color_curve_table[g];
b = color_curve_table[b];
color_lookup_table[i] = (r << 16) | (g << 8) | (b);
}
} else {
alert("Error: Unsupported color depth [%d]", color_depth);
}
}
void render16() {
uint16 *src, *dest;
uint16 *dest, *src;
uint32 pitch;
int x, y;
pitch = (backbuffer->pitch >> 1) - 256;
src = (uint16*)ppu->output->buffer + (1 << 10);
SNES::video_info vi;
snes->get_video_info(&vi);
pitch = (backbuffer->pitch >> 1);
dest = (uint16*)backbuffer->pixels;
for(y=0;y<223;y++) {
for(x=0;x<256;x++) {
*dest++ = color_lookup_table[*src];
src += 2;
src = (uint16*)vi.data;
if(vi.width == 256 && vi.height == 224) {
for(y=0;y<224;y++) {
memcpy(dest, src, 512);
dest += pitch;
src += 256;
}
} else if(vi.width == 512 && vi.height == 224) {
for(y=0;y<224;y++) {
memcpy(dest, src, 1024);
dest += pitch;
src += 512;
}
} else if(vi.width == 256 && vi.height == 448) {
for(y=0;y<448;y++) {
memcpy(dest, src, 512);
dest += pitch;
src += 256;
}
} else if(vi.width == 512 && vi.height == 448) {
for(y=0;y<448;y++) {
memcpy(dest, src, 1024);
dest += pitch;
src += 512;
}
dest += pitch;
src += 512;
}
screen_info.rs.x = 0;
screen_info.rs.y = (vi.height == 224) ? 1 : 2;
screen_info.rs.w = vi.width;
screen_info.rs.h = (vi.height == 224) ? 223 : 446;
}
void render32() {
uint16 *src;
uint32 *dest, pitch;
int x, y;
pitch = (screen->pitch >> 2) - 256;
src = (uint16*)ppu->output->buffer + (1 << 10);
dest = (uint32*)screen->pixels;
for(y=0;y<223;y++) {
for(x=0;x<256;x++) {
*dest++ = color_lookup_table[*src];
src += 2;
}
dest += pitch;
src += 512;
}
}
void render32() {}
void render() {
if(SDL_MUSTLOCK(screen)) {

View File

@@ -1,14 +1,13 @@
#define INTERFACE_MAIN
#define BSNES_VERSION "0.011"
#define BSNES_TITLE "bsnes/SDL v" BSNES_VERSION
#include "../base.h"
#include "sdlmain.h"
#include "config.cpp"
#ifdef _WIN32_
HWND hwnd;
#endif
#include "config.cpp"
#include "bsnes.h"
#include "rom.cpp"
#include "render.cpp"
@@ -47,24 +46,31 @@ va_list args;
void init_snes() {
mem_bus = new bMemBus();
cpu = new bCPU();
if(cfg.apu.enabled) {
apu = new bAPU();
} else {
apu = new bAPUSkip();
}
apu = new bAPU();
dsp = new bDSP();
ppu = new bPPU();
snes = new bSNES();
bsnes = static_cast<bSNES*>(snes);
snes->init();
//TODO: add sound support and remove this,
//this is used with linux/bsd and mkfifo to
//play audio in real-time while sound output
//isn't available.
snes->log_audio_enable("output.wav");
snes->set_playback_buffer_size(2000);
}
void term_snes() {
if(mem_bus) { delete(mem_bus); mem_bus = 0; }
if(cpu) { delete(cpu); cpu = 0; }
if(apu) { delete(apu); apu = 0; }
if(ppu) { delete(ppu); ppu = 0; }
if(snes) { delete(snes); snes = 0; }
snes->term();
if(mem_bus) { delete(static_cast<bMemBus*>(mem_bus)); mem_bus = 0; }
if(cpu) { delete(static_cast<bCPU*> (cpu)); cpu = 0; }
if(apu) { delete(static_cast<bAPU*> (apu)); apu = 0; }
if(ppu) { delete(static_cast<bPPU*> (ppu)); ppu = 0; }
if(snes) { delete(static_cast<bSNES*> (snes)); snes = 0; }
}
void center_window() {
@@ -82,22 +88,17 @@ void set_window_info() {
//SDL won't draw anything if you blit an image that's larger than
//the display mode/window, even if you use the SoftStretch blit and
//clip the source + dest rectangles properly...
if(cfg.video.display_width < cfg.video.output_width) {
cfg.video.display_width = cfg.video.output_width;
if((int)config::video.display_width < (int)config::video.output_width) {
config::video.display_width = config::video.output_width;
}
if(cfg.video.display_height < cfg.video.output_height) {
cfg.video.display_height = cfg.video.output_height;
if((int)config::video.display_height < (int)config::video.output_height) {
config::video.display_height = config::video.output_height;
}
screen_info.rd.x = (cfg.video.display_width - cfg.video.output_width ) >> 1;
screen_info.rd.y = (cfg.video.display_height - cfg.video.output_height) >> 1;
screen_info.rd.w = cfg.video.output_width;
screen_info.rd.h = cfg.video.output_height;
screen_info.rs.x = 0;
screen_info.rs.y = 0;
screen_info.rs.w = 256;
screen_info.rs.h = 223;
screen_info.rd.x = ((int)config::video.display_width - (int)config::video.output_width ) >> 1;
screen_info.rd.y = ((int)config::video.display_height - (int)config::video.output_height) >> 1;
screen_info.rd.w = config::video.output_width;
screen_info.rd.h = config::video.output_height;
}
#ifdef _WIN32_
@@ -115,14 +116,13 @@ SDL_Event event;
return 0;
}
cfg.load("bsnes_sdl.cfg");
config_file.load("bsnes_sdl.cfg");
rom_image = new ROMImage();
init_snes();
rom_image->select(argv[1]);
rom_image->load();
snes->power();
if(rom_image->loaded() == false) {
alert("Failed to load image. Usage: bsnes_sdl <filename.smc>");
@@ -132,13 +132,11 @@ SDL_Event event;
SDL_Init(SDL_INIT_VIDEO);
atexit(SDL_Quit);
set_window_info();
screen = SDL_SetVideoMode(cfg.video.display_width, cfg.video.display_height, 16,
SDL_SWSURFACE | ((cfg.video.fullscreen)?SDL_FULLSCREEN:0));
if(!screen) {
alert("Failed to initialize SDL");
goto _end;
}
backbuffer = SDL_CreateRGBSurface(SDL_SWSURFACE, 256, 223, 16, 0xf800, 0x07e0, 0x001f, 0x0000);
screen = SDL_SetVideoMode(config::video.display_width, config::video.display_height, 16,
SDL_SWSURFACE | ((config::video.fullscreen)?SDL_FULLSCREEN:0));
if(!screen) { alert("Failed to initialize SDL"); goto _end; }
backbuffer = SDL_CreateRGBSurface(SDL_SWSURFACE, 512, 448, 16, 0xf800, 0x07e0, 0x001f, 0x0000);
if(!backbuffer) { alert("Failed to initialize SDL"); goto _end; }
SDL_WM_SetCaption(BSNES_TITLE, 0);
#ifdef _WIN32_
@@ -146,18 +144,21 @@ SDL_Event event;
#endif
center_window();
update_color_lookup_table();
snes->power();
bsnes->set_status(bSNES::RUN);
int cursor_status;
while(1) {
bsnes->snes_run();
bsnes->run();
while(SDL_PollEvent(&event)) {
switch(event.type) {
case SDL_KEYUP:
switch(event.key.keysym.sym) {
case SDLK_ESCAPE:
goto _end;
case SDLK_BACKSPACE:
snes->capture_screenshot();
break;
case SDLK_F10: //toggle cursor display
cursor_status = (SDL_ShowCursor(SDL_QUERY) == SDL_ENABLE)?SDL_DISABLE:SDL_ENABLE;
SDL_ShowCursor(cursor_status);
@@ -174,7 +175,7 @@ int cursor_status;
}
_end:
cfg.save("bsnes_sdl.cfg");
config_file.save("bsnes_sdl.cfg");
term_snes();
return 0;

View File

@@ -13,5 +13,5 @@
SDL_Surface *screen, *backbuffer;
struct {
SDL_Rect rs, rd;
}screen_info;
SDL_Rect rs, rd;
} screen_info;

View File

@@ -1 +1 @@
c:\root\bsnes_g2\bsnes_sdl.exe c:\root\bsnes_testrom\zelda_us.smc
bsnes_sdl c:\root\bsnes_testrom\zelda_us.smc

View File

@@ -1,24 +1,48 @@
#include "../base.h"
#include "snes_video.cpp"
#include "snes_audio.cpp"
#include "snes_input.cpp"
void SNES::run() {
uint32 cycles;
uint32 cycles, r;
if(apusync.cycles < 0) {
cpu->run();
apusync.cycles += apusync.apu_multbl[cpu->cycles_executed()];
} else {
apu->run();
apusync.cycles -= apusync.cpu_multbl[apu->cycles_executed()];
cycles = apu->cycles_executed();
apusync.dsp += cycles;
apusync.cycles -= apusync.cpu_multbl[cycles];
//1024000(SPC700) / 32000(DSP) = 32spc/dsp ticks
//24576000(Sound clock crystal) / 32000(DSP) = 768crystal/dsp ticks
while(apusync.dsp >= 768) {
apusync.dsp -= 768;
audio_update(dsp->run());
}
}
}
void SNES::init() {
srtc = new SRTC();
sdd1 = new SDD1();
srtc->init();
sdd1->init();
video_init();
audio_init();
}
void SNES::term() {
audio_term();
}
void SNES::power() {
cpu->power();
apu->power();
dsp->power();
ppu->power();
mem_bus->power();
@@ -30,16 +54,18 @@ int i;
for(i=0x2100;i<=0x213f;i++)mem_bus->set_mmio_mapper(i, ppu->mmio);
for(i=0x2140;i<=0x217f;i++)mem_bus->set_mmio_mapper(i, cpu->mmio);
for(i=0x2180;i<=0x2183;i++)mem_bus->set_mmio_mapper(i, cpu->mmio);
//S-RTC
mem_bus->set_mmio_mapper(0x2800, srtc->mmio);
mem_bus->set_mmio_mapper(0x2801, srtc->mmio);
//input
mem_bus->set_mmio_mapper(0x4016, cpu->mmio);
mem_bus->set_mmio_mapper(0x4017, cpu->mmio);
for(i=0x4200;i<=0x421f;i++)mem_bus->set_mmio_mapper(i, cpu->mmio);
for(i=0x4300;i<=0x437f;i++)mem_bus->set_mmio_mapper(i, cpu->mmio);
//S-DD1
for(i=0x4800;i<=0x4807;i++)mem_bus->set_mmio_mapper(i, sdd1->mmio);
srtc->enable();
sdd1->enable();
memset(video.data, 0, 512 * 448 * sizeof(uint32));
memset(video.ppu_data, 0, 512 * 480 * sizeof(uint16));
video_update();
}
void SNES::reset() {
@@ -47,13 +73,30 @@ void SNES::reset() {
cpu->reset();
apu->reset();
dsp->reset();
ppu->reset();
mem_bus->reset();
srtc->reset();
sdd1->reset();
memset(video.data, 0, 512 * 448 * sizeof(uint32));
memset(video.ppu_data, 0, 512 * 480 * sizeof(uint16));
video_update();
}
void SNES::frame() {
video_update();
}
void SNES::scanline() {
video_scanline();
}
/****************
*** PAL/NTSC ***
****************/
void SNES::set_region(uint8 new_region) {
if(new_region == NTSC) {
snes_region = NTSC;
@@ -68,6 +111,10 @@ void SNES::set_region(uint8 new_region) {
uint8 SNES::region() { return snes_region; }
/**************
*** Timing ***
**************/
void SNES::update_timing() {
apusync.cycles = 0;
if(snes_region == NTSC) {
@@ -87,6 +134,7 @@ int i;
/***************************
*** Debugging functions ***
***************************/
void SNES::notify(uint32 message, uint32 param1, uint32 param2) {}
void SNES::debugger_enable() {
@@ -102,8 +150,10 @@ bool SNES::debugger_enabled() {
}
SNES::SNES() {
is_debugger_enabled = true;
is_debugger_enabled = false;
snes_region = NTSC;
update_timing();
dsp_buffer.data = 0;
}

View File

@@ -6,10 +6,12 @@ uint8 snes_region;
//APU synchronization
struct {
int32 cpu_freq, apu_freq;
int32 cpu_multbl[1024], apu_multbl[1024];
int32 cycles;
}apusync;
int32 cpu_freq, apu_freq;
int32 cpu_multbl[1024], apu_multbl[1024];
int32 cycles;
int32 dsp;
} apusync;
void update_timing();
@@ -17,29 +19,22 @@ public:
enum { NTSC = 0, PAL = 1 };
//system functions
void run();
virtual void render_frame() = 0;
virtual void init();
virtual void power();
virtual void reset();
virtual uint8 region();
virtual void set_region(uint8 new_region);
virtual void run();
virtual void init();
virtual void term();
virtual void power();
virtual void reset();
//input functions
enum {
DEV_JOYPAD1 = 0,
DEV_JOYPAD2 = 1
};
enum {
JOYPAD_B = 0, JOYPAD_Y = 1,
JOYPAD_SELECT = 2, JOYPAD_START = 3,
JOYPAD_UP = 4, JOYPAD_DOWN = 5,
JOYPAD_LEFT = 6, JOYPAD_RIGHT = 7,
JOYPAD_A = 8, JOYPAD_X = 9,
JOYPAD_L = 10, JOYPAD_R = 11
};
virtual void poll_input() = 0;
virtual bool get_input_status(uint8 device, uint8 button) = 0;
virtual void frame();
virtual void scanline();
//PAL/NTSC
uint8 region();
void set_region(uint8 new_region);
#include "snes_video.h"
#include "snes_audio.h"
#include "snes_input.h"
//debugging functions
enum {
@@ -53,10 +48,12 @@ enum {
OAM_READ, OAM_WRITE,
CGRAM_READ, CGRAM_WRITE,
};
virtual void notify(uint32 message, uint32 param1 = 0, uint32 param2 = 0);
virtual void debugger_enable();
virtual void debugger_disable();
virtual bool debugger_enabled();
//message functions
virtual void notify(uint32 message, uint32 param1 = 0, uint32 param2 = 0);
SNES();
};

145
src/snes/snes_audio.cpp Normal file
View File

@@ -0,0 +1,145 @@
void SNES::set_playback_buffer_size(uint32 buffer_size) {
if(dsp_buffer.data) {
free(dsp_buffer.data);
dsp_buffer.data = 0;
}
//* 2 is for left/right channel data
dsp_buffer.data = (uint16*)malloc(buffer_size * sizeof(uint16) * 2);
memset(dsp_buffer.data, 0, buffer_size * sizeof(uint16) * 2);
dsp_buffer.size = buffer_size;
dsp_buffer.pos = 0;
}
uint32 SNES::get_playback_buffer_pos() {
return dsp_buffer.pos;
}
uint16 *SNES::get_playback_buffer() {
return dsp_buffer.data;
}
void SNES::audio_update(uint32 data) {
if(pcmfp) {
fputc(data, pcmfp);
fputc(data >> 8, pcmfp);
fputc(data >> 16, pcmfp);
fputc(data >> 24, pcmfp);
}
if((bool)config::snes.mute == true)data = 0x0000;
dsp_buffer.data[dsp_buffer.pos++] = (data) & 0xffff;
dsp_buffer.data[dsp_buffer.pos++] = (data >> 16) & 0xffff;
dsp_buffer.pos %= dsp_buffer.size;
sound_run();
}
void SNES::log_audio_enable(const char *fn) {
char tfn[256];
int i;
if(pcmfp)log_audio_disable();
if(!fn) {
for(i=0;i<=999;i++) {
sprintf(tfn, "audio%0.3d.wav", i);
pcmfp = fopen(tfn, "rb");
if(!pcmfp)break;
fclose(pcmfp);
pcmfp = 0;
}
if(i >= 1000)return;
} else {
strcpy(tfn, fn);
}
pcmfp = fopen(tfn, "wb");
if(!pcmfp)return;
//header
fwrite("RIFF", 1, 4, pcmfp);
//file size
fputc(0, pcmfp);
fputc(0, pcmfp);
fputc(0, pcmfp);
fputc(0, pcmfp);
//format
fwrite("WAVE", 1, 4, pcmfp);
fwrite("fmt ", 1, 4, pcmfp);
//fmt size
fputc(0x12, pcmfp);
fputc(0x00, pcmfp);
fputc(0x00, pcmfp);
fputc(0x00, pcmfp);
//fmt type (PCM)
fputc(1, pcmfp);
fputc(0, pcmfp);
//channels
fputc(2, pcmfp);
fputc(0, pcmfp);
//sample rate (32000hz)
fputc(0x00, pcmfp);
fputc(0x7d, pcmfp);
fputc(0x00, pcmfp);
fputc(0x00, pcmfp);
//byte rate (32000 * 2 * (16 / 8)
fputc(0x00, pcmfp);
fputc(0xf4, pcmfp);
fputc(0x01, pcmfp);
fputc(0x00, pcmfp);
//block align (bytes per sample) (4)
fputc(4, pcmfp);
fputc(0, pcmfp);
//???
fputc(0x10, pcmfp);
fputc(0x00, pcmfp);
fputc(0x00, pcmfp);
fputc(0x00, pcmfp);
fwrite("fact", 1, 4, pcmfp);
fputc(0x04, pcmfp);
fputc(0x00, pcmfp);
fputc(0x00, pcmfp);
fputc(0x00, pcmfp);
fputc(0x0b, pcmfp);
fputc(0xf4, pcmfp);
fputc(0x01, pcmfp);
fputc(0x00, pcmfp);
//data
fwrite("data", 1, 4, pcmfp);
//data size
fputc(0, pcmfp);
fputc(0, pcmfp);
fputc(0, pcmfp);
fputc(0, pcmfp);
}
void SNES::log_audio_disable() {
if(pcmfp) {
int fsize, t;
fseek(pcmfp, 0, SEEK_END);
fsize = ftell(pcmfp);
fseek(pcmfp, 4, SEEK_SET);
t = fsize - 2;
fputc(t, pcmfp);
fputc(t >> 8, pcmfp);
fputc(t >> 16, pcmfp);
fputc(t >> 24, pcmfp);
fseek(pcmfp, 0x36, SEEK_SET);
t = fsize - 0x3a;
fputc(t, pcmfp);
fputc(t >> 8, pcmfp);
fputc(t >> 16, pcmfp);
fputc(t >> 24, pcmfp);
fclose(pcmfp);
pcmfp = 0;
}
}
void SNES::audio_init() {
}
void SNES::audio_term() {
log_audio_disable();
}

22
src/snes/snes_audio.h Normal file
View File

@@ -0,0 +1,22 @@
struct {
uint16 *data;
uint32 size, pos;
} dsp_buffer;
FILE *pcmfp;
//buffer_size is in samples
void set_playback_buffer_size(uint32 buffer_size);
uint32 get_playback_buffer_pos();
uint16 *get_playback_buffer();
//if a filename is not specified, one will be generated
//automatically ("audio%0.3d.wav")
void log_audio_enable(const char *fn = 0);
void log_audio_disable();
void audio_update(uint32 data);
void audio_init();
void audio_term();
virtual void sound_run() = 0;

0
src/snes/snes_input.cpp Normal file
View File

21
src/snes/snes_input.h Normal file
View File

@@ -0,0 +1,21 @@
enum {
DEV_JOYPAD1 = 0,
DEV_JOYPAD2 = 1
};
enum {
JOYPAD_B = 0, JOYPAD_Y = 1,
JOYPAD_SELECT = 2, JOYPAD_START = 3,
JOYPAD_UP = 4, JOYPAD_DOWN = 5,
JOYPAD_LEFT = 6, JOYPAD_RIGHT = 7,
JOYPAD_A = 8, JOYPAD_X = 9,
JOYPAD_L = 10, JOYPAD_R = 11
};
//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(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.
virtual bool get_input_status(uint8 device, uint8 button) = 0;

343
src/snes/snes_video.cpp Normal file
View File

@@ -0,0 +1,343 @@
#include "snes_video_ex.cpp"
const uint8 SNES::color_curve_table[32] = {
0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c,
0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78,
0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0,
0xc8, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0xff
};
void SNES::update_color_lookup_table() {
int i, l, r, g, b;
double lr = 0.2126, lg = 0.7152, lb = 0.0722; //luminance
uint32 col;
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);
r = (col >> 16) & 0xff;
g = (col >> 8) & 0xff;
b = (col ) & 0xff;
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];
}
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:
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;
}
}
}
void SNES::set_video_format(uint8 mode, uint8 depth) {
//only make changes at the start of a new frame
video.format_changed = true;
video_changed.mode = mode;
video_changed.depth = depth;
}
//internal function called at the start of the frame
//after SNES::set_video_format() is called
void SNES::update_video_format() {
video.mode = video_changed.mode;
video.depth = video_changed.depth;
update_color_lookup_table();
video.format_changed = false;
}
void SNES::get_video_info(video_info *info) {
info->data = video.data;
info->mode = video.mode;
switch(video.mode) {
case VM_256x224:
info->width = 256;
info->height = 224;
break;
case VM_512x224:
info->width = 512;
info->height = 224;
break;
case VM_256x448:
info->width = 256;
info->height = 448;
break;
case VM_512x448:
info->width = 512;
info->height = 448;
break;
case VM_VARIABLE:
if(video.frame_hires == false) {
info->width = 256;
} else {
info->width = 512;
}
if(video.frame_interlace == false) {
info->height = 224;
} else {
info->height = 448;
}
break;
}
}
void SNES::video_update_256x224(uint16 *src) {
int x, y;
uint16 *dest;
dest = video.data;
for(y=0;y<224;y++) {
if(video_frame[y].hires == false) {
for(x=0;x<256;x++) {
*dest++ = color_lookup_table[*src++];
}
src += 768;
} else {
for(x=0;x<256;x++) {
*dest++ = color_lookup_table[*src];
src += 2;
}
src += 512;
}
}
}
void SNES::video_update_512x224(uint16 *src) {
int x, y;
uint16 *dest;
dest = video.data;
for(y=0;y<224;y++) {
if(video_frame[y].hires == false) {
for(x=0;x<256;x++) {
*dest++ = color_lookup_table[*src];
*dest++ = color_lookup_table[*src++];
}
src += 768;
} else {
for(x=0;x<512;x++) {
*dest++ = color_lookup_table[*src++];
}
src += 512;
}
}
}
void SNES::video_update_256x448(uint16 *src) {
int x, y;
uint16 *dest;
bool field = !cpu->interlace_field();
dest = video.data;
for(y=0;y<224;y++) {
if(video_frame[y].interlace == false) {
if(video_frame[y].hires == false) {
for(x=0;x<512;x++) {
*dest++ = color_lookup_table[*(src + (uint8)x)];
}
src += 1024;
} else {
for(x=0;x<512;x++) {
*dest++ = color_lookup_table[*(src + ((x & 255) << 1))];
}
src += 1024;
}
} else {
if(field) {
dest += 256;
src += 512;
}
if(video_frame[y].hires == false) {
for(x=0;x<256;x++) {
*dest++ = color_lookup_table[*src++];
}
src += 256;
} else {
for(x=0;x<256;x++) {
*dest++ = color_lookup_table[*src];
src += 2;
}
}
if(!field) {
dest += 256;
src += 512;
}
}
}
}
void SNES::video_update_512x448(uint16 *src) {
int x, y;
uint16 *dest;
bool field = !cpu->interlace_field();
dest = video.data;
for(y=0;y<224;y++) {
if(video_frame[y].interlace == false) {
if(video_frame[y].hires == false) {
for(x=0;x<512;x++) {
*dest++ = color_lookup_table[*(src + (uint8)x)];
*dest++ = color_lookup_table[*(src + (uint8)x)];
}
src += 1024;
} else {
for(x=0;x<1024;x++) {
*dest++ = color_lookup_table[*(src + (x & 511))];
}
src += 1024;
}
} else {
if(field) {
dest += 512;
src += 512;
}
if(video_frame[y].hires == false) {
for(x=0;x<256;x++) {
*dest++ = color_lookup_table[*(src + x)];
*dest++ = color_lookup_table[*(src + x)];
}
src += 512;
} else {
for(x=0;x<512;x++) {
*dest++ = color_lookup_table[*src++];
}
}
if(!field) {
dest += 512;
src += 512;
}
}
}
}
void SNES::video_update() {
if(ppu->renderer_enabled()) {
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;
}
break;
}
//SNES::capture_screenshot() was called by emulation interface
if(flag_output_screenshot == true) {
output_screenshot();
flag_output_screenshot = false;
}
}
video_run();
video.frame_hires = false;
video.frame_interlace = false;
}
void SNES::video_scanline() {
int y = cpu->vcounter();
int o = int(cpu->overscan()) << 3;
if(y <= (0 + o) || y >= (224 + o))return;
y -= o;
PPU::scanline_info si;
ppu->get_scanline_info(&si);
video_frame[y].hires = si.hires;
video_frame[y].interlace = si.interlace;
video.frame_hires |= si.hires;
video.frame_interlace |= si.interlace;
}
uint16 *SNES::get_ppu_output_handle() {
return (uint16*)(video.ppu_data +
(cpu->vcounter() * 1024) +
((cpu->interlace() && cpu->interlace_field())?512:0));
}
void SNES::video_init() {
int i, c;
video.format_changed = false;
video.data = (uint16*)malloc(512 * 448 * sizeof(uint32));
video.ppu_data = (uint16*)malloc(512 * 480 * sizeof(uint16));
memset(video.data, 0, 512 * 448 * sizeof(uint32));
memset(video.ppu_data, 0, 512 * 480 * sizeof(uint16));
for(i=0;i<224;i++) {
video_frame[i].hires = false;
video_frame[i].interlace = false;
}
video.frame_hires = false;
video.frame_interlace = false;
set_video_format(VM_VARIABLE, 16);
update_video_format();
flag_output_screenshot = false;
}

72
src/snes/snes_video.h Normal file
View File

@@ -0,0 +1,72 @@
enum {
VM_256x224, //video data will be scaled to 256x224
VM_512x224, //..
VM_256x448, //..
VM_512x448, //..
VM_VARIABLE //video data can be 256x224 - 512x448
};
enum {
VMF_NORMAL = 0,
VMF_HIRES = 1,
VMF_INTERLACE = 2,
VMF_HINTERLACE = 3
};
//video color adjustment
//via config::snes.video_color_adjust_mode
enum {
VCA_NORMAL = 0,
VCA_GRAYSCALE = 1,
VCA_VGA = 2,
VCA_GENESIS = 3
};
static const uint8 color_curve_table[32];
uint32 color_lookup_table[32768];
struct {
uint16 *data, *ppu_data;
uint8 mode;
uint8 depth;
bool frame_hires, frame_interlace;
bool format_changed;
} video, video_changed;
struct {
bool hires, interlace;
} video_frame[224];
struct video_info {
uint16 *data;
uint8 mode;
uint32 width, height;
};
//public functions
void capture_screenshot();
void update_color_lookup_table();
virtual void set_video_format(uint8 mode, uint8 depth);
virtual void get_video_info(video_info *info);
virtual void video_run() = 0;
//private functions
uint16 *get_ppu_output_handle(); //used by PPU only
private:
//when a screenshot is requested, wait until the frame
//has finished rendering, so we can tell the image size
bool flag_output_screenshot;
uint16 to_rgb555(uint32 color);
void output_screenshot();
void update_video_format();
void video_update_256x224(uint16 *src);
void video_update_512x224(uint16 *src);
void video_update_256x448(uint16 *src);
void video_update_512x448(uint16 *src);
void video_update();
void video_scanline();
void video_init();
public:

106
src/snes/snes_video_ex.cpp Normal file
View File

@@ -0,0 +1,106 @@
void SNES::capture_screenshot() {
flag_output_screenshot = true;
}
//used to convert pixel data to write to rgb555 format
//bitmap image via SNES::output_screenshot() function
uint16 SNES::to_rgb555(uint32 color) {
if(video.depth == 15) {
//rgb555
return color & 0x7fff;
}
if(video.depth == 16) {
//rgb565->rgb555
return ((color >> 1) & 0x7fe0) | (color & 0x001f);
}
if(video.depth == 24 || video.depth == 32) {
//rgb888->rgb555
return ((color >> 9) & 0x7c00) | ((color >> 6) & 0x03e0) | ((color >> 3) & 0x001f);
}
//unsupported color depth
return color;
}
void SNES::output_screenshot() {
FILE *fp;
char fn[256];
int i;
//get a file name that doesn't exit...
for(i=0;i<=999;i++) {
sprintf(fn, "image%0.3d.bmp", i);
fp = fopen(fn, "rb");
if(!fp)break;
fclose(fp);
fp = 0;
}
if(i >= 1000)return;
fp = fopen(fn, "wb");
if(!fp)return;
video_info vi;
get_video_info(&vi);
int width, height;
width = vi.width;
height = (vi.height == 224) ? 223 : 446;
int32 fsize;
fsize = 0x36 + (width * height * sizeof(uint16));
//header
fputc('B', fp);
fputc('M', fp);
//file size
fputc(fsize, fp);
fputc(fsize >> 8, fp);
fputc(fsize >> 16, fp);
fputc(fsize >> 24, fp);
//???
fputc(0, fp);
fputc(0, fp);
fputc(0, fp);
fputc(0, fp);
//start of data
fputc(0x36, fp);
fputc(0x00, fp);
fputc(0x00, fp);
fputc(0x00, fp);
//remaining header size
fputc(0x28, fp);
fputc(0x00, fp);
fputc(0x00, fp);
fputc(0x00, fp);
//width
fputc(width, fp);
fputc(width >> 8, fp);
fputc(width >> 16, fp);
fputc(width >> 24, fp);
//height
fputc(height, fp);
fputc(height >> 8, fp);
fputc(height >> 16, fp);
fputc(height >> 24, fp);
//format
fputc(0x01, fp);
fputc(0x00, fp);
//bpp
fputc(16, fp);
fputc( 0, fp);
//???
for(i=0;i<24;i++) { fputc(0, fp); }
int x, y;
uint16 c;
for(y=height;y>=1;y--) {
for(x=0;x<width;x++) {
c = to_rgb555(video.data[y * width + x]);
fputc(c, fp);
fputc(c >> 8, fp);
}
}
fclose(fp);
fp = 0;
}

View File

@@ -1,15 +1,16 @@
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 \
memory.obj bmemory.obj \
cpu.obj bcpu.obj \
apu.obj bapu.obj bapuskip.obj \
bdsp.obj \
ppu.obj bppu.obj \
snes.obj \
srtc.obj sdd1.obj
LIBS = kernel32.lib user32.lib gdi32.lib comdlg32.lib ddraw.lib dxguid.lib
LIBS = kernel32.lib user32.lib gdi32.lib comdlg32.lib ddraw.lib dsound.lib dxguid.lib
all: $(OBJS)
rc /r /fobsnes.res bsnes.rc
@@ -21,7 +22,7 @@ clean:
######################
### win32-specific ###
######################
winmain.obj: *.cpp *.h
winmain.obj: *.cpp *.h ../config/*
$(CC) $(CFLAGS) /c winmain.cpp
#################
@@ -62,6 +63,12 @@ bapu.obj: ../apu/bapu/*
bapuskip.obj: ../apu/bapuskip/*
$(CC) $(CFLAGS) /c ../apu/bapuskip/bapuskip.cpp
###########
### dsp ###
###########
bdsp.obj: ../dsp/bdsp/*
$(CC) $(CFLAGS) /c ../dsp/bdsp/bdsp.cpp
###########
### ppu ###
###########

View File

@@ -43,31 +43,31 @@ uint32 bSNES::get_status() {
return run_status;
}
void bSNES::snes_run() {
void bSNES::run() {
if(!rom_image->loaded())return;
switch(run_status) {
case RUN:
while(update_frame == false) {
run();
SNES::run();
}
update_frame = false;
return;
case STOP:
break;
case RUNONCE:
run();
SNES::run();
set_status(STOP);
break;
case RUNTOSIGNAL:
run();
SNES::run();
if(w_bp->hit() == true) {
set_status(STOP);
disassemble_bp_op();
}
break;
case RUNTOFRAME:
run();
SNES::run();
if(update_frame == true) {
update_frame = false;
set_status(STOP);
@@ -79,7 +79,7 @@ void bSNES::snes_run() {
}
return;
case RUNTOCPUSTEP:
run();
SNES::run();
if(status.cpu_ran == true) {
set_status(STOP);
} else if(w_bp->hit() == true) {
@@ -88,7 +88,7 @@ void bSNES::snes_run() {
}
break;
case RUNTOCPUPROCEED:
run();
SNES::run();
if(cpu->in_opcode() == false && status.cpu_stop_pos == cpu->regs.pc.d) {
set_status(STOP);
disassemble_cpu_op();
@@ -98,14 +98,14 @@ void bSNES::snes_run() {
}
break;
case RUNTOCPUTRACE:
run();
SNES::run();
if(status.cpu_trace_pos >= status.cpu_trace_stop) {
set_status(STOP);
disassemble_cpu_op();
}
break;
case RUNTOAPUSTEP:
run();
SNES::run();
if(status.apu_ran == true || w_bp->hit() == true) {
set_status(STOP);
}
@@ -113,37 +113,85 @@ void bSNES::snes_run() {
}
}
void bSNES::render_frame() {
dd_renderer->update();
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);
}
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() {
ds_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(cfg.input.joypad1.up);
joypad1.down = KeyDown(cfg.input.joypad1.down);
joypad1.left = KeyDown(cfg.input.joypad1.left);
joypad1.right = KeyDown(cfg.input.joypad1.right);
joypad1.select = KeyDown(cfg.input.joypad1.select);
joypad1.start = KeyDown(cfg.input.joypad1.start);
joypad1.y = KeyDown(cfg.input.joypad1.y);
joypad1.b = KeyDown(cfg.input.joypad1.b);
joypad1.x = KeyDown(cfg.input.joypad1.x);
joypad1.a = KeyDown(cfg.input.joypad1.a);
joypad1.l = KeyDown(cfg.input.joypad1.l);
joypad1.r = KeyDown(cfg.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;
@@ -163,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) {
@@ -183,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;
}
@@ -272,7 +340,6 @@ void bSNES::notify(uint32 message, uint32 param1, uint32 param2) {
switch(message) {
case RENDER_FRAME:
update_frame = true;
render_frame();
break;
}

View File

@@ -35,13 +35,15 @@ enum {
RUNTOAPUSTEP
};
enum { DRAM = 0, SPCRAM = 1, VRAM = 2, OAM = 3, CGRAM = 4 };
void run();
void video_run();
void sound_run();
void set_status(uint32 new_status);
uint32 get_status();
void snes_run();
void render_frame();
//input functions
void poll_input();
void poll_input(uint8 type);
bool get_input_status(uint8 device, uint8 button);
//debugging functions

Binary file not shown.

View File

@@ -1,52 +1,55 @@
#define __config_add(name, def, type) add(&name, #name, def, type)
namespace config {
class Config : public config {
public:
struct Video {
static Setting mode, use_vram, vblank;
} video;
Setting Video::mode(&config_file, "video.mode",
"Video mode\n"
" 0 = 256x224w\n"
" 1 = 512x448w\n"
" 2 = 960x720w\n"
" 3 = 640x480f\n"
" 4 = 1024x768f",
1, Setting::DEC);
Setting Video::use_vram(&config_file, "video.use_vram", "Use Video RAM instead of System RAM", true, Setting::TRUE_FALSE);
Setting Video::vblank(&config_file, "video.vblank", "Wait for vertical retrace when updating screen", false, Setting::TRUE_FALSE);
struct {
uint32 enabled;
}apu;
struct GUI {
static Setting show_fps;
} gui;
Setting GUI::show_fps(&config_file, "gui.show_fps", "Show framerate in window title", true, Setting::TRUE_FALSE);
struct {
uint32 mode;
uint32 use_vram;
uint32 color_curve;
uint32 vblank;
}video;
struct Input {
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::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);
struct {
struct {
uint32 up, down, left, right;
uint32 a, b, x, y, l, r;
uint32 select, start;
}joypad1;
}input;
struct {
uint32 show_fps;
}gui;
Config() {
__config_add(apu.enabled, true, TRUEFALSE);
__config_add(video.mode, 1, DEC);
__config_add(video.use_vram, true, TRUEFALSE);
__config_add(video.color_curve, true, ENABLED);
__config_add(video.vblank, false, TRUEFALSE);
__config_add(input.joypad1.up, VK_UP, HEX);
__config_add(input.joypad1.down, VK_DOWN, HEX);
__config_add(input.joypad1.left, VK_LEFT, HEX);
__config_add(input.joypad1.right, VK_RIGHT, HEX);
__config_add(input.joypad1.a, 'X', HEX);
__config_add(input.joypad1.b, 'Z', HEX);
__config_add(input.joypad1.x, 'S', HEX);
__config_add(input.joypad1.y, 'A', HEX);
__config_add(input.joypad1.l, 'D', HEX);
__config_add(input.joypad1.r, 'C', HEX);
__config_add(input.joypad1.select, VK_SHIFT, HEX);
__config_add(input.joypad1.start, VK_RETURN, HEX);
__config_add(gui.show_fps, true, TRUEFALSE);
}
}cfg;
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);
};

Some files were not shown because too many files have changed in this diff Show More