mirror of
https://github.com/ares-emulator/ares.git
synced 2026-11-11 01:11:42 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86db200a23 | ||
|
|
2781365ac3 | ||
|
|
a238a7e754 | ||
|
|
299d211ab1 | ||
|
|
fc2278486e | ||
|
|
f7d5a87667 | ||
|
|
3e2934529e | ||
|
|
ff9afca894 | ||
|
|
3f002928ed | ||
|
|
b124e8a8f0 | ||
|
|
0f965050b1 | ||
|
|
b9cd02ddee | ||
|
|
b7198a2334 | ||
|
|
8967e05524 | ||
|
|
1b1e7f8f2c | ||
|
|
00eb27681e | ||
|
|
9ad4b27681 | ||
|
|
060eaba2ea | ||
|
|
d75a495fc1 | ||
|
|
f593b12416 | ||
|
|
2f3385776d | ||
|
|
fe8afe05de | ||
|
|
bd9f52cf8c | ||
|
|
3545360033 | ||
|
|
5c61c3acbd | ||
|
|
3d3513e365 | ||
|
|
6a0f2eaf1a | ||
|
|
9422ea5905 | ||
|
|
582fc8f846 | ||
|
|
73893352f7 | ||
|
|
47c9ec1f80 | ||
|
|
b482344691 | ||
|
|
cbdedc9f9c | ||
|
|
12a1585ee2 | ||
|
|
4644bb4494 | ||
|
|
a54b551e4d | ||
|
|
30724164be | ||
|
|
517e0cc454 | ||
|
|
2c489cdadd | ||
|
|
28a956111d | ||
|
|
7115ed9ab5 | ||
|
|
566184f669 | ||
|
|
8b5fbf511f | ||
|
|
c6a6972a75 | ||
|
|
59713ea9b1 | ||
|
|
4e1e3e02bb | ||
|
|
a26e1ae006 | ||
|
|
160ddb4457 | ||
|
|
8dda1836ac | ||
|
|
1e9c07c803 | ||
|
|
18884d6aa2 | ||
|
|
2c463d0322 | ||
|
|
2c3963c24e | ||
|
|
3d57bf7914 | ||
|
|
9fa501cc3a | ||
|
|
c66e3471e0 | ||
|
|
17924254a1 | ||
|
|
27fe06917e | ||
|
|
164ef7b678 | ||
|
|
2ad30215df | ||
|
|
d4ae964be4 | ||
|
|
228105e6de | ||
|
|
6c5e8ed49e | ||
|
|
019287a648 | ||
|
|
719dac2dc1 | ||
|
|
abf2ef5832 | ||
|
|
953a50d827 | ||
|
|
7c116300bc | ||
|
|
cbfd7ed270 | ||
|
|
78d926b31d | ||
|
|
a4d03c263f | ||
|
|
5283135a49 | ||
|
|
2c9cf460f3 | ||
|
|
2bcb236dd6 | ||
|
|
77ff400bb1 | ||
|
|
c87166f9c5 | ||
|
|
6827540f11 | ||
|
|
0615773904 | ||
|
|
c73e19bdf2 | ||
|
|
c0cac55065 | ||
|
|
e45e82bd19 | ||
|
|
26a7ac35ca | ||
|
|
9748b37532 | ||
|
|
0feacdc6a5 | ||
|
|
be2495b253 | ||
|
|
24762209aa | ||
|
|
74ec42e889 | ||
|
|
fa76d1fb16 | ||
|
|
c437bc2d25 | ||
|
|
86ec0d1226 | ||
|
|
12c771e5c9 | ||
|
|
451d562186 | ||
|
|
aa5efe67ef | ||
|
|
1749d4bd9b | ||
|
|
d21820dab7 | ||
|
|
51474d86e7 | ||
|
|
7fc641c787 | ||
|
|
1059725734 | ||
|
|
6396c8c9ac | ||
|
|
57647bf62b | ||
|
|
5176bfead1 | ||
|
|
91281a8247 | ||
|
|
c46beaff32 | ||
|
|
b3c49a4636 | ||
|
|
7ae217bc79 | ||
|
|
5d37682c6b | ||
|
|
d70cb368de | ||
|
|
4fcfa471a4 | ||
|
|
01da5ed20d | ||
|
|
433f230c9c | ||
|
|
91e49ab940 | ||
|
|
e464f45605 | ||
|
|
e062350552 | ||
|
|
74d6e885f6 | ||
|
|
ec34c02e4f | ||
|
|
0aa84312c5 | ||
|
|
17a654cfc4 | ||
|
|
027a3db94c | ||
|
|
38a7c74f28 | ||
|
|
3147c3c996 | ||
|
|
d6605625bc | ||
|
|
e534e56002 | ||
|
|
cf9c02e571 | ||
|
|
0483193439 | ||
|
|
6112bcaced | ||
|
|
64ae531ead | ||
|
|
e90c318c0e | ||
|
|
a4551216e9 | ||
|
|
2e38fa5fc6 | ||
|
|
e2b96c8bab | ||
|
|
4c3c07eb38 | ||
|
|
3e7ca5baec | ||
|
|
c74700f35d | ||
|
|
368578d736 | ||
|
|
65d9640b20 | ||
|
|
836f3fc976 | ||
|
|
5636e2b0f6 | ||
|
|
37bc375c64 | ||
|
|
50467b7e6d | ||
|
|
dc37e934e6 | ||
|
|
21e5daebfc | ||
|
|
8f579b1993 | ||
|
|
ed336af513 | ||
|
|
03186eb4d3 | ||
|
|
972495df89 | ||
|
|
3366b1dc6c | ||
|
|
2c86419dd8 | ||
|
|
0e30732950 | ||
|
|
0fb5569c6b | ||
|
|
f289cdd08a | ||
|
|
f0598aaeb3 | ||
|
|
1a498d64c9 | ||
|
|
c2f58d1c79 | ||
|
|
3d826f5b26 | ||
|
|
2bb95a13cb | ||
|
|
1de21e178c | ||
|
|
cde0e1603d | ||
|
|
6fe063815c | ||
|
|
b527a33b85 | ||
|
|
29a9c53a62 | ||
|
|
56fe0d8269 | ||
|
|
dc28723d52 | ||
|
|
36af497fdf | ||
|
|
d5c094e9e8 | ||
|
|
6adc70cbc8 | ||
|
|
2e385844d0 | ||
|
|
7e48079a46 | ||
|
|
f9eb9304f0 | ||
|
|
c3eb8bc49e | ||
|
|
8759a7d3e8 | ||
|
|
a785ce924b | ||
|
|
938b1647b0 | ||
|
|
6abe6bfb19 | ||
|
|
d22fae0cae | ||
|
|
4effa2b6d2 | ||
|
|
e6dfd5ee74 | ||
|
|
a5966490c9 | ||
|
|
f5f75cdeef | ||
|
|
a6209af97b | ||
|
|
93e24926ae | ||
|
|
6f3997dc38 | ||
|
|
b380834c0e | ||
|
|
9e551be17a | ||
|
|
d77c6f0788 | ||
|
|
a1df486779 | ||
|
|
cdedb9b0f5 | ||
|
|
9ff41ed45a | ||
|
|
56aa5dac0d | ||
|
|
ebde92c891 | ||
|
|
7dc55eeb54 | ||
|
|
ec4e0050e7 | ||
|
|
306d23f38b | ||
|
|
fa3dfa38cd | ||
|
|
a0601f1c79 | ||
|
|
19b56bc118 | ||
|
|
2f12a72cad | ||
|
|
ed0fe5599f | ||
|
|
081408e961 | ||
|
|
56d6488b3d | ||
|
|
85f1fb6b7f | ||
|
|
34d6d465c4 | ||
|
|
fcef806553 | ||
|
|
61258a7063 | ||
|
|
b1d216b8af | ||
|
|
c53023e6c8 | ||
|
|
637aa19a72 | ||
|
|
2cf4d8cac8 | ||
|
|
0071e8a7d4 | ||
|
|
d546f147c3 | ||
|
|
b03a942bf4 | ||
|
|
23416eae32 | ||
|
|
2ade0e39de | ||
|
|
62930f8da8 | ||
|
|
be1f424398 | ||
|
|
003afbbb58 | ||
|
|
3010c8a8c9 | ||
|
|
4a935893d2 | ||
|
|
32096b8e85 | ||
|
|
3142bd3aef | ||
|
|
9985c693bd | ||
|
|
81e15695c8 | ||
|
|
f486867292 | ||
|
|
ffa7040b6f | ||
|
|
247a269054 | ||
|
|
50b2a8f3ab | ||
|
|
f1e36f2775 | ||
|
|
997fda03fe | ||
|
|
c6ecbe81b2 | ||
|
|
9c9ef927d7 | ||
|
|
029ae18514 | ||
|
|
dcfce18d7f | ||
|
|
295c729ecf | ||
|
|
43e0dd770d | ||
|
|
6e9c08b491 | ||
|
|
1808e3f50c | ||
|
|
8f39e06900 | ||
|
|
7742b49289 | ||
|
|
4750a7f224 | ||
|
|
ff78815e90 | ||
|
|
00a5c6864e | ||
|
|
a38572199d | ||
|
|
bcd501edf6 | ||
|
|
8d1ad79fdb | ||
|
|
b2db7f16f6 | ||
|
|
080b53a652 | ||
|
|
2b9fcab30f | ||
|
|
5565d2def4 | ||
|
|
9aa05bcbe3 | ||
|
|
e0434ca82b | ||
|
|
9c5ecf52ce | ||
|
|
6d890244c1 | ||
|
|
f7657dca4f | ||
|
|
eda2918f58 | ||
|
|
d75c13107c | ||
|
|
1a5984be70 | ||
|
|
e1711eb28a | ||
|
|
1a564fa93e | ||
|
|
6118facaf2 | ||
|
|
d52a3b9dfc | ||
|
|
38495197d2 | ||
|
|
dd6a448bc4 | ||
|
|
1cea9ad1d9 | ||
|
|
14c9dc04fd | ||
|
|
9e983e720f | ||
|
|
b2dd52c0a4 | ||
|
|
9a3d949fc2 | ||
|
|
00819b70c0 | ||
|
|
cd83a939e8 |
No files matched your search
@@ -0,0 +1,27 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Report a bug *not* relating to game compatibility
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: Game Issue Report
|
||||
about: Report an issue running a specific game in ares
|
||||
title: "[system] Game"
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Before submitting an issue, please use the search to make sure an issue doesn't already exist.
|
||||
|
||||
**Describe the issue**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**Rom Information**
|
||||
Specify the impacted rom filename and any associated hashes (sha256 preferred)
|
||||
|
||||
```
|
||||
file: Some Game (REGION).ext
|
||||
sha256: <hash>
|
||||
```
|
||||
@@ -13,12 +13,6 @@ jobs:
|
||||
- ares
|
||||
platform:
|
||||
- name: windows
|
||||
os: windows-latest
|
||||
compiler: g++
|
||||
shell: 'msys2 {0}'
|
||||
msystem: mingw64
|
||||
msys-env: x86_64
|
||||
- name: windows-clang
|
||||
os: windows-latest
|
||||
compiler: clang++
|
||||
shell: 'msys2 {0}'
|
||||
@@ -49,9 +43,63 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get update -y -qq
|
||||
sudo apt-get install libsdl2-dev libgtksourceview2.0-dev libgtk2.0-dev libao-dev libopenal-dev
|
||||
- uses: actions/checkout@v2
|
||||
- name: "macOS: Import Certificate"
|
||||
if: runner.os == 'macOS'
|
||||
uses: devbotsxyz/import-signing-certificate@2ac4f44d28045073d23153256efbb4c4b2d8aa22 # Don't use rolling branch for security reasons
|
||||
with:
|
||||
certificate-data: ${{ secrets.MACOS_CERTIFICATE_DATA }}
|
||||
certificate-passphrase: ${{ secrets.MACOS_CERTIFICATE_PASSPHRASE }}
|
||||
keychain-name: ares-macos-keychain
|
||||
keychain-password: ${{ secrets.MACOS_KEYCHAIN_PASSWORD }}
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
- name: "macOS: recover cache"
|
||||
if: runner.os == 'macOS'
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
cache-name: moltenvk
|
||||
with:
|
||||
path: thirdparty/MoltenVK/MoltenVK
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('thirdparty/MoltenVK/HEAD') }}
|
||||
- name: Install macOS Dependencies
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
brew install make ninja
|
||||
echo "MAKE=gmake" >> $GITHUB_ENV
|
||||
pushd thirdparty/MoltenVK
|
||||
./build-moltenvk.sh
|
||||
popd
|
||||
- name: Make
|
||||
run: make -j4 -C desktop-ui build=optimized local=false lto=true compiler=${{ matrix.platform.compiler }}
|
||||
if: runner.os != 'macOS'
|
||||
run: ${MAKE:-make} -j4 -C desktop-ui build=optimized local=false compiler=${{ matrix.platform.compiler }}
|
||||
- name: "macOS: Make universal app"
|
||||
if: runner.os == 'macOS'
|
||||
run: scripts/macos-make-universal.sh
|
||||
env:
|
||||
MAKEFLAGS: -j3
|
||||
MACOS_CERTIFICATE_NAME: ${{ secrets.MACOS_CERTIFICATE_NAME }}
|
||||
MACOS_KEYCHAIN_NAME: ares-macos-keychain
|
||||
MACOS_KEYCHAIN_PASSWORD: ${{ secrets.MACOS_KEYCHAIN_PASSWORD }}
|
||||
- name: "Windows: sign"
|
||||
if: runner.os == 'Windows'
|
||||
uses: DanaBear/code-sign-action@98c79121b376beab8d6a9484f445089db4461bca
|
||||
with:
|
||||
certificate: '${{ secrets.WIN_CERTIFICATE }}'
|
||||
password: '${{ secrets.WIN_PASSWORD }}'
|
||||
certificatesha1: '${{ secrets.WIN_CERTHASH }}'
|
||||
certificatename: '${{ secrets.WIN_CERTNAME }}'
|
||||
folder: 'desktop-ui/out'
|
||||
timestampUrl: 'http://timestamp.digicert.com'
|
||||
- name: "macOS: notarize"
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
ditto -c -k --keepParent desktop-ui/out/ares.app /tmp/ares.zip
|
||||
xcrun notarytool submit /tmp/ares.zip --apple-id "$MACOS_NOTARIZATION_USERNAME" --password "$MACOS_NOTARIZATION_PASSWORD" --team-id "$MACOS_NOTARIZATION_TEAMID" --wait
|
||||
xcrun stapler staple desktop-ui/out/ares.app
|
||||
env:
|
||||
MACOS_NOTARIZATION_USERNAME: ${{ secrets.MACOS_NOTARIZATION_USERNAME }}
|
||||
MACOS_NOTARIZATION_PASSWORD: ${{ secrets.MACOS_NOTARIZATION_PASSWORD }}
|
||||
MACOS_NOTARIZATION_TEAMID: ${{ secrets.MACOS_NOTARIZATION_TEAMID }}
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
name: 'Split Subtrees'
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- '*'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}
|
||||
jobs:
|
||||
packages_split:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
package:
|
||||
- nall
|
||||
- hiro
|
||||
- ruby
|
||||
- libco
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- if: "!startsWith(github.ref, 'refs/tags/')"
|
||||
uses: "symplify/[email protected]"
|
||||
with:
|
||||
package_directory: '${{ matrix.package }}'
|
||||
repository_organization: 'ares-emulator'
|
||||
repository_name: '${{ matrix.package }}'
|
||||
user_name: "Luke Usher"
|
||||
user_email: "[email protected]"
|
||||
@@ -1,2 +1,6 @@
|
||||
obj/
|
||||
out/
|
||||
obj-amd64/
|
||||
obj-arm64/
|
||||
out-amd64/
|
||||
out-arm64/
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
all: desktop
|
||||
targets = all clean install uninstall verbose
|
||||
|
||||
desktop:
|
||||
$(MAKE) -C desktop-ui
|
||||
$(targets):
|
||||
@$(MAKE) -C desktop-ui $@
|
||||
|
||||
.PHONY: all desktop
|
||||
.PHONY: $(targets)
|
||||
@@ -145,4 +145,247 @@ Gailly and Mark Adler; it does not include third-party code.
|
||||
If you redistribute modified sources, we would appreciate that you include in
|
||||
the file ChangeLog history information documenting your changes. Please read
|
||||
the FAQ for more information on the distribution of modified source versions.
|
||||
----------------------------------------------------------------------
|
||||
----------------------------------------------------------------------
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
xxHash - Extremely Fast Hash algorithm
|
||||
Header File
|
||||
Copyright (C) 2012-2021 Yann Collet
|
||||
|
||||
BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 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.
|
||||
|
||||
You can contact the author at:
|
||||
- xxHash homepage: https://www.xxhash.com
|
||||
- xxHash source repository: https://github.com/Cyan4973/xxHash
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
MoltenVK
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
@@ -1,4 +1,4 @@
|
||||

|
||||

