mirror of
https://github.com/ares-emulator/ares.git
synced 2026-11-09 01:09:46 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
443aabb89b | ||
|
|
93275dba2c | ||
|
|
3767568cdb | ||
|
|
7cfb150be8 | ||
|
|
049211abae | ||
|
|
a7ce0c22fa | ||
|
|
7205a39f23 | ||
|
|
99b759b630 | ||
|
|
a9ced1175b | ||
|
|
aeb5bc4597 | ||
|
|
f031407a14 | ||
|
|
3c42e12b75 | ||
|
|
2ce9d210af | ||
|
|
a038b2cf61 | ||
|
|
cee2ea332f | ||
|
|
b1e3d90790 | ||
|
|
f306d1223c | ||
|
|
e90e6ab846 | ||
|
|
6a103ed073 | ||
|
|
96663687f2 | ||
|
|
8d17ae1697 | ||
|
|
a2e4807810 | ||
|
|
803daf7642 | ||
|
|
c88fb1155b | ||
|
|
246a5eee93 | ||
|
|
4607b40e23 | ||
|
|
d0a963ab42 | ||
|
|
bb58366efd | ||
|
|
0891a102e4 | ||
|
|
0e74d5c8e3 | ||
|
|
672940cf06 | ||
|
|
9ecfbdbe76 | ||
|
|
679dbaccc9 | ||
|
|
57b4d68298 | ||
|
|
5d55f2b8e3 | ||
|
|
f094529d6d | ||
|
|
c2735c5c1c | ||
|
|
e1f5ab8333 | ||
|
|
cf0ef61c1f | ||
|
|
2b9907c8d0 | ||
|
|
2abe2286b2 | ||
|
|
b6d8be087f | ||
|
|
49892159d5 | ||
|
|
e88e2cf032 | ||
|
|
bcf9cab22d | ||
|
|
2973fa71e5 | ||
|
|
16f86afd9f | ||
|
|
329a8c958b | ||
|
|
91f3dfaf3d | ||
|
|
daa8d82f7b | ||
|
|
fb98870d56 | ||
|
|
07183d56f4 | ||
|
|
232cb6fcf6 | ||
|
|
320779a845 | ||
|
|
86e962bc13 | ||
|
|
0cafaad19b | ||
|
|
043b4ffd37 | ||
|
|
6174fa384e | ||
|
|
e5d1f2dfea | ||
|
|
54f6ea31f5 | ||
|
|
1d26512309 | ||
|
|
43f1e524e0 | ||
|
|
402c8acc5f | ||
|
|
2451fec704 | ||
|
|
8a70118446 | ||
|
|
fa017c26a5 | ||
|
|
670920a5a4 | ||
|
|
82364646d3 | ||
|
|
40220a6ba1 | ||
|
|
96a6f98862 | ||
|
|
3423bcf65e | ||
|
|
a74ef69d86 | ||
|
|
177475f053 | ||
|
|
32ba2fe676 | ||
|
|
3aa1b3b738 | ||
|
|
abf4c58ce6 | ||
|
|
6b0a52fcb6 | ||
|
|
c7e59d13fa | ||
|
|
ea2b6886b5 | ||
|
|
80e651741c | ||
|
|
6b70ae6d69 | ||
|
|
a3a777ef93 | ||
|
|
abaf7a82f3 | ||
|
|
0bf24b3161 | ||
|
|
a527ed6d1d | ||
|
|
cf0582f607 | ||
|
|
02942aca6b | ||
|
|
aae3caab38 | ||
|
|
2a71b978be | ||
|
|
336af4c219 | ||
|
|
1afbf893d7 | ||
|
|
6bcab5e7ed | ||
|
|
5e4e64dbd7 | ||
|
|
728db642f0 | ||
|
|
e1f1cbbb4b | ||
|
|
b7f96b7109 | ||
|
|
32c15726ae | ||
|
|
6160a81368 | ||
|
|
523b6ce5cf | ||
|
|
95e407aae1 | ||
|
|
5e1d6d1aab | ||
|
|
9f0f4b3432 | ||
|
|
f4a38d121d | ||
|
|
abf454cecb | ||
|
|
6c40a5b4c6 | ||
|
|
56458d5ba5 | ||
|
|
8d04515b5f | ||
|
|
f66c93c0c2 | ||
|
|
f4c9c11596 | ||
|
|
906191ac45 | ||
|
|
a0c063cabe | ||
|
|
ebf41cb696 | ||
|
|
c0dfad661b | ||
|
|
0b80107cdf | ||
|
|
f7eb4483b2 | ||
|
|
9178761932 | ||
|
|
235a04d54d | ||
|
|
75c0a5f36e | ||
|
|
6de4810ab7 | ||
|
|
1a5ab89cd4 | ||
|
|
4a80dbabcb | ||
|
|
6399a5cd15 | ||
|
|
acd2130a4d | ||
|
|
b3b32ef026 | ||
|
|
4ce8290e49 | ||
|
|
fd6698c5f6 | ||
|
|
f92605cfbd | ||
|
|
2c3c9fe25f | ||
|
|
18e013beda | ||
|
|
1e26a8be76 | ||
|
|
300fa4801f | ||
|
|
c5106a1982 | ||
|
|
1d22db7e1c | ||
|
|
c986a14401 | ||
|
|
a6d3a57df9 | ||
|
|
8d04df1641 | ||
|
|
6baebd0f06 | ||
|
|
e34bfc04e3 | ||
|
|
0d7c70bdba | ||
|
|
fccd73f8e3 | ||
|
|
c52c7fd1bb | ||
|
|
5210341ad3 | ||
|
|
d7e456a46f | ||
|
|
a3b126f079 | ||
|
|
bc8c308ee9 | ||
|
|
124a678c8e | ||
|
|
333dd85ba5 | ||
|
|
d08a9bdcc2 | ||
|
|
f4a018036d | ||
|
|
65f4e176c2 | ||
|
|
3b8e9bbff5 | ||
|
|
8f2c6eff9c | ||
|
|
85a5292648 | ||
|
|
c8f261201c | ||
|
|
e42c79677d | ||
|
|
de9ec5d83c | ||
|
|
93e89446e2 | ||
|
|
2a28c46a41 | ||
|
|
23fa258dfd | ||
|
|
e86a18f293 | ||
|
|
665229a892 | ||
|
|
4d46be8344 | ||
|
|
5a839f1010 | ||
|
|
d5dcc645e4 | ||
|
|
35f6a93713 | ||
|
|
d01b8db441 | ||
|
|
a8373002b5 | ||
|
|
5d192fb215 | ||
|
|
e13722870e | ||
|
|
82e49616a2 | ||
|
|
7d425dc5e7 | ||
|
|
aa975f8f6c | ||
|
|
7fcd6d1f5a | ||
|
|
9d7c83aa39 | ||
|
|
1d8aa3fc3f | ||
|
|
7692f102e5 | ||
|
|
3be1ced3ef | ||
|
|
c78d931e28 | ||
|
|
e7f24c06d5 | ||
|
|
dc5ff13b5c | ||
|
|
7140b64b13 | ||
|
|
4d4d3308b9 | ||
|
|
e02a7cc6fb | ||
|
|
ce981795f5 | ||
|
|
51d16a58cd | ||
|
|
89287dabf1 | ||
|
|
5b52f94aa1 | ||
|
|
f67373732c | ||
|
|
8d43e67b96 | ||
|
|
8a37b00fef | ||
|
|
80c2678930 | ||
|
|
510cc55d76 | ||
|
|
5c285e3ce3 | ||
|
|
21203b2401 | ||
|
|
2c8f3ef84d | ||
|
|
80348ae691 | ||
|
|
5e55f1a8a7 | ||
|
|
c63257029e | ||
|
|
93d44072da | ||
|
|
2cf921be41 | ||
|
|
cc8ccbf1bd | ||
|
|
10e589672f | ||
|
|
3c28ecd8b6 | ||
|
|
cc64eb50c0 | ||
|
|
5cb877b49c | ||
|
|
275ba735de | ||
|
|
2b894add40 | ||
|
|
58690cd5fc | ||
|
|
b03da384d8 | ||
|
|
f382a18d10 | ||
|
|
f63cad6de9 | ||
|
|
34d2c091c7 | ||
|
|
d845582d4a | ||
|
|
9788752bc1 | ||
|
|
5f60ab2dde | ||
|
|
628432e75c | ||
|
|
980e9ed687 | ||
|
|
e98a313ed6 | ||
|
|
90d42d1ea3 | ||
|
|
11d6b7c673 | ||
|
|
c04bcf03e0 | ||
|
|
c4d73cc427 | ||
|
|
8634c11cff | ||
|
|
8a5c48e3ed | ||
|
|
b22513a6e8 | ||
|
|
e5220e8fd0 | ||
|
|
8099bf54fd | ||
|
|
bed4ce3659 | ||
|
|
f630a9418d | ||
|
|
f9e4070b15 | ||
|
|
5050d6c45d | ||
|
|
b56dac15e9 | ||
|
|
a8e35d0299 | ||
|
|
1462bac4c5 | ||
|
|
5e2a8f08bf | ||
|
|
e88cb48f19 | ||
|
|
70a669287b | ||
|
|
9d3addc241 | ||
|
|
f22e1405dc | ||
|
|
4542ac5674 | ||
|
|
5c3ac88547 | ||
|
|
85fcda5b42 | ||
|
|
f512fdc969 | ||
|
|
64286fbd46 | ||
|
|
e0c4cc76f2 | ||
|
|
7b4a346336 | ||
|
|
763f7da939 | ||
|
|
ee188dcb31 | ||
|
|
1f8b3f86b9 | ||
|
|
18e334a6cf | ||
|
|
24675b0161 | ||
|
|
f4caedcd5e | ||
|
|
bdc6313e46 | ||
|
|
fd3a56b0a2 | ||
|
|
663b6951e3 | ||
|
|
554430276c | ||
|
|
3748baf8e8 | ||
|
|
e1bd1bfdf9 | ||
|
|
5f6b75a013 | ||
|
|
2d6fe4aa16 | ||
|
|
b49e5496da | ||
|
|
645d481493 | ||
|
|
bc14c35cb5 | ||
|
|
16210cf1bf | ||
|
|
d34e54c808 | ||
|
|
76508d02bd | ||
|
|
32690bb6f0 | ||
|
|
9eca885d8b | ||
|
|
f9a53d2167 | ||
|
|
fd5b22c5cd | ||
|
|
2f6edc7efe | ||
|
|
12ac1b5534 | ||
|
|
c8fa67ec39 | ||
|
|
5341d0d7d4 | ||
|
|
ad0105de25 | ||
|
|
e3663a9eec | ||
|
|
a01ccb72f2 | ||
|
|
27996d81d4 | ||
|
|
47af07c507 | ||
|
|
cc6ab702c0 | ||
|
|
d633b92a57 | ||
|
|
579be2bb04 | ||
|
|
89db771c73 | ||
|
|
33b95510cb | ||
|
|
31eee5f6f1 | ||
|
|
7ba1c2925d | ||
|
|
dbc1b4ccc6 | ||
|
|
a3a1969775 | ||
|
|
ab67ec12cd | ||
|
|
02cb1268e4 | ||
|
|
50994a2c60 | ||
|
|
9e099e9b15 | ||
|
|
16cbd8b149 | ||
|
|
5491a6a9c8 | ||
|
|
d0a14c682d | ||
|
|
4eb4b6cdb7 | ||
|
|
9bc59159d0 | ||
|
|
f518f04ffa | ||
|
|
2ce06bc4d1 | ||
|
|
a3285b6959 | ||
|
|
8a7ac00d03 | ||
|
|
38cf1b0f8e | ||
|
|
d26ffccc9f | ||
|
|
9ee4afd17f | ||
|
|
61dbb64999 | ||
|
|
2d5e1ba552 | ||
|
|
a31b0a7768 | ||
|
|
7c9ff4ba6d | ||
|
|
0bc30dd26d | ||
|
|
f73032e17f | ||
|
|
2eee9bcd4e | ||
|
|
e3a3927e95 | ||
|
|
f04e55f863 | ||
|
|
08aecccc5a | ||
|
|
3471756c06 | ||
|
|
d40e6e1cd3 | ||
|
|
6a262e01ae | ||
|
|
2f6a51b3ef | ||
|
|
ee3b764b8e | ||
|
|
4216d5ae0a | ||
|
|
878efe0a05 | ||
|
|
98aee19e93 | ||
|
|
7810a05b60 | ||
|
|
b83e868a6e | ||
|
|
ce3153b10e | ||
|
|
259642ae2a | ||
|
|
30921061c7 | ||
|
|
757da94463 | ||
|
|
1c026f5edf | ||
|
|
2d90b6343d | ||
|
|
4c312ab2af | ||
|
|
55d197fdc3 | ||
|
|
53290b9c71 | ||
|
|
764a881d36 | ||
|
|
a10e089100 | ||
|
|
590ec4cb32 | ||
|
|
30d143092d | ||
|
|
2dbd5a43a0 | ||
|
|
08c013dc46 | ||
|
|
0d169fe641 |
No files matched your search
+34
-10
@@ -11,27 +11,51 @@ jobs:
|
||||
matrix:
|
||||
program:
|
||||
- ares
|
||||
os:
|
||||
platform:
|
||||
- name: windows
|
||||
version: latest
|
||||
os: windows-latest
|
||||
compiler: g++
|
||||
shell: 'msys2 {0}'
|
||||
msystem: mingw64
|
||||
msys-env: x86_64
|
||||
- name: windows-clang
|
||||
os: windows-latest
|
||||
compiler: clang++
|
||||
shell: 'msys2 {0}'
|
||||
msystem: clang64
|
||||
msys-env: clang-x86_64
|
||||
- name: macos
|
||||
version: latest
|
||||
os: macos-latest
|
||||
compiler: clang++
|
||||
shell: sh
|
||||
- name: ubuntu
|
||||
version: latest
|
||||
runs-on: ${{ matrix.os.name }}-${{ matrix.os.version }}
|
||||
os: ubuntu-latest
|
||||
compiler: g++
|
||||
shell: sh
|
||||
name: ${{ matrix.program }}-${{ matrix.platform.name }}
|
||||
runs-on: ${{ matrix.platform.os }}
|
||||
defaults:
|
||||
run:
|
||||
shell: ${{ matrix.platform.shell }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Dependencies
|
||||
if: matrix.os.name == 'ubuntu'
|
||||
- name: Install MSYS2 Dependencies
|
||||
if: matrix.platform.shell == 'msys2 {0}'
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: ${{ matrix.platform.msystem }}
|
||||
install: make mingw-w64-${{ matrix.platform.msys-env }}-toolchain
|
||||
- name: Install Linux Dependencies
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt-get update -y -qq
|
||||
sudo apt-get install libsdl2-dev libgtksourceview2.0-dev libgtk2.0-dev libao-dev libopenal-dev
|
||||
- uses: actions/checkout@v2
|
||||
- name: Make
|
||||
run: make -j4 -C desktop-ui build=optimized local=false lto=true
|
||||
run: make -j4 -C desktop-ui build=optimized local=false lto=true compiler=${{ matrix.platform.compiler }}
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ matrix.program }}-${{ matrix.os.name }}
|
||||
name: ${{ matrix.program }}-${{ matrix.platform.name }}
|
||||
path: desktop-ui/out/*
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
name: 'Split Subtrees'
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- '*'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}
|
||||
jobs:
|
||||
packages_split:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
package:
|
||||
- nall
|
||||
- hiro
|
||||
- ruby
|
||||
- libco
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- if: "!startsWith(github.ref, 'refs/tags/')"
|
||||
uses: "symplify/[email protected]"
|
||||
with:
|
||||
package_directory: '${{ matrix.package }}'
|
||||
repository_organization: 'ares-emulator'
|
||||
repository_name: '${{ matrix.package }}'
|
||||
user_name: "Luke Usher"
|
||||
user_email: "[email protected]"
|
||||
@@ -0,0 +1,2 @@
|
||||
obj/
|
||||
out/
|
||||
@@ -0,0 +1,6 @@
|
||||
all: desktop
|
||||
|
||||
desktop:
|
||||
$(MAKE) -C desktop-ui
|
||||
|
||||
.PHONY: all desktop
|
||||
@@ -1,3 +1,4 @@
|
||||
----------------------------------------------------------------------
|
||||
ares
|
||||
|
||||
Copyright (c) 2004-2021 ares team, Near et al
|
||||
@@ -13,3 +14,135 @@ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
----------------------------------------------------------------------
|
||||
|
||||
----------------------------------------------------------------------
|
||||
Stack-less Just-In-Time compiler
|
||||
|
||||
Copyright Zoltan Herczeg ([email protected]). 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.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) 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(S) 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.
|
||||
----------------------------------------------------------------------
|
||||
|
||||
----------------------------------------------------------------------
|
||||
MAME
|
||||
Copyright (c) 1997-2021 MAMEdev and contributors
|
||||
|
||||
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.
|
||||
----------------------------------------------------------------------
|
||||
|
||||
----------------------------------------------------------------------
|
||||
libchdr
|
||||
|
||||
Copyright Romain Tisserand
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the <organization> 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 <COPYRIGHT HOLDER> 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
|
||||
----------------------------------------------------------------------
|
||||
|
||||
----------------------------------------------------------------------
|
||||
LZMA SDK is placed in the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute
|
||||
the original LZMA SDK code, either in source code form or as a compiled binary,
|
||||
for any purpose, commercial or non-commercial, and by any means.
|
||||
----------------------------------------------------------------------
|
||||
|
||||
----------------------------------------------------------------------
|
||||
zlib
|
||||
|
||||
(C) 1995-2017 Jean-loup Gailly and Mark Adler
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
Jean-loup Gailly Mark Adler
|
||||
[email protected] [email protected]
|
||||
|
||||
If you use the zlib library in a product, we would appreciate *not* receiving
|
||||
lengthy legal documents to sign. The sources are provided for free but without
|
||||
warranty of any kind. The library has been entirely written by Jean-loup
|
||||
Gailly and Mark Adler; it does not include third-party code.
|
||||
|
||||
If you redistribute modified sources, we would appreciate that you include in
|
||||
the file ChangeLog history information documenting your changes. Please read
|
||||
the FAQ for more information on the distribution of modified source versions.
|
||||
----------------------------------------------------------------------
|
||||
@@ -1,4 +1,4 @@
|
||||
# ares
|
||||

|
||||
|
||||
[](https://github.com/higan-emu/ares/blob/master/LICENSE)
|
||||
|
||||
@@ -9,7 +9,7 @@ Official Releases
|
||||
-----------------
|
||||
|
||||
Official releases are available from
|
||||
[the ares website](https://ares-emulator.github.io).
|
||||
[the ares website](https://ares-emu.net).
|
||||
|
||||
Nightly Builds
|
||||
--------------
|
||||
|
||||
@@ -27,6 +27,10 @@ ifeq ($(profile),performance)
|
||||
flags += -DPROFILE_PERFORMANCE
|
||||
endif
|
||||
|
||||
ifneq ($(filter $(cores),a26),)
|
||||
include $(ares.path)/a26/GNUmakefile
|
||||
endif
|
||||
|
||||
ifneq ($(filter $(cores),fc),)
|
||||
include $(ares.path)/fc/GNUmakefile
|
||||
endif
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
ares.components += mos6502
|
||||
|
||||
ares.objects += ares-a26-system
|
||||
ares.objects += ares-a26-controller
|
||||
ares.objects += ares-a26-cartridge
|
||||
ares.objects += ares-a26-cpu
|
||||
ares.objects += ares-a26-tia
|
||||
ares.objects += ares-a26-riot
|
||||
|
||||
$(object.path)/ares-a26-system.o: $(ares.path)/a26/system/system.cpp
|
||||
$(object.path)/ares-a26-controller.o: $(ares.path)/a26/controller/controller.cpp
|
||||
$(object.path)/ares-a26-cartridge.o: $(ares.path)/a26/cartridge/cartridge.cpp
|
||||
$(object.path)/ares-a26-cpu.o: $(ares.path)/a26/cpu/cpu.cpp
|
||||
$(object.path)/ares-a26-tia.o: $(ares.path)/a26/tia/tia.cpp
|
||||
$(object.path)/ares-a26-riot.o: $(ares.path)/a26/riot/riot.cpp
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <ares/ares.hpp>
|
||||
|
||||
#include <component/processor/mos6502/mos6502.hpp>
|
||||
|
||||
namespace ares::Atari2600 {
|
||||
#include <ares/inline.hpp>
|
||||
auto enumerate() -> vector<string>;
|
||||
auto load(Node::System& node, string name) -> bool;
|
||||
|
||||
struct Region {
|
||||
static inline auto NTSC() -> bool;
|
||||
static inline auto PAL() -> bool;
|
||||
static inline auto SECAM() -> bool;
|
||||
};
|
||||
|
||||
#include <a26/controller/controller.hpp>
|
||||
#include <a26/system/system.hpp>
|
||||
#include <a26/cartridge/cartridge.hpp>
|
||||
#include <a26/cpu/cpu.hpp>
|
||||
#include <a26/tia/tia.hpp>
|
||||
#include <a26/riot/riot.hpp>
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
struct Atari16k : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<n8> rom;
|
||||
Memory::Writable<n8> ram;
|
||||
n2 bank;
|
||||
bool hasRam;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(rom, "program.rom");
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(n16 address) -> n8 override {
|
||||
if(address == 0x1ff6) bank = 0;
|
||||
if(address == 0x1ff7) bank = 1;
|
||||
if(address == 0x1ff8) bank = 2;
|
||||
if(address == 0x1ff9) bank = 3;
|
||||
|
||||
if(address.bit(12)) {
|
||||
if(hasRam && address >= 0x1080 && address <= 0x10ff) return ram.read(address & 0x7f);
|
||||
return rom.read((bank * 0x1000) + (address & 0xfff));
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto write(n16 address, n8 data) -> bool override {
|
||||
if(address == 0x1ff6) bank = 0;
|
||||
if(address == 0x1ff7) bank = 1;
|
||||
if(address == 0x1ff8) bank = 2;
|
||||
if(address == 0x1ff9) bank = 3;
|
||||
|
||||
if(address >= 0x1000 && address <= 0x107f) {
|
||||
hasRam = true;
|
||||
ram.write(address & 0x7f, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
bank = 0;
|
||||
hasRam = 0;
|
||||
ram.allocate(128);
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(bank);
|
||||
s(hasRam);
|
||||
s(ram);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
struct Atari32k : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<n8> rom;
|
||||
Memory::Writable<n8> ram;
|
||||
n3 bank;
|
||||
bool hasRam;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(rom, "program.rom");
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(n16 address) -> n8 override {
|
||||
if(address == 0x1ff4) bank = 0;
|
||||
if(address == 0x1ff5) bank = 1;
|
||||
if(address == 0x1ff6) bank = 2;
|
||||
if(address == 0x1ff7) bank = 3;
|
||||
if(address == 0x1ff8) bank = 4;
|
||||
if(address == 0x1ff9) bank = 5;
|
||||
if(address == 0x1ffa) bank = 6;
|
||||
if(address == 0x1ffb) bank = 7;
|
||||
|
||||
if(address.bit(12)) {
|
||||
if(hasRam && address >= 0x1080 && address <= 0x10ff) return ram.read(address & 0x7f);
|
||||
return rom.read((bank * 0x1000) + (address & 0xfff));
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto write(n16 address, n8 data) -> bool override {
|
||||
if(address == 0x1ff4) bank = 0;
|
||||
if(address == 0x1ff5) bank = 1;
|
||||
if(address == 0x1ff6) bank = 2;
|
||||
if(address == 0x1ff7) bank = 3;
|
||||
if(address == 0x1ff8) bank = 4;
|
||||
if(address == 0x1ff9) bank = 5;
|
||||
if(address == 0x1ffa) bank = 6;
|
||||
if(address == 0x1ffb) bank = 7;
|
||||
|
||||
if(address >= 0x1000 && address <= 0x107f) {
|
||||
hasRam = true;
|
||||
ram.write(address & 0x7f, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
bank = 0;
|
||||
hasRam = 0;
|
||||
ram.allocate(128);
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(bank);
|
||||
s(hasRam);
|
||||
s(ram);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
struct Atari8k : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<n8> rom;
|
||||
Memory::Writable<n8> ram;
|
||||
n1 bank;
|
||||
bool hasRam;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(rom, "program.rom");
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(n16 address) -> n8 override {
|
||||
if(address == 0x1ff8) bank = 0;
|
||||
if(address == 0x1ff9) bank = 1;
|
||||
|
||||
if(address.bit(12)) {
|
||||
if(hasRam && address >= 0x1080 && address <= 0x10ff) return ram.read(address & 0x7f);
|
||||
return rom.read((bank * 0x1000) + (address & 0xfff));
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto write(n16 address, n8 data) -> bool override {
|
||||
if(address == 0x1ff8) bank = 0;
|
||||
if(address == 0x1ff9) bank = 1;
|
||||
|
||||
if(address >= 0x1000 && address <= 0x107f) {
|
||||
hasRam = true;
|
||||
ram.write(address & 0x7f, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
bank = 0;
|
||||
hasRam = 0;
|
||||
ram.allocate(128);
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(bank);
|
||||
s(hasRam);
|
||||
s(ram);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
namespace Board {
|
||||
|
||||
#include "linear.cpp"
|
||||
#include "atari8k.cpp"
|
||||
#include "atari16k.cpp"
|
||||
#include "atari32k.cpp"
|
||||
#include "commavid.cpp"
|
||||
#include "parker-bros.cpp"
|
||||
#include "tigervision.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 0xff; }
|
||||
virtual auto write(n16 address, n8 data) -> bool { return false; }
|
||||
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,41 @@
|
||||
struct Commavid : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<n8> rom;
|
||||
Memory::Writable<n8> ram;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(rom, "program.rom");
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(n16 address) -> n8 override {
|
||||
if(address.bit(12)) {
|
||||
if(address >= 0x1000 && address <= 0x13ff) return ram.read(address & 0x3ff);
|
||||
return rom.read(address & 0xfff);
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto write(n16 address, n8 data) -> bool override {
|
||||
if(address >= 0x1400 && address <= 0x17ff) {
|
||||
ram.write(address & 0x3ff, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
ram.allocate(1_KiB);
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(ram);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
struct Linear : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<n8> rom;
|
||||
Memory::Writable<n8> ram;
|
||||
bool hasRam;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(rom, "program.rom");
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(n16 address) -> n8 override {
|
||||
if(address.bit(12)) {
|
||||
if(hasRam && address >= 0x1080 && address <= 0x10ff) return ram.read(address & 0x7f);
|
||||
return rom.read(address & 0xfff);
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto write(n16 address, n8 data) -> bool override {
|
||||
if(address >= 0x1000 && address <= 0x107f) {
|
||||
hasRam = true;
|
||||
ram.write(address & 0x7f, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
hasRam = 0;
|
||||
ram.allocate(128);
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(hasRam);
|
||||
s(ram);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
struct ParkerBros : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<n8> rom;
|
||||
n3 banks[3];
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(rom, "program.rom");
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(n16 address) -> n8 override {
|
||||
bankswitch(address);
|
||||
|
||||
if(address.bit(12)) {
|
||||
if(address >= 0x1000 && address <= 0x13ff) return rom.read((banks[0] * 0x400) + (address & 0x3ff));
|
||||
if(address >= 0x1400 && address <= 0x17ff) return rom.read((banks[1] * 0x400) + (address & 0x3ff));
|
||||
if(address >= 0x1800 && address <= 0x1bff) return rom.read((banks[2] * 0x400) + (address & 0x3ff));
|
||||
if(address >= 0x1c00 && address <= 0x1fff) return rom.read(( 7 * 0x400) + (address & 0x3ff));
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto write(n16 address, n8 data) -> bool override {
|
||||
bankswitch(address);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto bankswitch(n16 address) -> void {
|
||||
if(address >= 0x1fe0 && address <= 0x1fe7) banks[0] = address - 0x1fe0;
|
||||
if(address >= 0x1fe8 && address <= 0x1fef) banks[1] = address - 0x1fe8;
|
||||
if(address >= 0x1ff0 && address <= 0x1ff7) banks[2] = address - 0x1ff0;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
banks[0] = 0;
|
||||
banks[1] = 1;
|
||||
banks[2] = 2;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(banks[0]);
|
||||
s(banks[1]);
|
||||
s(banks[2]);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
struct Tigervision : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<n8> rom;
|
||||
n8 bank;
|
||||
n8 nextBank;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(rom, "program.rom");
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(n16 address) -> n8 override {
|
||||
if(address.bit(12)) {
|
||||
if(bank != nextBank) bank = nextBank;
|
||||
if(address <= 0x17ff) return rom.read((bank * 0x800) + (address & 0x7ff));
|
||||
return rom.read((rom.size() - 0x800) + (address & 0x7ff));
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto write(n16 address, n8 data) -> bool override {
|
||||
if(address.bit(12) && bank != nextBank) bank = nextBank;
|
||||
if(address <= 0x3f) nextBank = data;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
bank = 0;
|
||||
nextBank = 0;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(bank);
|
||||
s(nextBank);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
#include <a26/a26.hpp>
|
||||
|
||||
namespace ares::Atari2600 {
|
||||
|
||||
Cartridge& cartridge = cartridgeSlot.cartridge;
|
||||
#include "board/board.cpp"
|
||||
#include "slot.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto Cartridge::allocate(Node::Port parent) -> Node::Peripheral {
|
||||
return node = parent->append<Node::Peripheral>(string{system.name(), " Cartridge"});
|
||||
}
|
||||
|
||||
auto Cartridge::connect() -> void {
|
||||
if(!node->setPak(pak = platform->pak(node))) return;
|
||||
|
||||
information = {};
|
||||
information.title = pak->attribute("title");
|
||||
information.region = pak->attribute("region");
|
||||
information.board = pak->attribute("board");
|
||||
|
||||
if(information.board == "Linear") board = new Board::Linear{*this};
|
||||
if(information.board == "Atari8k") board = new Board::Atari8k{*this};
|
||||
if(information.board == "Atari16k") board = new Board::Atari16k{*this};
|
||||
if(information.board == "Atari32k") board = new Board::Atari32k{*this};
|
||||
if(information.board == "Commavid") board = new Board::Commavid{*this};
|
||||
if(information.board == "ParkerBros8k") board = new Board::ParkerBros{*this};
|
||||
if(information.board == "Tigervision") board = new Board::Tigervision{*this};
|
||||
|
||||
if(!board) board = new Board::Interface{*this};
|
||||
board->pak = pak;
|
||||
board->load();
|
||||
power();
|
||||
}
|
||||
|
||||
auto Cartridge::disconnect() -> void {
|
||||
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(!node) return 0xff;
|
||||
if(board) return board->read(address);
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto Cartridge::write(n16 address, n8 data) -> bool {
|
||||
if(!node) return false;
|
||||
if(board) return board->write(address, data);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
struct Cartridge;
|
||||
#include "board/board.hpp"
|
||||
|
||||
struct Cartridge {
|
||||
Node::Peripheral node;
|
||||
VFS::Pak pak;
|
||||
|
||||
auto title() const -> string { return information.title; }
|
||||
auto region() const -> string { return information.region; }
|
||||
|
||||
//cartridge.cpp
|
||||
auto allocate(Node::Port) -> Node::Peripheral;
|
||||
auto connect() -> void;
|
||||
auto disconnect() -> void;
|
||||
|
||||
auto save() -> void;
|
||||
auto power() -> void;
|
||||
|
||||
auto read(n16 address) -> n8;
|
||||
auto write(n16 address, n8 data) -> bool;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
unique_pointer<Board::Interface> board;
|
||||
|
||||
//private:
|
||||
struct Information {
|
||||
string title;
|
||||
string region;
|
||||
string board;
|
||||
} information;
|
||||
};
|
||||
|
||||
#include "slot.hpp"
|
||||
extern Cartridge& cartridge;
|
||||
@@ -0,0 +1,3 @@
|
||||
auto Cartridge::serialize(serializer& s) -> void {
|
||||
if(board) s(*board);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
CartridgeSlot cartridgeSlot{"Cartridge Slot"};
|
||||
|
||||
CartridgeSlot::CartridgeSlot(string name) : name(name) {
|
||||
}
|
||||
|
||||
auto CartridgeSlot::load(Node::Object parent) -> void {
|
||||
port = parent->append<Node::Port>(name);
|
||||
port->setFamily(system.name());
|
||||
port->setType("Cartridge");
|
||||
port->setAllocate([&](auto name) { return cartridge.allocate(port); });
|
||||
port->setConnect([&] { return cartridge.connect(); });
|
||||
port->setDisconnect([&] { return cartridge.disconnect(); });
|
||||
}
|
||||
|
||||
auto CartridgeSlot::unload() -> void {
|
||||
cartridge.disconnect();
|
||||
port = {};
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
struct CartridgeSlot {
|
||||
Node::Port port;
|
||||
Cartridge cartridge;
|
||||
|
||||
//slot.cpp
|
||||
CartridgeSlot(string name);
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
|
||||
const string name;
|
||||
};
|
||||
|
||||
extern CartridgeSlot cartridgeSlot;
|
||||
@@ -0,0 +1,8 @@
|
||||
#include <a26/a26.hpp>
|
||||
|
||||
namespace ares::Atari2600 {
|
||||
|
||||
#include "port.cpp"
|
||||
#include "gamepad/gamepad.cpp"
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
struct Controller {
|
||||
Node::Peripheral node;
|
||||
|
||||
virtual ~Controller() = default;
|
||||
|
||||
virtual auto read() -> n8 { return 0xff; }
|
||||
virtual auto write(n8 data) -> void {}
|
||||
};
|
||||
|
||||
#include "port.hpp"
|
||||
#include "gamepad/gamepad.hpp"
|
||||
@@ -0,0 +1,37 @@
|
||||
Gamepad::Gamepad(Node::Port parent) {
|
||||
node = parent->append<Node::Peripheral>("Gamepad");
|
||||
|
||||
up = node->append<Node::Input::Button>("Up");
|
||||
down = node->append<Node::Input::Button>("Down");
|
||||
left = node->append<Node::Input::Button>("Left");
|
||||
right = node->append<Node::Input::Button>("Right");
|
||||
fire = node->append<Node::Input::Button>("Fire");
|
||||
}
|
||||
|
||||
auto Gamepad::read() -> n8 {
|
||||
platform->input(up);
|
||||
platform->input(down);
|
||||
platform->input(left);
|
||||
platform->input(right);
|
||||
platform->input(fire);
|
||||
|
||||
if(!(up->value() & down->value())) {
|
||||
yHold = 0, upLatch = up->value(), downLatch = down->value();
|
||||
} else if(!yHold) {
|
||||
yHold = 1, swap(upLatch, downLatch);
|
||||
}
|
||||
|
||||
if(!(left->value() & right->value())) {
|
||||
xHold = 0, leftLatch = left->value(), rightLatch = right->value();
|
||||
} else if(!xHold) {
|
||||
xHold = 1, swap(leftLatch, rightLatch);
|
||||
}
|
||||
|
||||
n8 data = 0xff;
|
||||
data.bit(0) = !upLatch;
|
||||
data.bit(1) = !downLatch;
|
||||
data.bit(2) = !leftLatch;
|
||||
data.bit(3) = !rightLatch;
|
||||
data.bit(4) = !fire->value();
|
||||
return data;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
struct Gamepad : Controller {
|
||||
Node::Input::Button up;
|
||||
Node::Input::Button down;
|
||||
Node::Input::Button left;
|
||||
Node::Input::Button right;
|
||||
Node::Input::Button fire;
|
||||
|
||||
Gamepad(Node::Port);
|
||||
|
||||
auto read() -> n8 override;
|
||||
|
||||
private:
|
||||
b1 yHold;
|
||||
b1 upLatch;
|
||||
b1 downLatch;
|
||||
b1 xHold;
|
||||
b1 leftLatch;
|
||||
b1 rightLatch;
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
ControllerPort controllerPort1{"Controller Port 1"};
|
||||
ControllerPort controllerPort2{"Controller Port 2"};
|
||||
|
||||
ControllerPort::ControllerPort(string name) : name(name) {
|
||||
}
|
||||
|
||||
auto ControllerPort::load(Node::Object parent) -> void {
|
||||
port = parent->append<Node::Port>(name);
|
||||
port->setFamily(system.name());
|
||||
port->setType("Controller");
|
||||
port->setHotSwappable(true);
|
||||
port->setAllocate([&](auto name) { return allocate(name); });
|
||||
port->setSupported({"Gamepad"});
|
||||
}
|
||||
|
||||
auto ControllerPort::unload() -> void {
|
||||
device = {};
|
||||
port = {};
|
||||
}
|
||||
|
||||
auto ControllerPort::allocate(string name) -> Node::Peripheral {
|
||||
if(name == "Gamepad") device = new Gamepad(port);
|
||||
if(device) return device->node;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto ControllerPort::serialize(serializer& s) -> void {
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
struct ControllerPort {
|
||||
Node::Port port;
|
||||
unique_pointer<Controller> device;
|
||||
|
||||
//port.cpp
|
||||
ControllerPort(string name);
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
auto allocate(string name) -> Node::Peripheral;
|
||||
|
||||
auto read() -> n8 { if(device) return device->read(); return 0xff; }
|
||||
auto write(n8 data) -> void { if(device) return device->write(data); }
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
const string name;
|
||||
};
|
||||
|
||||
extern ControllerPort controllerPort1;
|
||||
extern ControllerPort controllerPort2;
|
||||
@@ -0,0 +1,42 @@
|
||||
#include <a26/a26.hpp>
|
||||
|
||||
namespace ares::Atari2600 {
|
||||
|
||||
CPU cpu;
|
||||
#include "memory.cpp"
|
||||
#include "timing.cpp"
|
||||
#include "debugger.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto CPU::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Object>("CPU");
|
||||
debugger.load(node);
|
||||
}
|
||||
|
||||
auto CPU::unload() -> void {
|
||||
debugger = {};
|
||||
node = {};
|
||||
}
|
||||
|
||||
auto CPU::main() -> void {
|
||||
debugger.instruction();
|
||||
instruction();
|
||||
}
|
||||
|
||||
auto CPU::step(u32 clocks) -> void {
|
||||
Thread::step(clocks);
|
||||
Thread::synchronize();
|
||||
}
|
||||
|
||||
auto CPU::power(bool reset) -> void {
|
||||
MOS6502::BCD = 1;
|
||||
MOS6502::power();
|
||||
Thread::create(system.frequency() / 3, {&CPU::main, this});
|
||||
|
||||
PC.byte(0) = readBus(0xfffc);
|
||||
PC.byte(1) = readBus(0xfffd);
|
||||
|
||||
io = {};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
struct CPU : MOS6502, Thread {
|
||||
Node::Object node;
|
||||
|
||||
struct Debugger {
|
||||
//debugger.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto instruction() -> void;
|
||||
|
||||
struct Tracer {
|
||||
Node::Debugger::Tracer::Instruction instruction;
|
||||
} tracer;
|
||||
} debugger;
|
||||
|
||||
//cpu.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
|
||||
auto main() -> void;
|
||||
auto step(u32 clocks) -> void;
|
||||
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
//memory.cpp
|
||||
auto readBus(n16 address) -> n8;
|
||||
auto writeBus(n16 address, n8 data) -> void;
|
||||
|
||||
auto readIO(n16 address) -> n8;
|
||||
auto writeIO(n16 address, n8 data) -> void;
|
||||
|
||||
auto readDebugger(n16 address) -> n8 override;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
//timing.cpp
|
||||
auto read(n16 address) -> n8 override;
|
||||
auto write(n16 address, n8 data) -> void override;
|
||||
auto lastCycle() -> void override;
|
||||
auto nmi(n16& vector) -> void override;
|
||||
|
||||
auto rdyLine(bool) -> void;
|
||||
|
||||
//protected:
|
||||
struct IO {
|
||||
n1 rdyLine = 1;
|
||||
} io;
|
||||
};
|
||||
|
||||
extern CPU cpu;
|
||||
@@ -0,0 +1,12 @@
|
||||
auto CPU::Debugger::load(Node::Object parent) -> void {
|
||||
tracer.instruction = parent->append<Node::Debugger::Tracer::Instruction>("Instruction", "CPU");
|
||||
tracer.instruction->setAddressBits(16);
|
||||
}
|
||||
|
||||
auto CPU::Debugger::instruction() -> void {
|
||||
if(tracer.instruction->enabled() && tracer.instruction->address(cpu.PC)) {
|
||||
tracer.instruction->notify(cpu.disassembleInstruction(), cpu.disassembleContext());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
inline auto CPU::readBus(n16 address) -> n8 {
|
||||
address &= 0x1fff;
|
||||
n8 data = cartridge.read(address);
|
||||
|
||||
if(address.bit(12) == 0 && address.bit(7) == 0) {
|
||||
return tia.read(address & 0xf);
|
||||
}
|
||||
|
||||
if(address.bit(12) == 0 && address.bit(9) == 0 && address.bit(7) == 1) {
|
||||
return riot.readRam(address & 0x7f);
|
||||
}
|
||||
|
||||
if(address.bit(12) == 0 && address.bit(9) == 1 && address.bit(7) == 1) {
|
||||
return riot.readIo((address - 0x80) & 0x1f);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
inline auto CPU::writeBus(n16 address, n8 data) -> void {
|
||||
address &= 0x1fff;
|
||||
cartridge.write(address, data);
|
||||
|
||||
if(address.bit(12) == 0 && address.bit(7) == 0) {
|
||||
return tia.write(address & 0x3f, data);
|
||||
}
|
||||
|
||||
if(address.bit(12) == 0 && address.bit(9) == 0 && address.bit(7) == 1) {
|
||||
return riot.writeRam(address & 0x7f, data);
|
||||
}
|
||||
|
||||
if(address.bit(12) == 0 && address.bit(9) == 1 && address.bit(7) == 1) {
|
||||
return riot.writeIo((address - 0x80) & 0x1f, data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
auto CPU::readDebugger(n16 address) -> n8 {
|
||||
return readBus(address);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
auto CPU::serialize(serializer& s) -> void {
|
||||
MOS6502::serialize(s);
|
||||
Thread::serialize(s);
|
||||
s(io.rdyLine);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
auto CPU::read(n16 address) -> n8 {
|
||||
if(io.rdyLine == 0) {
|
||||
while (io.rdyLine == 0) {
|
||||
step(1);
|
||||
}
|
||||
|
||||
MDR = readBus(address);
|
||||
return MDR;
|
||||
}
|
||||
|
||||
step(1);
|
||||
MDR = readBus(address);
|
||||
return MDR;
|
||||
}
|
||||
|
||||
auto CPU::write(n16 address, n8 data) -> void {
|
||||
step(1);
|
||||
writeBus(address, MDR = data);
|
||||
}
|
||||
|
||||
auto CPU::lastCycle() -> void {
|
||||
}
|
||||
|
||||
auto CPU::nmi(n16& vector) -> void {
|
||||
|
||||
}
|
||||
|
||||
auto CPU::rdyLine(bool line) -> void {
|
||||
io.rdyLine = line;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
auto RIOT::readRam(n8 address) -> n8 {
|
||||
return ram[address];
|
||||
}
|
||||
|
||||
auto RIOT::writeRam(n8 address, n8 data) -> void {
|
||||
ram[address] = data;
|
||||
}
|
||||
|
||||
auto RIOT::readIo(n8 address) -> n8 {
|
||||
switch(address) {
|
||||
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;
|
||||
}
|
||||
|
||||
debug(unimplemented, "[RIOT] IO read ", hex(address));
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto RIOT::writeIo(n8 address, n8 data) -> void {
|
||||
switch(address) {
|
||||
case 0x00: writePortA(data); return;
|
||||
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: {
|
||||
const n16 intervals[] = {1, 8, 64, 1024};
|
||||
timer.counter = data;
|
||||
timer.interval = intervals[address - 0x14];
|
||||
timer.reload = timer.interval;
|
||||
return; }
|
||||
}
|
||||
|
||||
debug(unimplemented, "[RIOT] IO write ",hex(address), " = ", hex(data));
|
||||
}
|
||||
|
||||
auto RIOT::readPortA() -> n8 {
|
||||
n8 data;
|
||||
// TODO: Handle when configured as outputs
|
||||
data.bit(0, 3) = controllerPort2.read().bit(0, 4);
|
||||
data.bit(4, 7) = controllerPort1.read().bit(0, 4);
|
||||
return data;
|
||||
}
|
||||
|
||||
auto RIOT::writePortA(n8 data) -> void {
|
||||
port[0].data = data;
|
||||
}
|
||||
|
||||
auto RIOT::readPortB() -> n8 {
|
||||
system.controls.poll();
|
||||
|
||||
n8 data;
|
||||
|
||||
for(auto n : range(8)) if(port[1].direction.bit(n)) { data.bit(n) = port[1].data.bit(n); }
|
||||
|
||||
// Atari hard-wires these bits as input
|
||||
data.bit(0) = !system.controls.reset->value();
|
||||
data.bit(1) = !system.controls.select->value();
|
||||
|
||||
// These are toggle switches; flip the values on the rising edge
|
||||
if(system.controls.tvType->value() && !tvTypeLatch) {
|
||||
tvType = !tvType;
|
||||
}
|
||||
tvTypeLatch = system.controls.tvType->value();
|
||||
|
||||
if(system.controls.leftDifficulty->value() && !leftDifficultyLatch) {
|
||||
leftDifficulty = !leftDifficulty;
|
||||
}
|
||||
leftDifficultyLatch = system.controls.leftDifficulty->value();
|
||||
|
||||
if(system.controls.rightDifficulty->value() && !rightDifficultyLatch) {
|
||||
rightDifficulty = !rightDifficulty;
|
||||
}
|
||||
rightDifficultyLatch = system.controls.rightDifficulty->value();
|
||||
|
||||
data.bit(3) = tvType;
|
||||
data.bit(6) = leftDifficulty;
|
||||
data.bit(7) = rightDifficulty;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
auto RIOT::writePortB(n8 data) -> void {
|
||||
port[1].data = data;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#include <a26/a26.hpp>
|
||||
|
||||
namespace ares::Atari2600 {
|
||||
|
||||
RIOT riot;
|
||||
|
||||
#include "io.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto RIOT::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Object>("RIOT");
|
||||
|
||||
ram.allocate(128);
|
||||
}
|
||||
|
||||
auto RIOT::unload() -> void {
|
||||
ram.reset();
|
||||
node.reset();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
auto RIOT::step(u32 clocks) -> void {
|
||||
Thread::step(clocks);
|
||||
Thread::synchronize();
|
||||
}
|
||||
|
||||
auto RIOT::power(bool reset) -> void {
|
||||
Thread::create(system.frequency() / 3, {&RIOT::main, this});
|
||||
timer = {};
|
||||
port[0] = {.data = 0xff};
|
||||
port[1] = {.data = 0xff};
|
||||
leftDifficulty = 1;
|
||||
rightDifficulty = 1;
|
||||
tvType = 1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
struct RIOT : Thread {
|
||||
Node::Object node;
|
||||
Memory::Writable<n8> ram;
|
||||
|
||||
//riot.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
|
||||
auto main() -> void;
|
||||
auto step(u32 clocks) -> void;
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
auto decrementTimer() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readRam(n8 address) -> n8;
|
||||
auto writeRam(n8 address, n8 data) -> void;
|
||||
auto readIo(n8 address) -> n8;
|
||||
auto writeIo(n8 address, n8 data) -> void;
|
||||
|
||||
auto readPortA() -> n8;
|
||||
auto readPortB() -> n8;
|
||||
auto writePortA(n8 data) -> void;
|
||||
auto writePortB(n8 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
struct {
|
||||
u8 counter;
|
||||
n16 interval;
|
||||
n16 reload;
|
||||
} timer;
|
||||
|
||||
struct Port {
|
||||
n8 data;
|
||||
n8 direction;
|
||||
} port[2];
|
||||
|
||||
n1 leftDifficulty;
|
||||
n1 leftDifficultyLatch;
|
||||
n1 rightDifficulty;
|
||||
n1 rightDifficultyLatch;
|
||||
n1 tvType;
|
||||
n1 tvTypeLatch;
|
||||
};
|
||||
|
||||
extern RIOT riot;
|
||||
@@ -0,0 +1,21 @@
|
||||
//serialization.cpp
|
||||
auto RIOT::serialize(serializer& s) -> void {
|
||||
Thread::serialize(s);
|
||||
|
||||
s(ram);
|
||||
s(timer.counter);
|
||||
s(timer.interval);
|
||||
s(timer.reload);
|
||||
|
||||
for(auto n : range(2)) {
|
||||
s(port[n].data);
|
||||
s(port[n].direction);
|
||||
}
|
||||
|
||||
s(leftDifficulty);
|
||||
s(leftDifficultyLatch);
|
||||
s(rightDifficulty);
|
||||
s(rightDifficultyLatch);
|
||||
s(tvType);
|
||||
s(tvTypeLatch);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
auto System::Controls::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Object>("Controls");
|
||||
|
||||
reset = node->append<Node::Input::Button>("Reset");
|
||||
select = node->append<Node::Input::Button>("Select");
|
||||
leftDifficulty = node->append<Node::Input::Button>("Left Difficulty");
|
||||
rightDifficulty = node->append<Node::Input::Button>("Right Difficulty");
|
||||
tvType = node->append<Node::Input::Button>("TV Type");
|
||||
}
|
||||
|
||||
auto System::Controls::poll() -> void {
|
||||
platform->input(reset);
|
||||
platform->input(select);
|
||||
platform->input(leftDifficulty);
|
||||
platform->input(rightDifficulty);
|
||||
platform->input(tvType);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
auto System::serialize(bool synchronize) -> serializer {
|
||||
if(synchronize) scheduler.enter(Scheduler::Mode::Synchronize);
|
||||
serializer s;
|
||||
|
||||
u32 signature = SerializerSignature;
|
||||
char version[16] = {};
|
||||
char description[512] = {};
|
||||
memory::copy(&version, (const char*)SerializerVersion, SerializerVersion.size());
|
||||
|
||||
s(signature);
|
||||
s(synchronize);
|
||||
s(version);
|
||||
s(description);
|
||||
|
||||
serialize(s, synchronize);
|
||||
return s;
|
||||
}
|
||||
|
||||
auto System::unserialize(serializer& s) -> bool {
|
||||
u32 signature = 0;
|
||||
bool synchronize = true;
|
||||
char version[16] = {};
|
||||
char description[512] = {};
|
||||
|
||||
s(signature);
|
||||
s(synchronize);
|
||||
s(version);
|
||||
s(description);
|
||||
|
||||
if(signature != SerializerSignature) return false;
|
||||
if(string{version} != SerializerVersion) return false;
|
||||
|
||||
if(synchronize) power(/* reset = */ false);
|
||||
serialize(s, synchronize);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto System::serialize(serializer& s, bool synchronize) -> void {
|
||||
scheduler.setSynchronize(synchronize);
|
||||
s(cartridge);
|
||||
s(cpu);
|
||||
s(tia);
|
||||
s(riot);
|
||||
s(controllerPort1);
|
||||
s(controllerPort2);
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
#include <a26/a26.hpp>
|
||||
|
||||
namespace ares::Atari2600 {
|
||||
|
||||
auto enumerate() -> vector<string> {
|
||||
return {
|
||||
"[Atari] Atari 2600 (NTSC)",
|
||||
"[Atari] Atari 2600 (PAL)",
|
||||
"[Atari] Atari 2600 (SECAM)",
|
||||
};
|
||||
}
|
||||
|
||||
auto load(Node::System& node, string name) -> bool {
|
||||
if(!enumerate().find(name)) return false;
|
||||
return system.load(node, name);
|
||||
}
|
||||
|
||||
Random random;
|
||||
Scheduler scheduler;
|
||||
System system;
|
||||
#include "controls.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto System::game() -> string {
|
||||
if(cartridge.node) {
|
||||
return cartridge.title();
|
||||
}
|
||||
|
||||
return "(no cartridge connected)";
|
||||
}
|
||||
|
||||
auto System::run() -> void {
|
||||
scheduler.enter();
|
||||
}
|
||||
|
||||
auto System::load(Node::System& root, string name) -> bool {
|
||||
if(node) unload();
|
||||
|
||||
information = {};
|
||||
if(name.find("NTSC")) {
|
||||
information.name = "Atari 2600";
|
||||
information.region = Region::NTSC;
|
||||
information.frequency = 3579546;
|
||||
}
|
||||
if(name.find("PAL")) {
|
||||
information.name = "Atari 2600";
|
||||
information.region = Region::PAL;
|
||||
information.frequency = 3546894;
|
||||
}
|
||||
if(name.find("SECAM")) {
|
||||
information.name = "Atari 2600";
|
||||
information.region = Region::SECAM;
|
||||
information.frequency = 3546894;
|
||||
}
|
||||
|
||||
node = Node::System::create(information.name);
|
||||
node->setGame({&System::game, this});
|
||||
node->setRun({&System::run, this});
|
||||
node->setPower({&System::power, this});
|
||||
node->setSave({&System::save, this});
|
||||
node->setUnload({&System::unload, this});
|
||||
node->setSerialize({&System::serialize, this});
|
||||
node->setUnserialize({&System::unserialize, this});
|
||||
root = node;
|
||||
|
||||
scheduler.reset();
|
||||
controls.load(node);
|
||||
riot.load(node);
|
||||
cpu.load(node);
|
||||
tia.load(node);
|
||||
cartridgeSlot.load(node);
|
||||
controllerPort1.load(node);
|
||||
controllerPort2.load(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto System::save() -> void {
|
||||
if(!node) return;
|
||||
cartridge.save();
|
||||
}
|
||||
|
||||
auto System::unload() -> void {
|
||||
if(!node) return;
|
||||
save();
|
||||
riot.unload();
|
||||
cpu.unload();
|
||||
tia.unload();
|
||||
cartridgeSlot.unload();
|
||||
controllerPort1.unload();
|
||||
controllerPort2.unload();
|
||||
node = {};
|
||||
}
|
||||
|
||||
auto System::power(bool reset) -> void {
|
||||
for(auto& setting : node->find<Node::Setting::Setting>()) setting->setLatch();
|
||||
|
||||
random.entropy(Random::Entropy::Low);
|
||||
cartridge.power();
|
||||
riot.power(reset);
|
||||
cpu.power(reset);
|
||||
tia.power(reset);
|
||||
scheduler.power(cpu);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
extern Random random;
|
||||
|
||||
struct System {
|
||||
Node::System node;
|
||||
|
||||
struct Controls {
|
||||
Node::Object node;
|
||||
Node::Input::Button reset;
|
||||
Node::Input::Button select;
|
||||
Node::Input::Button leftDifficulty;
|
||||
Node::Input::Button rightDifficulty;
|
||||
Node::Input::Button tvType;
|
||||
|
||||
//controls.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto poll() -> void;
|
||||
} controls;
|
||||
|
||||
enum class Region : u32 { NTSC, PAL, SECAM };
|
||||
|
||||
auto name() const -> string { return information.name; }
|
||||
auto region() const -> Region { return information.region; }
|
||||
auto frequency() const -> u32 { return information.frequency; }
|
||||
|
||||
//system.cpp
|
||||
auto game() -> string;
|
||||
auto run() -> void;
|
||||
|
||||
auto load(Node::System& node, string name) -> bool;
|
||||
auto save() -> void;
|
||||
auto unload() -> void;
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(bool synchronize) -> serializer;
|
||||
auto unserialize(serializer&) -> bool;
|
||||
|
||||
private:
|
||||
struct Information {
|
||||
string name = "Atari 2600";
|
||||
Region region = Region::NTSC;
|
||||
u32 frequency = Constants::Colorburst::NTSC;
|
||||
} information;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&, bool synchronize) -> void;
|
||||
};
|
||||
|
||||
extern System system;
|
||||
|
||||
auto Region::NTSC() -> bool { return system.region() == System::Region::NTSC; }
|
||||
auto Region::PAL() -> bool { return system.region() == System::Region::PAL; }
|
||||
auto Region::SECAM() -> bool { return system.region() == System::Region::SECAM; }
|
||||
@@ -0,0 +1,64 @@
|
||||
auto TIA::runAudio() -> void {
|
||||
if(io.hcounter == 9 || io.hcounter == 81) {
|
||||
audio[0].phase0();
|
||||
audio[1].phase0();
|
||||
}
|
||||
|
||||
if(io.hcounter == 37 || io.hcounter == 149) {
|
||||
auto a = audio[0].phase1();
|
||||
auto b = audio[1].phase1();
|
||||
|
||||
double output = 0;
|
||||
output += volume[a];
|
||||
output += volume[b];
|
||||
|
||||
stream->frame(output / 2.0);
|
||||
}
|
||||
}
|
||||
|
||||
auto TIA::AudioChannel::phase0() -> void {
|
||||
if (enable) {
|
||||
switch (control.bit(0, 1)) {
|
||||
case 0x00: case 0x01: pulseCounterPaused = 0; break;
|
||||
case 0x02: pulseCounterPaused = noiseCounter.bit(1, 4) != 1; break;
|
||||
case 0x03: pulseCounterPaused = !noiseCounter.bit(0); break;
|
||||
}
|
||||
|
||||
noiseFeedback = (noiseCounter.bit(2) ^ noiseCounter.bit(0)) || noiseCounter == 0;
|
||||
if(control.bit(0, 2) == 0) {
|
||||
noiseFeedback = !(control.bit(2, 3)) ||
|
||||
!(noiseCounter || (pulseCounter != 0x0a)) ||
|
||||
(pulseCounter.bit(0) ^ noiseCounter.bit(0));
|
||||
}
|
||||
}
|
||||
|
||||
enable = divCounter == frequency;
|
||||
if (divCounter == frequency || divCounter == 0x1f) {
|
||||
divCounter = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
divCounter++;
|
||||
}
|
||||
|
||||
auto TIA::AudioChannel::phase1() -> u8 {
|
||||
if(!enable) return pulseCounter.bit(0) * volume;
|
||||
|
||||
pulseFeedback = 0;
|
||||
switch(control.bit(2, 3)) {
|
||||
case 0x00: pulseFeedback = pulseCounter != 0x0a && control.bit(0, 2) && (pulseCounter.bit(1) ^ pulseCounter.bit(0)); break;
|
||||
case 0x01: pulseFeedback = !pulseCounter.bit(3); ; break;
|
||||
case 0x02: pulseFeedback = !noiseCounter.bit(0); ; break;
|
||||
case 0x03: pulseFeedback = !(pulseCounter.bit(1) || !pulseCounter.bit(1, 3)); ; break;
|
||||
}
|
||||
|
||||
noiseCounter >>= 1;
|
||||
if (noiseFeedback) noiseCounter.bit(4) = 1;
|
||||
|
||||
if(!pulseCounterPaused) {
|
||||
pulseCounter = ~(pulseCounter >> 1) & 7;
|
||||
if (pulseFeedback) pulseCounter.bit(3) = 1;
|
||||
}
|
||||
|
||||
return pulseCounter.bit(0) * volume;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
auto TIA::color(n32 color) -> n64 {
|
||||
u64 ntscColors[128] = {
|
||||
0x0000'0000'0000ull, 0x4040'4040'4040ull, 0x6c6c'6c6c'6c6cull, 0x9090'9090'9090ull, 0xb0b0'b0b0'b0b0ull, 0xc8c8'c8c8'c8c8ull, 0xdcdc'dcdc'dcdcull, 0xecec'ecec'ececull,
|
||||
0x4444'4444'0000ull, 0x6464'6464'1010ull, 0x8484'8484'2424ull, 0xa0a0'a0a0'3434ull, 0xb8b8'b8b8'4040ull, 0xd0d0'd0d0'5050ull, 0xe8e8'e8e8'5c5cull, 0xfcfc'fcfc'6868ull,
|
||||
0x7070'2828'0000ull, 0x8484'4444'1414ull, 0x9898'5c5c'2828ull, 0xacac'7878'3c4cull, 0xbcbc'8c8c'4c4cull, 0xcccc'a0a0'5c5cull, 0xdcdc'b4b4'6868ull, 0xecec'c8c8'7878ull,
|
||||
0x8484'1818'0000ull, 0x9898'3434'1818ull, 0xacac'5050'3030ull, 0xc0c0'6868'4848ull, 0xd0d0'8080'5c5cull, 0xe0e0'9494'7070ull, 0xecec'a8a8'8080ull, 0xfcfc'bcbc'9494ull,
|
||||
0x8888'0000'0000ull, 0x9c9c'2020'2020ull, 0xb0b0'3c3c'3c3cull, 0xc0c0'5858'5858ull, 0xd0d0'7070'7070ull, 0xe0e0'8888'8888ull, 0xecec'a0a0'a0a0ull, 0xfcfc'b4b4'b4b4ull,
|
||||
0x7878'0000'5c5cull, 0x8c8c'2020'7474ull, 0xa0a0'3c3c'8888ull, 0xb0b0'5858'9c9cull, 0xc0c0'7070'b0b0ull, 0xd0d0'8484'c0c0ull, 0xdcdc'9c9c'd0d0ull, 0xecec'b0b0'e0e0ull,
|
||||
0x4848'0000'7878ull, 0x6060'2020'9090ull, 0x7878'3c3c'a4a4ull, 0x8c8c'5858'b8b8ull, 0xa0a0'7070'ccccull, 0xb4b4'8484'dcdcull, 0xc4c4'9c9c'ececull, 0xd4d4'b0b0'fcfcull,
|
||||
0x1414'0000'8484ull, 0x3030'2020'9898ull, 0x4c4c'3c3c'acacull, 0x6868'5858'c0c0ull, 0x7c7c'7070'd0d0ull, 0x9494'8888'e0e0ull, 0xa8a8'a0a0'ececull, 0xbcbc'b4b4'fcfcull,
|
||||
0x0000'0000'8888ull, 0x1c1c'2020'9c9cull, 0x3838'4040'b0b0ull, 0x5050'5c5c'c0c0ull, 0x6868'7474'd0d0ull, 0x7c7c'8c8c'e0e0ull, 0x9090'a4a4'ececull, 0xa4a4'b8b8'fcfcull,
|
||||
0x0000'1818'7c7cull, 0x1c1c'3838'9090ull, 0x3838'5454'a8a8ull, 0x5050'7070'bcbcull, 0x6868'8888'ccccull, 0x7c7c'9c9c'dcdcull, 0x9090'b4b4'ececull, 0xa4a4'c8c8'fcfcull,
|
||||
0x0000'2c2c'5c5cull, 0x1c1c'4c4c'7878ull, 0x3838'6868'9090ull, 0x5050'8484'acacull, 0x6868'9c9c'c0c0ull, 0x7c7c'b4b4'd4d4ull, 0x9090'cccc'e8e8ull, 0xa4a4'e0e0'fcfcull,
|
||||
0x0000'3c3c'2c2cull, 0x1c1c'5c5c'4848ull, 0x3838'7c7c'6464ull, 0x5050'9c9c'8080ull, 0x6868'b4b4'9494ull, 0x7c7c'd0d0'acacull, 0x9090'e4e4'c0c0ull, 0xa4a4'fcfc'd4d4ull,
|
||||
0x0000'3c3c'0000ull, 0x2020'5c5c'2020ull, 0x4040'7c7c'4040ull, 0x5c5c'9c9c'5c5cull, 0x7474'b4b4'7474ull, 0x8c8c'd0d0'8c8cull, 0xa4a4'e4e4'a4a4ull, 0xb8b8'fcfc'b8b8ull,
|
||||
0x1414'3838'0000ull, 0x3434'5c5c'1c1cull, 0x5050'7c7c'3838ull, 0x6c6c'9898'5050ull, 0x8484'b4b4'6868ull, 0x9c9c'cccc'7c7cull, 0xb4b4'e4e4'9090ull, 0xc8c8'fcfc'a4a4ull,
|
||||
0x2c2c'3030'0000ull, 0x4c4c'5050'1c1cull, 0x6868'7070'3434ull, 0x8484'8c8c'4c4cull, 0x9c9c'a8a8'6464ull, 0xb4b4'c0c0'7878ull, 0xcccc'd4d4'8888ull, 0xe0e0'ecec'9c9cull,
|
||||
0x4444'2828'0000ull, 0x6464'4848'1818ull, 0x8484'6868'3030ull, 0xa0a0'8484'4444ull, 0xb8b8'9c9c'5858ull, 0xd0d0'b4b4'6c6cull, 0x8e8c'cccc'7c7cull, 0xfcfc'c0c0'8c8cull,
|
||||
};
|
||||
|
||||
u64 palColors[128] = {
|
||||
0x0000'0000'0000ull, 0x2828'2828'2828ull, 0x5050'5050'5050ull, 0x7474'7474'7474ull, 0x9494'9494'9494ull, 0xb4b4'b4b4'b4b4ull, 0xd0d0'd0d0'd0d0ull, 0xecec'ecec'ececull,
|
||||
0x0000'0000'0000ull, 0x2828'2828'2828ull, 0x5050'5050'5050ull, 0x7474'7474'7474ull, 0x9494'9494'9494ull, 0xb4b4'b4b4'b4b4ull, 0xd0d0'd0d0'd0d0ull, 0xecec'ecec'ececull,
|
||||
0x8080'5858'0000ull, 0x9494'7070'2020ull, 0xa8a8'8484'3c3cull, 0xbcbc'9c9c'5858ull, 0xcccc'acac'7070ull, 0xdcdc'c0c0'8484ull, 0xecec'd0d0'9c9cull, 0xfcfc'e0e0'b0b0ull,
|
||||
0x4444'5c5c'0000ull, 0x5c5c'7878'2020ull, 0x7474'9090'3c3cull, 0x8c8c'acac'5858ull, 0xa0a0'c0c0'7070ull, 0xb0b0'd4d4'8484ull, 0xc4c4'e8e8'9c9cull, 0xd4d4'fcfc'b0b0ull,
|
||||
0x7070'3434'0000ull, 0x8888'5050'2020ull, 0xa0a0'6868'3c3cull, 0xb4b4'8484'5858ull, 0xc8c8'9898'7070ull, 0xdcdc'acac'8484ull, 0xecec'c0c0'9c9cull, 0xfcfc'd4d4'b0b0ull,
|
||||
0x0000'6464'1414ull, 0x2020'8080'3434ull, 0x3c3c'9898'5050ull, 0x5858'b0b0'6c6cull, 0x7070'c4c4'8484ull, 0x8484'd8d8'9c9cull, 0x9c9c'e8e8'b4b4ull, 0xb0b0'fcfc'c8c8ull,
|
||||
0x7070'0000'1414ull, 0x8888'2020'3434ull, 0xa0a0'3c3c'5050ull, 0xb4b4'5858'6c6cull, 0xc8c8'7070'8484ull, 0xdcdc'8484'9c9cull, 0xecec'9c9c'b4b4ull, 0xfcfc'b0b0'c8c8ull,
|
||||
0x0000'5c5c'5c5cull, 0x2020'7474'7474ull, 0x3c3c'8c8c'8c8cull, 0x5858'a4a4'a4a4ull, 0x7070'b8b8'b8b8ull, 0x8484'c8c8'c8c8ull, 0x9c9c'dcdc'dcdcull, 0xb0b0'ecec'ececull,
|
||||
0x7070'0000'5c5cull, 0x8484'2020'7474ull, 0x9494'3c3c'8888ull, 0xa8a8'5858'9c9cull, 0xb4b4'7070'b0b0ull, 0xc4c4'8484'c0c0ull, 0xd0d0'9c9c'd0d0ull, 0xe0e0'b0b0'e0e0ull,
|
||||
0x0000'3c3c'7070ull, 0x1c1c'5858'8888ull, 0x3838'7474'a0a0ull, 0x5050'8c8c'b4b4ull, 0x6868'a4a4'c8c8ull, 0x7c7c'b8b8'dcdcull, 0x9090'cccc'ececull, 0xa4a4'e0e0'fcfcull,
|
||||
0x5858'0000'7070ull, 0x6c6c'2020'8888ull, 0x8080'3c3c'a0a0ull, 0x9494'5858'b4b4ull, 0xa4a4'7070'c8c8ull, 0xb4b4'8484'dcdcull, 0xc4c4'9c9c'ececull, 0xd4d4'b0b0'fcfcull,
|
||||
0x0000'2020'7070ull, 0x1c1c'3c3c'8888ull, 0x3838'5858'a0a0ull, 0x5050'7474'b4b4ull, 0x6868'8888'c8c8ull, 0x7c7c'a0a0'dcdcull, 0x9090'b4b4'ececull, 0xa4a4'c8c8'fcfcull,
|
||||
0x3c3c'0000'8080ull, 0x5454'2020'9494ull, 0x6c6c'3c3c'a8a8ull, 0x8080'5858'bcbcull, 0x9494'7070'ccccull, 0xa8a8'8484'dcdcull, 0xb8b8'9c9c'ececull, 0xc8c8'b0b0'fcfcull,
|
||||
0x0000'0000'8888ull, 0x2020'2020'9c9cull, 0x3c3c'3c3c'b0b0ull, 0x5858'5858'c0c0ull, 0x7070'7070'd0d0ull, 0x8484'8484'e0e0ull, 0x9c9c'9c9c'ececull, 0xb0b0'b0b0'fcfcull,
|
||||
0x0000'0000'0000ull, 0x2828'2828'2828ull, 0x5050'5050'5050ull, 0x7474'7474'7474ull, 0x9494'9494'9494ull, 0xb4b4'b4b4'b4b4ull, 0xd0d0'd0d0'd0d0ull, 0xecec'ecec'ececull,
|
||||
0x0000'0000'0000ull, 0x2828'2828'2828ull, 0x5050'5050'5050ull, 0x7474'7474'7474ull, 0x9494'9494'9494ull, 0xb4b4'b4b4'b4b4ull, 0xd0d0'd0d0'd0d0ull, 0xecec'ecec'ececull,
|
||||
};
|
||||
|
||||
return Region::PAL() ? palColors[color & 0x7f] : ntscColors[color & 0x7f];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
auto TIA::read(n8 address) -> n8 {
|
||||
n8 value;
|
||||
|
||||
switch(address) {
|
||||
case 0x00: value.bit(7) = collision.M0P1; value.bit(6) = collision.M0P0; return value; // CXM0P
|
||||
case 0x01: value.bit(7) = collision.M1P0; value.bit(6) = collision.M1P1; return value; // CXM1P
|
||||
case 0x02: value.bit(7) = collision.P0PF; value.bit(6) = collision.P0BL; return value; // CX0FB
|
||||
case 0x03: value.bit(7) = collision.P1PF; value.bit(6) = collision.P1BL; return value; // CX1FB
|
||||
case 0x04: value.bit(7) = collision.M0PF; value.bit(6) = collision.M0BL; return value; // CXM0FB
|
||||
case 0x05: value.bit(7) = collision.M1PF; value.bit(6) = collision.M1BL; return value; // CXM1FB
|
||||
case 0x06: value.bit(7) = collision.BLPF; return value; // CXBLPF
|
||||
case 0x07: value.bit(7) = collision.P0P1; value.bit(6) = collision.M0M1; return value; // CXPPMM
|
||||
case 0x0c: value.bit(7) = controllerPort1.read().bit(4); return value; // INPT4
|
||||
case 0x0d: value.bit(7) = controllerPort2.read().bit(4); return value; // INPT5
|
||||
}
|
||||
|
||||
debug(unimplemented, "[TIA] read ", hex(address));
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto TIA::write(n8 address, n8 data) -> void {
|
||||
switch(address) {
|
||||
case 0x00: vsync(data.bit(1)); return; // VSYNC
|
||||
case 0x01: writeQueue.add(address, data, 1); return; // VBLANK
|
||||
case 0x02: cpu.rdyLine(0); return; // WSYNC
|
||||
case 0x03: io.hcounter = 0; return; // RSYNC
|
||||
case 0x04: player[0].size = data.bit(0,2); missile[0].size = data.bit(4, 5); return; // NUSIZ1
|
||||
case 0x05: player[1].size = data.bit(0,2); missile[1].size = data.bit(4, 5); return; // NUSIZ1
|
||||
case 0x06: io.p0Color = data.bit(1, 7); return; // COLUP0
|
||||
case 0x07: io.p1Color = data.bit(1, 7); return; // COLUP1
|
||||
case 0x08: io.fgColor = data.bit(1, 7); return; // COLUPF
|
||||
case 0x09: io.bgColor = data.bit(1, 7); return; // COLUBK
|
||||
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 0x10: resp(0); return; // RESP0
|
||||
case 0x11: resp(1); return; // RESP1
|
||||
case 0x12: resm(0); return; // RESM0
|
||||
case 0x13: resm(1); return; // RESM1
|
||||
case 0x14: resbl(); return; // RESBL
|
||||
case 0x15: audio[0].control = data; return; // AUDC0
|
||||
case 0x16: audio[1].control = data; return; // AUDC1
|
||||
case 0x17: audio[0].frequency = data; return; // AUDF0
|
||||
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 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
|
||||
case 0x20: writeQueue.add(address, data, 2); return; // HMP0
|
||||
case 0x21: writeQueue.add(address, data, 2); return; // HMP1
|
||||
case 0x22: writeQueue.add(address, data, 2); return; // HMM0
|
||||
case 0x23: writeQueue.add(address, data, 2); return; // HMM1
|
||||
case 0x24: writeQueue.add(address, data, 2); return; // HMBL
|
||||
case 0x25: player[0].delay = data.bit(0); return; // VDELP0
|
||||
case 0x26: player[1].delay = data.bit(0); return; // VDELP1
|
||||
case 0x27: ball.delay = data.bit(0); return; // VDELBL
|
||||
case 0x28: resmp(0, data); return; // RESMP0
|
||||
case 0x29: resmp(1, data); return; // RESMP1
|
||||
case 0x2a: writeQueue.add(address, data, 6); return; // HMOVE
|
||||
case 0x2b: writeQueue.add(address, data, 2); return; // HMCLR
|
||||
case 0x2c: cxclr(); return; // CXCLR
|
||||
}
|
||||
|
||||
debug(unimplemented, "[TIA] write ",hex(address), " = ", hex(data));
|
||||
}
|
||||
|
||||
|
||||
auto TIA::vsync(n1 state) -> void {
|
||||
if(io.vsync && !state) {
|
||||
io.vcounter = 0;
|
||||
frame();
|
||||
}
|
||||
|
||||
io.vsync = state;
|
||||
}
|
||||
|
||||
auto TIA::ctrlpf(n8 data) -> void {
|
||||
playfield.mirror = data.bit(0);
|
||||
playfield.scoreMode = data.bit(1);
|
||||
playfield.priority = data.bit(2);
|
||||
ball.size = data.bit(4, 5);
|
||||
}
|
||||
|
||||
auto TIA::cxclr() -> void {
|
||||
collision.M0P0 = 0;
|
||||
collision.M0P1 = 0;
|
||||
collision.M1P0 = 0;
|
||||
collision.M1P1 = 0;
|
||||
collision.P0PF = 0;
|
||||
collision.P0BL = 0;
|
||||
collision.P1PF = 0;
|
||||
collision.P1BL = 0;
|
||||
collision.M0PF = 0;
|
||||
collision.M0BL = 0;
|
||||
collision.M1PF = 0;
|
||||
collision.M1BL = 0;
|
||||
collision.BLPF = 0;
|
||||
collision.P0P1 = 0;
|
||||
collision.M0M1 = 0;
|
||||
}
|
||||
|
||||
auto TIA::hmove() -> void {
|
||||
for(auto& p : player) p.position = (p.position - p.offset + 160) % 160;
|
||||
for(auto& m : missile) m.position = (m.position - m.offset + 160) % 160;
|
||||
ball.position = (ball.position - ball.offset + 160) % 160;
|
||||
io.hmoveTriggered = io.vcounter;
|
||||
}
|
||||
|
||||
auto TIA::hmclr() -> void {
|
||||
for(auto& p : player) p.offset = 0;
|
||||
for(auto& m : missile) m.offset = 0;
|
||||
ball.offset = 0;
|
||||
}
|
||||
|
||||
auto TIA::grp(n1 index, n8 data) -> void {
|
||||
player[index].graphics[0] = data;
|
||||
player[!index].graphics[1] = player[!index].graphics[0];
|
||||
if(index) ball.enable[1] = ball.enable[0];
|
||||
}
|
||||
|
||||
auto TIA::resp(n1 index) -> void {
|
||||
player[index].position = (io.hcounter < 68 ? 3 : io.hcounter - 68 + 8) % 160;
|
||||
}
|
||||
|
||||
auto TIA::resm(n1 index) -> void {
|
||||
missile[index].position = (io.hcounter < 68 ? 2 : io.hcounter - 68 + 7) % 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);
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
//serialization.cpp
|
||||
auto TIA::serialize(serializer& s) -> void {
|
||||
Thread::serialize(s);
|
||||
|
||||
for(auto n : range(writeQueue.maxItems)) {
|
||||
s(writeQueue.items[n].active);
|
||||
s(writeQueue.items[n].address);
|
||||
s(writeQueue.items[n].data);
|
||||
s(writeQueue.items[n].delay);
|
||||
}
|
||||
|
||||
s(io.vcounter);
|
||||
s(io.hcounter);
|
||||
s(io.hmoveTriggered);
|
||||
s(io.vsync);
|
||||
s(io.vblank);
|
||||
s(io.bgColor);
|
||||
s(io.p0Color);
|
||||
s(io.p1Color);
|
||||
s(io.fgColor);
|
||||
|
||||
s(playfield.graphics);
|
||||
s(playfield.mirror);
|
||||
s(playfield.scoreMode);
|
||||
s(playfield.priority);
|
||||
|
||||
for(auto n : range(2)) {
|
||||
s(player[n].graphics[0]);
|
||||
s(player[n].graphics[1]);
|
||||
s(player[n].reflect);
|
||||
s(player[n].size);
|
||||
s(player[n].offset);
|
||||
s(player[n].position);
|
||||
s(player[n].delay);
|
||||
}
|
||||
|
||||
for(auto n : range(2)) {
|
||||
s(missile[n].enable);
|
||||
s(missile[n].reset);
|
||||
s(missile[n].size);
|
||||
s(missile[n].offset);
|
||||
s(missile[n].position);
|
||||
}
|
||||
|
||||
for(auto n : range(2)) {
|
||||
s(ball.enable[0]);
|
||||
s(ball.enable[1]);
|
||||
s(ball.size);
|
||||
s(ball.offset);
|
||||
s(ball.position);
|
||||
}
|
||||
|
||||
s(collision.M0P0);
|
||||
s(collision.M0P1);
|
||||
s(collision.M1P0);
|
||||
s(collision.M1P1);
|
||||
s(collision.P0PF);
|
||||
s(collision.P0BL);
|
||||
s(collision.P1PF);
|
||||
s(collision.P1BL);
|
||||
s(collision.M0PF);
|
||||
s(collision.M0BL);
|
||||
s(collision.M1PF);
|
||||
s(collision.M1BL);
|
||||
s(collision.BLPF);
|
||||
s(collision.P0P1);
|
||||
s(collision.M0M1);
|
||||
|
||||
for(auto n : range(26)) {
|
||||
s(volume[n]);
|
||||
}
|
||||
|
||||
for(auto n : range(2)) {
|
||||
s(audio[n].enable);
|
||||
s(audio[n].divCounter);
|
||||
s(audio[n].noiseCounter);
|
||||
s(audio[n].noiseFeedback);
|
||||
s(audio[n].pulseCounter);
|
||||
s(audio[n].pulseCounterPaused);
|
||||
s(audio[n].pulseFeedback);
|
||||
s(audio[n].volume);
|
||||
s(audio[n].control);
|
||||
s(audio[n].frequency);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
#include <a26/a26.hpp>
|
||||
|
||||
namespace ares::Atari2600 {
|
||||
|
||||
TIA tia;
|
||||
|
||||
#include "audio.cpp"
|
||||
#include "color.cpp"
|
||||
#include "io.cpp"
|
||||
#include "serialization.cpp"
|
||||
#include "write-queue.cpp"
|
||||
|
||||
auto TIA::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Object>("TIA");
|
||||
|
||||
screen = node->append<Node::Video::Screen>("Screen", 160, vlines());
|
||||
screen->colors(1 << 7, {&TIA::color, this});
|
||||
screen->setSize(160, vlines());
|
||||
screen->setScale(2.0, 1.0);
|
||||
screen->setAspect(1.0, 1.0);
|
||||
screen->setViewport(0, 0, 160, vlines());
|
||||
|
||||
stream = node->append<Node::Audio::Stream>("Audio");
|
||||
stream->setChannels(1);
|
||||
stream->setFrequency(31'403);
|
||||
stream->addHighPassFilter( 20.0, 1);
|
||||
stream->addLowPassFilter (2840.0, 1);
|
||||
}
|
||||
|
||||
auto TIA::unload() -> void {
|
||||
screen->quit();
|
||||
node->remove(screen);
|
||||
screen.reset();
|
||||
node.reset();
|
||||
}
|
||||
|
||||
auto TIA::main() -> void {
|
||||
scanline();
|
||||
io.vcounter++;
|
||||
io.hmoveTriggered = 255;
|
||||
}
|
||||
|
||||
auto TIA::scanline() -> void {
|
||||
cpu.rdyLine(1);
|
||||
for(io.hcounter = 0; io.hcounter < 228; io.hcounter++) {
|
||||
auto x = io.hcounter - 68;
|
||||
if(x >= 0) pixel(x);
|
||||
writeQueue.step();
|
||||
runAudio();
|
||||
step();
|
||||
}
|
||||
}
|
||||
|
||||
auto TIA::pixel(n8 x) -> void {
|
||||
auto output = screen->pixels().data() + (io.vcounter * 160);
|
||||
|
||||
// Output only black during vblank, or for the first 8px of a scanline where hmove was triggered
|
||||
if (io.vblank || (io.hmoveTriggered == io.vcounter && x < 8)) {
|
||||
output[x] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
output[x] = io.bgColor;
|
||||
auto pf = runPlayfield(x);
|
||||
auto bl = runBall(x);
|
||||
auto p0 = runPlayer(x, 0);
|
||||
auto p1 = runPlayer(x, 1);
|
||||
auto m0 = runMissile(x, 0);
|
||||
auto m1 = runMissile(x, 1);
|
||||
|
||||
if(!playfield.priority && (pf || bl)) output[x] = io.fgColor;
|
||||
if(p1 || m1) output[x] = io.p1Color;
|
||||
if(p0 || m0) output[x] = io.p0Color;
|
||||
if(playfield.priority && (pf || bl)) output[x] = io.fgColor;
|
||||
|
||||
// Update Collision
|
||||
if(m0 && p1) collision.M0P1 = 1;
|
||||
if(m0 && p0) collision.M0P0 = 1;
|
||||
if(m1 && p0) collision.M1P0 = 1;
|
||||
if(m1 && p1) collision.M1P1 = 1;
|
||||
if(p0 && pf) collision.P0PF = 1;
|
||||
if(p0 && bl) collision.P0BL = 1;
|
||||
if(p1 && pf) collision.P1PF = 1;
|
||||
if(p1 && bl) collision.P1BL = 1;
|
||||
if(m0 && pf) collision.M0PF = 1;
|
||||
if(m0 && bl) collision.M0BL = 1;
|
||||
if(m1 && pf) collision.M1PF = 1;
|
||||
if(m1 && bl) collision.M1BL = 1;
|
||||
if(bl && pf) collision.BLPF = 1;
|
||||
if(p0 && p1) collision.P0P1 = 1;
|
||||
if(m0 && m1) collision.M0M1 = 1;
|
||||
}
|
||||
|
||||
auto TIA::runPlayfield(n8 x) -> n1 {
|
||||
auto pos = x >> 2;
|
||||
|
||||
if(!playfield.mirror || pos < 20) return playfield.graphics.bit(pos % 20);
|
||||
return playfield.graphics.bit(19 - (pos % 20));
|
||||
}
|
||||
|
||||
auto TIA::runPlayer(n8 x, n1 index) -> n1 {
|
||||
auto& player = this->player[index];
|
||||
auto position = player.position;
|
||||
|
||||
// Handle player stretch capability
|
||||
auto width = 8;
|
||||
if(player.size == 5) width = 16;
|
||||
if(player.size == 7) width = 32;
|
||||
|
||||
// Handle repeat capability
|
||||
auto repeat = 1;
|
||||
if(player.size == 1 || player.size == 2 || player.size == 4) repeat = 2;
|
||||
if(player.size == 3 || player.size == 6) repeat = 3;
|
||||
|
||||
auto spacing = 8;
|
||||
if(player.size == 2 || player.size == 6) spacing = 24;
|
||||
if(player.size == 4 ) spacing = 56;
|
||||
|
||||
for(int i = 0; i < repeat; i++) {
|
||||
if (x >= position && x < position + width) {
|
||||
auto bit = player.reflect ? (x - position) / (width / 8) : 7 - ((x - position) / (width / 8));
|
||||
return player.graphics[player.delay].bit(bit);
|
||||
}
|
||||
|
||||
position = (position + (spacing + width)) % 160;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto TIA::runMissile(n8 x, n1 index) -> n1 {
|
||||
auto& missile = this->missile[index];
|
||||
if (!missile.enable || missile.reset) return 0;
|
||||
|
||||
const int missileSizes[4] = {1, 2, 4, 8};
|
||||
return x >= missile.position && x < missile.position + missileSizes[missile.size];
|
||||
}
|
||||
|
||||
auto TIA::runBall(n8 x) -> n1 {
|
||||
if(!ball.enable[ball.delay]) return 0;
|
||||
|
||||
const int ballSizes[4] = {1, 2, 4, 8};
|
||||
return x >= ball.position && x < ball.position + ballSizes[ball.size];
|
||||
}
|
||||
|
||||
auto TIA::frame() -> void {
|
||||
screen->frame();
|
||||
scheduler.exit(Event::Frame);
|
||||
}
|
||||
|
||||
auto TIA::step(u32 clocks) -> void {
|
||||
Thread::step(clocks);
|
||||
Thread::synchronize();
|
||||
}
|
||||
|
||||
auto TIA::power(bool reset) -> void {
|
||||
Thread::create(system.frequency(), {&TIA::main, this});
|
||||
screen->power();
|
||||
io = {};
|
||||
playfield = {};
|
||||
for(auto& p : player) p = {};
|
||||
for(auto& m : missile) m = {};
|
||||
ball = {};
|
||||
collision = {};
|
||||
for(auto n : range(writeQueue.maxItems)) writeQueue.items[n] = {};
|
||||
|
||||
for(u32 level : range(15)) {
|
||||
volume[level] = pow(2, level * -2.0 / 6.0);
|
||||
}
|
||||
volume[15] = 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
struct TIA : Thread {
|
||||
Node::Object node;
|
||||
Node::Video::Screen screen;
|
||||
Node::Audio::Stream stream;
|
||||
|
||||
auto vlines() const -> u32 { return Region::NTSC() ? 262 : 312; }
|
||||
|
||||
//tia.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
|
||||
auto main() -> void;
|
||||
auto step(u32 clocks = 1) -> void;
|
||||
auto power(bool reset) -> void;
|
||||
auto frame() -> void;
|
||||
auto scanline() -> void;
|
||||
auto pixel(n8 x) -> void;
|
||||
|
||||
auto runPlayfield(n8 x) -> n1;
|
||||
auto runBall(n8 x) -> n1;
|
||||
auto runPlayer(n8 x, n1 index) -> n1;
|
||||
auto runMissile(n8 x, n1 index) -> n1;
|
||||
auto runCollision() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto read(n8 address) -> n8;
|
||||
auto write(n8 address, n8 data) -> void;
|
||||
auto vsync(n1 state) -> void;
|
||||
auto ctrlpf(n8 data) -> void;
|
||||
auto cxclr() -> void;
|
||||
auto hmove() -> void;
|
||||
auto hmclr() -> void;
|
||||
auto grp(n1 index, n8 data) -> void;
|
||||
auto resp(n1 index) -> void;
|
||||
auto resm(n1 index) -> void;
|
||||
auto resmp(n1 index, n8 data) -> void;
|
||||
auto resbl() -> void;
|
||||
|
||||
// audio.cpp
|
||||
auto runAudio() -> void;
|
||||
|
||||
//color.cpp
|
||||
auto color(n32) -> n64;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
// write-queue.cpp
|
||||
struct WriteQueue {
|
||||
inline auto add(u8 address, n8 data, i8 delay) -> void;
|
||||
inline auto step() -> void;
|
||||
|
||||
struct QueueItem {
|
||||
inline auto commit() -> void;
|
||||
n1 active;
|
||||
i8 address;
|
||||
n8 data;
|
||||
i8 delay;
|
||||
};
|
||||
|
||||
QueueItem items[16];
|
||||
const int maxItems = 16;
|
||||
} writeQueue;
|
||||
|
||||
struct {
|
||||
u9 vcounter;
|
||||
u8 hcounter;
|
||||
u8 hmoveTriggered;
|
||||
|
||||
n1 vsync;
|
||||
n1 vblank;
|
||||
|
||||
n7 bgColor;
|
||||
n7 p0Color;
|
||||
n7 p1Color;
|
||||
n7 fgColor;
|
||||
} io;
|
||||
|
||||
struct {
|
||||
n20 graphics;
|
||||
n1 mirror;
|
||||
n1 scoreMode;
|
||||
n1 priority;
|
||||
} playfield;
|
||||
|
||||
struct {
|
||||
n8 graphics[2];
|
||||
n1 reflect;
|
||||
n3 size;
|
||||
i4 offset;
|
||||
n8 position;
|
||||
n1 delay;
|
||||
} player[2];
|
||||
|
||||
struct {
|
||||
n1 enable;
|
||||
n1 reset;
|
||||
n2 size;
|
||||
i4 offset;
|
||||
n8 position;
|
||||
} missile[2];
|
||||
|
||||
struct {
|
||||
n1 enable[2];
|
||||
n2 size;
|
||||
i4 offset;
|
||||
n1 delay;
|
||||
n8 position;
|
||||
} ball;
|
||||
|
||||
struct {
|
||||
n1 M0P0;
|
||||
n1 M0P1;
|
||||
n1 M1P0;
|
||||
n1 M1P1;
|
||||
n1 P0PF;
|
||||
n1 P0BL;
|
||||
n1 P1PF;
|
||||
n1 P1BL;
|
||||
n1 M0PF;
|
||||
n1 M0BL;
|
||||
n1 M1PF;
|
||||
n1 M1BL;
|
||||
n1 BLPF;
|
||||
n1 P0P1;
|
||||
n1 M0M1;
|
||||
} collision;
|
||||
|
||||
struct AudioChannel {
|
||||
auto phase0() -> void;
|
||||
auto phase1() -> u8;
|
||||
n1 enable;
|
||||
n8 divCounter;
|
||||
n8 noiseCounter;
|
||||
n1 noiseFeedback;
|
||||
n8 pulseCounter;
|
||||
n1 pulseCounterPaused;
|
||||
n1 pulseFeedback;
|
||||
n4 volume;
|
||||
n4 control;
|
||||
n5 frequency;
|
||||
} audio[2];
|
||||
|
||||
f64 volume[16];
|
||||
};
|
||||
|
||||
extern TIA tia;
|
||||
@@ -0,0 +1,46 @@
|
||||
auto TIA::WriteQueue::add(u8 address, n8 data, i8 delay) -> void {
|
||||
for(auto n : range(maxItems)) {
|
||||
if(!items[n].active) {
|
||||
items[n].address = address;
|
||||
items[n].data = data;
|
||||
items[n].delay = delay;
|
||||
items[n].active = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto TIA::WriteQueue::step() -> void {
|
||||
auto& items = tia.writeQueue.items;
|
||||
|
||||
for(auto n : range(maxItems)) {
|
||||
if(items[n].active && --items[n].delay == 0) {
|
||||
items[n].commit();
|
||||
items[n].active = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto TIA::WriteQueue::QueueItem::commit() -> void {
|
||||
switch(address) {
|
||||
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
|
||||
case 0x0e: for(auto n : range(8)) tia.playfield.graphics.bit(4 + n) = data.bit(7 - n); return; // PF1
|
||||
case 0x0f: tia.playfield.graphics.bit(12, 19) = data; return; // PF2
|
||||
case 0x1b: tia.grp(0, data); return; // GRP0
|
||||
case 0x1c: tia.grp(1, data); return; // GRP1
|
||||
case 0x1d: tia.missile[0].enable = data.bit(1); return; // ENAM0
|
||||
case 0x1e: tia.missile[1].enable = data.bit(1); return; // ENAM1
|
||||
case 0x1f: tia.ball.enable[0] = data.bit(1); return; // ENABL
|
||||
case 0x20: tia.player[0].offset = data.bit(4, 7); return; // HMP0
|
||||
case 0x21: tia.player[1].offset = data.bit(4, 7); return; // HMP1
|
||||
case 0x22: tia.missile[0].offset = data.bit(4, 7); return; // HMM0
|
||||
case 0x23: tia.missile[1].offset = data.bit(4, 7); return; // HMM1
|
||||
case 0x24: tia.ball.offset = data.bit(4, 7); return; // HMBL
|
||||
case 0x2a: tia.hmove(); return; // HMOVE
|
||||
case 0x2b: tia.hmclr(); return; // HMCLR
|
||||
}
|
||||
debug(unimplemented, "[TIA] writeQueue: ",hex(address), " = ", hex(data));
|
||||
}
|
||||
+5
-4
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <libco/libco.h>
|
||||
#include <sljit.h>
|
||||
|
||||
#include <nall/platform.hpp>
|
||||
#include <nall/adaptive-array.hpp>
|
||||
@@ -37,16 +38,16 @@ using namespace nall;
|
||||
|
||||
namespace ares {
|
||||
static const string Name = "ares";
|
||||
static const string Version = "124";
|
||||
static const string Version = "129";
|
||||
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-emulator.github.io";
|
||||
static const string WebsiteURI = "https://ares-emulator.github.io";
|
||||
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)
|
||||
static const string SerializerVersion = "123.1";
|
||||
static const string SerializerVersion = "129";
|
||||
|
||||
namespace VFS {
|
||||
using Pak = shared_pointer<vfs::directory>;
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
#include <ares/ares.hpp>
|
||||
|
||||
namespace ares::Memory {
|
||||
|
||||
alignas(4096) u8 fixedBuffer[1_GiB];
|
||||
|
||||
FixedAllocator::FixedAllocator() {
|
||||
_allocator.resize(sizeof(fixedBuffer), 0, fixedBuffer);
|
||||
}
|
||||
|
||||
auto FixedAllocator::get() -> bump_allocator& {
|
||||
static FixedAllocator allocator;
|
||||
return allocator._allocator;
|
||||
}
|
||||
|
||||
}
|
||||
#include <ares/ares.hpp>
|
||||
|
||||
namespace ares::Memory {
|
||||
|
||||
#if defined(PLATFORM_MACOS) && defined(ARCHITECTURE_ARM64)
|
||||
//stub for unsupported platforms
|
||||
FixedAllocator::FixedAllocator() {
|
||||
}
|
||||
#else
|
||||
alignas(4096) u8 fixedBuffer[128_MiB];
|
||||
|
||||
FixedAllocator::FixedAllocator() {
|
||||
_allocator.resize(sizeof(fixedBuffer), 0, fixedBuffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
auto FixedAllocator::get() -> bump_allocator& {
|
||||
static FixedAllocator allocator;
|
||||
return allocator._allocator;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,8 +27,8 @@ struct Instruction : Tracer {
|
||||
for(auto& history : _history) history = ~0;
|
||||
}
|
||||
|
||||
auto address(u32 address) -> bool {
|
||||
address &= (1ull << _addressBits) - 1; //mask upper bits of address
|
||||
auto address(u64 address) -> bool {
|
||||
address &= ~0ull >> (64 - _addressBits); //mask upper bits of address
|
||||
_address = address;
|
||||
address >>= _addressMask; //clip unneeded alignment bits (to reduce _masks size)
|
||||
|
||||
@@ -55,9 +55,9 @@ struct Instruction : Tracer {
|
||||
|
||||
//mark an already-executed address as not executed yet for trace masking.
|
||||
//call when writing to executable RAM to support self-modifying code.
|
||||
auto invalidate(u32 address) -> void {
|
||||
auto invalidate(u64 address) -> void {
|
||||
if(unlikely(_mask && updateMasks())) {
|
||||
address &= (1ull << _addressBits) - 1;
|
||||
address &= ~0ull >> (64 - _addressBits);
|
||||
address >>= _addressMask;
|
||||
_masks[address >> 3] &= ~(1 << (address & 7));
|
||||
}
|
||||
@@ -104,7 +104,7 @@ struct Instruction : Tracer {
|
||||
|
||||
protected:
|
||||
auto updateMasks() -> bool {
|
||||
auto size = 1ull << _addressBits >> _addressMask >> 3;
|
||||
auto size = 1ull << (_addressBits - _addressMask - 3);
|
||||
if(!_mask || !size) return _masks.reset(), false;
|
||||
if(_masks.size() == size) return true;
|
||||
_masks.reset();
|
||||
|
||||
@@ -36,6 +36,7 @@ auto Screen::main(uintptr_t) -> void {
|
||||
auto Screen::quit() -> void {
|
||||
_kill = true;
|
||||
_thread.join();
|
||||
_sprites.reset();
|
||||
}
|
||||
|
||||
auto Screen::power() -> void {
|
||||
|
||||
@@ -16,6 +16,7 @@ struct Platform {
|
||||
virtual auto pak(Node::Object) -> shared_pointer<vfs::directory> { return {}; }
|
||||
virtual auto event(Event) -> void {}
|
||||
virtual auto log(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 {}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
build := stable
|
||||
nall.path := ../../../nall
|
||||
include $(nall.path)/GNUmakefile
|
||||
|
||||
all:
|
||||
sourcery resource.bml resource.cpp resource.hpp
|
||||
|
||||
clean:
|
||||
rm resource.cpp
|
||||
rm resource.hpp
|
||||
$(call delete,resource.cpp)
|
||||
$(call delete,resource.hpp)
|
||||
@@ -84,7 +84,8 @@ inline auto Thread::synchronize() -> void {
|
||||
template<typename... P>
|
||||
inline auto Thread::synchronize(Thread& thread, P&&... p) -> void {
|
||||
//switching to another thread does not guarantee it will catch up before switching back.
|
||||
while(thread.clock() < clock()) {
|
||||
//make sure not to switch to threads that were destroyed during synchronization
|
||||
while(thread.clock() < clock() && thread.handle()) {
|
||||
//disable synchronization for auxiliary threads during scheduler synchronization.
|
||||
//synchronization can begin inside of this while loop.
|
||||
if(scheduler.synchronizing()) break;
|
||||
|
||||
@@ -74,7 +74,7 @@ auto M24C::write() -> void {
|
||||
case Mode::Device:
|
||||
if(counter <= 8) {
|
||||
device = device << 1 | data();
|
||||
} else if(!select()) {
|
||||
} else if(select() != Acknowledge) {
|
||||
mode = Mode::Standby;
|
||||
} else if(device & 1) {
|
||||
mode = Mode::Read;
|
||||
|
||||
@@ -50,7 +50,7 @@ template<u32 Size> auto M68000::_address(EffectiveAddress& ea) -> string {
|
||||
if(ea.mode == 2) return {_addressRegister(AddressRegister{ea.reg})};
|
||||
if(ea.mode == 5) return {"$", hex(_readDisplacement(read(AddressRegister{ea.reg})), 6L)};
|
||||
if(ea.mode == 6) return {"$", hex(_readIndex(read(AddressRegister{ea.reg})), 6L)};
|
||||
if(ea.mode == 7) return {"$", hex((i16)_readPC<Word>(), 6L)};
|
||||
if(ea.mode == 7) { auto imm = _readPC<Word>(); return {"$", imm >= 0x8000 ? hex((i16)imm, 6L, 'f') : hex((i16)imm, 6L, '0') }; }
|
||||
if(ea.mode == 8) return {"$", hex(_readPC<Long>(), 6L)};
|
||||
if(ea.mode == 9) return {"$", hex(_pc + (i16)_readPC(), 6L)};
|
||||
if(ea.mode == 10) return {"$", hex(_readIndex(_pc), 6L)};
|
||||
@@ -65,7 +65,7 @@ template<u32 Size> auto M68000::_effectiveAddress(EffectiveAddress& ea) -> strin
|
||||
if(ea.mode == 4) return {"-(", _addressRegister(AddressRegister{ea.reg}), ")"};
|
||||
if(ea.mode == 5) return {"($", hex(_readDisplacement(read(AddressRegister{ea.reg})), 6L), ")"};
|
||||
if(ea.mode == 6) return {"($", hex(_readIndex(read(AddressRegister{ea.reg})), 6L), ")"};
|
||||
if(ea.mode == 7) return {"($", hex((i16)_readPC<Word>(), 6L), ")"};
|
||||
if(ea.mode == 7) { auto imm = _readPC<Word>(); return {"($", imm >= 0x8000 ? hex((i16)imm, 6L, 'f') : hex((i16)imm, 6L, '0'), ")"}; }
|
||||
if(ea.mode == 8) return {"($", hex(_readPC<Long>(), 6L), ")"};
|
||||
if(ea.mode == 9) return {"($", hex(_readDisplacement(_pc), 6L), ")"};
|
||||
if(ea.mode == 10) return {"($", hex(_readIndex(_pc), 6L), ")"};
|
||||
|
||||
@@ -414,6 +414,7 @@ M68000::M68000() {
|
||||
for(n3 mode : range(8))
|
||||
for(n3 reg : range(8)) {
|
||||
auto opcode = pattern("1011 ---+ 11-- ----") | areg << 9 | mode << 3 | reg << 0;
|
||||
if(mode == 7 && reg >= 5) continue;
|
||||
|
||||
AddressRegister with{areg};
|
||||
EffectiveAddress from{mode, reg};
|
||||
@@ -1256,7 +1257,7 @@ M68000::M68000() {
|
||||
for(n3 mode : range(8))
|
||||
for(n3 reg : range(8)) {
|
||||
auto opcode = pattern("0100 1010 ++-- ----") | mode << 3 | reg << 0;
|
||||
if(mode == 7 && reg >= 2) continue;
|
||||
if(mode == 1 || mode == 7 && reg >= 2) continue;
|
||||
|
||||
EffectiveAddress from{mode, reg};
|
||||
bind(opcode | 0 << 6, TST<Byte>, from);
|
||||
|
||||
@@ -332,14 +332,20 @@ auto M68000::instructionCHK(DataRegister compare, EffectiveAddress maximum) -> v
|
||||
|
||||
r.z = clip<Word>(target) == 0;
|
||||
r.n = sign<Word>(target) < 0;
|
||||
if(r.n) return exception(Exception::BoundsCheck, Vector::BoundsCheck);
|
||||
if(r.n) {
|
||||
prefetched();
|
||||
return exception(Exception::BoundsCheck, Vector::BoundsCheck);
|
||||
}
|
||||
|
||||
auto result = (n64)target - source;
|
||||
r.c = sign<Word>(result >> 1) < 0;
|
||||
r.v = sign<Word>((target ^ source) & (target ^ result)) < 0;
|
||||
r.z = clip<Word>(result) == 0;
|
||||
r.n = sign<Word>(result) < 0;
|
||||
if(r.n == r.v && !r.z) return exception(Exception::BoundsCheck, Vector::BoundsCheck);
|
||||
if(r.n == r.v && !r.z) {
|
||||
prefetched();
|
||||
return exception(Exception::BoundsCheck, Vector::BoundsCheck);
|
||||
}
|
||||
prefetch();
|
||||
}
|
||||
|
||||
@@ -390,22 +396,30 @@ template<u32 Size> auto M68000::instructionCMPM(EffectiveAddress from, Effective
|
||||
}
|
||||
|
||||
auto M68000::instructionDBCC(n4 test, DataRegister with) -> void {
|
||||
auto displacement = extension<Word>();
|
||||
if(condition(test)) {
|
||||
idle(4);
|
||||
} else {
|
||||
idle(2);
|
||||
r.pc -= 2;
|
||||
if(!condition(test)) { // cc false
|
||||
auto disp = sign<Word>(r.irc);
|
||||
// predict branch
|
||||
r.pc += disp;
|
||||
prefetch();
|
||||
// decrement register
|
||||
n16 result = read<Word>(with);
|
||||
write<Word>(with, result - 1);
|
||||
if(result) {
|
||||
idle(2);
|
||||
r.pc -= 4;
|
||||
r.pc += sign<Word>(displacement);
|
||||
prefetch();
|
||||
// branch taken
|
||||
prefetch();
|
||||
return;
|
||||
} else {
|
||||
idle(2);
|
||||
// branch not taken
|
||||
r.pc -= disp;
|
||||
}
|
||||
} else { // cc true
|
||||
idle(2);
|
||||
r.pc += 2;
|
||||
}
|
||||
prefetch();
|
||||
prefetch();
|
||||
}
|
||||
|
||||
auto M68000::instructionDIVS(EffectiveAddress from, DataRegister with) -> void {
|
||||
@@ -413,6 +427,7 @@ auto M68000::instructionDIVS(EffectiveAddress from, DataRegister with) -> void {
|
||||
n32 divisor = read<Word>(from) << 16, odivisor = divisor;
|
||||
|
||||
if(divisor == 0) {
|
||||
prefetched();
|
||||
return exception(Exception::DivisionByZero, Vector::DivisionByZero);
|
||||
}
|
||||
|
||||
@@ -489,6 +504,7 @@ auto M68000::instructionDIVU(EffectiveAddress from, DataRegister with) -> void {
|
||||
n32 divisor = read<Word>(from) << 16;
|
||||
|
||||
if(divisor == 0) {
|
||||
prefetched();
|
||||
return exception(Exception::DivisionByZero, Vector::DivisionByZero);
|
||||
}
|
||||
|
||||
@@ -1262,7 +1278,7 @@ auto M68000::instructionSWAP(DataRegister with) -> void {
|
||||
auto M68000::instructionTAS(EffectiveAddress with) -> void {
|
||||
n32 data;
|
||||
|
||||
if(with.mode == DataRegisterDirect) {
|
||||
if(lockable() || with.mode == DataRegisterDirect) {
|
||||
data = read<Byte, Hold>(with);
|
||||
prefetch();
|
||||
write<Byte>(with, data | 0x80);
|
||||
|
||||
@@ -50,21 +50,36 @@ auto M68000::supervisor() -> bool {
|
||||
|
||||
auto M68000::exception(u32 exception, u32 vector, u32 priority) -> void {
|
||||
r.stop = false;
|
||||
idle(10); //todo: not accurate
|
||||
|
||||
auto pc = r.pc;
|
||||
auto sr = readSR();
|
||||
|
||||
// register setup (+6 cyc)
|
||||
idle(6);
|
||||
n32 pc = r.pc - 4;
|
||||
n32 sr = readSR();
|
||||
if(!r.s) swap(r.a[7], r.sp);
|
||||
r.i = priority;
|
||||
r.s = 1;
|
||||
r.t = 0;
|
||||
|
||||
push<Long>(pc - 4);
|
||||
push<Word>(sr);
|
||||
// push pc low (+4 cyc)
|
||||
push<Word>(pc & 0x0000ffff);
|
||||
|
||||
// external interrupt handling
|
||||
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)
|
||||
r.i = priority;
|
||||
}
|
||||
|
||||
// push pc high & sr (+8 cyc)
|
||||
push<Long>(sr << 16 | pc >> 16);
|
||||
|
||||
// read vector address (+8 cyc)
|
||||
r.pc = read<Long>(vector << 2);
|
||||
// todo: if the vector address read causes an address error, this exception should be escalated
|
||||
|
||||
// prefetch (+8 cyc)
|
||||
prefetch();
|
||||
if(exception == Exception::Interrupt) idle(2); // dead cycles (+2 cyc)
|
||||
prefetch();
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,10 @@ struct M68000 {
|
||||
virtual auto wait(u32 clocks) -> void = 0;
|
||||
virtual auto read(n1 upper, n1 lower, n24 address, n16 data = 0) -> n16 = 0;
|
||||
virtual auto write(n1 upper, n1 lower, n24 address, n16 data) -> void = 0;
|
||||
virtual auto lockable() -> bool { return true; }
|
||||
|
||||
auto ird() const -> n16 { return r.ird; }
|
||||
auto irc() const -> n16 { return r.irc; }
|
||||
|
||||
enum : bool { User, Supervisor };
|
||||
enum : u32 { Byte, Word, Long };
|
||||
@@ -70,8 +72,8 @@ struct M68000 {
|
||||
M68000();
|
||||
auto power() -> void;
|
||||
auto supervisor() -> bool;
|
||||
auto exception(u32 exception, u32 vector, u32 priority = 7) -> void;
|
||||
auto interrupt(u32 vector, u32 priority = 7) -> void;
|
||||
auto exception(u32 exception, u32 vector, u32 priority = 0) -> void;
|
||||
auto interrupt(u32 vector, u32 priority = 0) -> void;
|
||||
|
||||
//registers.cpp
|
||||
struct DataRegister {
|
||||
|
||||
@@ -57,19 +57,24 @@ auto MOS6502::disassembleInstruction(maybe<n16> _pc) -> string {
|
||||
switch(auto opcode = readDebugger(pc)) {
|
||||
op(0x00, "brk", immediate)
|
||||
op(0x01, "ora", indirectX)
|
||||
op(0x04, "nop", zeroPage)
|
||||
op(0x05, "ora", zeroPage)
|
||||
op(0x06, "asl", zeroPage)
|
||||
op(0x08, "php", implied)
|
||||
op(0x09, "ora", immediate)
|
||||
op(0x0a, "asl", implied)
|
||||
op(0x0c, "nop", absolute)
|
||||
op(0x0d, "ora", absolute)
|
||||
op(0x0e, "asl", absolute)
|
||||
op(0x10, "bpl", branch)
|
||||
op(0x11, "ora", indirectY)
|
||||
op(0x14, "nop", zeroPageX)
|
||||
op(0x15, "ora", zeroPageX)
|
||||
op(0x16, "asl", zeroPageX)
|
||||
op(0x18, "clc", implied)
|
||||
op(0x19, "ora", absoluteY)
|
||||
op(0x1a, "nop", implied)
|
||||
op(0x1c, "nop", absoluteX)
|
||||
op(0x1d, "ora", absoluteX)
|
||||
op(0x1e, "asl", absoluteX)
|
||||
op(0x20, "jsr", absolute)
|
||||
@@ -85,14 +90,18 @@ auto MOS6502::disassembleInstruction(maybe<n16> _pc) -> string {
|
||||
op(0x2e, "rol", absolute)
|
||||
op(0x30, "bmi", branch)
|
||||
op(0x31, "and", indirectY)
|
||||
op(0x34, "nop", zeroPageX)
|
||||
op(0x35, "and", zeroPageX)
|
||||
op(0x36, "rol", zeroPageX)
|
||||
op(0x38, "sec", implied)
|
||||
op(0x39, "and", absoluteY)
|
||||
op(0x3a, "nop", implied)
|
||||
op(0x3c, "nop", absoluteX)
|
||||
op(0x3d, "and", absoluteX)
|
||||
op(0x3e, "rol", absoluteX)
|
||||
op(0x40, "rti", implied)
|
||||
op(0x41, "eor", indirectX)
|
||||
op(0x44, "nop", zeroPage)
|
||||
op(0x45, "eor", zeroPage)
|
||||
op(0x46, "lsr", zeroPage)
|
||||
op(0x48, "pha", implied)
|
||||
@@ -103,14 +112,18 @@ auto MOS6502::disassembleInstruction(maybe<n16> _pc) -> string {
|
||||
op(0x4e, "lsr", absolute)
|
||||
op(0x50, "bvc", branch)
|
||||
op(0x51, "eor", indirectY)
|
||||
op(0x54, "nop", zeroPageX)
|
||||
op(0x55, "eor", zeroPageX)
|
||||
op(0x56, "lsr", zeroPageX)
|
||||
op(0x58, "cli", implied)
|
||||
op(0x59, "eor", absoluteY)
|
||||
op(0x5a, "nop", implied)
|
||||
op(0x5c, "nop", absoluteX)
|
||||
op(0x5d, "eor", absoluteX)
|
||||
op(0x5e, "lsr", absoluteX)
|
||||
op(0x60, "rts", implied)
|
||||
op(0x61, "adc", indirectX)
|
||||
op(0x64, "nop", zeroPage)
|
||||
op(0x65, "adc", zeroPage)
|
||||
op(0x66, "ror", zeroPage)
|
||||
op(0x68, "pla", implied)
|
||||
@@ -121,17 +134,23 @@ auto MOS6502::disassembleInstruction(maybe<n16> _pc) -> string {
|
||||
op(0x6e, "ror", absolute)
|
||||
op(0x70, "bvs", branch)
|
||||
op(0x71, "adc", indirectY)
|
||||
op(0x74, "nop", zeroPageX)
|
||||
op(0x75, "adc", zeroPageX)
|
||||
op(0x76, "ror", zeroPageX)
|
||||
op(0x78, "sei", implied)
|
||||
op(0x79, "adc", absoluteY)
|
||||
op(0x7a, "nop", implied)
|
||||
op(0x7c, "nop", absoluteX)
|
||||
op(0x7d, "adc", absoluteX)
|
||||
op(0x7e, "ror", absoluteX)
|
||||
op(0x80, "nop", immediate)
|
||||
op(0x81, "sta", indirectX)
|
||||
op(0x82, "nop", immediate)
|
||||
op(0x84, "sty", zeroPage)
|
||||
op(0x85, "sta", zeroPage)
|
||||
op(0x86, "stx", zeroPage)
|
||||
op(0x88, "dey", implied)
|
||||
op(0x89, "nop", immediate)
|
||||
op(0x8a, "txa", implied)
|
||||
op(0x8c, "sty", absolute)
|
||||
op(0x8d, "sta", absolute)
|
||||
@@ -170,6 +189,7 @@ auto MOS6502::disassembleInstruction(maybe<n16> _pc) -> string {
|
||||
op(0xbe, "ldx", absoluteY)
|
||||
op(0xc0, "cpy", immediate)
|
||||
op(0xc1, "cmp", indirectX)
|
||||
op(0xc2, "nop", immediate)
|
||||
op(0xc4, "cpy", zeroPage)
|
||||
op(0xc5, "cmp", zeroPage)
|
||||
op(0xc6, "dec", zeroPage)
|
||||
@@ -181,14 +201,18 @@ auto MOS6502::disassembleInstruction(maybe<n16> _pc) -> string {
|
||||
op(0xce, "dec", absolute)
|
||||
op(0xd0, "bne", branch)
|
||||
op(0xd1, "cmp", indirectY)
|
||||
op(0xd4, "nop", zeroPageX)
|
||||
op(0xd5, "cmp", zeroPageX)
|
||||
op(0xd6, "dec", zeroPageX)
|
||||
op(0xd8, "cld", implied)
|
||||
op(0xd9, "cmp", absoluteY)
|
||||
op(0xda, "nop", implied)
|
||||
op(0xdc, "nop", absoluteX)
|
||||
op(0xdd, "cmp", absoluteX)
|
||||
op(0xde, "dec", absoluteX)
|
||||
op(0xe0, "cpx", immediate)
|
||||
op(0xe1, "sbc", indirectX)
|
||||
op(0xe2, "nop", immediate)
|
||||
op(0xe4, "cpx", zeroPage)
|
||||
op(0xe5, "sbc", zeroPage)
|
||||
op(0xe6, "inc", zeroPage)
|
||||
@@ -200,10 +224,13 @@ auto MOS6502::disassembleInstruction(maybe<n16> _pc) -> string {
|
||||
op(0xee, "inc", absolute)
|
||||
op(0xf0, "beq", branch)
|
||||
op(0xf1, "sbc", indirectY)
|
||||
op(0xf4, "nop", zeroPageX)
|
||||
op(0xf5, "sbc", zeroPageX)
|
||||
op(0xf6, "inc", zeroPageX)
|
||||
op(0xf8, "sed", implied)
|
||||
op(0xf9, "sbc", absoluteY)
|
||||
op(0xfa, "nop", implied)
|
||||
op(0xfc, "nop", absoluteX)
|
||||
op(0xfd, "sbc", absoluteX)
|
||||
op(0xfe, "inc", absoluteX)
|
||||
|
||||
|
||||
@@ -20,19 +20,24 @@ auto MOS6502::instruction() -> void {
|
||||
switch(code) {
|
||||
op(0x00, Break)
|
||||
op(0x01, IndirectXRead, fp(ORA), A)
|
||||
op(0x04, NoOperationZeroPage)
|
||||
op(0x05, ZeroPageRead, fp(ORA), A)
|
||||
op(0x06, ZeroPageModify, fp(ASL))
|
||||
op(0x08, PushP)
|
||||
op(0x09, Immediate, fp(ORA), A)
|
||||
op(0x0a, Implied, fp(ASL), A)
|
||||
op(0x0c, NoOperationAbsolute)
|
||||
op(0x0d, AbsoluteRead, fp(ORA), A)
|
||||
op(0x0e, AbsoluteModify, fp(ASL))
|
||||
op(0x10, Branch, N == 0)
|
||||
op(0x11, IndirectYRead, fp(ORA), A)
|
||||
op(0x14, NoOperationZeroPage, X)
|
||||
op(0x15, ZeroPageRead, fp(ORA), A, X)
|
||||
op(0x16, ZeroPageModify, fp(ASL), X)
|
||||
op(0x18, Clear, C)
|
||||
op(0x19, AbsoluteRead, fp(ORA), A, Y)
|
||||
op(0x1a, NoOperation)
|
||||
op(0x1c, NoOperationAbsolute, X)
|
||||
op(0x1d, AbsoluteRead, fp(ORA), A, X)
|
||||
op(0x1e, AbsoluteModify, fp(ASL), X)
|
||||
op(0x20, CallAbsolute)
|
||||
@@ -48,14 +53,18 @@ auto MOS6502::instruction() -> void {
|
||||
op(0x2e, AbsoluteModify, fp(ROL))
|
||||
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(0x38, Set, C)
|
||||
op(0x39, AbsoluteRead, fp(AND), A, Y)
|
||||
op(0x3a, NoOperation)
|
||||
op(0x3c, NoOperationAbsolute, X)
|
||||
op(0x3d, AbsoluteRead, fp(AND), A, X)
|
||||
op(0x3e, AbsoluteModify, fp(ROL), X)
|
||||
op(0x40, ReturnInterrupt)
|
||||
op(0x41, IndirectXRead, fp(EOR), A)
|
||||
op(0x44, NoOperationZeroPage)
|
||||
op(0x45, ZeroPageRead, fp(EOR), A)
|
||||
op(0x46, ZeroPageModify, fp(LSR))
|
||||
op(0x48, Push, A)
|
||||
@@ -66,14 +75,18 @@ auto MOS6502::instruction() -> void {
|
||||
op(0x4e, AbsoluteModify, fp(LSR))
|
||||
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(0x58, Clear, I)
|
||||
op(0x59, AbsoluteRead, fp(EOR), A, Y)
|
||||
op(0x5a, NoOperation)
|
||||
op(0x5c, NoOperationAbsolute, X)
|
||||
op(0x5d, AbsoluteRead, fp(EOR), A, X)
|
||||
op(0x5e, AbsoluteModify, fp(LSR), 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(0x68, Pull, A)
|
||||
@@ -84,17 +97,23 @@ auto MOS6502::instruction() -> void {
|
||||
op(0x6e, AbsoluteModify, fp(ROR))
|
||||
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(0x78, Set, I)
|
||||
op(0x79, AbsoluteRead, fp(ADC), A, Y)
|
||||
op(0x7a, NoOperation)
|
||||
op(0x7c, NoOperationAbsolute, X)
|
||||
op(0x7d, AbsoluteRead, fp(ADC), A, X)
|
||||
op(0x7e, AbsoluteModify, fp(ROR), X)
|
||||
op(0x80, NoOperationImmediate)
|
||||
op(0x81, IndirectXWrite, A)
|
||||
op(0x82, NoOperationImmediate)
|
||||
op(0x84, ZeroPageWrite, Y)
|
||||
op(0x85, ZeroPageWrite, A)
|
||||
op(0x86, ZeroPageWrite, X)
|
||||
op(0x88, Implied, fp(DEC), Y)
|
||||
op(0x89, NoOperationImmediate)
|
||||
op(0x8a, Transfer, X, A, 1)
|
||||
op(0x8c, AbsoluteWrite, Y)
|
||||
op(0x8d, AbsoluteWrite, A)
|
||||
@@ -133,6 +152,7 @@ auto MOS6502::instruction() -> void {
|
||||
op(0xbe, AbsoluteRead, fp(LD), X, Y)
|
||||
op(0xc0, Immediate, fp(CPY), Y)
|
||||
op(0xc1, IndirectXRead, fp(CMP), A)
|
||||
op(0xc2, NoOperationImmediate)
|
||||
op(0xc4, ZeroPageRead, fp(CPY), Y)
|
||||
op(0xc5, ZeroPageRead, fp(CMP), A)
|
||||
op(0xc6, ZeroPageModify, fp(DEC))
|
||||
@@ -144,14 +164,18 @@ auto MOS6502::instruction() -> void {
|
||||
op(0xce, AbsoluteModify, fp(DEC))
|
||||
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(0xd8, Clear, D)
|
||||
op(0xd9, AbsoluteRead, fp(CMP), A, Y)
|
||||
op(0xda, NoOperation)
|
||||
op(0xdc, NoOperationAbsolute, X)
|
||||
op(0xdd, AbsoluteRead, fp(CMP), A, X)
|
||||
op(0xde, AbsoluteModify, fp(DEC), 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))
|
||||
@@ -163,10 +187,13 @@ auto MOS6502::instruction() -> void {
|
||||
op(0xee, AbsoluteModify, fp(INC))
|
||||
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(0xf8, Set, D)
|
||||
op(0xf9, AbsoluteRead, fp(SBC), A, Y)
|
||||
op(0xfa, NoOperation)
|
||||
op(0xfc, NoOperationAbsolute, X)
|
||||
op(0xfd, AbsoluteRead, fp(SBC), A, X)
|
||||
op(0xfe, AbsoluteModify, fp(INC), X)
|
||||
}
|
||||
|
||||
@@ -139,6 +139,34 @@ auto MOS6502::instructionNoOperation() -> void {
|
||||
L idle();
|
||||
}
|
||||
|
||||
auto MOS6502::instructionNoOperationAbsolute() -> void {
|
||||
n16 absolute = operand();
|
||||
absolute |= operand() << 8;
|
||||
L read(absolute);
|
||||
}
|
||||
|
||||
auto MOS6502::instructionNoOperationAbsolute(n8 index) -> void {
|
||||
n16 absolute = operand();
|
||||
absolute |= operand() << 8;
|
||||
idlePageCrossed(absolute, absolute + index);
|
||||
L read(absolute + index);
|
||||
}
|
||||
|
||||
auto MOS6502::instructionNoOperationImmediate() -> void {
|
||||
L operand();
|
||||
}
|
||||
|
||||
auto MOS6502::instructionNoOperationZeroPage() -> void {
|
||||
auto zeroPage = operand();
|
||||
L load(zeroPage);
|
||||
}
|
||||
|
||||
auto MOS6502::instructionNoOperationZeroPage(n8 index) -> void {
|
||||
auto zeroPage = operand();
|
||||
load(zeroPage);
|
||||
L load(zeroPage + index);
|
||||
}
|
||||
|
||||
auto MOS6502::instructionPull(n8& data) -> void {
|
||||
idle();
|
||||
idle();
|
||||
|
||||
@@ -70,6 +70,11 @@ struct MOS6502 {
|
||||
auto instructionJumpAbsolute() -> void;
|
||||
auto instructionJumpIndirect() -> void;
|
||||
auto instructionNoOperation() -> void;
|
||||
auto instructionNoOperationAbsolute() -> void;
|
||||
auto instructionNoOperationAbsolute(n8 index) -> void;
|
||||
auto instructionNoOperationImmediate() -> void;
|
||||
auto instructionNoOperationZeroPage() -> void;
|
||||
auto instructionNoOperationZeroPage(n8 index) -> void;
|
||||
auto instructionPull(n8& data) -> void;
|
||||
auto instructionPullP() -> void;
|
||||
auto instructionPush(n8& data) -> void;
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
auto SH2::Recompiler::invalidate(u32 address) -> void {
|
||||
auto pool = pools[address >> 8 & 0xffffff];
|
||||
if(!pool) return;
|
||||
memory::jitprotect(false);
|
||||
pool->blocks[address >> 1 & 0x7f] = nullptr;
|
||||
memory::jitprotect(true);
|
||||
}
|
||||
|
||||
auto SH2::Recompiler::pool(u32 address) -> Pool* {
|
||||
auto& pool = pools[address >> 8 & 0xffffff];
|
||||
if(!pool) pool = (Pool*)allocator.acquire(sizeof(Pool));
|
||||
@@ -7,7 +15,9 @@ auto SH2::Recompiler::pool(u32 address) -> Pool* {
|
||||
auto SH2::Recompiler::block(u32 address) -> Block* {
|
||||
if(auto block = pool(address)->blocks[address >> 1 & 0x7f]) return block;
|
||||
auto block = emit(address);
|
||||
return pool(address)->blocks[address >> 1 & 0x7f] = block;
|
||||
pool(address)->blocks[address >> 1 & 0x7f] = block;
|
||||
memory::jitprotect(true);
|
||||
return block;
|
||||
}
|
||||
|
||||
alwaysinline auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
@@ -25,34 +35,37 @@ alwaysinline auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define CCR mem(sreg(1), offsetof(Registers, CCR))
|
||||
|
||||
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);
|
||||
reset();
|
||||
}
|
||||
|
||||
auto block = (Block*)allocator.acquire(sizeof(Block));
|
||||
block->code = allocator.acquire();
|
||||
bind({block->code, allocator.available()});
|
||||
beginFunction(2);
|
||||
|
||||
bool hasBranched = 0;
|
||||
while(true) {
|
||||
u16 instruction = self.readWord(address);
|
||||
bool branched = emitInstruction(instruction);
|
||||
mov(rax, mem64(&self.CCR));
|
||||
inc(rax);
|
||||
mov(mem64(&self.CCR), rax);
|
||||
call(&SH2::instructionEpilogue, &self);
|
||||
add64(CCR, CCR, imm(1));
|
||||
call(&SH2::instructionEpilogue);
|
||||
address += 2;
|
||||
if(hasBranched || (address & 0xfe) == 0) break; //block boundary
|
||||
hasBranched = branched;
|
||||
test(al, al);
|
||||
jz(imm8(1));
|
||||
ret();
|
||||
testJumpEpilog();
|
||||
}
|
||||
ret();
|
||||
jumpEpilog();
|
||||
|
||||
memory::jitprotect(false);
|
||||
block->code = endFunction();
|
||||
|
||||
allocator.reserve(size());
|
||||
return block;
|
||||
}
|
||||
|
||||
#undef CCR
|
||||
@@ -67,7 +67,7 @@ auto SH2::illegalInstruction() -> void {
|
||||
push(SR);
|
||||
push(PC);
|
||||
jump(readLong(VBR + vector * 4) + 4);
|
||||
debug(unusual, "[SH2] illegal instruction: 0x", hex(busReadWord(PC - 4), 4L));
|
||||
debug(unusual, "[SH2] illegal instruction: 0x", hex(busReadWord(PC - 4), 4L), " @ 0x", hex(PC - 4));
|
||||
}
|
||||
|
||||
auto SH2::illegalSlotInstruction() -> void {
|
||||
|
||||
@@ -45,7 +45,7 @@ auto SH2::instruction() -> void {
|
||||
}
|
||||
}
|
||||
|
||||
auto SH2::instructionEpilogue() -> bool {
|
||||
auto SH2::instructionEpilogue() -> s32 {
|
||||
switch(PPM) {
|
||||
case Branch::Step: PC = PC + 2; return 0;
|
||||
case Branch::Slot: PC = PC + 2; PPM = Branch::Take; return 0;
|
||||
|
||||
@@ -5,9 +5,10 @@ auto SH2::ADD(u32 m, u32 n) -> void {
|
||||
|
||||
//ADDC Rm,Rn
|
||||
auto SH2::ADDC(u32 m, u32 n) -> void {
|
||||
bool c = (u64)R[n] + R[m] >> 32;
|
||||
R[n] = R[n] + R[m] + SR.T;
|
||||
SR.T = c;
|
||||
u32 a = R[n];
|
||||
u32 b = a + R[m];
|
||||
R[n] = b + SR.T;
|
||||
SR.T = a > b || b > R[n];
|
||||
}
|
||||
|
||||
//ADD #imm,Rn
|
||||
@@ -17,9 +18,11 @@ auto SH2::ADDI(u32 i, u32 n) -> void {
|
||||
|
||||
//ADDV Rm,Rn
|
||||
auto SH2::ADDV(u32 m, u32 n) -> void {
|
||||
bool v = ~(R[n] ^ R[m]) & (R[n] ^ R[n] + R[m]) >> 31;
|
||||
u32 d = ((s32)R[n] < 0);
|
||||
u32 s = ((s32)R[m] < 0) + d;
|
||||
R[n] += R[m];
|
||||
SR.T = v;
|
||||
u32 r = ((s32)R[n] < 0) + d;
|
||||
SR.T = s != 1 ? r == 1 : 0;
|
||||
}
|
||||
|
||||
//AND Rm,Rn
|
||||
@@ -140,7 +143,11 @@ auto SH2::CMPPZ(u32 n) -> void {
|
||||
|
||||
//CMP/STR Rm,Rn
|
||||
auto SH2::CMPSTR(u32 m, u32 n) -> void {
|
||||
SR.T = bool(R[n] ^ R[m]);
|
||||
u32 t = R[n] ^ R[m];
|
||||
SR.T = !(t & 0xff << 24)
|
||||
|| !(t & 0xff << 16)
|
||||
|| !(t & 0xff << 8)
|
||||
|| !(t & 0xff);
|
||||
}
|
||||
|
||||
//DIV0S Rm,Rn
|
||||
@@ -353,8 +360,9 @@ auto SH2::MOVBLG(u32 d) -> void {
|
||||
|
||||
//MOV.B Rm,@-Rn
|
||||
auto SH2::MOVBM(u32 m, u32 n) -> void {
|
||||
u32 t = R[m];
|
||||
R[n] -= 1;
|
||||
writeByte(R[n], R[m]);
|
||||
writeByte(R[n], t);
|
||||
}
|
||||
|
||||
//MOV.B @Rm+,Rn
|
||||
@@ -405,8 +413,9 @@ auto SH2::MOVWLG(u32 d) -> void {
|
||||
|
||||
//MOV.W Rm,@-Rn
|
||||
auto SH2::MOVWM(u32 m, u32 n) -> void {
|
||||
u32 t = R[m];
|
||||
R[n] -= 2;
|
||||
writeWord(R[n], R[m]);
|
||||
writeWord(R[n], t);
|
||||
}
|
||||
|
||||
//MOV.W @Rm+,Rn
|
||||
@@ -457,8 +466,9 @@ auto SH2::MOVLLG(u32 d) -> void {
|
||||
|
||||
//MOV.L Rm,@-Rn
|
||||
auto SH2::MOVLM(u32 m, u32 n) -> void {
|
||||
u32 t = R[m];
|
||||
R[n] -= 4;
|
||||
writeLong(R[n], R[m]);
|
||||
writeLong(R[n], t);
|
||||
}
|
||||
|
||||
//MOV.L @Rm+,Rn
|
||||
@@ -536,7 +546,7 @@ auto SH2::NEG(u32 m, u32 n) -> void {
|
||||
auto SH2::NEGC(u32 m, u32 n) -> void {
|
||||
u32 t = 0 - R[m];
|
||||
R[n] = t - SR.T;
|
||||
SR.T = (s32)t >= 0 || t < R[n];
|
||||
SR.T = 0 < t || t < R[n];
|
||||
}
|
||||
|
||||
//NOP
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -133,6 +133,7 @@ auto SH2::WDT::serialize(serializer& s) -> void {
|
||||
s(rstcsr.rste);
|
||||
s(rstcsr.wovf);
|
||||
s(wtcnt);
|
||||
s(pendingIRQ);
|
||||
}
|
||||
|
||||
auto SH2::UBC::serialize(serializer& s) -> void {
|
||||
|
||||
@@ -3,14 +3,26 @@
|
||||
|
||||
namespace ares {
|
||||
|
||||
#define SP R[15]
|
||||
#define SP R[15]
|
||||
#define R regs.R
|
||||
#define PC regs.PC
|
||||
#define PR regs.PR
|
||||
#define GBR regs.GBR
|
||||
#define VBR regs.VBR
|
||||
#define MAC regs.MAC
|
||||
#define MACL regs.MACL
|
||||
#define MACH regs.MACH
|
||||
#define CCR regs.CCR
|
||||
#define SR regs.SR
|
||||
#define PPC regs.PPC
|
||||
#define PPM regs.PPM
|
||||
#define ET regs.ET
|
||||
#define ID regs.ID
|
||||
|
||||
#include "sh7604/sh7604.cpp"
|
||||
#include "exceptions.cpp"
|
||||
#include "instruction.cpp"
|
||||
#include "instructions.cpp"
|
||||
#include "cached.cpp"
|
||||
//#include "recompiler.cpp"
|
||||
#include "serialization.cpp"
|
||||
#include "disassembler.cpp"
|
||||
|
||||
@@ -30,6 +42,7 @@ auto SH2::power(bool reset) -> void {
|
||||
SR.M = undefined;
|
||||
PPC = 0;
|
||||
PPM = Branch::Step;
|
||||
ET = 0;
|
||||
ID = 0;
|
||||
exceptions = !reset ? ResetCold : ResetWarm;
|
||||
|
||||
@@ -47,10 +60,29 @@ auto SH2::power(bool reset) -> void {
|
||||
cache.power();
|
||||
|
||||
if constexpr(Accuracy::Recompiler) {
|
||||
auto buffer = ares::Memory::FixedAllocator::get().acquire(512_MiB);
|
||||
recompiler.allocator.resize(512_MiB, bump_allocator::executable | bump_allocator::zero_fill, buffer);
|
||||
auto buffer = ares::Memory::FixedAllocator::get().tryAcquire(64_MiB);
|
||||
recompiler.allocator.resize(64_MiB, bump_allocator::executable | bump_allocator::zero_fill, buffer);
|
||||
recompiler.reset();
|
||||
}
|
||||
}
|
||||
|
||||
#undef SP
|
||||
#undef R
|
||||
#undef PC
|
||||
#undef PR
|
||||
#undef GBR
|
||||
#undef VBR
|
||||
#undef MAC
|
||||
#undef MACL
|
||||
#undef MACH
|
||||
#undef CCR
|
||||
#undef SR
|
||||
#undef PPC
|
||||
#undef PPM
|
||||
#undef ET
|
||||
#undef ID
|
||||
|
||||
//#include "cached.cpp"
|
||||
#include "recompiler.cpp"
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <nall/recompiler/amd64/amd64.hpp>
|
||||
#include <nall/recompiler/generic/generic.hpp>
|
||||
|
||||
//Hitachi SH-2
|
||||
|
||||
@@ -16,7 +16,7 @@ struct SH2 {
|
||||
virtual auto busWriteLong(u32 address, u32 data) -> void = 0;
|
||||
|
||||
auto inDelaySlot() const -> bool {
|
||||
return PPM != Branch::Step;
|
||||
return regs.PPM != Branch::Step;
|
||||
}
|
||||
|
||||
//sh2.cpp
|
||||
@@ -39,7 +39,7 @@ struct SH2 {
|
||||
auto jump(u32 pc) -> void;
|
||||
auto branch(u32 pc) -> void;
|
||||
auto delaySlot(u32 pc) -> void;
|
||||
auto instructionEpilogue() -> bool;
|
||||
auto instructionEpilogue() -> s32;
|
||||
auto instruction() -> void;
|
||||
auto execute(u16 opcode) -> void;
|
||||
|
||||
@@ -220,21 +220,23 @@ struct SH2 {
|
||||
u32 M;
|
||||
};
|
||||
|
||||
u32 R[16]; //general purpose registers
|
||||
u32 PC; //program counter
|
||||
u32 PR; //procedure register
|
||||
u32 GBR; //global base register
|
||||
u32 VBR; //vector base register
|
||||
union {
|
||||
u64 MAC; //multiply-and-accumulate register
|
||||
struct { u32 order_msb2(MACH, MACL); };
|
||||
};
|
||||
u32 CCR; //clock counter register
|
||||
S32 SR; //status register
|
||||
u32 PPC; //program counter for delay slots
|
||||
u32 PPM; //delay slot mode
|
||||
u32 ET; //exception triggered flag
|
||||
u32 ID; //interrupts disabled flag
|
||||
struct Registers {
|
||||
u32 R[16]; //general purpose registers
|
||||
u32 PC; //program counter
|
||||
u32 PR; //procedure register
|
||||
u32 GBR; //global base register
|
||||
u32 VBR; //vector base register
|
||||
union {
|
||||
u64 MAC; //multiply-and-accumulate register
|
||||
struct { u32 order_msb2(MACH, MACL); };
|
||||
};
|
||||
u32 CCR; //clock counter register
|
||||
S32 SR; //status register
|
||||
u32 PPC; //program counter for delay slots
|
||||
u32 PPM; //delay slot mode
|
||||
u32 ET; //exception triggered flag
|
||||
u32 ID; //interrupts disabled flag
|
||||
} regs;
|
||||
|
||||
enum : u32 {
|
||||
ResetCold = 1 << 0,
|
||||
@@ -244,14 +246,13 @@ struct SH2 {
|
||||
};
|
||||
u32 exceptions = 0; //delayed exception flags
|
||||
|
||||
struct Recompiler : recompiler::amd64 {
|
||||
using recompiler::amd64::call;
|
||||
struct Recompiler : recompiler::generic {
|
||||
SH2& self;
|
||||
Recompiler(SH2& self) : self(self) {}
|
||||
Recompiler(SH2& self) : self(self), generic(allocator) {}
|
||||
|
||||
struct Block {
|
||||
auto execute(SH2& self) -> void {
|
||||
((void (*)(u32*, SH2*))code)(&self.R[0], &self);
|
||||
((void (*)(SH2*, Registers*))code)(&self, &self.regs);
|
||||
}
|
||||
|
||||
u8* code;
|
||||
@@ -265,25 +266,13 @@ struct SH2 {
|
||||
for(u32 index : range(1 << 24)) pools[index] = nullptr;
|
||||
}
|
||||
|
||||
auto invalidate(u32 address) -> void {
|
||||
pool(address)->blocks[address >> 1 & 0x7f] = nullptr;
|
||||
}
|
||||
|
||||
auto invalidate(u32 address) -> void;
|
||||
auto pool(u32 address) -> Pool*;
|
||||
auto block(u32 address) -> Block*;
|
||||
auto emit(u32 address) -> Block*;
|
||||
auto emitInstruction(u16 opcode) -> bool;
|
||||
|
||||
template<typename V, typename... P>
|
||||
alwaysinline auto call(V (SH2::*function)(P...)) -> void {
|
||||
static_assert(sizeof...(P) <= 5);
|
||||
if constexpr(ABI::Windows) {
|
||||
if constexpr(sizeof...(P) >= 5) mov(dis8(rsp, 0x28), ra5);
|
||||
if constexpr(sizeof...(P) >= 4) mov(dis8(rsp, 0x20), ra4);
|
||||
}
|
||||
mov(ra0, rbp);
|
||||
call(imm64{function}, rax);
|
||||
}
|
||||
auto getSR(reg dst) -> void;
|
||||
auto setSR(reg src) -> void;
|
||||
|
||||
bump_allocator allocator;
|
||||
Pool* pools[1 << 24];
|
||||
|
||||
@@ -22,6 +22,13 @@ print("dreq1\n");
|
||||
}
|
||||
}
|
||||
|
||||
if(self->wdt.pendingIRQ) {
|
||||
if(self->SR.I < ipra.wdtip) {
|
||||
self->interrupt(ipra.wdtip, vcrwdt.witv);
|
||||
self->wdt.pendingIRQ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(self->sci.pendingTransmitEmptyIRQ) {
|
||||
if(self->SR.I < iprb.sciip) {
|
||||
self->interrupt(iprb.sciip, vcrb.stxv);
|
||||
|
||||
@@ -675,17 +675,26 @@ auto SH2::internalWriteByte(u32 address, n8 data) -> void {
|
||||
dmac.drcr[1] = data.bit(0,1);
|
||||
return;
|
||||
|
||||
//WTCSR: watchdog timer control/status register
|
||||
//WTCSR/WTCNT select
|
||||
case 0xffff'fe80:
|
||||
wdt.wtcsr.cks = data.bit(0,2);
|
||||
wdt.wtcsr.tme = data.bit(5);
|
||||
wdt.wtcsr.wtit = data.bit(6);
|
||||
wdt.wtcsr.ovf &= data.bit(7);
|
||||
wdt.select = data.bit(0,7);
|
||||
return;
|
||||
|
||||
//WTCNT: watchdog timer counter
|
||||
//WTCSR/WTCNT data
|
||||
case 0xffff'fe81:
|
||||
wdt.wtcnt = data.bit(0,7);
|
||||
//WTCSR: watchdog timer control/status register
|
||||
if(wdt.select == 0xa5) {
|
||||
wdt.wtcsr.cks = data.bit(0,2);
|
||||
wdt.wtcsr.tme = data.bit(5);
|
||||
wdt.wtcsr.wtit = data.bit(6);
|
||||
wdt.wtcsr.ovf &= data.bit(7);
|
||||
if(!wdt.wtcsr.tme) wdt.wtcnt = 0;
|
||||
}
|
||||
|
||||
//WTCNT: watchdog timer counter
|
||||
if(wdt.select == 0x5a) {
|
||||
wdt.wtcnt = data.bit(0,7);
|
||||
}
|
||||
return;
|
||||
|
||||
//RSTCSR: reset control/status register
|
||||
|
||||
@@ -112,8 +112,8 @@
|
||||
n7 fovv; //FRT overflow interrupt vector number
|
||||
} vcrd;
|
||||
struct VCRWDT { //vector number setting register WDT
|
||||
n4 bcmv; //BSC compare match interrupt vector number
|
||||
n4 witv; //WDT interval interrupt vector number
|
||||
n7 bcmv; //BSC compare match interrupt vector number
|
||||
n7 witv; //WDT interval interrupt vector number
|
||||
} vcrwdt;
|
||||
} intc;
|
||||
|
||||
@@ -210,6 +210,9 @@
|
||||
|
||||
//watchdog timer
|
||||
struct WDT {
|
||||
//timer.cpp
|
||||
auto run() -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
@@ -225,6 +228,11 @@
|
||||
n1 wovf; //watchdog timer overflow flag
|
||||
} rstcsr;
|
||||
n8 wtcnt; //watchdog timer counter
|
||||
|
||||
//internal
|
||||
n32 counter;
|
||||
n1 pendingIRQ;
|
||||
n8 select;
|
||||
} wdt;
|
||||
|
||||
//user break controller
|
||||
|
||||
@@ -26,3 +26,18 @@ auto SH2::FRT::run() -> void {
|
||||
}
|
||||
}
|
||||
}
|
||||
auto SH2::WDT::run() -> void {
|
||||
static constexpr u32 frequencies[8] = {2, 64, 128, 256, 512, 1024, 2096, 8192};
|
||||
|
||||
if(!wtcsr.tme) return;
|
||||
if(wtcsr.wtit) return; // Watchdog mode currently unsupported
|
||||
|
||||
if(++counter >= frequencies[wtcsr.cks]) {
|
||||
counter -= frequencies[wtcsr.cks];
|
||||
|
||||
if(++wtcnt == 0) {
|
||||
wtcsr.ovf = 1;
|
||||
pendingIRQ = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,25 @@ auto TMS9918::Background::run(n8 hoffset, n8 voffset) -> void {
|
||||
}
|
||||
|
||||
auto TMS9918::Background::text1(n8 hoffset, n8 voffset) -> void {
|
||||
debug(unimplemented, "[TMS9918::Background::text1]");
|
||||
// Offset by 6px and blank the last 10px to centre the display
|
||||
i32 x = hoffset - 6;
|
||||
if (x < 0) return;
|
||||
if (hoffset > 246) return;
|
||||
|
||||
n14 nameAddress = (voffset.bit(3,7) * 40) + (x / 6);
|
||||
nameAddress.bit(10,13) = io.nameTableAddress;
|
||||
|
||||
n8 pattern = self.vram.read(nameAddress);
|
||||
|
||||
n14 patternAddress;
|
||||
patternAddress.bit( 0, 2) = voffset.bit(0,2);
|
||||
patternAddress.bit( 3,10) = pattern;
|
||||
patternAddress.bit(11,13) = io.patternTableAddress;
|
||||
|
||||
n3 index = 7 - (x % 6);
|
||||
if(self.vram.read(patternAddress).bit(index)) {
|
||||
output.color = self.dac.io.colorForeground;
|
||||
}
|
||||
}
|
||||
|
||||
auto TMS9918::Background::graphics1(n8 hoffset, n8 voffset) -> void {
|
||||
|
||||
@@ -32,6 +32,9 @@ auto TMS9918::Sprite::setup(n8 voffset) -> void {
|
||||
}
|
||||
|
||||
auto TMS9918::Sprite::run(n8 hoffset, n8 voffset) -> void {
|
||||
// Text mode does not support sprites
|
||||
if (self.videoMode() == 1) return;
|
||||
|
||||
output = {};
|
||||
|
||||
n4 color;
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
struct Bandai74161 : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "BANDAI-74161" ) return new Bandai74161(Revision::B74161);
|
||||
if(id == "BANDAI-74161A") return new Bandai74161(Revision::B74161A);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Memory::Readable<n8> programROM;
|
||||
Memory::Readable<n8> characterROM;
|
||||
|
||||
enum class Revision : u32 {
|
||||
B74161,
|
||||
B74161A,
|
||||
} revision;
|
||||
|
||||
Bandai74161(Revision revision) : revision(revision) {}
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(programROM, "program.rom");
|
||||
Interface::load(characterROM, "character.rom");
|
||||
mirror = pak->attribute("mirror") == "vertical";
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
if(address < 0x8000) return data;
|
||||
n3 bank = (address < 0xc000 ? programBank : (n3)0x7);
|
||||
return programROM.read(bank << 14 | (n14)address);
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address < 0x8000) return;
|
||||
characterBank = data.bit(0,3);
|
||||
programBank = data.bit(4,6);
|
||||
nametableBank = data.bit(7);
|
||||
}
|
||||
|
||||
auto addressCIRAM(n32 address) const -> n32 {
|
||||
if(revision == Revision::B74161A) {
|
||||
return nametableBank << 10 | (n10)address;
|
||||
}
|
||||
return address >> !mirror & 0x0400 | (n10)address;
|
||||
}
|
||||
|
||||
auto readCHR(n32 address, n8 data) -> n8 override {
|
||||
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
|
||||
return characterROM.read(characterBank << 13 | (n13)address);
|
||||
}
|
||||
|
||||
auto writeCHR(n32 address, n8 data) -> void override {
|
||||
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(mirror);
|
||||
s(programBank);
|
||||
s(characterBank);
|
||||
s(nametableBank);
|
||||
}
|
||||
|
||||
n1 mirror;
|
||||
n3 programBank;
|
||||
n4 characterBank;
|
||||
n1 nametableBank;
|
||||
};
|
||||
@@ -6,123 +6,80 @@ struct BandaiFCG : Interface {
|
||||
|
||||
Memory::Readable<n8> programROM;
|
||||
Memory::Readable<n8> characterROM;
|
||||
Memory::Writable<n8> characterRAM;
|
||||
M24C eeprom;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(programROM, "program.rom");
|
||||
Interface::load(characterROM, "character.rom");
|
||||
Interface::load(characterRAM, "character.ram");
|
||||
if(auto fp = pak->read("save.eeprom")) {
|
||||
eeprom.load(M24C::Type::X24C01);
|
||||
fp->read({eeprom.memory, eeprom.size()});
|
||||
}
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
Interface::save(characterRAM, "save.ram");
|
||||
if(auto fp = pak->write("save.eeprom")) {
|
||||
fp->write({eeprom.memory, eeprom.size()});
|
||||
}
|
||||
}
|
||||
|
||||
auto main() -> void override {
|
||||
if(irqCounterEnable) {
|
||||
if(irqEnable) {
|
||||
if(--irqCounter == 0xffff) {
|
||||
cpu.irqLine(1);
|
||||
irqCounterEnable = false;
|
||||
irqEnable = false;
|
||||
}
|
||||
}
|
||||
tick();
|
||||
}
|
||||
|
||||
auto addressCIRAM(n32 address) const -> n32 {
|
||||
switch(mirror) {
|
||||
case 0: return address >> 0 & 0x0400 | address & 0x03ff;
|
||||
case 1: return address >> 1 & 0x0400 | address & 0x03ff;
|
||||
case 2: return 0x0000 | address & 0x03ff;
|
||||
case 3: return 0x0400 | address & 0x03ff;
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
if(address >= 0x6000 && address <= 0x7fff) {
|
||||
data.bit(4) = eeprom.read();
|
||||
return data;
|
||||
}
|
||||
|
||||
if(address & 0x8000) {
|
||||
n1 region = bool(address & 0x4000);
|
||||
n4 bank = (region == 0 ? programBank : (n4)0x0f);
|
||||
return programROM.read(bank << 14 | (n14)address);
|
||||
}
|
||||
|
||||
return data;
|
||||
if(address < 0x8000) return data;
|
||||
n4 bank = (address < 0xc000 ? programBank : (n4)0x0f);
|
||||
return programROM.read(bank << 14 | (n14)address);
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address >= 0x6000) {
|
||||
switch(address & 0xf) {
|
||||
case 0x0: characterBank[0] = data; break;
|
||||
case 0x1: characterBank[1] = data; break;
|
||||
case 0x2: characterBank[2] = data; break;
|
||||
case 0x3: characterBank[3] = data; break;
|
||||
case 0x4: characterBank[4] = data; break;
|
||||
case 0x5: characterBank[5] = data; break;
|
||||
case 0x6: characterBank[6] = data; break;
|
||||
case 0x7: characterBank[7] = data; break;
|
||||
case 0x8: programBank = data.bit(0,3); break;
|
||||
case 0x9: mirror = data.bit(0,1); break;
|
||||
case 0xa:
|
||||
cpu.irqLine(0);
|
||||
irqCounterEnable = data.bit(0);
|
||||
irqCounter = irqLatch;
|
||||
break;
|
||||
case 0xb: irqLatch.byte(0) = data; break;
|
||||
case 0xc: irqLatch.byte(1) = data; break;
|
||||
case 0xd:
|
||||
eeprom.clock = data.bit(5);
|
||||
eeprom.data = data.bit(6);
|
||||
eeprom.write();
|
||||
break;
|
||||
}
|
||||
switch(address & 0xe00f) {
|
||||
case 0x6000: characterBank[0] = data; break;
|
||||
case 0x6001: characterBank[1] = data; break;
|
||||
case 0x6002: characterBank[2] = data; break;
|
||||
case 0x6003: characterBank[3] = data; break;
|
||||
case 0x6004: characterBank[4] = data; break;
|
||||
case 0x6005: characterBank[5] = data; break;
|
||||
case 0x6006: characterBank[6] = data; break;
|
||||
case 0x6007: characterBank[7] = data; break;
|
||||
case 0x6008: programBank = data.bit(0,3); break;
|
||||
case 0x6009: mirror = data.bit(0,1); break;
|
||||
case 0x600a:
|
||||
irqEnable = data.bit(0);
|
||||
cpu.irqLine(0);
|
||||
break;
|
||||
case 0x600b: irqCounter.byte(0) = data; break;
|
||||
case 0x600c: irqCounter.byte(1) = data; break;
|
||||
}
|
||||
}
|
||||
|
||||
auto addressCIRAM(n32 address) const -> n32 {
|
||||
switch(mirror) {
|
||||
case 0: return address >> 0 & 0x0400 | (n10)address;
|
||||
case 1: return address >> 1 & 0x0400 | (n10)address;
|
||||
case 2: return 0x0000 | (n10)address;
|
||||
case 3: return 0x0400 | (n10)address;
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto readCHR(n32 address, n8 data) -> n8 override {
|
||||
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
|
||||
address = characterBank[address >> 10] << 10 | (n10)address;
|
||||
if(characterROM) return characterROM.read(address);
|
||||
if(characterRAM) return characterRAM.read(address);
|
||||
return data;
|
||||
return characterROM.read(address);
|
||||
}
|
||||
|
||||
auto writeCHR(n32 address, n8 data) -> void override {
|
||||
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
|
||||
address = characterBank[address >> 10] << 10 | (n10)address;
|
||||
if(characterRAM) return characterRAM.write(address, data);
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
eeprom.power();
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(characterRAM);
|
||||
s(eeprom);
|
||||
s(characterBank);
|
||||
s(programBank);
|
||||
s(mirror);
|
||||
s(irqCounterEnable);
|
||||
s(irqEnable);
|
||||
s(irqCounter);
|
||||
s(irqLatch);
|
||||
}
|
||||
|
||||
n8 characterBank[8];
|
||||
n4 programBank;
|
||||
n2 mirror;
|
||||
n1 irqCounterEnable;
|
||||
n1 irqEnable;
|
||||
n16 irqCounter;
|
||||
n16 irqLatch;
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
struct BandaiKaraoke : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "BANDAI-KARAOKE") return new BandaiKaraoke;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Memory::Readable<n8> programROM;
|
||||
Memory::Readable<n8> optionROM;
|
||||
Memory::Writable<n8> characterRAM;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(programROM, "program.rom");
|
||||
Interface::load(optionROM, "option.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 < 0x6000) return data;
|
||||
if(address < 0x8000) {
|
||||
data.bit(0) = 1; //A button
|
||||
data.bit(1) = 1; //B button
|
||||
//data.bit(2) = 1; //1-bit ADC
|
||||
return data;
|
||||
}
|
||||
|
||||
if(address < 0xc000) {
|
||||
if(!optionEnable) return programROM.read(programBank << 14 | (n14)address);
|
||||
if(optionROM) return optionROM.read(programBank << 14 | (n14)address);
|
||||
return data;
|
||||
}
|
||||
return programROM.read(0xf << 14 | (n14)address);
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address < 0x8000) return;
|
||||
programBank = data.bit(0,3);
|
||||
optionEnable = !data.bit(4);
|
||||
mirror = data.bit(5);
|
||||
}
|
||||
|
||||
auto addressCIRAM(n32 address) -> n32 {
|
||||
return address >> mirror & 0x0400 | (n10)address;
|
||||
}
|
||||
|
||||
auto readCHR(n32 address, n8 data) -> n8 override {
|
||||
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
|
||||
return characterRAM.read(address);
|
||||
}
|
||||
|
||||
auto writeCHR(n32 address, n8 data) -> void override {
|
||||
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
|
||||
return characterRAM.write(address, data);
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(characterRAM);
|
||||
s(programBank);
|
||||
s(optionEnable);
|
||||
s(mirror);
|
||||
}
|
||||
|
||||
n4 programBank;
|
||||
n1 optionEnable;
|
||||
n1 mirror;
|
||||
};
|
||||
@@ -0,0 +1,139 @@
|
||||
struct BandaiLZ93D50 : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "BANDAI-LZ93D50") return new BandaiLZ93D50;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Memory::Readable<n8> programROM;
|
||||
Memory::Writable<n8> programRAM;
|
||||
Memory::Readable<n8> characterROM;
|
||||
Memory::Writable<n8> characterRAM;
|
||||
M24C eeprom;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(programROM, "program.rom");
|
||||
Interface::load(programRAM, "save.ram");
|
||||
Interface::load(characterROM, "character.rom");
|
||||
Interface::load(characterRAM, "character.ram");
|
||||
if(auto fp = pak->read("save.eeprom")) {
|
||||
if(fp->size() == 128) eeprom.load(M24C::Type::X24C01);
|
||||
if(fp->size() == 256) eeprom.load(M24C::Type::M24C02);
|
||||
if(eeprom) fp->read({eeprom.memory, eeprom.size()});
|
||||
}
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
Interface::save(programRAM, "save.ram");
|
||||
Interface::save(characterRAM, "character.ram");
|
||||
if(auto fp = pak->write("save.eeprom")) {
|
||||
if(eeprom) fp->write({eeprom.memory, eeprom.size()});
|
||||
}
|
||||
}
|
||||
|
||||
auto main() -> void override {
|
||||
if(irqEnable) {
|
||||
if(--irqCounter == 0xffff) {
|
||||
cpu.irqLine(1);
|
||||
irqEnable = false;
|
||||
}
|
||||
}
|
||||
tick();
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
if(address < 0x6000) return data;
|
||||
if(address < 0x8000) {
|
||||
if(programRAM && ramEnable) return programRAM.read((n13)address);
|
||||
if(eeprom) data.bit(4) = eeprom.read();
|
||||
return data;
|
||||
}
|
||||
|
||||
n5 bank = (address < 0xc000 ? (n5)programBank : (n5)0xf);
|
||||
if(characterRAM) bank.bit(4) = bool(programOuterBank);
|
||||
return programROM.read(bank << 14 | (n14)address);
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address < 0x6000) return;
|
||||
if(address < 0x8000) {
|
||||
if(programRAM && ramEnable) return programRAM.write((n13)address, data);
|
||||
}
|
||||
|
||||
switch(address & 0x800f) {
|
||||
case 0x8000: characterBank[0] = data; programOuterBank.bit(0) = data.bit(0); break;
|
||||
case 0x8001: characterBank[1] = data; programOuterBank.bit(1) = data.bit(0); break;
|
||||
case 0x8002: characterBank[2] = data; programOuterBank.bit(2) = data.bit(0); break;
|
||||
case 0x8003: characterBank[3] = data; programOuterBank.bit(3) = data.bit(0); break;
|
||||
case 0x8004: characterBank[4] = data; break;
|
||||
case 0x8005: characterBank[5] = data; break;
|
||||
case 0x8006: characterBank[6] = data; break;
|
||||
case 0x8007: characterBank[7] = data; break;
|
||||
case 0x8008: programBank = data.bit(0,3); break;
|
||||
case 0x8009: mirror = data.bit(0,1); break;
|
||||
case 0x800a:
|
||||
irqEnable = data.bit(0);
|
||||
irqCounter = irqLatch;
|
||||
cpu.irqLine(0);
|
||||
break;
|
||||
case 0x800b: irqLatch.byte(0) = data; break;
|
||||
case 0x800c: irqLatch.byte(1) = data; break;
|
||||
case 0x800d:
|
||||
if(eeprom) {
|
||||
eeprom.clock = data.bit(5);
|
||||
eeprom.data = data.bit(6);
|
||||
eeprom.write();
|
||||
}
|
||||
ramEnable = data.bit(5);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto addressCIRAM(n32 address) const -> n32 {
|
||||
switch(mirror) {
|
||||
case 0: return address >> 0 & 0x0400 | (n10)address;
|
||||
case 1: return address >> 1 & 0x0400 | (n10)address;
|
||||
case 2: return 0x0000 | (n10)address;
|
||||
case 3: return 0x0400 | (n10)address;
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto readCHR(n32 address, n8 data) -> n8 override {
|
||||
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
|
||||
if(characterRAM) return characterRAM.read(address);
|
||||
address = characterBank[address >> 10] << 10 | (n10)address;
|
||||
return characterROM.read(address);
|
||||
}
|
||||
|
||||
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 {
|
||||
eeprom.power();
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(programRAM);
|
||||
s(characterRAM);
|
||||
s(eeprom);
|
||||
s(characterBank);
|
||||
s(programBank);
|
||||
s(programOuterBank);
|
||||
s(mirror);
|
||||
s(ramEnable);
|
||||
s(irqEnable);
|
||||
s(irqCounter);
|
||||
s(irqLatch);
|
||||
}
|
||||
|
||||
n8 characterBank[8];
|
||||
n4 programBank;
|
||||
n4 programOuterBank;
|
||||
n2 mirror;
|
||||
n1 ramEnable;
|
||||
n1 irqEnable;
|
||||
n16 irqCounter;
|
||||
n16 irqLatch;
|
||||
};
|
||||
@@ -0,0 +1,76 @@
|
||||
struct BandaiOeka : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "BANDAI-OEKA") return new BandaiOeka;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Memory::Readable<n8> programROM;
|
||||
Memory::Writable<n8> characterRAM;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(programROM, "program.rom");
|
||||
Interface::load(characterRAM, "character.ram");
|
||||
mirror = pak->attribute("mirror") == "vertical";
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
Interface::save(characterRAM, "character.ram");
|
||||
}
|
||||
|
||||
auto addressTest(n32 address) -> void {
|
||||
if((characterAddress >> 12) != 2 && (address >> 12) == 2) {
|
||||
characterBank.bit(0,1) = address >> 8 & 3;
|
||||
}
|
||||
characterAddress = address;
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
if(address < 0x8000) return data;
|
||||
return programROM.read(programBank << 15 | (n15)address);
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address == 0x2006) {
|
||||
if(++characterLatch) addressTest(data << 8);
|
||||
}
|
||||
if(address < 0x8000) return;
|
||||
programBank = data.bit(0,1);
|
||||
characterBank.bit(2) = data.bit(2);
|
||||
}
|
||||
|
||||
auto addressCHR(n32 address) const -> n32 {
|
||||
n3 bank = (address < 0x1000) ? characterBank : n3(characterBank & 4 | 3);
|
||||
return bank << 12 | (n12)address;
|
||||
}
|
||||
|
||||
auto addressCIRAM(n32 address) const -> n32 {
|
||||
return address >> !mirror & 0x0400 | (n10)address;
|
||||
}
|
||||
|
||||
auto readCHR(n32 address, n8 data) -> n8 override {
|
||||
addressTest(address);
|
||||
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
|
||||
return characterRAM.read(addressCHR(address));
|
||||
}
|
||||
|
||||
auto writeCHR(n32 address, n8 data) -> void override {
|
||||
addressTest(address);
|
||||
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
|
||||
return characterRAM.write(addressCHR(address), data);
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(characterRAM);
|
||||
s(mirror);
|
||||
s(programBank);
|
||||
s(characterBank);
|
||||
s(characterLatch);
|
||||
s(characterAddress);
|
||||
}
|
||||
|
||||
n1 mirror;
|
||||
n2 programBank;
|
||||
n3 characterBank;
|
||||
n1 characterLatch;
|
||||
n32 characterAddress;
|
||||
};
|
||||
@@ -1,8 +1,17 @@
|
||||
namespace Board {
|
||||
|
||||
#include "bandai-74161.cpp"
|
||||
#include "bandai-fcg.cpp"
|
||||
#include "jaleco-jf.cpp"
|
||||
#include "jaleco-jf11-jf14.cpp"
|
||||
#include "bandai-karaoke.cpp"
|
||||
#include "bandai-lz93d50.cpp"
|
||||
#include "bandai-oeka.cpp"
|
||||
#include "colordreams-74x377.cpp"
|
||||
#include "gtrom.cpp"
|
||||
#include "jaleco-jf05.cpp"
|
||||
#include "jaleco-jf11.cpp"
|
||||
#include "jaleco-jf16.cpp"
|
||||
#include "jaleco-jf17.cpp"
|
||||
#include "jaleco-jf23.cpp"
|
||||
#include "konami-vrc1.cpp"
|
||||
#include "konami-vrc2.cpp"
|
||||
#include "konami-vrc3.cpp"
|
||||
@@ -13,7 +22,6 @@ namespace Board {
|
||||
#include "hvc-axrom.cpp"
|
||||
#include "hvc-bnrom.cpp"
|
||||
#include "hvc-cnrom.cpp"
|
||||
#include "hvc-dxrom.cpp"
|
||||
#include "hvc-exrom.cpp"
|
||||
#include "hvc-fmr.cpp"
|
||||
#include "hvc-fxrom.cpp"
|
||||
@@ -24,20 +32,36 @@ namespace Board {
|
||||
#include "hvc-sxrom.cpp"
|
||||
#include "hvc-txrom.cpp"
|
||||
#include "hvc-uxrom.cpp"
|
||||
#include "irem-g101.cpp"
|
||||
#include "irem-h3001.cpp"
|
||||
#include "irem-lrog017.cpp"
|
||||
#include "irem-tam-s1.cpp"
|
||||
#include "mlt-action52.cpp"
|
||||
#include "namco-118.cpp"
|
||||
#include "namco-163.cpp"
|
||||
#include "namco-340.cpp"
|
||||
#include "sunsoft-1.cpp"
|
||||
#include "sunsoft-2.cpp"
|
||||
#include "sunsoft-3.cpp"
|
||||
#include "sunsoft-4.cpp"
|
||||
#include "sunsoft-5b.cpp"
|
||||
#include "taito-tc0190.cpp"
|
||||
#include "taito-tc0690.cpp"
|
||||
#include "taito-x1-005.cpp"
|
||||
#include "taito-x1-017.cpp"
|
||||
|
||||
auto Interface::create(string board) -> Interface* {
|
||||
Interface* p = nullptr;
|
||||
if(!p) p = Bandai74161::create(board);
|
||||
if(!p) p = BandaiFCG::create(board);
|
||||
if(!p) p = BandaiKaraoke::create(board);
|
||||
if(!p) p = BandaiLZ93D50::create(board);
|
||||
if(!p) p = BandaiOeka::create(board);
|
||||
if(!p) p = ColorDreams_74x377::create(board);
|
||||
if(!p) p = GTROM::create(board);
|
||||
if(!p) p = HVC_AxROM::create(board);
|
||||
if(!p) p = HVC_BNROM::create(board);
|
||||
if(!p) p = HVC_CNROM::create(board);
|
||||
if(!p) p = HVC_DxROM::create(board);
|
||||
if(!p) p = HVC_ExROM::create(board);
|
||||
if(!p) p = HVC_FMR::create(board);
|
||||
if(!p) p = HVC_FxROM::create(board);
|
||||
@@ -48,9 +72,15 @@ 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 = IremG101::create(board);
|
||||
if(!p) p = IremH3001::create(board);
|
||||
if(!p) p = IremLROG017::create(board);
|
||||
if(!p) p = IremTAMS1::create(board);
|
||||
if(!p) p = JalecoJF::create(board);
|
||||
if(!p) p = Jaleco_JF11_JF14::create(board);
|
||||
if(!p) p = JalecoJF05::create(board);
|
||||
if(!p) p = JalecoJF11::create(board);
|
||||
if(!p) p = JalecoJF16::create(board);
|
||||
if(!p) p = JalecoJF17::create(board);
|
||||
if(!p) p = JalecoJF23::create(board);
|
||||
if(!p) p = KonamiVRC1::create(board);
|
||||
if(!p) p = KonamiVRC2::create(board);
|
||||
if(!p) p = KonamiVRC3::create(board);
|
||||
@@ -58,11 +88,19 @@ 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 = MltAction52::create(board);
|
||||
if(!p) p = Namco118::create(board);
|
||||
if(!p) p = Namco163::create(board);
|
||||
if(!p) p = Namco340::create(board);
|
||||
if(!p) p = Sunsoft1::create(board);
|
||||
if(!p) p = Sunsoft2::create(board);
|
||||
if(!p) p = Sunsoft3::create(board);
|
||||
if(!p) p = Sunsoft4::create(board);
|
||||
if(!p) p = Sunsoft5B::create(board);
|
||||
if(!p) p = TaitoTC0190::create(board);
|
||||
if(!p) p = TaitoTC0690::create(board);
|
||||
if(!p) p = TaitoX1005::create(board);
|
||||
if(!p) p = TaitoX1017::create(board);
|
||||
if(!p) p = new Interface;
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
struct ColorDreams_74x377 : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "COLORDREAMS-74*377") return new ColorDreams_74x377();
|
||||
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(programBank << 15 | (n15)address);
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address < 0x8000) return;
|
||||
programBank = data.bit(0,1);
|
||||
characterBank = data.bit(4,7);
|
||||
}
|
||||
|
||||
auto readCHR(n32 address, n8 data) -> n8 override {
|
||||
if(address & 0x2000) {
|
||||
address = address >> !mirror & 0x0400 | (n10)address;
|
||||
return ppu.readCIRAM(address);
|
||||
}
|
||||
address = characterBank << 13 | (n13)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);
|
||||
}
|
||||
address = characterBank << 13 | (n13)address;
|
||||
if(characterRAM) return characterRAM.write(address, data);
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(characterRAM);
|
||||
s(programBank);
|
||||
s(characterBank);
|
||||
s(mirror);
|
||||
}
|
||||
|
||||
n2 programBank;
|
||||
n4 characterBank;
|
||||
n1 mirror;
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
struct GTROM : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "GTROM" ) return new GTROM();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Memory::Readable<n8> programROM;
|
||||
Memory::Writable<n8> characterRAM;
|
||||
Memory::Writable<n8> videoRAM;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(programROM, "program.rom");
|
||||
Interface::load(characterRAM, "character.ram");
|
||||
videoRAM.allocate(16_KiB);
|
||||
}
|
||||
|
||||
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(programBank << 15 | (n15)address);
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address >= 0x5000 && address <= 0x5fff) {
|
||||
programBank = data.bit(0, 3);
|
||||
characterBank = data.bit(4);
|
||||
videoBank = data.bit(5);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto readCHR(n32 address, n8 data) -> n8 override {
|
||||
if(address & 0x2000) return videoRAM.read(videoBank << 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 videoRAM.write(videoBank << 13 | (n13)address, data);
|
||||
return characterRAM.write(characterBank << 13 | (n13)address, data);
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
programBank = 0;
|
||||
characterBank = 0;
|
||||
videoBank = 0;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(characterRAM);
|
||||
s(videoRAM);
|
||||
s(programBank);
|
||||
s(characterBank);
|
||||
s(videoBank);
|
||||
}
|
||||
|
||||
n4 programBank;
|
||||
n1 characterBank;
|
||||
n1 videoBank;
|
||||
};
|
||||
@@ -26,7 +26,7 @@ struct HVC_BNROM : Interface {
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address < 0x8000) return;
|
||||
programBank = data.bit(0,1);
|
||||
programBank = data;
|
||||
}
|
||||
|
||||
auto readCHR(n32 address, n8 data) -> n8 override {
|
||||
@@ -57,5 +57,5 @@ struct HVC_BNROM : Interface {
|
||||
}
|
||||
|
||||
n1 mirror; //0 = horizontal, 1 = vertical
|
||||
n2 programBank;
|
||||
n8 programBank;
|
||||
};
|
||||
@@ -1,6 +1,8 @@
|
||||
struct HVC_CNROM : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "HVC-CNROM") return new HVC_CNROM;
|
||||
if(id == "HVC-CNROM" ) return new HVC_CNROM(Revision::CNROM);
|
||||
if(id == "HVC-CNROM-SEC") return new HVC_CNROM(Revision::CNROMS);
|
||||
if(id == "HVC-CPROM" ) return new HVC_CNROM(Revision::CPROM);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -8,11 +10,20 @@ struct HVC_CNROM : Interface {
|
||||
Memory::Readable<n8> characterROM;
|
||||
Memory::Writable<n8> characterRAM;
|
||||
|
||||
enum class Revision : u32 {
|
||||
CNROM,
|
||||
CNROMS,
|
||||
CPROM,
|
||||
} revision;
|
||||
|
||||
HVC_CNROM(Revision revision) : revision(revision) {}
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(programROM, "program.rom");
|
||||
Interface::load(characterROM, "character.rom");
|
||||
Interface::load(characterRAM, "character.ram");
|
||||
mirror = pak->attribute("mirror") == "vertical";
|
||||
key = pak->attribute("chip/key").natural();
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
@@ -26,7 +37,8 @@ struct HVC_CNROM : Interface {
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address < 0x8000) return;
|
||||
characterBank = data.bit(0,1);
|
||||
characterBank = data & programROM.read((n15)address);
|
||||
characterEnable = (data == key);
|
||||
}
|
||||
|
||||
auto readCHR(n32 address, n8 data) -> n8 override {
|
||||
@@ -34,10 +46,16 @@ struct HVC_CNROM : Interface {
|
||||
address = address >> !mirror & 0x0400 | (n10)address;
|
||||
return ppu.readCIRAM(address);
|
||||
}
|
||||
if(revision == Revision::CPROM) {
|
||||
n2 bank = (address < 0x1000) ? (n2)0 : characterBank;
|
||||
return characterRAM.read(bank << 12 | (n12)address);
|
||||
}
|
||||
if(revision == Revision::CNROMS) {
|
||||
if(!characterEnable) return 0xff;
|
||||
return characterROM.read((n13)address);
|
||||
}
|
||||
address = characterBank << 13 | (n13)address;
|
||||
if(characterROM) return characterROM.read(address);
|
||||
if(characterRAM) return characterRAM.read(address);
|
||||
return data;
|
||||
return characterROM.read(address);
|
||||
}
|
||||
|
||||
auto writeCHR(n32 address, n8 data) -> void override {
|
||||
@@ -45,19 +63,22 @@ struct HVC_CNROM : Interface {
|
||||
address = address >> !mirror & 0x0400 | (n10)address;
|
||||
return ppu.writeCIRAM(address, data);
|
||||
}
|
||||
address = characterBank << 13 | (n13)address;
|
||||
if(characterRAM) return characterRAM.write(address, data);
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
if(revision == Revision::CPROM) {
|
||||
n2 bank = (address < 0x1000) ? (n2)0 : characterBank;
|
||||
return characterRAM.write(bank << 12 | (n12)address, data);
|
||||
}
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(characterRAM);
|
||||
s(mirror);
|
||||
s(key);
|
||||
s(characterBank);
|
||||
s(characterEnable);
|
||||
}
|
||||
|
||||
n1 mirror; //0 = horizontal, 1 = vertical
|
||||
n8 key;
|
||||
n2 characterBank;
|
||||
n1 characterEnable;
|
||||
};
|
||||
@@ -1,94 +0,0 @@
|
||||
struct HVC_DxROM : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "HVC-DEROM" ) return new HVC_DxROM(Revision::DEROM);
|
||||
if(id == "HVC-DE1ROM") return new HVC_DxROM(Revision::DE1ROM);
|
||||
if(id == "HVC-DRROM" ) return new HVC_DxROM(Revision::DRROM);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Memory::Readable<n8> programROM;
|
||||
Memory::Readable<n8> characterROM;
|
||||
Memory::Writable<n8> characterCIRAM;
|
||||
|
||||
enum class Revision : u32 {
|
||||
DEROM,
|
||||
DE1ROM,
|
||||
DRROM,
|
||||
} revision;
|
||||
|
||||
HVC_DxROM(Revision revision) : revision(revision) {}
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(programROM, "program.rom");
|
||||
Interface::load(characterROM, "character.rom");
|
||||
Interface::load(characterCIRAM, "character.ram");
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
Interface::save(characterCIRAM, "character.ram");
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
switch(address) {
|
||||
case 0x8000 ... 0x9fff: return programROM.read(programBank[0] << 13 | (n13)address);
|
||||
case 0xa000 ... 0xbfff: return programROM.read(programBank[1] << 13 | (n13)address);
|
||||
case 0xc000 ... 0xdfff: return programROM.read(0x0e << 13 | (n13)address);
|
||||
case 0xe000 ... 0xffff: return programROM.read(0x0f << 13 | (n13)address);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
switch(address & 0xe001) {
|
||||
case 0x8000:
|
||||
bankSelect = data.bit(0,2);
|
||||
break;
|
||||
case 0x8001:
|
||||
switch(bankSelect) {
|
||||
case 0: characterBank[0] = data & ~1; break;
|
||||
case 1: characterBank[1] = data & ~1; break;
|
||||
case 2: characterBank[2] = data; break;
|
||||
case 3: characterBank[3] = data; break;
|
||||
case 4: characterBank[4] = data; break;
|
||||
case 5: characterBank[5] = data; break;
|
||||
case 6: programBank[0] = data; break;
|
||||
case 7: programBank[1] = data; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto readCHR(n32 address, n8 data) -> n8 override {
|
||||
switch(address) {
|
||||
case 0x0000 ... 0x07ff: return characterROM.read(characterBank[0] << 10 | (n11)address);
|
||||
case 0x0800 ... 0x0fff: return characterROM.read(characterBank[1] << 10 | (n11)address);
|
||||
case 0x1000 ... 0x13ff: return characterROM.read(characterBank[2] << 10 | (n10)address);
|
||||
case 0x1400 ... 0x17ff: return characterROM.read(characterBank[3] << 10 | (n10)address);
|
||||
case 0x1800 ... 0x1bff: return characterROM.read(characterBank[4] << 10 | (n10)address);
|
||||
case 0x1c00 ... 0x1fff: return characterROM.read(characterBank[5] << 10 | (n10)address);
|
||||
case 0x2000 ... 0x27ff: return ppu.readCIRAM((n11)address);
|
||||
case 0x2800 ... 0x2fff: return characterCIRAM.read((n11)address);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
auto writeCHR(n32 address, n8 data) -> void override {
|
||||
switch(address) {
|
||||
case 0x2000 ... 0x27ff: return ppu.writeCIRAM((n11)address, data);
|
||||
case 0x2800 ... 0x2fff: return characterCIRAM.write((n11)address, data);
|
||||
}
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(bankSelect);
|
||||
s(programBank);
|
||||
s(characterBank);
|
||||
}
|
||||
|
||||
n3 bankSelect;
|
||||
n4 programBank[2];
|
||||
n6 characterBank[6];
|
||||
};
|
||||
@@ -31,8 +31,9 @@ struct HVC_HKROM : Interface { //MMC6
|
||||
if(!(characterAddress & 0x1000) && (address & 0x1000)) {
|
||||
if(irqDelay == 0) {
|
||||
if(irqCounter == 0) {
|
||||
irqCounter = irqLatch;
|
||||
} else if(--irqCounter == 0) {
|
||||
irqCounter = irqLatch + 1;
|
||||
}
|
||||
if(--irqCounter == 0) {
|
||||
if(irqEnable) irqLine = 1;
|
||||
}
|
||||
}
|
||||
@@ -64,6 +65,7 @@ struct HVC_HKROM : Interface { //MMC6
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address == 0x2006 && ++characterLatch) irqTest(data << 8);
|
||||
if(address < 0x7000) return;
|
||||
|
||||
if(address < 0x8000) {
|
||||
@@ -179,6 +181,7 @@ struct HVC_HKROM : Interface { //MMC6
|
||||
s(irqEnable);
|
||||
s(irqDelay);
|
||||
s(irqLine);
|
||||
s(characterLatch);
|
||||
s(characterAddress);
|
||||
}
|
||||
|
||||
@@ -194,7 +197,8 @@ struct HVC_HKROM : Interface { //MMC6
|
||||
n8 irqLatch;
|
||||
n8 irqCounter;
|
||||
n1 irqEnable;
|
||||
n1 irqDelay;
|
||||
n8 irqDelay;
|
||||
n1 irqLine;
|
||||
n1 characterLatch;
|
||||
n16 characterAddress;
|
||||
};
|
||||
@@ -69,7 +69,13 @@ struct HVC_SxROM : Interface { //MMC1
|
||||
HVC_SxROM(Revision revision) : revision(revision) {}
|
||||
|
||||
auto load() -> void override {
|
||||
auto chip = pak->attribute("chip");
|
||||
chipRevision = ChipRevision::MMC1B2;
|
||||
if(chip == "MMC1" ) chipRevision = ChipRevision::MMC1;
|
||||
if(chip == "MMC1A" ) chipRevision = ChipRevision::MMC1A;
|
||||
if(chip == "MMC1B1") chipRevision = ChipRevision::MMC1B1;
|
||||
if(chip == "MMC1B3") chipRevision = ChipRevision::MMC1B3;
|
||||
if(chip == "MMC1C" ) chipRevision = ChipRevision::MMC1C;
|
||||
|
||||
Interface::load(programROM, "program.rom");
|
||||
Interface::load(programRAM, "save.ram");
|
||||
@@ -130,7 +136,7 @@ struct HVC_SxROM : Interface { //MMC1
|
||||
if(revision == Revision::SNROM) {
|
||||
if(characterBank[0].bit(4)) return data;
|
||||
}
|
||||
if(ramDisable) return 0x00;
|
||||
if(!programRAM || ramDisable) return 0x00;
|
||||
return programRAM.read(addressProgramRAM(address));
|
||||
}
|
||||
|
||||
@@ -146,7 +152,7 @@ struct HVC_SxROM : Interface { //MMC1
|
||||
if(revision == Revision::SNROM) {
|
||||
if(characterBank[0].bit(4)) return;
|
||||
}
|
||||
if(ramDisable) return;
|
||||
if(!programRAM || ramDisable) return;
|
||||
return programRAM.write(addressProgramRAM(address), data);
|
||||
}
|
||||
|
||||
@@ -180,7 +186,9 @@ struct HVC_SxROM : Interface { //MMC1
|
||||
break;
|
||||
case 3:
|
||||
programBank = shiftValue.bit(0,3);
|
||||
ramDisable = shiftValue.bit(4);
|
||||
if(chipRevision > ChipRevision::MMC1A) {
|
||||
ramDisable = shiftValue.bit(4);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,8 +65,9 @@ struct HVC_TxROM : Interface { //MMC3
|
||||
if(!(characterAddress & 0x1000) && (address & 0x1000)) {
|
||||
if(irqDelay == 0) {
|
||||
if(irqCounter == 0) {
|
||||
irqCounter = irqLatch;
|
||||
} else if(--irqCounter == 0) {
|
||||
irqCounter = irqLatch + 1;
|
||||
}
|
||||
if(--irqCounter == 0) {
|
||||
if(irqEnable) irqLine = 1;
|
||||
}
|
||||
}
|
||||
@@ -79,7 +80,7 @@ struct HVC_TxROM : Interface { //MMC3
|
||||
if(address < 0x6000) return data;
|
||||
|
||||
if(address < 0x8000) {
|
||||
if(!ramEnable) return data;
|
||||
if(!ramEnable || !programRAM) return data;
|
||||
return programRAM.read((n13)address);
|
||||
}
|
||||
|
||||
@@ -95,10 +96,11 @@ struct HVC_TxROM : Interface { //MMC3
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address == 0x2006 && ++characterLatch) irqTest(data << 8);
|
||||
if(address < 0x6000) return;
|
||||
|
||||
if(address < 0x8000) {
|
||||
if(!ramEnable || !ramWritable) return;
|
||||
if(!ramEnable || !ramWritable || !programRAM) return;
|
||||
return programRAM.write((n13)address, data);
|
||||
}
|
||||
|
||||
@@ -163,20 +165,54 @@ struct HVC_TxROM : Interface { //MMC3
|
||||
}
|
||||
|
||||
auto addressCIRAM(n32 address) const -> n32 {
|
||||
if(revision == Revision::TKSROM || revision == Revision::TLSROM) {
|
||||
address.bit(12) = 0;
|
||||
if(characterMode == 0) {
|
||||
if(address <= 0x27ff) return characterBank[0].bit(7) << 10 | (n10)address;
|
||||
if(address <= 0x2fff) return characterBank[1].bit(7) << 10 | (n10)address;
|
||||
} else {
|
||||
if(address <= 0x23ff) return characterBank[2].bit(7) << 10 | (n10)address;
|
||||
if(address <= 0x27ff) return characterBank[3].bit(7) << 10 | (n10)address;
|
||||
if(address <= 0x2bff) return characterBank[4].bit(7) << 10 | (n10)address;
|
||||
if(address <= 0x2fff) return characterBank[5].bit(7) << 10 | (n10)address;
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
return address >> mirror & 0x0400 | (n10)address;
|
||||
}
|
||||
|
||||
auto readCHR(n32 address, n8 data) -> n8 override {
|
||||
irqTest(address);
|
||||
if(revision == Revision::TVROM) {
|
||||
if(address < 0x2000) return characterROM.read(addressCHR(address));
|
||||
address.bit(12) = 0;
|
||||
if(address < 0x2800) return ppu.readCIRAM((n11)address);
|
||||
return characterRAM.read((n11)address);
|
||||
}
|
||||
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
|
||||
if(characterROM) return characterROM.read(addressCHR(address));
|
||||
if(revision == Revision::TQROM) {
|
||||
address = addressCHR(address);
|
||||
if(address < 0x10000) return characterROM.read(address);
|
||||
return characterRAM.read(address);
|
||||
}
|
||||
if(characterRAM) return characterRAM.read(addressCHR(address));
|
||||
return data;
|
||||
return characterROM.read(addressCHR(address));
|
||||
}
|
||||
|
||||
auto writeCHR(n32 address, n8 data) -> void override {
|
||||
irqTest(address);
|
||||
if(revision == Revision::TVROM) {
|
||||
if(address < 0x2000) return;
|
||||
address.bit(12) = 0;
|
||||
if(address < 0x2800) return ppu.writeCIRAM((n11)address, data);
|
||||
return characterRAM.write((n11)address, data);
|
||||
}
|
||||
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
|
||||
if(revision == Revision::TQROM) {
|
||||
address = addressCHR(address);
|
||||
if(address < 0x10000) return;
|
||||
return characterRAM.write(address, data);
|
||||
}
|
||||
if(characterRAM) return characterRAM.write(addressCHR(address), data);
|
||||
}
|
||||
|
||||
@@ -201,6 +237,7 @@ struct HVC_TxROM : Interface { //MMC3
|
||||
s(irqEnable);
|
||||
s(irqDelay);
|
||||
s(irqLine);
|
||||
s(characterLatch);
|
||||
s(characterAddress);
|
||||
}
|
||||
|
||||
@@ -217,5 +254,6 @@ struct HVC_TxROM : Interface { //MMC3
|
||||
n1 irqEnable;
|
||||
n8 irqDelay;
|
||||
n1 irqLine;
|
||||
n1 characterLatch;
|
||||
n16 characterAddress;
|
||||
};
|
||||
@@ -1,7 +1,9 @@
|
||||
struct HVC_UxROM : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "HVC-UNROM") return new HVC_UxROM(Revision::UNROM);
|
||||
if(id == "HVC-UOROM") return new HVC_UxROM(Revision::UOROM);
|
||||
if(id == "HVC-UNROM" ) return new HVC_UxROM(Revision::UNROM);
|
||||
if(id == "HVC-UNROMA") return new HVC_UxROM(Revision::UNROMA);
|
||||
if(id == "HVC-UN1ROM") return new HVC_UxROM(Revision::UN1ROM);
|
||||
if(id == "HVC-UOROM" ) return new HVC_UxROM(Revision::UOROM);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -11,6 +13,8 @@ struct HVC_UxROM : Interface {
|
||||
|
||||
enum class Revision : u32 {
|
||||
UNROM,
|
||||
UNROMA,
|
||||
UN1ROM,
|
||||
UOROM,
|
||||
} revision;
|
||||
|
||||
@@ -29,13 +33,18 @@ struct HVC_UxROM : Interface {
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
if(address < 0x8000) return data;
|
||||
n4 bank = (address < 0xc000 ? programBank : (n4)0xf);
|
||||
n8 bank;
|
||||
switch(address >> 14 & 1) {
|
||||
case 0: bank = (revision == Revision::UNROMA ? (n8)0x00 : programBank); break;
|
||||
case 1: bank = (revision == Revision::UNROMA ? programBank : (n8)0xff); break;
|
||||
}
|
||||
return programROM.read(bank << 14 | (n14)address);
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address < 0x8000) return;
|
||||
programBank = data.bit(0,3);
|
||||
programBank = data;
|
||||
if(revision == Revision::UN1ROM) programBank >>= 2;
|
||||
}
|
||||
|
||||
auto readCHR(n32 address, n8 data) -> n8 override {
|
||||
@@ -66,5 +75,5 @@ struct HVC_UxROM : Interface {
|
||||
}
|
||||
|
||||
n1 mirror; //0 = horizontal, 1 = vertical
|
||||
n4 programBank;
|
||||
n8 programBank;
|
||||
};
|
||||
@@ -0,0 +1,89 @@
|
||||
struct IremG101 : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "IREM-G101" ) return new IremG101(Revision::G101);
|
||||
if(id == "IREM-G101A") return new IremG101(Revision::G101A);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Memory::Readable<n8> programROM;
|
||||
Memory::Writable<n8> programRAM;
|
||||
Memory::Readable<n8> characterROM;
|
||||
|
||||
enum class Revision : u32 {
|
||||
G101,
|
||||
G101A,
|
||||
} revision;
|
||||
|
||||
IremG101(Revision revision) : revision(revision) {}
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(programROM, "program.rom");
|
||||
Interface::load(programRAM, "save.ram");
|
||||
Interface::load(characterROM, "character.rom");
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
Interface::save(programRAM, "save.ram");
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
if(address < 0x6000) return data;
|
||||
if(address < 0x8000 && !programRAM) return data;
|
||||
if(address < 0x8000) return programRAM.read((n13)address);
|
||||
|
||||
n5 bank;
|
||||
switch(address >> 13 & 3) {
|
||||
case 0: bank = (programMode == 0 ? programBank[0] : (n5)0x1e); break;
|
||||
case 1: bank = programBank[1]; break;
|
||||
case 2: bank = (programMode == 1 ? programBank[0] : (n5)0x1e); break;
|
||||
case 3: bank = 0x1f; break;
|
||||
}
|
||||
return programROM.read(bank << 13 | (n13)address);
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address < 0x6000) return;
|
||||
if(address < 0x8000 && !programRAM) return;
|
||||
if(address < 0x8000) return programRAM.write((n13)address, data);
|
||||
|
||||
switch(address & 0xf000) {
|
||||
case 0x8000: programBank[0] = data.bit(0,4); break;
|
||||
case 0xa000: programBank[1] = data.bit(0,4); break;
|
||||
case 0xb000: characterBank[address & 7] = data; break;
|
||||
case 0x9000:
|
||||
if(revision == Revision::G101) {
|
||||
mirror = data.bit(0);
|
||||
programMode = data.bit(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto addressCIRAM(n32 address) const -> n32 {
|
||||
if(revision == Revision::G101A) return (n10)address;
|
||||
return address >> mirror & 0x0400 | (n10)address;
|
||||
}
|
||||
|
||||
auto readCHR(n32 address, n8 data) -> n8 override {
|
||||
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
|
||||
n8 bank = characterBank[address >> 10 & 7];
|
||||
return characterROM.read(bank << 10 | (n10)address);
|
||||
}
|
||||
|
||||
auto writeCHR(n32 address, n8 data) -> void override {
|
||||
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(programRAM);
|
||||
s(programBank);
|
||||
s(characterBank);
|
||||
s(programMode);
|
||||
s(mirror);
|
||||
}
|
||||
|
||||
n5 programBank[2];
|
||||
n8 characterBank[8];
|
||||
n1 programMode;
|
||||
n1 mirror;
|
||||
};
|
||||
@@ -0,0 +1,104 @@
|
||||
struct IremH3001 : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "IREM-H3001") return new IremH3001;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Memory::Readable<n8> programROM;
|
||||
Memory::Readable<n8> characterROM;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(programROM, "program.rom");
|
||||
Interface::load(characterROM, "character.rom");
|
||||
}
|
||||
|
||||
auto main() -> void override {
|
||||
if(irqEnable) {
|
||||
if(irqCounter && --irqCounter == 0) irqLine = 1;
|
||||
}
|
||||
cpu.irqLine(irqLine);
|
||||
tick();
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
if(address < 0x8000) return data;
|
||||
|
||||
n6 bank;
|
||||
switch(address >> 13 & 3) {
|
||||
case 0: bank = (programMode == 0 ? programBank[0] : (n6)0x3e); break;
|
||||
case 1: bank = programBank[1]; break;
|
||||
case 2: bank = (programMode == 1 ? programBank[0] : (n6)0x3e); break;
|
||||
case 3: bank = 0x3f; break;
|
||||
}
|
||||
return programROM.read(bank << 13 | (n13)address);
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address < 0x8000) return;
|
||||
|
||||
switch(address) {
|
||||
case 0x8000: programBank[0] = data.bit(0,5); break;
|
||||
case 0xa000: programBank[1] = data.bit(0,5); break;
|
||||
case 0xb000: characterBank[0] = data; break;
|
||||
case 0xb001: characterBank[1] = data; break;
|
||||
case 0xb002: characterBank[2] = data; break;
|
||||
case 0xb003: characterBank[3] = data; break;
|
||||
case 0xb004: characterBank[4] = data; break;
|
||||
case 0xb005: characterBank[5] = data; break;
|
||||
case 0xb006: characterBank[6] = data; break;
|
||||
case 0xb007: characterBank[7] = data; break;
|
||||
case 0x9000: programMode = data.bit(7); break;
|
||||
case 0x9001: mirror = data.bit(6,7); break;
|
||||
case 0x9003:
|
||||
irqEnable = data.bit(7);
|
||||
irqLine = 0;
|
||||
break;
|
||||
case 0x9004:
|
||||
irqCounter = irqLatch;
|
||||
irqLine = 0;
|
||||
break;
|
||||
case 0x9005: irqLatch = irqLatch.bit(0,7) << 0 | data << 8; break;
|
||||
case 0x9006: irqLatch = irqLatch.bit(8,15) << 8 | data << 0; break;
|
||||
}
|
||||
}
|
||||
|
||||
auto addressCIRAM(n32 address) const -> n32 {
|
||||
switch(mirror) {
|
||||
case 0: return address >> 0 & 0x0400 | (n10)address;
|
||||
case 1: return (n10)address;
|
||||
case 2: return address >> 1 & 0x0400 | (n10)address;
|
||||
case 3: return (n10)address;
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto readCHR(n32 address, n8 data) -> n8 override {
|
||||
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
|
||||
n8 bank = characterBank[address >> 10 & 7];
|
||||
return characterROM.read(bank << 10 | (n10)address);
|
||||
}
|
||||
|
||||
auto writeCHR(n32 address, n8 data) -> void override {
|
||||
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(programBank);
|
||||
s(characterBank);
|
||||
s(programMode);
|
||||
s(mirror);
|
||||
s(irqLatch);
|
||||
s(irqCounter);
|
||||
s(irqEnable);
|
||||
s(irqLine);
|
||||
}
|
||||
|
||||
n6 programBank[2];
|
||||
n8 characterBank[8];
|
||||
n1 programMode;
|
||||
n2 mirror;
|
||||
n16 irqLatch;
|
||||
n16 irqCounter;
|
||||
n1 irqEnable;
|
||||
n1 irqLine;
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
struct IremLROG017 : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "IREM-LROG017") return new IremLROG017;
|
||||
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;
|
||||
return programROM.read(programBank << 15 | (n15)address);
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address < 0x8000) return;
|
||||
programBank = data.bit(0,3);
|
||||
characterBank = data.bit(4,7);
|
||||
}
|
||||
|
||||
auto addressCHR(n32 address) const -> n32 {
|
||||
if(address < 0x1000) return 1 << 11 | (n11)address;
|
||||
if(address < 0x1800) return 2 << 11 | (n11)address;
|
||||
if(address < 0x2000) return 3 << 11 | (n11)address;
|
||||
if(address < 0x2800) return 0 << 11 | (n11)address;
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto readCHR(n32 address, n8 data) -> n8 override {
|
||||
if(address < 0x0800) return characterROM.read(characterBank << 11 | (n11)address);
|
||||
if(address < 0x2800) return characterRAM.read(addressCHR(address));
|
||||
if(address < 0x3000) return ppu.readCIRAM((n11)address);
|
||||
return data;
|
||||
}
|
||||
|
||||
auto writeCHR(n32 address, n8 data) -> void override {
|
||||
if(address < 0x0800) return;
|
||||
if(address < 0x2800) return characterRAM.write(addressCHR(address), data);
|
||||
if(address < 0x3000) return ppu.writeCIRAM((n11)address, data);
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(characterRAM);
|
||||
s(programBank);
|
||||
s(characterBank);
|
||||
}
|
||||
|
||||
n4 programBank;
|
||||
n4 characterBank;
|
||||
};
|
||||
@@ -5,7 +5,7 @@ struct IremTAMS1 : Interface {
|
||||
}
|
||||
|
||||
Memory::Readable<n8> programROM;
|
||||
Memory::Writable<n8> characterROM;
|
||||
Memory::Readable<n8> characterROM;
|
||||
Memory::Writable<n8> characterRAM;
|
||||
|
||||
auto load() -> void override {
|
||||
|
||||
Loaded 100 of 638 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user