Compare commits

...

58 Commits
v108 ... v110

Author SHA1 Message Date
byuu
675662e739 v110
Corrections for IOKit joypad driver [Sintendo]
2019-09-21 04:59:29 +09:00
byuu
409dd371b9 v109.5
Added SHVC-4PV5B-01 prototype PCB to database.
Added Firepower 2000 fast PPU render cycle override.
Backported higan's newer accuracy PPU with sprite caching support.
2019-09-21 04:26:27 +09:00
byuu
18d2ab6435 v109.4
Rename hiro::Property to hiro::Attribute
Disable XChaCha20 CSPRNG on Android for now due to compilation issues
Add macOS IOKit joypad support [Sintendo]
2019-09-17 03:37:03 +09:00
byuu
1e626e75ef v109.3
Fixed crash when idling with the snow effect enabled.
Added Android target to libretro port [rtretiakov]
Various nall library improvements.
2019-09-13 22:15:11 +09:00
byuu
c6d90d3ff1 Dyslexia. 2019-09-10 22:35:30 +09:00
byuu
29caf77751 v109.2
Fixed alt-key menu activation on Windows.
Removed 2160p HD mode 7 due to Direct3D limit of 2048x2048 textures.
Reverted to safe ruby drivers when no configuration file is present.
Removed ASIO driver because nobody is interested in improving it.
Added macOS libretro target [rtretiakov]
2019-09-10 22:32:33 +09:00
byuu
29b13083d5 . 2019-09-07 18:22:55 +09:00
byuu
3883172a4e v109.1
Mask A23 for ExLoROM board mappings (fixes Thracia 776 fan translation)
2019-09-07 18:03:12 +09:00
byuu
fa77fc6a8f v109
Typo fix.
2019-09-06 23:54:58 +09:00
byuu
56c9a5195e v109
Keep focus on the panel list when changing settings / tools panels.
Fixed Windows combo box flickering when changing panels.
Suppress Alt+F4 on Windows in fullscreen mode.
2019-09-06 23:19:44 +09:00
byuu
a6ebce428f Minor cleanups. 2019-09-06 12:33:18 +09:00
byuu
0788627898 Minor cleanups. 2019-09-06 12:30:54 +09:00
byuu
1195c46ac0 v108.17
Enhanced perspective correction support [DerKoun]
2019-09-03 18:50:46 +09:00
byuu
90f094b931 Fix SGB JOYP incrementing behavior [endrift]
Fix GB JOYP read setting d6+d7 in SameBoy
Improve headered IPS patch handling
2019-09-03 17:55:54 +09:00
byuu
2bb1606552 v108.16
Added compatibility option to disable accurate CPU ALU emulation
Refactored settings panels
Added dialog to choose whether IPS patches are for headered ROMs
Disabled extended SNES header decoding thanks to ROM hacks ignoring them
2019-09-03 12:01:45 +09:00
byuu
08e5e81609 v108.15
Add "No VRAM Blocking" and "Echo Shadow RAM" options:
These allow compatibility with very old ROM hacks that
only previously ran in ZSNES and earlier Snes9X releases.
2019-09-02 13:51:04 +09:00
byuu
03aaaba889 Minor SGB change. 2019-09-02 11:03:14 +09:00
byuu
556ab4c809 Added a fix for a programming bug in Dirt Racer (Europe)
The game relies on uninitialized memory containing certain values.
The game will periodically freeze at boot even on real hardware.
This commit adds a workaround to allow the game to always boot.
2019-09-02 10:53:53 +09:00
byuu
23467b5b1f Merged libretro target [Themaister and rtretiakov] 2019-09-02 10:19:18 +09:00
byuu
5ae1bcd973 Force disable entropy when recording movies from reset.
This prevents potential desyncs in games that don't initialize RAM/IO.
2019-08-31 09:27:14 +09:00
byuu
64e3658bcb Minor code restructuring. 2019-08-31 09:13:16 +09:00
byuu
ab515b59d4 Crayon Shin-chan - Arashi o Yobu Enji fix 2019-08-31 08:14:52 +09:00
byuu
bb7b2f2e60 Added entropy setting (none, low, high) to settings->emulator 2019-08-31 07:08:10 +09:00
byuu
639b9db961 v108.14
* extremely pedantic mosaic improvement for the fast PPU
2019-08-31 06:38:24 +09:00
byuu
f857f35e72 Fix Super Mario RPG regression. 2019-08-26 02:46:08 +09:00
byuu
5dc27a9fb3 Bubsy II (PAL) fix. 2019-08-26 01:37:24 +09:00
byuu
ce3dba130c v108.13
* fix CPU DMA regression from higan v106.62 (fixes Battle Grand Prix)
2019-08-25 01:13:19 +09:00
byuu
f9ca7a4927 Fix an IRQ regression from a few releases back.
Add fast PPU render cycle position setting.
2019-08-24 01:23:18 +09:00
byuu
db1c37c799 v108.12
* fix sprite index mask (Addams Family bugfix)
* fix exclusive mode in Direct3D driver
2019-08-24 08:34:17 +09:00
byuu
7a98db84ac Fix SGB Killer Instinct MLT_REQ to start with player 1 2019-08-22 01:02:34 +09:00
byuu
b73493b492 Fix for Great Battle IV background graphics. 2019-08-22 00:50:59 +09:00
byuu
95831d3675 v108.11
* added (primary-monitor only) fullscreen support for macOS
* improved settings windows a bit
* correct Program::focused() move from Video::exclusive()->fullScreen()
2019-08-18 03:20:14 +09:00
byuu
ab25877af4 macOS fixes. 2019-08-17 23:53:21 +09:00
byuu
5ba538ee39 v108.10
* provide actual display device names in ruby::Video::hasMonitors()
* macOS: fixed LineEdit::onChange (fixes state manager state naming)
* macOS: fixed RadioLabel height to not clip off bottom of radio icons
* macOS: fixed RadioLabel initial check states (fixed input settings
focus mode options)
* macOS: fixed TableViewItem::setSelected (fixes initial selection on
settings/tools windows)
* macOS: fixed TextEdit geometry (fixed manifest viewer)
* macOS: don't allow multiple TableViewItems to be selected in
single-selection mode
** (fixes settings/tools windows selecting multiple items via menubar
options)
2019-08-17 23:41:44 +09:00
byuu
04b85ade6b Add funding icon. 2019-08-16 19:49:48 +09:00
byuu
0b088b6b55 v108.9
* multi-monitor support
* improved pause/frame advance support
* added option to disable video dimming when idle
2019-08-16 19:44:16 +09:00
byuu
252f479b22 v108.8
* added deinterlacing option
2019-08-12 23:12:34 +09:00
byuu
46dbe00f14 Add dismissable warning when using nightlies. 2019-08-07 10:18:26 +09:00
byuu
66ad62b79f Fix refactoring regression and simplify dot PPU mode 7 code. 2019-08-07 10:04:31 +09:00
byuu
27b2d11839 Small fix. 2019-08-06 13:51:22 +09:00
byuu
96c381f91f v108.7
* removed emulator/bits.hpp dependency
2019-08-06 13:45:04 +09:00
byuu
5757949023 v108.6
* fix IRQ regression in Power Rangers: Fighting Edition
2019-08-06 02:48:28 +09:00
byuu
bad27bb8f3 . 2019-08-05 11:09:57 +09:00
byuu
06ceb7d29e Minor speedups for SuperFX and Cx4 emulation. 2019-08-05 11:08:25 +09:00
byuu
e030428054 v108.5
* double-click a cheat finder result to add a new cheat code
* fixed v108.1 regression not enabling coprocessor LLE when requested
* add "[HLE] " title bar indicator for HLE mode
* default to LLE mode for coprocessors
* simplify game titles in main window (eg omit SGB BIOS name)
* add more GUI tooltips to explain options
* pause emulator during modal loops (helps Windows menubar navigation)
* add support for decoding Game Genie + Pro Action Replay SNES cheats
* add support for decoding Game Genie + GameShark Game Boy cheats
* add tool-tip explanation to verified/unverified status bar icon
2019-08-05 09:27:51 +09:00
byuu
24dce7dd92 Remove CPU::boot() 2019-08-02 11:25:59 +09:00
byuu
b577e6d5d0 Simplifications to CPU interrupt handling. 2019-08-02 11:23:31 +09:00
byuu
db988d9588 Improvements to muting and snow. 2019-08-02 06:35:35 +09:00
byuu
f6303518d5 v108.4
* ~4.6% speedup (with the fast PPU)
* fix out-of-bounds DSP memory access [Sour]
2019-08-02 04:45:06 +09:00
byuu
9e8913cea0 Remove icarus, merge heuristics into bsnes. 2019-08-01 09:05:01 +09:00
byuu
0e56b27228 Okay maybe not all of them, but close to all of them ... 2019-08-01 08:54:40 +09:00
byuu
fe81130f54 Remove all template integer types from fast PPU core. 2019-08-01 08:49:18 +09:00
byuu
216b472418 Cirrus CI update, Cocoa compilation fix (hopefully.) 2019-08-01 03:15:52 +09:00
byuu
bc7456246c v108.3
* add support for TableView::onActivate(TableViewCell) to Windows, macOS
** allows the new input/hotkey mapping panels to work on Windows, macOS
* polish BrowserDialog behavior
2019-08-01 02:40:35 +09:00
byuu
a7b30b069c Fix blur emulation in the accurate PPU mode. 2019-08-01 01:10:27 +09:00
byuu
454b39cb78 v108.2
* reverted higan's thread scheduler to olde bsnes scheduler
** this allows CPU overclocking compatibility with coprocessors
2019-08-01 00:33:28 +09:00
byuu
f65b7a8528 v108.1
* added CPU and SA1 overclocking support
* added fast forward speed limiting
* added option to mute during fast forwarding and rewinding
* lowered volume when not muting during FF/rewind
* reformatted settings/tools windows from tabs to lists
* moved focus settings to input settings panel
* redesigned input and hotkey settings panels to be easier to use
* fixed offscreen placement issue with path settings panel
* added hotkey combinational logic option (AND / OR mode setting)
* added search support to file browser dialog
* fixed --fullscreen command-line option
2019-07-31 06:57:31 +09:00
byuu
7e88bdde09 Cirrus CI updates. 2019-07-29 04:01:54 +09:00
337 changed files with 11914 additions and 7014 deletions

View File

