void PCFXDBG_CheckBP(int type, uint32 address, uint32 value, unsigned int len) { std::vector<PCFX_BPOINT>::iterator bpit, bpit_end; if(type == BPOINT_READ) { bpit = BreakPointsRead.begin(); bpit_end = BreakPointsRead.end(); if(MDFN_UNLIKELY(address >= 0xA4000000 && address <= 0xABFFFFFF)) { VDC_SimulateResult result; unsigned which_vdc = (bool)(address & 0x8000000); fx_vdc_chips[which_vdc]->SimulateRead16(1, &result); if(result.ReadCount) PCFXDBG_CheckBP(BPOINT_AUX_READ, 0x80000 | (which_vdc << 16) | result.ReadStart, 0, result.ReadCount); if(result.WriteCount) PCFXDBG_CheckBP(BPOINT_AUX_WRITE, 0x80000 | (which_vdc << 16) | result.WriteStart, 0/*FIXME(HOW? :b)*/, result.WriteCount); } } else if(type == BPOINT_WRITE) { bpit = BreakPointsWrite.begin(); bpit_end = BreakPointsWrite.end(); if(MDFN_UNLIKELY(address >= 0xB4000000 && address <= 0xBBFFFFFF)) { VDC_SimulateResult result; unsigned which_vdc = (bool)(address & 0x8000000); fx_vdc_chips[which_vdc]->SimulateWrite16(1, value, &result); if(result.ReadCount) PCFXDBG_CheckBP(BPOINT_AUX_READ, 0x80000 | (which_vdc << 16) | result.ReadStart, 0, result.ReadCount); if(result.WriteCount) PCFXDBG_CheckBP(BPOINT_AUX_WRITE, 0x80000 | (which_vdc << 16) | result.WriteStart, 0/*FIXME(HOW? :b)*/, result.WriteCount); } } else if(type == BPOINT_IO_READ) { bpit = BreakPointsIORead.begin(); bpit_end = BreakPointsIORead.end(); if(address >= 0x400 && address <= 0x5FF) { VDC_SimulateResult result; unsigned which_vdc = (bool)(address & 0x100); fx_vdc_chips[which_vdc]->SimulateRead16((bool)(address & 4), &result); if(result.ReadCount) PCFXDBG_CheckBP(BPOINT_AUX_READ, 0x80000 | (which_vdc << 16) | result.ReadStart, 0, result.ReadCount); if(result.WriteCount) PCFXDBG_CheckBP(BPOINT_AUX_WRITE, 0x80000 | (which_vdc << 16) | result.WriteStart, 0/*FIXME(HOW? :b)*/, result.WriteCount); } } else if(type == BPOINT_IO_WRITE) { bpit = BreakPointsIOWrite.begin(); bpit_end = BreakPointsIOWrite.end(); if(address >= 0x400 && address <= 0x5FF) { VDC_SimulateResult result; unsigned which_vdc = (bool)(address & 0x100); fx_vdc_chips[which_vdc]->SimulateWrite16((bool)(address & 4), value, &result); if(result.ReadCount) PCFXDBG_CheckBP(BPOINT_AUX_READ, 0x80000 | (which_vdc << 16) | result.ReadStart, 0, result.ReadCount); if(result.WriteCount) PCFXDBG_CheckBP(BPOINT_AUX_WRITE, 0x80000 | (which_vdc << 16) | result.WriteStart, 0/*FIXME(HOW? :b)*/, result.WriteCount); } } else if(type == BPOINT_AUX_READ) { bpit = BreakPointsAux0Read.begin(); bpit_end = BreakPointsAux0Read.end(); } else if(type == BPOINT_AUX_WRITE) { bpit = BreakPointsAux0Write.begin(); bpit_end = BreakPointsAux0Write.end(); } else return; for(; bpit != bpit_end; bpit++) { uint32 tmp_address = address; uint32 tmp_len = len; while(tmp_len--) { if(tmp_address >= bpit->A[0] && tmp_address <= bpit->A[1]) { FoundBPoint = TRUE; break; } tmp_address++; } } }
static void ValidateSetting(const char *value, const MDFNSetting *setting) { MDFNSettingType base_type = setting->type; if(base_type == MDFNST_UINT) { unsigned long long ullvalue; if(!TranslateSettingValueUI(value, ullvalue)) { throw MDFN_Error(0, _("Setting \"%s\", value \"%s\", is not set to a valid unsigned integer."), setting->name, value); } if(setting->minimum) { unsigned long long minimum; TranslateSettingValueUI(setting->minimum, minimum); if(ullvalue < minimum) { throw MDFN_Error(0, _("Setting \"%s\" is set too small(\"%s\"); the minimum acceptable value is \"%s\"."), setting->name, value, setting->minimum); } } if(setting->maximum) { unsigned long long maximum; TranslateSettingValueUI(setting->maximum, maximum); if(ullvalue > maximum) { throw MDFN_Error(0, _("Setting \"%s\" is set too large(\"%s\"); the maximum acceptable value is \"%s\"."), setting->name, value, setting->maximum); } } } else if(base_type == MDFNST_INT) { long long llvalue; if(!TranslateSettingValueI(value, llvalue)) { throw MDFN_Error(0, _("Setting \"%s\", value \"%s\", is not set to a valid signed integer."), setting->name, value); } if(setting->minimum) { long long minimum; TranslateSettingValueI(setting->minimum, minimum); if(llvalue < minimum) { throw MDFN_Error(0, _("Setting \"%s\" is set too small(\"%s\"); the minimum acceptable value is \"%s\"."), setting->name, value, setting->minimum); } } if(setting->maximum) { long long maximum; TranslateSettingValueI(setting->maximum, maximum); if(llvalue > maximum) { throw MDFN_Error(0, _("Setting \"%s\" is set too large(\"%s\"); the maximum acceptable value is \"%s\"."), setting->name, value, setting->maximum); } } } else if(base_type == MDFNST_FLOAT) { double dvalue; if(!MR_StringToDouble(value, &dvalue)) { throw MDFN_Error(0, _("Setting \"%s\", value \"%s\", is not set to a floating-point(real) number."), setting->name, value); } if(std::isnan(dvalue)) throw MDFN_Error(0, _("Setting \"%s\", value \"%s\", is NaN!"), setting->name, value); if(setting->minimum) { double minimum; if(MDFN_UNLIKELY(!MR_StringToDouble(setting->minimum, &minimum))) throw MDFN_Error(0, _("Minimum value, \"%f\", for setting \"%s\" is not set to a floating-point(real) number."), minimum, setting->name); if(MDFN_UNLIKELY(dvalue < minimum)) throw MDFN_Error(0, _("Setting \"%s\" is set too small(\"%s\"); the minimum acceptable value is \"%s\"."), setting->name, value, setting->minimum); } if(setting->maximum) { double maximum; if(MDFN_UNLIKELY(!MR_StringToDouble(setting->maximum, &maximum))) throw MDFN_Error(0, _("Maximum value, \"%f\", for setting \"%s\" is not set to a floating-point(real) number."), maximum, setting->name); if(MDFN_UNLIKELY(dvalue > maximum)) throw MDFN_Error(0, _("Setting \"%s\" is set too large(\"%s\"); the maximum acceptable value is \"%s\"."), setting->name, value, setting->maximum); } } else if(base_type == MDFNST_BOOL) { if(strlen(value) != 1 || (value[0] != '0' && value[0] != '1')) { throw MDFN_Error(0, _("Setting \"%s\", value \"%s\", is not a valid boolean value."), setting->name, value); } } else if(base_type == MDFNST_ENUM) { const MDFNSetting_EnumList *enum_list = setting->enum_list; bool found = false; std::string valid_string_list; assert(enum_list); while(enum_list->string) { if(!MDFN_strazicmp(value, enum_list->string)) { found = true; break; } if(enum_list->description) // Don't list out undocumented and deprecated values. valid_string_list = valid_string_list + (enum_list == setting->enum_list ? "" : " ") + std::string(enum_list->string); enum_list++; } if(!found) { throw MDFN_Error(0, _("Setting \"%s\", value \"%s\", is not a recognized string. Recognized strings: %s"), setting->name, value, valid_string_list.c_str()); } } else if(base_type == MDFNST_MULTI_ENUM) { std::vector<std::string> mel = MDFN_strsplit(value); assert(setting->enum_list); for(auto& mee : mel) { bool found = false; const MDFNSetting_EnumList* enum_list = setting->enum_list; MDFN_trim(&mee); while(enum_list->string) { if(!MDFN_strazicmp(mee.c_str(), enum_list->string)) { found = true; break; } enum_list++; } if(!found) { std::string valid_string_list; enum_list = setting->enum_list; while(enum_list->string) { if(enum_list->description) // Don't list out undocumented and deprecated values. valid_string_list = valid_string_list + (enum_list == setting->enum_list ? "" : " ") + std::string(enum_list->string); enum_list++; } throw MDFN_Error(0, _("Setting \"%s\", value \"%s\" component \"%s\", is not a recognized string. Recognized strings: %s"), setting->name, value, mee.c_str(), valid_string_list.c_str()); } } } if(setting->validate_func && !setting->validate_func(setting->name, value)) { if(base_type == MDFNST_STRING) throw MDFN_Error(0, _("Setting \"%s\" is not set to a valid string: \"%s\""), setting->name, value); else throw MDFN_Error(0, _("Setting \"%s\" is not set to a valid unsigned integer: \"%s\""), setting->name, value); } }
// // 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); }
// // 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.
void SetHBVB(const sscpu_timestamp_t event_timestamp, const bool new_hb_status, const bool new_vb_status) { const bool old_hb_status = hb_status; const bool old_vb_status = vb_status; hb_status = new_hb_status; vb_status = new_vb_status; if(MDFN_UNLIKELY(vbcdpending & hb_status & (old_hb_status ^ hb_status))) { vbcdpending = false; if(vb_status) // Going into v-blank { // // v-blank erase // if((TVMR & TVMR_VBE) || FBVBErasePending) { SS_DBGTI(SS_DBG_VDP1, "[VDP1] VB erase start of framebuffer %d.", !FBDrawWhich); FBVBErasePending = false; FBVBEraseActive = true; FBVBEraseLastTS = event_timestamp; } } else // Leaving v-blank { // Run vblank erase at end of vblank all at once(not strictly accurate, but should only have visible side effects wrt the debugger and reset). if(FBVBEraseActive) { int32 count = event_timestamp - FBVBEraseLastTS; //printf("%d %d, %d\n", event_timestamp, FBVBEraseLastTS, count); // // // uint32 y = EraseParams.y_start; do { uint16* fbyptr; uint32 x = EraseParams.x_start; fbyptr = &FB[!FBDrawWhich][(y & 0xFF) << 9]; if(EraseParams.rot8) fbyptr += (y & 0x100); count -= 8; do { for(unsigned sub = 0; sub < 8; sub++) { //printf("%d %d:%d %04x\n", FBDrawWhich, x, y, fill_data); //printf("%lld\n", &fbyptr[x & fb_x_mask] - FB[!FBDrawWhich]); fbyptr[x & EraseParams.fb_x_mask] = EraseParams.fill_data; x++; } count -= 8; if(MDFN_UNLIKELY(count <= 0)) { SS_DBGTI(SS_DBG_WARNING | SS_DBG_VDP1, "[VDP1] VB erase of framebuffer %d ran out of time.", !FBDrawWhich); goto AbortVBErase; } } while(x < EraseParams.x_bound); } while(++y <= EraseParams.y_end); AbortVBErase:; // FBVBEraseActive = false; } // // // // if(!(FBCR & FBCR_FCM) || (FBManualPending && (FBCR & FBCR_FCT))) // Swap framebuffers { if(DrawingActive) { SS_DBGTI(SS_DBG_WARNING | SS_DBG_VDP1, "[VDP1] Drawing aborted by framebuffer swap."); DrawingActive = false; } FBDrawWhich = !FBDrawWhich; SS_DBGTI(SS_DBG_VDP1, "[VDP1] Displayed framebuffer changed to %d.", !FBDrawWhich); // On fb swap, copy CEF to BEF, clear CEF, and copy COPR to LOPR. EDSR = EDSR >> 1; LOPR = CurCommandAddr >> 2; // EraseParams.rot8 = (TVMR & (TVMR_8BPP | TVMR_ROTATE)) == (TVMR_8BPP | TVMR_ROTATE); EraseParams.fb_x_mask = EraseParams.rot8 ? 0xFF : 0x1FF; EraseParams.y_start = EWLR & 0x1FF; EraseParams.x_start = ((EWLR >> 9) & 0x3F) << 3; EraseParams.y_end = EWRR & 0x1FF; EraseParams.x_bound = ((EWRR >> 9) & 0x7F) << 3; EraseParams.fill_data = EWDR; // if(PTMR & 0x2) // Start drawing(but only if we swapped the frame) { StartDrawing(); SS_SetEventNT(&events[SS_EVENT_VDP1], Update(event_timestamp)); } } if(!(FBCR & FBCR_FCM) || (FBManualPending && !(FBCR & FBCR_FCT))) { if(TVMR & TVMR_ROTATE) { EraseYCounter = ~0U; FBVBErasePending = true; } else { EraseYCounter = EraseParams.y_start; } } FBManualPending = false; } }
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); }