mirror of
https://github.com/ares-emulator/ares.git
synced 2026-11-13 03:57:15 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e6e9fd36e | ||
|
|
8111bf3163 | ||
|
|
c287c1b225 | ||
|
|
863593ed7f | ||
|
|
c20d405006 | ||
|
|
e1e1bedb2d | ||
|
|
9f060c769b | ||
|
|
e7f9af4bbd | ||
|
|
65c34c2ea4 | ||
|
|
9babe6a02b | ||
|
|
7e5292cd16 | ||
|
|
a8274cd127 | ||
|
|
d99b0a3725 | ||
|
|
e43f3f75f7 | ||
|
|
f894a85253 | ||
|
|
09fbcc4e65 | ||
|
|
73d90e6e7c | ||
|
|
38dd6defa9 | ||
|
|
030488aba7 | ||
|
|
b2175473f0 | ||
|
|
bc2a54800f | ||
|
|
6e182d8542 | ||
|
|
38f64fa70c | ||
|
|
7eb1b28671 | ||
|
|
42c16831b7 | ||
|
|
5982fa8ae3 | ||
|
|
5f435345f1 | ||
|
|
8343f8136c | ||
|
|
4dcba42240 | ||
|
|
81d5cdba32 | ||
|
|
5151003fa4 | ||
|
|
c6a838d6ee | ||
|
|
349f1d281c | ||
|
|
cac4aab301 | ||
|
|
5911338cac | ||
|
|
c0af33d4ce | ||
|
|
ea77623238 | ||
|
|
033752679f | ||
|
|
025fc6450a | ||
|
|
d6858ad821 | ||
|
|
0b46609e95 | ||
|
|
b5a8edc5d0 | ||
|
|
757a7dd670 | ||
|
|
950573df14 | ||
|
|
f43da416fd | ||
|
|
18f45ddc85 | ||
|
|
7135646c44 | ||
|
|
4ab8472a53 | ||
|
|
a47e793b57 | ||
|
|
1ffb4f6d5a | ||
|
|
aa350116d4 | ||
|
|
e8a5579cb7 | ||
|
|
f091fcfacd | ||
|
|
fa9d350a66 | ||
|
|
5ff5df0e2e | ||
|
|
efb7f61f04 | ||
|
|
3cd82c8cfe | ||
|
|
ace4115c86 | ||
|
|
31ca4552ea | ||
|
|
873aded84b | ||
|
|
4dc46976ef | ||
|
|
156603ceff | ||
|
|
12f0d04709 | ||
|
|
693447d0e0 | ||
|
|
27719b391b | ||
|
|
3455da48df | ||
|
|
dac6a2069f | ||
|
|
39174a2a79 | ||
|
|
23e322fd57 | ||
|
|
5c0c1fb4aa | ||
|
|
c27a62b624 | ||
|
|
9c93735045 | ||
|
|
9e03af99d9 | ||
|
|
eec5104909 | ||
|
|
00a67f1671 | ||
|
|
a76847dbd6 | ||
|
|
f209d544b0 | ||
|
|
5453fe084a | ||
|
|
426fb79639 | ||
|
|
33c645a441 | ||
|
|
ba9a291784 | ||
|
|
9fb67ba7a7 | ||
|
|
54794f86d9 | ||
|
|
7f43d0658e | ||
|
|
0d24374019 | ||
|
|
78a5e584b5 | ||
|
|
ffe97222a1 | ||
|
|
71e5f89f8b | ||
|
|
cac454a6a9 | ||
|
|
6c8265577c | ||
|
|
401aab6d64 | ||
|
|
c0fb9ba815 | ||
|
|
356208f312 | ||
|
|
fbf10d14fc | ||
|
|
723d8a8cd4 | ||
|
|
6eedb9399b | ||
|
|
36f821f5ac | ||
|
|
3bbfde5e9b | ||
|
|
b8e355b218 | ||
|
|
c34ed82fa3 | ||
|
|
3de728fa3b | ||
|
|
2b0738ec56 | ||
|
|
79e25f30bf | ||
|
|
5bf09dac13 | ||
|
|
b4fafd9028 | ||
|
|
21f730968b | ||
|
|
6cd4470562 | ||
|
|
393ba03358 | ||
|
|
9159438401 | ||
|
|
3372230021 | ||
|
|
8c4af6ac4c | ||
|
|
b5ee4124e1 | ||
|
|
4dbd6fad37 | ||
|
|
a876f4479d | ||
|
|
afa3f08f8c | ||
|
|
371d6eeaa8 | ||
|
|
e1a940cafa | ||
|
|
7c4a36a482 | ||
|
|
bea295a915 | ||
|
|
db1d29ebcf | ||
|
|
54845f6194 | ||
|
|
7c7d2c2155 | ||
|
|
edb88e2e3a | ||
|
|
ac75269342 | ||
|
|
bfc9636403 | ||
|
|
c6ca36e447 | ||
|
|
4e97f23446 | ||
|
|
d7dfae7a7a | ||
|
|
62f1512d44 | ||
|
|
e6481d975e | ||
|
|
61a94f025e | ||
|
|
d76e79076f | ||
|
|
a574410a03 | ||
|
|
dc97c814a3 | ||
|
|
9325b04b23 | ||
|
|
762a2302f7 | ||
|
|
2b6346df6e | ||
|
|
587aa20dcc | ||
|
|
4615a9f4b7 | ||
|
|
fc7cdb57f7 | ||
|
|
0a8f25e7d8 | ||
|
|
f3cdf203c3 | ||
|
|
76615ce847 | ||
|
|
5e04d6d31b | ||
|
|
2d81319a2f | ||
|
|
4b3b8b9f5b | ||
|
|
36df7ab00f | ||
|
|
47d6d25687 | ||
|
|
117f4f7b4e | ||
|
|
c23a3f736d | ||
|
|
dd25ac4f72 | ||
|
|
804e6699a4 | ||
|
|
c2dede6c85 | ||
|
|
6cabb5d118 | ||
|
|
5c92d877c8 | ||
|
|
9db8decbd6 | ||
|
|
0ef0a3e3a7 | ||
|
|
dbb3b85bb3 | ||
|
|
c94794e959 | ||
|
|
2abc056d06 | ||
|
|
9664144d1b | ||
|
|
f31d209883 | ||
|
|
21209edad6 | ||
|
|
8f3261e870 | ||
|
|
a65bf18645 | ||
|
|
a327e9b953 | ||
|
|
672e81572c | ||
|
|
deb1cf6d79 | ||
|
|
70f833957e | ||
|
|
8b5e30dad9 | ||
|
|
9fb9dd59ba | ||
|
|
67fc9ea81b | ||
|
|
4f834bd506 | ||
|
|
785c148768 | ||
|
|
cfda608bc2 | ||
|
|
f94e053767 | ||
|
|
6e8bcd9bda | ||
|
|
57c01b8e17 | ||
|
|
bf5536cb18 | ||
|
|
e33f6e4056 | ||
|
|
ffccbc6e9c | ||
|
|
caecd0bca2 | ||
|
|
1b3e826575 | ||
|
|
a6d380fb57 | ||
|
|
edf25196ee | ||
|
|
de8f344b89 | ||
|
|
8498b95ab5 | ||
|
|
8a547cacc0 | ||
|
|
1bdfa3e21a | ||
|
|
b5cd509a77 | ||
|
|
fb2cdd9b71 | ||
|
|
eaab017b26 | ||
|
|
74deb9351d | ||
|
|
cfa7f123de | ||
|
|
dd7fd4ea52 | ||
|
|
5229e0f93a | ||
|
|
dc466a69f4 | ||
|
|
61a1304210 | ||
|
|
5b6899419c | ||
|
|
9d1984a2bd | ||
|
|
99a87acdf2 | ||
|
|
fd8e717707 | ||
|
|
426947c0cb | ||
|
|
043c5e0de1 | ||
|
|
5104b34f2c | ||
|
|
66ecd2e65c | ||
|
|
7ad14ff6bf | ||
|
|
feffcc22cf | ||
|
|
ca0044b663 | ||
|
|
35299b18d3 | ||
|
|
8316233b3e | ||
|
|
2ad5d0a562 | ||
|
|
ec0b625ea5 | ||
|
|
a3c57b4fbd | ||
|
|
e24e89b272 | ||
|
|
6d3bc6044a | ||
|
|
ae2ec42c60 | ||
|
|
3288e476a0 | ||
|
|
3da6974ae4 | ||
|
|
ea7f9a8c0f | ||
|
|
1ea995f78d | ||
|
|
4782fa43a0 | ||
|
|
5ee8f117a1 | ||
|
|
f515ed15c5 | ||
|
|
39c73a89ff | ||
|
|
feebb5b698 | ||
|
|
db2e0d45e1 | ||
|
|
b145f0f000 | ||
|
|
0af2690e50 | ||
|
|
7438418d97 | ||
|
|
8ff104393e | ||
|
|
8aaa89df6b | ||
|
|
dd1ca486ec | ||
|
|
fd7f5bea38 | ||
|
|
facd8afea4 | ||
|
|
72fa7d38c3 |
No files matched your search
+29
-18
@@ -51,7 +51,11 @@ jobs:
|
||||
windres: rc
|
||||
shell: bash
|
||||
msvc-arch: x64_arm64
|
||||
- name: macos
|
||||
- name: macos-compat
|
||||
os: macos-12
|
||||
compiler: clang++
|
||||
shell: sh
|
||||
- name: macos-latest
|
||||
os: macos-latest
|
||||
compiler: clang++
|
||||
shell: sh
|
||||
@@ -86,7 +90,7 @@ jobs:
|
||||
keychain-name: ares-macos-keychain
|
||||
keychain-password: ${{ secrets.MACOS_KEYCHAIN_PASSWORD }}
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Windows Dependencies
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
@@ -107,23 +111,24 @@ jobs:
|
||||
fi
|
||||
- name: "macOS: recover MoltenVK cache"
|
||||
if: runner.os == 'macOS'
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
cache-name: moltenvk
|
||||
with:
|
||||
path: thirdparty/MoltenVK/MoltenVK
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('thirdparty/MoltenVK/HEAD') }}
|
||||
- name: "macOS: recover SDL cache"
|
||||
if: runner.os == 'macOS'
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
cache-name: sdl
|
||||
with:
|
||||
path: thirdparty/SDL/SDL
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('thirdparty/SDL/HEAD') }}
|
||||
- name: Install macOS Dependencies
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
if [ $ImageOS == "macos14" ]; then
|
||||
sudo xcode-select -s /Applications/Xcode_15.2.app/Contents/Developer
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.13
|
||||
echo "Set Xcode version to 15.2 for macos-latest build."
|
||||
elif [ $ImageOS == "macos12" ]; then
|
||||
sudo xcode-select -s /Applications/Xcode_13.4.app/Contents/Developer
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.9
|
||||
echo "Set Xcode version to 13.4 for macos-compat build."
|
||||
fi
|
||||
brew install make ninja cmake
|
||||
pip install --no-input setuptools
|
||||
rustup toolchain install nightly
|
||||
@@ -137,6 +142,7 @@ jobs:
|
||||
pushd thirdparty/MoltenVK
|
||||
./build-moltenvk.sh
|
||||
popd
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.15
|
||||
pushd thirdparty/librashader
|
||||
./build-librashader.sh
|
||||
popd
|
||||
@@ -178,7 +184,7 @@ jobs:
|
||||
MACOS_NOTARIZATION_PASSWORD: ${{ secrets.MACOS_NOTARIZATION_PASSWORD }}
|
||||
MACOS_NOTARIZATION_TEAMID: ${{ secrets.MACOS_NOTARIZATION_TEAMID }}
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.program }}-${{ matrix.platform.name }}
|
||||
path: desktop-ui/out/*
|
||||
@@ -190,11 +196,11 @@ jobs:
|
||||
needs:
|
||||
- build
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
path: 'src'
|
||||
- name: Download Artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: 'bin'
|
||||
- name: Package Artifacts
|
||||
@@ -210,9 +216,10 @@ jobs:
|
||||
bindir="${GITHUB_WORKSPACE}/bin"
|
||||
|
||||
# Hack: Workaround for GitHub artifacts losing attributes.
|
||||
chmod +x ${bindir}/ares-macos/ares.app/Contents/MacOS/ares
|
||||
chmod +x ${bindir}/ares-macos-latest/ares.app/Contents/MacOS/ares
|
||||
chmod +x ${bindir}/ares-macos-compat/ares.app/Contents/MacOS/ares
|
||||
|
||||
for package in windows windows-msvc-arm64 macos
|
||||
for package in windows windows-msvc-arm64 macos-latest macos-compat
|
||||
do
|
||||
mkdir "${package}"
|
||||
cd "${package}"
|
||||
@@ -312,10 +319,14 @@ jobs:
|
||||
uses: actions/upload-release-asset@v1
|
||||
env: { GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' }
|
||||
with: { upload_url: '${{ env.UPLOAD_URL }}', asset_path: 'ares-windows-msvc-arm64.zip', asset_name: 'ares-windows-msvc-arm64.zip', asset_content_type: 'application/zip' }
|
||||
- name: Upload ares-macos
|
||||
- name: Upload ares-macos-latest
|
||||
uses: actions/upload-release-asset@v1
|
||||
env: { GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' }
|
||||
with: { upload_url: '${{ env.UPLOAD_URL }}', asset_path: 'ares-macos.zip', asset_name: 'ares-macos.zip', asset_content_type: 'application/zip' }
|
||||
with: { upload_url: '${{ env.UPLOAD_URL }}', asset_path: 'ares-macos-latest.zip', asset_name: 'ares-macos-latest.zip', asset_content_type: 'application/zip' }
|
||||
- name: Upload ares-macos-compat
|
||||
uses: actions/upload-release-asset@v1
|
||||
env: { GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' }
|
||||
with: { upload_url: '${{ env.UPLOAD_URL }}', asset_path: 'ares-macos-compat.zip', asset_name: 'ares-macos-compat.zip', asset_content_type: 'application/zip' }
|
||||
# - name: Upload ares-ubuntu
|
||||
# uses: actions/upload-release-asset@v1
|
||||
# env: { GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' }
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
*.moc
|
||||
*.user
|
||||
*.xcuserdata
|
||||
.vs/
|
||||
.vscode/
|
||||
.idea/
|
||||
@@ -12,3 +13,6 @@ out-amd64/
|
||||
out-arm64/
|
||||
thirdparty/SDL/SDL
|
||||
thirdparty/SDL/libSDL2-2.0.0.dylib
|
||||
macos-xcode/
|
||||
.swiftpm
|
||||
*.xcodeproj
|
||||
@@ -28,7 +28,7 @@ g++ make pkg-config libgtk-3-dev libcanberra-gtk-module libgl-dev libasound2-dev
|
||||
```
|
||||
###### Additional Audio Drivers
|
||||
ares supports additional audio drivers besides the ALSA drivers included above. Installing these additional packages will allow them to be selected in Settings > Drivers:
|
||||
`libao-dev libopenal-dev'
|
||||
`libao-dev libopenal-dev`
|
||||
|
||||
###### GTK2 & GTK3
|
||||
By default, GTK3 is used, but support for GTK2 is available. You will need to install the additional package `libgtk2.0-dev` as well as specifying the command line option `hiro=gtk2` at compile time.
|
||||
@@ -40,6 +40,19 @@ You should then be able to select SDL for input in the Settings > Drivers menu.
|
||||
##### Building with clang
|
||||
|
||||
clang++ is now the preferred compiler for ares. If clang is detected on Windows/macOS/BSD, it will be selected by default. On Linux and other platforms, g++ remains the default if present. To build with clang, it is necessary to install both the `clang` and `lld` packages. If you would like to manually specify a compiler, you can use the following option: `compiler=[g++|clang++]`
|
||||
|
||||
##### Librashader Support
|
||||
If you do not want to include librashader support, you can pass the following option to the `make` command to skip these requirements using: `librashader=false`
|
||||
|
||||
Librashader requires rust in order to build. You can install it with the following command:
|
||||
```
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
source ~/.cargo/env
|
||||
rustup toolchain install nightly
|
||||
```
|
||||
|
||||
In order to build librashader, change into the `ares/thirdparty/librashader` directory and run the script `build-librashader.sh`
|
||||
Note that once the build completes, it will instruct you to run three copy commands to install the library on your system. These paths may be different depending on Linux distribution.
|
||||
|
||||
--------------
|
||||
|
||||
@@ -123,7 +136,7 @@ a26 fc sfc n64 sg ms md ps1 pce ng msx cv myvision gb gba ws ngp spec
|
||||
Build Output
|
||||
------------
|
||||
|
||||
There is a single binary produced at the end of compilation which can be found in `desktop-ui/out`. On OS's besides Linux, the `Database` & `Shader` directories are copied over here as well. On Linux, running `make install` after compilation will copy these directories and binary into suitable locations (see desktop-ui/GNUmakefile for details). Alternatively, these directories can be copied from `ares/Shaders/*` and `mia/Database/*`.
|
||||
There is a single binary produced at the end of compilation which can be found in `desktop-ui/out`. On OS's besides Linux, the `Database` & `Shader` directories are copied over here as well. On Linux, running `make install` after compilation will copy these directories and binary into suitable locations (see desktop-ui/GNUmakefile for details). Alternatively, these directories can be copied from `thirdparty/slang-shaders/*` into a `Shader` directory, and by copying `mia/Database/*`
|
||||
|
||||
|
||||
Command-line options
|
||||
|
||||
@@ -34,9 +34,6 @@ auto CPU::power(bool reset) -> void {
|
||||
MOS6502::power();
|
||||
Thread::create(system.frequency() / 3, {&CPU::main, this});
|
||||
|
||||
PC.byte(0) = readBus(0xfffc);
|
||||
PC.byte(1) = readBus(0xfffd);
|
||||
|
||||
io = {};
|
||||
}
|
||||
|
||||
|
||||
@@ -34,8 +34,6 @@ struct CPU : MOS6502, Thread {
|
||||
//timing.cpp
|
||||
auto read(n16 address) -> n8 override;
|
||||
auto write(n16 address, n8 data) -> void override;
|
||||
auto lastCycle() -> void override;
|
||||
auto nmi(n16& vector) -> void override;
|
||||
|
||||
auto rdyLine(bool) -> void;
|
||||
|
||||
@@ -43,6 +41,7 @@ struct CPU : MOS6502, Thread {
|
||||
struct IO {
|
||||
n1 rdyLine = 1;
|
||||
n32 scanlineCycles = 0;
|
||||
n8 openBus = 0;
|
||||
} io;
|
||||
};
|
||||
|
||||
|
||||
@@ -2,4 +2,5 @@ auto CPU::serialize(serializer& s) -> void {
|
||||
MOS6502::serialize(s);
|
||||
Thread::serialize(s);
|
||||
s(io.rdyLine);
|
||||
s(io.openBus);
|
||||
}
|
||||
+5
-12
@@ -6,24 +6,17 @@ auto CPU::read(n16 address) -> n8 {
|
||||
step(1);
|
||||
}
|
||||
|
||||
MDR = readBus(address);
|
||||
return MDR;
|
||||
io.openBus = readBus(address);
|
||||
return io.openBus;
|
||||
}
|
||||
|
||||
MDR = readBus(address);
|
||||
return MDR;
|
||||
io.openBus = readBus(address);
|
||||
return io.openBus;
|
||||
}
|
||||
|
||||
auto CPU::write(n16 address, n8 data) -> void {
|
||||
step(1);
|
||||
writeBus(address, MDR = data);
|
||||
}
|
||||
|
||||
auto CPU::lastCycle() -> void {
|
||||
}
|
||||
|
||||
auto CPU::nmi(n16& vector) -> void {
|
||||
|
||||
writeBus(address, io.openBus = data);
|
||||
}
|
||||
|
||||
auto CPU::rdyLine(bool line) -> void {
|
||||
|
||||
@@ -93,4 +93,4 @@ auto RIOT::readPortB() -> n8 {
|
||||
|
||||
auto RIOT::writePortB(n8 data) -> void {
|
||||
port[1].data = data;
|
||||
}
|
||||
}
|
||||
@@ -44,4 +44,4 @@ auto RIOT::power(bool reset) -> void {
|
||||
tvType = 1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -47,4 +47,4 @@ struct RIOT : Thread {
|
||||
n1 tvTypeLatch;
|
||||
};
|
||||
|
||||
extern RIOT riot;
|
||||
extern RIOT riot;
|
||||
@@ -20,4 +20,4 @@ auto RIOT::serialize(serializer& s) -> void {
|
||||
s(rightDifficultyLatch);
|
||||
s(tvType);
|
||||
s(tvTypeLatch);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
static const string SerializerVersion = "v132";
|
||||
static const string SerializerVersion = "v139";
|
||||
|
||||
auto System::serialize(bool synchronize) -> serializer {
|
||||
if(synchronize) scheduler.enter(Scheduler::Mode::Synchronize);
|
||||
|
||||
@@ -61,4 +61,4 @@ auto TIA::AudioChannel::phase1() -> u8 {
|
||||
}
|
||||
|
||||
return pulseCounter.bit(0) * volume;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
auto TIA::Ball::reset() -> void {
|
||||
counter = -4; // Resetting takes 4 cycles
|
||||
}
|
||||
|
||||
auto TIA::Ball::step(u8 clocks) -> void {
|
||||
if(!clocks) return;
|
||||
while(clocks--) {
|
||||
if(++counter == 160) {
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
const int ballSizes[4] = {1, 2, 4, 8};
|
||||
output = enable[delay] && counter < ballSizes[size];
|
||||
}
|
||||
}
|
||||
|
||||
+11
-9
@@ -106,10 +106,12 @@ auto TIA::cxclr() -> void {
|
||||
}
|
||||
|
||||
auto TIA::hmove() -> void {
|
||||
for(auto& p : player) p.position = (p.position - p.offset + 160) % 160;
|
||||
for(auto& m : missile) m.position = (m.position - m.offset + 160) % 160;
|
||||
ball.position = (ball.position - ball.offset + 160) % 160;
|
||||
io.hmoveTriggered = io.vcounter;
|
||||
u4 mask = 1 << 3;
|
||||
for(auto& p : player) p.step(p.offset ^ mask);
|
||||
for(auto& m : missile) m.step(m.offset ^ mask);
|
||||
ball.step(ball.offset ^ mask);
|
||||
|
||||
io.hmoveTriggered = 1;
|
||||
}
|
||||
|
||||
auto TIA::hmclr() -> void {
|
||||
@@ -125,17 +127,17 @@ auto TIA::grp(n1 index, n8 data) -> void {
|
||||
}
|
||||
|
||||
auto TIA::resp(n1 index) -> void {
|
||||
player[index].position = (io.hcounter - 68 + 5) % 160;
|
||||
player[index].reset();
|
||||
}
|
||||
|
||||
auto TIA::resm(n1 index) -> void {
|
||||
missile[index].position = (io.hcounter - 68 + 4) % 160;
|
||||
missile[index].reset();
|
||||
}
|
||||
|
||||
auto TIA::resmp(n1 index, n8 data) -> void {
|
||||
missile[index].reset = data.bit(1);
|
||||
missile[index].lockedToPlayer = data.bit(1);
|
||||
}
|
||||
|
||||
auto TIA::resbl() -> void {
|
||||
ball.position = (io.hcounter - 68 + 4) % 160;
|
||||
}
|
||||
ball.reset();
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
auto TIA::Missile::reset() -> void {
|
||||
counter = -4;
|
||||
}
|
||||
|
||||
auto TIA::Missile::start() -> void {
|
||||
startCounter = 4;
|
||||
starting = true;
|
||||
}
|
||||
|
||||
auto TIA::Missile::width() -> u8 {
|
||||
const int missileSizes[4] = {1, 2, 4, 8};
|
||||
return missileSizes[size];
|
||||
}
|
||||
|
||||
auto TIA::Missile::stepPixelCounter() -> void {
|
||||
if(--widthCounter == 0) {
|
||||
pixelCounter--;
|
||||
widthCounter = width();
|
||||
}
|
||||
}
|
||||
|
||||
auto TIA::Missile::step(u8 clocks) -> void {
|
||||
if(!clocks) return;
|
||||
while(clocks--) {
|
||||
counter++;
|
||||
|
||||
if(counter == 156) start();
|
||||
if(counter == 160) counter = 0;
|
||||
|
||||
if(starting && (startCounter-- == 0)) {
|
||||
starting = false;
|
||||
pixelCounter = 1;
|
||||
widthCounter = width();
|
||||
}
|
||||
|
||||
output = 0;
|
||||
if(pixelCounter) {
|
||||
stepPixelCounter();
|
||||
output = enable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
auto TIA::runMissile(n8 x, n1 index) -> n1 {
|
||||
auto& missile = this->missile[index];
|
||||
auto& player = this->player[index];
|
||||
auto position = missile.position;
|
||||
|
||||
if(missile.reset) {
|
||||
auto offset = 3;
|
||||
if(player.size == 5) offset = 6;
|
||||
if(player.size == 7) offset = 10;
|
||||
missile.position = player.position + offset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!missile.enable) return 0;
|
||||
|
||||
const int missileSizes[4] = {1, 2, 4, 8};
|
||||
|
||||
// Handle player stretch capability
|
||||
auto width = missileSizes[missile.size];
|
||||
auto repeatWidth = width;
|
||||
if(player.size == 5) repeatWidth *= 2;
|
||||
if(player.size == 7) repeatWidth *= 4;
|
||||
|
||||
// Handle repeat capability
|
||||
auto repeat = 1;
|
||||
if(player.size == 1 || player.size == 2 || player.size == 4) repeat = 2;
|
||||
if(player.size == 3 || player.size == 6) repeat = 3;
|
||||
|
||||
auto spacing = 8;
|
||||
if(player.size == 2 || player.size == 6) spacing = 24;
|
||||
if(player.size == 4 ) spacing = 56;
|
||||
|
||||
for(int i = 0; i < repeat; i++) {
|
||||
if (x >= position && x < position + width) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
position = (position + (spacing + repeatWidth)) % 160;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,57 @@
|
||||
auto TIA::Player::reset() -> void {
|
||||
counter = -4;
|
||||
}
|
||||
|
||||
auto TIA::Player::start(n1 copy) -> void {
|
||||
this->copy = copy;
|
||||
startCounter = 5;
|
||||
starting = true;
|
||||
}
|
||||
|
||||
auto TIA::Player::width() -> u8 {
|
||||
switch(size) {
|
||||
case 5: return 2;
|
||||
case 7: return 4;
|
||||
default: return 1;
|
||||
}
|
||||
}
|
||||
|
||||
auto TIA::Player::stepPixelCounter() -> void {
|
||||
if(--widthCounter == 0) {
|
||||
pixelCounter--;
|
||||
widthCounter = width();
|
||||
}
|
||||
|
||||
if(!copy && missile.lockedToPlayer && pixelCounter == 4) {
|
||||
missile.reset();
|
||||
}
|
||||
}
|
||||
|
||||
auto TIA::Player::step(u8 clocks) -> void {
|
||||
if(!clocks) return;
|
||||
while(clocks--) {
|
||||
counter++;
|
||||
|
||||
n1 first = size == 1 || size == 3;
|
||||
n1 second = size == 2 || size == 3 || size == 6;
|
||||
n1 third = size == 4 || size == 6;
|
||||
|
||||
if(first && counter == 12) start(true);
|
||||
if(second && counter == 28) start(true);
|
||||
if(third && counter == 60) start(true);
|
||||
if(counter == 156) start(false);
|
||||
if(counter == 160) counter = 0;
|
||||
|
||||
if(starting && (startCounter-- == 0)) {
|
||||
starting = false;
|
||||
pixelCounter = 8;
|
||||
widthCounter = width();
|
||||
}
|
||||
|
||||
output = 0;
|
||||
if(pixelCounter) {
|
||||
stepPixelCounter();
|
||||
output = graphics[delay].bit(reflect ? (7 - pixelCounter) : pixelCounter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
auto TIA::runPlayfield(n8 x) -> n1 {
|
||||
|
||||
if((x % 4) == 0) {
|
||||
auto pos = x >> 2;
|
||||
playfield.pixel = (!playfield.mirror || pos < 20) ? playfield.graphics.bit(pos % 20)
|
||||
: playfield.graphics.bit(19 - (pos % 20));
|
||||
}
|
||||
|
||||
return playfield.pixel;
|
||||
}
|
||||
@@ -30,25 +30,36 @@ auto TIA::serialize(serializer& s) -> void {
|
||||
s(player[n].reflect);
|
||||
s(player[n].size);
|
||||
s(player[n].offset);
|
||||
s(player[n].position);
|
||||
s(player[n].delay);
|
||||
s(player[n].counter);
|
||||
s(player[n].startCounter);
|
||||
s(player[n].pixelCounter);
|
||||
s(player[n].widthCounter);
|
||||
s(player[n].starting);
|
||||
s(player[n].output);
|
||||
s(player[n].copy);
|
||||
}
|
||||
|
||||
for(auto n : range(2)) {
|
||||
s(missile[n].enable);
|
||||
s(missile[n].reset);
|
||||
s(missile[n].lockedToPlayer);
|
||||
s(missile[n].size);
|
||||
s(missile[n].offset);
|
||||
s(missile[n].position);
|
||||
s(missile[n].counter);
|
||||
s(missile[n].startCounter);
|
||||
s(missile[n].pixelCounter);
|
||||
s(missile[n].widthCounter);
|
||||
s(missile[n].starting);
|
||||
s(missile[n].output);
|
||||
}
|
||||
|
||||
for(auto n : range(2)) {
|
||||
s(ball.enable[0]);
|
||||
s(ball.enable[1]);
|
||||
s(ball.size);
|
||||
s(ball.offset);
|
||||
s(ball.position);
|
||||
}
|
||||
s(ball.enable[0]);
|
||||
s(ball.enable[1]);
|
||||
s(ball.delay);
|
||||
s(ball.size);
|
||||
s(ball.offset);
|
||||
s(ball.counter);
|
||||
s(ball.output);
|
||||
|
||||
s(collision.M0P0);
|
||||
s(collision.M0P1);
|
||||
@@ -82,4 +93,4 @@ auto TIA::serialize(serializer& s) -> void {
|
||||
s(audio[n].control);
|
||||
s(audio[n].frequency);
|
||||
}
|
||||
}
|
||||
}
|
||||
+69
-136
@@ -7,6 +7,10 @@ TIA tia;
|
||||
#include "audio.cpp"
|
||||
#include "color.cpp"
|
||||
#include "io.cpp"
|
||||
#include "playfield.cpp"
|
||||
#include "player.cpp"
|
||||
#include "missile.cpp"
|
||||
#include "ball.cpp"
|
||||
#include "serialization.cpp"
|
||||
#include "write-queue.cpp"
|
||||
|
||||
@@ -37,7 +41,7 @@ auto TIA::unload() -> void {
|
||||
auto TIA::main() -> void {
|
||||
scanline();
|
||||
io.vcounter++;
|
||||
io.hmoveTriggered = 255;
|
||||
io.hmoveTriggered = 0;
|
||||
}
|
||||
|
||||
auto TIA::scanline() -> void {
|
||||
@@ -47,7 +51,54 @@ auto TIA::scanline() -> void {
|
||||
writeQueue.step();
|
||||
auto x = io.hcounter - 68;
|
||||
auto y = io.vcounter - voffset();
|
||||
if(x >= 0 && y > 0 && y < vlines()) pixel(x);
|
||||
bool hblank = (io.hmoveTriggered && io.hcounter < 76) || io.hcounter < 68;
|
||||
|
||||
// Playfield ignores hmove and always begins at cycle 68
|
||||
//if(io.hcounter >= 68) playfield.step();
|
||||
|
||||
// Objects are stepped only during active display
|
||||
if(!hblank) {
|
||||
ball.step();
|
||||
for(auto& player : this->player) player.step();
|
||||
for(auto& missile : this->missile) missile.step();
|
||||
}
|
||||
|
||||
n8 pixel = io.bgColor;
|
||||
auto pf = runPlayfield(x);
|
||||
auto bl = ball.output;
|
||||
auto p0 = player[0].output;
|
||||
auto p1 = player[1].output;
|
||||
auto m0 = missile[0].output;
|
||||
auto m1 = missile[1].output;
|
||||
|
||||
if(!playfield.priority && (pf || bl)) pixel = io.fgColor;
|
||||
if(p1 || m1) pixel = io.p1Color;
|
||||
if(p0 || m0) pixel = io.p0Color;
|
||||
if(playfield.priority && (pf || bl)) pixel = io.fgColor;
|
||||
|
||||
// Update Collision
|
||||
if(m0 && p1) collision.M0P1 = 1;
|
||||
if(m0 && p0) collision.M0P0 = 1;
|
||||
if(m1 && p0) collision.M1P0 = 1;
|
||||
if(m1 && p1) collision.M1P1 = 1;
|
||||
if(p0 && pf) collision.P0PF = 1;
|
||||
if(p0 && bl) collision.P0BL = 1;
|
||||
if(p1 && pf) collision.P1PF = 1;
|
||||
if(p1 && bl) collision.P1BL = 1;
|
||||
if(m0 && pf) collision.M0PF = 1;
|
||||
if(m0 && bl) collision.M0BL = 1;
|
||||
if(m1 && pf) collision.M1PF = 1;
|
||||
if(m1 && bl) collision.M1BL = 1;
|
||||
if(bl && pf) collision.BLPF = 1;
|
||||
if(p0 && p1) collision.P0P1 = 1;
|
||||
if(m0 && m1) collision.M0M1 = 1;
|
||||
|
||||
if(x >= 0 && y > 0 && y < displayHeight()) {
|
||||
if(io.vblank || hblank) pixel = 0;
|
||||
auto output = screen->pixels().data() + (y * 180) + 10;
|
||||
output[x] = pixel;
|
||||
}
|
||||
|
||||
runAudio();
|
||||
step();
|
||||
if(io.hcounter == 0) cpu.io.rdyLine = 1;
|
||||
@@ -60,139 +111,8 @@ auto TIA::scanline() -> void {
|
||||
}
|
||||
}
|
||||
|
||||
auto TIA::pixel(n8 x) -> void {
|
||||
auto y = io.vcounter - voffset();
|
||||
auto output = screen->pixels().data() + (y * 180) + 10;
|
||||
if(y >= displayHeight()) return;
|
||||
|
||||
// Output only black during vblank, or for the first 8px of a scanline where hmove was triggered
|
||||
if (io.vblank || (io.hmoveTriggered == io.vcounter && x < 8)) {
|
||||
output[x] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
output[x] = io.bgColor;
|
||||
auto pf = runPlayfield(x);
|
||||
auto bl = runBall(x);
|
||||
auto p0 = runPlayer(x, 0);
|
||||
auto p1 = runPlayer(x, 1);
|
||||
auto m0 = runMissile(x, 0);
|
||||
auto m1 = runMissile(x, 1);
|
||||
|
||||
if(!playfield.priority && (pf || bl)) output[x] = io.fgColor;
|
||||
if(p1 || m1) output[x] = io.p1Color;
|
||||
if(p0 || m0) output[x] = io.p0Color;
|
||||
if(playfield.priority && (pf || bl)) output[x] = io.fgColor;
|
||||
|
||||
// Update Collision
|
||||
if(m0 && p1) collision.M0P1 = 1;
|
||||
if(m0 && p0) collision.M0P0 = 1;
|
||||
if(m1 && p0) collision.M1P0 = 1;
|
||||
if(m1 && p1) collision.M1P1 = 1;
|
||||
if(p0 && pf) collision.P0PF = 1;
|
||||
if(p0 && bl) collision.P0BL = 1;
|
||||
if(p1 && pf) collision.P1PF = 1;
|
||||
if(p1 && bl) collision.P1BL = 1;
|
||||
if(m0 && pf) collision.M0PF = 1;
|
||||
if(m0 && bl) collision.M0BL = 1;
|
||||
if(m1 && pf) collision.M1PF = 1;
|
||||
if(m1 && bl) collision.M1BL = 1;
|
||||
if(bl && pf) collision.BLPF = 1;
|
||||
if(p0 && p1) collision.P0P1 = 1;
|
||||
if(m0 && m1) collision.M0M1 = 1;
|
||||
}
|
||||
|
||||
auto TIA::runPlayfield(n8 x) -> n1 {
|
||||
if((x % 4) == 0) {
|
||||
auto pos = x >> 2;
|
||||
playfield.pixel = (!playfield.mirror || pos < 20) ? playfield.graphics.bit(pos % 20)
|
||||
: playfield.graphics.bit(19 - (pos % 20));
|
||||
}
|
||||
|
||||
return playfield.pixel;
|
||||
}
|
||||
|
||||
auto TIA::runPlayer(n8 x, n1 index) -> n1 {
|
||||
auto& player = this->player[index];
|
||||
auto position = player.position;
|
||||
|
||||
// Handle player stretch capability
|
||||
auto width = 8;
|
||||
if(player.size == 5) width = 16;
|
||||
if(player.size == 7) width = 32;
|
||||
|
||||
// Handle repeat capability
|
||||
auto repeat = 1;
|
||||
if(player.size == 1 || player.size == 2 || player.size == 4) repeat = 2;
|
||||
if(player.size == 3 || player.size == 6) repeat = 3;
|
||||
|
||||
auto spacing = 8;
|
||||
if(player.size == 2 || player.size == 6) spacing = 24;
|
||||
if(player.size == 4 ) spacing = 56;
|
||||
|
||||
for(int i = 0; i < repeat; i++) {
|
||||
if (x >= position && x < position + width) {
|
||||
auto bit = player.reflect ? (x - position) / (width / 8) : 7 - ((x - position) / (width / 8));
|
||||
return player.graphics[player.delay].bit(bit);
|
||||
}
|
||||
|
||||
position = (position + (spacing + width)) % 160;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto TIA::runMissile(n8 x, n1 index) -> n1 {
|
||||
auto& missile = this->missile[index];
|
||||
auto& player = this->player[index];
|
||||
auto position = missile.position;
|
||||
|
||||
if(missile.reset) {
|
||||
auto offset = 3;
|
||||
if(player.size == 5) offset = 6;
|
||||
if(player.size == 7) offset = 10;
|
||||
missile.position = player.position + offset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!missile.enable) return 0;
|
||||
|
||||
const int missileSizes[4] = {1, 2, 4, 8};
|
||||
|
||||
// Handle player stretch capability
|
||||
auto width = missileSizes[missile.size];
|
||||
auto repeatWidth = width;
|
||||
if(player.size == 5) repeatWidth *= 2;
|
||||
if(player.size == 7) repeatWidth *= 4;
|
||||
|
||||
// Handle repeat capability
|
||||
auto repeat = 1;
|
||||
if(player.size == 1 || player.size == 2 || player.size == 4) repeat = 2;
|
||||
if(player.size == 3 || player.size == 6) repeat = 3;
|
||||
|
||||
auto spacing = 8;
|
||||
if(player.size == 2 || player.size == 6) spacing = 24;
|
||||
if(player.size == 4 ) spacing = 56;
|
||||
|
||||
for(int i = 0; i < repeat; i++) {
|
||||
if (x >= position && x < position + width) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
position = (position + (spacing + repeatWidth)) % 160;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto TIA::runBall(n8 x) -> n1 {
|
||||
if(!ball.enable[ball.delay]) return 0;
|
||||
|
||||
const int ballSizes[4] = {1, 2, 4, 8};
|
||||
return x >= ball.position && x < ball.position + ballSizes[ball.size];
|
||||
}
|
||||
|
||||
auto TIA::frame() -> void {
|
||||
screen->refreshRateHint(system.frequency(), 228, io.vcounter);
|
||||
screen->frame();
|
||||
scheduler.exit(Event::Frame);
|
||||
}
|
||||
@@ -207,7 +127,20 @@ auto TIA::power(bool reset) -> void {
|
||||
screen->power();
|
||||
io = {};
|
||||
playfield = {};
|
||||
for(auto& p : player) p = {};
|
||||
for(auto& p : player) {
|
||||
p.graphics[0] = p.graphics[1] = 0;
|
||||
p.reflect = 0;
|
||||
p.size = 0;
|
||||
p.offset = 0;
|
||||
p.delay = 0;
|
||||
p.counter = 0;
|
||||
p.startCounter = 0;
|
||||
p.pixelCounter = 0;
|
||||
p.widthCounter = 0;
|
||||
p.starting = 0;
|
||||
p.output = 0;
|
||||
p.copy = 0;
|
||||
};
|
||||
for(auto& m : missile) m = {};
|
||||
ball = {};
|
||||
collision = {};
|
||||
@@ -219,4 +152,4 @@ auto TIA::power(bool reset) -> void {
|
||||
volume[15] = 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+42
-17
@@ -19,7 +19,6 @@ struct TIA : Thread {
|
||||
auto pixel(n8 x) -> void;
|
||||
|
||||
auto runPlayfield(n8 x) -> n1;
|
||||
auto runBall(n8 x) -> n1;
|
||||
auto runPlayer(n8 x, n1 index) -> n1;
|
||||
auto runMissile(n8 x, n1 index) -> n1;
|
||||
auto runCollision() -> void;
|
||||
@@ -67,7 +66,7 @@ struct TIA : Thread {
|
||||
struct {
|
||||
u9 vcounter;
|
||||
u8 hcounter;
|
||||
u8 hmoveTriggered;
|
||||
n1 hmoveTriggered;
|
||||
|
||||
n1 vsync;
|
||||
n1 vblank;
|
||||
@@ -86,29 +85,55 @@ struct TIA : Thread {
|
||||
n1 priority;
|
||||
} playfield;
|
||||
|
||||
struct {
|
||||
struct Missile {
|
||||
auto step(u8 cycles = 1) -> void;
|
||||
auto start() -> void;
|
||||
auto reset() -> void;
|
||||
auto width() -> u8;
|
||||
auto stepPixelCounter() -> void;
|
||||
n1 enable;
|
||||
n1 lockedToPlayer;
|
||||
n2 size;
|
||||
n4 offset;
|
||||
i9 counter;
|
||||
u8 startCounter;
|
||||
u8 pixelCounter;
|
||||
u8 widthCounter;
|
||||
n1 starting;
|
||||
n1 output;
|
||||
} missile[2];
|
||||
|
||||
struct Player {
|
||||
Missile& missile;
|
||||
auto step(u8 cycles = 1) -> void;
|
||||
auto start(n1 copy) -> void;
|
||||
auto reset() -> void;
|
||||
auto width() -> u8;
|
||||
auto stepPixelCounter() -> void;
|
||||
n8 graphics[2];
|
||||
n1 reflect;
|
||||
n3 size;
|
||||
i4 offset;
|
||||
n8 position;
|
||||
n4 offset;
|
||||
n1 delay;
|
||||
} player[2];
|
||||
i9 counter;
|
||||
u8 startCounter;
|
||||
u8 pixelCounter;
|
||||
u8 widthCounter;
|
||||
n1 starting;
|
||||
n1 output;
|
||||
n1 copy;
|
||||
} player[2] {{missile[0]}, {missile[1]}};
|
||||
|
||||
struct {
|
||||
n1 enable;
|
||||
n1 reset;
|
||||
n2 size;
|
||||
i4 offset;
|
||||
n8 position;
|
||||
} missile[2];
|
||||
struct Ball {
|
||||
auto step(u8 cycles = 1) -> void;
|
||||
auto reset() -> void;
|
||||
|
||||
struct {
|
||||
n1 enable[2];
|
||||
n2 size;
|
||||
i4 offset;
|
||||
n4 offset;
|
||||
n1 delay;
|
||||
n8 position;
|
||||
i9 counter;
|
||||
n1 output;
|
||||
} ball;
|
||||
|
||||
struct {
|
||||
@@ -147,4 +172,4 @@ struct TIA : Thread {
|
||||
f64 volume[16];
|
||||
};
|
||||
|
||||
extern TIA tia;
|
||||
extern TIA tia;
|
||||
@@ -43,4 +43,4 @@ auto TIA::WriteQueue::QueueItem::commit() -> void {
|
||||
case 0x2b: tia.hmclr(); return; // HMCLR
|
||||
}
|
||||
debug(unimplemented, "[TIA] writeQueue: ",hex(address), " = ", hex(data));
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -10,7 +10,7 @@ Platform* platform = nullptr;
|
||||
bool _runAhead = false;
|
||||
|
||||
const string Name = "ares";
|
||||
const string Version = "137";
|
||||
const string Version = "141";
|
||||
const string Copyright = "ares team, Near";
|
||||
const string License = "ISC";
|
||||
const string LicenseURI = "https://opensource.org/licenses/ISC";
|
||||
|
||||
@@ -25,11 +25,15 @@ Screen::~Screen() {
|
||||
|
||||
auto Screen::main(uintptr_t) -> void {
|
||||
while(!_kill) {
|
||||
usleep(1);
|
||||
if(_frame) {
|
||||
unique_lock<mutex> lock(_frameMutex);
|
||||
|
||||
auto timeout = std::chrono::milliseconds(10);
|
||||
if(_frameCondition.wait_for(lock, timeout, [&] { return _frame.load(); })) {
|
||||
refresh();
|
||||
_frame = false;
|
||||
}
|
||||
|
||||
if(_kill) break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +72,15 @@ auto Screen::setRefresh(function<void ()> refresh) -> void {
|
||||
_refresh = refresh;
|
||||
}
|
||||
|
||||
auto Screen::refreshRateHint(double pixelFrequency, int dotsPerLine, int linesPerFrame) -> void {
|
||||
refreshRateHint(1.0f / ((double)(dotsPerLine * linesPerFrame) / pixelFrequency));
|
||||
}
|
||||
|
||||
auto Screen::refreshRateHint(double refreshRate) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
platform->refreshRateHint(refreshRate);
|
||||
}
|
||||
|
||||
auto Screen::setViewport(u32 x, u32 y, u32 width, u32 height) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_viewportX = x;
|
||||
@@ -181,10 +194,12 @@ auto Screen::frame() -> void {
|
||||
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_inputA.swap(_inputB);
|
||||
_frame = true;
|
||||
if constexpr(!ares::Video::Threaded) {
|
||||
refresh();
|
||||
_frame = false;
|
||||
} else {
|
||||
_frame = true;
|
||||
_frameCondition.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@ struct Screen : Video {
|
||||
|
||||
auto setRefresh(function<void ()> refresh) -> void;
|
||||
auto setViewport(u32 x, u32 y, u32 width, u32 height) -> void;
|
||||
auto refreshRateHint(double refreshRate) -> void;
|
||||
auto refreshRateHint(double pixelFrequency, int dotsPerLine, int linesPerFrame) -> void;
|
||||
|
||||
auto setSize(u32 width, u32 height) -> void;
|
||||
auto setScale(f64 scaleX, f64 scaleY) -> void;
|
||||
@@ -97,6 +99,8 @@ protected:
|
||||
//unserialized:
|
||||
nall::thread _thread;
|
||||
recursive_mutex _mutex;
|
||||
mutex _frameMutex;
|
||||
condition_variable _frameCondition;
|
||||
atomic<bool> _kill = false;
|
||||
atomic<bool> _frame = false;
|
||||
function<void ()> _refresh;
|
||||
|
||||
@@ -18,6 +18,7 @@ struct Platform {
|
||||
virtual auto log(Node::Debugger::Tracer::Tracer, string_view message) -> void {}
|
||||
virtual auto status(string_view message) -> void {}
|
||||
virtual auto video(Node::Video::Screen, const u32* data, u32 pitch, u32 width, u32 height) -> void {}
|
||||
virtual auto refreshRateHint(double refreshRate) -> void {}
|
||||
virtual auto audio(Node::Audio::Stream) -> void {}
|
||||
virtual auto input(Node::Input::Input) -> void {}
|
||||
virtual auto cheat(u32 addr) -> maybe<u32> { return nothing; }
|
||||
|
||||
@@ -165,6 +165,8 @@ auto M93LCx6::ShiftRegister::read() -> n1 {
|
||||
}
|
||||
|
||||
auto M93LCx6::ShiftRegister::write(n1 bit) -> void {
|
||||
// wait for start bit
|
||||
if(!bit && !count) return;
|
||||
value <<= 1;
|
||||
value.bit(0) = bit;
|
||||
count++;
|
||||
|
||||
@@ -69,4 +69,4 @@ namespace ares {
|
||||
s(state.portB);
|
||||
s(state.portC);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,4 +32,5 @@ namespace ares {
|
||||
n8 portC;
|
||||
} state;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ struct ARM7TDMI {
|
||||
virtual auto step(u32 clocks) -> void = 0;
|
||||
virtual auto sleep() -> void = 0;
|
||||
virtual auto get(u32 mode, n32 address) -> n32 = 0;
|
||||
virtual auto getDebugger(u32 mode, n32 address) -> n32 { return get(mode, address); }
|
||||
virtual auto set(u32 mode, n32 address, n32 word) -> void = 0;
|
||||
|
||||
//arm7tdmi.cpp
|
||||
|
||||
@@ -17,12 +17,12 @@ auto ARM7TDMI::disassembleInstruction(maybe<n32> pc, maybe<boolean> thumb) -> st
|
||||
|
||||
_pc = pc();
|
||||
if(!thumb()) {
|
||||
n32 opcode = read(Word | Nonsequential, _pc & ~3);
|
||||
n32 opcode = getDebugger(Word | Nonsequential, _pc & ~3);
|
||||
n12 index = (opcode & 0x0ff00000) >> 16 | (opcode & 0x000000f0) >> 4;
|
||||
_c = _conditions[opcode >> 28];
|
||||
return pad(armDisassemble[index](opcode), -40);
|
||||
} else {
|
||||
n16 opcode = read(Half | Nonsequential, _pc & ~1);
|
||||
n16 opcode = getDebugger(Half | Nonsequential, _pc & ~1);
|
||||
return pad(thumbDisassemble[opcode](), -40);
|
||||
}
|
||||
}
|
||||
@@ -121,7 +121,7 @@ auto ARM7TDMI::armDisassembleDataRegisterShift
|
||||
auto ARM7TDMI::armDisassembleLoadImmediate
|
||||
(n8 immediate, n1 half, n4 d, n4 n, n1 writeback, n1 up, n1 pre) -> string {
|
||||
string data;
|
||||
if(n == 15) data = {" =0x", hex(read((half ? Half : Byte) | Nonsequential,
|
||||
if(n == 15) data = {" =0x", hex(getDebugger((half ? Half : Byte) | Nonsequential,
|
||||
_pc + 8 + (up ? +immediate : -immediate)), half ? 4L : 2L)};
|
||||
|
||||
return {"ldr", _c, half ? "sh" : "sb", " ",
|
||||
@@ -150,7 +150,7 @@ auto ARM7TDMI::armDisassembleMemorySwap
|
||||
auto ARM7TDMI::armDisassembleMoveHalfImmediate
|
||||
(n8 immediate, n4 d, n4 n, n1 mode, n1 writeback, n1 up, n1 pre) -> string {
|
||||
string data;
|
||||
if(n == 15) data = {" =0x", hex(read(Half | Nonsequential, _pc + (up ? +immediate : -immediate)), 4L)};
|
||||
if(n == 15) data = {" =0x", hex(getDebugger(Half | Nonsequential, _pc + (up ? +immediate : -immediate)), 4L)};
|
||||
|
||||
return {mode ? "ldr" : "str", _c, "h ",
|
||||
_r[d], ",[", _r[n],
|
||||
@@ -173,7 +173,7 @@ auto ARM7TDMI::armDisassembleMoveHalfRegister
|
||||
auto ARM7TDMI::armDisassembleMoveImmediateOffset
|
||||
(n12 immediate, n4 d, n4 n, n1 mode, n1 writeback, n1 byte, n1 up, n1 pre) -> string {
|
||||
string data;
|
||||
if(n == 15) data = {" =0x", hex(read((byte ? Byte : Word) | Nonsequential,
|
||||
if(n == 15) data = {" =0x", hex(getDebugger((byte ? Byte : Word) | Nonsequential,
|
||||
_pc + 8 + (up ? +immediate : -immediate)), byte ? 2L : 4L)};
|
||||
return {mode ? "ldr" : "str", _c, byte ? "b" : "", " ", _r[d], ",[", _r[n],
|
||||
pre == 0 ? "]" : "",
|
||||
@@ -308,7 +308,7 @@ auto ARM7TDMI::thumbDisassembleBranchExchange
|
||||
|
||||
auto ARM7TDMI::thumbDisassembleBranchFarPrefix
|
||||
(i11 displacementHi) -> string {
|
||||
n11 displacementLo = read(Half | Nonsequential, (_pc & ~1) + 2);
|
||||
n11 displacementLo = getDebugger(Half | Nonsequential, (_pc & ~1) + 2);
|
||||
i22 displacement = displacementHi << 11 | displacementLo << 0;
|
||||
n32 address = _pc + 4 + displacement * 2;
|
||||
return {"bl 0x", hex(address, 8L)};
|
||||
@@ -340,7 +340,7 @@ auto ARM7TDMI::thumbDisassembleImmediate
|
||||
auto ARM7TDMI::thumbDisassembleLoadLiteral
|
||||
(n8 displacement, n3 d) -> string {
|
||||
n32 address = ((_pc + 4) & ~3) + (displacement << 2);
|
||||
n32 data = read(Word | Nonsequential, address);
|
||||
n32 data = getDebugger(Word | Nonsequential, address);
|
||||
return {"ldr ", _r[d], ",[pc,#0x", hex(address, 8L), "] =0x", hex(data, 8L)};
|
||||
}
|
||||
|
||||
|
||||
@@ -182,14 +182,15 @@ auto ARM7TDMI::armInstructionMoveImmediateOffset
|
||||
auto ARM7TDMI::armInstructionMoveMultiple
|
||||
(n16 list, n4 n, n1 mode, n1 writeback, n1 type, n1 up, n1 pre) -> void {
|
||||
n32 rn = r(n);
|
||||
n32 bitCount = list ? bit::count(list) : 16;
|
||||
if(pre == 0 && up == 1) rn = rn + 0; //IA
|
||||
if(pre == 1 && up == 1) rn = rn + 4; //IB
|
||||
if(pre == 1 && up == 0) rn = rn - bit::count(list) * 4 + 0; //DB
|
||||
if(pre == 0 && up == 0) rn = rn - bit::count(list) * 4 + 4; //DA
|
||||
if(pre == 1 && up == 0) rn = rn - bitCount * 4 + 0; //DB
|
||||
if(pre == 0 && up == 0) rn = rn - bitCount * 4 + 4; //DA
|
||||
|
||||
if(writeback && mode == 1) {
|
||||
if(up == 1) r(n) = r(n) + bit::count(list) * 4; //IA,IB
|
||||
if(up == 0) r(n) = r(n) - bit::count(list) * 4; //DA,DB
|
||||
if(writeback && mode == 1 && !list.bit(n)) {
|
||||
if(up == 1) r(n) = r(n) + bitCount * 4; //IA,IB
|
||||
if(up == 0) r(n) = r(n) - bitCount * 4; //DA,DB
|
||||
}
|
||||
|
||||
auto cpsrMode = cpsr().m;
|
||||
@@ -199,12 +200,17 @@ auto ARM7TDMI::armInstructionMoveMultiple
|
||||
if(usr) cpsr().m = PSR::USR;
|
||||
|
||||
u32 sequential = Nonsequential;
|
||||
for(u32 m : range(16)) {
|
||||
if(!list.bit(m)) continue;
|
||||
if(mode == 1) r(m) = read(Word | sequential, rn);
|
||||
if(mode == 0) write(Word | sequential, rn, r(m) + (m == 15 ? 4 : 0));
|
||||
rn += 4;
|
||||
sequential = Sequential;
|
||||
if(!list) {
|
||||
if(mode == 1) r(15) = read(Word | sequential, rn);
|
||||
if(mode == 0) write(Word | sequential, rn, r(15) + 4);
|
||||
} else {
|
||||
for(u32 m : range(16)) {
|
||||
if(!list.bit(m)) continue;
|
||||
if(mode == 1) r(m) = read(Word | sequential, rn);
|
||||
if(mode == 0) write(Word | sequential, rn, r(m) + (m == 15 ? 4 : 0));
|
||||
rn += 4;
|
||||
sequential = Sequential;
|
||||
}
|
||||
}
|
||||
|
||||
if(usr) cpsr().m = cpsrMode;
|
||||
@@ -219,8 +225,8 @@ auto ARM7TDMI::armInstructionMoveMultiple
|
||||
}
|
||||
|
||||
if(writeback && mode == 0) {
|
||||
if(up == 1) r(n) = r(n) + bit::count(list) * 4; //IA,IB
|
||||
if(up == 0) r(n) = r(n) - bit::count(list) * 4; //DA,DB
|
||||
if(up == 1) r(n) = r(n) + bitCount * 4; //IA,IB
|
||||
if(up == 0) r(n) = r(n) - bitCount * 4; //DA,DB
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,8 +255,7 @@ auto ARM7TDMI::armInstructionMoveRegisterOffset
|
||||
|
||||
auto ARM7TDMI::armInstructionMoveToRegisterFromStatus
|
||||
(n4 d, n1 mode) -> void {
|
||||
if(mode && (cpsr().m == PSR::USR || cpsr().m == PSR::SYS)) return;
|
||||
r(d) = mode ? spsr() : cpsr();
|
||||
r(d) = (mode && cpsr().m != PSR::USR && cpsr().m != PSR::SYS) ? spsr() : cpsr();
|
||||
}
|
||||
|
||||
auto ARM7TDMI::armInstructionMoveToStatusFromImmediate
|
||||
|
||||
@@ -125,23 +125,29 @@ auto ARM7TDMI::thumbInstructionMoveHalfImmediate
|
||||
auto ARM7TDMI::thumbInstructionMoveMultiple
|
||||
(n8 list, n3 n, n1 mode) -> void {
|
||||
n32 rn = r(n);
|
||||
n32 bitCount = list ? bit::count(list) : 16;
|
||||
|
||||
if(mode == 1 && !list.bit(n)) r(n) = r(n) + bitCount * 4;
|
||||
|
||||
u32 sequential = Nonsequential;
|
||||
for(u32 m : range(8)) {
|
||||
if(!list.bit(m)) continue;
|
||||
switch(mode) {
|
||||
case 0: write(Word | sequential, rn, r(m)); break; //STMIA
|
||||
case 1: r(m) = read(Word | sequential, rn); break; //LDMIA
|
||||
if(!list) {
|
||||
if(mode == 1) r(15) = read(Word | sequential, rn);
|
||||
if(mode == 0) write(Word | sequential, rn, r(15) + 2);
|
||||
} else {
|
||||
for(u32 m : range(8)) {
|
||||
if(!list.bit(m)) continue;
|
||||
if(mode == 1) r(m) = read(Word | sequential, rn); //LDMIA
|
||||
if(mode == 0) write(Word | sequential, rn, r(m)); //STMIA
|
||||
rn += 4;
|
||||
sequential = Sequential;
|
||||
}
|
||||
rn += 4;
|
||||
sequential = Sequential;
|
||||
}
|
||||
|
||||
if(mode == 0 || !list.bit(n)) r(n) = rn;
|
||||
if(mode == 1) {
|
||||
if(mode) {
|
||||
idle();
|
||||
} else {
|
||||
pipeline.nonsequential = true;
|
||||
r(n) = r(n) + bitCount * 4;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,37 +197,35 @@ auto ARM7TDMI::thumbInstructionSoftwareInterrupt
|
||||
|
||||
auto ARM7TDMI::thumbInstructionStackMultiple
|
||||
(n8 list, n1 lrpc, n1 mode) -> void {
|
||||
n32 sp;
|
||||
switch(mode) {
|
||||
case 0: sp = r(13) - (bit::count(list) + lrpc) * 4; break; //PUSH
|
||||
case 1: sp = r(13); //POP
|
||||
}
|
||||
n32 sp = r(13);
|
||||
n32 bitCount = (list | lrpc) ? (bit::count(list) + lrpc) : 16;
|
||||
if(mode == 1) r(13) = r(13) + bitCount * 4; //POP
|
||||
if(mode == 0) sp = sp - bitCount * 4 + 0; //PUSH
|
||||
|
||||
u32 sequential = Nonsequential;
|
||||
for(u32 m : range(8)) {
|
||||
if(!list.bit(m)) continue;
|
||||
switch(mode) {
|
||||
case 0: write(Word | sequential, sp, r(m)); break; //PUSH
|
||||
case 1: r(m) = read(Word | sequential, sp); break; //POP
|
||||
if(bitCount == 16) {
|
||||
if(mode == 1) r(15) = read(Word | sequential, sp); //POP
|
||||
if(mode == 0) write(Word | sequential, sp, r(15) + 2); //PUSH
|
||||
} else {
|
||||
for(u32 m : range(8)) {
|
||||
if(!list.bit(m)) continue;
|
||||
if(mode == 1) r(m) = read(Word | sequential, sp); //POP
|
||||
if(mode == 0) write(Word | sequential, sp, r(m)); //PUSH
|
||||
sp += 4;
|
||||
sequential = Sequential;
|
||||
}
|
||||
sp += 4;
|
||||
sequential = Sequential;
|
||||
}
|
||||
|
||||
if(lrpc) {
|
||||
switch(mode) {
|
||||
case 0: write(Word | sequential, sp, r(14)); break; //PUSH
|
||||
case 1: r(15) = read(Word | sequential, sp); break; //POP
|
||||
}
|
||||
if(mode == 1) r(15) = read(Word | sequential, sp); //POP
|
||||
if(mode == 0) write(Word | sequential, sp, r(14)); //PUSH
|
||||
sp += 4;
|
||||
}
|
||||
|
||||
if(mode == 1) {
|
||||
if(mode) {
|
||||
idle();
|
||||
r(13) = r(13) + (bit::count(list) + lrpc) * 4; //POP
|
||||
} else {
|
||||
pipeline.nonsequential = true;
|
||||
r(13) = r(13) - (bit::count(list) + lrpc) * 4; //PUSH
|
||||
r(13) = r(13) - bitCount * 4; //PUSH
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -75,8 +75,8 @@ struct GSU {
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
//disassembler.cpp
|
||||
noinline auto disassembleInstruction() -> string;
|
||||
noinline auto disassembleContext() -> string;
|
||||
NALL_NOINLINE auto disassembleInstruction() -> string;
|
||||
NALL_NOINLINE auto disassembleContext() -> string;
|
||||
|
||||
auto disassembleOpcode(char* output) -> void;
|
||||
auto disassembleALT0(char* output) -> void;
|
||||
|
||||
@@ -111,8 +111,8 @@ struct HG51B {
|
||||
auto writeRegister(n7 address, n24 data) -> void;
|
||||
|
||||
//disassembler.cpp
|
||||
noinline auto disassembleInstruction(maybe<n15> pb = {}, maybe<n8> pc = {}) -> string;
|
||||
noinline auto disassembleContext() -> string;
|
||||
NALL_NOINLINE auto disassembleInstruction(maybe<n15> pb = {}, maybe<n8> pc = {}) -> string;
|
||||
NALL_NOINLINE auto disassembleContext() -> string;
|
||||
|
||||
protected:
|
||||
struct Registers {
|
||||
|
||||
@@ -124,8 +124,8 @@ struct I8080 {
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
//disassembler.cpp
|
||||
noinline auto disassembleInstruction(maybe<n16> pc = {}) -> string;
|
||||
noinline auto disassembleContext() -> string;
|
||||
NALL_NOINLINE auto disassembleInstruction(maybe<n16> pc = {}) -> string;
|
||||
NALL_NOINLINE auto disassembleContext() -> string;
|
||||
|
||||
auto disassemble(n16 pc, n8 prefix, n8 code) -> string;
|
||||
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
auto MOS6502::addressAbsolute() -> n16 {
|
||||
n16 absolute = operand();
|
||||
absolute |= operand() << 8;
|
||||
return absolute;
|
||||
}
|
||||
|
||||
auto MOS6502::addressAbsoluteJMP() -> n16 {
|
||||
n16 absolute = operand();
|
||||
// JMP Indirect last_cycle
|
||||
L absolute |= operand() << 8;
|
||||
return absolute;
|
||||
}
|
||||
|
||||
auto MOS6502::addressAbsoluteXRead() -> n16 {
|
||||
n16 absolute = operand();
|
||||
absolute |= operand() << 8;
|
||||
idlePageCrossed(absolute, absolute + X);
|
||||
return absolute + X;
|
||||
}
|
||||
|
||||
auto MOS6502::addressAbsoluteXWrite() -> n16 {
|
||||
n16 absolute = operand();
|
||||
absolute |= operand() << 8;
|
||||
idlePageAlways(absolute, absolute + X);
|
||||
return absolute + X;
|
||||
}
|
||||
|
||||
auto MOS6502::addressAbsoluteYRead() -> n16 {
|
||||
n16 absolute = operand();
|
||||
absolute |= operand() << 8;
|
||||
idlePageCrossed(absolute, absolute + Y);
|
||||
return absolute + Y;
|
||||
}
|
||||
|
||||
auto MOS6502::addressAbsoluteYWrite() -> n16 {
|
||||
n16 absolute = operand();
|
||||
absolute |= operand() << 8;
|
||||
idlePageAlways(absolute, absolute + Y);
|
||||
return absolute + Y;
|
||||
}
|
||||
|
||||
auto MOS6502::addressAccumulator() -> n16 {
|
||||
return PC;
|
||||
}
|
||||
|
||||
auto MOS6502::addressImmediate() -> n16 {
|
||||
return PC++;
|
||||
}
|
||||
|
||||
auto MOS6502::addressImplied() -> n16 {
|
||||
return PC;
|
||||
}
|
||||
|
||||
auto MOS6502::addressIndirect() -> n16 {
|
||||
n16 absolute = operand();
|
||||
absolute |= operand() << 8;
|
||||
n16 indirect = read(absolute);
|
||||
absolute.byte(0)++;
|
||||
// JMP Indirect last_cycle
|
||||
L indirect |= read(absolute) << 8;
|
||||
return indirect;
|
||||
}
|
||||
|
||||
auto MOS6502::addressIndirectX() -> n16 {
|
||||
n8 zeroPage = operand();
|
||||
idleZeroPage(zeroPage);
|
||||
n16 absolute = load(zeroPage + X);
|
||||
absolute |= load(zeroPage + X + 1) << 8;
|
||||
return absolute;
|
||||
}
|
||||
|
||||
auto MOS6502::addressIndirectYRead() -> n16 {
|
||||
n16 zeroPage = operand();
|
||||
n16 absolute = load(zeroPage + 0);
|
||||
absolute |= load(zeroPage + 1) << 8;
|
||||
idlePageCrossed(absolute, absolute + Y);
|
||||
return absolute + Y;
|
||||
}
|
||||
|
||||
auto MOS6502::addressIndirectYWrite() -> n16 {
|
||||
n8 zeroPage = operand();
|
||||
n16 absolute = load(zeroPage + 0);
|
||||
absolute |= load(zeroPage + 1) << 8;
|
||||
idlePageAlways(absolute, absolute + Y);
|
||||
return absolute + Y;
|
||||
}
|
||||
|
||||
auto MOS6502::addressRelative() -> n16 {
|
||||
return PC++;
|
||||
}
|
||||
|
||||
auto MOS6502::addressZeroPage() -> n16 {
|
||||
return operand();
|
||||
}
|
||||
|
||||
auto MOS6502::addressZeroPageX() -> n16 {
|
||||
n8 zeropage = operand();
|
||||
idleZeroPage(zeropage);
|
||||
zeropage += X;
|
||||
return zeropage;
|
||||
}
|
||||
|
||||
auto MOS6502::addressZeroPageY() -> n16 {
|
||||
n8 zeropage = operand();
|
||||
idleZeroPage(zeropage);
|
||||
zeropage += Y;
|
||||
return zeropage;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
auto MOS6502::algorithmADC(n8 i) -> n8 {
|
||||
auto MOS6502::algorithmADC() -> void {
|
||||
n8 i = MDR;
|
||||
i16 o = A + i + C;
|
||||
if(!BCD || !D) {
|
||||
C = o.bit(8);
|
||||
@@ -17,197 +18,319 @@ auto MOS6502::algorithmADC(n8 i) -> n8 {
|
||||
if(o > 0x9f) o += 0x60;
|
||||
C = o > 0xff;
|
||||
}
|
||||
return o;
|
||||
A = o;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmALR(n8 i) -> n8 {
|
||||
n8 o = A & i;
|
||||
C = o.bit(0);
|
||||
o >>= 1;
|
||||
Z = o == 0;
|
||||
N = o.bit(7);
|
||||
|
||||
return o;
|
||||
auto MOS6502::algorithmAND() -> void {
|
||||
A &= MDR;
|
||||
Z = A == 0;
|
||||
N = A.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmAND(n8 i) -> n8 {
|
||||
n8 o = A & i;
|
||||
Z = o == 0;
|
||||
N = o.bit(7);
|
||||
return o;
|
||||
auto MOS6502::algorithmASLA() -> void {
|
||||
C = A.bit(7);
|
||||
A <<= 1;
|
||||
Z = A == 0;
|
||||
N = A.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmANC(n8 i) -> n8 {
|
||||
n8 o = A & i;
|
||||
Z = o == 0;
|
||||
N = o.bit(7);
|
||||
C = N;
|
||||
return o;
|
||||
auto MOS6502::algorithmASLM() -> void {
|
||||
C = MDR.bit(7);
|
||||
MDR <<= 1;
|
||||
Z = MDR == 0;
|
||||
N = MDR.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmARR(n8 i) -> n8 {
|
||||
n8 o = A & i;
|
||||
Z = o == 0;
|
||||
N = o.bit(7);
|
||||
auto MOS6502::algorithmBCC() -> void {
|
||||
algorithmBranch(C == 0);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmBCS() -> void {
|
||||
algorithmBranch(C == 1);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmBEQ() -> void {
|
||||
algorithmBranch(Z == 1);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmBIT() -> void {
|
||||
Z = (A & MDR) == 0;
|
||||
V = MDR.bit(6);
|
||||
N = MDR.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmBMI() -> void {
|
||||
algorithmBranch(N == 1);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmBNE() -> void {
|
||||
algorithmBranch(Z == 0);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmBPL() -> void {
|
||||
algorithmBranch(N == 0);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmBRK() -> void {
|
||||
MDR = read(MAR); // dummy implied read
|
||||
PC++;
|
||||
push(PCH);
|
||||
push(PCL);
|
||||
|
||||
n16 vector = 0xfffe;
|
||||
nmi(vector);
|
||||
|
||||
push(P | 0x30);
|
||||
I = 1;
|
||||
|
||||
PCL = read(vector++);
|
||||
cancelNmi();
|
||||
PCH = read(vector++);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmBVC() -> void {
|
||||
algorithmBranch(V == 0);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmBVS() -> void {
|
||||
algorithmBranch(V == 1);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmBranch(bool take) -> void {
|
||||
if(!take) {
|
||||
L MDR = read(MAR); // dummy relative read
|
||||
} else {
|
||||
bool irq = irqPending();
|
||||
MDR = read(MAR);
|
||||
n16 newPC = PC + (i8)MDR;
|
||||
bool pageCrossed = newPC >> 8 != PC >> 8;
|
||||
|
||||
// idle page crossed without carry
|
||||
if (pageCrossed) {
|
||||
read(PC & 0xff00 | newPC & 0x00ff);
|
||||
PC = newPC;
|
||||
L idle();
|
||||
} else {
|
||||
// a taken non-page-crossing branch ignores
|
||||
// IRQ/NMI during its last clock, so that
|
||||
// next instruction executes before the IRQ
|
||||
if (!irq)
|
||||
delayIrq();
|
||||
else
|
||||
lastCycle();
|
||||
|
||||
PC = newPC;
|
||||
// idle page crossed with carry
|
||||
idle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmCLC() -> void {
|
||||
C = 0;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmCLD() -> void {
|
||||
D = 0;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmCLI() -> void {
|
||||
I = 0;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmCLV() -> void {
|
||||
V = 0;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmCMP() -> void {
|
||||
n9 o = A - MDR;
|
||||
C = !o.bit(8);
|
||||
Z = n8(o) == 0;
|
||||
N = o.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmCPX() -> void {
|
||||
n9 o = X - MDR;
|
||||
C = !o.bit(8);
|
||||
Z = n8(o) == 0;
|
||||
N = o.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmCPY() -> void {
|
||||
n9 o = Y - MDR;
|
||||
C = !o.bit(8);
|
||||
Z = n8(o) == 0;
|
||||
N = o.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmDEC() -> void {
|
||||
MDR--;
|
||||
Z = MDR == 0;
|
||||
N = MDR.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmDEX() -> void {
|
||||
X--;
|
||||
Z = X == 0;
|
||||
N = X.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmDEY() -> void {
|
||||
Y--;
|
||||
Z = Y == 0;
|
||||
N = Y.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmEOR() -> void {
|
||||
A ^= MDR;
|
||||
Z = A == 0;
|
||||
N = A.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmINC() -> void {
|
||||
MDR++;
|
||||
Z = MDR == 0;
|
||||
N = MDR.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmINX() -> void {
|
||||
X++;
|
||||
Z = X == 0;
|
||||
N = X.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmINY() -> void {
|
||||
Y++;
|
||||
Z = Y == 0;
|
||||
N = Y.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmJMP() -> void {
|
||||
PC = MAR;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmJSR() -> void {
|
||||
PC--;
|
||||
push(PCH);
|
||||
L push(PCL);
|
||||
PC = MAR;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmLDA() -> void {
|
||||
A = MDR;
|
||||
Z = A == 0;
|
||||
N = A.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmLDX() -> void {
|
||||
X = MDR;
|
||||
Z = X == 0;
|
||||
N = X.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmLDY() -> void {
|
||||
Y = MDR;
|
||||
Z = Y == 0;
|
||||
N = Y.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmLSRA() -> void {
|
||||
C = A.bit(0);
|
||||
A >>= 1;
|
||||
Z = A == 0;
|
||||
N = A.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmLSRM() -> void {
|
||||
C = MDR.bit(0);
|
||||
MDR >>= 1;
|
||||
Z = MDR == 0;
|
||||
N = MDR.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmNOP() -> void {}
|
||||
|
||||
auto MOS6502::algorithmORA() -> void {
|
||||
A |= MDR;
|
||||
Z = A == 0;
|
||||
N = A.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmPHA() -> void {
|
||||
read(MAR); // dummy implied read
|
||||
L push(A);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmPHP() -> void {
|
||||
MDR = read(MAR); // dummy implied read
|
||||
L push(P | 0x30);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmPLA() -> void {
|
||||
MDR = read(MAR); // dummy implied read
|
||||
MDR = read(0x0100 | S); // dummy stack read
|
||||
L A = pull();
|
||||
Z = n8(A) == 0;
|
||||
N = A.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmPLP() -> void {
|
||||
MDR = read(MAR); // dummy implied read
|
||||
MDR = read(0x0100 | S); // dummy stack read
|
||||
L P = pull();
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmROLA() -> void {
|
||||
bool c = C;
|
||||
o = c << 7 | o >> 1;
|
||||
Z = o == 0;
|
||||
N = o.bit(7);
|
||||
C = o.bit(6);
|
||||
V = o.bit(6) ^ o.bit(5);
|
||||
|
||||
return o;
|
||||
C = A.bit(7);
|
||||
A = A << 1 | c;
|
||||
Z = A == 0;
|
||||
N = A.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmASL(n8 i) -> n8 {
|
||||
C = i.bit(7);
|
||||
i <<= 1;
|
||||
Z = i == 0;
|
||||
N = i.bit(7);
|
||||
return i;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmATX(n8 i) -> n8 {
|
||||
A |= 0xff; //TODO: OR value may differ on non-NES platforms
|
||||
n8 o = A & i;
|
||||
X = o;
|
||||
Z = o == 0;
|
||||
N = o.bit(7);
|
||||
return o;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmAXS(n8 i) -> n8 {
|
||||
n9 o = (A & X) - i;
|
||||
C = !o.bit(8);
|
||||
Z = n8(o) == 0;
|
||||
N = o.bit(7);
|
||||
return o;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmBIT(n8 i) -> n8 {
|
||||
Z = (A & i) == 0;
|
||||
V = i.bit(6);
|
||||
N = i.bit(7);
|
||||
return A;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmCMP(n8 i) -> n8 {
|
||||
n9 o = A - i;
|
||||
C = !o.bit(8);
|
||||
Z = n8(o) == 0;
|
||||
N = o.bit(7);
|
||||
return A;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmCPX(n8 i) -> n8 {
|
||||
n9 o = X - i;
|
||||
C = !o.bit(8);
|
||||
Z = n8(o) == 0;
|
||||
N = o.bit(7);
|
||||
return X;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmCPY(n8 i) -> n8 {
|
||||
n9 o = Y - i;
|
||||
C = !o.bit(8);
|
||||
Z = n8(o) == 0;
|
||||
N = o.bit(7);
|
||||
return Y;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmDEC(n8 i) -> n8 {
|
||||
i--;
|
||||
Z = i == 0;
|
||||
N = i.bit(7);
|
||||
return i;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmDCP(n8 i) -> n8 {
|
||||
auto value = algorithmDEC(i);
|
||||
A = algorithmCMP(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmEOR(n8 i) -> n8 {
|
||||
n8 o = A ^ i;
|
||||
Z = o == 0;
|
||||
N = o.bit(7);
|
||||
return o;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmINC(n8 i) -> n8 {
|
||||
i++;
|
||||
Z = i == 0;
|
||||
N = i.bit(7);
|
||||
return i;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmISC(n8 i) -> n8 {
|
||||
auto value = algorithmINC(i);
|
||||
A = algorithmSBC(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmLD(n8 i) -> n8 {
|
||||
Z = i == 0;
|
||||
N = i.bit(7);
|
||||
return i;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmLSR(n8 i) -> n8 {
|
||||
C = i.bit(0);
|
||||
i >>= 1;
|
||||
Z = i == 0;
|
||||
N = i.bit(7);
|
||||
return i;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmSRE(n8 i) -> n8 {
|
||||
auto value = algorithmLSR(i);
|
||||
A = algorithmEOR(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmORA(n8 i) -> n8 {
|
||||
n8 o = A | i;
|
||||
Z = o == 0;
|
||||
N = o.bit(7);
|
||||
return o;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmROL(n8 i) -> n8 {
|
||||
auto MOS6502::algorithmROLM() -> void {
|
||||
bool c = C;
|
||||
C = i.bit(7);
|
||||
i = i << 1 | c;
|
||||
Z = i == 0;
|
||||
N = i.bit(7);
|
||||
return i;
|
||||
C = MDR.bit(7);
|
||||
MDR = MDR << 1 | c;
|
||||
Z = MDR == 0;
|
||||
N = MDR.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmRLA(n8 i) -> n8 {
|
||||
auto value = algorithmROL(i);
|
||||
A = algorithmAND(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmROR(n8 i) -> n8 {
|
||||
auto MOS6502::algorithmRORA() -> void {
|
||||
bool c = C;
|
||||
C = i.bit(0);
|
||||
i = c << 7 | i >> 1;
|
||||
Z = i == 0;
|
||||
N = i.bit(7);
|
||||
return i;
|
||||
C = A.bit(0);
|
||||
A = A >> 1 | c << 7;
|
||||
Z = A == 0;
|
||||
N = A.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmRRA(n8 i) -> n8 {
|
||||
auto value = algorithmROR(i);
|
||||
A = algorithmADC(value);
|
||||
return value;
|
||||
auto MOS6502::algorithmRORM() -> void {
|
||||
bool c = C;
|
||||
C = MDR.bit(0);
|
||||
MDR = MDR >> 1 | c << 7;
|
||||
Z = MDR == 0;
|
||||
N = MDR.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmSBC(n8 i) -> n8 {
|
||||
i = ~i;
|
||||
auto MOS6502::algorithmRTI() -> void {
|
||||
MDR = read(MAR); // dummy implied read
|
||||
MDR = read(0x0100 | S); // dummy stack read
|
||||
P = pull();
|
||||
PCL = pull();
|
||||
L PCH = pull();
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmRTS() -> void {
|
||||
MDR = read(MAR); // dummy implied read
|
||||
MDR = read(0x0100 | S); // dummy stack read
|
||||
PCL = pull();
|
||||
PCH = pull();
|
||||
L idle();
|
||||
PC++;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmSBC() -> void {
|
||||
n8 i = ~MDR;
|
||||
i16 o = A + i + C;
|
||||
if(!BCD || !D) {
|
||||
C = o.bit(8);
|
||||
@@ -226,11 +349,215 @@ auto MOS6502::algorithmSBC(n8 i) -> n8 {
|
||||
if(o <= 0xff) o -= 0x60;
|
||||
C = o > 0xff;
|
||||
}
|
||||
return o;
|
||||
A = o;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmSLO(n8 i) -> n8 {
|
||||
auto value = algorithmASL(i);
|
||||
A = algorithmORA(value);
|
||||
return value;
|
||||
}
|
||||
auto MOS6502::algorithmSEC() -> void {
|
||||
C = 1;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmSED() -> void {
|
||||
D = 1;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmSEI() -> void {
|
||||
I = 1;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmSTA() -> void {
|
||||
MDR = A;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmSTX() -> void {
|
||||
MDR = X;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmSTY() -> void {
|
||||
MDR = Y;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmTAX() -> void {
|
||||
X = A;
|
||||
Z = X == 0;
|
||||
N = X.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmTAY() -> void {
|
||||
Y = A;
|
||||
Z = Y == 0;
|
||||
N = Y.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmTSX() -> void {
|
||||
X = S;
|
||||
Z = X == 0;
|
||||
N = X.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmTXA() -> void {
|
||||
A = X;
|
||||
Z = A == 0;
|
||||
N = A.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmTXS() -> void {
|
||||
S = X;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmTYA() -> void {
|
||||
A = Y;
|
||||
Z = A == 0;
|
||||
N = A.bit(7);
|
||||
}
|
||||
|
||||
// unofficial algorithm
|
||||
auto MOS6502::algorithmAAC() -> void {
|
||||
A &= MDR;
|
||||
C = A.bit(7);
|
||||
Z = A == 0;
|
||||
N = A.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmAAX() -> void {
|
||||
MDR = A & X;
|
||||
}
|
||||
|
||||
// Highly unstable, do not use.
|
||||
// A base value in A is determined based on the
|
||||
// contets of A and a constant, which may be
|
||||
// typically $00, $ff, $ee, etc. The value of
|
||||
// this constant depends on temerature, the chip
|
||||
// series, and maybe other factors, as well.
|
||||
// In order to eliminate these uncertaincies
|
||||
// from the equation, use either 0 as the operand
|
||||
// or a value of $FF in the accumulator.
|
||||
auto MOS6502::algorithmANE() -> void {
|
||||
A = (A | 0xff) & X & MDR;
|
||||
Z = A == 0;
|
||||
N = A.bit(7);
|
||||
}
|
||||
|
||||
// This operation involves the adder:
|
||||
// V-flag is set according to (A AND oper) + oper
|
||||
// The carry is not set, but bit 7 (sign)
|
||||
// is exchanged with the carry
|
||||
auto MOS6502::algorithmARR() -> void {
|
||||
A = ((A & MDR) >> 1) | (C << 7);
|
||||
C = A.bit(6);
|
||||
Z = A == 0;
|
||||
N = A.bit(7);
|
||||
V = C ^ A.bit(5);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmASR() -> void {
|
||||
A &= MDR;
|
||||
C = A.bit(0);
|
||||
A >>= 1;
|
||||
Z = A == 0;
|
||||
N = A.bit(7);
|
||||
}
|
||||
|
||||
// Highly unstable, involves a 'magic' constant, see ANE
|
||||
auto MOS6502::algorithmATX() -> void {
|
||||
X = A = (A | 0xff) & MDR;
|
||||
Z = A == 0;
|
||||
N = A.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmAXS() -> void {
|
||||
X &= A;
|
||||
n9 o = X - MDR;
|
||||
C = !o.bit(8);
|
||||
X = o;
|
||||
Z = X == 0;
|
||||
N = X.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmDCP() -> void {
|
||||
MDR--;
|
||||
algorithmCMP();
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmISC() -> void {
|
||||
MDR++;
|
||||
algorithmSBC();
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmLAS() -> void {
|
||||
S = A = X = MDR & S;
|
||||
Z = A == 0;
|
||||
N = A.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmLAX() -> void {
|
||||
A = X = MDR;
|
||||
Z = A == 0;
|
||||
N = A.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmRLA() -> void {
|
||||
bool c = C;
|
||||
C = MDR.bit(7);
|
||||
MDR = MDR << 1 | c;
|
||||
A &= MDR;
|
||||
Z = A == 0;
|
||||
N = A.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmRRA() -> void {
|
||||
bool c = C;
|
||||
C = MDR.bit(0);
|
||||
MDR = MDR >> 1 | c << 7;
|
||||
algorithmADC();
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmSLO() -> void {
|
||||
C = MDR.bit(7);
|
||||
MDR <<= 1;
|
||||
A |= MDR;
|
||||
Z = A == 0;
|
||||
N = A.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmSRE() -> void {
|
||||
C = MDR.bit(0);
|
||||
MDR >>= 1;
|
||||
A ^= MDR;
|
||||
Z = A == 0;
|
||||
N = A.bit(7);
|
||||
}
|
||||
|
||||
// unstable: sometimes 'AND (H+1)' is dropped,
|
||||
// page boundary crossings may not work (with
|
||||
// the high-byte of the value used as the
|
||||
// high-byte of the address)
|
||||
auto MOS6502::algorithmSXA() -> void {
|
||||
MAR = ((X & ((MAR >> 8) + 1)) << 8) | (MAR & 0xff);
|
||||
MDR = MAR >> 8;
|
||||
}
|
||||
|
||||
// unstable: sometimes 'AND (H+1)' is dropped,
|
||||
// page boundary crossings may not work (with
|
||||
// the high-byte of the value used as the
|
||||
// high-byte of the address)
|
||||
auto MOS6502::algorithmSYA() -> void {
|
||||
MAR = ((Y & ((MAR >> 8) + 1)) << 8) | (MAR & 0xff);
|
||||
MDR = MAR >> 8;
|
||||
}
|
||||
|
||||
// unstable: sometimes 'AND (H+1)' is dropped,
|
||||
// page boundary crossings may not work (with
|
||||
// the high-byte of the value used as the
|
||||
// high-byte of the address)
|
||||
auto MOS6502::algorithmTAS() -> void {
|
||||
S = A & X;
|
||||
MDR = A & X & (((MAR - Y) >> 8) + 1);
|
||||
}
|
||||
|
||||
// These instructions freeze the CPU.
|
||||
// The processor will be trapped infinitely
|
||||
// in T1 phase with $FF on the data bus.
|
||||
// Reset required.
|
||||
auto MOS6502::algorithmJAM() -> void {
|
||||
PC--;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
#define op(id, name, ...) case id: return instruction##name(__VA_ARGS__);
|
||||
#define fp(name) &MOS6502::algorithm##name
|
||||
#define addr(name) &MOS6502::address##name
|
||||
|
||||
auto MOS6502::interrupt() -> void {
|
||||
idle();
|
||||
@@ -14,233 +15,282 @@ auto MOS6502::interrupt() -> void {
|
||||
L PCH = read(vector++);
|
||||
}
|
||||
|
||||
auto MOS6502::reset() -> void {
|
||||
idle();
|
||||
idle();
|
||||
read(0x0100 | S--); // Dummy push (PCH)
|
||||
read(0x0100 | S--); // Dummy push (PCL)
|
||||
n16 vector = 0xfffc;
|
||||
read(0x0100 | S--); // Dummy push (P)
|
||||
I = 1;
|
||||
PCL = read(vector++);
|
||||
L PCH = read(vector++);
|
||||
resetting = 0;
|
||||
}
|
||||
|
||||
auto MOS6502::instruction() -> void {
|
||||
auto code = opcode();
|
||||
if(resetting) reset();
|
||||
|
||||
switch(code) {
|
||||
op(0x00, Break)
|
||||
op(0x01, IndirectXRead, fp(ORA), A)
|
||||
op(0x04, NoOperationZeroPage)
|
||||
op(0x05, ZeroPageRead, fp(ORA), A)
|
||||
op(0x06, ZeroPageModify, fp(ASL))
|
||||
op(0x07, ZeroPageModify, fp(SLO))
|
||||
op(0x08, PushP)
|
||||
op(0x09, Immediate, fp(ORA), A)
|
||||
op(0x0a, Implied, fp(ASL), A)
|
||||
op(0x0b, Immediate, fp(ANC), A)
|
||||
op(0x0c, NoOperationAbsolute)
|
||||
op(0x0d, AbsoluteRead, fp(ORA), A)
|
||||
op(0x0e, AbsoluteModify, fp(ASL))
|
||||
op(0x0f, AbsoluteModify, fp(SLO))
|
||||
op(0x10, Branch, N == 0)
|
||||
op(0x11, IndirectYRead, fp(ORA), A)
|
||||
op(0x14, NoOperationZeroPage, X)
|
||||
op(0x15, ZeroPageRead, fp(ORA), A, X)
|
||||
op(0x16, ZeroPageModify, fp(ASL), X)
|
||||
op(0x17, ZeroPageModify, fp(SLO), X)
|
||||
op(0x18, Clear, C)
|
||||
op(0x19, AbsoluteRead, fp(ORA), A, Y)
|
||||
op(0x1a, NoOperation)
|
||||
op(0x1b, AbsoluteModify, fp(SLO), Y)
|
||||
op(0x1c, NoOperationAbsolute, X)
|
||||
op(0x1d, AbsoluteRead, fp(ORA), A, X)
|
||||
op(0x1e, AbsoluteModify, fp(ASL), X)
|
||||
op(0x1f, AbsoluteModify, fp(SLO), X)
|
||||
op(0x20, CallAbsolute)
|
||||
op(0x21, IndirectXRead, fp(AND), A)
|
||||
op(0x24, ZeroPageRead, fp(BIT), A)
|
||||
op(0x25, ZeroPageRead, fp(AND), A)
|
||||
op(0x26, ZeroPageModify, fp(ROL))
|
||||
op(0x27, ZeroPageModify, fp(RLA))
|
||||
op(0x28, PullP)
|
||||
op(0x29, Immediate, fp(AND), A)
|
||||
op(0x2a, Implied, fp(ROL), A)
|
||||
op(0x2b, Immediate, fp(ANC), A)
|
||||
op(0x2c, AbsoluteRead, fp(BIT), A)
|
||||
op(0x2d, AbsoluteRead, fp(AND), A)
|
||||
op(0x2e, AbsoluteModify, fp(ROL))
|
||||
op(0x2f, AbsoluteModify, fp(RLA))
|
||||
op(0x30, Branch, N == 1)
|
||||
op(0x31, IndirectYRead, fp(AND), A)
|
||||
op(0x34, NoOperationZeroPage, X)
|
||||
op(0x35, ZeroPageRead, fp(AND), A, X)
|
||||
op(0x36, ZeroPageModify, fp(ROL), X)
|
||||
op(0x37, ZeroPageModify, fp(RLA), X)
|
||||
op(0x38, Set, C)
|
||||
op(0x39, AbsoluteRead, fp(AND), A, Y)
|
||||
op(0x3a, NoOperation)
|
||||
op(0x3b, AbsoluteModify, fp(RLA), Y)
|
||||
op(0x3c, NoOperationAbsolute, X)
|
||||
op(0x3d, AbsoluteRead, fp(AND), A, X)
|
||||
op(0x3e, AbsoluteModify, fp(ROL), X)
|
||||
op(0x3f, AbsoluteModify, fp(RLA), X)
|
||||
op(0x40, ReturnInterrupt)
|
||||
op(0x41, IndirectXRead, fp(EOR), A)
|
||||
op(0x43, IndirectXRead, fp(SRE), X)
|
||||
op(0x44, NoOperationZeroPage)
|
||||
op(0x45, ZeroPageRead, fp(EOR), A)
|
||||
op(0x46, ZeroPageModify, fp(LSR))
|
||||
op(0x47, ZeroPageModify, fp(SRE))
|
||||
op(0x48, Push, A)
|
||||
op(0x49, Immediate, fp(EOR), A)
|
||||
op(0x4a, Implied, fp(LSR), A)
|
||||
op(0x4b, Immediate, fp(ALR), A)
|
||||
op(0x4c, JumpAbsolute)
|
||||
op(0x4d, AbsoluteRead, fp(EOR), A)
|
||||
op(0x4e, AbsoluteModify, fp(LSR))
|
||||
op(0x4f, AbsoluteModify, fp(SRE))
|
||||
op(0x50, Branch, V == 0)
|
||||
op(0x51, IndirectYRead, fp(EOR), A)
|
||||
op(0x54, NoOperationZeroPage, X)
|
||||
op(0x55, ZeroPageRead, fp(EOR), A, X)
|
||||
op(0x56, ZeroPageModify, fp(LSR), X)
|
||||
op(0x57, ZeroPageModify, fp(SRE), X)
|
||||
op(0x58, Clear, I)
|
||||
op(0x59, AbsoluteRead, fp(EOR), A, Y)
|
||||
op(0x5a, NoOperation)
|
||||
op(0x5b, AbsoluteModify, fp(SRE), Y)
|
||||
op(0x5c, NoOperationAbsolute, X)
|
||||
op(0x5d, AbsoluteRead, fp(EOR), A, X)
|
||||
op(0x5e, AbsoluteModify, fp(LSR), X)
|
||||
op(0x5f, AbsoluteModify, fp(SRE), X)
|
||||
op(0x60, ReturnSubroutine)
|
||||
op(0x61, IndirectXRead, fp(ADC), A)
|
||||
op(0x64, NoOperationZeroPage)
|
||||
op(0x65, ZeroPageRead, fp(ADC), A)
|
||||
op(0x66, ZeroPageModify, fp(ROR))
|
||||
op(0x67, ZeroPageModify, fp(RRA))
|
||||
op(0x68, Pull, A)
|
||||
op(0x69, Immediate, fp(ADC), A)
|
||||
op(0x6a, Implied, fp(ROR), A)
|
||||
op(0x6b, Immediate, fp(ARR), A)
|
||||
op(0x6c, JumpIndirect)
|
||||
op(0x6d, AbsoluteRead, fp(ADC), A)
|
||||
op(0x6e, AbsoluteModify, fp(ROR))
|
||||
op(0x6f, AbsoluteModify, fp(RRA))
|
||||
op(0x70, Branch, V == 1)
|
||||
op(0x71, IndirectYRead, fp(ADC), A)
|
||||
op(0x74, NoOperationZeroPage, X)
|
||||
op(0x75, ZeroPageRead, fp(ADC), A, X)
|
||||
op(0x76, ZeroPageModify, fp(ROR), X)
|
||||
op(0x77, ZeroPageModify, fp(RRA), X)
|
||||
op(0x78, Set, I)
|
||||
op(0x79, AbsoluteRead, fp(ADC), A, Y)
|
||||
op(0x7a, NoOperation)
|
||||
op(0x7b, AbsoluteModify, fp(RRA), Y)
|
||||
op(0x7c, NoOperationAbsolute, X)
|
||||
op(0x7d, AbsoluteRead, fp(ADC), A, X)
|
||||
op(0x7e, AbsoluteModify, fp(ROR), X)
|
||||
op(0x7f, AbsoluteModify, fp(RRA), X)
|
||||
op(0x80, NoOperationImmediate)
|
||||
op(0x81, IndirectXWrite, A)
|
||||
op(0x82, NoOperationImmediate)
|
||||
op(0x84, ZeroPageWrite, Y)
|
||||
op(0x85, ZeroPageWrite, A)
|
||||
op(0x86, ZeroPageWrite, X)
|
||||
op(0x88, Implied, fp(DEC), Y)
|
||||
op(0x89, NoOperationImmediate)
|
||||
op(0x8a, Transfer, X, A, 1)
|
||||
op(0x8c, AbsoluteWrite, Y)
|
||||
op(0x8d, AbsoluteWrite, A)
|
||||
op(0x8e, AbsoluteWrite, X)
|
||||
op(0x90, Branch, C == 0)
|
||||
op(0x91, IndirectYWrite, A)
|
||||
op(0x94, ZeroPageWrite, Y, X)
|
||||
op(0x95, ZeroPageWrite, A, X)
|
||||
op(0x96, ZeroPageWrite, X, Y)
|
||||
op(0x98, Transfer, Y, A, 1)
|
||||
op(0x99, AbsoluteWrite, A, Y)
|
||||
op(0x9a, Transfer, X, S, 0)
|
||||
op(0x9d, AbsoluteWrite, A, X)
|
||||
op(0xa0, Immediate, fp(LD), Y)
|
||||
op(0xa1, IndirectXRead, fp(LD), A)
|
||||
op(0xa2, Immediate, fp(LD), X)
|
||||
op(0xa4, ZeroPageRead, fp(LD), Y)
|
||||
op(0xa5, ZeroPageRead, fp(LD), A)
|
||||
op(0xa6, ZeroPageRead, fp(LD), X)
|
||||
op(0xa8, Transfer, A, Y, 1)
|
||||
op(0xa9, Immediate, fp(LD), A)
|
||||
op(0xaa, Transfer, A, X, 1)
|
||||
op(0xab, Immediate, fp(ATX), A)
|
||||
op(0xac, AbsoluteRead, fp(LD), Y)
|
||||
op(0xad, AbsoluteRead, fp(LD), A)
|
||||
op(0xae, AbsoluteRead, fp(LD), X)
|
||||
op(0xb0, Branch, C == 1)
|
||||
op(0xb1, IndirectYRead, fp(LD), A)
|
||||
op(0xb3, IndirectYRead, fp(LD), A, X)
|
||||
op(0xb4, ZeroPageRead, fp(LD), Y, X)
|
||||
op(0xb5, ZeroPageRead, fp(LD), A, X)
|
||||
op(0xb6, ZeroPageRead, fp(LD), X, Y)
|
||||
op(0xb8, Clear, V)
|
||||
op(0xb9, AbsoluteRead, fp(LD), A, Y)
|
||||
op(0xba, Transfer, S, X, 1)
|
||||
op(0xbc, AbsoluteRead, fp(LD), Y, X)
|
||||
op(0xbd, AbsoluteRead, fp(LD), A, X)
|
||||
op(0xbe, AbsoluteRead, fp(LD), X, Y)
|
||||
op(0xc0, Immediate, fp(CPY), Y)
|
||||
op(0xc1, IndirectXRead, fp(CMP), A)
|
||||
op(0xc2, NoOperationImmediate)
|
||||
op(0xc4, ZeroPageRead, fp(CPY), Y)
|
||||
op(0xc5, ZeroPageRead, fp(CMP), A)
|
||||
op(0xc6, ZeroPageModify, fp(DEC))
|
||||
op(0xc7, ZeroPageModify, fp(DCP))
|
||||
op(0xc8, Implied, fp(INC), Y)
|
||||
op(0xc9, Immediate, fp(CMP), A)
|
||||
op(0xca, Implied, fp(DEC), X)
|
||||
op(0xcb, Immediate, fp(AXS), X)
|
||||
op(0xcc, AbsoluteRead, fp(CPY), Y)
|
||||
op(0xcd, AbsoluteRead, fp(CMP), A)
|
||||
op(0xce, AbsoluteModify, fp(DEC))
|
||||
op(0xcf, AbsoluteModify, fp(DCP))
|
||||
op(0xd0, Branch, Z == 0)
|
||||
op(0xd1, IndirectYRead, fp(CMP), A)
|
||||
op(0xd4, NoOperationZeroPage, X)
|
||||
op(0xd5, ZeroPageRead, fp(CMP), A, X)
|
||||
op(0xd6, ZeroPageModify, fp(DEC), X)
|
||||
op(0xd7, ZeroPageModify, fp(DCP), X)
|
||||
op(0xd8, Clear, D)
|
||||
op(0xd9, AbsoluteRead, fp(CMP), A, Y)
|
||||
op(0xda, NoOperation)
|
||||
op(0xdb, AbsoluteModify, fp(DCP), Y)
|
||||
op(0xdc, NoOperationAbsolute, X)
|
||||
op(0xdd, AbsoluteRead, fp(CMP), A, X)
|
||||
op(0xde, AbsoluteModify, fp(DEC), X)
|
||||
op(0xdf, AbsoluteModify, fp(DCP), X)
|
||||
op(0xe0, Immediate, fp(CPX), X)
|
||||
op(0xe1, IndirectXRead, fp(SBC), A)
|
||||
op(0xe2, NoOperationImmediate)
|
||||
op(0xe4, ZeroPageRead, fp(CPX), X)
|
||||
op(0xe5, ZeroPageRead, fp(SBC), A)
|
||||
op(0xe6, ZeroPageModify, fp(INC))
|
||||
op(0xe7, ZeroPageModify, fp(ISC))
|
||||
op(0xe8, Implied, fp(INC), X)
|
||||
op(0xe9, Immediate, fp(SBC), A)
|
||||
op(0xea, NoOperation)
|
||||
op(0xeb, Immediate, fp(SBC), A)
|
||||
op(0xec, AbsoluteRead, fp(CPX), X)
|
||||
op(0xed, AbsoluteRead, fp(SBC), A)
|
||||
op(0xee, AbsoluteModify, fp(INC))
|
||||
op(0xef, AbsoluteModify, fp(ISC))
|
||||
op(0xf0, Branch, Z == 1)
|
||||
op(0xf1, IndirectYRead, fp(SBC), A)
|
||||
op(0xf4, NoOperationZeroPage, X)
|
||||
op(0xf5, ZeroPageRead, fp(SBC), A, X)
|
||||
op(0xf6, ZeroPageModify, fp(INC), X)
|
||||
op(0xf7, ZeroPageModify, fp(ISC), X)
|
||||
op(0xf8, Set, D)
|
||||
op(0xf9, AbsoluteRead, fp(SBC), A, Y)
|
||||
op(0xfa, NoOperation)
|
||||
op(0xfb, AbsoluteModify, fp(ISC), Y)
|
||||
op(0xfc, NoOperationAbsolute, X)
|
||||
op(0xfd, AbsoluteRead, fp(SBC), A, X)
|
||||
op(0xfe, AbsoluteModify, fp(INC), X)
|
||||
op(0xff, AbsoluteModify, fp(ISC), X)
|
||||
switch(opcode()) {
|
||||
op(0x00, None, addr(Implied), fp(BRK))
|
||||
op(0x01, Load, addr(IndirectX), fp(ORA))
|
||||
op(0x02, None, addr(Implied), fp(JAM))
|
||||
op(0x03, Modify, addr(IndirectX), fp(SLO))
|
||||
op(0x04, Load, addr(ZeroPage), fp(NOP))
|
||||
op(0x05, Load, addr(ZeroPage), fp(ORA))
|
||||
op(0x06, Modify, addr(ZeroPage), fp(ASLM))
|
||||
op(0x07, Modify, addr(ZeroPage), fp(SLO))
|
||||
op(0x08, None, addr(Implied), fp(PHP))
|
||||
op(0x09, Load, addr(Immediate), fp(ORA))
|
||||
op(0x0a, Load, addr(Accumulator), fp(ASLA))
|
||||
op(0x0b, Load, addr(Immediate), fp(AAC))
|
||||
op(0x0c, Load, addr(Absolute), fp(NOP))
|
||||
op(0x0d, Load, addr(Absolute), fp(ORA))
|
||||
op(0x0e, Modify, addr(Absolute), fp(ASLM))
|
||||
op(0x0f, Modify, addr(Absolute), fp(SLO))
|
||||
op(0x10, None, addr(Relative), fp(BPL))
|
||||
op(0x11, Load, addr(IndirectYRead), fp(ORA))
|
||||
op(0x12, None, addr(Implied), fp(JAM))
|
||||
op(0x13, Modify, addr(IndirectYWrite), fp(SLO))
|
||||
op(0x14, Load, addr(ZeroPageX), fp(NOP))
|
||||
op(0x15, Load, addr(ZeroPageX), fp(ORA))
|
||||
op(0x16, Modify, addr(ZeroPageX), fp(ASLM))
|
||||
op(0x17, Modify, addr(ZeroPageX), fp(SLO))
|
||||
op(0x18, Load, addr(Implied), fp(CLC))
|
||||
op(0x19, Load, addr(AbsoluteYRead), fp(ORA))
|
||||
op(0x1a, Load, addr(Implied), fp(NOP))
|
||||
op(0x1b, Modify, addr(AbsoluteYWrite), fp(SLO))
|
||||
op(0x1c, Load, addr(AbsoluteXRead), fp(NOP))
|
||||
op(0x1d, Load, addr(AbsoluteXRead), fp(ORA))
|
||||
op(0x1e, Modify, addr(AbsoluteXWrite), fp(ASLM))
|
||||
op(0x1f, Modify, addr(AbsoluteXWrite), fp(SLO))
|
||||
op(0x20, Load, addr(Absolute), fp(JSR))
|
||||
op(0x21, Load, addr(IndirectX), fp(AND))
|
||||
op(0x22, None, addr(Implied), fp(JAM))
|
||||
op(0x23, Modify, addr(IndirectX), fp(RLA))
|
||||
op(0x24, Load, addr(ZeroPage), fp(BIT))
|
||||
op(0x25, Load, addr(ZeroPage), fp(AND))
|
||||
op(0x26, Modify, addr(ZeroPage), fp(ROLM))
|
||||
op(0x27, Modify, addr(ZeroPage), fp(RLA))
|
||||
op(0x28, None, addr(Implied), fp(PLP))
|
||||
op(0x29, Load, addr(Immediate), fp(AND))
|
||||
op(0x2a, Load, addr(Implied), fp(ROLA))
|
||||
op(0x2b, Load, addr(Immediate), fp(AAC))
|
||||
op(0x2c, Load, addr(Absolute), fp(BIT))
|
||||
op(0x2d, Load, addr(Absolute), fp(AND))
|
||||
op(0x2e, Modify, addr(Absolute), fp(ROLM))
|
||||
op(0x2f, Modify, addr(Absolute), fp(RLA))
|
||||
op(0x30, None, addr(Relative), fp(BMI))
|
||||
op(0x31, Load, addr(IndirectYRead), fp(AND))
|
||||
op(0x32, None, addr(Implied), fp(JAM))
|
||||
op(0x33, Modify, addr(IndirectYWrite), fp(RLA))
|
||||
op(0x34, Load, addr(ZeroPageX), fp(NOP))
|
||||
op(0x35, Load, addr(ZeroPageX), fp(AND))
|
||||
op(0x36, Modify, addr(ZeroPageX), fp(ROLM))
|
||||
op(0x37, Modify, addr(ZeroPageX), fp(RLA))
|
||||
op(0x38, Load, addr(Implied), fp(SEC))
|
||||
op(0x39, Load, addr(AbsoluteYRead), fp(AND))
|
||||
op(0x3a, Load, addr(Implied), fp(NOP))
|
||||
op(0x3b, Modify, addr(AbsoluteYWrite), fp(RLA))
|
||||
op(0x3c, Load, addr(AbsoluteXRead), fp(NOP))
|
||||
op(0x3d, Load, addr(AbsoluteXRead), fp(AND))
|
||||
op(0x3e, Modify, addr(AbsoluteXWrite), fp(ROLM))
|
||||
op(0x3f, Modify, addr(AbsoluteXWrite), fp(RLA))
|
||||
op(0x40, None, addr(Implied), fp(RTI))
|
||||
op(0x41, Load, addr(IndirectX), fp(EOR))
|
||||
op(0x42, None, addr(Implied), fp(JAM))
|
||||
op(0x43, Modify, addr(IndirectX), fp(SRE))
|
||||
op(0x44, Load, addr(ZeroPage), fp(NOP))
|
||||
op(0x45, Load, addr(ZeroPage), fp(EOR))
|
||||
op(0x46, Modify, addr(ZeroPage), fp(LSRM))
|
||||
op(0x47, Modify, addr(ZeroPage), fp(SRE))
|
||||
op(0x48, None, addr(Implied), fp(PHA))
|
||||
op(0x49, Load, addr(Immediate), fp(EOR))
|
||||
op(0x4a, Load, addr(Implied), fp(LSRA))
|
||||
op(0x4b, Load, addr(Immediate), fp(ASR))
|
||||
op(0x4c, None, addr(AbsoluteJMP), fp(JMP))
|
||||
op(0x4d, Load, addr(Absolute), fp(EOR))
|
||||
op(0x4e, Modify, addr(Absolute), fp(LSRM))
|
||||
op(0x4f, Modify, addr(Absolute), fp(SRE))
|
||||
op(0x50, None, addr(Relative), fp(BVC))
|
||||
op(0x51, Load, addr(IndirectYRead), fp(EOR))
|
||||
op(0x52, None, addr(Implied), fp(JAM))
|
||||
op(0x53, Modify, addr(IndirectYWrite), fp(SRE))
|
||||
op(0x54, Load, addr(ZeroPageX), fp(NOP))
|
||||
op(0x55, Load, addr(ZeroPageX), fp(EOR))
|
||||
op(0x56, Modify, addr(ZeroPageX), fp(LSRM))
|
||||
op(0x57, Modify, addr(ZeroPageX), fp(SRE))
|
||||
op(0x58, Load, addr(Implied), fp(CLI))
|
||||
op(0x59, Load, addr(AbsoluteYRead), fp(EOR))
|
||||
op(0x5a, Load, addr(Implied), fp(NOP))
|
||||
op(0x5b, Modify, addr(AbsoluteYWrite), fp(SRE))
|
||||
op(0x5c, Load, addr(AbsoluteXRead), fp(NOP))
|
||||
op(0x5d, Load, addr(AbsoluteXRead), fp(EOR))
|
||||
op(0x5e, Modify, addr(AbsoluteXWrite), fp(LSRM))
|
||||
op(0x5f, Modify, addr(AbsoluteXWrite), fp(SRE))
|
||||
op(0x60, None, addr(Implied), fp(RTS))
|
||||
op(0x61, Load, addr(IndirectX), fp(ADC))
|
||||
op(0x62, None, addr(Implied), fp(JAM))
|
||||
op(0x63, Modify, addr(IndirectX), fp(RRA))
|
||||
op(0x64, Load, addr(ZeroPage), fp(NOP))
|
||||
op(0x65, Load, addr(ZeroPage), fp(ADC))
|
||||
op(0x66, Modify, addr(ZeroPage), fp(RORM))
|
||||
op(0x67, Modify, addr(ZeroPage), fp(RRA))
|
||||
op(0x68, None, addr(Implied), fp(PLA))
|
||||
op(0x69, Load, addr(Immediate), fp(ADC))
|
||||
op(0x6a, Load, addr(Implied), fp(RORA))
|
||||
op(0x6b, Load, addr(Immediate), fp(ARR))
|
||||
op(0x6c, None, addr(Indirect), fp(JMP))
|
||||
op(0x6d, Load, addr(Absolute), fp(ADC))
|
||||
op(0x6e, Modify, addr(Absolute), fp(RORM))
|
||||
op(0x6f, Modify, addr(Absolute), fp(RRA))
|
||||
op(0x70, None, addr(Relative), fp(BVS))
|
||||
op(0x71, Load, addr(IndirectYRead), fp(ADC))
|
||||
op(0x72, None, addr(Implied), fp(JAM))
|
||||
op(0x73, Modify, addr(IndirectYWrite), fp(RRA))
|
||||
op(0x74, Load, addr(ZeroPageX), fp(NOP))
|
||||
op(0x75, Load, addr(ZeroPageX), fp(ADC))
|
||||
op(0x76, Modify, addr(ZeroPageX), fp(RORM))
|
||||
op(0x77, Modify, addr(ZeroPageX), fp(RRA))
|
||||
op(0x78, Load, addr(Implied), fp(SEI))
|
||||
op(0x79, Load, addr(AbsoluteYRead), fp(ADC))
|
||||
op(0x7a, Load, addr(Implied), fp(NOP))
|
||||
op(0x7b, Modify, addr(AbsoluteYWrite), fp(RRA))
|
||||
op(0x7c, Load, addr(AbsoluteXRead), fp(NOP))
|
||||
op(0x7d, Load, addr(AbsoluteXRead), fp(ADC))
|
||||
op(0x7e, Modify, addr(AbsoluteXWrite), fp(RORM))
|
||||
op(0x7f, Modify, addr(AbsoluteXWrite), fp(RRA))
|
||||
op(0x80, Load, addr(Immediate), fp(NOP))
|
||||
op(0x81, Store, addr(IndirectX), fp(STA))
|
||||
op(0x82, Load, addr(Immediate), fp(NOP))
|
||||
op(0x83, Store, addr(IndirectX), fp(AAX))
|
||||
op(0x84, Store, addr(ZeroPage), fp(STY))
|
||||
op(0x85, Store, addr(ZeroPage), fp(STA))
|
||||
op(0x86, Store, addr(ZeroPage), fp(STX))
|
||||
op(0x87, Store, addr(ZeroPage), fp(AAX))
|
||||
op(0x88, Load, addr(Implied), fp(DEY))
|
||||
op(0x89, Load, addr(Immediate), fp(NOP))
|
||||
op(0x8a, Load, addr(Implied), fp(TXA))
|
||||
op(0x8b, Load, addr(Immediate), fp(ANE))
|
||||
op(0x8c, Store, addr(Absolute), fp(STY))
|
||||
op(0x8d, Store, addr(Absolute), fp(STA))
|
||||
op(0x8e, Store, addr(Absolute), fp(STX))
|
||||
op(0x8f, Store, addr(Absolute), fp(AAX))
|
||||
op(0x90, None, addr(Relative), fp(BCC))
|
||||
op(0x91, Store, addr(IndirectYWrite), fp(STA))
|
||||
op(0x92, None, addr(Implied), fp(JAM))
|
||||
op(0x93, Store, addr(IndirectYWrite), fp(AAX))
|
||||
op(0x94, Store, addr(ZeroPageX), fp(STY))
|
||||
op(0x95, Store, addr(ZeroPageX), fp(STA))
|
||||
op(0x96, Store, addr(ZeroPageY), fp(STX))
|
||||
op(0x97, Store, addr(ZeroPageY), fp(AAX))
|
||||
op(0x98, Load, addr(Implied), fp(TYA))
|
||||
op(0x99, Store, addr(AbsoluteYWrite), fp(STA))
|
||||
op(0x9a, Load, addr(Implied), fp(TXS))
|
||||
op(0x9b, Store, addr(AbsoluteYWrite), fp(TAS))
|
||||
op(0x9c, Store, addr(AbsoluteXWrite), fp(SYA))
|
||||
op(0x9d, Store, addr(AbsoluteXWrite), fp(STA))
|
||||
op(0x9e, Store, addr(AbsoluteYWrite), fp(SXA))
|
||||
op(0x9f, Store, addr(AbsoluteYWrite), fp(AAX))
|
||||
op(0xa0, Load, addr(Immediate), fp(LDY))
|
||||
op(0xa1, Load, addr(IndirectX), fp(LDA))
|
||||
op(0xa2, Load, addr(Immediate), fp(LDX))
|
||||
op(0xa3, Load, addr(IndirectX), fp(LAX))
|
||||
op(0xa4, Load, addr(ZeroPage), fp(LDY))
|
||||
op(0xa5, Load, addr(ZeroPage), fp(LDA))
|
||||
op(0xa6, Load, addr(ZeroPage), fp(LDX))
|
||||
op(0xa7, Load, addr(ZeroPage), fp(LAX))
|
||||
op(0xa8, Load, addr(Implied), fp(TAY))
|
||||
op(0xa9, Load, addr(Immediate), fp(LDA))
|
||||
op(0xaa, Load, addr(Implied), fp(TAX))
|
||||
op(0xab, Load, addr(Immediate), fp(ATX))
|
||||
op(0xac, Load, addr(Absolute), fp(LDY))
|
||||
op(0xad, Load, addr(Absolute), fp(LDA))
|
||||
op(0xae, Load, addr(Absolute), fp(LDX))
|
||||
op(0xaf, Load, addr(Absolute), fp(LAX))
|
||||
op(0xb0, None, addr(Relative), fp(BCS))
|
||||
op(0xb1, Load, addr(IndirectYRead), fp(LDA))
|
||||
op(0xb2, None, addr(Implied), fp(JAM))
|
||||
op(0xb3, Load, addr(IndirectYRead), fp(LAX))
|
||||
op(0xb4, Load, addr(ZeroPageX), fp(LDY))
|
||||
op(0xb5, Load, addr(ZeroPageX), fp(LDA))
|
||||
op(0xb6, Load, addr(ZeroPageY), fp(LDX))
|
||||
op(0xb7, Load, addr(ZeroPageY), fp(LAX))
|
||||
op(0xb8, Load, addr(Implied), fp(CLV))
|
||||
op(0xb9, Load, addr(AbsoluteYRead), fp(LDA))
|
||||
op(0xba, Load, addr(Implied), fp(TSX))
|
||||
op(0xbb, Load, addr(AbsoluteYRead), fp(LAS))
|
||||
op(0xbc, Load, addr(AbsoluteXRead), fp(LDY))
|
||||
op(0xbd, Load, addr(AbsoluteXRead), fp(LDA))
|
||||
op(0xbe, Load, addr(AbsoluteYRead), fp(LDX))
|
||||
op(0xbf, Load, addr(AbsoluteYRead), fp(LAX))
|
||||
op(0xc0, Load, addr(Immediate), fp(CPY))
|
||||
op(0xc1, Load, addr(IndirectX), fp(CMP))
|
||||
op(0xc2, Load, addr(Immediate), fp(NOP))
|
||||
op(0xc3, Modify, addr(IndirectX), fp(DCP))
|
||||
op(0xc4, Load, addr(ZeroPage), fp(CPY))
|
||||
op(0xc5, Load, addr(ZeroPage), fp(CMP))
|
||||
op(0xc6, Modify, addr(ZeroPage), fp(DEC))
|
||||
op(0xc7, Modify, addr(ZeroPage), fp(DCP))
|
||||
op(0xc8, Load, addr(Implied), fp(INY))
|
||||
op(0xc9, Load, addr(Immediate), fp(CMP))
|
||||
op(0xca, Load, addr(Implied), fp(DEX))
|
||||
op(0xcb, Load, addr(Immediate), fp(AXS))
|
||||
op(0xcc, Load, addr(Absolute), fp(CPY))
|
||||
op(0xcd, Load, addr(Absolute), fp(CMP))
|
||||
op(0xce, Modify, addr(Absolute), fp(DEC))
|
||||
op(0xcf, Modify, addr(Absolute), fp(DCP))
|
||||
op(0xd0, None, addr(Relative), fp(BNE))
|
||||
op(0xd1, Load, addr(IndirectYRead), fp(CMP))
|
||||
op(0xd2, None, addr(Implied), fp(JAM))
|
||||
op(0xd3, Modify, addr(IndirectYWrite), fp(DCP))
|
||||
op(0xd4, Load, addr(ZeroPageX), fp(NOP))
|
||||
op(0xd5, Load, addr(ZeroPageX), fp(CMP))
|
||||
op(0xd6, Modify, addr(ZeroPageX), fp(DEC))
|
||||
op(0xd7, Modify, addr(ZeroPageX), fp(DCP))
|
||||
op(0xd8, Load, addr(Implied), fp(CLD))
|
||||
op(0xd9, Load, addr(AbsoluteYRead), fp(CMP))
|
||||
op(0xda, Load, addr(Implied), fp(NOP))
|
||||
op(0xdb, Modify, addr(AbsoluteYWrite), fp(DCP))
|
||||
op(0xdc, Load, addr(AbsoluteXRead), fp(NOP))
|
||||
op(0xdd, Load, addr(AbsoluteXRead), fp(CMP))
|
||||
op(0xde, Modify, addr(AbsoluteXWrite), fp(DEC))
|
||||
op(0xdf, Modify, addr(AbsoluteXWrite), fp(DCP))
|
||||
op(0xe0, Load, addr(Immediate), fp(CPX))
|
||||
op(0xe1, Load, addr(IndirectX), fp(SBC))
|
||||
op(0xe2, Load, addr(Immediate), fp(NOP))
|
||||
op(0xe3, Modify, addr(IndirectX), fp(ISC))
|
||||
op(0xe4, Load, addr(ZeroPage), fp(CPX))
|
||||
op(0xe5, Load, addr(ZeroPage), fp(SBC))
|
||||
op(0xe6, Modify, addr(ZeroPage), fp(INC))
|
||||
op(0xe7, Modify, addr(ZeroPage), fp(ISC))
|
||||
op(0xe8, Load, addr(Implied), fp(INX))
|
||||
op(0xe9, Load, addr(Immediate), fp(SBC))
|
||||
op(0xea, Load, addr(Implied), fp(NOP))
|
||||
op(0xeb, Load, addr(Immediate), fp(SBC))
|
||||
op(0xec, Load, addr(Absolute), fp(CPX))
|
||||
op(0xed, Load, addr(Absolute), fp(SBC))
|
||||
op(0xee, Modify, addr(Absolute), fp(INC))
|
||||
op(0xef, Modify, addr(Absolute), fp(ISC))
|
||||
op(0xf0, None, addr(Relative), fp(BEQ))
|
||||
op(0xf1, Load, addr(IndirectYRead), fp(SBC))
|
||||
op(0xf2, None, addr(Implied), fp(JAM))
|
||||
op(0xf3, Modify, addr(IndirectYWrite), fp(ISC))
|
||||
op(0xf4, Load, addr(ZeroPageX), fp(NOP))
|
||||
op(0xf5, Load, addr(ZeroPageX), fp(SBC))
|
||||
op(0xf6, Modify, addr(ZeroPageX), fp(INC))
|
||||
op(0xf7, Modify, addr(ZeroPageX), fp(ISC))
|
||||
op(0xf8, Load, addr(Implied), fp(SED))
|
||||
op(0xf9, Load, addr(AbsoluteYRead), fp(SBC))
|
||||
op(0xfa, Load, addr(Implied), fp(NOP))
|
||||
op(0xfb, Modify, addr(AbsoluteYWrite), fp(ISC))
|
||||
op(0xfc, Load, addr(AbsoluteXRead), fp(NOP))
|
||||
op(0xfd, Load, addr(AbsoluteXRead), fp(SBC))
|
||||
op(0xfe, Modify, addr(AbsoluteXWrite), fp(INC))
|
||||
op(0xff, Modify, addr(AbsoluteXWrite), fp(ISC))
|
||||
}
|
||||
|
||||
//unimplemented instruction
|
||||
debug(unusual, "[6502] unimplemented opcode ", hex(code));
|
||||
return instructionNoOperation();
|
||||
}
|
||||
|
||||
#undef op
|
||||
#undef fp
|
||||
#undef addr
|
||||
@@ -1,264 +1,24 @@
|
||||
auto MOS6502::instructionAbsoluteModify(fp alu) -> void {
|
||||
n16 absolute = operand();
|
||||
absolute |= operand() << 8;
|
||||
auto data = read(absolute);
|
||||
write(absolute, data);
|
||||
L write(absolute, ALU(data));
|
||||
auto MOS6502::instructionNone(addr mode, algorithm alg) -> void {
|
||||
MAR = ADDR();
|
||||
ALG();
|
||||
}
|
||||
|
||||
auto MOS6502::instructionAbsoluteModify(fp alu, n8 index) -> void {
|
||||
n16 absolute = operand();
|
||||
absolute |= operand() << 8;
|
||||
idlePageAlways(absolute, absolute + index);
|
||||
auto data = read(absolute + index);
|
||||
write(absolute + index, data);
|
||||
L write(absolute + index, ALU(data));
|
||||
auto MOS6502::instructionLoad(addr mode, algorithm alg) -> void {
|
||||
MAR = ADDR();
|
||||
L MDR = read(MAR);
|
||||
ALG();
|
||||
}
|
||||
|
||||
auto MOS6502::instructionAbsoluteRead(fp alu, n8& data) -> void {
|
||||
n16 absolute = operand();
|
||||
absolute |= operand() << 8;
|
||||
L data = ALU(read(absolute));
|
||||
auto MOS6502::instructionStore(addr mode, algorithm alg) -> void {
|
||||
MAR = ADDR();
|
||||
ALG();
|
||||
L write(MAR, MDR);
|
||||
}
|
||||
|
||||
auto MOS6502::instructionAbsoluteRead(fp alu, n8& data, n8 index) -> void {
|
||||
n16 absolute = operand();
|
||||
absolute |= operand() << 8;
|
||||
idlePageCrossed(absolute, absolute + index);
|
||||
L data = ALU(read(absolute + index));
|
||||
}
|
||||
|
||||
auto MOS6502::instructionAbsoluteWrite(n8& data) -> void {
|
||||
n16 absolute = operand();
|
||||
absolute |= operand() << 8;
|
||||
L write(absolute, data);
|
||||
}
|
||||
|
||||
auto MOS6502::instructionAbsoluteWrite(n8& data, n8 index) -> void {
|
||||
n16 absolute = operand();
|
||||
absolute |= operand() << 8;
|
||||
idlePageAlways(absolute, absolute + index);
|
||||
L write(absolute + index, data);
|
||||
}
|
||||
|
||||
auto MOS6502::instructionBranch(bool take) -> void {
|
||||
if(!take) {
|
||||
L operand();
|
||||
} else {
|
||||
i8 displacement = operand();
|
||||
idlePageCrossed(PC, PC + displacement);
|
||||
L idle();
|
||||
PC = PC + displacement;
|
||||
}
|
||||
}
|
||||
|
||||
auto MOS6502::instructionBreak() -> void {
|
||||
operand();
|
||||
push(PCH);
|
||||
push(PCL);
|
||||
n16 vector = 0xfffe;
|
||||
nmi(vector);
|
||||
push(P | 0x30);
|
||||
I = 1;
|
||||
PCL = read(vector++);
|
||||
L PCH = read(vector++);
|
||||
}
|
||||
|
||||
auto MOS6502::instructionCallAbsolute() -> void {
|
||||
n16 absolute = operand();
|
||||
absolute |= operand() << 8;
|
||||
idle();
|
||||
PC--;
|
||||
push(PCH);
|
||||
L push(PCL);
|
||||
PC = absolute;
|
||||
}
|
||||
|
||||
auto MOS6502::instructionClear(bool& flag) -> void {
|
||||
L idle();
|
||||
flag = 0;
|
||||
}
|
||||
|
||||
auto MOS6502::instructionImmediate(fp alu, n8& data) -> void {
|
||||
L data = ALU(operand());
|
||||
}
|
||||
|
||||
auto MOS6502::instructionImplied(fp alu, n8& data) -> void {
|
||||
L idle();
|
||||
data = ALU(data);
|
||||
}
|
||||
|
||||
auto MOS6502::instructionIndirectXRead(fp alu, n8& data) -> void {
|
||||
auto zeroPage = operand();
|
||||
load(zeroPage);
|
||||
n16 absolute = load(zeroPage + X + 0);
|
||||
absolute |= load(zeroPage + X + 1) << 8;
|
||||
L data = ALU(read(absolute));
|
||||
}
|
||||
|
||||
auto MOS6502::instructionIndirectXWrite(n8& data) -> void {
|
||||
auto zeroPage = operand();
|
||||
load(zeroPage);
|
||||
n16 absolute = load(zeroPage + X + 0);
|
||||
absolute |= load(zeroPage + X + 1) << 8;
|
||||
L write(absolute, data);
|
||||
}
|
||||
|
||||
auto MOS6502::instructionIndirectYRead(fp alu, n8& data) -> void {
|
||||
auto zeroPage = operand();
|
||||
n16 absolute = load(zeroPage + 0);
|
||||
absolute |= load(zeroPage + 1) << 8;
|
||||
idlePageCrossed(absolute, absolute + Y);
|
||||
L data = ALU(read(absolute + Y));
|
||||
}
|
||||
|
||||
auto MOS6502::instructionIndirectYRead(fp alu, n8& data, n8& data2) -> void {
|
||||
instructionIndirectYRead(alu, data);
|
||||
data2 = data;
|
||||
}
|
||||
|
||||
auto MOS6502::instructionIndirectYWrite(n8& data) -> void {
|
||||
auto zeroPage = operand();
|
||||
n16 absolute = load(zeroPage + 0);
|
||||
absolute |= load(zeroPage + 1) << 8;
|
||||
idlePageAlways(absolute, absolute + Y);
|
||||
L write(absolute + Y, data);
|
||||
}
|
||||
|
||||
auto MOS6502::instructionJumpAbsolute() -> void {
|
||||
n16 absolute = operand();
|
||||
L absolute |= operand() << 8;
|
||||
PC = absolute;
|
||||
}
|
||||
|
||||
auto MOS6502::instructionJumpIndirect() -> void {
|
||||
n16 absolute = operand();
|
||||
absolute |= operand() << 8;
|
||||
n16 pc = read(absolute);
|
||||
absolute.byte(0)++; //MOS6502: $00ff wraps here to $0000; not $0100
|
||||
L pc |= read(absolute) << 8;
|
||||
PC = pc;
|
||||
}
|
||||
|
||||
auto MOS6502::instructionNoOperation() -> void {
|
||||
L idle();
|
||||
}
|
||||
|
||||
auto MOS6502::instructionNoOperationAbsolute() -> void {
|
||||
n16 absolute = operand();
|
||||
absolute |= operand() << 8;
|
||||
L read(absolute);
|
||||
}
|
||||
|
||||
auto MOS6502::instructionNoOperationAbsolute(n8 index) -> void {
|
||||
n16 absolute = operand();
|
||||
absolute |= operand() << 8;
|
||||
idlePageCrossed(absolute, absolute + index);
|
||||
L read(absolute + index);
|
||||
}
|
||||
|
||||
auto MOS6502::instructionNoOperationImmediate() -> void {
|
||||
L operand();
|
||||
}
|
||||
|
||||
auto MOS6502::instructionNoOperationZeroPage() -> void {
|
||||
auto zeroPage = operand();
|
||||
L load(zeroPage);
|
||||
}
|
||||
|
||||
auto MOS6502::instructionNoOperationZeroPage(n8 index) -> void {
|
||||
auto zeroPage = operand();
|
||||
load(zeroPage);
|
||||
L load(zeroPage + index);
|
||||
}
|
||||
|
||||
auto MOS6502::instructionPull(n8& data) -> void {
|
||||
idle();
|
||||
idle();
|
||||
L data = pull();
|
||||
Z = data == 0;
|
||||
N = data.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::instructionPullP() -> void {
|
||||
idle();
|
||||
idle();
|
||||
L P = pull();
|
||||
}
|
||||
|
||||
auto MOS6502::instructionPush(n8& data) -> void {
|
||||
idle();
|
||||
L push(data);
|
||||
}
|
||||
|
||||
auto MOS6502::instructionPushP() -> void {
|
||||
idle();
|
||||
L push(P | 0x30);
|
||||
}
|
||||
|
||||
auto MOS6502::instructionReturnInterrupt() -> void {
|
||||
idle();
|
||||
idle();
|
||||
P = pull();
|
||||
PCL = pull();
|
||||
L PCH = pull();
|
||||
}
|
||||
|
||||
auto MOS6502::instructionReturnSubroutine() -> void {
|
||||
idle();
|
||||
idle();
|
||||
PCL = pull();
|
||||
PCH = pull();
|
||||
L idle();
|
||||
PC++;
|
||||
}
|
||||
|
||||
auto MOS6502::instructionSet(bool& flag) -> void {
|
||||
L idle();
|
||||
flag = 1;
|
||||
}
|
||||
|
||||
auto MOS6502::instructionTransfer(n8& source, n8& target, bool flag) -> void {
|
||||
L idle();
|
||||
target = source;
|
||||
if(!flag) return;
|
||||
Z = target == 0;
|
||||
N = target.bit(7);
|
||||
}
|
||||
|
||||
auto MOS6502::instructionZeroPageModify(fp alu) -> void {
|
||||
auto zeroPage = operand();
|
||||
auto data = load(zeroPage);
|
||||
store(zeroPage, data);
|
||||
L store(zeroPage, ALU(data));
|
||||
}
|
||||
|
||||
auto MOS6502::instructionZeroPageModify(fp alu, n8 index) -> void {
|
||||
auto zeroPage = operand();
|
||||
load(zeroPage);
|
||||
auto data = load(zeroPage + index);
|
||||
store(zeroPage + index, data);
|
||||
L store(zeroPage + index, ALU(data));
|
||||
}
|
||||
|
||||
auto MOS6502::instructionZeroPageRead(fp alu, n8& data) -> void {
|
||||
auto zeroPage = operand();
|
||||
L data = ALU(load(zeroPage));
|
||||
}
|
||||
|
||||
auto MOS6502::instructionZeroPageRead(fp alu, n8& data, n8 index) -> void {
|
||||
auto zeroPage = operand();
|
||||
load(zeroPage);
|
||||
L data = ALU(load(zeroPage + index));
|
||||
}
|
||||
|
||||
auto MOS6502::instructionZeroPageWrite(n8& data) -> void {
|
||||
auto zeroPage = operand();
|
||||
L store(zeroPage, data);
|
||||
}
|
||||
|
||||
auto MOS6502::instructionZeroPageWrite(n8& data, n8 index) -> void {
|
||||
auto zeroPage = operand();
|
||||
read(zeroPage);
|
||||
L store(zeroPage + index, data);
|
||||
auto MOS6502::instructionModify(addr mode, algorithm alg) -> void {
|
||||
MAR = ADDR();
|
||||
MDR = read(MAR);
|
||||
write(MAR, MDR); // dummy write
|
||||
ALG();
|
||||
L write(MAR, MDR);
|
||||
}
|
||||
@@ -2,6 +2,10 @@ auto MOS6502::idle() -> void {
|
||||
read(PC);
|
||||
}
|
||||
|
||||
auto MOS6502::idleZeroPage(n8 addr) -> void {
|
||||
read(addr);
|
||||
}
|
||||
|
||||
auto MOS6502::idlePageCrossed(n16 x, n16 y) -> void {
|
||||
if(x >> 8 == y >> 8) return;
|
||||
read((x & 0xff00) | (y & 0x00ff));
|
||||
|
||||
@@ -5,7 +5,8 @@ namespace ares {
|
||||
|
||||
#define PCH PC.byte(1)
|
||||
#define PCL PC.byte(0)
|
||||
#define ALU (this->*alu)
|
||||
#define ADDR (this->*mode)
|
||||
#define ALG (this->*alg)
|
||||
#define C P.c
|
||||
#define Z P.z
|
||||
#define I P.i
|
||||
@@ -14,6 +15,7 @@ namespace ares {
|
||||
#define N P.n
|
||||
#define L lastCycle();
|
||||
|
||||
#include "addresses.cpp"
|
||||
#include "memory.cpp"
|
||||
#include "algorithms.cpp"
|
||||
#include "instruction.cpp"
|
||||
@@ -23,7 +25,8 @@ namespace ares {
|
||||
|
||||
#undef PCH
|
||||
#undef PCL
|
||||
#undef ALU
|
||||
#undef ADDR
|
||||
#undef ALG
|
||||
#undef C
|
||||
#undef Z
|
||||
#undef I
|
||||
@@ -34,17 +37,17 @@ namespace ares {
|
||||
|
||||
auto MOS6502::power(bool reset) -> void {
|
||||
if(reset) {
|
||||
S-=3;
|
||||
P.i = 1;
|
||||
resetting = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
A = 0x00;
|
||||
X = 0x00;
|
||||
Y = 0x00;
|
||||
S = 0xfd;
|
||||
S = 0x00;
|
||||
P = 0x04;
|
||||
MDR = 0x00;
|
||||
resetting = 1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,8 +9,12 @@ struct MOS6502 {
|
||||
|
||||
virtual auto read(n16 addr) -> n8 = 0;
|
||||
virtual auto write(n16 addr, n8 data) -> void = 0;
|
||||
virtual auto lastCycle() -> void = 0;
|
||||
virtual auto nmi(n16& vector) -> void = 0;
|
||||
|
||||
virtual auto lastCycle() -> void {}
|
||||
virtual auto cancelNmi() -> void {}
|
||||
virtual auto delayIrq() -> void {}
|
||||
virtual auto irqPending() -> bool { return false; }
|
||||
virtual auto nmi(n16& vector) -> void {}
|
||||
virtual auto readDebugger(n16 addr) -> n8 { return 0; }
|
||||
|
||||
//mos6502.cpp
|
||||
@@ -18,6 +22,7 @@ struct MOS6502 {
|
||||
|
||||
//memory.cpp
|
||||
auto idle() -> void;
|
||||
auto idleZeroPage(n8) -> void;
|
||||
auto idlePageCrossed(n16, n16) -> void;
|
||||
auto idlePageAlways(n16, n16) -> void;
|
||||
auto opcode() -> n8;
|
||||
@@ -27,87 +32,132 @@ struct MOS6502 {
|
||||
auto push(n8 data) -> void;
|
||||
auto pull() -> n8;
|
||||
|
||||
//addresses.cpp
|
||||
using addr = auto (MOS6502::*)() -> n16;
|
||||
auto addressAbsolute() -> n16;
|
||||
auto addressAbsoluteJMP() -> n16;
|
||||
auto addressAbsoluteXRead() -> n16;
|
||||
auto addressAbsoluteXWrite() -> n16;
|
||||
auto addressAbsoluteYRead() -> n16;
|
||||
auto addressAbsoluteYWrite() -> n16;
|
||||
auto addressAccumulator() -> n16;
|
||||
auto addressImmediate() -> n16;
|
||||
auto addressImplied() -> n16;
|
||||
auto addressIndirect() -> n16;
|
||||
auto addressIndirectX() -> n16;
|
||||
auto addressIndirectYRead() -> n16;
|
||||
auto addressIndirectYWrite() -> n16;
|
||||
auto addressRelative() -> n16;
|
||||
auto addressZeroPage() -> n16;
|
||||
auto addressZeroPageX() -> n16;
|
||||
auto addressZeroPageY() -> n16;
|
||||
|
||||
//algorithms.cpp
|
||||
using fp = auto (MOS6502::*)(n8) -> n8;
|
||||
auto algorithmADC(n8) -> n8;
|
||||
auto algorithmAND(n8) -> n8;
|
||||
auto algorithmANC(n8) -> n8;
|
||||
auto algorithmARR(n8) -> n8;
|
||||
auto algorithmASL(n8) -> n8;
|
||||
auto algorithmATX(n8) -> n8;
|
||||
auto algorithmALR(n8) -> n8;
|
||||
auto algorithmAXS(n8) -> n8;
|
||||
auto algorithmBIT(n8) -> n8;
|
||||
auto algorithmCMP(n8) -> n8;
|
||||
auto algorithmCPX(n8) -> n8;
|
||||
auto algorithmCPY(n8) -> n8;
|
||||
auto algorithmDEC(n8) -> n8;
|
||||
auto algorithmDCP(n8) -> n8;
|
||||
auto algorithmEOR(n8) -> n8;
|
||||
auto algorithmINC(n8) -> n8;
|
||||
auto algorithmISC(n8) -> n8;
|
||||
auto algorithmLD (n8) -> n8;
|
||||
auto algorithmLSR(n8) -> n8;
|
||||
auto algorithmSRE(n8) -> n8;
|
||||
auto algorithmORA(n8) -> n8;
|
||||
auto algorithmROL(n8) -> n8;
|
||||
auto algorithmRLA(n8) -> n8;
|
||||
auto algorithmROR(n8) -> n8;
|
||||
auto algorithmRRA(n8) -> n8;
|
||||
auto algorithmSBC(n8) -> n8;
|
||||
auto algorithmSLO(n8) -> n8;
|
||||
using algorithm = auto (MOS6502::*)() -> void;
|
||||
|
||||
// official algorithms
|
||||
auto algorithmADC() -> void;
|
||||
auto algorithmAND() -> void;
|
||||
auto algorithmASLA() -> void;
|
||||
auto algorithmASLM() -> void;
|
||||
auto algorithmBCC() -> void;
|
||||
auto algorithmBCS() -> void;
|
||||
auto algorithmBEQ() -> void;
|
||||
auto algorithmBIT() -> void;
|
||||
auto algorithmBMI() -> void;
|
||||
auto algorithmBNE() -> void;
|
||||
auto algorithmBPL() -> void;
|
||||
auto algorithmBRK() -> void;
|
||||
auto algorithmBVC() -> void;
|
||||
auto algorithmBVS() -> void;
|
||||
auto algorithmBranch(bool take) -> void;
|
||||
auto algorithmCLC() -> void;
|
||||
auto algorithmCLD() -> void;
|
||||
auto algorithmCLI() -> void;
|
||||
auto algorithmCLV() -> void;
|
||||
auto algorithmCMP() -> void;
|
||||
auto algorithmCPX() -> void;
|
||||
auto algorithmCPY() -> void;
|
||||
auto algorithmDEC() -> void;
|
||||
auto algorithmDEX() -> void;
|
||||
auto algorithmDEY() -> void;
|
||||
auto algorithmEOR() -> void;
|
||||
auto algorithmINC() -> void;
|
||||
auto algorithmINX() -> void;
|
||||
auto algorithmINY() -> void;
|
||||
auto algorithmJMP() -> void;
|
||||
auto algorithmJSR() -> void;
|
||||
auto algorithmNOP() -> void;
|
||||
auto algorithmLDA() -> void;
|
||||
auto algorithmLDX() -> void;
|
||||
auto algorithmLDY() -> void;
|
||||
auto algorithmLSRA() -> void;
|
||||
auto algorithmLSRM() -> void;
|
||||
auto algorithmORA() -> void;
|
||||
auto algorithmPHA() -> void;
|
||||
auto algorithmPHP() -> void;
|
||||
auto algorithmPLA() -> void;
|
||||
auto algorithmPLP() -> void;
|
||||
auto algorithmROLA() -> void;
|
||||
auto algorithmROLM() -> void;
|
||||
auto algorithmRORA() -> void;
|
||||
auto algorithmRORM() -> void;
|
||||
auto algorithmRTI() -> void;
|
||||
auto algorithmRTS() -> void;
|
||||
auto algorithmSBC() -> void;
|
||||
auto algorithmSEC() -> void;
|
||||
auto algorithmSED() -> void;
|
||||
auto algorithmSEI() -> void;
|
||||
auto algorithmSTA() -> void;
|
||||
auto algorithmSTX() -> void;
|
||||
auto algorithmSTY() -> void;
|
||||
auto algorithmTAX() -> void;
|
||||
auto algorithmTAY() -> void;
|
||||
auto algorithmTSX() -> void;
|
||||
auto algorithmTXA() -> void;
|
||||
auto algorithmTXS() -> void;
|
||||
auto algorithmTYA() -> void;
|
||||
|
||||
// unofficial algorithms
|
||||
auto algorithmAAC() -> void;
|
||||
auto algorithmAAX() -> void;
|
||||
auto algorithmANE() -> void;
|
||||
auto algorithmARR() -> void;
|
||||
auto algorithmASR() -> void;
|
||||
auto algorithmATX() -> void;
|
||||
auto algorithmAXS() -> void;
|
||||
auto algorithmDCP() -> void;
|
||||
auto algorithmISC() -> void;
|
||||
auto algorithmLAS() -> void;
|
||||
auto algorithmLAX() -> void;
|
||||
auto algorithmRLA() -> void;
|
||||
auto algorithmRRA() -> void;
|
||||
auto algorithmSLO() -> void;
|
||||
auto algorithmSRE() -> void;
|
||||
auto algorithmSXA() -> void;
|
||||
auto algorithmSYA() -> void;
|
||||
auto algorithmTAS() -> void;
|
||||
|
||||
// freeze algorithms: need reset
|
||||
auto algorithmJAM() -> void;
|
||||
|
||||
//instruction.cpp
|
||||
auto interrupt() -> void;
|
||||
auto reset() -> void;
|
||||
auto instruction() -> void;
|
||||
|
||||
//instructions.cpp
|
||||
auto instructionAbsoluteModify(fp alu) -> void;
|
||||
auto instructionAbsoluteModify(fp alu, n8 index) -> void;
|
||||
auto instructionAbsoluteRead(fp alu, n8& data) -> void;
|
||||
auto instructionAbsoluteRead(fp alu, n8& data, n8 index) -> void;
|
||||
auto instructionAbsoluteWrite(n8& data) -> void;
|
||||
auto instructionAbsoluteWrite(n8& data, n8 index) -> void;
|
||||
auto instructionBranch(bool take) -> void;
|
||||
auto instructionBreak() -> void;
|
||||
auto instructionCallAbsolute() -> void;
|
||||
auto instructionClear(bool& flag) -> void;
|
||||
auto instructionImmediate(fp alu, n8& data) -> void;
|
||||
auto instructionImplied(fp alu, n8& data) -> void;
|
||||
auto instructionIndirectXRead(fp alu, n8& data) -> void;
|
||||
auto instructionIndirectXWrite(n8& data) -> void;
|
||||
auto instructionIndirectYRead(fp alu, n8& data) -> void;
|
||||
auto instructionIndirectYRead(fp, n8& data, n8& data2) -> void;
|
||||
auto instructionIndirectYWrite(n8& data) -> void;
|
||||
auto instructionJumpAbsolute() -> void;
|
||||
auto instructionJumpIndirect() -> void;
|
||||
auto instructionNoOperation() -> void;
|
||||
auto instructionNoOperationAbsolute() -> void;
|
||||
auto instructionNoOperationAbsolute(n8 index) -> void;
|
||||
auto instructionNoOperationImmediate() -> void;
|
||||
auto instructionNoOperationZeroPage() -> void;
|
||||
auto instructionNoOperationZeroPage(n8 index) -> void;
|
||||
auto instructionPull(n8& data) -> void;
|
||||
auto instructionPullP() -> void;
|
||||
auto instructionPush(n8& data) -> void;
|
||||
auto instructionPushP() -> void;
|
||||
auto instructionReturnInterrupt() -> void;
|
||||
auto instructionReturnSubroutine() -> void;
|
||||
auto instructionSet(bool& flag) -> void;
|
||||
auto instructionTransfer(n8& source, n8& target, bool flag) -> void;
|
||||
auto instructionZeroPageModify(fp alu) -> void;
|
||||
auto instructionZeroPageModify(fp alu, n8 index) -> void;
|
||||
auto instructionZeroPageRead(fp alu, n8& data) -> void;
|
||||
auto instructionZeroPageRead(fp alu, n8& data, n8 index) -> void;
|
||||
auto instructionZeroPageWrite(n8& data) -> void;
|
||||
auto instructionZeroPageWrite(n8& data, n8 index) -> void;
|
||||
auto instructionNone(addr mode, algorithm alg) -> void;
|
||||
auto instructionLoad(addr mode, algorithm alg) -> void;
|
||||
auto instructionStore(addr mode, algorithm alg) -> void;
|
||||
auto instructionModify(addr mode, algorithm alg) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
//disassembler.cpp
|
||||
noinline auto disassembleInstruction(maybe<n16> pc = {}) -> string;
|
||||
noinline auto disassembleContext() -> string;
|
||||
NALL_NOINLINE auto disassembleInstruction(maybe<n16> pc = {}) -> string;
|
||||
NALL_NOINLINE auto disassembleContext() -> string;
|
||||
|
||||
struct PR {
|
||||
bool c; //carry
|
||||
@@ -138,7 +188,9 @@ struct MOS6502 {
|
||||
n8 S;
|
||||
PR P;
|
||||
n16 PC;
|
||||
n16 MAR;
|
||||
n8 MDR;
|
||||
n1 resetting = 0;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -11,5 +11,7 @@ auto MOS6502::serialize(serializer& s) -> void {
|
||||
s(P.v);
|
||||
s(P.n);
|
||||
s(PC);
|
||||
s(MAR);
|
||||
s(MDR);
|
||||
s(resetting);
|
||||
}
|
||||
@@ -14,7 +14,7 @@ auto SH2::delaySlot(u32 pc) -> void {
|
||||
}
|
||||
|
||||
auto SH2::instruction() -> void {
|
||||
if constexpr(Accuracy::Interpreter) {
|
||||
if(Accuracy::Interpreter || !recompiler.enabled) {
|
||||
step(1);
|
||||
exceptionHandler();
|
||||
if constexpr(Accuracy::AddressErrors) {
|
||||
@@ -26,9 +26,7 @@ auto SH2::instruction() -> void {
|
||||
instructionPrologue(opcode);
|
||||
execute(opcode);
|
||||
instructionEpilogue();
|
||||
}
|
||||
|
||||
if constexpr(Accuracy::Recompiler) {
|
||||
} else {
|
||||
exceptionHandler();
|
||||
|
||||
// Recompiled blocks may be very small, negating the impact
|
||||
|
||||
@@ -513,19 +513,19 @@ auto SH2::MOVI(u32 i, u32 n) -> void {
|
||||
}
|
||||
|
||||
//MOV.W @(disp,PC),Rn
|
||||
noinline auto SH2::MOVWI(u32 d, u32 n) -> void {
|
||||
NALL_NOINLINE auto SH2::MOVWI(u32 d, u32 n) -> void {
|
||||
u32 pc = inDelaySlot() ? PPC - 2 : PC;
|
||||
R[n] = (s16)readWord(pc + d * 2);
|
||||
}
|
||||
|
||||
//MOV.L @(disp,PC),Rn
|
||||
noinline auto SH2::MOVLI(u32 d, u32 n) -> void {
|
||||
NALL_NOINLINE auto SH2::MOVLI(u32 d, u32 n) -> void {
|
||||
u32 pc = inDelaySlot() ? PPC - 2 : PC;
|
||||
R[n] = readLong((pc & ~3) + d * 4);
|
||||
}
|
||||
|
||||
//MOVA @(disp,PC),R0
|
||||
noinline auto SH2::MOVA(u32 d) -> void {
|
||||
NALL_NOINLINE auto SH2::MOVA(u32 d) -> void {
|
||||
u32 pc = inDelaySlot() ? PPC - 2 : PC;
|
||||
R[0] = (pc & ~3) + d * 4;
|
||||
}
|
||||
|
||||
@@ -300,6 +300,7 @@ struct SH2 {
|
||||
|
||||
static auto mask(u8 address, u8 size) -> u64;
|
||||
|
||||
bool enabled = false;
|
||||
bool callInstructionPrologue = false;
|
||||
bool inDelaySlot;
|
||||
u32 generation;
|
||||
|
||||
@@ -29,8 +29,8 @@ struct SM83 {
|
||||
|
||||
//disassembler.cpp
|
||||
virtual auto readDebugger(n16 address) -> n8 { return 0; }
|
||||
noinline auto disassembleInstruction(maybe<n16> pc = {}) -> string;
|
||||
noinline auto disassembleContext() -> string;
|
||||
NALL_NOINLINE auto disassembleInstruction(maybe<n16> pc = {}) -> string;
|
||||
NALL_NOINLINE auto disassembleContext() -> string;
|
||||
|
||||
//memory.cpp
|
||||
auto operand() -> n8;
|
||||
|
||||
@@ -117,9 +117,9 @@ struct SPC700 {
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
//disassembler.cpp
|
||||
noinline auto disassembleInstruction(n16 address, n1 p) -> string;
|
||||
noinline auto disassembleInstruction() -> string;
|
||||
noinline auto disassembleContext() -> string;
|
||||
NALL_NOINLINE auto disassembleInstruction(n16 address, n1 p) -> string;
|
||||
NALL_NOINLINE auto disassembleInstruction() -> string;
|
||||
NALL_NOINLINE auto disassembleContext() -> string;
|
||||
|
||||
struct Flags {
|
||||
bool c; //carry
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
noinline auto TLCS900H::disassembleInstruction() -> string {
|
||||
NALL_NOINLINE auto TLCS900H::disassembleInstruction() -> string {
|
||||
string output;
|
||||
|
||||
auto pc = load(PC);
|
||||
@@ -636,7 +636,7 @@ noinline auto TLCS900H::disassembleInstruction() -> string {
|
||||
return pad(output, -48);
|
||||
}
|
||||
|
||||
noinline auto TLCS900H::disassembleContext() -> string {
|
||||
NALL_NOINLINE auto TLCS900H::disassembleContext() -> string {
|
||||
string output;
|
||||
output.append("XWA:", hex(r.xwa[RFP].l.l0, 8L), " ");
|
||||
output.append("XBC:", hex(r.xbc[RFP].l.l0, 8L), " ");
|
||||
|
||||
@@ -25,8 +25,8 @@ struct uPD96050 {
|
||||
auto writeDP(n12 address, n8 data) -> void;
|
||||
|
||||
//disassembler.cpp
|
||||
noinline auto disassembleInstruction(maybe<n14> ip = {}) -> string;
|
||||
noinline auto disassembleContext() -> string;
|
||||
NALL_NOINLINE auto disassembleInstruction(maybe<n14> ip = {}) -> string;
|
||||
NALL_NOINLINE auto disassembleContext() -> string;
|
||||
|
||||
enum class Revision : u32 { uPD7725, uPD96050 } revision;
|
||||
n24 programROM[16384];
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
auto V30MZ::prefixFlush() -> void {
|
||||
prefix.count = 0;
|
||||
prefix.lock = 0;
|
||||
prefix.repeat = 0;
|
||||
prefix.segment = 0;
|
||||
}
|
||||
|
||||
auto V30MZ::interrupt(u8 vector, InterruptSource source) -> bool {
|
||||
if(source == InterruptSource::INT) wait(32);
|
||||
else if (source == InterruptSource::NMI) wait(26);
|
||||
@@ -5,15 +12,15 @@ auto V30MZ::interrupt(u8 vector, InterruptSource source) -> bool {
|
||||
|
||||
state.halt = 0;
|
||||
state.poll = 1;
|
||||
state.prefix = 0;
|
||||
|
||||
//if an IRQ fires during a rep string instruction;
|
||||
//flush prefix queue and seek back to first prefix.
|
||||
//this allows the transfer to resume after the IRQ.
|
||||
if(repeat()) {
|
||||
PC -= prefixes.size();
|
||||
prefixes.flush();
|
||||
if(state.prefix) {
|
||||
state.prefix = 0;
|
||||
PC -= prefix.count;
|
||||
}
|
||||
prefixFlush();
|
||||
|
||||
auto pc = read<Word>(0x0000, vector * 4 + 0);
|
||||
auto ps = read<Word>(0x0000, vector * 4 + 2);
|
||||
@@ -309,7 +316,7 @@ auto V30MZ::instruction() -> void {
|
||||
op(0xff, Group4MemImm<Word>)
|
||||
}
|
||||
|
||||
if(!state.prefix) prefixes.flush();
|
||||
if(!state.prefix) prefixFlush();
|
||||
if(PSW.BRK) interrupt(1, InterruptSource::SingleStep);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ auto V30MZ::instructionDecimalAdjust(bool negate) -> void {
|
||||
al = AL;
|
||||
AL += negate ? -0x60 : 0x60;
|
||||
PSW.CY = 1;
|
||||
if(negate) PSW.V |= (al ^ 0x06) & (al ^ AL) & 0x80; // undefined
|
||||
if(negate) PSW.V |= (al ^ 0x60) & (al ^ AL) & 0x80; // undefined
|
||||
else PSW.V |= (AL ^ al) & (AL ^ 0x60) & 0x80; // undefined
|
||||
} else {
|
||||
PSW.CY = 0;
|
||||
|
||||
@@ -220,8 +220,5 @@ auto V30MZ::instructionPopMem() -> void {
|
||||
wait(1);
|
||||
modRM();
|
||||
auto data = pop();
|
||||
//NEC bug: pop into a register will adjust the stack, but fail to set the register properly
|
||||
//in practice, this isn't an issue as assemblers will favor one-byte pop instructions,
|
||||
//but this difference can be used to distinguish Intel x86 chips from NEC V20/V30 chips.
|
||||
if(modrm.mod != 3) setMemory<Word>(data);
|
||||
setMemory<Word>(data);
|
||||
}
|
||||
@@ -1,21 +1,21 @@
|
||||
auto V30MZ::instructionSegment(n16 segment) -> void {
|
||||
if(prefixes.full()) prefixes.read(0);
|
||||
prefixes.write(opcode);
|
||||
prefix.segment = opcode;
|
||||
prefix.count++;
|
||||
state.prefix = 1;
|
||||
state.poll = 0;
|
||||
}
|
||||
|
||||
auto V30MZ::instructionRepeat() -> void {
|
||||
if(prefixes.full()) prefixes.read(0);
|
||||
prefixes.write(opcode);
|
||||
prefix.repeat = opcode;
|
||||
prefix.count++;
|
||||
wait(4);
|
||||
state.prefix = 1;
|
||||
state.poll = 0;
|
||||
}
|
||||
|
||||
auto V30MZ::instructionLock() -> void {
|
||||
if(prefixes.full()) prefixes.read(0);
|
||||
prefixes.write(opcode);
|
||||
prefix.lock = opcode;
|
||||
prefix.count++;
|
||||
state.prefix = 1;
|
||||
state.poll = 0;
|
||||
}
|
||||
@@ -75,8 +75,8 @@ auto V30MZ::instructionTranslate() -> void {
|
||||
auto V30MZ::instructionBound() -> void {
|
||||
wait(12);
|
||||
modRM();
|
||||
auto lo = getMemory<Word>(0);
|
||||
auto hi = getMemory<Word>(2);
|
||||
auto reg = getRegister<Word>();
|
||||
i16 lo = (i16)getMemory<Word>(0);
|
||||
i16 hi = (i16)getMemory<Word>(2);
|
||||
i16 reg = (i16)getRegister<Word>();
|
||||
if(reg < lo || reg > hi) interrupt(5, InterruptSource::CPU);
|
||||
}
|
||||
@@ -65,13 +65,13 @@ template<u32 size> auto V30MZ::instructionExchangeMemReg() -> void {
|
||||
|
||||
auto V30MZ::instructionLoadEffectiveAddressRegMem() -> void {
|
||||
wait(1);
|
||||
modRM();
|
||||
modRM(true);
|
||||
setRegister<Word>(modrm.address);
|
||||
}
|
||||
|
||||
auto V30MZ::instructionLoadSegmentMem(u16& segment) -> void {
|
||||
wait(4);
|
||||
modRM();
|
||||
modRM(true);
|
||||
setRegister<Word>(getMemory<Word>());
|
||||
segment = getMemory<Word>(2);
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
template<u32 size> auto V30MZ::instructionInString() -> void {
|
||||
wait(3);
|
||||
if(!repeat() || CW) {
|
||||
if(!prefix.repeat || CW) {
|
||||
auto data = in<size>(DW);
|
||||
write<size>(DS1, IY, data);
|
||||
IY += PSW.DIR ? -size : size;
|
||||
|
||||
if(!repeat() || !--CW) return;
|
||||
if(!prefix.repeat || !--CW) return;
|
||||
|
||||
state.prefix = 1;
|
||||
PC--;
|
||||
@@ -15,12 +15,12 @@ template<u32 size> auto V30MZ::instructionInString() -> void {
|
||||
|
||||
template<u32 size> auto V30MZ::instructionOutString() -> void {
|
||||
wait(3);
|
||||
if(!repeat() || CW) {
|
||||
if(!prefix.repeat || CW) {
|
||||
auto data = read<size>(segment(DS0), IX);
|
||||
out<size>(DW, data);
|
||||
IX += PSW.DIR ? -size : size;
|
||||
|
||||
if(!repeat() || !--CW) return;
|
||||
if(!prefix.repeat || !--CW) return;
|
||||
|
||||
state.prefix = 1;
|
||||
PC--;
|
||||
@@ -30,13 +30,13 @@ template<u32 size> auto V30MZ::instructionOutString() -> void {
|
||||
|
||||
template<u32 size> auto V30MZ::instructionMoveString() -> void {
|
||||
wait(3);
|
||||
if(!repeat() || CW) {
|
||||
if(!prefix.repeat || CW) {
|
||||
auto data = read<size>(segment(DS0), IX);
|
||||
write<size>(DS1, IY, data);
|
||||
IX += PSW.DIR ? -size : size;
|
||||
IY += PSW.DIR ? -size : size;
|
||||
|
||||
if(!repeat() || !--CW) return;
|
||||
if(!prefix.repeat || !--CW) return;
|
||||
|
||||
state.prefix = 1;
|
||||
PC--;
|
||||
@@ -46,16 +46,16 @@ template<u32 size> auto V30MZ::instructionMoveString() -> void {
|
||||
|
||||
template<u32 size> auto V30MZ::instructionCompareString() -> void {
|
||||
wait(4);
|
||||
if(!repeat() || CW) {
|
||||
if(!prefix.repeat || CW) {
|
||||
auto x = read<size>(segment(DS0), IX);
|
||||
auto y = read<size>(DS1, IY);
|
||||
IX += PSW.DIR ? -size : size;
|
||||
IY += PSW.DIR ? -size : size;
|
||||
SUB<size>(x, y);
|
||||
|
||||
if(!repeat() || !--CW) return;
|
||||
if(repeat() == RepeatWhileZeroLo && PSW.Z == 1) return;
|
||||
if(repeat() == RepeatWhileZeroHi && PSW.Z == 0) return;
|
||||
if(!prefix.repeat || !--CW) return;
|
||||
if(prefix.repeat == RepeatWhileZeroLo && PSW.Z == 1) return;
|
||||
if(prefix.repeat == RepeatWhileZeroHi && PSW.Z == 0) return;
|
||||
|
||||
state.prefix = 1;
|
||||
PC--;
|
||||
@@ -65,11 +65,11 @@ template<u32 size> auto V30MZ::instructionCompareString() -> void {
|
||||
|
||||
template<u32 size> auto V30MZ::instructionStoreString() -> void {
|
||||
wait(2);
|
||||
if(!repeat() || CW) {
|
||||
if(!prefix.repeat || CW) {
|
||||
write<size>(DS1, IY, getAccumulator<size>());
|
||||
IY += PSW.DIR ? -size : size;
|
||||
|
||||
if(!repeat() || !--CW) return;
|
||||
if(!prefix.repeat || !--CW) return;
|
||||
|
||||
state.prefix = 1;
|
||||
PC--;
|
||||
@@ -79,11 +79,11 @@ template<u32 size> auto V30MZ::instructionStoreString() -> void {
|
||||
|
||||
template<u32 size> auto V30MZ::instructionLoadString() -> void {
|
||||
wait(2);
|
||||
if(!repeat() || CW) {
|
||||
if(!prefix.repeat || CW) {
|
||||
setAccumulator<size>(read<size>(segment(DS0), IX));
|
||||
IX += PSW.DIR ? -size : size;
|
||||
|
||||
if(!repeat() || !--CW) return;
|
||||
if(!prefix.repeat || !--CW) return;
|
||||
|
||||
state.prefix = 1;
|
||||
PC--;
|
||||
@@ -93,15 +93,15 @@ template<u32 size> auto V30MZ::instructionLoadString() -> void {
|
||||
|
||||
template<u32 size> auto V30MZ::instructionScanString() -> void {
|
||||
wait(3);
|
||||
if(!repeat() || CW) {
|
||||
if(!prefix.repeat || CW) {
|
||||
auto x = getAccumulator<size>();
|
||||
auto y = read<size>(DS1, IY);
|
||||
IY += PSW.DIR ? -size : size;
|
||||
SUB<size>(x, y);
|
||||
|
||||
if(!repeat() || !--CW) return;
|
||||
if(repeat() == RepeatWhileZeroLo && PSW.Z == 1) return;
|
||||
if(repeat() == RepeatWhileZeroHi && PSW.Z == 0) return;
|
||||
if(!prefix.repeat || !--CW) return;
|
||||
if(prefix.repeat == RepeatWhileZeroLo && PSW.Z == 1) return;
|
||||
if(prefix.repeat == RepeatWhileZeroHi && PSW.Z == 0) return;
|
||||
|
||||
state.prefix = 1;
|
||||
PC--;
|
||||
|
||||
@@ -1,13 +1,31 @@
|
||||
auto V30MZ::modRM() -> void {
|
||||
auto V30MZ::modRM(bool forceAddress) -> void {
|
||||
auto data = fetch<Byte>();
|
||||
modrm.mem = data >> 0 & 7;
|
||||
modrm.reg = data >> 3 & 7;
|
||||
modrm.mod = data >> 6 & 3;
|
||||
modrm.useAddress = modrm.mod != 3;
|
||||
|
||||
if(modrm.mod == 3) return;
|
||||
if(modrm.mod == 0 && modrm.mem == 6) {
|
||||
modrm.segment = segment(DS0);
|
||||
modrm.address = fetch<Word>();
|
||||
} else if (modrm.mod == 3) {
|
||||
if (!forceAddress) return;
|
||||
// This is used when LEA/LDS/LES is called with register addressing mode,
|
||||
// which is normally undefined behaviour.
|
||||
//
|
||||
// TODO: Does this incur a 1-clock penalty? When?
|
||||
modrm.useAddress = true;
|
||||
wait(1);
|
||||
switch(modrm.mem) {
|
||||
case 0: modrm.segment = segment(DS0); modrm.address = BW + AW; break;
|
||||
case 1: modrm.segment = segment(DS0); modrm.address = BW + CW; break;
|
||||
case 2: modrm.segment = segment(SS ); modrm.address = BP + DW; break;
|
||||
case 3: modrm.segment = segment(SS ); modrm.address = BP + BW; break;
|
||||
case 4: modrm.segment = segment(DS0); modrm.address = IX + SP; break;
|
||||
case 5: modrm.segment = segment(DS0); modrm.address = IY + BP; break;
|
||||
case 6: modrm.segment = segment(SS ); modrm.address = BP + IX; break;
|
||||
case 7: modrm.segment = segment(DS0); modrm.address = BW + IY; break;
|
||||
}
|
||||
} else {
|
||||
switch(modrm.mem) {
|
||||
case 0: modrm.segment = segment(DS0); modrm.address = BW + IX; wait(1); break;
|
||||
@@ -35,21 +53,21 @@ template<> auto V30MZ::setRegister<Byte>(u16 data) -> void { *RB[modrm.reg] = da
|
||||
template<> auto V30MZ::setRegister<Word>(u16 data) -> void { *RW[modrm.reg] = data; }
|
||||
|
||||
template<> auto V30MZ::getMemory<Byte>(u32 offset) -> u16 {
|
||||
if(modrm.mod == 3) return *RB[modrm.mem];
|
||||
if(!modrm.useAddress) return *RB[modrm.mem];
|
||||
return read<Byte>(modrm.segment, modrm.address + offset);
|
||||
}
|
||||
|
||||
template<> auto V30MZ::getMemory<Word>(u32 offset) -> u16 {
|
||||
if(modrm.mod == 3) return *RW[modrm.mem];
|
||||
if(!modrm.useAddress) return *RW[modrm.mem];
|
||||
return read<Word>(modrm.segment, modrm.address + offset);
|
||||
}
|
||||
|
||||
template<> auto V30MZ::setMemory<Byte>(u16 data) -> void {
|
||||
if(modrm.mod == 3) return (void)(*RB[modrm.mem] = data);
|
||||
if(!modrm.useAddress) return (void)(*RB[modrm.mem] = data);
|
||||
return write<Byte>(modrm.segment, modrm.address, data);
|
||||
}
|
||||
|
||||
template<> auto V30MZ::setMemory<Word>(u16 data) -> void {
|
||||
if(modrm.mod == 3) return (void)(*RW[modrm.mem] = data);
|
||||
if(!modrm.useAddress) return (void)(*RW[modrm.mem] = data);
|
||||
return write<Word>(modrm.segment, modrm.address, data);
|
||||
}
|
||||
@@ -1,18 +1,8 @@
|
||||
auto V30MZ::repeat() -> u8 {
|
||||
for(auto prefix : prefixes) {
|
||||
if(prefix == RepeatWhileZeroLo) return prefix;
|
||||
if(prefix == RepeatWhileZeroHi) return prefix;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto V30MZ::segment(u16 segment) -> u16 {
|
||||
for(auto prefix : prefixes) {
|
||||
if(prefix == SegmentOverrideDS1) return DS1;
|
||||
if(prefix == SegmentOverridePS ) return PS;
|
||||
if(prefix == SegmentOverrideSS ) return SS;
|
||||
if(prefix == SegmentOverrideDS0) return DS0;
|
||||
}
|
||||
if(prefix.segment == SegmentOverrideDS1) return DS1;
|
||||
if(prefix.segment == SegmentOverridePS ) return PS;
|
||||
if(prefix.segment == SegmentOverrideSS ) return SS;
|
||||
if(prefix.segment == SegmentOverrideDS0) return DS0;
|
||||
return segment;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,13 +4,18 @@ auto V30MZ::serialize(serializer& s) -> void {
|
||||
s(state.prefix);
|
||||
|
||||
s(opcode);
|
||||
s(prefixes);
|
||||
|
||||
s(prefix.count);
|
||||
s(prefix.lock);
|
||||
s(prefix.repeat);
|
||||
s(prefix.segment);
|
||||
|
||||
s(modrm.mod);
|
||||
s(modrm.reg);
|
||||
s(modrm.mem);
|
||||
s(modrm.segment);
|
||||
s(modrm.address);
|
||||
s(modrm.useAddress);
|
||||
|
||||
s(AW);
|
||||
s(CW);
|
||||
|
||||
@@ -28,7 +28,7 @@ auto V30MZ::power() -> void {
|
||||
state.prefix = 0;
|
||||
|
||||
opcode = 0;
|
||||
prefixes.flush();
|
||||
prefixFlush();
|
||||
modrm.mod = 0;
|
||||
modrm.reg = 0;
|
||||
modrm.mem = 0;
|
||||
|
||||
@@ -39,6 +39,7 @@ struct V30MZ {
|
||||
auto power() -> void;
|
||||
|
||||
//instruction.cpp
|
||||
auto prefixFlush() -> void;
|
||||
auto interrupt(u8 vector) -> bool;
|
||||
auto nonMaskableInterrupt() -> bool;
|
||||
auto instruction() -> void;
|
||||
@@ -51,7 +52,7 @@ struct V30MZ {
|
||||
template<u32> auto setAccumulator(u32) -> void;
|
||||
|
||||
//modrm.cpp
|
||||
auto modRM() -> void;
|
||||
auto modRM(bool forceAddress = false) -> void;
|
||||
|
||||
auto getSegment() -> u16;
|
||||
auto setSegment(u16) -> void;
|
||||
@@ -242,7 +243,12 @@ struct V30MZ {
|
||||
} state;
|
||||
|
||||
u8 opcode;
|
||||
queue<u8[7]> prefixes;
|
||||
struct Prefix {
|
||||
u16 count;
|
||||
u8 lock;
|
||||
u8 repeat;
|
||||
u8 segment;
|
||||
} prefix;
|
||||
|
||||
struct ModRM {
|
||||
u2 mod;
|
||||
@@ -251,6 +257,8 @@ struct V30MZ {
|
||||
|
||||
u16 segment;
|
||||
u16 address;
|
||||
|
||||
bool useAddress;
|
||||
} modrm;
|
||||
|
||||
union { u16 AW; struct { u8 order_lsb2(AL, AH); }; }; //AX
|
||||
|
||||
@@ -235,9 +235,9 @@ struct WDC65816 {
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
//disassembler.cpp
|
||||
noinline auto disassembleInstruction(n24 address, bool e, bool m, bool x) -> string;
|
||||
noinline auto disassembleInstruction() -> string;
|
||||
noinline auto disassembleContext(maybe<bool> e = {}) -> string;
|
||||
NALL_NOINLINE auto disassembleInstruction(n24 address, bool e, bool m, bool x) -> string;
|
||||
NALL_NOINLINE auto disassembleInstruction() -> string;
|
||||
NALL_NOINLINE auto disassembleContext(maybe<bool> e = {}) -> string;
|
||||
|
||||
struct f8 {
|
||||
bool c = 0; //carry
|
||||
|
||||
@@ -211,8 +211,8 @@ struct Z80 {
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
//disassembler.cpp
|
||||
noinline auto disassembleInstruction(maybe<n16> pc = {}) -> string;
|
||||
noinline auto disassembleContext() -> string;
|
||||
NALL_NOINLINE auto disassembleInstruction(maybe<n16> pc = {}) -> string;
|
||||
NALL_NOINLINE auto disassembleContext() -> string;
|
||||
|
||||
auto disassemble(n16 pc, n8 prefix, n8 code) -> string;
|
||||
auto disassembleCB(n16 pc, n8 prefix, n8 code) -> string;
|
||||
|
||||
@@ -17,6 +17,7 @@ auto VDP::load(Node::Object parent) -> void {
|
||||
screen->setScale(1.0, 1.0);
|
||||
screen->setAspect(8.0, 7.0);
|
||||
screen->setViewport(0, 0, 284, 243);
|
||||
screen->refreshRateHint(system.colorburst(), 228, 262);
|
||||
|
||||
TMS9918::load(screen);
|
||||
}
|
||||
|
||||
+51
-82
@@ -3,12 +3,14 @@
|
||||
namespace ares::Famicom {
|
||||
|
||||
APU apu;
|
||||
#include "length.cpp"
|
||||
#include "envelope.cpp"
|
||||
#include "sweep.cpp"
|
||||
#include "pulse.cpp"
|
||||
#include "triangle.cpp"
|
||||
#include "noise.cpp"
|
||||
#include "dmc.cpp"
|
||||
#include "framecounter.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto APU::load(Node::Object parent) -> void {
|
||||
@@ -54,13 +56,20 @@ auto APU::main() -> void {
|
||||
u32 triangleOutput = triangle.clock();
|
||||
u32 noiseOutput = noise.clock();
|
||||
u32 dmcOutput = dmc.clock();
|
||||
clockFrameCounterDivider();
|
||||
frame.main();
|
||||
|
||||
s32 output = 0;
|
||||
output += pulseDAC[pulseOutput];
|
||||
output += dmcTriangleNoiseDAC[dmcOutput][triangleOutput][noiseOutput];
|
||||
|
||||
stream->frame(sclamp<16>(output) / 32768.0);
|
||||
|
||||
tick();
|
||||
}
|
||||
|
||||
auto APU::tick() -> void {
|
||||
Thread::step(rate());
|
||||
Thread::synchronize(cpu);
|
||||
}
|
||||
|
||||
auto APU::setIRQ() -> void {
|
||||
@@ -68,28 +77,28 @@ auto APU::setIRQ() -> void {
|
||||
}
|
||||
|
||||
auto APU::power(bool reset) -> void {
|
||||
pulse1 = {};
|
||||
pulse2 = {};
|
||||
triangle = {};
|
||||
noise = {};
|
||||
dmc = {};
|
||||
dmc.periodCounter = Region::PAL() ? dmcPeriodTablePAL[0] : dmcPeriodTableNTSC[0];
|
||||
frame = {};
|
||||
enabledChannels = 0;
|
||||
Thread::create(system.frequency(), {&APU::main, this});
|
||||
|
||||
pulse1.power(reset);
|
||||
pulse2.power(reset);
|
||||
triangle.power(reset);
|
||||
noise.power(reset);
|
||||
dmc.power(reset);
|
||||
frame.power(reset);
|
||||
|
||||
setIRQ();
|
||||
}
|
||||
|
||||
auto APU::readIO(n16 address) -> n8 {
|
||||
n8 data = cpu.MDR;
|
||||
n8 data = cpu.io.openBus;
|
||||
|
||||
switch(address) {
|
||||
|
||||
case 0x4015: {
|
||||
data.bit(0) = (bool)pulse1.lengthCounter;
|
||||
data.bit(1) = (bool)pulse2.lengthCounter;
|
||||
data.bit(2) = (bool)triangle.lengthCounter;
|
||||
data.bit(3) = (bool)noise.lengthCounter;
|
||||
data.bit(0) = (bool)pulse1.length.counter;
|
||||
data.bit(1) = (bool)pulse2.length.counter;
|
||||
data.bit(2) = (bool)triangle.length.counter;
|
||||
data.bit(3) = (bool)noise.length.counter;
|
||||
data.bit(4) = (bool)dmc.lengthCounter;
|
||||
data.bit(5) = 0;
|
||||
data.bit(6) = frame.irqPending;
|
||||
@@ -111,6 +120,7 @@ auto APU::writeIO(n16 address, n8 data) -> void {
|
||||
pulse1.envelope.speed = data.bit(0,3);
|
||||
pulse1.envelope.useSpeedAsVolume = data.bit(4);
|
||||
pulse1.envelope.loopMode = data.bit(5);
|
||||
pulse1.length.setHalt(frame.lengthClocking(), data.bit(5));
|
||||
pulse1.duty = data.bit(6,7);
|
||||
return;
|
||||
}
|
||||
@@ -137,9 +147,7 @@ auto APU::writeIO(n16 address, n8 data) -> void {
|
||||
pulse1.dutyCounter = 0;
|
||||
pulse1.envelope.reloadDecay = true;
|
||||
|
||||
if(enabledChannels.bit(0)) {
|
||||
pulse1.lengthCounter = lengthCounterTable[data.bit(3,7)];
|
||||
}
|
||||
pulse1.length.setCounter(frame.lengthClocking(), data.bit(3,7));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -147,6 +155,7 @@ auto APU::writeIO(n16 address, n8 data) -> void {
|
||||
pulse2.envelope.speed = data.bit(0,3);
|
||||
pulse2.envelope.useSpeedAsVolume = data.bit(4);
|
||||
pulse2.envelope.loopMode = data.bit(5);
|
||||
pulse2.length.setHalt(frame.lengthClocking(), data.bit(5));
|
||||
pulse2.duty = data.bit(6,7);
|
||||
return;
|
||||
}
|
||||
@@ -173,15 +182,13 @@ auto APU::writeIO(n16 address, n8 data) -> void {
|
||||
pulse2.dutyCounter = 0;
|
||||
pulse2.envelope.reloadDecay = true;
|
||||
|
||||
if(enabledChannels.bit(1)) {
|
||||
pulse2.lengthCounter = lengthCounterTable[data.bit(3,7)];
|
||||
}
|
||||
pulse2.length.setCounter(frame.lengthClocking(), data.bit(3,7));
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x4008: {
|
||||
triangle.linearLength = data.bit(0,6);
|
||||
triangle.haltLengthCounter = data.bit(7);
|
||||
triangle.length.setHalt(frame.lengthClocking(), data.bit(7));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -195,15 +202,14 @@ auto APU::writeIO(n16 address, n8 data) -> void {
|
||||
|
||||
triangle.reloadLinear = true;
|
||||
|
||||
if(enabledChannels.bit(2)) {
|
||||
triangle.lengthCounter = lengthCounterTable[data.bit(3,7)];
|
||||
}
|
||||
triangle.length.setCounter(frame.lengthClocking(), data.bit(3,7));
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x400c: {
|
||||
noise.envelope.speed = data.bit(0,3);
|
||||
noise.envelope.useSpeedAsVolume = data.bit(4);
|
||||
noise.length.setHalt(frame.lengthClocking(), data.bit(5));
|
||||
noise.envelope.loopMode = data.bit(5);
|
||||
return;
|
||||
}
|
||||
@@ -217,9 +223,7 @@ auto APU::writeIO(n16 address, n8 data) -> void {
|
||||
case 0x400f: {
|
||||
noise.envelope.reloadDecay = true;
|
||||
|
||||
if(enabledChannels.bit(3)) {
|
||||
noise.lengthCounter = lengthCounterTable[data.bit(3,7)];
|
||||
}
|
||||
noise.length.setCounter(frame.lengthClocking(), data.bit(3, 7));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -249,78 +253,43 @@ auto APU::writeIO(n16 address, n8 data) -> void {
|
||||
}
|
||||
|
||||
case 0x4015: {
|
||||
if(data.bit(0) == 0) pulse1.lengthCounter = 0;
|
||||
if(data.bit(1) == 0) pulse2.lengthCounter = 0;
|
||||
if(data.bit(2) == 0) triangle.lengthCounter = 0;
|
||||
if(data.bit(3) == 0) noise.lengthCounter = 0;
|
||||
if(data.bit(4) == 0) dmc.stop();
|
||||
if(data.bit(4) == 1) dmc.start();
|
||||
pulse1.length.setEnable(data.bit(0));
|
||||
pulse2.length.setEnable(data.bit(1));
|
||||
triangle.length.setEnable(data.bit(2));
|
||||
noise.length.setEnable(data.bit(3));
|
||||
if (data.bit(4) == 0) dmc.stop();
|
||||
if (data.bit(4) == 1) dmc.start();
|
||||
|
||||
dmc.irqPending = false;
|
||||
setIRQ();
|
||||
enabledChannels = data.bit(0,4);
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x4017: {
|
||||
frame.mode = data.bit(6,7);
|
||||
|
||||
frame.counter = 0;
|
||||
if(frame.mode.bit(1)) {
|
||||
clockFrameCounter();
|
||||
}
|
||||
if(frame.mode.bit(0)) {
|
||||
frame.irqPending = false;
|
||||
setIRQ();
|
||||
}
|
||||
frame.divider = FrameCounter::NtscPeriod;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x4017: return frame.write(data);
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::clockFrameCounter() -> void {
|
||||
frame.counter++;
|
||||
auto APU::clockQuarterFrame() -> void {
|
||||
pulse1.envelope.clock();
|
||||
pulse2.envelope.clock();
|
||||
triangle.clockLinearLength();
|
||||
noise.envelope.clock();
|
||||
}
|
||||
|
||||
if(frame.counter.bit(0)) {
|
||||
pulse1.clockLength();
|
||||
pulse1.sweep.clock(0);
|
||||
pulse2.clockLength();
|
||||
pulse2.sweep.clock(1);
|
||||
triangle.clockLength();
|
||||
noise.clockLength();
|
||||
}
|
||||
auto APU::clockHalfFrame() -> void {
|
||||
pulse1.length.main();
|
||||
pulse1.sweep.clock(0);
|
||||
pulse2.length.main();
|
||||
pulse2.sweep.clock(1);
|
||||
triangle.length.main();
|
||||
noise.length.main();
|
||||
|
||||
pulse1.envelope.clock();
|
||||
pulse2.envelope.clock();
|
||||
triangle.clockLinearLength();
|
||||
noise.envelope.clock();
|
||||
|
||||
if(frame.counter == 0) {
|
||||
if(frame.mode.bit(1)) {
|
||||
frame.divider += FrameCounter::NtscPeriod;
|
||||
}
|
||||
if(frame.mode == 0) {
|
||||
frame.irqPending = true;
|
||||
setIRQ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::clockFrameCounterDivider() -> void {
|
||||
frame.divider -= 2;
|
||||
if(frame.divider <= 0) {
|
||||
clockFrameCounter();
|
||||
frame.divider += FrameCounter::NtscPeriod;
|
||||
}
|
||||
}
|
||||
|
||||
const n8 APU::lengthCounterTable[32] = {
|
||||
0x0a,0xfe,0x14,0x02,0x28,0x04,0x50,0x06,0xa0,0x08,0x3c,0x0a,0x0e,0x0c,0x1a,0x0e,
|
||||
0x0c,0x10,0x18,0x12,0x30,0x14,0x60,0x16,0xc0,0x18,0x48,0x1a,0x10,0x1c,0x20,0x1e,
|
||||
};
|
||||
|
||||
const n16 APU::noisePeriodTableNTSC[16] = {
|
||||
4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068,
|
||||
};
|
||||
|
||||
+89
-24
@@ -1,4 +1,4 @@
|
||||
struct APU {
|
||||
struct APU : Thread {
|
||||
Node::Object node;
|
||||
Node::Audio::Stream stream;
|
||||
|
||||
@@ -9,6 +9,7 @@ struct APU {
|
||||
auto unload() -> void;
|
||||
|
||||
auto main() -> void;
|
||||
auto tick() -> void;
|
||||
auto setIRQ() -> void;
|
||||
|
||||
auto power(bool reset) -> void;
|
||||
@@ -19,6 +20,37 @@ struct APU {
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
struct Length {
|
||||
//length.cpp
|
||||
auto main() -> void;
|
||||
|
||||
auto power(bool reset, bool isTriangle) -> void;
|
||||
|
||||
auto setEnable(n1 value) -> void;
|
||||
auto setHalt(bool lengthClocking, n1 value) -> void;
|
||||
auto setCounter(bool lengthClocking, n5 index) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
n8 counter;
|
||||
n1 halt;
|
||||
n1 enable;
|
||||
|
||||
bool delayHalt;
|
||||
n1 newHalt;
|
||||
|
||||
bool delayCounter;
|
||||
n5 counterIndex;
|
||||
|
||||
constexpr static u8 table[32] = {
|
||||
0x0a, 0xfe, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06,
|
||||
0xa0, 0x08, 0x3c, 0x0a, 0x0e, 0x0c, 0x1a, 0x0e,
|
||||
0x0c, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16,
|
||||
0xc0, 0x18, 0x48, 0x1a, 0x10, 0x1c, 0x20, 0x1e,
|
||||
};
|
||||
};
|
||||
|
||||
struct Envelope {
|
||||
//envelope.cpp
|
||||
auto volume() const -> u32;
|
||||
@@ -54,18 +86,18 @@ struct APU {
|
||||
|
||||
struct Pulse {
|
||||
//pulse.cpp
|
||||
auto clockLength() -> void;
|
||||
auto checkPeriod() -> bool;
|
||||
auto clock() -> n8;
|
||||
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
Length length;
|
||||
Envelope envelope;
|
||||
Sweep sweep;
|
||||
|
||||
n16 lengthCounter;
|
||||
n16 periodCounter = 1;
|
||||
n16 periodCounter;
|
||||
n2 duty;
|
||||
n3 dutyCounter;
|
||||
n11 period;
|
||||
@@ -73,38 +105,40 @@ struct APU {
|
||||
|
||||
struct Triangle {
|
||||
//triangle.cpp
|
||||
auto clockLength() -> void;
|
||||
auto clockLinearLength() -> void;
|
||||
auto clock() -> n8;
|
||||
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
n16 lengthCounter;
|
||||
n16 periodCounter = 1;
|
||||
Length length;
|
||||
|
||||
n16 periodCounter;
|
||||
n8 linearLength;
|
||||
n1 haltLengthCounter;
|
||||
n11 period;
|
||||
n5 stepCounter = 16;
|
||||
n5 stepCounter;
|
||||
n8 linearLengthCounter;
|
||||
n1 reloadLinear;
|
||||
} triangle;
|
||||
|
||||
struct Noise {
|
||||
//noise.cpp
|
||||
auto clockLength() -> void;
|
||||
auto clock() -> n8;
|
||||
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
Length length;
|
||||
Envelope envelope;
|
||||
|
||||
n16 lengthCounter;
|
||||
n16 periodCounter = 1;
|
||||
n16 periodCounter;
|
||||
n4 period;
|
||||
n1 shortMode;
|
||||
n15 lfsr = 1;
|
||||
n15 lfsr;
|
||||
} noise;
|
||||
|
||||
struct DMC {
|
||||
@@ -113,12 +147,15 @@ struct APU {
|
||||
auto stop() -> void;
|
||||
auto clock() -> n8;
|
||||
|
||||
auto power(bool reset) -> void;
|
||||
auto setDMABuffer(n8 data) -> void;
|
||||
auto dmaAddress() -> n16 const { return 0x8000 | readAddress; }
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
n16 lengthCounter;
|
||||
n16 periodCounter;
|
||||
n16 dmaDelayCounter;
|
||||
n1 irqPending;
|
||||
n4 period;
|
||||
n1 irqEnable;
|
||||
@@ -132,31 +169,59 @@ struct APU {
|
||||
n8 dmaBuffer;
|
||||
n1 sampleValid;
|
||||
n8 sample;
|
||||
n8 dmaDelayCounter;
|
||||
} dmc;
|
||||
|
||||
struct FrameCounter {
|
||||
static constexpr u16 NtscPeriod = 14915; //~(21.477MHz / 6 / 240hz)
|
||||
enum mode: u8 { Freq60Hz = 0, Freq48Hz = 1 };
|
||||
|
||||
auto getPeriod() const -> u16 {
|
||||
return Region::PAL() ? periodPAL[mode][step] : periodNTSC[mode][step];
|
||||
}
|
||||
auto lengthClocking() const -> bool {
|
||||
// Because the CPU writes before the APU clock,
|
||||
// the counter must be 1
|
||||
return counter == 1 && (step == 1 || step == 4);
|
||||
}
|
||||
|
||||
auto main() -> void;
|
||||
auto power(bool reset) -> void;
|
||||
auto write(n8 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
n1 irqInhibit;
|
||||
n1 mode;
|
||||
|
||||
n1 irqPending;
|
||||
n2 mode;
|
||||
n2 counter;
|
||||
i32 divider = 1;
|
||||
} frame;
|
||||
n3 step;
|
||||
n16 counter;
|
||||
|
||||
bool odd;
|
||||
bool delay;
|
||||
n3 delayCounter;
|
||||
|
||||
constexpr static u16 periodNTSC[2][6] = {
|
||||
{ 7457, 7456, 7458, 7457, 1, 1, },
|
||||
{ 7457, 7456, 7458, 7458, 7452, 1, },
|
||||
};
|
||||
constexpr static u16 periodPAL[2][6] = {
|
||||
{ 8313, 8314, 8312, 8313, 1, 1, },
|
||||
{ 8313, 8314, 8312, 8320, 8312, 1,},
|
||||
};
|
||||
};
|
||||
|
||||
//apu.cpp
|
||||
auto clockFrameCounter() -> void;
|
||||
auto clockFrameCounterDivider() -> void;
|
||||
auto clockQuarterFrame() -> void;
|
||||
auto clockHalfFrame() -> void;
|
||||
|
||||
n5 enabledChannels;
|
||||
FrameCounter frame;
|
||||
|
||||
//unserialized:
|
||||
u16 pulseDAC[32];
|
||||
u16 dmcTriangleNoiseDAC[128][16][16];
|
||||
|
||||
static const n8 lengthCounterTable[32];
|
||||
static const n16 dmcPeriodTableNTSC[16];
|
||||
static const n16 dmcPeriodTablePAL[16];
|
||||
static const n16 noisePeriodTableNTSC[16];
|
||||
|
||||
+43
-32
@@ -2,44 +2,19 @@ auto APU::DMC::start() -> void {
|
||||
if(lengthCounter == 0) {
|
||||
readAddress = 0x4000 + (addressLatch << 6);
|
||||
lengthCounter = (lengthLatch << 4) + 1;
|
||||
|
||||
if (!dmaBufferValid)
|
||||
dmaDelayCounter = periodCounter & 1 ? 2 : 3;
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::DMC::stop() -> void {
|
||||
lengthCounter = 0;
|
||||
dmaDelayCounter = 0;
|
||||
cpu.rdyLine(1);
|
||||
cpu.rdyAddress(false);
|
||||
}
|
||||
|
||||
auto APU::DMC::clock() -> n8 {
|
||||
n8 result = dacLatch;
|
||||
|
||||
if(dmaDelayCounter > 0) {
|
||||
dmaDelayCounter--;
|
||||
|
||||
if(dmaDelayCounter == 1) {
|
||||
cpu.rdyAddress(true, 0x8000 | readAddress);
|
||||
} else if(dmaDelayCounter == 0) {
|
||||
cpu.rdyLine(1);
|
||||
cpu.rdyAddress(false);
|
||||
|
||||
dmaBuffer = cpu.MDR;
|
||||
dmaBufferValid = true;
|
||||
lengthCounter--;
|
||||
readAddress++;
|
||||
|
||||
if(lengthCounter == 0) {
|
||||
if(loopMode) {
|
||||
start();
|
||||
} else if(irqEnable) {
|
||||
irqPending = true;
|
||||
apu.setIRQ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(--periodCounter == 0) {
|
||||
if(sampleValid) {
|
||||
s32 delta = (((sample >> bitCounter) & 1) << 2) - 2;
|
||||
@@ -52,6 +27,9 @@ auto APU::DMC::clock() -> n8 {
|
||||
sampleValid = true;
|
||||
sample = dmaBuffer;
|
||||
dmaBufferValid = false;
|
||||
|
||||
if (lengthCounter > 0)
|
||||
cpu.dmcDMAPending();
|
||||
} else {
|
||||
sampleValid = false;
|
||||
}
|
||||
@@ -60,10 +38,43 @@ auto APU::DMC::clock() -> n8 {
|
||||
periodCounter = Region::PAL() ? dmcPeriodTablePAL[period] : dmcPeriodTableNTSC[period];
|
||||
}
|
||||
|
||||
if(lengthCounter > 0 && !dmaBufferValid && dmaDelayCounter == 0) {
|
||||
cpu.rdyLine(0);
|
||||
dmaDelayCounter = 4;
|
||||
}
|
||||
if (dmaDelayCounter > 0 && --dmaDelayCounter == 0)
|
||||
cpu.dmcDMAPending();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
auto APU::DMC::power(bool reset) -> void {
|
||||
lengthCounter = 0;
|
||||
periodCounter = Region::PAL() ? dmcPeriodTablePAL[0] : dmcPeriodTableNTSC[0];
|
||||
irqPending = false;
|
||||
period = 0;
|
||||
irqEnable = 0;
|
||||
loopMode = 0;
|
||||
dacLatch = 0;
|
||||
addressLatch = 0;
|
||||
lengthLatch = 0;
|
||||
readAddress = 0;
|
||||
bitCounter = 0;
|
||||
dmaBufferValid = 0;
|
||||
dmaBuffer = 0;
|
||||
sampleValid = 0;
|
||||
sample = 0;
|
||||
}
|
||||
|
||||
auto APU::DMC::setDMABuffer(n8 data) -> void {
|
||||
dmaBuffer = data;
|
||||
dmaBufferValid = true;
|
||||
lengthCounter--;
|
||||
readAddress++;
|
||||
|
||||
if (lengthCounter == 0) {
|
||||
if (loopMode) {
|
||||
readAddress = 0x4000 + (addressLatch << 6);
|
||||
lengthCounter = (lengthLatch << 4) + 1;
|
||||
} else if (irqEnable) {
|
||||
irqPending = true;
|
||||
apu.setIRQ();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
auto APU::FrameCounter::main() -> void {
|
||||
--counter;
|
||||
odd = !odd;
|
||||
|
||||
if (delay && --delayCounter == 0) {
|
||||
delay = false;
|
||||
step = 0;
|
||||
counter = getPeriod() + 2;
|
||||
if (mode == Freq48Hz) {
|
||||
apu.clockHalfFrame();
|
||||
}
|
||||
}
|
||||
|
||||
if (counter != 0)
|
||||
return;
|
||||
|
||||
switch(step) {
|
||||
case 0:
|
||||
step = 1;
|
||||
apu.clockQuarterFrame();
|
||||
break;
|
||||
case 1:
|
||||
step = 2;
|
||||
apu.clockHalfFrame();
|
||||
break;
|
||||
case 2:
|
||||
step = 3;
|
||||
apu.clockQuarterFrame();
|
||||
break;
|
||||
case 3:
|
||||
step = 4;
|
||||
if (mode == Freq60Hz && irqInhibit == 0)
|
||||
irqPending = 1;
|
||||
break;
|
||||
case 4:
|
||||
step = 5;
|
||||
apu.clockHalfFrame();
|
||||
if (mode == Freq60Hz && irqInhibit == 0) {
|
||||
irqPending = 1;
|
||||
apu.setIRQ();
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
step = 0;
|
||||
if (mode == Freq60Hz && irqInhibit == 0)
|
||||
irqPending = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
counter = getPeriod();
|
||||
}
|
||||
|
||||
auto APU::FrameCounter::power(bool reset) -> void {
|
||||
irqInhibit = 0;
|
||||
if (!reset) mode = Freq60Hz;
|
||||
|
||||
irqPending = 0;
|
||||
step = 0;
|
||||
counter = getPeriod();
|
||||
|
||||
odd = true;
|
||||
delay = false;
|
||||
delayCounter = 0;
|
||||
}
|
||||
|
||||
auto APU::FrameCounter::write(n8 data) -> void {
|
||||
mode = data.bit(7);
|
||||
delay = true;
|
||||
|
||||
// If the write occurs during an APU cycle,
|
||||
// the effects occur 3 CPU cycles after the
|
||||
// $4017 write cycle, and if the write occurs
|
||||
// between APU cycles, the effects occurs 4 CPU
|
||||
// cycles after the write cycle.
|
||||
delayCounter = odd ? 1 : 2;
|
||||
|
||||
irqInhibit = data.bit(6);
|
||||
if (irqInhibit) {
|
||||
irqPending = 0;
|
||||
apu.setIRQ();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
auto APU::Length::main() -> void {
|
||||
if (halt == 0 && counter)
|
||||
counter--;
|
||||
|
||||
if (delayHalt) {
|
||||
delayHalt = false;
|
||||
halt = newHalt;
|
||||
}
|
||||
|
||||
if (delayCounter) {
|
||||
delayCounter = false;
|
||||
if (counter == 0)
|
||||
counter = table[counterIndex];
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Length::power(bool reset, bool isTriangle) -> void {
|
||||
enable = 0;
|
||||
|
||||
if (!reset || !isTriangle) {
|
||||
counter = 0;
|
||||
halt = 0;
|
||||
|
||||
delayHalt = false;
|
||||
newHalt = 0;
|
||||
|
||||
delayCounter = false;
|
||||
counterIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Length::setEnable(n1 value) -> void {
|
||||
enable = value;
|
||||
if (!enable)
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
auto APU::Length::setHalt(bool lengthClocking, n1 value) -> void {
|
||||
if (!lengthClocking) {
|
||||
halt = value;
|
||||
} else {
|
||||
delayHalt = true;
|
||||
newHalt = value;
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Length::setCounter(bool lengthClocking, n5 index) -> void {
|
||||
if (enable) {
|
||||
if (!lengthClocking)
|
||||
counter = table[index];
|
||||
else {
|
||||
delayCounter = true;
|
||||
counterIndex = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
-7
@@ -1,11 +1,5 @@
|
||||
auto APU::Noise::clockLength() -> void {
|
||||
if(envelope.loopMode == 0) {
|
||||
if(lengthCounter > 0) lengthCounter--;
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Noise::clock() -> n8 {
|
||||
if(lengthCounter == 0) return 0;
|
||||
if(length.counter == 0) return 0;
|
||||
|
||||
n8 result = (lfsr & 1) ? envelope.volume() : 0;
|
||||
|
||||
@@ -24,3 +18,13 @@ auto APU::Noise::clock() -> n8 {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
auto APU::Noise::power(bool reset) -> void {
|
||||
length.power(reset, false);
|
||||
envelope = {};
|
||||
|
||||
periodCounter = 1;
|
||||
period = 0;
|
||||
shortMode = 0;
|
||||
lfsr = 1;
|
||||
}
|
||||
+12
-7
@@ -1,12 +1,6 @@
|
||||
auto APU::Pulse::clockLength() -> void {
|
||||
if(envelope.loopMode == 0) {
|
||||
if(lengthCounter) lengthCounter--;
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Pulse::clock() -> n8 {
|
||||
if(!sweep.checkPeriod()) return 0;
|
||||
if(lengthCounter == 0) return 0;
|
||||
if(length.counter == 0) return 0;
|
||||
|
||||
static constexpr u32 dutyTable[4][8] = {
|
||||
{0, 0, 0, 0, 0, 0, 0, 1}, //12.5%
|
||||
@@ -24,3 +18,14 @@ auto APU::Pulse::clock() -> n8 {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
auto APU::Pulse::power(bool reset) -> void {
|
||||
length.power(reset, false);
|
||||
envelope = {};
|
||||
sweep = {};
|
||||
|
||||
periodCounter = 1;
|
||||
duty = 0;
|
||||
dutyCounter = 0;
|
||||
period = 0;
|
||||
}
|
||||
@@ -1,10 +1,22 @@
|
||||
auto APU::serialize(serializer& s) -> void {
|
||||
Thread::serialize(s);
|
||||
s(pulse1);
|
||||
s(pulse2);
|
||||
s(triangle);
|
||||
s(dmc);
|
||||
s(frame);
|
||||
s(enabledChannels);
|
||||
}
|
||||
|
||||
auto APU::Length::serialize(serializer& s) -> void {
|
||||
s(counter);
|
||||
s(halt);
|
||||
s(enable);
|
||||
|
||||
s(delayHalt);
|
||||
s(newHalt);
|
||||
|
||||
s(delayCounter);
|
||||
s(counterIndex);
|
||||
}
|
||||
|
||||
auto APU::Envelope::serialize(serializer& s) -> void {
|
||||
@@ -29,7 +41,7 @@ auto APU::Sweep::serialize(serializer& s) -> void {
|
||||
auto APU::Pulse::serialize(serializer& s) -> void {
|
||||
s(envelope);
|
||||
s(sweep);
|
||||
s(lengthCounter);
|
||||
s(length);
|
||||
s(periodCounter);
|
||||
s(duty);
|
||||
s(dutyCounter);
|
||||
@@ -37,10 +49,9 @@ auto APU::Pulse::serialize(serializer& s) -> void {
|
||||
}
|
||||
|
||||
auto APU::Triangle::serialize(serializer& s) -> void {
|
||||
s(lengthCounter);
|
||||
s(length);
|
||||
s(periodCounter);
|
||||
s(linearLength);
|
||||
s(haltLengthCounter);
|
||||
s(period);
|
||||
s(stepCounter);
|
||||
s(linearLengthCounter);
|
||||
@@ -49,7 +60,7 @@ auto APU::Triangle::serialize(serializer& s) -> void {
|
||||
|
||||
auto APU::Noise::serialize(serializer& s) -> void {
|
||||
s(envelope);
|
||||
s(lengthCounter);
|
||||
s(length);
|
||||
s(periodCounter);
|
||||
s(period);
|
||||
s(shortMode);
|
||||
@@ -59,7 +70,6 @@ auto APU::Noise::serialize(serializer& s) -> void {
|
||||
auto APU::DMC::serialize(serializer& s) -> void {
|
||||
s(lengthCounter);
|
||||
s(periodCounter);
|
||||
s(dmaDelayCounter);
|
||||
s(irqPending);
|
||||
s(period);
|
||||
s(irqEnable);
|
||||
@@ -73,11 +83,18 @@ auto APU::DMC::serialize(serializer& s) -> void {
|
||||
s(dmaBuffer);
|
||||
s(sampleValid);
|
||||
s(sample);
|
||||
s(dmaDelayCounter);
|
||||
}
|
||||
|
||||
auto APU::FrameCounter::serialize(serializer& s) -> void {
|
||||
s(irqPending);
|
||||
s(irqInhibit);
|
||||
s(mode);
|
||||
|
||||
s(irqPending);
|
||||
s(step);
|
||||
s(counter);
|
||||
s(divider);
|
||||
|
||||
s(odd);
|
||||
s(delay);
|
||||
s(delayCounter);
|
||||
}
|
||||
@@ -1,9 +1,3 @@
|
||||
auto APU::Triangle::clockLength() -> void {
|
||||
if(haltLengthCounter == 0) {
|
||||
if(lengthCounter > 0) lengthCounter--;
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Triangle::clockLinearLength() -> void {
|
||||
if(reloadLinear) {
|
||||
linearLengthCounter = linearLength;
|
||||
@@ -11,13 +5,13 @@ auto APU::Triangle::clockLinearLength() -> void {
|
||||
linearLengthCounter--;
|
||||
}
|
||||
|
||||
if(haltLengthCounter == 0) reloadLinear = false;
|
||||
if(length.halt == 0) reloadLinear = false;
|
||||
}
|
||||
|
||||
auto APU::Triangle::clock() -> n8 {
|
||||
n8 result = stepCounter & 0x0f;
|
||||
if((stepCounter & 0x10) == 0) result ^= 0x0f;
|
||||
if(lengthCounter == 0 || linearLengthCounter == 0) return result;
|
||||
if(length.counter == 0 || linearLengthCounter == 0) return result;
|
||||
|
||||
if(--periodCounter == 0) {
|
||||
stepCounter++;
|
||||
@@ -26,3 +20,14 @@ auto APU::Triangle::clock() -> n8 {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
auto APU::Triangle::power(bool reset) -> void {
|
||||
length.power(reset, true);
|
||||
|
||||
periodCounter = 1;
|
||||
linearLength = 0;
|
||||
period = 0;
|
||||
stepCounter = 16;
|
||||
linearLengthCounter = 0;
|
||||
reloadLinear = 0;
|
||||
}
|
||||
@@ -19,6 +19,7 @@ struct BandaiFCG : Interface {
|
||||
irqEnable = false;
|
||||
}
|
||||
}
|
||||
tick();
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
|
||||
@@ -37,6 +37,7 @@ struct BandaiLZ93D50 : Interface {
|
||||
irqEnable = false;
|
||||
}
|
||||
}
|
||||
tick();
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace Board {
|
||||
#include "bandai-karaoke.cpp"
|
||||
#include "bandai-lz93d50.cpp"
|
||||
#include "bandai-oeka.cpp"
|
||||
#include "camerica-bf909x.cpp"
|
||||
#include "colordreams-74x377.cpp"
|
||||
#include "gtrom.cpp"
|
||||
#include "jaleco-jf05.cpp"
|
||||
@@ -54,8 +55,14 @@ namespace Board {
|
||||
#include "taito-tc0690.cpp"
|
||||
#include "taito-x1-005.cpp"
|
||||
#include "taito-x1-017.cpp"
|
||||
#include "tengen-80003x.cpp"
|
||||
#include "unrom-512.cpp"
|
||||
|
||||
#include "unl-bmc.cpp"
|
||||
#include "unl-sachen-74ls374n.cpp"
|
||||
#include "unl-sachen-sa0037.cpp"
|
||||
#include "unl-txc.cpp"
|
||||
|
||||
auto Interface::create(string board) -> Interface* {
|
||||
Interface* p = nullptr;
|
||||
if(!p) p = Action53::create(board);
|
||||
@@ -66,6 +73,7 @@ auto Interface::create(string board) -> Interface* {
|
||||
if(!p) p = BandaiKaraoke::create(board);
|
||||
if(!p) p = BandaiLZ93D50::create(board);
|
||||
if(!p) p = BandaiOeka::create(board);
|
||||
if(!p) p = Camerica_BF909x::create(board);
|
||||
if(!p) p = ColorDreams_74x377::create(board);
|
||||
if(!p) p = GTROM::create(board);
|
||||
if(!p) p = HVC_AxROM::create(board);
|
||||
@@ -112,11 +120,28 @@ auto Interface::create(string board) -> Interface* {
|
||||
if(!p) p = TaitoTC0690::create(board);
|
||||
if(!p) p = TaitoX1005::create(board);
|
||||
if(!p) p = TaitoX1017::create(board);
|
||||
if(!p) p = Tengen80003x::create(board);
|
||||
if(!p) p = UNROM512::create(board);
|
||||
|
||||
if(!p) p = BMC::create(board);
|
||||
if(!p) p = Sachen74LS374N::create(board);
|
||||
if(!p) p = SachenSA0037::create(board);
|
||||
if(!p) p = TXC::create(board);
|
||||
|
||||
if(!p) p = new Interface;
|
||||
return p;
|
||||
}
|
||||
|
||||
auto Interface::main() -> void {
|
||||
cartridge.step(cartridge.rate() * 4095);
|
||||
tick();
|
||||
}
|
||||
|
||||
auto Interface::tick() -> void {
|
||||
cartridge.step(cartridge.rate());
|
||||
cartridge.synchronize(cpu);
|
||||
}
|
||||
|
||||
auto Interface::load(Memory::Readable<n8>& memory, string name) -> bool {
|
||||
if(auto fp = pak->read(name)) {
|
||||
memory.allocate(fp->size(), 0xff);
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace Board {
|
||||
#include "ic/jv001.hpp"
|
||||
#include "ic/txc05-00002-010.hpp"
|
||||
|
||||
struct Interface {
|
||||
VFS::Pak pak;
|
||||
@@ -11,7 +13,8 @@ struct Interface {
|
||||
virtual auto save() -> void {}
|
||||
virtual auto unload() -> void {}
|
||||
|
||||
virtual auto main() -> void {}
|
||||
virtual auto main() -> void;
|
||||
virtual auto tick() -> void;
|
||||
|
||||
virtual auto readPRG(n32 address, n8 data) -> n8 { return data; }
|
||||
virtual auto writePRG(n32 address, n8 data) -> void {}
|
||||
@@ -23,6 +26,8 @@ struct Interface {
|
||||
|
||||
virtual auto power() -> void {}
|
||||
|
||||
virtual auto ppuAddressBus(n14 address) -> void {}
|
||||
|
||||
virtual auto serialize(serializer&) -> void {}
|
||||
|
||||
protected:
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
struct Camerica_BF909x : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "CAMERICA-BF9093" ) return new Camerica_BF909x(Revision::CAMERICA_BF9093);
|
||||
if(id == "CAMERICA-BF9096" ) return new Camerica_BF909x(Revision::CAMERICA_BF9096);
|
||||
if(id == "CAMERICA-BF9096A") return new Camerica_BF909x(Revision::CAMERICA_BF9096A);
|
||||
if(id == "CAMERICA-BF9097" ) return new Camerica_BF909x(Revision::CAMERICA_BF9097);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Memory::Readable<n8> programROM;
|
||||
Memory::Writable<n8> characterRAM;
|
||||
|
||||
enum class Revision : u32 {
|
||||
CAMERICA_BF9093,
|
||||
CAMERICA_BF9096,
|
||||
CAMERICA_BF9096A,
|
||||
CAMERICA_BF9097,
|
||||
} revision;
|
||||
|
||||
Camerica_BF909x(Revision revision) : revision(revision) {}
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(programROM, "program.rom");
|
||||
Interface::load(characterRAM, "character.ram");
|
||||
mirror = pak->attribute("mirror") == "vertical";
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
Interface::save(characterRAM, "character.ram");
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
if(address < 0x8000) return data;
|
||||
n4 bank;
|
||||
if(address < 0xC000) {
|
||||
bank = programBank;
|
||||
} else {
|
||||
bank = 0x0f;
|
||||
}
|
||||
if(revision == Revision::CAMERICA_BF9096 || revision == Revision::CAMERICA_BF9096A) {
|
||||
bank.bit(2,3) = programOuterBank;
|
||||
}
|
||||
return programROM.read(bank << 14 | (n14)address);
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address < 0x8000) return;
|
||||
if(address < 0xC000) {
|
||||
if(revision == Revision::CAMERICA_BF9096) {
|
||||
programOuterBank = data.bit(3,4);
|
||||
} else if(revision == Revision::CAMERICA_BF9096A) {
|
||||
programOuterBank.bit(0) = data.bit(4);
|
||||
programOuterBank.bit(1) = data.bit(3);
|
||||
} else if(address < 0xA000 && revision == Revision::CAMERICA_BF9097) {
|
||||
characterBank = data.bit(4);
|
||||
}
|
||||
} else {
|
||||
programBank = data.bit(0,3);
|
||||
}
|
||||
}
|
||||
|
||||
auto addressCIRAM(n32 address) const -> n32 {
|
||||
if(revision == Revision::CAMERICA_BF9097) {
|
||||
return characterBank << 10 | (n10)address;
|
||||
} else {
|
||||
return address >> !mirror & 0x0400 | (n10)address;
|
||||
}
|
||||
}
|
||||
|
||||
auto readCHR(n32 address, n8 data) -> n8 override {
|
||||
if(address & 0x2000) {
|
||||
return ppu.readCIRAM(addressCIRAM(address));
|
||||
}
|
||||
return characterRAM.read((n13)address);
|
||||
}
|
||||
|
||||
auto writeCHR(n32 address, n8 data) -> void override {
|
||||
if(address & 0x2000) {
|
||||
return ppu.writeCIRAM(addressCIRAM(address), data);
|
||||
}
|
||||
return characterRAM.write((n13)address, data);
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(characterRAM);
|
||||
s(mirror);
|
||||
s(programBank);
|
||||
s(programOuterBank);
|
||||
s(characterBank);
|
||||
}
|
||||
|
||||
n1 mirror; //0 = horizontal, 1 = vertical
|
||||
n4 programBank;
|
||||
n2 programOuterBank;
|
||||
n1 characterBank;
|
||||
};
|
||||
@@ -7,6 +7,7 @@ struct HVC_CNROM : Interface {
|
||||
}
|
||||
|
||||
Memory::Readable<n8> programROM;
|
||||
Memory::Writable<n8> programRAM;
|
||||
Memory::Readable<n8> characterROM;
|
||||
Memory::Writable<n8> characterRAM;
|
||||
|
||||
@@ -20,6 +21,7 @@ struct HVC_CNROM : Interface {
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(programROM, "program.rom");
|
||||
Interface::load(programRAM, "save.ram");
|
||||
Interface::load(characterROM, "character.rom");
|
||||
Interface::load(characterRAM, "character.ram");
|
||||
mirror = pak->attribute("mirror") == "vertical";
|
||||
@@ -27,16 +29,21 @@ struct HVC_CNROM : Interface {
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
Interface::save(programRAM, "save.ram");
|
||||
Interface::save(characterRAM, "character.ram");
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
if(address < 0x8000) return data;
|
||||
if(address < 0x6000) return data;
|
||||
if(address < 0x8000 && !programRAM) return data;
|
||||
if(address < 0x8000) return programRAM.read(address);
|
||||
return programROM.read((n15)address);
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address < 0x8000) return;
|
||||
if(address < 0x6000) return;
|
||||
if(address < 0x8000 && !programRAM) return;
|
||||
if(address < 0x8000) return programRAM.write(address, data);
|
||||
characterBank = data & programROM.read((n15)address);
|
||||
characterEnable = (data == key);
|
||||
}
|
||||
@@ -70,6 +77,7 @@ struct HVC_CNROM : Interface {
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(programRAM);
|
||||
s(characterRAM);
|
||||
s(mirror);
|
||||
s(key);
|
||||
|
||||
@@ -163,7 +163,7 @@ struct HVC_ExROM : Interface { //MMC5
|
||||
pulse1.envelope.clock();
|
||||
pulse2.clockLength();
|
||||
pulse2.envelope.clock();
|
||||
frameDivider += APU::FrameCounter::NtscPeriod;
|
||||
frameDivider += 14915; // ~(21.477MHz / 6 / 240hz)
|
||||
}
|
||||
|
||||
i32 output = 0;
|
||||
@@ -172,6 +172,8 @@ struct HVC_ExROM : Interface { //MMC5
|
||||
stream->frame(sclamp<16>(-output) / 32768.0);
|
||||
|
||||
cpu.irqLine((irqLine & irqEnable) || (pcm.irqLine & pcm.irqEnable) || timerLine);
|
||||
|
||||
tick();
|
||||
}
|
||||
|
||||
auto scanline() -> void {
|
||||
@@ -354,7 +356,7 @@ struct HVC_ExROM : Interface { //MMC5
|
||||
pulse1.period.bit(8,10) = data.bit(0,2);
|
||||
pulse1.dutyCounter = 0;
|
||||
pulse1.envelope.reloadDecay = 1;
|
||||
pulse1.lengthCounter = apu.lengthCounterTable[data.bit(3,7)];
|
||||
pulse1.lengthCounter = APU::Length::table[data.bit(3,7)];
|
||||
break;
|
||||
|
||||
case 0x5004:
|
||||
@@ -375,7 +377,7 @@ struct HVC_ExROM : Interface { //MMC5
|
||||
pulse2.period.bit(8,10) = data.bit(0,2);
|
||||
pulse2.dutyCounter = 0;
|
||||
pulse2.envelope.reloadDecay = 1;
|
||||
pulse2.lengthCounter = apu.lengthCounterTable[data.bit(3,7)];
|
||||
pulse2.lengthCounter = APU::Length::table[data.bit(3,7)];
|
||||
break;
|
||||
|
||||
case 0x5010:
|
||||
|
||||
@@ -22,6 +22,7 @@ struct HVC_FMR : Interface {
|
||||
|
||||
auto main() -> void override {
|
||||
fds.main();
|
||||
tick();
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
|
||||
@@ -24,6 +24,7 @@ struct HVC_HKROM : Interface { //MMC6
|
||||
auto main() -> void override {
|
||||
if(irqDelay) irqDelay--;
|
||||
cpu.irqLine(irqLine);
|
||||
tick();
|
||||
}
|
||||
|
||||
auto irqTest(n32 address) -> void {
|
||||
|
||||
@@ -59,6 +59,7 @@ struct HVC_NROM : Interface {
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(programRAM);
|
||||
s(characterRAM);
|
||||
s(mirror);
|
||||
}
|
||||
|
||||
@@ -90,6 +90,7 @@ struct HVC_SxROM : Interface { //MMC1
|
||||
|
||||
auto main() -> void override {
|
||||
if(writeDelay) writeDelay--;
|
||||
tick();
|
||||
}
|
||||
|
||||
auto addressProgramROM(n32 address) -> n32 {
|
||||
|
||||
@@ -17,6 +17,7 @@ struct HVC_TxROM : Interface { //MMC3
|
||||
if(id == "HVC-TVROM" ) return new HVC_TxROM(Revision::TVROM);
|
||||
if(id == "NES-QJ" ) return new HVC_TxROM(Revision::NESQJ);
|
||||
if(id == "PAL-ZZ" ) return new HVC_TxROM(Revision::PALZZ);
|
||||
if(id == "MC-ACC" ) return new HVC_TxROM(Revision::MCACC);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -43,6 +44,7 @@ struct HVC_TxROM : Interface { //MMC3
|
||||
TVROM,
|
||||
NESQJ,
|
||||
PALZZ,
|
||||
MCACC,
|
||||
} revision;
|
||||
|
||||
HVC_TxROM(Revision revision) : revision(revision) {}
|
||||
@@ -62,21 +64,7 @@ struct HVC_TxROM : Interface { //MMC3
|
||||
auto main() -> void override {
|
||||
if(irqDelay) irqDelay--;
|
||||
cpu.irqLine(irqLine);
|
||||
}
|
||||
|
||||
auto irqTest(n16 address) -> void {
|
||||
if(!(characterAddress & 0x1000) && (address & 0x1000)) {
|
||||
if(irqDelay == 0) {
|
||||
if(irqCounter == 0) {
|
||||
irqCounter = irqLatch + 1;
|
||||
}
|
||||
if(--irqCounter == 0) {
|
||||
if(irqEnable) irqLine = 1;
|
||||
}
|
||||
}
|
||||
irqDelay = 6;
|
||||
}
|
||||
characterAddress = address;
|
||||
tick();
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
@@ -107,7 +95,6 @@ struct HVC_TxROM : Interface { //MMC3
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address == 0x2006 && ++characterLatch) irqTest(data << 8);
|
||||
if(address < 0x6000) return;
|
||||
|
||||
if(address < 0x8000) {
|
||||
@@ -148,6 +135,7 @@ struct HVC_TxROM : Interface { //MMC3
|
||||
break;
|
||||
case 0xc001:
|
||||
irqCounter = 0;
|
||||
mcAccPulseCounter = 0;
|
||||
break;
|
||||
case 0xe000:
|
||||
irqEnable = 0;
|
||||
@@ -196,7 +184,6 @@ struct HVC_TxROM : Interface { //MMC3
|
||||
}
|
||||
|
||||
auto readCHR(n32 address, n8 data) -> n8 override {
|
||||
irqTest(address);
|
||||
if(revision == Revision::TVROM) {
|
||||
if(address < 0x2000) return characterROM.read(addressCHR(address));
|
||||
address.bit(12) = 0;
|
||||
@@ -222,7 +209,6 @@ struct HVC_TxROM : Interface { //MMC3
|
||||
}
|
||||
|
||||
auto writeCHR(n32 address, n8 data) -> void override {
|
||||
irqTest(address);
|
||||
if(revision == Revision::TVROM) {
|
||||
if(address < 0x2000) return;
|
||||
address.bit(12) = 0;
|
||||
@@ -244,6 +230,29 @@ struct HVC_TxROM : Interface { //MMC3
|
||||
ramWritable = 1;
|
||||
}
|
||||
|
||||
auto ppuAddressBus(n14 address) -> void override {
|
||||
if (!(characterAddress & 0x1000) && (address & 0x1000)) {
|
||||
bool clocking = false;
|
||||
|
||||
if (revision == Revision::MCACC) {
|
||||
clocking = ++mcAccPulseCounter == 1;
|
||||
} else {
|
||||
clocking = irqDelay == 0;
|
||||
irqDelay = 6;
|
||||
}
|
||||
|
||||
if (clocking) {
|
||||
if (irqCounter == 0)
|
||||
irqCounter = irqLatch + 1;
|
||||
|
||||
if (--irqCounter == 0) {
|
||||
if (irqEnable) irqLine = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
characterAddress = address;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(programRAM);
|
||||
s(characterRAM);
|
||||
@@ -263,6 +272,8 @@ struct HVC_TxROM : Interface { //MMC3
|
||||
s(irqLine);
|
||||
s(characterLatch);
|
||||
s(characterAddress);
|
||||
if (revision == Revision::MCACC)
|
||||
s(mcAccPulseCounter);
|
||||
}
|
||||
|
||||
n1 characterMode;
|
||||
@@ -281,4 +292,5 @@ struct HVC_TxROM : Interface { //MMC3
|
||||
n1 irqLine;
|
||||
n1 characterLatch;
|
||||
n16 characterAddress;
|
||||
n3 mcAccPulseCounter;
|
||||
};
|
||||
@@ -8,6 +8,7 @@ struct HVC_UxROM : Interface {
|
||||
}
|
||||
|
||||
Memory::Readable<n8> programROM;
|
||||
Memory::Writable<n8> programRAM;
|
||||
Memory::Readable<n8> characterROM;
|
||||
Memory::Writable<n8> characterRAM;
|
||||
|
||||
@@ -22,17 +23,21 @@ struct HVC_UxROM : Interface {
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(programROM, "program.rom");
|
||||
Interface::load(programRAM, "save.ram");
|
||||
Interface::load(characterROM, "character.rom");
|
||||
Interface::load(characterRAM, "character.ram");
|
||||
mirror = pak->attribute("mirror") == "vertical";
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
Interface::save(programRAM, "save.ram");
|
||||
Interface::save(characterRAM, "character.ram");
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
if(address < 0x8000) return data;
|
||||
if(address < 0x6000) return data;
|
||||
if(address < 0x8000 && !programRAM) return data;
|
||||
if(address < 0x8000) return programRAM.read(address);
|
||||
n8 bank;
|
||||
switch(address >> 14 & 1) {
|
||||
case 0: bank = (revision == Revision::UNROMA ? (n8)0x00 : programBank); break;
|
||||
@@ -42,7 +47,9 @@ struct HVC_UxROM : Interface {
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address < 0x8000) return;
|
||||
if(address < 0x6000) return;
|
||||
if(address < 0x8000 && !programRAM) return;
|
||||
if(address < 0x8000) return programRAM.write(address, data);
|
||||
programBank = data;
|
||||
if(revision == Revision::UN1ROM) programBank >>= 2;
|
||||
}
|
||||
@@ -69,6 +76,7 @@ struct HVC_UxROM : Interface {
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(programRAM);
|
||||
s(characterRAM);
|
||||
s(mirror);
|
||||
s(programBank);
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
// JV001: 136/172 136 172
|
||||
// .--\/--. | .--\/--. | .--\/--.
|
||||
// PPU A13 -> |01 28| -- GND | PPU A13 -> |01 28| -- GND | PPU A13 -> |01 28| -- GND
|
||||
// CPU D5 <> |02 27| <- PPU R/W | CPU D5 <> |02 27| <- NC | CPU D0 <> |02 27| <- PPU R/W
|
||||
// CPU D4 <> |03 26| -> Invert | CPU D4 <> |03 26| -> NC | CPU D1 <> |03 26| -> CIRAM A10
|
||||
// CPU D3 <> |04 25| <- PPU A11 | CPU D3 <> |04 25| <- NC | CPU D2 <> |04 25| <- PPU A11
|
||||
// CPU D2 <> |05 24| <- PPU A10 | CPU D2 <> |05 24| <- NC | CPU D3 <> |05 24| <- PPU A10
|
||||
// CPU D1 <> |06 23| -> O0 | CPU D1 <> |06 23| -> NC | CPU D4 <> |06 23| -> CHR A13
|
||||
// CPU D0 <> |07 22| -> O1 | CPU D0 <> |07 22| -> NC | CPU D5 <> |07 22| -> CHR A14
|
||||
// CPU A0 -> |08 21| -> O2 | CPU A0 -> |08 21| -> NC | CPU A0 -> |08 21| -> NC
|
||||
// CPU A1 -> |09 20| -> O3 | CPU A1 -> |09 20| -> NC | CPU A1 -> |09 20| -> NC
|
||||
// CPU A8 -> |10 19| -> O4 | CPU A8 -> |10 19| -> NC | CPU A8 -> |10 19| -> NC
|
||||
// M2 -> |11 18| -> O5 | M2 -> |11 18| -> NC | M2 -> |11 18| -> NC
|
||||
// /ROMSEL -> |12 17| <- CHR /OE | /ROMSEL -> |12 17| <- CHR /OE | /ROMSEL -> |12 17| <- CHR /OE
|
||||
// CPU R/W -> |13 16| <- CPU A13 | CPU R/W -> |13 16| <- NC | CPU R/W -> |13 16| <- CPU A13
|
||||
// 5V -- |14 15| <- CPU A14 | 5V -- |14 15| <- NC | 5V -- |14 15| <- CPU A14
|
||||
// '------' | '------' | '------'
|
||||
struct JV001 {
|
||||
auto writePRG(n16 address, n8 data) -> void {
|
||||
address &= 0xe103;
|
||||
switch (address) {
|
||||
case 0x4100: if (increment) {
|
||||
++reg.bit(0,3);
|
||||
} else {
|
||||
reg.bit(0,3) = in ^ (invert ? 0x0f : 0);
|
||||
}
|
||||
return;
|
||||
case 0x4101: invert = data.bit(0);
|
||||
return;
|
||||
case 0x4102: reg.bit(4,5) = data.bit(4,5);
|
||||
in = data.bit(0,3);
|
||||
return;
|
||||
case 0x4103: increment = data.bit(0);
|
||||
return;
|
||||
default: out.bit(0,3) = reg.bit(0,3);
|
||||
out.bit(4,5) = reg.bit(4,5) ^ (invert ? 0x03 : 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void {
|
||||
s(reg);
|
||||
s(invert);
|
||||
s(in);
|
||||
s(increment);
|
||||
s(out);
|
||||
}
|
||||
|
||||
n6 reg;
|
||||
n1 invert;
|
||||
n4 in;
|
||||
n1 increment;
|
||||
n6 out;
|
||||
};
|
||||
@@ -0,0 +1,91 @@
|
||||
#pragma once
|
||||
// TXC 05-00002-010
|
||||
// .--\/--.
|
||||
// Q2 <- |01 24| -> Q3
|
||||
// Q1 <- |02 23| -> Q4
|
||||
// Q0 <- |03 22| -> o3
|
||||
// i1 -> |04 21| <- CPU A13 (rn)
|
||||
// i0 -> |05 20| <- CPU A14 (rn)
|
||||
// io2 <> |06 19| -- GND
|
||||
// 5V -- |07 18| <- CPU R/W (n)
|
||||
// D5 <> |08 17| <- /ROMSEL (rn)
|
||||
// D4 <> |09 16| <- M2 (n)
|
||||
// D2 <> |10 15| <- CPU A8 (rn)
|
||||
// D1 <> |11 14| <- CPU A1 (rn)
|
||||
// D0 <> |12 13| <- CPU A0 (rn)
|
||||
// '------'
|
||||
// 36 132 173
|
||||
// .--\/--. .--\/--. .--\/--.
|
||||
// NC <- |01 24| -> NC (r) PRG A15 <- |01 24| -> NC NC <- |01 24| -> NC
|
||||
// (r) PRG A16 <- |02 23| -> NC (r) CHR A14 <- |02 23| -> NC *(r) CHR A15 <- |02 23| -> NC
|
||||
// (r) PRG A15 <- |03 22| -> NC (r) CHR A13 <- |03 22| -> NC (r) CHR A13 <- |03 22| -> CHR A14
|
||||
// GND -> |04 21| <- CPU A13 (rn) GND -> |04 21| <- CPU A13 (fr) GND -> |04 21| <- CPU A13 (fr)
|
||||
// 5V -> |05 20| <- CPU A14 (rn) 5V -> |05 20| <- CPU A14 (fr) 5V -> |05 20| <- CPU A14 (fr)
|
||||
// NC <> |06 19| -- GND NC <> |06 19| -- GND NC <> |06 19| <- GND
|
||||
// 5V -- |07 18| <- CPU R/W (n) 5V -- |07 18| <- CPU R/W (f) 5V -- |07 18| <- CPU R/W (f)
|
||||
// NC <> |08 17| <- /ROMSEL (rn) GND <> |08 17| <- /ROMSEL (fr) GND <> |08 17| <- /ROMSEL (fr)
|
||||
// NC <> |09 16| <- M2 (n) (fr) CPU D3 <> |09 16| <- M2 (f) (fr) CPU D3 <> |09 16| <- M2 (f)
|
||||
// NC <> |10 15| <- CPU A8 (rn) (fr) CPU D2 <> |10 15| <- CPU A8 (fr) (fr) CPU D2 <> |10 15| <- CPU A8 (fr)
|
||||
// (rn) CPU D5 <> |11 14| <- CPU A1 (rn) (fr) CPU D1 <> |11 14| <- CPU A1 (fr) (fr) CPU D1 <> |11 14| <- CPU A1 (fr)
|
||||
// (rn) CPU D4 <> |12 13| <- CPU A0 (rn) (fr) CPU D0 <> |12 13| <- CPU A0 (fr) (fr) CPU D0 <> |12 13| <- CPU A0 (fr)
|
||||
// '------'` '------' '------'
|
||||
// 173*: TXC pin 2 was routed to CHR ROM pin 1 on a 28-pin package.
|
||||
// On a UVEPROM, this is A15.
|
||||
// However, no games were ever released using more than 32 KiB of CHR.
|
||||
// TXC 05-00002-010: 36/132/173
|
||||
struct TXC05_00002_010 {
|
||||
auto writePRG(n16 address, n8 data) -> void {
|
||||
address &= 0xe103;
|
||||
switch (address) {
|
||||
case 0x4100: if (increment) {
|
||||
++reg.bit(0,3);
|
||||
} else {
|
||||
reg.bit(0,3) = in ^ (invert ? 0x0f : 0);
|
||||
}
|
||||
return;
|
||||
case 0x4101: invert = data.bit(0);
|
||||
if (overdown) {
|
||||
o3 = (invert ? io2 : i0) | data.bit(5);
|
||||
} else {
|
||||
io2 = invert ? i1 : i0;
|
||||
o3 = io2 | data.bit(5);
|
||||
}
|
||||
return;
|
||||
case 0x4102: reg.bit(4,5) = data.bit(4,5);
|
||||
in.bit(0,2) = data.bit(0,2);
|
||||
in.bit(3) = in.bit(3) ^ invert;
|
||||
return;
|
||||
case 0x4103: increment = data.bit(0);
|
||||
return;
|
||||
default: out.bit(0,3) = reg.bit(0,3);
|
||||
out.bit(4) = reg.bit(4) ^ invert;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void {
|
||||
s(i0);
|
||||
s(i1);
|
||||
s(io2);
|
||||
s(o3);
|
||||
|
||||
s(increment);
|
||||
s(invert);
|
||||
s(overdown);
|
||||
s(reg);
|
||||
s(in);
|
||||
s(out);
|
||||
}
|
||||
|
||||
bool overdown; // io2 is using input or output flag
|
||||
|
||||
// 4 pin
|
||||
n1 i0, i1, io2, o3;
|
||||
|
||||
n1 increment;
|
||||
n1 invert;
|
||||
n6 reg;
|
||||
n4 in;
|
||||
n5 out;
|
||||
};
|
||||
|
||||
@@ -17,6 +17,7 @@ struct IremH3001 : Interface {
|
||||
if(irqCounter && --irqCounter == 0) irqLine = 1;
|
||||
}
|
||||
cpu.irqLine(irqLine);
|
||||
tick();
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
|
||||
@@ -39,6 +39,7 @@ struct JalecoJF23 : Interface {
|
||||
}
|
||||
}
|
||||
cpu.irqLine(irqLine);
|
||||
tick();
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
|
||||
@@ -38,6 +38,7 @@ struct KonamiVRC3 : Interface {
|
||||
}
|
||||
}
|
||||
cpu.irqLine(irqLine);
|
||||
tick();
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
|
||||
@@ -47,6 +47,7 @@ struct KonamiVRC4 : Interface {
|
||||
}
|
||||
}
|
||||
cpu.irqLine(irqLine);
|
||||
tick();
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
|
||||
@@ -38,6 +38,7 @@ struct KonamiVRC5 : Interface {
|
||||
cpu.irqLine(1);
|
||||
}
|
||||
}
|
||||
tick();
|
||||
}
|
||||
|
||||
//converts JIS X 0208 codepoint to CIRAM tile# + QTRAM bank#
|
||||
|
||||
@@ -134,6 +134,8 @@ struct KonamiVRC6 : Interface {
|
||||
sawtooth.clock();
|
||||
f64 output = (pulse1.output + pulse2.output + sawtooth.output) / 61.0 * 0.5;
|
||||
stream->frame(-output);
|
||||
|
||||
tick();
|
||||
}
|
||||
|
||||
auto addressPRG(n32 address) const -> n32 {
|
||||
|
||||
@@ -66,6 +66,8 @@ struct KonamiVRC7 : Interface {
|
||||
if(!disableFM) sample = ym2413.clock();
|
||||
stream->frame(sample);
|
||||
}
|
||||
|
||||
tick();
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
|
||||
@@ -41,6 +41,8 @@ struct Namco163 : Interface {
|
||||
double output = clockSound();
|
||||
stream->frame(output / 255.0 * 0.5);
|
||||
}
|
||||
|
||||
tick();
|
||||
}
|
||||
|
||||
auto clockSound() -> double {
|
||||
|
||||
@@ -65,6 +65,7 @@ struct Sunsoft1 : Interface {
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(characterRAM);
|
||||
s(characterBank);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ struct Sunsoft2 : Interface {
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(characterRAM);
|
||||
s(programBank);
|
||||
s(characterBank);
|
||||
s(mirror);
|
||||
|
||||
@@ -26,6 +26,7 @@ struct Sunsoft3 : Interface {
|
||||
irqEnable = 0;
|
||||
cpu.irqLine(1);
|
||||
}
|
||||
tick();
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
@@ -104,6 +105,8 @@ struct Sunsoft3 : Interface {
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(programRAM);
|
||||
s(characterRAM);
|
||||
s(programBank);
|
||||
s(characterBank);
|
||||
s(mirror);
|
||||
|
||||
Loaded 100 of 2165 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user