diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..a8b7ed028 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +# Other data. +/data + +# Build files. +/build + +# Swap files. +*.swp + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..d5e9ff82b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,35 @@ +# +# CEN64: Cycle-Accurate Nintendo 64 Simulator. +# Copyright (C) 2014, Tyler J. Stachecki. +# +# This file is subject to the terms and conditions defined in +# 'LICENSE', which is part of this source code package. +# + +cmake_minimum_required(VERSION 2.6) +project(cen64) + +if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU") + set(CMAKE_C_FLAGS "-Wall -Wextra -std=c99 -D_POSIX_SOURCE") + + set(CMAKE_C_FLAGS_DEBUG "-ggdb3 -g3 -O0") + set(CMAKE_C_FLAGS_MINSIZEREL "-Os -DNDEBUG") + set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG") + set(CMAKE_C_FLAGS_RELWITHDEBINFO "-Og") +endif ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU") + +include_directories(${PROJECT_SOURCE_DIR}) +include_directories(${PROJECT_SOURCE_DIR}/bus) +include_directories(${PROJECT_SOURCE_DIR}/pif) +include_directories(${PROJECT_SOURCE_DIR}/vr4300) + +# Glob all the files together. +file(GLOB BUS_SOURCES ${PROJECT_SOURCE_DIR}/bus/*.c) +file(GLOB CEN64_SOURCES ${PROJECT_SOURCE_DIR}/*.c) +file(GLOB PIF_SOURCES ${PROJECT_SOURCE_DIR}/pif/*.c) +file(GLOB VR4300_SOURCES ${PROJECT_SOURCE_DIR}/vr4300/*.c) + +# Create the executable. +add_executable(cen64 ${CEN64_SOURCES} ${BUS_SOURCES} ${PIF_SOURCES} + ${VR4300_SOURCES}) + diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..ec0ac4f88 --- /dev/null +++ b/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2014, Tyler J. Stachecki +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Tyler J. Stachecki nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL TYLER J. STACHECKI BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/README.md b/README.md new file mode 100644 index 000000000..a3c5548ab --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +

+ +

