cen64/rsp/cp0.h
Giovanni Bajo 87ebca00b5 Fix a few pipelining bugs with RSP
1) Setting SP_PC was not resetting the pipeline. This caused that
changing the PC within a HALT/UNHALT sequence was still causing
previous instructions in the pipeline (at the old address) to be
executed. This is not how the hardware works: SP_PC is immediate and
discards the whole pipeline.

2) BREAK did not correctly halt the processor at the right instruction,
which in turn caused resumption after HALT to execute the wrong
set of instructions. This was caused by the fact that the SP_STATUS
change was written into the EXDF latch, which in turn takes 3 cycles
to reach completion. Instead, we now use the DFWB latch, and we cause
it to abort the RSP cycle if the processor is halted. This happens
at the beginning of next cycle, which is the correct moment.

2bis) Since we are at it, use rsp_status_write to modify the RSP in
this case, rather than a direct write to the register. This change
fixes a race condition: SP_STATUS must be accessed atomically when
cen64 runs in multithreaded mode. To use rsp_status_write, we need
to introduce a nonexisting SP_SET_BROKE bit: we use the MSB, but then
mask it out in MTC0 to avoid some code to inadvertently have that bit
set.

3) When unhalting after BREAK, it's important to keep the correct
PC which comes from the EX stage (the one that was going to be
executed if BREAK didn't occur). Before, it was using the IF PC (fetch)
which is farther in the future.

Fixes #155
2021-12-17 00:23:47 +01:00

95 lines
3.2 KiB
C

//
// rsp/cp0.c: RSP control coprocessor.
//
// CEN64: Cycle-Accurate Nintendo 64 Emulator.
// Copyright (C) 2015, Tyler J. Stachecki.
//
// This file is subject to the terms and conditions defined in
// 'LICENSE', which is part of this source code package.
//
#ifndef __rsp_cp0_h__
#define __rsp_cp0_h__
#include "common.h"
// SP_STATUS read bits.
#define SP_STATUS_HALT 0x0001
#define SP_STATUS_BROKE 0x0002
#define SP_STATUS_DMA_BUSY 0x0004
#define SP_STATUS_DMA_FULL 0x0008
#define SP_STATUS_IO_FULL 0x0010
#define SP_STATUS_SSTEP 0x0020
#define SP_STATUS_INTR_BREAK 0x0040
#define SP_STATUS_SIG0 0x0080
#define SP_STATUS_SIG1 0x0100
#define SP_STATUS_SIG2 0x0200
#define SP_STATUS_SIG3 0x0400
#define SP_STATUS_SIG4 0x0800
#define SP_STATUS_SIG5 0x1000
#define SP_STATUS_SIG6 0x2000
#define SP_STATUS_SIG7 0x4000
// SP_STATUS write bits.
#define SP_CLR_HALT 0x00000001
#define SP_SET_HALT 0x00000002
#define SP_CLR_BROKE 0x00000004
#define SP_CLR_INTR 0x00000008
#define SP_SET_INTR 0x00000010
#define SP_CLR_SSTEP 0x00000020
#define SP_SET_SSTEP 0x00000040
#define SP_CLR_INTR_BREAK 0x00000080
#define SP_SET_INTR_BREAK 0x00000100
#define SP_CLR_SIG0 0x00000200
#define SP_SET_SIG0 0x00000400
#define SP_CLR_SIG1 0x00000800
#define SP_SET_SIG1 0x00001000
#define SP_CLR_SIG2 0x00002000
#define SP_SET_SIG2 0x00004000
#define SP_CLR_SIG3 0x00008000
#define SP_SET_SIG3 0x00010000
#define SP_CLR_SIG4 0x00020000
#define SP_SET_SIG4 0x00040000
#define SP_CLR_SIG5 0x00080000
#define SP_SET_SIG5 0x00100000
#define SP_CLR_SIG6 0x00200000
#define SP_SET_SIG6 0x00400000
#define SP_CLR_SIG7 0x00800000
#define SP_SET_SIG7 0x01000000
#define SP_SET_BROKE 0x80000000 // Does not exist in hardware, used only internally
struct rsp;
// Registers list.
enum rsp_cp0_register {
RSP_CP0_REGISTER_DMA_CACHE = 32,
RSP_CP0_REGISTER_DMA_DRAM = 33,
RSP_CP0_REGISTER_DMA_READ_LENGTH = 34,
RSP_CP0_REGISTER_DMA_WRITE_LENGTH = 35,
RSP_CP0_REGISTER_SP_STATUS = 36,
RSP_CP0_REGISTER_DMA_FULL = 37,
RSP_CP0_REGISTER_DMA_BUSY = 38,
RSP_CP0_REGISTER_SP_RESERVED = 39,
RSP_CP0_REGISTER_CMD_START = 40,
RSP_CP0_REGISTER_CMD_END = 41,
RSP_CP0_REGISTER_CMD_CURRENT = 42,
RSP_CP0_REGISTER_CMD_STATUS = 43,
RSP_CP0_REGISTER_CMD_CLOCK = 44,
RSP_CP0_REGISTER_CMD_BUSY = 45,
RSP_CP0_REGISTER_CMD_PIPE_BUSY = 46,
RSP_CP0_REGISTER_CMD_TMEM_BUSY = 47,
};
void RSP_MFC0(struct rsp *rsp, uint32_t iw, uint32_t rs, uint32_t rt);
void RSP_MTC0(struct rsp *rsp, uint32_t iw, uint32_t rs, uint32_t rt);
uint32_t rsp_read_cp0_reg(struct rsp *rsp, unsigned src);
void rsp_write_cp0_reg(struct rsp *rsp, unsigned dest, uint32_t rt);
void rsp_status_write(struct rsp *rsp, uint32_t rt);
cen64_cold void rsp_cp0_init(struct rsp *rsp);
#endif