From bfdb287505723b11f8419abcf8d5e213d9cc76f8 Mon Sep 17 00:00:00 2001 From: Near <77224854+near-san@users.noreply.github.com> Date: Sat, 4 Jul 2020 19:46:00 +0000 Subject: [PATCH] Update to ares v114r16 release. - Super Famicom: mosaic fix wasn't exactly right before; now it should be - PlayStation: started implementing CD interface - PlayStation: started implementing GTE coprocessor --- ares/ares/information.hpp | 2 +- ares/pce/pcd/pcd.cpp | 4 +- ares/ps1/GNUmakefile | 3 +- ares/ps1/cpu/core/core.cpp | 2 +- ares/ps1/cpu/core/decoder.hpp | 59 +++++ ares/ps1/cpu/core/disassembler.cpp | 47 ++++ ares/ps1/cpu/core/disassembler.hpp | 4 + ares/ps1/cpu/core/gte-instructions.cpp | 292 ++++++++++++++++++++++++ ares/ps1/cpu/core/gte-registers.hpp | 169 ++++++++++++++ ares/ps1/cpu/core/gte.hpp | 74 ++++++ ares/ps1/cpu/interrupt.cpp | 7 +- ares/ps1/disc/command.cpp | 45 ++++ ares/ps1/disc/disc.cpp | 84 +++++++ ares/ps1/disc/disc.hpp | 100 ++++++++ ares/ps1/disc/io.cpp | 164 +++++++++++++ ares/ps1/disc/irq.cpp | 11 + ares/ps1/disc/serialization.cpp | 2 + ares/ps1/interface/interface.cpp | 4 + ares/ps1/memory/bus.hpp | 2 +- ares/ps1/ps1.hpp | 1 + ares/ps1/spu/io.cpp | 10 +- ares/ps1/system/serialization.cpp | 1 + ares/ps1/system/system.cpp | 3 + ares/sfc/ppu-performance/background.cpp | 4 +- ares/sfc/ppu/background.cpp | 4 +- lucia/emulator/mega-drive.cpp | 1 + lucia/emulator/pc-engine.cpp | 1 + lucia/emulator/playstation.cpp | 28 +++ 28 files changed, 1109 insertions(+), 19 deletions(-) create mode 100644 ares/ps1/cpu/core/gte-registers.hpp create mode 100644 ares/ps1/disc/command.cpp create mode 100644 ares/ps1/disc/disc.cpp create mode 100644 ares/ps1/disc/disc.hpp create mode 100644 ares/ps1/disc/io.cpp create mode 100644 ares/ps1/disc/irq.cpp create mode 100644 ares/ps1/disc/serialization.cpp diff --git a/ares/ares/information.hpp b/ares/ares/information.hpp index 01f283861..95a4d7399 100644 --- a/ares/ares/information.hpp +++ b/ares/ares/information.hpp @@ -2,7 +2,7 @@ namespace ares { static const string Name = "ares"; - static const string Version = "114.15"; + static const string Version = "114.16"; static const string Copyright = "ares team"; static const string License = "BY-NC-ND 4.0"; static const string LicenseURI = "https://creativecommons.org/licenses/by-nc-nd/4.0/"; diff --git a/ares/pce/pcd/pcd.cpp b/ares/pce/pcd/pcd.cpp index 4accb2299..eb525c1af 100644 --- a/ares/pce/pcd/pcd.cpp +++ b/ares/pce/pcd/pcd.cpp @@ -85,8 +85,8 @@ auto PCD::connect() -> void { vector subchannel; subchannel.resize(sectors * 96); for(uint sector : range(sectors)) { - pcd.fd->seek(sector * 2448 + 2352); - pcd.fd->read(subchannel.data() + sector * 96, 96); + fd->seek(sector * 2448 + 2352); + fd->read(subchannel.data() + sector * 96, 96); } session.decode(subchannel, 96); } diff --git a/ares/ps1/GNUmakefile b/ares/ps1/GNUmakefile index 07d426517..e3e9825b6 100644 --- a/ares/ps1/GNUmakefile +++ b/ares/ps1/GNUmakefile @@ -1,9 +1,10 @@ ares.objects += ares-ps1-interface ares-ps1-memory ares-ps1-system -ares.objects += ares-ps1-cpu ares-ps1-gpu ares-ps1-spu +ares.objects += ares-ps1-disc ares-ps1-cpu ares-ps1-gpu ares-ps1-spu $(object.path)/ares-ps1-interface.o: $(ares.path)/ps1/interface/interface.cpp $(object.path)/ares-ps1-memory.o: $(ares.path)/ps1/memory/memory.cpp $(object.path)/ares-ps1-system.o: $(ares.path)/ps1/system/system.cpp +$(object.path)/ares-ps1-disc.o: $(ares.path)/ps1/disc/disc.cpp $(object.path)/ares-ps1-cpu.o: $(ares.path)/ps1/cpu/cpu.cpp $(object.path)/ares-ps1-gpu.o: $(ares.path)/ps1/gpu/gpu.cpp $(object.path)/ares-ps1-spu.o: $(ares.path)/ps1/spu/spu.cpp diff --git a/ares/ps1/cpu/core/core.cpp b/ares/ps1/cpu/core/core.cpp index 13e80d8ef..b6e5cc81a 100644 --- a/ares/ps1/cpu/core/core.cpp +++ b/ares/ps1/cpu/core/core.cpp @@ -86,7 +86,7 @@ auto CPU::instructionDebug() -> void { static vector mask; if(!mask) mask.resize(0x0800'0000); -//if(mask[(PC & 0x1fff'ffff) >> 2]) return; + if(mask[(PC & 0x1fff'ffff) >> 2]) return; mask[(PC & 0x1fff'ffff) >> 2] = 1; static uint counter = 0; diff --git a/ares/ps1/cpu/core/decoder.hpp b/ares/ps1/cpu/core/decoder.hpp index a2b274121..96df47650 100644 --- a/ares/ps1/cpu/core/decoder.hpp +++ b/ares/ps1/cpu/core/decoder.hpp @@ -196,7 +196,21 @@ { switch(OP >> 21 & 0x1f) { op(0x00, MFC0, RT, RDn); + op(0x01, INVALID); + op(0x02, INVALID); + op(0x03, INVALID); op(0x04, MTC0, RT, RDn); + op(0x05, INVALID); + op(0x06, INVALID); + op(0x07, INVALID); + op(0x08, INVALID); + op(0x09, INVALID); + op(0x0a, INVALID); + op(0x0b, INVALID); + op(0x0c, INVALID); + op(0x0d, INVALID); + op(0x0e, INVALID); + op(0x0f, INVALID); } switch(OP & 0x3f) { @@ -208,6 +222,51 @@ #ifdef DECODER_GTE { + switch(OP >> 21 & 0x1f) { + op(0x00, MFC2, RT, RDn); + op(0x01, INVALID); + op(0x02, CFC2, RT, RDn); + op(0x03, INVALID); + op(0x04, MTC2, RT, RDn); + op(0x05, INVALID); + op(0x06, CTC2, RT, RDn); + op(0x07, INVALID); + op(0x08, INVALID); + op(0x09, INVALID); + op(0x0a, INVALID); + op(0x0b, INVALID); + op(0x0c, INVALID); + op(0x0d, INVALID); + op(0x0e, INVALID); + op(0x0f, INVALID); + } + + switch(OP & 0x3f) { + op(0x00, RTPS); + op(0x01, RTPS); //0x00 mirror? + op(0x06, NCLIP); + op(0x0c, OP); + op(0x10, DPCS); + op(0x11, INTPL); + op(0x12, MVMVA); + op(0x13, NCDS); + op(0x14, CDP); + op(0x16, NCDT); + op(0x1a, DCPL); //0x29 mirror? + op(0x1b, NCCS); + op(0x1c, CC); + op(0x1e, NCS); + op(0x20, NCT); + op(0x28, SQR); + op(0x29, DCPL); + op(0x2a, DPCT); + op(0x2d, AVSZ3); + op(0x2e, AVSZ4); + op(0x30, RTPT); + op(0x3d, GPF); + op(0x3e, GPL); + op(0x3f, NCCT); + } } #undef DECODER_GTE #endif diff --git a/ares/ps1/cpu/core/disassembler.cpp b/ares/ps1/cpu/core/disassembler.cpp index afb66e628..6aef5943a 100644 --- a/ares/ps1/cpu/core/disassembler.cpp +++ b/ares/ps1/cpu/core/disassembler.cpp @@ -260,6 +260,23 @@ auto CPU::Disassembler::SCC() -> vector { } auto CPU::Disassembler::GTE() -> vector { + auto rtName = [&] { return cpuRegisterName (instruction >> 16 & 31); }; + auto rtValue = [&] { return cpuRegisterValue(instruction >> 16 & 31); }; + auto drName = [&] { return gteDataRegisterName (instruction >> 11 & 31); }; + auto drValue = [&] { return gteDataRegisterValue(instruction >> 11 & 31); }; + auto crName = [&] { return gteControlRegisterName (instruction >> 11 & 31); }; + auto crValue = [&] { return gteControlRegisterValue(instruction >> 11 & 31); }; + + switch(instruction >> 21 & 0x1f) { + case 0x00: return {"mfc2", rtName(), drValue()}; + case 0x02: return {"cfc2", rtName(), crValue()}; + case 0x04: return {"mtc2", rtValue(), drName()}; + case 0x06: return {"ctc2", rtValue(), crName()}; + } + if(!(instruction >> 25 & 1)) return {}; + switch(instruction & 0x3f) { + } + return {}; } @@ -306,6 +323,36 @@ auto CPU::Disassembler::sccRegisterValue(u8 index) const -> string { return sccRegisterName(index); } +auto CPU::Disassembler::gteDataRegisterName(u8 index) const -> string { + static const string registers[32] = { + "vxy0", "vz0", "vxy1", "vz2", "vxy2", "vz2", "rgbc", "otz", + "ir0", "ir1", "ir2", "ir3", "sxy0", "sxy1", "sxy2", "sxyp", + "sz0", "sz1", "sz2", "sz3", "rgb0", "rgb1", "rgb2", "res1", + "mac0", "mac1", "mac2", "mac3", "irgb", "orgb", "lzcs", "lzcr", + }; + return registers[index]; +} + +auto CPU::Disassembler::gteDataRegisterValue(u8 index) const -> string { + if(showValues) return {gteDataRegisterName(index)}; //todo + return gteDataRegisterName(index); +} + +auto CPU::Disassembler::gteControlRegisterName(u8 index) const -> string { + static const string registers[32] = { + "rt11+rt12", "rt13+rt21", "rt22+rt23", "rt31+rt32", "rt33", "trx", "try", "trz", + "l11+l12", "l13+l21", "l22+l23", "l31+l32", "l33", "rbk", "gbk", "bbk", + "lr1+lr2", "lr3+lg1", "lg2+lg3", "lb1+lb2", "lb3", "rfc", "gfc", "bfc", + "ofx", "ofy", "h", "dqa", "dqb", "zsf3", "zsf4", "flag", + }; + return registers[index]; +} + +auto CPU::Disassembler::gteControlRegisterValue(u8 index) const -> string { + if(showValues) return {gteControlRegisterName(index)}; //todo + return gteControlRegisterName(index); +} + template auto CPU::Disassembler::hint(P&&... p) const -> string { if(showColors) return {"\e[0m\e[37m", forward

