Updated to v067r23 release.

byuu says:

Added missing $4200 IRQ lock, which fixes Chou Aniki on the fast CPU
core, so slower PCs can get their brotherly love on.
Added range-based controller IOBit latching to the fast CPU core, which
enables Super Scope and Justifier support. Uses the priority queue as
well, so there is zero speed-hit. Given the way range-testing works, the
trigger point may vary by 1-2 pixels when firing at the same spot. Not
really a big deal when it avoids a massive speed penalty.
Fixed PAL and interlace-mode HVIRQs at V=0,H<2 on the fast CPU core.
Added the dot-renderer's sprite list update-on-OAM-write functionality
to the scanline-based PPU renderer. Unfortunately it looks like all the
speed gain was already taken from the global dirty flag I was using
before, but this certainly won't hurt speed any, so whatever.
Added #ifdef to stop CoInitialize(0) on non-Windows ports.
Added #ifdefs to stop gradient fade on Windows port. Not going to fuck
over the Linux port aesthetic because of Qt bug #47,326,927. If there's
a way to tell what Qt theme is being used, I can leave it enabled for
XP/Vista themes.
Moved HDMA trigger from 1104 to 1112, and reduced channel overhead from
24 to 16, to better simulate one-cycle DMA->CPU sync.

Code clarity: I've re-added my varint.hpp classes, and am actively using
them in the accuracy cores. So far, I haven't done anything that would
detriment speed, but it is certainly cool. The APU ports exposed by the
CPU and SMP now take uint2 address arguments, the CPU WRAM address
register is a uint17, and the IRQ H/VTIME values are uint10. This
basically allows the source to clearly convey the data sizes, and
eliminates the need to manually mask values when writing to registers or
reading from memory. I'm going to be doing this everywhere, and it will
have a speed impact eventually, because the automation means we can't
skip masks when we know the data is already masked off.

Source: archive contains the launcher code, so that I can look into why
it's crashing on XP tomorrow.

It doesn't look like Circuit USA's flags are going to work too well with
this new CPU core. Still not sure what the hell Robocop vs The
Terminator is doing, I'll read through the mega SNES thread for clues
tomorrow. Speedy Gonzales is definitely broken, as modifying the MDR was
breaking things with my current core. Probably because the new CPU core
doesn't wait for a cycle edge to trigger.

I was thinking that perhaps we could keep some form of cheat codes list
to work as game-specific hacks for the performance core. Keeps the hacks
out of the emulator, but could allow the remaining bugs to be worked
around for people who have no choice but to use the performance core.
This commit is contained in:
Tim Allen 2010-08-16 19:42:20 +10:00
parent cda10094da
commit 70429285ba
38 changed files with 265 additions and 183 deletions

View File

@ -1,6 +1,6 @@
include nall/Makefile
snes := snes
profile := accuracy
profile := performance
ui := qt
# compiler
@ -53,8 +53,8 @@ objects := $(patsubst %,obj/%.o,$(objects))
# targets
build: ui_build $(objects)
ifeq ($(platform),osx)
test -d ../bsnes.app || mkdir -p ../bsnes.app/Contents/MacOS
$(strip $(cpp) -o ../bsnes.app/Contents/MacOS/bsnes $(objects) $(link))
test -d ../bsnes-$(profile).app || mkdir -p ../bsnes-$(profile).app/Contents/MacOS
$(strip $(cpp) -o ../bsnes-$(profile).app/Contents/MacOS/bsnes-$(profile) $(objects) $(link))
else
$(strip $(cpp) -o out/bsnes-$(profile) $(objects) $(link))
endif
@ -87,6 +87,6 @@ clean: ui_clean
-@$(call delete,*.manifest)
archive-all:
tar -cjf bsnes-`date +%Y%m%d`.tar.bz2 libco nall obj out qt ruby snes Makefile sync.sh
tar -cjf bsnes-`date +%Y%m%d`.tar.bz2 launcher libco nall obj out qt ruby snes Makefile sync.sh cc.bat clean.bat
help:;

1
cc.bat Executable file
View File

@ -0,0 +1 @@
@mingw32-make -j 2

1
clean.bat Executable file
View File

@ -0,0 +1 @@
@mingw32-make clean

9
launcher/bsnes.Manifest Executable file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32" name="bsnes" version="1.0.0.0" processorArchitecture="x86"/>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="X86" publicKeyToken="6595b64144ccf1df" language="*"/>
</dependentAssembly>
</dependency>
</assembly>

BIN
launcher/bsnes.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

4
launcher/cc.bat Executable file
View File

