mirror of
https://github.com/libretro/bsnes-libretro.git
synced 2024-11-23 08:59:40 +00:00
Update to v106r60 release.
byuu says: I added (imperfect) memory conflict timing to the SA1. Before: - WRAM↔↔ROM ran 7% too fast - ROM↔↔ROM ran 100% too fast - WRAM↔↔IRAM ran 7% too fast - ROM↔↔IRAM ran 7% too fast - IRAM↔↔IRAM ran 287% too fast - BWRAM↔↔BWRAM ran 100% too fast - HDMA ROM↔↔ROM ran 15% too fast - HDMA WRAM↔↔ROM ran 15% too fast - DMA ROM↔↔ROM ran 100% too fast After: - ROM↔↔ROM runs 14% too fast - HDMA WRAM↔↔ROM runs 7% too fast - DMA ROM↔↔ROM runs 4% too fast If you enable this with the fast PPU + DSP, your framerate in SA1 games will drop by 51%. And even if you disable it, you'll still lose 9% speed in SA1 games, and 2% speed in non-SA1 games, because of changes needed to make this support possible. By default, I'm leaving this off. Compile with `-DACCURATE_SA1` (or uncomment the line in sfc/sfc.hpp) if you want to try it out. This'll almost certainly cause some SA1 regressions, so I guess we'll tackle those as they arise.
This commit is contained in:
parent
bd814f0358
commit
a3e0f6da25
@ -28,7 +28,7 @@ using namespace nall;
|
||||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "106.59";
|
||||
static const string Version = "106.60";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "https://byuu.org/";
|
||||
|
@ -67,8 +67,8 @@ auto APU::power() -> void {
|
||||
phase = 0;
|
||||
cycle = 0;
|
||||
|
||||
LinearFeedbackShiftRegisterGenerator r;
|
||||
for(auto& n : wave.pattern) n = r();
|
||||
PRNG prng;
|
||||
for(auto& n : wave.pattern) n = prng.random();
|
||||
}
|
||||
|
||||
auto APU::readIO(uint16 addr) -> uint8 {
|
||||
|
@ -7,20 +7,24 @@ L fetch();
|
||||
idle6(absolute);
|
||||
L idle();
|
||||
aa(PC) = absolute;
|
||||
idleBranch();
|
||||
}
|
||||
}
|
||||
|
||||
auto WDC65816::instructionBranchLong() -> void {
|
||||
uint16 displacement = fetch();
|
||||
hi(displacement) = fetch();
|
||||
uint16 absolute = PC + (int16)displacement;
|
||||
L idle();
|
||||
aa(PC) = PC + (int16)displacement;
|
||||
aa(PC) = absolute;
|
||||
idleBranch();
|
||||
}
|
||||
|
||||
auto WDC65816::instructionJumpShort() -> void {
|
||||
uint16 data = fetch();
|
||||
L hi(data) = fetch();
|
||||
aa(PC) = data;
|
||||
idleJump();
|
||||
}
|
||||
|
||||
auto WDC65816::instructionJumpLong() -> void {
|
||||
@ -28,6 +32,7 @@ auto WDC65816::instructionJumpLong() -> void {
|
||||
hi(data) = fetch();
|
||||
L db(data) = fetch();
|
||||
PC = data;
|
||||
idleJump();
|
||||
}
|
||||
|
||||
auto WDC65816::instructionJumpIndirect() -> void {
|
||||
@ -36,6 +41,7 @@ auto WDC65816::instructionJumpIndirect() -> void {
|
||||
uint16 data = read(uint16(absolute + 0));
|
||||
L hi(data) = read(uint16(absolute + 1));
|
||||
aa(PC) = data;
|
||||
idleJump();
|
||||
}
|
||||
|
||||
auto WDC65816::instructionJumpIndexedIndirect() -> void {
|
||||
@ -45,6 +51,7 @@ auto WDC65816::instructionJumpIndexedIndirect() -> void {
|
||||
uint16 data = read(db(PC) << 16 | uint16(absolute + X + 0));
|
||||
L hi(data) = read(db(PC) << 16 | uint16(absolute + X + 1));
|
||||
aa(PC) = data;
|
||||
idleJump();
|
||||
}
|
||||
|
||||
auto WDC65816::instructionJumpIndirectLong() -> void {
|
||||
@ -54,6 +61,7 @@ auto WDC65816::instructionJumpIndirectLong() -> void {
|
||||
hi(data) = read(uint16(absolute + 1));
|
||||
L db(data) = read(uint16(absolute + 2));
|
||||
PC = data;
|
||||
idleJump();
|
||||
}
|
||||
|
||||
auto WDC65816::instructionCallShort() -> void {
|
||||
@ -64,6 +72,7 @@ auto WDC65816::instructionCallShort() -> void {
|
||||
push(hi(PC));
|
||||
L push(lo(PC));
|
||||
aa(PC) = data;
|
||||
idleJump();
|
||||
}
|
||||
|
||||
auto WDC65816::instructionCallLong() -> void {
|
||||
@ -77,6 +86,7 @@ auto WDC65816::instructionCallLong() -> void {
|
||||
L pushN(lo(PC));
|
||||
PC = data;
|
||||
E hi(S) = 0x01;
|
||||
idleJump();
|
||||
}
|
||||
|
||||
auto WDC65816::instructionCallIndexedIndirect() -> void {
|
||||
@ -89,6 +99,7 @@ auto WDC65816::instructionCallIndexedIndirect() -> void {
|
||||
L hi(data) = read(db(PC) << 16 | uint16(absolute + X + 1));
|
||||
aa(PC) = data;
|
||||
E hi(S) = 0x01;
|
||||
idleJump();
|
||||
}
|
||||
|
||||
auto WDC65816::instructionReturnInterrupt() -> void {
|
||||
@ -104,6 +115,7 @@ E XF = 1, MF = 1;
|
||||
hi(PC) = pull();
|
||||
L db(PC) = pull();
|
||||
}
|
||||
idleJump();
|
||||
}
|
||||
|
||||
auto WDC65816::instructionReturnShort() -> void {
|
||||
@ -114,6 +126,7 @@ auto WDC65816::instructionReturnShort() -> void {
|
||||
L idle();
|
||||
aa(PC) = data;
|
||||
aa(PC)++;
|
||||
idleJump();
|
||||
}
|
||||
|
||||
auto WDC65816::instructionReturnLong() -> void {
|
||||
@ -125,4 +138,5 @@ L db(data) = pullN();
|
||||
PC = data;
|
||||
aa(PC)++;
|
||||
E hi(S) = 0x01;
|
||||
idleJump();
|
||||
}
|
||||
|
@ -23,8 +23,8 @@ auto WDC65816::idle4(uint16 x, uint16 y) -> void {
|
||||
if(!XF || hi(x) != hi(y)) idle();
|
||||
}
|
||||
|
||||
auto WDC65816::idle6(uint16 addr) -> void {
|
||||
if(EF && hi(PC) != hi(addr)) idle();
|
||||
auto WDC65816::idle6(uint16 address) -> void {
|
||||
if(EF && hi(PC) != hi(address)) idle();
|
||||
}
|
||||
|
||||
auto WDC65816::fetch() -> uint8 {
|
||||
|
@ -22,6 +22,8 @@ auto WDC65816::serialize(serializer& s) -> void {
|
||||
s.integer(r.irq);
|
||||
s.integer(r.wai);
|
||||
s.integer(r.stp);
|
||||
s.integer(r.rwb);
|
||||
s.integer(r.mar);
|
||||
s.integer(r.mdr);
|
||||
s.integer(r.vector);
|
||||
}
|
||||
|
@ -54,9 +54,11 @@ auto WDC65816::power() -> void {
|
||||
P = 0x34;
|
||||
EF = 1;
|
||||
|
||||
r.mdr = 0x00;
|
||||
r.wai = false;
|
||||
r.stp = false;
|
||||
r.rwb = false;
|
||||
r.mar = 0x000000;
|
||||
r.mdr = 0x00;
|
||||
r.vector = 0xfffc; //reset vector address
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,8 @@ namespace Processor {
|
||||
|
||||
struct WDC65816 {
|
||||
virtual auto idle() -> void = 0;
|
||||
virtual auto idleBranch() -> void {}
|
||||
virtual auto idleJump() -> void {}
|
||||
virtual auto read(uint24 addr) -> uint8 = 0;
|
||||
virtual auto write(uint24 addr, uint8 data) -> void = 0;
|
||||
virtual auto lastCycle() -> void = 0;
|
||||
@ -24,7 +26,7 @@ struct WDC65816 {
|
||||
inline auto idleIRQ() -> void;
|
||||
inline auto idle2() -> void;
|
||||
inline auto idle4(uint16 x, uint16 y) -> void;
|
||||
inline auto idle6(uint16 addr) -> void;
|
||||
inline auto idle6(uint16 address) -> void;
|
||||
inline auto fetch() -> uint8;
|
||||
inline auto pull() -> uint8;
|
||||
auto push(uint8 data) -> void;
|
||||
@ -250,6 +252,8 @@ struct WDC65816 {
|
||||
bool irq = false; //IRQ pin (0 = low, 1 = trigger)
|
||||
bool wai = false; //raised during wai, cleared after interrupt triggered
|
||||
bool stp = false; //raised during stp, never cleared
|
||||
bool rwb = false; //read/write pin
|
||||
uint24 mar; //memory address register
|
||||
uint8 mdr; //memory data register
|
||||
uint16 vector; //interrupt vector address
|
||||
} r;
|
||||
|
@ -66,7 +66,7 @@ private:
|
||||
auto loadCartridgeSufamiTurboA(Markup::Node) -> void;
|
||||
auto loadCartridgeSufamiTurboB(Markup::Node) -> void;
|
||||
|
||||
auto loadMemory(MappedRAM&, Markup::Node, bool required) -> void;
|
||||
auto loadMemory(Memory&, Markup::Node, bool required) -> void;
|
||||
auto loadMap(Markup::Node, SuperFamicom::Memory&) -> void;
|
||||
auto loadMap(Markup::Node, const function<uint8 (uint24, uint8)>&, const function<void (uint24, uint8)>&) -> void;
|
||||
|
||||
@ -99,7 +99,7 @@ private:
|
||||
auto saveCartridgeSufamiTurboA(Markup::Node) -> void;
|
||||
auto saveCartridgeSufamiTurboB(Markup::Node) -> void;
|
||||
|
||||
auto saveMemory(MappedRAM&, Markup::Node) -> void;
|
||||
auto saveMemory(Memory&, Markup::Node) -> void;
|
||||
|
||||
auto saveRAM(Markup::Node) -> void;
|
||||
auto saveMCC(Markup::Node) -> void;
|
||||
|
@ -119,7 +119,7 @@ auto Cartridge::loadCartridgeSufamiTurboB(Markup::Node node) -> void {
|
||||
|
||||
//
|
||||
|
||||
auto Cartridge::loadMemory(MappedRAM& ram, Markup::Node node, bool required) -> void {
|
||||
auto Cartridge::loadMemory(Memory& ram, Markup::Node node, bool required) -> void {
|
||||
if(auto memory = game.memory(node)) {
|
||||
ram.allocate(memory->size);
|
||||
if(memory->type == "RAM" && !memory->nonVolatile) return;
|
||||
@ -306,7 +306,7 @@ auto Cartridge::loadSA1(Markup::Node node) -> void {
|
||||
|
||||
if(auto mcu = node["mcu"]) {
|
||||
for(auto map : mcu.find("map")) {
|
||||
loadMap(map, {&SA1::mmcromRead, &sa1}, {&SA1::mmcromWrite, &sa1});
|
||||
loadMap(map, {&SA1::ROM::readCPU, &sa1.rom}, {&SA1::ROM::writeCPU, &sa1.rom});
|
||||
}
|
||||
if(auto memory = mcu["memory(type=ROM,content=Program)"]) {
|
||||
loadMemory(sa1.rom, memory, File::Required);
|
||||
@ -319,14 +319,14 @@ auto Cartridge::loadSA1(Markup::Node node) -> void {
|
||||
if(auto memory = node["memory(type=RAM,content=Save)"]) {
|
||||
loadMemory(sa1.bwram, memory, File::Optional);
|
||||
for(auto map : memory.find("map")) {
|
||||
loadMap(map, {&SA1::mmcbwramRead, &sa1}, {&SA1::mmcbwramWrite, &sa1});
|
||||
loadMap(map, {&SA1::BWRAM::readCPU, &sa1.bwram}, {&SA1::BWRAM::writeCPU, &sa1.bwram});
|
||||
}
|
||||
}
|
||||
|
||||
if(auto memory = node["memory(type=RAM,content=Internal)"]) {
|
||||
loadMemory(sa1.iram, memory, File::Optional);
|
||||
for(auto map : memory.find("map")) {
|
||||
loadMap(map, sa1.cpuiram);
|
||||
loadMap(map, {&SA1::IRAM::readCPU, &sa1.iram}, {&SA1::IRAM::writeCPU, &sa1.iram});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ auto Cartridge::saveCartridgeSufamiTurboB(Markup::Node node) -> void {
|
||||
|
||||
//
|
||||
|
||||
auto Cartridge::saveMemory(MappedRAM& ram, Markup::Node node) -> void {
|
||||
auto Cartridge::saveMemory(Memory& ram, Markup::Node node) -> void {
|
||||
if(auto memory = game.memory(node)) {
|
||||
if(memory->type == "RAM" && !memory->nonVolatile) return;
|
||||
if(memory->type == "RTC" && !memory->nonVolatile) return;
|
||||
|
@ -1,30 +0,0 @@
|
||||
//ROM / RAM access from the S-CPU
|
||||
|
||||
auto SA1::CPUIRAM::size() const -> uint {
|
||||
return sa1.iram.size();
|
||||
}
|
||||
|
||||
auto SA1::CPUIRAM::read(uint24 addr, uint8) -> uint8 {
|
||||
cpu.synchronize(sa1);
|
||||
return sa1.iram.read(addr & 0x07ff);
|
||||
}
|
||||
|
||||
auto SA1::CPUIRAM::write(uint24 addr, uint8 data) -> void {
|
||||
cpu.synchronize(sa1);
|
||||
sa1.iram.write(addr & 0x07ff, data);
|
||||
}
|
||||
|
||||
auto SA1::CPUBWRAM::size() const -> uint {
|
||||
return sa1.bwram.size();
|
||||
}
|
||||
|
||||
auto SA1::CPUBWRAM::read(uint24 addr, uint8) -> uint8 {
|
||||
cpu.synchronize(sa1);
|
||||
if(dma) return sa1.dmaCC1Read(addr);
|
||||
return sa1.bwram.read(addr);
|
||||
}
|
||||
|
||||
auto SA1::CPUBWRAM::write(uint24 addr, uint8 data) -> void {
|
||||
cpu.synchronize(sa1);
|
||||
sa1.bwram.write(addr, data);
|
||||
}
|
120
higan/sfc/coprocessor/sa1/bwram.cpp
Normal file
120
higan/sfc/coprocessor/sa1/bwram.cpp
Normal file
@ -0,0 +1,120 @@
|
||||
auto SA1::BWRAM::conflict() const -> bool {
|
||||
if(!cpu.r.rwb) return false;
|
||||
if((cpu.r.mar & 0x40e000) == 0x006000) return true; //00-3f,80-bf:6000-7fff
|
||||
if((cpu.r.mar & 0xf00000) == 0x400000) return true; //40-4f:0000-ffff
|
||||
return false;
|
||||
}
|
||||
|
||||
auto SA1::BWRAM::read(uint24 address, uint8 data) -> uint8 {
|
||||
if(!size()) return data;
|
||||
address = bus.mirror(address, size());
|
||||
return _data[address];
|
||||
}
|
||||
|
||||
auto SA1::BWRAM::write(uint24 address, uint8 data) -> void {
|
||||
if(!size()) return;
|
||||
address = bus.mirror(address, size());
|
||||
_data[address] = data;
|
||||
}
|
||||
|
||||
//note: addresses are translated prior to invoking this function:
|
||||
//00-3f,80-bf:6000-7fff size=0x2000 => 00:0000-1fff
|
||||
//40-4f:0000-ffff => untranslated
|
||||
auto SA1::BWRAM::readCPU(uint24 address, uint8 data) -> uint8 {
|
||||
cpu.synchronize(sa1);
|
||||
|
||||
if(address < 0x2000) { //$00-3f,80-bf:6000-7fff
|
||||
address = sa1.mmio.sbm * 0x2000 + (address & 0x1fff);
|
||||
}
|
||||
|
||||
if(dma) return sa1.dmaCC1Read(address);
|
||||
return read(address, data);
|
||||
}
|
||||
|
||||
auto SA1::BWRAM::writeCPU(uint24 address, uint8 data) -> void {
|
||||
cpu.synchronize(sa1);
|
||||
|
||||
if(address < 0x2000) { //$00-3f,80-bf:6000-7fff
|
||||
address = sa1.mmio.sbm * 0x2000 + (address & 0x1fff);
|
||||
}
|
||||
|
||||
return write(address, data);
|
||||
}
|
||||
|
||||
auto SA1::BWRAM::readSA1(uint24 address, uint8 data) -> uint8 {
|
||||
if(sa1.mmio.sw46 == 0) {
|
||||
//$40-43:0000-ffff x 32 projection
|
||||
address = (sa1.mmio.cbm & 0x1f) * 0x2000 + (address & 0x1fff);
|
||||
return readLinear(address, data);
|
||||
} else {
|
||||
//$60-6f:0000-ffff x 128 projection
|
||||
address = sa1.mmio.cbm * 0x2000 + (address & 0x1fff);
|
||||
return readBitmap(address, data);
|
||||
}
|
||||
}
|
||||
|
||||
auto SA1::BWRAM::writeSA1(uint24 address, uint8 data) -> void {
|
||||
if(sa1.mmio.sw46 == 0) {
|
||||
//$40-43:0000-ffff x 32 projection
|
||||
address = (sa1.mmio.cbm & 0x1f) * 0x2000 + (address & 0x1fff);
|
||||
return writeLinear(address, data);
|
||||
} else {
|
||||
//$60-6f:0000-ffff x 128 projection
|
||||
address = sa1.mmio.cbm * 0x2000 + (address & 0x1fff);
|
||||
return writeBitmap(address, data);
|
||||
}
|
||||
}
|
||||
|
||||
auto SA1::BWRAM::readLinear(uint24 address, uint8 data) -> uint8 {
|
||||
return read(address, data);
|
||||
}
|
||||
|
||||
auto SA1::BWRAM::writeLinear(uint24 address, uint8 data) -> void {
|
||||
return write(address, data);
|
||||
}
|
||||
|
||||
auto SA1::BWRAM::readBitmap(uint20 address, uint8 data) -> uint8 {
|
||||
if(sa1.mmio.bbf == 0) {
|
||||
//4bpp
|
||||
uint shift = address & 1;
|
||||
address >>= 1;
|
||||
switch(shift) {
|
||||
case 0: return read(address).bits(0,3);
|
||||
case 1: return read(address).bits(4,7);
|
||||
}
|
||||
} else {
|
||||
//2bpp
|
||||
uint shift = address & 3;
|
||||
address >>= 2;
|
||||
switch(shift) {
|
||||
case 0: return read(address).bits(0,1);
|
||||
case 1: return read(address).bits(2,3);
|
||||
case 2: return read(address).bits(4,5);
|
||||
case 3: return read(address).bits(6,7);
|
||||
}
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto SA1::BWRAM::writeBitmap(uint20 address, uint8 data) -> void {
|
||||
if(sa1.mmio.bbf == 0) {
|
||||
//4bpp
|
||||
uint shift = address & 1;
|
||||
address >>= 1;
|
||||
switch(shift) {
|
||||
case 0: data = read(address) & 0xf0 | data.bits(0,3) << 0; break;
|
||||
case 1: data = read(address) & 0x0f | data.bits(0,3) << 4; break;
|
||||
}
|
||||
} else {
|
||||
//2bpp
|
||||
uint shift = address & 3;
|
||||
address >>= 2;
|
||||
switch(shift) {
|
||||
case 0: data = read(address) & 0xfc | data.bits(0,1) << 0; break;
|
||||
case 1: data = read(address) & 0xf3 | data.bits(0,1) << 2; break;
|
||||
case 2: data = read(address) & 0xcf | data.bits(0,1) << 4; break;
|
||||
case 3: data = read(address) & 0x3f | data.bits(0,1) << 6; break;
|
||||
}
|
||||
}
|
||||
write(address, data);
|
||||
}
|
@ -1,45 +1,32 @@
|
||||
//====================
|
||||
//direct data transfer
|
||||
//====================
|
||||
|
||||
auto SA1::dmaNormal() -> void {
|
||||
while(mmio.dtc--) {
|
||||
uint8 data = r.mdr;
|
||||
uint32 dsa = mmio.dsa++;
|
||||
uint32 dda = mmio.dda++;
|
||||
uint24 source = mmio.dsa++;
|
||||
uint16 target = mmio.dda++;
|
||||
|
||||
//source and destination cannot be the same
|
||||
if(mmio.sd == DMA::SourceBWRAM && mmio.dd == DMA::DestBWRAM) continue;
|
||||
if(mmio.sd == DMA::SourceIRAM && mmio.dd == DMA::DestIRAM ) continue;
|
||||
|
||||
switch(mmio.sd) {
|
||||
case DMA::SourceROM:
|
||||
if((dsa & 0x408000) == 0x008000 || (dsa & 0xc00000) == 0xc00000) {
|
||||
data = busRead(dsa, data);
|
||||
}
|
||||
break;
|
||||
|
||||
case DMA::SourceBWRAM:
|
||||
if((dsa & 0x40e000) == 0x006000 || (dsa & 0xf00000) == 0x400000) {
|
||||
data = busRead(dsa, data);
|
||||
}
|
||||
break;
|
||||
|
||||
case DMA::SourceIRAM:
|
||||
data = iram.read(dsa & 0x07ff);
|
||||
break;
|
||||
if(mmio.sd == DMA::SourceROM && mmio.dd == DMA::DestBWRAM) {
|
||||
step(bwram.conflict() ? 8 : 4);
|
||||
data = rom.readSA1(source, data);
|
||||
bwram.write(target, data);
|
||||
}
|
||||
|
||||
switch(mmio.dd) {
|
||||
case DMA::DestBWRAM:
|
||||
if((dda & 0x40e000) == 0x006000 || (dda & 0xf00000) == 0x400000) {
|
||||
busWrite(dda, data);
|
||||
}
|
||||
break;
|
||||
if(mmio.sd == DMA::SourceROM && mmio.dd == DMA::DestIRAM) {
|
||||
step(iram.conflict() ? 6 : 4);
|
||||
data = rom.readSA1(source, data);
|
||||
iram.write(target, data);
|
||||
}
|
||||
|
||||
case DMA::DestIRAM:
|
||||
iram.write(dda & 0x07ff, data);
|
||||
break;
|
||||
if(mmio.sd == DMA::SourceBWRAM && mmio.dd == DMA::DestIRAM) {
|
||||
step(bwram.conflict() ? 8 : iram.conflict() ? 6 : 4);
|
||||
data = bwram.read(source, data);
|
||||
iram.write(target, data);
|
||||
}
|
||||
|
||||
if(mmio.sd == DMA::SourceIRAM && mmio.dd == DMA::DestBWRAM) {
|
||||
step(bwram.conflict() ? 8 : iram.conflict() ? 6 : 4);
|
||||
data = iram.read(source, data);
|
||||
bwram.write(target, data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,17 +34,9 @@ auto SA1::dmaNormal() -> void {
|
||||
if(mmio.dma_irqen) mmio.dma_irqcl = 0;
|
||||
}
|
||||
|
||||
//((byte & 6) << 3) + (byte & 1) explanation:
|
||||
//transforms a byte index (0-7) into a planar index:
|
||||
//result[] = { 0, 1, 16, 17, 32, 33, 48, 49 };
|
||||
//works for 2bpp, 4bpp and 8bpp modes
|
||||
|
||||
//===========================
|
||||
//type-1 character conversion
|
||||
//===========================
|
||||
|
||||
auto SA1::dmaCC1() -> void {
|
||||
cpubwram.dma = true;
|
||||
bwram.dma = true;
|
||||
mmio.chdma_irqfl = true;
|
||||
if(mmio.chdma_irqen) {
|
||||
mmio.chdma_irqcl = 0;
|
||||
@ -65,6 +44,12 @@ auto SA1::dmaCC1() -> void {
|
||||
}
|
||||
}
|
||||
|
||||
//((byte & 6) << 3) + (byte & 1) explanation:
|
||||
//transforms a byte index (0-7) into a planar index:
|
||||
//result[] = {0, 1, 16, 17, 32, 33, 48, 49};
|
||||
//works for 2bpp, 4bpp and 8bpp modes
|
||||
|
||||
//type-1 character conversion
|
||||
auto SA1::dmaCC1Read(uint addr) -> uint8 {
|
||||
//16 bytes/char (2bpp); 32 bytes/char (4bpp); 64 bytes/char (8bpp)
|
||||
uint charmask = (1 << (6 - mmio.dmacb)) - 1;
|
||||
@ -88,16 +73,16 @@ auto SA1::dmaCC1Read(uint addr) -> uint8 {
|
||||
|
||||
uint8 out[] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
for(auto x : range(8)) {
|
||||
out[0] |= (data & 1) << (7 - x); data >>= 1;
|
||||
out[1] |= (data & 1) << (7 - x); data >>= 1;
|
||||
out[0] |= (data & 1) << 7 - x; data >>= 1;
|
||||
out[1] |= (data & 1) << 7 - x; data >>= 1;
|
||||
if(mmio.dmacb == 2) continue;
|
||||
out[2] |= (data & 1) << (7 - x); data >>= 1;
|
||||
out[3] |= (data & 1) << (7 - x); data >>= 1;
|
||||
out[2] |= (data & 1) << 7 - x; data >>= 1;
|
||||
out[3] |= (data & 1) << 7 - x; data >>= 1;
|
||||
if(mmio.dmacb == 1) continue;
|
||||
out[4] |= (data & 1) << (7 - x); data >>= 1;
|
||||
out[5] |= (data & 1) << (7 - x); data >>= 1;
|
||||
out[6] |= (data & 1) << (7 - x); data >>= 1;
|
||||
out[7] |= (data & 1) << (7 - x); data >>= 1;
|
||||
out[4] |= (data & 1) << 7 - x; data >>= 1;
|
||||
out[5] |= (data & 1) << 7 - x; data >>= 1;
|
||||
out[6] |= (data & 1) << 7 - x; data >>= 1;
|
||||
out[7] |= (data & 1) << 7 - x; data >>= 1;
|
||||
}
|
||||
|
||||
for(auto byte : range(bpp)) {
|
||||
@ -110,10 +95,7 @@ auto SA1::dmaCC1Read(uint addr) -> uint8 {
|
||||
return iram.read((mmio.dda + (addr & charmask)) & 0x07ff);
|
||||
}
|
||||
|
||||
//===========================
|
||||
//type-2 character conversion
|
||||
//===========================
|
||||
|
||||
auto SA1::dmaCC2() -> void {
|
||||
//select register file index (0-7 or 8-15)
|
||||
const uint8* brf = &mmio.brf[(dma.line & 1) << 3];
|
||||
|
@ -54,9 +54,9 @@ auto SA1::readIO(uint24 addr, uint8) -> uint8 {
|
||||
//(VDPL) variable-length data read port low
|
||||
case 0x230c: {
|
||||
uint24 data;
|
||||
data.byte(0) = vbrRead(mmio.va + 0);
|
||||
data.byte(1) = vbrRead(mmio.va + 1);
|
||||
data.byte(2) = vbrRead(mmio.va + 2);
|
||||
data.byte(0) = readVBR(mmio.va + 0);
|
||||
data.byte(1) = readVBR(mmio.va + 1);
|
||||
data.byte(2) = readVBR(mmio.va + 2);
|
||||
data >>= mmio.vbit;
|
||||
|
||||
return data >> 0;
|
||||
@ -65,9 +65,9 @@ auto SA1::readIO(uint24 addr, uint8) -> uint8 {
|
||||
//(VDPH) variable-length data read port high
|
||||
case 0x230d: {
|
||||
uint24 data;
|
||||
data.byte(0) = vbrRead(mmio.va + 0);
|
||||
data.byte(1) = vbrRead(mmio.va + 1);
|
||||
data.byte(2) = vbrRead(mmio.va + 2);
|
||||
data.byte(0) = readVBR(mmio.va + 0);
|
||||
data.byte(1) = readVBR(mmio.va + 1);
|
||||
data.byte(2) = readVBR(mmio.va + 2);
|
||||
data >>= mmio.vbit;
|
||||
|
||||
if(mmio.hl == 1) {
|
||||
@ -82,7 +82,7 @@ auto SA1::readIO(uint24 addr, uint8) -> uint8 {
|
||||
|
||||
//(VC) version code register
|
||||
case 0x230e: {
|
||||
return 0x01; //true value unknown
|
||||
return 0x23; //RF5A123
|
||||
}
|
||||
|
||||
}
|
||||
@ -333,7 +333,7 @@ auto SA1::writeIO(uint24 addr, uint8 data) -> void {
|
||||
mmio.dmasize = (data >> 2) & 7;
|
||||
mmio.dmacb = (data & 0x03);
|
||||
|
||||
if(mmio.chdend) cpubwram.dma = false;
|
||||
if(mmio.chdend) bwram.dma = false;
|
||||
if(mmio.dmasize > 5) mmio.dmasize = 5;
|
||||
if(mmio.dmacb > 2) mmio.dmacb = 2;
|
||||
return;
|
||||
|
33
higan/sfc/coprocessor/sa1/iram.cpp
Normal file
33
higan/sfc/coprocessor/sa1/iram.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
auto SA1::IRAM::conflict() const -> bool {
|
||||
if(!cpu.r.rwb) return false;
|
||||
if((cpu.r.mar & 0x40f800) == 0x003000) return true; //00-3f,80-bf:3000-37ff
|
||||
return false;
|
||||
}
|
||||
|
||||
auto SA1::IRAM::read(uint24 address, uint8 data) -> uint8 {
|
||||
if(!size()) return data;
|
||||
return _data[address & size() - 1];
|
||||
}
|
||||
|
||||
auto SA1::IRAM::write(uint24 address, uint8 data) -> void {
|
||||
if(!size()) return;
|
||||
_data[address & size() - 1] = data;
|
||||
}
|
||||
|
||||
auto SA1::IRAM::readCPU(uint24 address, uint8 data) -> uint8 {
|
||||
cpu.synchronize(sa1);
|
||||
return read(address, data);
|
||||
}
|
||||
|
||||
auto SA1::IRAM::writeCPU(uint24 address, uint8 data) -> void {
|
||||
cpu.synchronize(sa1);
|
||||
return write(address, data);
|
||||
}
|
||||
|
||||
auto SA1::IRAM::readSA1(uint24 address, uint8 data) -> uint8 {
|
||||
return read(address, data);
|
||||
}
|
||||
|
||||
auto SA1::IRAM::writeSA1(uint24 address, uint8 data) -> void {
|
||||
return write(address, data);
|
||||
}
|
@ -1,270 +1,169 @@
|
||||
auto SA1::busRead(uint24 addr, uint8 data) -> uint8 {
|
||||
if((addr & 0x40fe00) == 0x002200) { //$00-3f,80-bf:2200-23ff
|
||||
return readIO(addr, data);
|
||||
auto SA1::idle() -> void {
|
||||
r.rwb = 0;
|
||||
step(2);
|
||||
}
|
||||
|
||||
//RTx, JMx, JSx
|
||||
auto SA1::idleJump() -> void {
|
||||
//ROM access penalty cycle: does not apply to BWRAM or IRAM
|
||||
if((r.pc & 0x408000) == 0x008000 || (r.pc & 0xc00000) == 0xc00000) idle();
|
||||
}
|
||||
|
||||
//Bxx
|
||||
auto SA1::idleBranch() -> void {
|
||||
if(r.pc & 1) idleJump();
|
||||
}
|
||||
|
||||
auto SA1::read(uint24 address) -> uint8 {
|
||||
r.rwb = 1;
|
||||
r.mar = address;
|
||||
uint8 data = r.mdr;
|
||||
|
||||
//00-3f,80-bf:2200-23ff
|
||||
if((address & 0x40fe00) == 0x002200) {
|
||||
step(2);
|
||||
return r.mdr = readIO(address, data);
|
||||
}
|
||||
|
||||
if((addr & 0x408000) == 0x008000) { //$00-3f,80-bf:8000-ffff
|
||||
addr = ((addr & 0x800000) >> 2) | ((addr & 0x3f0000) >> 1) | (addr & 0x7fff);
|
||||
return mmcromRead(addr, data);
|
||||
//00-3f,80-bf:8000-ffff
|
||||
if((address & 0x408000) == 0x008000) {
|
||||
step(rom.conflict() ? 4 : 2);
|
||||
return r.mdr = rom.readSA1(address, data);
|
||||
}
|
||||
|
||||
if((addr & 0xc00000) == 0xc00000) { //$c0-ff:0000-ffff
|
||||
return mmcromRead(addr, data);
|
||||
//c0-ff:0000-ffff
|
||||
if((address & 0xc00000) == 0xc00000) {
|
||||
step(rom.conflict() ? 4 : 2);
|
||||
return r.mdr = rom.readSA1(address, data);
|
||||
}
|
||||
|
||||
if((addr & 0x40e000) == 0x006000) { //$00-3f,80-bf:6000-7fff
|
||||
return mmcSA1Read(addr, data);
|
||||
//00-3f,80-bf:6000-7fff
|
||||
if((address & 0x40e000) == 0x006000) {
|
||||
step(bwram.conflict() ? 8 : 4);
|
||||
return r.mdr = bwram.readSA1(address, data);
|
||||
}
|
||||
|
||||
if((addr & 0x40f800) == 0x000000) { //$00-3f,80-bf:0000-07ff
|
||||
synchronize(cpu);
|
||||
return iram.read(addr & 2047, data);
|
||||
//00-3f,80-bf:0000-07ff
|
||||
if((address & 0x40f800) == 0x000000) {
|
||||
step(iram.conflict() ? 6 : 2);
|
||||
return r.mdr = iram.readSA1(address, data);
|
||||
}
|
||||
|
||||
if((addr & 0x40f800) == 0x003000) { //$00-3f,80-bf:3000-37ff
|
||||
synchronize(cpu);
|
||||
return iram.read(addr & 2047, data);
|
||||
//00-3f,80-bf:3000-37ff
|
||||
if((address & 0x40f800) == 0x003000) {
|
||||
step(iram.conflict() ? 6 : 2);
|
||||
return r.mdr = iram.readSA1(address, data);
|
||||
}
|
||||
|
||||
if((addr & 0xf00000) == 0x400000) { //$40-4f:0000-ffff
|
||||
synchronize(cpu);
|
||||
return bwram.read(addr & (bwram.size() - 1), data);
|
||||
//40-4f:0000-ffff
|
||||
if((address & 0xf00000) == 0x400000) {
|
||||
step(bwram.conflict() ? 8 : 4);
|
||||
return r.mdr = bwram.readLinear(address, data);
|
||||
}
|
||||
|
||||
if((addr & 0xf00000) == 0x600000) { //$60-6f:0000-ffff
|
||||
synchronize(cpu);
|
||||
return bitmapRead(addr & 0x0fffff, data);
|
||||
//60-6f:0000-ffff
|
||||
if((address & 0xf00000) == 0x600000) {
|
||||
step(bwram.conflict() ? 8 : 4);
|
||||
return r.mdr = bwram.readBitmap(address, data);
|
||||
}
|
||||
|
||||
//unmapped region
|
||||
step(2);
|
||||
return data;
|
||||
}
|
||||
|
||||
auto SA1::busWrite(uint24 addr, uint8 data) -> void {
|
||||
if((addr & 0x40fe00) == 0x002200) { //$00-3f,80-bf:2200-23ff
|
||||
return writeIO(addr, data);
|
||||
auto SA1::write(uint24 address, uint8 data) -> void {
|
||||
r.rwb = 1;
|
||||
r.mar = address;
|
||||
r.mdr = data;
|
||||
|
||||
//00-3f,80-bf:2200-23ff
|
||||
if((address & 0x40fe00) == 0x002200) {
|
||||
step(2);
|
||||
return writeIO(address, data);
|
||||
}
|
||||
|
||||
if((addr & 0x40e000) == 0x006000) { //$00-3f,80-bf:6000-7fff
|
||||
return mmcSA1Write(addr, data);
|
||||
//00-3f,80-bf:8000-ffff
|
||||
if((address & 0x408000) == 0x008000) {
|
||||
step(rom.conflict() ? 4 : 2);
|
||||
return rom.writeSA1(address, data);
|
||||
}
|
||||
|
||||
if((addr & 0x40f800) == 0x000000) { //$00-3f,80-bf:0000-07ff
|
||||
synchronize(cpu);
|
||||
return iram.write(addr & 2047, data);
|
||||
//c0-ff:0000-ffff
|
||||
if((address & 0xc00000) == 0xc00000) {
|
||||
step(rom.conflict() ? 4 : 2);
|
||||
return rom.writeSA1(address, data);
|
||||
}
|
||||
|
||||
if((addr & 0x40f800) == 0x003000) { //$00-3f,80-bf:3000-37ff
|
||||
synchronize(cpu);
|
||||
return iram.write(addr & 2047, data);
|
||||
//00-3f,80-bf:6000-7fff
|
||||
if((address & 0x40e000) == 0x006000) {
|
||||
step(bwram.conflict() ? 8 : 4);
|
||||
return bwram.writeSA1(address, data);
|
||||
}
|
||||
|
||||
if((addr & 0xf00000) == 0x400000) { //$40-4f:0000-ffff
|
||||
synchronize(cpu);
|
||||
return bwram.write(addr & (bwram.size() - 1), data);
|
||||
//00-3f,80-bf:0000-07ff
|
||||
if((address & 0x40f800) == 0x000000) {
|
||||
step(iram.conflict() ? 6 : 2);
|
||||
return iram.writeSA1(address, data);
|
||||
}
|
||||
|
||||
if((addr & 0xf00000) == 0x600000) { //$60-6f:0000-ffff
|
||||
synchronize(cpu);
|
||||
return bitmapWrite(addr & 0x0fffff, data);
|
||||
//00-3f,80-bf:3000-37ff
|
||||
if((address & 0x40f800) == 0x003000) {
|
||||
step(iram.conflict() ? 6 : 2);
|
||||
return iram.writeSA1(address, data);
|
||||
}
|
||||
|
||||
//40-4f:0000-ffff
|
||||
if((address & 0xf00000) == 0x400000) {
|
||||
step(bwram.conflict() ? 8 : 4);
|
||||
return bwram.writeLinear(address, data);
|
||||
}
|
||||
|
||||
//60-6f:0000-ffff
|
||||
if((address & 0xf00000) == 0x600000) {
|
||||
step(bwram.conflict() ? 8 : 4);
|
||||
return bwram.writeBitmap(address, data);
|
||||
}
|
||||
|
||||
//unmapped region
|
||||
step(2);
|
||||
return;
|
||||
}
|
||||
|
||||
//$230c (VDPL), $230d (VDPH) use this bus to read variable-length data.
|
||||
//this is used both to keep VBR-reads from accessing MMIO registers, and
|
||||
//to avoid syncing the S-CPU and SA-1*; as both chips are able to access
|
||||
//these ports.
|
||||
auto SA1::vbrRead(uint24 addr, uint8 data) -> uint8 {
|
||||
if((addr & 0x408000) == 0x008000) { //$00-3f,80-bf:8000-ffff
|
||||
addr = ((addr & 0x800000) >> 2) | ((addr & 0x3f0000) >> 1) | (addr & 0x7fff);
|
||||
return mmcromRead(addr, data);
|
||||
auto SA1::readVBR(uint24 address, uint8 data) -> uint8 {
|
||||
//00-3f,80-bf:8000-ffff
|
||||
if((address & 0x408000) == 0x008000) {
|
||||
return rom.readSA1(address, data);
|
||||
}
|
||||
|
||||
if((addr & 0xc00000) == 0xc00000) { //$c0-ff:0000-ffff
|
||||
return mmcromRead(addr, data);
|
||||
//c0-ff:0000-ffff
|
||||
if((address & 0xc00000) == 0xc00000) {
|
||||
return rom.readSA1(address, data);
|
||||
}
|
||||
|
||||
if((addr & 0x40e000) == 0x006000) { //$00-3f,80-bf:6000-7fff
|
||||
return bwram.read(addr & (bwram.size() - 1), data);
|
||||
//00-3f,80-bf:6000-7fff
|
||||
if((address & 0x40e000) == 0x006000) {
|
||||
return bwram.read(address, data);
|
||||
}
|
||||
|
||||
if((addr & 0xf00000) == 0x400000) { //$40-4f:0000-ffff
|
||||
return bwram.read(addr & (bwram.size() - 1), data);
|
||||
//40-4f:0000-ffff
|
||||
if((address & 0xf00000) == 0x400000) {
|
||||
return bwram.read(address, data);
|
||||
}
|
||||
|
||||
if((addr & 0x40f800) == 0x000000) { //$00-3f,80-bf:0000-07ff
|
||||
return iram.read(addr & 2047, data);
|
||||
//00-3f,80-bf:0000-07ff
|
||||
if((address & 0x40f800) == 0x000000) {
|
||||
return iram.read(address, data);
|
||||
}
|
||||
|
||||
if((addr & 0x40f800) == 0x003000) { //$00-3f,80-bf:3000-37ff
|
||||
return iram.read(addr & 2047, data);
|
||||
//00-3f,80-bf:3000-37ff
|
||||
if((address & 0x40f800) == 0x003000) {
|
||||
return iram.read(address, data);
|
||||
}
|
||||
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
//ROM, I-RAM and MMIO registers are accessed at ~10.74MHz (2 clock ticks)
|
||||
//BW-RAM is accessed at ~5.37MHz (4 clock ticks)
|
||||
//tick() == 2 clock ticks
|
||||
//note: bus conflict delays are not emulated at this time
|
||||
|
||||
auto SA1::idle() -> void {
|
||||
tick();
|
||||
}
|
||||
|
||||
auto SA1::read(uint24 addr) -> uint8 {
|
||||
tick();
|
||||
if(((addr & 0x40e000) == 0x006000) || ((addr & 0xd00000) == 0x400000)) tick();
|
||||
return busRead(addr, r.mdr);
|
||||
}
|
||||
|
||||
auto SA1::write(uint24 addr, uint8 data) -> void {
|
||||
tick();
|
||||
if(((addr & 0x40e000) == 0x006000) || ((addr & 0xd00000) == 0x400000)) tick();
|
||||
busWrite(addr, r.mdr = data);
|
||||
}
|
||||
|
||||
//note: addresses are translated prior to invoking this function:
|
||||
//$00-3f,80-bf:8000-ffff mask=0x408000 => $00-3f:0000-ffff
|
||||
//$c0-ff:0000-ffff mask=0
|
||||
auto SA1::mmcromRead(uint24 addr, uint8) -> uint8 {
|
||||
//reset vector overrides
|
||||
if((addr & 0xffffe0) == 0x007fe0) { //$00:ffe0-ffef
|
||||
if(addr == 0x7fea && sa1.mmio.cpu_nvsw) return sa1.mmio.snv >> 0;
|
||||
if(addr == 0x7feb && sa1.mmio.cpu_nvsw) return sa1.mmio.snv >> 8;
|
||||
if(addr == 0x7fee && sa1.mmio.cpu_ivsw) return sa1.mmio.siv >> 0;
|
||||
if(addr == 0x7fef && sa1.mmio.cpu_ivsw) return sa1.mmio.siv >> 8;
|
||||
}
|
||||
|
||||
static auto read = [](uint addr) {
|
||||
if((addr & 0x400000) && bsmemory.size()) return bsmemory.read(addr, 0x00);
|
||||
return sa1.rom.read(bus.mirror(addr, sa1.rom.size()));
|
||||
};
|
||||
|
||||
bool lo = addr < 0x400000; //*bmode==0 only applies to $00-3f,80-bf:8000-ffff
|
||||
addr &= 0x3fffff;
|
||||
|
||||
if(addr < 0x100000) { //$00-1f,8000-ffff; $c0-cf:0000-ffff
|
||||
if(lo && mmio.cbmode == 0) return read(addr);
|
||||
return read((mmio.cb << 20) | (addr & 0x0fffff));
|
||||
}
|
||||
|
||||
if(addr < 0x200000) { //$20-3f,8000-ffff; $d0-df:0000-ffff
|
||||
if(lo && mmio.dbmode == 0) return read(addr);
|
||||
return read((mmio.db << 20) | (addr & 0x0fffff));
|
||||
}
|
||||
|
||||
if(addr < 0x300000) { //$80-9f,8000-ffff; $e0-ef:0000-ffff
|
||||
if(lo && mmio.ebmode == 0) return read(addr);
|
||||
return read((mmio.eb << 20) | (addr & 0x0fffff));
|
||||
}
|
||||
|
||||
if(addr < 0x400000) { //$a0-bf,8000-ffff; $f0-ff:0000-ffff
|
||||
if(lo && mmio.fbmode == 0) return read(addr);
|
||||
return read((mmio.fb << 20) | (addr & 0x0fffff));
|
||||
}
|
||||
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
auto SA1::mmcromWrite(uint24 addr, uint8 data) -> void {
|
||||
}
|
||||
|
||||
auto SA1::mmcbwramRead(uint24 addr, uint8 data) -> uint8 {
|
||||
if(addr < 0x2000) { //$00-3f,80-bf:6000-7fff
|
||||
cpu.synchronize(sa1);
|
||||
addr = bus.mirror(mmio.sbm * 0x2000 + (addr & 0x1fff), cpubwram.size());
|
||||
return cpubwram.read(addr);
|
||||
}
|
||||
|
||||
if((addr & 0xf00000) == 0x400000) { //$40-4f:0000-ffff
|
||||
return cpubwram.read(addr & 0x0fffff);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
auto SA1::mmcbwramWrite(uint24 addr, uint8 data) -> void {
|
||||
if(addr < 0x2000) { //$00-3f,80-bf:6000-7fff
|
||||
cpu.synchronize(sa1);
|
||||
addr = bus.mirror(mmio.sbm * 0x2000 + (addr & 0x1fff), cpubwram.size());
|
||||
return cpubwram.write(addr, data);
|
||||
}
|
||||
|
||||
if((addr & 0xf00000) == 0x400000) { //$40-4f:0000-ffff
|
||||
return cpubwram.write(addr & 0x0fffff, data);
|
||||
}
|
||||
}
|
||||
|
||||
auto SA1::mmcSA1Read(uint addr, uint8 data) -> uint8 {
|
||||
synchronize(cpu);
|
||||
if(mmio.sw46 == 0) {
|
||||
//$40-43:0000-ffff x 32 projection
|
||||
addr = bus.mirror((mmio.cbm & 0x1f) * 0x2000 + (addr & 0x1fff), bwram.size());
|
||||
return bwram.read(addr, data);
|
||||
} else {
|
||||
//$60-6f:0000-ffff x 128 projection
|
||||
addr = bus.mirror(mmio.cbm * 0x2000 + (addr & 0x1fff), 0x100000);
|
||||
return bitmapRead(addr, data);
|
||||
}
|
||||
}
|
||||
|
||||
auto SA1::mmcSA1Write(uint addr, uint8 data) -> void {
|
||||
synchronize(cpu);
|
||||
if(mmio.sw46 == 0) {
|
||||
//$40-43:0000-ffff x 32 projection
|
||||
addr = bus.mirror((mmio.cbm & 0x1f) * 0x2000 + (addr & 0x1fff), bwram.size());
|
||||
bwram.write(addr, data);
|
||||
} else {
|
||||
//$60-6f:0000-ffff x 128 projection
|
||||
addr = bus.mirror(mmio.cbm * 0x2000 + (addr & 0x1fff), 0x100000);
|
||||
bitmapWrite(addr, data);
|
||||
}
|
||||
}
|
||||
|
||||
auto SA1::bitmapRead(uint addr, uint8 data) -> uint8 {
|
||||
if(mmio.bbf == 0) {
|
||||
//4bpp
|
||||
uint shift = addr & 1;
|
||||
addr = (addr >> 1) & (bwram.size() - 1);
|
||||
switch(shift) {
|
||||
case 0: return (bwram.read(addr) >> 0) & 15;
|
||||
case 1: return (bwram.read(addr) >> 4) & 15;
|
||||
}
|
||||
} else {
|
||||
//2bpp
|
||||
uint shift = addr & 3;
|
||||
addr = (addr >> 2) & (bwram.size() - 1);
|
||||
switch(shift) {
|
||||
case 0: return (bwram.read(addr) >> 0) & 3;
|
||||
case 1: return (bwram.read(addr) >> 2) & 3;
|
||||
case 2: return (bwram.read(addr) >> 4) & 3;
|
||||
case 3: return (bwram.read(addr) >> 6) & 3;
|
||||
}
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto SA1::bitmapWrite(uint addr, uint8 data) -> void {
|
||||
if(mmio.bbf == 0) {
|
||||
//4bpp
|
||||
uint shift = addr & 1;
|
||||
addr = (addr >> 1) & (bwram.size() - 1);
|
||||
switch(shift) {
|
||||
case 0: data = (bwram.read(addr) & 0xf0) | ((data & 15) << 0); break;
|
||||
case 1: data = (bwram.read(addr) & 0x0f) | ((data & 15) << 4); break;
|
||||
}
|
||||
} else {
|
||||
//2bpp
|
||||
uint shift = addr & 3;
|
||||
addr = (addr >> 2) & (bwram.size() - 1);
|
||||
switch(shift) {
|
||||
case 0: data = (bwram.read(addr) & 0xfc) | ((data & 3) << 0); break;
|
||||
case 1: data = (bwram.read(addr) & 0xf3) | ((data & 3) << 2); break;
|
||||
case 2: data = (bwram.read(addr) & 0xcf) | ((data & 3) << 4); break;
|
||||
case 3: data = (bwram.read(addr) & 0x3f) | ((data & 3) << 6); break;
|
||||
}
|
||||
}
|
||||
|
||||
bwram.write(addr, data);
|
||||
}
|
||||
|
74
higan/sfc/coprocessor/sa1/rom.cpp
Normal file
74
higan/sfc/coprocessor/sa1/rom.cpp
Normal file
@ -0,0 +1,74 @@
|
||||
auto SA1::ROM::conflict() const -> bool {
|
||||
if(!cpu.r.rwb) return false;
|
||||
if((cpu.r.mar & 0x408000) == 0x008000) return true; //00-3f,80-bf:8000-ffff
|
||||
if((cpu.r.mar & 0xc00000) == 0xc00000) return true; //c0-ff:0000-ffff
|
||||
return false;
|
||||
}
|
||||
|
||||
auto SA1::ROM::read(uint24 address, uint8 data) -> uint8 {
|
||||
address = bus.mirror(address, size());
|
||||
return _data[address];
|
||||
}
|
||||
|
||||
auto SA1::ROM::write(uint24 address, uint8 data) -> void {
|
||||
}
|
||||
|
||||
//note: addresses are translated prior to invoking this function:
|
||||
//00-3f,80-bf:8000-ffff mask=0x408000 => 00-3f:0000-ffff
|
||||
//c0-ff:0000-ffff => untranslated
|
||||
auto SA1::ROM::readCPU(uint24 address, uint8 data) -> uint8 {
|
||||
//reset vector overrides
|
||||
if((address & 0xffffe0) == 0x007fe0) { //00:ffe0-ffef
|
||||
if(address == 0x7fea && sa1.mmio.cpu_nvsw) return sa1.mmio.snv >> 0;
|
||||
if(address == 0x7feb && sa1.mmio.cpu_nvsw) return sa1.mmio.snv >> 8;
|
||||
if(address == 0x7fee && sa1.mmio.cpu_ivsw) return sa1.mmio.siv >> 0;
|
||||
if(address == 0x7fef && sa1.mmio.cpu_ivsw) return sa1.mmio.siv >> 8;
|
||||
}
|
||||
|
||||
static auto read = [](uint address) {
|
||||
if((address & 0x400000) && bsmemory.size()) return bsmemory.read(address, 0x00);
|
||||
return sa1.rom.read(address);
|
||||
};
|
||||
|
||||
bool lo = address < 0x400000; //*bmode==0 only applies to 00-3f,80-bf:8000-ffff
|
||||
address &= 0x3fffff;
|
||||
|
||||
if(address < 0x100000) { //00-1f,8000-ffff; c0-cf:0000-ffff
|
||||
if(lo && sa1.mmio.cbmode == 0) return read(address);
|
||||
return read(sa1.mmio.cb << 20 | address & 0x0fffff);
|
||||
}
|
||||
|
||||
if(address < 0x200000) { //20-3f,8000-ffff; d0-df:0000-ffff
|
||||
if(lo && sa1.mmio.dbmode == 0) return read(address);
|
||||
return read(sa1.mmio.db << 20 | address & 0x0fffff);
|
||||
}
|
||||
|
||||
if(address < 0x300000) { //80-9f,8000-ffff; e0-ef:0000-ffff
|
||||
if(lo && sa1.mmio.ebmode == 0) return read(address);
|
||||
return read(sa1.mmio.eb << 20 | address & 0x0fffff);
|
||||
}
|
||||
|
||||
if(address < 0x400000) { //a0-bf,8000-ffff; f0-ff:0000-ffff
|
||||
if(lo && sa1.mmio.fbmode == 0) return read(address);
|
||||
return read(sa1.mmio.fb << 20 | address & 0x0fffff);
|
||||
}
|
||||
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
auto SA1::ROM::writeCPU(uint24 address, uint8 data) -> void {
|
||||
}
|
||||
|
||||
auto SA1::ROM::readSA1(uint24 address, uint8 data) -> uint8 {
|
||||
if((address & 0x408000) == 0x008000) {
|
||||
address = (address & 0x800000) >> 2 | (address & 0x3f0000) >> 1 | address & 0x007fff;
|
||||
}
|
||||
return readCPU(address, data);
|
||||
}
|
||||
|
||||
auto SA1::ROM::writeSA1(uint24 address, uint8 data) -> void {
|
||||
if((address & 0x408000) == 0x008000) {
|
||||
address = (address & 0x800000) >> 2 | (address & 0x3f0000) >> 1 | address & 0x007fff;
|
||||
}
|
||||
return writeCPU(address, data);
|
||||
}
|
@ -2,7 +2,9 @@
|
||||
|
||||
namespace SuperFamicom {
|
||||
|
||||
#include "bus.cpp"
|
||||
#include "rom.cpp"
|
||||
#include "bwram.cpp"
|
||||
#include "iram.cpp"
|
||||
#include "dma.cpp"
|
||||
#include "memory.cpp"
|
||||
#include "io.cpp"
|
||||
@ -19,8 +21,7 @@ auto SA1::main() -> void {
|
||||
|
||||
if(mmio.sa1_rdyb || mmio.sa1_resb) {
|
||||
//SA-1 co-processor is asleep
|
||||
tick();
|
||||
synchronize(cpu);
|
||||
step(2);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -81,23 +82,23 @@ auto SA1::synchronizing() const -> bool {
|
||||
return scheduler.synchronizing();
|
||||
}
|
||||
|
||||
auto SA1::tick() -> void {
|
||||
step(2);
|
||||
if(++status.counter == 0) synchronize(cpu);
|
||||
auto SA1::step(uint clocks) -> void {
|
||||
Thread::step(clocks);
|
||||
synchronize(cpu);
|
||||
|
||||
//adjust counters:
|
||||
//note that internally, status counters are in clocks;
|
||||
//whereas MMIO register counters are in dots (4 clocks = 1 dot)
|
||||
if(mmio.hvselb == 0) {
|
||||
//HV timer
|
||||
status.hcounter += 2;
|
||||
if(status.hcounter >= 1364) {
|
||||
status.hcounter = 0;
|
||||
status.hcounter += clocks;
|
||||
while(status.hcounter >= 1364) {
|
||||
status.hcounter -= 1364;
|
||||
if(++status.vcounter >= status.scanlines) status.vcounter = 0;
|
||||
}
|
||||
} else {
|
||||
//linear timer
|
||||
status.hcounter += 2;
|
||||
status.hcounter += clocks;
|
||||
status.vcounter += (status.hcounter >> 11);
|
||||
status.hcounter &= 0x07ff;
|
||||
status.vcounter &= 0x01ff;
|
||||
@ -106,9 +107,9 @@ auto SA1::tick() -> void {
|
||||
//test counters for timer IRQ
|
||||
switch((mmio.ven << 1) + (mmio.hen << 0)) {
|
||||
case 0: break;
|
||||
case 1: if(status.hcounter == (mmio.hcnt << 2)) triggerIRQ(); break;
|
||||
case 1: if(status.hcounter == mmio.hcnt << 2) triggerIRQ(); break;
|
||||
case 2: if(status.vcounter == mmio.vcnt && status.hcounter == 0) triggerIRQ(); break;
|
||||
case 3: if(status.vcounter == mmio.vcnt && status.hcounter == (mmio.hcnt << 2)) triggerIRQ(); break;
|
||||
case 3: if(status.vcounter == mmio.vcnt && status.hcounter == mmio.hcnt << 2) triggerIRQ(); break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,7 +132,7 @@ auto SA1::power() -> void {
|
||||
bwram.writeProtect(false);
|
||||
iram.writeProtect(false);
|
||||
|
||||
cpubwram.dma = false;
|
||||
bwram.dma = false;
|
||||
for(auto addr : range(iram.size())) {
|
||||
iram.write(addr, 0x00);
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
//Super Accelerator 1
|
||||
|
||||
struct SA1 : Processor::WDC65816, Thread {
|
||||
//sa1.cpp
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto tick() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
auto interrupt() -> void override;
|
||||
|
||||
alwaysinline auto triggerIRQ() -> void;
|
||||
@ -13,20 +15,6 @@ struct SA1 : Processor::WDC65816, Thread {
|
||||
auto unload() -> void;
|
||||
auto power() -> void;
|
||||
|
||||
//bus.cpp
|
||||
struct CPUIRAM : Memory {
|
||||
auto size() const -> uint;
|
||||
alwaysinline auto read(uint24, uint8 = 0) -> uint8;
|
||||
alwaysinline auto write(uint24, uint8) -> void;
|
||||
};
|
||||
|
||||
struct CPUBWRAM : Memory {
|
||||
auto size() const -> uint;
|
||||
alwaysinline auto read(uint24, uint8 = 0) -> uint8;
|
||||
alwaysinline auto write(uint24, uint8) -> void;
|
||||
bool dma;
|
||||
};
|
||||
|
||||
//dma.cpp
|
||||
struct DMA {
|
||||
enum CDEN : uint { DmaNormal = 0, DmaCharConversion = 1 };
|
||||
@ -41,25 +29,16 @@ struct SA1 : Processor::WDC65816, Thread {
|
||||
auto dmaCC2() -> void;
|
||||
|
||||
//memory.cpp
|
||||
auto busRead(uint24 addr, uint8 data) -> uint8;
|
||||
auto busWrite(uint24 addr, uint8 data) -> void;
|
||||
auto vbrRead(uint24 addr, uint8 data = 0) -> uint8;
|
||||
alwaysinline auto conflictROM() const -> bool;
|
||||
alwaysinline auto conflictBWRAM() const -> bool;
|
||||
alwaysinline auto conflictIRAM() const -> bool;
|
||||
|
||||
alwaysinline auto idle() -> void override;
|
||||
alwaysinline auto idleJump() -> void override;
|
||||
alwaysinline auto idleBranch() -> void override;
|
||||
alwaysinline auto read(uint24 addr) -> uint8 override;
|
||||
alwaysinline auto write(uint24 addr, uint8 data) -> void override;
|
||||
|
||||
auto mmcromRead(uint24 addr, uint8 data) -> uint8;
|
||||
auto mmcromWrite(uint24 addr, uint8 data) -> void;
|
||||
|
||||
auto mmcbwramRead(uint24 addr, uint8 data) -> uint8;
|
||||
auto mmcbwramWrite(uint24 addr, uint8 data) -> void;
|
||||
|
||||
auto mmcSA1Read(uint addr, uint8 data) -> uint8;
|
||||
auto mmcSA1Write(uint addr, uint8 data) -> void;
|
||||
|
||||
auto bitmapRead(uint addr, uint8 data) -> uint8;
|
||||
auto bitmapWrite(uint addr, uint8 data) -> void;
|
||||
auto readVBR(uint24 addr, uint8 data = 0) -> uint8;
|
||||
|
||||
//io.cpp
|
||||
auto readIO(uint24 addr, uint8 data) -> uint8;
|
||||
@ -68,12 +47,55 @@ struct SA1 : Processor::WDC65816, Thread {
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
MappedRAM rom;
|
||||
MappedRAM iram;
|
||||
MappedRAM bwram;
|
||||
struct ROM : MappedRAM {
|
||||
//rom.cpp
|
||||
alwaysinline auto conflict() const -> bool;
|
||||
|
||||
CPUIRAM cpuiram;
|
||||
CPUBWRAM cpubwram;
|
||||
alwaysinline auto read(uint24 address, uint8 data = 0) -> uint8 override;
|
||||
alwaysinline auto write(uint24 address, uint8 data) -> void override;
|
||||
|
||||
auto readCPU(uint24 address, uint8 data = 0) -> uint8;
|
||||
auto writeCPU(uint24 address, uint8 data) -> void;
|
||||
|
||||
auto readSA1(uint24 address, uint8 data = 0) -> uint8;
|
||||
auto writeSA1(uint24 address, uint8 data) -> void;
|
||||
} rom;
|
||||
|
||||
struct BWRAM : MappedRAM {
|
||||
//bwram.cpp
|
||||
alwaysinline auto conflict() const -> bool;
|
||||
|
||||
alwaysinline auto read(uint24 address, uint8 data = 0) -> uint8 override;
|
||||
alwaysinline auto write(uint24 address, uint8 data) -> void override;
|
||||
|
||||
auto readCPU(uint24 address, uint8 data = 0) -> uint8;
|
||||
auto writeCPU(uint24 address, uint8 data) -> void;
|
||||
|
||||
auto readSA1(uint24 address, uint8 data = 0) -> uint8;
|
||||
auto writeSA1(uint24 address, uint8 data) -> void;
|
||||
|
||||
auto readLinear(uint24 address, uint8 data = 0) -> uint8;
|
||||
auto writeLinear(uint24 address, uint8 data) -> void;
|
||||
|
||||
auto readBitmap(uint20 address, uint8 data = 0) -> uint8;
|
||||
auto writeBitmap(uint20 address, uint8 data) -> void;
|
||||
|
||||
bool dma;
|
||||
} bwram;
|
||||
|
||||
struct IRAM : MappedRAM {
|
||||
//iram.cpp
|
||||
alwaysinline auto conflict() const -> bool;
|
||||
|
||||
alwaysinline auto read(uint24 address, uint8 data = 0) -> uint8 override;
|
||||
alwaysinline auto write(uint24 address, uint8 data) -> void override;
|
||||
|
||||
auto readCPU(uint24 address, uint8 data) -> uint8;
|
||||
auto writeCPU(uint24 address, uint8 data) -> void;
|
||||
|
||||
auto readSA1(uint24 address, uint8 data = 0) -> uint8;
|
||||
auto writeSA1(uint24 address, uint8 data) -> void;
|
||||
} iram;
|
||||
|
||||
private:
|
||||
DMA dma;
|
||||
|
@ -4,6 +4,7 @@ auto SA1::serialize(serializer& s) -> void {
|
||||
|
||||
s.array(iram.data(), iram.size());
|
||||
s.array(bwram.data(), bwram.size());
|
||||
s.integer(bwram.dma);
|
||||
|
||||
//sa1.hpp
|
||||
s.integer(status.counter);
|
||||
@ -14,11 +15,6 @@ auto SA1::serialize(serializer& s) -> void {
|
||||
s.integer(status.vcounter);
|
||||
s.integer(status.hcounter);
|
||||
|
||||
//bus/bus.hpp
|
||||
s.array(iram.data(), iram.size());
|
||||
|
||||
s.integer(cpubwram.dma);
|
||||
|
||||
//dma/dma.hpp
|
||||
s.integer(dma.line);
|
||||
|
||||
|
@ -1,5 +1,9 @@
|
||||
//ROM / RAM access from the S-CPU
|
||||
|
||||
auto SuperFX::CPUROM::data() -> uint8* {
|
||||
return superfx.rom.data();
|
||||
}
|
||||
|
||||
auto SuperFX::CPUROM::size() const -> uint {
|
||||
return superfx.rom.size();
|
||||
}
|
||||
@ -19,6 +23,10 @@ auto SuperFX::CPUROM::write(uint24 addr, uint8 data) -> void {
|
||||
superfx.rom.write(addr, data);
|
||||
}
|
||||
|
||||
auto SuperFX::CPURAM::data() -> uint8* {
|
||||
return superfx.ram.data();
|
||||
}
|
||||
|
||||
auto SuperFX::CPURAM::size() const -> uint {
|
||||
return superfx.ram.size();
|
||||
}
|
||||
|
@ -10,15 +10,17 @@ struct SuperFX : Processor::GSU, Thread {
|
||||
|
||||
//bus.cpp
|
||||
struct CPUROM : Memory {
|
||||
auto size() const -> uint;
|
||||
auto read(uint24, uint8) -> uint8;
|
||||
auto write(uint24, uint8) -> void;
|
||||
auto data() -> uint8* override;
|
||||
auto size() const -> uint override;
|
||||
auto read(uint24, uint8) -> uint8 override;
|
||||
auto write(uint24, uint8) -> void override;
|
||||
};
|
||||
|
||||
struct CPURAM : Memory {
|
||||
auto size() const -> uint;
|
||||
auto read(uint24, uint8) -> uint8;
|
||||
auto write(uint24, uint8) -> void;
|
||||
auto data() -> uint8* override;
|
||||
auto size() const -> uint override;
|
||||
auto read(uint24, uint8) -> uint8 override;
|
||||
auto write(uint24, uint8) -> void override;
|
||||
};
|
||||
|
||||
//core.cpp
|
||||
|
@ -166,12 +166,14 @@ private:
|
||||
//dma.cpp
|
||||
inline auto step(uint clocks) -> void;
|
||||
inline auto edge() -> void;
|
||||
inline auto valid(uint24 address) -> bool;
|
||||
inline auto read(uint24 address, bool valid) -> uint8;
|
||||
inline auto read(uint24 address) -> uint8;
|
||||
inline auto validA(uint24 address) -> bool;
|
||||
inline auto readA(uint24 address) -> uint8;
|
||||
inline auto readA(uint24 address, bool valid) -> uint8;
|
||||
inline auto readB(uint8 address, bool valid) -> uint8;
|
||||
inline auto flush() -> void;
|
||||
inline auto write(uint24 address, uint8 data, bool valid) -> void;
|
||||
inline auto write(uint24 address, uint8 data) -> void;
|
||||
inline auto writeA(uint24 address, uint8 data) -> void;
|
||||
inline auto writeA(uint24 address, uint8 data, bool valid) -> void;
|
||||
inline auto writeB(uint8 address, uint8 data, bool valid) -> void;
|
||||
inline auto transfer(uint24 address, uint2 index) -> void;
|
||||
|
||||
inline auto dmaRun() -> void;
|
||||
|
@ -25,6 +25,7 @@ auto CPU::dmaFlush() -> void {
|
||||
}
|
||||
|
||||
auto CPU::dmaRun() -> void {
|
||||
r.rwb = 0;
|
||||
dmaStep(8);
|
||||
dmaFlush();
|
||||
dmaEdge();
|
||||
@ -38,6 +39,7 @@ auto CPU::hdmaReset() -> void {
|
||||
}
|
||||
|
||||
auto CPU::hdmaSetup() -> void {
|
||||
r.rwb = 0;
|
||||
dmaStep(8);
|
||||
dmaFlush();
|
||||
for(auto& channel : channels) channel.hdmaSetup();
|
||||
@ -46,6 +48,7 @@ auto CPU::hdmaSetup() -> void {
|
||||
}
|
||||
|
||||
auto CPU::hdmaRun() -> void {
|
||||
r.rwb = 0;
|
||||
dmaStep(8);
|
||||
dmaFlush();
|
||||
for(auto& channel : channels) channel.hdmaTransfer();
|
||||
@ -64,7 +67,7 @@ auto CPU::Channel::edge() -> void {
|
||||
return cpu.dmaEdge();
|
||||
}
|
||||
|
||||
auto CPU::Channel::valid(uint24 address) -> bool {
|
||||
auto CPU::Channel::validA(uint24 address) -> bool {
|
||||
//A-bus cannot access the B-bus or CPU I/O registers
|
||||
if((address & 0x40ff00) == 0x2100) return false; //00-3f,80-bf:2100-21ff
|
||||
if((address & 0x40fe00) == 0x4000) return false; //00-3f,80-bf:4000-41ff
|
||||
@ -73,7 +76,11 @@ auto CPU::Channel::valid(uint24 address) -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto CPU::Channel::read(uint24 address, bool valid) -> uint8 {
|
||||
auto CPU::Channel::readA(uint24 address) -> uint8 {
|
||||
return readA(address, validA(address));
|
||||
}
|
||||
|
||||
auto CPU::Channel::readA(uint24 address, bool valid) -> uint8 {
|
||||
step(4);
|
||||
cpu.r.mdr = valid ? bus.read(address, cpu.r.mdr) : (uint8)0x00;
|
||||
step(4);
|
||||
@ -81,41 +88,53 @@ auto CPU::Channel::read(uint24 address, bool valid) -> uint8 {
|
||||
return cpu.r.mdr;
|
||||
}
|
||||
|
||||
auto CPU::Channel::read(uint24 address) -> uint8 {
|
||||
return read(address, valid(address));
|
||||
auto CPU::Channel::readB(uint8 address, bool valid) -> uint8 {
|
||||
step(4);
|
||||
cpu.r.mdr = valid ? bus.read(0x2100 | address, cpu.r.mdr) : (uint8)0x00;
|
||||
step(4);
|
||||
flush();
|
||||
return cpu.r.mdr;
|
||||
}
|
||||
|
||||
auto CPU::Channel::flush() -> void {
|
||||
return cpu.dmaFlush();
|
||||
}
|
||||
|
||||
auto CPU::Channel::write(uint24 address, uint8 data, bool valid) -> void {
|
||||
auto CPU::Channel::writeA(uint24 address, uint8 data) -> void {
|
||||
return writeA(address, data, validA(address));
|
||||
}
|
||||
|
||||
auto CPU::Channel::writeA(uint24 address, uint8 data, bool valid) -> void {
|
||||
cpu.pipe.valid = valid;
|
||||
cpu.pipe.address = address;
|
||||
cpu.pipe.data = data;
|
||||
}
|
||||
|
||||
auto CPU::Channel::write(uint24 address, uint8 data) -> void {
|
||||
return write(address, data, valid(address));
|
||||
auto CPU::Channel::writeB(uint8 address, uint8 data, bool valid) -> void {
|
||||
cpu.pipe.valid = valid;
|
||||
cpu.pipe.address = 0x2100 | address;
|
||||
cpu.pipe.data = data;
|
||||
}
|
||||
|
||||
auto CPU::Channel::transfer(uint24 aAddress, uint2 index) -> void {
|
||||
uint24 bAddress = 0x2100 | targetAddress;
|
||||
auto CPU::Channel::transfer(uint24 addressA, uint2 index) -> void {
|
||||
uint8 addressB = targetAddress;
|
||||
switch(transferMode) {
|
||||
case 1: case 5: bAddress += index.bit(0); break;
|
||||
case 3: case 7: bAddress += index.bit(1); break;
|
||||
case 4: bAddress += index; break;
|
||||
case 1: case 5: addressB += index.bit(0); break;
|
||||
case 3: case 7: addressB += index.bit(1); break;
|
||||
case 4: addressB += index; break;
|
||||
}
|
||||
|
||||
//transfers from WRAM to WRAM are invalid
|
||||
bool valid = bAddress != 0x2180 || ((aAddress & 0xfe0000) != 0x7e0000 && (aAddress & 0x40e000) != 0x0000);
|
||||
bool valid = addressB != 0x2180 || ((addressA & 0xfe0000) != 0x7e0000 && (addressA & 0x40e000) != 0x0000);
|
||||
|
||||
cpu.r.rwb = 1;
|
||||
cpu.r.mar = addressA;
|
||||
if(direction == 0) {
|
||||
auto data = read(aAddress);
|
||||
write(bAddress, data, valid);
|
||||
auto data = readA(addressA);
|
||||
writeB(addressB, data, valid);
|
||||
} else {
|
||||
auto data = read(bAddress, valid);
|
||||
write(aAddress, data);
|
||||
auto data = readB(addressB, valid);
|
||||
writeA(addressA, data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,6 +148,7 @@ auto CPU::Channel::dmaRun() -> void {
|
||||
edge();
|
||||
} while(dmaEnable && --transferSize);
|
||||
|
||||
cpu.r.rwb = 0;
|
||||
step(8);
|
||||
flush();
|
||||
edge();
|
||||
@ -164,7 +184,8 @@ auto CPU::Channel::hdmaSetup() -> void {
|
||||
}
|
||||
|
||||
auto CPU::Channel::hdmaReload() -> void {
|
||||
auto data = read(sourceBank << 16 | hdmaAddress);
|
||||
cpu.r.rwb = 1;
|
||||
auto data = readA(cpu.r.mar = sourceBank << 16 | hdmaAddress);
|
||||
|
||||
if((uint7)lineCounter == 0) {
|
||||
lineCounter = data;
|
||||
@ -174,11 +195,13 @@ auto CPU::Channel::hdmaReload() -> void {
|
||||
hdmaDoTransfer = !hdmaCompleted;
|
||||
|
||||
if(indirect) {
|
||||
data = read(sourceBank << 16 | hdmaAddress++);
|
||||
cpu.r.rwb = 1;
|
||||
data = readA(cpu.r.mar = sourceBank << 16 | hdmaAddress++);
|
||||
indirectAddress = data << 8 | 0x00; //todo: should 0x00 be indirectAddress >> 8 ?
|
||||
if(hdmaCompleted && hdmaFinished()) return;
|
||||
|
||||
data = read(sourceBank << 16 | hdmaAddress++);
|
||||
cpu.r.rwb = 1;
|
||||
data = readA(cpu.r.mar = sourceBank << 16 | hdmaAddress++);
|
||||
indirectAddress = data << 8 | indirectAddress >> 8;
|
||||
}
|
||||
}
|
||||
|
@ -1,37 +1,42 @@
|
||||
auto CPU::idle() -> void {
|
||||
status.clockCount = 6;
|
||||
dmaEdge();
|
||||
r.rwb = 0;
|
||||
step(6);
|
||||
aluEdge();
|
||||
}
|
||||
|
||||
auto CPU::read(uint24 addr) -> uint8 {
|
||||
status.clockCount = speed(addr);
|
||||
auto CPU::read(uint24 address) -> uint8 {
|
||||
status.clockCount = speed(address);
|
||||
dmaEdge();
|
||||
r.rwb = 1;
|
||||
r.mar = address;
|
||||
step(status.clockCount - 4);
|
||||
auto data = bus.read(addr, r.mdr);
|
||||
auto data = bus.read(r.mar, r.mdr);
|
||||
step(4);
|
||||
aluEdge();
|
||||
//$00-3f,80-bf:4000-43ff reads are internal to CPU, and do not update the MDR
|
||||
if((addr & 0x40fc00) != 0x4000) r.mdr = data;
|
||||
if((r.mar & 0x40fc00) != 0x4000) r.mdr = data;
|
||||
return data;
|
||||
}
|
||||
|
||||
auto CPU::write(uint24 addr, uint8 data) -> void {
|
||||
auto CPU::write(uint24 address, uint8 data) -> void {
|
||||
aluEdge();
|
||||
status.clockCount = speed(addr);
|
||||
status.clockCount = speed(address);
|
||||
dmaEdge();
|
||||
r.rwb = 1;
|
||||
r.mar = address;
|
||||
step(status.clockCount);
|
||||
bus.write(addr, r.mdr = data);
|
||||
bus.write(r.mar, r.mdr = data);
|
||||
}
|
||||
|
||||
auto CPU::speed(uint24 addr) const -> uint {
|
||||
if(addr & 0x408000) return addr & 0x800000 ? io.romSpeed : 8;
|
||||
if(addr + 0x6000 & 0x4000) return 8;
|
||||
if(addr - 0x4000 & 0x7e00) return 6;
|
||||
auto CPU::speed(uint24 address) const -> uint {
|
||||
if(address & 0x408000) return address & 0x800000 ? io.romSpeed : 8;
|
||||
if(address + 0x6000 & 0x4000) return 8;
|
||||
if(address - 0x4000 & 0x7e00) return 6;
|
||||
return 12;
|
||||
}
|
||||
|
||||
auto CPU::readDisassembler(uint24 addr) -> uint8 {
|
||||
return bus.read(addr, r.mdr);
|
||||
auto CPU::readDisassembler(uint24 address) -> uint8 {
|
||||
return bus.read(address, r.mdr);
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ auto CPU::step(uint clocks) -> void {
|
||||
|
||||
if(!status.dramRefreshed && hcounter() >= status.dramRefreshPosition) {
|
||||
status.dramRefreshed = true;
|
||||
r.rwb = 0;
|
||||
for(auto _ : range(5)) {
|
||||
step(8);
|
||||
aluEdge();
|
||||
@ -23,9 +24,12 @@ auto CPU::step(uint clocks) -> void {
|
||||
}
|
||||
|
||||
#if defined(DEBUGGER)
|
||||
synchronizeSMP();
|
||||
synchronizePPU();
|
||||
synchronizeCoprocessors();
|
||||
synchronize(smp);
|
||||
synchronize(ppu);
|
||||
#endif
|
||||
|
||||
#if defined(DEBUGGER) || defined(ACCURATE_SA1)
|
||||
for(auto coprocessor : coprocessors) synchronize(*coprocessor);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,3 @@
|
||||
//Memory
|
||||
|
||||
auto Memory::size() const -> uint { return 0; }
|
||||
|
||||
//StaticRAM
|
||||
|
||||
StaticRAM::StaticRAM(uint size) : _size(size) { _data = new uint8[_size]; }
|
||||
|
@ -1,5 +1,12 @@
|
||||
struct Memory {
|
||||
virtual inline auto size() const -> uint;
|
||||
inline explicit operator bool() const { return size() > 0; }
|
||||
|
||||
virtual auto reset() -> void {}
|
||||
virtual auto allocate(uint) -> void {}
|
||||
|
||||
virtual auto data() -> uint8* = 0;
|
||||
virtual auto size() const -> uint = 0;
|
||||
|
||||
virtual auto read(uint24 addr, uint8 data = 0) -> uint8 = 0;
|
||||
virtual auto write(uint24 addr, uint8 data) -> void = 0;
|
||||
};
|
||||
@ -16,7 +23,7 @@ struct StaticRAM : Memory {
|
||||
inline auto operator[](uint24 addr) -> uint8&;
|
||||
inline auto operator[](uint24 addr) const -> const uint8&;
|
||||
|
||||
private:
|
||||
protected:
|
||||
uint8* _data = nullptr;
|
||||
uint _size = 0;
|
||||
};
|
||||
@ -33,7 +40,7 @@ struct MappedRAM : Memory {
|
||||
inline auto write(uint24 addr, uint8 data) -> void;
|
||||
inline auto operator[](uint24 addr) const -> const uint8&;
|
||||
|
||||
private:
|
||||
protected:
|
||||
uint8* _data = nullptr;
|
||||
uint _size = 0;
|
||||
bool _writeProtect = false;
|
||||
|
@ -20,6 +20,8 @@
|
||||
#include <gb/gb.hpp>
|
||||
#endif
|
||||
|
||||
//#define ACCURATE_SA1
|
||||
|
||||
namespace SuperFamicom {
|
||||
#define platform Emulator::platform
|
||||
namespace File = Emulator::File;
|
||||
|
@ -24,6 +24,10 @@ auto BSMemory::power() -> void {
|
||||
memory.writeProtect(!regs.writeEnable);
|
||||
}
|
||||
|
||||
auto BSMemory::data() -> uint8* {
|
||||
return memory.data();
|
||||
}
|
||||
|
||||
auto BSMemory::size() const -> uint {
|
||||
return memory.size();
|
||||
}
|
||||
|
@ -4,9 +4,10 @@ struct BSMemory : Memory {
|
||||
auto unload() -> void;
|
||||
auto power() -> void;
|
||||
|
||||
auto size() const -> uint;
|
||||
auto read(uint24 addr, uint8) -> uint8;
|
||||
auto write(uint24 addr, uint8 data) -> void;
|
||||
auto data() -> uint8* override;
|
||||
auto size() const -> uint override;
|
||||
auto read(uint24 addr, uint8 data) -> uint8 override;
|
||||
auto write(uint24 addr, uint8 data) -> void override;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
@ -67,3 +67,12 @@
|
||||
#undef PairBits
|
||||
#undef TypeBits
|
||||
#undef HalfBits
|
||||
|
||||
namespace nall {
|
||||
//TODO: these types are for expressing smaller bit ranges in class interfaces
|
||||
//for instance, XChaCha20 taking a 192-bit nonce
|
||||
//however, they still allow more bits than expressed ...
|
||||
//some sort of wrapper needs to be devised to ensure these sizes are masked and wrap appropriately
|
||||
|
||||
using uint192_t = uint256_t;
|
||||
}
|
||||
|
@ -26,11 +26,23 @@ template<typename T, typename U> alwaysinline auto ror(const T& lhs, const U& rh
|
||||
#if INTMAX_BITS >= 128
|
||||
inline auto operator"" _u128(const char* s) -> uint128_t {
|
||||
uint128_t p = 0;
|
||||
while(*s) {
|
||||
auto c = *s++;
|
||||
if(c == '\'') continue;
|
||||
if(c < '0' || c > '9') break;
|
||||
p = (p << 3) + (p << 1) + (c - '0');
|
||||
if(s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
|
||||
s += 2;
|
||||
while(*s) {
|
||||
auto c = *s++;
|
||||
if(c == '\'');
|
||||
else if(c >= '0' && c <= '9') p = (p << 4) + (c - '0');
|
||||
else if(c >= 'a' && c <= 'f') p = (p << 4) + (c - 'a' + 10);
|
||||
else if(c >= 'A' && c <= 'F') p = (p << 4) + (c - 'A' + 10);
|
||||
else break;
|
||||
}
|
||||
} else {
|
||||
while(*s) {
|
||||
auto c = *s++;
|
||||
if(c == '\'');
|
||||
else if(c >= '0' && c <= '9') p = (p << 3) + (p << 1) + (c - '0');
|
||||
else break;
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
84
nall/array-span.hpp
Normal file
84
nall/array-span.hpp
Normal file
@ -0,0 +1,84 @@
|
||||
#pragma once
|
||||
|
||||
#include <nall/array-view.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
template<typename T> struct array_span : array_view<T> {
|
||||
using type = array_span;
|
||||
using super = array_view<T>;
|
||||
|
||||
inline array_span() {
|
||||
super::_data = nullptr;
|
||||
super::_size = 0;
|
||||
}
|
||||
|
||||
inline array_span(nullptr_t) {
|
||||
super::_data = nullptr;
|
||||
super::_size = 0;
|
||||
}
|
||||
|
||||
inline array_span(void* data, uint64_t size) {
|
||||
super::_data = (T*)data;
|
||||
super::_size = (int)size;
|
||||
}
|
||||
|
||||
inline operator T*() { return (T*)super::operator const T*(); }
|
||||
|
||||
inline auto operator[](uint index) -> T& { return (T&)super::operator[](index); }
|
||||
|
||||
template<typename U = T> inline auto data() -> U* { return (U*)super::_data; }
|
||||
|
||||
inline auto begin() -> iterator<T> { return {(T*)super::_data, (uint)0}; }
|
||||
inline auto end() -> iterator<T> { return {(T*)super::_data, (uint)super::_size}; }
|
||||
|
||||
inline auto rbegin() -> reverse_iterator<T> { return {(T*)super::_data, (uint)super::_size - 1}; }
|
||||
inline auto rend() -> reverse_iterator<T> { return {(T*)super::_data, (uint)-1}; }
|
||||
|
||||
auto write(T value) -> void {
|
||||
operator[](0) = value;
|
||||
super::_data++;
|
||||
super::_size--;
|
||||
}
|
||||
|
||||
//array_span<uint8_t> specializations
|
||||
template<typename U> auto writel(U value, uint size) -> void;
|
||||
template<typename U> auto writem(U value, uint size) -> void;
|
||||
template<typename U> auto writevn(U value, uint size) -> void;
|
||||
template<typename U> auto writevi(U value, uint size) -> void;
|
||||
};
|
||||
|
||||
//array_span<uint8_t>
|
||||
|
||||
template<> inline auto array_span<uint8_t>::write(uint8_t value) -> void {
|
||||
operator[](0) = value;
|
||||
_data++;
|
||||
_size--;
|
||||
}
|
||||
|
||||
template<> template<typename U> inline auto array_span<uint8_t>::writel(U value, uint size) -> void {
|
||||
for(uint byte : range(size)) write(value >> byte * 8);
|
||||
}
|
||||
|
||||
template<> template<typename U> inline auto array_span<uint8_t>::writem(U value, uint size) -> void {
|
||||
for(uint byte : reverse(range(size))) write(value >> byte * 8);
|
||||
}
|
||||
|
||||
template<> template<typename U> inline auto array_span<uint8_t>::writevn(U value, uint size) -> void {
|
||||
while(true) {
|
||||
auto byte = value & 0x7f;
|
||||
value >>= 7;
|
||||
if(value == 0) return write(0x80 | byte);
|
||||
write(byte);
|
||||
value--;
|
||||
}
|
||||
}
|
||||
|
||||
template<> template<typename U> inline auto array_span<uint8_t>::writevi(U value, uint size) -> void {
|
||||
bool negate = value < 0;
|
||||
if(negate) value = ~value;
|
||||
value = value << 1 | negate;
|
||||
writevn(value);
|
||||
}
|
||||
|
||||
}
|
@ -1,13 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <nall/iterator.hpp>
|
||||
#include <nall/range.hpp>
|
||||
#include <nall/traits.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct string;
|
||||
template<typename T> struct vector;
|
||||
template<typename T> struct array;
|
||||
|
||||
template<typename T> struct array_view {
|
||||
using type = array_view;
|
||||
|
||||
@ -16,13 +14,25 @@ template<typename T> struct array_view {
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
inline array_view(nullptr_t) {
|
||||
_data = nullptr;
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
inline array_view(const void* data, uint64_t size) {
|
||||
_data = (const T*)data;
|
||||
_size = (uint)size;
|
||||
_size = (int)size;
|
||||
}
|
||||
|
||||
inline explicit operator bool() const { return _data && _size > 0; }
|
||||
inline operator const T*() const { return _data; }
|
||||
|
||||
inline operator const T*() const {
|
||||
#ifdef DEBUG
|
||||
struct out_of_bounds {};
|
||||
if(_size <= 0) throw out_of_bounds{};
|
||||
#endif
|
||||
return _data;
|
||||
}
|
||||
|
||||
inline auto operator++() -> type& { _data++; _size--; return *this; }
|
||||
inline auto operator--() -> type& { _data--; _size++; return *this; }
|
||||
@ -30,6 +40,9 @@ template<typename T> struct array_view {
|
||||
inline auto operator++(int) -> type { auto copy = *this; ++(*this); return copy; }
|
||||
inline auto operator--(int) -> type { auto copy = *this; --(*this); return copy; }
|
||||
|
||||
inline auto operator-=(int distance) -> type& { _data -= distance; _size += distance; return *this; }
|
||||
inline auto operator+=(int distance) -> type& { _data += distance; _size -= distance; return *this; }
|
||||
|
||||
inline auto operator[](uint index) const -> const T& {
|
||||
#ifdef DEBUG
|
||||
struct out_of_bounds {};
|
||||
@ -52,9 +65,62 @@ template<typename T> struct array_view {
|
||||
inline auto rbegin() const -> reverse_iterator_const<T> { return {_data, (uint)_size - 1}; }
|
||||
inline auto rend() const -> reverse_iterator_const<T> { return {_data, (uint)-1}; }
|
||||
|
||||
auto read() -> T {
|
||||
auto value = operator[](0);
|
||||
_data++;
|
||||
_size--;
|
||||
return value;
|
||||
}
|
||||
|
||||
//array_view<uint8_t> specializations
|
||||
template<typename U> auto readl(U& value, uint size) -> U;
|
||||
template<typename U> auto readm(U& value, uint size) -> U;
|
||||
template<typename U> auto readvn(U& value, uint size) -> U;
|
||||
template<typename U> auto readvi(U& value, uint size) -> U;
|
||||
|
||||
template<typename U = uint64_t> auto readl(uint size) -> U { U value; return readl(value, size); }
|
||||
template<typename U = uint64_t> auto readm(uint size) -> U { U value; return readm(value, size); }
|
||||
template<typename U = uint64_t> auto readvn(uint size) -> U { U value; return readvn(value, size); }
|
||||
template<typename U = int64_t> auto readvi(uint size) -> U { U value; return readvi(value, size); }
|
||||
|
||||
protected:
|
||||
const T* _data;
|
||||
int _size;
|
||||
};
|
||||
|
||||
//array_view<uint8_t>
|
||||
|
||||
template<> template<typename U> inline auto array_view<uint8_t>::readl(U& value, uint size) -> U {
|
||||
value = 0;
|
||||
for(uint byte : range(size)) value |= read() << byte * 8;
|
||||
return value;
|
||||
}
|
||||
|
||||
template<> template<typename U> inline auto array_view<uint8_t>::readm(U& value, uint size) -> U {
|
||||
value = 0;
|
||||
for(uint byte : reverse(range(size))) value |= read() << byte * 8;
|
||||
return value;
|
||||
}
|
||||
|
||||
template<> template<typename U> inline auto array_view<uint8_t>::readvn(U& value, uint size) -> U {
|
||||
value = 0;
|
||||
uint shift = 1;
|
||||
while(true) {
|
||||
auto byte = read();
|
||||
value += (byte & 0x7f) * shift;
|
||||
if(byte & 0x80) break;
|
||||
shift <<= 7;
|
||||
value += shift;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
template<> template<typename U> inline auto array_view<uint8_t>::readvi(U& value, uint size) -> U {
|
||||
value = readvn<U>();
|
||||
bool negate = value & 1;
|
||||
value >>= 1;
|
||||
if(negate) value = ~value;
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
|
272
nall/beat/archive/archive.hpp
Normal file
272
nall/beat/archive/archive.hpp
Normal file
@ -0,0 +1,272 @@
|
||||
#pragma once
|
||||
|
||||
#include <nall/random.hpp>
|
||||
#include <nall/cipher/chacha20.hpp>
|
||||
#include <nall/elliptic-curve/ed25519.hpp>
|
||||
#include <nall/encode/base.hpp>
|
||||
#include <nall/decode/base.hpp>
|
||||
#include <nall/encode/lzsa.hpp>
|
||||
#include <nall/decode/lzsa.hpp>
|
||||
|
||||
namespace nall { namespace Beat {
|
||||
|
||||
struct Archive {
|
||||
struct Encryption {
|
||||
string type;
|
||||
uint256_t key = 0;
|
||||
uint192_t nonce = 0;
|
||||
};
|
||||
|
||||
struct Signature {
|
||||
string type;
|
||||
uint256_t privateKey = 0;
|
||||
uint256_t publicKey = 0;
|
||||
uint512_t signature = 0;
|
||||
};
|
||||
|
||||
struct Compression {
|
||||
string type;
|
||||
uint size = 0;
|
||||
};
|
||||
|
||||
//timestamps are human-readable strings in ISO 8601 format; save for T=>space
|
||||
//times are stored in UTC, rather than local times
|
||||
struct Timestamps {
|
||||
string created;
|
||||
string modified;
|
||||
string accessed;
|
||||
};
|
||||
|
||||
struct Permissions {
|
||||
string name;
|
||||
bool readable = false;
|
||||
bool writable = false;
|
||||
bool executable = false;
|
||||
};
|
||||
|
||||
struct Node {
|
||||
string name;
|
||||
|
||||
//paths and files
|
||||
Timestamps timestamps;
|
||||
struct {
|
||||
Permissions owner;
|
||||
Permissions group;
|
||||
Permissions other;
|
||||
} permissions;
|
||||
|
||||
//files only
|
||||
uint offset = 0;
|
||||
uint size = 0;
|
||||
|
||||
Compression compression;
|
||||
|
||||
string filename;
|
||||
vector<uint8_t> filedata;
|
||||
};
|
||||
|
||||
auto append(const Node& node) -> bool;
|
||||
auto encryptionManifest() -> string;
|
||||
auto manifest() -> string;
|
||||
auto create() -> vector<uint8_t>;
|
||||
|
||||
//internal functions
|
||||
auto encode() -> vector<uint8_t>;
|
||||
auto encode(Node& node, uint64_t offset) -> vector<uint8_t>;
|
||||
|
||||
Encryption encryption;
|
||||
Signature signature;
|
||||
Compression compression; //solid archiving
|
||||
vector<Node> nodes;
|
||||
};
|
||||
|
||||
auto Archive::append(const Node& node) -> bool {
|
||||
//prevent multiple nodes with the same name
|
||||
if(nodes.find([&](auto& item) { return item.name == node.name; })) return false;
|
||||
|
||||
nodes.append(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Archive::encryptionManifest() -> string {
|
||||
string manifest;
|
||||
manifest.append("encryption\n");
|
||||
manifest.append(" type: ", encryption.type, "\n");
|
||||
manifest.append(" nonce: ", Encode::Base<57>(encryption.nonce), "\n");
|
||||
return manifest;
|
||||
}
|
||||
|
||||
auto Archive::manifest() -> string {
|
||||
string manifest;
|
||||
manifest.append("archive\n");
|
||||
|
||||
for(auto& node : nodes) {
|
||||
if(node.name.endsWith("/")) {
|
||||
manifest.append(" path: ", string{node.name}.trimRight("/", 1L), "\n");
|
||||
} else {
|
||||
manifest.append(" file: ", node.name, "\n");
|
||||
manifest.append(" offset: ", node.offset, "\n");
|
||||
manifest.append(" size: ", node.size, "\n");
|
||||
if(node.compression.type) {
|
||||
manifest.append(" compression: ", node.compression.type, "\n");
|
||||
manifest.append(" size: ", node.compression.size, "\n");
|
||||
}
|
||||
}
|
||||
if(node.timestamps.created || node.timestamps.modified || node.timestamps.accessed) {
|
||||
manifest.append(" timestamp\n");
|
||||
if(auto timestamp = node.timestamps.created ) manifest.append(" created: ", timestamp, "\n");
|
||||
if(auto timestamp = node.timestamps.modified) manifest.append(" modified: ", timestamp, "\n");
|
||||
if(auto timestamp = node.timestamps.accessed) manifest.append(" accessed: ", timestamp, "\n");
|
||||
}
|
||||
if(node.permissions.owner.name || node.permissions.group.name || node.permissions.other.name) {
|
||||
manifest.append(" permission\n");
|
||||
if(node.permissions.owner.name) {
|
||||
manifest.append(" owner: ", node.permissions.owner.name, "\n");
|
||||
if(node.permissions.owner.readable ) manifest.append(" readable\n");
|
||||
if(node.permissions.owner.writable ) manifest.append(" writable\n");
|
||||
if(node.permissions.owner.executable) manifest.append(" executable\n");
|
||||
}
|
||||
if(node.permissions.group.name) {
|
||||
manifest.append(" group: ", node.permissions.group.name, "\n");
|
||||
if(node.permissions.group.readable ) manifest.append(" readable\n");
|
||||
if(node.permissions.group.writable ) manifest.append(" writable\n");
|
||||
if(node.permissions.group.executable) manifest.append(" executable\n");
|
||||
}
|
||||
if(node.permissions.other.name) {
|
||||
manifest.append(" other\n");
|
||||
if(node.permissions.other.readable ) manifest.append(" readable\n");
|
||||
if(node.permissions.other.writable ) manifest.append(" writable\n");
|
||||
if(node.permissions.other.executable) manifest.append(" executable\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(compression.type) {
|
||||
manifest.append(" compression: ", compression.type, "\n");
|
||||
manifest.append(" size: ", compression.size, "\n");
|
||||
}
|
||||
|
||||
if(signature.type == "ed25519") {
|
||||
manifest.append(" signature: ", signature.type, "\n");
|
||||
manifest.append(" publicKey: ", Encode::Base<57>(signature.publicKey), "\n");
|
||||
manifest.append(" signature: ", Encode::Base<57>(signature.signature), "\n");
|
||||
}
|
||||
|
||||
return manifest;
|
||||
}
|
||||
|
||||
auto Archive::create() -> vector<uint8_t> {
|
||||
vector<uint8_t> output;
|
||||
|
||||
output.append('B');
|
||||
output.append('P');
|
||||
output.append('A');
|
||||
output.append('1');
|
||||
|
||||
nodes.sort([&](auto& lhs, auto& rhs) {
|
||||
return string::compare(lhs.name, rhs.name) < 0;
|
||||
});
|
||||
|
||||
auto content = encode();
|
||||
if(compression.type == "lzsa") {
|
||||
content = Encode::LZSA(content);
|
||||
compression.size = content.size();
|
||||
}
|
||||
|
||||
if(signature.type == "ed25519") {
|
||||
EllipticCurve::Ed25519 ed25519;
|
||||
signature.publicKey = ed25519.publicKey(signature.privateKey);
|
||||
signature.signature = ed25519.sign(content, signature.privateKey);
|
||||
}
|
||||
|
||||
if(encryption.type == "xchacha20") {
|
||||
//a randomly generated nonce is preferred
|
||||
if(!encryption.nonce) {
|
||||
CSPRNG csprng;
|
||||
encryption.nonce = csprng.random<uint192_t>();
|
||||
}
|
||||
Cipher::XChaCha20 xchacha20{encryption.key, encryption.nonce};
|
||||
content = xchacha20.encrypt(content);
|
||||
|
||||
string manifest;
|
||||
manifest.append("encryption\n");
|
||||
manifest.append(" type: ", encryption.type, "\n");
|
||||
manifest.append(" nonce: ", Encode::Base<57>(encryption.nonce), "\n");
|
||||
|
||||
output.append(content);
|
||||
for(uint8_t byte : manifest) output.append(byte);
|
||||
output.appendl(manifest.size(), 8);
|
||||
} else {
|
||||
encryption = {};
|
||||
output.append(content);
|
||||
}
|
||||
|
||||
auto sha256 = Hash::SHA256(output).value();
|
||||
output.appendl(sha256, 32);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
auto Archive::encode() -> vector<uint8_t> {
|
||||
vector<uint8_t> output;
|
||||
|
||||
for(auto& node : nodes) {
|
||||
if(node.filename) {
|
||||
node.timestamps.created = chrono::utc::datetime(inode::timestamp(node.filename, inode::time::create));
|
||||
node.timestamps.accessed = chrono::utc::datetime(inode::timestamp(node.filename, inode::time::access));
|
||||
node.timestamps.modified = chrono::utc::datetime(inode::timestamp(node.filename, inode::time::modify));
|
||||
|
||||
uint mode = inode::mode(node.filename);
|
||||
node.permissions.owner.name = inode::user(node.filename);
|
||||
node.permissions.owner.executable = mode & 0100;
|
||||
node.permissions.owner.writable = mode & 0200;
|
||||
node.permissions.owner.readable = mode & 0400;
|
||||
node.permissions.group.name = inode::group(node.filename);
|
||||
node.permissions.group.executable = mode & 0010;
|
||||
node.permissions.group.writable = mode & 0020;
|
||||
node.permissions.group.readable = mode & 0040;
|
||||
node.permissions.other.name = " ";
|
||||
node.permissions.other.executable = mode & 0001;
|
||||
node.permissions.other.writable = mode & 0002;
|
||||
node.permissions.other.readable = mode & 0004;
|
||||
}
|
||||
|
||||
if(node.name.endsWith("/")) continue;
|
||||
|
||||
auto buffer = encode(node, output.size());
|
||||
output.append(buffer);
|
||||
}
|
||||
|
||||
auto manifest = this->manifest();
|
||||
for(auto byte : manifest) output.append(byte);
|
||||
for(auto byte : range(8)) output.append((uint64_t)manifest.size() >> byte * 8);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
auto Archive::encode(Node& node, uint64_t offset) -> vector<uint8_t> {
|
||||
node.offset = offset;
|
||||
|
||||
vector<uint8_t> output;
|
||||
|
||||
if(node.filename) {
|
||||
output = file::read(node.filename);
|
||||
} else {
|
||||
output = node.filedata;
|
||||
}
|
||||
|
||||
node.size = output.size();
|
||||
|
||||
if(node.compression.type == "lzsa") {
|
||||
output = Encode::LZSA(output);
|
||||
node.compression.size = output.size();
|
||||
} else {
|
||||
node.compression = {};
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
}}
|
@ -1,12 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <nall/arithmetic.hpp>
|
||||
#include <nall/array-view.hpp>
|
||||
|
||||
namespace nall { namespace Cipher {
|
||||
|
||||
//64-bit nonce; 64-bit x 64-byte (256GB) counter
|
||||
struct ChaCha20 {
|
||||
auto initialize(uint256_t key, uint64_t nonce, uint64_t counter = 0) -> void {
|
||||
static const uint256_t sigma = 0x6b20657479622d323320646e61707865_u256; //"expand 32-byte k"
|
||||
ChaCha20(uint256_t key, uint64_t nonce, uint64_t counter = 0) {
|
||||
static const uint128_t sigma = 0x6b20657479622d323320646e61707865_u128; //"expand 32-byte k"
|
||||
|
||||
input[ 0] = sigma >> 0;
|
||||
input[ 1] = sigma >> 32;
|
||||
@ -24,25 +26,31 @@ struct ChaCha20 {
|
||||
input[13] = counter >> 32;
|
||||
input[14] = nonce >> 0;
|
||||
input[15] = nonce >> 32;
|
||||
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
auto encrypt(const uint8_t* input, uint8_t* output, uint64_t length) -> void {
|
||||
while(length--) {
|
||||
if(!offset) cipher();
|
||||
auto encrypt(array_view<uint8_t> input) -> vector<uint8_t> {
|
||||
vector<uint8_t> output;
|
||||
while(input) {
|
||||
if(!offset) {
|
||||
cipher();
|
||||
increment();
|
||||
}
|
||||
auto byte = offset++;
|
||||
*output++ = *input++ ^ (block[byte >> 2] >> (byte & 3) * 8);
|
||||
output.append(*input++ ^ (block[byte >> 2] >> (byte & 3) * 8));
|
||||
offset &= 63;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
auto decrypt(const uint8_t* input, uint8_t* output, uint64_t length) -> void {
|
||||
encrypt(input, output, length); //reciprocal cipher
|
||||
auto decrypt(array_view<uint8_t> input) -> vector<uint8_t> {
|
||||
return encrypt(input); //reciprocal cipher
|
||||
}
|
||||
|
||||
private:
|
||||
//protected:
|
||||
inline auto rol(uint32_t value, uint bits) -> uint32_t {
|
||||
return value << bits | value >> (32 - bits);
|
||||
return value << bits | value >> 32 - bits;
|
||||
}
|
||||
|
||||
auto quarterRound(uint32_t x[16], uint a, uint b, uint c, uint d) -> void {
|
||||
@ -54,7 +62,7 @@ private:
|
||||
|
||||
auto cipher() -> void {
|
||||
memory::copy(block, input, 64);
|
||||
for(auto n : range(10)) {
|
||||
for(uint n : range(10)) {
|
||||
quarterRound(block, 0, 4, 8, 12);
|
||||
quarterRound(block, 1, 5, 9, 13);
|
||||
quarterRound(block, 2, 6, 10, 14);
|
||||
@ -64,7 +72,10 @@ private:
|
||||
quarterRound(block, 2, 7, 8, 13);
|
||||
quarterRound(block, 3, 4, 9, 14);
|
||||
}
|
||||
for(auto n : range(16)) {
|
||||
}
|
||||
|
||||
auto increment() -> void {
|
||||
for(uint n : range(16)) {
|
||||
block[n] += input[n];
|
||||
}
|
||||
if(!++input[12]) ++input[13];
|
||||
@ -75,4 +86,24 @@ private:
|
||||
uint64_t offset;
|
||||
};
|
||||
|
||||
struct HChaCha20 : protected ChaCha20 {
|
||||
HChaCha20(uint256_t key, uint128_t nonce) : ChaCha20(key, nonce >> 64, nonce >> 0) {
|
||||
cipher();
|
||||
}
|
||||
|
||||
auto key() const -> uint256_t {
|
||||
uint256_t key = 0;
|
||||
for(uint n : range(4)) key |= (uint256_t)block[ 0 + n] << (n + 0) * 32;
|
||||
for(uint n : range(4)) key |= (uint256_t)block[12 + n] << (n + 4) * 32;
|
||||
return key;
|
||||
}
|
||||
};
|
||||
|
||||
//192-bit nonce; 64-bit x 64-byte (256GB) counter
|
||||
struct XChaCha20 : ChaCha20 {
|
||||
XChaCha20(uint256_t key, uint192_t nonce, uint64_t counter = 0):
|
||||
ChaCha20(HChaCha20(key, nonce).key(), nonce >> 128, counter) {
|
||||
}
|
||||
};
|
||||
|
||||
}}
|
||||
|
@ -1,5 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
//required bytes: ceil(bits / log2(base))
|
||||
//base57 => 128=22, 256=44, 512=88
|
||||
//base62 => 128=22, 256=43, 512=86
|
||||
//base64 => 128=22, 256=43, 512=86
|
||||
|
||||
#include <nall/arithmetic.hpp>
|
||||
|
||||
namespace nall { namespace Encode {
|
||||
|
@ -9,7 +9,7 @@
|
||||
namespace nall {
|
||||
|
||||
struct inode {
|
||||
enum class time : uint { access, modify };
|
||||
enum class time : uint { create, modify, access };
|
||||
|
||||
static auto exists(const string& name) -> bool {
|
||||
return access(name, F_OK) == 0;
|
||||
@ -27,31 +27,55 @@ struct inode {
|
||||
return access(name, X_OK) == 0;
|
||||
}
|
||||
|
||||
static auto mode(const string& name) -> uint {
|
||||
struct stat data{};
|
||||
stat(name, &data);
|
||||
return data.st_mode;
|
||||
}
|
||||
|
||||
static auto uid(const string& name) -> uint {
|
||||
struct stat data{0};
|
||||
struct stat data{};
|
||||
stat(name, &data);
|
||||
return data.st_uid;
|
||||
}
|
||||
|
||||
static auto gid(const string& name) -> uint {
|
||||
struct stat data{0};
|
||||
struct stat data{};
|
||||
stat(name, &data);
|
||||
return data.st_gid;
|
||||
}
|
||||
|
||||
static auto mode(const string& name) -> uint {
|
||||
struct stat data{0};
|
||||
stat(name, &data);
|
||||
return data.st_mode;
|
||||
#if !defined(PLATFORM_WINDOWS)
|
||||
static auto user(const string& name) -> string {
|
||||
struct passwd* pw = getpwuid(uid(name));
|
||||
if(pw && pw->pw_name) return pw->pw_name;
|
||||
return {};
|
||||
}
|
||||
|
||||
static auto group(const string& name) -> string {
|
||||
struct group* gr = getgrgid(gid(name));
|
||||
if(gr && gr->gr_name) return gr->gr_name;
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
|
||||
static auto timestamp(const string& name, time mode = time::modify) -> uint64_t {
|
||||
struct stat data = {0};
|
||||
struct stat data{};
|
||||
stat(name, &data);
|
||||
switch(mode) { default:
|
||||
case time::access: return data.st_atime;
|
||||
switch(mode) {
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
case time::create: return data.st_ctime;
|
||||
#else
|
||||
//st_birthtime may return -1 or st_atime if it is not supported
|
||||
//the best that can be done in this case is to return st_mtime if it's older
|
||||
case time::create: return min((uint)data.st_birthtime, (uint)data.st_mtime);
|
||||
#endif
|
||||
case time::modify: return data.st_mtime;
|
||||
//for performance reasons, last access time is usually not enabled on various filesystems
|
||||
//ensure that the last access time is not older than the last modify time (eg for NTFS)
|
||||
case time::access: return max((uint)data.st_atime, data.st_mtime);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//returns true if 'name' already exists
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <nall/any.hpp>
|
||||
#include <nall/arithmetic.hpp>
|
||||
#include <nall/array.hpp>
|
||||
#include <nall/array-span.hpp>
|
||||
#include <nall/array-view.hpp>
|
||||
#include <nall/atoi.hpp>
|
||||
#include <nall/bit.hpp>
|
||||
|
@ -115,11 +115,3 @@ namespace Math {
|
||||
#else
|
||||
#define unreachable throw
|
||||
#endif
|
||||
|
||||
#if defined(COMPILER_GCC) && __GNUC__ == 4 && __GNUC_MINOR__ <= 7
|
||||
//GCC 4.7.x has a bug (#54849) when specifying override with a trailing return type:
|
||||
//auto function() -> return_type override; //this is the syntax that the C++11 standard requires
|
||||
//auto function() override -> return_type; //this is the syntax that GCC 4.7.x requires
|
||||
//in order to compile code correctly with both compilers, we disable the override keyword for GCC
|
||||
#define override
|
||||
#endif
|
||||
|
139
nall/random.hpp
139
nall/random.hpp
@ -1,25 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <nall/arithmetic.hpp>
|
||||
#include <nall/chrono.hpp>
|
||||
#include <nall/range.hpp>
|
||||
#include <nall/serializer.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/cipher/chacha20.hpp>
|
||||
|
||||
#if defined(PLATFORM_LINUX)
|
||||
#include <sys/random.h>
|
||||
#elif defined(PLATFORM_WINDOWS)
|
||||
#include <wincrypt.h>
|
||||
#endif
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct RandomNumberGenerator {
|
||||
virtual auto seed(uint64_t) -> void = 0;
|
||||
virtual auto operator()() -> uint64_t = 0;
|
||||
virtual auto serialize(serializer&) -> void = 0;
|
||||
};
|
||||
|
||||
//Galois LFSR using CRC64 polynomials
|
||||
struct LinearFeedbackShiftRegisterGenerator : RandomNumberGenerator {
|
||||
auto seed(uint64_t seed) -> void {
|
||||
lfsr = seed;
|
||||
for(uint n = 0; n < 8; n++) operator()();
|
||||
template<typename Base> struct RNG {
|
||||
template<typename T = uint64_t> auto random() -> T {
|
||||
T value = 0;
|
||||
for(uint n : range((sizeof(T) + 3) / 4)) {
|
||||
value = value << 32 | (uint32_t)static_cast<Base*>(this)->read();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
auto operator()() -> uint64_t {
|
||||
return lfsr = (lfsr >> 1) ^ (-(lfsr & 1) & crc64);
|
||||
template<typename T = uint64_t> auto bound(T range) -> T {
|
||||
T threshold = -range % range;
|
||||
while(true) {
|
||||
T value = random<T>();
|
||||
if(value >= threshold) return value % range;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
auto randomSeed() -> uint256_t {
|
||||
uint256_t seed = 0;
|
||||
#if defined(PLATFORM_BSD) || defined(PLATFORM_MACOS)
|
||||
for(uint n : range(8)) seed = seed << 32 | (uint32_t)arc4random();
|
||||
#elif defined(PLATFORM_LINUX)
|
||||
getrandom(&seed, 32, GRND_NONBLOCK);
|
||||
#elif defined(PLATFORM_WINDOWS)
|
||||
HCRYPTPROV provider;
|
||||
if(CryptAcquireContext(&provider, nullptr, MS_STRONG_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
|
||||
CryptGenRandom(provider, 32, (BYTE*)&seed);
|
||||
CryptReleaseContext(provider, 0);
|
||||
}
|
||||
#else
|
||||
//it's ... better than nothing ...
|
||||
srand(time(nullptr));
|
||||
for(uint n : range(32)) seed = seed << 8 | (uint8_t)rand();
|
||||
#endif
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
//Galois linear feedback shift register using CRC64 polynomials
|
||||
struct PRNG_LFSR : RNG<PRNG_LFSR> {
|
||||
auto seed(maybe<uint64_t> seed = {}) -> void {
|
||||
lfsr = seed ? seed() : (uint64_t)randomSeed();
|
||||
for(uint n : range(8)) read(); //hide the CRC64 polynomial from initial output
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void {
|
||||
@ -27,13 +66,81 @@ struct LinearFeedbackShiftRegisterGenerator : RandomNumberGenerator {
|
||||
}
|
||||
|
||||
private:
|
||||
auto read() -> uint64_t {
|
||||
return lfsr = (lfsr >> 1) ^ (-(lfsr & 1) & crc64);
|
||||
}
|
||||
|
||||
static const uint64_t crc64 = 0xc96c'5795'd787'0f42;
|
||||
uint64_t lfsr = crc64;
|
||||
|
||||
friend class RNG<PRNG_LFSR>;
|
||||
};
|
||||
|
||||
inline auto random() -> uint64_t {
|
||||
static LinearFeedbackShiftRegisterGenerator lfsr;
|
||||
return lfsr();
|
||||
struct PRNG_PCG : RNG<PRNG_PCG> {
|
||||
auto seed(maybe<uint32_t> seed = {}, maybe<uint32_t> sequence = {}) -> void {
|
||||
if(!seed) seed = (uint32_t)randomSeed();
|
||||
if(!sequence) sequence = 0;
|
||||
|
||||
state = 0;
|
||||
increment = sequence() << 1 | 1;
|
||||
read();
|
||||
state += seed();
|
||||
read();
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void {
|
||||
s.integer(state);
|
||||
s.integer(increment);
|
||||
}
|
||||
|
||||
private:
|
||||
auto read() -> uint32_t {
|
||||
uint64_t state = this->state;
|
||||
this->state = state * 6'364'136'223'846'793'005ull + increment;
|
||||
uint32_t xorshift = (state >> 18 ^ state) >> 27;
|
||||
uint32_t rotate = state >> 59;
|
||||
return xorshift >> rotate | xorshift << (-rotate & 31);
|
||||
}
|
||||
|
||||
uint64_t state = 0;
|
||||
uint64_t increment = 0;
|
||||
|
||||
friend class RNG<PRNG_PCG>;
|
||||
};
|
||||
|
||||
//XChaCha20 cryptographically secure pseudo-random number generator
|
||||
struct CSPRNG_XChaCha20 : RNG<CSPRNG_XChaCha20> {
|
||||
CSPRNG_XChaCha20() { seed(); }
|
||||
|
||||
auto seed(maybe<uint256_t> key = {}, maybe<uint192_t> nonce = {}) -> void {
|
||||
//the randomness comes from the key; the nonce just adds a bit of added entropy
|
||||
if(!key) key = randomSeed();
|
||||
if(!nonce) nonce = (uint192_t)clock() << 64 | chrono::nanosecond();
|
||||
context = {key(), nonce()};
|
||||
}
|
||||
|
||||
private:
|
||||
auto read() -> uint32_t {
|
||||
if(!counter) { context.cipher(); context.increment(); }
|
||||
uint32_t value = context.block[counter++];
|
||||
if(counter == 16) counter = 0; //64-bytes per block; 4 bytes per read
|
||||
return value;
|
||||
}
|
||||
|
||||
Cipher::XChaCha20 context{0, 0};
|
||||
uint counter = 0;
|
||||
|
||||
friend class RNG<CSPRNG_XChaCha20>;
|
||||
};
|
||||
|
||||
//
|
||||
|
||||
using PRNG = PRNG_PCG;
|
||||
using CSPRNG = CSPRNG_XChaCha20;
|
||||
|
||||
template<typename T = uint64_t> inline auto random() -> T {
|
||||
static PRNG_PCG pcg; //note: unseeded
|
||||
return pcg.random<T>();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -147,7 +147,9 @@ public:
|
||||
|
||||
explicit operator bool() const { return _size; }
|
||||
operator const char*() const { return (const char*)data(); }
|
||||
operator array_span<char>() { return {(char*)get(), size()}; }
|
||||
operator array_view<char>() const { return {(const char*)data(), size()}; }
|
||||
operator array_span<uint8_t>() { return {(uint8_t*)get(), size()}; }
|
||||
operator array_view<uint8_t>() const { return {(const uint8_t*)data(), size()}; }
|
||||
|
||||
auto operator==(const string& source) const -> bool {
|
||||
|
@ -51,9 +51,12 @@ suffix_array_invert:
|
||||
8 "t"
|
||||
0 ""
|
||||
|
||||
suffix_array_phi:
|
||||
phi = [2,5,9,0,1,7,8,3,4,0]
|
||||
|
||||
suffix_array_lcp:
|
||||
prefixes = [0,1,3,1,2,0,2,0,1] => lcp[n] == lcp(n, n-1)
|
||||
"" -
|
||||
prefixes = [0,0,1,3,1,2,0,2,0,1] => lcp[n] == lcp(n, n-1)
|
||||
"" 0
|
||||
"aacatat" 0
|
||||
"acaacatat" 1 "a"
|
||||
"acatat" 3 "aca"
|
||||
@ -64,13 +67,13 @@ suffix_array_lcp:
|
||||
"t" 0
|
||||
"tat" 1 "t"
|
||||
|
||||
suffix_array_plcp:
|
||||
plcp = [1,0,0,3,2,2,1,1,0,0]
|
||||
|
||||
suffix_array_lrcp:
|
||||
llcp = [0,0,0,3,1,0,0,0,0,1] => llcp[m] == lcp(l, m)
|
||||
rlcp = [0,1,1,1,2,0,2,0,0,0] => rlcp[m] == lcp(m, r)
|
||||
|
||||
suffix_array_phi:
|
||||
phi = [2,5,9,0,1,7,8,3,4]
|
||||
|
||||
suffix_array_lpf:
|
||||
lengths = [0,0,1,3,2,1,0,2,1,0]
|
||||
offsets = [0,0,0,0,1,3,4,5,6,2]
|
||||
@ -87,7 +90,7 @@ suffix_array_lpf:
|
||||
|
||||
*/
|
||||
|
||||
// via induced sorting
|
||||
// suffix array via induced sorting
|
||||
// O(n)
|
||||
inline auto suffix_array(array_view<uint8_t> input) -> vector<int> {
|
||||
return induced_sort(input.data(), input.size());
|
||||
@ -95,128 +98,169 @@ inline auto suffix_array(array_view<uint8_t> input) -> vector<int> {
|
||||
|
||||
// inverse
|
||||
// O(n)
|
||||
inline auto suffix_array_invert(array_view<int> suffixes) -> vector<int> {
|
||||
vector<int> inverted;
|
||||
inverted.reset(), inverted.reallocate(suffixes.size());
|
||||
for(int n : range(suffixes.size())) inverted[suffixes[n]] = n;
|
||||
return inverted;
|
||||
inline auto suffix_array_invert(array_view<int> sa) -> vector<int> {
|
||||
vector<int> isa;
|
||||
isa.reallocate(sa.size());
|
||||
for(int i : range(sa.size())) isa[sa[i]] = i;
|
||||
return isa;
|
||||
}
|
||||
|
||||
// auxiliary data structure for plcp and lpf computation
|
||||
// O(n)
|
||||
inline auto suffix_array_phi(array_view<int> sa) -> vector<int> {
|
||||
vector<int> phi;
|
||||
phi.reallocate(sa.size());
|
||||
phi[sa[0]] = 0;
|
||||
for(int i : range(1, sa.size())) phi[sa[i]] = sa[i - 1];
|
||||
return phi;
|
||||
}
|
||||
|
||||
// longest common prefix: lcp(l, r)
|
||||
// O(n)
|
||||
inline auto suffix_array_lcp(int l, int r, array_view<int> sa, array_view<uint8_t> input) -> int {
|
||||
int i = sa[l], j = sa[r], k = 0, size = input.size();
|
||||
while(i + k < size && j + k < size && input[i + k] == input[j + k]) k++;
|
||||
return k;
|
||||
}
|
||||
|
||||
// longest common prefix: lcp(i, j, k)
|
||||
// O(n)
|
||||
inline auto suffix_array_lcp(int i, int j, int k, array_view<uint8_t> input) -> int {
|
||||
int size = input.size();
|
||||
while(i + k < size && j + k < size && input[i + k] == input[j + k]) k++;
|
||||
return k;
|
||||
}
|
||||
|
||||
// longest common prefix: lcp[n] == lcp(n, n-1)
|
||||
// algorithm: kasai
|
||||
// O(n)
|
||||
inline auto suffix_array_lcp(array_view<int> suffixes, array_view<int> inverted, array_view<uint8_t> input) -> vector<int> {
|
||||
int size = input.size();
|
||||
vector<int> prefixes;
|
||||
prefixes.reset(), prefixes.reallocate(size);
|
||||
for(int i = 0, l = 0; i < size; i++) {
|
||||
if(inverted[i] == size) { l = 0; continue; } //the next substring is empty; ignore it
|
||||
int j = suffixes[inverted[i] + 1];
|
||||
while(i + l < size && j + l < size && input[i + l] == input[j + l]) l++;
|
||||
prefixes[inverted[i]] = l;
|
||||
if(l) l--;
|
||||
inline auto suffix_array_lcp(array_view<int> sa, array_view<int> isa, array_view<uint8_t> input) -> vector<int> {
|
||||
int k = 0, size = input.size();
|
||||
vector<int> lcp;
|
||||
lcp.reallocate(size + 1);
|
||||
for(int i : range(size)) {
|
||||
if(isa[i] == size) { k = 0; continue; } //the next substring is empty; ignore it
|
||||
int j = sa[isa[i] + 1];
|
||||
while(i + k < size && j + k < size && input[i + k] == input[j + k]) k++;
|
||||
lcp[1 + isa[i]] = k;
|
||||
if(k) k--;
|
||||
}
|
||||
return prefixes;
|
||||
lcp[0] = 0;
|
||||
return lcp;
|
||||
}
|
||||
|
||||
// longest common prefix (from permuted longest common prefix)
|
||||
// O(n)
|
||||
inline auto suffix_array_lcp(array_view<int> plcp, array_view<int> sa) -> vector<int> {
|
||||
vector<int> lcp;
|
||||
lcp.reallocate(plcp.size());
|
||||
for(int i : range(plcp.size())) lcp[i] = plcp[sa[i]];
|
||||
return lcp;
|
||||
}
|
||||
|
||||
// permuted longest common prefix
|
||||
// O(n)
|
||||
inline auto suffix_array_plcp(array_view<int> phi, array_view<uint8_t> input) -> vector<int> {
|
||||
vector<int> plcp;
|
||||
plcp.reallocate(phi.size());
|
||||
int k = 0, size = input.size();
|
||||
for(int i : range(size)) {
|
||||
int j = phi[i];
|
||||
while(i + k < size && j + k < size && input[i + k] == input[j + k]) k++;
|
||||
plcp[i] = k;
|
||||
if(k) k--;
|
||||
}
|
||||
return plcp;
|
||||
}
|
||||
|
||||
// permuted longest common prefix (from longest common prefix)
|
||||
// O(n)
|
||||
inline auto suffix_array_plcp(array_view<int> lcp, array_view<int> sa) -> vector<int> {
|
||||
vector<int> plcp;
|
||||
plcp.reallocate(lcp.size());
|
||||
for(int i : range(lcp.size())) plcp[sa[i]] = lcp[i];
|
||||
return plcp;
|
||||
}
|
||||
|
||||
// longest common prefixes - left + right
|
||||
// llcp[m] == lcp(l, m)
|
||||
// rlcp[m] == lcp(m, r)
|
||||
// O(n)
|
||||
inline auto suffix_array_lrcp(vector<int>& llcp, vector<int>& rlcp, array_view<int> lcp, array_view<int> suffixes, array_view<uint8_t> input) -> void {
|
||||
llcp.reset(), llcp.reallocate(lcp.size() + 1);
|
||||
rlcp.reset(), rlcp.reallocate(lcp.size() + 1);
|
||||
// requires: lcp -or- plcp+sa
|
||||
inline auto suffix_array_lrcp(vector<int>& llcp, vector<int>& rlcp, array_view<int> lcp, array_view<int> plcp, array_view<int> sa, array_view<uint8_t> input) -> void {
|
||||
int size = input.size();
|
||||
llcp.reset(), llcp.reallocate(size + 1);
|
||||
rlcp.reset(), rlcp.reallocate(size + 1);
|
||||
|
||||
function<int (int, int)> recurse = [&](int l, int r) -> int {
|
||||
if(l == lcp.size()) return 0;
|
||||
if(l == r - 1) return lcp[l];
|
||||
if(l == r - 1) {
|
||||
if(r > size) return 0;
|
||||
if(lcp) return lcp[r];
|
||||
return plcp[sa[r]];
|
||||
}
|
||||
int m = l + r >> 1;
|
||||
llcp[m] = recurse(l, m);
|
||||
rlcp[m] = recurse(m, r);
|
||||
return min(llcp[m], rlcp[m]);
|
||||
};
|
||||
recurse(0, lcp.size() + 1);
|
||||
recurse(0, size + 1);
|
||||
|
||||
llcp[0] = 0;
|
||||
rlcp[0] = 0;
|
||||
}
|
||||
|
||||
// auxiliary data for suffix_array_lpf
|
||||
// O(n)
|
||||
inline auto suffix_array_phi(array_view<int> suffixes) -> vector<int> {
|
||||
vector<int> phi;
|
||||
phi.reset(), phi.reallocate(suffixes.size() - 1);
|
||||
for(int i : range(1, suffixes.size())) {
|
||||
phi[suffixes[i]] = suffixes[i - 1];
|
||||
}
|
||||
return phi;
|
||||
}
|
||||
|
||||
// longest previous factor
|
||||
// O(n)
|
||||
inline auto suffix_array_lpf(vector<int>& lengths, vector<int>& offsets, array_view<int> phi, array_view<uint8_t> input) -> void {
|
||||
int l = 0, size = input.size();
|
||||
// optional: plcp
|
||||
inline auto suffix_array_lpf(vector<int>& lengths, vector<int>& offsets, array_view<int> phi, array_view<int> plcp, array_view<uint8_t> input) -> void {
|
||||
int k = 0, size = input.size();
|
||||
lengths.reset(), lengths.resize(size + 1, -1);
|
||||
offsets.reset(), offsets.resize(size + 1, -1);
|
||||
|
||||
function<void (int, int, int)> recurse = [&](int i, int l, int j) -> void {
|
||||
function<void (int, int, int)> recurse = [&](int i, int j, int k) -> void {
|
||||
if(lengths[i] < 0) {
|
||||
lengths[i] = l;
|
||||
lengths[i] = k;
|
||||
offsets[i] = j;
|
||||
} else if(lengths[i] < k) {
|
||||
if(offsets[i] > j) {
|
||||
recurse(offsets[i], j, lengths[i]);
|
||||
} else {
|
||||
recurse(j, offsets[i], lengths[i]);
|
||||
}
|
||||
lengths[i] = k;
|
||||
offsets[i] = j;
|
||||
} else {
|
||||
if(lengths[i] < l) {
|
||||
if(offsets[i] > j) {
|
||||
recurse(offsets[i], lengths[i], j);
|
||||
} else {
|
||||
recurse(j, lengths[i], offsets[i]);
|
||||
}
|
||||
lengths[i] = l;
|
||||
offsets[i] = j;
|
||||
if(offsets[i] > j) {
|
||||
recurse(offsets[i], j, k);
|
||||
} else {
|
||||
if(offsets[i] > j) {
|
||||
recurse(offsets[i], l, j);
|
||||
} else {
|
||||
recurse(j, l, offsets[i]);
|
||||
}
|
||||
recurse(j, offsets[i], k);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for(int i : range(size)) {
|
||||
int j = phi[i];
|
||||
while(i + l < size && j + l < size && input[i + l] == input[j + l]) l++;
|
||||
if(plcp) k = plcp[i];
|
||||
else while(i + k < size && j + k < size && input[i + k] == input[j + k]) k++;
|
||||
if(i > j) {
|
||||
recurse(i, l, j);
|
||||
recurse(i, j, k);
|
||||
} else {
|
||||
recurse(j, l, i);
|
||||
recurse(j, i, k);
|
||||
}
|
||||
if(l) l--;
|
||||
if(k) k--;
|
||||
}
|
||||
|
||||
//there can be no previous factor for the start of input; clear these values from -1 to 0
|
||||
lengths[0] = 0;
|
||||
offsets[0] = 0;
|
||||
}
|
||||
|
||||
// longest common prefix: lcp(l, r)
|
||||
// O(n)
|
||||
inline auto suffix_array_lcp(int l, int r, array_view<int> suffixes, array_view<uint8_t> input) -> int {
|
||||
int k = 0, size = input.size();
|
||||
l = suffixes[l], r = suffixes[r];
|
||||
while(l + k < size && r + k < size) {
|
||||
if(input[l + k] != input[r + k]) break;
|
||||
k++;
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
// O(n log m)
|
||||
inline auto suffix_array_find(int& length, int& offset, array_view<int> suffixes, array_view<uint8_t> input, array_view<uint8_t> match) -> bool {
|
||||
inline auto suffix_array_find(int& length, int& offset, array_view<int> sa, array_view<uint8_t> input, array_view<uint8_t> match) -> bool {
|
||||
length = 0, offset = 0;
|
||||
int l = 0, r = input.size();
|
||||
|
||||
while(l < r - 1) {
|
||||
int m = l + r >> 1;
|
||||
int s = suffixes[m];
|
||||
int s = sa[m];
|
||||
|
||||
int k = 0;
|
||||
while(k < match.size() && s + k < input.size()) {
|
||||
@ -241,13 +285,13 @@ inline auto suffix_array_find(int& length, int& offset, array_view<int> suffixes
|
||||
}
|
||||
|
||||
// O(n + log m)
|
||||
inline auto suffix_array_find(int& length, int& offset, array_view<int> llcp, array_view<int> rlcp, array_view<int> suffixes, array_view<uint8_t> input, array_view<uint8_t> match) -> bool {
|
||||
inline auto suffix_array_find(int& length, int& offset, array_view<int> llcp, array_view<int> rlcp, array_view<int> sa, array_view<uint8_t> input, array_view<uint8_t> match) -> bool {
|
||||
length = 0, offset = 0;
|
||||
int l = 0, r = input.size(), k = 0;
|
||||
|
||||
while(l < r - 1) {
|
||||
int m = l + r >> 1;
|
||||
int s = suffixes[m];
|
||||
int s = sa[m];
|
||||
|
||||
while(k < match.size() && s + k < input.size()) {
|
||||
if(match[k] != input[s + k]) break;
|
||||
@ -274,52 +318,47 @@ inline auto suffix_array_find(int& length, int& offset, array_view<int> llcp, ar
|
||||
|
||||
//
|
||||
|
||||
//there are multiple strategies for building the required auxiliary structures for suffix arrays
|
||||
|
||||
struct SuffixArray {
|
||||
using type = SuffixArray;
|
||||
|
||||
//O(n)
|
||||
inline SuffixArray(array_view<uint8_t> input) : input(input) {
|
||||
suffixes = suffix_array(input);
|
||||
}
|
||||
|
||||
//O(n)
|
||||
inline auto lcp() -> type& {
|
||||
inverted = suffix_array_invert(suffixes);
|
||||
prefixes = suffix_array_lcp(suffixes, inverted, input);
|
||||
return *this;
|
||||
sa = suffix_array(input);
|
||||
}
|
||||
|
||||
//O(n)
|
||||
inline auto lrcp() -> type& {
|
||||
lcp();
|
||||
suffix_array_lrcp(prefixesL, prefixesR, prefixes, suffixes, input);
|
||||
//if(!isa) isa = suffix_array_invert(sa);
|
||||
//if(!lcp) lcp = suffix_array_lcp(sa, isa, input);
|
||||
if(!phi) phi = suffix_array_phi(sa);
|
||||
if(!plcp) plcp = suffix_array_plcp(phi, input);
|
||||
//if(!lcp) lcp = suffix_array_lcp(plcp, sa);
|
||||
if(!llcp || !rlcp) suffix_array_lrcp(llcp, rlcp, lcp, plcp, sa, input);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//O(n)
|
||||
inline auto lpf() -> type& {
|
||||
auto phi = suffix_array_phi(suffixes);
|
||||
suffix_array_lpf(lengths, offsets, phi, input);
|
||||
if(!phi) phi = suffix_array_phi(sa);
|
||||
//if(!plcp) plcp = suffix_array_plcp(phi, input);
|
||||
if(!lengths || !offsets) suffix_array_lpf(lengths, offsets, phi, plcp, input);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline auto operator[](int offset) const -> int {
|
||||
return suffixes[offset];
|
||||
return sa[offset];
|
||||
}
|
||||
|
||||
//O(n log m)
|
||||
//inline auto find(int& length, int& offset, array_view<uint8_t> match) -> bool {
|
||||
// return suffix_array_find(length, offset, suffixes, input, match);
|
||||
//}
|
||||
|
||||
//requires: lrcp()
|
||||
//O(n + log m)
|
||||
//O(n + log m) with lrcp()
|
||||
inline auto find(int& length, int& offset, array_view<uint8_t> match) -> bool {
|
||||
return suffix_array_find(length, offset, prefixesL, prefixesR, suffixes, input, match);
|
||||
if(!llcp || !rlcp) return suffix_array_find(length, offset, sa, input, match); //O(n log m)
|
||||
return suffix_array_find(length, offset, llcp, rlcp, sa, input, match); //O(n + log m)
|
||||
}
|
||||
|
||||
//requires: lpf()
|
||||
//O(n)
|
||||
//O(n) with lpf()
|
||||
inline auto previous(int& length, int& offset, int address) -> void {
|
||||
length = lengths[address];
|
||||
offset = offsets[address];
|
||||
@ -329,13 +368,15 @@ struct SuffixArray {
|
||||
array_view<uint8_t> input;
|
||||
|
||||
//suffix array and auxiliary data structures
|
||||
vector<int> suffixes; //suffix array
|
||||
vector<int> inverted; //inverted suffix array
|
||||
vector<int> prefixes; //longest common prefixes - lcp(n, n-1)
|
||||
vector<int> prefixesL; //longest common prefixes - lcp(l, m)
|
||||
vector<int> prefixesR; //longest common prefixes - lcp(m, r)
|
||||
vector<int> lengths; //longest previous factors
|
||||
vector<int> offsets; //longest previous factors
|
||||
vector<int> sa; //suffix array
|
||||
vector<int> isa; //inverted suffix array
|
||||
vector<int> phi; //phi
|
||||
vector<int> plcp; //permuted longest common prefixes
|
||||
vector<int> lcp; //longest common prefixes
|
||||
vector<int> llcp; //longest common prefixes - left
|
||||
vector<int> rlcp; //longest common prefixes - right
|
||||
vector<int> lengths; //longest previous factors
|
||||
vector<int> offsets; //longest previous factors
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <new>
|
||||
|
||||
#include <nall/array-span.hpp>
|
||||
#include <nall/array-view.hpp>
|
||||
#include <nall/bit.hpp>
|
||||
#include <nall/function.hpp>
|
||||
@ -30,6 +31,7 @@ struct vector_base {
|
||||
~vector_base();
|
||||
|
||||
explicit operator bool() const;
|
||||
operator array_span<T>();
|
||||
operator array_view<T>() const;
|
||||
template<typename Cast = T> auto capacity() const -> uint;
|
||||
template<typename Cast = T> auto size() const -> uint;
|
||||
@ -125,7 +127,7 @@ struct vector_base {
|
||||
auto foreach(const function<void (const T&)>& callback) -> void;
|
||||
auto foreach(const function<void (uint, const T&)>& callback) -> void;
|
||||
|
||||
private:
|
||||
protected:
|
||||
T* _pool = nullptr; //pointer to first initialized element in pool
|
||||
uint _size = 0; //number of initialized elements in pool
|
||||
uint _left = 0; //number of allocated elements free on the left of pool
|
||||
@ -150,3 +152,5 @@ namespace nall {
|
||||
using vector_base<T>::vector_base;
|
||||
};
|
||||
}
|
||||
|
||||
#include <nall/vector/specialization/uint8_t.hpp>
|
||||
|
@ -31,6 +31,10 @@ template<typename T> vector<T>::operator bool() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
template<typename T> vector<T>::operator array_span<T>() {
|
||||
return {data(), size()};
|
||||
}
|
||||
|
||||
template<typename T> vector<T>::operator array_view<T>() const {
|
||||
return {data(), size()};
|
||||
}
|
||||
|
18
nall/vector/specialization/uint8_t.hpp
Normal file
18
nall/vector/specialization/uint8_t.hpp
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
namespace nall {
|
||||
|
||||
template<> struct vector<uint8_t> : vector_base<uint8_t> {
|
||||
using type = vector<uint8_t>;
|
||||
using vector_base<uint8_t>::vector_base;
|
||||
|
||||
template<typename U> auto appendl(U value, uint size) -> void {
|
||||
for(uint byte : range(size)) append(uint8_t(value >> byte * 8));
|
||||
}
|
||||
|
||||
template<typename U> auto appendm(U value, uint size) -> void {
|
||||
for(uint byte : reverse(range(size))) append(uint8_t(value >> byte * 8));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -138,7 +138,7 @@ struct InputJoypadDirectInput {
|
||||
property.diph.dwHow = DIPH_DEVICE;
|
||||
device->GetProperty(DIPROP_GUIDANDPATH, &property.diph);
|
||||
string devicePath = (const char*)utf8_t(property.wszPath);
|
||||
jp.pathID = Hash::CRC32(devicePath.data(), devicePath.size()).value();
|
||||
jp.pathID = Hash::CRC32(devicePath).value();
|
||||
jp.hid->setVendorID(jp.vendorID);
|
||||
jp.hid->setProductID(jp.productID);
|
||||
jp.hid->setPathID(jp.pathID);
|
||||
|
@ -266,7 +266,7 @@ private:
|
||||
auto createJoypadHID(Joypad& jp) -> void {
|
||||
jp.hid->setVendorID(jp.vendorID.hex());
|
||||
jp.hid->setProductID(jp.productID.hex());
|
||||
jp.hid->setPathID(Hash::CRC32(jp.deviceName.data(), jp.deviceName.size()).value());
|
||||
jp.hid->setPathID(Hash::CRC32(jp.deviceName).value());
|
||||
|
||||
for(uint n : range(jp.axes.size())) jp.hid->axes().append(n);
|
||||
for(uint n : range(jp.hats.size())) jp.hid->hats().append(n);
|
||||
|
Loading…
Reference in New Issue
Block a user