New APIs, Document.m no longer requires GB_INTERNAL, fixed a bug where the sprite viewer showed incorrect sprites for some CGB exclusive games.

This commit is contained in:
Lior Halphon 2017-04-19 23:26:39 +03:00
parent 3feaeb153e
commit fb55c35f87
7 changed files with 183 additions and 70 deletions

View File

@ -1,4 +1,3 @@
#define GB_INTERNAL // Todo: several debugging functions access the GB struct directly
#include <AVFoundation/AVFoundation.h> #include <AVFoundation/AVFoundation.h>
#include <CoreAudio/CoreAudio.h> #include <CoreAudio/CoreAudio.h>
#include "GBAudioClient.h" #include "GBAudioClient.h"
@ -47,8 +46,8 @@
@property GBAudioClient *audioClient; @property GBAudioClient *audioClient;
- (void) vblank; - (void) vblank;
- (void) log: (const char *) log withAttributes: (GB_log_attributes) attributes; - (void) log: (const char *) log withAttributes: (GB_log_attributes) attributes;
- (const char *) getDebuggerInput; - (char *) getDebuggerInput;
- (const char *) getAsyncDebuggerInput; - (char *) getAsyncDebuggerInput;
- (void) cameraRequestUpdate; - (void) cameraRequestUpdate;
- (uint8_t) cameraGetPixelAtX:(uint8_t)x andY:(uint8_t)y; - (uint8_t) cameraGetPixelAtX:(uint8_t)x andY:(uint8_t)y;
- (void) printImage:(uint32_t *)image height:(unsigned) height - (void) printImage:(uint32_t *)image height:(unsigned) height
@ -71,14 +70,14 @@ static void consoleLog(GB_gameboy_t *gb, const char *string, GB_log_attributes a
static char *consoleInput(GB_gameboy_t *gb) static char *consoleInput(GB_gameboy_t *gb)
{ {
Document *self = (__bridge Document *)GB_get_user_data(gb); Document *self = (__bridge Document *)GB_get_user_data(gb);
return strdup([self getDebuggerInput]); return [self getDebuggerInput];
} }
static char *asyncConsoleInput(GB_gameboy_t *gb) static char *asyncConsoleInput(GB_gameboy_t *gb)
{ {
Document *self = (__bridge Document *)GB_get_user_data(gb); Document *self = (__bridge Document *)GB_get_user_data(gb);
const char *ret = [self getAsyncDebuggerInput]; char *ret = [self getAsyncDebuggerInput];
return ret? strdup(ret) : NULL; return ret;
} }
static uint32_t rgbEncode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b) static uint32_t rgbEncode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b)
@ -195,8 +194,7 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
if (!running) return; if (!running) return;
GB_debugger_set_disabled(&gb, true); GB_debugger_set_disabled(&gb, true);
if (GB_debugger_is_stopped(&gb)) { if (GB_debugger_is_stopped(&gb)) {
gb.debug_stopped = false; [self interruptDebugInputRead];
[self consoleInput:nil];
} }
stopping = true; stopping = true;
running = false; running = false;
@ -413,7 +411,7 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
return !GB_debugger_is_stopped(&gb); return !GB_debugger_is_stopped(&gb);
} }
else if ([anItem action] == @selector(reset:) && anItem.tag != 0) { else if ([anItem action] == @selector(reset:) && anItem.tag != 0) {
[(NSMenuItem*)anItem setState:(anItem.tag == 1 && !gb.is_cgb) || (anItem.tag == 2 && gb.is_cgb)]; [(NSMenuItem*)anItem setState:(anItem.tag == 1 && !GB_is_cgb(&gb)) || (anItem.tag == 2 && GB_is_cgb(&gb))];
} }
else if ([anItem action] == @selector(toggleBlend:)) { else if ([anItem action] == @selector(toggleBlend:)) {
[(NSMenuItem*)anItem setState:self.view.shouldBlendFrameWithPrevious]; [(NSMenuItem*)anItem setState:self.view.shouldBlendFrameWithPrevious];
@ -557,7 +555,14 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
[sender setStringValue:@""]; [sender setStringValue:@""];
} }
- (const char *) getDebuggerInput - (void) interruptDebugInputRead
{
[has_debugger_input lock];
[debugger_input_queue addObject:[NSNull null]];
[has_debugger_input unlockWithCondition:1];
}
- (char *) getDebuggerInput
{ {
[self log:">"]; [self log:">"];
in_sync_input = true; in_sync_input = true;
@ -566,10 +571,13 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
[debugger_input_queue removeObjectAtIndex:0]; [debugger_input_queue removeObjectAtIndex:0];
[has_debugger_input unlockWithCondition:[debugger_input_queue count] != 0]; [has_debugger_input unlockWithCondition:[debugger_input_queue count] != 0];
in_sync_input = false; in_sync_input = false;
return [input UTF8String]; if ((id) input == [NSNull null]) {
return NULL;
}
return strdup([input UTF8String]);
} }
- (const char *) getAsyncDebuggerInput - (char *) getAsyncDebuggerInput
{ {
[has_debugger_input lock]; [has_debugger_input lock];
NSString *input = [debugger_input_queue firstObject]; NSString *input = [debugger_input_queue firstObject];
@ -577,7 +585,7 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
[debugger_input_queue removeObjectAtIndex:0]; [debugger_input_queue removeObjectAtIndex:0];
} }
[has_debugger_input unlockWithCondition:[debugger_input_queue count] != 0]; [has_debugger_input unlockWithCondition:[debugger_input_queue count] != 0];
return [input UTF8String]; return input? strdup([input UTF8String]): NULL;
} }
- (IBAction)saveState:(id)sender - (IBAction)saveState:(id)sender
@ -731,8 +739,7 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
case 2: case 2:
/* OAM */ /* OAM */
{ {
oamCount = GB_get_oam_info(&gb, oamInfo); oamCount = GB_get_oam_info(&gb, oamInfo, &oamHeight);
oamHeight = (gb.io_registers[GB_IO_LCDC] & 4) ? 16:8;
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
if (!oamUpdating) { if (!oamUpdating) {
oamUpdating = true; oamUpdating = true;
@ -799,17 +806,23 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
uint16_t n_banks = 1; uint16_t n_banks = 1;
switch ([(GBMemoryByteArray *)(hex_controller.byteArray) mode]) { switch ([(GBMemoryByteArray *)(hex_controller.byteArray) mode]) {
case GBMemoryROM: case GBMemoryROM: {
n_banks = gb.rom_size / 0x4000; size_t rom_size;
GB_get_direct_access(&gb, GB_DIRECT_ACCESS_ROM, &rom_size, NULL);
n_banks = rom_size / 0x4000;
break; break;
}
case GBMemoryVRAM: case GBMemoryVRAM:
n_banks = gb.is_cgb ? 2 : 1; n_banks = GB_is_cgb(&gb) ? 2 : 1;
break; break;
case GBMemoryExternalRAM: case GBMemoryExternalRAM: {
n_banks = (gb.mbc_ram_size + 0x1FFF) / 0x2000; size_t ram_size;
GB_get_direct_access(&gb, GB_DIRECT_ACCESS_CART_RAM, &ram_size, NULL);
n_banks = (ram_size + 0x1FFF) / 0x2000;
break; break;
}
case GBMemoryRAM: case GBMemoryRAM:
n_banks = gb.is_cgb ? 8 : 1; n_banks = GB_is_cgb(&gb) ? 8 : 1;
break; break;
case GBMemoryEntireSpace: case GBMemoryEntireSpace:
break; break;
@ -833,23 +846,28 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
self.memoryBankItem.enabled = [sender indexOfSelectedItem] != GBMemoryEntireSpace; self.memoryBankItem.enabled = [sender indexOfSelectedItem] != GBMemoryEntireSpace;
GBMemoryByteArray *byteArray = (GBMemoryByteArray *)(hex_controller.byteArray); GBMemoryByteArray *byteArray = (GBMemoryByteArray *)(hex_controller.byteArray);
[byteArray setMode:(GB_memory_mode_t)[sender indexOfSelectedItem]]; [byteArray setMode:(GB_memory_mode_t)[sender indexOfSelectedItem]];
uint16_t bank;
switch ((GB_memory_mode_t)[sender indexOfSelectedItem]) { switch ((GB_memory_mode_t)[sender indexOfSelectedItem]) {
case GBMemoryEntireSpace: case GBMemoryEntireSpace:
case GBMemoryROM: case GBMemoryROM:
lineRep.valueOffset = 0; lineRep.valueOffset = 0;
byteArray.selectedBank = gb.mbc_rom_bank; GB_get_direct_access(&gb, GB_DIRECT_ACCESS_ROM, NULL, &bank);
byteArray.selectedBank = bank;
break; break;
case GBMemoryVRAM: case GBMemoryVRAM:
lineRep.valueOffset = 0x8000; lineRep.valueOffset = 0x8000;
byteArray.selectedBank = gb.cgb_vram_bank; GB_get_direct_access(&gb, GB_DIRECT_ACCESS_VRAM, NULL, &bank);
byteArray.selectedBank = bank;
break; break;
case GBMemoryExternalRAM: case GBMemoryExternalRAM:
lineRep.valueOffset = 0xA000; lineRep.valueOffset = 0xA000;
byteArray.selectedBank = gb.mbc_ram_bank; GB_get_direct_access(&gb, GB_DIRECT_ACCESS_CART_RAM, NULL, &bank);
byteArray.selectedBank = bank;
break; break;
case GBMemoryRAM: case GBMemoryRAM:
lineRep.valueOffset = 0xC000; lineRep.valueOffset = 0xC000;
byteArray.selectedBank = gb.cgb_ram_bank; GB_get_direct_access(&gb, GB_DIRECT_ACCESS_RAM, NULL, &bank);
byteArray.selectedBank = bank;
break; break;
} }
[self.memoryBankInput setStringValue:[NSString stringWithFormat:@"$%x", byteArray.selectedBank]]; [self.memoryBankInput setStringValue:[NSString stringWithFormat:@"$%x", byteArray.selectedBank]];
@ -1011,16 +1029,18 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
uint16_t map_base = 0x1800; uint16_t map_base = 0x1800;
GB_map_type_t map_type = (GB_map_type_t) self.tilemapMapButton.indexOfSelectedItem; GB_map_type_t map_type = (GB_map_type_t) self.tilemapMapButton.indexOfSelectedItem;
GB_tileset_type_t tileset_type = (GB_tileset_type_t) self.TilemapSetButton.indexOfSelectedItem; GB_tileset_type_t tileset_type = (GB_tileset_type_t) self.TilemapSetButton.indexOfSelectedItem;
uint8_t lcdc = ((uint8_t *)GB_get_direct_access(&gb, GB_DIRECT_ACCESS_IO, NULL, NULL))[GB_IO_LCDC];
uint8_t *vram = GB_get_direct_access(&gb, GB_DIRECT_ACCESS_VRAM, NULL, NULL);
if (map_type == GB_MAP_9C00 || (map_type == GB_MAP_AUTO && gb.io_registers[GB_IO_LCDC] & 0x08)) { if (map_type == GB_MAP_9C00 || (map_type == GB_MAP_AUTO && lcdc & 0x08)) {
map_base = 0x1c00; map_base = 0x1c00;
} }
if (tileset_type == GB_TILESET_AUTO) { if (tileset_type == GB_TILESET_AUTO) {
tileset_type = (gb.io_registers[GB_IO_LCDC] & 0x10)? GB_TILESET_8800 : GB_TILESET_8000; tileset_type = (lcdc & 0x10)? GB_TILESET_8800 : GB_TILESET_8000;
} }
uint8_t tile = gb.vram[map_base + map_offset]; uint8_t tile = vram[map_base + map_offset];
uint16_t tile_address = 0; uint16_t tile_address = 0;
if (tileset_type == GB_TILESET_8000) { if (tileset_type == GB_TILESET_8000) {
tile_address = 0x8000 + tile * 0x10; tile_address = 0x8000 + tile * 0x10;
@ -1029,8 +1049,8 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
tile_address = 0x9000 + (int8_t)tile * 0x10; tile_address = 0x9000 + (int8_t)tile * 0x10;
} }
if (gb.is_cgb) { if (GB_is_cgb(&gb)) {
uint8_t attributes = gb.vram[map_base + map_offset + 0x2000]; uint8_t attributes = vram[map_base + map_offset + 0x2000];
self.vramStatusLabel.stringValue = [NSString stringWithFormat:@"Tile number $%02x (%d:$%04x) at map address $%04x (Attributes: %c%c%c%d%d)", self.vramStatusLabel.stringValue = [NSString stringWithFormat:@"Tile number $%02x (%d:$%04x) at map address $%04x (Attributes: %c%c%c%d%d)",
tile, tile,
attributes & 0x8? 1 : 0, attributes & 0x8? 1 : 0,
@ -1073,9 +1093,10 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
return [NSString stringWithFormat:@"%s %d", row >=8 ? "Object" : "Background", (int)(row & 7)]; return [NSString stringWithFormat:@"%s %d", row >=8 ? "Object" : "Background", (int)(row & 7)];
} }
uint8_t *palette_data = GB_get_direct_access(&gb, row >= 8? GB_DIRECT_ACCESS_OBP : GB_DIRECT_ACCESS_BGP, NULL, NULL);
uint16_t index = columnIndex - 1 + (row & 7) * 4; uint16_t index = columnIndex - 1 + (row & 7) * 4;
return @(((row >= 8? gb.sprite_palletes_data : gb.background_palletes_data)[(index << 1) + 1] << 8) | return @((palette_data[(index << 1) + 1] << 8) | palette_data[(index << 1)]);
(row >= 8? gb.sprite_palletes_data : gb.background_palletes_data)[(index << 1)]);
} }
else if (tableView == self.spritesTableView) { else if (tableView == self.spritesTableView) {
switch (columnIndex) { switch (columnIndex) {
@ -1092,7 +1113,7 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
case 5: case 5:
return [NSString stringWithFormat:@"$%04x", oamInfo[row].oam_addr]; return [NSString stringWithFormat:@"$%04x", oamInfo[row].oam_addr];
case 6: case 6:
if (gb.cgb_mode) { if (GB_is_cgb(&gb)) {
return [NSString stringWithFormat:@"%c%c%c%d%d", return [NSString stringWithFormat:@"%c%c%c%d%d",
oamInfo[row].flags & 0x80? 'P' : '-', oamInfo[row].flags & 0x80? 'P' : '-',
oamInfo[row].flags & 0x40? 'Y' : '-', oamInfo[row].flags & 0x40? 'Y' : '-',

View File

@ -1368,7 +1368,7 @@ static bool palettes(GB_gameboy_t *gb, char *arguments, char *modifiers, const d
GB_log(gb, "Background palettes: \n"); GB_log(gb, "Background palettes: \n");
for (unsigned i = 0; i < 32; i++) { for (unsigned i = 0; i < 32; i++) {
GB_log(gb, "%04x ", ((uint16_t *)&gb->background_palletes_data)[i]); GB_log(gb, "%04x ", ((uint16_t *)&gb->background_palettes_data)[i]);
if (i % 4 == 3) { if (i % 4 == 3) {
GB_log(gb, "\n"); GB_log(gb, "\n");
} }
@ -1376,7 +1376,7 @@ static bool palettes(GB_gameboy_t *gb, char *arguments, char *modifiers, const d
GB_log(gb, "Sprites palettes: \n"); GB_log(gb, "Sprites palettes: \n");
for (unsigned i = 0; i < 32; i++) { for (unsigned i = 0; i < 32; i++) {
GB_log(gb, "%04x ", ((uint16_t *)&gb->sprite_palletes_data)[i]); GB_log(gb, "%04x ", ((uint16_t *)&gb->sprite_palettes_data)[i]);
if (i % 4 == 3) { if (i % 4 == 3) {
GB_log(gb, "\n"); GB_log(gb, "\n");
} }
@ -1704,6 +1704,12 @@ next_command:
gb->stack_leak_detection = false; gb->stack_leak_detection = false;
input = gb->input_callback(gb); input = gb->input_callback(gb);
if (input == NULL) {
/* Debugging is no currently available, continue running */
gb->debug_stopped = false;
return;
}
if (GB_debugger_do_command(gb, input)) { if (GB_debugger_do_command(gb, input)) {
goto next_command; goto next_command;
} }

View File

@ -157,7 +157,7 @@ static uint32_t get_pixel(GB_gameboy_t *gb, uint8_t x, uint8_t y)
sprite_pixel = (gb->io_registers[use_obp1? GB_IO_OBP1:GB_IO_OBP0] >> (sprite_pixel << 1)) & 3; sprite_pixel = (gb->io_registers[use_obp1? GB_IO_OBP1:GB_IO_OBP0] >> (sprite_pixel << 1)) & 3;
sprite_palette = use_obp1; sprite_palette = use_obp1;
} }
return gb->sprite_palletes_rgb[sprite_palette * 4 + sprite_pixel]; return gb->sprite_palettes_rgb[sprite_palette * 4 + sprite_pixel];
} }
if (bg_enabled) { if (bg_enabled) {
@ -188,14 +188,14 @@ static uint32_t get_pixel(GB_gameboy_t *gb, uint8_t x, uint8_t y)
sprite_pixel = (gb->io_registers[use_obp1? GB_IO_OBP1:GB_IO_OBP0] >> (sprite_pixel << 1)) & 3; sprite_pixel = (gb->io_registers[use_obp1? GB_IO_OBP1:GB_IO_OBP0] >> (sprite_pixel << 1)) & 3;
sprite_palette = use_obp1; sprite_palette = use_obp1;
} }
return gb->sprite_palletes_rgb[sprite_palette * 4 + sprite_pixel]; return gb->sprite_palettes_rgb[sprite_palette * 4 + sprite_pixel];
} }
if (!gb->cgb_mode) { if (!gb->cgb_mode) {
background_pixel = ((gb->io_registers[GB_IO_BGP] >> (background_pixel << 1)) & 3); background_pixel = ((gb->io_registers[GB_IO_BGP] >> (background_pixel << 1)) & 3);
} }
return gb->background_palletes_rgb[(attributes & 7) * 4 + background_pixel]; return gb->background_palettes_rgb[(attributes & 7) * 4 + background_pixel];
} }
static int64_t get_nanoseconds(void) static int64_t get_nanoseconds(void)
@ -269,7 +269,7 @@ static inline uint8_t scale_channel(uint8_t x)
void GB_palette_changed(GB_gameboy_t *gb, bool background_palette, uint8_t index) void GB_palette_changed(GB_gameboy_t *gb, bool background_palette, uint8_t index)
{ {
uint8_t *palette_data = background_palette? gb->background_palletes_data : gb->sprite_palletes_data; uint8_t *palette_data = background_palette? gb->background_palettes_data : gb->sprite_palettes_data;
uint16_t color = palette_data[index & ~1] | (palette_data[index | 1] << 8); uint16_t color = palette_data[index & ~1] | (palette_data[index | 1] << 8);
// No need to &, scale channel does that. // No need to &, scale channel does that.
@ -277,7 +277,7 @@ void GB_palette_changed(GB_gameboy_t *gb, bool background_palette, uint8_t index
uint8_t g = scale_channel(color >> 5); uint8_t g = scale_channel(color >> 5);
uint8_t b = scale_channel(color >> 10); uint8_t b = scale_channel(color >> 10);
assert (gb->rgb_encode_callback); assert (gb->rgb_encode_callback);
(background_palette? gb->background_palletes_rgb : gb->sprite_palletes_rgb)[index / 2] = gb->rgb_encode_callback(gb, r, g, b); (background_palette? gb->background_palettes_rgb : gb->sprite_palettes_rgb)[index / 2] = gb->rgb_encode_callback(gb, r, g, b);
} }
/* /*
@ -584,17 +584,17 @@ void GB_draw_tileset(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette
palette = none_palette; palette = none_palette;
break; break;
case GB_PALETTE_BACKGROUND: case GB_PALETTE_BACKGROUND:
palette = gb->background_palletes_rgb + (4 * (palette_index & 7)); palette = gb->background_palettes_rgb + (4 * (palette_index & 7));
break; break;
case GB_PALETTE_OAM: case GB_PALETTE_OAM:
palette = gb->sprite_palletes_rgb + (4 * (palette_index & 7)); palette = gb->sprite_palettes_rgb + (4 * (palette_index & 7));
break; break;
} }
for (unsigned y = 0; y < 192; y++) { for (unsigned y = 0; y < 192; y++) {
for (unsigned x = 0; x < 256; x++) { for (unsigned x = 0; x < 256; x++) {
if (x >= 128 && !gb->is_cgb) { if (x >= 128 && !gb->is_cgb) {
*(dest++) = gb->background_palletes_rgb[0]; *(dest++) = gb->background_palettes_rgb[0];
continue; continue;
} }
uint16_t tile = (x % 128) / 8 + y / 8 * 16; uint16_t tile = (x % 128) / 8 + y / 8 * 16;
@ -634,10 +634,10 @@ void GB_draw_tilemap(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette
palette = none_palette; palette = none_palette;
break; break;
case GB_PALETTE_BACKGROUND: case GB_PALETTE_BACKGROUND:
palette = gb->background_palletes_rgb + (4 * (palette_index & 7)); palette = gb->background_palettes_rgb + (4 * (palette_index & 7));
break; break;
case GB_PALETTE_OAM: case GB_PALETTE_OAM:
palette = gb->sprite_palletes_rgb + (4 * (palette_index & 7)); palette = gb->sprite_palettes_rgb + (4 * (palette_index & 7));
break; break;
case GB_PALETTE_AUTO: case GB_PALETTE_AUTO:
break; break;
@ -683,16 +683,16 @@ void GB_draw_tilemap(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette
*(dest++) = palette[pixel]; *(dest++) = palette[pixel];
} }
else { else {
*(dest++) = gb->background_palletes_rgb[(attributes & 7) * 4 + pixel]; *(dest++) = gb->background_palettes_rgb[(attributes & 7) * 4 + pixel];
} }
} }
} }
} }
uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest) uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest, uint8_t *sprite_height)
{ {
uint8_t count = 0; uint8_t count = 0;
unsigned sprite_height = (gb->io_registers[GB_IO_LCDC] & 4) ? 16:8; *sprite_height = (gb->io_registers[GB_IO_LCDC] & 4) ? 16:8;
uint8_t oam_to_dest_index[40] = {0,}; uint8_t oam_to_dest_index[40] = {0,};
for (unsigned y = 0; y < LINES; y++) { for (unsigned y = 0; y < LINES; y++) {
GB_sprite_t *sprite = (GB_sprite_t *) &gb->oam; GB_sprite_t *sprite = (GB_sprite_t *) &gb->oam;
@ -701,7 +701,7 @@ uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest)
int sprite_y = sprite->y - 16; int sprite_y = sprite->y - 16;
bool obscured = false; bool obscured = false;
// Is sprite not in this line? // Is sprite not in this line?
if (sprite_y > y || sprite_y + sprite_height <= y) continue; if (sprite_y > y || sprite_y + *sprite_height <= y) continue;
if (++sprites_in_line == 11) obscured = true; if (++sprites_in_line == 11) obscured = true;
GB_oam_info_t *info = NULL; GB_oam_info_t *info = NULL;
@ -710,7 +710,7 @@ uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest)
oam_to_dest_index[i] = ++count; oam_to_dest_index[i] = ++count;
info->x = sprite->x; info->x = sprite->x;
info->y = sprite->y; info->y = sprite->y;
info->tile = sprite_height == 16? sprite->tile & 0xFE : sprite->tile; info->tile = *sprite_height == 16? sprite->tile & 0xFE : sprite->tile;
info->flags = sprite->flags; info->flags = sprite->flags;
info->obscured_by_line_limit = false; info->obscured_by_line_limit = false;
info->oam_addr = 0xFE00 + i * sizeof(*sprite); info->oam_addr = 0xFE00 + i * sizeof(*sprite);
@ -727,8 +727,11 @@ uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest)
uint16_t vram_address = dest[i].tile * 0x10; uint16_t vram_address = dest[i].tile * 0x10;
uint8_t flags = dest[i].flags; uint8_t flags = dest[i].flags;
uint8_t palette = gb->cgb_mode? (flags & 7) : ((flags & 0x10)? 1 : 0); uint8_t palette = gb->cgb_mode? (flags & 7) : ((flags & 0x10)? 1 : 0);
if (gb->is_cgb && (flags & 0x8)) {
vram_address += 0x2000;
}
for (unsigned y = 0; y < sprite_height; y++) { for (unsigned y = 0; y < *sprite_height; y++) {
for (unsigned x = 0; x < 8; x++) { for (unsigned x = 0; x < 8; x++) {
uint8_t color = (((gb->vram[vram_address ] >> ((~x)&7)) & 1 ) | uint8_t color = (((gb->vram[vram_address ] >> ((~x)&7)) & 1 ) |
((gb->vram[vram_address + 1] >> ((~x)&7)) & 1) << 1 ); ((gb->vram[vram_address + 1] >> ((~x)&7)) & 1) << 1 );
@ -736,7 +739,7 @@ uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest)
if (!gb->cgb_mode) { if (!gb->cgb_mode) {
color = (gb->io_registers[palette? GB_IO_OBP1:GB_IO_OBP0] >> (color << 1)) & 3; color = (gb->io_registers[palette? GB_IO_OBP1:GB_IO_OBP0] >> (color << 1)) & 3;
} }
dest[i].image[((flags & 0x20)?7-x:x) + ((flags & 0x40)?sprite_height - 1 -y:y) * 8] = gb->sprite_palletes_rgb[palette * 4 + color]; dest[i].image[((flags & 0x20)?7-x:x) + ((flags & 0x40)?*sprite_height - 1 -y:y) * 8] = gb->sprite_palettes_rgb[palette * 4 + color];
} }
vram_address += 2; vram_address += 2;
} }

View File

@ -35,6 +35,6 @@ typedef struct {
void GB_draw_tileset(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index); void GB_draw_tileset(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index);
void GB_draw_tilemap(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index, GB_map_type_t map_type, GB_tileset_type_t tileset_type); void GB_draw_tilemap(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index, GB_map_type_t map_type, GB_tileset_type_t tileset_type);
uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest); uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest, uint8_t *sprite_height);
#endif /* display_h */ #endif /* display_h */

View File

@ -447,13 +447,13 @@ void GB_set_async_input_callback(GB_gameboy_t *gb, GB_input_callback_t callback)
void GB_set_rgb_encode_callback(GB_gameboy_t *gb, GB_rgb_encode_callback_t callback) void GB_set_rgb_encode_callback(GB_gameboy_t *gb, GB_rgb_encode_callback_t callback)
{ {
if (!gb->rgb_encode_callback && !gb->is_cgb) { if (!gb->rgb_encode_callback && !gb->is_cgb) {
gb->sprite_palletes_rgb[4] = gb->sprite_palletes_rgb[0] = gb->background_palletes_rgb[0] = gb->sprite_palettes_rgb[4] = gb->sprite_palettes_rgb[0] = gb->background_palettes_rgb[0] =
callback(gb, 0xFF, 0xFF, 0xFF); callback(gb, 0xFF, 0xFF, 0xFF);
gb->sprite_palletes_rgb[5] = gb->sprite_palletes_rgb[1] = gb->background_palletes_rgb[1] = gb->sprite_palettes_rgb[5] = gb->sprite_palettes_rgb[1] = gb->background_palettes_rgb[1] =
callback(gb, 0xAA, 0xAA, 0xAA); callback(gb, 0xAA, 0xAA, 0xAA);
gb->sprite_palletes_rgb[6] = gb->sprite_palletes_rgb[2] = gb->background_palletes_rgb[2] = gb->sprite_palettes_rgb[6] = gb->sprite_palettes_rgb[2] = gb->background_palettes_rgb[2] =
callback(gb, 0x55, 0x55, 0x55); callback(gb, 0x55, 0x55, 0x55);
gb->sprite_palletes_rgb[7] = gb->sprite_palletes_rgb[3] = gb->background_palletes_rgb[3] = gb->sprite_palettes_rgb[7] = gb->sprite_palettes_rgb[3] = gb->background_palettes_rgb[3] =
callback(gb, 0, 0, 0); callback(gb, 0, 0, 0);
} }
gb->rgb_encode_callback = callback; gb->rgb_encode_callback = callback;
@ -540,6 +540,11 @@ bool GB_is_inited(GB_gameboy_t *gb)
return gb->magic == 'SAME'; return gb->magic == 'SAME';
} }
bool GB_is_cgb(GB_gameboy_t *gb)
{
return gb->is_cgb;
}
void GB_set_turbo_mode(GB_gameboy_t *gb, bool on, bool no_frame_skip) void GB_set_turbo_mode(GB_gameboy_t *gb, bool on, bool no_frame_skip)
{ {
gb->turbo = on; gb->turbo = on;
@ -592,13 +597,13 @@ void GB_reset(GB_gameboy_t *gb)
memset(gb->vram, 0, gb->vram_size); memset(gb->vram, 0, gb->vram_size);
if (gb->rgb_encode_callback) { if (gb->rgb_encode_callback) {
gb->sprite_palletes_rgb[4] = gb->sprite_palletes_rgb[0] = gb->background_palletes_rgb[0] = gb->sprite_palettes_rgb[4] = gb->sprite_palettes_rgb[0] = gb->background_palettes_rgb[0] =
gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF); gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF);
gb->sprite_palletes_rgb[5] = gb->sprite_palletes_rgb[1] = gb->background_palletes_rgb[1] = gb->sprite_palettes_rgb[5] = gb->sprite_palettes_rgb[1] = gb->background_palettes_rgb[1] =
gb->rgb_encode_callback(gb, 0xAA, 0xAA, 0xAA); gb->rgb_encode_callback(gb, 0xAA, 0xAA, 0xAA);
gb->sprite_palletes_rgb[6] = gb->sprite_palletes_rgb[2] = gb->background_palletes_rgb[2] = gb->sprite_palettes_rgb[6] = gb->sprite_palettes_rgb[2] = gb->background_palettes_rgb[2] =
gb->rgb_encode_callback(gb, 0x55, 0x55, 0x55); gb->rgb_encode_callback(gb, 0x55, 0x55, 0x55);
gb->sprite_palletes_rgb[7] = gb->sprite_palletes_rgb[3] = gb->background_palletes_rgb[3] = gb->sprite_palettes_rgb[7] = gb->sprite_palettes_rgb[3] = gb->background_palettes_rgb[3] =
gb->rgb_encode_callback(gb, 0, 0, 0); gb->rgb_encode_callback(gb, 0, 0, 0);
} }
@ -621,3 +626,63 @@ void GB_switch_model_and_reset(GB_gameboy_t *gb, bool is_cgb)
GB_reset(gb); GB_reset(gb);
} }
void *GB_get_direct_access(GB_gameboy_t *gb, GB_direct_access_t access, size_t *size, uint16_t *bank)
{
/* Set size and bank to dummy pointers if not set */
if (!size) {
size = alloca(sizeof(size));
}
if (!bank) {
bank = alloca(sizeof(bank));
}
switch (access) {
case GB_DIRECT_ACCESS_ROM:
*size = gb->rom_size;
*bank = gb->mbc_rom_bank;
return gb->rom;
case GB_DIRECT_ACCESS_RAM:
*size = gb->ram_size;
*bank = gb->cgb_ram_bank;
return gb->ram;
case GB_DIRECT_ACCESS_CART_RAM:
*size = gb->mbc_ram_size;
*bank = gb->mbc_ram_bank;
return gb->mbc_ram;
case GB_DIRECT_ACCESS_VRAM:
*size = gb->vram_size;
*bank = gb->cgb_vram_bank;
return gb->vram;
case GB_DIRECT_ACCESS_HRAM:
*size = sizeof(gb->hram);
*bank = 0;
return &gb->hram;
case GB_DIRECT_ACCESS_IO:
*size = sizeof(gb->io_registers);
*bank = 0;
return &gb->io_registers;
case GB_DIRECT_ACCESS_BOOTROM:
*size = gb->is_cgb? sizeof(gb->boot_rom) : 0x100;
*bank = 0;
return &gb->boot_rom;
case GB_DIRECT_ACCESS_OAM:
*size = sizeof(gb->oam);
*bank = 0;
return &gb->oam;
case GB_DIRECT_ACCESS_BGP:
*size = sizeof(gb->background_palettes_data);
*bank = 0;
return &gb->background_palettes_data;
case GB_DIRECT_ACCESS_OBP:
*size = sizeof(gb->sprite_palettes_data);
*bank = 0;
return &gb->sprite_palettes_data;
default:
*size = 0;
*bank = 0;
return NULL;
}
}

View File

@ -361,10 +361,10 @@ struct GB_gameboy_internal_s {
uint32_t vram_size; // Different between CGB and DMG uint32_t vram_size; // Different between CGB and DMG
uint8_t cgb_vram_bank; uint8_t cgb_vram_bank;
uint8_t oam[0xA0]; uint8_t oam[0xA0];
uint8_t background_palletes_data[0x40]; uint8_t background_palettes_data[0x40];
uint8_t sprite_palletes_data[0x40]; uint8_t sprite_palettes_data[0x40];
uint32_t background_palletes_rgb[0x20]; uint32_t background_palettes_rgb[0x20];
uint32_t sprite_palletes_rgb[0x20]; uint32_t sprite_palettes_rgb[0x20];
int16_t previous_lcdc_x; int16_t previous_lcdc_x;
uint8_t padding; uint8_t padding;
bool effective_window_enabled; bool effective_window_enabled;
@ -481,11 +481,29 @@ __attribute__((__format__ (__printf__, fmtarg, firstvararg)))
void GB_init(GB_gameboy_t *gb); void GB_init(GB_gameboy_t *gb);
void GB_init_cgb(GB_gameboy_t *gb); void GB_init_cgb(GB_gameboy_t *gb);
bool GB_is_inited(GB_gameboy_t *gb); bool GB_is_inited(GB_gameboy_t *gb);
bool GB_is_cgb(GB_gameboy_t *gb);
void GB_free(GB_gameboy_t *gb); void GB_free(GB_gameboy_t *gb);
void GB_reset(GB_gameboy_t *gb); void GB_reset(GB_gameboy_t *gb);
void GB_switch_model_and_reset(GB_gameboy_t *gb, bool is_cgb); void GB_switch_model_and_reset(GB_gameboy_t *gb, bool is_cgb);
void GB_run(GB_gameboy_t *gb); void GB_run(GB_gameboy_t *gb);
typedef enum {
GB_DIRECT_ACCESS_ROM,
GB_DIRECT_ACCESS_RAM,
GB_DIRECT_ACCESS_CART_RAM,
GB_DIRECT_ACCESS_VRAM,
GB_DIRECT_ACCESS_HRAM,
GB_DIRECT_ACCESS_IO, /* Warning: Some registers can only be read/written correctly via GB_memory_read/write. */
GB_DIRECT_ACCESS_BOOTROM,
GB_DIRECT_ACCESS_OAM,
GB_DIRECT_ACCESS_BGP,
GB_DIRECT_ACCESS_OBP,
} GB_direct_access_t;
/* Returns a mutable pointer to various hardware memories. If that memory is banked, the current bank
is returned at *bank, even if only a portion of the memory is banked. */
void *GB_get_direct_access(GB_gameboy_t *gb, GB_direct_access_t access, size_t *size, uint16_t *bank);
void *GB_get_user_data(GB_gameboy_t *gb); void *GB_get_user_data(GB_gameboy_t *gb);
void GB_set_user_data(GB_gameboy_t *gb, void *data); void GB_set_user_data(GB_gameboy_t *gb, void *data);

View File

@ -206,8 +206,8 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
} }
uint8_t index_reg = (addr & 0xFF) - 1; uint8_t index_reg = (addr & 0xFF) - 1;
return ((addr & 0xFF) == GB_IO_BGPD? return ((addr & 0xFF) == GB_IO_BGPD?
gb->background_palletes_data : gb->background_palettes_data :
gb->sprite_palletes_data)[gb->io_registers[index_reg] & 0x3F]; gb->sprite_palettes_data)[gb->io_registers[index_reg] & 0x3F];
} }
case GB_IO_KEY1: case GB_IO_KEY1:
@ -526,8 +526,8 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
} }
uint8_t index_reg = (addr & 0xFF) - 1; uint8_t index_reg = (addr & 0xFF) - 1;
((addr & 0xFF) == GB_IO_BGPD? ((addr & 0xFF) == GB_IO_BGPD?
gb->background_palletes_data : gb->background_palettes_data :
gb->sprite_palletes_data)[gb->io_registers[index_reg] & 0x3F] = value; gb->sprite_palettes_data)[gb->io_registers[index_reg] & 0x3F] = value;
GB_palette_changed(gb, (addr & 0xFF) == GB_IO_BGPD, gb->io_registers[index_reg] & 0x3F); GB_palette_changed(gb, (addr & 0xFF) == GB_IO_BGPD, gb->io_registers[index_reg] & 0x3F);
if (gb->io_registers[index_reg] & 0x80) { if (gb->io_registers[index_reg] & 0x80) {
gb->io_registers[index_reg]++; gb->io_registers[index_reg]++;