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.
213 lines
4.3 KiB
C
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;
|
|
}
|
|
|
|
}
|