@@ -6,7 +6,7 @@ linux-x86_64-binaries_task:
- apt-get update && apt-get -y install build-essential libgtk2.0-dev libpulse-dev mesa-common-dev libgtksourceview2.0-dev libcairo2-dev libsdl2-dev libxv-dev libao-dev libopenal-dev libudev-dev zip
compile_script:
- make -C bsnes
- make -C bsnes local=false
package_script:
- mkdir bsnes-nightly
@@ -14,67 +14,68 @@ linux-x86_64-binaries_task:
- mkdir bsnes-nightly/Firmware
- cp -a bsnes/out/bsnes bsnes-nightly/bsnes
- cp -a bsnes/Database/* bsnes-nightly/Database
- cp -a icarus/Database/* bsnes-nightly/Database
- cp -a GPLv3.txt bsnes-nightly/
- cp -a GPLv3.txt bsnes-nightly
- zip -r bsnes-nightly.zip bsnes-nightly
bsnes-nightly_artifacts:
path: "bsnes-nightly/**"
path: "bsnes-nightly.zip"
freebsd-x86_64-binaries_task:
freebsd_instance:
image: freebsd-12-0-release-amd64
setup_script:
- pkg install --yes gmake gdb gcc8 pkgconf sdl2 openal-soft gtksourceview2 libXv
- pkg install --yes gmake gdb gcc8 pkgconf sdl2 openal-soft gtksourceview2 libXv zip
compile_script:
- gmake -C bsnes
- gmake -C bsnes local=false
package_script:
- mkdir bsnes-nightly
- mkdir bsnes-nightly/Database
- mkdir bsnes-nightly/Firmware
- cp -a bsnes/out/bsnes bsnes-nightly/bsnes
- cp -a bsnes/Database/* bsnes-nightly/Database/
- cp -a icarus/Database/* bsnes-nightly/Database/
- cp -a GPLv3.txt bsnes-nightly/
- cp -a bsnes/Database/* bsnes-nightly/Database
- cp -a GPLv3.txt bsnes-nightly
- zip -r bsnes-nightly.zip bsnes-nightly
bsnes-nightly_artifacts:
path: "bsnes-nightly/**"
path: "bsnes-nightly.zip"
windows-x86_64-binaries_task:
container:
image: ubuntu:latest
setup_script:
- apt-get update && apt-get -y install build-essential mingw-w64
- apt-get update && apt-get -y install build-essential mingw-w64 zip
compile_script:
- make -C bsnes platform=windows compiler="x86_64-w64-mingw32-g++" windres="x86_64-w64-mingw32-windres"
- make -C bsnes local=false platform=windows compiler="x86_64-w64-mingw32-g++" windres="x86_64-w64-mingw32-windres"
package_script:
- mkdir bsnes-nightly
- mkdir bsnes-nightly/Database
- mkdir bsnes-nightly/Firmware
- cp -a bsnes/out/bsnes bsnes-nightly/bsnes.exe
- cp -a bsnes/Database/* bsnes-nightly/Database/
- cp -a icarus/Database/* bsnes-nightly/Database/
- cp -a GPLv3.txt bsnes-nightly/
- cp -a bsnes/Database/* bsnes-nightly/Database
- cp -a GPLv3.txt bsnes-nightly
- zip -r bsnes-nightly.zip bsnes-nightly
bsnes-nightly_artifacts:
path: "bsnes-nightly/**"
path: "bsnes-nightly.zip"
macOS-x86_64-binaries_task:
osx_instance:
image: mojave-base
compile_script:
- make -C bsnes
- make -C bsnes local=false
package_script:
- mkdir bsnes-nightly
- cp -a bsnes/out/bsnes.app bsnes-nightly/
- cp -a GPLv3.txt bsnes-nightly/
- cp -a bsnes/out/bsnes.app bsnes-nightly
- cp -a GPLv3.txt bsnes-nightly
- zip -r bsnes-nightly.zip bsnes-nightly
bsnes-nightly_artifacts:
path: "bsnes-nightly/**"
path: "bsnes-nightly.zip"

1
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1 @@
patreon: byuu

View File

@@ -1,10 +1,9 @@
----------------------------------------------------------------------
bsnes - Suite of videogame console emulators
icarus - Game library importer for higan
bsnes - Super Nintendo emulator
Copyright © 2004-2019 byuu
Copyright © 2004-2019 byuu
https://byuu.org/
https://byuu.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,12 +20,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
----------------------------------------------------------------------
----------------------------------------------------------------------
hiro - User interface toolkit
libco - C cooperative threading library
nall - C++ template library
ruby - Hardware abstraction layer
libco - C cooperative threading library
nall - C++ template library
ruby - Hardware abstraction library
hiro - User interface library
Copyright © 2006-2019 byuu
Copyright © 2006-2019 byuu
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the

View File

@@ -20,10 +20,6 @@ However, bugs will exist, regressions will occur, so proceed at your own risk.
If stability is required, please download the latest stable release from the
[official website.](https://bsnes.byuu.org)
Nightly builds are available via Cirrus CI.
- [Nightly builds](https://cirrus-ci.com/github/byuu/bsnes)
Unique Features
---------------
@@ -60,12 +56,13 @@ Standard Features
- Sprite limit disable support
- Cubic audio interpolation support
- Optional high-level emulation of most SNES coprocessors
- SuperFX overclocking of up to 800%
- CPU, SA1, and SuperFX overclocking support
- Frame advance support
- Screenshot support
- Cheat code search support
- Movie recording and playback support
- Rewind support
- HiDPI support
Links
-----
@@ -73,3 +70,12 @@ Links
- [Official website](https://bsnes.byuu.org)
- [Official git repository](https://github.com/byuu/bsnes)
- [Donations](https://patreon.com/byuu)
Nightly Builds
--------------
- [Download](https://cirrus-ci.com/github/byuu/bsnes/master)
- ![Build status](https://api.cirrus-ci.com/github/byuu/bsnes.svg?task=windows-x86_64-binaries)
- ![Build status](https://api.cirrus-ci.com/github/byuu/bsnes.svg?task=macOS-x86_64-binaries)
- ![Build status](https://api.cirrus-ci.com/github/byuu/bsnes.svg?task=linux-x86_64-binaries)
- ![Build status](https://api.cirrus-ci.com/github/byuu/bsnes.svg?task=freebsd-x86_64-binaries)

View File

@@ -2,6 +2,7 @@ target := bsnes
binary := application
build := performance
openmp := true
local := true
flags += -I. -I..
# in order for this to work, obj/lzma.o must be omitted or bsnes will hang on startup.
@@ -11,6 +12,11 @@ ifeq ($(profile),true)
options += -pg
endif
# binaries built with this flag are faster, but are not portable to multiple machines.
ifeq ($(local),true)
flags += -march=native
endif
nall.path := ../nall
include $(nall.path)/GNUmakefile
@@ -30,7 +36,6 @@ else ifeq ($(platform),macos)
endif
else ifneq ($(filter $(platform),linux bsd),)
ifeq ($(binary),application)
flags += -march=native
options += -Wl,-export-dynamic
options += -lX11 -lXext
else ifeq ($(binary),library)

View File

@@ -10,45 +10,45 @@ Audio::~Audio() {
}
auto Audio::reset(Interface* interface) -> void {
this->interface = interface;
streams.reset();
channels = 0;
_interface = interface;
_streams.reset();
_channels = 0;
}
auto Audio::setFrequency(double frequency) -> void {
this->frequency = frequency;
for(auto& stream : streams) {
_frequency = frequency;
for(auto& stream : _streams) {
stream->setFrequency(stream->inputFrequency, frequency);
}
}
auto Audio::setVolume(double volume) -> void {
this->volume = volume;
_volume = volume;
}
auto Audio::setBalance(double balance) -> void {
this->balance = balance;
_balance = balance;
}
auto Audio::createStream(uint channels, double frequency) -> shared_pointer<Stream> {
this->channels = max(this->channels, channels);
_channels = max(_channels, channels);
shared_pointer<Stream> stream = new Stream;
stream->reset(channels, frequency, this->frequency);
streams.append(stream);
stream->reset(channels, frequency, _frequency);
_streams.append(stream);
return stream;
}
auto Audio::process() -> void {
while(streams) {
for(auto& stream : streams) {
while(_streams) {
for(auto& stream : _streams) {
if(!stream->pending()) return;
}
double samples[channels];
double samples[_channels];
for(auto& sample : samples) sample = 0.0;
for(auto& stream : streams) {
double buffer[channels];
for(auto& stream : _streams) {
double buffer[_channels];
uint length = stream->read(buffer), offset = 0;
for(auto& sample : samples) {
@@ -57,16 +57,16 @@ auto Audio::process() -> void {
}
}
for(auto c : range(channels)) {
samples[c] = max(-1.0, min(+1.0, samples[c] * volume));
for(auto c : range(_channels)) {
samples[c] = max(-1.0, min(+1.0, samples[c] * _volume));
}
if(channels == 2) {
if(balance < 0.0) samples[1] *= 1.0 + balance;
if(balance > 0.0) samples[0] *= 1.0 - balance;
if(_channels == 2) {
if(_balance < 0.0) samples[1] *= 1.0 + _balance;
if(_balance > 0.0) samples[0] *= 1.0 - _balance;
}
platform->audioFrame(samples, channels);
platform->audioFrame(samples, _channels);
}
}

View File

@@ -17,6 +17,11 @@ struct Audio {
~Audio();
auto reset(Interface* interface) -> void;
inline auto channels() const -> uint { return _channels; }
inline auto frequency() const -> double { return _frequency; }
inline auto volume() const -> double { return _volume; }
inline auto balance() const -> double { return _balance; }
auto setFrequency(double frequency) -> void;
auto setVolume(double volume) -> void;
auto setBalance(double balance) -> void;
@@ -26,14 +31,14 @@ struct Audio {
private:
auto process() -> void;
Interface* interface = nullptr;
vector<shared_pointer<Stream>> streams;
Interface* _interface = nullptr;
vector<shared_pointer<Stream>> _streams;
uint channels = 0;
double frequency = 48000.0;
uint _channels = 0;
double _frequency = 48000.0;
double volume = 1.0;
double balance = 0.0;
double _volume = 1.0;
double _balance = 0.0;
friend class Stream;
};

View File

@@ -1,91 +0,0 @@
#pragma once
struct BitsCaptureLo{};
struct BitsCaptureHi{};
struct BitsLo {
const uint lo;
inline BitsLo(uint lo) : lo(lo) {}
};
struct BitsHi {
const uint hi;
inline BitsHi(uint hi) : hi(hi) {}
};
struct BitsRange {
const uint lo, hi;
inline BitsRange(uint lo, uint hi) : lo(lo), hi(hi) {}
};
inline auto operator*(BitsCaptureLo, uint lhs) { return BitsLo{lhs}; }
inline auto operator*(uint rhs, BitsCaptureHi) { return BitsHi{rhs}; }
inline auto operator-(const BitsLo& lhs, const BitsHi& rhs) { return BitsRange{lhs.lo, rhs.hi}; }
template<typename T> struct Bits {
T& value;
using type =
conditional_t<sizeof(T) <= 1, uint8_t,
conditional_t<sizeof(T) <= 2, uint16_t,
conditional_t<sizeof(T) <= 4, uint32_t,
conditional_t<sizeof(T) <= 8, uint64_t,
void>>>>;
const uint lo;
const type mask;
inline Bits(T& value, BitsRange range) : value(value), lo(range.lo), mask(~0ull >> 64 - (range.hi - range.lo + 1) << range.lo) {}
inline Bits(const Bits& source) { operator=((uint64_t)source); }
inline auto operator=(const Bits& source) -> Bits& { return operator=((uint64_t)source); }
inline operator type() const {
return (value & mask) >> lo;
}
template<typename U> inline auto operator=(U source) -> Bits& {
value = value & ~mask | source << lo & mask;
return *this;
}
template<typename U> inline auto operator&=(U source) -> Bits& {
value = value & (~mask | source << lo & mask);
return *this;
}
template<typename U> inline auto operator^=(U source) -> Bits& {
value = value ^ source << lo & mask;
return *this;
}
template<typename U> inline auto operator|=(U source) -> Bits& {
value = value | source << lo & mask;
return *this;
}
};
template<typename T> struct Bits<const T> {
T value;
using type =
conditional_t<sizeof(T) <= 1, uint8_t,
conditional_t<sizeof(T) <= 2, uint16_t,
conditional_t<sizeof(T) <= 4, uint32_t,
conditional_t<sizeof(T) <= 8, uint64_t,
void>>>>;
const uint lo;
const type mask;
inline Bits(const T& value, BitsRange range) : value(value), lo(range.lo), mask(~0ull >> 64 - (range.hi - range.lo + 1) << range.lo) {}
Bits(const Bits& source) = delete;
auto operator=(const Bits& source) -> Bits& = delete;
inline operator type() const {
return (value & mask) >> lo;
}
};
#define bits(value,range) Bits<decltype(value)>{value,BitsCaptureLo{}*range*BitsCaptureHi{}}
#define bit1(value,index) Bits<decltype(value)>{value,BitsRange{(uint)index,(uint)index}}
#define bit8(value,index) Bits<decltype(value)>{value,BitsRange{(uint)index*8,(uint)index*8+7}}
#define cbits(value,range) Bits<const decltype(value)>{value,BitsCaptureLo{}*range*BitsCaptureHi{}}
#define cbit1(value,index) Bits<const decltype(value)>{value,BitsRange{(uint)index,(uint)index}}
#define cbit8(value,index) Bits<const decltype(value)>{value,BitsRange{(uint)index*8,(uint)index*8+7}}

View File

@@ -3,7 +3,6 @@
#include <nall/platform.hpp>
#include <nall/adaptive-array.hpp>
#include <nall/any.hpp>
#include <nall/bit-field.hpp>
#include <nall/chrono.hpp>
#include <nall/dl.hpp>
#include <nall/endian.hpp>
@@ -23,7 +22,6 @@ using namespace nall;
#include <libco/libco.h>
#include <emulator/bits.hpp>
#include <emulator/types.hpp>
#include <emulator/memory/readable.hpp>
#include <emulator/memory/writable.hpp>
@@ -31,13 +29,13 @@ using namespace nall;
namespace Emulator {
static const string Name = "bsnes";
static const string Version = "108";
static const string Version = "110";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "https://byuu.org";
//incremented only when serialization format changes
static const string SerializerVersion = "108";
static const string SerializerVersion = "110";
namespace Constants {
namespace Colorburst {

View File

@@ -37,6 +37,7 @@ struct Game {
string sha256;
string label;
string name;
string title;
string region;
string revision;
string board;
@@ -50,6 +51,7 @@ auto Game::load(string_view text) -> void {
sha256 = document["game/sha256"].text();
label = document["game/label"].text();
name = document["game/name"].text();
title = document["game/title"].text();
region = document["game/region"].text();
revision = document["game/revision"].text();
board = document["game/board"].text();

View File

@@ -61,6 +61,7 @@ struct Interface {
virtual auto hashes() -> vector<string> { return {}; }
virtual auto manifests() -> vector<string> { return {}; }
virtual auto titles() -> vector<string> { return {}; }
virtual auto title() -> string { return {}; }
virtual auto load() -> bool { return false; }
virtual auto save() -> void {}
virtual auto unload() -> void {}

View File

@@ -24,14 +24,14 @@ struct Readable {
memory::fill<T>(self.data, self.mask + 1, fill);
}
inline auto load(vfs::shared::file fp) -> void {
inline auto load(shared_pointer<vfs::file> fp) -> void {
fp->read(self.data, min(fp->size(), self.size * sizeof(T)));
for(uint address = self.size; address <= self.mask; address++) {
self.data[address] = self.data[mirror(address, self.size)];
}
}
inline auto save(vfs::shared::file fp) -> void {
inline auto save(shared_pointer<vfs::file> fp) -> void {
fp->write(self.data, self.size * sizeof(T));
}

View File

@@ -24,14 +24,14 @@ struct Writable {
memory::fill<T>(self.data, self.mask + 1, fill);
}
inline auto load(vfs::shared::file fp) -> void {
inline auto load(shared_pointer<vfs::file> fp) -> void {
fp->read(self.data, min(fp->size(), self.size * sizeof(T)));
for(uint address = self.size; address <= self.mask; address++) {
self.data[address] = self.data[mirror(address, self.size)];
}
}
inline auto save(vfs::shared::file fp) -> void {
inline auto save(shared_pointer<vfs::file> fp) -> void {
fp->write(self.data, self.size * sizeof(T));
}

View File

@@ -14,7 +14,7 @@ struct Platform {
};
virtual auto path(uint id) -> string { return ""; }
virtual auto open(uint id, string name, vfs::file::mode mode, bool required = false) -> vfs::shared::file { return {}; }
virtual auto open(uint id, string name, vfs::file::mode mode, bool required = false) -> shared_pointer<vfs::file> { return {}; }
virtual auto load(uint id, string name, string type, vector<string> options = {}) -> Load { return {}; }
virtual auto videoFrame(const uint16* data, uint pitch, uint width, uint height, uint scale) -> void {}
virtual auto audioFrame(const float* samples, uint channels) -> void {}

View File

@@ -65,10 +65,10 @@ struct Random {
if((random() & 1) == 0) hivalue = ~lovalue;
for(uint32 address : range(size)) {
uint8 value = bit1(address,lobit) ? lovalue : hivalue;
if(bit1(address,hibit)) value = ~value;
if((random() & 511) == 0) bit1(value,random() & 7) ^= 1;
if((random() & 2047) == 0) bit1(value,random() & 7) ^= 1;
uint8 value = (address & 1ull << lobit) ? lovalue : hivalue;
if((address & 1ull << hibit)) value = ~value;
if((random() & 511) == 0) value ^= 1 << (random() & 7);
if((random() & 2047) == 0) value ^= 1 << (random() & 7);
data[address] = value;
}
}

View File

@@ -1,98 +0,0 @@
#pragma once
#define uintmax uint64
namespace Emulator {
struct Scheduler {
enum class Mode : uint {
Run,
SynchronizeMaster,
SynchronizeSlave,
};
enum class Event : uint {
Step,
Frame,
Synchronize,
};
inline auto synchronizing() const -> bool { return _mode == Mode::SynchronizeSlave; }
auto reset() -> void {
_host = co_active();
_threads.reset();
}
auto primary(Thread& thread) -> void {
_master = _resume = thread.handle();
uintmax clock = 0;
for(auto& thread : _threads) {
thread->_clock = clock++; //this bias prioritizes threads appended earlier first
}
}
auto append(Thread& thread) -> bool {
if(_threads.find(&thread)) return false;
thread._clock = _threads.size();
return _threads.append(&thread), true;
}
auto remove(Thread& thread) -> bool {
if(auto offset = _threads.find(&thread)) return _threads.remove(*offset), true;
return false;
}
auto enter(Mode mode = Mode::Run) -> Event {
_mode = mode;
_host = co_active();
co_switch(_resume);
return _event;
}
inline auto resume(Thread& thread) -> void {
if(_mode != Mode::SynchronizeSlave) co_switch(thread.handle());
}
auto exit(Event event) -> void {
uintmax minimum = -1;
for(auto thread : _threads) {
if(thread->_clock < minimum) minimum = thread->_clock;
}
for(auto thread : _threads) {
thread->_clock -= minimum;
}
_event = event;
_resume = co_active();
co_switch(_host);
}
inline auto synchronize(Thread& thread) -> void {
if(thread.handle() == _master) {
while(enter(Mode::SynchronizeMaster) != Event::Synchronize);
} else {
_resume = thread.handle();
while(enter(Mode::SynchronizeSlave) != Event::Synchronize);
}
}
inline auto synchronize() -> void {
if(co_active() == _master) {
if(_mode == Mode::SynchronizeMaster) return exit(Event::Synchronize);
} else {
if(_mode == Mode::SynchronizeSlave) return exit(Event::Synchronize);
}
}
private:
cothread_t _host = nullptr; //program thread (used to exit scheduler)
cothread_t _resume = nullptr; //resume thread (used to enter scheduler)
cothread_t _master = nullptr; //primary thread (used to synchronize components)
Mode _mode = Mode::Run;
Event _event = Event::Step;
vector<Thread*> _threads;
};
}
#undef uintmax

View File

@@ -1,64 +0,0 @@
#pragma once
#define uintmax uint64
namespace Emulator {
struct Thread {
enum : uintmax { Second = (uintmax)-1 >> 1 };
virtual ~Thread() {
if(_handle) co_delete(_handle);
}
inline auto active() const { return co_active() == _handle; }
inline auto handle() const { return _handle; }
inline auto frequency() const { return _frequency; }
inline auto scalar() const { return _scalar; }
inline auto clock() const { return _clock; }
auto setHandle(cothread_t handle) -> void {
_handle = handle;
}
auto setFrequency(double frequency) -> void {
_frequency = frequency + 0.5;
_scalar = Second / _frequency;
}
auto setScalar(uintmax scalar) -> void {
_scalar = scalar;
}
auto setClock(uintmax clock) -> void {
_clock = clock;
}
auto create(auto (*entrypoint)() -> void, double frequency) -> void {
if(_handle) co_delete(_handle);
_handle = co_create(64 * 1024 * sizeof(void*), entrypoint);
setFrequency(frequency);
setClock(0);
}
inline auto step(uint clocks) -> void {
_clock += _scalar * clocks;
}
auto serialize(serializer& s) -> void {
s.integer(_frequency);
s.integer(_scalar);
s.integer(_clock);
}
protected:
cothread_t _handle = nullptr;
uintmax _frequency = 0;
uintmax _scalar = 0;
uintmax _clock = 0;
friend class Scheduler;
};
}
#undef uintmax

View File

@@ -8,7 +8,6 @@ using int5 = nall::Integer< 5>;
using int6 = nall::Integer< 6>;
using int7 = nall::Integer< 7>;
using int8 = int8_t;
//using int8 = nall::Integer< 8>;
using int9 = nall::Integer< 9>;
using int10 = nall::Integer<10>;
using int11 = nall::Integer<11>;
@@ -17,7 +16,6 @@ using int13 = nall::Integer<13>;
using int14 = nall::Integer<14>;
using int15 = nall::Integer<15>;
using int16 = int16_t;
//using int16 = nall::Integer<16>;
using int17 = nall::Integer<17>;
using int18 = nall::Integer<18>;
using int19 = nall::Integer<19>;
@@ -34,40 +32,8 @@ using int29 = nall::Integer<29>;
using int30 = nall::Integer<30>;
using int31 = nall::Integer<31>;
using int32 = int32_t;
//using int32 = nall::Integer<32>;
using int33 = nall::Integer<33>;
using int34 = nall::Integer<34>;
using int35 = nall::Integer<35>;
using int36 = nall::Integer<36>;
using int37 = nall::Integer<37>;
using int38 = nall::Integer<38>;
using int39 = nall::Integer<39>;
using int40 = nall::Integer<40>;
using int41 = nall::Integer<41>;
using int42 = nall::Integer<42>;
using int43 = nall::Integer<43>;
using int44 = nall::Integer<44>;
using int45 = nall::Integer<45>;
using int46 = nall::Integer<46>;
using int47 = nall::Integer<47>;
using int48 = nall::Integer<48>;
using int49 = nall::Integer<49>;
using int50 = nall::Integer<50>;
using int51 = nall::Integer<51>;
using int52 = nall::Integer<52>;
using int53 = nall::Integer<53>;
using int54 = nall::Integer<54>;
using int55 = nall::Integer<55>;
using int56 = nall::Integer<56>;
using int57 = nall::Integer<57>;
using int58 = nall::Integer<58>;
using int59 = nall::Integer<59>;
using int60 = nall::Integer<60>;
using int61 = nall::Integer<61>;
using int62 = nall::Integer<62>;
using int63 = nall::Integer<63>;
using int48 = nall::Integer<48>; //Cx4
using int64 = int64_t;
//using int64 = nall::Integer<64>;
using uint1 = nall::Natural< 1>;
using uint2 = nall::Natural< 2>;
@@ -77,7 +43,6 @@ using uint5 = nall::Natural< 5>;
using uint6 = nall::Natural< 6>;
using uint7 = nall::Natural< 7>;
using uint8 = uint8_t;
//using uint8 = nall::Natural< 8>;
using uint9 = nall::Natural< 9>;
using uint10 = nall::Natural<10>;
using uint11 = nall::Natural<11>;
@@ -86,7 +51,6 @@ using uint13 = nall::Natural<13>;
using uint14 = nall::Natural<14>;
using uint15 = nall::Natural<15>;
using uint16 = uint16_t;
//using uint16 = nall::Natural<16>;
using uint17 = nall::Natural<17>;
using uint18 = nall::Natural<18>;
using uint19 = nall::Natural<19>;
@@ -103,37 +67,6 @@ using uint29 = nall::Natural<29>;
using uint30 = nall::Natural<30>;
using uint31 = nall::Natural<31>;
using uint32 = uint32_t;
//using uint32 = nall::Natural<32>;
using uint33 = nall::Natural<33>;
using uint34 = nall::Natural<34>;
using uint35 = nall::Natural<35>;
using uint36 = nall::Natural<36>;
using uint37 = nall::Natural<37>;
using uint38 = nall::Natural<38>;
using uint39 = nall::Natural<39>;
using uint40 = nall::Natural<40>;
using uint41 = nall::Natural<41>;
using uint42 = nall::Natural<42>;
using uint43 = nall::Natural<43>;
using uint44 = nall::Natural<44>;
using uint45 = nall::Natural<45>;
using uint46 = nall::Natural<46>;
using uint47 = nall::Natural<47>;
using uint48 = nall::Natural<48>;
using uint49 = nall::Natural<49>;
using uint50 = nall::Natural<50>;
using uint51 = nall::Natural<51>;
using uint52 = nall::Natural<52>;
using uint53 = nall::Natural<53>;
using uint54 = nall::Natural<54>;
using uint55 = nall::Natural<55>;
using uint56 = nall::Natural<56>;
using uint57 = nall::Natural<57>;
using uint58 = nall::Natural<58>;
using uint59 = nall::Natural<59>;
using uint60 = nall::Natural<60>;
using uint61 = nall::Natural<61>;
using uint62 = nall::Natural<62>;
using uint63 = nall::Natural<63>;
using uint40 = nall::Natural<40>; //SA1
using uint48 = nall::Natural<48>; //Cx4
using uint64 = uint64_t;
//using uint64 = nall::Natural<64>;

View File

@@ -310,6 +310,7 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
(gb->apu.is_active[GB_WAVE] ? (gb->apu.samples[GB_WAVE]) : 0);
case GB_IO_JOYP:
GB_timing_sync(gb);
return gb->io_registers[addr & 0xFF] | 0xC0;
case GB_IO_TMA:
case GB_IO_LCDC:
case GB_IO_SCY:

View File

@@ -6,6 +6,7 @@ struct SuperFamicom {
auto manifest() const -> string;
auto region() const -> string;
auto videoRegion() const -> string;
auto revision() const -> string;
auto board() const -> string;
auto title() const -> string;
@@ -139,6 +140,11 @@ auto SuperFamicom::manifest() const -> string {
}
auto SuperFamicom::region() const -> string {
//Unlicensed software (homebrew, ROM hacks, etc) often change the standard region code,
//and then neglect to change the extended header region code. Thanks to that, we can't
//decode and display the full game serial + region code.
return videoRegion();
string region;
char A = data[headerAddress + 0x02]; //game type
@@ -186,6 +192,11 @@ auto SuperFamicom::region() const -> string {
return region ? region : "NTSC";
}
auto SuperFamicom::videoRegion() const -> string {
auto region = data[headerAddress + 0x29];
return (region <= 0x01 || region >= 0x12) ? "NTSC" : "PAL";
}
auto SuperFamicom::revision() const -> string {
string revision;

View File

@@ -1,11 +1,11 @@
auto ARM7TDMI::ADD(uint32 source, uint32 modify, bool carry) -> uint32 {
uint32 result = source + modify + carry;
if(cpsr().t || bit1(opcode,20)) {
if(cpsr().t || (opcode & 1 << 20)) {
uint32 overflow = ~(source ^ modify) & (source ^ result);
cpsr().v = 1 << 31 & (overflow);
cpsr().c = 1 << 31 & (overflow ^ source ^ modify ^ result);
cpsr().z = result == 0;
cpsr().n = bit1(result,31);
cpsr().n = result >> 31;
}
return result;
}
@@ -19,10 +19,10 @@ auto ARM7TDMI::ASR(uint32 source, uint8 shift) -> uint32 {
}
auto ARM7TDMI::BIT(uint32 result) -> uint32 {
if(cpsr().t || bit1(opcode,20)) {
if(cpsr().t || (opcode & 1 << 20)) {
cpsr().c = carry;
cpsr().z = result == 0;
cpsr().n = bit1(result,31);
cpsr().n = result >> 31;
}
return result;
}
@@ -49,9 +49,9 @@ auto ARM7TDMI::MUL(uint32 product, uint32 multiplicand, uint32 multiplier) -> ui
if(multiplier >> 16 && multiplier >> 16 != 0xffff) idle();
if(multiplier >> 24 && multiplier >> 24 != 0xff) idle();
product += multiplicand * multiplier;
if(cpsr().t || bit1(opcode,20)) {
if(cpsr().t || (opcode & 1 << 20)) {
cpsr().z = product == 0;
cpsr().n = bit1(product,31);
cpsr().n = product >> 31;
}
return product;
}

View File

@@ -148,14 +148,14 @@ struct ARM7TDMI {
}
inline auto operator=(uint32 data) -> PSR& {
m = bits(data,0-4);
t = bit1(data,5);
f = bit1(data,6);
i = bit1(data,7);
v = bit1(data,28);
c = bit1(data,29);
z = bit1(data,30);
n = bit1(data,31);
m = data >> 0 & 31;
t = data >> 5 & 1;
f = data >> 6 & 1;
i = data >> 7 & 1;
v = data >> 28 & 1;
c = data >> 29 & 1;
z = data >> 30 & 1;
n = data >> 31 & 1;
return *this;
}

View File

@@ -186,7 +186,7 @@ auto ARM7TDMI::armDisassembleMoveMultiple
(uint16 list, uint4 n, uint1 mode, uint1 writeback, uint1 type, uint1 up, uint1 pre) -> string {
string registers;
for(auto index : range(16)) {
if(bit1(list,index)) registers.append(_r[index], ",");
if((list & 1 << index)) registers.append(_r[index], ",");
}
registers.trimRight(",", 1L);
return {mode ? "ldm" : "stm", _c,
@@ -358,7 +358,7 @@ auto ARM7TDMI::thumbDisassembleMoveMultiple
(uint8 list, uint3 n, uint1 mode) -> string {
string registers;
for(uint m : range(8)) {
if(bit1(list,m)) registers.append(_r[m], ",");
if((list & 1 << m)) registers.append(_r[m], ",");
}
registers.trimRight(",", 1L);
return {mode ? "ldmia" : "stmia", " ", _r[n], "!,{", registers, "}"};
@@ -395,7 +395,7 @@ auto ARM7TDMI::thumbDisassembleStackMultiple
(uint8 list, uint1 lrpc, uint1 mode) -> string {
string registers;
for(uint m : range(8)) {
if(bit1(list,m)) registers.append(_r[m], ",");
if((list & 1 << m)) registers.append(_r[m], ",");
}
if(lrpc) registers.append(!mode ? "lr," : "pc,");
registers.trimRight(",", 1L);

View File

@@ -68,8 +68,11 @@ auto ARM7TDMI::armInitialize() -> void {
#define pattern(s) \
std::integral_constant<uint32_t, bit::test(s)>::value
#define bit1(value, index) (value >> index & 1)
#define bits(value, lo, hi) (value >> lo & (1ull << (hi - lo + 1)) - 1)
#define arguments \
bits(opcode, 0-23), /* displacement */ \
bits(opcode, 0,23), /* displacement */ \
bit1(opcode,24) /* link */
for(uint4 displacementLo : range(16))
for(uint4 displacementHi : range(16))
@@ -81,7 +84,7 @@ auto ARM7TDMI::armInitialize() -> void {
#undef arguments
#define arguments \
bits(opcode, 0- 3) /* m */
bits(opcode, 0, 3) /* m */
{
auto opcode = pattern(".... 0001 0010 ---- ---- ---- 0001 ????");
bind(opcode, BranchExchangeRegister);
@@ -89,12 +92,12 @@ auto ARM7TDMI::armInitialize() -> void {
#undef arguments
#define arguments \
bits(opcode, 0- 7), /* immediate */ \
bits(opcode, 8-11), /* shift */ \
bits(opcode,12-15), /* d */ \
bits(opcode,16-19), /* n */ \
bits(opcode, 0, 7), /* immediate */ \
bits(opcode, 8,11), /* shift */ \
bits(opcode,12,15), /* d */ \
bits(opcode,16,19), /* n */ \
bit1(opcode,20), /* save */ \
bits(opcode,21-24) /* mode */
bits(opcode,21,24) /* mode */
for(uint4 shiftHi : range(16))
for(uint1 save : range(2))
for(uint4 mode : range(16)) {
@@ -105,13 +108,13 @@ auto ARM7TDMI::armInitialize() -> void {
#undef arguments
#define arguments \
bits(opcode, 0- 3), /* m */ \
bits(opcode, 5- 6), /* type */ \
bits(opcode, 7-11), /* shift */ \
bits(opcode,12-15), /* d */ \
bits(opcode,16-19), /* n */ \
bits(opcode, 0, 3), /* m */ \
bits(opcode, 5, 6), /* type */ \
bits(opcode, 7,11), /* shift */ \
bits(opcode,12,15), /* d */ \
bits(opcode,16,19), /* n */ \
bit1(opcode,20), /* save */ \
bits(opcode,21-24) /* mode */
bits(opcode,21,24) /* mode */
for(uint2 type : range(4))
for(uint1 shiftLo : range(2))
for(uint1 save : range(2))
@@ -123,13 +126,13 @@ auto ARM7TDMI::armInitialize() -> void {
#undef arguments
#define arguments \
bits(opcode, 0- 3), /* m */ \
bits(opcode, 5- 6), /* type */ \
bits(opcode, 8-11), /* s */ \
bits(opcode,12-15), /* d */ \
bits(opcode,16-19), /* n */ \
bits(opcode, 0, 3), /* m */ \
bits(opcode, 5, 6), /* type */ \
bits(opcode, 8,11), /* s */ \
bits(opcode,12,15), /* d */ \
bits(opcode,16,19), /* n */ \
bit1(opcode,20), /* save */ \
bits(opcode,21-24) /* mode */
bits(opcode,21,24) /* mode */
for(uint2 type : range(4))
for(uint1 save : range(2))
for(uint4 mode : range(16)) {
@@ -140,10 +143,10 @@ auto ARM7TDMI::armInitialize() -> void {
#undef arguments
#define arguments \
bits(opcode, 0- 3) << 0 | bits(opcode, 8-11) << 4, /* immediate */ \
bits(opcode, 0, 3) << 0 | bits(opcode, 8,11) << 4, /* immediate */ \
bit1(opcode, 5), /* half */ \
bits(opcode,12-15), /* d */ \
bits(opcode,16-19), /* n */ \
bits(opcode,12,15), /* d */ \
bits(opcode,16,19), /* n */ \
bit1(opcode,21), /* writeback */ \
bit1(opcode,23), /* up */ \
bit1(opcode,24) /* pre */
@@ -157,10 +160,10 @@ auto ARM7TDMI::armInitialize() -> void {
#undef arguments
#define arguments \
bits(opcode, 0- 3), /* m */ \
bits(opcode, 0, 3), /* m */ \
bit1(opcode, 5), /* half */ \
bits(opcode,12-15), /* d */ \
bits(opcode,16-19), /* n */ \
bits(opcode,12,15), /* d */ \
bits(opcode,16,19), /* n */ \
bit1(opcode,21), /* writeback */ \
bit1(opcode,23), /* up */ \
bit1(opcode,24) /* pre */
@@ -174,9 +177,9 @@ auto ARM7TDMI::armInitialize() -> void {
#undef arguments
#define arguments \
bits(opcode, 0- 3), /* m */ \
bits(opcode,12-15), /* d */ \
bits(opcode,16-19), /* n */ \
bits(opcode, 0, 3), /* m */ \
bits(opcode,12,15), /* d */ \
bits(opcode,16,19), /* n */ \
bit1(opcode,22) /* byte */
for(uint1 byte : range(2)) {
auto opcode = pattern(".... 0001 0?00 ???? ???? ---- 1001 ????") | byte << 22;
@@ -185,9 +188,9 @@ auto ARM7TDMI::armInitialize() -> void {
#undef arguments
#define arguments \
bits(opcode, 0- 3) << 0 | bits(opcode, 8-11) << 4, /* immediate */ \
bits(opcode,12-15), /* d */ \
bits(opcode,16-19), /* n */ \
bits(opcode, 0, 3) << 0 | bits(opcode, 8,11) << 4, /* immediate */ \
bits(opcode,12,15), /* d */ \
bits(opcode,16,19), /* n */ \
bit1(opcode,20), /* mode */ \
bit1(opcode,21), /* writeback */ \
bit1(opcode,23), /* up */ \
@@ -202,9 +205,9 @@ auto ARM7TDMI::armInitialize() -> void {
#undef arguments
#define arguments \
bits(opcode, 0- 3), /* m */ \
bits(opcode,12-15), /* d */ \
bits(opcode,16-19), /* n */ \
bits(opcode, 0, 3), /* m */ \
bits(opcode,12,15), /* d */ \
bits(opcode,16,19), /* n */ \
bit1(opcode,20), /* mode */ \
bit1(opcode,21), /* writeback */ \
bit1(opcode,23), /* up */ \
@@ -219,9 +222,9 @@ auto ARM7TDMI::armInitialize() -> void {
#undef arguments
#define arguments \
bits(opcode, 0-11), /* immediate */ \
bits(opcode,12-15), /* d */ \
bits(opcode,16-19), /* n */ \
bits(opcode, 0,11), /* immediate */ \
bits(opcode,12,15), /* d */ \
bits(opcode,16,19), /* n */ \
bit1(opcode,20), /* mode */ \
bit1(opcode,21), /* writeback */ \
bit1(opcode,22), /* byte */ \
@@ -240,8 +243,8 @@ auto ARM7TDMI::armInitialize() -> void {
#undef arguments
#define arguments \
bits(opcode, 0-15), /* list */ \
bits(opcode,16-19), /* n */ \
bits(opcode, 0,15), /* list */ \
bits(opcode,16,19), /* n */ \
bit1(opcode,20), /* mode */ \
bit1(opcode,21), /* writeback */ \
bit1(opcode,22), /* type */ \
@@ -260,11 +263,11 @@ auto ARM7TDMI::armInitialize() -> void {
#undef arguments
#define arguments \
bits(opcode, 0- 3), /* m */ \
bits(opcode, 5- 6), /* type */ \
bits(opcode, 7-11), /* shift */ \
bits(opcode,12-15), /* d */ \
bits(opcode,16-19), /* n */ \
bits(opcode, 0, 3), /* m */ \
bits(opcode, 5, 6), /* type */ \
bits(opcode, 7,11), /* shift */ \
bits(opcode,12,15), /* d */ \
bits(opcode,16,19), /* n */ \
bit1(opcode,20), /* mode */ \
bit1(opcode,21), /* writeback */ \
bit1(opcode,22), /* byte */ \
@@ -284,7 +287,7 @@ auto ARM7TDMI::armInitialize() -> void {
#undef arguments
#define arguments \
bits(opcode,12-15), /* d */ \
bits(opcode,12,15), /* d */ \
bit1(opcode,22) /* mode */
for(uint1 mode : range(2)) {
auto opcode = pattern(".... 0001 0?00 ---- ???? ---- 0000 ----") | mode << 22;
@@ -293,9 +296,9 @@ auto ARM7TDMI::armInitialize() -> void {
#undef arguments
#define arguments \
bits(opcode, 0- 7), /* immediate */ \
bits(opcode, 8-11), /* rotate */ \
bits(opcode,16-19), /* field */ \
bits(opcode, 0, 7), /* immediate */ \
bits(opcode, 8,11), /* rotate */ \
bits(opcode,16,19), /* field */ \
bit1(opcode,22) /* mode */
for(uint4 immediateHi : range(16))
for(uint1 mode : range(2)) {
@@ -305,8 +308,8 @@ auto ARM7TDMI::armInitialize() -> void {
#undef arguments
#define arguments \
bits(opcode, 0- 3), /* m */ \
bits(opcode,16-19), /* field */ \
bits(opcode, 0, 3), /* m */ \
bits(opcode,16,19), /* field */ \
bit1(opcode,22) /* mode */
for(uint1 mode : range(2)) {
auto opcode = pattern(".... 0001 0?10 ???? ---- ---- 0000 ????") | mode << 22;
@@ -315,10 +318,10 @@ auto ARM7TDMI::armInitialize() -> void {
#undef arguments
#define arguments \
bits(opcode, 0- 3), /* m */ \
bits(opcode, 8-11), /* s */ \
bits(opcode,12-15), /* n */ \
bits(opcode,16-19), /* d */ \
bits(opcode, 0, 3), /* m */ \
bits(opcode, 8,11), /* s */ \
bits(opcode,12,15), /* n */ \
bits(opcode,16,19), /* d */ \
bit1(opcode,20), /* save */ \
bit1(opcode,21) /* accumulate */
for(uint1 save : range(2))
@@ -329,10 +332,10 @@ auto ARM7TDMI::armInitialize() -> void {
#undef arguments
#define arguments \
bits(opcode, 0- 3), /* m */ \
bits(opcode, 8-11), /* s */ \
bits(opcode,12-15), /* l */ \
bits(opcode,16-19), /* h */ \
bits(opcode, 0, 3), /* m */ \
bits(opcode, 8,11), /* s */ \
bits(opcode,12,15), /* l */ \
bits(opcode,16,19), /* h */ \
bit1(opcode,20), /* save */ \
bit1(opcode,21), /* accumulate */ \
bit1(opcode,22) /* sign */
@@ -345,7 +348,7 @@ auto ARM7TDMI::armInitialize() -> void {
#undef arguments
#define arguments \
bits(opcode, 0-23) /* immediate */
bits(opcode, 0,23) /* immediate */
for(uint4 immediateLo : range(16))
for(uint4 immediateHi : range(16)) {
auto opcode = pattern(".... 1111 ???? ???? ???? ???? ???? ????") | immediateLo << 4 | immediateHi << 20;
@@ -356,7 +359,7 @@ auto ARM7TDMI::armInitialize() -> void {
#define arguments
for(uint12 id : range(4096)) {
if(armInstruction[id]) continue;
auto opcode = pattern(".... ???? ???? ---- ---- ---- ???? ----") | bits(id,0-3) << 4 | bits(id,4-11) << 20;
auto opcode = pattern(".... ???? ???? ---- ---- ---- ???? ----") | bits(id,0,3) << 4 | bits(id,4,11) << 20;
bind(opcode, Undefined);
}
#undef arguments
@@ -386,7 +389,7 @@ auto ARM7TDMI::thumbInitialize() -> void {
for(uint4 m : range(16))
for(uint2 mode : range(4)) {
if(mode == 3) continue;
auto opcode = pattern("0100 01?? ???? ????") | bits(d,0-2) << 0 | m << 3 | bit1(d,3) << 7 | mode << 8;
auto opcode = pattern("0100 01?? ???? ????") | bits(d,0,2) << 0 | m << 3 | bit1(d,3) << 7 | mode << 8;
bind(opcode, ALUExtended, d, m, mode);
}
@@ -533,6 +536,9 @@ auto ARM7TDMI::thumbInitialize() -> void {
bind(opcode, Undefined);
}
#undef bit1
#undef bits
#undef bind
#undef pattern
}

View File

@@ -20,7 +20,7 @@ auto ARM7TDMI::armALU(uint4 mode, uint4 d, uint4 n, uint32 rm) -> void {
case 15: r(d) = BIT(~rm); break; //MVN
}
if(exception() && d == 15 && bit1(opcode,20)) {
if(exception() && d == 15 && (opcode & 1 << 20)) {
cpsr() = spsr();
}
}
@@ -29,21 +29,21 @@ auto ARM7TDMI::armMoveToStatus(uint4 field, uint1 mode, uint32 data) -> void {
if(mode && (cpsr().m == PSR::USR || cpsr().m == PSR::SYS)) return;
PSR& psr = mode ? spsr() : cpsr();
if(field.bit(0)) {
if(field & 1) {
if(mode || privileged()) {
psr.m = bits(data,0-4);
psr.t = bit1(data,5);
psr.f = bit1(data,6);
psr.i = bit1(data,7);
psr.m = data >> 0 & 31;
psr.t = data >> 5 & 1;
psr.f = data >> 6 & 1;
psr.i = data >> 7 & 1;
if(!mode && psr.t) r(15).data += 2;
}
}
if(field.bit(3)) {
psr.v = bit1(data,28);
psr.c = bit1(data,29);
psr.z = bit1(data,30);
psr.n = bit1(data,31);
if(field & 8) {
psr.v = data >> 28 & 1;
psr.c = data >> 29 & 1;
psr.z = data >> 30 & 1;
psr.n = data >> 31 & 1;
}
}
@@ -193,13 +193,13 @@ auto ARM7TDMI::armInstructionMoveMultiple
auto cpsrMode = cpsr().m;
bool usr = false;
if(type && mode == 1 && !bit1(list,15)) usr = true;
if(type && mode == 1 && !(list & 0x8000)) usr = true;
if(type && mode == 0) usr = true;
if(usr) cpsr().m = PSR::USR;
uint sequential = Nonsequential;
for(uint m : range(16)) {
if(!bit1(list,m)) continue;
if(!(list & 1 << m)) continue;
if(mode == 1) r(m) = read(Word | sequential, rn);
if(mode == 0) write(Word | sequential, rn, r(m));
rn += 4;
@@ -210,7 +210,7 @@ auto ARM7TDMI::armInstructionMoveMultiple
if(mode) {
idle();
if(type && bit1(list,15) && cpsr().m != PSR::USR && cpsr().m != PSR::SYS) {
if(type && (list & 0x8000) && cpsr().m != PSR::USR && cpsr().m != PSR::SYS) {
cpsr() = spsr();
}
} else {
@@ -299,7 +299,7 @@ auto ARM7TDMI::armInstructionMultiplyLong
if(save) {
cpsr().z = rd == 0;
cpsr().n = bit1(rd,63);
cpsr().n = rd >> 63 & 1;
}
}

View File

@@ -127,7 +127,7 @@ auto ARM7TDMI::thumbInstructionMoveMultiple
uint32 rn = r(n);
for(uint m : range(8)) {
if(!bit1(list,m)) continue;
if(!(list & 1 << m)) continue;
switch(mode) {
case 0: write(Word | Nonsequential, rn, r(m)); break; //STMIA
case 1: r(m) = read(Word | Nonsequential, rn); break; //LDMIA
@@ -135,7 +135,7 @@ auto ARM7TDMI::thumbInstructionMoveMultiple
rn += 4;
}
if(mode == 0 || !bit1(list,n)) r(n) = rn;
if(mode == 0 || !(list & 1 << n)) r(n) = rn;
if(mode == 1) idle();
}
@@ -193,7 +193,7 @@ auto ARM7TDMI::thumbInstructionStackMultiple
uint sequential = Nonsequential;
for(uint m : range(8)) {
if(!bit1(list,m)) continue;
if(!(list & 1 << m)) continue;
switch(mode) {
case 0: write(Word | sequential, sp, r(m)); break; //PUSH
case 1: r(m) = read(Word | sequential, sp); break; //POP

View File

@@ -34,22 +34,22 @@ struct Register {
};
struct SFR {
union {
uint16_t data = 0;
BooleanBitField<uint16_t, 15> irq; //interrupt flag
BooleanBitField<uint16_t, 12> b; //with flag
BooleanBitField<uint16_t, 11> ih; //immediate higher 8-bit flag
BooleanBitField<uint16_t, 10> il; //immediate lower 8-bit flag
BooleanBitField<uint16_t, 9> alt2; //alt2 instruction mode
BooleanBitField<uint16_t, 8> alt1; //alt1 instruction mode
BooleanBitField<uint16_t, 6> r; //ROM r14 read flag
BooleanBitField<uint16_t, 5> g; //go flag
BooleanBitField<uint16_t, 4> ov; //overflow flag
BooleanBitField<uint16_t, 3> s; //sign flag
BooleanBitField<uint16_t, 2> cy; //carry flag
BooleanBitField<uint16_t, 1> z; //zero flag
NaturalBitField<uint16_t, 9, 8> alt; //instruction mode (composite flag)
};
uint16_t data = 0;
BitField<16, 1> z {&data}; //zero flag
BitField<16, 2> cy {&data}; //carry flag
BitField<16, 3> s {&data}; //sign flag
BitField<16, 4> ov {&data}; //overflow flag
BitField<16, 5> g {&data}; //go flag
BitField<16, 6> r {&data}; //ROM r14 flag
BitField<16, 8> alt1{&data}; //alt1 instruction mode
BitField<16, 9> alt2{&data}; //alt2 instruction mode
BitField<16,10> il {&data}; //immediate lower 8-bit flag
BitField<16,11> ih {&data}; //immediate upper 8-bit flag
BitField<16,12> b {&data}; //with flag
BitField<16,15> irq {&data}; //interrupt flag
BitRange<16,8,9> alt{&data}; //composite instruction mode
inline operator uint() const { return data & 0x9f7e; }
inline auto& operator=(const uint value) { return data = value, *this; }

View File

@@ -84,8 +84,8 @@ auto HG51B::cache() -> bool {
io.cache.address[io.cache.page] = address;
for(uint offset : range(256)) {
step(wait(address)); bit8(programRAM[io.cache.page][offset],0) = read(address++);
step(wait(address)); bit8(programRAM[io.cache.page][offset],1) = read(address++);
step(wait(address)); programRAM[io.cache.page][offset] = read(address++) << 0;
step(wait(address)); programRAM[io.cache.page][offset] |= read(address++) << 8;
}
return io.cache.enable = 0, true;
}

View File

@@ -203,11 +203,11 @@ auto HG51B::instructionLD(uint15& out, uint8 imm) -> void {
}
auto HG51B::instructionLDL(uint15& out, uint8 imm) -> void {
bits(out,0-7) = imm;
out = out & 0x7f00 | imm << 0;
}
auto HG51B::instructionLDH(uint15& out, uint7 imm) -> void {
bits(out,8-14) = imm;
out = out & 0x00ff | (imm & 0x7f) << 8;
}
auto HG51B::instructionMUL(uint7 reg) -> void {

View File

@@ -1,7 +1,7 @@
auto HG51B::readRegister(uint7 address) -> uint24 {
switch(address) {
case 0x01: return bits(r.mul,24-47);
case 0x02: return bits(r.mul, 0-23);
case 0x01: return r.mul >> 24 & 0xffffff;
case 0x02: return r.mul >> 0 & 0xffffff;
case 0x03: return r.mdr;
case 0x08: return r.rom;
case 0x0c: return r.ram;
@@ -64,8 +64,8 @@ auto HG51B::readRegister(uint7 address) -> uint24 {
auto HG51B::writeRegister(uint7 address, uint24 data) -> void {
switch(address) {
case 0x01: bits(r.mul,24-47) = data; return;
case 0x02: bits(r.mul, 0-23) = data; return;
case 0x01: r.mul = r.mul & 0xffffffull | data << 24; return;
case 0x02: r.mul = r.mul & ~0xffffffull | data << 0; return;
case 0x03: r.mdr = data; return;
case 0x08: r.rom = data; return;
case 0x0c: r.ram = data; return;

View File

@@ -7,32 +7,33 @@ auto SPC700::instructionAbsoluteBitModify(uint3 mode) -> void {
switch(mode) {
case 0: //or addr:bit
idle();
CF |= bit1(data,bit);
CF |= bool(data & 1 << bit);
break;
case 1: //or !addr:bit
idle();
CF |= !bit1(data,bit);
CF |= !bool(data & 1 << bit);
break;
case 2: //and addr:bit
CF &= bit1(data,bit);
CF &= bool(data & 1 << bit);
break;
case 3: //and !addr:bit
CF &= !bit1(data,bit);
CF &= !bool(data & 1 << bit);
break;
case 4: //eor addr:bit
idle();
CF ^= bit1(data,bit);
CF ^= bool(data & 1 << bit);
break;
case 5: //ld addr:bit
CF = bit1(data,bit);
CF = bool(data & 1 << bit);
break;
case 6: //st addr:bit
idle();
bit1(data,bit) = CF;
data &= ~(1 << bit);
data |= CF << bit;
write(address, data);
break;
case 7: //not addr:bit
bit1(data,bit) ^= 1;
data ^= 1 << bit;
write(address, data);
break;
}
@@ -41,7 +42,8 @@ auto SPC700::instructionAbsoluteBitModify(uint3 mode) -> void {
auto SPC700::instructionAbsoluteBitSet(uint3 bit, bool value) -> void {
uint8 address = fetch();
uint8 data = load(address);
bit1(data,bit) = value;
data &= ~(1 << bit);
data |= value << bit;
store(address, data);
}
@@ -95,7 +97,7 @@ auto SPC700::instructionBranchBit(uint3 bit, bool match) -> void {
uint8 data = load(address);
idle();
uint8 displacement = fetch();
if(bit1(data,bit) != match) return;
if(bool(data & 1 << bit) != match) return;
idle();
idle();
PC += (int8)displacement;

View File

@@ -134,14 +134,14 @@ struct SPC700 {
}
inline auto& operator=(uint8 data) {
c = bit1(data,0);
z = bit1(data,1);
i = bit1(data,2);
h = bit1(data,3);
b = bit1(data,4);
p = bit1(data,5);
v = bit1(data,6);
n = bit1(data,7);
c = data & 0x01;
z = data & 0x02;
i = data & 0x04;
h = data & 0x08;
b = data & 0x10;
p = data & 0x20;
v = data & 0x40;
n = data & 0x80;
return *this;
}
};

View File

@@ -37,12 +37,12 @@ struct uPD96050 {
}
inline auto operator=(uint16 data) -> Flag& {
ov0 = bit1(data,0);
ov1 = bit1(data,1);
z = bit1(data,2);
c = bit1(data,3);
s0 = bit1(data,4);
s1 = bit1(data,5);
ov0 = data >> 0 & 1;
ov1 = data >> 1 & 1;
z = data >> 2 & 1;
c = data >> 3 & 1;
s0 = data >> 4 & 1;
s1 = data >> 5 & 1;
return *this;
}
@@ -64,17 +64,17 @@ struct uPD96050 {
}
inline auto operator=(uint16 data) -> Status& {
p0 = bit1(data, 0);
p1 = bit1(data, 1);
ei = bit1(data, 7);
sic = bit1(data, 8);
soc = bit1(data, 9);
drc = bit1(data,10);
dma = bit1(data,11);
drs = bit1(data,12);
usf0 = bit1(data,13);
usf1 = bit1(data,14);
rqm = bit1(data,15);
p0 = data >> 0 & 1;
p1 = data >> 1 & 1;
ei = data >> 7 & 1;
sic = data >> 8 & 1;
soc = data >> 9 & 1;
drc = data >> 10 & 1;
dma = data >> 11 & 1;
drs = data >> 12 & 1;
usf0 = data >> 13 & 1;
usf1 = data >> 14 & 1;
rqm = data >> 15 & 1;
return *this;
}

View File

@@ -31,10 +31,10 @@ auto WDC65816::disassemble(uint24 address, bool e, bool m, bool x) -> string {
return data | readWord(address + 1) << 8;
};
auto opcode = read(address); address(0,15)++;
auto operand0 = read(address); address(0,15)++;
auto operand1 = read(address); address(0,15)++;
auto operand2 = read(address); address(0,15)++;
auto opcode = read(address); address.bit(0,15)++;
auto operand0 = read(address); address.bit(0,15)++;
auto operand1 = read(address); address.bit(0,15)++;
auto operand2 = read(address); address.bit(0,15)++;
uint8 operandByte = operand0 << 0;
uint16 operandWord = operand0 << 0 | operand1 << 8;

View File

@@ -8,7 +8,7 @@ N push(PC.b);
IF = 1;
DF = 0;
PC.l = read(r.vector + 0);
PC.h = read(r.vector + 1);
L PC.h = read(r.vector + 1);
PC.b = 0x00;
idleJump();
}

View File

@@ -45,7 +45,7 @@ auto WDC65816::instructionLongRead8(alu8 op, r16 I) -> void {
V.l = fetch();
V.h = fetch();
V.b = fetch();
L W.l = read(V.d + I.w + 0);
L W.l = readLong(V.d + I.w + 0);
alu(W.l);
}
@@ -53,8 +53,8 @@ auto WDC65816::instructionLongRead16(alu16 op, r16 I) -> void {
V.l = fetch();
V.h = fetch();
V.b = fetch();
W.l = read(V.d + I.w + 0);
L W.h = read(V.d + I.w + 1);
W.l = readLong(V.d + I.w + 0);
L W.h = readLong(V.d + I.w + 1);
alu(W.w);
}
@@ -157,7 +157,7 @@ auto WDC65816::instructionIndirectLongRead8(alu8 op, r16 I) -> void {
V.l = readDirectN(U.l + 0);
V.h = readDirectN(U.l + 1);
V.b = readDirectN(U.l + 2);
L W.l = read(V.d + I.w + 0);
L W.l = readLong(V.d + I.w + 0);
alu(W.l);
}
@@ -167,8 +167,8 @@ auto WDC65816::instructionIndirectLongRead16(alu16 op, r16 I) -> void {
V.l = readDirectN(U.l + 0);
V.h = readDirectN(U.l + 1);
V.b = readDirectN(U.l + 2);
W.l = read(V.d + I.w + 0);
L W.h = read(V.d + I.w + 1);
W.l = readLong(V.d + I.w + 0);
L W.h = readLong(V.d + I.w + 1);
alu(W.w);
}

View File

@@ -30,15 +30,15 @@ auto WDC65816::instructionLongWrite8(r16 I) -> void {
V.l = fetch();
V.h = fetch();
V.b = fetch();
L write(V.d + I.w + 0, A.l);
L writeLong(V.d + I.w + 0, A.l);
}
auto WDC65816::instructionLongWrite16(r16 I) -> void {
V.l = fetch();
V.h = fetch();
V.b = fetch();
write(V.d + I.w + 0, A.l);
L write(V.d + I.w + 1, A.h);
writeLong(V.d + I.w + 0, A.l);
L writeLong(V.d + I.w + 1, A.h);
}
auto WDC65816::instructionDirectWrite8(r16 F) -> void {
@@ -130,7 +130,7 @@ auto WDC65816::instructionIndirectLongWrite8(r16 I) -> void {
V.l = readDirectN(U.l + 0);
V.h = readDirectN(U.l + 1);
V.b = readDirectN(U.l + 2);
L write(V.d + I.w + 0, A.l);
L writeLong(V.d + I.w + 0, A.l);
}
auto WDC65816::instructionIndirectLongWrite16(r16 I) -> void {
@@ -139,8 +139,8 @@ auto WDC65816::instructionIndirectLongWrite16(r16 I) -> void {
V.l = readDirectN(U.l + 0);
V.h = readDirectN(U.l + 1);
V.b = readDirectN(U.l + 2);
write(V.d + I.w + 0, A.l);
L write(V.d + I.w + 1, A.h);
writeLong(V.d + I.w + 0, A.l);
L writeLong(V.d + I.w + 1, A.h);
}
auto WDC65816::instructionStackWrite8() -> void {

View File

@@ -50,31 +50,39 @@ auto WDC65816::pushN(uint8 data) -> void {
}
auto WDC65816::readDirect(uint address) -> uint8 {
if(EF && !D.l) return read(D.w | uint8(address));
return read(uint16(D.w + address));
if(EF && !D.l) return read(D.w | address & 0xff);
return read(D.w + address & 0xffff);
}
auto WDC65816::writeDirect(uint address, uint8 data) -> void {
if(EF && !D.l) return write(D.w | uint8(address), data);
write(uint16(D.w + address), data);
if(EF && !D.l) return write(D.w | address & 0xff, data);
write(D.w + address & 0xffff, data);
}
auto WDC65816::readDirectN(uint address) -> uint8 {
return read(uint16(D.w + address));
return read(D.w + address & 0xffff);
}
auto WDC65816::readBank(uint address) -> uint8 {
return read((B << 16) + address);
return read((B << 16) + address & 0xffffff);
}
auto WDC65816::writeBank(uint address, uint8 data) -> void {
write((B << 16) + address, data);
write((B << 16) + address & 0xffffff, data);
}
auto WDC65816::readLong(uint address) -> uint8 {
return read(address & 0xffffff);
}
auto WDC65816::writeLong(uint address, uint8 data) -> void {
write(address & 0xffffff, data);
}
auto WDC65816::readStack(uint address) -> uint8 {
return read(uint16(S.w + address));
return read(S.w + address & 0xffff);
}
auto WDC65816::writeStack(uint address, uint8 data) -> void {
write(uint16(S.w + address), data);
write(S.w + address & 0xffff, data);
}

View File

@@ -10,8 +10,8 @@ struct WDC65816 {
virtual auto idle() -> void = 0;
virtual auto idleBranch() -> void {}
virtual auto idleJump() -> void {}
virtual auto read(uint24 addr) -> uint8 = 0;
virtual auto write(uint24 addr, uint8 data) -> void = 0;
virtual auto read(uint addr) -> uint8 = 0;
virtual auto write(uint addr, uint8 data) -> void = 0;
virtual auto lastCycle() -> void = 0;
virtual auto interruptPending() const -> bool = 0;
virtual auto interrupt() -> void;
@@ -19,6 +19,9 @@ struct WDC65816 {
virtual auto readDisassembler(uint addr) -> uint8 { return 0; }
inline auto irq() const -> bool { return r.irq; }
inline auto irq(bool line) -> void { r.irq = line; }
using r8 = uint8;
union r16 {
@@ -44,22 +47,24 @@ struct WDC65816 {
auto power() -> void;
//memory.cpp
inline auto idleIRQ() -> void;
inline auto idle2() -> void;
inline auto idle4(uint16 x, uint16 y) -> void;
inline auto idle6(uint16 address) -> void;
inline auto fetch() -> uint8;
inline auto pull() -> uint8;
auto push(uint8 data) -> void;
inline auto pullN() -> uint8;
inline auto pushN(uint8 data) -> void;
inline auto readDirect(uint address) -> uint8;
inline auto writeDirect(uint address, uint8 data) -> void;
inline auto readDirectN(uint address) -> uint8;
inline auto readBank(uint address) -> uint8;
inline auto writeBank(uint address, uint8 data) -> void;
inline auto readStack(uint address) -> uint8;
inline auto writeStack(uint address, uint8 data) -> void;
alwaysinline auto idleIRQ() -> void;
alwaysinline auto idle2() -> void;
alwaysinline auto idle4(uint16 x, uint16 y) -> void;
alwaysinline auto idle6(uint16 address) -> void;
alwaysinline auto fetch() -> uint8;
alwaysinline auto pull() -> uint8;
auto push(uint8 data) -> void;
alwaysinline auto pullN() -> uint8;
alwaysinline auto pushN(uint8 data) -> void;
alwaysinline auto readDirect(uint address) -> uint8;
alwaysinline auto writeDirect(uint address, uint8 data) -> void;
alwaysinline auto readDirectN(uint address) -> uint8;
alwaysinline auto readBank(uint address) -> uint8;
alwaysinline auto writeBank(uint address, uint8 data) -> void;
alwaysinline auto readLong(uint address) -> uint8;
alwaysinline auto writeLong(uint address, uint8 data) -> void;
alwaysinline auto readStack(uint address) -> uint8;
alwaysinline auto writeStack(uint address, uint8 data) -> void;
//algorithms.cpp
using alu8 = auto (WDC65816::*)( uint8) -> uint8;

View File

@@ -1,4 +1,4 @@
processors += wdc65816 spc700 arm7tdmi gsu hg51b upd96050
processors += wdc65816 spc700 arm7tdmi
objects += sfc-interface sfc-system sfc-controller
objects += sfc-cartridge sfc-memory

View File

@@ -37,6 +37,17 @@ auto Cartridge::titles() const -> vector<string> {
return titles;
}
auto Cartridge::title() const -> string {
if(slotGameBoy.label) return slotGameBoy.label;
if(has.MCC && slotBSMemory.label) return slotBSMemory.label;
if(slotBSMemory.label) return {game.label, " + ", slotBSMemory.label};
if(slotSufamiTurboA.label && slotSufamiTurboB.label) return {slotSufamiTurboA.label, " + ", slotSufamiTurboB.label};
if(slotSufamiTurboA.label) return slotSufamiTurboA.label;
if(slotSufamiTurboB.label) return slotSufamiTurboB.label;
if(has.Cx4 || has.DSP1 || has.DSP2 || has.DSP4 || has.ST0010) return {"[HLE] ", game.label};
return game.label;
}
auto Cartridge::load() -> bool {
information = {};
has = {};
@@ -54,6 +65,7 @@ auto Cartridge::load() -> bool {
if(auto fp = platform->open(ID::SuperFamicom, "manifest.bml", File::Read, File::Required)) {
game.load(fp->reads());
} else return false;
loadCartridge(game.document);
//Game Boy

View File

@@ -1,10 +1,12 @@
struct Cartridge {
auto pathID() const -> uint { return information.pathID; }
auto region() const -> string { return information.region; }
auto headerTitle() const -> string { return game.title; }
auto hashes() const -> vector<string>;
auto manifests() const -> vector<string>;
auto titles() const -> vector<string>;
auto title() const -> string;
auto load() -> bool;
auto save() -> void;

View File

@@ -141,7 +141,7 @@ auto Cartridge::loadMap(Markup::Node map, T& memory) -> uint {
auto Cartridge::loadMap(
Markup::Node map,
const function<uint8 (uint, uint8)>& reader,
const function<void (uint, uint8)>& writer
const function<void (uint, uint8)>& writer
) -> uint {
auto addr = map["address"].text();
auto size = map["size"].natural();
@@ -428,7 +428,7 @@ auto Cartridge::loadHitachiDSP(Markup::Node node, uint roms) -> void {
}
}
if(configuration.hacks.coprocessors.hle) {
if(configuration.hacks.coprocessor.preferHLE) {
has.Cx4 = true;
for(auto map : node.find("map")) {
loadMap(map, {&Cx4::read, &cx4}, {&Cx4::write, &cx4});
@@ -503,7 +503,7 @@ auto Cartridge::loaduPD7725(Markup::Node node) -> void {
}
}
if(failed || configuration.hacks.coprocessors.hle) {
if(failed || configuration.hacks.coprocessor.preferHLE) {
auto manifest = BML::serialize(game.document);
if(manifest.find("identifier: DSP1")) { //also matches DSP1B
has.DSP1 = true;
@@ -583,7 +583,7 @@ auto Cartridge::loaduPD96050(Markup::Node node) -> void {
}
}
if(failed || configuration.hacks.coprocessors.hle) {
if(failed || configuration.hacks.coprocessor.preferHLE) {
auto manifest = BML::serialize(game.document);
if(manifest.find("identifier: ST010")) {
has.ST0010 = true;

View File

@@ -6,6 +6,10 @@ namespace SuperFamicom {
#include "serialization.cpp"
ArmDSP armdsp;
auto ArmDSP::synchronizeCPU() -> void {
if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread);
}
auto ArmDSP::Enter() -> void {
armdsp.boot();
while(true) scheduler.synchronize(), armdsp.main();
@@ -32,8 +36,8 @@ auto ArmDSP::main() -> void {
auto ArmDSP::step(uint clocks) -> void {
if(bridge.timer && --bridge.timer == 0);
Thread::step(clocks);
synchronize(cpu);
clock += clocks * (uint64_t)cpu.frequency;
synchronizeCPU();
}
//MMIO: 00-3f,80-bf:3800-38ff
@@ -41,7 +45,7 @@ auto ArmDSP::step(uint clocks) -> void {
//a0 ignored
auto ArmDSP::read(uint addr, uint8) -> uint8 {
cpu.synchronize(*this);
cpu.synchronizeCoprocessors();
uint8 data = 0x00;
addr &= 0xff06;
@@ -65,7 +69,7 @@ auto ArmDSP::read(uint addr, uint8) -> uint8 {
}
auto ArmDSP::write(uint addr, uint8 data) -> void {
cpu.synchronize(*this);
cpu.synchronizeCoprocessors();
addr &= 0xff06;

View File

@@ -7,6 +7,7 @@
struct ArmDSP : Processor::ARM7TDMI, Thread {
#include "registers.hpp"
auto synchronizeCPU() -> void;
static auto Enter() -> void;
auto boot() -> void;
auto main() -> void;

View File

@@ -83,9 +83,9 @@ auto ArmDSP::set(uint mode, uint32 addr, uint32 word) -> void {
if(addr == 0x4000'0010) bridge.signal = true;
if(addr == 0x4000'0020) bit8(bridge.timerlatch,0) = word;
if(addr == 0x4000'0024) bit8(bridge.timerlatch,1) = word;
if(addr == 0x4000'0028) bit8(bridge.timerlatch,2) = word;
if(addr == 0x4000'0020) bridge.timerlatch = bridge.timerlatch & 0xffff00 | word << 0;
if(addr == 0x4000'0024) bridge.timerlatch = bridge.timerlatch & 0xff00ff | word << 8;
if(addr == 0x4000'0028) bridge.timerlatch = bridge.timerlatch & 0x00ffff | word << 16;
if(addr == 0x4000'002c) bridge.timer = bridge.timerlatch;
}

View File

@@ -7,6 +7,10 @@ namespace SuperFamicom {
#include "serialization.cpp"
EpsonRTC epsonrtc;
auto EpsonRTC::synchronizeCPU() -> void {
if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread);
}
auto EpsonRTC::Enter() -> void {
while(true) scheduler.synchronize(), epsonrtc.main();
}
@@ -27,7 +31,11 @@ auto EpsonRTC::main() -> void {
}
step(1);
synchronize(cpu);
synchronizeCPU();
}
auto EpsonRTC::step(uint clocks) -> void {
clock += clocks * (uint64_t)cpu.frequency;
}
auto EpsonRTC::initialize() -> void {
@@ -127,7 +135,7 @@ auto EpsonRTC::synchronize(uint64 timestamp) -> void {
}
auto EpsonRTC::read(uint addr, uint8 data) -> uint8 {
cpu.synchronize(*this);
cpu.synchronizeCoprocessors();
addr &= 3;
if(addr == 0) {
@@ -152,7 +160,7 @@ auto EpsonRTC::read(uint addr, uint8 data) -> uint8 {
}
auto EpsonRTC::write(uint addr, uint8 data) -> void {
cpu.synchronize(*this);
cpu.synchronizeCoprocessors();
addr &= 3, data &= 15;
if(addr == 0) {

View File

@@ -1,10 +1,10 @@
//Epson RTC-4513 Real-Time Clock
struct EpsonRTC : Thread {
using Thread::synchronize;
auto synchronizeCPU() -> void;
static auto Enter() -> void;
auto main() -> void;
auto step(uint clocks) -> void;
auto initialize() -> void;
auto power() -> void;

View File

@@ -5,6 +5,10 @@ namespace SuperFamicom {
#include "serialization.cpp"
Event event;
auto Event::synchronizeCPU() -> void {
if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread);
}
auto Event::Enter() -> void {
while(true) scheduler.synchronize(), event.main();
}
@@ -26,7 +30,11 @@ auto Event::main() -> void {
}
step(1);
synchronize(cpu);
synchronizeCPU();
}
auto Event::step(uint clocks) -> void {
clock += clocks * (uint64_t)cpu.frequency;
}
auto Event::unload() -> void {
@@ -40,7 +48,7 @@ auto Event::power() -> void {
create(Event::Enter, 1);
//DIP switches 0-3 control the time: 3 minutes + 0-15 extra minutes
timer = (3 + bits(dip.value,0-3)) * 60; //in seconds
timer = (3 + (dip.value & 15)) * 60; //in seconds
//DIP switches 4-5 serve an unknown purpose
//DIP switches 6-7 are not connected

View File

@@ -15,8 +15,10 @@
struct Event : Thread {
//event.cpp
auto synchronizeCPU() -> void;
static auto Enter() -> void;
auto main() -> void;
auto step(uint clocks) -> void;
auto unload() -> void;
auto power() -> void;

View File

@@ -1,4 +1,5 @@
#include <sfc/sfc.hpp>
#include <processor/hg51b/hg51b.cpp>
namespace SuperFamicom {
@@ -7,19 +8,23 @@ namespace SuperFamicom {
#include "data-rom.cpp"
HitachiDSP hitachidsp;
auto HitachiDSP::synchronizeCPU() -> void {
if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread);
}
auto HitachiDSP::Enter() -> void {
while(true) scheduler.synchronize(), hitachidsp.main();
}
auto HitachiDSP::step(uint clocks) -> void {
HG51B::step(clocks);
Thread::step(clocks);
synchronize(cpu);
clock += clocks * (uint64_t)cpu.frequency;
synchronizeCPU();
}
auto HitachiDSP::halt() -> void {
HG51B::halt();
if(io.irq == 0) r.i = 1, cpu.r.irq = 1;
if(io.irq == 0) cpu.irq(r.i = 1);
}
auto HitachiDSP::unload() -> void {

View File

@@ -3,6 +3,7 @@ struct HitachiDSP : Processor::HG51B, Thread {
WritableMemory ram;
//hitachidsp.cpp
auto synchronizeCPU() -> void;
static auto Enter() -> void;
auto step(uint clocks) -> void override;
auto halt() -> void override;

View File

@@ -134,21 +134,21 @@ auto HitachiDSP::readIO(uint address, uint8 data) -> uint8 {
//IO
switch(address) {
case 0x7f40: return bit8(io.dma.source,0);
case 0x7f41: return bit8(io.dma.source,1);
case 0x7f42: return bit8(io.dma.source,2);
case 0x7f43: return bit8(io.dma.length,0);
case 0x7f44: return bit8(io.dma.length,1);
case 0x7f45: return bit8(io.dma.target,0);
case 0x7f46: return bit8(io.dma.target,1);
case 0x7f47: return bit8(io.dma.target,2);
case 0x7f40: return io.dma.source >> 0;
case 0x7f41: return io.dma.source >> 8;
case 0x7f42: return io.dma.source >> 16;
case 0x7f43: return io.dma.length >> 0;
case 0x7f44: return io.dma.length >> 8;
case 0x7f45: return io.dma.target >> 0;
case 0x7f46: return io.dma.target >> 8;
case 0x7f47: return io.dma.target >> 16;
case 0x7f48: return io.cache.page;
case 0x7f49: return bit8(io.cache.base,0);
case 0x7f4a: return bit8(io.cache.base,1);
case 0x7f4b: return bit8(io.cache.base,2);
case 0x7f49: return io.cache.base >> 0;
case 0x7f4a: return io.cache.base >> 8;
case 0x7f4b: return io.cache.base >> 16;
case 0x7f4c: return io.cache.lock[0] << 0 | io.cache.lock[1] << 1;
case 0x7f4d: return bit8(io.cache.pb,0);
case 0x7f4e: return bit8(io.cache.pb,1);
case 0x7f4d: return io.cache.pb >> 0;
case 0x7f4e: return io.cache.pb >> 8;
case 0x7f4f: return io.cache.pc;
case 0x7f50: return io.wait.ram << 0 | io.wait.rom << 4;
case 0x7f51: return io.irq;
@@ -166,7 +166,11 @@ auto HitachiDSP::readIO(uint address, uint8 data) -> uint8 {
//registers
if((address >= 0x7f80 && address <= 0x7faf) || (address >= 0x7fc0 && address <= 0x7fef)) {
address &= 0x3f;
return r.gpr[address / 3].byte(address % 3);
switch(address % 3) {
case 0: return r.gpr[address / 3] >> 0;
case 1: return r.gpr[address / 3] >> 8;
case 2: return r.gpr[address / 3] >> 16;
}
}
return 0x00;
@@ -177,16 +181,16 @@ auto HitachiDSP::writeIO(uint address, uint8 data) -> void {
//IO
switch(address) {
case 0x7f40: bit8(io.dma.source,0) = data; return;
case 0x7f41: bit8(io.dma.source,1) = data; return;
case 0x7f42: bit8(io.dma.source,2) = data; return;
case 0x7f40: io.dma.source = io.dma.source & 0xffff00 | data << 0; return;
case 0x7f41: io.dma.source = io.dma.source & 0xff00ff | data << 8; return;
case 0x7f42: io.dma.source = io.dma.source & 0x00ffff | data << 16; return;
case 0x7f43: bit8(io.dma.length,0) = data; return;
case 0x7f44: bit8(io.dma.length,1) = data; return;
case 0x7f43: io.dma.length = io.dma.length & 0xff00 | data << 0; return;
case 0x7f44: io.dma.length = io.dma.length & 0x00ff | data << 8; return;
case 0x7f45: bit8(io.dma.target,0) = data; return;
case 0x7f46: bit8(io.dma.target,1) = data; return;
case 0x7f47: bit8(io.dma.target,2) = data;
case 0x7f45: io.dma.target = io.dma.target & 0xffff00 | data << 0; return;
case 0x7f46: io.dma.target = io.dma.target & 0xff00ff | data << 8; return;
case 0x7f47: io.dma.target = io.dma.target & 0x00ffff | data << 16;
if(io.halt) io.dma.enable = 1;
return;
@@ -195,17 +199,17 @@ auto HitachiDSP::writeIO(uint address, uint8 data) -> void {
if(io.halt) io.cache.enable = 1;
return;
case 0x7f49: bit8(io.cache.base,0) = data; return;
case 0x7f4a: bit8(io.cache.base,1) = data; return;
case 0x7f4b: bit8(io.cache.base,2) = data; return;
case 0x7f49: io.cache.base = io.cache.base & 0xffff00 | data << 0; return;
case 0x7f4a: io.cache.base = io.cache.base & 0xff00ff | data << 8; return;
case 0x7f4b: io.cache.base = io.cache.base & 0x00ffff | data << 16; return;
case 0x7f4c:
io.cache.lock[0] = bit1(data,0);
io.cache.lock[1] = bit1(data,1);
io.cache.lock[0] = bool(data & 1);
io.cache.lock[1] = bool(data & 2);
return;
case 0x7f4d: bit8(io.cache.pb,0) = data; return;
case 0x7f4e: bit8(io.cache.pb,1) = data; return;
case 0x7f4d: io.cache.pb = io.cache.pb & 0xff00 | data << 0; return;
case 0x7f4e: io.cache.pb = io.cache.pb & 0x00ff | data << 8; return;
case 0x7f4f:
io.cache.pc = data;
@@ -217,13 +221,13 @@ auto HitachiDSP::writeIO(uint address, uint8 data) -> void {
return;
case 0x7f50:
io.wait.ram = bits(data,0-2);
io.wait.rom = bits(data,4-6);
io.wait.ram = data >> 0 & 7;
io.wait.rom = data >> 4 & 7;
return;
case 0x7f51:
io.irq = data & 1;
if(io.irq == 1) r.i = 0, cpu.r.irq = 0;
if(io.irq == 1) cpu.irq(r.i = 0);
return;
case 0x7f52:
@@ -259,6 +263,11 @@ auto HitachiDSP::writeIO(uint address, uint8 data) -> void {
//registers
if((address >= 0x7f80 && address <= 0x7faf) || (address >= 0x7fc0 && address <= 0x7fef)) {
address &= 0x3f;
bit8(r.gpr[address / 3],address % 3) = data;
switch(address % 3) {
case 0: r.gpr[address / 3] = r.gpr[address / 3] & 0xffff00 | data << 0; break;
case 1: r.gpr[address / 3] = r.gpr[address / 3] & 0xff00ff | data << 8; break;
case 2: r.gpr[address / 3] = r.gpr[address / 3] & 0x00ffff | data << 16; break;
}
return;
}
}

View File

@@ -46,6 +46,10 @@ namespace SameBoy {
}
}
auto ICD::synchronizeCPU() -> void {
if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread);
}
auto ICD::Enter() -> void {
while(true) {
scheduler.synchronize();
@@ -61,7 +65,11 @@ auto ICD::main() -> void {
stream->sample(float(0.0), float(0.0));
step(128);
}
synchronize(cpu);
synchronizeCPU();
}
auto ICD::step(uint clocks) -> void {
clock += clocks * (uint64_t)cpu.frequency;
}
auto ICD::load() -> bool {
@@ -139,9 +147,8 @@ auto ICD::power(bool reset) -> void {
for(auto& packet : this->packet) packet = {};
packetSize = 0;
joypID = 3;
joyp14Lock = 0;
joyp15Lock = 0;
joypID = 0;
joypLock = 1;
pulseLock = 1;
strobeLock = 0;
packetLock = 0;

View File

@@ -4,8 +4,10 @@ struct ICD : Emulator::Platform, Thread {
inline auto pathID() const -> uint { return information.pathID; }
auto synchronizeCPU() -> void;
static auto Enter() -> void;
auto main() -> void;
auto step(uint clocks) -> void;
auto load() -> bool;
auto save() -> void;
@@ -42,8 +44,7 @@ private:
uint7 packetSize;
uint2 joypID;
uint1 joyp14Lock;
uint1 joyp15Lock;
uint1 joypLock;
uint1 pulseLock;
uint1 strobeLock;
uint1 packetLock;

View File

@@ -27,9 +27,8 @@ auto ICD::apuWrite(float left, float right) -> void {
auto ICD::joypWrite(bool p14, bool p15) -> void {
//joypad handling
if(p14 == 1 && p15 == 1) {
if(joyp14Lock == 0 && joyp15Lock == 0) {
joyp14Lock = 1;
joyp15Lock = 1;
if(joypLock == 0) {
joypLock = 1;
joypID++;
if(mltReq == 0) joypID &= 0; //1-player mode
if(mltReq == 1) joypID &= 1; //2-player mode
@@ -46,35 +45,35 @@ auto ICD::joypWrite(bool p14, bool p15) -> void {
uint4 input = 0xf;
if(p14 == 1 && p15 == 1) input = 0xf - joypID;
if(p14 == 0) input &= bits(joypad,0-3); //d-pad
if(p15 == 0) input &= bits(joypad,4-7); //buttons
if(p14 == 0) input &= (joypad >> 0 & 15); //d-pad
if(p15 == 0) input &= (joypad >> 4 & 15); //buttons
GB_icd_set_joyp(&sameboy, input);
if(p14 == 0 && p15 == 1) joyp14Lock = 0;
if(p14 == 1 && p15 == 0) joyp15Lock = 0;
if(p14 == 0 && p15 == 1);
if(p14 == 1 && p15 == 0) joypLock ^= 1;
//packet handling
if(p14 == 0 && p15 == 0) { //pulse
pulseLock = false;
pulseLock = 0;
packetOffset = 0;
bitOffset = 0;
strobeLock = true;
packetLock = false;
strobeLock = 1;
packetLock = 0;
return;
}
if(pulseLock) return;
if(pulseLock == 1) return;
if(p14 == 1 && p15 == 1) {
strobeLock = false;
strobeLock = 0;
return;
}
if(strobeLock) {
if(strobeLock == 1) {
if(p14 == 1 || p15 == 1) { //malformed packet
packetLock = false;
pulseLock = true;
packetLock = 0;
pulseLock = 1;
bitOffset = 0;
packetOffset = 0;
} else {
@@ -85,18 +84,21 @@ auto ICD::joypWrite(bool p14, bool p15) -> void {
//p14:0, p15:1 = 0
//p14:1, p15:0 = 1
bool bit = p15 == 0;
strobeLock = true;
strobeLock = 1;
if(packetLock) {
if(packetLock == 1) {
if(p14 == 0 && p15 == 1) {
if((joypPacket[0] >> 3) == 0x11) {
mltReq = joypPacket[1] & 3;
joypID = 3; //required: the next time P14==1 && P15==1; increment and start from ID=0 (Joypad 1)
if(mltReq == 0) joypID &= 0; //1-player mode
if(mltReq == 1) joypID &= 1; //2-player mode
if(mltReq == 2) joypID &= 3; //4-player mode (unverified; but the most likely behavior)
if(mltReq == 3) joypID &= 3; //4-player mode
}
if(packetSize < 64) packet[packetSize++] = joypPacket;
packetLock = false;
pulseLock = true;
packetLock = 0;
pulseLock = 1;
}
return;
}
@@ -107,5 +109,5 @@ auto ICD::joypWrite(bool p14, bool p15) -> void {
joypPacket[packetOffset] = bitData;
if(++packetOffset) return;
packetLock = true;
packetLock = 1;
}

View File

@@ -57,10 +57,10 @@ auto ICD::writeIO(uint addr, uint8 data) -> void {
}
auto frequency = system.cpuFrequency();
switch(data & 3) {
case 0: setFrequency(frequency / 4); break; //fast (glitchy, even on real hardware)
case 1: setFrequency(frequency / 5); break; //normal
case 2: setFrequency(frequency / 7); break; //slow
case 3: setFrequency(frequency / 9); break; //very slow
case 0: this->frequency = frequency / 4; break; //fast (glitchy, even on real hardware)
case 1: this->frequency = frequency / 5; break; //normal
case 2: this->frequency = frequency / 7; break; //slow
case 3: this->frequency = frequency / 9; break; //very slow
}
r6003 = data;
return;

View File

@@ -16,8 +16,7 @@ auto ICD::serialize(serializer& s) -> void {
s.integer(packetSize);
s.integer(joypID);
s.integer(joyp14Lock);
s.integer(joyp15Lock);
s.integer(joypLock);
s.integer(pulseLock);
s.integer(strobeLock);
s.integer(packetLock);

View File

@@ -40,8 +40,8 @@ auto MCC::read(uint address, uint8 data) -> uint8 {
case 2: return r.mapping << 7;
case 3: return r.psramEnableLo << 7;
case 4: return r.psramEnableHi << 7;
case 5: return r.psramMapping.bit(0) << 7;
case 6: return r.psramMapping.bit(1) << 7;
case 5: return (r.psramMapping >> 0 & 1) << 7;
case 6: return (r.psramMapping >> 1 & 1) << 7;
case 7: return r.romEnableLo << 7;
case 8: return r.romEnableHi << 7;
case 9: return r.exEnableLo << 7;
@@ -60,20 +60,20 @@ auto MCC::read(uint address, uint8 data) -> uint8 {
auto MCC::write(uint address, uint8 data) -> void {
if((address & 0xf0f000) == 0x005000) { //$00-0f:5000-5fff
switch(address >> 16 & 15) {
case 1: irq.enable = bit1(data,7); break;
case 2: w.mapping = bit1(data,7); break;
case 3: w.psramEnableLo = bit1(data,7); break;
case 4: w.psramEnableHi = bit1(data,7); break;
case 5: bit1(w.psramMapping,0) = bit1(data,7); break;
case 6: bit1(w.psramMapping,1) = bit1(data,7); break;
case 7: w.romEnableLo = bit1(data,7); break;
case 8: w.romEnableHi = bit1(data,7); break;
case 9: w.exEnableLo = bit1(data,7); break;
case 10: w.exEnableHi = bit1(data,7); break;
case 11: w.exMapping = bit1(data,7); break;
case 12: w.internallyWritable = bit1(data,7); break;
case 13: w.externallyWritable = bit1(data,7); break;
case 14: if(bit1(data,7)) commit(); break;
case 1: irq.enable = data >> 7; break;
case 2: w.mapping = data >> 7; break;
case 3: w.psramEnableLo = data >> 7; break;
case 4: w.psramEnableHi = data >> 7; break;
case 5: w.psramMapping = w.psramMapping & 2 | data >> 7 << 0; break;
case 6: w.psramMapping = w.psramMapping & 1 | data >> 7 << 1; break;
case 7: w.romEnableLo = data >> 7; break;
case 8: w.romEnableHi = data >> 7; break;
case 9: w.exEnableLo = data >> 7; break;
case 10: w.exEnableHi = data >> 7; break;
case 11: w.exMapping = data >> 7; break;
case 12: w.internallyWritable = data >> 7; break;
case 13: w.externallyWritable = data >> 7; break;
case 14: if(data >> 7) commit(); break;
}
}
}

View File

@@ -6,6 +6,11 @@ MSU1 msu1;
#include "serialization.cpp"
auto MSU1::synchronizeCPU() -> void {
if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread);
}
auto MSU1::Enter() -> void {
while(true) scheduler.synchronize(), msu1.main();
}
@@ -36,7 +41,11 @@ auto MSU1::main() -> void {
stream->sample(float(left), float(right));
step(1);
synchronize(cpu);
synchronizeCPU();
}
auto MSU1::step(uint clocks) -> void {
clock += clocks * (uint64_t)cpu.frequency;
}
auto MSU1::unload() -> void {
@@ -46,7 +55,7 @@ auto MSU1::unload() -> void {
auto MSU1::power() -> void {
create(MSU1::Enter, 44100);
stream = Emulator::audio.createStream(2, frequency());
stream = Emulator::audio.createStream(2, frequency);
io.dataSeekOffset = 0;
io.dataReadOffset = 0;
@@ -98,9 +107,9 @@ auto MSU1::audioOpen() -> void {
}
auto MSU1::readIO(uint addr, uint8) -> uint8 {
cpu.synchronize(*this);
cpu.synchronizeCoprocessors();
switch(0x2000 | (addr & 7)) {
switch(0x2000 | addr & 7) {
case 0x2000:
return (
Revision << 0
@@ -128,18 +137,18 @@ auto MSU1::readIO(uint addr, uint8) -> uint8 {
}
auto MSU1::writeIO(uint addr, uint8 data) -> void {
cpu.synchronize(*this);
cpu.synchronizeCoprocessors();
switch(0x2000 | (addr & 7)) {
case 0x2000: bit8(io.dataSeekOffset,0) = data; break;
case 0x2001: bit8(io.dataSeekOffset,1) = data; break;
case 0x2002: bit8(io.dataSeekOffset,2) = data; break;
case 0x2003: bit8(io.dataSeekOffset,3) = data;
switch(0x2000 | addr & 7) {
case 0x2000: io.dataSeekOffset = io.dataSeekOffset & 0xffffff00 | data << 0; break;
case 0x2001: io.dataSeekOffset = io.dataSeekOffset & 0xffff00ff | data << 8; break;
case 0x2002: io.dataSeekOffset = io.dataSeekOffset & 0xff00ffff | data << 16; break;
case 0x2003: io.dataSeekOffset = io.dataSeekOffset & 0x00ffffff | data << 24;
io.dataReadOffset = io.dataSeekOffset;
if(dataFile) dataFile->seek(io.dataReadOffset);
break;
case 0x2004: bit8(io.audioTrack,0) = data; break;
case 0x2005: bit8(io.audioTrack,1) = data;
case 0x2004: io.audioTrack = io.audioTrack & 0xff00 | data << 0; break;
case 0x2005: io.audioTrack = io.audioTrack & 0x00ff | data << 8;
io.audioPlay = false;
io.audioRepeat = false;
io.audioPlayOffset = 8;
@@ -156,9 +165,9 @@ auto MSU1::writeIO(uint addr, uint8 data) -> void {
case 0x2007:
if(io.audioBusy) break;
if(io.audioError) break;
io.audioPlay = bit1(data,0);
io.audioRepeat = bit1(data,1);
boolean audioResume = bit1(data,2);
io.audioPlay = bool(data & 1);
io.audioRepeat = bool(data & 2);
boolean audioResume = bool(data & 4);
if(!io.audioPlay && audioResume) {
io.audioResumeTrack = io.audioTrack;
io.audioResumeOffset = io.audioPlayOffset;

View File

@@ -1,8 +1,10 @@
struct MSU1 : Thread {
shared_pointer<Emulator::Stream> stream;
auto synchronizeCPU() -> void;
static auto Enter() -> void;
auto main() -> void;
auto step(uint clocks) -> void;
auto unload() -> void;
auto power() -> void;
@@ -15,8 +17,8 @@ struct MSU1 : Thread {
auto serialize(serializer&) -> void;
private:
vfs::shared::file dataFile;
vfs::shared::file audioFile;
shared_pointer<vfs::file> dataFile;
shared_pointer<vfs::file> audioFile;
enum Flag : uint {
Revision = 0x02, //max: 0x07

View File

@@ -1,10 +1,15 @@
#include <sfc/sfc.hpp>
#include <processor/upd96050/upd96050.cpp>
namespace SuperFamicom {
#include "serialization.cpp"
NECDSP necdsp;
auto NECDSP::synchronizeCPU() -> void {
if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread);
}
auto NECDSP::Enter() -> void {
while(true) scheduler.synchronize(), necdsp.main();
}
@@ -12,11 +17,15 @@ auto NECDSP::Enter() -> void {
auto NECDSP::main() -> void {
exec();
step(1);
synchronize(cpu);
synchronizeCPU();
}
auto NECDSP::step(uint clocks) -> void {
clock += clocks * (uint64_t)cpu.frequency;
}
auto NECDSP::read(uint addr, uint8) -> uint8 {
cpu.synchronize(*this);
cpu.synchronizeCoprocessors();
if(addr & 1) {
return uPD96050::readSR();
} else {
@@ -25,7 +34,7 @@ auto NECDSP::read(uint addr, uint8) -> uint8 {
}
auto NECDSP::write(uint addr, uint8 data) -> void {
cpu.synchronize(*this);
cpu.synchronizeCoprocessors();
if(addr & 1) {
return uPD96050::writeSR(data);
} else {
@@ -34,12 +43,12 @@ auto NECDSP::write(uint addr, uint8 data) -> void {
}
auto NECDSP::readRAM(uint addr, uint8) -> uint8 {
cpu.synchronize(*this);
cpu.synchronizeCoprocessors();
return uPD96050::readDP(addr);
}
auto NECDSP::writeRAM(uint addr, uint8 data) -> void {
cpu.synchronize(*this);
cpu.synchronizeCoprocessors();
return uPD96050::writeDP(addr, data);
}

View File

@@ -1,6 +1,8 @@
struct NECDSP : Processor::uPD96050, Thread {
auto synchronizeCPU() -> void;
static auto Enter() -> void;
auto main() -> void;
auto step(uint clocks) -> void;
auto read(uint addr, uint8 data) -> uint8;
auto write(uint addr, uint8 data) -> void;

View File

@@ -1,5 +1,5 @@
auto SA1::BWRAM::conflict() const -> bool {
if(configuration.hacks.coprocessors.delayedSync) return false;
if(configuration.hacks.coprocessor.delayedSync) return false;
if((cpu.r.mar & 0x40e000) == 0x006000) return true; //00-3f,80-bf:6000-7fff
if((cpu.r.mar & 0xf00000) == 0x400000) return true; //40-4f:0000-ffff
@@ -22,7 +22,7 @@ auto SA1::BWRAM::write(uint address, uint8 data) -> void {
//00-3f,80-bf:6000-7fff size=0x2000 => 00:0000-1fff
//40-4f:0000-ffff => untranslated
auto SA1::BWRAM::readCPU(uint address, uint8 data) -> uint8 {
cpu.synchronize(sa1);
cpu.synchronizeCoprocessors();
if(address < 0x2000) { //$00-3f,80-bf:6000-7fff
address = sa1.mmio.sbm * 0x2000 + (address & 0x1fff);
@@ -33,7 +33,7 @@ auto SA1::BWRAM::readCPU(uint address, uint8 data) -> uint8 {
}
auto SA1::BWRAM::writeCPU(uint address, uint8 data) -> void {
cpu.synchronize(sa1);
cpu.synchronizeCoprocessors();
if(address < 0x2000) { //$00-3f,80-bf:6000-7fff
address = sa1.mmio.sbm * 0x2000 + (address & 0x1fff);
@@ -80,18 +80,18 @@ auto SA1::BWRAM::readBitmap(uint20 address, uint8 data) -> uint8 {
uint shift = address & 1;
address >>= 1;
switch(shift) {
case 0: return cbits(read(address),0-3);
case 1: return cbits(read(address),4-7);
case 0: return read(address) >> 0 & 15;
case 1: return read(address) >> 4 & 15;
}
} else {
//2bpp
uint shift = address & 3;
address >>= 2;
switch(shift) {
case 0: return cbits(read(address),0-1);
case 1: return cbits(read(address),2-3);
case 2: return cbits(read(address),4-5);
case 3: return cbits(read(address),6-7);
case 0: return read(address) >> 0 & 3;
case 1: return read(address) >> 2 & 3;
case 2: return read(address) >> 4 & 3;
case 3: return read(address) >> 6 & 3;
}
}
unreachable;

View File

@@ -51,7 +51,7 @@ auto SA1::dmaCC1() -> void {
mmio.chdma_irqfl = true;
if(mmio.chdma_irqen) {
mmio.chdma_irqcl = 0;
cpu.r.irq = 1;
cpu.irq(1);
}
}

View File

@@ -1,5 +1,5 @@
auto SA1::readIOCPU(uint address, uint8 data) -> uint8 {
cpu.synchronize(sa1);
cpu.synchronizeCoprocessors();
switch(0x2200 | address & 0x1ff) {
@@ -25,7 +25,7 @@ auto SA1::readIOCPU(uint address, uint8 data) -> uint8 {
}
auto SA1::readIOSA1(uint address, uint8) -> uint8 {
synchronize(cpu);
synchronizeCPU();
switch(0x2200 | address & 0x1ff) {
@@ -101,7 +101,7 @@ auto SA1::readIOSA1(uint address, uint8) -> uint8 {
}
auto SA1::writeIOCPU(uint address, uint8 data) -> void {
cpu.synchronize(sa1);
cpu.synchronizeCoprocessors();
switch(0x2200 | address & 0x1ff) {
@@ -136,14 +136,14 @@ auto SA1::writeIOCPU(uint address, uint8 data) -> void {
if(!mmio.cpu_irqen && (data & 0x80)) {
if(mmio.cpu_irqfl) {
mmio.cpu_irqcl = 0;
cpu.r.irq = 1;
cpu.irq(1);
}
}
if(!mmio.chdma_irqen && (data & 0x20)) {
if(mmio.chdma_irqfl) {
mmio.chdma_irqcl = 0;
cpu.r.irq = 1;
cpu.irq(1);
}
}
@@ -160,7 +160,7 @@ auto SA1::writeIOCPU(uint address, uint8 data) -> void {
if(mmio.cpu_irqcl ) mmio.cpu_irqfl = false;
if(mmio.chdma_irqcl) mmio.chdma_irqfl = false;
if(!mmio.cpu_irqfl && !mmio.chdma_irqfl) cpu.r.irq = 0;
if(!mmio.cpu_irqfl && !mmio.chdma_irqfl) cpu.irq(0);
return;
}
@@ -236,7 +236,7 @@ auto SA1::writeIOCPU(uint address, uint8 data) -> void {
}
auto SA1::writeIOSA1(uint address, uint8 data) -> void {
synchronize(cpu);
synchronizeCPU();
switch(0x2200 | address & 0x1ff) {
@@ -251,7 +251,7 @@ auto SA1::writeIOSA1(uint address, uint8 data) -> void {
mmio.cpu_irqfl = true;
if(mmio.cpu_irqen) {
mmio.cpu_irqcl = 0;
cpu.r.irq = 1;
cpu.irq(1);
}
}
@@ -403,19 +403,19 @@ auto SA1::writeIOSA1(uint address, uint8 data) -> void {
//(MAL) multiplicand / dividend low
case 0x2251: {
bit8(mmio.ma,0) = data;
mmio.ma = mmio.ma & ~0x00ff | data << 0;
return;
}
//(MAH) multiplicand / dividend high
case 0x2252: {
bit8(mmio.ma,1) = data;
mmio.ma = mmio.ma & ~0xff00 | data << 8;
return;
}
//(MBL) multiplier / divisor low
case 0x2253: {
bit8(mmio.mb,0) = data;
mmio.mb = mmio.mb & ~0x00ff | data << 0;
return;
}
@@ -423,7 +423,7 @@ auto SA1::writeIOSA1(uint address, uint8 data) -> void {
//multiplication / cumulative sum only resets MB
//division resets both MA and MB
case 0x2254: {
bit8(mmio.mb,1) = data;
mmio.mb = mmio.mb & ~0xff00 | data << 8;
if(mmio.acm == 0) {
if(mmio.md == 0) {

View File

@@ -1,5 +1,5 @@
auto SA1::IRAM::conflict() const -> bool {
if(configuration.hacks.coprocessors.delayedSync) return false;
if(configuration.hacks.coprocessor.delayedSync) return false;
if((cpu.r.mar & 0x40f800) == 0x003000) return cpu.refresh() == 0; //00-3f,80-bf:3000-37ff
return false;
@@ -18,12 +18,12 @@ auto SA1::IRAM::write(uint address, uint8 data) -> void {
}
auto SA1::IRAM::readCPU(uint address, uint8 data) -> uint8 {
cpu.synchronize(sa1);
cpu.synchronizeCoprocessors();
return read(address, data);
}
auto SA1::IRAM::writeCPU(uint address, uint8 data) -> void {
cpu.synchronize(sa1);
cpu.synchronizeCoprocessors();
return write(address, data);
}

View File

@@ -18,7 +18,7 @@ auto SA1::idleBranch() -> void {
if(r.pc.d & 1) idleJump();
}
auto SA1::read(uint24 address) -> uint8 {
auto SA1::read(uint address) -> uint8 {
r.mar = address;
uint8 data = r.mdr;
@@ -62,7 +62,7 @@ auto SA1::read(uint24 address) -> uint8 {
return data;
}
auto SA1::write(uint24 address, uint8 data) -> void {
auto SA1::write(uint address, uint8 data) -> void {
r.mar = address;
r.mdr = data;

View File

@@ -1,5 +1,5 @@
auto SA1::ROM::conflict() const -> bool {
if(configuration.hacks.coprocessors.delayedSync) return false;
if(configuration.hacks.coprocessor.delayedSync) return false;
if((cpu.r.mar & 0x408000) == 0x008000) return true; //00-3f,80-bf:8000-ffff
if((cpu.r.mar & 0xc00000) == 0xc00000) return true; //c0-ff:0000-ffff

View File

@@ -11,6 +11,10 @@ namespace SuperFamicom {
#include "serialization.cpp"
SA1 sa1;
auto SA1::synchronizeCPU() -> void {
if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread);
}
auto SA1::Enter() -> void {
while(true) scheduler.synchronize(), sa1.main();
}
@@ -78,13 +82,9 @@ auto SA1::interruptPending() const -> bool {
return status.interruptPending;
}
auto SA1::synchronizing() const -> bool {
return scheduler.synchronizing();
}
auto SA1::step() -> void {
Thread::step(2);
synchronize(cpu);
clock += (uint64_t)cpu.frequency << 1;
synchronizeCPU();
//adjust counters:
//note that internally, status counters are in clocks;
@@ -127,8 +127,10 @@ auto SA1::unload() -> void {
}
auto SA1::power() -> void {
double overclock = max(1.0, min(4.0, configuration.hacks.sa1.overclock / 100.0));
WDC65816::power();
create(SA1::Enter, system.cpuFrequency());
create(SA1::Enter, system.cpuFrequency() * overclock);
bwram.dma = false;
for(uint address : range(iram.size())) {

View File

@@ -1,7 +1,10 @@
//Super Accelerator (SA-1)
struct SA1 : Processor::WDC65816, Thread {
inline auto synchronizing() const -> bool override { return scheduler.mode == Scheduler::Mode::SynchronizeAll; }
//sa1.cpp
auto synchronizeCPU() -> void;
static auto Enter() -> void;
auto main() -> void;
auto step() -> void;
@@ -10,7 +13,6 @@ struct SA1 : Processor::WDC65816, Thread {
alwaysinline auto triggerIRQ() -> void;
alwaysinline auto lastCycle() -> void override;
alwaysinline auto interruptPending() const -> bool override;
auto synchronizing() const -> bool override;
auto unload() -> void;
auto power() -> void;
@@ -36,8 +38,8 @@ struct SA1 : Processor::WDC65816, Thread {
alwaysinline auto idle() -> void override;
alwaysinline auto idleJump() -> void override;
alwaysinline auto idleBranch() -> void override;
alwaysinline auto read(uint24 address) -> uint8 override;
alwaysinline auto write(uint24 address, uint8 data) -> void override;
alwaysinline auto read(uint address) -> uint8 override;
alwaysinline auto write(uint address, uint8 data) -> void override;
auto readVBR(uint address, uint8 data = 0) -> uint8;
auto readDisassembler(uint address) -> uint8 override;

View File

@@ -66,11 +66,11 @@ auto SDD1::dmaRead(uint addr, uint8 data) -> uint8 {
auto SDD1::dmaWrite(uint addr, uint8 data) -> void {
uint channel = addr >> 4 & 7;
switch(addr & 15) {
case 2: bit8(dma[channel].addr,0) = data; break;
case 3: bit8(dma[channel].addr,1) = data; break;
case 4: bit8(dma[channel].addr,2) = data; break;
case 5: bit8(dma[channel].size,0) = data; break;
case 6: bit8(dma[channel].size,1) = data; break;
case 2: dma[channel].addr = dma[channel].addr & 0xffff00 | data << 0; break;
case 3: dma[channel].addr = dma[channel].addr & 0xff00ff | data << 8; break;
case 4: dma[channel].addr = dma[channel].addr & 0x00ffff | data << 16; break;
case 5: dma[channel].size = dma[channel].size & 0xff00 | data << 0; break;
case 6: dma[channel].size = dma[channel].size & 0x00ff | data << 8; break;
}
return cpu.writeDMA(addr, data);
}
@@ -100,7 +100,7 @@ auto SDD1::mcuRead(uint addr, uint8 data) -> uint8 {
if(r4800 & r4801) {
//at least one channel has S-DD1 decompression enabled ...
for(auto n : range(8)) {
if(bit1(r4800,n) && bit1(r4801,n)) {
if((r4800 & 1 << n) && (r4801 & 1 << n)) {
//S-DD1 always uses fixed transfer mode, so address will not change during transfer
if(addr == dma[n].addr) {
if(!dmaReady) {
@@ -113,7 +113,7 @@ auto SDD1::mcuRead(uint addr, uint8 data) -> uint8 {
data = decompressor.read();
if(--dma[n].size == 0) {
dmaReady = false;
bit1(r4801,n) = 0;
r4801 &= ~(1 << n);
}
return data;

View File

@@ -7,6 +7,10 @@ namespace SuperFamicom {
#include "serialization.cpp"
SharpRTC sharprtc;
auto SharpRTC::synchronizeCPU() -> void {
if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread);
}
auto SharpRTC::Enter() -> void {
while(true) scheduler.synchronize(), sharprtc.main();
}
@@ -15,7 +19,11 @@ auto SharpRTC::main() -> void {
tickSecond();
step(1);
synchronize(cpu);
synchronizeCPU();
}
auto SharpRTC::step(uint clocks) -> void {
clock += clocks * (uint64_t)cpu.frequency;
}
auto SharpRTC::initialize() -> void {

View File

@@ -1,8 +1,8 @@
struct SharpRTC : Thread {
using Thread::synchronize;
auto synchronizeCPU() -> void;
static auto Enter() -> void;
auto main() -> void;
auto step(uint clocks) -> void;
auto initialize() -> void;
auto power() -> void;

View File

@@ -16,6 +16,10 @@ SPC7110::~SPC7110() {
delete decompressor;
}
auto SPC7110::synchronizeCPU() -> void {
if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread);
}
auto SPC7110::Enter() -> void {
while(true) scheduler.synchronize(), spc7110.main();
}
@@ -27,9 +31,13 @@ auto SPC7110::main() -> void {
addClocks(1);
}
auto SPC7110::step(uint clocks) -> void {
clock += clocks * (uint64_t)cpu.frequency;
}
auto SPC7110::addClocks(uint clocks) -> void {
step(clocks);
synchronize(cpu);
synchronizeCPU();
}
auto SPC7110::unload() -> void {
@@ -96,7 +104,7 @@ auto SPC7110::power() -> void {
}
auto SPC7110::read(uint addr, uint8 data) -> uint8 {
cpu.synchronize(*this);
cpu.synchronizeCoprocessors();
if((addr & 0xff0000) == 0x500000) addr = 0x4800; //$50:0000-ffff == $4800
if((addr & 0xff0000) == 0x580000) addr = 0x4808; //$58:0000-ffff == $4808
addr = 0x4800 | (addr & 0x3f); //$00-3f,80-bf:4800-483f
@@ -180,7 +188,7 @@ auto SPC7110::read(uint addr, uint8 data) -> uint8 {
}
auto SPC7110::write(uint addr, uint8 data) -> void {
cpu.synchronize(*this);
cpu.synchronizeCoprocessors();
if((addr & 0xff0000) == 0x500000) addr = 0x4800; //$50:0000-ffff == $4800
if((addr & 0xff0000) == 0x580000) addr = 0x4808; //$58:0000-ffff == $4808
addr = 0x4800 | (addr & 0x3f); //$00-3f,80-bf:4800-483f

View File

@@ -4,8 +4,10 @@ struct SPC7110 : Thread {
SPC7110();
~SPC7110();
auto synchronizeCPU() -> void;
static auto Enter() -> void;
auto main() -> void;
auto step(uint clocks) -> void;
auto unload() -> void;
auto power() -> void;

View File

@@ -1,5 +1,5 @@
auto SuperFX::stop() -> void {
cpu.r.irq = 1;
cpu.irq(1);
}
auto SuperFX::color(uint8 source) -> uint8 {

View File

@@ -1,5 +1,5 @@
auto SuperFX::readIO(uint addr, uint8) -> uint8 {
cpu.synchronize(*this);
cpu.synchronizeCoprocessors();
addr = 0x3000 | addr & 0x3ff;
if(addr >= 0x3100 && addr <= 0x32ff) {
@@ -18,7 +18,7 @@ auto SuperFX::readIO(uint addr, uint8) -> uint8 {
case 0x3031: {
uint8 r = regs.sfr >> 8;
regs.sfr.irq = 0;
cpu.r.irq = 0;
cpu.irq(0);
return r;
}
@@ -51,7 +51,7 @@ auto SuperFX::readIO(uint addr, uint8) -> uint8 {
}
auto SuperFX::writeIO(uint addr, uint8 data) -> void {
cpu.synchronize(*this);
cpu.synchronizeCoprocessors();
addr = 0x3000 | addr & 0x3ff;
if(addr >= 0x3100 && addr <= 0x32ff) {

View File

@@ -2,8 +2,8 @@ auto SuperFX::read(uint addr, uint8 data) -> uint8 {
if((addr & 0xc00000) == 0x000000) { //$00-3f:0000-7fff,:8000-ffff
while(!regs.scmr.ron) {
step(6);
synchronize(cpu);
if(scheduler.synchronizing()) break;
synchronizeCPU();
if(synchronizing()) break;
}
return rom.read((((addr & 0x3f0000) >> 1) | (addr & 0x7fff)) & romMask);
}
@@ -11,8 +11,8 @@ auto SuperFX::read(uint addr, uint8 data) -> uint8 {
if((addr & 0xe00000) == 0x400000) { //$40-5f:0000-ffff
while(!regs.scmr.ron) {
step(6);
synchronize(cpu);
if(scheduler.synchronizing()) break;
synchronizeCPU();
if(synchronizing()) break;
}
return rom.read(addr & romMask);
}
@@ -20,8 +20,8 @@ auto SuperFX::read(uint addr, uint8 data) -> uint8 {
if((addr & 0xe00000) == 0x600000) { //$60-7f:0000-ffff
while(!regs.scmr.ran) {
step(6);
synchronize(cpu);
if(scheduler.synchronizing()) break;
synchronizeCPU();
if(synchronizing()) break;
}
return ram.read(addr & ramMask);
}
@@ -33,8 +33,8 @@ auto SuperFX::write(uint addr, uint8 data) -> void {
if((addr & 0xe00000) == 0x600000) { //$60-7f:0000-ffff
while(!regs.scmr.ran) {
step(6);
synchronize(cpu);
if(scheduler.synchronizing()) break;
synchronizeCPU();
if(synchronizing()) break;
}
return ram.write(addr & ramMask, data);
}

View File

@@ -1,4 +1,5 @@
#include <sfc/sfc.hpp>
#include <processor/gsu/gsu.cpp>
namespace SuperFamicom {
@@ -10,6 +11,10 @@ namespace SuperFamicom {
#include "serialization.cpp"
SuperFX superfx;
auto SuperFX::synchronizeCPU() -> void {
if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread);
}
auto SuperFX::Enter() -> void {
while(true) scheduler.synchronize(), superfx.main();
}
@@ -37,8 +42,10 @@ auto SuperFX::unload() -> void {
}
auto SuperFX::power() -> void {
double overclock = max(1.0, min(8.0, configuration.hacks.superfx.overclock / 100.0));
GSU::power();
create(SuperFX::Enter, Frequency);
create(SuperFX::Enter, Frequency * overclock);
romMask = rom.size() - 1;
ramMask = ram.size() - 1;

View File

@@ -2,7 +2,10 @@ struct SuperFX : Processor::GSU, Thread {
ReadableMemory rom;
WritableMemory ram;
inline auto synchronizing() const -> bool { return scheduler.mode == Scheduler::Mode::SynchronizeAll; }
//superfx.cpp
auto synchronizeCPU() -> void;
static auto Enter() -> void;
auto main() -> void;
auto unload() -> void;

View File

@@ -14,8 +14,8 @@ auto SuperFX::step(uint clocks) -> void {
}
}
Thread::step(clocks);
synchronize(cpu);
clock += clocks * (uint64_t)cpu.frequency;
synchronizeCPU();
}
auto SuperFX::syncROMBuffer() -> void {

View File

@@ -10,38 +10,54 @@ CPU cpu;
#include "irq.cpp"
#include "serialization.cpp"
auto CPU::synchronizeSMP() -> void {
if(smp.clock < 0) co_switch(smp.thread);
}
auto CPU::synchronizePPU() -> void {
if(ppu.clock < 0) co_switch(ppu.thread);
}
auto CPU::synchronizeCoprocessors() -> void {
for(auto coprocessor : coprocessors) {
if(coprocessor->clock < 0) co_switch(coprocessor->thread);
}
}
auto CPU::Enter() -> void {
while(true) scheduler.synchronize(), cpu.main();
while(true) {
if(scheduler.mode == Scheduler::Mode::SynchronizeCPU) {
scheduler.leave(Scheduler::Event::Synchronize);
}
cpu.main();
}
}
auto CPU::main() -> void {
if(r.wai) return instructionWait();
if(r.stp) return instructionStop();
if(!status.interruptPending) return instruction();
if(status.interruptPending) {
status.interruptPending = false;
if(status.nmiPending) {
status.nmiPending = false;
r.vector = r.e ? 0xfffa : 0xffea;
interrupt();
} else if(status.irqPending) {
status.irqPending = false;
r.vector = r.e ? 0xfffe : 0xffee;
interrupt();
} else if(status.resetPending) {
status.resetPending = false;
for(uint repeat : range(22)) step<6,0>(); //step(132);
r.vector = 0xfffc;
interrupt();
} else if(status.powerPending) {
status.powerPending = false;
for(uint repeat : range(31)) step<6,0>(); //step(186);
r.pc.l = bus.read(0xfffc, r.mdr);
r.pc.h = bus.read(0xfffd, r.mdr);
}
if(status.nmiPending) {
status.nmiPending = 0;
r.vector = r.e ? 0xfffa : 0xffea;
return interrupt();
}
instruction();
if(status.irqPending) {
status.irqPending = 0;
r.vector = r.e ? 0xfffe : 0xffee;
return interrupt();
}
if(status.resetPending) {
status.resetPending = 0;
for(uint repeat : range(22)) step<6,0>(); //step(132);
r.vector = 0xfffc;
return interrupt();
}
status.interruptPending = 0;
}
auto CPU::load() -> bool {
@@ -58,8 +74,8 @@ auto CPU::power(bool reset) -> void {
PPUcounter::reset();
PPUcounter::scanline = {&CPU::scanline, this};
function<auto (uint, uint8) -> uint8> reader;
function<auto (uint, uint8) -> void> writer;
function<uint8 (uint, uint8)> reader;
function<void (uint, uint8)> writer;
reader = {&CPU::readRAM, this};
writer = {&CPU::writeRAM, this};
@@ -80,6 +96,15 @@ auto CPU::power(bool reset) -> void {
if(!reset) random.array(wram, sizeof(wram));
if(configuration.hacks.hotfixes) {
//Dirt Racer (Europe) relies on uninitialized memory containing certain values to boot without freezing.
//the game itself is broken and will fail to run sometimes on real hardware, but for the sake of expedience,
//WRAM is initialized to a constant value that will allow this game to always boot in successfully.
if(cartridge.headerTitle() == "DIRT RACER") {
for(auto& byte : wram) byte = 0xff;
}
}
for(uint n : range(8)) {
channels[n] = {};
if(n != 7) channels[n].next = channels[n + 1];
@@ -90,13 +115,11 @@ auto CPU::power(bool reset) -> void {
alu = {};
status = {};
status.lineClocks = lineclocks();
status.dramRefreshPosition = (version == 1 ? 530 : 538);
status.hdmaSetupPosition = (version == 1 ? 12 + 8 - dmaCounter() : 12 + dmaCounter());
status.hdmaPosition = 1104;
status.powerPending = reset == 0;
status.resetPending = reset == 1;
status.interruptPending = true;
status.resetPending = 1;
status.interruptPending = 1;
}
}

View File

@@ -2,9 +2,12 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter {
inline auto interruptPending() const -> bool override { return status.interruptPending; }
inline auto pio() const -> uint8 { return io.pio; }
inline auto refresh() const -> bool { return status.dramRefresh == 1; }
inline auto synchronizing() const -> bool override { return scheduler.synchronizing(); }
inline auto synchronizing() const -> bool override { return scheduler.mode == Scheduler::Mode::SynchronizeCPU; }
//cpu.cpp
auto synchronizeSMP() -> void;
auto synchronizePPU() -> void;
auto synchronizeCoprocessors() -> void;
static auto Enter() -> void;
auto main() -> void;
auto load() -> bool;
@@ -22,8 +25,8 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter {
//memory.cpp
auto idle() -> void override;
auto read(uint24 addr) -> uint8 override;
auto write(uint24 addr, uint8 data) -> void override;
auto read(uint addr) -> uint8 override;
auto write(uint addr, uint8 data) -> void override;
auto readDisassembler(uint addr) -> uint8 override;
//io.cpp
@@ -37,7 +40,6 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter {
auto writeDMA(uint address, uint8 data) -> void;
//timing.cpp
inline auto dmaClocks() const -> uint;
inline auto dmaCounter() const -> uint;
inline auto joypadCounter() const -> uint;
@@ -48,7 +50,6 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter {
alwaysinline auto aluEdge() -> void;
alwaysinline auto dmaEdge() -> void;
alwaysinline auto lastCycle() -> void;
//irq.cpp
alwaysinline auto pollInterrupts() -> void;
@@ -58,6 +59,7 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter {
alwaysinline auto nmiTest() -> bool;
alwaysinline auto irqTest() -> bool;
alwaysinline auto lastCycle() -> void;
//joypad.cpp
auto joypadEdge() -> void;
@@ -68,6 +70,11 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter {
uint8 wram[128 * 1024];
vector<Thread*> coprocessors;
struct Overclocking {
uint counter = 0;
uint target = 0;
} overclocking;
private:
uint version = 2; //allowed: 1, 2
@@ -78,18 +85,17 @@ private:
struct Status {
uint clockCount = 0;
uint lineClocks = 0;
bool irqLock = false;
bool irqLock = 0;
uint dramRefreshPosition = 0;
uint dramRefresh = 0; //0 = not refreshed; 1 = refresh active; 2 = refresh inactive
uint hdmaSetupPosition = 0;
bool hdmaSetupTriggered = false;
bool hdmaSetupTriggered = 0;
uint hdmaPosition = 0;
bool hdmaTriggered = false;
bool hdmaTriggered = 0;
boolean nmiValid = 0;
boolean nmiLine = 0;
@@ -103,18 +109,16 @@ private:
boolean irqPending = 0;
boolean irqHold = 0;
bool powerPending = false;
bool resetPending = false;
bool resetPending = 0;
bool interruptPending = 0;
bool interruptPending = false;
bool dmaActive = false;
bool dmaPending = false;
bool hdmaPending = false;
bool dmaActive = 0;
bool dmaPending = 0;
bool hdmaPending = 0;
bool hdmaMode = 0; //0 = init, 1 = run
bool autoJoypadActive = false;
bool autoJoypadLatch = false;
bool autoJoypadActive = 0;
bool autoJoypadLatch = 0;
uint autoJoypadCounter = 0;
} status;

View File

@@ -14,6 +14,7 @@ auto CPU::hdmaActive() -> bool {
}
auto CPU::dmaRun() -> void {
counter.dma += 8;
step<8,0>();
dmaEdge();
for(auto& channel : channels) channel.dmaRun();
@@ -25,12 +26,14 @@ auto CPU::hdmaReset() -> void {
}
auto CPU::hdmaSetup() -> void {
counter.dma += 8;
step<8,0>();
for(auto& channel : channels) channel.hdmaSetup();
status.irqLock = true;
}
auto CPU::hdmaRun() -> void {
counter.dma += 8;
step<8,0>();
for(auto& channel : channels) channel.hdmaTransfer();
for(auto& channel : channels) channel.hdmaAdvance();
@@ -40,8 +43,14 @@ auto CPU::hdmaRun() -> void {
//
template<uint Clocks, bool Synchronize>
auto CPU::Channel::step() -> void { return cpu.step<Clocks, Synchronize>(); }
auto CPU::Channel::edge() -> void { return cpu.dmaEdge(); }
auto CPU::Channel::step() -> void {
cpu.counter.dma += Clocks;
cpu.step<Clocks, Synchronize>();
}
auto CPU::Channel::edge() -> void {
cpu.dmaEdge();
}
auto CPU::Channel::validA(uint24 address) -> bool {
//A-bus cannot access the B-bus or CPU I/O registers
@@ -175,6 +184,6 @@ auto CPU::Channel::hdmaTransfer() -> void {
auto CPU::Channel::hdmaAdvance() -> void {
if(!hdmaActive()) return;
lineCounter--;
hdmaDoTransfer = bit1(lineCounter,7);
hdmaDoTransfer = bool(lineCounter & 0x80);
hdmaReload();
}

View File

@@ -3,7 +3,7 @@ auto CPU::readRAM(uint addr, uint8 data) -> uint8 {
}
auto CPU::readAPU(uint addr, uint8 data) -> uint8 {
synchronize(smp);
synchronizeSMP();
return smp.portRead(addr & 3);
}
@@ -41,21 +41,21 @@ auto CPU::readCPU(uint addr, uint8 data) -> uint8 {
data |= (vcounter() >= ppu.vdisp()) << 7; //vblank
return data;
case 0x4213: return io.pio; //RDIO
case 0x4213: return io.pio; //RDIO
case 0x4214: return bit8(io.rddiv,0); //RDDIVL
case 0x4215: return bit8(io.rddiv,1); //RDDIVH
case 0x4216: return bit8(io.rdmpy,0); //RDMPYL
case 0x4217: return bit8(io.rdmpy,1); //RDMPYH
case 0x4214: return io.rddiv >> 0; //RDDIVL
case 0x4215: return io.rddiv >> 8; //RDDIVH
case 0x4216: return io.rdmpy >> 0; //RDMPYL
case 0x4217: return io.rdmpy >> 8; //RDMPYH
case 0x4218: return bit8(io.joy1,0); //JOY1L
case 0x4219: return bit8(io.joy1,1); //JOY1H
case 0x421a: return bit8(io.joy2,0); //JOY2L
case 0x421b: return bit8(io.joy2,1); //JOY2H
case 0x421c: return bit8(io.joy3,0); //JOY3L
case 0x421d: return bit8(io.joy3,1); //JOY3H
case 0x421e: return bit8(io.joy4,0); //JOY4L
case 0x421f: return bit8(io.joy4,1); //JOY4H
case 0x4218: return io.joy1 >> 0; //JOY1L
case 0x4219: return io.joy1 >> 8; //JOY1H
case 0x421a: return io.joy2 >> 0; //JOY2L
case 0x421b: return io.joy2 >> 8; //JOY2H
case 0x421c: return io.joy3 >> 0; //JOY3L
case 0x421d: return io.joy3 >> 8; //JOY3H
case 0x421e: return io.joy4 >> 0; //JOY4L
case 0x421f: return io.joy4 >> 8; //JOY4H
}
@@ -77,18 +77,18 @@ auto CPU::readDMA(uint addr, uint8 data) -> uint8 {
| channel.direction << 7
);
case 0x4301: return channel.targetAddress; //BBADx
case 0x4302: return bit8(channel.sourceAddress,0); //A1TxL
case 0x4303: return bit8(channel.sourceAddress,1); //A1TxH
case 0x4304: return channel.sourceBank; //A1Bx
case 0x4305: return bit8(channel.transferSize,0); //DASxL
case 0x4306: return bit8(channel.transferSize,1); //DASxH
case 0x4307: return channel.indirectBank; //DASBx
case 0x4308: return bit8(channel.hdmaAddress,0); //A2AxL
case 0x4309: return bit8(channel.hdmaAddress,1); //A2AxH
case 0x430a: return channel.lineCounter; //NTRLx
case 0x430b: return channel.unknown; //???x
case 0x430f: return channel.unknown; //???x ($43xb mirror)
case 0x4301: return channel.targetAddress; //BBADx
case 0x4302: return channel.sourceAddress >> 0; //A1TxL
case 0x4303: return channel.sourceAddress >> 8; //A1TxH
case 0x4304: return channel.sourceBank; //A1Bx
case 0x4305: return channel.transferSize >> 0; //DASxL
case 0x4306: return channel.transferSize >> 8; //DASxH
case 0x4307: return channel.indirectBank; //DASBx
case 0x4308: return channel.hdmaAddress >> 0; //A2AxL
case 0x4309: return channel.hdmaAddress >> 8; //A2AxH
case 0x430a: return channel.lineCounter; //NTRLx
case 0x430b: return channel.unknown; //???x
case 0x430f: return channel.unknown; //???x ($43xb mirror)
}
@@ -100,7 +100,7 @@ auto CPU::writeRAM(uint addr, uint8 data) -> void {
}
auto CPU::writeAPU(uint addr, uint8 data) -> void {
synchronize(smp);
synchronizeSMP();
return smp.portWrite(addr & 3, data);
}
@@ -111,15 +111,15 @@ auto CPU::writeCPU(uint addr, uint8 data) -> void {
return bus.write(0x7e0000 | io.wramAddress++, data);
case 0x2181: //WMADDL
bits(io.wramAddress,0-7) = data;
io.wramAddress = io.wramAddress & 0x1ff00 | data << 0;
return;
case 0x2182: //WMADDM
bits(io.wramAddress,8-15) = data;
io.wramAddress = io.wramAddress & 0x100ff | data << 8;
return;
case 0x2183: //WMADDH
bit1(io.wramAddress,16) = bit1(data,0);
io.wramAddress = io.wramAddress & 0x0ffff | (data & 1) << 16;
return;
case 0x4016: //JOYSER0
@@ -136,7 +136,7 @@ auto CPU::writeCPU(uint addr, uint8 data) -> void {
return;
case 0x4201: //WRIO
if(bit1(io.pio,7) && !bit1(data,7)) ppu.latchCounters();
if((io.pio & 0x80) && !(data & 0x80)) ppu.latchCounters();
io.pio = data;
return;
@@ -149,18 +149,22 @@ auto CPU::writeCPU(uint addr, uint8 data) -> void {
if(alu.mpyctr || alu.divctr) return;
io.wrmpyb = data;
io.rddiv = (io.wrmpyb << 8) | io.wrmpya;
io.rddiv = io.wrmpyb << 8 | io.wrmpya;
alu.mpyctr = 8; //perform multiplication over the next eight cycles
alu.shift = io.wrmpyb;
if(!configuration.hacks.cpu.fastMath) {
alu.mpyctr = 8; //perform multiplication over the next eight cycles
alu.shift = io.wrmpyb;
} else {
io.rdmpy = io.wrmpya * io.wrmpyb;
}
return;
case 0x4204: //WRDIVL
bit8(io.wrdiva,0) = data;
io.wrdiva = io.wrdiva & 0xff00 | data << 0;
return;
case 0x4205: //WRDIVH
bit8(io.wrdiva,1) = data;
io.wrdiva = io.wrdiva & 0x00ff | data << 8;
return;
case 0x4206: //WRDIVB
@@ -169,37 +173,47 @@ auto CPU::writeCPU(uint addr, uint8 data) -> void {
io.wrdivb = data;
alu.divctr = 16; //perform division over the next sixteen cycles
alu.shift = io.wrdivb << 16;
if(!configuration.hacks.cpu.fastMath) {
alu.divctr = 16; //perform division over the next sixteen cycles
alu.shift = io.wrdivb << 16;
} else {
if(io.wrdivb) {
io.rddiv = io.wrdiva / io.wrdivb;
io.rdmpy = io.wrdiva % io.wrdivb;
} else {
io.rddiv = 0xffff;
io.rdmpy = io.wrdiva;
}
}
return;
case 0x4207: //HTIMEL
io.htime = (io.htime >> 2) - 1;
bits(io.htime,0-7) = data;
io.htime = io.htime & 0x100 | data << 0;
io.htime = (io.htime + 1) << 2;
return;
case 0x4208: //HTIMEH
io.htime = (io.htime >> 2) - 1;
bit1(io.htime,8) = data & 1;
io.htime = io.htime & 0x0ff | (data & 1) << 8;
io.htime = (io.htime + 1) << 2;
return;
case 0x4209: //VTIMEL
bits(io.vtime,0-7) = data;
io.vtime = io.vtime & 0x100 | data << 0;
return;
case 0x420a: //VTIMEH
bit1(io.vtime,8) = data & 1;
io.vtime = io.vtime & 0x0ff | (data & 1) << 8;
return;
case 0x420b: //DMAEN
for(auto n : range(8)) channels[n].dmaEnable = bit1(data,n);
for(auto n : range(8)) channels[n].dmaEnable = bool(data & 1 << n);
if(data) status.dmaPending = true;
return;
case 0x420c: //HDMAEN
for(auto n : range(8)) channels[n].hdmaEnable = bit1(data,n);
for(auto n : range(8)) channels[n].hdmaEnable = bool(data & 1 << n);
return;
case 0x420d: //MEMSEL
@@ -215,12 +229,12 @@ auto CPU::writeDMA(uint addr, uint8 data) -> void {
switch(addr & 0xff8f) {
case 0x4300: //DMAPx
channel.transferMode = bits(data,0-2);
channel.fixedTransfer = bit1(data,3);
channel.reverseTransfer = bit1(data,4);
channel.unused = bit1(data,5);
channel.indirect = bit1(data,6);
channel.direction = bit1(data,7);
channel.transferMode = data >> 0 & 7;
channel.fixedTransfer = data >> 3 & 1;
channel.reverseTransfer = data >> 4 & 1;
channel.unused = data >> 5 & 1;
channel.indirect = data >> 6 & 1;
channel.direction = data >> 7 & 1;
return;
case 0x4301: //BBADx
@@ -228,11 +242,11 @@ auto CPU::writeDMA(uint addr, uint8 data) -> void {
return;
case 0x4302: //A1TxL
bit8(channel.sourceAddress,0) = data;
channel.sourceAddress = channel.sourceAddress & 0xff00 | data << 0;
return;
case 0x4303: //A1TxH
bit8(channel.sourceAddress,1) = data;
channel.sourceAddress = channel.sourceAddress & 0x00ff | data << 8;
return;
case 0x4304: //A1Bx
@@ -240,11 +254,11 @@ auto CPU::writeDMA(uint addr, uint8 data) -> void {
return;
case 0x4305: //DASxL
bit8(channel.transferSize,0) = data;
channel.transferSize = channel.transferSize & 0xff00 | data << 0;
return;
case 0x4306: //DASxH
bit8(channel.transferSize,1) = data;
channel.transferSize = channel.transferSize & 0x00ff | data << 8;
return;
case 0x4307: //DASBx
@@ -252,11 +266,11 @@ auto CPU::writeDMA(uint addr, uint8 data) -> void {
return;
case 0x4308: //A2AxL
bit8(channel.hdmaAddress,0) = data;
channel.hdmaAddress = channel.hdmaAddress & 0xff00 | data << 0;
return;
case 0x4309: //A2AxH
bit8(channel.hdmaAddress,1) = data;
channel.hdmaAddress = channel.hdmaAddress & 0x00ff | data << 8;
return;
case 0x430a: //NTRLx

View File

@@ -5,48 +5,52 @@
//it is used to emulate hardware communication delay between opcode and interrupt units.
auto CPU::pollInterrupts() -> void {
//NMI hold
if(status.nmiHold.lower() && io.nmiEnable) status.nmiTransition = true;
if(status.nmiHold.lower() && io.nmiEnable) {
status.nmiTransition = 1;
}
//NMI test
if(status.nmiValid.flip(vcounter(2) >= ppu.vdisp())) {
if(status.nmiLine = status.nmiValid) status.nmiHold = true; //hold /NMI for four cycles
if(status.nmiLine = status.nmiValid) status.nmiHold = 1; //hold /NMI for four cycles
}
//IRQ hold
status.irqHold = false;
if(status.irqLine && io.irqEnable) status.irqTransition = true;
status.irqHold = 0;
if(status.irqLine && io.irqEnable) {
status.irqTransition = 1;
}
//IRQ test
if(status.irqValid.raise(io.irqEnable
&& (!io.virqEnable || vcounter(10) == io.vtime)
&& (!io.hirqEnable || hcounter(10) == io.htime)
&& (vcounter(6) || hcounter(6)) //IRQs cannot trigger on last dot of fields
)) status.irqLine = status.irqHold = true; //hold /IRQ for four cycles
)) status.irqLine = status.irqHold = 1; //hold /IRQ for four cycles
}
auto CPU::nmitimenUpdate(uint8 data) -> void {
io.hirqEnable = bit1(data,4);
io.virqEnable = bit1(data,5);
io.hirqEnable = data & 0x10;
io.virqEnable = data & 0x20;
io.irqEnable = io.hirqEnable || io.virqEnable;
if(io.virqEnable && !io.hirqEnable && status.irqLine) {
status.irqTransition = true;
status.irqTransition = 1;
} else if(!io.irqEnable) {
status.irqLine = false;
status.irqTransition = false;
status.irqLine = 0;
status.irqTransition = 0;
}
if(io.nmiEnable.raise(bit1(data,7)) && status.nmiLine) {
status.nmiTransition = true;
if(io.nmiEnable.raise(data & 0x80) && status.nmiLine) {
status.nmiTransition = 1;
}
status.irqLock = true;
status.irqLock = 1;
}
auto CPU::rdnmi() -> bool {
bool result = status.nmiLine;
if(!status.nmiHold) {
status.nmiLine = false;
status.nmiLine = 0;
}
return result;
}
@@ -54,22 +58,34 @@ auto CPU::rdnmi() -> bool {
auto CPU::timeup() -> bool {
bool result = status.irqLine;
if(!status.irqHold) {
status.irqLine = false;
status.irqTransition = false;
status.irqLine = 0;
status.irqTransition = 0;
}
return result;
}
auto CPU::nmiTest() -> bool {
if(!status.nmiTransition) return false;
status.nmiTransition = false;
r.wai = false;
return true;
if(!status.nmiTransition) return 0;
status.nmiTransition = 0;
r.wai = 0;
return 1;
}
auto CPU::irqTest() -> bool {
if(!status.irqTransition && !r.irq) return false;
status.irqTransition = false;
r.wai = false;
if(!status.irqTransition && !r.irq) return 0;
status.irqTransition = 0;
r.wai = 0;
return !r.p.i;
}
//used to test for NMI/IRQ, which can trigger on the edge of every opcode.
//test one cycle early to simulate two-stage pipeline of the 65816 CPU.
//
//status.irqLock is used to simulate hardware delay before interrupts can
//trigger during certain events (immediately after DMA, writes to $4200, etc)
auto CPU::lastCycle() -> void {
if(!status.irqLock) {
if(nmiTest()) status.nmiPending = 1, status.interruptPending = 1;
if(irqTest()) status.irqPending = 1, status.interruptPending = 1;
}
}

View File

@@ -1,14 +1,12 @@
auto CPU::idle() -> void {
status.irqLock = false;
status.clockCount = 6;
dmaEdge();
step<6,0>();
status.irqLock = 0;
aluEdge();
}
auto CPU::read(uint24 address) -> uint8 {
status.irqLock = false;
auto CPU::read(uint address) -> uint8 {
if(address & 0x408000) {
if(address & 0x800000 && io.fastROM) {
status.clockCount = 6;
@@ -38,6 +36,7 @@ auto CPU::read(uint24 address) -> uint8 {
step<8,1>();
}
status.irqLock = 0;
auto data = bus.read(address, r.mdr);
step<4,0>();
aluEdge();
@@ -46,8 +45,7 @@ auto CPU::read(uint24 address) -> uint8 {
return data;
}
auto CPU::write(uint24 address, uint8 data) -> void {
status.irqLock = false;
auto CPU::write(uint address, uint8 data) -> void {
aluEdge();
if(address & 0x408000) {
@@ -79,6 +77,7 @@ auto CPU::write(uint24 address, uint8 data) -> void {
step<12,1>();
}
status.irqLock = 0;
bus.write(address, r.mdr = data);
}

View File

@@ -11,7 +11,6 @@ auto CPU::serialize(serializer& s) -> void {
s.integer(counter.dma);
s.integer(status.clockCount);
s.integer(status.lineClocks);
s.integer(status.irqLock);
@@ -36,9 +35,7 @@ auto CPU::serialize(serializer& s) -> void {
s.boolean(status.irqPending);
s.boolean(status.irqHold);
s.integer(status.powerPending);
s.integer(status.resetPending);
s.integer(status.interruptPending);
s.integer(status.dmaActive);

View File

@@ -1,12 +1,3 @@
//the number of clock cycles that have elapsed since (H)DMA began
auto CPU::dmaClocks() const -> uint {
if(counter.cpu >= counter.dma) {
return counter.cpu - counter.dma;
} else {
return 0 - counter.cpu + counter.dma;
}
}
//DMA clock divider
auto CPU::dmaCounter() const -> uint {
return counter.cpu & 7;
@@ -27,13 +18,31 @@ auto CPU::stepOnce() -> void {
template<uint Clocks, bool Synchronize>
auto CPU::step() -> void {
static_assert(Clocks == 2 || Clocks == 4 || Clocks == 6 || Clocks == 8 || Clocks == 10 || Clocks == 12);
for(auto coprocessor : coprocessors) {
coprocessor->clock -= Clocks * (uint64)coprocessor->frequency;
}
if(overclocking.target) {
overclocking.counter += Clocks;
if(overclocking.counter < overclocking.target) {
if constexpr(Synchronize) {
if(configuration.hacks.coprocessor.delayedSync) return;
synchronizeCoprocessors();
}
return;
}
}
if constexpr(Clocks >= 2) stepOnce();
if constexpr(Clocks >= 4) stepOnce();
if constexpr(Clocks >= 6) stepOnce();
if constexpr(Clocks >= 8) stepOnce();
if constexpr(Clocks >= 10) stepOnce();
if constexpr(Clocks >= 12) stepOnce();
Thread::step(Clocks);
smp.clock -= Clocks * (uint64)smp.frequency;
ppu.clock -= Clocks;
if(!status.dramRefresh && hcounter() >= status.dramRefreshPosition) {
//note: pattern should technically be 5-3, 5-3, 5-3, 5-3, 5-3 per logic analyzer
@@ -46,8 +55,8 @@ auto CPU::step() -> void {
}
if constexpr(Synchronize) {
if(configuration.hacks.coprocessors.delayedSync) return;
for(auto coprocessor : coprocessors) synchronize(*coprocessor);
if(configuration.hacks.coprocessor.delayedSync) return;
synchronizeCoprocessors();
}
}
@@ -64,12 +73,10 @@ auto CPU::step(uint clocks) -> void {
//called by ppu.tick() when Hcounter=0
auto CPU::scanline() -> void {
status.lineClocks = lineclocks();
//forcefully sync S-CPU to other processors, in case chips are not communicating
synchronize(smp);
synchronize(ppu);
for(auto coprocessor : coprocessors) synchronize(*coprocessor);
synchronizeSMP();
synchronizePPU();
synchronizeCoprocessors();
if(vcounter() == 0) {
//HDMA setup triggers once every frame
@@ -88,6 +95,17 @@ auto CPU::scanline() -> void {
status.hdmaPosition = 1104;
status.hdmaTriggered = false;
}
//overclocking
if(vcounter() == (Region::NTSC() ? 261 : 311)) {
overclocking.counter = 0;
overclocking.target = 0;
double overclock = configuration.hacks.cpu.overclock / 100.0;
if(overclock > 1.0) {
int clocks = (Region::NTSC() ? 262 : 312) * 1364;
overclocking.target = clocks * overclock - clocks;
}
}
}
auto CPU::aluEdge() -> void {
@@ -123,12 +141,11 @@ auto CPU::dmaEdge() -> void {
status.hdmaPending = false;
if(hdmaEnable()) {
if(!dmaEnable()) {
counter.dma = counter.cpu;
step(8 - dmaCounter());
step(counter.dma = 8 - dmaCounter());
}
status.hdmaMode == 0 ? hdmaSetup() : hdmaRun();
if(!dmaEnable()) {
step(status.clockCount - dmaClocks() % status.clockCount);
step(status.clockCount - counter.dma % status.clockCount);
status.dmaActive = false;
}
}
@@ -137,10 +154,9 @@ auto CPU::dmaEdge() -> void {
if(status.dmaPending) {
status.dmaPending = false;
if(dmaEnable()) {
counter.dma = counter.cpu;
step(8 - dmaCounter());
step(counter.dma = 8 - dmaCounter());
dmaRun();
step(status.clockCount - dmaClocks() % status.clockCount);
step(status.clockCount - counter.dma % status.clockCount);
status.dmaActive = false;
}
}
@@ -203,16 +219,3 @@ auto CPU::joypadEdge() -> void {
status.autoJoypadCounter++;
}
}
//used to test for NMI/IRQ, which can trigger on the edge of every opcode.
//test one cycle early to simulate two-stage pipeline of x816 CPU.
//
//status.irq_lock is used to simulate hardware delay before interrupts can
//trigger during certain events (immediately after DMA, writes to $4200, etc)
auto CPU::lastCycle() -> void {
if(!status.irqLock) {
if(nmiTest()) status.nmiPending = true;
if(irqTest()) status.irqPending = true;
status.interruptPending = (status.nmiPending || status.irqPending);
}
}

View File

@@ -415,7 +415,7 @@ MISC_CLOCK( 30 )
inline VOICE_CLOCK( V1 )
{
m.t_dir_addr = m.t_dir * 0x100 + m.t_srcn * 4;
m.t_dir_addr = (m.t_dir * 0x100 + m.t_srcn * 4) & 0xffff;
m.t_srcn = VREG(v->regs,srcn);
}
inline VOICE_CLOCK( V2 )
@@ -612,7 +612,7 @@ VOICE_CLOCK(V9_V6_V3) { voice_V9(v); voice_V6(v+1); voice_V3(v+2); }
//// Echo
// Current echo buffer pointer for left/right channel
#define ECHO_PTR( ch ) (&m.ram [m.t_echo_ptr + ch * 2])
#define ECHO_PTR( ch ) (&m.echo [m.t_echo_ptr + ch * 2])
// Sample in echo history buffer, where 0 is the oldest
#define ECHO_FIR( i ) (m.echo_hist_pos [i])
@@ -835,9 +835,10 @@ void SPC_DSP::run( int clocks_remain )
//// Setup
void SPC_DSP::init( void* ram_64k )
void SPC_DSP::init( void* ram_64k, void* echo_64k )
{
m.ram = (uint8_t*) ram_64k;
m.ram = (uint8_t*) ram_64k;
m.echo = (uint8_t*) echo_64k;
mute_voices( 0 );
disable_surround( false );
set_output( 0, 0 );
@@ -861,7 +862,8 @@ void SPC_DSP::init( void* ram_64k )
void SPC_DSP::soft_reset_common()
{
require( m.ram ); // init() must have been called already
require( m.ram ); // init() must have been called already
require( m.echo );
m.noise = 0x4000;
m.echo_hist_pos = m.echo_hist;

View File

@@ -15,7 +15,7 @@ public:
// Setup
// Initializes DSP and has it use the 64K RAM provided
void init( void* ram_64k );
void init( void* ram_64k, void* echo_64k );
// Sets destination for output samples. If out is NULL or out_size is 0,
// doesn't generate any.
@@ -178,7 +178,8 @@ private:
voice_t voices [voice_count];
// non-emulation state
uint8_t* ram; // 64K shared RAM between DSP and SMP
uint8_t* ram; // 64K shared RAM between DSP and SMP
uint8_t* echo; // should point at the same memory as ram; used for older hack compatibility
int mute_mask;
sample_t* out;
sample_t* out_end;
@@ -231,6 +232,9 @@ private:
void echo_30();
void soft_reset_common();
public:
bool mute() { return m.regs[r_flg] & 0x40; }
};
#include <assert.h>

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