memory: separate the memory-related code into it's own file

After doing a little reading about the way the memory banks are
mapped, it looks like this code is going to grow. Separate it into
it's own file.

While we're at it, make gb_mem_read() a proper function instead of a
callback. Because these functions are used so frequently, this
corresponds to a ~10-20% performance benefit (due to LTO).
This commit is contained in:
2018-07-09 05:28:14 +00:00
parent c8ba18f6e3
commit cd66ad8a89
5 changed files with 278 additions and 201 deletions

145
src/gbemu/memory.c Normal file
View File

@@ -0,0 +1,145 @@
/*
* Log logic for memory-mapping for
*/
#include "gbemu/memory.h"
#include "gbemu/video.h"
#include "common/common.h"
#include <string.h>
#include <stdio.h>
#include <errno.h>
#define MAX_RAM_LEN (1 << 20) /* Up to 8Mb Cartridge */
#define UNMAP_BOOTROM_ADDR 0xff50
static struct gb_video *video;
static uint8_t ram[MAX_RAM_LEN];
static unsigned char bootrom[0x100] = {0};
static unsigned char cart_low[0x100] = {0};
static int bootrom_mapped = 1;
void gb_mem_init(struct gb_memory *memory, struct gb_video *v)
{
video = v;
memset(&ram, 0, MAX_RAM_LEN);
bootrom_mapped = 1;
}
uint8_t gb_mem_read(struct gb_memory *cpu, uint16_t addr)
{
switch (addr) {
case 0xFF40 ... 0xFF4B:
return gb_video_mem_read(video, addr);
default:
return ram[addr];
}
}
void gb_mem_write(struct gb_memory *cpu, uint16_t addr, uint8_t val)
{
switch (addr) {
case UNMAP_BOOTROM_ADDR:
if (val == 1) {
bootrom_mapped = 0;
memcpy(ram, cart_low, sizeof(cart_low));
}
break;
case 0xFF40 ... 0xFF4B:
gb_video_mem_write(video, addr, val);
break;
case 0 ... 0x100:
if (bootrom_mapped) {
break;
}
/* Intentional fall-through */
default:
ram[addr] = val;
}
}
void gb_mem_force_write(struct gb_memory *cpu, uint16_t addr, uint8_t val)
{
switch (addr) {
case UNMAP_BOOTROM_ADDR:
break;
case 0xFF40 ... 0xFF4B:
gb_video_mem_write(video, addr, val);
break;
case 0 ... 0x100:
/* Intentional fall-through */
default:
ram[addr] = val;
}
}
static void load_file_to_buffer(char *filename, uint8_t *buffer, size_t size)
{
//TODO: Check error codes like a real programmer :P
FILE* prog_file;
long filesize;
prog_file = fopen(filename, "r");
if(prog_file < 0) {
printf("Failed to load game file: %d\n", errno);
return;
}
fseek(prog_file, 0, SEEK_END);
filesize = ftell(prog_file);
fseek(prog_file, 0, SEEK_SET);
if (fread(buffer, MIN(filesize, size), 1, prog_file) < 0) {
printf("Failed to read filed: %d\n", errno);
}
fclose(prog_file);
}
int gb_memory_load_file(struct gb_memory *memory, enum gb_memory_range destination, char *filename)
{
//TODO: Check error codes like a real programmer :P
FILE* prog_file;
long filesize;
switch (destination) {
case GB_MEMORY_BOOTROM:
load_file_to_buffer(filename, bootrom, sizeof(bootrom));
load_file_to_buffer(filename, ram, sizeof(bootrom));
break;
case GB_MEMORY_CART:
load_file_to_buffer(filename, ram, sizeof(ram));
load_file_to_buffer(filename, cart_low, sizeof(cart_low));
break;
default:
return -1;
}
}
/* static void mem_dump(char *filename) */
/* { */
/* //TODO: Check error codes like a real programmer :P */
/* FILE* dump_file; */
/* char *token = strtok(filename, " "); */
/* if (token == NULL) { */
/* gb_log("usage: load <file>\n"); */
/* return; */
/* } */
/* strip_newline(token); */
/* dump_file = fopen(token, "w"); */
/* if(dump_file == NULL) { */
/* gb_log("Failed to open mem dump file: %d\n", errno); */
/* return; */
/* } */
/* fwrite(ram, MAX_RAM_LEN, 1, dump_file); */
/* fclose(dump_file); */
/* } */