mirror of
https://github.com/ares-emulator/ares.git
synced 2026-11-09 17:03:16 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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
|
||||
--------------
|
||||
|
||||
+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 = "128";
|
||||
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 = "128";
|
||||
|
||||
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,7 +27,7 @@ struct Instruction : Tracer {
|
||||
for(auto& history : _history) history = ~0;
|
||||
}
|
||||
|
||||
auto address(u32 address) -> bool {
|
||||
auto address(u64 address) -> bool {
|
||||
address &= (1ull << _addressBits) - 1; //mask upper bits of address
|
||||
_address = address;
|
||||
address >>= _addressMask; //clip unneeded alignment bits (to reduce _masks size)
|
||||
@@ -55,7 +55,7 @@ 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 >>= _addressMask;
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
@@ -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];
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+10
-13
@@ -1,6 +1,10 @@
|
||||
struct Jaleco_JF11_JF14 : Interface {
|
||||
struct JalecoJF05 : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "JALECO-JF11-JF14") return new Jaleco_JF11_JF14;
|
||||
if(id == "JALECO-JF-05") return new JalecoJF05;
|
||||
if(id == "JALECO-JF-06") return new JalecoJF05;
|
||||
if(id == "JALECO-JF-07") return new JalecoJF05;
|
||||
if(id == "JALECO-JF-08") return new JalecoJF05;
|
||||
if(id == "JALECO-JF-10") return new JalecoJF05;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -15,13 +19,12 @@ struct Jaleco_JF11_JF14 : Interface {
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
if(address < 0x8000) return data;
|
||||
return programROM.read(programBank << 15 | (n15)address);
|
||||
return programROM.read(address);
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address < 0x6000 || address > 0x7fff) return;
|
||||
characterBank = data.bit(0,1);
|
||||
programBank = data.bit(4,5);
|
||||
characterBank = data.bit(0) << 1 | data.bit(1);
|
||||
}
|
||||
|
||||
auto readCHR(n32 address, n8 data) -> n8 override {
|
||||
@@ -30,8 +33,7 @@ struct Jaleco_JF11_JF14 : Interface {
|
||||
return ppu.readCIRAM(address);
|
||||
}
|
||||
address = characterBank << 13 | (n13)address;
|
||||
if(characterROM) return characterROM.read(address);
|
||||
return data;
|
||||
return characterROM.read(address);
|
||||
}
|
||||
|
||||
auto writeCHR(n32 address, n8 data) -> void override {
|
||||
@@ -41,16 +43,11 @@ struct Jaleco_JF11_JF14 : Interface {
|
||||
}
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(mirror);
|
||||
s(programBank);
|
||||
s(characterBank);
|
||||
}
|
||||
|
||||
n1 mirror; //0 = horizontal, 1 = vertical
|
||||
n2 programBank;
|
||||
n1 mirror;
|
||||
n2 characterBank;
|
||||
};
|
||||
@@ -0,0 +1,70 @@
|
||||
//todo: uPD7756 ADPCM unsupported
|
||||
|
||||
struct JalecoJF11 : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "JALECO-JF-11") return new JalecoJF11(Revision::JF11);
|
||||
if(id == "JALECO-JF-13") return new JalecoJF11(Revision::JF13);
|
||||
if(id == "JALECO-JF-14") return new JalecoJF11(Revision::JF14);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Memory::Readable<n8> programROM;
|
||||
Memory::Readable<n8> characterROM;
|
||||
|
||||
enum class Revision : u32 {
|
||||
JF11,
|
||||
JF13,
|
||||
JF14,
|
||||
} revision;
|
||||
|
||||
JalecoJF11(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;
|
||||
return programROM.read(programBank << 15 | (n15)address);
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address < 0x6000 || address > 0x7fff) return;
|
||||
if(revision == Revision::JF13 && address > 0x6fff) return;
|
||||
|
||||
programBank = data.bit(4,5);
|
||||
if(revision == Revision::JF13) {
|
||||
characterBank = data.bit(6) << 2 | data.bit(0,1);
|
||||
} else {
|
||||
characterBank = data.bit(0,3);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
return characterROM.read(address);
|
||||
}
|
||||
|
||||
auto writeCHR(n32 address, n8 data) -> void override {
|
||||
if(address & 0x2000) {
|
||||
address = address >> !mirror & 0x0400 | (n10)address;
|
||||
return ppu.writeCIRAM(address, data);
|
||||
}
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(mirror);
|
||||
s(programBank);
|
||||
s(characterBank);
|
||||
}
|
||||
|
||||
n1 mirror; //0 = horizontal, 1 = vertical
|
||||
n2 programBank;
|
||||
n4 characterBank;
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
struct JalecoJF16 : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "JALECO-JF-16") return new JalecoJF16(Revision::JF16);
|
||||
if(id == "IREM-IF-12" ) return new JalecoJF16(Revision::IF12);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Memory::Readable<n8> programROM;
|
||||
Memory::Readable<n8> characterROM;
|
||||
|
||||
enum class Revision : u32 {
|
||||
JF16,
|
||||
IF12,
|
||||
} revision;
|
||||
|
||||
JalecoJF16(Revision revision) : revision(revision) {}
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(programROM, "program.rom");
|
||||
Interface::load(characterROM, "character.rom");
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
if(address < 0x8000) return data;
|
||||
|
||||
n3 bank;
|
||||
switch(address >> 14 & 1) {
|
||||
case 0: bank = programBank; break;
|
||||
case 1: bank = 7; break;
|
||||
}
|
||||
return programROM.read(bank << 14 | (n14)address);
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address < 0x8000) return;
|
||||
programBank = data.bit(0,2);
|
||||
mirror = data.bit(3);
|
||||
characterBank = data.bit(4,7);
|
||||
}
|
||||
|
||||
auto addressCIRAM(n32 address) const -> n32 {
|
||||
if(revision == Revision::IF12) {
|
||||
return address >> !mirror & 0x0400 | (n10)address;
|
||||
}
|
||||
return mirror << 10 | (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);
|
||||
}
|
||||
|
||||
n1 mirror;
|
||||
n3 programBank;
|
||||
n4 characterBank;
|
||||
};
|
||||
@@ -0,0 +1,74 @@
|
||||
//todo: uPD7756 ADPCM unsupported
|
||||
|
||||
struct JalecoJF17 : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "JALECO-JF-17") return new JalecoJF17(Revision::JF17);
|
||||
if(id == "JALECO-JF-19") return new JalecoJF17(Revision::JF19);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Memory::Readable<n8> programROM;
|
||||
Memory::Readable<n8> characterROM;
|
||||
|
||||
enum class Revision : u32 {
|
||||
JF17,
|
||||
JF19,
|
||||
} revision;
|
||||
|
||||
JalecoJF17(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;
|
||||
|
||||
n4 bank;
|
||||
switch(address >> 14 & 1) {
|
||||
case 0: bank = (revision == Revision::JF17 ? programBank : (n4)0x0); break;
|
||||
case 1: bank = (revision == Revision::JF19 ? programBank : (n4)0xf); break;
|
||||
}
|
||||
return programROM.read(bank << 14 | (n14)address);
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address < 0x8000) return;
|
||||
if(!programSelect && data.bit(7)) programBank = data.bit(0,3);
|
||||
if(!characterSelect && data.bit(6)) characterBank = data.bit(0,3);
|
||||
programSelect = data.bit(7);
|
||||
characterSelect = data.bit(6);
|
||||
}
|
||||
|
||||
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;
|
||||
return characterROM.read(address);
|
||||
}
|
||||
|
||||
auto writeCHR(n32 address, n8 data) -> void override {
|
||||
if(address & 0x2000) {
|
||||
address = address >> !mirror & 0x0400 | (n10)address;
|
||||
return ppu.writeCIRAM(address, data);
|
||||
}
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(mirror);
|
||||
s(programSelect);
|
||||
s(programBank);
|
||||
s(characterSelect);
|
||||
s(characterBank);
|
||||
}
|
||||
|
||||
n1 mirror;
|
||||
n1 programSelect;
|
||||
n4 programBank;
|
||||
n1 characterSelect;
|
||||
n4 characterBank;
|
||||
};
|
||||
@@ -1,9 +1,14 @@
|
||||
//JALECO-JF-(23A,24A,25,27B,29A,37,40)
|
||||
//todo: uPD7756 ADPCM unsupported
|
||||
|
||||
struct JalecoJF : Interface {
|
||||
struct JalecoJF23 : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "JALECO-JF") return new JalecoJF;
|
||||
if(id == "JALECO-JF-23A") return new JalecoJF23;
|
||||
if(id == "JALECO-JF-24A") return new JalecoJF23;
|
||||
if(id == "JALECO-JF-25" ) return new JalecoJF23;
|
||||
if(id == "JALECO-JF-27B") return new JalecoJF23;
|
||||
if(id == "JALECO-JF-29A") return new JalecoJF23;
|
||||
if(id == "JALECO-JF-37" ) return new JalecoJF23;
|
||||
if(id == "JALECO-JF-40" ) return new JalecoJF23;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
struct KonamiVRC2 : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "KONAMI-VRC-2") return new KonamiVRC2;
|
||||
if(id == "KONAMI-VRC-2" ) return new KonamiVRC2(Revision::VRC2);
|
||||
if(id == "KONAMI-VRC-2A") return new KonamiVRC2(Revision::VRC2A);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -9,6 +10,13 @@ struct KonamiVRC2 : Interface {
|
||||
Memory::Readable<n8> characterROM;
|
||||
Memory::Writable<n8> characterRAM;
|
||||
|
||||
enum class Revision : u32 {
|
||||
VRC2,
|
||||
VRC2A,
|
||||
} revision;
|
||||
|
||||
KonamiVRC2(Revision revision) : revision(revision) {}
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(programROM, "program.rom");
|
||||
Interface::load(programRAM, "save.ram");
|
||||
@@ -98,6 +106,7 @@ struct KonamiVRC2 : Interface {
|
||||
|
||||
auto addressCHR(n32 address) const -> n32 {
|
||||
n8 bank = characterBank[address >> 10];
|
||||
if(revision == Revision::VRC2A) bank >>= 1;
|
||||
return bank << 10 | (n10)address;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ struct KonamiVRC3 : Interface {
|
||||
|
||||
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);
|
||||
|
||||
n4 bank = (address < 0xc000 ? programBank : (n4)0xf);
|
||||
@@ -52,6 +53,7 @@ struct KonamiVRC3 : Interface {
|
||||
|
||||
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) {
|
||||
|
||||
@@ -52,6 +52,7 @@ struct KonamiVRC4 : Interface {
|
||||
|
||||
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, banks = programROM.size() >> 13;
|
||||
@@ -67,6 +68,7 @@ struct KonamiVRC4 : Interface {
|
||||
|
||||
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);
|
||||
|
||||
bool a0 = address & pinA0;
|
||||
|
||||
@@ -147,12 +147,14 @@ struct KonamiVRC6 : Interface {
|
||||
|
||||
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);
|
||||
return programROM.read(addressPRG(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);
|
||||
|
||||
bool a0 = address & pinA0;
|
||||
|
||||
@@ -16,6 +16,7 @@ struct KonamiVRC7 : Interface {
|
||||
Interface::load(programRAM, "save.ram");
|
||||
Interface::load(characterROM, "character.rom");
|
||||
Interface::load(characterRAM, "character.ram");
|
||||
pinA0 = 1 << pak->attribute("pinout/a0").natural();
|
||||
|
||||
stream = cartridge.node->append<Node::Audio::Stream>("YM2413");
|
||||
stream->setChannels(1);
|
||||
@@ -71,6 +72,7 @@ struct KonamiVRC7 : Interface {
|
||||
|
||||
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(address);
|
||||
|
||||
n8 bank;
|
||||
@@ -86,29 +88,34 @@ struct KonamiVRC7 : Interface {
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address < 0x6000) return;
|
||||
if(address < 0x8000 && !programRAM) return;
|
||||
if(address < 0x8000) return programRAM.write(address, data);
|
||||
|
||||
bool a0 = address & pinA0;
|
||||
address &= 0xf020;
|
||||
address |= a0;
|
||||
|
||||
switch(address) {
|
||||
case 0x8000: programBank[0] = data; break;
|
||||
case 0x8010: programBank[1] = data; break;
|
||||
case 0x8001: programBank[1] = data; break;
|
||||
case 0x9000: programBank[2] = data; break;
|
||||
case 0x9010: ym2413.address(data); break;
|
||||
case 0x9030: ym2413.write(data); break;
|
||||
case 0x9001: ym2413.address(data); break;
|
||||
case 0x9021: ym2413.write(data); break;
|
||||
case 0xa000: characterBank[0] = data; break;
|
||||
case 0xa010: characterBank[1] = data; break;
|
||||
case 0xa001: characterBank[1] = data; break;
|
||||
case 0xb000: characterBank[2] = data; break;
|
||||
case 0xb010: characterBank[3] = data; break;
|
||||
case 0xb001: characterBank[3] = data; break;
|
||||
case 0xc000: characterBank[4] = data; break;
|
||||
case 0xc010: characterBank[5] = data; break;
|
||||
case 0xc001: characterBank[5] = data; break;
|
||||
case 0xd000: characterBank[6] = data; break;
|
||||
case 0xd010: characterBank[7] = data; break;
|
||||
case 0xd001: characterBank[7] = data; break;
|
||||
case 0xe000:
|
||||
if(disableFM && !data.bit(6)) ym2413.power(1);
|
||||
mirror = data.bit(0,1);
|
||||
disableFM = data.bit(6);
|
||||
ramWritable = data.bit(7);
|
||||
break;
|
||||
case 0xe010:
|
||||
case 0xe001:
|
||||
irqLatch = data;
|
||||
break;
|
||||
case 0xf000:
|
||||
@@ -121,7 +128,7 @@ struct KonamiVRC7 : Interface {
|
||||
}
|
||||
irqLine = 0;
|
||||
break;
|
||||
case 0xf010:
|
||||
case 0xf001:
|
||||
irqEnable = irqAcknowledge;
|
||||
irqLine = 0;
|
||||
break;
|
||||
@@ -193,4 +200,7 @@ struct KonamiVRC7 : Interface {
|
||||
i16 irqScalar;
|
||||
n1 irqLine;
|
||||
n6 divider;
|
||||
|
||||
//unserialized:
|
||||
n8 pinA0;
|
||||
};
|
||||
@@ -0,0 +1,75 @@
|
||||
struct MltAction52 : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "MLT-ACTION52") return new MltAction52();
|
||||
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 readPRG(n32 address, n8 data) -> n8 override {
|
||||
if(address < 0x8000) return data;
|
||||
|
||||
n2 chipOffset;
|
||||
switch(programChip) {
|
||||
case 0: chipOffset = 0; break;
|
||||
case 1: chipOffset = 1; break;
|
||||
case 2: return data;
|
||||
case 3: chipOffset = 2; break;
|
||||
}
|
||||
|
||||
bool region = address.bit(14);
|
||||
n5 bank = programMode ? programBank : (n5)(programBank & ~1 | region);
|
||||
address = chipOffset << 19 | bank << 14 | (n14)address;
|
||||
return programROM.read(address);
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address < 0x8000) return;
|
||||
|
||||
characterBank = address.bit(0,3) << 2 | data.bit(0,1);
|
||||
programMode = address.bit(5);
|
||||
programBank = address.bit(6,10);
|
||||
programChip = address.bit(11,12);
|
||||
mirror = address.bit(13);
|
||||
}
|
||||
|
||||
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);
|
||||
return data;
|
||||
}
|
||||
|
||||
auto writeCHR(n32 address, n8 data) -> void override {
|
||||
if(address & 0x2000) {
|
||||
address = address >> mirror & 0x0400 | (n10)address;
|
||||
return ppu.writeCIRAM(address, data);
|
||||
}
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(characterBank);
|
||||
s(programMode);
|
||||
s(programBank);
|
||||
s(programChip);
|
||||
s(mirror);
|
||||
}
|
||||
|
||||
n6 characterBank;
|
||||
n1 programMode;
|
||||
n5 programBank;
|
||||
n2 programChip;
|
||||
n1 mirror;
|
||||
};
|
||||
@@ -0,0 +1,136 @@
|
||||
struct Namco118 : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "NAMCO-118" ) return new Namco118(Revision::N118);
|
||||
if(id == "NAMCO-3425") return new Namco118(Revision::N3425);
|
||||
if(id == "NAMCO-3433") return new Namco118(Revision::N3433);
|
||||
if(id == "NAMCO-3446") return new Namco118(Revision::N3446);
|
||||
if(id == "NAMCO-3453") return new Namco118(Revision::N3453);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Memory::Readable<n8> programROM;
|
||||
Memory::Readable<n8> characterROM;
|
||||
Memory::Writable<n8> characterCIRAM;
|
||||
|
||||
enum class Revision : u32 {
|
||||
N118,
|
||||
N3425,
|
||||
N3433,
|
||||
N3446,
|
||||
N3453,
|
||||
} revision;
|
||||
|
||||
Namco118(Revision revision) : revision(revision) {}
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(programROM, "program.rom");
|
||||
Interface::load(characterROM, "character.rom");
|
||||
Interface::load(characterCIRAM, "character.ram");
|
||||
mirror = pak->attribute("mirror") == "vertical";
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
Interface::save(characterCIRAM, "character.ram");
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
if(address < 0x8000) return data;
|
||||
|
||||
n4 bank;
|
||||
switch(address >> 13 & 3) {
|
||||
case 0: bank = programBank[0]; break;
|
||||
case 1: bank = programBank[1]; break;
|
||||
case 2: bank = 0x0e; break;
|
||||
case 3: bank = 0x0f; break;
|
||||
}
|
||||
return programROM.read(bank << 13 | (n13)address);
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address >= 0x8000) nametableBank = data.bit(6);
|
||||
|
||||
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 addressCHR(n32 address) const -> n32 {
|
||||
n32 chrAddress = 0;
|
||||
if(revision == Revision::N3446) {
|
||||
switch(address) {
|
||||
case 0x0000 ... 0x07ff: chrAddress = characterBank[2] << 11 | (n11)address; break;
|
||||
case 0x0800 ... 0x0fff: chrAddress = characterBank[3] << 11 | (n11)address; break;
|
||||
case 0x1000 ... 0x17ff: chrAddress = characterBank[4] << 11 | (n11)address; break;
|
||||
case 0x1800 ... 0xffff: chrAddress = characterBank[5] << 11 | (n11)address; break;
|
||||
}
|
||||
} else {
|
||||
switch(address) {
|
||||
case 0x0000 ... 0x07ff: chrAddress = characterBank[0] << 11 | (n11)address; break;
|
||||
case 0x0800 ... 0x0fff: chrAddress = characterBank[1] << 11 | (n11)address; break;
|
||||
case 0x1000 ... 0x13ff: chrAddress = characterBank[2] << 10 | (n10)address; break;
|
||||
case 0x1400 ... 0x17ff: chrAddress = characterBank[3] << 10 | (n10)address; break;
|
||||
case 0x1800 ... 0x1bff: chrAddress = characterBank[4] << 10 | (n10)address; break;
|
||||
case 0x1c00 ... 0x1fff: chrAddress = characterBank[5] << 10 | (n10)address; break;
|
||||
}
|
||||
chrAddress |= (address & 0x1000) << 4;
|
||||
}
|
||||
return chrAddress;
|
||||
}
|
||||
|
||||
auto addressCIRAM(n32 address) const -> n32 {
|
||||
if(revision == Revision::N3425) {
|
||||
n6 bank = characterBank[address >> 11 & 1];
|
||||
return bank.bit(4) << 10 | (n10)address;
|
||||
}
|
||||
if(revision == Revision::N3453) {
|
||||
return nametableBank << 10 | (n10)address;
|
||||
}
|
||||
return address >> !mirror & 0x0400 | (n10)address;
|
||||
}
|
||||
|
||||
auto readCHR(n32 address, n8 data) -> n8 override {
|
||||
if(address & 0x2000) {
|
||||
if(!characterCIRAM) return ppu.readCIRAM(addressCIRAM(address));
|
||||
if(address & 0x0800) return characterCIRAM.read((n11)address);
|
||||
return ppu.readCIRAM((n11)address);
|
||||
}
|
||||
return characterROM.read(addressCHR(address));
|
||||
}
|
||||
|
||||
auto writeCHR(n32 address, n8 data) -> void override {
|
||||
if(address & 0x2000) {
|
||||
if(!characterCIRAM) return ppu.writeCIRAM(addressCIRAM(address), data);
|
||||
if(address & 0x0800) return characterCIRAM.write((n11)address, data);
|
||||
return ppu.writeCIRAM((n11)address, data);
|
||||
}
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(characterCIRAM);
|
||||
s(mirror);
|
||||
s(bankSelect);
|
||||
s(programBank);
|
||||
s(characterBank);
|
||||
s(nametableBank);
|
||||
}
|
||||
|
||||
n1 mirror;
|
||||
n3 bankSelect;
|
||||
n4 programBank[2];
|
||||
n6 characterBank[6];
|
||||
n1 nametableBank;
|
||||
};
|
||||
@@ -0,0 +1,148 @@
|
||||
//todo: sound
|
||||
|
||||
struct Namco163 : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "NAMCO-163") return new Namco163;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Memory::Readable<n8> programROM;
|
||||
Memory::Writable<n8> programRAM;
|
||||
Memory::Readable<n8> characterROM;
|
||||
|
||||
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 main() -> void override {
|
||||
cpu.irqLine(irqLine);
|
||||
if(irqEnable) {
|
||||
if(irqCounter != 0x7fff && ++irqCounter == 0x7fff) irqLine = 1;
|
||||
}
|
||||
tick();
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
if(address < 0x4800) return data;
|
||||
if(address < 0x5000) return data;
|
||||
//if(address < 0x5000) return soundRAM.read(soundAddress);
|
||||
|
||||
if(address < 0x6000) {
|
||||
if(address < 0x5800) return irqCounter.bit(0,7);
|
||||
if(address < 0x6000) return irqCounter.bit(8,14) | irqEnable << 7;
|
||||
}
|
||||
|
||||
if(address < 0x8000) {
|
||||
if(!programRAM) return data;
|
||||
return programRAM.read((n13)address);
|
||||
}
|
||||
|
||||
n6 bank;
|
||||
switch(address >> 13 & 3) {
|
||||
case 0: bank = programBank[0]; break;
|
||||
case 1: bank = programBank[1]; break;
|
||||
case 2: bank = programBank[2]; break;
|
||||
case 3: bank = 0x3f; break;
|
||||
}
|
||||
return programROM.read(bank << 13 | (n13)address);
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address >= 0x6000 && address < 0x8000) {
|
||||
if(!programRAM) return;
|
||||
if(!ramEnable[address >> 11 & 3]) return;
|
||||
return programRAM.write((n13)address, data);
|
||||
}
|
||||
|
||||
switch(address & 0xf800) {
|
||||
case 0x4800:
|
||||
//soundRAM.write(soundAddres, data);
|
||||
break;
|
||||
case 0x5000:
|
||||
irqCounter.bit(0,7) = data;
|
||||
irqLine = 0;
|
||||
break;
|
||||
case 0x5800:
|
||||
irqCounter.bit(8,14) = data.bit(0,6);
|
||||
irqEnable = data.bit(7);
|
||||
irqLine = 0;
|
||||
break;
|
||||
case 0x8000: characterBank[0] = data; break;
|
||||
case 0x8800: characterBank[1] = data; break;
|
||||
case 0x9000: characterBank[2] = data; break;
|
||||
case 0x9800: characterBank[3] = data; break;
|
||||
case 0xa000: characterBank[4] = data; break;
|
||||
case 0xa800: characterBank[5] = data; break;
|
||||
case 0xb000: characterBank[6] = data; break;
|
||||
case 0xb800: characterBank[7] = data; break;
|
||||
case 0xc000: characterBank[8] = data; break;
|
||||
case 0xc800: characterBank[9] = data; break;
|
||||
case 0xd000: characterBank[10] = data; break;
|
||||
case 0xd800: characterBank[11] = data; break;
|
||||
case 0xe000:
|
||||
programBank[0] = data.bit(0,5);
|
||||
//soundEnable = !data.bit(6);
|
||||
break;
|
||||
case 0xe800:
|
||||
programBank[1] = data.bit(0,5);
|
||||
ciramEnable[0] = !data.bit(6);
|
||||
ciramEnable[1] = !data.bit(7);
|
||||
break;
|
||||
case 0xf000:
|
||||
programBank[2] = data.bit(0,5);
|
||||
break;
|
||||
case 0xf800:
|
||||
ramEnable[0] = data.bit(4,7) == 4 && !data.bit(0);
|
||||
ramEnable[1] = data.bit(4,7) == 4 && !data.bit(1);
|
||||
ramEnable[2] = data.bit(4,7) == 4 && !data.bit(2);
|
||||
ramEnable[3] = data.bit(4,7) == 4 && !data.bit(3);
|
||||
//soundAddress = data.bit(0,6);
|
||||
//soundRepeat = data.bit(7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto readCHR(n32 address, n8 data) -> n8 override {
|
||||
if(address & 0x2000) address.bit(12) = 0;
|
||||
bool ciramSelect = address & 0x2000 || ciramEnable[address >> 12 & 1];
|
||||
n8 bank = characterBank[address >> 10];
|
||||
if(bank >= 0xe0 && ciramSelect) {
|
||||
return ppu.readCIRAM(bank.bit(0) << 10 | (n10)address);
|
||||
}
|
||||
return characterROM.read(bank << 10 | (n10)address);
|
||||
}
|
||||
|
||||
auto writeCHR(n32 address, n8 data) -> void override {
|
||||
if(address & 0x2000) address.bit(12) = 0;
|
||||
bool ciramSelect = address & 0x2000 || ciramEnable[address >> 12 & 1];
|
||||
n8 bank = characterBank[address >> 10];
|
||||
if(bank >= 0xe0 && ciramSelect) {
|
||||
return ppu.writeCIRAM(bank.bit(0) << 10 | (n10)address, data);
|
||||
}
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(programRAM);
|
||||
s(ramEnable);
|
||||
s(ciramEnable);
|
||||
s(programBank);
|
||||
s(characterBank);
|
||||
s(irqCounter);
|
||||
s(irqEnable);
|
||||
s(irqLine);
|
||||
}
|
||||
|
||||
n1 ramEnable[4];
|
||||
n1 ciramEnable[2];
|
||||
n6 programBank[3];
|
||||
n8 characterBank[12];
|
||||
n15 irqCounter;
|
||||
n1 irqEnable;
|
||||
n1 irqLine;
|
||||
};
|
||||
@@ -0,0 +1,108 @@
|
||||
struct Namco340 : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "NAMCO-175") return new Namco340(Revision::N175);
|
||||
if(id == "NAMCO-340") return new Namco340(Revision::N340);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Memory::Readable<n8> programROM;
|
||||
Memory::Writable<n8> programRAM;
|
||||
Memory::Readable<n8> characterROM;
|
||||
|
||||
enum class Revision : u32 {
|
||||
N175,
|
||||
N340,
|
||||
} revision;
|
||||
|
||||
Namco340(Revision revision) : revision(revision) {}
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(programROM, "program.rom");
|
||||
Interface::load(programRAM, "save.ram");
|
||||
Interface::load(characterROM, "character.rom");
|
||||
staticMirror = pak->attribute("mirror") == "vertical";
|
||||
}
|
||||
|
||||
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) {
|
||||
if(!ramEnable || !programRAM) return data;
|
||||
return programRAM.read((n13)address);
|
||||
}
|
||||
|
||||
n6 bank;
|
||||
switch(address >> 13 & 3) {
|
||||
case 0: bank = programBank[0]; break;
|
||||
case 1: bank = programBank[1]; break;
|
||||
case 2: bank = programBank[2]; break;
|
||||
case 3: bank = 0x3f; break;
|
||||
}
|
||||
return programROM.read(bank << 13 | (n13)address);
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address < 0x6000) return;
|
||||
if(address < 0x8000) {
|
||||
if(!ramEnable || !programRAM) return;
|
||||
return programRAM.write((n13)address, data);
|
||||
}
|
||||
|
||||
switch(address & 0xf800) {
|
||||
case 0x8000: characterBank[0] = data; break;
|
||||
case 0x8800: characterBank[1] = data; break;
|
||||
case 0x9000: characterBank[2] = data; break;
|
||||
case 0x9800: characterBank[3] = data; break;
|
||||
case 0xa000: characterBank[4] = data; break;
|
||||
case 0xa800: characterBank[5] = data; break;
|
||||
case 0xb000: characterBank[6] = data; break;
|
||||
case 0xb800: characterBank[7] = data; break;
|
||||
case 0xc000: ramEnable = data.bit(0); break;
|
||||
case 0xe000: programBank[0] = data.bit(0,5); mirror = data.bit(6,7); break;
|
||||
case 0xe800: programBank[1] = data.bit(0,5); break;
|
||||
case 0xf000: programBank[2] = data.bit(0,5); break;
|
||||
}
|
||||
}
|
||||
|
||||
auto addressCIRAM(n32 address) const -> n32 {
|
||||
if(revision == Revision::N175) {
|
||||
return address >> !staticMirror & 0x0400 | (n10)address;
|
||||
}
|
||||
|
||||
switch(mirror) {
|
||||
case 0: return 0x0000 | (n10)address;
|
||||
case 1: return address >> 0 & 0x0400 | (n10)address;
|
||||
case 2: return 0x0400 | (n10)address;
|
||||
case 3: return address >> 1 & 0x0400 | (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(programRAM);
|
||||
s(mirror);
|
||||
s(staticMirror);
|
||||
s(ramEnable);
|
||||
s(programBank);
|
||||
s(characterBank);
|
||||
}
|
||||
|
||||
n2 mirror;
|
||||
n1 staticMirror;
|
||||
n1 ramEnable;
|
||||
n6 programBank[3];
|
||||
n8 characterBank[8];
|
||||
};
|
||||
@@ -66,7 +66,7 @@ struct Sunsoft5B : Interface {
|
||||
bank &= 0x3f;
|
||||
|
||||
if(ramSelect) {
|
||||
if(!ramEnable) return data;
|
||||
if(!ramEnable || !programRAM) return data;
|
||||
return programRAM.read((n13)address);
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ struct Sunsoft5B : Interface {
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if((address & 0xe000) == 0x6000) {
|
||||
programRAM.write((n13)address, data);
|
||||
if(programRAM) programRAM.write((n13)address, data);
|
||||
}
|
||||
|
||||
if(address == 0x8000) {
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
struct TaitoTC0190 : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "TAITO-TC0190") return new TaitoTC0190;
|
||||
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 readPRG(n32 address, n8 data) -> n8 override {
|
||||
if(address < 0x8000) return data;
|
||||
|
||||
n6 bank;
|
||||
switch(address >> 13 & 3) {
|
||||
case 0: bank = programBank[0]; break;
|
||||
case 1: bank = programBank[1]; break;
|
||||
case 2: bank = 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 & 0xa003) {
|
||||
case 0x8000:
|
||||
programBank[0] = data.bit(0,5);
|
||||
mirror = data.bit(6);
|
||||
break;
|
||||
case 0x8001:
|
||||
programBank[1] = data.bit(0,5);
|
||||
break;
|
||||
case 0x8002: characterBank[0] = data; break;
|
||||
case 0x8003: characterBank[1] = data; break;
|
||||
case 0xa000: characterBank[2] = data; break;
|
||||
case 0xa001: characterBank[3] = data; break;
|
||||
case 0xa002: characterBank[4] = data; break;
|
||||
case 0xa003: characterBank[5] = data; break;
|
||||
}
|
||||
}
|
||||
|
||||
auto addressCHR(n32 address) const -> n32 {
|
||||
if(address <= 0x07ff) return characterBank[0] << 11 | (n11)address;
|
||||
if(address <= 0x0fff) return characterBank[1] << 11 | (n11)address;
|
||||
if(address <= 0x13ff) return characterBank[2] << 10 | (n10)address;
|
||||
if(address <= 0x17ff) return characterBank[3] << 10 | (n10)address;
|
||||
if(address <= 0x1bff) return characterBank[4] << 10 | (n10)address;
|
||||
if(address <= 0x1fff) return characterBank[5] << 10 | (n10)address;
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto addressCIRAM(n32 address) const -> n32 {
|
||||
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(addressCHR(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);
|
||||
}
|
||||
|
||||
n1 mirror;
|
||||
n6 programBank[2];
|
||||
n8 characterBank[6];
|
||||
};
|
||||
@@ -0,0 +1,130 @@
|
||||
struct TaitoTC0690 : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "TAITO-TC0690") return new TaitoTC0690;
|
||||
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(irqDelay) irqDelay--;
|
||||
if(irqLineDelay && --irqLineDelay == 0) irqLine = 1;
|
||||
cpu.irqLine(irqLine);
|
||||
tick();
|
||||
}
|
||||
|
||||
auto irqTest(n16 address) -> void {
|
||||
if(!(characterAddress & 0x1000) && (address & 0x1000)) {
|
||||
if(irqDelay == 0) {
|
||||
if(irqCounter == 0) {
|
||||
irqCounter = irqLatch + 1;
|
||||
}
|
||||
if(--irqCounter == 0) {
|
||||
if(irqEnable) irqLineDelay = 6;
|
||||
}
|
||||
}
|
||||
irqDelay = 6;
|
||||
}
|
||||
characterAddress = address;
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
if(address < 0x8000) return data;
|
||||
|
||||
n6 bank;
|
||||
switch(address >> 13 & 3) {
|
||||
case 0: bank = programBank[0]; break;
|
||||
case 1: bank = programBank[1]; break;
|
||||
case 2: bank = 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 & 0xe003) {
|
||||
case 0x8000: case 0x8001:
|
||||
programBank[address & 1] = data.bit(0,5);
|
||||
break;
|
||||
case 0x8002: characterBank[0] = data; break;
|
||||
case 0x8003: characterBank[1] = data; break;
|
||||
case 0xa000: characterBank[2] = data; break;
|
||||
case 0xa001: characterBank[3] = data; break;
|
||||
case 0xa002: characterBank[4] = data; break;
|
||||
case 0xa003: characterBank[5] = data; break;
|
||||
case 0xc000:
|
||||
irqLatch = ~data;
|
||||
break;
|
||||
case 0xc001:
|
||||
irqCounter = 0;
|
||||
break;
|
||||
case 0xc002:
|
||||
irqEnable = 1;
|
||||
break;
|
||||
case 0xc003:
|
||||
irqEnable = 0;
|
||||
irqLine = 0;
|
||||
break;
|
||||
case 0xe000:
|
||||
mirror = data.bit(6);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto addressCHR(n32 address) const -> n32 {
|
||||
if(address <= 0x07ff) return characterBank[0] << 11 | (n11)address;
|
||||
if(address <= 0x0fff) return characterBank[1] << 11 | (n11)address;
|
||||
if(address <= 0x13ff) return characterBank[2] << 10 | (n10)address;
|
||||
if(address <= 0x17ff) return characterBank[3] << 10 | (n10)address;
|
||||
if(address <= 0x1bff) return characterBank[4] << 10 | (n10)address;
|
||||
if(address <= 0x1fff) return characterBank[5] << 10 | (n10)address;
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto addressCIRAM(n32 address) const -> n32 {
|
||||
return address >> mirror & 0x0400 | (n10)address;
|
||||
}
|
||||
|
||||
auto readCHR(n32 address, n8 data) -> n8 override {
|
||||
irqTest(address);
|
||||
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
|
||||
return characterROM.read(addressCHR(address));
|
||||
}
|
||||
|
||||
auto writeCHR(n32 address, n8 data) -> void override {
|
||||
irqTest(address);
|
||||
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(mirror);
|
||||
s(programBank);
|
||||
s(characterBank);
|
||||
s(irqLatch);
|
||||
s(irqCounter);
|
||||
s(irqEnable);
|
||||
s(irqDelay);
|
||||
s(irqLineDelay);
|
||||
s(irqLine);
|
||||
s(characterAddress);
|
||||
}
|
||||
|
||||
n1 mirror;
|
||||
n6 programBank[2];
|
||||
n8 characterBank[6];
|
||||
n8 irqLatch;
|
||||
n8 irqCounter;
|
||||
n1 irqEnable;
|
||||
n8 irqDelay;
|
||||
n8 irqLineDelay;
|
||||
n1 irqLine;
|
||||
n16 characterAddress;
|
||||
};
|
||||
@@ -0,0 +1,110 @@
|
||||
struct TaitoX1005 : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "TAITO-X1-005" ) return new TaitoX1005(Revision::X1005);
|
||||
if(id == "TAITO-X1-005A") return new TaitoX1005(Revision::X1005A);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Memory::Readable<n8> programROM;
|
||||
Memory::Writable<n8> programRAM;
|
||||
Memory::Readable<n8> characterROM;
|
||||
|
||||
enum class Revision : u32 {
|
||||
X1005,
|
||||
X1005A,
|
||||
} revision;
|
||||
|
||||
TaitoX1005(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 < 0x7f00) return data;
|
||||
|
||||
if(address < 0x8000) {
|
||||
if(!ramEnable) return data;
|
||||
return programRAM.read((n7)address);
|
||||
}
|
||||
|
||||
n6 bank;
|
||||
switch(address >> 13 & 3) {
|
||||
case 0: bank = programBank[0]; break;
|
||||
case 1: bank = programBank[1]; break;
|
||||
case 2: bank = programBank[2]; break;
|
||||
case 3: bank = 0x3f; break;
|
||||
}
|
||||
return programROM.read(bank << 13 | (n13)address);
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address >= 0x7f00 && address < 0x8000) {
|
||||
if(!ramEnable) return;
|
||||
return programRAM.write((n7)address, data);
|
||||
}
|
||||
|
||||
switch(address) {
|
||||
case 0x7ef0 ... 0x7ef1:
|
||||
characterBank[address & 1] = data >> 1;
|
||||
nametableBank[address & 1] = data.bit(7);
|
||||
break;
|
||||
case 0x7ef2: characterBank[2] = data; break;
|
||||
case 0x7ef3: characterBank[3] = data; break;
|
||||
case 0x7ef4: characterBank[4] = data; break;
|
||||
case 0x7ef5: characterBank[5] = data; break;
|
||||
case 0x7ef6 ... 0x7ef7: mirror = data.bit(0); break;
|
||||
case 0x7ef8 ... 0x7ef9: ramEnable = data == 0xa3; break;
|
||||
case 0x7efa ... 0x7efb: programBank[0] = data.bit(0,5); break;
|
||||
case 0x7efc ... 0x7efd: programBank[1] = data.bit(0,5); break;
|
||||
case 0x7efe ... 0x7eff: programBank[2] = data.bit(0,5); break;
|
||||
}
|
||||
}
|
||||
|
||||
auto addressCHR(n32 address) const -> n32 {
|
||||
if(address <= 0x07ff) return characterBank[0] << 11 | (n11)address;
|
||||
if(address <= 0x0fff) return characterBank[1] << 11 | (n11)address;
|
||||
if(address <= 0x13ff) return characterBank[2] << 10 | (n10)address;
|
||||
if(address <= 0x17ff) return characterBank[3] << 10 | (n10)address;
|
||||
if(address <= 0x1bff) return characterBank[4] << 10 | (n10)address;
|
||||
if(address <= 0x1fff) return characterBank[5] << 10 | (n10)address;
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto addressCIRAM(n32 address) const -> n32 {
|
||||
if(revision == Revision::X1005A) {
|
||||
return nametableBank[address >> 12 & 1] << 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(addressCHR(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(mirror);
|
||||
s(ramEnable);
|
||||
s(programBank);
|
||||
s(characterBank);
|
||||
s(nametableBank);
|
||||
}
|
||||
|
||||
n1 mirror;
|
||||
n1 ramEnable;
|
||||
n6 programBank[3];
|
||||
n8 characterBank[6];
|
||||
n1 nametableBank[2];
|
||||
};
|
||||
@@ -0,0 +1,102 @@
|
||||
struct TaitoX1017 : Interface {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "TAITO-X1-017") return new TaitoX1017;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Memory::Readable<n8> programROM;
|
||||
Memory::Writable<n8> programRAM;
|
||||
Memory::Readable<n8> characterROM;
|
||||
|
||||
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 && address < 0x7400) {
|
||||
if(!ramEnable[address >> 11 & 3]) return data;
|
||||
return programRAM.read((n13)address);
|
||||
}
|
||||
if(address < 0x8000) return data;
|
||||
|
||||
n6 bank;
|
||||
switch(address >> 13 & 3) {
|
||||
case 0: bank = programBank[0]; break;
|
||||
case 1: bank = programBank[1]; break;
|
||||
case 2: bank = programBank[2]; break;
|
||||
case 3: bank = 0x3f; break;
|
||||
}
|
||||
return programROM.read(bank << 13 | (n13)address);
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address >= 0x6000 && address < 0x7400) {
|
||||
if(!ramEnable[address >> 11 & 3]) return;
|
||||
return programRAM.write((n13)address, data);
|
||||
}
|
||||
|
||||
switch(address) {
|
||||
case 0x7ef0: characterBank[0] = data >> 1; break;
|
||||
case 0x7ef1: characterBank[1] = data >> 1; break;
|
||||
case 0x7ef2: characterBank[2] = data; break;
|
||||
case 0x7ef3: characterBank[3] = data; break;
|
||||
case 0x7ef4: characterBank[4] = data; break;
|
||||
case 0x7ef5: characterBank[5] = data; break;
|
||||
case 0x7ef6:
|
||||
mirror = data.bit(0);
|
||||
characterMode = data.bit(1);
|
||||
break;
|
||||
case 0x7ef7: ramEnable[0] = data == 0xca; break;
|
||||
case 0x7ef8: ramEnable[1] = data == 0x69; break;
|
||||
case 0x7ef9: ramEnable[2] = data == 0x84; break;
|
||||
case 0x7efa: programBank[0] = data >> 2; break;
|
||||
case 0x7efb: programBank[1] = data >> 2; break;
|
||||
case 0x7efc: programBank[2] = data >> 2; break;
|
||||
}
|
||||
}
|
||||
|
||||
auto addressCHR(n32 address) const -> n32 {
|
||||
if(characterMode) address ^= 0x1000;
|
||||
if(address <= 0x07ff) return characterBank[0] << 11 | (n11)address;
|
||||
if(address <= 0x0fff) return characterBank[1] << 11 | (n11)address;
|
||||
if(address <= 0x13ff) return characterBank[2] << 10 | (n10)address;
|
||||
if(address <= 0x17ff) return characterBank[3] << 10 | (n10)address;
|
||||
if(address <= 0x1bff) return characterBank[4] << 10 | (n10)address;
|
||||
if(address <= 0x1fff) return characterBank[5] << 10 | (n10)address;
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto addressCIRAM(n32 address) const -> n32 {
|
||||
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(addressCHR(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(mirror);
|
||||
s(ramEnable);
|
||||
s(programBank);
|
||||
s(characterBank);
|
||||
s(characterMode);
|
||||
}
|
||||
|
||||
n1 mirror;
|
||||
n1 ramEnable[3];
|
||||
n6 programBank[3];
|
||||
n8 characterBank[6];
|
||||
n1 characterMode;
|
||||
};
|
||||
@@ -138,6 +138,7 @@ auto PPU::renderScanline() -> void {
|
||||
|
||||
//257-320
|
||||
for(u32 sprite : range(8)) {
|
||||
if(enable()) io.oamAddress = 0;
|
||||
u32 nametable = loadCHR(0x2000 | (n12)io.v.address);
|
||||
step(1);
|
||||
|
||||
@@ -198,9 +199,8 @@ auto PPU::renderScanline() -> void {
|
||||
|
||||
//337-338
|
||||
loadCHR(0x2000 | (n12)io.v.address);
|
||||
step(1);
|
||||
bool skip = enable() && io.field == 1 && io.ly == vlines() - 1;
|
||||
step(1);
|
||||
bool skip = !Region::PAL() && enable() && io.field == 1 && io.ly == vlines() - 1;
|
||||
step(2);
|
||||
|
||||
//339
|
||||
loadCHR(0x2000 | (n12)io.v.address);
|
||||
|
||||
+14
-8
@@ -34,6 +34,18 @@ auto PPU::readTileCGB(bool select, u32 x, u32 y, n16& tiledata, n8& attributes)
|
||||
if(attributes.bit(5)) tiledata = hflip(tiledata);
|
||||
}
|
||||
|
||||
auto PPU::readObjectCGB(i16 y, n8 tile, n8 attributes, n16& tiledata) -> void {
|
||||
const s32 Height = (status.obSize == 0 ? 8 : 16);
|
||||
tile &= ~status.obSize;
|
||||
y = status.ly - y;
|
||||
|
||||
if(attributes.bit(6)) y ^= Height - 1;
|
||||
n14 tiledataAddress = (attributes.bit(3) ? 0x2000 : 0x0000) + (tile << 4) + (y << 1);
|
||||
tiledata.byte(0) = vram[tiledataAddress + 0];
|
||||
tiledata.byte(1) = vram[tiledataAddress + 1];
|
||||
if(attributes.bit(5)) tiledata = hflip(tiledata);
|
||||
}
|
||||
|
||||
auto PPU::scanlineCGB() -> void {
|
||||
px = 0;
|
||||
|
||||
@@ -45,18 +57,11 @@ auto PPU::scanlineCGB() -> void {
|
||||
Sprite& s = sprite[sprites];
|
||||
s.y = oam[n + 0] - 16;
|
||||
s.x = oam[n + 1] - 8;
|
||||
s.tile = oam[n + 2] & ~status.obSize;
|
||||
s.tile = oam[n + 2];
|
||||
s.attributes = oam[n + 3];
|
||||
|
||||
if(s32(status.ly) < s.y) continue;
|
||||
if(s32(status.ly) >= s.y + Height) continue;
|
||||
s.y = status.ly - s.y;
|
||||
|
||||
if(s.attributes.bit(6)) s.y ^= Height - 1;
|
||||
n14 tiledataAddress = (s.attributes.bit(3) ? 0x2000 : 0x0000) + (s.tile << 4) + (s.y << 1);
|
||||
s.tiledata.byte(0) = vram[tiledataAddress + 0];
|
||||
s.tiledata.byte(1) = vram[tiledataAddress + 1];
|
||||
if(s.attributes.bit(5)) s.tiledata = hflip(s.tiledata);
|
||||
|
||||
if(++sprites == 10) break;
|
||||
}
|
||||
@@ -135,6 +140,7 @@ auto PPU::runObjectsCGB() -> void {
|
||||
|
||||
s32 tileX = px - s.x;
|
||||
if(tileX < 0 || tileX > 7) continue;
|
||||
if(tileX == 0 || px == 0) readObjectCGB(s.y, s.tile, s.attributes, s.tiledata);
|
||||
|
||||
n2 index;
|
||||
index.bit(0) = s.tiledata.bit( 7 - tileX);
|
||||
|
||||
+15
-8
@@ -23,6 +23,19 @@ auto PPU::readTileDMG(bool select, u32 x, u32 y, n16& tiledata) -> void {
|
||||
tiledata.byte(1) = vram[tiledataAddress + 1];
|
||||
}
|
||||
|
||||
auto PPU::readObjectDMG(i16 y, n8 tile, n8 attributes, n16& tiledata) -> void {
|
||||
const s32 Height = (status.obSize == 0 ? 8 : 16);
|
||||
|
||||
tile &= ~status.obSize;
|
||||
y = s32(status.ly) - y;
|
||||
|
||||
if(attributes.bit(6)) y ^= Height - 1;
|
||||
n13 tiledataAddress = (tile << 4) + (y << 1);
|
||||
tiledata.byte(0) = vram[tiledataAddress + 0];
|
||||
tiledata.byte(1) = vram[tiledataAddress + 1];
|
||||
if(attributes.bit(5)) tiledata = hflip(tiledata);
|
||||
}
|
||||
|
||||
auto PPU::scanlineDMG() -> void {
|
||||
px = 0;
|
||||
|
||||
@@ -34,18 +47,11 @@ auto PPU::scanlineDMG() -> void {
|
||||
Sprite& s = sprite[sprites];
|
||||
s.y = oam[n + 0] - 16;
|
||||
s.x = oam[n + 1] - 8;
|
||||
s.tile = oam[n + 2] & ~status.obSize;
|
||||
s.tile = oam[n + 2];
|
||||
s.attributes = oam[n + 3];
|
||||
|
||||
if(s32(status.ly) < s.y) continue;
|
||||
if(s32(status.ly) >= s.y + Height) continue;
|
||||
s.y = s32(status.ly) - s.y;
|
||||
|
||||
if(s.attributes.bit(6)) s.y ^= Height - 1;
|
||||
n13 tiledataAddress = (s.tile << 4) + (s.y << 1);
|
||||
s.tiledata.byte(0) = vram[tiledataAddress + 0];
|
||||
s.tiledata.byte(1) = vram[tiledataAddress + 1];
|
||||
if(s.attributes.bit(5)) s.tiledata = hflip(s.tiledata);
|
||||
|
||||
if(++sprites == 10) break;
|
||||
}
|
||||
@@ -125,6 +131,7 @@ auto PPU::runObjectsDMG() -> void {
|
||||
|
||||
s32 tileX = px - s.x;
|
||||
if(tileX < 0 || tileX > 7) continue;
|
||||
if(tileX == 0 || px == 0) readObjectDMG(s.y, s.tile, s.attributes, s.tiledata);
|
||||
|
||||
n2 index;
|
||||
index.bit(0) = s.tiledata.bit( 7 - tileX);
|
||||
|
||||
+3
-3
@@ -90,9 +90,6 @@ auto PPU::main() -> void {
|
||||
|
||||
status.lx = 0;
|
||||
|
||||
latch.windowDisplayEnable = status.windowDisplayEnable;
|
||||
latch.wx = status.wx;
|
||||
|
||||
if(status.ly == 0) {
|
||||
latch.wy = 0;
|
||||
}
|
||||
@@ -112,6 +109,9 @@ auto PPU::main() -> void {
|
||||
scanline();
|
||||
step(80);
|
||||
|
||||
latch.windowDisplayEnable = status.windowDisplayEnable;
|
||||
latch.wx = status.wx;
|
||||
|
||||
if(status.ly >= status.wy && status.wx < 7) latch.wy++;
|
||||
|
||||
mode(3);
|
||||
|
||||
@@ -52,6 +52,7 @@ struct PPU : Thread {
|
||||
|
||||
//dmg.cpp
|
||||
auto readTileDMG(bool select, u32 x, u32 y, n16& tiledata) -> void;
|
||||
auto readObjectDMG(i16 y, n8 tile, n8 attributes, n16& tiledata) -> void;
|
||||
auto scanlineDMG() -> void;
|
||||
auto runDMG() -> void;
|
||||
auto runBackgroundDMG() -> void;
|
||||
@@ -60,6 +61,7 @@ struct PPU : Thread {
|
||||
|
||||
//cgb.cpp
|
||||
auto readTileCGB(bool select, u32 x, u32 y, n16& tiledata, n8& attributes) -> void;
|
||||
auto readObjectCGB(i16 y, n8 tile, n8 attributes, n16& tiledata) -> void;
|
||||
auto scanlineCGB() -> void;
|
||||
auto runCGB() -> void;
|
||||
auto runBackgroundCGB() -> void;
|
||||
|
||||
@@ -12,7 +12,7 @@ auto PPU::color(n32 color) -> n64 {
|
||||
f64 lb = pow(B / 31.0, lcdGamma);
|
||||
f64 lg = pow(G / 31.0, lcdGamma);
|
||||
f64 lr = pow(R / 31.0, lcdGamma);
|
||||
r = pow(( 0 * lb + 50 * lg + 255 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280);
|
||||
r = pow(( 0 * lb + 50 * lg + 240 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280);
|
||||
g = pow(( 30 * lb + 230 * lg + 10 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280);
|
||||
b = pow((220 * lb + 10 * lg + 50 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280);
|
||||
}
|
||||
|
||||
+2
-8
@@ -20,15 +20,8 @@ auto APU::unload() -> void {
|
||||
}
|
||||
|
||||
auto APU::main() -> void {
|
||||
//handle a bus switching request from the CPU
|
||||
if(state.resLine) {
|
||||
//switching requires a minimum of 9 cycles to allow the 68K to read back unavailable bus state
|
||||
if(state.busreqLine && !state.busreqLatch) step(9);
|
||||
state.busreqLatch = state.busreqLine;
|
||||
}
|
||||
|
||||
//stall the APU until the CPU relinquishes control of the bus
|
||||
if(!state.resLine || !busownerAPU()) {
|
||||
if(!state.resLine || state.busreqLatch) {
|
||||
return step(1);
|
||||
}
|
||||
|
||||
@@ -50,6 +43,7 @@ auto APU::main() -> void {
|
||||
|
||||
auto APU::step(u32 clocks) -> void {
|
||||
Thread::step(clocks);
|
||||
state.busreqLatch = busownerCPU() ? 1 : 0;
|
||||
Thread::synchronize(cpu, vdp, vdp.psg, opn2);
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -21,8 +21,12 @@ struct APU : Z80, Z80::Bus, Thread {
|
||||
} debugger;
|
||||
|
||||
auto synchronizing() const -> bool override { return scheduler.synchronizing(); }
|
||||
auto busownerAPU() const -> bool { return (state.resLine & state.busreqLatch) == 0; } //Z80 has bus access
|
||||
auto busownerCPU() const -> bool { return (state.resLine & state.busreqLatch) == 1; } //68K has bus access
|
||||
auto busgrantedCPU() const -> bool { return state.resLine & state.busreqLatch; }
|
||||
auto busownerCPU() const -> bool { return state.resLine & state.busreqLine; }
|
||||
// Note: bus ownership is flagged according to line state, since it could be too slow
|
||||
// to wait for latching with only instruction-level granularity on the Z80 emulation.
|
||||
// At worst, a positive signal would indicate bus grant is imminent, which should be safe.
|
||||
// (fixes Arkagis Revolution - Trial Version [Rev 00])
|
||||
|
||||
//z80.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
|
||||
+1
-1
@@ -89,7 +89,7 @@ auto APU::writeExternal(n24 address, n8 data) -> void {
|
||||
|
||||
auto APU::in(n16 address) -> n8 {
|
||||
//unused on Mega Drive
|
||||
return 0x00;
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto APU::out(n16 address, n8 data) -> void {
|
||||
|
||||
+40
-14
@@ -27,7 +27,7 @@ inline auto Bus::read(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
|
||||
}
|
||||
|
||||
if(address >= 0xa00000 && address <= 0xa0ffff) {
|
||||
if(!apu.busownerCPU()) return data;
|
||||
if(!apu.busgrantedCPU()) return data;
|
||||
if(cpu.active()) cpu.wait(3); //todo: inaccurate approximation of Z80 RAM access delay
|
||||
address.bit(15) = 0; //a080000-a0ffff mirrors a00000-a07fff
|
||||
//word reads load the even input byte into both output bytes
|
||||
@@ -85,7 +85,7 @@ inline auto Bus::write(n1 upper, n1 lower, n24 address, n16 data) -> void {
|
||||
}
|
||||
|
||||
if(address >= 0xa00000 && address <= 0xa0ffff) {
|
||||
if(!apu.busownerCPU()) return;
|
||||
if(!apu.busgrantedCPU()) return;
|
||||
if(cpu.active()) cpu.wait(3); //todo: inaccurate approximation of Z80 RAM access delay
|
||||
address.bit(15) = 0; //a08000-a0ffff mirrors a00000-a07fff
|
||||
//word writes store the upper input byte into the lower output byte
|
||||
@@ -114,22 +114,48 @@ inline auto Bus::write(n1 upper, n1 lower, n24 address, n16 data) -> void {
|
||||
}
|
||||
}
|
||||
|
||||
//0-127
|
||||
inline auto Bus::waitRefreshExternal() -> void {
|
||||
while(cpu.refresh.external >= 126) {
|
||||
if(cpu.active()) cpu.wait(1);
|
||||
if(scheduler.synchronizing()) break;
|
||||
if(apu.active()) apu.step(1);
|
||||
if(vdp.active()) break;
|
||||
if(state.acquired & VDPDMA) return; // refresh is synched with VDP during DMA
|
||||
if(cpu.refresh.external < cpu.refresh.externalLowBound) return;
|
||||
|
||||
if(cpu.refresh.externalEnd == 0)
|
||||
cpu.refresh.externalEnd = min(cpu.refresh.external + cpu.refresh.externalLength, cpu.refresh.externalHighBound);
|
||||
if(cpu.refresh.external < cpu.refresh.externalEnd) {
|
||||
while(cpu.refresh.external < cpu.refresh.externalEnd) {
|
||||
if(cpu.active()) cpu.wait(1);
|
||||
if(apu.active()) apu.step(1);
|
||||
if(scheduler.synchronizing()) return;
|
||||
if(vdp.active()) return;
|
||||
}
|
||||
cpu.refresh.external -= cpu.refresh.externalEnd;
|
||||
cpu.refresh.externalEnd = 0;
|
||||
}
|
||||
|
||||
while(cpu.refresh.external >= cpu.refresh.externalHighBound) {
|
||||
cpu.refresh.external -= cpu.refresh.externalHighBound;
|
||||
cpu.refresh.externalEnd = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//0-132
|
||||
inline auto Bus::waitRefreshRAM() -> void {
|
||||
while(cpu.refresh.ram >= 130) {
|
||||
if(cpu.active()) cpu.wait(1);
|
||||
if(scheduler.synchronizing()) break;
|
||||
if(apu.active()) apu.step(1);
|
||||
if(vdp.active()) break;
|
||||
if(state.acquired & VDPDMA) return; // refresh is synched with VDP during DMA
|
||||
if(cpu.refresh.ram < cpu.refresh.ramLowBound) return;
|
||||
|
||||
if(cpu.refresh.ramEnd == 0)
|
||||
cpu.refresh.ramEnd = min(cpu.refresh.ram + cpu.refresh.ramLength, cpu.refresh.ramHighBound);
|
||||
if(cpu.refresh.ram < cpu.refresh.ramEnd) {
|
||||
while(cpu.refresh.ram < cpu.refresh.ramEnd) {
|
||||
if(cpu.active()) cpu.wait(1);
|
||||
if(apu.active()) apu.step(1);
|
||||
if(scheduler.synchronizing()) return;
|
||||
if(vdp.active()) return;
|
||||
}
|
||||
cpu.refresh.ram -= cpu.refresh.ramEnd;
|
||||
cpu.refresh.ramEnd = 0;
|
||||
}
|
||||
|
||||
while(cpu.refresh.ram >= cpu.refresh.ramHighBound) {
|
||||
cpu.refresh.ram -= cpu.refresh.ramHighBound;
|
||||
cpu.refresh.ramEnd = 0;
|
||||
}
|
||||
}
|
||||
@@ -4,15 +4,16 @@ struct Banked : Interface {
|
||||
Memory::Writable<n16> wram;
|
||||
Memory::Writable<n8 > uram;
|
||||
Memory::Writable<n8 > lram;
|
||||
u32 sramAddr, sramSize;
|
||||
M24C m24c;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(rom, "program.rom");
|
||||
if(auto fp = pak->read("save.ram")) {
|
||||
Interface::load(wram, uram, lram, "save.ram");
|
||||
Interface::load(sramAddr, sramSize, wram, uram, lram, "save.ram");
|
||||
}
|
||||
if(auto fp = pak->read("save.eeprom")) {
|
||||
Interface::load(m24c, "save.eeprom");
|
||||
Interface::load(sramAddr, sramSize, m24c, "save.eeprom");
|
||||
rsda = fp->attribute("rsda").natural();
|
||||
wsda = fp->attribute("wsda").natural();
|
||||
wscl = fp->attribute("wscl").natural();
|
||||
@@ -29,7 +30,7 @@ struct Banked : Interface {
|
||||
}
|
||||
|
||||
auto read(n1 upper, n1 lower, n22 address, n16 data) -> n16 override {
|
||||
if(address >= 0x200000) {
|
||||
if(address >= sramAddr && address < sramAddr+sramSize) {
|
||||
if(wram && ramEnable) {
|
||||
return wram[address >> 1];
|
||||
}
|
||||
@@ -62,7 +63,7 @@ struct Banked : Interface {
|
||||
//emulating ramWritable will break commercial software:
|
||||
//it does not appear that many (any?) games actually connect $a130f1.d1 to /WE;
|
||||
//hence RAM ends up always being writable, and many games fail to set d1=1
|
||||
if(address >= 0x200000) {
|
||||
if(address >= sramAddr && address < sramAddr+sramSize) {
|
||||
if(wram && ramEnable) {
|
||||
if(upper) wram[address >> 1].byte(1) = data.byte(1);
|
||||
if(lower) wram[address >> 1].byte(0) = data.byte(0);
|
||||
@@ -80,7 +81,7 @@ struct Banked : Interface {
|
||||
}
|
||||
|
||||
if(m24c) {
|
||||
if(rom.size() * 2 > 0x200000 && upper && lower) {
|
||||
if(rom.size() * 2 > sramAddr && upper && lower) {
|
||||
eepromEnable = !data.bit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -26,11 +26,13 @@ auto Interface::load(Memory::Readable<n16>& rom, string name) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto Interface::load(Memory::Writable<n16>& wram, Memory::Writable<n8>& uram, Memory::Writable<n8>& lram, string name) -> bool {
|
||||
auto Interface::load(u32& addr, u32& size, Memory::Writable<n16>& wram, Memory::Writable<n8>& uram, Memory::Writable<n8>& lram, string name) -> bool {
|
||||
wram.reset();
|
||||
uram.reset();
|
||||
lram.reset();
|
||||
if(auto fp = pak->read(name)) {
|
||||
addr = fp->attribute("address").natural();
|
||||
size = fp->size() << 1;
|
||||
auto mode = fp->attribute("mode");
|
||||
if(mode == "word") {
|
||||
wram.allocate(fp->size() >> 1);
|
||||
@@ -49,10 +51,12 @@ auto Interface::load(Memory::Writable<n16>& wram, Memory::Writable<n8>& uram, Me
|
||||
return false;
|
||||
}
|
||||
|
||||
auto Interface::load(M24C& m24c, string name) -> bool {
|
||||
auto Interface::load(u32& addr, u32& size, M24C& m24c, string name) -> bool {
|
||||
m24c.reset();
|
||||
if(auto fp = pak->read(name)) {
|
||||
auto mode = fp->attribute("mode");
|
||||
addr = fp->attribute("address").natural();
|
||||
size = 1;
|
||||
m24c.reset();
|
||||
if(mode == "X24C01" ) m24c.load(M24C::Type::X24C01 );
|
||||
if(mode == "M24C01" ) m24c.load(M24C::Type::M24C01 );
|
||||
|
||||
@@ -21,8 +21,8 @@ struct Interface {
|
||||
virtual auto serialize(serializer&) -> void {}
|
||||
|
||||
auto load(Memory::Readable<n16>& rom, string name) -> bool;
|
||||
auto load(Memory::Writable<n16>& wram, Memory::Writable<n8>& uram, Memory::Writable<n8>& lram, string name) -> bool;
|
||||
auto load(M24C& m24c, string name) -> bool;
|
||||
auto load(u32& addr, u32 &size, Memory::Writable<n16>& wram, Memory::Writable<n8>& uram, Memory::Writable<n8>& lram, string name) -> bool;
|
||||
auto load(u32& addr, u32 &size, M24C& m24c, string name) -> bool;
|
||||
|
||||
auto save(Memory::Writable<n16>& wram, Memory::Writable<n8>& uram, Memory::Writable<n8>& lram, string name) -> bool;
|
||||
auto save(M24C& m24c, string name) -> bool;
|
||||
|
||||
@@ -4,15 +4,16 @@ struct Linear : Interface {
|
||||
Memory::Writable<n16> wram;
|
||||
Memory::Writable<n8 > uram;
|
||||
Memory::Writable<n8 > lram;
|
||||
u32 sramAddr, sramSize;
|
||||
M24C m24c;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(rom, "program.rom");
|
||||
if(auto fp = pak->read("save.ram")) {
|
||||
Interface::load(wram, uram, lram, "save.ram");
|
||||
Interface::load(sramAddr, sramSize, wram, uram, lram, "save.ram");
|
||||
}
|
||||
if(auto fp = pak->read("save.eeprom")) {
|
||||
Interface::load(m24c, "save.eeprom");
|
||||
Interface::load(sramAddr, sramSize, m24c, "save.eeprom");
|
||||
rsda = fp->attribute("rsda").natural();
|
||||
wsda = fp->attribute("wsda").natural();
|
||||
wscl = fp->attribute("wscl").natural();
|
||||
@@ -29,7 +30,7 @@ struct Linear : Interface {
|
||||
}
|
||||
|
||||
auto read(n1 upper, n1 lower, n22 address, n16 data) -> n16 override {
|
||||
if(address >= 0x200000) {
|
||||
if(address >= sramAddr && address < sramAddr+sramSize) {
|
||||
if(wram && ramEnable) {
|
||||
return wram[address >> 1];
|
||||
}
|
||||
@@ -60,7 +61,7 @@ struct Linear : Interface {
|
||||
//emulating ramWritable will break commercial software:
|
||||
//it does not appear that many (any?) games actually connect $a130f1.d1 to /WE;
|
||||
//hence RAM ends up always being writable, and many games fail to set d1=1
|
||||
if(address >= 0x200000) {
|
||||
if(address >= sramAddr && address < sramAddr+sramSize) {
|
||||
if(wram && ramEnable) {
|
||||
if(upper) wram[address >> 1].byte(1) = data.byte(1);
|
||||
if(lower) wram[address >> 1].byte(0) = data.byte(0);
|
||||
@@ -78,7 +79,7 @@ struct Linear : Interface {
|
||||
}
|
||||
|
||||
if(m24c) {
|
||||
if(rom.size() * 2 > 0x200000 && upper && lower) {
|
||||
if(rom.size() * 2 > sramAddr && upper && lower) {
|
||||
eepromEnable = !data.bit(0);
|
||||
return;
|
||||
}
|
||||
@@ -98,17 +99,16 @@ struct Linear : Interface {
|
||||
}
|
||||
|
||||
auto writeIO(n1 upper, n1 lower, n24 address, n16 data) -> void override {
|
||||
if(!lower) return; //todo: unconfirmed
|
||||
if(address == 0xa130f0) {
|
||||
if(rom.size() * 2 > sramAddr && address == 0xa130f0) {
|
||||
ramEnable = data.bit(0);
|
||||
ramWritable = data.bit(1);
|
||||
}
|
||||
}
|
||||
|
||||
auto power(bool reset) -> void override {
|
||||
ramEnable = 1;
|
||||
ramEnable = rom.size() * 2 <= sramAddr;
|
||||
ramWritable = 1;
|
||||
eepromEnable = rom.size() * 2 <= 0x200000;
|
||||
eepromEnable = rom.size() * 2 <= sramAddr;
|
||||
m24c.power();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ struct Mega32X : Interface {
|
||||
Memory::Writable<n16> wram;
|
||||
Memory::Writable<n8 > uram;
|
||||
Memory::Writable<n8 > lram;
|
||||
u32 sramAddr, sramSize;
|
||||
M24C m24c;
|
||||
|
||||
auto load() -> void override {
|
||||
@@ -14,11 +15,11 @@ struct Mega32X : Interface {
|
||||
}
|
||||
|
||||
if(auto fp = pak->read("save.ram")) {
|
||||
Interface::load(wram, uram, lram, "save.ram");
|
||||
Interface::load(sramAddr, sramSize, wram, uram, lram, "save.ram");
|
||||
}
|
||||
|
||||
if(auto fp = pak->read("save.eeprom")) {
|
||||
Interface::load(m24c, "save.eeprom");
|
||||
Interface::load(sramAddr, sramSize, m24c, "save.eeprom");
|
||||
rsda = fp->attribute("rsda").natural();
|
||||
wsda = fp->attribute("wsda").natural();
|
||||
wscl = fp->attribute("wscl").natural();
|
||||
@@ -36,7 +37,7 @@ struct Mega32X : Interface {
|
||||
}
|
||||
|
||||
auto read(n1 upper, n1 lower, n22 address, n16 data) -> n16 override {
|
||||
if(address >= 0x200000) {
|
||||
if(address >= sramAddr && address < sramAddr+sramSize) {
|
||||
if(wram) {
|
||||
return wram[address >> 1];
|
||||
}
|
||||
@@ -60,7 +61,7 @@ struct Mega32X : Interface {
|
||||
}
|
||||
|
||||
auto write(n1 upper, n1 lower, n22 address, n16 data) -> void override {
|
||||
if(address >= 0x200000) {
|
||||
if(address >= sramAddr && address < sramAddr+sramSize) {
|
||||
if(wram) {
|
||||
if(upper) wram[address >> 1].byte(1) = data.byte(1);
|
||||
if(lower) wram[address >> 1].byte(0) = data.byte(0);
|
||||
|
||||
+3
-2
@@ -1,6 +1,7 @@
|
||||
auto CPU::read(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
|
||||
auto CPU::read(n1 upper, n1 lower, n24 address, n16) -> n16 {
|
||||
while(bus.acquired()) wait(1);
|
||||
return bus.read(upper, lower, address, data);
|
||||
//using m68k prefetch for open-bus data
|
||||
return bus.read(upper, lower, address, r.irc);
|
||||
}
|
||||
|
||||
auto CPU::write(n1 upper, n1 lower, n24 address, n16 data) -> void {
|
||||
|
||||
@@ -61,7 +61,6 @@ auto CPU::main() -> void {
|
||||
|
||||
auto CPU::step(u32 clocks) -> void {
|
||||
refresh.ram += clocks;
|
||||
while(refresh.ram >= 133) refresh.ram -= 133;
|
||||
refresh.external += clocks;
|
||||
Thread::step(clocks);
|
||||
cyclesUntilSync -= clocks;
|
||||
|
||||
+17
-3
@@ -5,6 +5,10 @@ struct CPU : M68000, Thread {
|
||||
Memory::Readable<n16> tmss;
|
||||
Memory::Writable<n16> ram;
|
||||
|
||||
// Bus locking is unavailable for the main cpu of MegaDrive models 1 & 2,
|
||||
// preventing the TAS instruction from working correctly in most cases.
|
||||
auto lockable() -> bool override { return false; }
|
||||
|
||||
struct Debugger {
|
||||
//debugger.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
@@ -43,7 +47,7 @@ struct CPU : M68000, Thread {
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
//bus.cpp
|
||||
auto read(n1 upper, n1 lower, n24 address, n16 data = 0) -> n16 override;
|
||||
auto read(n1 upper, n1 lower, n24 address, n16 _ = 0) -> n16 override;
|
||||
auto write(n1 upper, n1 lower, n24 address, n16 data) -> void override;
|
||||
|
||||
//io.cpp
|
||||
@@ -62,8 +66,18 @@ struct CPU : M68000, Thread {
|
||||
} io;
|
||||
|
||||
struct Refresh {
|
||||
n8 ram;
|
||||
n7 external;
|
||||
int ram;
|
||||
int ramEnd;
|
||||
int external;
|
||||
int externalEnd;
|
||||
|
||||
static constexpr int ramLowBound = 113;
|
||||
static constexpr int ramHighBound = 132;
|
||||
static constexpr int ramLength = 3;
|
||||
static constexpr int externalLowBound = 126;
|
||||
static constexpr int externalHighBound = 132;
|
||||
static constexpr int externalLength = 2;
|
||||
|
||||
} refresh;
|
||||
|
||||
struct State {
|
||||
|
||||
+4
-4
@@ -2,9 +2,9 @@
|
||||
|
||||
auto CPU::readIO(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
|
||||
if(address >= 0xa10000 && address <= 0xa100ff) {
|
||||
if(!lower) return data; //even byte writes ignored
|
||||
address.bit(5,7) = 0; //a10020-a100ff mirrors a10000-a1001f
|
||||
if(!lower) return data.byte(0) << 8;
|
||||
|
||||
address.bit(5,7) = 0; //a10020-a100ff mirrors a10000-a1001f
|
||||
switch(address) {
|
||||
case 0xa10000:
|
||||
data.bit(0) = io.version; //0 = Model 1; 1 = Model 2+
|
||||
@@ -38,11 +38,11 @@ auto CPU::readIO(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
|
||||
break;
|
||||
}
|
||||
|
||||
return data;
|
||||
return data.byte(1)=data.byte(0), data;
|
||||
}
|
||||
|
||||
if(address >= 0xa11100 && address <= 0xa111ff) {
|
||||
data.bit(8) = apu.busownerAPU();
|
||||
data.bit(8) = !apu.busgrantedCPU();
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ auto CPU::serialize(serializer& s) -> void {
|
||||
s(io.romEnable);
|
||||
s(io.vdpEnable);
|
||||
s(refresh.ram);
|
||||
s(refresh.ramEnd);
|
||||
s(refresh.external);
|
||||
s(refresh.externalEnd);
|
||||
s(state.interruptPending);
|
||||
}
|
||||
@@ -17,7 +17,7 @@ auto M32X::SH7604::Debugger::load(Node::Object parent) -> void {
|
||||
}
|
||||
|
||||
auto M32X::SH7604::Debugger::instruction() -> void {
|
||||
if(tracer.instruction->enabled() && tracer.instruction->address(self->PC - 4)) {
|
||||
if(tracer.instruction->enabled() && tracer.instruction->address(self->regs.PC - 4)) {
|
||||
tracer.instruction->notify(self->disassembleInstruction(), self->disassembleContext());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,16 @@ auto M32X::PWM::step(u32 clocks) -> void {
|
||||
|
||||
auto M32X::PWM::power(bool reset) -> void {
|
||||
Thread::create(23'020'200, {&M32X::PWM::main, this});
|
||||
lmode = 0;
|
||||
rmode = 0;
|
||||
mono = 0;
|
||||
dreqIRQ = 0;
|
||||
timer = 0;
|
||||
cycle = 0;
|
||||
periods = 0;
|
||||
counter = 0;
|
||||
lsample = 0;
|
||||
rsample = 0;
|
||||
lfifo.flush();
|
||||
rfifo.flush();
|
||||
}
|
||||
+10
-10
@@ -14,27 +14,27 @@ auto M32X::SH7604::unload() -> void {
|
||||
}
|
||||
|
||||
auto M32X::SH7604::main() -> void {
|
||||
if(!SH2::inDelaySlot() && !SH2::ID) {
|
||||
if(!SH2::inDelaySlot() && !regs.ID) {
|
||||
if(irq.vres.active && irq.vres.enable) {
|
||||
debugger.interrupt("VRES");
|
||||
irq.vres.active = 0;
|
||||
return ET = 1, interrupt(14, 71);
|
||||
return regs.ET = 1, interrupt(14, 71);
|
||||
}
|
||||
if(irq.vint.active && irq.vint.enable && SR.I < 12) {
|
||||
if(irq.vint.active && irq.vint.enable && regs.SR.I < 12) {
|
||||
debugger.interrupt("VINT");
|
||||
return ET = 1, interrupt(12, 70);
|
||||
return regs.ET = 1, interrupt(12, 70);
|
||||
}
|
||||
if(irq.hint.active && irq.hint.enable && SR.I < 10) {
|
||||
if(irq.hint.active && irq.hint.enable && regs.SR.I < 10) {
|
||||
debugger.interrupt("HINT");
|
||||
return ET = 1, interrupt(10, 69);
|
||||
return regs.ET = 1, interrupt(10, 69);
|
||||
}
|
||||
if(irq.cmd.active && irq.cmd.enable && SR.I < 8) {
|
||||
if(irq.cmd.active && irq.cmd.enable && regs.SR.I < 8) {
|
||||
debugger.interrupt("CMD");
|
||||
return ET = 1, interrupt(8, 68);
|
||||
return regs.ET = 1, interrupt(8, 68);
|
||||
}
|
||||
if(irq.pwm.active && irq.pwm.enable && SR.I < 6) {
|
||||
if(irq.pwm.active && irq.pwm.enable && regs.SR.I < 6) {
|
||||
debugger.interrupt("PWM");
|
||||
return ET = 1, interrupt(6, 67);
|
||||
return regs.ET = 1, interrupt(6, 67);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,17 @@ auto M32X::VDP::unload() -> void {
|
||||
auto M32X::VDP::power(bool reset) -> void {
|
||||
dram.fill(0);
|
||||
cram.fill(0);
|
||||
mode = 0;
|
||||
lines = 0;
|
||||
priority = 0;
|
||||
dotshift = 0;
|
||||
autofillLength = 0;
|
||||
autofillAddress = 0;
|
||||
autofillData = 0;
|
||||
framebufferAccess = 0;
|
||||
framebufferActive = 0;
|
||||
framebufferSelect = 0;
|
||||
hblank = 0;
|
||||
vblank = 1;
|
||||
selectFramebuffer(framebufferSelect);
|
||||
}
|
||||
|
||||
+8
-8
@@ -54,17 +54,15 @@ auto MCD::CDD::clock() -> void {
|
||||
}
|
||||
|
||||
auto MCD::CDD::advance() -> void {
|
||||
//is the next sector still within the current track?
|
||||
if(auto track = session.inTrack(io.sector + 1)) {
|
||||
if(track() == io.track) {
|
||||
io.sector++;
|
||||
io.sample = 0;
|
||||
return;
|
||||
}
|
||||
io.track = track();
|
||||
io.sector++;
|
||||
io.sample = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
//if not, then the track has been completed
|
||||
io.status = Status::Paused;
|
||||
io.status = Status::LeadOut;
|
||||
io.track = 0xaa;
|
||||
}
|
||||
|
||||
auto MCD::CDD::sample() -> void {
|
||||
@@ -244,6 +242,8 @@ auto MCD::CDD::process() -> void {
|
||||
io.status = Status::Playing;
|
||||
} break;
|
||||
|
||||
default:
|
||||
io.status = Status::CommandError;
|
||||
}
|
||||
|
||||
status[0] = io.status;
|
||||
|
||||
+36
-24
@@ -11,6 +11,7 @@ auto MCD::GPU::step(u32 clocks) -> void {
|
||||
if(!--image.vdots) {
|
||||
active = 0;
|
||||
irq.raise();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,11 +27,20 @@ auto MCD::GPU::write(n19 address, n4 data) -> void {
|
||||
}
|
||||
|
||||
auto MCD::GPU::render(n19 address, n9 width) -> void {
|
||||
n8 stampShift = 11 + 4 + stamp.tile.size;
|
||||
n8 mapShift = (4 << stamp.map.size) - stamp.tile.size;
|
||||
n4 stampShift = 4;
|
||||
n4 mapShift = 4 << stamp.map.size;
|
||||
n11 indexMask = ~0;
|
||||
n5 pixelOffsetMask = 0x0f;
|
||||
|
||||
n2 stampMask = !stamp.tile.size ? 1 : 3;
|
||||
n23 mapMask = !stamp.map.size ? 0x07ffff : 0x7fffff;
|
||||
if(stamp.tile.size) {
|
||||
stampShift++;
|
||||
mapShift--;
|
||||
indexMask &= ~3;
|
||||
pixelOffsetMask |= 0x10;
|
||||
}
|
||||
|
||||
n19 imageWidth = image.vcells+1 << 6;
|
||||
n24 mapMask = !stamp.map.size ? 0x07ffff : 0x7fffff;
|
||||
|
||||
n24 x = mcd.wram[vector.address++] << 8; //13.3 -> 13.11
|
||||
n24 y = mcd.wram[vector.address++] << 8; //13.3 -> 13.11
|
||||
@@ -44,30 +54,32 @@ auto MCD::GPU::render(n19 address, n9 width) -> void {
|
||||
y &= mapMask;
|
||||
}
|
||||
|
||||
n4 output;
|
||||
n4 output = 0;
|
||||
if(bool outside = (x | y) & ~mapMask; !outside) {
|
||||
auto xstamp = x >> stampShift;
|
||||
auto ystamp = y >> stampShift << mapShift;
|
||||
auto xtrunc = x >> 11;
|
||||
auto ytrunc = y >> 11;
|
||||
auto xstamp = xtrunc >> stampShift;
|
||||
auto ystamp = ytrunc >> stampShift;
|
||||
|
||||
auto data = mcd.wram[stamp.map.address + xstamp + ystamp];
|
||||
auto index = n10(data >> 0);
|
||||
auto lroll = n1 (data >> 13); //0 = 0 degrees; 1 = 90 degrees
|
||||
auto hroll = n1 (data >> 14); //0 = 0 degrees; 1 = 180 degrees
|
||||
auto hflip = n1 (data >> 15);
|
||||
auto mapEntry = mcd.wram[stamp.map.address + (ystamp << mapShift) + xstamp];
|
||||
n11 index = mapEntry & indexMask;
|
||||
n1 lroll = mapEntry >> 13; //0 = 0 degrees; 1 = 90 degrees
|
||||
n1 hroll = mapEntry >> 14; //0 = 0 degrees; 1 = 180 degrees
|
||||
n1 hflip = mapEntry >> 15;
|
||||
|
||||
if(index) { //stamp index 0 is not rendered
|
||||
auto xpixel = n6(x >> 11);
|
||||
auto ypixel = n6(y >> 11);
|
||||
if(index) {
|
||||
if(hflip) { xtrunc = ~xtrunc; }
|
||||
if(hroll) { xtrunc = ~xtrunc; ytrunc = ~ytrunc; }
|
||||
if(lroll) { auto t = xtrunc; xtrunc = ~ytrunc; ytrunc = t; }
|
||||
|
||||
if(hflip) { xpixel = ~xpixel; }
|
||||
if(hroll) { xpixel = ~xpixel; ypixel = ~ypixel; }
|
||||
if(lroll) { auto tpixel = xpixel; xpixel = ~ypixel; ypixel = tpixel; }
|
||||
n5 xpixel = xtrunc & pixelOffsetMask;
|
||||
n5 ypixel = ytrunc & pixelOffsetMask;
|
||||
|
||||
n6 pixel = n3(xpixel) + n3(ypixel) * 8;
|
||||
xpixel = xpixel >> 3 & stampMask;
|
||||
ypixel = ypixel >> 3 & stampMask;
|
||||
n4 cell = ypixel + xpixel * (1 + stampMask);
|
||||
output = read(index << 8 | cell << 6 | pixel);
|
||||
output = read(
|
||||
index << 8 |
|
||||
(xpixel & ~7) << stampShift |
|
||||
ypixel << 3 |
|
||||
xpixel & 7);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +91,7 @@ auto MCD::GPU::render(n19 address, n9 width) -> void {
|
||||
case 3: output = input; break;
|
||||
}
|
||||
write(address, output);
|
||||
if(!(++address & 7)) address += (image.vcells + 1 << 6) - 8;
|
||||
if(!(++address & 7)) address += imageWidth - 8;
|
||||
|
||||
x += xstep;
|
||||
y += ystep;
|
||||
|
||||
@@ -252,7 +252,7 @@ auto MCD::writeIO(n1 upper, n1 lower, n24 address, n16 data) -> void {
|
||||
|
||||
if(address == 0xff8030) {
|
||||
if(lower) {
|
||||
timer.frequency = data.byte(0);
|
||||
timer.counter = timer.frequency = data.byte(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ auto System::load(Node::System& root, string name) -> bool {
|
||||
information.name = "Mega Drive";
|
||||
information.mega32X = 1;
|
||||
information.megaCD = 0;
|
||||
cpu.minCyclesBetweenSyncs = 15; // sync approx every 25-26 pixels
|
||||
cpu.minCyclesBetweenSyncs = 14; // sync approx every 24-25 pixels
|
||||
}
|
||||
if(name.match("[Sega] Mega CD (*)")) {
|
||||
information.name = "Mega Drive";
|
||||
@@ -159,7 +159,7 @@ auto System::power(bool reset) -> void {
|
||||
bus.power(reset);
|
||||
cpu.power(reset);
|
||||
apu.power(reset); //apu.power() calls opn2.power()
|
||||
vdp.power(reset); //vdp.power() calls vdp.psg.power()
|
||||
if(!reset) vdp.power(reset); //vdp.power() calls vdp.psg.power()
|
||||
controllerPort1.power(reset);
|
||||
controllerPort2.power(reset);
|
||||
extensionPort.power(reset);
|
||||
|
||||
+7
-2
@@ -1,6 +1,11 @@
|
||||
auto VDP::DAC::pixel(u32 x) -> void {
|
||||
if(!pixels) return;
|
||||
|
||||
if(!vdp.displayEnable() || vdp.vcounter() == 0x1ff) {
|
||||
output(0b101 << 9 | vdp.cram.color(vdp.io.backgroundColor));
|
||||
return;
|
||||
}
|
||||
|
||||
Pixel g = {vdp.io.backgroundColor, 0, 1};
|
||||
Pixel a = vdp.layerA.pixel(x);
|
||||
Pixel b = vdp.layerB.pixel(x);
|
||||
@@ -15,8 +20,8 @@ auto VDP::DAC::pixel(u32 x) -> void {
|
||||
s = {};
|
||||
}
|
||||
|
||||
auto& bg = a.above() || a.color && !b.above() ? a : b.color ? b : g;
|
||||
auto& fg = s.above() || s.color && !b.above() && !a.above() ? s : bg;
|
||||
auto& bg = a.above() || a.solid() && !b.above() ? a : b.solid() ? b : g;
|
||||
auto& fg = s.above() || s.solid() && !b.above() && !a.above() ? s : bg;
|
||||
|
||||
auto pixel = fg;
|
||||
auto mode = 1; //0 = shadow, 1 = normal, 2 = highlight
|
||||
|
||||
@@ -23,6 +23,7 @@ auto VDP::DMA::run() -> bool {
|
||||
}
|
||||
|
||||
auto VDP::DMA::load() -> void {
|
||||
if(delay > 0) { delay--; return; }
|
||||
auto address = mode.bit(0) << 23 | source << 1;
|
||||
auto data = bus.read(1, 1, address);
|
||||
vdp.writeDataPort(data);
|
||||
@@ -76,6 +77,7 @@ auto VDP::DMA::power(bool reset) -> void {
|
||||
mode = 0;
|
||||
source = 0;
|
||||
length = 0;
|
||||
data = 0;
|
||||
wait = 1;
|
||||
read = 0;
|
||||
enable = 0;
|
||||
|
||||
+26
-21
@@ -151,22 +151,17 @@ auto VDP::readControlPort() -> n16 {
|
||||
command.latch = 0;
|
||||
|
||||
n16 result;
|
||||
result.bit( 0) = Region::PAL();
|
||||
result.bit( 1) = command.pending;
|
||||
result.bit( 2) = hblank();
|
||||
result.bit( 3) = vblank() || !io.displayEnable;
|
||||
result.bit( 4) = io.interlaceMode.bit(0) && field();
|
||||
result.bit( 5) = sprite.collision;
|
||||
result.bit( 6) = sprite.overflow;
|
||||
result.bit( 7) = irq.vblank.pending;
|
||||
result.bit( 8) = fifo.full();
|
||||
result.bit( 9) = fifo.empty();
|
||||
result.bit(10) = 1; //constants (bits 10-15)
|
||||
result.bit(11) = 0; //todo: should these bits be open bus instead?
|
||||
result.bit(12) = 1;
|
||||
result.bit(13) = 1;
|
||||
result.bit(14) = 0;
|
||||
result.bit(15) = 0;
|
||||
result.bit( 0) = Region::PAL();
|
||||
result.bit( 1) = command.pending;
|
||||
result.bit( 2) = hblank();
|
||||
result.bit( 3) = vblank() || !io.displayEnable;
|
||||
result.bit( 4) = io.interlaceMode.bit(0) && field();
|
||||
result.bit( 5) = sprite.collision;
|
||||
result.bit( 6) = sprite.overflow;
|
||||
result.bit( 7) = irq.vblank.pending;
|
||||
result.bit( 8) = fifo.full();
|
||||
result.bit( 9) = fifo.empty();
|
||||
result.bit(10,15) = cpu.irc().bit(10,15); // open bus
|
||||
|
||||
sprite.collision = 0;
|
||||
sprite.overflow = 0;
|
||||
@@ -175,6 +170,13 @@ auto VDP::readControlPort() -> n16 {
|
||||
|
||||
auto VDP::writeControlPort(n16 data) -> void {
|
||||
//command write (lo)
|
||||
|
||||
// Note: A pending interrupt will be delayed when a register write
|
||||
// is used to enable that interrupt. Further testing is required,
|
||||
// but the current method should be sufficient to handle most cases.
|
||||
// Required for Sesame Street Counting Cafe et al.
|
||||
irq.delay = 2; // 4 pixel delay (4-6 M68k clocks) from this write
|
||||
|
||||
if(command.latch) {
|
||||
command.latch = 0;
|
||||
|
||||
@@ -185,6 +187,7 @@ auto VDP::writeControlPort(n16 data) -> void {
|
||||
|
||||
prefetch.read(command.target, command.address);
|
||||
|
||||
if(command.pending && dma.mode == 1) dma.delay = 4; // based on measurement noted by Mask of Destiny
|
||||
dma.wait = dma.mode == 2;
|
||||
dma.synchronize();
|
||||
return;
|
||||
@@ -203,7 +206,9 @@ auto VDP::writeControlPort(n16 data) -> void {
|
||||
debugger.io(n5(data >> 8), n8(data));
|
||||
|
||||
//register write (d13 is ignored)
|
||||
switch(data.bit(8,12)) {
|
||||
u5 reg = data.bit(8,12);
|
||||
if(!io.videoMode5 && reg > 0xA) return;
|
||||
switch(reg) {
|
||||
|
||||
//mode register 1
|
||||
case 0x00: {
|
||||
@@ -214,7 +219,6 @@ auto VDP::writeControlPort(n16 data) -> void {
|
||||
irq.hblank.enable = data.bit(4);
|
||||
io.leftColumnBlank = data.bit(5);
|
||||
|
||||
irq.poll();
|
||||
if(!io.videoMode4) debug(unimplemented, "[VDP] M4=0");
|
||||
return;
|
||||
}
|
||||
@@ -222,13 +226,15 @@ auto VDP::writeControlPort(n16 data) -> void {
|
||||
//mode register 2
|
||||
case 0x01: {
|
||||
io.videoMode5 = data.bit(2);
|
||||
io.overscan = data.bit(3);
|
||||
if(io.overscan ^ data.bit(3)) {
|
||||
io.overscan = data.bit(3);
|
||||
vblankcheck();
|
||||
}
|
||||
dma.enable = data.bit(4);
|
||||
irq.vblank.enable = data.bit(5);
|
||||
io.displayEnable = data.bit(6);
|
||||
vram.mode = data.bit(7);
|
||||
|
||||
irq.poll();
|
||||
if(!io.videoMode5) debug(unimplemented, "[VDP] M5=0");
|
||||
return;
|
||||
}
|
||||
@@ -281,7 +287,6 @@ auto VDP::writeControlPort(n16 data) -> void {
|
||||
layers.vscrollMode = data.bit(2);
|
||||
irq.external.enable = data.bit(3);
|
||||
|
||||
irq.poll();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+11
-7
@@ -1,32 +1,35 @@
|
||||
auto VDP::IRQ::poll() -> void {
|
||||
if(delay) { delay--; return; }
|
||||
|
||||
if(external.enable && external.pending) {
|
||||
vdp.debugger.interrupt(CPU::Interrupt::External);
|
||||
cpu.raise(CPU::Interrupt::External);
|
||||
} else {
|
||||
external.pending = 0;
|
||||
cpu.lower(CPU::Interrupt::External);
|
||||
}
|
||||
|
||||
if(hblank.enable && hblank.pending) {
|
||||
vdp.debugger.interrupt(CPU::Interrupt::HorizontalBlank);
|
||||
cpu.raise(CPU::Interrupt::HorizontalBlank);
|
||||
} else {
|
||||
// note: pending H-INT is not cleared when disabled
|
||||
cpu.lower(CPU::Interrupt::HorizontalBlank);
|
||||
}
|
||||
|
||||
if(vblank.enable && vblank.pending) {
|
||||
vdp.debugger.interrupt(CPU::Interrupt::VerticalBlank);
|
||||
cpu.raise(CPU::Interrupt::VerticalBlank);
|
||||
} else {
|
||||
vblank.pending = 0;
|
||||
cpu.lower(CPU::Interrupt::VerticalBlank);
|
||||
}
|
||||
}
|
||||
|
||||
auto VDP::IRQ::acknowledge(u8 level) -> void {
|
||||
if(level == 2) external.pending = 0;
|
||||
if(level == 4) hblank.pending = 0;
|
||||
if(level == 4) {
|
||||
// H-INT ack can errantly cancel V-INT if they coincide.
|
||||
// Required for Fatal Rewind et al.
|
||||
if(vblank.pending && vblank.enable)
|
||||
vblank.pending = 0;
|
||||
else
|
||||
hblank.pending = 0;
|
||||
}
|
||||
if(level == 6) vblank.pending = 0;
|
||||
}
|
||||
|
||||
@@ -34,4 +37,5 @@ auto VDP::IRQ::power(bool reset) -> void {
|
||||
external = {};
|
||||
hblank = {};
|
||||
vblank = {};
|
||||
delay = 0;
|
||||
}
|
||||
Loaded 100 of 560 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user