Update to SameBoy-master [2019-12-02]
This commit is contained in:
byuu 2019-12-02 20:22:51 +09:00
parent a64c1adaa8
commit dafd673177
47 changed files with 295 additions and 14289 deletions

View File

@ -29,7 +29,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "bsnes";
static const string Version = "112.11";
static const string Version = "112.12";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "https://byuu.org";

File diff suppressed because it is too large Load Diff

View File

@ -1,164 +0,0 @@
#ifndef apu_h
#define apu_h
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include "gb_struct_def.h"
#ifdef GB_INTERNAL
/* Speed = 1 / Length (in seconds) */
#define DAC_DECAY_SPEED 20000
#define DAC_ATTACK_SPEED 20000
/* Divides nicely and never overflows with 4 channels and 8 (1-8) volume levels */
#ifdef WIIU
/* Todo: Remove this hack once https://github.com/libretro/RetroArch/issues/6252 is fixed*/
#define MAX_CH_AMP (0xFF0 / 2)
#else
#define MAX_CH_AMP 0xFF0
#endif
#define CH_STEP (MAX_CH_AMP/0xF/8)
#endif
/* APU ticks are 2MHz, triggered by an internal APU clock. */
typedef struct
{
int16_t left;
int16_t right;
} GB_sample_t;
typedef struct
{
double left;
double right;
} GB_double_sample_t;
enum GB_CHANNELS {
GB_SQUARE_1,
GB_SQUARE_2,
GB_WAVE,
GB_NOISE,
GB_N_CHANNELS
};
typedef void (*GB_sample_callback_t)(GB_gameboy_t *gb, GB_sample_t *sample);
typedef struct
{
bool global_enable;
uint8_t apu_cycles;
uint8_t samples[GB_N_CHANNELS];
bool is_active[GB_N_CHANNELS];
uint8_t div_divider; // The DIV register ticks the APU at 512Hz, but is then divided
// once more to generate 128Hz and 64Hz clocks
uint8_t lf_div; // The APU runs in 2MHz, but channels 1, 2 and 4 run in 1MHZ so we divide
// need to divide the signal.
uint8_t square_sweep_countdown; // In 128Hz
uint8_t square_sweep_calculate_countdown; // In 2 MHz
uint16_t new_sweep_sample_legnth;
uint16_t shadow_sweep_sample_legnth;
bool sweep_enabled;
bool sweep_decreasing;
struct {
uint16_t pulse_length; // Reloaded from NRX1 (xorred), in 256Hz DIV ticks
uint8_t current_volume; // Reloaded from NRX2
uint8_t volume_countdown; // Reloaded from NRX2
uint8_t current_sample_index; /* For save state compatibility,
highest bit is reused (See NR14/NR24's
write code)*/
uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length, xorred $7FF)
uint16_t sample_length; // From NRX3, NRX4, in APU ticks
bool length_enabled; // NRX4
} square_channels[2];
struct {
bool enable; // NR30
uint16_t pulse_length; // Reloaded from NR31 (xorred), in 256Hz DIV ticks
uint8_t shift; // NR32
uint16_t sample_length; // NR33, NR34, in APU ticks
bool length_enabled; // NR34
uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length, xorred $7FF)
uint8_t current_sample_index;
uint8_t current_sample; // Current sample before shifting.
int8_t wave_form[32];
bool wave_form_just_read;
} wave_channel;
struct {
uint16_t pulse_length; // Reloaded from NR41 (xorred), in 256Hz DIV ticks
uint8_t current_volume; // Reloaded from NR42
uint8_t volume_countdown; // Reloaded from NR42
uint16_t lfsr;
bool narrow;
uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length)
uint16_t sample_length; // From NR43, in APU ticks
bool length_enabled; // NR44
uint8_t alignment; // If (NR43 & 7) != 0, samples are aligned to 512KHz clock instead of
// 1MHz. This variable keeps track of the alignment.
} noise_channel;
bool skip_div_event;
bool current_lfsr_sample;
} GB_apu_t;
typedef enum {
GB_HIGHPASS_OFF, // Do not apply any filter, keep DC offset
GB_HIGHPASS_ACCURATE, // Apply a highpass filter similar to the one used on hardware
GB_HIGHPASS_REMOVE_DC_OFFSET, // Remove DC Offset without affecting the waveform
GB_HIGHPASS_MAX
} GB_highpass_mode_t;
typedef struct {
unsigned sample_rate;
double sample_cycles; // In 8 MHz units
double cycles_per_sample;
// Samples are NOT normalized to MAX_CH_AMP * 4 at this stage!
unsigned cycles_since_render;
unsigned last_update[GB_N_CHANNELS];
GB_sample_t current_sample[GB_N_CHANNELS];
GB_sample_t summed_samples[GB_N_CHANNELS];
double dac_discharge[GB_N_CHANNELS];
GB_highpass_mode_t highpass_mode;
double highpass_rate;
GB_double_sample_t highpass_diff;
GB_sample_callback_t sample_callback;
bool rate_set_in_clocks;
} GB_apu_output_t;
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate);
void GB_set_sample_rate_by_clocks(GB_gameboy_t *gb, double cycles_per_sample); /* Cycles are in 8MHz units */
void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode);
void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback);
#ifdef GB_INTERNAL
bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, unsigned index);
void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value);
uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg);
void GB_apu_div_event(GB_gameboy_t *gb);
void GB_apu_init(GB_gameboy_t *gb);
void GB_apu_run(GB_gameboy_t *gb);
void GB_apu_update_cycles_per_sample(GB_gameboy_t *gb);
#endif
#endif /* apu_h */

View File

@ -1,149 +0,0 @@
#include "gb.h"
static int noise_seed = 0;
/* This is not a complete emulation of the camera chip. Only the features used by the GameBoy Camera ROMs are supported.
We also do not emulate the timing of the real cart, as it might be actually faster than the webcam. */
static uint8_t generate_noise(uint8_t x, uint8_t y)
{
int value = (x + y * 128 + noise_seed);
uint8_t *data = (uint8_t *) &value;
unsigned hash = 0;
while ((int *) data != &value + 1) {
hash ^= (*data << 8);
if (hash & 0x8000) {
hash ^= 0x8a00;
hash ^= *data;
}
data++;
hash <<= 1;
}
return (hash >> 8);
}
static long get_processed_color(GB_gameboy_t *gb, uint8_t x, uint8_t y)
{
if (x >= 128) {
x = 0;
}
if (y >= 112) {
y = 0;
}
long color = gb->camera_get_pixel_callback? gb->camera_get_pixel_callback(gb, x, y) : (generate_noise(x, y));
static const double gain_values[] =
{0.8809390, 0.9149149, 0.9457498, 0.9739758,
1.0000000, 1.0241412, 1.0466537, 1.0677433,
1.0875793, 1.1240310, 1.1568911, 1.1868043,
1.2142561, 1.2396208, 1.2743837, 1.3157323,
1.3525190, 1.3856512, 1.4157897, 1.4434309,
1.4689574, 1.4926697, 1.5148087, 1.5355703,
1.5551159, 1.5735801, 1.5910762, 1.6077008,
1.6235366, 1.6386550, 1.6531183, 1.6669808};
/* Multiply color by gain value */
color *= gain_values[gb->camera_registers[GB_CAMERA_GAIN_AND_EDGE_ENHACEMENT_FLAGS] & 0x1F];
/* Color is multiplied by the exposure register to simulate exposure. */
color = color * ((gb->camera_registers[GB_CAMERA_EXPOSURE_HIGH] << 8) + gb->camera_registers[GB_CAMERA_EXPOSURE_LOW]) / 0x1000;
return color;
}
uint8_t GB_camera_read_image(GB_gameboy_t *gb, uint16_t addr)
{
if (gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] & 1) {
/* Forbid reading the image while the camera is busy. */
return 0xFF;
}
uint8_t tile_x = addr / 0x10 % 0x10;
uint8_t tile_y = addr / 0x10 / 0x10;
uint8_t y = ((addr >> 1) & 0x7) + tile_y * 8;
uint8_t bit = addr & 1;
uint8_t ret = 0;
for (uint8_t x = tile_x * 8; x < tile_x * 8 + 8; x++) {
long color = get_processed_color(gb, x, y);
static const double edge_enhancement_ratios[] = {0.5, 0.75, 1, 1.25, 2, 3, 4, 5};
double edge_enhancement_ratio = edge_enhancement_ratios[(gb->camera_registers[GB_CAMERA_EDGE_ENHANCEMENT_INVERT_AND_VOLTAGE] >> 4) & 0x7];
if ((gb->camera_registers[GB_CAMERA_GAIN_AND_EDGE_ENHACEMENT_FLAGS] & 0xE0) == 0xE0) {
color += (color * 4) * edge_enhancement_ratio;
color -= get_processed_color(gb, x - 1, y) * edge_enhancement_ratio;
color -= get_processed_color(gb, x + 1, y) * edge_enhancement_ratio;
color -= get_processed_color(gb, x, y - 1) * edge_enhancement_ratio;
color -= get_processed_color(gb, x, y + 1) * edge_enhancement_ratio;
}
/* The camera's registers are used as a threshold pattern, which defines the dithering */
uint8_t pattern_base = ((x & 3) + (y & 3) * 4) * 3 + GB_CAMERA_DITHERING_PATTERN_START;
if (color < gb->camera_registers[pattern_base]) {
color = 3;
}
else if (color < gb->camera_registers[pattern_base + 1]) {
color = 2;
}
else if (color < gb->camera_registers[pattern_base + 2]) {
color = 1;
}
else {
color = 0;
}
ret <<= 1;
ret |= (color >> bit) & 1;
}
return ret;
}
void GB_set_camera_get_pixel_callback(GB_gameboy_t *gb, GB_camera_get_pixel_callback_t callback)
{
gb->camera_get_pixel_callback = callback;
}
void GB_set_camera_update_request_callback(GB_gameboy_t *gb, GB_camera_update_request_callback_t callback)
{
gb->camera_update_request_callback = callback;
}
void GB_camera_updated(GB_gameboy_t *gb)
{
gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] &= ~1;
}
void GB_camera_write_register(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
{
addr &= 0x7F;
if (addr == GB_CAMERA_SHOOT_AND_1D_FLAGS) {
value &= 0x7;
noise_seed = rand();
if ((value & 1) && !(gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] & 1) && gb->camera_update_request_callback) {
/* If no callback is set, ignore the write as if the camera is instantly done */
gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] |= 1;
gb->camera_update_request_callback(gb);
}
}
else {
if (addr >= 0x36) {
GB_log(gb, "Wrote invalid camera register %02x: %2x\n", addr, value);
return;
}
gb->camera_registers[addr] = value;
}
}
uint8_t GB_camera_read_register(GB_gameboy_t *gb, uint16_t addr)
{
if ((addr & 0x7F) == 0) {
return gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS];
}
return 0;
}

View File

@ -1,29 +0,0 @@
#ifndef camera_h
#define camera_h
#include <stdint.h>
#include "gb_struct_def.h"
typedef uint8_t (*GB_camera_get_pixel_callback_t)(GB_gameboy_t *gb, uint8_t x, uint8_t y);
typedef void (*GB_camera_update_request_callback_t)(GB_gameboy_t *gb);
enum {
GB_CAMERA_SHOOT_AND_1D_FLAGS = 0,
GB_CAMERA_GAIN_AND_EDGE_ENHACEMENT_FLAGS = 1,
GB_CAMERA_EXPOSURE_HIGH = 2,
GB_CAMERA_EXPOSURE_LOW = 3,
GB_CAMERA_EDGE_ENHANCEMENT_INVERT_AND_VOLTAGE = 4,
GB_CAMERA_DITHERING_PATTERN_START = 6,
GB_CAMERA_DITHERING_PATTERN_END = 0x35,
};
uint8_t GB_camera_read_image(GB_gameboy_t *gb, uint16_t addr);
void GB_set_camera_get_pixel_callback(GB_gameboy_t *gb, GB_camera_get_pixel_callback_t callback);
void GB_set_camera_update_request_callback(GB_gameboy_t *gb, GB_camera_update_request_callback_t callback);
void GB_camera_updated(GB_gameboy_t *gb);
void GB_camera_write_register(GB_gameboy_t *gb, uint16_t addr, uint8_t value);
uint8_t GB_camera_read_register(GB_gameboy_t *gb, uint16_t addr);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,43 +0,0 @@
#ifndef debugger_h
#define debugger_h
#include <stdbool.h>
#include <stdint.h>
#include "gb_struct_def.h"
#include "symbol_hash.h"
#ifdef GB_INTERNAL
#ifdef DISABLE_DEBUGGER
#define GB_debugger_run(gb) (void)0
#define GB_debugger_handle_async_commands(gb) (void)0
#define GB_debugger_ret_hook(gb) (void)0
#define GB_debugger_call_hook(gb, addr) (void)addr
#define GB_debugger_test_write_watchpoint(gb, addr, value) ((void)addr, (void)value)
#define GB_debugger_test_read_watchpoint(gb, addr) (void)addr
#else
void GB_debugger_run(GB_gameboy_t *gb);
void GB_debugger_handle_async_commands(GB_gameboy_t *gb);
void GB_debugger_call_hook(GB_gameboy_t *gb, uint16_t call_addr);
void GB_debugger_ret_hook(GB_gameboy_t *gb);
void GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, uint16_t addr, uint8_t value);
void GB_debugger_test_read_watchpoint(GB_gameboy_t *gb, uint16_t addr);
const GB_bank_symbol_t *GB_debugger_find_symbol(GB_gameboy_t *gb, uint16_t addr);
#endif /* DISABLE_DEBUGGER */
#endif
#ifdef GB_INTERNAL
bool /* Returns true if debugger waits for more commands. Not relevant for non-GB_INTERNAL */
#else
void
#endif
GB_debugger_execute_command(GB_gameboy_t *gb, char *input); /* Destroys input. */
void GB_debugger_load_symbol_file(GB_gameboy_t *gb, const char *path);
const char *GB_debugger_name_for_address(GB_gameboy_t *gb, uint16_t addr);
bool GB_debugger_evaluate(GB_gameboy_t *gb, const char *string, uint16_t *result, uint16_t *result_bank); /* result_bank is -1 if unused. */
void GB_debugger_break(GB_gameboy_t *gb);
bool GB_debugger_is_stopped(GB_gameboy_t *gb);
void GB_debugger_set_disabled(GB_gameboy_t *gb, bool disabled);
void GB_debugger_clear_symbols(GB_gameboy_t *gb);
#endif /* debugger_h */

File diff suppressed because it is too large Load Diff

View File

@ -1,54 +0,0 @@
#ifndef display_h
#define display_h
#include "gb.h"
#include <stdbool.h>
#include <stdint.h>
#ifdef GB_INTERNAL
void GB_display_run(GB_gameboy_t *gb, uint8_t cycles);
void GB_palette_changed(GB_gameboy_t *gb, bool background_palette, uint8_t index);
void GB_window_related_write(GB_gameboy_t *gb, uint8_t addr, uint8_t value);
void GB_STAT_update(GB_gameboy_t *gb);
void GB_lcd_off(GB_gameboy_t *gb);
#endif
typedef enum {
GB_PALETTE_NONE,
GB_PALETTE_BACKGROUND,
GB_PALETTE_OAM,
GB_PALETTE_AUTO,
} GB_palette_type_t;
typedef enum {
GB_MAP_AUTO,
GB_MAP_9800,
GB_MAP_9C00,
} GB_map_type_t;
typedef enum {
GB_TILESET_AUTO,
GB_TILESET_8800,
GB_TILESET_8000,
} GB_tileset_type_t;
typedef struct {
uint32_t image[128];
uint8_t x, y, tile, flags;
uint16_t oam_addr;
bool obscured_by_line_limit;
} GB_oam_info_t;
typedef enum {
GB_COLOR_CORRECTION_DISABLED,
GB_COLOR_CORRECTION_CORRECT_CURVES,
GB_COLOR_CORRECTION_EMULATE_HARDWARE,
GB_COLOR_CORRECTION_PRESERVE_BRIGHTNESS,
} GB_color_correction_mode_t;
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);
uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest, uint8_t *sprite_height);
uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color);
void GB_set_color_correction_mode(GB_gameboy_t *gb, GB_color_correction_mode_t mode);
#endif /* display_h */

File diff suppressed because it is too large Load Diff

View File