(p)..., "\e[0m"}; diff --git a/ares/ps1/cpu/core/disassembler.hpp b/ares/ps1/cpu/core/disassembler.hpp index d266a901a..c05ba76d3 100644 --- a/ares/ps1/cpu/core/disassembler.hpp +++ b/ares/ps1/cpu/core/disassembler.hpp @@ -22,6 +22,10 @@ auto cpuRegisterIndex(u8 index, i16 offset) const -> string; auto sccRegisterName(u8 index) const -> string; auto sccRegisterValue(u8 index) const -> string; + auto gteDataRegisterName(u8 index) const -> string; + auto gteDataRegisterValue(u8 index) const -> string; + auto gteControlRegisterName(u8 index) const -> string; + auto gteControlRegisterValue(u8 index) const -> string; u32 address; u32 instruction; diff --git a/ares/ps1/cpu/core/gte-instructions.cpp b/ares/ps1/cpu/core/gte-instructions.cpp index e69de29bb..d7413d0e2 100644 --- a/ares/ps1/cpu/core/gte-instructions.cpp +++ b/ares/ps1/cpu/core/gte-instructions.cpp @@ -0,0 +1,292 @@ +#include "gte-registers.hpp" + +auto CPU::instructionCFC2(u32& rt, u8 rd) -> void { + switch(rd) { + case 0: rt = RT11 << 0 | RT12 << 16; break; + case 1: rt = RT13 << 0 | RT21 << 16; break; + case 2: rt = RT22 << 0 | RT23 << 16; break; + case 3: rt = RT31 << 0 | RT32 << 16; break; + case 4: rt = RT33 << 0; break; + case 5: rt = TRX; break; + case 6: rt = TRY; break; + case 7: rt = TRZ; break; + case 8: rt = L11 << 0 | L12 << 16; break; + case 9: rt = L13 << 0 | L21 << 16; break; + case 10: rt = L22 << 0 | L23 << 16; break; + case 11: rt = L31 << 0 | L32 << 16; break; + case 12: rt = L33 << 0; break; + case 13: rt = RBK; break; + case 14: rt = GBK; break; + case 15: rt = BBK; break; + case 16: rt = LR1 << 0 | LR2 << 16; break; + case 17: rt = LR3 << 0 | LG1 << 16; break; + case 18: rt = LG2 << 0 | LG3 << 16; break; + case 19: rt = LB1 << 0 | LB2 << 16; break; + case 20: rt = LB3 << 0; break; + case 21: rt = RFC; break; + case 22: rt = GFC; break; + case 23: rt = BFC; break; + case 24: rt = OFX; break; + case 25: rt = OFY; break; + case 26: rt = H; break; + case 27: rt = DQA; break; + case 28: rt = DQB; break; + case 29: rt = ZSF3; break; + case 30: rt = ZSF4; break; + case 31: rt = 0; + rt |= FLAG.saturated.ir0 << 12; + rt |= FLAG.saturated.sy2 << 13; + rt |= FLAG.saturated.sx2 << 14; + rt |= FLAG.underflow.mac0 << 15; + rt |= FLAG.overflow.mac0 << 16; + rt |= FLAG.overflow.division << 17; + rt |= FLAG.saturated.sz1_otz << 18; + rt |= FLAG.saturated.b << 19; + rt |= FLAG.saturated.g << 20; + rt |= FLAG.saturated.r << 21; + rt |= FLAG.saturated.ir3 << 22; + rt |= FLAG.saturated.ir2 << 23; + rt |= FLAG.saturated.ir1 << 24; + rt |= FLAG.underflow.mac3 << 25; + rt |= FLAG.underflow.mac2 << 26; + rt |= FLAG.underflow.mac1 << 27; + rt |= FLAG.overflow.mac3 << 28; + rt |= FLAG.overflow.mac2 << 29; + rt |= FLAG.overflow.mac1 << 30; + rt |= FLAG.error << 31; + break; + } +} + +auto CPU::instructionCTC2(cu32& rt, u8 rd) -> void { + switch(rd) { + case 0: RT11 = rt >> 0; RT12 = rt >> 16; break; + case 1: RT13 = rt >> 0; RT21 = rt >> 16; break; + case 2: RT22 = rt >> 0; RT23 = rt >> 16; break; + case 3: RT31 = rt >> 0; RT32 = rt >> 16; break; + case 4: RT33 = rt >> 0; break; + case 5: TRX = rt; break; + case 6: TRY = rt; break; + case 7: TRZ = rt; break; + case 8: L11 = rt >> 0; L12 = rt >> 16; break; + case 9: L13 = rt >> 0; L21 = rt >> 16; break; + case 10: L22 = rt >> 0; L23 = rt >> 16; break; + case 11: L31 = rt >> 0; L32 = rt >> 16; break; + case 12: L33 = rt >> 0; break; + case 13: RBK = rt; break; + case 14: GBK = rt; break; + case 15: BBK = rt; break; + case 16: LR1 = rt >> 0; LR2 = rt >> 16; break; + case 17: LR3 = rt >> 0; LG1 = rt >> 16; break; + case 18: LG2 = rt >> 0; LG3 = rt >> 16; break; + case 19: LB1 = rt >> 0; LB2 = rt >> 16; break; + case 20: LB3 = rt >> 0; break; + case 21: RFC = rt; break; + case 22: GFC = rt; break; + case 23: BFC = rt; break; + case 24: OFX = rt; break; + case 25: OFY = rt; break; + case 26: H = rt; break; + case 27: DQA = rt; break; + case 28: DQB = rt; break; + case 29: ZSF3 = rt; break; + case 30: ZSF4 = rt; break; + case 31: + FLAG.saturated.ir0 = rt >> 12 & 1; + FLAG.saturated.sy2 = rt >> 13 & 1; + FLAG.saturated.sx2 = rt >> 14 & 1; + FLAG.underflow.mac0 = rt >> 15 & 1; + FLAG.overflow.mac0 = rt >> 16 & 1; + FLAG.overflow.division = rt >> 17 & 1; + FLAG.saturated.sz1_otz = rt >> 18 & 1; + FLAG.saturated.b = rt >> 19 & 1; + FLAG.saturated.g = rt >> 20 & 1; + FLAG.saturated.r = rt >> 21 & 1; + FLAG.saturated.ir3 = rt >> 22 & 1; + FLAG.saturated.ir2 = rt >> 23 & 1; + FLAG.saturated.ir1 = rt >> 24 & 1; + FLAG.underflow.mac3 = rt >> 25 & 1; + FLAG.underflow.mac2 = rt >> 26 & 1; + FLAG.underflow.mac1 = rt >> 27 & 1; + FLAG.overflow.mac3 = rt >> 28 & 1; + FLAG.overflow.mac2 = rt >> 29 & 1; + FLAG.overflow.mac1 = rt >> 30 & 1; + FLAG.error = rt >> 31 & 1; + break; + } +} + +auto CPU::instructionMFC2(u32& rt, u8 rd) -> void { + switch(rd) { + case 0: rt = VX0 << 0 | VY0 << 16; break; + case 1: rt = VZ0 << 0; break; + case 2: rt = VX1 << 0 | VY1 << 16; break; + case 3: rt = VZ1 << 0; break; + case 4: rt = VX2 << 0 | VY2 << 16; break; + case 5: rt = VZ2 << 0; break; + case 6: rt = RGBC; break; + case 7: rt = OTZ; break; + case 8: rt = IR0; break; + case 9: rt = IR1; break; + case 10: rt = IR2; break; + case 11: rt = IR3; break; + case 12: rt = SX0 << 0 | SY0 << 16; break; + case 13: rt = SX1 << 0 | SY1 << 16; break; + case 14: rt = SX2 << 0 | SY2 << 16; break; + case 15: rt = SX2 << 0 | SY2 << 16; break; + case 16: rt = SZ0; break; + case 17: rt = SZ1; break; + case 18: rt = SZ2; break; + case 19: rt = SZ3; break; + case 20: rt = RGB0; break; + case 21: rt = RGB1; break; + case 22: rt = RGB2; break; + case 23: rt = RES1; break; + case 24: rt = MAC0; break; + case 25: rt = MAC1; break; + case 26: rt = MAC2; break; + case 27: rt = MAC3; break; + case 28: //IRGB + case 29: {//ORGB + i8 r = (IR1 >> 7 < 0) ? 0 : (IR1 >> 7 > 0x1f) ? 0x1f : (IR1 >> 7); + i8 g = (IR2 >> 7 < 0) ? 0 : (IR2 >> 7 > 0x1f) ? 0x1f : (IR2 >> 7); + i8 b = (IR3 >> 7 < 0) ? 0 : (IR3 >> 7 > 0x1f) ? 0x1f : (IR3 >> 7); + rt = r << 0 | g << 5 | b << 10; + } break; + case 30: rt = LZCS; break; + case 31: rt = LZCR; break; + } +} + +auto CPU::instructionMTC2(cu32& rt, u8 rd) -> void { + switch(rd) { + case 0: VX0 = rt >> 0; VY0 = rt >> 16; break; + case 1: VZ0 = rt >> 0; break; + case 2: VX1 = rt >> 0; VY1 = rt >> 16; break; + case 3: VZ1 = rt >> 0; break; + case 4: VX2 = rt >> 0; VY2 = rt >> 16; break; + case 5: VZ2 = rt >> 0; break; + case 6: RGBC = rt; break; + case 7: OTZ = rt; break; + case 8: IR0 = rt; break; + case 9: IR1 = rt; break; + case 10: IR2 = rt; break; + case 11: IR3 = rt; break; + case 12: SX0 = rt >> 0; SY0 = rt >> 16; break; + case 13: SX1 = rt >> 0; SY1 = rt >> 16; break; + case 14: SX2 = rt >> 0; SY2 = rt >> 16; break; + case 15: {//SXP + SX0 = SX1; SY0 = SY1; + SX1 = SX2; SY1 = SY2; + SX2 = rt >> 0; SY2 = rt >> 16; + } break; + case 16: SZ0 = rt; break; + case 17: SZ1 = rt; break; + case 18: SZ2 = rt; break; + case 19: SZ3 = rt; break; + case 20: RGB0 = rt; break; + case 21: RGB1 = rt; break; + case 22: RGB2 = rt; break; + case 23: RES1 = rt; break; + case 24: MAC0 = rt; break; + case 25: MAC1 = rt; break; + case 26: MAC2 = rt; break; + case 27: MAC3 = rt; break; + case 28: IRGB = rt; break; + case 29: ORGB = rt; break; + case 30: LZCS = rt; break; + case 31: LZCR = rt; break; + } +} + +// + +auto CPU::instructionAVSZ3() -> void { + print("AVSZ3\n"); +} + +auto CPU::instructionAVSZ4() -> void { + print("AVSZ4\n"); +} + +auto CPU::instructionCC() -> void { + print("CC\n"); +} + +auto CPU::instructionCDP() -> void { + print("CDP\n"); +} + +auto CPU::instructionDCPL() -> void { + print("DCPL\n"); +} + +auto CPU::instructionDPCS() -> void { + print("DPCS\n"); +} + +auto CPU::instructionDPCT() -> void { + print("DPCT\n"); +} + +auto CPU::instructionGPF() -> void { + print("GPF\n"); +} + +auto CPU::instructionGPL() -> void { + print("GPL\n"); +} + +auto CPU::instructionINTPL() -> void { + print("INTPL\n"); +} + +auto CPU::instructionMVMVA() -> void { + print("MVMVA\n"); +} + +auto CPU::instructionNCCS() -> void { + print("NCCS\n"); +} + +auto CPU::instructionNCCT() -> void { + print("NCCT\n"); +} + +auto CPU::instructionNCDS() -> void { + print("NCDS\n"); +} + +auto CPU::instructionNCDT() -> void { + print("NCDT\n"); +} + +auto CPU::instructionNCLIP() -> void { + print("NCLIP\n"); +} + +auto CPU::instructionNCS() -> void { + print("NCS\n"); +} + +auto CPU::instructionNCT() -> void { + print("NCT\n"); +} + +auto CPU::instructionOP() -> void { + print("OP\n"); +} + +auto CPU::instructionRTPS() -> void { + print("RTPS\n"); +} + +auto CPU::instructionRTPT() -> void { + print("RTPT\n"); +} + +auto CPU::instructionSQR() -> void { + print("SQR\n"); +} + +#include "gte-registers.hpp" diff --git a/ares/ps1/cpu/core/gte-registers.hpp b/ares/ps1/cpu/core/gte-registers.hpp new file mode 100644 index 000000000..5da5699f9 --- /dev/null +++ b/ares/ps1/cpu/core/gte-registers.hpp @@ -0,0 +1,169 @@ +#if !defined(GTE_REGISTERS_HPP) + #define GTE_REGISTERS_HPP + + #define VX0 gte.vx0 + #define VY0 gte.vy0 + #define VZ0 gte.vz0 + #define VX1 gte.vx1 + #define VY1 gte.vy1 + #define VZ1 gte.vz1 + #define VX2 gte.vx2 + #define VY2 gte.vy2 + #define VZ2 gte.vz2 + #define RGBC gte.rgbc + #define OTZ gte.otz + #define IR0 gte.ir0 + #define IR1 gte.ir1 + #define IR2 gte.ir2 + #define IR3 gte.ir3 + #define SX0 gte.sx0 + #define SY0 gte.sy0 + #define SX1 gte.sx1 + #define SY1 gte.sy1 + #define SX2 gte.sx2 + #define SY2 gte.sy2 + #define SZ0 gte.sz0 + #define SZ1 gte.sz1 + #define SZ2 gte.sz2 + #define SZ3 gte.sz3 + #define RGB0 gte.rgb0 + #define RGB1 gte.rgb1 + #define RGB2 gte.rgb2 + #define RES1 gte.res1 + #define MAC0 gte.mac0 + #define MAC1 gte.mac1 + #define MAC2 gte.mac2 + #define MAC3 gte.mac3 + #define IRGB gte.irgb + #define ORGB gte.orgb + #define LZCS gte.lzcs + #define LZCR gte.lzcr + #define RT11 gte.rt11 + #define RT12 gte.rt12 + #define RT13 gte.rt13 + #define RT21 gte.rt21 + #define RT22 gte.rt22 + #define RT23 gte.rt23 + #define RT31 gte.rt31 + #define RT32 gte.rt32 + #define RT33 gte.rt33 + #define TRX gte.trx + #define TRY gte.trY + #define TRZ gte.trz + #define L11 gte.l11 + #define L12 gte.l12 + #define L13 gte.l13 + #define L21 gte.l21 + #define L22 gte.l22 + #define L23 gte.l23 + #define L31 gte.l31 + #define L32 gte.l32 + #define L33 gte.l33 + #define RBK gte.rbk + #define GBK gte.gbk + #define BBK gte.bbk + #define LR1 gte.lr1 + #define LR2 gte.lr2 + #define LR3 gte.lr3 + #define LG1 gte.lg1 + #define LG2 gte.lg2 + #define LG3 gte.lg3 + #define LB1 gte.lb1 + #define LB2 gte.lb2 + #define LB3 gte.lb3 + #define RFC gte.rfc + #define GFC gte.gfc + #define BFC gte.bfc + #define OFX gte.ofx + #define OFY gte.ofy + #define H gte.h + #define DQA gte.dqa + #define DQB gte.dqb + #define ZSF3 gte.zsf3 + #define ZSF4 gte.zsf4 + #define FLAG gte.flag +#else + #undef GTE_REGISTERS_HPP + + #undef VX0 + #undef VY0 + #undef VZ0 + #undef VX1 + #undef VY1 + #undef VZ1 + #undef VX2 + #undef VY2 + #undef VZ2 + #undef RGBC + #undef OTZ + #undef IR0 + #undef IR1 + #undef IR2 + #undef IR3 + #undef SX0 + #undef SY0 + #undef SX1 + #undef SY1 + #undef SX2 + #undef SY2 + #undef SZ0 + #undef SZ1 + #undef SZ2 + #undef SZ3 + #undef RGB0 + #undef RGB1 + #undef RGB2 + #undef RES1 + #undef MAC0 + #undef MAC1 + #undef MAC2 + #undef MAC3 + #undef IRGB + #undef ORGB + #undef LZCS + #undef LZCR + #undef RT11 + #undef RT12 + #undef RT13 + #undef RT21 + #undef RT22 + #undef RT23 + #undef RT31 + #undef RT32 + #undef RT33 + #undef TRX + #undef TRY + #undef TRZ + #undef L11 + #undef L12 + #undef L13 + #undef L21 + #undef L22 + #undef L23 + #undef L31 + #undef L32 + #undef L33 + #undef RBK + #undef GBK + #undef BBK + #undef LR1 + #undef LR2 + #undef LR3 + #undef LG1 + #undef LG2 + #undef LG3 + #undef LB1 + #undef LB2 + #undef LB3 + #undef RFC + #undef GFC + #undef BFC + #undef OFX + #undef OFY + #undef H + #undef DQA + #undef DQB + #undef ZSF3 + #undef ZSF4 + #undef FLAG +#endif diff --git a/ares/ps1/cpu/core/gte.hpp b/ares/ps1/cpu/core/gte.hpp index 214accc12..d6bbc4716 100644 --- a/ares/ps1/cpu/core/gte.hpp +++ b/ares/ps1/cpu/core/gte.hpp @@ -1,7 +1,81 @@ //{ //Geometry Transformation Engine struct GTE { + i16 vx0, vy0, vz0; + i16 vx1, vy1, vz1; + i16 vx2, vy2, vz2; + u32 rgbc; + u16 otz; + i16 ir0, ir1, ir2, ir3; + i16 sx0, sy0; + i16 sx1, sy1; + i16 sx2, sy2; + u16 sz0, sz1, sz2, sz3; + u32 rgb0, rgb1, rgb2; + u32 res1; + i32 mac0, mac1, mac2, mac3; + u32 irgb, orgb; + u32 lzcs, lzcr; + i16 rt11, rt12, rt13; + i16 rt21, rt22, rt23; + i16 rt31, rt32, rt33; + i32 trx, trY, trz; + i16 l11, l12, l13; + i16 l21, l22, l23; + i16 l31, l32, l33; + i32 rbk, gbk, bbk; + i16 lr1, lr2, lr3; + i16 lg1, lg2, lg3; + i16 lb1, lb2, lb3; + i32 rfc, gfc, bfc; + i32 ofx, ofy; + u16 h; + i16 dqa; + i32 dqb; + i16 zsf3, zsf4; + struct Flag { + struct Saturated { + bool ir0, ir1, ir2, ir3; + bool sx2, sy2, sz1_otz; + bool r, g, b; + } saturated; + struct Underflow { + bool mac0, mac1, mac2, mac3; + } underflow; + struct Overflow { + bool mac0, mac1, mac2, mac3; + bool division; + } overflow; + bool error; + } flag; } gte; //gte-instructions.cpp + auto instructionCFC2(u32& rt, u8 rd) -> void; + auto instructionCTC2(cu32& rt, u8 rd) -> void; + auto instructionMFC2(u32& rt, u8 rd) -> void; + auto instructionMTC2(cu32& rt, u8 rd) -> void; + + auto instructionAVSZ3() -> void; + auto instructionAVSZ4() -> void; + auto instructionCC() -> void; + auto instructionCDP() -> void; + auto instructionDCPL() -> void; + auto instructionDPCS() -> void; + auto instructionDPCT() -> void; + auto instructionGPF() -> void; + auto instructionGPL() -> void; + auto instructionINTPL() -> void; + auto instructionMVMVA() -> void; + auto instructionNCCS() -> void; + auto instructionNCCT() -> void; + auto instructionNCDS() -> void; + auto instructionNCDT() -> void; + auto instructionNCLIP() -> void; + auto instructionNCS() -> void; + auto instructionNCT() -> void; + auto instructionOP() -> void; + auto instructionRTPS() -> void; + auto instructionRTPT() -> void; + auto instructionSQR() -> void; //}; diff --git a/ares/ps1/cpu/interrupt.cpp b/ares/ps1/cpu/interrupt.cpp index 27ab185bc..92783906e 100644 --- a/ares/ps1/cpu/interrupt.cpp +++ b/ares/ps1/cpu/interrupt.cpp @@ -139,8 +139,7 @@ auto CPU::Interrupt::writeHalf(u32 address, u16 value) -> void { } } -auto CPU::Interrupt::writeWord(u32 address, u32 value) -> void { - uint32 data = value; - writeHalf(address & ~3 | 0, value >> 0); - writeHalf(address & ~3 | 2, value >> 16); +auto CPU::Interrupt::writeWord(u32 address, u32 data) -> void { + writeHalf(address & ~3 | 0, data >> 0); + writeHalf(address & ~3 | 2, data >> 16); } diff --git a/ares/ps1/disc/command.cpp b/ares/ps1/disc/command.cpp new file mode 100644 index 000000000..a33385817 --- /dev/null +++ b/ares/ps1/disc/command.cpp @@ -0,0 +1,45 @@ +auto Disc::command(u8 operation) -> void { + print("* CDC ", hex(operation, 2L), "\n"); + + switch(operation) { + case 0x01: return commandGetStatus(); + case 0x19: return commandTest(); + } +} + +//0x01 +auto Disc::commandGetStatus() -> void { + uint8 data; + data.bit(0) = ssr.error; + data.bit(1) = ssr.motorOn; + data.bit(2) = ssr.seekError; + data.bit(3) = ssr.idError; + data.bit(4) = ssr.shellOpen; + data.bit(5) = ssr.reading; + data.bit(6) = ssr.seeking; + data.bit(7) = ssr.playingCDDA; + fifo.response.write(data); + ssr.shellOpen = 0; + + irq.acknowledge.flag = 1; + irq.poll(); +} + +//0x19 +auto Disc::commandTest() -> void { + u8 operation = fifo.parameter.read(); + switch(operation) { + case 0x20: return commandTestControllerDate(); + } +} + +//0x19 0x20 +auto Disc::commandTestControllerDate() -> void { + fifo.response.write(0x95); + fifo.response.write(0x05); + fifo.response.write(0x16); + fifo.response.write(0xc1); + + irq.acknowledge.flag = 1; + irq.poll(); +} diff --git a/ares/ps1/disc/disc.cpp b/ares/ps1/disc/disc.cpp new file mode 100644 index 000000000..69ef57333 --- /dev/null +++ b/ares/ps1/disc/disc.cpp @@ -0,0 +1,84 @@ +#include + +namespace ares::PlayStation { + +Disc disc; +#include "io.cpp" +#include "command.cpp" +#include "irq.cpp" +#include "serialization.cpp" + +auto Disc::load(Node::Object parent) -> void { + node = parent->append("PlayStation"); + + tray = node->append("Disc Tray"); + tray->setFamily("PlayStation"); + tray->setType("Compact Disc"); + tray->setHotSwappable(true); + tray->setAllocate([&](auto name) { return allocate(tray); }); + tray->setConnect([&] { return connect(); }); + tray->setDisconnect([&] { return disconnect(); }); + + fifo.parameter.resize(16); + fifo.response.resize(16); + fifo.data.resize(2340); +} + +auto Disc::unload() -> void { + fifo.parameter.reset(); + fifo.response.reset(); + fifo.data.reset(); + + disconnect(); + tray = {}; + node = {}; +} + +auto Disc::allocate(Node::Port parent) -> Node::Peripheral { + return cd = parent->append("PlayStation"); +} + +auto Disc::connect() -> void { + cd->setManifest([&] { return information.manifest; }); + + information = {}; + if(auto fp = platform->open(cd, "manifest.bml", File::Read, File::Required)) { + information.manifest = fp->reads(); + } + + auto document = BML::unserialize(information.manifest); + information.name = document["game/label"].string(); + + fd = platform->open(cd, "cd.rom", File::Read, File::Required); + if(!fd) return disconnect(); + + //read disc TOC (table of contents) + uint sectors = fd->size() / 2448; + vector subchannel; + subchannel.resize(sectors * 96); + for(uint sector : range(sectors)) { + fd->seek(sector * 2448 + 2352); + fd->read(subchannel.data() + sector * 96, 96); + } + session.decode(subchannel, 96); +} + +auto Disc::disconnect() -> void { + fd = {}; + cd = {}; +} + +auto Disc::main() -> void { + step(33'868'800 / 75); +} + +auto Disc::step(uint clocks) -> void { + Thread::clock += clocks; +} + +auto Disc::power() -> void { + Thread::reset(); + io = {}; +} + +} diff --git a/ares/ps1/disc/disc.hpp b/ares/ps1/disc/disc.hpp new file mode 100644 index 000000000..64a804271 --- /dev/null +++ b/ares/ps1/disc/disc.hpp @@ -0,0 +1,100 @@ +struct Disc : Thread { + Node::Component node; + Node::Port tray; + Node::Peripheral cd; + Shared::File fd; + CD::Session session; + + auto manifest() const -> string { return information.manifest; } + auto name() const -> string { return information.name; } + + //disc.cpp + auto load(Node::Object) -> void; + auto unload() -> void; + + auto allocate(Node::Port) -> Node::Peripheral; + auto connect() -> void; + auto disconnect() -> void; + + auto main() -> void; + auto step(uint clocks) -> void; + auto power() -> void; + + //io.cpp + auto readByte(u32 address) -> u32; + auto readHalf(u32 address) -> u32; + auto readWord(u32 address) -> u32; + auto writeByte(u32 address, u32 data) -> void; + auto writeHalf(u32 address, u32 data) -> void; + auto writeWord(u32 address, u32 data) -> void; + + //command.cpp + auto command(u8 operation) -> void; + auto commandGetStatus() -> void; + auto commandTest() -> void; + auto commandTestControllerDate() -> void; + + //serialization.cpp + auto serialize(serializer&) -> void; + + struct Information { + string manifest; + string name; + } information; + + struct Interrupt { + uint1 enable; + uint1 flag; + }; + + struct IRQ { + //irq.cpp + auto poll() -> void; + + uint5 flag; + uint5 mask; + + Interrupt ready; //INT1 + Interrupt complete; //INT2 + Interrupt acknowledge; //INT3 + Interrupt end; //INT4 + Interrupt error; //INT5 + Interrupt unknown; //INT8 + Interrupt start; //INT10 + } irq; + + struct FIFO { + queue parameter; + queue response; + queue data; + } fifo; + + struct ADPCM { + uint1 mute; + } adpcm; + + struct CDDA { + uint8 volume[4]; + uint8 volumeLatch[4]; + } cdda; + + struct PrimaryStatusRegister { + } psr; + + struct SecondaryStatusRegister { + uint1 error; + uint1 motorOn; + uint1 seekError; + uint1 idError; + uint1 shellOpen; + uint1 reading; + uint1 seeking; + uint1 playingCDDA; + } ssr; + + struct IO { + uint2 index; + } io; +}; + +extern Disc disc; diff --git a/ares/ps1/disc/io.cpp b/ares/ps1/disc/io.cpp new file mode 100644 index 000000000..ebc898b58 --- /dev/null +++ b/ares/ps1/disc/io.cpp @@ -0,0 +1,164 @@ +auto Disc::readByte(u32 address) -> u32 { + uint8 data = 0; + + if(address == 0x1f80'1800) { + data.bit(0) = io.index.bit(0); + data.bit(1) = io.index.bit(1); + data.bit(2) = 0; //XA-ADPCM FIFO (0 = empty) + data.bit(3) = fifo.parameter.empty(); //1 when empty + data.bit(4) = !fifo.parameter.full(); //0 when full + data.bit(5) = !fifo.response.empty(); //0 when empty + data.bit(6) = !fifo.data.empty(); //0 when empty + data.bit(7) = 0; //command/parameter busy (0 = ready) + } + + //response FIFO + if(address == 0x1f80'1801 && (io.index == 0 || io.index == 1 || io.index == 2 || io.index == 3)) { + if(!fifo.response.empty()) data = fifo.response.read(); + } + + //data FIFO + if(address == 0x1f80'1802 && (io.index == 0 || io.index == 1 || io.index == 2 || io.index == 3)) { + } + + //interrupt enable + if(address == 0x1f80'1803 && (io.index == 0 || io.index == 2)) { + data.bit(0) = irq.ready.enable; + data.bit(1) = irq.complete.enable; + data.bit(2) = irq.acknowledge.enable; + data.bit(3) = irq.end.enable; + data.bit(4) = irq.error.enable; + data.bit(5) = 1; + data.bit(6) = 1; + data.bit(7) = 1; + } + + //interrupt flag + if(address == 0x1f80'1803 && (io.index == 1 || io.index == 3)) { + uint3 flags = 0; + if(irq.error.flag ) flags = 5; + if(irq.end.flag ) flags = 4; + if(irq.acknowledge.flag) flags = 3; + if(irq.complete.flag ) flags = 2; + if(irq.ready.flag ) flags = 1; + data.bit(0,2) = flags; + data.bit(3) = irq.end.flag; + data.bit(4) = irq.error.flag; + data.bit(5) = 1; + data.bit(6) = 1; + data.bit(7) = 1; + } + +print("* r", hex(address, 8L), ":", io.index, "=", hex(data, 2L), "\n"); + + return data; +} + +auto Disc::readHalf(u32 address) -> u32 { + uint16 data = readByte(address & ~1 | 0) << 0; + return data | readByte(address & ~1 | 1) << 8; +} + +auto Disc::readWord(u32 address) -> u32 { + uint32 data = readHalf(address & ~3 | 0) << 0; + return data | readHalf(address & ~3 | 2) << 16; +} + +auto Disc::writeByte(u32 address, u32 value) -> void { + uint8 data = value; + +print("* w", hex(address, 8L), ":", io.index, "=", hex(data, 2L), "\n"); + + if(address == 0x1f80'1800) { + io.index = data.bit(0,1); + } + + //command register + if(address == 0x1f80'1801 && io.index == 0) { + command(data); + } + + //sound map data output + if(address == 0x1f80'1801 && io.index == 1) { + } + + //sound map coding information + if(address == 0x1f80'1801 && io.index == 2) { + } + + //audio volume for right CD output to right SPU input + if(address == 0x1f80'1801 && io.index == 3) { + cdda.volumeLatch[3] = data; + } + + //parameter FIFO + if(address == 0x1f80'1802 && io.index == 0) { + if(!fifo.parameter.full()) fifo.parameter.write(data); + } + + //interrupt enable + if(address == 0x1f80'1802 && io.index == 1) { + irq.ready.enable = data.bit(0); + irq.complete.enable = data.bit(1); + irq.acknowledge.enable = data.bit(2); + irq.end.enable = data.bit(3); + irq.error.enable = data.bit(4); + irq.poll(); + } + + //audio volume for left CD output to left SPU input + if(address == 0x1f80'1802 && io.index == 2) { + cdda.volumeLatch[0] = data; + } + + //audio volume for right CD output to left SPU input + if(address == 0x1f80'1802 && io.index == 3) { + cdda.volumeLatch[2] = data; + } + + //request register + if(address == 0x1f80'1803 && io.index == 0) { + } + + //interrupt flag + if(address == 0x1f80'1803 && io.index == 1) { + if(data.bit(0,2) == 7) { + if(0); + else if(irq.ready.flag ) irq.ready.flag = 0; + else if(irq.complete.flag ) irq.complete.flag = 0; + else if(irq.acknowledge.flag) irq.acknowledge.flag = 0; + else if(irq.end.flag ) irq.end.flag = 0; + else if(irq.error.flag ) irq.error.flag = 0; + } + if(data.bit(3)) irq.end.flag = 0; + if(data.bit(4)) irq.error.flag = 0; + if(data.bit(6)) fifo.parameter.flush(); + irq.poll(); + } + + //audio volume for left CD output to right SPU input + if(address == 0x1f80'1803 && io.index == 2) { + cdda.volumeLatch[1] = data; + } + + //audio volume apply changes + if(address == 0x1f80'1803 && io.index == 3) { + adpcm.mute = data.bit(0); + if(data.bit(5)) { + cdda.volume[0] = cdda.volumeLatch[0]; + cdda.volume[1] = cdda.volumeLatch[1]; + cdda.volume[2] = cdda.volumeLatch[2]; + cdda.volume[3] = cdda.volumeLatch[3]; + } + } +} + +auto Disc::writeHalf(u32 address, u32 data) -> void { + writeByte(address & ~1 | 0, data >> 0); + writeByte(address & ~1 | 1, data >> 8); +} + +auto Disc::writeWord(u32 address, u32 data) -> void { + writeHalf(address & ~3 | 0, data >> 0); + writeHalf(address & ~3 | 2, data >> 16); +} diff --git a/ares/ps1/disc/irq.cpp b/ares/ps1/disc/irq.cpp new file mode 100644 index 000000000..df05df50e --- /dev/null +++ b/ares/ps1/disc/irq.cpp @@ -0,0 +1,11 @@ +auto Disc::IRQ::poll() -> void { + bool pending = 0; + pending |= ready.flag & ready.enable; + pending |= complete.flag & complete.enable; + pending |= acknowledge.flag & acknowledge.enable; + pending |= end.flag & end.enable; + pending |= error.flag & error.enable; + + if(pending == 0) cpu.interrupt.lower(CPU::Interrupt::CDROM); + if(pending == 1) cpu.interrupt.raise(CPU::Interrupt::CDROM), print("* IRQ\n"); +} diff --git a/ares/ps1/disc/serialization.cpp b/ares/ps1/disc/serialization.cpp new file mode 100644 index 000000000..a1871971c --- /dev/null +++ b/ares/ps1/disc/serialization.cpp @@ -0,0 +1,2 @@ +auto Disc::serialize(serializer& s) -> void { +} diff --git a/ares/ps1/interface/interface.cpp b/ares/ps1/interface/interface.cpp index b540a9897..e79c8ad1a 100644 --- a/ares/ps1/interface/interface.cpp +++ b/ares/ps1/interface/interface.cpp @@ -5,6 +5,10 @@ namespace ares::PlayStation { Interface* interface = nullptr; auto PlayStationInterface::game() -> string { + if(disc.cd) { + return disc.name(); + } + return "(no disc inserted)"; } diff --git a/ares/ps1/memory/bus.hpp b/ares/ps1/memory/bus.hpp index 9ea63e587..8ce0274f8 100644 --- a/ares/ps1/memory/bus.hpp +++ b/ares/ps1/memory/bus.hpp @@ -9,7 +9,7 @@ if(address <= 0x1f80'107f) return cpu.interrupt.access(__VA_ARGS__); \ if(address <= 0x1f80'10ff) return cpu.dma.access(__VA_ARGS__); \ if(address <= 0x1f80'17ff) return unmapped; \ - if(address <= 0x1f80'180f) return unmapped; \ + if(address <= 0x1f80'180f) return disc.access(__VA_ARGS__); \ if(address <= 0x1f80'181f) return gpu.access(__VA_ARGS__); \ if(address <= 0x1f80'1bff) return unmapped; \ if(address <= 0x1f80'1fff) return spu.access(__VA_ARGS__); \ diff --git a/ares/ps1/ps1.hpp b/ares/ps1/ps1.hpp index 3a12be3d6..23cb3e74a 100644 --- a/ares/ps1/ps1.hpp +++ b/ares/ps1/ps1.hpp @@ -20,6 +20,7 @@ namespace ares::PlayStation { #include #include + #include #include #include #include diff --git a/ares/ps1/spu/io.cpp b/ares/ps1/spu/io.cpp index 79f3d00e5..80a27aa93 100644 --- a/ares/ps1/spu/io.cpp +++ b/ares/ps1/spu/io.cpp @@ -380,8 +380,8 @@ auto SPU::readHalf(u32 address) -> u32 { } auto SPU::readWord(u32 address) -> u32 { - uint16 data = readHalf(address & ~1 ^ 0) << 0; - return data | readHalf(address & ~1 ^ 2) << 16; + uint32 data = readHalf(address & ~3 | 0) << 0; + return data | readHalf(address & ~3 | 2) << 16; } auto SPU::writeByte(u32 address, u32 value) -> void { @@ -792,7 +792,7 @@ auto SPU::writeHalf(u32 address, u32 value) -> void { } } -auto SPU::writeWord(u32 address, u32 value) -> void { - writeHalf(address & ~1 ^ 0, value >> 0); - writeHalf(address & ~1 ^ 2, value >> 16); +auto SPU::writeWord(u32 address, u32 data) -> void { + writeHalf(address & ~3 | 0, data >> 0); + writeHalf(address & ~3 | 2, data >> 16); } diff --git a/ares/ps1/system/serialization.cpp b/ares/ps1/system/serialization.cpp index 3d7a87732..bbfa71699 100644 --- a/ares/ps1/system/serialization.cpp +++ b/ares/ps1/system/serialization.cpp @@ -48,6 +48,7 @@ auto System::serializeAll(serializer& s, bool synchronize) -> void { cpu.serialize(s); gpu.serialize(s); spu.serialize(s); + disc.serialize(s); } auto System::serializeInit(bool synchronize) -> uint { diff --git a/ares/ps1/system/system.cpp b/ares/ps1/system/system.cpp index ce06db15c..0bfa7e559 100644 --- a/ares/ps1/system/system.cpp +++ b/ares/ps1/system/system.cpp @@ -22,6 +22,7 @@ auto System::load(Node::Object& root) -> void { cpu.load(node); gpu.load(node); spu.load(node); + disc.load(node); } auto System::unload() -> void { @@ -30,6 +31,7 @@ auto System::unload() -> void { cpu.unload(); gpu.unload(); spu.unload(); + disc.unload(); node.reset(); } @@ -48,6 +50,7 @@ auto System::power(bool reset) -> void { cpu.power(reset); gpu.power(reset); spu.power(reset); + disc.power(); information.serializeSize[0] = serializeInit(0); information.serializeSize[1] = serializeInit(1); diff --git a/ares/sfc/ppu-performance/background.cpp b/ares/sfc/ppu-performance/background.cpp index 68982d427..95d2ada90 100644 --- a/ares/sfc/ppu-performance/background.cpp +++ b/ares/sfc/ppu-performance/background.cpp @@ -30,7 +30,7 @@ auto PPU::Background::render() -> void { uint y = ppu.vcounter(); if(hires) { hscroll <<= 1; - if(ppu.io.interlace) y = y << 1 | ppu.field(); + if(ppu.io.interlace) y = y << 1 | (ppu.field() && !io.mosaicEnable); } if(io.mosaicEnable) { y -= ppu.mosaic.voffset() << (hires && ppu.io.interlace); @@ -111,7 +111,7 @@ auto PPU::Background::render() -> void { color += data >> shift + 49 & 128; } - mosaicCounter = io.mosaicEnable ? (uint)ppu.mosaic.size : 1; + mosaicCounter = io.mosaicEnable ? ppu.mosaic.size << hires : 1; mosaicPalette = color; mosaicPriority = tilePriority; if(directColorMode) { diff --git a/ares/sfc/ppu/background.cpp b/ares/sfc/ppu/background.cpp index 1716436df..497ff038f 100644 --- a/ares/sfc/ppu/background.cpp +++ b/ares/sfc/ppu/background.cpp @@ -37,7 +37,7 @@ auto PPU::Background::fetchNameTable() -> void { if(hires()) { hscroll <<= 1; - if(ppu.io.interlace) vpixel = vpixel << 1 | ppu.field(); + if(ppu.io.interlace) vpixel = vpixel << 1 | (ppu.field() && !mosaic.enable); } if(mosaic.enable) { vpixel -= ppu.mosaic.voffset() << (hires() && ppu.io.interlace); @@ -197,7 +197,7 @@ auto PPU::Background::run(bool screen) -> void { if(x == 0 && (!hires() || screen == Screen::Below)) { mosaic.hcounter = ppu.mosaic.size; mosaic.pixel = pixel; - } else if(--mosaic.hcounter == 0) { + } else if((!hires() || screen == Screen::Below) && --mosaic.hcounter == 0) { mosaic.hcounter = ppu.mosaic.size; mosaic.pixel = pixel; } else if(mosaic.enable) { diff --git a/lucia/emulator/mega-drive.cpp b/lucia/emulator/mega-drive.cpp index 457948709..9da752dcc 100644 --- a/lucia/emulator/mega-drive.cpp +++ b/lucia/emulator/mega-drive.cpp @@ -167,6 +167,7 @@ auto MegaCD::open(ares::Node::Object node, string name, vfs::file::mode mode, bo } if(auto result = vfs::cdrom::open(game.location)) return result; + MessageDialog().setText( "Failed to load CD-ROM image." ).setAlignment(presentation).error(); diff --git a/lucia/emulator/pc-engine.cpp b/lucia/emulator/pc-engine.cpp index 41828dfdc..b1309f69f 100644 --- a/lucia/emulator/pc-engine.cpp +++ b/lucia/emulator/pc-engine.cpp @@ -173,6 +173,7 @@ auto PCEngineCD::open(ares::Node::Object node, string name, vfs::file::mode mode } if(auto result = vfs::cdrom::open(game.location)) return result; + MessageDialog().setText( "Failed to load CD-ROM image." ).setAlignment(presentation).error(); diff --git a/lucia/emulator/playstation.cpp b/lucia/emulator/playstation.cpp index 91bf5e629..8532c3b52 100644 --- a/lucia/emulator/playstation.cpp +++ b/lucia/emulator/playstation.cpp @@ -26,6 +26,11 @@ auto PlayStation::load() -> bool { return false; } + if(auto port = root->find("PlayStation/Disc Tray")) { + port->allocate(); + port->connect(); + } + return true; } @@ -34,6 +39,29 @@ auto PlayStation::open(ares::Node::Object node, string name, vfs::file::mode mod return Emulator::loadFirmware(firmware[regionID]); } + if(name == "manifest.bml") { + if(auto manifest = medium->manifest(game.location)) { + return vfs::memory::open(manifest.data(), manifest.size()); + } + return Emulator::manifest(game.location); + } + + if(name == "cd.rom") { + if(game.location.iendsWith(".zip")) { + MessageDialog().setText( + "Sorry, compressed CD-ROM images are not currently supported.\n" + "Please extract the image prior to loading it." + ).setAlignment(presentation).error(); + return {}; + } + + if(auto result = vfs::cdrom::open(game.location)) return result; + + MessageDialog().setText( + "Failed to load CD-ROM image." + ).setAlignment(presentation).error(); + } + return {}; }