Files
gb-emu/src/gbemu/memory.c
Max Regan 1076d02638 interrupt: first-pass implementation of interrupts
Only manual and V-blank interrupts work, for now. This implements
enough to make the EI and DI parts of Blargg's Interrupt test pass.
2018-09-20 20:55:51 -07:00

213 lines
4.3 KiB
C

/*
* Log logic for memory-mapping for
*/
#include "gbemu/memory.h"
#include "gbemu/video.h"
#include "gbemu/cpu.h"
#include "common/common.h"
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define MAX_RAM_LEN (1 << 20) /* Up to 8Mb Cartridge */
#define UNMAP_BOOTROM_ADDR 0xff50
static struct gb_video *video;
static struct gb_interrupt *interrupt;
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;
static void load_file_to_buffer(const 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);
}
void gb_mem_init(struct gb_memory *memory, struct gb_video *v, struct gb_interrupt *i,
const char *bootrom_file, const char *rom_file)
{
interrupt = i;
video = v;
memset(&ram, 0, MAX_RAM_LEN);
bootrom_mapped = 1;
load_file_to_buffer(rom_file, cart_low, sizeof(cart_low));
load_file_to_buffer(rom_file, ram, sizeof(ram));
load_file_to_buffer(bootrom_file, bootrom, sizeof(bootrom));
load_file_to_buffer(bootrom_file, ram, sizeof(ram));
}
static uint8_t ser_byte = 0;
static uint8_t ser_ctl = 0;
static void gb_serial_write(struct gb_memory *memory, uint16_t addr, uint8_t val)
{
int fd;
switch (addr) {
case 0xFF01:
ser_byte = val;
break;
case 0xFF02:
if (val == 0x81) {
fd = open("serial.log", O_WRONLY | O_CREAT | O_APPEND,
S_IRWXU | S_IRGRP | S_IROTH | S_IWUSR);
printf("%c", ser_byte);
write(fd, &ser_byte, sizeof(ser_byte));
close(fd);
}
break;
default:
ASSERT(0);
}
}
static uint8_t gb_serial_read(struct gb_memory *memory, uint16_t addr)
{
int fd;
static uint8_t ser_byte = 0;
switch (addr) {
case 0xFF01:
return ser_byte;
case 0xFF02:
return ser_ctl;
default:
ASSERT(0);
}
}
uint8_t gb_mem_read(struct gb_memory *memory, uint16_t addr)
{
switch (addr) {
case 0xFF01 ... 0xFF02:
return gb_serial_read(memory, addr);
case 0xFF40 ... 0xFF4B:
return gb_video_mem_read(video, addr);
case 0xFF0F:
return gb_interrupt_if_read(interrupt);
case 0xFFFF:
return gb_interrupt_ie_read(interrupt);
default:
return ram[addr];
}
}
void gb_mbc3_write(struct gb_memory *memory, uint16_t addr, uint8_t val)
{
switch (addr) {
case 0x0000 ... 0x1FFF:
/* RAM and Timer Enable */
ASSERT(0);
case 0xA000 ... 0xBFFF:
/* RAM Bank */
ASSERT(0);
case 0x2000 ... 0x3FFF:
/* ROM Bank Number */
ASSERT(0);
case 0x4000 ... 0x5FFF:
/* RAM Bank Number / RTC Reg Select */
ASSERT(0);
case 0x6000 ... 0x7FFF:
/* Latch Clock Data */
ASSERT(0);
case 0xFF01 ... 0xFF02:
gb_serial_write(memory, addr, val);
break;
case 0xFF40 ... 0xFF4B:
gb_video_mem_write(video, addr, val);
break;
case 0xFF0F:
gb_interrupt_if_write(interrupt, val);
break;
case 0xFFFF:
gb_interrupt_ie_write(interrupt, val);
break;
default:
ram[addr] = val;
}
}
void gb_mem_write(struct gb_memory *memory, uint16_t addr, uint8_t val)
{
if (!bootrom_mapped && ram[0x147] == 0x05) {
gb_mbc3_write(memory, addr, val);
return;
}
switch (addr) {
case UNMAP_BOOTROM_ADDR:
if (val == 1) {
bootrom_mapped = 0;
memcpy(ram, cart_low, sizeof(cart_low));
}
return;
case 0xFF01 ... 0xFF02:
gb_serial_write(memory, addr, val);
return;
case 0xFF40 ... 0xFF4B:
gb_video_mem_write(video, addr, val);
return;
case 0xFF0F:
gb_interrupt_if_write(interrupt, val);
return;
case 0xFFFF:
gb_interrupt_ie_write(interrupt, val);
return;
case 0x8000 ... 0x87FF:
case 0x9800 ... 0x9BFF:
break;
case 0 ... 0x100:
if (bootrom_mapped) {
return;
}
break;
}
ram[addr] = val;
}
void gb_mem_force_write(struct gb_memory *memory, 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;
}
}