mirror of
https://github.com/ares-emulator/ares.git
synced 2026-11-09 09:19:42 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39025af1fe | ||
|
|
5cdefb088d | ||
|
|
d0253599a6 | ||
|
|
1e83da0241 | ||
|
|
ed0a1c8852 | ||
|
|
dd9c728a12 | ||
|
|
7ec1e1430c | ||
|
|
16379aabff | ||
|
|
11f24b47c3 | ||
|
|
32f205975c | ||
|
|
21ca9cf76e | ||
|
|
d32b3ffe45 | ||
|
|
5864382c67 | ||
|
|
f392e4f428 | ||
|
|
d0075978f8 | ||
|
|
c1e5777dc1 | ||
|
|
ec6c9fc330 | ||
|
|
9bc95db1f0 | ||
|
|
6d80a8e940 | ||
|
|
2a0f9651b9 | ||
|
|
51a10b0bd0 | ||
|
|
7e2f490fd0 | ||
|
|
2063393f20 | ||
|
|
4c15ed753e | ||
|
|
704fc91b9c | ||
|
|
757d74bac0 | ||
|
|
ff36889d47 | ||
|
|
3d10cd34d6 | ||
|
|
acaa1cabb5 | ||
|
|
e8f38d15f7 | ||
|
|
527aaefef1 | ||
|
|
a938ef8aa8 | ||
|
|
91658b989a | ||
|
|
801eaa4088 | ||
|
|
d6b2ca53e2 | ||
|
|
d823641ccc | ||
|
|
dec675896d | ||
|
|
e2caf175d6 | ||
|
|
322c99ca75 | ||
|
|
b28bb3dbd7 | ||
|
|
3f4334e5ff | ||
|
|
99a40e43ef | ||
|
|
9bbe36d73c | ||
|
|
4bae7965f9 | ||
|
|
9e4b3795f1 | ||
|
|
a82913c1f9 | ||
|
|
b7db1c074b | ||
|
|
1478637209 | ||
|
|
432c4e3b2a | ||
|
|
18c84a83e4 | ||
|
|
c4bc588082 | ||
|
|
ed3083db8a | ||
|
|
6d5ef628e8 | ||
|
|
6e8f4f224a | ||
|
|
4bdeb3727f | ||
|
|
20a3aac83b | ||
|
|
2950d3fe44 | ||
|
|
b55c5010f3 | ||
|
|
f893afca89 | ||
|
|
0d131bf4d8 | ||
|
|
7e99155f8e | ||
|
|
c95dc1bad7 | ||
|
|
b85395fe7b | ||
|
|
91c46dbad4 | ||
|
|
6a253b17af | ||
|
|
4116855924 | ||
|
|
1146c0def3 | ||
|
|
e176d8b3ee | ||
|
|
9b5e339cc1 | ||
|
|
8f626e304a | ||
|
|
bbe9a9b0a4 | ||
|
|
c00e402eeb | ||
|
|
5e6afeaa5e | ||
|
|
a3222abe79 | ||
|
|
b0ee66d4ad | ||
|
|
6567d9dcf0 | ||
|
|
9a8e7f8b6e | ||
|
|
1c9d38895d | ||
|
|
cd11421d72 | ||
|
|
079295e2ca | ||
|
|
2a819403db | ||
|
|
df031a7d4c | ||
|
|
0dd3edebe0 | ||
|
|
f9d38f5daf |
No files matched your search
@@ -8,6 +8,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
program:
|
||||
- ares
|
||||
@@ -77,6 +78,7 @@ jobs:
|
||||
sudo apt-get install libsdl2-dev libgtk-3-dev libao-dev libopenal-dev
|
||||
- name: "macOS: Import Certificate"
|
||||
if: runner.os == 'macOS' && github.event_name != 'pull_request'
|
||||
continue-on-error: ${{ github.repository != 'ares-emulator/ares' }}
|
||||
uses: wpilibsuite/import-signing-certificate@2ac4f44d28045073d23153256efbb4c4b2d8aa22 # Don't use rolling branch for security reasons
|
||||
with:
|
||||
certificate-data: ${{ secrets.MACOS_CERTIFICATE_DATA }}
|
||||
@@ -85,7 +87,7 @@ jobs:
|
||||
keychain-password: ${{ secrets.MACOS_KEYCHAIN_PASSWORD }}
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
- name: "macOS: recover cache"
|
||||
- name: "macOS: recover MoltenVK cache"
|
||||
if: runner.os == 'macOS'
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
@@ -93,12 +95,23 @@ jobs:
|
||||
with:
|
||||
path: thirdparty/MoltenVK/MoltenVK
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('thirdparty/MoltenVK/HEAD') }}
|
||||
- name: "macOS: recover SDL cache"
|
||||
if: runner.os == 'macOS'
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
cache-name: sdl
|
||||
with:
|
||||
path: thirdparty/SDL/SDL
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('thirdparty/SDL/HEAD') }}
|
||||
- name: Install macOS Dependencies
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
brew install make ninja sdl2
|
||||
brew install make ninja cmake
|
||||
pip install --no-input setuptools
|
||||
echo "MAKE=gmake" >> $GITHUB_ENV
|
||||
pushd thirdparty/SDL
|
||||
./build-SDL.sh
|
||||
popd
|
||||
pushd thirdparty/MoltenVK
|
||||
./build-moltenvk.sh
|
||||
popd
|
||||
@@ -112,7 +125,7 @@ jobs:
|
||||
run: ${MAKE:-make} -j4 -C desktop-ui build=optimized local=false compiler="${{ matrix.platform.compiler }}" windres="${{ matrix.platform.windres }}"
|
||||
- name: "macOS: Make universal app"
|
||||
if: runner.os == 'macOS'
|
||||
run: scripts/macos-make-universal.sh
|
||||
run: scripts/macos-make-universal.sh build=optimized local=false
|
||||
env:
|
||||
MAKEFLAGS: -j3
|
||||
MACOS_CERTIFICATE_NAME: ${{ secrets.MACOS_CERTIFICATE_NAME }}
|
||||
@@ -130,6 +143,7 @@ jobs:
|
||||
timestampUrl: 'http://timestamp.digicert.com'
|
||||
- name: "macOS: notarize"
|
||||
if: runner.os == 'macOS' && github.event_name != 'pull_request'
|
||||
continue-on-error: ${{ github.repository != 'ares-emulator/ares' }}
|
||||
run: |
|
||||
ditto -c -k --keepParent desktop-ui/out/ares.app /tmp/ares.zip
|
||||
xcrun notarytool submit /tmp/ares.zip --apple-id "$MACOS_NOTARIZATION_USERNAME" --password "$MACOS_NOTARIZATION_PASSWORD" --team-id "$MACOS_NOTARIZATION_TEAMID" --wait
|
||||
|
||||
@@ -34,7 +34,8 @@ ares supports additional audio drivers besides the ALSA drivers included above.
|
||||
By default, GTK3 is used, but support for GTK2 is available. You will need to install the additional package `libgtk2.0-dev` as well as specifying the command line option `hiro=gtk2` at compile time.
|
||||
|
||||
###### SDL2 for input
|
||||
If you would like to use SDL for input, you will need to install the following the `libsdl2-dev` and `libsdl2-0.0` packages and perform a clean build of ares. You should then be able to select SDL for input in the Settings > Drivers menu.
|
||||
If you would like to use SDL for input (e.g. for using a controller), you will need to install the `libsdl2-dev` and `libsdl2-2.0-0` packages and perform a clean build of ares.
|
||||
You should then be able to select SDL for input in the Settings > Drivers menu.
|
||||
|
||||
##### Building with clang
|
||||
|
||||
|
||||
@@ -13,10 +13,6 @@ ares.objects := ares ares-fixed-allocator
|
||||
$(object.path)/ares.o: $(ares.path)/ares/ares.cpp
|
||||
$(object.path)/ares-fixed-allocator.o: $(ares.path)/ares/memory/fixed-allocator.cpp
|
||||
|
||||
ifeq ($(platform),macos)
|
||||
ares.dylibs :=
|
||||
endif
|
||||
|
||||
ifeq ($(vulkan),true)
|
||||
flags += -DVULKAN
|
||||
endif
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
inline auto CPU::readBus(n16 address) -> n8 {
|
||||
address &= 0x1fff;
|
||||
if(auto result = platform->cheat(address)) return *result;
|
||||
|
||||
n8 data = cartridge.read(address);
|
||||
|
||||
if(address.bit(12) == 0 && address.bit(7) == 0) {
|
||||
|
||||
+2
-1
@@ -72,8 +72,9 @@ auto TIA::write(n8 address, n8 data) -> void {
|
||||
|
||||
|
||||
auto TIA::vsync(n1 state) -> void {
|
||||
if(io.vsync && !state) {
|
||||
if(!io.vsync && state) {
|
||||
frame();
|
||||
tia.io.vcounter = 0;
|
||||
}
|
||||
|
||||
io.vsync = state;
|
||||
|
||||
+10
-15
@@ -13,12 +13,12 @@ TIA tia;
|
||||
auto TIA::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Object>("TIA");
|
||||
|
||||
screen = node->append<Node::Video::Screen>("Screen", 160, vlines());
|
||||
screen = node->append<Node::Video::Screen>("Screen", 180, displayHeight());
|
||||
screen->colors(1 << 7, {&TIA::color, this});
|
||||
screen->setSize(160, displayHeight());
|
||||
screen->setScale(1.0, 1.0);
|
||||
screen->setAspect(12.0, 7.0);
|
||||
screen->setViewport(0, 0, 160, displayHeight());
|
||||
screen->setSize(180, displayHeight());
|
||||
screen->setScale(2.0, 1.0);
|
||||
Region::PAL() ? screen->setAspect(38.0, 45.0) : screen->setAspect(4.0, 5.0);
|
||||
screen->setViewport(0, 0, 180, displayHeight());
|
||||
|
||||
stream = node->append<Node::Audio::Stream>("Audio");
|
||||
stream->setChannels(1);
|
||||
@@ -46,7 +46,8 @@ auto TIA::scanline() -> void {
|
||||
for(io.hcounter = 0; io.hcounter < 228; io.hcounter++) {
|
||||
writeQueue.step();
|
||||
auto x = io.hcounter - 68;
|
||||
if(x >= 0 && io.vcounter < vlines()) pixel(x);
|
||||
auto y = io.vcounter - voffset();
|
||||
if(x >= 0 && y > 0 && y < vlines()) pixel(x);
|
||||
runAudio();
|
||||
step();
|
||||
if(io.hcounter == 0) cpu.io.rdyLine = 1;
|
||||
@@ -60,7 +61,9 @@ auto TIA::scanline() -> void {
|
||||
}
|
||||
|
||||
auto TIA::pixel(n8 x) -> void {
|
||||
auto output = screen->pixels().data() + (io.vcounter * 160);
|
||||
auto y = io.vcounter - voffset();
|
||||
auto output = screen->pixels().data() + (y * 180) + 10;
|
||||
if(y >= displayHeight()) return;
|
||||
|
||||
// Output only black during vblank, or for the first 8px of a scanline where hmove was triggered
|
||||
if (io.vblank || (io.hmoveTriggered == io.vcounter && x < 8)) {
|
||||
@@ -190,14 +193,6 @@ auto TIA::runBall(n8 x) -> n1 {
|
||||
}
|
||||
|
||||
auto TIA::frame() -> void {
|
||||
auto height = io.vcounter;
|
||||
|
||||
if(height <= 0) {
|
||||
debug(unusual, "invalid screen height");
|
||||
height = vlines();
|
||||
}
|
||||
|
||||
screen->setViewport(0, 0, 160, height);
|
||||
screen->frame();
|
||||
scheduler.exit(Event::Frame);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ struct TIA : Thread {
|
||||
Node::Audio::Stream stream;
|
||||
|
||||
auto vlines() const -> u32 { return Region::NTSC() ? 262 : 312; }
|
||||
auto displayHeight() const -> u32 { return Region::NTSC() ? 192 : 228; }
|
||||
auto displayHeight() const -> u32 { return Region::PAL() ? 243 : 228; }
|
||||
auto voffset() const -> s32 { return Region::PAL() ? 40 : 20; }
|
||||
|
||||
//tia.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
|
||||
@@ -23,7 +23,7 @@ auto TIA::WriteQueue::step() -> void {
|
||||
|
||||
auto TIA::WriteQueue::QueueItem::commit() -> void {
|
||||
switch(address) {
|
||||
case 0x01: tia.io.vblank = data.bit(1); if(tia.io.vblank == 0) { tia.io.vcounter = 0; } return; // VBLANK
|
||||
case 0x01: tia.io.vblank = data.bit(1); return; // VBLANK
|
||||
case 0x0b: tia.player[0].reflect = data.bit(3); return; // REFP0
|
||||
case 0x0c: tia.player[1].reflect = data.bit(3); return; // REFP1
|
||||
case 0x0d: tia.playfield.graphics.bit(0, 3) = data.bit(4, 7); return; // PF0
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ Platform* platform = nullptr;
|
||||
bool _runAhead = false;
|
||||
|
||||
const string Name = "ares";
|
||||
const string Version = "134";
|
||||
const string Version = "136";
|
||||
const string Copyright = "ares team, Near";
|
||||
const string License = "ISC";
|
||||
const string LicenseURI = "https://opensource.org/licenses/ISC";
|
||||
|
||||
@@ -12,10 +12,11 @@ struct Tracer : Debugger {
|
||||
auto file() const -> bool { return _file; }
|
||||
auto autoLineBreak() const -> bool { return _autoLineBreak; }
|
||||
|
||||
auto setToggle(function<void ()> toggle) -> void { _toggle = toggle; }
|
||||
auto setComponent(string component) -> void { _component = component; }
|
||||
auto setPrefix(bool prefix) -> void { _prefix = prefix; }
|
||||
auto setTerminal(bool terminal) -> void { _terminal = terminal; }
|
||||
auto setFile(bool file) -> void { _file = file; }
|
||||
auto setTerminal(bool terminal) -> void { _terminal = terminal; if(_toggle) _toggle(); }
|
||||
auto setFile(bool file) -> void { _file = file; if(_toggle) _toggle(); }
|
||||
auto setAutoLineBreak(bool autoLineBreak) -> void { _autoLineBreak = autoLineBreak; }
|
||||
|
||||
auto serialize(string& output, string depth) -> void override {
|
||||
@@ -35,6 +36,7 @@ struct Tracer : Debugger {
|
||||
}
|
||||
|
||||
protected:
|
||||
function<void ()> _toggle;
|
||||
string _component;
|
||||
bool _prefix = false;
|
||||
bool _terminal = false;
|
||||
|
||||
@@ -76,6 +76,11 @@ auto Screen::setViewport(u32 x, u32 y, u32 width, u32 height) -> void {
|
||||
_viewportHeight = height;
|
||||
}
|
||||
|
||||
auto Screen::setOverscan(bool overscan) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_overscan = overscan;
|
||||
}
|
||||
|
||||
auto Screen::setSize(u32 width, u32 height) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_width = width;
|
||||
|
||||
@@ -16,6 +16,7 @@ struct Screen : Video {
|
||||
auto scaleY() const -> f64 { return _scaleY; }
|
||||
auto aspectX() const -> f64 { return _aspectX; }
|
||||
auto aspectY() const -> f64 { return _aspectY; }
|
||||
auto overscan() const -> bool { return _overscan; }
|
||||
auto colors() const -> u32 { return _colors; }
|
||||
auto pixels(bool frame = 0) -> array_span<u32>;
|
||||
|
||||
@@ -37,6 +38,7 @@ struct Screen : Video {
|
||||
auto setSize(u32 width, u32 height) -> void;
|
||||
auto setScale(f64 scaleX, f64 scaleY) -> void;
|
||||
auto setAspect(f64 aspectX, f64 aspectY) -> void;
|
||||
auto setOverscan(bool overscan) -> void;
|
||||
|
||||
auto setSaturation(f64 saturation) -> void;
|
||||
auto setGamma(f64 gamma) -> void;
|
||||
@@ -81,6 +83,7 @@ protected:
|
||||
bool _colorBleed = false;
|
||||
bool _colorBleedWidth = 1;
|
||||
bool _interframeBlending = false;
|
||||
bool _overscan = true;
|
||||
u32 _rotation = 0; //counter-clockwise (90 = left, 270 = right)
|
||||
|
||||
function<n64 (n32)> _color;
|
||||
|
||||
@@ -20,6 +20,7 @@ struct Platform {
|
||||
virtual auto video(Node::Video::Screen, const u32* data, u32 pitch, u32 width, u32 height) -> void {}
|
||||
virtual auto audio(Node::Audio::Stream) -> void {}
|
||||
virtual auto input(Node::Input::Input) -> void {}
|
||||
virtual auto cheat(u32 addr) -> maybe<u32> { return nothing; }
|
||||
};
|
||||
|
||||
extern Platform* platform;
|
||||
|
||||
@@ -62,7 +62,7 @@ struct ARM7TDMI {
|
||||
auto thumbInitialize() -> void;
|
||||
|
||||
//instructions-arm.cpp
|
||||
auto armALU(n4 mode, n4 target, n4 source, n32 data) -> void;
|
||||
auto armALU(n4 mode, n4 target, n32 source, n32 data) -> void;
|
||||
auto armMoveToStatus(n4 field, n1 source, n32 data) -> void;
|
||||
|
||||
auto armInstructionBranch(i24, n1) -> void;
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
auto ARM7TDMI::armALU(n4 mode, n4 d, n4 n, n32 rm) -> void {
|
||||
n32 rn = r(n);
|
||||
|
||||
auto ARM7TDMI::armALU(n4 mode, n4 d, n32 rn, n32 rm) -> void {
|
||||
switch(mode) {
|
||||
case 0: r(d) = BIT(rn & rm); break; //AND
|
||||
case 1: r(d) = BIT(rn ^ rm); break; //EOR
|
||||
@@ -64,14 +62,16 @@ auto ARM7TDMI::armInstructionBranchExchangeRegister
|
||||
|
||||
auto ARM7TDMI::armInstructionDataImmediate
|
||||
(n8 immediate, n4 shift, n4 d, n4 n, n1 save, n4 mode) -> void {
|
||||
n32 rn = r(n);
|
||||
n32 data = immediate;
|
||||
carry = cpsr().c;
|
||||
if(shift) data = ROR(data, shift << 1);
|
||||
armALU(mode, d, n, data);
|
||||
armALU(mode, d, rn, data);
|
||||
}
|
||||
|
||||
auto ARM7TDMI::armInstructionDataImmediateShift
|
||||
(n4 m, n2 type, n5 shift, n4 d, n4 n, n1 save, n4 mode) -> void {
|
||||
n32 rn = r(n);
|
||||
n32 rm = r(m);
|
||||
carry = cpsr().c;
|
||||
|
||||
@@ -82,12 +82,13 @@ auto ARM7TDMI::armInstructionDataImmediateShift
|
||||
case 3: rm = shift ? ROR(rm, shift) : RRX(rm); break;
|
||||
}
|
||||
|
||||
armALU(mode, d, n, rm);
|
||||
armALU(mode, d, rn, rm);
|
||||
}
|
||||
|
||||
auto ARM7TDMI::armInstructionDataRegisterShift
|
||||
(n4 m, n2 type, n4 s, n4 d, n4 n, n1 save, n4 mode) -> void {
|
||||
n8 rs = r(s) + (s == 15 ? 4 : 0);
|
||||
n32 rn = r(n) + (n == 15 ? 4 : 0);
|
||||
n32 rm = r(m) + (m == 15 ? 4 : 0);
|
||||
carry = cpsr().c;
|
||||
|
||||
@@ -98,7 +99,7 @@ auto ARM7TDMI::armInstructionDataRegisterShift
|
||||
case 3: if(rs) rm = ROR(rm, rs & 31 ? u32(rs & 31) : 32); break;
|
||||
}
|
||||
|
||||
armALU(mode, d, n, rm);
|
||||
armALU(mode, d, rn, rm);
|
||||
}
|
||||
|
||||
auto ARM7TDMI::armInstructionLoadImmediate
|
||||
@@ -167,7 +168,7 @@ auto ARM7TDMI::armInstructionMoveHalfRegister
|
||||
auto ARM7TDMI::armInstructionMoveImmediateOffset
|
||||
(n12 immediate, n4 d, n4 n, n1 mode, n1 writeback, n1 byte, n1 up, n1 pre) -> void {
|
||||
n32 rn = r(n);
|
||||
n32 rd = r(d);
|
||||
n32 rd = r(d) + (d == 15 ? 4 : 0);
|
||||
|
||||
if(pre == 1) rn = up ? rn + immediate : rn - immediate;
|
||||
if(mode == 1) rd = load((byte ? Byte : Word) | Nonsequential, rn);
|
||||
@@ -201,7 +202,7 @@ auto ARM7TDMI::armInstructionMoveMultiple
|
||||
for(u32 m : range(16)) {
|
||||
if(!list.bit(m)) continue;
|
||||
if(mode == 1) r(m) = read(Word | sequential, rn);
|
||||
if(mode == 0) write(Word | sequential, rn, r(m));
|
||||
if(mode == 0) write(Word | sequential, rn, r(m) + (m == 15 ? 4 : 0));
|
||||
rn += 4;
|
||||
sequential = Sequential;
|
||||
}
|
||||
|
||||
@@ -126,17 +126,23 @@ auto ARM7TDMI::thumbInstructionMoveMultiple
|
||||
(n8 list, n3 n, n1 mode) -> void {
|
||||
n32 rn = r(n);
|
||||
|
||||
u32 sequential = Nonsequential;
|
||||
for(u32 m : range(8)) {
|
||||
if(!list.bit(m)) continue;
|
||||
switch(mode) {
|
||||
case 0: write(Word | Nonsequential, rn, r(m)); break; //STMIA
|
||||
case 1: r(m) = read(Word | Nonsequential, rn); break; //LDMIA
|
||||
case 0: write(Word | sequential, rn, r(m)); break; //STMIA
|
||||
case 1: r(m) = read(Word | sequential, rn); break; //LDMIA
|
||||
}
|
||||
rn += 4;
|
||||
sequential = Sequential;
|
||||
}
|
||||
|
||||
if(mode == 0 || !list.bit(n)) r(n) = rn;
|
||||
if(mode == 1) idle();
|
||||
if(mode == 1) {
|
||||
idle();
|
||||
} else {
|
||||
pipeline.nonsequential = true;
|
||||
}
|
||||
}
|
||||
|
||||
auto ARM7TDMI::thumbInstructionMoveRegisterOffset
|
||||
|
||||
@@ -197,9 +197,9 @@ template<u32 Size> auto M68000::instructionASR(n4 count, DataRegister with) -> v
|
||||
|
||||
template<u32 Size> auto M68000::instructionASR(DataRegister from, DataRegister with) -> void {
|
||||
auto count = read<Long>(from) & 63;
|
||||
prefetch();
|
||||
idle((Size != Long ? 2 : 4) + count * 2);
|
||||
auto result = ASR<Size>(read<Size>(with), count);
|
||||
prefetch();
|
||||
write<Size>(with, result);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ auto SH2::hint(P&&... p) const -> string {
|
||||
return {std::forward<P>(p)...};
|
||||
}
|
||||
|
||||
auto SH2::disassembleInstruction() -> string {
|
||||
auto SH2::disassembleInstruction(u16 opcode) -> string {
|
||||
vector<string> s;
|
||||
|
||||
auto registerName = [&](u32 r) -> string {
|
||||
@@ -48,8 +48,6 @@ auto SH2::disassembleInstruction() -> string {
|
||||
return {"0x", hex(PC + (i12)d * 2, 8L)};
|
||||
};
|
||||
|
||||
u16 opcode = readWord(PC - 4);
|
||||
|
||||
#define n (opcode >> 8 & 0x00f)
|
||||
#define m (opcode >> 4 & 0x00f)
|
||||
#define i (opcode >> 0 & 0x0ff)
|
||||
|
||||
@@ -23,6 +23,7 @@ auto SH2::instruction() -> void {
|
||||
}
|
||||
ID = 0;
|
||||
u16 opcode = readWord(PC - 4);
|
||||
instructionPrologue(opcode);
|
||||
execute(opcode);
|
||||
instructionEpilogue();
|
||||
}
|
||||
|
||||
@@ -129,6 +129,10 @@ auto SH2::Recompiler::emit(u32 address) -> Block* {
|
||||
inDelaySlot = 1; //force runtime check on first instruction
|
||||
while(true) {
|
||||
u16 instruction = instructions[index++];
|
||||
if(callInstructionPrologue) {
|
||||
mov32(reg(1), imm(instruction));
|
||||
call(&SH2::instructionPrologue);
|
||||
}
|
||||
auto branch = emitInstruction(instruction);
|
||||
inDelaySlot = branch == Branch::Slot;
|
||||
add64(CCR, CCR, imm(1));
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
namespace ares {
|
||||
|
||||
struct SH2 {
|
||||
virtual auto instructionPrologue(u16 instruction) -> void = 0;
|
||||
virtual auto step(u32 clocks) -> void = 0;
|
||||
virtual auto busReadByte(u32 address) -> u32 = 0;
|
||||
virtual auto busReadWord(u32 address) -> u32 = 0;
|
||||
@@ -189,7 +190,7 @@ struct SH2 {
|
||||
|
||||
//disassembler.cpp
|
||||
template<typename... P> auto hint(P&&...) const -> string;
|
||||
auto disassembleInstruction() -> string;
|
||||
auto disassembleInstruction(u16 opcode) -> string;
|
||||
auto disassembleContext() -> string;
|
||||
|
||||
static constexpr u32 undefined = 0;
|
||||
@@ -299,6 +300,7 @@ struct SH2 {
|
||||
|
||||
static auto mask(u8 address, u8 size) -> u64;
|
||||
|
||||
bool callInstructionPrologue = false;
|
||||
bool inDelaySlot;
|
||||
u32 generation;
|
||||
bump_allocator allocator;
|
||||
|
||||
@@ -13,11 +13,9 @@ auto SH2::INTC::run() -> void {
|
||||
if(self->dmac.pendingIRQ & 1) {
|
||||
self->interrupt(ipra.dmacip, self->dmac.vcrdma[0] & 0x7f);
|
||||
self->dmac.pendingIRQ &= ~1;
|
||||
print("dreq0\n");
|
||||
} else if(self->dmac.pendingIRQ & 2) {
|
||||
self->interrupt(ipra.dmacip, self->dmac.vcrdma[1] & 0x7f);
|
||||
self->dmac.pendingIRQ &= ~2;
|
||||
print("dreq1\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,6 +290,7 @@ auto Z80::instructionINDR() -> void { Q = 1;
|
||||
if(!B) return;
|
||||
wait(5);
|
||||
PC -= 2;
|
||||
WZ = PC + 1;
|
||||
XF = PC.bit(11);
|
||||
YF = PC.bit(13);
|
||||
|
||||
@@ -327,6 +328,7 @@ auto Z80::instructionINIR() -> void { Q = 1;
|
||||
if(!B) return;
|
||||
wait(5);
|
||||
PC -= 2;
|
||||
WZ = PC + 1;
|
||||
XF = PC.bit(11);
|
||||
YF = PC.bit(13);
|
||||
|
||||
@@ -516,6 +518,7 @@ auto Z80::instructionOTDR() -> void { Q = 1;
|
||||
if(!B) return;
|
||||
wait(5);
|
||||
PC -= 2;
|
||||
WZ = PC + 1;
|
||||
XF = PC.bit(11);
|
||||
YF = PC.bit(13);
|
||||
|
||||
@@ -537,6 +540,7 @@ auto Z80::instructionOTIR() -> void { Q = 1;
|
||||
if(!B) return;
|
||||
wait(5);
|
||||
PC -= 2;
|
||||
WZ = PC + 1;
|
||||
XF = PC.bit(11);
|
||||
YF = PC.bit(13);
|
||||
|
||||
|
||||
@@ -14,10 +14,7 @@ auto TMS9918::Background::run(n8 hoffset, n8 voffset) -> void {
|
||||
}
|
||||
|
||||
auto TMS9918::Background::text1(n8 hoffset, n8 voffset) -> void {
|
||||
// Offset by 6px and blank the last 10px to centre the display
|
||||
i32 x = hoffset - 6;
|
||||
if (x < 0) return;
|
||||
if (hoffset > 246) return;
|
||||
i32 x = hoffset;
|
||||
|
||||
n14 nameAddress = (voffset.bit(3,7) * 40) + (x / 6);
|
||||
nameAddress.bit(10,13) = io.nameTableAddress;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
auto TMS9918::DAC::setup(n8 y) -> void {
|
||||
output = self.screen->pixels().data() + y * 256;
|
||||
auto TMS9918::DAC::setup(n9 y) -> void {
|
||||
y = (y + 27) % 243;
|
||||
output = self.screen->pixels().data() + y * 284;
|
||||
for (u32 x : range(284)) output[x] = io.colorBackground;
|
||||
}
|
||||
|
||||
auto TMS9918::DAC::run(n8 x, n8 y) -> void {
|
||||
@@ -8,7 +10,7 @@ auto TMS9918::DAC::run(n8 x, n8 y) -> void {
|
||||
if(self.background.output.color) color = self.background.output.color;
|
||||
if(self.sprite.output.color) color = self.sprite.output.color;
|
||||
}
|
||||
output[x] = color;
|
||||
output[(x + 13) % 284] = color;
|
||||
}
|
||||
|
||||
auto TMS9918::DAC::power() -> void {
|
||||
|
||||
@@ -18,11 +18,11 @@ auto TMS9918::unload() -> void {
|
||||
}
|
||||
|
||||
auto TMS9918::main() -> void {
|
||||
dac.setup(io.vcounter);
|
||||
if(io.vcounter < 192) {
|
||||
n8 y = io.vcounter;
|
||||
background.setup(y);
|
||||
sprite.setup(y);
|
||||
dac.setup(y);
|
||||
for(n8 x : range(256)) {
|
||||
background.run(x, y);
|
||||
sprite.run(x, y);
|
||||
|
||||
@@ -101,7 +101,7 @@ protected:
|
||||
TMS9918& self;
|
||||
|
||||
//dac.cpp
|
||||
auto setup(n8 voffset) -> void;
|
||||
auto setup(n9 voffset) -> void;
|
||||
auto run(n8 hoffset, n8 voffset) -> void;
|
||||
auto power() -> void;
|
||||
|
||||
|
||||
@@ -20,10 +20,7 @@ auto V9938::Background::run(n8 x, n8 y) -> void {
|
||||
}
|
||||
|
||||
auto V9938::Background::text1(n8 hoffset, n8 voffset) -> void {
|
||||
// Offset by 6px and blank the last 10px to centre the display
|
||||
i32 x = hoffset - 6;
|
||||
if (x < 0) return;
|
||||
if (hoffset > 246) return;
|
||||
i32 x = hoffset;
|
||||
|
||||
n14 nameAddress = (voffset.bit(3,7) * 40) + (x / 6);
|
||||
nameAddress.bit(10,16) = io.nameTableAddress.bit(10,16);
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
auto V9938::DAC::setup(n8 voffset) -> void {
|
||||
n9 vcenter = voffset + (!self.overscan() ? 10 : 0); //centers output within buffer
|
||||
output = self.screen->pixels().data() + vcenter * 1024;
|
||||
if(self.interlace() && self.field()) output += 512;
|
||||
auto V9938::DAC::setup(n9 voffset) -> void {
|
||||
//TODO: Verify border in overscan mode
|
||||
voffset = (voffset + (self.latch.overscan ? 13 : 27)) % 243;
|
||||
output = self.screen->pixels().data() + voffset * 1136;
|
||||
if(self.interlace() && self.field()) output += 568;
|
||||
for(auto n: range(568)) output[n] = self.pram[self.io.colorBackground];
|
||||
output += 26;
|
||||
}
|
||||
|
||||
auto V9938::DAC::run(n8 hoffset, n8 voffset) -> void {
|
||||
|
||||
@@ -19,12 +19,12 @@ auto V9938::unload() -> void {
|
||||
}
|
||||
|
||||
auto V9938::main() -> void {
|
||||
dac.setup(io.vcounter);
|
||||
if(io.vcounter < vlines()) {
|
||||
n9 y = io.vcounter;
|
||||
latch.vscroll = io.vscroll;
|
||||
background.setup(y);
|
||||
sprite.setup(y + latch.vscroll);
|
||||
dac.setup(y);
|
||||
|
||||
while(io.hcounter < 256) {
|
||||
n9 x = io.hcounter;
|
||||
|
||||
@@ -154,7 +154,7 @@ protected:
|
||||
V9938& self;
|
||||
|
||||
//dac.cpp
|
||||
auto setup(n8 voffset) -> void;
|
||||
auto setup(n9 voffset) -> void;
|
||||
auto run(n8 hoffset, n8 voffset) -> void;
|
||||
auto power() -> void;
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
namespace Board {
|
||||
|
||||
#include "coleco.cpp"
|
||||
#include "xin1.cpp"
|
||||
#include "megacart.cpp"
|
||||
|
||||
auto Interface::load(Memory::Readable<n8>& memory, string name) -> bool {
|
||||
if(auto fp = pak->read(name)) {
|
||||
memory.allocate(fp->size());
|
||||
memory.load(fp);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto Interface::load(Memory::Writable<n8>& memory, string name) -> bool {
|
||||
if(auto fp = pak->read(name)) {
|
||||
memory.allocate(fp->size());
|
||||
memory.load(fp);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto Interface::save(Memory::Writable<n8>& memory, string name) -> bool {
|
||||
if(auto fp = pak->write(name)) {
|
||||
memory.save(fp);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace Board {
|
||||
|
||||
struct Interface {
|
||||
VFS::Pak pak;
|
||||
|
||||
Interface(Cartridge& cartridge) : cartridge(cartridge) {}
|
||||
virtual ~Interface() = default;
|
||||
virtual auto load() -> void {}
|
||||
virtual auto save() -> void {}
|
||||
virtual auto unload() -> void {}
|
||||
virtual auto read(n16 address) -> n8 { return 0; }
|
||||
virtual auto write(n16 address, n8 data) -> void {}
|
||||
virtual auto power() -> void {}
|
||||
virtual auto serialize(serializer&) -> void {}
|
||||
|
||||
auto load(Memory::Readable<n8>&, string name) -> bool;
|
||||
auto load(Memory::Writable<n8>&, string name) -> bool;
|
||||
auto save(Memory::Writable<n8>&, string name) -> bool;
|
||||
|
||||
Cartridge& cartridge;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
struct coleco : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<n8> rom;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(rom, "program.rom");
|
||||
}
|
||||
|
||||
auto save() -> void override {}
|
||||
|
||||
auto unload() -> void override {}
|
||||
|
||||
auto read(n16 address) -> n8 override {
|
||||
return rom.read(address);
|
||||
}
|
||||
|
||||
auto write(n16 address, n8 data) -> void override {
|
||||
return;
|
||||
}
|
||||
|
||||
auto power() -> void override {}
|
||||
|
||||
auto serialize(serializer& s) -> void override {}
|
||||
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
struct megacart : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<n8> rom;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(rom, "program.rom");
|
||||
bankCount = rom.size() >> 14;
|
||||
}
|
||||
|
||||
auto save() -> void override {}
|
||||
|
||||
auto unload() -> void override {}
|
||||
|
||||
auto read(n16 address) -> n8 override {
|
||||
if(address >= 0x7fc0) return bank = address & (bankCount - 1);
|
||||
if(address >= 0x4000) return rom.read((bank << 14) + (address - 0x4000));
|
||||
return rom.read((bankCount << 14) + (address - 0x4000));
|
||||
}
|
||||
|
||||
auto write(n16 address, n8 data) -> void override {
|
||||
return;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
bank = 0;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(bank);
|
||||
s(bankCount);
|
||||
}
|
||||
|
||||
n16 bank;
|
||||
n16 bankCount;
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
struct xin1 : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<n8> rom;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(rom, "program.rom");
|
||||
bankCount = rom.size() / 0x8000;
|
||||
}
|
||||
|
||||
auto save() -> void override {}
|
||||
|
||||
auto unload() -> void override {}
|
||||
|
||||
auto read(n16 address) -> n8 override {
|
||||
if(address >= 0x7fc0) return bank = (address - 0x7fc0) % bankCount;
|
||||
return rom.read(address + (bank * 0x8000));
|
||||
}
|
||||
|
||||
auto write(n16 address, n8 data) -> void override {
|
||||
return;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
bank = bankCount - 1;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(bank);
|
||||
s(bankCount);
|
||||
}
|
||||
|
||||
n16 bank;
|
||||
n16 bankCount;
|
||||
};
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace ares::ColecoVision {
|
||||
|
||||
Cartridge& cartridge = cartridgeSlot.cartridge;
|
||||
#include "board/board.cpp"
|
||||
#include "slot.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
@@ -16,31 +17,43 @@ auto Cartridge::connect() -> void {
|
||||
information = {};
|
||||
information.title = pak->attribute("title");
|
||||
information.region = pak->attribute("region");
|
||||
information.board = pak->attribute("board");
|
||||
|
||||
if(auto fp = pak->read("program.rom")) {
|
||||
rom.allocate(fp->size());
|
||||
rom.load(fp);
|
||||
}
|
||||
|
||||
if(information.board == "xin1") board = new Board::xin1{*this};
|
||||
if(information.board == "coleco") board = new Board::coleco{*this};
|
||||
if(information.board == "megacart") board = new Board::megacart{*this};
|
||||
|
||||
|
||||
if(!board) board = new Board::Interface{*this};
|
||||
board->pak = pak;
|
||||
board->load();
|
||||
power();
|
||||
}
|
||||
|
||||
auto Cartridge::disconnect() -> void {
|
||||
if(!node) return;
|
||||
rom.reset();
|
||||
pak.reset();
|
||||
if(!node || !board) return;
|
||||
board->unload();
|
||||
board->pak.reset();
|
||||
board.reset();
|
||||
node.reset();
|
||||
}
|
||||
|
||||
auto Cartridge::save() -> void {
|
||||
if(!node) return;
|
||||
if(board) board->save();
|
||||
}
|
||||
|
||||
auto Cartridge::power() -> void {
|
||||
if(board) board->power();
|
||||
}
|
||||
|
||||
auto Cartridge::read(n16 address) -> n8 {
|
||||
if(address >= rom.size()) return 0xff;
|
||||
return rom.read(address);
|
||||
if(board) return board->read(address);
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto Cartridge::write(n16 address, n8 data) -> void {
|
||||
if(board) return board->write(address, data);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
struct Cartridge;
|
||||
#include "board/board.hpp"
|
||||
|
||||
struct Cartridge {
|
||||
Node::Peripheral node;
|
||||
VFS::Pak pak;
|
||||
@@ -14,17 +17,19 @@ struct Cartridge {
|
||||
auto power() -> void;
|
||||
|
||||
auto read(n16 address) -> n8;
|
||||
auto write(n16 address, n8 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
private:
|
||||
unique_pointer<Board::Interface> board;
|
||||
|
||||
//private:
|
||||
struct Information {
|
||||
string title;
|
||||
string region;
|
||||
string board;
|
||||
} information;
|
||||
|
||||
Memory::Readable<n8> rom;
|
||||
};
|
||||
|
||||
#include "slot.hpp"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
auto CPU::read(n16 address) -> n8 {
|
||||
n8 data = 0xff;
|
||||
if(auto result = platform->cheat(address)) return *result;
|
||||
if(address >= 0x0000 && address <= 0x1fff && io.replaceBIOS) return expansion.read(address);
|
||||
if(address >= 0x2000 && address <= 0x7fff && io.replaceRAM ) return expansion.read(address);
|
||||
if(address >= 0x0000 && address <= 0x1fff) return system.bios[address & 0x1fff];
|
||||
|
||||
+16
-3
@@ -11,12 +11,12 @@ auto VDP::load(Node::Object parent) -> void {
|
||||
|
||||
node = parent->append<Node::Object>("VDP");
|
||||
|
||||
screen = node->append<Node::Video::Screen>("Screen", 256, 192);
|
||||
screen = node->append<Node::Video::Screen>("Screen", 284, 243);
|
||||
screen->colors(1 << 4, {&VDP::color, this});
|
||||
screen->setSize(256, 192);
|
||||
screen->setSize(284, 243);
|
||||
screen->setScale(1.0, 1.0);
|
||||
screen->setAspect(8.0, 7.0);
|
||||
screen->setViewport(0, 0, 256, 192);
|
||||
screen->setViewport(0, 0, 284, 243);
|
||||
|
||||
TMS9918::load(screen);
|
||||
}
|
||||
@@ -39,6 +39,19 @@ auto VDP::irq(bool line) -> void {
|
||||
}
|
||||
|
||||
auto VDP::frame() -> void {
|
||||
if(screen->overscan()) {
|
||||
screen->setSize(284, 243);
|
||||
screen->setViewport(0, 0, 284, 243);
|
||||
} else {
|
||||
int x = 16;
|
||||
int y = 16;
|
||||
int width = 284 - 32;
|
||||
int height = 243 - 32;
|
||||
|
||||
screen->setSize(width, height);
|
||||
screen->setViewport(x, y, width, height);
|
||||
}
|
||||
|
||||
screen->frame();
|
||||
scheduler.exit(Event::Frame);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
//$4018-ffff = Cartridge
|
||||
|
||||
inline auto CPU::readBus(n16 address) -> n8 {
|
||||
if(auto result = platform->cheat(address)) return *result;
|
||||
n8 data = cartridge.readPRG(address, MDR);
|
||||
if(address <= 0x1fff) return ram.read(address);
|
||||
if(address <= 0x3fff) return ppu.readIO(address);
|
||||
|
||||
+11
-3
@@ -1,9 +1,13 @@
|
||||
auto FDSTimer::clock() -> void {
|
||||
if(enable && !--counter) {
|
||||
if(!enable || !irq) return;
|
||||
|
||||
if(!counter) {
|
||||
pending = 1;
|
||||
fds.poll();
|
||||
if(repeat) counter = period;
|
||||
irq &= repeat;
|
||||
} else {
|
||||
counter--;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,12 +44,16 @@ auto FDSTimer::write(n16 address, n8 data) -> void {
|
||||
counter = period;
|
||||
} else {
|
||||
pending = 0;
|
||||
fds.poll();
|
||||
}
|
||||
return;
|
||||
|
||||
case 0x4025:
|
||||
case 0x4023:
|
||||
enable = data.bit(0);
|
||||
if(!enable) pending = 0;
|
||||
if(!enable) {
|
||||
pending = 0;
|
||||
fds.poll();
|
||||
}
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
+19
-12
@@ -16,17 +16,11 @@ auto PPU::load(Node::Object parent) -> void {
|
||||
|
||||
node = parent->append<Node::Object>("PPU");
|
||||
|
||||
screen = node->append<Node::Video::Screen>("Screen", 256, 240);
|
||||
screen = node->append<Node::Video::Screen>("Screen", 282, displayHeight());
|
||||
screen->colors(1 << 9, {&PPU::color, this});
|
||||
screen->setSize(256, 240);
|
||||
screen->setSize(282, displayHeight());
|
||||
screen->setScale(1.0, 1.0);
|
||||
screen->setAspect(8.0, 7.0);
|
||||
|
||||
overscan = screen->append<Node::Setting::Boolean>("Overscan", true, [&](auto value) {
|
||||
if(value == 0) screen->setSize(256, 224);
|
||||
if(value == 1) screen->setSize(256, 240);
|
||||
});
|
||||
overscan->setDynamic(true);
|
||||
Region::PAL() ? screen->setAspect(55.0, 43.0) :screen->setAspect(8.0, 7.0);
|
||||
|
||||
debugger.load(node);
|
||||
}
|
||||
@@ -35,7 +29,6 @@ auto PPU::unload() -> void {
|
||||
screen->quit();
|
||||
node->remove(screen);
|
||||
debugger.unload();
|
||||
overscan.reset();
|
||||
screen.reset();
|
||||
node.reset();
|
||||
ciram.reset();
|
||||
@@ -80,8 +73,22 @@ auto PPU::scanline() -> void {
|
||||
|
||||
auto PPU::frame() -> void {
|
||||
io.field++;
|
||||
if(overscan->value() == 0) screen->setViewport(0, 8, 256, 224);
|
||||
if(overscan->value() == 1) screen->setViewport(0, 0, 256, 240);
|
||||
|
||||
if(screen->overscan()) {
|
||||
screen->setSize(282, displayHeight());
|
||||
screen->setViewport(0, 0, 282, displayHeight());
|
||||
} else {
|
||||
int x = 16;
|
||||
int y = 8;
|
||||
int width = 282 - 32;
|
||||
int height = displayHeight() - 16;
|
||||
|
||||
if(Region::PAL()) height -= 48;
|
||||
|
||||
screen->setSize(width, height);
|
||||
screen->setViewport(x, y, width, height);
|
||||
}
|
||||
|
||||
screen->frame();
|
||||
scheduler.exit(Event::Frame);
|
||||
}
|
||||
|
||||
+3
-1
@@ -1,7 +1,6 @@
|
||||
struct PPU : Thread {
|
||||
Node::Object node;
|
||||
Node::Video::Screen screen;
|
||||
Node::Setting::Boolean overscan;
|
||||
Memory::Writable<n8> ciram;
|
||||
Memory::Writable<n8> cgram;
|
||||
Memory::Writable<n8> oam;
|
||||
@@ -20,6 +19,7 @@ struct PPU : Thread {
|
||||
|
||||
auto rate() const -> u32 { return Region::PAL() ? 5 : 4; }
|
||||
auto vlines() const -> u32 { return Region::PAL() ? 312 : 262; }
|
||||
auto displayHeight() const -> u32 { return Region::PAL() ? 288 : 242; }
|
||||
|
||||
//ppu.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
@@ -133,6 +133,8 @@ struct PPU : Thread {
|
||||
OAM oam[8]; //primary
|
||||
OAM soam[8]; //secondary
|
||||
} latch;
|
||||
|
||||
u32* output;
|
||||
};
|
||||
|
||||
extern PPU ppu;
|
||||
+15
-3
@@ -7,9 +7,7 @@ auto PPU::loadCHR(n16 address) -> n8 {
|
||||
}
|
||||
|
||||
auto PPU::renderPixel() -> void {
|
||||
//todo: renderPixel() is called when Y=261 ... why?
|
||||
if(io.ly >= screen->canvasHeight()) return;
|
||||
auto output = screen->pixels().data() + io.ly * 256;
|
||||
|
||||
u32 x = io.lx - 1;
|
||||
u32 mask = 0x8000 >> (io.v.fineX + (x & 7));
|
||||
@@ -17,6 +15,9 @@ auto PPU::renderPixel() -> void {
|
||||
u32 objectPalette = 0;
|
||||
bool objectPriority = 0;
|
||||
|
||||
//PAL systems blank the topmost scanline and the first and last 2px of active display
|
||||
if(Region::PAL()) if(io.ly == 0 || x < 2 || x > 254) return;
|
||||
|
||||
palette |= latch.tiledataLo & mask ? 1 : 0;
|
||||
palette |= latch.tiledataHi & mask ? 2 : 0;
|
||||
if(palette) {
|
||||
@@ -55,7 +56,13 @@ auto PPU::renderPixel() -> void {
|
||||
}
|
||||
|
||||
if(!enable()) palette = 0;
|
||||
output[x] = io.emphasis << 6 | readCGRAM(palette);
|
||||
|
||||
if(Region::PAL()) {
|
||||
output[(x + 18) % 282] = io.emphasis << 6 | readCGRAM(palette);
|
||||
return;
|
||||
}
|
||||
|
||||
output[(x + 15) % 282] = io.emphasis << 6 | readCGRAM(palette);
|
||||
}
|
||||
|
||||
auto PPU::renderSprite() -> void {
|
||||
@@ -80,6 +87,11 @@ auto PPU::renderSprite() -> void {
|
||||
}
|
||||
|
||||
auto PPU::renderScanline() -> void {
|
||||
if(io.ly < screen->canvasHeight()) {
|
||||
output = screen->pixels().data() + io.ly * 282;
|
||||
for(auto n : range(282)) output[n] = Region::PAL() ? 0x3f : (io.emphasis << 6 | readCGRAM(0));
|
||||
}
|
||||
|
||||
//Vblank
|
||||
if(io.ly >= 240 && io.ly <= vlines() - 2) return step(341), scanline();
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace ares::GameBoy {
|
||||
Bus bus;
|
||||
|
||||
auto Bus::read(u32 cycle, n16 address, n8 data) -> n8 {
|
||||
if(auto result = platform->cheat(address)) return *result;
|
||||
data &= cpu.readIO(cycle, address, data);
|
||||
data &= apu.readIO(cycle, address, data);
|
||||
data &= ppu.readIO(cycle, address, data);
|
||||
|
||||
@@ -21,6 +21,10 @@ auto APU::readIO(n32 address) -> n8 {
|
||||
case 0x0400'0068: return square2.read(1);
|
||||
case 0x0400'0069: return square2.read(2);
|
||||
|
||||
//zero
|
||||
case 0x0400'006a: return 0x00;
|
||||
case 0x0400'006b: return 0x00;
|
||||
|
||||
//NR23, NR24
|
||||
case 0x0400'006c: return square2.read(3);
|
||||
case 0x0400'006d: return square2.read(4);
|
||||
|
||||
@@ -15,7 +15,7 @@ auto CPU::get(u32 mode, n32 address) -> n32 {
|
||||
word = prefetchRead();
|
||||
if(mode & Word) word |= prefetchRead() << 16;
|
||||
} else {
|
||||
if(!context.dmaActive) prefetchWait();
|
||||
if(!context.dmaActive) prefetchReset(); //todo: Prefetch buffer should also be reset when DMA accesses ROM
|
||||
step(clocks - 1);
|
||||
word = cartridge.read(mode, address);
|
||||
step(1);
|
||||
@@ -23,6 +23,7 @@ auto CPU::get(u32 mode, n32 address) -> n32 {
|
||||
} else {
|
||||
if(memory.biosSwap && address < 0x0400'0000) address ^= 0x0200'0000;
|
||||
prefetchStep(clocks);
|
||||
if(auto result = platform->cheat(address)) return *result;
|
||||
if(address < 0x0200'0000) word = bios.read(mode, address);
|
||||
else if(address < 0x0300'0000) word = readEWRAM(mode, address);
|
||||
else if(address < 0x0400'0000) word = readIWRAM(mode, address);
|
||||
@@ -42,7 +43,7 @@ auto CPU::set(u32 mode, n32 address, n32 word) -> void {
|
||||
if(address >= 0x1000'0000) {
|
||||
prefetchStep(clocks);
|
||||
} else if(address & 0x0800'0000) {
|
||||
if(!context.dmaActive) prefetchWait();
|
||||
if(!context.dmaActive) prefetchReset(); //todo: Prefetch buffer should also be reset when DMA accesses ROM
|
||||
step(clocks);
|
||||
cartridge.write(mode, address, word);
|
||||
} else {
|
||||
|
||||
@@ -126,9 +126,9 @@ auto CPU::power() -> void {
|
||||
for(u32 n = 0x100; n <= 0x10f; n++) bus.io[n] = this; //Timers
|
||||
for(u32 n = 0x120; n <= 0x12b; n++) bus.io[n] = this; //Serial
|
||||
for(u32 n = 0x130; n <= 0x133; n++) bus.io[n] = this; //Keypad
|
||||
for(u32 n = 0x134; n <= 0x159; n++) bus.io[n] = this; //Serial
|
||||
for(u32 n = 0x200; n <= 0x209; n++) bus.io[n] = this; //System
|
||||
for(u32 n = 0x300; n <= 0x301; n++) bus.io[n] = this; //System
|
||||
for(u32 n = 0x134; n <= 0x15b; n++) bus.io[n] = this; //Serial
|
||||
for(u32 n = 0x200; n <= 0x20b; n++) bus.io[n] = this; //System
|
||||
for(u32 n = 0x300; n <= 0x303; n++) bus.io[n] = this; //System
|
||||
//0x080-0x083 mirrored via gba/memory/memory.cpp //System
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ struct CPU : ARM7TDMI, Thread, IO {
|
||||
//prefetch.cpp
|
||||
auto prefetchSync(n32 address) -> void;
|
||||
auto prefetchStep(u32 clocks) -> void;
|
||||
auto prefetchWait() -> void;
|
||||
auto prefetchReset() -> void;
|
||||
auto prefetchRead() -> n16;
|
||||
|
||||
//bus.cpp
|
||||
|
||||
+10
-6
@@ -13,12 +13,8 @@ auto CPU::DMA::transfer() -> void {
|
||||
mode |= latch.length() == length() ? Nonsequential : Sequential;
|
||||
|
||||
if(mode & Nonsequential) {
|
||||
if((source() & 0x0800'0000) && (target() & 0x0800'0000)) {
|
||||
//ROM -> ROM transfer
|
||||
} else {
|
||||
cpu.idle();
|
||||
cpu.idle();
|
||||
}
|
||||
cpu.idle();
|
||||
cpu.idle();
|
||||
}
|
||||
|
||||
if(latch.source() < 0x0200'0000) {
|
||||
@@ -31,6 +27,14 @@ auto CPU::DMA::transfer() -> void {
|
||||
if(mode & Half) cpu.dmabus.data |= cpu.dmabus.data << 16;
|
||||
}
|
||||
|
||||
if(mode & Nonsequential) {
|
||||
if((source() & 0x0800'0000) && (target() & 0x0800'0000)) {
|
||||
//ROM -> ROM transfer
|
||||
mode |= Sequential;
|
||||
mode ^= Nonsequential;
|
||||
}
|
||||
}
|
||||
|
||||
if(latch.target() < 0x0200'0000) {
|
||||
cpu.idle(); //cannot access BIOS
|
||||
} else {
|
||||
|
||||
+18
-1
@@ -144,6 +144,10 @@ auto CPU::readIO(n32 address) -> n8 {
|
||||
joybus.siIRQEnable << 0
|
||||
| joybus.mode << 6
|
||||
);
|
||||
|
||||
//zero
|
||||
case 0x0400'0136: return 0;
|
||||
case 0x0400'0137: return 0;
|
||||
|
||||
//JOYCNT
|
||||
case 0x0400'0140: return (
|
||||
@@ -201,14 +205,26 @@ auto CPU::readIO(n32 address) -> n8 {
|
||||
| wait.prefetch << 6
|
||||
| wait.gameType << 7
|
||||
);
|
||||
|
||||
//zero
|
||||
case 0x0400'0206: return 0;
|
||||
case 0x0400'0207: return 0;
|
||||
|
||||
//IME
|
||||
case 0x0400'0208: return irq.ime;
|
||||
case 0x0400'0209: return 0;
|
||||
|
||||
//zero
|
||||
case 0x0400'020a: return 0;
|
||||
case 0x0400'020b: return 0;
|
||||
|
||||
//POSTFLG + HALTCNT
|
||||
case 0x0400'0300: return context.booted;
|
||||
case 0x0400'0301: return 0;
|
||||
|
||||
//zero
|
||||
case 0x0400'0302: return 0;
|
||||
case 0x0400'0303: return 0;
|
||||
|
||||
//MEMCNT_L
|
||||
case 0x0400'0800: return (
|
||||
@@ -293,10 +309,11 @@ auto CPU::writeIO(n32 address, n8 data) -> void {
|
||||
bool enable = timer().enable;
|
||||
|
||||
timer().frequency = data.bit(0,1);
|
||||
timer().cascade = data.bit(2);
|
||||
timer().irq = data.bit(6);
|
||||
timer().enable = data.bit(7);
|
||||
|
||||
if(address != 0x0400'0102) timer().cascade = data.bit(2);
|
||||
|
||||
if(!enable && timer().enable) { //0->1 transition
|
||||
timer().pending = true;
|
||||
}
|
||||
|
||||
@@ -18,11 +18,9 @@ auto CPU::prefetchStep(u32 clocks) -> void {
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::prefetchWait() -> void {
|
||||
if(!wait.prefetch || context.dmaActive || prefetch.full()) return;
|
||||
|
||||
prefetchStep(prefetch.wait);
|
||||
prefetch.wait = _wait(Half | Nonsequential, prefetch.load);
|
||||
auto CPU::prefetchReset() -> void {
|
||||
prefetch.addr = 0;
|
||||
prefetch.load = 0;
|
||||
}
|
||||
|
||||
auto CPU::prefetchRead() -> n16 {
|
||||
|
||||
@@ -56,12 +56,12 @@ auto PPU::Objects::scanline(u32 y) -> void {
|
||||
}
|
||||
} else if(!buffer[bx].enable || object.priority < buffer[bx].priority) {
|
||||
buffer[bx].priority = object.priority; //updated regardless of transparency
|
||||
buffer[bx].mosaic = object.mosaic; //updated regardless of transparency
|
||||
if(color) {
|
||||
if(object.colors == 0) color = object.palette * 16 + color;
|
||||
buffer[bx].enable = true;
|
||||
buffer[bx].color = ppu.pram[256 + color];
|
||||
buffer[bx].translucent = object.mode == 1;
|
||||
buffer[bx].mosaic = object.mosaic;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -80,12 +80,15 @@ auto PPU::Objects::run(u32 x, u32 y) -> void {
|
||||
}
|
||||
|
||||
output = buffer[x];
|
||||
|
||||
|
||||
//horizontal mosaic
|
||||
if(!output.mosaic || ++mosaicOffset >= 1 + io.mosaicWidth) {
|
||||
if(mosaicOffset >= 1 + io.mosaicWidth) {
|
||||
mosaicOffset = 0;
|
||||
mosaic = output;
|
||||
} else if(!mosaic.mosaic || !output.mosaic) {
|
||||
mosaic = output;
|
||||
}
|
||||
mosaicOffset++;
|
||||
}
|
||||
|
||||
auto PPU::Objects::power() -> void {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
alwaysinline auto Bus::read(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
|
||||
if(auto result = platform->cheat(address)) return *result;
|
||||
if(address >= 0x000000 && address <= 0x3fffff) {
|
||||
waitRefreshExternal();
|
||||
if(!cpu.io.romEnable) {
|
||||
|
||||
@@ -11,9 +11,7 @@ ControlPad::ControlPad(Node::Port parent) {
|
||||
start = node->append<Node::Input::Button>("Start");
|
||||
}
|
||||
|
||||
auto ControlPad::readData() -> n8 {
|
||||
n6 data;
|
||||
|
||||
auto ControlPad::poll() -> void {
|
||||
platform->input(up);
|
||||
platform->input(down);
|
||||
platform->input(left);
|
||||
@@ -34,6 +32,10 @@ auto ControlPad::readData() -> n8 {
|
||||
} else if(!xHold) {
|
||||
xHold = 1, swap(leftLatch, rightLatch);
|
||||
}
|
||||
}
|
||||
|
||||
auto ControlPad::readData() -> Data {
|
||||
n6 data;
|
||||
|
||||
if(select == 0) {
|
||||
data.bit(0) = upLatch;
|
||||
@@ -51,10 +53,9 @@ auto ControlPad::readData() -> n8 {
|
||||
}
|
||||
|
||||
data = ~data;
|
||||
return latch << 7 | select << 6 | data;
|
||||
return {data, 0x3f};
|
||||
}
|
||||
|
||||
auto ControlPad::writeData(n8 data) -> void {
|
||||
select = data.bit(6);
|
||||
latch = data.bit(7);
|
||||
}
|
||||
@@ -10,12 +10,12 @@ struct ControlPad : Controller {
|
||||
|
||||
ControlPad(Node::Port);
|
||||
|
||||
auto readData() -> n8 override;
|
||||
auto poll() -> void override;
|
||||
auto readData() -> Data override;
|
||||
auto writeData(n8 data) -> void override;
|
||||
|
||||
private:
|
||||
n1 select = 1;
|
||||
n1 latch;
|
||||
|
||||
b1 yHold;
|
||||
b1 upLatch;
|
||||
|
||||
@@ -17,9 +17,15 @@
|
||||
struct Controller {
|
||||
Node::Peripheral node;
|
||||
|
||||
struct Data {
|
||||
n8 value;
|
||||
n8 mask;
|
||||
};
|
||||
|
||||
virtual ~Controller() = default;
|
||||
|
||||
virtual auto readData() -> n8 { return 0xff; }
|
||||
virtual auto poll() -> void {}
|
||||
virtual auto readData() -> Data { return {0x7f, 0x7f}; }
|
||||
virtual auto writeData(n8 data) -> void {}
|
||||
};
|
||||
|
||||
|
||||
@@ -23,15 +23,13 @@ FightingPad::~FightingPad() {
|
||||
|
||||
auto FightingPad::main() -> void {
|
||||
if(timeout) {
|
||||
timeout--;
|
||||
} else {
|
||||
counter = 0;
|
||||
if(!--timeout) counter = 0;
|
||||
}
|
||||
Thread::step(1);
|
||||
Thread::synchronize(cpu);
|
||||
}
|
||||
|
||||
auto FightingPad::readData() -> n8 {
|
||||
auto FightingPad::poll() -> void {
|
||||
platform->input(up);
|
||||
platform->input(down);
|
||||
platform->input(left);
|
||||
@@ -56,7 +54,9 @@ auto FightingPad::readData() -> n8 {
|
||||
} else if(!xHold) {
|
||||
xHold = 1, swap(leftLatch, rightLatch);
|
||||
}
|
||||
}
|
||||
|
||||
auto FightingPad::readData() -> Data {
|
||||
n6 data;
|
||||
|
||||
if(select == 0) {
|
||||
@@ -82,8 +82,6 @@ auto FightingPad::readData() -> n8 {
|
||||
data.bit(1) = downLatch;
|
||||
data.bit(2) = leftLatch;
|
||||
data.bit(3) = rightLatch;
|
||||
data.bit(4) = b->value();
|
||||
data.bit(5) = c->value();
|
||||
}
|
||||
|
||||
if(counter == 3) {
|
||||
@@ -91,20 +89,21 @@ auto FightingPad::readData() -> n8 {
|
||||
data.bit(1) = y->value();
|
||||
data.bit(2) = x->value();
|
||||
data.bit(3) = mode->value();
|
||||
data.bit(4,5) = 0;
|
||||
}
|
||||
|
||||
data.bit(4) = b->value();
|
||||
data.bit(5) = c->value();
|
||||
}
|
||||
|
||||
data = ~data;
|
||||
return latch << 7 | select << 6 | data;
|
||||
return {data, 0x3f};
|
||||
}
|
||||
|
||||
auto FightingPad::writeData(n8 data) -> void {
|
||||
if(!select && data.bit(6)) { //0->1 transition
|
||||
if(++counter == 5) counter = 0;
|
||||
if(counter < 4) ++counter;
|
||||
timeout = 1600; //~1.6ms
|
||||
}
|
||||
|
||||
select = data.bit(6);
|
||||
latch = data.bit(7);
|
||||
timeout = 1600; //~1.6ms
|
||||
}
|
||||
@@ -15,12 +15,12 @@ struct FightingPad : Controller, Thread {
|
||||
FightingPad(Node::Port);
|
||||
~FightingPad();
|
||||
auto main() -> void;
|
||||
auto readData() -> n8 override;
|
||||
auto poll() -> void override;
|
||||
auto readData() -> Data override;
|
||||
auto writeData(n8 data) -> void override;
|
||||
|
||||
private:
|
||||
n1 select = 1;
|
||||
n1 latch;
|
||||
n3 counter;
|
||||
n32 timeout;
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@ MegaMouse::MegaMouse(Node::Port parent) {
|
||||
t_data = 10 * timerfreq / 1000000;
|
||||
|
||||
Thread::create(timerfreq, {&MegaMouse::main, this});
|
||||
Thread::synchronize(cpu);
|
||||
}
|
||||
|
||||
MegaMouse::~MegaMouse() {
|
||||
@@ -68,7 +67,7 @@ auto MegaMouse::main() -> void {
|
||||
Thread::synchronize(cpu);
|
||||
}
|
||||
|
||||
auto MegaMouse::readData() -> n8 {
|
||||
auto MegaMouse::readData() -> Data {
|
||||
n8 data;
|
||||
|
||||
if (th) {
|
||||
@@ -81,14 +80,15 @@ auto MegaMouse::readData() -> n8 {
|
||||
}
|
||||
|
||||
data.bit(4) = tl;
|
||||
data.bit(5) = tr;
|
||||
data.bit(6) = th;
|
||||
data.bit(7) = latch;
|
||||
|
||||
return data;
|
||||
return {data, 0x1f};
|
||||
}
|
||||
|
||||
auto MegaMouse::writeData(n8 data) -> void {
|
||||
//todo: this check wouldn't be necessary if the logic below was correct.
|
||||
//this function should only respond to level changes.
|
||||
if(tr == data.bit(5) && th == data.bit(6)) return;
|
||||
|
||||
// Falling TH
|
||||
if (!data.bit(6) && th) {
|
||||
// When TH falls low, make sure the second nibble is driven. The
|
||||
@@ -142,6 +142,5 @@ auto MegaMouse::writeData(n8 data) -> void {
|
||||
timeout = t_handshake;
|
||||
}
|
||||
|
||||
latch = data.bit(7);
|
||||
th = data.bit(6);
|
||||
}
|
||||
@@ -10,14 +10,13 @@ struct MegaMouse : Controller, Thread {
|
||||
~MegaMouse();
|
||||
|
||||
auto main() -> void;
|
||||
auto readData() -> n8 override;
|
||||
auto readData() -> Data override;
|
||||
auto writeData(n8 data) -> void override;
|
||||
|
||||
private:
|
||||
n1 th = 1;
|
||||
n1 tr = 1;
|
||||
n1 tl = 1;
|
||||
n1 latch;
|
||||
n8 index = 0;
|
||||
n4 status[10];
|
||||
s16 maxspeed = 255;
|
||||
|
||||
@@ -29,10 +29,21 @@ auto ControllerPort::allocate(string name) -> Node::Peripheral {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto ControllerPort::update() -> void {
|
||||
auto [inputData, inputMask] = device ? device->readData() : Controller::Data{0x7f, 0x7f};
|
||||
n8 outputMask = 0x80 | control;
|
||||
n8 prevLines = dataLines;
|
||||
dataLines = inputData & inputMask | dataLines & ~inputMask;
|
||||
dataLines = dataLatch & outputMask | dataLines & ~outputMask;
|
||||
//todo: gradually pull up floating lines
|
||||
if(device && prevLines != dataLines) return device->writeData(dataLines);
|
||||
}
|
||||
|
||||
auto ControllerPort::power(bool reset) -> void {
|
||||
if(!reset) {
|
||||
control = 0x00;
|
||||
dataLatch = 0x7f;
|
||||
dataLines = 0x7f;
|
||||
serialControl = 0x00;
|
||||
serialTxBuffer = 0xff;
|
||||
serialRxBuffer = 0x00;
|
||||
@@ -42,6 +53,7 @@ auto ControllerPort::power(bool reset) -> void {
|
||||
auto ControllerPort::serialize(serializer& s) -> void {
|
||||
s(control);
|
||||
s(dataLatch);
|
||||
s(dataLines);
|
||||
s(serialControl);
|
||||
s(serialTxBuffer);
|
||||
s(serialRxBuffer);
|
||||
|
||||
@@ -11,17 +11,17 @@ struct ControllerPort {
|
||||
auto connect(Node::Peripheral) -> void;
|
||||
auto disconnect() -> void { device.reset(); }
|
||||
|
||||
auto update() -> void;
|
||||
|
||||
auto readControl() -> n8 { return control; }
|
||||
auto writeControl(n8 data) -> void { control = data; }
|
||||
auto writeControl(n8 data) -> void { control = data; update(); }
|
||||
|
||||
auto readData() -> n8 {
|
||||
n8 data = device ? device->readData() : n8(~0);
|
||||
return dataLatch = dataLatch & (0x80 | control) | data & ~(0x80 | control);
|
||||
}
|
||||
auto writeData(n8 data) -> void {
|
||||
dataLatch = dataLatch & ~(0x80 | control) | data & (0x80 | control);
|
||||
if(device) return device->writeData(dataLatch);
|
||||
if(device) device->poll();
|
||||
update();
|
||||
return dataLines;
|
||||
}
|
||||
auto writeData(n8 data) -> void { dataLatch = data; update(); }
|
||||
|
||||
// TODO: Implement working serial transfers. This code is mostly placeholder.
|
||||
|
||||
@@ -57,6 +57,7 @@ protected:
|
||||
const string name;
|
||||
n8 control; //d0-d6 = PC0-PC6 (0 = input; 1 = output); d7 = TH-INT enable
|
||||
n8 dataLatch;
|
||||
n8 dataLines;
|
||||
n8 serialControl;
|
||||
n8 serialTxBuffer;
|
||||
n8 serialRxBuffer;
|
||||
|
||||
@@ -13,13 +13,19 @@ auto M32X::SH7604::Debugger::load(Node::Object parent) -> void {
|
||||
tracer.instruction = parent->append<Node::Debugger::Tracer::Instruction>("Instruction", parent->name());
|
||||
tracer.instruction->setAddressBits(32, 1);
|
||||
tracer.instruction->setDepth(16);
|
||||
if constexpr(SH2::Accuracy::Recompiler) {
|
||||
tracer.instruction->setToggle([&] {
|
||||
self->recompiler.reset();
|
||||
self->recompiler.callInstructionPrologue = tracer.instruction->enabled();
|
||||
});
|
||||
}
|
||||
|
||||
tracer.interrupt = parent->append<Node::Debugger::Tracer::Notification>("Interrupt", parent->name());
|
||||
}
|
||||
|
||||
auto M32X::SH7604::Debugger::instruction() -> void {
|
||||
auto M32X::SH7604::Debugger::instruction(u16 opcode) -> void {
|
||||
if(tracer.instruction->enabled() && tracer.instruction->address(self->regs.PC - 4)) {
|
||||
tracer.instruction->notify(self->disassembleInstruction(), self->disassembleContext());
|
||||
tracer.instruction->notify(self->disassembleInstruction(opcode), self->disassembleContext());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ struct M32X {
|
||||
|
||||
//debugger.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto instruction() -> void;
|
||||
auto instruction(u16 opcode) -> void;
|
||||
auto interrupt(string_view) -> void;
|
||||
|
||||
struct Tracer {
|
||||
@@ -37,6 +37,7 @@ struct M32X {
|
||||
auto unload() -> void;
|
||||
|
||||
auto main() -> void;
|
||||
auto instructionPrologue(u16 instruction) -> void override;
|
||||
auto step(u32 clocks) -> void override;
|
||||
auto power(bool reset) -> void;
|
||||
auto restart() -> void;
|
||||
|
||||
@@ -38,7 +38,6 @@ auto M32X::SH7604::main() -> void {
|
||||
#undef raise
|
||||
}
|
||||
|
||||
debugger.instruction();
|
||||
SH2::instruction();
|
||||
SH2::intc.run();
|
||||
SH2::dmac.run();
|
||||
@@ -46,6 +45,10 @@ auto M32X::SH7604::main() -> void {
|
||||
if(m32x.shs.active()) m32x.shs.dmac.dreq[1] = 0;
|
||||
}
|
||||
|
||||
auto M32X::SH7604::instructionPrologue(u16 instruction) -> void {
|
||||
debugger.instruction(instruction);
|
||||
}
|
||||
|
||||
auto M32X::SH7604::step(u32 clocks) -> void {
|
||||
if(clocks > 0) {
|
||||
auto cycles = clocks;
|
||||
|
||||
@@ -60,6 +60,7 @@ auto MCD::CDD::advance() -> void {
|
||||
io.track = track();
|
||||
io.sector++;
|
||||
io.sample = 0;
|
||||
readSubcode();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -93,6 +94,34 @@ auto MCD::CDD::position(s32 sector) -> double {
|
||||
return sqrt(sector / sectors * (outerRadius - innerRadius) + innerRadius) / radius;
|
||||
}
|
||||
|
||||
auto MCD::CDD::readSubcode() -> void {
|
||||
if(!mcd.fd) return;
|
||||
|
||||
mcd.fd->seek(((abs(mcd.cdd.session.leadIn.lba) + io.sector) * 2448) + 2352);
|
||||
vector<u8> subchannel;
|
||||
subchannel.resize(96);
|
||||
mcd.fd->read({subchannel.data(), 96});
|
||||
|
||||
io.subcodePosition += 49;
|
||||
n6 index = io.subcodePosition;
|
||||
|
||||
//subchannel data is stored by ares as 12x8 for each channel (PQRSTUVW)
|
||||
//we need to convert this back to the raw subchannel encoding as per EMCA-130
|
||||
for(int i = 0; i < 96; i+= 2) {
|
||||
n16 data = 0;
|
||||
for(auto channel : range(8)) {
|
||||
n8 input = subchannel[(channel * 12) + (i / 8)];
|
||||
n2 bits = input >> (6 - (i & 6));
|
||||
data.bit( 7 - channel) = bits.bit(0);
|
||||
data.bit(15 - channel) = bits.bit(1);
|
||||
}
|
||||
|
||||
subcode[index++] = data;
|
||||
}
|
||||
|
||||
mcd.irq.subcode.raise();
|
||||
}
|
||||
|
||||
auto MCD::CDD::process() -> void {
|
||||
//if(command[0]) print("CDD ", command[0], ":", command[3], "\n");
|
||||
|
||||
|
||||
@@ -26,6 +26,15 @@ auto MCD::Debugger::load(Node::Object parent) -> void {
|
||||
mcd.bram[address] = data;
|
||||
});
|
||||
|
||||
memory.pcmRam = parent->append<Node::Debugger::Memory>("CD PCM RAM");
|
||||
memory.pcmRam->setSize(64_KiB);
|
||||
memory.pcmRam->setRead([&](u32 address) -> u8 {
|
||||
return mcd.pcm.ram[address];
|
||||
});
|
||||
memory.bram->setWrite([&](u32 address, u8 data) -> void {
|
||||
mcd.pcm.ram[address] = data;
|
||||
});
|
||||
|
||||
tracer.instruction = parent->append<Node::Debugger::Tracer::Instruction>("Instruction", "MCD");
|
||||
tracer.instruction->setAddressBits(24);
|
||||
tracer.instruction->setDepth(16);
|
||||
|
||||
@@ -176,8 +176,15 @@ auto MCD::readIO(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
|
||||
data.bit(1,15) = gpu.vector.base.bit(3,17);
|
||||
}
|
||||
|
||||
if(address == 0xff8068) {
|
||||
data.bit(0) = Unmapped;
|
||||
data.bit(1,7) = cdd.io.subcodePosition.bit(0,6);
|
||||
data.bit(8,15) = Unmapped;
|
||||
}
|
||||
|
||||
if(address >= 0xff8100 && address <= 0xff81ff) {
|
||||
debug(unusual, "[MCD::readIO] address=0x", hex(address, 6L));
|
||||
n6 index = address - 0xff8100 >> 1;
|
||||
data = cdd.subcode[index];
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
@@ -21,6 +21,7 @@ struct MCD : M68000, Thread {
|
||||
Node::Debugger::Memory pram;
|
||||
Node::Debugger::Memory wram;
|
||||
Node::Debugger::Memory bram;
|
||||
Node::Debugger::Memory pcmRam;
|
||||
} memory;
|
||||
|
||||
struct Tracer {
|
||||
@@ -306,6 +307,7 @@ struct MCD : M68000, Thread {
|
||||
auto sample() -> void;
|
||||
auto position(s32 sector) -> double;
|
||||
auto process() -> void;
|
||||
auto readSubcode() -> void;
|
||||
auto valid() -> bool;
|
||||
auto checksum() -> void;
|
||||
auto insert() -> void;
|
||||
@@ -344,12 +346,14 @@ struct MCD : M68000, Thread {
|
||||
n16 sample; //current audio sample# within current frame
|
||||
n7 track; //current track#
|
||||
n1 tocRead;
|
||||
n6 subcodePosition;
|
||||
} io;
|
||||
|
||||
n1 hostClockEnable;
|
||||
n1 statusPending;
|
||||
n4 status [10];
|
||||
n4 command[10];
|
||||
n16 subcode[64];
|
||||
} cdd;
|
||||
|
||||
struct Timer {
|
||||
|
||||
@@ -148,11 +148,13 @@ auto MCD::CDD::serialize(serializer& s) -> void {
|
||||
s(io.sample);
|
||||
s(io.track);
|
||||
s(io.tocRead);
|
||||
s(io.subcodePosition);
|
||||
|
||||
s(hostClockEnable);
|
||||
s(statusPending);
|
||||
s(status);
|
||||
s(command);
|
||||
s(subcode);
|
||||
}
|
||||
|
||||
auto MCD::Timer::serialize(serializer& s) -> void {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
static const string SerializerVersion = "v133.1";
|
||||
static const string SerializerVersion = "v135";
|
||||
|
||||
auto System::serialize(bool synchronize) -> serializer {
|
||||
if(synchronize) scheduler.enter(Scheduler::Mode::Synchronize);
|
||||
|
||||
@@ -1,19 +1,11 @@
|
||||
auto VDP::pixels() -> u32* {
|
||||
u32* output = nullptr;
|
||||
if(overscan->value() == 0 && io.overscan == 0) {
|
||||
if(state.vcounter >= 224) return nullptr;
|
||||
output = screen->pixels().data() + (state.vcounter - 0) * 2 * 1280;
|
||||
}
|
||||
if(overscan->value() == 0 && io.overscan == 1) {
|
||||
if(state.vcounter <= 7) return nullptr;
|
||||
if(state.vcounter >= 232) return nullptr;
|
||||
output = screen->pixels().data() + (state.vcounter - 8) * 2 * 1280;
|
||||
}
|
||||
if(overscan->value() == 1 && io.overscan == 0) {
|
||||
|
||||
if(io.overscan == 0) {
|
||||
if(state.vcounter >= 232) return nullptr;
|
||||
output = screen->pixels().data() + (state.vcounter + 8) * 2 * 1280;
|
||||
}
|
||||
if(overscan->value() == 1 && io.overscan == 1) {
|
||||
if(io.overscan == 1) {
|
||||
output = screen->pixels().data() + (state.vcounter + 0) * 2 * 1280;
|
||||
}
|
||||
if(latch.interlace) output += state.field * 1280;
|
||||
|
||||
@@ -26,12 +26,6 @@ auto VDP::load(Node::Object parent) -> void {
|
||||
screen->setScale(0.25, 0.5);
|
||||
screen->setAspect(32, 35);
|
||||
|
||||
overscan = screen->append<Node::Setting::Boolean>("Overscan", true, [&](auto value) {
|
||||
if(value == 0) screen->setSize(1280, 448);
|
||||
if(value == 1) screen->setSize(1280, 480);
|
||||
});
|
||||
overscan->setDynamic(true);
|
||||
|
||||
psg.load(node);
|
||||
debugger.load(node);
|
||||
}
|
||||
@@ -39,7 +33,6 @@ auto VDP::load(Node::Object parent) -> void {
|
||||
auto VDP::unload() -> void {
|
||||
debugger = {};
|
||||
psg.unload();
|
||||
overscan.reset();
|
||||
screen->quit();
|
||||
node->remove(screen);
|
||||
screen.reset();
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
struct VDP : Thread {
|
||||
Node::Object node;
|
||||
Node::Video::Screen screen;
|
||||
Node::Setting::Boolean overscan;
|
||||
|
||||
struct Debugger {
|
||||
//debugger.cpp
|
||||
|
||||
+51
-35
@@ -34,17 +34,11 @@ VDP vdp;
|
||||
auto VDP::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Object>("VDP");
|
||||
|
||||
screen = node->append<Node::Video::Screen>("Screen", 1280, 480);
|
||||
screen = node->append<Node::Video::Screen>("Screen", 1388, visibleHeight() * 2);
|
||||
screen->colors(1 << 16, {&VDP::color, this});
|
||||
screen->setSize(1280, 480);
|
||||
screen->setSize(1388, visibleHeight() * 2);
|
||||
screen->setScale(0.25, 0.5);
|
||||
screen->setAspect(32, 35);
|
||||
|
||||
overscan = screen->append<Node::Setting::Boolean>("Overscan", true, [&](auto value) {
|
||||
if(value == 0) screen->setSize(1280, 448);
|
||||
if(value == 1) screen->setSize(1280, 480);
|
||||
});
|
||||
overscan->setDynamic(true);
|
||||
Region::PAL() ? screen->setAspect(111.0, 100.0) : screen->setAspect(32.0, 35.0);
|
||||
|
||||
psg.load(node);
|
||||
debugger.load(node);
|
||||
@@ -55,7 +49,6 @@ auto VDP::load(Node::Object parent) -> void {
|
||||
auto VDP::unload() -> void {
|
||||
debugger.unload();
|
||||
psg.unload();
|
||||
overscan.reset();
|
||||
screen->quit();
|
||||
node->remove(screen);
|
||||
screen.reset();
|
||||
@@ -63,37 +56,60 @@ auto VDP::unload() -> void {
|
||||
}
|
||||
|
||||
auto VDP::pixels() -> u32* {
|
||||
//TODO: vcounter values of top border may not be correct here
|
||||
|
||||
u32* output = nullptr;
|
||||
if(overscan->value() == 0 && latch.overscan == 0) {
|
||||
if(vcounter() >= 224) return nullptr;
|
||||
output = screen->pixels().data() + (vcounter() - 0) * 2 * 1280;
|
||||
}
|
||||
if(overscan->value() == 0 && latch.overscan == 1) {
|
||||
if(vcounter() <= 7) return nullptr;
|
||||
if(vcounter() >= 232) return nullptr;
|
||||
output = screen->pixels().data() + (vcounter() - 8) * 2 * 1280;
|
||||
}
|
||||
if(overscan->value() == 1 && latch.overscan == 0) {
|
||||
if(vcounter() >= 0x1f8) { // top border
|
||||
output = screen->pixels().data() + (vcounter() - 0x1f8) * 2 * 1280;
|
||||
} else if(vcounter() >= 232) {
|
||||
return nullptr;
|
||||
} else {
|
||||
output = screen->pixels().data() + (vcounter() + 8) * 2 * 1280;
|
||||
}
|
||||
}
|
||||
if(overscan->value() == 1 && latch.overscan == 1) {
|
||||
if(vcounter() >= 240) return nullptr;
|
||||
output = screen->pixels().data() + (vcounter() + 0) * 2 * 1280;
|
||||
}
|
||||
if(latch.interlace) output += field() * 1280;
|
||||
return output;
|
||||
if(Region::NTSC() && vcounter() >= 0x1ed) return nullptr;
|
||||
if(Region::PAL() && vcounter() >= 0x1f0) return nullptr;
|
||||
|
||||
//account for vcounter jumps during blanking periods
|
||||
n9 y = vcounter();
|
||||
if(Region::NTSC() && v28() && vcounter() >= 0x1e5) y -= 250;
|
||||
if(Region::PAL() && v28() && vcounter() >= 0x1ca) y -= 201;
|
||||
if(v30() && Region::PAL() && vcounter() >= 0x1d2) y -= 201;
|
||||
|
||||
auto offset = Region::PAL() ? 38 : 11;
|
||||
if(latch.overscan) offset -= 8;
|
||||
|
||||
y = (y + offset) % visibleHeight();
|
||||
|
||||
output = screen->pixels().data() + y * 2 * 1388;
|
||||
if(latch.interlace) output += field() * 1388;
|
||||
|
||||
//TODO: this should probably be handled in DAC
|
||||
n32 bg = 1 << 11 | 1 << 9 | cram.color(io.backgroundColor);
|
||||
for(auto n: range(1388)) output[n] = bg;
|
||||
|
||||
return output + 52;
|
||||
}
|
||||
|
||||
auto VDP::frame() -> void {
|
||||
if(latch.interlace == 0) screen->setProgressive(1);
|
||||
if(latch.interlace == 1) screen->setInterlace(field());
|
||||
screen->setViewport(0, 0, screen->width(), screen->height());
|
||||
|
||||
if(screen->overscan()) {
|
||||
screen->setSize(1388, visibleHeight() * 2);
|
||||
screen->setViewport(0, 0, screen->width(), screen->height());
|
||||
} else {
|
||||
int x = 14 * 4;
|
||||
int y = 12 * 2;
|
||||
int width = 1388 - (28 * 4);
|
||||
int height = (visibleHeight() * 2) - (24 * 2);
|
||||
|
||||
if(Region::PAL()) {
|
||||
y += 28 * 2;
|
||||
height -= 48 * 2;
|
||||
|
||||
if(v30()) {
|
||||
y -= 8 * 2;
|
||||
height += 16 * 2;
|
||||
}
|
||||
}
|
||||
|
||||
screen->setSize(width, height);
|
||||
screen->setViewport(x, y, width, height);
|
||||
}
|
||||
|
||||
screen->frame();
|
||||
scheduler.exit(Event::Frame);
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,7 +5,6 @@
|
||||
struct VDP : Thread {
|
||||
Node::Object node;
|
||||
Node::Video::Screen screen;
|
||||
Node::Setting::Boolean overscan;
|
||||
|
||||
struct Debugger {
|
||||
VDP& self;
|
||||
@@ -53,6 +52,7 @@ struct VDP : Thread {
|
||||
auto screenWidth() const -> u32 { return latch.displayWidth ? 320 : 256; }
|
||||
auto screenHeight() const -> u32 { return io.overscan ? 240 : 224; }
|
||||
auto frameHeight() const -> u32 { return Region::PAL() ? 312 : 262; }
|
||||
auto visibleHeight() const -> u32 { return Region::PAL() ? 294 : 243; }
|
||||
|
||||
//vdp.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
|
||||
@@ -3,6 +3,7 @@ auto CPU::mdr() const -> n8 {
|
||||
}
|
||||
|
||||
auto CPU::read(n16 address) -> n8 {
|
||||
if(auto result = platform->cheat(address)) return *result;
|
||||
n8 data = mdr();
|
||||
if(address >= 0xc000 && bus.ramEnable) data = ram.read(address);
|
||||
if(Device::MasterSystem() && bus.biosEnable) data = bios.read(address, data);
|
||||
|
||||
+7
-4
@@ -1,6 +1,9 @@
|
||||
auto VDP::DAC::setup(n8 y) -> void {
|
||||
self.screen->setFillColor(palette(16 | io.backdropColor));
|
||||
output = self.screen->pixels().data() + ((240-self.vlines())/2 + y) * 256;
|
||||
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;
|
||||
|
||||
for (u32 x : range(284)) output[x] = palette(16 | io.backdropColor);
|
||||
}
|
||||
|
||||
auto VDP::DAC::run(n8 x, n8 y) -> void {
|
||||
@@ -15,7 +18,7 @@ auto VDP::DAC::run(n8 x, n8 y) -> void {
|
||||
}
|
||||
}
|
||||
|
||||
output[x] = color;
|
||||
output[(x + 13) % 284] = color;
|
||||
}
|
||||
|
||||
auto VDP::DAC::palette(n5 index) -> n12 {
|
||||
|
||||
+25
-18
@@ -18,7 +18,7 @@ auto VDP::load(Node::Object parent) -> void {
|
||||
|
||||
node = parent->append<Node::Object>("VDP");
|
||||
|
||||
screen = node->append<Node::Video::Screen>("Screen", 256, 240);
|
||||
screen = node->append<Node::Video::Screen>("Screen", 284, screenHeight());
|
||||
|
||||
u32 defaultRevision = 2;
|
||||
if((Model::MarkIII() || Model::MasterSystemI()) && Region::NTSCJ()) defaultRevision = 1;
|
||||
@@ -27,15 +27,9 @@ auto VDP::load(Node::Object parent) -> void {
|
||||
|
||||
if(Display::CRT()) {
|
||||
screen->colors(1 << 6, {&VDP::colorMasterSystem, this});
|
||||
screen->setSize(256, 240);
|
||||
screen->setSize(284, screenHeight());
|
||||
screen->setScale(1.0, 1.0);
|
||||
screen->setAspect(8.0, 7.0);
|
||||
|
||||
overscan = screen->append<Node::Setting::Boolean>("Overscan", true, [&](auto value) {
|
||||
if(value == 0) screen->setSize(256, vlines());
|
||||
if(value == 1) screen->setSize(256, 240);
|
||||
});
|
||||
overscan->setDynamic(true);
|
||||
Region::PAL() ? screen->setAspect(19.0, 14.0) : screen->setAspect(8.0, 7.0);
|
||||
|
||||
colorEmulation = screen->append<Node::Setting::Boolean>("Color Emulation", true, [&](auto value) {
|
||||
screen->resetPalette();
|
||||
@@ -48,7 +42,7 @@ auto VDP::load(Node::Object parent) -> void {
|
||||
screen->setSize(160, 144);
|
||||
screen->setScale(1.0, 1.0);
|
||||
screen->setAspect(6.0, 5.0);
|
||||
screen->setViewport(0, 0, 256, 240);
|
||||
screen->setViewport(0, 0, 284, screenHeight());
|
||||
|
||||
interframeBlending = screen->append<Node::Setting::Boolean>("Interframe Blending", true, [&](auto value) {
|
||||
screen->setInterframeBlending(value);
|
||||
@@ -65,7 +59,6 @@ auto VDP::unload() -> void {
|
||||
revision.reset();
|
||||
screen->quit();
|
||||
node->remove(screen);
|
||||
overscan.reset();
|
||||
screen.reset();
|
||||
node.reset();
|
||||
vram.reset();
|
||||
@@ -89,11 +82,11 @@ auto VDP::main() -> void {
|
||||
}
|
||||
|
||||
//684 clocks/scanline
|
||||
dac.setup(io.vcounter);
|
||||
if(io.vcounter < vlines()) {
|
||||
u8 y = io.vcounter;
|
||||
background.setup(y);
|
||||
sprite.setup(y);
|
||||
dac.setup(y);
|
||||
for(u8 x : range(256)) {
|
||||
background.run(x, y);
|
||||
sprite.run(x, y);
|
||||
@@ -108,13 +101,28 @@ auto VDP::main() -> void {
|
||||
|
||||
if(io.vcounter == 240) {
|
||||
if(Display::CRT()) {
|
||||
if(overscan->value() == 0) screen->setViewport(0, (240-vlines())/2, 256, vlines());
|
||||
if(overscan->value() == 1) screen->setViewport(0, 0, 256, 240);
|
||||
if(screen->overscan()) {
|
||||
screen->setSize(284, screenHeight());
|
||||
screen->setViewport(0, 0, 284, screenHeight());
|
||||
} else {
|
||||
int x = 16;
|
||||
int y = 24;
|
||||
int width = 284 - 32;
|
||||
int height = screenHeight() - 48;
|
||||
|
||||
if(Region::PAL()) {
|
||||
y += 24;
|
||||
height -= 48;
|
||||
}
|
||||
|
||||
screen->setSize(width, height);
|
||||
screen->setViewport(x, y, width, height);
|
||||
}
|
||||
} else if (Mode::MasterSystem()) {
|
||||
screen->setViewport(0, (240-vlines())/2, 256, vlines());
|
||||
screen->setViewport(21, 27, 248, 200);
|
||||
}
|
||||
if(Mode::GameGear()) {
|
||||
screen->setViewport(48, 48, 160, 144);
|
||||
screen->setViewport(61, 51, 160, 144);
|
||||
}
|
||||
screen->frame();
|
||||
scheduler.exit(Event::Frame);
|
||||
@@ -142,8 +150,7 @@ auto VDP::step(u32 clocks) -> void {
|
||||
|
||||
auto VDP::updateScreenSize() -> void {
|
||||
if(Display::CRT()) {
|
||||
if(overscan->value() == 0) screen->setSize(256, vlines());
|
||||
if(overscan->value() == 1) screen->setSize(256, 240);
|
||||
screen->setSize(284, screenHeight());
|
||||
}
|
||||
//if(Display::LCD()) {
|
||||
// screen->setSize(160, 144);
|
||||
|
||||
+2
-2
@@ -5,7 +5,6 @@
|
||||
struct VDP : Thread {
|
||||
Node::Object node;
|
||||
Node::Video::Screen screen;
|
||||
Node::Setting::Boolean overscan;
|
||||
Node::Setting::Boolean colorEmulation;
|
||||
Node::Setting::Natural revision;
|
||||
Node::Setting::Boolean interframeBlending; //Game Gear only
|
||||
@@ -40,6 +39,7 @@ struct VDP : Thread {
|
||||
auto step(u32 clocks) -> void;
|
||||
|
||||
auto updateScreenSize() -> void;
|
||||
auto screenHeight() const -> u32 { return Region::PAL() ? 288 : 243; }
|
||||
|
||||
auto vlines() -> u32;
|
||||
auto vblank() -> bool;
|
||||
@@ -137,7 +137,7 @@ struct VDP : Thread {
|
||||
VDP& self;
|
||||
|
||||
//dac.cpp
|
||||
auto setup(n8 voffset) -> void;
|
||||
auto setup(n9 voffset) -> void;
|
||||
auto run(n8 hoffset, n8 voffset) -> void;
|
||||
auto palette(n5 index) -> n12;
|
||||
auto power() -> void;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
auto CPU::read(n16 address) -> n8 {
|
||||
if(auto result = platform->cheat(address)) return *result;
|
||||
n2 page = address.bit(14,15);
|
||||
n2 primary = slot[page].primary;
|
||||
|
||||
|
||||
+22
-7
@@ -10,9 +10,9 @@ auto VDP::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Object>("VDP");
|
||||
|
||||
if(Model::MSX()) {
|
||||
screen = node->append<Node::Video::Screen>("Screen", 256, 192);
|
||||
screen = node->append<Node::Video::Screen>("Screen", 284, 243);
|
||||
screen->colors(1 << 4, {&VDP::colorMSX, this});
|
||||
screen->setSize(256, 192);
|
||||
screen->setSize(284, 243);
|
||||
screen->setScale(1.0, 1.0);
|
||||
screen->setAspect(8.0, 7.0);
|
||||
TMS9918::vram.allocate(16_KiB, 0x00);
|
||||
@@ -20,9 +20,9 @@ auto VDP::load(Node::Object parent) -> void {
|
||||
}
|
||||
|
||||
if(Model::MSX2()) {
|
||||
screen = node->append<Node::Video::Screen>("Screen", 512, 424);
|
||||
screen = node->append<Node::Video::Screen>("Screen", 568, 486);
|
||||
screen->colors(1 << 9, {&VDP::colorMSX2, this});
|
||||
screen->setSize(512, 424);
|
||||
screen->setSize(568, 486);
|
||||
screen->setScale(0.5, 0.5);
|
||||
screen->setAspect(8.0, 7.0);
|
||||
V9938::vram.allocate(128_KiB, 0x00);
|
||||
@@ -51,16 +51,33 @@ auto VDP::irq(bool line) -> void {
|
||||
}
|
||||
|
||||
auto VDP::frame() -> void {
|
||||
int screenScale = Model::MSX2() ? 2 : 1;
|
||||
|
||||
if(Model::MSX2()) {
|
||||
if(V9938::interlace() == 0) screen->setProgressive(1);
|
||||
if(V9938::interlace() == 1) screen->setInterlace(V9938::field());
|
||||
}
|
||||
screen->setViewport(0, 0, screen->width(), screen->height());
|
||||
|
||||
if(screen->overscan()) {
|
||||
screen->setSize(284 * screenScale, 243 * screenScale);
|
||||
screen->setViewport(0, 0, 284 * screenScale, 243 * screenScale);
|
||||
} else {
|
||||
int x = 16;
|
||||
int y = 16;
|
||||
int width = 284 - 32;
|
||||
int height = 243 - 32;
|
||||
|
||||
screen->setSize(width * screenScale, height * screenScale);
|
||||
screen->setViewport(x * screenScale, y * screenScale, width * screenScale, height * screenScale);
|
||||
}
|
||||
|
||||
screen->frame();
|
||||
scheduler.exit(Event::Frame);
|
||||
}
|
||||
|
||||
auto VDP::power() -> void {
|
||||
screen->power();
|
||||
|
||||
if(Model::MSX()) {
|
||||
TMS9918::power();
|
||||
Thread::create(system.colorburst() * 2, [&] { TMS9918::main(); });
|
||||
@@ -70,8 +87,6 @@ auto VDP::power() -> void {
|
||||
V9938::power();
|
||||
Thread::create(system.colorburst() * 2, [&] { V9938::main(); });
|
||||
}
|
||||
|
||||
screen->power();
|
||||
}
|
||||
|
||||
/* Z80 I/O ports 0x98 - 0x9b */
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
auto CPU::read(n16 address) -> n8 {
|
||||
if(auto result = platform->cheat(address)) return *result;
|
||||
|
||||
// VIDEO
|
||||
if (address == 0xe000) return vdp.data();
|
||||
|
||||
@@ -11,12 +11,12 @@ auto VDP::load(Node::Object parent) -> void {
|
||||
|
||||
node = parent->append<Node::Object>("VDP");
|
||||
|
||||
screen = node->append<Node::Video::Screen>("Screen", 256, 192);
|
||||
screen = node->append<Node::Video::Screen>("Screen", 284, 243);
|
||||
screen->colors(1 << 4, {&VDP::color, this});
|
||||
screen->setSize(256, 192);
|
||||
screen->setViewport(0, 0, 256, 192);
|
||||
screen->setSize(284, 243);
|
||||
screen->setViewport(0, 0, 284, 243);
|
||||
screen->setScale(1.0, 1.0);
|
||||
screen->setAspect(1.0, 1.0);
|
||||
screen->setAspect(8.0, 7.0);
|
||||
|
||||
TMS9918::load(screen);
|
||||
}
|
||||
@@ -39,6 +39,19 @@ auto VDP::irq(bool line) -> void {
|
||||
}
|
||||
|
||||
auto VDP::frame() -> void {
|
||||
if(screen->overscan()) {
|
||||
screen->setSize(284, 243);
|
||||
screen->setViewport(0, 0, 284, 243);
|
||||
} else {
|
||||
int x = 16;
|
||||
int y = 16;
|
||||
int width = 284 - 32;
|
||||
int height = 243 - 32;
|
||||
|
||||
screen->setSize(width, height);
|
||||
screen->setViewport(x, y, width, height);
|
||||
}
|
||||
|
||||
screen->frame();
|
||||
scheduler.exit(Event::Frame);
|
||||
}
|
||||
|
||||
+4
-3
@@ -38,7 +38,7 @@ auto AI::sample(f64& left, f64& right) -> void {
|
||||
|
||||
if(io.dmaLength[0] && io.dmaEnable) {
|
||||
io.dmaAddress[0].bit(13,23) += io.dmaAddressCarry;
|
||||
auto data = rdram.ram.read<Word>(io.dmaAddress[0]);
|
||||
auto data = rdram.ram.read<Word>(io.dmaAddress[0], "AI");
|
||||
auto l = s16(data >> 16);
|
||||
auto r = s16(data >> 0);
|
||||
left = l / 32768.0;
|
||||
@@ -50,8 +50,9 @@ auto AI::sample(f64& left, f64& right) -> void {
|
||||
}
|
||||
if(!io.dmaLength[0]) {
|
||||
if(--io.dmaCount) {
|
||||
io.dmaAddress[0] = io.dmaAddress[1];
|
||||
io.dmaLength [0] = io.dmaLength [1];
|
||||
io.dmaAddress[0] = io.dmaAddress[1];
|
||||
io.dmaLength [0] = io.dmaLength [1];
|
||||
io.dmaOriginPc[0] = io.dmaOriginPc[1];
|
||||
mi.raise(MI::IRQ::AI);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ struct AI : Thread, Memory::RCP<AI> {
|
||||
n1 dmaAddressCarry;
|
||||
n18 dmaLength[2];
|
||||
n2 dmaCount;
|
||||
u64 dmaOriginPc[2];
|
||||
n14 dacRate;
|
||||
n4 bitRate;
|
||||
} io;
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
auto AI::readWord(u32 address, Thread& thread) -> u32 {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
address = (address & 0x1f) >> 2;
|
||||
n32 data;
|
||||
|
||||
if(address != 3) {
|
||||
@@ -22,7 +22,7 @@ auto AI::readWord(u32 address, Thread& thread) -> u32 {
|
||||
}
|
||||
|
||||
auto AI::writeWord(u32 address, u32 data_, Thread& thread) -> void {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
address = (address & 0x1f) >> 2;
|
||||
n32 data = data_;
|
||||
|
||||
if(address == 0) {
|
||||
@@ -38,6 +38,7 @@ auto AI::writeWord(u32 address, u32 data_, Thread& thread) -> void {
|
||||
if(io.dmaCount < 2) {
|
||||
if(io.dmaCount == 0) mi.raise(MI::IRQ::AI);
|
||||
io.dmaLength[io.dmaCount] = length;
|
||||
io.dmaOriginPc[io.dmaCount] = cpu.ipu.pc;
|
||||
io.dmaCount++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,10 +47,12 @@ auto Cartridge::connect() -> void {
|
||||
|
||||
rtc.load();
|
||||
|
||||
isviewer.ram.allocate(64_KiB);
|
||||
isviewer.tracer = node->append<Node::Debugger::Tracer::Notification>("ISViewer", "Cartridge");
|
||||
isviewer.tracer->setAutoLineBreak(false);
|
||||
isviewer.tracer->setTerminal(true);
|
||||
if(rom.size <= 0x03fe'ffff) {
|
||||
isviewer.ram.allocate(64_KiB);
|
||||
isviewer.tracer = node->append<Node::Debugger::Tracer::Notification>("ISViewer", "Cartridge");
|
||||
isviewer.tracer->setAutoLineBreak(false);
|
||||
isviewer.tracer->setTerminal(true);
|
||||
}
|
||||
|
||||
debugger.load(node);
|
||||
|
||||
|
||||
@@ -42,6 +42,8 @@ struct Cartridge {
|
||||
Memory::Writable ram; //unserialized
|
||||
Node::Debugger::Tracer::Notification tracer;
|
||||
|
||||
auto enabled() -> bool { return ram.size; }
|
||||
|
||||
//isviewer.cpp
|
||||
auto messageChar(char c) -> void;
|
||||
auto readHalf(u32 address) -> u16;
|
||||
|
||||
@@ -85,7 +85,8 @@ auto Cartridge::Flash::writeWord(u32 address, u64 data) -> void {
|
||||
}
|
||||
if(mode == Mode::Write) {
|
||||
for(u32 index = 0; index < 128; index += 2) {
|
||||
u16 half = rdram.ram.read<Half>(source + index);
|
||||
// FIXME: this is obviously wrong, the flash can't access RDRAM
|
||||
u16 half = rdram.ram.read<Half>(source + index, "Flash");
|
||||
Memory::Writable::write<Half>(offset + index, half);
|
||||
}
|
||||
}
|
||||
|
||||
+11
-3
@@ -91,6 +91,10 @@ auto CPU::instruction() -> void {
|
||||
step(1 * 2);
|
||||
return exception.nmi();
|
||||
}
|
||||
if (scc.sysadFrozen) {
|
||||
step(1 * 2);
|
||||
return;
|
||||
}
|
||||
|
||||
if constexpr(Accuracy::CPU::Recompiler) {
|
||||
// Fast path: attempt to lookup previously compiled blocks with devirtualizeFast
|
||||
@@ -111,16 +115,20 @@ auto CPU::instruction() -> void {
|
||||
}
|
||||
|
||||
if constexpr(Accuracy::CPU::Interpreter) {
|
||||
pipeline.address = ipu.pc;
|
||||
auto data = fetch(ipu.pc);
|
||||
if (!data) return;
|
||||
pipeline.instruction = *data;
|
||||
debugger.instruction();
|
||||
instructionPrologue(*data);
|
||||
decoderEXECUTE();
|
||||
instructionEpilogue();
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionPrologue(u32 instruction) -> void {
|
||||
pipeline.address = ipu.pc;
|
||||
pipeline.instruction = instruction;
|
||||
debugger.instruction();
|
||||
}
|
||||
|
||||
auto CPU::instructionEpilogue() -> s32 {
|
||||
if constexpr(Accuracy::CPU::Recompiler) {
|
||||
//simulates timings without performing actual icache loads
|
||||
|
||||
+18
-22
@@ -36,6 +36,7 @@ struct CPU : Thread {
|
||||
auto synchronize() -> void;
|
||||
|
||||
auto instruction() -> void;
|
||||
auto instructionPrologue(u32 instruction) -> void;
|
||||
auto instructionEpilogue() -> s32;
|
||||
|
||||
auto power(bool reset) -> void;
|
||||
@@ -147,26 +148,12 @@ struct CPU : Thread {
|
||||
cpu.step(48 * 2);
|
||||
valid = 1;
|
||||
tag = address & ~0x0000'0fff;
|
||||
words[0] = cpu.busRead<Word>(tag | index | 0x00);
|
||||
words[1] = cpu.busRead<Word>(tag | index | 0x04);
|
||||
words[2] = cpu.busRead<Word>(tag | index | 0x08);
|
||||
words[3] = cpu.busRead<Word>(tag | index | 0x0c);
|
||||
words[4] = cpu.busRead<Word>(tag | index | 0x10);
|
||||
words[5] = cpu.busRead<Word>(tag | index | 0x14);
|
||||
words[6] = cpu.busRead<Word>(tag | index | 0x18);
|
||||
words[7] = cpu.busRead<Word>(tag | index | 0x1c);
|
||||
cpu.busReadBurst<ICache>(tag | index, words);
|
||||
}
|
||||
|
||||
auto writeBack(CPU& cpu) -> void {
|
||||
cpu.step(48 * 2);
|
||||
cpu.busWrite<Word>(tag | index | 0x00, words[0]);
|
||||
cpu.busWrite<Word>(tag | index | 0x04, words[1]);
|
||||
cpu.busWrite<Word>(tag | index | 0x08, words[2]);
|
||||
cpu.busWrite<Word>(tag | index | 0x0c, words[3]);
|
||||
cpu.busWrite<Word>(tag | index | 0x10, words[4]);
|
||||
cpu.busWrite<Word>(tag | index | 0x14, words[5]);
|
||||
cpu.busWrite<Word>(tag | index | 0x18, words[6]);
|
||||
cpu.busWrite<Word>(tag | index | 0x1c, words[7]);
|
||||
cpu.busWriteBurst<ICache>(tag | index, words);
|
||||
}
|
||||
|
||||
auto read(u32 address) const -> u32 { return words[address >> 2 & 7]; }
|
||||
@@ -191,16 +178,17 @@ struct CPU : Thread {
|
||||
//8KB
|
||||
struct Line {
|
||||
auto hit(u32 address) const -> bool;
|
||||
template<u32 Size> auto fill(u32 address, u64 data) -> void;
|
||||
auto fill(u32 address) -> void;
|
||||
auto writeBack() -> void;
|
||||
template<u32 Size> auto read(u32 address) const -> u64;
|
||||
template<u32 Size> auto write(u32 address, u64 data) -> void;
|
||||
|
||||
bool valid;
|
||||
bool dirty;
|
||||
u16 dirty;
|
||||
u32 tag;
|
||||
u16 index;
|
||||
u64 fillPc;
|
||||
u64 dirtyPc;
|
||||
union {
|
||||
u8 bytes[16];
|
||||
u16 halfs[8];
|
||||
@@ -300,6 +288,8 @@ struct CPU : Thread {
|
||||
auto fetch(u64 vaddr) -> maybe<u32>;
|
||||
template<u32 Size> auto busWrite(u32 address, u64 data) -> void;
|
||||
template<u32 Size> auto busRead(u32 address) -> u64;
|
||||
template<u32 Size> auto busWriteBurst(u32 address, u32 *data) -> void;
|
||||
template<u32 Size> auto busReadBurst(u32 address, u32 *data) -> void;
|
||||
template<u32 Size> auto read(u64 vaddr) -> maybe<u64>;
|
||||
template<u32 Size> auto write(u64 vaddr, u64 data, bool alignedError=true) -> bool;
|
||||
template<u32 Size> auto vaddrAlignedError(u64 vaddr, bool write) -> bool;
|
||||
@@ -637,6 +627,7 @@ struct CPU : Thread {
|
||||
//other
|
||||
n64 latch;
|
||||
n1 nmiPending;
|
||||
n1 sysadFrozen;
|
||||
} scc;
|
||||
|
||||
//interpreter-scc.cpp
|
||||
@@ -661,7 +652,7 @@ struct CPU : Thread {
|
||||
|
||||
struct Coprocessor {
|
||||
static constexpr u8 revision = 0x00;
|
||||
static constexpr u8 implementation = 0x0b;
|
||||
static constexpr u8 implementation = 0x0a;
|
||||
} coprocessor;
|
||||
|
||||
struct ControlStatus {
|
||||
@@ -696,7 +687,9 @@ struct CPU : Thread {
|
||||
//interpreter-fpu.cpp
|
||||
float_env fenv;
|
||||
|
||||
template<typename T> auto fgr(u32) -> T&;
|
||||
template<typename T> auto fgr_t(u32) -> T&;
|
||||
template<typename T> auto fgr_s(u32) -> T&;
|
||||
template<typename T> auto fgr_d(u32) -> T&;
|
||||
auto getControlRegisterFPU(n5) -> u32;
|
||||
auto setControlRegisterFPU(n5, n32) -> void;
|
||||
template<bool CVT> auto checkFPUExceptions() -> bool;
|
||||
@@ -707,8 +700,10 @@ struct CPU : Thread {
|
||||
auto fpeInvalidOperation() -> bool;
|
||||
auto fpeUnimplemented() -> bool;
|
||||
auto fpuCheckStart() -> bool;
|
||||
auto fpuCheckInput(f32& f) -> bool;
|
||||
auto fpuCheckInput(f64& f) -> bool;
|
||||
template <typename T>
|
||||
auto fpuCheckInput(T& f) -> bool;
|
||||
template <typename T>
|
||||
auto fpuCheckInputs(T& f1, T& f2) -> bool;
|
||||
auto fpuCheckOutput(f32& f) -> bool;
|
||||
auto fpuCheckOutput(f64& f) -> bool;
|
||||
auto fpuClearCause() -> void;
|
||||
@@ -911,6 +906,7 @@ struct CPU : Thread {
|
||||
auto emitFPU(u32 instruction) -> bool;
|
||||
auto emitCOP2(u32 instruction) -> bool;
|
||||
|
||||
bool callInstructionPrologue = false;
|
||||
bump_allocator allocator;
|
||||
Pool* pools[1 << 21]; //2_MiB * sizeof(void*) == 16_MiB
|
||||
} recompiler{*this};
|
||||
|
||||
+10
-53
@@ -2,63 +2,19 @@ auto CPU::DataCache::Line::hit(u32 address) const -> bool {
|
||||
return valid && tag == (address & ~0x0000'0fff);
|
||||
}
|
||||
|
||||
template<u32 Size> auto CPU::DataCache::Line::fill(u32 address, u64 data) -> void {
|
||||
cpu.step(40 * 2);
|
||||
valid = 1;
|
||||
dirty = 1;
|
||||
tag = address & ~0x0000'0fff;
|
||||
//read words according to critical doubleword first scheme
|
||||
switch(address & 8) {
|
||||
case 0:
|
||||
if constexpr(Size != Dual) {
|
||||
words[0] = cpu.busRead<Word>(tag | index | 0x0);
|
||||
words[1] = cpu.busRead<Word>(tag | index | 0x4);
|
||||
}
|
||||
write<Size>(address, data);
|
||||
words[2] = cpu.busRead<Word>(tag | index | 0x8);
|
||||
words[3] = cpu.busRead<Word>(tag | index | 0xc);
|
||||
break;
|
||||
case 8:
|
||||
if constexpr(Size != Dual) {
|
||||
words[2] = cpu.busRead<Word>(tag | index | 0x8);
|
||||
words[3] = cpu.busRead<Word>(tag | index | 0xc);
|
||||
}
|
||||
write<Size>(address, data);
|
||||
words[0] = cpu.busRead<Word>(tag | index | 0x0);
|
||||
words[1] = cpu.busRead<Word>(tag | index | 0x4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::DataCache::Line::fill(u32 address) -> void {
|
||||
cpu.step(40 * 2);
|
||||
valid = 1;
|
||||
dirty = 0;
|
||||
tag = address & ~0x0000'0fff;
|
||||
//read words according to critical doubleword first scheme
|
||||
switch(address & 8) {
|
||||
case 0:
|
||||
words[0] = cpu.busRead<Word>(tag | index | 0x0);
|
||||
words[1] = cpu.busRead<Word>(tag | index | 0x4);
|
||||
words[2] = cpu.busRead<Word>(tag | index | 0x8);
|
||||
words[3] = cpu.busRead<Word>(tag | index | 0xc);
|
||||
break;
|
||||
case 8:
|
||||
words[2] = cpu.busRead<Word>(tag | index | 0x8);
|
||||
words[3] = cpu.busRead<Word>(tag | index | 0xc);
|
||||
words[0] = cpu.busRead<Word>(tag | index | 0x0);
|
||||
words[1] = cpu.busRead<Word>(tag | index | 0x4);
|
||||
break;
|
||||
}
|
||||
valid = 1;
|
||||
dirty = 0;
|
||||
tag = address & ~0x0000'0fff;
|
||||
fillPc = cpu.ipu.pc;
|
||||
cpu.busReadBurst<DCache>(tag | index, words);
|
||||
}
|
||||
|
||||
auto CPU::DataCache::Line::writeBack() -> void {
|
||||
cpu.step(40 * 2);
|
||||
dirty = 0;
|
||||
cpu.busWrite<Word>(tag | index | 0x0, words[0]);
|
||||
cpu.busWrite<Word>(tag | index | 0x4, words[1]);
|
||||
cpu.busWrite<Word>(tag | index | 0x8, words[2]);
|
||||
cpu.busWrite<Word>(tag | index | 0xc, words[3]);
|
||||
cpu.busWriteBurst<DCache>(tag | index, words);
|
||||
}
|
||||
|
||||
auto CPU::DataCache::line(u32 vaddr) -> Line& {
|
||||
@@ -86,7 +42,8 @@ auto CPU::DataCache::Line::write(u32 address, u64 data) -> void {
|
||||
words[address >> 2 & 2 | 0] = data >> 32;
|
||||
words[address >> 2 & 2 | 1] = data >> 0;
|
||||
}
|
||||
dirty = 1;
|
||||
dirty |= ((1 << Size) - 1) << (address & 0xF);
|
||||
dirtyPc = cpu.ipu.pc;
|
||||
}
|
||||
|
||||
template<u32 Size>
|
||||
@@ -105,7 +62,7 @@ auto CPU::DataCache::readDebug(u32 vaddr, u32 address) -> u8 {
|
||||
auto& line = this->line(vaddr);
|
||||
if(!line.hit(address)) {
|
||||
Thread dummyThread{};
|
||||
return bus.read<Byte>(address, dummyThread);
|
||||
return bus.read<Byte>(address, dummyThread, "Ares Debugger");
|
||||
}
|
||||
return line.read<Byte>(address);
|
||||
}
|
||||
@@ -115,7 +72,7 @@ auto CPU::DataCache::write(u32 vaddr, u32 address, u64 data) -> void {
|
||||
auto& line = this->line(vaddr);
|
||||
if(!line.hit(address)) {
|
||||
if(line.valid && line.dirty) line.writeBack();
|
||||
return line.fill<Size>(address, data);
|
||||
line.fill(address);
|
||||
} else {
|
||||
cpu.step(1 * 2);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,12 @@ auto CPU::Debugger::load(Node::Object parent) -> void {
|
||||
tracer.instruction = parent->append<Node::Debugger::Tracer::Instruction>("Instruction", "CPU");
|
||||
tracer.instruction->setAddressBits(64, 2);
|
||||
tracer.instruction->setDepth(64);
|
||||
if constexpr(Accuracy::CPU::Recompiler) {
|
||||
tracer.instruction->setToggle([&] {
|
||||
cpu.recompiler.reset();
|
||||
cpu.recompiler.callInstructionPrologue = tracer.instruction->enabled();
|
||||
});
|
||||
}
|
||||
|
||||
tracer.exception = parent->append<Node::Debugger::Tracer::Notification>("Exception", "CPU");
|
||||
tracer.interrupt = parent->append<Node::Debugger::Tracer::Notification>("Interrupt", "CPU");
|
||||
|
||||
@@ -7,7 +7,7 @@ auto CPU::FPU::setFloatingPointMode(bool mode) -> void {
|
||||
}
|
||||
}
|
||||
|
||||
template<> auto CPU::fgr<s32>(u32 index) -> s32& {
|
||||
template<> auto CPU::fgr_t<s32>(u32 index) -> s32& {
|
||||
if(scc.status.floatingPointMode) {
|
||||
return fpu.r[index].s32;
|
||||
} else if(index & 1) {
|
||||
@@ -17,21 +17,41 @@ template<> auto CPU::fgr<s32>(u32 index) -> s32& {
|
||||
}
|
||||
}
|
||||
|
||||
template<> auto CPU::fgr<u32>(u32 index) -> u32& {
|
||||
return (u32&)fgr<s32>(index);
|
||||
template<> auto CPU::fgr_s<s32>(u32 index) -> s32& {
|
||||
if(scc.status.floatingPointMode) {
|
||||
return fpu.r[index].s32;
|
||||
} else {
|
||||
return fpu.r[index & ~1].s32;
|
||||
}
|
||||
}
|
||||
|
||||
template<> auto CPU::fgr<f32>(u32 index) -> f32& {
|
||||
template<> auto CPU::fgr_d<s32>(u32 index) -> s32& {
|
||||
fpu.r[index].s32h = 0;
|
||||
return fpu.r[index].s32;
|
||||
}
|
||||
|
||||
template<> auto CPU::fgr_t<u32>(u32 index) -> u32& {
|
||||
return (u32&)fgr_t<s32>(index);
|
||||
}
|
||||
|
||||
template<> auto CPU::fgr_t<f32>(u32 index) -> f32& {
|
||||
return fpu.r[index].f32;
|
||||
}
|
||||
|
||||
template<> auto CPU::fgr_d<f32>(u32 index) -> f32& {
|
||||
fpu.r[index].f32h = 0;
|
||||
return fpu.r[index].f32;
|
||||
}
|
||||
|
||||
template<> auto CPU::fgr_s<f32>(u32 index) -> f32& {
|
||||
if(scc.status.floatingPointMode) {
|
||||
return fpu.r[index].f32;
|
||||
} else if(index & 1) {
|
||||
return fpu.r[index & ~1].f32h;
|
||||
} else {
|
||||
return fpu.r[index & ~1].f32;
|
||||
}
|
||||
}
|
||||
|
||||
template<> auto CPU::fgr<s64>(u32 index) -> s64& {
|
||||
template<> auto CPU::fgr_t<s64>(u32 index) -> s64& {
|
||||
if(scc.status.floatingPointMode) {
|
||||
return fpu.r[index].s64;
|
||||
} else {
|
||||
@@ -39,11 +59,31 @@ template<> auto CPU::fgr<s64>(u32 index) -> s64& {
|
||||
}
|
||||
}
|
||||
|
||||
template<> auto CPU::fgr<u64>(u32 index) -> u64& {
|
||||
return (u64&)fgr<s64>(index);
|
||||
template<> auto CPU::fgr_d<s64>(u32 index) -> s64& {
|
||||
return fpu.r[index].s64;
|
||||
}
|
||||
|
||||
template<> auto CPU::fgr<f64>(u32 index) -> f64& {
|
||||
template<> auto CPU::fgr_s<s64>(u32 index) -> s64& {
|
||||
return fgr_t<s64>(index);
|
||||
}
|
||||
|
||||
template<> auto CPU::fgr_t<u64>(u32 index) -> u64& {
|
||||
return (u64&)fgr_t<s64>(index);
|
||||
}
|
||||
|
||||
template<> auto CPU::fgr_s<u64>(u32 index) -> u64& {
|
||||
return fgr_t<u64>(index);
|
||||
}
|
||||
|
||||
template<> auto CPU::fgr_t<f64>(u32 index) -> f64& {
|
||||
return fpu.r[index].f64;
|
||||
}
|
||||
|
||||
template<> auto CPU::fgr_d<f64>(u32 index) -> f64& {
|
||||
return fgr_t<f64>(index);
|
||||
}
|
||||
|
||||
template<> auto CPU::fgr_s<f64>(u32 index) -> f64& {
|
||||
if(scc.status.floatingPointMode) {
|
||||
return fpu.r[index].f64;
|
||||
} else {
|
||||
@@ -210,8 +250,9 @@ auto CPU::checkFPUExceptions() -> bool {
|
||||
|
||||
#define CHECK_FPE_IMPL(type, res, operation, convert) \
|
||||
fenv.clearExcept(); \
|
||||
type res = [&]() noinline -> type { return operation; }(); \
|
||||
if (checkFPUExceptions<convert>()) return;
|
||||
volatile type v##res = [&]() noinline -> type { return operation; }(); \
|
||||
if (checkFPUExceptions<convert>()) return; \
|
||||
type res = v##res;
|
||||
|
||||
#define CHECK_FPE(type, res, operation) CHECK_FPE_IMPL(type, res, operation, false)
|
||||
#define CHECK_FPE_CONV(type, res, operation) CHECK_FPE_IMPL(type, res, operation, true)
|
||||
@@ -240,7 +281,9 @@ auto CPU::fpuCheckStart() -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto CPU::fpuCheckInput(f32& f) -> bool {
|
||||
template <typename T>
|
||||
auto CPU::fpuCheckInput(T& f) -> bool {
|
||||
static_assert(std::is_same_v<T, f32> || std::is_same_v<T, f64>);
|
||||
switch (fpclassify(f)) {
|
||||
case FP_SUBNORMAL:
|
||||
if(fpeUnimplemented()) return exception.floatingPoint(), false;
|
||||
@@ -253,19 +296,23 @@ auto CPU::fpuCheckInput(f32& f) -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto CPU::fpuCheckInput(f64& f) -> bool {
|
||||
switch (fpclassify(f)) {
|
||||
case FP_SUBNORMAL:
|
||||
template <typename T>
|
||||
auto CPU::fpuCheckInputs(T& f1, T& f2) -> bool {
|
||||
static_assert(std::is_same_v<T, f32> || std::is_same_v<T, f64>);
|
||||
int cl1 = fpclassify(f1), cl2 = fpclassify(f2);
|
||||
if((cl1 == FP_NAN && !qnan(f1)) || (cl2 == FP_NAN && !qnan(f2))) {
|
||||
if(fpeUnimplemented()) return exception.floatingPoint(), false;
|
||||
return true;
|
||||
case FP_NAN:
|
||||
if(qnan(f) ? fpeInvalidOperation() : fpeUnimplemented())
|
||||
return exception.floatingPoint(), false;
|
||||
return true;
|
||||
}
|
||||
if(cl1 == FP_SUBNORMAL || cl2 == FP_SUBNORMAL) {
|
||||
if(fpeUnimplemented()) return exception.floatingPoint(), false;
|
||||
}
|
||||
if((cl1 == FP_NAN && qnan(f1)) || (cl2 == FP_NAN && qnan(f2))) {
|
||||
if(fpeInvalidOperation()) return exception.floatingPoint(), false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
auto fpuFlushResult(T f, u32 roundMode) -> T
|
||||
{
|
||||
@@ -364,9 +411,9 @@ auto CPU::fpuCheckInputConv<s64>(f64& f) -> bool {
|
||||
}
|
||||
|
||||
#define CF fpu.csr.compare
|
||||
#define FD(type) fgr<type>(fd)
|
||||
#define FS(type) fgr<type>(fs)
|
||||
#define FT(type) fgr<type>(ft)
|
||||
#define FD(type) fgr_d<type>(fd)
|
||||
#define FS(type) fgr_s<type>(fs)
|
||||
#define FT(type) fgr_t<type>(ft)
|
||||
|
||||
auto CPU::BC1(bool value, bool likely, s16 imm) -> void {
|
||||
if(!fpuCheckStart()) return;
|
||||
@@ -416,8 +463,7 @@ auto CPU::FABS_D(u8 fd, u8 fs) -> void {
|
||||
auto CPU::FADD_S(u8 fd, u8 fs, u8 ft) -> void {
|
||||
if(!fpuCheckStart()) return;
|
||||
f32 ffs = FS(f32), fft = FT(f32);
|
||||
if(!fpuCheckInput(ffs)) return;
|
||||
if(!fpuCheckInput(fft)) return;
|
||||
if(!fpuCheckInputs(ffs, fft)) return;
|
||||
CHECK_FPE(f32, ffd, FS(f32) + FT(f32));
|
||||
if(!fpuCheckOutput(ffd)) return;
|
||||
FD(f32) = ffd;
|
||||
@@ -427,8 +473,7 @@ auto CPU::FADD_S(u8 fd, u8 fs, u8 ft) -> void {
|
||||
auto CPU::FADD_D(u8 fd, u8 fs, u8 ft) -> void {
|
||||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f64), fft = FT(f64);
|
||||
if(!fpuCheckInput(ffs)) return;
|
||||
if(!fpuCheckInput(fft)) return;
|
||||
if(!fpuCheckInputs(ffs, fft)) return;
|
||||
CHECK_FPE(f64, ffd, ffs + fft);
|
||||
if(!fpuCheckOutput(ffd)) return;
|
||||
FD(f64) = ffd;
|
||||
@@ -758,8 +803,7 @@ auto CPU::FCVT_W_D(u8 fd, u8 fs) -> void {
|
||||
auto CPU::FDIV_S(u8 fd, u8 fs, u8 ft) -> void {
|
||||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f32), fft = FT(f32);
|
||||
if(!fpuCheckInput(ffs)) return;
|
||||
if(!fpuCheckInput(fft)) return;
|
||||
if(!fpuCheckInputs(ffs, fft)) return;
|
||||
CHECK_FPE(f32, ffd, ffs / fft);
|
||||
if(!fpuCheckOutput(ffd)) return;
|
||||
FD(f32) = ffd;
|
||||
@@ -769,8 +813,7 @@ auto CPU::FDIV_S(u8 fd, u8 fs, u8 ft) -> void {
|
||||
auto CPU::FDIV_D(u8 fd, u8 fs, u8 ft) -> void {
|
||||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f64), fft = FT(f64);
|
||||
if(!fpuCheckInput(ffs)) return;
|
||||
if(!fpuCheckInput(fft)) return;
|
||||
if(!fpuCheckInputs(ffs, fft)) return;
|
||||
CHECK_FPE(f64, ffd, ffs / fft);
|
||||
if(!fpuCheckOutput(ffd)) return;
|
||||
FD(f64) = ffd;
|
||||
@@ -814,8 +857,7 @@ auto CPU::FFLOOR_W_D(u8 fd, u8 fs) -> void {
|
||||
}
|
||||
|
||||
auto CPU::FMOV_S(u8 fd, u8 fs) -> void {
|
||||
if(!scc.status.enable.coprocessor1) return exception.coprocessor1();
|
||||
FD(f32) = FS(f32);
|
||||
return FMOV_D(fd, fs);
|
||||
}
|
||||
|
||||
auto CPU::FMOV_D(u8 fd, u8 fs) -> void {
|
||||
@@ -826,8 +868,7 @@ auto CPU::FMOV_D(u8 fd, u8 fs) -> void {
|
||||
auto CPU::FMUL_S(u8 fd, u8 fs, u8 ft) -> void {
|
||||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f32), fft = FT(f32);
|
||||
if(!fpuCheckInput(ffs)) return;
|
||||
if(!fpuCheckInput(fft)) return;
|
||||
if(!fpuCheckInputs(ffs, fft)) return;
|
||||
CHECK_FPE(f32, ffd, ffs * fft);
|
||||
if(!fpuCheckOutput(ffd)) return;
|
||||
FD(f32) = ffd;
|
||||
@@ -837,8 +878,7 @@ auto CPU::FMUL_S(u8 fd, u8 fs, u8 ft) -> void {
|
||||
auto CPU::FMUL_D(u8 fd, u8 fs, u8 ft) -> void {
|
||||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f64), fft = FT(f64);
|
||||
if(!fpuCheckInput(ffs)) return;
|
||||
if(!fpuCheckInput(fft)) return;
|
||||
if(!fpuCheckInputs(ffs, fft)) return;
|
||||
CHECK_FPE(f64, ffd, ffs * fft);
|
||||
if(!fpuCheckOutput(ffd)) return;
|
||||
FD(f64) = ffd;
|
||||
@@ -926,8 +966,7 @@ auto CPU::FSQRT_D(u8 fd, u8 fs) -> void {
|
||||
auto CPU::FSUB_S(u8 fd, u8 fs, u8 ft) -> void {
|
||||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f32), fft = FT(f32);
|
||||
if(!fpuCheckInput(ffs)) return;
|
||||
if(!fpuCheckInput(fft)) return;
|
||||
if(!fpuCheckInputs(ffs, fft)) return;
|
||||
CHECK_FPE(f32, ffd, ffs - fft);
|
||||
if(!fpuCheckOutput(ffd)) return;
|
||||
FD(f32) = ffd;
|
||||
@@ -937,8 +976,7 @@ auto CPU::FSUB_S(u8 fd, u8 fs, u8 ft) -> void {
|
||||
auto CPU::FSUB_D(u8 fd, u8 fs, u8 ft) -> void {
|
||||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f64), fft = FT(f64);
|
||||
if(!fpuCheckInput(ffs)) return;
|
||||
if(!fpuCheckInput(fft)) return;
|
||||
if(!fpuCheckInputs(ffs, fft)) return;
|
||||
CHECK_FPE(f64, ffd, ffs - fft);
|
||||
if(!fpuCheckOutput(ffd)) return;
|
||||
FD(f64) = ffd;
|
||||
@@ -995,14 +1033,14 @@ auto CPU::LWC1(u8 ft, cr64& rs, s16 imm) -> void {
|
||||
if(auto data = read<Word>(rs.u64 + imm)) FT(u32) = *data;
|
||||
}
|
||||
|
||||
auto CPU::MFC1(r64& rt, u8 fs) -> void {
|
||||
auto CPU::MFC1(r64& rt, u8 ft) -> void {
|
||||
if(!scc.status.enable.coprocessor1) return exception.coprocessor1();
|
||||
rt.u64 = FS(s32);
|
||||
rt.u64 = FT(s32);
|
||||
}
|
||||
|
||||
auto CPU::MTC1(cr64& rt, u8 fs) -> void {
|
||||
auto CPU::MTC1(cr64& rt, u8 ft) -> void {
|
||||
if(!scc.status.enable.coprocessor1) return exception.coprocessor1();
|
||||
FS(s32) = rt.u32;
|
||||
FT(s32) = rt.u32;
|
||||
}
|
||||
|
||||
auto CPU::SDC1(u8 ft, cr64& rs, s16 imm) -> void {
|
||||
|
||||
+15
-5
@@ -146,12 +146,22 @@ auto CPU::devirtualizeDebug(u64 vaddr) -> u64 {
|
||||
|
||||
template<u32 Size>
|
||||
inline auto CPU::busWrite(u32 address, u64 data) -> void {
|
||||
bus.write<Size>(address, data, *this);
|
||||
bus.write<Size>(address, data, *this, "CPU");
|
||||
}
|
||||
|
||||
template<u32 Size>
|
||||
inline auto CPU::busWriteBurst(u32 address, u32 *data) -> void {
|
||||
bus.writeBurst<Size>(address, data, *this);
|
||||
}
|
||||
|
||||
template<u32 Size>
|
||||
inline auto CPU::busRead(u32 address) -> u64 {
|
||||
return bus.read<Size>(address, *this);
|
||||
return bus.read<Size>(address, *this, "CPU");
|
||||
}
|
||||
|
||||
template<u32 Size>
|
||||
inline auto CPU::busReadBurst(u32 address, u32 *data) -> void {
|
||||
return bus.readBurst<Size>(address, data, *this);
|
||||
}
|
||||
|
||||
auto CPU::fetch(u64 vaddr) -> maybe<u32> {
|
||||
@@ -229,7 +239,7 @@ auto CPU::readDebug(u64 vaddr) -> u8 {
|
||||
case Context::Segment::Mapped:
|
||||
if(auto match = tlb.load(vaddr, true)) {
|
||||
if(match.cache) return dcache.readDebug(vaddr, match.address & context.physMask);
|
||||
return bus.read<Byte>(match.address & context.physMask, dummyThread);
|
||||
return bus.read<Byte>(match.address & context.physMask, dummyThread, "Ares Debugger");
|
||||
}
|
||||
return 0;
|
||||
case Context::Segment::Cached:
|
||||
@@ -237,9 +247,9 @@ auto CPU::readDebug(u64 vaddr) -> u8 {
|
||||
case Context::Segment::Cached32:
|
||||
return dcache.readDebug(vaddr, vaddr & 0xffff'ffff);
|
||||
case Context::Segment::Direct:
|
||||
return bus.read<Byte>(vaddr & 0x1fff'ffff, dummyThread);
|
||||
return bus.read<Byte>(vaddr & 0x1fff'ffff, dummyThread, "Ares Debugger");
|
||||
case Context::Segment::Direct32:
|
||||
return bus.read<Byte>(vaddr & 0xffff'ffff, dummyThread);
|
||||
return bus.read<Byte>(vaddr & 0xffff'ffff, dummyThread, "Ares Debugger");
|
||||
}
|
||||
|
||||
unreachable;
|
||||
|
||||
@@ -36,7 +36,11 @@ auto CPU::Recompiler::emit(u32 vaddr, u32 address, bool singleInstruction) -> Bl
|
||||
Thread thread;
|
||||
bool hasBranched = 0;
|
||||
while(true) {
|
||||
u32 instruction = bus.read<Word>(address, thread);
|
||||
u32 instruction = bus.read<Word>(address, thread, "Ares Recompiler");
|
||||
if(callInstructionPrologue) {
|
||||
mov32(reg(1), imm(instruction));
|
||||
call(&CPU::instructionPrologue);
|
||||
}
|
||||
bool branched = emitEXECUTE(instruction);
|
||||
if(unlikely(instruction == 0x1000'ffff //beq 0,0,<pc>
|
||||
|| instruction == (2 << 26 | vaddr >> 2 & 0x3ff'ffff))) { //j <pc>
|
||||
|
||||
@@ -117,6 +117,7 @@ auto CPU::serialize(serializer& s) -> void {
|
||||
s(scc.epcError);
|
||||
s(scc.latch);
|
||||
s(scc.nmiPending);
|
||||
s(scc.sysadFrozen);
|
||||
|
||||
for(auto& r : fpu.r) s(r.u64);
|
||||
s(fpu.csr.roundMode);
|
||||
|
||||
Loaded 100 of 886 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user