From 0ed46cc2b1ff6939fcfe18b243591fdc941726f6 Mon Sep 17 00:00:00 2001 From: Near <77224854+near-san@users.noreply.github.com> Date: Wed, 24 Jun 2020 09:25:00 +0000 Subject: [PATCH] Update to ares v114r08 release. - PlayStation: fixed a bug in the SLLV instruction - PlayStation: implemented interrupts --- ares/ares/information.hpp | 2 +- ares/ps1/cpu/core/core.cpp | 14 ++- ares/ps1/cpu/core/decoder.hpp | 2 +- ares/ps1/cpu/core/disassembler.hpp | 2 +- ares/ps1/cpu/cpu.cpp | 1 + ares/ps1/cpu/cpu.hpp | 45 ++++++++- ares/ps1/cpu/dma.cpp | 28 +++++- ares/ps1/cpu/interrupt.cpp | 146 +++++++++++++++++++++++++++++ ares/ps1/gpu/gpu.cpp | 5 + ares/ps1/gpu/gpu.hpp | 9 +- ares/ps1/gpu/io.cpp | 31 +++++- ares/ps1/memory/bus.hpp | 5 +- ares/ps1/memory/memory.hpp | 37 -------- ares/ps1/spu/io.cpp | 52 ++++++++++ ares/ps1/spu/spu.cpp | 2 + ares/ps1/spu/spu.hpp | 20 ++++ 16 files changed, 346 insertions(+), 55 deletions(-) create mode 100644 ares/ps1/cpu/interrupt.cpp create mode 100644 ares/ps1/spu/io.cpp diff --git a/ares/ares/information.hpp b/ares/ares/information.hpp index 6778e2f34..83c08c376 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.7"; + static const string Version = "114.8"; 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/ps1/cpu/core/core.cpp b/ares/ps1/cpu/core/core.cpp index 068ff671f..979045ece 100644 --- a/ares/ps1/cpu/core/core.cpp +++ b/ares/ps1/cpu/core/core.cpp @@ -26,13 +26,21 @@ auto CPU::raiseException(uint code, uint coprocessor) -> void { auto CPU::instruction() -> void { auto address = PC; + if(auto interrupts = scc.cause.interruptPending & scc.status.interruptMask) { + if(interrupt.line & scc.status.frame[0].interruptEnable) { + scc.cause.interruptPending = interrupts; + step(1); + return raiseException(0); + } + } + if constexpr(1) { pipeline.address = address; pipeline.instruction = bus.readWord(address); //instructionDebug(); decoderEXECUTE(); instructionEpilogue(); - step(1); + step(2); } } @@ -65,8 +73,8 @@ auto CPU::instructionEpilogue() -> bool { } auto CPU::instructionDebug() -> void { - pipeline.address = PC; - pipeline.instruction = bus.readWord(pipeline.address); +//pipeline.address = PC; +//pipeline.instruction = bus.readWord(pipeline.address); static vector mask; if(!mask) mask.resize(0x0800'0000); diff --git a/ares/ps1/cpu/core/decoder.hpp b/ares/ps1/cpu/core/decoder.hpp index 62066eb9b..a2b274121 100644 --- a/ares/ps1/cpu/core/decoder.hpp +++ b/ares/ps1/cpu/core/decoder.hpp @@ -87,7 +87,7 @@ op(0x01, INVALID); op(0x02, SRL, RD, RT, SA); op(0x03, SRA, RD, RT, SA); - op(0x04, SLLV, RD, RT, SA); + op(0x04, SLLV, RD, RT, RS); op(0x05, INVALID); op(0x06, SRLV, RD, RT, RS); op(0x07, SRAV, RD, RT, RS); diff --git a/ares/ps1/cpu/core/disassembler.hpp b/ares/ps1/cpu/core/disassembler.hpp index 5ac535c6e..d266a901a 100644 --- a/ares/ps1/cpu/core/disassembler.hpp +++ b/ares/ps1/cpu/core/disassembler.hpp @@ -7,7 +7,7 @@ auto disassemble(u32 address, u32 instruction) -> string; template auto hint(P&&... p) const -> string; - bool showColors = true; + bool showColors = false; bool showValues = true; //private: diff --git a/ares/ps1/cpu/cpu.cpp b/ares/ps1/cpu/cpu.cpp index 21b52d067..fcbffc649 100644 --- a/ares/ps1/cpu/cpu.cpp +++ b/ares/ps1/cpu/cpu.cpp @@ -4,6 +4,7 @@ namespace ares::PlayStation { CPU cpu; #include "core/core.cpp" +#include "interrupt.cpp" #include "dma.cpp" #include "serialization.cpp" diff --git a/ares/ps1/cpu/cpu.hpp b/ares/ps1/cpu/cpu.hpp index ea389509f..2c5ff36fa 100644 --- a/ares/ps1/cpu/cpu.hpp +++ b/ares/ps1/cpu/cpu.hpp @@ -19,14 +19,57 @@ struct CPU : Thread { #include "core/core.hpp" - struct DMA : Memory::IO { + struct Interrupt { + enum : uint { Vblank, GPU, CDROM, DMA, Timer0, Timer1, Timer2, Peripheral, SIO, SPU, PIO }; + + CPU& self; + Interrupt(CPU& self) : self(self) {} + + //interrupt.cpp + auto poll() -> void; + auto pulse(uint source) -> void; + auto raise(uint source) -> void; + auto lower(uint source) -> void; + + auto readByte(u32 address) -> u8; + auto readHalf(u32 address) -> u16; + auto readWord(u32 address) -> u32; + auto writeByte(u32 address, u8 data) -> void; + auto writeHalf(u32 address, u16 data) -> void; + auto writeWord(u32 address, u32 data) -> void; + + struct Source { + uint1 line = 0; + uint1 stat = 0; + uint1 mask = 0; + }; + + uint1 line = 0; + Source vblank; + Source gpu; + Source cdrom; + Source dma; + Source timer0; + Source timer1; + Source timer2; + Source peripheral; + Source sio; + Source spu; + Source pio; + } interrupt{*this}; + + struct DMA { enum : uint { MDECIN, MDECOUT, GPU, CDROM, SPU, PIO, OTC }; //channels CPU& self; DMA(CPU& self) : self(self) {} //dma.cpp + auto readByte(u32 address) -> u8; + auto readHalf(u32 address) -> u16; auto readWord(u32 address) -> u32; + auto writeByte(u32 address, u8 data) -> void; + auto writeHalf(u32 address, u16 data) -> void; auto writeWord(u32 address, u32 data) -> void; auto transferLinear(uint c) -> void; auto transferLinked(uint c) -> void; diff --git a/ares/ps1/cpu/dma.cpp b/ares/ps1/cpu/dma.cpp index 2ddef3045..936bd0461 100644 --- a/ares/ps1/cpu/dma.cpp +++ b/ares/ps1/cpu/dma.cpp @@ -1,20 +1,28 @@ +auto CPU::DMA::readByte(u32 address) -> u8 { + return 0; +} + +auto CPU::DMA::readHalf(u32 address) -> u16 { + return 0; +} + auto CPU::DMA::readWord(u32 address) -> u32 { uint32 data = 0; uint32 c = address >> 4 & 7; //DnMADR: DMA Base Address - if((address & 0xffff'ff0f) == 0x1f80'1080 && c < 7) { + if((address & 0xffff'ff8f) == 0x1f80'1080 && c < 7) { data.bit(0,23) = channel[c].address; } //DnBCR: DMA Block Control - if((address & 0xffff'ff0f) == 0x1f80'1084 && c < 7) { + if((address & 0xffff'ff8f) == 0x1f80'1084 && c < 7) { data.bit( 0,15) = channel[c].length; data.bit(16,31) = channel[c].blocks; } //DnCHCR: DMA Channel Control - if((address & 0xffff'ff0f) == 0x1f80'1088 && c < 7) { + if((address & 0xffff'ff8f) == 0x1f80'1088 && c < 7) { data.bit( 0) = channel[c].direction; data.bit( 1) = channel[c].step; data.bit( 2) = channel[c].chopping.enable; @@ -68,6 +76,12 @@ auto CPU::DMA::readWord(u32 address) -> u32 { return data; } +auto CPU::DMA::writeByte(u32 address, u8 value) -> void { +} + +auto CPU::DMA::writeHalf(u32 address, u16 value) -> void { +} + auto CPU::DMA::writeWord(u32 address, u32 value) -> void { uint32 data = value; uint32 c = address >> 4 & 7; @@ -95,12 +109,14 @@ auto CPU::DMA::writeWord(u32 address, u32 value) -> void { channel[c].trigger = data.bit(28); channel[c].unknown = data.bit(29,30); - //print(c, " ", channel[c].direction, " ", channel[c].synchronization, " ", hex(channel[c].address, 8L), "\n"); - if(channel[c].active()) { + //u32 base = address & 0xffff'fff0; + //print("DMA", c, " ", hex(readWord(base + 8), 8L), " ", hex(readWord(base + 0), 8L), " ", hex(readWord(base + 4), 8L), "\n"); + if(channel[c].synchronization == 0) transferLinear(c); if(channel[c].synchronization == 1) transferLinear(c); if(channel[c].synchronization == 2) transferLinked(c); + self.interrupt.pulse(CPU::Interrupt::DMA); } } @@ -194,6 +210,8 @@ auto CPU::DMA::transferLinked(uint c) -> void { address = header & 0xffffff; if(address & 0x800000) break; } while(--timeout); + channel[c].enable = 0; + channel[c].trigger = 0; } auto CPU::DMA::pollIRQ() -> void { diff --git a/ares/ps1/cpu/interrupt.cpp b/ares/ps1/cpu/interrupt.cpp new file mode 100644 index 000000000..34f63f977 --- /dev/null +++ b/ares/ps1/cpu/interrupt.cpp @@ -0,0 +1,146 @@ +auto CPU::Interrupt::poll() -> void { + line = 0; + line |= vblank.stat & vblank.mask; + line |= gpu.stat & gpu.mask; + line |= cdrom.stat & cdrom.mask; + line |= dma.stat & dma.mask; + line |= timer0.stat & timer0.mask; + line |= timer1.stat & timer1.mask; + line |= timer2.stat & timer2.mask; + line |= peripheral.stat & peripheral.mask; + line |= sio.stat & sio.mask; + line |= spu.stat & spu.mask; + line |= pio.stat & pio.mask; + self.scc.cause.interruptPending.bit(2) = line; +} + +auto CPU::Interrupt::pulse(uint source) -> void { + raise(source); + lower(source); +} + +auto CPU::Interrupt::raise(uint source) -> void { + if(source == 0 && !vblank.line) vblank.line = vblank.stat = 1; + if(source == 1 && !gpu.line) gpu.line = gpu.stat = 1; + if(source == 2 && !cdrom.line) cdrom.line = cdrom.stat = 1; + if(source == 3 && !dma.line) dma.line = dma.stat = 1; + if(source == 4 && !timer0.line) timer0.line = timer0.stat = 1; + if(source == 5 && !timer1.line) timer1.line = timer1.stat = 1; + if(source == 6 && !timer2.line) timer2.line = timer2.stat = 1; + if(source == 7 && !peripheral.line) peripheral.line = peripheral.stat = 1; + if(source == 8 && !sio.line) sio.line = sio.stat = 1; + if(source == 9 && !spu.line) spu.line = spu.stat = 1; + if(source == 10 && !pio.line) pio.line = pio.stat = 1; + poll(); +} + +auto CPU::Interrupt::lower(uint source) -> void { + if(source == 0) vblank.line = 0; + if(source == 1) gpu.line = 0; + if(source == 2) cdrom.line = 0; + if(source == 3) dma.line = 0; + if(source == 4) timer0.line = 0; + if(source == 5) timer1.line = 0; + if(source == 6) timer2.line = 0; + if(source == 7) peripheral.line = 0; + if(source == 8) sio.line = 0; + if(source == 9) spu.line = 0; + if(source == 10) pio.line = 0; + poll(); +} + +auto CPU::Interrupt::readByte(u32 address) -> u8 { + uint8 data = 0; + print("* read byte ", hex(address, 8L), "\n"); + return data; +} + +auto CPU::Interrupt::readHalf(u32 address) -> u16 { + uint16 data = 0; + + //I_STAT + if(address == 0x1f80'1070) { + data.bit( 0) = vblank.stat; + data.bit( 1) = gpu.stat; + data.bit( 2) = cdrom.stat; + data.bit( 3) = dma.stat; + data.bit( 4) = timer0.stat; + data.bit( 5) = timer1.stat; + data.bit( 6) = timer2.stat; + data.bit( 7) = peripheral.stat; + data.bit( 8) = sio.stat; + data.bit( 9) = spu.stat; + data.bit(10) = pio.stat; + } + + //I_MASK + if(address == 0x1f80'1074) { + data.bit( 0) = vblank.mask; + data.bit( 1) = gpu.mask; + data.bit( 2) = cdrom.mask; + data.bit( 3) = dma.mask; + data.bit( 4) = timer0.mask; + data.bit( 5) = timer1.mask; + data.bit( 6) = timer2.mask; + data.bit( 7) = peripheral.mask; + data.bit( 8) = sio.mask; + data.bit( 9) = spu.mask; + data.bit(10) = pio.mask; + } + + return data; +} + +auto CPU::Interrupt::readWord(u32 address) -> u32 { + uint32 data = 0; + data |= readHalf(address & ~3 | 0) << 0; + data |= readHalf(address & ~3 | 2) << 16; + return data; +} + +auto CPU::Interrupt::writeByte(u32 address, u8 value) -> void { + uint8 data = value; + print("* write byte ", hex(address, 8L), "\n"); +} + +auto CPU::Interrupt::writeHalf(u32 address, u16 value) -> void { + uint16 data = value; + + //I_STAT + if(address == 0x1f80'1070) { + if(!data.bit( 0)) vblank.stat = 0; + if(!data.bit( 1)) gpu.stat = 0; + if(!data.bit( 2)) cdrom.stat = 0; + if(!data.bit( 3)) dma.stat = 0; + if(!data.bit( 4)) timer0.stat = 0; + if(!data.bit( 5)) timer1.stat = 0; + if(!data.bit( 6)) timer2.stat = 0; + if(!data.bit( 7)) peripheral.stat = 0; + if(!data.bit( 8)) sio.stat = 0; + if(!data.bit( 9)) spu.stat = 0; + if(!data.bit(10)) pio.stat = 0; + poll(); + } + + //I_MASK + if(address == 0x1f80'1074) { + vblank.mask = data.bit( 0); + gpu.mask = data.bit( 1); + cdrom.mask = data.bit( 2); + dma.mask = data.bit( 3); + timer0.mask = data.bit( 4); + timer1.mask = data.bit( 5); + timer2.mask = data.bit( 6); + peripheral.mask = data.bit( 7); + sio.mask = data.bit( 8); + spu.mask = data.bit( 9); + pio.mask = data.bit(10); + poll(); + } +} + +auto CPU::Interrupt::writeWord(u32 address, u32 value) -> void { + uint32 data = value; + writeHalf(address & ~3 | 0, value >> 0); + writeHalf(address & ~3 | 2, value >> 16); +} diff --git a/ares/ps1/gpu/gpu.cpp b/ares/ps1/gpu/gpu.cpp index 9f63ad213..19d7bca81 100644 --- a/ares/ps1/gpu/gpu.cpp +++ b/ares/ps1/gpu/gpu.cpp @@ -22,8 +22,13 @@ auto GPU::unload() -> void { } auto GPU::main() -> void { + if(io.vcounter == 240) { + cpu.interrupt.raise(CPU::Interrupt::Vblank); + } + if(++io.vcounter == 262) { io.vcounter = 0; + cpu.interrupt.lower(CPU::Interrupt::Vblank); refreshed = true; } diff --git a/ares/ps1/gpu/gpu.hpp b/ares/ps1/gpu/gpu.hpp index 4a107108b..a60df8718 100644 --- a/ares/ps1/gpu/gpu.hpp +++ b/ares/ps1/gpu/gpu.hpp @@ -1,6 +1,6 @@ //Graphics Processing Unit -struct GPU : Thread, Memory::IO { +struct GPU : Thread { Node::Component node; Node::Screen screen; @@ -14,14 +14,19 @@ struct GPU : Thread, Memory::IO { auto power(bool reset) -> void; //io.cpp + auto readByte(u32 address) -> u8; + auto readHalf(u32 address) -> u16; auto readWord(u32 address) -> u32; + auto writeByte(u32 address, u8 data) -> void; + auto writeHalf(u32 address, u16 data) -> void; auto writeWord(u32 address, u32 data) -> void; //serialization.cpp auto serialize(serializer&) -> void; struct IO { - u16 vcounter = 0; + uint16 vcounter = 0; + uint2 dmaDirection = 0; } io; //unserialized: diff --git a/ares/ps1/gpu/io.cpp b/ares/ps1/gpu/io.cpp index 0aaa4b1d3..d913fd548 100644 --- a/ares/ps1/gpu/io.cpp +++ b/ares/ps1/gpu/io.cpp @@ -1,3 +1,11 @@ +auto GPU::readByte(u32 address) -> u8 { + return 0; +} + +auto GPU::readHalf(u32 address) -> u16 { + return 0; +} + auto GPU::readWord(u32 address) -> u32 { uint32 data = 0; @@ -7,14 +15,27 @@ auto GPU::readWord(u32 address) -> u32 { //GPUSTAT if(address == 0x1f80'1814) { - data.bit(26) = 1; //ready to receive command - data.bit(27) = 1; //ready to send VRAM to CPU - data.bit(28) = 1; //ready to receive DMA block + switch(io.dmaDirection) { + case 0: data.bit(25) = 0; break; + case 1: data.bit(25) = 1; break; + case 2: data.bit(25) = 1; break; + case 3: data.bit(25) = 1; break; + } + data.bit(26) = 1; //ready to receive command + data.bit(27) = 1; //ready to send VRAM to CPU + data.bit(28) = 1; //ready to receive DMA block + data.bit(29,30) = io.dmaDirection; } return data; } +auto GPU::writeByte(u32 address, u8 value) -> void { +} + +auto GPU::writeHalf(u32 address, u16 value) -> void { +} + auto GPU::writeWord(u32 address, u32 value) -> void { uint32 data = value; @@ -24,5 +45,9 @@ auto GPU::writeWord(u32 address, u32 value) -> void { //GP1 if(address == 0x1f80'1814) { + u8 command = data >> 24; + if(command == 0x04) { + io.dmaDirection = data.bit(0,1); + } } } diff --git a/ares/ps1/memory/bus.hpp b/ares/ps1/memory/bus.hpp index 83270b7e6..9ea63e587 100644 --- a/ares/ps1/memory/bus.hpp +++ b/ares/ps1/memory/bus.hpp @@ -5,11 +5,14 @@ if(address <= 0x1eff'ffff) return unmapped; \ if(address <= 0x1f7f'ffff) return unmapped; \ if(address <= 0x1f80'03ff) return cpu.cache.access(__VA_ARGS__); \ - if(address <= 0x1f80'107f) return unmapped; \ + if(address <= 0x1f80'106f) return unmapped; \ + if(address <= 0x1f80'107f) return cpu.interrupt.access(__VA_ARGS__); \ if(address <= 0x1f80'10ff) return cpu.dma.access(__VA_ARGS__); \ if(address <= 0x1f80'17ff) return unmapped; \ if(address <= 0x1f80'180f) return unmapped; \ if(address <= 0x1f80'181f) return gpu.access(__VA_ARGS__); \ + if(address <= 0x1f80'1bff) return unmapped; \ + if(address <= 0x1f80'1fff) return spu.access(__VA_ARGS__); \ if(address <= 0x1fbf'ffff) return unmapped; \ if(address <= 0x1fff'ffff) return bios.access(__VA_ARGS__); \ return unmapped; \ diff --git a/ares/ps1/memory/memory.hpp b/ares/ps1/memory/memory.hpp index 9cf1f3a2b..5bfd0d88c 100644 --- a/ares/ps1/memory/memory.hpp +++ b/ares/ps1/memory/memory.hpp @@ -94,43 +94,6 @@ struct Writable { u32 maskWord = 0; }; -template -struct IO { - auto readByte(u32 address) -> u8 { - auto data = ((T*)this)->readWord(address); - switch(address & 3) { - case 0: return data >> 0; - case 1: return data >> 8; - case 2: return data >> 16; - case 3: return data >> 24; - } unreachable; - } - - auto readHalf(u32 address) -> u16 { - auto data = ((T*)this)->readWord(address); - switch(address & 2) { - case 0: return data >> 0; - case 2: return data >> 16; - } unreachable; - } - - auto writeByte(u32 address, u8 data) -> void { - switch(address & 3) { - case 0: return ((T*)this)->writeWord(address, data << 0); - case 1: return ((T*)this)->writeWord(address, data << 8); - case 2: return ((T*)this)->writeWord(address, data << 16); - case 3: return ((T*)this)->writeWord(address, data << 24); - } - } - - auto writeHalf(u32 address, u16 data) -> void { - switch(address & 2) { - case 0: return ((T*)this)->writeWord(address, data << 0); - case 2: return ((T*)this)->writeWord(address, data << 16); - } - } -}; - } struct Bus { diff --git a/ares/ps1/spu/io.cpp b/ares/ps1/spu/io.cpp new file mode 100644 index 000000000..f95908d92 --- /dev/null +++ b/ares/ps1/spu/io.cpp @@ -0,0 +1,52 @@ +auto SPU::readByte(u32 address) -> u8 { + return 0; +} + +auto SPU::readHalf(u32 address) -> u16 { + uint16 data = 0; + + //SPURAMCNT + if(address == 0x1f80'1dac) { + data.bit(1,3) = io.ramTransferType; + } + + //SPUSTAT + if(address == 0x1f80'1dae) { + data.bit(0) = io.cdAudioEnable; + data.bit(1) = io.externalAudioEnable; + data.bit(2) = io.cdAudioReverb; + data.bit(3) = io.externalAudioReverb; + data.bit(4,5) = io.ramTransferMode; + data.bit(7) = io.ramTransferMode.bit(1); //unverified + } + + return data; +} + +auto SPU::readWord(u32 address) -> u32 { + return 0; +} + +auto SPU::writeByte(u32 address, u8 value) -> void { +} + +auto SPU::writeHalf(u32 address, u16 value) -> void { + uint16 data = value; + + //SPUCNT + if(address == 0x1f80'1daa) { + io.cdAudioEnable = data.bit(0); + io.externalAudioEnable = data.bit(1); + io.cdAudioReverb = data.bit(2); + io.externalAudioReverb = data.bit(3); + io.ramTransferMode = data.bit(4,5); + } + + //SPURAMCHT + if(address == 0x1f80'1dac) { + io.ramTransferType = data.bit(1,3); + } +} + +auto SPU::writeWord(u32 address, u32 value) -> void { +} diff --git a/ares/ps1/spu/spu.cpp b/ares/ps1/spu/spu.cpp index d144224f7..5c944cbda 100644 --- a/ares/ps1/spu/spu.cpp +++ b/ares/ps1/spu/spu.cpp @@ -3,6 +3,7 @@ namespace ares::PlayStation { SPU spu; +#include "io.cpp" #include "serialization.cpp" auto SPU::load(Node::Object parent) -> void { @@ -33,6 +34,7 @@ auto SPU::step(uint clocks) -> void { auto SPU::power(bool reset) -> void { Thread::reset(); + io = {}; } } diff --git a/ares/ps1/spu/spu.hpp b/ares/ps1/spu/spu.hpp index b30bf70fb..ecb600e7a 100644 --- a/ares/ps1/spu/spu.hpp +++ b/ares/ps1/spu/spu.hpp @@ -14,8 +14,28 @@ struct SPU : Thread { auto power(bool reset) -> void; + //io.cpp + auto readByte(u32 address) -> u8; + auto readHalf(u32 address) -> u16; + auto readWord(u32 address) -> u32; + auto writeByte(u32 address, u8 data) -> void; + auto writeHalf(u32 address, u16 data) -> void; + auto writeWord(u32 address, u32 data) -> void; + //serialization.cpp auto serialize(serializer&) -> void; + + struct IO { + //SPUCNT + uint1 cdAudioEnable = 0; + uint1 externalAudioEnable = 0; + uint1 cdAudioReverb = 0; + uint1 externalAudioReverb = 0; + uint2 ramTransferMode = 0; + + //SPURAMCNT + uint3 ramTransferType = 0; + } io; }; extern SPU spu;