+ +# About + +This is my pet project. It's something I pick up whenever I get bored. To me, +what Nintendo and SGI did with this console is nothing short of amazing. The +ingenuity and design of the hardware was well-ahead of it's time, and it is +an absolute blast to reverse-engineer and study. I started this project in +order to learn more about what _really_ went on at the hardware level back in +the (good old) days. + +That being said, I've also grown tired of people complaining that "cycle +accurate simulation on N-gen consoles is impossible." Not to be a bigot, but +no, it's not. Is it hard to write a simulator that is capable of running fast +enough to emulate a 93.75MHz processor on modern machines? Absolutely. Is it +impossible? No, certainly not. It just takes time. Programming is an art, +and like anything else, it takes time if done well. + +Getting back on track: this simulator attempts to be everything that every +other emulator hoped to be, but never quite attained due to... emulation. My +hopes are that properly simulating things at the cycle and pixel-accurate level +will preserve the original fidelity of the work that so many companies put +out. + +Thank you to every single one of you developers for filling my childhood +with excellent memories. I'd also like to thank the community on all their +hard work and effort spent reverse-engineering this little gem. Without +further ado... + diff --git a/VERSION b/VERSION new file mode 100644 index 000000000..be5863417 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.3 diff --git a/assets/logo-small.png b/assets/logo-small.png new file mode 100644 index 000000000..307f27693 Binary files /dev/null and b/assets/logo-small.png differ diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 000000000..1b1189de5 Binary files /dev/null and b/assets/logo.png differ diff --git a/bus/controller.c b/bus/controller.c new file mode 100644 index 000000000..bbdb43626 --- /dev/null +++ b/bus/controller.c @@ -0,0 +1,36 @@ +// +// bus/controller.c: System bus controller. +// +// CEN64: Cycle-Accurate Nintendo 64 Simulator. +// Copyright (C) 2014, Tyler J. Stachecki. +// +// This file is subject to the terms and conditions defined in +// 'LICENSE', which is part of this source code package. +// + +#include "common.h" +#include "bus/controller.h" +#include "pif/controller.h" + +// Initializes the bus component. +int bus_init(struct bus_controller *bus) { + bus->num_requests = 0; + bus->rq_head = 0; + bus->rq_tail = 0; + + return 0; +} + +// Issues a read request to the bus. +unsigned bus_read_word(struct bus_controller *bus, + uint32_t address, uint32_t *word) { + + if (address >= 0x1FC00000 && address < 0x1FC07C00) + return read_pifrom(bus->pif, word, address & 0xFFC); + + printf("bus_read_word: Failed to access: 0x%.8X\n", address); + abort(); + + return 0; +} + diff --git a/bus/controller.h b/bus/controller.h new file mode 100644 index 000000000..d8d55dbc0 --- /dev/null +++ b/bus/controller.h @@ -0,0 +1,31 @@ +// +// bus/controller.h: System bus controller. +// +// CEN64: Cycle-Accurate Nintendo 64 Simulator. +// Copyright (C) 2014, Tyler J. Stachecki. +// +// This file is subject to the terms and conditions defined in +// 'LICENSE', which is part of this source code package. +// + +#ifndef __bus_controller_h__ +#define __bus_controller_h__ +#define BUS_REQUEST_QUEUE_SIZE 8 + +struct bus_request; +struct vr4300; + +struct bus_controller { + struct bus_request *rq[BUS_REQUEST_QUEUE_SIZE]; + unsigned num_requests, rq_head, rq_tail; + + struct pif_controller *pif; + struct vr4300 *vr4300; +}; + +int bus_init(struct bus_controller *bus); +unsigned bus_read_word(struct bus_controller *bus, + uint32_t address, uint32_t *word); + +#endif + diff --git a/cen64.c b/cen64.c new file mode 100644 index 000000000..0b88e3234 --- /dev/null +++ b/cen64.c @@ -0,0 +1,32 @@ +// +// cen64.c: CEN64 entry point. +// +// CEN64: Cycle-Accurate Nintendo 64 Simulator. +// Copyright (C) 2014, Tyler J. Stachecki. +// +// This file is subject to the terms and conditions defined in +// 'LICENSE', which is part of this source code package. +// + +#include "common.h" +#include "device.h" + +int main(int argc, const char *argv[]) { + struct cen64_device *device; + + if (argc < 2) { + printf("%s \n", argv[0]); + return 0; + } + + if ((device = device_create(argv[1])) == NULL) { + printf("Failed to create a device.\n"); + return 1; + } + + device_run(device); + + device_destroy(device); + return 0; +} + diff --git a/common.h b/common.h new file mode 100644 index 000000000..1b3a0aed9 --- /dev/null +++ b/common.h @@ -0,0 +1,86 @@ +// +// common.h: Common definitions and such. +// +// CEN64: Cycle-Accurate Nintendo 64 Simulator. +// Copyright (C) 2014, Tyler J. Stachecki. +// +// This file is subject to the terms and conditions defined in +// 'LICENSE', which is part of this source code package. +// + +#ifndef __common_h__ +#define __common_h__ + +#ifdef _MSC_VER +#define inline _inline +#endif + +#ifndef __cplusplus +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#endif + +#ifndef _MSC_VER +#ifndef __cplusplus +#include +#else +#include +#endif + +#else +typedef char bool; +#define false 0 +#define true 1 +#endif + +#ifndef NDEBUG +#ifndef __cplusplus +#include +#else +#include +#endif +#endif + +#define CACHE_LINE_SIZE 64 + +// Define cen64_align(). +#ifdef _MSC_VER +#define cen64_align(decl, value) __declspec(align(value)) decl + +#elif (defined __GNUC__) +#define cen64_align(decl, value) decl __attribute__ ((aligned(value))) + +#else +#define cen64_align(decl, value) decl value +#endif + +// Define likely()/unlikely(). +#ifdef __GNUC__ +#define likely(expr) __builtin_expect(!!(expr), !0) +#define unlikely(expr) __builtin_expect(!!(expr), 0) + +#else +#define likely(expr) expr +#define unlikely(expr) expr +#endif + +// Define unused(). +#ifdef __GNUC__ +#define unused(decl) __attribute__((unused)) decl +#else +#define unused(decl) decl +#endif + +#endif + diff --git a/device.c b/device.c new file mode 100644 index 000000000..2a8cfa2a0 --- /dev/null +++ b/device.c @@ -0,0 +1,97 @@ +// +// device.c: CEN64 device container. +// +// CEN64: Cycle-Accurate Nintendo 64 Simulator. +// Copyright (C) 2014, Tyler J. Stachecki. +// +// This file is subject to the terms and conditions defined in +// 'LICENSE', which is part of this source code package. +// + +#include "common.h" +#include "device.h" +#include "pif/controller.h" + +// Loads the PIFROM from a file into memory. +static int load_pifrom(const char *file, uint8_t *rom) { + int status = 0; + size_t i, last; + FILE *f; + + if ((f = fopen(file, "rb")) == NULL) { + printf("load_pifrom: Failed to open: %s\n", file); + return -1; + } + + for (i = 0; i < PIFROM_SIZE; i += last) { + last = fread(rom + i, 1, PIFROM_SIZE - i, f); + + if (feof(f)) { + printf("load_pifrom: ROM file is smaller than expected.\n"); + status = -1; + break; + } + + else if (ferror(f)) { + printf("load_pifrom: An error occured while reading the ROM.\n"); + status = -2; + break; + } + } + + fclose(f); + return status; +} + +// Creates and initializes a device. +struct cen64_device *device_create(const char *pifrom) { + struct cen64_device *device; + + // Allocate memory. + if ((device = (struct cen64_device*) malloc(sizeof(*device))) == NULL) + return NULL; + + // Initialize the PIF. + if (load_pifrom(pifrom, device->pifrom) < 0 || + init_pif(&device->pif, &device->bus, device->pifrom)) { + printf("create_device: Failed to initialize the PIF.\n"); + + free(device); + return NULL; + } + + // Initialize the bus. + if (bus_init(&device->bus)) { + printf("create_device: Failed to initialize the bus.\n"); + + free(device); + return NULL; + } + + device->bus.pif = &device->pif; + device->bus.vr4300 = &device->vr4300; + + // Initialize the VR4300. + if (vr4300_init(&device->vr4300, &device->bus)) { + printf("create_device: Failed to initialize the VR4300.\n"); + + free(device); + return NULL; + } + + return device; +} + +// Deallocates and cleans up a device. +void device_destroy(struct cen64_device *device) { + free(device); +} + +// Kicks off threads and starts the device. +void device_run(struct cen64_device *device) { + unsigned i; + + for (i = 0; i < 93750000; i++) + vr4300_cycle(&device->vr4300); +} + diff --git a/device.h b/device.h new file mode 100644 index 000000000..0a252ad82 --- /dev/null +++ b/device.h @@ -0,0 +1,34 @@ +// +// device.h: CEN64 device container. +// +// CEN64: Cycle-Accurate Nintendo 64 Simulator. +// Copyright (C) 2014, Tyler J. Stachecki. +// +// This file is subject to the terms and conditions defined in +// 'LICENSE', which is part of this source code package. +// + +#ifndef __device_h__ +#define __device_h__ +#include "common.h" +#include "bus/controller.h" +#include "pif/controller.h" +#include "vr4300/cpu.h" + +#define PIFROM_SIZE 2048 + +struct cen64_device { + struct bus_controller bus; + struct pif_controller pif; + struct vr4300 vr4300; + + uint8_t pifrom[PIFROM_SIZE]; +}; + +struct cen64_device *device_create(const char *pifrom); +void device_destroy(struct cen64_device *device); + +void device_run(struct cen64_device *device); + +#endif + diff --git a/pif/controller.c b/pif/controller.c new file mode 100644 index 000000000..f01c5956e --- /dev/null +++ b/pif/controller.c @@ -0,0 +1,31 @@ +// +// pif/controller.c: Peripheral interface controller. +// +// CEN64: Cycle-Accurate Nintendo 64 Simulator. +// Copyright (C) 2014, Tyler J. Stachecki. +// +// This file is subject to the terms and conditions defined in +// 'LICENSE', which is part of this source code package. +// + +#include "common.h" +#include "bus/controller.h" +#include "pif/controller.h" + +// Initializes the PIF. +int init_pif(struct pif_controller *pif, + struct bus_controller *bus, const uint8_t *rom) { + pif->bus = bus; + pif->rom = rom; + + return 0; +} + +// Reads a word from PIFROM. +int read_pifrom(struct pif_controller *pif, uint32_t *word, unsigned off) { + assert((off & 0x3) == 0 && "read_pifrom: Offset not word aligned."); + + memcpy(word, pif->rom + off, sizeof(*word)); + return 0; +} + diff --git a/pif/controller.h b/pif/controller.h new file mode 100644 index 000000000..16b51cdb0 --- /dev/null +++ b/pif/controller.h @@ -0,0 +1,28 @@ +// +// pif/controller.h: Peripheral interface controller. +// +// CEN64: Cycle-Accurate Nintendo 64 Simulator. +// Copyright (C) 2014, Tyler J. Stachecki. +// +// This file is subject to the terms and conditions defined in +// 'LICENSE', which is part of this source code package. +// + +#ifndef __pif_controller_h__ +#define __pif_controller_h__ +#include "common.h" + +struct bus_controller *bus; + +struct pif_controller { + struct bus_controller *bus; + const uint8_t *rom; +}; + +int init_pif(struct pif_controller *pif, + struct bus_controller *bus, const uint8_t *rom); + +int read_pifrom(struct pif_controller *pif, uint32_t *word, unsigned off); + +#endif + diff --git a/vr4300/cp0.c b/vr4300/cp0.c new file mode 100644 index 000000000..fe4ac91e6 --- /dev/null +++ b/vr4300/cp0.c @@ -0,0 +1,18 @@ +// +// vr4300/cp0.c: VR4300 system control coprocessor. +// +// CEN64: Cycle-Accurate Nintendo 64 Simulator. +// Copyright (C) 2014, Tyler J. Stachecki. +// +// This file is subject to the terms and conditions defined in +// 'LICENSE', which is part of this source code package. +// + +#include "common.h" +#include "vr4300/cp0.h" + +// Initializes the coprocessor. +void vr4300_cp0_init(struct vr4300_cp0 *cp0) { + memset(cp0, 0, sizeof(*cp0)); +} + diff --git a/vr4300/cp0.h b/vr4300/cp0.h new file mode 100644 index 000000000..1b668e172 --- /dev/null +++ b/vr4300/cp0.h @@ -0,0 +1,52 @@ +// +// vr4300/cp0.h: VR4300 system control coprocessor. +// +// CEN64: Cycle-Accurate Nintendo 64 Simulator. +// Copyright (C) 2014, Tyler J. Stachecki. +// +// This file is subject to the terms and conditions defined in +// 'LICENSE', which is part of this source code package. +// + +#ifndef __vr4300_cp0_h__ +#define __vr4300_cp0_h__ +#include "common.h" + +// Registers list. +enum vr4300_cp0_register { + VR4300_CP0_REGISTER_INDEX, + VR4300_CP0_REGISTER_RANDOM, + VR4300_CP0_REGISTER_ENTRYLO0, + VR4300_CP0_REGISTER_ENTRYLO1, + VR4300_CP0_REGISTER_CONTEXT, + VR4300_CP0_REGISTER_PAGEMASK, + VR4300_CP0_REGISTER_WIRED, + VR4300_CP0_REGISTER_BADVADDR = 8, + VR4300_CP0_REGISTER_COUNT, + VR4300_CP0_REGISTER_ENTRYHI, + VR4300_CP0_REGISTER_COMPARE, + VR4300_CP0_REGISTER_STATUS, + VR4300_CP0_REGISTER_CAUSE, + VR4300_CP0_REGISTER_EPC, + VR4300_CP0_REGISTER_PRID, + VR4300_CP0_REGISTER_CONFIG, + VR4300_CP0_REGISTER_LLADDR, + VR4300_CP0_REGISTER_WATCHLO, + VR4300_CP0_REGISTER_WATCHHI, + VR4300_CP0_REGISTER_XCONTEXT, + VR4300_CP0_REGISTER_PARITYERROR = 26, + VR4300_CP0_REGISTER_CACHEERR, + VR4300_CP0_REGISTER_TAGLO, + VR4300_CP0_REGISTER_TAGHI, + VR4300_CP0_REGISTER_ERROREPC, + NUM_VR4300_CP0_REGISTERS = 32, +}; + +struct vr4300_cp0 { + uint64_t regs[NUM_VR4300_CP0_REGISTERS]; +}; + +void vr4300_cp0_init(struct vr4300_cp0 *cp0); + +#endif + diff --git a/vr4300/cpu.c b/vr4300/cpu.c new file mode 100644 index 000000000..e4327692f --- /dev/null +++ b/vr4300/cpu.c @@ -0,0 +1,34 @@ +// +// vr4300/cpu.c: VR4300 processor container. +// +// CEN64: Cycle-Accurate Nintendo 64 Simulator. +// Copyright (C) 2014, Tyler J. Stachecki. +// +// This file is subject to the terms and conditions defined in +// 'LICENSE', which is part of this source code package. +// + +#include "common.h" +#include "vr4300/cp0.h" +#include "vr4300/cpu.h" +#include "vr4300/icache.h" +#include "vr4300/pipeline.h" + +// Sets the opaque pointer used for external accesses. +static void vr4300_connect_bus(struct vr4300 *vr4300, + struct bus_controller *bus) { + vr4300->bus = bus; +} + +// Initializes the VR4300 component. +int vr4300_init(struct vr4300 *vr4300, struct bus_controller *bus) { + vr4300_connect_bus(vr4300, bus); + + vr4300_cp0_init(&vr4300->cp0); + vr4300_icache_init(&vr4300->icache); + vr4300_pipeline_init(&vr4300->pipeline); + + vr4300->signals = VR4300_SIGNAL_COLDRESET; + return 0; +} + diff --git a/vr4300/cpu.h b/vr4300/cpu.h new file mode 100644 index 000000000..17927a03a --- /dev/null +++ b/vr4300/cpu.h @@ -0,0 +1,55 @@ +// +// vr4300/cpu.h: VR4300 processor container. +// +// CEN64: Cycle-Accurate Nintendo 64 Simulator. +// Copyright (C) 2014, Tyler J. Stachecki. +// +// This file is subject to the terms and conditions defined in +// 'LICENSE', which is part of this source code package. +// + +#ifndef __vr4300_cpu_h__ +#define __vr4300_cpu_h__ +#include "common.h" +#include "vr4300/cp0.h" +#include "vr4300/icache.h" +#include "vr4300/pipeline.h" + +struct bus_controller; + +enum vr4300_signals { + VR4300_SIGNAL_FORCEEXIT = 0x000000001, + VR4300_SIGNAL_COLDRESET = 0x000000002, +}; + +enum vr4300_register { + VR4300_REGISTER_R0, VR4300_REGISTER_AT, VR4300_REGISTER_V0, + VR4300_REGISTER_V1, VR4300_REGISTER_A0, VR4300_REGISTER_A1, + VR4300_REGISTER_A2, VR4300_REGISTER_A3, VR4300_REGISTER_T0, + VR4300_REGISTER_T1, VR4300_REGISTER_T2, VR4300_REGISTER_T3, + VR4300_REGISTER_T4, VR4300_REGISTER_R5, VR4300_REGISTER_T6, + VR4300_REGISTER_T7, VR4300_REGISTER_S0, VR4300_REGISTER_S1, + VR4300_REGISTER_S2, VR4300_REGISTER_S3, VR4300_REGISTER_S4, + VR4300_REGISTER_S5, VR4300_REGISTER_S6, VR4300_REGISTER_S7, + VR4300_REGISTER_T8, VR4300_REGISTER_T9, VR4300_REGISTER_K0, + VR4300_REGISTER_K1, VR4300_REGISTER_GP, VR4300_REGISTER_SP, + VR4300_REGISTER_FP, VR4300_REGISTER_RA, VR4300_REGISTER_LO, + VR4300_REGISTER_HI, NUM_VR4300_REGISTERS +}; + +struct vr4300 { + uint64_t regs[NUM_VR4300_REGISTERS]; + + struct vr4300_cp0 cp0; + struct vr4300_icache icache; + struct vr4300_pipeline pipeline; + + struct bus_controller *bus; + unsigned signals; +}; + +void vr4300_cycle(struct vr4300 *vr4300); +int vr4300_init(struct vr4300 *vr4300, struct bus_controller *bus); + +#endif + diff --git a/vr4300/decoder.c b/vr4300/decoder.c new file mode 100644 index 000000000..bea73a3d9 --- /dev/null +++ b/vr4300/decoder.c @@ -0,0 +1,255 @@ +// +// vr4300/decoder.c: VR4300 decoder. +// +// CEN64: Cycle-Accurate Nintendo 64 Simulator. +// Copyright (C) 2014, Tyler J. Stachecki. +// +// This file is subject to the terms and conditions defined in +// 'LICENSE', which is part of this source code package. +// + +#include "common.h" +#include "vr4300/decoder.h" +#include "vr4300/opcodes.h" + +// ============================================================================ +// Escaped opcode table: Special. +// +// 31---------26------------------------------------------5--------0 +// | SPECIAL/6 | | FMT/6 | +// ------6----------------------------------------------------6----- +// |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| +// 000 | SLL | | SRL | SRA | SLLV | | SRLV | SRAV | +// 001 | JR | JALR | | |SYSCALL| BREAK | | SYNC | +// 010 | MFHI | MTHI | MFLO | MTLO | DSLLV | | DSRLV | DSRAV | +// 011 | MULT | MULTU | DIV | DIVU | DMULT | DMULTU| DDIV | DDIVU | +// 100 | ADD | ADDU | SUB | SUBU | AND | OR | XOR | NOR | +// 101 | | | SLT | SLTU | DADD | DADDU | DSUB | DSUBU | +// 110 | TGE | TGEU | TLT | TLTU | TEQ | | TNE | | +// 111 | DSLL | | DSRL | DSRA |DSLL32 | |DSRL32 |DSRA32 | +// |-------|-------|-------|-------|-------|-------|-------|-------| +// +// ============================================================================ +cen64_align(static const struct vr4300_opcode + vr4300_spec_opcode_table[64], CACHE_LINE_SIZE) = { + {SLL}, {INVALID}, {SRL}, {SRA}, + {SLLV}, {INVALID}, {SRLV}, {SRAV}, + {JR}, {JALR}, {INVALID}, {INVALID}, + {SYSCALL}, {BREAK}, {INVALID}, {SYNC}, + {MFHI}, {MTHI}, {MFLO}, {MTLO}, + {DSLLV}, {INVALID}, {DSRLV}, {DSRAV}, + {MULT}, {MULTU}, {DIV}, {DIVU}, + {DMULT}, {DMULTU}, {DDIV}, {DDIVU}, + {ADD}, {ADDU}, {SUB}, {SUBU}, + {AND}, {OR}, {XOR}, {NOR}, + {INVALID}, {INVALID}, {SLT}, {SLTU}, + {DADD}, {DADDU}, {DSUB}, {DSUBU}, + {TGE}, {TGEU}, {TLT}, {TLTU}, + {TEQ}, {INVALID}, {TNE}, {INVALID}, + {DSLL}, {INVALID}, {DSRL}, {DSRA}, + {DSLL32}, {INVALID}, {DSRL32}, {DSRA32} +}; + +// ============================================================================ +// Escaped opcode table: RegImm. +// +// 31---------26----------20-------16------------------------------0 +// | = REGIMM | | FMT/5 | | +// ------6---------------------5------------------------------------ +// |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| +// 00 | BLTZ | BGEZ | BLTZL | BGEZL | | | | | +// 01 | TGEI | TGEIU | TLTI | TLTIU | TEQI | | TNEI | | +// 10 | BLTZAL| BGEZAL|BLTZALL|BGEZALL| | | | | +// 11 | | | | | | | | | +// |-------|-------|-------|-------|-------|-------|-------|-------| +// +// ============================================================================ +cen64_align(static const struct vr4300_opcode + vr4300_regimm_opcode_table[32], CACHE_LINE_SIZE) = { + {BLTZ}, {BGEZ}, {BLTZL}, {BGEZL}, + {INVALID}, {INVALID}, {INVALID}, {INVALID}, + {TGEI}, {TGEIU}, {TLTI}, {TLTIU}, + {TEQI}, {INVALID}, {TNEI}, {INVALID}, + {BLTZAL}, {BGEZAL}, {BLTZALL}, {BGEZALL}, + {INVALID}, {INVALID}, {INVALID}, {INVALID}, + {INVALID}, {INVALID}, {INVALID}, {INVALID}, + {INVALID}, {INVALID}, {INVALID}, {INVALID} +}; + +// ============================================================================ +// Escaped opcode table: COP0. +// +// 31--------26-25------21 ----------------------------------------0 +// | COP0/6 | FMT/5 | | +// ------6----------5----------------------------------------------- +// |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| +// 00 | MFC0 | DMFC0 | CFC0 | --- | MTC0 | DMTC0 | CTC0 | --- | +// 01 | BC0 | --- | --- | --- | --- | --- | --- | --- | +// 10 | TLB | --- | --- | --- | --- | --- | --- | --- | +// 11 | --- | --- | --- | --- | --- | --- | --- | --- | +// |-------|-------|-------|-------|-------|-------|-------|-------| +// ============================================================================ +cen64_align(static const struct vr4300_opcode + vr4300_cop0_opcode_table[32], CACHE_LINE_SIZE) = { + {MFC0}, {DMFC0}, {CFC0}, {INVALID}, + {MTC0}, {DMTC0}, {CTC0}, {INVALID}, + {BC0}, {INVALID}, {INVALID}, {INVALID}, + {INVALID}, {INVALID}, {INVALID}, {INVALID}, + {TLB}, {INVALID}, {INVALID}, {INVALID}, + {INVALID}, {INVALID}, {INVALID}, {INVALID}, + {INVALID}, {INVALID}, {INVALID}, {INVALID}, + {INVALID}, {INVALID}, {INVALID}, {INVALID} +}; + +// ============================================================================ +// Escaped opcode table: COP1. +// +// 31--------26-25------21 ----------------------------------------0 +// | COP1/6 | FMT/5 | | +// ------6----------5----------------------------------------------- +// |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| +// 00 | MFC1 | DMFC1 | CFC1 | --- | MTC1 | DMTC1 | CTC1 | --- | +// 01 | BC1 | --- | --- | --- | --- | --- | --- | --- | +// 10 | FPUS | FPUD | --- | --- | FPUW | FPUL | --- | --- | +// 11 | --- | --- | --- | --- | --- | --- | --- | --- | +// |-------|-------|-------|-------|-------|-------|-------|-------| +// ============================================================================ +cen64_align(static const struct vr4300_opcode + vr4300_cop1_opcode_table[32], CACHE_LINE_SIZE) = { + {MFC1}, {DMFC1}, {CFC1}, {INVALID}, + {MTC1}, {DMTC1}, {CTC1}, {INVALID}, + {BC1}, {INVALID}, {INVALID}, {INVALID}, + {INVALID}, {INVALID}, {INVALID}, {INVALID}, + {FPUS}, {FPUD}, {INVALID}, {INVALID}, + {FPUW}, {FPUL}, {INVALID}, {INVALID}, + {INVALID}, {INVALID}, {INVALID}, {INVALID}, + {INVALID}, {INVALID}, {INVALID}, {INVALID} +}; + +// ============================================================================ +// Escaped opcode table: COP2. +// +// 31--------26-25------21 ----------------------------------------0 +// | COP2/6 | FMT/5 | | +// ------6----------5----------------------------------------------- +// |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| +// 00 | MFC2 | DMFC2 | CFC2 | --- | MTC2 | DMTC2 | CTC2 | --- | +// 01 | BC2 | --- | --- | --- | --- | --- | --- | --- | +// 10 | --- | --- | --- | --- | --- | --- | --- | --- | +// 11 | --- | --- | --- | --- | --- | --- | --- | --- | +// |-------|-------|-------|-------|-------|-------|-------|-------| +// +// ============================================================================ +cen64_align(static const struct vr4300_opcode + vr4300_cop2_opcode_table[32], CACHE_LINE_SIZE) = { + {MFC2}, {DMFC2}, {CFC2}, {INVALID}, + {MTC2}, {DMTC2}, {CTC2}, {INVALID}, + {BC2}, {INVALID}, {INVALID}, {INVALID}, + {INVALID}, {INVALID}, {INVALID}, {INVALID}, + {INVALID}, {INVALID}, {INVALID}, {INVALID}, + {INVALID}, {INVALID}, {INVALID}, {INVALID}, + {INVALID}, {INVALID}, {INVALID}, {INVALID}, + {INVALID}, {INVALID}, {INVALID}, {INVALID} +}; + +// ============================================================================ +// First-order opcode table. +// +// 0b000000 => Lookup in vr4300_spec_opcode_table. +// 0b000001 => Lookup in vr4300_regimm_opcode_table. +// 0b010000 => Lookup in vr4300_cop0_opcode_table. +// 0b010001 => Lookup in vr4300_cop0_opcode_table. +// 0b010010 => Lookup in vr4300_cop2_opcode_table. +// +// 31---------26---------------------------------------------------0 +// | OPCODE/6 | | +// ------6---------------------------------------------------------- +// |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| +// 000 | *SPEC | *RGIM | J | JAL | BEQ | BNE | BLEZ | BGTZ | +// 001 | ADDI | ADDIU | SLTI | SLTIU | ANDI | ORI | XORI | LUI | +// 010 | *COP0 | *COP1 | *COP2 | | BEQL | BNEL | BLEZL | BGTZL | +// 011 | DADDI |DADDIU | LDL | LDR | | | | | +// 100 | LB | LH | LWL | LW | LBU | LHU | LWR | LWU | +// 101 | SB | SH | SWL | SW | SDL | SDR | SWR | CACHE | +// 110 | LL | LWC1 | LWC2 | | LLD | LDC1 | LDC2 | LD | +// 111 | SC | SWC1 | SWC2 | | SCD | SDC1 | SDC2 | SD | +// |-------|-------|-------|-------|-------|-------|-------|-------| +// +// ============================================================================ +cen64_align(static const struct vr4300_opcode + vr4300_opcode_table[64], CACHE_LINE_SIZE) = { + {INVALID}, {INVALID}, {J}, {JAL}, + {BEQ}, {BNE}, {BLEZ}, {BGTZ}, + {ADDI}, {ADDIU}, {SLTI}, {SLTIU}, + {ANDI}, {ORI}, {XORI}, {LUI}, + {INVALID}, {INVALID}, {INVALID}, {INVALID}, + {BEQL}, {BNEL}, {BLEZL}, {BGTZL}, + {DADDI}, {DADDIU}, {LDL}, {LDR}, + {INVALID}, {INVALID}, {INVALID}, {INVALID}, + {LB}, {LH}, {LWL}, {LW}, + {LBU}, {LHU}, {LWR}, {LWU}, + {SB}, {SH}, {SWL}, {SW}, + {SDL}, {SDR}, {SWR}, {CACHE}, + {LL}, {LWC1}, {LWC2}, {INVALID}, + {LLD}, {LDC1}, {LDC2}, {LD}, + {SC}, {SWC1}, {SWC2}, {INVALID}, + {SCD}, {SDC1}, {SDC2}, {SD} +}; + +// Escaped table listings. Most of these will never +// see a processor cache line, so not much waste here. +struct vr4300_opcode_escape { + const struct vr4300_opcode *table; + unsigned shift, mask; +}; + +cen64_align(static const struct vr4300_opcode_escape + vr4300_escape_table[64], CACHE_LINE_SIZE) = { + {vr4300_spec_opcode_table, 0, 0x3F}, {vr4300_regimm_opcode_table, 16, 0x1F}, + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, + + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, + + {vr4300_cop0_opcode_table, 21, 0x1F}, {vr4300_cop1_opcode_table, 21, 0x1F}, + {vr4300_cop2_opcode_table, 21, 0x1F}, {vr4300_opcode_table, 26, 0x3F}, + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, + + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, + + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, + + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, + + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, + + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, + {vr4300_opcode_table, 26, 0x3F}, {vr4300_opcode_table, 26, 0x3F}, +}; + +// Decodes an instruction word. +const struct vr4300_opcode* vr4300_decode_instruction(uint32_t iw) { + const struct vr4300_opcode_escape *escape = vr4300_escape_table + (iw >> 26); + unsigned index = iw >> escape->shift & escape->mask; + return escape->table + index; +} + diff --git a/vr4300/decoder.h b/vr4300/decoder.h new file mode 100644 index 000000000..0749f1955 --- /dev/null +++ b/vr4300/decoder.h @@ -0,0 +1,35 @@ +// +// vr4300/decoder.h: VR4300 decoder. +// +// CEN64: Cycle-Accurate Nintendo 64 Simulator. +// Copyright (C) 2014, Tyler J. Stachecki. +// +// This file is subject to the terms and conditions defined in +// 'LICENSE', which is part of this source code package. +// + +#ifndef __vr4300_decoder_h__ +#define __vr4300_decoder_h__ +#include "common.h" +#include "vr4300/opcodes.h" + +#define GET_RS(opcode) ((opcode) >> 21 & 0x1F) +#define GET_RT(opcode) ((opcode) >> 16 & 0x1F) +#define GET_RD(opcode) ((opcode) >> 11 & 0x1F) + +#define GET_FS(opcode) ((opcode) >> 11 & 0x1F) +#define GET_FT(opcode) ((opcode) >> 16 & 0x1F) +#define GET_FD(opcode) ((opcode) >> 6 & 0x1F) + +#define OPCODE_INFO_NONE (0) +#define OPCODE_INFO_BRANCH (1 << 1) + +struct vr4300_opcode { + uint8_t id; + uint8_t flags; +}; + +const struct vr4300_opcode* vr4300_decode_instruction(uint32_t); + +#endif + diff --git a/vr4300/docs/vr43xx.pdf b/vr4300/docs/vr43xx.pdf new file mode 100644 index 000000000..dabe6c0e6 Binary files /dev/null and b/vr4300/docs/vr43xx.pdf differ diff --git a/vr4300/fault.c b/vr4300/fault.c new file mode 100644 index 000000000..acdb9e2a3 --- /dev/null +++ b/vr4300/fault.c @@ -0,0 +1,96 @@ +// +// vr4300/fault.c: VR4300 fault management. +// +// CEN64: Cycle-Accurate Nintendo 64 Simulator. +// Copyright (C) 2014, Tyler J. Stachecki. +// +// This file is subject to the terms and conditions defined in +// 'LICENSE', which is part of this source code package. +// + +#include "common.h" +#include "bus/controller.h" +#include "vr4300/cp0.h" +#include "vr4300/cpu.h" +#include "vr4300/fault.h" +#include "vr4300/pipeline.h" + +const char *vr4300_fault_mnemonics[NUM_VR4300_FAULTS] = { +#define X(fault) #fault, +#include "vr4300/fault.md" +#undef X +}; + +// Sets attributes common to all exceptions. +static void vr4300_common_exceptions(struct vr4300_pipeline *pipeline) { + pipeline->icrf_latch.segment = get_default_segment(); + + pipeline->exception_history = 0; + pipeline->fault_present = true; + pipeline->cycles_to_stall = 2; + pipeline->skip_stages = 0; +} + +// Sets attributes common to all interlocks. +static void vr4300_common_interlocks(struct vr4300_pipeline *pipeline, + unsigned cycles_to_stall, unsigned skip_stages) { + pipeline->cycles_to_stall = cycles_to_stall; + pipeline->skip_stages = skip_stages; +} + +// Raise a fault that originated in the DC stage. +static void vr4300_dc_fault(struct vr4300_pipeline *pipeline, + enum vr4300_fault_id fault) { + vr4300_common_exceptions(pipeline); + pipeline->exdc_latch.common.fault = fault; + pipeline->rfex_latch.common.fault = fault; + pipeline->icrf_latch.common.fault = fault; +} + +// IADE: Instruction address error exception +void VR4300_IADE(unused(struct vr4300 *vr4300)) { + abort(); +} + +// UNC: Uncached read interlock. +void VR4300_UNC(struct vr4300 *vr4300) { + struct vr4300_pipeline *pipeline = &vr4300->pipeline; + struct vr4300_icrf_latch *icrf_latch = &pipeline->icrf_latch; + struct vr4300_rfex_latch *rfex_latch = &pipeline->rfex_latch; + const struct segment *segment = icrf_latch->segment; + uint64_t address; + + vr4300_common_interlocks(pipeline, ~0, 4); + + address = icrf_latch->common.pc - segment->offset; + bus_read_word(vr4300->bus, address, &rfex_latch->iw); + pipeline->cycles_to_stall = 50; +} + +// RST: External reset exception. +void VR4300_RST(struct vr4300 *vr4300) { + struct vr4300_pipeline *pipeline = &vr4300->pipeline; + struct vr4300_cp0 *cp0 = &vr4300->cp0; + + // Prepare pipeline for restart. + vr4300->pipeline.icrf_latch.pc = 0xFFFFFFFFBFC00000ULL; + vr4300_dc_fault(pipeline, VR4300_FAULT_RST); + + // Cold reset exception. + if (vr4300->signals & VR4300_SIGNAL_COLDRESET) { + vr4300->signals &= ~VR4300_SIGNAL_COLDRESET; + + cp0->regs[VR4300_CP0_REGISTER_STATUS] &= ~0x08300000ULL; + cp0->regs[VR4300_CP0_REGISTER_CONFIG] &= ~0xFFFF7FF0ULL; + + cp0->regs[VR4300_CP0_REGISTER_STATUS] |= 0x00400004ULL; + cp0->regs[VR4300_CP0_REGISTER_CONFIG] |= 0x7006E460ULL; + + cp0->regs[VR4300_CP0_REGISTER_RANDOM] = 31; + } + + // Soft reset exception. + else + abort(); +} + diff --git a/vr4300/fault.h b/vr4300/fault.h new file mode 100644 index 000000000..053865ab3 --- /dev/null +++ b/vr4300/fault.h @@ -0,0 +1,30 @@ +// +// vr4300/fault.h: VR4300 fault management. +// +// CEN64: Cycle-Accurate Nintendo 64 Simulator. +// Copyright (C) 2014, Tyler J. Stachecki. +// +// This file is subject to the terms and conditions defined in +// 'LICENSE', which is part of this source code package. +// + +#ifndef __vr4300_fault_h__ +#define __vr4300_fault_h__ + +struct vr4300; + +enum vr4300_fault_id { +#define X(fault) VR4300_FAULT_##fault, +#include "vr4300/fault.md" + NUM_VR4300_FAULTS +#undef X +}; + +extern const char *vr4300_fault_mnemonics[NUM_VR4300_FAULTS]; + +#define X(fault) void VR4300_##fault(struct vr4300 *vr4300); +#include "vr4300/fault.md" +#undef X + +#endif + diff --git a/vr4300/fault.md b/vr4300/fault.md new file mode 100644 index 000000000..938db0777 --- /dev/null +++ b/vr4300/fault.md @@ -0,0 +1,19 @@ +// +// vr4300/fault.md: VR4300 fault management. +// +// CEN64: Cycle-Accurate Nintendo 64 Simulator. +// Copyright (C) 2014, Tyler J. Stachecki. +// +// This file is subject to the terms and conditions defined in +// 'LICENSE', which is part of this source code package. +// + +#ifndef VR4300_FAULT_LIST +#define VR4300_FAULT_LIST \ + X(NONE) X(CP0I) X(RST) X(NMI) X(OVFL) X(TRAP) X(FPE) X(DADE) X(DTLB) \ + X(WAT) X(INTR) X(DCM) X(DCB) X(COP) X(DBE) X(SYSC) X(BRPT) X(CPU) \ + X(RSVD) X(LDI) X(MCI) X(IADE) X(ITM) X(ICB) X(UNC) X(IBE) +#endif + +VR4300_FAULT_LIST + diff --git a/vr4300/icache.c b/vr4300/icache.c new file mode 100644 index 000000000..e244e0255 --- /dev/null +++ b/vr4300/icache.c @@ -0,0 +1,121 @@ +// +// vr4300/icache.c: VR4300 instruction cache. +// +// CEN64: Cycle-Accurate Nintendo 64 Simulator. +// Copyright (C) 2014, Tyler J. Stachecki. +// +// This file is subject to the terms and conditions defined in +// 'LICENSE', which is part of this source code package. +// + +#include "common.h" +#include "vr4300/icache.h" + +static inline struct vr4300_icache_line* get_line( + struct vr4300_icache *icache, uint64_t vaddr); +static inline const struct vr4300_icache_line* get_line_const( + const struct vr4300_icache *icache, uint64_t vaddr); + +static inline uint32_t get_tag(const struct vr4300_icache_line *line); +static void invalidate_line(struct vr4300_icache_line *line); +static bool is_valid(const struct vr4300_icache_line *line); +static void set_tag(struct vr4300_icache_line *line, uint32_t tag); +static void validate_line(struct vr4300_icache_line *line, uint32_t tag); + +// Returns the line for a given virtual address. +struct vr4300_icache_line* get_line( + struct vr4300_icache *icache, uint64_t vaddr) { + return icache->lines + (vaddr >> 5 & 0x1FF); +} + +// Returns the line for a given virtual address. +const struct vr4300_icache_line* get_line_const( + const struct vr4300_icache *icache, uint64_t vaddr) { + return icache->lines + (vaddr >> 5 & 0x1FF); +} + +// Returns the physical tag associated with the line. +uint32_t get_tag(const struct vr4300_icache_line *line) { + return line->metadata >> 12; +} + +// Invalidates the line, but leaves the physical tag untouched. +void invalidate_line(struct vr4300_icache_line *line) { + line->metadata &= ~0x1; +} + +// Returns true if the line is valid, otherwise returns false. +bool is_valid(const struct vr4300_icache_line *line) { + return (line->metadata & 0x1) == 0x1; +} + +// Sets the tag of the specified line, retaining current valid bit. +void set_tag(struct vr4300_icache_line *line, uint32_t tag) { + line->metadata = (tag << 12) | (line->metadata & 0x1); +} + +// Sets the line's physical tag and validates the line. +static void validate_line(struct vr4300_icache_line *line, uint32_t tag) { + line->metadata = (tag << 12) | 0x1; +} + +// Fills an instruction cache line with data. +void vr4300_icache_fill(struct vr4300_icache *icache, + uint64_t vaddr, uint32_t paddr, const void *data) { + struct vr4300_icache_line *line = get_line(icache, vaddr); + + memcpy(line->data, data, sizeof(line->data)); + validate_line(line, paddr >> 5); +} + +// Returns the tag of the line associated with vaddr. +uint32_t vr4300_icache_get_tag(const struct vr4300_icache *icache, + uint64_t vaddr) { + const struct vr4300_icache_line *line = get_line_const(icache, vaddr); + + return get_tag(line); +} + +// Initializes the instruction cache. +void vr4300_icache_init(struct vr4300_icache *icache) { + memset(icache->lines, 0, sizeof(icache->lines)); + +} + +// Invalidates an instruction cache line (regardless if hit or miss). +void vr4300_icache_invalidate(struct vr4300_icache *icache, uint64_t vaddr) { + struct vr4300_icache_line *line = get_line(icache, vaddr); + + invalidate_line(line); +} + +// Invalidates an instruction cache line (only on a hit). +void vr4300_icache_invalidate_hit(struct vr4300_icache *icache, + uint64_t vaddr, uint32_t paddr) { + struct vr4300_icache_line *line = get_line(icache, vaddr); + uint32_t ptag = get_tag(line); + + if (ptag == (paddr >> 5) && is_valid(line)) + invalidate_line(line); +} + +// Probes the instruction cache for a matching line. +const struct vr4300_icache_line* vr4300_icache_probe( + const struct vr4300_icache *icache, uint64_t vaddr, uint32_t paddr) { + const struct vr4300_icache_line *line = get_line_const(icache, vaddr); + uint32_t ptag = get_tag(line); + + // Virtually index, and physically tagged. + return (ptag == (paddr >> 5) && is_valid(line)) + ? line + : NULL; +} + +// Sets the physical tag associated with the line. +void vr4300_icache_set_tag(struct vr4300_icache *icache, + uint64_t vaddr, uint32_t tag) { + struct vr4300_icache_line *line = get_line(icache, vaddr); + + set_tag(line, tag); +} + diff --git a/vr4300/icache.h b/vr4300/icache.h new file mode 100644 index 000000000..aff3211e7 --- /dev/null +++ b/vr4300/icache.h @@ -0,0 +1,41 @@ +// +// vr4300/icache.h: VR4300 instruction cache. +// +// CEN64: Cycle-Accurate Nintendo 64 Simulator. +// Copyright (C) 2014, Tyler J. Stachecki. +// +// This file is subject to the terms and conditions defined in +// 'LICENSE', which is part of this source code package. +// + +#ifndef __vr4300_icache_h__ +#define __vr4300_icache_h__ +#include "common.h" + +struct vr4300_icache_line { + uint8_t data[8 * 4]; + uint32_t metadata; +}; + +struct vr4300_icache { + struct vr4300_icache_line lines[512]; +}; + +void vr4300_icache_init(struct vr4300_icache *icache); +int vr4300_icache_serialize(struct vr4300_icache *icache, FILE *f); +int vr4300_icache_unserialize(struct vr4300_icache *icache, FILE *f); + +void vr4300_icache_fill(struct vr4300_icache *icache, + uint64_t vaddr, uint32_t paddr, const void *data); +uint32_t vr4300_icache_get_tag(const struct vr4300_icache *icache, + uint64_t vaddr); +void vr4300_icache_invalidate(struct vr4300_icache *icache, uint64_t vaddr); +void vr4300_icache_invalidate_hit(struct vr4300_icache *icache, + uint64_t vaddr, uint32_t paddr); +const struct vr4300_icache_line* vr4300_icache_probe( + const struct vr4300_icache *icache, uint64_t vaddr, uint32_t paddr); +void vr4300_icache_set_tag(struct vr4300_icache *icache, + uint64_t vaddr, uint32_t tag); + +#endif + diff --git a/vr4300/opcodes.c b/vr4300/opcodes.c new file mode 100644 index 000000000..3a9915936 --- /dev/null +++ b/vr4300/opcodes.c @@ -0,0 +1,18 @@ +// +// vr4300/opcodes.c: VR4300 opcode types and info. +// +// CEN64: Cycle-Accurate Nintendo 64 Simulator. +// Copyright (C) 2014, Tyler J. Stachecki. +// +// This file is subject to the terms and conditions defined in +// 'LICENSE', which is part of this source code package. +// + +#include "vr4300/opcodes.h" + +const char *vr4300_opcode_mnemonics[NUM_VR4300_OPCODES] = { +#define X(op) #op, +#include "vr4300/opcodes.md" +#undef X +}; + diff --git a/vr4300/opcodes.h b/vr4300/opcodes.h new file mode 100644 index 000000000..269a4dcda --- /dev/null +++ b/vr4300/opcodes.h @@ -0,0 +1,190 @@ +// +// vr4300/opcodes.h: VR4300 opcode types and info. +// +// CEN64: Cycle-Accurate Nintendo 64 Simulator. +// Copyright (C) 2014, Tyler J. Stachecki. +// +// This file is subject to the terms and conditions defined in +// 'LICENSE', which is part of this source code package. +// + +#ifndef __vr4300_opcodes_h__ +#define __vr4300_opcodes_h__ +#include "common.h" + +enum vr4300_opcode_id { +#define X(op) VR4300_OPCODE_##op, +#include "vr4300/opcodes.md" + NUM_VR4300_OPCODES +#undef X +}; + +extern const char *vr4300_opcode_mnemonics[NUM_VR4300_OPCODES]; + +/* Flags for each instruction. */ +#define VR4300_BUILD_OP(op, flags) \ + (VR4300_OPCODE_##op), (flags) + +#define INFO1(x) (OPCODE_INFO_##x) +#define INFO2(x,y) (INFO1(x) | OPCODE_INFO_##y) +#define INFO3(x,y,z) (INFO2(x,y) | OPCODE_INFO_##z) +#define INFO4(x,y,z,a) (INFO3(x,y,z) | OPCODE_INFO_##a) +#define INFO5(x,y,z,a,b) (INFO4(x,y,z,a) | OPCODE_INFO_##b) +#define INVALID VR4300_BUILD_OP(INV, INFO1(NONE)) + +#define BC0 VR4300_BUILD_OP(BC0, INFO1(BRANCH)) +#define BC1 VR4300_BUILD_OP(BC1, INFO1(BRANCH)) +#define BC2 VR4300_BUILD_OP(BC1, INFO1(BRANCH)) +#define FPUS VR4300_BUILD_OP(FPUS, INFO1(NONE)) +#define FPUD VR4300_BUILD_OP(FPUD, INFO1(NONE)) +#define FPUW VR4300_BUILD_OP(FPUW, INFO1(NONE)) +#define FPUL VR4300_BUILD_OP(FPUL, INFO1(NONE)) +#define TLB VR4300_BUILD_OP(TLB, INFO1(NONE)) + +#define ADD VR4300_BUILD_OP(ADD, INFO1(NONE)) +#define ADDI VR4300_BUILD_OP(ADDI, INFO1(NONE)) +#define ADDIU VR4300_BUILD_OP(ADDIU, INFO1(NONE)) +#define ADDU VR4300_BUILD_OP(ADDU, INFO1(NONE)) +#define AND VR4300_BUILD_OP(AND, INFO1(NONE)) +#define ANDI VR4300_BUILD_OP(ANDI, INFO1(NONE)) +#define BEQ VR4300_BUILD_OP(BEQ, INFO1(BRANCH)) +#define BEQL VR4300_BUILD_OP(BEQL, INFO1(BRANCH)) +#define BGEZ VR4300_BUILD_OP(BGEZ, INFO1(BRANCH)) +#define BGEZAL VR4300_BUILD_OP(BGEZAL, INFO1(BRANCH)) +#define BGEZALL VR4300_BUILD_OP(BGEZALL, INFO1(BRANCH)) +#define BGEZL VR4300_BUILD_OP(BGEZL, INFO1(BRANCH)) +#define BGTZ VR4300_BUILD_OP(BGTZ, INFO1(BRANCH)) +#define BGTZL VR4300_BUILD_OP(BGTZL, INFO1(BRANCH)) +#define BLEZ VR4300_BUILD_OP(BLEZ, INFO1(BRANCH)) +#define BLEZL VR4300_BUILD_OP(BLEZL, INFO1(BRANCH)) +#define BLTZ VR4300_BUILD_OP(BLTZ, INFO1(BRANCH)) +#define BLTZAL VR4300_BUILD_OP(BLTZAL, INFO1(BRANCH)) +#define BLTZALL VR4300_BUILD_OP(BLTZALL, INFO1(BRANCH)) +#define BLTZL VR4300_BUILD_OP(BLTZL, INFO1(BRANCH)) +#define BNE VR4300_BUILD_OP(BNE, INFO1(BRANCH)) +#define BNEL VR4300_BUILD_OP(BNEL, INFO1(BRANCH)) +#define BREAK VR4300_BUILD_OP(BREAK, INFO1(NONE)) +#define CACHE VR4300_BUILD_OP(CACHE, INFO1(NONE)) +#define CFC0 VR4300_BUILD_OP(CFC0, INFO1(NONE)) +#define CFC1 VR4300_BUILD_OP(CFC1, INFO1(NONE)) +#define CFC2 VR4300_BUILD_OP(CFC2, INFO1(NONE)) +#define COP0 VR4300_BUILD_OP(COP0, INFO1(NONE)) +#define COP1 VR4300_BUILD_OP(COP1, INFO1(NONE)) +#define COP2 VR4300_BUILD_OP(COP2, INFO1(NONE)) +#define CTC0 VR4300_BUILD_OP(CTC0, INFO1(NONE)) +#define CTC1 VR4300_BUILD_OP(CTC1, INFO1(NONE)) +#define CTC2 VR4300_BUILD_OP(CTC2, INFO1(NONE)) +#define DADD VR4300_BUILD_OP(DADD, INFO1(NONE)) +#define DADDI VR4300_BUILD_OP(DADDI, INFO1(NONE)) +#define DADDIU VR4300_BUILD_OP(DADDIU, INFO1(NONE)) +#define DADDU VR4300_BUILD_OP(DADDU, INFO1(NONE)) +#define DDIV VR4300_BUILD_OP(DDIV, INFO1(NONE)) +#define DDIVU VR4300_BUILD_OP(DDIVU, INFO1(NONE)) +#define DIV VR4300_BUILD_OP(DIV, INFO1(NONE)) +#define DIVU VR4300_BUILD_OP(DIVU, INFO1(NONE)) +#define DMFC0 VR4300_BUILD_OP(DMFC0, INFO1(NONE)) +#define DMFC1 VR4300_BUILD_OP(DMFC1, INFO1(NONE)) +#define DMFC2 VR4300_BUILD_OP(DMFC2, INFO1(NONE)) +#define DMTC0 VR4300_BUILD_OP(DMTC0, INFO1(NONE)) +#define DMTC1 VR4300_BUILD_OP(DMTC1, INFO1(NONE)) +#define DMTC2 VR4300_BUILD_OP(DMTC2, INFO1(NONE)) +#define DMULT VR4300_BUILD_OP(DMULT, INFO1(NONE)) +#define DMULTU VR4300_BUILD_OP(DMULTU, INFO1(NONE)) +#define DSLL VR4300_BUILD_OP(DSLL, INFO1(NONE)) +#define DSLLV VR4300_BUILD_OP(DSLLV, INFO1(NONE)) +#define DSLL32 VR4300_BUILD_OP(DSLL32, INFO1(NONE)) +#define DSRA VR4300_BUILD_OP(DSRA, INFO1(NONE)) +#define DSRAV VR4300_BUILD_OP(DSRAV, INFO1(NONE)) +#define DSRA32 VR4300_BUILD_OP(DSRA32, INFO1(NONE)) +#define DSRL VR4300_BUILD_OP(DSRL, INFO1(NONE)) +#define DSRLV VR4300_BUILD_OP(DSRLV, INFO1(NONE)) +#define DSRL32 VR4300_BUILD_OP(DSRL32, INFO1(NONE)) +#define DSUB VR4300_BUILD_OP(DSUB, INFO1(NONE)) +#define DSUBU VR4300_BUILD_OP(DSUBU, INFO1(NONE)) +#define ERET VR4300_BUILD_OP(ERET, INFO1(NONE)) +#define J VR4300_BUILD_OP(J, INFO1(BRANCH)) +#define JAL VR4300_BUILD_OP(JAL, INFO1(BRANCH)) +#define JALR VR4300_BUILD_OP(JALR, INFO1(BRANCH)) +#define JR VR4300_BUILD_OP(JR, INFO1(BRANCH)) +#define LB VR4300_BUILD_OP(LB, INFO1(NONE)) +#define LBU VR4300_BUILD_OP(LBU, INFO1(NONE)) +#define LD VR4300_BUILD_OP(LD, INFO1(NONE)) +#define LDC0 VR4300_BUILD_OP(LDC0, INFO1(NONE)) +#define LDC1 VR4300_BUILD_OP(LDC1, INFO1(NONE)) +#define LDC2 VR4300_BUILD_OP(LDC2, INFO1(NONE)) +#define LDL VR4300_BUILD_OP(LDL, INFO1(NONE)) +#define LDR VR4300_BUILD_OP(LDR, INFO1(NONE)) +#define LH VR4300_BUILD_OP(LH, INFO1(NONE)) +#define LHU VR4300_BUILD_OP(LHU, INFO1(NONE)) +#define LL VR4300_BUILD_OP(LL, INFO1(NONE)) +#define LLD VR4300_BUILD_OP(LLD, INFO1(NONE)) +#define LUI VR4300_BUILD_OP(LUI, INFO1(NONE)) +#define LW VR4300_BUILD_OP(LW, INFO1(NONE)) +#define LWC0 VR4300_BUILD_OP(LWC0, INFO1(NONE)) +#define LWC1 VR4300_BUILD_OP(LWC1, INFO1(NONE)) +#define LWC2 VR4300_BUILD_OP(LWC2, INFO1(NONE)) +#define LWL VR4300_BUILD_OP(LWL, INFO1(NONE)) +#define LWR VR4300_BUILD_OP(LWR, INFO1(NONE)) +#define LWU VR4300_BUILD_OP(LWU, INFO1(NONE)) +#define MFC0 VR4300_BUILD_OP(MFC0, INFO1(NONE)) +#define MFC1 VR4300_BUILD_OP(MFC1, INFO1(NONE)) +#define MFC2 VR4300_BUILD_OP(MFC2, INFO1(NONE)) +#define MFHI VR4300_BUILD_OP(MFHI, INFO1(NONE)) +#define MFLO VR4300_BUILD_OP(MFLO, INFO1(NONE)) +#define MTC0 VR4300_BUILD_OP(MTC0, INFO1(NONE)) +#define MTC1 VR4300_BUILD_OP(MTC1, INFO1(NONE)) +#define MTC2 VR4300_BUILD_OP(MTC2, INFO1(NONE)) +#define MTHI VR4300_BUILD_OP(MTHI, INFO1(NONE)) +#define MTLO VR4300_BUILD_OP(MTLO, INFO1(NONE)) +#define MULT VR4300_BUILD_OP(MULT, INFO1(NONE)) +#define MULTU VR4300_BUILD_OP(MULTU, INFO1(NONE)) +#define NOR VR4300_BUILD_OP(NOR, INFO1(NONE)) +#define OR VR4300_BUILD_OP(OR, INFO1(NONE)) +#define ORI VR4300_BUILD_OP(ORI, INFO1(NONE)) +#define SB VR4300_BUILD_OP(SB, INFO1(NONE)) +#define SC VR4300_BUILD_OP(SC, INFO1(NONE)) +#define SCD VR4300_BUILD_OP(SCD, INFO1(NONE)) +#define SD VR4300_BUILD_OP(SD, INFO1(NONE)) +#define SDC0 VR4300_BUILD_OP(SDC0, INFO1(NONE)) +#define SDC1 VR4300_BUILD_OP(SDC1, INFO1(NONE)) +#define SDC2 VR4300_BUILD_OP(SDC2, INFO1(NONE)) +#define SDL VR4300_BUILD_OP(SDL, INFO1(NONE)) +#define SDR VR4300_BUILD_OP(SDR, INFO1(NONE)) +#define SH VR4300_BUILD_OP(SH, INFO1(NONE)) +#define SLL VR4300_BUILD_OP(SLL, INFO1(NONE)) +#define SLLV VR4300_BUILD_OP(SLLV, INFO1(NONE)) +#define SLT VR4300_BUILD_OP(SLT, INFO1(NONE)) +#define SLTI VR4300_BUILD_OP(SLTI, INFO1(NONE)) +#define SLTIU VR4300_BUILD_OP(SLTIU, INFO1(NONE)) +#define SLTU VR4300_BUILD_OP(SLTU, INFO1(NONE)) +#define SRA VR4300_BUILD_OP(SRA, INFO1(NONE)) +#define SRAV VR4300_BUILD_OP(SRAV, INFO1(NONE)) +#define SRL VR4300_BUILD_OP(SRL, INFO1(NONE)) +#define SRLV VR4300_BUILD_OP(SRLV, INFO1(NONE)) +#define SUB VR4300_BUILD_OP(SUB, INFO1(NONE)) +#define SUBU VR4300_BUILD_OP(SUBU, INFO1(NONE)) +#define SW VR4300_BUILD_OP(SW, INFO1(NONE)) +#define SWC0 VR4300_BUILD_OP(SWC0, INFO1(NONE)) +#define SWC1 VR4300_BUILD_OP(SWC1, INFO1(NONE)) +#define SWC2 VR4300_BUILD_OP(SWC2, INFO1(NONE)) +#define SWL VR4300_BUILD_OP(SWL, INFO1(NONE)) +#define SWR VR4300_BUILD_OP(SWR, INFO1(NONE)) +#define SYNC VR4300_BUILD_OP(SYNC, INFO1(NONE)) +#define SYSCALL VR4300_BUILD_OP(SYSCALL, INFO1(NONE)) +#define TEQ VR4300_BUILD_OP(TEQ, INFO1(NONE)) +#define TEQI VR4300_BUILD_OP(TEQI, INFO1(NONE)) +#define TGE VR4300_BUILD_OP(TGE, INFO1(NONE)) +#define TGEI VR4300_BUILD_OP(TGEI, INFO1(NONE)) +#define TGEIU VR4300_BUILD_OP(TGEIU, INFO1(NONE)) +#define TGEU VR4300_BUILD_OP(TGEU, INFO1(NONE)) +#define TLT VR4300_BUILD_OP(TLT, INFO1(NONE)) +#define TLTI VR4300_BUILD_OP(TLTI, INFO1(NONE)) +#define TLTIU VR4300_BUILD_OP(TLTIU, INFO1(NONE)) +#define TLTU VR4300_BUILD_OP(TLTU, INFO1(NONE)) +#define TNE VR4300_BUILD_OP(TNE, INFO1(NONE)) +#define TNEI VR4300_BUILD_OP(TNEI, INFO1(NONE)) +#define XOR VR4300_BUILD_OP(XOR, INFO1(NONE)) +#define XORI VR4300_BUILD_OP(XORI, INFO1(NONE)) + +#endif + diff --git a/vr4300/opcodes.md b/vr4300/opcodes.md new file mode 100644 index 000000000..adfa96e7d --- /dev/null +++ b/vr4300/opcodes.md @@ -0,0 +1,34 @@ +// +// vr4300/opcodes.md: VR4300 opcode types and info. +// +// CEN64: Cycle-Accurate Nintendo 64 Simulator. +// Copyright (C) 2014, Tyler J. Stachecki. +// +// This file is subject to the terms and conditions defined in +// 'LICENSE', which is part of this source code package. +// + +#ifndef VR4300_OPCODE_TABLE +#define VR4300_OPCODE_TABLE X(INV) \ + X(ADD) X(ADDI) X(ADDIU) X(ADDU) X(AND) X(ANDI) X(BC0) X(BC1) X(BC2) \ + X(BEQ) X(BEQL) X(BGEZ) X(BGEZAL) X(BGEZALL) X(BGEZL) X(BGTZ) X(BGTZL) \ + X(BLEZ) X(BLEZL) X(BLTZ) X(BLTZAL) X(BLTZALL) X(BLTZL) X(BNE) X(BNEL) \ + X(BREAK) X(CACHE) X(CFC0) X(CFC1) X(CFC2) X(COP0) X(COP1) X(COP2) \ + X(CTC0) X(CTC1) X(CTC2) X(DADD) X(DADDI) X(DADDIU) X(DADDU) X(DDIV) \ + X(DDIVU) X(DIV) X(DIVU) X(DMFC0) X(DMFC1) X(DMFC2) X(DMTC0) X(DMTC1) \ + X(DMTC2) X(DMULT) X(DMULTU) X(DSLL) X(DSLLV) X(DSLL32) X(DSRA) X(DSRAV) \ + X(DSRA32) X(DSRL) X(DSRLV) X(DSRL32) X(DSUB) X(DSUBU) X(FPUD) X(FPUL) \ + X(FPUS) X(FPUW) X(J) X(JAL) X(JALR) X(JR) X(LB) X(LBU) X(LD) X(LDC0) \ + X(LDC1) X(LDC2) X(LDL) X(LDR) X(LH) X(LHU) X(LL) X(LLD) X(LUI) X(LW) \ + X(LWC0) X(LWC1) X(LWC2) X(LWL) X(LWR) X(LWU) X(MFC0) X(MFC1) X(MFC2) \ + X(MFHI) X(MFLO) X(MTC0) X(MTC1) X(MTC2) X(MTHI) X(MTLO) X(MULT) \ + X(MULTU) X(NOR) X(OR) X(ORI) X(SB) X(SC) X(SCD) X(SD) X(SDC0) X(SDC1) \ + X(SDC2) X(SDL) X(SDR) X(SH) X(SLL) X(SLLV) X(SLT) X(SLTI) X(SLTIU) \ + X(SLTU) X(SRA) X(SRAV) X(SRL) X(SRLV) X(SUB) X(SUBU) X(SW) X(SWC0) \ + X(SWC1) X(SWC2) X(SWL) X(SWR) X(SYNC) X(SYSCALL) X(TEQ) X(TEQI) X(TGE) \ + X(TGEI) X(TGEIU) X(TGEU) X(TLB) X(TLT) X(TLTI) X(TLTIU) X(TLTU) X(TNE) \ + X(TNEI) X(XOR) X(XORI) +#endif + +VR4300_OPCODE_TABLE + diff --git a/vr4300/pipeline.c b/vr4300/pipeline.c new file mode 100644 index 000000000..42cd66001 --- /dev/null +++ b/vr4300/pipeline.c @@ -0,0 +1,285 @@ +// +// vr4300/pipeline.c: VR4300 processor pipeline. +// +// CEN64: Cycle-Accurate Nintendo 64 Simulator. +// Copyright (C) 2014, Tyler J. Stachecki. +// +// This file is subject to the terms and conditions defined in +// 'LICENSE', which is part of this source code package. +// + +#include "common.h" +#include "vr4300/cp0.h" +#include "vr4300/cpu.h" +#include "vr4300/decoder.h" +#include "vr4300/fault.h" +#include "vr4300/pipeline.h" +#include "vr4300/segment.h" + +// Instruction cache stage. +static inline int vr4300_ic_stage (struct vr4300 *vr4300) { + struct vr4300_icrf_latch *icrf_latch = &vr4300->pipeline.icrf_latch; + const struct segment *segment = icrf_latch->segment; + uint64_t pc = icrf_latch->pc; + + icrf_latch->common.pc = pc; + + // Look up the segment that we're in. + if ((pc - segment->start) > segment->length) { + uint32_t cp0_status = vr4300->cp0.regs[VR4300_CP0_REGISTER_STATUS]; + + if (unlikely((segment = get_segment(pc, cp0_status)) == NULL)) { + VR4300_IADE(vr4300); + return 1; + } + + icrf_latch->segment = segment; + } + + // We didn't have an IADE, so reset the status vector. + icrf_latch->common.fault = VR4300_FAULT_NONE; + return 0; +} + +// Register fetch and decode stage. +static inline int vr4300_rf_stage (struct vr4300 *vr4300) { + const struct vr4300_icrf_latch *icrf_latch = &vr4300->pipeline.icrf_latch; + struct vr4300_rfex_latch *rfex_latch = &vr4300->pipeline.rfex_latch; + const struct segment *segment = icrf_latch->segment; + + rfex_latch->common = icrf_latch->common; + + if (!segment->cached) { + VR4300_UNC(vr4300); + return 1; + } + + return 0; +} + +// Execution stage. +static inline int vr4300_ex_stage (struct vr4300 *vr4300) { + const struct vr4300_rfex_latch *rfex_latch = &vr4300->pipeline.rfex_latch; + struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch; + + exdc_latch->common = rfex_latch->common; + return 0; +} + +// Data cache fetch stage. +static inline int vr4300_dc_stage (struct vr4300 *vr4300) { + const struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch; + struct vr4300_dcwb_latch *dcwb_latch = &vr4300->pipeline.dcwb_latch; + + dcwb_latch->common = exdc_latch->common; + return 0; +} + +// Writeback stage. +static inline int vr4300_wb_stage (struct vr4300 *vr4300) { + const struct vr4300_dcwb_latch *dcwb_latch = &vr4300->pipeline.dcwb_latch; + + if (dcwb_latch->common.fault != VR4300_FAULT_NONE) + return 0; + + return 0; +} + +// Advances the processor pipeline by one pclock. +// May have exceptions, so check for aborted stages. +static void vr4300_cycle_slow_wb(struct vr4300 *vr4300) { + struct vr4300_pipeline *pipeline = &vr4300->pipeline; + struct vr4300_dcwb_latch *dcwb_latch = &pipeline->dcwb_latch; + struct vr4300_exdc_latch *exdc_latch = &pipeline->exdc_latch; + struct vr4300_rfex_latch *rfex_latch = &pipeline->rfex_latch; + struct vr4300_icrf_latch *icrf_latch = &pipeline->icrf_latch; + + // If we haven't had exceptions for at least a + // full pipeline's length, switch back to fast mode. + if (pipeline->exception_history++ > 4) + pipeline->fault_present = false; + + if (dcwb_latch->common.fault == VR4300_FAULT_NONE) { + if (vr4300_wb_stage(vr4300)) + return; + } + + else + dcwb_latch->common = exdc_latch->common; + + if (exdc_latch->common.fault == VR4300_FAULT_NONE) { + if (vr4300_dc_stage(vr4300)) + return; + } + + else + exdc_latch->common = rfex_latch->common; + + if (rfex_latch->common.fault == VR4300_FAULT_NONE) { + if (vr4300_ex_stage(vr4300)) + return; + } + + else + rfex_latch->common = icrf_latch->common; + + if (icrf_latch->common.fault == VR4300_FAULT_NONE) + if (vr4300_rf_stage(vr4300)) + return; + + if (vr4300_ic_stage(vr4300)) + return; +} + +// Advances the processor pipeline by one pclock. +// May have exceptions, so check for aborted stages. +// +// Starts from DC stage (WB resolved an interlock). +static void vr4300_cycle_slow_dc(struct vr4300 *vr4300) { + struct vr4300_pipeline *pipeline = &vr4300->pipeline; + struct vr4300_exdc_latch *exdc_latch = &pipeline->exdc_latch; + struct vr4300_rfex_latch *rfex_latch = &pipeline->rfex_latch; + struct vr4300_icrf_latch *icrf_latch = &pipeline->icrf_latch; + + if (exdc_latch->common.fault == VR4300_FAULT_NONE) { + if (vr4300_dc_stage(vr4300)) + return; + } + + else + exdc_latch->common = rfex_latch->common; + + if (rfex_latch->common.fault == VR4300_FAULT_NONE) { + if (vr4300_ex_stage(vr4300)) + return; + } + + else + rfex_latch->common = icrf_latch->common; + + if (icrf_latch->common.fault == VR4300_FAULT_NONE) + if (vr4300_rf_stage(vr4300)) + return; + + if (vr4300_ic_stage(vr4300)) + return; + + pipeline->skip_stages = 0; +} + +// Advances the processor pipeline by one pclock. +// May have exceptions, so check for aborted stages. +// +// Starts from EX stage (DC resolved an interlock). +static void vr4300_cycle_slow_ex(struct vr4300 *vr4300) { + struct vr4300_pipeline *pipeline = &vr4300->pipeline; + struct vr4300_rfex_latch *rfex_latch = &pipeline->rfex_latch; + struct vr4300_icrf_latch *icrf_latch = &pipeline->icrf_latch; + + if (rfex_latch->common.fault == VR4300_FAULT_NONE) { + if (vr4300_ex_stage(vr4300)) + return; + } + + else + rfex_latch->common = icrf_latch->common; + + if (icrf_latch->common.fault == VR4300_FAULT_NONE) + if (vr4300_rf_stage(vr4300)) + return; + + if (vr4300_ic_stage(vr4300)) + return; + + pipeline->skip_stages = 0; +} + +// Advances the processor pipeline by one pclock. +// May have exceptions, so check for aborted stages. +// +// Starts from RF stage (EX resolved an interlock). +static void vr4300_cycle_slow_rf(struct vr4300 *vr4300) { + struct vr4300_pipeline *pipeline = &vr4300->pipeline; + struct vr4300_icrf_latch *icrf_latch = &pipeline->icrf_latch; + + if (icrf_latch->common.fault == VR4300_FAULT_NONE) + if (vr4300_rf_stage(vr4300)) + return; + + if (vr4300_ic_stage(vr4300)) + return; + + pipeline->skip_stages = 0; +} + +// Advances the processor pipeline by one pclock. +// May have exceptions, so check for aborted stages. +// +// Starts from IC stage (RF resolved an interlock). +static void vr4300_cycle_slow_ic(struct vr4300 *vr4300) { + struct vr4300_pipeline *pipeline = &vr4300->pipeline; + + if (vr4300_ic_stage(vr4300)) + return; + + pipeline->skip_stages = 0; +} + +// LUT of stages for fault handling. +typedef void (*pipeline_function)(struct vr4300 *vr4300); +static const pipeline_function pipeline_function_lut[5] = { + vr4300_cycle_slow_wb, + vr4300_cycle_slow_dc, + vr4300_cycle_slow_ex, + vr4300_cycle_slow_rf, + vr4300_cycle_slow_ic, +}; + +// Advances the processor pipeline by one pclock. +void vr4300_cycle(struct vr4300 *vr4300) { + struct vr4300_pipeline *pipeline = &vr4300->pipeline; + + // We're stalling for an interlock, + // or we just took an exception... + if (pipeline->cycles_to_stall > 0) { + pipeline->cycles_to_stall--; + return; + } + + // The reset exception has a very high priority and will abort basically + // anything that's active, even if we have an interlock or something that's + // current active. Thus, we check for it here and handle it early. + if (unlikely(vr4300->signals & VR4300_SIGNAL_COLDRESET)) + VR4300_RST(vr4300); + + // Ordinarily, we would need to check every pipeline stage to see if it is + // aborted, and conditionally not execute it. Since faults are rare, we'll + // only bother checking for aborted stages when we know they can be present. + if (pipeline->fault_present || pipeline->skip_stages) { + pipeline_function_lut[pipeline->skip_stages](vr4300); + return; + } + + if (vr4300_wb_stage(vr4300)) + return; + + if (vr4300_dc_stage(vr4300)) + return; + + if (vr4300_ex_stage(vr4300)) + return; + + if (vr4300_rf_stage(vr4300)) + return; + + if (vr4300_ic_stage(vr4300)) + return; +} + +// Initializes the pipeline with default values. +void vr4300_pipeline_init(struct vr4300_pipeline *pipeline) { + memset(pipeline, 0, sizeof(*pipeline)); + + pipeline->icrf_latch.segment = get_default_segment(); +} + diff --git a/vr4300/pipeline.h b/vr4300/pipeline.h new file mode 100644 index 000000000..3a1ee9452 --- /dev/null +++ b/vr4300/pipeline.h @@ -0,0 +1,59 @@ +// +// vr4300/pipeline.h: VR4300 processor pipeline. +// +// CEN64: Cycle-Accurate Nintendo 64 Simulator. +// Copyright (C) 2014, Tyler J. Stachecki. +// +// This file is subject to the terms and conditions defined in +// 'LICENSE', which is part of this source code package. +// + +#ifndef __vr4300_pipeline_h__ +#define __vr4300_pipeline_h__ +#include "common.h" +#include "vr4300/fault.h" +#include "vr4300/segment.h" + +struct vr4300; + +struct vr4300_latch { + uint64_t pc; + enum vr4300_fault_id fault; + uint32_t cause_data; +}; + +struct vr4300_icrf_latch { + struct vr4300_latch common; + const struct segment *segment; + uint64_t pc; +}; + +struct vr4300_rfex_latch { + struct vr4300_latch common; + uint32_t iw; +}; + +struct vr4300_exdc_latch { + struct vr4300_latch common; +}; + +struct vr4300_dcwb_latch { + struct vr4300_latch common; +}; + +struct vr4300_pipeline { + struct vr4300_dcwb_latch dcwb_latch; + struct vr4300_exdc_latch exdc_latch; + struct vr4300_rfex_latch rfex_latch; + struct vr4300_icrf_latch icrf_latch; + + unsigned exception_history; + unsigned cycles_to_stall; + unsigned skip_stages; + bool fault_present; +}; + +void vr4300_pipeline_init(struct vr4300_pipeline *pipeline); + +#endif + diff --git a/vr4300/segment.c b/vr4300/segment.c new file mode 100644 index 000000000..fbf283d06 --- /dev/null +++ b/vr4300/segment.c @@ -0,0 +1,273 @@ +// +// vr4300/segment.c: VR4300 MMU segment manager. +// +// CEN64: Cycle-Accurate Nintendo 64 Simulator. +// Copyright (C) 2014, Tyler J. Stachecki. +// +// This file is subject to the terms and conditions defined in +// 'LICENSE', which is part of this source code package. +// + +#include "common.h" +#include "vr4300/segment.h" + +// +// Note: sseg, ksseg, kseg0, kseg1, and kseg3 do not appear below. +// +// As stated on pages 128, 130, and 134 of the "NEC VR43xx +// Microprocessor User's Manual": "The VR4300 internally uses 64-bit +// addresses. In the 32-bit mode, a 32-bit value with bits 32 through +// 63 sign-extended is used an address." +// + +static const struct segment USEGs[] = { + /* useg, suseg, kuseg. */ { + 0x0000000000000000ULL, /* start */ + 0x0000000080000000ULL, /* length */ + 0x0000000000000000ULL, /* offset */ + + true, /* mapped */ + true, /* cached */ + + /* xuseg, xsuseg, xkuseg. */ }, { + 0x0000000000000000ULL, /* start */ + 0x0000010000000000ULL, /* length */ + 0x0000000000000000ULL, /* offset */ + + true, /* mapped */ + true, /* cached */ +}}; + +/* xsseg, xksseg. */ +static const struct segment XSSEG = { + 0x4000000000000000ULL, /* start */ + 0x0000010000000000ULL, /* length */ + 0x0000000000000000ULL, /* offset */ + + true, /* mapped */ + true, /* cached */ +}; + +static const struct segment KSEGs[] = { + /* (c)kseg0. */ { + 0xFFFFFFFF80000000ULL, /* start */ + 0x0000000020000000ULL, /* length */ + 0xFFFFFFFF80000000ULL, /* offset */ + + false, /* mapped */ + true, /* cached */ + + /* (c)kseg1. */ }, { + 0xFFFFFFFFA0000000ULL, /* start */ + 0x0000000020000000ULL, /* length */ + 0xFFFFFFFFA0000000ULL, /* offset */ + + false, /* mapped */ + false, /* cached */ + + /* (c)sseg, (c)ksseg. */ }, { + 0xFFFFFFFFC0000000ULL, /* start */ + 0x0000000020000000ULL, /* length */ + 0x0000000000000000ULL, /* offset */ + + true, /* mapped */ + true, /* cached */ + + /* (c)kseg3. */ }, { + 0xFFFFFFFFE0000000ULL, /* start */ + 0x0000000020000000ULL, /* length */ + 0x0000000000000000ULL, /* offset */ + + true, /* mapped */ + true, /* cached */ +}}; + +static const struct segment XKSEG = { + 0xC000000000000000ULL, /* start */ + 0x0000010000000000ULL, /* length */ + 0x0000000000000000ULL, /* offset */ + + true, /* mapped */ + true, /* cached */ +}; + +static const struct segment XKPHYS0 = { + 0x8000000000000000ULL, /* start */ + 0x0000000100000000ULL, /* length */ + 0x8000000000000000ULL, /* offset */ + + false, /* mapped */ + true, /* cached */ +}; + +static const struct segment XKPHYS1 = { + 0x8800000000000000ULL, /* start */ + 0x0000000100000000ULL, /* length */ + 0x8800000000000000ULL, /* offset */ + + false, /* mapped */ + true, /* cached */ +}; + +static const struct segment XKPHYS2 = { + 0x9000000000000000ULL, /* start */ + 0x0000000100000000ULL, /* length */ + 0x9000000000000000ULL, /* offset */ + + false, /* mapped */ + false, /* cached */ +}; + +static const struct segment XKPHYS3 = { + 0x9800000000000000ULL, /* start */ + 0x0000000100000000ULL, /* length */ + 0x9800000000000000ULL, /* offset */ + + false, /* mapped */ + true, /* cached */ +}; + +static const struct segment XKPHYS4 = { + 0xA000000000000000ULL, /* start */ + 0x0000000100000000ULL, /* length */ + 0xA000000000000000ULL, /* offset */ + + false, /* mapped */ + true, /* cached */ +}; + +static const struct segment XKPHYS5 = { + 0xA800000000000000ULL, /* start */ + 0x0000000100000000ULL, /* length */ + 0xA800000000000000ULL, /* offset */ + + false, /* mapped */ + true, /* cached */ +}; + +static const struct segment XKPHYS6 = { + 0xB000000000000000ULL, /* start */ + 0x0000000100000000ULL, /* length */ + 0xB000000000000000ULL, /* offset */ + + false, /* mapped */ + true, /* cached */ +}; + +static const struct segment XKPHYS7 = { + 0xB800000000000000ULL, /* start */ + 0x0000000100000000ULL, /* length */ + 0xB800000000000000ULL, /* offset */ + + false, /* mapped */ + true, /* cached */ +}; + +static const struct segment *kernel_segs_lut[16] = { + &XKPHYS0, + &XKPHYS1, + &XKPHYS2, + &XKPHYS3, + &XKPHYS4, + &XKPHYS5, + &XKPHYS6, + &XKPHYS7, + &XKSEG, + &XKSEG, + &XKSEG, + &XKSEG, + &XKSEG, + &XKSEG, + &XKSEG, + &XKSEG, +}; + + +// Returns a default segment that should cause +// a cached segment miss and result in a lookup. +const struct segment* get_default_segment(void) { + static const struct segment default_segment = { + 1ULL, + 0ULL, + 0ULL, + false, + false, + }; + + return &default_segment; +} + +// Returns the segment given a CP0 status register and a virtual address. +const struct segment* get_segment(uint64_t address, uint32_t cp0_status) { + const struct segment *seg; + + // LUT used to determine if we're in a 64-bit mode or not. + // i.e., if we're in supervisor mode, is the ux bit set? + cen64_align(static const uint8_t segment_mode_lut[256], CACHE_LINE_SIZE) = { +#define _ sizeof(*seg) +/*ks:sx:ux | k k k k, s, k, k, k, u, k, k, k, ?, k, k, k */ +/* 0: 0: 0 |*/ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +/* 0: 0: 1 |*/ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,_,_,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +/* 0: 1: 0 |*/ 0,0,0,0,0,0,0,0,_,_,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +/* 0: 1: 1 |*/ 0,0,0,0,0,0,0,0,_,_,0,0,0,0,0,0,_,_,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +/* 1: 0: 0 |*/ _,_,_,_,_,_,_,_,0,0,_,_,_,_,_,_,0,0,_,_,_,_,_,_,0,0,_,_,_,_,_,_, +/* 1: 0: 1 |*/ _,_,_,_,_,_,_,_,0,0,_,_,_,_,_,_,_,_,_,_,_,_,_,_,0,0,_,_,_,_,_,_, +/* 1: 1: 0 |*/ _,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,0,0,_,_,_,_,_,_,0,0,_,_,_,_,_,_, +/* 1: 1: 1 |*/ _,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,0,0,_,_,_,_,_,_, +#undef _ + }; + + unsigned segment_mode = segment_mode_lut[cp0_status & 0xFF]; + unsigned mode_and_flags_mask = cp0_status & 0x1E; + +#ifndef NDEBUG + uint64_t sexaddress = (int64_t) ((int32_t) address); + + char kernel = (cp0_status & 0x6) || ((cp0_status & 0x18) == 0); + char supervisor = ((cp0_status & 0x6) == 0) && ((cp0_status & 0x18) == 0x8); + char user = ((cp0_status & 0x6) == 0) && ((cp0_status & 0x18) == 0x10); + + char use_kx = kernel && (cp0_status & 0x80); + char use_sx = supervisor && (cp0_status & 0x40); + char use_ux = user && (cp0_status & 0x20); + char use_64 = use_kx | use_sx | use_ux; + + // Ensure that only one of {kernel, supervisor, user} are produced. + assert(((kernel + supervisor + user) == 1) && "Impossible situation."); + + // Ensure that either 64-bit mode is used, or the address is sign-extended. + assert((use_64 || (sexaddress == address)) && "Invalid 32-bit address."); +#endif + + // Check for useg/suseg/kuseg or xuseg/xsuseg/xkuseg first. + seg = (const struct segment *) ((uintptr_t) USEGs + segment_mode); + + if (address < seg->length) + return seg; + + // If we're not in user mode... + else if (mode_and_flags_mask != 0x10) { + seg = &KSEGs[2]; + + // Assume we're csseg and check for xsseg/xksseg. + if ((address >> 40) == 0x400000) + return &XSSEG; + + // If we're in kernel mode, check for ckseg0, ckseg1, and ckseg3. + else if (mode_and_flags_mask != 0x08) { + if (address >= KSEGs[0].start) + return KSEGs + (address >> 29 & 0x3); + + // Check for xkseg and xkphys. + else if ((address - XKPHYS0.start) < 0x400000FF80000000ULL) + seg = kernel_segs_lut[address >> 59 & 0xF]; + } + + // Check matching segment or return invalid. + if (likely((address - seg->start) < seg->length)) + return seg; + } + + return NULL; +} + diff --git a/vr4300/segment.h b/vr4300/segment.h new file mode 100644 index 000000000..35cfdb5d5 --- /dev/null +++ b/vr4300/segment.h @@ -0,0 +1,28 @@ +// +// vr4300/segment.h: VR4300 MMU segment manager. +// +// CEN64: Cycle-Accurate Nintendo 64 Simulator. +// Copyright (C) 2014, Tyler J. Stachecki. +// +// This file is subject to the terms and conditions defined in +// 'LICENSE', which is part of this source code package. +// + +#ifndef __vr4300_segment_h__ +#define __vr4300_segment_h__ +#include "common.h" + +struct segment { + uint64_t start; + uint64_t length; + uint64_t offset; + + bool cached; + bool mapped; +}; + +const struct segment* get_default_segment(void); +const struct segment* get_segment(uint64_t address, uint32_t cp0_status); + +#endif +