@ -1,729 +0,0 @@
#ifndef GB_h
#define GB_h
#define typeof __typeof__
#include <stdbool.h>
#include <stdint.h>
#include <time.h>
#include "gb_struct_def.h"
#include "save_state.h"
#include "apu.h"
#include "camera.h"
#include "debugger.h"
#include "display.h"
#include "joypad.h"
#include "mbc.h"
#include "memory.h"
#include "printer.h"
#include "timing.h"
#include "rewind.h"
#include "sm83_cpu.h"
#include "symbol_hash.h"
#include "sgb.h"
#define GB_STRUCT_VERSION 13
#define GB_MODEL_FAMILY_MASK 0xF00
#define GB_MODEL_DMG_FAMILY 0x000
#define GB_MODEL_MGB_FAMILY 0x100
#define GB_MODEL_CGB_FAMILY 0x200
#define GB_MODEL_PAL_BIT 0x1000
#define GB_MODEL_NO_SFC_BIT 0x2000
#ifdef GB_INTERNAL
#if __clang__
#define UNROLL _Pragma("unroll")
#elif __GNUC__
#define UNROLL _Pragma("GCC unroll 8")
#else
#define UNROLL
#endif
#endif
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define GB_BIG_ENDIAN
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define GB_LITTLE_ENDIAN
#else
#error Unable to detect endianess
#endif
typedef union {
struct {
uint8_t seconds;
uint8_t minutes;
uint8_t hours;
uint8_t days;
uint8_t high;
};
uint8_t data[5];
} GB_rtc_time_t;
typedef enum {
// GB_MODEL_DMG_0 = 0x000,
// GB_MODEL_DMG_A = 0x001,
GB_MODEL_DMG_B = 0x002,
// GB_MODEL_DMG_C = 0x003,
GB_MODEL_SGB = 0x004,
GB_MODEL_SGB_NTSC = GB_MODEL_SGB,
GB_MODEL_SGB_PAL = GB_MODEL_SGB | GB_MODEL_PAL_BIT,
GB_MODEL_SGB_NTSC_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT,
GB_MODEL_SGB_NO_SFC = GB_MODEL_SGB_NTSC_NO_SFC,
GB_MODEL_SGB_PAL_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT | GB_MODEL_PAL_BIT,
// GB_MODEL_MGB = 0x100,
GB_MODEL_SGB2 = 0x101,
GB_MODEL_SGB2_NO_SFC = GB_MODEL_SGB2 | GB_MODEL_NO_SFC_BIT,
// GB_MODEL_CGB_0 = 0x200,
// GB_MODEL_CGB_A = 0x201,
// GB_MODEL_CGB_B = 0x202,
GB_MODEL_CGB_C = 0x203,
// GB_MODEL_CGB_D = 0x204,
GB_MODEL_CGB_E = 0x205,
GB_MODEL_AGB = 0x206,
} GB_model_t;
enum {
GB_REGISTER_AF,
GB_REGISTER_BC,
GB_REGISTER_DE,
GB_REGISTER_HL,
GB_REGISTER_SP,
GB_REGISTERS_16_BIT /* Count */
};
/* Todo: Actually use these! */
enum {
GB_CARRY_FLAG = 16,
GB_HALF_CARRY_FLAG = 32,
GB_SUBSTRACT_FLAG = 64,
GB_ZERO_FLAG = 128,
};
#define GB_MAX_IR_QUEUE 256
enum {
/* Joypad and Serial */
GB_IO_JOYP = 0x00, // Joypad (R/W)
GB_IO_SB = 0x01, // Serial transfer data (R/W)
GB_IO_SC = 0x02, // Serial Transfer Control (R/W)
/* Missing */
/* Timers */
GB_IO_DIV = 0x04, // Divider Register (R/W)
GB_IO_TIMA = 0x05, // Timer counter (R/W)
GB_IO_TMA = 0x06, // Timer Modulo (R/W)
GB_IO_TAC = 0x07, // Timer Control (R/W)
/* Missing */
GB_IO_IF = 0x0f, // Interrupt Flag (R/W)
/* Sound */
GB_IO_NR10 = 0x10, // Channel 1 Sweep register (R/W)
GB_IO_NR11 = 0x11, // Channel 1 Sound length/Wave pattern duty (R/W)
GB_IO_NR12 = 0x12, // Channel 1 Volume Envelope (R/W)
GB_IO_NR13 = 0x13, // Channel 1 Frequency lo (Write Only)
GB_IO_NR14 = 0x14, // Channel 1 Frequency hi (R/W)
/* NR20 does not exist */
GB_IO_NR21 = 0x16, // Channel 2 Sound Length/Wave Pattern Duty (R/W)
GB_IO_NR22 = 0x17, // Channel 2 Volume Envelope (R/W)
GB_IO_NR23 = 0x18, // Channel 2 Frequency lo data (W)
GB_IO_NR24 = 0x19, // Channel 2 Frequency hi data (R/W)
GB_IO_NR30 = 0x1a, // Channel 3 Sound on/off (R/W)
GB_IO_NR31 = 0x1b, // Channel 3 Sound Length
GB_IO_NR32 = 0x1c, // Channel 3 Select output level (R/W)
GB_IO_NR33 = 0x1d, // Channel 3 Frequency's lower data (W)
GB_IO_NR34 = 0x1e, // Channel 3 Frequency's higher data (R/W)
/* NR40 does not exist */
GB_IO_NR41 = 0x20, // Channel 4 Sound Length (R/W)
GB_IO_NR42 = 0x21, // Channel 4 Volume Envelope (R/W)
GB_IO_NR43 = 0x22, // Channel 4 Polynomial Counter (R/W)
GB_IO_NR44 = 0x23, // Channel 4 Counter/consecutive, Inital (R/W)
GB_IO_NR50 = 0x24, // Channel control / ON-OFF / Volume (R/W)
GB_IO_NR51 = 0x25, // Selection of Sound output terminal (R/W)
GB_IO_NR52 = 0x26, // Sound on/off
/* Missing */
GB_IO_WAV_START = 0x30, // Wave pattern start
GB_IO_WAV_END = 0x3f, // Wave pattern end
/* Graphics */
GB_IO_LCDC = 0x40, // LCD Control (R/W)
GB_IO_STAT = 0x41, // LCDC Status (R/W)
GB_IO_SCY = 0x42, // Scroll Y (R/W)
GB_IO_SCX = 0x43, // Scroll X (R/W)
GB_IO_LY = 0x44, // LCDC Y-Coordinate (R)
GB_IO_LYC = 0x45, // LY Compare (R/W)
GB_IO_DMA = 0x46, // DMA Transfer and Start Address (W)
GB_IO_BGP = 0x47, // BG Palette Data (R/W) - Non CGB Mode Only
GB_IO_OBP0 = 0x48, // Object Palette 0 Data (R/W) - Non CGB Mode Only
GB_IO_OBP1 = 0x49, // Object Palette 1 Data (R/W) - Non CGB Mode Only
GB_IO_WY = 0x4a, // Window Y Position (R/W)
GB_IO_WX = 0x4b, // Window X Position minus 7 (R/W)
// Has some undocumented compatibility flags written at boot.
// Unfortunately it is not readable or writable after boot has finished, so research of this
// register is quite limited. The value written to this register, however, can be controlled
// in some cases.
GB_IO_DMG_EMULATION = 0x4c,
/* General CGB features */
GB_IO_KEY1 = 0x4d, // CGB Mode Only - Prepare Speed Switch
/* Missing */
GB_IO_VBK = 0x4f, // CGB Mode Only - VRAM Bank
GB_IO_BIOS = 0x50, // Write to disable the BIOS mapping
/* CGB DMA */
GB_IO_HDMA1 = 0x51, // CGB Mode Only - New DMA Source, High
GB_IO_HDMA2 = 0x52, // CGB Mode Only - New DMA Source, Low
GB_IO_HDMA3 = 0x53, // CGB Mode Only - New DMA Destination, High
GB_IO_HDMA4 = 0x54, // CGB Mode Only - New DMA Destination, Low
GB_IO_HDMA5 = 0x55, // CGB Mode Only - New DMA Length/Mode/Start
/* IR */
GB_IO_RP = 0x56, // CGB Mode Only - Infrared Communications Port
/* Missing */
/* CGB Paletts */
GB_IO_BGPI = 0x68, // CGB Mode Only - Background Palette Index
GB_IO_BGPD = 0x69, // CGB Mode Only - Background Palette Data
GB_IO_OBPI = 0x6a, // CGB Mode Only - Sprite Palette Index
GB_IO_OBPD = 0x6b, // CGB Mode Only - Sprite Palette Data
// 1 is written for DMG ROMs on a CGB. Does not appear to have an effect.
GB_IO_DMG_EMULATION_INDICATION = 0x6c, // (FEh) Bit 0 (Read/Write)
/* Missing */
GB_IO_SVBK = 0x70, // CGB Mode Only - WRAM Bank
GB_IO_UNKNOWN2 = 0x72, // (00h) - Bit 0-7 (Read/Write)
GB_IO_UNKNOWN3 = 0x73, // (00h) - Bit 0-7 (Read/Write)
GB_IO_UNKNOWN4 = 0x74, // (00h) - Bit 0-7 (Read/Write) - CGB Mode Only
GB_IO_UNKNOWN5 = 0x75, // (8Fh) - Bit 4-6 (Read/Write)
GB_IO_PCM_12 = 0x76, // Channels 1 and 2 amplitudes
GB_IO_PCM_34 = 0x77, // Channels 3 and 4 amplitudes
GB_IO_UNKNOWN8 = 0x7F, // Unknown, write only
};
typedef enum {
GB_LOG_BOLD = 1,
GB_LOG_DASHED_UNDERLINE = 2,
GB_LOG_UNDERLINE = 4,
GB_LOG_UNDERLINE_MASK = GB_LOG_DASHED_UNDERLINE | GB_LOG_UNDERLINE
} GB_log_attributes;
#ifdef GB_INTERNAL
#define LCDC_PERIOD 70224
#define CPU_FREQUENCY 0x400000
#define SGB_NTSC_FREQUENCY (21477272 / 5)
#define SGB_PAL_FREQUENCY (21281370 / 5)
#define DIV_CYCLES (0x100)
#define INTERNAL_DIV_CYCLES (0x40000)
#if !defined(MIN)
#define MIN(A,B) ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __a : __b; })
#endif
#if !defined(MAX)
#define MAX(A,B) ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __b : __a; })
#endif
#endif
typedef void (*GB_vblank_callback_t)(GB_gameboy_t *gb);
typedef void (*GB_log_callback_t)(GB_gameboy_t *gb, const char *string, GB_log_attributes attributes);
typedef char *(*GB_input_callback_t)(GB_gameboy_t *gb);
typedef uint32_t (*GB_rgb_encode_callback_t)(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b);
typedef void (*GB_infrared_callback_t)(GB_gameboy_t *gb, bool on, long cycles_since_last_update);
typedef void (*GB_rumble_callback_t)(GB_gameboy_t *gb, bool rumble_on);
typedef void (*GB_serial_transfer_bit_start_callback_t)(GB_gameboy_t *gb, bool bit_to_send);
typedef bool (*GB_serial_transfer_bit_end_callback_t)(GB_gameboy_t *gb);
typedef void (*GB_update_input_hint_callback_t)(GB_gameboy_t *gb);
typedef void (*GB_joyp_write_callback_t)(GB_gameboy_t *gb, uint8_t value);
typedef void (*GB_icd_pixel_callback_t)(GB_gameboy_t *gb, uint8_t row);
typedef void (*GB_icd_hreset_callback_t)(GB_gameboy_t *gb);
typedef void (*GB_icd_vreset_callback_t)(GB_gameboy_t *gb);
typedef struct {
bool state;
long delay;
} GB_ir_queue_item_t;
struct GB_breakpoint_s;
struct GB_watchpoint_s;
typedef struct {
uint8_t pixel; // Color, 0-3
uint8_t palette; // Palette, 0 - 7 (CGB); 0-1 in DMG (or just 0 for BG)
uint8_t priority; // Sprite priority 0 in DMG, OAM index in CGB
bool bg_priority; // For sprite FIFO the BG priority bit. For the BG FIFO  the CGB attributes priority bit
} GB_fifo_item_t;
#define GB_FIFO_LENGTH 16
typedef struct {
GB_fifo_item_t fifo[GB_FIFO_LENGTH];
uint8_t read_end;
uint8_t write_end;
} GB_fifo_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.
This struct is not packed, but dumped sections exclusively use types that have the same alignment in both 32 and 64
bit platforms. */
/* We make sure bool is 1 for cross-platform save state compatibility. */
/* Todo: We might want to typedef our own bool if this prevents SameBoy from working on specific platforms. */
_Static_assert(sizeof(bool) == 1, "sizeof(bool) != 1");
#ifdef GB_INTERNAL
struct GB_gameboy_s {
#else
struct GB_gameboy_internal_s {
#endif
GB_SECTION(header,
/* The magic makes sure a state file is:
- Indeed a SameBoy state file.
- Has the same endianess has the current platform. */
volatile uint32_t magic;
/* The version field makes sure we don't load save state files with a completely different structure.
This happens when struct fields are removed/resized in an backward incompatible manner. */
uint32_t version;
);
GB_SECTION(core_state,
/* Registers */
uint16_t pc;
union {
uint16_t registers[GB_REGISTERS_16_BIT];
struct {
uint16_t af,
bc,
de,
hl,
sp;
};
struct {
#ifdef GB_BIG_ENDIAN
uint8_t a, f,
b, c,
d, e,
h, l;
#else
uint8_t f, a,
c, b,
e, d,
l, h;
#endif
};
};
uint8_t ime;
uint8_t interrupt_enable;
uint8_t cgb_ram_bank;
/* CPU and General Hardware Flags*/
GB_model_t model;
bool cgb_mode;
bool cgb_double_speed;
bool halted;
bool stopped;
bool boot_rom_finished;
bool ime_toggle; /* ei has delayed a effect.*/
bool halt_bug;
bool just_halted;
/* Misc state */
bool infrared_input;
GB_printer_t printer;
uint8_t extra_oam[0xff00 - 0xfea0];
uint32_t ram_size; // Different between CGB and DMG
);
/* DMA and HDMA */
GB_SECTION(dma,
bool hdma_on;
bool hdma_on_hblank;
uint8_t hdma_steps_left;
int16_t hdma_cycles; // in 8MHz units
uint16_t hdma_current_src, hdma_current_dest;
uint8_t dma_steps_left;
uint8_t dma_current_dest;
uint16_t dma_current_src;
int16_t dma_cycles;
bool is_dma_restarting;
uint8_t last_opcode_read; /* Required to emulte HDMA reads from Exxx */
bool hdma_starting;
);
/* MBC */
GB_SECTION(mbc,
uint16_t mbc_rom_bank;
uint8_t mbc_ram_bank;
uint32_t mbc_ram_size;
bool mbc_ram_enable;
union {
struct {
uint8_t bank_low:5;
uint8_t bank_high:2;
uint8_t mode:1;
} mbc1;
struct {
uint8_t rom_bank:4;
} mbc2;
struct {
uint8_t rom_bank:7;
uint8_t padding:1;
uint8_t ram_bank:4;
} mbc3;
struct {
uint8_t rom_bank_low;
uint8_t rom_bank_high:1;
uint8_t ram_bank:4;
} mbc5;
struct {
uint8_t bank_low:6;
uint8_t bank_high:3;
uint8_t mode:1;
} huc1;
struct {
uint8_t rom_bank;
uint8_t ram_bank;
} huc3;
};
uint16_t mbc_rom0_bank; /* For some MBC1 wirings. */
bool camera_registers_mapped;
uint8_t camera_registers[0x36];
bool rumble_state;
);
/* HRAM and HW Registers */
GB_SECTION(hram,
uint8_t hram[0xFFFF - 0xFF80];
uint8_t io_registers[0x80];
);
/* Timing */
GB_SECTION(timing,
GB_UNIT(display);
GB_UNIT(div);
uint16_t div_counter;
uint8_t tima_reload_state; /* After TIMA overflows, it becomes 0 for 4 cycles before actually reloading. */
uint16_t serial_cycles;
uint16_t serial_length;
uint8_t double_speed_alignment;
uint8_t serial_count;
);
/* APU */
GB_SECTION(apu,
GB_apu_t apu;
);
/* RTC */
GB_SECTION(rtc,
GB_rtc_time_t rtc_real, rtc_latched;
uint64_t last_rtc_second;
bool rtc_latch;
);
/* Video Display */
GB_SECTION(video,
uint32_t vram_size; // Different between CGB and DMG
uint8_t cgb_vram_bank;
uint8_t oam[0xA0];
uint8_t background_palettes_data[0x40];
uint8_t sprite_palettes_data[0x40];
uint8_t position_in_line;
bool stat_interrupt_line;
uint8_t effective_scx;
uint8_t wy_diff;
/* The LCDC will skip the first frame it renders after turning it on.
On the CGB, a frame is not skipped if the previous frame was skipped as well.
See https://www.reddit.com/r/EmuDev/comments/6exyxu/ */
/* TODO: Drop this and properly emulate the dropped vreset signal*/
enum {
GB_FRAMESKIP_LCD_TURNED_ON, // On a DMG, the LCD renders a blank screen during this state,
// on a CGB, the previous frame is repeated (which might be
// blank if the LCD was off for more than a few cycles)
GB_FRAMESKIP_FIRST_FRAME_SKIPPED, // This state is 'skipped' when emulating a DMG
GB_FRAMESKIP_SECOND_FRAME_RENDERED,
} frame_skip_state;
bool oam_read_blocked;
bool vram_read_blocked;
bool oam_write_blocked;
bool vram_write_blocked;
bool window_disabled_while_active;
uint8_t current_line;
uint16_t ly_for_comparison;
GB_fifo_t bg_fifo, oam_fifo;
uint8_t fetcher_x;
uint8_t fetcher_y;
uint16_t cycles_for_line;
uint8_t current_tile;
uint8_t current_tile_attributes;
uint8_t current_tile_data[2];
uint8_t fetcher_state;
bool bg_fifo_paused;
bool oam_fifo_paused;
bool in_window;
uint8_t visible_objs[10];
uint8_t obj_comparators[10];
uint8_t n_visible_objs;
uint8_t oam_search_index;
uint8_t accessed_oam_row;
uint8_t extra_penalty_for_sprite_at_0;
uint8_t mode_for_interrupt;
bool lyc_interrupt_line;
bool cgb_palettes_blocked;
uint8_t current_lcd_line; // The LCD can go out of sync since the vsync signal is skipped in some cases.
uint32_t cycles_in_stop_mode;
);
/* Unsaved data. This includes all pointers, as well as everything that shouldn't be on a save state */
/* This data is reserved on reset and must come last in the struct */
GB_SECTION(unsaved,
/* ROM */
uint8_t *rom;
uint32_t rom_size;
const GB_cartridge_t *cartridge_type;
enum {
GB_STANDARD_MBC1_WIRING,
GB_MBC1M_WIRING,
} mbc1_wiring;
unsigned pending_cycles;
/* Various RAMs */
uint8_t *ram;
uint8_t *vram;
uint8_t *mbc_ram;
/* I/O */
uint32_t *screen;
uint32_t background_palettes_rgb[0x20];
uint32_t sprite_palettes_rgb[0x20];
GB_color_correction_mode_t color_correction_mode;
bool keys[4][GB_KEY_MAX];
/* Timing */
uint64_t last_sync;
uint64_t cycles_since_last_sync; // In 8MHz units
/* Audio */
GB_apu_output_t apu_output;
/* Callbacks */
void *user_data;
GB_log_callback_t log_callback;
GB_input_callback_t input_callback;
GB_input_callback_t async_input_callback;
GB_rgb_encode_callback_t rgb_encode_callback;
GB_vblank_callback_t vblank_callback;
GB_infrared_callback_t infrared_callback;
GB_camera_get_pixel_callback_t camera_get_pixel_callback;
GB_camera_update_request_callback_t camera_update_request_callback;
GB_rumble_callback_t rumble_callback;
GB_serial_transfer_bit_start_callback_t serial_transfer_bit_start_callback;
GB_serial_transfer_bit_end_callback_t serial_transfer_bit_end_callback;
GB_update_input_hint_callback_t update_input_hint_callback;
GB_joyp_write_callback_t joyp_write_callback;
GB_icd_pixel_callback_t icd_pixel_callback;
GB_icd_vreset_callback_t icd_hreset_callback;
GB_icd_vreset_callback_t icd_vreset_callback;
GB_read_memory_callback_t read_memory_callback;
/* IR */
long cycles_since_ir_change; // In 8MHz units
long cycles_since_input_ir_change; // In 8MHz units
GB_ir_queue_item_t ir_queue[GB_MAX_IR_QUEUE];
size_t ir_queue_length;
/*** Debugger ***/
volatile bool debug_stopped, debug_disable;
bool debug_fin_command, debug_next_command;
/* Breakpoints */
uint16_t n_breakpoints;
struct GB_breakpoint_s *breakpoints;
bool has_jump_to_breakpoints;
void *nontrivial_jump_state;
bool non_trivial_jump_breakpoint_occured;
/* SLD (Todo: merge with backtrace) */
bool stack_leak_detection;
int debug_call_depth;
uint16_t sp_for_call_depth[0x200]; /* Should be much more than enough */
uint16_t addr_for_call_depth[0x200];
/* Backtrace */
unsigned backtrace_size;
uint16_t backtrace_sps[0x200];
struct {
uint16_t bank;
uint16_t addr;
} backtrace_returns[0x200];
/* Watchpoints */
uint16_t n_watchpoints;
struct GB_watchpoint_s *watchpoints;
/* Symbol tables */
GB_symbol_map_t *bank_symbols[0x200];
GB_reversed_symbol_map_t reversed_symbol_map;
/* Ticks command */
unsigned long debugger_ticks;
/* Rewind */
#define GB_REWIND_FRAMES_PER_KEY 255
size_t rewind_buffer_length;
struct {
uint8_t *key_state;
uint8_t *compressed_states[GB_REWIND_FRAMES_PER_KEY];
unsigned pos;
} *rewind_sequences; // lasts about 4 seconds
size_t rewind_pos;
/* SGB - saved and allocated optionally */
GB_sgb_t *sgb;
double sgb_intro_jingle_phases[7];
double sgb_intro_sweep_phase;
double sgb_intro_sweep_previous_sample;
/* Misc */
bool turbo;
bool turbo_dont_skip;
bool disable_rendering;
uint8_t boot_rom[0x900];
bool vblank_just_occured; // For slow operations involving syscalls; these should only run once per vblank
uint8_t cycles_since_run; // How many cycles have passed since the last call to GB_run(), in 8MHz units
double clock_multiplier;
);
};
#ifndef GB_INTERNAL
struct GB_gameboy_s {
char __internal[sizeof(struct GB_gameboy_internal_s)];
};
#endif
#ifndef __printflike
/* Missing from Linux headers. */
#define __printflike(fmtarg, firstvararg) \
__attribute__((__format__ (__printf__, fmtarg, firstvararg)))
#endif
void GB_init(GB_gameboy_t *gb, GB_model_t model);
bool GB_is_inited(GB_gameboy_t *gb);
bool GB_is_cgb(GB_gameboy_t *gb);
bool GB_is_sgb(GB_gameboy_t *gb); // Returns true if the model is SGB or SGB2
bool GB_is_hle_sgb(GB_gameboy_t *gb); // Returns true if the model is SGB or SGB2 and the SFC/SNES side is HLE'd
GB_model_t GB_get_model(GB_gameboy_t *gb);
void GB_free(GB_gameboy_t *gb);
void GB_reset(GB_gameboy_t *gb);
void GB_switch_model_and_reset(GB_gameboy_t *gb, GB_model_t model);
/* Returns the time passed, in 8MHz ticks. */
uint8_t GB_run(GB_gameboy_t *gb);
/* Returns the time passed since the last frame, in nanoseconds */
uint64_t GB_run_frame(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_IE,
} 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_set_user_data(GB_gameboy_t *gb, void *data);
int GB_load_boot_rom(GB_gameboy_t *gb, const char *path);
void GB_load_boot_rom_from_buffer(GB_gameboy_t *gb, const unsigned char *buffer, size_t size);
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_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);
void GB_load_battery_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size);
void GB_load_battery(GB_gameboy_t *gb, const char *path);
void GB_set_turbo_mode(GB_gameboy_t *gb, bool on, bool no_frame_skip);
void GB_set_rendering_disabled(GB_gameboy_t *gb, bool disabled);
void GB_log(GB_gameboy_t *gb, const char *fmt, ...) __printflike(2, 3);
void GB_attributed_log(GB_gameboy_t *gb, GB_log_attributes attributes, const char *fmt, ...) __printflike(3, 4);
void GB_set_pixels_output(GB_gameboy_t *gb, uint32_t *output);
void GB_set_infrared_input(GB_gameboy_t *gb, bool state);
void GB_queue_infrared_input(GB_gameboy_t *gb, bool state, long cycles_after_previous_change); /* In 8MHz units*/
void GB_set_vblank_callback(GB_gameboy_t *gb, GB_vblank_callback_t callback);
void GB_set_log_callback(GB_gameboy_t *gb, GB_log_callback_t callback);
void GB_set_input_callback(GB_gameboy_t *gb, GB_input_callback_t callback);
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_infrared_callback(GB_gameboy_t *gb, GB_infrared_callback_t callback);
void GB_set_rumble_callback(GB_gameboy_t *gb, GB_rumble_callback_t callback);
void GB_set_update_input_hint_callback(GB_gameboy_t *gb, GB_update_input_hint_callback_t callback);
/* These APIs are used when using internal clock */
void GB_set_serial_transfer_bit_start_callback(GB_gameboy_t *gb, GB_serial_transfer_bit_start_callback_t callback);
void GB_set_serial_transfer_bit_end_callback(GB_gameboy_t *gb, GB_serial_transfer_bit_end_callback_t callback);
/* These APIs are used when using external clock */
bool GB_serial_get_data_bit(GB_gameboy_t *gb);
void GB_serial_set_data_bit(GB_gameboy_t *gb, bool data);
void GB_disconnect_serial(GB_gameboy_t *gb);
/* For integration with SFC/SNES emulators */
void GB_set_joyp_write_callback(GB_gameboy_t *gb, GB_joyp_write_callback_t callback);
void GB_set_icd_pixel_callback(GB_gameboy_t *gb, GB_icd_pixel_callback_t callback);
void GB_set_icd_hreset_callback(GB_gameboy_t *gb, GB_icd_hreset_callback_t callback);
void GB_set_icd_vreset_callback(GB_gameboy_t *gb, GB_icd_vreset_callback_t callback);
#ifdef GB_INTERNAL
uint32_t GB_get_clock_rate(GB_gameboy_t *gb);
#endif
void GB_set_clock_multiplier(GB_gameboy_t *gb, double multiplier);
unsigned GB_get_screen_width(GB_gameboy_t *gb);
unsigned GB_get_screen_height(GB_gameboy_t *gb);
double GB_get_usual_frame_rate(GB_gameboy_t *gb);
unsigned GB_get_player_count(GB_gameboy_t *gb);
#endif /* GB_h */

View File

@ -1,5 +0,0 @@
#ifndef gb_struct_def_h
#define gb_struct_def_h
struct GB_gameboy_s;
typedef struct GB_gameboy_s GB_gameboy_t;
#endif

View File

@ -1,92 +0,0 @@
#include "gb.h"
#include <assert.h>
void GB_update_joyp(GB_gameboy_t *gb)
{
if (gb->model & GB_MODEL_NO_SFC_BIT) return;
uint8_t key_selection = 0;
uint8_t previous_state = 0;
/* Todo: add delay to key selection */
previous_state = gb->io_registers[GB_IO_JOYP] & 0xF;
key_selection = (gb->io_registers[GB_IO_JOYP] >> 4) & 3;
gb->io_registers[GB_IO_JOYP] &= 0xF0;
uint8_t current_player = gb->sgb? (gb->sgb->current_player & (gb->sgb->player_count - 1) & 3) : 0;
switch (key_selection) {
case 3:
if (gb->sgb && gb->sgb->player_count > 1) {
gb->io_registers[GB_IO_JOYP] |= 0xF - current_player;
}
else {
/* Nothing is wired, all up */
gb->io_registers[GB_IO_JOYP] |= 0x0F;
}
break;
case 2:
/* Direction keys */
for (uint8_t i = 0; i < 4; i++) {
gb->io_registers[GB_IO_JOYP] |= (!gb->keys[current_player][i]) << i;
}
/* Forbid pressing two opposing keys, this breaks a lot of games; even if it's somewhat possible. */
if (!(gb->io_registers[GB_IO_JOYP] & 1)) {
gb->io_registers[GB_IO_JOYP] |= 2;
}
if (!(gb->io_registers[GB_IO_JOYP] & 4)) {
gb->io_registers[GB_IO_JOYP] |= 8;
}
break;
case 1:
/* Other keys */
for (uint8_t i = 0; i < 4; i++) {
gb->io_registers[GB_IO_JOYP] |= (!gb->keys[current_player][i + 4]) << i;
}
break;
case 0:
for (uint8_t i = 0; i < 4; i++) {
gb->io_registers[GB_IO_JOYP] |= (!(gb->keys[current_player][i] || gb->keys[current_player][i + 4])) << i;
}
break;
default:
break;
}
/* Todo: This assumes the keys *always* bounce, which is incorrect when emulating an SGB */
if (previous_state != (gb->io_registers[GB_IO_JOYP] & 0xF)) {
/* The joypad interrupt DOES occur on CGB (Tested on CGB-E), unlike what some documents say. */
gb->io_registers[GB_IO_IF] |= 0x10;
}
gb->io_registers[GB_IO_JOYP] |= 0xC0;
}
void GB_icd_set_joyp(GB_gameboy_t *gb, uint8_t value)
{
uint8_t previous_state = gb->io_registers[GB_IO_JOYP] & 0xF;
gb->io_registers[GB_IO_JOYP] &= 0xF0;
gb->io_registers[GB_IO_JOYP] |= value & 0xF;
if (previous_state & ~(gb->io_registers[GB_IO_JOYP] & 0xF)) {
gb->io_registers[GB_IO_IF] |= 0x10;
}
gb->io_registers[GB_IO_JOYP] |= 0xC0;
}
void GB_set_key_state(GB_gameboy_t *gb, GB_key_t index, bool pressed)
{
assert(index >= 0 && index < GB_KEY_MAX);
gb->keys[0][index] = pressed;
GB_update_joyp(gb);
}
void GB_set_key_state_for_player(GB_gameboy_t *gb, GB_key_t index, unsigned player, bool pressed)
{
assert(index >= 0 && index < GB_KEY_MAX);
assert(player < 4);
gb->keys[player][index] = pressed;
GB_update_joyp(gb);
}

View File

@ -1,25 +0,0 @@
#ifndef joypad_h
#define joypad_h
#include "gb_struct_def.h"
#include <stdbool.h>
typedef enum {
GB_KEY_RIGHT,
GB_KEY_LEFT,
GB_KEY_UP,
GB_KEY_DOWN,
GB_KEY_A,
GB_KEY_B,
GB_KEY_SELECT,
GB_KEY_START,
GB_KEY_MAX
} GB_key_t;
void GB_set_key_state(GB_gameboy_t *gb, GB_key_t index, bool pressed);
void GB_set_key_state_for_player(GB_gameboy_t *gb, GB_key_t index, unsigned player, bool pressed);
void GB_icd_set_joyp(GB_gameboy_t *gb, uint8_t value);
#ifdef GB_INTERNAL
void GB_update_joyp(GB_gameboy_t *gb);
#endif
#endif /* joypad_h */

View File

