mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-09-17 09:12:04 +02:00
Compare commits
58 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
675662e739 | ||
|
409dd371b9 | ||
|
18d2ab6435 | ||
|
1e626e75ef | ||
|
c6d90d3ff1 | ||
|
29caf77751 | ||
|
29b13083d5 | ||
|
3883172a4e | ||
|
fa77fc6a8f | ||
|
56c9a5195e | ||
|
a6ebce428f | ||
|
0788627898 | ||
|
1195c46ac0 | ||
|
90f094b931 | ||
|
2bb1606552 | ||
|
08e5e81609 | ||
|
03aaaba889 | ||
|
556ab4c809 | ||
|
23467b5b1f | ||
|
5ae1bcd973 | ||
|
64e3658bcb | ||
|
ab515b59d4 | ||
|
bb7b2f2e60 | ||
|
639b9db961 | ||
|
f857f35e72 | ||
|
5dc27a9fb3 | ||
|
ce3dba130c | ||
|
f9ca7a4927 | ||
|
db1c37c799 | ||
|
7a98db84ac | ||
|
b73493b492 | ||
|
95831d3675 | ||
|
ab25877af4 | ||
|
5ba538ee39 | ||
|
04b85ade6b | ||
|
0b088b6b55 | ||
|
252f479b22 | ||
|
46dbe00f14 | ||
|
66ad62b79f | ||
|
27b2d11839 | ||
|
96c381f91f | ||
|
5757949023 | ||
|
bad27bb8f3 | ||
|
06ceb7d29e | ||
|
e030428054 | ||
|
24dce7dd92 | ||
|
b577e6d5d0 | ||
|
db988d9588 | ||
|
f6303518d5 | ||
|
9e8913cea0 | ||
|
0e56b27228 | ||
|
fe81130f54 | ||
|
216b472418 | ||
|
bc7456246c | ||
|
a7b30b069c | ||
|
454b39cb78 | ||
|
f65b7a8528 | ||
|
7e88bdde09 |
41
.cirrus.yml
41
.cirrus.yml
@@ -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
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
patreon: byuu
|
17
LICENSE.txt
17
LICENSE.txt
@@ -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
|
||||
|
16
README.md
16
README.md
@@ -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)
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
|
@@ -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)
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
};
|
||||
|
@@ -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}}
|
@@ -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 {
|
||||
|
@@ -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();
|
||||
|
@@ -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 {}
|
||||
|
@@ -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));
|
||||
}
|
||||
|
||||
|
@@ -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));
|
||||
}
|
||||
|
||||
|
@@ -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 {}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
@@ -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
|
@@ -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>;
|
||||
|
@@ -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:
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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; }
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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 {
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
};
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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();
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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 {
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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) {
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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 {
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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) {
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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())) {
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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 {
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
auto SuperFX::stop() -> void {
|
||||
cpu.r.irq = 1;
|
||||
cpu.irq(1);
|
||||
}
|
||||
|
||||
auto SuperFX::color(uint8 source) -> uint8 {
|
||||
|
@@ -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) {
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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 {
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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();
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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
Reference in New Issue
Block a user