Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1076d02638 | |||
| 593d9d3600 |
@@ -37,7 +37,6 @@ static int64_t parse_val(const char *str);
|
|||||||
static const char prompt[] = "gbdb > ";
|
static const char prompt[] = "gbdb > ";
|
||||||
static const char usage[] =
|
static const char usage[] =
|
||||||
"Available commands:\n"
|
"Available commands:\n"
|
||||||
"load <file>: loads the given file as the gameboy cartridge\n"
|
|
||||||
"run: runs the CPU until a breakpoint or halt is hit\n"
|
"run: runs the CPU until a breakpoint or halt is hit\n"
|
||||||
"break <addr>: Adds a breakpoint for the given addess\n"
|
"break <addr>: Adds a breakpoint for the given addess\n"
|
||||||
"step <cycles>: executes \"cycle\" instructions (default 1)\n"
|
"step <cycles>: executes \"cycle\" instructions (default 1)\n"
|
||||||
@@ -48,6 +47,7 @@ static const char usage[] =
|
|||||||
static struct lr35902_ops cpu_ops;
|
static struct lr35902_ops cpu_ops;
|
||||||
static struct lr35902_state cpu;
|
static struct lr35902_state cpu;
|
||||||
static struct gb_video video;
|
static struct gb_video video;
|
||||||
|
static struct gb_interrupt interrupt;
|
||||||
static void *memory = NULL;
|
static void *memory = NULL;
|
||||||
|
|
||||||
static int paused_breakpoint = 0;
|
static int paused_breakpoint = 0;
|
||||||
@@ -108,13 +108,14 @@ static void strip_newline(char *string)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void init(void)
|
static void init(const char *bootrom, const char *rom)
|
||||||
{
|
{
|
||||||
cpu_ops.undef_d3 = breakpoint_cb;
|
cpu_ops.undef_d3 = breakpoint_cb;
|
||||||
|
|
||||||
lr35902_init(&cpu, memory, &cpu_ops);
|
gb_interrupt_init(&interrupt, &cpu);
|
||||||
gb_video_init(&video, memory);
|
lr35902_init(&cpu, &interrupt, memory, &cpu_ops);
|
||||||
gb_mem_init(memory, &video);
|
gb_video_init(&video, memory, &interrupt);
|
||||||
|
gb_mem_init(memory, &video, &interrupt, bootrom, rom);
|
||||||
|
|
||||||
config.log_fd = -1;
|
config.log_fd = -1;
|
||||||
config.trace_fd = -1;
|
config.trace_fd = -1;
|
||||||
@@ -570,31 +571,6 @@ static void mem(struct tokens **tokens)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void load(struct tokens **tokens)
|
|
||||||
{
|
|
||||||
char *token = token_next(tokens);
|
|
||||||
if (token == NULL) {
|
|
||||||
gb_error("usage: load <file>\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
gb_log("loading %s\n", token);
|
|
||||||
|
|
||||||
gb_memory_load_file(memory, GB_MEMORY_CART, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void load_bootrom(struct tokens **tokens)
|
|
||||||
{
|
|
||||||
char *token = token_next(tokens);
|
|
||||||
if (token == NULL) {
|
|
||||||
gb_error("usage: bootrom <file>\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
gb_memory_load_file(memory, GB_MEMORY_BOOTROM, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void quit(struct tokens **tokens)
|
static void quit(struct tokens **tokens)
|
||||||
{
|
{
|
||||||
exit(0);
|
exit(0);
|
||||||
@@ -869,9 +845,10 @@ static int load_initfile(const char *initfile_path, struct tri *commands)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, const char **argv)
|
||||||
{
|
{
|
||||||
struct tri commands;
|
struct tri commands;
|
||||||
|
const char *rom, *bootrom;
|
||||||
|
|
||||||
tri_init(&commands);
|
tri_init(&commands);
|
||||||
tri_add_string(&commands, "#", comment);
|
tri_add_string(&commands, "#", comment);
|
||||||
@@ -882,8 +859,6 @@ int main(int argc, char **argv)
|
|||||||
tri_add_string(&commands, "stats", stats);
|
tri_add_string(&commands, "stats", stats);
|
||||||
tri_add_string(&commands, "exit", quit);
|
tri_add_string(&commands, "exit", quit);
|
||||||
tri_add_string(&commands, "quit", quit);
|
tri_add_string(&commands, "quit", quit);
|
||||||
tri_add_string(&commands, "load", load);
|
|
||||||
tri_add_string(&commands, "bootrom", load_bootrom);
|
|
||||||
tri_add_string(&commands, "mem", mem);
|
tri_add_string(&commands, "mem", mem);
|
||||||
/* FIXME */
|
/* FIXME */
|
||||||
/* tri_add_string(&commands, "dump", mem_dump); */
|
/* tri_add_string(&commands, "dump", mem_dump); */
|
||||||
@@ -897,11 +872,22 @@ int main(int argc, char **argv)
|
|||||||
tri_add_string(&commands, "set", set);
|
tri_add_string(&commands, "set", set);
|
||||||
tri_add_string(&commands, "echo", echo);
|
tri_add_string(&commands, "echo", echo);
|
||||||
|
|
||||||
init();
|
|
||||||
|
|
||||||
if (argc > 1) {
|
if (argc < 3) {
|
||||||
if (load_initfile(argv[1], &commands)) {
|
gb_error("usage: %s <BOOT_ROM> <ROM> INIT_SCRIPT\n", argv[0]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bootrom = argv[1];
|
||||||
|
rom = argv[2];
|
||||||
|
|
||||||
|
init(bootrom, rom);
|
||||||
|
|
||||||
|
if (argc > 3) {
|
||||||
|
gb_log("%s\n", argv[0]);
|
||||||
|
if (load_initfile(argv[3], &commands)) {
|
||||||
gb_error("Failed to load initfile\n");
|
gb_error("Failed to load initfile\n");
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,16 @@
|
|||||||
|
|
||||||
#define ARRAY_SIZE(x) (sizeof((x)) / (sizeof((x)[0])))
|
#define ARRAY_SIZE(x) (sizeof((x)) / (sizeof((x)[0])))
|
||||||
|
|
||||||
|
#define SET_BIT(x, idx) do {(x) |= (1 << (idx));} while (0)
|
||||||
|
#define CLR_BIT(x, idx) do {(x) &= ~(1 << (idx));} while (0)
|
||||||
|
#define GET_BIT(x, bit) (((x) >> (bit)) & 1)
|
||||||
|
#define WRITE_BIT(x, idx, bit) \
|
||||||
|
do { \
|
||||||
|
(x) &= ~(1 << (idx)); \
|
||||||
|
(x) |= (bit) << (idx); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Replaces any characters from A-Z with their lowercase counterpart.
|
* Replaces any characters from A-Z with their lowercase counterpart.
|
||||||
* All other characters preceding '\0' are ignored
|
* All other characters preceding '\0' are ignored
|
||||||
|
|||||||
@@ -10,15 +10,6 @@
|
|||||||
#include "gbemu/cpu.h"
|
#include "gbemu/cpu.h"
|
||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
|
|
||||||
#define SET_BIT(x, idx) do {(x) |= (1 << (idx));} while (0)
|
|
||||||
#define CLR_BIT(x, idx) do {(x) &= ~(1 << (idx));} while (0)
|
|
||||||
#define GET_BIT(x, bit) (((x) >> (bit)) & 1)
|
|
||||||
#define WRITE_BIT(x, idx, bit) \
|
|
||||||
do { \
|
|
||||||
(x) &= ~(1 << (idx)); \
|
|
||||||
(x) |= (bit) << (idx); \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define CALC_H_ADD(a, b) ((((a) & 0xf) + ((b) & 0xf)) > 0xf)
|
#define CALC_H_ADD(a, b) ((((a) & 0xf) + ((b) & 0xf)) > 0xf)
|
||||||
#define CALC_H_SUB(a, b) (((a) & 0xf) < ((b) & 0xf))
|
#define CALC_H_SUB(a, b) (((a) & 0xf) < ((b) & 0xf))
|
||||||
|
|
||||||
@@ -30,6 +21,8 @@
|
|||||||
#define CALC_C_ADD_8(a, b) (0xff - (a) < (b))
|
#define CALC_C_ADD_8(a, b) (0xff - (a) < (b))
|
||||||
#define CALC_C_SUB(a, b) ((a) < (b))
|
#define CALC_C_SUB(a, b) ((a) < (b))
|
||||||
|
|
||||||
|
static void lr35902_handle_pending(struct lr35902_state *cpu);
|
||||||
|
|
||||||
static int cpu_reg16_to_idx[NUM_LR35902_REGS_16] = {
|
static int cpu_reg16_to_idx[NUM_LR35902_REGS_16] = {
|
||||||
[LR35902_REG_BC] = 0,
|
[LR35902_REG_BC] = 0,
|
||||||
[LR35902_REG_DE] = 1,
|
[LR35902_REG_DE] = 1,
|
||||||
@@ -79,11 +72,13 @@ void lr35902_set_reg_8(struct lr35902_state *cpu, lr35902_regs_8 reg,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void lr35902_init(struct lr35902_state *cpu,
|
void lr35902_init(struct lr35902_state *cpu,
|
||||||
|
struct gb_interrupt *interrupt,
|
||||||
struct gb_memory *memory,
|
struct gb_memory *memory,
|
||||||
const struct lr35902_ops *ops)
|
const struct lr35902_ops *ops)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
cpu->interrupt = interrupt;
|
||||||
cpu->memory = memory;
|
cpu->memory = memory;
|
||||||
|
|
||||||
cpu->undef_d3 = ops->undef_d3;
|
cpu->undef_d3 = ops->undef_d3;
|
||||||
@@ -1638,7 +1633,7 @@ int lr35902_cycle(struct lr35902_state *cpu)
|
|||||||
break;
|
break;
|
||||||
case 0xd9: /* RETI */
|
case 0xd9: /* RETI */
|
||||||
RET(cpu);
|
RET(cpu);
|
||||||
cpu->int_state = LR35902_INT_ON;
|
gb_interrupt_ime_set(cpu->interrupt);
|
||||||
break;
|
break;
|
||||||
case 0xda: /* JP C, a16 */
|
case 0xda: /* JP C, a16 */
|
||||||
val_16 = gb_mem_read(cpu->memory, cpu->pc++);
|
val_16 = gb_mem_read(cpu->memory, cpu->pc++);
|
||||||
@@ -1738,7 +1733,8 @@ int lr35902_cycle(struct lr35902_state *cpu)
|
|||||||
cpu->a = gb_mem_read(cpu->memory, 0xFF00 + cpu->c);
|
cpu->a = gb_mem_read(cpu->memory, 0xFF00 + cpu->c);
|
||||||
break;
|
break;
|
||||||
case 0xf3: /* DI */
|
case 0xf3: /* DI */
|
||||||
/*TODO: implement me */
|
gb_interrupt_ime_clear(cpu->interrupt);
|
||||||
|
break;
|
||||||
case 0xf4: /* UNDEF */
|
case 0xf4: /* UNDEF */
|
||||||
break;
|
break;
|
||||||
case 0xf5: /* PUSH AF */
|
case 0xf5: /* PUSH AF */
|
||||||
@@ -1776,7 +1772,8 @@ int lr35902_cycle(struct lr35902_state *cpu)
|
|||||||
cpu->a = gb_mem_read(cpu->memory, val_16);
|
cpu->a = gb_mem_read(cpu->memory, val_16);
|
||||||
break;
|
break;
|
||||||
case 0xfb: /* EI */
|
case 0xfb: /* EI */
|
||||||
/* TODO: implement me */
|
gb_log("EI\n");
|
||||||
|
gb_interrupt_ime_set(cpu->interrupt);
|
||||||
break;
|
break;
|
||||||
case 0xfc: /* UNDEF */
|
case 0xfc: /* UNDEF */
|
||||||
ASSERT(0);
|
ASSERT(0);
|
||||||
@@ -1794,3 +1791,33 @@ int lr35902_cycle(struct lr35902_state *cpu)
|
|||||||
cpu->metrics.cycles += cycles + 1;
|
cpu->metrics.cycles += cycles + 1;
|
||||||
return cycles + 1;
|
return cycles + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void lr35902_interrupt(struct lr35902_state *cpu, enum gb_interrupt_kind kind)
|
||||||
|
{
|
||||||
|
uint16_t addr;
|
||||||
|
|
||||||
|
gb_log("Interrupt! (%d)\n", kind);
|
||||||
|
|
||||||
|
switch (kind) {
|
||||||
|
case GB_INT_VBLANK:
|
||||||
|
addr = 0x40;
|
||||||
|
break;
|
||||||
|
case GB_INT_LCD_STAT:
|
||||||
|
addr = 0x48;
|
||||||
|
break;
|
||||||
|
case GB_INT_TIMER:
|
||||||
|
addr = 0x50;
|
||||||
|
break;
|
||||||
|
case GB_INT_SERIAL:
|
||||||
|
addr = 0x58;
|
||||||
|
break;
|
||||||
|
case GB_INT_JOYPAD:
|
||||||
|
addr = 0x60;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ASSERT(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
PUSH_16(cpu, cpu->pc);
|
||||||
|
cpu->pc = addr;
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
#define GB_CPU_H
|
#define GB_CPU_H
|
||||||
|
|
||||||
#include "gbemu/memory.h"
|
#include "gbemu/memory.h"
|
||||||
|
#include "gbemu/cpu.h"
|
||||||
|
#include "gbemu/interrupt.h"
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
@@ -70,6 +72,7 @@ struct lr35902_event {
|
|||||||
struct lr35902_state {
|
struct lr35902_state {
|
||||||
|
|
||||||
struct gb_memory *memory;
|
struct gb_memory *memory;
|
||||||
|
struct gb_interrupt *interrupt;
|
||||||
|
|
||||||
union {
|
union {
|
||||||
/*
|
/*
|
||||||
@@ -122,6 +125,7 @@ struct lr35902_ops {
|
|||||||
};
|
};
|
||||||
|
|
||||||
void lr35902_init(struct lr35902_state *cpu,
|
void lr35902_init(struct lr35902_state *cpu,
|
||||||
|
struct gb_interrupt *interrupt,
|
||||||
struct gb_memory *memory,
|
struct gb_memory *memory,
|
||||||
const struct lr35902_ops *ops);
|
const struct lr35902_ops *ops);
|
||||||
|
|
||||||
@@ -141,4 +145,6 @@ void lr35902_set_reg_8(struct lr35902_state *cpu,
|
|||||||
|
|
||||||
int lr35902_cycle(struct lr35902_state *cpu);
|
int lr35902_cycle(struct lr35902_state *cpu);
|
||||||
|
|
||||||
|
void lr35902_interrupt(struct lr35902_state *cpu, enum gb_interrupt_kind kind);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
90
src/gbemu/interrupt.c
Normal file
90
src/gbemu/interrupt.c
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
#include "gbemu/interrupt.h"
|
||||||
|
#include "gbemu/cpu.h"
|
||||||
|
|
||||||
|
#include "common/common.h"
|
||||||
|
|
||||||
|
void gb_interrupt_init(struct gb_interrupt *interrupt, struct lr35902_state *cpu)
|
||||||
|
{
|
||||||
|
interrupt->flags = 0;
|
||||||
|
interrupt->enable = 0;
|
||||||
|
|
||||||
|
interrupt->cpu = cpu;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fire_if_pending(struct gb_interrupt *interrupt)
|
||||||
|
{
|
||||||
|
uint8_t active_interrupts = interrupt->flags & interrupt->enable;
|
||||||
|
enum gb_interrupt_kind kind;
|
||||||
|
|
||||||
|
if (!interrupt->master_enable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!active_interrupts) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
kind = __builtin_ctz(active_interrupts);
|
||||||
|
ASSERT(kind >= GB_INT_MIN);
|
||||||
|
ASSERT(kind <= GB_INT_MAX);
|
||||||
|
|
||||||
|
interrupt->master_enable = false;
|
||||||
|
CLR_BIT(interrupt->flags, kind);
|
||||||
|
lr35902_interrupt(interrupt->cpu, kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
int gb_interrupt_set(struct gb_interrupt *interrupt, enum gb_interrupt_kind kind)
|
||||||
|
{
|
||||||
|
uint8_t old = interrupt->flags;
|
||||||
|
uint8_t new = interrupt->flags;
|
||||||
|
|
||||||
|
ASSERT(kind >= GB_INT_MIN);
|
||||||
|
ASSERT(kind <= GB_INT_MAX);
|
||||||
|
|
||||||
|
SET_BIT(new, kind);
|
||||||
|
|
||||||
|
fire_if_pending(interrupt);
|
||||||
|
}
|
||||||
|
|
||||||
|
int gb_interrupt_clear(struct gb_interrupt *interrupt, enum gb_interrupt_kind kind)
|
||||||
|
{
|
||||||
|
ASSERT(kind >= GB_INT_MIN);
|
||||||
|
ASSERT(kind <= GB_INT_MAX);
|
||||||
|
CLR_BIT(interrupt->flags, kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t gb_interrupt_if_read(struct gb_interrupt *interrupt)
|
||||||
|
{
|
||||||
|
return interrupt->flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gb_interrupt_if_write(struct gb_interrupt *interrupt, uint8_t val)
|
||||||
|
{
|
||||||
|
interrupt->flags = val;
|
||||||
|
fire_if_pending(interrupt);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t gb_interrupt_ie_read(struct gb_interrupt *interrupt)
|
||||||
|
{
|
||||||
|
return interrupt->enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gb_interrupt_ie_write(struct gb_interrupt *interrupt, uint8_t val)
|
||||||
|
{
|
||||||
|
uint8_t active_interrupts = interrupt->flags & interrupt->enable;
|
||||||
|
enum gb_interrupt_kind kind;
|
||||||
|
|
||||||
|
interrupt->enable = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gb_interrupt_ime_set(struct gb_interrupt *interrupt)
|
||||||
|
{
|
||||||
|
interrupt->master_enable = true;
|
||||||
|
|
||||||
|
fire_if_pending(interrupt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gb_interrupt_ime_clear(struct gb_interrupt *interrupt)
|
||||||
|
{
|
||||||
|
interrupt->master_enable = false;
|
||||||
|
}
|
||||||
61
src/gbemu/interrupt.h
Normal file
61
src/gbemu/interrupt.h
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#ifndef INTERRUPT_H
|
||||||
|
#define INTERRUPT_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
struct lr35902_state;
|
||||||
|
|
||||||
|
enum gb_interrupt_kind {
|
||||||
|
GB_INT_MIN = 0,
|
||||||
|
GB_INT_VBLANK = 0,
|
||||||
|
GB_INT_LCD_STAT = 1,
|
||||||
|
GB_INT_TIMER = 2,
|
||||||
|
GB_INT_SERIAL = 3,
|
||||||
|
GB_INT_JOYPAD = 4,
|
||||||
|
GB_INT_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct gb_interrupt {
|
||||||
|
uint8_t flags; /* IF */
|
||||||
|
uint8_t enable; /* IE */
|
||||||
|
bool master_enable; /* IME */
|
||||||
|
|
||||||
|
struct lr35902_state *cpu;
|
||||||
|
};
|
||||||
|
|
||||||
|
void gb_interrupt_init(struct gb_interrupt *interrupt, struct lr35902_state *cpu);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The "hardware" interface to setting/clearing interrupts
|
||||||
|
*/
|
||||||
|
int gb_interrupt_set(struct gb_interrupt *interrupt, enum gb_interrupt_kind kind);
|
||||||
|
|
||||||
|
int gb_interrupt_clear(struct gb_interrupt *interrupt, enum gb_interrupt_kind kind);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IF - Interrupt Flags
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint8_t gb_interrupt_if_read(struct gb_interrupt *interrupt);
|
||||||
|
|
||||||
|
void gb_interrupt_if_write(struct gb_interrupt *interrupt, uint8_t val);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IE - Interrupt Enable
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint8_t gb_interrupt_ie_read(struct gb_interrupt *interrupt);
|
||||||
|
|
||||||
|
void gb_interrupt_ie_write(struct gb_interrupt *interrupt, uint8_t val);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IME - Interrupt Master Enable
|
||||||
|
*/
|
||||||
|
|
||||||
|
void gb_interrupt_ime_set(struct gb_interrupt *interrupt);
|
||||||
|
|
||||||
|
void gb_interrupt_ime_clear(struct gb_interrupt *interrupt);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "gbemu/memory.h"
|
#include "gbemu/memory.h"
|
||||||
#include "gbemu/video.h"
|
#include "gbemu/video.h"
|
||||||
|
#include "gbemu/cpu.h"
|
||||||
|
|
||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
|
|
||||||
@@ -19,17 +20,46 @@
|
|||||||
#define UNMAP_BOOTROM_ADDR 0xff50
|
#define UNMAP_BOOTROM_ADDR 0xff50
|
||||||
|
|
||||||
static struct gb_video *video;
|
static struct gb_video *video;
|
||||||
|
static struct gb_interrupt *interrupt;
|
||||||
|
|
||||||
static uint8_t ram[MAX_RAM_LEN];
|
static uint8_t ram[MAX_RAM_LEN];
|
||||||
static unsigned char bootrom[0x100] = {0};
|
static unsigned char bootrom[0x100] = {0};
|
||||||
static unsigned char cart_low[0x100] = {0};
|
static unsigned char cart_low[0x100] = {0};
|
||||||
static int bootrom_mapped = 1;
|
static int bootrom_mapped = 1;
|
||||||
|
|
||||||
void gb_mem_init(struct gb_memory *memory, struct gb_video *v)
|
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;
|
video = v;
|
||||||
memset(&ram, 0, MAX_RAM_LEN);
|
memset(&ram, 0, MAX_RAM_LEN);
|
||||||
bootrom_mapped = 1;
|
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_byte = 0;
|
||||||
@@ -80,6 +110,10 @@ uint8_t gb_mem_read(struct gb_memory *memory, uint16_t addr)
|
|||||||
return gb_serial_read(memory, addr);
|
return gb_serial_read(memory, addr);
|
||||||
case 0xFF40 ... 0xFF4B:
|
case 0xFF40 ... 0xFF4B:
|
||||||
return gb_video_mem_read(video, addr);
|
return gb_video_mem_read(video, addr);
|
||||||
|
case 0xFF0F:
|
||||||
|
return gb_interrupt_if_read(interrupt);
|
||||||
|
case 0xFFFF:
|
||||||
|
return gb_interrupt_ie_read(interrupt);
|
||||||
default:
|
default:
|
||||||
return ram[addr];
|
return ram[addr];
|
||||||
}
|
}
|
||||||
@@ -109,8 +143,12 @@ void gb_mbc3_write(struct gb_memory *memory, uint16_t addr, uint8_t val)
|
|||||||
case 0xFF40 ... 0xFF4B:
|
case 0xFF40 ... 0xFF4B:
|
||||||
gb_video_mem_write(video, addr, val);
|
gb_video_mem_write(video, addr, val);
|
||||||
break;
|
break;
|
||||||
|
case 0xFF0F:
|
||||||
|
gb_interrupt_if_write(interrupt, val);
|
||||||
|
break;
|
||||||
case 0xFFFF:
|
case 0xFFFF:
|
||||||
gb_log("Writing to interrupt mask: 0x%02x\n", val);
|
gb_interrupt_ie_write(interrupt, val);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
ram[addr] = val;
|
ram[addr] = val;
|
||||||
}
|
}
|
||||||
@@ -137,6 +175,12 @@ void gb_mem_write(struct gb_memory *memory, uint16_t addr, uint8_t val)
|
|||||||
case 0xFF40 ... 0xFF4B:
|
case 0xFF40 ... 0xFF4B:
|
||||||
gb_video_mem_write(video, addr, val);
|
gb_video_mem_write(video, addr, val);
|
||||||
return;
|
return;
|
||||||
|
case 0xFF0F:
|
||||||
|
gb_interrupt_if_write(interrupt, val);
|
||||||
|
return;
|
||||||
|
case 0xFFFF:
|
||||||
|
gb_interrupt_ie_write(interrupt, val);
|
||||||
|
return;
|
||||||
case 0x8000 ... 0x87FF:
|
case 0x8000 ... 0x87FF:
|
||||||
case 0x9800 ... 0x9BFF:
|
case 0x9800 ... 0x9BFF:
|
||||||
break;
|
break;
|
||||||
@@ -145,9 +189,6 @@ void gb_mem_write(struct gb_memory *memory, uint16_t addr, uint8_t val)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0xFFFF:
|
|
||||||
gb_log("Writing to interrupt mask: 0x%02x\n", val);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ram[addr] = val;
|
ram[addr] = val;
|
||||||
@@ -169,67 +210,3 @@ void gb_mem_force_write(struct gb_memory *memory, uint16_t addr, uint8_t 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); */
|
|
||||||
/* } */
|
|
||||||
|
|||||||
@@ -5,17 +5,11 @@
|
|||||||
|
|
||||||
struct gb_memory;
|
struct gb_memory;
|
||||||
struct gb_video;
|
struct gb_video;
|
||||||
|
struct gb_interrupt;
|
||||||
|
|
||||||
enum gb_memory_range {
|
void gb_mem_init(struct gb_memory *memory, struct gb_video *v,
|
||||||
GB_MEMORY_BOOTROM,
|
struct gb_interrupt *interrupt,
|
||||||
GB_MEMORY_CART,
|
const char *bootrom, const char *rom);
|
||||||
};
|
|
||||||
|
|
||||||
void gb_mem_init(struct gb_memory *memory, struct gb_video *v);
|
|
||||||
|
|
||||||
int gb_memory_load_file(struct gb_memory *memory,
|
|
||||||
enum gb_memory_range destination,
|
|
||||||
char *filename);
|
|
||||||
|
|
||||||
uint8_t gb_mem_read(struct gb_memory *mem, uint16_t addr);
|
uint8_t gb_mem_read(struct gb_memory *mem, uint16_t addr);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "gbemu/video.h"
|
#include "gbemu/video.h"
|
||||||
#include "gbemu/memory.h"
|
#include "gbemu/memory.h"
|
||||||
|
#include "gbemu/interrupt.h"
|
||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
#include "common/bmp.h"
|
#include "common/bmp.h"
|
||||||
|
|
||||||
@@ -9,7 +10,6 @@
|
|||||||
/* TODO: This whole implementation is very simple.*/
|
/* TODO: This whole implementation is very simple.*/
|
||||||
/* TODO: Actual graphics output */
|
/* TODO: Actual graphics output */
|
||||||
/* TODO: Implementation of most registers */
|
/* TODO: Implementation of most registers */
|
||||||
/* TODO: Interrupts */
|
|
||||||
|
|
||||||
static struct bmp *bmp;
|
static struct bmp *bmp;
|
||||||
|
|
||||||
@@ -28,12 +28,13 @@ static uint8_t color_tbl[4] = {
|
|||||||
0,
|
0,
|
||||||
};
|
};
|
||||||
|
|
||||||
void gb_video_init(struct gb_video *video, struct gb_memory *memory)
|
void gb_video_init(struct gb_video *video, struct gb_memory *memory, struct gb_interrupt *interrupt)
|
||||||
{
|
{
|
||||||
memset(video, 0, sizeof(*video));
|
memset(video, 0, sizeof(*video));
|
||||||
|
|
||||||
video->debug_logging = 0;
|
video->debug_logging = 0;
|
||||||
video->memory = memory;
|
video->memory = memory;
|
||||||
|
video->interrupt = interrupt;
|
||||||
|
|
||||||
bmp = bmp_new(BMP_GRAYSCALE, BUFFER_WIDTH, BUFFER_HEIGHT);
|
bmp = bmp_new(BMP_GRAYSCALE, BUFFER_WIDTH, BUFFER_HEIGHT);
|
||||||
}
|
}
|
||||||
@@ -114,12 +115,15 @@ void gb_video_cycle(struct gb_video *video, int cycles)
|
|||||||
video->curline += 1;
|
video->curline += 1;
|
||||||
if (video->curline > LCD_Y_MAX) {
|
if (video->curline > LCD_Y_MAX) {
|
||||||
video->curline = 0;
|
video->curline = 0;
|
||||||
|
gb_interrupt_clear(video->interrupt, GB_INT_VBLANK);
|
||||||
if (video->lcdcont & (1 << 7)) {
|
if (video->lcdcont & (1 << 7)) {
|
||||||
screenshot_count++;
|
screenshot_count++;
|
||||||
if (screenshot_count % 30 == 0) {
|
if (screenshot_count % 30 == 0) {
|
||||||
gb_video_screenshot(video);
|
gb_video_screenshot(video);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (video->curline == 144) {
|
||||||
|
gb_interrupt_set(video->interrupt, GB_INT_VBLANK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,9 +95,10 @@ struct gb_video {
|
|||||||
int debug_logging;
|
int debug_logging;
|
||||||
|
|
||||||
struct gb_memory *memory;
|
struct gb_memory *memory;
|
||||||
|
struct gb_interrupt *interrupt;
|
||||||
};
|
};
|
||||||
|
|
||||||
void gb_video_init(struct gb_video *video, struct gb_memory *memory);
|
void gb_video_init(struct gb_video *video, struct gb_memory *memory, struct gb_interrupt *interrupt);
|
||||||
void gb_video_cycle(struct gb_video *video, int cycles);
|
void gb_video_cycle(struct gb_video *video, int cycles);
|
||||||
uint8_t gb_video_mem_read(struct gb_video *video, uint16_t addr);
|
uint8_t gb_video_mem_read(struct gb_video *video, uint16_t addr);
|
||||||
void gb_video_mem_write(struct gb_video *video, uint16_t addr, uint8_t val);
|
void gb_video_mem_write(struct gb_video *video, uint16_t addr, uint8_t val);
|
||||||
|
|||||||
Reference in New Issue
Block a user