@ -1,154 +0,0 @@
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "gb.h"
const GB_cartridge_t GB_cart_defs[256] = {
// From http://gbdev.gg8.se/wiki/articles/The_Cartridge_Header#0147_-_Cartridge_Type
/* MBC SUBTYPE RAM BAT. RTC RUMB. */
{ GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 00h ROM ONLY
{ GB_MBC1 , GB_STANDARD_MBC, false, false, false, false}, // 01h MBC1
{ GB_MBC1 , GB_STANDARD_MBC, true , false, false, false}, // 02h MBC1+RAM
{ GB_MBC1 , GB_STANDARD_MBC, true , true , false, false}, // 03h MBC1+RAM+BATTERY
[5] =
{ GB_MBC2 , GB_STANDARD_MBC, true , false, false, false}, // 05h MBC2
{ GB_MBC2 , GB_STANDARD_MBC, true , true , false, false}, // 06h MBC2+BATTERY
[8] =
{ GB_NO_MBC, GB_STANDARD_MBC, true , false, false, false}, // 08h ROM+RAM
{ GB_NO_MBC, GB_STANDARD_MBC, true , true , false, false}, // 09h ROM+RAM+BATTERY
[0xB] =
/* Todo: Not supported yet */
{ GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 0Bh MMM01
{ GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 0Ch MMM01+RAM
{ GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 0Dh MMM01+RAM+BATTERY
[0xF] =
{ GB_MBC3 , GB_STANDARD_MBC, false, true, true , false}, // 0Fh MBC3+TIMER+BATTERY
{ GB_MBC3 , GB_STANDARD_MBC, true , true, true , false}, // 10h MBC3+TIMER+RAM+BATTERY
{ GB_MBC3 , GB_STANDARD_MBC, false, false, false, false}, // 11h MBC3
{ GB_MBC3 , GB_STANDARD_MBC, true , false, false, false}, // 12h MBC3+RAM
{ GB_MBC3 , GB_STANDARD_MBC, true , true , false, false}, // 13h MBC3+RAM+BATTERY
[0x19] =
{ GB_MBC5 , GB_STANDARD_MBC, false, false, false, false}, // 19h MBC5
{ GB_MBC5 , GB_STANDARD_MBC, true , false, false, false}, // 1Ah MBC5+RAM
{ GB_MBC5 , GB_STANDARD_MBC, true , true , false, false}, // 1Bh MBC5+RAM+BATTERY
{ GB_MBC5 , GB_STANDARD_MBC, false, false, false, true }, // 1Ch MBC5+RUMBLE
{ GB_MBC5 , GB_STANDARD_MBC, true , false, false, true }, // 1Dh MBC5+RUMBLE+RAM
{ GB_MBC5 , GB_STANDARD_MBC, true , true , false, true }, // 1Eh MBC5+RUMBLE+RAM+BATTERY
[0xFC] =
{ GB_MBC5 , GB_CAMERA , true , true , false, false}, // FCh POCKET CAMERA
{ GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // FDh BANDAI TAMA5 (Todo: Not supported)
{ GB_HUC3 , GB_STANDARD_MBC, true , true , false, false}, // FEh HuC3 (Todo: Mapper support only)
{ GB_HUC1 , GB_STANDARD_MBC, true , true , false, false}, // FFh HuC1+RAM+BATTERY (Todo: No IR bindings)
};
void GB_update_mbc_mappings(GB_gameboy_t *gb)
{
switch (gb->cartridge_type->mbc_type) {
case GB_NO_MBC: return;
case GB_MBC1:
switch (gb->mbc1_wiring) {
case GB_STANDARD_MBC1_WIRING:
gb->mbc_rom_bank = gb->mbc1.bank_low | (gb->mbc1.bank_high << 5);
if (gb->mbc1.mode == 0) {
gb->mbc_ram_bank = 0;
gb->mbc_rom0_bank = 0;
}
else {
gb->mbc_ram_bank = gb->mbc1.bank_high;
gb->mbc_rom0_bank = gb->mbc1.bank_high << 5;
}
if ((gb->mbc_rom_bank & 0x1F) == 0) {
gb->mbc_rom_bank++;
}
break;
case GB_MBC1M_WIRING:
gb->mbc_rom_bank = (gb->mbc1.bank_low & 0xF) | (gb->mbc1.bank_high << 4);
if (gb->mbc1.mode == 0) {
gb->mbc_ram_bank = 0;
gb->mbc_rom0_bank = 0;
}
else {
gb->mbc_rom0_bank = gb->mbc1.bank_high << 4;
gb->mbc_ram_bank = 0;
}
if ((gb->mbc1.bank_low & 0x1F) == 0) {
gb->mbc_rom_bank++;
}
break;
}
break;
case GB_MBC2:
gb->mbc_rom_bank = gb->mbc2.rom_bank;
if ((gb->mbc_rom_bank & 0xF) == 0) {
gb->mbc_rom_bank = 1;
}
break;
case GB_MBC3:
gb->mbc_rom_bank = gb->mbc3.rom_bank;
gb->mbc_ram_bank = gb->mbc3.ram_bank;
if (gb->mbc_rom_bank == 0) {
gb->mbc_rom_bank = 1;
}
break;
case GB_MBC5:
gb->mbc_rom_bank = gb->mbc5.rom_bank_low | (gb->mbc5.rom_bank_high << 8);
gb->mbc_ram_bank = gb->mbc5.ram_bank;
break;
case GB_HUC1:
if (gb->huc1.mode == 0) {
gb->mbc_rom_bank = gb->huc1.bank_low | (gb->mbc1.bank_high << 6);
gb->mbc_ram_bank = 0;
}
else {
gb->mbc_rom_bank = gb->huc1.bank_low;
gb->mbc_ram_bank = gb->huc1.bank_high;
}
break;
case GB_HUC3:
gb->mbc_rom_bank = gb->huc3.rom_bank;
gb->mbc_ram_bank = gb->huc3.ram_bank;
break;
}
}
void GB_configure_cart(GB_gameboy_t *gb)
{
gb->cartridge_type = &GB_cart_defs[gb->rom[0x147]];
if (gb->rom[0x147] == 0 && gb->rom_size > 0x8000) {
GB_log(gb, "ROM header reports no MBC, but file size is over 32Kb. Assuming cartridge uses MBC3.\n");
gb->cartridge_type = &GB_cart_defs[0x11];
}
else if (gb->rom[0x147] != 0 && memcmp(gb->cartridge_type, &GB_cart_defs[0], sizeof(GB_cart_defs[0])) == 0) {
GB_log(gb, "Cartridge type %02x is not yet supported.\n", gb->rom[0x147]);
}
if (gb->cartridge_type->has_ram) {
if (gb->cartridge_type->mbc_type == GB_MBC2) {
gb->mbc_ram_size = 0x200;
}
else {
static const int ram_sizes[256] = {0, 0x800, 0x2000, 0x8000, 0x20000, 0x10000};
gb->mbc_ram_size = ram_sizes[gb->rom[0x149]];
}
gb->mbc_ram = malloc(gb->mbc_ram_size);
/* Todo: Some games assume unintialized MBC RAM is 0xFF. It this true for all cartridges types? */
memset(gb->mbc_ram, 0xFF, gb->mbc_ram_size);
}
/* MBC1 has at least 3 types of wiring (We currently support two (Standard and 4bit-MBC1M) of these).
See http://forums.nesdev.com/viewtopic.php?f=20&t=14099 */
/* Attempt to "guess" wiring */
if (gb->cartridge_type->mbc_type == GB_MBC1) {
if (gb->rom_size >= 0x44000 && memcmp(gb->rom + 0x104, gb->rom + 0x40104, 0x30) == 0) {
gb->mbc1_wiring = GB_MBC1M_WIRING;
}
}
/* Set MBC5's bank to 1 correctly */
if (gb->cartridge_type->mbc_type == GB_MBC5) {
gb->mbc5.rom_bank_low = 1;
}
}

View File

@ -1,32 +0,0 @@
#ifndef MBC_h
#define MBC_h
#include "gb_struct_def.h"
#include <stdbool.h>
typedef struct {
enum {
GB_NO_MBC,
GB_MBC1,
GB_MBC2,
GB_MBC3,
GB_MBC5,
GB_HUC1, /* Todo: HUC1 features are not emulated. Should be unified with the CGB IR sensor API. */
GB_HUC3,
} mbc_type;
enum {
GB_STANDARD_MBC,
GB_CAMERA,
} mbc_subtype;
bool has_ram;
bool has_battery;
bool has_rtc;
bool has_rumble;
} GB_cartridge_t;
#ifdef GB_INTERNAL
extern const GB_cartridge_t GB_cart_defs[256];
void GB_update_mbc_mappings(GB_gameboy_t *gb);
void GB_configure_cart(GB_gameboy_t *gb);
#endif
#endif /* MBC_h */

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +0,0 @@
#ifndef memory_h
#define memory_h
#include "gb_struct_def.h"
#include <stdint.h>
typedef uint8_t (*GB_read_memory_callback_t)(GB_gameboy_t *gb, uint16_t addr, uint8_t data);
void GB_set_read_memory_callback(GB_gameboy_t *gb, GB_read_memory_callback_t callback);
uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr);
void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value);
#ifdef GB_INTERNAL
void GB_dma_run(GB_gameboy_t *gb);
void GB_hdma_run(GB_gameboy_t *gb);
void GB_trigger_oam_bug(GB_gameboy_t *gb, uint16_t address);
void GB_trigger_oam_bug_read_increase(GB_gameboy_t *gb, uint16_t address);
#endif
#endif /* memory_h */

View File

@ -1,216 +0,0 @@
#include "gb.h"
/* TODO: Emulation is VERY basic and assumes the ROM correctly uses the printer's interface.
Incorrect usage is not correctly emulated, as it's not well documented, nor do I
have my own GB Printer to figure it out myself.
It also does not currently emulate communication timeout, which means that a bug
might prevent the printer operation until the GameBoy is restarted.
Also, field mask values are assumed. */
static void handle_command(GB_gameboy_t *gb)
{
switch (gb->printer.command_id) {
case GB_PRINTER_INIT_COMMAND:
gb->printer.status = 0;
gb->printer.image_offset = 0;
break;
case GB_PRINTER_START_COMMAND:
if (gb->printer.command_length == 4) {
gb->printer.status = 6; /* Printing */
uint32_t image[gb->printer.image_offset];
uint8_t palette = gb->printer.command_data[2];
uint32_t colors[4] = {gb->rgb_encode_callback(gb, 0xff, 0xff, 0xff),
gb->rgb_encode_callback(gb, 0xaa, 0xaa, 0xaa),
gb->rgb_encode_callback(gb, 0x55, 0x55, 0x55),
gb->rgb_encode_callback(gb, 0x00, 0x00, 0x00)};
for (unsigned i = 0; i < gb->printer.image_offset; i++) {
image[i] = colors[(palette >> (gb->printer.image[i] * 2)) & 3];
}
if (gb->printer.callback) {
gb->printer.callback(gb, image, gb->printer.image_offset / 160,
gb->printer.command_data[1] >> 4, gb->printer.command_data[1] & 7,
gb->printer.command_data[3] & 0x7F);
}
gb->printer.image_offset = 0;
}
break;
case GB_PRINTER_DATA_COMMAND:
if (gb->printer.command_length == GB_PRINTER_DATA_SIZE) {
gb->printer.image_offset %= sizeof(gb->printer.image);
gb->printer.status = 8; /* Received 0x280 bytes */
uint8_t *byte = gb->printer.command_data;
for (unsigned row = 2; row--; ) {
for (unsigned tile_x = 0; tile_x < 160 / 8; tile_x++) {
for (unsigned y = 0; y < 8; y++, byte += 2) {
for (unsigned x_pixel = 0; x_pixel < 8; x_pixel++) {
gb->printer.image[gb->printer.image_offset + tile_x * 8 + x_pixel + y * 160] =
((*byte) >> 7) | (((*(byte + 1)) >> 7) << 1);
(*byte) <<= 1;
(*(byte + 1)) <<= 1;
}
}
}
gb->printer.image_offset += 8 * 160;
}
}
case GB_PRINTER_NOP_COMMAND:
default:
break;
}
}
static void byte_reieve_completed(GB_gameboy_t *gb, uint8_t byte_received)
{
gb->printer.byte_to_send = 0;
switch (gb->printer.command_state) {
case GB_PRINTER_COMMAND_MAGIC1:
if (byte_received != 0x88) {
return;
}
gb->printer.status &= ~1;
gb->printer.command_length = 0;
gb->printer.checksum = 0;
break;
case GB_PRINTER_COMMAND_MAGIC2:
if (byte_received != 0x33) {
if (byte_received != 0x88) {
gb->printer.command_state = GB_PRINTER_COMMAND_MAGIC1;
}
return;
}
break;
case GB_PRINTER_COMMAND_ID:
gb->printer.command_id = byte_received & 0xF;
break;
case GB_PRINTER_COMMAND_COMPRESSION:
gb->printer.compression = byte_received & 1;
break;
case GB_PRINTER_COMMAND_LENGTH_LOW:
gb->printer.length_left = byte_received;
break;
case GB_PRINTER_COMMAND_LENGTH_HIGH:
gb->printer.length_left |= (byte_received & 3) << 8;
break;
case GB_PRINTER_COMMAND_DATA:
if (gb->printer.command_length != GB_PRINTER_MAX_COMMAND_LENGTH) {
if (gb->printer.compression) {
if (!gb->printer.compression_run_lenth) {
gb->printer.compression_run_is_compressed = byte_received & 0x80;
gb->printer.compression_run_lenth = (byte_received & 0x7F) + 1 + gb->printer.compression_run_is_compressed;
}
else if (gb->printer.compression_run_is_compressed) {
while (gb->printer.compression_run_lenth) {
gb->printer.command_data[gb->printer.command_length++] = byte_received;
gb->printer.compression_run_lenth--;
if (gb->printer.command_length == GB_PRINTER_MAX_COMMAND_LENGTH) {
gb->printer.compression_run_lenth = 0;
}
}
}
else {
gb->printer.command_data[gb->printer.command_length++] = byte_received;
gb->printer.compression_run_lenth--;
}
}
else {
gb->printer.command_data[gb->printer.command_length++] = byte_received;
}
}
gb->printer.length_left--;
break;
case GB_PRINTER_COMMAND_CHECKSUM_LOW:
gb->printer.checksum ^= byte_received;
break;
case GB_PRINTER_COMMAND_CHECKSUM_HIGH:
gb->printer.checksum ^= byte_received << 8;
if (gb->printer.checksum) {
gb->printer.status |= 1; /* Checksum error*/
gb->printer.command_state = GB_PRINTER_COMMAND_MAGIC1;
return;
}
gb->printer.byte_to_send = 0x81;
break;
case GB_PRINTER_COMMAND_ACTIVE:
if ((gb->printer.command_id & 0xF) == GB_PRINTER_INIT_COMMAND) {
/* Games expect INIT commands to return 0? */
gb->printer.byte_to_send = 0;
}
else {
gb->printer.byte_to_send = gb->printer.status;
}
break;
case GB_PRINTER_COMMAND_STATUS:
/* Printing is done instantly, but let the game recieve a 6 (Printing) status at least once, for compatibility */
if (gb->printer.status == 6) {
gb->printer.status = 4; /* Done */
}
gb->printer.command_state = GB_PRINTER_COMMAND_MAGIC1;
handle_command(gb);
return;
}
if (gb->printer.command_state >= GB_PRINTER_COMMAND_ID && gb->printer.command_state < GB_PRINTER_COMMAND_CHECKSUM_LOW) {
gb->printer.checksum += byte_received;
}
if (gb->printer.command_state != GB_PRINTER_COMMAND_DATA) {
gb->printer.command_state++;
}
if (gb->printer.command_state == GB_PRINTER_COMMAND_DATA) {
if (gb->printer.length_left == 0) {
gb->printer.command_state++;
}
}
}
static void serial_start(GB_gameboy_t *gb, bool bit_received)
{
gb->printer.byte_being_recieved <<= 1;
gb->printer.byte_being_recieved |= bit_received;
gb->printer.bits_recieved++;
if (gb->printer.bits_recieved == 8) {
byte_reieve_completed(gb, gb->printer.byte_being_recieved);
gb->printer.bits_recieved = 0;
gb->printer.byte_being_recieved = 0;
}
}
static bool serial_end(GB_gameboy_t *gb)
{
bool ret = gb->printer.bit_to_send;
gb->printer.bit_to_send = gb->printer.byte_to_send & 0x80;
gb->printer.byte_to_send <<= 1;
return ret;
}
void GB_connect_printer(GB_gameboy_t *gb, GB_print_image_callback_t callback)
{
memset(&gb->printer, 0, sizeof(gb->printer));
GB_set_serial_transfer_bit_start_callback(gb, serial_start);
GB_set_serial_transfer_bit_end_callback(gb, serial_end);
gb->printer.callback = callback;
}

View File

@ -1,63 +0,0 @@
#ifndef printer_h
#define printer_h
#include <stdint.h>
#include <stdbool.h>
#include "gb_struct_def.h"
#define GB_PRINTER_MAX_COMMAND_LENGTH 0x280
#define GB_PRINTER_DATA_SIZE 0x280
typedef void (*GB_print_image_callback_t)(GB_gameboy_t *gb,
uint32_t *image,
uint8_t height,
uint8_t top_margin,
uint8_t bottom_margin,
uint8_t exposure);
typedef struct
{
/* Communication state machine */
enum {
GB_PRINTER_COMMAND_MAGIC1,
GB_PRINTER_COMMAND_MAGIC2,
GB_PRINTER_COMMAND_ID,
GB_PRINTER_COMMAND_COMPRESSION,
GB_PRINTER_COMMAND_LENGTH_LOW,
GB_PRINTER_COMMAND_LENGTH_HIGH,
GB_PRINTER_COMMAND_DATA,
GB_PRINTER_COMMAND_CHECKSUM_LOW,
GB_PRINTER_COMMAND_CHECKSUM_HIGH,
GB_PRINTER_COMMAND_ACTIVE,
GB_PRINTER_COMMAND_STATUS,
} command_state : 8;
enum {
GB_PRINTER_INIT_COMMAND = 1,
GB_PRINTER_START_COMMAND = 2,
GB_PRINTER_DATA_COMMAND = 4,
GB_PRINTER_NOP_COMMAND = 0xF,
} command_id : 8;
bool compression;
uint16_t length_left;
uint8_t command_data[GB_PRINTER_MAX_COMMAND_LENGTH];
uint16_t command_length;
uint16_t checksum;
uint8_t status;
uint8_t byte_to_send;
uint8_t image[160 * 200];
uint16_t image_offset;
GB_print_image_callback_t callback;
uint8_t compression_run_lenth;
bool compression_run_is_compressed;
uint8_t bits_recieved;
uint8_t byte_being_recieved;
bool bit_to_send;
} GB_printer_t;
void GB_connect_printer(GB_gameboy_t *gb, GB_print_image_callback_t callback);
#endif

View File

@ -1,38 +0,0 @@
#include "random.h"
#include <time.h>
static uint64_t seed;
static bool enabled = true;
uint8_t GB_random(void)
{
if (!enabled) return 0;
seed *= 0x27BB2EE687B0B0FDL;
seed += 0xB504F32D;
return seed >> 56;
}
uint32_t GB_random32(void)
{
GB_random();
return seed >> 32;
}
void GB_random_seed(uint64_t new_seed)
{
seed = new_seed;
}
void GB_random_set_enabled(bool enable)
{
enabled = enable;
}
static void __attribute__((constructor)) init_seed(void)
{
seed = time(NULL);
for (unsigned i = 64; i--;) {
GB_random();
}
}

View File

@ -1,12 +0,0 @@
#ifndef random_h
#define random_h
#include <stdint.h>
#include <stdbool.h>
uint8_t GB_random(void);
uint32_t GB_random32(void);
void GB_random_seed(uint64_t seed);
void GB_random_set_enabled(bool enable);
#endif /* random_h */

View File

@ -1,208 +0,0 @@
#include "gb.h"
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <math.h>
static uint8_t *state_compress(const uint8_t *prev, const uint8_t *data, size_t uncompressed_size)
{
size_t malloc_size = 0x1000;
uint8_t *compressed = malloc(malloc_size);
size_t counter_pos = 0;
size_t data_pos = sizeof(uint16_t);
bool prev_mode = true;
*(uint16_t *)compressed = 0;
#define COUNTER (*(uint16_t *)&compressed[counter_pos])
#define DATA (compressed[data_pos])
while (uncompressed_size) {
if (prev_mode) {
if (*data == *prev && COUNTER != 0xffff) {
COUNTER++;
data++;
prev++;
uncompressed_size--;
}
else {
prev_mode = false;
counter_pos += sizeof(uint16_t);
data_pos = counter_pos + sizeof(uint16_t);
if (data_pos >= malloc_size) {
malloc_size *= 2;
compressed = realloc(compressed, malloc_size);
}
COUNTER = 0;
}
}
else {
if (*data != *prev && COUNTER != 0xffff) {
COUNTER++;
DATA = *data;
data_pos++;
data++;
prev++;
uncompressed_size--;
if (data_pos >= malloc_size) {
malloc_size *= 2;
compressed = realloc(compressed, malloc_size);
}
}
else {
prev_mode = true;
counter_pos = data_pos;
data_pos = counter_pos + sizeof(uint16_t);
if (counter_pos >= malloc_size - 1) {
malloc_size *= 2;
compressed = realloc(compressed, malloc_size);
}
COUNTER = 0;
}
}
}
return realloc(compressed, data_pos);
#undef DATA
#undef COUNTER
}
static void state_decompress(const uint8_t *prev, uint8_t *data, uint8_t *dest, size_t uncompressed_size)
{
size_t counter_pos = 0;
size_t data_pos = sizeof(uint16_t);
bool prev_mode = true;
#define COUNTER (*(uint16_t *)&data[counter_pos])
#define DATA (data[data_pos])
while (uncompressed_size) {
if (prev_mode) {
if (COUNTER) {
COUNTER--;
*(dest++) = *(prev++);
uncompressed_size--;
}
else {
prev_mode = false;
counter_pos += sizeof(uint16_t);
data_pos = counter_pos + sizeof(uint16_t);
}
}
else {
if (COUNTER) {
COUNTER--;
*(dest++) = DATA;
data_pos++;
prev++;
uncompressed_size--;
}
else {
prev_mode = true;
counter_pos = data_pos;
data_pos += sizeof(uint16_t);
}
}
}
#undef DATA
#undef COUNTER
}
void GB_rewind_push(GB_gameboy_t *gb)
{
const size_t save_size = GB_get_save_state_size(gb);
if (!gb->rewind_sequences) {
if (gb->rewind_buffer_length) {
gb->rewind_sequences = malloc(sizeof(*gb->rewind_sequences) * gb->rewind_buffer_length);
memset(gb->rewind_sequences, 0, sizeof(*gb->rewind_sequences) * gb->rewind_buffer_length);
gb->rewind_pos = 0;
}
else {
return;
}
}
if (gb->rewind_sequences[gb->rewind_pos].pos == GB_REWIND_FRAMES_PER_KEY) {
gb->rewind_pos++;
if (gb->rewind_pos == gb->rewind_buffer_length) {
gb->rewind_pos = 0;
}
if (gb->rewind_sequences[gb->rewind_pos].key_state) {
free(gb->rewind_sequences[gb->rewind_pos].key_state);
gb->rewind_sequences[gb->rewind_pos].key_state = NULL;
}
for (unsigned i = 0; i < GB_REWIND_FRAMES_PER_KEY; i++) {
if (gb->rewind_sequences[gb->rewind_pos].compressed_states[i]) {
free(gb->rewind_sequences[gb->rewind_pos].compressed_states[i]);
gb->rewind_sequences[gb->rewind_pos].compressed_states[i] = 0;
}
}
gb->rewind_sequences[gb->rewind_pos].pos = 0;
}
if (!gb->rewind_sequences[gb->rewind_pos].key_state) {
gb->rewind_sequences[gb->rewind_pos].key_state = malloc(save_size);
GB_save_state_to_buffer(gb, gb->rewind_sequences[gb->rewind_pos].key_state);
}
else {
uint8_t *save_state = malloc(save_size);
GB_save_state_to_buffer(gb, save_state);
gb->rewind_sequences[gb->rewind_pos].compressed_states[gb->rewind_sequences[gb->rewind_pos].pos++] =
state_compress(gb->rewind_sequences[gb->rewind_pos].key_state, save_state, save_size);
free(save_state);
}
}
bool GB_rewind_pop(GB_gameboy_t *gb)
{
if (!gb->rewind_sequences || !gb->rewind_sequences[gb->rewind_pos].key_state) {
return false;
}
const size_t save_size = GB_get_save_state_size(gb);
if (gb->rewind_sequences[gb->rewind_pos].pos == 0) {
GB_load_state_from_buffer(gb, gb->rewind_sequences[gb->rewind_pos].key_state, save_size);
free(gb->rewind_sequences[gb->rewind_pos].key_state);
gb->rewind_sequences[gb->rewind_pos].key_state = NULL;
gb->rewind_pos = gb->rewind_pos == 0? gb->rewind_buffer_length - 1 : gb->rewind_pos - 1;
return true;
}
uint8_t *save_state = malloc(save_size);
state_decompress(gb->rewind_sequences[gb->rewind_pos].key_state,
gb->rewind_sequences[gb->rewind_pos].compressed_states[--gb->rewind_sequences[gb->rewind_pos].pos],
save_state,
save_size);
free(gb->rewind_sequences[gb->rewind_pos].compressed_states[gb->rewind_sequences[gb->rewind_pos].pos]);
gb->rewind_sequences[gb->rewind_pos].compressed_states[gb->rewind_sequences[gb->rewind_pos].pos] = NULL;
GB_load_state_from_buffer(gb, save_state, save_size);
free(save_state);
return true;
}
void GB_rewind_free(GB_gameboy_t *gb)
{
if (!gb->rewind_sequences) return;
for (unsigned i = 0; i < gb->rewind_buffer_length; i++) {
if (gb->rewind_sequences[i].key_state) {
free(gb->rewind_sequences[i].key_state);
}
for (unsigned j = 0; j < GB_REWIND_FRAMES_PER_KEY; j++) {
if (gb->rewind_sequences[i].compressed_states[j]) {
free(gb->rewind_sequences[i].compressed_states[j]);
}
}
}
free(gb->rewind_sequences);
gb->rewind_sequences = NULL;
}
void GB_set_rewind_length(GB_gameboy_t *gb, double seconds)
{
GB_rewind_free(gb);
if (seconds == 0) {
gb->rewind_buffer_length = 0;
}
else {
gb->rewind_buffer_length = (size_t) ceil(seconds * CPU_FREQUENCY / LCDC_PERIOD / GB_REWIND_FRAMES_PER_KEY);
}
}

View File

@ -1,14 +0,0 @@
#ifndef rewind_h
#define rewind_h
#include <stdbool.h>
#include "gb_struct_def.h"
#ifdef GB_INTERNAL
void GB_rewind_push(GB_gameboy_t *gb);
void GB_rewind_free(GB_gameboy_t *gb);
#endif
bool GB_rewind_pop(GB_gameboy_t *gb);
void GB_set_rewind_length(GB_gameboy_t *gb, double seconds);
#endif

View File

@ -1,377 +0,0 @@
#include "gb.h"
#include <stdio.h>
#include <errno.h>
static bool dump_section(FILE *f, const void *src, uint32_t size)
{
if (fwrite(&size, 1, sizeof(size), f) != sizeof(size)) {
return false;
}
if (fwrite(src, 1, size, f) != size) {
return false;
}
return true;
}
#define DUMP_SECTION(gb, f, section) dump_section(f, GB_GET_SECTION(gb, section), GB_SECTION_SIZE(section))
/* Todo: we need a sane and protable save state format. */
int GB_save_state(GB_gameboy_t *gb, const char *path)
{
FILE *f = fopen(path, "wb");
if (!f) {
GB_log(gb, "Could not open save state: %s.\n", strerror(errno));
return errno;
}
if (fwrite(GB_GET_SECTION(gb, header), 1, GB_SECTION_SIZE(header), f) != GB_SECTION_SIZE(header)) goto error;
if (!DUMP_SECTION(gb, f, core_state)) goto error;
if (!DUMP_SECTION(gb, f, dma )) goto error;
if (!DUMP_SECTION(gb, f, mbc )) goto error;
if (!DUMP_SECTION(gb, f, hram )) goto error;
if (!DUMP_SECTION(gb, f, timing )) goto error;
if (!DUMP_SECTION(gb, f, apu )) goto error;
if (!DUMP_SECTION(gb, f, rtc )) goto error;
if (!DUMP_SECTION(gb, f, video )) goto error;
if (GB_is_hle_sgb(gb)) {
if (!dump_section(f, gb->sgb, sizeof(*gb->sgb))) goto error;
}
if (fwrite(gb->mbc_ram, 1, gb->mbc_ram_size, f) != gb->mbc_ram_size) {
goto error;
}
if (fwrite(gb->ram, 1, gb->ram_size, f) != gb->ram_size) {
goto error;
}
if (fwrite(gb->vram, 1, gb->vram_size, f) != gb->vram_size) {
goto error;
}
errno = 0;
error:
fclose(f);
return errno;
}
#undef DUMP_SECTION
size_t GB_get_save_state_size(GB_gameboy_t *gb)
{
return GB_SECTION_SIZE(header)
+ GB_SECTION_SIZE(core_state) + sizeof(uint32_t)
+ GB_SECTION_SIZE(dma ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(mbc ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(hram ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(timing ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(apu ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(rtc ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(video ) + sizeof(uint32_t)
+ (GB_is_hle_sgb(gb)? sizeof(*gb->sgb) + sizeof(uint32_t) : 0)
+ gb->mbc_ram_size
+ gb->ram_size
+ gb->vram_size;
}
/* A write-line function for memory copying */
static void buffer_write(const void *src, size_t size, uint8_t **dest)
{
memcpy(*dest, src, size);
*dest += size;
}
static void buffer_dump_section(uint8_t **buffer, const void *src, uint32_t size)
{
buffer_write(&size, sizeof(size), buffer);
buffer_write(src, size, buffer);
}
#define DUMP_SECTION(gb, buffer, section) buffer_dump_section(&buffer, GB_GET_SECTION(gb, section), GB_SECTION_SIZE(section))
void GB_save_state_to_buffer(GB_gameboy_t *gb, uint8_t *buffer)
{
buffer_write(GB_GET_SECTION(gb, header), GB_SECTION_SIZE(header), &buffer);
DUMP_SECTION(gb, buffer, core_state);
DUMP_SECTION(gb, buffer, dma );
DUMP_SECTION(gb, buffer, mbc );
DUMP_SECTION(gb, buffer, hram );
DUMP_SECTION(gb, buffer, timing );
DUMP_SECTION(gb, buffer, apu );
DUMP_SECTION(gb, buffer, rtc );
DUMP_SECTION(gb, buffer, video );
if (GB_is_hle_sgb(gb)) {
buffer_dump_section(&buffer, gb->sgb, sizeof(*gb->sgb));
}
buffer_write(gb->mbc_ram, gb->mbc_ram_size, &buffer);
buffer_write(gb->ram, gb->ram_size, &buffer);
buffer_write(gb->vram, gb->vram_size, &buffer);
}
/* Best-effort read function for maximum future compatibility. */
static bool read_section(FILE *f, void *dest, uint32_t size)
{
uint32_t saved_size = 0;
if (fread(&saved_size, 1, sizeof(size), f) != sizeof(size)) {
return false;
}
if (saved_size <= size) {
if (fread(dest, 1, saved_size, f) != saved_size) {
return false;
}
}
else {
if (fread(dest, 1, size, f) != size) {
return false;
}
fseek(f, saved_size - size, SEEK_CUR);
}
return true;
}
#undef DUMP_SECTION
static bool verify_state_compatibility(GB_gameboy_t *gb, GB_gameboy_t *save)
{
if (gb->magic != save->magic) {
GB_log(gb, "The file is not a save state, or is from an incompatible operating system.\n");
return false;
}
if (gb->version != save->version) {
GB_log(gb, "The save state is for a different version of SameBoy.\n");
return false;
}
if (gb->mbc_ram_size < save->mbc_ram_size) {
GB_log(gb, "The save state has non-matching MBC RAM size.\n");
return false;
}
if (gb->vram_size != save->vram_size) {
GB_log(gb, "The save state has non-matching VRAM size. Try changing the emulated model.\n");
return false;
}
if (GB_is_hle_sgb(gb) != GB_is_hle_sgb(save)) {
GB_log(gb, "The save state is %sfor a Super Game Boy. Try changing the emulated model.\n", GB_is_hle_sgb(save)? "" : "not ");
return false;
}
if (gb->ram_size != save->ram_size) {
if (gb->ram_size == 0x1000 * 8 && save->ram_size == 0x2000 * 8) {
/* A bug in versions prior to 0.12 made CGB instances allocate twice the ammount of RAM.
Ignore this issue to retain compatibility with older, 0.11, save states. */
}
else {
GB_log(gb, "The save state has non-matching RAM size. Try changing the emulated model.\n");
return false;
}
}
return true;
}
#define READ_SECTION(gb, f, section) read_section(f, GB_GET_SECTION(gb, section), GB_SECTION_SIZE(section))
int GB_load_state(GB_gameboy_t *gb, const char *path)
{
GB_gameboy_t save;
/* Every unread value should be kept the same. */
memcpy(&save, gb, sizeof(save));
/* ...Except ram size, we use it to detect old saves with incorrect ram sizes */
save.ram_size = 0;
FILE *f = fopen(path, "rb");
if (!f) {
GB_log(gb, "Could not open save state: %s.\n", strerror(errno));
return errno;
}
if (fread(GB_GET_SECTION(&save, header), 1, GB_SECTION_SIZE(header), f) != GB_SECTION_SIZE(header)) goto error;
if (!READ_SECTION(&save, f, core_state)) goto error;
if (!READ_SECTION(&save, f, dma )) goto error;
if (!READ_SECTION(&save, f, mbc )) goto error;
if (!READ_SECTION(&save, f, hram )) goto error;
if (!READ_SECTION(&save, f, timing )) goto error;
if (!READ_SECTION(&save, f, apu )) goto error;
if (!READ_SECTION(&save, f, rtc )) goto error;
if (!READ_SECTION(&save, f, video )) goto error;
if (save.ram_size == 0) {
/* Save doesn't have ram size specified, it's a pre 0.12 save state with potentially
incorrect RAM amount if it's a CGB instance */
if (GB_is_cgb(&save)) {
save.ram_size = 0x2000 * 8; // Incorrect RAM size
}
else {
save.ram_size = gb->ram_size;
}
}
if (!verify_state_compatibility(gb, &save)) {
errno = -1;
goto error;
}
if (GB_is_hle_sgb(gb)) {
if (!read_section(f, gb->sgb, sizeof(*gb->sgb))) goto error;
}
memset(gb->mbc_ram + save.mbc_ram_size, 0xFF, gb->mbc_ram_size - save.mbc_ram_size);
if (fread(gb->mbc_ram, 1, save.mbc_ram_size, f) != save.mbc_ram_size) {
fclose(f);
return EIO;
}
if (fread(gb->ram, 1, gb->ram_size, f) != gb->ram_size) {
fclose(f);
return EIO;
}
/* Fix for 0.11 save states that allocate twice the amount of RAM in CGB instances */
fseek(f, save.ram_size - gb->ram_size, SEEK_CUR);
if (fread(gb->vram, 1, gb->vram_size, f) != gb->vram_size) {
fclose(f);
return EIO;
}
size_t orig_ram_size = gb->ram_size;
memcpy(gb, &save, sizeof(save));
gb->ram_size = orig_ram_size;
errno = 0;
if (gb->cartridge_type->has_rumble && gb->rumble_callback) {
gb->rumble_callback(gb, gb->rumble_state);
}
for (unsigned i = 0; i < 32; i++) {
GB_palette_changed(gb, false, i * 2);
GB_palette_changed(gb, true, i * 2);
}
gb->bg_fifo.read_end &= 0xF;
gb->bg_fifo.write_end &= 0xF;
gb->oam_fifo.read_end &= 0xF;
gb->oam_fifo.write_end &= 0xF;
error:
fclose(f);
return errno;
}
#undef READ_SECTION
/* An read-like function for buffer-copying */
static size_t buffer_read(void *dest, size_t length, const uint8_t **buffer, size_t *buffer_length)
{
if (length > *buffer_length) {
length = *buffer_length;
}
memcpy(dest, *buffer, length);
*buffer += length;
*buffer_length -= length;
return length;
}
static bool buffer_read_section(const uint8_t **buffer, size_t *buffer_length, void *dest, uint32_t size)
{
uint32_t saved_size = 0;
if (buffer_read(&saved_size, sizeof(size), buffer, buffer_length) != sizeof(size)) {
return false;
}
if (saved_size > *buffer_length) return false;
if (saved_size <= size) {
if (buffer_read(dest, saved_size, buffer, buffer_length) != saved_size) {
return false;
}
}
else {
if (buffer_read(dest, size, buffer, buffer_length) != size) {
return false;
}
*buffer += saved_size - size;
*buffer_length -= saved_size - size;
}
return true;
}
#define READ_SECTION(gb, buffer, length, section) buffer_read_section(&buffer, &length, GB_GET_SECTION(gb, section), GB_SECTION_SIZE(section))
int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t length)
{
GB_gameboy_t save;
/* Every unread value should be kept the same. */
memcpy(&save, gb, sizeof(save));
if (buffer_read(GB_GET_SECTION(&save, header), GB_SECTION_SIZE(header), &buffer, &length) != GB_SECTION_SIZE(header)) return -1;
if (!READ_SECTION(&save, buffer, length, core_state)) return -1;
if (!READ_SECTION(&save, buffer, length, dma )) return -1;
if (!READ_SECTION(&save, buffer, length, mbc )) return -1;
if (!READ_SECTION(&save, buffer, length, hram )) return -1;
if (!READ_SECTION(&save, buffer, length, timing )) return -1;
if (!READ_SECTION(&save, buffer, length, apu )) return -1;
if (!READ_SECTION(&save, buffer, length, rtc )) return -1;
if (!READ_SECTION(&save, buffer, length, video )) return -1;
if (!verify_state_compatibility(gb, &save)) {
return -1;
}
if (GB_is_hle_sgb(gb)) {
if (!buffer_read_section(&buffer, &length, gb->sgb, sizeof(*gb->sgb))) return -1;
}
memset(gb->mbc_ram + save.mbc_ram_size, 0xFF, gb->mbc_ram_size - save.mbc_ram_size);
if (buffer_read(gb->mbc_ram, save.mbc_ram_size, &buffer, &length) != save.mbc_ram_size) {
return -1;
}
if (buffer_read(gb->ram, gb->ram_size, &buffer, &length) != gb->ram_size) {
return -1;
}
if (buffer_read(gb->vram,gb->vram_size, &buffer, &length) != gb->vram_size) {
return -1;
}
/* Fix for 0.11 save states that allocate twice the amount of RAM in CGB instances */
buffer += save.ram_size - gb->ram_size;
length -= save.ram_size - gb->ram_size;
memcpy(gb, &save, sizeof(save));
if (gb->cartridge_type->has_rumble && gb->rumble_callback) {
gb->rumble_callback(gb, gb->rumble_state);
}
for (unsigned i = 0; i < 32; i++) {
GB_palette_changed(gb, false, i * 2);
GB_palette_changed(gb, true, i * 2);
}
gb->bg_fifo.read_end &= 0xF;
gb->bg_fifo.write_end &= 0xF;
gb->oam_fifo.read_end &= 0xF;
gb->oam_fifo.write_end &= 0xF;
return 0;
}
#undef READ_SECTION

View File

@ -1,24 +0,0 @@
/* Macros to make the GB_gameboy_t struct more future compatible when state saving */
#ifndef save_state_h
#define save_state_h
#include <stddef.h>
#define GB_PADDING(type, old_usage) type old_usage##__do_not_use
#define GB_SECTION(name, ...) __attribute__ ((aligned (8))) struct {} name##_section_start; __VA_ARGS__; struct {} name##_section_end
#define GB_SECTION_OFFSET(name) (offsetof(GB_gameboy_t, name##_section_start))
#define GB_SECTION_SIZE(name) (offsetof(GB_gameboy_t, name##_section_end) - offsetof(GB_gameboy_t, name##_section_start))
#define GB_GET_SECTION(gb, name) ((void*)&((gb)->name##_section_start))
#define GB_aligned_double __attribute__ ((aligned (8))) double
/* Public calls related to save states */
int GB_save_state(GB_gameboy_t *gb, const char *path);
size_t GB_get_save_state_size(GB_gameboy_t *gb);
/* Assumes buffer is big enough to contain the save state. Use with GB_get_save_state_size(). */
void GB_save_state_to_buffer(GB_gameboy_t *gb, uint8_t *buffer);
int GB_load_state(GB_gameboy_t *gb, const char *path);
int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t length);
#endif /* save_state_h */

View File

@ -1,846 +0,0 @@
#include "gb.h"
#include "random.h"
#include <math.h>
#include <assert.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#define INTRO_ANIMATION_LENGTH 200
enum {
PAL01 = 0x00,
PAL23 = 0x01,
PAL03 = 0x02,
PAL12 = 0x03,
ATTR_BLK = 0x04,
ATTR_LIN = 0x05,
ATTR_DIV = 0x06,
PAL_SET = 0x0A,
PAL_TRN = 0x0B,
DATA_SND = 0x0F,
MLT_REQ = 0x11,
CHR_TRN = 0x13,
PCT_TRN = 0x14,
ATTR_TRN = 0x15,
ATTR_SET = 0x16,
MASK_EN = 0x17,
};
typedef enum {
MASK_DISABLED,
MASK_FREEZE,
MASK_BLACK,
MASK_COLOR_0,
} mask_mode_t;
typedef enum {
TRANSFER_LOW_TILES,
TRANSFER_HIGH_TILES,
TRANSFER_BORDER_DATA,
TRANSFER_PALETTES,
TRANSFER_ATTRIBUTES,
} transfer_dest_t;
#define SGB_PACKET_SIZE 16
static inline void pal_command(GB_gameboy_t *gb, unsigned first, unsigned second)
{
gb->sgb->effective_palettes[0] = gb->sgb->effective_palettes[4] =
gb->sgb->effective_palettes[8] = gb->sgb->effective_palettes[12] =
gb->sgb->command[1] | (gb->sgb->command[2] << 8);
for (unsigned i = 0; i < 3; i++) {
gb->sgb->effective_palettes[first * 4 + i + 1] = gb->sgb->command[3 + i * 2] | (gb->sgb->command[4 + i * 2] << 8);
}
for (unsigned i = 0; i < 3; i++) {
gb->sgb->effective_palettes[second * 4 + i + 1] = gb->sgb->command[9 + i * 2] | (gb->sgb->command[10 + i * 2] << 8);
}
}
static inline void load_attribute_file(GB_gameboy_t *gb, unsigned file_index)
{
if (file_index > 0x2C) return;
uint8_t *output = gb->sgb->attribute_map;
for (unsigned i = 0; i < 90; i++) {
uint8_t byte = gb->sgb->attribute_files[file_index * 90 + i];
for (unsigned j = 4; j--;) {
*(output++) = byte >> 6;
byte <<= 2;
}
}
}
static const uint16_t built_in_palettes[] =
{
0x67BF, 0x265B, 0x10B5, 0x2866,
0x637B, 0x3AD9, 0x0956, 0x0000,
0x7F1F, 0x2A7D, 0x30F3, 0x4CE7,
0x57FF, 0x2618, 0x001F, 0x006A,
0x5B7F, 0x3F0F, 0x222D, 0x10EB,
0x7FBB, 0x2A3C, 0x0015, 0x0900,
0x2800, 0x7680, 0x01EF, 0x2FFF,
0x73BF, 0x46FF, 0x0110, 0x0066,
0x533E, 0x2638, 0x01E5, 0x0000,
0x7FFF, 0x2BBF, 0x00DF, 0x2C0A,
0x7F1F, 0x463D, 0x74CF, 0x4CA5,
0x53FF, 0x03E0, 0x00DF, 0x2800,
0x433F, 0x72D2, 0x3045, 0x0822,
0x7FFA, 0x2A5F, 0x0014, 0x0003,
0x1EED, 0x215C, 0x42FC, 0x0060,
0x7FFF, 0x5EF7, 0x39CE, 0x0000,
0x4F5F, 0x630E, 0x159F, 0x3126,
0x637B, 0x121C, 0x0140, 0x0840,
0x66BC, 0x3FFF, 0x7EE0, 0x2C84,
0x5FFE, 0x3EBC, 0x0321, 0x0000,
0x63FF, 0x36DC, 0x11F6, 0x392A,
0x65EF, 0x7DBF, 0x035F, 0x2108,
0x2B6C, 0x7FFF, 0x1CD9, 0x0007,
0x53FC, 0x1F2F, 0x0E29, 0x0061,
0x36BE, 0x7EAF, 0x681A, 0x3C00,
0x7BBE, 0x329D, 0x1DE8, 0x0423,
0x739F, 0x6A9B, 0x7293, 0x0001,
0x5FFF, 0x6732, 0x3DA9, 0x2481,
0x577F, 0x3EBC, 0x456F, 0x1880,
0x6B57, 0x6E1B, 0x5010, 0x0007,
0x0F96, 0x2C97, 0x0045, 0x3200,
0x67FF, 0x2F17, 0x2230, 0x1548,
};
static const struct {
char name[16];
unsigned palette_index;
} palette_assignments[] =
{
{"ZELDA", 5},
{"SUPER MARIOLAND", 6},
{"MARIOLAND2", 0x14},
{"SUPERMARIOLAND3", 2},
{"KIRBY DREAM LAND", 0xB},
{"HOSHINOKA-BI", 0xB},
{"KIRBY'S PINBALL", 3},
{"YOSSY NO TAMAGO", 0xC},
{"MARIO & YOSHI", 0xC},
{"YOSSY NO COOKIE", 4},
{"YOSHI'S COOKIE", 4},
{"DR.MARIO", 0x12},
{"TETRIS", 0x11},
{"YAKUMAN", 0x13},
{"METROID2", 0x1F},
{"KAERUNOTAMENI", 9},
{"GOLF", 0x18},
{"ALLEY WAY", 0x16},
{"BASEBALL", 0xF},
{"TENNIS", 0x17},
{"F1RACE", 0x1E},
{"KID ICARUS", 0xE},
{"QIX", 0x19},
{"SOLARSTRIKER", 7},
{"X", 0x1C},
{"GBWARS", 0x15},
};
static void command_ready(GB_gameboy_t *gb)
{
/* SGB header commands are used to send the contents of the header to the SNES CPU.
A header command looks like this:
Command ID: 0b1111xxx1, where xxx is the packet index. (e.g. F1 for [0x104, 0x112), F3 for [0x112, 0x120))
Checksum: Simple one byte sum for the following content bytes
0xE content bytes. The last command, FB, is padded with zeros, so information past the header is not sent. */
if ((gb->sgb->command[0] & 0xF1) == 0xF1) {
if(gb->boot_rom_finished) return;
uint8_t checksum = 0;
for (unsigned i = 2; i < 0x10; i++) {
checksum += gb->sgb->command[i];
}
if (checksum != gb->sgb->command[1]) {
GB_log(gb, "Failed checksum for SGB header command, disabling SGB features\n");
gb->sgb->disable_commands = true;
return;
}
unsigned index = (gb->sgb->command[0] >> 1) & 7;
if (index > 5) {
return;
}
memcpy(&gb->sgb->received_header[index * 14], &gb->sgb->command[2], 14);
if (gb->sgb->command[0] == 0xfb) {
if (gb->sgb->received_header[0x42] != 3 || gb->sgb->received_header[0x47] != 0x33) {
gb->sgb->disable_commands = true;
for (unsigned i = 0; i < sizeof(palette_assignments) / sizeof(palette_assignments[0]); i++) {
if (memcmp(palette_assignments[i].name, &gb->sgb->received_header[0x30], sizeof(palette_assignments[i].name)) == 0) {
gb->sgb->effective_palettes[0] = built_in_palettes[palette_assignments[i].palette_index * 4 - 4];
gb->sgb->effective_palettes[1] = built_in_palettes[palette_assignments[i].palette_index * 4 + 1 - 4];
gb->sgb->effective_palettes[2] = built_in_palettes[palette_assignments[i].palette_index * 4 + 2 - 4];
gb->sgb->effective_palettes[3] = built_in_palettes[palette_assignments[i].palette_index * 4 + 3 - 4];
break;
}
}
}
}
return;
}
/* Ignore malformed commands (0 length)*/
if ((gb->sgb->command[0] & 7) == 0) return;
switch (gb->sgb->command[0] >> 3) {
case PAL01:
pal_command(gb, 0, 1);
break;
case PAL23:
pal_command(gb, 2, 3);
break;
case PAL03:
pal_command(gb, 0, 3);
break;
case PAL12:
pal_command(gb, 1, 2);
break;
case ATTR_BLK: {
struct {
uint8_t count;
struct {
uint8_t control;
uint8_t palettes;
uint8_t left, top, right, bottom;
} data[];
} *command = (void *)(gb->sgb->command + 1);
if (command->count > 0x12) return;
for (unsigned i = 0; i < command->count; i++) {
bool inside = command->data[i].control & 1;
bool middle = command->data[i].control & 2;
bool outside = command->data[i].control & 4;
uint8_t inside_palette = command->data[i].palettes & 0x3;
uint8_t middle_palette = (command->data[i].palettes >> 2) & 0x3;
uint8_t outside_palette = (command->data[i].palettes >> 4) & 0x3;
if (inside && !middle && !outside) {
middle = true;
middle_palette = inside_palette;
}
else if (outside && !middle && !inside) {
middle = true;
middle_palette = outside_palette;
}
command->data[i].left &= 0x1F;
command->data[i].top &= 0x1F;
command->data[i].right &= 0x1F;
command->data[i].bottom &= 0x1F;
for (unsigned y = 0; y < 18; y++) {
for (unsigned x = 0; x < 20; x++) {
if (x < command->data[i].left || x > command->data[i].right ||
y < command->data[i].top || y > command->data[i].bottom) {
if (outside) {
gb->sgb->attribute_map[x + 20 * y] = outside_palette;
}
}
else if (x > command->data[i].left && x < command->data[i].right &&
y > command->data[i].top && y < command->data[i].bottom) {
if (inside) {
gb->sgb->attribute_map[x + 20 * y] = inside_palette;
}
}
else if(middle) {
gb->sgb->attribute_map[x + 20 * y] = middle_palette;
}
}
}
}
break;
}
case ATTR_LIN: {
struct {
uint8_t count;
uint8_t data[];
} *command = (void *)(gb->sgb->command + 1);
if (command->count > sizeof(gb->sgb->command) - 2) return;
for (unsigned i = 0; i < command->count; i++) {
bool horizontal = command->data[i] & 0x80;
uint8_t palette = (command->data[i] >> 5) & 0x3;
uint8_t line = (command->data[i]) & 0x1F;
if (horizontal) {
if (line > 18) continue;
for (unsigned x = 0; x < 20; x++) {
gb->sgb->attribute_map[x + 20 * line] = palette;
}
}
else {
if (line > 20) continue;
for (unsigned y = 0; y < 18; y++) {
gb->sgb->attribute_map[line + 20 * y] = palette;
}
}
}
break;
}
case ATTR_DIV: {
uint8_t high_palette = gb->sgb->command[1] & 3;
uint8_t low_palette = (gb->sgb->command[1] >> 2) & 3;
uint8_t middle_palette = (gb->sgb->command[1] >> 4) & 3;
bool horizontal = gb->sgb->command[1] & 0x40;
uint8_t line = gb->sgb->command[2] & 0x1F;
for (unsigned y = 0; y < 18; y++) {
for (unsigned x = 0; x < 20; x++) {
if ((horizontal? y : x) < line) {
gb->sgb->attribute_map[x + 20 * y] = low_palette;
}
else if ((horizontal? y : x) == line) {
gb->sgb->attribute_map[x + 20 * y] = middle_palette;
}
else {
gb->sgb->attribute_map[x + 20 * y] = high_palette;
}
}
}
break;
}
case PAL_SET:
memcpy(&gb->sgb->effective_palettes[0],
&gb->sgb->ram_palettes[4 * (gb->sgb->command[1] + (gb->sgb->command[2] & 1) * 0x100)],
8);
memcpy(&gb->sgb->effective_palettes[4],
&gb->sgb->ram_palettes[4 * (gb->sgb->command[3] + (gb->sgb->command[4] & 1) * 0x100)],
8);
memcpy(&gb->sgb->effective_palettes[8],
&gb->sgb->ram_palettes[4 * (gb->sgb->command[5] + (gb->sgb->command[6] & 1) * 0x100)],
8);
memcpy(&gb->sgb->effective_palettes[12],
&gb->sgb->ram_palettes[4 * (gb->sgb->command[7] + (gb->sgb->command[8] & 1) * 0x100)],
8);
gb->sgb->effective_palettes[12] = gb->sgb->effective_palettes[8] =
gb->sgb->effective_palettes[4] = gb->sgb->effective_palettes[0];
if (gb->sgb->command[9] & 0x80) {
load_attribute_file(gb, gb->sgb->command[9] & 0x3F);
}
if (gb->sgb->command[9] & 0x40) {
gb->sgb->mask_mode = MASK_DISABLED;
}
break;
case PAL_TRN:
gb->sgb->vram_transfer_countdown = 2;
gb->sgb->transfer_dest = TRANSFER_PALETTES;
break;
case DATA_SND:
// Not supported, but used by almost all SGB games for hot patching, so let's mute the warning for this
break;
case MLT_REQ:
if (gb->sgb->player_count == 1) {
gb->sgb->current_player = 0;
}
gb->sgb->player_count = (gb->sgb->command[1] & 3) + 1; /* Todo: When breaking save state comaptibility,
fix this to be 0 based. */
if (gb->sgb->player_count == 3) {
gb->sgb->current_player++;
}
gb->sgb->mlt_lock = true;
break;
case CHR_TRN:
gb->sgb->vram_transfer_countdown = 2;
gb->sgb->transfer_dest = (gb->sgb->command[1] & 1)? TRANSFER_HIGH_TILES : TRANSFER_LOW_TILES;
break;
case PCT_TRN:
gb->sgb->vram_transfer_countdown = 2;
gb->sgb->transfer_dest = TRANSFER_BORDER_DATA;
break;
case ATTR_TRN:
gb->sgb->vram_transfer_countdown = 2;
gb->sgb->transfer_dest = TRANSFER_ATTRIBUTES;
break;
case ATTR_SET:
load_attribute_file(gb, gb->sgb->command[0] & 0x3F);
if (gb->sgb->command[0] & 0x40) {
gb->sgb->mask_mode = MASK_DISABLED;
}
break;
case MASK_EN:
gb->sgb->mask_mode = gb->sgb->command[1] & 3;
break;
default:
if ((gb->sgb->command[0] >> 3) == 8 &&
(gb->sgb->command[1] & ~0x80) == 0 &&
(gb->sgb->command[2] & ~0x80) == 0) {
/* Mute/dummy sound commands, ignore this command as it's used by many games at startup */
break;
}
GB_log(gb, "Unimplemented SGB command %x: ", gb->sgb->command[0] >> 3);
for (unsigned i = 0; i < gb->sgb->command_write_index / 8; i++) {
GB_log(gb, "%02x ", gb->sgb->command[i]);
}
GB_log(gb, "\n");
}
}
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value)
{
if (gb->joyp_write_callback) {
gb->joyp_write_callback(gb, value);
}
if (!GB_is_sgb(gb)) return;
if (!GB_is_hle_sgb(gb)) {
/* Notify via callback */
return;
}
if (gb->sgb->disable_commands) return;
if (gb->sgb->command_write_index >= sizeof(gb->sgb->command) * 8) {
return;
}
uint16_t command_size = (gb->sgb->command[0] & 7 ?: 1) * SGB_PACKET_SIZE * 8;
if ((gb->sgb->command[0] & 0xF1) == 0xF1) {
command_size = SGB_PACKET_SIZE * 8;
}
if ((value & 0x20) == 0 && (gb->io_registers[GB_IO_JOYP] & 0x20) != 0) {
gb->sgb->mlt_lock ^= true;
}
switch ((value >> 4) & 3) {
case 3:
gb->sgb->ready_for_pulse = true;
if ((gb->sgb->player_count & 1) == 0 && !gb->sgb->mlt_lock) {
gb->sgb->current_player++;
gb->sgb->current_player &= 3;
gb->sgb->mlt_lock = true;
}
break;
case 2: // Zero
if (!gb->sgb->ready_for_pulse || !gb->sgb->ready_for_write) return;
if (gb->sgb->ready_for_stop) {
if (gb->sgb->command_write_index == command_size) {
command_ready(gb);
gb->sgb->command_write_index = 0;
memset(gb->sgb->command, 0, sizeof(gb->sgb->command));
}
gb->sgb->ready_for_pulse = false;
gb->sgb->ready_for_write = false;
gb->sgb->ready_for_stop = false;
}
else {
gb->sgb->command_write_index++;
gb->sgb->ready_for_pulse = false;
if (((gb->sgb->command_write_index) & (SGB_PACKET_SIZE * 8 - 1)) == 0) {
gb->sgb->ready_for_stop = true;
}
}
break;
case 1: // One
if (!gb->sgb->ready_for_pulse || !gb->sgb->ready_for_write) return;
if (gb->sgb->ready_for_stop) {
GB_log(gb, "Corrupt SGB command.\n");
gb->sgb->ready_for_pulse = false;
gb->sgb->ready_for_write = false;
gb->sgb->command_write_index = 0;
memset(gb->sgb->command, 0, sizeof(gb->sgb->command));
}
else {
gb->sgb->command[gb->sgb->command_write_index / 8] |= 1 << (gb->sgb->command_write_index & 7);
gb->sgb->command_write_index++;
gb->sgb->ready_for_pulse = false;
if (((gb->sgb->command_write_index) & (SGB_PACKET_SIZE * 8 - 1)) == 0) {
gb->sgb->ready_for_stop = true;
}
}
break;
case 0:
if (!gb->sgb->ready_for_pulse) return;
gb->sgb->ready_for_write = true;
gb->sgb->ready_for_pulse = false;
if (((gb->sgb->command_write_index) & (SGB_PACKET_SIZE * 8 - 1)) != 0 ||
gb->sgb->command_write_index == 0 ||
gb->sgb->ready_for_stop) {
gb->sgb->command_write_index = 0;
memset(gb->sgb->command, 0, sizeof(gb->sgb->command));
gb->sgb->ready_for_stop = false;
}
break;
default:
break;
}
}
static uint32_t convert_rgb15(GB_gameboy_t *gb, uint16_t color)
{
return GB_convert_rgb15(gb, color);
}
static uint32_t convert_rgb15_with_fade(GB_gameboy_t *gb, uint16_t color, uint8_t fade)
{
uint8_t r = ((color) & 0x1F) - fade;
uint8_t g = ((color >> 5) & 0x1F) - fade;
uint8_t b = ((color >> 10) & 0x1F) - fade;
if (r >= 0x20) r = 0;
if (g >= 0x20) g = 0;
if (b >= 0x20) b = 0;
color = r | (g << 5) | (b << 10);
return GB_convert_rgb15(gb, color);
}
#include <stdio.h>
static void render_boot_animation (GB_gameboy_t *gb)
{
#include "sgb_animation_logo.inc"
uint32_t *output = &gb->screen[48 + 40 * 256];
uint8_t *input = animation_logo;
unsigned fade_blue = 0;
unsigned fade_red = 0;
if (gb->sgb->intro_animation < 80 - 32) {
fade_blue = 32;
}
else if (gb->sgb->intro_animation < 80) {
fade_blue = 80 - gb->sgb->intro_animation;
}
else if (gb->sgb->intro_animation > INTRO_ANIMATION_LENGTH - 32) {
fade_red = fade_blue = gb->sgb->intro_animation - INTRO_ANIMATION_LENGTH + 32;
}
uint32_t colors[] = {
convert_rgb15(gb, 0),
convert_rgb15_with_fade(gb, 0x14A5, fade_blue),
convert_rgb15_with_fade(gb, 0x54E0, fade_blue),
convert_rgb15_with_fade(gb, 0x0019, fade_red),
convert_rgb15(gb, 0x0011),
convert_rgb15(gb, 0x0009),
};
unsigned y_min = (144 - animation_logo_height) / 2;
unsigned y_max = y_min + animation_logo_height;
for (unsigned y = 0; y < 144; y++) {
for (unsigned x = 0; x < 160; x++) {
if (y < y_min || y >= y_max) {
*(output++) = colors[0];
}
else {
uint8_t color = *input;
if (color >= 3) {
if (color == gb->sgb->intro_animation / 2 - 3) {
color = 5;
}
else if (color == gb->sgb->intro_animation / 2 - 4) {
color = 4;
}
else if (color < gb->sgb->intro_animation / 2 - 4) {
color = 3;
}
else {
color = 0;
}
}
*(output++) = colors[color];
input++;
}
}
output += 256 - 160;
}
}
static void render_jingle(GB_gameboy_t *gb, size_t count);
void GB_sgb_render(GB_gameboy_t *gb)
{
if (gb->apu_output.sample_rate) {
render_jingle(gb, gb->apu_output.sample_rate / GB_get_usual_frame_rate(gb));
}
if (gb->sgb->intro_animation < INTRO_ANIMATION_LENGTH) gb->sgb->intro_animation++;
if (!gb->screen || !gb->rgb_encode_callback) return;
if (gb->sgb->mask_mode != MASK_FREEZE) {
memcpy(gb->sgb->effective_screen_buffer,
gb->sgb->screen_buffer,
sizeof(gb->sgb->effective_screen_buffer));
}
if (gb->sgb->vram_transfer_countdown) {
if (--gb->sgb->vram_transfer_countdown == 0) {
if (gb->sgb->transfer_dest == TRANSFER_LOW_TILES || gb->sgb->transfer_dest == TRANSFER_HIGH_TILES) {
uint8_t *base = &gb->sgb->pending_border.tiles[gb->sgb->transfer_dest == TRANSFER_HIGH_TILES ? 0x80 * 8 * 8 : 0];
for (unsigned tile = 0; tile < 0x80; tile++) {
unsigned tile_x = (tile % 10) * 16;
unsigned tile_y = (tile / 10) * 8;
for (unsigned y = 0; y < 0x8; y++) {
for (unsigned x = 0; x < 0x8; x++) {
base[tile * 8 * 8 + y * 8 + x] = gb->sgb->screen_buffer[(tile_x + x) + (tile_y + y) * 160] +
gb->sgb->screen_buffer[(tile_x + x + 8) + (tile_y + y) * 160] * 4;
}
}
}
}
else {
unsigned size = 0;
uint16_t *data = NULL;
switch (gb->sgb->transfer_dest) {
case TRANSFER_PALETTES:
size = 0x100;
data = gb->sgb->ram_palettes;
break;
case TRANSFER_BORDER_DATA:
size = 0x88;
data = gb->sgb->pending_border.raw_data;
break;
case TRANSFER_ATTRIBUTES:
size = 0xFE;
data = (uint16_t *)gb->sgb->attribute_files;
break;
default:
return; // Corrupt state?
}
for (unsigned tile = 0; tile < size; tile++) {
unsigned tile_x = (tile % 20) * 8;
unsigned tile_y = (tile / 20) * 8;
for (unsigned y = 0; y < 0x8; y++) {
static const uint16_t pixel_to_bits[4] = {0x0000, 0x0080, 0x8000, 0x8080};
*data = 0;
for (unsigned x = 0; x < 8; x++) {
*data |= pixel_to_bits[gb->sgb->screen_buffer[(tile_x + x) + (tile_y + y) * 160] & 3] >> x;
}
#ifdef GB_BIG_ENDIAN
if (gb->sgb->transfer_dest == TRANSFER_ATTRIBUTES) {
*data = __builtin_bswap16(*data);
}
#endif
data++;
}
}
if (gb->sgb->transfer_dest == TRANSFER_BORDER_DATA) {
gb->sgb->border_animation = 64;
}
}
}
}
uint32_t colors[4 * 4];
for (unsigned i = 0; i < 4 * 4; i++) {
colors[i] = convert_rgb15(gb, gb->sgb->effective_palettes[i]);
}
if (gb->sgb->intro_animation < INTRO_ANIMATION_LENGTH) {
render_boot_animation(gb);
}
else {
uint32_t *output = &gb->screen[48 + 40 * 256];
uint8_t *input = gb->sgb->effective_screen_buffer;
switch ((mask_mode_t) gb->sgb->mask_mode) {
case MASK_DISABLED:
case MASK_FREEZE: {
for (unsigned y = 0; y < 144; y++) {
for (unsigned x = 0; x < 160; x++) {
uint8_t palette = gb->sgb->attribute_map[x / 8 + y / 8 * 20] & 3;
*(output++) = colors[(*(input++) & 3) + palette * 4];
}
output += 256 - 160;
}
break;
}
case MASK_BLACK:
{
uint32_t black = convert_rgb15(gb, 0);
for (unsigned y = 0; y < 144; y++) {
for (unsigned x = 0; x < 160; x++) {
*(output++) = black;
}
output += 256 - 160;
}
break;
}
case MASK_COLOR_0:
{
for (unsigned y = 0; y < 144; y++) {
for (unsigned x = 0; x < 160; x++) {
*(output++) = colors[0];
}
output += 256 - 160;
}
break;
}
}
}
uint32_t border_colors[16 * 4];
if (gb->sgb->border_animation == 0 || gb->sgb->intro_animation < INTRO_ANIMATION_LENGTH) {
for (unsigned i = 0; i < 16 * 4; i++) {
border_colors[i] = convert_rgb15(gb, gb->sgb->border.palette[i]);
}
}
else if (gb->sgb->border_animation > 32) {
gb->sgb->border_animation--;
for (unsigned i = 0; i < 16 * 4; i++) {
border_colors[i] = convert_rgb15_with_fade(gb, gb->sgb->border.palette[i], 64 - gb->sgb->border_animation);
}
}
else {
gb->sgb->border_animation--;
for (unsigned i = 0; i < 16 * 4; i++) {
border_colors[i] = convert_rgb15_with_fade(gb, gb->sgb->border.palette[i], gb->sgb->border_animation);
}
}
if (gb->sgb->border_animation == 32) {
memcpy(&gb->sgb->border, &gb->sgb->pending_border, sizeof(gb->sgb->border));
}
for (unsigned tile_y = 0; tile_y < 28; tile_y++) {
for (unsigned tile_x = 0; tile_x < 32; tile_x++) {
bool gb_area = false;
if (tile_x >= 6 && tile_x < 26 && tile_y >= 5 && tile_y < 23) {
gb_area = true;
}
uint16_t tile = gb->sgb->border.map[tile_x + tile_y * 32];
uint8_t flip_x = (tile & 0x4000)? 0x7 : 0;
uint8_t flip_y = (tile & 0x8000)? 0x7 : 0;
uint8_t palette = (tile >> 10) & 3;
for (unsigned y = 0; y < 8; y++) {
for (unsigned x = 0; x < 8; x++) {
uint8_t color = gb->sgb->border.tiles[(tile & 0xFF) * 64 + (x ^ flip_x) + (y ^ flip_y) * 8] & 0xF;
if (color == 0) {
if (gb_area) continue;
gb->screen[tile_x * 8 + x + (tile_y * 8 + y) * 0x100] = colors[0];
}
else {
gb->screen[tile_x * 8 + x + (tile_y * 8 + y) * 0x100] = border_colors[color + palette * 16];
}
}
}
}
}
}
void GB_sgb_load_default_data(GB_gameboy_t *gb)
{
#include "sgb_border.inc"
memcpy(gb->sgb->border.map, tilemap, sizeof(tilemap));
memcpy(gb->sgb->border.palette, palette, sizeof(palette));
/* Expend tileset */
for (unsigned tile = 0; tile < sizeof(tiles) / 32; tile++) {
for (unsigned y = 0; y < 8; y++) {
for (unsigned x = 0; x < 8; x++) {
gb->sgb->border.tiles[tile * 8 * 8 + y * 8 + x] =
(tiles[tile * 32 + y * 2 + 0] & (1 << (7 ^ x)) ? 1 : 0) |
(tiles[tile * 32 + y * 2 + 1] & (1 << (7 ^ x)) ? 2 : 0) |
(tiles[tile * 32 + y * 2 + 16] & (1 << (7 ^ x)) ? 4 : 0) |
(tiles[tile * 32 + y * 2 + 17] & (1 << (7 ^ x)) ? 8 : 0);
}
}
}
if (gb->model != GB_MODEL_SGB2) {
/* Delete the "2" */
gb->sgb->border.map[25 * 32 + 25] = gb->sgb->border.map[25 * 32 + 26] =
gb->sgb->border.map[26 * 32 + 25] = gb->sgb->border.map[26 * 32 + 26] =
gb->sgb->border.map[27 * 32 + 25] = gb->sgb->border.map[27 * 32 + 26] =
gb->sgb->border.map[0];
/* Re-center */
memmove(&gb->sgb->border.map[25 * 32 + 1], &gb->sgb->border.map[25 * 32], (32 * 3 - 1) * sizeof(gb->sgb->border.map[0]));
}
gb->sgb->effective_palettes[0] = built_in_palettes[0];
gb->sgb->effective_palettes[1] = built_in_palettes[1];
gb->sgb->effective_palettes[2] = built_in_palettes[2];
gb->sgb->effective_palettes[3] = built_in_palettes[3];
}
static double fm_synth(double phase)
{
return (sin(phase * M_PI * 2) +
sin(phase * M_PI * 2 + sin(phase * M_PI * 2)) +
sin(phase * M_PI * 2 + sin(phase * M_PI * 3)) +
sin(phase * M_PI * 2 + sin(phase * M_PI * 4))) / 4;
}
static double fm_sweep(double phase)
{
double ret = 0;
for (unsigned i = 0; i < 8; i++) {
ret += sin((phase * M_PI * 2 + sin(phase * M_PI * 8) / 4) * pow(1.25, i)) * (8 - i) / 36;
}
return ret;
}
static double random_double(void)
{
return ((signed)(GB_random32() % 0x10001) - 0x8000) / (double) 0x8000;
}
static void render_jingle(GB_gameboy_t *gb, size_t count)
{
const double frequencies[7] = {
466.16, // Bb4
587.33, // D5
698.46, // F5
830.61, // Ab5
1046.50, // C6
1244.51, // Eb6
1567.98, // G6
};
assert(gb->apu_output.sample_callback);
if (gb->sgb->intro_animation < 0) {
GB_sample_t sample = {0, 0};
for (unsigned i = 0; i < count; i++) {
gb->apu_output.sample_callback(gb, &sample);
}
return;
}
if (gb->sgb->intro_animation >= INTRO_ANIMATION_LENGTH) return;
signed jingle_stage = (gb->sgb->intro_animation - 64) / 3;
double sweep_cutoff_ratio = 2000.0 * pow(2, gb->sgb->intro_animation / 20.0) / gb->apu_output.sample_rate;
double sweep_phase_shift = 1000.0 * pow(2, gb->sgb->intro_animation / 40.0) / gb->apu_output.sample_rate;
if (sweep_cutoff_ratio > 1) {
sweep_cutoff_ratio = 1;
}
GB_sample_t stereo;
for (unsigned i = 0; i < count; i++) {
double sample = 0;
for (signed f = 0; f < 7 && f < jingle_stage; f++) {
sample += fm_synth(gb->sgb_intro_jingle_phases[f]) *
(0.75 * pow(0.5, jingle_stage - f) + 0.25) / 5.0;
gb->sgb_intro_jingle_phases[f] += frequencies[f] / gb->apu_output.sample_rate;
}
if (gb->sgb->intro_animation > 100) {
sample *= pow((INTRO_ANIMATION_LENGTH - gb->sgb->intro_animation) / (INTRO_ANIMATION_LENGTH - 100.0), 3);
}
if (gb->sgb->intro_animation < 120) {
double next = fm_sweep(gb->sgb_intro_sweep_phase) * 0.3 + random_double() * 0.7;
gb->sgb_intro_sweep_phase += sweep_phase_shift;
gb->sgb_intro_sweep_previous_sample = next * (sweep_cutoff_ratio) +
gb->sgb_intro_sweep_previous_sample * (1 - sweep_cutoff_ratio);
sample += gb->sgb_intro_sweep_previous_sample * pow((120 - gb->sgb->intro_animation) / 120.0, 2) * 0.8;
}
stereo.left = stereo.right = sample * 0x7000;
gb->apu_output.sample_callback(gb, &stereo);
}
return;
}

View File

@ -1,66 +0,0 @@
#ifndef sgb_h
#define sgb_h
#include "gb_struct_def.h"
#include <stdint.h>
#include <stdbool.h>
typedef struct GB_sgb_s GB_sgb_t;
#ifdef GB_INTERNAL
struct GB_sgb_s {
uint8_t command[16 * 7];
uint16_t command_write_index;
bool ready_for_pulse;
bool ready_for_write;
bool ready_for_stop;
bool disable_commands;
/* Screen buffer */
uint8_t screen_buffer[160 * 144]; // Live image from the Game Boy
uint8_t effective_screen_buffer[160 * 144]; // Image actually rendered to the screen
/* Multiplayer Input */
uint8_t player_count, current_player;
/* Mask */
uint8_t mask_mode;
/* Data Transfer */
uint8_t vram_transfer_countdown, transfer_dest;
/* Border */
struct {
uint8_t tiles[0x100 * 8 * 8]; /* High nibble not used*/
union {
struct {
uint16_t map[32 * 32];
uint16_t palette[16 * 4];
};
uint16_t raw_data[0x440];
};
} border, pending_border;
uint8_t border_animation;
/* Colorization */
uint16_t effective_palettes[4 * 4];
uint16_t ram_palettes[4 * 512];
uint8_t attribute_map[20 * 18];
uint8_t attribute_files[0xFE0];
/* Intro */
int16_t intro_animation;
/* GB Header */
uint8_t received_header[0x54];
/* Multiplayer (cont) */
bool mlt_lock;
};
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value);
void GB_sgb_render(GB_gameboy_t *gb);
void GB_sgb_load_default_data(GB_gameboy_t *gb);
#endif
#endif

View File

@ -1,563 +0,0 @@
static uint8_t animation_logo[] = {
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3, 0x3, 0x3, 0x3, 0x3, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x1, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x1, 0x3, 0x3, 0x1, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x1, 0x1, 0x1, 0x1, 0x3, 0x3, 0x1, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1, 0x3, 0x3, 0x3, 0x3, 0x3, 0x1, 0x1, 0x0, 0x0, 0x0, 0x3, 0x3, 0x3, 0x1, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x3, 0x3, 0x3, 0x3, 0x3, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x3, 0x3, 0x3, 0x1, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4,
0x4, 0x4, 0x4, 0x3, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x3, 0x3, 0x1, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xE, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x4,
0x4, 0x4, 0x4, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x3, 0x3, 0x1, 0x1, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xE, 0xE, 0x1,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x4, 0x4,
0x4, 0x4, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x3, 0x3, 0x1, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xE, 0xE, 0xE, 0x1,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4,
0x4, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3, 0x3, 0x3, 0x1, 0x1, 0x0, 0x1,
0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x7, 0x0, 0x0, 0x1, 0x9, 0x9, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x1, 0xC, 0xC, 0xC, 0xC, 0x0, 0x0, 0x0, 0x0, 0x0, 0xE, 0xE, 0xE, 0xE, 0x1,
0x0, 0x0, 0xE, 0xE, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4,
0x4, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x3, 0x1, 0x1, 0x0, 0x0, 0x5,
0x5, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x6, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x7, 0x7, 0x7, 0x9, 0x9, 0x9, 0x9, 0x9, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0x0, 0x0, 0x0, 0x0, 0xE, 0xE, 0xE, 0xE, 0x1,
0x1, 0xE, 0xE, 0xE, 0xE, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4,
0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3, 0x3, 0x1, 0x0, 0x0, 0x5, 0x5,
0x5, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x6, 0x6, 0x6, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1,
0x7, 0x7, 0x7, 0x9, 0x1, 0x1, 0x1, 0x9, 0x9, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC,
0xC, 0xC, 0xC, 0xC, 0x1, 0x1, 0xC, 0xC, 0x0, 0x0, 0x0, 0xE, 0xE, 0xE, 0xE, 0xE,
0xE, 0xE, 0xE, 0xE, 0xE, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x1, 0x1, 0x0, 0x1, 0x5, 0x5,
0x5, 0x1, 0x0, 0x0, 0x0, 0x0, 0x6, 0x6, 0x6, 0x6, 0x1, 0x0, 0x0, 0x0, 0x0, 0x7,
0x7, 0x7, 0x9, 0x1, 0x1, 0x0, 0x0, 0x9, 0x9, 0x1, 0x0, 0x0, 0x0, 0x0, 0xC, 0xC,
0xC, 0xC, 0x1, 0x1, 0x0, 0xC, 0xC, 0xC, 0x1, 0x0, 0x0, 0xE, 0xE, 0xE, 0xE, 0xE,
0xE, 0xE, 0xE, 0xE, 0xE, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x5, 0x5, 0x5,
0x5, 0x1, 0x0, 0x0, 0x0, 0x1, 0x6, 0x6, 0x6, 0x1, 0x1, 0x0, 0x0, 0x0, 0x7, 0x7,
0x7, 0x7, 0x1, 0x1, 0x0, 0x0, 0x1, 0x9, 0x9, 0x9, 0x0, 0x0, 0x0, 0x1, 0xC, 0xC,
0xC, 0x1, 0x1, 0x0, 0x0, 0xC, 0xC, 0xC, 0x1, 0x0, 0x0, 0x1, 0xD, 0x1, 0x1, 0x1,
0x1, 0xE, 0xE, 0xE, 0xE, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4,
0x4, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x5, 0x5, 0x5,
0x1, 0x1, 0x0, 0x0, 0x0, 0x6, 0x6, 0x6, 0x6, 0x1, 0x0, 0x0, 0x0, 0x0, 0x7, 0x7,
0x7, 0x1, 0x1, 0x0, 0x0, 0x0, 0x9, 0x9, 0x9, 0x1, 0x0, 0x0, 0x0, 0xC, 0xC, 0xC,
0xC, 0x1, 0x0, 0x0, 0xC, 0xC, 0xC, 0xC, 0x1, 0x0, 0x0, 0xD, 0xD, 0x1, 0x0, 0x0,
0xE, 0xE, 0xF, 0xF, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x4,
0x4, 0x4, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x5, 0x5, 0x5,
0x1, 0x0, 0x0, 0x0, 0x1, 0x6, 0x6, 0x6, 0x1, 0x1, 0x0, 0x0, 0x0, 0x7, 0x7, 0x7,
0x7, 0x1, 0x0, 0x0, 0x0, 0x0, 0x9, 0x9, 0x9, 0x1, 0x0, 0x0, 0xC, 0xC, 0xC, 0xC,
0x1, 0x1, 0x0, 0x1, 0xC, 0xC, 0xC, 0x1, 0x1, 0x0, 0x1, 0xD, 0x1, 0x1, 0x0, 0xF,
0xF, 0xF, 0xF, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4,
0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x5, 0x5, 0x5, 0x1,
0x1, 0x0, 0x0, 0x0, 0x6, 0x6, 0x6, 0x6, 0x1, 0x0, 0x0, 0x0, 0x1, 0x7, 0x7, 0x7,
0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x9, 0x9, 0x9, 0x1, 0x0, 0x0, 0xC, 0xC, 0xC, 0xC,
0x1, 0x0, 0x1, 0xC, 0xC, 0xC, 0xC, 0x1, 0x0, 0x0, 0xD, 0xD, 0x1, 0x0, 0x1, 0xF,
0xF, 0xF, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x4, 0x4, 0x4, 0x4, 0x4, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x6, 0x6, 0x1, 0x1,
0x0, 0x0, 0x0, 0x6, 0x6, 0x6, 0x6, 0x1, 0x0, 0x0, 0x0, 0x0, 0x7, 0x7, 0x7, 0x7,
0x1, 0x0, 0x0, 0x0, 0x0, 0x9, 0x9, 0x9, 0x9, 0x1, 0x0, 0x1, 0xC, 0xC, 0xB, 0xB,
0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0x1, 0x0, 0x0, 0xD, 0xD, 0x1, 0x1, 0x0, 0xF, 0xF,
0xF, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x1, 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x0, 0x1, 0x6, 0x6, 0x6, 0x1, 0x0,
0x0, 0x0, 0x1, 0x6, 0x6, 0x7, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x7, 0x7, 0x7, 0x1,
0x1, 0x0, 0x0, 0x0, 0x0, 0x9, 0x9, 0x9, 0x1, 0x1, 0x0, 0xC, 0xC, 0xB, 0xB, 0x1,
0xC, 0xC, 0xC, 0xC, 0x1, 0x1, 0x1, 0x0, 0x1, 0xD, 0x1, 0x1, 0x0, 0x1, 0xF, 0xF,
0xF, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x6, 0x6, 0x6, 0x1, 0x1, 0x0,
0x0, 0x0, 0x6, 0x6, 0x7, 0x7, 0x1, 0x0, 0x0, 0x0, 0x0, 0x7, 0x7, 0x7, 0x8, 0x1,
0x0, 0x0, 0x0, 0x0, 0x9, 0x9, 0x9, 0x9, 0x1, 0x0, 0x0, 0xC, 0xB, 0xB, 0xB, 0x1,
0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0xD, 0xD, 0x1, 0x0, 0x0, 0xF, 0xF, 0xF,
0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5, 0x5, 0x5, 0x5, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x1, 0x4, 0x4, 0x4, 0x1, 0x0, 0x6, 0x6, 0x6, 0x1, 0x0, 0x0,
0x0, 0x6, 0x6, 0x7, 0x7, 0x1, 0x1, 0x0, 0x0, 0x0, 0x7, 0x7, 0x7, 0x8, 0x8, 0x1,
0x0, 0x0, 0x0, 0x1, 0x9, 0x9, 0xA, 0x1, 0x1, 0x0, 0xC, 0xB, 0xB, 0xB, 0x1, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xD, 0xD, 0x1, 0x1, 0x0, 0x0, 0xF, 0xF, 0xF,
0x1, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5, 0x5, 0x5, 0x5, 0x1, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x4, 0x4, 0x1, 0x1, 0x6, 0x6, 0x6, 0x1, 0x0, 0x0,
0x1, 0x6, 0x7, 0x7, 0x7, 0x1, 0x0, 0x0, 0x0, 0x7, 0x7, 0x7, 0x8, 0x8, 0xA, 0x1,
0x0, 0x0, 0x0, 0x9, 0x9, 0xA, 0xA, 0x1, 0x0, 0x1, 0xB, 0xB, 0xB, 0xD, 0x1, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xD, 0xD, 0x1, 0x1, 0x0, 0x0, 0x0, 0xF, 0xF, 0xF,
0x1, 0x0, 0x0, 0x0, 0xF, 0xF, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5, 0x5, 0x5, 0x5, 0x5, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x4, 0x4, 0x1, 0x1, 0x6, 0x6, 0x1, 0x1, 0x0, 0x1,
0x6, 0x1, 0x7, 0x7, 0x7, 0x1, 0x0, 0x0, 0x1, 0x7, 0x7, 0x8, 0x8, 0xA, 0xA, 0x1,
0x0, 0x0, 0xA, 0xA, 0xA, 0xA, 0x1, 0x1, 0x0, 0xB, 0xB, 0x1, 0xD, 0xD, 0xD, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xD, 0xD, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF, 0xF,
0x1, 0x0, 0x0, 0x0, 0xF, 0xF, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5, 0x5, 0x5, 0x5, 0x5,
0x5, 0x1, 0x0, 0x0, 0x1, 0x4, 0x4, 0x1, 0x1, 0x0, 0x6, 0x6, 0x1, 0x0, 0x1, 0x6,
0x1, 0x1, 0x7, 0x7, 0x7, 0x1, 0x0, 0x1, 0x7, 0x7, 0x8, 0x8, 0x1, 0x1, 0xA, 0xA,
0x1, 0xA, 0xA, 0xA, 0xA, 0x1, 0x1, 0xB, 0xB, 0xB, 0x1, 0x1, 0x1, 0xD, 0xD, 0x1,
0x0, 0x0, 0x0, 0x1, 0xD, 0xD, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF, 0xF,
0x1, 0x0, 0x1, 0xF, 0xF, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x5, 0x5, 0x5,
0x5, 0x5, 0x5, 0x4, 0x4, 0x4, 0x1, 0x1, 0x0, 0x0, 0x6, 0x6, 0x6, 0x6, 0x6, 0x1,
0x1, 0x0, 0x1, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x8, 0x8, 0x8, 0x1, 0x0, 0x1, 0xA,
0xA, 0xA, 0xA, 0xB, 0xB, 0xB, 0xB, 0xB, 0xB, 0x1, 0x1, 0x0, 0x0, 0xD, 0xD, 0xD,
0xD, 0xD, 0xD, 0xD, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF,
0xF, 0xF, 0xF, 0xF, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x5,
0x5, 0x5, 0x5, 0x5, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x6, 0x1, 0x1, 0x1,
0x0, 0x0, 0x0, 0x1, 0x7, 0x7, 0x7, 0x1, 0x8, 0x8, 0x8, 0x1, 0x1, 0x0, 0x0, 0x0,
0xB, 0xB, 0xB, 0xB, 0xB, 0xB, 0xB, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0xD,
0xD, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
0xF, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x8, 0x8, 0x8, 0x1, 0x0, 0x0, 0x0, 0x0,
0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x8, 0x8, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x8, 0x8, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x8, 0x8, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x8, 0x8, 0x8, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0,
0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2,
0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x8, 0x8, 0x1, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x1, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0,
0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2,
0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x8, 0x8, 0x1, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0,
0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2,
0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2,
0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x8, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1,
0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2,
0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0,
0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2,
0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2,
0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2,
0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0,
0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x1,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2,
0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2,
0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2,
0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2,
0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0,
0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2,
0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2,
0x1, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2,
0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2,
0x1, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0,
0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2,
0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2,
0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
0x2, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0,
0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2,
0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2,
0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0,
0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x1, 0x0, 0x1, 0x2, 0x2,
0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0,
0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2,
0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2,
0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x2,
0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0,
0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2,
0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1,
0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0,
0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
0x1, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1,
0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2,
0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0,
0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1,
0x1, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1,
0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2,
0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0,
0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1,
0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1,
0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1,
0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2,
0x2, 0x1, 0x1, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2,
0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0,
0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1,
0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1,
0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2,
0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0,
0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0,
0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2,
0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0,
0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0,
0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0,
0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0,
0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x0,
0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0,
0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1,
0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1,
0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
};
static const unsigned animation_logo_height = sizeof(animation_logo) / 160;

View File

@ -1,658 +0,0 @@
static const uint16_t palette[] = {
0x0000, 0x0011, 0x18C6, 0x001A, 0x318C, 0x39CE, 0x5294, 0x5AD6,
0x739C, 0x45A8, 0x4520, 0x18A5, 0x4631, 0x2033, 0x20EC, 0x18B7
};
static const uint16_t tilemap[] = {
0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001,
0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001,
0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001,
0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001,
0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002,
0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002,
0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002,
0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002,
0x1001, 0x1003, 0x1004, 0x1005, 0x1005, 0x1005, 0x1005, 0x1005,
0x1005, 0x1005, 0x1005, 0x1005, 0x1005, 0x1005, 0x1005, 0x1005,
0x1005, 0x1005, 0x1005, 0x1005, 0x1005, 0x1005, 0x1005, 0x1005,
0x1005, 0x1005, 0x1005, 0x1005, 0x1005, 0x5004, 0x5003, 0x1001,
0x1001, 0x1006, 0x1007, 0x1007, 0x1007, 0x1008, 0x1009, 0x100A,
0x100B, 0x100C, 0x100D, 0x100E, 0x100F, 0x1010, 0x1011, 0x1012,
0x1013, 0x1014, 0x1015, 0x100E, 0x1016, 0x1017, 0x1018, 0x1019,
0x101A, 0x101B, 0x101C, 0x1007, 0x1007, 0x1007, 0x5006, 0x1001,
0x1001, 0x101D, 0x101E, 0x101E, 0x101E, 0x101F, 0x1020, 0x1021,
0x1022, 0x1023, 0x1024, 0x1025, 0x5024, 0x1026, 0x1025, 0x1025,
0x1027, 0x1028, 0x1029, 0x102A, 0x102B, 0x102C, 0x102D, 0x102E,
0x102F, 0x1030, 0x1031, 0x101E, 0x101E, 0x101E, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1034, 0x1035, 0x5034, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x8034, 0x1036, 0xC034, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0x1037, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0x1038, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1039, 0x103A, 0x1001,
0x1001, 0x103B, 0x103C, 0x1032, 0x1032, 0xC03C, 0x103D, 0x103D,
0x103D, 0x103D, 0x103D, 0x103D, 0x103D, 0x103D, 0x103D, 0x103D,
0x103D, 0x103D, 0x103D, 0x103D, 0x103D, 0x103D, 0x103D, 0x103D,
0x103D, 0x103D, 0x103E, 0x103F, 0x1040, 0x1041, 0x1001, 0x1001,
0x1001, 0x1042, 0x1043, 0x1044, 0x1044, 0x1044, 0x1044, 0x1044,
0x1044, 0x1044, 0x1044, 0x1044, 0x1044, 0x1044, 0x1044, 0x1044,
0x1044, 0x1044, 0x1044, 0x1044, 0x1044, 0x1044, 0x1044, 0x1044,
0x1044, 0x1044, 0x1045, 0x1046, 0x1001, 0x1001, 0x1001, 0x1001,
0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1047, 0x1048, 0x1049,
0x104A, 0x104B, 0x104C, 0x104D, 0x104E, 0x104F, 0x1050, 0x1051,
0x1052, 0x1053, 0x1054, 0x1055, 0x1056, 0x1057, 0x1058, 0x1059,
0x105A, 0x105B, 0x105C, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001,
0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x105D, 0x105E, 0x105F,
0x1060, 0x1061, 0x1062, 0x1063, 0x1064, 0x1065, 0x1066, 0x1067,
0x1068, 0x1069, 0x106A, 0x106B, 0x106C, 0x106D, 0x106E, 0x106F,
0x1070, 0x1071, 0x1072, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001,
0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1073, 0x1074, 0x1075,
0x1076, 0x1077, 0x1078, 0x1079, 0x107A, 0x107B, 0x107C, 0x107D,
0x107E, 0x107F, 0x1080, 0x1081, 0x1082, 0x1083, 0x507A, 0x1084,
0x1001, 0x1085, 0x507A, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001,
};
const uint8_t tiles[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x00, 0xFF, 0x01, 0xFE, 0x06, 0xF9, 0x08, 0xF7,
0x11, 0xEF, 0x22, 0xDB, 0x20, 0xDB, 0x40, 0xB7,
0xFF, 0x00, 0xFF, 0x00, 0xFE, 0x00, 0xF8, 0x00,
0xF1, 0x00, 0xE6, 0x04, 0xE4, 0x00, 0xC8, 0x00,
0x7F, 0x80, 0x80, 0x7F, 0x00, 0xFF, 0x7F, 0xFF,
0x80, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x00, 0x80, 0x00, 0x00, 0x00, 0x7F, 0x00,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x40, 0xB7, 0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF,
0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF,
0xC8, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00,
0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x03, 0xFF, 0x02, 0xFF,
0x02, 0xFF, 0x02, 0xFF, 0x02, 0xFF, 0x02, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00,
0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0xC1, 0xDD, 0x00, 0xC9,
0x14, 0xFF, 0x14, 0xFF, 0x14, 0xFF, 0x00, 0xC9,
0x00, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x36, 0x22,
0x14, 0x00, 0x14, 0x00, 0x14, 0x00, 0x36, 0x22,
0x00, 0xFF, 0x00, 0xFF, 0xC7, 0xDF, 0x01, 0xCF,
0x11, 0xFF, 0x11, 0xFF, 0x11, 0xFF, 0x01, 0xCF,
0x00, 0x00, 0x00, 0x00, 0xE7, 0x00, 0x31, 0x20,
0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x31, 0x20,
0x00, 0xFF, 0x00, 0xFF, 0xC2, 0xFF, 0x03, 0xFF,
0x02, 0xFE, 0x02, 0xFE, 0x02, 0xFF, 0x02, 0xFF,
0x00, 0x00, 0x00, 0x00, 0xC2, 0x00, 0x03, 0x00,
0x03, 0x01, 0x03, 0x00, 0x02, 0x00, 0x02, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0x08, 0xFF, 0x18, 0xFF,
0x08, 0x4E, 0x08, 0x4E, 0x09, 0x1F, 0x08, 0x1C,
0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x18, 0x00,
0xB9, 0x10, 0xB9, 0xA1, 0xE9, 0xA0, 0xEB, 0x41,
0x00, 0xFF, 0x00, 0xFF, 0x4F, 0xFF, 0x02, 0x1F,
0x02, 0x4F, 0x02, 0x4F, 0xF2, 0xFF, 0x02, 0xE7,
0x00, 0x00, 0x00, 0x00, 0x4F, 0x00, 0xE2, 0xA0,
0xB2, 0xA0, 0xB2, 0x10, 0xF2, 0x00, 0x1A, 0x10,
0x00, 0xFF, 0x00, 0xFF, 0xBC, 0xFD, 0x22, 0xFB,
0x22, 0xFB, 0x3C, 0xFD, 0x24, 0xFF, 0x26, 0xF9,
0x00, 0x00, 0x00, 0x00, 0xBE, 0x00, 0x26, 0x00,
0x26, 0x00, 0x3E, 0x00, 0x24, 0x00, 0x26, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0x50, 0xFF, 0x49, 0xEF,
0x49, 0xF0, 0x46, 0xFF, 0x49, 0xF0, 0x49, 0xEF,
0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x59, 0x00,
0x4F, 0x06, 0x46, 0x00, 0x4F, 0x06, 0x59, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0x88, 0xFF, 0x00, 0x72,
0x00, 0xF2, 0x05, 0xFF, 0x00, 0xF8, 0x00, 0x78,
0x00, 0x00, 0x00, 0x00, 0x88, 0x00, 0x8D, 0x08,
0x0D, 0x05, 0x05, 0x00, 0x07, 0x05, 0x87, 0x02,
0x00, 0xFF, 0x00, 0xFF, 0x8A, 0xFF, 0x02, 0x27,
0x02, 0x27, 0x52, 0xFF, 0x02, 0x8F, 0x02, 0x8F,
0x00, 0x00, 0x00, 0x00, 0x8A, 0x00, 0xDA, 0x88,
0xDA, 0x50, 0x52, 0x00, 0x72, 0x50, 0x72, 0x20,
0x00, 0xFF, 0x00, 0xFF, 0xFA, 0xFF, 0x22, 0xFF,
0x22, 0xFF, 0x23, 0xFF, 0x22, 0xFF, 0x22, 0xFF,
0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x22, 0x00,
0x22, 0x00, 0x23, 0x00, 0x22, 0x00, 0x22, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0x20, 0xFF, 0x20, 0xFF,
0x20, 0xFF, 0xE0, 0xFF, 0x20, 0xFF, 0x20, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x20, 0x00,
0x20, 0x00, 0xE0, 0x00, 0x20, 0x00, 0x20, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0x33, 0x37, 0x00, 0x77,
0x80, 0xFF, 0x20, 0x27, 0x08, 0xFF, 0x00, 0x77,
0x00, 0x00, 0x00, 0x00, 0xFB, 0x40, 0x88, 0x88,
0x80, 0x00, 0xF8, 0x50, 0x08, 0x00, 0x88, 0x88,
0x00, 0xFF, 0x00, 0xFF, 0xEF, 0xFF, 0x88, 0xFF,
0x88, 0xFF, 0x8F, 0xFF, 0x88, 0xFF, 0x88, 0xFF,
0x00, 0x00, 0x00, 0x00, 0xEF, 0x00, 0x88, 0x00,
0x88, 0x00, 0x8F, 0x00, 0x88, 0x00, 0x88, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0xF9, 0xFD, 0x80, 0xF9,
0x84, 0xFF, 0xF4, 0xFF, 0x84, 0xFF, 0x80, 0xF9,
0x00, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x86, 0x02,
0x84, 0x00, 0xF4, 0x00, 0x84, 0x00, 0x86, 0x02,
0x00, 0xFF, 0x00, 0xFF, 0xC0, 0xDF, 0x00, 0xCF,
0x10, 0xFF, 0x10, 0xFF, 0x10, 0xFF, 0x00, 0xCF,
0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x30, 0x20,
0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x30, 0x20,
0x00, 0xFF, 0x00, 0xFF, 0x30, 0x36, 0x00, 0x74,
0x82, 0xFF, 0x22, 0x27, 0x0A, 0xFF, 0x00, 0x74,
0x00, 0x00, 0x00, 0x00, 0xF9, 0x40, 0x8B, 0x89,
0x82, 0x00, 0xFA, 0x50, 0x0A, 0x00, 0x8B, 0x89,
0x00, 0xFF, 0x00, 0xFF, 0xE2, 0xEF, 0x02, 0xE7,
0x0A, 0xFF, 0x0A, 0xFF, 0x0A, 0xFF, 0x00, 0xE4,
0x00, 0x00, 0x00, 0x00, 0xF2, 0x00, 0x1A, 0x10,
0x0A, 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x1B, 0x12,
0x00, 0xFF, 0x00, 0xFF, 0x14, 0xFF, 0x16, 0xFF,
0x14, 0xFC, 0x15, 0xFE, 0x14, 0xFF, 0x04, 0xCF,
0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x16, 0x00,
0x17, 0x01, 0x15, 0x00, 0x14, 0x00, 0x34, 0x10,
0x00, 0xFF, 0x00, 0xFF, 0x2F, 0xFF, 0x28, 0xFF,
0x28, 0xFF, 0xA8, 0x7F, 0x28, 0x3F, 0x68, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x28, 0x00,
0x28, 0x00, 0xA8, 0x00, 0xE8, 0x80, 0x68, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0x7F, 0x00, 0x3F,
0x40, 0xFF, 0x40, 0xFF, 0x40, 0xFF, 0x00, 0x3F,
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0xC0, 0x80,
0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0xC0, 0x80,
0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF,
0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF,
0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00,
0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x01, 0xFF,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0xC1, 0xDD, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0xE3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0xC1, 0xDF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0xE1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0x02, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0x4A, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0x4A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0x0A, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0x22, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0x82, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0x20, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0x60, 0x67, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0xF8, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0x8F, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0x8F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0xA2, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0xF9, 0xFD, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0xC0, 0xDF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0x60, 0x66, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0xF9, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0xE0, 0xEE, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0xF1, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0xC4, 0xDF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0xE4, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0x2F, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0x00, 0x7F, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x80, 0xFF,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF,
0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF,
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x3C, 0xFF,
0x7E, 0xFF, 0xE7, 0xE7, 0xFF, 0x7E, 0xFF, 0x7E,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x7E,
0x81, 0xC3, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00,
0xFF, 0x7E, 0xFF, 0x3C, 0xFF, 0x00, 0x7E, 0x81,
0x3C, 0xC3, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0xC3, 0x81,
0x7E, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xF7, 0x00, 0xF7, 0x00, 0xF7, 0x00, 0xF7,
0x00, 0xF7, 0x00, 0xED, 0x00, 0xED, 0x00, 0xED,
0x09, 0x00, 0x09, 0x00, 0x09, 0x00, 0x09, 0x00,
0x09, 0x00, 0x11, 0x02, 0x11, 0x02, 0x11, 0x02,
0x00, 0xED, 0x00, 0xDB, 0x00, 0xDB, 0x00, 0xDB,
0x00, 0xB7, 0x00, 0xB7, 0x00, 0x6F, 0x00, 0x6F,
0x11, 0x02, 0x23, 0x04, 0x23, 0x04, 0x23, 0x04,
0x47, 0x08, 0x47, 0x08, 0x8F, 0x10, 0x8F, 0x10,
0x00, 0xFE, 0x00, 0xFD, 0x00, 0xFB, 0x00, 0xF7,
0x00, 0xEE, 0x00, 0xDD, 0x00, 0xBB, 0x00, 0x77,
0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00,
0x10, 0x01, 0x21, 0x02, 0x43, 0x04, 0x87, 0x08,
0x00, 0xDF, 0x00, 0xBF, 0x00, 0xBF, 0x00, 0x7F,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x1F, 0x20, 0x3F, 0x40, 0x3F, 0x40, 0x7F, 0x80,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF, 0x00, 0xB7,
0x00, 0xB7, 0x00, 0xDB, 0x00, 0xDD, 0x00, 0xEE,
0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x88, 0x40,
0x88, 0x40, 0xC4, 0x20, 0xC2, 0x20, 0xE1, 0x10,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x7F,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00,
0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x7F, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFC,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFC, 0x00, 0xE3, 0x00, 0x1F,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0x00, 0x1C, 0x00, 0xE0, 0x00,
0x00, 0xFE, 0x00, 0xFD, 0x00, 0xF3, 0x00, 0xEF,
0x00, 0x1C, 0x00, 0xF3, 0x00, 0xEF, 0x00, 0x1F,
0x01, 0x00, 0x02, 0x00, 0x0C, 0x00, 0x10, 0x00,
0xE0, 0x03, 0x03, 0x0C, 0x0F, 0x10, 0x1F, 0xE0,
0x00, 0xEF, 0x00, 0xDF, 0x00, 0xBF, 0x00, 0x7F,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x0F, 0x10, 0x1F, 0x20, 0x3F, 0x40, 0x7F, 0x80,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x00, 0xF7, 0x00, 0xF9, 0x00, 0xFE, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xF0, 0x08, 0xF8, 0x06, 0xFE, 0x01, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x00, 0x80, 0x00, 0xFF, 0x00, 0x7F, 0x00, 0x80,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x7F, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x7F,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x00, 0x03, 0x00, 0xFF, 0x00, 0xFC, 0x00, 0x03,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFC, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xFC,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x00, 0xFC, 0x00, 0xE3, 0x00, 0x1F, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0x03, 0x03, 0x1C, 0x1F, 0xE0, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x01, 0xFF, 0x01, 0xFD, 0x03, 0xFF, 0x03, 0xFF,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x01, 0xFE, 0x02, 0xFE, 0x02, 0xFC, 0x00,
0x0E, 0xEE, 0x3F, 0xFF, 0x75, 0x71, 0xFB, 0xE7,
0xE3, 0xCB, 0xC7, 0x9F, 0x07, 0x3E, 0x84, 0x7C,
0xFB, 0x1B, 0xE6, 0x26, 0x8E, 0x82, 0x3E, 0x22,
0x7C, 0x54, 0x7D, 0x25, 0xF9, 0x40, 0xFB, 0x01,
0x00, 0xFF, 0x00, 0xFF, 0x80, 0xFF, 0x80, 0xFF,
0x00, 0x7F, 0x80, 0x4F, 0x31, 0x7F, 0x71, 0xFD,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x80, 0xFF, 0x80,
0xFF, 0x00, 0xFF, 0x30, 0xFF, 0xB1, 0xDE, 0x52,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0x7B, 0x87, 0xFF, 0x8E, 0xFE,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x84, 0xFA, 0x82, 0xF9, 0x88,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0xC1, 0xFD, 0xE3, 0x7B,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xC3, 0xBC, 0x24,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x01, 0xFF, 0x03, 0xFF, 0xE3, 0xFB, 0xF7, 0xBF,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x01, 0xFE, 0x02, 0x7C, 0x64, 0xFC, 0xB4,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0x7F, 0x80, 0xFF, 0xA0, 0x2F, 0xF0, 0xFF,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x70, 0x8F, 0x80,
0x00, 0xFF, 0x00, 0xFF, 0x02, 0xFD, 0x00, 0xF7,
0x00, 0xFF, 0x11, 0xEE, 0x11, 0xEE, 0x10, 0xEF,
0xFF, 0x00, 0xFF, 0x00, 0xFC, 0x03, 0xF8, 0x0F,
0xF0, 0x0F, 0xE0, 0x1F, 0xE1, 0x1E, 0xE0, 0x1F,
0x00, 0xFF, 0x00, 0xFF, 0x10, 0xE7, 0x00, 0xFB,
0xC4, 0x3B, 0x98, 0x03, 0x00, 0xEF, 0x80, 0x7F,
0xFF, 0x00, 0xFF, 0x00, 0x0F, 0xF8, 0x07, 0xFC,
0x07, 0xF8, 0xEF, 0x74, 0xFF, 0x10, 0x7F, 0x80,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xEF, 0x00, 0xFF, 0x22, 0xDD, 0x06, 0xB9,
0xFF, 0x00, 0xFF, 0x00, 0xF8, 0x07, 0xF0, 0x0F,
0xF0, 0x1F, 0xE0, 0x1F, 0xC0, 0x3F, 0xC4, 0x7B,
0x00, 0xFF, 0x00, 0xFF, 0x80, 0x7D, 0x02, 0xFD,
0x02, 0xBD, 0x40, 0xBF, 0x40, 0xBF, 0x40, 0xBF,
0xFF, 0x00, 0xFF, 0x00, 0x7E, 0x83, 0x7C, 0x83,
0x7C, 0xC3, 0x7C, 0x83, 0x3C, 0xC3, 0x3C, 0xC3,
0x00, 0xFF, 0x00, 0xFF, 0x10, 0xEF, 0x00, 0xFF,
0x00, 0xF7, 0x00, 0xF7, 0x48, 0xB6, 0x48, 0xB7,
0xFF, 0x00, 0xFF, 0x00, 0x0F, 0xF0, 0x0F, 0xF0,
0x0F, 0xF8, 0x0F, 0xF8, 0x07, 0xF9, 0x06, 0xF9,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xBF, 0x02, 0xFC,
0x02, 0x7D, 0x02, 0xFD, 0x02, 0xFD, 0x20, 0xDD,
0xFF, 0x00, 0xFF, 0x00, 0xC1, 0x7E, 0x81, 0x7F,
0x81, 0xFE, 0x01, 0xFE, 0x03, 0xFC, 0x03, 0xFE,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xBF, 0x40, 0xBF,
0x47, 0xB8, 0x08, 0xF0, 0x08, 0xF7, 0x0F, 0xF0,
0xFF, 0x00, 0xFF, 0x00, 0xC0, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x87, 0x7F, 0x87, 0x78, 0x80, 0x7F,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFB, 0x24, 0xCB,
0xE4, 0x1B, 0x00, 0x1F, 0x00, 0xFF, 0x80, 0x3F,
0xFF, 0x00, 0xFF, 0x00, 0x1C, 0xE7, 0x18, 0xF7,
0x18, 0xE7, 0xF8, 0xE7, 0xF8, 0x07, 0x78, 0xC7,
0x00, 0xFF, 0x00, 0xFF, 0x04, 0xF9, 0x00, 0xFF,
0x71, 0x8E, 0x89, 0x06, 0x81, 0x7E, 0xE1, 0x1E,
0xFF, 0x00, 0xFF, 0x00, 0x03, 0xFE, 0x01, 0xFE,
0x00, 0xFF, 0x70, 0xFF, 0x70, 0x8F, 0x01, 0xFE,
0x00, 0xFF, 0x00, 0xFF, 0x02, 0xF9, 0x00, 0xFF,
0x00, 0xFF, 0x03, 0xFC, 0x06, 0xB9, 0x44, 0xBB,
0xFF, 0x00, 0xFF, 0x00, 0xFC, 0x07, 0xF0, 0x0F,
0xE0, 0x1F, 0xC1, 0x3E, 0xC3, 0x7C, 0x87, 0x78,
0x00, 0xFF, 0x00, 0xFF, 0x08, 0xF7, 0x00, 0xFD,
0xC0, 0x3F, 0x11, 0x0E, 0x00, 0xFF, 0x08, 0xF7,
0xFF, 0x00, 0xFF, 0x00, 0x07, 0xF8, 0x03, 0xFE,
0x01, 0xFE, 0xE0, 0xFF, 0xF0, 0x0F, 0xF0, 0x0F,
0x00, 0xFF, 0x00, 0xFF, 0x08, 0x77, 0x40, 0xBF,
0x04, 0xBB, 0x00, 0xFE, 0x00, 0xDD, 0x00, 0x7F,
0xFF, 0x00, 0xFF, 0x00, 0x87, 0xF8, 0x87, 0x78,
0xC3, 0x7C, 0xC3, 0x3D, 0xE2, 0x3F, 0xE0, 0x9F,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFD, 0x06, 0xF9,
0x0C, 0x73, 0x08, 0xF7, 0x10, 0xE7, 0x20, 0xCF,
0xFF, 0x00, 0xFF, 0x00, 0xC3, 0x3E, 0x83, 0x7C,
0x87, 0xF8, 0x0F, 0xF0, 0x1F, 0xE8, 0x3F, 0xD0,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xF7, 0x00, 0xFF,
0x01, 0xDE, 0x06, 0xF8, 0x1C, 0xC3, 0x00, 0xF3,
0xFF, 0x00, 0xFF, 0x00, 0xF8, 0x0F, 0xE0, 0x1F,
0xE0, 0x3F, 0xC3, 0x3D, 0xE7, 0x38, 0xFF, 0x0C,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xDF, 0x00, 0xFF,
0x00, 0xF7, 0x08, 0x77, 0x08, 0xF7, 0x08, 0xF7,
0xFF, 0x00, 0xFF, 0x00, 0x3F, 0xE0, 0x0F, 0xF0,
0x0F, 0xF8, 0x87, 0xF8, 0x87, 0x78, 0x07, 0xF8,
0x03, 0xFF, 0x03, 0xFF, 0x01, 0xFF, 0x00, 0xFE,
0x18, 0xDF, 0x1C, 0xFD, 0x0F, 0xEF, 0x07, 0xF7,
0xFC, 0x00, 0xFE, 0x02, 0xFF, 0x01, 0xFF, 0x01,
0xFF, 0x38, 0xF3, 0x12, 0xF9, 0x19, 0xFC, 0x0C,
0x02, 0x79, 0x80, 0xFD, 0xC0, 0xDF, 0xF0, 0xFE,
0x79, 0x3F, 0x19, 0xDB, 0x19, 0xFB, 0xF9, 0xF7,
0xFF, 0x84, 0xFF, 0x82, 0x7F, 0x60, 0x9F, 0x91,
0xEF, 0xA9, 0xF6, 0x34, 0xFE, 0x1C, 0x1F, 0x11,
0x63, 0xEF, 0xF3, 0xEB, 0xC6, 0xCE, 0xEF, 0xDE,
0x8C, 0x9C, 0xDE, 0xBD, 0x9C, 0x9D, 0xFF, 0xEF,
0x9E, 0x02, 0xBC, 0xA4, 0x3D, 0x14, 0x7B, 0x4A,
0x73, 0x21, 0xF7, 0x94, 0xF7, 0xF6, 0xFE, 0xEE,
0x8D, 0xEC, 0x9E, 0x7D, 0x1C, 0x5B, 0x38, 0xFA,
0x79, 0xF7, 0x71, 0x75, 0xF3, 0xF3, 0xEF, 0xCF,
0xF3, 0x90, 0xF7, 0x14, 0xEF, 0xA8, 0xEF, 0x2D,
0xCF, 0x41, 0x8E, 0x8A, 0x3C, 0x3C, 0x39, 0x19,
0x67, 0xFF, 0xEF, 0xFE, 0xEC, 0xDC, 0xCF, 0xCF,
0xDD, 0xDC, 0xDC, 0x9F, 0x2C, 0x2F, 0xD7, 0xC7,
0xB9, 0x21, 0xBB, 0xAA, 0xB3, 0x81, 0x76, 0x76,
0x77, 0x76, 0xE7, 0xA4, 0xD7, 0x44, 0xFB, 0xCB,
0xB3, 0x37, 0x73, 0x72, 0xF4, 0xEC, 0xEF, 0xCD,
0xCD, 0x09, 0x11, 0xF3, 0x29, 0xA7, 0xF1, 0xCF,
0xCD, 0x49, 0xDF, 0xDE, 0xBF, 0xA5, 0x7F, 0x5D,
0xF6, 0x32, 0xFE, 0x14, 0xFE, 0x70, 0xFF, 0xC1,
0xF0, 0x77, 0xF0, 0x67, 0xE0, 0xCF, 0x80, 0x97,
0xC8, 0xBB, 0x98, 0xBB, 0x90, 0xD3, 0xE8, 0xE7,
0xDF, 0x58, 0xBF, 0x28, 0x7F, 0x50, 0x7F, 0x28,
0xF7, 0x84, 0xFF, 0xDC, 0xEF, 0xA4, 0xDF, 0xC0,
0x00, 0xFF, 0x04, 0xF3, 0x03, 0xF8, 0x00, 0xFF,
0x08, 0xF7, 0x03, 0xFC, 0x00, 0xBF, 0x18, 0xC7,
0xF0, 0x0F, 0xF8, 0x0F, 0xFE, 0x05, 0xFF, 0x00,
0xE7, 0x18, 0xC0, 0x3F, 0xC0, 0x7F, 0xE0, 0x3F,
0x00, 0xFF, 0x00, 0xFF, 0x08, 0xF6, 0x08, 0x77,
0x08, 0xF5, 0x08, 0xF7, 0x10, 0xE7, 0x70, 0x87,
0x1F, 0xE0, 0x0F, 0xF0, 0x07, 0xF9, 0x86, 0xF9,
0x86, 0x7B, 0x0C, 0xF3, 0x08, 0xFF, 0x38, 0xCF,
0x0A, 0xF1, 0x88, 0x77, 0x0E, 0xF1, 0x00, 0xFF,
0x00, 0xFF, 0x7F, 0x80, 0x41, 0xBE, 0x81, 0x3E,
0x84, 0x7F, 0x0E, 0xF1, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x3E, 0xC1, 0x7E, 0x81, 0x7E, 0xC1,
0x04, 0xFB, 0x04, 0xDB, 0x24, 0xDB, 0x20, 0xDF,
0x20, 0xDF, 0x00, 0xFF, 0x08, 0xE7, 0x19, 0xE6,
0x38, 0xC7, 0x38, 0xE7, 0x38, 0xC7, 0x18, 0xE7,
0x18, 0xE7, 0x18, 0xE7, 0x10, 0xFF, 0x10, 0xEF,
0x48, 0xB5, 0x80, 0x3F, 0x84, 0x7B, 0x80, 0x7F,
0xA1, 0x5E, 0x21, 0x5E, 0x02, 0x7C, 0x02, 0x7D,
0x46, 0xBB, 0x44, 0xFB, 0x40, 0xBF, 0x40, 0xBF,
0xC0, 0x3F, 0xC1, 0xBE, 0xE1, 0x9F, 0xE3, 0x9C,
0x60, 0x9D, 0x64, 0x99, 0x84, 0x3B, 0x84, 0x7B,
0x04, 0x7B, 0x40, 0xBB, 0x41, 0xBA, 0x09, 0xF2,
0x03, 0xFE, 0x43, 0xBE, 0x43, 0xFC, 0xC3, 0x3C,
0xC7, 0xB8, 0x87, 0x7C, 0x86, 0x7D, 0x86, 0x7D,
0x80, 0x7F, 0x80, 0x7F, 0x8F, 0x70, 0x10, 0xEF,
0x10, 0xEF, 0x1F, 0xE0, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x0F, 0xF0, 0x0F, 0xF0,
0x0F, 0xF0, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x48, 0xB7, 0x48, 0xB7, 0xC0, 0x3F, 0x01, 0xFE,
0x01, 0xFE, 0x81, 0x2E, 0x50, 0xAF, 0x50, 0xAF,
0x30, 0xCF, 0x30, 0xCF, 0xF0, 0x0F, 0xF0, 0x0F,
0xF0, 0x0F, 0x70, 0xDF, 0x20, 0xDF, 0x60, 0x9F,
0x06, 0xF8, 0x00, 0xFD, 0xF0, 0x0F, 0x00, 0x7E,
0x00, 0xEE, 0xE2, 0x1C, 0x02, 0xFD, 0x0C, 0xF1,
0x03, 0xFD, 0x03, 0xFE, 0xE1, 0x1E, 0xF1, 0x8F,
0xF1, 0x1F, 0x01, 0xFF, 0x03, 0xFC, 0x07, 0xFA,
0x08, 0xF3, 0x08, 0xF7, 0x08, 0xF7, 0x00, 0xFF,
0x40, 0xBB, 0x01, 0xFE, 0x20, 0xDF, 0x18, 0xE7,
0x87, 0x7C, 0x87, 0x78, 0x87, 0x78, 0x87, 0x78,
0x87, 0x7C, 0xC0, 0x3F, 0xE0, 0x1F, 0xF0, 0x0F,
0x08, 0xF7, 0x08, 0xF7, 0x01, 0xFE, 0x11, 0xEE,
0x01, 0xDE, 0x82, 0x7C, 0x04, 0xF9, 0x38, 0xC3,
0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xE0, 0x1F,
0xE1, 0x3E, 0x03, 0xFD, 0x07, 0xFA, 0x0F, 0xF4,
0x10, 0x6F, 0x00, 0x7F, 0x01, 0x7E, 0x01, 0xFE,
0x01, 0xFE, 0x11, 0xEE, 0x10, 0xEE, 0x12, 0xEC,
0xE0, 0x9F, 0xF0, 0x8F, 0xF0, 0x8F, 0xF0, 0x0F,
0xF0, 0x0F, 0xE1, 0x1E, 0xE1, 0x1F, 0xE1, 0x1F,
0x40, 0x9F, 0x80, 0x3F, 0x80, 0x7F, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x7F, 0xA0, 0x7F, 0xC0, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x00, 0xFF, 0x00, 0xFB, 0x08, 0xF7, 0x00, 0xFF,
0x01, 0xBE, 0x03, 0xFC, 0x00, 0x7F, 0x80, 0x7F,
0xFE, 0x01, 0xFC, 0x07, 0xF0, 0x0F, 0xE0, 0x1F,
0xC1, 0x7E, 0x80, 0x7F, 0x80, 0xFF, 0x00, 0xFF,
0x08, 0xF7, 0x10, 0xE7, 0x60, 0x8F, 0xC0, 0x3F,
0x80, 0x7F, 0xE0, 0x0F, 0x00, 0xEF, 0x00, 0xEF,
0x0F, 0xF0, 0x1F, 0xE8, 0x3F, 0xD0, 0x7F, 0x80,
0xFF, 0x00, 0x1F, 0xF0, 0x1F, 0xF0, 0x1F, 0xF0,
0x02, 0xF8, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x04, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xD0, 0xC6, 0x00, 0x1F, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0xC9, 0xFF, 0xE0, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xE7, 0x86, 0x01, 0x39, 0x01, 0xFF, 0x03, 0xFF,
0x03, 0xFF, 0x00, 0xFC, 0x00, 0xFE, 0x00, 0xFF,
0xFF, 0x9E, 0xFF, 0xC7, 0xFE, 0x00, 0xFE, 0x02,
0xFF, 0x03, 0xFF, 0x02, 0xFF, 0x01, 0xFF, 0x00,
0xC3, 0xD3, 0xC0, 0xBC, 0x80, 0xBF, 0x00, 0x7F,
0x80, 0x7F, 0x00, 0x7F, 0x00, 0xFF, 0x00, 0xFF,
0x7F, 0x6B, 0x7F, 0x03, 0xFF, 0xC0, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x80, 0xFF, 0x00, 0xFF, 0x00,
0xC7, 0x1B, 0x00, 0x7C, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x23, 0xFF, 0x83, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xC0, 0x1F, 0x00, 0x7F, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x20, 0xFF, 0x80, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x50, 0x4F, 0x00, 0x9F, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x40, 0xFF, 0x60, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x07, 0xF0, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x08, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xC7, 0x18, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x20, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x80, 0x7F, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xF7, 0x08, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x0C, 0xE1, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x12, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x38, 0x87, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x40, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x8F, 0x30, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x40, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xF0, 0x07, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x08, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x03, 0xF0, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x0C, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x0E, 0xF1, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x7F, 0x80, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00
};

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +0,0 @@
#ifndef sm83_cpu_h
#define sm83_cpu_h
#include "gb_struct_def.h"
#include <stdint.h>
void GB_cpu_disassemble(GB_gameboy_t *gb, uint16_t pc, uint16_t count);
#ifdef GB_INTERNAL
void GB_cpu_run(GB_gameboy_t *gb);
#endif
#endif /* sm83_cpu_h */

View File

@ -1,788 +0,0 @@
#include <stdio.h>
#include <stdbool.h>
#include "gb.h"
typedef void GB_opcode_t(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc);
static void ill(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
GB_log(gb, ".BYTE $%02x\n", opcode);
(*pc)++;
}
static void nop(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
GB_log(gb, "NOP\n");
(*pc)++;
}
static void stop(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
uint8_t next = GB_read_memory(gb, (*pc)++);
if (next) {
GB_log(gb, "CORRUPTED STOP (%02x)\n", next);
}
else {
GB_log(gb, "STOP\n");
}
}
static char *register_names[] = {"af", "bc", "de", "hl", "sp"};
static void ld_rr_d16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t register_id;
uint16_t value;
register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1;
value = GB_read_memory(gb, (*pc)++);
value |= GB_read_memory(gb, (*pc)++) << 8;
const char *symbol = GB_debugger_name_for_address(gb, value);
if (symbol) {
GB_log(gb, "LD %s, %s ; =$%04x\n", register_names[register_id], symbol, value);
}
else {
GB_log(gb, "LD %s, $%04x\n", register_names[register_id], value);
}
}
static void ld_drr_a(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t register_id;
register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1;
GB_log(gb, "LD [%s], a\n", register_names[register_id]);
}
static void inc_rr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t register_id;
register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1;
GB_log(gb, "INC %s\n", register_names[register_id]);
}
static void inc_hr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t register_id;
(*pc)++;
register_id = ((opcode >> 4) + 1) & 0x03;
GB_log(gb, "INC %c\n", register_names[register_id][0]);
}
static void dec_hr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t register_id;
(*pc)++;
register_id = ((opcode >> 4) + 1) & 0x03;
GB_log(gb, "DEC %c\n", register_names[register_id][0]);
}
static void ld_hr_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t register_id;
(*pc)++;
register_id = ((opcode >> 4) + 1) & 0x03;
GB_log(gb, "LD %c, $%02x\n", register_names[register_id][0], GB_read_memory(gb, (*pc)++));
}
static void rlca(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "RLCA\n");
}
static void rla(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "RLA\n");
}
static void ld_da16_sp(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc){
uint16_t addr;
(*pc)++;
addr = GB_read_memory(gb, (*pc)++);
addr |= GB_read_memory(gb, (*pc)++) << 8;
const char *symbol = GB_debugger_name_for_address(gb, addr);
if (symbol) {
GB_log(gb, "LD [%s], sp ; =$%04x\n", symbol, addr);
}
else {
GB_log(gb, "LD [$%04x], sp\n", addr);
}
}
static void add_hl_rr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t register_id;
(*pc)++;
register_id = (opcode >> 4) + 1;
GB_log(gb, "ADD hl, %s\n", register_names[register_id]);
}
static void ld_a_drr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t register_id;
register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1;
GB_log(gb, "LD a, [%s]\n", register_names[register_id]);
}
static void dec_rr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t register_id;
register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1;
GB_log(gb, "DEC %s\n", register_names[register_id]);
}
static void inc_lr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t register_id;
register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1;
GB_log(gb, "INC %c\n", register_names[register_id][1]);
}
static void dec_lr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t register_id;
register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1;
GB_log(gb, "DEC %c\n", register_names[register_id][1]);
}
static void ld_lr_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t register_id;
register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1;
GB_log(gb, "LD %c, $%02x\n", register_names[register_id][1], GB_read_memory(gb, (*pc)++));
}
static void rrca(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
GB_log(gb, "RRCA\n");
(*pc)++;
}
static void rra(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
GB_log(gb, "RRA\n");
(*pc)++;
}
static void jr_r8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
uint16_t addr = *pc + (int8_t) GB_read_memory(gb, (*pc)) + 1;
const char *symbol = GB_debugger_name_for_address(gb, addr);
if (symbol) {
GB_attributed_log(gb, GB_LOG_UNDERLINE, "JR %s ; =$%04x\n", symbol, addr);
}
else {
GB_attributed_log(gb, GB_LOG_UNDERLINE, "JR $%04x\n", addr);
}
(*pc)++;
}
static const char *condition_code(uint8_t opcode)
{
switch ((opcode >> 3) & 0x3) {
case 0:
return "nz";
case 1:
return "z";
case 2:
return "nc";
case 3:
return "c";
}
return NULL;
}
static void jr_cc_r8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
uint16_t addr = *pc + (int8_t) GB_read_memory(gb, (*pc)) + 1;
const char *symbol = GB_debugger_name_for_address(gb, addr);
if (symbol) {
GB_attributed_log(gb, GB_LOG_DASHED_UNDERLINE, "JR %s, %s ; =$%04x\n", condition_code(opcode), symbol, addr);
}
else {
GB_attributed_log(gb, GB_LOG_DASHED_UNDERLINE, "JR %s, $%04x\n", condition_code(opcode), addr);
}
(*pc)++;
}
static void daa(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
GB_log(gb, "DAA\n");
(*pc)++;
}
static void cpl(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
GB_log(gb, "CPL\n");
(*pc)++;
}
static void scf(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
GB_log(gb, "SCF\n");
(*pc)++;
}
static void ccf(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
GB_log(gb, "CCF\n");
(*pc)++;
}
static void ld_dhli_a(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
GB_log(gb, "LD [hli], a\n");
(*pc)++;
}
static void ld_dhld_a(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
GB_log(gb, "LD [hld], a\n");
(*pc)++;
}
static void ld_a_dhli(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
GB_log(gb, "LD a, [hli]\n");
(*pc)++;
}
static void ld_a_dhld(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
GB_log(gb, "LD a, [hld]\n");
(*pc)++;
}
static void inc_dhl(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
GB_log(gb, "INC [hl]\n");
(*pc)++;
}
static void dec_dhl(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
GB_log(gb, "DEC [hl]\n");
(*pc)++;
}
static void ld_dhl_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "LD [hl], $%02x\n", GB_read_memory(gb, (*pc)++));
}
static const char *get_src_name(uint8_t opcode)
{
uint8_t src_register_id;
uint8_t src_low;
src_register_id = ((opcode >> 1) + 1) & 3;
src_low = (opcode & 1);
if (src_register_id == GB_REGISTER_AF) {
return src_low? "a": "[hl]";
}
if (src_low) {
return register_names[src_register_id] + 1;
}
static const char *high_register_names[] = {"a", "b", "d", "h"};
return high_register_names[src_register_id];
}
static const char *get_dst_name(uint8_t opcode)
{
uint8_t dst_register_id;
uint8_t dst_low;
dst_register_id = ((opcode >> 4) + 1) & 3;
dst_low = opcode & 8;
if (dst_register_id == GB_REGISTER_AF) {
return dst_low? "a": "[hl]";
}
if (dst_low) {
return register_names[dst_register_id] + 1;
}
static const char *high_register_names[] = {"a", "b", "d", "h"};
return high_register_names[dst_register_id];
}
static void ld_r_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "LD %s, %s\n", get_dst_name(opcode), get_src_name(opcode));
}
static void add_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "ADD %s\n", get_src_name(opcode));
}
static void adc_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "ADC %s\n", get_src_name(opcode));
}
static void sub_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "SUB %s\n", get_src_name(opcode));
}
static void sbc_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "SBC %s\n", get_src_name(opcode));
}
static void and_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "AND %s\n", get_src_name(opcode));
}
static void xor_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "XOR %s\n", get_src_name(opcode));
}
static void or_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "OR %s\n", get_src_name(opcode));
}
static void cp_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "CP %s\n", get_src_name(opcode));
}
static void halt(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "HALT\n");
}
static void ret_cc(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_attributed_log(gb, GB_LOG_DASHED_UNDERLINE, "RET %s\n", condition_code(opcode));
}
static void pop_rr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t register_id;
register_id = ((GB_read_memory(gb, (*pc)++) >> 4) + 1) & 3;
GB_log(gb, "POP %s\n", register_names[register_id]);
}
static void jp_cc_a16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8);
const char *symbol = GB_debugger_name_for_address(gb, addr);
if (symbol) {
GB_attributed_log(gb, GB_LOG_DASHED_UNDERLINE, "JP %s, %s ; =$%04x\n", condition_code(opcode), symbol, addr);
}
else {
GB_attributed_log(gb, GB_LOG_DASHED_UNDERLINE, "JP %s, $%04x\n", condition_code(opcode), addr);
}
(*pc) += 2;
}
static void jp_a16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8);
const char *symbol = GB_debugger_name_for_address(gb, addr);
if (symbol) {
GB_log(gb, "JP %s ; =$%04x\n", symbol, addr);
}
else {
GB_log(gb, "JP $%04x\n", addr);
}
(*pc) += 2;
}
static void call_cc_a16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8);
const char *symbol = GB_debugger_name_for_address(gb, addr);
if (symbol) {
GB_log(gb, "CALL %s, %s ; =$%04x\n", condition_code(opcode), symbol, addr);
}
else {
GB_log(gb, "CALL %s, $%04x\n", condition_code(opcode), addr);
}
(*pc) += 2;
}
static void push_rr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t register_id;
register_id = ((GB_read_memory(gb, (*pc)++) >> 4) + 1) & 3;
GB_log(gb, "PUSH %s\n", register_names[register_id]);
}
static void add_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "ADD $%02x\n", GB_read_memory(gb, (*pc)++));
}
static void adc_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "ADC $%02x\n", GB_read_memory(gb, (*pc)++));
}
static void sub_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "SUB $%02x\n", GB_read_memory(gb, (*pc)++));
}
static void sbc_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "SBC $%02x\n", GB_read_memory(gb, (*pc)++));
}
static void and_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "AND $%02x\n", GB_read_memory(gb, (*pc)++));
}
static void xor_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "XOR $%02x\n", GB_read_memory(gb, (*pc)++));
}
static void or_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "OR $%02x\n", GB_read_memory(gb, (*pc)++));
}
static void cp_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "CP $%02x\n", GB_read_memory(gb, (*pc)++));
}
static void rst(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "RST $%02x\n", opcode ^ 0xC7);
}
static void ret(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_attributed_log(gb, GB_LOG_UNDERLINE, "RET\n");
}
static void reti(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_attributed_log(gb, GB_LOG_UNDERLINE, "RETI\n");
}
static void call_a16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8);
const char *symbol = GB_debugger_name_for_address(gb, addr);
if (symbol) {
GB_log(gb, "CALL %s ; =$%04x\n", symbol, addr);
}
else {
GB_log(gb, "CALL $%04x\n", addr);
}
(*pc) += 2;
}
static void ld_da8_a(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
uint8_t addr = GB_read_memory(gb, (*pc)++);
const char *symbol = GB_debugger_name_for_address(gb, 0xff00 + addr);
if (symbol) {
GB_log(gb, "LDH [%s & $FF], a ; =$%02x\n", symbol, addr);
}
else {
GB_log(gb, "LDH [$%02x], a\n", addr);
}
}
static void ld_a_da8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
uint8_t addr = GB_read_memory(gb, (*pc)++);
const char *symbol = GB_debugger_name_for_address(gb, 0xff00 + addr);
if (symbol) {
GB_log(gb, "LDH a, [%s & $FF] ; =$%02x\n", symbol, addr);
}
else {
GB_log(gb, "LDH a, [$%02x]\n", addr);
}
}
static void ld_dc_a(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "LDH [c], a\n");
}
static void ld_a_dc(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "LDH a, [c]\n");
}
static void add_sp_r8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
int8_t temp = GB_read_memory(gb, (*pc)++);
GB_log(gb, "ADD SP, %s$%02x\n", temp < 0? "-" : "", temp < 0? -temp : temp);
}
static void jp_hl(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "JP hl\n");
}
static void ld_da16_a(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8);
const char *symbol = GB_debugger_name_for_address(gb, addr);
if (symbol) {
GB_log(gb, "LD [%s], a ; =$%04x\n", symbol, addr);
}
else {
GB_log(gb, "LD [$%04x], a\n", addr);
}
(*pc) += 2;
}
static void ld_a_da16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8);
const char *symbol = GB_debugger_name_for_address(gb, addr);
if (symbol) {
GB_log(gb, "LD a, [%s] ; =$%04x\n", symbol, addr);
}
else {
GB_log(gb, "LD a, [$%04x]\n", addr);
}
(*pc) += 2;
}
static void di(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "DI\n");
}
static void ei(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "EI\n");
}
static void ld_hl_sp_r8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
int8_t temp = GB_read_memory(gb, (*pc)++);
GB_log(gb, "LD hl, sp, %s$%02x\n", temp < 0? "-" : "", temp < 0? -temp : temp);
}
static void ld_sp_hl(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "LD sp, hl\n");
}
static void rlc_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "RLC %s\n", get_src_name(opcode));
}
static void rrc_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "RRC %s\n", get_src_name(opcode));
}
static void rl_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "RL %s\n", get_src_name(opcode));
}
static void rr_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "RR %s\n", get_src_name(opcode));
}
static void sla_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "SLA %s\n", get_src_name(opcode));
}
static void sra_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "SRA %s\n", get_src_name(opcode));
}
static void srl_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "SRL %s\n", get_src_name(opcode));
}
static void swap_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "SWAP %s\n", get_src_name(opcode));
}
static void bit_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t bit;
(*pc)++;
bit = ((opcode >> 3) & 7);
if ((opcode & 0xC0) == 0x40) { /* Bit */
GB_log(gb, "BIT %s, %d\n", get_src_name(opcode), bit);
}
else if ((opcode & 0xC0) == 0x80) { /* res */
GB_log(gb, "RES %s, %d\n", get_src_name(opcode), bit);
}
else if ((opcode & 0xC0) == 0xC0) { /* set */
GB_log(gb, "SET %s, %d\n", get_src_name(opcode), bit);
}
}
static void cb_prefix(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
opcode = GB_read_memory(gb, ++*pc);
switch (opcode >> 3) {
case 0:
rlc_r(gb, opcode, pc);
break;
case 1:
rrc_r(gb, opcode, pc);
break;
case 2:
rl_r(gb, opcode, pc);
break;
case 3:
rr_r(gb, opcode, pc);
break;
case 4:
sla_r(gb, opcode, pc);
break;
case 5:
sra_r(gb, opcode, pc);
break;
case 6:
swap_r(gb, opcode, pc);
break;
case 7:
srl_r(gb, opcode, pc);
break;
default:
bit_r(gb, opcode, pc);
break;
}
}
static GB_opcode_t *opcodes[256] = {
/* X0 X1 X2 X3 X4 X5 X6 X7 */
/* X8 X9 Xa Xb Xc Xd Xe Xf */
nop, ld_rr_d16, ld_drr_a, inc_rr, inc_hr, dec_hr, ld_hr_d8, rlca, /* 0X */
ld_da16_sp, add_hl_rr, ld_a_drr, dec_rr, inc_lr, dec_lr, ld_lr_d8, rrca,
stop, ld_rr_d16, ld_drr_a, inc_rr, inc_hr, dec_hr, ld_hr_d8, rla, /* 1X */
jr_r8, add_hl_rr, ld_a_drr, dec_rr, inc_lr, dec_lr, ld_lr_d8, rra,
jr_cc_r8, ld_rr_d16, ld_dhli_a, inc_rr, inc_hr, dec_hr, ld_hr_d8, daa, /* 2X */
jr_cc_r8, add_hl_rr, ld_a_dhli, dec_rr, inc_lr, dec_lr, ld_lr_d8, cpl,
jr_cc_r8, ld_rr_d16, ld_dhld_a, inc_rr, inc_dhl, dec_dhl, ld_dhl_d8, scf, /* 3X */
jr_cc_r8, add_hl_rr, ld_a_dhld, dec_rr, inc_hr, dec_hr, ld_hr_d8, ccf,
ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, /* 4X */
ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r,
ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, /* 5X */
ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r,
ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, /* 6X */
ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r,
ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, halt, ld_r_r, /* 7X */
ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r,
add_a_r, add_a_r, add_a_r, add_a_r, add_a_r, add_a_r, add_a_r, add_a_r, /* 8X */
adc_a_r, adc_a_r, adc_a_r, adc_a_r, adc_a_r, adc_a_r, adc_a_r, adc_a_r,
sub_a_r, sub_a_r, sub_a_r, sub_a_r, sub_a_r, sub_a_r, sub_a_r, sub_a_r, /* 9X */
sbc_a_r, sbc_a_r, sbc_a_r, sbc_a_r, sbc_a_r, sbc_a_r, sbc_a_r, sbc_a_r,
and_a_r, and_a_r, and_a_r, and_a_r, and_a_r, and_a_r, and_a_r, and_a_r, /* aX */
xor_a_r, xor_a_r, xor_a_r, xor_a_r, xor_a_r, xor_a_r, xor_a_r, xor_a_r,
or_a_r, or_a_r, or_a_r, or_a_r, or_a_r, or_a_r, or_a_r, or_a_r, /* bX */
cp_a_r, cp_a_r, cp_a_r, cp_a_r, cp_a_r, cp_a_r, cp_a_r, cp_a_r,
ret_cc, pop_rr, jp_cc_a16, jp_a16, call_cc_a16,push_rr, add_a_d8, rst, /* cX */
ret_cc, ret, jp_cc_a16, cb_prefix, call_cc_a16,call_a16, adc_a_d8, rst,
ret_cc, pop_rr, jp_cc_a16, ill, call_cc_a16,push_rr, sub_a_d8, rst, /* dX */
ret_cc, reti, jp_cc_a16, ill, call_cc_a16,ill, sbc_a_d8, rst,
ld_da8_a, pop_rr, ld_dc_a, ill, ill, push_rr, and_a_d8, rst, /* eX */
add_sp_r8, jp_hl, ld_da16_a, ill, ill, ill, xor_a_d8, rst,
ld_a_da8, pop_rr, ld_a_dc, di, ill, push_rr, or_a_d8, rst, /* fX */
ld_hl_sp_r8,ld_sp_hl, ld_a_da16, ei, ill, ill, cp_a_d8, rst,
};
void GB_cpu_disassemble(GB_gameboy_t *gb, uint16_t pc, uint16_t count)
{
const GB_bank_symbol_t *function_symbol = GB_debugger_find_symbol(gb, pc);
if (function_symbol && pc - function_symbol->addr > 0x1000) {
function_symbol = NULL;
}
if (function_symbol && pc != function_symbol->addr) {
GB_log(gb, "%s:\n", function_symbol->name);
}
uint16_t current_function = function_symbol? function_symbol->addr : 0;
while (count--) {
function_symbol = GB_debugger_find_symbol(gb, pc);
if (function_symbol && function_symbol->addr == pc) {
if (current_function != function_symbol->addr) {
GB_log(gb, "\n");
}
GB_log(gb, "%s:\n", function_symbol->name);
}
if (function_symbol) {
GB_log(gb, "%s%04x <+%03x>: ", pc == gb->pc? " ->": " ", pc, pc - function_symbol->addr);
}
else {
GB_log(gb, "%s%04x: ", pc == gb->pc? " ->": " ", pc);
}
uint8_t opcode = GB_read_memory(gb, pc);
opcodes[opcode](gb, opcode, &pc);
}
}

