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).
This commit is contained in:
byuu 2005-10-02 00:38:34 +00:00
parent 7e2cfb6d40
commit 397b9c4505
107 changed files with 4295 additions and 1709 deletions

106
bsnes.cfg
View File

@ -1,57 +1,87 @@
#[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 = false
# 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

BIN
bsnes.exe

Binary file not shown.

View File

@ -17,5 +17,5 @@ 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

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

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

Binary file not shown.

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,8 @@
#include <time.h>
#include "lib/libbase.h"
#include "lib/libvector.h"
#include "lib/libstring.h"
#include "lib/libconfig.h"
#if defined(_WIN32)
#define _WIN32_
@ -11,12 +14,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,152 +13,26 @@
#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:
case CPUSTATE_RUN:
if(status.cycle_pos == 0) {
//interrupts only trigger on opcode edges
if(nmi_test() == true) {
if(time.nmi_pending == true) {
time.nmi_pending = false;
irq(0xffea);
break;
}
if(irq_test() == true) {
if(time.irq_pending == true) {
time.irq_pending = false;
irq(0xffee);
break;
}
@ -184,19 +58,14 @@ void bCPU::scanline() {
//connected status bit.
status.joypad1_read_pos = 16;
}
if(status.virq_enabled == false) {
status.irq_read = false;
}
}
void bCPU::frame() {
hdma_initialize();
status.nmi_read = false;
status.nmi_exec = false;
status.irq_read = false;
time.nmi_read = 1;
time.nmi_line = 1;
time.nmi_transition = 0;
}
void bCPU::power() {
@ -234,12 +103,6 @@ void bCPU::reset() {
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;
@ -258,43 +121,52 @@ 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.1 FastROM enable bit.
*/
void bCPU::cpu_io() {
if(status.is_last_cycle)last_cycle_exec();
status.cycle_count = 6;
add_cycles(6);
}
uint8 bCPU::mem_read(uint32 addr) {
if(status.is_last_cycle)last_cycle_exec();
status.cycle_count = mem_bus->speed(addr);
add_cycles(2);
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) {
if(status.is_last_cycle)last_cycle_exec();
status.cycle_count = mem_bus->speed(addr);
add_cycles(6);
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 +178,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;
@ -53,20 +52,13 @@ struct {
uint8 opcode;
uint32 cycles_executed;
//set by last_cycle(), cleared by last_cycle_exec()
bool is_last_cycle;
uint8 dma_state;
uint32 dma_cycle_count;
bool hdma_triggered;
//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;
@ -98,7 +90,7 @@ struct {
//$4214-$4216
uint16 r4214;
uint16 r4216;
}status;
} status;
struct {
uint32 read_index; //set to 0 at beginning of DMA/HDMA
@ -136,12 +128,13 @@ struct {
bool hdma_repeat;
uint16 hdma_iaddr;
bool hdma_active;
}channel[8];
} channel[8];
inline bool hdma_test();
inline void irq(uint16 addr);
inline bool nmi_test();
inline bool irq_test();
inline void irq(uint16 addr);
inline uint8 pio_status();
inline void run();
@ -225,6 +218,8 @@ struct {
enum { CYCLE_OPREAD = 0, CYCLE_READ, CYCLE_WRITE, CYCLE_IO };
inline void exec_cycle();
inline void last_cycle();
inline void last_cycle_exec();
inline void cycle_edge();
inline bool in_opcode();

View File

@ -113,11 +113,11 @@ uint16 index;
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;
// add_cycles(8);
add_cycles(8);
active_channels++;
if(channel[i].hdma_line_counter == 0) {
@ -140,7 +140,7 @@ static hdma_xferlen[8] = { 1, 2, 2, 4, 4, 4, 2, 4 };
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);
add_cycles(16);
}
}
@ -157,12 +157,12 @@ static hdma_xferlen[8] = { 1, 2, 2, 4, 4, 4, 2, 4 };
}
hdma_write(i, l, x);
// add_cycles(8);
add_cycles(8);
}
}
if(active_channels != 0) {
// add_cycles(18);
add_cycles(18);
}
}

View File

@ -1,4 +1,15 @@
inline void bCPU::cycle_edge() {
void bCPU::last_cycle() {
status.is_last_cycle = true;
}
void bCPU::last_cycle_exec() {
status.is_last_cycle = false;
time.nmi_pending = nmi_test();
time.irq_pending = irq_test();
}
void bCPU::cycle_edge() {
int c, n, z;
if(status.dma_state != DMASTATE_STOP) {
switch(status.dma_state) {
@ -36,15 +47,17 @@ int c, n, z;
}
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);
}
}

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

@ -0,0 +1,70 @@
/*
[IRQ cycles]
[1] pbr,pc ; opcode
[2] pbr,pc ; io
[3] 0,s ; pbr
[4] 0,s-1 ; pch
[5] 0,s-2 ; pcl
[6] 0,s-3 ; p
[7] 0,va ; aavl
[8] 0,va+1 ; aavh
*/
void bCPU::irq(uint16 addr) {
//WDC documentation is incorrect, first cycle
//is a memory read fetch from PBR:PC
add_cycles(mem_bus->speed(regs.pc.d));
add_cycles(6);
stack_write(regs.pc.b);
stack_write(regs.pc.h);
stack_write(regs.pc.l);
stack_write(regs.p);
rd.l = op_read(OPMODE_ADDR, addr);
rd.h = op_read(OPMODE_ADDR, addr + 1);
regs.pc.b = 0x00;
regs.pc.w = rd.w;
regs.p.i = 1;
regs.p.d = 0;
//let debugger know the new IRQ opcode address
snes->notify(SNES::CPU_EXEC_OPCODE_END);
}
bool bCPU::nmi_test() {
if(time.nmi_transition == 0)return false;
time.nmi_transition = 0;
if(status.cpu_state == CPUSTATE_WAI) {
status.cpu_state = CPUSTATE_RUN;
}
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;
if(status.cpu_state == CPUSTATE_WAI) {
status.cpu_state = CPUSTATE_RUN;
}
if(regs.p.i)return false;
return true;
}

