2014-03-09 22:02:31 -04:00
|
|
|
//
|
|
|
|
// bus/memorymap.c: System memory mapper.
|
|
|
|
//
|
2015-07-01 19:44:21 -04:00
|
|
|
// CEN64: Cycle-Accurate Nintendo 64 Emulator.
|
|
|
|
// Copyright (C) 2015, Tyler J. Stachecki.
|
2014-03-09 22:02:31 -04:00
|
|
|
//
|
|
|
|
// This file is subject to the terms and conditions defined in
|
|
|
|
// 'LICENSE', which is part of this source code package.
|
|
|
|
//
|
|
|
|
|
|
|
|
#include "common.h"
|
|
|
|
#include "memorymap.h"
|
|
|
|
|
|
|
|
static void fixup(struct memory_map *, struct memory_map_node *);
|
|
|
|
static void rotate_left(struct memory_map *, struct memory_map_node *);
|
|
|
|
static void rotate_right(struct memory_map *, struct memory_map_node *);
|
|
|
|
|
|
|
|
// Creates a new memory map.
|
2014-11-01 16:59:12 -04:00
|
|
|
void create_memory_map(struct memory_map *map) {
|
|
|
|
map->next_map_index = 1;
|
2014-11-11 17:21:25 -05:00
|
|
|
map->nil = map->mappings;
|
2014-03-09 22:02:31 -04:00
|
|
|
map->root = map->nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rebalances the tree after a node is inserted.
|
2014-11-01 16:59:12 -04:00
|
|
|
void fixup(struct memory_map *map, struct memory_map_node *node) {
|
2014-11-11 17:21:25 -05:00
|
|
|
struct memory_map_node *cur;
|
2014-03-09 22:02:31 -04:00
|
|
|
|
|
|
|
// Rebalance the whole tree as needed.
|
|
|
|
while (node->parent->color == MEMORY_MAP_RED) {
|
|
|
|
if (node->parent == node->parent->parent->left) {
|
|
|
|
cur = node->parent->parent->right;
|
|
|
|
|
|
|
|
// Case 1: We only need to update colors.
|
|
|
|
if (cur->color == MEMORY_MAP_RED) {
|
|
|
|
node->parent->color = MEMORY_MAP_BLACK;
|
|
|
|
cur->color = MEMORY_MAP_BLACK;
|
|
|
|
node->parent->parent->color = MEMORY_MAP_RED;
|
|
|
|
node = node->parent->parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
|
|
|
// Case 2: We need to perform a left rotation.
|
|
|
|
if (node == node->parent->right) {
|
|
|
|
node = node->parent;
|
|
|
|
rotate_left(map, node);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Case 3: We need to perform a right rotation.
|
|
|
|
node->parent->color = MEMORY_MAP_BLACK;
|
|
|
|
node->parent->parent->color = MEMORY_MAP_RED;
|
|
|
|
rotate_right(map, node->parent->parent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
cur = node->parent->parent->left;
|
|
|
|
|
|
|
|
// Case 1: We only need to update colors.
|
|
|
|
if (cur->color == MEMORY_MAP_RED) {
|
|
|
|
node->parent->color = MEMORY_MAP_BLACK;
|
|
|
|
cur->color = MEMORY_MAP_BLACK;
|
|
|
|
node->parent->parent->color = MEMORY_MAP_RED;
|
|
|
|
node = node->parent->parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
|
|
|
// Case 2: We need to perform a right rotation.
|
|
|
|
if (node == node->parent->left) {
|
|
|
|
node = node->parent;
|
|
|
|
rotate_right(map, node);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Case 3: We need to perform a left rotation.
|
|
|
|
node->parent->color = MEMORY_MAP_BLACK;
|
|
|
|
node->parent->parent->color = MEMORY_MAP_RED;
|
|
|
|
rotate_left(map, node->parent->parent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// When we rebalanced the tree, we might have accidentally colored
|
|
|
|
// the root red, so unconditionally color if back after rebalancing.
|
|
|
|
map->root->color = MEMORY_MAP_BLACK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Inserts a mapping into the tree.
|
2014-11-01 16:59:12 -04:00
|
|
|
int map_address_range(struct memory_map *map, uint32_t start, uint32_t length,
|
2014-04-18 13:34:23 -04:00
|
|
|
void *instance, memory_rd_function on_read, memory_wr_function on_write) {
|
2014-03-09 22:02:31 -04:00
|
|
|
struct memory_map_node *check = map->root;
|
|
|
|
struct memory_map_node *cur = map->nil;
|
2014-11-11 17:21:25 -05:00
|
|
|
uint32_t end = start + length - 1;
|
2014-03-09 22:02:31 -04:00
|
|
|
|
2014-11-01 16:59:12 -04:00
|
|
|
struct memory_map_node *new_node;
|
2014-11-11 17:21:25 -05:00
|
|
|
struct memory_mapping mapping;
|
2014-03-09 22:02:31 -04:00
|
|
|
|
|
|
|
// Make sure we have enough space in the map.
|
2014-11-01 16:59:12 -04:00
|
|
|
const unsigned num_mappings = sizeof(map->mappings) /
|
|
|
|
sizeof(map->mappings[0]) - 1;
|
|
|
|
|
|
|
|
if (unlikely(map->next_map_index >= num_mappings)) {
|
|
|
|
debug("map_address_range: Out of free mappings.");
|
|
|
|
return 1;
|
|
|
|
}
|
2014-03-09 22:02:31 -04:00
|
|
|
|
2014-11-01 16:59:12 -04:00
|
|
|
new_node = &map->mappings[map->next_map_index++];
|
2014-03-09 22:02:31 -04:00
|
|
|
|
|
|
|
// Walk down the tree.
|
|
|
|
while (check != map->nil) {
|
|
|
|
cur = check;
|
|
|
|
|
|
|
|
check = (start < cur->mapping.start)
|
|
|
|
? check->left : check->right;
|
|
|
|
}
|
|
|
|
|
2014-11-11 17:21:25 -05:00
|
|
|
// Insert the entry.
|
2014-03-09 22:02:31 -04:00
|
|
|
if (cur == map->nil)
|
2014-11-01 16:59:12 -04:00
|
|
|
map->root = new_node;
|
2014-03-09 22:02:31 -04:00
|
|
|
|
|
|
|
else if (start < cur->mapping.start)
|
2014-11-01 16:59:12 -04:00
|
|
|
cur->left = new_node;
|
2014-03-09 22:02:31 -04:00
|
|
|
else
|
2014-11-01 16:59:12 -04:00
|
|
|
cur->right = new_node;
|
2014-03-09 22:02:31 -04:00
|
|
|
|
2014-11-01 16:59:12 -04:00
|
|
|
new_node->left = map->nil;
|
|
|
|
new_node->right = map->nil;
|
|
|
|
new_node->parent = cur;
|
2014-03-09 22:02:31 -04:00
|
|
|
|
2014-11-11 17:21:25 -05:00
|
|
|
// Initialize the entry.
|
|
|
|
mapping.instance = instance;
|
|
|
|
mapping.on_read = on_read;
|
|
|
|
mapping.on_write = on_write;
|
2014-03-09 22:02:31 -04:00
|
|
|
|
2014-11-11 17:21:25 -05:00
|
|
|
mapping.end = end;
|
|
|
|
mapping.length = length;
|
|
|
|
mapping.start = start;
|
2014-03-09 22:02:31 -04:00
|
|
|
|
2014-11-11 17:21:25 -05:00
|
|
|
new_node->mapping = mapping;
|
2014-03-09 22:02:31 -04:00
|
|
|
|
2014-11-11 17:21:25 -05:00
|
|
|
// Rebalance the tree.
|
2014-11-01 16:59:12 -04:00
|
|
|
new_node->color = MEMORY_MAP_RED;
|
2014-11-11 17:21:25 -05:00
|
|
|
fixup(map, new_node);
|
2014-11-01 16:59:12 -04:00
|
|
|
return 0;
|
2014-03-09 22:02:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Returns a pointer to a region given an address.
|
|
|
|
const struct memory_mapping *resolve_mapped_address(
|
|
|
|
const struct memory_map *map, uint32_t address) {
|
|
|
|
const struct memory_map_node *cur = map->root;
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (address < cur->mapping.start)
|
|
|
|
cur = cur->left;
|
|
|
|
else if (address > cur->mapping.end)
|
|
|
|
cur = cur->right;
|
|
|
|
|
|
|
|
else
|
|
|
|
return &cur->mapping;
|
|
|
|
} while (cur != map->nil);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Performs a left rotation centered at n.
|
|
|
|
static void rotate_left(struct memory_map *map, struct memory_map_node *n) {
|
|
|
|
struct memory_map_node *y = n->right;
|
|
|
|
|
|
|
|
// Turn y's left subtree into n's right subtree.
|
|
|
|
n->right = y->left;
|
|
|
|
|
|
|
|
if (y->left != map->nil)
|
|
|
|
y->left->parent = n;
|
|
|
|
|
|
|
|
// Link n's parent to y.
|
|
|
|
y->parent = n->parent;
|
|
|
|
|
|
|
|
if (n->parent == map->nil)
|
|
|
|
map->root = y;
|
|
|
|
else if (n == n->parent->left)
|
|
|
|
n->parent->left = y;
|
|
|
|
else
|
|
|
|
n->parent->right = y;
|
|
|
|
|
|
|
|
// Put n on y's left.
|
|
|
|
y->left = n;
|
|
|
|
n->parent = y;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Performs a right rotation centered at n.
|
|
|
|
static void rotate_right(struct memory_map *map, struct memory_map_node *n) {
|
|
|
|
struct memory_map_node *y = n->left;
|
|
|
|
|
|
|
|
// Turn y's right subtree into n's left subtree.
|
|
|
|
n->left = y->right;
|
|
|
|
|
|
|
|
if (y->right != map->nil)
|
|
|
|
y->right->parent = n;
|
|
|
|
|
|
|
|
// Link n's parent to y.
|
|
|
|
y->parent = n->parent;
|
|
|
|
|
|
|
|
if (n->parent == map->nil)
|
|
|
|
map->root = y;
|
|
|
|
else if (n == n->parent->left)
|
|
|
|
n->parent->left = y;
|
|
|
|
else
|
|
|
|
n->parent->right = y;
|
|
|
|
|
|
|
|
// Put n on y's right.
|
|
|
|
y->right = n;
|
|
|
|
n->parent = y;
|
|
|
|
}
|
|
|
|
|