View File

@ -1,110 +0,0 @@
#include "gb.h"
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
static size_t GB_map_find_symbol_index(GB_symbol_map_t *map, uint16_t addr)
{
if (!map->symbols) {
return 0;
}
ssize_t min = 0;
ssize_t max = map->n_symbols;
while (min < max) {
size_t pivot = (min + max) / 2;
if (map->symbols[pivot].addr == addr) return pivot;
if (map->symbols[pivot].addr > addr) {
max = pivot;
}
else {
min = pivot + 1;
}
}
return (size_t) min;
}
GB_bank_symbol_t *GB_map_add_symbol(GB_symbol_map_t *map, uint16_t addr, const char *name)
{
size_t index = GB_map_find_symbol_index(map, addr);
if (index < map->n_symbols && map->symbols[index].addr == addr) return NULL;
map->symbols = realloc(map->symbols, (map->n_symbols + 1) * sizeof(map->symbols[0]));
memmove(&map->symbols[index + 1], &map->symbols[index], (map->n_symbols - index) * sizeof(map->symbols[0]));
map->symbols[index].addr = addr;
map->symbols[index].name = strdup(name);
map->n_symbols++;
return &map->symbols[index];
}
const GB_bank_symbol_t *GB_map_find_symbol(GB_symbol_map_t *map, uint16_t addr)
{
if (!map) return NULL;
size_t index = GB_map_find_symbol_index(map, addr);
if (index < map->n_symbols && map->symbols[index].addr != addr) {
index--;
}
if (index < map->n_symbols) {
return &map->symbols[index];
}
return NULL;
}
GB_symbol_map_t *GB_map_alloc(void)
{
GB_symbol_map_t *map = malloc(sizeof(*map));
memset(map, 0, sizeof(*map));
return map;
}
void GB_map_free(GB_symbol_map_t *map)
{
for (unsigned i = 0; i < map->n_symbols; i++) {
free(map->symbols[i].name);
}
if (map->symbols) {
free(map->symbols);
}
free(map);
}
static int hash_name(const char *name)
{
int r = 0;
while (*name) {
r <<= 1;
if (r & 0x400) {
r ^= 0x401;
}
r += (unsigned char)*(name++);
}
return r & 0x3FF;
}
void GB_reversed_map_add_symbol(GB_reversed_symbol_map_t *map, uint16_t bank, GB_bank_symbol_t *bank_symbol)
{
int hash = hash_name(bank_symbol->name);
GB_symbol_t *symbol = malloc(sizeof(*symbol));
symbol->name = bank_symbol->name;
symbol->addr = bank_symbol->addr;
symbol->bank = bank;
symbol->next = map->buckets[hash];
map->buckets[hash] = symbol;
}
const GB_symbol_t *GB_reversed_map_find_symbol(GB_reversed_symbol_map_t *map, const char *name)
{
int hash = hash_name(name);
GB_symbol_t *symbol = map->buckets[hash];
while (symbol) {
if (strcmp(symbol->name, name) == 0) return symbol;
symbol = symbol->next;
}
return NULL;
}

