diff --git a/libretro.cpp b/libretro.cpp index e5dc20bf..465ad5a7 100755 --- a/libretro.cpp +++ b/libretro.cpp @@ -1721,7 +1721,13 @@ static void SetInput(int port, const char *type, void *ptr) static int StateAction(StateMem *sm, int load, int data_only) { - return(0); +#if 0 + if(!MDFN_GetSettingB("psx.clobbers_lament")) + { + return(0); + } +#endif + SFORMAT StateRegs[] = { SFVAR(CD_TrayOpen), @@ -1742,6 +1748,9 @@ static int StateAction(StateMem *sm, int load, int data_only) // Call SetDisc() BEFORE we load CDC state, since SetDisc() has emulation side effects. We might want to clean this up in the future. if(load) { + if(CD_SelectedDisc >= (int)cdifs->size()) + CD_SelectedDisc = -1; + CDC->SetDisc(CD_TrayOpen, (CD_SelectedDisc >= 0 && !CD_TrayOpen) ? (*cdifs)[CD_SelectedDisc] : NULL, (CD_SelectedDisc >= 0 && !CD_TrayOpen) ? cdifs_scex_ids[CD_SelectedDisc] : NULL); } @@ -1751,15 +1760,14 @@ static int StateAction(StateMem *sm, int load, int data_only) ret &= CPU->StateAction(sm, load, data_only); ret &= DMA_StateAction(sm, load, data_only); ret &= TIMER_StateAction(sm, load, data_only); - ret &= CDC->StateAction(sm, load, data_only); + ret &= SIO_StateAction(sm, load, data_only); + + ret &= CDC->StateAction(sm, load, data_only); + ret &= MDEC_StateAction(sm, load, data_only); + ret &= GPU->StateAction(sm, load, data_only); + ret &= SPU->StateAction(sm, load, data_only); - // These need some work still: - //ret &= MDEC_StateAction(sm, load, data_only); - //ret &= SPU->StateAction(sm, load, data_only); ret &= FIO->StateAction(sm, load, data_only); - //ret &= SIO_StateAction(sm, load, data_only); - //ret &= GPU->StateAction(sm, load, data_only); - //// End needing work ret &= IRQ_StateAction(sm, load, data_only); // Do it last. @@ -1913,6 +1921,8 @@ static MDFNSetting PSXSettings[] = { "psx.dbg_level", MDFNSF_NOFLAGS, gettext_noop("Debug printf verbosity level."), NULL, MDFNST_UINT, "0", "0", "4" }, #endif + { "psx.clobbers_lament", MDFNSF_NOFLAGS, gettext_noop("Enable experimental save state functionality."), gettext_noop("Save states will destroy your saved game/memory card data if you're careless, and that will make clobber sad. Poor clobber."), MDFNST_BOOL, "0" }, + { NULL }, }; @@ -2021,7 +2031,7 @@ static Deinterlacer deint; #define MEDNAFEN_CORE_NAME_MODULE "psx" #define MEDNAFEN_CORE_NAME "Mednafen PSX" -#define MEDNAFEN_CORE_VERSION "v0.9.35.1" +#define MEDNAFEN_CORE_VERSION "v0.9.36" #define MEDNAFEN_CORE_EXTENSIONS "cue|toc|m3u|ccd" #define MEDNAFEN_CORE_GEOMETRY_BASE_W 320 #define MEDNAFEN_CORE_GEOMETRY_BASE_H 240 @@ -3039,16 +3049,6 @@ static size_t serialize_size; size_t retro_serialize_size(void) { - //if (serialize_size) - // return serialize_size; - - if (!StateAction) - { - if (log_cb) - log_cb(RETRO_LOG_WARN, "[mednafen]: Module doesn't support save states.\n"); - return 0; - } - StateMem st; memset(&st, 0, sizeof(st)); diff --git a/mednafen/cdrom/SimpleFIFO.h b/mednafen/cdrom/SimpleFIFO.h index b5da866f..0e612435 100644 --- a/mednafen/cdrom/SimpleFIFO.h +++ b/mednafen/cdrom/SimpleFIFO.h @@ -27,6 +27,39 @@ class SimpleFIFO } + INLINE void SaveStatePostLoad(void) + { + read_pos %= data.size(); + write_pos %= data.size(); + in_count %= (data.size() + 1); + } + +#if 0 + INLINE int StateAction(StateMem *sm, int load, int data_only, const char* sname) + { + SFORMAT StateRegs[] = + { + std::vector data; + uint32 size; + + SFVAR(read_pos), + SFVAR(write_pos), + SFVAR(in_count), + SFEND; + } + int ret = MDFNSS_StateAction(sm, load, data_only, sname); + + if(load) + { + read_pos %= data.size(); + write_pos %= data.size(); + in_count %= (data.size() + 1); + } + + return(ret); + } +#endif + INLINE uint32 CanRead(void) { return(in_count); diff --git a/mednafen/psx/cdc.cpp b/mednafen/psx/cdc.cpp index 28cd5d1b..f02f520a 100644 --- a/mednafen/psx/cdc.cpp +++ b/mednafen/psx/cdc.cpp @@ -329,7 +329,8 @@ int PS_CDC::StateAction(StateMem *sm, int load, int data_only) if(load) { - + DMABuffer.SaveStatePostLoad(); + SectorPipe_Pos %= SectorPipe_Count; } return(ret); } diff --git a/mednafen/psx/dma.cpp b/mednafen/psx/dma.cpp index 6a0c84dd..db6ae198 100644 --- a/mednafen/psx/dma.cpp +++ b/mednafen/psx/dma.cpp @@ -233,7 +233,7 @@ static void RecalcHalt(void) } -static INLINE void ChRW(const unsigned ch, const uint32_t CRModeCache, uint32_t *V) +static INLINE void ChRW(const unsigned ch, const uint32_t CRModeCache, uint32_t *V, int32_t *offset) { unsigned extra_cyc_overhead = 0; @@ -255,7 +255,7 @@ static INLINE void ChRW(const unsigned ch, const uint32_t CRModeCache, uint32_t { } else - *V = MDEC_DMARead(); + *V = MDEC_DMARead(offset); break; case CH_GPU: @@ -419,6 +419,7 @@ static INLINE void RunChannelI(const unsigned ch, const uint32_t CRModeCache, in // { uint32_t vtmp; + int32_t voffs = 0; if(MDFN_UNLIKELY(DMACH[ch].CurAddr & 0x800000)) { @@ -431,10 +432,10 @@ static INLINE void RunChannelI(const unsigned ch, const uint32_t CRModeCache, in if(CRModeCache & 0x1) vtmp = MainRAM.ReadU32(DMACH[ch].CurAddr & 0x1FFFFC); - ChRW(ch, CRModeCache, &vtmp); + ChRW(ch, CRModeCache, &vtmp, &voffs); if(!(CRModeCache & 0x1)) - MainRAM.WriteU32(DMACH[ch].CurAddr & 0x1FFFFC, vtmp); + MainRAM.WriteU32((DMACH[ch].CurAddr + (voffs << 2)) & 0x1FFFFC, vtmp); } if(CRModeCache & 0x2) @@ -687,7 +688,8 @@ void DMA_Write(const pscpu_timestamp_t timestamp, uint32_t A, uint32_t V) if(!(OldCC & (1 << 24)) && (V & (1 << 24))) { - //PSX_WARNING("[DMA] Started DMA for channel=%d --- CHCR=0x%08x --- BCR=0x%08x --- scanline=%d", ch, DMACH[ch].ChanControl, DMACH[ch].BlockControl, GPU->GetScanlineNum()); + //if(ch == 0 || ch == 1) + // PSX_WARNING("[DMA] Started DMA for channel=%d --- CHCR=0x%08x --- BCR=0x%08x --- scanline=%d", ch, DMACH[ch].ChanControl, DMACH[ch].BlockControl, GPU->GetScanlineNum()); DMACH[ch].WordCounter = 0; DMACH[ch].ClockCounter = 0; diff --git a/mednafen/psx/frontio.cpp b/mednafen/psx/frontio.cpp index 72515d3b..df68cd6e 100644 --- a/mednafen/psx/frontio.cpp +++ b/mednafen/psx/frontio.cpp @@ -149,13 +149,10 @@ int FrontIO::StateAction(StateMem* sm, int load, int data_only) SFVAR(istatus), - // FIXME: -#if 0 - pscpu_timestamp_t irq10_pulse_ts[2]; - - int32 dsr_pulse_delay[4]; - int32 dsr_active_until_ts[4]; -#endif + // FIXME: Step mode save states. + SFARRAY32(irq10_pulse_ts, sizeof(irq10_pulse_ts) / sizeof(irq10_pulse_ts[0])), + SFARRAY32(dsr_pulse_delay, sizeof(dsr_pulse_delay) / sizeof(dsr_pulse_delay[0])), + SFARRAY32(dsr_active_until_ts, sizeof(dsr_active_until_ts) / sizeof(dsr_active_until_ts[0])), SFEND }; @@ -186,6 +183,11 @@ int FrontIO::StateAction(StateMem* sm, int load, int data_only) ret &= DevicesTap[i]->StateAction(sm, load, data_only, tmpbuf); } + if(load) + { + IRQ_Assert(IRQ_SIO, istatus); + } + return(ret); } diff --git a/mednafen/psx/gpu.cpp b/mednafen/psx/gpu.cpp index 326513dc..d19e1aee 100644 --- a/mednafen/psx/gpu.cpp +++ b/mednafen/psx/gpu.cpp @@ -1538,4 +1538,119 @@ void PS_GPU::StartFrame(EmulateSpecStruct *espec_arg) LineWidths = espec->LineWidths; } +int PS_GPU::StateAction(StateMem *sm, int load, int data_only) +{ + SFORMAT StateRegs[] = + { + SFARRAY16(&GPURAM[0][0], sizeof(GPURAM) / sizeof(GPURAM[0][0])), + + SFVAR(DMAControl), + + SFVAR(ClipX0), + SFVAR(ClipY0), + SFVAR(ClipX1), + SFVAR(ClipY1), + + SFVAR(OffsX), + SFVAR(OffsY), + + SFVAR(dtd), + SFVAR(dfe), + + SFVAR(MaskSetOR), + SFVAR(MaskEvalAND), + + SFVAR(tww), + SFVAR(twh), + SFVAR(twx), + SFVAR(twy), + + SFVAR(TexPageX), + SFVAR(TexPageY), + + SFVAR(SpriteFlip), + + SFVAR(abr), + SFVAR(TexMode), + + SFARRAY32(&BlitterFIFO.data[0], BlitterFIFO.data.size()), + SFVAR(BlitterFIFO.read_pos), + SFVAR(BlitterFIFO.write_pos), + SFVAR(BlitterFIFO.in_count), + + SFVAR(DataReadBuffer), + + SFVAR(IRQPending), + + SFVAR(InCmd), + SFVAR(InCmd_CC), + +#define TVHELPER(n) SFVAR(n.x), SFVAR(n.y), SFVAR(n.u), SFVAR(n.v), SFVAR(n.r), SFVAR(n.g), SFVAR(n.b) + TVHELPER(InQuad_F3Vertices[0]), + TVHELPER(InQuad_F3Vertices[1]), + TVHELPER(InQuad_F3Vertices[2]), +#undef TVHELPER + SFVAR(InQuad_clut), + + SFVAR(InPLine_PrevPoint.x), + SFVAR(InPLine_PrevPoint.y), + SFVAR(InPLine_PrevPoint.r), + SFVAR(InPLine_PrevPoint.g), + SFVAR(InPLine_PrevPoint.b), + + SFVAR(FBRW_X), + SFVAR(FBRW_Y), + SFVAR(FBRW_W), + SFVAR(FBRW_H), + SFVAR(FBRW_CurY), + SFVAR(FBRW_CurX), + + SFVAR(DisplayMode), + SFVAR(DisplayOff), + SFVAR(DisplayFB_XStart), + SFVAR(DisplayFB_YStart), + + SFVAR(HorizStart), + SFVAR(HorizEnd), + + SFVAR(VertStart), + SFVAR(VertEnd), + + SFVAR(DisplayFB_CurYOffset), + SFVAR(DisplayFB_CurLineYReadout), + + SFVAR(InVBlank), + + SFVAR(LinesPerField), + SFVAR(scanline), + SFVAR(field), + SFVAR(field_ram_readout), + SFVAR(PhaseChange), + + SFVAR(DotClockCounter), + + SFVAR(GPUClockCounter), + SFVAR(LineClockCounter), + SFVAR(LinePhase), + + SFVAR(DrawTimeAvail), + + SFEND + }; + int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "GPU"); + + if(load) + { + RecalcTexWindowLUT(); + BlitterFIFO.SaveStatePostLoad(); + + HorizStart &= 0xFFF; + HorizEnd &= 0xFFF; + + IRQ_Assert(IRQ_GPU, IRQPending); + } + + return(ret); +} + } diff --git a/mednafen/psx/gpu.h b/mednafen/psx/gpu.h index 90af62af..2f28a533 100644 --- a/mednafen/psx/gpu.h +++ b/mednafen/psx/gpu.h @@ -46,6 +46,8 @@ class PS_GPU void Power(void) MDFN_COLD; + int StateAction(StateMem *sm, int load, int data_only); + void ResetTS(void); void StartFrame(EmulateSpecStruct *espec); diff --git a/mednafen/psx/input/guncon.cpp b/mednafen/psx/input/guncon.cpp index ef7f86ec..c242b6b7 100644 --- a/mednafen/psx/input/guncon.cpp +++ b/mednafen/psx/input/guncon.cpp @@ -30,6 +30,7 @@ class InputDevice_GunCon : public InputDevice virtual ~InputDevice_GunCon(); virtual void Power(void); + virtual int StateAction(StateMem* sm, int load, int data_only, const char* section_name); virtual void UpdateInput(const void *data); virtual bool RequireNoFrameskip(void); virtual pscpu_timestamp_t GPULineHook(const pscpu_timestamp_t line_timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width, const unsigned pix_clock_offset, const unsigned pix_clock, const unsigned pix_clock_divider); @@ -113,6 +114,52 @@ void InputDevice_GunCon::Power(void) line_counter = 0; } +int InputDevice_GunCon::StateAction(StateMem* sm, int load, int data_only, const char* section_name) +{ + SFORMAT StateRegs[] = + { + SFVAR(dtr), + + SFVAR(buttons), + SFVAR(trigger_eff), + SFVAR(trigger_noclear), + SFVAR(hit_x), + SFVAR(hit_y), + + SFVAR(nom_x), + SFVAR(nom_y), + SFVAR(os_shot_counter), + SFVAR(prev_oss), + + SFVAR(command_phase), + SFVAR(bitpos), + SFVAR(receive_buffer), + + SFVAR(command), + + SFARRAY(transmit_buffer, sizeof(transmit_buffer)), + SFVAR(transmit_pos), + SFVAR(transmit_count), + + SFVAR(prev_vsync), + SFVAR(line_counter), + + SFEND + }; + int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name); + + if(load) + { + if((transmit_pos + transmit_count) > sizeof(transmit_buffer)) + { + transmit_pos = 0; + transmit_count = 0; + } + } + + return(ret); +} + void InputDevice_GunCon::UpdateInput(const void *data) { uint8 *d8 = (uint8 *)data; diff --git a/mednafen/psx/input/justifier.cpp b/mednafen/psx/input/justifier.cpp index f77f1add..a74cd0c4 100644 --- a/mednafen/psx/input/justifier.cpp +++ b/mednafen/psx/input/justifier.cpp @@ -30,6 +30,7 @@ class InputDevice_Justifier : public InputDevice virtual ~InputDevice_Justifier(); virtual void Power(void); + virtual int StateAction(StateMem* sm, int load, int data_only, const char* section_name); virtual void UpdateInput(const void *data); virtual bool RequireNoFrameskip(void); virtual pscpu_timestamp_t GPULineHook(const pscpu_timestamp_t timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width, const unsigned pix_clock_offset, const unsigned pix_clock, const unsigned pix_clock_divider); @@ -135,6 +136,52 @@ void InputDevice_Justifier::UpdateInput(const void *data) prev_oss = d8[4] & 0x8; } +int InputDevice_Justifier::StateAction(StateMem* sm, int load, int data_only, const char* section_name) +{ + SFORMAT StateRegs[] = + { + SFVAR(dtr), + + SFVAR(buttons), + SFVAR(trigger_eff), + SFVAR(trigger_noclear), + + SFVAR(need_hit_detect), + + SFVAR(nom_x), + SFVAR(nom_y), + SFVAR(os_shot_counter), + SFVAR(prev_oss), + + SFVAR(command_phase), + SFVAR(bitpos), + SFVAR(receive_buffer), + + SFVAR(command), + + SFARRAY(transmit_buffer, sizeof(transmit_buffer)), + SFVAR(transmit_pos), + SFVAR(transmit_count), + + SFVAR(prev_vsync), + SFVAR(line_counter), + + SFEND + }; + int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name); + + if(load) + { + if((transmit_pos + transmit_count) > sizeof(transmit_buffer)) + { + transmit_pos = 0; + transmit_count = 0; + } + } + + return(ret); +} + bool InputDevice_Justifier::RequireNoFrameskip(void) { return(true); diff --git a/mednafen/psx/input/memcard.cpp b/mednafen/psx/input/memcard.cpp index 6ac235e7..8151ec18 100644 --- a/mednafen/psx/input/memcard.cpp +++ b/mednafen/psx/input/memcard.cpp @@ -53,12 +53,26 @@ class InputDevice_Memcard : public InputDevice private: + void Format(void); + bool presence_new; uint8 card_data[1 << 17]; uint8 rw_buffer[128]; uint8 write_xor; + // + // Used to avoid saving unused memory cards' card data in save states. + // Set to false on object initialization, set to true when data is written to card_data that differs + // from existing data(either from loading a memory card saved to disk, or from a game writing to the memory card). + // + // Save and load its state to/from save states. + // + bool data_used; + + // + // Do not save dirty_count in save states! + // uint64 dirty_count; bool dtr; @@ -74,38 +88,43 @@ class InputDevice_Memcard : public InputDevice uint32 transmit_count; }; +void InputDevice_Memcard::Format(void) +{ + memset(card_data, 0x00, sizeof(card_data)); + + card_data[0x00] = 0x4D; + card_data[0x01] = 0x43; + card_data[0x7F] = 0x0E; + + for(unsigned int A = 0x80; A < 0x800; A += 0x80) + { + card_data[A + 0x00] = 0xA0; + card_data[A + 0x08] = 0xFF; + card_data[A + 0x09] = 0xFF; + card_data[A + 0x7F] = 0xA0; + } + + for(unsigned int A = 0x0800; A < 0x1200; A += 0x80) + { + card_data[A + 0x00] = 0xFF; + card_data[A + 0x01] = 0xFF; + card_data[A + 0x02] = 0xFF; + card_data[A + 0x03] = 0xFF; + card_data[A + 0x08] = 0xFF; + card_data[A + 0x09] = 0xFF; + } +} + InputDevice_Memcard::InputDevice_Memcard() { Power(); + data_used = false; dirty_count = 0; // Init memcard as formatted. assert(sizeof(card_data) == (1 << 17)); - memset(card_data, 0x00, sizeof(card_data)); - - card_data[0x00] = 0x4D; - card_data[0x01] = 0x43; - card_data[0x7F] = 0x0E; - - for(unsigned int A = 0x80; A < 0x800; A += 0x80) - { - card_data[A + 0x00] = 0xA0; - card_data[A + 0x08] = 0xFF; - card_data[A + 0x09] = 0xFF; - card_data[A + 0x7F] = 0xA0; - } - - for(unsigned int A = 0x0800; A < 0x1200; A += 0x80) - { - card_data[A + 0x00] = 0xFF; - card_data[A + 0x01] = 0xFF; - card_data[A + 0x02] = 0xFF; - card_data[A + 0x03] = 0xFF; - card_data[A + 0x08] = 0xFF; - card_data[A + 0x09] = 0xFF; - } - + Format(); } InputDevice_Memcard::~InputDevice_Memcard() @@ -143,7 +162,6 @@ int InputDevice_Memcard::StateAction(StateMem* sm, int load, int data_only, cons { SFVAR(presence_new), - SFARRAY(card_data, sizeof(card_data)), SFARRAY(rw_buffer, sizeof(rw_buffer)), SFVAR(write_xor), @@ -159,14 +177,41 @@ int InputDevice_Memcard::StateAction(StateMem* sm, int load, int data_only, cons SFVAR(transmit_buffer), SFVAR(transmit_count), + SFVAR(data_used), + SFEND }; - int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name); - if(load) + SFORMAT CD_StateRegs[] = { - dirty_count++; + SFARRAY(card_data, sizeof(card_data)), + SFEND + }; + int ret = 1; + + if(MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name) != 0) + { + //printf("%s data_used=%d\n", section_name, data_used); + if(data_used) + { + std::string tmp_name = std::string(section_name) + "_DT"; + + ret &= MDFNSS_StateAction(sm, load, data_only, CD_StateRegs, tmp_name.c_str()); + } + + if(load) + { + if(data_used) + dirty_count++; + else + { + //printf("Format: %s\n", section_name); + Format(); + } + } } + else + ret = 0; return(ret); } @@ -417,6 +462,7 @@ bool InputDevice_Memcard::Clock(bool TxD, int32 &dsr_pulse_delay) { memcpy(&card_data[addr << 7], rw_buffer, 128); dirty_count++; + data_used = true; } } @@ -461,13 +507,18 @@ void InputDevice_Memcard::ReadNV(uint8 *buffer, uint32 offset, uint32 size) void InputDevice_Memcard::WriteNV(const uint8 *buffer, uint32 offset, uint32 size) { if(size) + { dirty_count++; + } while(size--) { - card_data[offset & (sizeof(card_data) - 1)] = *buffer; - buffer++; - offset++; + if(card_data[offset & (sizeof(card_data) - 1)] != *buffer) + data_used = true; + + card_data[offset & (sizeof(card_data) - 1)] = *buffer; + buffer++; + offset++; } } diff --git a/mednafen/psx/input/mouse.cpp b/mednafen/psx/input/mouse.cpp index dd91a031..3ff12210 100644 --- a/mednafen/psx/input/mouse.cpp +++ b/mednafen/psx/input/mouse.cpp @@ -13,6 +13,7 @@ class InputDevice_Mouse : public InputDevice virtual ~InputDevice_Mouse(); virtual void Power(void); + virtual int StateAction(StateMem* sm, int load, int data_only, const char* section_name); virtual void UpdateInput(const void *data); virtual void Update(const pscpu_timestamp_t timestamp); @@ -105,6 +106,45 @@ void InputDevice_Mouse::Power(void) transmit_count = 0; } +int InputDevice_Mouse::StateAction(StateMem* sm, int load, int data_only, const char* section_name) +{ + SFORMAT StateRegs[] = + { + SFVAR(clear_timeout), + + SFVAR(dtr), + + SFVAR(button), + SFVAR(button_post_mask), + SFVAR(accum_xdelta), + SFVAR(accum_ydelta), + + SFVAR(command_phase), + SFVAR(bitpos), + SFVAR(receive_buffer), + + SFVAR(command), + + SFARRAY(transmit_buffer, sizeof(transmit_buffer)), + SFVAR(transmit_pos), + SFVAR(transmit_count), + + SFEND + }; + int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name); + + if(load) + { + if((transmit_pos + transmit_count) > sizeof(transmit_buffer)) + { + transmit_pos = 0; + transmit_count = 0; + } + } + + return(ret); +} + void InputDevice_Mouse::UpdateInput(const void *data) { accum_xdelta += (int32)MDFN_de32lsb((uint8*)data + 0); diff --git a/mednafen/psx/input/multitap.cpp b/mednafen/psx/input/multitap.cpp index bc26ca11..70be7412 100644 --- a/mednafen/psx/input/multitap.cpp +++ b/mednafen/psx/input/multitap.cpp @@ -144,6 +144,42 @@ void InputDevice_Multitap::Power(void) } } +int InputDevice_Multitap::StateAction(StateMem* sm, int load, int data_only, const char* section_name) +{ + SFORMAT StateRegs[] = + { + SFVAR(dtr), + + SFVAR(selected_device), + SFVAR(full_mode_setting), + + SFVAR(full_mode), + SFVAR(mc_mode), + + SFVAR(fm_dp), + SFARRAY(&fm_buffer[0][0], sizeof(fm_buffer) / sizeof(fm_buffer[0][0])), + + SFVAR(fm_deferred_error_temp), + SFVAR(fm_deferred_error), + SFVAR(fm_command_error), + + SFVAR(command), + SFVAR(receive_buffer), + SFVAR(bit_counter), + SFVAR(byte_counter), + + SFEND + }; + int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name); + + if(load) + { + + } + + return(ret); +} + void InputDevice_Multitap::SetDTR(bool new_dtr) { bool old_dtr = dtr; diff --git a/mednafen/psx/input/multitap.h b/mednafen/psx/input/multitap.h index c1f17d58..4736d1b2 100644 --- a/mednafen/psx/input/multitap.h +++ b/mednafen/psx/input/multitap.h @@ -11,6 +11,7 @@ class InputDevice_Multitap : public InputDevice InputDevice_Multitap(); virtual ~InputDevice_Multitap(); virtual void Power(void); + virtual int StateAction(StateMem* sm, int load, int data_only, const char* section_name); void SetSubDevice(unsigned int sub_index, InputDevice *device, InputDevice *mc_device); diff --git a/mednafen/psx/mdec.cpp b/mednafen/psx/mdec.cpp index 049587e7..79e4bf2b 100644 --- a/mednafen/psx/mdec.cpp +++ b/mednafen/psx/mdec.cpp @@ -15,9 +15,48 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* + MDEC_READ_FIFO(tfr) vs InCounter vs MDEC_DMACanRead() is a bit fragile right now. Actually, the entire horrible state machine monstrosity is fragile. + + TODO: OutFIFOReady, so <16bpp works right. + + TODO CODE: + + bool InFIFOReady; + + if(InFIFO.CanWrite()) + { + InFIFO.WriteUnit(V); + + if(InCommand) + { + if(InCounter != 0xFFFF) + { + InCounter--; + + // This condition when InFIFO.CanWrite() != 0 is a bit buggy on real hardware, decoding loop *seems* to be reading too + // much and pulling garbage from the FIFO. + if(InCounter == 0xFFFF) + InFIFOReady = true; + } + + if(InFIFO.CanWrite() == 0) + InFIFOReady = true; + } + } +*/ + +// Good test-case games: +// Dragon Knight 4(bad disc?) +// Final Fantasy 7 intro movie. +// GameShark Version 4.0 intro movie; (clever) abuse of DMA channel 0. +// SimCity 2000 startup. + + #include "psx.h" #include "mdec.h" +#include "../masmem.h" #include "../cdrom/SimpleFIFO.h" #include @@ -33,35 +72,47 @@ namespace MDFN_IEN_PSX { +static int32 ClockCounter; +static unsigned MDRPhase; +static SimpleFIFO InFIFO(0x20); +static SimpleFIFO OutFIFO(0x20); -static bool block_ready; -static int16_t block_y[2][2][8][8]; -static int16_t block_cb[8][8]; // [y >> 1][x >> 1] -static int16_t block_cr[8][8]; // [y >> 1][x >> 1] +static int8 block_y[8][8]; +static int8 block_cb[8][8]; // [y >> 1][x >> 1] +static int8 block_cr[8][8]; // [y >> 1][x >> 1] -static int32_t run_time; -static uint32_t Command; +static uint32 Control; +static uint32 Command; +static bool InCommand; -static uint8_t QMatrix[2][64]; -static uint32_t QMIndex; +static uint8 QMatrix[2][64]; +static uint32 QMIndex; -static int16_t IDCTMatrix[64] MDFN_ALIGN(16); -static uint32_t IDCTMIndex; +static int16 IDCTMatrix[64] MDFN_ALIGN(16); +static uint32 IDCTMIndex; -static uint8_t QScale; +static uint8 QScale; -static int16_t Coeff[6][64] MDFN_ALIGN(16); -static uint32_t CoeffIndex; -static uint32_t DecodeWB; +static int16 Coeff[64] MDFN_ALIGN(16); +static uint32 CoeffIndex; +static uint32 DecodeWB; -static SimpleFIFO InputBuffer(65536); -static SimpleFIFO OutBuffer(384); +static union +{ + uint32 pix32[48]; + uint16 pix16[96]; + uint8 pix8[192]; +} PixelBuffer; +static uint32 PixelBufferReadOffset; +static uint32 PixelBufferCount32; -static uint32_t InCounter; -static bool BlockEnd; -static bool DecodeEnd; +static uint16 InCounter; -static const uint8_t ZigZag[64] = +static uint8 RAMOffsetY; +static uint8 RAMOffsetCounter; +static uint8 RAMOffsetWWS; + +static const uint8 ZigZag[64] = { 0x00, 0x08, 0x01, 0x02, 0x09, 0x10, 0x18, 0x11, 0x0a, 0x03, 0x04, 0x0b, 0x12, 0x19, 0x20, 0x28, @@ -75,21 +126,20 @@ static const uint8_t ZigZag[64] = void MDEC_Power(void) { -#if 0 - for(int i = 0; i < 64; i++) - { - int d = ((ZigZag[i] & 0x7) << 3) | ((ZigZag[i] >> 3) & 0x7); - printf("0x%02x, ", d); + ClockCounter = 0; + MDRPhase = 0; - if((i & 0x7) == 7) - printf("\n"); - } -#endif + InFIFO.Flush(); + OutFIFO.Flush(); - run_time = 0; - block_ready = false; + memset(block_y, 0, sizeof(block_y)); + memset(block_cb, 0, sizeof(block_cb)); + memset(block_cr, 0, sizeof(block_cr)); + Control = 0; Command = 0; + InCommand = false; + memset(QMatrix, 0, sizeof(QMatrix)); QMIndex = 0; @@ -102,30 +152,40 @@ void MDEC_Power(void) CoeffIndex = 0; DecodeWB = 0; - OutBuffer.Flush(); + memset(PixelBuffer.pix32, 0, sizeof(PixelBuffer.pix32)); + PixelBufferReadOffset = 0; + PixelBufferCount32 = 0; InCounter = 0; - BlockEnd = 0; - DecodeEnd = 0; -} -#define SFFIFO16(fifoobj) SFARRAY16(&fifoobj.data[0], fifoobj.data.size()), \ - SFVAR(fifoobj.read_pos), \ - SFVAR(fifoobj.write_pos), \ - SFVAR(fifoobj.in_count) + RAMOffsetY = 0; + RAMOffsetCounter = 0; + RAMOffsetWWS = 0; +} int MDEC_StateAction(StateMem *sm, int load, int data_only) { SFORMAT StateRegs[] = { - SFVAR(block_ready), + SFVAR(ClockCounter), + SFVAR(MDRPhase), - SFARRAY16(&block_y[0][0][0][0], sizeof(block_y) / sizeof(block_y[0][0][0][0])), - SFARRAY16(&block_cb[0][0], sizeof(block_cb) / sizeof(block_cb[0][0])), - SFARRAY16(&block_cr[0][0], sizeof(block_cr) / sizeof(block_cr[0][0])), +#define SFFIFO32(fifoobj) SFARRAY32(&fifoobj.data[0], fifoobj.data.size()), \ + SFVAR(fifoobj.read_pos), \ + SFVAR(fifoobj.write_pos), \ + SFVAR(fifoobj.in_count) - SFVAR(run_time), + SFFIFO32(InFIFO), + SFFIFO32(OutFIFO), +#undef SFFIFO + + SFARRAY(&block_y[0][0], sizeof(block_y) / sizeof(block_y[0][0])), + SFARRAY(&block_cb[0][0], sizeof(block_cb) / sizeof(block_cb[0][0])), + SFARRAY(&block_cr[0][0], sizeof(block_cr) / sizeof(block_cr[0][0])), + + SFVAR(Control), SFVAR(Command), + SFVAR(InCommand), SFARRAY(&QMatrix[0][0], sizeof(QMatrix) / sizeof(QMatrix[0][0])), SFVAR(QMIndex), @@ -135,17 +195,19 @@ int MDEC_StateAction(StateMem *sm, int load, int data_only) SFVAR(QScale), - SFARRAY16(&Coeff[0][0], sizeof(Coeff) / sizeof(Coeff[0][0])), + SFARRAY16(&Coeff[0], sizeof(Coeff) / sizeof(Coeff[0])), SFVAR(CoeffIndex), SFVAR(DecodeWB), - - SFFIFO16(InputBuffer), - SFFIFO16(OutBuffer), + SFARRAY32(&PixelBuffer.pix32[0], sizeof(PixelBuffer.pix32) / sizeof(PixelBuffer.pix32[0])), + SFVAR(PixelBufferReadOffset), + SFVAR(PixelBufferCount32), SFVAR(InCounter), - SFVAR(BlockEnd), - SFVAR(DecodeEnd), + + SFVAR(RAMOffsetY), + SFVAR(RAMOffsetCounter), + SFVAR(RAMOffsetWWS), SFEND }; @@ -154,542 +216,427 @@ int MDEC_StateAction(StateMem *sm, int load, int data_only) if(load) { - + InFIFO.SaveStatePostLoad(); + OutFIFO.SaveStatePostLoad(); } return(ret); } -static void DecodeImage(void); -static INLINE void WriteImageData(uint16_t V) +static INLINE int8 Mask9ClampS8(int32 v) { - const uint32_t qmw = (bool)(DecodeWB < 2); + v = sign_x_to_s32(9, v); - //printf("MDEC DMA SubWrite: %04x\n", V); + if(v < -128) + v = -128; - if(!CoeffIndex) - { - if(DecodeWB == 0 && V == 0xFE00) - { - InputBuffer.Flush(); - return; - } - QScale = V >> 10; + if(v > 127) + v = 127; - { - int q = QMatrix[qmw][0]; // No QScale here! - int ci = sign_10_to_s16(V & 0x3FF); - int tmp; - - if(q != 0) - tmp = ((ci * q) << 4) + (ci ? ((ci < 0) ? 8 : -8) : 0); - else - tmp = (ci * 2) << 4; - - // Not sure if it should be 0x3FFF or 0x3FF0 or maybe 0x3FF8? - Coeff[DecodeWB][ZigZag[0]] = std::min(0x3FFF, std::max(-0x4000, tmp)); - CoeffIndex++; - } - } - else - { - if(V == 0xFE00) - { - BlockEnd = true; - while(CoeffIndex < 64) - Coeff[DecodeWB][ZigZag[CoeffIndex++]] = 0; - } - else - { - uint32_t rlcount = V >> 10; - - for(uint32_t i = 0; i < rlcount && CoeffIndex < 64; i++) - { - Coeff[DecodeWB][ZigZag[CoeffIndex]] = 0; - CoeffIndex++; - } - - if(CoeffIndex < 64) - { - int q = QScale * QMatrix[qmw][CoeffIndex]; - int ci = sign_10_to_s16(V & 0x3FF); - int tmp; - - if(q != 0) - tmp = (((ci * q) >> 3) << 4) + (ci ? ((ci < 0) ? 8 : -8) : 0); - else - tmp = (ci * 2) << 4; - - // Not sure if it should be 0x3FFF or 0x3FF0 or maybe 0x3FF8? - Coeff[DecodeWB][ZigZag[CoeffIndex]] = std::min(0x3FFF, std::max(-0x4000, tmp)); - CoeffIndex++; - } - } - } - - if(CoeffIndex == 64 && BlockEnd) - { - BlockEnd = false; - CoeffIndex = 0; - - //printf("Block %d finished\n", DecodeWB); - - DecodeWB++; - if(DecodeWB == (((Command >> 27) & 2) ? 6 : 3)) - { - DecodeWB = 0; - - DecodeImage(); - } - } + return v; } -template -static void IDCT_1D_Multi(int16_t *in_coeff, int16_t *out_coeff) +template +static void IDCT_1D_Multi(int16 *in_coeff, T *out_coeff) { - unsigned col, x; #if defined(__SSE2__) - for(col = 0; col < 8; col++) - { - __m128i c = _mm_load_si128((__m128i *)&in_coeff[(col * 8)]); +{ + for(unsigned col = 0; col < 8; col++) + { + __m128i c = _mm_load_si128((__m128i *)&in_coeff[(col * 8)]); - for(x = 0; x < 8; x++) - { - __m128i sum; - __m128i m; - int32_t tmp[4] MDFN_ALIGN(16); + for(unsigned x = 0; x < 8; x++) + { + __m128i sum; + __m128i m; + int32 tmp[4] MDFN_ALIGN(16); - m = _mm_load_si128((__m128i *)&IDCTMatrix[(x * 8)]); - sum = _mm_madd_epi16(m, c); - sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, (3 << 0) | (2 << 2) | (1 << 4) | (0 << 6))); - sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, (1 << 0) | (0 << 2))); + m = _mm_load_si128((__m128i *)&IDCTMatrix[(x * 8)]); + sum = _mm_madd_epi16(m, c); + sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, (3 << 0) | (2 << 2) | (1 << 4) | (0 << 6))); + sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, (1 << 0) | (0 << 2))); - //_mm_store_ss((float *)&tmp[0], (__m128)sum); - _mm_store_si128((__m128i*)tmp, sum); + //_mm_store_ss((float *)&tmp[0], (__m128)sum); + _mm_store_si128((__m128i*)tmp, sum); - if(phase) - out_coeff[(col * 8) + x] = (tmp[0] + 0x4000) >> 15; - else - out_coeff[(x * 8) + col] = (tmp[0] + 0x4000) >> 15; - } - } + if(sizeof(T) == 1) + out_coeff[(col * 8) + x] = Mask9ClampS8((tmp[0] + 0x4000) >> 15); + else + out_coeff[(x * 8) + col] = (tmp[0] + 0x4000) >> 15; + } + } +} #else - for(col = 0; col < 8; col++) + for(unsigned col = 0; col < 8; col++) + { + for(unsigned x = 0; x < 8; x++) + { + int32 sum = 0; + + for(unsigned u = 0; u < 8; u++) { - for(x = 0; x < 8; x++) - { - unsigned u; - int32_t sum = 0; - - for(u = 0; u < 8; u++) - sum += (in_coeff[(col * 8) + u] * IDCTMatrix[(x * 8) + u]); - - if(phase) - out_coeff[(col * 8) + x] = (sum + 0x4000) >> 15; - else - out_coeff[(x * 8) + col] = (sum + 0x4000) >> 15; - } + sum += (in_coeff[(col * 8) + u] * IDCTMatrix[(x * 8) + u]); } + + if(sizeof(T) == 1) + out_coeff[(col * 8) + x] = Mask9ClampS8((sum + 0x4000) >> 15); + else + out_coeff[(x * 8) + col] = (sum + 0x4000) >> 15; + } + } #endif } -static void IDCT(int16_t *in_coeff, int16_t *out_coeff) NO_INLINE; -static void IDCT(int16_t *in_coeff, int16_t *out_coeff) +static void IDCT(int16 *in_coeff, int8 *out_coeff) NO_INLINE; +static void IDCT(int16 *in_coeff, int8 *out_coeff) { - int16_t tmpbuf[64] MDFN_ALIGN(16); + int16 tmpbuf[64] MDFN_ALIGN(16); - IDCT_1D_Multi<0>(in_coeff, tmpbuf); - IDCT_1D_Multi<1>(tmpbuf, out_coeff); + IDCT_1D_Multi(in_coeff, tmpbuf); + IDCT_1D_Multi(tmpbuf, out_coeff); } -static void YCbCr_to_RGB(const int32_t y, const int32_t cb, const int32_t cr, uint8_t &r, uint8_t &g, uint8_t &b) +static INLINE void YCbCr_to_RGB(const int8 y, const int8 cb, const int8 cr, int &r, int &g, int &b) { - int rt = (y + 128) + ((91881 * cr) >> 16); - int gt = (y + 128) - ((22525 * cb) >> 16) - ((46812 * cr) >> 16); - int bt = (y + 128) + ((116130 * cb) >> 16); + // + // The formula for green is still a bit off(precision/rounding issues when both cb and cr are non-zero). + // - r = std::max(std::min(rt, 255), 0); - g = std::max(std::min(gt, 255), 0); - b = std::max(std::min(bt, 255), 0); + r = Mask9ClampS8(y + (((359 * cr) + 0x80) >> 8)); + //g = Mask9ClampS8(y + (((-88 * cb) + (-183 * cr) + 0x80) >> 8)); + g = Mask9ClampS8(y + ((((-88 * cb) &~ 0x1F) + ((-183 * cr) &~ 0x07) + 0x80) >> 8)); + b = Mask9ClampS8(y + (((454 * cb) + 0x80) >> 8)); + + r ^= 0x80; + g ^= 0x80; + b ^= 0x80; } -static void DecodeImage(void) +static INLINE uint16 RGB_to_RGB555(uint8 r, uint8 g, uint8 b) { - //puts("DECODE"); + r = (r + 4) >> 3; + g = (g + 4) >> 3; + b = (b + 4) >> 3; - if((Command >> 27) & 0x2) + if(r > 0x1F) + r = 0x1F; + + if(g > 0x1F) + g = 0x1F; + + if(b > 0x1F) + b = 0x1F; + + return((r << 0) | (g << 5) | (b << 10)); +} + +static void EncodeImage(const unsigned ybn) +{ + //printf("ENCODE, %d\n", (Command & 0x08000000) ? 256 : 384); + + PixelBufferCount32 = 0; + + switch((Command >> 27) & 0x3) + { + case 0: // 4bpp + { + const uint8 us_xor = (Command & (1U << 26)) ? 0x00 : 0x88; + uint8* pix_out = PixelBuffer.pix8; + + for(int y = 0; y < 8; y++) { - run_time -= 2048; + for(int x = 0; x < 8; x += 2) + { + uint8 p0 = std::min(127, block_y[y][x + 0] + 8); + uint8 p1 = std::min(127, block_y[y][x + 1] + 8); - IDCT(Coeff[0], &block_cr[0][0]); - IDCT(Coeff[1], &block_cb[0][0]); - IDCT(Coeff[2], &block_y[0][0][0][0]); - IDCT(Coeff[3], &block_y[0][1][0][0]); - IDCT(Coeff[4], &block_y[1][0][0][0]); - IDCT(Coeff[5], &block_y[1][1][0][0]); + *pix_out = ((p0 >> 4) | (p1 & 0xF0)) ^ us_xor; + pix_out++; + } + } + PixelBufferCount32 = 8; + } + break; + + + case 1: // 8bpp + { + const uint8 us_xor = (Command & (1U << 26)) ? 0x00 : 0x80; + uint8* pix_out = PixelBuffer.pix8; + + for(int y = 0; y < 8; y++) + { + for(int x = 0; x < 8; x++) + { + *pix_out = (uint8)block_y[y][x] ^ us_xor; + pix_out++; + } + } + PixelBufferCount32 = 16; + } + break; + + case 2: // 24bpp + { + const uint8 rgb_xor = (Command & (1U << 26)) ? 0x80 : 0x00; + uint8* pix_out = PixelBuffer.pix8; + + for(int y = 0; y < 8; y++) + { + const int8* by = &block_y[y][0]; + const int8* cb = &block_cb[(y >> 1) | ((ybn & 2) << 1)][(ybn & 1) << 2]; + const int8* cr = &block_cr[(y >> 1) | ((ybn & 2) << 1)][(ybn & 1) << 2]; + + for(int x = 0; x < 8; x++) + { + int r, g, b; + + YCbCr_to_RGB(by[x], cb[x >> 1], cr[x >> 1], r, g, b); + + pix_out[0] = r ^ rgb_xor; + pix_out[1] = g ^ rgb_xor; + pix_out[2] = b ^ rgb_xor; + pix_out += 3; + } + } + PixelBufferCount32 = 48; + } + break; + + case 3: // 16bpp + { + uint16 pixel_xor = ((Command & 0x02000000) ? 0x8000 : 0x0000) | ((Command & (1U << 26)) ? 0x4210 : 0x0000); + uint16* pix_out = PixelBuffer.pix16; + + for(int y = 0; y < 8; y++) + { + const int8* by = &block_y[y][0]; + const int8* cb = &block_cb[(y >> 1) | ((ybn & 2) << 1)][(ybn & 1) << 2]; + const int8* cr = &block_cr[(y >> 1) | ((ybn & 2) << 1)][(ybn & 1) << 2]; + + for(int x = 0; x < 8; x++) + { + int r, g, b; + + YCbCr_to_RGB(by[x], cb[x >> 1], cr[x >> 1], r, g, b); + + StoreU16_LE(pix_out, pixel_xor ^ RGB_to_RGB555(r, g, b)); + pix_out++; + } + } + PixelBufferCount32 = 32; + } + break; + + } +} + +static INLINE void WriteImageData(uint16 V, int32* eat_cycles) +{ + const uint32 qmw = (bool)(DecodeWB < 2); + + //printf("MDEC DMA SubWrite: %04x, %d\n", V, CoeffIndex); + + if(!CoeffIndex) + { + if(V == 0xFE00) + { + //printf("FE00 @ %u\n", DecodeWB); + return; + } + + QScale = V >> 10; + + { + int q = QMatrix[qmw][0]; // No QScale here! + int ci = sign_10_to_s16(V & 0x3FF); + int tmp; + + if(q != 0) + tmp = ((ci * q) << 4) + (ci ? ((ci < 0) ? 8 : -8) : 0); + else + tmp = (ci * 2) << 4; + + // Not sure if it should be 0x3FFF or 0x3FF0 or maybe 0x3FF8? + Coeff[ZigZag[0]] = std::min(0x3FFF, std::max(-0x4000, tmp)); + CoeffIndex++; + } + } + else + { + if(V == 0xFE00) + { + while(CoeffIndex < 64) + Coeff[ZigZag[CoeffIndex++]] = 0; } else { - run_time -= 341; - IDCT(Coeff[2], &block_y[0][0][0][0]); + uint32 rlcount = V >> 10; + + for(uint32 i = 0; i < rlcount && CoeffIndex < 64; i++) + { + Coeff[ZigZag[CoeffIndex]] = 0; + CoeffIndex++; + } + + if(CoeffIndex < 64) + { + int q = QScale * QMatrix[qmw][CoeffIndex]; + int ci = sign_10_to_s16(V & 0x3FF); + int tmp; + + if(q != 0) + tmp = (((ci * q) >> 3) << 4) + (ci ? ((ci < 0) ? 8 : -8) : 0); + else + tmp = (ci * 2) << 4; + + // Not sure if it should be 0x3FFF or 0x3FF0 or maybe 0x3FF8? + Coeff[ZigZag[CoeffIndex]] = std::min(0x3FFF, std::max(-0x4000, tmp)); + CoeffIndex++; + } } + } - block_ready = true; -} + if(CoeffIndex == 64) + { + CoeffIndex = 0; -static void EncodeImage(void) -{ - int x, y; - //printf("ENCODE, %d\n", (Command & 0x08000000) ? 256 : 384); + //printf("Block %d finished\n", DecodeWB); - block_ready = false; - - switch((Command >> 27) & 0x3) + switch(DecodeWB) { - case 0: // 4bpp, TODO - break; - case 1: // 8bpp - { - uint8_t us_xor = (Command & (1U << 26)) ? 0x00 : 0x80; + case 0: IDCT(Coeff, &block_cr[0][0]); break; + case 1: IDCT(Coeff, &block_cb[0][0]); break; + case 2: IDCT(Coeff, &block_y[0][0]); break; + case 3: IDCT(Coeff, &block_y[0][0]); break; + case 4: IDCT(Coeff, &block_y[0][0]); break; + case 5: IDCT(Coeff, &block_y[0][0]); break; + } - for(y = 0; y < 8; y++) - { - uint32_t qb = 0; + // + // Approximate, actual timing seems to be a bit complex. + // + *eat_cycles += 341; - for(x = 0; x < 8; x++) - { - int yv = block_y[0][0][y][x]; - - if(yv < -128) - yv = -128; - - if(yv > 127) - yv = 127; - - qb |= ((uint8)yv ^ us_xor) << ((x & 1) * 8); - if((x & 1) == 1) - { - if(OutBuffer.CanWrite()) - OutBuffer.WriteUnit(qb); - - qb = 0; - } - -#if 0 - qb |= yv << ((x & 3) * 8); - if((x & 3) == 3) - { - if(OutBuffer.CanWrite()) - { - printf("0x%08x\n", qb); - OutBuffer.WriteUnit(qb); - } - qb = 0; - } -#endif - } - } - } - break; - - case 2: // 24bpp - { - uint8_t output[16][16][3]; // [y][x][cc] - - for(y = 0; y < 16; y++) - { - for(x = 0; x < 16; x++) - { - uint8_t r, g, b; - - YCbCr_to_RGB(block_y[(y >> 3) & 1][(x >> 3) & 1][y & 7][x & 7], block_cb[y >> 1][x >> 1], block_cr[y >> 1][x >> 1], r, g, b); - - output[y][x][0] = r; - output[y][x][1] = g; - output[y][x][2] = b; - } - } - - for(int i = 0; i < 384; i++) - { - if(OutBuffer.CanWrite()) - OutBuffer.WriteUnit((&output[0][0][0])[i * 2 + 0] | ((&output[0][0][0])[i * 2 + 1] << 8)); - } - } - break; - - case 3: // 16bpp - { - uint16_t pixel_or = (Command & 0x02000000) ? 0x8000 : 0x0000; - - for(y = 0; y < 16; y++) - { - for(x = 0; x < 16; x++) - { - uint8_t r, g, b; - - YCbCr_to_RGB(block_y[(y >> 3) & 1][(x >> 3) & 1][y & 7][x & 7], block_cb[y >> 1][x >> 1], block_cr[y >> 1][x >> 1], r, g, b); - - if(OutBuffer.CanWrite()) - OutBuffer.WriteUnit(pixel_or | ((r >> 3) << 0) | ((g >> 3) << 5) | ((b >> 3) << 10)); - } - } - } - break; - - } -} - -void MDEC_DMAWrite(uint32_t V) -{ - int i; - if (InCounter <= 0) - return; - - InCounter--; - - switch((Command >> 29) & 0x7) + if(DecodeWB >= 2) { - case 1: - for(i = 0; i < 2; i++) - { - if(InputBuffer.CanWrite()) - InputBuffer.WriteUnit(V); - - V >>= 16; - } - break; - - case 2: - for(i = 0; i < 4; i++) - { - QMatrix[QMIndex >> 6][QMIndex & 0x3F] = (uint8)V; - QMIndex = (QMIndex + 1) & 0x7F; - V >>= 8; - } - break; - - case 3: - for(i = 0; i < 2; i++) - { - IDCTMatrix[((IDCTMIndex & 0x7) << 3) | ((IDCTMIndex >> 3) & 0x7)] = (int16)(V & 0xFFFF) >> 3; - IDCTMIndex = (IDCTMIndex + 1) & 0x3F; - - V >>= 16; - } - break; - - default: - PSX_DBG(PSX_DBG_WARNING, "MYSTERY1: %08x\n", V); - break; + EncodeImage((DecodeWB + 4) % 6); } -} -uint32_t MDEC_DMARead(void) -{ - uint32_t V = 0; - - if(((Command >> 29) & 0x7) == 0x1 && OutBuffer.CanRead() >= 2) + DecodeWB++; + if(DecodeWB == (((Command >> 27) & 2) ? 6 : 3)) { - V = OutBuffer.ReadUnit() | (OutBuffer.ReadUnit() << 16); + DecodeWB = ((Command >> 27) & 2) ? 0 : 2; } - else - { - PSX_DBG(PSX_DBG_WARNING, "[MDEC] BONUS GNOMES\n"); - V = rand(); - } - - return(V); + } } -// Test case related to this: GameShark Version 4.0 intro movie(coupled with (clever) abuse of DMA channel 0). -// also: SimCity 2000 startup. -bool MDEC_DMACanWrite(void) +#if 1 + +// +// +// +#define MDEC_WAIT_COND(n) { case __COUNTER__: if(!(n)) { MDRPhase = __COUNTER__ - MDRPhaseBias - 1; return; } } + +#define MDEC_WRITE_FIFO(n) { MDEC_WAIT_COND(OutFIFO.CanWrite()); OutFIFO.WriteUnit(n); } +#define MDEC_READ_FIFO(n) { MDEC_WAIT_COND(InFIFO.CanRead()); n = InFIFO.ReadUnit(); } +#define MDEC_EAT_CLOCKS(n) { ClockCounter -= (n); MDEC_WAIT_COND(ClockCounter > 0); } + +void MDEC_Run(int32 clocks) { - return (InCounter > 0 && ((Command >> 29) & 0x7) >= 1 && ((Command >> 29) & 0x7) <= 3); -} + static const unsigned MDRPhaseBias = __COUNTER__ + 1; -bool MDEC_DMACanRead(void) -{ - return (OutBuffer.CanRead() >= 2); -} + //MDFN_DispMessage("%u", OutFIFO.CanRead()); -void MDEC_Write(const pscpu_timestamp_t timestamp, uint32_t A, uint32_t V) -{ - //PSX_WARNING("[MDEC] Write: 0x%08x 0x%08x, %d", A, V, timestamp); - if(A & 4) - { - if(V & 0x80000000) // Reset? - { - Command = 0; - - block_ready = false; - run_time = 0; - QMIndex = 0; - IDCTMIndex = 0; - - QScale = 0; - - memset(Coeff, 0, sizeof(Coeff)); - CoeffIndex = 0; - DecodeWB = 0; - - InputBuffer.Flush(); - OutBuffer.Flush(); - - InCounter = 0; - BlockEnd = false; - } - } - else - { - Command = V; - - switch((Command >> 29) & 0x7) - { - case 1: - InputBuffer.Flush(); - OutBuffer.Flush(); - - block_ready = false; - BlockEnd = false; - CoeffIndex = 0; - - if((Command >> 27) & 2) - DecodeWB = 0; - else - DecodeWB = 2; - - InCounter = V & 0xFFFF; - break; - - case 2: - QMIndex = 0; - InCounter = 0x10 + ((Command & 0x1) ? 0x10 : 0x00); - break; - - case 3: - IDCTMIndex = 0; - InCounter = 0x20; - break; - - default: - InCounter = V & 0xFFFF; - break; - } - } -} - -uint32_t MDEC_Read(const pscpu_timestamp_t timestamp, uint32_t A) -{ - uint32_t ret = 0; - - if(A & 4) - { - ret = 0; - - if(InputBuffer.CanRead()) - ret |= 0x20000000; - - ret |= ((Command >> 25) & 0xF) << 23; - } - else - ret = Command; - - //PSX_WARNING("[MDEC] Read: 0x%08x 0x%08x -- %d %d", A, ret, InputBuffer.CanRead(), InCounter); - - return(ret); -} - -void MDEC_Run(int32_t clocks) -{ - run_time += clocks; - - while(run_time > 0) - { - run_time--; - - if(block_ready && !OutBuffer.CanRead()) - EncodeImage(); - - if(block_ready && OutBuffer.CanRead()) - break; - - if(!InputBuffer.CanRead()) - break; - - WriteImageData(InputBuffer.ReadUnit()); - } - - if(run_time > 0) - run_time = 0; -} - -#if 0 -// Maybe we should just use libco.... -#define MDEC_WRITE_FIFO(n) case __COUNTER__: if(!InFIFO.CanRead()) { MDRPhase = __COUNTER__ - 1; return; } OutFIFO.Write(n); -#define MDEC_READ_FIFO(n) case __COUNTER__: if(!InFIFO.CanRead()) { MDRPhase = __COUNTER__ - 1; return; } n = InFIFO.Read(); -#define MDEC_EAT_CLOCKS(n) ClockCounter -= clocks; case __COUNTER__: if(ClockCounter <= 0) { MDRPhase = __COUNTER__ - 1; return; } -void MDEC_Run2(int32_t clocks) -{ ClockCounter += clocks; if(ClockCounter > 128) - ClockCounter = 128; - - switch(MDRPhase) { - MDEC_READ_FIFO(Command); + //if(MDRPhase != 0) + // printf("SNORT: %d\n", ClockCounter); + ClockCounter = 128; + } - // - // - // - if(((Command >> 29) & 0x7) == 1) + switch(MDRPhase + MDRPhaseBias) + { + for(;;) { - InCounter = Command & 0xFFFF; - OutBuffer.Flush(); + InCommand = false; + MDEC_READ_FIFO(Command); // This must be the first MDEC_* macro used! + InCommand = true; + MDEC_EAT_CLOCKS(1); - block_ready = false; - BlockEnd = false; - CoeffIndex = 0; + //printf("****************** Command: %08x, %02x\n", Command, Command >> 29); - if((Command >> 27) & 2) - DecodeWB = 0; - else - DecodeWB = 2; - - InCounter--; - while(InCounter != 0xFFFF) + // + // + // + if(((Command >> 29) & 0x7) == 1) { - uint32_t tfr; - bool need_encode; + InCounter = Command & 0xFFFF; + OutFIFO.Flush(); + //OutBuffer.Flush(); - MDEC_READ_FIFO(tfr); + PixelBufferCount32 = 0; + CoeffIndex = 0; - need_encode = WriteImageData(tfr); - need_encode |= WriteImageData(tfr >> 16); + if((Command >> 27) & 2) + DecodeWB = 0; + else + DecodeWB = 2; - if(need_encode) - { + switch((Command >> 27) & 0x3) + { + case 0: + case 1: RAMOffsetWWS = 0; break; + case 2: RAMOffsetWWS = 6; break; + case 3: RAMOffsetWWS = 4; break; + } + RAMOffsetY = 0; + RAMOffsetCounter = RAMOffsetWWS; + InCounter--; + do + { + uint32 tfr; + int32 need_eat; // = 0; - } + MDEC_READ_FIFO(tfr); + InCounter--; + +// printf("KA: %04x %08x\n", InCounter, tfr); + + need_eat = 0; + PixelBufferCount32 = 0; + WriteImageData(tfr, &need_eat); + WriteImageData(tfr >> 16, &need_eat); + + MDEC_EAT_CLOCKS(need_eat); + + PixelBufferReadOffset = 0; + while(PixelBufferReadOffset != PixelBufferCount32) + { + MDEC_WRITE_FIFO(LoadU32_LE(&PixelBuffer.pix32[PixelBufferReadOffset++])); + } + } while(InCounter != 0xFFFF); } - } - // - // - // - else if(((Command >> 29) & 0x7) == 2) - { - QMIndex = 0; - InCounter = 0x10 + ((Command & 0x1) ? 0x10 : 0x00); - - InCounter--; - while(InCounter != 0xFFFF) + // + // + // + else if(((Command >> 29) & 0x7) == 2) { - uint32_t tfr; + QMIndex = 0; + InCounter = 0x10 + ((Command & 0x1) ? 0x10 : 0x00); + + InCounter--; + do + { + uint32 tfr; MDEC_READ_FIFO(tfr); + InCounter--; + + //printf("KA: %04x %08x\n", InCounter, tfr); for(int i = 0; i < 4; i++) { @@ -697,40 +644,176 @@ void MDEC_Run2(int32_t clocks) QMIndex = (QMIndex + 1) & 0x7F; tfr >>= 8; } + } while(InCounter != 0xFFFF); } - } - // - // - // - else if(((Command >> 29) & 0x7) == 3) - { - IDCTMIndex = 0; - InCounter = 0x20; - - InCounter--; - while(InCounter != 0xFFFF) + // + // + // + else if(((Command >> 29) & 0x7) == 3) { - uint32_t tfr; + IDCTMIndex = 0; + InCounter = 0x20; - MDEC_READ_FIFO(tfr); - - for(unsigned i = 0; i < 2; i++) + InCounter--; + do { - int tmp = (int16)(tfr & 0xFFFF) >> 3; + uint32 tfr; - IDCTMatrix[((IDCTMIndex & 0x7) << 3) | ((IDCTMIndex >> 3) & 0x7)] = (int16)(V & 0xFFFF) >> 3; - IDCTMIndex = (IDCTMIndex + 1) & 0x3F; + MDEC_READ_FIFO(tfr); + InCounter--; - tfr >>= 16; - } + for(unsigned i = 0; i < 2; i++) + { + IDCTMatrix[((IDCTMIndex & 0x7) << 3) | ((IDCTMIndex >> 3) & 0x7)] = (int16)(tfr & 0xFFFF) >> 3; + IDCTMIndex = (IDCTMIndex + 1) & 0x3F; + + tfr >>= 16; + } + } while(InCounter != 0xFFFF); } + else + { + InCounter = Command & 0xFFFF; + } + } // end for(;;) + } +} +#endif + +void MDEC_DMAWrite(uint32 V) +{ + if(InFIFO.CanWrite()) + { + InFIFO.WriteUnit(V); + MDEC_Run(0); + } + else + { + PSX_DBG(PSX_DBG_WARNING, "Input FIFO DMA write overflow?!\n"); + } +} + +uint32 MDEC_DMARead(int32* offs) +{ + uint32 V = 0; + + *offs = 0; + + if(MDFN_LIKELY(OutFIFO.CanRead())) + { + V = OutFIFO.ReadUnit(); + + *offs = (RAMOffsetY & 0x7) * RAMOffsetWWS; + + if(RAMOffsetY & 0x08) + { + *offs = (*offs - RAMOffsetWWS*7); + } + + RAMOffsetCounter--; + if(!RAMOffsetCounter) + { + RAMOffsetCounter = RAMOffsetWWS; + RAMOffsetY++; + } + } + else + { + PSX_DBG(PSX_DBG_WARNING, "[MDEC] BONUS GNOMES\n"); + V = rand(); + } + + return(V); +} + +bool MDEC_DMACanWrite(void) +{ + return((InFIFO.CanWrite() >= 0x20) && (Control & (1U << 30)) && InCommand && InCounter != 0xFFFF); +} + +bool MDEC_DMACanRead(void) +{ + return((OutFIFO.CanRead() >= 0x20) && (Control & (1U << 29))); +} + +void MDEC_Write(const pscpu_timestamp_t timestamp, uint32 A, uint32 V) +{ + //PSX_WARNING("[MDEC] Write: 0x%08x 0x%08x, %d --- %u %u", A, V, timestamp, InFIFO.CanRead(), OutFIFO.CanRead()); + if(A & 4) + { + if(V & 0x80000000) // Reset? + { + MDRPhase = 0; + InCounter = 0; + Command = 0; + InCommand = false; + + PixelBufferCount32 = 0; + ClockCounter = 0; + QMIndex = 0; + IDCTMIndex = 0; + + QScale = 0; + + memset(Coeff, 0, sizeof(Coeff)); + CoeffIndex = 0; + DecodeWB = 0; + + InFIFO.Flush(); + OutFIFO.Flush(); + } + Control = V & 0x7FFFFFFF; + } + else + { + if(InFIFO.CanWrite()) + { + InFIFO.WriteUnit(V); + + if(!InCommand) + { + if(ClockCounter < 1) + ClockCounter = 1; + } + MDEC_Run(0); } else { - InCounter = Command & 0xFFFF; + PSX_DBG(PSX_DBG_WARNING, "MDEC manual write FIFO overflow?!\n"); } } } -#endif +uint32 MDEC_Read(const pscpu_timestamp_t timestamp, uint32 A) +{ + uint32 ret = 0; + + if(A & 4) + { + ret = 0; + + ret |= (OutFIFO.CanRead() == 0) << 31; + ret |= (InFIFO.CanWrite() == 0) << 30; + ret |= InCommand << 29; + + ret |= MDEC_DMACanWrite() << 28; + ret |= MDEC_DMACanRead() << 27; + + ret |= ((Command >> 25) & 0xF) << 23; + + // Needs refactoring elsewhere to work right: ret |= ((DecodeWB + 4) % 6) << 16; + + ret |= InCounter & 0xFFFF; + } + else + { + if(OutFIFO.CanRead()) + ret = OutFIFO.ReadUnit(); + } + + //PSX_WARNING("[MDEC] Read: 0x%08x 0x%08x -- %d %d", A, ret, InputBuffer.CanRead(), InCounter); + + return(ret); +} + } diff --git a/mednafen/psx/mdec.h b/mednafen/psx/mdec.h index 6a036a83..7a76c78c 100644 --- a/mednafen/psx/mdec.h +++ b/mednafen/psx/mdec.h @@ -6,7 +6,7 @@ namespace MDFN_IEN_PSX void MDEC_DMAWrite(uint32_t V); -uint32_t MDEC_DMARead(void); +uint32_t MDEC_DMARead(int32_t *offs); void MDEC_Write(const pscpu_timestamp_t timestamp, uint32_t A, uint32_t V); uint32_t MDEC_Read(const pscpu_timestamp_t timestamp, uint32_t A); diff --git a/mednafen/psx/sio.cpp b/mednafen/psx/sio.cpp index e561fe90..b2d2ad8f 100644 --- a/mednafen/psx/sio.cpp +++ b/mednafen/psx/sio.cpp @@ -103,4 +103,26 @@ void SIO_Write(pscpu_timestamp_t timestamp, uint32_t A, uint32_t V) } } +int SIO_StateAction(StateMem *sm, int load, int data_only) +{ + SFORMAT StateRegs[] = + { + SFVAR(Status), + SFVAR(Mode), + SFVAR(Control), + SFVAR(BaudRate), + SFVAR(DataBuffer), + + SFEND + }; + int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "SIO"); + + if(load) + { + + } + + return(ret); +} + } diff --git a/mednafen/psx/sio.h b/mednafen/psx/sio.h index 97fdda3b..d74973e9 100644 --- a/mednafen/psx/sio.h +++ b/mednafen/psx/sio.h @@ -8,6 +8,8 @@ void SIO_Write(pscpu_timestamp_t timestamp, uint32_t A, uint32_t V); uint32_t SIO_Read(pscpu_timestamp_t timestamp, uint32_t A); void SIO_Power(void); +int SIO_StateAction(StateMem *sm, int load, int data_only); + } #endif diff --git a/mednafen/psx/spu.cpp b/mednafen/psx/spu.cpp index 515db52c..915c08a6 100644 --- a/mednafen/psx/spu.cpp +++ b/mednafen/psx/spu.cpp @@ -74,28 +74,28 @@ #include "cdc.h" #include "spu.h" -#include "../clamp.h" - uint32_t IntermediateBufferPos; int16_t IntermediateBuffer[4096][2]; namespace MDFN_IEN_PSX { -static const int16_t FIR_Table[256][4] = +static const int16 FIR_Table[256][4] = { #include "spu_fir_table.inc" }; -static const uint32_t NoiseFreqTable[64] = +static const uint32 NoiseFreqTable[64] = { #include "spu_nft.inc" }; PS_SPU::PS_SPU() { - IntermediateBufferPos = 0; - memset(IntermediateBuffer, 0, sizeof(IntermediateBuffer)); + IntermediateBufferPos = 0; + memset(IntermediateBuffer, 0, sizeof(IntermediateBuffer)); + + for(int16 a = 32760; a >= 0; a++); } PS_SPU::~PS_SPU() @@ -104,1345 +104,1411 @@ PS_SPU::~PS_SPU() void PS_SPU::Power(void) { - int i; - //lastts = 0; - clock_divider = 768; + clock_divider = 768; - memset(SPURAM, 0, sizeof(SPURAM)); + memset(SPURAM, 0, sizeof(SPURAM)); - for(i = 0; i < 24; i++) - { - memset(Voices[i].DecodeBuffer, 0, sizeof(Voices[i].DecodeBuffer)); + for(int i = 0; i < 24; i++) + { + memset(Voices[i].DecodeBuffer, 0, sizeof(Voices[i].DecodeBuffer)); + Voices[i].DecodeM2 = 0; + Voices[i].DecodeM1 = 0; - Voices[i].DecodeWritePos = 0; + Voices[i].DecodePlayDelay = 0; + Voices[i].DecodeWritePos = 0; + Voices[i].DecodeReadPos = 0; + Voices[i].DecodeAvail = 0; - Voices[i].DecodeFlags = 0; + Voices[i].DecodeShift = 0; + Voices[i].DecodeWeight = 0; + Voices[i].DecodeFlags = 0; - Voices[i].Sweep[0].Power(); - Voices[i].Sweep[1].Power(); + Voices[i].IgnoreSampLA = false; - Voices[i].Pitch = 0; - Voices[i].CurPhase = 0; - Voices[i].CurPhase_SD = 0; + Voices[i].Sweep[0].Power(); + Voices[i].Sweep[1].Power(); - Voices[i].StartAddr = 0; + Voices[i].Pitch = 0; + Voices[i].CurPhase = 0; - Voices[i].CurAddr = 0; + Voices[i].StartAddr = 0; - Voices[i].ADSRControl = 0; + Voices[i].CurAddr = 0; - Voices[i].LoopAddr = 0; + Voices[i].ADSRControl = 0; - Voices[i].PreLRSample = 0; + Voices[i].LoopAddr = 0; - memset(&Voices[i].ADSR, 0, sizeof(SPU_ADSR)); - } + Voices[i].PreLRSample = 0; - GlobalSweep[0].Power(); - GlobalSweep[1].Power(); + memset(&Voices[i].ADSR, 0, sizeof(SPU_ADSR)); + } - NoiseCounter = 0; - LFSR = 0; + GlobalSweep[0].Power(); + GlobalSweep[1].Power(); - FM_Mode = 0; - Noise_Mode = 0; - Reverb_Mode = 0; - ReverbWA = 0; + NoiseCounter = 0; + LFSR = 0; - ReverbVol[0] = ReverbVol[1] = 0; + FM_Mode = 0; + Noise_Mode = 0; + Reverb_Mode = 0; + ReverbWA = 0; - CDVol[0] = CDVol[1] = 0; + ReverbVol[0] = ReverbVol[1] = 0; - ExternVol[0] = ExternVol[1] = 0; + CDVol[0] = CDVol[1] = 0; - IRQAddr = 0; + ExternVol[0] = ExternVol[1] = 0; - RWAddr = 0; + IRQAddr = 0; - SPUControl = 0; + RWAddr = 0; - VoiceOn = 0; - VoiceOff = 0; + SPUControl = 0; - BlockEnd = 0; + VoiceOn = 0; + VoiceOff = 0; - CWA = 0; + BlockEnd = 0; - memset(Regs, 0, sizeof(Regs)); + CWA = 0; - memset(RDSB, 0, sizeof(RDSB)); - RDSB_WP = 0; + memset(Regs, 0, sizeof(Regs)); - memset(RUSB, 0, sizeof(RUSB)); - RUSB_WP = 0; + memset(RDSB, 0, sizeof(RDSB)); + RDSB_WP = 0; - ReverbCur = ReverbWA; + memset(RUSB, 0, sizeof(RUSB)); + RUSB_WP = 0; - IRQAsserted = false; + ReverbCur = ReverbWA; + + IRQAsserted = false; } -static INLINE void CalcVCDelta(const uint8_t zs, uint8_t speed, bool log_mode, bool dec_mode, int16_t Current, int &increment, int &divinco) +static INLINE void CalcVCDelta(const uint8 zs, uint8 speed, bool log_mode, bool dec_mode, bool inv_increment, int16 Current, int &increment, int &divinco) { - increment = (7 - (speed & 0x3)); + increment = (7 - (speed & 0x3)); - if(dec_mode) // Decrement mode - increment = ~increment; + if(inv_increment) + increment = ~increment; - divinco = 32768; + divinco = 32768; - if(speed < 0x2C) - increment <<= (0x2F - speed) >> 2; + if(speed < 0x2C) + increment <<= (0x2F - speed) >> 2; - if(speed >= 0x30) - divinco >>= (speed - 0x2C) >> 2; + if(speed >= 0x30) + divinco >>= (speed - 0x2C) >> 2; - if(log_mode) + if(log_mode) + { + if(dec_mode) // Log decrement mode + increment = (Current * increment) >> 15; + else // Log increment mode { - if(dec_mode) // Log decrement mode - increment = (Current * increment) >> 15; - else // Log increment mode - { - if(Current >= 0x6000) - { - if(speed < 0x28) - increment >>= 2; - else if(speed >= 0x2C) - divinco >>= 2; - else // 0x28 ... 0x2B - { - increment >>= 1; - divinco >>= 1; - } - } - } - } // end if(log_mode) + if((Current & 0x7FFF) >= 0x6000) + { + if(speed < 0x28) + increment >>= 2; + else if(speed >= 0x2C) + divinco >>= 2; + else // 0x28 ... 0x2B + { + increment >>= 1; + divinco >>= 1; + } + } + } + } // end if(log_mode) - if(divinco == 0 && speed < zs) //0x7F) - divinco = 1; + if(divinco == 0 && speed < zs) //0x7F) + divinco = 1; } INLINE void SPU_Sweep::Power(void) { - Control = 0; - Current = 0; - Divider = 0; + Control = 0; + Current = 0; + Divider = 0; } -INLINE void SPU_Sweep::WriteControl(uint16_t value) +INLINE void SPU_Sweep::WriteControl(uint16 value) { - Control = value; - Divider = 0; // Not sure about this. + Control = value; } -INLINE int16_t SPU_Sweep::ReadVolume(void) +INLINE int16 SPU_Sweep::ReadVolume(void) { - return(Current); + return((int16)Current); } void SPU_Sweep::Clock(void) { - const uint16_t end_value = (Control & 0x2000) ? 0x0000 : 0x7FFF; + if(!(Control & 0x8000)) + { + Current = (Control & 0x7FFF) << 1; + return; + } - if(!(Control & 0x8000)) + if(Control & 0x8000) // Sweep enabled + { + const bool log_mode = (bool)(Control & 0x4000); + const bool dec_mode = (bool)(Control & 0x2000); + const bool inv_mode = (bool)(Control & 0x1000); + const bool inv_increment = (dec_mode ^ inv_mode) | (dec_mode & log_mode); + const uint16 vc_cv_xor = (inv_mode & !(dec_mode & log_mode)) ? 0xFFFF : 0x0000; + const uint16 TestInvert = inv_mode ? 0xFFFF : 0x0000; + int increment; + int divinco; + + CalcVCDelta(0x7F, Control & 0x7F, log_mode, dec_mode, inv_increment, (int16)(Current ^ vc_cv_xor), increment, divinco); + //printf("%d %d\n", divinco, increment); + + if((dec_mode & !(inv_mode & log_mode)) && ((Current & 0x8000) == (inv_mode ? 0x0000 : 0x8000) || (Current == 0))) + { + // + // Not sure if this condition should stop the Divider adding or force the increment value to 0. + // + Current = 0; + } + else + { + Divider += divinco; + + if(Divider & 0x8000) { - Current = (Control & 0x7FFF) << 1; - return; + Divider = 0; + + if(dec_mode || ((Current ^ TestInvert) != 0x7FFF)) + { + uint16 PrevCurrent = Current; + Current = Current + increment; + + //printf("%04x %04x\n", PrevCurrent, Current); + + if(!dec_mode && ((Current ^ PrevCurrent) & 0x8000) && ((Current ^ TestInvert) & 0x8000)) + Current = 0x7FFF ^ TestInvert; + } } - - // Invert bits - if(Control & 0x1000) - Current = ~Current; - - if((Control & 0x8000) && Current != end_value) // Sweep enabled - { - int increment, divinco; - - CalcVCDelta(0x7F, Control & 0x7F, Control & 0x4000, Control & 0x2000, Current, increment, divinco); - - Divider += divinco; - if(Divider & 0x8000) - { - Divider = 0; - Current += increment; - } - - if(Current & 0x8000) // Overflow or underflow - Current = end_value; - } - - // Invert bits back - if(Control & 0x1000) - Current = ~Current; + } + } } -INLINE void SPU_Sweep::WriteVolume(int16_t value) +INLINE void SPU_Sweep::WriteVolume(int16 value) { - Current = value; + Current = value; } -void PS_SPU::DecodeSamples(SPU_Voice *voice) + +// +// Take care not to trigger SPU IRQ for the next block before its decoding start. +// +void PS_SPU::RunDecoder(SPU_Voice *voice) { - int i; - uint16_t settings, coded; - uint32_t flags, shift, weight; + // 5 through 0xF appear to be 0 on the real thing. + static const int32 Weights[16][2] = + { + // s-1 s-2 + { 0, 0 }, + { 60, 0 }, + { 115, -52 }, + { 98, -55 }, + { 122, -60 }, + }; - // Handle delayed flags from the previously-decoded block. - if(voice->DecodeFlags & 0x1) + if(voice->DecodeAvail >= 11) + { + if(SPUControl & 0x40) + { + unsigned test_addr = (voice->CurAddr - 1) & 0x3FFFF; + if(IRQAddr == test_addr || IRQAddr == (test_addr & 0x3FFF8)) { - BlockEnd |= 1 << (voice - Voices); - - if(!(voice->DecodeFlags & 0x2)) // Force enveloping to 0 if not "looping". TODO: Should we reset the ADSR divider counter too? - { - if(!(Noise_Mode & (1 << (voice - Voices)))) - { - voice->ADSR.Phase = ADSR_RELEASE; - voice->ADSR.EnvLevel = 0; - } - } + //SPUIRQ_DBG("SPU IRQ (VDA): 0x%06x", addr); + IRQAsserted = true; + IRQ_Assert(IRQ_SPU, IRQAsserted); } + } + return; + } - // Note: Only voice->CurAddr &= 0x3FFFF at the end so IRQ address testing will work. - // 5 through 0xF appear to be 0 on the real thing. - static const int32_t Weights[16][2] = + if((voice->CurAddr & 0x7) == 0) + { + // Handle delayed flags from the previously-decoded block. + if(voice->DecodeFlags & 0x1) + { + voice->CurAddr = voice->LoopAddr & ~0x7; + + BlockEnd |= 1 << (voice - Voices); + + if(!(voice->DecodeFlags & 0x2)) // Force enveloping to 0 if not "looping". TODO: Should we reset the ADSR divider counter too? { - // s-1 s-2 - { 0, 0 }, - { 60, 0 }, - { 115, -52 }, - { 98, -55 }, - { 122, -60 }, - }; - const uint32_t PrevCurAddr = voice->CurAddr; - - coded = 0; - settings = SPURAM[(voice->CurAddr++) & 0x3FFFF]; - shift = settings & 0xF; - weight = (settings >> 4) & 0xF; - flags = (settings >> 8) & 0xFF; - - for(i = 0; i < 28; i++) - { - int32_t sample; - - if(!(i & 3)) - coded = SPURAM[(voice->CurAddr++) & 0x3FFFF]; - - sample = (int16)((coded & 0xF) << 12); - sample >>= shift; - - sample += ((voice->DecodeBuffer[(voice->DecodeWritePos - 1) & 0x1F] * Weights[weight][0]) >> 6) - + ((voice->DecodeBuffer[(voice->DecodeWritePos - 2) & 0x1F] * Weights[weight][1]) >> 6); - clamp(&sample, -32768, 32767); - - voice->DecodeBuffer[voice->DecodeWritePos] = sample; - voice->DecodeWritePos = (voice->DecodeWritePos + 1) & 0x1F; - - coded >>= 4; + if(!(Noise_Mode & (1 << (voice - Voices)))) + { + voice->ADSR.Phase = ADSR_RELEASE; + voice->ADSR.EnvLevel = 0; + } } + } + } - // SPU IRQ - if(SPUControl & 0x40) + //for(int z = 0; z < 4; z++) + { + const uint16 CV = SPURAM[voice->CurAddr]; + + if(SPUControl & 0x40) + { + unsigned test_addr = voice->CurAddr & 0x3FFFF; + if(IRQAddr == test_addr || IRQAddr == (test_addr & 0x3FFF8)) { - uint32_t TestIRQAddr = IRQAddr; - - if(TestIRQAddr < PrevCurAddr) - TestIRQAddr |= 0x40000; - - if(TestIRQAddr >= PrevCurAddr && TestIRQAddr < voice->CurAddr) - { - //SPUIRQ_DBG("SPU IRQ(sample decoding) on voice %d", (int)(voice - Voices)); - IRQAsserted = true; - IRQ_Assert(IRQ_SPU, IRQAsserted); - } + //SPUIRQ_DBG("SPU IRQ: 0x%06x", addr); + IRQAsserted = true; + IRQ_Assert(IRQ_SPU, IRQAsserted); } + } - voice->CurAddr &= 0x3FFFF; + if((voice->CurAddr & 0x7) == 0) + { + voice->DecodeShift = CV & 0xF; + voice->DecodeWeight = (CV >> 4) & 0xF; + voice->DecodeFlags = (CV >> 8) & 0xFF; - if(flags & 0x4) + if(voice->DecodeFlags & 0x4) { - //if((int)(voice - Voices) == 22) - //{ - // SPUIRQ_DBG("[SPU] Flag loop set for voice %d, 0x%08x", (int)(voice - Voices), PrevCurAddr); - //} - voice->LoopAddr = PrevCurAddr; + if(!voice->IgnoreSampLA) + { + voice->LoopAddr = voice->CurAddr; + } + else + { + if(voice->LoopAddr != voice->CurAddr) + { + PSX_DBG(PSX_DBG_FLOOD, "[SPU] Ignore: LoopAddr=0x%08x, SampLA=0x%08x\n", voice->LoopAddr, voice->CurAddr); + } + } } + } + else + { + const unsigned shift = voice->DecodeShift; + const int32 weight_m1 = Weights[voice->DecodeWeight][0]; + const int32 weight_m2 = Weights[voice->DecodeWeight][1]; + uint32 coded = (uint32)CV << 12; + int16 *tb = &voice->DecodeBuffer[voice->DecodeWritePos]; - voice->DecodeFlags = flags; + for(int i = 0; i < 4; i++) + { + int32 sample = (int16)(coded & 0xF000) >> shift; - //printf("DEC END: %08x\n", voice->CurAddr); + sample += ((voice->DecodeM2 * weight_m2) >> 6); + sample += ((voice->DecodeM1 * weight_m1) >> 6); - voice->DecodeBuffer[0x20] = voice->DecodeBuffer[0x00]; - voice->DecodeBuffer[0x21] = voice->DecodeBuffer[0x01]; - voice->DecodeBuffer[0x22] = voice->DecodeBuffer[0x02]; - voice->DecodeBuffer[0x23] = voice->DecodeBuffer[0x03]; + clamp(&sample, -32768, 32767); + + tb[i] = sample; + voice->DecodeM2 = voice->DecodeM1; + voice->DecodeM1 = sample; + coded >>= 4; + } + voice->DecodeWritePos = (voice->DecodeWritePos + 4) & 0x1F; + voice->DecodeAvail += 4; + } + voice->CurAddr = (voice->CurAddr + 1) & 0x3FFFF; + } } void PS_SPU::CacheEnvelope(SPU_Voice *voice) { - int32_t Sl, Dr, Ar, Rr, Sr; - uint32_t raw = voice->ADSRControl; - SPU_ADSR *ADSR = (SPU_ADSR*)&voice->ADSR; + uint32 raw = voice->ADSRControl; + SPU_ADSR *ADSR = &voice->ADSR; + int32 Sl, Dr, Ar, Rr, Sr; - Sl = (raw >> 0) & 0x0F; - Dr = (raw >> 4) & 0x0F; - Ar = (raw >> 8) & 0x7F; + Sl = (raw >> 0) & 0x0F; + Dr = (raw >> 4) & 0x0F; + Ar = (raw >> 8) & 0x7F; - Rr = (raw >> 16) & 0x1F; - Sr = (raw >> 22) & 0x7F; + Rr = (raw >> 16) & 0x1F; + Sr = (raw >> 22) & 0x7F; - ADSR->AttackExp = (bool)(raw & (1 << 15)); - ADSR->ReleaseExp = (bool)(raw & (1 << 21)); - ADSR->SustainExp = (bool)(raw & (1 << 31)); - ADSR->SustainDec = (bool)(raw & (1 << 30)); + ADSR->AttackExp = (bool)(raw & (1 << 15)); + ADSR->ReleaseExp = (bool)(raw & (1 << 21)); + ADSR->SustainExp = (bool)(raw & (1 << 31)); + ADSR->SustainDec = (bool)(raw & (1 << 30)); - ADSR->AttackRate = Ar; - ADSR->DecayRate = Dr << 2; - ADSR->SustainRate = Sr; - ADSR->ReleaseRate = Rr << 2; + ADSR->AttackRate = Ar; + ADSR->DecayRate = Dr << 2; + ADSR->SustainRate = Sr; + ADSR->ReleaseRate = Rr << 2; - ADSR->SustainLevel = (Sl + 1) << 11; + ADSR->SustainLevel = (Sl + 1) << 11; } void PS_SPU::ResetEnvelope(SPU_Voice *voice) { - SPU_ADSR *ADSR = (SPU_ADSR*)&voice->ADSR; + SPU_ADSR *ADSR = &voice->ADSR; - ADSR->EnvLevel = 0; - ADSR->Divider = 0; - ADSR->Phase = ADSR_ATTACK; + ADSR->EnvLevel = 0; + ADSR->Divider = 0; + ADSR->Phase = ADSR_ATTACK; } void PS_SPU::ReleaseEnvelope(SPU_Voice *voice) { - SPU_ADSR *ADSR = (SPU_ADSR*)&voice->ADSR; + SPU_ADSR *ADSR = &voice->ADSR; - ADSR->Divider = 0; - ADSR->Phase = ADSR_RELEASE; + ADSR->Divider = 0; + ADSR->Phase = ADSR_RELEASE; } void PS_SPU::RunEnvelope(SPU_Voice *voice) { - int increment, divinco; - int16_t uoflow_reset; - SPU_ADSR *ADSR = (SPU_ADSR*)&voice->ADSR; + SPU_ADSR *ADSR = &voice->ADSR; + int increment; + int divinco; + int16 uoflow_reset; - if(ADSR->Phase == ADSR_ATTACK && ADSR->EnvLevel == 0x7FFF) - ADSR->Phase++; + if(ADSR->Phase == ADSR_ATTACK && ADSR->EnvLevel == 0x7FFF) + ADSR->Phase++; - //static INLINE void CalcVCDelta(const uint8_t zs, uint8_t speed, bool log_mode, bool decrement, int16_t Current, int &increment, int &divinco) - switch(ADSR->Phase) - { - default: assert(0); - break; + //static INLINE void CalcVCDelta(const uint8 zs, uint8 speed, bool log_mode, bool decrement, bool inv_increment, int16 Current, int &increment, int &divinco) + switch(ADSR->Phase) + { + default: assert(0); + break; - case ADSR_ATTACK: - CalcVCDelta(0x7F, ADSR->AttackRate, ADSR->AttackExp, false, (int16)ADSR->EnvLevel, increment, divinco); - uoflow_reset = 0x7FFF; - break; + case ADSR_ATTACK: + CalcVCDelta(0x7F, ADSR->AttackRate, ADSR->AttackExp, false, false, (int16)ADSR->EnvLevel, increment, divinco); + uoflow_reset = 0x7FFF; + break; - case ADSR_DECAY: - CalcVCDelta(0x1F << 2, ADSR->DecayRate, true, true, (int16)ADSR->EnvLevel, increment, divinco); - uoflow_reset = 0; - break; + case ADSR_DECAY: + CalcVCDelta(0x1F << 2, ADSR->DecayRate, true, true, true, (int16)ADSR->EnvLevel, increment, divinco); + uoflow_reset = 0; + break; - case ADSR_SUSTAIN: - CalcVCDelta(0x7F, ADSR->SustainRate, ADSR->SustainExp, ADSR->SustainDec, (int16)ADSR->EnvLevel, increment, divinco); - uoflow_reset = ADSR->SustainDec ? 0 : 0x7FFF; - break; + case ADSR_SUSTAIN: + CalcVCDelta(0x7F, ADSR->SustainRate, ADSR->SustainExp, ADSR->SustainDec, ADSR->SustainDec, (int16)ADSR->EnvLevel, increment, divinco); + uoflow_reset = ADSR->SustainDec ? 0 : 0x7FFF; + break; - case ADSR_RELEASE: - CalcVCDelta(0x1F << 2, ADSR->ReleaseRate, ADSR->ReleaseExp, true, (int16)ADSR->EnvLevel, increment, divinco); - uoflow_reset = 0; - break; - } + case ADSR_RELEASE: + CalcVCDelta(0x1F << 2, ADSR->ReleaseRate, ADSR->ReleaseExp, true, true, (int16)ADSR->EnvLevel, increment, divinco); + uoflow_reset = 0; + break; + } - ADSR->Divider += divinco; - if(ADSR->Divider & 0x8000) - { - const uint16_t prev_level = ADSR->EnvLevel; + ADSR->Divider += divinco; + if(ADSR->Divider & 0x8000) + { + const uint16 prev_level = ADSR->EnvLevel; - ADSR->Divider = 0; - ADSR->EnvLevel += increment; + ADSR->Divider = 0; + ADSR->EnvLevel += increment; - if(ADSR->Phase == ADSR_ATTACK) - { - // If previous the upper bit was 0, but now it's 1, handle overflow. - if(((prev_level ^ ADSR->EnvLevel) & ADSR->EnvLevel) & 0x8000) - ADSR->EnvLevel = uoflow_reset; - } - else - { - if(ADSR->EnvLevel & 0x8000) - ADSR->EnvLevel = uoflow_reset; - } - if(ADSR->Phase == ADSR_DECAY && (uint16)ADSR->EnvLevel < ADSR->SustainLevel) - ADSR->Phase++; - } + if(ADSR->Phase == ADSR_ATTACK) + { + // If previous the upper bit was 0, but now it's 1, handle overflow. + if(((prev_level ^ ADSR->EnvLevel) & ADSR->EnvLevel) & 0x8000) + ADSR->EnvLevel = uoflow_reset; + } + else + { + if(ADSR->EnvLevel & 0x8000) + ADSR->EnvLevel = uoflow_reset; + } + if(ADSR->Phase == ADSR_DECAY && (uint16)ADSR->EnvLevel < ADSR->SustainLevel) + ADSR->Phase++; + } } -INLINE void PS_SPU::CheckIRQAddr(uint32_t addr) +INLINE void PS_SPU::CheckIRQAddr(uint32 addr) { - if(SPUControl & 0x40) - { - if(IRQAddr == addr) - { - //SPUIRQ_DBG("SPU IRQ (ALT): 0x%06x", addr); - IRQAsserted = true; - IRQ_Assert(IRQ_SPU, IRQAsserted); - } - } + if(SPUControl & 0x40) + { + if(IRQAddr == addr) + { + //SPUIRQ_DBG("SPU IRQ (ALT): 0x%06x", addr); + IRQAsserted = true; + IRQ_Assert(IRQ_SPU, IRQAsserted); + } + } } -INLINE void PS_SPU::WriteSPURAM(uint32_t addr, uint16_t value) +INLINE void PS_SPU::WriteSPURAM(uint32 addr, uint16 value) { - CheckIRQAddr(addr); + CheckIRQAddr(addr); - SPURAM[addr] = value; + SPURAM[addr] = value; } -INLINE uint16_t PS_SPU::ReadSPURAM(uint32_t addr) +INLINE uint16 PS_SPU::ReadSPURAM(uint32 addr) { - CheckIRQAddr(addr); - return(SPURAM[addr]); + CheckIRQAddr(addr); + return(SPURAM[addr]); } #include "spu_reverb.inc" -int32_t PS_SPU::UpdateFromCDC(int32_t clocks) +int32 PS_SPU::UpdateFromCDC(int32 clocks) //pscpu_timestamp_t PS_SPU::Update(const pscpu_timestamp_t timestamp) { - //int32_t clocks = timestamp - lastts; - int32_t sample_clocks = 0; - //lastts = timestamp; + //int32 clocks = timestamp - lastts; + int32 sample_clocks = 0; + //lastts = timestamp; - clock_divider -= clocks; + clock_divider -= clocks; - while(clock_divider <= 0) + while(clock_divider <= 0) + { + clock_divider += 768; + sample_clocks++; + } + + while(sample_clocks > 0) + { + // Accumulated normal sound output. + int32 accum_l = 0; + int32 accum_r = 0; + + // Accumulated sound output for reverb input + int32 accum_fv_l = 0; + int32 accum_fv_r = 0; + + // Output of reverb processing. + int32 reverb_l = 0; + int32 reverb_r = 0; + + // Final output. + int32 output_l = 0; + int32 output_r = 0; + + const uint32 PhaseModCache = FM_Mode & ~ 1; +/* +** +** 0x1F801DAE Notes and Conjecture: +** ------------------------------------------------------------------------------------- +** | 15 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 4 3 2 1 0 | +** | ? | *13| ? | ba | *10 | wrr|rdr| df | is | c | +** ------------------------------------------------------------------------------------- +** +** c - Appears to be delayed copy of lower 6 bits from 0x1F801DAA. +** +** is - Interrupt asserted out status. (apparently not instantaneous status though...) +** +** df - Related to (c & 0x30) == 0x20 or (c & 0x30) == 0x30, at least. +** 0 = DMA busy(FIFO not empty when in DMA write mode?)? +** 1 = DMA ready? Something to do with the FIFO? +** +** rdr - Read(DMA read?) Ready? +** +** wrr - Write(DMA write?) Ready? +** +** *10 - Unknown. Some sort of (FIFO?) busy status?(BIOS tests for this bit in places) +** +** ba - Alternates between 0 and 1, even when SPUControl bit15 is 0; might be related to CD audio and voice 1 and 3 writing to SPU RAM. +** +** *13 - Unknown, was set to 1 when testing with an SPU delay system reg value of 0x200921E1(test result might not be reliable, re-run). +*/ + SPUStatus = SPUControl & 0x3F; + SPUStatus |= IRQAsserted ? 0x40 : 0x00; + + if(Regs[0xD6] == 0x4) // TODO: Investigate more(case 0x2C in global regs r/w handler) + SPUStatus |= (CWA & 0x100) ? 0x800 : 0x000; + + for(int voice_num = 0; voice_num < 24; voice_num++) + { + SPU_Voice *voice = &Voices[voice_num]; + int32 voice_pvs; + + voice->PreLRSample = 0; + + //PSX_WARNING("[SPU] Voice %d CurPhase=%08x, pitch=%04x, CurAddr=%08x", voice_num, voice->CurPhase, voice->Pitch, voice->CurAddr); + + // + // Decode new samples if necessary. + // + RunDecoder(voice); + + + // + // + // + int l, r; + + if(Noise_Mode & (1 << voice_num)) + voice_pvs = (int16)LFSR; + else { - clock_divider += 768; - sample_clocks++; + const int si = voice->DecodeReadPos; + const int pi = ((voice->CurPhase & 0xFFF) >> 4); + + voice_pvs = ((voice->DecodeBuffer[(si + 0) & 0x1F] * FIR_Table[pi][0]) + + (voice->DecodeBuffer[(si + 1) & 0x1F] * FIR_Table[pi][1]) + + (voice->DecodeBuffer[(si + 2) & 0x1F] * FIR_Table[pi][2]) + + (voice->DecodeBuffer[(si + 3) & 0x1F] * FIR_Table[pi][3])) >> 15; } - while(sample_clocks > 0) + voice_pvs = (voice_pvs * (int16)voice->ADSR.EnvLevel) >> 15; + voice->PreLRSample = voice_pvs; + + if(voice_num == 1 || voice_num == 3) { - // Accumulated normal sound output. - int32_t accum_l = 0; - int32_t accum_r = 0; + int index = voice_num >> 1; - // Accumulated sound output for reverb input - int32_t accum_fv_l = 0; - int32_t accum_fv_r = 0; - - // Output of reverb processing. - int32_t reverb_l = 0; - int32_t reverb_r = 0; - - // Final output. - int32_t output_l = 0; - int32_t output_r = 0; - - const uint32_t PhaseModCache = FM_Mode & ~ 1; - /* - ** - ** 0x1F801DAE Notes and Conjecture: - ** ------------------------------------------------------------------------------------- - ** | 15 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 4 3 2 1 0 | - ** | ? | *13| ? | ba | *10 | wrr|rdr| df | is | c | - ** ------------------------------------------------------------------------------------- - ** - ** c - Appears to be delayed copy of lower 6 bits from 0x1F801DAA. - ** - ** is - Interrupt asserted out status. (apparently not instantaneous status though...) - ** - ** df - Related to (c & 0x30) == 0x20 or (c & 0x30) == 0x30, at least. - ** 0 = DMA busy(FIFO not empty when in DMA write mode?)? - ** 1 = DMA ready? Something to do with the FIFO? - ** - ** rdr - Read(DMA read?) Ready? - ** - ** wrr - Write(DMA write?) Ready? - ** - ** *10 - Unknown. Some sort of (FIFO?) busy status?(BIOS tests for this bit in places) - ** - ** ba - Alternates between 0 and 1, even when SPUControl bit15 is 0; might be related to CD audio and voice 1 and 3 writing to SPU RAM. - ** - ** *13 - Unknown, was set to 1 when testing with an SPU delay system reg value of 0x200921E1(test result might not be reliable, re-run). - */ - SPUStatus = SPUControl & 0x3F; - SPUStatus |= IRQAsserted ? 0x40 : 0x00; - - if(Regs[0xD6] == 0x4) // TODO: Investigate more(case 0x2C in global regs r/w handler) - SPUStatus |= (CWA & 0x100) ? 0x800 : 0x000; - - for(int voice_num = 0; voice_num < 24; voice_num++) - { - SPU_Voice *voice = &Voices[voice_num]; - int32_t voice_pvs; - - voice->PreLRSample = 0; - - //PSX_WARNING("[SPU] Voice %d CurPhase=%08x, pitch=%04x, CurAddr=%08x", voice_num, voice->CurPhase, voice->Pitch, voice->CurAddr); - - // Decode new samples if necessary. - if(voice->DecodeFlags & 0x1) - voice->CurAddr = voice->LoopAddr & ~0x7; - - if(voice->CurPhase_SD >= (24 << 12)) - { - voice->CurPhase_SD -= 28 << 12; - DecodeSamples(voice); - } - else - { - CheckIRQAddr(voice->CurAddr); - } - // - // - // - int l, r; - - if(Noise_Mode & (1 << voice_num)) - voice_pvs = (int16)LFSR; - else - { - const int si = voice->CurPhase >> 12; - const int pi = ((voice->CurPhase & 0xFFF) >> 4); - - voice_pvs = ((voice->DecodeBuffer[si + 0] * FIR_Table[pi][0]) + - (voice->DecodeBuffer[si + 1] * FIR_Table[pi][1]) + - (voice->DecodeBuffer[si + 2] * FIR_Table[pi][2]) + - (voice->DecodeBuffer[si + 3] * FIR_Table[pi][3])) >> 15; - } - - voice_pvs = (voice_pvs * (int16)voice->ADSR.EnvLevel) >> 15; - voice->PreLRSample = voice_pvs; - - if(voice_num == 1 || voice_num == 3) - { - int index = voice_num >> 1; - - WriteSPURAM(0x400 | (index * 0x200) | CWA, voice_pvs); - } + WriteSPURAM(0x400 | (index * 0x200) | CWA, voice_pvs); + } - l = (voice_pvs * voice->Sweep[0].ReadVolume()) >> 15; - r = (voice_pvs * voice->Sweep[1].ReadVolume()) >> 15; + l = (voice_pvs * voice->Sweep[0].ReadVolume()) >> 15; + r = (voice_pvs * voice->Sweep[1].ReadVolume()) >> 15; - accum_l += l; - accum_r += r; + accum_l += l; + accum_r += r; - if(Reverb_Mode & (1 << voice_num)) - { - accum_fv_l += l; - accum_fv_r += r; - } + if(Reverb_Mode & (1 << voice_num)) + { + accum_fv_l += l; + accum_fv_r += r; + } - // Run sweep - for(int lr = 0; lr < 2; lr++) - voice->Sweep[lr].Clock(); + // Run sweep + for(int lr = 0; lr < 2; lr++) + voice->Sweep[lr].Clock(); - // Run enveloping - RunEnvelope(voice); + // Increment stuff + if(!voice->DecodePlayDelay) + { + unsigned phase_inc; - // Increment stuff - { - int32_t phase_inc; + // Run enveloping + RunEnvelope(voice); - if(PhaseModCache & (1 << voice_num)) - { - // This old formula: phase_inc = (voice->Pitch * ((voice - 1)->PreLRSample + 0x8000)) >> 15; - // is incorrect, as it does not handle carrier pitches >= 0x8000 properly. + if(PhaseModCache & (1 << voice_num)) + { + // This old formula: phase_inc = (voice->Pitch * ((voice - 1)->PreLRSample + 0x8000)) >> 15; + // is incorrect, as it does not handle carrier pitches >= 0x8000 properly. + phase_inc = voice->Pitch + (((int16)voice->Pitch * ((voice - 1)->PreLRSample)) >> 15); + } + else + phase_inc = voice->Pitch; - phase_inc = voice->Pitch + (((int16)voice->Pitch * ((voice - 1)->PreLRSample)) >> 15); - if(phase_inc < 0) - { - PSX_DBG(PSX_DBG_ERROR, "[SPU] phase_inc < 0 (THIS SHOULD NOT HAPPEN)\n"); - phase_inc = 0; - } - } - else - phase_inc = voice->Pitch; + if(phase_inc > 0x3FFF) + phase_inc = 0x3FFF; - if(phase_inc > 0x3FFF) - phase_inc = 0x3FFF; + { + const uint32 tmp_phase = voice->CurPhase + phase_inc; + const unsigned used = tmp_phase >> 12; - voice->CurPhase = (voice->CurPhase + phase_inc) & 0x1FFFF; - voice->CurPhase_SD += phase_inc; - } + voice->CurPhase = tmp_phase & 0xFFF; + voice->DecodeAvail -= used; + voice->DecodeReadPos = (voice->DecodeReadPos + used) & 0x1F; + } + } + else + voice->DecodePlayDelay--; - if(VoiceOff & (1 << voice_num)) - { - if(voice->ADSR.Phase != ADSR_RELEASE) - { - ReleaseEnvelope(voice); - } - } + if(VoiceOff & (1U << voice_num)) + { + if(voice->ADSR.Phase != ADSR_RELEASE) + { + ReleaseEnvelope(voice); + } + } - if(VoiceOn & (1 << voice_num)) - { - ResetEnvelope(voice); + if(VoiceOn & (1U << voice_num)) + { + //printf("Voice On: %u\n", voice_num); - voice->DecodeFlags = 0; - voice->DecodeWritePos = 0; + ResetEnvelope(voice); - BlockEnd &= ~(1 << voice_num); + voice->DecodeFlags = 0; + voice->DecodeWritePos = 0; + voice->DecodeReadPos = 0; + voice->DecodeAvail = 0; + voice->DecodePlayDelay = 4; - // Weight/filter previous value initialization: - voice->DecodeBuffer[0x1E] = 0; - voice->DecodeBuffer[0x1F] = 0; + BlockEnd &= ~(1 << voice_num); - voice->CurPhase = 0; - voice->CurPhase_SD = 28 << 12; // Trigger initial sample decode + // + // Weight/filter previous value initialization: + // + voice->DecodeM2 = 0; + voice->DecodeM1 = 0; - voice->CurAddr = voice->StartAddr & ~0x7; - } + voice->CurPhase = 0; + voice->CurAddr = voice->StartAddr & ~0x7; + voice->IgnoreSampLA = false; + } - if(!(SPUControl & 0x8000)) - { - voice->ADSR.Phase = ADSR_RELEASE; - voice->ADSR.EnvLevel = 0; - } - } + if(!(SPUControl & 0x8000)) + { + voice->ADSR.Phase = ADSR_RELEASE; + voice->ADSR.EnvLevel = 0; + } + } - VoiceOff = 0; - VoiceOn = 0; + VoiceOff = 0; + VoiceOn = 0; - // "Mute" control doesn't seem to affect CD audio(though CD audio reverb wasn't tested...) - // TODO: If we add sub-sample timing accuracy, see if it's checked for every channel at different times, or just once. - if(!(SPUControl & 0x4000)) - { - accum_l = 0; - accum_r = 0; - accum_fv_l = 0; - accum_fv_r = 0; - } + // "Mute" control doesn't seem to affect CD audio(though CD audio reverb wasn't tested...) + // TODO: If we add sub-sample timing accuracy, see if it's checked for every channel at different times, or just once. + if(!(SPUControl & 0x4000)) + { + accum_l = 0; + accum_r = 0; + accum_fv_l = 0; + accum_fv_r = 0; + } - // Get CD-DA - { - int32_t cda_raw[2]; - int32_t cdav[2]; + // Get CD-DA + { + int32 cda_raw[2]; + int32 cdav[2]; - CDC->GetCDAudio(cda_raw); // PS_CDC::GetCDAudio() guarantees the variables passed by reference will be set to 0, - // and that their range shall be -32768 through 32767. + CDC->GetCDAudio(cda_raw); // PS_CDC::GetCDAudio() guarantees the variables passed by reference will be set to 0, + // and that their range shall be -32768 through 32767. - WriteSPURAM(CWA | 0x000, cda_raw[0]); - WriteSPURAM(CWA | 0x200, cda_raw[1]); + WriteSPURAM(CWA | 0x000, cda_raw[0]); + WriteSPURAM(CWA | 0x200, cda_raw[1]); - for(unsigned i = 0; i < 2; i++) - cdav[i] = (cda_raw[i] * CDVol[i]) >> 15; + for(unsigned i = 0; i < 2; i++) + cdav[i] = (cda_raw[i] * CDVol[i]) >> 15; - if(SPUControl & 0x0001) - { - accum_l += cdav[0]; - accum_r += cdav[1]; + if(SPUControl & 0x0001) + { + accum_l += cdav[0]; + accum_r += cdav[1]; - if(SPUControl & 0x0004) // TODO: Test this bit(and see if it is really dependent on bit0) - { - accum_fv_l += cdav[0]; - accum_fv_r += cdav[1]; - } - } - } + if(SPUControl & 0x0004) // TODO: Test this bit(and see if it is really dependent on bit0) + { + accum_fv_l += cdav[0]; + accum_fv_r += cdav[1]; + } + } + } - CWA = (CWA + 1) & 0x1FF; + CWA = (CWA + 1) & 0x1FF; - NoiseCounter += NoiseFreqTable[(SPUControl >> 8) & 0x3F]; - if(NoiseCounter >= 0x8000) - { - NoiseCounter -= 0x8000; - LFSR = (LFSR << 1) | (((LFSR >> 15) ^ (LFSR >> 12) ^ (LFSR >> 11) ^ (LFSR >> 10) ^ 1) & 1); - } - - clamp(&accum_l, -32768, 32767); - clamp(&accum_r, -32768, 32767); - clamp(&accum_fv_l, -32768, 32767); - clamp(&accum_fv_r, -32768, 32767); + NoiseCounter += NoiseFreqTable[(SPUControl >> 8) & 0x3F]; + if(NoiseCounter >= 0x8000) + { + NoiseCounter -= 0x8000; + LFSR = (LFSR << 1) | (((LFSR >> 15) ^ (LFSR >> 12) ^ (LFSR >> 11) ^ (LFSR >> 10) ^ 1) & 1); + } + clamp(&accum_l, -32768, 32767); + clamp(&accum_r, -32768, 32767); + clamp(&accum_fv_l, -32768, 32767); + clamp(&accum_fv_r, -32768, 32767); + #if 0 - accum_l = 0; - accum_r = 0; - //accum_fv_l = (short)(rand()); - //accum_fv_r = (short)(rand()); + accum_l = 0; + accum_r = 0; + //accum_fv_l = (short)(rand()); + //accum_fv_r = (short)(rand()); #endif - RunReverb(accum_fv_l, accum_fv_r, reverb_l, reverb_r); + RunReverb(accum_fv_l, accum_fv_r, reverb_l, reverb_r); - //MDFN_DispMessage("%d %d\n", MainVol[0], MainVol[1], ReverbVol[0], ReverbVol[1]); + //MDFN_DispMessage("%d %d\n", MainVol[0], MainVol[1], ReverbVol[0], ReverbVol[1]); - // FIXME: Dry volume versus everything else - output_l = (((accum_l * GlobalSweep[0].ReadVolume()) >> 16) + ((reverb_l * ReverbVol[0]) >> 15)); - output_r = (((accum_r * GlobalSweep[1].ReadVolume()) >> 16) + ((reverb_r * ReverbVol[1]) >> 15)); + // FIXME: Dry volume versus everything else + output_l = (((accum_l * GlobalSweep[0].ReadVolume()) >> 16) + ((reverb_l * ReverbVol[0]) >> 15)); + output_r = (((accum_r * GlobalSweep[1].ReadVolume()) >> 16) + ((reverb_r * ReverbVol[1]) >> 15)); - //output_l = reverb_l; - //output_r = reverb_r; + //output_l = reverb_l; + //output_r = reverb_r; - clamp(&output_l, -32768, 32767); - clamp(&output_r, -32768, 32767); + clamp(&output_l, -32768, 32767); + clamp(&output_r, -32768, 32767); - if(IntermediateBufferPos < 4096) // Overflow might occur in some debugger use cases. - { - IntermediateBuffer[IntermediateBufferPos][0] = output_l; - IntermediateBuffer[IntermediateBufferPos][1] = output_r; - IntermediateBufferPos++; - } + if(IntermediateBufferPos < 4096) // Overflow might occur in some debugger use cases. + { + IntermediateBuffer[IntermediateBufferPos][0] = output_l; + IntermediateBuffer[IntermediateBufferPos][1] = output_r; + IntermediateBufferPos++; + } - sample_clocks--; + sample_clocks--; - // Clock global sweep - for(int lr = 0; lr < 2; lr++) - GlobalSweep[lr].Clock(); - } + // Clock global sweep + for(int lr = 0; lr < 2; lr++) + GlobalSweep[lr].Clock(); + } - //assert(clock_divider < 768); + //assert(clock_divider < 768); - return clock_divider; + return clock_divider; } -void PS_SPU::WriteDMA(uint32_t V) +void PS_SPU::WriteDMA(uint32 V) { - //SPUIRQ_DBG("DMA Write, RWAddr after=0x%06x", RWAddr); - WriteSPURAM(RWAddr, V); - RWAddr = (RWAddr + 1) & 0x3FFFF; + //SPUIRQ_DBG("DMA Write, RWAddr after=0x%06x", RWAddr); + WriteSPURAM(RWAddr, V); + RWAddr = (RWAddr + 1) & 0x3FFFF; - WriteSPURAM(RWAddr, V >> 16); - RWAddr = (RWAddr + 1) & 0x3FFFF; + WriteSPURAM(RWAddr, V >> 16); + RWAddr = (RWAddr + 1) & 0x3FFFF; - CheckIRQAddr(RWAddr); + CheckIRQAddr(RWAddr); } -uint32_t PS_SPU::ReadDMA(void) +uint32 PS_SPU::ReadDMA(void) { - uint32_t ret; + uint32 ret; - ret = (uint16)ReadSPURAM(RWAddr); - RWAddr = (RWAddr + 1) & 0x3FFFF; + ret = (uint16)ReadSPURAM(RWAddr); + RWAddr = (RWAddr + 1) & 0x3FFFF; - ret |= (uint32)(uint16)ReadSPURAM(RWAddr) << 16; - RWAddr = (RWAddr + 1) & 0x3FFFF; + ret |= (uint32)(uint16)ReadSPURAM(RWAddr) << 16; + RWAddr = (RWAddr + 1) & 0x3FFFF; - CheckIRQAddr(RWAddr); + CheckIRQAddr(RWAddr); - //SPUIRQ_DBG("DMA Read, RWAddr after=0x%06x", RWAddr); + //SPUIRQ_DBG("DMA Read, RWAddr after=0x%06x", RWAddr); - return ret; + return(ret); } -void PS_SPU::Write(pscpu_timestamp_t timestamp, uint32_t A, uint16_t V) +void PS_SPU::Write(pscpu_timestamp_t timestamp, uint32 A, uint16 V) { - //if((A & 0x3FF) < 0x180) - // PSX_WARNING("[SPU] Write: %08x %04x", A, V); + //if((A & 0x3FF) < 0x180) + // PSX_WARNING("[SPU] Write: %08x %04x", A, V); - A &= 0x3FF; + A &= 0x3FF; - if(A >= 0x200) - { - //printf("Write: %08x %04x\n", A, V); - if(A < 0x260) - { - SPU_Voice *voice = &Voices[(A - 0x200) >> 2]; - voice->Sweep[(A & 2) >> 1].WriteVolume(V); - } - else if(A < 0x280) - AuxRegs[(A & 0x1F) >> 1] = V; + if(A >= 0x200) + { + //printf("Write: %08x %04x\n", A, V); + if(A < 0x260) + { + SPU_Voice *voice = &Voices[(A - 0x200) >> 2]; + voice->Sweep[(A & 2) >> 1].WriteVolume(V); + } + else if(A < 0x280) + AuxRegs[(A & 0x1F) >> 1] = V; - return; - } + return; + } - if(A < 0x180) - { - SPU_Voice *voice = &Voices[A >> 4]; + if(A < 0x180) + { + SPU_Voice *voice = &Voices[A >> 4]; - switch(A & 0xF) - { - case 0x00: - case 0x02: - voice->Sweep[(A & 2) >> 1].WriteControl(V); - break; - case 0x04: - voice->Pitch = V; - break; - case 0x06: - voice->StartAddr = (V << 2) & 0x3FFFF; - break; - case 0x08: - voice->ADSRControl &= 0xFFFF0000; - voice->ADSRControl |= V; - CacheEnvelope(voice); - break; - case 0x0A: - voice->ADSRControl &= 0x0000FFFF; - voice->ADSRControl |= V << 16; - CacheEnvelope(voice); - break; - case 0x0C: - voice->ADSR.EnvLevel = V; - break; - case 0x0E: - voice->LoopAddr = (V << 2) & 0x3FFFF; - //if((voice - Voices) == 22) - //{ - // SPUIRQ_DBG("Manual loop address setting for voice %d: %04x", (int)(voice - Voices), V); - //} - break; - } - } - else - { - switch(A & 0x7F) - { - case 0x00: - case 0x02: - GlobalSweep[(A & 2) >> 1].WriteControl(V); - break; - case 0x04: - ReverbVol[0] = (int16)V; - break; - case 0x06: - ReverbVol[1] = (int16)V; - break; - // Voice ON: - case 0x08: - VoiceOn &= 0xFFFF0000; - VoiceOn |= V << 0; - break; - case 0x0a: - VoiceOn &= 0x0000FFFF; - VoiceOn |= (V & 0xFF) << 16; - break; - // Voice OFF: - case 0x0c: - VoiceOff &= 0xFFFF0000; - VoiceOff |= V << 0; - break; - case 0x0e: - VoiceOff &= 0x0000FFFF; - VoiceOff |= (V & 0xFF) << 16; - break; - case 0x10: - FM_Mode &= 0xFFFF0000; - FM_Mode |= V << 0; - break; - case 0x12: - FM_Mode &= 0x0000FFFF; - FM_Mode |= (V & 0xFF) << 16; - break; - case 0x14: - Noise_Mode &= 0xFFFF0000; - Noise_Mode |= V << 0; - break; - case 0x16: - Noise_Mode &= 0x0000FFFF; - Noise_Mode |= (V & 0xFF) << 16; - break; - case 0x18: - Reverb_Mode &= 0xFFFF0000; - Reverb_Mode |= V << 0; - break; - case 0x1A: - Reverb_Mode &= 0x0000FFFF; - Reverb_Mode |= (V & 0xFF) << 16; - break; - case 0x1C: - BlockEnd &= 0xFFFF0000; - BlockEnd |= V << 0; - break; - case 0x1E: - BlockEnd &= 0x0000FFFF; - BlockEnd |= V << 16; - break; - case 0x22: - ReverbWA = (V << 2) & 0x3FFFF; - ReverbCur = ReverbWA; - //PSX_WARNING("[SPU] Reverb WA set: 0x%04x", V); - break; - case 0x24: - IRQAddr = (V << 2) & 0x3FFFF; - CheckIRQAddr(RWAddr); - //SPUIRQ_DBG("Set IRQAddr=0x%06x", IRQAddr); - break; - case 0x26: - RWAddr = (V << 2) & 0x3FFFF; - CheckIRQAddr(RWAddr); - //SPUIRQ_DBG("Set RWAddr=0x%06x", RWAddr); - break; - case 0x28: - WriteSPURAM(RWAddr, V); - RWAddr = (RWAddr + 1) & 0x3FFFF; - CheckIRQAddr(RWAddr); - break; - case 0x2A: - //if((SPUControl & 0x80) && !(V & 0x80)) - // printf("\n\n\n\n ************** REVERB PROCESSING DISABLED\n\n\n\n"); + switch(A & 0xF) + { + case 0x00: + case 0x02: + voice->Sweep[(A & 2) >> 1].WriteControl(V); + break; - SPUControl = V; - //SPUIRQ_DBG("Set SPUControl=0x%04x -- IRQA=%06x, RWA=%06x", V, IRQAddr, RWAddr); - //printf("SPU control write: %04x\n", V); - if(!(V & 0x40)) - { - IRQAsserted = false; - IRQ_Assert(IRQ_SPU, IRQAsserted); - } - CheckIRQAddr(RWAddr); - break; - case 0x2C: - PSX_WARNING("[SPU] Global reg 0x2c set: 0x%04x", V); - break; - case 0x30: - CDVol[0] = V; - break; - case 0x32: - CDVol[1] = V; - break; - case 0x34: - ExternVol[0] = V; - break; - case 0x36: - ExternVol[1] = V; - break; - case 0x38: - case 0x3A: - GlobalSweep[(A & 2) >> 1].WriteVolume(V); - break; - } - } + case 0x04: voice->Pitch = V; + break; - Regs[(A & 0x1FF) >> 1] = V; + case 0x06: voice->StartAddr = (V << 2) & 0x3FFFF; + break; + + case 0x08: voice->ADSRControl &= 0xFFFF0000; + voice->ADSRControl |= V; + CacheEnvelope(voice); + break; + + case 0x0A: voice->ADSRControl &= 0x0000FFFF; + voice->ADSRControl |= V << 16; + CacheEnvelope(voice); + break; + + case 0x0C: voice->ADSR.EnvLevel = V; + break; + + case 0x0E: voice->LoopAddr = (V << 2) & 0x3FFFF; + voice->IgnoreSampLA = true; + //if((voice - Voices) == 22) + //{ + // SPUIRQ_DBG("Manual loop address setting for voice %d: %04x", (int)(voice - Voices), V); + //} + break; + } + } + else + { + switch(A & 0x7F) + { + case 0x00: + case 0x02: GlobalSweep[(A & 2) >> 1].WriteControl(V); + break; + + case 0x04: ReverbVol[0] = (int16)V; + break; + + case 0x06: ReverbVol[1] = (int16)V; + break; + + // Voice ON: + case 0x08: VoiceOn &= 0xFFFF0000; + VoiceOn |= V << 0; + break; + + case 0x0a: VoiceOn &= 0x0000FFFF; + VoiceOn |= (V & 0xFF) << 16; + break; + + // Voice OFF: + case 0x0c: VoiceOff &= 0xFFFF0000; + VoiceOff |= V << 0; + break; + + case 0x0e: VoiceOff &= 0x0000FFFF; + VoiceOff |= (V & 0xFF) << 16; + break; + + case 0x10: FM_Mode &= 0xFFFF0000; + FM_Mode |= V << 0; + break; + + case 0x12: FM_Mode &= 0x0000FFFF; + FM_Mode |= (V & 0xFF) << 16; + break; + + case 0x14: Noise_Mode &= 0xFFFF0000; + Noise_Mode |= V << 0; + break; + + case 0x16: Noise_Mode &= 0x0000FFFF; + Noise_Mode |= (V & 0xFF) << 16; + break; + + case 0x18: Reverb_Mode &= 0xFFFF0000; + Reverb_Mode |= V << 0; + break; + + case 0x1A: Reverb_Mode &= 0x0000FFFF; + Reverb_Mode |= (V & 0xFF) << 16; + break; + + case 0x1C: BlockEnd &= 0xFFFF0000; + BlockEnd |= V << 0; + break; + + case 0x1E: BlockEnd &= 0x0000FFFF; + BlockEnd |= V << 16; + break; + + case 0x22: ReverbWA = (V << 2) & 0x3FFFF; + ReverbCur = ReverbWA; + //PSX_WARNING("[SPU] Reverb WA set: 0x%04x", V); + break; + + case 0x24: IRQAddr = (V << 2) & 0x3FFFF; + CheckIRQAddr(RWAddr); + //SPUIRQ_DBG("Set IRQAddr=0x%06x", IRQAddr); + break; + + case 0x26: RWAddr = (V << 2) & 0x3FFFF; + CheckIRQAddr(RWAddr); + //SPUIRQ_DBG("Set RWAddr=0x%06x", RWAddr); + break; + + case 0x28: WriteSPURAM(RWAddr, V); + RWAddr = (RWAddr + 1) & 0x3FFFF; + CheckIRQAddr(RWAddr); + break; + + case 0x2A: //if((SPUControl & 0x80) && !(V & 0x80)) + // printf("\n\n\n\n ************** REVERB PROCESSING DISABLED\n\n\n\n"); + + SPUControl = V; + //SPUIRQ_DBG("Set SPUControl=0x%04x -- IRQA=%06x, RWA=%06x", V, IRQAddr, RWAddr); + //printf("SPU control write: %04x\n", V); + if(!(V & 0x40)) + { + IRQAsserted = false; + IRQ_Assert(IRQ_SPU, IRQAsserted); + } + CheckIRQAddr(RWAddr); + break; + + case 0x2C: PSX_WARNING("[SPU] Global reg 0x2c set: 0x%04x", V); + break; + + case 0x30: CDVol[0] = V; + break; + + case 0x32: CDVol[1] = V; + break; + + case 0x34: ExternVol[0] = V; + break; + + case 0x36: ExternVol[1] = V; + break; + + case 0x38: + case 0x3A: GlobalSweep[(A & 2) >> 1].WriteVolume(V); + break; + } + } + + Regs[(A & 0x1FF) >> 1] = V; } -uint16_t PS_SPU::Read(pscpu_timestamp_t timestamp, uint32_t A) +uint16 PS_SPU::Read(pscpu_timestamp_t timestamp, uint32 A) { - A &= 0x3FF; + A &= 0x3FF; - PSX_DBGINFO("[SPU] Read: %08x", A); + PSX_DBGINFO("[SPU] Read: %08x", A); - if(A >= 0x200) - { - if(A < 0x260) - { - SPU_Voice *voice = &Voices[(A - 0x200) >> 2]; + if(A >= 0x200) + { + if(A < 0x260) + { + SPU_Voice *voice = &Voices[(A - 0x200) >> 2]; - //printf("Read: %08x %04x\n", A, voice->Sweep[(A & 2) >> 1].ReadVolume()); + //printf("Read: %08x %04x\n", A, voice->Sweep[(A & 2) >> 1].ReadVolume()); - return voice->Sweep[(A & 2) >> 1].ReadVolume(); - } - else if(A < 0x280) - return(AuxRegs[(A & 0x1F) >> 1]); + return voice->Sweep[(A & 2) >> 1].ReadVolume(); + } + else if(A < 0x280) + return(AuxRegs[(A & 0x1F) >> 1]); - return(0xFFFF); - } + return(0xFFFF); + } - if(A < 0x180) - { - SPU_Voice *voice = (SPU_Voice*)&Voices[A >> 4]; + if(A < 0x180) + { + SPU_Voice *voice = &Voices[A >> 4]; - switch(A & 0xF) - { - case 0x0C: - return(voice->ADSR.EnvLevel); - case 0x0E: - return(voice->LoopAddr >> 2); - } - } - else - { - switch(A & 0x7F) - { - case 0x1C: - return(BlockEnd); - case 0x1E: - return(BlockEnd >> 16); - case 0x26: - //PSX_WARNING("[SPU] RWADDR Read"); - break; - case 0x28: - PSX_WARNING("[SPU] SPURAM Read port(?) Read"); - { - uint16_t ret = ReadSPURAM(RWAddr); + switch(A & 0xF) + { + case 0x0C: return(voice->ADSR.EnvLevel); + case 0x0E: return(voice->LoopAddr >> 2); + } + } + else + { + switch(A & 0x7F) + { + case 0x1C: return(BlockEnd); + case 0x1E: return(BlockEnd >> 16); - RWAddr = (RWAddr + 1) & 0x3FFFF; - CheckIRQAddr(RWAddr); + case 0x26: //PSX_WARNING("[SPU] RWADDR Read"); + break; - return ret; - } - case 0x2a: - return(SPUControl); - /* FIXME: What is this used for? */ - case 0x3C: - //PSX_WARNING("[SPU] Read Unknown: %08x", A); - return(0); - case 0x38: - case 0x3A: - return(GlobalSweep[(A & 2) >> 1].ReadVolume()); - } - } + case 0x28: PSX_WARNING("[SPU] SPURAM Read port(?) Read"); - return(Regs[(A & 0x1FF) >> 1]); + { + uint16 ret = ReadSPURAM(RWAddr); + + RWAddr = (RWAddr + 1) & 0x3FFFF; + CheckIRQAddr(RWAddr); + + return(ret); + } + + case 0x2a: + return(SPUControl); + +/* FIXME: What is this used for? */ + case 0x3C: + //PSX_WARNING("[SPU] Read Unknown: %08x", A); + return(0); + + case 0x38: + case 0x3A: return(GlobalSweep[(A & 2) >> 1].ReadVolume()); + } + } + + return(Regs[(A & 0x1FF) >> 1]); } -void PS_SPU::StartFrame(double rate, uint32_t quality) + +void PS_SPU::StartFrame(double rate, uint32 quality) { } -int32_t PS_SPU::EndFrame(int16_t *SoundBuf) +int32 PS_SPU::EndFrame(int16 *SoundBuf) { return 0; } -#define SFSWEEP(r) SFVAR((r).Control), \ - SFVAR((r).Current), \ - SFVAR((r).Divider) - -#define SFVOICE(n) SFARRAY32(&Voices[n].DecodeBuffer[0], sizeof(Voices[n].DecodeBuffer) / sizeof(Voices[n].DecodeBuffer[0])), \ - SFVAR(Voices[n].DecodeWritePos), \ - SFVAR(Voices[n].DecodeFlags), \ - \ - SFSWEEP(Voices[n].Sweep[0]), \ - SFSWEEP(Voices[n].Sweep[1]), \ - \ - SFVAR(Voices[n].Pitch), \ - SFVAR(Voices[n].CurPhase), \ - SFVAR(Voices[n].CurPhase_SD), \ - \ - SFVAR(Voices[n].StartAddr), \ - SFVAR(Voices[n].CurAddr), \ - SFVAR(Voices[n].ADSRControl), \ - SFVAR(Voices[n].LoopAddr), \ - SFVAR(Voices[n].PreLRSample), \ - \ - SFVAR(Voices[n].ADSR.EnvLevel), \ - SFVAR(Voices[n].ADSR.Divider), \ - SFVAR(Voices[n].ADSR.Phase), \ - \ - SFVAR(Voices[n].ADSR.AttackExp), \ - SFVAR(Voices[n].ADSR.SustainExp), \ - SFVAR(Voices[n].ADSR.SustainDec), \ - SFVAR(Voices[n].ADSR.ReleaseExp), \ - \ - SFVAR(Voices[n].ADSR.AttackRate), \ - SFVAR(Voices[n].ADSR.DecayRate), \ - SFVAR(Voices[n].ADSR.SustainRate), \ - SFVAR(Voices[n].ADSR.ReleaseRate), \ - \ - SFVAR(Voices[n].ADSR.SustainLevel) - int PS_SPU::StateAction(StateMem *sm, int load, int data_only) { - SFORMAT StateRegs[] = - { - SFVOICE(0), - SFVOICE(1), - SFVOICE(2), - SFVOICE(3), - SFVOICE(4), - SFVOICE(5), - SFVOICE(6), - SFVOICE(7), - SFVOICE(8), - SFVOICE(9), - SFVOICE(10), - SFVOICE(11), - SFVOICE(12), - SFVOICE(13), - SFVOICE(14), - SFVOICE(15), - SFVOICE(16), - SFVOICE(17), - SFVOICE(18), - SFVOICE(19), - SFVOICE(20), - SFVOICE(21), - SFVOICE(22), - SFVOICE(23), + SFORMAT StateRegs[] = + { +#define SFSWEEP(r) SFVAR((r).Control), \ + SFVAR((r).Current), \ + SFVAR((r).Divider) - SFVAR(NoiseCounter), - SFVAR(LFSR), +#define SFVOICE(n) SFARRAY16(&Voices[n].DecodeBuffer[0], sizeof(Voices[n].DecodeBuffer) / sizeof(Voices[n].DecodeBuffer[0])), \ + SFVAR(Voices[n].DecodeM2), \ + SFVAR(Voices[n].DecodeM1), \ + SFVAR(Voices[n].DecodePlayDelay), \ + SFVAR(Voices[n].DecodeWritePos), \ + SFVAR(Voices[n].DecodeReadPos), \ + SFVAR(Voices[n].DecodeAvail), \ + SFVAR(Voices[n].DecodeShift), \ + SFVAR(Voices[n].DecodeWeight), \ + SFVAR(Voices[n].DecodeFlags), \ + SFVAR(Voices[n].IgnoreSampLA), \ + \ + SFSWEEP(Voices[n].Sweep[0]), \ + SFSWEEP(Voices[n].Sweep[1]), \ + \ + SFVAR(Voices[n].Pitch), \ + SFVAR(Voices[n].CurPhase), \ + \ + SFVAR(Voices[n].StartAddr), \ + SFVAR(Voices[n].CurAddr), \ + SFVAR(Voices[n].ADSRControl), \ + SFVAR(Voices[n].LoopAddr), \ + SFVAR(Voices[n].PreLRSample), \ + \ + SFVAR(Voices[n].ADSR.EnvLevel), \ + SFVAR(Voices[n].ADSR.Divider), \ + SFVAR(Voices[n].ADSR.Phase), \ + \ + SFVAR(Voices[n].ADSR.AttackExp), \ + SFVAR(Voices[n].ADSR.SustainExp), \ + SFVAR(Voices[n].ADSR.SustainDec), \ + SFVAR(Voices[n].ADSR.ReleaseExp), \ + \ + SFVAR(Voices[n].ADSR.AttackRate), \ + SFVAR(Voices[n].ADSR.DecayRate), \ + SFVAR(Voices[n].ADSR.SustainRate), \ + SFVAR(Voices[n].ADSR.ReleaseRate), \ + \ + SFVAR(Voices[n].ADSR.SustainLevel) - SFVAR(FM_Mode), - SFVAR(Noise_Mode), - SFVAR(Reverb_Mode), + SFVOICE(0), + SFVOICE(1), + SFVOICE(2), + SFVOICE(3), + SFVOICE(4), + SFVOICE(5), + SFVOICE(6), + SFVOICE(7), + SFVOICE(8), + SFVOICE(9), + SFVOICE(10), + SFVOICE(11), + SFVOICE(12), + SFVOICE(13), + SFVOICE(14), + SFVOICE(15), + SFVOICE(16), + SFVOICE(17), + SFVOICE(18), + SFVOICE(19), + SFVOICE(20), + SFVOICE(21), + SFVOICE(22), + SFVOICE(23), +#undef SFVOICE - SFVAR(ReverbWA), + SFVAR(NoiseCounter), + SFVAR(LFSR), - SFSWEEP(GlobalSweep[0]), - SFSWEEP(GlobalSweep[1]), + SFVAR(FM_Mode), + SFVAR(Noise_Mode), + SFVAR(Reverb_Mode), - SFARRAY32(ReverbVol, sizeof(ReverbVol) / sizeof(ReverbVol[0])), + SFVAR(ReverbWA), - SFARRAY32(CDVol, sizeof(CDVol) / sizeof(CDVol[0])), - SFARRAY32(ExternVol, sizeof(ExternVol) / sizeof(ExternVol[0])), + SFSWEEP(GlobalSweep[0]), + SFSWEEP(GlobalSweep[1]), - SFVAR(IRQAddr), + SFARRAY32(ReverbVol, sizeof(ReverbVol) / sizeof(ReverbVol[0])), - SFVAR(RWAddr), + SFARRAY32(CDVol, sizeof(CDVol) / sizeof(CDVol[0])), + SFARRAY32(ExternVol, sizeof(ExternVol) / sizeof(ExternVol[0])), + + SFVAR(IRQAddr), - SFVAR(SPUControl), + SFVAR(RWAddr), - SFVAR(VoiceOn), - SFVAR(VoiceOff), + SFVAR(SPUControl), - SFVAR(BlockEnd), + SFVAR(VoiceOn), + SFVAR(VoiceOff), - SFVAR(CWA), + SFVAR(BlockEnd), - SFARRAY16(Regs, sizeof(Regs) / sizeof(Regs[0])), - SFARRAY16(AuxRegs, sizeof(AuxRegs) / sizeof(AuxRegs[0])), + SFVAR(CWA), - SFARRAY16(&RDSB[0][0], sizeof(RDSB) / sizeof(RDSB[0][0])), - SFVAR(RDSB_WP), + SFARRAY16(Regs, sizeof(Regs) / sizeof(Regs[0])), + SFARRAY16(AuxRegs, sizeof(AuxRegs) / sizeof(AuxRegs[0])), - SFARRAY16(&RUSB[0][0], sizeof(RUSB) / sizeof(RUSB[0][0])), - SFVAR(RUSB_WP), + SFARRAY16(&RDSB[0][0], sizeof(RDSB) / sizeof(RDSB[0][0])), + SFVAR(RDSB_WP), - SFVAR(ReverbCur), - SFVAR(IRQAsserted), + SFARRAY16(&RUSB[0][0], sizeof(RUSB) / sizeof(RUSB[0][0])), + SFVAR(RUSB_WP), - SFVAR(clock_divider), + SFVAR(ReverbCur), + SFVAR(IRQAsserted), - SFARRAY16(SPURAM, 524288 / sizeof(uint16)), - SFEND - }; + SFVAR(clock_divider), + + SFARRAY16(SPURAM, 524288 / sizeof(uint16)), + SFEND + }; #undef SFSWEEP - int ret = 1; + int ret = 1; - ret &= MDFNSS_StateAction(sm, load, data_only, StateRegs, "SPU"); + ret &= MDFNSS_StateAction(sm, load, data_only, StateRegs, "SPU"); - if(load) - { + if(load) + { + for(unsigned i = 0; i < 24; i++) + { + Voices[i].DecodeReadPos &= 0x1F; + Voices[i].DecodeWritePos &= 0x1F; + } - } + RDSB_WP &= 0x3F; + RUSB_WP &= 0x3F; - return ret; + IRQ_Assert(IRQ_SPU, IRQAsserted); + } + + return(ret); } -uint16_t PS_SPU::PeekSPURAM(uint32_t address) +uint16 PS_SPU::PeekSPURAM(uint32 address) { - return SPURAM[address & 0x3FFFF]; + return(SPURAM[address & 0x3FFFF]); } -void PS_SPU::PokeSPURAM(uint32_t address, uint16_t value) +void PS_SPU::PokeSPURAM(uint32 address, uint16 value) { - SPURAM[address & 0x3FFFF] = value; + SPURAM[address & 0x3FFFF] = value; } -uint32_t PS_SPU::GetRegister(unsigned int which, char *special, const uint32_t special_len) +uint32 PS_SPU::GetRegister(unsigned int which, char *special, const uint32 special_len) { - uint32_t ret = 0xDEADBEEF; + uint32 ret = 0xDEADBEEF; - if(which >= 0x8000) - { - unsigned int v = (which - 0x8000) >> 8; + if(which >= 0x8000) + { + unsigned int v = (which - 0x8000) >> 8; - switch((which & 0xFF) | 0x8000) - { - case GSREG_V0_VOL_CTRL_L: - ret = Regs[v * 8 + 0x0]; - break; + switch((which & 0xFF) | 0x8000) + { + case GSREG_V0_VOL_CTRL_L: + ret = Regs[v * 8 + 0x0]; + break; - case GSREG_V0_VOL_CTRL_R: - ret = Regs[v * 8 + 0x1]; - break; + case GSREG_V0_VOL_CTRL_R: + ret = Regs[v * 8 + 0x1]; + break; - case GSREG_V0_VOL_L: - ret = Voices[v].Sweep[0].ReadVolume() & 0xFFFF; - break; + case GSREG_V0_VOL_L: + ret = Voices[v].Sweep[0].ReadVolume() & 0xFFFF; + break; - case GSREG_V0_VOL_R: - ret = Voices[v].Sweep[1].ReadVolume() & 0xFFFF; - break; + case GSREG_V0_VOL_R: + ret = Voices[v].Sweep[1].ReadVolume() & 0xFFFF; + break; - case GSREG_V0_PITCH: - ret = Voices[v].Pitch; - break; + case GSREG_V0_PITCH: + ret = Voices[v].Pitch; + break; - case GSREG_V0_STARTADDR: - ret = Voices[v].StartAddr; - break; + case GSREG_V0_STARTADDR: + ret = Voices[v].StartAddr; + break; - case GSREG_V0_ADSR_CTRL: - ret = Voices[v].ADSRControl; - break; + case GSREG_V0_ADSR_CTRL: + ret = Voices[v].ADSRControl; + break; - case GSREG_V0_ADSR_LEVEL: - ret = Voices[v].ADSR.EnvLevel; - break; + case GSREG_V0_ADSR_LEVEL: + ret = Voices[v].ADSR.EnvLevel; + break; - case GSREG_V0_LOOP_ADDR: - ret = Voices[v].LoopAddr; - break; + case GSREG_V0_LOOP_ADDR: + ret = Voices[v].LoopAddr; + break; - case GSREG_V0_READ_ADDR: - ret = Voices[v].CurAddr; - break; - } - } - else switch(which) - { - case GSREG_SPUCONTROL: - ret = SPUControl; - break; + case GSREG_V0_READ_ADDR: + ret = Voices[v].CurAddr; + break; + } + } + else switch(which) + { + case GSREG_SPUCONTROL: + ret = SPUControl; + break; - case GSREG_FM_ON: - ret = FM_Mode; - break; + case GSREG_FM_ON: + ret = FM_Mode; + break; - case GSREG_NOISE_ON: - ret = Noise_Mode; - break; + case GSREG_NOISE_ON: + ret = Noise_Mode; + break; - case GSREG_REVERB_ON: - ret = Reverb_Mode; - break; + case GSREG_REVERB_ON: + ret = Reverb_Mode; + break; - case GSREG_CDVOL_L: - ret = (uint16)CDVol[0]; - break; + case GSREG_CDVOL_L: + ret = (uint16)CDVol[0]; + break; - case GSREG_CDVOL_R: - ret = (uint16)CDVol[1]; - break; + case GSREG_CDVOL_R: + ret = (uint16)CDVol[1]; + break; - case GSREG_DRYVOL_CTRL_L: - ret = Regs[0xC0]; - break; + case GSREG_DRYVOL_CTRL_L: + ret = Regs[0xC0]; + break; - case GSREG_DRYVOL_CTRL_R: - ret = Regs[0xC1]; - break; + case GSREG_DRYVOL_CTRL_R: + ret = Regs[0xC1]; + break; - case GSREG_DRYVOL_L: - ret = GlobalSweep[0].ReadVolume() & 0xFFFF; - break; + case GSREG_DRYVOL_L: + ret = GlobalSweep[0].ReadVolume() & 0xFFFF; + break; - case GSREG_DRYVOL_R: - ret = GlobalSweep[1].ReadVolume() & 0xFFFF; - break; + case GSREG_DRYVOL_R: + ret = GlobalSweep[1].ReadVolume() & 0xFFFF; + break; - case GSREG_WETVOL_L: - ret = (uint16)ReverbVol[0]; - break; + case GSREG_WETVOL_L: + ret = (uint16)ReverbVol[0]; + break; - case GSREG_WETVOL_R: - ret = (uint16)ReverbVol[1]; - break; + case GSREG_WETVOL_R: + ret = (uint16)ReverbVol[1]; + break; - case GSREG_RWADDR: - ret = RWAddr; - break; + case GSREG_RWADDR: + ret = RWAddr; + break; - case GSREG_IRQADDR: - ret = IRQAddr; - break; + case GSREG_IRQADDR: + ret = IRQAddr; + break; - case GSREG_REVERBWA: - ret = ReverbWA >> 2; - break; + case GSREG_REVERBWA: + ret = ReverbWA >> 2; + break; - case GSREG_VOICEON: - ret = VoiceOn; - break; + case GSREG_VOICEON: + ret = VoiceOn; + break; - case GSREG_VOICEOFF: - ret = VoiceOff; - break; + case GSREG_VOICEOFF: + ret = VoiceOff; + break; - case GSREG_BLOCKEND: - ret = BlockEnd; - break; + case GSREG_BLOCKEND: + ret = BlockEnd; + break; - case GSREG_FB_SRC_A ... GSREG_IN_COEF_R: - ret = ReverbRegs[which - GSREG_FB_SRC_A] & 0xFFFF; - break; - } + case GSREG_FB_SRC_A ... GSREG_IN_COEF_R: + ret = ReverbRegs[which - GSREG_FB_SRC_A] & 0xFFFF; + break; + } - return ret; + return(ret); } -void PS_SPU::SetRegister(unsigned int which, uint32_t value) +void PS_SPU::SetRegister(unsigned int which, uint32 value) { - switch(which) - { - case GSREG_SPUCONTROL: - SPUControl = value; - break; - case GSREG_FM_ON: - FM_Mode = value & 0xFFFFFF; - break; + switch(which) + { + case GSREG_SPUCONTROL: + SPUControl = value; + break; - case GSREG_NOISE_ON: - Noise_Mode = value & 0xFFFFFF; - break; + case GSREG_FM_ON: + FM_Mode = value & 0xFFFFFF; + break; - case GSREG_REVERB_ON: - Reverb_Mode = value & 0xFFFFFF; - break; + case GSREG_NOISE_ON: + Noise_Mode = value & 0xFFFFFF; + break; - case GSREG_CDVOL_L: - CDVol[0] = (int16)value; - break; + case GSREG_REVERB_ON: + Reverb_Mode = value & 0xFFFFFF; + break; - case GSREG_CDVOL_R: - CDVol[1] = (int16)value; - break; + case GSREG_CDVOL_L: + CDVol[0] = (int16)value; + break; - case GSREG_DRYVOL_CTRL_L: - Regs[0xC0] = value; - GlobalSweep[0].WriteControl(value); - //GlobalSweep[0].Control = value; - break; + case GSREG_CDVOL_R: + CDVol[1] = (int16)value; + break; - case GSREG_DRYVOL_CTRL_R: - Regs[0xC1] = value; - GlobalSweep[1].WriteControl(value); - //GlobalSweep[1].Control = value; - break; + case GSREG_DRYVOL_CTRL_L: + Regs[0xC0] = value; + GlobalSweep[0].WriteControl(value); + //GlobalSweep[0].Control = value; + break; - case GSREG_DRYVOL_L: - GlobalSweep[0].WriteVolume(value); - break; + case GSREG_DRYVOL_CTRL_R: + Regs[0xC1] = value; + GlobalSweep[1].WriteControl(value); + //GlobalSweep[1].Control = value; + break; - case GSREG_DRYVOL_R: - GlobalSweep[1].WriteVolume(value); - break; + case GSREG_DRYVOL_L: + GlobalSweep[0].WriteVolume(value); + break; - case GSREG_WETVOL_L: - ReverbVol[0] = (int16)value; - break; + case GSREG_DRYVOL_R: + GlobalSweep[1].WriteVolume(value); + break; - case GSREG_WETVOL_R: - ReverbVol[1] = (int16)value; - break; + case GSREG_WETVOL_L: + ReverbVol[0] = (int16)value; + break; - case GSREG_RWADDR: - RWAddr = value & 0x3FFFF; - break; + case GSREG_WETVOL_R: + ReverbVol[1] = (int16)value; + break; - case GSREG_IRQADDR: - IRQAddr = value & 0x3FFFC; - break; + case GSREG_RWADDR: + RWAddr = value & 0x3FFFF; + break; - // - // REVERB_WA - // + case GSREG_IRQADDR: + IRQAddr = value & 0x3FFFC; + break; - case GSREG_VOICEON: - VoiceOn = value & 0xFFFFFF; - break; + // + // REVERB_WA + // - case GSREG_VOICEOFF: - VoiceOff = value & 0xFFFFFF; - break; + case GSREG_VOICEON: + VoiceOn = value & 0xFFFFFF; + break; - case GSREG_BLOCKEND: - BlockEnd = value & 0xFFFFFF; - break; - } + case GSREG_VOICEOFF: + VoiceOff = value & 0xFFFFFF; + break; + + case GSREG_BLOCKEND: + BlockEnd = value & 0xFFFFFF; + break; + + + } } + + } diff --git a/mednafen/psx/spu.h b/mednafen/psx/spu.h index ba0d84b2..c1b294c7 100644 --- a/mednafen/psx/spu.h +++ b/mednafen/psx/spu.h @@ -58,22 +58,31 @@ class SPU_Sweep private: uint16_t Control; - int16 Current; + uint16_t Current; uint32_t Divider; }; struct SPU_Voice { - int32_t DecodeBuffer[32 + 4]; // + 4 so we don't have to do & 0x1F in our MAC - int32_t DecodeWritePos; + int16 DecodeBuffer[0x20]; + int16 DecodeM2; + int16 DecodeM1; + uint32 DecodePlayDelay; + uint32 DecodeWritePos; + uint32 DecodeReadPos; + uint32 DecodeAvail; + + bool IgnoreSampLA; + + uint8 DecodeShift; + uint8 DecodeWeight; uint8_t DecodeFlags; SPU_Sweep Sweep[2]; uint16_t Pitch; uint32_t CurPhase; - int32_t CurPhase_SD; // Offseted compared to CurPhase, used for triggering sample decode. uint32_t StartAddr; @@ -116,7 +125,7 @@ class PS_SPU void WriteSPURAM(uint32_t addr, uint16_t value); uint16_t ReadSPURAM(uint32_t addr); - void DecodeSamples(SPU_Voice *voice); + void RunDecoder(SPU_Voice *voice); void CacheEnvelope(SPU_Voice *voice); void ResetEnvelope(SPU_Voice *voice);