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:
145
src/gbemu/memory.c
Normal file
145
src/gbemu/memory.c
Normal 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); */
|
||||
/* } */
|
||||
Reference in New Issue
Block a user