View File

@ -1,38 +0,0 @@
#ifndef symbol_hash_h
#define symbol_hash_h
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
typedef struct {
char *name;
uint16_t addr;
} GB_bank_symbol_t;
typedef struct GB_symbol_s {
struct GB_symbol_s *next;
const char *name;
uint16_t bank;
uint16_t addr;
} GB_symbol_t;
typedef struct {
GB_bank_symbol_t *symbols;
size_t n_symbols;
} GB_symbol_map_t;
typedef struct {
GB_symbol_t *buckets[0x400];
} GB_reversed_symbol_map_t;
#ifdef GB_INTERNAL
void GB_reversed_map_add_symbol(GB_reversed_symbol_map_t *map, uint16_t bank, GB_bank_symbol_t *symbol);
const GB_symbol_t *GB_reversed_map_find_symbol(GB_reversed_symbol_map_t *map, const char *name);
GB_bank_symbol_t *GB_map_add_symbol(GB_symbol_map_t *map, uint16_t addr, const char *name);
const GB_bank_symbol_t *GB_map_find_symbol(GB_symbol_map_t *map, uint16_t addr);
GB_symbol_map_t *GB_map_alloc(void);
void GB_map_free(GB_symbol_map_t *map);
#endif
#endif /* symbol_hash_h */

