Initial commit.

This commit is contained in:
Tyler Stachecki 2014-03-08 11:12:15 -05:00
commit 2f3aded155
34 changed files with 2119 additions and 0 deletions

9
.gitignore vendored Normal file
View file

@ -0,0 +1,9 @@
# Other data.
/data
# Build files.
/build
# Swap files.
*.swp

35
CMakeLists.txt Normal file
View file

@ -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})

25
LICENSE Normal file
View file

@ -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.

31
README.md Normal file
View file

@ -0,0 +1,31 @@
<p align="center">
<img src="/assets/logo.png" />
</p>
# 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...

1
VERSION Normal file
View file

@ -0,0 +1 @@
0.3

BIN
assets/logo-small.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

36
bus/controller.c Normal file
View file

@ -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;
}

31
bus/controller.h Normal file
View file

@ -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

32
cen64.c Normal file
View file

@ -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 <pifrom.bin>\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;
}

86
common.h Normal file
View file

@ -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 <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#else
#include <cassert>
#include <cstddef>
#include <cstdlib>
#include <cstdint>
#include <cstdio>
#include <cstring>
#endif
#ifndef _MSC_VER
#ifndef __cplusplus
#include <stdbool.h>
#else
#include <cstdbool>
#endif
#else
typedef char bool;
#define false 0
#define true 1
#endif
#ifndef NDEBUG
#ifndef __cplusplus
#include <stdio.h>
#else
#include <cstdio>
#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

97
device.c Normal file
View file

@ -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);
}

34
device.h Normal file
View file

@ -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

31
pif/controller.c Normal file
View file

@ -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;
}

28
pif/controller.h Normal file
View file

@ -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

18
vr4300/cp0.c Normal file
View file

@ -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));
}

52
vr4300/cp0.h Normal file
View file

@ -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

34
vr4300/cpu.c Normal file
View file

@ -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;
}

55
vr4300/cpu.h Normal file
View file

@ -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

255
vr4300/decoder.c Normal file
View file

@ -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;
}

35
vr4300/decoder.h Normal file
View file

@ -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

BIN
vr4300/docs/vr43xx.pdf Normal file

Binary file not shown.

96
vr4300/fault.c Normal file
View file

@ -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();
}

30
vr4300/fault.h Normal file
View file

@ -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

19
vr4300/fault.md Normal file
View file

@ -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

121
vr4300/icache.c Normal file
View file

@ -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);
}

41
vr4300/icache.h Normal file
View file

@ -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

18
vr4300/opcodes.c Normal file
View file

@ -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
};

190
vr4300/opcodes.h Normal file
View file

@ -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

34
vr4300/opcodes.md Normal file
View file

@ -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

285
vr4300/pipeline.c Normal file
View file

@ -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();
}

59
vr4300/pipeline.h Normal file
View file

@ -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

273
vr4300/segment.c Normal file
View file

@ -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;
}

28
vr4300/segment.h Normal file
View file

@ -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