mirror of
https://github.com/ares-emulator/ares.git
synced 2026-11-09 17:03:16 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
443aabb89b | ||
|
|
93275dba2c | ||
|
|
3767568cdb | ||
|
|
7cfb150be8 | ||
|
|
049211abae | ||
|
|
a7ce0c22fa | ||
|
|
7205a39f23 | ||
|
|
99b759b630 | ||
|
|
a9ced1175b | ||
|
|
aeb5bc4597 | ||
|
|
f031407a14 | ||
|
|
3c42e12b75 | ||
|
|
2ce9d210af | ||
|
|
a038b2cf61 | ||
|
|
cee2ea332f | ||
|
|
b1e3d90790 | ||
|
|
f306d1223c | ||
|
|
e90e6ab846 | ||
|
|
6a103ed073 | ||
|
|
96663687f2 | ||
|
|
8d17ae1697 | ||
|
|
a2e4807810 | ||
|
|
803daf7642 | ||
|
|
c88fb1155b | ||
|
|
246a5eee93 | ||
|
|
4607b40e23 | ||
|
|
d0a963ab42 | ||
|
|
bb58366efd | ||
|
|
0891a102e4 | ||
|
|
0e74d5c8e3 | ||
|
|
672940cf06 | ||
|
|
9ecfbdbe76 | ||
|
|
679dbaccc9 | ||
|
|
57b4d68298 | ||
|
|
5d55f2b8e3 | ||
|
|
f094529d6d | ||
|
|
c2735c5c1c | ||
|
|
e1f5ab8333 | ||
|
|
cf0ef61c1f | ||
|
|
2b9907c8d0 | ||
|
|
2abe2286b2 | ||
|
|
b6d8be087f | ||
|
|
49892159d5 | ||
|
|
e88e2cf032 | ||
|
|
bcf9cab22d | ||
|
|
2973fa71e5 | ||
|
|
16f86afd9f | ||
|
|
329a8c958b | ||
|
|
91f3dfaf3d | ||
|
|
daa8d82f7b | ||
|
|
fb98870d56 | ||
|
|
07183d56f4 | ||
|
|
232cb6fcf6 | ||
|
|
320779a845 | ||
|
|
86e962bc13 | ||
|
|
0cafaad19b | ||
|
|
043b4ffd37 |
No files matched your search
@@ -27,6 +27,10 @@ ifeq ($(profile),performance)
|
||||
flags += -DPROFILE_PERFORMANCE
|
||||
endif
|
||||
|
||||
ifneq ($(filter $(cores),a26),)
|
||||
include $(ares.path)/a26/GNUmakefile
|
||||
endif
|
||||
|
||||
ifneq ($(filter $(cores),fc),)
|
||||
include $(ares.path)/fc/GNUmakefile
|
||||
endif
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
ares.components += mos6502
|
||||
|
||||
ares.objects += ares-a26-system
|
||||
ares.objects += ares-a26-controller
|
||||
ares.objects += ares-a26-cartridge
|
||||
ares.objects += ares-a26-cpu
|
||||
ares.objects += ares-a26-tia
|
||||
ares.objects += ares-a26-riot
|
||||
|
||||
$(object.path)/ares-a26-system.o: $(ares.path)/a26/system/system.cpp
|
||||
$(object.path)/ares-a26-controller.o: $(ares.path)/a26/controller/controller.cpp
|
||||
$(object.path)/ares-a26-cartridge.o: $(ares.path)/a26/cartridge/cartridge.cpp
|
||||
$(object.path)/ares-a26-cpu.o: $(ares.path)/a26/cpu/cpu.cpp
|
||||
$(object.path)/ares-a26-tia.o: $(ares.path)/a26/tia/tia.cpp
|
||||
$(object.path)/ares-a26-riot.o: $(ares.path)/a26/riot/riot.cpp
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <ares/ares.hpp>
|
||||
|
||||
#include <component/processor/mos6502/mos6502.hpp>
|
||||
|
||||
namespace ares::Atari2600 {
|
||||
#include <ares/inline.hpp>
|
||||
auto enumerate() -> vector<string>;
|
||||
auto load(Node::System& node, string name) -> bool;
|
||||
|
||||
struct Region {
|
||||
static inline auto NTSC() -> bool;
|
||||
static inline auto PAL() -> bool;
|
||||
static inline auto SECAM() -> bool;
|
||||
};
|
||||
|
||||
#include <a26/controller/controller.hpp>
|
||||
#include <a26/system/system.hpp>
|
||||
#include <a26/cartridge/cartridge.hpp>
|
||||
#include <a26/cpu/cpu.hpp>
|
||||
#include <a26/tia/tia.hpp>
|
||||
#include <a26/riot/riot.hpp>
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
struct Atari16k : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<n8> rom;
|
||||
Memory::Writable<n8> ram;
|
||||
n2 bank;
|
||||
bool hasRam;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(rom, "program.rom");
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(n16 address) -> n8 override {
|
||||
if(address == 0x1ff6) bank = 0;
|
||||
if(address == 0x1ff7) bank = 1;
|
||||
if(address == 0x1ff8) bank = 2;
|
||||
if(address == 0x1ff9) bank = 3;
|
||||
|
||||
if(address.bit(12)) {
|
||||
if(hasRam && address >= 0x1080 && address <= 0x10ff) return ram.read(address & 0x7f);
|
||||
return rom.read((bank * 0x1000) + (address & 0xfff));
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto write(n16 address, n8 data) -> bool override {
|
||||
if(address == 0x1ff6) bank = 0;
|
||||
if(address == 0x1ff7) bank = 1;
|
||||
if(address == 0x1ff8) bank = 2;
|
||||
if(address == 0x1ff9) bank = 3;
|
||||
|
||||
if(address >= 0x1000 && address <= 0x107f) {
|
||||
hasRam = true;
|
||||
ram.write(address & 0x7f, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
bank = 0;
|
||||
hasRam = 0;
|
||||
ram.allocate(128);
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(bank);
|
||||
s(hasRam);
|
||||
s(ram);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
struct Atari32k : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<n8> rom;
|
||||
Memory::Writable<n8> ram;
|
||||
n3 bank;
|
||||
bool hasRam;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(rom, "program.rom");
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(n16 address) -> n8 override {
|
||||
if(address == 0x1ff4) bank = 0;
|
||||
if(address == 0x1ff5) bank = 1;
|
||||
if(address == 0x1ff6) bank = 2;
|
||||
if(address == 0x1ff7) bank = 3;
|
||||
if(address == 0x1ff8) bank = 4;
|
||||
if(address == 0x1ff9) bank = 5;
|
||||
if(address == 0x1ffa) bank = 6;
|
||||
if(address == 0x1ffb) bank = 7;
|
||||
|
||||
if(address.bit(12)) {
|
||||
if(hasRam && address >= 0x1080 && address <= 0x10ff) return ram.read(address & 0x7f);
|
||||
return rom.read((bank * 0x1000) + (address & 0xfff));
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto write(n16 address, n8 data) -> bool override {
|
||||
if(address == 0x1ff4) bank = 0;
|
||||
if(address == 0x1ff5) bank = 1;
|
||||
if(address == 0x1ff6) bank = 2;
|
||||
if(address == 0x1ff7) bank = 3;
|
||||
if(address == 0x1ff8) bank = 4;
|
||||
if(address == 0x1ff9) bank = 5;
|
||||
if(address == 0x1ffa) bank = 6;
|
||||
if(address == 0x1ffb) bank = 7;
|
||||
|
||||
if(address >= 0x1000 && address <= 0x107f) {
|
||||
hasRam = true;
|
||||
ram.write(address & 0x7f, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
bank = 0;
|
||||
hasRam = 0;
|
||||
ram.allocate(128);
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(bank);
|
||||
s(hasRam);
|
||||
s(ram);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
struct Atari8k : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<n8> rom;
|
||||
Memory::Writable<n8> ram;
|
||||
n1 bank;
|
||||
bool hasRam;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(rom, "program.rom");
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(n16 address) -> n8 override {
|
||||
if(address == 0x1ff8) bank = 0;
|
||||
if(address == 0x1ff9) bank = 1;
|
||||
|
||||
if(address.bit(12)) {
|
||||
if(hasRam && address >= 0x1080 && address <= 0x10ff) return ram.read(address & 0x7f);
|
||||
return rom.read((bank * 0x1000) + (address & 0xfff));
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto write(n16 address, n8 data) -> bool override {
|
||||
if(address == 0x1ff8) bank = 0;
|
||||
if(address == 0x1ff9) bank = 1;
|
||||
|
||||
if(address >= 0x1000 && address <= 0x107f) {
|
||||
hasRam = true;
|
||||
ram.write(address & 0x7f, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
bank = 0;
|
||||
hasRam = 0;
|
||||
ram.allocate(128);
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(bank);
|
||||
s(hasRam);
|
||||
s(ram);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
namespace Board {
|
||||
|
||||
#include "linear.cpp"
|
||||
#include "atari8k.cpp"
|
||||
#include "atari16k.cpp"
|
||||
#include "atari32k.cpp"
|
||||
#include "commavid.cpp"
|
||||
#include "parker-bros.cpp"
|
||||
#include "tigervision.cpp"
|
||||
|
||||
auto Interface::load(Memory::Readable<n8>& memory, string name) -> bool {
|
||||
if(auto fp = pak->read(name)) {
|
||||
memory.allocate(fp->size());
|
||||
memory.load(fp);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto Interface::load(Memory::Writable<n8>& memory, string name) -> bool {
|
||||
if(auto fp = pak->read(name)) {
|
||||
memory.allocate(fp->size());
|
||||
memory.load(fp);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto Interface::save(Memory::Writable<n8>& memory, string name) -> bool {
|
||||
if(auto fp = pak->write(name)) {
|
||||
memory.save(fp);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace Board {
|
||||
|
||||
struct Interface {
|
||||
VFS::Pak pak;
|
||||
|
||||
Interface(Cartridge& cartridge) : cartridge(cartridge) {}
|
||||
virtual ~Interface() = default;
|
||||
virtual auto load() -> void {}
|
||||
virtual auto save() -> void {}
|
||||
virtual auto unload() -> void {}
|
||||
virtual auto read(n16 address) -> n8 { return 0xff; }
|
||||
virtual auto write(n16 address, n8 data) -> bool { return false; }
|
||||
virtual auto power() -> void {}
|
||||
virtual auto serialize(serializer&) -> void {}
|
||||
|
||||
auto load(Memory::Readable<n8>&, string name) -> bool;
|
||||
auto load(Memory::Writable<n8>&, string name) -> bool;
|
||||
auto save(Memory::Writable<n8>&, string name) -> bool;
|
||||
|
||||
Cartridge& cartridge;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
struct Commavid : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<n8> rom;
|
||||
Memory::Writable<n8> ram;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(rom, "program.rom");
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(n16 address) -> n8 override {
|
||||
if(address.bit(12)) {
|
||||
if(address >= 0x1000 && address <= 0x13ff) return ram.read(address & 0x3ff);
|
||||
return rom.read(address & 0xfff);
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto write(n16 address, n8 data) -> bool override {
|
||||
if(address >= 0x1400 && address <= 0x17ff) {
|
||||
ram.write(address & 0x3ff, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
ram.allocate(1_KiB);
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(ram);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
struct Linear : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<n8> rom;
|
||||
Memory::Writable<n8> ram;
|
||||
bool hasRam;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(rom, "program.rom");
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(n16 address) -> n8 override {
|
||||
if(address.bit(12)) {
|
||||
if(hasRam && address >= 0x1080 && address <= 0x10ff) return ram.read(address & 0x7f);
|
||||
return rom.read(address & 0xfff);
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto write(n16 address, n8 data) -> bool override {
|
||||
if(address >= 0x1000 && address <= 0x107f) {
|
||||
hasRam = true;
|
||||
ram.write(address & 0x7f, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
hasRam = 0;
|
||||
ram.allocate(128);
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(hasRam);
|
||||
s(ram);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
struct ParkerBros : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<n8> rom;
|
||||
n3 banks[3];
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(rom, "program.rom");
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(n16 address) -> n8 override {
|
||||
bankswitch(address);
|
||||
|
||||
if(address.bit(12)) {
|
||||
if(address >= 0x1000 && address <= 0x13ff) return rom.read((banks[0] * 0x400) + (address & 0x3ff));
|
||||
if(address >= 0x1400 && address <= 0x17ff) return rom.read((banks[1] * 0x400) + (address & 0x3ff));
|
||||
if(address >= 0x1800 && address <= 0x1bff) return rom.read((banks[2] * 0x400) + (address & 0x3ff));
|
||||
if(address >= 0x1c00 && address <= 0x1fff) return rom.read(( 7 * 0x400) + (address & 0x3ff));
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto write(n16 address, n8 data) -> bool override {
|
||||
bankswitch(address);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto bankswitch(n16 address) -> void {
|
||||
if(address >= 0x1fe0 && address <= 0x1fe7) banks[0] = address - 0x1fe0;
|
||||
if(address >= 0x1fe8 && address <= 0x1fef) banks[1] = address - 0x1fe8;
|
||||
if(address >= 0x1ff0 && address <= 0x1ff7) banks[2] = address - 0x1ff0;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
banks[0] = 0;
|
||||
banks[1] = 1;
|
||||
banks[2] = 2;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(banks[0]);
|
||||
s(banks[1]);
|
||||
s(banks[2]);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
struct Tigervision : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<n8> rom;
|
||||
n8 bank;
|
||||
n8 nextBank;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(rom, "program.rom");
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(n16 address) -> n8 override {
|
||||
if(address.bit(12)) {
|
||||
if(bank != nextBank) bank = nextBank;
|
||||
if(address <= 0x17ff) return rom.read((bank * 0x800) + (address & 0x7ff));
|
||||
return rom.read((rom.size() - 0x800) + (address & 0x7ff));
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto write(n16 address, n8 data) -> bool override {
|
||||
if(address.bit(12) && bank != nextBank) bank = nextBank;
|
||||
if(address <= 0x3f) nextBank = data;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
bank = 0;
|
||||
nextBank = 0;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s(bank);
|
||||
s(nextBank);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
#include <a26/a26.hpp>
|
||||
|
||||
namespace ares::Atari2600 {
|
||||
|
||||
Cartridge& cartridge = cartridgeSlot.cartridge;
|
||||
#include "board/board.cpp"
|
||||
#include "slot.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto Cartridge::allocate(Node::Port parent) -> Node::Peripheral {
|
||||
return node = parent->append<Node::Peripheral>(string{system.name(), " Cartridge"});
|
||||
}
|
||||
|
||||
auto Cartridge::connect() -> void {
|
||||
if(!node->setPak(pak = platform->pak(node))) return;
|
||||
|
||||
information = {};
|
||||
information.title = pak->attribute("title");
|
||||
information.region = pak->attribute("region");
|
||||
information.board = pak->attribute("board");
|
||||
|
||||
if(information.board == "Linear") board = new Board::Linear{*this};
|
||||
if(information.board == "Atari8k") board = new Board::Atari8k{*this};
|
||||
if(information.board == "Atari16k") board = new Board::Atari16k{*this};
|
||||
if(information.board == "Atari32k") board = new Board::Atari32k{*this};
|
||||
if(information.board == "Commavid") board = new Board::Commavid{*this};
|
||||
if(information.board == "ParkerBros8k") board = new Board::ParkerBros{*this};
|
||||
if(information.board == "Tigervision") board = new Board::Tigervision{*this};
|
||||
|
||||
if(!board) board = new Board::Interface{*this};
|
||||
board->pak = pak;
|
||||
board->load();
|
||||
power();
|
||||
}
|
||||
|
||||
auto Cartridge::disconnect() -> void {
|
||||
if(!node || !board) return;
|
||||
board->unload();
|
||||
board->pak.reset();
|
||||
board.reset();
|
||||
node.reset();
|
||||
}
|
||||
|
||||
auto Cartridge::save() -> void {
|
||||
if(!node) return;
|
||||
if(board) board->save();
|
||||
}
|
||||
|
||||
auto Cartridge::power() -> void {
|
||||
if(board) board->power();
|
||||
}
|
||||
|
||||
auto Cartridge::read(n16 address) -> n8 {
|
||||
if(!node) return 0xff;
|
||||
if(board) return board->read(address);
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto Cartridge::write(n16 address, n8 data) -> bool {
|
||||
if(!node) return false;
|
||||
if(board) return board->write(address, data);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
struct Cartridge;
|
||||
#include "board/board.hpp"
|
||||
|
||||
struct Cartridge {
|
||||
Node::Peripheral node;
|
||||
VFS::Pak pak;
|
||||
|
||||
auto title() const -> string { return information.title; }
|
||||
auto region() const -> string { return information.region; }
|
||||
|
||||
//cartridge.cpp
|
||||
auto allocate(Node::Port) -> Node::Peripheral;
|
||||
auto connect() -> void;
|
||||
auto disconnect() -> void;
|
||||
|
||||
auto save() -> void;
|
||||
auto power() -> void;
|
||||
|
||||
auto read(n16 address) -> n8;
|
||||
auto write(n16 address, n8 data) -> bool;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
unique_pointer<Board::Interface> board;
|
||||
|
||||
//private:
|
||||
struct Information {
|
||||
string title;
|
||||
string region;
|
||||
string board;
|
||||
} information;
|
||||
};
|
||||
|
||||
#include "slot.hpp"
|
||||
extern Cartridge& cartridge;
|
||||
@@ -0,0 +1,3 @@
|
||||
auto Cartridge::serialize(serializer& s) -> void {
|
||||
if(board) s(*board);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
CartridgeSlot cartridgeSlot{"Cartridge Slot"};
|
||||
|
||||
CartridgeSlot::CartridgeSlot(string name) : name(name) {
|
||||
}
|
||||
|
||||
auto CartridgeSlot::load(Node::Object parent) -> void {
|
||||
port = parent->append<Node::Port>(name);
|
||||
port->setFamily(system.name());
|
||||
port->setType("Cartridge");
|
||||
port->setAllocate([&](auto name) { return cartridge.allocate(port); });
|
||||
port->setConnect([&] { return cartridge.connect(); });
|
||||
port->setDisconnect([&] { return cartridge.disconnect(); });
|
||||
}
|
||||
|
||||
auto CartridgeSlot::unload() -> void {
|
||||
cartridge.disconnect();
|
||||
port = {};
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
struct CartridgeSlot {
|
||||
Node::Port port;
|
||||
Cartridge cartridge;
|
||||
|
||||
//slot.cpp
|
||||
CartridgeSlot(string name);
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
|
||||
const string name;
|
||||
};
|
||||
|
||||
extern CartridgeSlot cartridgeSlot;
|
||||
@@ -0,0 +1,8 @@
|
||||
#include <a26/a26.hpp>
|
||||
|
||||
namespace ares::Atari2600 {
|
||||
|
||||
#include "port.cpp"
|
||||
#include "gamepad/gamepad.cpp"
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
struct Controller {
|
||||
Node::Peripheral node;
|
||||
|
||||
virtual ~Controller() = default;
|
||||
|
||||
virtual auto read() -> n8 { return 0xff; }
|
||||
virtual auto write(n8 data) -> void {}
|
||||
};
|
||||
|
||||
#include "port.hpp"
|
||||
#include "gamepad/gamepad.hpp"
|
||||
@@ -0,0 +1,37 @@
|
||||
Gamepad::Gamepad(Node::Port parent) {
|
||||
node = parent->append<Node::Peripheral>("Gamepad");
|
||||
|
||||
up = node->append<Node::Input::Button>("Up");
|
||||
down = node->append<Node::Input::Button>("Down");
|
||||
left = node->append<Node::Input::Button>("Left");
|
||||
right = node->append<Node::Input::Button>("Right");
|
||||
fire = node->append<Node::Input::Button>("Fire");
|
||||
}
|
||||
|
||||
auto Gamepad::read() -> n8 {
|
||||
platform->input(up);
|
||||
platform->input(down);
|
||||
platform->input(left);
|
||||
platform->input(right);
|
||||
platform->input(fire);
|
||||
|
||||
if(!(up->value() & down->value())) {
|
||||
yHold = 0, upLatch = up->value(), downLatch = down->value();
|
||||
} else if(!yHold) {
|
||||
yHold = 1, swap(upLatch, downLatch);
|
||||
}
|
||||
|
||||
if(!(left->value() & right->value())) {
|
||||
xHold = 0, leftLatch = left->value(), rightLatch = right->value();
|
||||
} else if(!xHold) {
|
||||
xHold = 1, swap(leftLatch, rightLatch);
|
||||
}
|
||||
|
||||
n8 data = 0xff;
|
||||
data.bit(0) = !upLatch;
|
||||
data.bit(1) = !downLatch;
|
||||
data.bit(2) = !leftLatch;
|
||||
data.bit(3) = !rightLatch;
|
||||
data.bit(4) = !fire->value();
|
||||
return data;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
struct Gamepad : Controller {
|
||||
Node::Input::Button up;
|
||||
Node::Input::Button down;
|
||||
Node::Input::Button left;
|
||||
Node::Input::Button right;
|
||||
Node::Input::Button fire;
|
||||
|
||||
Gamepad(Node::Port);
|
||||
|
||||
auto read() -> n8 override;
|
||||
|
||||
private:
|
||||
b1 yHold;
|
||||
b1 upLatch;
|
||||
b1 downLatch;
|
||||
b1 xHold;
|
||||
b1 leftLatch;
|
||||
b1 rightLatch;
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
ControllerPort controllerPort1{"Controller Port 1"};
|
||||
ControllerPort controllerPort2{"Controller Port 2"};
|
||||
|
||||
ControllerPort::ControllerPort(string name) : name(name) {
|
||||
}
|
||||
|
||||
auto ControllerPort::load(Node::Object parent) -> void {
|
||||
port = parent->append<Node::Port>(name);
|
||||
port->setFamily(system.name());
|
||||
port->setType("Controller");
|
||||
port->setHotSwappable(true);
|
||||
port->setAllocate([&](auto name) { return allocate(name); });
|
||||
port->setSupported({"Gamepad"});
|
||||
}
|
||||
|
||||
auto ControllerPort::unload() -> void {
|
||||
device = {};
|
||||
port = {};
|
||||
}
|
||||
|
||||
auto ControllerPort::allocate(string name) -> Node::Peripheral {
|
||||
if(name == "Gamepad") device = new Gamepad(port);
|
||||
if(device) return device->node;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto ControllerPort::serialize(serializer& s) -> void {
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
struct ControllerPort {
|
||||
Node::Port port;
|
||||
unique_pointer<Controller> device;
|
||||
|
||||
//port.cpp
|
||||
ControllerPort(string name);
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
auto allocate(string name) -> Node::Peripheral;
|
||||
|
||||
auto read() -> n8 { if(device) return device->read(); return 0xff; }
|
||||
auto write(n8 data) -> void { if(device) return device->write(data); }
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
const string name;
|
||||
};
|
||||
|
||||
extern ControllerPort controllerPort1;
|
||||
extern ControllerPort controllerPort2;
|
||||
@@ -0,0 +1,42 @@
|
||||
#include <a26/a26.hpp>
|
||||
|
||||
namespace ares::Atari2600 {
|
||||
|
||||
CPU cpu;
|
||||
#include "memory.cpp"
|
||||
#include "timing.cpp"
|
||||
#include "debugger.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto CPU::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Object>("CPU");
|
||||
debugger.load(node);
|
||||
}
|
||||
|
||||
auto CPU::unload() -> void {
|
||||
debugger = {};
|
||||
node = {};
|
||||
}
|
||||
|
||||
auto CPU::main() -> void {
|
||||
debugger.instruction();
|
||||
instruction();
|
||||
}
|
||||
|
||||
auto CPU::step(u32 clocks) -> void {
|
||||
Thread::step(clocks);
|
||||
Thread::synchronize();
|
||||
}
|
||||
|
||||
auto CPU::power(bool reset) -> void {
|
||||
MOS6502::BCD = 1;
|
||||
MOS6502::power();
|
||||
Thread::create(system.frequency() / 3, {&CPU::main, this});
|
||||
|
||||
PC.byte(0) = readBus(0xfffc);
|
||||
PC.byte(1) = readBus(0xfffd);
|
||||
|
||||
io = {};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
struct CPU : MOS6502, Thread {
|
||||
Node::Object node;
|
||||
|
||||
struct Debugger {
|
||||
//debugger.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto instruction() -> void;
|
||||
|
||||
struct Tracer {
|
||||
Node::Debugger::Tracer::Instruction instruction;
|
||||
} tracer;
|
||||
} debugger;
|
||||
|
||||
//cpu.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
|
||||
auto main() -> void;
|
||||
auto step(u32 clocks) -> void;
|
||||
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
//memory.cpp
|
||||
auto readBus(n16 address) -> n8;
|
||||
auto writeBus(n16 address, n8 data) -> void;
|
||||
|
||||
auto readIO(n16 address) -> n8;
|
||||
auto writeIO(n16 address, n8 data) -> void;
|
||||
|
||||
auto readDebugger(n16 address) -> n8 override;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
//timing.cpp
|
||||
auto read(n16 address) -> n8 override;
|
||||
auto write(n16 address, n8 data) -> void override;
|
||||
auto lastCycle() -> void override;
|
||||
auto nmi(n16& vector) -> void override;
|
||||
|
||||
auto rdyLine(bool) -> void;
|
||||
|
||||
//protected:
|
||||
struct IO {
|
||||
n1 rdyLine = 1;
|
||||
} io;
|
||||
};
|
||||
|
||||
extern CPU cpu;
|
||||
@@ -0,0 +1,12 @@
|
||||
auto CPU::Debugger::load(Node::Object parent) -> void {
|
||||
tracer.instruction = parent->append<Node::Debugger::Tracer::Instruction>("Instruction", "CPU");
|
||||
tracer.instruction->setAddressBits(16);
|
||||
}
|
||||
|
||||
auto CPU::Debugger::instruction() -> void {
|
||||
if(tracer.instruction->enabled() && tracer.instruction->address(cpu.PC)) {
|
||||
tracer.instruction->notify(cpu.disassembleInstruction(), cpu.disassembleContext());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
inline auto CPU::readBus(n16 address) -> n8 {
|
||||
address &= 0x1fff;
|
||||
n8 data = cartridge.read(address);
|
||||
|
||||
if(address.bit(12) == 0 && address.bit(7) == 0) {
|
||||
return tia.read(address & 0xf);
|
||||
}
|
||||
|
||||
if(address.bit(12) == 0 && address.bit(9) == 0 && address.bit(7) == 1) {
|
||||
return riot.readRam(address & 0x7f);
|
||||
}
|
||||
|
||||
if(address.bit(12) == 0 && address.bit(9) == 1 && address.bit(7) == 1) {
|
||||
return riot.readIo((address - 0x80) & 0x1f);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
inline auto CPU::writeBus(n16 address, n8 data) -> void {
|
||||
address &= 0x1fff;
|
||||
cartridge.write(address, data);
|
||||
|
||||
if(address.bit(12) == 0 && address.bit(7) == 0) {
|
||||
return tia.write(address & 0x3f, data);
|
||||
}
|
||||
|
||||
if(address.bit(12) == 0 && address.bit(9) == 0 && address.bit(7) == 1) {
|
||||
return riot.writeRam(address & 0x7f, data);
|
||||
}
|
||||
|
||||
if(address.bit(12) == 0 && address.bit(9) == 1 && address.bit(7) == 1) {
|
||||
return riot.writeIo((address - 0x80) & 0x1f, data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
auto CPU::readDebugger(n16 address) -> n8 {
|
||||
return readBus(address);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
auto CPU::serialize(serializer& s) -> void {
|
||||
MOS6502::serialize(s);
|
||||
Thread::serialize(s);
|
||||
s(io.rdyLine);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
auto CPU::read(n16 address) -> n8 {
|
||||
if(io.rdyLine == 0) {
|
||||
while (io.rdyLine == 0) {
|
||||
step(1);
|
||||
}
|
||||
|
||||
MDR = readBus(address);
|
||||
return MDR;
|
||||
}
|
||||
|
||||
step(1);
|
||||
MDR = readBus(address);
|
||||
return MDR;
|
||||
}
|
||||
|
||||
auto CPU::write(n16 address, n8 data) -> void {
|
||||
step(1);
|
||||
writeBus(address, MDR = data);
|
||||
}
|
||||
|
||||
auto CPU::lastCycle() -> void {
|
||||
}
|
||||
|
||||
auto CPU::nmi(n16& vector) -> void {
|
||||
|
||||
}
|
||||
|
||||
auto CPU::rdyLine(bool line) -> void {
|
||||
io.rdyLine = line;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
auto RIOT::readRam(n8 address) -> n8 {
|
||||
return ram[address];
|
||||
}
|
||||
|
||||
auto RIOT::writeRam(n8 address, n8 data) -> void {
|
||||
ram[address] = data;
|
||||
}
|
||||
|
||||
auto RIOT::readIo(n8 address) -> n8 {
|
||||
switch(address) {
|
||||
case 0x00: return readPortA();
|
||||
case 0x01: return port[0].direction;
|
||||
case 0x02: return readPortB();
|
||||
case 0x03: return port[1].direction;
|
||||
case 0x04: return timer.counter;
|
||||
}
|
||||
|
||||
debug(unimplemented, "[RIOT] IO read ", hex(address));
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto RIOT::writeIo(n8 address, n8 data) -> void {
|
||||
switch(address) {
|
||||
case 0x00: writePortA(data); return;
|
||||
case 0x01: port[0].direction = data; return;
|
||||
case 0x02: writePortB(data); return;
|
||||
case 0x03: port[1].direction = data; return;
|
||||
case 0x14: case 0x15: case 0x16: case 0x17: {
|
||||
const n16 intervals[] = {1, 8, 64, 1024};
|
||||
timer.counter = data;
|
||||
timer.interval = intervals[address - 0x14];
|
||||
timer.reload = timer.interval;
|
||||
return; }
|
||||
}
|
||||
|
||||
debug(unimplemented, "[RIOT] IO write ",hex(address), " = ", hex(data));
|
||||
}
|
||||
|
||||
auto RIOT::readPortA() -> n8 {
|
||||
n8 data;
|
||||
// TODO: Handle when configured as outputs
|
||||
data.bit(0, 3) = controllerPort2.read().bit(0, 4);
|
||||
data.bit(4, 7) = controllerPort1.read().bit(0, 4);
|
||||
return data;
|
||||
}
|
||||
|
||||
auto RIOT::writePortA(n8 data) -> void {
|
||||
port[0].data = data;
|
||||
}
|
||||
|
||||
auto RIOT::readPortB() -> n8 {
|
||||
system.controls.poll();
|
||||
|
||||
n8 data;
|
||||
|
||||
for(auto n : range(8)) if(port[1].direction.bit(n)) { data.bit(n) = port[1].data.bit(n); }
|
||||
|
||||
// Atari hard-wires these bits as input
|
||||
data.bit(0) = !system.controls.reset->value();
|
||||
data.bit(1) = !system.controls.select->value();
|
||||
|
||||
// These are toggle switches; flip the values on the rising edge
|
||||
if(system.controls.tvType->value() && !tvTypeLatch) {
|
||||
tvType = !tvType;
|
||||
}
|
||||
tvTypeLatch = system.controls.tvType->value();
|
||||
|
||||
if(system.controls.leftDifficulty->value() && !leftDifficultyLatch) {
|
||||
leftDifficulty = !leftDifficulty;
|
||||
}
|
||||
leftDifficultyLatch = system.controls.leftDifficulty->value();
|
||||
|
||||
if(system.controls.rightDifficulty->value() && !rightDifficultyLatch) {
|
||||
rightDifficulty = !rightDifficulty;
|
||||
}
|
||||
rightDifficultyLatch = system.controls.rightDifficulty->value();
|
||||
|
||||
data.bit(3) = tvType;
|
||||
data.bit(6) = leftDifficulty;
|
||||
data.bit(7) = rightDifficulty;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
auto RIOT::writePortB(n8 data) -> void {
|
||||
port[1].data = data;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#include <a26/a26.hpp>
|
||||
|
||||
namespace ares::Atari2600 {
|
||||
|
||||
RIOT riot;
|
||||
|
||||
#include "io.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto RIOT::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Object>("RIOT");
|
||||
|
||||
ram.allocate(128);
|
||||
}
|
||||
|
||||
auto RIOT::unload() -> void {
|
||||
ram.reset();
|
||||
node.reset();
|
||||
}
|
||||
|
||||
auto RIOT::main() -> void {
|
||||
decrementTimer();
|
||||
step(1);
|
||||
}
|
||||
|
||||
auto RIOT::decrementTimer() -> void {
|
||||
if(--timer.interval == 0) {
|
||||
if(--timer.counter == 0xff) {
|
||||
timer.reload = 1;
|
||||
}
|
||||
|
||||
timer.interval = timer.reload;
|
||||
}
|
||||
}
|
||||
|
||||
auto RIOT::step(u32 clocks) -> void {
|
||||
Thread::step(clocks);
|
||||
Thread::synchronize();
|
||||
}
|
||||
|
||||
auto RIOT::power(bool reset) -> void {
|
||||
Thread::create(system.frequency() / 3, {&RIOT::main, this});
|
||||
timer = {};
|
||||
port[0] = {.data = 0xff};
|
||||
port[1] = {.data = 0xff};
|
||||
leftDifficulty = 1;
|
||||
rightDifficulty = 1;
|
||||
tvType = 1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
struct RIOT : Thread {
|
||||
Node::Object node;
|
||||
Memory::Writable<n8> ram;
|
||||
|
||||
//riot.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
|
||||
auto main() -> void;
|
||||
auto step(u32 clocks) -> void;
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
auto decrementTimer() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readRam(n8 address) -> n8;
|
||||
auto writeRam(n8 address, n8 data) -> void;
|
||||
auto readIo(n8 address) -> n8;
|
||||
auto writeIo(n8 address, n8 data) -> void;
|
||||
|
||||
auto readPortA() -> n8;
|
||||
auto readPortB() -> n8;
|
||||
auto writePortA(n8 data) -> void;
|
||||
auto writePortB(n8 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
struct {
|
||||
u8 counter;
|
||||
n16 interval;
|
||||
n16 reload;
|
||||
} timer;
|
||||
|
||||
struct Port {
|
||||
n8 data;
|
||||
n8 direction;
|
||||
} port[2];
|
||||
|
||||
n1 leftDifficulty;
|
||||
n1 leftDifficultyLatch;
|
||||
n1 rightDifficulty;
|
||||
n1 rightDifficultyLatch;
|
||||
n1 tvType;
|
||||
n1 tvTypeLatch;
|
||||
};
|
||||
|
||||
extern RIOT riot;
|
||||
@@ -0,0 +1,21 @@
|
||||
//serialization.cpp
|
||||
auto RIOT::serialize(serializer& s) -> void {
|
||||
Thread::serialize(s);
|
||||
|
||||
s(ram);
|
||||
s(timer.counter);
|
||||
s(timer.interval);
|
||||
s(timer.reload);
|
||||
|
||||
for(auto n : range(2)) {
|
||||
s(port[n].data);
|
||||
s(port[n].direction);
|
||||
}
|
||||
|
||||
s(leftDifficulty);
|
||||
s(leftDifficultyLatch);
|
||||
s(rightDifficulty);
|
||||
s(rightDifficultyLatch);
|
||||
s(tvType);
|
||||
s(tvTypeLatch);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
auto System::Controls::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Object>("Controls");
|
||||
|
||||
reset = node->append<Node::Input::Button>("Reset");
|
||||
select = node->append<Node::Input::Button>("Select");
|
||||
leftDifficulty = node->append<Node::Input::Button>("Left Difficulty");
|
||||
rightDifficulty = node->append<Node::Input::Button>("Right Difficulty");
|
||||
tvType = node->append<Node::Input::Button>("TV Type");
|
||||
}
|
||||
|
||||
auto System::Controls::poll() -> void {
|
||||
platform->input(reset);
|
||||
platform->input(select);
|
||||
platform->input(leftDifficulty);
|
||||
platform->input(rightDifficulty);
|
||||
platform->input(tvType);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
auto System::serialize(bool synchronize) -> serializer {
|
||||
if(synchronize) scheduler.enter(Scheduler::Mode::Synchronize);
|
||||
serializer s;
|
||||
|
||||
u32 signature = SerializerSignature;
|
||||
char version[16] = {};
|
||||
char description[512] = {};
|
||||
memory::copy(&version, (const char*)SerializerVersion, SerializerVersion.size());
|
||||
|
||||
s(signature);
|
||||
s(synchronize);
|
||||
s(version);
|
||||
s(description);
|
||||
|
||||
serialize(s, synchronize);
|
||||
return s;
|
||||
}
|
||||
|
||||
auto System::unserialize(serializer& s) -> bool {
|
||||
u32 signature = 0;
|
||||
bool synchronize = true;
|
||||
char version[16] = {};
|
||||
char description[512] = {};
|
||||
|
||||
s(signature);
|
||||
s(synchronize);
|
||||
s(version);
|
||||
s(description);
|
||||
|
||||
if(signature != SerializerSignature) return false;
|
||||
if(string{version} != SerializerVersion) return false;
|
||||
|
||||
if(synchronize) power(/* reset = */ false);
|
||||
serialize(s, synchronize);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto System::serialize(serializer& s, bool synchronize) -> void {
|
||||
scheduler.setSynchronize(synchronize);
|
||||
s(cartridge);
|
||||
s(cpu);
|
||||
s(tia);
|
||||
s(riot);
|
||||
s(controllerPort1);
|
||||
s(controllerPort2);
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
#include <a26/a26.hpp>
|
||||
|
||||
namespace ares::Atari2600 {
|
||||
|
||||
auto enumerate() -> vector<string> {
|
||||
return {
|
||||
"[Atari] Atari 2600 (NTSC)",
|
||||
"[Atari] Atari 2600 (PAL)",
|
||||
"[Atari] Atari 2600 (SECAM)",
|
||||
};
|
||||
}
|
||||
|
||||
auto load(Node::System& node, string name) -> bool {
|
||||
if(!enumerate().find(name)) return false;
|
||||
return system.load(node, name);
|
||||
}
|
||||
|
||||
Random random;
|
||||
Scheduler scheduler;
|
||||
System system;
|
||||
#include "controls.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto System::game() -> string {
|
||||
if(cartridge.node) {
|
||||
return cartridge.title();
|
||||
}
|
||||
|
||||
return "(no cartridge connected)";
|
||||
}
|
||||
|
||||
auto System::run() -> void {
|
||||
scheduler.enter();
|
||||
}
|
||||
|
||||
auto System::load(Node::System& root, string name) -> bool {
|
||||
if(node) unload();
|
||||
|
||||
information = {};
|
||||
if(name.find("NTSC")) {
|
||||
information.name = "Atari 2600";
|
||||
information.region = Region::NTSC;
|
||||
information.frequency = 3579546;
|
||||
}
|
||||
if(name.find("PAL")) {
|
||||
information.name = "Atari 2600";
|
||||
information.region = Region::PAL;
|
||||
information.frequency = 3546894;
|
||||
}
|
||||
if(name.find("SECAM")) {
|
||||
information.name = "Atari 2600";
|
||||
information.region = Region::SECAM;
|
||||
information.frequency = 3546894;
|
||||
}
|
||||
|
||||
node = Node::System::create(information.name);
|
||||
node->setGame({&System::game, this});
|
||||
node->setRun({&System::run, this});
|
||||
node->setPower({&System::power, this});
|
||||
node->setSave({&System::save, this});
|
||||
node->setUnload({&System::unload, this});
|
||||
node->setSerialize({&System::serialize, this});
|
||||
node->setUnserialize({&System::unserialize, this});
|
||||
root = node;
|
||||
|
||||
scheduler.reset();
|
||||
controls.load(node);
|
||||
riot.load(node);
|
||||
cpu.load(node);
|
||||
tia.load(node);
|
||||
cartridgeSlot.load(node);
|
||||
controllerPort1.load(node);
|
||||
controllerPort2.load(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto System::save() -> void {
|
||||
if(!node) return;
|
||||
cartridge.save();
|
||||
}
|
||||
|
||||
auto System::unload() -> void {
|
||||
if(!node) return;
|
||||
save();
|
||||
riot.unload();
|
||||
cpu.unload();
|
||||
tia.unload();
|
||||
cartridgeSlot.unload();
|
||||
controllerPort1.unload();
|
||||
controllerPort2.unload();
|
||||
node = {};
|
||||
}
|
||||
|
||||
auto System::power(bool reset) -> void {
|
||||
for(auto& setting : node->find<Node::Setting::Setting>()) setting->setLatch();
|
||||
|
||||
random.entropy(Random::Entropy::Low);
|
||||
cartridge.power();
|
||||
riot.power(reset);
|
||||
cpu.power(reset);
|
||||
tia.power(reset);
|
||||
scheduler.power(cpu);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
extern Random random;
|
||||
|
||||
struct System {
|
||||
Node::System node;
|
||||
|
||||
struct Controls {
|
||||
Node::Object node;
|
||||
Node::Input::Button reset;
|
||||
Node::Input::Button select;
|
||||
Node::Input::Button leftDifficulty;
|
||||
Node::Input::Button rightDifficulty;
|
||||
Node::Input::Button tvType;
|
||||
|
||||
//controls.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto poll() -> void;
|
||||
} controls;
|
||||
|
||||
enum class Region : u32 { NTSC, PAL, SECAM };
|
||||
|
||||
auto name() const -> string { return information.name; }
|
||||
auto region() const -> Region { return information.region; }
|
||||
auto frequency() const -> u32 { return information.frequency; }
|
||||
|
||||
//system.cpp
|
||||
auto game() -> string;
|
||||
auto run() -> void;
|
||||
|
||||
auto load(Node::System& node, string name) -> bool;
|
||||
auto save() -> void;
|
||||
auto unload() -> void;
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(bool synchronize) -> serializer;
|
||||
auto unserialize(serializer&) -> bool;
|
||||
|
||||
private:
|
||||
struct Information {
|
||||
string name = "Atari 2600";
|
||||
Region region = Region::NTSC;
|
||||
u32 frequency = Constants::Colorburst::NTSC;
|
||||
} information;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&, bool synchronize) -> void;
|
||||
};
|
||||
|
||||
extern System system;
|
||||
|
||||
auto Region::NTSC() -> bool { return system.region() == System::Region::NTSC; }
|
||||
auto Region::PAL() -> bool { return system.region() == System::Region::PAL; }
|
||||
auto Region::SECAM() -> bool { return system.region() == System::Region::SECAM; }
|
||||
@@ -0,0 +1,64 @@
|
||||
auto TIA::runAudio() -> void {
|
||||
if(io.hcounter == 9 || io.hcounter == 81) {
|
||||
audio[0].phase0();
|
||||
audio[1].phase0();
|
||||
}
|
||||
|
||||
if(io.hcounter == 37 || io.hcounter == 149) {
|
||||
auto a = audio[0].phase1();
|
||||
auto b = audio[1].phase1();
|
||||
|
||||
double output = 0;
|
||||
output += volume[a];
|
||||
output += volume[b];
|
||||
|
||||
stream->frame(output / 2.0);
|
||||
}
|
||||
}
|
||||
|
||||
auto TIA::AudioChannel::phase0() -> void {
|
||||
if (enable) {
|
||||
switch (control.bit(0, 1)) {
|
||||
case 0x00: case 0x01: pulseCounterPaused = 0; break;
|
||||
case 0x02: pulseCounterPaused = noiseCounter.bit(1, 4) != 1; break;
|
||||
case 0x03: pulseCounterPaused = !noiseCounter.bit(0); break;
|
||||
}
|
||||
|
||||
noiseFeedback = (noiseCounter.bit(2) ^ noiseCounter.bit(0)) || noiseCounter == 0;
|
||||
if(control.bit(0, 2) == 0) {
|
||||
noiseFeedback = !(control.bit(2, 3)) ||
|
||||
!(noiseCounter || (pulseCounter != 0x0a)) ||
|
||||
(pulseCounter.bit(0) ^ noiseCounter.bit(0));
|
||||
}
|
||||
}
|
||||
|
||||
enable = divCounter == frequency;
|
||||
if (divCounter == frequency || divCounter == 0x1f) {
|
||||
divCounter = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
divCounter++;
|
||||
}
|
||||
|
||||
auto TIA::AudioChannel::phase1() -> u8 {
|
||||
if(!enable) return pulseCounter.bit(0) * volume;
|
||||
|
||||
pulseFeedback = 0;
|
||||
switch(control.bit(2, 3)) {
|
||||
case 0x00: pulseFeedback = pulseCounter != 0x0a && control.bit(0, 2) && (pulseCounter.bit(1) ^ pulseCounter.bit(0)); break;
|
||||
case 0x01: pulseFeedback = !pulseCounter.bit(3); ; break;
|
||||
case 0x02: pulseFeedback = !noiseCounter.bit(0); ; break;
|
||||
case 0x03: pulseFeedback = !(pulseCounter.bit(1) || !pulseCounter.bit(1, 3)); ; break;
|
||||
}
|
||||
|
||||
noiseCounter >>= 1;
|
||||
if (noiseFeedback) noiseCounter.bit(4) = 1;
|
||||
|
||||
if(!pulseCounterPaused) {
|
||||
pulseCounter = ~(pulseCounter >> 1) & 7;
|
||||
if (pulseFeedback) pulseCounter.bit(3) = 1;
|
||||
}
|
||||
|
||||
return pulseCounter.bit(0) * volume;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
auto TIA::color(n32 color) -> n64 {
|
||||
u64 ntscColors[128] = {
|
||||
0x0000'0000'0000ull, 0x4040'4040'4040ull, 0x6c6c'6c6c'6c6cull, 0x9090'9090'9090ull, 0xb0b0'b0b0'b0b0ull, 0xc8c8'c8c8'c8c8ull, 0xdcdc'dcdc'dcdcull, 0xecec'ecec'ececull,
|
||||
0x4444'4444'0000ull, 0x6464'6464'1010ull, 0x8484'8484'2424ull, 0xa0a0'a0a0'3434ull, 0xb8b8'b8b8'4040ull, 0xd0d0'd0d0'5050ull, 0xe8e8'e8e8'5c5cull, 0xfcfc'fcfc'6868ull,
|
||||
0x7070'2828'0000ull, 0x8484'4444'1414ull, 0x9898'5c5c'2828ull, 0xacac'7878'3c4cull, 0xbcbc'8c8c'4c4cull, 0xcccc'a0a0'5c5cull, 0xdcdc'b4b4'6868ull, 0xecec'c8c8'7878ull,
|
||||
0x8484'1818'0000ull, 0x9898'3434'1818ull, 0xacac'5050'3030ull, 0xc0c0'6868'4848ull, 0xd0d0'8080'5c5cull, 0xe0e0'9494'7070ull, 0xecec'a8a8'8080ull, 0xfcfc'bcbc'9494ull,
|
||||
0x8888'0000'0000ull, 0x9c9c'2020'2020ull, 0xb0b0'3c3c'3c3cull, 0xc0c0'5858'5858ull, 0xd0d0'7070'7070ull, 0xe0e0'8888'8888ull, 0xecec'a0a0'a0a0ull, 0xfcfc'b4b4'b4b4ull,
|
||||
0x7878'0000'5c5cull, 0x8c8c'2020'7474ull, 0xa0a0'3c3c'8888ull, 0xb0b0'5858'9c9cull, 0xc0c0'7070'b0b0ull, 0xd0d0'8484'c0c0ull, 0xdcdc'9c9c'd0d0ull, 0xecec'b0b0'e0e0ull,
|
||||
0x4848'0000'7878ull, 0x6060'2020'9090ull, 0x7878'3c3c'a4a4ull, 0x8c8c'5858'b8b8ull, 0xa0a0'7070'ccccull, 0xb4b4'8484'dcdcull, 0xc4c4'9c9c'ececull, 0xd4d4'b0b0'fcfcull,
|
||||
0x1414'0000'8484ull, 0x3030'2020'9898ull, 0x4c4c'3c3c'acacull, 0x6868'5858'c0c0ull, 0x7c7c'7070'd0d0ull, 0x9494'8888'e0e0ull, 0xa8a8'a0a0'ececull, 0xbcbc'b4b4'fcfcull,
|
||||
0x0000'0000'8888ull, 0x1c1c'2020'9c9cull, 0x3838'4040'b0b0ull, 0x5050'5c5c'c0c0ull, 0x6868'7474'd0d0ull, 0x7c7c'8c8c'e0e0ull, 0x9090'a4a4'ececull, 0xa4a4'b8b8'fcfcull,
|
||||
0x0000'1818'7c7cull, 0x1c1c'3838'9090ull, 0x3838'5454'a8a8ull, 0x5050'7070'bcbcull, 0x6868'8888'ccccull, 0x7c7c'9c9c'dcdcull, 0x9090'b4b4'ececull, 0xa4a4'c8c8'fcfcull,
|
||||
0x0000'2c2c'5c5cull, 0x1c1c'4c4c'7878ull, 0x3838'6868'9090ull, 0x5050'8484'acacull, 0x6868'9c9c'c0c0ull, 0x7c7c'b4b4'd4d4ull, 0x9090'cccc'e8e8ull, 0xa4a4'e0e0'fcfcull,
|
||||
0x0000'3c3c'2c2cull, 0x1c1c'5c5c'4848ull, 0x3838'7c7c'6464ull, 0x5050'9c9c'8080ull, 0x6868'b4b4'9494ull, 0x7c7c'd0d0'acacull, 0x9090'e4e4'c0c0ull, 0xa4a4'fcfc'd4d4ull,
|
||||
0x0000'3c3c'0000ull, 0x2020'5c5c'2020ull, 0x4040'7c7c'4040ull, 0x5c5c'9c9c'5c5cull, 0x7474'b4b4'7474ull, 0x8c8c'd0d0'8c8cull, 0xa4a4'e4e4'a4a4ull, 0xb8b8'fcfc'b8b8ull,
|
||||
0x1414'3838'0000ull, 0x3434'5c5c'1c1cull, 0x5050'7c7c'3838ull, 0x6c6c'9898'5050ull, 0x8484'b4b4'6868ull, 0x9c9c'cccc'7c7cull, 0xb4b4'e4e4'9090ull, 0xc8c8'fcfc'a4a4ull,
|
||||
0x2c2c'3030'0000ull, 0x4c4c'5050'1c1cull, 0x6868'7070'3434ull, 0x8484'8c8c'4c4cull, 0x9c9c'a8a8'6464ull, 0xb4b4'c0c0'7878ull, 0xcccc'd4d4'8888ull, 0xe0e0'ecec'9c9cull,
|
||||
0x4444'2828'0000ull, 0x6464'4848'1818ull, 0x8484'6868'3030ull, 0xa0a0'8484'4444ull, 0xb8b8'9c9c'5858ull, 0xd0d0'b4b4'6c6cull, 0x8e8c'cccc'7c7cull, 0xfcfc'c0c0'8c8cull,
|
||||
};
|
||||
|
||||
u64 palColors[128] = {
|
||||
0x0000'0000'0000ull, 0x2828'2828'2828ull, 0x5050'5050'5050ull, 0x7474'7474'7474ull, 0x9494'9494'9494ull, 0xb4b4'b4b4'b4b4ull, 0xd0d0'd0d0'd0d0ull, 0xecec'ecec'ececull,
|
||||
0x0000'0000'0000ull, 0x2828'2828'2828ull, 0x5050'5050'5050ull, 0x7474'7474'7474ull, 0x9494'9494'9494ull, 0xb4b4'b4b4'b4b4ull, 0xd0d0'd0d0'd0d0ull, 0xecec'ecec'ececull,
|
||||
0x8080'5858'0000ull, 0x9494'7070'2020ull, 0xa8a8'8484'3c3cull, 0xbcbc'9c9c'5858ull, 0xcccc'acac'7070ull, 0xdcdc'c0c0'8484ull, 0xecec'd0d0'9c9cull, 0xfcfc'e0e0'b0b0ull,
|
||||
0x4444'5c5c'0000ull, 0x5c5c'7878'2020ull, 0x7474'9090'3c3cull, 0x8c8c'acac'5858ull, 0xa0a0'c0c0'7070ull, 0xb0b0'd4d4'8484ull, 0xc4c4'e8e8'9c9cull, 0xd4d4'fcfc'b0b0ull,
|
||||
0x7070'3434'0000ull, 0x8888'5050'2020ull, 0xa0a0'6868'3c3cull, 0xb4b4'8484'5858ull, 0xc8c8'9898'7070ull, 0xdcdc'acac'8484ull, 0xecec'c0c0'9c9cull, 0xfcfc'd4d4'b0b0ull,
|
||||
0x0000'6464'1414ull, 0x2020'8080'3434ull, 0x3c3c'9898'5050ull, 0x5858'b0b0'6c6cull, 0x7070'c4c4'8484ull, 0x8484'd8d8'9c9cull, 0x9c9c'e8e8'b4b4ull, 0xb0b0'fcfc'c8c8ull,
|
||||
0x7070'0000'1414ull, 0x8888'2020'3434ull, 0xa0a0'3c3c'5050ull, 0xb4b4'5858'6c6cull, 0xc8c8'7070'8484ull, 0xdcdc'8484'9c9cull, 0xecec'9c9c'b4b4ull, 0xfcfc'b0b0'c8c8ull,
|
||||
0x0000'5c5c'5c5cull, 0x2020'7474'7474ull, 0x3c3c'8c8c'8c8cull, 0x5858'a4a4'a4a4ull, 0x7070'b8b8'b8b8ull, 0x8484'c8c8'c8c8ull, 0x9c9c'dcdc'dcdcull, 0xb0b0'ecec'ececull,
|
||||
0x7070'0000'5c5cull, 0x8484'2020'7474ull, 0x9494'3c3c'8888ull, 0xa8a8'5858'9c9cull, 0xb4b4'7070'b0b0ull, 0xc4c4'8484'c0c0ull, 0xd0d0'9c9c'd0d0ull, 0xe0e0'b0b0'e0e0ull,
|
||||
0x0000'3c3c'7070ull, 0x1c1c'5858'8888ull, 0x3838'7474'a0a0ull, 0x5050'8c8c'b4b4ull, 0x6868'a4a4'c8c8ull, 0x7c7c'b8b8'dcdcull, 0x9090'cccc'ececull, 0xa4a4'e0e0'fcfcull,
|
||||
0x5858'0000'7070ull, 0x6c6c'2020'8888ull, 0x8080'3c3c'a0a0ull, 0x9494'5858'b4b4ull, 0xa4a4'7070'c8c8ull, 0xb4b4'8484'dcdcull, 0xc4c4'9c9c'ececull, 0xd4d4'b0b0'fcfcull,
|
||||
0x0000'2020'7070ull, 0x1c1c'3c3c'8888ull, 0x3838'5858'a0a0ull, 0x5050'7474'b4b4ull, 0x6868'8888'c8c8ull, 0x7c7c'a0a0'dcdcull, 0x9090'b4b4'ececull, 0xa4a4'c8c8'fcfcull,
|
||||
0x3c3c'0000'8080ull, 0x5454'2020'9494ull, 0x6c6c'3c3c'a8a8ull, 0x8080'5858'bcbcull, 0x9494'7070'ccccull, 0xa8a8'8484'dcdcull, 0xb8b8'9c9c'ececull, 0xc8c8'b0b0'fcfcull,
|
||||
0x0000'0000'8888ull, 0x2020'2020'9c9cull, 0x3c3c'3c3c'b0b0ull, 0x5858'5858'c0c0ull, 0x7070'7070'd0d0ull, 0x8484'8484'e0e0ull, 0x9c9c'9c9c'ececull, 0xb0b0'b0b0'fcfcull,
|
||||
0x0000'0000'0000ull, 0x2828'2828'2828ull, 0x5050'5050'5050ull, 0x7474'7474'7474ull, 0x9494'9494'9494ull, 0xb4b4'b4b4'b4b4ull, 0xd0d0'd0d0'd0d0ull, 0xecec'ecec'ececull,
|
||||
0x0000'0000'0000ull, 0x2828'2828'2828ull, 0x5050'5050'5050ull, 0x7474'7474'7474ull, 0x9494'9494'9494ull, 0xb4b4'b4b4'b4b4ull, 0xd0d0'd0d0'd0d0ull, 0xecec'ecec'ececull,
|
||||
};
|
||||
|
||||
return Region::PAL() ? palColors[color & 0x7f] : ntscColors[color & 0x7f];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
auto TIA::read(n8 address) -> n8 {
|
||||
n8 value;
|
||||
|
||||
switch(address) {
|
||||
case 0x00: value.bit(7) = collision.M0P1; value.bit(6) = collision.M0P0; return value; // CXM0P
|
||||
case 0x01: value.bit(7) = collision.M1P0; value.bit(6) = collision.M1P1; return value; // CXM1P
|
||||
case 0x02: value.bit(7) = collision.P0PF; value.bit(6) = collision.P0BL; return value; // CX0FB
|
||||
case 0x03: value.bit(7) = collision.P1PF; value.bit(6) = collision.P1BL; return value; // CX1FB
|
||||
case 0x04: value.bit(7) = collision.M0PF; value.bit(6) = collision.M0BL; return value; // CXM0FB
|
||||
case 0x05: value.bit(7) = collision.M1PF; value.bit(6) = collision.M1BL; return value; // CXM1FB
|
||||
case 0x06: value.bit(7) = collision.BLPF; return value; // CXBLPF
|
||||
case 0x07: value.bit(7) = collision.P0P1; value.bit(6) = collision.M0M1; return value; // CXPPMM
|
||||
case 0x0c: value.bit(7) = controllerPort1.read().bit(4); return value; // INPT4
|
||||
case 0x0d: value.bit(7) = controllerPort2.read().bit(4); return value; // INPT5
|
||||
}
|
||||
|
||||
debug(unimplemented, "[TIA] read ", hex(address));
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto TIA::write(n8 address, n8 data) -> void {
|
||||
switch(address) {
|
||||
case 0x00: vsync(data.bit(1)); return; // VSYNC
|
||||
case 0x01: writeQueue.add(address, data, 1); return; // VBLANK
|
||||
case 0x02: cpu.rdyLine(0); return; // WSYNC
|
||||
case 0x03: io.hcounter = 0; return; // RSYNC
|
||||
case 0x04: player[0].size = data.bit(0,2); missile[0].size = data.bit(4, 5); return; // NUSIZ1
|
||||
case 0x05: player[1].size = data.bit(0,2); missile[1].size = data.bit(4, 5); return; // NUSIZ1
|
||||
case 0x06: io.p0Color = data.bit(1, 7); return; // COLUP0
|
||||
case 0x07: io.p1Color = data.bit(1, 7); return; // COLUP1
|
||||
case 0x08: io.fgColor = data.bit(1, 7); return; // COLUPF
|
||||
case 0x09: io.bgColor = data.bit(1, 7); return; // COLUBK
|
||||
case 0x0a: ctrlpf(data); return; // CTRLPF
|
||||
case 0x0b: writeQueue.add(address, data, 1); return; // REFP0
|
||||
case 0x0c: writeQueue.add(address, data, 1); return; // REFP1
|
||||
case 0x0d: writeQueue.add(address, data, 6); return; // PF0
|
||||
case 0x0e: writeQueue.add(address, data, 6); return; // PF1
|
||||
case 0x0f: writeQueue.add(address, data, 6); return; // PF2
|
||||
case 0x10: resp(0); return; // RESP0
|
||||
case 0x11: resp(1); return; // RESP1
|
||||
case 0x12: resm(0); return; // RESM0
|
||||
case 0x13: resm(1); return; // RESM1
|
||||
case 0x14: resbl(); return; // RESBL
|
||||
case 0x15: audio[0].control = data; return; // AUDC0
|
||||
case 0x16: audio[1].control = data; return; // AUDC1
|
||||
case 0x17: audio[0].frequency = data; return; // AUDF0
|
||||
case 0x18: audio[1].frequency = data; return; // AUDF1
|
||||
case 0x19: audio[0].volume = data; return; // AUDV0
|
||||
case 0x1a: audio[1].volume = data; return; // AUDV1
|
||||
case 0x1b: writeQueue.add(address, data, 3); return; // GRP0
|
||||
case 0x1c: writeQueue.add(address, data, 3); return; // GRP1
|
||||
case 0x1d: writeQueue.add(address, data, 1); return; // ENAM0
|
||||
case 0x1e: writeQueue.add(address, data, 1); return; // ENAM1
|
||||
case 0x1f: writeQueue.add(address, data, 1); return; // ENAB1
|
||||
case 0x20: writeQueue.add(address, data, 2); return; // HMP0
|
||||
case 0x21: writeQueue.add(address, data, 2); return; // HMP1
|
||||
case 0x22: writeQueue.add(address, data, 2); return; // HMM0
|
||||
case 0x23: writeQueue.add(address, data, 2); return; // HMM1
|
||||
case 0x24: writeQueue.add(address, data, 2); return; // HMBL
|
||||
case 0x25: player[0].delay = data.bit(0); return; // VDELP0
|
||||
case 0x26: player[1].delay = data.bit(0); return; // VDELP1
|
||||
case 0x27: ball.delay = data.bit(0); return; // VDELBL
|
||||
case 0x28: resmp(0, data); return; // RESMP0
|
||||
case 0x29: resmp(1, data); return; // RESMP1
|
||||
case 0x2a: writeQueue.add(address, data, 6); return; // HMOVE
|
||||
case 0x2b: writeQueue.add(address, data, 2); return; // HMCLR
|
||||
case 0x2c: cxclr(); return; // CXCLR
|
||||
}
|
||||
|
||||
debug(unimplemented, "[TIA] write ",hex(address), " = ", hex(data));
|
||||
}
|
||||
|
||||
|
||||
auto TIA::vsync(n1 state) -> void {
|
||||
if(io.vsync && !state) {
|
||||
io.vcounter = 0;
|
||||
frame();
|
||||
}
|
||||
|
||||
io.vsync = state;
|
||||
}
|
||||
|
||||
auto TIA::ctrlpf(n8 data) -> void {
|
||||
playfield.mirror = data.bit(0);
|
||||
playfield.scoreMode = data.bit(1);
|
||||
playfield.priority = data.bit(2);
|
||||
ball.size = data.bit(4, 5);
|
||||
}
|
||||
|
||||
auto TIA::cxclr() -> void {
|
||||
collision.M0P0 = 0;
|
||||
collision.M0P1 = 0;
|
||||
collision.M1P0 = 0;
|
||||
collision.M1P1 = 0;
|
||||
collision.P0PF = 0;
|
||||
collision.P0BL = 0;
|
||||
collision.P1PF = 0;
|
||||
collision.P1BL = 0;
|
||||
collision.M0PF = 0;
|
||||
collision.M0BL = 0;
|
||||
collision.M1PF = 0;
|
||||
collision.M1BL = 0;
|
||||
collision.BLPF = 0;
|
||||
collision.P0P1 = 0;
|
||||
collision.M0M1 = 0;
|
||||
}
|
||||
|
||||
auto TIA::hmove() -> void {
|
||||
for(auto& p : player) p.position = (p.position - p.offset + 160) % 160;
|
||||
for(auto& m : missile) m.position = (m.position - m.offset + 160) % 160;
|
||||
ball.position = (ball.position - ball.offset + 160) % 160;
|
||||
io.hmoveTriggered = io.vcounter;
|
||||
}
|
||||
|
||||
auto TIA::hmclr() -> void {
|
||||
for(auto& p : player) p.offset = 0;
|
||||
for(auto& m : missile) m.offset = 0;
|
||||
ball.offset = 0;
|
||||
}
|
||||
|
||||
auto TIA::grp(n1 index, n8 data) -> void {
|
||||
player[index].graphics[0] = data;
|
||||
player[!index].graphics[1] = player[!index].graphics[0];
|
||||
if(index) ball.enable[1] = ball.enable[0];
|
||||
}
|
||||
|
||||
auto TIA::resp(n1 index) -> void {
|
||||
player[index].position = (io.hcounter < 68 ? 3 : io.hcounter - 68 + 8) % 160;
|
||||
}
|
||||
|
||||
auto TIA::resm(n1 index) -> void {
|
||||
missile[index].position = (io.hcounter < 68 ? 2 : io.hcounter - 68 + 7) % 160;
|
||||
}
|
||||
|
||||
auto TIA::resmp(n1 index, n8 data) -> void {
|
||||
missile[index].reset = data.bit(1);
|
||||
if(missile[index].reset) {
|
||||
auto offset = 3;
|
||||
if(player[index].size == 5) offset = 6;
|
||||
if(player[index].size == 7) offset = 10;
|
||||
missile[index].position = player[index].position + offset;
|
||||
}
|
||||
}
|
||||
|
||||
auto TIA::resbl() -> void {
|
||||
ball.position = (io.hcounter < 68 ? 2 : io.hcounter - 68 + 7);
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
//serialization.cpp
|
||||
auto TIA::serialize(serializer& s) -> void {
|
||||
Thread::serialize(s);
|
||||
|
||||
for(auto n : range(writeQueue.maxItems)) {
|
||||
s(writeQueue.items[n].active);
|
||||
s(writeQueue.items[n].address);
|
||||
s(writeQueue.items[n].data);
|
||||
s(writeQueue.items[n].delay);
|
||||
}
|
||||
|
||||
s(io.vcounter);
|
||||
s(io.hcounter);
|
||||
s(io.hmoveTriggered);
|
||||
s(io.vsync);
|
||||
s(io.vblank);
|
||||
s(io.bgColor);
|
||||
s(io.p0Color);
|
||||
s(io.p1Color);
|
||||
s(io.fgColor);
|
||||
|
||||
s(playfield.graphics);
|
||||
s(playfield.mirror);
|
||||
s(playfield.scoreMode);
|
||||
s(playfield.priority);
|
||||
|
||||
for(auto n : range(2)) {
|
||||
s(player[n].graphics[0]);
|
||||
s(player[n].graphics[1]);
|
||||
s(player[n].reflect);
|
||||
s(player[n].size);
|
||||
s(player[n].offset);
|
||||
s(player[n].position);
|
||||
s(player[n].delay);
|
||||
}
|
||||
|
||||
for(auto n : range(2)) {
|
||||
s(missile[n].enable);
|
||||
s(missile[n].reset);
|
||||
s(missile[n].size);
|
||||
s(missile[n].offset);
|
||||
s(missile[n].position);
|
||||
}
|
||||
|
||||
for(auto n : range(2)) {
|
||||
s(ball.enable[0]);
|
||||
s(ball.enable[1]);
|
||||
s(ball.size);
|
||||
s(ball.offset);
|
||||
s(ball.position);
|
||||
}
|
||||
|
||||
s(collision.M0P0);
|
||||
s(collision.M0P1);
|
||||
s(collision.M1P0);
|
||||
s(collision.M1P1);
|
||||
s(collision.P0PF);
|
||||
s(collision.P0BL);
|
||||
s(collision.P1PF);
|
||||
s(collision.P1BL);
|
||||
s(collision.M0PF);
|
||||
s(collision.M0BL);
|
||||
s(collision.M1PF);
|
||||
s(collision.M1BL);
|
||||
s(collision.BLPF);
|
||||
s(collision.P0P1);
|
||||
s(collision.M0M1);
|
||||
|
||||
for(auto n : range(26)) {
|
||||
s(volume[n]);
|
||||
}
|
||||
|
||||
for(auto n : range(2)) {
|
||||
s(audio[n].enable);
|
||||
s(audio[n].divCounter);
|
||||
s(audio[n].noiseCounter);
|
||||
s(audio[n].noiseFeedback);
|
||||
s(audio[n].pulseCounter);
|
||||
s(audio[n].pulseCounterPaused);
|
||||
s(audio[n].pulseFeedback);
|
||||
s(audio[n].volume);
|
||||
s(audio[n].control);
|
||||
s(audio[n].frequency);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
#include <a26/a26.hpp>
|
||||
|
||||
namespace ares::Atari2600 {
|
||||
|
||||
TIA tia;
|
||||
|
||||
#include "audio.cpp"
|
||||
#include "color.cpp"
|
||||
#include "io.cpp"
|
||||
#include "serialization.cpp"
|
||||
#include "write-queue.cpp"
|
||||
|
||||
auto TIA::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Object>("TIA");
|
||||
|
||||
screen = node->append<Node::Video::Screen>("Screen", 160, vlines());
|
||||
screen->colors(1 << 7, {&TIA::color, this});
|
||||
screen->setSize(160, vlines());
|
||||
screen->setScale(2.0, 1.0);
|
||||
screen->setAspect(1.0, 1.0);
|
||||
screen->setViewport(0, 0, 160, vlines());
|
||||
|
||||
stream = node->append<Node::Audio::Stream>("Audio");
|
||||
stream->setChannels(1);
|
||||
stream->setFrequency(31'403);
|
||||
stream->addHighPassFilter( 20.0, 1);
|
||||
stream->addLowPassFilter (2840.0, 1);
|
||||
}
|
||||
|
||||
auto TIA::unload() -> void {
|
||||
screen->quit();
|
||||
node->remove(screen);
|
||||
screen.reset();
|
||||
node.reset();
|
||||
}
|
||||
|
||||
auto TIA::main() -> void {
|
||||
scanline();
|
||||
io.vcounter++;
|
||||
io.hmoveTriggered = 255;
|
||||
}
|
||||
|
||||
auto TIA::scanline() -> void {
|
||||
cpu.rdyLine(1);
|
||||
for(io.hcounter = 0; io.hcounter < 228; io.hcounter++) {
|
||||
auto x = io.hcounter - 68;
|
||||
if(x >= 0) pixel(x);
|
||||
writeQueue.step();
|
||||
runAudio();
|
||||
step();
|
||||
}
|
||||
}
|
||||
|
||||
auto TIA::pixel(n8 x) -> void {
|
||||
auto output = screen->pixels().data() + (io.vcounter * 160);
|
||||
|
||||
// Output only black during vblank, or for the first 8px of a scanline where hmove was triggered
|
||||
if (io.vblank || (io.hmoveTriggered == io.vcounter && x < 8)) {
|
||||
output[x] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
output[x] = io.bgColor;
|
||||
auto pf = runPlayfield(x);
|
||||
auto bl = runBall(x);
|
||||
auto p0 = runPlayer(x, 0);
|
||||
auto p1 = runPlayer(x, 1);
|
||||
auto m0 = runMissile(x, 0);
|
||||
auto m1 = runMissile(x, 1);
|
||||
|
||||
if(!playfield.priority && (pf || bl)) output[x] = io.fgColor;
|
||||
if(p1 || m1) output[x] = io.p1Color;
|
||||
if(p0 || m0) output[x] = io.p0Color;
|
||||
if(playfield.priority && (pf || bl)) output[x] = io.fgColor;
|
||||
|
||||
// Update Collision
|
||||
if(m0 && p1) collision.M0P1 = 1;
|
||||
if(m0 && p0) collision.M0P0 = 1;
|
||||
if(m1 && p0) collision.M1P0 = 1;
|
||||
if(m1 && p1) collision.M1P1 = 1;
|
||||
if(p0 && pf) collision.P0PF = 1;
|
||||
if(p0 && bl) collision.P0BL = 1;
|
||||
if(p1 && pf) collision.P1PF = 1;
|
||||
if(p1 && bl) collision.P1BL = 1;
|
||||
if(m0 && pf) collision.M0PF = 1;
|
||||
if(m0 && bl) collision.M0BL = 1;
|
||||
if(m1 && pf) collision.M1PF = 1;
|
||||
if(m1 && bl) collision.M1BL = 1;
|
||||
if(bl && pf) collision.BLPF = 1;
|
||||
if(p0 && p1) collision.P0P1 = 1;
|
||||
if(m0 && m1) collision.M0M1 = 1;
|
||||
}
|
||||
|
||||
auto TIA::runPlayfield(n8 x) -> n1 {
|
||||
auto pos = x >> 2;
|
||||
|
||||
if(!playfield.mirror || pos < 20) return playfield.graphics.bit(pos % 20);
|
||||
return playfield.graphics.bit(19 - (pos % 20));
|
||||
}
|
||||
|
||||
auto TIA::runPlayer(n8 x, n1 index) -> n1 {
|
||||
auto& player = this->player[index];
|
||||
auto position = player.position;
|
||||
|
||||
// Handle player stretch capability
|
||||
auto width = 8;
|
||||
if(player.size == 5) width = 16;
|
||||
if(player.size == 7) width = 32;
|
||||
|
||||
// Handle repeat capability
|
||||
auto repeat = 1;
|
||||
if(player.size == 1 || player.size == 2 || player.size == 4) repeat = 2;
|
||||
if(player.size == 3 || player.size == 6) repeat = 3;
|
||||
|
||||
auto spacing = 8;
|
||||
if(player.size == 2 || player.size == 6) spacing = 24;
|
||||
if(player.size == 4 ) spacing = 56;
|
||||
|
||||
for(int i = 0; i < repeat; i++) {
|
||||
if (x >= position && x < position + width) {
|
||||
auto bit = player.reflect ? (x - position) / (width / 8) : 7 - ((x - position) / (width / 8));
|
||||
return player.graphics[player.delay].bit(bit);
|
||||
}
|
||||
|
||||
position = (position + (spacing + width)) % 160;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto TIA::runMissile(n8 x, n1 index) -> n1 {
|
||||
auto& missile = this->missile[index];
|
||||
if (!missile.enable || missile.reset) return 0;
|
||||
|
||||
const int missileSizes[4] = {1, 2, 4, 8};
|
||||
return x >= missile.position && x < missile.position + missileSizes[missile.size];
|
||||
}
|
||||
|
||||
auto TIA::runBall(n8 x) -> n1 {
|
||||
if(!ball.enable[ball.delay]) return 0;
|
||||
|
||||
const int ballSizes[4] = {1, 2, 4, 8};
|
||||
return x >= ball.position && x < ball.position + ballSizes[ball.size];
|
||||
}
|
||||
|
||||
auto TIA::frame() -> void {
|
||||
screen->frame();
|
||||
scheduler.exit(Event::Frame);
|
||||
}
|
||||
|
||||
auto TIA::step(u32 clocks) -> void {
|
||||
Thread::step(clocks);
|
||||
Thread::synchronize();
|
||||
}
|
||||
|
||||
auto TIA::power(bool reset) -> void {
|
||||
Thread::create(system.frequency(), {&TIA::main, this});
|
||||
screen->power();
|
||||
io = {};
|
||||
playfield = {};
|
||||
for(auto& p : player) p = {};
|
||||
for(auto& m : missile) m = {};
|
||||
ball = {};
|
||||
collision = {};
|
||||
for(auto n : range(writeQueue.maxItems)) writeQueue.items[n] = {};
|
||||
|
||||
for(u32 level : range(15)) {
|
||||
volume[level] = pow(2, level * -2.0 / 6.0);
|
||||
}
|
||||
volume[15] = 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
struct TIA : Thread {
|
||||
Node::Object node;
|
||||
Node::Video::Screen screen;
|
||||
Node::Audio::Stream stream;
|
||||
|
||||
auto vlines() const -> u32 { return Region::NTSC() ? 262 : 312; }
|
||||
|
||||
//tia.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
|
||||
auto main() -> void;
|
||||
auto step(u32 clocks = 1) -> void;
|
||||
auto power(bool reset) -> void;
|
||||
auto frame() -> void;
|
||||
auto scanline() -> void;
|
||||
auto pixel(n8 x) -> void;
|
||||
|
||||
auto runPlayfield(n8 x) -> n1;
|
||||
auto runBall(n8 x) -> n1;
|
||||
auto runPlayer(n8 x, n1 index) -> n1;
|
||||
auto runMissile(n8 x, n1 index) -> n1;
|
||||
auto runCollision() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto read(n8 address) -> n8;
|
||||
auto write(n8 address, n8 data) -> void;
|
||||
auto vsync(n1 state) -> void;
|
||||
auto ctrlpf(n8 data) -> void;
|
||||
auto cxclr() -> void;
|
||||
auto hmove() -> void;
|
||||
auto hmclr() -> void;
|
||||
auto grp(n1 index, n8 data) -> void;
|
||||
auto resp(n1 index) -> void;
|
||||
auto resm(n1 index) -> void;
|
||||
auto resmp(n1 index, n8 data) -> void;
|
||||
auto resbl() -> void;
|
||||
|
||||
// audio.cpp
|
||||
auto runAudio() -> void;
|
||||
|
||||
//color.cpp
|
||||
auto color(n32) -> n64;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
// write-queue.cpp
|
||||
struct WriteQueue {
|
||||
inline auto add(u8 address, n8 data, i8 delay) -> void;
|
||||
inline auto step() -> void;
|
||||
|
||||
struct QueueItem {
|
||||
inline auto commit() -> void;
|
||||
n1 active;
|
||||
i8 address;
|
||||
n8 data;
|
||||
i8 delay;
|
||||
};
|
||||
|
||||
QueueItem items[16];
|
||||
const int maxItems = 16;
|
||||
} writeQueue;
|
||||
|
||||
struct {
|
||||
u9 vcounter;
|
||||
u8 hcounter;
|
||||
u8 hmoveTriggered;
|
||||
|
||||
n1 vsync;
|
||||
n1 vblank;
|
||||
|
||||
n7 bgColor;
|
||||
n7 p0Color;
|
||||
n7 p1Color;
|
||||
n7 fgColor;
|
||||
} io;
|
||||
|
||||
struct {
|
||||
n20 graphics;
|
||||
n1 mirror;
|
||||
n1 scoreMode;
|
||||
n1 priority;
|
||||
} playfield;
|
||||
|
||||
struct {
|
||||
n8 graphics[2];
|
||||
n1 reflect;
|
||||
n3 size;
|
||||
i4 offset;
|
||||
n8 position;
|
||||
n1 delay;
|
||||
} player[2];
|
||||
|
||||
struct {
|
||||
n1 enable;
|
||||
n1 reset;
|
||||
n2 size;
|
||||
i4 offset;
|
||||
n8 position;
|
||||
} missile[2];
|
||||
|
||||
struct {
|
||||
n1 enable[2];
|
||||
n2 size;
|
||||
i4 offset;
|
||||
n1 delay;
|
||||
n8 position;
|
||||
} ball;
|
||||
|
||||
struct {
|
||||
n1 M0P0;
|
||||
n1 M0P1;
|
||||
n1 M1P0;
|
||||
n1 M1P1;
|
||||
n1 P0PF;
|
||||
n1 P0BL;
|
||||
n1 P1PF;
|
||||
n1 P1BL;
|
||||
n1 M0PF;
|
||||
n1 M0BL;
|
||||
n1 M1PF;
|
||||
n1 M1BL;
|
||||
n1 BLPF;
|
||||
n1 P0P1;
|
||||
n1 M0M1;
|
||||
} collision;
|
||||
|
||||
struct AudioChannel {
|
||||
auto phase0() -> void;
|
||||
auto phase1() -> u8;
|
||||
n1 enable;
|
||||
n8 divCounter;
|
||||
n8 noiseCounter;
|
||||
n1 noiseFeedback;
|
||||
n8 pulseCounter;
|
||||
n1 pulseCounterPaused;
|
||||
n1 pulseFeedback;
|
||||
n4 volume;
|
||||
n4 control;
|
||||
n5 frequency;
|
||||
} audio[2];
|
||||
|
||||
f64 volume[16];
|
||||
};
|
||||
|
||||
extern TIA tia;
|
||||
@@ -0,0 +1,46 @@
|
||||
auto TIA::WriteQueue::add(u8 address, n8 data, i8 delay) -> void {
|
||||
for(auto n : range(maxItems)) {
|
||||
if(!items[n].active) {
|
||||
items[n].address = address;
|
||||
items[n].data = data;
|
||||
items[n].delay = delay;
|
||||
items[n].active = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto TIA::WriteQueue::step() -> void {
|
||||
auto& items = tia.writeQueue.items;
|
||||
|
||||
for(auto n : range(maxItems)) {
|
||||
if(items[n].active && --items[n].delay == 0) {
|
||||
items[n].commit();
|
||||
items[n].active = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto TIA::WriteQueue::QueueItem::commit() -> void {
|
||||
switch(address) {
|
||||
case 0x01: tia.io.vblank = data.bit(1); return; // VBLANK
|
||||
case 0x0b: tia.player[0].reflect = data.bit(3); return; // REFP0
|
||||
case 0x0c: tia.player[1].reflect = data.bit(3); return; // REFP1
|
||||
case 0x0d: tia.playfield.graphics.bit(0, 3) = data.bit(4, 7); return; // PF0
|
||||
case 0x0e: for(auto n : range(8)) tia.playfield.graphics.bit(4 + n) = data.bit(7 - n); return; // PF1
|
||||
case 0x0f: tia.playfield.graphics.bit(12, 19) = data; return; // PF2
|
||||
case 0x1b: tia.grp(0, data); return; // GRP0
|
||||
case 0x1c: tia.grp(1, data); return; // GRP1
|
||||
case 0x1d: tia.missile[0].enable = data.bit(1); return; // ENAM0
|
||||
case 0x1e: tia.missile[1].enable = data.bit(1); return; // ENAM1
|
||||
case 0x1f: tia.ball.enable[0] = data.bit(1); return; // ENABL
|
||||
case 0x20: tia.player[0].offset = data.bit(4, 7); return; // HMP0
|
||||
case 0x21: tia.player[1].offset = data.bit(4, 7); return; // HMP1
|
||||
case 0x22: tia.missile[0].offset = data.bit(4, 7); return; // HMM0
|
||||
case 0x23: tia.missile[1].offset = data.bit(4, 7); return; // HMM1
|
||||
case 0x24: tia.ball.offset = data.bit(4, 7); return; // HMBL
|
||||
case 0x2a: tia.hmove(); return; // HMOVE
|
||||
case 0x2b: tia.hmclr(); return; // HMCLR
|
||||
}
|
||||
debug(unimplemented, "[TIA] writeQueue: ",hex(address), " = ", hex(data));
|
||||
}
|
||||
+2
-2
@@ -38,7 +38,7 @@ using namespace nall;
|
||||
|
||||
namespace ares {
|
||||
static const string Name = "ares";
|
||||
static const string Version = "128";
|
||||
static const string Version = "129";
|
||||
static const string Copyright = "ares team, Near";
|
||||
static const string License = "ISC";
|
||||
static const string LicenseURI = "https://opensource.org/licenses/ISC";
|
||||
@@ -47,7 +47,7 @@ namespace ares {
|
||||
|
||||
//incremented only when serialization format changes
|
||||
static const u32 SerializerSignature = 0x31545342; //"BST1" (little-endian)
|
||||
static const string SerializerVersion = "128";
|
||||
static const string SerializerVersion = "129";
|
||||
|
||||
namespace VFS {
|
||||
using Pak = shared_pointer<vfs::directory>;
|
||||
|
||||
@@ -28,7 +28,7 @@ struct Instruction : Tracer {
|
||||
}
|
||||
|
||||
auto address(u64 address) -> bool {
|
||||
address &= (1ull << _addressBits) - 1; //mask upper bits of address
|
||||
address &= ~0ull >> (64 - _addressBits); //mask upper bits of address
|
||||
_address = address;
|
||||
address >>= _addressMask; //clip unneeded alignment bits (to reduce _masks size)
|
||||
|
||||
@@ -57,7 +57,7 @@ struct Instruction : Tracer {
|
||||
//call when writing to executable RAM to support self-modifying code.
|
||||
auto invalidate(u64 address) -> void {
|
||||
if(unlikely(_mask && updateMasks())) {
|
||||
address &= (1ull << _addressBits) - 1;
|
||||
address &= ~0ull >> (64 - _addressBits);
|
||||
address >>= _addressMask;
|
||||
_masks[address >> 3] &= ~(1 << (address & 7));
|
||||
}
|
||||
@@ -104,7 +104,7 @@ struct Instruction : Tracer {
|
||||
|
||||
protected:
|
||||
auto updateMasks() -> bool {
|
||||
auto size = 1ull << _addressBits >> _addressMask >> 3;
|
||||
auto size = 1ull << (_addressBits - _addressMask - 3);
|
||||
if(!_mask || !size) return _masks.reset(), false;
|
||||
if(_masks.size() == size) return true;
|
||||
_masks.reset();
|
||||
|
||||
@@ -84,7 +84,8 @@ inline auto Thread::synchronize() -> void {
|
||||
template<typename... P>
|
||||
inline auto Thread::synchronize(Thread& thread, P&&... p) -> void {
|
||||
//switching to another thread does not guarantee it will catch up before switching back.
|
||||
while(thread.clock() < clock()) {
|
||||
//make sure not to switch to threads that were destroyed during synchronization
|
||||
while(thread.clock() < clock() && thread.handle()) {
|
||||
//disable synchronization for auxiliary threads during scheduler synchronization.
|
||||
//synchronization can begin inside of this while loop.
|
||||
if(scheduler.synchronizing()) break;
|
||||
|
||||
@@ -67,7 +67,7 @@ auto SH2::illegalInstruction() -> void {
|
||||
push(SR);
|
||||
push(PC);
|
||||
jump(readLong(VBR + vector * 4) + 4);
|
||||
debug(unusual, "[SH2] illegal instruction: 0x", hex(busReadWord(PC - 4), 4L));
|
||||
debug(unusual, "[SH2] illegal instruction: 0x", hex(busReadWord(PC - 4), 4L), " @ 0x", hex(PC - 4));
|
||||
}
|
||||
|
||||
auto SH2::illegalSlotInstruction() -> void {
|
||||
|
||||
@@ -133,6 +133,7 @@ auto SH2::WDT::serialize(serializer& s) -> void {
|
||||
s(rstcsr.rste);
|
||||
s(rstcsr.wovf);
|
||||
s(wtcnt);
|
||||
s(pendingIRQ);
|
||||
}
|
||||
|
||||
auto SH2::UBC::serialize(serializer& s) -> void {
|
||||
|
||||
@@ -22,6 +22,13 @@ print("dreq1\n");
|
||||
}
|
||||
}
|
||||
|
||||
if(self->wdt.pendingIRQ) {
|
||||
if(self->SR.I < ipra.wdtip) {
|
||||
self->interrupt(ipra.wdtip, vcrwdt.witv);
|
||||
self->wdt.pendingIRQ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(self->sci.pendingTransmitEmptyIRQ) {
|
||||
if(self->SR.I < iprb.sciip) {
|
||||
self->interrupt(iprb.sciip, vcrb.stxv);
|
||||
|
||||
@@ -675,17 +675,26 @@ auto SH2::internalWriteByte(u32 address, n8 data) -> void {
|
||||
dmac.drcr[1] = data.bit(0,1);
|
||||
return;
|
||||
|
||||
//WTCSR: watchdog timer control/status register
|
||||
//WTCSR/WTCNT select
|
||||
case 0xffff'fe80:
|
||||
wdt.wtcsr.cks = data.bit(0,2);
|
||||
wdt.wtcsr.tme = data.bit(5);
|
||||
wdt.wtcsr.wtit = data.bit(6);
|
||||
wdt.wtcsr.ovf &= data.bit(7);
|
||||
wdt.select = data.bit(0,7);
|
||||
return;
|
||||
|
||||
//WTCNT: watchdog timer counter
|
||||
//WTCSR/WTCNT data
|
||||
case 0xffff'fe81:
|
||||
wdt.wtcnt = data.bit(0,7);
|
||||
//WTCSR: watchdog timer control/status register
|
||||
if(wdt.select == 0xa5) {
|
||||
wdt.wtcsr.cks = data.bit(0,2);
|
||||
wdt.wtcsr.tme = data.bit(5);
|
||||
wdt.wtcsr.wtit = data.bit(6);
|
||||
wdt.wtcsr.ovf &= data.bit(7);
|
||||
if(!wdt.wtcsr.tme) wdt.wtcnt = 0;
|
||||
}
|
||||
|
||||
//WTCNT: watchdog timer counter
|
||||
if(wdt.select == 0x5a) {
|
||||
wdt.wtcnt = data.bit(0,7);
|
||||
}
|
||||
return;
|
||||
|
||||
//RSTCSR: reset control/status register
|
||||
|
||||
@@ -112,8 +112,8 @@
|
||||
n7 fovv; //FRT overflow interrupt vector number
|
||||
} vcrd;
|
||||
struct VCRWDT { //vector number setting register WDT
|
||||
n4 bcmv; //BSC compare match interrupt vector number
|
||||
n4 witv; //WDT interval interrupt vector number
|
||||
n7 bcmv; //BSC compare match interrupt vector number
|
||||
n7 witv; //WDT interval interrupt vector number
|
||||
} vcrwdt;
|
||||
} intc;
|
||||
|
||||
@@ -210,6 +210,9 @@
|
||||
|
||||
//watchdog timer
|
||||
struct WDT {
|
||||
//timer.cpp
|
||||
auto run() -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
@@ -225,6 +228,11 @@
|
||||
n1 wovf; //watchdog timer overflow flag
|
||||
} rstcsr;
|
||||
n8 wtcnt; //watchdog timer counter
|
||||
|
||||
//internal
|
||||
n32 counter;
|
||||
n1 pendingIRQ;
|
||||
n8 select;
|
||||
} wdt;
|
||||
|
||||
//user break controller
|
||||
|
||||
@@ -26,3 +26,18 @@ auto SH2::FRT::run() -> void {
|
||||
}
|
||||
}
|
||||
}
|
||||
auto SH2::WDT::run() -> void {
|
||||
static constexpr u32 frequencies[8] = {2, 64, 128, 256, 512, 1024, 2096, 8192};
|
||||
|
||||
if(!wtcsr.tme) return;
|
||||
if(wtcsr.wtit) return; // Watchdog mode currently unsupported
|
||||
|
||||
if(++counter >= frequencies[wtcsr.cks]) {
|
||||
counter -= frequencies[wtcsr.cks];
|
||||
|
||||
if(++wtcnt == 0) {
|
||||
wtcsr.ovf = 1;
|
||||
pendingIRQ = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ auto PPU::DAC::run(u32 x, u32 y) -> n15 {
|
||||
n15 color = above.color;
|
||||
|
||||
//color blending
|
||||
if(active[SFX]) {
|
||||
if(active[SFX] || (above.translucent && io.blendBelow[belowLayer])) {
|
||||
if(above.translucent && io.blendBelow[belowLayer]) {
|
||||
color = blend(above.color, eva, below.color, evb);
|
||||
} else if(io.blendMode == 1 && io.blendAbove[aboveLayer] && io.blendBelow[belowLayer]) {
|
||||
|
||||
@@ -17,6 +17,10 @@ FightingPad::FightingPad(Node::Port parent) {
|
||||
Thread::create(1'000'000, {&FightingPad::main, this});
|
||||
}
|
||||
|
||||
FightingPad::~FightingPad() {
|
||||
Thread::destroy();
|
||||
}
|
||||
|
||||
auto FightingPad::main() -> void {
|
||||
if(timeout) {
|
||||
timeout--;
|
||||
|
||||
@@ -13,6 +13,7 @@ struct FightingPad : Controller, Thread {
|
||||
Node::Input::Button start;
|
||||
|
||||
FightingPad(Node::Port);
|
||||
~FightingPad();
|
||||
auto main() -> void;
|
||||
auto readData() -> n8 override;
|
||||
auto writeData(n8 data) -> void override;
|
||||
|
||||
@@ -11,6 +11,8 @@ auto ControllerPort::load(Node::Object parent) -> void {
|
||||
port->setType("Controller");
|
||||
port->setHotSwappable(true);
|
||||
port->setAllocate([&](auto name) { return allocate(name); });
|
||||
port->setDisconnect([&] { return disconnect(); });
|
||||
|
||||
port->setSupported({"Control Pad", "Fighting Pad"});
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ struct ControllerPort {
|
||||
|
||||
ControllerPort(string name);
|
||||
auto connect(Node::Peripheral) -> void;
|
||||
auto disconnect() -> void;
|
||||
auto disconnect() -> void { device.reset(); }
|
||||
|
||||
auto readControl() -> n8 { return control; }
|
||||
auto writeControl(n8 data) -> void { control = data; }
|
||||
|
||||
@@ -43,6 +43,7 @@ auto M32X::SH7604::main() -> void {
|
||||
SH2::intc.run();
|
||||
SH2::dmac.run();
|
||||
SH2::frt.run();
|
||||
SH2::wdt.run();
|
||||
}
|
||||
|
||||
auto M32X::SH7604::step(u32 clocks) -> void {
|
||||
|
||||
+4
-2
@@ -32,13 +32,15 @@ auto AI::main() -> void {
|
||||
auto AI::sample() -> void {
|
||||
if(io.dmaCount == 0) return stream->frame(0.0, 0.0);
|
||||
|
||||
io.dmaAddress[0].bit(13,23) += io.dmaAddressCarry;
|
||||
auto data = rdram.ram.read<Word>(io.dmaAddress[0]);
|
||||
auto left = s16(data >> 16);
|
||||
auto right = s16(data >> 0);
|
||||
stream->frame(left / 32768.0, right / 32768.0);
|
||||
|
||||
io.dmaAddress[0] += 4;
|
||||
io.dmaLength [0] -= 4;
|
||||
io.dmaAddress[0].bit(0,12) += 4;
|
||||
io.dmaAddressCarry = io.dmaAddress[0].bit(0,12) == 0;
|
||||
io.dmaLength[0] -= 4;
|
||||
if(!io.dmaLength[0]) {
|
||||
mi.raise(MI::IRQ::AI);
|
||||
if(--io.dmaCount) {
|
||||
|
||||
@@ -36,6 +36,7 @@ struct AI : Thread, Memory::IO<AI> {
|
||||
struct IO {
|
||||
n1 dmaEnable;
|
||||
n24 dmaAddress[2];
|
||||
n1 dmaAddressCarry;
|
||||
n18 dmaLength[2];
|
||||
n2 dmaCount;
|
||||
n14 dacRate;
|
||||
|
||||
@@ -6,6 +6,7 @@ auto AI::serialize(serializer& s) -> void {
|
||||
|
||||
s(io.dmaEnable);
|
||||
s(io.dmaAddress);
|
||||
s(io.dmaAddressCarry);
|
||||
s(io.dmaLength);
|
||||
s(io.dmaCount);
|
||||
s(io.dacRate);
|
||||
|
||||
@@ -4,5 +4,6 @@ namespace ares::Nintendo64 {
|
||||
|
||||
#include "port.cpp"
|
||||
#include "gamepad/gamepad.cpp"
|
||||
#include "mouse/mouse.cpp"
|
||||
|
||||
}
|
||||
@@ -3,9 +3,11 @@ struct Controller {
|
||||
|
||||
virtual ~Controller() = default;
|
||||
virtual auto save() -> void {}
|
||||
virtual auto comm(n8 send, n8 recv, n8 input[], n8 output[]) -> n2 { return 1; }
|
||||
virtual auto read() -> n32 { return 0; }
|
||||
virtual auto serialize(serializer&) -> void {}
|
||||
};
|
||||
|
||||
#include "port.hpp"
|
||||
#include "gamepad/gamepad.hpp"
|
||||
#include "mouse/mouse.hpp"
|
||||
@@ -83,6 +83,94 @@ auto Gamepad::rumble(bool enable) -> void {
|
||||
platform->input(motor);
|
||||
}
|
||||
|
||||
auto Gamepad::comm(n8 send, n8 recv, n8 input[], n8 output[]) -> n2 {
|
||||
b1 valid = 0;
|
||||
b1 over = 0;
|
||||
|
||||
//status
|
||||
if(input[0] == 0x00 || input[0] == 0xff) {
|
||||
output[0] = 0x05; //0x05 = gamepad; 0x02 = mouse
|
||||
output[1] = 0x00;
|
||||
output[2] = 0x02; //0x02 = nothing present in controller slot
|
||||
if(ram || motor) {
|
||||
output[2] = 0x01; //0x01 = pak present
|
||||
}
|
||||
valid = 1;
|
||||
}
|
||||
|
||||
//read controller state
|
||||
if(input[0] == 0x01) {
|
||||
u32 data = read();
|
||||
output[0] = data >> 24;
|
||||
output[1] = data >> 16;
|
||||
output[2] = data >> 8;
|
||||
output[3] = data >> 0;
|
||||
if(recv <= 4) {
|
||||
over = 0;
|
||||
} else {
|
||||
over = 1;
|
||||
}
|
||||
valid = 1;
|
||||
}
|
||||
|
||||
//read pak
|
||||
if(input[0] == 0x02 && send >= 3 && recv >= 1) {
|
||||
//controller pak
|
||||
if(ram) {
|
||||
u32 address = (input[1] << 8 | input[2] << 0) & ~31;
|
||||
if(pif.addressCRC(address) == (n5)input[2]) {
|
||||
for(u32 index : range(recv - 1)) {
|
||||
output[index] = ram.read<Byte>(address++);
|
||||
}
|
||||
output[recv - 1] = pif.dataCRC({&output[0], recv - 1});
|
||||
valid = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//rumble pak
|
||||
if(motor) {
|
||||
u32 address = (input[1] << 8 | input[2] << 0) & ~31;
|
||||
if(pif.addressCRC(address) == (n5)input[2]) {
|
||||
for(u32 index : range(recv - 1)) {
|
||||
output[index] = 0x80;
|
||||
}
|
||||
output[recv - 1] = pif.dataCRC({&output[0], recv - 1});
|
||||
valid = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//write pak
|
||||
if(input[0] == 0x03 && send >= 3 && recv >= 1) {
|
||||
//controller pak
|
||||
if(ram) {
|
||||
u32 address = (input[1] << 8 | input[2] << 0) & ~31;
|
||||
if(pif.addressCRC(address) == (n5)input[2]) {
|
||||
for(u32 index : range(send - 3)) {
|
||||
ram.write<Byte>(address++, input[3 + index]);
|
||||
}
|
||||
output[0] = pif.dataCRC({&input[3], send - 3});
|
||||
valid = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//rumble pak
|
||||
if(motor) {
|
||||
u32 address = (input[1] << 8 | input[2] << 0) & ~31;
|
||||
if(pif.addressCRC(address) == (n5)input[2]) {
|
||||
output[0] = pif.dataCRC({&input[3], send - 3});
|
||||
valid = 1;
|
||||
rumble(input[3] & 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
n2 status = 0;
|
||||
status.bit(0) = valid;
|
||||
status.bit(1) = over;
|
||||
return status;
|
||||
}
|
||||
|
||||
auto Gamepad::read() -> n32 {
|
||||
platform->input(x);
|
||||
platform->input(y);
|
||||
|
||||
@@ -29,6 +29,7 @@ struct Gamepad : Controller {
|
||||
auto connect() -> void;
|
||||
auto disconnect() -> void;
|
||||
auto rumble(bool enable) -> void;
|
||||
auto comm(n8 send, n8 recv, n8 input[], n8 output[]) -> n2 override;
|
||||
auto read() -> n32 override;
|
||||
auto formatControllerPak() -> void;
|
||||
auto serialize(serializer&) -> void override;
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
Mouse::Mouse(Node::Port parent) {
|
||||
node = parent->append<Node::Peripheral>("Mouse");
|
||||
|
||||
x = node->append<Node::Input::Axis> ("X");
|
||||
y = node->append<Node::Input::Axis> ("Y");
|
||||
left = node->append<Node::Input::Button>("Left");
|
||||
right = node->append<Node::Input::Button>("Right");
|
||||
}
|
||||
|
||||
Mouse::~Mouse() {
|
||||
}
|
||||
|
||||
auto Mouse::comm(n8 send, n8 recv, n8 input[], n8 output[]) -> n2 {
|
||||
b1 valid = 0;
|
||||
b1 over = 0;
|
||||
|
||||
//status
|
||||
if(input[0] == 0x00 || input[0] == 0xff) {
|
||||
output[0] = 0x02; //0x05 = gamepad; 0x02 = mouse
|
||||
output[1] = 0x00;
|
||||
output[2] = 0x02; //0x02 = nothing present in controller slot
|
||||
valid = 1;
|
||||
}
|
||||
|
||||
//read controller state
|
||||
if(input[0] == 0x01) {
|
||||
u32 data = read();
|
||||
output[0] = data >> 24;
|
||||
output[1] = data >> 16;
|
||||
output[2] = data >> 8;
|
||||
output[3] = data >> 0;
|
||||
if(recv <= 4) {
|
||||
over = 0;
|
||||
} else {
|
||||
over = 1;
|
||||
}
|
||||
valid = 1;
|
||||
}
|
||||
|
||||
n2 status = 0;
|
||||
status.bit(0) = valid;
|
||||
status.bit(1) = over;
|
||||
return status;
|
||||
}
|
||||
|
||||
auto Mouse::read() -> n32 {
|
||||
platform->input(x);
|
||||
platform->input(y);
|
||||
platform->input(left);
|
||||
platform->input(right);
|
||||
|
||||
//TODO: Deal with more reasonable values for Mouse axis
|
||||
auto ax = x->value();
|
||||
auto ay = -y->value();
|
||||
|
||||
n32 data;
|
||||
data.byte(0) = ay;
|
||||
data.byte(1) = ax;
|
||||
data.bit(30) = right->value();
|
||||
data.bit(31) = left->value();
|
||||
|
||||
return data;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
struct Mouse : Controller {
|
||||
Node::Input::Axis x;
|
||||
Node::Input::Axis y;
|
||||
Node::Input::Button left;
|
||||
Node::Input::Button right;
|
||||
|
||||
Mouse(Node::Port);
|
||||
~Mouse();
|
||||
|
||||
auto comm(n8 send, n8 recv, n8 input[], n8 output[]) -> n2 override;
|
||||
auto read() -> n32 override;
|
||||
};
|
||||
@@ -12,7 +12,7 @@ auto ControllerPort::load(Node::Object parent) -> void {
|
||||
port->setType("Controller");
|
||||
port->setHotSwappable(true);
|
||||
port->setAllocate([&](auto name) { return allocate(name); });
|
||||
port->setSupported({"Gamepad"});
|
||||
port->setSupported({"Gamepad", "Mouse"});
|
||||
}
|
||||
|
||||
auto ControllerPort::unload() -> void {
|
||||
@@ -26,6 +26,7 @@ auto ControllerPort::save() -> void {
|
||||
|
||||
auto ControllerPort::allocate(string name) -> Node::Peripheral {
|
||||
if(name == "Gamepad") device = new Gamepad(port);
|
||||
if(name == "Mouse" ) device = new Mouse(port);
|
||||
if(device) return device->node;
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -18,7 +18,8 @@ auto CPU::Context::setMode() -> void {
|
||||
break;
|
||||
}
|
||||
|
||||
if(bits == 32 || bits == 64) {
|
||||
if(bits == 32) {
|
||||
physMask = 0x1fff'ffff;
|
||||
segment[0] = Segment::Mapped;
|
||||
segment[1] = Segment::Mapped;
|
||||
segment[2] = Segment::Mapped;
|
||||
@@ -47,6 +48,7 @@ auto CPU::Context::setMode() -> void {
|
||||
}
|
||||
|
||||
if(bits == 64) {
|
||||
physMask = 0x7fff'ffff;
|
||||
for(auto n : range(8))
|
||||
switch(mode) {
|
||||
case Mode::Kernel:
|
||||
|
||||
@@ -52,9 +52,9 @@ auto CPU::synchronize() -> void {
|
||||
|
||||
queue.step(clocks, [](u32 event) {
|
||||
switch(event) {
|
||||
case Queue::RSP_DMA: return rsp.dmaTransfer();
|
||||
case Queue::PI_DMA_Read: return pi.dmaRead();
|
||||
case Queue::PI_DMA_Write: return pi.dmaWrite();
|
||||
case Queue::RSP_DMA: return rsp.dmaTransferStep();
|
||||
case Queue::PI_DMA_Read: return pi.dmaFinished();
|
||||
case Queue::PI_DMA_Write: return pi.dmaFinished();
|
||||
case Queue::PI_BUS_Write: return pi.writeFinished();
|
||||
case Queue::SI_DMA_Read: return si.dmaRead();
|
||||
case Queue::SI_DMA_Write: return si.dmaWrite();
|
||||
|
||||
+18
-15
@@ -95,6 +95,7 @@ struct CPU : Thread {
|
||||
auto setMode() -> void;
|
||||
|
||||
bool endian;
|
||||
u64 physMask;
|
||||
u32 mode;
|
||||
u32 bits;
|
||||
u32 segment[8]; //512_MiB chunks
|
||||
@@ -167,9 +168,8 @@ struct CPU : Thread {
|
||||
};
|
||||
|
||||
//tlb.cpp
|
||||
auto load(u32 address) -> Match;
|
||||
auto store(u32 address) -> Match;
|
||||
auto exception(u32 address) -> void;
|
||||
auto load(u64 vaddr) -> Match;
|
||||
auto store(u64 vaddr) -> Match;
|
||||
|
||||
struct Entry {
|
||||
//scc-tlb.cpp
|
||||
@@ -195,20 +195,20 @@ struct CPU : Thread {
|
||||
} tlb{*this};
|
||||
|
||||
//memory.cpp
|
||||
auto kernelSegment32(u32 address) const -> Context::Segment;
|
||||
auto supervisorSegment32(u32 address) const -> Context::Segment;
|
||||
auto userSegment32(u32 address) const -> Context::Segment;
|
||||
auto kernelSegment32(u32 vaddr) const -> Context::Segment;
|
||||
auto supervisorSegment32(u32 vaddr) const -> Context::Segment;
|
||||
auto userSegment32(u32 vaddr) const -> Context::Segment;
|
||||
|
||||
auto kernelSegment64(u64 address) const -> Context::Segment;
|
||||
auto supervisorSegment64(u64 address) const -> Context::Segment;
|
||||
auto userSegment64(u64 address) const -> Context::Segment;
|
||||
auto kernelSegment64(u64 vaddr) const -> Context::Segment;
|
||||
auto supervisorSegment64(u64 vaddr) const -> Context::Segment;
|
||||
auto userSegment64(u64 vaddr) const -> Context::Segment;
|
||||
|
||||
auto segment(u64 address) -> Context::Segment;
|
||||
auto devirtualize(u64 address) -> maybe<u64>;
|
||||
auto fetch(u64 address) -> u32;
|
||||
template<u32 Size> auto read(u64 address) -> maybe<u64>;
|
||||
template<u32 Size> auto write(u64 address, u64 data) -> bool;
|
||||
auto addressException(u64 address) -> void;
|
||||
auto segment(u64 vaddr) -> Context::Segment;
|
||||
auto devirtualize(u64 vaddr) -> maybe<u64>;
|
||||
auto fetch(u64 vaddr) -> u32;
|
||||
template<u32 Size> auto read(u64 vaddr) -> maybe<u64>;
|
||||
template<u32 Size> auto write(u64 vaddr, u64 data) -> bool;
|
||||
auto addressException(u64 vaddr) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
@@ -522,6 +522,9 @@ struct CPU : Thread {
|
||||
|
||||
//30: Error Exception Program Counter
|
||||
n64 epcError;
|
||||
|
||||
//other
|
||||
n64 latch;
|
||||
} scc;
|
||||
|
||||
//interpreter-scc.cpp
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
auto CPU::DataCache::Line::hit(u32 address) const -> bool {
|
||||
return valid && tag == (address & ~0xe000'0fff);
|
||||
return valid && tag == (address & ~0x0000'0fff);
|
||||
}
|
||||
|
||||
template<u32 Size> auto CPU::DataCache::Line::fill(u32 address, u64 data) -> void {
|
||||
cpu.step(40);
|
||||
valid = 1;
|
||||
dirty = 1;
|
||||
tag = address & ~0xe000'0fff;
|
||||
tag = address & ~0x0000'0fff;
|
||||
//read words according to critical doubleword first scheme
|
||||
switch(address & 8) {
|
||||
case 0:
|
||||
@@ -34,7 +34,7 @@ auto CPU::DataCache::Line::fill(u32 address) -> void {
|
||||
cpu.step(40);
|
||||
valid = 1;
|
||||
dirty = 0;
|
||||
tag = address & ~0xe000'0fff;
|
||||
tag = address & ~0x0000'0fff;
|
||||
//read words according to critical doubleword first scheme
|
||||
switch(address & 8) {
|
||||
case 0:
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
auto CPU::InstructionCache::Line::hit(u32 address) const -> bool {
|
||||
return valid && tag == (address & ~0xe000'0fff);
|
||||
return valid && tag == (address & ~0x0000'0fff);
|
||||
}
|
||||
|
||||
auto CPU::InstructionCache::Line::fill(u32 address) -> void {
|
||||
cpu.step(48);
|
||||
valid = 1;
|
||||
tag = address & ~0xe000'0fff;
|
||||
tag = address & ~0x0000'0fff;
|
||||
words[0] = bus.read<Word>(tag | index | 0x00);
|
||||
words[1] = bus.read<Word>(tag | index | 0x04);
|
||||
words[2] = bus.read<Word>(tag | index | 0x08);
|
||||
@@ -42,7 +42,7 @@ auto CPU::InstructionCache::step(u32 address) -> void {
|
||||
if(!line.hit(address)) {
|
||||
cpu.step(48);
|
||||
line.valid = 1;
|
||||
line.tag = address & ~0xe000'0fff;
|
||||
line.tag = address & ~0x0000'0fff;
|
||||
} else {
|
||||
cpu.step(2);
|
||||
}
|
||||
|
||||
+193
-188
@@ -119,8 +119,8 @@ auto CPU::BREAK() -> void {
|
||||
}
|
||||
|
||||
auto CPU::CACHE(u8 operation, cr64& rs, s16 imm) -> void {
|
||||
u32 address = rs.u64 + imm;
|
||||
if (auto phys = devirtualize(address)) address = *phys;
|
||||
u32 address;
|
||||
if (auto phys = devirtualize(rs.u64 + imm)) address = *phys;
|
||||
else return;
|
||||
|
||||
switch(operation) {
|
||||
@@ -394,88 +394,88 @@ auto CPU::LD(r64& rt, cr64& rs, s16 imm) -> void {
|
||||
|
||||
auto CPU::LDL(r64& rt, cr64& rs, s16 imm) -> void {
|
||||
if(!context.kernelMode() && context.bits == 32) return exception.reservedInstruction();
|
||||
u64 address = rs.u64 + imm;
|
||||
u64 vaddr = rs.u64 + imm;
|
||||
u64 data = rt.u64;
|
||||
|
||||
if(context.littleEndian())
|
||||
switch(address & 7) {
|
||||
switch(vaddr & 7) {
|
||||
case 0:
|
||||
data &= 0x00ffffffffffffffull;
|
||||
if(auto byte = read<Byte>(address & ~7 | 7)) data |= byte() << 56; else return;
|
||||
if(auto byte = read<Byte>(vaddr & ~7 | 7)) data |= byte() << 56; else return;
|
||||
break;
|
||||
case 1:
|
||||
data &= 0x0000ffffffffffffull;
|
||||
if(auto half = read<Half>(address & ~7 | 6)) data |= half() << 48; else return;
|
||||
if(auto half = read<Half>(vaddr & ~7 | 6)) data |= half() << 48; else return;
|
||||
break;
|
||||
case 2:
|
||||
data &= 0x000000ffffffffffull;
|
||||
if(auto byte = read<Byte>(address & ~7 | 5)) data |= byte() << 56; else return;
|
||||
if(auto half = read<Half>(address & ~7 | 6)) data |= half() << 40; else return;
|
||||
if(auto byte = read<Byte>(vaddr & ~7 | 5)) data |= byte() << 56; else return;
|
||||
if(auto half = read<Half>(vaddr & ~7 | 6)) data |= half() << 40; else return;
|
||||
break;
|
||||
case 3:
|
||||
data &= 0x00000000ffffffffull;
|
||||
if(auto word = read<Word>(address & ~7 | 4)) data |= word() << 32; else return;
|
||||
if(auto word = read<Word>(vaddr & ~7 | 4)) data |= word() << 32; else return;
|
||||
break;
|
||||
case 4:
|
||||
data &= 0x0000000000ffffffull;
|
||||
if(auto byte = read<Byte>(address & ~7 | 3)) data |= byte() << 56; else return;
|
||||
if(auto word = read<Word>(address & ~7 | 4)) data |= word() << 24; else return;
|
||||
if(auto byte = read<Byte>(vaddr & ~7 | 3)) data |= byte() << 56; else return;
|
||||
if(auto word = read<Word>(vaddr & ~7 | 4)) data |= word() << 24; else return;
|
||||
break;
|
||||
case 5:
|
||||
data &= 0x000000000000ffffull;
|
||||
if(auto half = read<Half>(address & ~7 | 2)) data |= half() << 48; else return;
|
||||
if(auto word = read<Word>(address & ~7 | 4)) data |= word() << 16; else return;
|
||||
if(auto half = read<Half>(vaddr & ~7 | 2)) data |= half() << 48; else return;
|
||||
if(auto word = read<Word>(vaddr & ~7 | 4)) data |= word() << 16; else return;
|
||||
break;
|
||||
case 6:
|
||||
data &= 0x00000000000000ffull;
|
||||
if(auto byte = read<Byte>(address & ~7 | 1)) data |= byte() << 56; else return;
|
||||
if(auto half = read<Half>(address & ~7 | 2)) data |= half() << 40; else return;
|
||||
if(auto word = read<Word>(address & ~7 | 4)) data |= word() << 8; else return;
|
||||
if(auto byte = read<Byte>(vaddr & ~7 | 1)) data |= byte() << 56; else return;
|
||||
if(auto half = read<Half>(vaddr & ~7 | 2)) data |= half() << 40; else return;
|
||||
if(auto word = read<Word>(vaddr & ~7 | 4)) data |= word() << 8; else return;
|
||||
break;
|
||||
case 7:
|
||||
data &= 0x0000000000000000ull;
|
||||
if(auto dual = read<Dual>(address & ~7 | 0)) data |= dual() << 0; else return;
|
||||
if(auto dual = read<Dual>(vaddr & ~7 | 0)) data |= dual() << 0; else return;
|
||||
break;
|
||||
}
|
||||
|
||||
if(context.bigEndian())
|
||||
switch(address & 7) {
|
||||
switch(vaddr & 7) {
|
||||
case 0:
|
||||
data &= 0x0000000000000000ull;
|
||||
if(auto dual = read<Dual>(address & ~7 | 0)) data |= dual() << 0; else return;
|
||||
if(auto dual = read<Dual>(vaddr & ~7 | 0)) data |= dual() << 0; else return;
|
||||
break;
|
||||
case 1:
|
||||
data &= 0x00000000000000ffull;
|
||||
if(auto byte = read<Byte>(address & ~7 | 1)) data |= byte() << 56; else return;
|
||||
if(auto half = read<Half>(address & ~7 | 2)) data |= half() << 40; else return;
|
||||
if(auto word = read<Word>(address & ~7 | 4)) data |= word() << 8; else return;
|
||||
if(auto byte = read<Byte>(vaddr & ~7 | 1)) data |= byte() << 56; else return;
|
||||
if(auto half = read<Half>(vaddr & ~7 | 2)) data |= half() << 40; else return;
|
||||
if(auto word = read<Word>(vaddr & ~7 | 4)) data |= word() << 8; else return;
|
||||
break;
|
||||
case 2:
|
||||
data &= 0x000000000000ffffull;
|
||||
if(auto half = read<Half>(address & ~7 | 2)) data |= half() << 48; else return;
|
||||
if(auto word = read<Word>(address & ~7 | 4)) data |= word() << 16; else return;
|
||||
if(auto half = read<Half>(vaddr & ~7 | 2)) data |= half() << 48; else return;
|
||||
if(auto word = read<Word>(vaddr & ~7 | 4)) data |= word() << 16; else return;
|
||||
break;
|
||||
case 3:
|
||||
data &= 0x0000000000ffffffull;
|
||||
if(auto byte = read<Byte>(address & ~7 | 3)) data |= byte() << 56; else return;
|
||||
if(auto word = read<Word>(address & ~7 | 4)) data |= word() << 24; else return;
|
||||
if(auto byte = read<Byte>(vaddr & ~7 | 3)) data |= byte() << 56; else return;
|
||||
if(auto word = read<Word>(vaddr & ~7 | 4)) data |= word() << 24; else return;
|
||||
break;
|
||||
case 4:
|
||||
data &= 0x00000000ffffffffull;
|
||||
if(auto word = read<Word>(address & ~7 | 4)) data |= word() << 32; else return;
|
||||
if(auto word = read<Word>(vaddr & ~7 | 4)) data |= word() << 32; else return;
|
||||
break;
|
||||
case 5:
|
||||
data &= 0x000000ffffffffffull;
|
||||
if(auto byte = read<Byte>(address & ~7 | 5)) data |= byte() << 56; else return;
|
||||
if(auto half = read<Half>(address & ~7 | 6)) data |= half() << 40; else return;
|
||||
if(auto byte = read<Byte>(vaddr & ~7 | 5)) data |= byte() << 56; else return;
|
||||
if(auto half = read<Half>(vaddr & ~7 | 6)) data |= half() << 40; else return;
|
||||
break;
|
||||
case 6:
|
||||
data &= 0x0000ffffffffffffull;
|
||||
if(auto half = read<Half>(address & ~7 | 6)) data |= half() << 48; else return;
|
||||
if(auto half = read<Half>(vaddr & ~7 | 6)) data |= half() << 48; else return;
|
||||
break;
|
||||
case 7:
|
||||
data &= 0x00ffffffffffffffull;
|
||||
if(auto byte = read<Byte>(address & ~7 | 7)) data |= byte() << 56; else return;
|
||||
if(auto byte = read<Byte>(vaddr & ~7 | 7)) data |= byte() << 56; else return;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -484,88 +484,88 @@ auto CPU::LDL(r64& rt, cr64& rs, s16 imm) -> void {
|
||||
|
||||
auto CPU::LDR(r64& rt, cr64& rs, s16 imm) -> void {
|
||||
if(!context.kernelMode() && context.bits == 32) return exception.reservedInstruction();
|
||||
u64 address = rs.u64 + imm;
|
||||
u64 vaddr = rs.u64 + imm;
|
||||
u64 data = rt.u64;
|
||||
|
||||
if(context.littleEndian())
|
||||
switch(address & 7) {
|
||||
switch(vaddr & 7) {
|
||||
case 0:
|
||||
data &= 0x0000000000000000ull;
|
||||
if(auto dual = read<Dual>(address & ~7 | 0)) data |= dual() << 0; else return;
|
||||
if(auto dual = read<Dual>(vaddr & ~7 | 0)) data |= dual() << 0; else return;
|
||||
break;
|
||||
case 1:
|
||||
data &= 0xff00000000000000ull;
|
||||
if(auto word = read<Word>(address & ~7 | 0)) data |= word() << 24; else return;
|
||||
if(auto half = read<Half>(address & ~7 | 4)) data |= half() << 8; else return;
|
||||
if(auto byte = read<Byte>(address & ~7 | 6)) data |= byte() << 0; else return;
|
||||
if(auto word = read<Word>(vaddr & ~7 | 0)) data |= word() << 24; else return;
|
||||
if(auto half = read<Half>(vaddr & ~7 | 4)) data |= half() << 8; else return;
|
||||
if(auto byte = read<Byte>(vaddr & ~7 | 6)) data |= byte() << 0; else return;
|
||||
break;
|
||||
case 2:
|
||||
data &= 0xffff000000000000ull;
|
||||
if(auto word = read<Word>(address & ~7 | 0)) data |= word() << 16; else return;
|
||||
if(auto half = read<Half>(address & ~7 | 4)) data |= half() << 0; else return;
|
||||
if(auto word = read<Word>(vaddr & ~7 | 0)) data |= word() << 16; else return;
|
||||
if(auto half = read<Half>(vaddr & ~7 | 4)) data |= half() << 0; else return;
|
||||
break;
|
||||
case 3:
|
||||
data &= 0xffffff0000000000ull;
|
||||
if(auto word = read<Word>(address & ~7 | 0)) data |= word() << 8; else return;
|
||||
if(auto byte = read<Byte>(address & ~7 | 4)) data |= byte() << 0; else return;
|
||||
if(auto word = read<Word>(vaddr & ~7 | 0)) data |= word() << 8; else return;
|
||||
if(auto byte = read<Byte>(vaddr & ~7 | 4)) data |= byte() << 0; else return;
|
||||
break;
|
||||
case 4:
|
||||
data &= 0xffffffff00000000ull;
|
||||
if(auto word = read<Word>(address & ~7 | 0)) data |= word() << 0; else return;
|
||||
if(auto word = read<Word>(vaddr & ~7 | 0)) data |= word() << 0; else return;
|
||||
break;
|
||||
case 5:
|
||||
data &= 0xffffffffff000000ull;
|
||||
if(auto half = read<Half>(address & ~7 | 0)) data |= half() << 8; else return;
|
||||
if(auto byte = read<Byte>(address & ~7 | 2)) data |= byte() << 0; else return;
|
||||
if(auto half = read<Half>(vaddr & ~7 | 0)) data |= half() << 8; else return;
|
||||
if(auto byte = read<Byte>(vaddr & ~7 | 2)) data |= byte() << 0; else return;
|
||||
break;
|
||||
case 6:
|
||||
data &= 0xffffffffffff0000ull;
|
||||
if(auto half = read<Half>(address & ~7 | 0)) data |= half() << 0; else return;
|
||||
if(auto half = read<Half>(vaddr & ~7 | 0)) data |= half() << 0; else return;
|
||||
break;
|
||||
case 7:
|
||||
data &= 0xffffffffffffff00ull;
|
||||
if(auto byte = read<Byte>(address & ~7 | 0)) data |= byte() << 0; else return;
|
||||
if(auto byte = read<Byte>(vaddr & ~7 | 0)) data |= byte() << 0; else return;
|
||||
break;
|
||||
}
|
||||
|
||||
if(context.bigEndian())
|
||||
switch(address & 7) {
|
||||
switch(vaddr & 7) {
|
||||
case 0:
|
||||
data &= 0xffffffffffffff00ull;
|
||||
if(auto byte = read<Byte>(address & ~7 | 0)) data |= byte() << 0; else return;
|
||||
if(auto byte = read<Byte>(vaddr & ~7 | 0)) data |= byte() << 0; else return;
|
||||
break;
|
||||
case 1:
|
||||
data &= 0xffffffffffff0000ull;
|
||||
if(auto half = read<Half>(address & ~7 | 0)) data |= half() << 0; else return;
|
||||
if(auto half = read<Half>(vaddr & ~7 | 0)) data |= half() << 0; else return;
|
||||
break;
|
||||
case 2:
|
||||
data &= 0xffffffffff000000ull;
|
||||
if(auto half = read<Half>(address & ~7 | 0)) data |= half() << 8; else return;
|
||||
if(auto byte = read<Byte>(address & ~7 | 2)) data |= byte() << 0; else return;
|
||||
if(auto half = read<Half>(vaddr & ~7 | 0)) data |= half() << 8; else return;
|
||||
if(auto byte = read<Byte>(vaddr & ~7 | 2)) data |= byte() << 0; else return;
|
||||
break;
|
||||
case 3:
|
||||
data &= 0xffffffff00000000ull;
|
||||
if(auto word = read<Word>(address & ~7 | 0)) data |= word() << 0; else return;
|
||||
if(auto word = read<Word>(vaddr & ~7 | 0)) data |= word() << 0; else return;
|
||||
break;
|
||||
case 4:
|
||||
data &= 0xffffff0000000000ull;
|
||||
if(auto word = read<Word>(address & ~7 | 0)) data |= word() << 8; else return;
|
||||
if(auto byte = read<Byte>(address & ~7 | 4)) data |= byte() << 0; else return;
|
||||
if(auto word = read<Word>(vaddr & ~7 | 0)) data |= word() << 8; else return;
|
||||
if(auto byte = read<Byte>(vaddr & ~7 | 4)) data |= byte() << 0; else return;
|
||||
break;
|
||||
case 5:
|
||||
data &= 0xffff000000000000ull;
|
||||
if(auto word = read<Word>(address & ~7 | 0)) data |= word() << 16; else return;
|
||||
if(auto half = read<Half>(address & ~7 | 4)) data |= half() << 0; else return;
|
||||
if(auto word = read<Word>(vaddr & ~7 | 0)) data |= word() << 16; else return;
|
||||
if(auto half = read<Half>(vaddr & ~7 | 4)) data |= half() << 0; else return;
|
||||
break;
|
||||
case 6:
|
||||
data &= 0xff00000000000000ull;
|
||||
if(auto word = read<Word>(address & ~7 | 0)) data |= word() << 24; else return;
|
||||
if(auto half = read<Half>(address & ~7 | 4)) data |= half() << 8; else return;
|
||||
if(auto byte = read<Byte>(address & ~7 | 6)) data |= byte() << 0; else return;
|
||||
if(auto word = read<Word>(vaddr & ~7 | 0)) data |= word() << 24; else return;
|
||||
if(auto half = read<Half>(vaddr & ~7 | 4)) data |= half() << 8; else return;
|
||||
if(auto byte = read<Byte>(vaddr & ~7 | 6)) data |= byte() << 0; else return;
|
||||
break;
|
||||
case 7:
|
||||
data &= 0x0000000000000000ull;
|
||||
if(auto dual = read<Dual>(address & ~7 | 0)) data |= dual() << 0; else return;
|
||||
if(auto dual = read<Dual>(vaddr & ~7 | 0)) data |= dual() << 0; else return;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -582,9 +582,9 @@ auto CPU::LHU(r64& rt, cr64& rs, s16 imm) -> void {
|
||||
|
||||
auto CPU::LL(r64& rt, cr64& rs, s16 imm) -> void {
|
||||
if(auto address = devirtualize(rs.u64 + imm)) {
|
||||
if (auto data = read<Word>(*address)) {
|
||||
if (auto data = read<Word>(rs.u64 + imm)) {
|
||||
rt.u64 = s32(*data);
|
||||
scc.ll = (*address & 0x1fff'ffff) >> 4;
|
||||
scc.ll = *address >> 4;
|
||||
scc.llbit = 1;
|
||||
}
|
||||
}
|
||||
@@ -593,9 +593,9 @@ auto CPU::LL(r64& rt, cr64& rs, s16 imm) -> void {
|
||||
auto CPU::LLD(r64& rt, cr64& rs, s16 imm) -> void {
|
||||
if(!context.kernelMode() && context.bits == 32) return exception.reservedInstruction();
|
||||
if(auto address = devirtualize(rs.u64 + imm)) {
|
||||
if (auto data = read<Dual>(*address)) {
|
||||
if (auto data = read<Dual>(rs.u64 + imm)) {
|
||||
rt.u64 = *data;
|
||||
scc.ll = (*address & 0x1fff'ffff) >> 4;
|
||||
scc.ll = *address >> 4;
|
||||
scc.llbit = 1;
|
||||
}
|
||||
}
|
||||
@@ -610,110 +610,119 @@ auto CPU::LW(r64& rt, cr64& rs, s16 imm) -> void {
|
||||
}
|
||||
|
||||
auto CPU::LWL(r64& rt, cr64& rs, s16 imm) -> void {
|
||||
u64 address = rs.u64 + imm;
|
||||
u64 vaddr = rs.u64 + imm;
|
||||
u32 data = rt.u32;
|
||||
auto mem = read<Word>(vaddr & ~3);
|
||||
if (!mem) return;
|
||||
|
||||
if(context.littleEndian())
|
||||
switch(address & 3) {
|
||||
switch(vaddr & 3) {
|
||||
case 0:
|
||||
data &= 0x00ffffff;
|
||||
if(auto byte = read<Byte>(address & ~3 | 3)) data |= byte() << 24; else return;
|
||||
*mem <<= 24;
|
||||
break;
|
||||
case 1:
|
||||
data &= 0x0000ffff;
|
||||
if(auto half = read<Half>(address & ~3 | 2)) data |= half() << 16; else return;
|
||||
*mem <<= 16;
|
||||
break;
|
||||
case 2:
|
||||
data &= 0x000000ff;
|
||||
if(auto byte = read<Byte>(address & ~3 | 1)) data |= byte() << 24; else return;
|
||||
if(auto half = read<Half>(address & ~3 | 2)) data |= half() << 8; else return;
|
||||
*mem <<= 8;
|
||||
break;
|
||||
case 3:
|
||||
data &= 0x00000000;
|
||||
if(auto word = read<Word>(address & ~3 | 0)) data |= word() << 0; else return;
|
||||
*mem <<= 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if(context.bigEndian())
|
||||
switch(address & 3) {
|
||||
switch(vaddr & 3) {
|
||||
case 0:
|
||||
data &= 0x00000000;
|
||||
if(auto word = read<Word>(address & ~3 | 0)) data |= word() << 0; else return;
|
||||
*mem <<= 0;
|
||||
break;
|
||||
case 1:
|
||||
data &= 0x000000ff;
|
||||
if(auto byte = read<Byte>(address & ~3 | 1)) data |= byte() << 24; else return;
|
||||
if(auto half = read<Half>(address & ~3 | 2)) data |= half() << 8; else return;
|
||||
*mem <<= 8;
|
||||
break;
|
||||
case 2:
|
||||
data &= 0x0000ffff;
|
||||
if(auto half = read<Half>(address & ~3 | 2)) data |= half() << 16; else return;
|
||||
*mem <<= 16;
|
||||
break;
|
||||
case 3:
|
||||
data &= 0x00ffffff;
|
||||
if(auto byte = read<Byte>(address & ~3 | 3)) data |= byte() << 24; else return;
|
||||
*mem <<= 24;
|
||||
break;
|
||||
}
|
||||
|
||||
data |= *mem;
|
||||
rt.s64 = (s32)data;
|
||||
}
|
||||
|
||||
auto CPU::LWR(r64& rt, cr64& rs, s16 imm) -> void {
|
||||
u64 address = rs.u64 + imm;
|
||||
u64 vaddr = rs.u64 + imm;
|
||||
u32 data = rt.u32;
|
||||
auto mem = read<Word>(vaddr & ~3);
|
||||
if (!mem) return;
|
||||
|
||||
if(context.littleEndian())
|
||||
switch(address & 3) {
|
||||
switch(vaddr & 3) {
|
||||
case 0:
|
||||
data &= 0x00000000;
|
||||
if(auto word = read<Word>(address & ~3 | 0)) data |= word() << 0; else return;
|
||||
*mem >>= 0;
|
||||
data |= *mem;
|
||||
rt.s64 = (s32)data;
|
||||
break;
|
||||
case 1:
|
||||
data &= 0xff000000;
|
||||
if(auto half = read<Half>(address & ~3 | 0)) data |= half() << 8; else return;
|
||||
if(auto byte = read<Byte>(address & ~3 | 2)) data |= byte() << 0; else return;
|
||||
*mem >>= 8;
|
||||
data |= *mem;
|
||||
if(context.bits == 32) rt.u32 = data;
|
||||
if(context.bits == 64) rt.s64 = (s32)data;
|
||||
break;
|
||||
case 2:
|
||||
data &= 0xffff0000;
|
||||
if(auto half = read<Half>(address & ~3 | 0)) data |= half() << 0; else return;
|
||||
*mem >>= 16;
|
||||
data |= *mem;
|
||||
if(context.bits == 32) rt.u32 = data;
|
||||
if(context.bits == 64) rt.s64 = (s32)data;
|
||||
break;
|
||||
case 3:
|
||||
data &= 0xffffff00;
|
||||
if(auto byte = read<Byte>(address & ~3 | 0)) data |= byte() << 0; else return;
|
||||
*mem >>= 24;
|
||||
data |= *mem;
|
||||
if(context.bits == 32) rt.u32 = data;
|
||||
if(context.bits == 64) rt.s64 = (s32)data;
|
||||
break;
|
||||
}
|
||||
|
||||
if(context.bigEndian())
|
||||
switch(address & 3) {
|
||||
switch(vaddr & 3) {
|
||||
case 0:
|
||||
data &= 0xffffff00;
|
||||
if(auto byte = read<Byte>(address & ~3 | 0)) data |= byte() << 0; else return;
|
||||
*mem >>= 24;
|
||||
data |= *mem;
|
||||
if(context.bits == 32) rt.u32 = data;
|
||||
if(context.bits == 64) rt.s64 = (s32)data;
|
||||
break;
|
||||
case 1:
|
||||
data &= 0xffff0000;
|
||||
if(auto half = read<Half>(address & ~3 | 0)) data |= half() << 0; else return;
|
||||
*mem >>= 16;
|
||||
data |= *mem;
|
||||
if(context.bits == 32) rt.u32 = data;
|
||||
if(context.bits == 64) rt.s64 = (s32)data;
|
||||
break;
|
||||
case 2:
|
||||
data &= 0xff000000;
|
||||
if(auto half = read<Half>(address & ~3 | 0)) data |= half() << 8; else return;
|
||||
if(auto byte = read<Byte>(address & ~3 | 2)) data |= byte() << 0; else return;
|
||||
*mem >>= 8;
|
||||
data |= *mem;
|
||||
if(context.bits == 32) rt.u32 = data;
|
||||
if(context.bits == 64) rt.s64 = (s32)data;
|
||||
break;
|
||||
case 3:
|
||||
data &= 0x00000000;
|
||||
if(auto word = read<Word>(address & ~3 | 0)) data |= word() << 0; else return;
|
||||
*mem >>= 0;
|
||||
data |= *mem;
|
||||
rt.s64 = (s32)data;
|
||||
break;
|
||||
}
|
||||
@@ -770,25 +779,21 @@ auto CPU::SB(cr64& rt, cr64& rs, s16 imm) -> void {
|
||||
}
|
||||
|
||||
auto CPU::SC(r64& rt, cr64& rs, s16 imm) -> void {
|
||||
if(auto address = devirtualize(rs.u64 + imm)) {
|
||||
if(scc.llbit) {
|
||||
scc.llbit = 0;
|
||||
rt.u64 = write<Word>(*address, rt.u32);
|
||||
} else {
|
||||
rt.u64 = 0;
|
||||
}
|
||||
if(scc.llbit) {
|
||||
scc.llbit = 0;
|
||||
rt.u64 = write<Word>(rs.u64 + imm, rt.u32);
|
||||
} else {
|
||||
rt.u64 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::SCD(r64& rt, cr64& rs, s16 imm) -> void {
|
||||
if(!context.kernelMode() && context.bits == 32) return exception.reservedInstruction();
|
||||
if(auto address = devirtualize(rs.u64 + imm)) {
|
||||
if(scc.llbit) {
|
||||
scc.llbit = 0;
|
||||
rt.u64 = write<Dual>(*address, rt.u64);
|
||||
} else {
|
||||
rt.u64 = 0;
|
||||
}
|
||||
if(scc.llbit) {
|
||||
scc.llbit = 0;
|
||||
rt.u64 = write<Dual>(rs.u64 + imm, rt.u64);
|
||||
} else {
|
||||
rt.u64 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -799,144 +804,144 @@ auto CPU::SD(cr64& rt, cr64& rs, s16 imm) -> void {
|
||||
|
||||
auto CPU::SDL(cr64& rt, cr64& rs, s16 imm) -> void {
|
||||
if(!context.kernelMode() && context.bits == 32) return exception.reservedInstruction();
|
||||
u64 address = rs.u64 + imm;
|
||||
u64 vaddr = rs.u64 + imm;
|
||||
u64 data = rt.u64;
|
||||
|
||||
if(context.littleEndian())
|
||||
switch(address & 7) {
|
||||
switch(vaddr & 7) {
|
||||
case 0:
|
||||
if(!write<Byte>(address & ~7 | 7, data >> 56)) return;
|
||||
if(!write<Byte>(vaddr & ~7 | 7, data >> 56)) return;
|
||||
break;
|
||||
case 1:
|
||||
if(!write<Half>(address & ~7 | 6, data >> 48)) return;
|
||||
if(!write<Half>(vaddr & ~7 | 6, data >> 48)) return;
|
||||
break;
|
||||
case 2:
|
||||
if(!write<Byte>(address & ~7 | 5, data >> 56)) return;
|
||||
if(!write<Half>(address & ~7 | 6, data >> 40)) return;
|
||||
if(!write<Byte>(vaddr & ~7 | 5, data >> 56)) return;
|
||||
if(!write<Half>(vaddr & ~7 | 6, data >> 40)) return;
|
||||
break;
|
||||
case 3:
|
||||
if(!write<Word>(address & ~7 | 4, data >> 32)) return;
|
||||
if(!write<Word>(vaddr & ~7 | 4, data >> 32)) return;
|
||||
break;
|
||||
case 4:
|
||||
if(!write<Byte>(address & ~7 | 3, data >> 56)) return;
|
||||
if(!write<Word>(address & ~7 | 4, data >> 24)) return;
|
||||
if(!write<Byte>(vaddr & ~7 | 3, data >> 56)) return;
|
||||
if(!write<Word>(vaddr & ~7 | 4, data >> 24)) return;
|
||||
break;
|
||||
case 5:
|
||||
if(!write<Half>(address & ~7 | 2, data >> 48)) return;
|
||||
if(!write<Word>(address & ~7 | 4, data >> 16)) return;
|
||||
if(!write<Half>(vaddr & ~7 | 2, data >> 48)) return;
|
||||
if(!write<Word>(vaddr & ~7 | 4, data >> 16)) return;
|
||||
break;
|
||||
case 6:
|
||||
if(!write<Byte>(address & ~7 | 1, data >> 56)) return;
|
||||
if(!write<Half>(address & ~7 | 2, data >> 40)) return;
|
||||
if(!write<Word>(address & ~7 | 4, data >> 8)) return;
|
||||
if(!write<Byte>(vaddr & ~7 | 1, data >> 56)) return;
|
||||
if(!write<Half>(vaddr & ~7 | 2, data >> 40)) return;
|
||||
if(!write<Word>(vaddr & ~7 | 4, data >> 8)) return;
|
||||
break;
|
||||
case 7:
|
||||
if(!write<Dual>(address & ~7 | 0, data >> 0)) return;
|
||||
if(!write<Dual>(vaddr & ~7 | 0, data >> 0)) return;
|
||||
break;
|
||||
}
|
||||
|
||||
if(context.bigEndian())
|
||||
switch(address & 7) {
|
||||
switch(vaddr & 7) {
|
||||
case 0:
|
||||
if(!write<Dual>(address & ~7 | 0, data >> 0)) return;
|
||||
if(!write<Dual>(vaddr & ~7 | 0, data >> 0)) return;
|
||||
break;
|
||||
case 1:
|
||||
if(!write<Byte>(address & ~7 | 1, data >> 56)) return;
|
||||
if(!write<Half>(address & ~7 | 2, data >> 40)) return;
|
||||
if(!write<Word>(address & ~7 | 4, data >> 8)) return;
|
||||
if(!write<Byte>(vaddr & ~7 | 1, data >> 56)) return;
|
||||
if(!write<Half>(vaddr & ~7 | 2, data >> 40)) return;
|
||||
if(!write<Word>(vaddr & ~7 | 4, data >> 8)) return;
|
||||
break;
|
||||
case 2:
|
||||
if(!write<Half>(address & ~7 | 2, data >> 48)) return;
|
||||
if(!write<Word>(address & ~7 | 4, data >> 16)) return;
|
||||
if(!write<Half>(vaddr & ~7 | 2, data >> 48)) return;
|
||||
if(!write<Word>(vaddr & ~7 | 4, data >> 16)) return;
|
||||
break;
|
||||
case 3:
|
||||
if(!write<Byte>(address & ~7 | 3, data >> 56)) return;
|
||||
if(!write<Word>(address & ~7 | 4, data >> 24)) return;
|
||||
if(!write<Byte>(vaddr & ~7 | 3, data >> 56)) return;
|
||||
if(!write<Word>(vaddr & ~7 | 4, data >> 24)) return;
|
||||
break;
|
||||
case 4:
|
||||
if(!write<Word>(address & ~7 | 4, data >> 32)) return;
|
||||
if(!write<Word>(vaddr & ~7 | 4, data >> 32)) return;
|
||||
break;
|
||||
case 5:
|
||||
if(!write<Byte>(address & ~7 | 5, data >> 56)) return;
|
||||
if(!write<Half>(address & ~7 | 6, data >> 40)) return;
|
||||
if(!write<Byte>(vaddr & ~7 | 5, data >> 56)) return;
|
||||
if(!write<Half>(vaddr & ~7 | 6, data >> 40)) return;
|
||||
break;
|
||||
case 6:
|
||||
if(!write<Half>(address & ~7 | 6, data >> 48)) return;
|
||||
if(!write<Half>(vaddr & ~7 | 6, data >> 48)) return;
|
||||
break;
|
||||
case 7:
|
||||
if(!write<Byte>(address & ~7 | 7, data >> 56)) return;
|
||||
if(!write<Byte>(vaddr & ~7 | 7, data >> 56)) return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::SDR(cr64& rt, cr64& rs, s16 imm) -> void {
|
||||
if(!context.kernelMode() && context.bits == 32) return exception.reservedInstruction();
|
||||
u64 address = rs.u64 + imm;
|
||||
u64 vaddr = rs.u64 + imm;
|
||||
u64 data = rt.u64;
|
||||
|
||||
if(context.littleEndian())
|
||||
switch(address & 7) {
|
||||
switch(vaddr & 7) {
|
||||
case 0:
|
||||
if(!write<Dual>(address & ~7 | 0, data >> 0)) return;
|
||||
if(!write<Dual>(vaddr & ~7 | 0, data >> 0)) return;
|
||||
break;
|
||||
case 1:
|
||||
if(!write<Word>(address & ~7 | 0, data >> 24)) return;
|
||||
if(!write<Half>(address & ~7 | 4, data >> 8)) return;
|
||||
if(!write<Byte>(address & ~7 | 6, data >> 0)) return;
|
||||
if(!write<Word>(vaddr & ~7 | 0, data >> 24)) return;
|
||||
if(!write<Half>(vaddr & ~7 | 4, data >> 8)) return;
|
||||
if(!write<Byte>(vaddr & ~7 | 6, data >> 0)) return;
|
||||
break;
|
||||
case 2:
|
||||
if(!write<Word>(address & ~7 | 0, data >> 16)) return;
|
||||
if(!write<Half>(address & ~7 | 4, data >> 0)) return;
|
||||
if(!write<Word>(vaddr & ~7 | 0, data >> 16)) return;
|
||||
if(!write<Half>(vaddr & ~7 | 4, data >> 0)) return;
|
||||
break;
|
||||
case 3:
|
||||
if(!write<Word>(address & ~7 | 0, data >> 8)) return;
|
||||
if(!write<Byte>(address & ~7 | 4, data >> 0)) return;
|
||||
if(!write<Word>(vaddr & ~7 | 0, data >> 8)) return;
|
||||
if(!write<Byte>(vaddr & ~7 | 4, data >> 0)) return;
|
||||
break;
|
||||
case 4:
|
||||
if(!write<Word>(address & ~7 | 0, data >> 0)) return;
|
||||
if(!write<Word>(vaddr & ~7 | 0, data >> 0)) return;
|
||||
break;
|
||||
case 5:
|
||||
if(!write<Half>(address & ~7 | 0, data >> 8)) return;
|
||||
if(!write<Byte>(address & ~7 | 2, data >> 0)) return;
|
||||
if(!write<Half>(vaddr & ~7 | 0, data >> 8)) return;
|
||||
if(!write<Byte>(vaddr & ~7 | 2, data >> 0)) return;
|
||||
break;
|
||||
case 6:
|
||||
if(!write<Half>(address & ~7 | 0, data >> 0)) return;
|
||||
if(!write<Half>(vaddr & ~7 | 0, data >> 0)) return;
|
||||
break;
|
||||
case 7:
|
||||
if(!write<Byte>(address & ~7 | 0, data >> 0)) return;
|
||||
if(!write<Byte>(vaddr & ~7 | 0, data >> 0)) return;
|
||||
break;
|
||||
}
|
||||
|
||||
if(context.bigEndian())
|
||||
switch(address & 7) {
|
||||
switch(vaddr & 7) {
|
||||
case 0:
|
||||
if(!write<Byte>(address & ~7 | 0, data >> 0)) return;
|
||||
if(!write<Byte>(vaddr & ~7 | 0, data >> 0)) return;
|
||||
break;
|
||||
case 1:
|
||||
if(!write<Half>(address & ~7 | 0, data >> 0)) return;
|
||||
if(!write<Half>(vaddr & ~7 | 0, data >> 0)) return;
|
||||
break;
|
||||
case 2:
|
||||
if(!write<Half>(address & ~7 | 0, data >> 8)) return;
|
||||
if(!write<Byte>(address & ~7 | 2, data >> 0)) return;
|
||||
if(!write<Half>(vaddr & ~7 | 0, data >> 8)) return;
|
||||
if(!write<Byte>(vaddr & ~7 | 2, data >> 0)) return;
|
||||
break;
|
||||
case 3:
|
||||
if(!write<Word>(address & ~7 | 0, data >> 0)) return;
|
||||
if(!write<Word>(vaddr & ~7 | 0, data >> 0)) return;
|
||||
break;
|
||||
case 4:
|
||||
if(!write<Word>(address & ~7 | 0, data >> 8)) return;
|
||||
if(!write<Byte>(address & ~7 | 4, data >> 0)) return;
|
||||
if(!write<Word>(vaddr & ~7 | 0, data >> 8)) return;
|
||||
if(!write<Byte>(vaddr & ~7 | 4, data >> 0)) return;
|
||||
break;
|
||||
case 5:
|
||||
if(!write<Word>(address & ~7 | 0, data >> 16)) return;
|
||||
if(!write<Half>(address & ~7 | 4, data >> 0)) return;
|
||||
if(!write<Word>(vaddr & ~7 | 0, data >> 16)) return;
|
||||
if(!write<Half>(vaddr & ~7 | 4, data >> 0)) return;
|
||||
break;
|
||||
case 6:
|
||||
if(!write<Word>(address & ~7 | 0, data >> 24)) return;
|
||||
if(!write<Half>(address & ~7 | 4, data >> 8)) return;
|
||||
if(!write<Byte>(address & ~7 | 6, data >> 0)) return;
|
||||
if(!write<Word>(vaddr & ~7 | 0, data >> 24)) return;
|
||||
if(!write<Half>(vaddr & ~7 | 4, data >> 8)) return;
|
||||
if(!write<Byte>(vaddr & ~7 | 6, data >> 0)) return;
|
||||
break;
|
||||
case 7:
|
||||
if(!write<Dual>(address & ~7 | 0, data >> 0)) return;
|
||||
if(!write<Dual>(vaddr & ~7 | 0, data >> 0)) return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -999,79 +1004,79 @@ auto CPU::SW(cr64& rt, cr64& rs, s16 imm) -> void {
|
||||
}
|
||||
|
||||
auto CPU::SWL(cr64& rt, cr64& rs, s16 imm) -> void {
|
||||
u64 address = rs.u64 + imm;
|
||||
u64 vaddr = rs.u64 + imm;
|
||||
u32 data = rt.u32;
|
||||
|
||||
if(context.littleEndian())
|
||||
switch(address & 3) {
|
||||
switch(vaddr & 3) {
|
||||
case 0:
|
||||
if(!write<Byte>(address & ~3 | 3, data >> 24)) return;
|
||||
if(!write<Byte>(vaddr & ~3 | 3, data >> 24)) return;
|
||||
break;
|
||||
case 1:
|
||||
if(!write<Half>(address & ~3 | 2, data >> 16)) return;
|
||||
if(!write<Half>(vaddr & ~3 | 2, data >> 16)) return;
|
||||
break;
|
||||
case 2:
|
||||
if(!write<Byte>(address & ~3 | 1, data >> 24)) return;
|
||||
if(!write<Half>(address & ~3 | 2, data >> 8)) return;
|
||||
if(!write<Byte>(vaddr & ~3 | 1, data >> 24)) return;
|
||||
if(!write<Half>(vaddr & ~3 | 2, data >> 8)) return;
|
||||
break;
|
||||
case 3:
|
||||
if(!write<Word>(address & ~3 | 0, data >> 0)) return;
|
||||
if(!write<Word>(vaddr & ~3 | 0, data >> 0)) return;
|
||||
break;
|
||||
}
|
||||
|
||||
if(context.bigEndian())
|
||||
switch(address & 3) {
|
||||
switch(vaddr & 3) {
|
||||
case 0:
|
||||
if(!write<Word>(address & ~3 | 0, data >> 0)) return;
|
||||
if(!write<Word>(vaddr & ~3 | 0, data >> 0)) return;
|
||||
break;
|
||||
case 1:
|
||||
if(!write<Byte>(address & ~3 | 1, data >> 24)) return;
|
||||
if(!write<Half>(address & ~3 | 2, data >> 8)) return;
|
||||
if(!write<Byte>(vaddr & ~3 | 1, data >> 24)) return;
|
||||
if(!write<Half>(vaddr & ~3 | 2, data >> 8)) return;
|
||||
break;
|
||||
case 2:
|
||||
if(!write<Half>(address & ~3 | 2, data >> 16)) return;
|
||||
if(!write<Half>(vaddr & ~3 | 2, data >> 16)) return;
|
||||
break;
|
||||
case 3:
|
||||
if(!write<Byte>(address & ~3 | 3, data >> 24)) return;
|
||||
if(!write<Byte>(vaddr & ~3 | 3, data >> 24)) return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::SWR(cr64& rt, cr64& rs, s16 imm) -> void {
|
||||
u64 address = rs.u64 + imm;
|
||||
u64 vaddr = rs.u64 + imm;
|
||||
u32 data = rt.u32;
|
||||
|
||||
if(context.littleEndian())
|
||||
switch(address & 3) {
|
||||
switch(vaddr & 3) {
|
||||
case 0:
|
||||
if(!write<Word>(address & ~3 | 0, data >> 0)) return;
|
||||
if(!write<Word>(vaddr & ~3 | 0, data >> 0)) return;
|
||||
break;
|
||||
case 1:
|
||||
if(!write<Half>(address & ~3 | 0, data >> 8)) return;
|
||||
if(!write<Byte>(address & ~3 | 2, data >> 0)) return;
|
||||
if(!write<Half>(vaddr & ~3 | 0, data >> 8)) return;
|
||||
if(!write<Byte>(vaddr & ~3 | 2, data >> 0)) return;
|
||||
break;
|
||||
case 2:
|
||||
if(!write<Half>(address & ~3 | 0, data >> 0)) return;
|
||||
if(!write<Half>(vaddr & ~3 | 0, data >> 0)) return;
|
||||
break;
|
||||
case 3:
|
||||
if(!write<Byte>(address & ~3 | 0, data >> 0)) return;
|
||||
if(!write<Byte>(vaddr & ~3 | 0, data >> 0)) return;
|
||||
break;
|
||||
}
|
||||
|
||||
if(context.bigEndian())
|
||||
switch(address & 3) {
|
||||
switch(vaddr & 3) {
|
||||
case 0:
|
||||
if(!write<Byte>(address & ~3 | 0, data >> 0)) return;
|
||||
if(!write<Byte>(vaddr & ~3 | 0, data >> 0)) return;
|
||||
break;
|
||||
case 1:
|
||||
if(!write<Half>(address & ~3 | 0, data >> 0)) return;
|
||||
if(!write<Half>(vaddr & ~3 | 0, data >> 0)) return;
|
||||
break;
|
||||
case 2:
|
||||
if(!write<Half>(address & ~3 | 0, data >> 8)) return;
|
||||
if(!write<Byte>(address & ~3 | 2, data >> 0)) return;
|
||||
if(!write<Half>(vaddr & ~3 | 0, data >> 8)) return;
|
||||
if(!write<Byte>(vaddr & ~3 | 2, data >> 0)) return;
|
||||
break;
|
||||
case 3:
|
||||
if(!write<Word>(address & ~3 | 0, data >> 0)) return;
|
||||
if(!write<Word>(vaddr & ~3 | 0, data >> 0)) return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,11 +126,15 @@ auto CPU::getControlRegister(n5 index) -> u64 {
|
||||
case 30: //error exception program counter
|
||||
data = scc.epcError;
|
||||
break;
|
||||
default:
|
||||
data = scc.latch;
|
||||
break;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
auto CPU::setControlRegister(n5 index, n64 data) -> void {
|
||||
scc.latch = data;
|
||||
//read-only variables are defined but commented out for documentation purposes
|
||||
switch(index) {
|
||||
case 0: //index
|
||||
@@ -320,6 +324,7 @@ auto CPU::TLBP() -> void {
|
||||
auto& entry = tlb.entry[index];
|
||||
auto mask = ~entry.pageMask & ~0x1fff;
|
||||
if((entry.virtualAddress & mask) != (scc.tlb.virtualAddress & mask)) continue;
|
||||
if(entry.region != scc.tlb.region) continue;
|
||||
if(!entry.global[0] || !entry.global[1]) {
|
||||
if(entry.addressSpaceID != scc.tlb.addressSpaceID) continue;
|
||||
}
|
||||
|
||||
+106
-104
@@ -1,223 +1,225 @@
|
||||
//32-bit segments
|
||||
|
||||
auto CPU::kernelSegment32(u32 address) const -> Context::Segment {
|
||||
if(address <= 0x7fff'ffff) return Context::Segment::Mapped; //kuseg
|
||||
if(address <= 0x9fff'ffff) return Context::Segment::Cached; //kseg0
|
||||
if(address <= 0xbfff'ffff) return Context::Segment::Direct; //kseg1
|
||||
if(address <= 0xdfff'ffff) return Context::Segment::Mapped; //ksseg
|
||||
if(address <= 0xffff'ffff) return Context::Segment::Mapped; //kseg3
|
||||
auto CPU::kernelSegment32(u32 vaddr) const -> Context::Segment {
|
||||
if(vaddr <= 0x7fff'ffff) return Context::Segment::Mapped; //kuseg
|
||||
if(vaddr <= 0x9fff'ffff) return Context::Segment::Cached; //kseg0
|
||||
if(vaddr <= 0xbfff'ffff) return Context::Segment::Direct; //kseg1
|
||||
if(vaddr <= 0xdfff'ffff) return Context::Segment::Mapped; //ksseg
|
||||
if(vaddr <= 0xffff'ffff) return Context::Segment::Mapped; //kseg3
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto CPU::supervisorSegment32(u32 address) const -> Context::Segment {
|
||||
if(address <= 0x7fff'ffff) return Context::Segment::Mapped; //suseg
|
||||
if(address <= 0xbfff'ffff) return Context::Segment::Unused;
|
||||
if(address <= 0xdfff'ffff) return Context::Segment::Mapped; //sseg
|
||||
if(address <= 0xffff'ffff) return Context::Segment::Unused;
|
||||
auto CPU::supervisorSegment32(u32 vaddr) const -> Context::Segment {
|
||||
if(vaddr <= 0x7fff'ffff) return Context::Segment::Mapped; //suseg
|
||||
if(vaddr <= 0xbfff'ffff) return Context::Segment::Unused;
|
||||
if(vaddr <= 0xdfff'ffff) return Context::Segment::Mapped; //sseg
|
||||
if(vaddr <= 0xffff'ffff) return Context::Segment::Unused;
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto CPU::userSegment32(u32 address) const -> Context::Segment {
|
||||
if(address <= 0x7fff'ffff) return Context::Segment::Mapped; //useg
|
||||
if(address <= 0xffff'ffff) return Context::Segment::Unused;
|
||||
auto CPU::userSegment32(u32 vaddr) const -> Context::Segment {
|
||||
if(vaddr <= 0x7fff'ffff) return Context::Segment::Mapped; //useg
|
||||
if(vaddr <= 0xffff'ffff) return Context::Segment::Unused;
|
||||
unreachable;
|
||||
}
|
||||
|
||||
//64-bit segments
|
||||
|
||||
auto CPU::kernelSegment64(u64 address) const -> Context::Segment {
|
||||
if(address <= 0x0000'00ff'ffff'ffffull) return Context::Segment::Mapped; //xkuseg
|
||||
if(address <= 0x3fff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(address <= 0x4000'00ff'ffff'ffffull) return Context::Segment::Mapped; //xksseg
|
||||
if(address <= 0x7fff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(address <= 0x8000'0000'ffff'ffffull) return Context::Segment::Cached; //xkphys*
|
||||
if(address <= 0x87ff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(address <= 0x8800'0000'ffff'ffffull) return Context::Segment::Cached; //xkphys*
|
||||
if(address <= 0x8fff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(address <= 0x9000'0000'ffff'ffffull) return Context::Segment::Direct; //xkphys*
|
||||
if(address <= 0x97ff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(address <= 0x9800'0000'ffff'ffffull) return Context::Segment::Cached; //xkphys*
|
||||
if(address <= 0x9fff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(address <= 0xa000'0000'ffff'ffffull) return Context::Segment::Cached; //xkphys*
|
||||
if(address <= 0xa7ff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(address <= 0xa800'0000'ffff'ffffull) return Context::Segment::Cached; //xkphys*
|
||||
if(address <= 0xafff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(address <= 0xb000'0000'ffff'ffffull) return Context::Segment::Cached; //xkphys*
|
||||
if(address <= 0xb7ff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(address <= 0xb800'0000'ffff'ffffull) return Context::Segment::Cached; //xkphys*
|
||||
if(address <= 0xbfff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(address <= 0xc000'00ff'7fff'ffffull) return Context::Segment::Mapped; //xkseg
|
||||
if(address <= 0xffff'ffff'7fff'ffffull) return Context::Segment::Unused;
|
||||
if(address <= 0xffff'ffff'9fff'ffffull) return Context::Segment::Cached; //ckseg0
|
||||
if(address <= 0xffff'ffff'bfff'ffffull) return Context::Segment::Direct; //ckseg1
|
||||
if(address <= 0xffff'ffff'dfff'ffffull) return Context::Segment::Mapped; //ckseg2
|
||||
if(address <= 0xffff'ffff'ffff'ffffull) return Context::Segment::Mapped; //ckseg3
|
||||
auto CPU::kernelSegment64(u64 vaddr) const -> Context::Segment {
|
||||
if(vaddr <= 0x0000'00ff'ffff'ffffull) return Context::Segment::Mapped; //xkuseg
|
||||
if(vaddr <= 0x3fff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(vaddr <= 0x4000'00ff'ffff'ffffull) return Context::Segment::Mapped; //xksseg
|
||||
if(vaddr <= 0x7fff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(vaddr <= 0x8000'0000'ffff'ffffull) return Context::Segment::Cached; //xkphys*
|
||||
if(vaddr <= 0x87ff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(vaddr <= 0x8800'0000'ffff'ffffull) return Context::Segment::Cached; //xkphys*
|
||||
if(vaddr <= 0x8fff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(vaddr <= 0x9000'0000'ffff'ffffull) return Context::Segment::Direct; //xkphys*
|
||||
if(vaddr <= 0x97ff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(vaddr <= 0x9800'0000'ffff'ffffull) return Context::Segment::Cached; //xkphys*
|
||||
if(vaddr <= 0x9fff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(vaddr <= 0xa000'0000'ffff'ffffull) return Context::Segment::Cached; //xkphys*
|
||||
if(vaddr <= 0xa7ff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(vaddr <= 0xa800'0000'ffff'ffffull) return Context::Segment::Cached; //xkphys*
|
||||
if(vaddr <= 0xafff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(vaddr <= 0xb000'0000'ffff'ffffull) return Context::Segment::Cached; //xkphys*
|
||||
if(vaddr <= 0xb7ff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(vaddr <= 0xb800'0000'ffff'ffffull) return Context::Segment::Cached; //xkphys*
|
||||
if(vaddr <= 0xbfff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(vaddr <= 0xc000'00ff'7fff'ffffull) return Context::Segment::Mapped; //xkseg
|
||||
if(vaddr <= 0xffff'ffff'7fff'ffffull) return Context::Segment::Unused;
|
||||
if(vaddr <= 0xffff'ffff'9fff'ffffull) return Context::Segment::Cached; //ckseg0
|
||||
if(vaddr <= 0xffff'ffff'bfff'ffffull) return Context::Segment::Direct; //ckseg1
|
||||
if(vaddr <= 0xffff'ffff'dfff'ffffull) return Context::Segment::Mapped; //ckseg2
|
||||
if(vaddr <= 0xffff'ffff'ffff'ffffull) return Context::Segment::Mapped; //ckseg3
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto CPU::supervisorSegment64(u64 address) const -> Context::Segment {
|
||||
if(address <= 0x0000'00ff'ffff'ffffull) return Context::Segment::Mapped; //xsuseg
|
||||
if(address <= 0x3fff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(address <= 0x4000'00ff'ffff'ffffull) return Context::Segment::Mapped; //xsseg
|
||||
if(address <= 0xffff'ffff'bfff'ffffull) return Context::Segment::Unused;
|
||||
if(address <= 0xffff'ffff'dfff'ffffull) return Context::Segment::Mapped; //csseg
|
||||
if(address <= 0xffff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
auto CPU::supervisorSegment64(u64 vaddr) const -> Context::Segment {
|
||||
if(vaddr <= 0x0000'00ff'ffff'ffffull) return Context::Segment::Mapped; //xsuseg
|
||||
if(vaddr <= 0x3fff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(vaddr <= 0x4000'00ff'ffff'ffffull) return Context::Segment::Mapped; //xsseg
|
||||
if(vaddr <= 0xffff'ffff'bfff'ffffull) return Context::Segment::Unused;
|
||||
if(vaddr <= 0xffff'ffff'dfff'ffffull) return Context::Segment::Mapped; //csseg
|
||||
if(vaddr <= 0xffff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto CPU::userSegment64(u64 address) const -> Context::Segment {
|
||||
if(address <= 0x0000'00ff'ffff'ffffull) return Context::Segment::Mapped; //xuseg
|
||||
if(address <= 0xffff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
auto CPU::userSegment64(u64 vaddr) const -> Context::Segment {
|
||||
if(vaddr <= 0x0000'00ff'ffff'ffffull) return Context::Segment::Mapped; //xuseg
|
||||
if(vaddr <= 0xffff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
unreachable;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
auto CPU::segment(u64 address) -> Context::Segment {
|
||||
auto segment = context.segment[address >> 29 & 7];
|
||||
//if(likely(context.bits == 32))
|
||||
return (Context::Segment)segment;
|
||||
auto CPU::segment(u64 vaddr) -> Context::Segment {
|
||||
auto segment = context.segment[vaddr >> 29 & 7];
|
||||
if(likely(context.bits == 32))
|
||||
return (Context::Segment)segment;
|
||||
switch(segment) {
|
||||
case Context::Segment::Kernel64:
|
||||
return kernelSegment64(address);
|
||||
return kernelSegment64(vaddr);
|
||||
case Context::Segment::Supervisor64:
|
||||
return supervisorSegment64(address);
|
||||
return supervisorSegment64(vaddr);
|
||||
case Context::Segment::User64:
|
||||
return userSegment64(address);
|
||||
return userSegment64(vaddr);
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto CPU::devirtualize(u64 address) -> maybe<u64> {
|
||||
switch(context.segment[address >> 29 & 7]) {
|
||||
auto CPU::devirtualize(u64 vaddr) -> maybe<u64> {
|
||||
switch(segment(vaddr)) {
|
||||
case Context::Segment::Unused:
|
||||
addressException(address);
|
||||
addressException(vaddr);
|
||||
exception.addressLoad();
|
||||
return nothing;
|
||||
case Context::Segment::Mapped:
|
||||
if(auto match = tlb.load(address)) return match.address;
|
||||
tlb.exception(address);
|
||||
if(auto match = tlb.load(vaddr)) return match.address & context.physMask;
|
||||
addressException(vaddr);
|
||||
return nothing;
|
||||
case Context::Segment::Cached:
|
||||
case Context::Segment::Direct:
|
||||
return address;
|
||||
return vaddr & context.physMask;
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto CPU::fetch(u64 address) -> u32 {
|
||||
switch(segment(address)) {
|
||||
auto CPU::fetch(u64 vaddr) -> u32 {
|
||||
switch(segment(vaddr)) {
|
||||
case Context::Segment::Unused:
|
||||
step(1);
|
||||
addressException(address);
|
||||
addressException(vaddr);
|
||||
exception.addressLoad();
|
||||
return 0; //nop
|
||||
case Context::Segment::Mapped:
|
||||
if(auto match = tlb.load(address)) {
|
||||
if(match.cache) return icache.fetch(match.address);
|
||||
if(auto match = tlb.load(vaddr)) {
|
||||
if(match.cache) return icache.fetch(match.address & context.physMask);
|
||||
step(1);
|
||||
return bus.read<Word>(match.address);
|
||||
return bus.read<Word>(match.address & context.physMask);
|
||||
}
|
||||
step(1);
|
||||
tlb.exception(address);
|
||||
addressException(vaddr);
|
||||
return 0; //nop
|
||||
case Context::Segment::Cached:
|
||||
return icache.fetch(address);
|
||||
return icache.fetch(vaddr & context.physMask);
|
||||
case Context::Segment::Direct:
|
||||
step(1);
|
||||
return bus.read<Word>(address);
|
||||
return bus.read<Word>(vaddr & context.physMask);
|
||||
}
|
||||
|
||||
unreachable;
|
||||
}
|
||||
|
||||
template<u32 Size>
|
||||
auto CPU::read(u64 address) -> maybe<u64> {
|
||||
auto CPU::read(u64 vaddr) -> maybe<u64> {
|
||||
if constexpr(Accuracy::CPU::AddressErrors) {
|
||||
if(unlikely(address & Size - 1)) {
|
||||
if(unlikely(vaddr & Size - 1)) {
|
||||
step(1);
|
||||
addressException(address);
|
||||
addressException(vaddr);
|
||||
exception.addressLoad();
|
||||
return nothing;
|
||||
}
|
||||
if (context.bits == 32 && unlikely((s32)address != address)) {
|
||||
if (context.bits == 32 && unlikely((s32)vaddr != vaddr)) {
|
||||
step(1);
|
||||
addressException(address);
|
||||
addressException(vaddr);
|
||||
exception.addressLoad();
|
||||
return nothing;
|
||||
}
|
||||
}
|
||||
|
||||
switch(segment(address)) {
|
||||
switch(segment(vaddr)) {
|
||||
case Context::Segment::Unused:
|
||||
step(1);
|
||||
addressException(address);
|
||||
addressException(vaddr);
|
||||
exception.addressLoad();
|
||||
return nothing;
|
||||
case Context::Segment::Mapped:
|
||||
if(auto match = tlb.load(address)) {
|
||||
if(match.cache) return dcache.read<Size>(match.address);
|
||||
if(auto match = tlb.load(vaddr)) {
|
||||
if(match.cache) return dcache.read<Size>(match.address & context.physMask);
|
||||
step(1);
|
||||
return bus.read<Size>(match.address);
|
||||
return bus.read<Size>(match.address & context.physMask);
|
||||
}
|
||||
step(1);
|
||||
tlb.exception(address);
|
||||
addressException(vaddr);
|
||||
return nothing;
|
||||
case Context::Segment::Cached:
|
||||
return dcache.read<Size>(address);
|
||||
return dcache.read<Size>(vaddr & context.physMask);
|
||||
case Context::Segment::Direct:
|
||||
step(1);
|
||||
return bus.read<Size>(address);
|
||||
return bus.read<Size>(vaddr & context.physMask);
|
||||
}
|
||||
|
||||
unreachable;
|
||||
}
|
||||
|
||||
template<u32 Size>
|
||||
auto CPU::write(u64 address, u64 data) -> bool {
|
||||
auto CPU::write(u64 vaddr, u64 data) -> bool {
|
||||
if constexpr(Accuracy::CPU::AddressErrors) {
|
||||
if(unlikely(address & Size - 1)) {
|
||||
if(unlikely(vaddr & Size - 1)) {
|
||||
step(1);
|
||||
addressException(address);
|
||||
addressException(vaddr);
|
||||
exception.addressStore();
|
||||
return false;
|
||||
}
|
||||
if (context.bits == 32 && unlikely((s32)address != address)) {
|
||||
if (context.bits == 32 && unlikely((s32)vaddr != vaddr)) {
|
||||
step(1);
|
||||
addressException(address);
|
||||
addressException(vaddr);
|
||||
exception.addressStore();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
switch(segment(address)) {
|
||||
switch(segment(vaddr)) {
|
||||
case Context::Segment::Unused:
|
||||
step(1);
|
||||
addressException(address);
|
||||
addressException(vaddr);
|
||||
exception.addressStore();
|
||||
return false;
|
||||
case Context::Segment::Mapped:
|
||||
if(auto match = tlb.store(address)) {
|
||||
if(match.cache) return dcache.write<Size>(match.address, data), true;
|
||||
if(auto match = tlb.store(vaddr)) {
|
||||
if(match.cache) return dcache.write<Size>(match.address & context.physMask, data), true;
|
||||
step(1);
|
||||
return bus.write<Size>(match.address, data), true;
|
||||
return bus.write<Size>(match.address & context.physMask, data), true;
|
||||
}
|
||||
step(1);
|
||||
tlb.exception(address);
|
||||
addressException(vaddr);
|
||||
return false;
|
||||
case Context::Segment::Cached:
|
||||
return dcache.write<Size>(address, data), true;
|
||||
return dcache.write<Size>(vaddr & context.physMask, data), true;
|
||||
case Context::Segment::Direct:
|
||||
step(1);
|
||||
return bus.write<Size>(address, data), true;
|
||||
return bus.write<Size>(vaddr & context.physMask, data), true;
|
||||
}
|
||||
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto CPU::addressException(u64 address) -> void {
|
||||
scc.badVirtualAddress = address;
|
||||
scc.context.badVirtualAddress = address >> 13;
|
||||
scc.xcontext.badVirtualAddress = address >> 13;
|
||||
scc.xcontext.region = address >> 62;
|
||||
auto CPU::addressException(u64 vaddr) -> void {
|
||||
scc.badVirtualAddress = vaddr;
|
||||
scc.tlb.virtualAddress.bit(13,39) = vaddr >> 13;
|
||||
scc.tlb.region = vaddr >> 62;
|
||||
scc.context.badVirtualAddress = vaddr >> 13;
|
||||
scc.xcontext.badVirtualAddress = vaddr >> 13;
|
||||
scc.xcontext.region = vaddr >> 62;
|
||||
}
|
||||
@@ -8,6 +8,7 @@ auto CPU::serialize(serializer& s) -> void {
|
||||
s(branch.state);
|
||||
|
||||
s(context.endian);
|
||||
s(context.physMask);
|
||||
s(context.mode);
|
||||
s(context.bits);
|
||||
s(context.segment);
|
||||
|
||||
+22
-29
@@ -1,61 +1,54 @@
|
||||
//the N64 TLB is 32-bit only: only the 64-bit XTLB exception vector is used.
|
||||
|
||||
auto CPU::TLB::load(u32 address) -> Match {
|
||||
auto CPU::TLB::load(u64 vaddr) -> Match {
|
||||
for(auto& entry : this->entry) {
|
||||
if(!entry.globals && entry.addressSpaceID != self.scc.tlb.addressSpaceID) continue;
|
||||
if((u32)(address & entry.addressMaskHi) != (u32)entry.virtualAddress) continue;
|
||||
bool lo = address & entry.addressSelect;
|
||||
if((vaddr & entry.addressMaskHi) != entry.virtualAddress) continue;
|
||||
if(vaddr >> 62 != entry.region) continue;
|
||||
bool lo = vaddr & entry.addressSelect;
|
||||
if(!entry.valid[lo]) {
|
||||
exception(address);
|
||||
self.debugger.tlbLoadInvalid(address);
|
||||
self.addressException(vaddr);
|
||||
self.debugger.tlbLoadInvalid(vaddr);
|
||||
self.exception.tlbLoadInvalid();
|
||||
return {false};
|
||||
}
|
||||
physicalAddress = entry.physicalAddress[lo] + (address & entry.addressMaskLo);
|
||||
self.debugger.tlbLoad(address, physicalAddress);
|
||||
physicalAddress = entry.physicalAddress[lo] + (vaddr & entry.addressMaskLo);
|
||||
self.debugger.tlbLoad(vaddr, physicalAddress);
|
||||
return {true, entry.cacheAlgorithm[lo] != 2, physicalAddress};
|
||||
}
|
||||
exception(address);
|
||||
self.debugger.tlbLoadMiss(address);
|
||||
self.addressException(vaddr);
|
||||
self.debugger.tlbLoadMiss(vaddr);
|
||||
self.exception.tlbLoadMiss();
|
||||
return {false};
|
||||
}
|
||||
|
||||
auto CPU::TLB::store(u32 address) -> Match {
|
||||
auto CPU::TLB::store(u64 vaddr) -> Match {
|
||||
for(auto& entry : this->entry) {
|
||||
if(!entry.globals && entry.addressSpaceID != self.scc.tlb.addressSpaceID) continue;
|
||||
if((u32)(address & entry.addressMaskHi) != (u32)entry.virtualAddress) continue;
|
||||
bool lo = address & entry.addressSelect;
|
||||
if((vaddr & entry.addressMaskHi) != entry.virtualAddress) continue;
|
||||
if(vaddr >> 62 != entry.region) continue;
|
||||
bool lo = vaddr & entry.addressSelect;
|
||||
if(!entry.valid[lo]) {
|
||||
exception(address);
|
||||
self.debugger.tlbStoreInvalid(address);
|
||||
self.addressException(vaddr);
|
||||
self.debugger.tlbStoreInvalid(vaddr);
|
||||
self.exception.tlbStoreInvalid();
|
||||
return {false};
|
||||
}
|
||||
if(!entry.dirty[lo]) {
|
||||
exception(address);
|
||||
self.debugger.tlbModification(address);
|
||||
self.addressException(vaddr);
|
||||
self.debugger.tlbModification(vaddr);
|
||||
self.exception.tlbModification();
|
||||
return {false};
|
||||
}
|
||||
physicalAddress = entry.physicalAddress[lo] + (address & entry.addressMaskLo);
|
||||
self.debugger.tlbStore(address, physicalAddress);
|
||||
physicalAddress = entry.physicalAddress[lo] + (vaddr & entry.addressMaskLo);
|
||||
self.debugger.tlbStore(vaddr, physicalAddress);
|
||||
return {true, entry.cacheAlgorithm[lo] != 2, physicalAddress};
|
||||
}
|
||||
exception(address);
|
||||
self.debugger.tlbStoreMiss(address);
|
||||
self.addressException(vaddr);
|
||||
self.debugger.tlbStoreMiss(vaddr);
|
||||
self.exception.tlbStoreMiss();
|
||||
return {false};
|
||||
}
|
||||
|
||||
auto CPU::TLB::exception(u32 address) -> void {
|
||||
self.scc.badVirtualAddress = address;
|
||||
self.scc.tlb.virtualAddress.bit(13,39) = address >> 13;
|
||||
self.scc.context.badVirtualAddress = address >> 13;
|
||||
self.scc.xcontext.badVirtualAddress = address >> 13;
|
||||
self.scc.xcontext.region = 0;
|
||||
}
|
||||
|
||||
auto CPU::TLB::Entry::synchronize() -> void {
|
||||
pageMask = pageMask & (0b101010101010 << 13);
|
||||
pageMask |= pageMask >> 1;
|
||||
|
||||
+2
-3
@@ -4,9 +4,6 @@ auto PI::dmaRead() -> void {
|
||||
u16 data = rdram.ram.read<Half>(io.dramAddress + address);
|
||||
busWrite<Half>(io.pbusAddress + address, data);
|
||||
}
|
||||
io.dmaBusy = 0;
|
||||
io.interrupt = 1;
|
||||
mi.raise(MI::IRQ::PI);
|
||||
}
|
||||
|
||||
auto PI::dmaWrite() -> void {
|
||||
@@ -48,7 +45,9 @@ auto PI::dmaWrite() -> void {
|
||||
|
||||
first_block = false;
|
||||
}
|
||||
}
|
||||
|
||||
auto PI::dmaFinished() -> void {
|
||||
io.dmaBusy = 0;
|
||||
io.interrupt = 1;
|
||||
mi.raise(MI::IRQ::PI);
|
||||
|
||||
+4
-2
@@ -98,14 +98,16 @@ auto PI::ioWrite(u32 address, u32 data_) -> void {
|
||||
//PI_READ_LENGTH
|
||||
io.readLength = n24(data);
|
||||
io.dmaBusy = 1;
|
||||
queue.insert(Queue::PI_DMA_Read, io.readLength * 9);
|
||||
queue.insert(Queue::PI_DMA_Read, io.readLength * 36);
|
||||
dmaRead();
|
||||
}
|
||||
|
||||
if(address == 3) {
|
||||
//PI_WRITE_LENGTH
|
||||
io.writeLength = n24(data);
|
||||
io.dmaBusy = 1;
|
||||
queue.insert(Queue::PI_DMA_Write, io.writeLength * 9);
|
||||
queue.insert(Queue::PI_DMA_Write, io.writeLength * 36);
|
||||
dmaWrite();
|
||||
}
|
||||
|
||||
if(address == 4) {
|
||||
|
||||
@@ -21,6 +21,7 @@ struct PI : Memory::IO<PI> {
|
||||
//dma.cpp
|
||||
auto dmaRead() -> void;
|
||||
auto dmaWrite() -> void;
|
||||
auto dmaFinished() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto ioRead(u32 address) -> u32;
|
||||
|
||||
+51
-130
@@ -140,155 +140,76 @@ auto PIF::scan() -> void {
|
||||
}
|
||||
n8 output[64];
|
||||
b1 valid = 0;
|
||||
b1 over = 0;
|
||||
|
||||
//status
|
||||
if(input[0] == 0x00 || input[0] == 0xff) {
|
||||
//controller
|
||||
if(channel < 4 && controllers[channel]->device) {
|
||||
output[0] = 0x05; //0x05 = gamepad; 0x02 = mouse
|
||||
output[1] = 0x00;
|
||||
output[2] = 0x02; //0x02 = nothing present in controller slot
|
||||
if(auto& device = controllers[channel]->device) {
|
||||
if(auto gamepad = dynamic_cast<Gamepad*>(device.data())) {
|
||||
if(gamepad->ram || gamepad->motor) {
|
||||
output[2] = 0x01; //0x01 = pak present
|
||||
}
|
||||
}
|
||||
//controller port communication
|
||||
if (channel < 4 && controllers[channel]->device) {
|
||||
n2 status = controllers[channel]->device->comm(send, recv, input, output);
|
||||
valid = status.bit(0);
|
||||
over = status.bit(1);
|
||||
}
|
||||
|
||||
if (channel >= 4) {
|
||||
//status
|
||||
if(input[0] == 0x00 || input[0] == 0xff) {
|
||||
//cartridge EEPROM (4kbit)
|
||||
if(cartridge.eeprom.size == 512) {
|
||||
output[0] = 0x00;
|
||||
output[1] = 0x80;
|
||||
output[2] = 0x00;
|
||||
valid = 1;
|
||||
}
|
||||
|
||||
//cartridge EEPROM (16kbit)
|
||||
if(cartridge.eeprom.size == 2048) {
|
||||
output[0] = 0x00;
|
||||
output[1] = 0xc0;
|
||||
output[2] = 0x00;
|
||||
valid = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//read EEPROM
|
||||
if(input[0] == 0x04 && send >= 2) {
|
||||
u32 address = input[1] * 8;
|
||||
for(u32 index : range(recv)) {
|
||||
output[index] = cartridge.eeprom.read<Byte>(address++);
|
||||
}
|
||||
valid = 1;
|
||||
}
|
||||
|
||||
//cartridge EEPROM (4kbit)
|
||||
if(channel >= 4 && cartridge.eeprom.size == 512) {
|
||||
//write EEPROM
|
||||
if(input[0] == 0x05 && send >= 2 && recv >= 1) {
|
||||
u32 address = input[1] * 8;
|
||||
for(u32 index : range(send - 2)) {
|
||||
cartridge.eeprom.write<Byte>(address++, input[2 + index]);
|
||||
}
|
||||
output[0] = 0x00;
|
||||
output[1] = 0x80;
|
||||
output[2] = 0x00;
|
||||
valid = 1;
|
||||
}
|
||||
|
||||
//cartridge EEPROM (16kbit)
|
||||
if(channel >= 4 && cartridge.eeprom.size == 2048) {
|
||||
output[0] = 0x00;
|
||||
output[1] = 0xc0;
|
||||
output[2] = 0x00;
|
||||
valid = 1;
|
||||
//RTC status
|
||||
if(input[0] == 0x06) {
|
||||
debug(unimplemented, "[SI::main] RTC status");
|
||||
}
|
||||
}
|
||||
|
||||
//read controller state
|
||||
if(input[0] == 0x01) {
|
||||
if(channel < 4 && controllers[channel]->device) {
|
||||
u32 data = controllers[channel]->device->read();
|
||||
output[0] = data >> 24;
|
||||
output[1] = data >> 16;
|
||||
output[2] = data >> 8;
|
||||
output[3] = data >> 0;
|
||||
if(recv <= 4) {
|
||||
ram.write<Byte>(recvOffset, 0x00 | recv & 0x3f);
|
||||
} else {
|
||||
ram.write<Byte>(recvOffset, 0x40 | recv & 0x3f);
|
||||
}
|
||||
valid = 1;
|
||||
//RTC read
|
||||
if(input[0] == 0x07) {
|
||||
debug(unimplemented, "[SI::main] RTC read");
|
||||
}
|
||||
}
|
||||
|
||||
//read pak
|
||||
if(input[0] == 0x02 && send >= 3 && recv >= 1) {
|
||||
if(auto& device = controllers[channel]->device) {
|
||||
if(auto gamepad = dynamic_cast<Gamepad*>(device.data())) {
|
||||
//controller pak
|
||||
if(auto& ram = gamepad->ram) {
|
||||
u32 address = (input[1] << 8 | input[2] << 0) & ~31;
|
||||
if(addressCRC(address) == (n5)input[2]) {
|
||||
for(u32 index : range(recv - 1)) {
|
||||
output[index] = ram.read<Byte>(address++);
|
||||
}
|
||||
output[recv - 1] = dataCRC({&output[0], recv - 1});
|
||||
valid = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//rumble pak
|
||||
if(gamepad->motor) {
|
||||
u32 address = (input[1] << 8 | input[2] << 0) & ~31;
|
||||
if(addressCRC(address) == (n5)input[2]) {
|
||||
for(u32 index : range(recv - 1)) {
|
||||
output[index] = 0x80;
|
||||
}
|
||||
output[recv - 1] = dataCRC({&output[0], recv - 1});
|
||||
valid = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
//RTC write
|
||||
if(input[0] == 0x08) {
|
||||
debug(unimplemented, "[SI::main] RTC write");
|
||||
}
|
||||
}
|
||||
|
||||
//write pak
|
||||
if(input[0] == 0x03 && send >= 3 && recv >= 1) {
|
||||
if(auto& device = controllers[channel]->device) {
|
||||
if(auto gamepad = dynamic_cast<Gamepad*>(device.data())) {
|
||||
//controller pak
|
||||
if(auto& ram = gamepad->ram) {
|
||||
u32 address = (input[1] << 8 | input[2] << 0) & ~31;
|
||||
if(addressCRC(address) == (n5)input[2]) {
|
||||
for(u32 index : range(send - 3)) {
|
||||
ram.write<Byte>(address++, input[3 + index]);
|
||||
}
|
||||
output[0] = dataCRC({&input[3], send - 3});
|
||||
valid = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//rumble pak
|
||||
if(gamepad->motor) {
|
||||
u32 address = (input[1] << 8 | input[2] << 0) & ~31;
|
||||
if(addressCRC(address) == (n5)input[2]) {
|
||||
output[0] = dataCRC({&input[3], send - 3});
|
||||
valid = 1;
|
||||
gamepad->rumble(input[3] & 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//read EEPROM
|
||||
if(input[0] == 0x04 && send >= 2) {
|
||||
u32 address = input[1] * 8;
|
||||
for(u32 index : range(recv)) {
|
||||
output[index] = cartridge.eeprom.read<Byte>(address++);
|
||||
}
|
||||
valid = 1;
|
||||
}
|
||||
|
||||
//write EEPROM
|
||||
if(input[0] == 0x05 && send >= 2 && recv >= 1) {
|
||||
u32 address = input[1] * 8;
|
||||
for(u32 index : range(send - 2)) {
|
||||
cartridge.eeprom.write<Byte>(address++, input[2 + index]);
|
||||
}
|
||||
output[0] = 0x00;
|
||||
valid = 1;
|
||||
}
|
||||
|
||||
//RTC status
|
||||
if(input[0] == 0x06) {
|
||||
debug(unimplemented, "[SI::main] RTC status");
|
||||
}
|
||||
|
||||
//RTC read
|
||||
if(input[0] == 0x07) {
|
||||
debug(unimplemented, "[SI::main] RTC read");
|
||||
}
|
||||
|
||||
//RTC write
|
||||
if(input[0] == 0x08) {
|
||||
debug(unimplemented, "[SI::main] RTC write");
|
||||
}
|
||||
|
||||
if(!valid) {
|
||||
ram.write<Byte>(recvOffset, 0x80 | recv & 0x3f);
|
||||
}
|
||||
if(over) {
|
||||
ram.write<Byte>(recvOffset, 0x40 | recv & 0x3f);
|
||||
}
|
||||
for(u32 index : range(recv)) {
|
||||
ram.write<Byte>(offset++, output[index]);
|
||||
}
|
||||
|
||||
+19
-11
@@ -22,14 +22,14 @@ auto RDP::readWord(u32 address) -> u32 {
|
||||
data.bit( 0) = command.source;
|
||||
data.bit( 1) = command.freeze;
|
||||
data.bit( 2) = command.flush;
|
||||
data.bit( 3) = 0; //start gclk?
|
||||
data.bit( 3) = command.startGclk;
|
||||
data.bit( 4) = command.tmemBusy > 0;
|
||||
data.bit( 5) = command.pipeBusy > 0;
|
||||
data.bit( 6) = command.bufferBusy > 0;
|
||||
data.bit( 7) = command.ready;
|
||||
data.bit( 8) = 0; //DMA busy
|
||||
data.bit( 9) = 0; //end valid
|
||||
data.bit(10) = 0; //start valid
|
||||
data.bit( 9) = command.endValid;
|
||||
data.bit(10) = command.startValid;
|
||||
}
|
||||
|
||||
if(address == 4) {
|
||||
@@ -62,18 +62,18 @@ auto RDP::writeWord(u32 address, u32 data_) -> void {
|
||||
|
||||
if(address == 0) {
|
||||
//DPC_START
|
||||
command.start = data.bit(0,23) & ~7;
|
||||
command.current = command.start;
|
||||
if(!command.startValid) command.start = data.bit(0,23) & ~7;
|
||||
command.startValid = 1;
|
||||
}
|
||||
|
||||
if(address == 1) {
|
||||
//DPC_END
|
||||
command.end = data.bit(0,23) & ~7;
|
||||
if(command.end > command.current) {
|
||||
command.freeze = 0;
|
||||
render();
|
||||
command.ready = 1;
|
||||
if(command.startValid) {
|
||||
command.current = command.start;
|
||||
command.startValid = 0;
|
||||
}
|
||||
flushCommands();
|
||||
}
|
||||
|
||||
if(address == 2) {
|
||||
@@ -84,8 +84,8 @@ auto RDP::writeWord(u32 address, u32 data_) -> void {
|
||||
//DPC_STATUS
|
||||
if(data.bit(0)) command.source = 0;
|
||||
if(data.bit(1)) command.source = 1;
|
||||
if(data.bit(2)) command.freeze = 0;
|
||||
//if(data.bit(3)) command.freeze = 1;
|
||||
if(data.bit(2)) command.freeze = 0, flushCommands();
|
||||
if(data.bit(3)) command.freeze = 1;
|
||||
if(data.bit(4)) command.flush = 0;
|
||||
if(data.bit(5)) command.flush = 1;
|
||||
if(data.bit(6)) command.tmemBusy = 0;
|
||||
@@ -172,3 +172,11 @@ auto RDP::IO::writeWord(u32 address, u32 data_) -> void {
|
||||
|
||||
self.debugger.ioDPS(Write, address, data);
|
||||
}
|
||||
|
||||
auto RDP::flushCommands() -> void {
|
||||
if(command.freeze) return;
|
||||
command.pipeBusy = 1;
|
||||
command.startGclk = 1;
|
||||
if(command.end > command.current) render();
|
||||
command.ready = 1;
|
||||
}
|
||||
@@ -67,6 +67,7 @@ struct RDP : Thread, Memory::IO<RDP> {
|
||||
//io.cpp
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
auto flushCommands() -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
@@ -82,6 +83,9 @@ struct RDP : Thread, Memory::IO<RDP> {
|
||||
n1 source; //0 = RDRAM, 1 = DMEM
|
||||
n1 freeze;
|
||||
n1 flush;
|
||||
n1 startValid;
|
||||
n1 endValid;
|
||||
n1 startGclk;
|
||||
n1 ready = 1;
|
||||
} command;
|
||||
|
||||
|
||||
@@ -619,6 +619,8 @@ auto RDP::syncTile() -> void {
|
||||
//0x29
|
||||
auto RDP::syncFull() -> void {
|
||||
mi.raise(MI::IRQ::DP);
|
||||
command.pipeBusy = 0;
|
||||
command.startGclk = 0;
|
||||
}
|
||||
|
||||
//0x2a
|
||||
|
||||
+37
-21
@@ -1,27 +1,43 @@
|
||||
auto RSP::dmaTransfer() -> void {
|
||||
if(dma.requests.empty()) return;
|
||||
auto request = *dma.requests.read();
|
||||
auto region = !request.pbusRegion ? 0x0400'0000 : 0x0400'1000;
|
||||
auto RSP::dmaTransferStart(void) -> void {
|
||||
if(dma.busy.any()) return;
|
||||
if(dma.full.any()) {
|
||||
dma.current = dma.pending;
|
||||
dma.busy = dma.full;
|
||||
dma.full = {0,0};
|
||||
queue.insert(Queue::RSP_DMA, (dma.current.length+8) / 8 * 3);
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::dmaTransferStep() -> void {
|
||||
auto& region = !dma.current.pbusRegion ? dmem : imem;
|
||||
|
||||
if(request.type == DMA::Request::Type::Read) {
|
||||
for(u32 block : range(request.count)) {
|
||||
for(u32 offset = 0; offset < request.length; offset += 4) {
|
||||
u32 data = bus.read<Word>(request.dramAddress + offset);
|
||||
bus.write<Word>(region + (request.pbusAddress + offset & 0xFFF), data);
|
||||
}
|
||||
request.pbusAddress += request.length;
|
||||
request.dramAddress += request.length + request.skip;
|
||||
if(dma.busy.read) {
|
||||
if constexpr(Accuracy::RSP::Recompiler) {
|
||||
if(dma.current.pbusRegion) recompiler.invalidate();
|
||||
}
|
||||
for(u32 i = 0; i <= dma.current.length; i += 8) {
|
||||
u64 data = rdram.ram.read<Dual>(dma.current.dramAddress);
|
||||
region.write<Dual>(dma.current.pbusAddress, data);
|
||||
dma.current.dramAddress += 8;
|
||||
dma.current.pbusAddress += 8;
|
||||
}
|
||||
}
|
||||
if(dma.busy.write) {
|
||||
for(u32 i = 0; i <= dma.current.length; i += 8) {
|
||||
u64 data = region.read<Dual>(dma.current.pbusAddress);
|
||||
rdram.ram.write<Dual>(dma.current.dramAddress, data);
|
||||
dma.current.dramAddress += 8;
|
||||
dma.current.pbusAddress += 8;
|
||||
}
|
||||
}
|
||||
|
||||
if(request.type == DMA::Request::Type::Write) {
|
||||
for(u32 block : range(request.count)) {
|
||||
for(u32 offset = 0; offset < request.length; offset += 4) {
|
||||
u32 data = bus.read<Word>(region + (request.pbusAddress + offset & 0xFFF));
|
||||
bus.write<Word>(request.dramAddress + offset, data);
|
||||
}
|
||||
request.pbusAddress += request.length;
|
||||
request.dramAddress += request.length + request.skip;
|
||||
}
|
||||
if(dma.current.count) {
|
||||
dma.current.count -= 1;
|
||||
dma.current.dramAddress += dma.current.skip;
|
||||
queue.insert(Queue::RSP_DMA, (dma.current.length+8) / 8 * 3);
|
||||
} else {
|
||||
dma.busy = {0,0};
|
||||
dma.current.length = 0xFF8;
|
||||
dmaTransferStart();
|
||||
}
|
||||
}
|
||||
@@ -301,24 +301,69 @@ auto RSP::SDV(cr128& vt, cr32& rs, s8 imm) -> void {
|
||||
template<u8 e>
|
||||
auto RSP::SFV(cr128& vt, cr32& rs, s8 imm) -> void {
|
||||
auto address = rs.u32 + imm * 16;
|
||||
auto start = e >> 1;
|
||||
auto end = start + 4;
|
||||
auto base = address & 15;
|
||||
address &= ~15;
|
||||
for(u32 offset = start; offset < end; offset++) {
|
||||
dmem.write<Byte>(address + (base & 15), vt.element(offset & 7) >> 7);
|
||||
base += 4;
|
||||
auto base = address & 7;
|
||||
address &= ~7;
|
||||
switch (e) {
|
||||
case 0: case 15:
|
||||
dmem.write<Byte>(address + (base + 0 & 15), vt.element(0) >> 7);
|
||||
dmem.write<Byte>(address + (base + 4 & 15), vt.element(1) >> 7);
|
||||
dmem.write<Byte>(address + (base + 8 & 15), vt.element(2) >> 7);
|
||||
dmem.write<Byte>(address + (base + 12 & 15), vt.element(3) >> 7);
|
||||
break;
|
||||
case 1:
|
||||
dmem.write<Byte>(address + (base + 0 & 15), vt.element(6) >> 7);
|
||||
dmem.write<Byte>(address + (base + 4 & 15), vt.element(7) >> 7);
|
||||
dmem.write<Byte>(address + (base + 8 & 15), vt.element(4) >> 7);
|
||||
dmem.write<Byte>(address + (base + 12 & 15), vt.element(5) >> 7);
|
||||
break;
|
||||
case 4:
|
||||
dmem.write<Byte>(address + (base + 0 & 15), vt.element(1) >> 7);
|
||||
dmem.write<Byte>(address + (base + 4 & 15), vt.element(2) >> 7);
|
||||
dmem.write<Byte>(address + (base + 8 & 15), vt.element(3) >> 7);
|
||||
dmem.write<Byte>(address + (base + 12 & 15), vt.element(0) >> 7);
|
||||
break;
|
||||
case 5:
|
||||
dmem.write<Byte>(address + (base + 0 & 15), vt.element(7) >> 7);
|
||||
dmem.write<Byte>(address + (base + 4 & 15), vt.element(4) >> 7);
|
||||
dmem.write<Byte>(address + (base + 8 & 15), vt.element(5) >> 7);
|
||||
dmem.write<Byte>(address + (base + 12 & 15), vt.element(6) >> 7);
|
||||
break;
|
||||
case 8:
|
||||
dmem.write<Byte>(address + (base + 0 & 15), vt.element(4) >> 7);
|
||||
dmem.write<Byte>(address + (base + 4 & 15), vt.element(5) >> 7);
|
||||
dmem.write<Byte>(address + (base + 8 & 15), vt.element(6) >> 7);
|
||||
dmem.write<Byte>(address + (base + 12 & 15), vt.element(7) >> 7);
|
||||
break;
|
||||
case 11:
|
||||
dmem.write<Byte>(address + (base + 0 & 15), vt.element(3) >> 7);
|
||||
dmem.write<Byte>(address + (base + 4 & 15), vt.element(0) >> 7);
|
||||
dmem.write<Byte>(address + (base + 8 & 15), vt.element(1) >> 7);
|
||||
dmem.write<Byte>(address + (base + 12 & 15), vt.element(2) >> 7);
|
||||
break;
|
||||
case 12:
|
||||
dmem.write<Byte>(address + (base + 0 & 15), vt.element(5) >> 7);
|
||||
dmem.write<Byte>(address + (base + 4 & 15), vt.element(6) >> 7);
|
||||
dmem.write<Byte>(address + (base + 8 & 15), vt.element(7) >> 7);
|
||||
dmem.write<Byte>(address + (base + 12 & 15), vt.element(4) >> 7);
|
||||
break;
|
||||
default:
|
||||
dmem.write<Byte>(address + (base + 0 & 15), 0);
|
||||
dmem.write<Byte>(address + (base + 4 & 15), 0);
|
||||
dmem.write<Byte>(address + (base + 8 & 15), 0);
|
||||
dmem.write<Byte>(address + (base + 12 & 15), 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<u8 e>
|
||||
auto RSP::SHV(cr128& vt, cr32& rs, s8 imm) -> void {
|
||||
auto address = rs.u32 + imm * 16;
|
||||
auto index = address & 7;
|
||||
address &= ~7;
|
||||
for(u32 offset = 0; offset < 8; offset++) {
|
||||
auto byte = e + offset * 2;
|
||||
auto value = vt.byte(byte + 0 & 15) << 1 | vt.byte(byte + 1 & 15) >> 7;
|
||||
dmem.write<Byte>(address, value);
|
||||
address += 2;
|
||||
dmem.write<Byte>(address + (index + offset * 2 & 15), value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -381,14 +426,14 @@ auto RSP::SSV(cr128& vt, cr32& rs, s8 imm) -> void {
|
||||
template<u8 e>
|
||||
auto RSP::STV(u8 vt, cr32& rs, s8 imm) -> void {
|
||||
auto address = rs.u32 + imm * 16;
|
||||
auto start = vt;
|
||||
auto end = min(32, start + 8);
|
||||
auto element = 8 - (e >> 1);
|
||||
auto base = (address & 15) + (element << 1);
|
||||
address &= ~15;
|
||||
auto start = vt & ~7;
|
||||
auto end = start + 8;
|
||||
auto element = 16 - (e & ~1);
|
||||
auto base = (address & 7) - (e & ~1);
|
||||
address &= ~7;
|
||||
for(u32 offset = start; offset < end; offset++) {
|
||||
dmem.writeUnaligned<Half>(address + (base & 15), vpu.r[offset].element(element++ & 7));
|
||||
base += 2;
|
||||
dmem.write<Byte>(address + (base++ & 15), vpu.r[offset].byte(element++ & 15));
|
||||
dmem.write<Byte>(address + (base++ & 15), vpu.r[offset].byte(element++ & 15));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -411,8 +456,8 @@ auto RSP::SWV(cr128& vt, cr32& rs, s8 imm) -> void {
|
||||
auto address = rs.u32 + imm * 16;
|
||||
auto start = e;
|
||||
auto end = start + 16;
|
||||
auto base = address & 15;
|
||||
address &= ~15;
|
||||
auto base = address & 7;
|
||||
address &= ~7;
|
||||
for(u32 offset = start; offset < end; offset++) {
|
||||
dmem.write<Byte>(address + (base++ & 15), vt.byte(offset & 15));
|
||||
}
|
||||
@@ -424,15 +469,21 @@ auto RSP::VABS(r128& vd, cr128& vs, cr128& vt) -> void {
|
||||
r128 vte = vt(e);
|
||||
for(u32 n : range(8)) {
|
||||
if(vs.s16(n) < 0) {
|
||||
if(vte.s16(n) == -32768) vte.s16(n) = -32767;
|
||||
ACCL.s16(n) = -vte.s16(n);
|
||||
if(vte.s16(n) == -32768) {
|
||||
ACCL.s16(n) = -32768;
|
||||
vd.s16(n) = 32767;
|
||||
} else {
|
||||
ACCL.s16(n) = -vte.s16(n);
|
||||
vd.s16(n) = -vte.s16(n);
|
||||
}
|
||||
} else if(vs.s16(n) > 0) {
|
||||
ACCL.s16(n) = +vte.s16(n);
|
||||
vd.s16(n) = +vte.s16(n);
|
||||
} else {
|
||||
ACCL.s16(n) = 0;
|
||||
vd.s16(n) = 0;
|
||||
}
|
||||
}
|
||||
vd = ACCL;
|
||||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
@@ -580,10 +631,14 @@ auto RSP::VCL(r128& vd, cr128& vs, cr128& vt) -> void {
|
||||
if(VCOL.get(n)) {
|
||||
if(VCOH.get(n)) {
|
||||
ACCL.u16(n) = VCCL.get(n) ? -vte.u16(n) : vs.u16(n);
|
||||
} else if(VCE.get(n)) {
|
||||
ACCL.u16(n) = VCCL.set(n, vs.u16(n) + vte.u16(n) <= 0xffff) ? -vte.u16(n) : vs.u16(n);
|
||||
} else {
|
||||
ACCL.u16(n) = VCCL.set(n, vs.u16(n) + vte.u16(n) == 0) ? -vte.u16(n) : vs.u16(n);
|
||||
u16 sum = vs.u16(n) + vte.u16(n);
|
||||
bool carry = (vs.u16(n) + vte.u16(n)) != sum;
|
||||
if(VCE.get(n)) {
|
||||
ACCL.u16(n) = VCCL.set(n, (!sum || !carry)) ? -vte.u16(n) : vs.u16(n);
|
||||
} else {
|
||||
ACCL.u16(n) = VCCL.set(n, (!sum && !carry)) ? -vte.u16(n) : vs.u16(n);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(VCOH.get(n)) {
|
||||
@@ -678,12 +733,11 @@ auto RSP::VEQ(r128& vd, cr128& vs, cr128& vt) -> void {
|
||||
if constexpr(Accuracy::RSP::SISD) {
|
||||
cr128 vte = vt(e);
|
||||
for(u32 n : range(8)) {
|
||||
ACCL.u16(n) = VCCL.set(n, !VCE.get(n) && vs.u16(n) == vte.u16(n)) ? vs.u16(n) : vte.u16(n);
|
||||
ACCL.u16(n) = VCCL.set(n, !VCOH.get(n) && vs.u16(n) == vte.u16(n)) ? vs.u16(n) : vte.u16(n);
|
||||
}
|
||||
VCCH = zero; //unverified
|
||||
VCOL = zero;
|
||||
VCOH = zero;
|
||||
VCE = zero;
|
||||
vd = ACCL;
|
||||
}
|
||||
|
||||
@@ -706,12 +760,11 @@ auto RSP::VGE(r128& vd, cr128& vs, cr128& vt) -> void {
|
||||
if constexpr(Accuracy::RSP::SISD) {
|
||||
cr128 vte = vt(e);
|
||||
for(u32 n : range(8)) {
|
||||
ACCL.u16(n) = VCCL.set(n, vs.s16(n) > vte.s16(n) || (vs.s16(n) == vte.s16(n) && (!VCOL.get(n) || VCE.get(n)))) ? vs.u16(n) : vte.u16(n);
|
||||
ACCL.u16(n) = VCCL.set(n, vs.s16(n) > vte.s16(n) || (vs.s16(n) == vte.s16(n) && (!VCOL.get(n) || !VCOH.get(n)))) ? vs.u16(n) : vte.u16(n);
|
||||
}
|
||||
VCCH = zero; //unverified
|
||||
VCOL = zero;
|
||||
VCOH = zero;
|
||||
VCE = zero;
|
||||
vd = ACCL;
|
||||
}
|
||||
|
||||
@@ -737,12 +790,11 @@ auto RSP::VLT(r128& vd, cr128& vs, cr128& vt) -> void {
|
||||
if constexpr(Accuracy::RSP::SISD) {
|
||||
cr128 vte = vt(e);
|
||||
for(u32 n : range(8)) {
|
||||
ACCL.u16(n) = VCCL.set(n, vs.s16(n) < vte.s16(n) || (vs.s16(n) == vte.s16(n) && VCOL.get(n) && !VCE.get(n))) ? vs.u16(n) : vte.u16(n);
|
||||
ACCL.u16(n) = VCCL.set(n, vs.s16(n) < vte.s16(n) || (vs.s16(n) == vte.s16(n) && VCOL.get(n) && VCOH.get(n))) ? vs.u16(n) : vte.u16(n);
|
||||
}
|
||||
VCCH = zero; //unverified
|
||||
VCCH = zero;
|
||||
VCOL = zero;
|
||||
VCOH = zero;
|
||||
VCE = zero;
|
||||
vd = ACCL;
|
||||
}
|
||||
|
||||
@@ -768,7 +820,7 @@ auto RSP::VMACF(r128& vd, cr128& vs, cr128& vt) -> void {
|
||||
if constexpr(Accuracy::RSP::SISD) {
|
||||
cr128 vte = vt(e);
|
||||
for(u32 n : range(8)) {
|
||||
accumulatorSet(n, accumulatorGet(n) + vs.s16(n) * vte.s16(n) * 2);
|
||||
accumulatorSet(n, accumulatorGet(n) + (s64)vs.s16(n) * (s64)vte.s16(n) * 2);
|
||||
if constexpr(U == 0) {
|
||||
vd.u16(n) = accumulatorSaturate(n, 1, 0x8000, 0x7fff);
|
||||
}
|
||||
@@ -823,10 +875,9 @@ auto RSP::VMACQ(r128& vd) -> void {
|
||||
for(u32 n : range(8)) {
|
||||
s32 product = ACCH.element(n) << 16 | ACCM.element(n) << 0;
|
||||
if(product < 0 && !(product & 1 << 5)) product += 32;
|
||||
else if(product > 0 && !(product & 1 << 5)) product -= 32;
|
||||
else if(product >= 32 && !(product & 1 << 5)) product -= 32;
|
||||
ACCH.element(n) = product >> 16;
|
||||
ACCM.element(n) = product >> 0;
|
||||
ACCL.element(n) = 0;
|
||||
vd.element(n) = sclamp<16>(product >> 1) & ~15;
|
||||
}
|
||||
}
|
||||
@@ -974,17 +1025,11 @@ auto RSP::VMADN(r128& vd, cr128& vs, cr128& vt) -> void {
|
||||
}
|
||||
}
|
||||
|
||||
template<u8 E>
|
||||
template<u8 e>
|
||||
auto RSP::VMOV(r128& vd, u8 de, cr128& vt) -> void {
|
||||
u8 e = E;
|
||||
switch(e) {
|
||||
case 0x0 ... 0x1: e = e & 0b000 | de & 0b111; break; //hardware glitch
|
||||
case 0x2 ... 0x3: e = e & 0b001 | de & 0b110; break; //hardware glitch
|
||||
case 0x4 ... 0x7: e = e & 0b011 | de & 0b100; break; //hardware glitch
|
||||
case 0x8 ... 0xf: e = e & 0b111 | de & 0b000; break; //normal behavior
|
||||
}
|
||||
vd.u16(de) = vt.u16(e);
|
||||
ACCL = vt(e);
|
||||
cr128 vte = vt(e);
|
||||
vd.u16(de) = vte.u16(de);
|
||||
ACCL = vte;
|
||||
}
|
||||
|
||||
template<u8 e>
|
||||
@@ -1105,9 +1150,9 @@ auto RSP::VMULF(r128& vd, cr128& vs, cr128& vt) -> void {
|
||||
if constexpr(Accuracy::RSP::SISD) {
|
||||
cr128 vte = vt(e);
|
||||
for(u32 n : range(8)) {
|
||||
accumulatorSet(n, vs.s16(n) * vte.s16(n) * 2 + 0x8000);
|
||||
accumulatorSet(n, (s64)vs.s16(n) * (s64)vte.s16(n) * 2 + 0x8000);
|
||||
if constexpr(U == 0) {
|
||||
vd.u16(n) = ACCM.u16(n);
|
||||
vd.u16(n) = accumulatorSaturate(n, 1, 0x8000, 0x7fff);
|
||||
}
|
||||
if constexpr(U == 1) {
|
||||
vd.u16(n) = ACCH.s16(n) < 0 ? 0x0000 : (ACCH.s16(n) ^ ACCM.s16(n)) < 0 ? 0xffff : ACCM.u16(n);
|
||||
@@ -1181,12 +1226,11 @@ auto RSP::VNE(r128& vd, cr128& vs, cr128& vt) -> void {
|
||||
if constexpr(Accuracy::RSP::SISD) {
|
||||
cr128 vte = vt(e);
|
||||
for(u32 n : range(8)) {
|
||||
ACCL.u16(n) = VCCL.set(n, vs.u16(n) != vte.u16(n) || VCE.get(n)) ? vs.u16(n) : vte.u16(n);
|
||||
ACCL.u16(n) = VCCL.set(n, vs.u16(n) != vte.u16(n) || VCOH.get(n)) ? vs.u16(n) : vte.u16(n);
|
||||
}
|
||||
VCCH = zero; //unverified
|
||||
VCOL = zero;
|
||||
VCOH = zero;
|
||||
VCE = zero;
|
||||
vd = ACCL;
|
||||
}
|
||||
|
||||
@@ -1308,12 +1352,12 @@ auto RSP::VRND(r128& vd, u8 vs, cr128& vt) -> void {
|
||||
acc |= ACCM.element(n); acc <<= 16;
|
||||
acc |= ACCL.element(n); acc <<= 16;
|
||||
acc >>= 16;
|
||||
if(D == 0 && acc < 0) acc += product;
|
||||
if(D == 1 && acc >= 0) acc += product;
|
||||
if(D == 0 && acc < 0) acc = sclip<48>(acc + product);
|
||||
if(D == 1 && acc >= 0) acc = sclip<48>(acc + product);
|
||||
ACCH.element(n) = acc >> 32;
|
||||
ACCM.element(n) = acc >> 16;
|
||||
ACCL.element(n) = acc >> 0;
|
||||
vd.element(n) = acc >> 16;
|
||||
vd.element(n) = sclamp<16>(acc >> 16);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -291,8 +291,8 @@ auto RSP::decoderVU() -> void {
|
||||
vu(0x1b, VZERO, VD, VS, VT); //VSAC
|
||||
vu(0x1c, VZERO, VD, VS, VT); //VSUM
|
||||
vu(0x1d, VSAR, VD, VS);
|
||||
op(0x1e, INVALID);
|
||||
op(0x1f, INVALID);
|
||||
vu(0x1e, VZERO, VD, VS, VT);
|
||||
vu(0x1f, VZERO, VD, VS, VT);
|
||||
vu(0x20, VLT, VD, VS, VT);
|
||||
vu(0x21, VEQ, VD, VS, VT);
|
||||
vu(0x22, VNE, VD, VS, VT);
|
||||
@@ -307,8 +307,8 @@ auto RSP::decoderVU() -> void {
|
||||
vu(0x2b, VNOR, VD, VS, VT);
|
||||
vu(0x2c, VXOR, VD, VS, VT);
|
||||
vu(0x2d, VNXOR, VD, VS, VT);
|
||||
op(0x2e, INVALID);
|
||||
op(0x2f, INVALID);
|
||||
vu(0x2e, VZERO, VD, VS, VT);
|
||||
vu(0x2f, VZERO, VD, VS, VT);
|
||||
vu(0x30, VRCP, VD, DE, VT);
|
||||
vu(0x31, VRCPL, VD, DE, VT);
|
||||
vu(0x32, VRCPH, VD, DE, VT);
|
||||
@@ -320,7 +320,7 @@ auto RSP::decoderVU() -> void {
|
||||
vu(0x38, VZERO, VD, VS, VT); //VEXTT
|
||||
vu(0x39, VZERO, VD, VS, VT); //VEXTQ
|
||||
vu(0x3a, VZERO, VD, VS, VT); //VEXTN
|
||||
op(0x3b, INVALID);
|
||||
vu(0x3b, VZERO, VD, VS, VT);
|
||||
vu(0x3c, VZERO, VD, VS, VT); //VINST
|
||||
vu(0x3d, VZERO, VD, VS, VT); //VINSQ
|
||||
vu(0x3e, VZERO, VD, VS, VT); //VINSN
|
||||
|
||||
+27
-52
@@ -12,35 +12,28 @@ auto RSP::ioRead(u32 address) -> u32 {
|
||||
|
||||
if(address == 0) {
|
||||
//SP_PBUS_ADDRESS
|
||||
data.bit( 0,11) = dma.pbusAddress;
|
||||
data.bit(12) = dma.pbusRegion;
|
||||
data.bit( 0,11) = dma.current.pbusAddress;
|
||||
data.bit(12) = dma.current.pbusRegion;
|
||||
}
|
||||
|
||||
if(address == 1) {
|
||||
//SP_DRAM_ADDRESS
|
||||
data.bit(0,23) = dma.dramAddress;
|
||||
data.bit(0,23) = dma.current.dramAddress;
|
||||
}
|
||||
|
||||
if(address == 2) {
|
||||
//SP_READ_LENGTH
|
||||
data.bit( 0,11) = dma.read.length;
|
||||
data.bit(12,19) = dma.read.count;
|
||||
data.bit(20,31) = dma.read.skip;
|
||||
}
|
||||
|
||||
if(address == 3) {
|
||||
//SP_WRITE_LENGTH
|
||||
data.bit( 0,11) = dma.write.length;
|
||||
data.bit(12,19) = dma.write.count;
|
||||
data.bit(20,31) = dma.write.skip;
|
||||
if(address == 2 || address == 3) {
|
||||
//SP_READ_LENGTH or SP_WRITE_LENGTH
|
||||
data.bit( 0,11) = dma.current.length;
|
||||
data.bit(12,19) = dma.current.count;
|
||||
data.bit(20,31) = dma.current.skip;
|
||||
}
|
||||
|
||||
if(address == 4) {
|
||||
//SP_STATUS
|
||||
data.bit( 0) = status.halted;
|
||||
data.bit( 1) = status.broken;
|
||||
data.bit( 2) = !dma.requests.empty();
|
||||
data.bit( 3) = dma.requests.full();
|
||||
data.bit( 2) = dma.busy.any();
|
||||
data.bit( 3) = dma.full.any();
|
||||
data.bit( 4) = status.full;
|
||||
data.bit( 5) = status.singleStep;
|
||||
data.bit( 6) = status.interruptOnBreak;
|
||||
@@ -56,12 +49,12 @@ auto RSP::ioRead(u32 address) -> u32 {
|
||||
|
||||
if(address == 5) {
|
||||
//SP_DMA_FULL
|
||||
data.bit(0) = dma.requests.full();
|
||||
data.bit(0) = dma.full.any();
|
||||
}
|
||||
|
||||
if(address == 6) {
|
||||
//SP_DMA_BUSY
|
||||
data.bit(0) = !dma.requests.empty();
|
||||
data.bit(0) = dma.busy.any();
|
||||
}
|
||||
|
||||
if(address == 7) {
|
||||
@@ -89,51 +82,33 @@ auto RSP::ioWrite(u32 address, u32 data_) -> void {
|
||||
|
||||
if(address == 0) {
|
||||
//SP_PBUS_ADDRESS
|
||||
dma.pbusAddress = data.bit( 0,11);
|
||||
dma.pbusRegion = data.bit(12);
|
||||
dma.pending.pbusAddress.bit(3,11) = data.bit( 3,11);
|
||||
dma.pending.pbusRegion = data.bit(12);
|
||||
}
|
||||
|
||||
if(address == 1) {
|
||||
//SP_DRAM_ADDRESS
|
||||
dma.dramAddress = data.bit(0,23);
|
||||
dma.pending.dramAddress.bit(3,23) = data.bit(3,23);
|
||||
}
|
||||
|
||||
if(address == 2) {
|
||||
//SP_READ_LENGTH
|
||||
dma.read.length = data.bit( 0,11);
|
||||
dma.read.count = data.bit(12,19);
|
||||
dma.read.skip = data.bit(20,31);
|
||||
if(!dma.requests.full()) {
|
||||
DMA::Request request;
|
||||
request.type = DMA::Request::Type::Read;
|
||||
request.pbusRegion = dma.pbusRegion;
|
||||
request.pbusAddress = dma.pbusAddress & ~7;
|
||||
request.dramAddress = dma.dramAddress & ~7;
|
||||
request.length = 1 + (dma.read.length | 7);
|
||||
request.count = 1 + (dma.read.count);
|
||||
request.skip = dma.read.skip & ~7;
|
||||
dma.requests.write(request);
|
||||
queue.insert(Queue::RSP_DMA, request.length * request.count / 4);
|
||||
}
|
||||
dma.pending.length.bit(3,11) = data.bit( 3,11);
|
||||
dma.pending.count = data.bit(12,19);
|
||||
dma.pending.skip.bit(3,11) = data.bit(23,31);
|
||||
dma.full.read = 1;
|
||||
dma.full.write = 0;
|
||||
dmaTransferStart();
|
||||
}
|
||||
|
||||
if(address == 3) {
|
||||
//SP_WRITE_LENGTH
|
||||
dma.write.length = data.bit( 0,11);
|
||||
dma.write.count = data.bit(12,19);
|
||||
dma.write.skip = data.bit(20,31);
|
||||
if(!dma.requests.full()) {
|
||||
DMA::Request request;
|
||||
request.type = DMA::Request::Type::Write;
|
||||
request.pbusRegion = dma.pbusRegion;
|
||||
request.pbusAddress = dma.pbusAddress & ~7;
|
||||
request.dramAddress = dma.dramAddress & ~7;
|
||||
request.length = 1 + (dma.write.length | 7);
|
||||
request.count = 1 + (dma.write.count);
|
||||
request.skip = dma.write.skip & ~7;
|
||||
dma.requests.write(request);
|
||||
queue.insert(Queue::RSP_DMA, request.length * request.count / 4);
|
||||
}
|
||||
dma.pending.length.bit(3,11) = data.bit( 3,11);
|
||||
dma.pending.count = data.bit(12,19);
|
||||
dma.pending.skip.bit(3,11) = data.bit(23,31);
|
||||
dma.full.write = 1;
|
||||
dma.full.read = 0;
|
||||
dmaTransferStart();
|
||||
}
|
||||
|
||||
if(address == 4) {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
auto RSP::Recompiler::pool() -> Pool* {
|
||||
if(context) return context;
|
||||
|
||||
u32 hashcode = 0;
|
||||
nall::Hash::CRC32 hashcode;
|
||||
for(u32 offset : range(4096)) {
|
||||
hashcode = (hashcode << 5) + hashcode + self.imem.read<Byte>(offset);
|
||||
hashcode.input(self.imem.read<Byte>(offset));
|
||||
}
|
||||
|
||||
PoolHashPair pair;
|
||||
pair.pool = (Pool*)allocator.acquire();
|
||||
pair.hashcode = hashcode;
|
||||
pair.hashcode = hashcode.value();
|
||||
if(auto result = pools.find(pair)) {
|
||||
return context = result->pool;
|
||||
}
|
||||
@@ -857,9 +857,13 @@ auto RSP::Recompiler::emitVU(u32 instruction) -> bool {
|
||||
return 0;
|
||||
}
|
||||
|
||||
//INVALID
|
||||
//Invalid opcodes
|
||||
case 0x1e ... 0x1f: {
|
||||
return 0;
|
||||
lea(reg(1), Vd);
|
||||
lea(reg(2), Vs);
|
||||
lea(reg(3), Vt);
|
||||
callvu(&RSP::VZERO);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//VLT Vd,Vs,Vt(e)
|
||||
@@ -990,6 +994,10 @@ auto RSP::Recompiler::emitVU(u32 instruction) -> bool {
|
||||
|
||||
//INVALID
|
||||
case 0x2e ... 0x2f: {
|
||||
lea(reg(1), Vd);
|
||||
lea(reg(2), Vs);
|
||||
lea(reg(3), Vt);
|
||||
callvu(&RSP::VZERO);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1073,6 +1081,10 @@ auto RSP::Recompiler::emitVU(u32 instruction) -> bool {
|
||||
|
||||
//INVALID
|
||||
case 0x3b: {
|
||||
lea(reg(1), Vd);
|
||||
lea(reg(2), Vs);
|
||||
lea(reg(3), Vt);
|
||||
callvu(&RSP::VZERO);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+16
-21
@@ -43,7 +43,8 @@ struct RSP : Thread, Memory::IO<RSP> {
|
||||
} pipeline;
|
||||
|
||||
//dma.cpp
|
||||
auto dmaTransfer() -> void;
|
||||
auto dmaTransferStart() -> void;
|
||||
auto dmaTransferStep() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readWord(u32 address) -> u32;
|
||||
@@ -55,29 +56,23 @@ struct RSP : Thread, Memory::IO<RSP> {
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
struct DMA {
|
||||
n1 pbusRegion;
|
||||
n12 pbusAddress;
|
||||
n24 dramAddress;
|
||||
|
||||
struct Transfer {
|
||||
n12 length;
|
||||
n12 skip;
|
||||
n8 count;
|
||||
} read, write;
|
||||
|
||||
struct Request {
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
enum class Type : u32 { Read, Write } type;
|
||||
struct Regs {
|
||||
n1 pbusRegion;
|
||||
n12 pbusAddress;
|
||||
n24 dramAddress;
|
||||
n16 length;
|
||||
n16 skip;
|
||||
n16 count;
|
||||
};
|
||||
nall::queue<Request[2]> requests;
|
||||
n12 length;
|
||||
n12 skip;
|
||||
n8 count;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
} pending, current;
|
||||
|
||||
struct Status {
|
||||
n1 read;
|
||||
n1 write;
|
||||
|
||||
auto any() -> n1 { return read | write; }
|
||||
} busy, full;
|
||||
} dma;
|
||||
|
||||
struct Status : Memory::IO<Status> {
|
||||
|
||||
@@ -6,16 +6,12 @@ auto RSP::serialize(serializer& s) -> void {
|
||||
s(pipeline.address);
|
||||
s(pipeline.instruction);
|
||||
|
||||
s(dma.pbusRegion);
|
||||
s(dma.pbusAddress);
|
||||
s(dma.dramAddress);
|
||||
s(dma.read.length);
|
||||
s(dma.read.skip);
|
||||
s(dma.read.count);
|
||||
s(dma.write.length);
|
||||
s(dma.write.skip);
|
||||
s(dma.write.count);
|
||||
s(dma.requests);
|
||||
s(dma.pending);
|
||||
s(dma.current);
|
||||
s(dma.busy.read);
|
||||
s(dma.busy.write);
|
||||
s(dma.full.read);
|
||||
s(dma.full.write);
|
||||
|
||||
s(status.semaphore);
|
||||
s(status.halted);
|
||||
@@ -49,8 +45,7 @@ auto RSP::serialize(serializer& s) -> void {
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::DMA::Request::serialize(serializer& s) -> void {
|
||||
s((u32&)type);
|
||||
auto RSP::DMA::Regs::serialize(serializer& s) -> void {
|
||||
s(pbusRegion);
|
||||
s(pbusAddress);
|
||||
s(dramAddress);
|
||||
|
||||
@@ -46,6 +46,7 @@ auto System::serialize(serializer& s, bool synchronize) -> void {
|
||||
s(vi);
|
||||
s(ai);
|
||||
s(pi);
|
||||
s(pif);
|
||||
s(ri);
|
||||
s(si);
|
||||
s(cpu);
|
||||
|
||||
@@ -23,6 +23,7 @@ auto Disc::command(u8 operation) -> void {
|
||||
case 0x04: commandFastForward(); break;
|
||||
case 0x05: commandRewind(); break;
|
||||
case 0x06: commandReadWithRetry(); break;
|
||||
case 0x07: commandMotorOn(); break;
|
||||
case 0x08: commandStop(); break;
|
||||
case 0x09: commandPause(); break;
|
||||
case 0x0a: commandInitialize(); break;
|
||||
@@ -42,12 +43,14 @@ auto Disc::command(u8 operation) -> void {
|
||||
case 0x19: commandTest(); break;
|
||||
case 0x1a: commandGetID(); break;
|
||||
case 0x1b: commandReadWithoutRetry(); break;
|
||||
case 0x1e: commandReadToc(); break;
|
||||
case 0x20 ... 0x4f: commandInvalid(); break;
|
||||
case 0x50 ... 0x57: commandInvalid(); break; //secret unlock commands
|
||||
case 0x58 ... 0xff: commandInvalid(); break;
|
||||
default: commandUnimplemented(operation); break;
|
||||
}
|
||||
|
||||
fifo.parameter.flush();
|
||||
debugger.commandEpilogue(operation);
|
||||
}
|
||||
|
||||
@@ -58,6 +61,8 @@ auto Disc::commandTest() -> void {
|
||||
debugger.commandPrologue(operation, suboperation);
|
||||
|
||||
switch(suboperation) {
|
||||
case 0x04: commandTestStartReadSCEX(); break;
|
||||
case 0x05: commandTestStopReadSCEX(); break;
|
||||
case 0x20: commandTestControllerDate(); break;
|
||||
default: commandUnimplemented(operation, suboperation); break;
|
||||
}
|
||||
@@ -153,13 +158,34 @@ auto Disc::commandReadWithRetry() -> void {
|
||||
drive.seeking = 2 << drive.mode.speed;
|
||||
drive.lba.current = drive.lba.request;
|
||||
ssr.reading = 1;
|
||||
|
||||
ssr.playingCDDA = 0;
|
||||
fifo.response.write(status());
|
||||
|
||||
irq.acknowledge.flag = 1;
|
||||
irq.poll();
|
||||
}
|
||||
|
||||
//0x07
|
||||
auto Disc::commandMotorOn() -> void {
|
||||
if(event.invocation == 0) {
|
||||
event.invocation = 1;
|
||||
event.counter = 50'000;
|
||||
|
||||
fifo.response.write(status());
|
||||
|
||||
irq.acknowledge.flag = 1;
|
||||
irq.poll();
|
||||
return;
|
||||
}
|
||||
|
||||
if(event.invocation == 1) {
|
||||
fifo.response.write(status());
|
||||
irq.complete.flag = 1;
|
||||
irq.poll();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//0x08
|
||||
auto Disc::commandStop() -> void {
|
||||
if(event.invocation == 0) {
|
||||
@@ -398,22 +424,49 @@ auto Disc::commandGetTrackStart() -> void {
|
||||
|
||||
//0x15
|
||||
auto Disc::commandSeekData() -> void {
|
||||
drive.lba.current = drive.lba.request;
|
||||
if(event.invocation == 0) {
|
||||
event.invocation = 1;
|
||||
event.counter = 50'000; // TODO: Calculate seek time
|
||||
|
||||
fifo.response.write(status());
|
||||
fifo.response.write(status());
|
||||
|
||||
irq.complete.flag = 1;
|
||||
irq.poll();
|
||||
irq.acknowledge.flag = 1;
|
||||
irq.poll();
|
||||
return;
|
||||
}
|
||||
|
||||
if(event.invocation == 1) {
|
||||
drive.lba.current = drive.lba.request;
|
||||
ssr.playingCDDA = 0;
|
||||
|
||||
fifo.response.write(status());
|
||||
|
||||
irq.complete.flag = 1;
|
||||
irq.poll();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//0x16
|
||||
auto Disc::commandSeekCDDA() -> void {
|
||||
drive.lba.current = drive.lba.request;
|
||||
ssr.playingCDDA = 0;
|
||||
// TODO: use subchannel data to seek rather than LBA
|
||||
commandSeekData();
|
||||
}
|
||||
|
||||
//0x19 0x04
|
||||
auto Disc::commandTestStartReadSCEX() -> void {
|
||||
fifo.response.write(status());
|
||||
irq.acknowledge.flag = 1;
|
||||
irq.poll();
|
||||
}
|
||||
|
||||
irq.complete.flag = 1;
|
||||
//0x19 0x05
|
||||
auto Disc::commandTestStopReadSCEX() -> void {
|
||||
// Report no SCEX string found to appease mod-chip detection
|
||||
fifo.response.write(0);
|
||||
fifo.response.write(0);
|
||||
irq.acknowledge.flag = 1;
|
||||
irq.poll();
|
||||
}
|
||||
|
||||
@@ -539,6 +592,28 @@ auto Disc::commandReadWithoutRetry() -> void {
|
||||
return commandReadWithRetry();
|
||||
}
|
||||
|
||||
//0x1e
|
||||
auto Disc::commandReadToc() -> void {
|
||||
if(event.invocation == 0) {
|
||||
event.invocation = 1;
|
||||
event.counter = 475'000;
|
||||
|
||||
fifo.response.write(status());
|
||||
|
||||
irq.acknowledge.flag = 1;
|
||||
irq.poll();
|
||||
return;
|
||||
}
|
||||
|
||||
if(event.invocation == 1) {
|
||||
fifo.response.write(status());
|
||||
|
||||
irq.complete.flag = 1;
|
||||
irq.poll();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto Disc::commandUnimplemented(u8 operation, maybe<u8> suboperation) -> void {
|
||||
if(!suboperation) {
|
||||
debug(unimplemented, "Disc::command($", hex(operation, 2L), ")");
|
||||
|
||||
@@ -109,6 +109,10 @@ auto Disc::Debugger::commandPrologue(u8 operation, maybe<u8> suboperation) -> vo
|
||||
name = "ReadWithoutRetry";
|
||||
}
|
||||
|
||||
if(operation == 0x1e) {
|
||||
name = "ReadToc";
|
||||
}
|
||||
|
||||
if(!name) {
|
||||
if(!suboperation) name = {"$", hex(operation, 2L)};
|
||||
if( suboperation) name = {"Test$", hex(*suboperation, 2L)};
|
||||
|
||||
@@ -60,6 +60,7 @@ struct Disc : Thread, Memory::Interface {
|
||||
auto commandFastForward() -> void;
|
||||
auto commandRewind() -> void;
|
||||
auto commandReadWithRetry() -> void;
|
||||
auto commandMotorOn() -> void;
|
||||
auto commandStop() -> void;
|
||||
auto commandPause() -> void;
|
||||
auto commandInitialize() -> void;
|
||||
@@ -74,9 +75,12 @@ struct Disc : Thread, Memory::Interface {
|
||||
auto commandGetTrackStart() -> void;
|
||||
auto commandSeekData() -> void;
|
||||
auto commandSeekCDDA() -> void;
|
||||
auto commandTestStartReadSCEX() -> void;
|
||||
auto commandTestStopReadSCEX() -> void;
|
||||
auto commandTestControllerDate() -> void;
|
||||
auto commandGetID() -> void;
|
||||
auto commandReadWithoutRetry() -> void;
|
||||
auto commandReadToc() -> void;
|
||||
auto commandUnimplemented(u8, maybe<u8> = nothing) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
|
||||
@@ -1,32 +1,29 @@
|
||||
auto SuperFX::read(n24 address, n8 data) -> n8 {
|
||||
if((address & 0xc00000) == 0x000000) { //$00-3f:0000-7fff,:8000-ffff
|
||||
address = Bus::mirror(address, rom.size());
|
||||
while(!regs.scmr.ron) {
|
||||
step(6);
|
||||
synchronize(cpu);
|
||||
if(scheduler.synchronizing()) break;
|
||||
}
|
||||
return rom.read((((address & 0x3f0000) >> 1) | (address & 0x7fff)));
|
||||
return rom.read((((address & 0x3f0000) >> 1) | (address & 0x7fff)) & romMask);
|
||||
}
|
||||
|
||||
if((address & 0xe00000) == 0x400000) { //$40-5f:0000-ffff
|
||||
address = Bus::mirror(address, rom.size());
|
||||
while(!regs.scmr.ron) {
|
||||
step(6);
|
||||
synchronize(cpu);
|
||||
if(scheduler.synchronizing()) break;
|
||||
}
|
||||
return rom.read(address);
|
||||
return rom.read(address & romMask);
|
||||
}
|
||||
|
||||
if((address & 0xfe0000) == 0x700000) { //$70-71:0000-ffff
|
||||
address = Bus::mirror(address, ram.size());
|
||||
while(!regs.scmr.ran) {
|
||||
step(6);
|
||||
synchronize(cpu);
|
||||
if(scheduler.synchronizing()) break;
|
||||
}
|
||||
return ram.read(address);
|
||||
return ram.read(address & ramMask);
|
||||
}
|
||||
|
||||
return data;
|
||||
@@ -34,13 +31,12 @@ auto SuperFX::read(n24 address, n8 data) -> n8 {
|
||||
|
||||
auto SuperFX::write(n24 address, n8 data) -> void {
|
||||
if((address & 0xfe0000) == 0x700000) { //$70-71:0000-ffff
|
||||
address = Bus::mirror(address, ram.size());
|
||||
while(!regs.scmr.ran) {
|
||||
step(6);
|
||||
synchronize(cpu);
|
||||
if(scheduler.synchronizing()) break;
|
||||
}
|
||||
return ram.write(address, data);
|
||||
return ram.write(address & ramMask, data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,13 +44,27 @@ auto SuperFX::main() -> void {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: find a better place for this routine
|
||||
auto romSizeRound(u32 romSize) -> u32 {
|
||||
u32 count = 0;
|
||||
if (romSize && !(romSize & (romSize - 1))) return romSize;
|
||||
while(romSize != 0) {
|
||||
romSize >>= 1;
|
||||
count += 1;
|
||||
}
|
||||
return 1 << count;
|
||||
}
|
||||
|
||||
auto SuperFX::power() -> void {
|
||||
GSU::power();
|
||||
|
||||
Thread::create(Frequency, {&SuperFX::main, this});
|
||||
cpu.coprocessors.append(this);
|
||||
|
||||
romMask = rom.size() - 1;
|
||||
// SuperFX voxel demo has a non power-of-two rom
|
||||
// resulting in an incorrect/broken rom mask
|
||||
// fix this by ensuring romSize is a power of two
|
||||
romMask = romSizeRound(rom.size()) - 1;
|
||||
ramMask = ram.size() - 1;
|
||||
bramMask = bram.size() - 1;
|
||||
|
||||
|
||||
@@ -47,7 +47,10 @@ auto CPU::step(u32 clocks) -> void {
|
||||
|
||||
Thread::step(clocks);
|
||||
for(auto peripheral : peripherals) Thread::synchronize(*peripheral);
|
||||
|
||||
#if !defined(PROFILE_PERFORMANCE)
|
||||
for(auto coprocessor : coprocessors) Thread::synchronize(*coprocessor);
|
||||
#endif
|
||||
}
|
||||
|
||||
//called by ppu.tick() when Hcounter=0
|
||||
|
||||
Loaded 100 of 119 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user