View File

@ -1,294 +0,0 @@
#include "gb.h"
#ifdef _WIN32
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0500
#endif
#include <windows.h>
#else
#include <sys/time.h>
#endif
static const unsigned GB_TAC_TRIGGER_BITS[] = {512, 8, 32, 128};
#ifndef DISABLE_TIMEKEEPING
static int64_t get_nanoseconds(void)
{
#ifndef _WIN32
struct timeval now;
gettimeofday(&now, NULL);
return (now.tv_usec) * 1000 + now.tv_sec * 1000000000L;
#else
FILETIME time;
GetSystemTimeAsFileTime(&time);
return (((int64_t)time.dwHighDateTime << 32) | time.dwLowDateTime) * 100L;
#endif
}
static void nsleep(uint64_t nanoseconds)
{
#ifndef _WIN32
struct timespec sleep = {0, nanoseconds};
nanosleep(&sleep, NULL);
#else
HANDLE timer;
LARGE_INTEGER time;
timer = CreateWaitableTimer(NULL, true, NULL);
time.QuadPart = -(nanoseconds / 100L);
SetWaitableTimer(timer, &time, 0, NULL, NULL, false);
WaitForSingleObject(timer, INFINITE);
CloseHandle(timer);
#endif
}
bool GB_timing_sync_turbo(GB_gameboy_t *gb)
{
if (!gb->turbo_dont_skip) {
int64_t nanoseconds = get_nanoseconds();
if (nanoseconds <= gb->last_sync + (1000000000LL * LCDC_PERIOD / GB_get_clock_rate(gb))) {
return true;
}
gb->last_sync = nanoseconds;
}
return false;
}
void GB_timing_sync(GB_gameboy_t *gb)
{
if (gb->turbo) {
gb->cycles_since_last_sync = 0;
return;
}
/* Prevent syncing if not enough time has passed.*/
if (gb->cycles_since_last_sync < LCDC_PERIOD / 3) return;
uint64_t target_nanoseconds = gb->cycles_since_last_sync * 1000000000LL / 2 / GB_get_clock_rate(gb); /* / 2 because we use 8MHz units */
int64_t nanoseconds = get_nanoseconds();
int64_t time_to_sleep = target_nanoseconds + gb->last_sync - nanoseconds;
if (time_to_sleep > 0 && time_to_sleep < LCDC_PERIOD * 1000000000LL / GB_get_clock_rate(gb)) {
nsleep(time_to_sleep);
gb->last_sync += target_nanoseconds;
}
else {
gb->last_sync = nanoseconds;
}
gb->cycles_since_last_sync = 0;
if (gb->update_input_hint_callback) {
gb->update_input_hint_callback(gb);
}
}
#else
bool GB_timing_sync_turbo(GB_gameboy_t *gb)
{
return false;
}
void GB_timing_sync(GB_gameboy_t *gb)
{
}
#endif
static void GB_ir_run(GB_gameboy_t *gb)
{
if (gb->ir_queue_length == 0) return;
if (gb->cycles_since_input_ir_change >= gb->ir_queue[0].delay) {
gb->cycles_since_input_ir_change -= gb->ir_queue[0].delay;
gb->infrared_input = gb->ir_queue[0].state;
gb->ir_queue_length--;
memmove(&gb->ir_queue[0], &gb->ir_queue[1], sizeof(gb->ir_queue[0]) * (gb->ir_queue_length));
}
}
static void advance_tima_state_machine(GB_gameboy_t *gb)
{
if (gb->tima_reload_state == GB_TIMA_RELOADED) {
gb->tima_reload_state = GB_TIMA_RUNNING;
}
else if (gb->tima_reload_state == GB_TIMA_RELOADING) {
gb->io_registers[GB_IO_IF] |= 4;
gb->tima_reload_state = GB_TIMA_RELOADED;
}
}
static void increase_tima(GB_gameboy_t *gb)
{
gb->io_registers[GB_IO_TIMA]++;
if (gb->io_registers[GB_IO_TIMA] == 0) {
gb->io_registers[GB_IO_TIMA] = gb->io_registers[GB_IO_TMA];
gb->tima_reload_state = GB_TIMA_RELOADING;
}
}
static void GB_set_internal_div_counter(GB_gameboy_t *gb, uint32_t value)
{
/* TIMA increases when a specific high-bit becomes a low-bit. */
value &= INTERNAL_DIV_CYCLES - 1;
uint32_t triggers = gb->div_counter & ~value;
if ((gb->io_registers[GB_IO_TAC] & 4) && (triggers & GB_TAC_TRIGGER_BITS[gb->io_registers[GB_IO_TAC] & 3])) {
increase_tima(gb);
}
/* TODO: Can switching to double speed mode trigger an event? */
if (triggers & (gb->cgb_double_speed? 0x2000 : 0x1000)) {
GB_apu_run(gb);
GB_apu_div_event(gb);
}
gb->div_counter = value;
}
static void GB_timers_run(GB_gameboy_t *gb, uint8_t cycles)
{
GB_STATE_MACHINE(gb, div, cycles, 1) {
GB_STATE(gb, div, 1);
GB_STATE(gb, div, 2);
GB_STATE(gb, div, 3);
}
GB_set_internal_div_counter(gb, 0);
main:
GB_SLEEP(gb, div, 1, 3);
while (true) {
advance_tima_state_machine(gb);
GB_set_internal_div_counter(gb, gb->div_counter + 4);
gb->apu.apu_cycles += 4 << !gb->cgb_double_speed;
GB_SLEEP(gb, div, 2, 4);
}
/* Todo: This is ugly to allow compatibility with 0.11 save states. Fix me when breaking save compatibility */
{
div3:
/* Compensate for lack of prefetch emulation, as well as DIV's internal initial value */
GB_set_internal_div_counter(gb, 8);
goto main;
}
}
static void advance_serial(GB_gameboy_t *gb, uint8_t cycles)
{
if (gb->serial_length == 0) {
gb->serial_cycles += cycles;
return;
}
while (cycles > gb->serial_length) {
advance_serial(gb, gb->serial_length);
cycles -= gb->serial_length;
}
uint16_t previous_serial_cycles = gb->serial_cycles;
gb->serial_cycles += cycles;
if ((gb->serial_cycles & gb->serial_length) != (previous_serial_cycles & gb->serial_length)) {
gb->serial_count++;
if (gb->serial_count == 8) {
gb->serial_length = 0;
gb->serial_count = 0;
gb->io_registers[GB_IO_SC] &= ~0x80;
gb->io_registers[GB_IO_IF] |= 8;
}
gb->io_registers[GB_IO_SB] <<= 1;
if (gb->serial_transfer_bit_end_callback) {
gb->io_registers[GB_IO_SB] |= gb->serial_transfer_bit_end_callback(gb);
}
else {
gb->io_registers[GB_IO_SB] |= 1;
}
if (gb->serial_length) {
/* Still more bits to send */
if (gb->serial_transfer_bit_start_callback) {
gb->serial_transfer_bit_start_callback(gb, gb->io_registers[GB_IO_SB] & 0x80);
}
}
}
return;
}
void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles)
{
// Affected by speed boost
gb->dma_cycles += cycles;
if (!gb->stopped) {
GB_timers_run(gb, cycles);
advance_serial(gb, cycles); // TODO: Verify what happens in STOP mode
}
gb->debugger_ticks += cycles;
if (!gb->cgb_double_speed) {
cycles <<= 1;
}
// Not affected by speed boost
gb->double_speed_alignment += cycles;
gb->hdma_cycles += cycles;
gb->apu_output.sample_cycles += cycles;
gb->cycles_since_ir_change += cycles;
gb->cycles_since_input_ir_change += cycles;
gb->cycles_since_last_sync += cycles;
gb->cycles_since_run += cycles;
if (!gb->stopped) { // TODO: Verify what happens in STOP mode
GB_dma_run(gb);
GB_hdma_run(gb);
}
GB_apu_run(gb);
GB_display_run(gb, cycles);
GB_ir_run(gb);
}
/*
This glitch is based on the expected results of mooneye-gb rapid_toggle test.
This glitch happens because how TIMA is increased, see GB_set_internal_div_counter.
According to GiiBiiAdvance, GBC's behavior is different, but this was not tested or implemented.
*/
void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t new_tac)
{
/* Glitch only happens when old_tac is enabled. */
if (!(old_tac & 4)) return;
unsigned old_clocks = GB_TAC_TRIGGER_BITS[old_tac & 3];
unsigned new_clocks = GB_TAC_TRIGGER_BITS[new_tac & 3];
/* The bit used for overflow testing must have been 1 */
if (gb->div_counter & old_clocks) {
/* And now either the timer must be disabled, or the new bit used for overflow testing be 0. */
if (!(new_tac & 4) || gb->div_counter & new_clocks) {
increase_tima(gb);
}
}
}
void GB_rtc_run(GB_gameboy_t *gb)
{
if ((gb->rtc_real.high & 0x40) == 0) { /* is timer running? */
time_t current_time = time(NULL);
while (gb->last_rtc_second < current_time) {
gb->last_rtc_second++;
if (++gb->rtc_real.seconds == 60)
{
gb->rtc_real.seconds = 0;
if (++gb->rtc_real.minutes == 60)
{
gb->rtc_real.minutes = 0;
if (++gb->rtc_real.hours == 24)
{
gb->rtc_real.hours = 0;
if (++gb->rtc_real.days == 0)
{
if (gb->rtc_real.high & 1) /* Bit 8 of days*/
{
gb->rtc_real.high |= 0x80; /* Overflow bit */
}
gb->rtc_real.high ^= 1;
}
}
}
}
}
}
}

