mirror of
https://github.com/ares-emulator/ares.git
synced 2026-11-09 01:09:46 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
7c4a36a482 | ||
|
|
e1a940cafa | ||
|
|
db1d29ebcf | ||
|
|
bea295a915 | ||
|
|
54845f6194 | ||
|
|
7c7d2c2155 | ||
|
|
ac75269342 | ||
|
|
edb88e2e3a | ||
|
|
c6ca36e447 | ||
|
|
bfc9636403 | ||
|
|
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 | ||
|
|
ba31eafda9 | ||
|
|
7eb0fb268b | ||
|
|
1c876d8bf6 | ||
|
|
40a461621d | ||
|
|
61b358e45b | ||
|
|
ad70fadc8e | ||
|
|
1bd36fb2cc | ||
|
|
f7d6650c26 | ||
|
|
69daac5356 | ||
|
|
5440ef5a4f | ||
|
|
ae89968e2c | ||
|
|
571832a83d | ||
|
|
a04c215003 | ||
|
|
7be6217102 | ||
|
|
a29cd2ce58 | ||
|
|
d5d3d169ca | ||
|
|
9ae2e43c68 | ||
|
|
5815c9d746 | ||
|
|
3e49cebfa4 | ||
|
|
0c8eaf0d4e | ||
|
|
f0fd88f33e | ||
|
|
827babcbbc | ||
|
|
4666cdb9b8 | ||
|
|
c9c1a0d532 | ||
|
|
d53fdb0087 | ||
|
|
c564dc7bc5 | ||
|
|
0111abc565 | ||
|
|
65264090b0 | ||
|
|
70f361094b | ||
|
|
be8fa76e7d | ||
|
|
66dbed4ee0 | ||
|
|
05c749f09b | ||
|
|
b4399c59cc | ||
|
|
37145465f7 | ||
|
|
8b88e3093e | ||
|
|
33a00d4da0 | ||
|
|
71302c0938 | ||
|
|
b702465cc1 | ||
|
|
b822fb3224 | ||
|
|
cc850c7dc1 | ||
|
|
f18c1f9c67 | ||
|
|
ff98ad031c |
No files matched your search
+51
-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,28 +90,49 @@ 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: |
|
||||
export PATH="/c/Users/runneradmin/.cargo/bin:$PATH" # correct on windows-latest as of 2024-02-19
|
||||
if [[ ${{ matrix.platform.name }} == *-arm64 ]]; then
|
||||
rustup toolchain install nightly
|
||||
rustup default nightly
|
||||
rustup target add aarch64-pc-windows-msvc
|
||||
pushd thirdparty/librashader
|
||||
./build-librashader.sh aarch64-pc-windows-msvc
|
||||
popd
|
||||
else
|
||||
rustup toolchain install nightly
|
||||
rustup default nightly
|
||||
pushd thirdparty/librashader
|
||||
./build-librashader.sh
|
||||
popd
|
||||
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
|
||||
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
|
||||
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
|
||||
rustup default nightly
|
||||
rustup target add x86_64-apple-darwin
|
||||
rustup target add aarch64-apple-darwin
|
||||
echo "MAKE=gmake" >> $GITHUB_ENV
|
||||
pushd thirdparty/SDL
|
||||
./build-SDL.sh
|
||||
@@ -115,6 +140,9 @@ jobs:
|
||||
pushd thirdparty/MoltenVK
|
||||
./build-moltenvk.sh
|
||||
popd
|
||||
pushd thirdparty/librashader
|
||||
./build-librashader.sh
|
||||
popd
|
||||
- name: Set up MSVC environment
|
||||
if: matrix.platform.msvc-arch != ''
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
@@ -153,7 +181,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/*
|
||||
@@ -165,11 +193,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
|
||||
@@ -185,9 +213,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}"
|
||||
@@ -287,10 +316,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 }}' }
|
||||
|
||||
+7
-1
@@ -1,5 +1,6 @@
|
||||
*.moc
|
||||
*.user
|
||||
*.xcuserdata
|
||||
.vs/
|
||||
.vscode/
|
||||
.idea/
|
||||
@@ -9,4 +10,9 @@ out/
|
||||
obj-amd64/
|
||||
obj-arm64/
|
||||
out-amd64/
|
||||
out-arm64/
|
||||
out-arm64/
|
||||
thirdparty/SDL/SDL
|
||||
thirdparty/SDL/libSDL2-2.0.0.dylib
|
||||
macos-xcode/
|
||||
.swiftpm
|
||||
*.xcodeproj
|
||||
@@ -470,3 +470,362 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
----------------------------------------------------------------------
|
||||
|
||||
----------------------------------------------------------------------
|
||||
librashader
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
### 1. Definitions
|
||||
|
||||
**1.1. “Contributor”**
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
**1.2. “Contributor Version”**
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
**1.3. “Contribution”**
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
**1.4. “Covered Software”**
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
**1.5. “Incompatible With Secondary Licenses”**
|
||||
means
|
||||
|
||||
* **(a)** that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
* **(b)** that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
**1.6. “Executable Form”**
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
**1.7. “Larger Work”**
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
**1.8. “License”**
|
||||
means this document.
|
||||
|
||||
**1.9. “Licensable”**
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
**1.10. “Modifications”**
|
||||
means any of the following:
|
||||
|
||||
* **(a)** any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
* **(b)** any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
**1.11. “Patent Claims” of a Contributor**
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
**1.12. “Secondary License”**
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
**1.13. “Source Code Form”**
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
**1.14. “You” (or “Your”)**
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, “You” includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, “control” means **(a)** the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or **(b)** ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
|
||||
### 2. License Grants and Conditions
|
||||
|
||||
#### 2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
* **(a)** under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
* **(b)** under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
#### 2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
#### 2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
* **(a)** for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
* **(b)** for infringements caused by: **(i)** Your and any other third party's
|
||||
modifications of Covered Software, or **(ii)** the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
* **(c)** under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
#### 2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
#### 2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
#### 2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
#### 2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
|
||||
### 3. Responsibilities
|
||||
|
||||
#### 3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
#### 3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
* **(a)** such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
* **(b)** You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
#### 3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
#### 3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
#### 3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
|
||||
### 4. Inability to Comply Due to Statute or Regulation
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: **(a)** comply with
|
||||
the terms of this License to the maximum extent possible; and **(b)**
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
|
||||
### 5. Termination
|
||||
|
||||
**5.1.** The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated **(a)** provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and **(b)** on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
**5.2.** If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
**5.3.** In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
|
||||
### 6. Disclaimer of Warranty
|
||||
|
||||
> Covered Software is provided under this License on an “as is”
|
||||
> basis, without warranty of any kind, either expressed, implied, or
|
||||
> statutory, including, without limitation, warranties that the
|
||||
> Covered Software is free of defects, merchantable, fit for a
|
||||
> particular purpose or non-infringing. The entire risk as to the
|
||||
> quality and performance of the Covered Software is with You.
|
||||
> Should any Covered Software prove defective in any respect, You
|
||||
> (not any Contributor) assume the cost of any necessary servicing,
|
||||
> repair, or correction. This disclaimer of warranty constitutes an
|
||||
> essential part of this License. No use of any Covered Software is
|
||||
> authorized under this License except under this disclaimer.
|
||||
|
||||
### 7. Limitation of Liability
|
||||
|
||||
> Under no circumstances and under no legal theory, whether tort
|
||||
> (including negligence), contract, or otherwise, shall any
|
||||
> Contributor, or anyone who distributes Covered Software as
|
||||
> permitted above, be liable to You for any direct, indirect,
|
||||
> special, incidental, or consequential damages of any character
|
||||
> including, without limitation, damages for lost profits, loss of
|
||||
> goodwill, work stoppage, computer failure or malfunction, or any
|
||||
> and all other commercial damages or losses, even if such party
|
||||
> shall have been informed of the possibility of such damages. This
|
||||
> limitation of liability shall not apply to liability for death or
|
||||
> personal injury resulting from such party's negligence to the
|
||||
> extent applicable law prohibits such limitation. Some
|
||||
> jurisdictions do not allow the exclusion or limitation of
|
||||
> incidental or consequential damages, so this exclusion and
|
||||
> limitation may not apply to You.
|
||||
|
||||
|
||||
### 8. Litigation
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
|
||||
### 9. Miscellaneous
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
|
||||
### 10. Versions of the License
|
||||
|
||||
#### 10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
#### 10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
#### 10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
#### 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
## Exhibit A - Source Code Form License Notice
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
## Exhibit B - “Incompatible With Secondary Licenses” Notice
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
@@ -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.
|
||||
|
||||
--------------
|
||||
|
||||
@@ -63,6 +76,32 @@ Once complete, open a CLANG64 terminal window and proceed with building ares.
|
||||
###### Debug Symbols
|
||||
When building with clang, by default symbols will be generated for debug builds using an MSVC compatible format (CodeView) for use with Windows debugging tools. In order to generate GDB compatible symbols, specify the following option: `symformat=gdb`
|
||||
|
||||
###### Librashader Support
|
||||
ares has introduced support for the [librashader](https://github.com/SnowflakePowered/librashader) library, which is a preprocessor, compiler, and runtime for RetroArch 'slang' shaders. If you are not interested in working with librashaders, you can pass the following option to the `make` command to skip these requirements using: `librashader=false`
|
||||
|
||||
Building the librashader library on Windows requires Rust. Follow these steps to prepare your system and build the librashader library:
|
||||
|
||||
1. Download and execute the rustup-init.exe installer from: https://rustup.rs/
|
||||
2. Open a new Windows terminal and run the following commands:
|
||||
```
|
||||
rustup toolchain install nightly
|
||||
rustup default nightly
|
||||
```
|
||||
3. With Rust successfully installed, you will need to update your PATH environment variable within your MSYS2 environment. In an MSYS2 shell, open $HOME/.bash_profile and append something like the following (note you will need to replace both instances of %USER_NAME% with the real user name you used to install Rust):
|
||||
```
|
||||
if [ -d "/C/Users/%USER_NAME%/.cargo/bin" ] ; then
|
||||
PATH="/C/Users/%USER_NAME%/.cargo/bin:${PATH}"
|
||||
fi
|
||||
```
|
||||
4. Open a new MSYS2 based shell for the compiler you are using (MinGW64|CLANG64|UCRT64), execute `rustup` to validate the path is set properly within your MSYS2 environment.
|
||||
5. In your MSYS2 compiler shell, `cd` into the `ares/thirdparty/librashader` directory
|
||||
6. Execute the build script, build should complete successfully:
|
||||
```
|
||||
./build-librashader.sh
|
||||
```
|
||||
|
||||
This only needs to be done once, or anytime the librashader library is updated. Once built, ares will build with librashader support by default requiring no additional flags.
|
||||
|
||||
Compilation
|
||||
-----------
|
||||
|
||||
@@ -97,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
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
#version 150
|
||||
|
||||
//anti-aliased nearest-neighbor
|
||||
|
||||
precision highp float;
|
||||
|
||||
uniform sampler2D source[];
|
||||
uniform vec4 sourceSize[];
|
||||
uniform vec4 targetSize[];
|
||||
|
||||
in Vertex {
|
||||
vec2 texCoord;
|
||||
};
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
vec4 vpow(vec4 n, float e) {
|
||||
return vec4(pow(n.x, e), pow(n.y, e), pow(n.z, e), pow(n.w, e));
|
||||
}
|
||||
|
||||
vec4 toLQV(vec3 c) {
|
||||
return vec4(c.r, c.g, c.b, c.r * 0.2989 + c.g * 0.5870 + c.b * 0.1140);
|
||||
}
|
||||
|
||||
vec3 fromLQV(vec4 c) {
|
||||
float f = c.w / (c.r * 0.2989 + c.g * 0.5870 + c.b * 0.1140);
|
||||
return vec3(c.rgb) * f;
|
||||
}
|
||||
|
||||
vec3 percent(float ssize, float tsize, float coord) {
|
||||
float minfull = (coord * tsize - 0.5) / tsize * ssize;
|
||||
float maxfull = (coord * tsize + 0.5) / tsize * ssize;
|
||||
float realfull = floor(maxfull);
|
||||
|
||||
if(minfull > realfull) {
|
||||
return vec3(
|
||||
1,
|
||||
(realfull + 0.5) / ssize,
|
||||
(realfull + 0.5) / ssize
|
||||
);
|
||||
}
|
||||
|
||||
return vec3(
|
||||
(maxfull - realfull) / (maxfull - minfull),
|
||||
(realfull - 0.5) / ssize,
|
||||
(realfull + 0.5) / ssize
|
||||
);
|
||||
}
|
||||
|
||||
void main() {
|
||||
float srgb = 2.1;
|
||||
float gamma = 3.0;
|
||||
|
||||
vec3 x = percent(sourceSize[0].x, targetSize[0].x, texCoord.x);
|
||||
vec3 y = percent(sourceSize[0].y, targetSize[0].y, texCoord.y);
|
||||
|
||||
//get points to interpolate across in linear RGB
|
||||
vec4 a = toLQV(vpow(texture(source[0], vec2(x[1], y[1])), srgb).rgb);
|
||||
vec4 b = toLQV(vpow(texture(source[0], vec2(x[2], y[1])), srgb).rgb);
|
||||
vec4 c = toLQV(vpow(texture(source[0], vec2(x[1], y[2])), srgb).rgb);
|
||||
vec4 d = toLQV(vpow(texture(source[0], vec2(x[2], y[2])), srgb).rgb);
|
||||
|
||||
//use perceptual gamma for luminance component
|
||||
a.w = pow(a.w, 1 / gamma);
|
||||
b.w = pow(b.w, 1 / gamma);
|
||||
c.w = pow(c.w, 1 / gamma);
|
||||
d.w = pow(d.w, 1 / gamma);
|
||||
|
||||
//interpolate
|
||||
vec4 gammaLQV =
|
||||
(1.0 - x[0]) * (1.0 - y[0]) * a +
|
||||
(0.0 + x[0]) * (1.0 - y[0]) * b +
|
||||
(1.0 - x[0]) * (0.0 + y[0]) * c +
|
||||
(0.0 + x[0]) * (0.0 + y[0]) * d;
|
||||
|
||||
//convert luminance gamma back to linear
|
||||
gammaLQV.w = pow(gammaLQV.w, gamma);
|
||||
|
||||
//convert color back to sRGB
|
||||
fragColor = vpow(vec4(fromLQV(gammaLQV), 1), 1 / srgb);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
input
|
||||
filter: nearest
|
||||
|
||||
program
|
||||
fragment: AANN.fs
|
||||
|
||||
output
|
||||
filter: nearest
|
||||
@@ -1,10 +0,0 @@
|
||||
input
|
||||
filter: nearest
|
||||
|
||||
program
|
||||
filter: nearest
|
||||
wrap: border
|
||||
fragment: shadertoy.fs
|
||||
|
||||
output
|
||||
filter: nearest
|
||||
@@ -1,145 +0,0 @@
|
||||
//
|
||||
// PUBLIC DOMAIN CRT STYLED SCAN-LINE SHADER
|
||||
//
|
||||
// by Timothy Lottes
|
||||
//
|
||||
// This is more along the style of a really good CGA arcade monitor.
|
||||
// With RGB inputs instead of NTSC.
|
||||
// The shadow mask example has the mask rotated 90 degrees for less chromatic aberration.
|
||||
//
|
||||
// Left it unoptimized to show the theory behind the algorithm.
|
||||
//
|
||||
// It is an example what I personally would want as a display option for pixel art games.
|
||||
// Please take and use, change, or whatever.
|
||||
//
|
||||
|
||||
#version 150
|
||||
|
||||
uniform sampler2D source[];
|
||||
uniform vec4 sourceSize[];
|
||||
uniform int phase;
|
||||
|
||||
in Vertex {
|
||||
vec2 texCoord;
|
||||
};
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
// Emulated input resolution.
|
||||
vec2 res=sourceSize[0].xy;
|
||||
|
||||
// Hardness of scanline.
|
||||
// -8.0 = soft
|
||||
// -16.0 = medium
|
||||
float hardScan=-8.0;
|
||||
|
||||
// Hardness of pixels in scanline.
|
||||
// -2.0 = soft
|
||||
// -4.0 = hard
|
||||
float hardPix=-3.0;
|
||||
|
||||
// Display warp.
|
||||
// 0.0 = none
|
||||
// 1.0/8.0 = extreme
|
||||
vec2 warp=vec2(1.0/32.0,1.0/24.0);
|
||||
|
||||
// Amount of shadow mask.
|
||||
float maskDark=0.5;
|
||||
float maskLight=1.5;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
// sRGB to Linear.
|
||||
// Assuing using sRGB typed textures this should not be needed.
|
||||
float ToLinear1(float c){return(c<=0.04045)?c/12.92:pow((c+0.055)/1.055,2.4);}
|
||||
vec3 ToLinear(vec3 c){return vec3(ToLinear1(c.r),ToLinear1(c.g),ToLinear1(c.b));}
|
||||
|
||||
// Linear to sRGB.
|
||||
// Assuing using sRGB typed textures this should not be needed.
|
||||
float ToSrgb1(float c){return(c<0.0031308?c*12.92:1.055*pow(c,0.41666)-0.055);}
|
||||
vec3 ToSrgb(vec3 c){return vec3(ToSrgb1(c.r),ToSrgb1(c.g),ToSrgb1(c.b));}
|
||||
|
||||
// Nearest emulated sample given floating point position and texel offset.
|
||||
// Also zero's off screen.
|
||||
vec3 Fetch(vec2 pos,vec2 off){
|
||||
pos=(floor(pos*res+off)+vec2(0.5,0.5))/res;
|
||||
return ToLinear(1.2 * texture(source[0],pos.xy,-16.0).rgb);}
|
||||
|
||||
// Distance in emulated pixels to nearest texel.
|
||||
vec2 Dist(vec2 pos){pos=pos*res;return -((pos-floor(pos))-vec2(0.5));}
|
||||
|
||||
// 1D Gaussian.
|
||||
float Gaus(float pos,float scale){return exp2(scale*pos*pos);}
|
||||
|
||||
// 3-tap Gaussian filter along horz line.
|
||||
vec3 Horz3(vec2 pos,float off){
|
||||
vec3 b=Fetch(pos,vec2(-1.0,off));
|
||||
vec3 c=Fetch(pos,vec2( 0.0,off));
|
||||
vec3 d=Fetch(pos,vec2( 1.0,off));
|
||||
float dst=Dist(pos).x;
|
||||
// Convert distance to weight.
|
||||
float scale=hardPix;
|
||||
float wb=Gaus(dst-1.0,scale);
|
||||
float wc=Gaus(dst+0.0,scale);
|
||||
float wd=Gaus(dst+1.0,scale);
|
||||
// Return filtered sample.
|
||||
return (b*wb+c*wc+d*wd)/(wb+wc+wd);}
|
||||
|
||||
// 5-tap Gaussian filter along horz line.
|
||||
vec3 Horz5(vec2 pos,float off){
|
||||
vec3 a=Fetch(pos,vec2(-2.0,off));
|
||||
vec3 b=Fetch(pos,vec2(-1.0,off));
|
||||
vec3 c=Fetch(pos,vec2( 0.0,off));
|
||||
vec3 d=Fetch(pos,vec2( 1.0,off));
|
||||
vec3 e=Fetch(pos,vec2( 2.0,off));
|
||||
float dst=Dist(pos).x;
|
||||
// Convert distance to weight.
|
||||
float scale=hardPix;
|
||||
float wa=Gaus(dst-2.0,scale);
|
||||
float wb=Gaus(dst-1.0,scale);
|
||||
float wc=Gaus(dst+0.0,scale);
|
||||
float wd=Gaus(dst+1.0,scale);
|
||||
float we=Gaus(dst+2.0,scale);
|
||||
// Return filtered sample.
|
||||
return (a*wa+b*wb+c*wc+d*wd+e*we)/(wa+wb+wc+wd+we);}
|
||||
|
||||
// Return scanline weight.
|
||||
float Scan(vec2 pos,float off){
|
||||
float dst=Dist(pos).y;
|
||||
return Gaus(dst+off,hardScan);}
|
||||
|
||||
// Allow nearest three lines to effect pixel.
|
||||
vec3 Tri(vec2 pos){
|
||||
vec3 a=Horz3(pos,-1.0);
|
||||
vec3 b=Horz5(pos, 0.0);
|
||||
vec3 c=Horz3(pos, 1.0);
|
||||
float wa=Scan(pos,-1.0);
|
||||
float wb=Scan(pos, 0.0);
|
||||
float wc=Scan(pos, 1.0);
|
||||
return a*wa+b*wb+c*wc;}
|
||||
|
||||
// Distortion of scanlines, and end of screen alpha.
|
||||
vec2 Warp(vec2 pos){
|
||||
pos=pos*2.0-1.0;
|
||||
pos*=vec2(1.0+(pos.y*pos.y)*warp.x,1.0+(pos.x*pos.x)*warp.y);
|
||||
return pos*0.5+0.5;}
|
||||
|
||||
// Shadow mask.
|
||||
vec3 Mask(vec2 pos){
|
||||
pos.x+=pos.y*3.0;
|
||||
vec3 mask=vec3(maskDark,maskDark,maskDark);
|
||||
pos.x=fract(pos.x/6.0);
|
||||
if(pos.x<0.333)mask.r=maskLight;
|
||||
else if(pos.x<0.666)mask.g=maskLight;
|
||||
else mask.b=maskLight;
|
||||
return mask;}
|
||||
|
||||
void main() {
|
||||
vec2 pos = texCoord.xy * 1.0001;
|
||||
hardScan=-12.0;
|
||||
// maskDark=maskLight;
|
||||
pos=Warp(pos.xy);
|
||||
fragColor.rgb=Tri(pos)*Mask(gl_FragCoord.xy);
|
||||
fragColor.a=1.0;
|
||||
fragColor = vec4(ToSrgb(fragColor.rgb), fragColor.a);
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
#version 150
|
||||
|
||||
in vec4 position;
|
||||
in vec2 texCoord;
|
||||
|
||||
out Vertex {
|
||||
vec2 vTexCoord;
|
||||
};
|
||||
|
||||
uniform vec4 targetSize;
|
||||
uniform vec4 sourceSize[];
|
||||
|
||||
void main() {
|
||||
gl_Position = position;
|
||||
vTexCoord = texCoord;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
input
|
||||
filter: nearest
|
||||
|
||||
program
|
||||
filter: nearest
|
||||
vertex: lottes.vs
|
||||
fragment: lottes.fs
|
||||
|
||||
output
|
||||
height: 0
|
||||
width: 0
|
||||
filter: linear
|
||||
@@ -1,21 +0,0 @@
|
||||
#version 150
|
||||
#define distortion 0.2
|
||||
|
||||
uniform sampler2D source[];
|
||||
uniform vec4 sourceSize[];
|
||||
|
||||
in Vertex {
|
||||
vec2 texCoord;
|
||||
};
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
vec2 radialDistortion(vec2 coord) {
|
||||
vec2 cc = coord - vec2(0.5);
|
||||
float dist = dot(cc, cc) * distortion;
|
||||
return coord + cc * (1.0 - dist) * dist;
|
||||
}
|
||||
|
||||
void main() {
|
||||
fragColor = texture(source[0], radialDistortion(texCoord));
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
program
|
||||
filter: linear
|
||||
wrap: border
|
||||
fragment: curvature.fs
|
||||
@@ -1,25 +0,0 @@
|
||||
#version 150
|
||||
|
||||
uniform sampler2D source[];
|
||||
uniform vec4 sourceSize[];
|
||||
|
||||
in Vertex {
|
||||
vec2 texCoord;
|
||||
};
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
vec3 grayscale(vec3 color) {
|
||||
return vec3(dot(color, vec3(0.3, 0.59, 0.11)));
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 offset = fract(texCoord * sourceSize[0].xy) - 0.5;
|
||||
offset /= sourceSize[0].xy;
|
||||
|
||||
vec3 cx = texture(source[0], texCoord - offset).xyz;
|
||||
vec3 cy = texture(source[0], texCoord).xyz;
|
||||
vec3 cz = vec3(5.0 * grayscale(abs(cx - cy)));
|
||||
|
||||
fragColor = vec4(clamp(cz, 0.0, 1.0), 1.0);
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
program
|
||||
filter: linear
|
||||
wrap: edge
|
||||
fragment: edge-detection.fs
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.5 KiB |
@@ -1,99 +0,0 @@
|
||||
#version 150
|
||||
|
||||
// GritsScanlines by torridgristle
|
||||
// license: public domain (https://forums.libretro.com/t/lightweight-lut-based-scanline-glow-concept-prototype-glsl/18336/7)
|
||||
|
||||
/* Runtime parameters
|
||||
#in LuminanceDawnbringer
|
||||
#in LuminanceLUT
|
||||
#in ScanlinesOpacity
|
||||
#in GammaCorrection
|
||||
#in TrinitronColors
|
||||
*/
|
||||
|
||||
// static parameters
|
||||
//#define LuminanceDawnbringer
|
||||
#define LuminanceLUT
|
||||
//#define TrinitronColors
|
||||
#define ScanlinesOpacity 0.9
|
||||
//#define GammaCorrection 1.2
|
||||
|
||||
uniform sampler2D source[];
|
||||
uniform vec4 sourceSize[];
|
||||
uniform vec4 targetSize;
|
||||
uniform sampler2D pixmap[];
|
||||
|
||||
in Vertex {
|
||||
vec2 texCoord;
|
||||
};
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
#ifdef LuminanceLUT
|
||||
#define LUT_SizeLum 16.0
|
||||
|
||||
// Code taken from RetroArch's LUT shader
|
||||
float luminancelut(vec4 org)
|
||||
{
|
||||
vec4 imgColorLum = org;
|
||||
float redLum = ( imgColorLum.r * (LUT_SizeLum - 1.0) + 0.4999 ) / (LUT_SizeLum * LUT_SizeLum);
|
||||
float greenLum = ( imgColorLum.g * (LUT_SizeLum - 1.0) + 0.4999 ) / LUT_SizeLum;
|
||||
float blue1Lum = (floor( imgColorLum.b * (LUT_SizeLum - 1.0) ) / LUT_SizeLum) + redLum;
|
||||
float blue2Lum = (ceil( imgColorLum.b * (LUT_SizeLum - 1.0) ) / LUT_SizeLum) + redLum;
|
||||
float mixerLum = clamp(max((imgColorLum.b - blue1Lum) / (blue2Lum - blue1Lum), 0.0), 0.0, 32.0);
|
||||
float color1Lum = texture(pixmap[1], vec2( blue1Lum, greenLum )).x;
|
||||
float color2Lum = texture(pixmap[1], vec2( blue2Lum, greenLum )).x;
|
||||
return mix(color1Lum, color2Lum, mixerLum);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TrinitronColors
|
||||
#define LUT_SizeTrinitron 32.0
|
||||
|
||||
vec4 TrinitronD50(vec4 org)
|
||||
{
|
||||
vec4 imgColorTrinitron = org;
|
||||
float redTrinitron = ( imgColorTrinitron.r * (LUT_SizeTrinitron - 1.0) + 0.4999 ) / (LUT_SizeTrinitron * LUT_SizeTrinitron);
|
||||
float greenTrinitron = ( imgColorTrinitron.g * (LUT_SizeTrinitron - 1.0) + 0.4999 ) / LUT_SizeTrinitron;
|
||||
float blue1Trinitron = (floor( imgColorTrinitron.b * (LUT_SizeTrinitron - 1.0) ) / LUT_SizeTrinitron) + redTrinitron;
|
||||
float blue2Trinitron = (ceil( imgColorTrinitron.b * (LUT_SizeTrinitron - 1.0) ) / LUT_SizeTrinitron) + redTrinitron;
|
||||
float mixerTrinitron = clamp(max((imgColorTrinitron.b - blue1Trinitron) / (blue2Trinitron - blue1Trinitron), 0.0), 0.0, 32.0);
|
||||
vec4 color1Trinitron = texture(pixmap[2], vec2( blue1Trinitron, greenTrinitron ));
|
||||
vec4 color2Trinitron = texture(pixmap[2], vec2( blue2Trinitron, greenTrinitron ));
|
||||
vec4 fragColorTrinitron = mix(color1Trinitron, color2Trinitron, mixerTrinitron);
|
||||
return vec4(pow(fragColorTrinitron.rgb,vec3(GammaCorrection,GammaCorrection,GammaCorrection)),1.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
|
||||
//Source Image
|
||||
vec4 org = texture(source[0], texCoord);
|
||||
|
||||
#ifdef LuminanceLUT
|
||||
// Use a 3DLUT instead of an equation so that it can use any arbitrary mess you can come up with.
|
||||
float luminance = luminancelut(org);
|
||||
#elif defined LuminanceDawnbringer
|
||||
// Dawnbringer's brightness equation from Dawnbringer's Toolbox scripts for Grafx2
|
||||
float luminance = sqrt(org.r*org.r*0.0676 + org.g*org.g*0.3025 + org.b*org.b*0.0361) * 1.5690256395005606;
|
||||
#else
|
||||
// Plain, standard, fine; slightly faster
|
||||
float luminance = ((0.299*org.r) + (0.587*org.g) + (0.114*org.b));
|
||||
#endif
|
||||
|
||||
// Don't let it exceed 1.0
|
||||
luminance = clamp(luminance, 0.0, 1.0);
|
||||
|
||||
// Scanline Mapping, based on the Phosphor LUT shader's method of tiling a texture over the screen
|
||||
vec2 LUTeffectiveCoord = vec2(luminance,fract((texCoord.y*targetSize.y)/4.0));
|
||||
|
||||
// Scanline Layer
|
||||
vec4 screen = texture(pixmap[0], LUTeffectiveCoord);
|
||||
|
||||
// Output multiplying the scanlines into the original image, with control over opacity
|
||||
#ifdef TrinitronColors
|
||||
org = TrinitronD50(org);
|
||||
#endif
|
||||
fragColor = ((screen*ScanlinesOpacity)+(1.0 - ScanlinesOpacity)) * (org);
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 70 KiB |
@@ -1,35 +0,0 @@
|
||||
settings
|
||||
LuminanceDawnbringer
|
||||
active: false
|
||||
LuminanceLUT
|
||||
active: false
|
||||
TrinitronColors
|
||||
active: true
|
||||
ScanlinesOpacity: 0.9
|
||||
min: 0.0
|
||||
max: 1.0
|
||||
step: 0.05
|
||||
GammaCorrection: 1.2
|
||||
min: 0.5
|
||||
max: 2.0
|
||||
step: 0.1
|
||||
|
||||
|
||||
program
|
||||
filter: nearest
|
||||
wrap: edge
|
||||
width: 100%
|
||||
height: 400%
|
||||
fragment: GritsScanlines.fs
|
||||
pixmap: Scanline-LUT-4px.png
|
||||
filter: linear
|
||||
wrap: edge
|
||||
pixmap: Fake-Self-Illumination.png
|
||||
filter: linear
|
||||
wrap: edge
|
||||
pixmap: Trinitron D50 Absolute Colorimetric - LUT.png
|
||||
filter: linear
|
||||
wrap: edge
|
||||
|
||||
output1
|
||||
filter: linear
|
||||
@@ -1,39 +0,0 @@
|
||||
#version 150
|
||||
|
||||
/*
|
||||
Interlacing
|
||||
Author: hunterk
|
||||
License: Public domain
|
||||
|
||||
Note: This shader is designed to work with the typical interlaced output from an emulator, which displays both even and odd fields twice.
|
||||
This shader will un-weave the image, resulting in a standard, alternating-field interlacing.
|
||||
*/
|
||||
|
||||
uniform sampler2D source[];
|
||||
uniform vec2 targetSize;
|
||||
uniform vec4 sourceSize[];
|
||||
uniform int phase;
|
||||
|
||||
in Vertex {
|
||||
vec2 texCoord;
|
||||
};
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
// This controls how bright the intermediate lines are; 0.0 = totally black
|
||||
#define percent 0.0
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 res = texture(source[0], texCoord);
|
||||
float y = 0.0;
|
||||
|
||||
// assume anything with a vertical resolution greater than 400 lines is interlaced
|
||||
if (sourceSize[0].y > 400.0) y = sourceSize[0].y * texCoord.y + phase;
|
||||
else
|
||||
y = 2.00001 * sourceSize[0].y * texCoord.y;
|
||||
|
||||
if (mod(y, 2.0) > 0.99999) fragColor = res;
|
||||
else
|
||||
fragColor = vec4(percent) * res;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
input
|
||||
filter: nearest
|
||||
|
||||
program
|
||||
fragment: interlace.fs
|
||||
modulo: 2
|
||||
@@ -1,11 +0,0 @@
|
||||
input
|
||||
filter: nearest
|
||||
|
||||
program
|
||||
filter: nearest
|
||||
height: 100%
|
||||
fragment: pal-r57shell.fs
|
||||
|
||||
output
|
||||
height: 0
|
||||
filter: linear
|
||||
@@ -1,368 +0,0 @@
|
||||
#version 150
|
||||
|
||||
// NES PAL composite signal simulation for RetroArch
|
||||
// shader by r57shell
|
||||
// thanks to feos & HardWareMan & NewRisingSun
|
||||
|
||||
// also TV subpixels and scanlines
|
||||
|
||||
// LICENSE: PUBLIC DOMAIN
|
||||
|
||||
// NOTE: for nice TV subpixels and scanlines I recommend to
|
||||
// disable this features here and apply CRT-specialized shader.
|
||||
|
||||
// Quality considerations
|
||||
|
||||
// there are three main options:
|
||||
// USE_RAW (R), USE_DELAY_LINE (D), USE_COLORIMETRY (C)
|
||||
// here is table of quality in decreasing order:
|
||||
// RDC, RD, RC, DC, D, C
|
||||
|
||||
// compatibility macros
|
||||
#define float2 vec2
|
||||
#define float3 vec3
|
||||
#define float4 vec4
|
||||
#define frac(c) fract(c)
|
||||
#define saturate(c) clamp(c, 0.0, 1.0)
|
||||
#define fmod(x,y) mod(x,y)
|
||||
#define mul(x,y) (y*x)
|
||||
#define float2x2 mat2
|
||||
#define float3x3 mat3
|
||||
#define float4x4 mat4
|
||||
#define bool2 bvec2
|
||||
#define bool3 bvec3
|
||||
#define bool4 bvec4
|
||||
#define static
|
||||
|
||||
uniform sampler2D source[];
|
||||
uniform vec4 sourceSize[];
|
||||
uniform vec4 outputSize;
|
||||
uniform int phase;
|
||||
|
||||
in Vertex {
|
||||
vec2 texCoord;
|
||||
};
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
// TWEAKS start
|
||||
|
||||
// use delay line technique
|
||||
// without delay line technique, color would interleave
|
||||
// to avoid this, set HueRotation to zero.
|
||||
#define USE_DELAY_LINE
|
||||
|
||||
// use this if you need to swap even/odd V sign.
|
||||
// sign of V changes each scanline
|
||||
// so if some scanline is positive, then next is negative
|
||||
// and if you want to match picture
|
||||
// to actual running PAL NES on TV
|
||||
// you may want to have this option, to change signs
|
||||
// if they don't match
|
||||
//#define SWAP_VSIGN
|
||||
|
||||
// rough simulation of scanlines
|
||||
// better if you use additional shader instead
|
||||
// if you still use it, make sure that SizeY
|
||||
// is at least twice lower than output height
|
||||
#define USE_SCANLINES // FIXME: scanlines are broken and I'm too lazy to fix it right now
|
||||
|
||||
// to change gamma of virtual TV from 2.2 to something else
|
||||
#define USE_GAMMA
|
||||
|
||||
// use sampled version. it's much more slower version of shader.
|
||||
// because it is computing x4 more values. NOT RECOMMENDED.
|
||||
//#define USE_SAMPLED
|
||||
|
||||
#ifndef PARAMETER_UNIFORM
|
||||
// NTSC standard gamma = 2.2
|
||||
// PAL standard gamma = 2.8
|
||||
// according to many sources, very unlikely gamma of TV is 2.8
|
||||
// most likely gamma of PAL TV is in range 2.4-2.5
|
||||
const float Gamma_static = 2.8; // gamma of virtual TV
|
||||
|
||||
const float Brightness_static = 0.25;
|
||||
const float Contrast_static = 1.1;
|
||||
const float Saturation_static = 1.1;
|
||||
|
||||
const int
|
||||
Ywidth_static = 12,
|
||||
Uwidth_static = 23,
|
||||
Vwidth_static = 23;
|
||||
|
||||
#define Brightness Brightness_static
|
||||
#define Gamma Gamma_static
|
||||
|
||||
#define Ywidth Ywidth_static
|
||||
#define Uwidth Uwidth_static
|
||||
#define Vwidth Vwidth_static
|
||||
|
||||
#define SCANLINE_MUL (sw.x*dark_scanline+sw.y)
|
||||
|
||||
int Mwidth = int(max(float(Ywidth), max(float(Uwidth), float(Vwidth))));
|
||||
|
||||
// correct one is -2.5
|
||||
// works only with USE_RAW
|
||||
const float HueShift = -2.5;
|
||||
|
||||
// rotation of hue due to luma level.
|
||||
const float HueRotation = 2.;
|
||||
|
||||
// touch this only if you know what you doing
|
||||
const float Phase_Y = 2.; // fmod(341*10,12)
|
||||
const float Phase_One = 0.; // alternating phases.
|
||||
const float Phase_Two = 8.;
|
||||
|
||||
// screen size, scanlines = y*2; y one field, and y other field.
|
||||
const int SizeX = 256;
|
||||
const int SizeY = 240;
|
||||
|
||||
// count of pixels of virtual TV.
|
||||
// value close to 1000 produce small artifacts
|
||||
const int TV_Pixels = 400;
|
||||
|
||||
const float dark_scanline = 0.5; // half
|
||||
#endif
|
||||
|
||||
// this is using following matrixes.
|
||||
// it provides more scientific approach
|
||||
// by conversion into linear XYZ space
|
||||
// and back to sRGB.
|
||||
// it's using Gamma setting too.
|
||||
// define USE_GAMMA is not required.
|
||||
#define USE_COLORIMETRY
|
||||
|
||||
const float3x3 RGB_to_XYZ =
|
||||
mat3(
|
||||
0.4306190, 0.3415419, 0.1783091,
|
||||
0.2220379, 0.7066384, 0.0713236,
|
||||
0.0201853, 0.1295504, 0.9390944
|
||||
);
|
||||
|
||||
const float3x3 XYZ_to_sRGB =
|
||||
mat3(
|
||||
3.2406, -1.5372, -0.4986,
|
||||
-0.9689, 1.8758, 0.0415,
|
||||
0.0557, -0.2040, 1.0570
|
||||
);
|
||||
|
||||
// TWEAKS end
|
||||
|
||||
const float YUV_u = 0.492;
|
||||
const float YUV_v = 0.877;
|
||||
|
||||
const mat3 RGB_to_YUV =
|
||||
mat3(
|
||||
float3( 0.299, 0.587, 0.114), //Y
|
||||
float3(-0.299,-0.587, 0.886)*YUV_u, //B-Y
|
||||
float3( 0.701,-0.587,-0.114)*YUV_v //R-Y
|
||||
);
|
||||
|
||||
#ifdef USE_DELAY_LINE
|
||||
const float comb_line = 1.;
|
||||
#else
|
||||
const float comb_line = 2.;
|
||||
#endif
|
||||
|
||||
static const float Voltage_0 = 0.518;
|
||||
static const float Voltage_1 = 1.962;
|
||||
static const float DeltaV = (Voltage_1-Voltage_0);
|
||||
|
||||
float RGB_y = Contrast_static/Ywidth_static/DeltaV;
|
||||
float RGB_u = comb_line*Contrast_static*Saturation_static/YUV_u/Uwidth_static/DeltaV;
|
||||
float RGB_v = comb_line*Contrast_static*Saturation_static/YUV_v/Vwidth_static/DeltaV;
|
||||
|
||||
mat3 YUV_to_RGB =
|
||||
mat3(
|
||||
float3(1., 1., 1.)*RGB_y,
|
||||
float3(0., -0.114/0.587, 1.)*RGB_u,
|
||||
float3(1., -0.299/0.587, 0.)*RGB_v
|
||||
);
|
||||
|
||||
const float pi = 3.1415926535897932384626433832795;
|
||||
|
||||
float sinn(float x)
|
||||
{
|
||||
return sin(/*fmod(x,24)*/x*(pi*2./24.));
|
||||
}
|
||||
|
||||
float coss(float x)
|
||||
{
|
||||
return cos(/*fmod(x,24)*/x*(pi*2./24.));
|
||||
}
|
||||
|
||||
float3 monitor(sampler2D tex, float2 p)
|
||||
{
|
||||
float2 size = sourceSize[0].xy;//float2(SizeX,SizeY);
|
||||
// align vertical coord to center of texel
|
||||
float2 uv = float2(
|
||||
|
||||
p.x,
|
||||
|
||||
(floor(p.y*sourceSize[0].y)+0.5)/sourceSize[0].y);
|
||||
#ifdef USE_DELAY_LINE
|
||||
float2 sh = (sourceSize[0].xy/sourceSize[0].xy/size)*float2(14./10.,-1.0);
|
||||
#endif
|
||||
float2 pc = uv*sourceSize[0].xy/sourceSize[0].xy*size*float2(10.,1.);
|
||||
float alpha = dot(floor(float2(pc.x,pc.y)),float2(2.,Phase_Y*2.));
|
||||
alpha += Phase_One*2.;
|
||||
|
||||
// 1/size.x of screen in uv coords = sourceSize[0].x/sourceSize[0].x/size.x;
|
||||
// then 1/10*size.x of screen:
|
||||
float ustep = sourceSize[0].x/sourceSize[0].x/size.x/10.;
|
||||
|
||||
float border = sourceSize[0].x/sourceSize[0].x;
|
||||
float ss = 2.0;
|
||||
#ifdef SWAP_VSIGN
|
||||
#define PAL_SWITCH(A) A < 1.
|
||||
#else
|
||||
#define PAL_SWITCH(A) A > 1.
|
||||
#endif
|
||||
if (PAL_SWITCH(fmod(uv.y*sourceSize[0].y/sourceSize[0].y*size.y,2.0)))
|
||||
{
|
||||
// cos(pi-alpha) = -cos(alpha)
|
||||
// sin(pi-alpha) = sin(alpha)
|
||||
// pi - alpha
|
||||
alpha = -alpha+12012.0;
|
||||
ss = -2.0;
|
||||
}
|
||||
|
||||
float ysum = 0., usum = 0., vsum = 0.;
|
||||
for (int i=0; i<Mwidth; ++i)
|
||||
{
|
||||
float4 res = texture(tex, uv);
|
||||
float3 yuv = mul(RGB_to_YUV, res.xyz);
|
||||
float a1 = alpha+(HueShift+2.5)*2.-yuv.x*ss*HueRotation;
|
||||
float sig = yuv.x+dot(yuv.yz,sign(float2(sinn(a1),coss(a1))));
|
||||
#ifdef USE_DELAY_LINE
|
||||
float4 res1 = texture(tex, uv+sh);
|
||||
float3 yuv1 = mul(RGB_to_YUV, res1.xyz);
|
||||
float a2 = (HueShift+2.5)*2.+12012.0-alpha+yuv.x*ss*HueRotation;
|
||||
float sig1 = yuv1.x+dot(yuv1.yz,sign(float2(sinn(a2),coss(a2))));
|
||||
#endif
|
||||
if (i < Ywidth)
|
||||
ysum += sig;
|
||||
|
||||
#ifdef USE_DELAY_LINE
|
||||
if (i < Uwidth)
|
||||
usum += (sig+sig1)*sinn(alpha);
|
||||
if (i < Vwidth)
|
||||
vsum += (sig-sig1)*coss(alpha);
|
||||
#else
|
||||
if (i < Uwidth)
|
||||
usum += sig*sinn(alpha);
|
||||
if (i < Vwidth)
|
||||
vsum += sig*coss(alpha);
|
||||
#endif
|
||||
alpha -= ss;
|
||||
uv.x -= ustep;
|
||||
}
|
||||
|
||||
float3 rgb = mul(float3(ysum+Brightness*Ywidth_static,usum,vsum), YUV_to_RGB);
|
||||
#if defined(USE_GAMMA) && !defined(USE_COLORIMETRY)
|
||||
float3 rgb1 = saturate(rgb);
|
||||
rgb = pow(rgb1, Gamma/2.2);
|
||||
#endif
|
||||
|
||||
#ifdef USE_COLORIMETRY
|
||||
float3 rgb1 = saturate(rgb);
|
||||
rgb = pow(rgb1, float3(Gamma, Gamma, Gamma));
|
||||
#endif
|
||||
|
||||
#if (defined(USE_SCANLINES))
|
||||
float2 q = (p*sourceSize[0].xy/sourceSize[0].xy)*float2(TV_Pixels*3.,size.y*2.);
|
||||
#endif
|
||||
|
||||
#ifdef USE_SCANLINES
|
||||
float scanlines = size.y * outputSize.z;
|
||||
float top = fmod(q.y-0.5*scanlines*2.,2.);
|
||||
float bottom = top+frac(scanlines)*2.;
|
||||
float2 sw = saturate(min(float2(1.,2.),float2(bottom, bottom))
|
||||
-max(float2(0.,1.),float2(top)))
|
||||
+saturate(min(float2(3.,4.),float2(bottom, bottom))
|
||||
-max(float2(2.,3.),float2(top)))
|
||||
+floor(scanlines);
|
||||
|
||||
rgb = rgb*SCANLINE_MUL/(sw.x+sw.y);
|
||||
|
||||
/*
|
||||
//old stupid method
|
||||
float z =
|
||||
#ifdef ANIMATE_SCANLINE
|
||||
fmod(phase,2.0)+
|
||||
#endif
|
||||
0.5;
|
||||
|
||||
if (abs(fmod(q.y+0.5,2)-z)<0.5)
|
||||
rgb *= dark_scanline;
|
||||
*/
|
||||
#endif
|
||||
|
||||
// size of pixel screen in texture coords:
|
||||
//float output_pixel_size = sourceSize[0].x/(outputSize.x*sourceSize[0].x);
|
||||
|
||||
// correctness check
|
||||
//if (fmod(p.x*output_pixel_size,2.0) < 1.0)
|
||||
// rgb = float3(0.,0.,0.);
|
||||
|
||||
#ifdef USE_COLORIMETRY
|
||||
float3 xyz1 = mul(RGB_to_XYZ,rgb);
|
||||
float3 srgb = saturate(mul(XYZ_to_sRGB,xyz1));
|
||||
float3 a1 = 12.92*srgb;
|
||||
float3 a2 = 1.055*pow(srgb,float3(1./2.4))-0.055;
|
||||
float3 ssrgb;
|
||||
ssrgb.x = (srgb.x<0.0031308?a1.x:a2.x);
|
||||
ssrgb.y = (srgb.y<0.0031308?a1.y:a2.y);
|
||||
ssrgb.z = (srgb.z<0.0031308?a1.z:a2.z);
|
||||
return ssrgb;
|
||||
#else
|
||||
return rgb;
|
||||
#endif
|
||||
}
|
||||
|
||||
// pos (left corner, sample size)
|
||||
float4 monitor_sample(sampler2D tex, float2 p, float2 sample_)
|
||||
{
|
||||
// linear interpolation was...
|
||||
// now other thing.
|
||||
// http://imgur.com/m8Z8trV
|
||||
// AT LAST IT WORKS!!!!
|
||||
// going to check in retroarch...
|
||||
float2 size = sourceSize[0].xy;
|
||||
float2 next = float2(.25,1.)/size;
|
||||
float2 f = frac(float2(4.,1.)*size*p);
|
||||
sample_ *= float2(4.,1.)*size;
|
||||
float2 l;
|
||||
float2 r;
|
||||
if (f.x+sample_.x < 1.)
|
||||
{
|
||||
l.x = f.x+sample_.x;
|
||||
r.x = 0.;
|
||||
}
|
||||
else
|
||||
{
|
||||
l.x = 1.-f.x;
|
||||
r.x = min(1.,f.x+sample_.x-1.);
|
||||
}
|
||||
if (f.y+sample_.y < 1.)
|
||||
{
|
||||
l.y = f.y+sample_.y;
|
||||
r.y = 0.;
|
||||
}
|
||||
else
|
||||
{
|
||||
l.y = 1.-f.y;
|
||||
r.y = min(1.,f.y+sample_.y-1.);
|
||||
}
|
||||
float3 top = mix(monitor(tex, p), monitor(tex, p+float2(next.x,0.)), r.x/(l.x+r.x));
|
||||
float3 bottom = mix(monitor(tex, p+float2(0.,next.y)), monitor(tex, p+next), r.x/(l.x+r.x));
|
||||
return float4(mix(top,bottom, r.y/(l.y+r.y)),1.0);
|
||||
}
|
||||
|
||||
void main() {
|
||||
#ifdef USE_SAMPLED
|
||||
fragColor = vec4(monitor_sample(source[0], texCoord, outputSize.zw).rgb, 1.0);
|
||||
#else
|
||||
fragColor = float4(monitor(source[0], texCoord), 1.);
|
||||
#endif
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
program
|
||||
fragment: phosphorish.fs
|
||||
@@ -1,56 +0,0 @@
|
||||
/* Phosphorish
|
||||
|
||||
Copyright (C) 2011 Themaister
|
||||
|
||||
This program is licensed in the public domain
|
||||
*/
|
||||
|
||||
#version 150
|
||||
|
||||
uniform sampler2D source[];
|
||||
uniform vec4 sourceSize[];
|
||||
uniform vec4 targetSize;
|
||||
|
||||
in Vertex {
|
||||
vec2 texCoord;
|
||||
};
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
vec3 to_focus(float pixel)
|
||||
{
|
||||
pixel = mod(pixel + 3.0, 3.0);
|
||||
if (pixel >= 2.0) // Blue
|
||||
return vec3(pixel - 2.0, 0.0, 3.0 - pixel);
|
||||
else if (pixel >= 1.0) // Green
|
||||
return vec3(0.0, 2.0 - pixel, pixel - 1.0);
|
||||
else // Red
|
||||
return vec3(1.0 - pixel, pixel, 0.0);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
float y = mod(texCoord.y * sourceSize[0].y, 1.0);
|
||||
float intensity = exp(-0.2 * y);
|
||||
|
||||
vec2 one_x = vec2(1.0 / (3.0 * sourceSize[0].x), 0.0);
|
||||
|
||||
vec3 color = texture(source[0], texCoord.xy - 0.0 * one_x).rgb;
|
||||
vec3 color_prev = texture(source[0], texCoord.xy - 1.0 * one_x).rgb;
|
||||
vec3 color_prev_prev = texture(source[0], texCoord.xy - 2.0 * one_x).rgb;
|
||||
|
||||
float pixel_x = 3.0 * texCoord.x * sourceSize[0].x;
|
||||
|
||||
vec3 focus = to_focus(pixel_x - 0.0);
|
||||
vec3 focus_prev = to_focus(pixel_x - 1.0);
|
||||
vec3 focus_prev_prev = to_focus(pixel_x - 2.0);
|
||||
|
||||
vec3 result =
|
||||
0.8 * color * focus +
|
||||
0.6 * color_prev * focus_prev +
|
||||
0.3 * color_prev_prev * focus_prev_prev;
|
||||
|
||||
result = 2.3 * pow(result, vec3(1.4));
|
||||
|
||||
fragColor = vec4(intensity * result, 1.0);
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
program
|
||||
filter: linear
|
||||
wrap: border
|
||||
fragment: scanline.fs
|
||||
@@ -1,20 +0,0 @@
|
||||
#version 150
|
||||
|
||||
uniform sampler2D source[];
|
||||
|
||||
in Vertex {
|
||||
vec2 texCoord;
|
||||
};
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
vec4 rgba = texture(source[0], texCoord);
|
||||
vec4 intensity;
|
||||
if(fract(gl_FragCoord.y * (0.5 * 4.0 / 3.0)) > 0.5) {
|
||||
intensity = vec4(0);
|
||||
} else {
|
||||
intensity = smoothstep(0.2, 0.8, rgba) + normalize(rgba);
|
||||
}
|
||||
fragColor = intensity * -0.25 + rgba * 1.1;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
input
|
||||
filter: linear
|
||||
|
||||
program
|
||||
fragment: sharp-bilinear.fs
|
||||
|
||||
output
|
||||
filter: linear
|
||||
@@ -1,33 +0,0 @@
|
||||
#version 150
|
||||
|
||||
// Sharp Bilinear
|
||||
// Author: Themaister
|
||||
// License: Public Domain
|
||||
|
||||
uniform sampler2D source[];
|
||||
uniform vec4 sourceSize[];
|
||||
uniform vec4 targetSize;
|
||||
|
||||
in Vertex {
|
||||
vec2 texCoord;
|
||||
};
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
float prescale = floor(targetSize.y / sourceSize[0].y);
|
||||
vec2 texel = texCoord.xy * sourceSize[0].xy;
|
||||
vec2 texel_floored = floor(texel);
|
||||
vec2 s = fract(texel);
|
||||
float region_range = 0.5 - 0.5 / prescale;
|
||||
|
||||
// Figure out where in the texel to sample to get correct pre-scaled bilinear.
|
||||
// Uses the hardware bilinear interpolator to avoid having to sample 4 times manually.
|
||||
|
||||
vec2 center_dist = s - 0.5;
|
||||
vec2 f = (center_dist - clamp(center_dist, -region_range, region_range)) * prescale + 0.5;
|
||||
|
||||
vec2 mod_texel = texel_floored + f;
|
||||
|
||||
fragColor = texture(source[0], mod_texel / sourceSize[0].xy);;
|
||||
}
|
||||
@@ -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 = "136";
|
||||
const string Version = "140";
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,80 +32,125 @@ 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;
|
||||
@@ -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
|
||||
@@ -36,6 +34,7 @@ auto SH2::instruction() -> void {
|
||||
do {
|
||||
auto block = recompiler.block(PC - 4);
|
||||
block->execute(*this);
|
||||
ID = 0;
|
||||
} while (CCR < cyclesUntilRecompilerExit);
|
||||
|
||||
// Reset the count as it may have been set to 0 for an early exit
|
||||
@@ -43,7 +42,6 @@ auto SH2::instruction() -> void {
|
||||
|
||||
step(CCR);
|
||||
CCR = 0;
|
||||
ID = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -300,6 +300,7 @@ struct SH2 {
|
||||
|
||||
static auto mask(u8 address, u8 size) -> u64;
|
||||
|
||||
bool enabled = false;
|
||||
bool callInstructionPrologue = false;
|
||||
bool inDelaySlot;
|
||||
u32 generation;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -113,8 +113,8 @@ auto WDC65816::instructionIndexedIndirectRead8(alu8 op) -> void {
|
||||
U.l = fetch();
|
||||
idle2();
|
||||
idle();
|
||||
V.l = readDirect(U.l + X.w + 0);
|
||||
V.h = readDirect(U.l + X.w + 1);
|
||||
V.l = readDirectX(U.l + X.w, 0);
|
||||
V.h = readDirectX(U.l + X.w, 1);
|
||||
L W.l = readBank(V.w + 0);
|
||||
alu(W.l);
|
||||
}
|
||||
@@ -123,8 +123,8 @@ auto WDC65816::instructionIndexedIndirectRead16(alu16 op) -> void {
|
||||
U.l = fetch();
|
||||
idle2();
|
||||
idle();
|
||||
V.l = readDirect(U.l + X.w + 0);
|
||||
V.h = readDirect(U.l + X.w + 1);
|
||||
V.l = readDirectX(U.l + X.w, 0);
|
||||
V.h = readDirectX(U.l + X.w, 1);
|
||||
W.l = readBank(V.w + 0);
|
||||
L W.h = readBank(V.w + 1);
|
||||
alu(W.w);
|
||||
|
||||
@@ -90,8 +90,8 @@ auto WDC65816::instructionIndexedIndirectWrite8() -> void {
|
||||
U.l = fetch();
|
||||
idle2();
|
||||
idle();
|
||||
V.l = readDirect(U.l + X.w + 0);
|
||||
V.h = readDirect(U.l + X.w + 1);
|
||||
V.l = readDirectX(U.l + X.w, 0);
|
||||
V.h = readDirectX(U.l + X.w, 1);
|
||||
L writeBank(V.w + 0, A.l);
|
||||
}
|
||||
|
||||
@@ -99,8 +99,8 @@ auto WDC65816::instructionIndexedIndirectWrite16() -> void {
|
||||
U.l = fetch();
|
||||
idle2();
|
||||
idle();
|
||||
V.l = readDirect(U.l + X.w + 0);
|
||||
V.h = readDirect(U.l + X.w + 1);
|
||||
V.l = readDirectX(U.l + X.w, 0);
|
||||
V.h = readDirectX(U.l + X.w, 1);
|
||||
writeBank(V.w + 0, A.l);
|
||||
L writeBank(V.w + 1, A.h);
|
||||
}
|
||||
|
||||
@@ -59,6 +59,13 @@ inline auto WDC65816::writeDirect(u32 address, n8 data) -> void {
|
||||
write(n16(D.w + address), data);
|
||||
}
|
||||
|
||||
inline auto WDC65816::readDirectX(u32 address, u32 offset) -> n8 {
|
||||
// The (direct,X) addressing mode has a bug in which the high byte is
|
||||
// wrapped within the page if E = 1 and D&0xFF != 0.
|
||||
if(EF && D.l) return read(((D.w + address) & 0xffff00) | n8(D.w + address + offset));
|
||||
else return readDirect(address + offset);
|
||||
}
|
||||
|
||||
inline auto WDC65816::readDirectN(u32 address) -> n8 {
|
||||
return read(n16(D.w + address));
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ struct WDC65816 {
|
||||
auto pullN() -> n8;
|
||||
auto pushN(n8 data) -> void;
|
||||
auto readDirect(u32 address) -> n8;
|
||||
auto readDirectX(u32 address, u32 offset) -> n8;
|
||||
auto writeDirect(u32 address, n8 data) -> void;
|
||||
auto readDirectN(u32 address) -> n8;
|
||||
auto readBank(u32 address) -> n8;
|
||||
|
||||
@@ -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:
|
||||
|
||||
Loaded 100 of 5262 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user