mirror of https://github.com/mlaux/gb6.git
Compare commits
4 Commits
b0147a60d4
...
b2a6890524
Author | SHA1 | Date |
---|---|---|
Matthew Laux | b2a6890524 | |
Matthew Laux | 03dab9524b | |
Matthew Laux | af18bb9a39 | |
Matthew Laux | d6b6988742 |
|
@ -13,6 +13,7 @@ extern "C" {
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "rom.h"
|
#include "rom.h"
|
||||||
#include "lcd.h"
|
#include "lcd.h"
|
||||||
|
#include "mbc.h"
|
||||||
#include "instructions.h"
|
#include "instructions.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +63,16 @@ GLuint make_output_texture() {
|
||||||
return image_texture;
|
return image_texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char default_palette[] = { 0xff, 0xaa, 0x55, 0x00 };
|
unsigned int default_palette[] = { 0x9ff4e5, 0x00b9be, 0x005f8c, 0x002b59 };
|
||||||
|
// ugly evenly spaced: { 0xffffff, 0xaaaaaa, 0x555555, 0x000000 };
|
||||||
|
// bgb default: {0xe0f8d0, 0x88c070, 0x346856, 0x081820};
|
||||||
|
|
||||||
|
// https://lospec.com/palette-list/blk-aqu4
|
||||||
|
// { 0x9ff4e5, 0x00b9be, 0x005f8c, 0x002b59 };
|
||||||
|
|
||||||
|
// https://lospec.com/palette-list/velvet-cherry-gb
|
||||||
|
// { 0x9775a6, 0x683a68, 0x412752, 0x2d162c };
|
||||||
|
|
||||||
|
|
||||||
void convert_output(struct lcd *lcd) {
|
void convert_output(struct lcd *lcd) {
|
||||||
int x, y;
|
int x, y;
|
||||||
|
@ -72,9 +82,9 @@ void convert_output(struct lcd *lcd) {
|
||||||
int val = lcd->buf[y * 256 + x];
|
int val = lcd->buf[y * 256 + x];
|
||||||
int fill = default_palette[val];
|
int fill = default_palette[val];
|
||||||
//int fill = val ? 255 : 0;
|
//int fill = val ? 255 : 0;
|
||||||
output_image[out_index++] = fill;
|
output_image[out_index++] = (fill >> 16) & 0xff;
|
||||||
output_image[out_index++] = fill;
|
output_image[out_index++] = (fill >> 8) & 0xff;
|
||||||
output_image[out_index++] = fill;
|
output_image[out_index++] = fill & 0xff;
|
||||||
output_image[out_index++] = 255;
|
output_image[out_index++] = 255;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,9 +93,9 @@ void convert_output(struct lcd *lcd) {
|
||||||
for (x = 0; x < 160; x++) {
|
for (x = 0; x < 160; x++) {
|
||||||
int val = lcd->pixels[y * 160 + x];
|
int val = lcd->pixels[y * 160 + x];
|
||||||
int fill = default_palette[val];
|
int fill = default_palette[val];
|
||||||
visible_pixels[out_index++] = fill;
|
visible_pixels[out_index++] = (fill >> 16) & 0xff;
|
||||||
visible_pixels[out_index++] = fill;
|
visible_pixels[out_index++] = (fill >> 8) & 0xff;
|
||||||
visible_pixels[out_index++] = fill;
|
visible_pixels[out_index++] = fill & 0xff;
|
||||||
visible_pixels[out_index++] = 255;
|
visible_pixels[out_index++] = 255;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,6 +164,7 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
dmg_new(&dmg, &cpu, &rom, &lcd);
|
dmg_new(&dmg, &cpu, &rom, &lcd);
|
||||||
cpu.dmg = &dmg;
|
cpu.dmg = &dmg;
|
||||||
|
mbc_load_ram(dmg.rom->mbc, "save.sav");
|
||||||
// cpu_bind_mem_model(&cpu, &dmg, dmg_read, dmg_write);
|
// cpu_bind_mem_model(&cpu, &dmg, dmg_read, dmg_write);
|
||||||
|
|
||||||
cpu.pc = 0x100;
|
cpu.pc = 0x100;
|
||||||
|
@ -373,6 +384,7 @@ int main(int argc, char *argv[])
|
||||||
SDL_DestroyWindow(window);
|
SDL_DestroyWindow(window);
|
||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
|
|
||||||
|
mbc_save_ram(dmg.rom->mbc, "save.sav");
|
||||||
rom_free(&rom);
|
rom_free(&rom);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
104
src/dmg.c
104
src/dmg.c
|
@ -81,7 +81,7 @@ u8 dmg_read(void *_dmg, u16 address)
|
||||||
return dmg->zero_page[address - 0xff80];
|
return dmg->zero_page[address - 0xff80];
|
||||||
} else if (address == 0xff00) {
|
} else if (address == 0xff00) {
|
||||||
return get_button_state(dmg);
|
return get_button_state(dmg);
|
||||||
} else if (address == 0xff04) {
|
} else if (address == REG_TIMER_DIV) {
|
||||||
counter++;
|
counter++;
|
||||||
return counter;
|
return counter;
|
||||||
} else if (address == 0xff0f) {
|
} else if (address == 0xff0f) {
|
||||||
|
@ -145,34 +145,37 @@ void dmg_request_interrupt(struct dmg *dmg, int nr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO move to lcd.c, it needs to be able to access dmg_read though
|
// TODO move to lcd.c, it needs to be able to access dmg_read though
|
||||||
static void render_background(struct dmg *dmg, int lcdc)
|
static void render_background(struct dmg *dmg, int lcdc, int is_window)
|
||||||
{
|
{
|
||||||
int bg_base = (lcdc & LCDC_BG_TILE_MAP) ? 0x9c00 : 0x9800;
|
int bg_map = (lcdc & LCDC_BG_TILE_MAP) ? 0x9c00 : 0x9800;
|
||||||
int window_base = (lcdc & LCDC_WINDOW_TILE_MAP) ? 0x9c00 : 0x9800;
|
int window_map = (lcdc & LCDC_WINDOW_TILE_MAP) ? 0x9c00 : 0x9800;
|
||||||
int use_unsigned = lcdc & LCDC_BG_TILE_DATA;
|
int map_base_addr = is_window ? window_map : bg_map;
|
||||||
int tilebase = use_unsigned ? 0x8000 : 0x9000;
|
int unsigned_mode = lcdc & LCDC_BG_TILE_DATA;
|
||||||
|
int tile_base_addr = unsigned_mode ? 0x8000 : 0x9000;
|
||||||
|
int palette = lcd_read(dmg->lcd, REG_BGP);
|
||||||
|
u8 *dest = is_window ? dmg->lcd->window : dmg->lcd->buf;
|
||||||
|
|
||||||
//printf("%04x %04x %04x\n", bg_base, window_base, tilebase);
|
int tile_y, tile_x;
|
||||||
|
|
||||||
int k = 0, off = 0;
|
|
||||||
int tile_y = 0, tile_x = 0;
|
|
||||||
for (tile_y = 0; tile_y < 32; tile_y++) {
|
for (tile_y = 0; tile_y < 32; tile_y++) {
|
||||||
for (tile_x = 0; tile_x < 32; tile_x++) {
|
for (tile_x = 0; tile_x < 32; tile_x++) {
|
||||||
off = 256 * 8 * tile_y + 8 * tile_x;
|
int eff_addr, b, i;
|
||||||
int tile = dmg_read(dmg, bg_base + (tile_y * 32 + tile_x));
|
|
||||||
int eff_addr;
|
int off = 256 * 8 * tile_y + 8 * tile_x;
|
||||||
if (use_unsigned) {
|
int tile_index = dmg_read(dmg, map_base_addr + (tile_y * 32 + tile_x));
|
||||||
eff_addr = tilebase + 16 * tile;
|
|
||||||
|
if (unsigned_mode) {
|
||||||
|
eff_addr = tile_base_addr + 16 * tile_index;
|
||||||
} else {
|
} else {
|
||||||
eff_addr = tilebase + 16 * (signed char) tile;
|
eff_addr = tile_base_addr + 16 * (signed char) tile_index;
|
||||||
}
|
}
|
||||||
int b, i;
|
|
||||||
for (b = 0; b < 16; b += 2) {
|
for (b = 0; b < 16; b += 2) {
|
||||||
int data1 = dmg_read(dmg, eff_addr + b);
|
int data1 = dmg_read(dmg, eff_addr + b);
|
||||||
int data2 = dmg_read(dmg, eff_addr + b + 1);
|
int data2 = dmg_read(dmg, eff_addr + b + 1);
|
||||||
for (i = 7; i >= 0; i--) {
|
for (i = 7; i >= 0; i--) {
|
||||||
dmg->lcd->buf[off] = ((data1 & (1 << i)) ? 1 : 0) << 1;
|
int col_index = (data1 & (1 << i)) ? 1 : 0;
|
||||||
dmg->lcd->buf[off] |= (data2 & (1 << i)) ? 1 : 0;
|
col_index |= ((data2 & (1 << i)) ? 1 : 0) << 1;
|
||||||
|
dest[off] = (palette >> (col_index << 1)) & 3;
|
||||||
off++;
|
off++;
|
||||||
}
|
}
|
||||||
off += 248;
|
off += 248;
|
||||||
|
@ -191,8 +194,8 @@ struct oam_entry {
|
||||||
// TODO: only ten per scanline, priority, attributes, move to lcd.c
|
// TODO: only ten per scanline, priority, attributes, move to lcd.c
|
||||||
static void render_objs(struct dmg *dmg)
|
static void render_objs(struct dmg *dmg)
|
||||||
{
|
{
|
||||||
struct oam_entry *oam = (struct oam_entry *) dmg->lcd->oam;
|
|
||||||
int k, lcd_x, lcd_y, off;
|
int k, lcd_x, lcd_y, off;
|
||||||
|
struct oam_entry *oam = (struct oam_entry *) dmg->lcd->oam;
|
||||||
int tall = lcd_isset(dmg->lcd, REG_LCDC, LCDC_OBJ_SIZE);
|
int tall = lcd_isset(dmg->lcd, REG_LCDC, LCDC_OBJ_SIZE);
|
||||||
|
|
||||||
for (k = 0; k < 40; k++, oam++) {
|
for (k = 0; k < 40; k++, oam++) {
|
||||||
|
@ -202,23 +205,26 @@ static void render_objs(struct dmg *dmg)
|
||||||
if (oam->pos_x == 0 || oam->pos_x >= 168) {
|
if (oam->pos_x == 0 || oam->pos_x >= 168) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
int first_tile = 0x8000 + 16 * oam->tile;
|
||||||
|
int palette = oam->attrs & OAM_ATTR_PALETTE
|
||||||
|
? lcd_read(dmg->lcd, REG_OBP1)
|
||||||
|
: lcd_read(dmg->lcd, REG_OBP0);
|
||||||
|
|
||||||
lcd_x = oam->pos_x - 8;
|
lcd_x = oam->pos_x - 8;
|
||||||
lcd_y = oam->pos_y - 16;
|
lcd_y = oam->pos_y - 16;
|
||||||
|
|
||||||
off = 160 * lcd_y + lcd_x;
|
off = 160 * lcd_y + lcd_x;
|
||||||
int eff_addr = 0x8000 + 16 * oam->tile;
|
int b, i, tile_bytes = 16;
|
||||||
int b, i, limit = 16;
|
|
||||||
if (tall) {
|
if (tall) {
|
||||||
limit = 32;
|
tile_bytes = 32;
|
||||||
}
|
}
|
||||||
for (b = 0; b < limit; b += 2) {
|
for (b = 0; b < tile_bytes; b += 2) {
|
||||||
int use_tile = b;
|
int use_tile = b;
|
||||||
if (oam->attrs & OAM_ATTR_MIRROR_Y) {
|
if (oam->attrs & OAM_ATTR_MIRROR_Y) {
|
||||||
use_tile = (limit - 2) - b;
|
use_tile = (tile_bytes - 2) - b;
|
||||||
}
|
}
|
||||||
int data1 = dmg_read(dmg, eff_addr + use_tile);
|
int data1 = dmg_read(dmg, first_tile + use_tile);
|
||||||
int data2 = dmg_read(dmg, eff_addr + use_tile + 1);
|
int data2 = dmg_read(dmg, first_tile + use_tile + 1);
|
||||||
for (i = 7; i >= 0; i--) {
|
for (i = 7; i >= 0; i--) {
|
||||||
if (off < 0 || off >= 160 * 144) {
|
if (off < 0 || off >= 160 * 144) {
|
||||||
// terrible clipping. need to not have an if per-pixel
|
// terrible clipping. need to not have an if per-pixel
|
||||||
|
@ -228,10 +234,10 @@ static void render_objs(struct dmg *dmg)
|
||||||
if (oam->attrs & OAM_ATTR_MIRROR_X) {
|
if (oam->attrs & OAM_ATTR_MIRROR_X) {
|
||||||
use_index = 7 - i;
|
use_index = 7 - i;
|
||||||
}
|
}
|
||||||
int color = ((data1 & (1 << use_index)) ? 1 : 0) << 1
|
int col_index = ((data1 & (1 << use_index)) ? 1 : 0)
|
||||||
| ((data2 & (1 << use_index)) ? 1 : 0);
|
| ((data2 & (1 << use_index)) ? 1 : 0) << 1;
|
||||||
if (color) {
|
if (col_index) {
|
||||||
dmg->lcd->pixels[off] = color;
|
dmg->lcd->pixels[off] = (palette >> (col_index << 1)) & 3;
|
||||||
}
|
}
|
||||||
off++;
|
off++;
|
||||||
}
|
}
|
||||||
|
@ -240,6 +246,31 @@ static void render_objs(struct dmg *dmg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void timer_step(struct dmg *dmg)
|
||||||
|
{
|
||||||
|
if (!(dmg_read(dmg, REG_TIMER_CONTROL) & TIMER_CONTROL_ENABLED)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int passed = dmg->cpu->cycle_count - dmg->last_timer_update;
|
||||||
|
// TODO
|
||||||
|
if (passed < 10000) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 counter = dmg_read(dmg, REG_TIMER_COUNT);
|
||||||
|
u8 modulo = dmg_read(dmg, REG_TIMER_MOD);
|
||||||
|
|
||||||
|
counter++;
|
||||||
|
if (!counter) {
|
||||||
|
counter = modulo;
|
||||||
|
dmg_request_interrupt(dmg, INT_TIMER);
|
||||||
|
}
|
||||||
|
|
||||||
|
dmg_write(dmg, REG_TIMER_COUNT, counter);
|
||||||
|
dmg->last_timer_update = dmg->cpu->cycle_count;
|
||||||
|
}
|
||||||
|
|
||||||
void dmg_step(void *_dmg)
|
void dmg_step(void *_dmg)
|
||||||
{
|
{
|
||||||
struct dmg *dmg = (struct dmg *) _dmg;
|
struct dmg *dmg = (struct dmg *) _dmg;
|
||||||
|
@ -247,9 +278,9 @@ void dmg_step(void *_dmg)
|
||||||
// order of dependencies? i think cpu needs to step first then update
|
// order of dependencies? i think cpu needs to step first then update
|
||||||
// all other hw
|
// all other hw
|
||||||
cpu_step(dmg->cpu);
|
cpu_step(dmg->cpu);
|
||||||
|
// timer_step(dmg);
|
||||||
|
|
||||||
// each line takes 456 cycles
|
// each line takes 456 cycles
|
||||||
|
|
||||||
int cycle_diff = dmg->cpu->cycle_count - dmg->last_lcd_update;
|
int cycle_diff = dmg->cpu->cycle_count - dmg->last_lcd_update;
|
||||||
|
|
||||||
if (cycle_diff >= 456) {
|
if (cycle_diff >= 456) {
|
||||||
|
@ -280,14 +311,15 @@ void dmg_step(void *_dmg)
|
||||||
|
|
||||||
int lcdc = lcd_read(dmg->lcd, REG_LCDC);
|
int lcdc = lcd_read(dmg->lcd, REG_LCDC);
|
||||||
if (lcdc & LCDC_ENABLE_BG) {
|
if (lcdc & LCDC_ENABLE_BG) {
|
||||||
render_background(dmg, lcdc);
|
render_background(dmg, lcdc, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lcdc & LCDC_ENABLE_WINDOW) {
|
int window_enabled = lcdc & LCDC_ENABLE_WINDOW;
|
||||||
// printf("window\n");
|
if (window_enabled) {
|
||||||
|
render_background(dmg, lcdc, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
lcd_apply_scroll(dmg->lcd);
|
lcd_apply_scroll(dmg->lcd, window_enabled);
|
||||||
|
|
||||||
if (lcdc & LCDC_ENABLE_OBJ) {
|
if (lcdc & LCDC_ENABLE_OBJ) {
|
||||||
render_objs(dmg);
|
render_objs(dmg);
|
||||||
|
|
|
@ -15,6 +15,13 @@
|
||||||
#define BUTTON_SELECT (1 << 2)
|
#define BUTTON_SELECT (1 << 2)
|
||||||
#define BUTTON_START (1 << 3)
|
#define BUTTON_START (1 << 3)
|
||||||
|
|
||||||
|
#define REG_TIMER_DIV 0xFF04
|
||||||
|
#define REG_TIMER_COUNT 0xFF05
|
||||||
|
#define REG_TIMER_MOD 0xFF06
|
||||||
|
#define REG_TIMER_CONTROL 0xFF07
|
||||||
|
|
||||||
|
#define TIMER_CONTROL_ENABLED (1 << 2)
|
||||||
|
|
||||||
struct cpu;
|
struct cpu;
|
||||||
struct rom;
|
struct rom;
|
||||||
struct lcd;
|
struct lcd;
|
||||||
|
@ -27,6 +34,7 @@ struct dmg {
|
||||||
u8 video_ram[0x2000];
|
u8 video_ram[0x2000];
|
||||||
u8 zero_page[0x80];
|
u8 zero_page[0x80];
|
||||||
u32 last_lcd_update;
|
u32 last_lcd_update;
|
||||||
|
u32 last_timer_update;
|
||||||
int joypad_selected;
|
int joypad_selected;
|
||||||
int action_selected; // non-0 if A/B/start/select selected, 0 for directions
|
int action_selected; // non-0 if A/B/start/select selected, 0 for directions
|
||||||
u8 interrupt_enabled;
|
u8 interrupt_enabled;
|
||||||
|
|
19
src/lcd.c
19
src/lcd.c
|
@ -29,8 +29,11 @@ void lcd_set_mode(struct lcd *lcd, int mode)
|
||||||
|
|
||||||
void lcd_new(struct lcd *lcd)
|
void lcd_new(struct lcd *lcd)
|
||||||
{
|
{
|
||||||
lcd->buf = malloc(256 * 256);
|
const size_t size = 256 * 256;
|
||||||
memset(lcd->buf, 0, 256 * 256);
|
lcd->buf = malloc(size);
|
||||||
|
lcd->window = malloc(size);
|
||||||
|
memset(lcd->buf, 0, size);
|
||||||
|
memset(lcd->window, 0, size);
|
||||||
// todo < 8 bpp
|
// todo < 8 bpp
|
||||||
lcd->pixels = malloc(LCD_WIDTH * LCD_HEIGHT);
|
lcd->pixels = malloc(LCD_WIDTH * LCD_HEIGHT);
|
||||||
}
|
}
|
||||||
|
@ -66,18 +69,26 @@ void lcd_put_pixel(struct lcd *lcd, u8 x, u8 y, u8 value)
|
||||||
lcd->pixels[y * LCD_WIDTH + x] = value;
|
lcd->pixels[y * LCD_WIDTH + x] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void lcd_apply_scroll(struct lcd *lcd)
|
void lcd_apply_scroll(struct lcd *lcd, int use_window)
|
||||||
{
|
{
|
||||||
int scroll_y = lcd_read(lcd, REG_SCY);
|
int scroll_y = lcd_read(lcd, REG_SCY);
|
||||||
int scroll_x = lcd_read(lcd, REG_SCX);
|
int scroll_x = lcd_read(lcd, REG_SCX);
|
||||||
|
|
||||||
|
int win_x = lcd_read(lcd, REG_WX) - 7;
|
||||||
|
int win_y = lcd_read(lcd, REG_WY);
|
||||||
|
|
||||||
int lines;
|
int lines;
|
||||||
for (lines = 0; lines < 144; lines++) {
|
for (lines = 0; lines < 144; lines++) {
|
||||||
int src_y = (scroll_y + lines) & 0xff;
|
int src_y = (scroll_y + lines) & 0xff;
|
||||||
int cols;
|
int cols;
|
||||||
for (cols = 0; cols < 160; cols++) {
|
for (cols = 0; cols < 160; cols++) {
|
||||||
int src_off = (src_y << 8) + ((scroll_x + cols) & 0xff);
|
int src_off = (src_y << 8) + ((scroll_x + cols) & 0xff);
|
||||||
lcd->pixels[lines * 160 + cols] = lcd->buf[src_off];
|
u8 *src = lcd->buf;
|
||||||
|
if (use_window && cols >= win_x && lines >= win_y) {
|
||||||
|
src_off = (((lines - win_y) & 0xff) << 8) + ((cols - win_x) & 0xff);
|
||||||
|
src = lcd->window;
|
||||||
|
}
|
||||||
|
lcd->pixels[lines * 160 + cols] = src[src_off];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,7 @@ struct lcd {
|
||||||
u8 oam[0xa0];
|
u8 oam[0xa0];
|
||||||
u8 regs[0x0c];
|
u8 regs[0x0c];
|
||||||
u8 *buf; // 256x256
|
u8 *buf; // 256x256
|
||||||
|
u8 *window;
|
||||||
u8 *pixels; // the actual 160x144 visible area
|
u8 *pixels; // the actual 160x144 visible area
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -64,7 +65,7 @@ int lcd_isset(struct lcd *lcd, u16 addr, u8 bit);
|
||||||
void lcd_set_mode(struct lcd *lcd, int mode);
|
void lcd_set_mode(struct lcd *lcd, int mode);
|
||||||
|
|
||||||
int lcd_step(struct lcd *lcd);
|
int lcd_step(struct lcd *lcd);
|
||||||
void lcd_apply_scroll(struct lcd *lcd);
|
void lcd_apply_scroll(struct lcd *lcd, int use_window);
|
||||||
|
|
||||||
// output the pixels to the screen
|
// output the pixels to the screen
|
||||||
void lcd_draw(struct lcd *lcd);
|
void lcd_draw(struct lcd *lcd);
|
||||||
|
|
45
src/mbc.c
45
src/mbc.c
|
@ -57,6 +57,7 @@ struct mbc *mbc_new(int type)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
mbc.type = type;
|
mbc.type = type;
|
||||||
|
mbc.has_battery = type == 3;
|
||||||
|
|
||||||
return &mbc;
|
return &mbc;
|
||||||
}
|
}
|
||||||
|
@ -76,3 +77,47 @@ int mbc_write(struct mbc *mbc, struct dmg *dmg, u16 addr, u8 data)
|
||||||
}
|
}
|
||||||
return mbc1_write(mbc, dmg, addr, data);
|
return mbc1_write(mbc, dmg, addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int mbc_save_ram(struct mbc *mbc, const char *filename)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
if (!mbc->has_battery) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fp = fopen(filename, "w");
|
||||||
|
if (!fp) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fwrite(mbc->ram, 1, RAM_SIZE, fp) < RAM_SIZE) {
|
||||||
|
fclose(fp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mbc_load_ram(struct mbc *mbc, const char *filename)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
if (!mbc->has_battery) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fp = fopen(filename, "r");
|
||||||
|
if (!fp) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fread(mbc->ram, 1, RAM_SIZE, fp) < RAM_SIZE) {
|
||||||
|
fclose(fp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
return 1;
|
||||||
|
}
|
|
@ -3,14 +3,17 @@
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
|
#define RAM_SIZE 0x8000
|
||||||
|
|
||||||
struct dmg;
|
struct dmg;
|
||||||
|
|
||||||
struct mbc {
|
struct mbc {
|
||||||
int type;
|
int type;
|
||||||
|
int has_battery;
|
||||||
int rom_bank;
|
int rom_bank;
|
||||||
int ram_bank;
|
int ram_bank;
|
||||||
int ram_enabled;
|
int ram_enabled;
|
||||||
u8 ram[0x8000];
|
u8 ram[RAM_SIZE];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mbc *mbc_new(int type);
|
struct mbc *mbc_new(int type);
|
||||||
|
@ -19,5 +22,8 @@ struct mbc *mbc_new(int type);
|
||||||
int mbc_read(struct mbc *mbc, struct dmg *dmg, u16 addr, u8 *out_data);
|
int mbc_read(struct mbc *mbc, struct dmg *dmg, u16 addr, u8 *out_data);
|
||||||
// return 1 if handled, return 0 for base dmg behavior
|
// return 1 if handled, return 0 for base dmg behavior
|
||||||
int mbc_write(struct mbc *mbc, struct dmg *dmg, u16 addr, u8 data);
|
int mbc_write(struct mbc *mbc, struct dmg *dmg, u16 addr, u8 data);
|
||||||
|
// 1 if success, 0 if error
|
||||||
|
int mbc_save_ram(struct mbc *mbc, const char *filename);
|
||||||
|
int mbc_load_ram(struct mbc *mbc, const char *filename);
|
||||||
|
|
||||||
#endif
|
#endif
|
Loading…
Reference in New Issue