View File

@ -1,44 +0,0 @@
#ifndef timing_h
#define timing_h
#include "gb_struct_def.h"
#ifdef GB_INTERNAL
void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles);
void GB_rtc_run(GB_gameboy_t *gb);
void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t new_tac);
bool GB_timing_sync_turbo(GB_gameboy_t *gb); /* Returns true if should skip frame */
void GB_timing_sync(GB_gameboy_t *gb);
enum {
GB_TIMA_RUNNING = 0,
GB_TIMA_RELOADING = 1,
GB_TIMA_RELOADED = 2
};
#define GB_HALT_VALUE (0xFFFF)
#define GB_SLEEP(gb, unit, state, cycles) do {\
(gb)->unit##_cycles -= (cycles) * __state_machine_divisor; \
if ((gb)->unit##_cycles <= 0) {\
(gb)->unit##_state = state;\
return;\
unit##state:; \
}\
} while (0)
#define GB_HALT(gb, unit) (gb)->unit##_cycles = GB_HALT_VALUE
#define GB_STATE_MACHINE(gb, unit, cycles, divisor) \
static const int __state_machine_divisor = divisor;\
(gb)->unit##_cycles += cycles; \
if ((gb)->unit##_cycles <= 0 || (gb)->unit##_cycles == GB_HALT_VALUE) {\
return;\
}\
switch ((gb)->unit##_state)
#endif
#define GB_STATE(gb, unit, state) case state: goto unit##state
#define GB_UNIT(unit) int32_t unit##_cycles, unit##_state
#endif /* timing_h */

View File

@ -48,6 +48,23 @@ bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, unsigned index)
return false;
}
static uint8_t agb_bias_for_channel(GB_gameboy_t *gb, unsigned index)
{
if (!gb->apu.is_active[index]) return 0;
switch (index) {
case GB_SQUARE_1:
return gb->apu.square_channels[GB_SQUARE_1].current_volume;
case GB_SQUARE_2:
return gb->apu.square_channels[GB_SQUARE_2].current_volume;
case GB_WAVE:
return 0;
case GB_NOISE:
return gb->apu.noise_channel.current_volume;
}
return 0;
}
static void update_sample(GB_gameboy_t *gb, unsigned index, int8_t value, unsigned cycles_offset)
{
if (gb->model >= GB_MODEL_AGB) {
@ -66,15 +83,17 @@ static void update_sample(GB_gameboy_t *gb, unsigned index, int8_t value, unsign
}
GB_sample_t output;
uint8_t bias = agb_bias_for_channel(gb, index);
if (gb->io_registers[GB_IO_NR51] & (1 << index)) {
output.right = (0xf - value * 2) * right_volume;
output.right = (0xf - value * 2 + bias) * right_volume;
}
else {
output.right = 0xf * right_volume;
}
if (gb->io_registers[GB_IO_NR51] & (0x10 << index)) {
output.left = (0xf - value * 2) * left_volume;
output.left = (0xf - value * 2 + bias) * left_volume;
}
else {
output.left = 0xf * left_volume;
@ -681,6 +700,21 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
case GB_IO_NR14:
case GB_IO_NR24: {
unsigned index = reg == GB_IO_NR24? GB_SQUARE_2: GB_SQUARE_1;
/* TODO: When the sample length changes right before being updated, the countdown should change to the
old length, but the current sample should not change. Because our write timing isn't accurate to
the T-cycle, we hack around it by stepping the sample index backwards. */
if ((value & 0x80) == 0 && gb->apu.is_active[index]) {
/* On an AGB, as well as on CGB C and earlier (TODO: Tested: 0, B and C), it behaves slightly different on
double speed. */
if (gb->model == GB_MODEL_CGB_E /* || gb->model == GB_MODEL_CGB_D */ || gb->apu.square_channels[index].sample_countdown & 1) {
if (gb->apu.square_channels[index].sample_countdown >> 1 == (gb->apu.square_channels[index].sample_length ^ 0x7FF)) {
gb->apu.square_channels[index].current_sample_index--;
gb->apu.square_channels[index].current_sample_index &= 7;
}
}
}
gb->apu.square_channels[index].sample_length &= 0xFF;
gb->apu.square_channels[index].sample_length |= (value & 7) << 8;
if (index == GB_SQUARE_1) {
@ -970,9 +1004,23 @@ void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate)
if (sample_rate) {
gb->apu_output.highpass_rate = pow(0.999958, GB_get_clock_rate(gb) / (double)sample_rate);
}
gb->apu_output.rate_set_in_clocks = false;
GB_apu_update_cycles_per_sample(gb);
}
void GB_set_sample_rate_by_clocks(GB_gameboy_t *gb, double cycles_per_sample)
{
if (cycles_per_sample == 0) {
GB_set_sample_rate(gb, 0);
return;
}
gb->apu_output.cycles_per_sample = cycles_per_sample;
gb->apu_output.sample_rate = GB_get_clock_rate(gb) / cycles_per_sample * 2;
gb->apu_output.highpass_rate = pow(0.999958, cycles_per_sample);
gb->apu_output.rate_set_in_clocks = true;
}
void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback)
{
gb->apu_output.sample_callback = callback;
@ -985,6 +1033,7 @@ void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode)
void GB_apu_update_cycles_per_sample(GB_gameboy_t *gb)
{
if (gb->apu_output.rate_set_in_clocks) return;
if (gb->apu_output.sample_rate) {
gb->apu_output.cycles_per_sample = 2 * GB_get_clock_rate(gb) / (double)gb->apu_output.sample_rate; /* 2 * because we use 8MHz units */
}

View File

@ -143,9 +143,12 @@ typedef struct {
GB_double_sample_t highpass_diff;
GB_sample_callback_t sample_callback;
bool rate_set_in_clocks;
} GB_apu_output_t;
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate);
void GB_set_sample_rate_by_clocks(GB_gameboy_t *gb, double cycles_per_sample); /* Cycles are in 8MHz units */
void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode);
void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback);
#ifdef GB_INTERNAL

View File

