| 1 | /* |
| 2 | * z64 |
| 3 | * |
| 4 | * Copyright (C) 2007 ziggy |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License as published by |
| 8 | * the Free Software Foundation; either version 2 of the License, or |
| 9 | * (at your option) any later version. |
| 10 | * |
| 11 | * This program is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | * GNU General Public License for more details. |
| 15 | * |
| 16 | * You should have received a copy of the GNU General Public License along |
| 17 | * with this program; if not, write to the Free Software Foundation, Inc., |
| 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| 19 | * |
| 20 | **/ |
| 21 | |
| 22 | #ifndef _RSP_H_ |
| 23 | #define _RSP_H_ |
| 24 | |
| 25 | #define M64P_PLUGIN_PROTOTYPES 1 |
| 26 | #include "m64p_types.h" |
| 27 | #include "m64p_plugin.h" |
| 28 | #include "z64.h" |
| 29 | #include <math.h> // sqrt |
| 30 | #include <stdlib.h> |
| 31 | #include <stdio.h> |
| 32 | #include <string.h> // memset |
| 33 | |
| 34 | #define INLINE inline |
| 35 | |
| 36 | extern void log(m64p_msg_level level, const char *msg, ...); |
| 37 | |
| 38 | /* defined in systems/n64.c */ |
| 39 | #define rdram ((UINT32*)z64_rspinfo.RDRAM) |
| 40 | //extern UINT32 *rdram; |
| 41 | #define rsp_imem ((UINT32*)z64_rspinfo.IMEM) |
| 42 | //extern UINT32 *rsp_imem; |
| 43 | #define rsp_dmem ((UINT32*)z64_rspinfo.DMEM) |
| 44 | //extern UINT32 *rsp_dmem; |
| 45 | //extern void dp_full_sync(void); |
| 46 | |
| 47 | #define vi_origin (*(UINT32*)z64_rspinfo.VI_ORIGIN_REG) |
| 48 | //extern UINT32 vi_origin; |
| 49 | #define vi_width (*(UINT32*)z64_rspinfo.VI_WIDTH_REG) |
| 50 | //extern UINT32 vi_width; |
| 51 | #define vi_control (*(UINT32*)z64_rspinfo.VI_STATUS_REG) |
| 52 | //extern UINT32 vi_control; |
| 53 | |
| 54 | #define dp_start (*(UINT32*)z64_rspinfo.DPC_START_REG) |
| 55 | //extern UINT32 dp_start; |
| 56 | #define dp_end (*(UINT32*)z64_rspinfo.DPC_END_REG) |
| 57 | //extern UINT32 dp_end; |
| 58 | #define dp_current (*(UINT32*)z64_rspinfo.DPC_CURRENT_REG) |
| 59 | //extern UINT32 dp_current; |
| 60 | #define dp_status (*(UINT32*)z64_rspinfo.DPC_STATUS_REG) |
| 61 | //extern UINT32 dp_status; |
| 62 | |
| 63 | #define sp_pc (*(UINT32*)z64_rspinfo.SP_PC_REG) |
| 64 | |
| 65 | typedef union |
| 66 | { |
| 67 | UINT64 d[2]; |
| 68 | UINT32 l[4]; |
| 69 | INT16 s[8]; |
| 70 | UINT8 b[16]; |
| 71 | } VECTOR_REG; |
| 72 | |
| 73 | typedef union |
| 74 | { |
| 75 | INT64 q; |
| 76 | INT32 l[2]; |
| 77 | INT16 w[4]; |
| 78 | } ACCUMULATOR_REG; |
| 79 | |
| 80 | typedef struct |
| 81 | { |
| 82 | // vectors first , need to be memory aligned for sse |
| 83 | VECTOR_REG v[32]; |
| 84 | ACCUMULATOR_REG accum[8]; |
| 85 | |
| 86 | //UINT32 pc; |
| 87 | UINT32 r[32]; |
| 88 | UINT16 flag[4]; |
| 89 | |
| 90 | INT32 square_root_res; |
| 91 | INT32 square_root_high; |
| 92 | INT32 reciprocal_res; |
| 93 | INT32 reciprocal_high; |
| 94 | |
| 95 | UINT32 ppc; |
| 96 | UINT32 nextpc; |
| 97 | |
| 98 | UINT32 step_count; |
| 99 | |
| 100 | int inval_gen; |
| 101 | |
| 102 | RSP_INFO ext; |
| 103 | } RSP_REGS; |
| 104 | |
| 105 | #define z64_rspinfo (rsp.ext) |
| 106 | |
| 107 | int rsp_execute(int cycles); |
| 108 | void rsp_reset(void); |
| 109 | void rsp_init(RSP_INFO info); |
| 110 | offs_t rsp_dasm_one(char *buffer, offs_t pc, UINT32 op); |
| 111 | |
| 112 | extern UINT32 sp_read_reg(UINT32 reg); |
| 113 | extern void sp_write_reg(UINT32 reg, UINT32 data); |
| 114 | // extern READ32_HANDLER( n64_dp_reg_r ); |
| 115 | // extern WRITE32_HANDLER( n64_dp_reg_w ); |
| 116 | |
| 117 | #define RSREG ((op >> 21) & 0x1f) |
| 118 | #define RTREG ((op >> 16) & 0x1f) |
| 119 | #define RDREG ((op >> 11) & 0x1f) |
| 120 | #define SHIFT ((op >> 6) & 0x1f) |
| 121 | |
| 122 | #define RSVAL (rsp.r[RSREG]) |
| 123 | #define RTVAL (rsp.r[RTREG]) |
| 124 | #define RDVAL (rsp.r[RDREG]) |
| 125 | |
| 126 | #define _RSREG(op) ((op >> 21) & 0x1f) |
| 127 | #define _RTREG(op) ((op >> 16) & 0x1f) |
| 128 | #define _RDREG(op) ((op >> 11) & 0x1f) |
| 129 | #define _SHIFT(op) ((op >> 6) & 0x1f) |
| 130 | |
| 131 | #define _RSVAL(op) (rsp.r[_RSREG(op)]) |
| 132 | #define _RTVAL(op) (rsp.r[_RTREG(op)]) |
| 133 | #define _RDVAL(op) (rsp.r[_RDREG(op)]) |
| 134 | |
| 135 | #define SIMM16 ((INT32)(INT16)(op)) |
| 136 | #define UIMM16 ((UINT16)(op)) |
| 137 | #define UIMM26 (op & 0x03ffffff) |
| 138 | |
| 139 | #define _SIMM16(op) ((INT32)(INT16)(op)) |
| 140 | #define _UIMM16(op) ((UINT16)(op)) |
| 141 | #define _UIMM26(op) (op & 0x03ffffff) |
| 142 | |
| 143 | |
| 144 | /*#define _JUMP(pc) \ |
| 145 | if ((GENTRACE("_JUMP %x\n", rsp.nextpc), 1) && rsp_jump(rsp.nextpc)) return 1; \ |
| 146 | if (rsp.inval_gen || sp_pc != pc+8) return 0; |
| 147 | */ |
| 148 | |
| 149 | #define CARRY_FLAG(x) ((rsp.flag[0] & (1 << ((x)))) ? 1 : 0) |
| 150 | #define CLEAR_CARRY_FLAGS() { rsp.flag[0] &= ~0xff; } |
| 151 | #define SET_CARRY_FLAG(x) { rsp.flag[0] |= (1 << ((x))); } |
| 152 | #define CLEAR_CARRY_FLAG(x) { rsp.flag[0] &= ~(1 << ((x))); } |
| 153 | |
| 154 | #define COMPARE_FLAG(x) ((rsp.flag[1] & (1 << ((x)))) ? 1 : 0) |
| 155 | #define CLEAR_COMPARE_FLAGS() { rsp.flag[1] &= ~0xff; } |
| 156 | #define SET_COMPARE_FLAG(x) { rsp.flag[1] |= (1 << ((x))); } |
| 157 | #define CLEAR_COMPARE_FLAG(x) { rsp.flag[1] &= ~(1 << ((x))); } |
| 158 | |
| 159 | #define ZERO_FLAG(x) ((rsp.flag[0] & (1 << (8+(x)))) ? 1 : 0) |
| 160 | #define CLEAR_ZERO_FLAGS() { rsp.flag[0] &= ~0xff00; } |
| 161 | #define SET_ZERO_FLAG(x) { rsp.flag[0] |= (1 << (8+(x))); } |
| 162 | #define CLEAR_ZERO_FLAG(x) { rsp.flag[0] &= ~(1 << (8+(x))); } |
| 163 | |
| 164 | //#define rsp z64_rsp // to avoid namespace collision with other libs |
| 165 | extern RSP_REGS rsp __attribute__((aligned(16))); |
| 166 | |
| 167 | |
| 168 | //#define ROPCODE(pc) cpu_readop32(pc) |
| 169 | #define ROPCODE(pc) program_read_dword_32be(pc | 0x1000) |
| 170 | |
| 171 | INLINE UINT8 program_read_byte_32be(UINT32 address) |
| 172 | { |
| 173 | return ((UINT8*)z64_rspinfo.DMEM)[(address&0x1fff)^3]; |
| 174 | } |
| 175 | |
| 176 | INLINE UINT16 program_read_word_32be(UINT32 address) |
| 177 | { |
| 178 | return ((UINT16*)z64_rspinfo.DMEM)[((address&0x1fff)>>1)^1]; |
| 179 | } |
| 180 | |
| 181 | INLINE UINT32 program_read_dword_32be(UINT32 address) |
| 182 | { |
| 183 | return ((UINT32*)z64_rspinfo.DMEM)[(address&0x1fff)>>2]; |
| 184 | } |
| 185 | |
| 186 | INLINE void program_write_byte_32be(UINT32 address, UINT8 data) |
| 187 | { |
| 188 | ((UINT8*)z64_rspinfo.DMEM)[(address&0x1fff)^3] = data; |
| 189 | } |
| 190 | |
| 191 | INLINE void program_write_word_32be(UINT32 address, UINT16 data) |
| 192 | { |
| 193 | ((UINT16*)z64_rspinfo.DMEM)[((address&0x1fff)>>1)^1] = data; |
| 194 | } |
| 195 | |
| 196 | INLINE void program_write_dword_32be(UINT32 address, UINT32 data) |
| 197 | { |
| 198 | ((UINT32*)z64_rspinfo.DMEM)[(address&0x1fff)>>2] = data; |
| 199 | } |
| 200 | |
| 201 | INLINE UINT8 READ8(UINT32 address) |
| 202 | { |
| 203 | address = 0x04000000 | (address & 0xfff); |
| 204 | return program_read_byte_32be(address); |
| 205 | } |
| 206 | |
| 207 | INLINE UINT16 READ16(UINT32 address) |
| 208 | { |
| 209 | address = 0x04000000 | (address & 0xfff); |
| 210 | |
| 211 | if (address & 1) |
| 212 | { |
| 213 | //osd_die("RSP: READ16: unaligned %08X at %08X\n", address, rsp.ppc); |
| 214 | return ((program_read_byte_32be(address+0) & 0xff) << 8) | (program_read_byte_32be(address+1) & 0xff); |
| 215 | } |
| 216 | |
| 217 | return program_read_word_32be(address); |
| 218 | } |
| 219 | |
| 220 | INLINE UINT32 READ32(UINT32 address) |
| 221 | { |
| 222 | address = 0x04000000 | (address & 0xfff); |
| 223 | |
| 224 | if (address & 3) |
| 225 | { |
| 226 | //fatalerror("RSP: READ32: unaligned %08X at %08X\n", address, rsp.ppc); |
| 227 | return ((program_read_byte_32be(address + 0) & 0xff) << 24) | |
| 228 | ((program_read_byte_32be(address + 1) & 0xff) << 16) | |
| 229 | ((program_read_byte_32be(address + 2) & 0xff) << 8) | |
| 230 | ((program_read_byte_32be(address + 3) & 0xff) << 0); |
| 231 | } |
| 232 | |
| 233 | return program_read_dword_32be(address); |
| 234 | } |
| 235 | |
| 236 | |
| 237 | INLINE void WRITE8(UINT32 address, UINT8 data) |
| 238 | { |
| 239 | address = 0x04000000 | (address & 0xfff); |
| 240 | program_write_byte_32be(address, data); |
| 241 | } |
| 242 | |
| 243 | INLINE void WRITE16(UINT32 address, UINT16 data) |
| 244 | { |
| 245 | address = 0x04000000 | (address & 0xfff); |
| 246 | |
| 247 | if (address & 1) |
| 248 | { |
| 249 | //fatalerror("RSP: WRITE16: unaligned %08X, %04X at %08X\n", address, data, rsp.ppc); |
| 250 | program_write_byte_32be(address + 0, (data >> 8) & 0xff); |
| 251 | program_write_byte_32be(address + 1, (data >> 0) & 0xff); |
| 252 | return; |
| 253 | } |
| 254 | |
| 255 | program_write_word_32be(address, data); |
| 256 | } |
| 257 | |
| 258 | INLINE void WRITE32(UINT32 address, UINT32 data) |
| 259 | { |
| 260 | address = 0x04000000 | (address & 0xfff); |
| 261 | |
| 262 | if (address & 3) |
| 263 | { |
| 264 | //fatalerror("RSP: WRITE32: unaligned %08X, %08X at %08X\n", address, data, rsp.ppc); |
| 265 | program_write_byte_32be(address + 0, (data >> 24) & 0xff); |
| 266 | program_write_byte_32be(address + 1, (data >> 16) & 0xff); |
| 267 | program_write_byte_32be(address + 2, (data >> 8) & 0xff); |
| 268 | program_write_byte_32be(address + 3, (data >> 0) & 0xff); |
| 269 | return; |
| 270 | } |
| 271 | |
| 272 | program_write_dword_32be(address, data); |
| 273 | } |
| 274 | |
| 275 | int rsp_jump(int pc); |
| 276 | void rsp_invalidate(int begin, int len); |
| 277 | void rsp_execute_one(UINT32 op); |
| 278 | |
| 279 | |
| 280 | |
| 281 | #define JUMP_ABS(addr) { rsp.nextpc = 0x04001000 | (((addr) << 2) & 0xfff); } |
| 282 | #define JUMP_ABS_L(addr,l) { rsp.nextpc = 0x04001000 | (((addr) << 2) & 0xfff); rsp.r[l] = sp_pc + 4; } |
| 283 | #define JUMP_REL(offset) { rsp.nextpc = 0x04001000 | ((sp_pc + ((offset) << 2)) & 0xfff); } |
| 284 | #define JUMP_REL_L(offset,l) { rsp.nextpc = 0x04001000 | ((sp_pc + ((offset) << 2)) & 0xfff); rsp.r[l] = sp_pc + 4; } |
| 285 | #define JUMP_PC(addr) { rsp.nextpc = 0x04001000 | ((addr) & 0xfff); } |
| 286 | #define JUMP_PC_L(addr,l) { rsp.nextpc = 0x04001000 | ((addr) & 0xfff); rsp.r[l] = sp_pc + 4; } |
| 287 | #define LINK(l) rsp.r[l] = sp_pc + 4 |
| 288 | |
| 289 | #define VDREG ((op >> 6) & 0x1f) |
| 290 | #define VS1REG ((op >> 11) & 0x1f) |
| 291 | #define VS2REG ((op >> 16) & 0x1f) |
| 292 | #define EL ((op >> 21) & 0xf) |
| 293 | |
| 294 | #define VREG_B(reg, offset) rsp.v[(reg)].b[((offset)^1)] |
| 295 | #define VREG_S(reg, offset) rsp.v[(reg)].s[((offset))] |
| 296 | #define VREG_L(reg, offset) rsp.v[(reg)].l[((offset))] |
| 297 | |
| 298 | #define VEC_EL_1(x,z) (z) //(vector_elements_1[(x)][(z)]) |
| 299 | #define VEC_EL_2(x,z) (vector_elements_2[(x)][(z)]) |
| 300 | |
| 301 | #define ACCUM(x) rsp.accum[((x))].q |
| 302 | #define ACCUM_H(x) rsp.accum[((x))].w[3] |
| 303 | #define ACCUM_M(x) rsp.accum[((x))].w[2] |
| 304 | #define ACCUM_L(x) rsp.accum[((x))].w[1] |
| 305 | |
| 306 | void unimplemented_opcode(UINT32 op); |
| 307 | void handle_vector_ops(UINT32 op); |
| 308 | UINT32 get_cop0_reg(int reg); |
| 309 | void set_cop0_reg(int reg, UINT32 data); |
| 310 | void handle_lwc2(UINT32 op); |
| 311 | void handle_swc2(UINT32 op); |
| 312 | |
| 313 | INLINE UINT32 n64_dp_reg_r(UINT32 offset, UINT32 dummy) |
| 314 | { |
| 315 | switch (offset) |
| 316 | { |
| 317 | case 0x00/4: // DP_START_REG |
| 318 | return dp_start; |
| 319 | |
| 320 | case 0x04/4: // DP_END_REG |
| 321 | return dp_end; |
| 322 | |
| 323 | case 0x08/4: // DP_CURRENT_REG |
| 324 | return dp_current; |
| 325 | |
| 326 | case 0x0c/4: // DP_STATUS_REG |
| 327 | return dp_status; |
| 328 | |
| 329 | case 0x10/4: // DP_CLOCK_REG |
| 330 | return *z64_rspinfo.DPC_CLOCK_REG; |
| 331 | |
| 332 | default: |
| 333 | log(M64MSG_WARNING, "dp_reg_r: %08X\n", offset); |
| 334 | break; |
| 335 | } |
| 336 | |
| 337 | return 0; |
| 338 | } |
| 339 | INLINE void n64_dp_reg_w(UINT32 offset, UINT32 data, UINT32 dummy) |
| 340 | { |
| 341 | switch (offset) |
| 342 | { |
| 343 | case 0x00/4: // DP_START_REG |
| 344 | dp_start = data; |
| 345 | dp_current = dp_start; |
| 346 | break; |
| 347 | |
| 348 | case 0x04/4: // DP_END_REG |
| 349 | dp_end = data; |
| 350 | //rdp_process_list(); |
| 351 | if (dp_end<dp_start)//three lines for debugging |
| 352 | { |
| 353 | log(M64MSG_INFO, "RDP End < RDP Start!");//happens in Stunt Racer with Ville Linde's sp_dma |
| 354 | break; |
| 355 | } |
| 356 | if (dp_end==dp_start) |
| 357 | break; |
| 358 | if (z64_rspinfo.ProcessRdpList != NULL) { z64_rspinfo.ProcessRdpList(); } |
| 359 | break; |
| 360 | |
| 361 | case 0x0c/4: // DP_STATUS_REG |
| 362 | if (data & 0x00000001) dp_status &= ~DP_STATUS_XBUS_DMA; |
| 363 | if (data & 0x00000002) dp_status |= DP_STATUS_XBUS_DMA; |
| 364 | if (data & 0x00000004) dp_status &= ~DP_STATUS_FREEZE; |
| 365 | if (data & 0x00000008) dp_status |= DP_STATUS_FREEZE; |
| 366 | if (data & 0x00000010) dp_status &= ~DP_STATUS_FLUSH; |
| 367 | if (data & 0x00000020) dp_status |= DP_STATUS_FLUSH; |
| 368 | break; |
| 369 | |
| 370 | default: |
| 371 | log(M64MSG_WARNING, "dp_reg_w: %08X, %08X\n", data, offset); |
| 372 | break; |
| 373 | } |
| 374 | } |
| 375 | |
| 376 | #define INTEL86 |
| 377 | #if defined(INTEL86) && defined __GNUC__ && __GNUC__ >= 2 |
| 378 | static __inline__ unsigned long long RDTSC(void) |
| 379 | { |
| 380 | unsigned long long int x; |
| 381 | __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x)); |
| 382 | return x; |
| 383 | } |
| 384 | // inline volatile uint64_t RDTSC() { |
| 385 | // register uint64_t TSC asm("eax"); |
| 386 | // asm volatile (".byte 15, 49" : : : "eax", "edx"); |
| 387 | // return TSC; |
| 388 | // } |
| 389 | // #define RDTSC1(n) __asm__ __volatile__("rdtsc" : "=a" (n): ) |
| 390 | // #define RDTSC2(n) __asm__ __volatile__ ("rdtsc\nmov %%edx,%%eax" : "=a" (n): ) |
| 391 | // inline void RDTSC(uint64_t& a) { uint32_t b, c; RDTSC1(b); RDTSC2(c); |
| 392 | // a = (((uint64_t)c)<<32) | b; } |
| 393 | #elif defined(INTEL86) && defined WIN32 |
| 394 | #define rdtsc __asm __emit 0fh __asm __emit 031h |
| 395 | #define cpuid __asm __emit 0fh __asm __emit 0a2h |
| 396 | inline uint64_t RDTSC() { |
| 397 | static uint32_t temp; |
| 398 | __asm { |
| 399 | push edx |
| 400 | push eax |
| 401 | rdtsc |
| 402 | mov temp, eax |
| 403 | pop eax |
| 404 | pop edx |
| 405 | } |
| 406 | return temp; |
| 407 | } |
| 408 | #else |
| 409 | #define RDTSC(n) n=0 |
| 410 | #endif |
| 411 | |
| 412 | #ifdef GENTRACE |
| 413 | #undef GENTRACE |
| 414 | #include <stdarg.h> |
| 415 | inline void GENTRACE(const char * s, ...) { |
| 416 | va_list ap; |
| 417 | va_start(ap, s); |
| 418 | vfprintf(stderr, s, ap); |
| 419 | va_end(ap); |
| 420 | int i; |
| 421 | for (i=0; i<32; i++) |
| 422 | fprintf(stderr, "r%d=%x ", i, rsp.r[i]); |
| 423 | fprintf(stderr, "\n"); |
| 424 | for (i=0; i<32; i++) |
| 425 | fprintf(stderr, "v%d=%x %x %x %x %x %x %x %x ", i, |
| 426 | (UINT16)rsp.v[i].s[0], |
| 427 | (UINT16)rsp.v[i].s[1], |
| 428 | (UINT16)rsp.v[i].s[2], |
| 429 | (UINT16)rsp.v[i].s[3], |
| 430 | (UINT16)rsp.v[i].s[4], |
| 431 | (UINT16)rsp.v[i].s[5], |
| 432 | (UINT16)rsp.v[i].s[6], |
| 433 | (UINT16)rsp.v[i].s[7] |
| 434 | ); |
| 435 | fprintf(stderr, "\n"); |
| 436 | |
| 437 | fprintf(stderr, "f0=%x f1=%x f2=%x f3=%x\n", rsp.flag[0], |
| 438 | rsp.flag[1],rsp.flag[2],rsp.flag[3]); |
| 439 | } |
| 440 | #endif |
| 441 | //#define GENTRACE printf |
| 442 | //#define GENTRACE |
| 443 | |
| 444 | #ifdef RSPTIMING |
| 445 | extern uint64_t rsptimings[512]; |
| 446 | extern int rspcounts[512]; |
| 447 | #endif |
| 448 | |
| 449 | #endif // ifndef _RSP_H_ |