mirror of
https://github.com/n64dev/cen64.git
synced 2024-05-11 17:05:33 -04:00
Initial commit.
This commit is contained in:
commit
2f3aded155
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Other data.
|
||||
/data
|
||||
|
||||
# Build files.
|
||||
/build
|
||||
|
||||
# Swap files.
|
||||
*.swp
|
||||
|
35
CMakeLists.txt
Normal file
35
CMakeLists.txt
Normal 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
25
LICENSE
Normal 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
31
README.md
Normal 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...
|
||||
|
BIN
assets/logo-small.png
Normal file
BIN
assets/logo-small.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
assets/logo.png
Normal file
BIN
assets/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 38 KiB |
36
bus/controller.c
Normal file
36
bus/controller.c
Normal 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
31
bus/controller.h
Normal 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
32
cen64.c
Normal 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
86
common.h
Normal 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
97
device.c
Normal 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
34
device.h
Normal 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
31
pif/controller.c
Normal 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
28
pif/controller.h
Normal 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
18
vr4300/cp0.c
Normal 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
52
vr4300/cp0.h
Normal 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
34
vr4300/cpu.c
Normal 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
55
vr4300/cpu.h
Normal 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
255
vr4300/decoder.c
Normal 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
35
vr4300/decoder.h
Normal 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
BIN
vr4300/docs/vr43xx.pdf
Normal file
Binary file not shown.
96
vr4300/fault.c
Normal file
96
vr4300/fault.c
Normal 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
30
vr4300/fault.h
Normal 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
19
vr4300/fault.md
Normal 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
121
vr4300/icache.c
Normal 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
41
vr4300/icache.h
Normal 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
18
vr4300/opcodes.c
Normal 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
190
vr4300/opcodes.h
Normal 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
34
vr4300/opcodes.md
Normal 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
285
vr4300/pipeline.c
Normal 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
59
vr4300/pipeline.h
Normal 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
273
vr4300/segment.c
Normal 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
28
vr4300/segment.h
Normal 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
|
||||
|
Loading…
Reference in a new issue