void MDFNSS_LoadSM(Stream *st, bool data_only) { if(MDFN_LIKELY(data_only)) { StateMem sm(st); MDFN_StateAction(&sm, MEDNAFEN_VERSION_NUMERIC, true); sm.ThrowDeferred(); } else { uint8 header[32]; uint32 width, height, preview_len; uint32 stateversion; uint32 total_len; int64 start_pos; bool svbe; start_pos = st->tell(); st->read(header, 32); if(memcmp(header, "MEDNAFENSVESTATE", 16) && memcmp(header, "MDFNSVST", 8)) throw MDFN_Error(0, _("Missing/Wrong save state header ID.")); stateversion = MDFN_de32lsb(header + 16); total_len = MDFN_de32lsb(header + 20) & 0x7FFFFFFF; svbe = MDFN_de32lsb(header + 20) & 0x80000000; width = MDFN_de32lsb(header + 24); height = MDFN_de32lsb(header + 28); preview_len = width * height * 3; if((int)stateversion < 0x900) // Ensuring that (int)stateversion is > 0 is the most important part. throw MDFN_Error(0, _("Invalid/Unsupported version in save state header.")); st->seek(preview_len, SEEK_CUR); // Skip preview StateMem sm(st, start_pos + total_len, svbe); MDFN_StateAction(&sm, stateversion, false); // Load state data. sm.ThrowDeferred(); st->seek(start_pos + total_len, SEEK_SET); // Seek to just beyond end of save state before returning. } }
INLINE void PS_CPU::WriteMemory(pscpu_timestamp_t ×tamp, uint32 address, uint32 value, bool DS24) { if(MDFN_LIKELY(!(CP0.SR & 0x10000))) { if(sizeof(T) == 1) PSX_MemWrite8(timestamp, address, value); else if(sizeof(T) == 2) PSX_MemWrite16(timestamp, address, value); else { if(DS24) PSX_MemWrite24(timestamp, address, value); else PSX_MemWrite32(timestamp, address, value); } } else { #if PS_CPU_EMULATE_ICACHE if(BIU & 0x800) // Instruction cache is enabled/active { if(BIU & 0x4) // TAG test mode. { // TODO: Respect written value. __ICache *ICI = &ICache[((address & 0xFF0) >> 2)]; const uint8 valid_bits = 0x00; ICI[0].TV = ((valid_bits & 0x01) ? 0x00 : 0x02) | ((BIU & 0x800) ? 0x0 : 0x1); ICI[1].TV = ((valid_bits & 0x02) ? 0x00 : 0x02) | ((BIU & 0x800) ? 0x0 : 0x1); ICI[2].TV = ((valid_bits & 0x04) ? 0x00 : 0x02) | ((BIU & 0x800) ? 0x0 : 0x1); ICI[3].TV = ((valid_bits & 0x08) ? 0x00 : 0x02) | ((BIU & 0x800) ? 0x0 : 0x1); } else if(!(BIU & 0x1)) { ICache[(address & 0xFFC) >> 2].Data = value << ((address & 0x3) * 8); }
// // Remember to handle an end condition on the same iteration of the while(DMACH[ch].ClockCounter > 0) loop that caused it, // otherwise RecalcHalt() might take the CPU out of a halted state before the end-of-DMA is signaled(especially a problem considering our largeish // DMA update timing granularity). // static INLINE void RunChannelI(const unsigned ch, const uint32_t CRModeCache, int32_t clocks) { //const uint32_t dc = (DMAControl >> (ch * 4)) & 0xF; DMACH[ch].ClockCounter += clocks; while(MDFN_LIKELY(DMACH[ch].ClockCounter > 0)) { if(DMACH[ch].WordCounter == 0) // Begin WordCounter reload. { if(!(DMACH[ch].ChanControl & (1 << 24))) // Needed for the forced-DMA-stop kludge(see DMA_Write()). break; if(!ChCan(ch, CRModeCache)) break; DMACH[ch].CurAddr = DMACH[ch].BaseAddr; if(CRModeCache & (1U << 10)) { uint32_t header; if(MDFN_UNLIKELY(DMACH[ch].CurAddr & 0x800000)) { DMACH[ch].ChanControl &= ~(0x11 << 24); DMAIntControl |= 0x8000; RecalcIRQOut(); break; } header = MainRAM.ReadU32(DMACH[ch].CurAddr & 0x1FFFFC); DMACH[ch].CurAddr = (DMACH[ch].CurAddr + 4) & 0xFFFFFF; DMACH[ch].WordCounter = header >> 24; DMACH[ch].BaseAddr = header & 0xFFFFFF; // printf to debug Soul Reaver ;) //if(DMACH[ch].WordCounter > 0x10) // printf("What the lala? 0x%02x @ 0x%08x\n", DMACH[ch].WordCounter, DMACH[ch].CurAddr - 4); if(DMACH[ch].WordCounter) DMACH[ch].ClockCounter -= 15; else DMACH[ch].ClockCounter -= 10; goto SkipPayloadStuff; // 3 cheers for gluten-free spaghetticode(necessary because the newly-loaded WordCounter might be 0, and we actually // want 0 to mean 0 and not 65536 in this context)! } else { DMACH[ch].WordCounter = DMACH[ch].BlockControl & 0xFFFF; if(CRModeCache & (1U << 9)) { if(ch == 2) // Technically should apply to all channels, but since we don't implement CPU read penalties for channels other than 2 yet, it's like this to avoid making DMA longer than what games can handle. DMACH[ch].ClockCounter -= 7; DMACH[ch].BlockControl = (DMACH[ch].BlockControl & 0xFFFF) | ((DMACH[ch].BlockControl - (1U << 16)) & 0xFFFF0000); } } } // End WordCounter reload.
// // When updating this function make sure to adhere to the guarantees in state.h. // bool MDFNSS_StateAction(StateMem *sm, const unsigned load, const bool data_only, SFORMAT *sf, const char *sname, const bool optional) noexcept { //printf("Section: %s %zu\n", sname, strlen(sname)); if(MDFN_UNLIKELY(sm->deferred_error)) { return(load ? false : true); } try { Stream* st = sm->st; if(MDFN_LIKELY(data_only)) // Not particularly likely, but it's more important to optimize for this code path... { static const uint8 SSFastCanary[8] = { 0x42, 0xA3, 0x10, 0x87, 0xBC, 0x6D, 0xF2, 0x79 }; char sname_canary[32 + 8]; if(load) { st->read(sname_canary, 32 + 8); if(strncmp(sname_canary, sname, 32)) throw MDFN_Error(0, _("Section name mismatch in state loading fast path.")); if(memcmp(sname_canary + 32, SSFastCanary, 8)) throw MDFN_Error(0, _("Section canary is a zombie AAAAAAAAAAGH!")); FastRWChunk<true>(st, sf); } else { memset(sname_canary, 0, sizeof(sname_canary)); strncpy(sname_canary, sname, 32); memcpy(sname_canary + 32, SSFastCanary, 8); st->write(sname_canary, 32 + 8); FastRWChunk<false>(st, sf); } } else { if(load) { char sname_tmp[32]; bool found = false; uint32 tmp_size; uint32 total = 0; while(st->tell() < sm->sss_bound) { st->read(sname_tmp, 32); tmp_size = st->get_LE<uint32>(); total += tmp_size + 32 + 4; // Yay, we found the section if(!strncmp(sname_tmp, sname, 32)) { ReadStateChunk(st, sf, tmp_size, sm->svbe); found = true; break; } else { st->seek(tmp_size, SEEK_CUR); } } st->seek(-(int64)total, SEEK_CUR); if(!found) { if(optional) { printf("Missing optional section: %.32s\n", sname); return(false); } else throw MDFN_Error(0, _("Section missing: %.32s"), sname); } } else { int64 data_start_pos; int64 end_pos; uint8 sname_tmp[32]; memset(sname_tmp, 0, sizeof(sname_tmp)); strncpy((char *)sname_tmp, sname, 32); if(strlen(sname) > 32) printf("Warning: section name is too long: %s\n", sname); st->write(sname_tmp, 32); st->put_LE<uint32>(0); // We'll come back and write this later. data_start_pos = st->tell(); SubWrite(st, sf); end_pos = st->tell(); st->seek(data_start_pos - 4, SEEK_SET); st->put_LE<uint32>(end_pos - data_start_pos); st->seek(end_pos, SEEK_SET); } } } catch(...) { sm->deferred_error = std::current_exception(); return(load ? false : true); } return(true); }
sscpu_timestamp_t Update(sscpu_timestamp_t timestamp) { if(MDFN_UNLIKELY(timestamp < lastts)) { // Don't else { } normal execution, since this bug condition miiight occur in the call from SetHBVB(), // and we need drawing to start ASAP before silly games overwrite the beginning of the command table. // SS_DBGTI(SS_DBG_WARNING | SS_DBG_VDP1, "[VDP1] [BUG] timestamp(%d) < lastts(%d)", timestamp, lastts); timestamp = lastts; } // // // int32 cycles = timestamp - lastts; lastts = timestamp; CycleCounter += cycles; if(CycleCounter > VDP1_UpdateTimingGran) CycleCounter = VDP1_UpdateTimingGran; if(CycleCounter > 0 && SCU_CheckVDP1HaltKludge()) { //puts("Kludge"); CycleCounter = 0; } else if(DrawingActive) { while(CycleCounter > 0) { uint16 cmd_data[0x10]; // Fetch command data memcpy(cmd_data, &VRAM[CurCommandAddr], sizeof(cmd_data)); CycleCounter -= 16; //SS_DBGTI(SS_DBG_WARNING | SS_DBG_VDP1, "[VDP1] Command @ 0x%06x: 0x%04x\n", CurCommandAddr, cmd_data[0]); if(MDFN_LIKELY(!(cmd_data[0] & 0xC000))) { const unsigned cc = cmd_data[0] & 0xF; if(MDFN_UNLIKELY(cc >= 0xC)) { DrawingActive = false; break; } else { static int32 (*const command_table[0xC])(const uint16* cmd_data) = { /* 0x0 */ /* 0x1 */ /* 0x2 */ /* 0x3 */ CMD_NormalSprite, CMD_ScaledSprite, CMD_DistortedSprite, CMD_DistortedSprite, /* 0x4 */ /* 0x5 */ /* 0x6 */ /* 0x7 */ CMD_Polygon, CMD_Polyline, CMD_Line, CMD_Polyline, /* 0x8*/ /* 0x9 */ /* 0xA */ /* 0xB */ CMD_SetUserClip, CMD_SetSystemClip, CMD_SetLocalCoord, CMD_SetUserClip }; CycleCounter -= command_table[cc](cmd_data); } } else if(MDFN_UNLIKELY(cmd_data[0] & 0x8000)) { SS_DBGTI(SS_DBG_VDP1, "[VDP1] Drawing finished at 0x%05x", CurCommandAddr); DrawingActive = false; EDSR |= 0x2; // TODO: Does EDSR reflect IRQ out status? SCU_SetInt(SCU_INT_VDP1, true); SCU_SetInt(SCU_INT_VDP1, false); break; } CurCommandAddr = (CurCommandAddr + 0x10) & 0x3FFFF; switch((cmd_data[0] >> 12) & 0x3) { case 0: break; case 1: CurCommandAddr = (cmd_data[1] << 2) &~ 0xF; break; case 2: if(RetCommandAddr < 0) RetCommandAddr = CurCommandAddr; CurCommandAddr = (cmd_data[1] << 2) &~ 0xF; break; case 3: if(RetCommandAddr >= 0) { CurCommandAddr = RetCommandAddr; RetCommandAddr = -1; } break; } } } return timestamp + (DrawingActive ? std::max<int32>(VDP1_UpdateTimingGran, 0 - CycleCounter) : VDP1_IdleTimingGran); }