@ -0,0 +1,4 @@
@windres resource.rc resource.o
@mingw32-g++ -std=gnu++0x -mwindows -s -O3 -fomit-frame-pointer -I.. -o ../out/bsnes launcher.cpp resource.o
@del *.o
@pause

2
launcher/cc.sh Executable file
View File

@ -0,0 +1,2 @@
clear
g++ -std=gnu++0x -s -O3 -fomit-frame-pointer -I.. -o ../out/bsnes launcher.cpp

89
launcher/launcher.cpp Executable file
View File

@ -0,0 +1,89 @@
#include <nall/config.hpp>
#include <nall/detect.hpp>
#include <nall/file.hpp>
#include <nall/foreach.hpp>
#include <nall/platform.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
using namespace nall;
#if defined(PLATFORM_X) || defined(PLATFORM_OSX)
char* userpath(char *path) {
*path = 0;
struct passwd *userinfo = getpwuid(getuid());
if(userinfo) strcpy(path, userinfo->pw_dir);
return path;
}
char *getcwd(char *path) {
return getcwd(path, PATH_MAX);
}
#elif defined(PLATFORM_WIN)
#include <nall/utf8.hpp>
#include <process.h>
#include <wchar.h>
#include <windows.h>
char* realpath(const char *filename, char *resolvedname) {
wchar_t fn[_MAX_PATH] = L"";
_wfullpath(fn, nall::utf16_t(filename), _MAX_PATH);
strcpy(resolvedname, nall::utf8_t(fn));
return resolvedname;
}
char* userpath(char *path) {
wchar_t fp[_MAX_PATH] = L"";
SHGetFolderPathW(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, 0, 0, fp);
strcpy(path, nall::utf8_t(fp));
return path;
}
char* getcwd(char *path) {
wchar_t fp[_MAX_PATH] = L"";
_wgetcwd(fp, _MAX_PATH);
strcpy(path, nall::utf8_t(fp));
return path;
}
intptr_t execv(const char *cmdname, const char *const *argv) {
int argc = __argc;
wchar_t **wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
return _wexecv(nall::utf16_t(cmdname), wargv);
}
#endif
int main(int argc, char **argv) {
char path[PATH_MAX], *unused;
unused = realpath(argv[0], path);
string realPath = dir(path);
string basePath = string(dir(path), "bsnes.cfg");
unused = userpath(path);
if(!strend(path, "/") && !strend(path, "\\")) strcat(path, "/");
string userPath = string(path, ".bsnes/bsnes.cfg");
configuration config;
string profile;
config.attach(profile = "", "system.profile");
if(config.load(userPath) == false) config.load(basePath);
if(profile == "") profile = "compatibility";
string binaryName = string("bsnes-", profile);
#if defined(PLATFORM_WIN)
binaryName << ".dll";
#endif
string fileName = string("/usr/local/bin/", binaryName);
if(!file::exists(fileName)) fileName = string("/usr/bin/", binaryName);
if(!file::exists(fileName)) fileName = string(realPath, binaryName);
if(!file::exists(fileName)) {
#if defined(PLATFORM_WIN)
MessageBox(0, string("Error: unable to locate binary file :", binaryName), "bsnes", MB_OK);
#else
print("[bsnes] Error: unable to locate binary file: ", binaryName, "\n");
#endif
return 0;
}
execv(fileName, argv);
return 0;
}

2
launcher/resource.rc Executable file
View File

@ -0,0 +1,2 @@
1 24 "bsnes.Manifest"
IDI_ICON1 ICON DISCARDABLE "bsnes.ico"

View File

