mirror of
https://github.com/usatiuk/cardboy.git
synced 2025-10-28 23:27:49 +01:00
some opt
This commit is contained in:
@@ -568,6 +568,7 @@ union cart_rtc
|
|||||||
} reg;
|
} reg;
|
||||||
uint8_t bytes[5];
|
uint8_t bytes[5];
|
||||||
};
|
};
|
||||||
|
extern "C" uint8_t gb_rom_read(struct gb_s*, const uint_fast32_t);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emulator context.
|
* Emulator context.
|
||||||
@@ -577,15 +578,6 @@ union cart_rtc
|
|||||||
*/
|
*/
|
||||||
struct gb_s
|
struct gb_s
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Return byte from ROM at given address.
|
|
||||||
*
|
|
||||||
* \param gb_s emulator context
|
|
||||||
* \param addr address
|
|
||||||
* \return byte at address in ROM
|
|
||||||
*/
|
|
||||||
uint8_t (*gb_rom_read)(struct gb_s*, const uint_fast32_t addr);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return byte from cart RAM at given address.
|
* Return byte from cart RAM at given address.
|
||||||
*
|
*
|
||||||
@@ -796,17 +788,17 @@ uint8_t __gb_read(struct gb_s *gb, uint16_t addr)
|
|||||||
case 0x1:
|
case 0x1:
|
||||||
case 0x2:
|
case 0x2:
|
||||||
case 0x3:
|
case 0x3:
|
||||||
return gb->gb_rom_read(gb, addr);
|
return gb_rom_read(gb, addr);
|
||||||
|
|
||||||
case 0x4:
|
case 0x4:
|
||||||
case 0x5:
|
case 0x5:
|
||||||
case 0x6:
|
case 0x6:
|
||||||
case 0x7:
|
case 0x7:
|
||||||
if(gb->mbc == 1 && gb->cart_mode_select)
|
if(gb->mbc == 1 && gb->cart_mode_select)
|
||||||
return gb->gb_rom_read(gb,
|
return gb_rom_read(gb,
|
||||||
addr + ((gb->selected_rom_bank & 0x1F) - 1) * ROM_BANK_SIZE);
|
addr + ((gb->selected_rom_bank & 0x1F) - 1) * ROM_BANK_SIZE);
|
||||||
else
|
else
|
||||||
return gb->gb_rom_read(gb, addr + (gb->selected_rom_bank - 1) * ROM_BANK_SIZE);
|
return gb_rom_read(gb, addr + (gb->selected_rom_bank - 1) * ROM_BANK_SIZE);
|
||||||
|
|
||||||
case 0x8:
|
case 0x8:
|
||||||
case 0x9:
|
case 0x9:
|
||||||
@@ -3542,7 +3534,7 @@ int gb_get_save_size_s(struct gb_s *gb, size_t *ram_size)
|
|||||||
/* 0, 2KiB, 8KiB, 32KiB, 128KiB, 64KiB */
|
/* 0, 2KiB, 8KiB, 32KiB, 128KiB, 64KiB */
|
||||||
0x00, 0x800, 0x2000, 0x8000, 0x20000, 0x10000
|
0x00, 0x800, 0x2000, 0x8000, 0x20000, 0x10000
|
||||||
};
|
};
|
||||||
uint8_t ram_size_code = gb->gb_rom_read(gb, ram_size_location);
|
uint8_t ram_size_code = gb_rom_read(gb, ram_size_location);
|
||||||
|
|
||||||
/* MBC2 always has 512 half-bytes of cart RAM.
|
/* MBC2 always has 512 half-bytes of cart RAM.
|
||||||
* This assumes that only the lower nibble of each byte is used; the
|
* This assumes that only the lower nibble of each byte is used; the
|
||||||
@@ -3570,7 +3562,7 @@ uint_fast32_t gb_get_save_size(struct gb_s *gb)
|
|||||||
/* 0, 2KiB, 8KiB, 32KiB, 128KiB, 64KiB */
|
/* 0, 2KiB, 8KiB, 32KiB, 128KiB, 64KiB */
|
||||||
0x00, 0x800, 0x2000, 0x8000, 0x20000, 0x10000
|
0x00, 0x800, 0x2000, 0x8000, 0x20000, 0x10000
|
||||||
};
|
};
|
||||||
uint8_t ram_size_code = gb->gb_rom_read(gb, ram_size_location);
|
uint8_t ram_size_code = gb_rom_read(gb, ram_size_location);
|
||||||
|
|
||||||
/* MBC2 always has 512 half-bytes of cart RAM.
|
/* MBC2 always has 512 half-bytes of cart RAM.
|
||||||
* This assumes that only the lower nibble of each byte is used; the
|
* This assumes that only the lower nibble of each byte is used; the
|
||||||
@@ -3603,7 +3595,7 @@ uint8_t gb_colour_hash(struct gb_s *gb)
|
|||||||
uint16_t i;
|
uint16_t i;
|
||||||
|
|
||||||
for(i = ROM_TITLE_START_ADDR; i <= ROM_TITLE_END_ADDR; i++)
|
for(i = ROM_TITLE_START_ADDR; i <= ROM_TITLE_END_ADDR; i++)
|
||||||
x += gb->gb_rom_read(gb, i);
|
x += gb_rom_read(gb, i);
|
||||||
|
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
@@ -3626,7 +3618,7 @@ void gb_reset(struct gb_s *gb)
|
|||||||
if(gb->gb_bootrom_read == NULL)
|
if(gb->gb_bootrom_read == NULL)
|
||||||
{
|
{
|
||||||
uint8_t hdr_chk;
|
uint8_t hdr_chk;
|
||||||
hdr_chk = gb->gb_rom_read(gb, ROM_HEADER_CHECKSUM_LOC) != 0;
|
hdr_chk = gb_rom_read(gb, ROM_HEADER_CHECKSUM_LOC) != 0;
|
||||||
|
|
||||||
gb->cpu_reg.a = 0x01;
|
gb->cpu_reg.a = 0x01;
|
||||||
gb->cpu_reg.f.f_bits.z = 1;
|
gb->cpu_reg.f.f_bits.z = 1;
|
||||||
@@ -3692,7 +3684,6 @@ void gb_reset(struct gb_s *gb)
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum gb_init_error_e gb_init(struct gb_s *gb,
|
enum gb_init_error_e gb_init(struct gb_s *gb,
|
||||||
uint8_t (*gb_rom_read)(struct gb_s*, const uint_fast32_t),
|
|
||||||
uint8_t (*gb_cart_ram_read)(struct gb_s*, const uint_fast32_t),
|
uint8_t (*gb_cart_ram_read)(struct gb_s*, const uint_fast32_t),
|
||||||
void (*gb_cart_ram_write)(struct gb_s*, const uint_fast32_t, const uint8_t),
|
void (*gb_cart_ram_write)(struct gb_s*, const uint_fast32_t, const uint8_t),
|
||||||
void (*gb_error)(struct gb_s*, const enum gb_error_e, const uint16_t),
|
void (*gb_error)(struct gb_s*, const enum gb_error_e, const uint16_t),
|
||||||
@@ -3731,7 +3722,6 @@ enum gb_init_error_e gb_init(struct gb_s *gb,
|
|||||||
* some early homebrew ROMs supposedly may use this value. */
|
* some early homebrew ROMs supposedly may use this value. */
|
||||||
const uint8_t num_ram_banks[] = { 0, 1, 1, 4, 16, 8 };
|
const uint8_t num_ram_banks[] = { 0, 1, 1, 4, 16, 8 };
|
||||||
|
|
||||||
gb->gb_rom_read = gb_rom_read;
|
|
||||||
gb->gb_cart_ram_read = gb_cart_ram_read;
|
gb->gb_cart_ram_read = gb_cart_ram_read;
|
||||||
gb->gb_cart_ram_write = gb_cart_ram_write;
|
gb->gb_cart_ram_write = gb_cart_ram_write;
|
||||||
gb->gb_error = gb_error;
|
gb->gb_error = gb_error;
|
||||||
@@ -3751,24 +3741,24 @@ enum gb_init_error_e gb_init(struct gb_s *gb,
|
|||||||
uint16_t i;
|
uint16_t i;
|
||||||
|
|
||||||
for(i = 0x0134; i <= 0x014C; i++)
|
for(i = 0x0134; i <= 0x014C; i++)
|
||||||
x = x - gb->gb_rom_read(gb, i) - 1;
|
x = x - gb_rom_read(gb, i) - 1;
|
||||||
|
|
||||||
if(x != gb->gb_rom_read(gb, ROM_HEADER_CHECKSUM_LOC))
|
if(x != gb_rom_read(gb, ROM_HEADER_CHECKSUM_LOC))
|
||||||
return GB_INIT_INVALID_CHECKSUM;
|
return GB_INIT_INVALID_CHECKSUM;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if cartridge type is supported, and set MBC type. */
|
/* Check if cartridge type is supported, and set MBC type. */
|
||||||
{
|
{
|
||||||
const uint8_t mbc_value = gb->gb_rom_read(gb, mbc_location);
|
const uint8_t mbc_value = gb_rom_read(gb, mbc_location);
|
||||||
|
|
||||||
if(mbc_value > sizeof(cart_mbc) - 1 ||
|
if(mbc_value > sizeof(cart_mbc) - 1 ||
|
||||||
(gb->mbc = cart_mbc[mbc_value]) == -1)
|
(gb->mbc = cart_mbc[mbc_value]) == -1)
|
||||||
return GB_INIT_CARTRIDGE_UNSUPPORTED;
|
return GB_INIT_CARTRIDGE_UNSUPPORTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
gb->num_rom_banks_mask = num_rom_banks_mask[gb->gb_rom_read(gb, bank_count_location)] - 1;
|
gb->num_rom_banks_mask = num_rom_banks_mask[gb_rom_read(gb, bank_count_location)] - 1;
|
||||||
gb->cart_ram = cart_ram[gb->gb_rom_read(gb, mbc_location)];
|
gb->cart_ram = cart_ram[gb_rom_read(gb, mbc_location)];
|
||||||
gb->num_ram_banks = num_ram_banks[gb->gb_rom_read(gb, ram_size_location)];
|
gb->num_ram_banks = num_ram_banks[gb_rom_read(gb, ram_size_location)];
|
||||||
|
|
||||||
/* If the ROM says that it support RAM, but has 0 RAM banks, then
|
/* If the ROM says that it support RAM, but has 0 RAM banks, then
|
||||||
* disable RAM reads from the cartridge. */
|
* disable RAM reads from the cartridge. */
|
||||||
@@ -3804,7 +3794,7 @@ const char* gb_get_rom_name(struct gb_s* gb, char *title_str)
|
|||||||
|
|
||||||
for(; title_loc <= title_end; title_loc++)
|
for(; title_loc <= title_end; title_loc++)
|
||||||
{
|
{
|
||||||
const char title_char = gb->gb_rom_read(gb, title_loc);
|
const char title_char = gb_rom_read(gb, title_loc);
|
||||||
|
|
||||||
if(title_char >= ' ' && title_char <= '_')
|
if(title_char >= ' ' && title_char <= '_')
|
||||||
{
|
{
|
||||||
@@ -3886,7 +3876,6 @@ void gb_set_rtc(struct gb_s *gb, const struct tm * const time)
|
|||||||
* \returns 0 on success or an enum that describes the error.
|
* \returns 0 on success or an enum that describes the error.
|
||||||
*/
|
*/
|
||||||
enum gb_init_error_e gb_init(struct gb_s *gb,
|
enum gb_init_error_e gb_init(struct gb_s *gb,
|
||||||
uint8_t (*gb_rom_read)(struct gb_s*, const uint_fast32_t),
|
|
||||||
uint8_t (*gb_cart_ram_read)(struct gb_s*, const uint_fast32_t),
|
uint8_t (*gb_cart_ram_read)(struct gb_s*, const uint_fast32_t),
|
||||||
void (*gb_cart_ram_write)(struct gb_s*, const uint_fast32_t, const uint8_t),
|
void (*gb_cart_ram_write)(struct gb_s*, const uint_fast32_t, const uint8_t),
|
||||||
void (*gb_error)(struct gb_s*, const enum gb_error_e, const uint16_t),
|
void (*gb_error)(struct gb_s*, const enum gb_error_e, const uint16_t),
|
||||||
|
|||||||
@@ -36,6 +36,47 @@
|
|||||||
#define GB_PERF_ONLY(...)
|
#define GB_PERF_ONLY(...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static constexpr uint8_t kPattern2x2[4] = {0, 2, 3, 1}; // [ (y&1)<<1 | (x&1) ]
|
||||||
|
|
||||||
|
// exact predicate per your shouldPixelBeOn
|
||||||
|
static constexpr uint8_t on_bit(uint8_t v, uint8_t threshold) {
|
||||||
|
if (v >= 3)
|
||||||
|
return 1;
|
||||||
|
if (v == 0)
|
||||||
|
return 0;
|
||||||
|
return static_cast<uint8_t>(v > threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build 4 output bits (MSB = first pixel) from 4 packed 2-bit pixels.
|
||||||
|
// packed4 layout: p0 | p1<<2 | p2<<4 | p3<<6
|
||||||
|
static constexpr uint8_t makeNibble(uint8_t packed4, int xParity, int yParity) {
|
||||||
|
uint8_t out = 0;
|
||||||
|
int xp = xParity;
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
const uint8_t v = (packed4 >> (i * 2)) & 0x3;
|
||||||
|
const uint8_t th = kPattern2x2[(yParity << 1) | (xp & 1)];
|
||||||
|
out = static_cast<uint8_t>((out << 1) | on_bit(v, th));
|
||||||
|
xp ^= 1; // toggle x parity each pixel
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
using LUT256 = std::array<uint8_t, 256>;
|
||||||
|
using LUT2x256 = std::array<LUT256, 2>;
|
||||||
|
using LUTFull = std::array<LUT2x256, 2>;
|
||||||
|
|
||||||
|
static constexpr LUTFull buildNibbleLUT() {
|
||||||
|
LUTFull L{};
|
||||||
|
for (int yp = 0; yp < 2; ++yp)
|
||||||
|
for (int xp = 0; xp < 2; ++xp)
|
||||||
|
for (int p = 0; p < 256; ++p)
|
||||||
|
L[yp][xp][p] = makeNibble(static_cast<uint8_t>(p), xp, yp);
|
||||||
|
return L;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr LUTFull kNibbleLUT = buildNibbleLUT();
|
||||||
|
|
||||||
|
|
||||||
namespace apps {
|
namespace apps {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@@ -813,8 +854,8 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::memset(&gb, 0, sizeof(gb));
|
std::memset(&gb, 0, sizeof(gb));
|
||||||
const auto initResult = gb_init(&gb, &GameboyApp::romRead, &GameboyApp::cartRamRead, &GameboyApp::cartRamWrite,
|
const auto initResult =
|
||||||
&GameboyApp::errorCallback, this);
|
gb_init(&gb, &GameboyApp::cartRamRead, &GameboyApp::cartRamWrite, &GameboyApp::errorCallback, this);
|
||||||
if (initResult != GB_INIT_NO_ERROR) {
|
if (initResult != GB_INIT_NO_ERROR) {
|
||||||
setStatus(initErrorToString(initResult));
|
setStatus(initErrorToString(initResult));
|
||||||
romData.clear();
|
romData.clear();
|
||||||
@@ -1125,53 +1166,83 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void drawLineOriginal(GameboyApp& self, const uint8_t pixels[160], int srcLine) {
|
static void drawLineOriginal(GameboyApp& self, const uint8_t pixels[160], int srcLine) {
|
||||||
Framebuffer& fb = self.framebuffer;
|
Framebuffer& fb = self.framebuffer;
|
||||||
const int dstY = kOriginalOffsetY + srcLine;
|
const int dstY = kOriginalOffsetY + srcLine;
|
||||||
const int baseX = kOriginalOffsetX;
|
const int baseX = kOriginalOffsetX;
|
||||||
|
const int yParity = dstY & 1;
|
||||||
|
|
||||||
CARDBOY_CHECK((baseX % 8) == 0);
|
CARDBOY_CHECK((baseX % 8) == 0);
|
||||||
|
CARDBOY_CHECK((LCD_WIDTH % 8) == 0); // 160 is fine
|
||||||
|
|
||||||
int x = 0;
|
int x = 0;
|
||||||
while (x < LCD_WIDTH) {
|
while (x < LCD_WIDTH) {
|
||||||
uint8_t pack = 0;
|
// Build two 4-pixel packs (2 bits per pixel → 8-bit pack)
|
||||||
for (int i = 0; i < 8; ++i, ++x) {
|
uint8_t p4a = pixels[x + 0] | (pixels[x + 1] << 2) | (pixels[x + 2] << 4) | (pixels[x + 3] << 6);
|
||||||
const bool on = shouldPixelBeOn(pixels[x], baseX + x, dstY);
|
|
||||||
pack = static_cast<uint8_t>((pack << 1) | (on ? 1 : 0));
|
uint8_t p4b = pixels[x + 4] | (pixels[x + 5] << 2) | (pixels[x + 6] << 4) | (pixels[x + 7] << 6);
|
||||||
}
|
|
||||||
fb.drawBits8(baseX + x - 8, dstY, pack);
|
const int xParity = (baseX + x) & 1; // start parity for this byte
|
||||||
|
const uint8_t n0 = kNibbleLUT[yParity][xParity][p4a];
|
||||||
|
const uint8_t n1 = kNibbleLUT[yParity][xParity][p4b]; // same parity after 4 pixels
|
||||||
|
|
||||||
|
const uint8_t pack = static_cast<uint8_t>((n0 << 4) | (n1 & 0x0F));
|
||||||
|
fb.drawBits8(baseX + x, dstY, pack);
|
||||||
|
|
||||||
|
x += 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void drawLineFullHeight(GameboyApp& self, const uint8_t pixels[160], int srcLine) {
|
static void drawLineFullHeight(GameboyApp& self, const uint8_t pixels[160], int srcLine) {
|
||||||
Framebuffer& fb = self.framebuffer;
|
Framebuffer& fb = self.framebuffer;
|
||||||
int yStart = kFullHeightRowBounds[static_cast<std::size_t>(srcLine)];
|
const int yStart = kFullHeightRowBounds[static_cast<std::size_t>(srcLine)];
|
||||||
int yEnd = kFullHeightRowBounds[static_cast<std::size_t>(srcLine) + 1];
|
const int yEnd = kFullHeightRowBounds[static_cast<std::size_t>(srcLine) + 1];
|
||||||
|
|
||||||
CARDBOY_CHECK(yEnd > yStart);
|
CARDBOY_CHECK(yEnd > yStart);
|
||||||
CARDBOY_CHECK((kFullHeightColumnBounds[0] % 8) == 0);
|
CARDBOY_CHECK((kFullHeightColumnBounds[0] % 8) == 0);
|
||||||
|
|
||||||
for (int dstY = yStart; dstY < yEnd; ++dstY) {
|
for (int dstY = yStart; dstY < yEnd; ++dstY) {
|
||||||
uint8_t pack = 0;
|
std::array<uint8_t, 8> vals{}; // collected 2-bit pixel values for this output byte
|
||||||
int bitsCollected = 0;
|
int collected = 0;
|
||||||
|
const int yParity = dstY & 1;
|
||||||
|
|
||||||
for (int x = 0; x < LCD_WIDTH; ++x) {
|
for (int x = 0; x < LCD_WIDTH; ++x) {
|
||||||
const int colStart = kFullHeightColumnBounds[static_cast<std::size_t>(x)];
|
const int colStart = kFullHeightColumnBounds[static_cast<std::size_t>(x)];
|
||||||
const int colEnd = kFullHeightColumnBounds[static_cast<std::size_t>(x) + 1];
|
const int colEnd = kFullHeightColumnBounds[static_cast<std::size_t>(x) + 1];
|
||||||
CARDBOY_CHECK(colEnd > colStart);
|
CARDBOY_CHECK(colEnd > colStart);
|
||||||
|
|
||||||
|
// expand this source pixel across its horizontal span
|
||||||
|
const uint8_t v = static_cast<uint8_t>(pixels[x]);
|
||||||
for (int dstX = colStart; dstX < colEnd; ++dstX) {
|
for (int dstX = colStart; dstX < colEnd; ++dstX) {
|
||||||
const bool on = shouldPixelBeOn(pixels[x], dstX, dstY);
|
vals[static_cast<std::size_t>(collected)] = v;
|
||||||
pack = static_cast<uint8_t>((pack << 1) | (on ? 1 : 0));
|
++collected;
|
||||||
++bitsCollected;
|
|
||||||
if (bitsCollected == 8) {
|
if (collected == 8) {
|
||||||
const int byteStart = dstX - 7;
|
const int byteStart = dstX - 7;
|
||||||
CARDBOY_CHECK((byteStart % 8) == 0);
|
CARDBOY_CHECK((byteStart % 8) == 0);
|
||||||
|
const int xParity = byteStart & 1;
|
||||||
|
|
||||||
|
// build two 4-pixel packs from the 8 collected values (mask to 2 bits)
|
||||||
|
const uint8_t p4a = (vals[0]) | ((vals[1]) << 2) | ((vals[2]) << 4) | ((vals[3]) << 6);
|
||||||
|
|
||||||
|
const uint8_t p4b = (vals[4]) | ((vals[5]) << 2) | ((vals[6]) << 4) | ((vals[7]) << 6);
|
||||||
|
|
||||||
|
// two LUT hits → two nibbles → one byte
|
||||||
|
const uint8_t n0 = kNibbleLUT[yParity][xParity][p4a];
|
||||||
|
const uint8_t n1 = kNibbleLUT[yParity][xParity][p4b];
|
||||||
|
const uint8_t pack = static_cast<uint8_t>((n0 << 4) | (n1 & 0x0F));
|
||||||
|
|
||||||
fb.drawBits8(byteStart, dstY, pack);
|
fb.drawBits8(byteStart, dstY, pack);
|
||||||
pack = 0;
|
collected = 0; // reset for next byte
|
||||||
bitsCollected = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CARDBOY_CHECK(bitsCollected == 0);
|
CARDBOY_CHECK(collected == 0); // must end on byte boundary
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
static uint8_t romRead(struct gb_s* gb, const uint_fast32_t addr) {
|
static uint8_t romRead(struct gb_s* gb, const uint_fast32_t addr) {
|
||||||
auto* self = fromGb(gb);
|
auto* self = fromGb(gb);
|
||||||
CARDBOY_CHECK_CODE(if (!self) return 0xFF;
|
CARDBOY_CHECK_CODE(if (!self) return 0xFF;
|
||||||
@@ -1181,6 +1252,7 @@ private:
|
|||||||
return self->romDataView[static_cast<std::size_t>(addr)];
|
return self->romDataView[static_cast<std::size_t>(addr)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
static uint8_t cartRamRead(struct gb_s* gb, const uint_fast32_t addr) {
|
static uint8_t cartRamRead(struct gb_s* gb, const uint_fast32_t addr) {
|
||||||
auto* self = fromGb(gb);
|
auto* self = fromGb(gb);
|
||||||
if (!self)
|
if (!self)
|
||||||
@@ -1262,6 +1334,9 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern "C" __attribute__((always_inline)) uint8_t gb_rom_read(struct gb_s* gb, const uint_fast32_t addr) {
|
||||||
|
return GameboyApp::romRead(gb, addr);
|
||||||
|
}
|
||||||
class GameboyAppFactory final : public cardboy::sdk::IAppFactory {
|
class GameboyAppFactory final : public cardboy::sdk::IAppFactory {
|
||||||
public:
|
public:
|
||||||
const char* name() const override { return kGameboyAppName; }
|
const char* name() const override { return kGameboyAppName; }
|
||||||
|
|||||||
Reference in New Issue
Block a user