mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-09-18 09:31:34 +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
|
- 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:
|
compile_script:
|
||||||
- make -C bsnes
|
- make -C bsnes local=false
|
||||||
|
|
||||||
package_script:
|
package_script:
|
||||||
- mkdir bsnes-nightly
|
- mkdir bsnes-nightly
|
||||||
@@ -14,67 +14,68 @@ linux-x86_64-binaries_task:
|
|||||||
- mkdir bsnes-nightly/Firmware
|
- mkdir bsnes-nightly/Firmware
|
||||||
- cp -a bsnes/out/bsnes bsnes-nightly/bsnes
|
- cp -a bsnes/out/bsnes bsnes-nightly/bsnes
|
||||||
- cp -a bsnes/Database/* bsnes-nightly/Database
|
- 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:
|
bsnes-nightly_artifacts:
|
||||||
path: "bsnes-nightly/**"
|
path: "bsnes-nightly.zip"
|
||||||
|
|
||||||
freebsd-x86_64-binaries_task:
|
freebsd-x86_64-binaries_task:
|
||||||
freebsd_instance:
|
freebsd_instance:
|
||||||
image: freebsd-12-0-release-amd64
|
image: freebsd-12-0-release-amd64
|
||||||
|
|
||||||
setup_script:
|
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:
|
compile_script:
|
||||||
- gmake -C bsnes
|
- gmake -C bsnes local=false
|
||||||
|
|
||||||
package_script:
|
package_script:
|
||||||
- mkdir bsnes-nightly
|
- mkdir bsnes-nightly
|
||||||
- mkdir bsnes-nightly/Database
|
- mkdir bsnes-nightly/Database
|
||||||
- mkdir bsnes-nightly/Firmware
|
- mkdir bsnes-nightly/Firmware
|
||||||
- cp -a bsnes/out/bsnes bsnes-nightly/bsnes
|
- cp -a bsnes/out/bsnes bsnes-nightly/bsnes
|
||||||
- cp -a bsnes/Database/* bsnes-nightly/Database/
|
- 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:
|
bsnes-nightly_artifacts:
|
||||||
path: "bsnes-nightly/**"
|
path: "bsnes-nightly.zip"
|
||||||
|
|
||||||
windows-x86_64-binaries_task:
|
windows-x86_64-binaries_task:
|
||||||
container:
|
container:
|
||||||
image: ubuntu:latest
|
image: ubuntu:latest
|
||||||
|
|
||||||
setup_script:
|
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:
|
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:
|
package_script:
|
||||||
- mkdir bsnes-nightly
|
- mkdir bsnes-nightly
|
||||||
- mkdir bsnes-nightly/Database
|
- mkdir bsnes-nightly/Database
|
||||||
- mkdir bsnes-nightly/Firmware
|
- mkdir bsnes-nightly/Firmware
|
||||||
- cp -a bsnes/out/bsnes bsnes-nightly/bsnes.exe
|
- cp -a bsnes/out/bsnes bsnes-nightly/bsnes.exe
|
||||||
- cp -a bsnes/Database/* bsnes-nightly/Database/
|
- 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:
|
bsnes-nightly_artifacts:
|
||||||
path: "bsnes-nightly/**"
|
path: "bsnes-nightly.zip"
|
||||||
|
|
||||||
macOS-x86_64-binaries_task:
|
macOS-x86_64-binaries_task:
|
||||||
osx_instance:
|
osx_instance:
|
||||||
image: mojave-base
|
image: mojave-base
|
||||||
|
|
||||||
compile_script:
|
compile_script:
|
||||||
- make -C bsnes
|
- make -C bsnes local=false
|
||||||
|
|
||||||
package_script:
|
package_script:
|
||||||
- mkdir bsnes-nightly
|
- mkdir bsnes-nightly
|
||||||
- cp -a bsnes/out/bsnes.app bsnes-nightly/
|
- cp -a bsnes/out/bsnes.app bsnes-nightly
|
||||||
- cp -a GPLv3.txt bsnes-nightly/
|
- cp -a GPLv3.txt bsnes-nightly
|
||||||
|
- zip -r bsnes-nightly.zip bsnes-nightly
|
||||||
|
|
||||||
bsnes-nightly_artifacts:
|
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
|
bsnes - Super Nintendo emulator
|
||||||
icarus - Game library importer for higan
|
|
||||||
|
|
||||||
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
|
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
|
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
|
||||||
libco - C cooperative threading library
|
nall - C++ template library
|
||||||
nall - C++ template library
|
ruby - Hardware abstraction library
|
||||||
ruby - Hardware abstraction layer
|
hiro - User interface library
|
||||||
|
|
||||||
Copyright © 2006-2019 byuu
|
Copyright © 2006-2019 byuu
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for
|
Permission to use, copy, modify, and/or distribute this software for
|
||||||
any purpose with or without fee is hereby granted, provided that the
|
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
|
If stability is required, please download the latest stable release from the
|
||||||
[official website.](https://bsnes.byuu.org)
|
[official website.](https://bsnes.byuu.org)
|
||||||
|
|
||||||
Nightly builds are available via Cirrus CI.
|
|
||||||
|
|
||||||
- [Nightly builds](https://cirrus-ci.com/github/byuu/bsnes)
|
|
||||||
|
|
||||||
Unique Features
|
Unique Features
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
@@ -60,12 +56,13 @@ Standard Features
|
|||||||
- Sprite limit disable support
|
- Sprite limit disable support
|
||||||
- Cubic audio interpolation support
|
- Cubic audio interpolation support
|
||||||
- Optional high-level emulation of most SNES coprocessors
|
- Optional high-level emulation of most SNES coprocessors
|
||||||
- SuperFX overclocking of up to 800%
|
- CPU, SA1, and SuperFX overclocking support
|
||||||
- Frame advance support
|
- Frame advance support
|
||||||
- Screenshot support
|
- Screenshot support
|
||||||
- Cheat code search support
|
- Cheat code search support
|
||||||
- Movie recording and playback support
|
- Movie recording and playback support
|
||||||
- Rewind support
|
- Rewind support
|
||||||
|
- HiDPI support
|
||||||
|
|
||||||
Links
|
Links
|
||||||
-----
|
-----
|
||||||
@@ -73,3 +70,12 @@ Links
|
|||||||
- [Official website](https://bsnes.byuu.org)
|
- [Official website](https://bsnes.byuu.org)
|
||||||
- [Official git repository](https://github.com/byuu/bsnes)
|
- [Official git repository](https://github.com/byuu/bsnes)
|
||||||
- [Donations](https://patreon.com/byuu)
|
- [Donations](https://patreon.com/byuu)
|
||||||
|
|
||||||
|
Nightly Builds
|
||||||
|
--------------
|
||||||
|
|
||||||
|
- [Download](https://cirrus-ci.com/github/byuu/bsnes/master)
|
||||||
|
- 
|
||||||
|
- 
|
||||||
|
- 
|
||||||
|
- 
|
||||||
|
@@ -2,6 +2,7 @@ target := bsnes
|
|||||||
binary := application
|
binary := application
|
||||||
build := performance
|
build := performance
|
||||||
openmp := true
|
openmp := true
|
||||||
|
local := true
|
||||||
flags += -I. -I..
|
flags += -I. -I..
|
||||||
|
|
||||||
# in order for this to work, obj/lzma.o must be omitted or bsnes will hang on startup.
|
# 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
|
options += -pg
|
||||||
endif
|
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
|
nall.path := ../nall
|
||||||
include $(nall.path)/GNUmakefile
|
include $(nall.path)/GNUmakefile
|
||||||
|
|
||||||
@@ -30,7 +36,6 @@ else ifeq ($(platform),macos)
|
|||||||
endif
|
endif
|
||||||
else ifneq ($(filter $(platform),linux bsd),)
|
else ifneq ($(filter $(platform),linux bsd),)
|
||||||
ifeq ($(binary),application)
|
ifeq ($(binary),application)
|
||||||
flags += -march=native
|
|
||||||
options += -Wl,-export-dynamic
|
options += -Wl,-export-dynamic
|
||||||
options += -lX11 -lXext
|
options += -lX11 -lXext
|
||||||
else ifeq ($(binary),library)
|
else ifeq ($(binary),library)
|
||||||
|
@@ -10,45 +10,45 @@ Audio::~Audio() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto Audio::reset(Interface* interface) -> void {
|
auto Audio::reset(Interface* interface) -> void {
|
||||||
this->interface = interface;
|
_interface = interface;
|
||||||
streams.reset();
|
_streams.reset();
|
||||||
channels = 0;
|
_channels = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Audio::setFrequency(double frequency) -> void {
|
auto Audio::setFrequency(double frequency) -> void {
|
||||||
this->frequency = frequency;
|
_frequency = frequency;
|
||||||
for(auto& stream : streams) {
|
for(auto& stream : _streams) {
|
||||||
stream->setFrequency(stream->inputFrequency, frequency);
|
stream->setFrequency(stream->inputFrequency, frequency);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Audio::setVolume(double volume) -> void {
|
auto Audio::setVolume(double volume) -> void {
|
||||||
this->volume = volume;
|
_volume = volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Audio::setBalance(double balance) -> void {
|
auto Audio::setBalance(double balance) -> void {
|
||||||
this->balance = balance;
|
_balance = balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Audio::createStream(uint channels, double frequency) -> shared_pointer<Stream> {
|
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;
|
shared_pointer<Stream> stream = new Stream;
|
||||||
stream->reset(channels, frequency, this->frequency);
|
stream->reset(channels, frequency, _frequency);
|
||||||
streams.append(stream);
|
_streams.append(stream);
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Audio::process() -> void {
|
auto Audio::process() -> void {
|
||||||
while(streams) {
|
while(_streams) {
|
||||||
for(auto& stream : streams) {
|
for(auto& stream : _streams) {
|
||||||
if(!stream->pending()) return;
|
if(!stream->pending()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
double samples[channels];
|
double samples[_channels];
|
||||||
for(auto& sample : samples) sample = 0.0;
|
for(auto& sample : samples) sample = 0.0;
|
||||||
|
|
||||||
for(auto& stream : streams) {
|
for(auto& stream : _streams) {
|
||||||
double buffer[channels];
|
double buffer[_channels];
|
||||||
uint length = stream->read(buffer), offset = 0;
|
uint length = stream->read(buffer), offset = 0;
|
||||||
|
|
||||||
for(auto& sample : samples) {
|
for(auto& sample : samples) {
|
||||||
@@ -57,16 +57,16 @@ auto Audio::process() -> void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto c : range(channels)) {
|
for(auto c : range(_channels)) {
|
||||||
samples[c] = max(-1.0, min(+1.0, samples[c] * volume));
|
samples[c] = max(-1.0, min(+1.0, samples[c] * _volume));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(channels == 2) {
|
if(_channels == 2) {
|
||||||
if(balance < 0.0) samples[1] *= 1.0 + balance;
|
if(_balance < 0.0) samples[1] *= 1.0 + _balance;
|
||||||
if(balance > 0.0) samples[0] *= 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();
|
~Audio();
|
||||||
auto reset(Interface* interface) -> void;
|
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 setFrequency(double frequency) -> void;
|
||||||
auto setVolume(double volume) -> void;
|
auto setVolume(double volume) -> void;
|
||||||
auto setBalance(double balance) -> void;
|
auto setBalance(double balance) -> void;
|
||||||
@@ -26,14 +31,14 @@ struct Audio {
|
|||||||
private:
|
private:
|
||||||
auto process() -> void;
|
auto process() -> void;
|
||||||
|
|
||||||
Interface* interface = nullptr;
|
Interface* _interface = nullptr;
|
||||||
vector<shared_pointer<Stream>> streams;
|
vector<shared_pointer<Stream>> _streams;
|
||||||
|
|
||||||
uint channels = 0;
|
uint _channels = 0;
|
||||||
double frequency = 48000.0;
|
double _frequency = 48000.0;
|
||||||
|
|
||||||
double volume = 1.0;
|
double _volume = 1.0;
|
||||||
double balance = 0.0;
|
double _balance = 0.0;
|
||||||
|
|
||||||
friend class Stream;
|
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/platform.hpp>
|
||||||
#include <nall/adaptive-array.hpp>
|
#include <nall/adaptive-array.hpp>
|
||||||
#include <nall/any.hpp>
|
#include <nall/any.hpp>
|
||||||
#include <nall/bit-field.hpp>
|
|
||||||
#include <nall/chrono.hpp>
|
#include <nall/chrono.hpp>
|
||||||
#include <nall/dl.hpp>
|
#include <nall/dl.hpp>
|
||||||
#include <nall/endian.hpp>
|
#include <nall/endian.hpp>
|
||||||
@@ -23,7 +22,6 @@ using namespace nall;
|
|||||||
|
|
||||||
#include <libco/libco.h>
|
#include <libco/libco.h>
|
||||||
|
|
||||||
#include <emulator/bits.hpp>
|
|
||||||
#include <emulator/types.hpp>
|
#include <emulator/types.hpp>
|
||||||
#include <emulator/memory/readable.hpp>
|
#include <emulator/memory/readable.hpp>
|
||||||
#include <emulator/memory/writable.hpp>
|
#include <emulator/memory/writable.hpp>
|
||||||
@@ -31,13 +29,13 @@ using namespace nall;
|
|||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const string Name = "bsnes";
|
static const string Name = "bsnes";
|
||||||
static const string Version = "108";
|
static const string Version = "110";
|
||||||
static const string Author = "byuu";
|
static const string Author = "byuu";
|
||||||
static const string License = "GPLv3";
|
static const string License = "GPLv3";
|
||||||
static const string Website = "https://byuu.org";
|
static const string Website = "https://byuu.org";
|
||||||
|
|
||||||
//incremented only when serialization format changes
|
//incremented only when serialization format changes
|
||||||
static const string SerializerVersion = "108";
|
static const string SerializerVersion = "110";
|
||||||
|
|
||||||
namespace Constants {
|
namespace Constants {
|
||||||
namespace Colorburst {
|
namespace Colorburst {
|
||||||
|
@@ -37,6 +37,7 @@ struct Game {
|
|||||||
string sha256;
|
string sha256;
|
||||||
string label;
|
string label;
|
||||||
string name;
|
string name;
|
||||||
|
string title;
|
||||||
string region;
|
string region;
|
||||||
string revision;
|
string revision;
|
||||||
string board;
|
string board;
|
||||||
@@ -50,6 +51,7 @@ auto Game::load(string_view text) -> void {
|
|||||||
sha256 = document["game/sha256"].text();
|
sha256 = document["game/sha256"].text();
|
||||||
label = document["game/label"].text();
|
label = document["game/label"].text();
|
||||||
name = document["game/name"].text();
|
name = document["game/name"].text();
|
||||||
|
title = document["game/title"].text();
|
||||||
region = document["game/region"].text();
|
region = document["game/region"].text();
|
||||||
revision = document["game/revision"].text();
|
revision = document["game/revision"].text();
|
||||||
board = document["game/board"].text();
|
board = document["game/board"].text();
|
||||||
|
@@ -61,6 +61,7 @@ struct Interface {
|
|||||||
virtual auto hashes() -> vector<string> { return {}; }
|
virtual auto hashes() -> vector<string> { return {}; }
|
||||||
virtual auto manifests() -> vector<string> { return {}; }
|
virtual auto manifests() -> vector<string> { return {}; }
|
||||||
virtual auto titles() -> vector<string> { return {}; }
|
virtual auto titles() -> vector<string> { return {}; }
|
||||||
|
virtual auto title() -> string { return {}; }
|
||||||
virtual auto load() -> bool { return false; }
|
virtual auto load() -> bool { return false; }
|
||||||
virtual auto save() -> void {}
|
virtual auto save() -> void {}
|
||||||
virtual auto unload() -> void {}
|
virtual auto unload() -> void {}
|
||||||
|
@@ -24,14 +24,14 @@ struct Readable {
|
|||||||
memory::fill<T>(self.data, self.mask + 1, fill);
|
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)));
|
fp->read(self.data, min(fp->size(), self.size * sizeof(T)));
|
||||||
for(uint address = self.size; address <= self.mask; address++) {
|
for(uint address = self.size; address <= self.mask; address++) {
|
||||||
self.data[address] = self.data[mirror(address, self.size)];
|
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));
|
fp->write(self.data, self.size * sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -24,14 +24,14 @@ struct Writable {
|
|||||||
memory::fill<T>(self.data, self.mask + 1, fill);
|
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)));
|
fp->read(self.data, min(fp->size(), self.size * sizeof(T)));
|
||||||
for(uint address = self.size; address <= self.mask; address++) {
|
for(uint address = self.size; address <= self.mask; address++) {
|
||||||
self.data[address] = self.data[mirror(address, self.size)];
|
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));
|
fp->write(self.data, self.size * sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -14,7 +14,7 @@ struct Platform {
|
|||||||
};
|
};
|
||||||
|
|
||||||
virtual auto path(uint id) -> string { return ""; }
|
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 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 videoFrame(const uint16* data, uint pitch, uint width, uint height, uint scale) -> void {}
|
||||||
virtual auto audioFrame(const float* samples, uint channels) -> void {}
|
virtual auto audioFrame(const float* samples, uint channels) -> void {}
|
||||||
|
@@ -65,10 +65,10 @@ struct Random {
|
|||||||
if((random() & 1) == 0) hivalue = ~lovalue;
|
if((random() & 1) == 0) hivalue = ~lovalue;
|
||||||
|
|
||||||
for(uint32 address : range(size)) {
|
for(uint32 address : range(size)) {
|
||||||
uint8 value = bit1(address,lobit) ? lovalue : hivalue;
|
uint8 value = (address & 1ull << lobit) ? lovalue : hivalue;
|
||||||
if(bit1(address,hibit)) value = ~value;
|
if((address & 1ull << hibit)) value = ~value;
|
||||||
if((random() & 511) == 0) bit1(value,random() & 7) ^= 1;
|
if((random() & 511) == 0) value ^= 1 << (random() & 7);
|
||||||
if((random() & 2047) == 0) bit1(value,random() & 7) ^= 1;
|
if((random() & 2047) == 0) value ^= 1 << (random() & 7);
|
||||||
data[address] = value;
|
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 int6 = nall::Integer< 6>;
|
||||||
using int7 = nall::Integer< 7>;
|
using int7 = nall::Integer< 7>;
|
||||||
using int8 = int8_t;
|
using int8 = int8_t;
|
||||||
//using int8 = nall::Integer< 8>;
|
|
||||||
using int9 = nall::Integer< 9>;
|
using int9 = nall::Integer< 9>;
|
||||||
using int10 = nall::Integer<10>;
|
using int10 = nall::Integer<10>;
|
||||||
using int11 = nall::Integer<11>;
|
using int11 = nall::Integer<11>;
|
||||||
@@ -17,7 +16,6 @@ using int13 = nall::Integer<13>;
|
|||||||
using int14 = nall::Integer<14>;
|
using int14 = nall::Integer<14>;
|
||||||
using int15 = nall::Integer<15>;
|
using int15 = nall::Integer<15>;
|
||||||
using int16 = int16_t;
|
using int16 = int16_t;
|
||||||
//using int16 = nall::Integer<16>;
|
|
||||||
using int17 = nall::Integer<17>;
|
using int17 = nall::Integer<17>;
|
||||||
using int18 = nall::Integer<18>;
|
using int18 = nall::Integer<18>;
|
||||||
using int19 = nall::Integer<19>;
|
using int19 = nall::Integer<19>;
|
||||||
@@ -34,40 +32,8 @@ using int29 = nall::Integer<29>;
|
|||||||
using int30 = nall::Integer<30>;
|
using int30 = nall::Integer<30>;
|
||||||
using int31 = nall::Integer<31>;
|
using int31 = nall::Integer<31>;
|
||||||
using int32 = int32_t;
|
using int32 = int32_t;
|
||||||
//using int32 = nall::Integer<32>;
|
using int48 = nall::Integer<48>; //Cx4
|
||||||
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 int64 = int64_t;
|
using int64 = int64_t;
|
||||||
//using int64 = nall::Integer<64>;
|
|
||||||
|
|
||||||
using uint1 = nall::Natural< 1>;
|
using uint1 = nall::Natural< 1>;
|
||||||
using uint2 = nall::Natural< 2>;
|
using uint2 = nall::Natural< 2>;
|
||||||
@@ -77,7 +43,6 @@ using uint5 = nall::Natural< 5>;
|
|||||||
using uint6 = nall::Natural< 6>;
|
using uint6 = nall::Natural< 6>;
|
||||||
using uint7 = nall::Natural< 7>;
|
using uint7 = nall::Natural< 7>;
|
||||||
using uint8 = uint8_t;
|
using uint8 = uint8_t;
|
||||||
//using uint8 = nall::Natural< 8>;
|
|
||||||
using uint9 = nall::Natural< 9>;
|
using uint9 = nall::Natural< 9>;
|
||||||
using uint10 = nall::Natural<10>;
|
using uint10 = nall::Natural<10>;
|
||||||
using uint11 = nall::Natural<11>;
|
using uint11 = nall::Natural<11>;
|
||||||
@@ -86,7 +51,6 @@ using uint13 = nall::Natural<13>;
|
|||||||
using uint14 = nall::Natural<14>;
|
using uint14 = nall::Natural<14>;
|
||||||
using uint15 = nall::Natural<15>;
|
using uint15 = nall::Natural<15>;
|
||||||
using uint16 = uint16_t;
|
using uint16 = uint16_t;
|
||||||
//using uint16 = nall::Natural<16>;
|
|
||||||
using uint17 = nall::Natural<17>;
|
using uint17 = nall::Natural<17>;
|
||||||
using uint18 = nall::Natural<18>;
|
using uint18 = nall::Natural<18>;
|
||||||
using uint19 = nall::Natural<19>;
|
using uint19 = nall::Natural<19>;
|
||||||
@@ -103,37 +67,6 @@ using uint29 = nall::Natural<29>;
|
|||||||
using uint30 = nall::Natural<30>;
|
using uint30 = nall::Natural<30>;
|
||||||
using uint31 = nall::Natural<31>;
|
using uint31 = nall::Natural<31>;
|
||||||
using uint32 = uint32_t;
|
using uint32 = uint32_t;
|
||||||
//using uint32 = nall::Natural<32>;
|
using uint40 = nall::Natural<40>; //SA1
|
||||||
using uint33 = nall::Natural<33>;
|
using uint48 = nall::Natural<48>; //Cx4
|
||||||
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 uint64 = uint64_t;
|
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);
|
(gb->apu.is_active[GB_WAVE] ? (gb->apu.samples[GB_WAVE]) : 0);
|
||||||
case GB_IO_JOYP:
|
case GB_IO_JOYP:
|
||||||
GB_timing_sync(gb);
|
GB_timing_sync(gb);
|
||||||
|
return gb->io_registers[addr & 0xFF] | 0xC0;
|
||||||
case GB_IO_TMA:
|
case GB_IO_TMA:
|
||||||
case GB_IO_LCDC:
|
case GB_IO_LCDC:
|
||||||
case GB_IO_SCY:
|
case GB_IO_SCY:
|
||||||
|
@@ -6,6 +6,7 @@ struct SuperFamicom {
|
|||||||
|
|
||||||
auto manifest() const -> string;
|
auto manifest() const -> string;
|
||||||
auto region() const -> string;
|
auto region() const -> string;
|
||||||
|
auto videoRegion() const -> string;
|
||||||
auto revision() const -> string;
|
auto revision() const -> string;
|
||||||
auto board() const -> string;
|
auto board() const -> string;
|
||||||
auto title() const -> string;
|
auto title() const -> string;
|
||||||
@@ -139,6 +140,11 @@ auto SuperFamicom::manifest() const -> string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto SuperFamicom::region() 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;
|
string region;
|
||||||
|
|
||||||
char A = data[headerAddress + 0x02]; //game type
|
char A = data[headerAddress + 0x02]; //game type
|
||||||
@@ -186,6 +192,11 @@ auto SuperFamicom::region() const -> string {
|
|||||||
return region ? region : "NTSC";
|
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 {
|
auto SuperFamicom::revision() const -> string {
|
||||||
string revision;
|
string revision;
|
||||||
|
|
@@ -1,11 +1,11 @@
|
|||||||
auto ARM7TDMI::ADD(uint32 source, uint32 modify, bool carry) -> uint32 {
|
auto ARM7TDMI::ADD(uint32 source, uint32 modify, bool carry) -> uint32 {
|
||||||
uint32 result = source + modify + carry;
|
uint32 result = source + modify + carry;
|
||||||
if(cpsr().t || bit1(opcode,20)) {
|
if(cpsr().t || (opcode & 1 << 20)) {
|
||||||
uint32 overflow = ~(source ^ modify) & (source ^ result);
|
uint32 overflow = ~(source ^ modify) & (source ^ result);
|
||||||
cpsr().v = 1 << 31 & (overflow);
|
cpsr().v = 1 << 31 & (overflow);
|
||||||
cpsr().c = 1 << 31 & (overflow ^ source ^ modify ^ result);
|
cpsr().c = 1 << 31 & (overflow ^ source ^ modify ^ result);
|
||||||
cpsr().z = result == 0;
|
cpsr().z = result == 0;
|
||||||
cpsr().n = bit1(result,31);
|
cpsr().n = result >> 31;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -19,10 +19,10 @@ auto ARM7TDMI::ASR(uint32 source, uint8 shift) -> uint32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto ARM7TDMI::BIT(uint32 result) -> uint32 {
|
auto ARM7TDMI::BIT(uint32 result) -> uint32 {
|
||||||
if(cpsr().t || bit1(opcode,20)) {
|
if(cpsr().t || (opcode & 1 << 20)) {
|
||||||
cpsr().c = carry;
|
cpsr().c = carry;
|
||||||
cpsr().z = result == 0;
|
cpsr().z = result == 0;
|
||||||
cpsr().n = bit1(result,31);
|
cpsr().n = result >> 31;
|
||||||
}
|
}
|
||||||
return result;
|
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 >> 16 && multiplier >> 16 != 0xffff) idle();
|
||||||
if(multiplier >> 24 && multiplier >> 24 != 0xff) idle();
|
if(multiplier >> 24 && multiplier >> 24 != 0xff) idle();
|
||||||
product += multiplicand * multiplier;
|
product += multiplicand * multiplier;
|
||||||
if(cpsr().t || bit1(opcode,20)) {
|
if(cpsr().t || (opcode & 1 << 20)) {
|
||||||
cpsr().z = product == 0;
|
cpsr().z = product == 0;
|
||||||
cpsr().n = bit1(product,31);
|
cpsr().n = product >> 31;
|
||||||
}
|
}
|
||||||
return product;
|
return product;
|
||||||
}
|
}
|
||||||
|
@@ -148,14 +148,14 @@ struct ARM7TDMI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline auto operator=(uint32 data) -> PSR& {
|
inline auto operator=(uint32 data) -> PSR& {
|
||||||
m = bits(data,0-4);
|
m = data >> 0 & 31;
|
||||||
t = bit1(data,5);
|
t = data >> 5 & 1;
|
||||||
f = bit1(data,6);
|
f = data >> 6 & 1;
|
||||||
i = bit1(data,7);
|
i = data >> 7 & 1;
|
||||||
v = bit1(data,28);
|
v = data >> 28 & 1;
|
||||||
c = bit1(data,29);
|
c = data >> 29 & 1;
|
||||||
z = bit1(data,30);
|
z = data >> 30 & 1;
|
||||||
n = bit1(data,31);
|
n = data >> 31 & 1;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -186,7 +186,7 @@ auto ARM7TDMI::armDisassembleMoveMultiple
|
|||||||
(uint16 list, uint4 n, uint1 mode, uint1 writeback, uint1 type, uint1 up, uint1 pre) -> string {
|
(uint16 list, uint4 n, uint1 mode, uint1 writeback, uint1 type, uint1 up, uint1 pre) -> string {
|
||||||
string registers;
|
string registers;
|
||||||
for(auto index : range(16)) {
|
for(auto index : range(16)) {
|
||||||
if(bit1(list,index)) registers.append(_r[index], ",");
|
if((list & 1 << index)) registers.append(_r[index], ",");
|
||||||
}
|
}
|
||||||
registers.trimRight(",", 1L);
|
registers.trimRight(",", 1L);
|
||||||
return {mode ? "ldm" : "stm", _c,
|
return {mode ? "ldm" : "stm", _c,
|
||||||
@@ -358,7 +358,7 @@ auto ARM7TDMI::thumbDisassembleMoveMultiple
|
|||||||
(uint8 list, uint3 n, uint1 mode) -> string {
|
(uint8 list, uint3 n, uint1 mode) -> string {
|
||||||
string registers;
|
string registers;
|
||||||
for(uint m : range(8)) {
|
for(uint m : range(8)) {
|
||||||
if(bit1(list,m)) registers.append(_r[m], ",");
|
if((list & 1 << m)) registers.append(_r[m], ",");
|
||||||
}
|
}
|
||||||
registers.trimRight(",", 1L);
|
registers.trimRight(",", 1L);
|
||||||
return {mode ? "ldmia" : "stmia", " ", _r[n], "!,{", registers, "}"};
|
return {mode ? "ldmia" : "stmia", " ", _r[n], "!,{", registers, "}"};
|
||||||
@@ -395,7 +395,7 @@ auto ARM7TDMI::thumbDisassembleStackMultiple
|
|||||||
(uint8 list, uint1 lrpc, uint1 mode) -> string {
|
(uint8 list, uint1 lrpc, uint1 mode) -> string {
|
||||||
string registers;
|
string registers;
|
||||||
for(uint m : range(8)) {
|
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,");
|
if(lrpc) registers.append(!mode ? "lr," : "pc,");
|
||||||
registers.trimRight(",", 1L);
|
registers.trimRight(",", 1L);
|
||||||
|
@@ -68,8 +68,11 @@ auto ARM7TDMI::armInitialize() -> void {
|
|||||||
#define pattern(s) \
|
#define pattern(s) \
|
||||||
std::integral_constant<uint32_t, bit::test(s)>::value
|
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 \
|
#define arguments \
|
||||||
bits(opcode, 0-23), /* displacement */ \
|
bits(opcode, 0,23), /* displacement */ \
|
||||||
bit1(opcode,24) /* link */
|
bit1(opcode,24) /* link */
|
||||||
for(uint4 displacementLo : range(16))
|
for(uint4 displacementLo : range(16))
|
||||||
for(uint4 displacementHi : range(16))
|
for(uint4 displacementHi : range(16))
|
||||||
@@ -81,7 +84,7 @@ auto ARM7TDMI::armInitialize() -> void {
|
|||||||
#undef arguments
|
#undef arguments
|
||||||
|
|
||||||
#define arguments \
|
#define arguments \
|
||||||
bits(opcode, 0- 3) /* m */
|
bits(opcode, 0, 3) /* m */
|
||||||
{
|
{
|
||||||
auto opcode = pattern(".... 0001 0010 ---- ---- ---- 0001 ????");
|
auto opcode = pattern(".... 0001 0010 ---- ---- ---- 0001 ????");
|
||||||
bind(opcode, BranchExchangeRegister);
|
bind(opcode, BranchExchangeRegister);
|
||||||
@@ -89,12 +92,12 @@ auto ARM7TDMI::armInitialize() -> void {
|
|||||||
#undef arguments
|
#undef arguments
|
||||||
|
|
||||||
#define arguments \
|
#define arguments \
|
||||||
bits(opcode, 0- 7), /* immediate */ \
|
bits(opcode, 0, 7), /* immediate */ \
|
||||||
bits(opcode, 8-11), /* shift */ \
|
bits(opcode, 8,11), /* shift */ \
|
||||||
bits(opcode,12-15), /* d */ \
|
bits(opcode,12,15), /* d */ \
|
||||||
bits(opcode,16-19), /* n */ \
|
bits(opcode,16,19), /* n */ \
|
||||||
bit1(opcode,20), /* save */ \
|
bit1(opcode,20), /* save */ \
|
||||||
bits(opcode,21-24) /* mode */
|
bits(opcode,21,24) /* mode */
|
||||||
for(uint4 shiftHi : range(16))
|
for(uint4 shiftHi : range(16))
|
||||||
for(uint1 save : range(2))
|
for(uint1 save : range(2))
|
||||||
for(uint4 mode : range(16)) {
|
for(uint4 mode : range(16)) {
|
||||||
@@ -105,13 +108,13 @@ auto ARM7TDMI::armInitialize() -> void {
|
|||||||
#undef arguments
|
#undef arguments
|
||||||
|
|
||||||
#define arguments \
|
#define arguments \
|
||||||
bits(opcode, 0- 3), /* m */ \
|
bits(opcode, 0, 3), /* m */ \
|
||||||
bits(opcode, 5- 6), /* type */ \
|
bits(opcode, 5, 6), /* type */ \
|
||||||
bits(opcode, 7-11), /* shift */ \
|
bits(opcode, 7,11), /* shift */ \
|
||||||
bits(opcode,12-15), /* d */ \
|
bits(opcode,12,15), /* d */ \
|
||||||
bits(opcode,16-19), /* n */ \
|
bits(opcode,16,19), /* n */ \
|
||||||
bit1(opcode,20), /* save */ \
|
bit1(opcode,20), /* save */ \
|
||||||
bits(opcode,21-24) /* mode */
|
bits(opcode,21,24) /* mode */
|
||||||
for(uint2 type : range(4))
|
for(uint2 type : range(4))
|
||||||
for(uint1 shiftLo : range(2))
|
for(uint1 shiftLo : range(2))
|
||||||
for(uint1 save : range(2))
|
for(uint1 save : range(2))
|
||||||
@@ -123,13 +126,13 @@ auto ARM7TDMI::armInitialize() -> void {
|
|||||||
#undef arguments
|
#undef arguments
|
||||||
|
|
||||||
#define arguments \
|
#define arguments \
|
||||||
bits(opcode, 0- 3), /* m */ \
|
bits(opcode, 0, 3), /* m */ \
|
||||||
bits(opcode, 5- 6), /* type */ \
|
bits(opcode, 5, 6), /* type */ \
|
||||||
bits(opcode, 8-11), /* s */ \
|
bits(opcode, 8,11), /* s */ \
|
||||||
bits(opcode,12-15), /* d */ \
|
bits(opcode,12,15), /* d */ \
|
||||||
bits(opcode,16-19), /* n */ \
|
bits(opcode,16,19), /* n */ \
|
||||||
bit1(opcode,20), /* save */ \
|
bit1(opcode,20), /* save */ \
|
||||||
bits(opcode,21-24) /* mode */
|
bits(opcode,21,24) /* mode */
|
||||||
for(uint2 type : range(4))
|
for(uint2 type : range(4))
|
||||||
for(uint1 save : range(2))
|
for(uint1 save : range(2))
|
||||||
for(uint4 mode : range(16)) {
|
for(uint4 mode : range(16)) {
|
||||||
@@ -140,10 +143,10 @@ auto ARM7TDMI::armInitialize() -> void {
|
|||||||
#undef arguments
|
#undef arguments
|
||||||
|
|
||||||
#define 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 */ \
|
bit1(opcode, 5), /* half */ \
|
||||||
bits(opcode,12-15), /* d */ \
|
bits(opcode,12,15), /* d */ \
|
||||||
bits(opcode,16-19), /* n */ \
|
bits(opcode,16,19), /* n */ \
|
||||||
bit1(opcode,21), /* writeback */ \
|
bit1(opcode,21), /* writeback */ \
|
||||||
bit1(opcode,23), /* up */ \
|
bit1(opcode,23), /* up */ \
|
||||||
bit1(opcode,24) /* pre */
|
bit1(opcode,24) /* pre */
|
||||||
@@ -157,10 +160,10 @@ auto ARM7TDMI::armInitialize() -> void {
|
|||||||
#undef arguments
|
#undef arguments
|
||||||
|
|
||||||
#define arguments \
|
#define arguments \
|
||||||
bits(opcode, 0- 3), /* m */ \
|
bits(opcode, 0, 3), /* m */ \
|
||||||
bit1(opcode, 5), /* half */ \
|
bit1(opcode, 5), /* half */ \
|
||||||
bits(opcode,12-15), /* d */ \
|
bits(opcode,12,15), /* d */ \
|
||||||
bits(opcode,16-19), /* n */ \
|
bits(opcode,16,19), /* n */ \
|
||||||
bit1(opcode,21), /* writeback */ \
|
bit1(opcode,21), /* writeback */ \
|
||||||
bit1(opcode,23), /* up */ \
|
bit1(opcode,23), /* up */ \
|
||||||
bit1(opcode,24) /* pre */
|
bit1(opcode,24) /* pre */
|
||||||
@@ -174,9 +177,9 @@ auto ARM7TDMI::armInitialize() -> void {
|
|||||||
#undef arguments
|
#undef arguments
|
||||||
|
|
||||||
#define arguments \
|
#define arguments \
|
||||||
bits(opcode, 0- 3), /* m */ \
|
bits(opcode, 0, 3), /* m */ \
|
||||||
bits(opcode,12-15), /* d */ \
|
bits(opcode,12,15), /* d */ \
|
||||||
bits(opcode,16-19), /* n */ \
|
bits(opcode,16,19), /* n */ \
|
||||||
bit1(opcode,22) /* byte */
|
bit1(opcode,22) /* byte */
|
||||||
for(uint1 byte : range(2)) {
|
for(uint1 byte : range(2)) {
|
||||||
auto opcode = pattern(".... 0001 0?00 ???? ???? ---- 1001 ????") | byte << 22;
|
auto opcode = pattern(".... 0001 0?00 ???? ???? ---- 1001 ????") | byte << 22;
|
||||||
@@ -185,9 +188,9 @@ auto ARM7TDMI::armInitialize() -> void {
|
|||||||
#undef arguments
|
#undef arguments
|
||||||
|
|
||||||
#define 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 */ \
|
||||||
bits(opcode,12-15), /* d */ \
|
bits(opcode,12,15), /* d */ \
|
||||||
bits(opcode,16-19), /* n */ \
|
bits(opcode,16,19), /* n */ \
|
||||||
bit1(opcode,20), /* mode */ \
|
bit1(opcode,20), /* mode */ \
|
||||||
bit1(opcode,21), /* writeback */ \
|
bit1(opcode,21), /* writeback */ \
|
||||||
bit1(opcode,23), /* up */ \
|
bit1(opcode,23), /* up */ \
|
||||||
@@ -202,9 +205,9 @@ auto ARM7TDMI::armInitialize() -> void {
|
|||||||
#undef arguments
|
#undef arguments
|
||||||
|
|
||||||
#define arguments \
|
#define arguments \
|
||||||
bits(opcode, 0- 3), /* m */ \
|
bits(opcode, 0, 3), /* m */ \
|
||||||
bits(opcode,12-15), /* d */ \
|
bits(opcode,12,15), /* d */ \
|
||||||
bits(opcode,16-19), /* n */ \
|
bits(opcode,16,19), /* n */ \
|
||||||
bit1(opcode,20), /* mode */ \
|
bit1(opcode,20), /* mode */ \
|
||||||
bit1(opcode,21), /* writeback */ \
|
bit1(opcode,21), /* writeback */ \
|
||||||
bit1(opcode,23), /* up */ \
|
bit1(opcode,23), /* up */ \
|
||||||
@@ -219,9 +222,9 @@ auto ARM7TDMI::armInitialize() -> void {
|
|||||||
#undef arguments
|
#undef arguments
|
||||||
|
|
||||||
#define arguments \
|
#define arguments \
|
||||||
bits(opcode, 0-11), /* immediate */ \
|
bits(opcode, 0,11), /* immediate */ \
|
||||||
bits(opcode,12-15), /* d */ \
|
bits(opcode,12,15), /* d */ \
|
||||||
bits(opcode,16-19), /* n */ \
|
bits(opcode,16,19), /* n */ \
|
||||||
bit1(opcode,20), /* mode */ \
|
bit1(opcode,20), /* mode */ \
|
||||||
bit1(opcode,21), /* writeback */ \
|
bit1(opcode,21), /* writeback */ \
|
||||||
bit1(opcode,22), /* byte */ \
|
bit1(opcode,22), /* byte */ \
|
||||||
@@ -240,8 +243,8 @@ auto ARM7TDMI::armInitialize() -> void {
|
|||||||
#undef arguments
|
#undef arguments
|
||||||
|
|
||||||
#define arguments \
|
#define arguments \
|
||||||
bits(opcode, 0-15), /* list */ \
|
bits(opcode, 0,15), /* list */ \
|
||||||
bits(opcode,16-19), /* n */ \
|
bits(opcode,16,19), /* n */ \
|
||||||
bit1(opcode,20), /* mode */ \
|
bit1(opcode,20), /* mode */ \
|
||||||
bit1(opcode,21), /* writeback */ \
|
bit1(opcode,21), /* writeback */ \
|
||||||
bit1(opcode,22), /* type */ \
|
bit1(opcode,22), /* type */ \
|
||||||
@@ -260,11 +263,11 @@ auto ARM7TDMI::armInitialize() -> void {
|
|||||||
#undef arguments
|
#undef arguments
|
||||||
|
|
||||||
#define arguments \
|
#define arguments \
|
||||||
bits(opcode, 0- 3), /* m */ \
|
bits(opcode, 0, 3), /* m */ \
|
||||||
bits(opcode, 5- 6), /* type */ \
|
bits(opcode, 5, 6), /* type */ \
|
||||||
bits(opcode, 7-11), /* shift */ \
|
bits(opcode, 7,11), /* shift */ \
|
||||||
bits(opcode,12-15), /* d */ \
|
bits(opcode,12,15), /* d */ \
|
||||||
bits(opcode,16-19), /* n */ \
|
bits(opcode,16,19), /* n */ \
|
||||||
bit1(opcode,20), /* mode */ \
|
bit1(opcode,20), /* mode */ \
|
||||||
bit1(opcode,21), /* writeback */ \
|
bit1(opcode,21), /* writeback */ \
|
||||||
bit1(opcode,22), /* byte */ \
|
bit1(opcode,22), /* byte */ \
|
||||||
@@ -284,7 +287,7 @@ auto ARM7TDMI::armInitialize() -> void {
|
|||||||
#undef arguments
|
#undef arguments
|
||||||
|
|
||||||
#define arguments \
|
#define arguments \
|
||||||
bits(opcode,12-15), /* d */ \
|
bits(opcode,12,15), /* d */ \
|
||||||
bit1(opcode,22) /* mode */
|
bit1(opcode,22) /* mode */
|
||||||
for(uint1 mode : range(2)) {
|
for(uint1 mode : range(2)) {
|
||||||
auto opcode = pattern(".... 0001 0?00 ---- ???? ---- 0000 ----") | mode << 22;
|
auto opcode = pattern(".... 0001 0?00 ---- ???? ---- 0000 ----") | mode << 22;
|
||||||
@@ -293,9 +296,9 @@ auto ARM7TDMI::armInitialize() -> void {
|
|||||||
#undef arguments
|
#undef arguments
|
||||||
|
|
||||||
#define arguments \
|
#define arguments \
|
||||||
bits(opcode, 0- 7), /* immediate */ \
|
bits(opcode, 0, 7), /* immediate */ \
|
||||||
bits(opcode, 8-11), /* rotate */ \
|
bits(opcode, 8,11), /* rotate */ \
|
||||||
bits(opcode,16-19), /* field */ \
|
bits(opcode,16,19), /* field */ \
|
||||||
bit1(opcode,22) /* mode */
|
bit1(opcode,22) /* mode */
|
||||||
for(uint4 immediateHi : range(16))
|
for(uint4 immediateHi : range(16))
|
||||||
for(uint1 mode : range(2)) {
|
for(uint1 mode : range(2)) {
|
||||||
@@ -305,8 +308,8 @@ auto ARM7TDMI::armInitialize() -> void {
|
|||||||
#undef arguments
|
#undef arguments
|
||||||
|
|
||||||
#define arguments \
|
#define arguments \
|
||||||
bits(opcode, 0- 3), /* m */ \
|
bits(opcode, 0, 3), /* m */ \
|
||||||
bits(opcode,16-19), /* field */ \
|
bits(opcode,16,19), /* field */ \
|
||||||
bit1(opcode,22) /* mode */
|
bit1(opcode,22) /* mode */
|
||||||
for(uint1 mode : range(2)) {
|
for(uint1 mode : range(2)) {
|
||||||
auto opcode = pattern(".... 0001 0?10 ???? ---- ---- 0000 ????") | mode << 22;
|
auto opcode = pattern(".... 0001 0?10 ???? ---- ---- 0000 ????") | mode << 22;
|
||||||
@@ -315,10 +318,10 @@ auto ARM7TDMI::armInitialize() -> void {
|
|||||||
#undef arguments
|
#undef arguments
|
||||||
|
|
||||||
#define arguments \
|
#define arguments \
|
||||||
bits(opcode, 0- 3), /* m */ \
|
bits(opcode, 0, 3), /* m */ \
|
||||||
bits(opcode, 8-11), /* s */ \
|
bits(opcode, 8,11), /* s */ \
|
||||||
bits(opcode,12-15), /* n */ \
|
bits(opcode,12,15), /* n */ \
|
||||||
bits(opcode,16-19), /* d */ \
|
bits(opcode,16,19), /* d */ \
|
||||||
bit1(opcode,20), /* save */ \
|
bit1(opcode,20), /* save */ \
|
||||||
bit1(opcode,21) /* accumulate */
|
bit1(opcode,21) /* accumulate */
|
||||||
for(uint1 save : range(2))
|
for(uint1 save : range(2))
|
||||||
@@ -329,10 +332,10 @@ auto ARM7TDMI::armInitialize() -> void {
|
|||||||
#undef arguments
|
#undef arguments
|
||||||
|
|
||||||
#define arguments \
|
#define arguments \
|
||||||
bits(opcode, 0- 3), /* m */ \
|
bits(opcode, 0, 3), /* m */ \
|
||||||
bits(opcode, 8-11), /* s */ \
|
bits(opcode, 8,11), /* s */ \
|
||||||
bits(opcode,12-15), /* l */ \
|
bits(opcode,12,15), /* l */ \
|
||||||
bits(opcode,16-19), /* h */ \
|
bits(opcode,16,19), /* h */ \
|
||||||
bit1(opcode,20), /* save */ \
|
bit1(opcode,20), /* save */ \
|
||||||
bit1(opcode,21), /* accumulate */ \
|
bit1(opcode,21), /* accumulate */ \
|
||||||
bit1(opcode,22) /* sign */
|
bit1(opcode,22) /* sign */
|
||||||
@@ -345,7 +348,7 @@ auto ARM7TDMI::armInitialize() -> void {
|
|||||||
#undef arguments
|
#undef arguments
|
||||||
|
|
||||||
#define arguments \
|
#define arguments \
|
||||||
bits(opcode, 0-23) /* immediate */
|
bits(opcode, 0,23) /* immediate */
|
||||||
for(uint4 immediateLo : range(16))
|
for(uint4 immediateLo : range(16))
|
||||||
for(uint4 immediateHi : range(16)) {
|
for(uint4 immediateHi : range(16)) {
|
||||||
auto opcode = pattern(".... 1111 ???? ???? ???? ???? ???? ????") | immediateLo << 4 | immediateHi << 20;
|
auto opcode = pattern(".... 1111 ???? ???? ???? ???? ???? ????") | immediateLo << 4 | immediateHi << 20;
|
||||||
@@ -356,7 +359,7 @@ auto ARM7TDMI::armInitialize() -> void {
|
|||||||
#define arguments
|
#define arguments
|
||||||
for(uint12 id : range(4096)) {
|
for(uint12 id : range(4096)) {
|
||||||
if(armInstruction[id]) continue;
|
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);
|
bind(opcode, Undefined);
|
||||||
}
|
}
|
||||||
#undef arguments
|
#undef arguments
|
||||||
@@ -386,7 +389,7 @@ auto ARM7TDMI::thumbInitialize() -> void {
|
|||||||
for(uint4 m : range(16))
|
for(uint4 m : range(16))
|
||||||
for(uint2 mode : range(4)) {
|
for(uint2 mode : range(4)) {
|
||||||
if(mode == 3) continue;
|
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);
|
bind(opcode, ALUExtended, d, m, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -533,6 +536,9 @@ auto ARM7TDMI::thumbInitialize() -> void {
|
|||||||
bind(opcode, Undefined);
|
bind(opcode, Undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef bit1
|
||||||
|
#undef bits
|
||||||
|
|
||||||
#undef bind
|
#undef bind
|
||||||
#undef pattern
|
#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
|
case 15: r(d) = BIT(~rm); break; //MVN
|
||||||
}
|
}
|
||||||
|
|
||||||
if(exception() && d == 15 && bit1(opcode,20)) {
|
if(exception() && d == 15 && (opcode & 1 << 20)) {
|
||||||
cpsr() = spsr();
|
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;
|
if(mode && (cpsr().m == PSR::USR || cpsr().m == PSR::SYS)) return;
|
||||||
PSR& psr = mode ? spsr() : cpsr();
|
PSR& psr = mode ? spsr() : cpsr();
|
||||||
|
|
||||||
if(field.bit(0)) {
|
if(field & 1) {
|
||||||
if(mode || privileged()) {
|
if(mode || privileged()) {
|
||||||
psr.m = bits(data,0-4);
|
psr.m = data >> 0 & 31;
|
||||||
psr.t = bit1(data,5);
|
psr.t = data >> 5 & 1;
|
||||||
psr.f = bit1(data,6);
|
psr.f = data >> 6 & 1;
|
||||||
psr.i = bit1(data,7);
|
psr.i = data >> 7 & 1;
|
||||||
if(!mode && psr.t) r(15).data += 2;
|
if(!mode && psr.t) r(15).data += 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(field.bit(3)) {
|
if(field & 8) {
|
||||||
psr.v = bit1(data,28);
|
psr.v = data >> 28 & 1;
|
||||||
psr.c = bit1(data,29);
|
psr.c = data >> 29 & 1;
|
||||||
psr.z = bit1(data,30);
|
psr.z = data >> 30 & 1;
|
||||||
psr.n = bit1(data,31);
|
psr.n = data >> 31 & 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,13 +193,13 @@ auto ARM7TDMI::armInstructionMoveMultiple
|
|||||||
|
|
||||||
auto cpsrMode = cpsr().m;
|
auto cpsrMode = cpsr().m;
|
||||||
bool usr = false;
|
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(type && mode == 0) usr = true;
|
||||||
if(usr) cpsr().m = PSR::USR;
|
if(usr) cpsr().m = PSR::USR;
|
||||||
|
|
||||||
uint sequential = Nonsequential;
|
uint sequential = Nonsequential;
|
||||||
for(uint m : range(16)) {
|
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 == 1) r(m) = read(Word | sequential, rn);
|
||||||
if(mode == 0) write(Word | sequential, rn, r(m));
|
if(mode == 0) write(Word | sequential, rn, r(m));
|
||||||
rn += 4;
|
rn += 4;
|
||||||
@@ -210,7 +210,7 @@ auto ARM7TDMI::armInstructionMoveMultiple
|
|||||||
|
|
||||||
if(mode) {
|
if(mode) {
|
||||||
idle();
|
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();
|
cpsr() = spsr();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -299,7 +299,7 @@ auto ARM7TDMI::armInstructionMultiplyLong
|
|||||||
|
|
||||||
if(save) {
|
if(save) {
|
||||||
cpsr().z = rd == 0;
|
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);
|
uint32 rn = r(n);
|
||||||
|
|
||||||
for(uint m : range(8)) {
|
for(uint m : range(8)) {
|
||||||
if(!bit1(list,m)) continue;
|
if(!(list & 1 << m)) continue;
|
||||||
switch(mode) {
|
switch(mode) {
|
||||||
case 0: write(Word | Nonsequential, rn, r(m)); break; //STMIA
|
case 0: write(Word | Nonsequential, rn, r(m)); break; //STMIA
|
||||||
case 1: r(m) = read(Word | Nonsequential, rn); break; //LDMIA
|
case 1: r(m) = read(Word | Nonsequential, rn); break; //LDMIA
|
||||||
@@ -135,7 +135,7 @@ auto ARM7TDMI::thumbInstructionMoveMultiple
|
|||||||
rn += 4;
|
rn += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mode == 0 || !bit1(list,n)) r(n) = rn;
|
if(mode == 0 || !(list & 1 << n)) r(n) = rn;
|
||||||
if(mode == 1) idle();
|
if(mode == 1) idle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,7 +193,7 @@ auto ARM7TDMI::thumbInstructionStackMultiple
|
|||||||
|
|
||||||
uint sequential = Nonsequential;
|
uint sequential = Nonsequential;
|
||||||
for(uint m : range(8)) {
|
for(uint m : range(8)) {
|
||||||
if(!bit1(list,m)) continue;
|
if(!(list & 1 << m)) continue;
|
||||||
switch(mode) {
|
switch(mode) {
|
||||||
case 0: write(Word | sequential, sp, r(m)); break; //PUSH
|
case 0: write(Word | sequential, sp, r(m)); break; //PUSH
|
||||||
case 1: r(m) = read(Word | sequential, sp); break; //POP
|
case 1: r(m) = read(Word | sequential, sp); break; //POP
|
||||||
|
@@ -34,22 +34,22 @@ struct Register {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct SFR {
|
struct SFR {
|
||||||
union {
|
uint16_t data = 0;
|
||||||
uint16_t data = 0;
|
|
||||||
BooleanBitField<uint16_t, 15> irq; //interrupt flag
|
BitField<16, 1> z {&data}; //zero flag
|
||||||
BooleanBitField<uint16_t, 12> b; //with flag
|
BitField<16, 2> cy {&data}; //carry flag
|
||||||
BooleanBitField<uint16_t, 11> ih; //immediate higher 8-bit flag
|
BitField<16, 3> s {&data}; //sign flag
|
||||||
BooleanBitField<uint16_t, 10> il; //immediate lower 8-bit flag
|
BitField<16, 4> ov {&data}; //overflow flag
|
||||||
BooleanBitField<uint16_t, 9> alt2; //alt2 instruction mode
|
BitField<16, 5> g {&data}; //go flag
|
||||||
BooleanBitField<uint16_t, 8> alt1; //alt1 instruction mode
|
BitField<16, 6> r {&data}; //ROM r14 flag
|
||||||
BooleanBitField<uint16_t, 6> r; //ROM r14 read flag
|
BitField<16, 8> alt1{&data}; //alt1 instruction mode
|
||||||
BooleanBitField<uint16_t, 5> g; //go flag
|
BitField<16, 9> alt2{&data}; //alt2 instruction mode
|
||||||
BooleanBitField<uint16_t, 4> ov; //overflow flag
|
BitField<16,10> il {&data}; //immediate lower 8-bit flag
|
||||||
BooleanBitField<uint16_t, 3> s; //sign flag
|
BitField<16,11> ih {&data}; //immediate upper 8-bit flag
|
||||||
BooleanBitField<uint16_t, 2> cy; //carry flag
|
BitField<16,12> b {&data}; //with flag
|
||||||
BooleanBitField<uint16_t, 1> z; //zero flag
|
BitField<16,15> irq {&data}; //interrupt flag
|
||||||
NaturalBitField<uint16_t, 9, 8> alt; //instruction mode (composite flag)
|
|
||||||
};
|
BitRange<16,8,9> alt{&data}; //composite instruction mode
|
||||||
|
|
||||||
inline operator uint() const { return data & 0x9f7e; }
|
inline operator uint() const { return data & 0x9f7e; }
|
||||||
inline auto& operator=(const uint value) { return data = value, *this; }
|
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;
|
io.cache.address[io.cache.page] = address;
|
||||||
for(uint offset : range(256)) {
|
for(uint offset : range(256)) {
|
||||||
step(wait(address)); bit8(programRAM[io.cache.page][offset],0) = read(address++);
|
step(wait(address)); programRAM[io.cache.page][offset] = read(address++) << 0;
|
||||||
step(wait(address)); bit8(programRAM[io.cache.page][offset],1) = read(address++);
|
step(wait(address)); programRAM[io.cache.page][offset] |= read(address++) << 8;
|
||||||
}
|
}
|
||||||
return io.cache.enable = 0, true;
|
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 {
|
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 {
|
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 {
|
auto HG51B::instructionMUL(uint7 reg) -> void {
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
auto HG51B::readRegister(uint7 address) -> uint24 {
|
auto HG51B::readRegister(uint7 address) -> uint24 {
|
||||||
switch(address) {
|
switch(address) {
|
||||||
case 0x01: return bits(r.mul,24-47);
|
case 0x01: return r.mul >> 24 & 0xffffff;
|
||||||
case 0x02: return bits(r.mul, 0-23);
|
case 0x02: return r.mul >> 0 & 0xffffff;
|
||||||
case 0x03: return r.mdr;
|
case 0x03: return r.mdr;
|
||||||
case 0x08: return r.rom;
|
case 0x08: return r.rom;
|
||||||
case 0x0c: return r.ram;
|
case 0x0c: return r.ram;
|
||||||
@@ -64,8 +64,8 @@ auto HG51B::readRegister(uint7 address) -> uint24 {
|
|||||||
|
|
||||||
auto HG51B::writeRegister(uint7 address, uint24 data) -> void {
|
auto HG51B::writeRegister(uint7 address, uint24 data) -> void {
|
||||||
switch(address) {
|
switch(address) {
|
||||||
case 0x01: bits(r.mul,24-47) = data; return;
|
case 0x01: r.mul = r.mul & 0xffffffull | data << 24; return;
|
||||||
case 0x02: bits(r.mul, 0-23) = data; return;
|
case 0x02: r.mul = r.mul & ~0xffffffull | data << 0; return;
|
||||||
case 0x03: r.mdr = data; return;
|
case 0x03: r.mdr = data; return;
|
||||||
case 0x08: r.rom = data; return;
|
case 0x08: r.rom = data; return;
|
||||||
case 0x0c: r.ram = data; return;
|
case 0x0c: r.ram = data; return;
|
||||||
|
@@ -7,32 +7,33 @@ auto SPC700::instructionAbsoluteBitModify(uint3 mode) -> void {
|
|||||||
switch(mode) {
|
switch(mode) {
|
||||||
case 0: //or addr:bit
|
case 0: //or addr:bit
|
||||||
idle();
|
idle();
|
||||||
CF |= bit1(data,bit);
|
CF |= bool(data & 1 << bit);
|
||||||
break;
|
break;
|
||||||
case 1: //or !addr:bit
|
case 1: //or !addr:bit
|
||||||
idle();
|
idle();
|
||||||
CF |= !bit1(data,bit);
|
CF |= !bool(data & 1 << bit);
|
||||||
break;
|
break;
|
||||||
case 2: //and addr:bit
|
case 2: //and addr:bit
|
||||||
CF &= bit1(data,bit);
|
CF &= bool(data & 1 << bit);
|
||||||
break;
|
break;
|
||||||
case 3: //and !addr:bit
|
case 3: //and !addr:bit
|
||||||
CF &= !bit1(data,bit);
|
CF &= !bool(data & 1 << bit);
|
||||||
break;
|
break;
|
||||||
case 4: //eor addr:bit
|
case 4: //eor addr:bit
|
||||||
idle();
|
idle();
|
||||||
CF ^= bit1(data,bit);
|
CF ^= bool(data & 1 << bit);
|
||||||
break;
|
break;
|
||||||
case 5: //ld addr:bit
|
case 5: //ld addr:bit
|
||||||
CF = bit1(data,bit);
|
CF = bool(data & 1 << bit);
|
||||||
break;
|
break;
|
||||||
case 6: //st addr:bit
|
case 6: //st addr:bit
|
||||||
idle();
|
idle();
|
||||||
bit1(data,bit) = CF;
|
data &= ~(1 << bit);
|
||||||
|
data |= CF << bit;
|
||||||
write(address, data);
|
write(address, data);
|
||||||
break;
|
break;
|
||||||
case 7: //not addr:bit
|
case 7: //not addr:bit
|
||||||
bit1(data,bit) ^= 1;
|
data ^= 1 << bit;
|
||||||
write(address, data);
|
write(address, data);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -41,7 +42,8 @@ auto SPC700::instructionAbsoluteBitModify(uint3 mode) -> void {
|
|||||||
auto SPC700::instructionAbsoluteBitSet(uint3 bit, bool value) -> void {
|
auto SPC700::instructionAbsoluteBitSet(uint3 bit, bool value) -> void {
|
||||||
uint8 address = fetch();
|
uint8 address = fetch();
|
||||||
uint8 data = load(address);
|
uint8 data = load(address);
|
||||||
bit1(data,bit) = value;
|
data &= ~(1 << bit);
|
||||||
|
data |= value << bit;
|
||||||
store(address, data);
|
store(address, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +97,7 @@ auto SPC700::instructionBranchBit(uint3 bit, bool match) -> void {
|
|||||||
uint8 data = load(address);
|
uint8 data = load(address);
|
||||||
idle();
|
idle();
|
||||||
uint8 displacement = fetch();
|
uint8 displacement = fetch();
|
||||||
if(bit1(data,bit) != match) return;
|
if(bool(data & 1 << bit) != match) return;
|
||||||
idle();
|
idle();
|
||||||
idle();
|
idle();
|
||||||
PC += (int8)displacement;
|
PC += (int8)displacement;
|
||||||
|
@@ -134,14 +134,14 @@ struct SPC700 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline auto& operator=(uint8 data) {
|
inline auto& operator=(uint8 data) {
|
||||||
c = bit1(data,0);
|
c = data & 0x01;
|
||||||
z = bit1(data,1);
|
z = data & 0x02;
|
||||||
i = bit1(data,2);
|
i = data & 0x04;
|
||||||
h = bit1(data,3);
|
h = data & 0x08;
|
||||||
b = bit1(data,4);
|
b = data & 0x10;
|
||||||
p = bit1(data,5);
|
p = data & 0x20;
|
||||||
v = bit1(data,6);
|
v = data & 0x40;
|
||||||
n = bit1(data,7);
|
n = data & 0x80;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -37,12 +37,12 @@ struct uPD96050 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline auto operator=(uint16 data) -> Flag& {
|
inline auto operator=(uint16 data) -> Flag& {
|
||||||
ov0 = bit1(data,0);
|
ov0 = data >> 0 & 1;
|
||||||
ov1 = bit1(data,1);
|
ov1 = data >> 1 & 1;
|
||||||
z = bit1(data,2);
|
z = data >> 2 & 1;
|
||||||
c = bit1(data,3);
|
c = data >> 3 & 1;
|
||||||
s0 = bit1(data,4);
|
s0 = data >> 4 & 1;
|
||||||
s1 = bit1(data,5);
|
s1 = data >> 5 & 1;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,17 +64,17 @@ struct uPD96050 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline auto operator=(uint16 data) -> Status& {
|
inline auto operator=(uint16 data) -> Status& {
|
||||||
p0 = bit1(data, 0);
|
p0 = data >> 0 & 1;
|
||||||
p1 = bit1(data, 1);
|
p1 = data >> 1 & 1;
|
||||||
ei = bit1(data, 7);
|
ei = data >> 7 & 1;
|
||||||
sic = bit1(data, 8);
|
sic = data >> 8 & 1;
|
||||||
soc = bit1(data, 9);
|
soc = data >> 9 & 1;
|
||||||
drc = bit1(data,10);
|
drc = data >> 10 & 1;
|
||||||
dma = bit1(data,11);
|
dma = data >> 11 & 1;
|
||||||
drs = bit1(data,12);
|
drs = data >> 12 & 1;
|
||||||
usf0 = bit1(data,13);
|
usf0 = data >> 13 & 1;
|
||||||
usf1 = bit1(data,14);
|
usf1 = data >> 14 & 1;
|
||||||
rqm = bit1(data,15);
|
rqm = data >> 15 & 1;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -31,10 +31,10 @@ auto WDC65816::disassemble(uint24 address, bool e, bool m, bool x) -> string {
|
|||||||
return data | readWord(address + 1) << 8;
|
return data | readWord(address + 1) << 8;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto opcode = read(address); address(0,15)++;
|
auto opcode = read(address); address.bit(0,15)++;
|
||||||
auto operand0 = read(address); address(0,15)++;
|
auto operand0 = read(address); address.bit(0,15)++;
|
||||||
auto operand1 = read(address); address(0,15)++;
|
auto operand1 = read(address); address.bit(0,15)++;
|
||||||
auto operand2 = read(address); address(0,15)++;
|
auto operand2 = read(address); address.bit(0,15)++;
|
||||||
|
|
||||||
uint8 operandByte = operand0 << 0;
|
uint8 operandByte = operand0 << 0;
|
||||||
uint16 operandWord = operand0 << 0 | operand1 << 8;
|
uint16 operandWord = operand0 << 0 | operand1 << 8;
|
||||||
|
@@ -8,7 +8,7 @@ N push(PC.b);
|
|||||||
IF = 1;
|
IF = 1;
|
||||||
DF = 0;
|
DF = 0;
|
||||||
PC.l = read(r.vector + 0);
|
PC.l = read(r.vector + 0);
|
||||||
PC.h = read(r.vector + 1);
|
L PC.h = read(r.vector + 1);
|
||||||
PC.b = 0x00;
|
PC.b = 0x00;
|
||||||
idleJump();
|
idleJump();
|
||||||
}
|
}
|
||||||
|
@@ -45,7 +45,7 @@ auto WDC65816::instructionLongRead8(alu8 op, r16 I) -> void {
|
|||||||
V.l = fetch();
|
V.l = fetch();
|
||||||
V.h = fetch();
|
V.h = fetch();
|
||||||
V.b = 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);
|
alu(W.l);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,8 +53,8 @@ auto WDC65816::instructionLongRead16(alu16 op, r16 I) -> void {
|
|||||||
V.l = fetch();
|
V.l = fetch();
|
||||||
V.h = fetch();
|
V.h = fetch();
|
||||||
V.b = fetch();
|
V.b = fetch();
|
||||||
W.l = read(V.d + I.w + 0);
|
W.l = readLong(V.d + I.w + 0);
|
||||||
L W.h = read(V.d + I.w + 1);
|
L W.h = readLong(V.d + I.w + 1);
|
||||||
alu(W.w);
|
alu(W.w);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +157,7 @@ auto WDC65816::instructionIndirectLongRead8(alu8 op, r16 I) -> void {
|
|||||||
V.l = readDirectN(U.l + 0);
|
V.l = readDirectN(U.l + 0);
|
||||||
V.h = readDirectN(U.l + 1);
|
V.h = readDirectN(U.l + 1);
|
||||||
V.b = readDirectN(U.l + 2);
|
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);
|
alu(W.l);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,8 +167,8 @@ auto WDC65816::instructionIndirectLongRead16(alu16 op, r16 I) -> void {
|
|||||||
V.l = readDirectN(U.l + 0);
|
V.l = readDirectN(U.l + 0);
|
||||||
V.h = readDirectN(U.l + 1);
|
V.h = readDirectN(U.l + 1);
|
||||||
V.b = readDirectN(U.l + 2);
|
V.b = readDirectN(U.l + 2);
|
||||||
W.l = read(V.d + I.w + 0);
|
W.l = readLong(V.d + I.w + 0);
|
||||||
L W.h = read(V.d + I.w + 1);
|
L W.h = readLong(V.d + I.w + 1);
|
||||||
alu(W.w);
|
alu(W.w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -30,15 +30,15 @@ auto WDC65816::instructionLongWrite8(r16 I) -> void {
|
|||||||
V.l = fetch();
|
V.l = fetch();
|
||||||
V.h = fetch();
|
V.h = fetch();
|
||||||
V.b = 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 {
|
auto WDC65816::instructionLongWrite16(r16 I) -> void {
|
||||||
V.l = fetch();
|
V.l = fetch();
|
||||||
V.h = fetch();
|
V.h = fetch();
|
||||||
V.b = fetch();
|
V.b = fetch();
|
||||||
write(V.d + I.w + 0, A.l);
|
writeLong(V.d + I.w + 0, A.l);
|
||||||
L write(V.d + I.w + 1, A.h);
|
L writeLong(V.d + I.w + 1, A.h);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto WDC65816::instructionDirectWrite8(r16 F) -> void {
|
auto WDC65816::instructionDirectWrite8(r16 F) -> void {
|
||||||
@@ -130,7 +130,7 @@ auto WDC65816::instructionIndirectLongWrite8(r16 I) -> void {
|
|||||||
V.l = readDirectN(U.l + 0);
|
V.l = readDirectN(U.l + 0);
|
||||||
V.h = readDirectN(U.l + 1);
|
V.h = readDirectN(U.l + 1);
|
||||||
V.b = readDirectN(U.l + 2);
|
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 {
|
auto WDC65816::instructionIndirectLongWrite16(r16 I) -> void {
|
||||||
@@ -139,8 +139,8 @@ auto WDC65816::instructionIndirectLongWrite16(r16 I) -> void {
|
|||||||
V.l = readDirectN(U.l + 0);
|
V.l = readDirectN(U.l + 0);
|
||||||
V.h = readDirectN(U.l + 1);
|
V.h = readDirectN(U.l + 1);
|
||||||
V.b = readDirectN(U.l + 2);
|
V.b = readDirectN(U.l + 2);
|
||||||
write(V.d + I.w + 0, A.l);
|
writeLong(V.d + I.w + 0, A.l);
|
||||||
L write(V.d + I.w + 1, A.h);
|
L writeLong(V.d + I.w + 1, A.h);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto WDC65816::instructionStackWrite8() -> void {
|
auto WDC65816::instructionStackWrite8() -> void {
|
||||||
|
@@ -50,31 +50,39 @@ auto WDC65816::pushN(uint8 data) -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto WDC65816::readDirect(uint address) -> uint8 {
|
auto WDC65816::readDirect(uint address) -> uint8 {
|
||||||
if(EF && !D.l) return read(D.w | uint8(address));
|
if(EF && !D.l) return read(D.w | address & 0xff);
|
||||||
return read(uint16(D.w + address));
|
return read(D.w + address & 0xffff);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto WDC65816::writeDirect(uint address, uint8 data) -> void {
|
auto WDC65816::writeDirect(uint address, uint8 data) -> void {
|
||||||
if(EF && !D.l) return write(D.w | uint8(address), data);
|
if(EF && !D.l) return write(D.w | address & 0xff, data);
|
||||||
write(uint16(D.w + address), data);
|
write(D.w + address & 0xffff, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto WDC65816::readDirectN(uint address) -> uint8 {
|
auto WDC65816::readDirectN(uint address) -> uint8 {
|
||||||
return read(uint16(D.w + address));
|
return read(D.w + address & 0xffff);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto WDC65816::readBank(uint address) -> uint8 {
|
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 {
|
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 {
|
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 {
|
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 idle() -> void = 0;
|
||||||
virtual auto idleBranch() -> void {}
|
virtual auto idleBranch() -> void {}
|
||||||
virtual auto idleJump() -> void {}
|
virtual auto idleJump() -> void {}
|
||||||
virtual auto read(uint24 addr) -> uint8 = 0;
|
virtual auto read(uint addr) -> uint8 = 0;
|
||||||
virtual auto write(uint24 addr, uint8 data) -> void = 0;
|
virtual auto write(uint addr, uint8 data) -> void = 0;
|
||||||
virtual auto lastCycle() -> void = 0;
|
virtual auto lastCycle() -> void = 0;
|
||||||
virtual auto interruptPending() const -> bool = 0;
|
virtual auto interruptPending() const -> bool = 0;
|
||||||
virtual auto interrupt() -> void;
|
virtual auto interrupt() -> void;
|
||||||
@@ -19,6 +19,9 @@ struct WDC65816 {
|
|||||||
|
|
||||||
virtual auto readDisassembler(uint addr) -> uint8 { return 0; }
|
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;
|
using r8 = uint8;
|
||||||
|
|
||||||
union r16 {
|
union r16 {
|
||||||
@@ -44,22 +47,24 @@ struct WDC65816 {
|
|||||||
auto power() -> void;
|
auto power() -> void;
|
||||||
|
|
||||||
//memory.cpp
|
//memory.cpp
|
||||||
inline auto idleIRQ() -> void;
|
alwaysinline auto idleIRQ() -> void;
|
||||||
inline auto idle2() -> void;
|
alwaysinline auto idle2() -> void;
|
||||||
inline auto idle4(uint16 x, uint16 y) -> void;
|
alwaysinline auto idle4(uint16 x, uint16 y) -> void;
|
||||||
inline auto idle6(uint16 address) -> void;
|
alwaysinline auto idle6(uint16 address) -> void;
|
||||||
inline auto fetch() -> uint8;
|
alwaysinline auto fetch() -> uint8;
|
||||||
inline auto pull() -> uint8;
|
alwaysinline auto pull() -> uint8;
|
||||||
auto push(uint8 data) -> void;
|
auto push(uint8 data) -> void;
|
||||||
inline auto pullN() -> uint8;
|
alwaysinline auto pullN() -> uint8;
|
||||||
inline auto pushN(uint8 data) -> void;
|
alwaysinline auto pushN(uint8 data) -> void;
|
||||||
inline auto readDirect(uint address) -> uint8;
|
alwaysinline auto readDirect(uint address) -> uint8;
|
||||||
inline auto writeDirect(uint address, uint8 data) -> void;
|
alwaysinline auto writeDirect(uint address, uint8 data) -> void;
|
||||||
inline auto readDirectN(uint address) -> uint8;
|
alwaysinline auto readDirectN(uint address) -> uint8;
|
||||||
inline auto readBank(uint address) -> uint8;
|
alwaysinline auto readBank(uint address) -> uint8;
|
||||||
inline auto writeBank(uint address, uint8 data) -> void;
|
alwaysinline auto writeBank(uint address, uint8 data) -> void;
|
||||||
inline auto readStack(uint address) -> uint8;
|
alwaysinline auto readLong(uint address) -> uint8;
|
||||||
inline auto writeStack(uint address, uint8 data) -> void;
|
alwaysinline auto writeLong(uint address, uint8 data) -> void;
|
||||||
|
alwaysinline auto readStack(uint address) -> uint8;
|
||||||
|
alwaysinline auto writeStack(uint address, uint8 data) -> void;
|
||||||
|
|
||||||
//algorithms.cpp
|
//algorithms.cpp
|
||||||
using alu8 = auto (WDC65816::*)( uint8) -> uint8;
|
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-interface sfc-system sfc-controller
|
||||||
objects += sfc-cartridge sfc-memory
|
objects += sfc-cartridge sfc-memory
|
||||||
|
@@ -37,6 +37,17 @@ auto Cartridge::titles() const -> vector<string> {
|
|||||||
return titles;
|
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 {
|
auto Cartridge::load() -> bool {
|
||||||
information = {};
|
information = {};
|
||||||
has = {};
|
has = {};
|
||||||
@@ -54,6 +65,7 @@ auto Cartridge::load() -> bool {
|
|||||||
if(auto fp = platform->open(ID::SuperFamicom, "manifest.bml", File::Read, File::Required)) {
|
if(auto fp = platform->open(ID::SuperFamicom, "manifest.bml", File::Read, File::Required)) {
|
||||||
game.load(fp->reads());
|
game.load(fp->reads());
|
||||||
} else return false;
|
} else return false;
|
||||||
|
|
||||||
loadCartridge(game.document);
|
loadCartridge(game.document);
|
||||||
|
|
||||||
//Game Boy
|
//Game Boy
|
||||||
|
@@ -1,10 +1,12 @@
|
|||||||
struct Cartridge {
|
struct Cartridge {
|
||||||
auto pathID() const -> uint { return information.pathID; }
|
auto pathID() const -> uint { return information.pathID; }
|
||||||
auto region() const -> string { return information.region; }
|
auto region() const -> string { return information.region; }
|
||||||
|
auto headerTitle() const -> string { return game.title; }
|
||||||
|
|
||||||
auto hashes() const -> vector<string>;
|
auto hashes() const -> vector<string>;
|
||||||
auto manifests() const -> vector<string>;
|
auto manifests() const -> vector<string>;
|
||||||
auto titles() const -> vector<string>;
|
auto titles() const -> vector<string>;
|
||||||
|
auto title() const -> string;
|
||||||
|
|
||||||
auto load() -> bool;
|
auto load() -> bool;
|
||||||
auto save() -> void;
|
auto save() -> void;
|
||||||
|
@@ -141,7 +141,7 @@ auto Cartridge::loadMap(Markup::Node map, T& memory) -> uint {
|
|||||||
auto Cartridge::loadMap(
|
auto Cartridge::loadMap(
|
||||||
Markup::Node map,
|
Markup::Node map,
|
||||||
const function<uint8 (uint, uint8)>& reader,
|
const function<uint8 (uint, uint8)>& reader,
|
||||||
const function<void (uint, uint8)>& writer
|
const function<void (uint, uint8)>& writer
|
||||||
) -> uint {
|
) -> uint {
|
||||||
auto addr = map["address"].text();
|
auto addr = map["address"].text();
|
||||||
auto size = map["size"].natural();
|
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;
|
has.Cx4 = true;
|
||||||
for(auto map : node.find("map")) {
|
for(auto map : node.find("map")) {
|
||||||
loadMap(map, {&Cx4::read, &cx4}, {&Cx4::write, &cx4});
|
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);
|
auto manifest = BML::serialize(game.document);
|
||||||
if(manifest.find("identifier: DSP1")) { //also matches DSP1B
|
if(manifest.find("identifier: DSP1")) { //also matches DSP1B
|
||||||
has.DSP1 = true;
|
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);
|
auto manifest = BML::serialize(game.document);
|
||||||
if(manifest.find("identifier: ST010")) {
|
if(manifest.find("identifier: ST010")) {
|
||||||
has.ST0010 = true;
|
has.ST0010 = true;
|
||||||
|
@@ -6,6 +6,10 @@ namespace SuperFamicom {
|
|||||||
#include "serialization.cpp"
|
#include "serialization.cpp"
|
||||||
ArmDSP armdsp;
|
ArmDSP armdsp;
|
||||||
|
|
||||||
|
auto ArmDSP::synchronizeCPU() -> void {
|
||||||
|
if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread);
|
||||||
|
}
|
||||||
|
|
||||||
auto ArmDSP::Enter() -> void {
|
auto ArmDSP::Enter() -> void {
|
||||||
armdsp.boot();
|
armdsp.boot();
|
||||||
while(true) scheduler.synchronize(), armdsp.main();
|
while(true) scheduler.synchronize(), armdsp.main();
|
||||||
@@ -32,8 +36,8 @@ auto ArmDSP::main() -> void {
|
|||||||
|
|
||||||
auto ArmDSP::step(uint clocks) -> void {
|
auto ArmDSP::step(uint clocks) -> void {
|
||||||
if(bridge.timer && --bridge.timer == 0);
|
if(bridge.timer && --bridge.timer == 0);
|
||||||
Thread::step(clocks);
|
clock += clocks * (uint64_t)cpu.frequency;
|
||||||
synchronize(cpu);
|
synchronizeCPU();
|
||||||
}
|
}
|
||||||
|
|
||||||
//MMIO: 00-3f,80-bf:3800-38ff
|
//MMIO: 00-3f,80-bf:3800-38ff
|
||||||
@@ -41,7 +45,7 @@ auto ArmDSP::step(uint clocks) -> void {
|
|||||||
//a0 ignored
|
//a0 ignored
|
||||||
|
|
||||||
auto ArmDSP::read(uint addr, uint8) -> uint8 {
|
auto ArmDSP::read(uint addr, uint8) -> uint8 {
|
||||||
cpu.synchronize(*this);
|
cpu.synchronizeCoprocessors();
|
||||||
|
|
||||||
uint8 data = 0x00;
|
uint8 data = 0x00;
|
||||||
addr &= 0xff06;
|
addr &= 0xff06;
|
||||||
@@ -65,7 +69,7 @@ auto ArmDSP::read(uint addr, uint8) -> uint8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto ArmDSP::write(uint addr, uint8 data) -> void {
|
auto ArmDSP::write(uint addr, uint8 data) -> void {
|
||||||
cpu.synchronize(*this);
|
cpu.synchronizeCoprocessors();
|
||||||
|
|
||||||
addr &= 0xff06;
|
addr &= 0xff06;
|
||||||
|
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
struct ArmDSP : Processor::ARM7TDMI, Thread {
|
struct ArmDSP : Processor::ARM7TDMI, Thread {
|
||||||
#include "registers.hpp"
|
#include "registers.hpp"
|
||||||
|
|
||||||
|
auto synchronizeCPU() -> void;
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
auto boot() -> void;
|
auto boot() -> void;
|
||||||
auto main() -> 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'0010) bridge.signal = true;
|
||||||
|
|
||||||
if(addr == 0x4000'0020) bit8(bridge.timerlatch,0) = word;
|
if(addr == 0x4000'0020) bridge.timerlatch = bridge.timerlatch & 0xffff00 | word << 0;
|
||||||
if(addr == 0x4000'0024) bit8(bridge.timerlatch,1) = word;
|
if(addr == 0x4000'0024) bridge.timerlatch = bridge.timerlatch & 0xff00ff | word << 8;
|
||||||
if(addr == 0x4000'0028) bit8(bridge.timerlatch,2) = word;
|
if(addr == 0x4000'0028) bridge.timerlatch = bridge.timerlatch & 0x00ffff | word << 16;
|
||||||
|
|
||||||
if(addr == 0x4000'002c) bridge.timer = bridge.timerlatch;
|
if(addr == 0x4000'002c) bridge.timer = bridge.timerlatch;
|
||||||
}
|
}
|
||||||
|
@@ -7,6 +7,10 @@ namespace SuperFamicom {
|
|||||||
#include "serialization.cpp"
|
#include "serialization.cpp"
|
||||||
EpsonRTC epsonrtc;
|
EpsonRTC epsonrtc;
|
||||||
|
|
||||||
|
auto EpsonRTC::synchronizeCPU() -> void {
|
||||||
|
if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread);
|
||||||
|
}
|
||||||
|
|
||||||
auto EpsonRTC::Enter() -> void {
|
auto EpsonRTC::Enter() -> void {
|
||||||
while(true) scheduler.synchronize(), epsonrtc.main();
|
while(true) scheduler.synchronize(), epsonrtc.main();
|
||||||
}
|
}
|
||||||
@@ -27,7 +31,11 @@ auto EpsonRTC::main() -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
step(1);
|
step(1);
|
||||||
synchronize(cpu);
|
synchronizeCPU();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto EpsonRTC::step(uint clocks) -> void {
|
||||||
|
clock += clocks * (uint64_t)cpu.frequency;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto EpsonRTC::initialize() -> void {
|
auto EpsonRTC::initialize() -> void {
|
||||||
@@ -127,7 +135,7 @@ auto EpsonRTC::synchronize(uint64 timestamp) -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto EpsonRTC::read(uint addr, uint8 data) -> uint8 {
|
auto EpsonRTC::read(uint addr, uint8 data) -> uint8 {
|
||||||
cpu.synchronize(*this);
|
cpu.synchronizeCoprocessors();
|
||||||
addr &= 3;
|
addr &= 3;
|
||||||
|
|
||||||
if(addr == 0) {
|
if(addr == 0) {
|
||||||
@@ -152,7 +160,7 @@ auto EpsonRTC::read(uint addr, uint8 data) -> uint8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto EpsonRTC::write(uint addr, uint8 data) -> void {
|
auto EpsonRTC::write(uint addr, uint8 data) -> void {
|
||||||
cpu.synchronize(*this);
|
cpu.synchronizeCoprocessors();
|
||||||
addr &= 3, data &= 15;
|
addr &= 3, data &= 15;
|
||||||
|
|
||||||
if(addr == 0) {
|
if(addr == 0) {
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
//Epson RTC-4513 Real-Time Clock
|
//Epson RTC-4513 Real-Time Clock
|
||||||
|
|
||||||
struct EpsonRTC : Thread {
|
struct EpsonRTC : Thread {
|
||||||
using Thread::synchronize;
|
auto synchronizeCPU() -> void;
|
||||||
|
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
|
auto step(uint clocks) -> void;
|
||||||
|
|
||||||
auto initialize() -> void;
|
auto initialize() -> void;
|
||||||
auto power() -> void;
|
auto power() -> void;
|
||||||
|
@@ -5,6 +5,10 @@ namespace SuperFamicom {
|
|||||||
#include "serialization.cpp"
|
#include "serialization.cpp"
|
||||||
Event event;
|
Event event;
|
||||||
|
|
||||||
|
auto Event::synchronizeCPU() -> void {
|
||||||
|
if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread);
|
||||||
|
}
|
||||||
|
|
||||||
auto Event::Enter() -> void {
|
auto Event::Enter() -> void {
|
||||||
while(true) scheduler.synchronize(), event.main();
|
while(true) scheduler.synchronize(), event.main();
|
||||||
}
|
}
|
||||||
@@ -26,7 +30,11 @@ auto Event::main() -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
step(1);
|
step(1);
|
||||||
synchronize(cpu);
|
synchronizeCPU();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Event::step(uint clocks) -> void {
|
||||||
|
clock += clocks * (uint64_t)cpu.frequency;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Event::unload() -> void {
|
auto Event::unload() -> void {
|
||||||
@@ -40,7 +48,7 @@ auto Event::power() -> void {
|
|||||||
create(Event::Enter, 1);
|
create(Event::Enter, 1);
|
||||||
|
|
||||||
//DIP switches 0-3 control the time: 3 minutes + 0-15 extra minutes
|
//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 4-5 serve an unknown purpose
|
||||||
//DIP switches 6-7 are not connected
|
//DIP switches 6-7 are not connected
|
||||||
|
|
||||||
|
@@ -15,8 +15,10 @@
|
|||||||
|
|
||||||
struct Event : Thread {
|
struct Event : Thread {
|
||||||
//event.cpp
|
//event.cpp
|
||||||
|
auto synchronizeCPU() -> void;
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
|
auto step(uint clocks) -> void;
|
||||||
auto unload() -> void;
|
auto unload() -> void;
|
||||||
auto power() -> void;
|
auto power() -> void;
|
||||||
|
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
#include <sfc/sfc.hpp>
|
#include <sfc/sfc.hpp>
|
||||||
|
#include <processor/hg51b/hg51b.cpp>
|
||||||
|
|
||||||
namespace SuperFamicom {
|
namespace SuperFamicom {
|
||||||
|
|
||||||
@@ -7,19 +8,23 @@ namespace SuperFamicom {
|
|||||||
#include "data-rom.cpp"
|
#include "data-rom.cpp"
|
||||||
HitachiDSP hitachidsp;
|
HitachiDSP hitachidsp;
|
||||||
|
|
||||||
|
auto HitachiDSP::synchronizeCPU() -> void {
|
||||||
|
if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread);
|
||||||
|
}
|
||||||
|
|
||||||
auto HitachiDSP::Enter() -> void {
|
auto HitachiDSP::Enter() -> void {
|
||||||
while(true) scheduler.synchronize(), hitachidsp.main();
|
while(true) scheduler.synchronize(), hitachidsp.main();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto HitachiDSP::step(uint clocks) -> void {
|
auto HitachiDSP::step(uint clocks) -> void {
|
||||||
HG51B::step(clocks);
|
HG51B::step(clocks);
|
||||||
Thread::step(clocks);
|
clock += clocks * (uint64_t)cpu.frequency;
|
||||||
synchronize(cpu);
|
synchronizeCPU();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto HitachiDSP::halt() -> void {
|
auto HitachiDSP::halt() -> void {
|
||||||
HG51B::halt();
|
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 {
|
auto HitachiDSP::unload() -> void {
|
||||||
|
@@ -3,6 +3,7 @@ struct HitachiDSP : Processor::HG51B, Thread {
|
|||||||
WritableMemory ram;
|
WritableMemory ram;
|
||||||
|
|
||||||
//hitachidsp.cpp
|
//hitachidsp.cpp
|
||||||
|
auto synchronizeCPU() -> void;
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
auto step(uint clocks) -> void override;
|
auto step(uint clocks) -> void override;
|
||||||
auto halt() -> void override;
|
auto halt() -> void override;
|
||||||
|
@@ -134,21 +134,21 @@ auto HitachiDSP::readIO(uint address, uint8 data) -> uint8 {
|
|||||||
|
|
||||||
//IO
|
//IO
|
||||||
switch(address) {
|
switch(address) {
|
||||||
case 0x7f40: return bit8(io.dma.source,0);
|
case 0x7f40: return io.dma.source >> 0;
|
||||||
case 0x7f41: return bit8(io.dma.source,1);
|
case 0x7f41: return io.dma.source >> 8;
|
||||||
case 0x7f42: return bit8(io.dma.source,2);
|
case 0x7f42: return io.dma.source >> 16;
|
||||||
case 0x7f43: return bit8(io.dma.length,0);
|
case 0x7f43: return io.dma.length >> 0;
|
||||||
case 0x7f44: return bit8(io.dma.length,1);
|
case 0x7f44: return io.dma.length >> 8;
|
||||||
case 0x7f45: return bit8(io.dma.target,0);
|
case 0x7f45: return io.dma.target >> 0;
|
||||||
case 0x7f46: return bit8(io.dma.target,1);
|
case 0x7f46: return io.dma.target >> 8;
|
||||||
case 0x7f47: return bit8(io.dma.target,2);
|
case 0x7f47: return io.dma.target >> 16;
|
||||||
case 0x7f48: return io.cache.page;
|
case 0x7f48: return io.cache.page;
|
||||||
case 0x7f49: return bit8(io.cache.base,0);
|
case 0x7f49: return io.cache.base >> 0;
|
||||||
case 0x7f4a: return bit8(io.cache.base,1);
|
case 0x7f4a: return io.cache.base >> 8;
|
||||||
case 0x7f4b: return bit8(io.cache.base,2);
|
case 0x7f4b: return io.cache.base >> 16;
|
||||||
case 0x7f4c: return io.cache.lock[0] << 0 | io.cache.lock[1] << 1;
|
case 0x7f4c: return io.cache.lock[0] << 0 | io.cache.lock[1] << 1;
|
||||||
case 0x7f4d: return bit8(io.cache.pb,0);
|
case 0x7f4d: return io.cache.pb >> 0;
|
||||||
case 0x7f4e: return bit8(io.cache.pb,1);
|
case 0x7f4e: return io.cache.pb >> 8;
|
||||||
case 0x7f4f: return io.cache.pc;
|
case 0x7f4f: return io.cache.pc;
|
||||||
case 0x7f50: return io.wait.ram << 0 | io.wait.rom << 4;
|
case 0x7f50: return io.wait.ram << 0 | io.wait.rom << 4;
|
||||||
case 0x7f51: return io.irq;
|
case 0x7f51: return io.irq;
|
||||||
@@ -166,7 +166,11 @@ auto HitachiDSP::readIO(uint address, uint8 data) -> uint8 {
|
|||||||
//registers
|
//registers
|
||||||
if((address >= 0x7f80 && address <= 0x7faf) || (address >= 0x7fc0 && address <= 0x7fef)) {
|
if((address >= 0x7f80 && address <= 0x7faf) || (address >= 0x7fc0 && address <= 0x7fef)) {
|
||||||
address &= 0x3f;
|
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;
|
return 0x00;
|
||||||
@@ -177,16 +181,16 @@ auto HitachiDSP::writeIO(uint address, uint8 data) -> void {
|
|||||||
|
|
||||||
//IO
|
//IO
|
||||||
switch(address) {
|
switch(address) {
|
||||||
case 0x7f40: bit8(io.dma.source,0) = data; return;
|
case 0x7f40: io.dma.source = io.dma.source & 0xffff00 | data << 0; return;
|
||||||
case 0x7f41: bit8(io.dma.source,1) = data; return;
|
case 0x7f41: io.dma.source = io.dma.source & 0xff00ff | data << 8; return;
|
||||||
case 0x7f42: bit8(io.dma.source,2) = data; return;
|
case 0x7f42: io.dma.source = io.dma.source & 0x00ffff | data << 16; return;
|
||||||
|
|
||||||
case 0x7f43: bit8(io.dma.length,0) = data; return;
|
case 0x7f43: io.dma.length = io.dma.length & 0xff00 | data << 0; return;
|
||||||
case 0x7f44: bit8(io.dma.length,1) = data; return;
|
case 0x7f44: io.dma.length = io.dma.length & 0x00ff | data << 8; return;
|
||||||
|
|
||||||
case 0x7f45: bit8(io.dma.target,0) = data; return;
|
case 0x7f45: io.dma.target = io.dma.target & 0xffff00 | data << 0; return;
|
||||||
case 0x7f46: bit8(io.dma.target,1) = data; return;
|
case 0x7f46: io.dma.target = io.dma.target & 0xff00ff | data << 8; return;
|
||||||
case 0x7f47: bit8(io.dma.target,2) = data;
|
case 0x7f47: io.dma.target = io.dma.target & 0x00ffff | data << 16;
|
||||||
if(io.halt) io.dma.enable = 1;
|
if(io.halt) io.dma.enable = 1;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -195,17 +199,17 @@ auto HitachiDSP::writeIO(uint address, uint8 data) -> void {
|
|||||||
if(io.halt) io.cache.enable = 1;
|
if(io.halt) io.cache.enable = 1;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x7f49: bit8(io.cache.base,0) = data; return;
|
case 0x7f49: io.cache.base = io.cache.base & 0xffff00 | data << 0; return;
|
||||||
case 0x7f4a: bit8(io.cache.base,1) = data; return;
|
case 0x7f4a: io.cache.base = io.cache.base & 0xff00ff | data << 8; return;
|
||||||
case 0x7f4b: bit8(io.cache.base,2) = data; return;
|
case 0x7f4b: io.cache.base = io.cache.base & 0x00ffff | data << 16; return;
|
||||||
|
|
||||||
case 0x7f4c:
|
case 0x7f4c:
|
||||||
io.cache.lock[0] = bit1(data,0);
|
io.cache.lock[0] = bool(data & 1);
|
||||||
io.cache.lock[1] = bit1(data,1);
|
io.cache.lock[1] = bool(data & 2);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x7f4d: bit8(io.cache.pb,0) = data; return;
|
case 0x7f4d: io.cache.pb = io.cache.pb & 0xff00 | data << 0; return;
|
||||||
case 0x7f4e: bit8(io.cache.pb,1) = data; return;
|
case 0x7f4e: io.cache.pb = io.cache.pb & 0x00ff | data << 8; return;
|
||||||
|
|
||||||
case 0x7f4f:
|
case 0x7f4f:
|
||||||
io.cache.pc = data;
|
io.cache.pc = data;
|
||||||
@@ -217,13 +221,13 @@ auto HitachiDSP::writeIO(uint address, uint8 data) -> void {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x7f50:
|
case 0x7f50:
|
||||||
io.wait.ram = bits(data,0-2);
|
io.wait.ram = data >> 0 & 7;
|
||||||
io.wait.rom = bits(data,4-6);
|
io.wait.rom = data >> 4 & 7;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x7f51:
|
case 0x7f51:
|
||||||
io.irq = data & 1;
|
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;
|
return;
|
||||||
|
|
||||||
case 0x7f52:
|
case 0x7f52:
|
||||||
@@ -259,6 +263,11 @@ auto HitachiDSP::writeIO(uint address, uint8 data) -> void {
|
|||||||
//registers
|
//registers
|
||||||
if((address >= 0x7f80 && address <= 0x7faf) || (address >= 0x7fc0 && address <= 0x7fef)) {
|
if((address >= 0x7f80 && address <= 0x7faf) || (address >= 0x7fc0 && address <= 0x7fef)) {
|
||||||
address &= 0x3f;
|
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 {
|
auto ICD::Enter() -> void {
|
||||||
while(true) {
|
while(true) {
|
||||||
scheduler.synchronize();
|
scheduler.synchronize();
|
||||||
@@ -61,7 +65,11 @@ auto ICD::main() -> void {
|
|||||||
stream->sample(float(0.0), float(0.0));
|
stream->sample(float(0.0), float(0.0));
|
||||||
step(128);
|
step(128);
|
||||||
}
|
}
|
||||||
synchronize(cpu);
|
synchronizeCPU();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ICD::step(uint clocks) -> void {
|
||||||
|
clock += clocks * (uint64_t)cpu.frequency;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ICD::load() -> bool {
|
auto ICD::load() -> bool {
|
||||||
@@ -139,9 +147,8 @@ auto ICD::power(bool reset) -> void {
|
|||||||
for(auto& packet : this->packet) packet = {};
|
for(auto& packet : this->packet) packet = {};
|
||||||
packetSize = 0;
|
packetSize = 0;
|
||||||
|
|
||||||
joypID = 3;
|
joypID = 0;
|
||||||
joyp14Lock = 0;
|
joypLock = 1;
|
||||||
joyp15Lock = 0;
|
|
||||||
pulseLock = 1;
|
pulseLock = 1;
|
||||||
strobeLock = 0;
|
strobeLock = 0;
|
||||||
packetLock = 0;
|
packetLock = 0;
|
||||||
|
@@ -4,8 +4,10 @@ struct ICD : Emulator::Platform, Thread {
|
|||||||
|
|
||||||
inline auto pathID() const -> uint { return information.pathID; }
|
inline auto pathID() const -> uint { return information.pathID; }
|
||||||
|
|
||||||
|
auto synchronizeCPU() -> void;
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
|
auto step(uint clocks) -> void;
|
||||||
|
|
||||||
auto load() -> bool;
|
auto load() -> bool;
|
||||||
auto save() -> void;
|
auto save() -> void;
|
||||||
@@ -42,8 +44,7 @@ private:
|
|||||||
uint7 packetSize;
|
uint7 packetSize;
|
||||||
|
|
||||||
uint2 joypID;
|
uint2 joypID;
|
||||||
uint1 joyp14Lock;
|
uint1 joypLock;
|
||||||
uint1 joyp15Lock;
|
|
||||||
uint1 pulseLock;
|
uint1 pulseLock;
|
||||||
uint1 strobeLock;
|
uint1 strobeLock;
|
||||||
uint1 packetLock;
|
uint1 packetLock;
|
||||||
|
@@ -27,9 +27,8 @@ auto ICD::apuWrite(float left, float right) -> void {
|
|||||||
auto ICD::joypWrite(bool p14, bool p15) -> void {
|
auto ICD::joypWrite(bool p14, bool p15) -> void {
|
||||||
//joypad handling
|
//joypad handling
|
||||||
if(p14 == 1 && p15 == 1) {
|
if(p14 == 1 && p15 == 1) {
|
||||||
if(joyp14Lock == 0 && joyp15Lock == 0) {
|
if(joypLock == 0) {
|
||||||
joyp14Lock = 1;
|
joypLock = 1;
|
||||||
joyp15Lock = 1;
|
|
||||||
joypID++;
|
joypID++;
|
||||||
if(mltReq == 0) joypID &= 0; //1-player mode
|
if(mltReq == 0) joypID &= 0; //1-player mode
|
||||||
if(mltReq == 1) joypID &= 1; //2-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;
|
uint4 input = 0xf;
|
||||||
if(p14 == 1 && p15 == 1) input = 0xf - joypID;
|
if(p14 == 1 && p15 == 1) input = 0xf - joypID;
|
||||||
if(p14 == 0) input &= bits(joypad,0-3); //d-pad
|
if(p14 == 0) input &= (joypad >> 0 & 15); //d-pad
|
||||||
if(p15 == 0) input &= bits(joypad,4-7); //buttons
|
if(p15 == 0) input &= (joypad >> 4 & 15); //buttons
|
||||||
|
|
||||||
GB_icd_set_joyp(&sameboy, input);
|
GB_icd_set_joyp(&sameboy, input);
|
||||||
|
|
||||||
if(p14 == 0 && p15 == 1) joyp14Lock = 0;
|
if(p14 == 0 && p15 == 1);
|
||||||
if(p14 == 1 && p15 == 0) joyp15Lock = 0;
|
if(p14 == 1 && p15 == 0) joypLock ^= 1;
|
||||||
|
|
||||||
//packet handling
|
//packet handling
|
||||||
if(p14 == 0 && p15 == 0) { //pulse
|
if(p14 == 0 && p15 == 0) { //pulse
|
||||||
pulseLock = false;
|
pulseLock = 0;
|
||||||
packetOffset = 0;
|
packetOffset = 0;
|
||||||
bitOffset = 0;
|
bitOffset = 0;
|
||||||
strobeLock = true;
|
strobeLock = 1;
|
||||||
packetLock = false;
|
packetLock = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(pulseLock) return;
|
if(pulseLock == 1) return;
|
||||||
|
|
||||||
if(p14 == 1 && p15 == 1) {
|
if(p14 == 1 && p15 == 1) {
|
||||||
strobeLock = false;
|
strobeLock = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(strobeLock) {
|
if(strobeLock == 1) {
|
||||||
if(p14 == 1 || p15 == 1) { //malformed packet
|
if(p14 == 1 || p15 == 1) { //malformed packet
|
||||||
packetLock = false;
|
packetLock = 0;
|
||||||
pulseLock = true;
|
pulseLock = 1;
|
||||||
bitOffset = 0;
|
bitOffset = 0;
|
||||||
packetOffset = 0;
|
packetOffset = 0;
|
||||||
} else {
|
} else {
|
||||||
@@ -85,18 +84,21 @@ auto ICD::joypWrite(bool p14, bool p15) -> void {
|
|||||||
//p14:0, p15:1 = 0
|
//p14:0, p15:1 = 0
|
||||||
//p14:1, p15:0 = 1
|
//p14:1, p15:0 = 1
|
||||||
bool bit = p15 == 0;
|
bool bit = p15 == 0;
|
||||||
strobeLock = true;
|
strobeLock = 1;
|
||||||
|
|
||||||
if(packetLock) {
|
if(packetLock == 1) {
|
||||||
if(p14 == 0 && p15 == 1) {
|
if(p14 == 0 && p15 == 1) {
|
||||||
if((joypPacket[0] >> 3) == 0x11) {
|
if((joypPacket[0] >> 3) == 0x11) {
|
||||||
mltReq = joypPacket[1] & 3;
|
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;
|
if(packetSize < 64) packet[packetSize++] = joypPacket;
|
||||||
packetLock = false;
|
packetLock = 0;
|
||||||
pulseLock = true;
|
pulseLock = 1;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -107,5 +109,5 @@ auto ICD::joypWrite(bool p14, bool p15) -> void {
|
|||||||
joypPacket[packetOffset] = bitData;
|
joypPacket[packetOffset] = bitData;
|
||||||
if(++packetOffset) return;
|
if(++packetOffset) return;
|
||||||
|
|
||||||
packetLock = true;
|
packetLock = 1;
|
||||||
}
|
}
|
||||||
|
@@ -57,10 +57,10 @@ auto ICD::writeIO(uint addr, uint8 data) -> void {
|
|||||||
}
|
}
|
||||||
auto frequency = system.cpuFrequency();
|
auto frequency = system.cpuFrequency();
|
||||||
switch(data & 3) {
|
switch(data & 3) {
|
||||||
case 0: setFrequency(frequency / 4); break; //fast (glitchy, even on real hardware)
|
case 0: this->frequency = frequency / 4; break; //fast (glitchy, even on real hardware)
|
||||||
case 1: setFrequency(frequency / 5); break; //normal
|
case 1: this->frequency = frequency / 5; break; //normal
|
||||||
case 2: setFrequency(frequency / 7); break; //slow
|
case 2: this->frequency = frequency / 7; break; //slow
|
||||||
case 3: setFrequency(frequency / 9); break; //very slow
|
case 3: this->frequency = frequency / 9; break; //very slow
|
||||||
}
|
}
|
||||||
r6003 = data;
|
r6003 = data;
|
||||||
return;
|
return;
|
||||||
|
@@ -16,8 +16,7 @@ auto ICD::serialize(serializer& s) -> void {
|
|||||||
s.integer(packetSize);
|
s.integer(packetSize);
|
||||||
|
|
||||||
s.integer(joypID);
|
s.integer(joypID);
|
||||||
s.integer(joyp14Lock);
|
s.integer(joypLock);
|
||||||
s.integer(joyp15Lock);
|
|
||||||
s.integer(pulseLock);
|
s.integer(pulseLock);
|
||||||
s.integer(strobeLock);
|
s.integer(strobeLock);
|
||||||
s.integer(packetLock);
|
s.integer(packetLock);
|
||||||
|
@@ -40,8 +40,8 @@ auto MCC::read(uint address, uint8 data) -> uint8 {
|
|||||||
case 2: return r.mapping << 7;
|
case 2: return r.mapping << 7;
|
||||||
case 3: return r.psramEnableLo << 7;
|
case 3: return r.psramEnableLo << 7;
|
||||||
case 4: return r.psramEnableHi << 7;
|
case 4: return r.psramEnableHi << 7;
|
||||||
case 5: return r.psramMapping.bit(0) << 7;
|
case 5: return (r.psramMapping >> 0 & 1) << 7;
|
||||||
case 6: return r.psramMapping.bit(1) << 7;
|
case 6: return (r.psramMapping >> 1 & 1) << 7;
|
||||||
case 7: return r.romEnableLo << 7;
|
case 7: return r.romEnableLo << 7;
|
||||||
case 8: return r.romEnableHi << 7;
|
case 8: return r.romEnableHi << 7;
|
||||||
case 9: return r.exEnableLo << 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 {
|
auto MCC::write(uint address, uint8 data) -> void {
|
||||||
if((address & 0xf0f000) == 0x005000) { //$00-0f:5000-5fff
|
if((address & 0xf0f000) == 0x005000) { //$00-0f:5000-5fff
|
||||||
switch(address >> 16 & 15) {
|
switch(address >> 16 & 15) {
|
||||||
case 1: irq.enable = bit1(data,7); break;
|
case 1: irq.enable = data >> 7; break;
|
||||||
case 2: w.mapping = bit1(data,7); break;
|
case 2: w.mapping = data >> 7; break;
|
||||||
case 3: w.psramEnableLo = bit1(data,7); break;
|
case 3: w.psramEnableLo = data >> 7; break;
|
||||||
case 4: w.psramEnableHi = bit1(data,7); break;
|
case 4: w.psramEnableHi = data >> 7; break;
|
||||||
case 5: bit1(w.psramMapping,0) = bit1(data,7); break;
|
case 5: w.psramMapping = w.psramMapping & 2 | data >> 7 << 0; break;
|
||||||
case 6: bit1(w.psramMapping,1) = bit1(data,7); break;
|
case 6: w.psramMapping = w.psramMapping & 1 | data >> 7 << 1; break;
|
||||||
case 7: w.romEnableLo = bit1(data,7); break;
|
case 7: w.romEnableLo = data >> 7; break;
|
||||||
case 8: w.romEnableHi = bit1(data,7); break;
|
case 8: w.romEnableHi = data >> 7; break;
|
||||||
case 9: w.exEnableLo = bit1(data,7); break;
|
case 9: w.exEnableLo = data >> 7; break;
|
||||||
case 10: w.exEnableHi = bit1(data,7); break;
|
case 10: w.exEnableHi = data >> 7; break;
|
||||||
case 11: w.exMapping = bit1(data,7); break;
|
case 11: w.exMapping = data >> 7; break;
|
||||||
case 12: w.internallyWritable = bit1(data,7); break;
|
case 12: w.internallyWritable = data >> 7; break;
|
||||||
case 13: w.externallyWritable = bit1(data,7); break;
|
case 13: w.externallyWritable = data >> 7; break;
|
||||||
case 14: if(bit1(data,7)) commit(); break;
|
case 14: if(data >> 7) commit(); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,11 @@ MSU1 msu1;
|
|||||||
|
|
||||||
#include "serialization.cpp"
|
#include "serialization.cpp"
|
||||||
|
|
||||||
|
auto MSU1::synchronizeCPU() -> void {
|
||||||
|
if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
auto MSU1::Enter() -> void {
|
auto MSU1::Enter() -> void {
|
||||||
while(true) scheduler.synchronize(), msu1.main();
|
while(true) scheduler.synchronize(), msu1.main();
|
||||||
}
|
}
|
||||||
@@ -36,7 +41,11 @@ auto MSU1::main() -> void {
|
|||||||
|
|
||||||
stream->sample(float(left), float(right));
|
stream->sample(float(left), float(right));
|
||||||
step(1);
|
step(1);
|
||||||
synchronize(cpu);
|
synchronizeCPU();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto MSU1::step(uint clocks) -> void {
|
||||||
|
clock += clocks * (uint64_t)cpu.frequency;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto MSU1::unload() -> void {
|
auto MSU1::unload() -> void {
|
||||||
@@ -46,7 +55,7 @@ auto MSU1::unload() -> void {
|
|||||||
|
|
||||||
auto MSU1::power() -> void {
|
auto MSU1::power() -> void {
|
||||||
create(MSU1::Enter, 44100);
|
create(MSU1::Enter, 44100);
|
||||||
stream = Emulator::audio.createStream(2, frequency());
|
stream = Emulator::audio.createStream(2, frequency);
|
||||||
|
|
||||||
io.dataSeekOffset = 0;
|
io.dataSeekOffset = 0;
|
||||||
io.dataReadOffset = 0;
|
io.dataReadOffset = 0;
|
||||||
@@ -98,9 +107,9 @@ auto MSU1::audioOpen() -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto MSU1::readIO(uint addr, uint8) -> uint8 {
|
auto MSU1::readIO(uint addr, uint8) -> uint8 {
|
||||||
cpu.synchronize(*this);
|
cpu.synchronizeCoprocessors();
|
||||||
|
|
||||||
switch(0x2000 | (addr & 7)) {
|
switch(0x2000 | addr & 7) {
|
||||||
case 0x2000:
|
case 0x2000:
|
||||||
return (
|
return (
|
||||||
Revision << 0
|
Revision << 0
|
||||||
@@ -128,18 +137,18 @@ auto MSU1::readIO(uint addr, uint8) -> uint8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto MSU1::writeIO(uint addr, uint8 data) -> void {
|
auto MSU1::writeIO(uint addr, uint8 data) -> void {
|
||||||
cpu.synchronize(*this);
|
cpu.synchronizeCoprocessors();
|
||||||
|
|
||||||
switch(0x2000 | (addr & 7)) {
|
switch(0x2000 | addr & 7) {
|
||||||
case 0x2000: bit8(io.dataSeekOffset,0) = data; break;
|
case 0x2000: io.dataSeekOffset = io.dataSeekOffset & 0xffffff00 | data << 0; break;
|
||||||
case 0x2001: bit8(io.dataSeekOffset,1) = data; break;
|
case 0x2001: io.dataSeekOffset = io.dataSeekOffset & 0xffff00ff | data << 8; break;
|
||||||
case 0x2002: bit8(io.dataSeekOffset,2) = data; break;
|
case 0x2002: io.dataSeekOffset = io.dataSeekOffset & 0xff00ffff | data << 16; break;
|
||||||
case 0x2003: bit8(io.dataSeekOffset,3) = data;
|
case 0x2003: io.dataSeekOffset = io.dataSeekOffset & 0x00ffffff | data << 24;
|
||||||
io.dataReadOffset = io.dataSeekOffset;
|
io.dataReadOffset = io.dataSeekOffset;
|
||||||
if(dataFile) dataFile->seek(io.dataReadOffset);
|
if(dataFile) dataFile->seek(io.dataReadOffset);
|
||||||
break;
|
break;
|
||||||
case 0x2004: bit8(io.audioTrack,0) = data; break;
|
case 0x2004: io.audioTrack = io.audioTrack & 0xff00 | data << 0; break;
|
||||||
case 0x2005: bit8(io.audioTrack,1) = data;
|
case 0x2005: io.audioTrack = io.audioTrack & 0x00ff | data << 8;
|
||||||
io.audioPlay = false;
|
io.audioPlay = false;
|
||||||
io.audioRepeat = false;
|
io.audioRepeat = false;
|
||||||
io.audioPlayOffset = 8;
|
io.audioPlayOffset = 8;
|
||||||
@@ -156,9 +165,9 @@ auto MSU1::writeIO(uint addr, uint8 data) -> void {
|
|||||||
case 0x2007:
|
case 0x2007:
|
||||||
if(io.audioBusy) break;
|
if(io.audioBusy) break;
|
||||||
if(io.audioError) break;
|
if(io.audioError) break;
|
||||||
io.audioPlay = bit1(data,0);
|
io.audioPlay = bool(data & 1);
|
||||||
io.audioRepeat = bit1(data,1);
|
io.audioRepeat = bool(data & 2);
|
||||||
boolean audioResume = bit1(data,2);
|
boolean audioResume = bool(data & 4);
|
||||||
if(!io.audioPlay && audioResume) {
|
if(!io.audioPlay && audioResume) {
|
||||||
io.audioResumeTrack = io.audioTrack;
|
io.audioResumeTrack = io.audioTrack;
|
||||||
io.audioResumeOffset = io.audioPlayOffset;
|
io.audioResumeOffset = io.audioPlayOffset;
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
struct MSU1 : Thread {
|
struct MSU1 : Thread {
|
||||||
shared_pointer<Emulator::Stream> stream;
|
shared_pointer<Emulator::Stream> stream;
|
||||||
|
|
||||||
|
auto synchronizeCPU() -> void;
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
|
auto step(uint clocks) -> void;
|
||||||
auto unload() -> void;
|
auto unload() -> void;
|
||||||
auto power() -> void;
|
auto power() -> void;
|
||||||
|
|
||||||
@@ -15,8 +17,8 @@ struct MSU1 : Thread {
|
|||||||
auto serialize(serializer&) -> void;
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
vfs::shared::file dataFile;
|
shared_pointer<vfs::file> dataFile;
|
||||||
vfs::shared::file audioFile;
|
shared_pointer<vfs::file> audioFile;
|
||||||
|
|
||||||
enum Flag : uint {
|
enum Flag : uint {
|
||||||
Revision = 0x02, //max: 0x07
|
Revision = 0x02, //max: 0x07
|
||||||
|
@@ -1,10 +1,15 @@
|
|||||||
#include <sfc/sfc.hpp>
|
#include <sfc/sfc.hpp>
|
||||||
|
#include <processor/upd96050/upd96050.cpp>
|
||||||
|
|
||||||
namespace SuperFamicom {
|
namespace SuperFamicom {
|
||||||
|
|
||||||
#include "serialization.cpp"
|
#include "serialization.cpp"
|
||||||
NECDSP necdsp;
|
NECDSP necdsp;
|
||||||
|
|
||||||
|
auto NECDSP::synchronizeCPU() -> void {
|
||||||
|
if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread);
|
||||||
|
}
|
||||||
|
|
||||||
auto NECDSP::Enter() -> void {
|
auto NECDSP::Enter() -> void {
|
||||||
while(true) scheduler.synchronize(), necdsp.main();
|
while(true) scheduler.synchronize(), necdsp.main();
|
||||||
}
|
}
|
||||||
@@ -12,11 +17,15 @@ auto NECDSP::Enter() -> void {
|
|||||||
auto NECDSP::main() -> void {
|
auto NECDSP::main() -> void {
|
||||||
exec();
|
exec();
|
||||||
step(1);
|
step(1);
|
||||||
synchronize(cpu);
|
synchronizeCPU();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto NECDSP::step(uint clocks) -> void {
|
||||||
|
clock += clocks * (uint64_t)cpu.frequency;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto NECDSP::read(uint addr, uint8) -> uint8 {
|
auto NECDSP::read(uint addr, uint8) -> uint8 {
|
||||||
cpu.synchronize(*this);
|
cpu.synchronizeCoprocessors();
|
||||||
if(addr & 1) {
|
if(addr & 1) {
|
||||||
return uPD96050::readSR();
|
return uPD96050::readSR();
|
||||||
} else {
|
} else {
|
||||||
@@ -25,7 +34,7 @@ auto NECDSP::read(uint addr, uint8) -> uint8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto NECDSP::write(uint addr, uint8 data) -> void {
|
auto NECDSP::write(uint addr, uint8 data) -> void {
|
||||||
cpu.synchronize(*this);
|
cpu.synchronizeCoprocessors();
|
||||||
if(addr & 1) {
|
if(addr & 1) {
|
||||||
return uPD96050::writeSR(data);
|
return uPD96050::writeSR(data);
|
||||||
} else {
|
} else {
|
||||||
@@ -34,12 +43,12 @@ auto NECDSP::write(uint addr, uint8 data) -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto NECDSP::readRAM(uint addr, uint8) -> uint8 {
|
auto NECDSP::readRAM(uint addr, uint8) -> uint8 {
|
||||||
cpu.synchronize(*this);
|
cpu.synchronizeCoprocessors();
|
||||||
return uPD96050::readDP(addr);
|
return uPD96050::readDP(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto NECDSP::writeRAM(uint addr, uint8 data) -> void {
|
auto NECDSP::writeRAM(uint addr, uint8 data) -> void {
|
||||||
cpu.synchronize(*this);
|
cpu.synchronizeCoprocessors();
|
||||||
return uPD96050::writeDP(addr, data);
|
return uPD96050::writeDP(addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
struct NECDSP : Processor::uPD96050, Thread {
|
struct NECDSP : Processor::uPD96050, Thread {
|
||||||
|
auto synchronizeCPU() -> void;
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
|
auto step(uint clocks) -> void;
|
||||||
|
|
||||||
auto read(uint addr, uint8 data) -> uint8;
|
auto read(uint addr, uint8 data) -> uint8;
|
||||||
auto write(uint addr, uint8 data) -> void;
|
auto write(uint addr, uint8 data) -> void;
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
auto SA1::BWRAM::conflict() const -> bool {
|
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 & 0x40e000) == 0x006000) return true; //00-3f,80-bf:6000-7fff
|
||||||
if((cpu.r.mar & 0xf00000) == 0x400000) return true; //40-4f:0000-ffff
|
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
|
//00-3f,80-bf:6000-7fff size=0x2000 => 00:0000-1fff
|
||||||
//40-4f:0000-ffff => untranslated
|
//40-4f:0000-ffff => untranslated
|
||||||
auto SA1::BWRAM::readCPU(uint address, uint8 data) -> uint8 {
|
auto SA1::BWRAM::readCPU(uint address, uint8 data) -> uint8 {
|
||||||
cpu.synchronize(sa1);
|
cpu.synchronizeCoprocessors();
|
||||||
|
|
||||||
if(address < 0x2000) { //$00-3f,80-bf:6000-7fff
|
if(address < 0x2000) { //$00-3f,80-bf:6000-7fff
|
||||||
address = sa1.mmio.sbm * 0x2000 + (address & 0x1fff);
|
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 {
|
auto SA1::BWRAM::writeCPU(uint address, uint8 data) -> void {
|
||||||
cpu.synchronize(sa1);
|
cpu.synchronizeCoprocessors();
|
||||||
|
|
||||||
if(address < 0x2000) { //$00-3f,80-bf:6000-7fff
|
if(address < 0x2000) { //$00-3f,80-bf:6000-7fff
|
||||||
address = sa1.mmio.sbm * 0x2000 + (address & 0x1fff);
|
address = sa1.mmio.sbm * 0x2000 + (address & 0x1fff);
|
||||||
@@ -80,18 +80,18 @@ auto SA1::BWRAM::readBitmap(uint20 address, uint8 data) -> uint8 {
|
|||||||
uint shift = address & 1;
|
uint shift = address & 1;
|
||||||
address >>= 1;
|
address >>= 1;
|
||||||
switch(shift) {
|
switch(shift) {
|
||||||
case 0: return cbits(read(address),0-3);
|
case 0: return read(address) >> 0 & 15;
|
||||||
case 1: return cbits(read(address),4-7);
|
case 1: return read(address) >> 4 & 15;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//2bpp
|
//2bpp
|
||||||
uint shift = address & 3;
|
uint shift = address & 3;
|
||||||
address >>= 2;
|
address >>= 2;
|
||||||
switch(shift) {
|
switch(shift) {
|
||||||
case 0: return cbits(read(address),0-1);
|
case 0: return read(address) >> 0 & 3;
|
||||||
case 1: return cbits(read(address),2-3);
|
case 1: return read(address) >> 2 & 3;
|
||||||
case 2: return cbits(read(address),4-5);
|
case 2: return read(address) >> 4 & 3;
|
||||||
case 3: return cbits(read(address),6-7);
|
case 3: return read(address) >> 6 & 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unreachable;
|
unreachable;
|
||||||
|
@@ -51,7 +51,7 @@ auto SA1::dmaCC1() -> void {
|
|||||||
mmio.chdma_irqfl = true;
|
mmio.chdma_irqfl = true;
|
||||||
if(mmio.chdma_irqen) {
|
if(mmio.chdma_irqen) {
|
||||||
mmio.chdma_irqcl = 0;
|
mmio.chdma_irqcl = 0;
|
||||||
cpu.r.irq = 1;
|
cpu.irq(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
auto SA1::readIOCPU(uint address, uint8 data) -> uint8 {
|
auto SA1::readIOCPU(uint address, uint8 data) -> uint8 {
|
||||||
cpu.synchronize(sa1);
|
cpu.synchronizeCoprocessors();
|
||||||
|
|
||||||
switch(0x2200 | address & 0x1ff) {
|
switch(0x2200 | address & 0x1ff) {
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ auto SA1::readIOCPU(uint address, uint8 data) -> uint8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto SA1::readIOSA1(uint address, uint8) -> uint8 {
|
auto SA1::readIOSA1(uint address, uint8) -> uint8 {
|
||||||
synchronize(cpu);
|
synchronizeCPU();
|
||||||
|
|
||||||
switch(0x2200 | address & 0x1ff) {
|
switch(0x2200 | address & 0x1ff) {
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@ auto SA1::readIOSA1(uint address, uint8) -> uint8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto SA1::writeIOCPU(uint address, uint8 data) -> void {
|
auto SA1::writeIOCPU(uint address, uint8 data) -> void {
|
||||||
cpu.synchronize(sa1);
|
cpu.synchronizeCoprocessors();
|
||||||
|
|
||||||
switch(0x2200 | address & 0x1ff) {
|
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_irqen && (data & 0x80)) {
|
||||||
if(mmio.cpu_irqfl) {
|
if(mmio.cpu_irqfl) {
|
||||||
mmio.cpu_irqcl = 0;
|
mmio.cpu_irqcl = 0;
|
||||||
cpu.r.irq = 1;
|
cpu.irq(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!mmio.chdma_irqen && (data & 0x20)) {
|
if(!mmio.chdma_irqen && (data & 0x20)) {
|
||||||
if(mmio.chdma_irqfl) {
|
if(mmio.chdma_irqfl) {
|
||||||
mmio.chdma_irqcl = 0;
|
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.cpu_irqcl ) mmio.cpu_irqfl = false;
|
||||||
if(mmio.chdma_irqcl) mmio.chdma_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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,7 +236,7 @@ auto SA1::writeIOCPU(uint address, uint8 data) -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto SA1::writeIOSA1(uint address, uint8 data) -> void {
|
auto SA1::writeIOSA1(uint address, uint8 data) -> void {
|
||||||
synchronize(cpu);
|
synchronizeCPU();
|
||||||
|
|
||||||
switch(0x2200 | address & 0x1ff) {
|
switch(0x2200 | address & 0x1ff) {
|
||||||
|
|
||||||
@@ -251,7 +251,7 @@ auto SA1::writeIOSA1(uint address, uint8 data) -> void {
|
|||||||
mmio.cpu_irqfl = true;
|
mmio.cpu_irqfl = true;
|
||||||
if(mmio.cpu_irqen) {
|
if(mmio.cpu_irqen) {
|
||||||
mmio.cpu_irqcl = 0;
|
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
|
//(MAL) multiplicand / dividend low
|
||||||
case 0x2251: {
|
case 0x2251: {
|
||||||
bit8(mmio.ma,0) = data;
|
mmio.ma = mmio.ma & ~0x00ff | data << 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//(MAH) multiplicand / dividend high
|
//(MAH) multiplicand / dividend high
|
||||||
case 0x2252: {
|
case 0x2252: {
|
||||||
bit8(mmio.ma,1) = data;
|
mmio.ma = mmio.ma & ~0xff00 | data << 8;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//(MBL) multiplier / divisor low
|
//(MBL) multiplier / divisor low
|
||||||
case 0x2253: {
|
case 0x2253: {
|
||||||
bit8(mmio.mb,0) = data;
|
mmio.mb = mmio.mb & ~0x00ff | data << 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -423,7 +423,7 @@ auto SA1::writeIOSA1(uint address, uint8 data) -> void {
|
|||||||
//multiplication / cumulative sum only resets MB
|
//multiplication / cumulative sum only resets MB
|
||||||
//division resets both MA and MB
|
//division resets both MA and MB
|
||||||
case 0x2254: {
|
case 0x2254: {
|
||||||
bit8(mmio.mb,1) = data;
|
mmio.mb = mmio.mb & ~0xff00 | data << 8;
|
||||||
|
|
||||||
if(mmio.acm == 0) {
|
if(mmio.acm == 0) {
|
||||||
if(mmio.md == 0) {
|
if(mmio.md == 0) {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
auto SA1::IRAM::conflict() const -> bool {
|
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
|
if((cpu.r.mar & 0x40f800) == 0x003000) return cpu.refresh() == 0; //00-3f,80-bf:3000-37ff
|
||||||
return false;
|
return false;
|
||||||
@@ -18,12 +18,12 @@ auto SA1::IRAM::write(uint address, uint8 data) -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto SA1::IRAM::readCPU(uint address, uint8 data) -> uint8 {
|
auto SA1::IRAM::readCPU(uint address, uint8 data) -> uint8 {
|
||||||
cpu.synchronize(sa1);
|
cpu.synchronizeCoprocessors();
|
||||||
return read(address, data);
|
return read(address, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SA1::IRAM::writeCPU(uint address, uint8 data) -> void {
|
auto SA1::IRAM::writeCPU(uint address, uint8 data) -> void {
|
||||||
cpu.synchronize(sa1);
|
cpu.synchronizeCoprocessors();
|
||||||
return write(address, data);
|
return write(address, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -18,7 +18,7 @@ auto SA1::idleBranch() -> void {
|
|||||||
if(r.pc.d & 1) idleJump();
|
if(r.pc.d & 1) idleJump();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SA1::read(uint24 address) -> uint8 {
|
auto SA1::read(uint address) -> uint8 {
|
||||||
r.mar = address;
|
r.mar = address;
|
||||||
uint8 data = r.mdr;
|
uint8 data = r.mdr;
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ auto SA1::read(uint24 address) -> uint8 {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SA1::write(uint24 address, uint8 data) -> void {
|
auto SA1::write(uint address, uint8 data) -> void {
|
||||||
r.mar = address;
|
r.mar = address;
|
||||||
r.mdr = data;
|
r.mdr = data;
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
auto SA1::ROM::conflict() const -> bool {
|
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 & 0x408000) == 0x008000) return true; //00-3f,80-bf:8000-ffff
|
||||||
if((cpu.r.mar & 0xc00000) == 0xc00000) return true; //c0-ff:0000-ffff
|
if((cpu.r.mar & 0xc00000) == 0xc00000) return true; //c0-ff:0000-ffff
|
||||||
|
@@ -11,6 +11,10 @@ namespace SuperFamicom {
|
|||||||
#include "serialization.cpp"
|
#include "serialization.cpp"
|
||||||
SA1 sa1;
|
SA1 sa1;
|
||||||
|
|
||||||
|
auto SA1::synchronizeCPU() -> void {
|
||||||
|
if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread);
|
||||||
|
}
|
||||||
|
|
||||||
auto SA1::Enter() -> void {
|
auto SA1::Enter() -> void {
|
||||||
while(true) scheduler.synchronize(), sa1.main();
|
while(true) scheduler.synchronize(), sa1.main();
|
||||||
}
|
}
|
||||||
@@ -78,13 +82,9 @@ auto SA1::interruptPending() const -> bool {
|
|||||||
return status.interruptPending;
|
return status.interruptPending;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SA1::synchronizing() const -> bool {
|
|
||||||
return scheduler.synchronizing();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto SA1::step() -> void {
|
auto SA1::step() -> void {
|
||||||
Thread::step(2);
|
clock += (uint64_t)cpu.frequency << 1;
|
||||||
synchronize(cpu);
|
synchronizeCPU();
|
||||||
|
|
||||||
//adjust counters:
|
//adjust counters:
|
||||||
//note that internally, status counters are in clocks;
|
//note that internally, status counters are in clocks;
|
||||||
@@ -127,8 +127,10 @@ auto SA1::unload() -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto SA1::power() -> void {
|
auto SA1::power() -> void {
|
||||||
|
double overclock = max(1.0, min(4.0, configuration.hacks.sa1.overclock / 100.0));
|
||||||
|
|
||||||
WDC65816::power();
|
WDC65816::power();
|
||||||
create(SA1::Enter, system.cpuFrequency());
|
create(SA1::Enter, system.cpuFrequency() * overclock);
|
||||||
|
|
||||||
bwram.dma = false;
|
bwram.dma = false;
|
||||||
for(uint address : range(iram.size())) {
|
for(uint address : range(iram.size())) {
|
||||||
|
@@ -1,7 +1,10 @@
|
|||||||
//Super Accelerator (SA-1)
|
//Super Accelerator (SA-1)
|
||||||
|
|
||||||
struct SA1 : Processor::WDC65816, Thread {
|
struct SA1 : Processor::WDC65816, Thread {
|
||||||
|
inline auto synchronizing() const -> bool override { return scheduler.mode == Scheduler::Mode::SynchronizeAll; }
|
||||||
|
|
||||||
//sa1.cpp
|
//sa1.cpp
|
||||||
|
auto synchronizeCPU() -> void;
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
auto step() -> void;
|
auto step() -> void;
|
||||||
@@ -10,7 +13,6 @@ struct SA1 : Processor::WDC65816, Thread {
|
|||||||
alwaysinline auto triggerIRQ() -> void;
|
alwaysinline auto triggerIRQ() -> void;
|
||||||
alwaysinline auto lastCycle() -> void override;
|
alwaysinline auto lastCycle() -> void override;
|
||||||
alwaysinline auto interruptPending() const -> bool override;
|
alwaysinline auto interruptPending() const -> bool override;
|
||||||
auto synchronizing() const -> bool override;
|
|
||||||
|
|
||||||
auto unload() -> void;
|
auto unload() -> void;
|
||||||
auto power() -> void;
|
auto power() -> void;
|
||||||
@@ -36,8 +38,8 @@ struct SA1 : Processor::WDC65816, Thread {
|
|||||||
alwaysinline auto idle() -> void override;
|
alwaysinline auto idle() -> void override;
|
||||||
alwaysinline auto idleJump() -> void override;
|
alwaysinline auto idleJump() -> void override;
|
||||||
alwaysinline auto idleBranch() -> void override;
|
alwaysinline auto idleBranch() -> void override;
|
||||||
alwaysinline auto read(uint24 address) -> uint8 override;
|
alwaysinline auto read(uint address) -> uint8 override;
|
||||||
alwaysinline auto write(uint24 address, uint8 data) -> void override;
|
alwaysinline auto write(uint address, uint8 data) -> void override;
|
||||||
auto readVBR(uint address, uint8 data = 0) -> uint8;
|
auto readVBR(uint address, uint8 data = 0) -> uint8;
|
||||||
auto readDisassembler(uint address) -> uint8 override;
|
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 {
|
auto SDD1::dmaWrite(uint addr, uint8 data) -> void {
|
||||||
uint channel = addr >> 4 & 7;
|
uint channel = addr >> 4 & 7;
|
||||||
switch(addr & 15) {
|
switch(addr & 15) {
|
||||||
case 2: bit8(dma[channel].addr,0) = data; break;
|
case 2: dma[channel].addr = dma[channel].addr & 0xffff00 | data << 0; break;
|
||||||
case 3: bit8(dma[channel].addr,1) = data; break;
|
case 3: dma[channel].addr = dma[channel].addr & 0xff00ff | data << 8; break;
|
||||||
case 4: bit8(dma[channel].addr,2) = data; break;
|
case 4: dma[channel].addr = dma[channel].addr & 0x00ffff | data << 16; break;
|
||||||
case 5: bit8(dma[channel].size,0) = data; break;
|
case 5: dma[channel].size = dma[channel].size & 0xff00 | data << 0; break;
|
||||||
case 6: bit8(dma[channel].size,1) = data; break;
|
case 6: dma[channel].size = dma[channel].size & 0x00ff | data << 8; break;
|
||||||
}
|
}
|
||||||
return cpu.writeDMA(addr, data);
|
return cpu.writeDMA(addr, data);
|
||||||
}
|
}
|
||||||
@@ -100,7 +100,7 @@ auto SDD1::mcuRead(uint addr, uint8 data) -> uint8 {
|
|||||||
if(r4800 & r4801) {
|
if(r4800 & r4801) {
|
||||||
//at least one channel has S-DD1 decompression enabled ...
|
//at least one channel has S-DD1 decompression enabled ...
|
||||||
for(auto n : range(8)) {
|
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
|
//S-DD1 always uses fixed transfer mode, so address will not change during transfer
|
||||||
if(addr == dma[n].addr) {
|
if(addr == dma[n].addr) {
|
||||||
if(!dmaReady) {
|
if(!dmaReady) {
|
||||||
@@ -113,7 +113,7 @@ auto SDD1::mcuRead(uint addr, uint8 data) -> uint8 {
|
|||||||
data = decompressor.read();
|
data = decompressor.read();
|
||||||
if(--dma[n].size == 0) {
|
if(--dma[n].size == 0) {
|
||||||
dmaReady = false;
|
dmaReady = false;
|
||||||
bit1(r4801,n) = 0;
|
r4801 &= ~(1 << n);
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
@@ -7,6 +7,10 @@ namespace SuperFamicom {
|
|||||||
#include "serialization.cpp"
|
#include "serialization.cpp"
|
||||||
SharpRTC sharprtc;
|
SharpRTC sharprtc;
|
||||||
|
|
||||||
|
auto SharpRTC::synchronizeCPU() -> void {
|
||||||
|
if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread);
|
||||||
|
}
|
||||||
|
|
||||||
auto SharpRTC::Enter() -> void {
|
auto SharpRTC::Enter() -> void {
|
||||||
while(true) scheduler.synchronize(), sharprtc.main();
|
while(true) scheduler.synchronize(), sharprtc.main();
|
||||||
}
|
}
|
||||||
@@ -15,7 +19,11 @@ auto SharpRTC::main() -> void {
|
|||||||
tickSecond();
|
tickSecond();
|
||||||
|
|
||||||
step(1);
|
step(1);
|
||||||
synchronize(cpu);
|
synchronizeCPU();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto SharpRTC::step(uint clocks) -> void {
|
||||||
|
clock += clocks * (uint64_t)cpu.frequency;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SharpRTC::initialize() -> void {
|
auto SharpRTC::initialize() -> void {
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
struct SharpRTC : Thread {
|
struct SharpRTC : Thread {
|
||||||
using Thread::synchronize;
|
auto synchronizeCPU() -> void;
|
||||||
|
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
|
auto step(uint clocks) -> void;
|
||||||
|
|
||||||
auto initialize() -> void;
|
auto initialize() -> void;
|
||||||
auto power() -> void;
|
auto power() -> void;
|
||||||
|
@@ -16,6 +16,10 @@ SPC7110::~SPC7110() {
|
|||||||
delete decompressor;
|
delete decompressor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto SPC7110::synchronizeCPU() -> void {
|
||||||
|
if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread);
|
||||||
|
}
|
||||||
|
|
||||||
auto SPC7110::Enter() -> void {
|
auto SPC7110::Enter() -> void {
|
||||||
while(true) scheduler.synchronize(), spc7110.main();
|
while(true) scheduler.synchronize(), spc7110.main();
|
||||||
}
|
}
|
||||||
@@ -27,9 +31,13 @@ auto SPC7110::main() -> void {
|
|||||||
addClocks(1);
|
addClocks(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto SPC7110::step(uint clocks) -> void {
|
||||||
|
clock += clocks * (uint64_t)cpu.frequency;
|
||||||
|
}
|
||||||
|
|
||||||
auto SPC7110::addClocks(uint clocks) -> void {
|
auto SPC7110::addClocks(uint clocks) -> void {
|
||||||
step(clocks);
|
step(clocks);
|
||||||
synchronize(cpu);
|
synchronizeCPU();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SPC7110::unload() -> void {
|
auto SPC7110::unload() -> void {
|
||||||
@@ -96,7 +104,7 @@ auto SPC7110::power() -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto SPC7110::read(uint addr, uint8 data) -> uint8 {
|
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) == 0x500000) addr = 0x4800; //$50:0000-ffff == $4800
|
||||||
if((addr & 0xff0000) == 0x580000) addr = 0x4808; //$58:0000-ffff == $4808
|
if((addr & 0xff0000) == 0x580000) addr = 0x4808; //$58:0000-ffff == $4808
|
||||||
addr = 0x4800 | (addr & 0x3f); //$00-3f,80-bf:4800-483f
|
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 {
|
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) == 0x500000) addr = 0x4800; //$50:0000-ffff == $4800
|
||||||
if((addr & 0xff0000) == 0x580000) addr = 0x4808; //$58:0000-ffff == $4808
|
if((addr & 0xff0000) == 0x580000) addr = 0x4808; //$58:0000-ffff == $4808
|
||||||
addr = 0x4800 | (addr & 0x3f); //$00-3f,80-bf:4800-483f
|
addr = 0x4800 | (addr & 0x3f); //$00-3f,80-bf:4800-483f
|
||||||
|
@@ -4,8 +4,10 @@ struct SPC7110 : Thread {
|
|||||||
SPC7110();
|
SPC7110();
|
||||||
~SPC7110();
|
~SPC7110();
|
||||||
|
|
||||||
|
auto synchronizeCPU() -> void;
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
|
auto step(uint clocks) -> void;
|
||||||
auto unload() -> void;
|
auto unload() -> void;
|
||||||
auto power() -> void;
|
auto power() -> void;
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
auto SuperFX::stop() -> void {
|
auto SuperFX::stop() -> void {
|
||||||
cpu.r.irq = 1;
|
cpu.irq(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SuperFX::color(uint8 source) -> uint8 {
|
auto SuperFX::color(uint8 source) -> uint8 {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
auto SuperFX::readIO(uint addr, uint8) -> uint8 {
|
auto SuperFX::readIO(uint addr, uint8) -> uint8 {
|
||||||
cpu.synchronize(*this);
|
cpu.synchronizeCoprocessors();
|
||||||
addr = 0x3000 | addr & 0x3ff;
|
addr = 0x3000 | addr & 0x3ff;
|
||||||
|
|
||||||
if(addr >= 0x3100 && addr <= 0x32ff) {
|
if(addr >= 0x3100 && addr <= 0x32ff) {
|
||||||
@@ -18,7 +18,7 @@ auto SuperFX::readIO(uint addr, uint8) -> uint8 {
|
|||||||
case 0x3031: {
|
case 0x3031: {
|
||||||
uint8 r = regs.sfr >> 8;
|
uint8 r = regs.sfr >> 8;
|
||||||
regs.sfr.irq = 0;
|
regs.sfr.irq = 0;
|
||||||
cpu.r.irq = 0;
|
cpu.irq(0);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ auto SuperFX::readIO(uint addr, uint8) -> uint8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto SuperFX::writeIO(uint addr, uint8 data) -> void {
|
auto SuperFX::writeIO(uint addr, uint8 data) -> void {
|
||||||
cpu.synchronize(*this);
|
cpu.synchronizeCoprocessors();
|
||||||
addr = 0x3000 | addr & 0x3ff;
|
addr = 0x3000 | addr & 0x3ff;
|
||||||
|
|
||||||
if(addr >= 0x3100 && addr <= 0x32ff) {
|
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
|
if((addr & 0xc00000) == 0x000000) { //$00-3f:0000-7fff,:8000-ffff
|
||||||
while(!regs.scmr.ron) {
|
while(!regs.scmr.ron) {
|
||||||
step(6);
|
step(6);
|
||||||
synchronize(cpu);
|
synchronizeCPU();
|
||||||
if(scheduler.synchronizing()) break;
|
if(synchronizing()) break;
|
||||||
}
|
}
|
||||||
return rom.read((((addr & 0x3f0000) >> 1) | (addr & 0x7fff)) & romMask);
|
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
|
if((addr & 0xe00000) == 0x400000) { //$40-5f:0000-ffff
|
||||||
while(!regs.scmr.ron) {
|
while(!regs.scmr.ron) {
|
||||||
step(6);
|
step(6);
|
||||||
synchronize(cpu);
|
synchronizeCPU();
|
||||||
if(scheduler.synchronizing()) break;
|
if(synchronizing()) break;
|
||||||
}
|
}
|
||||||
return rom.read(addr & romMask);
|
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
|
if((addr & 0xe00000) == 0x600000) { //$60-7f:0000-ffff
|
||||||
while(!regs.scmr.ran) {
|
while(!regs.scmr.ran) {
|
||||||
step(6);
|
step(6);
|
||||||
synchronize(cpu);
|
synchronizeCPU();
|
||||||
if(scheduler.synchronizing()) break;
|
if(synchronizing()) break;
|
||||||
}
|
}
|
||||||
return ram.read(addr & ramMask);
|
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
|
if((addr & 0xe00000) == 0x600000) { //$60-7f:0000-ffff
|
||||||
while(!regs.scmr.ran) {
|
while(!regs.scmr.ran) {
|
||||||
step(6);
|
step(6);
|
||||||
synchronize(cpu);
|
synchronizeCPU();
|
||||||
if(scheduler.synchronizing()) break;
|
if(synchronizing()) break;
|
||||||
}
|
}
|
||||||
return ram.write(addr & ramMask, data);
|
return ram.write(addr & ramMask, data);
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
#include <sfc/sfc.hpp>
|
#include <sfc/sfc.hpp>
|
||||||
|
#include <processor/gsu/gsu.cpp>
|
||||||
|
|
||||||
namespace SuperFamicom {
|
namespace SuperFamicom {
|
||||||
|
|
||||||
@@ -10,6 +11,10 @@ namespace SuperFamicom {
|
|||||||
#include "serialization.cpp"
|
#include "serialization.cpp"
|
||||||
SuperFX superfx;
|
SuperFX superfx;
|
||||||
|
|
||||||
|
auto SuperFX::synchronizeCPU() -> void {
|
||||||
|
if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread);
|
||||||
|
}
|
||||||
|
|
||||||
auto SuperFX::Enter() -> void {
|
auto SuperFX::Enter() -> void {
|
||||||
while(true) scheduler.synchronize(), superfx.main();
|
while(true) scheduler.synchronize(), superfx.main();
|
||||||
}
|
}
|
||||||
@@ -37,8 +42,10 @@ auto SuperFX::unload() -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto SuperFX::power() -> void {
|
auto SuperFX::power() -> void {
|
||||||
|
double overclock = max(1.0, min(8.0, configuration.hacks.superfx.overclock / 100.0));
|
||||||
|
|
||||||
GSU::power();
|
GSU::power();
|
||||||
create(SuperFX::Enter, Frequency);
|
create(SuperFX::Enter, Frequency * overclock);
|
||||||
|
|
||||||
romMask = rom.size() - 1;
|
romMask = rom.size() - 1;
|
||||||
ramMask = ram.size() - 1;
|
ramMask = ram.size() - 1;
|
||||||
|
@@ -2,7 +2,10 @@ struct SuperFX : Processor::GSU, Thread {
|
|||||||
ReadableMemory rom;
|
ReadableMemory rom;
|
||||||
WritableMemory ram;
|
WritableMemory ram;
|
||||||
|
|
||||||
|
inline auto synchronizing() const -> bool { return scheduler.mode == Scheduler::Mode::SynchronizeAll; }
|
||||||
|
|
||||||
//superfx.cpp
|
//superfx.cpp
|
||||||
|
auto synchronizeCPU() -> void;
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
auto unload() -> void;
|
auto unload() -> void;
|
||||||
|
@@ -14,8 +14,8 @@ auto SuperFX::step(uint clocks) -> void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread::step(clocks);
|
clock += clocks * (uint64_t)cpu.frequency;
|
||||||
synchronize(cpu);
|
synchronizeCPU();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SuperFX::syncROMBuffer() -> void {
|
auto SuperFX::syncROMBuffer() -> void {
|
||||||
|
@@ -10,38 +10,54 @@ CPU cpu;
|
|||||||
#include "irq.cpp"
|
#include "irq.cpp"
|
||||||
#include "serialization.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 {
|
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 {
|
auto CPU::main() -> void {
|
||||||
if(r.wai) return instructionWait();
|
if(r.wai) return instructionWait();
|
||||||
if(r.stp) return instructionStop();
|
if(r.stp) return instructionStop();
|
||||||
|
if(!status.interruptPending) return instruction();
|
||||||
|
|
||||||
if(status.interruptPending) {
|
if(status.nmiPending) {
|
||||||
status.interruptPending = false;
|
status.nmiPending = 0;
|
||||||
if(status.nmiPending) {
|
r.vector = r.e ? 0xfffa : 0xffea;
|
||||||
status.nmiPending = false;
|
return interrupt();
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
auto CPU::load() -> bool {
|
||||||
@@ -58,8 +74,8 @@ auto CPU::power(bool reset) -> void {
|
|||||||
PPUcounter::reset();
|
PPUcounter::reset();
|
||||||
PPUcounter::scanline = {&CPU::scanline, this};
|
PPUcounter::scanline = {&CPU::scanline, this};
|
||||||
|
|
||||||
function<auto (uint, uint8) -> uint8> reader;
|
function<uint8 (uint, uint8)> reader;
|
||||||
function<auto (uint, uint8) -> void> writer;
|
function<void (uint, uint8)> writer;
|
||||||
|
|
||||||
reader = {&CPU::readRAM, this};
|
reader = {&CPU::readRAM, this};
|
||||||
writer = {&CPU::writeRAM, this};
|
writer = {&CPU::writeRAM, this};
|
||||||
@@ -80,6 +96,15 @@ auto CPU::power(bool reset) -> void {
|
|||||||
|
|
||||||
if(!reset) random.array(wram, sizeof(wram));
|
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)) {
|
for(uint n : range(8)) {
|
||||||
channels[n] = {};
|
channels[n] = {};
|
||||||
if(n != 7) channels[n].next = channels[n + 1];
|
if(n != 7) channels[n].next = channels[n + 1];
|
||||||
@@ -90,13 +115,11 @@ auto CPU::power(bool reset) -> void {
|
|||||||
alu = {};
|
alu = {};
|
||||||
|
|
||||||
status = {};
|
status = {};
|
||||||
status.lineClocks = lineclocks();
|
|
||||||
status.dramRefreshPosition = (version == 1 ? 530 : 538);
|
status.dramRefreshPosition = (version == 1 ? 530 : 538);
|
||||||
status.hdmaSetupPosition = (version == 1 ? 12 + 8 - dmaCounter() : 12 + dmaCounter());
|
status.hdmaSetupPosition = (version == 1 ? 12 + 8 - dmaCounter() : 12 + dmaCounter());
|
||||||
status.hdmaPosition = 1104;
|
status.hdmaPosition = 1104;
|
||||||
status.powerPending = reset == 0;
|
status.resetPending = 1;
|
||||||
status.resetPending = reset == 1;
|
status.interruptPending = 1;
|
||||||
status.interruptPending = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -2,9 +2,12 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter {
|
|||||||
inline auto interruptPending() const -> bool override { return status.interruptPending; }
|
inline auto interruptPending() const -> bool override { return status.interruptPending; }
|
||||||
inline auto pio() const -> uint8 { return io.pio; }
|
inline auto pio() const -> uint8 { return io.pio; }
|
||||||
inline auto refresh() const -> bool { return status.dramRefresh == 1; }
|
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
|
//cpu.cpp
|
||||||
|
auto synchronizeSMP() -> void;
|
||||||
|
auto synchronizePPU() -> void;
|
||||||
|
auto synchronizeCoprocessors() -> void;
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
auto load() -> bool;
|
auto load() -> bool;
|
||||||
@@ -22,8 +25,8 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter {
|
|||||||
|
|
||||||
//memory.cpp
|
//memory.cpp
|
||||||
auto idle() -> void override;
|
auto idle() -> void override;
|
||||||
auto read(uint24 addr) -> uint8 override;
|
auto read(uint addr) -> uint8 override;
|
||||||
auto write(uint24 addr, uint8 data) -> void override;
|
auto write(uint addr, uint8 data) -> void override;
|
||||||
auto readDisassembler(uint addr) -> uint8 override;
|
auto readDisassembler(uint addr) -> uint8 override;
|
||||||
|
|
||||||
//io.cpp
|
//io.cpp
|
||||||
@@ -37,7 +40,6 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter {
|
|||||||
auto writeDMA(uint address, uint8 data) -> void;
|
auto writeDMA(uint address, uint8 data) -> void;
|
||||||
|
|
||||||
//timing.cpp
|
//timing.cpp
|
||||||
inline auto dmaClocks() const -> uint;
|
|
||||||
inline auto dmaCounter() const -> uint;
|
inline auto dmaCounter() const -> uint;
|
||||||
inline auto joypadCounter() const -> uint;
|
inline auto joypadCounter() const -> uint;
|
||||||
|
|
||||||
@@ -48,7 +50,6 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter {
|
|||||||
|
|
||||||
alwaysinline auto aluEdge() -> void;
|
alwaysinline auto aluEdge() -> void;
|
||||||
alwaysinline auto dmaEdge() -> void;
|
alwaysinline auto dmaEdge() -> void;
|
||||||
alwaysinline auto lastCycle() -> void;
|
|
||||||
|
|
||||||
//irq.cpp
|
//irq.cpp
|
||||||
alwaysinline auto pollInterrupts() -> void;
|
alwaysinline auto pollInterrupts() -> void;
|
||||||
@@ -58,6 +59,7 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter {
|
|||||||
|
|
||||||
alwaysinline auto nmiTest() -> bool;
|
alwaysinline auto nmiTest() -> bool;
|
||||||
alwaysinline auto irqTest() -> bool;
|
alwaysinline auto irqTest() -> bool;
|
||||||
|
alwaysinline auto lastCycle() -> void;
|
||||||
|
|
||||||
//joypad.cpp
|
//joypad.cpp
|
||||||
auto joypadEdge() -> void;
|
auto joypadEdge() -> void;
|
||||||
@@ -68,6 +70,11 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter {
|
|||||||
uint8 wram[128 * 1024];
|
uint8 wram[128 * 1024];
|
||||||
vector<Thread*> coprocessors;
|
vector<Thread*> coprocessors;
|
||||||
|
|
||||||
|
struct Overclocking {
|
||||||
|
uint counter = 0;
|
||||||
|
uint target = 0;
|
||||||
|
} overclocking;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint version = 2; //allowed: 1, 2
|
uint version = 2; //allowed: 1, 2
|
||||||
|
|
||||||
@@ -78,18 +85,17 @@ private:
|
|||||||
|
|
||||||
struct Status {
|
struct Status {
|
||||||
uint clockCount = 0;
|
uint clockCount = 0;
|
||||||
uint lineClocks = 0;
|
|
||||||
|
|
||||||
bool irqLock = false;
|
bool irqLock = 0;
|
||||||
|
|
||||||
uint dramRefreshPosition = 0;
|
uint dramRefreshPosition = 0;
|
||||||
uint dramRefresh = 0; //0 = not refreshed; 1 = refresh active; 2 = refresh inactive
|
uint dramRefresh = 0; //0 = not refreshed; 1 = refresh active; 2 = refresh inactive
|
||||||
|
|
||||||
uint hdmaSetupPosition = 0;
|
uint hdmaSetupPosition = 0;
|
||||||
bool hdmaSetupTriggered = false;
|
bool hdmaSetupTriggered = 0;
|
||||||
|
|
||||||
uint hdmaPosition = 0;
|
uint hdmaPosition = 0;
|
||||||
bool hdmaTriggered = false;
|
bool hdmaTriggered = 0;
|
||||||
|
|
||||||
boolean nmiValid = 0;
|
boolean nmiValid = 0;
|
||||||
boolean nmiLine = 0;
|
boolean nmiLine = 0;
|
||||||
@@ -103,18 +109,16 @@ private:
|
|||||||
boolean irqPending = 0;
|
boolean irqPending = 0;
|
||||||
boolean irqHold = 0;
|
boolean irqHold = 0;
|
||||||
|
|
||||||
bool powerPending = false;
|
bool resetPending = 0;
|
||||||
bool resetPending = false;
|
bool interruptPending = 0;
|
||||||
|
|
||||||
bool interruptPending = false;
|
bool dmaActive = 0;
|
||||||
|
bool dmaPending = 0;
|
||||||
bool dmaActive = false;
|
bool hdmaPending = 0;
|
||||||
bool dmaPending = false;
|
|
||||||
bool hdmaPending = false;
|
|
||||||
bool hdmaMode = 0; //0 = init, 1 = run
|
bool hdmaMode = 0; //0 = init, 1 = run
|
||||||
|
|
||||||
bool autoJoypadActive = false;
|
bool autoJoypadActive = 0;
|
||||||
bool autoJoypadLatch = false;
|
bool autoJoypadLatch = 0;
|
||||||
uint autoJoypadCounter = 0;
|
uint autoJoypadCounter = 0;
|
||||||
} status;
|
} status;
|
||||||
|
|
||||||
|
@@ -14,6 +14,7 @@ auto CPU::hdmaActive() -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::dmaRun() -> void {
|
auto CPU::dmaRun() -> void {
|
||||||
|
counter.dma += 8;
|
||||||
step<8,0>();
|
step<8,0>();
|
||||||
dmaEdge();
|
dmaEdge();
|
||||||
for(auto& channel : channels) channel.dmaRun();
|
for(auto& channel : channels) channel.dmaRun();
|
||||||
@@ -25,12 +26,14 @@ auto CPU::hdmaReset() -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::hdmaSetup() -> void {
|
auto CPU::hdmaSetup() -> void {
|
||||||
|
counter.dma += 8;
|
||||||
step<8,0>();
|
step<8,0>();
|
||||||
for(auto& channel : channels) channel.hdmaSetup();
|
for(auto& channel : channels) channel.hdmaSetup();
|
||||||
status.irqLock = true;
|
status.irqLock = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::hdmaRun() -> void {
|
auto CPU::hdmaRun() -> void {
|
||||||
|
counter.dma += 8;
|
||||||
step<8,0>();
|
step<8,0>();
|
||||||
for(auto& channel : channels) channel.hdmaTransfer();
|
for(auto& channel : channels) channel.hdmaTransfer();
|
||||||
for(auto& channel : channels) channel.hdmaAdvance();
|
for(auto& channel : channels) channel.hdmaAdvance();
|
||||||
@@ -40,8 +43,14 @@ auto CPU::hdmaRun() -> void {
|
|||||||
//
|
//
|
||||||
|
|
||||||
template<uint Clocks, bool Synchronize>
|
template<uint Clocks, bool Synchronize>
|
||||||
auto CPU::Channel::step() -> void { return cpu.step<Clocks, Synchronize>(); }
|
auto CPU::Channel::step() -> void {
|
||||||
auto CPU::Channel::edge() -> void { return cpu.dmaEdge(); }
|
cpu.counter.dma += Clocks;
|
||||||
|
cpu.step<Clocks, Synchronize>();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto CPU::Channel::edge() -> void {
|
||||||
|
cpu.dmaEdge();
|
||||||
|
}
|
||||||
|
|
||||||
auto CPU::Channel::validA(uint24 address) -> bool {
|
auto CPU::Channel::validA(uint24 address) -> bool {
|
||||||
//A-bus cannot access the B-bus or CPU I/O registers
|
//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 {
|
auto CPU::Channel::hdmaAdvance() -> void {
|
||||||
if(!hdmaActive()) return;
|
if(!hdmaActive()) return;
|
||||||
lineCounter--;
|
lineCounter--;
|
||||||
hdmaDoTransfer = bit1(lineCounter,7);
|
hdmaDoTransfer = bool(lineCounter & 0x80);
|
||||||
hdmaReload();
|
hdmaReload();
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,7 @@ auto CPU::readRAM(uint addr, uint8 data) -> uint8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::readAPU(uint addr, uint8 data) -> uint8 {
|
auto CPU::readAPU(uint addr, uint8 data) -> uint8 {
|
||||||
synchronize(smp);
|
synchronizeSMP();
|
||||||
return smp.portRead(addr & 3);
|
return smp.portRead(addr & 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,21 +41,21 @@ auto CPU::readCPU(uint addr, uint8 data) -> uint8 {
|
|||||||
data |= (vcounter() >= ppu.vdisp()) << 7; //vblank
|
data |= (vcounter() >= ppu.vdisp()) << 7; //vblank
|
||||||
return data;
|
return data;
|
||||||
|
|
||||||
case 0x4213: return io.pio; //RDIO
|
case 0x4213: return io.pio; //RDIO
|
||||||
|
|
||||||
case 0x4214: return bit8(io.rddiv,0); //RDDIVL
|
case 0x4214: return io.rddiv >> 0; //RDDIVL
|
||||||
case 0x4215: return bit8(io.rddiv,1); //RDDIVH
|
case 0x4215: return io.rddiv >> 8; //RDDIVH
|
||||||
case 0x4216: return bit8(io.rdmpy,0); //RDMPYL
|
case 0x4216: return io.rdmpy >> 0; //RDMPYL
|
||||||
case 0x4217: return bit8(io.rdmpy,1); //RDMPYH
|
case 0x4217: return io.rdmpy >> 8; //RDMPYH
|
||||||
|
|
||||||
case 0x4218: return bit8(io.joy1,0); //JOY1L
|
case 0x4218: return io.joy1 >> 0; //JOY1L
|
||||||
case 0x4219: return bit8(io.joy1,1); //JOY1H
|
case 0x4219: return io.joy1 >> 8; //JOY1H
|
||||||
case 0x421a: return bit8(io.joy2,0); //JOY2L
|
case 0x421a: return io.joy2 >> 0; //JOY2L
|
||||||
case 0x421b: return bit8(io.joy2,1); //JOY2H
|
case 0x421b: return io.joy2 >> 8; //JOY2H
|
||||||
case 0x421c: return bit8(io.joy3,0); //JOY3L
|
case 0x421c: return io.joy3 >> 0; //JOY3L
|
||||||
case 0x421d: return bit8(io.joy3,1); //JOY3H
|
case 0x421d: return io.joy3 >> 8; //JOY3H
|
||||||
case 0x421e: return bit8(io.joy4,0); //JOY4L
|
case 0x421e: return io.joy4 >> 0; //JOY4L
|
||||||
case 0x421f: return bit8(io.joy4,1); //JOY4H
|
case 0x421f: return io.joy4 >> 8; //JOY4H
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,18 +77,18 @@ auto CPU::readDMA(uint addr, uint8 data) -> uint8 {
|
|||||||
| channel.direction << 7
|
| channel.direction << 7
|
||||||
);
|
);
|
||||||
|
|
||||||
case 0x4301: return channel.targetAddress; //BBADx
|
case 0x4301: return channel.targetAddress; //BBADx
|
||||||
case 0x4302: return bit8(channel.sourceAddress,0); //A1TxL
|
case 0x4302: return channel.sourceAddress >> 0; //A1TxL
|
||||||
case 0x4303: return bit8(channel.sourceAddress,1); //A1TxH
|
case 0x4303: return channel.sourceAddress >> 8; //A1TxH
|
||||||
case 0x4304: return channel.sourceBank; //A1Bx
|
case 0x4304: return channel.sourceBank; //A1Bx
|
||||||
case 0x4305: return bit8(channel.transferSize,0); //DASxL
|
case 0x4305: return channel.transferSize >> 0; //DASxL
|
||||||
case 0x4306: return bit8(channel.transferSize,1); //DASxH
|
case 0x4306: return channel.transferSize >> 8; //DASxH
|
||||||
case 0x4307: return channel.indirectBank; //DASBx
|
case 0x4307: return channel.indirectBank; //DASBx
|
||||||
case 0x4308: return bit8(channel.hdmaAddress,0); //A2AxL
|
case 0x4308: return channel.hdmaAddress >> 0; //A2AxL
|
||||||
case 0x4309: return bit8(channel.hdmaAddress,1); //A2AxH
|
case 0x4309: return channel.hdmaAddress >> 8; //A2AxH
|
||||||
case 0x430a: return channel.lineCounter; //NTRLx
|
case 0x430a: return channel.lineCounter; //NTRLx
|
||||||
case 0x430b: return channel.unknown; //???x
|
case 0x430b: return channel.unknown; //???x
|
||||||
case 0x430f: return channel.unknown; //???x ($43xb mirror)
|
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 {
|
auto CPU::writeAPU(uint addr, uint8 data) -> void {
|
||||||
synchronize(smp);
|
synchronizeSMP();
|
||||||
return smp.portWrite(addr & 3, data);
|
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);
|
return bus.write(0x7e0000 | io.wramAddress++, data);
|
||||||
|
|
||||||
case 0x2181: //WMADDL
|
case 0x2181: //WMADDL
|
||||||
bits(io.wramAddress,0-7) = data;
|
io.wramAddress = io.wramAddress & 0x1ff00 | data << 0;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x2182: //WMADDM
|
case 0x2182: //WMADDM
|
||||||
bits(io.wramAddress,8-15) = data;
|
io.wramAddress = io.wramAddress & 0x100ff | data << 8;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x2183: //WMADDH
|
case 0x2183: //WMADDH
|
||||||
bit1(io.wramAddress,16) = bit1(data,0);
|
io.wramAddress = io.wramAddress & 0x0ffff | (data & 1) << 16;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x4016: //JOYSER0
|
case 0x4016: //JOYSER0
|
||||||
@@ -136,7 +136,7 @@ auto CPU::writeCPU(uint addr, uint8 data) -> void {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x4201: //WRIO
|
case 0x4201: //WRIO
|
||||||
if(bit1(io.pio,7) && !bit1(data,7)) ppu.latchCounters();
|
if((io.pio & 0x80) && !(data & 0x80)) ppu.latchCounters();
|
||||||
io.pio = data;
|
io.pio = data;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -149,18 +149,22 @@ auto CPU::writeCPU(uint addr, uint8 data) -> void {
|
|||||||
if(alu.mpyctr || alu.divctr) return;
|
if(alu.mpyctr || alu.divctr) return;
|
||||||
|
|
||||||
io.wrmpyb = data;
|
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
|
if(!configuration.hacks.cpu.fastMath) {
|
||||||
alu.shift = io.wrmpyb;
|
alu.mpyctr = 8; //perform multiplication over the next eight cycles
|
||||||
|
alu.shift = io.wrmpyb;
|
||||||
|
} else {
|
||||||
|
io.rdmpy = io.wrmpya * io.wrmpyb;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x4204: //WRDIVL
|
case 0x4204: //WRDIVL
|
||||||
bit8(io.wrdiva,0) = data;
|
io.wrdiva = io.wrdiva & 0xff00 | data << 0;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x4205: //WRDIVH
|
case 0x4205: //WRDIVH
|
||||||
bit8(io.wrdiva,1) = data;
|
io.wrdiva = io.wrdiva & 0x00ff | data << 8;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x4206: //WRDIVB
|
case 0x4206: //WRDIVB
|
||||||
@@ -169,37 +173,47 @@ auto CPU::writeCPU(uint addr, uint8 data) -> void {
|
|||||||
|
|
||||||
io.wrdivb = data;
|
io.wrdivb = data;
|
||||||
|
|
||||||
alu.divctr = 16; //perform division over the next sixteen cycles
|
if(!configuration.hacks.cpu.fastMath) {
|
||||||
alu.shift = io.wrdivb << 16;
|
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;
|
return;
|
||||||
|
|
||||||
case 0x4207: //HTIMEL
|
case 0x4207: //HTIMEL
|
||||||
io.htime = (io.htime >> 2) - 1;
|
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;
|
io.htime = (io.htime + 1) << 2;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x4208: //HTIMEH
|
case 0x4208: //HTIMEH
|
||||||
io.htime = (io.htime >> 2) - 1;
|
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;
|
io.htime = (io.htime + 1) << 2;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x4209: //VTIMEL
|
case 0x4209: //VTIMEL
|
||||||
bits(io.vtime,0-7) = data;
|
io.vtime = io.vtime & 0x100 | data << 0;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x420a: //VTIMEH
|
case 0x420a: //VTIMEH
|
||||||
bit1(io.vtime,8) = data & 1;
|
io.vtime = io.vtime & 0x0ff | (data & 1) << 8;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x420b: //DMAEN
|
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;
|
if(data) status.dmaPending = true;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x420c: //HDMAEN
|
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;
|
return;
|
||||||
|
|
||||||
case 0x420d: //MEMSEL
|
case 0x420d: //MEMSEL
|
||||||
@@ -215,12 +229,12 @@ auto CPU::writeDMA(uint addr, uint8 data) -> void {
|
|||||||
switch(addr & 0xff8f) {
|
switch(addr & 0xff8f) {
|
||||||
|
|
||||||
case 0x4300: //DMAPx
|
case 0x4300: //DMAPx
|
||||||
channel.transferMode = bits(data,0-2);
|
channel.transferMode = data >> 0 & 7;
|
||||||
channel.fixedTransfer = bit1(data,3);
|
channel.fixedTransfer = data >> 3 & 1;
|
||||||
channel.reverseTransfer = bit1(data,4);
|
channel.reverseTransfer = data >> 4 & 1;
|
||||||
channel.unused = bit1(data,5);
|
channel.unused = data >> 5 & 1;
|
||||||
channel.indirect = bit1(data,6);
|
channel.indirect = data >> 6 & 1;
|
||||||
channel.direction = bit1(data,7);
|
channel.direction = data >> 7 & 1;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x4301: //BBADx
|
case 0x4301: //BBADx
|
||||||
@@ -228,11 +242,11 @@ auto CPU::writeDMA(uint addr, uint8 data) -> void {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x4302: //A1TxL
|
case 0x4302: //A1TxL
|
||||||
bit8(channel.sourceAddress,0) = data;
|
channel.sourceAddress = channel.sourceAddress & 0xff00 | data << 0;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x4303: //A1TxH
|
case 0x4303: //A1TxH
|
||||||
bit8(channel.sourceAddress,1) = data;
|
channel.sourceAddress = channel.sourceAddress & 0x00ff | data << 8;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x4304: //A1Bx
|
case 0x4304: //A1Bx
|
||||||
@@ -240,11 +254,11 @@ auto CPU::writeDMA(uint addr, uint8 data) -> void {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x4305: //DASxL
|
case 0x4305: //DASxL
|
||||||
bit8(channel.transferSize,0) = data;
|
channel.transferSize = channel.transferSize & 0xff00 | data << 0;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x4306: //DASxH
|
case 0x4306: //DASxH
|
||||||
bit8(channel.transferSize,1) = data;
|
channel.transferSize = channel.transferSize & 0x00ff | data << 8;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x4307: //DASBx
|
case 0x4307: //DASBx
|
||||||
@@ -252,11 +266,11 @@ auto CPU::writeDMA(uint addr, uint8 data) -> void {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x4308: //A2AxL
|
case 0x4308: //A2AxL
|
||||||
bit8(channel.hdmaAddress,0) = data;
|
channel.hdmaAddress = channel.hdmaAddress & 0xff00 | data << 0;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x4309: //A2AxH
|
case 0x4309: //A2AxH
|
||||||
bit8(channel.hdmaAddress,1) = data;
|
channel.hdmaAddress = channel.hdmaAddress & 0x00ff | data << 8;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x430a: //NTRLx
|
case 0x430a: //NTRLx
|
||||||
|
@@ -5,48 +5,52 @@
|
|||||||
//it is used to emulate hardware communication delay between opcode and interrupt units.
|
//it is used to emulate hardware communication delay between opcode and interrupt units.
|
||||||
auto CPU::pollInterrupts() -> void {
|
auto CPU::pollInterrupts() -> void {
|
||||||
//NMI hold
|
//NMI hold
|
||||||
if(status.nmiHold.lower() && io.nmiEnable) status.nmiTransition = true;
|
if(status.nmiHold.lower() && io.nmiEnable) {
|
||||||
|
status.nmiTransition = 1;
|
||||||
|
}
|
||||||
|
|
||||||
//NMI test
|
//NMI test
|
||||||
if(status.nmiValid.flip(vcounter(2) >= ppu.vdisp())) {
|
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
|
//IRQ hold
|
||||||
status.irqHold = false;
|
status.irqHold = 0;
|
||||||
if(status.irqLine && io.irqEnable) status.irqTransition = true;
|
if(status.irqLine && io.irqEnable) {
|
||||||
|
status.irqTransition = 1;
|
||||||
|
}
|
||||||
|
|
||||||
//IRQ test
|
//IRQ test
|
||||||
if(status.irqValid.raise(io.irqEnable
|
if(status.irqValid.raise(io.irqEnable
|
||||||
&& (!io.virqEnable || vcounter(10) == io.vtime)
|
&& (!io.virqEnable || vcounter(10) == io.vtime)
|
||||||
&& (!io.hirqEnable || hcounter(10) == io.htime)
|
&& (!io.hirqEnable || hcounter(10) == io.htime)
|
||||||
&& (vcounter(6) || hcounter(6)) //IRQs cannot trigger on last dot of fields
|
&& (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 {
|
auto CPU::nmitimenUpdate(uint8 data) -> void {
|
||||||
io.hirqEnable = bit1(data,4);
|
io.hirqEnable = data & 0x10;
|
||||||
io.virqEnable = bit1(data,5);
|
io.virqEnable = data & 0x20;
|
||||||
io.irqEnable = io.hirqEnable || io.virqEnable;
|
io.irqEnable = io.hirqEnable || io.virqEnable;
|
||||||
|
|
||||||
if(io.virqEnable && !io.hirqEnable && status.irqLine) {
|
if(io.virqEnable && !io.hirqEnable && status.irqLine) {
|
||||||
status.irqTransition = true;
|
status.irqTransition = 1;
|
||||||
} else if(!io.irqEnable) {
|
} else if(!io.irqEnable) {
|
||||||
status.irqLine = false;
|
status.irqLine = 0;
|
||||||
status.irqTransition = false;
|
status.irqTransition = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(io.nmiEnable.raise(bit1(data,7)) && status.nmiLine) {
|
if(io.nmiEnable.raise(data & 0x80) && status.nmiLine) {
|
||||||
status.nmiTransition = true;
|
status.nmiTransition = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
status.irqLock = true;
|
status.irqLock = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::rdnmi() -> bool {
|
auto CPU::rdnmi() -> bool {
|
||||||
bool result = status.nmiLine;
|
bool result = status.nmiLine;
|
||||||
if(!status.nmiHold) {
|
if(!status.nmiHold) {
|
||||||
status.nmiLine = false;
|
status.nmiLine = 0;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -54,22 +58,34 @@ auto CPU::rdnmi() -> bool {
|
|||||||
auto CPU::timeup() -> bool {
|
auto CPU::timeup() -> bool {
|
||||||
bool result = status.irqLine;
|
bool result = status.irqLine;
|
||||||
if(!status.irqHold) {
|
if(!status.irqHold) {
|
||||||
status.irqLine = false;
|
status.irqLine = 0;
|
||||||
status.irqTransition = false;
|
status.irqTransition = 0;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::nmiTest() -> bool {
|
auto CPU::nmiTest() -> bool {
|
||||||
if(!status.nmiTransition) return false;
|
if(!status.nmiTransition) return 0;
|
||||||
status.nmiTransition = false;
|
status.nmiTransition = 0;
|
||||||
r.wai = false;
|
r.wai = 0;
|
||||||
return true;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::irqTest() -> bool {
|
auto CPU::irqTest() -> bool {
|
||||||
if(!status.irqTransition && !r.irq) return false;
|
if(!status.irqTransition && !r.irq) return 0;
|
||||||
status.irqTransition = false;
|
status.irqTransition = 0;
|
||||||
r.wai = false;
|
r.wai = 0;
|
||||||
return !r.p.i;
|
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 {
|
auto CPU::idle() -> void {
|
||||||
status.irqLock = false;
|
|
||||||
status.clockCount = 6;
|
status.clockCount = 6;
|
||||||
dmaEdge();
|
dmaEdge();
|
||||||
step<6,0>();
|
step<6,0>();
|
||||||
|
status.irqLock = 0;
|
||||||
aluEdge();
|
aluEdge();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::read(uint24 address) -> uint8 {
|
auto CPU::read(uint address) -> uint8 {
|
||||||
status.irqLock = false;
|
|
||||||
|
|
||||||
if(address & 0x408000) {
|
if(address & 0x408000) {
|
||||||
if(address & 0x800000 && io.fastROM) {
|
if(address & 0x800000 && io.fastROM) {
|
||||||
status.clockCount = 6;
|
status.clockCount = 6;
|
||||||
@@ -38,6 +36,7 @@ auto CPU::read(uint24 address) -> uint8 {
|
|||||||
step<8,1>();
|
step<8,1>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
status.irqLock = 0;
|
||||||
auto data = bus.read(address, r.mdr);
|
auto data = bus.read(address, r.mdr);
|
||||||
step<4,0>();
|
step<4,0>();
|
||||||
aluEdge();
|
aluEdge();
|
||||||
@@ -46,8 +45,7 @@ auto CPU::read(uint24 address) -> uint8 {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::write(uint24 address, uint8 data) -> void {
|
auto CPU::write(uint address, uint8 data) -> void {
|
||||||
status.irqLock = false;
|
|
||||||
aluEdge();
|
aluEdge();
|
||||||
|
|
||||||
if(address & 0x408000) {
|
if(address & 0x408000) {
|
||||||
@@ -79,6 +77,7 @@ auto CPU::write(uint24 address, uint8 data) -> void {
|
|||||||
step<12,1>();
|
step<12,1>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
status.irqLock = 0;
|
||||||
bus.write(address, r.mdr = data);
|
bus.write(address, r.mdr = data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -11,7 +11,6 @@ auto CPU::serialize(serializer& s) -> void {
|
|||||||
s.integer(counter.dma);
|
s.integer(counter.dma);
|
||||||
|
|
||||||
s.integer(status.clockCount);
|
s.integer(status.clockCount);
|
||||||
s.integer(status.lineClocks);
|
|
||||||
|
|
||||||
s.integer(status.irqLock);
|
s.integer(status.irqLock);
|
||||||
|
|
||||||
@@ -36,9 +35,7 @@ auto CPU::serialize(serializer& s) -> void {
|
|||||||
s.boolean(status.irqPending);
|
s.boolean(status.irqPending);
|
||||||
s.boolean(status.irqHold);
|
s.boolean(status.irqHold);
|
||||||
|
|
||||||
s.integer(status.powerPending);
|
|
||||||
s.integer(status.resetPending);
|
s.integer(status.resetPending);
|
||||||
|
|
||||||
s.integer(status.interruptPending);
|
s.integer(status.interruptPending);
|
||||||
|
|
||||||
s.integer(status.dmaActive);
|
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
|
//DMA clock divider
|
||||||
auto CPU::dmaCounter() const -> uint {
|
auto CPU::dmaCounter() const -> uint {
|
||||||
return counter.cpu & 7;
|
return counter.cpu & 7;
|
||||||
@@ -27,13 +18,31 @@ auto CPU::stepOnce() -> void {
|
|||||||
template<uint Clocks, bool Synchronize>
|
template<uint Clocks, bool Synchronize>
|
||||||
auto CPU::step() -> void {
|
auto CPU::step() -> void {
|
||||||
static_assert(Clocks == 2 || Clocks == 4 || Clocks == 6 || Clocks == 8 || Clocks == 10 || Clocks == 12);
|
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 >= 2) stepOnce();
|
||||||
if constexpr(Clocks >= 4) stepOnce();
|
if constexpr(Clocks >= 4) stepOnce();
|
||||||
if constexpr(Clocks >= 6) stepOnce();
|
if constexpr(Clocks >= 6) stepOnce();
|
||||||
if constexpr(Clocks >= 8) stepOnce();
|
if constexpr(Clocks >= 8) stepOnce();
|
||||||
if constexpr(Clocks >= 10) stepOnce();
|
if constexpr(Clocks >= 10) stepOnce();
|
||||||
if constexpr(Clocks >= 12) stepOnce();
|
if constexpr(Clocks >= 12) stepOnce();
|
||||||
Thread::step(Clocks);
|
|
||||||
|
smp.clock -= Clocks * (uint64)smp.frequency;
|
||||||
|
ppu.clock -= Clocks;
|
||||||
|
|
||||||
if(!status.dramRefresh && hcounter() >= status.dramRefreshPosition) {
|
if(!status.dramRefresh && hcounter() >= status.dramRefreshPosition) {
|
||||||
//note: pattern should technically be 5-3, 5-3, 5-3, 5-3, 5-3 per logic analyzer
|
//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 constexpr(Synchronize) {
|
||||||
if(configuration.hacks.coprocessors.delayedSync) return;
|
if(configuration.hacks.coprocessor.delayedSync) return;
|
||||||
for(auto coprocessor : coprocessors) synchronize(*coprocessor);
|
synchronizeCoprocessors();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,12 +73,10 @@ auto CPU::step(uint clocks) -> void {
|
|||||||
|
|
||||||
//called by ppu.tick() when Hcounter=0
|
//called by ppu.tick() when Hcounter=0
|
||||||
auto CPU::scanline() -> void {
|
auto CPU::scanline() -> void {
|
||||||
status.lineClocks = lineclocks();
|
|
||||||
|
|
||||||
//forcefully sync S-CPU to other processors, in case chips are not communicating
|
//forcefully sync S-CPU to other processors, in case chips are not communicating
|
||||||
synchronize(smp);
|
synchronizeSMP();
|
||||||
synchronize(ppu);
|
synchronizePPU();
|
||||||
for(auto coprocessor : coprocessors) synchronize(*coprocessor);
|
synchronizeCoprocessors();
|
||||||
|
|
||||||
if(vcounter() == 0) {
|
if(vcounter() == 0) {
|
||||||
//HDMA setup triggers once every frame
|
//HDMA setup triggers once every frame
|
||||||
@@ -88,6 +95,17 @@ auto CPU::scanline() -> void {
|
|||||||
status.hdmaPosition = 1104;
|
status.hdmaPosition = 1104;
|
||||||
status.hdmaTriggered = false;
|
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 {
|
auto CPU::aluEdge() -> void {
|
||||||
@@ -123,12 +141,11 @@ auto CPU::dmaEdge() -> void {
|
|||||||
status.hdmaPending = false;
|
status.hdmaPending = false;
|
||||||
if(hdmaEnable()) {
|
if(hdmaEnable()) {
|
||||||
if(!dmaEnable()) {
|
if(!dmaEnable()) {
|
||||||
counter.dma = counter.cpu;
|
step(counter.dma = 8 - dmaCounter());
|
||||||
step(8 - dmaCounter());
|
|
||||||
}
|
}
|
||||||
status.hdmaMode == 0 ? hdmaSetup() : hdmaRun();
|
status.hdmaMode == 0 ? hdmaSetup() : hdmaRun();
|
||||||
if(!dmaEnable()) {
|
if(!dmaEnable()) {
|
||||||
step(status.clockCount - dmaClocks() % status.clockCount);
|
step(status.clockCount - counter.dma % status.clockCount);
|
||||||
status.dmaActive = false;
|
status.dmaActive = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,10 +154,9 @@ auto CPU::dmaEdge() -> void {
|
|||||||
if(status.dmaPending) {
|
if(status.dmaPending) {
|
||||||
status.dmaPending = false;
|
status.dmaPending = false;
|
||||||
if(dmaEnable()) {
|
if(dmaEnable()) {
|
||||||
counter.dma = counter.cpu;
|
step(counter.dma = 8 - dmaCounter());
|
||||||
step(8 - dmaCounter());
|
|
||||||
dmaRun();
|
dmaRun();
|
||||||
step(status.clockCount - dmaClocks() % status.clockCount);
|
step(status.clockCount - counter.dma % status.clockCount);
|
||||||
status.dmaActive = false;
|
status.dmaActive = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -203,16 +219,3 @@ auto CPU::joypadEdge() -> void {
|
|||||||
status.autoJoypadCounter++;
|
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 )
|
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);
|
m.t_srcn = VREG(v->regs,srcn);
|
||||||
}
|
}
|
||||||
inline VOICE_CLOCK( V2 )
|
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
|
//// Echo
|
||||||
|
|
||||||
// Current echo buffer pointer for left/right channel
|
// 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
|
// Sample in echo history buffer, where 0 is the oldest
|
||||||
#define ECHO_FIR( i ) (m.echo_hist_pos [i])
|
#define ECHO_FIR( i ) (m.echo_hist_pos [i])
|
||||||
@@ -835,9 +835,10 @@ void SPC_DSP::run( int clocks_remain )
|
|||||||
|
|
||||||
//// Setup
|
//// 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 );
|
mute_voices( 0 );
|
||||||
disable_surround( false );
|
disable_surround( false );
|
||||||
set_output( 0, 0 );
|
set_output( 0, 0 );
|
||||||
@@ -861,7 +862,8 @@ void SPC_DSP::init( void* ram_64k )
|
|||||||
|
|
||||||
void SPC_DSP::soft_reset_common()
|
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.noise = 0x4000;
|
||||||
m.echo_hist_pos = m.echo_hist;
|
m.echo_hist_pos = m.echo_hist;
|
||||||
|
@@ -15,7 +15,7 @@ public:
|
|||||||
// Setup
|
// Setup
|
||||||
|
|
||||||
// Initializes DSP and has it use the 64K RAM provided
|
// 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,
|
// Sets destination for output samples. If out is NULL or out_size is 0,
|
||||||
// doesn't generate any.
|
// doesn't generate any.
|
||||||
@@ -178,7 +178,8 @@ private:
|
|||||||
voice_t voices [voice_count];
|
voice_t voices [voice_count];
|
||||||
|
|
||||||
// non-emulation state
|
// 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;
|
int mute_mask;
|
||||||
sample_t* out;
|
sample_t* out;
|
||||||
sample_t* out_end;
|
sample_t* out_end;
|
||||||
@@ -231,6 +232,9 @@ private:
|
|||||||
void echo_30();
|
void echo_30();
|
||||||
|
|
||||||
void soft_reset_common();
|
void soft_reset_common();
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool mute() { return m.regs[r_flg] & 0x40; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#include <assert.h>
|
#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