Add GBS APIs
This commit is contained in:
parent
39c71b40e7
commit
9996c7b4a2
123
Core/gb.c
123
Core/gb.c
@ -308,6 +308,129 @@ int GB_load_rom(GB_gameboy_t *gb, const char *path)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GB_gbs_switch_track(GB_gameboy_t *gb, uint8_t track)
|
||||||
|
{
|
||||||
|
GB_reset(gb);
|
||||||
|
GB_write_memory(gb, 0xFF00 + GB_IO_LCDC, 0x80);
|
||||||
|
GB_write_memory(gb, 0xFF00 + GB_IO_TAC, gb->gbs_header.TAC);
|
||||||
|
GB_write_memory(gb, 0xFF00 + GB_IO_TMA, gb->gbs_header.TMA);
|
||||||
|
GB_write_memory(gb, 0xFF00 + GB_IO_NR52, 0x80);
|
||||||
|
GB_write_memory(gb, 0xFF00 + GB_IO_NR51, 0xFF);
|
||||||
|
GB_write_memory(gb, 0xFF00 + GB_IO_NR50, 0x77);
|
||||||
|
memset(gb->ram, 0, gb->ram_size);
|
||||||
|
memset(gb->hram, 0, sizeof(gb->hram));
|
||||||
|
memset(gb->oam, 0, sizeof(gb->oam));
|
||||||
|
if (gb->gbs_header.TAC || gb->gbs_header.TMA) {
|
||||||
|
GB_write_memory(gb, 0xFFFF, 0x04);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
GB_write_memory(gb, 0xFFFF, 0x01);
|
||||||
|
}
|
||||||
|
if (gb->gbs_header.TAC & 0x80) {
|
||||||
|
gb->cgb_double_speed = true; // Might mean double speed mode on a DMG
|
||||||
|
}
|
||||||
|
gb->sp = LE16(gb->gbs_header.sp);
|
||||||
|
gb->pc = 0x100;
|
||||||
|
gb->boot_rom_finished = true;
|
||||||
|
gb->a = track;
|
||||||
|
if (gb->sgb) {
|
||||||
|
gb->sgb->intro_animation = GB_SGB_INTRO_ANIMATION_LENGTH;
|
||||||
|
gb->sgb->disable_commands = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int GB_load_gbs(GB_gameboy_t *gb, const char *path, GB_gbs_info_t *info)
|
||||||
|
{
|
||||||
|
FILE *f = fopen(path, "rb");
|
||||||
|
if (!f) {
|
||||||
|
GB_log(gb, "Could not open GBS: %s.\n", strerror(errno));
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
fread(&gb->gbs_header, sizeof(gb->gbs_header), 1, f);
|
||||||
|
if (gb->gbs_header.magic != htonl('GBS\x01') ||
|
||||||
|
LE16(gb->gbs_header.load_address) < 0x400 ||
|
||||||
|
LE16(gb->gbs_header.load_address) >= 0x8000) {
|
||||||
|
GB_log(gb, "Not a valid GBS file: %s.\n", strerror(errno));
|
||||||
|
fclose(f);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
size_t data_size = ftell(f) - sizeof(gb->gbs_header);
|
||||||
|
gb->rom_size = (data_size + LE16(gb->gbs_header.load_address) + 0x3FFF) & ~0x3FFF; /* Round to bank */
|
||||||
|
/* And then round to a power of two */
|
||||||
|
while (gb->rom_size & (gb->rom_size - 1)) {
|
||||||
|
/* I promise this works. */
|
||||||
|
gb->rom_size |= gb->rom_size >> 1;
|
||||||
|
gb->rom_size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gb->rom_size == 0) {
|
||||||
|
gb->rom_size = 0x8000;
|
||||||
|
}
|
||||||
|
fseek(f, sizeof(gb->gbs_header), SEEK_SET);
|
||||||
|
if (gb->rom) {
|
||||||
|
free(gb->rom);
|
||||||
|
}
|
||||||
|
gb->rom = malloc(gb->rom_size);
|
||||||
|
memset(gb->rom, 0xFF, gb->rom_size); /* Pad with 0xFFs */
|
||||||
|
fread(gb->rom + LE16(gb->gbs_header.load_address), 1, data_size, f);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
gb->cartridge_type = &GB_cart_defs[0x11];
|
||||||
|
if (gb->mbc_ram) {
|
||||||
|
free(gb->mbc_ram);
|
||||||
|
gb->mbc_ram = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gb->cartridge_type->has_ram) {
|
||||||
|
gb->mbc_ram_size = 0x2000;
|
||||||
|
gb->mbc_ram = malloc(gb->mbc_ram_size);
|
||||||
|
memset(gb->mbc_ram, 0xFF, gb->mbc_ram_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate interrupt handlers
|
||||||
|
for (unsigned i = 0; i <= 0x38; i += 8) {
|
||||||
|
gb->rom[i] = 0xc3; // jp $XXXX
|
||||||
|
gb->rom[i + 1] = (LE16(gb->gbs_header.load_address) + i);
|
||||||
|
gb->rom[i + 2] = (LE16(gb->gbs_header.load_address) + i) >> 8;
|
||||||
|
}
|
||||||
|
for (unsigned i = 0x40; i <= 0x60; i += 8) {
|
||||||
|
gb->rom[i] = 0xc9; // ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate entry
|
||||||
|
memcpy(gb->rom + 0x100, (uint8_t[]) {
|
||||||
|
0xCD, // Call $XXXX
|
||||||
|
LE16(gb->gbs_header.init_address),
|
||||||
|
LE16(gb->gbs_header.init_address) >> 8,
|
||||||
|
0x76, // HALT
|
||||||
|
0x00, // NOP
|
||||||
|
0xAF, // XOR a
|
||||||
|
0xE0, // LDH [$FFXX], a
|
||||||
|
GB_IO_IF,
|
||||||
|
0xCD, // Call $XXXX
|
||||||
|
LE16(gb->gbs_header.play_address),
|
||||||
|
LE16(gb->gbs_header.play_address) >> 8,
|
||||||
|
0x18, // JR pc ± $XX
|
||||||
|
-10 // To HALT
|
||||||
|
}, 13);
|
||||||
|
|
||||||
|
GB_gbs_switch_track(gb, gb->gbs_header.first_track - 1);
|
||||||
|
if (info) {
|
||||||
|
memset(info, 0, sizeof(*info));
|
||||||
|
info->first_track = gb->gbs_header.first_track - 1;
|
||||||
|
info->track_count = gb->gbs_header.track_count;
|
||||||
|
memcpy(info->title, gb->gbs_header.title, sizeof(gb->gbs_header.title));
|
||||||
|
memcpy(info->author, gb->gbs_header.author, sizeof(gb->gbs_header.author));
|
||||||
|
memcpy(info->copyright, gb->gbs_header.copyright, sizeof(gb->gbs_header.copyright));
|
||||||
|
}
|
||||||
|
|
||||||
|
gb->tried_loading_sgb_border = true; // Don't even attempt on GBS files
|
||||||
|
gb->has_sgb_border = false;
|
||||||
|
load_default_border(gb);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int GB_load_isx(GB_gameboy_t *gb, const char *path)
|
int GB_load_isx(GB_gameboy_t *gb, const char *path)
|
||||||
{
|
{
|
||||||
FILE *f = fopen(path, "rb");
|
FILE *f = fopen(path, "rb");
|
||||||
|
29
Core/gb.h
29
Core/gb.h
@ -325,6 +325,29 @@ typedef struct {
|
|||||||
uint8_t write_end;
|
uint8_t write_end;
|
||||||
} GB_fifo_t;
|
} GB_fifo_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t magic;
|
||||||
|
uint8_t track_count;
|
||||||
|
uint8_t first_track;
|
||||||
|
uint16_t load_address;
|
||||||
|
uint16_t init_address;
|
||||||
|
uint16_t play_address;
|
||||||
|
uint16_t sp;
|
||||||
|
uint8_t TMA;
|
||||||
|
uint8_t TAC;
|
||||||
|
char title[32];
|
||||||
|
char author[32];
|
||||||
|
char copyright[32];
|
||||||
|
} GB_gbs_header_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t track_count;
|
||||||
|
uint8_t first_track;
|
||||||
|
char title[33];
|
||||||
|
char author[33];
|
||||||
|
char copyright[33];
|
||||||
|
} GB_gbs_info_t;
|
||||||
|
|
||||||
/* When state saving, each section is dumped independently of other sections.
|
/* When state saving, each section is dumped independently of other sections.
|
||||||
This allows adding data to the end of the section without worrying about future compatibility.
|
This allows adding data to the end of the section without worrying about future compatibility.
|
||||||
Some other changes might be "safe" as well.
|
Some other changes might be "safe" as well.
|
||||||
@ -718,6 +741,8 @@ struct GB_gameboy_internal_s {
|
|||||||
/* Temporary state */
|
/* Temporary state */
|
||||||
bool wx_just_changed;
|
bool wx_just_changed;
|
||||||
bool tile_sel_glitch;
|
bool tile_sel_glitch;
|
||||||
|
|
||||||
|
GB_gbs_header_t gbs_header;
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -777,7 +802,9 @@ void GB_load_boot_rom_from_buffer(GB_gameboy_t *gb, const unsigned char *buffer,
|
|||||||
int GB_load_rom(GB_gameboy_t *gb, const char *path);
|
int GB_load_rom(GB_gameboy_t *gb, const char *path);
|
||||||
void GB_load_rom_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size);
|
void GB_load_rom_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size);
|
||||||
int GB_load_isx(GB_gameboy_t *gb, const char *path);
|
int GB_load_isx(GB_gameboy_t *gb, const char *path);
|
||||||
|
int GB_load_gbs(GB_gameboy_t *gb, const char *path, GB_gbs_info_t *info);
|
||||||
|
void GB_gbs_switch_track(GB_gameboy_t *gb, uint8_t track);
|
||||||
|
|
||||||
int GB_save_battery_size(GB_gameboy_t *gb);
|
int GB_save_battery_size(GB_gameboy_t *gb);
|
||||||
int GB_save_battery_to_buffer(GB_gameboy_t *gb, uint8_t *buffer, size_t size);
|
int GB_save_battery_to_buffer(GB_gameboy_t *gb, uint8_t *buffer, size_t size);
|
||||||
int GB_save_battery(GB_gameboy_t *gb, const char *path);
|
int GB_save_battery(GB_gameboy_t *gb, const char *path);
|
||||||
|
Loading…
Reference in New Issue
Block a user