@ -1,9 +1,9 @@
#ifndef NALL_VARINT_HPP
#define NALL_VARINT_HPP
#include <type_traits>
#include <nall/bit.hpp>
#include <nall/static.hpp>
#include <nall/traits.hpp>
namespace nall {
template<unsigned bits> class uint_t {
@ -22,7 +22,7 @@ namespace nall {
>::type
>::type
>::type T;
static_assert<!is_void<T>::value> uint_assert;
static_assert(!std::is_same<T, void>::value, "");
T data;
public:
@ -63,7 +63,7 @@ namespace nall {
>::type
>::type
>::type T;
static_assert<!is_void<T>::value> int_assert;
static_assert(!std::is_same<T, void>::value, "");
T data;
public:

View File

@ -61,12 +61,12 @@ void Application::locateFile(string &filename, bool createDataDirectory) {
}
int Application::main(int &argc, char **argv) {
CoInitialize(0);
app = new App(argc, argv);
#if !defined(PLATFORM_WIN)
//Windows port uses 256x256 icon from resource file
app->setWindowIcon(QIcon(":/bsnes.png"));
#else
//Windows port uses 256x256 icon from resource file
CoInitialize(0);
#endif
initargs(argc, argv); //ensure argv[]s are in UTF-8 format

View File

@ -20,7 +20,9 @@ ProfileSettingsWindow::ProfileSettingsWindow() {
profileAccuracy->setStyleSheet(
"font-weight: bold;"
"font-size: 12pt;"
#if !defined(PLATFORM_WIN)
"background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 rgba(255, 0, 0, 48), stop: 1 rgba(255, 0, 0, 0));"
#endif
);
layout->addWidget(profileAccuracy);
@ -37,7 +39,9 @@ ProfileSettingsWindow::ProfileSettingsWindow() {
profileCompatibility->setStyleSheet(
"font-weight: bold;"
"font-size: 12pt;"
#if !defined(PLATFORM_WIN)
"background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 rgba(0, 0, 255, 48), stop: 1 rgba(0, 0, 255, 0));"
#endif
);
layout->addWidget(profileCompatibility);
@ -54,12 +58,14 @@ ProfileSettingsWindow::ProfileSettingsWindow() {
profilePerformance->setStyleSheet(
"font-weight: bold;"
"font-size: 12pt;"
#if !defined(PLATFORM_WIN)
"background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 rgba(0, 255, 0, 48), stop: 1 rgba(0, 255, 0, 0));"
#endif
);
layout->addWidget(profilePerformance);
profilePerformanceInfo = new QLabel(
"<b>System Requirements:</b> Intel Atom, Intel Pentium IV or AMD Athlon processor.<br>"
"<b>System Requirements:</b> Intel Pentium IV or AMD Athlon processor.<br>"
"High accuracy with reasonable compromises for performance.<br>"
"Sacrifices a small degree of compatibility to run full-speed on older hardware.<br>"
"Use this mode for slower systems, or if you are running on battery power."

View File

@ -1,2 +0,0 @@
@g++ -std=gnu++0x -O3 -fomit-frame-pointer -I../.. -o test test.cpp -DAUDIO_DIRECTSOUND -DAUDIO_XAUDIO2 -DINPUT_DIRECTINPUT -lole32 -luuid -ldxguid -ldsound -ldinput8
@pause

View File

@ -1,40 +0,0 @@
#include <nall/foreach.hpp>
#include <nall/string.hpp>
using namespace nall;
#include <ruby/ruby.cpp>
using namespace ruby;
#include <conio.h>
int main() {
CoInitialize(0);
audio.driver("XAudio2");
audio.set(Audio::Handle, (uintptr_t)GetDesktopWindow());
audio.set(Audio::Synchronize, true);
audio.set(Audio::Frequency, 44100U);
if(audio.init() == false) {
printf("Failed to initialize audio driver.\n");
getch();
return 0;
}
input.driver("DirectInput");
input.set(Input::Handle, (uintptr_t)GetDesktopWindow());
if(input.init() == false) {
printf("Failed to initialize input driver.\n");
getch();
return 0;
}
while(true) {
int16_t table[Scancode::Limit];
input.poll(table);
for(unsigned i = 0; i < Scancode::Limit; i++) {
//if(table[i]) printf("%.4x\n", i);
}
}
return 0;
}

Binary file not shown.

View File

@ -132,11 +132,6 @@ void CPU::reset() {
mmio_reset();
dma_reset();
timing_reset();
apu_port[0] = 0x00;
apu_port[1] = 0x00;
apu_port[2] = 0x00;
apu_port[3] = 0x00;
}
CPU::CPU() {

View File

@ -7,11 +7,12 @@ public:
void synchronize_ppu();
void synchronize_coprocessor();
uint8 port_read(uint2 port) const;
void port_write(uint2 port, uint8 data);
uint8 pio();
bool joylatch();
alwaysinline bool interrupt_pending() { return status.interrupt_pending; }
alwaysinline uint8 port_read(uint8 port) { return apu_port[port & 3]; }
alwaysinline void port_write(uint8 port, uint8 data) { apu_port[port & 3] = data; }
void enter();
void power();
@ -71,8 +72,11 @@ private:
bool hdma_mode; //0 = init, 1 = run
//MMIO
//$2140-217f
uint8 port[4];
//$2181-$2183
uint32 wram_addr;
uint17 wram_addr;
//$4016-$4017
bool joypad_strobe_latch;
@ -93,10 +97,11 @@ private:
//$4204-$4206
uint16 wrdiva;
uint8 wrdivb;
uint8 wrdivb;
//$4207-$420a
uint16 hirq_pos, virq_pos;
uint10 hirq_pos;
uint10 virq_pos;
//$420d
unsigned rom_speed;

View File

@ -3,8 +3,6 @@
void CPU::dma_add_clocks(unsigned clocks) {
status.dma_clocks += clocks;
add_clocks(clocks);
synchronize_ppu();
synchronize_coprocessor();
}
//=============

View File

@ -1,5 +1,8 @@
#ifdef CPU_CPP
uint8 CPU::port_read(uint2 port) const { return status.port[port]; }
void CPU::port_write(uint2 port, uint8 data) { status.port[port] = data; }
void CPU::op_io() {
status.clock_count = 6;
dma_edge();

View File

@ -1,5 +1,3 @@
uint8 apu_port[4];
void op_io();
debugvirtual uint8 op_read(uint32 addr);
debugvirtual void op_write(uint32 addr, uint8 data);

View File

@ -5,33 +5,27 @@ bool CPU::joylatch() { return status.joypad_strobe_latch; }
//WMDATA
uint8 CPU::mmio_r2180() {
uint8 r = bus.read(0x7e0000 | status.wram_addr);
status.wram_addr = (status.wram_addr + 1) & 0x01ffff;
return r;
return bus.read(0x7e0000 | status.wram_addr++);
}
//WMDATA
void CPU::mmio_w2180(uint8 data) {
bus.write(0x7e0000 | status.wram_addr, data);
status.wram_addr = (status.wram_addr + 1) & 0x01ffff;
bus.write(0x7e0000 | status.wram_addr++, data);
}
//WMADDL
void CPU::mmio_w2181(uint8 data) {
status.wram_addr = (status.wram_addr & 0xffff00) | (data);
status.wram_addr &= 0x01ffff;
status.wram_addr = (status.wram_addr & 0x01ff00) | (data << 0);
}
//WMADDM
void CPU::mmio_w2182(uint8 data) {
status.wram_addr = (status.wram_addr & 0xff00ff) | (data << 8);
status.wram_addr &= 0x01ffff;
status.wram_addr = (status.wram_addr & 0x0100ff) | (data << 8);
}
//WMADDH
void CPU::mmio_w2183(uint8 data) {
status.wram_addr = (status.wram_addr & 0x00ffff) | (data << 16);
status.wram_addr &= 0x01ffff;
status.wram_addr = (status.wram_addr & 0x00ffff) | (data << 16);
}
//JOYSER0
@ -42,10 +36,7 @@ void CPU::mmio_w4016(uint8 data) {
bool old_latch = status.joypad_strobe_latch;
bool new_latch = data & 1;
status.joypad_strobe_latch = new_latch;
if(old_latch != new_latch) {
input.poll();
}
if(old_latch != new_latch) input.poll();
}
//JOYSER0
@ -69,15 +60,13 @@ uint8 CPU::mmio_r4017() {
//NMITIMEN
void CPU::mmio_w4200(uint8 data) {
status.auto_joypad_poll = !!(data & 0x01);
status.auto_joypad_poll = data & 1;
nmitimen_update(data);
}
//WRIO
void CPU::mmio_w4201(uint8 data) {
if((status.pio & 0x80) && !(data & 0x80)) {
ppu.latch_counters();
}
if((status.pio & 0x80) && !(data & 0x80)) ppu.latch_counters();
status.pio = data;
}
@ -100,7 +89,7 @@ void CPU::mmio_w4203(uint8 data) {
//WRDIVL
void CPU::mmio_w4204(uint8 data) {
status.wrdiva = (status.wrdiva & 0xff00) | (data);
status.wrdiva = (status.wrdiva & 0xff00) | (data << 0);
}
//WRDIVH
@ -121,26 +110,22 @@ void CPU::mmio_w4206(uint8 data) {
//HTIMEL
void CPU::mmio_w4207(uint8 data) {
status.hirq_pos = (status.hirq_pos & ~0xff) | (data);
status.hirq_pos &= 0x01ff;
status.hirq_pos = (status.hirq_pos & 0x0100) | (data << 0);
}
//HTIMEH
void CPU::mmio_w4208(uint8 data) {
status.hirq_pos = (status.hirq_pos & 0xff) | (data << 8);
status.hirq_pos &= 0x01ff;
status.hirq_pos = (status.hirq_pos & 0x00ff) | (data << 8);
}
//VTIMEL
void CPU::mmio_w4209(uint8 data) {
status.virq_pos = (status.virq_pos & ~0xff) | (data);
status.virq_pos &= 0x01ff;
status.virq_pos = (status.virq_pos & 0x0100) | (data << 0);
}
//VTIMEH
void CPU::mmio_w420a(uint8 data) {
status.virq_pos = (status.virq_pos & 0xff) | (data << 8);
status.virq_pos &= 0x01ff;
status.virq_pos = (status.virq_pos & 0x00ff) | (data << 8);
}
//DMAEN
@ -191,16 +176,9 @@ uint8 CPU::mmio_r4211() {
uint8 CPU::mmio_r4212() {
uint8 r = (regs.mdr & 0x3e);
uint16 vs = ppu.overscan() == false ? 225 : 240;
//auto joypad polling
if(vcounter() >= vs && vcounter() <= (vs + 2))r |= 0x01;
//hblank
if(hcounter() <= 2 || hcounter() >= 1096)r |= 0x40;
//vblank
if(vcounter() >= vs)r |= 0x80;
if(vcounter() >= vs && vcounter() <= (vs + 2)) r |= 0x01; //auto joypad polling
if(hcounter() <= 2 || hcounter() >= 1096) r |= 0x40; //hblank
if(vcounter() >= vs) r |= 0x80; //vblank
return r;
}
@ -211,7 +189,7 @@ uint8 CPU::mmio_r4213() {
//RDDIVL
uint8 CPU::mmio_r4214() {
return status.rddiv;
return status.rddiv >> 0;
}
//RDDIVH
@ -221,7 +199,7 @@ uint8 CPU::mmio_r4215() {
//RDMPYL
uint8 CPU::mmio_r4216() {
return status.rdmpy;
return status.rdmpy >> 0;
}
//RDMPYH
@ -256,7 +234,7 @@ uint8 CPU::mmio_r43x1(uint8 i) {
//A1TxL
uint8 CPU::mmio_r43x2(uint8 i) {
return channel[i].source_addr;
return channel[i].source_addr >> 0;
}
//A1TxH
@ -272,7 +250,7 @@ uint8 CPU::mmio_r43x4(uint8 i) {
//DASxL
//union { uint16 transfer_size; uint16 indirect_addr; };
uint8 CPU::mmio_r43x5(uint8 i) {
return channel[i].transfer_size;
return channel[i].transfer_size >> 0;
}
//DASxH
@ -288,7 +266,7 @@ uint8 CPU::mmio_r43x7(uint8 i) {
//A2AxL
uint8 CPU::mmio_r43x8(uint8 i) {
return channel[i].hdma_addr;
return channel[i].hdma_addr >> 0;
}
//A2AxH
@ -323,7 +301,7 @@ void CPU::mmio_w43x1(uint8 i, uint8 data) {
//A1TxL
void CPU::mmio_w43x2(uint8 i, uint8 data) {
channel[i].source_addr = (channel[i].source_addr & 0xff00) | (data);
channel[i].source_addr = (channel[i].source_addr & 0xff00) | (data << 0);
}
//A1TxH
@ -339,7 +317,7 @@ void CPU::mmio_w43x4(uint8 i, uint8 data) {
//DASxL
//union { uint16 transfer_size; uint16 indirect_addr; };
void CPU::mmio_w43x5(uint8 i, uint8 data) {
channel[i].transfer_size = (channel[i].transfer_size & 0xff00) | (data);
channel[i].transfer_size = (channel[i].transfer_size & 0xff00) | (data << 0);
}
//DASxH
@ -355,7 +333,7 @@ void CPU::mmio_w43x7(uint8 i, uint8 data) {
//A2AxL
void CPU::mmio_w43x8(uint8 i, uint8 data) {
channel[i].hdma_addr = (channel[i].hdma_addr & 0xff00) | (data);
channel[i].hdma_addr = (channel[i].hdma_addr & 0xff00) | (data << 0);
}
//A2AxH
@ -377,6 +355,9 @@ void CPU::mmio_power() {
}
void CPU::mmio_reset() {
//$2140-217f
foreach(port, status.port) port = 0x00;
//$2181-$2183
status.wram_addr = 0x000000;
@ -386,9 +367,9 @@ void CPU::mmio_reset() {
status.joypad2_bits = ~0;
//$4200
status.nmi_enabled = false;
status.hirq_enabled = false;
status.virq_enabled = false;
status.nmi_enabled = false;
status.hirq_enabled = false;
status.virq_enabled = false;
status.auto_joypad_poll = false;
//$4201
@ -426,7 +407,7 @@ void CPU::mmio_reset() {
//ALU
alu.mpyctr = 0;
alu.divctr = 0;
alu.shift = 0;
alu.shift = 0;
}
uint8 CPU::mmio_read(unsigned addr) {
@ -435,7 +416,7 @@ uint8 CPU::mmio_read(unsigned addr) {
//APU
if((addr & 0xffc0) == 0x2140) { //$2140-$217f
synchronize_smp();
return smp.port_read(addr & 3);
return smp.port_read(addr);
}
//DMA
@ -492,7 +473,7 @@ void CPU::mmio_write(unsigned addr, uint8 data) {
//APU
if((addr & 0xffc0) == 0x2140) { //$2140-$217f
synchronize_smp();
port_write(addr & 3, data);
port_write(addr, data);
return;
}

View File

@ -44,6 +44,8 @@ void CPU::serialize(serializer &s) {
s.integer(status.hdma_pending);
s.integer(status.hdma_mode);
s.array(status.port);
s.integer(status.wram_addr);
s.integer(status.joypad_strobe_latch);
@ -108,11 +110,6 @@ void CPU::serialize(serializer &s) {
s.integer(pipe.valid);
s.integer(pipe.addr);
s.integer(pipe.data);
s.integer(apu_port[0]);
s.integer(apu_port[1]);
s.integer(apu_port[2]);
s.integer(apu_port[3]);
}
#endif

View File

@ -12,16 +12,16 @@ void CPU::run_auto_joypad_poll() {
joy4 |= (port1 & 2) ? (0x8000 >> i) : 0;
}
status.joy1l = joy1;
status.joy1l = joy1 >> 0;
status.joy1h = joy1 >> 8;
status.joy2l = joy2;
status.joy2l = joy2 >> 0;
status.joy2h = joy2 >> 8;
status.joy3l = joy3;
status.joy3l = joy3 >> 0;
status.joy3h = joy3 >> 8;
status.joy4l = joy4;
status.joy4l = joy4 >> 0;
status.joy4h = joy4 >> 8;
}

View File

@ -37,6 +37,7 @@ private:
enum : unsigned {
DramRefresh,
HdmaRun,
ControllerLatch,
};
};
nall::priority_queue<unsigned> queue;

View File

@ -122,7 +122,7 @@ void CPU::hdma_run() {
}
if(channels == 0) return;
add_clocks(24);
add_clocks(16);
for(unsigned i = 0; i < 8; i++) {
if(channel[i].hdma_enabled == false || channel[i].hdma_completed == true) continue;
channel[i].dma_enabled = false;

View File

@ -158,6 +158,7 @@ void CPU::mmio_write(unsigned addr, uint8 data) {
status.irq_transition = false;
}
status.irq_lock = true;
return;
}

View File

@ -4,6 +4,7 @@ void CPU::queue_event(unsigned id) {
switch(id) {
case QueueEvent::DramRefresh: return add_clocks(40);
case QueueEvent::HdmaRun: return hdma_run();
case QueueEvent::ControllerLatch: return ppu.latch_counters();
}
}
@ -31,7 +32,8 @@ void CPU::add_clocks(unsigned clocks) {
if(status.virq_enabled) {
unsigned cpu_time = vcounter() * 1364 + hcounter();
unsigned irq_time = status.vtime * 1364 + status.htime * 4;
if(cpu_time > irq_time) irq_time += 262 * 1364;
unsigned framelines = (system.region() == System::Region::NTSC ? 262 : 312) + field();
if(cpu_time > irq_time) irq_time += framelines * 1364;
bool irq_valid = status.irq_valid;
status.irq_valid = cpu_time <= irq_time && cpu_time + clocks > irq_time;
if(!irq_valid && status.irq_valid) status.irq_line = true;
@ -66,7 +68,11 @@ void CPU::scanline() {
queue.enqueue(534, QueueEvent::DramRefresh);
if(vcounter() <= (ppu.overscan() == false ? 224 : 239)) {
queue.enqueue(1104, QueueEvent::HdmaRun);
queue.enqueue(1104 + 8, QueueEvent::HdmaRun);
}
if(vcounter() == input.latchy) {
queue.enqueue(input.latchx, QueueEvent::ControllerLatch);
}
bool nmi_valid = status.nmi_valid;

View File

@ -105,11 +105,14 @@ void PPU::oam_mmio_write(uint16 addr, uint8 data) {
if(regs.display_disabled == true) {
memory::oam[addr] = data;
update_sprite_list(addr, data);
} else {
if(cpu.vcounter() < (!overscan() ? 225 : 240)) {
memory::oam[regs.ioamaddr] = data;
update_sprite_list(regs.ioamaddr, data);
} else {
memory::oam[addr] = data;
update_sprite_list(addr, data);
}
}
}

View File

@ -1,15 +1,37 @@
#ifdef PPU_CPP
void PPU::update_sprite_list(unsigned addr, uint8 data) {
if(addr < 0x0200) {
unsigned i = addr >> 2;
switch(addr & 3) {
case 0: sprite_list[i].x = (sprite_list[i].x & 0x0100) | data; break;
case 1: sprite_list[i].y = (data + 1) & 0xff; break;
case 2: sprite_list[i].character = data; break;
case 3: sprite_list[i].vflip = data & 0x80;
sprite_list[i].hflip = data & 0x40;
sprite_list[i].priority = (data >> 4) & 3;
sprite_list[i].palette = (data >> 1) & 7;
sprite_list[i].use_nameselect = data & 0x01;
}
} else {
unsigned i = (addr & 0x1f) << 2;
sprite_list[i + 0].x = ((data & 0x01) << 8) | (sprite_list[i + 0].x & 0xff);
sprite_list[i + 0].size = data & 0x02;
sprite_list[i + 1].x = ((data & 0x04) << 6) | (sprite_list[i + 1].x & 0xff);
sprite_list[i + 1].size = data & 0x08;
sprite_list[i + 2].x = ((data & 0x10) << 4) | (sprite_list[i + 2].x & 0xff);
sprite_list[i + 2].size = data & 0x20;
sprite_list[i + 3].x = ((data & 0x40) << 2) | (sprite_list[i + 3].x & 0xff);
sprite_list[i + 3].size = data & 0x80;
}
}
void PPU::build_sprite_list() {
if(sprite_list_valid == true) return;
sprite_list_valid = true;
const uint8 *tableA = memory::oam.data();
const uint8 *tableB = memory::oam.data() + 512;
for(unsigned i = 0; i < 128; i++) {
const bool x = *tableB & (1 << ((i & 3) << 1)); //0x01, 0x04, 0x10, 0x40
const bool size = *tableB & (2 << ((i & 3) << 1)); //0x02, 0x08, 0x20, 0x80
const bool size = sprite_list[i].size;
switch(cache.oam_basesize) {
case 0: sprite_list[i].width = (!size) ? 8 : 16;
@ -40,18 +62,6 @@ void PPU::build_sprite_list() {
if(regs.oam_interlace && !size) sprite_list[i].height = 16;
break;
}
sprite_list[i].x = (x << 8) + tableA[0];
sprite_list[i].y = (tableA[1] + 1) & 0xff;
sprite_list[i].character = tableA[2];
sprite_list[i].vflip = tableA[3] & 0x80;
sprite_list[i].hflip = tableA[3] & 0x40;
sprite_list[i].priority = (tableA[3] >> 4) & 3;
sprite_list[i].palette = (tableA[3] >> 1) & 7;
sprite_list[i].use_nameselect = tableA[3] & 1;
tableA += 4;
if((i & 3) == 3) tableB++;
}
}

View File

@ -62,6 +62,7 @@ struct sprite_item {
bool vflip, hflip;
uint8 palette;
uint8 priority;
bool size;
} sprite_list[128];
bool sprite_list_valid;
unsigned active_sprite;
@ -75,6 +76,7 @@ struct oam_tileitem {
enum { OAM_PRI_NONE = 4 };
uint8 oam_line_pal[256], oam_line_pri[256];
void update_sprite_list(unsigned addr, uint8 data);
void build_sprite_list();
bool is_sprite_on_scanline();
void load_oam_tiles();

View File

@ -180,6 +180,7 @@ void PPU::serialize(serializer &s) {
s.integer(sprite_list[n].hflip);
s.integer(sprite_list[n].palette);
s.integer(sprite_list[n].priority);
s.integer(sprite_list[n].size);
}
s.integer(sprite_list_valid);
s.integer(active_sprite);

View File

@ -82,6 +82,7 @@ private:
friend class System;
friend class Video;
friend class CPU;
};
extern Input input;

View File

@ -11,12 +11,12 @@ alwaysinline void SMP::ram_write(uint16 addr, uint8 data) {
if(status.ram_writable && !status.ram_disabled) memory::apuram[addr] = data;
}
uint8 SMP::port_read(uint8 port) {
return memory::apuram[0xf4 + (port & 3)];
uint8 SMP::port_read(uint2 port) const {
return memory::apuram[0xf4 + port];
}
void SMP::port_write(uint8 port, uint8 data) {
memory::apuram[0xf4 + (port & 3)] = data;
void SMP::port_write(uint2 port, uint8 data) {
memory::apuram[0xf4 + port] = data;
}
alwaysinline uint8 SMP::op_busread(uint16 addr) {
@ -45,15 +45,15 @@ alwaysinline uint8 SMP::op_busread(uint16 addr) {
case 0xf6: //CPUIO2
case 0xf7: { //CPUIO3
synchronize_cpu();
r = cpu.port_read(addr & 3);
r = cpu.port_read(addr);
} break;
case 0xf8: { //RAM0
r = status.smp_f8;
r = status.ram0;
} break;
case 0xf9: { //RAM1
r = status.smp_f9;
r = status.ram1;
} break;
case 0xfa: //T0TARGET
@ -146,7 +146,7 @@ alwaysinline void SMP::op_buswrite(uint16 addr, uint8 data) {
} break;
case 0xf3: { //DSPDATA
//0x80-0xff is a read-only mirror of 0x00-0x7f
//0x80-0xff are read-only mirrors of 0x00-0x7f
if(!(status.dsp_addr & 0x80)) {
dsp.write(status.dsp_addr & 0x7f, data);
}
@ -157,15 +157,15 @@ alwaysinline void SMP::op_buswrite(uint16 addr, uint8 data) {
case 0xf6: //CPUIO2
case 0xf7: { //CPUIO3
synchronize_cpu();
port_write(addr & 3, data);
port_write(addr, data);
} break;
case 0xf8: { //RAM0
status.smp_f8 = data;
status.ram0 = data;
} break;
case 0xf9: { //RAM1
status.smp_f9 = data;
status.ram1 = data;
} break;
case 0xfa: { //T0TARGET

View File

@ -19,8 +19,8 @@ void SMP::serialize(serializer &s) {
s.integer(status.dsp_addr);
s.integer(status.smp_f8);
s.integer(status.smp_f9);
s.integer(status.ram0);
s.integer(status.ram1);
s.integer(t0.stage0_ticks);
s.integer(t0.stage1_ticks);

View File

@ -65,26 +65,26 @@ void SMP::reset() {
create(Enter, system.apu_frequency());
regs.pc = 0xffc0;
regs.a = 0x00;
regs.x = 0x00;
regs.y = 0x00;
regs.a = 0x00;
regs.x = 0x00;
regs.y = 0x00;
regs.sp = 0xef;
regs.p = 0x02;
regs.p = 0x02;
for(unsigned i = 0; i < memory::apuram.size(); i++) {
memory::apuram.write(i, 0x00);
}
status.clock_counter = 0;
status.dsp_counter = 0;
status.timer_step = 3;
status.dsp_counter = 0;
status.timer_step = 3;
//$00f0
status.clock_speed = 0;
status.timer_speed = 0;
status.timers_enabled = true;
status.ram_disabled = false;
status.ram_writable = true;
status.clock_speed = 0;
status.timer_speed = 0;
status.timers_enabled = true;
status.ram_disabled = false;
status.ram_writable = true;
status.timers_disabled = false;
//$00f1
@ -94,8 +94,8 @@ void SMP::reset() {
status.dsp_addr = 0x00;
//$00f8,$00f9
status.smp_f8 = 0x00;
status.smp_f9 = 0x00;
status.ram0 = 0x00;
status.ram1 = 0x00;
t0.stage0_ticks = 0;
t1.stage0_ticks = 0;

View File

@ -5,8 +5,8 @@ public:
alwaysinline void synchronize_cpu();
alwaysinline void synchronize_dsp();
uint8 port_read(uint8 port);
void port_write(uint8 port, uint8 data);
uint8 port_read(uint2 port) const;
void port_write(uint2 port, uint8 data);
void enter();
void power();
@ -43,7 +43,8 @@ private:
uint8 dsp_addr;
//$00f8,$00f9
uint8 smp_f8, smp_f9;
uint8 ram0;
uint8 ram1;
} status;
static void Enter();

View File

@ -1,7 +1,7 @@
namespace SNES {
namespace Info {
static const char Name[] = "bsnes";
static const char Version[] = "067.22";
static const char Version[] = "067.23";
static const unsigned SerializerVersion = 12;
}
}
@ -28,6 +28,7 @@ namespace SNES {
#include <nall/stdint.hpp>
#include <nall/string.hpp>
#include <nall/utility.hpp>
#include <nall/varint.hpp>
#include <nall/vector.hpp>
using namespace nall;
@ -38,15 +39,20 @@ using namespace nall;
#endif
namespace SNES {
typedef int8_t int8;
typedef int16_t int16;
typedef int32_t int32;
typedef int64_t int64;
typedef uint8_t uint8;
typedef int8_t int8;
typedef int16_t int16;
typedef int32_t int32;
typedef int64_t int64;
typedef uint8_t uint8;
typedef uint16_t uint16;
typedef uint32_t uint32;
typedef uint64_t uint64;
typedef uint_t<2> uint2;
typedef uint_t<10> uint10;
typedef uint_t<17> uint17;
typedef uint_t<24> uint24;
struct Processor {
cothread_t thread;
unsigned frequency;

View File

@ -11,3 +11,4 @@ synchronize "ruby"
test -d libco/doc && rm -r libco/doc
test -d libco/test && rm -r libco/test
test -d ruby/_test && rm -r ruby/_test