mirror of
https://github.com/ares-emulator/ares.git
synced 2026-11-09 01:09:46 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39025af1fe | ||
|
|
5cdefb088d | ||
|
|
d0253599a6 | ||
|
|
1e83da0241 | ||
|
|
ed0a1c8852 | ||
|
|
dd9c728a12 | ||
|
|
7ec1e1430c | ||
|
|
16379aabff | ||
|
|
11f24b47c3 | ||
|
|
32f205975c | ||
|
|
21ca9cf76e | ||
|
|
d32b3ffe45 | ||
|
|
5864382c67 | ||
|
|
f392e4f428 | ||
|
|
d0075978f8 | ||
|
|
c1e5777dc1 | ||
|
|
ec6c9fc330 | ||
|
|
9bc95db1f0 | ||
|
|
6d80a8e940 | ||
|
|
2a0f9651b9 | ||
|
|
51a10b0bd0 | ||
|
|
7e2f490fd0 | ||
|
|
2063393f20 | ||
|
|
4c15ed753e | ||
|
|
704fc91b9c | ||
|
|
757d74bac0 | ||
|
|
ff36889d47 | ||
|
|
3d10cd34d6 | ||
|
|
acaa1cabb5 | ||
|
|
e8f38d15f7 | ||
|
|
527aaefef1 | ||
|
|
a938ef8aa8 | ||
|
|
91658b989a | ||
|
|
801eaa4088 | ||
|
|
d6b2ca53e2 | ||
|
|
d823641ccc | ||
|
|
dec675896d | ||
|
|
e2caf175d6 | ||
|
|
322c99ca75 | ||
|
|
b28bb3dbd7 | ||
|
|
3f4334e5ff | ||
|
|
99a40e43ef | ||
|
|
9bbe36d73c | ||
|
|
4bae7965f9 | ||
|
|
9e4b3795f1 | ||
|
|
a82913c1f9 | ||
|
|
b7db1c074b | ||
|
|
1478637209 | ||
|
|
432c4e3b2a | ||
|
|
18c84a83e4 | ||
|
|
c4bc588082 | ||
|
|
ed3083db8a | ||
|
|
6d5ef628e8 | ||
|
|
6e8f4f224a | ||
|
|
4bdeb3727f | ||
|
|
20a3aac83b | ||
|
|
2950d3fe44 | ||
|
|
b55c5010f3 | ||
|
|
f893afca89 | ||
|
|
0d131bf4d8 | ||
|
|
7e99155f8e | ||
|
|
c95dc1bad7 | ||
|
|
b85395fe7b | ||
|
|
91c46dbad4 | ||
|
|
6a253b17af | ||
|
|
4116855924 | ||
|
|
1146c0def3 | ||
|
|
e176d8b3ee | ||
|
|
9b5e339cc1 | ||
|
|
8f626e304a | ||
|
|
bbe9a9b0a4 | ||
|
|
c00e402eeb | ||
|
|
5e6afeaa5e | ||
|
|
a3222abe79 | ||
|
|
b0ee66d4ad | ||
|
|
6567d9dcf0 | ||
|
|
9a8e7f8b6e | ||
|
|
1c9d38895d | ||
|
|
cd11421d72 | ||
|
|
079295e2ca | ||
|
|
2a819403db | ||
|
|
df031a7d4c | ||
|
|
0dd3edebe0 | ||
|
|
f9d38f5daf | ||
|
|
d381bb4678 | ||
|
|
3392783359 | ||
|
|
67c6bfa4a5 | ||
|
|
8cea0b8329 | ||
|
|
4f7feeafd7 | ||
|
|
bcb60a6e1b | ||
|
|
af0713858c | ||
|
|
d3b11e4339 | ||
|
|
7faebad713 | ||
|
|
92c1c2ec44 | ||
|
|
bff2738163 | ||
|
|
8a4bf9d82c | ||
|
|
4537145d24 | ||
|
|
4544e1df69 | ||
|
|
30cc370e40 | ||
|
|
4a48753ea3 | ||
|
|
52df41a709 | ||
|
|
dc385dbe6e | ||
|
|
df8d5a6157 | ||
|
|
de1c12737a | ||
|
|
da9c82956a | ||
|
|
4dd837ae41 | ||
|
|
fa0f9a715f | ||
|
|
87ae65a094 | ||
|
|
0ceaf17be6 | ||
|
|
e7760bfc51 | ||
|
|
c27f369e31 | ||
|
|
020410ea0b | ||
|
|
6fbea44d3d | ||
|
|
d2e20f0d05 | ||
|
|
e589f8d9cf | ||
|
|
d201d6a547 | ||
|
|
7dc11a1c39 | ||
|
|
89e0001275 | ||
|
|
5b744c324d | ||
|
|
a47bbea7a2 | ||
|
|
414c205256 | ||
|
|
f1fb33af8e | ||
|
|
84e3663e80 | ||
|
|
77bfad91d8 | ||
|
|
cd8d84ed5b | ||
|
|
44b0c2c532 | ||
|
|
02dcc63431 | ||
|
|
864bc6e6d6 | ||
|
|
a572a97d10 | ||
|
|
939617aad9 | ||
|
|
ca6ada73ab | ||
|
|
d8e462035d | ||
|
|
e457e05c8b | ||
|
|
da3adb4daa | ||
|
|
3ebfb4433e | ||
|
|
0bea9d3a2e | ||
|
|
785edf60c8 | ||
|
|
62f5c36c17 | ||
|
|
ed35f2249b | ||
|
|
3fa22458f4 | ||
|
|
708247ab38 | ||
|
|
88a14fb5f2 | ||
|
|
5f55b6cf27 | ||
|
|
74e10c45f3 | ||
|
|
5984867fce | ||
|
|
dd01cf762b | ||
|
|
bfa1eeafde | ||
|
|
0661e9858c | ||
|
|
7b433f305f | ||
|
|
1c61fc95e6 | ||
|
|
401df969b0 | ||
|
|
5deec7ede5 | ||
|
|
6a7de62674 | ||
|
|
b7be6612b9 | ||
|
|
f976cdc3a8 | ||
|
|
e369049d0a | ||
|
|
72d8179bf0 | ||
|
|
8ed4af8b57 | ||
|
|
bcddb7a68f | ||
|
|
5c9f5419b0 | ||
|
|
4b3b78b77e | ||
|
|
07c320e55a | ||
|
|
506820bac7 | ||
|
|
46ddcac339 | ||
|
|
1349d45a51 | ||
|
|
ff11f77f92 | ||
|
|
f9c110cd49 | ||
|
|
379091bada | ||
|
|
ba475a7a59 | ||
|
|
06e1ac13ef | ||
|
|
675026ffdd | ||
|
|
f0449e36ff | ||
|
|
0f265028f2 | ||
|
|
0b2544a6cf | ||
|
|
99afcff4c7 | ||
|
|
a652e85069 | ||
|
|
51061e16af | ||
|
|
4e66bc0b32 | ||
|
|
0c6784294c | ||
|
|
2a6454a710 | ||
|
|
4f5b5da64f | ||
|
|
562bcd7b20 | ||
|
|
db5f7d7867 | ||
|
|
3e9ee7423d | ||
|
|
1215b37448 | ||
|
|
85488a7404 | ||
|
|
5eccee8457 | ||
|
|
671669b209 | ||
|
|
fe9a80b9eb | ||
|
|
4216a0c2c4 | ||
|
|
8ba3bd6348 | ||
|
|
546aed6852 | ||
|
|
b11ff923b9 | ||
|
|
4509471ce2 | ||
|
|
72cd3c5a65 | ||
|
|
929ad45380 | ||
|
|
bb67742864 | ||
|
|
b439a0d90a | ||
|
|
a96395a1c1 | ||
|
|
70675c0644 | ||
|
|
950d9a1967 | ||
|
|
2682e929d8 | ||
|
|
cd924569d2 | ||
|
|
0d27a86e01 | ||
|
|
38628f9c85 | ||
|
|
405ac8289c | ||
|
|
b29fb91777 | ||
|
|
9280622d29 | ||
|
|
714ecc1322 | ||
|
|
04525a7426 | ||
|
|
cc3f4543b1 | ||
|
|
2f65f315e1 | ||
|
|
de57072f83 | ||
|
|
9225847d40 | ||
|
|
6669e3e84b | ||
|
|
703e2ea49a | ||
|
|
40a6b8d36a | ||
|
|
d5381e47a2 | ||
|
|
7ea42e3201 | ||
|
|
c7c80d07ee | ||
|
|
5faa14b504 | ||
|
|
1647d3f4ef | ||
|
|
62eadd89e0 | ||
|
|
9fae1a48c2 | ||
|
|
166856f4dd | ||
|
|
48e4ee55c0 | ||
|
|
e8b85524d9 | ||
|
|
3854a1d1e8 | ||
|
|
090f8765c2 | ||
|
|
8ae96164ef | ||
|
|
5a647e77ad | ||
|
|
6c7bdf9c1a | ||
|
|
21a730b037 | ||
|
|
5d38bd3744 | ||
|
|
3d3838d8b4 | ||
|
|
d1ddbd504a | ||
|
|
c88f26230e | ||
|
|
ecab4fdca0 | ||
|
|
3581be3c44 | ||
|
|
2950cef605 | ||
|
|
aab3b447e7 | ||
|
|
ea665be25a | ||
|
|
e8f4554f24 | ||
|
|
0ba0e5f85e | ||
|
|
7dea8162db | ||
|
|
d4fd326630 | ||
|
|
cb19cdbf90 | ||
|
|
ec12ccc6ff | ||
|
|
59deec8b45 | ||
|
|
ef53e87b31 | ||
|
|
631085f1e6 | ||
|
|
543dec41b8 | ||
|
|
72ab5278b2 | ||
|
|
93937e8603 | ||
|
|
8476c880bc | ||
|
|
e39efe170b | ||
|
|
133044afa9 | ||
|
|
c78f06e208 | ||
|
|
f5dc3bbd48 | ||
|
|
ea688c7721 | ||
|
|
35c8f5f760 | ||
|
|
97aa37f040 | ||
|
|
8752695b4d | ||
|
|
cea1adcb44 | ||
|
|
91b6c7ac14 | ||
|
|
862bfec9e9 | ||
|
|
dad3db5498 | ||
|
|
f396a9cf60 | ||
|
|
beaab1791a | ||
|
|
cc6b26c8d7 | ||
|
|
a5319fdc07 | ||
|
|
c2c3c0f0f8 | ||
|
|
3c773a90eb | ||
|
|
8574a5879b | ||
|
|
3fd03e7a32 | ||
|
|
0130187f82 | ||
|
|
fde1f55f42 | ||
|
|
5337a4bfb8 | ||
|
|
6bf0d1ffcd | ||
|
|
aff3513931 | ||
|
|
9fb87dc0c9 | ||
|
|
a10b9ce0e9 | ||
|
|
ffcce5823c | ||
|
|
78f439dad1 | ||
|
|
251ea7c0de | ||
|
|
12b4530f39 | ||
|
|
c4a5fcea9a | ||
|
|
ef3d784ae4 | ||
|
|
14552167a2 | ||
|
|
8593f3e980 | ||
|
|
a617f2160d | ||
|
|
a18ad47482 | ||
|
|
e6c0c15114 | ||
|
|
2cfb4a98ec | ||
|
|
8a89b5a53a | ||
|
|
352f2202e2 | ||
|
|
7233efbc39 | ||
|
|
dbd779376a | ||
|
|
ecba464a58 | ||
|
|
fb711deb0d | ||
|
|
d901f4e336 | ||
|
|
631b0f6452 | ||
|
|
3de3f99ad1 | ||
|
|
d201c4827a | ||
|
|
2ccd7ba670 |
No files matched your search
@@ -13,7 +13,8 @@ Before submitting an issue, please use the search to make sure an issue doesn't
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**Rom Information**
|
||||
Specify the impacted rom filename and any associated hashes (sha256 preferred)
|
||||
Specify the impacted rom filename and any associated hashes (sha256 preferred);.
|
||||
The easiest way to do this is to open the game in ares, and go to "Tools -> Manifest" and copy/paste the contents.
|
||||
|
||||
```
|
||||
file: Some Game (REGION).ext
|
||||
|
||||
@@ -8,6 +8,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
program:
|
||||
- ares
|
||||
@@ -18,14 +19,14 @@ jobs:
|
||||
windres: windres
|
||||
shell: 'msys2 {0}'
|
||||
msystem: clang64
|
||||
install: mingw-w64-clang-x86_64-clang
|
||||
install: mingw-w64-clang-x86_64-clang mingw-w64-clang-x86_64-pkgconf mingw-w64-clang-x86_64-SDL2
|
||||
- name: windows-arm64
|
||||
os: windows-latest
|
||||
compiler: clang++ --target=aarch64-w64-windows-gnu --sysroot=/clangarm64 -resource-dir=/clangarm64/lib/clang/$(basename "$(clang++ -print-resource-dir)")
|
||||
windres: windres --target=aarch64-w64-windows-gnu
|
||||
shell: 'msys2 {0}'
|
||||
msystem: clang64
|
||||
install: mingw-w64-clang-x86_64-clang mingw-w64-clang-aarch64-clang
|
||||
install: mingw-w64-clang-x86_64-clang mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-pkgconf mingw-w64-clang-aarch64-SDL2
|
||||
- name: windows-msvc-x64
|
||||
os: windows-latest
|
||||
compiler: clang++
|
||||
@@ -77,7 +78,8 @@ jobs:
|
||||
sudo apt-get install libsdl2-dev libgtk-3-dev libao-dev libopenal-dev
|
||||
- name: "macOS: Import Certificate"
|
||||
if: runner.os == 'macOS' && github.event_name != 'pull_request'
|
||||
uses: devbotsxyz/import-signing-certificate@2ac4f44d28045073d23153256efbb4c4b2d8aa22 # Don't use rolling branch for security reasons
|
||||
continue-on-error: ${{ github.repository != 'ares-emulator/ares' }}
|
||||
uses: wpilibsuite/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 }}
|
||||
@@ -85,7 +87,7 @@ jobs:
|
||||
keychain-password: ${{ secrets.MACOS_KEYCHAIN_PASSWORD }}
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
- name: "macOS: recover cache"
|
||||
- name: "macOS: recover MoltenVK cache"
|
||||
if: runner.os == 'macOS'
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
@@ -93,11 +95,23 @@ jobs:
|
||||
with:
|
||||
path: thirdparty/MoltenVK/MoltenVK
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('thirdparty/MoltenVK/HEAD') }}
|
||||
- name: "macOS: recover SDL cache"
|
||||
if: runner.os == 'macOS'
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
cache-name: sdl
|
||||
with:
|
||||
path: thirdparty/SDL/SDL
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('thirdparty/SDL/HEAD') }}
|
||||
- name: Install macOS Dependencies
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
brew install make ninja
|
||||
brew install make ninja cmake
|
||||
pip install --no-input setuptools
|
||||
echo "MAKE=gmake" >> $GITHUB_ENV
|
||||
pushd thirdparty/SDL
|
||||
./build-SDL.sh
|
||||
popd
|
||||
pushd thirdparty/MoltenVK
|
||||
./build-moltenvk.sh
|
||||
popd
|
||||
@@ -111,7 +125,7 @@ jobs:
|
||||
run: ${MAKE:-make} -j4 -C desktop-ui build=optimized local=false compiler="${{ matrix.platform.compiler }}" windres="${{ matrix.platform.windres }}"
|
||||
- name: "macOS: Make universal app"
|
||||
if: runner.os == 'macOS'
|
||||
run: scripts/macos-make-universal.sh
|
||||
run: scripts/macos-make-universal.sh build=optimized local=false
|
||||
env:
|
||||
MAKEFLAGS: -j3
|
||||
MACOS_CERTIFICATE_NAME: ${{ secrets.MACOS_CERTIFICATE_NAME }}
|
||||
@@ -129,6 +143,7 @@ jobs:
|
||||
timestampUrl: 'http://timestamp.digicert.com'
|
||||
- name: "macOS: notarize"
|
||||
if: runner.os == 'macOS' && github.event_name != 'pull_request'
|
||||
continue-on-error: ${{ github.repository != 'ares-emulator/ares' }}
|
||||
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
|
||||
|
||||
+4
-1
@@ -1,9 +1,12 @@
|
||||
*.moc
|
||||
*.user
|
||||
.vs/
|
||||
.vscode/
|
||||
.idea/
|
||||
cmake-*/
|
||||
obj/
|
||||
out/
|
||||
obj-amd64/
|
||||
obj-arm64/
|
||||
out-amd64/
|
||||
out-arm64/
|
||||
out-arm64/
|
||||
@@ -436,3 +436,37 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
----------------------------------------------------------------------
|
||||
|
||||
----------------------------------------------------------------------
|
||||
ymfm
|
||||
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2021, Aaron Giles
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. 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.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
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 HOLDER 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.
|
||||
----------------------------------------------------------------------
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
[](https://github.com/higan-emu/ares/blob/master/LICENSE)
|
||||
|
||||
**ares** is a multi-system emulator that began development on October 14th, 2004.
|
||||
It is a descendent of [higan](https://github.com/higan-emu/higan) and [bsnes](https://github.com/bsnes-emu/bsnes/), and focuses on accuracy and preservation.
|
||||
It is a descendant of [higan](https://github.com/higan-emu/higan) and [bsnes](https://github.com/bsnes-emu/bsnes/), and focuses on accuracy and preservation.
|
||||
|
||||
Official Releases
|
||||
-----------------
|
||||
@@ -34,11 +34,12 @@ ares supports additional audio drivers besides the ALSA drivers included above.
|
||||
By default, GTK3 is used, but support for GTK2 is available. You will need to install the additional package `libgtk2.0-dev` as well as specifying the command line option `hiro=gtk2` at compile time.
|
||||
|
||||
###### SDL2 for input
|
||||
If you would like to use SDL for input, you will need to install the following the `libsdl2-dev` and `libsdl2-0.0` packages and perform a clean build of ares. You should then be able to select SDL for input in the Settings > Drivers menu.
|
||||
If you would like to use SDL for input (e.g. for using a controller), you will need to install the `libsdl2-dev` and `libsdl2-2.0-0` packages and perform a clean build of ares.
|
||||
You should then be able to select SDL for input in the Settings > Drivers menu.
|
||||
|
||||
##### Building with clang
|
||||
|
||||
clang++ is now the preferred compiler for ares. If it is detected, the build will default to building with clang. It is necessary to install both the `clang` and `lld` packages. If you would like to manually specify a compiler, you can use the following option: `compiler=[g++|clang++]`
|
||||
clang++ is now the preferred compiler for ares. If clang is detected on Windows/macOS/BSD, it will be selected by default. On Linux and other platforms, g++ remains the default if present. To build with clang, it is necessary to install both the `clang` and `lld` packages. If you would like to manually specify a compiler, you can use the following option: `compiler=[g++|clang++]`
|
||||
|
||||
--------------
|
||||
|
||||
@@ -47,7 +48,7 @@ clang++ is now the preferred compiler for ares. If it is detected, the build wil
|
||||
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
|
||||
pacman -S --needed base-devel mingw-w64-x86_64-toolchain mingw-w64-x86_64-SDL2
|
||||
```
|
||||
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.
|
||||
|
||||
@@ -55,7 +56,7 @@ from the MSYS2 MSYS terminal should setup everything you need to compile ares. N
|
||||
|
||||
clang is available through Visual Studio (or Build Tools for Visual Studio) through its installer and can be used to build ares. You will still need to supply GNU make in this instance. MSYS2 also offers a clang environment. You will want to make sure you select the clangw64 option during installation of MSYS2 which should provide and additional CLANG64 pre-configured environment. Install the clang toolchain package from the MSYS2 terminal:
|
||||
```
|
||||
pacman -S mingw-w64-clang-x86_64-toolchain
|
||||
pacman -S mingw-w64-clang-x86_64-toolchain mingw-w64-clang-x86_64-SDL2
|
||||
```
|
||||
Once complete, open a CLANG64 terminal window and proceed with building ares.
|
||||
|
||||
@@ -90,7 +91,7 @@ make clean
|
||||
#### Building specific cores
|
||||
If you would like to build a subset of cores, you can specify the `cores="core1 core2"` option. Currently available cores:
|
||||
```
|
||||
a26 fc sfc n64 sg ms md ps1 pce ng msx cv gb gba ws ngp spec
|
||||
a26 fc sfc n64 sg ms md ps1 pce ng msx cv myvision gb gba ws ngp spec
|
||||
```
|
||||
|
||||
Build Output
|
||||
@@ -148,4 +149,4 @@ High-level Components
|
||||
Contributing
|
||||
------------
|
||||
|
||||
Please join our discord to chat with other ares developers: https://discord.com/invite/gz2quhk2kv
|
||||
Please join our discord to chat with other ares developers: https://discord.com/invite/gz2quhk2kv
|
||||
@@ -13,10 +13,6 @@ 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
|
||||
|
||||
@@ -24,6 +24,7 @@ auto CPU::main() -> void {
|
||||
}
|
||||
|
||||
auto CPU::step(u32 clocks) -> void {
|
||||
if(io.rdyLine == 1) io.scanlineCycles += clocks;
|
||||
Thread::step(clocks);
|
||||
Thread::synchronize();
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ struct CPU : MOS6502, Thread {
|
||||
//protected:
|
||||
struct IO {
|
||||
n1 rdyLine = 1;
|
||||
n32 scanlineCycles = 0;
|
||||
} io;
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ auto CPU::Debugger::load(Node::Object parent) -> void {
|
||||
|
||||
auto CPU::Debugger::instruction() -> void {
|
||||
if(tracer.instruction->enabled() && tracer.instruction->address(cpu.PC)) {
|
||||
tracer.instruction->notify(cpu.disassembleInstruction(), cpu.disassembleContext());
|
||||
tracer.instruction->notify(cpu.disassembleInstruction(), {cpu.disassembleContext(), " [c:", cpu.io.scanlineCycles, " l:", tia.io.vcounter, " p:", tia.io.hcounter, "]"});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
inline auto CPU::readBus(n16 address) -> n8 {
|
||||
address &= 0x1fff;
|
||||
if(auto result = platform->cheat(address)) return *result;
|
||||
|
||||
n8 data = cartridge.read(address);
|
||||
|
||||
if(address.bit(12) == 0 && address.bit(7) == 0) {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
auto CPU::read(n16 address) -> n8 {
|
||||
step(1);
|
||||
|
||||
if(io.rdyLine == 0) {
|
||||
while (io.rdyLine == 0) {
|
||||
while(io.rdyLine == 0) {
|
||||
step(1);
|
||||
}
|
||||
|
||||
@@ -8,7 +10,6 @@ auto CPU::read(n16 address) -> n8 {
|
||||
return MDR;
|
||||
}
|
||||
|
||||
step(1);
|
||||
MDR = readBus(address);
|
||||
return MDR;
|
||||
}
|
||||
|
||||
+13
-4
@@ -7,12 +7,18 @@ auto RIOT::writeRam(n8 address, n8 data) -> void {
|
||||
}
|
||||
|
||||
auto RIOT::readIo(n8 address) -> n8 {
|
||||
switch(address) {
|
||||
switch(address.bit(0, 2)) {
|
||||
case 0x00: return readPortA();
|
||||
case 0x01: return port[0].direction;
|
||||
case 0x02: return readPortB();
|
||||
case 0x03: return port[1].direction;
|
||||
case 0x04: return timer.counter;
|
||||
case 0x04: timer.interruptFlag = address.bit(3); return timer.counter;
|
||||
case 0x05: case 0x07: {
|
||||
n8 output = 0;
|
||||
output.bit(6) = 0; //TODO: PA7 IRQ Flag
|
||||
output.bit(7) = timer.interruptFlag;
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
debug(unimplemented, "[RIOT] IO read ", hex(address));
|
||||
@@ -25,11 +31,14 @@ auto RIOT::writeIo(n8 address, n8 data) -> void {
|
||||
case 0x01: port[0].direction = data; return;
|
||||
case 0x02: writePortB(data); return;
|
||||
case 0x03: port[1].direction = data; return;
|
||||
case 0x14: case 0x15: case 0x16: case 0x17: {
|
||||
case 0x14: case 0x15: case 0x16: case 0x17:
|
||||
case 0x1c: case 0x1d: case 0x1e: case 0x1f: {
|
||||
const n16 intervals[] = {1, 8, 64, 1024};
|
||||
timer.counter = data;
|
||||
timer.interval = intervals[address - 0x14];
|
||||
timer.interval = intervals[address.bit(0, 1)];
|
||||
timer.reload = timer.interval;
|
||||
timer.interruptFlag = 0;
|
||||
timer.interruptEnable = address.bit(3);
|
||||
return; }
|
||||
}
|
||||
|
||||
|
||||
+5
-11
@@ -19,18 +19,12 @@ auto RIOT::unload() -> void {
|
||||
}
|
||||
|
||||
auto RIOT::main() -> void {
|
||||
decrementTimer();
|
||||
step(1);
|
||||
}
|
||||
|
||||
auto RIOT::decrementTimer() -> void {
|
||||
if(--timer.interval == 0) {
|
||||
if(--timer.counter == 0xff) {
|
||||
timer.reload = 1;
|
||||
}
|
||||
|
||||
timer.interval = timer.reload;
|
||||
if(--timer.counter == 0xff) {
|
||||
timer.interval = 1;
|
||||
timer.interruptFlag = 1;
|
||||
}
|
||||
|
||||
step(timer.interval);
|
||||
}
|
||||
|
||||
auto RIOT::step(u32 clocks) -> void {
|
||||
|
||||
@@ -30,6 +30,8 @@ struct RIOT : Thread {
|
||||
u8 counter;
|
||||
n16 interval;
|
||||
n16 reload;
|
||||
n1 interruptEnable;
|
||||
n1 interruptFlag;
|
||||
} timer;
|
||||
|
||||
struct Port {
|
||||
|
||||
@@ -6,6 +6,8 @@ auto RIOT::serialize(serializer& s) -> void {
|
||||
s(timer.counter);
|
||||
s(timer.interval);
|
||||
s(timer.reload);
|
||||
s(timer.interruptEnable);
|
||||
s(timer.interruptFlag);
|
||||
|
||||
for(auto n : range(2)) {
|
||||
s(port[n].data);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
static const string SerializerVersion = "v131";
|
||||
static const string SerializerVersion = "v132";
|
||||
|
||||
auto System::serialize(bool synchronize) -> serializer {
|
||||
if(synchronize) scheduler.enter(Scheduler::Mode::Synchronize);
|
||||
|
||||
+10
-15
@@ -33,9 +33,9 @@ auto TIA::write(n8 address, n8 data) -> void {
|
||||
case 0x0a: ctrlpf(data); return; // CTRLPF
|
||||
case 0x0b: writeQueue.add(address, data, 1); return; // REFP0
|
||||
case 0x0c: writeQueue.add(address, data, 1); return; // REFP1
|
||||
case 0x0d: writeQueue.add(address, data, 6); return; // PF0
|
||||
case 0x0e: writeQueue.add(address, data, 6); return; // PF1
|
||||
case 0x0f: writeQueue.add(address, data, 6); return; // PF2
|
||||
case 0x0d: writeQueue.add(address, data, 2); return; // PF0
|
||||
case 0x0e: writeQueue.add(address, data, 2); return; // PF1
|
||||
case 0x0f: writeQueue.add(address, data, 2); return; // PF2
|
||||
case 0x10: resp(0); return; // RESP0
|
||||
case 0x11: resp(1); return; // RESP1
|
||||
case 0x12: resm(0); return; // RESM0
|
||||
@@ -47,8 +47,8 @@ auto TIA::write(n8 address, n8 data) -> void {
|
||||
case 0x18: audio[1].frequency = data; return; // AUDF1
|
||||
case 0x19: audio[0].volume = data; return; // AUDV0
|
||||
case 0x1a: audio[1].volume = data; return; // AUDV1
|
||||
case 0x1b: writeQueue.add(address, data, 3); return; // GRP0
|
||||
case 0x1c: writeQueue.add(address, data, 3); return; // GRP1
|
||||
case 0x1b: writeQueue.add(address, data, 1); return; // GRP0
|
||||
case 0x1c: writeQueue.add(address, data, 1); return; // GRP1
|
||||
case 0x1d: writeQueue.add(address, data, 1); return; // ENAM0
|
||||
case 0x1e: writeQueue.add(address, data, 1); return; // ENAM1
|
||||
case 0x1f: writeQueue.add(address, data, 1); return; // ENAB1
|
||||
@@ -72,8 +72,9 @@ auto TIA::write(n8 address, n8 data) -> void {
|
||||
|
||||
|
||||
auto TIA::vsync(n1 state) -> void {
|
||||
if(io.vsync && !state) {
|
||||
if(!io.vsync && state) {
|
||||
frame();
|
||||
tia.io.vcounter = 0;
|
||||
}
|
||||
|
||||
io.vsync = state;
|
||||
@@ -124,23 +125,17 @@ auto TIA::grp(n1 index, n8 data) -> void {
|
||||
}
|
||||
|
||||
auto TIA::resp(n1 index) -> void {
|
||||
player[index].position = (io.hcounter < 68 ? 3 : io.hcounter - 68 + 8) % 160;
|
||||
player[index].position = (io.hcounter - 68 + 5) % 160;
|
||||
}
|
||||
|
||||
auto TIA::resm(n1 index) -> void {
|
||||
missile[index].position = (io.hcounter < 68 ? 2 : io.hcounter - 68 + 7) % 160;
|
||||
missile[index].position = (io.hcounter - 68 + 4) % 160;
|
||||
}
|
||||
|
||||
auto TIA::resmp(n1 index, n8 data) -> void {
|
||||
missile[index].reset = data.bit(1);
|
||||
if(missile[index].reset) {
|
||||
auto offset = 3;
|
||||
if(player[index].size == 5) offset = 6;
|
||||
if(player[index].size == 7) offset = 10;
|
||||
missile[index].position = player[index].position + offset;
|
||||
}
|
||||
}
|
||||
|
||||
auto TIA::resbl() -> void {
|
||||
ball.position = (io.hcounter < 68 ? 2 : io.hcounter - 68 + 7);
|
||||
ball.position = (io.hcounter - 68 + 4) % 160;
|
||||
}
|
||||
+60
-25
@@ -13,12 +13,12 @@ TIA tia;
|
||||
auto TIA::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Object>("TIA");
|
||||
|
||||
screen = node->append<Node::Video::Screen>("Screen", 160, vlines());
|
||||
screen = node->append<Node::Video::Screen>("Screen", 180, displayHeight());
|
||||
screen->colors(1 << 7, {&TIA::color, this});
|
||||
screen->setSize(160, displayHeight());
|
||||
screen->setScale(1.0, 1.0);
|
||||
screen->setAspect(12.0, 7.0);
|
||||
screen->setViewport(0, 0, 160, displayHeight());
|
||||
screen->setSize(180, displayHeight());
|
||||
screen->setScale(2.0, 1.0);
|
||||
Region::PAL() ? screen->setAspect(38.0, 45.0) : screen->setAspect(4.0, 5.0);
|
||||
screen->setViewport(0, 0, 180, displayHeight());
|
||||
|
||||
stream = node->append<Node::Audio::Stream>("Audio");
|
||||
stream->setChannels(1);
|
||||
@@ -35,19 +35,22 @@ auto TIA::unload() -> void {
|
||||
}
|
||||
|
||||
auto TIA::main() -> void {
|
||||
scanline();
|
||||
io.vcounter++;
|
||||
io.hmoveTriggered = 255;
|
||||
scanline();
|
||||
io.vcounter++;
|
||||
io.hmoveTriggered = 255;
|
||||
}
|
||||
|
||||
auto TIA::scanline() -> void {
|
||||
cpu.rdyLine(1);
|
||||
cpu.io.scanlineCycles = 0;
|
||||
|
||||
for(io.hcounter = 0; io.hcounter < 228; io.hcounter++) {
|
||||
auto x = io.hcounter - 68;
|
||||
if(x >= 0 && io.vcounter < vlines()) pixel(x);
|
||||
writeQueue.step();
|
||||
auto x = io.hcounter - 68;
|
||||
auto y = io.vcounter - voffset();
|
||||
if(x >= 0 && y > 0 && y < vlines()) pixel(x);
|
||||
runAudio();
|
||||
step();
|
||||
if(io.hcounter == 0) cpu.io.rdyLine = 1;
|
||||
}
|
||||
|
||||
// Prevent an infinite emulator hang when games miss vblank
|
||||
@@ -58,7 +61,9 @@ auto TIA::scanline() -> void {
|
||||
}
|
||||
|
||||
auto TIA::pixel(n8 x) -> void {
|
||||
auto output = screen->pixels().data() + (io.vcounter * 160);
|
||||
auto y = io.vcounter - voffset();
|
||||
auto output = screen->pixels().data() + (y * 180) + 10;
|
||||
if(y >= displayHeight()) return;
|
||||
|
||||
// Output only black during vblank, or for the first 8px of a scanline where hmove was triggered
|
||||
if (io.vblank || (io.hmoveTriggered == io.vcounter && x < 8)) {
|
||||
@@ -98,10 +103,13 @@ auto TIA::pixel(n8 x) -> void {
|
||||
}
|
||||
|
||||
auto TIA::runPlayfield(n8 x) -> n1 {
|
||||
auto pos = x >> 2;
|
||||
if((x % 4) == 0) {
|
||||
auto pos = x >> 2;
|
||||
playfield.pixel = (!playfield.mirror || pos < 20) ? playfield.graphics.bit(pos % 20)
|
||||
: playfield.graphics.bit(19 - (pos % 20));
|
||||
}
|
||||
|
||||
if(!playfield.mirror || pos < 20) return playfield.graphics.bit(pos % 20);
|
||||
return playfield.graphics.bit(19 - (pos % 20));
|
||||
return playfield.pixel;
|
||||
}
|
||||
|
||||
auto TIA::runPlayer(n8 x, n1 index) -> n1 {
|
||||
@@ -136,10 +144,45 @@ auto TIA::runPlayer(n8 x, n1 index) -> n1 {
|
||||
|
||||
auto TIA::runMissile(n8 x, n1 index) -> n1 {
|
||||
auto& missile = this->missile[index];
|
||||
if (!missile.enable || missile.reset) return 0;
|
||||
auto& player = this->player[index];
|
||||
auto position = missile.position;
|
||||
|
||||
if(missile.reset) {
|
||||
auto offset = 3;
|
||||
if(player.size == 5) offset = 6;
|
||||
if(player.size == 7) offset = 10;
|
||||
missile.position = player.position + offset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!missile.enable) return 0;
|
||||
|
||||
const int missileSizes[4] = {1, 2, 4, 8};
|
||||
return x >= missile.position && x < missile.position + missileSizes[missile.size];
|
||||
|
||||
// Handle player stretch capability
|
||||
auto width = missileSizes[missile.size];
|
||||
auto repeatWidth = width;
|
||||
if(player.size == 5) repeatWidth *= 2;
|
||||
if(player.size == 7) repeatWidth *= 4;
|
||||
|
||||
// Handle repeat capability
|
||||
auto repeat = 1;
|
||||
if(player.size == 1 || player.size == 2 || player.size == 4) repeat = 2;
|
||||
if(player.size == 3 || player.size == 6) repeat = 3;
|
||||
|
||||
auto spacing = 8;
|
||||
if(player.size == 2 || player.size == 6) spacing = 24;
|
||||
if(player.size == 4 ) spacing = 56;
|
||||
|
||||
for(int i = 0; i < repeat; i++) {
|
||||
if (x >= position && x < position + width) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
position = (position + (spacing + repeatWidth)) % 160;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto TIA::runBall(n8 x) -> n1 {
|
||||
@@ -150,14 +193,6 @@ auto TIA::runBall(n8 x) -> n1 {
|
||||
}
|
||||
|
||||
auto TIA::frame() -> void {
|
||||
auto height = io.vcounter;
|
||||
|
||||
if(height <= 0) {
|
||||
debug(unusual, "invalid screen height");
|
||||
height = vlines();
|
||||
}
|
||||
|
||||
screen->setViewport(0, 0, 160, height);
|
||||
screen->frame();
|
||||
scheduler.exit(Event::Frame);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ struct TIA : Thread {
|
||||
Node::Audio::Stream stream;
|
||||
|
||||
auto vlines() const -> u32 { return Region::NTSC() ? 262 : 312; }
|
||||
auto displayHeight() const -> u32 { return Region::NTSC() ? 192 : 228; }
|
||||
auto displayHeight() const -> u32 { return Region::PAL() ? 243 : 228; }
|
||||
auto voffset() const -> s32 { return Region::PAL() ? 40 : 20; }
|
||||
|
||||
//tia.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
@@ -79,6 +80,7 @@ struct TIA : Thread {
|
||||
|
||||
struct {
|
||||
n20 graphics;
|
||||
n1 pixel;
|
||||
n1 mirror;
|
||||
n1 scoreMode;
|
||||
n1 priority;
|
||||
|
||||
@@ -23,7 +23,7 @@ auto TIA::WriteQueue::step() -> void {
|
||||
|
||||
auto TIA::WriteQueue::QueueItem::commit() -> void {
|
||||
switch(address) {
|
||||
case 0x01: tia.io.vblank = data.bit(1); if(tia.io.vblank == 0) { tia.io.vcounter = 0; } return; // VBLANK
|
||||
case 0x01: tia.io.vblank = data.bit(1); return; // VBLANK
|
||||
case 0x0b: tia.player[0].reflect = data.bit(3); return; // REFP0
|
||||
case 0x0c: tia.player[1].reflect = data.bit(3); return; // REFP1
|
||||
case 0x0d: tia.playfield.graphics.bit(0, 3) = data.bit(4, 7); return; // PF0
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <ares/ares.hpp>
|
||||
#include <ares/debug/debug.cpp>
|
||||
#include <nall/gdb/server.cpp>
|
||||
#include <ares/node/node.cpp>
|
||||
#include <ares/resource/resource.cpp>
|
||||
|
||||
@@ -8,4 +9,13 @@ namespace ares {
|
||||
Platform* platform = nullptr;
|
||||
bool _runAhead = false;
|
||||
|
||||
const string Name = "ares";
|
||||
const string Version = "136";
|
||||
const string Copyright = "ares team, Near";
|
||||
const string License = "ISC";
|
||||
const string LicenseURI = "https://opensource.org/licenses/ISC";
|
||||
const string Website = "ares-emu.net";
|
||||
const string WebsiteURI = "https://ares-emu.net/";
|
||||
const u32 SerializerSignature = 0x31545342; //"BST1" (little-endian)
|
||||
|
||||
}
|
||||
+8
-10
@@ -40,16 +40,14 @@ using namespace nall;
|
||||
using namespace nall::primitives;
|
||||
|
||||
namespace ares {
|
||||
static const string Name = "ares";
|
||||
static const string Version = "133";
|
||||
static const string Copyright = "ares team, Near";
|
||||
static const string License = "ISC";
|
||||
static const string LicenseURI = "https://opensource.org/licenses/ISC";
|
||||
static const string Website = "ares-emu.net";
|
||||
static const string WebsiteURI = "https://ares-emu.net/";
|
||||
|
||||
//incremented only when serialization format changes
|
||||
static const u32 SerializerSignature = 0x31545342; //"BST1" (little-endian)
|
||||
extern const string Name;
|
||||
extern const string Version;
|
||||
extern const string Copyright;
|
||||
extern const string License;
|
||||
extern const string LicenseURI;
|
||||
extern const string Website;
|
||||
extern const string WebsiteURI;
|
||||
extern const u32 SerializerSignature;
|
||||
|
||||
namespace VFS {
|
||||
using Pak = shared_pointer<vfs::directory>;
|
||||
|
||||
@@ -1,18 +1,32 @@
|
||||
#include <ares/ares.hpp>
|
||||
|
||||
#if !defined(PLATFORM_MACOS)
|
||||
#define STATIC_ALLOCATION
|
||||
#endif
|
||||
|
||||
namespace ares::Memory {
|
||||
|
||||
#if defined(PLATFORM_MACOS)
|
||||
//stub for unsupported platforms
|
||||
FixedAllocator::FixedAllocator() {
|
||||
}
|
||||
#else
|
||||
alignas(4096) u8 fixedBuffer[128_MiB];
|
||||
constexpr u32 fixedBufferSize = 128_MiB;
|
||||
|
||||
#if defined(STATIC_ALLOCATION)
|
||||
u8 fixedBuffer[fixedBufferSize + 64_KiB];
|
||||
#endif
|
||||
|
||||
FixedAllocator::FixedAllocator() {
|
||||
_allocator.resize(sizeof(fixedBuffer), 0, fixedBuffer);
|
||||
u8* buffer = nullptr;
|
||||
|
||||
#if defined(STATIC_ALLOCATION)
|
||||
//align to 64 KiB (maximum page size of any supported OS)
|
||||
auto offset = -(uintptr)fixedBuffer % 64_KiB;
|
||||
//set protection to executable
|
||||
if(memory::protect(fixedBuffer + offset, fixedBufferSize, true)) {
|
||||
//use static allocation
|
||||
buffer = fixedBuffer + offset;
|
||||
}
|
||||
#endif
|
||||
|
||||
_allocator.resize(fixedBufferSize, bump_allocator::executable, buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
auto FixedAllocator::get() -> bump_allocator& {
|
||||
static FixedAllocator allocator;
|
||||
|
||||
@@ -71,9 +71,7 @@ struct Instruction : Tracer {
|
||||
if(!enabled()) return;
|
||||
|
||||
if(_omitted) {
|
||||
PlatformLog({
|
||||
"[Omitted: ", _omitted, "]\n"}
|
||||
);
|
||||
PlatformLog(shared(), {"[Omitted: ", _omitted, "]"});
|
||||
_omitted = 0;
|
||||
}
|
||||
|
||||
@@ -84,7 +82,7 @@ struct Instruction : Tracer {
|
||||
context, " ",
|
||||
extra
|
||||
};
|
||||
PlatformLog({output.strip(), "\n"});
|
||||
PlatformLog(shared(), {output.strip()});
|
||||
}
|
||||
|
||||
auto serialize(string& output, string depth) -> void override {
|
||||
|
||||
@@ -6,12 +6,7 @@ struct Notification : Tracer {
|
||||
|
||||
auto notify(const string& message = {}) -> void {
|
||||
if(!enabled()) return;
|
||||
|
||||
if(message) {
|
||||
PlatformLog({_component, " ", _name, ": ", message, "\n"});
|
||||
} else {
|
||||
PlatformLog({_component, " ", _name, "\n"});
|
||||
}
|
||||
PlatformLog(shared(), message);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
@@ -6,24 +6,40 @@ struct Tracer : Debugger {
|
||||
}
|
||||
|
||||
auto component() const -> string { return _component; }
|
||||
auto enabled() const -> bool { return _enabled; }
|
||||
auto enabled() const -> bool { return file() || terminal(); }
|
||||
auto prefix() const -> bool { return _prefix; }
|
||||
auto terminal() const -> bool { return _terminal; }
|
||||
auto file() const -> bool { return _file; }
|
||||
auto autoLineBreak() const -> bool { return _autoLineBreak; }
|
||||
|
||||
auto setToggle(function<void ()> toggle) -> void { _toggle = toggle; }
|
||||
auto setComponent(string component) -> void { _component = component; }
|
||||
auto setEnabled(bool enabled) -> void { _enabled = enabled; }
|
||||
auto setPrefix(bool prefix) -> void { _prefix = prefix; }
|
||||
auto setTerminal(bool terminal) -> void { _terminal = terminal; if(_toggle) _toggle(); }
|
||||
auto setFile(bool file) -> void { _file = file; if(_toggle) _toggle(); }
|
||||
auto setAutoLineBreak(bool autoLineBreak) -> void { _autoLineBreak = autoLineBreak; }
|
||||
|
||||
auto serialize(string& output, string depth) -> void override {
|
||||
Debugger::serialize(output, depth);
|
||||
output.append(depth, " component: ", _component, "\n");
|
||||
output.append(depth, " enabled: ", _enabled, "\n");
|
||||
output.append(depth, " prefix: ", _prefix, "\n");
|
||||
output.append(depth, " terminal: ", _terminal, "\n");
|
||||
output.append(depth, " file: ", _file, "\n");
|
||||
}
|
||||
|
||||
auto unserialize(Markup::Node node) -> void override {
|
||||
Debugger::unserialize(node);
|
||||
_component = node["component"].string();
|
||||
_enabled = node["enabled"].boolean();
|
||||
_prefix = node["prefix"].boolean();
|
||||
_terminal = node["terminal"].boolean();
|
||||
_file = node["file"].boolean();
|
||||
}
|
||||
|
||||
protected:
|
||||
function<void ()> _toggle;
|
||||
string _component;
|
||||
bool _enabled = false;
|
||||
bool _prefix = false;
|
||||
bool _terminal = false;
|
||||
bool _file = false;
|
||||
bool _autoLineBreak = true;
|
||||
};
|
||||
@@ -2,9 +2,17 @@ struct Rumble : Input {
|
||||
DeclareClass(Rumble, "input.rumble")
|
||||
using Input::Input;
|
||||
|
||||
auto enable() const -> bool { return _enable; }
|
||||
auto setEnable(bool enable) -> void { _enable = enable; }
|
||||
auto weakValue() const -> u16 { return _weak; }
|
||||
auto strongValue() const -> u16 { return _strong; }
|
||||
|
||||
auto setValues(u16 strong, u16 weak) -> void { _weak = weak; _strong = strong; }
|
||||
auto setWeak(u16 weak) -> void { _weak = weak; }
|
||||
auto setStrong(u16 strong) -> void { _strong = strong; }
|
||||
|
||||
// For systems with binary motors
|
||||
auto enable() const -> bool { return _weak > 0 || _strong > 0; }
|
||||
auto setEnable(bool enable) -> void { _weak = enable ? 65535 : 0; _strong = enable ? 65535 : 0; }
|
||||
protected:
|
||||
bool _enable = 0;
|
||||
u16 _weak = 0;
|
||||
u16 _strong = 0;
|
||||
};
|
||||
@@ -94,7 +94,7 @@ namespace ares::Core {
|
||||
// <ares/platform.hpp> forward declarations
|
||||
static auto PlatformAttach(Node::Object) -> void;
|
||||
static auto PlatformDetach(Node::Object) -> void;
|
||||
static auto PlatformLog(string_view) -> void;
|
||||
static auto PlatformLog(Node::Debugger::Tracer::Tracer tracer, string_view) -> void;
|
||||
|
||||
#include <ares/node/attribute.hpp>
|
||||
#include <ares/node/class.hpp>
|
||||
|
||||
@@ -76,6 +76,11 @@ auto Screen::setViewport(u32 x, u32 y, u32 width, u32 height) -> void {
|
||||
_viewportHeight = height;
|
||||
}
|
||||
|
||||
auto Screen::setOverscan(bool overscan) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_overscan = overscan;
|
||||
}
|
||||
|
||||
auto Screen::setSize(u32 width, u32 height) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_width = width;
|
||||
@@ -122,6 +127,11 @@ auto Screen::setColorBleed(bool colorBleed) -> void {
|
||||
_colorBleed = colorBleed;
|
||||
}
|
||||
|
||||
auto Screen::setColorBleedWidth(u32 width) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_colorBleedWidth = width;
|
||||
}
|
||||
|
||||
auto Screen::setInterframeBlending(bool interframeBlending) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_interframeBlending = interframeBlending;
|
||||
@@ -228,14 +238,18 @@ auto Screen::refresh() -> void {
|
||||
}
|
||||
}
|
||||
|
||||
if(_colorBleed) {
|
||||
if (_colorBleed) {
|
||||
n32 mask = 1 << 24 | 1 << 16 | 1 << 8 | 1 << 0;
|
||||
for(u32 y : range(height)) {
|
||||
for (u32 y : range(height)) {
|
||||
auto target = output + y * width;
|
||||
for(u32 x : range(width)) {
|
||||
auto a = target[x];
|
||||
auto b = target[x + (x != width - 1)];
|
||||
target[x] = (a + b - ((a ^ b) & mask)) >> 1;
|
||||
for (u32 x : range(0, width, _colorBleedWidth)) {
|
||||
for (u32 offset = 0; offset < _colorBleedWidth && (x + offset) < width; ++offset) {
|
||||
u32 next = x + _colorBleedWidth;
|
||||
if (next + offset >= width) next = x;
|
||||
auto a = target[x + offset];
|
||||
auto b = target[next + offset];
|
||||
target[x + offset] = (a + b - ((a ^ b) & mask)) >> 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ struct Screen : Video {
|
||||
auto scaleY() const -> f64 { return _scaleY; }
|
||||
auto aspectX() const -> f64 { return _aspectX; }
|
||||
auto aspectY() const -> f64 { return _aspectY; }
|
||||
auto overscan() const -> bool { return _overscan; }
|
||||
auto colors() const -> u32 { return _colors; }
|
||||
auto pixels(bool frame = 0) -> array_span<u32>;
|
||||
|
||||
@@ -37,6 +38,7 @@ struct Screen : Video {
|
||||
auto setSize(u32 width, u32 height) -> void;
|
||||
auto setScale(f64 scaleX, f64 scaleY) -> void;
|
||||
auto setAspect(f64 aspectX, f64 aspectY) -> void;
|
||||
auto setOverscan(bool overscan) -> void;
|
||||
|
||||
auto setSaturation(f64 saturation) -> void;
|
||||
auto setGamma(f64 gamma) -> void;
|
||||
@@ -44,6 +46,7 @@ struct Screen : Video {
|
||||
|
||||
auto setFillColor(u32 fillColor) -> void;
|
||||
auto setColorBleed(bool colorBleed) -> void;
|
||||
auto setColorBleedWidth(u32 width) -> void;
|
||||
auto setInterframeBlending(bool interframeBlending) -> void;
|
||||
auto setRotation(u32 rotation) -> void;
|
||||
|
||||
@@ -78,7 +81,9 @@ protected:
|
||||
f64 _luminance = 1.0;
|
||||
u32 _fillColor = 0;
|
||||
bool _colorBleed = false;
|
||||
bool _colorBleedWidth = 1;
|
||||
bool _interframeBlending = false;
|
||||
bool _overscan = true;
|
||||
u32 _rotation = 0; //counter-clockwise (90 = left, 270 = right)
|
||||
|
||||
function<n64 (n32)> _color;
|
||||
|
||||
@@ -15,11 +15,12 @@ struct Platform {
|
||||
virtual auto detach(Node::Object) -> void {}
|
||||
virtual auto pak(Node::Object) -> shared_pointer<vfs::directory> { return {}; }
|
||||
virtual auto event(Event) -> void {}
|
||||
virtual auto log(string_view message) -> void {}
|
||||
virtual auto log(Node::Debugger::Tracer::Tracer, string_view message) -> void {}
|
||||
virtual auto status(string_view message) -> void {}
|
||||
virtual auto video(Node::Video::Screen, const u32* data, u32 pitch, u32 width, u32 height) -> void {}
|
||||
virtual auto audio(Node::Audio::Stream) -> void {}
|
||||
virtual auto input(Node::Input::Input) -> void {}
|
||||
virtual auto cheat(u32 addr) -> maybe<u32> { return nothing; }
|
||||
};
|
||||
|
||||
extern Platform* platform;
|
||||
@@ -30,5 +31,5 @@ namespace ares::Core {
|
||||
// <ares/node/node.hpp> forward declarations
|
||||
auto PlatformAttach(Node::Object node) -> void { if(platform && node->name()) platform->attach(node); }
|
||||
auto PlatformDetach(Node::Object node) -> void { if(platform && node->name()) platform->detach(node); }
|
||||
auto PlatformLog(string_view text) -> void { if(platform) platform->log(text); }
|
||||
auto PlatformLog(Node::Debugger::Tracer::Tracer node, string_view text) -> void { if(platform) platform->log(node, text); }
|
||||
}
|
||||
@@ -41,7 +41,7 @@ private:
|
||||
vector<Thread*> _threads;
|
||||
bool _synchronize = false;
|
||||
|
||||
friend class Thread;
|
||||
friend struct Thread;
|
||||
};
|
||||
|
||||
extern Scheduler scheduler;
|
||||
@@ -46,5 +46,5 @@ protected:
|
||||
u64 _scalar = 0;
|
||||
u64 _clock = 0;
|
||||
|
||||
friend class Scheduler;
|
||||
friend struct Scheduler;
|
||||
};
|
||||
@@ -12,10 +12,13 @@ ares.objects += $(if $(findstring ym2612,$(ares.components)),ares-audio-ym2612)
|
||||
ares.objects += $(if $(findstring m24c,$(ares.components)),ares-eeprom-m24c)
|
||||
ares.objects += $(if $(findstring m93lcx6,$(ares.components)),ares-eeprom-m93lcx6)
|
||||
|
||||
ares.objects += $(if $(findstring i8255,$(ares.components)),ares-io-i8255)
|
||||
|
||||
ares.objects += $(if $(findstring arm7tdmi,$(ares.components)),ares-processor-arm7tdmi)
|
||||
ares.objects += $(if $(findstring gsu,$(ares.components)),ares-processor-gsu)
|
||||
ares.objects += $(if $(findstring hg51b,$(ares.components)),ares-processor-hg51b)
|
||||
ares.objects += $(if $(findstring huc6280,$(ares.components)),ares-processor-huc6280)
|
||||
ares.objects += $(if $(findstring i8080,$(ares.components)),ares-processor-i8080)
|
||||
ares.objects += $(if $(findstring m68hc05,$(ares.components)),ares-processor-m68hc05)
|
||||
ares.objects += $(if $(findstring m68000,$(ares.components)),ares-processor-m68000)
|
||||
ares.objects += $(if $(findstring mos6502,$(ares.components)),ares-processor-mos6502)
|
||||
@@ -30,6 +33,8 @@ ares.objects += $(if $(findstring v30mz,$(ares.components)),ares-processor-v30mz
|
||||
ares.objects += $(if $(findstring wdc65816,$(ares.components)),ares-processor-wdc65816)
|
||||
ares.objects += $(if $(findstring z80,$(ares.components)),ares-processor-z80)
|
||||
|
||||
ares.objects += $(if $(findstring sst39sf0x0,$(ares.components)),ares-flash-sst39sf0x0)
|
||||
|
||||
ares.objects += $(if $(findstring tms9918,$(ares.components)),ares-video-tms9918)
|
||||
ares.objects += $(if $(findstring v9938,$(ares.components)),ares-video-v9938)
|
||||
|
||||
@@ -39,16 +44,18 @@ $(object.path)/ares-audio-sn76489.o: $(ares.path)/component/audio/sn76489/s
|
||||
$(object.path)/ares-audio-t6w28.o: $(ares.path)/component/audio/t6w28/t6w28.cpp
|
||||
$(object.path)/ares-audio-ym2149.o: $(ares.path)/component/audio/ym2149/ym2149.cpp
|
||||
$(object.path)/ares-audio-ym2413.o: $(ares.path)/component/audio/ym2413/ym2413.cpp
|
||||
$(object.path)/ares-audio-ym2610.o: $(ares.path)/component/audio/ym2610/ym2610.cpp
|
||||
$(object.path)/ares-audio-ym2612.o: $(ares.path)/component/audio/ym2612/ym2612.cpp
|
||||
|
||||
$(object.path)/ares-eeprom-m24c.o: $(ares.path)/component/eeprom/m24c/m24c.cpp
|
||||
$(object.path)/ares-eeprom-m93lcx6.o: $(ares.path)/component/eeprom/m93lcx6/m93lcx6.cpp
|
||||
|
||||
$(object.path)/ares-io-i8255.o: $(ares.path)/component/io/i8255/i8255.cpp
|
||||
|
||||
$(object.path)/ares-processor-arm7tdmi.o: $(ares.path)/component/processor/arm7tdmi/arm7tdmi.cpp
|
||||
$(object.path)/ares-processor-gsu.o: $(ares.path)/component/processor/gsu/gsu.cpp
|
||||
$(object.path)/ares-processor-hg51b.o: $(ares.path)/component/processor/hg51b/hg51b.cpp
|
||||
$(object.path)/ares-processor-huc6280.o: $(ares.path)/component/processor/huc6280/huc6280.cpp
|
||||
$(object.path)/ares-processor-i8080.o: $(ares.path)/component/processor/i8080/i8080.cpp
|
||||
$(object.path)/ares-processor-m68hc05.o: $(ares.path)/component/processor/m68hc05/m68hc05.cpp
|
||||
$(object.path)/ares-processor-m68000.o: $(ares.path)/component/processor/m68000/m68000.cpp
|
||||
$(object.path)/ares-processor-mos6502.o: $(ares.path)/component/processor/mos6502/mos6502.cpp
|
||||
@@ -63,5 +70,7 @@ $(object.path)/ares-processor-v30mz.o: $(ares.path)/component/processor/v30mz
|
||||
$(object.path)/ares-processor-wdc65816.o: $(ares.path)/component/processor/wdc65816/wdc65816.cpp
|
||||
$(object.path)/ares-processor-z80.o: $(ares.path)/component/processor/z80/z80.cpp
|
||||
|
||||
$(object.path)/ares-flash-sst39sf0x0.o: $(ares.path)/component/flash/sst39sf0x0/sst39sf0x0.cpp
|
||||
|
||||
$(object.path)/ares-video-tms9918.o: $(ares.path)/component/video/tms9918/tms9918.cpp
|
||||
$(object.path)/ares-video-v9938.o: $(ares.path)/component/video/v9938/v9938.cpp
|
||||
@@ -1,106 +0,0 @@
|
||||
auto YM2610::read(n2 address) -> n8 {
|
||||
switch(address) {
|
||||
case 0: return fm.readStatus();
|
||||
case 1: return ssg.read();
|
||||
case 2: return pcmStatus();
|
||||
}
|
||||
|
||||
debug(unimplemented, "[YM2610] read ", hex(address));
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto YM2610::pcmStatus() -> n8 {
|
||||
n8 output;
|
||||
for(auto c : range(6)) output.bit(c) = pcmA.channels[c].ended;
|
||||
output.bit(7) = pcmB.ended;
|
||||
return output;
|
||||
}
|
||||
|
||||
auto YM2610::write(n2 address, n8 data) -> void {
|
||||
switch(address) {
|
||||
case 0: {
|
||||
registerAddress = 0x000 | data;
|
||||
if (registerAddress <= 0xf) ssg.select(registerAddress);
|
||||
else fm.writeAddress(registerAddress);
|
||||
return;
|
||||
}
|
||||
case 1: return writeLower(data);
|
||||
case 2: registerAddress = 0x100 | data; fm.writeAddress(registerAddress); return;
|
||||
case 3: return writeUpper(data);
|
||||
}
|
||||
}
|
||||
|
||||
auto YM2610::writeLower(n8 data) -> void {
|
||||
switch(registerAddress) {
|
||||
case range16(0x000, 0x00f):
|
||||
ssg.write(data);
|
||||
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 range3(0x016, 0x018):
|
||||
debug(unimplemented, "[YM2610::ADPCMB] Write ", hex(registerAddress), " = ", data);
|
||||
return;
|
||||
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:
|
||||
for(auto c : range(6)) {
|
||||
pcmA.channels[c].endedMask = data.bit(c);
|
||||
if(data.bit(c)) pcmA.channels[c].ended = 0;
|
||||
}
|
||||
pcmB.endedMask = data.bit(7);
|
||||
if(data.bit(7)) pcmB.ended = 0;
|
||||
return;
|
||||
default:
|
||||
if(registerAddress >= 0x01d && registerAddress <= 0x1ff) {
|
||||
fm.writeData(data);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
debug(unimplemented, "[YM2610] Write ", hex(registerAddress), " = ", data);
|
||||
}
|
||||
|
||||
auto YM2610::writeUpper(n8 data) -> void {
|
||||
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 range6(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 range6(0x110, 0x115): pcmA.channels[registerAddress - 0x110].startAddress.bit( 8, 15) = data; return;
|
||||
case range6(0x118, 0x11d): pcmA.channels[registerAddress - 0x118].startAddress.bit(16, 23) = data; return;
|
||||
case range6(0x120, 0x125): pcmA.channels[registerAddress - 0x120].endAddress.bit ( 8, 15) = data; return;
|
||||
case range6(0x128, 0x12d): pcmA.channels[registerAddress - 0x128].endAddress.bit (16, 23) = data; return;
|
||||
default:
|
||||
if(registerAddress >= 0x130 && registerAddress <= 0x1ff) {
|
||||
fm.writeAddress(registerAddress);
|
||||
fm.writeData(data);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
debug(unimplemented, "[YM2610] Write ", hex(registerAddress), " = ", data);
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
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;
|
||||
if(!channels[c].endedMask) channels[c].ended = 1;
|
||||
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 {
|
||||
if(ended) return;
|
||||
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;
|
||||
if(!endedMask) ended = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
if(ended) return;
|
||||
currentAddress = startAddress;
|
||||
currentNibble = 0;
|
||||
decodeAccumulator = 0;
|
||||
previousAccumulator = 0;
|
||||
decodePosition = 0;
|
||||
decodeStep = 127;
|
||||
playing = 1;
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
auto YM2610::serialize(serializer& s) -> void {
|
||||
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].endedMask);
|
||||
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(ended);
|
||||
s(endedMask);
|
||||
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);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
#include <ares/ares.hpp>
|
||||
#include "ym2610.hpp"
|
||||
|
||||
namespace ares {
|
||||
|
||||
#include "io.cpp"
|
||||
#include "pcm.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto YM2610::power() -> void {
|
||||
fm.power();
|
||||
ssg.power();
|
||||
pcmA.power();
|
||||
pcmB.power();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <component/audio/ym2612/ym2612.hpp>
|
||||
#include <component/audio/ym2149/ym2149.hpp>
|
||||
|
||||
namespace ares {
|
||||
|
||||
//Yamaha YM2610 (OPNB)
|
||||
|
||||
struct YM2610 {
|
||||
//ym2610.cpp
|
||||
auto power() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto read(n2 address) -> n8;
|
||||
auto write(n2 address, n8 data) -> void;
|
||||
auto writeLower(n8 data) -> void;
|
||||
auto writeUpper(n8 data) -> void;
|
||||
auto pcmStatus() -> n8;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
virtual auto readPCMA(u32 address) -> u8 { return 0; }
|
||||
virtual auto readPCMB(u32 address) -> u8 { return 0; }
|
||||
|
||||
protected:
|
||||
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 endedMask;
|
||||
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 ended;
|
||||
n1 endedMask;
|
||||
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,6 +1,6 @@
|
||||
auto YM2612::Channel::Operator::updateKeyState(bool state) -> void {
|
||||
if(keyOn == state) return; //no change
|
||||
keyOn = state;
|
||||
auto YM2612::Channel::Operator::updateKeyState() -> void {
|
||||
if(keyOn == keyLine) return; //no change
|
||||
keyOn = keyLine;
|
||||
|
||||
if(keyOn) {
|
||||
//restart phase and envelope generators
|
||||
@@ -27,7 +27,6 @@ auto YM2612::Channel::Operator::updateKeyState(bool state) -> void {
|
||||
}
|
||||
|
||||
auto YM2612::Channel::Operator::runEnvelope() -> void {
|
||||
updateKeyState(keyLine);
|
||||
if(ym2612.envelope.clock & (1 << envelope.divider) - 1) return;
|
||||
|
||||
u32 value = ym2612.envelope.clock >> envelope.divider;
|
||||
@@ -56,6 +55,7 @@ auto YM2612::Channel::Operator::runEnvelope() -> void {
|
||||
}
|
||||
|
||||
auto YM2612::Channel::Operator::runPhase() -> void {
|
||||
updateKeyState();
|
||||
phase.value += phase.delta; //advance wave position
|
||||
if(!(ssg.enable && envelope.value >= 0x200)) return; //SSG loop check
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ protected:
|
||||
Operator(Channel& channel) : channel(channel), ym2612(channel.ym2612) {}
|
||||
|
||||
//channel.cpp
|
||||
auto updateKeyState(bool) -> void;
|
||||
auto updateKeyState() -> void;
|
||||
|
||||
auto runEnvelope() -> void;
|
||||
auto runPhase() -> void;
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
#include <ares/ares.hpp>
|
||||
#include "sst39sf0x0.hpp"
|
||||
|
||||
namespace ares {
|
||||
|
||||
auto SST39SF0x0::reset() -> void {
|
||||
mode = Mode::Data;
|
||||
unlockStep = UnlockStep::Unlock0;
|
||||
}
|
||||
|
||||
auto SST39SF0x0::read(n32 address) -> n8 {
|
||||
if(mode == Mode::ID) {
|
||||
if(address == 0x00) return 0xBF; /* Manufacturer ID */
|
||||
if(address == 0x01) { /* Device ID */
|
||||
if(flash.size() <= 128 * 1024) return 0xB5;
|
||||
if(flash.size() <= 256 * 1024) return 0xB6;
|
||||
return 0xB7;
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
return flash.read(address);
|
||||
}
|
||||
|
||||
auto SST39SF0x0::write(n32 address, n8 data) -> void {
|
||||
if(mode == Mode::Program) {
|
||||
flash.write(address, data);
|
||||
mode = Mode::Data;
|
||||
} else if (mode == Mode::EraseBlock) {
|
||||
address &= ~0xFFF;
|
||||
for(u32 i = 0; i < 4096; i++) {
|
||||
flash.write(address + i, 0xFF);
|
||||
}
|
||||
mode = Mode::Data;
|
||||
} else if(mode == Mode::ID && data == 0xF0) {
|
||||
unlockStep = UnlockStep::Unlock0;
|
||||
mode = Mode::Data;
|
||||
} else {
|
||||
if(unlockStep == UnlockStep::Unlock0) {
|
||||
if((address & 0x7FFF) == 0x5555 && (data & 0xFF) == 0xAA) unlockStep = UnlockStep::Unlock1;
|
||||
} else if(unlockStep == UnlockStep::Unlock1) {
|
||||
unlockStep = UnlockStep::Unlock0;
|
||||
if((address & 0x7FFF) == 0x2AAA && (data & 0xFF) == 0x55) unlockStep = UnlockStep::Unlock2;
|
||||
} else {
|
||||
unlockStep = UnlockStep::Unlock0;
|
||||
if((address & 0x7FFF) == 0x5555) {
|
||||
switch(data) {
|
||||
case 0xA0:
|
||||
mode = Mode::Program;
|
||||
break;
|
||||
case 0x80:
|
||||
mode = Mode::Erase;
|
||||
break;
|
||||
case 0x10:
|
||||
if(mode != Mode::Erase) return;
|
||||
for(u32 i = 0; i < flash.size(); i++) {
|
||||
flash.write(i, 0xFF);
|
||||
}
|
||||
break;
|
||||
case 0x30:
|
||||
if(mode != Mode::Erase) return;
|
||||
mode = Mode::EraseBlock;
|
||||
break;
|
||||
case 0x90:
|
||||
mode = Mode::ID;
|
||||
break;
|
||||
case 0xF0:
|
||||
default:
|
||||
mode = Mode::Data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
//Generic flash
|
||||
|
||||
namespace ares {
|
||||
|
||||
struct SST39SF0x0 {
|
||||
auto reset() -> void;
|
||||
auto read(n32 address) -> n8;
|
||||
auto write(n32 address, n8 data) -> void;
|
||||
|
||||
Memory::Writable<n8> flash;
|
||||
|
||||
enum class Mode : u32 {
|
||||
Data,
|
||||
ID, /* 0x90 */
|
||||
Program, /* 0xA0 */
|
||||
Erase, /* 0x80 */
|
||||
EraseBlock /* 0x80 0x30 */
|
||||
} mode;
|
||||
|
||||
enum class UnlockStep : u32 {
|
||||
Unlock0, /* 0xF0 */
|
||||
Unlock1,
|
||||
Unlock2
|
||||
} unlockStep;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
#include <ares/ares.hpp>
|
||||
#include "i8255.hpp"
|
||||
|
||||
namespace ares {
|
||||
auto I8255::power() -> void {
|
||||
io.portCLowerInput = 1;
|
||||
io.portBInput = 1;
|
||||
io.groupBMode = 0;
|
||||
io.portCHigherInput = 1;
|
||||
io.portAInput = 1;
|
||||
io.groupAMode = 0;
|
||||
io.ioMode = 0;
|
||||
state.portA = 0xff;
|
||||
state.portB = 0xff;
|
||||
state.portC = 0xff;
|
||||
}
|
||||
|
||||
auto I8255::read(n2 port) -> n8 {
|
||||
switch(port) {
|
||||
case 0: return io.portAInput ? readPortA() : state.portA;
|
||||
case 1: return io.portBInput ? readPortB() : state.portB;
|
||||
case 2: {
|
||||
n8 result = readPortC();
|
||||
if(!io.portCLowerInput) result.bit(0,3) = state.portC.bit(0,3);
|
||||
if(!io.portCHigherInput) result.bit(4,7) = state.portC.bit(4,7);
|
||||
return result;
|
||||
}
|
||||
case 3: return 0xff;
|
||||
}
|
||||
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto I8255::write(n2 port, n8 data) -> void {
|
||||
switch(port) {
|
||||
case 0: state.portA = data; return writePortA(data);
|
||||
case 1: state.portB = data; return writePortB(data);
|
||||
case 2: state.portC = data; return writePortC(data);
|
||||
case 3: return writeControl(data);
|
||||
}
|
||||
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto I8255::writeControl(n8 data) -> void {
|
||||
io.portCLowerInput = data.bit(0);
|
||||
io.portBInput = data.bit(1);
|
||||
io.groupBMode = data.bit(2);
|
||||
io.portCHigherInput = data.bit(3);
|
||||
io.portAInput = data.bit(4);
|
||||
io.groupAMode = data.bit(5,6);
|
||||
io.ioMode = data.bit(7);
|
||||
|
||||
if(!io.ioMode) debug(unimplemented, "[I8255] BSR Mode");
|
||||
if(io.groupAMode != 0) debug(unimplemented, "[I8255] Group A Mode != 0");
|
||||
if(io.groupBMode != 0) debug(unimplemented, "[I8255] Group B Mode != 0");
|
||||
}
|
||||
|
||||
auto I8255::serialize(serializer& s) -> void {
|
||||
s(io.portCLowerInput);
|
||||
s(io.portBInput);
|
||||
s(io.groupBMode);
|
||||
s(io.portCHigherInput);
|
||||
s(io.portAInput);
|
||||
s(io.groupAMode);
|
||||
s(io.ioMode);
|
||||
|
||||
s(state.portA);
|
||||
s(state.portB);
|
||||
s(state.portC);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
namespace ares {
|
||||
struct I8255 {
|
||||
//i8255.cpp
|
||||
auto power() -> void;
|
||||
auto read(n2 port) -> n8;
|
||||
auto write(n2 port, n8 data) -> void;
|
||||
auto writeControl(n8 data) -> void;
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
virtual auto readPortA() -> n8 { return 0xff; };
|
||||
virtual auto readPortB() -> n8 { return 0xff; };
|
||||
virtual auto readPortC() -> n8 { return 0xff; };
|
||||
virtual auto writePortA(n8 data) -> void {};
|
||||
virtual auto writePortB(n8 data) -> void {};
|
||||
virtual auto writePortC(n8 data) -> void {};
|
||||
|
||||
struct IO {
|
||||
n1 portCLowerInput;
|
||||
n1 portBInput;
|
||||
n1 groupBMode;
|
||||
n1 portCHigherInput;
|
||||
n1 portAInput;
|
||||
n2 groupAMode;
|
||||
n1 ioMode;
|
||||
} io;
|
||||
|
||||
struct State {
|
||||
n8 portA;
|
||||
n8 portB;
|
||||
n8 portC;
|
||||
} state;
|
||||
};
|
||||
}
|
||||
@@ -62,7 +62,7 @@ struct ARM7TDMI {
|
||||
auto thumbInitialize() -> void;
|
||||
|
||||
//instructions-arm.cpp
|
||||
auto armALU(n4 mode, n4 target, n4 source, n32 data) -> void;
|
||||
auto armALU(n4 mode, n4 target, n32 source, n32 data) -> void;
|
||||
auto armMoveToStatus(n4 field, n1 source, n32 data) -> void;
|
||||
|
||||
auto armInstructionBranch(i24, n1) -> void;
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
auto ARM7TDMI::armALU(n4 mode, n4 d, n4 n, n32 rm) -> void {
|
||||
n32 rn = r(n);
|
||||
|
||||
auto ARM7TDMI::armALU(n4 mode, n4 d, n32 rn, n32 rm) -> void {
|
||||
switch(mode) {
|
||||
case 0: r(d) = BIT(rn & rm); break; //AND
|
||||
case 1: r(d) = BIT(rn ^ rm); break; //EOR
|
||||
@@ -64,14 +62,16 @@ auto ARM7TDMI::armInstructionBranchExchangeRegister
|
||||
|
||||
auto ARM7TDMI::armInstructionDataImmediate
|
||||
(n8 immediate, n4 shift, n4 d, n4 n, n1 save, n4 mode) -> void {
|
||||
n32 rn = r(n);
|
||||
n32 data = immediate;
|
||||
carry = cpsr().c;
|
||||
if(shift) data = ROR(data, shift << 1);
|
||||
armALU(mode, d, n, data);
|
||||
armALU(mode, d, rn, data);
|
||||
}
|
||||
|
||||
auto ARM7TDMI::armInstructionDataImmediateShift
|
||||
(n4 m, n2 type, n5 shift, n4 d, n4 n, n1 save, n4 mode) -> void {
|
||||
n32 rn = r(n);
|
||||
n32 rm = r(m);
|
||||
carry = cpsr().c;
|
||||
|
||||
@@ -82,12 +82,13 @@ auto ARM7TDMI::armInstructionDataImmediateShift
|
||||
case 3: rm = shift ? ROR(rm, shift) : RRX(rm); break;
|
||||
}
|
||||
|
||||
armALU(mode, d, n, rm);
|
||||
armALU(mode, d, rn, rm);
|
||||
}
|
||||
|
||||
auto ARM7TDMI::armInstructionDataRegisterShift
|
||||
(n4 m, n2 type, n4 s, n4 d, n4 n, n1 save, n4 mode) -> void {
|
||||
n8 rs = r(s) + (s == 15 ? 4 : 0);
|
||||
n32 rn = r(n) + (n == 15 ? 4 : 0);
|
||||
n32 rm = r(m) + (m == 15 ? 4 : 0);
|
||||
carry = cpsr().c;
|
||||
|
||||
@@ -98,7 +99,7 @@ auto ARM7TDMI::armInstructionDataRegisterShift
|
||||
case 3: if(rs) rm = ROR(rm, rs & 31 ? u32(rs & 31) : 32); break;
|
||||
}
|
||||
|
||||
armALU(mode, d, n, rm);
|
||||
armALU(mode, d, rn, rm);
|
||||
}
|
||||
|
||||
auto ARM7TDMI::armInstructionLoadImmediate
|
||||
@@ -167,7 +168,7 @@ auto ARM7TDMI::armInstructionMoveHalfRegister
|
||||
auto ARM7TDMI::armInstructionMoveImmediateOffset
|
||||
(n12 immediate, n4 d, n4 n, n1 mode, n1 writeback, n1 byte, n1 up, n1 pre) -> void {
|
||||
n32 rn = r(n);
|
||||
n32 rd = r(d);
|
||||
n32 rd = r(d) + (d == 15 ? 4 : 0);
|
||||
|
||||
if(pre == 1) rn = up ? rn + immediate : rn - immediate;
|
||||
if(mode == 1) rd = load((byte ? Byte : Word) | Nonsequential, rn);
|
||||
@@ -201,7 +202,7 @@ auto ARM7TDMI::armInstructionMoveMultiple
|
||||
for(u32 m : range(16)) {
|
||||
if(!list.bit(m)) continue;
|
||||
if(mode == 1) r(m) = read(Word | sequential, rn);
|
||||
if(mode == 0) write(Word | sequential, rn, r(m));
|
||||
if(mode == 0) write(Word | sequential, rn, r(m) + (m == 15 ? 4 : 0));
|
||||
rn += 4;
|
||||
sequential = Sequential;
|
||||
}
|
||||
|
||||
@@ -126,17 +126,23 @@ auto ARM7TDMI::thumbInstructionMoveMultiple
|
||||
(n8 list, n3 n, n1 mode) -> void {
|
||||
n32 rn = r(n);
|
||||
|
||||
u32 sequential = Nonsequential;
|
||||
for(u32 m : range(8)) {
|
||||
if(!list.bit(m)) continue;
|
||||
switch(mode) {
|
||||
case 0: write(Word | Nonsequential, rn, r(m)); break; //STMIA
|
||||
case 1: r(m) = read(Word | Nonsequential, rn); break; //LDMIA
|
||||
case 0: write(Word | sequential, rn, r(m)); break; //STMIA
|
||||
case 1: r(m) = read(Word | sequential, rn); break; //LDMIA
|
||||
}
|
||||
rn += 4;
|
||||
sequential = Sequential;
|
||||
}
|
||||
|
||||
if(mode == 0 || !list.bit(n)) r(n) = rn;
|
||||
if(mode == 1) idle();
|
||||
if(mode == 1) {
|
||||
idle();
|
||||
} else {
|
||||
pipeline.nonsequential = true;
|
||||
}
|
||||
}
|
||||
|
||||
auto ARM7TDMI::thumbInstructionMoveRegisterOffset
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
auto I8080::ADD(n8 x, n8 y, bool c) -> n8 {
|
||||
n9 z = x + y + c;
|
||||
|
||||
CF = z.bit(8);
|
||||
PF = parity(z);
|
||||
HF = n8(x ^ y ^ z).bit(4);
|
||||
ZF = n8(z) == 0;
|
||||
SF = z.bit(7);
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
auto I8080::AND(n8 x, n8 y) -> n8 {
|
||||
n8 z = x & y;
|
||||
|
||||
CF = 0;
|
||||
HF = n8(x | y).bit(3);
|
||||
PF = parity(z);
|
||||
ZF = z == 0;
|
||||
SF = z.bit(7);
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
auto I8080::CP(n8 x, n8 y) -> void {
|
||||
SUB(x, y, 0);
|
||||
}
|
||||
|
||||
auto I8080::DEC(n8 x) -> n8 {
|
||||
n8 z = x - 1;
|
||||
|
||||
PF = parity(z);
|
||||
HF = x.bit(0,3) != 0x00;
|
||||
ZF = z == 0;
|
||||
SF = z.bit(7);
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
auto I8080::INC(n8 x) -> n8 {
|
||||
n8 z = x + 1;
|
||||
|
||||
PF = parity(z);
|
||||
HF = x.bit(0,3) == 0x0f;
|
||||
ZF = z == 0;
|
||||
SF = z.bit(7);
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
auto I8080::OR(n8 x, n8 y) -> n8 {
|
||||
n8 z = x | y;
|
||||
|
||||
CF = 0;
|
||||
PF = parity(z);
|
||||
HF = 0;
|
||||
ZF = z == 0;
|
||||
SF = z.bit(7);
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
auto I8080::SUB(n8 x, n8 y, bool c) -> n8 {
|
||||
n9 z = x + ~y + !c; // internally implemented as negated ADD
|
||||
|
||||
CF = z.bit(8);
|
||||
PF = parity(z);
|
||||
HF = n8(x ^ ~y ^ z).bit(4);
|
||||
ZF = n8(z) == 0;
|
||||
SF = z.bit(7);
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
auto I8080::XOR(n8 x, n8 y) -> n8 {
|
||||
n8 z = x ^ y;
|
||||
|
||||
CF = 0;
|
||||
HF = 0;
|
||||
PF = parity(z);
|
||||
ZF = z == 0;
|
||||
SF = z.bit(7);
|
||||
|
||||
return z;
|
||||
}
|
||||
@@ -0,0 +1,410 @@
|
||||
no_optimize
|
||||
auto I8080::disassembleInstruction(maybe<n16> _pc) -> string {
|
||||
auto pc = _pc ? *_pc : PC;
|
||||
string s, output;
|
||||
|
||||
n8 prefix = 0x00;
|
||||
auto code = bus->read(pc++);
|
||||
if(code == 0xdd || code == 0xfd) {
|
||||
prefix = code;
|
||||
|
||||
code = bus->read(pc++);
|
||||
if(code == 0xdd || code == 0xfd) {
|
||||
if(prefix == 0xdd) {
|
||||
s.append("ix:");
|
||||
goto finish;
|
||||
}
|
||||
if(prefix == 0xfd) {
|
||||
s.append("iy:");
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output = disassemble(pc, prefix, code);
|
||||
finish:
|
||||
return pad(output, -18L);
|
||||
}
|
||||
|
||||
no_optimize
|
||||
auto I8080::disassembleContext() -> string {
|
||||
string s;
|
||||
|
||||
s.append("AF:", hex(af.word, 4L), " ");
|
||||
s.append("BC:", hex(bc.word, 4L), " ");
|
||||
s.append("DE:", hex(de.word, 4L), " ");
|
||||
s.append("HL:", hex(hl.word, 4L), " ");
|
||||
s.append("IX:", hex(ix.word, 4L), " ");
|
||||
s.append("IY:", hex(iy.word, 4L), " ");
|
||||
s.append("SP:", hex(SP, 4L), " ");
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
#define op(id, name, ...) case id: return {name, " ", vector<string>{__VA_ARGS__}.merge(",")};
|
||||
|
||||
#define N string{"$", hex(byte(), 2L)}
|
||||
#define IN string{"(", N, ")"}
|
||||
#define NN string{"$", hex(word(), 4L)}
|
||||
#define INN string{"(", NN, ")"}
|
||||
#define REL string{"$", hex(branch(), 4L)}
|
||||
|
||||
#define A "a"
|
||||
#define F "f"
|
||||
#define B "b"
|
||||
#define C "c"
|
||||
#define D "d"
|
||||
#define E "e"
|
||||
#define H prefix == 0xdd ? "ixh" : prefix == 0xfd ? "iyh" : "h"
|
||||
#define L prefix == 0xdd ? "ixl" : prefix == 0xfd ? "iyl" : "l"
|
||||
#define _H "h"
|
||||
#define _L "l"
|
||||
#define _HL "hl"
|
||||
|
||||
#define AF "af"
|
||||
#define BC "bc"
|
||||
#define DE "de"
|
||||
#define HL prefix == 0xdd ? "ix" : prefix == 0xfd ? "iy" : "hl"
|
||||
|
||||
#define AF_ "af'"
|
||||
#define BC_ "bc'"
|
||||
#define DE_ "de'"
|
||||
#define HL_ "hl'"
|
||||
|
||||
#define SP "sp"
|
||||
#define PC "pc"
|
||||
|
||||
#define I "i"
|
||||
#define R "r"
|
||||
|
||||
#define IC "(c)"
|
||||
#define IBC "(bc)"
|
||||
#define IDE "(de)"
|
||||
#define IHL "(hl)"
|
||||
#define ISP "(sp)"
|
||||
|
||||
no_optimize
|
||||
auto I8080::disassemble(n16 pc, n8 prefix, n8 code) -> string {
|
||||
auto byte = [&] {
|
||||
return bus->read(pc++);
|
||||
};
|
||||
|
||||
auto word = [&] {
|
||||
n16 data = byte() << 0;
|
||||
return data | byte() << 8;
|
||||
};
|
||||
|
||||
auto branch = [&] {
|
||||
auto d = byte();
|
||||
return pc + (i8)d;
|
||||
};
|
||||
|
||||
auto displace = [&] {
|
||||
if(!prefix) return string{};
|
||||
auto d = (i8)byte();
|
||||
return d >= 0 ? string{"+$", hex(d, 2L)} : string{"-$", hex(-d, 2L)};
|
||||
};
|
||||
|
||||
switch(code) {
|
||||
op(0x00, "nop ")
|
||||
op(0x01, "ld ", BC, NN)
|
||||
op(0x02, "ld ", IBC, A)
|
||||
op(0x03, "inc ", BC)
|
||||
op(0x04, "inc ", B)
|
||||
op(0x05, "dec ", B)
|
||||
op(0x06, "ld ", B, N)
|
||||
op(0x07, "rlca")
|
||||
op(0x08, "ex ", AF, AF_)
|
||||
op(0x09, "add ", HL, BC)
|
||||
op(0x0a, "ld ", A, IBC)
|
||||
op(0x0b, "dec ", BC)
|
||||
op(0x0c, "inc ", C)
|
||||
op(0x0d, "dec ", C)
|
||||
op(0x0e, "ld ", C, N)
|
||||
op(0x0f, "rrca")
|
||||
op(0x10, "djnz", REL)
|
||||
op(0x11, "ld ", DE, NN)
|
||||
op(0x12, "ld ", IDE, A)
|
||||
op(0x13, "inc ", DE)
|
||||
op(0x14, "inc ", D)
|
||||
op(0x15, "dec ", D)
|
||||
op(0x16, "ld ", D, N)
|
||||
op(0x17, "rla ")
|
||||
op(0x18, "jr ", REL)
|
||||
op(0x19, "add ", HL, DE)
|
||||
op(0x1a, "ld ", A, IDE)
|
||||
op(0x1b, "dec ", DE)
|
||||
op(0x1c, "inc ", E)
|
||||
op(0x1d, "dec ", E)
|
||||
op(0x1e, "ld ", E, N)
|
||||
op(0x1f, "rra ")
|
||||
op(0x20, "jr ", "nz", REL)
|
||||
op(0x21, "ld ", HL, NN)
|
||||
op(0x22, "ld ", INN, HL)
|
||||
op(0x23, "inc ", HL)
|
||||
op(0x24, "inc ", H)
|
||||
op(0x25, "dec ", H)
|
||||
op(0x26, "ld ", H, N)
|
||||
op(0x27, "daa ")
|
||||
op(0x28, "jr ", "z", REL)
|
||||
op(0x29, "add ", HL, HL)
|
||||
op(0x2a, "ld ", HL, INN)
|
||||
op(0x2b, "dec ", HL)
|
||||
op(0x2c, "inc ", L)
|
||||
op(0x2d, "dec ", L)
|
||||
op(0x2e, "ld ", L, N)
|
||||
op(0x2f, "cpl ")
|
||||
op(0x30, "jr ", "nc", REL)
|
||||
op(0x31, "ld ", SP, NN)
|
||||
op(0x32, "ld ", INN, A)
|
||||
op(0x33, "inc ", SP)
|
||||
op(0x34, "inc ", IHL)
|
||||
op(0x35, "dec ", IHL)
|
||||
op(0x36, "ld ", IHL, N)
|
||||
op(0x37, "scf ")
|
||||
op(0x38, "jr ", "c", REL)
|
||||
op(0x39, "add ", HL, SP)
|
||||
op(0x3a, "ld ", A, INN)
|
||||
op(0x3b, "dec ", SP)
|
||||
op(0x3c, "inc ", A)
|
||||
op(0x3d, "dec ", A)
|
||||
op(0x3e, "ld ", A, N)
|
||||
op(0x3f, "ccf ")
|
||||
op(0x40, "ld ", B, B)
|
||||
op(0x41, "ld ", B, C)
|
||||
op(0x42, "ld ", B, D)
|
||||
op(0x43, "ld ", B, E)
|
||||
op(0x44, "ld ", B, H)
|
||||
op(0x45, "ld ", B, L)
|
||||
op(0x46, "ld ", B, IHL)
|
||||
op(0x47, "ld ", B, A)
|
||||
op(0x48, "ld ", C, B)
|
||||
op(0x49, "ld ", C, C)
|
||||
op(0x4a, "ld ", C, D)
|
||||
op(0x4b, "ld ", C, E)
|
||||
op(0x4c, "ld ", C, H)
|
||||
op(0x4d, "ld ", C, L)
|
||||
op(0x4e, "ld ", C, IHL)
|
||||
op(0x4f, "ld ", C, A)
|
||||
op(0x50, "ld ", D, B)
|
||||
op(0x51, "ld ", D, C)
|
||||
op(0x52, "ld ", D, D)
|
||||
op(0x53, "ld ", D, E)
|
||||
op(0x54, "ld ", D, H)
|
||||
op(0x55, "ld ", D, L)
|
||||
op(0x56, "ld ", D, IHL)
|
||||
op(0x57, "ld ", D, A)
|
||||
op(0x58, "ld ", E, B)
|
||||
op(0x59, "ld ", E, C)
|
||||
op(0x5a, "ld ", E, D)
|
||||
op(0x5b, "ld ", E, E)
|
||||
op(0x5c, "ld ", E, H)
|
||||
op(0x5d, "ld ", E, L)
|
||||
op(0x5e, "ld ", E, IHL)
|
||||
op(0x5f, "ld ", E, A)
|
||||
op(0x60, "ld ", H, B)
|
||||
op(0x61, "ld ", H, C)
|
||||
op(0x62, "ld ", H, D)
|
||||
op(0x63, "ld ", H, E)
|
||||
op(0x64, "ld ", H, H)
|
||||
op(0x65, "ld ", H, L)
|
||||
op(0x66, "ld ", _H, IHL)
|
||||
op(0x67, "ld ", H, A)
|
||||
op(0x68, "ld ", L, B)
|
||||
op(0x69, "ld ", L, C)
|
||||
op(0x6a, "ld ", L, D)
|
||||
op(0x6b, "ld ", L, E)
|
||||
op(0x6c, "ld ", L, H)
|
||||
op(0x6d, "ld ", L, L)
|
||||
op(0x6e, "ld ", _L, IHL)
|
||||
op(0x6f, "ld ", L, A)
|
||||
op(0x70, "ld ", IHL, B)
|
||||
op(0x71, "ld ", IHL, C)
|
||||
op(0x72, "ld ", IHL, D)
|
||||
op(0x73, "ld ", IHL, E)
|
||||
op(0x74, "ld ", IHL, _H)
|
||||
op(0x75, "ld ", IHL, _L)
|
||||
op(0x76, "halt")
|
||||
op(0x77, "ld ", IHL, A)
|
||||
op(0x78, "ld ", A, B)
|
||||
op(0x79, "ld ", A, C)
|
||||
op(0x7a, "ld ", A, D)
|
||||
op(0x7b, "ld ", A, E)
|
||||
op(0x7c, "ld ", A, H)
|
||||
op(0x7d, "ld ", A, L)
|
||||
op(0x7e, "ld ", A, IHL)
|
||||
op(0x7f, "ld ", A, A)
|
||||
op(0x80, "add ", A, B)
|
||||
op(0x81, "add ", A, C)
|
||||
op(0x82, "add ", A, D)
|
||||
op(0x83, "add ", A, E)
|
||||
op(0x84, "add ", A, H)
|
||||
op(0x85, "add ", A, L)
|
||||
op(0x86, "add ", A, IHL)
|
||||
op(0x87, "add ", A, A)
|
||||
op(0x88, "adc ", A, B)
|
||||
op(0x89, "adc ", A, C)
|
||||
op(0x8a, "adc ", A, D)
|
||||
op(0x8b, "adc ", A, E)
|
||||
op(0x8c, "adc ", A, H)
|
||||
op(0x8d, "adc ", A, L)
|
||||
op(0x8e, "adc ", A, IHL)
|
||||
op(0x8f, "adc ", A, A)
|
||||
op(0x90, "sub ", A, B)
|
||||
op(0x91, "sub ", A, C)
|
||||
op(0x92, "sub ", A, D)
|
||||
op(0x93, "sub ", A, E)
|
||||
op(0x94, "sub ", A, H)
|
||||
op(0x95, "sub ", A, L)
|
||||
op(0x96, "sub ", A, IHL)
|
||||
op(0x97, "sub ", A, A)
|
||||
op(0x98, "sbc ", A, B)
|
||||
op(0x99, "sbc ", A, C)
|
||||
op(0x9a, "sbc ", A, D)
|
||||
op(0x9b, "sbc ", A, E)
|
||||
op(0x9c, "sbc ", A, H)
|
||||
op(0x9d, "sbc ", A, L)
|
||||
op(0x9e, "sbc ", A, IHL)
|
||||
op(0x9f, "sbc ", A, A)
|
||||
op(0xa0, "and ", A, B)
|
||||
op(0xa1, "and ", A, C)
|
||||
op(0xa2, "and ", A, D)
|
||||
op(0xa3, "and ", A, E)
|
||||
op(0xa4, "and ", A, H)
|
||||
op(0xa5, "and ", A, L)
|
||||
op(0xa6, "and ", A, IHL)
|
||||
op(0xa7, "and ", A, A)
|
||||
op(0xa8, "xor ", A, B)
|
||||
op(0xa9, "xor ", A, C)
|
||||
op(0xaa, "xor ", A, D)
|
||||
op(0xab, "xor ", A, E)
|
||||
op(0xac, "xor ", A, H)
|
||||
op(0xad, "xor ", A, L)
|
||||
op(0xae, "xor ", A, IHL)
|
||||
op(0xaf, "xor ", A, A)
|
||||
op(0xb0, "or ", A, B)
|
||||
op(0xb1, "or ", A, C)
|
||||
op(0xb2, "or ", A, D)
|
||||
op(0xb3, "or ", A, E)
|
||||
op(0xb4, "or ", A, H)
|
||||
op(0xb5, "or ", A, L)
|
||||
op(0xb6, "or ", A, IHL)
|
||||
op(0xb7, "or ", A, A)
|
||||
op(0xb8, "cp ", A, B)
|
||||
op(0xb9, "cp ", A, C)
|
||||
op(0xba, "cp ", A, D)
|
||||
op(0xbb, "cp ", A, E)
|
||||
op(0xbc, "cp ", A, H)
|
||||
op(0xbd, "cp ", A, L)
|
||||
op(0xbe, "cp ", A, IHL)
|
||||
op(0xbf, "cp ", A, A)
|
||||
op(0xc0, "ret ", "nz")
|
||||
op(0xc1, "pop ", BC)
|
||||
op(0xc2, "jp ", "nz", NN)
|
||||
op(0xc3, "jp ", NN)
|
||||
op(0xc4, "call", "nz", NN)
|
||||
op(0xc5, "push", BC)
|
||||
op(0xc6, "add ", A, N)
|
||||
op(0xc7, "rst ", "$0000")
|
||||
op(0xc8, "ret ", "z")
|
||||
op(0xc9, "ret ")
|
||||
op(0xca, "jp ", "z", NN)
|
||||
op(0xcb, "cb: ")
|
||||
op(0xcc, "call", "z", NN)
|
||||
op(0xcd, "call", NN)
|
||||
op(0xce, "adc ", A, N)
|
||||
op(0xcf, "rst ", "$0008")
|
||||
op(0xd0, "ret ", "nc")
|
||||
op(0xd1, "pop ", DE)
|
||||
op(0xd2, "jp ", "nc", NN)
|
||||
op(0xd3, "out ", IN, A)
|
||||
op(0xd4, "call", "nc", NN)
|
||||
op(0xd5, "push", DE)
|
||||
op(0xd6, "sub ", A, N)
|
||||
op(0xd7, "rst ", "$0010")
|
||||
op(0xd8, "ret ", "c")
|
||||
op(0xd9, "exx ")
|
||||
op(0xda, "jp ", "c", NN)
|
||||
op(0xdb, "in ", A, IN)
|
||||
op(0xdc, "call", "c", NN)
|
||||
op(0xdd, "ix: ")
|
||||
op(0xde, "sbc ", A, N)
|
||||
op(0xdf, "rst ", "$0018")
|
||||
op(0xe0, "ret ", "po")
|
||||
op(0xe1, "pop ", HL)
|
||||
op(0xe2, "jp ", "po", NN)
|
||||
op(0xe3, "ex ", ISP, HL)
|
||||
op(0xe4, "call", "po", NN)
|
||||
op(0xe5, "push", HL)
|
||||
op(0xe6, "and ", A, N)
|
||||
op(0xe7, "rst ", "$0020")
|
||||
op(0xe8, "ret ", "pe")
|
||||
op(0xe9, "jp ", HL) //officially jp (hl); but as read is not indirect, use jp hl
|
||||
op(0xea, "jp ", "pe", NN)
|
||||
op(0xeb, "ex ", DE, _HL)
|
||||
op(0xec, "call", "pe", NN)
|
||||
op(0xed, "ed: ")
|
||||
op(0xee, "xor ", A, N)
|
||||
op(0xef, "rst ", "$0028")
|
||||
op(0xf0, "ret ", "p")
|
||||
op(0xf1, "pop ", AF)
|
||||
op(0xf2, "jp ", "p", NN)
|
||||
op(0xf3, "di ")
|
||||
op(0xf4, "call", "p", NN)
|
||||
op(0xf5, "push", AF)
|
||||
op(0xf6, "or ", A, N)
|
||||
op(0xf7, "rst ", "$0030")
|
||||
op(0xf8, "ret ", "m")
|
||||
op(0xf9, "ld ", SP, HL)
|
||||
op(0xfa, "jp ", "m", NN)
|
||||
op(0xfb, "ei ")
|
||||
op(0xfc, "call", "m", NN)
|
||||
op(0xfd, "iy: ")
|
||||
op(0xfe, "cp ", A, N)
|
||||
op(0xff, "rst ", "$0038")
|
||||
}
|
||||
|
||||
unreachable;
|
||||
}
|
||||
|
||||
#undef op
|
||||
|
||||
#undef N
|
||||
#undef IN
|
||||
#undef NN
|
||||
#undef INN
|
||||
#undef REL
|
||||
|
||||
#undef A
|
||||
#undef F
|
||||
#undef B
|
||||
#undef C
|
||||
#undef D
|
||||
#undef E
|
||||
#undef H
|
||||
#undef L
|
||||
#undef _H
|
||||
#undef _L
|
||||
#undef _HL
|
||||
|
||||
#undef AF
|
||||
#undef BC
|
||||
#undef DE
|
||||
#undef HL
|
||||
|
||||
#undef AF_
|
||||
#undef BC_
|
||||
#undef DE_
|
||||
#undef HL_
|
||||
|
||||
#undef SP
|
||||
#undef PC
|
||||
|
||||
#undef I
|
||||
#undef R
|
||||
|
||||
#undef IC
|
||||
#undef IBC
|
||||
#undef IDE
|
||||
#undef IHL
|
||||
#undef ISP
|
||||
@@ -0,0 +1,81 @@
|
||||
#include <ares/ares.hpp>
|
||||
#include "i8080.hpp"
|
||||
|
||||
namespace ares {
|
||||
|
||||
#include "disassembler.cpp"
|
||||
#include "registers.hpp"
|
||||
#include "memory.cpp"
|
||||
#include "instruction.cpp"
|
||||
#include "algorithms.cpp"
|
||||
#include "instructions.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto I8080::power() -> void {
|
||||
prefix = Prefix::hl;
|
||||
af.word = 0xffd7; af_.word = 0x0000;
|
||||
bc.word = 0x0000; bc_.word = 0x0000;
|
||||
de.word = 0x0000; de_.word = 0x0000;
|
||||
hl.word = 0x0000; hl_.word = 0x0000;
|
||||
ix.word = 0x0000;
|
||||
iy.word = 0x0000;
|
||||
ir.word = 0x0000;
|
||||
wz.word = 0x0000;
|
||||
SP = 0xffff;
|
||||
PC = 0x0000;
|
||||
EI = 0;
|
||||
P = 0;
|
||||
Q = 0;
|
||||
HALT = 0;
|
||||
INTE = 0;
|
||||
}
|
||||
|
||||
auto I8080::reset() -> void {
|
||||
prefix = Prefix::hl;
|
||||
ir.word = 0x0000;
|
||||
WZ = PC = 0x0000;
|
||||
EI = 0;
|
||||
HALT = 0;
|
||||
INTE = 0;
|
||||
}
|
||||
|
||||
auto I8080::irq(n8 extbus) -> bool {
|
||||
//do not execute maskable interrupts if disabled or immediately after EI instruction
|
||||
if(!INTE || EI) return false;
|
||||
|
||||
WZ = extbus;
|
||||
wait(6);
|
||||
instruction(WZ);
|
||||
|
||||
INTE = 0;
|
||||
HALT = 0;
|
||||
if(P) PF = 0;
|
||||
P = 0;
|
||||
Q = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto I8080::nmi() -> bool {
|
||||
push(PC);
|
||||
WZ = 0x66;
|
||||
wait(5);
|
||||
PC = WZ;
|
||||
|
||||
INTE = 0;
|
||||
HALT = 0;
|
||||
if(P) PF = 0;
|
||||
P = 0;
|
||||
Q = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto I8080::parity(n8 value) const -> bool {
|
||||
value ^= value >> 4;
|
||||
value ^= value >> 2;
|
||||
value ^= value >> 1;
|
||||
return !(value & 1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
#pragma once
|
||||
|
||||
//Intel 8080
|
||||
|
||||
namespace ares {
|
||||
|
||||
struct I8080 {
|
||||
struct Bus {
|
||||
virtual auto read(n16 address) -> n8 = 0;
|
||||
virtual auto write(n16 address, n8 data) -> void = 0;
|
||||
|
||||
virtual auto in(n16 address) -> n8 = 0;
|
||||
virtual auto out(n16 address, n8 data) -> void = 0;
|
||||
};
|
||||
|
||||
virtual auto step(u32 clocks) -> void = 0;
|
||||
virtual auto synchronizing() const -> bool = 0;
|
||||
|
||||
//i8080.cpp
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
|
||||
auto irq(n8 extbus = 0xff) -> bool;
|
||||
auto nmi() -> bool;
|
||||
auto parity(n8) const -> bool;
|
||||
|
||||
//memory.cpp
|
||||
auto wait(u32 clocks = 1) -> void;
|
||||
auto opcode() -> n8;
|
||||
auto operand() -> n8;
|
||||
auto operands() -> n16;
|
||||
auto push(n16) -> void;
|
||||
auto pop() -> n16;
|
||||
auto read(n16 address) -> n8;
|
||||
auto write(n16 address, n8 data) -> void;
|
||||
auto in(n16 address) -> n8;
|
||||
auto out(n16 address, n8 data) -> void;
|
||||
|
||||
//instruction.cpp
|
||||
auto instruction() -> void;
|
||||
auto instruction(n8 code) -> void;
|
||||
|
||||
//algorithms.cpp
|
||||
auto ADD(n8, n8, bool = false) -> n8;
|
||||
auto AND(n8, n8) -> n8;
|
||||
auto CP (n8, n8) -> void;
|
||||
auto DEC(n8) -> n8;
|
||||
auto INC(n8) -> n8;
|
||||
auto OR (n8, n8) -> n8;
|
||||
auto SUB(n8, n8, bool = false) -> n8;
|
||||
auto XOR(n8, n8) -> n8;
|
||||
|
||||
//instructions.cpp
|
||||
auto instructionADC_a_irr(n16&) -> void;
|
||||
auto instructionADC_a_n() -> void;
|
||||
auto instructionADC_a_r(n8&) -> void;
|
||||
auto instructionADD_a_irr(n16&) -> void;
|
||||
auto instructionADD_a_n() -> void;
|
||||
auto instructionADD_a_r(n8&) -> void;
|
||||
auto instructionADD_hl_rr(n16&) -> void;
|
||||
auto instructionAND_a_irr(n16&) -> void;
|
||||
auto instructionAND_a_n() -> void;
|
||||
auto instructionAND_a_r(n8&) -> void;
|
||||
auto instructionCALL_c_nn(bool c) -> void;
|
||||
auto instructionCCF() -> void;
|
||||
auto instructionCP_a_irr(n16& x) -> void;
|
||||
auto instructionCP_a_n() -> void;
|
||||
auto instructionCP_a_r(n8& x) -> void;
|
||||
auto instructionCPL() -> void;
|
||||
auto instructionDAA() -> void;
|
||||
auto instructionDEC_irr(n16&) -> void;
|
||||
auto instructionDEC_r(n8&) -> void;
|
||||
auto instructionDEC_rr(n16&) -> void;
|
||||
auto instructionDI() -> void;
|
||||
auto instructionEI() -> void;
|
||||
auto instructionEX_irr_rr(n16&, n16&) -> void;
|
||||
auto instructionEX_rr_rr(n16&, n16&) -> void;
|
||||
auto instructionHALT() -> void;
|
||||
auto instructionIN_a_in() -> void;
|
||||
auto instructionINC_irr(n16&) -> void;
|
||||
auto instructionINC_r(n8&) -> void;
|
||||
auto instructionINC_rr(n16&) -> void;
|
||||
auto instructionJP_c_nn(bool) -> void;
|
||||
auto instructionJP_rr(n16&) -> void;
|
||||
auto instructionLD_a_inn() -> void;
|
||||
auto instructionLD_a_irr(n16& x) -> void;
|
||||
auto instructionLD_inn_a() -> void;
|
||||
auto instructionLD_inn_rr(n16&) -> void;
|
||||
auto instructionLD_irr_a(n16&) -> void;
|
||||
auto instructionLD_irr_n(n16&) -> void;
|
||||
auto instructionLD_irr_r(n16&, n8&) -> void;
|
||||
auto instructionLD_r_n(n8&) -> void;
|
||||
auto instructionLD_r_irr(n8&, n16&) -> void;
|
||||
auto instructionLD_r_r(n8&, n8&) -> void;
|
||||
auto instructionLD_rr_inn(n16&) -> void;
|
||||
auto instructionLD_rr_nn(n16&) -> void;
|
||||
auto instructionLD_sp_rr(n16&) -> void;
|
||||
auto instructionNOP() -> void;
|
||||
auto instructionOR_a_irr(n16&) -> void;
|
||||
auto instructionOR_a_n() -> void;
|
||||
auto instructionOR_a_r(n8&) -> void;
|
||||
auto instructionOUT_in_a() -> void;
|
||||
auto instructionPOP_rr(n16&) -> void;
|
||||
auto instructionPUSH_rr(n16&) -> void;
|
||||
auto instructionRET() -> void;
|
||||
auto instructionRET_c(bool c) -> void;
|
||||
auto instructionRLA() -> void;
|
||||
auto instructionRLCA() -> void;
|
||||
auto instructionRRA() -> void;
|
||||
auto instructionRRCA() -> void;
|
||||
auto instructionRST_o(n3) -> void;
|
||||
auto instructionSBC_a_irr(n16&) -> void;
|
||||
auto instructionSBC_a_n() -> void;
|
||||
auto instructionSBC_a_r(n8&) -> void;
|
||||
auto instructionSCF() -> void;
|
||||
auto instructionSUB_a_irr(n16&) -> void;
|
||||
auto instructionSUB_a_n() -> void;
|
||||
auto instructionSUB_a_r(n8&) -> void;
|
||||
auto instructionXOR_a_irr(n16&) -> void;
|
||||
auto instructionXOR_a_n() -> void;
|
||||
auto instructionXOR_a_r(n8&) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
//disassembler.cpp
|
||||
noinline auto disassembleInstruction(maybe<n16> pc = {}) -> string;
|
||||
noinline auto disassembleContext() -> string;
|
||||
|
||||
auto disassemble(n16 pc, n8 prefix, n8 code) -> string;
|
||||
|
||||
enum class Prefix : u32 { hl, ix, iy } prefix = Prefix::hl;
|
||||
|
||||
union Pair {
|
||||
Pair() : word(0) {}
|
||||
n16 word;
|
||||
struct Byte { n8 order_msb2(hi, lo); } byte;
|
||||
};
|
||||
|
||||
Pair af, af_;
|
||||
Pair bc, bc_;
|
||||
Pair de, de_;
|
||||
Pair hl, hl_;
|
||||
Pair ix;
|
||||
Pair iy;
|
||||
Pair ir;
|
||||
Pair wz;
|
||||
|
||||
n16 SP;
|
||||
n16 PC;
|
||||
b1 EI; //"ei" executed last
|
||||
b1 P; //"ld a,i" or "ld a,r" executed last
|
||||
b1 Q; //opcode that updated flag registers executed last
|
||||
b1 HALT; //"halt" instruction executed
|
||||
b1 INTE; //interrupt enable
|
||||
|
||||
Bus* bus = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
auto I8080::instruction() -> void {
|
||||
EI = 0;
|
||||
P = 0;
|
||||
|
||||
if(HALT) {
|
||||
return wait(1);
|
||||
}
|
||||
|
||||
n8 code;
|
||||
while(true) {
|
||||
code = opcode();
|
||||
if(code == 0xdd) { prefix = Prefix::ix; continue; }
|
||||
if(code == 0xfd) { prefix = Prefix::iy; continue; }
|
||||
break;
|
||||
}
|
||||
|
||||
instruction(code);
|
||||
|
||||
prefix = Prefix::hl;
|
||||
}
|
||||
|
||||
#define op(id, name, ...) case id: return instruction##name(__VA_ARGS__);
|
||||
|
||||
auto I8080::instruction(n8 code) -> void {
|
||||
switch(code) {
|
||||
op(0x00, NOP)
|
||||
op(0x01, LD_rr_nn, BC)
|
||||
op(0x02, LD_irr_a, BC)
|
||||
op(0x03, INC_rr, BC)
|
||||
op(0x04, INC_r, B)
|
||||
op(0x05, DEC_r, B)
|
||||
op(0x06, LD_r_n, B)
|
||||
op(0x07, RLCA)
|
||||
op(0x09, ADD_hl_rr, BC)
|
||||
op(0x0a, LD_a_irr, BC)
|
||||
op(0x0b, DEC_rr, BC)
|
||||
op(0x0c, INC_r, C)
|
||||
op(0x0d, DEC_r, C)
|
||||
op(0x0e, LD_r_n, C)
|
||||
op(0x0f, RRCA)
|
||||
op(0x11, LD_rr_nn, DE)
|
||||
op(0x12, LD_irr_a, DE)
|
||||
op(0x13, INC_rr, DE)
|
||||
op(0x14, INC_r, D)
|
||||
op(0x15, DEC_r, D)
|
||||
op(0x16, LD_r_n, D)
|
||||
op(0x17, RLA)
|
||||
op(0x19, ADD_hl_rr, DE)
|
||||
op(0x1a, LD_a_irr, DE)
|
||||
op(0x1b, DEC_rr, DE)
|
||||
op(0x1c, INC_r, E)
|
||||
op(0x1d, DEC_r, E)
|
||||
op(0x1e, LD_r_n, E)
|
||||
op(0x1f, RRA)
|
||||
op(0x21, LD_rr_nn, HL)
|
||||
op(0x22, LD_inn_rr, HL)
|
||||
op(0x23, INC_rr, HL)
|
||||
op(0x24, INC_r, H)
|
||||
op(0x25, DEC_r, H)
|
||||
op(0x26, LD_r_n, H)
|
||||
op(0x27, DAA)
|
||||
op(0x29, ADD_hl_rr, HL)
|
||||
op(0x2a, LD_rr_inn, HL)
|
||||
op(0x2b, DEC_rr, HL)
|
||||
op(0x2c, INC_r, L)
|
||||
op(0x2d, DEC_r, L)
|
||||
op(0x2e, LD_r_n, L)
|
||||
op(0x2f, CPL)
|
||||
op(0x31, LD_rr_nn, SP)
|
||||
op(0x32, LD_inn_a)
|
||||
op(0x33, INC_rr, SP)
|
||||
op(0x34, INC_irr, HL)
|
||||
op(0x35, DEC_irr, HL)
|
||||
op(0x36, LD_irr_n, HL)
|
||||
op(0x37, SCF)
|
||||
op(0x39, ADD_hl_rr, SP)
|
||||
op(0x3a, LD_a_inn)
|
||||
op(0x3b, DEC_rr, SP)
|
||||
op(0x3c, INC_r, A)
|
||||
op(0x3d, DEC_r, A)
|
||||
op(0x3e, LD_r_n, A)
|
||||
op(0x3f, CCF)
|
||||
op(0x40, LD_r_r, B, B)
|
||||
op(0x41, LD_r_r, B, C)
|
||||
op(0x42, LD_r_r, B, D)
|
||||
op(0x43, LD_r_r, B, E)
|
||||
op(0x44, LD_r_r, B, H)
|
||||
op(0x45, LD_r_r, B, L)
|
||||
op(0x46, LD_r_irr, B, HL)
|
||||
op(0x47, LD_r_r, B, A)
|
||||
op(0x48, LD_r_r, C, B)
|
||||
op(0x49, LD_r_r, C, C)
|
||||
op(0x4a, LD_r_r, C, D)
|
||||
op(0x4b, LD_r_r, C, E)
|
||||
op(0x4c, LD_r_r, C, H)
|
||||
op(0x4d, LD_r_r, C, L)
|
||||
op(0x4e, LD_r_irr, C, HL)
|
||||
op(0x4f, LD_r_r, C, A)
|
||||
op(0x50, LD_r_r, D, B)
|
||||
op(0x51, LD_r_r, D, C)
|
||||
op(0x52, LD_r_r, D, D)
|
||||
op(0x53, LD_r_r, D, E)
|
||||
op(0x54, LD_r_r, D, H)
|
||||
op(0x55, LD_r_r, D, L)
|
||||
op(0x56, LD_r_irr, D, HL)
|
||||
op(0x57, LD_r_r, D, A)
|
||||
op(0x58, LD_r_r, E, B)
|
||||
op(0x59, LD_r_r, E, C)
|
||||
op(0x5a, LD_r_r, E, D)
|
||||
op(0x5b, LD_r_r, E, E)
|
||||
op(0x5c, LD_r_r, E, H)
|
||||
op(0x5d, LD_r_r, E, L)
|
||||
op(0x5e, LD_r_irr, E, HL)
|
||||
op(0x5f, LD_r_r, E, A)
|
||||
op(0x60, LD_r_r, H, B)
|
||||
op(0x61, LD_r_r, H, C)
|
||||
op(0x62, LD_r_r, H, D)
|
||||
op(0x63, LD_r_r, H, E)
|
||||
op(0x64, LD_r_r, H, H)
|
||||
op(0x65, LD_r_r, H, L)
|
||||
op(0x66, LD_r_irr, H, HL)
|
||||
op(0x67, LD_r_r, H, A)
|
||||
op(0x68, LD_r_r, L, B)
|
||||
op(0x69, LD_r_r, L, C)
|
||||
op(0x6a, LD_r_r, L, D)
|
||||
op(0x6b, LD_r_r, L, E)
|
||||
op(0x6c, LD_r_r, L, H)
|
||||
op(0x6d, LD_r_r, L, L)
|
||||
op(0x6e, LD_r_irr, L, HL)
|
||||
op(0x6f, LD_r_r, L, A)
|
||||
op(0x70, LD_irr_r, HL, B)
|
||||
op(0x71, LD_irr_r, HL, C)
|
||||
op(0x72, LD_irr_r, HL, D)
|
||||
op(0x73, LD_irr_r, HL, E)
|
||||
op(0x74, LD_irr_r, HL, H)
|
||||
op(0x75, LD_irr_r, HL, L)
|
||||
op(0x76, HALT)
|
||||
op(0x77, LD_irr_r, HL, A)
|
||||
op(0x78, LD_r_r, A, B)
|
||||
op(0x79, LD_r_r, A, C)
|
||||
op(0x7a, LD_r_r, A, D)
|
||||
op(0x7b, LD_r_r, A, E)
|
||||
op(0x7c, LD_r_r, A, H)
|
||||
op(0x7d, LD_r_r, A, L)
|
||||
op(0x7e, LD_r_irr, A, HL)
|
||||
op(0x7f, LD_r_r, A, A)
|
||||
op(0x80, ADD_a_r, B)
|
||||
op(0x81, ADD_a_r, C)
|
||||
op(0x82, ADD_a_r, D)
|
||||
op(0x83, ADD_a_r, E)
|
||||
op(0x84, ADD_a_r, H)
|
||||
op(0x85, ADD_a_r, L)
|
||||
op(0x86, ADD_a_irr, HL)
|
||||
op(0x87, ADD_a_r, A)
|
||||
op(0x88, ADC_a_r, B)
|
||||
op(0x89, ADC_a_r, C)
|
||||
op(0x8a, ADC_a_r, D)
|
||||
op(0x8b, ADC_a_r, E)
|
||||
op(0x8c, ADC_a_r, H)
|
||||
op(0x8d, ADC_a_r, L)
|
||||
op(0x8e, ADC_a_irr, HL)
|
||||
op(0x8f, ADC_a_r, A)
|
||||
op(0x90, SUB_a_r, B)
|
||||
op(0x91, SUB_a_r, C)
|
||||
op(0x92, SUB_a_r, D)
|
||||
op(0x93, SUB_a_r, E)
|
||||
op(0x94, SUB_a_r, H)
|
||||
op(0x95, SUB_a_r, L)
|
||||
op(0x96, SUB_a_irr, HL)
|
||||
op(0x97, SUB_a_r, A)
|
||||
op(0x98, SBC_a_r, B)
|
||||
op(0x99, SBC_a_r, C)
|
||||
op(0x9a, SBC_a_r, D)
|
||||
op(0x9b, SBC_a_r, E)
|
||||
op(0x9c, SBC_a_r, H)
|
||||
op(0x9d, SBC_a_r, L)
|
||||
op(0x9e, SBC_a_irr, HL)
|
||||
op(0x9f, SBC_a_r, A)
|
||||
op(0xa0, AND_a_r, B)
|
||||
op(0xa1, AND_a_r, C)
|
||||
op(0xa2, AND_a_r, D)
|
||||
op(0xa3, AND_a_r, E)
|
||||
op(0xa4, AND_a_r, H)
|
||||
op(0xa5, AND_a_r, L)
|
||||
op(0xa6, AND_a_irr, HL)
|
||||
op(0xa7, AND_a_r, A)
|
||||
op(0xa8, XOR_a_r, B)
|
||||
op(0xa9, XOR_a_r, C)
|
||||
op(0xaa, XOR_a_r, D)
|
||||
op(0xab, XOR_a_r, E)
|
||||
op(0xac, XOR_a_r, H)
|
||||
op(0xad, XOR_a_r, L)
|
||||
op(0xae, XOR_a_irr, HL)
|
||||
op(0xaf, XOR_a_r, A)
|
||||
op(0xb0, OR_a_r, B)
|
||||
op(0xb1, OR_a_r, C)
|
||||
op(0xb2, OR_a_r, D)
|
||||
op(0xb3, OR_a_r, E)
|
||||
op(0xb4, OR_a_r, H)
|
||||
op(0xb5, OR_a_r, L)
|
||||
op(0xb6, OR_a_irr, HL)
|
||||
op(0xb7, OR_a_r, A)
|
||||
op(0xb8, CP_a_r, B)
|
||||
op(0xb9, CP_a_r, C)
|
||||
op(0xba, CP_a_r, D)
|
||||
op(0xbb, CP_a_r, E)
|
||||
op(0xbc, CP_a_r, H)
|
||||
op(0xbd, CP_a_r, L)
|
||||
op(0xbe, CP_a_irr, HL)
|
||||
op(0xbf, CP_a_r, A)
|
||||
op(0xc0, RET_c, ZF == 0)
|
||||
op(0xc1, POP_rr, BC)
|
||||
op(0xc2, JP_c_nn, ZF == 0)
|
||||
op(0xc3, JP_c_nn, 1)
|
||||
op(0xc4, CALL_c_nn, ZF == 0)
|
||||
op(0xc5, PUSH_rr, BC)
|
||||
op(0xc6, ADD_a_n)
|
||||
op(0xc7, RST_o, 0)
|
||||
op(0xc8, RET_c, ZF == 1)
|
||||
op(0xc9, RET)
|
||||
op(0xca, JP_c_nn, ZF == 1)
|
||||
op(0xcc, CALL_c_nn, ZF == 1)
|
||||
op(0xcd, CALL_c_nn, true)
|
||||
op(0xce, ADC_a_n)
|
||||
op(0xcf, RST_o, 1)
|
||||
op(0xd0, RET_c, CF == 0)
|
||||
op(0xd1, POP_rr, DE)
|
||||
op(0xd2, JP_c_nn, CF == 0)
|
||||
op(0xd3, OUT_in_a)
|
||||
op(0xd4, CALL_c_nn, CF == 0)
|
||||
op(0xd5, PUSH_rr, DE)
|
||||
op(0xd6, SUB_a_n)
|
||||
op(0xd7, RST_o, 2)
|
||||
op(0xd8, RET_c, CF == 1)
|
||||
op(0xda, JP_c_nn, CF == 1)
|
||||
op(0xdb, IN_a_in)
|
||||
op(0xdc, CALL_c_nn, CF == 1)
|
||||
op(0xde, SBC_a_n)
|
||||
op(0xdf, RST_o, 3)
|
||||
op(0xe0, RET_c, PF == 0)
|
||||
op(0xe1, POP_rr, HL)
|
||||
op(0xe2, JP_c_nn, PF == 0)
|
||||
op(0xe3, EX_irr_rr, SP, HL)
|
||||
op(0xe4, CALL_c_nn, PF == 0)
|
||||
op(0xe5, PUSH_rr, HL)
|
||||
op(0xe6, AND_a_n)
|
||||
op(0xe7, RST_o, 4)
|
||||
op(0xe8, RET_c, PF == 1)
|
||||
op(0xe9, JP_rr, HL)
|
||||
op(0xea, JP_c_nn, PF == 1)
|
||||
op(0xeb, EX_rr_rr, DE, HL)
|
||||
op(0xec, CALL_c_nn, PF == 1)
|
||||
op(0xee, XOR_a_n)
|
||||
op(0xef, RST_o, 5)
|
||||
op(0xf0, RET_c, SF == 0)
|
||||
op(0xf1, POP_rr, AF)
|
||||
op(0xf2, JP_c_nn, SF == 0)
|
||||
op(0xf3, DI)
|
||||
op(0xf4, CALL_c_nn, SF == 0)
|
||||
op(0xf5, PUSH_rr, AF)
|
||||
op(0xf6, OR_a_n)
|
||||
op(0xf7, RST_o, 6)
|
||||
op(0xf8, RET_c, SF == 1)
|
||||
op(0xf9, LD_sp_rr, HL)
|
||||
op(0xfa, JP_c_nn, SF == 1)
|
||||
op(0xfb, EI)
|
||||
op(0xfc, CALL_c_nn, SF == 1)
|
||||
op(0xfe, CP_a_n)
|
||||
op(0xff, RST_o, 7)
|
||||
}
|
||||
}
|
||||
|
||||
#undef op
|
||||
@@ -0,0 +1,364 @@
|
||||
//legend:
|
||||
// a = register A
|
||||
// c = condition
|
||||
// e = relative operand
|
||||
// in = (operand)
|
||||
// inn = (operand-word)
|
||||
// irr = (register-word)
|
||||
// o = opcode bits
|
||||
// n = operand
|
||||
// nn = operand-word
|
||||
// r = register
|
||||
|
||||
auto I8080::instructionADC_a_irr(n16& x) -> void { Q = 1;
|
||||
A = ADD(A, read(x), CF);
|
||||
}
|
||||
|
||||
auto I8080::instructionADC_a_n() -> void { Q = 1;
|
||||
A = ADD(A, operand(), CF);
|
||||
}
|
||||
|
||||
auto I8080::instructionADC_a_r(n8& x) -> void { Q = 1;
|
||||
A = ADD(A, x, CF);
|
||||
}
|
||||
|
||||
auto I8080::instructionADD_a_irr(n16& x) -> void { Q = 1;
|
||||
A = ADD(A, read(x));
|
||||
}
|
||||
|
||||
auto I8080::instructionADD_a_n() -> void { Q = 1;
|
||||
A = ADD(A, operand());
|
||||
}
|
||||
|
||||
auto I8080::instructionADD_a_r(n8& x) -> void { Q = 1;
|
||||
A = ADD(A, x);
|
||||
}
|
||||
|
||||
auto I8080::instructionADD_hl_rr(n16& x) -> void { Q = 1;
|
||||
WZ = HL + 1;
|
||||
bool pf = PF, zf = ZF, sf = SF, hf = HF;
|
||||
wait(3);
|
||||
auto lo = ADD(HL >> 0, x >> 0);
|
||||
wait(4);
|
||||
auto hi = ADD(HL >> 8, x >> 8, CF);
|
||||
HL = hi << 8 | lo << 0;
|
||||
PF = pf, ZF = zf, SF = sf, HF = hf; //restore unaffected flags
|
||||
}
|
||||
|
||||
auto I8080::instructionAND_a_irr(n16& x) -> void { Q = 1;
|
||||
A = AND(A, read(x));
|
||||
}
|
||||
|
||||
auto I8080::instructionAND_a_n() -> void { Q = 1;
|
||||
A = AND(A, operand());
|
||||
}
|
||||
|
||||
auto I8080::instructionAND_a_r(n8& x) -> void { Q = 1;
|
||||
A = AND(A, x);
|
||||
}
|
||||
|
||||
auto I8080::instructionCALL_c_nn(bool c) -> void { Q = 0;
|
||||
WZ = operands();
|
||||
if(!c) return;
|
||||
wait(1);
|
||||
push(PC);
|
||||
PC = WZ;
|
||||
}
|
||||
|
||||
auto I8080::instructionCCF() -> void {
|
||||
CF = !CF;
|
||||
Q = 1;
|
||||
}
|
||||
|
||||
auto I8080::instructionCP_a_irr(n16& x) -> void { Q = 1;
|
||||
CP(A, read(x));
|
||||
}
|
||||
|
||||
auto I8080::instructionCP_a_n() -> void { Q = 1;
|
||||
CP(A, operand());
|
||||
}
|
||||
|
||||
auto I8080::instructionCP_a_r(n8& x) -> void { Q = 1;
|
||||
CP(A, x);
|
||||
}
|
||||
|
||||
auto I8080::instructionCPL() -> void { Q = 1;
|
||||
A = ~A;
|
||||
}
|
||||
|
||||
auto I8080::instructionDAA() -> void { Q = 1;
|
||||
auto a = A;
|
||||
if(CF || (A.bit(0,7) > 0x99)) { A += 0x60; CF = 1; }
|
||||
if(HF || (A.bit(0,3) > 0x09)) { A += 0x06; }
|
||||
|
||||
PF = parity(A);
|
||||
HF = n8(A ^ a).bit(4);
|
||||
ZF = A == 0;
|
||||
SF = A.bit(7);
|
||||
}
|
||||
|
||||
auto I8080::instructionDEC_irr(n16& x) -> void { Q = 1;
|
||||
auto addr = x;
|
||||
auto data = read(addr);
|
||||
wait(1);
|
||||
write(addr, DEC(data));
|
||||
}
|
||||
|
||||
auto I8080::instructionDEC_r(n8& x) -> void { Q = 1;
|
||||
x = DEC(x);
|
||||
}
|
||||
|
||||
auto I8080::instructionDEC_rr(n16& x) -> void { Q = 0;
|
||||
wait(2);
|
||||
x--;
|
||||
}
|
||||
|
||||
auto I8080::instructionDI() -> void { Q = 0;
|
||||
INTE = 0;
|
||||
}
|
||||
|
||||
auto I8080::instructionEI() -> void { Q = 0;
|
||||
INTE = 1;
|
||||
EI = 1; //ignore maskable interrupts until after the next instruction
|
||||
}
|
||||
|
||||
auto I8080::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;
|
||||
}
|
||||
|
||||
auto I8080::instructionEX_rr_rr(n16& x, n16& y) -> void { Q = 0;
|
||||
swap(x, y);
|
||||
}
|
||||
|
||||
auto I8080::instructionHALT() -> void { Q = 0;
|
||||
HALT = 1;
|
||||
}
|
||||
|
||||
auto I8080::instructionIN_a_in() -> void { Q = 0;
|
||||
WZL = operand();
|
||||
WZH = A;
|
||||
A = in(WZ++);
|
||||
}
|
||||
|
||||
auto I8080::instructionINC_irr(n16& x) -> void { Q = 1;
|
||||
auto addr = x;
|
||||
auto data = read(addr);
|
||||
wait(1);
|
||||
write(addr, INC(data));
|
||||
}
|
||||
|
||||
auto I8080::instructionINC_r(n8& x) -> void { Q = 1;
|
||||
x = INC(x);
|
||||
}
|
||||
|
||||
auto I8080::instructionINC_rr(n16& x) -> void { Q = 0;
|
||||
wait(2);
|
||||
x++;
|
||||
}
|
||||
|
||||
auto I8080::instructionJP_c_nn(bool c) -> void { Q = 0;
|
||||
WZ = operands();
|
||||
if(c) PC = WZ;
|
||||
}
|
||||
|
||||
auto I8080::instructionJP_rr(n16& x) -> void { Q = 0;
|
||||
PC = x;
|
||||
}
|
||||
|
||||
auto I8080::instructionLD_a_inn() -> void { Q = 0;
|
||||
WZ = operands();
|
||||
A = read(WZ++);
|
||||
}
|
||||
|
||||
auto I8080::instructionLD_a_irr(n16& x) -> void { Q = 0;
|
||||
WZ = x;
|
||||
A = read(x);
|
||||
WZ++;
|
||||
}
|
||||
|
||||
auto I8080::instructionLD_inn_a() -> void { Q = 0;
|
||||
WZ = operands();
|
||||
write(WZ++, A);
|
||||
WZH = A;
|
||||
}
|
||||
|
||||
auto I8080::instructionLD_inn_rr(n16& x) -> void { Q = 0;
|
||||
WZ = operands();
|
||||
write(WZ + 0, x >> 0);
|
||||
write(WZ + 1, x >> 8);
|
||||
WZ++;
|
||||
}
|
||||
|
||||
auto I8080::instructionLD_irr_a(n16& x) -> void { Q = 0;
|
||||
WZ = x;
|
||||
write(x, A);
|
||||
WZL++;
|
||||
WZH = A;
|
||||
}
|
||||
|
||||
auto I8080::instructionLD_irr_n(n16& x) -> void { Q = 0;
|
||||
auto addr = x;
|
||||
auto data = operand();
|
||||
if(&x == &ix.word || &x == &iy.word) wait(2);
|
||||
write(addr, data);
|
||||
}
|
||||
|
||||
auto I8080::instructionLD_irr_r(n16& x, n8& y) -> void { Q = 0;
|
||||
write(x, y);
|
||||
}
|
||||
|
||||
auto I8080::instructionLD_r_n(n8& x) -> void { Q = 0;
|
||||
x = operand();
|
||||
}
|
||||
|
||||
auto I8080::instructionLD_r_irr(n8& x, n16& y) -> void { Q = 0;
|
||||
x = read(y);
|
||||
}
|
||||
|
||||
auto I8080::instructionLD_r_r(n8& x, n8& y) -> void { Q = 0;
|
||||
x = y;
|
||||
}
|
||||
|
||||
auto I8080::instructionLD_rr_inn(n16& x) -> void { Q = 0;
|
||||
WZ = operands();
|
||||
x.byte(0) = read(WZ);
|
||||
WZ++;
|
||||
x.byte(1) = read(WZ);
|
||||
}
|
||||
|
||||
auto I8080::instructionLD_rr_nn(n16& x) -> void { Q = 0;
|
||||
x = operands();
|
||||
}
|
||||
|
||||
auto I8080::instructionLD_sp_rr(n16& x) -> void { Q = 0;
|
||||
wait(2);
|
||||
SP = x;
|
||||
}
|
||||
|
||||
auto I8080::instructionNOP() -> void { Q = 0;
|
||||
}
|
||||
|
||||
auto I8080::instructionOR_a_irr(n16& x) -> void { Q = 1;
|
||||
A = OR(A, read(x));
|
||||
}
|
||||
|
||||
auto I8080::instructionOR_a_n() -> void { Q = 1;
|
||||
A = OR(A, operand());
|
||||
}
|
||||
|
||||
auto I8080::instructionOR_a_r(n8& x) -> void { Q = 1;
|
||||
A = OR(A, x);
|
||||
}
|
||||
|
||||
auto I8080::instructionOUT_in_a() -> void { Q = 0;
|
||||
WZL = operand();
|
||||
WZH = A;
|
||||
out(WZ, A);
|
||||
WZL++;
|
||||
}
|
||||
|
||||
//note: even though "pop af" affects flags, it does not set Q
|
||||
auto I8080::instructionPOP_rr(n16& x) -> void { Q = 0;
|
||||
x = pop();
|
||||
if(&x == &af.word) F = (F & 0xd7) | 2;
|
||||
}
|
||||
|
||||
auto I8080::instructionPUSH_rr(n16& x) -> void { Q = 0;
|
||||
wait(1);
|
||||
if(&x == &af.word) F = (F & 0xd7) | 2;
|
||||
push(x);
|
||||
}
|
||||
|
||||
auto I8080::instructionRET() -> void { Q = 0;
|
||||
WZ = pop();
|
||||
PC = WZ;
|
||||
}
|
||||
|
||||
auto I8080::instructionRET_c(bool c) -> void { Q = 0;
|
||||
wait(1);
|
||||
if(!c) return;
|
||||
WZ = pop();
|
||||
PC = WZ;
|
||||
}
|
||||
|
||||
auto I8080::instructionRLA() -> void { Q = 1;
|
||||
bool c = A.bit(7);
|
||||
A = A << 1 | CF;
|
||||
|
||||
CF = c;
|
||||
}
|
||||
|
||||
auto I8080::instructionRLCA() -> void { Q = 1;
|
||||
bool c = A.bit(7);
|
||||
A = A << 1 | c;
|
||||
|
||||
CF = c;
|
||||
}
|
||||
|
||||
auto I8080::instructionRRA() -> void { Q = 1;
|
||||
bool c = A.bit(0);
|
||||
A = CF << 7 | A >> 1;
|
||||
|
||||
CF = c;
|
||||
}
|
||||
|
||||
auto I8080::instructionRRCA() -> void { Q = 1;
|
||||
bool c = A.bit(0);
|
||||
A = c << 7 | A >> 1;
|
||||
|
||||
CF = c;
|
||||
}
|
||||
|
||||
auto I8080::instructionRST_o(n3 vector) -> void { Q = 0;
|
||||
wait(1);
|
||||
push(PC);
|
||||
WZ = vector << 3;
|
||||
PC = WZ;
|
||||
}
|
||||
|
||||
auto I8080::instructionSBC_a_irr(n16& x) -> void { Q = 1;
|
||||
A = SUB(A, read(x), CF);
|
||||
}
|
||||
|
||||
auto I8080::instructionSBC_a_n() -> void { Q = 1;
|
||||
A = SUB(A, operand(), CF);
|
||||
}
|
||||
|
||||
auto I8080::instructionSBC_a_r(n8& x) -> void { Q = 1;
|
||||
A = SUB(A, x, CF);
|
||||
}
|
||||
|
||||
auto I8080::instructionSCF() -> void {
|
||||
CF = 1;
|
||||
Q = 1;
|
||||
}
|
||||
|
||||
auto I8080::instructionSUB_a_irr(n16& x) -> void { Q = 1;
|
||||
A = SUB(A, read(x));
|
||||
}
|
||||
|
||||
auto I8080::instructionSUB_a_n() -> void { Q = 1;
|
||||
A = SUB(A, operand());
|
||||
}
|
||||
|
||||
auto I8080::instructionSUB_a_r(n8& x) -> void { Q = 1;
|
||||
A = SUB(A, x);
|
||||
}
|
||||
|
||||
auto I8080::instructionXOR_a_irr(n16& x) -> void { Q = 1;
|
||||
A = XOR(A, read(x));
|
||||
}
|
||||
|
||||
auto I8080::instructionXOR_a_n() -> void { Q = 1;
|
||||
A = XOR(A, operand());
|
||||
}
|
||||
|
||||
auto I8080::instructionXOR_a_r(n8& x) -> void { Q = 1;
|
||||
A = XOR(A, x);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
auto I8080::wait(u32 clocks) -> void {
|
||||
step(clocks);
|
||||
}
|
||||
|
||||
auto I8080::opcode() -> n8 {
|
||||
step(4);
|
||||
return bus->read(PC++);
|
||||
}
|
||||
|
||||
auto I8080::operand() -> n8 {
|
||||
step(3);
|
||||
return bus->read(PC++);
|
||||
}
|
||||
|
||||
auto I8080::operands() -> n16 {
|
||||
n16 data = operand() << 0;
|
||||
return data | operand() << 8;
|
||||
}
|
||||
|
||||
auto I8080::push(n16 x) -> void {
|
||||
write(--SP, x >> 8);
|
||||
write(--SP, x >> 0);
|
||||
}
|
||||
|
||||
auto I8080::pop() -> n16 {
|
||||
n16 data = read(SP++) << 0;
|
||||
return data | read(SP++) << 8;
|
||||
}
|
||||
|
||||
auto I8080::read(n16 address) -> n8 {
|
||||
step(3);
|
||||
return bus->read(address);
|
||||
}
|
||||
|
||||
auto I8080::write(n16 address, n8 data) -> void {
|
||||
step(3);
|
||||
return bus->write(address, data);
|
||||
}
|
||||
|
||||
auto I8080::in(n16 address) -> n8 {
|
||||
step(3);
|
||||
return bus->in(address);
|
||||
}
|
||||
|
||||
auto I8080::out(n16 address, n8 data) -> void {
|
||||
step(3);
|
||||
return bus->out(address, data);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#define AF af.word
|
||||
#define BC bc.word
|
||||
#define DE de.word
|
||||
#define HL hl.word
|
||||
#define WZ wz.word
|
||||
#define WZH wz.byte.hi
|
||||
#define WZL wz.byte.lo
|
||||
|
||||
#define A af.byte.hi
|
||||
#define F af.byte.lo
|
||||
#define B bc.byte.hi
|
||||
#define C bc.byte.lo
|
||||
#define D de.byte.hi
|
||||
#define E de.byte.lo
|
||||
#define H hl.byte.hi
|
||||
#define L hl.byte.lo
|
||||
|
||||
#define CF af.byte.lo.bit(0)
|
||||
#define PF af.byte.lo.bit(2)
|
||||
#define HF af.byte.lo.bit(4)
|
||||
#define ZF af.byte.lo.bit(6)
|
||||
#define SF af.byte.lo.bit(7)
|
||||
@@ -0,0 +1,22 @@
|
||||
auto I8080::serialize(serializer& s) -> void {
|
||||
s((u32&)prefix);
|
||||
s(af.word);
|
||||
s(bc.word);
|
||||
s(de.word);
|
||||
s(hl.word);
|
||||
s(af_.word);
|
||||
s(bc_.word);
|
||||
s(de_.word);
|
||||
s(hl_.word);
|
||||
s(ix.word);
|
||||
s(iy.word);
|
||||
s(ir.word);
|
||||
s(wz.word);
|
||||
s(SP);
|
||||
s(PC);
|
||||
s(EI);
|
||||
s(P);
|
||||
s(Q);
|
||||
s(HALT);
|
||||
s(INTE);
|
||||
}
|
||||
@@ -197,9 +197,9 @@ template<u32 Size> auto M68000::instructionASR(n4 count, DataRegister with) -> v
|
||||
|
||||
template<u32 Size> auto M68000::instructionASR(DataRegister from, DataRegister with) -> void {
|
||||
auto count = read<Long>(from) & 63;
|
||||
prefetch();
|
||||
idle((Size != Long ? 2 : 4) + count * 2);
|
||||
auto result = ASR<Size>(read<Size>(with), count);
|
||||
prefetch();
|
||||
write<Size>(with, result);
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ auto M68000::exception(u32 exception, u32 vector, u32 priority) -> void {
|
||||
if(exception == Exception::Interrupt) {
|
||||
// IACK vector number acquisition (+4 cyc normal or +10 to +18 cyc autovectored)
|
||||
// & justify vector number (+4 cyc)
|
||||
idle(16+4); // assuming autovector cycles (approximated for Megadrive)
|
||||
idle(14+4); // assuming autovector cycles (approximated for Megadrive)
|
||||
r.i = priority;
|
||||
}
|
||||
|
||||
|
||||
@@ -123,6 +123,12 @@ auto MOS6502::algorithmDEC(n8 i) -> n8 {
|
||||
return i;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmDCP(n8 i) -> n8 {
|
||||
auto value = algorithmDEC(i);
|
||||
A = algorithmCMP(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmEOR(n8 i) -> n8 {
|
||||
n8 o = A ^ i;
|
||||
Z = o == 0;
|
||||
@@ -137,6 +143,12 @@ auto MOS6502::algorithmINC(n8 i) -> n8 {
|
||||
return i;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmISC(n8 i) -> n8 {
|
||||
auto value = algorithmINC(i);
|
||||
A = algorithmSBC(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmLD(n8 i) -> n8 {
|
||||
Z = i == 0;
|
||||
N = i.bit(7);
|
||||
@@ -151,6 +163,12 @@ auto MOS6502::algorithmLSR(n8 i) -> n8 {
|
||||
return i;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmSRE(n8 i) -> n8 {
|
||||
auto value = algorithmLSR(i);
|
||||
A = algorithmEOR(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmORA(n8 i) -> n8 {
|
||||
n8 o = A | i;
|
||||
Z = o == 0;
|
||||
@@ -167,6 +185,12 @@ auto MOS6502::algorithmROL(n8 i) -> n8 {
|
||||
return i;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmRLA(n8 i) -> n8 {
|
||||
auto value = algorithmROL(i);
|
||||
A = algorithmAND(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmROR(n8 i) -> n8 {
|
||||
bool c = C;
|
||||
C = i.bit(0);
|
||||
@@ -176,6 +200,12 @@ auto MOS6502::algorithmROR(n8 i) -> n8 {
|
||||
return i;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmRRA(n8 i) -> n8 {
|
||||
auto value = algorithmROR(i);
|
||||
A = algorithmADC(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmSBC(n8 i) -> n8 {
|
||||
i = ~i;
|
||||
i16 o = A + i + C;
|
||||
@@ -200,13 +230,7 @@ auto MOS6502::algorithmSBC(n8 i) -> n8 {
|
||||
}
|
||||
|
||||
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;
|
||||
auto value = algorithmASL(i);
|
||||
A = algorithmORA(value);
|
||||
return value;
|
||||
}
|
||||
@@ -51,6 +51,7 @@ auto MOS6502::instruction() -> void {
|
||||
op(0x24, ZeroPageRead, fp(BIT), A)
|
||||
op(0x25, ZeroPageRead, fp(AND), A)
|
||||
op(0x26, ZeroPageModify, fp(ROL))
|
||||
op(0x27, ZeroPageModify, fp(RLA))
|
||||
op(0x28, PullP)
|
||||
op(0x29, Immediate, fp(AND), A)
|
||||
op(0x2a, Implied, fp(ROL), A)
|
||||
@@ -58,22 +59,28 @@ auto MOS6502::instruction() -> void {
|
||||
op(0x2c, AbsoluteRead, fp(BIT), A)
|
||||
op(0x2d, AbsoluteRead, fp(AND), A)
|
||||
op(0x2e, AbsoluteModify, fp(ROL))
|
||||
op(0x2f, AbsoluteModify, fp(RLA))
|
||||
op(0x30, Branch, N == 1)
|
||||
op(0x31, IndirectYRead, fp(AND), A)
|
||||
op(0x34, NoOperationZeroPage, X)
|
||||
op(0x35, ZeroPageRead, fp(AND), A, X)
|
||||
op(0x36, ZeroPageModify, fp(ROL), X)
|
||||
op(0x37, ZeroPageModify, fp(RLA), X)
|
||||
op(0x38, Set, C)
|
||||
op(0x39, AbsoluteRead, fp(AND), A, Y)
|
||||
op(0x3a, NoOperation)
|
||||
op(0x3b, AbsoluteModify, fp(RLA), Y)
|
||||
op(0x3c, NoOperationAbsolute, X)
|
||||
op(0x3d, AbsoluteRead, fp(AND), A, X)
|
||||
op(0x3e, AbsoluteModify, fp(ROL), X)
|
||||
op(0x3f, AbsoluteModify, fp(RLA), X)
|
||||
op(0x40, ReturnInterrupt)
|
||||
op(0x41, IndirectXRead, fp(EOR), A)
|
||||
op(0x43, IndirectXRead, fp(SRE), X)
|
||||
op(0x44, NoOperationZeroPage)
|
||||
op(0x45, ZeroPageRead, fp(EOR), A)
|
||||
op(0x46, ZeroPageModify, fp(LSR))
|
||||
op(0x47, ZeroPageModify, fp(SRE))
|
||||
op(0x48, Push, A)
|
||||
op(0x49, Immediate, fp(EOR), A)
|
||||
op(0x4a, Implied, fp(LSR), A)
|
||||
@@ -81,22 +88,27 @@ auto MOS6502::instruction() -> void {
|
||||
op(0x4c, JumpAbsolute)
|
||||
op(0x4d, AbsoluteRead, fp(EOR), A)
|
||||
op(0x4e, AbsoluteModify, fp(LSR))
|
||||
op(0x4f, AbsoluteModify, fp(SRE))
|
||||
op(0x50, Branch, V == 0)
|
||||
op(0x51, IndirectYRead, fp(EOR), A)
|
||||
op(0x54, NoOperationZeroPage, X)
|
||||
op(0x55, ZeroPageRead, fp(EOR), A, X)
|
||||
op(0x56, ZeroPageModify, fp(LSR), X)
|
||||
op(0x57, ZeroPageModify, fp(SRE), X)
|
||||
op(0x58, Clear, I)
|
||||
op(0x59, AbsoluteRead, fp(EOR), A, Y)
|
||||
op(0x5a, NoOperation)
|
||||
op(0x5b, AbsoluteModify, fp(SRE), Y)
|
||||
op(0x5c, NoOperationAbsolute, X)
|
||||
op(0x5d, AbsoluteRead, fp(EOR), A, X)
|
||||
op(0x5e, AbsoluteModify, fp(LSR), X)
|
||||
op(0x5f, AbsoluteModify, fp(SRE), X)
|
||||
op(0x60, ReturnSubroutine)
|
||||
op(0x61, IndirectXRead, fp(ADC), A)
|
||||
op(0x64, NoOperationZeroPage)
|
||||
op(0x65, ZeroPageRead, fp(ADC), A)
|
||||
op(0x66, ZeroPageModify, fp(ROR))
|
||||
op(0x67, ZeroPageModify, fp(RRA))
|
||||
op(0x68, Pull, A)
|
||||
op(0x69, Immediate, fp(ADC), A)
|
||||
op(0x6a, Implied, fp(ROR), A)
|
||||
@@ -104,17 +116,21 @@ auto MOS6502::instruction() -> void {
|
||||
op(0x6c, JumpIndirect)
|
||||
op(0x6d, AbsoluteRead, fp(ADC), A)
|
||||
op(0x6e, AbsoluteModify, fp(ROR))
|
||||
op(0x6f, AbsoluteModify, fp(RRA))
|
||||
op(0x70, Branch, V == 1)
|
||||
op(0x71, IndirectYRead, fp(ADC), A)
|
||||
op(0x74, NoOperationZeroPage, X)
|
||||
op(0x75, ZeroPageRead, fp(ADC), A, X)
|
||||
op(0x76, ZeroPageModify, fp(ROR), X)
|
||||
op(0x77, ZeroPageModify, fp(RRA), X)
|
||||
op(0x78, Set, I)
|
||||
op(0x79, AbsoluteRead, fp(ADC), A, Y)
|
||||
op(0x7a, NoOperation)
|
||||
op(0x7b, AbsoluteModify, fp(RRA), Y)
|
||||
op(0x7c, NoOperationAbsolute, X)
|
||||
op(0x7d, AbsoluteRead, fp(ADC), A, X)
|
||||
op(0x7e, AbsoluteModify, fp(ROR), X)
|
||||
op(0x7f, AbsoluteModify, fp(RRA), X)
|
||||
op(0x80, NoOperationImmediate)
|
||||
op(0x81, IndirectXWrite, A)
|
||||
op(0x82, NoOperationImmediate)
|
||||
@@ -167,6 +183,7 @@ auto MOS6502::instruction() -> void {
|
||||
op(0xc4, ZeroPageRead, fp(CPY), Y)
|
||||
op(0xc5, ZeroPageRead, fp(CMP), A)
|
||||
op(0xc6, ZeroPageModify, fp(DEC))
|
||||
op(0xc7, ZeroPageModify, fp(DCP))
|
||||
op(0xc8, Implied, fp(INC), Y)
|
||||
op(0xc9, Immediate, fp(CMP), A)
|
||||
op(0xca, Implied, fp(DEC), X)
|
||||
@@ -174,23 +191,28 @@ auto MOS6502::instruction() -> void {
|
||||
op(0xcc, AbsoluteRead, fp(CPY), Y)
|
||||
op(0xcd, AbsoluteRead, fp(CMP), A)
|
||||
op(0xce, AbsoluteModify, fp(DEC))
|
||||
op(0xcf, AbsoluteModify, fp(DCP))
|
||||
op(0xd0, Branch, Z == 0)
|
||||
op(0xd1, IndirectYRead, fp(CMP), A)
|
||||
op(0xd4, NoOperationZeroPage, X)
|
||||
op(0xd5, ZeroPageRead, fp(CMP), A, X)
|
||||
op(0xd6, ZeroPageModify, fp(DEC), X)
|
||||
op(0xd7, ZeroPageModify, fp(DCP), X)
|
||||
op(0xd8, Clear, D)
|
||||
op(0xd9, AbsoluteRead, fp(CMP), A, Y)
|
||||
op(0xda, NoOperation)
|
||||
op(0xdb, AbsoluteModify, fp(DCP), Y)
|
||||
op(0xdc, NoOperationAbsolute, X)
|
||||
op(0xdd, AbsoluteRead, fp(CMP), A, X)
|
||||
op(0xde, AbsoluteModify, fp(DEC), X)
|
||||
op(0xdf, AbsoluteModify, fp(DCP), X)
|
||||
op(0xe0, Immediate, fp(CPX), X)
|
||||
op(0xe1, IndirectXRead, fp(SBC), A)
|
||||
op(0xe2, NoOperationImmediate)
|
||||
op(0xe4, ZeroPageRead, fp(CPX), X)
|
||||
op(0xe5, ZeroPageRead, fp(SBC), A)
|
||||
op(0xe6, ZeroPageModify, fp(INC))
|
||||
op(0xe7, ZeroPageModify, fp(ISC))
|
||||
op(0xe8, Implied, fp(INC), X)
|
||||
op(0xe9, Immediate, fp(SBC), A)
|
||||
op(0xea, NoOperation)
|
||||
@@ -198,17 +220,21 @@ auto MOS6502::instruction() -> void {
|
||||
op(0xec, AbsoluteRead, fp(CPX), X)
|
||||
op(0xed, AbsoluteRead, fp(SBC), A)
|
||||
op(0xee, AbsoluteModify, fp(INC))
|
||||
op(0xef, AbsoluteModify, fp(ISC))
|
||||
op(0xf0, Branch, Z == 1)
|
||||
op(0xf1, IndirectYRead, fp(SBC), A)
|
||||
op(0xf4, NoOperationZeroPage, X)
|
||||
op(0xf5, ZeroPageRead, fp(SBC), A, X)
|
||||
op(0xf6, ZeroPageModify, fp(INC), X)
|
||||
op(0xf7, ZeroPageModify, fp(ISC), X)
|
||||
op(0xf8, Set, D)
|
||||
op(0xf9, AbsoluteRead, fp(SBC), A, Y)
|
||||
op(0xfa, NoOperation)
|
||||
op(0xfb, AbsoluteModify, fp(ISC), Y)
|
||||
op(0xfc, NoOperationAbsolute, X)
|
||||
op(0xfd, AbsoluteRead, fp(SBC), A, X)
|
||||
op(0xfe, AbsoluteModify, fp(INC), X)
|
||||
op(0xff, AbsoluteModify, fp(ISC), X)
|
||||
}
|
||||
|
||||
//unimplemented instruction
|
||||
|
||||
@@ -42,13 +42,18 @@ struct MOS6502 {
|
||||
auto algorithmCPX(n8) -> n8;
|
||||
auto algorithmCPY(n8) -> n8;
|
||||
auto algorithmDEC(n8) -> n8;
|
||||
auto algorithmDCP(n8) -> n8;
|
||||
auto algorithmEOR(n8) -> n8;
|
||||
auto algorithmINC(n8) -> n8;
|
||||
auto algorithmISC(n8) -> n8;
|
||||
auto algorithmLD (n8) -> n8;
|
||||
auto algorithmLSR(n8) -> n8;
|
||||
auto algorithmSRE(n8) -> n8;
|
||||
auto algorithmORA(n8) -> n8;
|
||||
auto algorithmROL(n8) -> n8;
|
||||
auto algorithmRLA(n8) -> n8;
|
||||
auto algorithmROR(n8) -> n8;
|
||||
auto algorithmRRA(n8) -> n8;
|
||||
auto algorithmSBC(n8) -> n8;
|
||||
auto algorithmSLO(n8) -> n8;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ auto SH2::hint(P&&... p) const -> string {
|
||||
return {std::forward<P>(p)...};
|
||||
}
|
||||
|
||||
auto SH2::disassembleInstruction() -> string {
|
||||
auto SH2::disassembleInstruction(u16 opcode) -> string {
|
||||
vector<string> s;
|
||||
|
||||
auto registerName = [&](u32 r) -> string {
|
||||
@@ -48,8 +48,6 @@ auto SH2::disassembleInstruction() -> string {
|
||||
return {"0x", hex(PC + (i12)d * 2, 8L)};
|
||||
};
|
||||
|
||||
u16 opcode = readWord(PC - 4);
|
||||
|
||||
#define n (opcode >> 8 & 0x00f)
|
||||
#define m (opcode >> 4 & 0x00f)
|
||||
#define i (opcode >> 0 & 0x0ff)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
auto SH2::exceptionHandler() -> void {
|
||||
if(!exceptions) return;
|
||||
if(inDelaySlot()) { cyclesUntilSync = 0; return; }
|
||||
if(inDelaySlot()) { cyclesUntilRecompilerExit = 0; return; }
|
||||
|
||||
if(exceptions & ResetCold) {
|
||||
exceptions &= ~ResetCold;
|
||||
|
||||
@@ -23,6 +23,7 @@ auto SH2::instruction() -> void {
|
||||
}
|
||||
ID = 0;
|
||||
u16 opcode = readWord(PC - 4);
|
||||
instructionPrologue(opcode);
|
||||
execute(opcode);
|
||||
instructionEpilogue();
|
||||
}
|
||||
@@ -35,7 +36,10 @@ auto SH2::instruction() -> void {
|
||||
do {
|
||||
auto block = recompiler.block(PC - 4);
|
||||
block->execute(*this);
|
||||
} while (CCR < cyclesUntilSync);
|
||||
} while (CCR < cyclesUntilRecompilerExit);
|
||||
|
||||
// Reset the count as it may have been set to 0 for an early exit
|
||||
cyclesUntilRecompilerExit = recompilerStepCycles;
|
||||
|
||||
step(CCR);
|
||||
CCR = 0;
|
||||
|
||||
@@ -43,6 +43,7 @@ auto SH2::Recompiler::pool(u32 address) -> Pool* {
|
||||
if(!pool) {
|
||||
pool = (Pool*)allocator.acquire(sizeof(Pool));
|
||||
memory::jitprotect(false);
|
||||
*pool = {};
|
||||
pool->generation = generation;
|
||||
memory::jitprotect(true);
|
||||
} else if(address >> 29 == Area::Cached && pool->generation != generation) {
|
||||
@@ -115,9 +116,7 @@ auto SH2::Recompiler::hash(u32 address, u8 size) -> u64 {
|
||||
auto SH2::Recompiler::emit(u32 address) -> Block* {
|
||||
if(unlikely(allocator.available() < 1_MiB)) {
|
||||
print("SH2 allocator flush\n");
|
||||
memory::jitprotect(false);
|
||||
allocator.release(bump_allocator::zero_fill);
|
||||
memory::jitprotect(true);
|
||||
allocator.release();
|
||||
reset();
|
||||
}
|
||||
|
||||
@@ -130,6 +129,10 @@ auto SH2::Recompiler::emit(u32 address) -> Block* {
|
||||
inDelaySlot = 1; //force runtime check on first instruction
|
||||
while(true) {
|
||||
u16 instruction = instructions[index++];
|
||||
if(callInstructionPrologue) {
|
||||
mov32(reg(1), imm(instruction));
|
||||
call(&SH2::instructionPrologue);
|
||||
}
|
||||
auto branch = emitInstruction(instruction);
|
||||
inDelaySlot = branch == Branch::Slot;
|
||||
add64(CCR, CCR, imm(1));
|
||||
|
||||
@@ -48,7 +48,7 @@ auto SH2::power(bool reset) -> void {
|
||||
ET = 0;
|
||||
ID = 0;
|
||||
exceptions = !reset ? ResetCold : ResetWarm;
|
||||
cyclesUntilSync = 0;
|
||||
cyclesUntilRecompilerExit = 0;
|
||||
|
||||
cache = {*this};
|
||||
intc = {*this};
|
||||
@@ -64,8 +64,10 @@ auto SH2::power(bool reset) -> void {
|
||||
cache.power();
|
||||
|
||||
if constexpr(Accuracy::Recompiler) {
|
||||
auto buffer = ares::Memory::FixedAllocator::get().tryAcquire(64_MiB);
|
||||
recompiler.allocator.resize(64_MiB, bump_allocator::executable | bump_allocator::zero_fill, buffer);
|
||||
if(!reset) {
|
||||
auto buffer = ares::Memory::FixedAllocator::get().tryAcquire(64_MiB);
|
||||
recompiler.allocator.resize(64_MiB, bump_allocator::executable, buffer);
|
||||
}
|
||||
recompiler.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
namespace ares {
|
||||
|
||||
struct SH2 {
|
||||
virtual auto instructionPrologue(u16 instruction) -> void = 0;
|
||||
virtual auto step(u32 clocks) -> void = 0;
|
||||
virtual auto busReadByte(u32 address) -> u32 = 0;
|
||||
virtual auto busReadWord(u32 address) -> u32 = 0;
|
||||
@@ -189,7 +190,7 @@ struct SH2 {
|
||||
|
||||
//disassembler.cpp
|
||||
template<typename... P> auto hint(P&&...) const -> string;
|
||||
auto disassembleInstruction() -> string;
|
||||
auto disassembleInstruction(u16 opcode) -> string;
|
||||
auto disassembleContext() -> string;
|
||||
|
||||
static constexpr u32 undefined = 0;
|
||||
@@ -245,8 +246,8 @@ struct SH2 {
|
||||
};
|
||||
u32 exceptions = 0; //delayed exception flags
|
||||
|
||||
s32 cyclesUntilSync = 0;
|
||||
s32 minCyclesBetweenSyncs = 0;
|
||||
s32 cyclesUntilRecompilerExit = 0;
|
||||
s32 recompilerStepCycles = 0;
|
||||
|
||||
struct Recompiler : recompiler::generic {
|
||||
SH2& self;
|
||||
@@ -299,6 +300,7 @@ struct SH2 {
|
||||
|
||||
static auto mask(u8 address, u8 size) -> u64;
|
||||
|
||||
bool callInstructionPrologue = false;
|
||||
bool inDelaySlot;
|
||||
u32 generation;
|
||||
bump_allocator allocator;
|
||||
|
||||
@@ -137,7 +137,7 @@ template<u32 Origin> auto SH2::writeByte(u32 address, u32 data) -> void {
|
||||
}
|
||||
|
||||
case Area::Uncached: {
|
||||
cyclesUntilSync = 0;
|
||||
cyclesUntilRecompilerExit = 0;
|
||||
return busWriteByte(address & 0x1fff'ffff, data);
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ template<u32 Origin> auto SH2::writeWord(u32 address, u32 data) -> void {
|
||||
}
|
||||
|
||||
case Area::Uncached: {
|
||||
cyclesUntilSync = 0;
|
||||
cyclesUntilRecompilerExit = 0;
|
||||
return busWriteWord(address & 0x1fff'fffe, data);
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@ template<u32 Origin> auto SH2::writeLong(u32 address, u32 data) -> void {
|
||||
}
|
||||
|
||||
case Area::Uncached: {
|
||||
cyclesUntilSync = 0;
|
||||
cyclesUntilRecompilerExit = 0;
|
||||
return busWriteLong(address & 0x1fff'fffc, data);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
auto SH2::INTC::run() -> void {
|
||||
if(self->inDelaySlot()) { self->cyclesUntilSync = 0; return; }
|
||||
if(self->inDelaySlot()) { self->cyclesUntilRecompilerExit = 0; return; }
|
||||
|
||||
if(self->frt.pendingOutputIRQ) {
|
||||
if(self->SR.I < iprb.frtip) {
|
||||
@@ -13,11 +13,9 @@ auto SH2::INTC::run() -> void {
|
||||
if(self->dmac.pendingIRQ & 1) {
|
||||
self->interrupt(ipra.dmacip, self->dmac.vcrdma[0] & 0x7f);
|
||||
self->dmac.pendingIRQ &= ~1;
|
||||
print("dreq0\n");
|
||||
} else if(self->dmac.pendingIRQ & 2) {
|
||||
self->interrupt(ipra.dmacip, self->dmac.vcrdma[1] & 0x7f);
|
||||
self->dmac.pendingIRQ &= ~2;
|
||||
print("dreq1\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ auto V30MZ::interrupt(u8 vector, InterruptSource source) -> bool {
|
||||
//if an IRQ fires during a rep string instruction;
|
||||
//flush prefix queue and seek back to first prefix.
|
||||
//this allows the transfer to resume after the IRQ.
|
||||
if(!prefixes.empty()) {
|
||||
if(repeat()) {
|
||||
PC -= prefixes.size();
|
||||
prefixes.flush();
|
||||
}
|
||||
|
||||
@@ -131,18 +131,20 @@ auto V30MZ::instructionEnter() -> void {
|
||||
auto offset = fetch<Word>();
|
||||
auto length = fetch<Byte>() & 0x1f;
|
||||
push(BP);
|
||||
BP = SP;
|
||||
SP -= offset;
|
||||
u16 bp = SP;
|
||||
|
||||
if(length) {
|
||||
wait(length > 1 ? 7 : 6);
|
||||
for(auto index = 1; index < length; index++) {
|
||||
wait(4);
|
||||
wait(2);
|
||||
auto data = read<Word>(segment(SS), BP - index * 2);
|
||||
push(data);
|
||||
}
|
||||
push(BP);
|
||||
push(bp);
|
||||
}
|
||||
|
||||
BP = bp;
|
||||
SP -= offset;
|
||||
}
|
||||
|
||||
auto V30MZ::instructionLeave() -> void {
|
||||
|
||||
@@ -9,7 +9,7 @@ template<> auto V30MZ::read<Word>(u16 segment, u16 address) -> u32 {
|
||||
u32 data = 0;
|
||||
step(speed(segment * 16 + address));
|
||||
data |= read(segment * 16 + address++) << 0;
|
||||
if(width(address) == Byte || !(address & 1)) step(speed(segment * 16 + address));
|
||||
if(width(segment * 16 + address) == Byte || !(address & 1)) step(speed(segment * 16 + address));
|
||||
data |= read(segment * 16 + address++) << 8;
|
||||
return data;
|
||||
}
|
||||
@@ -18,11 +18,11 @@ template<> auto V30MZ::read<Long>(u16 segment, u16 address) -> u32 {
|
||||
u32 data = 0;
|
||||
step(speed(segment * 16 + address));
|
||||
data |= read(segment * 16 + address++) << 0;
|
||||
if(width(address) == Byte || !(address & 1)) step(speed(segment * 16 + address));
|
||||
if(width(segment * 16 + address) == Byte || !(address & 1)) step(speed(segment * 16 + address));
|
||||
data |= read(segment * 16 + address++) << 8;
|
||||
if(width(address) == Byte || !(address & 1)) step(speed(segment * 16 + address));
|
||||
if(width(segment * 16 + address) == Byte || !(address & 1)) step(speed(segment * 16 + address));
|
||||
data |= read(segment * 16 + address++) << 16;
|
||||
if(width(address) == Byte || !(address & 1)) step(speed(segment * 16 + address));
|
||||
if(width(segment * 16 + address) == Byte || !(address & 1)) step(speed(segment * 16 + address));
|
||||
data |= read(segment * 16 + address++) << 24;
|
||||
return data;
|
||||
}
|
||||
@@ -35,12 +35,12 @@ template<> auto V30MZ::write<Byte>(u16 segment, u16 address, u16 data) -> void {
|
||||
template<> auto V30MZ::write<Word>(u16 segment, u16 address, u16 data) -> void {
|
||||
step(speed(segment * 16 + address));
|
||||
write(segment * 16 + address++, data >> 0);
|
||||
if(width(address) == Byte || !(address & 1)) step(speed(segment * 16 + address));
|
||||
if(width(segment * 16 + address) == Byte || !(address & 1)) step(speed(segment * 16 + address));
|
||||
write(segment * 16 + address++, data >> 8);
|
||||
}
|
||||
|
||||
template<> auto V30MZ::in<Byte>(u16 address) -> u16 {
|
||||
step(1);
|
||||
step(ioSpeed(address));
|
||||
u16 data = 0;
|
||||
data |= in(address++) << 0;
|
||||
return data;
|
||||
@@ -48,22 +48,22 @@ template<> auto V30MZ::in<Byte>(u16 address) -> u16 {
|
||||
|
||||
template<> auto V30MZ::in<Word>(u16 address) -> u16 {
|
||||
u16 data = 0;
|
||||
step(1);
|
||||
step(ioSpeed(address));
|
||||
data |= in(address++) << 0;
|
||||
if(!(address & 1)) step(1);
|
||||
if(ioWidth(address) == Byte || !(address & 1)) step(ioSpeed(address));
|
||||
data |= in(address++) << 8;
|
||||
return data;
|
||||
}
|
||||
|
||||
template<> auto V30MZ::out<Byte>(u16 address, u16 data) -> void {
|
||||
step(1);
|
||||
step(ioSpeed(address));
|
||||
out(address++, data >> 0);
|
||||
}
|
||||
|
||||
template<> auto V30MZ::out<Word>(u16 address, u16 data) -> void {
|
||||
step(1);
|
||||
step(ioSpeed(address));
|
||||
out(address++, data >> 0);
|
||||
if(!(address & 1)) step(1);
|
||||
if(ioWidth(address) == Byte || !(address & 1)) step(ioSpeed(address));
|
||||
out(address++, data >> 8);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@ struct V30MZ {
|
||||
virtual auto write(n20 address, n8 data) -> void = 0;
|
||||
virtual auto in(n16 port) -> n8 = 0;
|
||||
virtual auto out(n16 port, n8 data) -> void = 0;
|
||||
virtual auto ioWidth(n16 port) -> u32 = 0;
|
||||
virtual auto ioSpeed(n16 port) -> n32 = 0;
|
||||
|
||||
//v30mz.cpp
|
||||
auto power() -> void;
|
||||
|
||||
@@ -205,9 +205,10 @@ E S.h = 0x01;
|
||||
auto WDC65816::instructionPullB() -> void {
|
||||
idle();
|
||||
idle();
|
||||
L B = pull();
|
||||
L B = pullN();
|
||||
ZF = B == 0;
|
||||
NF = B & 0x80;
|
||||
E S.h = 0x01;
|
||||
}
|
||||
|
||||
auto WDC65816::instructionPullP() -> void {
|
||||
|
||||
@@ -290,6 +290,7 @@ auto Z80::instructionINDR() -> void { Q = 1;
|
||||
if(!B) return;
|
||||
wait(5);
|
||||
PC -= 2;
|
||||
WZ = PC + 1;
|
||||
XF = PC.bit(11);
|
||||
YF = PC.bit(13);
|
||||
|
||||
@@ -327,6 +328,7 @@ auto Z80::instructionINIR() -> void { Q = 1;
|
||||
if(!B) return;
|
||||
wait(5);
|
||||
PC -= 2;
|
||||
WZ = PC + 1;
|
||||
XF = PC.bit(11);
|
||||
YF = PC.bit(13);
|
||||
|
||||
@@ -516,6 +518,7 @@ auto Z80::instructionOTDR() -> void { Q = 1;
|
||||
if(!B) return;
|
||||
wait(5);
|
||||
PC -= 2;
|
||||
WZ = PC + 1;
|
||||
XF = PC.bit(11);
|
||||
YF = PC.bit(13);
|
||||
|
||||
@@ -537,6 +540,7 @@ auto Z80::instructionOTIR() -> void { Q = 1;
|
||||
if(!B) return;
|
||||
wait(5);
|
||||
PC -= 2;
|
||||
WZ = PC + 1;
|
||||
XF = PC.bit(11);
|
||||
YF = PC.bit(13);
|
||||
|
||||
|
||||
@@ -14,10 +14,7 @@ auto TMS9918::Background::run(n8 hoffset, n8 voffset) -> void {
|
||||
}
|
||||
|
||||
auto TMS9918::Background::text1(n8 hoffset, n8 voffset) -> void {
|
||||
// Offset by 6px and blank the last 10px to centre the display
|
||||
i32 x = hoffset - 6;
|
||||
if (x < 0) return;
|
||||
if (hoffset > 246) return;
|
||||
i32 x = hoffset;
|
||||
|
||||
n14 nameAddress = (voffset.bit(3,7) * 40) + (x / 6);
|
||||
nameAddress.bit(10,13) = io.nameTableAddress;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
auto TMS9918::DAC::setup(n8 y) -> void {
|
||||
output = self.screen->pixels().data() + y * 256;
|
||||
auto TMS9918::DAC::setup(n9 y) -> void {
|
||||
y = (y + 27) % 243;
|
||||
output = self.screen->pixels().data() + y * 284;
|
||||
for (u32 x : range(284)) output[x] = io.colorBackground;
|
||||
}
|
||||
|
||||
auto TMS9918::DAC::run(n8 x, n8 y) -> void {
|
||||
@@ -8,7 +10,7 @@ auto TMS9918::DAC::run(n8 x, n8 y) -> void {
|
||||
if(self.background.output.color) color = self.background.output.color;
|
||||
if(self.sprite.output.color) color = self.sprite.output.color;
|
||||
}
|
||||
output[x] = color;
|
||||
output[(x + 13) % 284] = color;
|
||||
}
|
||||
|
||||
auto TMS9918::DAC::power() -> void {
|
||||
|
||||
@@ -18,11 +18,11 @@ auto TMS9918::unload() -> void {
|
||||
}
|
||||
|
||||
auto TMS9918::main() -> void {
|
||||
dac.setup(io.vcounter);
|
||||
if(io.vcounter < 192) {
|
||||
n8 y = io.vcounter;
|
||||
background.setup(y);
|
||||
sprite.setup(y);
|
||||
dac.setup(y);
|
||||
for(n8 x : range(256)) {
|
||||
background.run(x, y);
|
||||
sprite.run(x, y);
|
||||
|
||||
@@ -101,7 +101,7 @@ protected:
|
||||
TMS9918& self;
|
||||
|
||||
//dac.cpp
|
||||
auto setup(n8 voffset) -> void;
|
||||
auto setup(n9 voffset) -> void;
|
||||
auto run(n8 hoffset, n8 voffset) -> void;
|
||||
auto power() -> void;
|
||||
|
||||
|
||||
@@ -20,10 +20,7 @@ auto V9938::Background::run(n8 x, n8 y) -> void {
|
||||
}
|
||||
|
||||
auto V9938::Background::text1(n8 hoffset, n8 voffset) -> void {
|
||||
// Offset by 6px and blank the last 10px to centre the display
|
||||
i32 x = hoffset - 6;
|
||||
if (x < 0) return;
|
||||
if (hoffset > 246) return;
|
||||
i32 x = hoffset;
|
||||
|
||||
n14 nameAddress = (voffset.bit(3,7) * 40) + (x / 6);
|
||||
nameAddress.bit(10,16) = io.nameTableAddress.bit(10,16);
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
auto V9938::DAC::setup(n8 voffset) -> void {
|
||||
n9 vcenter = voffset + (!self.overscan() ? 10 : 0); //centers output within buffer
|
||||
output = self.screen->pixels().data() + vcenter * 1024;
|
||||
if(self.interlace() && self.field()) output += 512;
|
||||
auto V9938::DAC::setup(n9 voffset) -> void {
|
||||
//TODO: Verify border in overscan mode
|
||||
voffset = (voffset + (self.latch.overscan ? 13 : 27)) % 243;
|
||||
output = self.screen->pixels().data() + voffset * 1136;
|
||||
if(self.interlace() && self.field()) output += 568;
|
||||
for(auto n: range(568)) output[n] = self.pram[self.io.colorBackground];
|
||||
output += 26;
|
||||
}
|
||||
|
||||
auto V9938::DAC::run(n8 hoffset, n8 voffset) -> void {
|
||||
|
||||
@@ -19,12 +19,12 @@ auto V9938::unload() -> void {
|
||||
}
|
||||
|
||||
auto V9938::main() -> void {
|
||||
dac.setup(io.vcounter);
|
||||
if(io.vcounter < vlines()) {
|
||||
n9 y = io.vcounter;
|
||||
latch.vscroll = io.vscroll;
|
||||
background.setup(y);
|
||||
sprite.setup(y + latch.vscroll);
|
||||
dac.setup(y);
|
||||
|
||||
while(io.hcounter < 256) {
|
||||
n9 x = io.hcounter;
|
||||
|
||||
@@ -154,7 +154,7 @@ protected:
|
||||
V9938& self;
|
||||
|
||||
//dac.cpp
|
||||
auto setup(n8 voffset) -> void;
|
||||
auto setup(n9 voffset) -> void;
|
||||
auto run(n8 hoffset, n8 voffset) -> void;
|
||||
auto power() -> void;
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
namespace Board {
|
||||
|
||||
#include "coleco.cpp"
|
||||
#include "xin1.cpp"
|
||||
#include "megacart.cpp"
|
||||
|
||||
auto Interface::load(Memory::Readable<n8>& memory, string name) -> bool {
|
||||
if(auto fp = pak->read(name)) {
|
||||
memory.allocate(fp->size());
|
||||
memory.load(fp);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto Interface::load(Memory::Writable<n8>& memory, string name) -> bool {
|
||||
if(auto fp = pak->read(name)) {
|
||||
memory.allocate(fp->size());
|
||||
memory.load(fp);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto Interface::save(Memory::Writable<n8>& memory, string name) -> bool {
|
||||
if(auto fp = pak->write(name)) {
|
||||
memory.save(fp);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace Board {
|
||||
|
||||
struct Interface {
|
||||
VFS::Pak pak;
|
||||
|
||||
Interface(Cartridge& cartridge) : cartridge(cartridge) {}
|
||||
virtual ~Interface() = default;
|
||||
virtual auto load() -> void {}
|
||||
virtual auto save() -> void {}
|
||||
virtual auto unload() -> void {}
|
||||
virtual auto read(n16 address) -> n8 { return 0; }
|
||||
virtual auto write(n16 address, n8 data) -> void {}
|
||||
virtual auto power() -> void {}
|
||||
virtual auto serialize(serializer&) -> void {}
|
||||
|
||||
auto load(Memory::Readable<n8>&, string name) -> bool;
|
||||
auto load(Memory::Writable<n8>&, string name) -> bool;
|
||||
auto save(Memory::Writable<n8>&, string name) -> bool;
|
||||
|
||||
Cartridge& cartridge;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
struct coleco : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<n8> rom;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(rom, "program.rom");
|
||||
}
|
||||
|
||||
auto save() -> void override {}
|
||||
|
||||
auto unload() -> void override {}
|
||||
|
||||
auto read(n16 address) -> n8 override {
|
||||
return rom.read(address);
|
||||
}
|
||||
|
||||
auto write(n16 address, n8 data) -> void override {
|
||||
return;
|
||||
}
|
||||
|
||||
auto power() -> void override {}
|
||||
|
||||
auto serialize(serializer& s) -> void override {}
|
||||
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
struct megacart : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<n8> rom;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(rom, "program.rom");
|
||||
bankCount = rom.size() >> 14;
|
||||
}
|
||||
|
||||
auto save() -> void override {}
|
||||
|
||||
auto unload() -> void override {}
|
||||
|
||||
auto read(n16 address) -> n8 override {
|
||||
if(address >= 0x7fc0) return bank = address & (bankCount - 1);
|
||||
if(address >= 0x4000) return rom.read((bank << 14) + (address - 0x4000));
|
||||
return rom.read((bankCount << 14) + (address - 0x4000));
|
||||
}
|
||||
|
||||
auto write(n16 address, n8 data) -> void override {
|
||||
return;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
bank = 0;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(bank);
|
||||
s(bankCount);
|
||||
}
|
||||
|
||||
n16 bank;
|
||||
n16 bankCount;
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
struct xin1 : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<n8> rom;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(rom, "program.rom");
|
||||
bankCount = rom.size() / 0x8000;
|
||||
}
|
||||
|
||||
auto save() -> void override {}
|
||||
|
||||
auto unload() -> void override {}
|
||||
|
||||
auto read(n16 address) -> n8 override {
|
||||
if(address >= 0x7fc0) return bank = (address - 0x7fc0) % bankCount;
|
||||
return rom.read(address + (bank * 0x8000));
|
||||
}
|
||||
|
||||
auto write(n16 address, n8 data) -> void override {
|
||||
return;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
bank = bankCount - 1;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(bank);
|
||||
s(bankCount);
|
||||
}
|
||||
|
||||
n16 bank;
|
||||
n16 bankCount;
|
||||
};
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace ares::ColecoVision {
|
||||
|
||||
Cartridge& cartridge = cartridgeSlot.cartridge;
|
||||
#include "board/board.cpp"
|
||||
#include "slot.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
@@ -16,31 +17,43 @@ auto Cartridge::connect() -> void {
|
||||
information = {};
|
||||
information.title = pak->attribute("title");
|
||||
information.region = pak->attribute("region");
|
||||
information.board = pak->attribute("board");
|
||||
|
||||
if(auto fp = pak->read("program.rom")) {
|
||||
rom.allocate(fp->size());
|
||||
rom.load(fp);
|
||||
}
|
||||
|
||||
if(information.board == "xin1") board = new Board::xin1{*this};
|
||||
if(information.board == "coleco") board = new Board::coleco{*this};
|
||||
if(information.board == "megacart") board = new Board::megacart{*this};
|
||||
|
||||
|
||||
if(!board) board = new Board::Interface{*this};
|
||||
board->pak = pak;
|
||||
board->load();
|
||||
power();
|
||||
}
|
||||
|
||||
auto Cartridge::disconnect() -> void {
|
||||
if(!node) return;
|
||||
rom.reset();
|
||||
pak.reset();
|
||||
if(!node || !board) return;
|
||||
board->unload();
|
||||
board->pak.reset();
|
||||
board.reset();
|
||||
node.reset();
|
||||
}
|
||||
|
||||
auto Cartridge::save() -> void {
|
||||
if(!node) return;
|
||||
if(board) board->save();
|
||||
}
|
||||
|
||||
auto Cartridge::power() -> void {
|
||||
if(board) board->power();
|
||||
}
|
||||
|
||||
auto Cartridge::read(n16 address) -> n8 {
|
||||
if(address >= rom.size()) return 0xff;
|
||||
return rom.read(address);
|
||||
if(board) return board->read(address);
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto Cartridge::write(n16 address, n8 data) -> void {
|
||||
if(board) return board->write(address, data);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
struct Cartridge;
|
||||
#include "board/board.hpp"
|
||||
|
||||
struct Cartridge {
|
||||
Node::Peripheral node;
|
||||
VFS::Pak pak;
|
||||
@@ -14,17 +17,19 @@ struct Cartridge {
|
||||
auto power() -> void;
|
||||
|
||||
auto read(n16 address) -> n8;
|
||||
auto write(n16 address, n8 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
private:
|
||||
unique_pointer<Board::Interface> board;
|
||||
|
||||
//private:
|
||||
struct Information {
|
||||
string title;
|
||||
string region;
|
||||
string board;
|
||||
} information;
|
||||
|
||||
Memory::Readable<n8> rom;
|
||||
};
|
||||
|
||||
#include "slot.hpp"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
auto CPU::read(n16 address) -> n8 {
|
||||
n8 data = 0xff;
|
||||
if(auto result = platform->cheat(address)) return *result;
|
||||
if(address >= 0x0000 && address <= 0x1fff && io.replaceBIOS) return expansion.read(address);
|
||||
if(address >= 0x2000 && address <= 0x7fff && io.replaceRAM ) return expansion.read(address);
|
||||
if(address >= 0x0000 && address <= 0x1fff) return system.bios[address & 0x1fff];
|
||||
|
||||
+16
-3
@@ -11,12 +11,12 @@ auto VDP::load(Node::Object parent) -> void {
|
||||
|
||||
node = parent->append<Node::Object>("VDP");
|
||||
|
||||
screen = node->append<Node::Video::Screen>("Screen", 256, 192);
|
||||
screen = node->append<Node::Video::Screen>("Screen", 284, 243);
|
||||
screen->colors(1 << 4, {&VDP::color, this});
|
||||
screen->setSize(256, 192);
|
||||
screen->setSize(284, 243);
|
||||
screen->setScale(1.0, 1.0);
|
||||
screen->setAspect(8.0, 7.0);
|
||||
screen->setViewport(0, 0, 256, 192);
|
||||
screen->setViewport(0, 0, 284, 243);
|
||||
|
||||
TMS9918::load(screen);
|
||||
}
|
||||
@@ -39,6 +39,19 @@ auto VDP::irq(bool line) -> void {
|
||||
}
|
||||
|
||||
auto VDP::frame() -> void {
|
||||
if(screen->overscan()) {
|
||||
screen->setSize(284, 243);
|
||||
screen->setViewport(0, 0, 284, 243);
|
||||
} else {
|
||||
int x = 16;
|
||||
int y = 16;
|
||||
int width = 284 - 32;
|
||||
int height = 243 - 32;
|
||||
|
||||
screen->setSize(width, height);
|
||||
screen->setViewport(x, y, width, height);
|
||||
}
|
||||
|
||||
screen->frame();
|
||||
scheduler.exit(Event::Frame);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ ares.components += mos6502
|
||||
ares.components += ym2149
|
||||
ares.components += ym2413
|
||||
ares.components += m24c
|
||||
ares.components += sst39sf0x0
|
||||
|
||||
ares.objects += ares-fc-system
|
||||
ares.objects += ares-fc-controller
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
struct Action53 : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "ACTION53") return new Action53();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Memory::Readable<n8> programROM;
|
||||
Memory::Readable<n8> characterROM;
|
||||
Memory::Writable<n8> characterRAM;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(programROM, "program.rom");
|
||||
Interface::load(characterROM, "character.rom");
|
||||
Interface::load(characterRAM, "character.ram");
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
Interface::save(characterRAM, "character.ram");
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
if(address < 0x8000) return data;
|
||||
|
||||
n8 programBankMask = (1 << programBankWidth) - 1;
|
||||
if (programBankMode < 2) {
|
||||
n8 outerBank = programOuterBank & ~programBankMask;
|
||||
n8 innerBank = programBank & programBankMask;
|
||||
return programROM.read((outerBank | innerBank) << 15 | (n15)address);
|
||||
}
|
||||
|
||||
n9 outerBank = (programOuterBank & ~programBankMask) << 1;
|
||||
n9 innerBank = programBank & ((2 << programBankWidth) - 1);
|
||||
n9 offset = (address >> 14) & 1;
|
||||
if(offset == (programBankMode & 1))
|
||||
return programROM.read((programOuterBank << 15) | (n15)address);
|
||||
return programROM.read((outerBank | innerBank) << 14 | (n14)address);
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
// Register select
|
||||
if((address & 0xF000) == 0x5000) {
|
||||
currentRegister = data & 0x81;
|
||||
return;
|
||||
}
|
||||
|
||||
// Register write
|
||||
if(address < 0x8000) return;
|
||||
switch(currentRegister) {
|
||||
case 0x00: { /* CHR bank */
|
||||
characterBank = data.bit(0, 1);
|
||||
if(!mirror) nametableBank = data.bit(4);
|
||||
} break;
|
||||
case 0x01: { /* PRG bank */
|
||||
programBank = data.bit(0, 3);
|
||||
if(!mirror) nametableBank = data.bit(4);
|
||||
} break;
|
||||
case 0x80: { /* Mode */
|
||||
nametableBank = data.bit(0);
|
||||
mirror = data.bit(1);
|
||||
programBankMode = data.bit(2, 3);
|
||||
programBankWidth = data.bit(4, 5);
|
||||
} break;
|
||||
case 0x81: { /* PRG outer bank */
|
||||
programOuterBank = data;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
inline auto addressCIRAM(n32 address) -> n11 {
|
||||
if(mirror) {
|
||||
return ((address >> nametableBank) & 0x0400) | (n10)address;
|
||||
}
|
||||
return (nametableBank << 10) | (n10)address;
|
||||
}
|
||||
|
||||
auto readCHR(n32 address, n8 data) -> n8 override {
|
||||
if(address & 0x2000) {
|
||||
return ppu.readCIRAM(addressCIRAM(address));
|
||||
}
|
||||
if(characterROM) return characterROM.read(characterBank << 13 | (n13)address);
|
||||
if(characterRAM) return characterRAM.read(characterBank << 13 | (n13)address);
|
||||
return data;
|
||||
}
|
||||
|
||||
auto writeCHR(n32 address, n8 data) -> void override {
|
||||
if(address & 0x2000) {
|
||||
return ppu.writeCIRAM(addressCIRAM(address), data);
|
||||
}
|
||||
if(characterRAM) return characterRAM.write(address, data);
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
currentRegister = 0;
|
||||
characterBank = 0;
|
||||
programBank = 0;
|
||||
programOuterBank = 0xFF;
|
||||
nametableBank = 0;
|
||||
mirror = 0;
|
||||
programBankMode = 0;
|
||||
programBankWidth = 0;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(characterRAM);
|
||||
s(currentRegister);
|
||||
s(characterBank);
|
||||
s(programBank);
|
||||
s(programOuterBank);
|
||||
s(nametableBank);
|
||||
s(mirror);
|
||||
s(programBankMode);
|
||||
s(programBankWidth);
|
||||
}
|
||||
|
||||
n8 currentRegister;
|
||||
n2 characterBank;
|
||||
n4 programBank;
|
||||
n8 programOuterBank;
|
||||
n1 nametableBank;
|
||||
n1 mirror;
|
||||
n2 programBankMode;
|
||||
n2 programBankWidth;
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
namespace Board {
|
||||
|
||||
#include "action53.cpp"
|
||||
#include "ave-nina-001.cpp"
|
||||
#include "ave-nina-06.cpp"
|
||||
#include "bandai-74161.cpp"
|
||||
@@ -34,10 +35,12 @@ namespace Board {
|
||||
#include "hvc-sxrom.cpp"
|
||||
#include "hvc-txrom.cpp"
|
||||
#include "hvc-uxrom.cpp"
|
||||
#include "inl-nsf.cpp"
|
||||
#include "irem-g101.cpp"
|
||||
#include "irem-h3001.cpp"
|
||||
#include "irem-lrog017.cpp"
|
||||
#include "irem-tam-s1.cpp"
|
||||
#include "magicfloor.cpp"
|
||||
#include "mlt-action52.cpp"
|
||||
#include "namco-118.cpp"
|
||||
#include "namco-163.cpp"
|
||||
@@ -51,9 +54,11 @@ namespace Board {
|
||||
#include "taito-tc0690.cpp"
|
||||
#include "taito-x1-005.cpp"
|
||||
#include "taito-x1-017.cpp"
|
||||
#include "unrom-512.cpp"
|
||||
|
||||
auto Interface::create(string board) -> Interface* {
|
||||
Interface* p = nullptr;
|
||||
if(!p) p = Action53::create(board);
|
||||
if(!p) p = AveNina001::create(board);
|
||||
if(!p) p = AveNina06::create(board);
|
||||
if(!p) p = Bandai74161::create(board);
|
||||
@@ -76,6 +81,7 @@ auto Interface::create(string board) -> Interface* {
|
||||
if(!p) p = HVC_SxROM::create(board);
|
||||
if(!p) p = HVC_TxROM::create(board);
|
||||
if(!p) p = HVC_UxROM::create(board);
|
||||
if(!p) p = INL_NSF::create(board);
|
||||
if(!p) p = IremG101::create(board);
|
||||
if(!p) p = IremH3001::create(board);
|
||||
if(!p) p = IremLROG017::create(board);
|
||||
@@ -92,6 +98,7 @@ auto Interface::create(string board) -> Interface* {
|
||||
if(!p) p = KonamiVRC5::create(board);
|
||||
if(!p) p = KonamiVRC6::create(board);
|
||||
if(!p) p = KonamiVRC7::create(board);
|
||||
if(!p) p = MagicFloor::create(board);
|
||||
if(!p) p = MltAction52::create(board);
|
||||
if(!p) p = Namco118::create(board);
|
||||
if(!p) p = Namco163::create(board);
|
||||
@@ -105,6 +112,7 @@ auto Interface::create(string board) -> Interface* {
|
||||
if(!p) p = TaitoTC0690::create(board);
|
||||
if(!p) p = TaitoX1005::create(board);
|
||||
if(!p) p = TaitoX1017::create(board);
|
||||
if(!p) p = UNROM512::create(board);
|
||||
if(!p) p = new Interface;
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -1,26 +1,30 @@
|
||||
struct GTROM : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "GTROM" ) return new GTROM();
|
||||
if(id == "GTROM") return new GTROM();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Memory::Readable<n8> programROM;
|
||||
SST39SF0x0 flash;
|
||||
Memory::Writable<n8> characterRAM;
|
||||
Memory::Writable<n8> videoRAM;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(programROM, "program.rom");
|
||||
Interface::load(flash.flash, "program.flash");
|
||||
Interface::load(characterRAM, "character.ram");
|
||||
videoRAM.allocate(16_KiB);
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
if(auto fp = pak->write("program.flash")) {
|
||||
flash.flash.save(fp);
|
||||
}
|
||||
|
||||
Interface::save(characterRAM, "character.ram");
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
if(address < 0x8000) return data;
|
||||
return programROM.read(programBank << 15 | (n15)address);
|
||||
return flash.read(programBank << 15 | (n15)address);
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
@@ -30,6 +34,8 @@ struct GTROM : Interface {
|
||||
videoBank = data.bit(5);
|
||||
return;
|
||||
}
|
||||
if(address < 0x8000) return;
|
||||
flash.write(programBank << 15 | (n15)address, data);
|
||||
}
|
||||
|
||||
auto readCHR(n32 address, n8 data) -> n8 override {
|
||||
|
||||
@@ -200,7 +200,7 @@ struct HVC_ExROM : Interface { //MMC5
|
||||
address &= 0x7fff;
|
||||
} else if(programMode == 1) {
|
||||
if((address & 0xc000) == 0x8000) bank = programBank[1] & ~1;
|
||||
if((address & 0xe000) == 0xc000) bank = programBank[3] & ~1;
|
||||
if((address & 0xc000) == 0xc000) bank = programBank[3] & ~1;
|
||||
address &= 0x3fff;
|
||||
} else if(programMode == 2) {
|
||||
if((address & 0xe000) == 0x8000) bank = programBank[1] & ~1 | 0;
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
struct INL_NSF : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "INL-NSF") return new INL_NSF;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Memory::Readable<n8> programROM;
|
||||
Memory::Readable<n8> characterROM;
|
||||
Memory::Writable<n8> characterRAM;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(programROM, "program.rom");
|
||||
Interface::load(characterROM, "character.rom");
|
||||
Interface::load(characterRAM, "character.ram");
|
||||
mirror = pak->attribute("mirror") == "vertical";
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
Interface::save(characterRAM, "character.ram");
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
if(address < 0x8000) return data;
|
||||
return programROM.read((banks[(address >> 12) & 7] << 12) | (n12)address);
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if((address & 0xF000) != 0x5000) return;
|
||||
banks[address & 7] = data;
|
||||
}
|
||||
|
||||
auto readCHR(n32 address, n8 data) -> n8 override {
|
||||
if(address & 0x2000) {
|
||||
address = address >> !mirror & 0x0400 | (n10)address;
|
||||
return ppu.readCIRAM(address);
|
||||
}
|
||||
if(characterROM) return characterROM.read(address);
|
||||
if(characterRAM) return characterRAM.read(address);
|
||||
return data;
|
||||
}
|
||||
|
||||
auto writeCHR(n32 address, n8 data) -> void override {
|
||||
if(address & 0x2000) {
|
||||
address = address >> !mirror & 0x0400 | (n10)address;
|
||||
return ppu.writeCIRAM(address, data);
|
||||
}
|
||||
if(characterRAM) return characterRAM.write(address, data);
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
banks[7] = 0xFF;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(characterRAM);
|
||||
s(mirror);
|
||||
s(banks);
|
||||
}
|
||||
|
||||
n1 mirror; //0 = horizontal, 1 = vertical
|
||||
n8 banks[8];
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
struct MagicFloor : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "MAGICFLOOR") return new MagicFloor();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Memory::Readable<n8> programROM;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(programROM, "program.rom");
|
||||
pin = (n2)(pak->attribute("pinout/va10").natural() - 10);
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
if(address < 0x8000) return data;
|
||||
return programROM.read((n15)address);
|
||||
}
|
||||
|
||||
auto readCHR(n32 address, n8 data) -> n8 override {
|
||||
return ppu.readCIRAM(((address >> pin) & 0x400) | (n10)address);
|
||||
}
|
||||
|
||||
auto writeCHR(n32 address, n8 data) -> void override {
|
||||
return ppu.writeCIRAM(((address >> pin) & 0x400) | (n10)address, data);
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(pin);
|
||||
}
|
||||
|
||||
n2 pin;
|
||||
};
|
||||
Loaded 100 of 1295 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user