diff --git a/vr4300/cp0.c b/vr4300/cp0.c index e8ff83ba1..41e0cb6b0 100644 --- a/vr4300/cp0.c +++ b/vr4300/cp0.c @@ -146,6 +146,7 @@ int VR4300_ERET(struct vr4300 *vr4300, pipeline->fault_present = true; vr4300->regs[VR4300_CP0_REGISTER_STATUS] = status; + vr4300->regs[VR4300_REGISTER_LLBIT] = 0; pipeline->icrf_latch.segment = get_segment(icrf_latch->pc, status); pipeline->exdc_latch.segment = get_default_segment(); diff --git a/vr4300/cpu.h b/vr4300/cpu.h index 53b73b540..6a1cf7bd2 100644 --- a/vr4300/cpu.h +++ b/vr4300/cpu.h @@ -64,8 +64,9 @@ enum vr4300_register { VR4300_REGISTER_CP1_27, VR4300_REGISTER_CP1_28, VR4300_REGISTER_CP1_29, VR4300_REGISTER_CP1_30, VR4300_REGISTER_CP1_31, - // Miscellanious registers. + // Miscellaneous registers. VR4300_REGISTER_HI, VR4300_REGISTER_LO, + VR4300_REGISTER_LLBIT, VR4300_CP1_FCR0, VR4300_CP1_FCR31, // Pipeline cycle type flag. diff --git a/vr4300/fault.c b/vr4300/fault.c index 8725bfc50..82f4d8f05 100644 --- a/vr4300/fault.c +++ b/vr4300/fault.c @@ -249,7 +249,8 @@ void VR4300_DCM(struct vr4300 *vr4300) { VR4300_ACCESS_DWORD ? 0x7 : 0x3; // Service a read. - if (exdc_latch->request.type == VR4300_BUS_REQUEST_READ) { + if (exdc_latch->request.type == VR4300_BUS_REQUEST_READ || + exdc_latch->request.type == VR4300_BUS_REQUEST_READ_CONDITIONAL) { unsigned rshiftamt = (8 - request->size) << 3; unsigned lshiftamt = (paddr & mask) << 3; uint32_t hiword, loword; @@ -328,7 +329,8 @@ void VR4300_DTLB(struct vr4300 *vr4300, unsigned miss, unsigned inv, unsigned mo // TLB miss/invalid exceptions are either TLBL or TLBS. if (miss | inv) - type = (exdc_latch->request.type == VR4300_BUS_REQUEST_WRITE) ? 0x3: 0x2; + type = (exdc_latch->request.type == VR4300_BUS_REQUEST_WRITE || + exdc_latch->request.type == VR4300_BUS_REQUEST_WRITE_CONDITIONAL) ? 0x3: 0x2; // OTOH, TLB modification exceptions are TLBM. else diff --git a/vr4300/functions.c b/vr4300/functions.c index c6141f089..c70546f04 100644 --- a/vr4300/functions.c +++ b/vr4300/functions.c @@ -482,6 +482,9 @@ cen64_cold static int vr4300_cacheop_dc_wb_invalidate( bus_write_word(vr4300->bus, bus_address + i * 4, data[i ^ (WORD_ADDR_XOR >> 2)], ~0); + if (vr4300->regs[VR4300_CP0_REGISTER_LLADDR] == paddr >> 4) + vr4300->regs[VR4300_CP0_REGISTER_LLADDR] = 0; + line->metadata &= ~0x2; return DCACHE_ACCESS_DELAY; } @@ -543,6 +546,9 @@ cen64_cold static int vr4300_cacheop_dc_hit_wb_invalidate( bus_write_word(vr4300->bus, bus_address + i * 4, data[i ^ (WORD_ADDR_XOR >> 2)], ~0); + if (vr4300->regs[VR4300_CP0_REGISTER_LLADDR] == paddr >> 4) + vr4300->regs[VR4300_CP0_REGISTER_LLADDR] = 0; + line->metadata &= ~0x1; return DCACHE_ACCESS_DELAY; } @@ -570,6 +576,9 @@ cen64_cold static int vr4300_cacheop_dc_hit_wb( bus_write_word(vr4300->bus, bus_address + i * 4, data[i ^ (WORD_ADDR_XOR >> 2)], ~0); + if (vr4300->regs[VR4300_CP0_REGISTER_LLADDR] == paddr >> 4) + vr4300->regs[VR4300_CP0_REGISTER_LLADDR] = 0; + // TODO: Technically, it's clean now... line->metadata &= ~0x2; return DCACHE_ACCESS_DELAY; @@ -1125,6 +1134,69 @@ cen64_hot int VR4300_LOAD_STORE(struct vr4300 *vr4300, return 0; } +// +// LL +// SC +// +// TODO/FIXME: Check for unaligned addresses. +// +int VR4300_LOAD_STORE_CONDITIONAL(struct vr4300 *vr4300, + uint32_t iw, uint64_t rs, uint64_t rt) { + struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch; + const uint64_t sel_mask = (int64_t) (int32_t) (iw << 2) >> 32; // 0 for load, -1 for store + + const uint64_t address = rs + (int64_t) (int16_t) iw; + const uint64_t dqm = ~0ULL & ~sel_mask; // sign-extend + + exdc_latch->request.vaddr = address & ~0x3; + exdc_latch->request.data = dqm | (sel_mask & rt); + exdc_latch->request.wdqm = (uint32_t) sel_mask; + exdc_latch->request.postshift = 0; + exdc_latch->request.access_type = VR4300_ACCESS_WORD; + exdc_latch->request.type = sel_mask ? + VR4300_BUS_REQUEST_WRITE_CONDITIONAL : + VR4300_BUS_REQUEST_READ_CONDITIONAL; + exdc_latch->request.size = 4; + + exdc_latch->dest = ~sel_mask & GET_RT(iw); + exdc_latch->result = 0; + + if (sel_mask) + exdc_latch->request.sc_reg = GET_RT(iw); + + return 0; +} + +// +// LLD +// SCD +// +// TODO/FIXME: Check for unaligned addresses. +// +int VR4300_LD_SD_CONDITIONAL(struct vr4300 *vr4300, + uint32_t iw, uint64_t rs, uint64_t rt) { + struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch; + uint64_t sel_mask = (int64_t) (int32_t) (iw << 2) >> 32; + + exdc_latch->request.vaddr = rs + (int64_t) (int16_t) iw; + exdc_latch->request.data = ~sel_mask | (sel_mask & rt); + exdc_latch->request.wdqm = sel_mask; + exdc_latch->request.postshift = 0; + exdc_latch->request.access_type = VR4300_ACCESS_DWORD; + exdc_latch->request.type = sel_mask ? + VR4300_BUS_REQUEST_WRITE_CONDITIONAL : + VR4300_BUS_REQUEST_READ_CONDITIONAL; + exdc_latch->request.size = 8; + + exdc_latch->dest = ~sel_mask & GET_RT(iw); + exdc_latch->result = 0; + + if (sel_mask) + exdc_latch->request.sc_reg = GET_RT(iw); + + return 0; +} + // // LDL // LDR diff --git a/vr4300/opcodes_priv.h b/vr4300/opcodes_priv.h index f49582f6e..30d6e1f1c 100644 --- a/vr4300/opcodes_priv.h +++ b/vr4300/opcodes_priv.h @@ -85,8 +85,8 @@ #define LDR VR4300_BUILD_OP(LDR, LDL_LDR, INFO3(NEEDRS, NEEDRT, LOAD)) #define LH VR4300_BUILD_OP(LH, LOAD_STORE, INFO2(NEEDRS, LOAD)) #define LHU VR4300_BUILD_OP(LHU, LOAD_STORE, INFO2(NEEDRS, LOAD)) -#define LL VR4300_BUILD_OP(LL, INVALID, INFO1(NONE)) -#define LLD VR4300_BUILD_OP(LLD, INVALID, INFO1(NONE)) +#define LL VR4300_BUILD_OP(LL, LOAD_STORE_CONDITIONAL, INFO2(NEEDRS, LOAD)) +#define LLD VR4300_BUILD_OP(LLD, LD_SD_CONDITIONAL, INFO2(NEEDRS, LOAD)) #define LUI VR4300_BUILD_OP(LUI, ADDIU_LUI_SUBIU, INFO1(NONE)) #define LW VR4300_BUILD_OP(LW, LOAD_STORE, INFO2(NEEDRS, LOAD)) #define LWL VR4300_BUILD_OP(LWL, LWL_LWR, INFO3(NEEDRS, NEEDRT, LOAD)) @@ -102,8 +102,8 @@ #define OR VR4300_BUILD_OP(OR, AND_OR_XOR, INFO2(NEEDRS, NEEDRT)) #define ORI VR4300_BUILD_OP(ORI, ANDI_ORI_XORI, INFO1(NEEDRS)) #define SB VR4300_BUILD_OP(SB, LOAD_STORE, INFO3(NEEDRS, NEEDRT, STORE)) -#define SC VR4300_BUILD_OP(SC, INVALID, INFO1(NONE)) -#define SCD VR4300_BUILD_OP(SCD, INVALID, INFO1(NONE)) +#define SC VR4300_BUILD_OP(SC, LOAD_STORE_CONDITIONAL, INFO3(NEEDRS, NEEDRT, STORE)) +#define SCD VR4300_BUILD_OP(SCD, LD_SD_CONDITIONAL, INFO3(NEEDRS, NEEDRT, STORE)) #define SD VR4300_BUILD_OP(SD, LD_SD, INFO3(NEEDRS, NEEDRT, STORE)) #define SDL VR4300_BUILD_OP(SDL, SDL_SDR, INFO3(NEEDRS, NEEDRT, STORE)) #define SDR VR4300_BUILD_OP(SDR, SDL_SDR, INFO3(NEEDRS, NEEDRT, STORE)) diff --git a/vr4300/pipeline.c b/vr4300/pipeline.c index e08548598..22bb45f88 100644 --- a/vr4300/pipeline.c +++ b/vr4300/pipeline.c @@ -361,6 +361,102 @@ static int vr4300_dc_stage(struct vr4300 *vr4300) { line->metadata |= exdc_latch->request.type; } + else if (unlikely(exdc_latch->request.type > VR4300_BUS_REQUEST_CACHE_WRITE)) { + // ll/lld/sc/scd + uint64_t dword, rtemp, wtemp, wdqm; + unsigned shiftamt, rshiftamt, lshiftamt; + uint32_t s_paddr; + + if (exdc_latch->request.type == VR4300_BUS_REQUEST_READ_CONDITIONAL) { + vr4300->regs[VR4300_REGISTER_LLBIT] = 1; + vr4300->regs[VR4300_CP0_REGISTER_LLADDR] = paddr >> 4; + } else { + if (!vr4300->regs[VR4300_REGISTER_LLBIT] || + vr4300->regs[VR4300_CP0_REGISTER_LLADDR] != paddr >> 4) { + // Fail the store, set the reg to 0, write nothing to memory + exdc_latch->request.type = VR4300_BUS_REQUEST_NONE; + vr4300->regs[exdc_latch->request.sc_reg] = 0; + return 0; // fail is not an exception + } else { + // Successful store, set the reg to 1 + vr4300->regs[exdc_latch->request.sc_reg] = 1; + } + } + + line = vr4300_dcache_probe(&vr4300->dcache, vaddr, paddr); + + if (cached) { + bool last_cache_was_store = dcwb_latch->last_op_was_cache_store; + dcwb_latch->last_op_was_cache_store = (exdc_latch->request.type == + VR4300_BUS_REQUEST_WRITE_CONDITIONAL); + + if (!line) { + request->paddr = paddr; + exdc_latch->cached = cached; + + if (vr4300->profile_samples) { + uint32_t idx = exdc_latch->common.pc - 0x80000000; + idx &= (8 * 1024 * 1024) - 1; + vr4300->profile_samples[idx + (8 * 1024 * 1024)]++; + } + + // Miss: stall for one cycle, then move to the DCM phase. + vr4300->pipeline.cycles_to_stall = 0; + vr4300->regs[PIPELINE_CYCLE_TYPE] = 6; + return 1; + } + + // Cached store-to-cached operation will result in a DCB. + if (last_cache_was_store) { + VR4300_DCB(vr4300); + return 1; + } + } + + else { + dcwb_latch->last_op_was_cache_store = false; + + request->paddr = paddr; + exdc_latch->cached = cached; + + if (vr4300->profile_samples) { + uint32_t idx = exdc_latch->common.pc - 0x80000000; + idx &= (8 * 1024 * 1024) - 1; + vr4300->profile_samples[idx + (8 * 1024 * 1024)]++; + } + + VR4300_DCM(vr4300); + return 1; + } + + s_paddr = paddr << 3; + paddr &= 0x8; + + // Pull out the cache line data, mux stuff around + // to produce the output/update the cache line. + memcpy(&dword, line->data + paddr, sizeof(dword)); + + shiftamt = (s_paddr ^ (WORD_ADDR_XOR << 3)) & request->access_type; + rshiftamt = (8 - request->size) << 3; + lshiftamt = (s_paddr & (0x7 << 3)); + + wdqm = request->wdqm << shiftamt; + wtemp = (request->data << shiftamt) & wdqm; + rtemp = ((int64_t) (dword << lshiftamt) + >> rshiftamt) & request->data; + + dword = (dword & ~wdqm) | wtemp; + dcwb_latch->result |= rtemp << request->postshift; + memcpy(line->data + paddr, &dword, sizeof(dword)); + + // We need to mark the line dirty if it's write. + // Metadata & 0x2 == dirty, and metadata 0x1 == valid. Map to those. + if (request->type == VR4300_BUS_REQUEST_READ_CONDITIONAL) + line->metadata |= 1; + else + line->metadata |= 2; + } + // Not a load/store, so execute cache operation. else { unsigned delay; diff --git a/vr4300/pipeline.h b/vr4300/pipeline.h index bed40fcd7..8974b9b2b 100644 --- a/vr4300/pipeline.h +++ b/vr4300/pipeline.h @@ -27,6 +27,8 @@ enum vr4300_bus_request_type { VR4300_BUS_REQUEST_CACHE = 4, VR4300_BUS_REQUEST_CACHE_IDX = 4, VR4300_BUS_REQUEST_CACHE_WRITE = 5, + VR4300_BUS_REQUEST_READ_CONDITIONAL, + VR4300_BUS_REQUEST_WRITE_CONDITIONAL, }; enum vr4300_access_type { @@ -45,6 +47,8 @@ struct vr4300_bus_request { enum vr4300_access_type access_type; enum vr4300_bus_request_type type; unsigned size, postshift; + + unsigned char sc_reg; }; struct vr4300_latch {