@ -127,13 +127,13 @@ static void display_vblank(GB_gameboy_t *gb)
if (GB_is_hle_sgb(gb)) {
GB_sgb_render(gb);
}
if (gb->turbo) {
if (GB_timing_sync_turbo(gb)) {
return;
}
}
if (!gb->disable_rendering && ((!(gb->io_registers[GB_IO_LCDC] & 0x80) || gb->stopped) || gb->frame_skip_state == GB_FRAMESKIP_LCD_TURNED_ON)) {
/* LCD is off, set screen to white or black (if LCD is on in stop mode) */
if (gb->sgb) {
@ -162,9 +162,20 @@ static inline uint8_t scale_channel(uint8_t x)
static inline uint8_t scale_channel_with_curve(uint8_t x)
{
return (uint8_t[]){0,2,4,7,12,18,25,34,42,52,62,73,85,97,109,121,134,146,158,170,182,193,203,213,221,230,237,243,248,251,253,255,}[x];
return (uint8_t[]){0,2,4,7,12,18,25,34,42,52,62,73,85,97,109,121,134,146,158,170,182,193,203,213,221,230,237,243,248,251,253,255}[x];
}
static inline uint8_t scale_channel_with_curve_agb(uint8_t x)
{
return (uint8_t[]){0,2,5,10,15,20,26,32,38,45,52,60,68,76,84,92,101,110,119,128,138,148,158,168,178,189,199,210,221,232,244,255}[x];
}
static inline uint8_t scale_channel_with_curve_sgb(uint8_t x)
{
return (uint8_t[]){0,2,5,9,15,20,27,34,42,50,58,67,76,85,94,104,114,123,133,143,153,163,173,182,192,202,211,220,229,238,247,255}[x];
}
uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color)
{
uint8_t r = (color) & 0x1F;
@ -177,13 +188,29 @@ uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color)
b = scale_channel(b);
}
else {
r = scale_channel_with_curve(r);
g = scale_channel_with_curve(g);
b = scale_channel_with_curve(b);
if (GB_is_sgb(gb)) {
return gb->rgb_encode_callback(gb,
scale_channel_with_curve_sgb(r),
scale_channel_with_curve_sgb(g),
scale_channel_with_curve_sgb(b));
}
bool agb = gb->model == GB_MODEL_AGB;
r = agb? scale_channel_with_curve_agb(r) : scale_channel_with_curve(r);
g = agb? scale_channel_with_curve_agb(g) : scale_channel_with_curve(g);
b = agb? scale_channel_with_curve_agb(b) : scale_channel_with_curve(b);
if (gb->color_correction_mode != GB_COLOR_CORRECTION_CORRECT_CURVES) {
uint8_t new_g = (g * 3 + b) / 4;
uint8_t new_r = r, new_b = b;
uint8_t new_r, new_g, new_b;
if (agb) {
new_r = (r * 7 + g * 1) / 8;
new_g = (g * 3 + b * 1) / 4;
new_b = (b * 7 + r * 1) / 8;
}
else {
new_g = (g * 3 + b) / 4;
new_r = r;
new_b = b;
}
if (gb->color_correction_mode == GB_COLOR_CORRECTION_PRESERVE_BRIGHTNESS) {
uint8_t old_max = MAX(r, MAX(g, b));
uint8_t new_max = MAX(new_r, MAX(new_g, new_b));
@ -200,7 +227,7 @@ uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color)
if (new_min != 0xff) {
new_r = 0xff - (0xff - new_r) * (0xff - old_min) / (0xff - new_min);
new_g = 0xff - (0xff - new_g) * (0xff - old_min) / (0xff - new_min);
new_b = 0xff - (0xff - new_b) * (0xff - old_min) / (0xff - new_min);;
new_b = 0xff - (0xff - new_b) * (0xff - old_min) / (0xff - new_min);
}
}
r = new_r;
@ -377,7 +404,6 @@ static void render_pixel_if_possible(GB_gameboy_t *gb)
}
uint8_t icd_pixel = 0;
{
uint8_t pixel = bg_enabled? fifo_item->pixel : 0;
if (pixel && bg_priority) {
@ -394,7 +420,6 @@ static void render_pixel_if_possible(GB_gameboy_t *gb)
else if (gb->model & GB_MODEL_NO_SFC_BIT) {
if (gb->icd_pixel_callback) {
icd_pixel = pixel;
//gb->icd_pixel_callback(gb, pixel);
}
}
else {
@ -423,7 +448,7 @@ static void render_pixel_if_possible(GB_gameboy_t *gb)
gb->screen[gb->position_in_line + gb->current_line * WIDTH] = gb->sprite_palettes_rgb[oam_fifo_item->palette * 4 + pixel];
}
}
if (gb->model & GB_MODEL_NO_SFC_BIT) {
if (gb->icd_pixel_callback) {
gb->icd_pixel_callback(gb, icd_pixel);
@ -769,10 +794,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
fifo_push_bg_row(&gb->bg_fifo, 0, 0, 0, false, false);
/* Todo: find out actual access time of SCX */
gb->position_in_line = - (gb->io_registers[GB_IO_SCX] & 7) - 8;
gb->current_lcd_line++; // Todo: unverified timing
if (gb->current_lcd_line == LINES && GB_is_sgb(gb)) {
display_vblank(gb);
}
gb->fetcher_x = ((gb->io_registers[GB_IO_SCX]) / 8) & 0x1f;
gb->extra_penalty_for_sprite_at_0 = (gb->io_registers[GB_IO_SCX] & 7);
@ -909,6 +931,12 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
}
GB_SLEEP(gb, display, 11, LINE_LENGTH - gb->cycles_for_line);
gb->mode_for_interrupt = 2;
// Todo: unverified timing
gb->current_lcd_line++;
if (gb->current_lcd_line == LINES && GB_is_sgb(gb)) {
display_vblank(gb);
}
if (gb->icd_hreset_callback) {
gb->icd_hreset_callback(gb);
@ -985,7 +1013,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
gb->window_disabled_while_active = false;
gb->current_line = 0;
// TODO: not the correct timing
gb->current_lcd_line = -1;
gb->current_lcd_line = 0;
if (gb->icd_vreset_callback) {
gb->icd_vreset_callback(gb);
}

View File

@ -12,11 +12,19 @@
#include "random.h"
#include "gb.h"
#ifdef DISABLE_REWIND
#define GB_rewind_free(...)
#define GB_rewind_push(...)
#endif
static inline uint32_t state_magic(void)
{
if (sizeof(bool) == 1) return 'SAME';
return 'S4ME';
}
void GB_attributed_logv(GB_gameboy_t *gb, GB_log_attributes attributes, const char *fmt, va_list args)
{
char *string = NULL;
@ -659,7 +667,7 @@ void GB_disconnect_serial(GB_gameboy_t *gb)
bool GB_is_inited(GB_gameboy_t *gb)
{
return gb->magic == 'SAME';
return gb->magic == state_magic();
}
bool GB_is_cgb(GB_gameboy_t *gb)
@ -711,7 +719,8 @@ static void reset_ram(GB_gameboy_t *gb)
case GB_MODEL_DMG_B:
case GB_MODEL_SGB_NTSC: /* Unverified*/
case GB_MODEL_SGB_PAL: /* Unverified */
case GB_MODEL_SGB_NO_SFC:
case GB_MODEL_SGB_NTSC_NO_SFC: /* Unverified */
case GB_MODEL_SGB_PAL_NO_SFC: /* Unverified */
for (unsigned i = 0; i < gb->ram_size; i++) {
gb->ram[i] = GB_random();
if (i & 0x100) {
@ -757,7 +766,8 @@ static void reset_ram(GB_gameboy_t *gb)
case GB_MODEL_DMG_B:
case GB_MODEL_SGB_NTSC: /* Unverified*/
case GB_MODEL_SGB_PAL: /* Unverified */
case GB_MODEL_SGB_NO_SFC:
case GB_MODEL_SGB_NTSC_NO_SFC: /* Unverified */
case GB_MODEL_SGB_PAL_NO_SFC: /* Unverified */
case GB_MODEL_SGB2:
case GB_MODEL_SGB2_NO_SFC:
for (unsigned i = 0; i < sizeof(gb->hram); i++) {
@ -782,7 +792,8 @@ static void reset_ram(GB_gameboy_t *gb)
case GB_MODEL_DMG_B:
case GB_MODEL_SGB_NTSC: /* Unverified */
case GB_MODEL_SGB_PAL: /* Unverified */
case GB_MODEL_SGB_NO_SFC: /* Unverified */
case GB_MODEL_SGB_NTSC_NO_SFC: /* Unverified */
case GB_MODEL_SGB_PAL_NO_SFC: /* Unverified */
case GB_MODEL_SGB2:
case GB_MODEL_SGB2_NO_SFC:
for (unsigned i = 0; i < 8; i++) {
@ -810,7 +821,8 @@ static void reset_ram(GB_gameboy_t *gb)
case GB_MODEL_DMG_B:
case GB_MODEL_SGB_NTSC: /* Unverified*/
case GB_MODEL_SGB_PAL: /* Unverified */
case GB_MODEL_SGB_NO_SFC: /* Unverified */
case GB_MODEL_SGB_NTSC_NO_SFC: /* Unverified */
case GB_MODEL_SGB_PAL_NO_SFC: /* Unverified */
case GB_MODEL_SGB2:
case GB_MODEL_SGB2_NO_SFC: {
uint8_t temp;
@ -857,7 +869,7 @@ void GB_reset(GB_gameboy_t *gb)
gb->mbc_rom_bank = 1;
gb->last_rtc_second = time(NULL);
gb->cgb_ram_bank = 1;
gb->io_registers[GB_IO_JOYP] = 0xF;
gb->io_registers[GB_IO_JOYP] = 0xCF;
gb->mbc_ram_size = mbc_ram_size;
if (GB_is_cgb(gb)) {
gb->ram_size = 0x1000 * 8;
@ -924,7 +936,7 @@ void GB_reset(GB_gameboy_t *gb)
gb->nontrivial_jump_state = NULL;
}
gb->magic = (uintptr_t)'SAME';
gb->magic = state_magic();
}
void GB_switch_model_and_reset(GB_gameboy_t *gb, GB_model_t model)
@ -1016,12 +1028,12 @@ void GB_set_clock_multiplier(GB_gameboy_t *gb, double multiplier)
uint32_t GB_get_clock_rate(GB_gameboy_t *gb)
{
if (gb->model == GB_MODEL_SGB_NTSC) {
return SGB_NTSC_FREQUENCY * gb->clock_multiplier;
}
if (gb->model == GB_MODEL_SGB_PAL) {
if (gb->model & GB_MODEL_PAL_BIT) {
return SGB_PAL_FREQUENCY * gb->clock_multiplier;
}
if ((gb->model & ~GB_MODEL_NO_SFC_BIT) == GB_MODEL_SGB) {
return SGB_NTSC_FREQUENCY * gb->clock_multiplier;
}
return CPU_FREQUENCY * gb->clock_multiplier;
}

View File

@ -1,7 +1,6 @@
#ifndef GB_h
#define GB_h
#define typeof __typeof__
#define _XOPEN_SOURCE 500
#include <stdbool.h>
#include <stdint.h>
#include <time.h>
@ -71,7 +70,9 @@ typedef enum {
GB_MODEL_SGB = 0x004,
GB_MODEL_SGB_NTSC = GB_MODEL_SGB,
GB_MODEL_SGB_PAL = GB_MODEL_SGB | GB_MODEL_PAL_BIT,
GB_MODEL_SGB_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT,
GB_MODEL_SGB_NTSC_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT,
GB_MODEL_SGB_NO_SFC = GB_MODEL_SGB_NTSC_NO_SFC,
GB_MODEL_SGB_PAL_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT | GB_MODEL_PAL_BIT,
// GB_MODEL_MGB = 0x100,
GB_MODEL_SGB2 = 0x101,
GB_MODEL_SGB2_NO_SFC = GB_MODEL_SGB2 | GB_MODEL_NO_SFC_BIT,
@ -277,10 +278,6 @@ typedef struct {
This struct is not packed, but dumped sections exclusively use types that have the same alignment in both 32 and 64
bit platforms. */
/* We make sure bool is 1 for cross-platform save state compatibility. */
/* Todo: We might want to typedef our own bool if this prevents SameBoy from working on specific platforms. */
//_Static_assert(sizeof(bool) == 1, "sizeof(bool) != 1");
#ifdef GB_INTERNAL
struct GB_gameboy_s {
#else
@ -544,6 +541,7 @@ struct GB_gameboy_internal_s {
GB_icd_pixel_callback_t icd_pixel_callback;
GB_icd_vreset_callback_t icd_hreset_callback;
GB_icd_vreset_callback_t icd_vreset_callback;
GB_read_memory_callback_t read_memory_callback;
/* IR */
long cycles_since_ir_change; // In 8MHz units

View File

@ -3,7 +3,7 @@
void GB_update_joyp(GB_gameboy_t *gb)
{
if (gb->model & GB_MODEL_SGB_NO_SFC) return;
if (gb->model & GB_MODEL_NO_SFC_BIT) return;
uint8_t key_selection = 0;
uint8_t previous_state = 0;
@ -12,7 +12,7 @@ void GB_update_joyp(GB_gameboy_t *gb)
previous_state = gb->io_registers[GB_IO_JOYP] & 0xF;
key_selection = (gb->io_registers[GB_IO_JOYP] >> 4) & 3;
gb->io_registers[GB_IO_JOYP] &= 0xF0;
uint8_t current_player = gb->sgb? gb->sgb->current_player : 0;
uint8_t current_player = gb->sgb? (gb->sgb->current_player & (gb->sgb->player_count - 1) & 3) : 0;
switch (key_selection) {
case 3:
if (gb->sgb && gb->sgb->player_count > 1) {
@ -73,7 +73,7 @@ void GB_icd_set_joyp(GB_gameboy_t *gb, uint8_t value)
if (previous_state & ~(gb->io_registers[GB_IO_JOYP] & 0xF)) {
gb->io_registers[GB_IO_IF] |= 0x10;
}
gb->io_registers[GB_IO_JOYP] |= 0xC0;
}
void GB_set_key_state(GB_gameboy_t *gb, GB_key_t index, bool pressed)

View File

@ -273,7 +273,8 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
case GB_MODEL_DMG_B:
case GB_MODEL_SGB_NTSC:
case GB_MODEL_SGB_PAL:
case GB_MODEL_SGB_NO_SFC:
case GB_MODEL_SGB_NTSC_NO_SFC:
case GB_MODEL_SGB_PAL_NO_SFC:
case GB_MODEL_SGB2:
case GB_MODEL_SGB2_NO_SFC:
;
@ -310,7 +311,6 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
(gb->apu.is_active[GB_WAVE] ? (gb->apu.samples[GB_WAVE]) : 0);
case GB_IO_JOYP:
GB_timing_sync(gb);
return gb->io_registers[addr & 0xFF] | 0xC0;
case GB_IO_TMA:
case GB_IO_LCDC:
case GB_IO_SCY:
@ -422,11 +422,9 @@ static GB_read_function_t * const read_map[] =
read_ram, read_high_memory, /* EXXX FXXX */
};
static GB_read_memory_callback_t GB_read_memory_callback_v = 0;
void GB_set_read_memory_callback(GB_gameboy_t *gb, GB_read_memory_callback_t callback)
{
GB_read_memory_callback_v = callback;
gb->read_memory_callback = callback;
}
uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr)
@ -437,9 +435,9 @@ uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr)
if (is_addr_in_dma_use(gb, addr)) {
addr = gb->dma_current_src;
}
if (GB_read_memory_callback_v) {
if (gb->read_memory_callback) {
uint8_t data = read_map[addr >> 12](gb, addr);
data = GB_read_memory_callback_v(gb, addr, data);
data = gb->read_memory_callback(gb, addr, data);
return data;
}
return read_map[addr >> 12](gb, addr);
@ -599,7 +597,8 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
case GB_MODEL_DMG_B:
case GB_MODEL_SGB_NTSC:
case GB_MODEL_SGB_PAL:
case GB_MODEL_SGB_NO_SFC:
case GB_MODEL_SGB_NTSC_NO_SFC:
case GB_MODEL_SGB_PAL_NO_SFC:
case GB_MODEL_SGB2:
case GB_MODEL_SGB2_NO_SFC:
case GB_MODEL_CGB_E:
@ -754,9 +753,15 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
return;
case GB_IO_JOYP:
gb->io_registers[GB_IO_JOYP] = value & 0xF0;
GB_sgb_write(gb, value);
GB_update_joyp(gb);
if (gb->joyp_write_callback) {
gb->joyp_write_callback(gb, value);
GB_update_joyp(gb);
}
else if ((gb->io_registers[GB_IO_JOYP] & 0x30) != (value & 0x30)) {
GB_sgb_write(gb, value);
gb->io_registers[GB_IO_JOYP] = value & 0xF0;
GB_update_joyp(gb);
}
return;
case GB_IO_BIOS:

View File

@ -72,6 +72,75 @@ static inline void load_attribute_file(GB_gameboy_t *gb, unsigned file_index)
}
}
static const uint16_t built_in_palettes[] =
{
0x67BF, 0x265B, 0x10B5, 0x2866,
0x637B, 0x3AD9, 0x0956, 0x0000,
0x7F1F, 0x2A7D, 0x30F3, 0x4CE7,
0x57FF, 0x2618, 0x001F, 0x006A,
0x5B7F, 0x3F0F, 0x222D, 0x10EB,
0x7FBB, 0x2A3C, 0x0015, 0x0900,
0x2800, 0x7680, 0x01EF, 0x2FFF,
0x73BF, 0x46FF, 0x0110, 0x0066,
0x533E, 0x2638, 0x01E5, 0x0000,
0x7FFF, 0x2BBF, 0x00DF, 0x2C0A,
0x7F1F, 0x463D, 0x74CF, 0x4CA5,
0x53FF, 0x03E0, 0x00DF, 0x2800,
0x433F, 0x72D2, 0x3045, 0x0822,
0x7FFA, 0x2A5F, 0x0014, 0x0003,
0x1EED, 0x215C, 0x42FC, 0x0060,
0x7FFF, 0x5EF7, 0x39CE, 0x0000,
0x4F5F, 0x630E, 0x159F, 0x3126,
0x637B, 0x121C, 0x0140, 0x0840,
0x66BC, 0x3FFF, 0x7EE0, 0x2C84,
0x5FFE, 0x3EBC, 0x0321, 0x0000,
0x63FF, 0x36DC, 0x11F6, 0x392A,
0x65EF, 0x7DBF, 0x035F, 0x2108,
0x2B6C, 0x7FFF, 0x1CD9, 0x0007,
0x53FC, 0x1F2F, 0x0E29, 0x0061,
0x36BE, 0x7EAF, 0x681A, 0x3C00,
0x7BBE, 0x329D, 0x1DE8, 0x0423,
0x739F, 0x6A9B, 0x7293, 0x0001,
0x5FFF, 0x6732, 0x3DA9, 0x2481,
0x577F, 0x3EBC, 0x456F, 0x1880,
0x6B57, 0x6E1B, 0x5010, 0x0007,
0x0F96, 0x2C97, 0x0045, 0x3200,
0x67FF, 0x2F17, 0x2230, 0x1548,
};
static const struct {
char name[16];
unsigned palette_index;
} palette_assignments[] =
{
{"ZELDA", 5},
{"SUPER MARIOLAND", 6},
{"MARIOLAND2", 0x14},
{"SUPERMARIOLAND3", 2},
{"KIRBY DREAM LAND", 0xB},
{"HOSHINOKA-BI", 0xB},
{"KIRBY'S PINBALL", 3},
{"YOSSY NO TAMAGO", 0xC},
{"MARIO & YOSHI", 0xC},
{"YOSSY NO COOKIE", 4},
{"YOSHI'S COOKIE", 4},
{"DR.MARIO", 0x12},
{"TETRIS", 0x11},
{"YAKUMAN", 0x13},
{"METROID2", 0x1F},
{"KAERUNOTAMENI", 9},
{"GOLF", 0x18},
{"ALLEY WAY", 0x16},
{"BASEBALL", 0xF},
{"TENNIS", 0x17},
{"F1RACE", 0x1E},
{"KID ICARUS", 0xE},
{"QIX", 0x19},
{"SOLARSTRIKER", 7},
{"X", 0x1C},
{"GBWARS", 0x15},
};
static void command_ready(GB_gameboy_t *gb)
{
/* SGB header commands are used to send the contents of the header to the SNES CPU.
@ -81,6 +150,8 @@ static void command_ready(GB_gameboy_t *gb)
0xE content bytes. The last command, FB, is padded with zeros, so information past the header is not sent. */
if ((gb->sgb->command[0] & 0xF1) == 0xF1) {
if(gb->boot_rom_finished) return;
uint8_t checksum = 0;
for (unsigned i = 2; i < 0x10; i++) {
checksum += gb->sgb->command[i];
@ -90,14 +161,23 @@ static void command_ready(GB_gameboy_t *gb)
gb->sgb->disable_commands = true;
return;
}
if (gb->sgb->command[0] == 0xf9) {
if (gb->sgb->command[0xc] != 3) { // SGB Flag
gb->sgb->disable_commands = true;
}
unsigned index = (gb->sgb->command[0] >> 1) & 7;
if (index > 5) {
return;
}
else if (gb->sgb->command[0] == 0xfb) {
if (gb->sgb->command[0x3] != 0x33) { // Old licensee code
memcpy(&gb->sgb->received_header[index * 14], &gb->sgb->command[2], 14);
if (gb->sgb->command[0] == 0xfb) {
if (gb->sgb->received_header[0x42] != 3 || gb->sgb->received_header[0x47] != 0x33) {
gb->sgb->disable_commands = true;
for (unsigned i = 0; i < sizeof(palette_assignments) / sizeof(palette_assignments[0]); i++) {
if (memcmp(palette_assignments[i].name, &gb->sgb->received_header[0x30], sizeof(palette_assignments[i].name)) == 0) {
gb->sgb->effective_palettes[0] = built_in_palettes[palette_assignments[i].palette_index * 4 - 4];
gb->sgb->effective_palettes[1] = built_in_palettes[palette_assignments[i].palette_index * 4 + 1 - 4];
gb->sgb->effective_palettes[2] = built_in_palettes[palette_assignments[i].palette_index * 4 + 2 - 4];
gb->sgb->effective_palettes[3] = built_in_palettes[palette_assignments[i].palette_index * 4 + 3 - 4];
break;
}
}
}
}
return;
@ -257,8 +337,15 @@ static void command_ready(GB_gameboy_t *gb)
// Not supported, but used by almost all SGB games for hot patching, so let's mute the warning for this
break;
case MLT_REQ:
gb->sgb->player_count = (uint8_t[]){1, 2, 1, 4}[gb->sgb->command[1] & 3];
gb->sgb->current_player = gb->sgb->player_count - 1;
if (gb->sgb->player_count == 1) {
gb->sgb->current_player = 0;
}
gb->sgb->player_count = (gb->sgb->command[1] & 3) + 1; /* Todo: When breaking save state comaptibility,
fix this to be 0 based. */
if (gb->sgb->player_count == 3) {
gb->sgb->current_player++;
}
gb->sgb->mlt_lock = true;
break;
case CHR_TRN:
gb->sgb->vram_transfer_countdown = 2;
@ -298,30 +385,33 @@ static void command_ready(GB_gameboy_t *gb)
}
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value)
{
if (gb->joyp_write_callback) {
gb->joyp_write_callback(gb, value);
}
{
if (!GB_is_sgb(gb)) return;
if (!GB_is_hle_sgb(gb)) {
/* Notify via callback */
return;
}
if (gb->sgb->disable_commands) return;
if (gb->sgb->command_write_index >= sizeof(gb->sgb->command) * 8) return;
if (gb->sgb->command_write_index >= sizeof(gb->sgb->command) * 8) {
return;
}
uint16_t command_size = (gb->sgb->command[0] & 7 ?: 1) * SGB_PACKET_SIZE * 8;
if ((gb->sgb->command[0] & 0xF1) == 0xF1) {
command_size = SGB_PACKET_SIZE * 8;
}
if ((value & 0x20) == 0 && (gb->io_registers[GB_IO_JOYP] & 0x20) != 0) {
gb->sgb->mlt_lock ^= true;
}
switch ((value >> 4) & 3) {
case 3:
gb->sgb->ready_for_pulse = true;
/* TODO: This is the logic used by BGB which *should* work for most/all games, but a proper test ROM is needed */
if (gb->sgb->player_count > 1 && (gb->io_registers[GB_IO_JOYP] & 0x30) == 0x10) {
if ((gb->sgb->player_count & 1) == 0 && !gb->sgb->mlt_lock) {
gb->sgb->current_player++;
gb->sgb->current_player &= gb->sgb->player_count - 1;
gb->sgb->current_player &= 3;
gb->sgb->mlt_lock = true;
}
break;
@ -382,22 +472,9 @@ void GB_sgb_write(GB_gameboy_t *gb, uint8_t value)
}
}
static inline uint8_t scale_channel(uint8_t x)
{
return (x << 3) | (x >> 2);
}
static uint32_t convert_rgb15(GB_gameboy_t *gb, uint16_t color)
{
uint8_t r = (color) & 0x1F;
uint8_t g = (color >> 5) & 0x1F;
uint8_t b = (color >> 10) & 0x1F;
r = scale_channel(r);
g = scale_channel(g);
b = scale_channel(b);
return gb->rgb_encode_callback(gb, r, g, b);
return GB_convert_rgb15(gb, color);
}
static uint32_t convert_rgb15_with_fade(GB_gameboy_t *gb, uint16_t color, uint8_t fade)
@ -410,11 +487,9 @@ static uint32_t convert_rgb15_with_fade(GB_gameboy_t *gb, uint16_t color, uint8_
if (g >= 0x20) g = 0;
if (b >= 0x20) b = 0;
r = scale_channel(r);
g = scale_channel(g);
b = scale_channel(b);
color = r | (g << 5) | (b << 10);
return gb->rgb_encode_callback(gb, r, g, b);
return GB_convert_rgb15(gb, color);
}
#include <stdio.h>
@ -679,10 +754,10 @@ void GB_sgb_load_default_data(GB_gameboy_t *gb)
/* Re-center */
memmove(&gb->sgb->border.map[25 * 32 + 1], &gb->sgb->border.map[25 * 32], (32 * 3 - 1) * sizeof(gb->sgb->border.map[0]));
}
gb->sgb->effective_palettes[0] = 0x639E;
gb->sgb->effective_palettes[1] = 0x263A;
gb->sgb->effective_palettes[2] = 0x10D4;
gb->sgb->effective_palettes[3] = 0x2866;
gb->sgb->effective_palettes[0] = built_in_palettes[0];
gb->sgb->effective_palettes[1] = built_in_palettes[1];
gb->sgb->effective_palettes[2] = built_in_palettes[2];
gb->sgb->effective_palettes[3] = built_in_palettes[3];
}
static double fm_synth(double phase)

View File

@ -49,6 +49,12 @@ struct GB_sgb_s {
/* Intro */
int16_t intro_animation;
/* GB Header */
uint8_t received_header[0x54];
/* Multiplayer (cont) */
bool mlt_lock;
};
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value);

View File

@ -33,6 +33,7 @@ static const GB_conflict_t cgb_conflict_map[0x80] = {
/* Todo: most values not verified, and probably differ between revisions */
};
/* Todo: verify on an MGB */
static const GB_conflict_t dmg_conflict_map[0x80] = {
[GB_IO_IF] = GB_CONFLICT_WRITE_CPU,
[GB_IO_LYC] = GB_CONFLICT_READ_OLD,
@ -40,7 +41,6 @@ static const GB_conflict_t dmg_conflict_map[0x80] = {
[GB_IO_SCY] = GB_CONFLICT_READ_NEW,
[GB_IO_STAT] = GB_CONFLICT_STAT_DMG,
/* Todo: these are GB_CONFLICT_READ_NEW on MGB/SGB2 */
[GB_IO_BGP] = GB_CONFLICT_PALETTE_DMG,
[GB_IO_OBP0] = GB_CONFLICT_PALETTE_DMG,
[GB_IO_OBP1] = GB_CONFLICT_PALETTE_DMG,
@ -51,6 +51,24 @@ static const GB_conflict_t dmg_conflict_map[0x80] = {
[GB_IO_SCX] = GB_CONFLICT_READ_NEW,
};
/* Todo: Verify on an SGB1 */
static const GB_conflict_t sgb_conflict_map[0x80] = {
[GB_IO_IF] = GB_CONFLICT_WRITE_CPU,
[GB_IO_LYC] = GB_CONFLICT_READ_OLD,
[GB_IO_LCDC] = GB_CONFLICT_READ_NEW,
[GB_IO_SCY] = GB_CONFLICT_READ_NEW,
[GB_IO_STAT] = GB_CONFLICT_STAT_DMG,
[GB_IO_BGP] = GB_CONFLICT_READ_NEW,
[GB_IO_OBP0] = GB_CONFLICT_READ_NEW,
[GB_IO_OBP1] = GB_CONFLICT_READ_NEW,
/* Todo: these were not verified at all */
[GB_IO_WY] = GB_CONFLICT_READ_NEW,
[GB_IO_WX] = GB_CONFLICT_READ_NEW,
[GB_IO_SCX] = GB_CONFLICT_READ_NEW,
};
static uint8_t cycle_read(GB_gameboy_t *gb, uint16_t addr)
{
if (gb->pending_cycles) {
@ -92,7 +110,17 @@ static void cycle_write(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
assert(gb->pending_cycles);
GB_conflict_t conflict = GB_CONFLICT_READ_OLD;
if ((addr & 0xFF80) == 0xFF00) {
conflict = (GB_is_cgb(gb)? cgb_conflict_map : dmg_conflict_map)[addr & 0x7F];
const GB_conflict_t *map = NULL;
if (GB_is_cgb(gb)) {
map = cgb_conflict_map;
}
else if (GB_is_sgb(gb)) {
map = sgb_conflict_map;
}
else {
map = dmg_conflict_map;
}
conflict = map[addr & 0x7F];
}
switch (conflict) {
case GB_CONFLICT_READ_OLD: