Update to ares v114r17 release.
- PlayStation: started implementing timers - MSX: added more cartridge mappers (Konami SCC, ASCII 8kbit, ASCII 16kbit, etc)
このコミットが含まれているのは:
コミット
5f035496bb
@ -2,7 +2,7 @@
|
||||
|
||||
namespace ares {
|
||||
static const string Name = "ares";
|
||||
static const string Version = "114.16";
|
||||
static const string Version = "114.17";
|
||||
static const string Copyright = "ares team";
|
||||
static const string License = "BY-NC-ND 4.0";
|
||||
static const string LicenseURI = "https://creativecommons.org/licenses/by-nc-nd/4.0/";
|
||||
|
43
ares/msx/cartridge/board/asc16.cpp
ノーマルファイル
43
ares/msx/cartridge/board/asc16.cpp
ノーマルファイル
@ -0,0 +1,43 @@
|
||||
//ASCII 16kbit
|
||||
|
||||
struct ASC16 : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<uint8> rom;
|
||||
|
||||
auto load(Markup::Node document) -> void override {
|
||||
auto board = document["game/board"];
|
||||
Interface::load(rom, board["memory(type=ROM,content=Program)"]);
|
||||
}
|
||||
|
||||
auto save(Markup::Node document) -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(uint16 address, uint8 data) -> uint8 override {
|
||||
if(address >= 0x4000 && address <= 0x7fff) data = rom.read(bank[0] << 14 | (uint14)address);
|
||||
if(address >= 0x8000 && address <= 0xbfff) data = rom.read(bank[1] << 14 | (uint14)address);
|
||||
|
||||
if(address >= 0xc000 && address <= 0xffff) data = rom.read(bank[0] << 14 | (uint14)address);
|
||||
if(address >= 0x0000 && address <= 0x3fff) data = rom.read(bank[1] << 14 | (uint14)address);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
auto write(uint16 address, uint8 data) -> void override {
|
||||
if(address >= 0x6000 && address <= 0x6fff) bank[0] = data;
|
||||
if(address >= 0x7000 && address <= 0xbfff) bank[1] = data;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
bank[0] = 0x0f; //R-Type = 0x0f; others = 0x00
|
||||
bank[1] = 0x00;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s.array(bank);
|
||||
}
|
||||
|
||||
uint8 bank[2];
|
||||
};
|
51
ares/msx/cartridge/board/asc8.cpp
ノーマルファイル
51
ares/msx/cartridge/board/asc8.cpp
ノーマルファイル
@ -0,0 +1,51 @@
|
||||
//ASCII 8kbit
|
||||
|
||||
struct ASC8 : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<uint8> rom;
|
||||
|
||||
auto load(Markup::Node document) -> void override {
|
||||
auto board = document["game/board"];
|
||||
Interface::load(rom, board["memory(type=ROM,content=Program)"]);
|
||||
}
|
||||
|
||||
auto save(Markup::Node document) -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(uint16 address, uint8 data) -> uint8 override {
|
||||
if(address >= 0x4000 && address <= 0x5fff) data = rom.read(bank[0] << 13 | (uint13)address);
|
||||
if(address >= 0x6000 && address <= 0x7fff) data = rom.read(bank[1] << 13 | (uint13)address);
|
||||
if(address >= 0x8000 && address <= 0x9fff) data = rom.read(bank[2] << 13 | (uint13)address);
|
||||
if(address >= 0xa000 && address <= 0xbfff) data = rom.read(bank[3] << 13 | (uint13)address);
|
||||
|
||||
if(address >= 0xc000 && address <= 0xdfff) data = rom.read(bank[0] << 13 | (uint13)address);
|
||||
if(address >= 0xe000 && address <= 0xffff) data = rom.read(bank[1] << 13 | (uint13)address);
|
||||
if(address >= 0x0000 && address <= 0x1fff) data = rom.read(bank[2] << 13 | (uint13)address);
|
||||
if(address >= 0x2000 && address <= 0x3fff) data = rom.read(bank[3] << 13 | (uint13)address);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
auto write(uint16 address, uint8 data) -> void override {
|
||||
if(address >= 0x6000 && address <= 0x67ff) bank[0] = data;
|
||||
if(address >= 0x6800 && address <= 0x6fff) bank[1] = data;
|
||||
if(address >= 0x7000 && address <= 0x77ff) bank[2] = data;
|
||||
if(address >= 0x7800 && address <= 0x7fff) bank[3] = data;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
bank[0] = 0;
|
||||
bank[1] = 0;
|
||||
bank[2] = 0;
|
||||
bank[3] = 0;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s.array(bank);
|
||||
}
|
||||
|
||||
uint8 bank[4];
|
||||
};
|
50
ares/msx/cartridge/board/board.cpp
ノーマルファイル
50
ares/msx/cartridge/board/board.cpp
ノーマルファイル
@ -0,0 +1,50 @@
|
||||
namespace Board {
|
||||
|
||||
#include "asc16.cpp"
|
||||
#include "asc8.cpp"
|
||||
#include "cross-blaim.cpp"
|
||||
#include "konami.cpp"
|
||||
#include "konami-scc.cpp"
|
||||
#include "linear.cpp"
|
||||
#include "super-lode-runner.cpp"
|
||||
#include "super-pierrot.cpp"
|
||||
|
||||
auto Interface::load(Memory::Readable<uint8>& memory, Markup::Node node) -> bool {
|
||||
if(!node) return false;
|
||||
memory.allocate(node["size"].natural());
|
||||
auto name = string{node["content"].string(), ".", node["type"].string()}.downcase();
|
||||
if(auto fp = platform->open(cartridge.node, name, File::Read, File::Required)) {
|
||||
memory.load(fp);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto Interface::load(Memory::Writable<uint8>& memory, Markup::Node node) -> bool {
|
||||
if(!node) return false;
|
||||
memory.allocate(node["size"].natural());
|
||||
if(node["volatile"]) return true;
|
||||
auto name = string{node["content"].string(), ".", node["type"].string()}.downcase();
|
||||
if(auto fp = platform->open(cartridge.node, name, File::Read)) {
|
||||
memory.load(fp);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto Interface::save(Memory::Writable<uint8>& memory, Markup::Node node) -> bool {
|
||||
if(!node) return false;
|
||||
if(node["volatile"]) return true;
|
||||
auto name = string{node["content"].string(), ".", node["type"].string()}.downcase();
|
||||
if(auto fp = platform->open(cartridge.node, name, File::Write)) {
|
||||
memory.save(fp);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto Interface::main() -> void {
|
||||
cartridge.step(system.colorburst());
|
||||
}
|
||||
|
||||
}
|
22
ares/msx/cartridge/board/board.hpp
ノーマルファイル
22
ares/msx/cartridge/board/board.hpp
ノーマルファイル
@ -0,0 +1,22 @@
|
||||
namespace Board {
|
||||
|
||||
struct Interface {
|
||||
Interface(Cartridge& cartridge) : cartridge(cartridge) {}
|
||||
virtual ~Interface() = default;
|
||||
virtual auto load(Markup::Node) -> void {}
|
||||
virtual auto save(Markup::Node) -> void {}
|
||||
virtual auto unload() -> void {}
|
||||
virtual auto main() -> void;
|
||||
virtual auto read(uint16 address, uint8 data) -> uint8 { return data; }
|
||||
virtual auto write(uint16 address, uint8 data) -> void {}
|
||||
virtual auto power() -> void {}
|
||||
virtual auto serialize(serializer&) -> void {}
|
||||
|
||||
auto load(Memory::Readable<uint8>&, Markup::Node) -> bool;
|
||||
auto load(Memory::Writable<uint8>&, Markup::Node) -> bool;
|
||||
auto save(Memory::Writable<uint8>&, Markup::Node) -> bool;
|
||||
|
||||
Cartridge& cartridge;
|
||||
};
|
||||
|
||||
}
|
56
ares/msx/cartridge/board/cross-blaim.cpp
ノーマルファイル
56
ares/msx/cartridge/board/cross-blaim.cpp
ノーマルファイル
@ -0,0 +1,56 @@
|
||||
//Cross Blaim
|
||||
|
||||
struct CrossBlaim : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<uint8> rom;
|
||||
|
||||
auto load(Markup::Node document) -> void override {
|
||||
auto board = document["game/board"];
|
||||
Interface::load(rom, board["memory(type=ROM,content=Program)"]);
|
||||
}
|
||||
|
||||
auto save(Markup::Node document) -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(uint16 address, uint8 data) -> uint8 override {
|
||||
if(mode == 0 || mode == 1) {
|
||||
if(address >> 14 == 0) data = rom.read((uint14)address + 16_KiB);
|
||||
if(address >> 14 == 1) data = rom.read((uint14)address);
|
||||
if(address >> 14 == 2) data = rom.read((uint14)address + 16_KiB);
|
||||
if(address >> 14 == 3) data = rom.read((uint14)address + 16_KiB);
|
||||
}
|
||||
|
||||
if(mode == 2) {
|
||||
if(address >> 14 == 0) data = 0xff;
|
||||
if(address >> 14 == 1) data = rom.read((uint14)address);
|
||||
if(address >> 14 == 2) data = rom.read((uint14)address + 32_KiB);
|
||||
if(address >> 14 == 3) data = 0xff;
|
||||
}
|
||||
|
||||
if(mode == 3) {
|
||||
if(address >> 14 == 0) data = 0xff;
|
||||
if(address >> 14 == 1) data = rom.read((uint14)address);
|
||||
if(address >> 14 == 2) data = rom.read((uint14)address + 48_KiB);
|
||||
if(address >> 14 == 3) data = 0xff;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
auto write(uint16 address, uint8 data) -> void override {
|
||||
mode = data;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
mode = 0;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s.integer(mode);
|
||||
}
|
||||
|
||||
uint2 mode;
|
||||
};
|
252
ares/msx/cartridge/board/konami-scc.cpp
ノーマルファイル
252
ares/msx/cartridge/board/konami-scc.cpp
ノーマルファイル
@ -0,0 +1,252 @@
|
||||
//Konami (with Sound Creative Chip)
|
||||
|
||||
struct KonamiSCC : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<uint8> rom;
|
||||
Node::Stream stream;
|
||||
|
||||
auto load(Markup::Node document) -> void override {
|
||||
auto board = document["game/board"];
|
||||
Interface::load(rom, board["memory(type=ROM,content=Program)"]);
|
||||
|
||||
stream = cartridge.node->append<Node::Stream>("SCC");
|
||||
stream->setChannels(1);
|
||||
stream->setFrequency(system.colorburst() / 16.0);
|
||||
stream->addHighPassFilter(20.0, 1);
|
||||
}
|
||||
|
||||
auto save(Markup::Node document) -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto main() -> void override {
|
||||
int sample = 256 * 5;
|
||||
|
||||
for(auto& voice : voices) {
|
||||
if(voice.frequency <= 8) continue; //voice is halted when frequency < 9
|
||||
|
||||
voice.clock += 32;
|
||||
while(voice.clock > voice.frequency) {
|
||||
voice.clock -= voice.frequency + 1;
|
||||
voice.counter++;
|
||||
}
|
||||
sample += voice.wave[voice.counter] * voice.volume * voice.key >> 3;
|
||||
}
|
||||
|
||||
stream->sample(mixer[sample] / 32768.0);
|
||||
cartridge.step(16);
|
||||
}
|
||||
|
||||
auto read(uint16 address, uint8 data) -> uint8 override {
|
||||
if(address >= 0x4000 && address <= 0x5fff) data = rom.read(bank[0] << 13 | (uint13)address);
|
||||
if(address >= 0x6000 && address <= 0x7fff) data = rom.read(bank[1] << 13 | (uint13)address);
|
||||
if(address >= 0x8000 && address <= 0x9fff) data = rom.read(bank[2] << 13 | (uint13)address);
|
||||
if(address >= 0xa000 && address <= 0xbfff) data = rom.read(bank[3] << 13 | (uint13)address);
|
||||
|
||||
if(address >= 0xc000 && address <= 0xdfff) data = rom.read(bank[0] << 13 | (uint13)address);
|
||||
if(address >= 0xe000 && address <= 0xffff) data = rom.read(bank[1] << 13 | (uint13)address);
|
||||
if(address >= 0x0000 && address <= 0x1fff) data = rom.read(bank[2] << 13 | (uint13)address);
|
||||
if(address >= 0x2000 && address <= 0x3fff) data = rom.read(bank[3] << 13 | (uint13)address);
|
||||
|
||||
address.bit(8) = 0; //SCC ignores A8
|
||||
|
||||
if(address >= 0x9800 && address <= 0x981f) {
|
||||
data = voices[0].wave[address & 0x1f];
|
||||
if(test.bit(6)) data = voices[0].wave[address + voices[0].counter & 0x1f];
|
||||
}
|
||||
|
||||
if(address >= 0x9820 && address <= 0x983f) {
|
||||
data = voices[1].wave[address & 0x1f];
|
||||
if(test.bit(6)) data = voices[1].wave[address + voices[1].counter & 0x1f];
|
||||
}
|
||||
|
||||
if(address >= 0x9840 && address <= 0x985f) {
|
||||
data = voices[2].wave[address & 0x1f];
|
||||
if(test.bit(6)) data = voices[2].wave[address + voices[2].counter & 0x1f];
|
||||
}
|
||||
|
||||
if(address >= 0x9860 && address <= 0x987f) {
|
||||
data = voices[3].wave[address & 0x1f];
|
||||
if(test.bit(6) || test.bit(7)) data = voices[3].wave[address + voices[3 + test.bit(6)].counter & 0x1f];
|
||||
}
|
||||
|
||||
if(address >= 0x9880 && address <= 0x98df) {
|
||||
data = 0xff;
|
||||
}
|
||||
|
||||
if(address >= 0x98e0 && address <= 0x98ff) {
|
||||
//reading the test register sets it to 0xff
|
||||
data = test = 0xff;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
auto write(uint16 address, uint8 data) -> void override {
|
||||
if(address >= 0x5000 && address <= 0x57ff) bank[0] = data;
|
||||
if(address >= 0x7000 && address <= 0x77ff) bank[1] = data;
|
||||
if(address >= 0x9000 && address <= 0x97ff) bank[2] = data;
|
||||
if(address >= 0xb000 && address <= 0xb7ff) bank[3] = data;
|
||||
|
||||
address.bit(8) = 0; //SCC ignores A8
|
||||
|
||||
if(address >= 0x9800 && address <= 0x981f && !test.bit(6)) {
|
||||
voices[0].wave[address & 0x1f] = data;
|
||||
}
|
||||
|
||||
if(address >= 0x9820 && address <= 0x983f && !test.bit(6)) {
|
||||
voices[1].wave[address & 0x1f] = data;
|
||||
}
|
||||
|
||||
if(address >= 0x9840 && address <= 0x985f && !test.bit(6)) {
|
||||
voices[2].wave[address & 0x1f] = data;
|
||||
}
|
||||
|
||||
if(address >= 0x9860 && address <= 0x987f && !test.bit(6) && !test.bit(7)) {
|
||||
voices[3].wave[address & 0x1f] = data;
|
||||
voices[4].wave[address & 0x1f] = data; //shares data with wave channel 3
|
||||
}
|
||||
|
||||
if(address == 0x9880) {
|
||||
voices[0].frequency.bit(0, 7) = data.bit(0,7);
|
||||
if(voices[0].frequency < 9) voices[0].clock = 0;
|
||||
if(test.bit(5)) voices[0].clock = 0, voices[0].counter = 0;
|
||||
}
|
||||
|
||||
if(address == 0x9881) {
|
||||
voices[0].frequency.bit(8,11) = data.bit(0,3);
|
||||
if(voices[0].frequency < 9) voices[0].clock = 0;
|
||||
if(test.bit(5)) voices[0].clock = 0, voices[0].counter = 0;
|
||||
}
|
||||
|
||||
if(address == 0x9882) {
|
||||
voices[1].frequency.bit(0, 7) = data.bit(0,7);
|
||||
if(voices[1].frequency < 9) voices[1].clock = 0;
|
||||
if(test.bit(5)) voices[1].clock = 0, voices[1].counter = 0;
|
||||
}
|
||||
|
||||
if(address == 0x9883) {
|
||||
voices[1].frequency.bit(8,11) = data.bit(0,3);
|
||||
if(voices[1].frequency < 9) voices[1].clock = 0;
|
||||
if(test.bit(5)) voices[1].clock = 0, voices[1].counter = 0;
|
||||
}
|
||||
|
||||
if(address == 0x9884) {
|
||||
voices[2].frequency.bit(0, 7) = data.bit(0,7);
|
||||
if(voices[2].frequency < 9) voices[2].clock = 0;
|
||||
if(test.bit(5)) voices[2].clock = 0, voices[2].counter = 0;
|
||||
}
|
||||
|
||||
if(address == 0x9885) {
|
||||
voices[2].frequency.bit(8,11) = data.bit(0,3);
|
||||
if(voices[2].frequency < 9) voices[2].clock = 0;
|
||||
if(test.bit(5)) voices[2].clock = 0, voices[2].counter = 0;
|
||||
}
|
||||
|
||||
if(address == 0x9886) {
|
||||
voices[3].frequency.bit(0, 7) = data.bit(0,7);
|
||||
if(voices[3].frequency < 9) voices[3].clock = 0;
|
||||
if(test.bit(5)) voices[3].clock = 0, voices[3].counter = 0;
|
||||
}
|
||||
|
||||
if(address == 0x9887) {
|
||||
voices[3].frequency.bit(8,11) = data.bit(0,3);
|
||||
if(voices[3].frequency < 9) voices[3].clock = 0;
|
||||
if(test.bit(5)) voices[3].clock = 0, voices[3].counter = 0;
|
||||
}
|
||||
|
||||
if(address == 0x9888) {
|
||||
voices[4].frequency.bit(0, 7) = data.bit(0,7);
|
||||
if(voices[4].frequency < 9) voices[4].clock = 0;
|
||||
if(test.bit(5)) voices[4].clock = 0, voices[4].counter = 0;
|
||||
}
|
||||
|
||||
if(address == 0x9889) {
|
||||
voices[4].frequency.bit(8,11) = data.bit(0,3);
|
||||
if(voices[4].frequency < 9) voices[4].clock = 0;
|
||||
if(test.bit(5)) voices[4].clock = 0, voices[4].counter = 0;
|
||||
}
|
||||
|
||||
if(address == 0x988a) {
|
||||
voices[0].volume = data.bit(0,3);
|
||||
}
|
||||
|
||||
if(address == 0x988b) {
|
||||
voices[1].volume = data.bit(0,3);
|
||||
}
|
||||
|
||||
if(address == 0x988c) {
|
||||
voices[2].volume = data.bit(0,3);
|
||||
}
|
||||
|
||||
if(address == 0x988d) {
|
||||
voices[3].volume = data.bit(0,3);
|
||||
}
|
||||
|
||||
if(address == 0x988e) {
|
||||
voices[4].volume = data.bit(0,3);
|
||||
}
|
||||
|
||||
if(address == 0x988f) {
|
||||
voices[0].key = data.bit(0);
|
||||
voices[1].key = data.bit(1);
|
||||
voices[2].key = data.bit(2);
|
||||
voices[3].key = data.bit(3);
|
||||
voices[4].key = data.bit(4);
|
||||
}
|
||||
|
||||
if(address >= 0x98e0 && address <= 0x98ff) test = data;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
bank[0] = 0;
|
||||
bank[1] = 1;
|
||||
bank[2] = 2;
|
||||
bank[3] = 3;
|
||||
voices[0] = {};
|
||||
voices[1] = {};
|
||||
voices[2] = {};
|
||||
voices[3] = {};
|
||||
voices[4] = {};
|
||||
test = 0;
|
||||
|
||||
mixer.resize(512 * 5);
|
||||
auto table = mixer.data() + 256 * 5;
|
||||
for(int n : range(256 * 5)) {
|
||||
int16 volume = n * 8 * 16 / 5;
|
||||
table[+n] = +volume;
|
||||
table[-n] = -volume;
|
||||
}
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s.array(bank);
|
||||
for(auto& voice : voices) {
|
||||
s.integer(voice.clock);
|
||||
s.integer(voice.frequency);
|
||||
s.integer(voice.counter);
|
||||
s.integer(voice.volume);
|
||||
s.integer(voice.key);
|
||||
s.array(voice.wave);
|
||||
}
|
||||
s.integer(test);
|
||||
}
|
||||
|
||||
uint8 bank[4];
|
||||
|
||||
struct Voice {
|
||||
uint16 clock;
|
||||
uint12 frequency;
|
||||
uint5 counter;
|
||||
uint4 volume;
|
||||
uint1 key;
|
||||
int8 wave[32];
|
||||
} voices[5];
|
||||
|
||||
uint8 test;
|
||||
|
||||
//unserialized:
|
||||
vector<int16> mixer;
|
||||
};
|
45
ares/msx/cartridge/board/konami.cpp
ノーマルファイル
45
ares/msx/cartridge/board/konami.cpp
ノーマルファイル
@ -0,0 +1,45 @@
|
||||
//Konami (without Sound Creative Chip)
|
||||
|
||||
struct Konami : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<uint8> rom;
|
||||
|
||||
auto load(Markup::Node document) -> void override {
|
||||
auto board = document["game/board"];
|
||||
Interface::load(rom, board["memory(type=ROM,content=Program)"]);
|
||||
}
|
||||
|
||||
auto save(Markup::Node document) -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(uint16 address, uint8 data) -> uint8 override {
|
||||
if(address >> 13 == 2) data = rom.read(bank[0] << 13 | (uint13)address);
|
||||
if(address >> 13 == 3) data = rom.read(bank[1] << 13 | (uint13)address);
|
||||
if(address >> 13 == 4) data = rom.read(bank[2] << 13 | (uint13)address);
|
||||
if(address >> 13 == 5) data = rom.read(bank[3] << 13 | (uint13)address);
|
||||
return data;
|
||||
}
|
||||
|
||||
auto write(uint16 address, uint8 data) -> void override {
|
||||
if(address >> 13 == 2) bank[0] = 0;
|
||||
if(address >> 13 == 3) bank[1] = data;
|
||||
if(address >> 13 == 4) bank[2] = data;
|
||||
if(address >> 13 == 5) bank[3] = data;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
bank[0] = 0;
|
||||
bank[1] = 1;
|
||||
bank[2] = 0;
|
||||
bank[3] = 0;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s.array(bank);
|
||||
}
|
||||
|
||||
uint8 bank[4];
|
||||
};
|
29
ares/msx/cartridge/board/linear.cpp
ノーマルファイル
29
ares/msx/cartridge/board/linear.cpp
ノーマルファイル
@ -0,0 +1,29 @@
|
||||
struct Linear : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<uint8> rom;
|
||||
|
||||
auto load(Markup::Node document) -> void override {
|
||||
auto board = document["game/board"];
|
||||
Interface::load(rom, board["memory(type=ROM,content=Program)"]);
|
||||
}
|
||||
|
||||
auto save(Markup::Node document) -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(uint16 address, uint8 data) -> uint8 override {
|
||||
data = rom.read(address);
|
||||
return data;
|
||||
}
|
||||
|
||||
auto write(uint16 address, uint8 data) -> void override {
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
}
|
||||
};
|
37
ares/msx/cartridge/board/super-lode-runner.cpp
ノーマルファイル
37
ares/msx/cartridge/board/super-lode-runner.cpp
ノーマルファイル
@ -0,0 +1,37 @@
|
||||
//Super Lode Runner
|
||||
//(not working: requires MSX BASIC)
|
||||
|
||||
struct SuperLodeRunner : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<uint8> rom;
|
||||
|
||||
auto load(Markup::Node document) -> void override {
|
||||
auto board = document["game/board"];
|
||||
Interface::load(rom, board["memory(type=ROM,content=Program)"]);
|
||||
}
|
||||
|
||||
auto save(Markup::Node document) -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(uint16 address, uint8 data) -> uint8 override {
|
||||
if(address >= 0x8000 && address <= 0xbfff) data = rom.read(bank << 14 | (uint14)address);
|
||||
return data;
|
||||
}
|
||||
|
||||
auto write(uint16 address, uint8 data) -> void override {
|
||||
if(address >= 0x0000 && address <= 0x3fff) bank = data;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
bank = 0;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s.integer(bank);
|
||||
}
|
||||
|
||||
uint8 bank;
|
||||
};
|
47
ares/msx/cartridge/board/super-pierrot.cpp
ノーマルファイル
47
ares/msx/cartridge/board/super-pierrot.cpp
ノーマルファイル
@ -0,0 +1,47 @@
|
||||
//Super Pierrot
|
||||
//(not working)
|
||||
|
||||
struct SuperPierrot : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<uint8> rom;
|
||||
|
||||
auto load(Markup::Node document) -> void override {
|
||||
auto board = document["game/board"];
|
||||
Interface::load(rom, board["memory(type=ROM,content=Program)"]);
|
||||
}
|
||||
|
||||
auto save(Markup::Node document) -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(uint16 address, uint8 data) -> uint8 override {
|
||||
if(address >= 0x4000 && address <= 0x7fff) data = rom.read(bank[0] << 14 | (uint14)address);
|
||||
if(address >= 0x8000 && address <= 0xbfff) data = rom.read(bank[1] << 14 | (uint14)address);
|
||||
return data;
|
||||
}
|
||||
|
||||
auto write(uint16 address, uint8 data) -> void override {
|
||||
if(address >= 0x4000 && address <= 0x4fff) bank[0] = data;
|
||||
if(address >= 0x6000 && address <= 0x6fff) bank[0] = data;
|
||||
if(address >= 0x8000 && address <= 0x8fff) bank[0] = data;
|
||||
if(address >= 0xa000 && address <= 0xafff) bank[0] = data;
|
||||
|
||||
if(address >= 0x5000 && address <= 0x5fff) bank[1] = data;
|
||||
if(address >= 0x7000 && address <= 0x7fff) bank[1] = data;
|
||||
if(address >= 0x9000 && address <= 0x9fff) bank[1] = data;
|
||||
if(address >= 0xa000 && address <= 0xafff) bank[1] = data;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
bank[0] = 0;
|
||||
bank[1] = 0;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s.array(bank);
|
||||
}
|
||||
|
||||
uint8 bank[2];
|
||||
};
|
@ -4,6 +4,7 @@ namespace ares::MSX {
|
||||
|
||||
Cartridge& cartridge = cartridgeSlot.cartridge;
|
||||
Cartridge& expansion = expansionSlot.cartridge;
|
||||
#include "board/board.cpp"
|
||||
#include "slot.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
@ -23,87 +24,56 @@ auto Cartridge::connect() -> void {
|
||||
auto document = BML::unserialize(information.manifest);
|
||||
information.name = document["game/label"].string();
|
||||
information.region = document["game/region"].string();
|
||||
information.board = document["game/board"].string();
|
||||
|
||||
if(auto memory = document["game/board/memory(type=ROM,content=Program)"]) {
|
||||
rom.allocate(memory["size"].natural());
|
||||
if(auto fp = platform->open(node, "program.rom", File::Read, File::Required)) {
|
||||
rom.load(fp);
|
||||
}
|
||||
}
|
||||
|
||||
if(auto memory = document["game/board/memory(type=RAM,content=Save)"]) {
|
||||
ram.allocate(memory["size"].natural());
|
||||
if(auto fp = platform->open(node, "save.ram", File::Read)) {
|
||||
ram.load(fp);
|
||||
}
|
||||
}
|
||||
if(information.board == "ASC16") board = new Board::ASC16{*this};
|
||||
if(information.board == "ASC8") board = new Board::ASC8{*this};
|
||||
if(information.board == "CrossBlaim") board = new Board::CrossBlaim{*this};
|
||||
if(information.board == "Konami") board = new Board::Konami{*this};
|
||||
if(information.board == "KonamiSCC") board = new Board::KonamiSCC{*this};
|
||||
if(information.board == "Linear") board = new Board::Linear{*this};
|
||||
if(information.board == "SuperLodeRunner") board = new Board::SuperLodeRunner{*this};
|
||||
if(information.board == "SuperPierrot") board = new Board::SuperPierrot{*this};
|
||||
if(!board) board = new Board::KonamiSCC{*this};
|
||||
board->load(document);
|
||||
|
||||
power();
|
||||
}
|
||||
|
||||
auto Cartridge::disconnect() -> void {
|
||||
if(!node) return;
|
||||
save();
|
||||
rom.reset();
|
||||
ram.reset();
|
||||
node = {};
|
||||
if(board) board->unload(), board.reset();
|
||||
node.reset();
|
||||
}
|
||||
|
||||
auto Cartridge::save() -> void {
|
||||
if(!node) return;
|
||||
auto document = BML::unserialize(information.manifest);
|
||||
board->save(document);
|
||||
}
|
||||
|
||||
if(auto memory = document["game/board/memory(type=RAM,content=Save)"]) {
|
||||
if(auto fp = platform->open(node, "save.ram", File::Write)) {
|
||||
ram.save(fp);
|
||||
}
|
||||
}
|
||||
auto Cartridge::main() -> void {
|
||||
if(board) return board->main();
|
||||
step(system.colorburst());
|
||||
}
|
||||
|
||||
auto Cartridge::step(uint clocks) -> void {
|
||||
Thread::step(clocks);
|
||||
Thread::synchronize(cpu);
|
||||
}
|
||||
|
||||
auto Cartridge::power() -> void {
|
||||
Thread::create(system.colorburst(), {&Cartridge::main, this});
|
||||
if(board) board->power();
|
||||
}
|
||||
|
||||
uint2 RB; //Cross Blaim
|
||||
uint8 B8K[4]={0,1,0,0}; //Konami 8K (non-SCC)
|
||||
|
||||
auto Cartridge::read(uint16 address) -> uint8 {
|
||||
if(!rom) return 0xff;
|
||||
|
||||
/*
|
||||
if(RB==0||RB==1){
|
||||
if(address>>14==0)return rom.read((uint14)address+16_KiB);
|
||||
if(address>>14==1)return rom.read((uint14)address);
|
||||
if(address>>14==2)return rom.read((uint14)address+16_KiB);
|
||||
if(address>>14==3)return rom.read((uint14)address+16_KiB);
|
||||
}
|
||||
if(RB==2){
|
||||
if(address>>14==0)return 0xff;
|
||||
if(address>>14==1)return rom.read((uint14)address);
|
||||
if(address>>14==2)return rom.read((uint14)address+32_KiB);
|
||||
if(address>>14==3)return 0xff;
|
||||
}
|
||||
if(RB==3){
|
||||
if(address>>14==0)return 0xff;
|
||||
if(address>>14==1)return rom.read((uint14)address);
|
||||
if(address>>14==2)return rom.read((uint14)address+48_KiB);
|
||||
if(address>>14==3)return 0xff;
|
||||
}
|
||||
*/
|
||||
|
||||
if(address>>13==2)return rom.read(B8K[0]<<13|(uint13)address);
|
||||
if(address>>13==3)return rom.read(B8K[1]<<13|(uint13)address);
|
||||
if(address>>13==4)return rom.read(B8K[2]<<13|(uint13)address);
|
||||
if(address>>13==5)return rom.read(B8K[3]<<13|(uint13)address);
|
||||
|
||||
return rom.read(address);
|
||||
if(board) return board->read(address, 0xff);
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto Cartridge::write(uint16 address, uint8 data) -> void {
|
||||
RB = data;
|
||||
if(address>>13==2)B8K[0]=0;
|
||||
if(address>>13==3)B8K[1]=data;
|
||||
if(address>>13==4)B8K[2]=data;
|
||||
if(address>>13==5)B8K[3]=data;
|
||||
if(board) return board->write(address, data);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
struct Cartridge {
|
||||
struct Cartridge;
|
||||
#include "board/board.hpp"
|
||||
|
||||
struct Cartridge : Thread {
|
||||
Node::Peripheral node;
|
||||
Memory::Readable<uint8> rom;
|
||||
Memory::Writable<uint8> ram;
|
||||
@ -13,6 +16,8 @@ struct Cartridge {
|
||||
auto disconnect() -> void;
|
||||
|
||||
auto save() -> void;
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
auto power() -> void;
|
||||
|
||||
auto read(uint16 address) -> uint8;
|
||||
@ -21,11 +26,14 @@ struct Cartridge {
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
unique_pointer<Board::Interface> board;
|
||||
|
||||
private:
|
||||
struct Information {
|
||||
string manifest;
|
||||
string name;
|
||||
string region;
|
||||
string board;
|
||||
} information;
|
||||
};
|
||||
|
||||
|
@ -1,3 +1,3 @@
|
||||
auto Cartridge::serialize(serializer& s) -> void {
|
||||
if(ram) s.array(ram.data(), ram.size());
|
||||
if(board) board->serialize(s);
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
ares.objects += ares-ps1-interface ares-ps1-memory ares-ps1-system
|
||||
ares.objects += ares-ps1-disc ares-ps1-cpu ares-ps1-gpu ares-ps1-spu
|
||||
ares.objects += ares-ps1-irq ares-ps1-dma ares-ps1-timer
|
||||
|
||||
$(object.path)/ares-ps1-interface.o: $(ares.path)/ps1/interface/interface.cpp
|
||||
$(object.path)/ares-ps1-memory.o: $(ares.path)/ps1/memory/memory.cpp
|
||||
@ -8,3 +9,6 @@ $(object.path)/ares-ps1-disc.o: $(ares.path)/ps1/disc/disc.cpp
|
||||
$(object.path)/ares-ps1-cpu.o: $(ares.path)/ps1/cpu/cpu.cpp
|
||||
$(object.path)/ares-ps1-gpu.o: $(ares.path)/ps1/gpu/gpu.cpp
|
||||
$(object.path)/ares-ps1-spu.o: $(ares.path)/ps1/spu/spu.cpp
|
||||
$(object.path)/ares-ps1-irq.o: $(ares.path)/ps1/irq/irq.cpp
|
||||
$(object.path)/ares-ps1-dma.o: $(ares.path)/ps1/dma/dma.cpp
|
||||
$(object.path)/ares-ps1-timer.o: $(ares.path)/ps1/timer/timer.cpp
|
||||
|
@ -7,7 +7,7 @@
|
||||
auto disassemble(u32 address, u32 instruction) -> string;
|
||||
template<typename... P> auto hint(P&&... p) const -> string;
|
||||
|
||||
bool showColors = false;
|
||||
bool showColors = true;
|
||||
bool showValues = true;
|
||||
|
||||
//private:
|
||||
|
@ -4,8 +4,6 @@ namespace ares::PlayStation {
|
||||
|
||||
CPU cpu;
|
||||
#include "core/core.cpp"
|
||||
#include "interrupt.cpp"
|
||||
#include "dma.cpp"
|
||||
#include "debugger.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
@ -28,10 +26,14 @@ auto CPU::main() -> void {
|
||||
}
|
||||
|
||||
auto CPU::step(uint clocks) -> void {
|
||||
gpu.clock -= clocks;
|
||||
spu.clock -= clocks;
|
||||
while(gpu.clock < 0) gpu.main();
|
||||
while(spu.clock < 0) spu.main();
|
||||
dma.step(clocks);
|
||||
timer.step(clocks);
|
||||
gpu.clock -= clocks;
|
||||
spu.clock -= clocks;
|
||||
disc.clock -= clocks;
|
||||
while(gpu.clock < 0) gpu.main();
|
||||
while(spu.clock < 0) spu.main();
|
||||
while(disc.clock < 0) disc.main();
|
||||
}
|
||||
|
||||
auto CPU::power(bool reset) -> void {
|
||||
@ -39,7 +41,6 @@ auto CPU::power(bool reset) -> void {
|
||||
powerCore(reset);
|
||||
ram.fill();
|
||||
cache.fill();
|
||||
dma.power(reset);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -37,97 +37,6 @@ struct CPU : Thread {
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
#include "core/core.hpp"
|
||||
|
||||
struct Interrupt {
|
||||
enum : uint { Vblank, GPU, CDROM, DMA, Timer0, Timer1, Timer2, Peripheral, SIO, SPU, PIO };
|
||||
|
||||
CPU& self;
|
||||
Interrupt(CPU& self) : self(self) {}
|
||||
|
||||
//interrupt.cpp
|
||||
auto poll() -> void;
|
||||
auto pulse(uint source) -> void;
|
||||
auto raise(uint source) -> void;
|
||||
auto lower(uint source) -> void;
|
||||
|
||||
auto readByte(u32 address) -> u8;
|
||||
auto readHalf(u32 address) -> u16;
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeByte(u32 address, u8 data) -> void;
|
||||
auto writeHalf(u32 address, u16 data) -> void;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
|
||||
struct Source {
|
||||
uint1 line = 0;
|
||||
uint1 stat = 0;
|
||||
uint1 mask = 0;
|
||||
};
|
||||
|
||||
uint1 line = 0;
|
||||
Source vblank;
|
||||
Source gpu;
|
||||
Source cdrom;
|
||||
Source dma;
|
||||
Source timer0;
|
||||
Source timer1;
|
||||
Source timer2;
|
||||
Source peripheral;
|
||||
Source sio;
|
||||
Source spu;
|
||||
Source pio;
|
||||
} interrupt{*this};
|
||||
|
||||
struct DMA {
|
||||
enum : uint { MDECIN, MDECOUT, GPU, CDROM, SPU, PIO, OTC }; //channels
|
||||
|
||||
CPU& self;
|
||||
DMA(CPU& self) : self(self) {}
|
||||
|
||||
//dma.cpp
|
||||
auto readByte(u32 address) -> u8;
|
||||
auto readHalf(u32 address) -> u16;
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeByte(u32 address, u8 data) -> void;
|
||||
auto writeHalf(u32 address, u16 data) -> void;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
auto transferLinear(uint c) -> void;
|
||||
auto transferLinked(uint c) -> void;
|
||||
auto pollIRQ() -> void;
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
struct IRQ {
|
||||
uint1 force;
|
||||
uint1 enable;
|
||||
uint1 flag;
|
||||
uint6 unknown;
|
||||
} irq;
|
||||
|
||||
struct Channel {
|
||||
//dma.cpp
|
||||
auto active() const -> bool;
|
||||
|
||||
uint1 masterEnable;
|
||||
uint3 priority;
|
||||
uint24 address;
|
||||
uint16 length;
|
||||
uint16 blocks;
|
||||
uint1 direction;
|
||||
uint1 step; //0 = increment; 1 = decrement
|
||||
uint2 synchronization;
|
||||
struct Chopping {
|
||||
uint1 enable;
|
||||
uint3 dmaWindow;
|
||||
uint3 cpuWindow;
|
||||
} chopping;
|
||||
uint1 enable;
|
||||
uint1 trigger;
|
||||
uint2 unknown;
|
||||
struct IRQ {
|
||||
uint1 enable;
|
||||
uint1 flag;
|
||||
} irq;
|
||||
} channel[7];
|
||||
} dma{*this};
|
||||
};
|
||||
|
||||
extern CPU cpu;
|
||||
|
@ -1,145 +0,0 @@
|
||||
auto CPU::Interrupt::poll() -> void {
|
||||
line = 0;
|
||||
line |= vblank.stat & vblank.mask;
|
||||
line |= gpu.stat & gpu.mask;
|
||||
line |= cdrom.stat & cdrom.mask;
|
||||
line |= dma.stat & dma.mask;
|
||||
line |= timer0.stat & timer0.mask;
|
||||
line |= timer1.stat & timer1.mask;
|
||||
line |= timer2.stat & timer2.mask;
|
||||
line |= peripheral.stat & peripheral.mask;
|
||||
line |= sio.stat & sio.mask;
|
||||
line |= spu.stat & spu.mask;
|
||||
line |= pio.stat & pio.mask;
|
||||
self.scc.cause.interruptPending.bit(2) = line;
|
||||
}
|
||||
|
||||
auto CPU::Interrupt::pulse(uint source) -> void {
|
||||
raise(source);
|
||||
lower(source);
|
||||
}
|
||||
|
||||
auto CPU::Interrupt::raise(uint source) -> void {
|
||||
if(source == 0 && !vblank.line) vblank.line = vblank.stat = 1;
|
||||
if(source == 1 && !gpu.line) gpu.line = gpu.stat = 1;
|
||||
if(source == 2 && !cdrom.line) cdrom.line = cdrom.stat = 1;
|
||||
if(source == 3 && !dma.line) dma.line = dma.stat = 1;
|
||||
if(source == 4 && !timer0.line) timer0.line = timer0.stat = 1;
|
||||
if(source == 5 && !timer1.line) timer1.line = timer1.stat = 1;
|
||||
if(source == 6 && !timer2.line) timer2.line = timer2.stat = 1;
|
||||
if(source == 7 && !peripheral.line) peripheral.line = peripheral.stat = 1;
|
||||
if(source == 8 && !sio.line) sio.line = sio.stat = 1;
|
||||
if(source == 9 && !spu.line) spu.line = spu.stat = 1;
|
||||
if(source == 10 && !pio.line) pio.line = pio.stat = 1;
|
||||
poll();
|
||||
}
|
||||
|
||||
auto CPU::Interrupt::lower(uint source) -> void {
|
||||
if(source == 0) vblank.line = 0;
|
||||
if(source == 1) gpu.line = 0;
|
||||
if(source == 2) cdrom.line = 0;
|
||||
if(source == 3) dma.line = 0;
|
||||
if(source == 4) timer0.line = 0;
|
||||
if(source == 5) timer1.line = 0;
|
||||
if(source == 6) timer2.line = 0;
|
||||
if(source == 7) peripheral.line = 0;
|
||||
if(source == 8) sio.line = 0;
|
||||
if(source == 9) spu.line = 0;
|
||||
if(source == 10) pio.line = 0;
|
||||
poll();
|
||||
}
|
||||
|
||||
auto CPU::Interrupt::readByte(u32 address) -> u8 {
|
||||
uint8 data = 0;
|
||||
print("* read byte ", hex(address, 8L), "\n");
|
||||
return data;
|
||||
}
|
||||
|
||||
auto CPU::Interrupt::readHalf(u32 address) -> u16 {
|
||||
uint16 data = 0;
|
||||
|
||||
//I_STAT
|
||||
if(address == 0x1f80'1070) {
|
||||
data.bit( 0) = vblank.stat;
|
||||
data.bit( 1) = gpu.stat;
|
||||
data.bit( 2) = cdrom.stat;
|
||||
data.bit( 3) = dma.stat;
|
||||
data.bit( 4) = timer0.stat;
|
||||
data.bit( 5) = timer1.stat;
|
||||
data.bit( 6) = timer2.stat;
|
||||
data.bit( 7) = peripheral.stat;
|
||||
data.bit( 8) = sio.stat;
|
||||
data.bit( 9) = spu.stat;
|
||||
data.bit(10) = pio.stat;
|
||||
}
|
||||
|
||||
//I_MASK
|
||||
if(address == 0x1f80'1074) {
|
||||
data.bit( 0) = vblank.mask;
|
||||
data.bit( 1) = gpu.mask;
|
||||
data.bit( 2) = cdrom.mask;
|
||||
data.bit( 3) = dma.mask;
|
||||
data.bit( 4) = timer0.mask;
|
||||
data.bit( 5) = timer1.mask;
|
||||
data.bit( 6) = timer2.mask;
|
||||
data.bit( 7) = peripheral.mask;
|
||||
data.bit( 8) = sio.mask;
|
||||
data.bit( 9) = spu.mask;
|
||||
data.bit(10) = pio.mask;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
auto CPU::Interrupt::readWord(u32 address) -> u32 {
|
||||
uint32 data = 0;
|
||||
data |= readHalf(address & ~3 | 0) << 0;
|
||||
data |= readHalf(address & ~3 | 2) << 16;
|
||||
return data;
|
||||
}
|
||||
|
||||
auto CPU::Interrupt::writeByte(u32 address, u8 value) -> void {
|
||||
uint8 data = value;
|
||||
print("* write byte ", hex(address, 8L), " = ", hex(value, 2L), "\n");
|
||||
}
|
||||
|
||||
auto CPU::Interrupt::writeHalf(u32 address, u16 value) -> void {
|
||||
uint16 data = value;
|
||||
|
||||
//I_STAT
|
||||
if(address == 0x1f80'1070) {
|
||||
if(!data.bit( 0)) vblank.stat = 0;
|
||||
if(!data.bit( 1)) gpu.stat = 0;
|
||||
if(!data.bit( 2)) cdrom.stat = 0;
|
||||
if(!data.bit( 3)) dma.stat = 0;
|
||||
if(!data.bit( 4)) timer0.stat = 0;
|
||||
if(!data.bit( 5)) timer1.stat = 0;
|
||||
if(!data.bit( 6)) timer2.stat = 0;
|
||||
if(!data.bit( 7)) peripheral.stat = 0;
|
||||
if(!data.bit( 8)) sio.stat = 0;
|
||||
if(!data.bit( 9)) spu.stat = 0;
|
||||
if(!data.bit(10)) pio.stat = 0;
|
||||
poll();
|
||||
}
|
||||
|
||||
//I_MASK
|
||||
if(address == 0x1f80'1074) {
|
||||
vblank.mask = data.bit( 0);
|
||||
gpu.mask = data.bit( 1);
|
||||
cdrom.mask = data.bit( 2);
|
||||
dma.mask = data.bit( 3);
|
||||
timer0.mask = data.bit( 4);
|
||||
timer1.mask = data.bit( 5);
|
||||
timer2.mask = data.bit( 6);
|
||||
peripheral.mask = data.bit( 7);
|
||||
sio.mask = data.bit( 8);
|
||||
spu.mask = data.bit( 9);
|
||||
pio.mask = data.bit(10);
|
||||
poll();
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::Interrupt::writeWord(u32 address, u32 data) -> void {
|
||||
writeHalf(address & ~3 | 0, data >> 0);
|
||||
writeHalf(address & ~3 | 2, data >> 16);
|
||||
}
|
@ -3,7 +3,10 @@ auto Disc::command(u8 operation) -> void {
|
||||
|
||||
switch(operation) {
|
||||
case 0x01: return commandGetStatus();
|
||||
case 0x13: return commandGetFirstAndLastTrackNumbers();
|
||||
case 0x14: return commandGetTrackStart();
|
||||
case 0x19: return commandTest();
|
||||
case 0x1a: return commandGetID();
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,8 +24,25 @@ auto Disc::commandGetStatus() -> void {
|
||||
fifo.response.write(data);
|
||||
ssr.shellOpen = 0;
|
||||
|
||||
irq.acknowledge.flag = 1;
|
||||
irq.poll();
|
||||
irq.acknowledge.delay = 3;
|
||||
}
|
||||
|
||||
//0x13
|
||||
auto Disc::commandGetFirstAndLastTrackNumbers() -> void {
|
||||
fifo.response.write(0x00);
|
||||
fifo.response.write(0x01);
|
||||
fifo.response.write(0x01);
|
||||
|
||||
irq.acknowledge.delay = 3;
|
||||
}
|
||||
|
||||
//0x14
|
||||
auto Disc::commandGetTrackStart() -> void {
|
||||
fifo.response.write(0x00);
|
||||
fifo.response.write(0x00);
|
||||
fifo.response.write(0x00);
|
||||
|
||||
irq.acknowledge.delay = 3;
|
||||
}
|
||||
|
||||
//0x19
|
||||
@ -40,6 +60,22 @@ auto Disc::commandTestControllerDate() -> void {
|
||||
fifo.response.write(0x16);
|
||||
fifo.response.write(0xc1);
|
||||
|
||||
irq.acknowledge.flag = 1;
|
||||
irq.poll();
|
||||
irq.acknowledge.delay = 3;
|
||||
}
|
||||
|
||||
//0x1a
|
||||
auto Disc::commandGetID() -> void {
|
||||
/*
|
||||
fifo.response.write(0x0a);
|
||||
fifo.response.write(0x90);
|
||||
fifo.response.write(0x00);
|
||||
fifo.response.write(0x00);
|
||||
fifo.response.write(0x00);
|
||||
fifo.response.write(0x00);
|
||||
fifo.response.write(0x00);
|
||||
fifo.response.write(0x00);
|
||||
*/
|
||||
|
||||
irq.acknowledge.delay = 3;
|
||||
irq.error.delay = 3;
|
||||
}
|
||||
|
@ -69,6 +69,22 @@ auto Disc::disconnect() -> void {
|
||||
}
|
||||
|
||||
auto Disc::main() -> void {
|
||||
if(irq.acknowledge.delay && !--irq.acknowledge.delay) {
|
||||
irq.acknowledge.flag = 1;
|
||||
irq.poll();
|
||||
} else if(irq.error.delay && !--irq.error.delay) {
|
||||
fifo.response.write(0x0a);
|
||||
fifo.response.write(0x90);
|
||||
fifo.response.write(0x00);
|
||||
fifo.response.write(0x00);
|
||||
fifo.response.write(0x00);
|
||||
fifo.response.write(0x00);
|
||||
fifo.response.write(0x00);
|
||||
fifo.response.write(0x00);
|
||||
|
||||
irq.error.flag = 1;
|
||||
irq.poll();
|
||||
}
|
||||
step(33'868'800 / 75);
|
||||
}
|
||||
|
||||
@ -76,7 +92,7 @@ auto Disc::step(uint clocks) -> void {
|
||||
Thread::clock += clocks;
|
||||
}
|
||||
|
||||
auto Disc::power() -> void {
|
||||
auto Disc::power(bool reset) -> void {
|
||||
Thread::reset();
|
||||
io = {};
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ struct Disc : Thread {
|
||||
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
auto power() -> void;
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readByte(u32 address) -> u32;
|
||||
@ -31,8 +31,11 @@ struct Disc : Thread {
|
||||
//command.cpp
|
||||
auto command(u8 operation) -> void;
|
||||
auto commandGetStatus() -> void;
|
||||
auto commandGetFirstAndLastTrackNumbers() -> void;
|
||||
auto commandGetTrackStart() -> void;
|
||||
auto commandTest() -> void;
|
||||
auto commandTestControllerDate() -> void;
|
||||
auto commandGetID() -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
@ -42,25 +45,26 @@ struct Disc : Thread {
|
||||
string name;
|
||||
} information;
|
||||
|
||||
struct Interrupt {
|
||||
uint1 enable;
|
||||
uint1 flag;
|
||||
};
|
||||
|
||||
struct IRQ {
|
||||
//irq.cpp
|
||||
auto poll() -> void;
|
||||
|
||||
struct Source {
|
||||
uint1 enable;
|
||||
uint1 flag;
|
||||
uint32 delay;
|
||||
};
|
||||
|
||||
uint5 flag;
|
||||
uint5 mask;
|
||||
|
||||
Interrupt ready; //INT1
|
||||
Interrupt complete; //INT2
|
||||
Interrupt acknowledge; //INT3
|
||||
Interrupt end; //INT4
|
||||
Interrupt error; //INT5
|
||||
Interrupt unknown; //INT8
|
||||
Interrupt start; //INT10
|
||||
Source ready; //INT1
|
||||
Source complete; //INT2
|
||||
Source acknowledge; //INT3
|
||||
Source end; //INT4
|
||||
Source error; //INT5
|
||||
Source unknown; //INT8
|
||||
Source start; //INT10
|
||||
} irq;
|
||||
|
||||
struct FIFO {
|
||||
|
@ -6,6 +6,6 @@ auto Disc::IRQ::poll() -> void {
|
||||
pending |= end.flag & end.enable;
|
||||
pending |= error.flag & error.enable;
|
||||
|
||||
if(pending == 0) cpu.interrupt.lower(CPU::Interrupt::CDROM);
|
||||
if(pending == 1) cpu.interrupt.raise(CPU::Interrupt::CDROM), print("* IRQ\n");
|
||||
if(pending == 0) interrupt.lower(Interrupt::CDROM);
|
||||
if(pending == 1) interrupt.raise(Interrupt::CDROM), print("* IRQ\n");
|
||||
}
|
||||
|
36
ares/ps1/dma/dma.cpp
ノーマルファイル
36
ares/ps1/dma/dma.cpp
ノーマルファイル
@ -0,0 +1,36 @@
|
||||
#include <ps1/ps1.hpp>
|
||||
|
||||
namespace ares::PlayStation {
|
||||
|
||||
DMA dma;
|
||||
#include "io.cpp"
|
||||
#include "transfer.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto DMA::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Component>("DMA");
|
||||
}
|
||||
|
||||
auto DMA::unload() -> void {
|
||||
node.reset();
|
||||
}
|
||||
|
||||
auto DMA::step(uint clocks) -> void {
|
||||
}
|
||||
|
||||
auto DMA::pollIRQ() -> void {
|
||||
irq.flag = irq.force;
|
||||
if(!irq.enable) return;
|
||||
for(uint n : range(7)) {
|
||||
if(channel[n].irq.flag & channel[n].irq.enable) irq.flag = 1;
|
||||
}
|
||||
}
|
||||
|
||||
auto DMA::power(bool reset) -> void {
|
||||
for(uint n : range(7)) {
|
||||
channel[n].priority = 1 + n;
|
||||
channel[n].masterEnable = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
66
ares/ps1/dma/dma.hpp
ノーマルファイル
66
ares/ps1/dma/dma.hpp
ノーマルファイル
@ -0,0 +1,66 @@
|
||||
struct DMA {
|
||||
Node::Component node;
|
||||
|
||||
//dma.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
|
||||
auto step(uint clocks) -> void;
|
||||
auto pollIRQ() -> void;
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readByte(u32 address) -> u32;
|
||||
auto readHalf(u32 address) -> u32;
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeByte(u32 address, u32 data) -> void;
|
||||
auto writeHalf(u32 address, u32 data) -> void;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
|
||||
//transfer.cpp
|
||||
auto transferLinear(uint c) -> void;
|
||||
auto transferLinked(uint c) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
enum : uint { MDECIN, MDECOUT, GPU, CDROM, SPU, PIO, OTC }; //channels
|
||||
|
||||
struct IRQ {
|
||||
uint1 force;
|
||||
uint1 enable;
|
||||
uint1 flag;
|
||||
uint6 unknown;
|
||||
} irq;
|
||||
|
||||
struct Channel {
|
||||
//dma.cpp
|
||||
auto active() const -> bool {
|
||||
if(synchronization == 0) return enable && trigger;
|
||||
return enable;
|
||||
}
|
||||
|
||||
uint1 masterEnable;
|
||||
uint3 priority;
|
||||
uint24 address;
|
||||
uint16 length;
|
||||
uint16 blocks;
|
||||
uint1 direction;
|
||||
uint1 step; //0 = increment; 1 = decrement
|
||||
uint2 synchronization;
|
||||
struct Chopping {
|
||||
uint1 enable;
|
||||
uint3 dmaWindow;
|
||||
uint3 cpuWindow;
|
||||
} chopping;
|
||||
uint1 enable;
|
||||
uint1 trigger;
|
||||
uint2 unknown;
|
||||
struct IRQ {
|
||||
uint1 enable;
|
||||
uint1 flag;
|
||||
} irq;
|
||||
} channel[7];
|
||||
};
|
||||
|
||||
extern DMA dma;
|
@ -1,15 +1,15 @@
|
||||
auto CPU::DMA::readByte(u32 address) -> u8 {
|
||||
print("* read byte ", hex(address, 8L), "\n");
|
||||
auto DMA::readByte(u32 address) -> u32 {
|
||||
print("* rb", hex(address, 8L), "\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto CPU::DMA::readHalf(u32 address) -> u16 {
|
||||
print("* read half ", hex(address, 8L), "\n");
|
||||
auto DMA::readHalf(u32 address) -> u32 {
|
||||
print("* rh", hex(address, 8L), "\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto CPU::DMA::readWord(u32 address) -> u32 {
|
||||
uint32 data = 0;
|
||||
auto DMA::readWord(u32 address) -> u32 {
|
||||
uint32 data;
|
||||
uint32 c = address >> 4 & 7;
|
||||
|
||||
//DnMADR: DMA Base Address
|
||||
@ -78,15 +78,15 @@ auto CPU::DMA::readWord(u32 address) -> u32 {
|
||||
return data;
|
||||
}
|
||||
|
||||
auto CPU::DMA::writeByte(u32 address, u8 value) -> void {
|
||||
print("* write byte ", hex(address, 8L), " = ", hex(value, 2L), "\n");
|
||||
auto DMA::writeByte(u32 address, u32 value) -> void {
|
||||
print("* wb", hex(address, 8L), " = ", hex(value, 2L), "\n");
|
||||
}
|
||||
|
||||
auto CPU::DMA::writeHalf(u32 address, u16 value) -> void {
|
||||
print("* write half ", hex(address, 8L), " = ", hex(value, 4L), "\n");
|
||||
auto DMA::writeHalf(u32 address, u32 value) -> void {
|
||||
print("* wh", hex(address, 8L), " = ", hex(value, 4L), "\n");
|
||||
}
|
||||
|
||||
auto CPU::DMA::writeWord(u32 address, u32 value) -> void {
|
||||
auto DMA::writeWord(u32 address, u32 value) -> void {
|
||||
uint32 data = value;
|
||||
uint32 c = address >> 4 & 7;
|
||||
|
||||
@ -120,7 +120,7 @@ auto CPU::DMA::writeWord(u32 address, u32 value) -> void {
|
||||
if(channel[c].synchronization == 0) transferLinear(c);
|
||||
if(channel[c].synchronization == 1) transferLinear(c);
|
||||
if(channel[c].synchronization == 2) transferLinked(c);
|
||||
self.interrupt.pulse(CPU::Interrupt::DMA);
|
||||
interrupt.pulse(Interrupt::DMA);
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,78 +164,3 @@ auto CPU::DMA::writeWord(u32 address, u32 value) -> void {
|
||||
pollIRQ();
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::DMA::transferLinear(uint c) -> void {
|
||||
u32 address = channel[c].address;
|
||||
u16 blocks = channel[c].synchronization == 0 ? uint16(1) : channel[c].blocks;
|
||||
|
||||
do {
|
||||
u16 length = channel[c].length;
|
||||
do {
|
||||
if(channel[c].direction == 0) {
|
||||
u32 data = 0;
|
||||
if(c == 6) {
|
||||
data = address - 4 & 0xfffffc; //point to previous entry
|
||||
if(length == 1) data = 0xffffff; //end of ordering table
|
||||
}
|
||||
bus.writeWord(address, data);
|
||||
}
|
||||
|
||||
if(channel[c].direction == 1) {
|
||||
u32 data = bus.readWord(address);
|
||||
if(c == 2) {
|
||||
gpu.gp0(data);
|
||||
}
|
||||
}
|
||||
|
||||
if(channel[c].step == 0) address += 4;
|
||||
if(channel[c].step == 1) address -= 4;
|
||||
} while(--length);
|
||||
} while(--blocks);
|
||||
channel[c].enable = 0;
|
||||
channel[c].trigger = 0;
|
||||
}
|
||||
|
||||
auto CPU::DMA::transferLinked(uint c) -> void {
|
||||
if(channel[c].direction == 0) return; //invalid direction for this mode
|
||||
|
||||
u32 address = channel[c].address;
|
||||
u32 timeout = 0x40000; //unverified; prevents potential infinite loop
|
||||
do {
|
||||
u32 header = bus.readWord(address);
|
||||
|
||||
for(uint count : range(header >> 24)) {
|
||||
address += 4;
|
||||
u32 data = bus.readWord(address);
|
||||
|
||||
if(c == 2) {
|
||||
gpu.gp0(data);
|
||||
}
|
||||
}
|
||||
|
||||
address = header & 0xffffff;
|
||||
if(address & 0x800000) break;
|
||||
} while(--timeout);
|
||||
channel[c].enable = 0;
|
||||
channel[c].trigger = 0;
|
||||
}
|
||||
|
||||
auto CPU::DMA::pollIRQ() -> void {
|
||||
irq.flag = irq.force;
|
||||
if(!irq.enable) return;
|
||||
for(uint n : range(7)) {
|
||||
if(channel[n].irq.flag & channel[n].irq.enable) irq.flag = 1;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::DMA::power(bool reset) -> void {
|
||||
for(uint n : range(7)) {
|
||||
channel[n].priority = 1 + n;
|
||||
channel[n].masterEnable = 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::DMA::Channel::active() const -> bool {
|
||||
if(synchronization == 0) return enable && trigger;
|
||||
return enable;
|
||||
}
|
2
ares/ps1/dma/serialization.cpp
ノーマルファイル
2
ares/ps1/dma/serialization.cpp
ノーマルファイル
@ -0,0 +1,2 @@
|
||||
auto DMA::serialize(serializer& s) -> void {
|
||||
}
|
54
ares/ps1/dma/transfer.cpp
ノーマルファイル
54
ares/ps1/dma/transfer.cpp
ノーマルファイル
@ -0,0 +1,54 @@
|
||||
auto DMA::transferLinear(uint c) -> void {
|
||||
u32 address = channel[c].address;
|
||||
u16 blocks = channel[c].synchronization == 0 ? uint16(1) : channel[c].blocks;
|
||||
|
||||
do {
|
||||
u16 length = channel[c].length;
|
||||
do {
|
||||
if(channel[c].direction == 0) {
|
||||
u32 data = 0;
|
||||
if(c == 6) {
|
||||
data = address - 4 & 0xfffffc; //point to previous entry
|
||||
if(length == 1) data = 0xffffff; //end of ordering table
|
||||
}
|
||||
bus.writeWord(address, data);
|
||||
}
|
||||
|
||||
if(channel[c].direction == 1) {
|
||||
u32 data = bus.readWord(address);
|
||||
if(c == 2) {
|
||||
gpu.gp0(data);
|
||||
}
|
||||
}
|
||||
|
||||
if(channel[c].step == 0) address += 4;
|
||||
if(channel[c].step == 1) address -= 4;
|
||||
} while(--length);
|
||||
} while(--blocks);
|
||||
channel[c].enable = 0;
|
||||
channel[c].trigger = 0;
|
||||
}
|
||||
|
||||
auto DMA::transferLinked(uint c) -> void {
|
||||
if(channel[c].direction == 0) return; //invalid direction for this mode
|
||||
|
||||
u32 address = channel[c].address;
|
||||
u32 timeout = 0x40000; //unverified; prevents potential infinite loop
|
||||
do {
|
||||
u32 header = bus.readWord(address);
|
||||
|
||||
for(uint count : range(header >> 24)) {
|
||||
address += 4;
|
||||
u32 data = bus.readWord(address);
|
||||
|
||||
if(c == 2) {
|
||||
gpu.gp0(data);
|
||||
}
|
||||
}
|
||||
|
||||
address = header & 0xffffff;
|
||||
if(address & 0x800000) break;
|
||||
} while(--timeout);
|
||||
channel[c].enable = 0;
|
||||
channel[c].trigger = 0;
|
||||
}
|
@ -36,13 +36,13 @@ auto GPU::unload() -> void {
|
||||
|
||||
auto GPU::main() -> void {
|
||||
if(io.vcounter == 240) {
|
||||
cpu.interrupt.raise(CPU::Interrupt::Vblank);
|
||||
interrupt.raise(Interrupt::Vblank);
|
||||
}
|
||||
|
||||
if(++io.vcounter == 262) {
|
||||
io.vcounter = 0;
|
||||
io.field = !io.field;
|
||||
cpu.interrupt.lower(CPU::Interrupt::Vblank);
|
||||
interrupt.lower(Interrupt::Vblank);
|
||||
refreshed = true;
|
||||
}
|
||||
|
||||
|
90
ares/ps1/irq/io.cpp
ノーマルファイル
90
ares/ps1/irq/io.cpp
ノーマルファイル
@ -0,0 +1,90 @@
|
||||
auto Interrupt::readByte(u32 address) -> u32 {
|
||||
print("* rb", hex(address, 8L), "\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto Interrupt::readHalf(u32 address) -> u32 {
|
||||
uint16 data;
|
||||
|
||||
//I_STAT
|
||||
if(address == 0x1f80'1070) {
|
||||
data.bit( 0) = vblank.stat;
|
||||
data.bit( 1) = gpu.stat;
|
||||
data.bit( 2) = cdrom.stat;
|
||||
data.bit( 3) = dma.stat;
|
||||
data.bit( 4) = timer0.stat;
|
||||
data.bit( 5) = timer1.stat;
|
||||
data.bit( 6) = timer2.stat;
|
||||
data.bit( 7) = peripheral.stat;
|
||||
data.bit( 8) = sio.stat;
|
||||
data.bit( 9) = spu.stat;
|
||||
data.bit(10) = pio.stat;
|
||||
}
|
||||
|
||||
//I_MASK
|
||||
if(address == 0x1f80'1074) {
|
||||
data.bit( 0) = vblank.mask;
|
||||
data.bit( 1) = gpu.mask;
|
||||
data.bit( 2) = cdrom.mask;
|
||||
data.bit( 3) = dma.mask;
|
||||
data.bit( 4) = timer0.mask;
|
||||
data.bit( 5) = timer1.mask;
|
||||
data.bit( 6) = timer2.mask;
|
||||
data.bit( 7) = peripheral.mask;
|
||||
data.bit( 8) = sio.mask;
|
||||
data.bit( 9) = spu.mask;
|
||||
data.bit(10) = pio.mask;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
auto Interrupt::readWord(u32 address) -> u32 {
|
||||
uint32 data = readHalf(address & ~3 | 0) << 0;
|
||||
return data | readHalf(address & ~3 | 2) << 16;
|
||||
}
|
||||
|
||||
auto Interrupt::writeByte(u32 address, u32 value) -> void {
|
||||
print("* wb", hex(address, 8L), " = ", hex(value, 2L), "\n");
|
||||
}
|
||||
|
||||
auto Interrupt::writeHalf(u32 address, u32 value) -> void {
|
||||
uint16 data = value;
|
||||
|
||||
//I_STAT
|
||||
if(address == 0x1f80'1070) {
|
||||
if(!data.bit( 0)) vblank.stat = 0;
|
||||
if(!data.bit( 1)) gpu.stat = 0;
|
||||
if(!data.bit( 2)) cdrom.stat = 0;
|
||||
if(!data.bit( 3)) dma.stat = 0;
|
||||
if(!data.bit( 4)) timer0.stat = 0;
|
||||
if(!data.bit( 5)) timer1.stat = 0;
|
||||
if(!data.bit( 6)) timer2.stat = 0;
|
||||
if(!data.bit( 7)) peripheral.stat = 0;
|
||||
if(!data.bit( 8)) sio.stat = 0;
|
||||
if(!data.bit( 9)) spu.stat = 0;
|
||||
if(!data.bit(10)) pio.stat = 0;
|
||||
poll();
|
||||
}
|
||||
|
||||
//I_MASK
|
||||
if(address == 0x1f80'1074) {
|
||||
vblank.mask = data.bit( 0);
|
||||
gpu.mask = data.bit( 1);
|
||||
cdrom.mask = data.bit( 2);
|
||||
dma.mask = data.bit( 3);
|
||||
timer0.mask = data.bit( 4);
|
||||
timer1.mask = data.bit( 5);
|
||||
timer2.mask = data.bit( 6);
|
||||
peripheral.mask = data.bit( 7);
|
||||
sio.mask = data.bit( 8);
|
||||
spu.mask = data.bit( 9);
|
||||
pio.mask = data.bit(10);
|
||||
poll();
|
||||
}
|
||||
}
|
||||
|
||||
auto Interrupt::writeWord(u32 address, u32 data) -> void {
|
||||
writeHalf(address & ~3 | 0, data >> 0);
|
||||
writeHalf(address & ~3 | 2, data >> 16);
|
||||
}
|
71
ares/ps1/irq/irq.cpp
ノーマルファイル
71
ares/ps1/irq/irq.cpp
ノーマルファイル
@ -0,0 +1,71 @@
|
||||
#include <ps1/ps1.hpp>
|
||||
|
||||
namespace ares::PlayStation {
|
||||
|
||||
Interrupt interrupt;
|
||||
#include "io.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto Interrupt::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Component>("Interrupt");
|
||||
}
|
||||
|
||||
auto Interrupt::unload() -> void {
|
||||
node.reset();
|
||||
}
|
||||
|
||||
auto Interrupt::poll() -> void {
|
||||
line = 0;
|
||||
line |= vblank.stat & vblank.mask;
|
||||
line |= gpu.stat & gpu.mask;
|
||||
line |= cdrom.stat & cdrom.mask;
|
||||
line |= dma.stat & dma.mask;
|
||||
line |= timer0.stat & timer0.mask;
|
||||
line |= timer1.stat & timer1.mask;
|
||||
line |= timer2.stat & timer2.mask;
|
||||
line |= peripheral.stat & peripheral.mask;
|
||||
line |= sio.stat & sio.mask;
|
||||
line |= spu.stat & spu.mask;
|
||||
line |= pio.stat & pio.mask;
|
||||
cpu.scc.cause.interruptPending.bit(2) = line;
|
||||
}
|
||||
|
||||
auto Interrupt::pulse(uint source) -> void {
|
||||
raise(source);
|
||||
lower(source);
|
||||
}
|
||||
|
||||
auto Interrupt::raise(uint source) -> void {
|
||||
if(source == 0 && !vblank.line) vblank.line = vblank.stat = 1;
|
||||
if(source == 1 && !gpu.line) gpu.line = gpu.stat = 1;
|
||||
if(source == 2 && !cdrom.line) cdrom.line = cdrom.stat = 1;
|
||||
if(source == 3 && !dma.line) dma.line = dma.stat = 1;
|
||||
if(source == 4 && !timer0.line) timer0.line = timer0.stat = 1;
|
||||
if(source == 5 && !timer1.line) timer1.line = timer1.stat = 1;
|
||||
if(source == 6 && !timer2.line) timer2.line = timer2.stat = 1;
|
||||
if(source == 7 && !peripheral.line) peripheral.line = peripheral.stat = 1;
|
||||
if(source == 8 && !sio.line) sio.line = sio.stat = 1;
|
||||
if(source == 9 && !spu.line) spu.line = spu.stat = 1;
|
||||
if(source == 10 && !pio.line) pio.line = pio.stat = 1;
|
||||
poll();
|
||||
}
|
||||
|
||||
auto Interrupt::lower(uint source) -> void {
|
||||
if(source == 0) vblank.line = 0;
|
||||
if(source == 1) gpu.line = 0;
|
||||
if(source == 2) cdrom.line = 0;
|
||||
if(source == 3) dma.line = 0;
|
||||
if(source == 4) timer0.line = 0;
|
||||
if(source == 5) timer1.line = 0;
|
||||
if(source == 6) timer2.line = 0;
|
||||
if(source == 7) peripheral.line = 0;
|
||||
if(source == 8) sio.line = 0;
|
||||
if(source == 9) spu.line = 0;
|
||||
if(source == 10) pio.line = 0;
|
||||
poll();
|
||||
}
|
||||
|
||||
auto Interrupt::power(bool reset) -> void {
|
||||
}
|
||||
|
||||
}
|
47
ares/ps1/irq/irq.hpp
ノーマルファイル
47
ares/ps1/irq/irq.hpp
ノーマルファイル
@ -0,0 +1,47 @@
|
||||
struct Interrupt {
|
||||
Node::Component node;
|
||||
|
||||
//irq.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
|
||||
auto poll() -> void;
|
||||
auto pulse(uint source) -> void;
|
||||
auto raise(uint source) -> void;
|
||||
auto lower(uint source) -> void;
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readByte(u32 address) -> u32;
|
||||
auto readHalf(u32 address) -> u32;
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeByte(u32 address, u32 data) -> void;
|
||||
auto writeHalf(u32 address, u32 data) -> void;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
enum : uint { Vblank, GPU, CDROM, DMA, Timer0, Timer1, Timer2, Peripheral, SIO, SPU, PIO };
|
||||
|
||||
struct Source {
|
||||
uint1 line = 0;
|
||||
uint1 stat = 0;
|
||||
uint1 mask = 0;
|
||||
};
|
||||
|
||||
uint1 line = 0;
|
||||
Source vblank;
|
||||
Source gpu;
|
||||
Source cdrom;
|
||||
Source dma;
|
||||
Source timer0;
|
||||
Source timer1;
|
||||
Source timer2;
|
||||
Source peripheral;
|
||||
Source sio;
|
||||
Source spu;
|
||||
Source pio;
|
||||
};
|
||||
|
||||
extern Interrupt interrupt;
|
2
ares/ps1/irq/serialization.cpp
ノーマルファイル
2
ares/ps1/irq/serialization.cpp
ノーマルファイル
@ -0,0 +1,2 @@
|
||||
auto Interrupt::serialize(serializer& s) -> void {
|
||||
}
|
@ -6,8 +6,9 @@
|
||||
if(address <= 0x1f7f'ffff) return unmapped; \
|
||||
if(address <= 0x1f80'03ff) return cpu.cache.access(__VA_ARGS__); \
|
||||
if(address <= 0x1f80'106f) return unmapped; \
|
||||
if(address <= 0x1f80'107f) return cpu.interrupt.access(__VA_ARGS__); \
|
||||
if(address <= 0x1f80'10ff) return cpu.dma.access(__VA_ARGS__); \
|
||||
if(address <= 0x1f80'107f) return interrupt.access(__VA_ARGS__); \
|
||||
if(address <= 0x1f80'10ff) return dma.access(__VA_ARGS__); \
|
||||
if(address <= 0x1f80'112f) return timer.access(__VA_ARGS__); \
|
||||
if(address <= 0x1f80'17ff) return unmapped; \
|
||||
if(address <= 0x1f80'180f) return disc.access(__VA_ARGS__); \
|
||||
if(address <= 0x1f80'181f) return gpu.access(__VA_ARGS__); \
|
||||
@ -24,6 +25,7 @@ inline auto Bus::readByte(u32 address) -> u8 {
|
||||
}
|
||||
|
||||
inline auto Bus::readHalf(u32 address) -> u16 {
|
||||
if(address == 0x1f80'1044) return 0x0007;
|
||||
decode(0, readHalf, address);
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,9 @@ namespace ares::PlayStation {
|
||||
#include <ps1/cpu/cpu.hpp>
|
||||
#include <ps1/gpu/gpu.hpp>
|
||||
#include <ps1/spu/spu.hpp>
|
||||
#include <ps1/irq/irq.hpp>
|
||||
#include <ps1/dma/dma.hpp>
|
||||
#include <ps1/timer/timer.hpp>
|
||||
#include <ps1/memory/bus.hpp>
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,9 @@ auto System::serializeAll(serializer& s, bool synchronize) -> void {
|
||||
gpu.serialize(s);
|
||||
spu.serialize(s);
|
||||
disc.serialize(s);
|
||||
interrupt.serialize(s);
|
||||
dma.serialize(s);
|
||||
timer.serialize(s);
|
||||
}
|
||||
|
||||
auto System::serializeInit(bool synchronize) -> uint {
|
||||
|
@ -23,6 +23,9 @@ auto System::load(Node::Object& root) -> void {
|
||||
gpu.load(node);
|
||||
spu.load(node);
|
||||
disc.load(node);
|
||||
interrupt.load(node);
|
||||
dma.load(node);
|
||||
timer.load(node);
|
||||
}
|
||||
|
||||
auto System::unload() -> void {
|
||||
@ -32,6 +35,9 @@ auto System::unload() -> void {
|
||||
gpu.unload();
|
||||
spu.unload();
|
||||
disc.unload();
|
||||
interrupt.unload();
|
||||
dma.unload();
|
||||
timer.unload();
|
||||
node.reset();
|
||||
}
|
||||
|
||||
@ -50,7 +56,10 @@ auto System::power(bool reset) -> void {
|
||||
cpu.power(reset);
|
||||
gpu.power(reset);
|
||||
spu.power(reset);
|
||||
disc.power();
|
||||
disc.power(reset);
|
||||
interrupt.power(reset);
|
||||
dma.power(reset);
|
||||
timer.power(reset);
|
||||
|
||||
information.serializeSize[0] = serializeInit(0);
|
||||
information.serializeSize[1] = serializeInit(1);
|
||||
|
172
ares/ps1/timer/io.cpp
ノーマルファイル
172
ares/ps1/timer/io.cpp
ノーマルファイル
@ -0,0 +1,172 @@
|
||||
auto Timer::readByte(u32 address) -> u32 {
|
||||
print("* rb", hex(address, 8L), "\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto Timer::readHalf(u32 address) -> u32 {
|
||||
uint16 data;
|
||||
|
||||
if(address == 0x1f80'1100) {
|
||||
data.bit(0,15) = htimer.counter;
|
||||
}
|
||||
|
||||
if(address == 0x1f80'1104) {
|
||||
data.bit( 0) = htimer.synchronizeEnable;
|
||||
data.bit( 1, 2) = htimer.synchronizeMode;
|
||||
data.bit( 3) = htimer.resetMode;
|
||||
data.bit( 4) = htimer.irqOnTarget;
|
||||
data.bit( 5) = htimer.irqOnSaturate;
|
||||
data.bit( 6) = htimer.irqRepeat;
|
||||
data.bit( 7) = htimer.irqMode;
|
||||
data.bit( 8, 9) = htimer.clockSource;
|
||||
data.bit(10) = htimer.irqLine;
|
||||
data.bit(11) = htimer.reachedTarget;
|
||||
data.bit(12) = htimer.reachedSaturate;
|
||||
data.bit(13,15) = htimer.unknown;
|
||||
htimer.reachedTarget = 0;
|
||||
htimer.reachedSaturate = 0;
|
||||
}
|
||||
|
||||
if(address == 0x1f80'1108) {
|
||||
data.bit(0,15) = htimer.target;
|
||||
}
|
||||
|
||||
if(address == 0x1f80'1110) {
|
||||
data.bit(0,15) = vtimer.counter;
|
||||
}
|
||||
|
||||
if(address == 0x1f80'1114) {
|
||||
data.bit( 0) = vtimer.synchronizeEnable;
|
||||
data.bit( 1, 2) = vtimer.synchronizeMode;
|
||||
data.bit( 3) = vtimer.resetMode;
|
||||
data.bit( 4) = vtimer.irqOnTarget;
|
||||
data.bit( 5) = vtimer.irqOnSaturate;
|
||||
data.bit( 6) = vtimer.irqRepeat;
|
||||
data.bit( 7) = vtimer.irqMode;
|
||||
data.bit( 8, 9) = vtimer.clockSource;
|
||||
data.bit(10) = vtimer.irqLine;
|
||||
data.bit(11) = vtimer.reachedTarget;
|
||||
data.bit(12) = vtimer.reachedSaturate;
|
||||
data.bit(13,15) = vtimer.unknown;
|
||||
vtimer.reachedTarget = 0;
|
||||
vtimer.reachedSaturate = 0;
|
||||
}
|
||||
|
||||
if(address == 0x1f80'1118) {
|
||||
data.bit(0,15) = vtimer.target;
|
||||
}
|
||||
|
||||
if(address == 0x1f80'1120) {
|
||||
data.bit(0,15) = ftimer.counter;
|
||||
}
|
||||
|
||||
if(address == 0x1f80'1124) {
|
||||
data.bit( 0) = ftimer.synchronizeEnable;
|
||||
data.bit( 1, 2) = ftimer.synchronizeMode;
|
||||
data.bit( 3) = ftimer.resetMode;
|
||||
data.bit( 4) = ftimer.irqOnTarget;
|
||||
data.bit( 5) = ftimer.irqOnSaturate;
|
||||
data.bit( 6) = ftimer.irqRepeat;
|
||||
data.bit( 7) = ftimer.irqMode;
|
||||
data.bit( 8, 9) = ftimer.clockSource;
|
||||
data.bit(10) = ftimer.irqLine;
|
||||
data.bit(11) = ftimer.reachedTarget;
|
||||
data.bit(12) = ftimer.reachedSaturate;
|
||||
data.bit(13,15) = ftimer.unknown;
|
||||
ftimer.reachedTarget = 0;
|
||||
ftimer.reachedSaturate = 0;
|
||||
}
|
||||
|
||||
if(address == 0x1f80'1128) {
|
||||
data.bit(0,15) = ftimer.counter;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
auto Timer::readWord(u32 address) -> u32 {
|
||||
uint32 data = readHalf(address & ~3 | 0) << 0;
|
||||
return data | readHalf(address & ~3 | 2) << 16;
|
||||
}
|
||||
|
||||
auto Timer::writeByte(u32 address, u32 value) -> void {
|
||||
print("* wb", hex(address, 8L), "\n");
|
||||
}
|
||||
|
||||
auto Timer::writeHalf(u32 address, u32 value) -> void {
|
||||
uint16 data = value;
|
||||
|
||||
if(address == 0x1f80'1100) {
|
||||
htimer.counter = data.bit(0,15);
|
||||
}
|
||||
|
||||
if(address == 0x1f80'1104) {
|
||||
htimer.synchronizeEnable = data.bit( 0);
|
||||
htimer.synchronizeMode = data.bit( 1, 2);
|
||||
htimer.resetMode = data.bit( 3);
|
||||
htimer.irqOnTarget = data.bit( 4);
|
||||
htimer.irqOnSaturate = data.bit( 5);
|
||||
htimer.irqRepeat = data.bit( 6);
|
||||
htimer.irqMode = data.bit( 7);
|
||||
htimer.clockSource = data.bit( 8, 9);
|
||||
htimer.irqLine = data.bit(10);
|
||||
htimer.reachedTarget = data.bit(11);
|
||||
htimer.reachedSaturate = data.bit(12);
|
||||
htimer.unknown = data.bit(13,15);
|
||||
}
|
||||
|
||||
if(address == 0x1f80'1108) {
|
||||
htimer.target = data.bit(0,15);
|
||||
}
|
||||
|
||||
if(address == 0x1f80'1110) {
|
||||
vtimer.counter = data.bit(0,15);
|
||||
}
|
||||
|
||||
if(address == 0x1f80'1114) {
|
||||
vtimer.synchronizeEnable = data.bit( 0);
|
||||
vtimer.synchronizeMode = data.bit( 1, 2);
|
||||
vtimer.resetMode = data.bit( 3);
|
||||
vtimer.irqOnTarget = data.bit( 4);
|
||||
vtimer.irqOnSaturate = data.bit( 5);
|
||||
vtimer.irqRepeat = data.bit( 6);
|
||||
vtimer.irqMode = data.bit( 7);
|
||||
vtimer.clockSource = data.bit( 8, 9);
|
||||
vtimer.irqLine = data.bit(10);
|
||||
vtimer.reachedTarget = data.bit(11);
|
||||
vtimer.reachedSaturate = data.bit(12);
|
||||
vtimer.unknown = data.bit(13,15);
|
||||
}
|
||||
|
||||
if(address == 0x1f80'1118) {
|
||||
vtimer.target = data.bit(0,15);
|
||||
}
|
||||
|
||||
if(address == 0x1f80'1120) {
|
||||
ftimer.counter = data.bit(0,15);
|
||||
}
|
||||
|
||||
if(address == 0x1f80'1124) {
|
||||
ftimer.synchronizeEnable = data.bit( 0);
|
||||
ftimer.synchronizeMode = data.bit( 1, 2);
|
||||
ftimer.resetMode = data.bit( 3);
|
||||
ftimer.irqOnTarget = data.bit( 4);
|
||||
ftimer.irqOnSaturate = data.bit( 5);
|
||||
ftimer.irqRepeat = data.bit( 6);
|
||||
ftimer.irqMode = data.bit( 7);
|
||||
ftimer.clockSource = data.bit( 8, 9);
|
||||
ftimer.irqLine = data.bit(10);
|
||||
ftimer.reachedTarget = data.bit(11);
|
||||
ftimer.reachedSaturate = data.bit(12);
|
||||
ftimer.unknown = data.bit(13,15);
|
||||
}
|
||||
|
||||
if(address == 0x1f80'1128) {
|
||||
ftimer.target = data.bit(0,15);
|
||||
}
|
||||
}
|
||||
|
||||
auto Timer::writeWord(u32 address, u32 data) -> void {
|
||||
writeHalf(address & ~3 | 0, data >> 0);
|
||||
writeHalf(address & ~3 | 2, data >> 16);
|
||||
}
|
2
ares/ps1/timer/serialization.cpp
ノーマルファイル
2
ares/ps1/timer/serialization.cpp
ノーマルファイル
@ -0,0 +1,2 @@
|
||||
auto Timer::serialize(serializer& s) -> void {
|
||||
}
|
23
ares/ps1/timer/timer.cpp
ノーマルファイル
23
ares/ps1/timer/timer.cpp
ノーマルファイル
@ -0,0 +1,23 @@
|
||||
#include <ps1/ps1.hpp>
|
||||
|
||||
namespace ares::PlayStation {
|
||||
|
||||
Timer timer;
|
||||
#include "io.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto Timer::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Component>("Timer");
|
||||
}
|
||||
|
||||
auto Timer::unload() -> void {
|
||||
node.reset();
|
||||
}
|
||||
|
||||
auto Timer::step(uint clocks) -> void {
|
||||
}
|
||||
|
||||
auto Timer::power(bool reset) -> void {
|
||||
}
|
||||
|
||||
}
|
44
ares/ps1/timer/timer.hpp
ノーマルファイル
44
ares/ps1/timer/timer.hpp
ノーマルファイル
@ -0,0 +1,44 @@
|
||||
struct Timer {
|
||||
Node::Component node;
|
||||
|
||||
//timer.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
|
||||
auto step(uint clocks) -> void;
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readByte(u32 address) -> u32;
|
||||
auto readHalf(u32 address) -> u32;
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeByte(u32 address, u32 data) -> void;
|
||||
auto writeHalf(u32 address, u32 data) -> void;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
struct Source {
|
||||
uint16 counter;
|
||||
uint16 target;
|
||||
uint1 synchronizeEnable;
|
||||
uint2 synchronizeMode;
|
||||
uint1 resetMode;
|
||||
uint1 irqOnTarget;
|
||||
uint1 irqOnSaturate;
|
||||
uint1 irqRepeat;
|
||||
uint1 irqMode;
|
||||
uint2 clockSource;
|
||||
uint1 irqLine;
|
||||
uint1 reachedTarget;
|
||||
uint1 reachedSaturate;
|
||||
uint3 unknown;
|
||||
};
|
||||
|
||||
Source htimer; //Hblank timer 0
|
||||
Source vtimer; //Vblank timer 1
|
||||
Source ftimer; //free timer 2
|
||||
};
|
||||
|
||||
extern Timer timer;
|
読み込み中…
新しいイシューから参照
ユーザーをブロックする