View File

@ -42,18 +42,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;
@ -96,44 +95,19 @@ uint8 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 +116,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 +134,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;
}
@ -291,9 +267,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);
@ -337,6 +311,8 @@ uint8 i;
case 0x4218:return cpu->mmio_r4218(); //JOY1L
case 0x4219:return cpu->mmio_r4219(); //JOY1H
case 0x421a:case 0x421b:case 0x421c:case 0x421d:case 0x421e:case 0x421f:return 0x00;
case 0x4000:dprintf("* 4000 read at %3d,%4d <%d>", cpu->time.v, cpu->time.hc, cpu->status.cycle_count);break;
case 0x4200:dprintf("* 4200 read at %3d,%4d", cpu->time.v, cpu->time.hc);break;
}
return cpu->regs.mdr;
@ -383,14 +359,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,32 +406,32 @@ 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
@ -479,7 +461,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 +531,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);
@ -600,6 +580,7 @@ uint8 i;
case 0x420b:cpu->mmio_w420b(value);return; //DMAEN
case 0x420c:cpu->mmio_w420c(value);return; //HDMAEN
case 0x420d:cpu->mmio_w420d(value);return; //MEMSEL
case 0x4000:dprintf("* 4000 write at %3d,%4d", cpu->time.v, cpu->time.hc);break;
}
}

View File

