mirror of
https://github.com/ares-emulator/ares.git
synced 2026-11-07 09:26:02 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd73766522 | ||
|
|
d3b8a29910 | ||
|
|
05232171d4 | ||
|
|
15ef1ac2d5 | ||
|
|
234c99911f | ||
|
|
94e66350a1 | ||
|
|
c4f133ac0b | ||
|
|
4307bf2eff | ||
|
|
17f29369c4 | ||
|
|
dc4f4659d4 | ||
|
|
e9639ebae3 | ||
|
|
bdf0b044e8 | ||
|
|
26c56bb4bc | ||
|
|
9c5ed3f68d | ||
|
|
b6890c9eff | ||
|
|
035fb15c71 | ||
|
|
6fd11e291f | ||
|
|
1c07ebf70c | ||
|
|
569acf7ba1 | ||
|
|
47bab688e6 | ||
|
|
460fec09a0 | ||
|
|
5d093ad9e6 | ||
|
|
3474c5c1bc | ||
|
|
d400eee976 | ||
|
|
8128704f23 | ||
|
|
a774671dc0 | ||
|
|
3f268697d2 | ||
|
|
26382de232 | ||
|
|
9c65426aab | ||
|
|
4355718dda | ||
|
|
022805be81 | ||
|
|
c76aaffa6e | ||
|
|
9408cb43d4 | ||
|
|
4565c732bd | ||
|
|
a776c509bd | ||
|
|
2976a1ab6b |
No files matched your search
@@ -17,11 +17,13 @@ jobs:
|
||||
msystem: mingw64
|
||||
install: make git cmake ccache ninja mingw-w64-x86_64-toolchain
|
||||
target-cmake-preset: windows-ci-mingw-native
|
||||
- name: windows-clang-cl-x64
|
||||
- name: windows-mingw-clang64-x64
|
||||
os: windows-latest
|
||||
windres: rc
|
||||
shell: bash
|
||||
target-cmake-preset: windows-ci-cl-native
|
||||
compiler: clang++
|
||||
shell: 'msys2 {0}'
|
||||
msystem: clang64
|
||||
install: make git cmake ccache ninja mingw-w64-clang-x86_64-clang
|
||||
target-cmake-preset: windows-ci-mingw-native
|
||||
- name: windows-arm64
|
||||
os: windows-11-arm
|
||||
compiler: clang++
|
||||
@@ -62,7 +64,7 @@ jobs:
|
||||
target-cmake-preset: ubuntu-ci-qt
|
||||
- name: freebsd
|
||||
os: ubuntu-24.04
|
||||
shell: bash
|
||||
shell: cpa.sh {0}
|
||||
target-cmake-preset: ubuntu-ci-clang
|
||||
name: ${{ matrix.program }}-${{ matrix.platform.name }}
|
||||
runs-on: ${{ matrix.platform.os }}
|
||||
@@ -106,18 +108,23 @@ jobs:
|
||||
CROSS_COMPILE: ${{ matrix.platform.native-cmake-preset != '' }}
|
||||
NATIVE_PRESET: ${{ matrix.platform.native-cmake-preset }}
|
||||
TARGET_PRESET: ${{ matrix.platform.target-cmake-preset }}
|
||||
- name: "Build: FreeBSD"
|
||||
- name: "Start FreeBSD VM"
|
||||
if: matrix.platform.name == 'freebsd'
|
||||
uses: cross-platform-actions/action@492b0c80085400348c599edace11141a4ee73524
|
||||
env:
|
||||
TARGET_PRESET: ${{ matrix.platform.target-cmake-preset }}
|
||||
uses: cross-platform-actions/action@e0b9770014ba65d5e0815f15b74031c3a635f641
|
||||
with:
|
||||
operating_system: freebsd
|
||||
version: '15.0'
|
||||
environment_variables: TARGET_PRESET
|
||||
run: |
|
||||
sudo pkg install -y cmake pkgconf ccache ninja gtk3 sdl3 librashader
|
||||
.github/scripts/build_ubuntu.sh
|
||||
version: '15.1'
|
||||
- name: "Install FreeBSD Dependencies"
|
||||
if: matrix.platform.name == 'freebsd'
|
||||
run: |
|
||||
sudo pkg upgrade -y
|
||||
sudo pkg install -y cmake-core pkgconf ccache ninja gtk3 sdl3 librashader
|
||||
- name: "Build: FreeBSD"
|
||||
if: matrix.platform.name == 'freebsd'
|
||||
env:
|
||||
TARGET_PRESET: ${{ matrix.platform.target-cmake-preset }}
|
||||
shell: cpa.sh {0} --environment-variables TARGET_PRESET
|
||||
run: .github/scripts/build_ubuntu.sh
|
||||
- name: "Compress Build Artifacts (Windows)"
|
||||
if: runner.os != 'macOS' && runner.os != 'Linux'
|
||||
run: |
|
||||
|
||||
@@ -24,11 +24,9 @@ jobs:
|
||||
platform:
|
||||
- name: windows-x64
|
||||
os: windows-latest
|
||||
compiler: clang++
|
||||
shell: 'msys2 {0}'
|
||||
msystem: clang64
|
||||
install: make git cmake ccache ninja mingw-w64-clang-x86_64-clang
|
||||
target-cmake-preset: windows-ci-mingw-native
|
||||
windres: rc
|
||||
shell: bash
|
||||
target-cmake-preset: windows-ci-cl-native
|
||||
- name: windows-clang-cl-arm64
|
||||
os: windows-latest
|
||||
windres: rc
|
||||
|
||||
@@ -40,13 +40,11 @@ if(ARES_BUILD_OPTIONAL_TARGETS)
|
||||
else()
|
||||
target_disable_subproject(genius "genius (database editor)")
|
||||
endif()
|
||||
add_subdirectory(tools/mame2bml)
|
||||
else()
|
||||
target_disable_subproject(arm7tdmi "arm7tdmi processor test harness")
|
||||
target_disable_subproject(armv6m "armv6m processor test harness")
|
||||
target_disable_subproject(i8080 "i8080 processor test harness")
|
||||
target_disable_subproject(m68000 "m68000 processor test harness")
|
||||
target_disable_subproject(mame2bml "mame2bml (MAME manifest converter)")
|
||||
target_disable_subproject(genius "genius (database editor)")
|
||||
endif()
|
||||
|
||||
|
||||
@@ -16,6 +16,40 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
----------------------------------------------------------------------
|
||||
|
||||
----------------------------------------------------------------------
|
||||
MAME-derived ROM decryption and arcade database
|
||||
|
||||
Portions of the ROM decryption code are derived from MAME source files
|
||||
licensed under the BSD 3-Clause License. The arcade database is generated
|
||||
from MAME machine data.
|
||||
|
||||
Copyright (c) 1997-2026 MAMEdev and contributors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
3. Neither the name of the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
----------------------------------------------------------------------
|
||||
|
||||
----------------------------------------------------------------------
|
||||
Stack-less Just-In-Time compiler
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
auto Video::clock(i16 x, n7 pixel, n1 hblank, n1 vblank) -> void {
|
||||
auto Video::clock(s16 x, n7 pixel, n1 hblank, n1 vblank) -> void {
|
||||
auto outputY = y();
|
||||
if(x < 0 || x >= 160 || outputY <= 0 || outputY >= displayHeight()) return;
|
||||
if(hblank || vblank) pixel = 0;
|
||||
screen->pixels().data()[outputY * 180 + x + 10] = pixel;
|
||||
}
|
||||
|
||||
auto Video::fill(i16 x, n7 pixel) -> void {
|
||||
auto Video::fill(s16 x, n7 pixel) -> void {
|
||||
auto outputY = y();
|
||||
if(x >= 160 || outputY <= 0 || outputY >= displayHeight()) return;
|
||||
auto output = screen->pixels().data() + outputY * 180 + 10;
|
||||
|
||||
@@ -9,7 +9,7 @@ struct Video {
|
||||
auto verticalOffset() const -> s32 { return Region::NTSC() ? 20 : 40; }
|
||||
|
||||
auto line() const -> u32 { return lineCounter; }
|
||||
auto y() const -> i32 { return (i32)lineCounter - verticalOffset(); }
|
||||
auto y() const -> s32 { return (s32)lineCounter - verticalOffset(); }
|
||||
|
||||
//video.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
@@ -18,8 +18,8 @@ struct Video {
|
||||
auto setPhosphor(bool) -> void;
|
||||
|
||||
//receiver.cpp
|
||||
auto clock(i16 x, n7 pixel, n1 hblank, n1 vblank) -> void;
|
||||
auto fill(i16 x, n7 pixel) -> void;
|
||||
auto clock(s16 x, n7 pixel, n1 hblank, n1 vblank) -> void;
|
||||
auto fill(s16 x, n7 pixel) -> void;
|
||||
auto endScanline() -> void;
|
||||
auto vsync(n1 level) -> void;
|
||||
auto frame() -> void;
|
||||
|
||||
@@ -196,6 +196,8 @@ struct ANTIC : Thread {
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
n8 dmaClock;
|
||||
n2 startPending;
|
||||
n2 stopPending;
|
||||
n8 dmaName[8];
|
||||
n6 lineWrite;
|
||||
n6 lineRead;
|
||||
@@ -227,6 +229,7 @@ struct ANTIC : Thread {
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
n1 active;
|
||||
n2 stallDelay;
|
||||
n9 scanline;
|
||||
} wsync{*this};
|
||||
|
||||
|
||||
@@ -75,11 +75,11 @@ auto ANTIC::DMA::arbitrate(Requests& requests) -> void {
|
||||
cpu.rdyLine(0);
|
||||
data = cpu.readDMA(address);
|
||||
self.step(Timing::ColorClocksPerMachineCycle);
|
||||
cpu.rdyLine(!self.wsync.active);
|
||||
cpu.rdyLine(!self.wsync.active || self.wsync.stallDelay);
|
||||
} else if(refreshPending) {
|
||||
cpu.rdyLine(0);
|
||||
self.step(Timing::ColorClocksPerMachineCycle);
|
||||
cpu.rdyLine(!self.wsync.active);
|
||||
cpu.rdyLine(!self.wsync.active || self.wsync.stallDelay);
|
||||
refreshPending = 0;
|
||||
data = cpu.dataBus();
|
||||
} else {
|
||||
|
||||
@@ -35,19 +35,23 @@ auto ANTIC::Interrupt::clock() -> void {
|
||||
|
||||
auto ANTIC::WSYNC::power() -> void {
|
||||
active = 0;
|
||||
stallDelay = 0;
|
||||
scanline = 0;
|
||||
}
|
||||
|
||||
auto ANTIC::WSYNC::clock() -> void {
|
||||
if(active && scanline == self.counter.scanline && self.counter.machineCycle == Timing::WSYNCReleaseCycle) {
|
||||
active = 0;
|
||||
stallDelay = 0;
|
||||
cpu.rdyLine(1);
|
||||
} else if(active && stallDelay && !--stallDelay) {
|
||||
cpu.rdyLine(0);
|
||||
}
|
||||
}
|
||||
|
||||
auto ANTIC::WSYNC::wait() -> void {
|
||||
active = 1;
|
||||
stallDelay = 2;
|
||||
scanline = self.counter.scanline + (self.counter.machineCycle >= 104);
|
||||
if(scanline == Timing::ScanlinesPerFrame) scanline = 0;
|
||||
cpu.rdyLine(0);
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
auto ANTIC::Playfield::power() -> void {
|
||||
dmaClock = 0;
|
||||
startPending = 0;
|
||||
stopPending = 0;
|
||||
for(auto& name : dmaName) name = 0;
|
||||
lineWrite = 0;
|
||||
lineRead = 0;
|
||||
@@ -163,19 +165,33 @@ auto ANTIC::Playfield::dmaStop() const -> u8 {
|
||||
|
||||
auto ANTIC::Playfield::clearDMARing() -> void {
|
||||
dmaClock = 0;
|
||||
startPending = 0;
|
||||
stopPending = 0;
|
||||
for(auto& name : dmaName) name = 0;
|
||||
}
|
||||
|
||||
auto ANTIC::Playfield::clockDMARing() -> void {
|
||||
bool startPulse = startPending.bit(0);
|
||||
bool stopPulse = stopPending.bit(0);
|
||||
startPending >>= 1;
|
||||
stopPending >>= 1;
|
||||
|
||||
auto start = dmaStart();
|
||||
auto stop = dmaStop();
|
||||
auto cycle = (u8)self.counter.machineCycle;
|
||||
if(start != 0xff && cycle + 2 == start) startPending.bit(1) = 1;
|
||||
if(stop != 0xff && cycle + 2 == stop) stopPending.bit(1) = 1;
|
||||
|
||||
if(self.counter.machineCycle == start) {
|
||||
auto width = fetchWidth();
|
||||
auto wideStop = 106 + (self.displayList.instruction.bit(4) ? (u8)self.io.hscroll >> 1 : 0);
|
||||
if(width && cycle + 2 == wideStop) stopPending.bit(1) = 1;
|
||||
|
||||
if(startPulse) {
|
||||
dmaClock.bit(0) = 1;
|
||||
lineWrite = 0;
|
||||
lineRead = 0;
|
||||
}
|
||||
if(self.counter.machineCycle == stop) {
|
||||
if(stopPulse) {
|
||||
dmaClock.bit(0) = 0;
|
||||
dmaName[0] = 0;
|
||||
}
|
||||
@@ -264,13 +280,14 @@ auto ANTIC::Playfield::queueDMA(DMA::Requests& requests) -> void {
|
||||
|
||||
clockDMARing();
|
||||
bool enabled = (self.io.dmactl & 3) != 0;
|
||||
auto physical = enabled && self.counter.machineCycle >= 10 && self.counter.machineCycle <= 105;
|
||||
bool earlyAbnormal = self.counter.machineCycle < 10;
|
||||
auto physical = earlyAbnormal || (enabled && self.counter.machineCycle >= 10 && self.counter.machineCycle <= 105);
|
||||
auto& properties = Modes[mode];
|
||||
|
||||
if(properties.character && dmaClock.bit(0)) {
|
||||
auto readPhase = lineRead;
|
||||
auto name = lineBufferRead(readPhase);
|
||||
if(self.displayList.firstScanline) {
|
||||
if(self.displayList.firstScanline || earlyAbnormal) {
|
||||
auto writePhase = lineWrite;
|
||||
requests.append(
|
||||
self.displayList.memoryScan,
|
||||
@@ -287,7 +304,7 @@ auto ANTIC::Playfield::queueDMA(DMA::Requests& requests) -> void {
|
||||
if(!properties.character && dmaClock.bit(2)) {
|
||||
bool next = dmaClock.bit(1);
|
||||
auto readPhase = bitmapLineBufferPhase(next);
|
||||
if(self.displayList.firstScanline) {
|
||||
if(self.displayList.firstScanline || earlyAbnormal) {
|
||||
auto writePhase = lineWrite;
|
||||
requests.append(
|
||||
self.displayList.memoryScan,
|
||||
|
||||
@@ -51,6 +51,8 @@ auto ANTIC::DMA::serialize(serializer& s) -> void {
|
||||
|
||||
auto ANTIC::Playfield::serialize(serializer& s) -> void {
|
||||
s(dmaClock);
|
||||
s(startPending);
|
||||
s(stopPending);
|
||||
for(auto& name : dmaName) s(name);
|
||||
s(lineWrite);
|
||||
s(lineRead);
|
||||
@@ -70,6 +72,7 @@ auto ANTIC::Playfield::serialize(serializer& s) -> void {
|
||||
|
||||
auto ANTIC::WSYNC::serialize(serializer& s) -> void {
|
||||
s(active);
|
||||
s(stallDelay);
|
||||
s(scanline);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
static const string SerializerVersion = "v148";
|
||||
static const string SerializerVersion = "v149";
|
||||
|
||||
auto System::serialize(bool synchronize) -> serializer {
|
||||
if(synchronize) scheduler.enter(Scheduler::Mode::Synchronize);
|
||||
@@ -29,10 +29,8 @@ auto System::unserialize(serializer& s) -> bool {
|
||||
s(version);
|
||||
s(description);
|
||||
|
||||
char expectedVersion[16] = {};
|
||||
memory::copy(&expectedVersion, (const char*)SerializerVersion, SerializerVersion.size());
|
||||
if(signature != SerializerSignature) return false;
|
||||
if(memory::compare(version, expectedVersion, sizeof(version))) return false;
|
||||
if(string{version} != SerializerVersion) return false;
|
||||
|
||||
if(synchronize) power(/* reset = */ false);
|
||||
serialize(s, synchronize);
|
||||
|
||||
@@ -56,6 +56,7 @@ auto Screen::power() -> void {
|
||||
memory::fill<n1>(_lineOverrideActive.data(), _canvasWidth * _canvasHeight, false);
|
||||
memory::fill<const u32*>(_lineOverride.data(), _canvasWidth * _canvasHeight, nullptr);
|
||||
_phosphorHistoryValid = false;
|
||||
_deinterlaceHistoryValid = false;
|
||||
}
|
||||
|
||||
auto Screen::pixels(bool frame) -> std::span<u32> {
|
||||
@@ -69,6 +70,7 @@ auto Screen::resetPalette() -> void {
|
||||
_palette.reset();
|
||||
refreshPalette();
|
||||
_phosphorHistoryValid = false;
|
||||
_deinterlaceHistoryValid = false;
|
||||
}
|
||||
|
||||
auto Screen::resetSprites() -> void {
|
||||
@@ -92,6 +94,7 @@ auto Screen::refreshRateHint(double refreshRate) -> void {
|
||||
|
||||
auto Screen::setViewport(u32 x, u32 y, u32 width, u32 height) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
if(_viewportX != x || _viewportY != y || _viewportWidth != width || _viewportHeight != height) _deinterlaceHistoryValid = false;
|
||||
_viewportX = x;
|
||||
_viewportY = y;
|
||||
_viewportWidth = width;
|
||||
@@ -105,6 +108,7 @@ auto Screen::setOverscan(bool overscan) -> void {
|
||||
|
||||
auto Screen::setSize(u32 width, u32 height) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
if(_width != width || _height != height) _deinterlaceHistoryValid = false;
|
||||
_width = width;
|
||||
_height = height;
|
||||
_phosphorHistoryValid = false;
|
||||
@@ -183,6 +187,17 @@ auto Screen::setProgressive(bool progressiveDouble) -> void {
|
||||
_interlace = false;
|
||||
_progressive = true;
|
||||
_progressiveDouble = progressiveDouble;
|
||||
_deinterlaceHistoryValid = false;
|
||||
}
|
||||
|
||||
auto Screen::setDeinterlace(string deinterlace) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
if(deinterlace != "Weave" && deinterlace != "Bob"
|
||||
&& deinterlace != "Blend" && deinterlace != "Phosphor") deinterlace = "Weave";
|
||||
_deinterlace = deinterlace;
|
||||
_deinterlaceHistory.reset();
|
||||
_deinterlaceHistoryValid = false;
|
||||
if(_output) memory::fill<u32>(_output.get(), _canvasWidth * _canvasHeight, 0);
|
||||
}
|
||||
|
||||
auto Screen::setInterlace(bool interlaceField) -> void {
|
||||
@@ -246,6 +261,20 @@ auto Screen::refresh() -> void {
|
||||
auto input = _inputB.get();
|
||||
auto output = _output.get();
|
||||
|
||||
auto phosphor = [](u32 current, u32 previous) -> u32 {
|
||||
u32 color = current & 0xff000000;
|
||||
for(u32 shift : {0, 8, 16}) {
|
||||
auto rise = current >> shift & 0xff;
|
||||
auto decay = (previous >> shift & 0xff) >> 1;
|
||||
color |= max(rise, decay) << shift;
|
||||
}
|
||||
return color;
|
||||
};
|
||||
|
||||
if(_interlace && _deinterlace == "Blend" && !_deinterlaceHistory) {
|
||||
_deinterlaceHistory = std::make_unique<u32[]>(width * height);
|
||||
}
|
||||
|
||||
for(u32 y : range(height)) {
|
||||
auto source = input + y * pitch;
|
||||
auto target = output + y * width;
|
||||
@@ -257,7 +286,35 @@ auto Screen::refresh() -> void {
|
||||
*target++ = color;
|
||||
}
|
||||
} else if(_interlace) {
|
||||
if((_interlaceField & 1) == (y & 1)) {
|
||||
if(_deinterlace == "Phosphor") {
|
||||
for(u32 x : range(width)) {
|
||||
auto current = (y & 1) == u32(_interlaceField) ? _palette[*source++] : 0xff000000;
|
||||
*target = phosphor(current, *target);
|
||||
target++;
|
||||
}
|
||||
} else if(_deinterlace != "Weave") {
|
||||
u32 sourceY = y, nextY = y;
|
||||
if((y & 1) != u32(_interlaceField)) {
|
||||
sourceY = y ? y - 1 : min(1u, height - 1);
|
||||
nextY = y + 1 < height ? y + 1 : sourceY;
|
||||
}
|
||||
source = input + sourceY * pitch;
|
||||
auto next = input + nextY * pitch;
|
||||
for(u32 x : range(width)) {
|
||||
auto color = _palette[*source++];
|
||||
if(sourceY != nextY) {
|
||||
auto other = _palette[*next++];
|
||||
color = (color & other) + (((color ^ other) & 0xfefefefe) >> 1);
|
||||
}
|
||||
if(_deinterlace == "Blend") {
|
||||
auto& previous = _deinterlaceHistory[y * width + x];
|
||||
auto current = color;
|
||||
if(_deinterlaceHistoryValid) color = (color & previous) + (((color ^ previous) & 0xfefefefe) >> 1);
|
||||
previous = current;
|
||||
}
|
||||
*target++ = color;
|
||||
}
|
||||
} else if((_interlaceField & 1) == (y & 1)) {
|
||||
for(u32 x : range(width)) {
|
||||
auto color = _palette[*source++];
|
||||
*target++ = color;
|
||||
@@ -277,14 +334,8 @@ auto Screen::refresh() -> void {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto previous = *target;
|
||||
u32 color = current & 0xff000000;
|
||||
for(u32 shift : {0, 8, 16}) {
|
||||
auto rise = current >> shift & 0xff;
|
||||
auto decay = (previous >> shift & 0xff) >> 1;
|
||||
color |= max(rise, decay) << shift;
|
||||
}
|
||||
*target++ = color;
|
||||
*target = phosphor(current, *target);
|
||||
target++;
|
||||
}
|
||||
} else if(_interframeBlending) {
|
||||
n32 mask = 1 << 24 | 1 << 16 | 1 << 8 | 1 << 0;
|
||||
@@ -302,6 +353,7 @@ auto Screen::refresh() -> void {
|
||||
}
|
||||
|
||||
if(_phosphor) _phosphorHistoryValid = true;
|
||||
if(_interlace && _deinterlace == "Blend") _deinterlaceHistoryValid = true;
|
||||
|
||||
if (_colorBleed) {
|
||||
n32 mask = 1 << 24 | 1 << 16 | 1 << 8 | 1 << 0;
|
||||
@@ -349,6 +401,9 @@ auto Screen::refresh() -> void {
|
||||
}
|
||||
}
|
||||
output = _rotate.get();
|
||||
auto x = viewX;
|
||||
viewX = viewY;
|
||||
viewY = width - x - viewWidth;
|
||||
swap(width, height);
|
||||
swap(viewWidth, viewHeight);
|
||||
}
|
||||
@@ -363,6 +418,8 @@ auto Screen::refresh() -> void {
|
||||
}
|
||||
}
|
||||
output = _rotate.get();
|
||||
viewX = width - viewX - viewWidth;
|
||||
viewY = height - viewY - viewHeight;
|
||||
}
|
||||
|
||||
if(_rotation == 270) {
|
||||
@@ -375,6 +432,9 @@ auto Screen::refresh() -> void {
|
||||
}
|
||||
}
|
||||
output = _rotate.get();
|
||||
auto y = viewY;
|
||||
viewY = viewX;
|
||||
viewX = height - y - viewHeight;
|
||||
swap(width, height);
|
||||
swap(viewWidth, viewHeight);
|
||||
}
|
||||
@@ -400,6 +460,7 @@ auto Screen::clearOverrideLineDraw(u32 y) -> void {
|
||||
auto Screen::refreshPalette() -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
if(_palette) return;
|
||||
_deinterlaceHistoryValid = false;
|
||||
|
||||
//generate the color lookup palettes to convert native colors to ARGB8888
|
||||
_palette = std::make_unique<u32[]>(_colors);
|
||||
@@ -454,6 +515,7 @@ auto Screen::serialize(string& output, string depth) -> void {
|
||||
output.append(depth, " fillColor: ", _fillColor, "\n");
|
||||
output.append(depth, " colorBleed: ", _colorBleed, "\n");
|
||||
output.append(depth, " interlace: ", _interlace, "\n");
|
||||
output.append(depth, " deinterlace: ", _deinterlace, "\n");
|
||||
output.append(depth, " phosphor: ", _phosphor, "\n");
|
||||
output.append(depth, " interframeBlending: ", _interframeBlending, "\n");
|
||||
output.append(depth, " rotation: ", _rotation, "\n");
|
||||
@@ -474,9 +536,11 @@ auto Screen::unserialize(Markup::Node node) -> void {
|
||||
_fillColor = node["fillColor"].natural();
|
||||
_colorBleed = node["colorBleed"].boolean();
|
||||
_interlace = node["interlace"].natural();
|
||||
_deinterlace = node["deinterlace"].string();
|
||||
_phosphor = node["phosphor"].boolean();
|
||||
_interframeBlending = node["interframeBlending"].boolean();
|
||||
_rotation = node["rotation"].natural();
|
||||
resetPalette();
|
||||
resetSprites();
|
||||
setDeinterlace(_deinterlace);
|
||||
}
|
||||
@@ -29,6 +29,7 @@ struct Screen : Video {
|
||||
auto phosphor() const -> bool { return _phosphor; }
|
||||
auto interframeBlending() const -> bool { return _interframeBlending; }
|
||||
auto rotation() const -> u32 { return _rotation; }
|
||||
auto deinterlace() const -> string { return _deinterlace; }
|
||||
|
||||
auto resetPalette() -> void;
|
||||
auto resetSprites() -> void;
|
||||
@@ -56,6 +57,7 @@ struct Screen : Video {
|
||||
|
||||
auto setProgressive(bool progressiveDouble = false) -> void;
|
||||
auto setInterlace(bool interlaceField) -> void;
|
||||
auto setDeinterlace(string deinterlace) -> void;
|
||||
|
||||
auto attach(Node::Video::Sprite) -> void;
|
||||
auto detach(Node::Video::Sprite) -> void;
|
||||
@@ -100,6 +102,7 @@ protected:
|
||||
std::unique_ptr<u32[]> _output;
|
||||
std::unique_ptr<u32[]> _rotate;
|
||||
std::unique_ptr<u32[]> _palette;
|
||||
std::unique_ptr<u32[]> _deinterlaceHistory;
|
||||
std::vector<n1> _lineOverrideActive;
|
||||
std::vector<const u32*> _lineOverride;
|
||||
std::vector<Node::Video::Sprite> _sprites;
|
||||
@@ -116,6 +119,8 @@ protected:
|
||||
bool _progressiveDouble = false;
|
||||
bool _interlace = false;
|
||||
bool _interlaceField = false;
|
||||
string _deinterlace = "Weave";
|
||||
bool _deinterlaceHistoryValid = false;
|
||||
u32 _viewportX = 0;
|
||||
u32 _viewportY = 0;
|
||||
u32 _viewportWidth = 0;
|
||||
|
||||
@@ -23,10 +23,6 @@ inline Thread::~Thread() {
|
||||
}
|
||||
|
||||
inline auto Thread::active() const -> bool { return co_active() == _handle; }
|
||||
inline auto Thread::handle() const -> cothread_t { return _handle; }
|
||||
inline auto Thread::frequency() const -> u64 { return _frequency; }
|
||||
inline auto Thread::scalar() const -> u64 { return _scalar; }
|
||||
inline auto Thread::clock() const -> u64 { return _clock; }
|
||||
|
||||
inline auto Thread::setHandle(cothread_t handle) -> void {
|
||||
_handle = handle;
|
||||
|
||||
@@ -19,10 +19,10 @@ struct Thread {
|
||||
|
||||
explicit operator bool() const { return _handle; }
|
||||
auto active() const -> bool;
|
||||
auto handle() const -> cothread_t;
|
||||
auto frequency() const -> u64;
|
||||
auto scalar() const -> u64;
|
||||
auto clock() const -> u64;
|
||||
auto handle() const -> cothread_t { return _handle; }
|
||||
auto frequency() const -> u64 { return _frequency; }
|
||||
auto scalar() const -> u64 { return _scalar; }
|
||||
auto clock() const -> u64 { return _clock; }
|
||||
|
||||
auto setHandle(cothread_t handle) -> void;
|
||||
auto setFrequency(double frequency) -> void;
|
||||
|
||||
@@ -23,6 +23,15 @@ macro(component_sources)
|
||||
set(ARES_HEADER_SOURCES ${ARES_HEADER_SOURCES} PARENT_SCOPE)
|
||||
endmacro()
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
arcadedip
|
||||
PRIMARY
|
||||
arcade/dip-switches.cpp
|
||||
INCLUDED
|
||||
arcade/dip-switches.hpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
ay38910
|
||||
@@ -448,6 +457,13 @@ component_sources(
|
||||
processor/z80/registers.hpp
|
||||
processor/z80/serialization.cpp
|
||||
processor/z80/z80.hpp
|
||||
processor/z80/sega/base.hpp
|
||||
processor/z80/sega/s315-5114.hpp
|
||||
processor/z80/sega/s315-5114.cpp
|
||||
processor/z80/sega/s315-5177.hpp
|
||||
processor/z80/sega/s315-5177.cpp
|
||||
processor/z80/sega/mc8123.hpp
|
||||
processor/z80/sega/mc8123.cpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
@@ -460,6 +476,16 @@ component_sources(
|
||||
rtc/s3511a/serialization.cpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
upd4990a
|
||||
PRIMARY
|
||||
rtc/upd4990a/upd4990a.cpp
|
||||
INCLUDED
|
||||
rtc/upd4990a/upd4990a.hpp
|
||||
rtc/upd4990a/serialization.cpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
sst39sf0x0
|
||||
@@ -497,3 +523,9 @@ component_sources(
|
||||
video/v9938/sprite.cpp
|
||||
video/v9938/v9938.hpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT parallel-flash
|
||||
PRIMARY eeprom/parallel-flash/parallel-flash.cpp
|
||||
INCLUDED eeprom/parallel-flash/parallel-flash.hpp
|
||||
)
|
||||
@@ -0,0 +1,129 @@
|
||||
#include <ares/ares.hpp>
|
||||
#include "dip-switches.hpp"
|
||||
|
||||
namespace ares::Arcade {
|
||||
|
||||
auto Dip::load(Node::Object parent, VFS::Pak pak, std::initializer_list<string> names, u32 excluded, bool reuseNode, string manifest) -> void {
|
||||
unload();
|
||||
for(auto name : names) ports.push_back({name});
|
||||
if(!pak) return;
|
||||
this->pak = pak;
|
||||
|
||||
Markup::Node saved;
|
||||
if(auto state = pak->read("dips.bml")) saved = BML::unserialize(state->reads());
|
||||
|
||||
auto fp = pak->read(manifest);
|
||||
if(!fp) return;
|
||||
auto document = BML::unserialize(fp->reads());
|
||||
auto games = document.find("game");
|
||||
if(games.size() != 1) return;
|
||||
|
||||
ownsNode = !reuseNode;
|
||||
node = reuseNode ? parent : parent->append<Node::Object>("DIP Switches");
|
||||
for(auto dip : games.front()) {
|
||||
if(dip.name() != "dip") continue;
|
||||
auto portName = dip["port"].string();
|
||||
u32 port = 0;
|
||||
while(port < ports.size() && ports[port].name != portName) port++;
|
||||
if(port == ports.size()) continue;
|
||||
|
||||
auto maskValue = dip["mask"].natural();
|
||||
auto defaultValue = dip["default"].natural();
|
||||
if(!maskValue || maskValue > 0xffff'ffff || (defaultValue & ~maskValue) || (maskValue & excluded)) continue;
|
||||
u32 mask = maskValue;
|
||||
ports[port].value = (ports[port].value & ~mask) | defaultValue;
|
||||
|
||||
auto name = dip["name"].string();
|
||||
if(!name || name == "Unused" || name == "Unknown") continue;
|
||||
|
||||
std::vector<Option> options;
|
||||
string defaultName;
|
||||
for(auto option : dip) {
|
||||
if(option.name() != "option") continue;
|
||||
auto optionName = option["name"].string();
|
||||
auto optionValue = option["value"].natural();
|
||||
if(!optionName || optionValue > 0xffff'ffff || (optionValue & ~maskValue)) continue;
|
||||
for(auto& existing : options) {
|
||||
if(existing.name == optionName) {
|
||||
optionName.append(" (", optionValue, ")");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(optionValue == defaultValue) defaultName = optionName;
|
||||
options.push_back({optionName, (u32)optionValue});
|
||||
}
|
||||
if(options.empty() || !defaultName) continue;
|
||||
|
||||
for(auto stored : saved.find("dip")) {
|
||||
if(stored["port"].string() != portName || stored["mask"].natural() != mask) continue;
|
||||
auto value = stored["value"].natural();
|
||||
for(auto& option : options) {
|
||||
if(option.value != value) continue;
|
||||
defaultName = option.name;
|
||||
ports[port].value = (ports[port].value & ~mask) | option.value;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
auto index = settings.size();
|
||||
settings.push_back({{}, port, mask, std::move(options)});
|
||||
auto& setting = settings.back();
|
||||
std::vector<string> allowed;
|
||||
for(auto& option : setting.options) allowed.push_back(option.name);
|
||||
setting.node = node->append<Node::Setting::String>(name, defaultName, [this, index](auto value) {
|
||||
update(index, value);
|
||||
});
|
||||
setting.node->setAllowedValues(std::move(allowed));
|
||||
setting.node->setDynamic(true);
|
||||
}
|
||||
}
|
||||
|
||||
auto Dip::unload() -> void {
|
||||
if(ownsNode) {
|
||||
if(auto parent = Node::parent(node)) parent->remove(node);
|
||||
}
|
||||
node.reset();
|
||||
pak.reset();
|
||||
ownsNode = false;
|
||||
settings.clear();
|
||||
ports.clear();
|
||||
}
|
||||
|
||||
auto Dip::read(string name) const -> u32 {
|
||||
for(auto& port : ports) {
|
||||
if(port.name == name) return port.value;
|
||||
}
|
||||
return 0xffff'ffff;
|
||||
}
|
||||
|
||||
auto Dip::update(u32 index, string value) -> void {
|
||||
if(index >= settings.size()) return;
|
||||
auto& setting = settings[index];
|
||||
for(auto& option : setting.options) {
|
||||
if(option.name != value) continue;
|
||||
auto& port = ports[setting.port];
|
||||
port.value = (port.value & ~setting.mask) | option.value;
|
||||
persist();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto Dip::persist() -> void {
|
||||
if(!pak) return;
|
||||
auto fp = pak->write("dips.bml");
|
||||
if(!fp) return;
|
||||
string state;
|
||||
for(auto& setting : settings) {
|
||||
auto& port = ports[setting.port];
|
||||
state.append("dip\n");
|
||||
state.append(" port: ", port.name, "\n");
|
||||
state.append(" mask: ", setting.mask, "\n");
|
||||
state.append(" value: ", port.value & setting.mask, "\n");
|
||||
}
|
||||
fp->resize(state.size());
|
||||
fp->seek(0);
|
||||
fp->writes(state);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
namespace ares::Arcade {
|
||||
|
||||
struct Dip {
|
||||
struct Option {
|
||||
string name;
|
||||
u32 value;
|
||||
};
|
||||
|
||||
struct Setting {
|
||||
Node::Setting::String node;
|
||||
u32 port;
|
||||
u32 mask;
|
||||
std::vector<Option> options;
|
||||
};
|
||||
|
||||
struct Port {
|
||||
string name;
|
||||
u32 value = 0xffff'ffff;
|
||||
};
|
||||
|
||||
auto load(Node::Object parent, VFS::Pak pak, std::initializer_list<string> names, u32 excluded = 0, bool reuseNode = false, string manifest = "manifest.bml") -> void;
|
||||
auto unload() -> void;
|
||||
auto read(string name) const -> u32;
|
||||
auto update(u32 index, string value) -> void;
|
||||
auto persist() -> void;
|
||||
|
||||
Node::Object node;
|
||||
VFS::Pak pak;
|
||||
bool ownsNode = false;
|
||||
std::vector<Port> ports;
|
||||
std::vector<Setting> settings;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
#include <ares/ares.hpp>
|
||||
#include "parallel-flash.hpp"
|
||||
|
||||
namespace ares {
|
||||
|
||||
ParallelFlash::ParallelFlash(Model model) {
|
||||
switch(model) {
|
||||
case Model::SST29EE010: capacity = 128_KiB; pageSize = 128; manufacturerID = 0xbf; deviceID = 0x07; break;
|
||||
case Model::SST29EE020: capacity = 256_KiB; pageSize = 128; manufacturerID = 0xbf; deviceID = 0x10; break;
|
||||
case Model::W29C040: capacity = 512_KiB; pageSize = 256; manufacturerID = 0xda; deviceID = 0x46; break;
|
||||
case Model::AT29C040A: capacity = 512_KiB; pageSize = 256; manufacturerID = 0x1f; deviceID = 0xa4; break;
|
||||
}
|
||||
}
|
||||
|
||||
auto ParallelFlash::power() -> void {
|
||||
phase = Phase::Read;
|
||||
command = 0;
|
||||
remaining = 0;
|
||||
id = false;
|
||||
toggle = false;
|
||||
}
|
||||
|
||||
auto ParallelFlash::read(u32 address) -> u8 {
|
||||
if(memory.size() != capacity) return 0xff;
|
||||
address &= capacity - 1;
|
||||
if(phase != Phase::Read) {
|
||||
toggle = !toggle;
|
||||
return (lastData ^ 0x80) & 0x80 | (toggle ? 0x40 : 0);
|
||||
}
|
||||
if(id) {
|
||||
if((address & 0x7fff) == 0) return manufacturerID;
|
||||
if((address & 0x7fff) == 1) return deviceID;
|
||||
return 0xff;
|
||||
}
|
||||
return memory[address];
|
||||
}
|
||||
|
||||
auto ParallelFlash::write(u32 address, u8 data) -> void {
|
||||
if(memory.size() != capacity || phase == Phase::Program || phase == Phase::Erase) return;
|
||||
address &= capacity - 1;
|
||||
auto load = [&] {
|
||||
if(phase != Phase::Load) nall::memory::fill<u8>(page, pageSize, 0xff);
|
||||
phase = Phase::Load;
|
||||
pageAddress = address & ~(pageSize - 1);
|
||||
page[address & (pageSize - 1)] = lastData = data;
|
||||
remaining = 200;
|
||||
command = 0;
|
||||
};
|
||||
if(phase == Phase::Load || command == 3) return load();
|
||||
u32 low = address & 0x7fff;
|
||||
switch(command) {
|
||||
case 0:
|
||||
if(low == 0x5555 && data == 0xaa) command = 1;
|
||||
else if(!protection && !id) load();
|
||||
break;
|
||||
case 1:
|
||||
command = low == 0x2aaa && data == 0x55 ? 2 : 0;
|
||||
break;
|
||||
case 2:
|
||||
command = 0;
|
||||
if(low != 0x5555) break;
|
||||
if(data == 0xa0) {
|
||||
protection = true;
|
||||
id = false;
|
||||
command = 3;
|
||||
}
|
||||
if(data == 0x90) id = true;
|
||||
if(data == 0xf0) id = false;
|
||||
if(data == 0x80) command = 4;
|
||||
break;
|
||||
case 4:
|
||||
command = low == 0x5555 && data == 0xaa ? 5 : 0;
|
||||
break;
|
||||
case 5:
|
||||
command = low == 0x2aaa && data == 0x55 ? 6 : 0;
|
||||
break;
|
||||
case 6:
|
||||
command = 0;
|
||||
if(low != 0x5555) break;
|
||||
if(data == 0x10) {
|
||||
phase = Phase::Erase;
|
||||
remaining = 20'000;
|
||||
lastData = 0xff;
|
||||
id = false;
|
||||
}
|
||||
if(data == 0x20) {
|
||||
protection = false;
|
||||
id = false;
|
||||
}
|
||||
if(data == 0x60) id = true;
|
||||
if(data == 0xf0) id = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto ParallelFlash::step(u32 clocks) -> void {
|
||||
while(remaining && clocks >= remaining) {
|
||||
clocks -= remaining;
|
||||
if(phase == Phase::Load) {
|
||||
phase = Phase::Program;
|
||||
remaining = 5'000;
|
||||
} else {
|
||||
if(phase == Phase::Program) nall::memory::copy(memory.data() + pageAddress, page, pageSize);
|
||||
if(phase == Phase::Erase) nall::memory::fill<u8>(memory.data(), memory.size(), 0xff);
|
||||
phase = Phase::Read;
|
||||
remaining = 0;
|
||||
}
|
||||
}
|
||||
if(remaining) remaining -= clocks;
|
||||
}
|
||||
|
||||
auto ParallelFlash::serialize(serializer& s) -> void {
|
||||
if(!memory.empty()) s(std::span<u8>{memory.data(), memory.size()});
|
||||
s(page);
|
||||
s(phase);
|
||||
s(command);
|
||||
s(pageAddress);
|
||||
s(remaining);
|
||||
s(lastData);
|
||||
s(id);
|
||||
s(protection);
|
||||
s(toggle);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
namespace ares {
|
||||
|
||||
struct ParallelFlash {
|
||||
enum class Model : u32 { SST29EE010, SST29EE020, W29C040, AT29C040A };
|
||||
std::vector<u8> memory;
|
||||
u32 capacity;
|
||||
u32 pageSize;
|
||||
u8 deviceID;
|
||||
u8 manufacturerID;
|
||||
u8 page[256] = {};
|
||||
enum class Phase : u32 { Read, Load, Program, Erase } phase = Phase::Read;
|
||||
u32 command = 0;
|
||||
u32 pageAddress = 0;
|
||||
u32 remaining = 0;
|
||||
u8 lastData = 0xff;
|
||||
bool id = false;
|
||||
bool protection = false;
|
||||
bool toggle = false;
|
||||
ParallelFlash(Model model);
|
||||
auto power() -> void;
|
||||
auto read(u32 address) -> u8;
|
||||
auto write(u32 address, u8 data) -> void;
|
||||
auto step(u32 microseconds) -> void;
|
||||
auto serialize(serializer&) -> void;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -20,7 +20,7 @@ auto HG51B::halt() -> void {
|
||||
auto HG51B::wait(n24 address) -> u32 {
|
||||
if(isROM(address)) return 1 + io.wait.rom;
|
||||
if(isRAM(address)) return 1 + io.wait.ram;
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto HG51B::main() -> void {
|
||||
@@ -73,20 +73,24 @@ auto HG51B::suspend() -> void {
|
||||
auto HG51B::cache() -> bool {
|
||||
n24 address = io.cache.base + r.pb * 512;
|
||||
|
||||
//try to use the current page ...
|
||||
if(io.cache.address[io.cache.page] == address) return io.cache.enable = 0, true;
|
||||
//if it's not valid, try to use the other page ...
|
||||
io.cache.page ^= 1;
|
||||
if(io.cache.address[io.cache.page] == address) return io.cache.enable = 0, true;
|
||||
//if it's not valid, try to load into the other page ...
|
||||
if(io.cache.lock[io.cache.page]) io.cache.page ^= 1;
|
||||
//if it's locked, try to load into the first page ...
|
||||
if(io.cache.lock[io.cache.page]) return io.cache.enable = 0, false;
|
||||
if(io.cache.preload == 0) {
|
||||
//try to use the current page ...
|
||||
if(io.cache.address[io.cache.page] == address) return io.cache.enable = 0, true;
|
||||
//if it's not valid, try to use the other page ...
|
||||
io.cache.page ^= 1;
|
||||
if(io.cache.address[io.cache.page] == address) return io.cache.enable = 0, true;
|
||||
//if it's not valid, try to load into the other page ...
|
||||
if(io.cache.lock[io.cache.page]) io.cache.page ^= 1;
|
||||
//if it's locked, try to load into the first page ...
|
||||
if(io.cache.lock[io.cache.page]) return io.cache.enable = 0, false;
|
||||
}
|
||||
io.cache.preload = 0;
|
||||
|
||||
io.cache.address[io.cache.page] = address;
|
||||
for(u32 offset : range(256)) {
|
||||
step(wait(address));
|
||||
programRAM[io.cache.page][offset].byte(0) = read(address++);
|
||||
step(wait(address));
|
||||
programRAM[io.cache.page][offset].byte(1) = read(address++);
|
||||
}
|
||||
return io.cache.enable = 0, true;
|
||||
|
||||
@@ -156,6 +156,7 @@ protected:
|
||||
struct Cache {
|
||||
n1 enable;
|
||||
n1 page;
|
||||
n1 preload;
|
||||
n1 lock[2];
|
||||
n24 address[2]; //cache address is in bytes; so 24-bit
|
||||
n24 base; //base address is also in bytes
|
||||
|
||||
@@ -12,13 +12,13 @@ auto HG51B::readRegister(n7 address) -> n24 {
|
||||
case 0x2e:
|
||||
io.bus.enable = 1;
|
||||
io.bus.reading = 1;
|
||||
io.bus.pending = 1 + io.wait.rom;
|
||||
io.bus.pending = io.wait.rom;
|
||||
io.bus.address = r.mar;
|
||||
return 0x000000;
|
||||
case 0x2f:
|
||||
io.bus.enable = 1;
|
||||
io.bus.reading = 1;
|
||||
io.bus.pending = 1 + io.wait.ram;
|
||||
io.bus.pending = io.wait.ram;
|
||||
io.bus.address = r.mar;
|
||||
return 0x000000;
|
||||
|
||||
@@ -76,13 +76,13 @@ auto HG51B::writeRegister(n7 address, n24 data) -> void {
|
||||
case 0x2e:
|
||||
io.bus.enable = 1;
|
||||
io.bus.writing = 1;
|
||||
io.bus.pending = 1 + io.wait.rom;
|
||||
io.bus.pending = io.wait.rom;
|
||||
io.bus.address = r.mar;
|
||||
return;
|
||||
case 0x2f:
|
||||
io.bus.enable = 1;
|
||||
io.bus.writing = 1;
|
||||
io.bus.pending = 1 + io.wait.ram;
|
||||
io.bus.pending = io.wait.ram;
|
||||
io.bus.address = r.mar;
|
||||
return;
|
||||
|
||||
|
||||
@@ -4,11 +4,11 @@ auto Z80::disassembleInstruction(maybe<n16> _pc) -> string {
|
||||
string s, output;
|
||||
|
||||
n8 prefix = 0x00;
|
||||
auto code = bus->read(pc++);
|
||||
auto code = bus->fetch(pc++);
|
||||
if(code == 0xdd || code == 0xfd) {
|
||||
prefix = code;
|
||||
|
||||
code = bus->read(pc++);
|
||||
code = bus->fetch(pc++);
|
||||
if(code == 0xdd || code == 0xfd) {
|
||||
if(prefix == 0xdd) {
|
||||
s.append("ix:");
|
||||
@@ -23,13 +23,13 @@ auto Z80::disassembleInstruction(maybe<n16> _pc) -> string {
|
||||
|
||||
if(code == 0xcb && prefix) {
|
||||
auto d = (i8)bus->read(pc++);
|
||||
code = bus->read(pc++);
|
||||
code = bus->read(pc++); //DD/FD CB's final byte is fetched without M1
|
||||
output = disassembleCBd(pc, prefix, d, code);
|
||||
} else if(code == 0xcb) {
|
||||
code = bus->read(pc++);
|
||||
code = bus->fetch(pc++);
|
||||
output = disassembleCB(pc, prefix, code);
|
||||
} else if(code == 0xed) {
|
||||
code = bus->read(pc++);
|
||||
code = bus->fetch(pc++);
|
||||
output = disassembleED(pc, prefix, code);
|
||||
} else {
|
||||
output = disassemble(pc, prefix, code);
|
||||
|
||||
@@ -4,7 +4,7 @@ auto Z80::wait(u32 clocks) -> void {
|
||||
|
||||
auto Z80::opcode() -> n8 {
|
||||
step(4);
|
||||
return bus->read(PC++);
|
||||
return bus->fetch(PC++);
|
||||
}
|
||||
|
||||
auto Z80::operand() -> n8 {
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <component/processor/z80/z80.hpp>
|
||||
|
||||
namespace ares {
|
||||
|
||||
struct SegaZ80 {
|
||||
virtual ~SegaZ80() = default;
|
||||
virtual auto decrypt(n16 address, n8 source, bool opcode) const -> n8 = 0;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
// MC-8123 decryption algorithm derived from MAME (BSD-3-Clause).
|
||||
// See the MAME section in LICENSE.
|
||||
auto MC8123::reorder(n8 source, const u8 (&bits)[8]) const -> n8 {
|
||||
n8 result = 0;
|
||||
for(u32 index : range(8)) result.bit(7 - index) = source.bit(bits[index]);
|
||||
return result;
|
||||
}
|
||||
|
||||
auto MC8123::permute(n8 source, n3 type, n2 permutation) const -> n8 {
|
||||
static constexpr u8 positions[7][4][8] = {
|
||||
{
|
||||
{7,5,3,1,2,0,6,4},
|
||||
{5,3,7,2,1,0,4,6},
|
||||
{0,3,4,6,7,1,5,2},
|
||||
{0,7,3,2,6,4,1,5},
|
||||
},
|
||||
{ // Type1a
|
||||
{4,2,6,5,3,7,1,0},
|
||||
{6,0,5,4,3,2,1,7},
|
||||
{2,3,6,1,4,0,7,5},
|
||||
{6,5,1,3,2,7,0,4},
|
||||
},
|
||||
{ // Type1b
|
||||
{1,0,3,2,5,6,4,7},
|
||||
{2,0,5,1,7,4,6,3},
|
||||
{6,4,7,2,0,5,1,3},
|
||||
{7,1,3,6,0,2,5,4},
|
||||
},
|
||||
{ // Type2a
|
||||
{0,1,4,3,5,6,2,7},
|
||||
{6,3,0,5,7,4,1,2},
|
||||
{1,6,4,5,0,3,7,2},
|
||||
{4,6,7,5,2,3,1,0},
|
||||
},
|
||||
{ // Type2b
|
||||
{1,3,4,6,5,7,0,2},
|
||||
{0,1,5,4,7,3,2,6},
|
||||
{3,5,4,1,6,2,0,7},
|
||||
{5,2,3,0,4,7,6,1},
|
||||
},
|
||||
{ // Type3a
|
||||
{5,3,1,7,0,2,6,4},
|
||||
{3,1,2,5,4,7,0,6},
|
||||
{5,6,1,2,7,0,4,3},
|
||||
{5,6,7,0,4,2,1,3},
|
||||
},
|
||||
{ // Type3b
|
||||
{3,7,5,4,0,6,2,1},
|
||||
{7,5,4,6,1,2,0,3},
|
||||
{7,4,3,0,5,1,6,2},
|
||||
{2,6,4,1,3,7,0,5},
|
||||
},
|
||||
};
|
||||
|
||||
return reorder(source, positions[type][permutation]);
|
||||
}
|
||||
|
||||
auto MC8123::type0(n8 value, n4 parameter, n2 permutation) const -> n8 {
|
||||
value = permute(value, 0, permutation);
|
||||
|
||||
if(parameter.bit(3) && value.bit(7)) value ^= 0x29;
|
||||
|
||||
if(parameter.bit(2) && value.bit(6)) value ^= 0x86;
|
||||
|
||||
if(value.bit(6)) value ^= 0x80;
|
||||
|
||||
if(parameter.bit(1) && value.bit(7)) value ^= 0x40;
|
||||
|
||||
if(value.bit(2)) value ^= 0x21;
|
||||
|
||||
value ^= 0x1a;
|
||||
|
||||
if(parameter.bit(2)) value ^= 0x25;
|
||||
if(parameter.bit(1)) value ^= 0xc0;
|
||||
if(parameter.bit(0)) value ^= 0x21;
|
||||
|
||||
if(parameter.bit(0)) value = reorder(value, {7,6,5,1,4,3,2,0});
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
auto MC8123::type1a(n8 value, n4 parameter, n2 permutation) const -> n8 {
|
||||
value = permute(value, 1, permutation);
|
||||
|
||||
if(parameter.bit(2)) value = reorder(value, {7,6,1,5,3,2,4,0});
|
||||
|
||||
if(value.bit(1)) value ^= 0x01;
|
||||
if(value.bit(6)) value ^= 0x08;
|
||||
if(value.bit(7)) value ^= 0x48;
|
||||
if(value.bit(2)) value ^= 0x4a;
|
||||
if(value.bit(4)) value ^= 0xc4;
|
||||
|
||||
if(value.bit(7) ^ value.bit(2)) value ^= 0x10;
|
||||
|
||||
value ^= 0x4b;
|
||||
|
||||
if(parameter.bit(3)) value ^= 0x84;
|
||||
if(parameter.bit(1)) value ^= 0x48;
|
||||
|
||||
if(parameter.bit(0)) value = reorder(value, {7,6,1,4,3,2,5,0});
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
auto MC8123::type1b(n8 value, n4 parameter, n2 permutation) const -> n8 {
|
||||
value = permute(value, 2, permutation);
|
||||
|
||||
if(value.bit(2) && value.bit(0)) value ^= 0x90;
|
||||
|
||||
if(value.bit(7)) value ^= 0x04;
|
||||
if(value.bit(5)) value ^= 0x84;
|
||||
if(value.bit(1)) value ^= 0x20;
|
||||
if(value.bit(6)) value ^= 0x02;
|
||||
if(value.bit(4)) value ^= 0x60;
|
||||
if(value.bit(0)) value ^= 0x46;
|
||||
if(value.bit(3)) value ^= 0xc7;
|
||||
|
||||
value ^= 0x51;
|
||||
|
||||
if(parameter.bit(3)) value ^= 0x12;
|
||||
if(parameter.bit(2)) value ^= 0xc9;
|
||||
if(parameter.bit(1)) value ^= 0x18;
|
||||
if(parameter.bit(0)) value ^= 0x47;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
auto MC8123::type2a(n8 value, n4 parameter, n2 permutation) const -> n8 {
|
||||
value = permute(value, 3, permutation);
|
||||
|
||||
if(value.bit(3) || (parameter.bit(1) && value.bit(2)))
|
||||
value = reorder(value, {6,0,7,4,3,2,1,5});
|
||||
|
||||
if(value.bit(5)) value ^= 0x80;
|
||||
if(value.bit(6)) value ^= 0x20;
|
||||
if(value.bit(0)) value ^= 0x40;
|
||||
if(value.bit(4)) value ^= 0x09;
|
||||
if(value.bit(1)) value ^= 0x04;
|
||||
|
||||
value ^= 0xf2;
|
||||
|
||||
if(parameter.bit(2)) value ^= 0x1f;
|
||||
|
||||
if(parameter.bit(3)) {
|
||||
if(parameter.bit(0)) value = reorder(value, {7,6,5,3,4,1,2,0});
|
||||
else value = reorder(value, {7,6,5,1,2,4,3,0});
|
||||
} else if(parameter.bit(0)) {
|
||||
value = reorder(value, {7,6,5,2,1,3,4,0});
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
auto MC8123::type2b(n8 value, n4 parameter, n2 permutation) const -> n8 {
|
||||
value = permute(value, 4, permutation);
|
||||
|
||||
if(value.bit(7) && value.bit(3)) value ^= 0x51;
|
||||
|
||||
if(value.bit(7)) value ^= 0x04;
|
||||
if(value.bit(5)) value ^= 0x88;
|
||||
if(value.bit(1)) value ^= 0x20;
|
||||
if(value.bit(4)) value ^= 0xaa;
|
||||
|
||||
if(value.bit(7) && value.bit(5)) value ^= 0x11;
|
||||
|
||||
if(value.bit(5) && value.bit(1)) value ^= 0x11;
|
||||
|
||||
if(value.bit(6)) value ^= 0xa0;
|
||||
if(value.bit(3)) value ^= 0xe2;
|
||||
if(value.bit(2)) value ^= 0x0a;
|
||||
|
||||
value ^= 0x8e;
|
||||
|
||||
if(parameter.bit(3)) value ^= 0x4a;
|
||||
if(parameter.bit(2)) value ^= 0xee;
|
||||
if(parameter.bit(1)) value ^= 0x80;
|
||||
if(parameter.bit(0)) value ^= 0x24;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
auto MC8123::type3a(n8 value, n4 parameter, n2 permutation) const -> n8 {
|
||||
value = permute(value, 5, permutation);
|
||||
|
||||
if(value.bit(2)) value ^= 0xb0;
|
||||
if(value.bit(3)) value ^= 0x01;
|
||||
|
||||
if(parameter.bit(0)) value = reorder(value, {7,2,5,4,3,1,0,6});
|
||||
|
||||
if(value.bit(1)) value ^= 0x41;
|
||||
if(value.bit(3)) value ^= 0x16;
|
||||
|
||||
if(parameter.bit(3)) value ^= 0x18;
|
||||
|
||||
if(value.bit(3)) value = reorder(value, {5,6,7,4,3,2,1,0});
|
||||
|
||||
if(value.bit(5)) value ^= 0x06;
|
||||
|
||||
value ^= 0x78;
|
||||
|
||||
if(parameter.bit(2)) value ^= 0x80;
|
||||
if(parameter.bit(1)) value ^= 0x10;
|
||||
if(parameter.bit(0)) value ^= 0x01;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
auto MC8123::type3b(n8 value, n4 parameter, n2 permutation) const -> n8 {
|
||||
value = permute(value, 6, permutation);
|
||||
|
||||
if(value.bit(2)) value ^= 0x80;
|
||||
|
||||
if(value.bit(7)) value = reorder(value, {7,6,3,4,5,2,1,0});
|
||||
|
||||
if(parameter.bit(3)) value ^= 0x80;
|
||||
|
||||
if(value.bit(4)) value ^= 0x40;
|
||||
if(value.bit(1)) value ^= 0x54;
|
||||
|
||||
if(value.bit(7) && value.bit(6)) value ^= 0x02;
|
||||
|
||||
if(value.bit(7)) value ^= 0x02;
|
||||
|
||||
if(parameter.bit(3)) value ^= 0x80;
|
||||
if(parameter.bit(2)) value ^= 0x01;
|
||||
|
||||
if(parameter.bit(3)) value = reorder(value, {4,6,3,2,5,0,1,7});
|
||||
|
||||
if(value.bit(4)) value ^= 0x02;
|
||||
if(value.bit(5)) value ^= 0x10;
|
||||
if(value.bit(7)) value ^= 0x04;
|
||||
|
||||
value ^= 0x2c;
|
||||
|
||||
if(parameter.bit(1)) value ^= 0x80;
|
||||
if(parameter.bit(0)) value ^= 0x08;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
auto MC8123::transform(n8 value, n8 key, bool opcode) const -> n8 {
|
||||
key ^= 0xff;
|
||||
if(!key) return value;
|
||||
|
||||
n3 type = 0;
|
||||
type.bit(0) = key.bit(0) ^ key.bit(2);
|
||||
type.bit(1) = key.bit(0) ^ key.bit(1) ^ key.bit(2) ^ key.bit(4);
|
||||
type.bit(2) = key.bit(4) ^ key.bit(5);
|
||||
|
||||
n2 permutation = 0;
|
||||
permutation.bit(0) = key.bit(0) ^ key.bit(1);
|
||||
permutation.bit(1) = key.bit(2) ^ key.bit(3);
|
||||
|
||||
n4 parameter = 0;
|
||||
parameter.bit(0) = key.bit(0);
|
||||
parameter.bit(1) = key.bit(0) ^ key.bit(2) ^ key.bit(3);
|
||||
parameter.bit(2) = key.bit(0) ^ key.bit(1) ^ key.bit(6);
|
||||
parameter.bit(3) = key.bit(1) ^ key.bit(6) ^ key.bit(7);
|
||||
|
||||
if(!opcode) {
|
||||
parameter.bit(0) = !parameter.bit(0);
|
||||
type.bit(0) = !type.bit(0);
|
||||
}
|
||||
|
||||
switch(type) {
|
||||
case 0:
|
||||
case 1: return type0(value, parameter, permutation);
|
||||
case 2: return type1a(value, parameter, permutation);
|
||||
case 3: return type1b(value, parameter, permutation);
|
||||
case 4: return type2a(value, parameter, permutation);
|
||||
case 5: return type2b(value, parameter, permutation);
|
||||
case 6: return type3a(value, parameter, permutation);
|
||||
case 7: return type3b(value, parameter, permutation);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
auto MC8123::decrypt(n16 address, n8 source, bool opcode) const -> n8 {
|
||||
if(address >= encryptedSize) return source;
|
||||
n12 index = 0;
|
||||
index.bit(0) = address.bit(0);
|
||||
index.bit(1) = address.bit(1);
|
||||
index.bit(2) = address.bit(2);
|
||||
index.bit(3) = address.bit(4);
|
||||
index.bit(4) = address.bit(6);
|
||||
index.bit(5) = address.bit(8);
|
||||
index.bit(6) = address.bit(10);
|
||||
index.bit(7) = address.bit(11);
|
||||
index.bit(8) = address.bit(12);
|
||||
index.bit(9) = address.bit(13);
|
||||
index.bit(10) = address.bit(14);
|
||||
index.bit(11) = address.bit(15);
|
||||
return transform(source, keyData[index | (opcode ? 0 : 0x1000)], opcode);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <component/processor/z80/sega/base.hpp>
|
||||
|
||||
namespace ares {
|
||||
|
||||
struct MC8123 : SegaZ80 {
|
||||
u8 keyData[0x2000] = {};
|
||||
u16 encryptedSize = 0xc000;
|
||||
|
||||
auto decrypt(n16 address, n8 source, bool opcode) const -> n8 override;
|
||||
|
||||
private:
|
||||
auto reorder(n8 source, const u8 (&bits)[8]) const -> n8;
|
||||
auto permute(n8 source, n3 type, n2 permutation) const -> n8;
|
||||
auto type0(n8 value, n4 parameter, n2 permutation) const -> n8;
|
||||
auto type1a(n8 value, n4 parameter, n2 permutation) const -> n8;
|
||||
auto type1b(n8 value, n4 parameter, n2 permutation) const -> n8;
|
||||
auto type2a(n8 value, n4 parameter, n2 permutation) const -> n8;
|
||||
auto type2b(n8 value, n4 parameter, n2 permutation) const -> n8;
|
||||
auto type3a(n8 value, n4 parameter, n2 permutation) const -> n8;
|
||||
auto type3b(n8 value, n4 parameter, n2 permutation) const -> n8;
|
||||
auto transform(n8 value, n8 key, bool opcode) const -> n8;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Sega 315-5114 decryption table derived from MAME (BSD-3-Clause).
|
||||
// See the MAME section in LICENSE.
|
||||
auto S315_5114::decrypt(n16 address, n8 source, bool opcode) const -> n8 {
|
||||
if(address >= 0x8000) return source;
|
||||
static constexpr u8 table[32][4] = {
|
||||
{0x28,0x08,0xa8,0x88}, {0x88,0x80,0x08,0x00},
|
||||
{0x28,0x08,0xa8,0x88}, {0x28,0xa8,0x08,0x88},
|
||||
{0x88,0x80,0x08,0x00}, {0x88,0x08,0x80,0x00},
|
||||
{0x88,0x08,0x80,0x00}, {0x28,0xa8,0x08,0x88},
|
||||
{0x28,0x08,0xa8,0x88}, {0x88,0x80,0x08,0x00},
|
||||
{0x88,0x80,0x08,0x00}, {0x88,0x80,0x08,0x00},
|
||||
{0x88,0x08,0x80,0x00}, {0x88,0x08,0x80,0x00},
|
||||
{0xa0,0x80,0xa8,0x88}, {0xa0,0x80,0xa8,0x88},
|
||||
{0x80,0xa0,0x00,0x20}, {0x28,0x08,0xa8,0x88},
|
||||
{0x28,0xa8,0x08,0x88}, {0x28,0x08,0xa8,0x88},
|
||||
{0x80,0xa0,0x00,0x20}, {0x80,0xa0,0x00,0x20},
|
||||
{0x28,0xa8,0x08,0x88}, {0x80,0xa0,0x00,0x20},
|
||||
{0xa0,0x80,0xa8,0x88}, {0x28,0x08,0xa8,0x88},
|
||||
{0x80,0xa0,0x00,0x20}, {0xa0,0x80,0xa8,0x88},
|
||||
{0xa0,0x80,0xa8,0x88}, {0x80,0xa0,0x00,0x20},
|
||||
{0xa0,0x80,0xa8,0x88}, {0xa0,0x80,0xa8,0x88},
|
||||
};
|
||||
|
||||
n4 row = 0;
|
||||
row.bit(0) = address.bit(0);
|
||||
row.bit(1) = address.bit(4);
|
||||
row.bit(2) = address.bit(8);
|
||||
row.bit(3) = address.bit(12);
|
||||
|
||||
n2 column = 0;
|
||||
column.bit(0) = source.bit(3);
|
||||
column.bit(1) = source.bit(5);
|
||||
n8 invert = 0;
|
||||
if(source.bit(7)) {
|
||||
column = 3 - column;
|
||||
invert = 0xa8;
|
||||
}
|
||||
n8 decoded = table[2 * row + !opcode][column] ^ invert;
|
||||
n8 result = source;
|
||||
result.bit(3) = decoded.bit(3);
|
||||
result.bit(5) = decoded.bit(5);
|
||||
result.bit(7) = decoded.bit(7);
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <component/processor/z80/sega/base.hpp>
|
||||
|
||||
namespace ares {
|
||||
|
||||
struct S315_5114 : SegaZ80 {
|
||||
auto decrypt(n16 address, n8 source, bool opcode) const -> n8 override;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// Sega 315-5177 decryption algorithm derived from MAME (BSD-3-Clause).
|
||||
// See the MAME section in LICENSE.
|
||||
auto S315_5177::decrypt(n16 address, n8 source, bool opcode) const -> n8 {
|
||||
if(address >= 0x8000) return source;
|
||||
|
||||
static constexpr u8 permutations[24][4] = {
|
||||
{6,4,2,0}, {4,6,2,0}, {2,4,6,0}, {0,4,2,6},
|
||||
{6,2,4,0}, {6,0,2,4}, {6,4,0,2}, {2,6,4,0},
|
||||
{4,2,6,0}, {4,6,0,2}, {6,0,4,2}, {0,6,4,2},
|
||||
{4,0,6,2}, {0,4,6,2}, {6,2,0,4}, {2,6,0,4},
|
||||
{0,6,2,4}, {2,0,6,4}, {0,2,6,4}, {4,2,0,6},
|
||||
{2,4,0,6}, {4,0,2,6}, {2,0,4,6}, {0,2,4,6},
|
||||
};
|
||||
|
||||
static constexpr u8 pattern[24] = {
|
||||
0x04,0x54,0x51,0x15,0x40,0x44,0x01,0x51,
|
||||
0x55,0x10,0x44,0x41,0x05,0x55,0x50,0x14,
|
||||
0x41,0x45,0x00,0x50,0x54,0x11,0x45,0x40,
|
||||
};
|
||||
|
||||
static constexpr u8 permutationPattern[64] = {
|
||||
0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3,
|
||||
3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6,
|
||||
6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9,10,
|
||||
10,10,10,10,11,11,11,11,11,12,12,12,12,12,13,13,
|
||||
};
|
||||
|
||||
n6 row = 0;
|
||||
row.bit(0) = address.bit(0);
|
||||
row.bit(1) = address.bit(3);
|
||||
row.bit(2) = address.bit(6);
|
||||
row.bit(3) = address.bit(9);
|
||||
row.bit(4) = address.bit(12);
|
||||
row.bit(5) = address.bit(14);
|
||||
|
||||
n7 index = 0;
|
||||
index.bit(0) = !opcode;
|
||||
index.bit(1,6) = row;
|
||||
n6 patternIndex = index.bit(0,5);
|
||||
unsigned permutationIndex = permutationPattern[patternIndex] + (index.bit(6) ? 8 : 0);
|
||||
n8 mask = pattern[patternIndex % 24];
|
||||
if(permutationIndex == 0) return source ^ mask;
|
||||
const u8* positions = permutations[permutationIndex];
|
||||
|
||||
n8 result = 0;
|
||||
result.bit(7) = source.bit(7);
|
||||
result.bit(6) = source.bit(positions[0]);
|
||||
result.bit(5) = source.bit(5);
|
||||
result.bit(4) = source.bit(positions[1]);
|
||||
result.bit(3) = source.bit(3);
|
||||
result.bit(2) = source.bit(positions[2]);
|
||||
result.bit(1) = source.bit(1);
|
||||
result.bit(0) = source.bit(positions[3]);
|
||||
return result ^ mask;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <component/processor/z80/sega/base.hpp>
|
||||
|
||||
namespace ares {
|
||||
|
||||
struct S315_5177 : SegaZ80 {
|
||||
auto decrypt(n16 address, n8 source, bool opcode) const -> n8 override;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
#include <ares/ares.hpp>
|
||||
#include "z80.hpp"
|
||||
#include <component/processor/z80/sega/s315-5114.hpp>
|
||||
#include <component/processor/z80/sega/s315-5177.hpp>
|
||||
#include <component/processor/z80/sega/mc8123.hpp>
|
||||
|
||||
namespace ares {
|
||||
|
||||
@@ -10,6 +13,9 @@ namespace ares {
|
||||
#include "algorithms.cpp"
|
||||
#include "instructions.cpp"
|
||||
#include "serialization.cpp"
|
||||
#include <component/processor/z80/sega/s315-5114.cpp>
|
||||
#include <component/processor/z80/sega/s315-5177.cpp>
|
||||
#include <component/processor/z80/sega/mc8123.cpp>
|
||||
|
||||
auto Z80::power(MOSFET mosfet) -> void {
|
||||
this->mosfet = mosfet;
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace ares {
|
||||
struct Z80 {
|
||||
struct Bus {
|
||||
virtual auto read(n16 address) -> n8 = 0;
|
||||
virtual auto fetch(n16 address) -> n8 { return read(address); }
|
||||
virtual auto write(n16 address, n8 data) -> void = 0;
|
||||
|
||||
virtual auto in(n16 address) -> n8 = 0;
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
auto uPD4990A::serialize(serializer& s) -> void {
|
||||
s(data);
|
||||
s(clockPin);
|
||||
s(strobePin);
|
||||
s(shiftRegister);
|
||||
s(commandRegister);
|
||||
s(command);
|
||||
s(outputBit);
|
||||
s(timePulseOutput);
|
||||
s(counter);
|
||||
s(timePulseMode);
|
||||
s(timePulseCounter);
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
#include <ares/ares.hpp>
|
||||
#include "upd4990a.hpp"
|
||||
|
||||
namespace ares {
|
||||
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto uPD4990A::load() -> void {
|
||||
n64 saved = timestamp();
|
||||
if(!saved || !(saved + 1)) {
|
||||
time_t t = time(0);
|
||||
struct tm tmm = *localtime(&t);
|
||||
second() = BCD::encode(tmm.tm_sec);
|
||||
minute() = BCD::encode(tmm.tm_min);
|
||||
hour() = BCD::encode(tmm.tm_hour);
|
||||
day() = BCD::encode(tmm.tm_mday);
|
||||
setWeekday(tmm.tm_wday);
|
||||
setMonth(tmm.tm_mon + 1);
|
||||
year() = BCD::encode(tmm.tm_year % 100);
|
||||
return;
|
||||
}
|
||||
|
||||
//catch up elapsed seconds since last save
|
||||
time_t now = time(0);
|
||||
time_t prev = (time_t)saved;
|
||||
if(now > prev) {
|
||||
n64 elapsed = now - prev;
|
||||
while(elapsed--) tickSecond();
|
||||
}
|
||||
}
|
||||
|
||||
auto uPD4990A::save() -> void {
|
||||
n64 t = time(0);
|
||||
for(auto n : range(8)) data[8 + n] = t.byte(n);
|
||||
}
|
||||
|
||||
auto uPD4990A::power() -> void {
|
||||
clockPin = 0;
|
||||
strobePin = 0;
|
||||
for(auto& b : shiftRegister) b = 0;
|
||||
commandRegister = 0;
|
||||
command = RegisterHold;
|
||||
outputBit = 0;
|
||||
timePulseOutput = 0;
|
||||
counter = 0;
|
||||
timePulseMode = TimePulse64Hz;
|
||||
timePulseCounter = 0;
|
||||
}
|
||||
|
||||
auto uPD4990A::tick() -> void {
|
||||
if(++counter == 0) tickSecond();
|
||||
|
||||
u32 halfPeriod = 256;
|
||||
if(timePulseMode == TimePulse256Hz) halfPeriod = 64;
|
||||
if(timePulseMode == TimePulse2048Hz) halfPeriod = 8;
|
||||
if(timePulseMode == 0x7) halfPeriod = 4;
|
||||
if(timePulseMode == TimePulse1s) halfPeriod = 16'384;
|
||||
if(timePulseMode == TimePulse10s) halfPeriod = 163'840;
|
||||
if(timePulseMode == TimePulse30s) halfPeriod = 491'520;
|
||||
if(timePulseMode == TimePulse60s) halfPeriod = 983'040;
|
||||
if(++timePulseCounter >= halfPeriod) {
|
||||
timePulseCounter = 0;
|
||||
timePulseOutput ^= 1;
|
||||
}
|
||||
|
||||
if(command == RegisterHold || command == TimeRead) {
|
||||
outputBit = counter.bit(14);
|
||||
}
|
||||
}
|
||||
|
||||
auto uPD4990A::tickSecond() -> void {
|
||||
auto bcdIncrement = [](n8& data) -> n8 {
|
||||
if((data & 0x0f) >= 0x09) {
|
||||
data = (data & 0xf0) + 0x10;
|
||||
} else {
|
||||
data++;
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
if(bcdIncrement(second()) < 0x60) return;
|
||||
second() = 0;
|
||||
|
||||
if(bcdIncrement(minute()) < 0x60) return;
|
||||
minute() = 0;
|
||||
|
||||
if(bcdIncrement(hour()) < 0x24) return;
|
||||
hour() = 0;
|
||||
|
||||
setWeekday((getWeekday() + 1) % 7);
|
||||
|
||||
n8 bcdDaysInMonth[12] = {0x31,0x28,0x31,0x30,0x31,0x30,0x31,0x31,0x30,0x31,0x30,0x31};
|
||||
u32 y = BCD::decode(year());
|
||||
if(!(y % 4)) bcdDaysInMonth[1] = 0x29;
|
||||
|
||||
u32 m = getMonth();
|
||||
if(m >= 1 && m <= 12) {
|
||||
if(bcdIncrement(day()) <= bcdDaysInMonth[m - 1]) return;
|
||||
}
|
||||
day() = 0x01;
|
||||
|
||||
m++;
|
||||
if(m <= 12) {
|
||||
setMonth(m);
|
||||
return;
|
||||
}
|
||||
setMonth(1);
|
||||
|
||||
if(bcdIncrement(year()) > 0x99) year() = 0;
|
||||
}
|
||||
|
||||
auto uPD4990A::dataOut() -> n1 {
|
||||
return outputBit;
|
||||
}
|
||||
|
||||
auto uPD4990A::timePulse() -> n1 {
|
||||
return timePulseOutput;
|
||||
}
|
||||
|
||||
auto uPD4990A::write(n1 din, n1 clock, n1 strobe) -> void {
|
||||
if(strobe && !strobePin) {
|
||||
command = commandRegister;
|
||||
|
||||
if(command == TimeSet) {
|
||||
for(u32 index : range(6)) data[index] = shiftRegister[index];
|
||||
}
|
||||
|
||||
if(command == TimeRead) {
|
||||
for(u32 index : range(6)) shiftRegister[index] = data[index];
|
||||
outputBit = counter.bit(14);
|
||||
}
|
||||
|
||||
if(command == RegisterShift) {
|
||||
outputBit = shiftRegister[0] & 1;
|
||||
}
|
||||
|
||||
if(command == RegisterHold) outputBit = counter.bit(14);
|
||||
if(command >= TimePulse64Hz && command <= TimePulse60s) {
|
||||
timePulseMode = command;
|
||||
timePulseCounter = 0;
|
||||
timePulseOutput = 0;
|
||||
}
|
||||
}
|
||||
strobePin = strobe;
|
||||
|
||||
if(clock && !clockPin) {
|
||||
n1 cmdOut = commandRegister.bit(0);
|
||||
commandRegister >>= 1;
|
||||
commandRegister.bit(3) = din;
|
||||
|
||||
if(command == RegisterShift) {
|
||||
for(u32 index : range(6)) {
|
||||
n1 nextBit = (index < 5) ? n1(shiftRegister[index + 1] & 1) : n1(cmdOut);
|
||||
shiftRegister[index] >>= 1;
|
||||
shiftRegister[index] |= (nextBit << 7);
|
||||
}
|
||||
outputBit = shiftRegister[0] & 1;
|
||||
}
|
||||
}
|
||||
clockPin = clock;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
namespace ares {
|
||||
|
||||
//NEC uPD4990A Calendar & Clock
|
||||
|
||||
struct uPD4990A {
|
||||
//upd4990a.cpp
|
||||
auto load() -> void;
|
||||
auto save() -> void;
|
||||
auto power() -> void;
|
||||
auto tick() -> void;
|
||||
auto tickSecond() -> void;
|
||||
|
||||
auto dataOut() -> n1;
|
||||
auto timePulse() -> n1;
|
||||
auto write(n1 din, n1 clock, n1 strobe) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
static constexpr u32 size = 16;
|
||||
n8 data[size];
|
||||
|
||||
auto second() -> n8& { return data[0]; } //BCD 00-59
|
||||
auto minute() -> n8& { return data[1]; } //BCD 00-59
|
||||
auto hour() -> n8& { return data[2]; } //BCD 00-23
|
||||
auto day() -> n8& { return data[3]; } //BCD 01-31
|
||||
auto monthWeekday() -> n8& { return data[4]; } //(month << 4) | weekday
|
||||
auto year() -> n8& { return data[5]; } //BCD 00-99
|
||||
|
||||
auto getMonth() -> u8 { return data[4] >> 4; }
|
||||
auto getWeekday() -> u8 { return data[4] & 0x0f; }
|
||||
auto setMonth(u8 m) -> void { data[4] = (m << 4) | (data[4] & 0x0f); }
|
||||
auto setWeekday(u8 w) -> void { data[4] = (data[4] & 0xf0) | (w & 0x0f); }
|
||||
|
||||
n1 clockPin;
|
||||
n1 strobePin;
|
||||
|
||||
n8 shiftRegister[6];
|
||||
|
||||
n4 commandRegister;
|
||||
n4 command;
|
||||
n1 outputBit;
|
||||
n1 timePulseOutput;
|
||||
n15 counter;
|
||||
n4 timePulseMode;
|
||||
u32 timePulseCounter;
|
||||
|
||||
auto timestamp() -> n64 {
|
||||
n64 value = 0;
|
||||
for(auto index : range(8)) value.byte(index) = data[8 + index];
|
||||
return value;
|
||||
}
|
||||
|
||||
enum Command : u32 {
|
||||
RegisterHold = 0x0,
|
||||
RegisterShift = 0x1,
|
||||
TimeSet = 0x2,
|
||||
TimeRead = 0x3,
|
||||
TimePulse64Hz = 0x4,
|
||||
TimePulse256Hz = 0x5,
|
||||
TimePulse2048Hz = 0x6,
|
||||
TimePulse1s = 0x8,
|
||||
TimePulse10s = 0x9,
|
||||
TimePulse30s = 0xa,
|
||||
TimePulse60s = 0xb,
|
||||
Test = 0xf,
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
ares_components(
|
||||
arcadedip
|
||||
mos6502
|
||||
ym2149
|
||||
ym2413
|
||||
@@ -33,7 +34,6 @@ ares_add_sources(
|
||||
INCLUDED #
|
||||
vs/unisystem.hpp
|
||||
vs/controls.cpp
|
||||
vs/dip-switches.cpp
|
||||
vs/io.cpp
|
||||
vs/standard-input.cpp
|
||||
vs/zapper-input.cpp
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <component/audio/ym2413/ym2413.hpp>
|
||||
#include <component/eeprom/m24c/m24c.hpp>
|
||||
#include <component/flash/sst39sf0x0/sst39sf0x0.hpp>
|
||||
#include <component/arcade/dip-switches.hpp>
|
||||
#include "ymfm_opn.h"
|
||||
|
||||
namespace ares::Famicom {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
static const string SerializerVersion = "v156";
|
||||
static const string SerializerVersion = "v157";
|
||||
|
||||
auto System::serialize(bool synchronize) -> serializer {
|
||||
if(synchronize) scheduler.enter(Scheduler::Mode::Synchronize);
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
static auto parseDipByte(string text, u8& value) -> bool {
|
||||
if(!text) return false;
|
||||
|
||||
u32 base = 10;
|
||||
u32 offset = 0;
|
||||
if(text.size() > 2 && text[0] == '0' && (text[1] == 'x' || text[1] == 'X')) {
|
||||
base = 16;
|
||||
offset = 2;
|
||||
}
|
||||
if(offset == text.size()) return false;
|
||||
|
||||
u32 result = 0;
|
||||
for(auto index : range(offset, text.size())) {
|
||||
u32 digit;
|
||||
auto character = text[(u32)index];
|
||||
if(character >= '0' && character <= '9') digit = character - '0';
|
||||
else if(character >= 'a' && character <= 'f') digit = character - 'a' + 10;
|
||||
else if(character >= 'A' && character <= 'F') digit = character - 'A' + 10;
|
||||
else return false;
|
||||
if(digit >= base || result > (255 - digit) / base) return false;
|
||||
result = result * base + digit;
|
||||
}
|
||||
|
||||
value = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto VsUniSystem::DIPSwitches::load(Node::Object parent) -> bool {
|
||||
unload();
|
||||
if(!cartridge.pak) return false;
|
||||
|
||||
auto manifestFile = cartridge.pak->read("manifest.bml");
|
||||
if(!manifestFile) return false;
|
||||
auto document = BML::unserialize(manifestFile->reads());
|
||||
auto games = document.find("game");
|
||||
if(games.size() != 1) return false;
|
||||
|
||||
u8 usedMasks = 0;
|
||||
std::vector<Definition> definitions;
|
||||
for(auto node : games.front()) {
|
||||
if(node.name() != "dip") continue;
|
||||
|
||||
Definition definition;
|
||||
definition.name = node["name"].string();
|
||||
if(!definition.name) return false;
|
||||
if(node["port"].string() != "DSW0") return false;
|
||||
if(!parseDipByte(node["mask"].string(), definition.mask) || !definition.mask) return false;
|
||||
if(usedMasks & definition.mask) return false;
|
||||
if(!parseDipByte(node["default"].string(), definition.defaultValue)) return false;
|
||||
if(definition.defaultValue & ~definition.mask) return false;
|
||||
|
||||
bool hasDefault = false;
|
||||
for(auto optionNode : node) {
|
||||
if(optionNode.name() != "option") continue;
|
||||
|
||||
Option option;
|
||||
option.name = optionNode["name"].string();
|
||||
if(!option.name) return false;
|
||||
if(!parseDipByte(optionNode["value"].string(), option.value)) return false;
|
||||
if(option.value & ~definition.mask) return false;
|
||||
for(auto& existing : definition.options) {
|
||||
if(existing.name == option.name) return false;
|
||||
}
|
||||
if(option.value == definition.defaultValue) hasDefault = true;
|
||||
definition.options.push_back(std::move(option));
|
||||
}
|
||||
|
||||
if(definition.options.empty() || !hasDefault) return false;
|
||||
usedMasks |= definition.mask;
|
||||
definitions.push_back(std::move(definition));
|
||||
}
|
||||
|
||||
create(parent, std::move(definitions));
|
||||
return true;
|
||||
}
|
||||
|
||||
auto VsUniSystem::DIPSwitches::create(Node::Object parent, std::vector<Definition> definitions) -> void {
|
||||
node = parent->append<Node::Object>("DIP Switches");
|
||||
settings.reserve(definitions.size());
|
||||
|
||||
for(auto& definition : definitions) {
|
||||
value |= definition.defaultValue;
|
||||
auto index = settings.size();
|
||||
|
||||
std::vector<string> allowedValues;
|
||||
for(auto& option : definition.options) allowedValues.push_back(option.name);
|
||||
string defaultValue;
|
||||
for(auto& option : definition.options) {
|
||||
if(option.value == definition.defaultValue) defaultValue = option.name;
|
||||
}
|
||||
|
||||
settings.push_back({{}, definition.mask, std::move(definition.options)});
|
||||
auto& setting = settings.back();
|
||||
setting.node = node->append<Node::Setting::String>(definition.name, defaultValue, [index](auto value) {
|
||||
vsUniSystem.dipSwitches.update(index, value);
|
||||
});
|
||||
setting.node->setAllowedValues(std::move(allowedValues));
|
||||
}
|
||||
}
|
||||
|
||||
auto VsUniSystem::DIPSwitches::unload() -> void {
|
||||
if(auto parent = Node::parent(node)) parent->remove(node);
|
||||
node.reset();
|
||||
settings.clear();
|
||||
value = 0;
|
||||
}
|
||||
|
||||
auto VsUniSystem::DIPSwitches::update(u32 index, string value) -> void {
|
||||
if(index >= settings.size()) return;
|
||||
auto& setting = settings[index];
|
||||
for(auto& option : setting.options) {
|
||||
if(option.name != value) continue;
|
||||
this->value = (this->value & ~setting.mask) | (option.value & setting.mask);
|
||||
return;
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -8,14 +8,15 @@ auto VsUniSystem::IO::read(n16 address, n8 data) -> n8 {
|
||||
data.bit(0) = readStream(address == 0x4017);
|
||||
data.bit(1) = 0;
|
||||
|
||||
n8 dips = vsUniSystem.dips.read("DSW0");
|
||||
if(address == 0x4016) {
|
||||
data.bit(2) = servicePulse.frames != 0;
|
||||
data.bit(3, 4) = vsUniSystem.dipSwitches.value.bit(0, 1);
|
||||
data.bit(3, 4) = dips.bit(0, 1);
|
||||
data.bit(5) = coinPulses[0].frames != 0;
|
||||
data.bit(6) = coinPulses[1].frames != 0;
|
||||
data.bit(7) = 0;
|
||||
} else {
|
||||
data.bit(2, 7) = vsUniSystem.dipSwitches.value.bit(2, 7);
|
||||
data.bit(2, 7) = dips.bit(2, 7);
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
@@ -6,10 +6,6 @@ auto VsUniSystem::Controls::serialize(serializer& s) -> void {
|
||||
if(input) input->serialize(s);
|
||||
}
|
||||
|
||||
auto VsUniSystem::DIPSwitches::serialize(serializer& s) -> void {
|
||||
s(value);
|
||||
}
|
||||
|
||||
auto VsUniSystem::IO::serialize(serializer& s) -> void {
|
||||
s(streams);
|
||||
s(positions);
|
||||
@@ -24,6 +20,5 @@ auto VsUniSystem::IO::serialize(serializer& s) -> void {
|
||||
|
||||
auto VsUniSystem::serialize(serializer& s) -> void {
|
||||
s(controls);
|
||||
s(dipSwitches);
|
||||
s(io);
|
||||
}
|
||||
@@ -7,7 +7,6 @@ VsUniSystem vsUniSystem;
|
||||
#include "standard-input.cpp"
|
||||
#include "zapper-input.cpp"
|
||||
#include "controls.cpp"
|
||||
#include "dip-switches.cpp"
|
||||
#include "io.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
@@ -17,7 +16,7 @@ auto VsUniSystem::load(Node::Object parent) -> void {
|
||||
}
|
||||
|
||||
auto VsUniSystem::unload() -> void {
|
||||
dipSwitches.unload();
|
||||
dips.unload();
|
||||
controls.unload();
|
||||
io.power();
|
||||
node.reset();
|
||||
@@ -29,10 +28,7 @@ auto VsUniSystem::power(bool reset) -> void {
|
||||
if(reset) return;
|
||||
|
||||
if(!controls.load(node)) return;
|
||||
if(!dipSwitches.load(parent)) {
|
||||
controls.unload();
|
||||
return;
|
||||
}
|
||||
dips.load(parent, cartridge.pak, {"DSW0"});
|
||||
}
|
||||
|
||||
auto VsUniSystem::frame() -> void {
|
||||
|
||||
@@ -67,37 +67,7 @@ struct VsUniSystem {
|
||||
std::unique_ptr<Input> input;
|
||||
} controls;
|
||||
|
||||
struct DIPSwitches {
|
||||
struct Option {
|
||||
string name;
|
||||
u8 value = 0;
|
||||
};
|
||||
|
||||
struct Setting {
|
||||
Node::Setting::String node;
|
||||
u8 mask = 0;
|
||||
std::vector<Option> options;
|
||||
};
|
||||
|
||||
Node::Object node;
|
||||
std::vector<Setting> settings;
|
||||
n8 value = 0;
|
||||
|
||||
auto load(Node::Object parent) -> bool;
|
||||
auto unload() -> void;
|
||||
auto update(u32 index, string value) -> void;
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
private:
|
||||
struct Definition {
|
||||
string name;
|
||||
u8 mask = 0;
|
||||
u8 defaultValue = 0;
|
||||
std::vector<Option> options;
|
||||
};
|
||||
|
||||
auto create(Node::Object parent, std::vector<Definition> definitions) -> void;
|
||||
} dipSwitches;
|
||||
Arcade::Dip dips;
|
||||
|
||||
struct IO {
|
||||
struct Pulse {
|
||||
|
||||
+12
-1
@@ -1,4 +1,4 @@
|
||||
ares_components(z80 sn76489 ym2413)
|
||||
ares_components(arcadedip z80 sn76489 ym2413 i8255)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
@@ -10,6 +10,8 @@ ares_add_sources(
|
||||
vdp/vdp.cpp
|
||||
psg/psg.cpp
|
||||
opll/opll.cpp
|
||||
systeme/ppi.cpp
|
||||
systeme/memory.cpp
|
||||
system/system.cpp
|
||||
cartridge/cartridge.cpp
|
||||
controller/controller.cpp
|
||||
@@ -48,6 +50,7 @@ ares_add_sources(
|
||||
cartridge/board/korea.cpp
|
||||
cartridge/board/pak4.cpp
|
||||
cartridge/board/sega.cpp
|
||||
cartridge/board/system-e.cpp
|
||||
cartridge/board/zemina.cpp
|
||||
)
|
||||
|
||||
@@ -151,6 +154,14 @@ ares_add_sources(
|
||||
opll/serialization.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
ms
|
||||
INCLUDED #
|
||||
systeme/ppi.hpp
|
||||
systeme/memory.hpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
ms
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
namespace Board {
|
||||
|
||||
#include "sega.cpp"
|
||||
#include "system-e.cpp"
|
||||
#include "codemasters.cpp"
|
||||
#include "korea.cpp"
|
||||
#include "zemina.cpp"
|
||||
|
||||
@@ -10,6 +10,7 @@ struct Interface {
|
||||
virtual auto unload() -> void {}
|
||||
virtual auto read(n16 address, n8 data) -> n8 { return data; }
|
||||
virtual auto write(n16 address, n8 data) -> void {}
|
||||
virtual auto out(n16 address, n8 data) -> void {}
|
||||
virtual auto power() -> void {}
|
||||
virtual auto serialize(serializer&) -> void {}
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
struct SystemE : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<n8> rom;
|
||||
n8 bank;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(rom, "program.rom");
|
||||
}
|
||||
|
||||
auto read(n16 address, n8 data) -> n8 override {
|
||||
if(address < 0x8000) return rom.read(address);
|
||||
if(address < 0xc000) return rom.read(0x10000 + ((u32)(bank & 15) << 14) + (address & 0x3fff));
|
||||
return data;
|
||||
}
|
||||
|
||||
auto write(n16 address, n8 data) -> void override {
|
||||
if(address < 0x8000 || address >= 0xc000) return;
|
||||
auto select = bank >> 5;
|
||||
auto& target = (select & 1) ? vdp2 : vdp;
|
||||
auto offset = (select & 1) ? ((select & 4) ? 0 : 0x4000) : ((select & 2) ? 0 : 0x4000);
|
||||
target.vram[offset | (address & 0x3fff)] = data;
|
||||
}
|
||||
|
||||
auto out(n16 address, n8 data) -> void override {
|
||||
if((address & 0xff) != 0xf7) return;
|
||||
bank = data;
|
||||
vdp2.vramBank = data.bit(7);
|
||||
vdp.vramBank = data.bit(6);
|
||||
}
|
||||
|
||||
auto power() -> void override { bank = 0; }
|
||||
auto serialize(serializer& s) -> void override { s(bank); }
|
||||
};
|
||||
@@ -19,7 +19,29 @@ auto Cartridge::connect() -> void {
|
||||
information.region = pak->attribute("region");
|
||||
information.board = pak->attribute("board");
|
||||
|
||||
cpu.encryption.reset();
|
||||
if(Model::SystemE()) {
|
||||
system.dips.load(system.node, pak, {"f2", "f3"});
|
||||
auto rotation = pak->attribute("rotation").natural();
|
||||
vdp.screen->setRotation(rotation ? 360 - rotation : 0); // MAME rotates clockwise; ares rotates counter-clockwise.
|
||||
ppi.game = pak->attribute("name");
|
||||
auto cpuName = pak->attribute("cpu");
|
||||
if(cpuName == "Sega 315-5177") cpu.encryption = std::make_unique<S315_5177>();
|
||||
if(cpuName == "MC-8123") {
|
||||
if(auto fp = pak->read("cpu.key")) {
|
||||
if(fp->size() == 0x2000) {
|
||||
auto chip = std::make_unique<MC8123>();
|
||||
// Fantasy Zone II encrypts only its fixed ROM.
|
||||
chip->encryptedSize = pak->attribute("name") == "fantzn2" ? 0x8000 : 0xc000;
|
||||
fp->read(chip->keyData, sizeof(chip->keyData));
|
||||
cpu.encryption = std::move(chip);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(information.board == "Sega") board = std::make_unique<Board::Sega>(*this);
|
||||
if(information.board == "sega/segae") board = std::make_unique<Board::SystemE>(*this);
|
||||
if(information.board == "Codemasters") board = std::make_unique<Board::Codemasters>(*this);
|
||||
if(information.board == "Korea") board = std::make_unique<Board::Korea>(*this);
|
||||
if(information.board == "Zemina") board = std::make_unique<Board::Zemina>(*this, 0); // Zemina made games
|
||||
@@ -43,6 +65,11 @@ auto Cartridge::connect() -> void {
|
||||
|
||||
auto Cartridge::disconnect() -> void {
|
||||
if(!node || !board) return;
|
||||
cpu.encryption.reset();
|
||||
if(Model::SystemE()) {
|
||||
system.dips.unload();
|
||||
ppi.game = {};
|
||||
}
|
||||
board->unload();
|
||||
board->pak.reset();
|
||||
board.reset();
|
||||
@@ -67,4 +94,8 @@ auto Cartridge::write(n16 address, n8 data) -> void {
|
||||
if(board) return board->write(address, data);
|
||||
}
|
||||
|
||||
auto Cartridge::out(n16 address, n8 data) -> void {
|
||||
if(board) board->out(address, data);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,6 +18,7 @@ struct Cartridge {
|
||||
|
||||
auto read(n16 address, n8 data) -> n8;
|
||||
auto write(n16 address, n8 data) -> void;
|
||||
auto out(n16 address, n8 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
+4
-2
@@ -8,7 +8,7 @@ CPU cpu;
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto CPU::load(Node::Object parent) -> void {
|
||||
ram.allocate(8_KiB);
|
||||
ram.allocate(Model::SystemE() ? 16_KiB : 8_KiB);
|
||||
|
||||
node = parent->append<Node::Object>("CPU");
|
||||
|
||||
@@ -56,7 +56,9 @@ auto CPU::setIRQ(bool value) -> void {
|
||||
auto CPU::power() -> void {
|
||||
Z80::bus = this;
|
||||
Z80::power();
|
||||
Thread::create(system.colorburst(), std::bind_front(&CPU::main, this));
|
||||
auto frequency = system.colorburst();
|
||||
if(Model::SystemE()) frequency *= 1.5;
|
||||
Thread::create(frequency, std::bind_front(&CPU::main, this));
|
||||
PC = 0x0000; //reset vector address
|
||||
SP = 0xfffd; //initial stack pointer location
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
struct CPU : Z80, Z80::Bus, Thread {
|
||||
Node::Object node;
|
||||
std::unique_ptr<SegaZ80> encryption;
|
||||
Memory::Writable<n8> ram; //8KB
|
||||
|
||||
struct Debugger {
|
||||
@@ -38,10 +39,13 @@ struct CPU : Z80, Z80::Bus, Thread {
|
||||
//memory.cpp
|
||||
auto mdr() const -> n8;
|
||||
auto read(n16 address) -> n8 override;
|
||||
auto fetch(n16 address) -> n8 override;
|
||||
auto write(n16 address, n8 data) -> void override;
|
||||
|
||||
auto in(n16 address) -> n8 override;
|
||||
auto out(n16 address, n8 data) -> void override;
|
||||
auto cheat(n16 address) -> maybe<n8>;
|
||||
auto readMapped(n16 address) -> n8;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
+24
-4
@@ -1,9 +1,26 @@
|
||||
auto CPU::read(n16 address) -> n8 {
|
||||
if(auto result = cheat(address)) return *result;
|
||||
n8 data = readMapped(address);
|
||||
return encryption ? encryption->decrypt(address, data, false) : data;
|
||||
}
|
||||
|
||||
auto CPU::fetch(n16 address) -> n8 {
|
||||
if(auto result = cheat(address)) return *result;
|
||||
n8 data = readMapped(address);
|
||||
return encryption ? encryption->decrypt(address, data, true) : data;
|
||||
}
|
||||
|
||||
auto CPU::mdr() const -> n8 {
|
||||
return (bus.mdr | bus.pullup) & ~bus.pulldown;
|
||||
}
|
||||
|
||||
auto CPU::read(n16 address) -> n8 {
|
||||
if(auto result = platform->cheat(address)) return *result;
|
||||
auto CPU::cheat(n16 address) -> maybe<n8> {
|
||||
if(auto result = platform->cheat(address)) return (n8)*result;
|
||||
return nothing;
|
||||
}
|
||||
|
||||
auto CPU::readMapped(n16 address) -> n8 {
|
||||
if(Model::SystemE()) return SystemE::memory.read(address);
|
||||
n8 data = mdr();
|
||||
if(address >= 0xc000 && bus.ramEnable) data = ram.read(address);
|
||||
if(Device::MasterSystem() && bus.biosEnable) data = bios.read(address, data);
|
||||
@@ -14,6 +31,7 @@ auto CPU::read(n16 address) -> n8 {
|
||||
}
|
||||
|
||||
auto CPU::write(n16 address, n8 data) -> void {
|
||||
if(Model::SystemE()) return SystemE::memory.write(address, data);
|
||||
bus.mdr = data;
|
||||
if(address >= 0xc000 && bus.ramEnable) ram.write(address, data);
|
||||
if(Device::MasterSystem() && bus.biosEnable) bios.write(address, data);
|
||||
@@ -26,6 +44,7 @@ auto CPU::write(n16 address, n8 data) -> void {
|
||||
//since I don't have explicit confirmation of this, I haven't implemented this yet.
|
||||
|
||||
auto CPU::in(n16 address) -> n8 {
|
||||
if(Model::SystemE()) return SystemE::memory.in(address);
|
||||
n8 data = mdr();
|
||||
if(0);
|
||||
|
||||
@@ -81,7 +100,7 @@ auto CPU::in(n16 address) -> n8 {
|
||||
data = mdr();
|
||||
}
|
||||
|
||||
else if((address & 0xc1) == 0x01) {
|
||||
else if((address & 0xc1) == 0x01 && Device::MasterSystem()) {
|
||||
if(bus.ioEnable && Display::CRT() && !Region::NTSCJ()) {
|
||||
data.bit(0) = controllerPort1.trDirection;
|
||||
data.bit(1) = controllerPort1.thDirection;
|
||||
@@ -174,6 +193,7 @@ auto CPU::in(n16 address) -> n8 {
|
||||
}
|
||||
|
||||
auto CPU::out(n16 address, n8 data) -> void {
|
||||
if(Model::SystemE()) return SystemE::memory.out(address, data);
|
||||
if(0);
|
||||
|
||||
else if((address & 0xff) == 0x00 && Display::LCD()) {
|
||||
@@ -219,7 +239,7 @@ auto CPU::out(n16 address, n8 data) -> void {
|
||||
bus.expansionEnable = !data.bit(7);
|
||||
}
|
||||
|
||||
else if((address & 0xc1) == 0x01) {
|
||||
else if((address & 0xc1) == 0x01 && Device::MasterSystem()) {
|
||||
auto thLevel1 = controllerPort1.thLevel;
|
||||
auto thLevel2 = controllerPort2.thLevel;
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <ms/vdp/vdp.cpp>
|
||||
#include <ms/psg/psg.cpp>
|
||||
#include <ms/opll/opll.cpp>
|
||||
#include <ms/systeme/ppi.cpp>
|
||||
#include <ms/systeme/memory.cpp>
|
||||
#include <ms/system/system.cpp>
|
||||
#include <ms/cartridge/cartridge.cpp>
|
||||
#include <ms/controller/controller.cpp>
|
||||
|
||||
+7
-1
@@ -4,7 +4,10 @@
|
||||
#include <ares/ares.hpp>
|
||||
#include <vector>
|
||||
|
||||
#include <component/processor/z80/z80.hpp>
|
||||
#include <component/processor/z80/sega/s315-5177.hpp>
|
||||
#include <component/processor/z80/sega/mc8123.hpp>
|
||||
#include <component/io/i8255/i8255.hpp>
|
||||
#include <component/arcade/dip-switches.hpp>
|
||||
#include <component/audio/sn76489/sn76489.hpp>
|
||||
#include <component/audio/ym2413/ym2413.hpp>
|
||||
|
||||
@@ -18,6 +21,7 @@ namespace ares::MasterSystem {
|
||||
inline static auto MasterSystemI() -> bool;
|
||||
inline static auto MasterSystemII() -> bool;
|
||||
inline static auto GameGear() -> bool;
|
||||
inline static auto SystemE() -> bool;
|
||||
};
|
||||
|
||||
struct Device {
|
||||
@@ -48,6 +52,8 @@ namespace ares::MasterSystem {
|
||||
#include <ms/vdp/vdp.hpp>
|
||||
#include <ms/psg/psg.hpp>
|
||||
#include <ms/opll/opll.hpp>
|
||||
#include <ms/systeme/ppi.hpp>
|
||||
#include <ms/systeme/memory.hpp>
|
||||
|
||||
#include <ms/system/system.hpp>
|
||||
#include <ms/cartridge/cartridge.hpp>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
auto System::Controls::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Object>("Controls");
|
||||
|
||||
if(Device::MasterSystem()) {
|
||||
if(Device::MasterSystem() && !MasterSystem::Model::SystemE()) {
|
||||
if(MasterSystem::Region::NTSCJ() == 1) rapid = node->append<Node::Input::Button>("Rapid");
|
||||
if(MasterSystem::Region::NTSCJ() == 0) reset = node->append<Node::Input::Button>("Reset");
|
||||
pause = node->append<Node::Input::Button>("Pause");
|
||||
@@ -19,7 +19,7 @@ auto System::Controls::load(Node::Object parent) -> void {
|
||||
}
|
||||
|
||||
auto System::Controls::poll() -> void {
|
||||
if(Device::MasterSystem()) {
|
||||
if(Device::MasterSystem() && !MasterSystem::Model::SystemE()) {
|
||||
if(MasterSystem::Region::NTSCJ() == 1) platform->input(rapid);
|
||||
if(MasterSystem::Region::NTSCJ() == 0) platform->input(reset);
|
||||
auto paused = pause->value();
|
||||
@@ -54,3 +54,65 @@ auto System::Controls::poll() -> void {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto System::ArcadeControls::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Object>("Controls");
|
||||
service = node->append<Node::Input::Button>("Service");
|
||||
for(u32 player : range(2)) {
|
||||
auto prefix = string{"Player ", player + 1, " "};
|
||||
coin[player] = node->append<Node::Input::Button>(string{prefix, "Coin"});
|
||||
start[player] = node->append<Node::Input::Button>(string{prefix, "Start"});
|
||||
up[player] = node->append<Node::Input::Button>(string{prefix, "Up"});
|
||||
down[player] = node->append<Node::Input::Button>(string{prefix, "Down"});
|
||||
left[player] = node->append<Node::Input::Button>(string{prefix, "Left"});
|
||||
right[player] = node->append<Node::Input::Button>(string{prefix, "Right"});
|
||||
button1[player] = node->append<Node::Input::Button>(string{prefix, "Button 1"});
|
||||
button2[player] = node->append<Node::Input::Button>(string{prefix, "Button 2"});
|
||||
}
|
||||
analogX = node->append<Node::Input::Axis>("Player 1 X-Axis");
|
||||
analogY = node->append<Node::Input::Axis>("Player 1 Y-Axis");
|
||||
accelerator = node->append<Node::Input::Axis>("Player 1 Accelerator");
|
||||
}
|
||||
|
||||
auto System::ArcadeControls::poll() -> void {
|
||||
platform->input(service);
|
||||
for(u32 player : range(2)) {
|
||||
platform->input(coin[player]);
|
||||
platform->input(start[player]);
|
||||
platform->input(up[player]);
|
||||
platform->input(down[player]);
|
||||
platform->input(left[player]);
|
||||
platform->input(right[player]);
|
||||
platform->input(button1[player]);
|
||||
platform->input(button2[player]);
|
||||
}
|
||||
platform->input(analogX);
|
||||
platform->input(analogY);
|
||||
platform->input(accelerator);
|
||||
if(ppi.game == "ridleofp" || ppi.game == "megrescu") {
|
||||
ppi.x += (i32)analogX->value() >> 8;
|
||||
ppi.y += (i32)analogY->value() >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
auto System::ArcadeControls::coinPort() const -> n8 {
|
||||
n8 data = 0xff;
|
||||
data.bit(0) = !coin[0]->value();
|
||||
data.bit(1) = !coin[1]->value();
|
||||
data.bit(3) = !service->value();
|
||||
if(cartridge.pak && cartridge.pak->attribute("name") == "hangonjr") data.bit(4) = !start[0]->value();
|
||||
else data.bit(6) = !start[0]->value();
|
||||
data.bit(7) = !start[1]->value();
|
||||
return data;
|
||||
}
|
||||
|
||||
auto System::ArcadeControls::playerPort(u32 player) const -> n8 {
|
||||
n8 data = 0xff;
|
||||
data.bit(0) = !up[player]->value();
|
||||
data.bit(1) = !down[player]->value();
|
||||
data.bit(2) = !left[player]->value();
|
||||
data.bit(3) = !right[player]->value();
|
||||
data.bit(4) = !button1[player]->value();
|
||||
data.bit(5) = !button2[player]->value();
|
||||
return data;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
static const string SerializerVersion = "v131";
|
||||
static const string SerializerVersion = "v132";
|
||||
|
||||
auto System::serialize(bool synchronize) -> serializer {
|
||||
if(synchronize) scheduler.enter(Scheduler::Mode::Synchronize);
|
||||
@@ -43,9 +43,13 @@ auto System::serialize(serializer& s, bool synchronize) -> void {
|
||||
s(cartridge);
|
||||
s(cpu);
|
||||
s(vdp);
|
||||
if(MasterSystem::Model::SystemE()) s(vdp2);
|
||||
s(psg);
|
||||
s(controllerPort1);
|
||||
s(controllerPort2);
|
||||
if(MasterSystem::Model::SystemE()) s(ppi);
|
||||
if(Device::MasterSystem() && !MasterSystem::Model::SystemE()) {
|
||||
s(controllerPort1);
|
||||
s(controllerPort2);
|
||||
}
|
||||
if(Device::MasterSystem()) {
|
||||
if(MasterSystem::Region::NTSCJ()) {
|
||||
if(MasterSystem::Model::MarkIII()) {
|
||||
|
||||
+33
-11
@@ -13,6 +13,7 @@ auto enumerate() -> std::vector<string> {
|
||||
"[Sega] Master System II (PAL)",
|
||||
"[Sega] Game Gear (NTSC-J)",
|
||||
"[Sega] Game Gear (NTSC-U)",
|
||||
"[Sega] System E",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -43,7 +44,8 @@ auto System::game() -> string {
|
||||
|
||||
auto System::run() -> void {
|
||||
scheduler.enter();
|
||||
controls.poll();
|
||||
if(MasterSystem::Model::SystemE()) arcadeControls.poll();
|
||||
else controls.poll();
|
||||
}
|
||||
|
||||
auto System::load(Node::System& root, string name) -> bool {
|
||||
@@ -66,6 +68,12 @@ auto System::load(Node::System& root, string name) -> bool {
|
||||
information.name = "Game Gear";
|
||||
information.model = Model::GameGear;
|
||||
}
|
||||
if(name == "[Sega] System E") {
|
||||
information.name = "System E";
|
||||
information.model = Model::SystemE;
|
||||
information.region = Region::NTSCU;
|
||||
information.colorburst = Constants::Colorburst::NTSC;
|
||||
}
|
||||
if(name.find("NTSC-J")) {
|
||||
information.region = Region::NTSCJ;
|
||||
information.colorburst = Constants::Colorburst::NTSC;
|
||||
@@ -92,14 +100,21 @@ auto System::load(Node::System& root, string name) -> bool {
|
||||
if(!node->setPak(pak = platform->pak(node))) return false;
|
||||
|
||||
scheduler.reset();
|
||||
controls.load(node);
|
||||
bios.load(node);
|
||||
if(MasterSystem::Model::SystemE()) arcadeControls.load(node->append<Node::Object>("Arcade"));
|
||||
else controls.load(node);
|
||||
if(!MasterSystem::Model::SystemE()) bios.load(node);
|
||||
cartridgeSlot.load(node);
|
||||
cpu.load(node);
|
||||
vdp.load(node);
|
||||
if(MasterSystem::Model::SystemE()) {
|
||||
vdp2.backLayer = true;
|
||||
vdp2.load(node);
|
||||
}
|
||||
psg.load(node);
|
||||
controllerPort1.load(node);
|
||||
controllerPort2.load(node);
|
||||
if(Device::MasterSystem() && !MasterSystem::Model::SystemE()) {
|
||||
controllerPort1.load(node);
|
||||
controllerPort2.load(node);
|
||||
}
|
||||
if(Device::MasterSystem()) {
|
||||
if(MasterSystem::Region::NTSCJ()) {
|
||||
if(MasterSystem::Model::MarkIII()) {
|
||||
@@ -127,9 +142,12 @@ auto System::unload() -> void {
|
||||
cartridgeSlot.unload();
|
||||
cpu.unload();
|
||||
vdp.unload();
|
||||
if(MasterSystem::Model::SystemE()) vdp2.unload();
|
||||
psg.unload();
|
||||
controllerPort1.unload();
|
||||
controllerPort2.unload();
|
||||
if(Device::MasterSystem() && !MasterSystem::Model::SystemE()) {
|
||||
controllerPort1.unload();
|
||||
controllerPort2.unload();
|
||||
}
|
||||
if(Device::MasterSystem()) {
|
||||
if(MasterSystem::Region::NTSCJ()) {
|
||||
if(MasterSystem::Model::MarkIII()) {
|
||||
@@ -143,7 +161,7 @@ auto System::unload() -> void {
|
||||
}
|
||||
}
|
||||
}
|
||||
bios.unload();
|
||||
if(!MasterSystem::Model::SystemE()) bios.unload();
|
||||
node.reset();
|
||||
}
|
||||
|
||||
@@ -154,13 +172,17 @@ auto System::power(bool reset) -> void {
|
||||
information.ms = cartridge.pak->attribute("ms").boolean();
|
||||
}
|
||||
|
||||
bios.power();
|
||||
if(!MasterSystem::Model::SystemE()) bios.power();
|
||||
cartridge.power();
|
||||
cpu.power();
|
||||
vdp.power();
|
||||
if(MasterSystem::Model::SystemE()) vdp2.power();
|
||||
psg.power();
|
||||
controllerPort1.power();
|
||||
controllerPort2.power();
|
||||
if(MasterSystem::Model::SystemE()) ppi.power();
|
||||
if(Device::MasterSystem() && !MasterSystem::Model::SystemE()) {
|
||||
controllerPort1.power();
|
||||
controllerPort2.power();
|
||||
}
|
||||
if(Device::MasterSystem()) {
|
||||
if(MasterSystem::Region::NTSCJ()) {
|
||||
if(MasterSystem::Model::MarkIII()) {
|
||||
|
||||
@@ -17,6 +17,7 @@ struct BIOS {
|
||||
struct System {
|
||||
Node::System node;
|
||||
VFS::Pak pak;
|
||||
Arcade::Dip dips;
|
||||
|
||||
struct Controls {
|
||||
Node::Object node;
|
||||
@@ -46,7 +47,18 @@ struct System {
|
||||
n1 rightLatch;
|
||||
} controls;
|
||||
|
||||
enum class Model : u32 { MarkIII, MasterSystemI, MasterSystemII, GameGear };
|
||||
struct ArcadeControls {
|
||||
Node::Object node;
|
||||
Node::Input::Button coin[2], start[2], service;
|
||||
Node::Input::Button up[2], down[2], left[2], right[2], button1[2], button2[2];
|
||||
Node::Input::Axis analogX, analogY, accelerator;
|
||||
auto load(Node::Object) -> void;
|
||||
auto poll() -> void;
|
||||
auto coinPort() const -> n8;
|
||||
auto playerPort(u32 player) const -> n8;
|
||||
} arcadeControls;
|
||||
|
||||
enum class Model : u32 { MarkIII, MasterSystemI, MasterSystemII, GameGear, SystemE };
|
||||
enum class Region : u32 { NTSCJ, NTSCU, PAL };
|
||||
|
||||
auto name() const -> string { return information.name; }
|
||||
@@ -88,6 +100,7 @@ auto Model::MarkIII() -> bool { return system.model() == System::Model::MarkIII;
|
||||
auto Model::MasterSystemI() -> bool { return system.model() == System::Model::MasterSystemI; }
|
||||
auto Model::MasterSystemII() -> bool { return system.model() == System::Model::MasterSystemII; }
|
||||
auto Model::GameGear() -> bool { return system.model() == System::Model::GameGear; }
|
||||
auto Model::SystemE() -> bool { return system.model() == System::Model::SystemE; }
|
||||
|
||||
auto Device::MasterSystem() -> bool { return system.model() != System::Model::GameGear; }
|
||||
auto Device::GameGear() -> bool { return system.model() == System::Model::GameGear; }
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
#include <ms/ms.hpp>
|
||||
|
||||
namespace ares::MasterSystem::SystemE {
|
||||
|
||||
Memory memory;
|
||||
|
||||
auto Memory::read(n16 address) -> n8 {
|
||||
if(address < 0xc000) return cartridge.read(address, 0xff);
|
||||
return cpu.ram.read(address);
|
||||
}
|
||||
|
||||
auto Memory::write(n16 address, n8 data) -> void {
|
||||
if(address >= 0xc000) cpu.ram.write(address, data);
|
||||
else if(address >= 0x8000) cartridge.write(address, data);
|
||||
}
|
||||
|
||||
auto Memory::in(n16 address) -> n8 {
|
||||
n8 data = 0xff;
|
||||
switch(address & 0xff) {
|
||||
case 0x7e: data = vdp2.vcounterQuery(); break;
|
||||
case 0x7f: data = vdp.hcounterQuery(); break;
|
||||
case 0xba: data = vdp2.data(); break;
|
||||
case 0xbb: data = vdp2.status(); break;
|
||||
case 0xbe: data = vdp.data(); break;
|
||||
case 0xbf: data = vdp.status(); break;
|
||||
case 0xe0: data = system.arcadeControls.coinPort(); break;
|
||||
case 0xe1: data = system.arcadeControls.playerPort(0); break;
|
||||
case 0xe2: data = system.arcadeControls.playerPort(1); break;
|
||||
case 0xf2: data = system.dips.read("f2"); break;
|
||||
case 0xf3: data = system.dips.read("f3"); break;
|
||||
case 0xf8: case 0xf9: case 0xfa: case 0xfb:
|
||||
data = ppi.read(address & 3);
|
||||
break;
|
||||
}
|
||||
cpu.debugger.in(address, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
auto Memory::out(n16 address, n8 data) -> void {
|
||||
switch(address & 0xff) {
|
||||
case 0x7b: case 0x7e: case 0x7f: psg.write(data); break;
|
||||
case 0xba: vdp2.data(data); break;
|
||||
case 0xbb: vdp2.control(data); break;
|
||||
case 0xbe: vdp.data(data); break;
|
||||
case 0xbf: vdp.control(data); break;
|
||||
case 0xf7:
|
||||
cartridge.out(address, data);
|
||||
break;
|
||||
case 0xf8: case 0xf9: case 0xfa: case 0xfb:
|
||||
ppi.write(address & 3, data);
|
||||
break;
|
||||
}
|
||||
cpu.debugger.out(address, data);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace SystemE {
|
||||
|
||||
struct Memory {
|
||||
auto read(n16 address) -> n8;
|
||||
auto write(n16 address, n8 data) -> void;
|
||||
auto in(n16 address) -> n8;
|
||||
auto out(n16 address, n8 data) -> void;
|
||||
};
|
||||
|
||||
extern Memory memory;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#include <ms/ms.hpp>
|
||||
|
||||
namespace ares::MasterSystem {
|
||||
|
||||
PPI ppi;
|
||||
|
||||
auto PPI::power() -> void {
|
||||
I8255::power();
|
||||
control = 0xff;
|
||||
x = y = startX = startY = latchX = latchY = 0;
|
||||
switches = 0;
|
||||
}
|
||||
|
||||
auto PPI::readPortA() -> n8 {
|
||||
if(game == "hangonjr") {
|
||||
if(control.bit(0)) {
|
||||
return std::clamp((i32)system.arcadeControls.accelerator->value() >> 7, 0, 255);
|
||||
}
|
||||
auto angle = ((i32)system.arcadeControls.analogX->value() + 32768) >> 8;
|
||||
if(angle < 0x20) angle = 0x20;
|
||||
if(angle > 0xe0) angle = 0xe0;
|
||||
return angle;
|
||||
}
|
||||
|
||||
if(game == "ridleofp" || game == "megrescu") {
|
||||
if(control.bit(4)) return 0xff;
|
||||
u16 value = control.bit(3) ? latchY : latchX;
|
||||
value |= (u16)switches << 12;
|
||||
return control.bit(2) ? value >> 8 : value & 0xff;
|
||||
}
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto PPI::readPortC() -> n8 {
|
||||
if(game == "hangonjr") return 0xef;
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto PPI::writePortC(n8 data) -> void {
|
||||
if((game == "ridleofp" || game == "megrescu") && control.bit(4) && !data.bit(4)) {
|
||||
latchX = (x - startX) & 0x0fff;
|
||||
latchY = (y - startY) & 0x0fff;
|
||||
switches = 0;
|
||||
if(system.arcadeControls.button1[0]->value()) switches |= 4;
|
||||
if(system.arcadeControls.button2[0]->value()) switches |= 1;
|
||||
if(switches) switches |= 8;
|
||||
}
|
||||
if(!control.bit(1) && data.bit(1)) startX = x;
|
||||
if(!control.bit(0) && data.bit(0)) startY = y;
|
||||
control = data;
|
||||
}
|
||||
|
||||
auto PPI::serialize(serializer& s) -> void {
|
||||
I8255::serialize(s);
|
||||
s(control);
|
||||
s(x); s(y); s(startX); s(startY); s(latchX); s(latchY);
|
||||
s(switches);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
struct PPI : I8255 {
|
||||
string game;
|
||||
n8 control;
|
||||
u16 x, y, startX, startY, latchX, latchY;
|
||||
n4 switches;
|
||||
|
||||
auto power() -> void;
|
||||
auto readPortA() -> n8 override;
|
||||
auto readPortC() -> n8 override;
|
||||
auto writePortC(n8 data) -> void override;
|
||||
auto serialize(serializer&) -> void;
|
||||
};
|
||||
|
||||
extern PPI ppi;
|
||||
+12
-12
@@ -30,7 +30,7 @@ auto VDP::Background::graphics1(n8 hoffset, n9 voffset) -> void {
|
||||
nameTableAddress.bit( 0, 4) = hoffset.bit(3,7);
|
||||
nameTableAddress.bit( 5, 9) = voffset.bit(3,7);
|
||||
nameTableAddress.bit(10,13) = io.nameTableAddress;
|
||||
n8 pattern = self.vram[nameTableAddress];
|
||||
n8 pattern = self.readVRAM(nameTableAddress);
|
||||
|
||||
n14 patternAddress;
|
||||
patternAddress.bit( 0, 2) = voffset.bit(0,2);
|
||||
@@ -41,9 +41,9 @@ auto VDP::Background::graphics1(n8 hoffset, n9 voffset) -> void {
|
||||
colorAddress.bit(0, 4) = pattern.bit(3,7);
|
||||
colorAddress.bit(6,13) = io.colorTableAddress;
|
||||
|
||||
n8 color = self.vram[colorAddress];
|
||||
n8 color = self.readVRAM(colorAddress);
|
||||
n3 index = hoffset ^ 7;
|
||||
if(!self.vram[patternAddress].bit(index)) {
|
||||
if(!self.readVRAM(patternAddress).bit(index)) {
|
||||
output.color = color.bit(0,3);
|
||||
} else {
|
||||
output.color = color.bit(4,7);
|
||||
@@ -55,7 +55,7 @@ auto VDP::Background::graphics2(n8 hoffset, n9 voffset) -> void {
|
||||
nameTableAddress.bit( 0, 4) = hoffset.bit(3,7);
|
||||
nameTableAddress.bit( 5, 9) = voffset.bit(3,7);
|
||||
nameTableAddress.bit(10,13) = io.nameTableAddress;
|
||||
n8 pattern = self.vram[nameTableAddress];
|
||||
n8 pattern = self.readVRAM(nameTableAddress);
|
||||
|
||||
n14 patternAddress;
|
||||
patternAddress.bit(0, 2) = voffset.bit(0,2);
|
||||
@@ -67,9 +67,9 @@ auto VDP::Background::graphics2(n8 hoffset, n9 voffset) -> void {
|
||||
colorAddress.bit(13) = io.colorTableAddress.bit(7);
|
||||
|
||||
n8 colorMask = io.colorTableAddress.bit(0,6) << 1 | 1;
|
||||
n8 color = self.vram[colorAddress];
|
||||
n8 color = self.readVRAM(colorAddress);
|
||||
n3 index = hoffset ^ 7;
|
||||
if(!self.vram[patternAddress].bit(index)) {
|
||||
if(!self.readVRAM(patternAddress).bit(index)) {
|
||||
output.color = color.bit(0,3);
|
||||
} else {
|
||||
output.color = color.bit(4,7);
|
||||
@@ -100,8 +100,8 @@ auto VDP::Background::graphics3(n8 hoffset, n9 voffset, u32 vlines) -> void {
|
||||
}
|
||||
|
||||
n16 pattern;
|
||||
pattern.byte(0) = self.vram[nameTableAddress | 0];
|
||||
pattern.byte(1) = self.vram[nameTableAddress | 1];
|
||||
pattern.byte(0) = self.readVRAM(nameTableAddress | 0);
|
||||
pattern.byte(1) = self.readVRAM(nameTableAddress | 1);
|
||||
|
||||
if(pattern.bit( 9)) hoffset ^= 7; //hflip
|
||||
if(pattern.bit(10)) voffset ^= 7; //vflip
|
||||
@@ -113,10 +113,10 @@ auto VDP::Background::graphics3(n8 hoffset, n9 voffset, u32 vlines) -> void {
|
||||
patternAddress.bit(5,13) = pattern.bit(0,8);
|
||||
|
||||
n3 index = hoffset ^ 7;
|
||||
output.color.bit(0) = self.vram[patternAddress | 0].bit(index);
|
||||
output.color.bit(1) = self.vram[patternAddress | 1].bit(index);
|
||||
output.color.bit(2) = self.vram[patternAddress | 2].bit(index);
|
||||
output.color.bit(3) = self.vram[patternAddress | 3].bit(index);
|
||||
output.color.bit(0) = self.readVRAM(patternAddress | 0).bit(index);
|
||||
output.color.bit(1) = self.readVRAM(patternAddress | 1).bit(index);
|
||||
output.color.bit(2) = self.readVRAM(patternAddress | 2).bit(index);
|
||||
output.color.bit(3) = self.readVRAM(patternAddress | 3).bit(index);
|
||||
|
||||
if(output.color == 0) output.priority = 0;
|
||||
}
|
||||
|
||||
+6
-1
@@ -1,7 +1,7 @@
|
||||
auto VDP::DAC::setup(n9 y) -> void {
|
||||
auto offset = Region::PAL() ? 48 : 27;
|
||||
y = (y + offset) % vdp.screenHeight();
|
||||
output = self.screen->pixels().data() + y * 284;
|
||||
output = self.backLayer ? self.backPixels : self.screen->pixels().data() + y * 284;
|
||||
|
||||
for (u32 x : range(284)) output[x] = palette(16 | io.backdropColor);
|
||||
}
|
||||
@@ -18,6 +18,11 @@ auto VDP::DAC::run(n8 x, n8 y) -> void {
|
||||
}
|
||||
}
|
||||
|
||||
if(Model::SystemE() && !self.backLayer) {
|
||||
bool opaque = self.displayEnable() && (!io.leftClip || x >= 8)
|
||||
&& (self.sprite.output.color || self.background.output.color);
|
||||
if(!opaque) color = vdp2.backPixels[(x + 13) % 284];
|
||||
}
|
||||
output[(x + 13) % 284] = color;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
auto VDP::Debugger::load(Node::Object parent) -> void {
|
||||
memory.vram = parent->append<Node::Debugger::Memory>("VDP VRAM");
|
||||
memory.vram->setSize(vdp.vram.size());
|
||||
memory.vram->setSize(self.vram.size());
|
||||
memory.vram->setRead([&](u32 address) -> u8 {
|
||||
return vdp.vram[address];
|
||||
return self.vram[address];
|
||||
});
|
||||
memory.vram->setWrite([&](u32 address, u8 data) -> void {
|
||||
vdp.vram[address] = data;
|
||||
self.vram[address] = data;
|
||||
});
|
||||
|
||||
memory.cram = parent->append<Node::Debugger::Memory>("VDP CRAM");
|
||||
memory.cram->setSize(vdp.cram.size());
|
||||
memory.cram->setSize(self.cram.size());
|
||||
memory.cram->setRead([&](u32 address) -> u8 {
|
||||
return vdp.cram[address];
|
||||
return self.cram[address];
|
||||
});
|
||||
memory.cram->setWrite([&](u32 address, u8 data) -> void {
|
||||
vdp.cram[address] = data;
|
||||
self.cram[address] = data;
|
||||
});
|
||||
|
||||
tracer.io = parent->append<Node::Debugger::Tracer::Notification>("I/O", "VDP");
|
||||
@@ -42,7 +42,7 @@ auto VDP::Debugger::io(n4 register, n8 data) -> void {
|
||||
};
|
||||
tracer.io->notify({
|
||||
"$", hex(register, 2L), " = #$", hex(data, 2L),
|
||||
" @ ", pad(vdp.vcounter(), 3L), ",", pad(vdp.hcounter(), 3L),
|
||||
" @ ", pad(self.vcounter(), 3L), ",", pad(self.hcounter(), 3L),
|
||||
" ", name[register]
|
||||
});
|
||||
}
|
||||
|
||||
+3
-3
@@ -36,7 +36,7 @@ auto VDP::data() -> n8 {
|
||||
latch.control = 0;
|
||||
|
||||
auto data = latch.vram;
|
||||
latch.vram = vram[io.address++];
|
||||
latch.vram = readVRAM(io.address++);
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ auto VDP::data(n8 data) -> void {
|
||||
latch.vram = data;
|
||||
|
||||
if(io.code <= 2) {
|
||||
vram[io.address] = data;
|
||||
writeVRAM(io.address, data);
|
||||
} else {
|
||||
if(Mode::MasterSystem()) {
|
||||
//writes immediate store 6-bits into CRAM
|
||||
@@ -96,7 +96,7 @@ auto VDP::control(n8 data) -> void {
|
||||
io.code.bit(0,1) = data.bit(6,7);
|
||||
|
||||
if(io.code == 0) {
|
||||
latch.vram = vram[io.address++];
|
||||
latch.vram = readVRAM(io.address++);
|
||||
}
|
||||
|
||||
if(io.code == 2) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
auto VDP::IRQ::poll() -> void {
|
||||
if(self.backLayer) return;
|
||||
if(line.pending && line.enable) return cpu.setIRQ(1);
|
||||
if(frame.pending && frame.enable) return cpu.setIRQ(1);
|
||||
return cpu.setIRQ(0);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
auto VDP::serialize(serializer& s) -> void {
|
||||
Thread::serialize(s);
|
||||
s(vram);
|
||||
s(vramBank);
|
||||
s(cram);
|
||||
s(background);
|
||||
s(sprite);
|
||||
|
||||
+12
-12
@@ -7,13 +7,13 @@ auto VDP::Sprite::setup(n9 voffset) -> void {
|
||||
n14 attributeAddress;
|
||||
attributeAddress.bit(7,13) = io.attributeTableAddress;
|
||||
for(u32 index : range(32)) {
|
||||
i9 y = self.vram[attributeAddress++];
|
||||
i9 y = self.readVRAM(attributeAddress++);
|
||||
if(y == 0xd0) break;
|
||||
if(y >= 0xe0) y -= 0x100;
|
||||
|
||||
i9 x = self.vram[attributeAddress++];
|
||||
n8 pattern = self.vram[attributeAddress++];
|
||||
n8 extra = self.vram[attributeAddress++];
|
||||
i9 x = self.readVRAM(attributeAddress++);
|
||||
n8 pattern = self.readVRAM(attributeAddress++);
|
||||
n8 extra = self.readVRAM(attributeAddress++);
|
||||
|
||||
if(extra.bit(7)) x -= 32;
|
||||
y += 1;
|
||||
@@ -35,12 +35,12 @@ auto VDP::Sprite::setup(n9 voffset) -> void {
|
||||
attributeAddress.bit(8,13) = io.attributeTableAddress.bit(1,6);
|
||||
|
||||
for(u32 index : range(64)) {
|
||||
i9 y = self.vram[attributeAddress + index];
|
||||
i9 y = self.readVRAM(attributeAddress + index);
|
||||
if(self.vlines() == 192 && y == 0xd0) break;
|
||||
if(y >= 0xf0) y -= 0x100;
|
||||
|
||||
i9 x = self.vram[attributeAddress + 0x80 + (index << 1)];
|
||||
n8 pattern = self.vram[attributeAddress + 0x81 + (index << 1)];
|
||||
i9 x = self.readVRAM(attributeAddress + 0x80 + (index << 1));
|
||||
n8 pattern = self.readVRAM(attributeAddress + 0x81 + (index << 1));
|
||||
|
||||
if(io.shift) x -= 8;
|
||||
y += 1;
|
||||
@@ -102,7 +102,7 @@ auto VDP::Sprite::graphics2(n8 hoffset, n9 voffset) -> void {
|
||||
address.bit(11,13) = io.patternTableAddress;
|
||||
|
||||
n3 index = x ^ 7;
|
||||
if(self.vram[address].bit(index)) {
|
||||
if(self.readVRAM(address).bit(index)) {
|
||||
if(output.color && self.displayEnable()) { io.collision = 1; break; }
|
||||
output.color = o.color;
|
||||
}
|
||||
@@ -126,10 +126,10 @@ auto VDP::Sprite::graphics3(n8 hoffset, n9 voffset, u32 vlines) -> void {
|
||||
|
||||
n3 index = x ^ 7;
|
||||
n4 color;
|
||||
color.bit(0) = self.vram[address | 0].bit(index);
|
||||
color.bit(1) = self.vram[address | 1].bit(index);
|
||||
color.bit(2) = self.vram[address | 2].bit(index);
|
||||
color.bit(3) = self.vram[address | 3].bit(index);
|
||||
color.bit(0) = self.readVRAM(address | 0).bit(index);
|
||||
color.bit(1) = self.readVRAM(address | 1).bit(index);
|
||||
color.bit(2) = self.readVRAM(address | 2).bit(index);
|
||||
color.bit(3) = self.readVRAM(address | 3).bit(index);
|
||||
if(color == 0) continue;
|
||||
|
||||
if(output.color && self.displayEnable()) { io.collision = 1; break; }
|
||||
|
||||
+45
-9
@@ -3,6 +3,7 @@
|
||||
namespace ares::MasterSystem {
|
||||
|
||||
VDP vdp;
|
||||
VDP vdp2;
|
||||
#include "io.cpp"
|
||||
#include "irq.cpp"
|
||||
#include "background.cpp"
|
||||
@@ -13,17 +14,25 @@ VDP vdp;
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto VDP::load(Node::Object parent) -> void {
|
||||
vram.allocate(16_KiB);
|
||||
vram.allocate(Model::SystemE() ? 32_KiB : 16_KiB);
|
||||
cram.allocate(32);
|
||||
|
||||
node = parent->append<Node::Object>("VDP");
|
||||
node = parent->append<Node::Object>(backLayer ? "Back VDP" : "VDP");
|
||||
|
||||
if(backLayer) {
|
||||
revision = node->append<Node::Setting::Natural>("Revision", 1);
|
||||
revision->setAllowedValues({1});
|
||||
debugger.load(node);
|
||||
return;
|
||||
}
|
||||
|
||||
screen = node->append<Node::Video::Screen>("Screen", 284, screenHeight());
|
||||
|
||||
u32 defaultRevision = 2;
|
||||
if((Model::MarkIII() || Model::MasterSystemI()) && Region::NTSCJ()) defaultRevision = 1;
|
||||
if(((Model::MarkIII() || Model::MasterSystemI()) && Region::NTSCJ()) || Model::SystemE()) defaultRevision = 1;
|
||||
revision = node->append<Node::Setting::Natural>("Revision", defaultRevision);
|
||||
revision->setAllowedValues({1, 2});
|
||||
if(Model::SystemE()) revision->setAllowedValues({1});
|
||||
else revision->setAllowedValues({1, 2});
|
||||
screen->refreshRateHint(system.colorburst() * 15, 3420, Region::PAL() ? 313 : 262);
|
||||
|
||||
if(Display::CRT()) {
|
||||
@@ -55,11 +64,12 @@ auto VDP::load(Node::Object parent) -> void {
|
||||
}
|
||||
|
||||
auto VDP::unload() -> void {
|
||||
debugger = {};
|
||||
debugger.memory = {};
|
||||
debugger.tracer = {};
|
||||
interframeBlending.reset();
|
||||
revision.reset();
|
||||
screen->quit();
|
||||
node->remove(screen);
|
||||
if(screen) screen->quit();
|
||||
if(screen) node->remove(screen);
|
||||
screen.reset();
|
||||
node.reset();
|
||||
vram.reset();
|
||||
@@ -84,11 +94,34 @@ auto VDP::main() -> void {
|
||||
|
||||
//684 clocks/scanline
|
||||
dac.setup(io.vcounter);
|
||||
if(Model::SystemE()) {
|
||||
vdp2.io.vcounter = io.vcounter;
|
||||
vdp2.io.hcounter = io.hcounter;
|
||||
if(vdp2.io.vcounter <= vdp2.vlines()) {
|
||||
if(vdp2.irq.line.counter-- == 0) {
|
||||
vdp2.irq.line.counter = vdp2.irq.line.coincidence;
|
||||
vdp2.irq.line.pending = 1;
|
||||
}
|
||||
} else {
|
||||
vdp2.irq.line.counter = vdp2.irq.line.coincidence;
|
||||
}
|
||||
if(vdp2.io.vcounter == vdp2.vlines() + 1) vdp2.irq.frame.pending = 1;
|
||||
vdp2.dac.setup(io.vcounter);
|
||||
}
|
||||
if(io.vcounter < vlines()) {
|
||||
u8 y = io.vcounter;
|
||||
background.setup(y);
|
||||
sprite.setup(y);
|
||||
if(Model::SystemE()) {
|
||||
vdp2.background.setup(y);
|
||||
vdp2.sprite.setup(y);
|
||||
}
|
||||
for(u8 x : range(256)) {
|
||||
if(Model::SystemE()) {
|
||||
vdp2.background.run(x, y);
|
||||
vdp2.sprite.run(x, y);
|
||||
vdp2.dac.run(x, y);
|
||||
}
|
||||
background.run(x, y);
|
||||
sprite.run(x, y);
|
||||
dac.run(x, y);
|
||||
@@ -164,8 +197,10 @@ auto VDP::vblank() -> bool {
|
||||
}
|
||||
|
||||
auto VDP::power() -> void {
|
||||
Thread::create(system.colorburst() * 15.0 / 5.0, std::bind_front(&VDP::main, this));
|
||||
screen->power();
|
||||
if(!backLayer) {
|
||||
Thread::create(system.colorburst() * 15.0 / 5.0, std::bind_front(&VDP::main, this));
|
||||
screen->power();
|
||||
}
|
||||
|
||||
for(auto& byte : vram) byte = 0x00;
|
||||
for(auto& byte : cram) byte = 0x00;
|
||||
@@ -176,6 +211,7 @@ auto VDP::power() -> void {
|
||||
irq.power();
|
||||
io = {};
|
||||
latch = {};
|
||||
vramBank = 0;
|
||||
}
|
||||
|
||||
}
|
||||
+9
-2
@@ -8,10 +8,16 @@ struct VDP : Thread {
|
||||
Node::Setting::Boolean colorEmulation;
|
||||
Node::Setting::Natural revision;
|
||||
Node::Setting::Boolean interframeBlending; //Game Gear only
|
||||
Memory::Writable<n8 > vram; //16KB
|
||||
Memory::Writable<n8 > vram; //16KB; 32KB on System E
|
||||
n1 vramBank;
|
||||
bool backLayer = false;
|
||||
u32 backPixels[284] = {};
|
||||
auto readVRAM(n14 address) const -> n8 { return vram[(Model::SystemE() ? (u32)vramBank << 14 : 0) | address]; }
|
||||
auto writeVRAM(n14 address, n8 data) -> void { vram[(Model::SystemE() ? (u32)vramBank << 14 : 0) | address] = data; }
|
||||
Memory::Writable<n12> cram; //Master System = 6-bit; Game Gear = 12-bit
|
||||
|
||||
struct Debugger {
|
||||
VDP& self;
|
||||
//debugger.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto io(n4 register, n8 data) -> void;
|
||||
@@ -24,7 +30,7 @@ struct VDP : Thread {
|
||||
struct Tracer {
|
||||
Node::Debugger::Tracer::Notification io;
|
||||
} tracer;
|
||||
} debugger;
|
||||
} debugger{*this};
|
||||
|
||||
auto displayEnable() const -> n1 { return io.displayEnable; }
|
||||
auto videoMode() const -> n4 { return io.videoMode; }
|
||||
@@ -207,3 +213,4 @@ private:
|
||||
};
|
||||
|
||||
extern VDP vdp;
|
||||
extern VDP vdp2;
|
||||
@@ -1,4 +1,4 @@
|
||||
ares_components(sm5k)
|
||||
ares_components(sm5k arcadedip)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
@@ -67,12 +67,6 @@ ares_add_sources(
|
||||
aleck64/io.cpp
|
||||
aleck64/vdp.cpp
|
||||
aleck64/game-config/11beat.hpp
|
||||
aleck64/game-config/doncdoon.hpp
|
||||
aleck64/game-config/kurufev.hpp
|
||||
aleck64/game-config/mayjin3.hpp
|
||||
aleck64/game-config/starsldr.hpp
|
||||
aleck64/game-config/twrshaft.hpp
|
||||
aleck64/game-config/vivdolls.hpp
|
||||
aleck64/game-config/hipai.hpp
|
||||
aleck64/game-config/hipai2.hpp
|
||||
aleck64/game-config/srmvs.hpp
|
||||
|
||||
@@ -10,12 +10,6 @@ namespace ares::Nintendo64 {
|
||||
#include "serialization.cpp"
|
||||
|
||||
#include "game-config/11beat.hpp"
|
||||
#include "game-config/starsldr.hpp"
|
||||
#include "game-config/doncdoon.hpp"
|
||||
#include "game-config/kurufev.hpp"
|
||||
#include "game-config/mayjin3.hpp"
|
||||
#include "game-config/vivdolls.hpp"
|
||||
#include "game-config/twrshaft.hpp"
|
||||
#include "game-config/hipai.hpp"
|
||||
#include "game-config/hipai2.hpp"
|
||||
#include "game-config/srmvs.hpp"
|
||||
@@ -34,6 +28,7 @@ namespace ares::Nintendo64 {
|
||||
}
|
||||
|
||||
auto Aleck64::unload() -> void {
|
||||
dips.unload();
|
||||
sdram.reset();
|
||||
debugger.unload();
|
||||
}
|
||||
@@ -44,19 +39,10 @@ namespace ares::Nintendo64 {
|
||||
|
||||
auto Aleck64::power(bool reset) -> void {
|
||||
if(!reset) {
|
||||
dipSwitch[0] = 0xffff'ffff;
|
||||
dipSwitch[1] = 0xffff'ffff;
|
||||
|
||||
//NOTE: We can't do this at 'load' time because cartridges are not attached yet...
|
||||
auto name = cartridge.pak->attribute("name");
|
||||
|
||||
if(name == "11beat" ) gameConfig = std::make_shared<_11beat>();
|
||||
if(name == "starsldr") gameConfig = std::make_shared<starsldr>();
|
||||
if(name == "doncdoon") gameConfig = std::make_shared<doncdoon>();
|
||||
if(name == "kurufev" ) gameConfig = std::make_shared<kurufev>();
|
||||
if(name == "mayjin3" ) gameConfig = std::make_shared<mayjin3>();
|
||||
if(name == "vivdolls") gameConfig = std::make_shared<vivdolls>();
|
||||
if(name == "twrshaft") gameConfig = std::make_shared<twrshaft>();
|
||||
if(name == "hipai" ) gameConfig = std::make_shared<hipai>();
|
||||
if(name == "hipai2" ) gameConfig = std::make_shared<hipai2>();
|
||||
if(name == "srmvs" ) gameConfig = std::make_shared<srmvs>();
|
||||
@@ -64,7 +50,7 @@ namespace ares::Nintendo64 {
|
||||
if(name == "mtetrisc") gameConfig = std::make_shared<mtetrisc>();
|
||||
if(!gameConfig) gameConfig = std::make_shared<GameConfig>(); //Fallback to default implementation
|
||||
|
||||
gameConfig->dipSwitches(dipSwitchNode);
|
||||
dips.load(dipSwitchNode, cartridge.pak, {"IN0"}, 0, true);
|
||||
|
||||
sdram.fill();
|
||||
vram.fill();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
struct Aleck64 {
|
||||
Node::Object dipSwitchNode;
|
||||
Arcade::Dip dips;
|
||||
enum BoardType { E90, E92 };
|
||||
|
||||
struct Writable : public Memory::Writable {
|
||||
@@ -235,13 +236,10 @@ struct Aleck64 {
|
||||
} io;
|
||||
} vdp;
|
||||
|
||||
n8 dipSwitch[2];
|
||||
|
||||
struct GameConfig {
|
||||
virtual ~GameConfig() = default;
|
||||
virtual auto type() -> BoardType { return BoardType::E92; }
|
||||
virtual auto dpadDisabled() -> n1 { return 0; }
|
||||
virtual auto dipSwitches(Node::Object parent) -> void {};
|
||||
virtual auto readExpansionPort() -> u32 { return 0xffff'ffff; }
|
||||
virtual auto writeExpansionPort(n32 data) -> void {};
|
||||
};
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
struct _11beat : Aleck64::GameConfig {
|
||||
auto dpadDisabled() -> n1 { return true; }
|
||||
|
||||
auto dipSwitches(Node::Object parent) -> void {
|
||||
Node::Setting::Boolean testMode = parent->append<Node::Setting::Boolean>("Test Mode", false, [&](auto value) {
|
||||
aleck64.dipSwitch[1].bit(7) = !value;
|
||||
});
|
||||
testMode->setDynamic(true);
|
||||
testMode->modify(testMode->value());
|
||||
}
|
||||
};
|
||||
@@ -1,9 +0,0 @@
|
||||
struct doncdoon : Aleck64::GameConfig {
|
||||
auto dipSwitches(Node::Object parent) -> void {
|
||||
Node::Setting::Boolean testMode = parent->append<Node::Setting::Boolean>("Test Mode", false, [&](auto value) {
|
||||
aleck64.dipSwitch[0].bit(0) = !value;
|
||||
});
|
||||
testMode->setDynamic(true);
|
||||
testMode->modify(testMode->value());
|
||||
}
|
||||
};
|
||||
@@ -1,69 +1,6 @@
|
||||
struct hipai : Aleck64::GameConfig {
|
||||
u8 mahjongRow = 0;
|
||||
|
||||
auto dipSwitches(Node::Object parent) -> void {
|
||||
Node::Setting::Boolean testMode = parent->append<Node::Setting::Boolean>("Test Mode", false, [&](auto value) {
|
||||
aleck64.dipSwitch[1].bit(7) = !value;
|
||||
});
|
||||
testMode->setDynamic(true);
|
||||
testMode->modify(testMode->value());
|
||||
|
||||
Node::Setting::Boolean freePlay = parent->append<Node::Setting::Boolean>("Free Play", false, [&](auto value) {
|
||||
aleck64.dipSwitch[0].bit(7) = !value;
|
||||
});
|
||||
freePlay->setDynamic(true);
|
||||
freePlay->modify(freePlay->value());
|
||||
|
||||
|
||||
Node::Setting::String coinage = parent->append<Node::Setting::String>("Coinage", "1 Coin 1 Credit", [&](auto value) {
|
||||
if(value == "1 Coin 1 Credit" ) aleck64.dipSwitch[0].bit(0,2) = 7;
|
||||
if(value == "1 Coin 2 Credits" ) aleck64.dipSwitch[0].bit(0,2) = 3;
|
||||
if(value == "1 Coin 3 Credits" ) aleck64.dipSwitch[0].bit(0,2) = 5;
|
||||
if(value == "1 Coin 4 Credits" ) aleck64.dipSwitch[0].bit(0,2) = 4;
|
||||
if(value == "1 Coins 2 Credits") aleck64.dipSwitch[0].bit(0,2) = 6;
|
||||
if(value == "3 Coins 1 Credit" ) aleck64.dipSwitch[0].bit(0,2) = 2;
|
||||
if(value == "4 Coins 1 Credit" ) aleck64.dipSwitch[0].bit(0,2) = 1;
|
||||
if(value == "5 Coins 1 Credit" ) aleck64.dipSwitch[0].bit(0,2) = 0;
|
||||
});
|
||||
coinage->setAllowedValues({"1 Coin 1 Credit" , "1 Coin 2 Credits" , "1 Coin 3 Credits" , "1 Coin 4 Credits",
|
||||
"1 Coins 2 Credit", "3 Coins 1 Credit", "4 Coins 1 Credit", "5 Coins 1 Credit",
|
||||
});
|
||||
coinage->setDynamic(true);
|
||||
coinage->modify(coinage->value());
|
||||
|
||||
Node::Setting::Boolean demoSounds = parent->append<Node::Setting::Boolean>("Demo Sounds", true, [&](auto value) {
|
||||
aleck64.dipSwitch[1].bit(5) = !value;
|
||||
});
|
||||
demoSounds->setDynamic(true);
|
||||
demoSounds->modify(demoSounds->value());
|
||||
|
||||
Node::Setting::Boolean allowContinue = parent->append<Node::Setting::Boolean>("Allow Continue", false, [&](auto value) {
|
||||
aleck64.dipSwitch[1].bit(4) = value;
|
||||
});
|
||||
allowContinue->setDynamic(true);
|
||||
allowContinue->modify(allowContinue->value());
|
||||
|
||||
Node::Setting::Boolean kuitan = parent->append<Node::Setting::Boolean>("Kuitan", false, [&](auto value) {
|
||||
aleck64.dipSwitch[1].bit(3) = value;
|
||||
});
|
||||
kuitan->setDynamic(true);
|
||||
kuitan->modify(kuitan->value());
|
||||
|
||||
Node::Setting::String difficulty = parent->append<Node::Setting::String>("Difficulty", "Normal", [&](auto value) {
|
||||
if(value == "Normal" ) aleck64.dipSwitch[1].bit(0,2) = 7;
|
||||
if(value == "Easiest" ) aleck64.dipSwitch[1].bit(0,2) = 6;
|
||||
if(value == "Very Easy") aleck64.dipSwitch[1].bit(0,2) = 5;
|
||||
if(value == "Easy" ) aleck64.dipSwitch[1].bit(0,2) = 4;
|
||||
if(value == "Normal+" ) aleck64.dipSwitch[1].bit(0,2) = 3;
|
||||
if(value == "Hard" ) aleck64.dipSwitch[1].bit(0,2) = 2;
|
||||
if(value == "Very Hard") aleck64.dipSwitch[1].bit(0,2) = 1;
|
||||
if(value == "Most Hard") aleck64.dipSwitch[1].bit(0,2) = 0;
|
||||
});
|
||||
difficulty->setAllowedValues({"Normal", "Easiest", "Very Easy", "Easy", "Normal+", "Hard", "Very Hard", "Most Hard"});
|
||||
difficulty->setDynamic(true);
|
||||
difficulty->modify(difficulty->value());
|
||||
}
|
||||
|
||||
auto readExpansionPort() -> u32 {
|
||||
n32 value = 0xffff'ffff;
|
||||
value.bit(16, 23) = aleck64.mahjong.read(mahjongRow);
|
||||
|
||||
@@ -1,74 +1,6 @@
|
||||
struct hipai2 : Aleck64::GameConfig {
|
||||
u8 mahjongRow = 0;
|
||||
|
||||
auto dipSwitches(Node::Object parent) -> void {
|
||||
Node::Setting::Boolean testMode = parent->append<Node::Setting::Boolean>("Test Mode", false, [&](auto value) {
|
||||
aleck64.dipSwitch[1].bit(7) = !value;
|
||||
});
|
||||
testMode->setDynamic(true);
|
||||
testMode->modify(testMode->value());
|
||||
|
||||
Node::Setting::Boolean freePlay = parent->append<Node::Setting::Boolean>("Free Play", false, [&](auto value) {
|
||||
aleck64.dipSwitch[0].bit(7) = !value;
|
||||
});
|
||||
freePlay->setDynamic(true);
|
||||
freePlay->modify(freePlay->value());
|
||||
|
||||
Node::Setting::Boolean backupSettings = parent->append<Node::Setting::Boolean>("Backup Settings", false, [&](auto value) {
|
||||
aleck64.dipSwitch[0].bit(6) = !value;
|
||||
});
|
||||
backupSettings->setDynamic(true);
|
||||
backupSettings->modify(backupSettings->value());
|
||||
|
||||
Node::Setting::String coinage = parent->append<Node::Setting::String>("Coinage", "1 Coin 1 Credit", [&](auto value) {
|
||||
if(value == "1 Coin 1 Credit" ) aleck64.dipSwitch[0].bit(0,2) = 7;
|
||||
if(value == "1 Coin 2 Credits" ) aleck64.dipSwitch[0].bit(0,2) = 3;
|
||||
if(value == "1 Coin 3 Credits" ) aleck64.dipSwitch[0].bit(0,2) = 5;
|
||||
if(value == "1 Coin 4 Credits" ) aleck64.dipSwitch[0].bit(0,2) = 4;
|
||||
if(value == "1 Coins 2 Credits") aleck64.dipSwitch[0].bit(0,2) = 6;
|
||||
if(value == "3 Coins 1 Credit" ) aleck64.dipSwitch[0].bit(0,2) = 2;
|
||||
if(value == "4 Coins 1 Credit" ) aleck64.dipSwitch[0].bit(0,2) = 1;
|
||||
if(value == "5 Coins 1 Credit" ) aleck64.dipSwitch[0].bit(0,2) = 0;
|
||||
});
|
||||
coinage->setAllowedValues({"1 Coin 1 Credit" , "1 Coin 2 Credits" , "1 Coin 3 Credits" , "1 Coin 4 Credits",
|
||||
"1 Coins 2 Credit", "3 Coins 1 Credit", "4 Coins 1 Credit", "5 Coins 1 Credit",
|
||||
});
|
||||
coinage->setDynamic(true);
|
||||
coinage->modify(coinage->value());
|
||||
|
||||
Node::Setting::Boolean demoSounds = parent->append<Node::Setting::Boolean>("Demo Sounds", true, [&](auto value) {
|
||||
aleck64.dipSwitch[1].bit(5) = !value;
|
||||
});
|
||||
demoSounds->setDynamic(true);
|
||||
demoSounds->modify(demoSounds->value());
|
||||
|
||||
Node::Setting::Boolean allowContinue = parent->append<Node::Setting::Boolean>("Allow Continue", false, [&](auto value) {
|
||||
aleck64.dipSwitch[1].bit(4) = value;
|
||||
});
|
||||
allowContinue->setDynamic(true);
|
||||
allowContinue->modify(allowContinue->value());
|
||||
|
||||
Node::Setting::Boolean kuitan = parent->append<Node::Setting::Boolean>("Kuitan", false, [&](auto value) {
|
||||
aleck64.dipSwitch[1].bit(3) = value;
|
||||
});
|
||||
kuitan->setDynamic(true);
|
||||
kuitan->modify(kuitan->value());
|
||||
|
||||
Node::Setting::String difficulty = parent->append<Node::Setting::String>("Difficulty", "Normal", [&](auto value) {
|
||||
if(value == "Normal" ) aleck64.dipSwitch[1].bit(0,2) = 7;
|
||||
if(value == "Easiest" ) aleck64.dipSwitch[1].bit(0,2) = 6;
|
||||
if(value == "Very Easy") aleck64.dipSwitch[1].bit(0,2) = 5;
|
||||
if(value == "Easy" ) aleck64.dipSwitch[1].bit(0,2) = 4;
|
||||
if(value == "Normal+" ) aleck64.dipSwitch[1].bit(0,2) = 3;
|
||||
if(value == "Hard" ) aleck64.dipSwitch[1].bit(0,2) = 2;
|
||||
if(value == "Very Hard") aleck64.dipSwitch[1].bit(0,2) = 1;
|
||||
if(value == "Most Hard") aleck64.dipSwitch[1].bit(0,2) = 0;
|
||||
});
|
||||
difficulty->setAllowedValues({"Normal", "Easiest", "Very Easy", "Easy", "Normal+", "Hard", "Very Hard", "Most Hard"});
|
||||
difficulty->setDynamic(true);
|
||||
difficulty->modify(difficulty->value());
|
||||
}
|
||||
|
||||
auto readExpansionPort() -> u32 {
|
||||
n32 value = 0xffff'ffff;
|
||||
value.bit(16, 23) = aleck64.mahjong.read(mahjongRow);
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
struct kurufev : Aleck64::GameConfig {
|
||||
auto dipSwitches(Node::Object parent) -> void {
|
||||
Node::Setting::Boolean testMode = parent->append<Node::Setting::Boolean>("Test Mode", false, [&](auto value) {
|
||||
aleck64.dipSwitch[0].bit(0) = !value;
|
||||
});
|
||||
testMode->setDynamic(true);
|
||||
testMode->modify(testMode->value());
|
||||
}
|
||||
};
|
||||
@@ -1,9 +0,0 @@
|
||||
struct mayjin3 : Aleck64::GameConfig {
|
||||
auto dipSwitches(Node::Object parent) -> void {
|
||||
Node::Setting::Boolean testMode = parent->append<Node::Setting::Boolean>("Test Mode", false, [&](auto value) {
|
||||
aleck64.dipSwitch[1].bit(7) = !value;
|
||||
});
|
||||
testMode->setDynamic(true);
|
||||
testMode->modify(testMode->value());
|
||||
}
|
||||
};
|
||||
@@ -1,11 +1,3 @@
|
||||
struct mtetrisc : Aleck64::GameConfig {
|
||||
auto type() -> Aleck64::BoardType { return Aleck64::BoardType::E90; }
|
||||
|
||||
auto dipSwitches(Node::Object parent) -> void {
|
||||
Node::Setting::Boolean testMode = parent->append<Node::Setting::Boolean>("Test Mode", false, [&](auto value) {
|
||||
aleck64.dipSwitch[1].bit(7) = !value;
|
||||
});
|
||||
testMode->setDynamic(true);
|
||||
testMode->modify(testMode->value());
|
||||
}
|
||||
};
|
||||
@@ -1,14 +1,6 @@
|
||||
struct srmvs : Aleck64::GameConfig {
|
||||
u8 mahjongRow = 0;
|
||||
|
||||
auto dipSwitches(Node::Object parent) -> void {
|
||||
Node::Setting::Boolean testMode = parent->append<Node::Setting::Boolean>("Test Mode", false, [&](auto value) {
|
||||
aleck64.dipSwitch[1].bit(7) = !value;
|
||||
});
|
||||
testMode->setDynamic(true);
|
||||
testMode->modify(testMode->value());
|
||||
}
|
||||
|
||||
auto readExpansionPort() -> u32 {
|
||||
n32 value = 0xffff'ffff;
|
||||
value.bit(16, 23) = aleck64.mahjong.read(mahjongRow);
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
struct starsldr : Aleck64::GameConfig {
|
||||
auto dipSwitches(Node::Object parent) -> void {
|
||||
Node::Setting::Boolean testMode = parent->append<Node::Setting::Boolean>("Test Mode", false, [&](auto value) {
|
||||
aleck64.dipSwitch[1].bit(7) = !value;
|
||||
});
|
||||
testMode->setDynamic(true);
|
||||
testMode->modify(testMode->value());
|
||||
|
||||
Node::Setting::String joystickType = parent->append<Node::Setting::String>("Joystick Type", "2D", [&](auto value) {
|
||||
aleck64.dipSwitch[0].bit(7) = value == "3D";
|
||||
});
|
||||
joystickType->setAllowedValues({ "2D", "3D" });
|
||||
joystickType->setDynamic(true);
|
||||
joystickType->modify(joystickType->value());
|
||||
|
||||
Node::Setting::String player = parent->append<Node::Setting::String>("Player", "3", [&](auto value) {
|
||||
if(value == "3") aleck64.dipSwitch[0].bit(3,4) = 3;
|
||||
if(value == "4") aleck64.dipSwitch[0].bit(3,4) = 2;
|
||||
if(value == "2") aleck64.dipSwitch[0].bit(3,4) = 1;
|
||||
if(value == "1") aleck64.dipSwitch[0].bit(3,4) = 0;
|
||||
});
|
||||
player->setAllowedValues({"4", "3", "2", "1"});
|
||||
player->setDynamic(true);
|
||||
player->modify(player->value());
|
||||
|
||||
Node::Setting::String autoLevel = parent->append<Node::Setting::String>("Auto Level", "Normal", [&](auto value) {
|
||||
if(value == "Normal") aleck64.dipSwitch[0].bit(5,6) = 3;
|
||||
if(value == "Slow" ) aleck64.dipSwitch[0].bit(5,6) = 2;
|
||||
if(value == "Fast1" ) aleck64.dipSwitch[0].bit(5,6) = 1;
|
||||
if(value == "Fast2" ) aleck64.dipSwitch[0].bit(5,6) = 0;
|
||||
});
|
||||
autoLevel->setAllowedValues({ "Normal", "Slow", "Fast1", "Fast2" });
|
||||
autoLevel->setDynamic(true);
|
||||
autoLevel->modify(autoLevel->value());
|
||||
|
||||
Node::Setting::String coinage = parent->append<Node::Setting::String>("Coinage", "1 Coin 1 Credit", [&](auto value) {
|
||||
if(value == "1 Coin 1 Credit" ) aleck64.dipSwitch[0].bit(0,2) = 7;
|
||||
if(value == "1 Coin 2 Credits" ) aleck64.dipSwitch[0].bit(0,2) = 6;
|
||||
if(value == "1 Coin 3 Credits" ) aleck64.dipSwitch[0].bit(0,2) = 5;
|
||||
if(value == "1 Coin 4 Credits" ) aleck64.dipSwitch[0].bit(0,2) = 4;
|
||||
if(value == "2 Coins 1 Credit" ) aleck64.dipSwitch[0].bit(0,2) = 3;
|
||||
if(value == "3 Coins 1 Credit") aleck64.dipSwitch[0].bit(0,2) = 2;
|
||||
if(value == "4 Coins 1 Credit") aleck64.dipSwitch[0].bit(0,2) = 1;
|
||||
if(value == "5 Coins 1 Credit") aleck64.dipSwitch[0].bit(0,2) = 0;
|
||||
});
|
||||
|
||||
coinage->setAllowedValues({ "1 Coin 1 Credit", "1 Coin 2 Credits", "1 Coin 3 Credits", "1 Coin 4 Credits",
|
||||
"2 Coins 1 Credit", "3 Coins 1 Credit", "4 Coins 1 Credit", "5 Coins 1 Credit" });
|
||||
coinage->setDynamic(true);
|
||||
coinage->modify(coinage->value());
|
||||
|
||||
Node::Setting::String language = parent->append<Node::Setting::String>("Language", "English", [&](auto value) {
|
||||
aleck64.dipSwitch[1].bit(6) = value == "English";
|
||||
});
|
||||
language->setAllowedValues({ "English", "Japanese" });
|
||||
language->setDynamic(true);
|
||||
language->modify(language->value());
|
||||
|
||||
Node::Setting::Boolean demoSound = parent->append<Node::Setting::Boolean>("Demo Sound", true, [&](auto value) {
|
||||
aleck64.dipSwitch[1].bit(5) = !value;
|
||||
});
|
||||
demoSound->setDynamic(true);
|
||||
demoSound->modify(demoSound->value());
|
||||
|
||||
Node::Setting::Boolean rapid = parent->append<Node::Setting::Boolean>("Rapid", false, [&](auto value) {
|
||||
aleck64.dipSwitch[1].bit(4) = value;
|
||||
});
|
||||
rapid->setDynamic(true);
|
||||
rapid->modify(rapid->value());
|
||||
|
||||
Node::Setting::String extend = parent->append<Node::Setting::String>("Extend", "30000000", [&](auto value) {
|
||||
if(value == "30,000,000") aleck64.dipSwitch[1].bit(2,3) = 3;
|
||||
if(value == "50,000,000") aleck64.dipSwitch[1].bit(2,3) = 2;
|
||||
if(value == "70,000,000") aleck64.dipSwitch[1].bit(2,3) = 1;
|
||||
if(value == "None" ) aleck64.dipSwitch[1].bit(2,3) = 0;
|
||||
});
|
||||
extend->setAllowedValues({ "30,000,000", "50,000,000", "70,000,000", "None" });
|
||||
extend->setDynamic(true);
|
||||
extend->modify(extend->value());
|
||||
|
||||
Node::Setting::String difficulty = parent->append<Node::Setting::String>("Difficulty", "Normal", [&](auto value) {
|
||||
if(value == "Normal") aleck64.dipSwitch[1].bit(0,1) = 3;
|
||||
if(value == "Easy" ) aleck64.dipSwitch[1].bit(0,1) = 2;
|
||||
if(value == "Hard1" ) aleck64.dipSwitch[1].bit(0,1) = 1;
|
||||
if(value == "Hard2" ) aleck64.dipSwitch[1].bit(0,1) = 0;
|
||||
});
|
||||
|
||||
difficulty->setAllowedValues({ "Normal", "Easy", "Hard1", "Hard2" });
|
||||
difficulty->setDynamic(true);
|
||||
difficulty->modify(difficulty->value());
|
||||
}
|
||||
};
|
||||
@@ -1,9 +0,0 @@
|
||||
struct twrshaft : Aleck64::GameConfig {
|
||||
auto dipSwitches(Node::Object parent) -> void {
|
||||
Node::Setting::Boolean testMode = parent->append<Node::Setting::Boolean>("Test Mode", false, [&](auto value) {
|
||||
aleck64.dipSwitch[1].bit(7) = !value;
|
||||
});
|
||||
testMode->setDynamic(true);
|
||||
testMode->modify(testMode->value());
|
||||
}
|
||||
};
|
||||
@@ -1,61 +0,0 @@
|
||||
struct vivdolls : Aleck64::GameConfig {
|
||||
auto dipSwitches(Node::Object parent) -> void {
|
||||
Node::Setting::Boolean testMode = parent->append<Node::Setting::Boolean>("Test Mode", false, [&](auto value) {
|
||||
aleck64.dipSwitch[1].bit(7) = !value;
|
||||
});
|
||||
testMode->setDynamic(true);
|
||||
testMode->modify(testMode->value());
|
||||
|
||||
Node::Setting::String coinage = parent->append<Node::Setting::String>("Coinage", "1 Coin 1 Credit", [&](auto value) {
|
||||
if(value == "1 Coin 1 Credit" ) aleck64.dipSwitch[0].bit(0,2) = 7;
|
||||
if(value == "1 Coin 2 Credits") aleck64.dipSwitch[0].bit(0,2) = 3;
|
||||
if(value == "1 Coin 3 Credits") aleck64.dipSwitch[0].bit(0,2) = 5;
|
||||
if(value == "1 Coin 4 Credits") aleck64.dipSwitch[0].bit(0,2) = 1;
|
||||
if(value == "2 Coins 1 Credit") aleck64.dipSwitch[0].bit(0,2) = 6;
|
||||
if(value == "3 Coins 1 Credit") aleck64.dipSwitch[0].bit(0,2) = 2;
|
||||
if(value == "4 Coins 1 Credit") aleck64.dipSwitch[0].bit(0,2) = 4;
|
||||
if(value == "5 Coins 1 Credit") aleck64.dipSwitch[0].bit(0,2) = 0;
|
||||
});
|
||||
|
||||
coinage->setAllowedValues({ "1 Coin 1 Credit", "1 Coin 2 Credits", "1 Coin 3 Credits", "1 Coin 4 Credits",
|
||||
"2 Coins 1 Credit", "3 Coins 1 Credit", "4 Coins 1 Credit", "5 Coins 1 Credit" });
|
||||
coinage->setDynamic(true);
|
||||
coinage->modify(coinage->value());
|
||||
|
||||
Node::Setting::String qualifyArea = parent->append<Node::Setting::String>("Qualify Area", "70%", [&](auto value) {
|
||||
if(value == "70%") aleck64.dipSwitch[1].bit(5) = 0;
|
||||
if(value == "80%") aleck64.dipSwitch[1].bit(5) = 1;
|
||||
});
|
||||
qualifyArea->setAllowedValues({ "70%", "80%" });
|
||||
qualifyArea->setDynamic(true);
|
||||
qualifyArea->modify(qualifyArea->value());
|
||||
|
||||
Node::Setting::String controls = parent->append<Node::Setting::String>("Controls", "JOYSTICK", [&](auto value) {
|
||||
if(value == "JAMMASTICK") aleck64.dipSwitch[1].bit(5) = 0;
|
||||
if(value == "JOYSTICK" ) aleck64.dipSwitch[1].bit(5) = 1;
|
||||
});
|
||||
controls->setAllowedValues({ "JAMMASTICK", "JOYSTICK" });
|
||||
controls->setDynamic(true);
|
||||
controls->modify(controls->value());
|
||||
|
||||
Node::Setting::String lives = parent->append<Node::Setting::String>("Lives", "4", [&](auto value) {
|
||||
if(value == "4") aleck64.dipSwitch[1].bit(2,3) = 3;
|
||||
if(value == "3") aleck64.dipSwitch[1].bit(2,3) = 2;
|
||||
if(value == "2") aleck64.dipSwitch[1].bit(2,3) = 1;
|
||||
if(value == "1") aleck64.dipSwitch[1].bit(2,3) = 0;
|
||||
});
|
||||
lives->setAllowedValues({ "4", "3", "2", "1" });
|
||||
lives->setDynamic(true);
|
||||
lives->modify(lives->value());
|
||||
|
||||
Node::Setting::String difficulty = parent->append<Node::Setting::String>("Difficulty", "Normal", [&](auto value) {
|
||||
if(value == "Normal" ) aleck64.dipSwitch[1].bit(0,1) = 3;
|
||||
if(value == "Easy" ) aleck64.dipSwitch[1].bit(0,1) = 2;
|
||||
if(value == "Hard" ) aleck64.dipSwitch[1].bit(0,1) = 1;
|
||||
if(value == "Hardest") aleck64.dipSwitch[1].bit(0,1) = 0;
|
||||
});
|
||||
difficulty->setAllowedValues({ "Normal", "Easy", "Hard", "Hardest" });
|
||||
difficulty->setDynamic(true);
|
||||
difficulty->modify(difficulty->value());
|
||||
}
|
||||
};
|
||||
@@ -35,8 +35,7 @@ auto Aleck64::readPort1() -> u32 {
|
||||
value.bit(15) = !aleck64.controls.p2start->value();
|
||||
}
|
||||
|
||||
value.bit(16, 23) = dipSwitch[1];
|
||||
value.bit(24, 31) = dipSwitch[0];
|
||||
value.bit(16, 31) = dips.read("IN0") >> 16;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -4,5 +4,4 @@ auto Aleck64::serialize(serializer& s) -> void {
|
||||
s(pram);
|
||||
|
||||
s(vdp.io.enable);
|
||||
s(dipSwitch);
|
||||
}
|
||||
@@ -683,6 +683,7 @@ auto CPU::Recompiler::emit(u64 vaddr, u32 address, u64 stateKey) -> Block* {
|
||||
for(size_t idx = 0; idx < plan.instructions.size(); idx++) {
|
||||
auto& ii = plan.instructions[idx];
|
||||
bool firstInstruction = (idx == 0);
|
||||
bool lastInstruction = (idx + 1 == plan.instructions.size());
|
||||
bool delaySlot = prevBranched;
|
||||
bool isInternalEntry = findInternalLabel(ii.vaddr) != nullptr;
|
||||
bool sectionBoundary = sectionIndex(ii.address + 4) != startSection;
|
||||
@@ -752,9 +753,12 @@ auto CPU::Recompiler::emit(u64 vaddr, u32 address, u64 stateKey) -> Block* {
|
||||
// EndBlock must be observed at entry, delay-slot completion, likely-branch flows and helpers.
|
||||
bool needEndBlockCheck = firstInstruction || delaySlot || info.likelyBranch() || emitCallfEmitted;
|
||||
// Commit architectural pipeline state only where correctness requires it.
|
||||
// The block exit after the last instruction needs a commit too: a window
|
||||
// that ends before a breakpoint can end on straight-line code.
|
||||
bool needPipelineCommit = needEndBlockCheck || info.branch() || sectionBoundary
|
||||
|| info.jitStateKeyMayChange()
|
||||
|| info.countCompareWrite();
|
||||
|| info.countCompareWrite()
|
||||
|| lastInstruction;
|
||||
if(needPipelineCommit) setupPipeline();
|
||||
if(needEndBlockCheck) {
|
||||
test32(PipelineReg(state), imm(Pipeline::EndBlock), set_z);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <nall/queue.hpp>
|
||||
#include <nall/recompiler/generic/generic.hpp>
|
||||
#include <component/processor/sm5k/sm5k.hpp>
|
||||
#include <component/arcade/dip-switches.hpp>
|
||||
#include <functional>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
@@ -194,6 +194,7 @@ auto RDP::IO::writeWord(u32 address, u32 data_, Thread& thread) -> void {
|
||||
|
||||
auto RDP::flushCommands() -> void {
|
||||
if(command.freeze || command.crashed) return;
|
||||
if(command.end <= command.current) return;
|
||||
command.bufferBusy = 1;
|
||||
command.pipeBusy = 1;
|
||||
command.startGclk = 1;
|
||||
|
||||
+13
-1
@@ -1,4 +1,4 @@
|
||||
ares_components(m68000 z80)
|
||||
ares_components(m68000 z80 arcadedip upd4990a)
|
||||
|
||||
target_link_libraries(ares PRIVATE ymfm)
|
||||
|
||||
@@ -57,6 +57,18 @@ ares_add_sources(
|
||||
cartridge/slot.hpp
|
||||
cartridge/board/board.cpp
|
||||
cartridge/board/board.hpp
|
||||
cartridge/board/cmc.cpp
|
||||
cartridge/board/cmc-tables.hpp
|
||||
cartridge/board/fatfury2.cpp
|
||||
cartridge/board/k2k2.cpp
|
||||
cartridge/board/k2k2-tables.hpp
|
||||
cartridge/board/kof98.cpp
|
||||
cartridge/board/pcm2.cpp
|
||||
cartridge/board/pcm2-tables.hpp
|
||||
cartridge/board/pvc.cpp
|
||||
cartridge/board/pvc-tables.hpp
|
||||
cartridge/board/sma.cpp
|
||||
cartridge/board/sma-tables.hpp
|
||||
cartridge/board/jockey-gp.cpp
|
||||
cartridge/board/mslugx.cpp
|
||||
cartridge/board/rom.cpp
|
||||
|
||||
@@ -2,6 +2,13 @@ namespace Board {
|
||||
|
||||
#include "rom.cpp"
|
||||
#include "mslugx.cpp"
|
||||
#include "cmc.cpp"
|
||||
#include "sma.cpp"
|
||||
#include "pcm2.cpp"
|
||||
#include "pvc.cpp"
|
||||
#include "k2k2.cpp"
|
||||
#include "fatfury2.cpp"
|
||||
#include "kof98.cpp"
|
||||
#include "jockey-gp.cpp"
|
||||
|
||||
auto Interface::load(Memory::Readable<n8>& memory, string name) -> bool {
|
||||
|
||||
@@ -15,6 +15,7 @@ struct Interface {
|
||||
virtual auto readS(n32 address) -> n8 { return 0xff; }
|
||||
virtual auto readVA(n32 address) -> n8 { return 0xff; }
|
||||
virtual auto readVB(n32 address) -> n8 { return 0xff; }
|
||||
virtual auto fixBankType() -> u32 { return 0; }
|
||||
virtual auto power() -> void {}
|
||||
virtual auto serialize(serializer&) -> void {}
|
||||
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
u8 kof99_type0_t03[256] = {
|
||||
#pragma once
|
||||
|
||||
// Decryption algorithms heavily based on/borrowed from MAME
|
||||
// See https://github.com/mamedev/mame/tree/master/src/devices/bus/neogeo
|
||||
// and the MAME section in LICENCE in the ares license text.
|
||||
|
||||
namespace CmcTables {
|
||||
|
||||
namespace Cmc42 {
|
||||
|
||||
static constexpr u8 type0High[256] = {
|
||||
0xfb, 0x86, 0x9d, 0xf1, 0xbf, 0x80, 0xd5, 0x43, 0xab, 0xb3, 0x9f, 0x6a, 0x33, 0xd9, 0xdb, 0xb6,
|
||||
0x66, 0x08, 0x69, 0x88, 0xcc, 0xb7, 0xde, 0x49, 0x97, 0x64, 0x1f, 0xa6, 0xc0, 0x2f, 0x52, 0x42,
|
||||
0x44, 0x5a, 0xf2, 0x28, 0x98, 0x87, 0x96, 0x8a, 0x83, 0x0b, 0x03, 0x61, 0x71, 0x99, 0x6b, 0xb5,
|
||||
@@ -17,7 +27,7 @@ u8 kof99_type0_t03[256] = {
|
||||
0xc6, 0xce, 0x18, 0x36, 0xdf, 0xa9, 0x8c, 0xd8, 0xa4, 0xf0, 0x3b, 0x51, 0x4a, 0x02, 0x3e, 0x53,
|
||||
};
|
||||
|
||||
u8 kof99_type0_t12[256] = {
|
||||
static constexpr u8 type0Low[256] = {
|
||||
0x1f, 0xac, 0x4d, 0xcd, 0xca, 0x70, 0x02, 0x6b, 0x18, 0x40, 0x62, 0xb2, 0x3f, 0x9b, 0x5b, 0xef,
|
||||
0x69, 0x68, 0x71, 0x3b, 0xcb, 0xd4, 0x30, 0xbc, 0x47, 0x72, 0x74, 0x5e, 0x84, 0x4c, 0x1b, 0xdb,
|
||||
0x6a, 0x35, 0x1d, 0xf5, 0xa1, 0xb3, 0x87, 0x5d, 0x57, 0x28, 0x2f, 0xc4, 0xfd, 0x24, 0x26, 0x36,
|
||||
@@ -36,7 +46,7 @@ u8 kof99_type0_t12[256] = {
|
||||
0x67, 0xae, 0x09, 0x89, 0xdc, 0x4e, 0xc6, 0xc0, 0x8a, 0xb1, 0x59, 0x8b, 0x41, 0x56, 0x8d, 0xba,
|
||||
};
|
||||
|
||||
u8 kof99_type1_t03[256] = {
|
||||
static constexpr u8 type1High[256] = {
|
||||
0xa9, 0x17, 0xaf, 0x0d, 0x34, 0x6e, 0x53, 0xb6, 0x7f, 0x58, 0xe9, 0x14, 0x5f, 0x55, 0xdb, 0xd4,
|
||||
0x42, 0x80, 0x99, 0x59, 0xa8, 0x3a, 0x57, 0x5d, 0xd5, 0x6f, 0x4c, 0x68, 0x35, 0x46, 0xa6, 0xe7,
|
||||
0x7b, 0x71, 0xe0, 0x93, 0xa2, 0x1f, 0x64, 0x21, 0xe3, 0xb1, 0x98, 0x26, 0xab, 0xad, 0xee, 0xe5,
|
||||
@@ -55,7 +65,7 @@ u8 kof99_type1_t03[256] = {
|
||||
0x63, 0x40, 0x2d, 0xe8, 0xb2, 0xe6, 0x39, 0x03, 0xeb, 0x8e, 0xe1, 0x0c, 0xe4, 0xe2, 0xf8, 0xac,
|
||||
};
|
||||
|
||||
u8 kof99_type1_t12[256] = {
|
||||
static constexpr u8 type1Low[256] = {
|
||||
0xea, 0xe6, 0x5e, 0xa7, 0x8e, 0xac, 0x34, 0x03, 0x30, 0x97, 0x52, 0x53, 0x76, 0xf2, 0x62, 0x0b,
|
||||
0x0a, 0xfc, 0x94, 0xb8, 0x67, 0x36, 0x11, 0xbc, 0xae, 0xca, 0xfa, 0x15, 0x04, 0x2b, 0x17, 0xc4,
|
||||
0x3e, 0x5b, 0x59, 0x01, 0x57, 0xe2, 0xba, 0xb7, 0xd1, 0x3f, 0xf0, 0x6a, 0x9c, 0x2a, 0xcb, 0xa9,
|
||||
@@ -74,9 +84,7 @@ u8 kof99_type1_t12[256] = {
|
||||
0xd2, 0xd3, 0xfd, 0x1d, 0xf5, 0x3b, 0xa5, 0x7f, 0xef, 0x9a, 0xb6, 0x42, 0xfb, 0x3c, 0xf7, 0x41,
|
||||
};
|
||||
|
||||
// underlined values are wrong (not enough evidence, FF fill in kof99 and garou)
|
||||
// they correspond to tiles 7d000-7efff
|
||||
u8 kof99_address_8_15_xor1[256] = {
|
||||
static constexpr u8 address8To15Xor1[256] = {
|
||||
0x00, 0xb1, 0x1e, 0xc5, 0x3d, 0x40, 0x45, 0x5e, 0xf2, 0xf8, 0x04, 0x63, 0x36, 0x87, 0x88, 0xbf,
|
||||
0xab, 0xcc, 0x78, 0x08, 0xdd, 0x20, 0xd4, 0x35, 0x09, 0x8e, 0x44, 0xae, 0x33, 0xa9, 0x9e, 0xcd,
|
||||
0xb3, 0xe5, 0xad, 0x41, 0xda, 0xbe, 0xf4, 0x16, 0x57, 0x2e, 0x53, 0x67, 0xaf, 0xdb, 0x8a, 0xd8,
|
||||
@@ -93,10 +101,9 @@ u8 kof99_address_8_15_xor1[256] = {
|
||||
0x3a, 0x8d, 0x32, 0x5d, 0x68, 0xb9, 0x9f, 0x75, 0x19, 0x3f, 0xac, 0x37, 0x4f, 0xe7, 0x93, 0x89,
|
||||
0x7e, 0x4a, 0x3b, 0xea, 0x74, 0x72, 0x43, 0xbd, 0x24, 0xef, 0xb6, 0xff, 0x64, 0x58, 0x84, 0x8b,
|
||||
0xa7, 0xbb, 0xb2, 0xe1, 0x26, 0x2b, 0x50, 0xca, 0x21, 0xf9, 0x98, 0xa1, 0xe2, 0x42, 0x82, 0x48,
|
||||
// ^^^^ ^^^^ ^^^^ ^^^^
|
||||
};
|
||||
|
||||
u8 kof99_address_8_15_xor2[256] = {
|
||||
static constexpr u8 address8To15Xor2[256] = {
|
||||
0x9b, 0x9d, 0xc1, 0x3d, 0xa9, 0xb8, 0xf4, 0x6f, 0xf6, 0x25, 0xc7, 0x47, 0xd5, 0x97, 0xdf, 0x6b,
|
||||
0xeb, 0x90, 0xa4, 0xb2, 0x5d, 0xf5, 0x66, 0xb0, 0xb9, 0x8b, 0x93, 0x64, 0xec, 0x7b, 0x65, 0x8c,
|
||||
0xf1, 0x43, 0x42, 0x6e, 0x45, 0x9f, 0xb3, 0x35, 0x06, 0x71, 0x96, 0xdb, 0xa0, 0xfb, 0x0b, 0x3a,
|
||||
@@ -115,7 +122,7 @@ u8 kof99_address_8_15_xor2[256] = {
|
||||
0x87, 0x07, 0x10, 0x60, 0x14, 0x73, 0x3c, 0x51, 0x19, 0xa3, 0xb5, 0xcf, 0x13, 0xf0, 0x15, 0x4e,
|
||||
};
|
||||
|
||||
u8 kof99_address_16_23_xor1[256] = {
|
||||
static constexpr u8 address16To23Xor1[256] = {
|
||||
0x00, 0x5f, 0x03, 0x52, 0xce, 0xe3, 0x7d, 0x8f, 0x6b, 0xf8, 0x20, 0xde, 0x7b, 0x7e, 0x39, 0xbe,
|
||||
0xf5, 0x94, 0x18, 0x78, 0x80, 0xc9, 0x7f, 0x7a, 0x3e, 0x63, 0xf2, 0xe0, 0x4e, 0xf7, 0x87, 0x27,
|
||||
0x69, 0x6c, 0xa4, 0x1d, 0x85, 0x5b, 0xe6, 0x44, 0x25, 0x0c, 0x98, 0xc7, 0x01, 0x02, 0xa3, 0x26,
|
||||
@@ -134,7 +141,7 @@ u8 kof99_address_16_23_xor1[256] = {
|
||||
0x65, 0xf4, 0x42, 0xa0, 0x8d, 0xa7, 0x48, 0x55, 0x21, 0x93, 0x24, 0xd2, 0xa6, 0xa5, 0xa8, 0x82,
|
||||
};
|
||||
|
||||
u8 kof99_address_16_23_xor2[256] = {
|
||||
static constexpr u8 address16To23Xor2[256] = {
|
||||
0x29, 0x97, 0x1a, 0x2c, 0x0b, 0x94, 0x3e, 0x75, 0x01, 0x0d, 0x1b, 0xe1, 0x4d, 0x38, 0x39, 0x8f,
|
||||
0xe7, 0xd0, 0x60, 0x90, 0xb2, 0x0f, 0xbb, 0x70, 0x1f, 0xe6, 0x5b, 0x87, 0xb4, 0x43, 0xfd, 0xf5,
|
||||
0xf6, 0xf9, 0xad, 0xc0, 0x98, 0x17, 0x9f, 0x91, 0x15, 0x51, 0x55, 0x64, 0x6c, 0x18, 0x61, 0x0e,
|
||||
@@ -153,7 +160,7 @@ u8 kof99_address_16_23_xor2[256] = {
|
||||
0xb7, 0xb3, 0xd8, 0xcf, 0x3b, 0x00, 0x92, 0xb8, 0x86, 0xac, 0x49, 0x7c, 0xf1, 0xd5, 0xcb, 0x7d,
|
||||
};
|
||||
|
||||
u8 kof99_address_0_7_xor[256] = {
|
||||
static constexpr u8 address0To7Xor[256] = {
|
||||
0x74, 0xad, 0x5d, 0x1d, 0x9e, 0xc3, 0xfa, 0x4e, 0xf7, 0xdb, 0xca, 0xa2, 0x64, 0x36, 0x56, 0x0c,
|
||||
0x4f, 0xcf, 0x43, 0x66, 0x1e, 0x91, 0xe3, 0xa5, 0x58, 0xc2, 0xc1, 0xd4, 0xb9, 0xdd, 0x76, 0x16,
|
||||
0xce, 0x61, 0x75, 0x01, 0x2b, 0x22, 0x38, 0x55, 0x50, 0xef, 0x6c, 0x99, 0x05, 0xe9, 0xe8, 0xe0,
|
||||
@@ -172,7 +179,11 @@ u8 kof99_address_0_7_xor[256] = {
|
||||
0x48, 0xac, 0x7f, 0x3f, 0x95, 0xdc, 0x98, 0x9b, 0xbe, 0x23, 0x57, 0x3e, 0x5b, 0xd0, 0x3d, 0xa6,
|
||||
};
|
||||
|
||||
u8 kof2000_type0_t03[256] = {
|
||||
}
|
||||
|
||||
namespace Cmc50 {
|
||||
|
||||
static constexpr u8 type0High[256] = {
|
||||
0x10, 0x61, 0xf1, 0x78, 0x85, 0x52, 0x68, 0xe3, 0x12, 0x0d, 0xfa, 0xf0, 0xc9, 0x36, 0x5e, 0x3d,
|
||||
0xf9, 0xa6, 0x01, 0x2e, 0xc7, 0x84, 0xea, 0x2b, 0x6d, 0x14, 0x38, 0x4f, 0x55, 0x1c, 0x9d, 0xa7,
|
||||
0x7a, 0xc6, 0xf8, 0x9a, 0xe6, 0x42, 0xb5, 0xed, 0x7d, 0x3a, 0xb1, 0x05, 0x43, 0x4a, 0x22, 0xfd,
|
||||
@@ -191,7 +202,7 @@ u8 kof2000_type0_t03[256] = {
|
||||
0x6e, 0x7f, 0x54, 0x3f, 0x02, 0xd1, 0x5b, 0x50, 0x56, 0x48, 0xf4, 0xbd, 0x62, 0x47, 0x04, 0xcd,
|
||||
};
|
||||
|
||||
u8 kof2000_type0_t12[256] = {
|
||||
static constexpr u8 type0Low[256] = {
|
||||
0xf4, 0x28, 0xb4, 0x8f, 0xfa, 0xeb, 0x8e, 0x54, 0x2b, 0x49, 0xd1, 0x76, 0x71, 0x47, 0x8b, 0x57,
|
||||
0x92, 0x85, 0x7c, 0xb8, 0x5c, 0x22, 0xf9, 0x26, 0xbc, 0x5b, 0x6d, 0x67, 0xae, 0x5f, 0x6f, 0xf5,
|
||||
0x9f, 0x48, 0x66, 0x40, 0x0d, 0x11, 0x4e, 0xb2, 0x6b, 0x35, 0x15, 0x0f, 0x18, 0x25, 0x1d, 0xba,
|
||||
@@ -210,7 +221,7 @@ u8 kof2000_type0_t12[256] = {
|
||||
0xdc, 0xc4, 0x32, 0xe7, 0x84, 0x3e, 0x30, 0xa1, 0x1e, 0xb5, 0x6e, 0x65, 0xe9, 0xad, 0xa7, 0x96,
|
||||
};
|
||||
|
||||
u8 kof2000_type1_t03[256] = {
|
||||
static constexpr u8 type1High[256] = {
|
||||
0x9a, 0x2f, 0xcc, 0x4e, 0x40, 0x69, 0xac, 0xca, 0xa5, 0x7b, 0x0a, 0x61, 0x91, 0x0d, 0x55, 0x74,
|
||||
0xcd, 0x8b, 0x0b, 0x80, 0x09, 0x5e, 0x38, 0xc7, 0xda, 0xbf, 0xf5, 0x37, 0x23, 0x31, 0x33, 0xe9,
|
||||
0xae, 0x87, 0xe5, 0xfa, 0x6e, 0x5c, 0xad, 0xf4, 0x76, 0x62, 0x9f, 0x2e, 0x01, 0xe2, 0xf6, 0x47,
|
||||
@@ -229,7 +240,7 @@ u8 kof2000_type1_t03[256] = {
|
||||
0xaf, 0x17, 0xf8, 0xfb, 0x60, 0x50, 0xeb, 0x4b, 0x99, 0x63, 0xba, 0xb2, 0x71, 0xcf, 0x10, 0xbe,
|
||||
};
|
||||
|
||||
u8 kof2000_type1_t12[256] = {
|
||||
static constexpr u8 type1Low[256] = {
|
||||
0xda, 0xa7, 0xd6, 0x6e, 0x2f, 0x5e, 0xf0, 0x3f, 0xa4, 0xce, 0xd3, 0xfd, 0x46, 0x2a, 0xac, 0xc9,
|
||||
0xbe, 0xeb, 0x9f, 0xd5, 0x3c, 0x61, 0x96, 0x11, 0xd0, 0x38, 0xca, 0x06, 0xed, 0x1b, 0x65, 0xe7,
|
||||
0x23, 0xdd, 0xd9, 0x05, 0xbf, 0x5b, 0x5d, 0xa5, 0x95, 0x00, 0xec, 0xf1, 0x01, 0xa9, 0xa6, 0xfc,
|
||||
@@ -248,7 +259,7 @@ u8 kof2000_type1_t12[256] = {
|
||||
0x1f, 0x49, 0x8f, 0x4c, 0xc1, 0x44, 0x17, 0x12, 0x6f, 0x32, 0xb7, 0x3e, 0x74, 0x1e, 0x21, 0x6a,
|
||||
};
|
||||
|
||||
u8 kof2000_address_8_15_xor1[256] = {
|
||||
static constexpr u8 address8To15Xor1[256] = {
|
||||
0xfc, 0x9b, 0x1c, 0x35, 0x72, 0x53, 0xd6, 0x7d, 0x84, 0xa4, 0xc5, 0x93, 0x7b, 0xe7, 0x47, 0xd5,
|
||||
0x24, 0xa2, 0xfa, 0x19, 0x0c, 0xb1, 0x8c, 0xb9, 0x9d, 0xd8, 0x59, 0x4f, 0x3c, 0xb2, 0x78, 0x4a,
|
||||
0x2a, 0x96, 0x9a, 0xf1, 0x1f, 0x22, 0xa8, 0x5b, 0x67, 0xa3, 0x0f, 0x00, 0xfb, 0xdf, 0xeb, 0x0a,
|
||||
@@ -267,7 +278,7 @@ u8 kof2000_address_8_15_xor1[256] = {
|
||||
0x2e, 0x08, 0x1d, 0xf9, 0xb6, 0xa6, 0x64, 0x07, 0x82, 0x38, 0x98, 0x3a, 0x9f, 0xde, 0xbc, 0xd9,
|
||||
};
|
||||
|
||||
u8 kof2000_address_8_15_xor2[256] = {
|
||||
static constexpr u8 address8To15Xor2[256] = {
|
||||
0x00, 0xbe, 0x06, 0x5a, 0xfa, 0x42, 0x15, 0xf2, 0x3f, 0x0a, 0x84, 0x93, 0x4e, 0x78, 0x3b, 0x89,
|
||||
0x32, 0x98, 0xa2, 0x87, 0x73, 0xdd, 0x26, 0xe5, 0x05, 0x71, 0x08, 0x6e, 0x9b, 0xe0, 0xdf, 0x9e,
|
||||
0xfc, 0x83, 0x81, 0xef, 0xb2, 0xc0, 0xc3, 0xbf, 0xa7, 0x6d, 0x1b, 0x95, 0xed, 0xb9, 0x3e, 0x13,
|
||||
@@ -286,7 +297,7 @@ u8 kof2000_address_8_15_xor2[256] = {
|
||||
0xe2, 0xc9, 0xaa, 0x4b, 0x8d, 0x4f, 0xe6, 0x64, 0xda, 0x74, 0xb6, 0x72, 0x57, 0x62, 0xfd, 0x58,
|
||||
};
|
||||
|
||||
u8 kof2000_address_16_23_xor1[256] = {
|
||||
static constexpr u8 address16To23Xor1[256] = {
|
||||
0x45, 0x9f, 0x6e, 0x2f, 0x28, 0xbc, 0x5e, 0x6d, 0xda, 0xb5, 0x0d, 0xb8, 0xc0, 0x8e, 0xa2, 0x32,
|
||||
0xee, 0xcd, 0x8d, 0x48, 0x8c, 0x27, 0x14, 0xeb, 0x65, 0xd7, 0xf2, 0x93, 0x99, 0x90, 0x91, 0xfc,
|
||||
0x5f, 0xcb, 0xfa, 0x75, 0x3f, 0x26, 0xde, 0x72, 0x33, 0x39, 0xc7, 0x1f, 0x88, 0x79, 0x73, 0xab,
|
||||
@@ -305,7 +316,7 @@ u8 kof2000_address_16_23_xor1[256] = {
|
||||
0xdf, 0x83, 0x74, 0xf5, 0x38, 0x20, 0xfd, 0xad, 0xc4, 0xb9, 0x55, 0x51, 0xb0, 0xef, 0x00, 0x57,
|
||||
};
|
||||
|
||||
u8 kof2000_address_16_23_xor2[256] = {
|
||||
static constexpr u8 address16To23Xor2[256] = {
|
||||
0x00, 0xb8, 0xf0, 0x34, 0xca, 0x21, 0x3c, 0xf9, 0x01, 0x8e, 0x75, 0x70, 0xec, 0x13, 0x27, 0x96,
|
||||
0xf4, 0x5b, 0x88, 0x1f, 0xeb, 0x4a, 0x7d, 0x9d, 0xbe, 0x02, 0x14, 0xaf, 0xa2, 0x06, 0xc6, 0xdb,
|
||||
0x35, 0x6b, 0x74, 0x45, 0x7b, 0x29, 0xd2, 0xfe, 0xb6, 0x15, 0xd0, 0x8a, 0xa9, 0x2d, 0x19, 0xf6,
|
||||
@@ -324,7 +335,7 @@ u8 kof2000_address_16_23_xor2[256] = {
|
||||
0x3f, 0xb7, 0x22, 0x97, 0x09, 0xe0, 0xa5, 0x20, 0x23, 0x2c, 0x72, 0xd6, 0x39, 0xab, 0x0a, 0xee,
|
||||
};
|
||||
|
||||
u8 kof2000_address_0_7_xor[256] = {
|
||||
static constexpr u8 address0To7Xor[256] = {
|
||||
0x26, 0x48, 0x06, 0x9b, 0x21, 0xa9, 0x1b, 0x76, 0xc9, 0xf8, 0xb4, 0x67, 0xe4, 0xff, 0x99, 0xf7,
|
||||
0x15, 0x9e, 0x62, 0x00, 0x72, 0x4d, 0xa0, 0x4f, 0x02, 0xf1, 0xea, 0xef, 0x0b, 0xf3, 0xeb, 0xa6,
|
||||
0x93, 0x78, 0x6f, 0x7c, 0xda, 0xd4, 0x7b, 0x05, 0xe9, 0xc6, 0xd6, 0xdb, 0x50, 0xce, 0xd2, 0x01,
|
||||
@@ -343,7 +354,11 @@ u8 kof2000_address_0_7_xor[256] = {
|
||||
0x32, 0x3e, 0x45, 0xaf, 0x1e, 0x43, 0x44, 0x8c, 0x53, 0x86, 0x6b, 0xee, 0xa8, 0x8a, 0x8f, 0x17,
|
||||
};
|
||||
|
||||
u8 m1_address_8_15_xor[256] = {
|
||||
}
|
||||
|
||||
namespace Music {
|
||||
|
||||
static constexpr u8 address8To15Xor[256] = {
|
||||
0x0a, 0x72, 0xb7, 0xaf, 0x67, 0xde, 0x1d, 0xb1, 0x78, 0xc4, 0x4f, 0xb5, 0x4b, 0x18, 0x76, 0xdd,
|
||||
0x11, 0xe2, 0x36, 0xa1, 0x82, 0x03, 0x98, 0xa0, 0x10, 0x5f, 0x3f, 0xd6, 0x1f, 0x90, 0x6a, 0x0b,
|
||||
0x70, 0xe0, 0x64, 0xcb, 0x9f, 0x38, 0x8b, 0x53, 0x04, 0xca, 0xf8, 0xd0, 0x07, 0x68, 0x56, 0x32,
|
||||
@@ -362,7 +377,7 @@ u8 m1_address_8_15_xor[256] = {
|
||||
0x7d, 0x1a, 0x02, 0x65, 0x54, 0x5e, 0x19, 0xcc, 0xdc, 0xdb, 0x73, 0xed, 0xad, 0x59, 0x2f, 0xa3,
|
||||
};
|
||||
|
||||
u8 m1_address_0_7_xor[256] = {
|
||||
static constexpr u8 address0To7Xor[256] = {
|
||||
0xf4, 0xbc, 0x02, 0xf7, 0x2c, 0x3d, 0xe8, 0xd9, 0x50, 0x62, 0xec, 0xbd, 0x53, 0x73, 0x79, 0x61,
|
||||
0x00, 0x34, 0xcf, 0xa2, 0x63, 0x28, 0x90, 0xaf, 0x44, 0x3b, 0xc5, 0x8d, 0x3a, 0x46, 0x07, 0x70,
|
||||
0x66, 0xbe, 0xd8, 0x8b, 0xe9, 0xa0, 0x4b, 0x98, 0xdc, 0xdf, 0xe2, 0x16, 0x74, 0xf1, 0x37, 0xf5,
|
||||
@@ -381,22 +396,6 @@ u8 m1_address_0_7_xor[256] = {
|
||||
0x76, 0xea, 0x5c, 0x82, 0x1a, 0x4f, 0xaa, 0xca, 0xe1, 0x0b, 0x4e, 0xcb, 0x6a, 0xef, 0xd1, 0xd6,
|
||||
};
|
||||
|
||||
#define BITSWAP16(n, \
|
||||
bit15, bit14, bit13, bit12, bit11, bit10, bit09, bit08, \
|
||||
bit07, bit06, bit05, bit04, bit03, bit02, bit01, bit00) \
|
||||
(((((n) >> (bit15)) & 1) << 15) | \
|
||||
((((n) >> (bit14)) & 1) << 14) | \
|
||||
((((n) >> (bit13)) & 1) << 13) | \
|
||||
((((n) >> (bit12)) & 1) << 12) | \
|
||||
((((n) >> (bit11)) & 1) << 11) | \
|
||||
((((n) >> (bit10)) & 1) << 10) | \
|
||||
((((n) >> (bit09)) & 1) << 9) | \
|
||||
((((n) >> (bit08)) & 1) << 8) | \
|
||||
((((n) >> (bit07)) & 1) << 7) | \
|
||||
((((n) >> (bit06)) & 1) << 6) | \
|
||||
((((n) >> (bit05)) & 1) << 5) | \
|
||||
((((n) >> (bit04)) & 1) << 4) | \
|
||||
((((n) >> (bit03)) & 1) << 3) | \
|
||||
((((n) >> (bit02)) & 1) << 2) | \
|
||||
((((n) >> (bit01)) & 1) << 1) | \
|
||||
((((n) >> (bit00)) & 1) << 0))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
#include "cmc-tables.hpp"
|
||||
|
||||
struct CmcContext {
|
||||
const u8* type0High;
|
||||
const u8* type0Low;
|
||||
const u8* type1High;
|
||||
const u8* type1Low;
|
||||
const u8* address8To15Xor1;
|
||||
const u8* address8To15Xor2;
|
||||
const u8* address16To23Xor1;
|
||||
const u8* address16To23Xor2;
|
||||
const u8* address0To7Xor;
|
||||
};
|
||||
|
||||
static auto decryptCmcGraphicsInternal(u8* r0, u8* r1, u8 c0, u8 c1, const u8* table0High, const u8* table0Low,
|
||||
const u8* table1, CmcContext& context, int base, int invert) -> void {
|
||||
u8 tmp = table1[(base & 0xff) ^ context.address0To7Xor[(base >> 8) & 0xff]];
|
||||
u8 xor0 = (table0High[(base >> 8) & 0xff] & 0xfe) | (tmp & 0x01);
|
||||
u8 xor1 = (tmp & 0xfe) | (table0Low[(base >> 8) & 0xff] & 0x01);
|
||||
|
||||
if(invert) {
|
||||
*r0 = c1 ^ xor0;
|
||||
*r1 = c0 ^ xor1;
|
||||
return;
|
||||
}
|
||||
|
||||
*r0 = c0 ^ xor0;
|
||||
*r1 = c1 ^ xor1;
|
||||
}
|
||||
|
||||
static auto reorderCmcMusic(n16 source, const u8 (&bits)[16]) -> n16 {
|
||||
n16 result = 0;
|
||||
for(u32 index : range(16)) result.bit(15 - index) = source.bit(bits[index]);
|
||||
return result;
|
||||
}
|
||||
|
||||
static auto decryptCmcMusicDescramble(u32 address, u16 key) -> u32 {
|
||||
static constexpr u8 positions[8][16] = {
|
||||
{ 4, 5, 9, 6,13,11,12, 0, 8, 3, 2, 1, 7,10,14,15},
|
||||
{12, 6, 0,10,14, 4,13, 5, 3, 2, 9,15,11, 8, 1, 7},
|
||||
{ 9,12,11,13, 5, 2, 0, 4, 1,15, 7,10, 3,14, 6, 8},
|
||||
{ 5,14,12, 1,10, 0, 6,13, 7,11, 4, 3, 9,15, 8, 2},
|
||||
{11, 0, 2, 5,12, 7, 4, 9,10, 8, 3,14,15, 6,13, 1},
|
||||
{13,10, 5, 8, 1,12,14, 6, 2, 9, 0, 7, 4, 3,15,11},
|
||||
{ 2, 4, 7,12, 0, 3, 9,11,14, 1,15, 6, 8,13, 5,10},
|
||||
{ 6, 1,13,15, 8, 5,10,14,11, 4,12, 2, 0, 7, 3, 9},
|
||||
};
|
||||
|
||||
int block = (address >> 16) & 7;
|
||||
int aux = address & 0xffff;
|
||||
|
||||
aux ^= reorderCmcMusic(key, {12,0,2,4,8,15,7,13,10,1,3,6,11,9,14,5});
|
||||
aux = reorderCmcMusic(aux, positions[block]);
|
||||
|
||||
aux ^= CmcTables::Music::address0To7Xor[(aux >> 8) & 0xff];
|
||||
aux ^= CmcTables::Music::address8To15Xor[aux & 0xff] << 8;
|
||||
aux = reorderCmcMusic(aux, {7,15,14,6,5,13,12,4,11,3,10,2,9,1,8,0});
|
||||
|
||||
return (block << 16) | aux;
|
||||
}
|
||||
|
||||
struct Cmc42 : Rom {
|
||||
u8 cmcKey;
|
||||
CmcContext context;
|
||||
|
||||
Cmc42(Cartridge& cartridge, u8 key) : Rom(cartridge), cmcKey(key) {
|
||||
context.type0High = CmcTables::Cmc42::type0High;
|
||||
context.type0Low = CmcTables::Cmc42::type0Low;
|
||||
context.type1High = CmcTables::Cmc42::type1High;
|
||||
context.type1Low = CmcTables::Cmc42::type1Low;
|
||||
context.address8To15Xor1 = CmcTables::Cmc42::address8To15Xor1;
|
||||
context.address8To15Xor2 = CmcTables::Cmc42::address8To15Xor2;
|
||||
context.address16To23Xor1 = CmcTables::Cmc42::address16To23Xor1;
|
||||
context.address16To23Xor2 = CmcTables::Cmc42::address16To23Xor2;
|
||||
context.address0To7Xor = CmcTables::Cmc42::address0To7Xor;
|
||||
}
|
||||
|
||||
auto decryptCromByte(n32 address) -> n8 {
|
||||
u32 wordAddress = address / 4;
|
||||
u32 byteIndex = address & 3;
|
||||
|
||||
int sourceAddress = wordAddress;
|
||||
sourceAddress ^= cmcKey;
|
||||
sourceAddress ^= context.address8To15Xor1[(sourceAddress >> 16) & 0xff] << 8;
|
||||
sourceAddress ^= context.address8To15Xor2[sourceAddress & 0xff] << 8;
|
||||
sourceAddress ^= context.address16To23Xor1[sourceAddress & 0xff] << 16;
|
||||
sourceAddress ^= context.address16To23Xor2[(sourceAddress >> 8) & 0xff] << 16;
|
||||
sourceAddress ^= context.address0To7Xor[(sourceAddress >> 8) & 0xff];
|
||||
|
||||
u32 cromSize = crom.size();
|
||||
if(cromSize == 0x3000000) {
|
||||
if(wordAddress < 0x2000000 / 4) sourceAddress &= (0x2000000 / 4) - 1;
|
||||
else sourceAddress = 0x2000000 / 4 + (sourceAddress & ((0x1000000 / 4) - 1));
|
||||
} else if(cromSize == 0x6000000) {
|
||||
if(wordAddress < 0x4000000 / 4) sourceAddress &= (0x4000000 / 4) - 1;
|
||||
else sourceAddress = 0x4000000 / 4 + (sourceAddress & ((0x1000000 / 4) - 1));
|
||||
} else {
|
||||
sourceAddress &= (cromSize / 4) - 1;
|
||||
}
|
||||
|
||||
u8 source0 = crom[sourceAddress * 4 + 0];
|
||||
u8 source1 = crom[sourceAddress * 4 + 1];
|
||||
u8 source2 = crom[sourceAddress * 4 + 2];
|
||||
u8 source3 = crom[sourceAddress * 4 + 3];
|
||||
|
||||
u8 decoded[4];
|
||||
decryptCmcGraphicsInternal(&decoded[0], &decoded[3], source0, source3,
|
||||
context.type0High, context.type0Low, context.type1High, context, sourceAddress, (sourceAddress >> 8) & 1);
|
||||
decryptCmcGraphicsInternal(&decoded[1], &decoded[2], source1, source2,
|
||||
context.type0Low, context.type0High, context.type1Low, context, sourceAddress,
|
||||
((sourceAddress >> 16) ^ context.address16To23Xor2[(sourceAddress >> 8) & 0xff]) & 1);
|
||||
|
||||
return decoded[byteIndex];
|
||||
}
|
||||
|
||||
auto readC(n32 address) -> n8 override {
|
||||
return decryptCromByte(address);
|
||||
}
|
||||
|
||||
auto readS(n32 address) -> n8 override {
|
||||
u32 cromSize = crom.size();
|
||||
u32 sromSize = srom.size();
|
||||
u32 i = address;
|
||||
u32 cromAddr = (cromSize - sromSize) + ((i & ~0x1f) + ((i & 7) << 2) + ((~i & 8) >> 2) + ((i & 0x10) >> 4));
|
||||
return decryptCromByte(cromAddr);
|
||||
}
|
||||
};
|
||||
|
||||
struct Cmc50 : Cmc42 {
|
||||
u16 musicKey;
|
||||
u32 fixBank;
|
||||
|
||||
Cmc50(Cartridge& cartridge, u8 key, u32 fixBank = 0) : Cmc42(cartridge, key), fixBank(fixBank) {
|
||||
context.type0High = CmcTables::Cmc50::type0High;
|
||||
context.type0Low = CmcTables::Cmc50::type0Low;
|
||||
context.type1High = CmcTables::Cmc50::type1High;
|
||||
context.type1Low = CmcTables::Cmc50::type1Low;
|
||||
context.address8To15Xor1 = CmcTables::Cmc50::address8To15Xor1;
|
||||
context.address8To15Xor2 = CmcTables::Cmc50::address8To15Xor2;
|
||||
context.address16To23Xor1 = CmcTables::Cmc50::address16To23Xor1;
|
||||
context.address16To23Xor2 = CmcTables::Cmc50::address16To23Xor2;
|
||||
context.address0To7Xor = CmcTables::Cmc50::address0To7Xor;
|
||||
}
|
||||
|
||||
auto load() -> void override {
|
||||
Rom::load();
|
||||
musicKey = 0;
|
||||
for(u32 i = 0; i < 0x10000; i++) musicKey += mrom[i];
|
||||
}
|
||||
|
||||
auto fixBankType() -> u32 override { return srom.size() > 0x20000 ? fixBank : 0; }
|
||||
|
||||
auto readM(n32 address) -> n8 override {
|
||||
return mrom[decryptCmcMusicDescramble(address, musicKey)];
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
//PRO-CT0 (ALPHA-8921 / SNK-9201)
|
||||
|
||||
struct ProCT0 {
|
||||
auto clock(n1 load, n1 h, n32 c) -> void {
|
||||
if(load) {
|
||||
sr = c;
|
||||
} else if(h) {
|
||||
sr = sr.bit(24,29) << 26 | sr.bit(16,21) << 18 | sr.bit(8,13) << 10 | sr.bit(0,5) << 2;
|
||||
} else {
|
||||
sr = sr.bit(26,31) << 24 | sr.bit(18,23) << 16 | sr.bit(10,15) << 8 | sr.bit(2,7);
|
||||
}
|
||||
}
|
||||
|
||||
auto output(n1 even, n1 h) const -> std::pair<n4, n4> {
|
||||
n4 gad, gbd;
|
||||
if(h) {
|
||||
gbd = sr.bit(30) << 3 | sr.bit(22) << 2 | sr.bit(14) << 1 | sr.bit(6);
|
||||
gad = sr.bit(31) << 3 | sr.bit(23) << 2 | sr.bit(15) << 1 | sr.bit(7);
|
||||
} else {
|
||||
gbd = sr.bit(25) << 3 | sr.bit(17) << 2 | sr.bit(9) << 1 | sr.bit(1);
|
||||
gad = sr.bit(24) << 3 | sr.bit(16) << 2 | sr.bit(8) << 1 | sr.bit(0);
|
||||
}
|
||||
if(even) std::swap(gad, gbd);
|
||||
return {gad, gbd};
|
||||
}
|
||||
|
||||
n32 sr;
|
||||
};
|
||||
|
||||
struct FatFury2 : Rom {
|
||||
using Rom::Rom;
|
||||
|
||||
static auto interleave(n16 value) -> n16 {
|
||||
static const u8 bits[16] = {15,11,14,10,13,9,12,8,7,3,6,2,5,1,4,0};
|
||||
return smaSwap(value, bits, 16);
|
||||
}
|
||||
|
||||
auto readP(n1 upper, n1 lower, n24 address, n16 data) -> n16 override {
|
||||
if(address <= 0x0fffff) return prom[address >> 1];
|
||||
if(address >= 0x200000 && address <= 0x2fffff) {
|
||||
auto [gad, gbd] = proct0.output(address.bit(2), address.bit(3));
|
||||
return gbd.bit(0,1) << 6 | gbd.bit(2,3) << 4 | gad.bit(0,1) << 2 | gad.bit(2,3);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
auto writeP(n1 upper, n1 lower, n24 address, n16 data) -> void override {
|
||||
if(address < 0x200000 || address > 0x2fffff) return;
|
||||
n32 c = interleave(address.bit(4,19)) << 16 | interleave(data);
|
||||
proct0.clock(address.bit(1), address.bit(3), c);
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
Rom::power();
|
||||
proct0 = {};
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
Rom::serialize(s);
|
||||
s(proct0.sr);
|
||||
}
|
||||
|
||||
ProCT0 proct0;
|
||||
};
|
||||
@@ -1,31 +1,15 @@
|
||||
struct JockeyGP : Interface {
|
||||
using Interface::Interface;
|
||||
|
||||
Memory::Readable<n16> prom;
|
||||
Memory::Readable<n8 > mrom;
|
||||
Memory::Readable<n8 > crom;
|
||||
Memory::Readable<n8 > srom;
|
||||
Memory::Readable<n8 > vromA;
|
||||
Memory::Readable<n8 > vromB;
|
||||
struct JockeyGP : Cmc50 {
|
||||
Memory::Writable<n16> ram;
|
||||
|
||||
JockeyGP(Cartridge& cartridge) : Cmc50(cartridge, 0xac) {}
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(prom, "program.rom");
|
||||
Interface::load(mrom, "music.rom");
|
||||
Interface::load(crom, "character.rom");
|
||||
Interface::load(srom, "static.rom");
|
||||
Interface::load(vromA, "voice-a.rom");
|
||||
Interface::load(vromB, "voice-b.rom");
|
||||
Cmc50::load();
|
||||
ram.allocate(0x100000 >> 1);
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
prom.reset();
|
||||
mrom.reset();
|
||||
crom.reset();
|
||||
srom.reset();
|
||||
vromA.reset();
|
||||
vromB.reset();
|
||||
Cmc50::unload();
|
||||
ram.reset();
|
||||
}
|
||||
|
||||
@@ -39,31 +23,9 @@ struct JockeyGP : Interface {
|
||||
if(lower && address >= 0x200000 && address <= 0x2fffff) ram[address >> 1] = data;
|
||||
}
|
||||
|
||||
auto readM(n32 address) -> n8 override {
|
||||
return mrom[address];
|
||||
}
|
||||
|
||||
auto readC(n32 address) -> n8 override {
|
||||
return crom[address];
|
||||
}
|
||||
|
||||
auto readS(n32 address) -> n8 override {
|
||||
return srom[address];
|
||||
}
|
||||
|
||||
auto readVA(n32 address) -> n8 override {
|
||||
return vromA[address];
|
||||
}
|
||||
|
||||
auto readVB(n32 address) -> n8 override {
|
||||
return vromB ? vromB[address] : vromA[address];
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
}
|
||||
auto power() -> void override {}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(ram);
|
||||
}
|
||||
};
|
||||
|
||||
Loaded 100 of 284 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user