SameBoy | Accurate GB/GBC emulator |
| download: https://git.y1.nz/archives/sameboy.tar.gz | |
| README | Files | Log | Refs | LICENSE |
commit 9996c7b4a21b8e5c47b9bddef64ad50305bf2e90 parent 39c71b40e7e6afe07df77fe21a4fc63281e861d9 Author: Lior Halphon <LIJI32@gmail.com> Date: Fri, 2 Apr 2021 19:08:03 +0300 Add GBS APIs Diffstat:
| M | Core/gb.c | 123 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | Core/gb.h | 29 | ++++++++++++++++++++++++++++- |
2 files changed, 151 insertions(+), 1 deletion(-)
diff --git a/Core/gb.c b/Core/gb.c @@ -308,6 +308,129 @@ int GB_load_rom(GB_gameboy_t *gb, const char *path) 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) { FILE *f = fopen(path, "rb"); diff --git a/Core/gb.h b/Core/gb.h @@ -325,6 +325,29 @@ typedef struct { uint8_t write_end; } 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. This allows adding data to the end of the section without worrying about future compatibility. Some other changes might be "safe" as well. @@ -718,6 +741,8 @@ struct GB_gameboy_internal_s { /* Temporary state */ bool wx_just_changed; 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); 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_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_to_buffer(GB_gameboy_t *gb, uint8_t *buffer, size_t size); int GB_save_battery(GB_gameboy_t *gb, const char *path);
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.