|
||||
|
||||
[](https://github.com/higan-emu/ares/blob/master/LICENSE)
|
||||
|
||||
@@ -16,3 +16,75 @@ Nightly Builds
|
||||
|
||||
Automated, untested builds of ares are available for Windows and macOS as a [pre-release](https://github.com/higan-emu/ares/releases/tag/nightly).
|
||||
Only the latest nightly build is kept.
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
|
||||
#### *nix building
|
||||
|
||||
g++ make pkg-config libgtk2.0-dev libcanberra-gtk-module libgl-dev libasound2-dev
|
||||
|
||||
By default, GTK2 is used, but support for GTK3 is available. You will need to install the additional package `libgtk-3-dev` as well
|
||||
as specifying the command line option `hiro=gtk3` at compile time.
|
||||
|
||||
#### Windows building
|
||||
|
||||
To build on Windows, using MSYS2 is recommended which can be download [here](https://www.msys2.org/). Follow the instructions
|
||||
on this page to install and setup an appropriate MINGW64 environment. Running the command `pacman -S --needed base-devel mingw-w64-x86_64-toolchain` from the MSYS2 MSYS terminal should setup everything you need to compile Ares. Note that in order
|
||||
to compile, you will want to be in a MINGW64 terminal window after install and setup is complete.
|
||||
|
||||
Compilation
|
||||
-----------
|
||||
|
||||
Check out the source code by running this command:
|
||||
|
||||
git clone https://github.com/ares-emulator/ares.git
|
||||
|
||||
From the root of the project directory run:
|
||||
|
||||
make -j4 build=release
|
||||
|
||||
that builds with build type of type 'release'.
|
||||
`-j4` indicates number of parallel build processes, and shouldn't be set higher than N-1 cores on your processor. Specifying this option can significantly decrease the time to build this project. There are multiple build types available (debug, etc.). Most additional options can be
|
||||
found in nall's make file (nall/GNUmakefile).
|
||||
|
||||
To start compilation from the beginning, run the following prior to compiling:
|
||||
|
||||
make clean
|
||||
|
||||
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/*`.
|
||||
|
||||
|
||||
Command-line options
|
||||
--------------------
|
||||
|
||||
When started from the command-line, ares accepts a few options.
|
||||
|
||||
```
|
||||
Usage: ./ares [options] game
|
||||
|
||||
--help Displays available options and exit
|
||||
--fullscreen Start in full screen mode
|
||||
--system system Specify the system name
|
||||
```
|
||||
|
||||
The --system option is useful when the system type cannot be auto-detected.
|
||||
--fullscreen will only have an effect if a game is also passed in argument.
|
||||
|
||||
Example:
|
||||
`ares --system MSX examples.rom --fullscreen`
|
||||
|
||||
|
||||
High-level Components
|
||||
---------------------
|
||||
|
||||
* __ares__: emulator cores and component implementations
|
||||
* __desktop-ui__: main GUI implementation for this project
|
||||
* __hiro__: cross-platform GUI toolkit that utilizes native APIs on supported platforms
|
||||
* __nall__: Near's alternative to the C++ standard library
|
||||
* __ruby__: interface between a hiro application and platform-specific APIs for emulator video, audio, and input
|
||||
* __mia__: internal ROM database and ROM/image loader
|
||||
* __libco__: cooperative multithreading library
|
||||
@@ -15,6 +15,10 @@ ares.objects := ares ares-fixed-allocator
|
||||
$(object.path)/ares.o: $(ares.path)/ares/ares.cpp
|
||||
$(object.path)/ares-fixed-allocator.o: $(ares.path)/ares/memory/fixed-allocator.cpp
|
||||
|
||||
ifeq ($(platform),macos)
|
||||
ares.dylibs :=
|
||||
endif
|
||||
|
||||
ifeq ($(vulkan),true)
|
||||
flags += -DVULKAN
|
||||
endif
|
||||
|
||||
@@ -16,7 +16,7 @@ auto TIA::load(Node::Object parent) -> void {
|
||||
screen = node->append<Node::Video::Screen>("Screen", 160, vlines());
|
||||
screen->colors(1 << 7, {&TIA::color, this});
|
||||
screen->setSize(160, vlines());
|
||||
screen->setScale(2.0, 1.0);
|
||||
screen->setScale(4.0, 2.0);
|
||||
screen->setAspect(1.0, 1.0);
|
||||
screen->setViewport(0, 0, 160, vlines());
|
||||
|
||||
@@ -44,7 +44,7 @@ auto TIA::scanline() -> void {
|
||||
cpu.rdyLine(1);
|
||||
for(io.hcounter = 0; io.hcounter < 228; io.hcounter++) {
|
||||
auto x = io.hcounter - 68;
|
||||
if(x >= 0) pixel(x);
|
||||
if(x >= 0 && io.vcounter < vlines()) pixel(x);
|
||||
writeQueue.step();
|
||||
runAudio();
|
||||
step();
|
||||
|
||||
+2
-2
@@ -38,7 +38,7 @@ using namespace nall;
|
||||
|
||||
namespace ares {
|
||||
static const string Name = "ares";
|
||||
static const string Version = "129";
|
||||
static const string Version = "130";
|
||||
static const string Copyright = "ares team, Near";
|
||||
static const string License = "ISC";
|
||||
static const string LicenseURI = "https://opensource.org/licenses/ISC";
|
||||
@@ -47,7 +47,7 @@ namespace ares {
|
||||
|
||||
//incremented only when serialization format changes
|
||||
static const u32 SerializerSignature = 0x31545342; //"BST1" (little-endian)
|
||||
static const string SerializerVersion = "129";
|
||||
static const string SerializerVersion = "130";
|
||||
|
||||
namespace VFS {
|
||||
using Pak = shared_pointer<vfs::directory>;
|
||||
|
||||
@@ -4,19 +4,19 @@ struct Debug {
|
||||
auto reset() -> void;
|
||||
|
||||
template<typename... P> auto unhandled(P&&... p) -> void {
|
||||
return _unhandled({forward<P>(p)...});
|
||||
return _unhandled({std::forward<P>(p)...});
|
||||
}
|
||||
|
||||
template<typename... P> auto unimplemented(P&&... p) -> void {
|
||||
return _unimplemented({forward<P>(p)...});
|
||||
return _unimplemented({std::forward<P>(p)...});
|
||||
}
|
||||
|
||||
template<typename... P> auto unusual(P&&... p) -> void {
|
||||
return _unusual({forward<P>(p)...});
|
||||
return _unusual({std::forward<P>(p)...});
|
||||
}
|
||||
|
||||
template<typename... P> auto unverified(P&&... p) -> void {
|
||||
return _unverified({forward<P>(p)...});
|
||||
return _unverified({std::forward<P>(p)...});
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -25,7 +25,7 @@ struct Stream : Audio {
|
||||
template<typename... P>
|
||||
auto frame(P&&... p) -> void {
|
||||
if(runAhead()) return;
|
||||
f64 samples[sizeof...(p)] = {forward<P>(p)...};
|
||||
f64 samples[sizeof...(p)] = {std::forward<P>(p)...};
|
||||
write(samples);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
struct Input : Object {
|
||||
DeclareClass(Input, "input")
|
||||
using Object::Object;
|
||||
u64 lastPoll;
|
||||
};
|
||||
@@ -33,7 +33,7 @@ struct Object : shared_pointer_this<Object> {
|
||||
template<typename T, typename... P>
|
||||
auto prepend(P&&... p) -> Node::Object {
|
||||
using Type = typename T::type;
|
||||
return prepend(shared_pointer<Type>::create(forward<P>(p)...));
|
||||
return prepend(shared_pointer<Type>::create(std::forward<P>(p)...));
|
||||
}
|
||||
|
||||
auto append(Node::Object node) -> Node::Object {
|
||||
@@ -47,7 +47,7 @@ struct Object : shared_pointer_this<Object> {
|
||||
template<typename T, typename... P>
|
||||
auto append(P&&... p) -> Node::Object {
|
||||
using Type = typename T::type;
|
||||
return append(shared_pointer<Type>::create(forward<P>(p)...));
|
||||
return append(shared_pointer<Type>::create(std::forward<P>(p)...));
|
||||
}
|
||||
|
||||
auto remove(Node::Object node) -> void {
|
||||
|
||||
@@ -82,10 +82,10 @@ protected:
|
||||
u32 _rotation = 0; //counter-clockwise (90 = left, 270 = right)
|
||||
|
||||
function<n64 (n32)> _color;
|
||||
unique_pointer<u32> _inputA;
|
||||
unique_pointer<u32> _inputB;
|
||||
unique_pointer<u32> _output;
|
||||
unique_pointer<u32> _rotate;
|
||||
unique_pointer<u32[]> _inputA;
|
||||
unique_pointer<u32[]> _inputB;
|
||||
unique_pointer<u32[]> _output;
|
||||
unique_pointer<u32[]> _rotate;
|
||||
unique_pointer<u32[]> _palette;
|
||||
vector<Node::Video::Sprite> _sprites;
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ inline auto Thread::synchronize(Thread& thread, P&&... p) -> void {
|
||||
co_switch(thread.handle());
|
||||
}
|
||||
//convenience: allow synchronizing multiple threads with one function call.
|
||||
if constexpr(sizeof...(p) > 0) synchronize(forward<P>(p)...);
|
||||
if constexpr(sizeof...(p) > 0) synchronize(std::forward<P>(p)...);
|
||||
}
|
||||
|
||||
inline auto Thread::serialize(serializer& s) -> void {
|
||||
|
||||
@@ -196,9 +196,11 @@ auto AY38910::write(n8 data) -> void {
|
||||
envelope.output = !envelope.attacking ? 15 : 0;
|
||||
break;
|
||||
case 14:
|
||||
portA.data = data;
|
||||
writeIO(0, data);
|
||||
break;
|
||||
case 15:
|
||||
portB.data = data;
|
||||
writeIO(1, data);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -63,6 +63,10 @@ auto SN76489::write(n8 data) -> void {
|
||||
case 0: tone0.pitch.bit(4,9) = data.bit(0,5); break;
|
||||
case 1: tone1.pitch.bit(4,9) = data.bit(0,5); break;
|
||||
case 2: tone2.pitch.bit(4,9) = data.bit(0,5); noise.pitch = tone2.pitch; break;
|
||||
case 3: noise.rate = data.bit(0,1);
|
||||
noise.enable = data.bit(2);
|
||||
noise.lfsr = 0x8000;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +1,84 @@
|
||||
auto YM2610::read(n2 address) -> n8 {
|
||||
switch(address) {
|
||||
case 0: return fm.readStatus();
|
||||
case 1: return ssg.read();
|
||||
|
||||
}
|
||||
|
||||
debug(unimplemented, "[YM2610] read ", hex(address));
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto YM2610::write(n2 address, n8 data) -> void {
|
||||
switch(address) {
|
||||
case 0: register = 0x000 | data; return;
|
||||
case 0: {
|
||||
registerAddress = 0x000 | data;
|
||||
if (registerAddress <= 0xf) ssg.select(registerAddress);
|
||||
else fm.writeAddress(registerAddress);
|
||||
return;
|
||||
}
|
||||
case 1: return writeLower(data);
|
||||
case 2: register = 0x100 | data; return;
|
||||
case 2: registerAddress = 0x100 | data; fm.writeAddress(registerAddress); return;
|
||||
case 3: return writeUpper(data);
|
||||
}
|
||||
}
|
||||
|
||||
auto YM2610::writeLower(n8 data) -> void {
|
||||
switch(register) {
|
||||
switch(registerAddress) {
|
||||
case 0x000 ... 0x00f:
|
||||
ssg.select(register);
|
||||
ssg.write(data);
|
||||
return;
|
||||
case 0x010 ... 0x01b:
|
||||
//ADPCM-B
|
||||
return;
|
||||
case 0x010:
|
||||
pcmB.repeat = data.bit(4);
|
||||
if(data.bit(0)) pcmB.power();
|
||||
if(data.bit(7)) pcmB.beginPlay();
|
||||
else pcmB.playing = 0;
|
||||
return;
|
||||
case 0x011: pcmB.left = data.bit(7);
|
||||
pcmB.right = data.bit(6); return;
|
||||
case 0x012: pcmB.startAddress.bit( 8, 15) = data; return;
|
||||
case 0x013: pcmB.startAddress.bit(16, 23) = data; return;
|
||||
case 0x014: pcmB.endAddress.bit ( 8, 15) = data; return;
|
||||
case 0x015: pcmB.endAddress.bit (16, 23) = data; return;
|
||||
case 0x016 ... 0x018: debug(unimplemented, "[YM2610::ADPCMB] Write ", hex(registerAddress), " = ", data);
|
||||
case 0x019: pcmB.delta.bit ( 0, 7) = data; return;
|
||||
case 0x01a: pcmB.delta.bit ( 8, 15) = data; return;
|
||||
case 0x01b: pcmB.volume = data; return;
|
||||
case 0x01c:
|
||||
//EOS
|
||||
debug(unimplemented, "[YM2610::EOS] Write ", hex(registerAddress), " = ", data);
|
||||
return;
|
||||
case 0x01d ... 0x1ff:
|
||||
fm.writeAddress(register);
|
||||
fm.writeData(data);
|
||||
return;
|
||||
}
|
||||
unreachable;
|
||||
|
||||
debug(unimplemented, "[YM2610] Write ", hex(registerAddress), " = ", data);
|
||||
}
|
||||
|
||||
auto YM2610::writeUpper(n8 data) -> void {
|
||||
switch(register) {
|
||||
case 0x000 ... 0x12f:
|
||||
//ADPCM-A
|
||||
return;
|
||||
case 0x130 ... 0x1ff:
|
||||
fm.writeAddress(register);
|
||||
fm.writeData(data);
|
||||
switch(registerAddress) {
|
||||
case 0x100:
|
||||
for(auto n : range(6)) {
|
||||
if(!data.bit(n)) continue;
|
||||
if(data.bit(7)) {
|
||||
pcmA.channels[n].keyOff();
|
||||
continue;
|
||||
}
|
||||
|
||||
pcmA.channels[n].keyOn();
|
||||
}
|
||||
return;
|
||||
case 0x101: pcmA.volume = data.bit(0, 5); return;
|
||||
case 0x108 ... 0x10d: pcmA.channels[registerAddress - 0x108].volume = data.bit(0,4);
|
||||
pcmA.channels[registerAddress - 0x108].left = data.bit(7);
|
||||
pcmA.channels[registerAddress - 0x108].right = data.bit(6); return;
|
||||
case 0x110 ... 0x115: pcmA.channels[registerAddress - 0x110].startAddress.bit( 8, 15) = data; return;
|
||||
case 0x118 ... 0x11d: pcmA.channels[registerAddress - 0x118].startAddress.bit(16, 23) = data; return;
|
||||
case 0x120 ... 0x125: pcmA.channels[registerAddress - 0x120].endAddress.bit ( 8, 15) = data; return;
|
||||
case 0x128 ... 0x12d: pcmA.channels[registerAddress - 0x128].endAddress.bit (16, 23) = data; return;
|
||||
case 0x130 ... 0x1ff: fm.writeAddress(registerAddress); fm.writeData(data); return;
|
||||
}
|
||||
|
||||
debug(unimplemented, "[YM2610] Write ", hex(registerAddress), " = ", data);
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
auto YM2610::PCMA::power() -> void {
|
||||
for(auto c : range(6)) {
|
||||
channels[c].startAddress.bit(0, 7) = 0x00;
|
||||
channels[c].endAddress.bit(0, 7) = 0x00;
|
||||
channels[c].decodeAccumulator = 0;
|
||||
channels[c].decodeStep = 0;
|
||||
}
|
||||
|
||||
i16 stepSize[] = {
|
||||
16, 17, 19, 21, 23, 25, 28, 31, 34, 37,
|
||||
41, 45, 50, 55, 60, 66, 73, 80, 88, 97,
|
||||
107, 118, 130, 143, 157, 173, 190, 209, 230, 253,
|
||||
279, 307, 337, 371, 408, 449, 494, 544, 598, 658,
|
||||
724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552
|
||||
};
|
||||
|
||||
// Generate a decode table
|
||||
for (int step = 0; step < 49; step++) {
|
||||
for (int nibble = 0; nibble < 16; nibble++) {
|
||||
int value = (2 * (nibble & 0x07) + 1) * stepSize[step] / 8;
|
||||
decodeTable[step * 16 + nibble] = ((nibble & 0x08) != 0) ? -value : value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto YM2610::PCMA::clock() -> array<i16[2]> {
|
||||
f32 left = 0;
|
||||
f32 right = 0;
|
||||
|
||||
for(auto c : range(6)) {
|
||||
if(!channels[c].playing) continue;
|
||||
if(((channels[c].currentAddress ^ (channels[c].endAddress + (1 << 8))) & 0xfffff) == 0) {
|
||||
channels[c].playing = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto sample = channels[c].decode(self.readPCMA(channels[c].currentAddress));
|
||||
|
||||
int totalVolume = (volume ^ 0x3f) + (channels[c].volume ^ 0x1f);
|
||||
int volumeMultiplier = 15 - (totalVolume & 7);
|
||||
int volumeShift = 5 + (totalVolume >> 3);
|
||||
|
||||
sample = ((sample * volumeMultiplier) >> volumeShift) & ~3;
|
||||
|
||||
if(channels[c].left) left += sample / 2047.0f;
|
||||
if(channels[c].right) right += sample / 2047.0f;
|
||||
}
|
||||
|
||||
return {sclamp<16>(left * 32768.0), sclamp<16>(right * 32768.0)};
|
||||
}
|
||||
|
||||
auto YM2610::PCMA::Channel::keyOn() -> void {
|
||||
playing = 1;
|
||||
currentAddress = startAddress;
|
||||
decodeAccumulator = 0;
|
||||
decodeStep = 0;
|
||||
currentNibble = 0;
|
||||
}
|
||||
|
||||
auto YM2610::PCMA::Channel::keyOff() -> void {
|
||||
playing = 0;
|
||||
}
|
||||
|
||||
auto YM2610::PCMA::Channel::decode(n8 value) -> i12 {
|
||||
int stepAdjust[] = { -1, -1, -1, -1, 2, 5, 7, 9, -1, -1, -1, -1, 2, 5, 7, 9 };
|
||||
|
||||
value = currentNibble ? value.bit(0, 3) : value.bit(4, 7);
|
||||
if(currentNibble) currentAddress++;
|
||||
currentNibble = !currentNibble;
|
||||
|
||||
decodeAccumulator += self.decodeTable[decodeStep + value];
|
||||
|
||||
decodeStep += stepAdjust[value & 7] * 16;
|
||||
decodeStep = std::clamp((int)decodeStep, 0, 48 * 16);
|
||||
|
||||
return decodeAccumulator;
|
||||
}
|
||||
|
||||
auto YM2610::PCMB::power() -> void {
|
||||
currentNibble = 0;
|
||||
currentAddress = 0;
|
||||
decodeAccumulator = 0;
|
||||
previousAccumulator = 0;
|
||||
decodePosition = 0;
|
||||
decodeStep = 127;
|
||||
volume = 255;
|
||||
}
|
||||
|
||||
auto YM2610::PCMB::clock() -> array<i16[2]> {
|
||||
f32 leftSample = 0;
|
||||
f32 rightSample = 0;
|
||||
|
||||
decode();
|
||||
|
||||
i32 sample = (previousAccumulator * (i32)((decodePosition ^ 0xffff) + 1) + decodeAccumulator * (i32)(decodePosition)) >> 16;
|
||||
sample = (sample * (i32)volume) >> 9;
|
||||
|
||||
if(left) leftSample += (sample / 65535.0);
|
||||
if(right) rightSample += (sample / 65535.0);
|
||||
|
||||
return {sclamp<16>(leftSample * 32768.0), sclamp<16>(rightSample * 32768.0)};
|
||||
}
|
||||
|
||||
auto YM2610::PCMB::decode() -> void {
|
||||
if(!playing) {
|
||||
return;
|
||||
}
|
||||
|
||||
u8 stepSize[8] = { 57, 57, 57, 57, 77, 102, 128, 153 };
|
||||
|
||||
u32 position = decodePosition + delta;
|
||||
decodePosition = (u16)position;
|
||||
if(position < 0x10000) return;
|
||||
|
||||
n8 value = self.readPCMB(currentAddress);
|
||||
value = currentNibble ? value.bit(0, 3) : value.bit(4, 7);
|
||||
currentNibble = !currentNibble;
|
||||
|
||||
if(currentNibble == 0) {
|
||||
currentAddress++;
|
||||
if(currentAddress == endAddress) {
|
||||
if(repeat) beginPlay();
|
||||
else {
|
||||
decodeAccumulator = 0;
|
||||
previousAccumulator = 0;
|
||||
playing = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
previousAccumulator = decodeAccumulator;
|
||||
|
||||
i32 _delta = (value.bit(0, 2) * 2 + 1) * decodeStep / 8;
|
||||
if(value.bit(3)) _delta = -_delta;
|
||||
|
||||
decodeAccumulator = sclamp<16>(decodeAccumulator + _delta);
|
||||
|
||||
decodeStep = std::clamp(((int)decodeStep * stepSize[value.bit(0, 2)]) / 64, 127, 24576);
|
||||
}
|
||||
|
||||
auto YM2610::PCMB::beginPlay() -> void {
|
||||
currentAddress = startAddress;
|
||||
currentNibble = 0;
|
||||
decodeAccumulator = 0;
|
||||
previousAccumulator = 0;
|
||||
decodePosition = 0;
|
||||
decodeStep = 127;
|
||||
playing = 1;
|
||||
}
|
||||
@@ -1,5 +1,41 @@
|
||||
auto YM2610::serialize(serializer& s) -> void {
|
||||
s(register);
|
||||
s(registerAddress);
|
||||
s(fm);
|
||||
s(ssg);
|
||||
s(pcmA);
|
||||
s(pcmB);
|
||||
}
|
||||
|
||||
auto YM2610::PCMA::serialize(serializer& s) -> void {
|
||||
for(auto n : range(6)) {
|
||||
s(channels[n].playing);
|
||||
s(channels[n].ended);
|
||||
s(channels[n].left);
|
||||
s(channels[n].right);
|
||||
s(channels[n].volume);
|
||||
s(channels[n].startAddress);
|
||||
s(channels[n].endAddress);
|
||||
s(channels[n].currentAddress);
|
||||
s(channels[n].decodeAccumulator);
|
||||
s(channels[n].decodeStep);
|
||||
s(channels[n].currentNibble);
|
||||
}
|
||||
}
|
||||
|
||||
auto YM2610::PCMB::serialize(serializer& s) -> void {
|
||||
s(playing);
|
||||
s(repeat);
|
||||
s(left);
|
||||
s(right);
|
||||
s(startAddress);
|
||||
s(endAddress);
|
||||
s(delta);
|
||||
s(volume);
|
||||
|
||||
s(currentAddress);
|
||||
s(currentNibble);
|
||||
s(decodeAccumulator);
|
||||
s(decodePosition);
|
||||
s(previousAccumulator);
|
||||
s(decodeStep);
|
||||
}
|
||||
@@ -4,11 +4,14 @@
|
||||
namespace ares {
|
||||
|
||||
#include "io.cpp"
|
||||
#include "pcm.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto YM2610::power() -> void {
|
||||
fm.power();
|
||||
ssg.power();
|
||||
pcmA.power();
|
||||
pcmB.power();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,10 +20,74 @@ struct YM2610 {
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
virtual auto readPCMA(u32 address) -> u8 { return 0; }
|
||||
virtual auto readPCMB(u32 address) -> u8 { return 0; }
|
||||
|
||||
protected:
|
||||
n9 register;
|
||||
n9 registerAddress;
|
||||
YM2612 fm;
|
||||
YM2149 ssg;
|
||||
|
||||
struct PCMA {
|
||||
YM2610& self;
|
||||
|
||||
// pcm.cpp
|
||||
auto power() -> void;
|
||||
auto clock() -> array<i16[2]>;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
struct Channel {
|
||||
PCMA& self;
|
||||
auto keyOn() -> void;
|
||||
auto keyOff() -> void;
|
||||
auto decode(n8) -> i12;
|
||||
n1 playing;
|
||||
n1 ended;
|
||||
n1 left;
|
||||
n1 right;
|
||||
n5 volume;
|
||||
n32 startAddress;
|
||||
n32 endAddress;
|
||||
n32 currentAddress;
|
||||
i12 decodeAccumulator;
|
||||
i32 decodeStep;
|
||||
bool currentNibble;
|
||||
} channels[6]{{*this}, {*this}, {*this}, {*this}, {*this}, {*this}};
|
||||
|
||||
n6 volume;
|
||||
int decodeTable[16 * 49];
|
||||
} pcmA{*this};
|
||||
|
||||
struct PCMB {
|
||||
YM2610& self;
|
||||
|
||||
// pcm.cpp
|
||||
auto power() -> void;
|
||||
auto clock() -> array<i16[2]>;
|
||||
auto decode() -> void;
|
||||
auto beginPlay() -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
n1 playing;
|
||||
n1 repeat;
|
||||
n1 left;
|
||||
n2 right;
|
||||
n32 startAddress;
|
||||
n32 endAddress;
|
||||
n16 delta;
|
||||
n8 volume;
|
||||
|
||||
n32 currentAddress;
|
||||
bool currentNibble;
|
||||
i32 decodeAccumulator;
|
||||
u32 decodePosition;
|
||||
i32 previousAccumulator;
|
||||
i32 decodeStep;
|
||||
} pcmB{*this};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
auto YM2612::Channel::Operator::trigger(bool state) -> void {
|
||||
auto YM2612::Channel::Operator::updateKeyState(bool state) -> void {
|
||||
if(keyOn == state) return; //no change
|
||||
|
||||
keyOn = state;
|
||||
envelope.state = Release;
|
||||
updateEnvelope();
|
||||
|
||||
if(keyOn) {
|
||||
//restart phase and envelope generators
|
||||
@@ -13,42 +10,46 @@ auto YM2612::Channel::Operator::trigger(bool state) -> void {
|
||||
updateEnvelope();
|
||||
|
||||
if(envelope.rate >= 62) {
|
||||
//skip attack and possibly decay stages
|
||||
//skip attack phase
|
||||
envelope.value = 0;
|
||||
envelope.state = envelope.sustainLevel ? Decay : Sustain;
|
||||
updateEnvelope();
|
||||
}
|
||||
} else if(ssg.enable && ssg.attack != ssg.invert) {
|
||||
//SSG-EG key-off
|
||||
envelope.value = 0x200 - envelope.value;
|
||||
} else {
|
||||
envelope.state = Release;
|
||||
updateEnvelope();
|
||||
|
||||
if(ssg.enable && ssg.attack != ssg.invert) {
|
||||
//SSG-EG key-off
|
||||
envelope.value = 0x200 - envelope.value;
|
||||
}
|
||||
}
|
||||
|
||||
updateLevel();
|
||||
}
|
||||
|
||||
auto YM2612::Channel::Operator::runEnvelope() -> void {
|
||||
u32 sustain = envelope.sustainLevel < 15 ? envelope.sustainLevel << 5 : 0x3f0;
|
||||
updateKeyState(keyLine);
|
||||
if(ym2612.envelope.clock & (1 << envelope.divider) - 1) return;
|
||||
|
||||
u32 value = ym2612.envelope.clock >> envelope.divider;
|
||||
u32 step = envelope.steps >> ((~value & 7) << 2) & 0xf;
|
||||
if(ssg.enable) step <<= 2; //SSG results in a 4x faster envelope
|
||||
u32 sustain = envelope.sustainLevel < 15 ? envelope.sustainLevel << 5 : 0x1f << 5;
|
||||
|
||||
if(envelope.state == Attack) {
|
||||
u32 next = envelope.value + (~u16(envelope.value) * step >> 4) & 0x3ff;
|
||||
if(next <= envelope.value) {
|
||||
envelope.value = next;
|
||||
} else {
|
||||
envelope.value = 0;
|
||||
envelope.state = envelope.value < sustain ? Decay : Sustain;
|
||||
if(envelope.value == 0) {
|
||||
envelope.state = Decay;
|
||||
updateEnvelope();
|
||||
} else if(envelope.rate < 62) {
|
||||
// will stop updating if attack rate is increased to upper threshold during attack phase (confirmed behavior)
|
||||
envelope.value += ~u16(envelope.value) * step >> 4;
|
||||
}
|
||||
} else if(!ssg.enable || envelope.value < 0x200) {
|
||||
envelope.value = min(envelope.value + step, 0x3ff);
|
||||
}
|
||||
if(envelope.state != Attack) {
|
||||
if(envelope.state == Decay && envelope.value >= sustain) {
|
||||
envelope.state = Sustain;
|
||||
updateEnvelope();
|
||||
}
|
||||
if(ssg.enable) step = envelope.value < 0x200 ? step << 2 : 0; //SSG results in a 4x faster envelope
|
||||
envelope.value = min(envelope.value + step, 0x3ff);
|
||||
}
|
||||
|
||||
updateLevel();
|
||||
@@ -59,18 +60,19 @@ auto YM2612::Channel::Operator::runPhase() -> void {
|
||||
if(!(ssg.enable && envelope.value >= 0x200)) return; //SSG loop check
|
||||
|
||||
if(!ssg.hold && !ssg.alternate) phase.value = 0;
|
||||
if(!ssg.hold || ssg.attack == ssg.invert) ssg.invert ^= ssg.alternate;
|
||||
if(!(ssg.hold && ssg.invert)) ssg.invert ^= ssg.alternate;
|
||||
|
||||
if(envelope.state == Attack) {
|
||||
//do nothing; SSG is meant to skip the attack phase
|
||||
} else if(envelope.state != Release && !ssg.hold) {
|
||||
//if still looping, reset the envelope
|
||||
envelope.state = Attack;
|
||||
if(envelope.attackRate >= 62) {
|
||||
envelope.value = 0;
|
||||
envelope.state = envelope.sustainLevel ? Decay : Sustain;
|
||||
}
|
||||
updateEnvelope();
|
||||
|
||||
if(envelope.rate >= 62) {
|
||||
//skip attack phase
|
||||
envelope.value = 0;
|
||||
}
|
||||
} else if(envelope.state == Release || (ssg.hold && ssg.attack == ssg.invert)) {
|
||||
//clear envelope once finished
|
||||
envelope.value = 0x3ff;
|
||||
@@ -115,11 +117,9 @@ auto YM2612::Channel::Operator::updatePhase() -> void {
|
||||
u32 tuning = detune & 3 ? detunes[(detune & 3) - 1][ksr & 7] >> (3 - (ksr >> 3)) : 0;
|
||||
|
||||
u32 lfo = ym2612.lfo.clock >> 2 & 0x1f;
|
||||
u32 pm = 4 * vibratos[channel.vibrato][lfo & 15] * (-lfo >> 4);
|
||||
u32 msb = 10;
|
||||
while(msb > 4 && ~pitch.value & 1 << msb) msb--;
|
||||
s32 pm = (pitch.value * vibratos[channel.vibrato][lfo & 15] >> 9) * (lfo > 15 ? -1 : 1);
|
||||
|
||||
phase.delta = pitch.value + (pm >> 10 - msb) << 6 >> 7 - octave.value;
|
||||
phase.delta = pitch.value + pm << 6 >> 7 - octave.value;
|
||||
phase.delta = (!detune.bit(2) ? phase.delta + tuning : phase.delta - tuning) & 0x1ffff;
|
||||
phase.delta = (multiple ? phase.delta * multiple : phase.delta >> 1) & 0xfffff;
|
||||
}
|
||||
@@ -147,6 +147,7 @@ auto YM2612::Channel::power() -> void {
|
||||
|
||||
for(auto& op : operators) {
|
||||
op.keyOn = 0;
|
||||
op.keyLine = 0;
|
||||
op.lfoEnable = 0;
|
||||
op.detune = 0;
|
||||
op.multiple = 0;
|
||||
|
||||
@@ -14,6 +14,8 @@ auto YM2612::writeData(n8 data) -> void {
|
||||
case 0x022: {
|
||||
lfo.rate = data.bit(0,2);
|
||||
lfo.enable = data.bit(3);
|
||||
lfo.clock = 0;
|
||||
lfo.divider = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -37,11 +39,12 @@ auto YM2612::writeData(n8 data) -> void {
|
||||
|
||||
//timer control
|
||||
case 0x027: {
|
||||
//d6,d7 = mode (unimplemented; treated as mode 0 always)
|
||||
|
||||
//reload period on 0->1 transition
|
||||
if(!timerA.enable && data.bit(0)) timerA.counter = timerA.period;
|
||||
if(!timerB.enable && data.bit(1)) timerB.counter = timerB.period;
|
||||
if(!timerB.enable && data.bit(1)) {
|
||||
timerB.counter = timerB.period;
|
||||
timerB.divider = 0;
|
||||
}
|
||||
|
||||
timerA.enable = data.bit(0);
|
||||
timerB.enable = data.bit(1);
|
||||
@@ -51,6 +54,7 @@ auto YM2612::writeData(n8 data) -> void {
|
||||
if(data.bit(4)) timerA.line = 0;
|
||||
if(data.bit(5)) timerB.line = 0;
|
||||
|
||||
//d7 is enable bit for CSM mode (not yet implemented)
|
||||
channels[2].mode = data.bit(6,7);
|
||||
for(auto& op : channels[2].operators) op.updatePitch();
|
||||
|
||||
@@ -64,10 +68,10 @@ auto YM2612::writeData(n8 data) -> void {
|
||||
if(index == 3 || index == 7) break;
|
||||
if(index >= 4) index--;
|
||||
|
||||
channels[index][0].trigger(data.bit(4));
|
||||
channels[index][1].trigger(data.bit(5));
|
||||
channels[index][2].trigger(data.bit(6));
|
||||
channels[index][3].trigger(data.bit(7));
|
||||
channels[index][0].keyLine = data.bit(4);
|
||||
channels[index][1].keyLine = data.bit(5);
|
||||
channels[index][2].keyLine = data.bit(6);
|
||||
channels[index][3].keyLine = data.bit(7);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -145,10 +149,11 @@ auto YM2612::writeData(n8 data) -> void {
|
||||
|
||||
//SSG-EG
|
||||
case 0x090: {
|
||||
op.ssg.hold = data.bit(0);
|
||||
op.ssg.alternate = data.bit(1);
|
||||
op.ssg.attack = data.bit(2);
|
||||
op.ssg.enable = data.bit(3);
|
||||
op.ssg.hold = op.ssg.enable ? data.bit(0) : 0;
|
||||
op.ssg.alternate = op.ssg.enable ? data.bit(1) : 0;
|
||||
op.ssg.attack = op.ssg.enable ? data.bit(2) : 0;
|
||||
channel[index].updateLevel();
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ auto YM2612::Channel::serialize(serializer& s) -> void {
|
||||
|
||||
auto YM2612::Channel::Operator::serialize(serializer& s) -> void {
|
||||
s(keyOn);
|
||||
s(keyLine);
|
||||
s(lfoEnable);
|
||||
s(detune);
|
||||
s(multiple);
|
||||
|
||||
@@ -27,7 +27,7 @@ auto YM2612::clock() -> array<i16[2]> {
|
||||
auto wave = [&](u32 n, u32 modulation) -> s32 {
|
||||
s32 x = (modulation >> 1) + (op[n].phase.value >> 10);
|
||||
s32 y = sine[x & 0x3ff] + op[n].outputLevel;
|
||||
return y < 0x2000 ? pow2[y & 0x1ff] << 2 >> (y >> 9) : 0;
|
||||
return y < 0x1a00 ? pow2[y & 0x1ff] << 2 >> (y >> 9) : 0; // -78 dB floor
|
||||
};
|
||||
|
||||
s32 feedback = modMask & op[0].output + op[0].prior >> 9 - channel.feedback;
|
||||
@@ -124,7 +124,7 @@ auto YM2612::clock() -> array<i16[2]> {
|
||||
|
||||
if(++envelope.divider == 3) {
|
||||
envelope.divider = 0;
|
||||
envelope.clock++;
|
||||
if(!++envelope.clock) ++envelope.clock; // 12-bit counter: 1..4095 - zero-value is skipped (confirmed behavior)
|
||||
}
|
||||
|
||||
for(auto& channel : channels) {
|
||||
|
||||
@@ -36,7 +36,7 @@ protected:
|
||||
} dac;
|
||||
|
||||
struct Envelope {
|
||||
n32 clock = 0;
|
||||
n12 clock = 0;
|
||||
n32 divider = 0;
|
||||
} envelope;
|
||||
|
||||
@@ -91,7 +91,7 @@ protected:
|
||||
Operator(Channel& channel) : channel(channel), ym2612(channel.ym2612) {}
|
||||
|
||||
//channel.cpp
|
||||
auto trigger(bool) -> void;
|
||||
auto updateKeyState(bool) -> void;
|
||||
|
||||
auto runEnvelope() -> void;
|
||||
auto runPhase() -> void;
|
||||
@@ -105,6 +105,7 @@ protected:
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
n1 keyOn = 0;
|
||||
n1 keyLine = 0;
|
||||
n1 lfoEnable = 0;
|
||||
n3 detune = 0;
|
||||
n4 multiple = 0;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
auto M68000::instructionABCD(EffectiveAddress from, EffectiveAddress with) -> void {
|
||||
if(from.mode == DataRegisterDirect) idle(2);
|
||||
auto target = read<Byte, Hold, Fast>(with);
|
||||
auto source = read<Byte>(from);
|
||||
auto target = read<Byte, Hold, Fast>(with);
|
||||
auto result = source + target + r.x;
|
||||
bool c = false;
|
||||
bool v = false;
|
||||
@@ -98,8 +98,8 @@ template<u32 Size> auto M68000::instructionADDX(EffectiveAddress from, Effective
|
||||
if constexpr(Size == Long) {
|
||||
if(from.mode == DataRegisterDirect) idle(4);
|
||||
}
|
||||
auto target = read<Size, Hold, Fast>(with);
|
||||
auto source = read<Size>(from);
|
||||
auto target = read<Size, Hold, Fast>(with);
|
||||
auto result = ADD<Size, Extend>(source, target);
|
||||
prefetch();
|
||||
write<Size>(with, result);
|
||||
@@ -491,8 +491,8 @@ auto M68000::instructionDIVS(EffectiveAddress from, DataRegister with) -> void {
|
||||
return;
|
||||
}
|
||||
|
||||
r.z = quotient == 0;
|
||||
r.n = quotient < 0;
|
||||
r.z = clip<Word>(quotient) == 0;
|
||||
r.n = sign<Word>(quotient) < 0;
|
||||
|
||||
idle(ticks);
|
||||
write<Long>(with, dividend | quotient);
|
||||
@@ -531,8 +531,8 @@ auto M68000::instructionDIVU(EffectiveAddress from, DataRegister with) -> void {
|
||||
ticks += force ? 6 : carry ? 4 : 2;
|
||||
quotient = quotient << 1 | carry;
|
||||
|
||||
r.z = quotient == 0;
|
||||
r.n = quotient < 0;
|
||||
r.z = clip<Word>(quotient) == 0;
|
||||
r.n = sign<Word>(quotient) < 0;
|
||||
|
||||
idle(ticks);
|
||||
write<Long>(with, dividend | quotient);
|
||||
@@ -1134,8 +1134,8 @@ auto M68000::instructionRTS() -> void {
|
||||
|
||||
auto M68000::instructionSBCD(EffectiveAddress from, EffectiveAddress with) -> void {
|
||||
if(from.mode == DataRegisterDirect) idle(2);
|
||||
auto target = read<Byte, Hold, Fast>(with);
|
||||
auto source = read<Byte>(from);
|
||||
auto target = read<Byte, Hold, Fast>(with);
|
||||
auto result = target - source - r.x;
|
||||
bool c = false;
|
||||
bool v = false;
|
||||
@@ -1256,8 +1256,8 @@ template<u32 Size> auto M68000::instructionSUBX(EffectiveAddress from, Effective
|
||||
if constexpr(Size == Long) {
|
||||
if(from.mode == DataRegisterDirect) idle(4);
|
||||
}
|
||||
auto target = read<Size, Hold, Fast>(with);
|
||||
auto source = read<Size>(from);
|
||||
auto target = read<Size, Hold, Fast>(with);
|
||||
auto result = SUB<Size, Extend>(source, target);
|
||||
prefetch();
|
||||
write<Size>(with, result);
|
||||
|
||||
@@ -17,6 +17,16 @@ auto MOS6502::algorithmADC(n8 i) -> n8 {
|
||||
return 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(n8 i) -> n8 {
|
||||
n8 o = A & i;
|
||||
Z = o == 0;
|
||||
@@ -24,6 +34,29 @@ auto MOS6502::algorithmAND(n8 i) -> n8 {
|
||||
return o;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmANC(n8 i) -> n8 {
|
||||
n8 o = A & i;
|
||||
Z = o == 0;
|
||||
N = o.bit(7);
|
||||
C = N;
|
||||
return o;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmARR(n8 i) -> n8 {
|
||||
n8 o = A & i;
|
||||
Z = o == 0;
|
||||
N = o.bit(7);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmASL(n8 i) -> n8 {
|
||||
C = i.bit(7);
|
||||
i <<= 1;
|
||||
@@ -32,6 +65,23 @@ auto MOS6502::algorithmASL(n8 i) -> n8 {
|
||||
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);
|
||||
@@ -142,3 +192,15 @@ auto MOS6502::algorithmSBC(n8 i) -> n8 {
|
||||
N = o.bit(7);
|
||||
return o;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmSLO(n8 i) -> n8 {
|
||||
C = i.bit(7);
|
||||
i <<= 1;
|
||||
Z = i == 0;
|
||||
N = i.bit(7);
|
||||
|
||||
A |= i;
|
||||
Z = A == 0;
|
||||
N = A.bit(7);
|
||||
return i;
|
||||
}
|
||||
@@ -23,23 +23,29 @@ auto MOS6502::instruction() -> void {
|
||||
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)
|
||||
@@ -48,6 +54,7 @@ auto MOS6502::instruction() -> void {
|
||||
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))
|
||||
@@ -70,6 +77,7 @@ auto MOS6502::instruction() -> void {
|
||||
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))
|
||||
@@ -92,6 +100,7 @@ auto MOS6502::instruction() -> void {
|
||||
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))
|
||||
@@ -136,11 +145,13 @@ auto MOS6502::instruction() -> void {
|
||||
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)
|
||||
@@ -159,6 +170,7 @@ auto MOS6502::instruction() -> void {
|
||||
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))
|
||||
@@ -182,6 +194,7 @@ auto MOS6502::instruction() -> void {
|
||||
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))
|
||||
@@ -199,6 +212,7 @@ auto MOS6502::instruction() -> void {
|
||||
}
|
||||
|
||||
//unimplemented instruction
|
||||
debug(unusual, "[6502] unimplemented opcode ", hex(code));
|
||||
return instructionNoOperation();
|
||||
}
|
||||
|
||||
|
||||
@@ -112,6 +112,11 @@ auto MOS6502::instructionIndirectYRead(fp alu, n8& data) -> void {
|
||||
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);
|
||||
|
||||
@@ -31,7 +31,12 @@ struct MOS6502 {
|
||||
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;
|
||||
@@ -45,6 +50,7 @@ struct MOS6502 {
|
||||
auto algorithmROL(n8) -> n8;
|
||||
auto algorithmROR(n8) -> n8;
|
||||
auto algorithmSBC(n8) -> n8;
|
||||
auto algorithmSLO(n8) -> n8;
|
||||
|
||||
//instruction.cpp
|
||||
auto interrupt() -> void;
|
||||
@@ -66,6 +72,7 @@ struct MOS6502 {
|
||||
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;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
template<typename... P>
|
||||
auto SH2::hint(P&&... p) const -> string {
|
||||
if(1) return {};
|
||||
if(1) return {"\e[0m\e[37m", forward<P>(p)..., "\e[0m"};
|
||||
return {forward<P>(p)...};
|
||||
if(1) return {"\e[0m\e[37m", std::forward<P>(p)..., "\e[0m"};
|
||||
return {std::forward<P>(p)...};
|
||||
}
|
||||
|
||||
auto SH2::disassembleInstruction() -> string {
|
||||
|
||||
@@ -34,22 +34,16 @@ auto SH2::push(u32 data) -> void {
|
||||
|
||||
auto SH2::interrupt(u8 level, u8 vector) -> void {
|
||||
push(SR);
|
||||
push(PC);
|
||||
push(PC - 4);
|
||||
jump(readLong(VBR + vector * 4) + 4);
|
||||
SR.I = level;
|
||||
}
|
||||
|
||||
auto SH2::exception(u8 vector) -> void {
|
||||
push(SR);
|
||||
push(PC - 2);
|
||||
jump(readLong(VBR + vector * 4) + 4);
|
||||
}
|
||||
|
||||
auto SH2::addressErrorCPU() -> void {
|
||||
static constexpr u8 vector = 0x09;
|
||||
SP &= ~3; //not accurate; but prevents infinite recursion
|
||||
push(SR);
|
||||
push(PC);
|
||||
push(PC - 4);
|
||||
jump(readLong(VBR + vector * 4) + 4);
|
||||
}
|
||||
|
||||
@@ -57,23 +51,23 @@ auto SH2::addressErrorDMA() -> void {
|
||||
static constexpr u8 vector = 0x0a;
|
||||
SP &= ~3; //not accurate; but prevents infinite recursion
|
||||
push(SR);
|
||||
push(PC);
|
||||
push(PC - 4);
|
||||
jump(readLong(VBR + vector * 4) + 4);
|
||||
}
|
||||
|
||||
auto SH2::illegalInstruction() -> void {
|
||||
if(inDelaySlot()) return illegalSlotInstruction();
|
||||
debug(unusual, "[SH2] illegal instruction: 0x", hex(busReadWord(PC - 4), 4L), " @ 0x", hex(PC - 4));
|
||||
static constexpr u8 vector = 0x04;
|
||||
push(SR);
|
||||
push(PC);
|
||||
push(PC - 4);
|
||||
jump(readLong(VBR + vector * 4) + 4);
|
||||
debug(unusual, "[SH2] illegal instruction: 0x", hex(busReadWord(PC - 4), 4L), " @ 0x", hex(PC - 4));
|
||||
}
|
||||
|
||||
auto SH2::illegalSlotInstruction() -> void {
|
||||
debug(unusual, "[SH2] illegal slot instruction: 0x", hex(busReadWord(PC - 4), 4L));
|
||||
static constexpr u8 vector = 0x06;
|
||||
push(SR);
|
||||
push(PC - 2);
|
||||
push(PPC - 4);
|
||||
jump(readLong(VBR + vector * 4) + 4);
|
||||
debug(unusual, "[SH2] illegal slot instruction: 0x", hex(busReadWord(PC - 4), 4L));
|
||||
}
|
||||
@@ -37,7 +37,7 @@ auto SH2::instruction() -> void {
|
||||
do {
|
||||
auto block = recompiler.block(PC - 4);
|
||||
block->execute(*this);
|
||||
} while (CCR <= recompiler.min_cycles);
|
||||
} while (CCR < cyclesUntilSync);
|
||||
|
||||
step(CCR);
|
||||
CCR = 0;
|
||||
|
||||
@@ -503,18 +503,21 @@ auto SH2::MOVI(u32 i, u32 n) -> void {
|
||||
}
|
||||
|
||||
//MOV.W @(disp,PC),Rn
|
||||
auto SH2::MOVWI(u32 d, u32 n) -> void {
|
||||
R[n] = (s16)readWord(PC + d * 2);
|
||||
noinline auto SH2::MOVWI(u32 d, u32 n) -> void {
|
||||
u32 pc = inDelaySlot() ? PPC - 2 : PC;
|
||||
R[n] = (s16)readWord(pc + d * 2);
|
||||
}
|
||||
|
||||
//MOV.L @(disp,PC),Rn
|
||||
auto SH2::MOVLI(u32 d, u32 n) -> void {
|
||||
R[n] = readLong((PC & ~3) + d * 4);
|
||||
noinline auto SH2::MOVLI(u32 d, u32 n) -> void {
|
||||
u32 pc = inDelaySlot() ? PPC - 2 : PC;
|
||||
R[n] = readLong((pc & ~3) + d * 4);
|
||||
}
|
||||
|
||||
//MOVA @(disp,PC),R0
|
||||
auto SH2::MOVA(u32 d) -> void {
|
||||
R[0] = (PC & ~3) + d * 4 - inDelaySlot() * 2;
|
||||
noinline auto SH2::MOVA(u32 d) -> void {
|
||||
u32 pc = inDelaySlot() ? PPC - 2 : PC;
|
||||
R[0] = (pc & ~3) + d * 4;
|
||||
}
|
||||
|
||||
//MOVT Rn
|
||||
@@ -602,7 +605,7 @@ auto SH2::ROTR(u32 n) -> void {
|
||||
|
||||
//RTE
|
||||
auto SH2::RTE() -> void {
|
||||
delaySlot(readLong(SP + 0));
|
||||
delaySlot(readLong(SP + 0) + 4);
|
||||
SR = readLong(SP + 4);
|
||||
SP += 8;
|
||||
}
|
||||
@@ -800,10 +803,7 @@ auto SH2::TAS(u32 n) -> void {
|
||||
) return (void)(exceptions |= AddressErrorCPU);
|
||||
}
|
||||
|
||||
auto cacheEnable = cache.enable;
|
||||
cache.enable = 0;
|
||||
u8 b = readByte(R[n]);
|
||||
cache.enable = cacheEnable;
|
||||
u8 b = readByte<Bus::Internal>(R[n]);
|
||||
SR.T = b == 0;
|
||||
writeByte(R[n], b | 0x80);
|
||||
}
|
||||
@@ -811,7 +811,7 @@ auto SH2::TAS(u32 n) -> void {
|
||||
//TRAPA #imm
|
||||
auto SH2::TRAPA(u32 i) -> void {
|
||||
push(SR);
|
||||
push(PC + 2);
|
||||
push(PC - 2);
|
||||
branch(readLong(VBR + i * 4) + 4);
|
||||
}
|
||||
|
||||
|
||||
@@ -74,13 +74,15 @@ auto SH2::Recompiler::emit(u32 address) -> Block* {
|
||||
return block;
|
||||
}
|
||||
|
||||
#define readByte &SH2::readByte
|
||||
#define readWord &SH2::readWord
|
||||
#define readLong &SH2::readLong
|
||||
#define writeByte &SH2::writeByte
|
||||
#define writeWord &SH2::writeWord
|
||||
#define writeLong &SH2::writeLong
|
||||
#define illegal &SH2::illegalInstruction
|
||||
#define readB &SH2::readByte<>
|
||||
#define readW &SH2::readWord<>
|
||||
#define readL &SH2::readLong<>
|
||||
#define writeB &SH2::writeByte<>
|
||||
#define writeW &SH2::writeWord<>
|
||||
#define writeL &SH2::writeLong<>
|
||||
#define readB &SH2::readByte<>
|
||||
#define writeB &SH2::writeByte<>
|
||||
#define illegal &SH2::illegalInstruction
|
||||
|
||||
auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
#define n (opcode >> 8 & 0x00f)
|
||||
@@ -97,7 +99,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
case 0x04: {
|
||||
add32(reg(1), Rn, R0);
|
||||
mov32(reg(2), Rm);
|
||||
call(writeByte);
|
||||
call(writeB);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -105,7 +107,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
case 0x05: {
|
||||
add32(reg(1), Rn, R0);
|
||||
mov32(reg(2), Rm);
|
||||
call(writeWord);
|
||||
call(writeW);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -113,7 +115,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
case 0x06: {
|
||||
add32(reg(1), Rn, R0);
|
||||
mov32(reg(2), Rm);
|
||||
call(writeLong);
|
||||
call(writeL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -126,7 +128,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
//MOV.B @(R0,Rm),Rn
|
||||
case 0x0c: {
|
||||
add32(reg(1), Rm, R0);
|
||||
call(readByte);
|
||||
call(readB);
|
||||
mov32_s8(reg(0), reg(0));
|
||||
mov32(Rn, reg(0));
|
||||
return 0;
|
||||
@@ -135,7 +137,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
//MOV.W @(R0,Rm),Rn
|
||||
case 0x0d: {
|
||||
add32(reg(1), Rm, R0);
|
||||
call(readWord);
|
||||
call(readW);
|
||||
mov32_s16(reg(0), reg(0));
|
||||
mov32(Rn, reg(0));
|
||||
return 0;
|
||||
@@ -144,7 +146,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
//MOV.L @(R0,Rm),Rn
|
||||
case 0x0e: {
|
||||
add32(reg(1), Rm, R0);
|
||||
call(readLong);
|
||||
call(readL);
|
||||
mov32(Rn, reg(0));
|
||||
return 0;
|
||||
}
|
||||
@@ -153,11 +155,11 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
case 0x0f: {
|
||||
mov32(reg(1), Rn);
|
||||
add32(Rn, reg(1), imm(4));
|
||||
call(readLong);
|
||||
call(readL);
|
||||
mov32(sreg(2), reg(0));
|
||||
mov32(reg(1), Rm);
|
||||
add32(Rm, reg(1), imm(4));
|
||||
call(readLong);
|
||||
call(readL);
|
||||
mov64_s32(reg(0), reg(0));
|
||||
mov64_s32(reg(1), sreg(2));
|
||||
mul64(reg(0), reg(0), reg(1));
|
||||
@@ -174,7 +176,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
case 0x10 ... 0x1f: {
|
||||
add32(reg(1), Rn, imm(d4*4));
|
||||
mov32(reg(2), Rm);
|
||||
call(writeLong);
|
||||
call(writeL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -182,7 +184,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
case 0x20: {
|
||||
mov32(reg(1), Rn);
|
||||
mov32(reg(2), Rm);
|
||||
call(writeByte);
|
||||
call(writeB);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -190,7 +192,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
case 0x21: {
|
||||
mov32(reg(1), Rn);
|
||||
mov32(reg(2), Rm);
|
||||
call(writeWord);
|
||||
call(writeW);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -198,7 +200,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
case 0x22: {
|
||||
mov32(reg(1), Rn);
|
||||
mov32(reg(2), Rm);
|
||||
call(writeLong);
|
||||
call(writeL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -207,7 +209,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
mov32(reg(2), Rm);
|
||||
sub32(reg(1), Rn, imm(1));
|
||||
mov32(Rn, reg(1));
|
||||
call(writeByte);
|
||||
call(writeB);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -216,7 +218,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
mov32(reg(2), Rm);
|
||||
sub32(reg(1), Rn, imm(2));
|
||||
mov32(Rn, reg(1));
|
||||
call(writeWord);
|
||||
call(writeW);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -225,7 +227,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
mov32(reg(2), Rm);
|
||||
sub32(reg(1), Rn, imm(4));
|
||||
mov32(Rn, reg(1));
|
||||
call(writeLong);
|
||||
call(writeL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -435,11 +437,11 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
case 0x4f: {
|
||||
mov32(reg(1), Rn);
|
||||
add32(Rn, reg(1), imm(2));
|
||||
call(readWord);
|
||||
call(readW);
|
||||
mov32(sreg(2), reg(0));
|
||||
mov32(reg(1), Rm);
|
||||
add32(Rm, reg(1), imm(2));
|
||||
call(readWord);
|
||||
call(readW);
|
||||
mov64_s16(reg(0), reg(0));
|
||||
mov64_s16(reg(1), sreg(2));
|
||||
mul64(reg(0), reg(0), reg(1));
|
||||
@@ -454,7 +456,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
//MOV.L @(disp,Rm),Rn
|
||||
case 0x50 ... 0x5f: {
|
||||
add32(reg(1), Rm, imm(d4*4));
|
||||
call(readLong);
|
||||
call(readL);
|
||||
mov32(Rn, reg(0));
|
||||
return 0;
|
||||
}
|
||||
@@ -462,7 +464,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
//MOV.B @Rm,Rn
|
||||
case 0x60: {
|
||||
mov32(reg(1), Rm);
|
||||
call(readByte);
|
||||
call(readB);
|
||||
mov32_s8(reg(0), reg(0));
|
||||
mov32(Rn, reg(0));
|
||||
return 0;
|
||||
@@ -471,7 +473,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
//MOV.W @Rm,Rn
|
||||
case 0x61: {
|
||||
mov32(reg(1), Rm);
|
||||
call(readWord);
|
||||
call(readW);
|
||||
mov32_s16(reg(0), reg(0));
|
||||
mov32(Rn, reg(0));
|
||||
return 0;
|
||||
@@ -480,7 +482,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
//MOV.L @Rm,Rn
|
||||
case 0x62: {
|
||||
mov32(reg(1), Rm);
|
||||
call(readLong);
|
||||
call(readL);
|
||||
mov32(Rn, reg(0));
|
||||
return 0;
|
||||
}
|
||||
@@ -497,7 +499,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
if(n != m) {
|
||||
add32(Rm, reg(1), imm(1));
|
||||
}
|
||||
call(readByte);
|
||||
call(readB);
|
||||
mov32_s8(reg(0), reg(0));
|
||||
mov32(Rn, reg(0));
|
||||
return 0;
|
||||
@@ -509,7 +511,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
if(n != m) {
|
||||
add32(Rm, reg(1), imm(2));
|
||||
}
|
||||
call(readWord);
|
||||
call(readW);
|
||||
mov32_s16(reg(0), reg(0));
|
||||
mov32(Rn, reg(0));
|
||||
return 0;
|
||||
@@ -521,7 +523,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
if (n != m) {
|
||||
add32(Rm, reg(1), imm(4));
|
||||
}
|
||||
call(readLong);
|
||||
call(readL);
|
||||
mov32(Rn, reg(0));
|
||||
return 0;
|
||||
}
|
||||
@@ -611,8 +613,14 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
|
||||
//MOV.W @(disp,PC),Rn
|
||||
case 0x90 ... 0x9f: {
|
||||
add32(reg(1), PC, imm(d8*2));
|
||||
call(readWord);
|
||||
auto delay = cmp32_jump(PPM, imm(0), flag_ne);
|
||||
mov32(reg(0), PC);
|
||||
auto tail = jump();
|
||||
setLabel(delay);
|
||||
sub32(reg(0), PPC, imm(2));
|
||||
setLabel(tail);
|
||||
add32(reg(1), reg(0), imm(d8*2));
|
||||
call(readW);
|
||||
mov32_s16(reg(0), reg(0));
|
||||
mov32(Rn, reg(0));
|
||||
return 0;
|
||||
@@ -637,9 +645,15 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
|
||||
//MOV.L @(disp,PC),Rn
|
||||
case 0xd0 ... 0xdf: {
|
||||
and32(reg(1), PC, imm(~3));
|
||||
add32(reg(1), reg(1), imm(d8*4));
|
||||
call(readLong);
|
||||
auto delay = cmp32_jump(PPM, imm(0), flag_ne);
|
||||
mov32(reg(0), PC);
|
||||
auto tail = jump();
|
||||
setLabel(delay);
|
||||
sub32(reg(0), PPC, imm(2));
|
||||
setLabel(tail);
|
||||
and32(reg(0), reg(0), imm(~3));
|
||||
add32(reg(1), reg(0), imm(d8*4));
|
||||
call(readL);
|
||||
mov32(Rn, reg(0));
|
||||
return 0;
|
||||
}
|
||||
@@ -673,7 +687,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
case 0x80: {
|
||||
add32(reg(1), Rm, imm(d4));
|
||||
mov32(reg(2), R0);
|
||||
call(writeByte);
|
||||
call(writeB);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -681,14 +695,14 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
case 0x81: {
|
||||
add32(reg(1), Rm, imm(d4*2));
|
||||
mov32(reg(2), R0);
|
||||
call(writeWord);
|
||||
call(writeW);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//MOV.B @(disp,Rn),R0
|
||||
case 0x84: {
|
||||
add32(reg(1), Rm, imm(d4));
|
||||
call(readByte);
|
||||
call(readB);
|
||||
mov32_s8(reg(0), reg(0));
|
||||
mov32(R0, reg(0));
|
||||
return 0;
|
||||
@@ -697,7 +711,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
//MOV.W @(disp,Rn),R0
|
||||
case 0x85: {
|
||||
add32(reg(1), Rm, imm(d4*2));
|
||||
call(readWord);
|
||||
call(readW);
|
||||
mov32_s16(reg(0), reg(0));
|
||||
mov32(R0, reg(0));
|
||||
return 0;
|
||||
@@ -750,7 +764,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
case 0xc0: {
|
||||
add32(reg(1), GBR, imm(d8));
|
||||
mov32(reg(2), R0);
|
||||
call(writeByte);
|
||||
call(writeB);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -758,7 +772,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
case 0xc1: {
|
||||
add32(reg(1), GBR, imm(d8*2));
|
||||
mov32(reg(2), R0);
|
||||
call(writeWord);
|
||||
call(writeW);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -766,7 +780,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
case 0xc2: {
|
||||
add32(reg(1), GBR, imm(d8*4));
|
||||
mov32(reg(2), R0);
|
||||
call(writeLong);
|
||||
call(writeL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -775,13 +789,13 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
getSR(reg(2));
|
||||
sub32(reg(1), R15, imm(4));
|
||||
mov32(R15, reg(1));
|
||||
call(writeLong);
|
||||
add32(reg(2), PC, imm(2));
|
||||
call(writeL);
|
||||
sub32(reg(2), PC, imm(2));
|
||||
sub32(reg(1), R15, imm(4));
|
||||
mov32(R15, reg(1));
|
||||
call(writeLong);
|
||||
call(writeL);
|
||||
add32(reg(1), VBR, imm(i * 4));
|
||||
call(readLong);
|
||||
call(readL);
|
||||
add32(reg(0), reg(0), imm(4));
|
||||
mov32(PPC, reg(0));
|
||||
mov32(PPM, imm(Branch::Take));
|
||||
@@ -791,7 +805,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
//MOV.B @(disp,GBR),R0
|
||||
case 0xc4: {
|
||||
add32(reg(1), GBR, imm(d8));
|
||||
call(readByte);
|
||||
call(readB);
|
||||
mov32_s8(reg(0), reg(0));
|
||||
mov32(R0, reg(0));
|
||||
return 0;
|
||||
@@ -800,7 +814,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
//MOV.W @(disp,GBR),R0
|
||||
case 0xc5: {
|
||||
add32(reg(1), GBR, imm(d8*2));
|
||||
call(readWord);
|
||||
call(readW);
|
||||
mov32_s16(reg(0), reg(0));
|
||||
mov32(R0, reg(0));
|
||||
return 0;
|
||||
@@ -809,19 +823,22 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
//MOV.L @(disp,GBR),R0
|
||||
case 0xc6: {
|
||||
add32(reg(1), GBR, imm(d8*4));
|
||||
call(readLong);
|
||||
call(readL);
|
||||
mov32(R0, reg(0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
//MOVA @(disp,PC),R0
|
||||
case 0xc7: {
|
||||
and32(reg(0), PC, imm(~3));
|
||||
auto delay = cmp32_jump(PPM, imm(0), flag_ne);
|
||||
mov32(reg(0), PC);
|
||||
auto tail = jump();
|
||||
setLabel(delay);
|
||||
sub32(reg(0), PPC, imm(2));
|
||||
setLabel(tail);
|
||||
and32(reg(0), reg(0), imm(~3));
|
||||
add32(reg(0), reg(0), imm(d8*4));
|
||||
mov32(R0, reg(0));
|
||||
auto skip = cmp32_jump(PPM, imm(0), flag_eq);
|
||||
sub32(R0, R0, imm(2));
|
||||
setLabel(skip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -854,7 +871,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
case 0xcc: {
|
||||
mov32(reg(1), GBR);
|
||||
add32(reg(1), reg(1), R0);
|
||||
call(readByte);
|
||||
call(readB);
|
||||
test32(reg(0), imm(i), set_z);
|
||||
mov32_f(T, flag_z);
|
||||
return 0;
|
||||
@@ -863,30 +880,30 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
//AND.B #imm,@(R0,GBR)
|
||||
case 0xcd: {
|
||||
add32(reg(1), GBR, R0);
|
||||
call(readByte);
|
||||
call(readB);
|
||||
and32(reg(2), reg(0), imm(i));
|
||||
add32(reg(1), GBR, R0);
|
||||
call(writeByte);
|
||||
call(writeB);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//XOR.B #imm,@(R0,GBR)
|
||||
case 0xce: {
|
||||
add32(reg(1), GBR, R0);
|
||||
call(readByte);
|
||||
call(readB);
|
||||
xor32(reg(2), reg(0), imm(i));
|
||||
add32(reg(1), GBR, R0);
|
||||
call(writeByte);
|
||||
call(writeB);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//OR.B #imm,@(R0,GBR)
|
||||
case 0xcf: {
|
||||
add32(reg(1), GBR, R0);
|
||||
call(readByte);
|
||||
call(readB);
|
||||
or32(reg(2), reg(0), imm(i));
|
||||
add32(reg(1), GBR, R0);
|
||||
call(writeByte);
|
||||
call(writeB);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -987,7 +1004,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
sub32(reg(1), Rn, imm(4));
|
||||
mov32(Rn, reg(1));
|
||||
mov32(reg(2), MACH);
|
||||
call(writeLong);
|
||||
call(writeL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -996,7 +1013,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
getSR(reg(2));
|
||||
sub32(reg(1), Rn, imm(4));
|
||||
mov32(Rn, reg(1));
|
||||
call(writeLong);
|
||||
call(writeL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1026,7 +1043,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
case 0x406: {
|
||||
mov32(reg(1), Rn);
|
||||
add32(Rn, reg(1), imm(4));
|
||||
call(readLong);
|
||||
call(readL);
|
||||
mov32(MACH, reg(0));
|
||||
return 0;
|
||||
}
|
||||
@@ -1035,7 +1052,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
case 0x407: {
|
||||
mov32(reg(1), Rn);
|
||||
add32(Rn, reg(1), imm(4));
|
||||
call(readLong);
|
||||
call(readL);
|
||||
setSR(reg(0));
|
||||
return 0;
|
||||
}
|
||||
@@ -1092,7 +1109,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
sub32(reg(1), Rn, imm(4));
|
||||
mov32(Rn, reg(1));
|
||||
mov32(reg(2), MACL);
|
||||
call(writeLong);
|
||||
call(writeL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1101,7 +1118,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
sub32(reg(1), Rn, imm(4));
|
||||
mov32(Rn, reg(1));
|
||||
mov32(reg(2), GBR);
|
||||
call(writeLong);
|
||||
call(writeL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1116,7 +1133,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
case 0x416: {
|
||||
mov32(reg(1), Rm);
|
||||
add32(Rm, reg(1), imm(4));
|
||||
call(readLong);
|
||||
call(readL);
|
||||
mov32(MACL, reg(0));
|
||||
return 0;
|
||||
}
|
||||
@@ -1125,7 +1142,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
case 0x417: {
|
||||
mov32(reg(1), Rm);
|
||||
add32(Rm, reg(1), imm(4));
|
||||
call(readLong);
|
||||
call(readL);
|
||||
mov32(GBR, reg(0));
|
||||
return 0;
|
||||
}
|
||||
@@ -1150,16 +1167,14 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
|
||||
//TAS @Rn
|
||||
case 0x41b: {
|
||||
and32(reg(1), Rn, imm(0x1fff'ffff));
|
||||
or32(reg(1), reg(1), imm(0x2000'0000));
|
||||
call(readByte);
|
||||
mov32(reg(1), Rn);
|
||||
call(&SH2::readByte<Bus::Internal>);
|
||||
test32(reg(0), reg(0), set_z);
|
||||
mov32_f(T, flag_z);
|
||||
or32(reg(0), reg(0), imm(0x80));
|
||||
mov32(reg(2), reg(0));
|
||||
and32(reg(1), Rn, imm(0x1fff'ffff));
|
||||
or32(reg(1), reg(1), imm(0x2000'0000));
|
||||
call(writeByte);
|
||||
mov32(reg(1), Rn);
|
||||
call(writeB);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1188,7 +1203,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
sub32(reg(1), Rn, imm(4));
|
||||
mov32(Rn, reg(1));
|
||||
mov32(reg(2), PR);
|
||||
call(writeLong);
|
||||
call(writeL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1197,7 +1212,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
sub32(reg(1), Rn, imm(4));
|
||||
mov32(Rn, reg(1));
|
||||
mov32(reg(2), VBR);
|
||||
call(writeLong);
|
||||
call(writeL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1228,7 +1243,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
case 0x426: {
|
||||
mov32(reg(1), Rm);
|
||||
add32(Rm, reg(1), imm(4));
|
||||
call(readLong);
|
||||
call(readL);
|
||||
mov32(PR, reg(0));
|
||||
return 0;
|
||||
}
|
||||
@@ -1237,7 +1252,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
case 0x427: {
|
||||
mov32(reg(1), Rm);
|
||||
add32(Rm, reg(1), imm(4));
|
||||
call(readLong);
|
||||
call(readL);
|
||||
mov32(VBR, reg(0));
|
||||
return 0;
|
||||
}
|
||||
@@ -1337,12 +1352,13 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
case 0x002b: {
|
||||
mov32(reg(1), R15);
|
||||
add32(R15, reg(1), imm(4));
|
||||
call(readLong);
|
||||
call(readL);
|
||||
add32(reg(0), reg(0), imm(4));
|
||||
mov32(PPC, reg(0));
|
||||
mov32(PPM, imm(Branch::Slot));
|
||||
mov32(reg(1), R15);
|
||||
add32(R15, reg(1), imm(4));
|
||||
call(readLong);
|
||||
call(readL);
|
||||
setSR(reg(0));
|
||||
return 1;
|
||||
}
|
||||
@@ -1422,10 +1438,10 @@ auto SH2::Recompiler::setSR(reg src) -> void {
|
||||
#undef ET
|
||||
#undef ID
|
||||
|
||||
#undef readByte
|
||||
#undef readWord
|
||||
#undef readLong
|
||||
#undef writeByte
|
||||
#undef writeWord
|
||||
#undef writeLong
|
||||
#undef readB
|
||||
#undef readW
|
||||
#undef readL
|
||||
#undef writeB
|
||||
#undef writeW
|
||||
#undef writeL
|
||||
#undef illegal
|
||||
@@ -45,11 +45,12 @@ auto SH2::power(bool reset) -> void {
|
||||
ET = 0;
|
||||
ID = 0;
|
||||
exceptions = !reset ? ResetCold : ResetWarm;
|
||||
cyclesUntilSync = 0;
|
||||
|
||||
cache = {*this};
|
||||
intc = {*this};
|
||||
dmac = {*this};
|
||||
sci = {*this};
|
||||
sci = {*this, reset ? sci.link : maybe<SH2&>()};
|
||||
wdt = {};
|
||||
ubc = {};
|
||||
frt = {*this};
|
||||
|
||||
@@ -29,7 +29,6 @@ struct SH2 {
|
||||
auto exceptionHandler() -> void;
|
||||
auto push(u32 data) -> void;
|
||||
auto interrupt(u8 level, u8 vector) -> void;
|
||||
auto exception(u8 vector) -> void;
|
||||
auto addressErrorCPU() -> void;
|
||||
auto addressErrorDMA() -> void;
|
||||
auto illegalInstruction() -> void;
|
||||
@@ -246,6 +245,9 @@ struct SH2 {
|
||||
};
|
||||
u32 exceptions = 0; //delayed exception flags
|
||||
|
||||
s32 cyclesUntilSync = 0;
|
||||
s32 minCyclesBetweenSyncs = 0;
|
||||
|
||||
struct Recompiler : recompiler::generic {
|
||||
SH2& self;
|
||||
Recompiler(SH2& self) : self(self), generic(allocator) {}
|
||||
@@ -276,7 +278,6 @@ struct SH2 {
|
||||
|
||||
bump_allocator allocator;
|
||||
Pool* pools[1 << 24];
|
||||
int min_cycles = 0;
|
||||
} recompiler{*this};
|
||||
|
||||
#include "sh7604/sh7604.hpp"
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
auto SH2::readByte(u32 address) -> u32 {
|
||||
template<u32 Origin> auto SH2::readByte(u32 address) -> u32 {
|
||||
switch(address >> 29) {
|
||||
|
||||
case Area::Cached: {
|
||||
if(likely(cache.enable)) return cache.read<Byte>(address);
|
||||
if constexpr(Origin == Bus::Cache) {
|
||||
if(likely(cache.enable)) return cache.read<Byte>(address);
|
||||
}
|
||||
return busReadByte(address & 0x1fff'ffff);
|
||||
}
|
||||
|
||||
@@ -11,7 +13,10 @@ auto SH2::readByte(u32 address) -> u32 {
|
||||
}
|
||||
|
||||
case Area::Data: {
|
||||
return cache.readData<Byte>(address);
|
||||
if constexpr(Origin == Bus::Cache) {
|
||||
return cache.readData<Byte>(address);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Area::IO: {
|
||||
@@ -28,7 +33,7 @@ auto SH2::readByte(u32 address) -> u32 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto SH2::readWord(u32 address) -> u32 {
|
||||
template<u32 Origin> auto SH2::readWord(u32 address) -> u32 {
|
||||
if constexpr(Accuracy::AddressErrors) {
|
||||
if(unlikely(address & 1)) return exceptions |= AddressErrorCPU, 0;
|
||||
}
|
||||
@@ -36,7 +41,9 @@ auto SH2::readWord(u32 address) -> u32 {
|
||||
switch(address >> 29) {
|
||||
|
||||
case Area::Cached: {
|
||||
if(likely(cache.enable)) return cache.read<Word>(address);
|
||||
if constexpr(Origin == Bus::Cache) {
|
||||
if(likely(cache.enable)) return cache.read<Word>(address);
|
||||
}
|
||||
return busReadWord(address & 0x1fff'fffe);
|
||||
}
|
||||
|
||||
@@ -45,7 +52,10 @@ auto SH2::readWord(u32 address) -> u32 {
|
||||
}
|
||||
|
||||
case Area::Data: {
|
||||
return cache.readData<Word>(address);
|
||||
if constexpr(Origin == Bus::Cache) {
|
||||
return cache.readData<Word>(address);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Area::IO: {
|
||||
@@ -60,7 +70,7 @@ auto SH2::readWord(u32 address) -> u32 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto SH2::readLong(u32 address) -> u32 {
|
||||
template<u32 Origin> auto SH2::readLong(u32 address) -> u32 {
|
||||
if constexpr(Accuracy::AddressErrors) {
|
||||
if(unlikely(address & 3)) return exceptions |= AddressErrorCPU, 0;
|
||||
}
|
||||
@@ -68,7 +78,9 @@ auto SH2::readLong(u32 address) -> u32 {
|
||||
switch(address >> 29) {
|
||||
|
||||
case Area::Cached: {
|
||||
if(likely(cache.enable)) return cache.read<Long>(address);
|
||||
if constexpr(Origin == Bus::Cache) {
|
||||
if(likely(cache.enable)) return cache.read<Long>(address);
|
||||
}
|
||||
return busReadLong(address & 0x1fff'fffc);
|
||||
}
|
||||
|
||||
@@ -77,11 +89,17 @@ auto SH2::readLong(u32 address) -> u32 {
|
||||
}
|
||||
|
||||
case Area::Address: {
|
||||
return cache.readAddress(address);
|
||||
if constexpr(Origin == Bus::Cache) {
|
||||
return cache.readAddress(address);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Area::Data: {
|
||||
return cache.readData<Long>(address);
|
||||
if constexpr(Origin == Bus::Cache) {
|
||||
return cache.readData<Long>(address);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Area::IO: {
|
||||
@@ -104,7 +122,7 @@ auto SH2::readLong(u32 address) -> u32 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto SH2::writeByte(u32 address, u32 data) -> void {
|
||||
template<u32 Origin> auto SH2::writeByte(u32 address, u32 data) -> void {
|
||||
if constexpr(Accuracy::Recompiler) {
|
||||
recompiler.invalidate(address);
|
||||
}
|
||||
@@ -112,20 +130,29 @@ auto SH2::writeByte(u32 address, u32 data) -> void {
|
||||
switch(address >> 29) {
|
||||
|
||||
case Area::Cached: {
|
||||
if(likely(cache.enable)) cache.write<Byte>(address, data);
|
||||
if constexpr(Origin == Bus::Cache) {
|
||||
if(likely(cache.enable)) cache.write<Byte>(address, data);
|
||||
}
|
||||
return busWriteByte(address & 0x1fff'ffff, data);
|
||||
}
|
||||
|
||||
case Area::Uncached: {
|
||||
cyclesUntilSync = 0;
|
||||
return busWriteByte(address & 0x1fff'ffff, data);
|
||||
}
|
||||
|
||||
case Area::Purge: {
|
||||
return cache.purge(address);
|
||||
if constexpr(Origin == Bus::Cache) {
|
||||
return cache.purge(address);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Area::Data: {
|
||||
return cache.writeData<Byte>(address, data);
|
||||
if constexpr(Origin == Bus::Cache) {
|
||||
return cache.writeData<Byte>(address, data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Area::IO: {
|
||||
@@ -140,7 +167,7 @@ auto SH2::writeByte(u32 address, u32 data) -> void {
|
||||
}
|
||||
}
|
||||
|
||||
auto SH2::writeWord(u32 address, u32 data) -> void {
|
||||
template<u32 Origin> auto SH2::writeWord(u32 address, u32 data) -> void {
|
||||
if constexpr(Accuracy::AddressErrors) {
|
||||
if(unlikely(address & 1)) return (void)(exceptions |= AddressErrorCPU);
|
||||
}
|
||||
@@ -152,20 +179,29 @@ auto SH2::writeWord(u32 address, u32 data) -> void {
|
||||
switch(address >> 29) {
|
||||
|
||||
case Area::Cached: {
|
||||
if(likely(cache.enable)) cache.write<Word>(address, data);
|
||||
if constexpr(Origin == Bus::Cache) {
|
||||
if(likely(cache.enable)) cache.write<Word>(address, data);
|
||||
}
|
||||
return busWriteWord(address & 0x1fff'fffe, data);
|
||||
}
|
||||
|
||||
case Area::Uncached: {
|
||||
cyclesUntilSync = 0;
|
||||
return busWriteWord(address & 0x1fff'fffe, data);
|
||||
}
|
||||
|
||||
case Area::Purge: {
|
||||
return cache.purge(address);
|
||||
if constexpr(Origin == Bus::Cache) {
|
||||
return cache.purge(address);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Area::Data: {
|
||||
return cache.writeData<Word>(address, data);
|
||||
if constexpr(Origin == Bus::Cache) {
|
||||
return cache.writeData<Word>(address, data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Area::IO: {
|
||||
@@ -177,7 +213,7 @@ auto SH2::writeWord(u32 address, u32 data) -> void {
|
||||
}
|
||||
}
|
||||
|
||||
auto SH2::writeLong(u32 address, u32 data) -> void {
|
||||
template<u32 Origin> auto SH2::writeLong(u32 address, u32 data) -> void {
|
||||
if constexpr(Accuracy::AddressErrors) {
|
||||
if(unlikely(address & 3)) return (void)(exceptions |= AddressErrorCPU);
|
||||
}
|
||||
@@ -190,24 +226,36 @@ auto SH2::writeLong(u32 address, u32 data) -> void {
|
||||
switch(address >> 29) {
|
||||
|
||||
case Area::Cached: {
|
||||
if(likely(cache.enable)) cache.write<Long>(address, data);
|
||||
if constexpr(Origin == Bus::Cache) {
|
||||
if(likely(cache.enable)) cache.write<Long>(address, data);
|
||||
}
|
||||
return busWriteLong(address & 0x1fff'fffc, data);
|
||||
}
|
||||
|
||||
case Area::Uncached: {
|
||||
cyclesUntilSync = 0;
|
||||
return busWriteLong(address & 0x1fff'fffc, data);
|
||||
}
|
||||
|
||||
case Area::Purge: {
|
||||
return cache.purge(address);
|
||||
if constexpr(Origin == Bus::Cache) {
|
||||
return cache.purge(address);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Area::Address: {
|
||||
return cache.writeAddress(address, data);
|
||||
if constexpr(Origin == Bus::Cache) {
|
||||
return cache.writeAddress(address, data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Area::Data: {
|
||||
return cache.writeData<Long>(address, data);
|
||||
if constexpr(Origin == Bus::Cache) {
|
||||
return cache.writeData<Long>(address, data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Area::IO: {
|
||||
|
||||
@@ -18,7 +18,7 @@ auto SH2::DMAC::transfer(bool c) -> void {
|
||||
switch(chcr[c].ts) {
|
||||
|
||||
case 0: //8-bit
|
||||
self->writeByte(dar[c], self->readByte(sar[c]));
|
||||
self->writeByte<Bus::Internal>(dar[c], self->readByte<Bus::Internal>(sar[c]));
|
||||
break;
|
||||
case 1: //16-bit
|
||||
if constexpr(Accuracy::AddressErrors) {
|
||||
@@ -27,7 +27,7 @@ auto SH2::DMAC::transfer(bool c) -> void {
|
||||
break;
|
||||
}
|
||||
}
|
||||
self->writeWord(dar[c], self->readWord(sar[c]));
|
||||
self->writeWord<Bus::Internal>(dar[c], self->readWord<Bus::Internal>(sar[c]));
|
||||
break;
|
||||
case 2: //32-bit
|
||||
if constexpr(Accuracy::AddressErrors) {
|
||||
@@ -36,7 +36,7 @@ auto SH2::DMAC::transfer(bool c) -> void {
|
||||
break;
|
||||
}
|
||||
}
|
||||
self->writeLong(dar[c], self->readLong(sar[c]));
|
||||
self->writeLong<Bus::Internal>(dar[c], self->readLong<Bus::Internal>(sar[c]));
|
||||
break;
|
||||
case 3: //32-bit x4
|
||||
if constexpr(Accuracy::AddressErrors) {
|
||||
@@ -45,10 +45,10 @@ auto SH2::DMAC::transfer(bool c) -> void {
|
||||
break;
|
||||
}
|
||||
}
|
||||
self->writeLong(dar[c] + 0, self->readLong(sar[c] + 0));
|
||||
self->writeLong(dar[c] + 4, self->readLong(sar[c] + 4));
|
||||
self->writeLong(dar[c] + 8, self->readLong(sar[c] + 8));
|
||||
self->writeLong(dar[c] + 12, self->readLong(sar[c] + 12));
|
||||
self->writeLong<Bus::Internal>(dar[c] + 0, self->readLong<Bus::Internal>(sar[c] + 0));
|
||||
self->writeLong<Bus::Internal>(dar[c] + 4, self->readLong<Bus::Internal>(sar[c] + 4));
|
||||
self->writeLong<Bus::Internal>(dar[c] + 8, self->readLong<Bus::Internal>(sar[c] + 8));
|
||||
self->writeLong<Bus::Internal>(dar[c] + 12, self->readLong<Bus::Internal>(sar[c] + 12));
|
||||
sar[c] += 16; //always increments regardless of chcr[c].sm
|
||||
tcr[c] -= 3; //always decrements by four instead of by one
|
||||
break;
|
||||
|
||||
@@ -553,7 +553,7 @@ auto SH2::internalWriteByte(u32 address, n8 data) -> void {
|
||||
sci.scr.te = data.bit(5);
|
||||
sci.scr.rie = data.bit(6);
|
||||
sci.scr.tie = data.bit(7);
|
||||
if(!te && sci.scr.te) sci.run();
|
||||
if(te && !sci.scr.te) sci.ssr.tdre = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -563,7 +563,8 @@ auto SH2::internalWriteByte(u32 address, n8 data) -> void {
|
||||
return;
|
||||
|
||||
//SSR: serial status register
|
||||
case 0xffff'fe04:
|
||||
case 0xffff'fe04: {
|
||||
bool tdre = sci.ssr.tdre;
|
||||
sci.ssr.mpbt = data.bit(0);
|
||||
//sci.ssr.mpb = data.bit(1) = readonly;
|
||||
//sci.ssr.tend = data.bit(2) = readonly;
|
||||
@@ -572,8 +573,9 @@ auto SH2::internalWriteByte(u32 address, n8 data) -> void {
|
||||
sci.ssr.orer &= data.bit(5);
|
||||
sci.ssr.rdrf &= data.bit(6);
|
||||
sci.ssr.tdre &= data.bit(7);
|
||||
if(sci.scr.te) sci.run();
|
||||
if(tdre && !sci.ssr.tdre) sci.run();
|
||||
return;
|
||||
}
|
||||
|
||||
//RDR: receive data register
|
||||
case 0xffff'fe05:
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
//struct SH2 {
|
||||
enum : u32 { Byte, Word, Long };
|
||||
|
||||
struct Bus {
|
||||
enum : u32 { Cache, Internal, Peripheral };
|
||||
};
|
||||
|
||||
struct Area { enum : u32 {
|
||||
Cached = 0,
|
||||
Uncached = 1,
|
||||
@@ -15,12 +19,12 @@
|
||||
};};
|
||||
|
||||
//bus.cpp
|
||||
auto readByte(u32 address) -> u32;
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto readLong(u32 address) -> u32;
|
||||
auto writeByte(u32 address, u32 data) -> void;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
auto writeLong(u32 address, u32 data) -> void;
|
||||
template<u32 Origin = Bus::Cache> auto readByte(u32 address) -> u32;
|
||||
template<u32 Origin = Bus::Cache> auto readWord(u32 address) -> u32;
|
||||
template<u32 Origin = Bus::Cache> auto readLong(u32 address) -> u32;
|
||||
template<u32 Origin = Bus::Cache> auto writeByte(u32 address, u32 data) -> void;
|
||||
template<u32 Origin = Bus::Cache> auto writeWord(u32 address, u32 data) -> void;
|
||||
template<u32 Origin = Bus::Cache> auto writeLong(u32 address, u32 data) -> void;
|
||||
|
||||
//io.cpp
|
||||
auto internalReadByte(u32 address, n8 data = 0) -> n8;
|
||||
@@ -197,7 +201,7 @@
|
||||
n1 fer; //framing error
|
||||
n1 orer; //overrun error
|
||||
n1 rdrf; //receive data register full
|
||||
n1 tdre; //transmit data register empty
|
||||
n1 tdre = 1; //transmit data register empty
|
||||
} ssr;
|
||||
n8 brr = 0xff; //bit rate register
|
||||
n8 tdr = 0xff; //transmit data register
|
||||
|
||||
@@ -139,22 +139,28 @@ template<u32 size> auto V30MZ::RCR(u16 x, u5 y) -> u16 {
|
||||
return result & mask;
|
||||
}
|
||||
|
||||
template<u32 size> auto V30MZ::ROL(u16 x, u4 y) -> u16 {
|
||||
PSW.CY = (x << y) & (1 << bits);
|
||||
u16 result = ((x << y) | (x >> (bits - y))) & mask;
|
||||
template<u32 size> auto V30MZ::ROL(u16 x, u5 y) -> u16 {
|
||||
u16 result = x;
|
||||
for(u32 n = 0; n < y; n++) {
|
||||
PSW.CY = result & sign;
|
||||
result = (result << 1) | PSW.CY;
|
||||
}
|
||||
PSW.V = (x ^ result) & sign;
|
||||
return result;
|
||||
return result & mask;
|
||||
}
|
||||
|
||||
template<u32 size> auto V30MZ::ROR(u16 x, u4 y) -> u16 {
|
||||
PSW.CY = (x >> (y - 1)) & 1;
|
||||
u16 result = ((x >> y) | (x << (bits - y))) & mask;
|
||||
template<u32 size> auto V30MZ::ROR(u16 x, u5 y) -> u16 {
|
||||
u16 result = x;
|
||||
for(u32 n = 0; n < y; n++) {
|
||||
PSW.CY = result & 1;
|
||||
result = (PSW.CY ? sign : 0) | (result >> 1);
|
||||
}
|
||||
PSW.V = (x ^ result) & sign;
|
||||
return result;
|
||||
return result & mask;
|
||||
}
|
||||
|
||||
template<u32 size> auto V30MZ::SAL(u16 x, u5 y) -> u16 {
|
||||
PSW.CY = (x << y) & (1 << bits);
|
||||
if(y != 0) PSW.CY = (x << y) & (1 << bits);
|
||||
u16 result = (x << y) & mask;
|
||||
PSW.P = parity(result);
|
||||
PSW.Z = result == 0;
|
||||
@@ -164,13 +170,12 @@ template<u32 size> auto V30MZ::SAL(u16 x, u5 y) -> u16 {
|
||||
}
|
||||
|
||||
template<u32 size> auto V30MZ::SAR(u16 x, u5 y) -> u16 {
|
||||
if(y & 16) {
|
||||
PSW.CY = x & sign;
|
||||
return 0 - PSW.CY;
|
||||
u16 result = x;
|
||||
for(u32 n = 0; n < y; n++) {
|
||||
PSW.CY = result & 1;
|
||||
result = (result & sign) | (result >> 1);
|
||||
}
|
||||
PSW.CY = (x >> (y - 1)) & 1;
|
||||
u16 result = (x >> y) & mask;
|
||||
if(x & sign) result |= mask << (bits - y);
|
||||
result &= mask;
|
||||
PSW.P = parity(result);
|
||||
PSW.Z = result == 0;
|
||||
PSW.S = result & sign;
|
||||
@@ -183,7 +188,7 @@ template<u32 size> auto V30MZ::SBB(u16 x, u16 y) -> u16 {
|
||||
}
|
||||
|
||||
template<u32 size> auto V30MZ::SHL(u16 x, u5 y) -> u16 {
|
||||
PSW.CY = (x << y) & (1 << bits);
|
||||
if(y != 0) PSW.CY = (x << y) & (1 << bits);
|
||||
u16 result = (x << y) & mask;
|
||||
PSW.P = parity(result);
|
||||
PSW.Z = result == 0;
|
||||
@@ -193,7 +198,7 @@ template<u32 size> auto V30MZ::SHL(u16 x, u5 y) -> u16 {
|
||||
}
|
||||
|
||||
template<u32 size> auto V30MZ::SHR(u16 x, u5 y) -> u16 {
|
||||
PSW.CY = (x >> (y - 1)) & 1;
|
||||
if(y != 0) PSW.CY = (x >> (y - 1)) & 1;
|
||||
u16 result = (x >> y) & mask;
|
||||
PSW.P = parity(result);
|
||||
PSW.Z = result == 0;
|
||||
|
||||
@@ -121,8 +121,8 @@ struct V30MZ {
|
||||
template<u32> auto OR (u16, u16) -> u16;
|
||||
template<u32> auto RCL (u16, u5 ) -> u16;
|
||||
template<u32> auto RCR (u16, u5 ) -> u16;
|
||||
template<u32> auto ROL (u16, u4 ) -> u16;
|
||||
template<u32> auto ROR (u16, u4 ) -> u16;
|
||||
template<u32> auto ROL (u16, u5 ) -> u16;
|
||||
template<u32> auto ROR (u16, u5 ) -> u16;
|
||||
template<u32> auto SAL (u16, u5 ) -> u16;
|
||||
template<u32> auto SAR (u16, u5 ) -> u16;
|
||||
template<u32> auto SBB (u16, u16) -> u16;
|
||||
|
||||
@@ -17,9 +17,10 @@ auto Z80::instruction() -> void {
|
||||
|
||||
if(code == 0xcb && prefix != Prefix::hl) {
|
||||
WZ = HL + (i8)operand();
|
||||
wait(2); //+1 fetch, +1 memory read
|
||||
n8 opcd = operand(); // pseudo opcode fetch (3 cycles)
|
||||
wait(2);
|
||||
//R is not incremented here
|
||||
instructionCBd(WZ, opcode());
|
||||
instructionCBd(WZ, opcd);
|
||||
} else if(code == 0xcb) {
|
||||
R.bit(0,6)++;
|
||||
instructionCB(opcode());
|
||||
@@ -242,7 +243,7 @@ auto Z80::instruction(n8 code) -> void {
|
||||
op(0xca, JP_c_nn, ZF == 1)
|
||||
//op(0xcb, cb:)
|
||||
op(0xcc, CALL_c_nn, ZF == 1)
|
||||
op(0xcd, CALL_nn)
|
||||
op(0xcd, CALL_c_nn, true)
|
||||
op(0xce, ADC_a_n)
|
||||
op(0xcf, RST_o, 1)
|
||||
op(0xd0, RET_c, CF == 0)
|
||||
@@ -297,6 +298,8 @@ auto Z80::instruction(n8 code) -> void {
|
||||
}
|
||||
|
||||
auto Z80::instructionCB(n8 code) -> void {
|
||||
n8 _;
|
||||
|
||||
switch(code) {
|
||||
op(0x00, RLC_r, B)
|
||||
op(0x01, RLC_r, C)
|
||||
@@ -304,7 +307,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0x03, RLC_r, E)
|
||||
op(0x04, RLC_r, H)
|
||||
op(0x05, RLC_r, L)
|
||||
op(0x06, RLC_irr, _HL)
|
||||
op(0x06, RLC_irr_r, _HL, _)
|
||||
op(0x07, RLC_r, A)
|
||||
op(0x08, RRC_r, B)
|
||||
op(0x09, RRC_r, C)
|
||||
@@ -312,7 +315,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0x0b, RRC_r, E)
|
||||
op(0x0c, RRC_r, H)
|
||||
op(0x0d, RRC_r, L)
|
||||
op(0x0e, RRC_irr, _HL)
|
||||
op(0x0e, RRC_irr_r, _HL, _)
|
||||
op(0x0f, RRC_r, A)
|
||||
op(0x10, RL_r, B)
|
||||
op(0x11, RL_r, C)
|
||||
@@ -320,7 +323,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0x13, RL_r, E)
|
||||
op(0x14, RL_r, H)
|
||||
op(0x15, RL_r, L)
|
||||
op(0x16, RL_irr, _HL)
|
||||
op(0x16, RL_irr_r, _HL, _)
|
||||
op(0x17, RL_r, A)
|
||||
op(0x18, RR_r, B)
|
||||
op(0x19, RR_r, C)
|
||||
@@ -328,7 +331,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0x1b, RR_r, E)
|
||||
op(0x1c, RR_r, H)
|
||||
op(0x1d, RR_r, L)
|
||||
op(0x1e, RR_irr, _HL)
|
||||
op(0x1e, RR_irr_r, _HL, _)
|
||||
op(0x1f, RR_r, A)
|
||||
op(0x20, SLA_r, B)
|
||||
op(0x21, SLA_r, C)
|
||||
@@ -336,7 +339,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0x23, SLA_r, E)
|
||||
op(0x24, SLA_r, H)
|
||||
op(0x25, SLA_r, L)
|
||||
op(0x26, SLA_irr, _HL)
|
||||
op(0x26, SLA_irr_r, _HL, _)
|
||||
op(0x27, SLA_r, A)
|
||||
op(0x28, SRA_r, B)
|
||||
op(0x29, SRA_r, C)
|
||||
@@ -344,7 +347,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0x2b, SRA_r, E)
|
||||
op(0x2c, SRA_r, H)
|
||||
op(0x2d, SRA_r, L)
|
||||
op(0x2e, SRA_irr, _HL)
|
||||
op(0x2e, SRA_irr_r, _HL, _)
|
||||
op(0x2f, SRA_r, A)
|
||||
op(0x30, SLL_r, B)
|
||||
op(0x31, SLL_r, C)
|
||||
@@ -352,7 +355,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0x33, SLL_r, E)
|
||||
op(0x34, SLL_r, H)
|
||||
op(0x35, SLL_r, L)
|
||||
op(0x36, SLL_irr, _HL)
|
||||
op(0x36, SLL_irr_r, _HL, _)
|
||||
op(0x37, SLL_r, A)
|
||||
op(0x38, SRL_r, B)
|
||||
op(0x39, SRL_r, C)
|
||||
@@ -360,7 +363,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0x3b, SRL_r, E)
|
||||
op(0x3c, SRL_r, H)
|
||||
op(0x3d, SRL_r, L)
|
||||
op(0x3e, SRL_irr, _HL)
|
||||
op(0x3e, SRL_irr_r, _HL, _)
|
||||
op(0x3f, SRL_r, A)
|
||||
op(0x40, BIT_o_r, 0, B)
|
||||
op(0x41, BIT_o_r, 0, C)
|
||||
@@ -368,7 +371,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0x43, BIT_o_r, 0, E)
|
||||
op(0x44, BIT_o_r, 0, H)
|
||||
op(0x45, BIT_o_r, 0, L)
|
||||
op(0x46, BIT_o_irr, 0, _HL)
|
||||
op(0x46, BIT_o_irr_r, 0, _HL, _)
|
||||
op(0x47, BIT_o_r, 0, A)
|
||||
op(0x48, BIT_o_r, 1, B)
|
||||
op(0x49, BIT_o_r, 1, C)
|
||||
@@ -376,7 +379,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0x4b, BIT_o_r, 1, E)
|
||||
op(0x4c, BIT_o_r, 1, H)
|
||||
op(0x4d, BIT_o_r, 1, L)
|
||||
op(0x4e, BIT_o_irr, 1, _HL)
|
||||
op(0x4e, BIT_o_irr_r, 1, _HL, _)
|
||||
op(0x4f, BIT_o_r, 1, A)
|
||||
op(0x50, BIT_o_r, 2, B)
|
||||
op(0x51, BIT_o_r, 2, C)
|
||||
@@ -384,7 +387,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0x53, BIT_o_r, 2, E)
|
||||
op(0x54, BIT_o_r, 2, H)
|
||||
op(0x55, BIT_o_r, 2, L)
|
||||
op(0x56, BIT_o_irr, 2, _HL)
|
||||
op(0x56, BIT_o_irr_r, 2, _HL, _)
|
||||
op(0x57, BIT_o_r, 2, A)
|
||||
op(0x58, BIT_o_r, 3, B)
|
||||
op(0x59, BIT_o_r, 3, C)
|
||||
@@ -392,7 +395,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0x5b, BIT_o_r, 3, E)
|
||||
op(0x5c, BIT_o_r, 3, H)
|
||||
op(0x5d, BIT_o_r, 3, L)
|
||||
op(0x5e, BIT_o_irr, 3, _HL)
|
||||
op(0x5e, BIT_o_irr_r, 3, _HL, _)
|
||||
op(0x5f, BIT_o_r, 3, A)
|
||||
op(0x60, BIT_o_r, 4, B)
|
||||
op(0x61, BIT_o_r, 4, C)
|
||||
@@ -400,7 +403,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0x63, BIT_o_r, 4, E)
|
||||
op(0x64, BIT_o_r, 4, H)
|
||||
op(0x65, BIT_o_r, 4, L)
|
||||
op(0x66, BIT_o_irr, 4, _HL)
|
||||
op(0x66, BIT_o_irr_r, 4, _HL, _)
|
||||
op(0x67, BIT_o_r, 4, A)
|
||||
op(0x68, BIT_o_r, 5, B)
|
||||
op(0x69, BIT_o_r, 5, C)
|
||||
@@ -408,7 +411,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0x6b, BIT_o_r, 5, E)
|
||||
op(0x6c, BIT_o_r, 5, H)
|
||||
op(0x6d, BIT_o_r, 5, L)
|
||||
op(0x6e, BIT_o_irr, 5, _HL)
|
||||
op(0x6e, BIT_o_irr_r, 5, _HL, _)
|
||||
op(0x6f, BIT_o_r, 5, A)
|
||||
op(0x70, BIT_o_r, 6, B)
|
||||
op(0x71, BIT_o_r, 6, C)
|
||||
@@ -416,7 +419,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0x73, BIT_o_r, 6, E)
|
||||
op(0x74, BIT_o_r, 6, H)
|
||||
op(0x75, BIT_o_r, 6, L)
|
||||
op(0x76, BIT_o_irr, 6, _HL)
|
||||
op(0x76, BIT_o_irr_r, 6, _HL, _)
|
||||
op(0x77, BIT_o_r, 6, A)
|
||||
op(0x78, BIT_o_r, 7, B)
|
||||
op(0x79, BIT_o_r, 7, C)
|
||||
@@ -424,7 +427,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0x7b, BIT_o_r, 7, E)
|
||||
op(0x7c, BIT_o_r, 7, H)
|
||||
op(0x7d, BIT_o_r, 7, L)
|
||||
op(0x7e, BIT_o_irr, 7, _HL)
|
||||
op(0x7e, BIT_o_irr_r, 7, _HL, _)
|
||||
op(0x7f, BIT_o_r, 7, A)
|
||||
op(0x80, RES_o_r, 0, B)
|
||||
op(0x81, RES_o_r, 0, C)
|
||||
@@ -432,7 +435,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0x83, RES_o_r, 0, E)
|
||||
op(0x84, RES_o_r, 0, H)
|
||||
op(0x85, RES_o_r, 0, L)
|
||||
op(0x86, RES_o_irr, 0, _HL)
|
||||
op(0x86, RES_o_irr_r, 0, _HL, _)
|
||||
op(0x87, RES_o_r, 0, A)
|
||||
op(0x88, RES_o_r, 1, B)
|
||||
op(0x89, RES_o_r, 1, C)
|
||||
@@ -440,7 +443,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0x8b, RES_o_r, 1, E)
|
||||
op(0x8c, RES_o_r, 1, H)
|
||||
op(0x8d, RES_o_r, 1, L)
|
||||
op(0x8e, RES_o_irr, 1, _HL)
|
||||
op(0x8e, RES_o_irr_r, 1, _HL, _)
|
||||
op(0x8f, RES_o_r, 1, A)
|
||||
op(0x90, RES_o_r, 2, B)
|
||||
op(0x91, RES_o_r, 2, C)
|
||||
@@ -448,7 +451,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0x93, RES_o_r, 2, E)
|
||||
op(0x94, RES_o_r, 2, H)
|
||||
op(0x95, RES_o_r, 2, L)
|
||||
op(0x96, RES_o_irr, 2, _HL)
|
||||
op(0x96, RES_o_irr_r, 2, _HL, _)
|
||||
op(0x97, RES_o_r, 2, A)
|
||||
op(0x98, RES_o_r, 3, B)
|
||||
op(0x99, RES_o_r, 3, C)
|
||||
@@ -456,7 +459,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0x9b, RES_o_r, 3, E)
|
||||
op(0x9c, RES_o_r, 3, H)
|
||||
op(0x9d, RES_o_r, 3, L)
|
||||
op(0x9e, RES_o_irr, 3, _HL)
|
||||
op(0x9e, RES_o_irr_r, 3, _HL, _)
|
||||
op(0x9f, RES_o_r, 3, A)
|
||||
op(0xa0, RES_o_r, 4, B)
|
||||
op(0xa1, RES_o_r, 4, C)
|
||||
@@ -464,7 +467,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0xa3, RES_o_r, 4, E)
|
||||
op(0xa4, RES_o_r, 4, H)
|
||||
op(0xa5, RES_o_r, 4, L)
|
||||
op(0xa6, RES_o_irr, 4, _HL)
|
||||
op(0xa6, RES_o_irr_r, 4, _HL, _)
|
||||
op(0xa7, RES_o_r, 4, A)
|
||||
op(0xa8, RES_o_r, 5, B)
|
||||
op(0xa9, RES_o_r, 5, C)
|
||||
@@ -472,7 +475,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0xab, RES_o_r, 5, E)
|
||||
op(0xac, RES_o_r, 5, H)
|
||||
op(0xad, RES_o_r, 5, L)
|
||||
op(0xae, RES_o_irr, 5, _HL)
|
||||
op(0xae, RES_o_irr_r, 5, _HL, _)
|
||||
op(0xaf, RES_o_r, 5, A)
|
||||
op(0xb0, RES_o_r, 6, B)
|
||||
op(0xb1, RES_o_r, 6, C)
|
||||
@@ -480,7 +483,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0xb3, RES_o_r, 6, E)
|
||||
op(0xb4, RES_o_r, 6, H)
|
||||
op(0xb5, RES_o_r, 6, L)
|
||||
op(0xb6, RES_o_irr, 6, _HL)
|
||||
op(0xb6, RES_o_irr_r, 6, _HL, _)
|
||||
op(0xb7, RES_o_r, 6, A)
|
||||
op(0xb8, RES_o_r, 7, B)
|
||||
op(0xb9, RES_o_r, 7, C)
|
||||
@@ -488,7 +491,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0xbb, RES_o_r, 7, E)
|
||||
op(0xbc, RES_o_r, 7, H)
|
||||
op(0xbd, RES_o_r, 7, L)
|
||||
op(0xbe, RES_o_irr, 7, _HL)
|
||||
op(0xbe, RES_o_irr_r, 7, _HL, _)
|
||||
op(0xbf, RES_o_r, 7, A)
|
||||
op(0xc0, SET_o_r, 0, B)
|
||||
op(0xc1, SET_o_r, 0, C)
|
||||
@@ -496,7 +499,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0xc3, SET_o_r, 0, E)
|
||||
op(0xc4, SET_o_r, 0, H)
|
||||
op(0xc5, SET_o_r, 0, L)
|
||||
op(0xc6, SET_o_irr, 0, _HL)
|
||||
op(0xc6, SET_o_irr_r, 0, _HL, _)
|
||||
op(0xc7, SET_o_r, 0, A)
|
||||
op(0xc8, SET_o_r, 1, B)
|
||||
op(0xc9, SET_o_r, 1, C)
|
||||
@@ -504,7 +507,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0xcb, SET_o_r, 1, E)
|
||||
op(0xcc, SET_o_r, 1, H)
|
||||
op(0xcd, SET_o_r, 1, L)
|
||||
op(0xce, SET_o_irr, 1, _HL)
|
||||
op(0xce, SET_o_irr_r, 1, _HL, _)
|
||||
op(0xcf, SET_o_r, 1, A)
|
||||
op(0xd0, SET_o_r, 2, B)
|
||||
op(0xd1, SET_o_r, 2, C)
|
||||
@@ -512,7 +515,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0xd3, SET_o_r, 2, E)
|
||||
op(0xd4, SET_o_r, 2, H)
|
||||
op(0xd5, SET_o_r, 2, L)
|
||||
op(0xd6, SET_o_irr, 2, _HL)
|
||||
op(0xd6, SET_o_irr_r, 2, _HL, _)
|
||||
op(0xd7, SET_o_r, 2, A)
|
||||
op(0xd8, SET_o_r, 3, B)
|
||||
op(0xd9, SET_o_r, 3, C)
|
||||
@@ -520,7 +523,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0xdb, SET_o_r, 3, E)
|
||||
op(0xdc, SET_o_r, 3, H)
|
||||
op(0xdd, SET_o_r, 3, L)
|
||||
op(0xde, SET_o_irr, 3, _HL)
|
||||
op(0xde, SET_o_irr_r, 3, _HL, _)
|
||||
op(0xdf, SET_o_r, 3, A)
|
||||
op(0xe0, SET_o_r, 4, B)
|
||||
op(0xe1, SET_o_r, 4, C)
|
||||
@@ -528,7 +531,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0xe3, SET_o_r, 4, E)
|
||||
op(0xe4, SET_o_r, 4, H)
|
||||
op(0xe5, SET_o_r, 4, L)
|
||||
op(0xe6, SET_o_irr, 4, _HL)
|
||||
op(0xe6, SET_o_irr_r, 4, _HL, _)
|
||||
op(0xe7, SET_o_r, 4, A)
|
||||
op(0xe8, SET_o_r, 5, B)
|
||||
op(0xe9, SET_o_r, 5, C)
|
||||
@@ -536,7 +539,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0xeb, SET_o_r, 5, E)
|
||||
op(0xec, SET_o_r, 5, H)
|
||||
op(0xed, SET_o_r, 5, L)
|
||||
op(0xee, SET_o_irr, 5, _HL)
|
||||
op(0xee, SET_o_irr_r, 5, _HL, _)
|
||||
op(0xef, SET_o_r, 5, A)
|
||||
op(0xf0, SET_o_r, 6, B)
|
||||
op(0xf1, SET_o_r, 6, C)
|
||||
@@ -544,7 +547,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0xf3, SET_o_r, 6, E)
|
||||
op(0xf4, SET_o_r, 6, H)
|
||||
op(0xf5, SET_o_r, 6, L)
|
||||
op(0xf6, SET_o_irr, 6, _HL)
|
||||
op(0xf6, SET_o_irr_r, 6, _HL, _)
|
||||
op(0xf7, SET_o_r, 6, A)
|
||||
op(0xf8, SET_o_r, 7, B)
|
||||
op(0xf9, SET_o_r, 7, C)
|
||||
@@ -552,7 +555,7 @@ auto Z80::instructionCB(n8 code) -> void {
|
||||
op(0xfb, SET_o_r, 7, E)
|
||||
op(0xfc, SET_o_r, 7, H)
|
||||
op(0xfd, SET_o_r, 7, L)
|
||||
op(0xfe, SET_o_irr, 7, _HL)
|
||||
op(0xfe, SET_o_irr_r, 7, _HL, _)
|
||||
op(0xff, SET_o_r, 7, A)
|
||||
}
|
||||
}
|
||||
@@ -625,70 +628,70 @@ auto Z80::instructionCBd(n16 addr, n8 code) -> void {
|
||||
op(0x3d, SRL_irr_r, addr, L)
|
||||
op(0x3e, SRL_irr_r, addr, _)
|
||||
op(0x3f, SRL_irr_r, addr, A)
|
||||
op(0x40, BIT_o_irr_r, 0, addr, B)
|
||||
op(0x41, BIT_o_irr_r, 0, addr, C)
|
||||
op(0x42, BIT_o_irr_r, 0, addr, D)
|
||||
op(0x43, BIT_o_irr_r, 0, addr, E)
|
||||
op(0x44, BIT_o_irr_r, 0, addr, H)
|
||||
op(0x45, BIT_o_irr_r, 0, addr, L)
|
||||
op(0x40, BIT_o_irr_r, 0, addr, _)
|
||||
op(0x41, BIT_o_irr_r, 0, addr, _)
|
||||
op(0x42, BIT_o_irr_r, 0, addr, _)
|
||||
op(0x43, BIT_o_irr_r, 0, addr, _)
|
||||
op(0x44, BIT_o_irr_r, 0, addr, _)
|
||||
op(0x45, BIT_o_irr_r, 0, addr, _)
|
||||
op(0x46, BIT_o_irr_r, 0, addr, _)
|
||||
op(0x47, BIT_o_irr_r, 0, addr, A)
|
||||
op(0x48, BIT_o_irr_r, 1, addr, B)
|
||||
op(0x49, BIT_o_irr_r, 1, addr, C)
|
||||
op(0x4a, BIT_o_irr_r, 1, addr, D)
|
||||
op(0x4b, BIT_o_irr_r, 1, addr, E)
|
||||
op(0x4c, BIT_o_irr_r, 1, addr, H)
|
||||
op(0x4d, BIT_o_irr_r, 1, addr, L)
|
||||
op(0x47, BIT_o_irr_r, 0, addr, _)
|
||||
op(0x48, BIT_o_irr_r, 1, addr, _)
|
||||
op(0x49, BIT_o_irr_r, 1, addr, _)
|
||||
op(0x4a, BIT_o_irr_r, 1, addr, _)
|
||||
op(0x4b, BIT_o_irr_r, 1, addr, _)
|
||||
op(0x4c, BIT_o_irr_r, 1, addr, _)
|
||||
op(0x4d, BIT_o_irr_r, 1, addr, _)
|
||||
op(0x4e, BIT_o_irr_r, 1, addr, _)
|
||||
op(0x4f, BIT_o_irr_r, 1, addr, A)
|
||||
op(0x50, BIT_o_irr_r, 2, addr, B)
|
||||
op(0x51, BIT_o_irr_r, 2, addr, C)
|
||||
op(0x52, BIT_o_irr_r, 2, addr, D)
|
||||
op(0x53, BIT_o_irr_r, 2, addr, E)
|
||||
op(0x54, BIT_o_irr_r, 2, addr, H)
|
||||
op(0x55, BIT_o_irr_r, 2, addr, L)
|
||||
op(0x4f, BIT_o_irr_r, 1, addr, _)
|
||||
op(0x50, BIT_o_irr_r, 2, addr, _)
|
||||
op(0x51, BIT_o_irr_r, 2, addr, _)
|
||||
op(0x52, BIT_o_irr_r, 2, addr, _)
|
||||
op(0x53, BIT_o_irr_r, 2, addr, _)
|
||||
op(0x54, BIT_o_irr_r, 2, addr, _)
|
||||
op(0x55, BIT_o_irr_r, 2, addr, _)
|
||||
op(0x56, BIT_o_irr_r, 2, addr, _)
|
||||
op(0x57, BIT_o_irr_r, 2, addr, A)
|
||||
op(0x58, BIT_o_irr_r, 3, addr, B)
|
||||
op(0x59, BIT_o_irr_r, 3, addr, C)
|
||||
op(0x5a, BIT_o_irr_r, 3, addr, D)
|
||||
op(0x5b, BIT_o_irr_r, 3, addr, E)
|
||||
op(0x5c, BIT_o_irr_r, 3, addr, H)
|
||||
op(0x5d, BIT_o_irr_r, 3, addr, L)
|
||||
op(0x57, BIT_o_irr_r, 2, addr, _)
|
||||
op(0x58, BIT_o_irr_r, 3, addr, _)
|
||||
op(0x59, BIT_o_irr_r, 3, addr, _)
|
||||
op(0x5a, BIT_o_irr_r, 3, addr, _)
|
||||
op(0x5b, BIT_o_irr_r, 3, addr, _)
|
||||
op(0x5c, BIT_o_irr_r, 3, addr, _)
|
||||
op(0x5d, BIT_o_irr_r, 3, addr, _)
|
||||
op(0x5e, BIT_o_irr_r, 3, addr, _)
|
||||
op(0x5f, BIT_o_irr_r, 3, addr, A)
|
||||
op(0x60, BIT_o_irr_r, 4, addr, B)
|
||||
op(0x61, BIT_o_irr_r, 4, addr, C)
|
||||
op(0x62, BIT_o_irr_r, 4, addr, D)
|
||||
op(0x63, BIT_o_irr_r, 4, addr, E)
|
||||
op(0x64, BIT_o_irr_r, 4, addr, H)
|
||||
op(0x65, BIT_o_irr_r, 4, addr, L)
|
||||
op(0x5f, BIT_o_irr_r, 3, addr, _)
|
||||
op(0x60, BIT_o_irr_r, 4, addr, _)
|
||||
op(0x61, BIT_o_irr_r, 4, addr, _)
|
||||
op(0x62, BIT_o_irr_r, 4, addr, _)
|
||||
op(0x63, BIT_o_irr_r, 4, addr, _)
|
||||
op(0x64, BIT_o_irr_r, 4, addr, _)
|
||||
op(0x65, BIT_o_irr_r, 4, addr, _)
|
||||
op(0x66, BIT_o_irr_r, 4, addr, _)
|
||||
op(0x67, BIT_o_irr_r, 4, addr, A)
|
||||
op(0x68, BIT_o_irr_r, 5, addr, B)
|
||||
op(0x69, BIT_o_irr_r, 5, addr, C)
|
||||
op(0x6a, BIT_o_irr_r, 5, addr, D)
|
||||
op(0x6b, BIT_o_irr_r, 5, addr, E)
|
||||
op(0x6c, BIT_o_irr_r, 5, addr, H)
|
||||
op(0x6d, BIT_o_irr_r, 5, addr, L)
|
||||
op(0x67, BIT_o_irr_r, 4, addr, _)
|
||||
op(0x68, BIT_o_irr_r, 5, addr, _)
|
||||
op(0x69, BIT_o_irr_r, 5, addr, _)
|
||||
op(0x6a, BIT_o_irr_r, 5, addr, _)
|
||||
op(0x6b, BIT_o_irr_r, 5, addr, _)
|
||||
op(0x6c, BIT_o_irr_r, 5, addr, _)
|
||||
op(0x6d, BIT_o_irr_r, 5, addr, _)
|
||||
op(0x6e, BIT_o_irr_r, 5, addr, _)
|
||||
op(0x6f, BIT_o_irr_r, 5, addr, A)
|
||||
op(0x70, BIT_o_irr_r, 6, addr, B)
|
||||
op(0x71, BIT_o_irr_r, 6, addr, C)
|
||||
op(0x72, BIT_o_irr_r, 6, addr, D)
|
||||
op(0x73, BIT_o_irr_r, 6, addr, E)
|
||||
op(0x74, BIT_o_irr_r, 6, addr, H)
|
||||
op(0x75, BIT_o_irr_r, 6, addr, L)
|
||||
op(0x6f, BIT_o_irr_r, 5, addr, _)
|
||||
op(0x70, BIT_o_irr_r, 6, addr, _)
|
||||
op(0x71, BIT_o_irr_r, 6, addr, _)
|
||||
op(0x72, BIT_o_irr_r, 6, addr, _)
|
||||
op(0x73, BIT_o_irr_r, 6, addr, _)
|
||||
op(0x74, BIT_o_irr_r, 6, addr, _)
|
||||
op(0x75, BIT_o_irr_r, 6, addr, _)
|
||||
op(0x76, BIT_o_irr_r, 6, addr, _)
|
||||
op(0x77, BIT_o_irr_r, 6, addr, A)
|
||||
op(0x78, BIT_o_irr_r, 7, addr, B)
|
||||
op(0x79, BIT_o_irr_r, 7, addr, C)
|
||||
op(0x7a, BIT_o_irr_r, 7, addr, D)
|
||||
op(0x7b, BIT_o_irr_r, 7, addr, E)
|
||||
op(0x7c, BIT_o_irr_r, 7, addr, H)
|
||||
op(0x7d, BIT_o_irr_r, 7, addr, L)
|
||||
op(0x77, BIT_o_irr_r, 6, addr, _)
|
||||
op(0x78, BIT_o_irr_r, 7, addr, _)
|
||||
op(0x79, BIT_o_irr_r, 7, addr, _)
|
||||
op(0x7a, BIT_o_irr_r, 7, addr, _)
|
||||
op(0x7b, BIT_o_irr_r, 7, addr, _)
|
||||
op(0x7c, BIT_o_irr_r, 7, addr, _)
|
||||
op(0x7d, BIT_o_irr_r, 7, addr, _)
|
||||
op(0x7e, BIT_o_irr_r, 7, addr, _)
|
||||
op(0x7f, BIT_o_irr_r, 7, addr, A)
|
||||
op(0x7f, BIT_o_irr_r, 7, addr, _)
|
||||
op(0x80, RES_o_irr_r, 0, addr, B)
|
||||
op(0x81, RES_o_irr_r, 0, addr, C)
|
||||
op(0x82, RES_o_irr_r, 0, addr, D)
|
||||
@@ -821,6 +824,8 @@ auto Z80::instructionCBd(n16 addr, n8 code) -> void {
|
||||
}
|
||||
|
||||
auto Z80::instructionED(n8 code) -> void {
|
||||
n8 _;
|
||||
|
||||
switch(code) {
|
||||
op(0x40, IN_r_ic, B)
|
||||
op(0x41, OUT_ic_r, B)
|
||||
@@ -870,7 +875,7 @@ auto Z80::instructionED(n8 code) -> void {
|
||||
op(0x6d, RETI)
|
||||
op(0x6e, IM_o, 0)
|
||||
op(0x6f, RLD)
|
||||
op(0x70, IN_ic)
|
||||
op(0x70, IN_r_ic, _)
|
||||
op(0x71, OUT_ic)
|
||||
op(0x72, SBC_hl_rr, SP)
|
||||
op(0x73, LD_inn_rr, SP)
|
||||
|
||||
@@ -24,9 +24,9 @@ auto Z80::instructionADC_a_r(n8& x) -> void { Q = 1;
|
||||
|
||||
auto Z80::instructionADC_hl_rr(n16& x) -> void { Q = 1;
|
||||
WZ = HL + 1;
|
||||
wait(4);
|
||||
auto lo = ADD(HL >> 0, x >> 0, CF);
|
||||
wait(3);
|
||||
auto lo = ADD(HL >> 0, x >> 0, CF);
|
||||
wait(4);
|
||||
auto hi = ADD(HL >> 8, x >> 8, CF);
|
||||
HL = hi << 8 | lo << 0;
|
||||
ZF = HL == 0;
|
||||
@@ -47,9 +47,9 @@ auto Z80::instructionADD_a_r(n8& x) -> void { Q = 1;
|
||||
auto Z80::instructionADD_hl_rr(n16& x) -> void { Q = 1;
|
||||
WZ = HL + 1;
|
||||
bool vf = VF, zf = ZF, sf = SF;
|
||||
wait(4);
|
||||
auto lo = ADD(HL >> 0, x >> 0);
|
||||
wait(3);
|
||||
auto lo = ADD(HL >> 0, x >> 0);
|
||||
wait(4);
|
||||
auto hi = ADD(HL >> 8, x >> 8, CF);
|
||||
HL = hi << 8 | lo << 0;
|
||||
VF = vf, ZF = zf, SF = sf; //restore unaffected flags
|
||||
@@ -67,14 +67,9 @@ auto Z80::instructionAND_a_r(n8& x) -> void { Q = 1;
|
||||
A = AND(A, x);
|
||||
}
|
||||
|
||||
auto Z80::instructionBIT_o_irr(n3 bit, n16& addr) -> void { Q = 1;
|
||||
BIT(bit, read(addr));
|
||||
XF = WZH.bit(3);
|
||||
YF = WZH.bit(5);
|
||||
}
|
||||
|
||||
auto Z80::instructionBIT_o_irr_r(n3 bit, n16& addr, n8& x) -> void { Q = 1;
|
||||
x = BIT(bit, read(addr));
|
||||
wait(1);
|
||||
XF = WZH.bit(3);
|
||||
YF = WZH.bit(5);
|
||||
}
|
||||
@@ -91,13 +86,6 @@ auto Z80::instructionCALL_c_nn(bool c) -> void { Q = 0;
|
||||
PC = WZ;
|
||||
}
|
||||
|
||||
auto Z80::instructionCALL_nn() -> void { Q = 0;
|
||||
WZ = operands();
|
||||
wait(1);
|
||||
push(PC);
|
||||
PC = WZ;
|
||||
}
|
||||
|
||||
auto Z80::instructionCCF() -> void {
|
||||
if(Q) XF = 0, YF = 0;
|
||||
HF = CF;
|
||||
@@ -225,8 +213,10 @@ auto Z80::instructionEI() -> void { Q = 0;
|
||||
auto Z80::instructionEX_irr_rr(n16& x, n16& y) -> void { Q = 0;
|
||||
WZL = read(x + 0);
|
||||
WZH = read(x + 1);
|
||||
wait(1);
|
||||
write(x + 0, y >> 0);
|
||||
write(x + 1, y >> 8);
|
||||
wait(2);
|
||||
y = WZ;
|
||||
}
|
||||
|
||||
@@ -245,7 +235,6 @@ auto Z80::instructionHALT() -> void { Q = 0;
|
||||
}
|
||||
|
||||
auto Z80::instructionIM_o(n2 code) -> void { Q = 0;
|
||||
wait(4);
|
||||
IM = code;
|
||||
}
|
||||
|
||||
@@ -260,11 +249,6 @@ auto Z80::instructionIN_r_ic(n8& x) -> void { Q = 1;
|
||||
WZ = BC + 1;
|
||||
}
|
||||
|
||||
auto Z80::instructionIN_ic() -> void { Q = 1;
|
||||
IN(in(BC));
|
||||
WZ = BC + 1;
|
||||
}
|
||||
|
||||
auto Z80::instructionINC_irr(n16& x) -> void { Q = 1;
|
||||
auto addr = displace(x);
|
||||
auto data = read(addr);
|
||||
@@ -351,7 +335,7 @@ auto Z80::instructionLD_a_inn() -> void { Q = 0;
|
||||
|
||||
auto Z80::instructionLD_a_irr(n16& x) -> void { Q = 0;
|
||||
WZ = x;
|
||||
A = read(displace(WZ));
|
||||
A = read(x);
|
||||
WZ++;
|
||||
}
|
||||
|
||||
@@ -370,14 +354,16 @@ auto Z80::instructionLD_inn_rr(n16& x) -> void { Q = 0;
|
||||
|
||||
auto Z80::instructionLD_irr_a(n16& x) -> void { Q = 0;
|
||||
WZ = x;
|
||||
write(displace(WZ), A);
|
||||
write(x, A);
|
||||
WZL++;
|
||||
WZH = A;
|
||||
}
|
||||
|
||||
auto Z80::instructionLD_irr_n(n16& x) -> void { Q = 0;
|
||||
auto addr = displace(x);
|
||||
write(addr, operand());
|
||||
auto addr = displace(x,0); // special case: defer wait cycles (overlap with n fetch)
|
||||
auto data = operand();
|
||||
if(&x == &ix.word || &x == &iy.word) wait(2);
|
||||
write(addr, data);
|
||||
}
|
||||
|
||||
auto Z80::instructionLD_irr_r(n16& x, n8& y) -> void { Q = 0;
|
||||
@@ -561,12 +547,10 @@ auto Z80::instructionPUSH_rr(n16& x) -> void { Q = 0;
|
||||
push(x);
|
||||
}
|
||||
|
||||
auto Z80::instructionRES_o_irr(n3 bit, n16& addr) -> void { Q = 1;
|
||||
write(addr, RES(bit, read(addr)));
|
||||
}
|
||||
|
||||
auto Z80::instructionRES_o_irr_r(n3 bit, n16& addr, n8& x) -> void { Q = 1;
|
||||
write(addr, x = RES(bit, read(addr)));
|
||||
auto data = read(addr);
|
||||
wait(1);
|
||||
write(addr, x = RES(bit, data));
|
||||
}
|
||||
|
||||
auto Z80::instructionRES_o_r(n3 bit, n8& x) -> void { Q = 1;
|
||||
@@ -574,7 +558,6 @@ auto Z80::instructionRES_o_r(n3 bit, n8& x) -> void { Q = 1;
|
||||
}
|
||||
|
||||
auto Z80::instructionRET() -> void { Q = 0;
|
||||
wait(1);
|
||||
WZ = pop();
|
||||
PC = WZ;
|
||||
}
|
||||
@@ -598,12 +581,10 @@ auto Z80::instructionRETN() -> void { Q = 0;
|
||||
IFF1 = IFF2;
|
||||
}
|
||||
|
||||
auto Z80::instructionRL_irr(n16& addr) -> void { Q = 1;
|
||||
write(addr, RL(read(addr)));
|
||||
}
|
||||
|
||||
auto Z80::instructionRL_irr_r(n16& addr, n8& x) -> void { Q = 1;
|
||||
write(addr, x = RL(read(addr)));
|
||||
auto data = read(addr);
|
||||
wait(1);
|
||||
write(addr, x = RL(data));
|
||||
}
|
||||
|
||||
auto Z80::instructionRL_r(n8& x) -> void { Q = 1;
|
||||
@@ -621,12 +602,10 @@ auto Z80::instructionRLA() -> void { Q = 1;
|
||||
YF = A.bit(5);
|
||||
}
|
||||
|
||||
auto Z80::instructionRLC_irr(n16& addr) -> void { Q = 1;
|
||||
write(addr, RLC(read(addr)));
|
||||
}
|
||||
|
||||
auto Z80::instructionRLC_irr_r(n16& addr, n8& x) -> void { Q = 1;
|
||||
write(addr, x = RLC(read(addr)));
|
||||
auto data = read(addr);
|
||||
wait(1);
|
||||
write(addr, x = RLC(data));
|
||||
}
|
||||
|
||||
auto Z80::instructionRLC_r(n8& x) -> void { Q = 1;
|
||||
@@ -647,9 +626,8 @@ auto Z80::instructionRLCA() -> void { Q = 1;
|
||||
auto Z80::instructionRLD() -> void { Q = 1;
|
||||
WZ = HL + 1;
|
||||
auto data = read(HL);
|
||||
wait(1);
|
||||
wait(4);
|
||||
write(HL, (data << 4) | (A & 0x0f));
|
||||
wait(3);
|
||||
A = (A & 0xf0) | (data >> 4);
|
||||
|
||||
NF = 0;
|
||||
@@ -661,12 +639,10 @@ auto Z80::instructionRLD() -> void { Q = 1;
|
||||
SF = A.bit(7);
|
||||
}
|
||||
|
||||
auto Z80::instructionRR_irr(n16& addr) -> void { Q = 1;
|
||||
write(addr, RR(read(addr)));
|
||||
}
|
||||
|
||||
auto Z80::instructionRR_irr_r(n16& addr, n8& x) -> void { Q = 1;
|
||||
write(addr, x = RR(read(addr)));
|
||||
auto data = read(addr);
|
||||
wait(1);
|
||||
write(addr, x = RR(data));
|
||||
}
|
||||
|
||||
auto Z80::instructionRR_r(n8& x) -> void { Q = 1;
|
||||
@@ -684,12 +660,10 @@ auto Z80::instructionRRA() -> void { Q = 1;
|
||||
YF = A.bit(5);
|
||||
}
|
||||
|
||||
auto Z80::instructionRRC_irr(n16& addr) -> void { Q = 1;
|
||||
write(addr, RRC(read(addr)));
|
||||
}
|
||||
|
||||
auto Z80::instructionRRC_irr_r(n16& addr, n8& x) -> void { Q = 1;
|
||||
write(addr, x = RRC(read(addr)));
|
||||
auto data = read(addr);
|
||||
wait(1);
|
||||
write(addr, x = RRC(data));
|
||||
}
|
||||
|
||||
auto Z80::instructionRRC_r(n8& x) -> void { Q = 1;
|
||||
@@ -710,9 +684,8 @@ auto Z80::instructionRRCA() -> void { Q = 1;
|
||||
auto Z80::instructionRRD() -> void { Q = 1;
|
||||
WZ = HL + 1;
|
||||
auto data = read(HL);
|
||||
wait(1);
|
||||
wait(4);
|
||||
write(HL, (data >> 4) | (A << 4));
|
||||
wait(3);
|
||||
A = (A & 0xf0) | (data & 0x0f);
|
||||
|
||||
NF = 0;
|
||||
@@ -745,9 +718,9 @@ auto Z80::instructionSBC_a_r(n8& x) -> void { Q = 1;
|
||||
|
||||
auto Z80::instructionSBC_hl_rr(n16& x) -> void { Q = 1;
|
||||
WZ = HL + 1;
|
||||
wait(4);
|
||||
auto lo = SUB(HL >> 0, x >> 0, CF);
|
||||
wait(3);
|
||||
auto lo = SUB(HL >> 0, x >> 0, CF);
|
||||
wait(4);
|
||||
auto hi = SUB(HL >> 8, x >> 8, CF);
|
||||
HL = hi << 8 | lo << 0;
|
||||
ZF = HL == 0;
|
||||
@@ -763,60 +736,50 @@ auto Z80::instructionSCF() -> void {
|
||||
Q = 1;
|
||||
}
|
||||
|
||||
auto Z80::instructionSET_o_irr(n3 bit, n16& addr) -> void { Q = 1;
|
||||
write(addr, SET(bit, read(addr)));
|
||||
}
|
||||
|
||||
auto Z80::instructionSET_o_irr_r(n3 bit, n16& addr, n8& x) -> void { Q = 1;
|
||||
write(addr, x = SET(bit, read(addr)));
|
||||
auto data = read(addr);
|
||||
wait(1);
|
||||
write(addr, x = SET(bit, data));
|
||||
}
|
||||
|
||||
auto Z80::instructionSET_o_r(n3 bit, n8& x) -> void { Q = 1;
|
||||
x = SET(bit, x);
|
||||
}
|
||||
|
||||
auto Z80::instructionSLA_irr(n16& addr) -> void { Q = 1;
|
||||
write(addr, SLA(read(addr)));
|
||||
}
|
||||
|
||||
auto Z80::instructionSLA_irr_r(n16& addr, n8& x) -> void { Q = 1;
|
||||
write(addr, x = SLA(read(addr)));
|
||||
auto data = read(addr);
|
||||
wait(1);
|
||||
write(addr, x = SLA(data));
|
||||
}
|
||||
|
||||
auto Z80::instructionSLA_r(n8& x) -> void { Q = 1;
|
||||
x = SLA(x);
|
||||
}
|
||||
|
||||
auto Z80::instructionSLL_irr(n16& addr) -> void { Q = 1;
|
||||
write(addr, SLL(read(addr)));
|
||||
}
|
||||
|
||||
auto Z80::instructionSLL_irr_r(n16& addr, n8& x) -> void { Q = 1;
|
||||
write(addr, x = SLL(read(addr)));
|
||||
auto data = read(addr);
|
||||
wait(1);
|
||||
write(addr, x = SLL(data));
|
||||
}
|
||||
|
||||
auto Z80::instructionSLL_r(n8& x) -> void { Q = 1;
|
||||
x = SLL(x);
|
||||
}
|
||||
|
||||
auto Z80::instructionSRA_irr(n16& addr) -> void { Q = 1;
|
||||
write(addr, SRA(read(addr)));
|
||||
}
|
||||
|
||||
auto Z80::instructionSRA_irr_r(n16& addr, n8& x) -> void { Q = 1;
|
||||
write(addr, x = SRA(read(addr)));
|
||||
auto data = read(addr);
|
||||
wait(1);
|
||||
write(addr, x = SRA(data));
|
||||
}
|
||||
|
||||
auto Z80::instructionSRA_r(n8& x) -> void { Q = 1;
|
||||
x = SRA(x);
|
||||
}
|
||||
|
||||
auto Z80::instructionSRL_irr(n16& addr) -> void { Q = 1;
|
||||
write(addr, SRL(read(addr)));
|
||||
}
|
||||
|
||||
auto Z80::instructionSRL_irr_r(n16& addr, n8& x) -> void { Q = 1;
|
||||
write(addr, x = SRL(read(addr)));
|
||||
auto data = read(addr);
|
||||
wait(1);
|
||||
write(addr, x = SRL(data));
|
||||
}
|
||||
|
||||
auto Z80::instructionSRL_r(n8& x) -> void { Q = 1;
|
||||
|
||||
@@ -27,10 +27,10 @@ auto Z80::pop() -> n16 {
|
||||
return data | read(SP++) << 8;
|
||||
}
|
||||
|
||||
auto Z80::displace(n16& x) -> n16 {
|
||||
auto Z80::displace(n16& x, u32 wclocks) -> n16 {
|
||||
if(&x != &ix.word && &x != &iy.word) return x;
|
||||
auto d = operand();
|
||||
wait(5);
|
||||
wait(wclocks);
|
||||
WZ = x + (i8)d;
|
||||
return WZ;
|
||||
}
|
||||
|
||||
@@ -31,29 +31,31 @@ auto Z80::power(MOSFET mosfet) -> void {
|
||||
HALT = 0;
|
||||
IFF1 = 0;
|
||||
IFF2 = 0;
|
||||
IM = 1;
|
||||
IM = 0;
|
||||
}
|
||||
|
||||
auto Z80::irq(bool maskable, n16 pc, n8 extbus) -> bool {
|
||||
auto Z80::reset() -> void {
|
||||
prefix = Prefix::hl;
|
||||
ir.word = 0x0000;
|
||||
WZ = PC = 0x0000;
|
||||
EI = 0;
|
||||
HALT = 0;
|
||||
IFF1 = 0;
|
||||
IFF2 = 0;
|
||||
IM = 0;
|
||||
}
|
||||
|
||||
auto Z80::irq(n8 extbus) -> bool {
|
||||
//do not execute maskable interrupts if disabled or immediately after EI instruction
|
||||
if(maskable && (!IFF1 || EI)) return false;
|
||||
if(!IFF1 || EI) return false;
|
||||
R.bit(0,6)++;
|
||||
|
||||
push(PC);
|
||||
|
||||
switch(maskable ? IM : (n2)1) {
|
||||
|
||||
switch(IM) {
|
||||
case 1: extbus = 0xff;
|
||||
case 0: {
|
||||
//external data bus ($ff = RST $38)
|
||||
WZ = extbus;
|
||||
wait(extbus | 0x38 == 0xff ? 6 : 7);
|
||||
break;
|
||||
}
|
||||
|
||||
case 1: {
|
||||
//constant address
|
||||
WZ = pc;
|
||||
wait(maskable ? 7 : 5);
|
||||
wait(6);
|
||||
instruction(WZ);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -63,18 +65,37 @@ auto Z80::irq(bool maskable, n16 pc, n8 extbus) -> bool {
|
||||
WZL = read(address + 0);
|
||||
WZH = read(address + 1);
|
||||
wait(7);
|
||||
push(PC);
|
||||
PC = WZ;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
PC = WZ;
|
||||
IFF1 = 0;
|
||||
if(maskable) IFF2 = 0;
|
||||
IFF2 = 0;
|
||||
HALT = 0;
|
||||
if(P) PF = 0;
|
||||
P = 0;
|
||||
Q = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Z80::nmi() -> bool {
|
||||
R.bit(0,6)++;
|
||||
|
||||
push(PC);
|
||||
WZ = 0x66;
|
||||
wait(5);
|
||||
PC = WZ;
|
||||
|
||||
IFF1 = 0;
|
||||
HALT = 0;
|
||||
if(P) PF = 0;
|
||||
P = 0;
|
||||
Q = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,8 +22,10 @@ struct Z80 {
|
||||
|
||||
//z80.cpp
|
||||
auto power(MOSFET = MOSFET::NMOS) -> void;
|
||||
auto reset() -> void;
|
||||
|
||||
auto irq(bool maskable, n16 vector = 0x0000, n8 extbus = 0xff) -> bool;
|
||||
auto irq(n8 extbus = 0xff) -> bool;
|
||||
auto nmi() -> bool;
|
||||
auto parity(n8) const -> bool;
|
||||
|
||||
//memory.cpp
|
||||
@@ -33,7 +35,7 @@ struct Z80 {
|
||||
auto operands() -> n16;
|
||||
auto push(n16) -> void;
|
||||
auto pop() -> n16;
|
||||
auto displace(n16&) -> n16;
|
||||
auto displace(n16&, u32 wclocks = 5) -> n16;
|
||||
auto read(n16 address) -> n8;
|
||||
auto write(n16 address, n8 data) -> void;
|
||||
auto in(n16 address) -> n8;
|
||||
|
||||
+2
-2
@@ -27,13 +27,13 @@ auto CPU::main() -> void {
|
||||
if(state.nmiPending) {
|
||||
state.nmiPending = 0; //edge-sensitive
|
||||
debugger.interrupt("NMI");
|
||||
irq(0, 0x0066, 0xff);
|
||||
nmi();
|
||||
}
|
||||
|
||||
if(state.irqLine) {
|
||||
//level-sensitive
|
||||
debugger.interrupt("IRQ");
|
||||
irq(1, 0x0038, 0xff);
|
||||
irq();
|
||||
}
|
||||
|
||||
debugger.instruction();
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@ FDS fds;
|
||||
|
||||
auto FDS::load(Node::Object parent) -> void {
|
||||
port = parent->append<Node::Port>("Disk Slot");
|
||||
port->setFamily("Famicom Disk");
|
||||
port->setFamily("Famicom Disk System");
|
||||
port->setType("Floppy Disk");
|
||||
port->setHotSwappable(true);
|
||||
port->setAllocate([&](auto name) { return allocate(port); });
|
||||
@@ -35,7 +35,7 @@ auto FDS::unload() -> void {
|
||||
}
|
||||
|
||||
auto FDS::allocate(Node::Port parent) -> Node::Peripheral {
|
||||
return node = parent->append<Node::Peripheral>("Famicom Disk");
|
||||
return node = parent->append<Node::Peripheral>("Famicom Disk System");
|
||||
}
|
||||
|
||||
auto FDS::connect() -> void {
|
||||
|
||||
@@ -24,7 +24,7 @@ struct MBC2 : Interface {
|
||||
return rom.read(io.rom.bank << 14 | (n14)address);
|
||||
}
|
||||
|
||||
if(address >= 0xa000 && address <= 0xa1ff) {
|
||||
if(address >= 0xa000 && address <= 0xbfff) {
|
||||
if(!ram || !io.ram.enable) return 0xff;
|
||||
auto pair = ram.read(address.bit(1,8));
|
||||
if((address & 1) == 0) data = 0xf0 | pair.bit(0,3);
|
||||
@@ -36,18 +36,16 @@ struct MBC2 : Interface {
|
||||
}
|
||||
|
||||
auto write(n16 address, n8 data) -> void override {
|
||||
if(address >= 0x0000 && address <= 0x1fff) {
|
||||
if(address >= 0x0000 && address <= 0x3fff) {
|
||||
if(!address.bit(8)) io.ram.enable = data.bit(0,3) == 0x0a;
|
||||
if(address.bit(8)) {
|
||||
io.rom.bank = data.bit(0,3);
|
||||
if(!io.rom.bank) io.rom.bank = 0x01;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(address >= 0x2000 && address <= 0x3fff) {
|
||||
if(address.bit(8)) io.rom.bank = data.bit(0,3);
|
||||
if(!io.rom.bank) io.rom.bank = 0x01;
|
||||
return;
|
||||
}
|
||||
|
||||
if(address >= 0xa000 && address <= 0xa1ff) {
|
||||
if(address >= 0xa000 && address <= 0xbfff) {
|
||||
if(!ram || !io.ram.enable) return;
|
||||
auto pair = ram.read(address.bit(1,8));
|
||||
if((address & 1) == 0) pair.bit(0,3) = data.bit(0,3);
|
||||
|
||||
+3
-23
@@ -134,30 +134,10 @@ auto CPU::power() -> void {
|
||||
|
||||
status = {};
|
||||
|
||||
//note: this may not be accurate, DIV values at startup may be due to LCD delays?
|
||||
//but probably not, since "CPU CGB" differs from "CPU CGB [A-E]"
|
||||
// TODO: Validate this for models other than dmg ABC/mgb
|
||||
status.div = 8;
|
||||
|
||||
//0146~0149
|
||||
if(version->latch() == "DMG-CPU" ) status.div = 0x0146;
|
||||
|
||||
//ffe6~ffe9
|
||||
if(version->latch() == "DMG-CPU A" ) status.div = 0xffe6;
|
||||
if(version->latch() == "DMG-CPU B" ) status.div = 0xffe6;
|
||||
if(version->latch() == "DMG-CPU C" ) status.div = 0xffe6;
|
||||
if(version->latch() == "CPU MGB" ) status.div = 0xffe6;
|
||||
|
||||
if(version->latch() == "SGB-CPU 01") status.div = 0xffe6; //unconfirmed
|
||||
if(version->latch() == "CPU SGB2" ) status.div = 0xffe6; //unconfirmed
|
||||
|
||||
//0206~0209
|
||||
if(version->latch() == "CPU CGB" ) status.div = 0x0206;
|
||||
|
||||
//fffa~fffd
|
||||
if(version->latch() == "CPU CGB A" ) status.div = 0xfffa;
|
||||
if(version->latch() == "CPU CGB B" ) status.div = 0xfffa;
|
||||
if(version->latch() == "CPU CGB C" ) status.div = 0xfffa;
|
||||
if(version->latch() == "CPU CGB D" ) status.div = 0xfffa;
|
||||
if(version->latch() == "CPU CGB E" ) status.div = 0xfffa;
|
||||
if(Model::GameBoyColor()) status.cgbMode = 1;
|
||||
}
|
||||
|
||||
}
|
||||
+6
-2
@@ -110,6 +110,10 @@ struct CPU : SM83, Thread {
|
||||
//$ff0f IF
|
||||
n5 interruptFlag;
|
||||
|
||||
//$ff4c KEY0
|
||||
n1 cgbMode;
|
||||
n1 opriEnable;
|
||||
|
||||
//$ff4d KEY1
|
||||
n1 speedSwitch;
|
||||
n1 speedDouble;
|
||||
@@ -124,8 +128,8 @@ struct CPU : SM83, Thread {
|
||||
n7 dmaLength;
|
||||
n1 hdmaActive;
|
||||
|
||||
//$ff6c ???
|
||||
n1 ff6c;
|
||||
//$ff6c OPRI
|
||||
n1 opri;
|
||||
|
||||
//$ff70 SVBK
|
||||
n3 wramBank = 1;
|
||||
|
||||
+24
-17
@@ -87,33 +87,33 @@ auto CPU::readIO(u32 cycle, n16 address, n8 data) -> n8 {
|
||||
return data;
|
||||
}
|
||||
|
||||
if(Model::GameBoyColor())
|
||||
if(Model::GameBoyColor() && status.cgbMode)
|
||||
if(address == 0xff4d && cycle == 2) { //KEY1
|
||||
data.bit(0) = status.speedSwitch;
|
||||
data.bit(7) = status.speedDouble;
|
||||
return data;
|
||||
}
|
||||
|
||||
if(Model::GameBoyColor())
|
||||
if(Model::GameBoyColor() && status.cgbMode)
|
||||
if(address == 0xff55 && cycle == 2) { //HDMA5
|
||||
data.bit(0,6) = status.dmaLength;
|
||||
data.bit(7) = !status.hdmaActive;
|
||||
return data;
|
||||
}
|
||||
|
||||
if(Model::GameBoyColor())
|
||||
if(Model::GameBoyColor() && status.cgbMode)
|
||||
if(address == 0xff56 && cycle == 2) { //RP
|
||||
//unemulated
|
||||
return 0x02;
|
||||
}
|
||||
|
||||
if(Model::GameBoyColor())
|
||||
if(address == 0xff6c && cycle == 2) { //???
|
||||
data.bit(0) = status.ff6c;
|
||||
if(address == 0xff6c && cycle == 2) { //OPRI
|
||||
data.bit(0) = status.opri;
|
||||
return data;
|
||||
}
|
||||
|
||||
if(Model::GameBoyColor())
|
||||
if(Model::GameBoyColor() && status.cgbMode)
|
||||
if(address == 0xff70 && cycle == 2) { //SVBK
|
||||
return status.wramBank;
|
||||
}
|
||||
@@ -207,37 +207,44 @@ auto CPU::writeIO(u32 cycle, n16 address, n8 data) -> void {
|
||||
return;
|
||||
}
|
||||
|
||||
if(Model::GameBoyColor())
|
||||
if(Model::GameBoyColor() && cartridge.bootromEnable)
|
||||
if(address == 0xff4c && cycle == 2) { //KEY0
|
||||
status.cgbMode = !(data & 0x0c);
|
||||
status.opriEnable = data.bit(3);
|
||||
return;
|
||||
}
|
||||
|
||||
if(Model::GameBoyColor() && status.cgbMode)
|
||||
if(address == 0xff4d && cycle == 2) { //KEY1
|
||||
status.speedSwitch = data.bit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if(Model::GameBoyColor())
|
||||
if(Model::GameBoyColor() && status.cgbMode)
|
||||
if(address == 0xff51 && cycle == 2) { //HDMA1
|
||||
status.dmaSource.bit(8,15) = data.bit(0,7);
|
||||
return;
|
||||
}
|
||||
|
||||
if(Model::GameBoyColor())
|
||||
if(Model::GameBoyColor() && status.cgbMode)
|
||||
if(address == 0xff52 && cycle == 2) { //HDMA2
|
||||
status.dmaSource.bit(4,7) = data.bit(4,7);
|
||||
return;
|
||||
}
|
||||
|
||||
if(Model::GameBoyColor())
|
||||
if(Model::GameBoyColor() && status.cgbMode)
|
||||
if(address == 0xff53 && cycle == 2) { //HDMA3
|
||||
status.dmaTarget.bit(8,15) = data.bit(0,7);
|
||||
return;
|
||||
}
|
||||
|
||||
if(Model::GameBoyColor())
|
||||
if(Model::GameBoyColor() && status.cgbMode)
|
||||
if(address == 0xff54 && cycle == 2) { //HDMA4
|
||||
status.dmaTarget.bit(4,7) = data.bit(4,7);
|
||||
return;
|
||||
}
|
||||
|
||||
if(Model::GameBoyColor())
|
||||
if(Model::GameBoyColor() && status.cgbMode)
|
||||
if(address == 0xff55 && cycle == 2) { //HDMA5
|
||||
//1->0 transistion stops an active HDMA (and does not trigger GDMA)
|
||||
if(status.hdmaActive && !data.bit(7)) {
|
||||
@@ -261,14 +268,14 @@ auto CPU::writeIO(u32 cycle, n16 address, n8 data) -> void {
|
||||
return;
|
||||
}
|
||||
|
||||
if(Model::GameBoyColor())
|
||||
if(Model::GameBoyColor() && status.cgbMode)
|
||||
if(address == 0xff56 && cycle == 2) { //RP
|
||||
return;
|
||||
}
|
||||
|
||||
if(Model::GameBoyColor())
|
||||
if(address == 0xff6c && cycle == 2) { //???
|
||||
status.ff6c = data.bit(0);
|
||||
if(Model::GameBoyColor() && (cartridge.bootromEnable || status.opriEnable))
|
||||
if(address == 0xff6c && cycle == 2) { //OPRI
|
||||
status.opri = data.bit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -296,7 +303,7 @@ auto CPU::writeIO(u32 cycle, n16 address, n8 data) -> void {
|
||||
return;
|
||||
}
|
||||
|
||||
if(Model::GameBoyColor())
|
||||
if(Model::GameBoyColor() && status.cgbMode)
|
||||
if(address == 0xff70 && cycle == 2) { //SVBK
|
||||
status.wramBank = data.bit(0,3);
|
||||
return;
|
||||
|
||||
@@ -28,6 +28,8 @@ auto CPU::serialize(serializer& s) -> void {
|
||||
|
||||
s(status.interruptFlag);
|
||||
|
||||
s(status.cgbMode);
|
||||
s(status.opriEnable);
|
||||
s(status.speedSwitch);
|
||||
s(status.speedDouble);
|
||||
|
||||
@@ -36,7 +38,7 @@ auto CPU::serialize(serializer& s) -> void {
|
||||
s(status.dmaLength);
|
||||
s(status.hdmaActive);
|
||||
|
||||
s(status.ff6c);
|
||||
s(status.opri);
|
||||
|
||||
s(status.wramBank);
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ auto CPU::step() -> void {
|
||||
|
||||
auto CPU::step(u32 clocks) -> void {
|
||||
for(auto n : range(clocks)) {
|
||||
status.div++;
|
||||
if(!r.stop) status.div++;
|
||||
if((n4 )status.div == 0) timer262144hz();
|
||||
if((n6 )status.div == 0) timer65536hz();
|
||||
if((n8 )status.div == 0) timer16384hz();
|
||||
|
||||
+39
-14
@@ -13,6 +13,8 @@
|
||||
//0x07: palette#
|
||||
|
||||
auto PPU::readTileCGB(bool select, u32 x, u32 y, n16& tiledata, n8& attributes) -> void {
|
||||
if(!cpu.status.cgbMode) return readTileDMG(select, x, y, tiledata);
|
||||
|
||||
n14 tilemapAddress = 0x1800 + (select << 10);
|
||||
tilemapAddress += (((y >> 3) << 5) + (x >> 3)) & 0x03ff;
|
||||
|
||||
@@ -35,6 +37,8 @@ auto PPU::readTileCGB(bool select, u32 x, u32 y, n16& tiledata, n8& attributes)
|
||||
}
|
||||
|
||||
auto PPU::readObjectCGB(i16 y, n8 tile, n8 attributes, n16& tiledata) -> void {
|
||||
if(!cpu.status.cgbMode) return readObjectDMG(y, tile, attributes, tiledata);
|
||||
|
||||
const s32 Height = (status.obSize == 0 ? 8 : 16);
|
||||
tile &= ~status.obSize;
|
||||
y = status.ly - y;
|
||||
@@ -65,6 +69,9 @@ auto PPU::scanlineCGB() -> void {
|
||||
|
||||
if(++sprites == 10) break;
|
||||
}
|
||||
|
||||
//sort by X-coordinate if opri (DMG priority) is enabled
|
||||
if(cpu.status.opri) sort(sprite, sprites, [](auto l, auto r) { return l.x < r.x; });
|
||||
}
|
||||
|
||||
auto PPU::runCGB() -> void {
|
||||
@@ -72,22 +79,34 @@ auto PPU::runCGB() -> void {
|
||||
ob = {};
|
||||
|
||||
n15 color = 0x7fff;
|
||||
runBackgroundCGB();
|
||||
if(cpu.status.cgbMode || status.bgEnable) runBackgroundCGB();
|
||||
if(latch.windowDisplayEnable) runWindowCGB();
|
||||
if(status.obEnable) runObjectsCGB();
|
||||
|
||||
if(ob.palette == 0) {
|
||||
color = bg.color;
|
||||
} else if(bg.palette == 0) {
|
||||
color = ob.color;
|
||||
} else if(status.bgEnable == 0) {
|
||||
color = ob.color;
|
||||
} else if(bg.priority) {
|
||||
color = bg.color;
|
||||
} else if(ob.priority) {
|
||||
color = ob.color;
|
||||
if(cpu.status.cgbMode ) {
|
||||
if(ob.palette == 0) {
|
||||
color = bg.color;
|
||||
} else if(bg.palette == 0) {
|
||||
color = ob.color;
|
||||
} else if(status.bgEnable == 0) {
|
||||
color = ob.color;
|
||||
} else if(bg.priority) {
|
||||
color = bg.color;
|
||||
} else if(ob.priority) {
|
||||
color = ob.color;
|
||||
} else {
|
||||
color = bg.color;
|
||||
}
|
||||
} else {
|
||||
color = bg.color;
|
||||
if(ob.palette == 0) {
|
||||
color = bg.color;
|
||||
} else if(bg.palette == 0) {
|
||||
color = ob.color;
|
||||
} else if(ob.priority) {
|
||||
color = ob.color;
|
||||
} else {
|
||||
color = bg.color;
|
||||
}
|
||||
}
|
||||
|
||||
if(Model::GameBoyColor()) {
|
||||
@@ -107,15 +126,17 @@ auto PPU::runBackgroundCGB() -> void {
|
||||
index.bit(1) = background.tiledata.bit(15 - tileX);
|
||||
n5 palette = background.attributes.bit(0,2) << 2 | index;
|
||||
|
||||
if(!cpu.status.cgbMode) palette = bgp[index];
|
||||
bg.color = bgpd[palette];
|
||||
bg.palette = index;
|
||||
bg.priority = background.attributes.bit(7);
|
||||
if(cpu.status.cgbMode) bg.priority = background.attributes.bit(7);
|
||||
}
|
||||
|
||||
auto PPU::runWindowCGB() -> void {
|
||||
if(status.ly < status.wy) return;
|
||||
if(px + 7 < status.wx) return;
|
||||
if(px + 7 == status.wx) latch.wy++;
|
||||
if(!cpu.status.cgbMode && !status.bgEnable) return;
|
||||
|
||||
n8 scrollY = latch.wy - 1;
|
||||
n8 scrollX = px + 7 - latch.wx;
|
||||
@@ -128,9 +149,11 @@ auto PPU::runWindowCGB() -> void {
|
||||
index.bit(1) = window.tiledata.bit(15 - tileX);
|
||||
n5 palette = window.attributes.bit(0,2) << 2 | index;
|
||||
|
||||
if(!cpu.status.cgbMode) palette = bgp[index];
|
||||
|
||||
bg.color = bgpd[palette];
|
||||
bg.palette = index;
|
||||
bg.priority = window.attributes.bit(7);
|
||||
if(cpu.status.cgbMode) bg.priority = window.attributes.bit(7);
|
||||
}
|
||||
|
||||
auto PPU::runObjectsCGB() -> void {
|
||||
@@ -148,6 +171,8 @@ auto PPU::runObjectsCGB() -> void {
|
||||
if(index == 0) continue;
|
||||
n5 palette = s.attributes.bit(0,2) << 2 | index;
|
||||
|
||||
if(!cpu.status.cgbMode) palette = obp[index];
|
||||
|
||||
ob.color = obpd[palette];
|
||||
ob.palette = index;
|
||||
ob.priority = !s.attributes.bit(7);
|
||||
|
||||
+6
-6
@@ -91,7 +91,7 @@ auto PPU::readIO(u32 cycle, n16 address, n8 data) -> n8 {
|
||||
return status.wx;
|
||||
}
|
||||
|
||||
if(Model::GameBoyColor())
|
||||
if(Model::GameBoyColor() && cpu.status.cgbMode)
|
||||
if(address == 0xff4f && cycle == 2) { //VBK
|
||||
return status.vramBank;
|
||||
}
|
||||
@@ -103,7 +103,7 @@ auto PPU::readIO(u32 cycle, n16 address, n8 data) -> n8 {
|
||||
return data;
|
||||
}
|
||||
|
||||
if(Model::GameBoyColor())
|
||||
if(Model::GameBoyColor() && ((!cpu.status.cgbMode && cartridge.bootromEnable) || cpu.status.cgbMode))
|
||||
if(address == 0xff69 && cycle == 2) { //BGPD
|
||||
return bgpd[status.bgpi >> 1].byte(status.bgpi & 1);
|
||||
}
|
||||
@@ -115,7 +115,7 @@ auto PPU::readIO(u32 cycle, n16 address, n8 data) -> n8 {
|
||||
return data;
|
||||
}
|
||||
|
||||
if(Model::GameBoyColor())
|
||||
if(Model::GameBoyColor() && ((!cpu.status.cgbMode && cartridge.bootromEnable) || cpu.status.cgbMode))
|
||||
if(address == 0xff6b && cycle == 2) { //OBPD
|
||||
return obpd[status.obpi >> 1].byte(status.obpi & 1);
|
||||
}
|
||||
@@ -236,7 +236,7 @@ auto PPU::writeIO(u32 cycle, n16 address, n8 data) -> void {
|
||||
return;
|
||||
}
|
||||
|
||||
if(Model::GameBoyColor())
|
||||
if(Model::GameBoyColor() && cpu.status.cgbMode)
|
||||
if(address == 0xff4f && cycle == 2) { //VBK
|
||||
status.vramBank = data.bit(0);
|
||||
return;
|
||||
@@ -249,7 +249,7 @@ auto PPU::writeIO(u32 cycle, n16 address, n8 data) -> void {
|
||||
return;
|
||||
}
|
||||
|
||||
if(Model::GameBoyColor())
|
||||
if(Model::GameBoyColor() && ((!cpu.status.cgbMode && cartridge.bootromEnable) || cpu.status.cgbMode))
|
||||
if(address == 0xff69 && cycle == 2) { //BGPD
|
||||
bgpd[status.bgpi >> 1].byte(status.bgpi & 1) = data;
|
||||
if(status.bgpiIncrement) status.bgpi++;
|
||||
@@ -262,7 +262,7 @@ auto PPU::writeIO(u32 cycle, n16 address, n8 data) -> void {
|
||||
status.obpiIncrement = data.bit(7);
|
||||
}
|
||||
|
||||
if(Model::GameBoyColor())
|
||||
if(Model::GameBoyColor() && ((!cpu.status.cgbMode && cartridge.bootromEnable) || cpu.status.cgbMode))
|
||||
if(address == 0xff6b && cycle == 2) { //OBPD
|
||||
obpd[status.obpi >> 1].byte(status.obpi & 1) = data;
|
||||
if(status.obpiIncrement) status.obpi++;
|
||||
|
||||
+1
-1
@@ -81,7 +81,7 @@ auto PPU::unload() -> void {
|
||||
}
|
||||
|
||||
auto PPU::main() -> void {
|
||||
if(!status.displayEnable) {
|
||||
if(!status.displayEnable || cpu.r.stop) {
|
||||
step(456 * 154);
|
||||
if(screen) screen->frame();
|
||||
scheduler.exit(Event::Frame);
|
||||
|
||||
@@ -18,11 +18,12 @@ auto PPU::compareLYC() const -> bool {
|
||||
auto ly = status.ly;
|
||||
auto lyc = status.lyc;
|
||||
|
||||
if(Model::GameBoy() || Model::SuperGameBoy()) {
|
||||
if(Model::GameBoy() || Model::SuperGameBoy() || !cpu.status.cgbMode) {
|
||||
auto lx = status.lx >> 2;
|
||||
if(ly != 0 && lx == 0) return 0;
|
||||
if(ly == 153 && lx == 2) return 0;
|
||||
if(ly == 153 && lx >= 3) return lyc == 0;
|
||||
return lyc == ly;
|
||||
}
|
||||
|
||||
if(Model::GameBoyColor() && cpu.lowSpeed()) {
|
||||
@@ -42,9 +43,10 @@ auto PPU::compareLYC() const -> bool {
|
||||
auto PPU::getLY() const -> n8 {
|
||||
auto ly = status.ly;
|
||||
|
||||
if(Model::GameBoy() || Model::SuperGameBoy()) {
|
||||
if(Model::GameBoy() || Model::SuperGameBoy() || !cpu.status.cgbMode) {
|
||||
auto lx = status.lx >> 2;
|
||||
if(ly == 153 && lx >= 1) return 0;
|
||||
return ly;
|
||||
}
|
||||
|
||||
if(Model::GameBoyColor() && cpu.lowSpeed()) {
|
||||
|
||||
@@ -13,7 +13,7 @@ Cartridge& cartridge = cartridgeSlot.cartridge;
|
||||
Cartridge::Cartridge() {
|
||||
mrom.data = new n8[mrom.size = 32 * 1024 * 1024];
|
||||
sram.data = new n8[sram.size = 32 * 1024];
|
||||
eeprom.data = new n8[eeprom.size = 8 * 1024];
|
||||
eeprom.data = new n8[eeprom.size = 16 * 1024];
|
||||
flash.data = new n8[flash.size = 128 * 1024];
|
||||
}
|
||||
|
||||
@@ -50,9 +50,9 @@ auto Cartridge::connect() -> void {
|
||||
|
||||
if(auto fp = pak->read("save.eeprom")) {
|
||||
has.eeprom = true;
|
||||
eeprom.size = min(8_KiB, fp->size());
|
||||
eeprom.size = min(16_KiB, fp->size());
|
||||
eeprom.bits = eeprom.size <= 512 ? 6 : 14;
|
||||
if(eeprom.size == 0) eeprom.size = 8192, eeprom.bits = 0; //auto-detect size
|
||||
if(eeprom.size == 0) eeprom.size = 16384, eeprom.bits = 0; //auto-detect size
|
||||
eeprom.mask = mrom.size > 16 * 1024 * 1024 ? 0x0fffff00 : 0x0f000000;
|
||||
eeprom.test = mrom.size > 16 * 1024 * 1024 ? 0x0dffff00 : 0x0d000000;
|
||||
for(auto n : range(eeprom.size)) eeprom.data[n] = 0xff;
|
||||
@@ -96,6 +96,7 @@ auto Cartridge::save() -> void {
|
||||
}
|
||||
|
||||
if(auto fp = pak->write("save.eeprom")) {
|
||||
if(fp->size() < eeprom.size) fp->resize(eeprom.size);
|
||||
fp->write({eeprom.data, eeprom.size});
|
||||
}
|
||||
|
||||
|
||||
@@ -13,10 +13,13 @@ auto Cartridge::EEPROM::read() -> bool {
|
||||
//EEPROM size auto-detection
|
||||
if(bits == 0 && mode == Mode::ReadAddress) {
|
||||
print("EEPROM address bits: ", --addressbits, "\n");
|
||||
bits = addressbits == 6 ? 6 : 14;
|
||||
size = 8192;
|
||||
bits = addressbits;
|
||||
size = 1 << addressbits;
|
||||
mode = Mode::ReadData;
|
||||
offset = 0;
|
||||
|
||||
print("EEPROM size: ", size, "\n");
|
||||
for(auto n : range(size)) data[n] = 0xff;
|
||||
//fallthrough
|
||||
}
|
||||
|
||||
|
||||
+7
-4
@@ -28,13 +28,13 @@ auto APU::main() -> void {
|
||||
if(state.nmiLine) {
|
||||
state.nmiLine = 0; //edge-sensitive
|
||||
debugger.interrupt("NMI");
|
||||
irq(0, 0x0066, 0xff);
|
||||
nmi();
|
||||
}
|
||||
|
||||
if(state.intLine) {
|
||||
//level-sensitive
|
||||
debugger.interrupt("IRQ");
|
||||
irq(1, 0x0038, 0xff);
|
||||
irq();
|
||||
}
|
||||
|
||||
debugger.instruction();
|
||||
@@ -69,10 +69,13 @@ auto APU::power(bool reset) -> void {
|
||||
Z80::power();
|
||||
Thread::create(system.frequency() / 15.0, {&APU::main, this});
|
||||
if(!reset) {
|
||||
Z80::power();
|
||||
ram.fill();
|
||||
state.resLine = 0;
|
||||
state.busreqLine = 0;
|
||||
state.busreqLatch = 0;
|
||||
} else {
|
||||
Z80::reset();
|
||||
}
|
||||
state.nmiLine = 0;
|
||||
state.intLine = 0;
|
||||
@@ -81,11 +84,11 @@ auto APU::power(bool reset) -> void {
|
||||
}
|
||||
|
||||
auto APU::restart() -> void {
|
||||
Z80::power();
|
||||
Z80::reset();
|
||||
Thread::restart({&APU::main, this});
|
||||
state.nmiLine = 0;
|
||||
state.intLine = 0;
|
||||
state.bank = 0;
|
||||
state.busreqLatch = state.busreqLine;
|
||||
opn2.power(true);
|
||||
}
|
||||
|
||||
|
||||
+12
-4
@@ -13,7 +13,7 @@ inline auto Bus::read(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
|
||||
|
||||
if(address >= 0x400000 && address <= 0x7fffff) {
|
||||
waitRefreshExternal();
|
||||
if(!cartridge.bootable()) {
|
||||
if(!cartridge.bootable() || !MegaCD()) {
|
||||
data = cartridge.read(upper, lower, address, data);
|
||||
} else {
|
||||
data = mcd.readExternal(upper, lower, address, data);
|
||||
@@ -22,7 +22,11 @@ inline auto Bus::read(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
|
||||
}
|
||||
|
||||
if(address >= 0x800000 && address <= 0x9fffff) {
|
||||
data = m32x.readExternal(upper, lower, address, data);
|
||||
if(!Mega32X()) {
|
||||
data = cartridge.read(upper, lower, address, data);
|
||||
} else {
|
||||
data = m32x.readExternal(upper, lower, address, data);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -71,7 +75,7 @@ inline auto Bus::write(n1 upper, n1 lower, n24 address, n16 data) -> void {
|
||||
|
||||
if(address >= 0x400000 && address <= 0x7fffff) {
|
||||
waitRefreshExternal();
|
||||
if(!cartridge.bootable()) {
|
||||
if(!cartridge.bootable() || !MegaCD()) {
|
||||
cartridge.write(upper, lower, address, data);
|
||||
} else {
|
||||
mcd.writeExternal(upper, lower, address, data);
|
||||
@@ -80,7 +84,11 @@ inline auto Bus::write(n1 upper, n1 lower, n24 address, n16 data) -> void {
|
||||
}
|
||||
|
||||
if(address >= 0x800000 && address <= 0x9fffff) {
|
||||
m32x.writeExternal(upper, lower, address, data);
|
||||
if(!Mega32X()) {
|
||||
cartridge.write(upper, lower, address, data);
|
||||
} else {
|
||||
m32x.writeExternal(upper, lower, address, data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ struct Banked : Interface {
|
||||
}
|
||||
}
|
||||
|
||||
auto read(n1 upper, n1 lower, n22 address, n16 data) -> n16 override {
|
||||
auto read(n1 upper, n1 lower, n24 address, n16 data) -> n16 override {
|
||||
if(address >= sramAddr && address < sramAddr+sramSize) {
|
||||
if(wram && ramEnable) {
|
||||
return wram[address >> 1];
|
||||
@@ -59,7 +59,7 @@ struct Banked : Interface {
|
||||
return data = rom[offset >> 1];
|
||||
}
|
||||
|
||||
auto write(n1 upper, n1 lower, n22 address, n16 data) -> void override {
|
||||
auto write(n1 upper, n1 lower, n24 address, n16 data) -> void override {
|
||||
//emulating ramWritable will break commercial software:
|
||||
//it does not appear that many (any?) games actually connect $a130f1.d1 to /WE;
|
||||
//hence RAM ends up always being writable, and many games fail to set d1=1
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
namespace Board {
|
||||
|
||||
#include "standard.cpp"
|
||||
#include "linear.cpp"
|
||||
#include "banked.cpp"
|
||||
#include "svp.cpp"
|
||||
|
||||
@@ -11,8 +11,8 @@ struct Interface {
|
||||
virtual auto save() -> void {}
|
||||
virtual auto main() -> void;
|
||||
virtual auto step(u32 clocks) -> void;
|
||||
virtual auto read(n1 upper, n1 lower, n22 address, n16 data) -> n16 { return data; }
|
||||
virtual auto write(n1 upper, n1 lower, n22 address, n16 data) -> void {}
|
||||
virtual auto read(n1 upper, n1 lower, n24 address, n16 data) -> n16 { return data; }
|
||||
virtual auto write(n1 upper, n1 lower, n24 address, n16 data) -> void {}
|
||||
virtual auto readIO(n1 upper, n1 lower, n24 address, n16 data) -> n16 { return data; }
|
||||
virtual auto writeIO(n1 upper, n1 lower, n24 address, n16 data) -> void {}
|
||||
virtual auto vblank(bool line) -> void {}
|
||||
|
||||
@@ -11,7 +11,7 @@ struct GameGenie : Interface {
|
||||
auto save() -> void override {
|
||||
}
|
||||
|
||||
auto read(n1 upper, n1 lower, n22 address, n16 data) -> n16 override {
|
||||
auto read(n1 upper, n1 lower, n24 address, n16 data) -> n16 override {
|
||||
if(enable) {
|
||||
for(auto& code : codes) {
|
||||
if(code.enable && code.address == address) return data = code.data;
|
||||
@@ -21,7 +21,7 @@ struct GameGenie : Interface {
|
||||
return data = rom[address >> 1];
|
||||
}
|
||||
|
||||
auto write(n1 upper, n1 lower, n22 address, n16 data) -> void override {
|
||||
auto write(n1 upper, n1 lower, n24 address, n16 data) -> void override {
|
||||
if(enable) {
|
||||
if(slot.connected()) return slot.cartridge.write(upper, lower, address, data);
|
||||
}
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
struct JCart : Linear {
|
||||
using Linear::Linear;
|
||||
struct JCart : Standard {
|
||||
using Standard::Standard;
|
||||
|
||||
auto read(n1 upper, n1 lower, n22 address, n16 data) -> n16 override {
|
||||
auto read(n1 upper, n1 lower, n24 address, n16 data) -> n16 override {
|
||||
if(address >= 0x380000) {
|
||||
data.bit( 0, 5) = 0b111111; //controller port 1
|
||||
data.bit( 6) = jcartSelect; //TH state
|
||||
data.bit( 8,13) = 0b111111; //controller port 2
|
||||
data.bit(14) = 0; //forced low
|
||||
}
|
||||
return Linear::read(upper, lower, address, data);
|
||||
return Standard::read(upper, lower, address, data);
|
||||
}
|
||||
|
||||
auto write(n1 upper, n1 lower, n22 address, n16 data) -> void override {
|
||||
auto write(n1 upper, n1 lower, n24 address, n16 data) -> void override {
|
||||
if(address >= 0x380000) {
|
||||
jcartSelect = data.bit(0);
|
||||
}
|
||||
return Linear::write(upper, lower, address, data);
|
||||
return Standard::write(upper, lower, address, data);
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
Linear::serialize(s);
|
||||
Standard::serialize(s);
|
||||
s(jcartSelect);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ struct Linear : Interface {
|
||||
}
|
||||
}
|
||||
|
||||
auto read(n1 upper, n1 lower, n22 address, n16 data) -> n16 override {
|
||||
auto read(n1 upper, n1 lower, n24 address, n16 data) -> n16 override {
|
||||
if(address >= sramAddr && address < sramAddr+sramSize) {
|
||||
if(wram && ramEnable) {
|
||||
return wram[address >> 1];
|
||||
@@ -57,7 +57,7 @@ struct Linear : Interface {
|
||||
return rom[address >> 1];
|
||||
}
|
||||
|
||||
auto write(n1 upper, n1 lower, n22 address, n16 data) -> void override {
|
||||
auto write(n1 upper, n1 lower, n24 address, n16 data) -> void override {
|
||||
//emulating ramWritable will break commercial software:
|
||||
//it does not appear that many (any?) games actually connect $a130f1.d1 to /WE;
|
||||
//hence RAM ends up always being writable, and many games fail to set d1=1
|
||||
|
||||
@@ -1,104 +1,63 @@
|
||||
struct Mega32X : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Writable<n16> wram;
|
||||
Memory::Writable<n8 > uram;
|
||||
Memory::Writable<n8 > lram;
|
||||
u32 sramAddr, sramSize;
|
||||
M24C m24c;
|
||||
unique_pointer<Board::Interface> board;
|
||||
|
||||
auto load() -> void override {
|
||||
if(auto fp = pak->read("program.rom")) {
|
||||
m32x.rom.allocate(fp->size() >> 1);
|
||||
for(auto address : range(fp->size() >> 1)) {
|
||||
m32x.rom.program(address, fp->readm(2L));
|
||||
}
|
||||
if(pak) {
|
||||
board = new Board::Standard(*cartridge);
|
||||
} else {
|
||||
board = new Board::Interface(*cartridge);
|
||||
}
|
||||
|
||||
if(auto fp = pak->read("save.ram")) {
|
||||
Interface::load(sramAddr, sramSize, wram, uram, lram, "save.ram");
|
||||
}
|
||||
board->pak = pak;
|
||||
board->load();
|
||||
|
||||
if(auto fp = pak->read("save.eeprom")) {
|
||||
Interface::load(sramAddr, sramSize, m24c, "save.eeprom");
|
||||
rsda = fp->attribute("rsda").natural();
|
||||
wsda = fp->attribute("wsda").natural();
|
||||
wscl = fp->attribute("wscl").natural();
|
||||
}
|
||||
cartridge->child = *board;
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
if(auto fp = pak->write("save.ram")) {
|
||||
Interface::save(wram, uram, lram, "save.ram");
|
||||
}
|
||||
|
||||
if(auto fp = pak->write("save.eeprom")) {
|
||||
Interface::save(m24c, "save.eeprom");
|
||||
}
|
||||
board->save();
|
||||
}
|
||||
|
||||
auto read(n1 upper, n1 lower, n22 address, n16 data) -> n16 override {
|
||||
if(address >= sramAddr && address < sramAddr+sramSize) {
|
||||
if(wram) {
|
||||
return wram[address >> 1];
|
||||
}
|
||||
|
||||
if(uram) {
|
||||
return uram[address >> 1] * 0x0101;
|
||||
}
|
||||
|
||||
if(lram) {
|
||||
return lram[address >> 1] * 0x0101;
|
||||
}
|
||||
|
||||
if(m24c && eepromEnable) {
|
||||
if(upper && rsda >> 3 == 1) data.bit(rsda) = m24c.read();
|
||||
if(lower && rsda >> 3 == 0) data.bit(rsda) = m24c.read();
|
||||
return data;
|
||||
}
|
||||
auto read(n1 upper, n1 lower, n24 address, n16 data) -> n16 override {
|
||||
if(!m32x.io.adapterEnable) {
|
||||
return board->read(upper, lower, address, data);
|
||||
}
|
||||
|
||||
return m32x.readExternal(upper, lower, address, data);
|
||||
if(address >= 0x000000 && address <= 0x0000ff) {
|
||||
return m32x.readExternal(upper, lower, address, data);
|
||||
}
|
||||
|
||||
if(m32x.dreq.vram) {
|
||||
return board->read(upper, lower, address, data);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
auto write(n1 upper, n1 lower, n22 address, n16 data) -> void override {
|
||||
if(address >= sramAddr && address < sramAddr+sramSize) {
|
||||
if(wram) {
|
||||
if(upper) wram[address >> 1].byte(1) = data.byte(1);
|
||||
if(lower) wram[address >> 1].byte(0) = data.byte(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if(uram) {
|
||||
if(upper) uram[address >> 1] = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if(lram) {
|
||||
if(lower) lram[address >> 1] = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if(m24c) {
|
||||
if(m32x.rom.size() * 2 > 0x200000 && upper && lower) {
|
||||
eepromEnable = !data.bit(0);
|
||||
return;
|
||||
}
|
||||
if(upper && wscl >> 3 == 1) m24c.clock = data.bit(wscl);
|
||||
if(upper && wsda >> 3 == 1) m24c.data = data.bit(wsda);
|
||||
if(lower && wscl >> 3 == 0) m24c.clock = data.bit(wscl);
|
||||
if(lower && wsda >> 3 == 0) m24c.data = data.bit(wsda);
|
||||
return m24c.write();
|
||||
}
|
||||
auto write(n1 upper, n1 lower, n24 address, n16 data) -> void override {
|
||||
if(!m32x.io.adapterEnable) {
|
||||
return board->write(upper, lower, address, data);
|
||||
}
|
||||
|
||||
return m32x.writeExternal(upper, lower, address, data);
|
||||
if(address >= 0x000000 && address <= 0x0000ff) {
|
||||
return m32x.writeExternal(upper, lower, address, data);
|
||||
}
|
||||
|
||||
if(m32x.dreq.vram) {
|
||||
return board->write(upper, lower, address, data);
|
||||
}
|
||||
}
|
||||
|
||||
auto readIO(n1 upper, n1 lower, n24 address, n16 data) -> n16 override {
|
||||
data = board->readIO(upper, lower, address, data);
|
||||
|
||||
return m32x.readExternalIO(upper, lower, address, data);
|
||||
}
|
||||
|
||||
auto writeIO(n1 upper, n1 lower, n24 address, n16 data) -> void override {
|
||||
board->writeIO(upper, lower, address, data);
|
||||
|
||||
return m32x.writeExternalIO(upper, lower, address, data);
|
||||
}
|
||||
|
||||
@@ -111,23 +70,10 @@ struct Mega32X : Interface {
|
||||
}
|
||||
|
||||
auto power(bool reset) -> void override {
|
||||
eepromEnable = m32x.rom.size() * 2 <= 0x200000;
|
||||
m24c.power();
|
||||
board->power(reset);
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(wram);
|
||||
s(uram);
|
||||
s(lram);
|
||||
s(m24c);
|
||||
s(eepromEnable);
|
||||
s(rsda);
|
||||
s(wsda);
|
||||
s(wscl);
|
||||
s(*board);
|
||||
}
|
||||
|
||||
n1 eepromEnable;
|
||||
n4 rsda;
|
||||
n4 wsda;
|
||||
n4 wscl;
|
||||
};
|
||||
@@ -0,0 +1,157 @@
|
||||
struct Standard : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<n16> rom;
|
||||
Memory::Writable<n16> wram;
|
||||
Memory::Writable<n8 > uram;
|
||||
Memory::Writable<n8 > lram;
|
||||
u32 sramAddr, sramSize;
|
||||
M24C m24c;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(rom, "program.rom");
|
||||
if(auto fp = pak->read("save.ram")) {
|
||||
Interface::load(sramAddr, sramSize, wram, uram, lram, "save.ram");
|
||||
ramEnable = fp->attribute("enable").boolean() ? 1 : 0;
|
||||
}
|
||||
if(auto fp = pak->read("save.eeprom")) {
|
||||
Interface::load(sramAddr, sramSize, m24c, "save.eeprom");
|
||||
rsda = fp->attribute("rsda").natural();
|
||||
wsda = fp->attribute("wsda").natural();
|
||||
wscl = fp->attribute("wscl").natural();
|
||||
}
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
if(auto fp = pak->write("save.ram")) {
|
||||
Interface::save(wram, uram, lram, "save.ram");
|
||||
}
|
||||
if(auto fp = pak->write("save.eeprom")) {
|
||||
Interface::save(m24c, "save.eeprom");
|
||||
}
|
||||
}
|
||||
|
||||
auto read(n1 upper, n1 lower, n24 address, n16 data) -> n16 override {
|
||||
if(address >= 0x400000) {
|
||||
if(address >= rom.size()*2) return data;
|
||||
return rom[address >> 1];
|
||||
}
|
||||
|
||||
auto bank = romBank[address >> 19];
|
||||
if(address >= sramAddr && address < sramAddr+sramSize) {
|
||||
if(wram && (ramEnable || bank > 0x3f)) {
|
||||
return wram[address >> 1];
|
||||
}
|
||||
|
||||
if(uram && (ramEnable || bank > 0x3f)) {
|
||||
return uram[address >> 1] * 0x0101;
|
||||
}
|
||||
|
||||
if(lram && (ramEnable || bank > 0x3f)) {
|
||||
return lram[address >> 1] * 0x0101;
|
||||
}
|
||||
|
||||
if(m24c && (eepromEnable || bank > 0x3f)) {
|
||||
if(upper && rsda >> 3 == 1) data.bit(rsda) = m24c.read();
|
||||
if(lower && rsda >> 3 == 0) data.bit(rsda) = m24c.read();
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
if(bank > 0x3f) return data;
|
||||
|
||||
n24 offset = bank << 19 | (n19)address;
|
||||
if(offset >= rom.size()*2) return data;
|
||||
return rom[offset >> 1];
|
||||
}
|
||||
|
||||
auto write(n1 upper, n1 lower, n24 address, n16 data) -> void override {
|
||||
if(address >= sramAddr && address < sramAddr+sramSize) {
|
||||
if(wram && ramWritable) {
|
||||
if(upper) wram[address >> 1].byte(1) = data.byte(1);
|
||||
if(lower) wram[address >> 1].byte(0) = data.byte(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if(uram && ramWritable) {
|
||||
if(upper) uram[address >> 1] = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if(lram && ramWritable) {
|
||||
if(lower) lram[address >> 1] = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if(m24c) {
|
||||
if(wscl == 8 && upper && lower) { // control via word write (32Mbit Acclaim mappers only)
|
||||
eepromEnable = !data.bit(0);
|
||||
return;
|
||||
}
|
||||
if(upper && wscl >> 3 == 1) m24c.clock = data.bit(wscl);
|
||||
if(upper && wsda >> 3 == 1) m24c.data = data.bit(wsda);
|
||||
if(lower && wscl >> 3 == 0) m24c.clock = data.bit(wscl);
|
||||
if(lower && wsda >> 3 == 0) m24c.data = data.bit(wsda);
|
||||
return m24c.write();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto readIO(n1 upper, n1 lower, n24 address, n16 data) -> n16 override {
|
||||
return data;
|
||||
}
|
||||
|
||||
auto writeIO(n1 upper, n1 lower, n24 address, n16 data) -> void override {
|
||||
if(!lower) {
|
||||
//todo: unconfirmed
|
||||
debug(unusual, "[Cartridge::Banked::writeIO] lower=0");
|
||||
return;
|
||||
}
|
||||
|
||||
if(address == 0xa130f0) {
|
||||
ramEnable = data.bit(0);
|
||||
ramWritable = !data.bit(1);
|
||||
}
|
||||
|
||||
if(address == 0xa130f2) romBank[1] = data.bit(0,5);
|
||||
if(address == 0xa130f4) romBank[2] = data.bit(0,5);
|
||||
if(address == 0xa130f6) romBank[3] = data.bit(0,5);
|
||||
if(address == 0xa130f8) romBank[4] = data.bit(0,5);
|
||||
if(address == 0xa130fa) romBank[5] = data.bit(0,5);
|
||||
if(address == 0xa130fc) romBank[6] = data.bit(0,5);
|
||||
if(address == 0xa130fe) romBank[7] = data.bit(0,5);
|
||||
}
|
||||
|
||||
auto power(bool reset) -> void override {
|
||||
for(auto index : range(8)) {
|
||||
if(rom.size()*2 > index << 19)
|
||||
romBank[index] = index;
|
||||
else
|
||||
romBank[index] = ~0;
|
||||
}
|
||||
ramWritable = 1;
|
||||
eepromEnable = m24c && wscl != 8 ? 1 : 0; // for 32Mbit Acclaim mappers (670125 & 670127) [id based on wscl]
|
||||
m24c.power();
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(romBank);
|
||||
s(wram);
|
||||
s(uram);
|
||||
s(lram);
|
||||
s(m24c);
|
||||
s(ramEnable);
|
||||
s(ramWritable);
|
||||
s(eepromEnable);
|
||||
s(rsda);
|
||||
s(wsda);
|
||||
s(wscl);
|
||||
}
|
||||
|
||||
n7 romBank[8];
|
||||
n1 ramEnable = 0;
|
||||
n1 ramWritable;
|
||||
n1 eepromEnable;
|
||||
n4 rsda;
|
||||
n4 wsda;
|
||||
n4 wscl;
|
||||
};
|
||||
@@ -53,7 +53,7 @@ struct SVP : Interface, SSP1601 {
|
||||
SSP1601::instruction();
|
||||
}
|
||||
|
||||
auto read(n1 upper, n1 lower, n22 address, n16 data) -> n16 override {
|
||||
auto read(n1 upper, n1 lower, n24 address, n16 data) -> n16 override {
|
||||
if(address >= 0x000000 && address <= 0x1fffff) {
|
||||
data = rom[address >> 1];
|
||||
}
|
||||
@@ -77,7 +77,7 @@ struct SVP : Interface, SSP1601 {
|
||||
return data;
|
||||
}
|
||||
|
||||
auto write(n1 upper, n1 lower, n22 address, n16 data) -> void override {
|
||||
auto write(n1 upper, n1 lower, n24 address, n16 data) -> void override {
|
||||
if(address >= 0x300000 && address <= 0x37ffff) {
|
||||
if(upper) dram[address >> 1].byte(1) = data.byte(1);
|
||||
if(lower) dram[address >> 1].byte(0) = data.byte(0);
|
||||
|
||||
@@ -25,12 +25,10 @@ auto Cartridge::connect() -> void {
|
||||
board = new Board::SVP(*this);
|
||||
} else if(pak->attribute("label") == "Game Genie") {
|
||||
board = new Board::GameGenie(*this);
|
||||
} else if(pak->read("program.rom") && pak->read("program.rom")->size() > 0x400000) {
|
||||
board = new Board::Banked(*this);
|
||||
} else if(pak->attribute("jcart").boolean()) {
|
||||
board = new Board::JCart(*this);
|
||||
} else {
|
||||
board = new Board::Linear(*this);
|
||||
board = new Board::Standard(*this);
|
||||
}
|
||||
board->pak = pak;
|
||||
board->load();
|
||||
@@ -43,6 +41,7 @@ auto Cartridge::disconnect() -> void {
|
||||
board->unload();
|
||||
board->pak.reset();
|
||||
board.reset();
|
||||
child.reset();
|
||||
pak.reset();
|
||||
node.reset();
|
||||
information = {};
|
||||
@@ -63,16 +62,24 @@ auto Cartridge::step(u32 clocks) -> void {
|
||||
}
|
||||
|
||||
auto Cartridge::power(bool reset) -> void {
|
||||
if(!board) board = new Board::Interface(*this);
|
||||
if(!board) {
|
||||
if(Mega32X()) {
|
||||
board = new Board::Mega32X{*this};
|
||||
} else {
|
||||
board = new Board::Interface(*this);
|
||||
}
|
||||
|
||||
board->load();
|
||||
}
|
||||
Thread::create(board->frequency(), {&Cartridge::main, this});
|
||||
board->power(reset);
|
||||
}
|
||||
|
||||
auto Cartridge::read(n1 upper, n1 lower, n22 address, n16 data) -> n16 {
|
||||
auto Cartridge::read(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
|
||||
return board->read(upper, lower, address, data);
|
||||
}
|
||||
|
||||
auto Cartridge::write(n1 upper, n1 lower, n22 address, n16 data) -> void {
|
||||
auto Cartridge::write(n1 upper, n1 lower, n24 address, n16 data) -> void {
|
||||
return board->write(upper, lower, address, data);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,8 +19,8 @@ struct Cartridge : Thread {
|
||||
auto step(u32 clocks) -> void;
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
auto read(n1 upper, n1 lower, n22 address, n16 data) -> n16;
|
||||
auto write(n1 upper, n1 lower, n22 address, n16 data) -> void;
|
||||
auto read(n1 upper, n1 lower, n24 address, n16 data) -> n16;
|
||||
auto write(n1 upper, n1 lower, n24 address, n16 data) -> void;
|
||||
|
||||
auto readIO(n1 upper, n1 lower, n24 address, n16 data) -> n16;
|
||||
auto writeIO(n1 upper, n1 lower, n24 address, n16 data) -> void;
|
||||
@@ -38,6 +38,7 @@ struct Cartridge : Thread {
|
||||
} information;
|
||||
|
||||
unique_pointer<Board::Interface> board;
|
||||
maybe<Board::Interface&> child;
|
||||
};
|
||||
|
||||
#include "slot.hpp"
|
||||
|
||||
@@ -5,5 +5,6 @@ namespace ares::MegaDrive {
|
||||
#include "port.cpp"
|
||||
#include "control-pad/control-pad.cpp"
|
||||
#include "fighting-pad/fighting-pad.cpp"
|
||||
#include "mega-mouse/mega-mouse.cpp"
|
||||
|
||||
}
|
||||
@@ -26,3 +26,4 @@ struct Controller {
|
||||
#include "port.hpp"
|
||||
#include "control-pad/control-pad.hpp"
|
||||
#include "fighting-pad/fighting-pad.hpp"
|
||||
#include "mega-mouse/mega-mouse.hpp"
|
||||
@@ -0,0 +1,147 @@
|
||||
MegaMouse::MegaMouse(Node::Port parent) {
|
||||
node = parent->append<Node::Peripheral>("Mega Mouse");
|
||||
|
||||
x = node->append<Node::Input::Axis>("X");
|
||||
y = node->append<Node::Input::Axis>("Y");
|
||||
left = node->append<Node::Input::Button>("Left");
|
||||
right = node->append<Node::Input::Button>("Right");
|
||||
middle = node->append<Node::Input::Button>("Middle");
|
||||
start = node->append<Node::Input::Button>("Start");
|
||||
|
||||
status[0] = 0x0; // TH high
|
||||
status[1] = 0xB; // TH low
|
||||
status[2] = 0xF; // 1st nibble
|
||||
status[3] = 0xF; // 2nd
|
||||
// status[4..9] // 3rd .. 8th nibbles
|
||||
|
||||
// Is is said that some games do not do the hanshake correctly
|
||||
// and rely heavily on timing, so we try to mimick the real
|
||||
// timing closely.
|
||||
|
||||
// One game has a programming error where it polls for TL low
|
||||
// instead of TL high for one of the nibbles so it falls rights
|
||||
// through and works by chance. With a 1MHz clock here, vertical
|
||||
// cursor motion was intermittent. So 2MHz is used.
|
||||
double timerfreq = 2'000'000;
|
||||
|
||||
// time between falling edge of clock (TR) and
|
||||
// handshake on TL.
|
||||
//
|
||||
// For first nibble, the measured time is min. 12us, max 34us
|
||||
// For folling nibbles are available in 12-14 us.
|
||||
t_handshake = 14 * timerfreq / 1000000;
|
||||
|
||||
// data changes a bit before the TL handshake occurs,
|
||||
// precisely 4.8us before. But for some games, 4.8 does not
|
||||
// work correclty and it helps to have the data update sooner. So
|
||||
// 10 is used here.
|
||||
t_data = 10 * timerfreq / 1000000;
|
||||
|
||||
Thread::create(timerfreq, {&MegaMouse::main, this});
|
||||
Thread::synchronize(cpu);
|
||||
}
|
||||
|
||||
MegaMouse::~MegaMouse() {
|
||||
Thread::destroy();
|
||||
}
|
||||
|
||||
auto MegaMouse::main() -> void {
|
||||
// process clocking after building the data to
|
||||
// cause at least some lag. In real life, it takes
|
||||
// about 10us to the mouse to react and change TL
|
||||
// to reflect TR...
|
||||
if (timeout)
|
||||
{
|
||||
timeout--;
|
||||
|
||||
if (timeout == t_data) {
|
||||
index++;
|
||||
if (index > 9) {
|
||||
index = 1;
|
||||
}
|
||||
}
|
||||
if (timeout == 0) {
|
||||
tl = tr;
|
||||
}
|
||||
}
|
||||
Thread::step(1);
|
||||
Thread::synchronize(cpu);
|
||||
}
|
||||
|
||||
auto MegaMouse::readData() -> n8 {
|
||||
n8 data;
|
||||
|
||||
if (th) {
|
||||
// When TH is high, the mouse drives a constant on the data pins.
|
||||
data.bit(0,3) = status[0];
|
||||
}
|
||||
else {
|
||||
// When TH is low, output is clocked
|
||||
data.bit(0,3) = status[index];
|
||||
}
|
||||
|
||||
data.bit(4) = tl;
|
||||
data.bit(5) = tr;
|
||||
data.bit(6) = th;
|
||||
data.bit(7) = latch;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
auto MegaMouse::writeData(n8 data) -> void {
|
||||
// Falling TH
|
||||
if (!data.bit(6) && th) {
|
||||
// When TH falls low, make sure the second nibble is driven. The
|
||||
// index is otherwise controlled by clocking on TR.
|
||||
index = 1;
|
||||
|
||||
platform->input(x);
|
||||
platform->input(y);
|
||||
platform->input(left);
|
||||
platform->input(right);
|
||||
platform->input(middle);
|
||||
platform->input(start);
|
||||
|
||||
// Button byte
|
||||
status[4] = 0;
|
||||
status[5].bit(0) = left->value();
|
||||
status[5].bit(1) = right->value();
|
||||
status[5].bit(2) = middle->value();
|
||||
status[5].bit(3) = start->value();
|
||||
|
||||
i16 ax = x->value();
|
||||
i16 ay = -y->value();
|
||||
|
||||
if (ax > maxspeed) {
|
||||
ax = maxspeed;
|
||||
} else if (ax < -maxspeed) {
|
||||
ax = -maxspeed;
|
||||
}
|
||||
|
||||
if (ay > maxspeed) {
|
||||
ay = maxspeed;
|
||||
} else if (ay < -maxspeed) {
|
||||
ay = -maxspeed;
|
||||
}
|
||||
|
||||
// X byte
|
||||
status[4].bit(0) = ax.bit(8);
|
||||
status[6] = ax.bit(4,7);
|
||||
status[7] = ax.bit(0,3);
|
||||
|
||||
// Y byte
|
||||
status[4].bit(1) = ay.bit(8);
|
||||
status[8] = ay.bit(4,7);
|
||||
status[9] = ay.bit(0,3);
|
||||
}
|
||||
|
||||
// Clock in is TR. The mouse reacts and ACKs by updating TL,
|
||||
// but not instantly.
|
||||
tr = data.bit(5);
|
||||
if (tr != tl) {
|
||||
timeout = t_handshake;
|
||||
}
|
||||
|
||||
latch = data.bit(7);
|
||||
th = data.bit(6);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
struct MegaMouse : Controller, Thread {
|
||||
Node::Input::Axis x;
|
||||
Node::Input::Axis y;
|
||||
Node::Input::Button left;
|
||||
Node::Input::Button right;
|
||||
Node::Input::Button middle;
|
||||
Node::Input::Button start;
|
||||
|
||||
MegaMouse(Node::Port);
|
||||
~MegaMouse();
|
||||
|
||||
auto main() -> void;
|
||||
auto readData() -> n8 override;
|
||||
auto writeData(n8 data) -> void override;
|
||||
|
||||
private:
|
||||
n1 th = 1;
|
||||
n1 tr = 1;
|
||||
n1 tl = 1;
|
||||
n1 latch;
|
||||
n8 index = 0;
|
||||
n4 status[10];
|
||||
s16 maxspeed = 255;
|
||||
n32 timeout = 0;
|
||||
|
||||
n32 t_data;
|
||||
n32 t_handshake;
|
||||
};
|
||||
@@ -13,7 +13,7 @@ auto ControllerPort::load(Node::Object parent) -> void {
|
||||
port->setAllocate([&](auto name) { return allocate(name); });
|
||||
port->setDisconnect([&] { return disconnect(); });
|
||||
|
||||
port->setSupported({"Control Pad", "Fighting Pad"});
|
||||
port->setSupported({"Control Pad", "Fighting Pad", "Mega Mouse"});
|
||||
}
|
||||
|
||||
auto ControllerPort::unload() -> void {
|
||||
@@ -24,6 +24,7 @@ auto ControllerPort::unload() -> void {
|
||||
auto ControllerPort::allocate(string name) -> Node::Peripheral {
|
||||
if(name == "Control Pad" ) device = new ControlPad(port);
|
||||
if(name == "Fighting Pad") device = new FightingPad(port);
|
||||
if(name == "Mega Mouse") device = new MegaMouse(port);
|
||||
if(device) return device->node;
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
auto M32X::readExternal(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
|
||||
if(!Mega32X()) return data;
|
||||
|
||||
if(!io.adapterEnable) {
|
||||
return rom[address >> 1];
|
||||
}
|
||||
if(!Mega32X() || !io.adapterEnable) return data;
|
||||
|
||||
if(address >= 0x000000 && address <= 0x0000ff) {
|
||||
if(address == 0x70) return io.vectorLevel4 >> 16;
|
||||
@@ -11,32 +7,29 @@ auto M32X::readExternal(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
|
||||
return vectors[address >> 1];
|
||||
}
|
||||
|
||||
if(address >= 0x000100 && address <= 0x3fffff) {
|
||||
if(dreq.vram) return rom[address >> 1];
|
||||
}
|
||||
|
||||
if(address >= 0x840000 && address <= 0x87ffff) {
|
||||
return vdp.bbram[address >> 1 & 0xffff];
|
||||
}
|
||||
|
||||
if(address >= 0x880000 && address <= 0x8fffff) {
|
||||
if(!dreq.vram) return rom[address >> 1 & 0x3ffff];
|
||||
if(!dreq.vram) {
|
||||
address &= 0x7ffff;
|
||||
return cartridge.child->read(upper, lower, address, data);
|
||||
}
|
||||
}
|
||||
|
||||
if(address >= 0x900000 && address <= 0x9fffff) {
|
||||
address = io.romBank * 0x100000 | address & 0x0fffff;
|
||||
if(!dreq.vram) return rom[address >> 1];
|
||||
if(!dreq.vram) {
|
||||
address = io.romBank * 0x100000 | address & 0x0fffff;
|
||||
return cartridge.child->read(upper, lower, address, data);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
auto M32X::writeExternal(n1 upper, n1 lower, n24 address, n16 data) -> void {
|
||||
if(!Mega32X()) return;
|
||||
|
||||
if(!io.adapterEnable) {
|
||||
return;
|
||||
}
|
||||
if(!Mega32X() || !io.adapterEnable) return;
|
||||
|
||||
if(address >= 0x000000 && address <= 0x0000ff) {
|
||||
if(address == 0x70 && upper) io.vectorLevel4.byte(3) = data.byte(1);
|
||||
@@ -64,4 +57,11 @@ auto M32X::writeExternal(n1 upper, n1 lower, n24 address, n16 data) -> void {
|
||||
if(lower && data.byte(0)) vdp.bbram[address >> 1 & 0xffff].byte(0) = data.byte(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if(address >= 0x900000 && address <= 0x9fffff) {
|
||||
if(!dreq.vram) {
|
||||
address = io.romBank * 0x100000 | address & 0x0fffff;
|
||||
return cartridge.child->write(upper, lower, address, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ auto M32X::readInternal(n1 upper, n1 lower, n32 address, n16 data) -> n16 {
|
||||
if(shs.active()) shs.step(1);
|
||||
}
|
||||
|
||||
return rom[address >> 1];
|
||||
return cartridge.child->read(upper, lower, address, data);
|
||||
}
|
||||
|
||||
if(address >= 0x0400'0000 && address <= 0x0403'ffff) {
|
||||
|
||||
@@ -11,14 +11,17 @@ auto M32X::readExternalIO(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
|
||||
if(address == 0xa15100) {
|
||||
data.bit( 0) = io.adapterEnable;
|
||||
data.bit( 1) = io.adapterReset;
|
||||
data.bit(2, 6) = 0;
|
||||
data.bit( 7) = io.resetEnable;
|
||||
data.bit(8, 14) = 0;
|
||||
data.bit(15) = vdp.framebufferAccess;
|
||||
}
|
||||
|
||||
//interrupt control
|
||||
if(address == 0xa15102) {
|
||||
data.bit(0) = shm.irq.cmd.active;
|
||||
data.bit(1) = shs.irq.cmd.active;
|
||||
data.bit(0) = shm.irq.cmd.active;
|
||||
data.bit(1) = shs.irq.cmd.active;
|
||||
data.bit(2, 15) = 0;
|
||||
}
|
||||
|
||||
//bank set
|
||||
@@ -153,7 +156,13 @@ auto M32X::writeExternalIO(n1 upper, n1 lower, n24 address, n16 data) -> void {
|
||||
if(address == 0xa15100) {
|
||||
if(lower) {
|
||||
io.adapterEnable = data.bit(0);
|
||||
io.adapterReset = data.bit(1);
|
||||
|
||||
if(data.bit(1) == 0) {
|
||||
shm.power(true);
|
||||
shs.power(true);
|
||||
}
|
||||
|
||||
io.adapterReset = data.bit(1);
|
||||
}
|
||||
if(upper) {
|
||||
vdp.framebufferAccess = data.bit(15);
|
||||
|
||||
@@ -15,7 +15,7 @@ auto M32X::readInternalIO(n1 upper, n1 lower, n29 address, n16 data) -> n16 {
|
||||
}
|
||||
data.bit( 7) = io.hintVblank;
|
||||
data.bit( 8) = !(bool)cartridge.node; //0 = cartridge connected
|
||||
data.bit( 9) = io.adapterEnable | 1;
|
||||
data.bit( 9) = io.adapterEnable;
|
||||
data.bit(15) = vdp.framebufferAccess;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ auto M32X::unload() -> void {
|
||||
shs.unload();
|
||||
vdp.unload();
|
||||
pwm.unload(node);
|
||||
rom.reset();
|
||||
vectors.reset();
|
||||
sdram.reset();
|
||||
node.reset();
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
struct M32X {
|
||||
Node::Object node;
|
||||
Memory::Readable<n16> rom;
|
||||
Memory::Readable<n16> vectors;
|
||||
Memory::Writable<n16> sdram;
|
||||
|
||||
|
||||
+12
-2
@@ -14,6 +14,8 @@ auto M32X::SH7604::unload() -> void {
|
||||
}
|
||||
|
||||
auto M32X::SH7604::main() -> void {
|
||||
if(!m32x.io.adapterReset) return step(cyclesUntilSync);
|
||||
|
||||
if(!SH2::inDelaySlot() && !regs.ID) {
|
||||
if(irq.vres.active && irq.vres.enable) {
|
||||
debugger.interrupt("VRES");
|
||||
@@ -48,12 +50,20 @@ auto M32X::SH7604::main() -> void {
|
||||
|
||||
auto M32X::SH7604::step(u32 clocks) -> void {
|
||||
Thread::step(clocks);
|
||||
Thread::synchronize(cpu);
|
||||
cyclesUntilSync -= clocks;
|
||||
|
||||
if(cyclesUntilSync <= 0) {
|
||||
if(m32x.shm.active()) Thread::synchronize(m32x.shs, cpu);
|
||||
if(m32x.shs.active()) Thread::synchronize(m32x.shm, cpu);
|
||||
while(cyclesUntilSync <= 0) {
|
||||
cyclesUntilSync += minCyclesBetweenSyncs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto M32X::SH7604::power(bool reset) -> void {
|
||||
Thread::create(23'000'000, {&M32X::SH7604::main, this});
|
||||
recompiler.min_cycles = 22;
|
||||
minCyclesBetweenSyncs = 20;
|
||||
SH2::power(reset);
|
||||
irq = {};
|
||||
irq.vres.enable = 1;
|
||||
|
||||
@@ -29,7 +29,7 @@ auto M32X::VDP::power(bool reset) -> void {
|
||||
}
|
||||
|
||||
auto M32X::VDP::scanline(u32 pixels[1280], u32 y) -> void {
|
||||
if(!Mega32X() || !pixels) return;
|
||||
if(!Mega32X() || !pixels || y >= (lines ? 240 : 224)) return;
|
||||
if(mode == 1) return scanlineMode1(pixels, y);
|
||||
if(mode == 2) return scanlineMode2(pixels, y);
|
||||
if(mode == 3) return scanlineMode3(pixels, y);
|
||||
|
||||
@@ -24,7 +24,7 @@ auto VDP::load(Node::Object parent) -> void {
|
||||
screen->colors(1 << 16, {&VDP::color, this});
|
||||
screen->setSize(1280, 480);
|
||||
screen->setScale(0.25, 0.5);
|
||||
screen->setAspect(1.0, 1.0);
|
||||
screen->setAspect(32, 35);
|
||||
|
||||
overscan = screen->append<Node::Setting::Boolean>("Overscan", true, [&](auto value) {
|
||||
if(value == 0) screen->setSize(1280, 448);
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ auto VDP::load(Node::Object parent) -> void {
|
||||
screen->colors(1 << 16, {&VDP::color, this});
|
||||
screen->setSize(1280, 480);
|
||||
screen->setScale(0.25, 0.5);
|
||||
screen->setAspect(1.0, 1.0);
|
||||
screen->setAspect(32, 35);
|
||||
|
||||
overscan = screen->append<Node::Setting::Boolean>("Overscan", true, [&](auto value) {
|
||||
if(value == 0) screen->setSize(1280, 448);
|
||||
|
||||
@@ -5,5 +5,10 @@ namespace ares::MasterSystem {
|
||||
#include "port.cpp"
|
||||
#include "gamepad/gamepad.cpp"
|
||||
#include "light-phaser/light-phaser.cpp"
|
||||
#include "paddle/paddle.cpp"
|
||||
#include "sports-pad/sports-pad.cpp"
|
||||
#include "md-control-pad/md-control-pad.cpp"
|
||||
#include "md-fighting-pad/md-fighting-pad.cpp"
|
||||
#include "mega-mouse/mega-mouse.cpp"
|
||||
|
||||
}
|
||||
@@ -20,9 +20,19 @@ struct Controller {
|
||||
virtual ~Controller() = default;
|
||||
|
||||
virtual auto read() -> n7 { return 0x7f; }
|
||||
virtual auto write(n8 data) -> void {}
|
||||
|
||||
// 0: trDirection
|
||||
// 1: thDirection
|
||||
// 2: trLevel
|
||||
// 3: thLevel
|
||||
virtual auto write(n4 data) -> void {}
|
||||
};
|
||||
|
||||
#include "port.hpp"
|
||||
#include "gamepad/gamepad.hpp"
|
||||
#include "light-phaser/light-phaser.hpp"
|
||||
#include "paddle/paddle.hpp"
|
||||
#include "sports-pad/sports-pad.hpp"
|
||||
#include "md-control-pad/md-control-pad.hpp"
|
||||
#include "md-fighting-pad/md-fighting-pad.hpp"
|
||||
#include "mega-mouse/mega-mouse.hpp"
|
||||
@@ -0,0 +1,61 @@
|
||||
MdControlPad::MdControlPad(Node::Port parent) {
|
||||
node = parent->append<Node::Peripheral>("MD Control Pad");
|
||||
|
||||
up = node->append<Node::Input::Button>("Up");
|
||||
down = node->append<Node::Input::Button>("Down");
|
||||
left = node->append<Node::Input::Button>("Left");
|
||||
right = node->append<Node::Input::Button>("Right");
|
||||
a = node->append<Node::Input::Button>("A");
|
||||
b = node->append<Node::Input::Button>("B");
|
||||
c = node->append<Node::Input::Button>("C");
|
||||
start = node->append<Node::Input::Button>("Start");
|
||||
}
|
||||
|
||||
auto MdControlPad::read() -> n7 {
|
||||
platform->input(up);
|
||||
platform->input(down);
|
||||
platform->input(left);
|
||||
platform->input(right);
|
||||
platform->input(a);
|
||||
platform->input(b);
|
||||
platform->input(c);
|
||||
platform->input(start);
|
||||
|
||||
if(!(up->value() & down->value())) {
|
||||
yHold = 0, upLatch = up->value(), downLatch = down->value();
|
||||
} else if(!yHold) {
|
||||
yHold = 1, swap(upLatch, downLatch);
|
||||
}
|
||||
|
||||
if(!(left->value() & right->value())) {
|
||||
xHold = 0, leftLatch = left->value(), rightLatch = right->value();
|
||||
} else if(!xHold) {
|
||||
xHold = 1, swap(leftLatch, rightLatch);
|
||||
}
|
||||
|
||||
n7 data;
|
||||
|
||||
if (th == 0) {
|
||||
data.bit(0) = upLatch;
|
||||
data.bit(1) = downLatch;
|
||||
data.bit(2,3) = ~0;
|
||||
data.bit(4) = a->value();
|
||||
data.bit(5) = start->value();
|
||||
} else {
|
||||
data.bit(0) = upLatch;
|
||||
data.bit(1) = downLatch;
|
||||
data.bit(2) = leftLatch;
|
||||
data.bit(3) = rightLatch;
|
||||
data.bit(4) = b->value();
|
||||
data.bit(5) = c->value();
|
||||
}
|
||||
|
||||
data = ~data;
|
||||
data.bit(6) = th;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
auto MdControlPad::write(n4 data) -> void {
|
||||
th = data.bit(3);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
struct MdControlPad : Controller {
|
||||
Node::Input::Button up;
|
||||
Node::Input::Button down;
|
||||
Node::Input::Button left;
|
||||
Node::Input::Button right;
|
||||
Node::Input::Button a;
|
||||
Node::Input::Button b;
|
||||
Node::Input::Button c;
|
||||
Node::Input::Button start;
|
||||
|
||||
MdControlPad(Node::Port);
|
||||
|
||||
auto read() -> n7 override;
|
||||
auto write(n4 data) -> void override;
|
||||
|
||||
private:
|
||||
n1 yHold;
|
||||
n1 upLatch;
|
||||
n1 downLatch;
|
||||
n1 xHold;
|
||||
n1 leftLatch;
|
||||
n1 rightLatch;
|
||||
n1 th;
|
||||
};
|
||||
@@ -0,0 +1,110 @@
|
||||
MdFightingPad::MdFightingPad(Node::Port parent) {
|
||||
node = parent->append<Node::Peripheral>("MD Fighting Pad");
|
||||
|
||||
up = node->append<Node::Input::Button>("Up");
|
||||
down = node->append<Node::Input::Button>("Down");
|
||||
left = node->append<Node::Input::Button>("Left");
|
||||
right = node->append<Node::Input::Button>("Right");
|
||||
a = node->append<Node::Input::Button>("A");
|
||||
b = node->append<Node::Input::Button>("B");
|
||||
c = node->append<Node::Input::Button>("C");
|
||||
x = node->append<Node::Input::Button>("X");
|
||||
y = node->append<Node::Input::Button>("Y");
|
||||
z = node->append<Node::Input::Button>("Z");
|
||||
mode = node->append<Node::Input::Button>("Mode");
|
||||
start = node->append<Node::Input::Button>("Start");
|
||||
|
||||
Thread::create(1'000'000, {&MdFightingPad::main, this});
|
||||
}
|
||||
|
||||
MdFightingPad::~MdFightingPad() {
|
||||
Thread::destroy();
|
||||
}
|
||||
|
||||
auto MdFightingPad::main() -> void {
|
||||
if(timeout) {
|
||||
timeout--;
|
||||
} else {
|
||||
counter = 0;
|
||||
}
|
||||
Thread::step(1);
|
||||
Thread::synchronize(cpu);
|
||||
}
|
||||
|
||||
auto MdFightingPad::read() -> n7 {
|
||||
platform->input(up);
|
||||
platform->input(down);
|
||||
platform->input(left);
|
||||
platform->input(right);
|
||||
platform->input(a);
|
||||
platform->input(b);
|
||||
platform->input(c);
|
||||
platform->input(x);
|
||||
platform->input(y);
|
||||
platform->input(z);
|
||||
platform->input(mode);
|
||||
platform->input(start);
|
||||
|
||||
if(!(up->value() & down->value())) {
|
||||
yHold = 0, upLatch = up->value(), downLatch = down->value();
|
||||
} else if(!yHold) {
|
||||
yHold = 1, swap(upLatch, downLatch);
|
||||
}
|
||||
|
||||
if(!(left->value() & right->value())) {
|
||||
xHold = 0, leftLatch = left->value(), rightLatch = right->value();
|
||||
} else if(!xHold) {
|
||||
xHold = 1, swap(leftLatch, rightLatch);
|
||||
}
|
||||
|
||||
n7 data;
|
||||
|
||||
if(select == 0) {
|
||||
if(counter == 0 || counter == 1 || counter == 4) {
|
||||
data.bit(0) = upLatch;
|
||||
data.bit(1) = downLatch;
|
||||
data.bit(2,3) = ~0;
|
||||
}
|
||||
|
||||
if(counter == 2) {
|
||||
data.bit(0,3) = ~0; //controller type detection
|
||||
}
|
||||
|
||||
if(counter == 3) {
|
||||
data.bit(0,3) = 0;
|
||||
}
|
||||
|
||||
data.bit(4) = a->value();
|
||||
data.bit(5) = start->value();
|
||||
} else {
|
||||
if(counter == 0 || counter == 1 || counter == 2 || counter == 4) {
|
||||
data.bit(0) = upLatch;
|
||||
data.bit(1) = downLatch;
|
||||
data.bit(2) = leftLatch;
|
||||
data.bit(3) = rightLatch;
|
||||
data.bit(4) = b->value();
|
||||
data.bit(5) = c->value();
|
||||
}
|
||||
|
||||
if(counter == 3) {
|
||||
data.bit(0) = z->value();
|
||||
data.bit(1) = y->value();
|
||||
data.bit(2) = x->value();
|
||||
data.bit(3) = mode->value();
|
||||
data.bit(4,5) = 0;
|
||||
}
|
||||
}
|
||||
data = ~data;
|
||||
data.bit(6) = select;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
auto MdFightingPad::write(n4 data) -> void {
|
||||
if(!select && data.bit(3)) { //0->1 transition
|
||||
if(++counter == 5) counter = 0;
|
||||
}
|
||||
|
||||
select = data.bit(3);
|
||||
timeout = 1600; //~1.6ms
|
||||
}
|
||||
Loaded 100 of 599 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user