@ -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;
@ -160,6 +167,7 @@ void bCPU::op_stp() {
status.cpu_state = CPUSTATE_STP;
break;
case 2:
last_cycle();
cpu_io();
regs.pc.w--;
status.cycle_pos = 0;
@ -174,8 +182,11 @@ void bCPU::op_wai() {
status.cpu_state = CPUSTATE_WAI;
break;
case 2:
last_cycle();
cpu_io();
regs.pc.w--;
if(status.cpu_state == CPUSTATE_WAI) {
regs.pc.w--;
}
status.cycle_pos = 0;
break;
}
@ -184,6 +195,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 +214,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 +225,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 +236,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 +247,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 +258,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 +269,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 +280,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 +294,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 +313,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 +329,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 +348,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 +367,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 +386,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 +405,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 +424,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 +443,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 +456,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 +468,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 +481,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 +499,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 +518,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 +544,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 +561,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 +578,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 +595,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 +608,7 @@ void bCPU::op_phb() {
cpu_io();
break;
case 2:
last_cycle();
stack_write(regs.db);
status.cycle_pos = 0;
break;
@ -583,6 +621,7 @@ void bCPU::op_phk() {
cpu_io();
break;
case 2:
last_cycle();
stack_write(regs.pc.b);
status.cycle_pos = 0;
break;
@ -595,6 +634,7 @@ void bCPU::op_php() {
cpu_io();
break;
case 2:
last_cycle();
stack_write(regs.p);
status.cycle_pos = 0;
break;
@ -610,6 +650,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 +659,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 +677,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 +686,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 +704,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 +713,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 +731,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 +740,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 +758,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 +776,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 +800,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 +825,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 +848,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,7 +207,6 @@ void bCPU::inc_vcounter() {
}
}
time.hc = 0;
time.dma_counter = time.line_cycles & 6;
if(time.v == 240 && time.interlace == false && time.interlace_field == 1) {
time.line_cycles = 1360;
@ -76,6 +214,8 @@ void bCPU::inc_vcounter() {
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,28 +242,73 @@ 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();
// ppu->render_scanline();
snes->scanline();
time.line_rendered = false;
}
if(hdma_test() == true) {
hdma_run();
if(time.line_rendered == false) {
//rendering should start at H=22, but due to inaccurate
//timing, and due to using a scanline-based renderer, use
//a higher value to allow more games to run properly...
//H=48 fixes off-by-one HDMA effects with FF6's battles
if(time.hc + cycles >= (48 * 4)) {
cycles = (time.hc + cycles) - (48 * 4);
time.hc = (48 * 4);
time.line_rendered = true;
ppu->render_scanline();
}
}
if(time.dram_refreshed == false) {
if(time.hc + cycles >= time.dram_refresh_pos) {
time.dram_refreshed = true;
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(status.hdma_triggered == false) {
//vcounter range verified on hardware
if(time.v <= (overscan() ? 239 : 224)) {
if(time.hc + cycles >= 1112) { //278 * 4 = 1112
cycles = (time.hc + cycles) - 1112;
time.hc = 1112;
status.hdma_triggered = true;
hdma_run();
}
}
}
time.hc += cycles;
}
void bCPU::time_reset() {
@ -150,10 +325,20 @@ 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;
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,42 @@ 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 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 +49,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 +64,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;
@ -49,19 +54,24 @@ cop(0x02, 0xfff4, 0xfff5, 0xffe4, 0xffe5) {
stp(0xdb) {
1:cpu_io();
status.cpu_state = CPUSTATE_STP;
2:cpu_io();
2:last_cycle();
cpu_io();
regs.pc.w--;
}
wai(0xcb) {
1:cpu_io();
status.cpu_state = CPUSTATE_WAI;
2:cpu_io();
regs.pc.w--;
2:last_cycle();
cpu_io();
if(status.cpu_state == CPUSTATE_WAI) {
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 +90,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 +114,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 +128,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 +164,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 +178,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 +198,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 +216,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 +232,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 +241,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 +254,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 +264,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 +274,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 = 2;
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,6 @@ public:
virtual bool interlace() = 0;
virtual bool interlace_field() = 0;
virtual bool overscan() = 0;
virtual void set_interlace(bool r) = 0;
virtual void set_overscan (bool r) = 0;

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

@ -0,0 +1,583 @@
#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++) {
if(status.soft_reset()) {
if(voice[v].env_state != SILENCE) {
voice[v].env_state = SILENCE;
voice[v].AdjustEnvelope();
}
} else if(status.KOFF & (1 << v)) {
if(voice[v].env_state != SILENCE && voice[v].env_state != RELEASE) {
voice[v].env_state = RELEASE;
voice[v].AdjustEnvelope();
}
} else if(status.kon & (1 << v)) {
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.012"
#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,27 +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;
}
}
void bPPU::render_scanline() {
//only allow frameskip setting to ignore actual rendering; not RTO, etc.
if(settings.frameskip_pos != 0)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();
}
}
bool bPPU::render_frame() { return (settings.frameskip_pos == 0); }
void bPPU::frame() {
if(settings.frameskip_changed == true) {
settings.frameskip_changed = false;
@ -58,12 +54,6 @@ void bPPU::frame() {
if(settings.frameskip_pos != 0)return;
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) {
@ -82,7 +72,6 @@ void bPPU::power() {
}
void bPPU::reset() {
memset(output->buffer, 0, 512 * 478 * 2);
frame();
memset(sprite_list, 0, sizeof(sprite_list));
@ -335,9 +324,9 @@ bPPU::bPPU() {
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 +335,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 +347,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 +355,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 +367,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

@ -25,12 +25,12 @@ struct sprite_item {
bool vflip, hflip;
uint8 palette;
uint8 priority;
}sprite_list[128];
} sprite_list[128];
struct {
int32 frameskip, frameskip_pos;
bool frameskip_changed;
}settings;
} settings;
struct {
//open bus support
@ -119,6 +119,7 @@ struct {
//$2130
uint8 color_mask, colorsub_mask;
bool addsub_mode;
bool direct_color;
//$2131
bool color_mode, color_halve;
@ -145,7 +146,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);
@ -225,7 +226,7 @@ struct {
void latch_counters();
/* PPU render functions */
//PPU render functions
#include "bppu_render.h"
@ -234,13 +235,17 @@ 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 render_frame();
bool scanline_is_hires() { return (regs.bg_mode == 5 || regs.bg_mode == 6); }
bPPU();
~bPPU();

View File

@ -393,6 +393,7 @@ 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);
}
//CGADDSUB
@ -538,9 +539,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 +561,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();
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,15 +18,17 @@ 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];
@ -55,7 +57,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 +68,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,36 +104,43 @@ 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;
build_window_tables(bg);
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...
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);
pos = regs.bg_scaddr[BG3] + tile_x;
t = *((uint16*)vram + (pos >> 1));
if(t & opt_valid_bit) {
if(!(t & 0x8000)) {
@ -153,18 +150,19 @@ uint8 *wt_sub = sub_windowtable[bg];
}
}
} else {
pos = regs.bg_scaddr[BG3] + (tile_x << 1);
pos = regs.bg_scaddr[BG3] + tile_x;
t = *((uint16*)vram + (pos >> 1));
if(t & opt_valid_bit) {
hoffset = ((t & 0x1ff8) | (hscroll & 7)) & screen_width_mask;
}
pos = regs.bg_scaddr[BG3] + 64 + (tile_x << 1);
pos = regs.bg_scaddr[BG3] + 64 + tile_x;
t = *((uint16*)vram + (pos >> 1));
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];
}
@ -189,30 +187,36 @@ uint8 *wt_sub = sub_windowtable[bg];
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;
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(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

@ -1,36 +1,45 @@
inline uint16 bPPU::get_palette(int index) {
inline uint16 bPPU::get_palette(uint8 index) {
return *((uint16*)cgram + index);
}
inline uint16 bPPU::get_direct_color(uint8 p, uint8 t) {
//p = 00000bgr
//t = BBGGGRRR
//r = 0BBb00GGGg0RRRr0
return ((t & 7) << 2) | ((p & 1) << 1) |
(((t >> 3) & 7) << 7) | (((p >> 1) & 1) << 6) |
((t >> 6) << 13) | ((p >> 2) << 12);
}
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.bg_color_enabled[p->bg_main & 0x7f] && sub_colorwindowtable[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.bg_color_enabled[p->bg_main & 0x7f] && sub_colorwindowtable[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.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;
@ -38,9 +47,9 @@ uint16 _r;
} else {
if(main_colorwindowtable[x]) {
if(regs.bg_color_enabled[BACK] && sub_colorwindowtable[x]) {
_r = addsub_pixel(0, BACK);
_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,72 @@
#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;
vofs = (int32(regs.m7_vofs + 1) << 19) >> 19;
int _pri, _x;
bool _bg_enabled = regs.bg_enabled[bg];
bool _bgsub_enabled = regs.bgsub_enabled[bg];
build_window_tables(bg);
uint8 *wt_main = main_windowtable[bg];
uint8 *wt_sub = sub_windowtable[bg];
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 +78,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 +90,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(!palette)continue;
if(regs.mode7_hflip == true) {
_x = 255 - x;
} else {
_x = x;
}
if(main_colorwindowtable[_x]) {
if(_bg_enabled == true && !wt_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

@ -256,14 +256,15 @@ int _pri;
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

@ -0,0 +1,148 @@
#define CLIP_10BIT_SIGNED(x) \
((x) & ((1 << 10) - 1)) + (((((x) & (1 << 13)) ^ (1 << 13)) - (1 << 13)) >> 3)
#define CAST_WORDTOINT(x) \
(int32)((((x) & 0x8000) ? ((x) | 0xffff0000) : ((x) & 0x00007fff)))
void bPPU::render_line_mode7(uint8 bg, uint8 pri0_pos, uint8 pri1_pos) {
int32 x;
int32 step_m7a, step_m7c, m7a, m7b, m7c, m7d;
int32 hoffset, voffset;
int32 centerx, centery;
int32 xx, yy;
int32 px, py;
int32 tx, ty, tile, palette;
uint8 layer_pos;
if(regs.bg_enabled[bg] == false && regs.bgsub_enabled[bg] == false)return;
uint16 *mtable_x, *mtable_y;
if(bg == BG1) {
mtable_x = (uint16*)mosaic_table[(regs.mosaic_enabled[BG1] == true) ? regs.mosaic_size : 0];
mtable_y = (uint16*)mosaic_table[(regs.mosaic_enabled[BG1] == true) ? regs.mosaic_size : 0];
} else { //bg == BG2
//Mode7 EXTBG BG2 uses BG1 mosaic enable to control vertical mosaic,
//and BG2 mosaic enable to control horizontal mosaic...
mtable_x = (uint16*)mosaic_table[(regs.mosaic_enabled[BG2] == true) ? regs.mosaic_size : 0];
mtable_y = (uint16*)mosaic_table[(regs.mosaic_enabled[BG1] == true) ? regs.mosaic_size : 0];
}
hoffset = ((int32)regs.m7_hofs << 19) >> 19;
voffset = ((int32)regs.m7_vofs << 19) >> 19;
centerx = ((int32)regs.m7x << 19) >> 19;
centery = ((int32)regs.m7y << 19) >> 19;
if(regs.mode7_vflip == true) {
yy = 255 - _y;
} else {
yy = _y;
}
yy = mtable_y[yy];
yy += CLIP_10BIT_SIGNED(voffset - centery);
m7b = CAST_WORDTOINT(regs.m7b) * yy + (centerx << 8);
m7d = CAST_WORDTOINT(regs.m7d) * yy + (centery << 8);
step_m7a = CAST_WORDTOINT(regs.m7a);
step_m7c = CAST_WORDTOINT(regs.m7c);
xx = CLIP_10BIT_SIGNED(hoffset - centerx);
m7a = CAST_WORDTOINT(regs.m7a) * xx;
m7c = CAST_WORDTOINT(regs.m7c) * xx;
int _pri, _x;
bool _bg_enabled, _bgsub_enabled;
_bg_enabled = regs.bg_enabled[bg];
_bgsub_enabled = regs.bgsub_enabled[bg];
build_window_tables(bg);
uint8 *wt_main = main_windowtable[bg];
uint8 *wt_sub = sub_windowtable[bg];
for(x=0;x<256;x++) {
px = ((m7a + step_m7a * mtable_x[x] + m7b) >> 8);
py = ((m7c + step_m7c * mtable_x[x] + m7d) >> 8);
switch(regs.mode7_repeat) {
case 0: //screen repitition outside of screen area
case 1: //same as case 0
px &= 1023;
py &= 1023;
tx = ((px >> 3) & 127);
ty = ((py >> 3) & 127);
tile = vram[(ty * 128 + tx) << 1];
palette = vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1];
break;
case 2: //palette color 0 outside of screen area
if(px < 0 || px > 1023 || py < 0 || py > 1023) {
palette = 0;
} else {
px &= 1023;
py &= 1023;
tx = ((px >> 3) & 127);
ty = ((py >> 3) & 127);
tile = vram[(ty * 128 + tx) << 1];
palette = vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1];
}
break;
case 3: //character 0 repetition outside of screen area
if(px < 0 || px > 1023 || py < 0 || py > 1023) {
tx = 0;
ty = 0;
} else {
px &= 1023;
py &= 1023;
tx = ((px >> 3) & 127);
ty = ((py >> 3) & 127);
}
tile = vram[(ty * 128 + tx) << 1];
palette = vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1];
break;
}
if(bg == BG1) {
_pri = pri0_pos;
} else {
_pri = (palette >> 7) ? pri1_pos : pri0_pos;
palette &= 0x7f;
}
if(!palette)continue;
if(regs.mode7_hflip == true) {
_x = 255 - x;
} else {
_x = x;
}
if(main_colorwindowtable[_x]) {
uint32 col;
if(regs.direct_color == true && bg == BG1) {
//direct color mode does not apply to bg2 as it is only 128 colors...
col = get_direct_color(0, palette);
} else {
col = get_palette(palette);
}
if(regs.bg_enabled[bg] == true && !wt_main[_x]) {
if(pixel_cache[_x].pri_main < _pri) {
pixel_cache[_x].pri_main = _pri;
pixel_cache[_x].bg_main = 0x80 | bg;
pixel_cache[_x].src_main = col;
pixel_cache[_x].color_exempt = false;
}
}
if(regs.bgsub_enabled[bg] == true && !wt_sub[_x]) {
if(pixel_cache[_x].pri_sub < _pri) {
pixel_cache[_x].pri_sub = _pri;
pixel_cache[_x].bg_sub = 0x80 | bg;
pixel_cache[_x].src_sub = col;
}
}
}
}
}
#undef CLIP_10BIT_SIGNED
#undef CAST_WORDTOINT

View File

@ -1,27 +1,17 @@
#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;
}
}
PPUOutput::~PPUOutput() {
if(buffer)memfree(buffer, "PPUOutput::buffer");
void PPU::get_scanline_info(scanline_info *info) {
info->hires = scanline_is_hires();
info->interlace = cpu->interlace();
}
void PPU::set_frameskip(int fs) {}
bool PPU::render_frame() { return true; }
PPU::PPU() {
ppu1_version = 1;
ppu2_version = 3;
mmio = &mmio_unmapped;
output = new PPUOutput();
}
PPU::~PPU() {
if(output)delete(output);
}
PPU::~PPU() {}

View File

@ -1,19 +1,25 @@
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;
//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 +31,12 @@ MMIO *mmio;
virtual void run() = 0;
virtual void scanline() = 0;
virtual void render_scanline() = 0;
virtual void frame() = 0;
virtual void power() = 0;
virtual void reset() = 0;
virtual void set_frameskip(int fs);
virtual bool render_frame();
PPU();
~PPU();

View File

@ -6,6 +6,7 @@ OBJS = sdlmain.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

@ -6,6 +6,7 @@ OBJS = sdlmain.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,7 +17,8 @@ void bSNES::snes_run() {
}
}
void bSNES::render_frame() {}
void bSNES::video_run() { render(); }
void bSNES::sound_run() {}
/***********************
*** Input functions ***
@ -28,18 +29,18 @@ void bSNES::render_frame() {}
//to throw error messages about a bad free call to stdout...
void bSNES::poll_input() {
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,10 +14,12 @@ 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();

View File

@ -1,18 +0,0 @@
apu.enabled = true
video.fullscreen = false
video.display_width = 256
video.display_height = 223
video.output_width = 256
video.output_height = 223
input.joypad1.up = 0x111
input.joypad1.down = 0x112
input.joypad1.left = 0x114
input.joypad1.right = 0x113
input.joypad1.a = 0x78
input.joypad1.b = 0x7a
input.joypad1.x = 0x73
input.joypad1.y = 0x61
input.joypad1.l = 0x64
input.joypad1.r = 0x63
input.joypad1.select = 0x12f
input.joypad1.start = 0x0d

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,19 @@ 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(0x4000, cpu->mmio); //test -- remove this
mem_bus->set_mmio_mapper(0x4016, cpu->mmio);
mem_bus->set_mmio_mapper(0x4017, cpu->mmio);
for(i=0x4200;i<=0x421f;i++)mem_bus->set_mmio_mapper(i, cpu->mmio);
for(i=0x4300;i<=0x437f;i++)mem_bus->set_mmio_mapper(i, cpu->mmio);
//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 +74,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 +112,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 +135,7 @@ int i;
/***************************
*** Debugging functions ***
***************************/
void SNES::notify(uint32 message, uint32 param1, uint32 param2) {}
void SNES::debugger_enable() {
@ -102,8 +151,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() = 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;

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

@ -0,0 +1,385 @@
#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
switch(video.depth) {
case 15: //rgb565
for(i=0;i<32768;i++) {
r = (i ) & 31;
g = (i >> 5) & 31;
b = (i >> 10) & 31;
if((bool)config::snes.video_color_curve == true) {
r = color_curve_table[r] >> 3;
g = color_curve_table[g] >> 3;
b = color_curve_table[b] >> 3;
}
if((int)config::snes.video_color_adjust_mode == VCA_GRAYSCALE) {
r = (r << 3) | (r >> 2);
g = (g << 3) | (g >> 2);
b = (b << 3) | (b >> 2);
l = int((double)r * lr) + ((double)g * lg) + ((double)b * lb);
if(l < 0)l = 0;
if(l > 255)l = 255;
r = g = b = l;
r >>= 3;
g >>= 3;
b >>= 3;
} else if((int)config::snes.video_color_adjust_mode == VCA_VGA) {
//rgb555->rgb332
r >>= 2;
g >>= 2;
b >>= 3;
r = (r << 2) | (r >> 1);
g = (g << 2) | (g >> 1);
b = (b << 3) | (b << 1) | (b >> 1);
} else if((int)config::snes.video_color_adjust_mode == VCA_GENESIS) {
//rgb555->rgb333
r >>= 2;
g >>= 2;
b >>= 2;
r = (r << 2) | (r >> 1);
g = (g << 2) | (g >> 1);
b = (b << 2) | (b >> 1);
}
color_lookup_table[i] = (r << 10) | (g << 5) | (b);
}
break;
case 16: //rgb565
for(i=0;i<32768;i++) {
r = (i ) & 31;
g = (i >> 5) & 31;
b = (i >> 10) & 31;
if((bool)config::snes.video_color_curve == true) {
r = color_curve_table[r] >> 3;
g = color_curve_table[g] >> 2;
b = color_curve_table[b] >> 3;
} else {
g = (g << 1) | (g >> 4);
}
if((int)config::snes.video_color_adjust_mode == VCA_GRAYSCALE) {
r = (r << 3) | (r >> 2);
g = (g << 2) | (g >> 4);
b = (b << 3) | (b >> 2);
l = int((double)r * lr) + ((double)g * lg) + ((double)b * lb);
if(l < 0)l = 0;
if(l > 255)l = 255;
r = g = b = l;
r >>= 3;
g >>= 2;
b >>= 3;
} else if((int)config::snes.video_color_adjust_mode == VCA_VGA) {
//rgb565->rgb332
r >>= 2;
g >>= 3;
b >>= 3;
r = (r << 2) | (r >> 1);
g = (g << 3) | (g);
b = (b << 3) | (b << 1) | (b >> 1);
} else if((int)config::snes.video_color_adjust_mode == VCA_GENESIS) {
//rgb565->rgb333
r >>= 2;
g >>= 3;
b >>= 2;
r = (r << 2) | (r >> 1);
g = (g << 3) | (g);
b = (b << 2) | (b >> 1);
}
color_lookup_table[i] = (r << 11) | (g << 5) | (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->render_frame())return;
if(video.format_changed == true) {
update_video_format();
}
uint16 *src = (uint16*)video.ppu_data + ((int(cpu->overscan()) << 3) * 1024);
switch(video.mode) {
case VM_256x224:video_update_256x224(src);break;
case VM_512x224:video_update_512x224(src);break;
case VM_256x448:video_update_256x448(src);break;
case VM_512x448:video_update_512x448(src);break;
case VM_VARIABLE:
switch(int(video.frame_hires) | (int(video.frame_interlace) << 1)) {
case 0:video_update_256x224(src);break;
case 1:video_update_512x224(src);break;
case 2:video_update_256x448(src);break;
case 3:video_update_512x448(src);break;
}
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

@ -6,10 +6,11 @@ OBJS = winmain.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,28 +113,32 @@ void bSNES::snes_run() {
}
}
void bSNES::render_frame() {
void bSNES::video_run() {
dd_renderer->update();
}
void bSNES::sound_run() {
ds_sound->run();
}
/***********************
*** Input functions ***
***********************/
void bSNES::poll_input() {
//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);
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);
} else {
joypad1.up = joypad1.down = joypad1.left = joypad1.right =
joypad1.select = joypad1.start =
@ -272,7 +276,6 @@ void bSNES::notify(uint32 message, uint32 param1, uint32 param2) {
switch(message) {
case RENDER_FRAME:
update_frame = true;
render_frame();
break;
}

View File

@ -35,10 +35,12 @@ 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();

Binary file not shown.

Binary file not shown.

View File

@ -1,52 +1,40 @@
#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 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", VK_UP, Setting::HEX);
Setting Input::Joypad::down (&config_file, "input.joypad1.down", "Joypad1 down", VK_DOWN, Setting::HEX);
Setting Input::Joypad::left (&config_file, "input.joypad1.left", "Joypad1 left", VK_LEFT, Setting::HEX);
Setting Input::Joypad::right (&config_file, "input.joypad1.right", "Joypad1 right", VK_RIGHT, Setting::HEX);
Setting Input::Joypad::a (&config_file, "input.joypad1.a", "Joypad1 A", 'X', Setting::HEX);
Setting Input::Joypad::b (&config_file, "input.joypad1.b", "Joypad1 B", 'Z', Setting::HEX);
Setting Input::Joypad::x (&config_file, "input.joypad1.x", "Joypad1 X", 'S', Setting::HEX);
Setting Input::Joypad::y (&config_file, "input.joypad1.y", "Joypad1 Y", 'A', Setting::HEX);
Setting Input::Joypad::l (&config_file, "input.joypad1.l", "Joypad1 L", 'D', Setting::HEX);
Setting Input::Joypad::r (&config_file, "input.joypad1.r", "Joypad1 R", 'C', Setting::HEX);
Setting Input::Joypad::select(&config_file, "input.joypad1.select", "Joypad1 select", VK_SHIFT, Setting::HEX);
Setting Input::Joypad::start (&config_file, "input.joypad1.start", "Joypad1 start", VK_RETURN, Setting::HEX);
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;
};

View File

@ -4,68 +4,6 @@ DDRenderer::DDRenderer() {
lpdds = 0;
lpddsb = 0;
lpddc = 0;
int i, 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;
}
void DDRenderer::update_color_lookup_table() {
int i, r, g, b;
lpddsb->GetSurfaceDesc(&ddsd);
color_depth = ddsd.ddpfPixelFormat.dwRGBBitCount;
if(color_depth == 15) {
for(i=0;i<32768;i++) {
r = (i ) & 31;
g = (i >> 5) & 31;
b = (i >> 10) & 31;
if(cfg.video.color_curve) {
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<32768;i++) {
r = (i ) & 31;
g = (i >> 5) & 31;
b = (i >> 10) & 31;
if(cfg.video.color_curve) {
r = color_curve_table[r] >> 3;
g = color_curve_table[g] >> 2;
b = color_curve_table[b] >> 3;
} else {
g = (g << 1) | (g >> 4);
}
color_lookup_table[i] = (r << 11) | (g << 5) | (b);
}
} else if(color_depth == 32) {
for(i=0;i<32768;i++) {
r = (i ) & 31;
g = (i >> 5) & 31;
b = (i >> 10) & 31;
if(cfg.video.color_curve) {
r = color_curve_table[r];
g = color_curve_table[g];
b = color_curve_table[b];
} else {
r = (r << 3) | (r >> 2);
g = (g << 3) | (g >> 2);
b = (b << 3) | (b >> 2);
}
color_lookup_table[i] = (r << 16) | (g << 8) | (b);
}
} else {
alert("Error: Unsupported color depth [%d]", color_depth);
}
}
void DDRenderer::set_window(HWND hwnd_handle) { hwnd = hwnd_handle; }
@ -76,25 +14,24 @@ void DDRenderer::set_window(HWND hwnd_handle) { hwnd = hwnd_handle; }
handles conversion from 16bpp->32bpp in hardware. This only works
when both the source and dest buffers are in VRAM, though.
The SNES resolution is 256x224. The top scanline is never drawn, so
256x223 is used. Hires mode doubles the screen width, and hires+interlace
double the screen height, making the resolution 512x446.
The SNES resolution is 256x224. Hires mode doubles the screen width, and
hires+interlace double the screen height, making the resolution 512x448.
There is one more problem, however. On some video cards, when blitting
from video memory from 256x223->512x446, a bilinear filter is applied by
from video memory from 256x224->512x448, a bilinear filter is applied by
the video card, and sometimes this filter tries to read past the source
video memory to get interpolation data. This results in a line of garble
on the right and bottom edges of the screen. Therefore, an additional
4 pixels in each direction is added to the backbuffer.
The backbuffer is thusly 512+4 * 476+4
The backbuffer is thusly 512+4 * 480+4
*/
void DDRenderer::create_backbuffer() {
int color_depth;
int depth;
lpdds->GetSurfaceDesc(&ddsd);
color_depth = ddsd.ddpfPixelFormat.dwRGBBitCount;
if(color_depth == 15 || color_depth == 16) {
depth = ddsd.ddpfPixelFormat.dwRGBBitCount;
if(depth == 15 || depth == 16) {
goto try_native_backbuffer;
} else {
if(cfg.video.use_vram == false) {
if((int)config::video.use_vram == false) {
goto try_native_backbuffer;
}
}
@ -103,14 +40,14 @@ int color_depth;
ddsd.dwSize = sizeof(DDSURFACEDESC);
ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
if(cfg.video.use_vram) {
if(config::video.use_vram) {
ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
} else {
ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
}
ddsd.dwWidth = 512 + 4;
ddsd.dwHeight = 476 + 4;
ddsd.dwHeight = 480 + 4;
ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB;
@ -126,14 +63,14 @@ try_native_backbuffer:
ddsd.dwSize = sizeof(DDSURFACEDESC);
ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
if(cfg.video.use_vram) {
if(config::video.use_vram) {
ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
} else {
ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
}
ddsd.dwWidth = 512 + 4;
ddsd.dwHeight = 476 + 4;
ddsd.dwHeight = 480 + 4;
lpdd->CreateSurface(&ddsd, &lpddsb, 0);
@ -147,6 +84,9 @@ DDBLTFX fx;
fx.dwSize = sizeof(DDBLTFX);
fx.dwFillColor = 0x00000000;
lpddsb->Blt(0, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &fx);
lpddsb->GetSurfaceDesc(&ddsd);
color_depth = ddsd.ddpfPixelFormat.dwRGBBitCount;
}
void DDRenderer::to_windowed() {
@ -166,7 +106,6 @@ void DDRenderer::to_windowed() {
lpdds->SetClipper(lpddc);
create_backbuffer();
update_color_lookup_table();
redraw();
}
@ -195,14 +134,13 @@ void DDRenderer::to_fullscreen(int _width, int _height) {
lpdds->SetClipper(lpddc);
create_backbuffer();
update_color_lookup_table();
redraw();
}
void DDRenderer::set_source_window() {
SetRect(&lpddrc, 0, 0,
(ppu->output->hires == false)?256:512,
(ppu->output->interlace == false)?223:446);
//skip first scanline
int i = (vi.height == 224) ? 1 : 2;
SetRect(&lpddrc, 0, i, vi.width, vi.height);
}
void DDRenderer::redraw() {
@ -226,7 +164,7 @@ int rx, ry;
break;
}
}
if(cfg.video.vblank) {
if(config::video.vblank) {
lpdd->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, 0);
}
hr = lpdds->Blt(&rd, lpddsb, &lpddrc, DDBLT_WAIT, 0);
@ -235,7 +173,7 @@ int rx, ry;
lpddsb->Restore();
}
if(cfg.gui.show_fps == false || bsnes->get_status() == bSNES::STOP)return;
if((int)config::gui.show_fps == false || bsnes->get_status() == bSNES::STOP)return;
uint32 fps;
char s[256], t[256];
fps_timer->tick();
@ -262,24 +200,22 @@ HRESULT hr;
if(hr != DD_OK)return;
set_source_window();
if(ppu->output->hires == false) {
if(ppu->output->interlace == false) {
update16_256x224();
} else {
update16_256x448();
}
} else {
if(ppu->output->interlace == false) {
update16_512x224();
} else {
update16_512x448();
}
if (vi.width == 256 && vi.height == 224) {
update16_256x224();
} else if(vi.width == 512 && vi.height == 224) {
update16_512x224();
} else if(vi.width == 256 && vi.height == 448) {
update16_256x448();
} else if(vi.width == 512 && vi.height == 448) {
update16_512x448();
}
lpddsb->Unlock(0);
}
#include "dd_renderer32.cpp"
//#include "dd_renderer32.cpp"
/*
void DDRenderer::update32() {
HRESULT hr;
hr = lpddsb->Lock(0, &ddsd, DDLOCK_WAIT, 0);
@ -302,17 +238,21 @@ HRESULT hr;
lpddsb->Unlock(0);
}
*/
void DDRenderer::update() {
snes->get_video_info(&vi);
switch(color_depth) {
case 15:
case 16:
update16();
break;
case 32:
update32();
//update32();
break;
}
redraw();
}

View File

@ -1,4 +1,5 @@
#include <ddraw.h>
class DDRenderer {
public:
LPDIRECTDRAW lpdd;
@ -9,11 +10,10 @@ DDSURFACEDESC ddsd;
DDSCAPS ddscaps;
RECT lpddrc;
HWND hwnd;
bool fullscreen;
int width, height; //used for fullscreen mode clipping only
uint8 color_depth;
uint8 color_curve_table[32];
uint32 color_lookup_table[32768];
bool fullscreen;
int width, height; //used for fullscreen mode clipping only
uint8 color_depth;
SNES::video_info vi; //initialized each frame at start of update()
void set_window(HWND hwnd_handle);
void create_backbuffer();
void to_windowed();
@ -21,18 +21,17 @@ uint32 color_lookup_table[32768];
void set_source_window();
void redraw();
inline void update16_256x224();
inline void update16_256x448();
inline void update16_512x224();
inline void update16_256x448();
inline void update16_512x448();
void update16();
inline void update32_256x224();
inline void update32_256x448();
inline void update32_512x224();
inline void update32_512x448();
void update32();
//inline void update32_256x224();
//inline void update32_512x224();
//inline void update32_256x448();
//inline void update32_512x448();
//void update32();
void update();
void destroy();
void update_color_lookup_table();
DDRenderer();
};

View File

@ -1,123 +1,79 @@
inline void DDRenderer::update16_256x224() {
uint16 *src;
uint16 *dest;
uint16 *src, *dest;
uint32 pitch;
int x, y;
src = (uint16*)ppu->output->buffer + (1 << 10);
dest = (uint16*)ddsd.lpSurface;
pitch = (ddsd.lPitch >> 1) - 256;
dest = (uint16*)ddsd.lpSurface;
src = (uint16*)vi.data;
int overscan_adjust = 0;
if(cpu->overscan() == true) {
src += 7 << 10;
overscan_adjust = 7;
#ifdef USE_X86_ASM
pitch = (ddsd.lPitch) - 512;
__asm {
mov edi,dest
mov esi,src
mov edx,224
ly:
mov ecx,32
lx:
movsd
movsd
movsd
movsd
loopnz lx
add edi,pitch
dec edx
jnz ly
}
#else
pitch = (ddsd.lPitch >> 1);
for(y=0;y<224;y++) {
memcpy(dest, src, 512);
dest += pitch;
src += 256;
}
#endif
}
for(y=1+overscan_adjust;y<224+overscan_adjust;y++) {
x = 256;
while(x--) {
*dest++ = color_lookup_table[*src];
src += 2;
}
inline void DDRenderer::update16_512x224() {
uint16 *src, *dest;
uint32 pitch;
int x, y;
pitch = (ddsd.lPitch >> 1);
dest = (uint16*)ddsd.lpSurface;
src = (uint16*)vi.data;
for(y=0;y<224;y++) {
memcpy(dest, src, 1024);
dest += pitch;
src += 512;
}
}
inline void DDRenderer::update16_256x448() {
uint16 *src;
uint16 *dest;
uint16 *src, *dest;
uint32 pitch;
int x, y;
src = (uint16*)ppu->output->buffer + (1 << 10);
pitch = (ddsd.lPitch >> 1);
dest = (uint16*)ddsd.lpSurface;
pitch = (ddsd.lPitch >> 1) - 256;
src = (uint16*)vi.data;
int overscan_adjust = 0;
if(cpu->overscan() == true) {
src += 7 << 10;
overscan_adjust = 14;
}
for(y=2+overscan_adjust;y<448+overscan_adjust;y++) {
x = 256;
while(x--) {
*dest++ = color_lookup_table[*src];
src += 2;
}
for(y=0;y<448;y++) {
memcpy(dest, src, 512);
dest += pitch;
if(ppu->output->line[y >> 1].interlace == false) {
src += (y & 1)?512:-512;
}
}
}
inline void DDRenderer::update16_512x224() {
uint16 *src;
uint16 *dest;
uint32 pitch;
int x, y;
src = (uint16*)ppu->output->buffer + (1 << 10);
dest = (uint16*)ddsd.lpSurface;
pitch = (ddsd.lPitch >> 1) - 512;
int overscan_adjust = 0;
if(cpu->overscan() == true) {
src += 7 << 10;
overscan_adjust = 7;
}
for(y=1+overscan_adjust;y<224+overscan_adjust;y++) {
if(ppu->output->line[y].hires == true) {
x = 512;
while(x--) {
*dest++ = color_lookup_table[*src++];
}
} else {
x = 256;
while(x--) {
*dest++ = color_lookup_table[*src];
*dest++ = color_lookup_table[*src];
src += 2;
}
}
dest += pitch;
src += 512;
src += 256;
}
}
inline void DDRenderer::update16_512x448() {
uint16 *src;
uint16 *dest;
uint16 *src, *dest;
uint32 pitch;
int x, y;
src = (uint16*)ppu->output->buffer + (1 << 10);
pitch = (ddsd.lPitch >> 1);
dest = (uint16*)ddsd.lpSurface;
pitch = (ddsd.lPitch >> 1) - 512;
src = (uint16*)vi.data;
int overscan_adjust = 0;
if(cpu->overscan() == true) {
src += 7 << 10;
overscan_adjust = 14;
}
for(y=2+overscan_adjust;y<448+overscan_adjust;y++) {
if(ppu->output->line[y >> 1].hires == true) {
x = 512;
while(x--) {
*dest++ = color_lookup_table[*src++];
}
} else {
x = 256;
while(x--) {
*dest++ = color_lookup_table[*src];
*dest++ = color_lookup_table[*src];
src += 2;
}
}
for(y=0;y<448;y++) {
memcpy(dest, src, 1024);
dest += pitch;
if(ppu->output->line[y >> 1].interlace == false) {
src += (y & 1)?512:-512;
}
src += 512;
}
}

77
src/win/ds_sound.cpp Normal file
View File

@ -0,0 +1,77 @@
DSSound::DSSound() {
data.buffer = 0;
data.lpos = data.lsample = data.lbuffer = 0;
}
void DSSound::run() {
if(snes->get_playback_buffer_pos() != 0)return;
uint32 pos, status;
//dsb_b[0]->SetFrequency(22050);
//do {
// dsb_b[0]->GetStatus(&status);
//} while(status & DSBSTATUS_PLAYING);
dsb_b[0]->Lock(0, DSP_BUFFER_SIZE * 4, &dslb, &dslbs, 0, 0, 0);
memcpy(dslb, snes->get_playback_buffer(), DSP_BUFFER_SIZE * 4);
dsb_b[0]->Unlock(dslb, dslbs, 0, 0);
dsb_b[0]->SetCurrentPosition(0);
//has the buffer stopped (possibly due to running too fast)?
dsb_b[0]->GetStatus(&status);
if(!(status & DSBSTATUS_PLAYING)) {
dsb_b[0]->Play(0, 0, 0);
}
}
void DSSound::init() {
DirectSoundCreate(0, &ds, 0);
ds->SetCooperativeLevel(w_main->hwnd, DSSCL_PRIORITY);
memset(&dsbd, 0, sizeof(dsbd));
dsbd.dwSize = sizeof(dsbd);
dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
dsbd.dwBufferBytes = 0;
dsbd.lpwfxFormat = 0;
ds->CreateSoundBuffer(&dsbd, &dsb_p, 0);
memset(&wfx, 0, sizeof(wfx));
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = 2;
wfx.nSamplesPerSec = 32000;
wfx.wBitsPerSample = 16;
wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels;
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
dsb_p->SetFormat(&wfx);
sample_size = (16 / 8) * 2;
buffer_size = DSP_BUFFER_SIZE * sample_size;
buffer_pos = 0;
dsb_b = new LPDIRECTSOUNDBUFFER[1];
memset(&dsbd, 0, sizeof(dsbd));
dsbd.dwSize = sizeof(dsbd);
dsbd.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_GLOBALFOCUS;
dsbd.dwBufferBytes = buffer_size;
dsbd.guid3DAlgorithm = GUID_NULL;
dsbd.lpwfxFormat = &wfx;
ds->CreateSoundBuffer(&dsbd, &dsb_b[0], 0);
ds->CreateSoundBuffer(&dsbd, &dsb_b[1], 0);
dsb_b[0]->SetFrequency(32000);
dsb_b[1]->SetFrequency(32000);
dsb_b[0]->Lock(0, buffer_size, &dslb, &dslbs, 0, 0, 0);
memset(dslb, 0, buffer_size);
dsb_b[0]->Unlock(dslb, dslbs, 0, 0);
dsb_b[1]->Lock(0, buffer_size, &dslb, &dslbs, 0, 0, 0);
memset(dslb, 0, buffer_size);
dsb_b[1]->Unlock(dslb, dslbs, 0, 0);
dsb_b[0]->Play(0, 0, 0);
buffer_pos = 0;
}

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