Update to bsnes v107r7 beta release.

byuu says:

  - bsnes: reset all thread clocks on power cycle
  - bsnes: use uint64 instead of uint128 for scheduler clocks
  - bsnes: use float instead of double for audio resampling
  - bsnes: begin work of integrating SameBoy (incomplete; needs
    additional features)
This commit is contained in:
Tim Allen 2019-07-15 21:02:13 +10:00
parent 52f34ea470
commit bd8e94a7c7
62 changed files with 13887 additions and 101 deletions

View File

@ -1,3 +1,5 @@
#define double float
namespace Emulator {
#include "stream.cpp"
@ -69,3 +71,5 @@ auto Audio::process() -> void {
}
}
#undef double

View File

@ -1,4 +1,5 @@
#pragma once
#define double float
#include <nall/dsp/iir/dc-removal.hpp>
#include <nall/dsp/iir/one-pole.hpp>
@ -81,3 +82,5 @@ private:
extern Audio audio;
}
#undef double

View File

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

View File

@ -17,7 +17,7 @@ struct Platform {
virtual auto open(uint id, string name, vfs::file::mode mode, bool required = false) -> vfs::shared::file { return {}; }
virtual auto load(uint id, string name, string type, vector<string> options = {}) -> Load { return {}; }
virtual auto videoFrame(const uint16* data, uint pitch, uint width, uint height, uint scale) -> void {}
virtual auto audioFrame(const double* samples, uint channels) -> void {}
virtual auto audioFrame(const float* samples, uint channels) -> void {}
virtual auto inputPoll(uint port, uint device, uint input) -> int16 { return 0; }
virtual auto inputRumble(uint port, uint device, uint input, bool enable) -> void {}
virtual auto dipSettings(Markup::Node node) -> uint { return 0; }

View File

@ -1,4 +1,5 @@
#pragma once
#define uintmax uint64
namespace Emulator {
@ -24,11 +25,15 @@ struct Scheduler {
auto primary(Thread& thread) -> void {
_master = _resume = thread.handle();
uintmax clock = 0;
for(auto& thread : _threads) {
thread->_clock = clock++; //this bias prioritizes threads appended earlier first
}
}
auto append(Thread& thread) -> bool {
if(_threads.find(&thread)) return false;
thread._clock += _threads.size(); //this bias prioritizes threads appended earlier first
thread._clock = _threads.size();
return _threads.append(&thread), true;
}
@ -89,3 +94,5 @@ private:
};
}
#undef uintmax

View File

@ -1,4 +1,5 @@
#pragma once
#define uintmax uint64
namespace Emulator {
@ -59,3 +60,5 @@ protected:
};
}
#undef uintmax

View File

@ -11,3 +11,28 @@ obj/gb-memory.o: gb/memory/memory.cpp
obj/gb-cpu.o: gb/cpu/cpu.cpp
obj/gb-ppu.o: gb/ppu/ppu.cpp
obj/gb-apu.o: gb/apu/apu.cpp
flags += -DGB_INTERNAL -Wno-multichar -Dtypeof=__typeof__
options += -I../sameboy
objects += sb-apu sb-camera sb-debugger sb-display sb-gb sb-joypad sb-mbc
objects += sb-memory sb-printer sb-random sb-rewind sb-save_state sb-sgb
objects += sb-sm83_cpu sb-sm83_disassembler sb-symbol_hash sb-timing
obj/sb-apu.o: ../sameboy/Core/apu.c
obj/sb-camera.o: ../sameboy/Core/camera.c
obj/sb-debugger.o: ../sameboy/Core/debugger.c
obj/sb-display.o: ../sameboy/Core/display.c
obj/sb-gb.o: ../sameboy/Core/gb.c
obj/sb-joypad.o: ../sameboy/Core/joypad.c
obj/sb-mbc.o: ../sameboy/Core/mbc.c
obj/sb-memory.o: ../sameboy/Core/memory.c
obj/sb-printer.o: ../sameboy/Core/printer.c
obj/sb-random.o: ../sameboy/Core/random.c
obj/sb-rewind.o: ../sameboy/Core/rewind.c
obj/sb-save_state.o: ../sameboy/Core/save_state.c
obj/sb-sgb.o: ../sameboy/Core/sgb.c
obj/sb-sm83_cpu.o: ../sameboy/Core/sm83_cpu.c
obj/sb-sm83_disassembler.o: ../sameboy/Core/sm83_disassembler.c
obj/sb-symbol_hash.o: ../sameboy/Core/symbol_hash.c
obj/sb-timing.o: ../sameboy/Core/timing.c

View File

@ -22,9 +22,9 @@ auto APU::main() -> void {
sequencer.run();
if(!Model::SuperGameBoy()) {
stream->sample(sequencer.left / 32768.0, sequencer.right / 32768.0);
stream->sample(float(sequencer.left / 32768.0), float(sequencer.right / 32768.0));
} else {
double samples[] = {sequencer.left / 32768.0, sequencer.right / 32768.0};
float samples[] = {float(sequencer.left / 32768.0), float(sequencer.right / 32768.0)};
superGameBoy->audioSample(samples, 2);
}

View File

@ -65,7 +65,7 @@ struct GameBoyColorInterface : Interface {
};
struct SuperGameBoyInterface {
virtual auto audioSample(const double* samples, uint channels) -> void = 0;
virtual auto audioSample(const float* samples, uint channels) -> void = 0;
virtual auto inputPoll(uint port, uint device, uint id) -> int16 = 0;
virtual auto lcdScanline() -> void = 0;

View File

@ -28,7 +28,7 @@ auto WDC65816::idle6(uint16 address) -> void {
}
auto WDC65816::fetch() -> uint8 {
return readPC(PC.b << 16 | PC.w++);
return read(PC.b << 16 | PC.w++);
}
auto WDC65816::pull() -> uint8 {

View File

@ -10,7 +10,6 @@ struct WDC65816 {
virtual auto idle() -> void = 0;
virtual auto idleBranch() -> void {}
virtual auto idleJump() -> void {}
virtual auto readPC(uint addr) -> uint8 = 0;
virtual auto read(uint addr) -> uint8 = 0;
virtual auto write(uint addr, uint8 data) -> void = 0;
virtual auto lastCycle() -> void = 0;

View File

@ -0,0 +1,21 @@
const uint8_t ICD::SGB1BootROM[256] = {
49,254,255,62,48,224,0,175,33,255,159,50,203,124,32,251,33,38,255,14,17,62,128,50,226,12,62,243,226,50,62,119,
119,62,252,224,71,33,95,192,14,8,175,50,13,32,252,17,79,1,62,251,14,6,245,6,0,26,27,50,128,71,13,32,
248,50,241,50,14,14,214,2,254,239,32,234,17,4,1,33,16,128,26,205,211,0,205,212,0,19,123,254,52,32,243,17,
230,0,6,8,26,19,34,35,5,32,249,62,25,234,16,153,33,47,153,14,12,61,40,8,50,13,32,249,46,15,24,243,
62,145,224,64,33,0,192,14,0,62,0,226,62,48,226,6,16,30,8,42,87,203,66,62,16,32,2,62,32,226,62,48,
226,203,26,29,32,239,5,32,232,62,32,226,62,48,226,205,194,0,125,254,96,32,210,14,19,62,193,226,12,62,7,226,
24,58,22,4,240,68,254,144,32,250,30,0,29,32,253,21,32,242,201,79,6,4,197,203,17,23,193,203,17,23,5,32,
245,34,35,34,35,201,60,66,185,165,185,165,66,60,0,0,0,0,0,0,0,0,0,0,0,0,0,0,62,1,224,80,
};
const uint8_t ICD::SGB2BootROM[256] = {
49,254,255,62,48,224,0,175,33,255,159,50,203,124,32,251,33,38,255,14,17,62,128,50,226,12,62,243,226,50,62,119,
119,62,252,224,71,33,95,192,14,8,175,50,13,32,252,17,79,1,62,251,14,6,245,6,0,26,27,50,128,71,13,32,
248,50,241,50,14,14,214,2,254,239,32,234,17,4,1,33,16,128,26,205,211,0,205,212,0,19,123,254,52,32,243,17,
230,0,6,8,26,19,34,35,5,32,249,62,25,234,16,153,33,47,153,14,12,61,40,8,50,13,32,249,46,15,24,243,
62,145,224,64,33,0,192,14,0,62,0,226,62,48,226,6,16,30,8,42,87,203,66,62,16,32,2,62,32,226,62,48,
226,203,26,29,32,239,5,32,232,62,32,226,62,48,226,205,194,0,125,254,96,32,210,14,19,62,193,226,12,62,7,226,
24,58,22,4,240,68,254,144,32,250,30,0,29,32,253,21,32,242,201,79,6,4,197,203,17,23,193,203,17,23,5,32,
245,34,35,34,35,201,60,66,185,165,185,165,66,60,0,0,0,0,0,0,0,0,0,0,0,0,0,0,62,255,224,80,
};

View File

@ -7,8 +7,15 @@ ICD icd;
#include "platform.cpp"
#include "interface.cpp"
#include "io.cpp"
#include "boot-roms.cpp"
#include "serialization.cpp"
namespace SameBoy {
static auto rgbEncode(GB_gameboy_t*, uint8_t r, uint8_t g, uint8_t b) -> uint32_t {
return r << 16 | g << 8 | b << 0;
}
}
auto ICD::Enter() -> void {
while(true) {
if(scheduler.synchronizing()) GameBoy::system.runToSave();
@ -23,19 +30,40 @@ auto ICD::main() -> void {
step(GameBoy::system._clocksExecuted);
GameBoy::system._clocksExecuted = 0;
} else { //DMG halted
stream->sample(0.0, 0.0);
stream->sample(float(0.0), float(0.0));
step(2); //two clocks per audio sample
}
synchronize(cpu);
}
auto ICD::load() -> bool {
information = {};
GB_random_set_enabled(false);
GB_init(&sameboy, GB_MODEL_DMG_B);
if(Frequency == 0) {
GB_load_boot_rom_from_buffer(&sameboy, (const unsigned char*)&SGB1BootROM, 256);
} else {
GB_load_boot_rom_from_buffer(&sameboy, (const unsigned char*)&SGB2BootROM, 256);
}
GB_set_pixels_output(&sameboy, bitmap);
GB_set_rgb_encode_callback(&sameboy, SameBoy::rgbEncode);
if(auto loaded = platform->load(ID::GameBoy, "Game Boy", "gb")) {
information.pathID = loaded.pathID;
} else return false;
if(auto fp = platform->open(pathID(), "program.rom", File::Read, File::Required)) {
auto size = fp->size();
auto data = (uint8_t*)malloc(size);
fp->read(data, size);
GB_load_rom_from_buffer(&sameboy, data, size);
} else return false;
GameBoy::superGameBoy = this;
GameBoy::system.load(&gameBoyInterface, GameBoy::System::Model::SuperGameBoy, cartridge.pathID());
return cartridge.loadGameBoy();
}
auto ICD::unload() -> void {
GB_free(&sameboy);
GameBoy::system.save();
GameBoy::system.unload();
}

View File

@ -1,6 +1,8 @@
struct ICD : Emulator::Platform, GameBoy::SuperGameBoyInterface, Thread {
shared_pointer<Emulator::Stream> stream;
inline auto pathID() const -> uint { return information.pathID; }
static auto Enter() -> void;
auto main() -> void;
@ -10,7 +12,7 @@ struct ICD : Emulator::Platform, GameBoy::SuperGameBoyInterface, Thread {
auto reset() -> void; //software reset
//platform.cpp
auto audioSample(const double* samples, uint channels) -> void override;
auto audioSample(const float* samples, uint channels) -> void override;
auto inputPoll(uint port, uint device, uint id) -> int16 override;
//interface.cpp
@ -23,6 +25,10 @@ struct ICD : Emulator::Platform, GameBoy::SuperGameBoyInterface, Thread {
auto readIO(uint addr, uint8 data) -> uint8;
auto writeIO(uint addr, uint8 data) -> void;
//boot-roms.cpp
static const uint8_t SGB1BootROM[256];
static const uint8_t SGB2BootROM[256];
//serialization.cpp
auto serialize(serializer&) -> void;
@ -62,6 +68,13 @@ private:
uint writeAddress;
GameBoy::GameBoyInterface gameBoyInterface;
struct Information {
uint pathID = 0;
} information;
GB_gameboy_t sameboy;
uint32_t bitmap[160 * 144];
};
extern ICD icd;

View File

@ -1,4 +1,4 @@
auto ICD::audioSample(const double* samples, uint channels) -> void {
auto ICD::audioSample(const float* samples, uint channels) -> void {
stream->write(samples);
}

View File

@ -34,7 +34,7 @@ auto MSU1::main() -> void {
}
}
stream->sample(left, right);
stream->sample(float(left), float(right));
step(1);
synchronize(cpu);
}

View File

@ -18,50 +18,6 @@ auto SA1::idleBranch() -> void {
if(r.pc.d & 1) idleJump();
}
auto SA1::readPC(uint address) -> uint8 {
r.mar = address;
uint8 data = r.mdr;
if((address & 0x40fe00) == 0x002200 //00-3f,80-bf:2200-23ff
) {
step();
return r.mdr = readIOSA1(address, data);
}
if((address & 0x408000) == 0x008000 //00-3f,80-bf:8000-ffff
|| (address & 0xc00000) == 0xc00000 //c0-ff:0000-ffff
) {
step();
if(rom.conflict()) step();
return r.mdr = rom.readSA1(address, data);
}
if((address & 0x40e000) == 0x006000 //00-3f,80-bf:6000-7fff
|| (address & 0xf00000) == 0x400000 //40-4f:0000-ffff
|| (address & 0xf00000) == 0x600000 //60-6f:0000-ffff
) {
step();
step();
if(bwram.conflict()) step();
if(bwram.conflict()) step();
if((address & 1 << 22) && (address & 1 << 21)) return r.mdr = bwram.readBitmap(address, data);
if((address & 1 << 22)) return r.mdr = bwram.readLinear(address, data);
return r.mdr = bwram.readSA1(address, data);
}
if((address & 0x40f800) == 0x000000 //00-3f,80-bf:0000-07ff
|| (address & 0x40f800) == 0x003000 //00-3f,80-bf:3000-37ff
) {
step();
if(iram.conflict()) step();
if(iram.conflict()) step();
return r.mdr = iram.readSA1(address, data);
}
step();
return data;
}
auto SA1::read(uint address) -> uint8 {
r.mar = address;
uint8 data = r.mdr;

View File

@ -36,7 +36,6 @@ struct SA1 : Processor::WDC65816, Thread {
alwaysinline auto idle() -> void override;
alwaysinline auto idleJump() -> void override;
alwaysinline auto idleBranch() -> void override;
alwaysinline auto readPC(uint address) -> uint8 override;
alwaysinline auto read(uint address) -> uint8 override;
alwaysinline auto write(uint address, uint8 data) -> void override;
auto readVBR(uint address, uint8 data = 0) -> uint8;

View File

@ -22,7 +22,6 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter {
//memory.cpp
auto idle() -> void override;
auto readPC(uint addr) -> uint8 override;
auto read(uint addr) -> uint8 override;
auto write(uint addr, uint8 data) -> void override;
auto readDisassembler(uint addr) -> uint8 override;

View File

@ -6,45 +6,6 @@ auto CPU::idle() -> void {
aluEdge();
}
auto CPU::readPC(uint address) -> uint8 {
status.irqLock = false;
if(address & 0x408000) {
if(address & 0x800000 && io.fastROM) {
status.clockCount = 6;
dmaEdge();
r.mar = address;
step<6,0>();
} else {
status.clockCount = 8;
dmaEdge();
r.mar = address;
step<8,0>();
}
} else if(address + 0x6000 & 0x4000) {
status.clockCount = 8;
dmaEdge();
r.mar = address;
step<8,0>();
} else if(address - 0x4000 & 0x7e00) {
status.clockCount = 6;
dmaEdge();
r.mar = address;
step<6,0>();
} else {
status.clockCount = 12;
dmaEdge();
r.mar = address;
step<12,0>();
}
auto data = bus.read(address, r.mdr);
aluEdge();
//$00-3f,80-bf:4000-43ff reads are internal to CPU, and do not update the MDR
if((address & 0x40fc00) != 0x4000) r.mdr = data;
return data;
}
auto CPU::read(uint address) -> uint8 {
status.irqLock = false;

View File

@ -19,7 +19,7 @@ void DSP::main() {
signed count = spc_dsp.sample_count();
if(count > 0) {
for(unsigned n = 0; n < count; n += 2) {
stream->sample(samplebuffer[n + 0] / 32768.0, samplebuffer[n + 1] / 32768.0);
stream->sample(samplebuffer[n + 0] / 32768.0f, samplebuffer[n + 1] / 32768.0f);
}
spc_dsp.set_output(samplebuffer, 8192);
}

View File

@ -17,6 +17,10 @@
#include <processor/wdc65816/wdc65816.hpp>
#include <gb/gb.hpp>
extern "C" {
#include <sameboy/Core/gb.h>
#include <sameboy/Core/random.h>
}
namespace SuperFamicom {
#define platform Emulator::platform

View File

@ -46,12 +46,14 @@ else ifneq ($(filter $(platform),linux bsd),)
mkdir -p $(prefix)/share/icons/
mkdir -p $(prefix)/share/$(name)/
mkdir -p $(prefix)/share/$(name)/Database/
mkdir -p $(prefix)/share/$(name)/Firmware/
mkdir -p $(prefix)/share/$(name)/Locale/
cp out/$(name) $(prefix)/bin/$(name)
cp $(ui)/resource/$(name).desktop $(prefix)/share/applications/$(name).desktop
cp $(ui)/resource/$(name).png $(prefix)/share/icons/$(name).png
cp Database/* $(prefix)/share/$(name)/Database/
cp ../icarus/Database/* $(prefix)/share/$(name)/Database/
cp Firmware/* $(prefix)/share/$(name)/Firmware/
cp Locale/* $(prefix)/share/$(name)/Locale/
endif

View File

@ -255,8 +255,9 @@ auto Program::videoFrame(const uint16* data, uint pitch, uint width, uint height
}
}
auto Program::audioFrame(const double* samples, uint channels) -> void {
audio.output(samples);
auto Program::audioFrame(const float* samples, uint channels) -> void {
double frame[] = {samples[0], samples[1]};
audio.output(frame);
}
auto Program::inputPoll(uint port, uint device, uint input) -> int16 {

View File

@ -10,7 +10,7 @@ struct Program : Lock, Emulator::Platform {
auto open(uint id, string name, vfs::file::mode mode, bool required) -> vfs::shared::file override;
auto load(uint id, string name, string type, vector<string> options = {}) -> Emulator::Platform::Load override;
auto videoFrame(const uint16* data, uint pitch, uint width, uint height, uint scale) -> void override;
auto audioFrame(const double* samples, uint channels) -> void override;
auto audioFrame(const float* samples, uint channels) -> void override;
auto inputPoll(uint port, uint device, uint input) -> int16 override;
auto inputRumble(uint port, uint device, uint input, bool enable) -> void override;

986
sameboy/Core/apu.c Normal file
View File

@ -0,0 +1,986 @@
#include <stdint.h>
#include <math.h>
#include <string.h>
#include <assert.h>
#include "gb.h"
#define likely(x) __builtin_expect((x), 1)
#define unlikely(x) __builtin_expect((x), 0)
static const uint8_t duties[] = {
0, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 1, 1, 1,
0, 1, 1, 1, 1, 1, 1, 0,
};
static void refresh_channel(GB_gameboy_t *gb, unsigned index, unsigned cycles_offset)
{
unsigned multiplier = gb->apu_output.cycles_since_render + cycles_offset - gb->apu_output.last_update[index];
gb->apu_output.summed_samples[index].left += gb->apu_output.current_sample[index].left * multiplier;
gb->apu_output.summed_samples[index].right += gb->apu_output.current_sample[index].right * multiplier;
gb->apu_output.last_update[index] = gb->apu_output.cycles_since_render + cycles_offset;
}
bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, unsigned index)
{
if (gb->model >= GB_MODEL_AGB) {
/* On the AGB, mixing is done digitally, so there are no per-channel
DACs. Instead, all channels are summed digital regardless of
whatever the DAC state would be on a CGB or earlier model. */
return true;
}
switch (index) {
case GB_SQUARE_1:
return gb->io_registers[GB_IO_NR12] & 0xF8;
case GB_SQUARE_2:
return gb->io_registers[GB_IO_NR22] & 0xF8;
case GB_WAVE:
return gb->apu.wave_channel.enable;
case GB_NOISE:
return gb->io_registers[GB_IO_NR42] & 0xF8;
}
return false;
}
static void update_sample(GB_gameboy_t *gb, unsigned index, int8_t value, unsigned cycles_offset)
{
if (gb->model >= GB_MODEL_AGB) {
/* On the AGB, because no analog mixing is done, the behavior of NR51 is a bit different.
A channel that is not connected to a terminal is idenitcal to a connected channel
playing PCM sample 0. */
gb->apu.samples[index] = value;
if (gb->apu_output.sample_rate) {
unsigned right_volume = (gb->io_registers[GB_IO_NR50] & 7) + 1;
unsigned left_volume = ((gb->io_registers[GB_IO_NR50] >> 4) & 7) + 1;
if (index == GB_WAVE) {
/* For some reason, channel 3 is inverted on the AGB */
value ^= 0xF;
}
GB_sample_t output;
if (gb->io_registers[GB_IO_NR51] & (1 << index)) {
output.right = (0xf - value * 2) * right_volume;
}
else {
output.right = 0xf * right_volume;
}
if (gb->io_registers[GB_IO_NR51] & (0x10 << index)) {
output.left = (0xf - value * 2) * left_volume;
}
else {
output.left = 0xf * left_volume;
}
if (*(uint32_t *)&(gb->apu_output.current_sample[index]) != *(uint32_t *)&output) {
refresh_channel(gb, index, cycles_offset);
gb->apu_output.current_sample[index] = output;
}
}
return;
}
if (!GB_apu_is_DAC_enabled(gb, index)) {
value = gb->apu.samples[index];
}
else {
gb->apu.samples[index] = value;
}
if (gb->apu_output.sample_rate) {
unsigned right_volume = 0;
if (gb->io_registers[GB_IO_NR51] & (1 << index)) {
right_volume = (gb->io_registers[GB_IO_NR50] & 7) + 1;
}
unsigned left_volume = 0;
if (gb->io_registers[GB_IO_NR51] & (0x10 << index)) {
left_volume = ((gb->io_registers[GB_IO_NR50] >> 4) & 7) + 1;
}
GB_sample_t output = {(0xf - value * 2) * left_volume, (0xf - value * 2) * right_volume};
if (*(uint32_t *)&(gb->apu_output.current_sample[index]) != *(uint32_t *)&output) {
refresh_channel(gb, index, cycles_offset);
gb->apu_output.current_sample[index] = output;
}
}
}
static double smooth(double x)
{
return 3*x*x - 2*x*x*x;
}
static void render(GB_gameboy_t *gb)
{
GB_sample_t output = {0,0};
UNROLL
for (unsigned i = 0; i < GB_N_CHANNELS; i++) {
double multiplier = CH_STEP;
if (gb->model < GB_MODEL_AGB) {
if (!GB_apu_is_DAC_enabled(gb, i)) {
gb->apu_output.dac_discharge[i] -= ((double) DAC_DECAY_SPEED) / gb->apu_output.sample_rate;
if (gb->apu_output.dac_discharge[i] < 0) {
multiplier = 0;
gb->apu_output.dac_discharge[i] = 0;
}
else {
multiplier *= smooth(gb->apu_output.dac_discharge[i]);
}
}
else {
gb->apu_output.dac_discharge[i] += ((double) DAC_ATTACK_SPEED) / gb->apu_output.sample_rate;
if (gb->apu_output.dac_discharge[i] > 1) {
gb->apu_output.dac_discharge[i] = 1;
}
else {
multiplier *= smooth(gb->apu_output.dac_discharge[i]);
}
}
}
if (likely(gb->apu_output.last_update[i] == 0)) {
output.left += gb->apu_output.current_sample[i].left * multiplier;
output.right += gb->apu_output.current_sample[i].right * multiplier;
}
else {
refresh_channel(gb, i, 0);
output.left += (signed long) gb->apu_output.summed_samples[i].left * multiplier
/ gb->apu_output.cycles_since_render;
output.right += (signed long) gb->apu_output.summed_samples[i].right * multiplier
/ gb->apu_output.cycles_since_render;
gb->apu_output.summed_samples[i] = (GB_sample_t){0, 0};
}
gb->apu_output.last_update[i] = 0;
}
gb->apu_output.cycles_since_render = 0;
GB_sample_t filtered_output = gb->apu_output.highpass_mode?
(GB_sample_t) {output.left - gb->apu_output.highpass_diff.left,
output.right - gb->apu_output.highpass_diff.right} :
output;
switch (gb->apu_output.highpass_mode) {
case GB_HIGHPASS_OFF:
gb->apu_output.highpass_diff = (GB_double_sample_t) {0, 0};
break;
case GB_HIGHPASS_ACCURATE:
gb->apu_output.highpass_diff = (GB_double_sample_t)
{output.left - filtered_output.left * gb->apu_output.highpass_rate,
output.right - filtered_output.right * gb->apu_output.highpass_rate};
break;
case GB_HIGHPASS_REMOVE_DC_OFFSET: {
unsigned mask = gb->io_registers[GB_IO_NR51];
unsigned left_volume = 0;
unsigned right_volume = 0;
UNROLL
for (unsigned i = GB_N_CHANNELS; i--;) {
if (gb->apu.is_active[i]) {
if (mask & 1) {
left_volume += (gb->io_registers[GB_IO_NR50] & 7) * CH_STEP * 0xF;
}
if (mask & 0x10) {
right_volume += ((gb->io_registers[GB_IO_NR50] >> 4) & 7) * CH_STEP * 0xF;
}
}
else {
left_volume += gb->apu_output.current_sample[i].left * CH_STEP;
right_volume += gb->apu_output.current_sample[i].right * CH_STEP;
}
mask >>= 1;
}
gb->apu_output.highpass_diff = (GB_double_sample_t)
{left_volume * (1 - gb->apu_output.highpass_rate) + gb->apu_output.highpass_diff.left * gb->apu_output.highpass_rate,
right_volume * (1 - gb->apu_output.highpass_rate) + gb->apu_output.highpass_diff.right * gb->apu_output.highpass_rate};
case GB_HIGHPASS_MAX:;
}
}
assert(gb->apu_output.sample_callback);
gb->apu_output.sample_callback(gb, &filtered_output);
}
static uint16_t new_sweep_sample_legnth(GB_gameboy_t *gb)
{
uint16_t delta = gb->apu.shadow_sweep_sample_legnth >> (gb->io_registers[GB_IO_NR10] & 7);
if (gb->io_registers[GB_IO_NR10] & 8) {
return gb->apu.shadow_sweep_sample_legnth - delta;
}
return gb->apu.shadow_sweep_sample_legnth + delta;
}
static void update_square_sample(GB_gameboy_t *gb, unsigned index)
{
if (gb->apu.square_channels[index].current_sample_index & 0x80) return;
uint8_t duty = gb->io_registers[index == GB_SQUARE_1? GB_IO_NR11 :GB_IO_NR21] >> 6;
update_sample(gb, index,
duties[gb->apu.square_channels[index].current_sample_index + duty * 8]?
gb->apu.square_channels[index].current_volume : 0,
0);
}
/* the effects of NRX2 writes on current volume are not well documented and differ
between models and variants. The exact behavior can only be verified on CGB as it
requires the PCM12 register. The behavior implemented here was verified on *my*
CGB, which might behave differently from other CGB revisions, as well as from the
DMG, MGB or SGB/2 */
static void nrx2_glitch(uint8_t *volume, uint8_t value, uint8_t old_value)
{
if (value & 8) {
(*volume)++;
}
if (((value ^ old_value) & 8)) {
*volume = 0x10 - *volume;
}
if ((value & 7) && !(old_value & 7) && *volume && !(value & 8)) {
(*volume)--;
}
if ((old_value & 7) && (value & 8)) {
(*volume)--;
}
(*volume) &= 0xF;
}
static void tick_square_envelope(GB_gameboy_t *gb, enum GB_CHANNELS index)
{
uint8_t nrx2 = gb->io_registers[index == GB_SQUARE_1? GB_IO_NR12 : GB_IO_NR22];
if (gb->apu.square_channels[index].volume_countdown || (nrx2 & 7)) {
if (!gb->apu.square_channels[index].volume_countdown || !--gb->apu.square_channels[index].volume_countdown) {
if ((nrx2 & 8) && gb->apu.square_channels[index].current_volume < 0xF) {
gb->apu.square_channels[index].current_volume++;
}
else if (!(nrx2 & 8) && gb->apu.square_channels[index].current_volume > 0) {
gb->apu.square_channels[index].current_volume--;
}
gb->apu.square_channels[index].volume_countdown = nrx2 & 7;
if (gb->apu.is_active[index]) {
update_square_sample(gb, index);
}
}
}
}
static void tick_noise_envelope(GB_gameboy_t *gb)
{
uint8_t nr42 = gb->io_registers[GB_IO_NR42];
if (gb->apu.noise_channel.volume_countdown || (nr42 & 7)) {
if (!--gb->apu.noise_channel.volume_countdown) {
if ((nr42 & 8) && gb->apu.noise_channel.current_volume < 0xF) {
gb->apu.noise_channel.current_volume++;
}
else if (!(nr42 & 8) && gb->apu.noise_channel.current_volume > 0) {
gb->apu.noise_channel.current_volume--;
}
gb->apu.noise_channel.volume_countdown = nr42 & 7;
if (gb->apu.is_active[GB_NOISE]) {
update_sample(gb, GB_NOISE,
(gb->apu.noise_channel.lfsr & 1) ?
gb->apu.noise_channel.current_volume : 0,
0);
}
}
}
}
void GB_apu_div_event(GB_gameboy_t *gb)
{
if (!gb->apu.global_enable) return;
if (gb->apu.skip_div_event) {
gb->apu.skip_div_event = false;
return;
}
gb->apu.div_divider++;
if ((gb->apu.div_divider & 1) == 0) {
for (unsigned i = GB_SQUARE_2 + 1; i--;) {
uint8_t nrx2 = gb->io_registers[i == GB_SQUARE_1? GB_IO_NR12 : GB_IO_NR22];
if (gb->apu.is_active[i] && gb->apu.square_channels[i].volume_countdown == 0 && (nrx2 & 7)) {
tick_square_envelope(gb, i);
}
}
if (gb->apu.is_active[GB_NOISE] && gb->apu.noise_channel.volume_countdown == 0 && (gb->io_registers[GB_IO_NR42] & 7)) {
tick_noise_envelope(gb);
}
}
if ((gb->apu.div_divider & 7) == 0) {
for (unsigned i = GB_SQUARE_2 + 1; i--;) {
tick_square_envelope(gb, i);
}
tick_noise_envelope(gb);
}
if ((gb->apu.div_divider & 1) == 1) {
for (unsigned i = GB_SQUARE_2 + 1; i--;) {
if (gb->apu.square_channels[i].length_enabled) {
if (gb->apu.square_channels[i].pulse_length) {
if (!--gb->apu.square_channels[i].pulse_length) {
gb->apu.is_active[i] = false;
update_sample(gb, i, 0, 0);
}
}
}
}
if (gb->apu.wave_channel.length_enabled) {
if (gb->apu.wave_channel.pulse_length) {
if (!--gb->apu.wave_channel.pulse_length) {
gb->apu.is_active[GB_WAVE] = false;
update_sample(gb, GB_WAVE, 0, 0);
}
}
}
if (gb->apu.noise_channel.length_enabled) {
if (gb->apu.noise_channel.pulse_length) {
if (!--gb->apu.noise_channel.pulse_length) {
gb->apu.is_active[GB_NOISE] = false;
update_sample(gb, GB_NOISE, 0, 0);
}
}
}
}
if ((gb->apu.div_divider & 3) == 3) {
if (!gb->apu.sweep_enabled) {
return;
}
if (gb->apu.square_sweep_countdown) {
if (!--gb->apu.square_sweep_countdown) {
if ((gb->io_registers[GB_IO_NR10] & 0x70) && (gb->io_registers[GB_IO_NR10] & 0x07)) {
gb->apu.square_channels[GB_SQUARE_1].sample_length =
gb->apu.shadow_sweep_sample_legnth =
gb->apu.new_sweep_sample_legnth;
}
if (gb->io_registers[GB_IO_NR10] & 0x70) {
/* Recalculation and overflow check only occurs after a delay */
gb->apu.square_sweep_calculate_countdown = 0x13 - gb->apu.lf_div;
}
gb->apu.square_sweep_countdown = ((gb->io_registers[GB_IO_NR10] >> 4) & 7);
if (!gb->apu.square_sweep_countdown) gb->apu.square_sweep_countdown = 8;
}
}
}
}
void GB_apu_run(GB_gameboy_t *gb)
{
/* Convert 4MHZ to 2MHz. apu_cycles is always divisable by 4. */
uint8_t cycles = gb->apu.apu_cycles >> 2;
gb->apu.apu_cycles = 0;
if (!cycles) return;
if (likely(!gb->stopped || GB_is_cgb(gb))) {
/* To align the square signal to 1MHz */
gb->apu.lf_div ^= cycles & 1;
gb->apu.noise_channel.alignment += cycles;
if (gb->apu.square_sweep_calculate_countdown) {
if (gb->apu.square_sweep_calculate_countdown > cycles) {
gb->apu.square_sweep_calculate_countdown -= cycles;
}
else {
/* APU bug: sweep frequency is checked after adding the sweep delta twice */
gb->apu.new_sweep_sample_legnth = new_sweep_sample_legnth(gb);
if (gb->apu.new_sweep_sample_legnth > 0x7ff) {
gb->apu.is_active[GB_SQUARE_1] = false;
update_sample(gb, GB_SQUARE_1, 0, gb->apu.square_sweep_calculate_countdown - cycles);
gb->apu.sweep_enabled = false;
}
gb->apu.sweep_decreasing |= gb->io_registers[GB_IO_NR10] & 8;
gb->apu.square_sweep_calculate_countdown = 0;
}
}
UNROLL
for (unsigned i = GB_SQUARE_1; i <= GB_SQUARE_2; i++) {
if (gb->apu.is_active[i]) {
uint8_t cycles_left = cycles;
while (unlikely(cycles_left > gb->apu.square_channels[i].sample_countdown)) {
cycles_left -= gb->apu.square_channels[i].sample_countdown + 1;
gb->apu.square_channels[i].sample_countdown = (gb->apu.square_channels[i].sample_length ^ 0x7FF) * 2 + 1;
gb->apu.square_channels[i].current_sample_index++;
gb->apu.square_channels[i].current_sample_index &= 0x7;
update_square_sample(gb, i);
}
if (cycles_left) {
gb->apu.square_channels[i].sample_countdown -= cycles_left;
}
}
}
gb->apu.wave_channel.wave_form_just_read = false;
if (gb->apu.is_active[GB_WAVE]) {
uint8_t cycles_left = cycles;
while (unlikely(cycles_left > gb->apu.wave_channel.sample_countdown)) {
cycles_left -= gb->apu.wave_channel.sample_countdown + 1;
gb->apu.wave_channel.sample_countdown = gb->apu.wave_channel.sample_length ^ 0x7FF;
gb->apu.wave_channel.current_sample_index++;
gb->apu.wave_channel.current_sample_index &= 0x1F;
gb->apu.wave_channel.current_sample =
gb->apu.wave_channel.wave_form[gb->apu.wave_channel.current_sample_index];
update_sample(gb, GB_WAVE,
gb->apu.wave_channel.current_sample >> gb->apu.wave_channel.shift,
cycles - cycles_left);
gb->apu.wave_channel.wave_form_just_read = true;
}
if (cycles_left) {
gb->apu.wave_channel.sample_countdown -= cycles_left;
gb->apu.wave_channel.wave_form_just_read = false;
}
}
if (gb->apu.is_active[GB_NOISE]) {
uint8_t cycles_left = cycles;
while (unlikely(cycles_left > gb->apu.noise_channel.sample_countdown)) {
cycles_left -= gb->apu.noise_channel.sample_countdown + 1;
gb->apu.noise_channel.sample_countdown = gb->apu.noise_channel.sample_length * 4 + 3;
/* Step LFSR */
unsigned high_bit_mask = gb->apu.noise_channel.narrow ? 0x4040 : 0x4000;
/* Todo: is this formula is different on a GBA? */
bool new_high_bit = (gb->apu.noise_channel.lfsr ^ (gb->apu.noise_channel.lfsr >> 1) ^ 1) & 1;
gb->apu.noise_channel.lfsr >>= 1;
if (new_high_bit) {
gb->apu.noise_channel.lfsr |= high_bit_mask;
}
else {
/* This code is not redundent, it's relevant when switching LFSR widths */
gb->apu.noise_channel.lfsr &= ~high_bit_mask;
}
gb->apu.current_lfsr_sample = gb->apu.noise_channel.lfsr & 1;
update_sample(gb, GB_NOISE,
gb->apu.current_lfsr_sample ?
gb->apu.noise_channel.current_volume : 0,
0);
}
if (cycles_left) {
gb->apu.noise_channel.sample_countdown -= cycles_left;
}
}
}
if (gb->apu_output.sample_rate) {
gb->apu_output.cycles_since_render += cycles;
if (gb->apu_output.sample_cycles >= gb->apu_output.cycles_per_sample) {
gb->apu_output.sample_cycles -= gb->apu_output.cycles_per_sample;
render(gb);
}
}
}
void GB_apu_init(GB_gameboy_t *gb)
{
memset(&gb->apu, 0, sizeof(gb->apu));
gb->apu.lf_div = 1;
/* APU glitch: When turning the APU on while DIV's bit 4 (or 5 in double speed mode) is on,
the first DIV/APU event is skipped. */
if (gb->div_counter & (gb->cgb_double_speed? 0x2000 : 0x1000)) {
gb->apu.skip_div_event = true;
}
}
uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg)
{
if (reg == GB_IO_NR52) {
uint8_t value = 0;
for (int i = 0; i < GB_N_CHANNELS; i++) {
value >>= 1;
if (gb->apu.is_active[i]) {
value |= 0x8;
}
}
if (gb->apu.global_enable) {
value |= 0x80;
}
value |= 0x70;
return value;
}
static const char read_mask[GB_IO_WAV_END - GB_IO_NR10 + 1] = {
/* NRX0 NRX1 NRX2 NRX3 NRX4 */
0x80, 0x3F, 0x00, 0xFF, 0xBF, // NR1X
0xFF, 0x3F, 0x00, 0xFF, 0xBF, // NR2X
0x7F, 0xFF, 0x9F, 0xFF, 0xBF, // NR3X
0xFF, 0xFF, 0x00, 0x00, 0xBF, // NR4X
0x00, 0x00, 0x70, 0xFF, 0xFF, // NR5X
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Unused
// Wave RAM
0, /* ... */
};
if (reg >= GB_IO_WAV_START && reg <= GB_IO_WAV_END && gb->apu.is_active[GB_WAVE]) {
if (!GB_is_cgb(gb) && !gb->apu.wave_channel.wave_form_just_read) {
return 0xFF;
}
reg = GB_IO_WAV_START + gb->apu.wave_channel.current_sample_index / 2;
}
return gb->io_registers[reg] | read_mask[reg - GB_IO_NR10];
}
void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
{
if (!gb->apu.global_enable && reg != GB_IO_NR52 && (GB_is_cgb(gb) ||
(
reg != GB_IO_NR11 &&
reg != GB_IO_NR21 &&
reg != GB_IO_NR31 &&
reg != GB_IO_NR41
)
)) {
return;
}
if (reg >= GB_IO_WAV_START && reg <= GB_IO_WAV_END && gb->apu.is_active[GB_WAVE]) {
if (!GB_is_cgb(gb) && !gb->apu.wave_channel.wave_form_just_read) {
return;
}
reg = GB_IO_WAV_START + gb->apu.wave_channel.current_sample_index / 2;
}
/* Todo: this can and should be rewritten with a function table. */
switch (reg) {
/* Globals */
case GB_IO_NR50:
case GB_IO_NR51:
gb->io_registers[reg] = value;
/* These registers affect the output of all 4 channels (but not the output of the PCM registers).*/
/* We call update_samples with the current value so the APU output is updated with the new outputs */
for (unsigned i = GB_N_CHANNELS; i--;) {
update_sample(gb, i, gb->apu.samples[i], 0);
}
break;
case GB_IO_NR52: {
uint8_t old_nrx1[] = {
gb->io_registers[GB_IO_NR11],
gb->io_registers[GB_IO_NR21],
gb->io_registers[GB_IO_NR31],
gb->io_registers[GB_IO_NR41]
};
if ((value & 0x80) && !gb->apu.global_enable) {
GB_apu_init(gb);
gb->apu.global_enable = true;
}
else if (!(value & 0x80) && gb->apu.global_enable) {
for (unsigned i = GB_N_CHANNELS; i--;) {
update_sample(gb, i, 0, 0);
}
memset(&gb->apu, 0, sizeof(gb->apu));
memset(gb->io_registers + GB_IO_NR10, 0, GB_IO_WAV_START - GB_IO_NR10);
old_nrx1[0] &= 0x3F;
old_nrx1[1] &= 0x3F;
gb->apu.global_enable = false;
}
if (!GB_is_cgb(gb) && (value & 0x80)) {
GB_apu_write(gb, GB_IO_NR11, old_nrx1[0]);
GB_apu_write(gb, GB_IO_NR21, old_nrx1[1]);
GB_apu_write(gb, GB_IO_NR31, old_nrx1[2]);
GB_apu_write(gb, GB_IO_NR41, old_nrx1[3]);
}
}
break;
/* Square channels */
case GB_IO_NR10:
if (gb->apu.sweep_decreasing && !(value & 8)) {
gb->apu.is_active[GB_SQUARE_1] = false;
update_sample(gb, GB_SQUARE_1, 0, 0);
gb->apu.sweep_enabled = false;
gb->apu.square_sweep_calculate_countdown = 0;
}
if ((value & 0x70) == 0) {
/* Todo: what happens if we set period to 0 while a calculate event is scheduled, and then
re-set it to non-zero? */
gb->apu.square_sweep_calculate_countdown = 0;
}
break;
case GB_IO_NR11:
case GB_IO_NR21: {
unsigned index = reg == GB_IO_NR21? GB_SQUARE_2: GB_SQUARE_1;
gb->apu.square_channels[index].pulse_length = (0x40 - (value & 0x3f));
if (!gb->apu.global_enable) {
value &= 0x3f;
}
break;
}
case GB_IO_NR12:
case GB_IO_NR22: {
unsigned index = reg == GB_IO_NR22? GB_SQUARE_2: GB_SQUARE_1;
if (((value & 0x7) == 0) && ((gb->io_registers[reg] & 0x7) != 0)) {
/* Envelope disabled */
gb->apu.square_channels[index].volume_countdown = 0;
}
if ((value & 0xF8) == 0) {
/* This disables the DAC */
gb->io_registers[reg] = value;
gb->apu.is_active[index] = false;
update_sample(gb, index, 0, 0);
}
else if (gb->apu.is_active[index]) {
nrx2_glitch(&gb->apu.square_channels[index].current_volume, value, gb->io_registers[reg]);
update_square_sample(gb, index);
}
break;
}
case GB_IO_NR13:
case GB_IO_NR23: {
unsigned index = reg == GB_IO_NR23? GB_SQUARE_2: GB_SQUARE_1;
gb->apu.square_channels[index].sample_length &= ~0xFF;
gb->apu.square_channels[index].sample_length |= value & 0xFF;
break;
}
case GB_IO_NR14:
case GB_IO_NR24: {
unsigned index = reg == GB_IO_NR24? GB_SQUARE_2: GB_SQUARE_1;
gb->apu.square_channels[index].sample_length &= 0xFF;
gb->apu.square_channels[index].sample_length |= (value & 7) << 8;
if (index == GB_SQUARE_1) {
gb->apu.shadow_sweep_sample_legnth =
gb->apu.new_sweep_sample_legnth =
gb->apu.square_channels[0].sample_length;
}
if (value & 0x80) {
/* Current sample index remains unchanged when restarting channels 1 or 2. It is only reset by
turning the APU off. */
if (!gb->apu.is_active[index]) {
gb->apu.square_channels[index].sample_countdown = (gb->apu.square_channels[index].sample_length ^ 0x7FF) * 2 + 6 - gb->apu.lf_div;
}
else {
/* Timing quirk: if already active, sound starts 2 (2MHz) ticks earlier.*/
gb->apu.square_channels[index].sample_countdown = (gb->apu.square_channels[index].sample_length ^ 0x7FF) * 2 + 4 - gb->apu.lf_div;
}
gb->apu.square_channels[index].current_volume = gb->io_registers[index == GB_SQUARE_1 ? GB_IO_NR12 : GB_IO_NR22] >> 4;
/* The volume changes caused by NRX4 sound start take effect instantly (i.e. the effect the previously
started sound). The playback itself is not instant which is why we don't update the sample for other
cases. */
if (gb->apu.is_active[index]) {
update_square_sample(gb, index);
}
gb->apu.square_channels[index].volume_countdown = gb->io_registers[index == GB_SQUARE_1 ? GB_IO_NR12 : GB_IO_NR22] & 7;
if ((gb->io_registers[index == GB_SQUARE_1 ? GB_IO_NR12 : GB_IO_NR22] & 0xF8) != 0 && !gb->apu.is_active[index]) {
gb->apu.is_active[index] = true;
update_sample(gb, index, 0, 0);
/* We use the highest bit in current_sample_index to mark this sample is not actually playing yet, */
gb->apu.square_channels[index].current_sample_index |= 0x80;
}
if (gb->apu.square_channels[index].pulse_length == 0) {
gb->apu.square_channels[index].pulse_length = 0x40;
gb->apu.square_channels[index].length_enabled = false;
}
if (index == GB_SQUARE_1) {
gb->apu.sweep_decreasing = false;
if (gb->io_registers[GB_IO_NR10] & 7) {
/* APU bug: if shift is nonzero, overflow check also occurs on trigger */
gb->apu.square_sweep_calculate_countdown = 0x13 - gb->apu.lf_div;
}
else {
gb->apu.square_sweep_calculate_countdown = 0;
}
gb->apu.sweep_enabled = gb->io_registers[GB_IO_NR10] & 0x77;
gb->apu.square_sweep_countdown = ((gb->io_registers[GB_IO_NR10] >> 4) & 7);
if (!gb->apu.square_sweep_countdown) gb->apu.square_sweep_countdown = 8;
}
}
/* APU glitch - if length is enabled while the DIV-divider's LSB is 1, tick the length once. */
if ((value & 0x40) &&
!gb->apu.square_channels[index].length_enabled &&
(gb->apu.div_divider & 1) &&
gb->apu.square_channels[index].pulse_length) {
gb->apu.square_channels[index].pulse_length--;
if (gb->apu.square_channels[index].pulse_length == 0) {
if (value & 0x80) {
gb->apu.square_channels[index].pulse_length = 0x3F;
}
else {
gb->apu.is_active[index] = false;
update_sample(gb, index, 0, 0);
}
}
}
gb->apu.square_channels[index].length_enabled = value & 0x40;
break;
}
/* Wave channel */
case GB_IO_NR30:
gb->apu.wave_channel.enable = value & 0x80;
if (!gb->apu.wave_channel.enable) {
gb->apu.is_active[GB_WAVE] = false;
update_sample(gb, GB_WAVE, 0, 0);
}
break;
case GB_IO_NR31:
gb->apu.wave_channel.pulse_length = (0x100 - value);
break;
case GB_IO_NR32:
gb->apu.wave_channel.shift = (uint8_t[]){4, 0, 1, 2}[(value >> 5) & 3];
if (gb->apu.is_active[GB_WAVE]) {
update_sample(gb, GB_WAVE, gb->apu.wave_channel.current_sample >> gb->apu.wave_channel.shift, 0);
}
break;
case GB_IO_NR33:
gb->apu.wave_channel.sample_length &= ~0xFF;
gb->apu.wave_channel.sample_length |= value & 0xFF;
break;
case GB_IO_NR34:
gb->apu.wave_channel.sample_length &= 0xFF;
gb->apu.wave_channel.sample_length |= (value & 7) << 8;
if ((value & 0x80)) {
/* DMG bug: wave RAM gets corrupted if the channel is retriggerred 1 cycle before the APU
reads from it. */
if (!GB_is_cgb(gb) &&
gb->apu.is_active[GB_WAVE] &&
gb->apu.wave_channel.sample_countdown == 0 &&
gb->apu.wave_channel.enable) {
unsigned offset = ((gb->apu.wave_channel.current_sample_index + 1) >> 1) & 0xF;
/* This glitch varies between models and even specific instances:
DMG-B: Most of them behave as emulated. A few behave differently.
SGB: As far as I know, all tested instances behave as emulated.
MGB, SGB2: Most instances behave non-deterministically, a few behave as emulated.
Additionally, I believe DMGs, including those we behave differently than emulated,
are all deterministic. */
if (offset < 4) {
gb->io_registers[GB_IO_WAV_START] = gb->io_registers[GB_IO_WAV_START + offset];
gb->apu.wave_channel.wave_form[0] = gb->apu.wave_channel.wave_form[offset / 2];
gb->apu.wave_channel.wave_form[1] = gb->apu.wave_channel.wave_form[offset / 2 + 1];
}
else {
memcpy(gb->io_registers + GB_IO_WAV_START,
gb->io_registers + GB_IO_WAV_START + (offset & ~3),
4);
memcpy(gb->apu.wave_channel.wave_form,
gb->apu.wave_channel.wave_form + (offset & ~3) * 2,
8);
}
}
if (!gb->apu.is_active[GB_WAVE]) {
gb->apu.is_active[GB_WAVE] = true;
update_sample(gb, GB_WAVE,
gb->apu.wave_channel.current_sample >> gb->apu.wave_channel.shift,
0);
}
gb->apu.wave_channel.sample_countdown = (gb->apu.wave_channel.sample_length ^ 0x7FF) + 3;
gb->apu.wave_channel.current_sample_index = 0;
if (gb->apu.wave_channel.pulse_length == 0) {
gb->apu.wave_channel.pulse_length = 0x100;
gb->apu.wave_channel.length_enabled = false;
}
/* Note that we don't change the sample just yet! This was verified on hardware. */
}
/* APU glitch - if length is enabled while the DIV-divider's LSB is 1, tick the length once. */
if ((value & 0x40) &&
!gb->apu.wave_channel.length_enabled &&
(gb->apu.div_divider & 1) &&
gb->apu.wave_channel.pulse_length) {
gb->apu.wave_channel.pulse_length--;
if (gb->apu.wave_channel.pulse_length == 0) {
if (value & 0x80) {
gb->apu.wave_channel.pulse_length = 0xFF;
}
else {
gb->apu.is_active[GB_WAVE] = false;
update_sample(gb, GB_WAVE, 0, 0);
}
}
}
gb->apu.wave_channel.length_enabled = value & 0x40;
if (gb->apu.is_active[GB_WAVE] && !gb->apu.wave_channel.enable) {
gb->apu.is_active[GB_WAVE] = false;
update_sample(gb, GB_WAVE, 0, 0);
}
break;
/* Noise Channel */
case GB_IO_NR41: {
gb->apu.noise_channel.pulse_length = (0x40 - (value & 0x3f));
break;
}
case GB_IO_NR42: {
if (((value & 0x7) == 0) && ((gb->io_registers[reg] & 0x7) != 0)) {
/* Envelope disabled */
gb->apu.noise_channel.volume_countdown = 0;
}
if ((value & 0xF8) == 0) {
/* This disables the DAC */
gb->io_registers[reg] = value;
gb->apu.is_active[GB_NOISE] = false;
update_sample(gb, GB_NOISE, 0, 0);
}
else if (gb->apu.is_active[GB_NOISE]){
nrx2_glitch(&gb->apu.noise_channel.current_volume, value, gb->io_registers[reg]);
update_sample(gb, GB_NOISE,
gb->apu.current_lfsr_sample ?
gb->apu.noise_channel.current_volume : 0,
0);
}
break;
}
case GB_IO_NR43: {
gb->apu.noise_channel.narrow = value & 8;
unsigned divisor = (value & 0x07) << 1;
if (!divisor) divisor = 1;
gb->apu.noise_channel.sample_length = (divisor << (value >> 4)) - 1;
/* Todo: changing the frequency sometimes delays the next sample. This is probably
due to how the frequency is actually calculated in the noise channel, which is probably
not by calculating the effective sample length and counting simiarly to the other channels.
This is not emulated correctly. */
break;
}
case GB_IO_NR44: {
if (value & 0x80) {
gb->apu.noise_channel.sample_countdown = (gb->apu.noise_channel.sample_length) * 2 + 6 - gb->apu.lf_div;
/* I'm COMPLETELY unsure about this logic, but it passes all relevant tests.
See comment in NR43. */
if ((gb->io_registers[GB_IO_NR43] & 7) && (gb->apu.noise_channel.alignment & 2) == 0) {
if ((gb->io_registers[GB_IO_NR43] & 7) == 1) {
gb->apu.noise_channel.sample_countdown += 2;
}
else {
gb->apu.noise_channel.sample_countdown -= 2;
}
}
if (gb->apu.is_active[GB_NOISE]) {
gb->apu.noise_channel.sample_countdown += 2;
}
gb->apu.noise_channel.current_volume = gb->io_registers[GB_IO_NR42] >> 4;
/* The volume changes caused by NRX4 sound start take effect instantly (i.e. the effect the previously
started sound). The playback itself is not instant which is why we don't update the sample for other
cases. */
if (gb->apu.is_active[GB_NOISE]) {
update_sample(gb, GB_NOISE,
gb->apu.current_lfsr_sample ?
gb->apu.noise_channel.current_volume : 0,
0);
}
gb->apu.noise_channel.lfsr = 0;
gb->apu.current_lfsr_sample = false;
gb->apu.noise_channel.volume_countdown = gb->io_registers[GB_IO_NR42] & 7;
if (!gb->apu.is_active[GB_NOISE] && (gb->io_registers[GB_IO_NR42] & 0xF8) != 0) {
gb->apu.is_active[GB_NOISE] = true;
update_sample(gb, GB_NOISE, 0, 0);
}
if (gb->apu.noise_channel.pulse_length == 0) {
gb->apu.noise_channel.pulse_length = 0x40;
gb->apu.noise_channel.length_enabled = false;
}
}
/* APU glitch - if length is enabled while the DIV-divider's LSB is 1, tick the length once. */
if ((value & 0x40) &&
!gb->apu.noise_channel.length_enabled &&
(gb->apu.div_divider & 1) &&
gb->apu.noise_channel.pulse_length) {
gb->apu.noise_channel.pulse_length--;
if (gb->apu.noise_channel.pulse_length == 0) {
if (value & 0x80) {
gb->apu.noise_channel.pulse_length = 0x3F;
}
else {
gb->apu.is_active[GB_NOISE] = false;
update_sample(gb, GB_NOISE, 0, 0);
}
}
}
gb->apu.noise_channel.length_enabled = value & 0x40;
break;
}
default:
if (reg >= GB_IO_WAV_START && reg <= GB_IO_WAV_END) {
gb->apu.wave_channel.wave_form[(reg - GB_IO_WAV_START) * 2] = value >> 4;
gb->apu.wave_channel.wave_form[(reg - GB_IO_WAV_START) * 2 + 1] = value & 0xF;
}
}
gb->io_registers[reg] = value;
}
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate)
{
gb->apu_output.sample_rate = sample_rate;
if (sample_rate) {
gb->apu_output.highpass_rate = pow(0.999958, GB_get_clock_rate(gb) / (double)sample_rate);
}
GB_apu_update_cycles_per_sample(gb);
}
void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback)
{
gb->apu_output.sample_callback = callback;
}
void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode)
{
gb->apu_output.highpass_mode = mode;
}
void GB_apu_update_cycles_per_sample(GB_gameboy_t *gb)
{
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 */
}
}

161
sameboy/Core/apu.h Normal file
View File

@ -0,0 +1,161 @@
#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;
} GB_apu_output_t;
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate);
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 */

149
sameboy/Core/camera.c Normal file
View File

@ -0,0 +1,149 @@
#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;
}

29
sameboy/Core/camera.h Normal file
View File

@ -0,0 +1,29 @@
#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

2459
sameboy/Core/debugger.c Normal file

File diff suppressed because it is too large Load Diff

43
sameboy/Core/debugger.h Normal file
View File

@ -0,0 +1,43 @@
#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 */

1172
sameboy/Core/display.c Normal file

File diff suppressed because it is too large Load Diff

54
sameboy/Core/display.h Normal file
View File

@ -0,0 +1,54 @@
#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 */

918
sameboy/Core/gb.c Normal file
View File

@ -0,0 +1,918 @@
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#ifndef _WIN32
#include <sys/select.h>
#include <unistd.h>
#endif
#include "random.h"
#include "gb.h"
#ifdef DISABLE_REWIND
#define GB_rewind_free(...)
#define GB_rewind_push(...)
#endif
void GB_attributed_logv(GB_gameboy_t *gb, GB_log_attributes attributes, const char *fmt, va_list args)
{
char *string = NULL;
vasprintf(&string, fmt, args);
if (string) {
if (gb->log_callback) {
gb->log_callback(gb, string, attributes);
}
else {
/* Todo: Add ANSI escape sequences for attributed text */
printf("%s", string);
}
}
free(string);
}
void GB_attributed_log(GB_gameboy_t *gb, GB_log_attributes attributes, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
GB_attributed_logv(gb, attributes, fmt, args);
va_end(args);
}
void GB_log(GB_gameboy_t *gb, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
GB_attributed_logv(gb, 0, fmt, args);
va_end(args);
}
#ifndef DISABLE_DEBUGGER
static char *default_input_callback(GB_gameboy_t *gb)
{
char *expression = NULL;
size_t size = 0;
if (getline(&expression, &size, stdin) == -1) {
/* The user doesn't have STDIN or used ^D. We make sure the program keeps running. */
GB_set_async_input_callback(gb, NULL); /* Disable async input */
return strdup("c");
}
if (!expression) {
return strdup("");
}
size_t length = strlen(expression);
if (expression[length - 1] == '\n') {
expression[length - 1] = 0;
}
return expression;
}
static char *default_async_input_callback(GB_gameboy_t *gb)
{
#ifndef _WIN32
fd_set set;
FD_ZERO(&set);
FD_SET(STDIN_FILENO, &set);
struct timeval time = {0,};
if (select(1, &set, NULL, NULL, &time) == 1) {
if (feof(stdin)) {
GB_set_async_input_callback(gb, NULL); /* Disable async input */
return NULL;
}
return default_input_callback(gb);
}
#endif
return NULL;
}
#endif
void GB_init(GB_gameboy_t *gb, GB_model_t model)
{
memset(gb, 0, sizeof(*gb));
gb->model = model;
if (GB_is_cgb(gb)) {
gb->ram = malloc(gb->ram_size = 0x1000 * 8);
gb->vram = malloc(gb->vram_size = 0x2000 * 2);
}
else {
gb->ram = malloc(gb->ram_size = 0x2000);
gb->vram = malloc(gb->vram_size = 0x2000);
}
#ifndef DISABLE_DEBUGGER
gb->input_callback = default_input_callback;
gb->async_input_callback = default_async_input_callback;
#endif
gb->cartridge_type = &GB_cart_defs[0]; // Default cartridge type
gb->clock_multiplier = 1.0;
GB_reset(gb);
}
GB_model_t GB_get_model(GB_gameboy_t *gb)
{
return gb->model;
}
void GB_free(GB_gameboy_t *gb)
{
gb->magic = 0;
if (gb->ram) {
free(gb->ram);
}
if (gb->vram) {
free(gb->vram);
}
if (gb->mbc_ram) {
free(gb->mbc_ram);
}
if (gb->rom) {
free(gb->rom);
}
if (gb->breakpoints) {
free(gb->breakpoints);
}
if (gb->sgb) {
free(gb->sgb);
}
if (gb->nontrivial_jump_state) {
free(gb->nontrivial_jump_state);
}
#ifndef DISABLE_DEBUGGER
GB_debugger_clear_symbols(gb);
#endif
GB_rewind_free(gb);
memset(gb, 0, sizeof(*gb));
}
int GB_load_boot_rom(GB_gameboy_t *gb, const char *path)
{
FILE *f = fopen(path, "rb");
if (!f) {
GB_log(gb, "Could not open boot ROM: %s.\n", strerror(errno));
return errno;
}
fread(gb->boot_rom, sizeof(gb->boot_rom), 1, f);
fclose(f);
return 0;
}
void GB_load_boot_rom_from_buffer(GB_gameboy_t *gb, const unsigned char *buffer, size_t size)
{
if (size > sizeof(gb->boot_rom)) {
size = sizeof(gb->boot_rom);
}
memset(gb->boot_rom, 0xFF, sizeof(gb->boot_rom));
memcpy(gb->boot_rom, buffer, size);
}
int GB_load_rom(GB_gameboy_t *gb, const char *path)
{
FILE *f = fopen(path, "rb");
if (!f) {
GB_log(gb, "Could not open ROM: %s.\n", strerror(errno));
return errno;
}
fseek(f, 0, SEEK_END);
gb->rom_size = (ftell(f) + 0x3FFF) & ~0x3FFF; /* Round to bank */
/* And then round to a power of two */
while (gb->rom_size & (gb->rom_size - 1)) {
/* I promise this works. */
gb->rom_size |= gb->rom_size >> 1;
gb->rom_size++;
}
fseek(f, 0, SEEK_SET);
if (gb->rom) {
free(gb->rom);
}
gb->rom = malloc(gb->rom_size);
memset(gb->rom, 0xFF, gb->rom_size); /* Pad with 0xFFs */
fread(gb->rom, gb->rom_size, 1, f);
fclose(f);
GB_configure_cart(gb);
return 0;
}
/* byuu */
int GB_load_rom_from_buffer(GB_gameboy_t* gb, const unsigned char* buffer, size_t size) {
gb->rom_size = (size + 0x3fff) & ~0x3fff;
while(gb->rom_size & (gb->rom_size - 1)) {
gb->rom_size |= gb->rom_size >> 1;
gb->rom_size++;
}
if(gb->rom) free(gb->rom);
gb->rom = (uint8_t*)malloc(gb->rom_size);
memset(gb->rom, 0xff, gb->rom_size);
memcpy(gb->rom, buffer, size);
GB_configure_cart(gb);
return 0;
}
typedef struct {
uint8_t seconds;
uint8_t padding1[3];
uint8_t minutes;
uint8_t padding2[3];
uint8_t hours;
uint8_t padding3[3];
uint8_t days;
uint8_t padding4[3];
uint8_t high;
uint8_t padding5[3];
} GB_vba_rtc_time_t;
typedef union {
struct __attribute__((packed)) {
GB_rtc_time_t rtc_real;
time_t last_rtc_second; /* Platform specific endianess and size */
} sameboy_legacy;
struct {
/* Used by VBA versions with 32-bit timestamp*/
GB_vba_rtc_time_t rtc_real, rtc_latched;
uint32_t last_rtc_second; /* Always little endian */
} vba32;
struct {
/* Used by BGB and VBA versions with 64-bit timestamp*/
GB_vba_rtc_time_t rtc_real, rtc_latched;
uint64_t last_rtc_second; /* Always little endian */
} vba64;
} GB_rtc_save_t;
int GB_save_battery(GB_gameboy_t *gb, const char *path)
{
if (!gb->cartridge_type->has_battery) return 0; // Nothing to save.
if (gb->mbc_ram_size == 0 && !gb->cartridge_type->has_rtc) return 0; /* Claims to have battery, but has no RAM or RTC */
FILE *f = fopen(path, "wb");
if (!f) {
GB_log(gb, "Could not open battery save: %s.\n", strerror(errno));
return errno;
}
if (fwrite(gb->mbc_ram, 1, gb->mbc_ram_size, f) != gb->mbc_ram_size) {
fclose(f);
return EIO;
}
if (gb->cartridge_type->has_rtc) {
GB_rtc_save_t rtc_save = {{{{0,}},},};
rtc_save.vba64.rtc_real.seconds = gb->rtc_real.seconds;
rtc_save.vba64.rtc_real.minutes = gb->rtc_real.minutes;
rtc_save.vba64.rtc_real.hours = gb->rtc_real.hours;
rtc_save.vba64.rtc_real.days = gb->rtc_real.days;
rtc_save.vba64.rtc_real.high = gb->rtc_real.high;
rtc_save.vba64.rtc_latched.seconds = gb->rtc_latched.seconds;
rtc_save.vba64.rtc_latched.minutes = gb->rtc_latched.minutes;
rtc_save.vba64.rtc_latched.hours = gb->rtc_latched.hours;
rtc_save.vba64.rtc_latched.days = gb->rtc_latched.days;
rtc_save.vba64.rtc_latched.high = gb->rtc_latched.high;
#ifdef GB_BIG_ENDIAN
rtc_save.vba64.last_rtc_second = __builtin_bswap64(gb->last_rtc_second);
#else
rtc_save.vba64.last_rtc_second = gb->last_rtc_second;
#endif
if (fwrite(&rtc_save.vba64, 1, sizeof(rtc_save.vba64), f) != sizeof(rtc_save.vba64)) {
fclose(f);
return EIO;
}
}
errno = 0;
fclose(f);
return errno;
}
/* Loading will silently stop if the format is incomplete */
void GB_load_battery(GB_gameboy_t *gb, const char *path)
{
FILE *f = fopen(path, "rb");
if (!f) {
return;
}
if (fread(gb->mbc_ram, 1, gb->mbc_ram_size, f) != gb->mbc_ram_size) {
goto reset_rtc;
}
GB_rtc_save_t rtc_save;
switch (fread(&rtc_save, 1, sizeof(rtc_save), f)) {
case sizeof(rtc_save.sameboy_legacy):
memcpy(&gb->rtc_real, &rtc_save.sameboy_legacy.rtc_real, sizeof(gb->rtc_real));
memcpy(&gb->rtc_latched, &rtc_save.sameboy_legacy.rtc_real, sizeof(gb->rtc_real));
gb->last_rtc_second = rtc_save.sameboy_legacy.last_rtc_second;
break;
case sizeof(rtc_save.vba32):
gb->rtc_real.seconds = rtc_save.vba32.rtc_real.seconds;
gb->rtc_real.minutes = rtc_save.vba32.rtc_real.minutes;
gb->rtc_real.hours = rtc_save.vba32.rtc_real.hours;
gb->rtc_real.days = rtc_save.vba32.rtc_real.days;
gb->rtc_real.high = rtc_save.vba32.rtc_real.high;
gb->rtc_latched.seconds = rtc_save.vba32.rtc_latched.seconds;
gb->rtc_latched.minutes = rtc_save.vba32.rtc_latched.minutes;
gb->rtc_latched.hours = rtc_save.vba32.rtc_latched.hours;
gb->rtc_latched.days = rtc_save.vba32.rtc_latched.days;
gb->rtc_latched.high = rtc_save.vba32.rtc_latched.high;
#ifdef GB_BIG_ENDIAN
gb->last_rtc_second = __builtin_bswap32(rtc_save.vba32.last_rtc_second);
#else
gb->last_rtc_second = rtc_save.vba32.last_rtc_second;
#endif
break;
case sizeof(rtc_save.vba64):
gb->rtc_real.seconds = rtc_save.vba64.rtc_real.seconds;
gb->rtc_real.minutes = rtc_save.vba64.rtc_real.minutes;
gb->rtc_real.hours = rtc_save.vba64.rtc_real.hours;
gb->rtc_real.days = rtc_save.vba64.rtc_real.days;
gb->rtc_real.high = rtc_save.vba64.rtc_real.high;
gb->rtc_latched.seconds = rtc_save.vba64.rtc_latched.seconds;
gb->rtc_latched.minutes = rtc_save.vba64.rtc_latched.minutes;
gb->rtc_latched.hours = rtc_save.vba64.rtc_latched.hours;
gb->rtc_latched.days = rtc_save.vba64.rtc_latched.days;
gb->rtc_latched.high = rtc_save.vba64.rtc_latched.high;
#ifdef GB_BIG_ENDIAN
gb->last_rtc_second = __builtin_bswap64(rtc_save.vba64.last_rtc_second);
#else
gb->last_rtc_second = rtc_save.vba64.last_rtc_second;
#endif
break;
default:
goto reset_rtc;
}
if (gb->last_rtc_second > time(NULL)) {
/* We must reset RTC here, or it will not advance. */
goto reset_rtc;
}
if (gb->last_rtc_second < 852076800) { /* 1/1/97. There weren't any RTC games that time,
so if the value we read is lower it means it wasn't
really RTC data. */
goto reset_rtc;
}
goto exit;
reset_rtc:
gb->last_rtc_second = time(NULL);
gb->rtc_real.high |= 0x80; /* This gives the game a hint that the clock should be reset. */
exit:
fclose(f);
return;
}
uint8_t GB_run(GB_gameboy_t *gb)
{
gb->vblank_just_occured = false;
if (gb->sgb && gb->sgb->intro_animation < 140) {
/* On the SGB, the GB is halted after finishing the boot ROM.
Then, after the boot animation is almost done, it's reset.
Since the SGB HLE does not perform any header validity checks,
we just halt the CPU (with hacky code) until the correct time.
This ensures the Nintendo logo doesn't flash on screen, and
the game does "run in background" while the animation is playing. */
GB_display_run(gb, 228);
gb->cycles_since_last_sync += 228;
return 228;
}
GB_debugger_run(gb);
gb->cycles_since_run = 0;
GB_cpu_run(gb);
if (gb->vblank_just_occured) {
GB_rtc_run(gb);
GB_debugger_handle_async_commands(gb);
GB_rewind_push(gb);
}
return gb->cycles_since_run;
}
uint64_t GB_run_frame(GB_gameboy_t *gb)
{
/* Configure turbo temporarily, the user wants to handle FPS capping manually. */
bool old_turbo = gb->turbo;
bool old_dont_skip = gb->turbo_dont_skip;
gb->turbo = true;
gb->turbo_dont_skip = true;
gb->cycles_since_last_sync = 0;
while (true) {
GB_run(gb);
if (gb->vblank_just_occured) {
break;
}
}
gb->turbo = old_turbo;
gb->turbo_dont_skip = old_dont_skip;
return gb->cycles_since_last_sync * 1000000000LL / 2 / GB_get_clock_rate(gb); /* / 2 because we use 8MHz units */
}
void GB_set_pixels_output(GB_gameboy_t *gb, uint32_t *output)
{
gb->screen = output;
}
void GB_set_vblank_callback(GB_gameboy_t *gb, GB_vblank_callback_t callback)
{
gb->vblank_callback = callback;
}
void GB_set_log_callback(GB_gameboy_t *gb, GB_log_callback_t callback)
{
gb->log_callback = callback;
}
void GB_set_input_callback(GB_gameboy_t *gb, GB_input_callback_t callback)
{
#ifndef DISABLE_DEBUGGER
if (gb->input_callback == default_input_callback) {
gb->async_input_callback = NULL;
}
gb->input_callback = callback;
#endif
}
void GB_set_async_input_callback(GB_gameboy_t *gb, GB_input_callback_t callback)
{
#ifndef DISABLE_DEBUGGER
gb->async_input_callback = callback;
#endif
}
void GB_set_rgb_encode_callback(GB_gameboy_t *gb, GB_rgb_encode_callback_t callback)
{
if (!gb->rgb_encode_callback && !GB_is_cgb(gb)) {
gb->sprite_palettes_rgb[4] = gb->sprite_palettes_rgb[0] = gb->background_palettes_rgb[0] =
callback(gb, 0xFF, 0xFF, 0xFF);
gb->sprite_palettes_rgb[5] = gb->sprite_palettes_rgb[1] = gb->background_palettes_rgb[1] =
callback(gb, 0xAA, 0xAA, 0xAA);
gb->sprite_palettes_rgb[6] = gb->sprite_palettes_rgb[2] = gb->background_palettes_rgb[2] =
callback(gb, 0x55, 0x55, 0x55);
gb->sprite_palettes_rgb[7] = gb->sprite_palettes_rgb[3] = gb->background_palettes_rgb[3] =
callback(gb, 0, 0, 0);
}
gb->rgb_encode_callback = callback;
for (unsigned i = 0; i < 32; i++) {
GB_palette_changed(gb, true, i * 2);
GB_palette_changed(gb, false, i * 2);
}
}
void GB_set_infrared_callback(GB_gameboy_t *gb, GB_infrared_callback_t callback)
{
gb->infrared_callback = callback;
}
void GB_set_infrared_input(GB_gameboy_t *gb, bool state)
{
gb->infrared_input = state;
gb->cycles_since_input_ir_change = 0;
gb->ir_queue_length = 0;
}
void GB_queue_infrared_input(GB_gameboy_t *gb, bool state, long cycles_after_previous_change)
{
if (gb->ir_queue_length == GB_MAX_IR_QUEUE) {
GB_log(gb, "IR Queue is full\n");
return;
}
gb->ir_queue[gb->ir_queue_length++] = (GB_ir_queue_item_t){state, cycles_after_previous_change};
}
void GB_set_rumble_callback(GB_gameboy_t *gb, GB_rumble_callback_t callback)
{
gb->rumble_callback = callback;
}
void GB_set_serial_transfer_bit_start_callback(GB_gameboy_t *gb, GB_serial_transfer_bit_start_callback_t callback)
{
gb->serial_transfer_bit_start_callback = callback;
}
void GB_set_serial_transfer_bit_end_callback(GB_gameboy_t *gb, GB_serial_transfer_bit_end_callback_t callback)
{
gb->serial_transfer_bit_end_callback = callback;
}
bool GB_serial_get_data_bit(GB_gameboy_t *gb)
{
if (gb->io_registers[GB_IO_SC] & 1) {
/* Internal Clock */
GB_log(gb, "Serial read request while using internal clock. \n");
return 0xFF;
}
return gb->io_registers[GB_IO_SB] & 0x80;
}
void GB_serial_set_data_bit(GB_gameboy_t *gb, bool data)
{
if (gb->io_registers[GB_IO_SC] & 1) {
/* Internal Clock */
GB_log(gb, "Serial write request while using internal clock. \n");
return;
}
gb->io_registers[GB_IO_SB] <<= 1;
gb->io_registers[GB_IO_SB] |= data;
gb->serial_count++;
if (gb->serial_count == 8) {
gb->io_registers[GB_IO_IF] |= 8;
gb->serial_count = 0;
}
}
void GB_disconnect_serial(GB_gameboy_t *gb)
{
gb->serial_transfer_bit_start_callback = NULL;
gb->serial_transfer_bit_end_callback = NULL;
/* Reset any internally-emulated device. Currently, only the printer. */
memset(&gb->printer, 0, sizeof(gb->printer));
}
bool GB_is_inited(GB_gameboy_t *gb)
{
return gb->magic == 'SAME';
}
bool GB_is_cgb(GB_gameboy_t *gb)
{
return (gb->model & GB_MODEL_FAMILY_MASK) == GB_MODEL_CGB_FAMILY;
}
bool GB_is_sgb(GB_gameboy_t *gb)
{
return (gb->model & ~GB_MODEL_PAL_BIT) == GB_MODEL_SGB || gb->model == GB_MODEL_SGB2;
}
void GB_set_turbo_mode(GB_gameboy_t *gb, bool on, bool no_frame_skip)
{
gb->turbo = on;
gb->turbo_dont_skip = no_frame_skip;
}
void GB_set_rendering_disabled(GB_gameboy_t *gb, bool disabled)
{
gb->disable_rendering = disabled;
}
void *GB_get_user_data(GB_gameboy_t *gb)
{
return gb->user_data;
}
void GB_set_user_data(GB_gameboy_t *gb, void *data)
{
gb->user_data = data;
}
static void reset_ram(GB_gameboy_t *gb)
{
switch (gb->model) {
case GB_MODEL_CGB_E:
case GB_MODEL_AGB: /* Unverified */
for (unsigned i = 0; i < gb->ram_size; i++) {
gb->ram[i] = GB_random();
}
break;
case GB_MODEL_DMG_B:
case GB_MODEL_SGB_NTSC: /* Unverified*/
case GB_MODEL_SGB_PAL: /* Unverified */
for (unsigned i = 0; i < gb->ram_size; i++) {
gb->ram[i] = GB_random();
if (i & 0x100) {
gb->ram[i] &= GB_random();
}
else {
gb->ram[i] |= GB_random();
}
}
break;
case GB_MODEL_SGB2:
for (unsigned i = 0; i < gb->ram_size; i++) {
gb->ram[i] = 0x55;
gb->ram[i] ^= GB_random() & GB_random() & GB_random();
}
break;
case GB_MODEL_CGB_C:
for (unsigned i = 0; i < gb->ram_size; i++) {
if ((i & 0x808) == 0x800 || (i & 0x808) == 0x008) {
gb->ram[i] = 0;
}
else {
gb->ram[i] = GB_random() | GB_random() | GB_random() | GB_random();
}
}
break;
}
/* HRAM */
switch (gb->model) {
case GB_MODEL_CGB_C:
// case GB_MODEL_CGB_D:
case GB_MODEL_CGB_E:
case GB_MODEL_AGB:
for (unsigned i = 0; i < sizeof(gb->hram); i++) {
gb->hram[i] = GB_random();
}
break;
case GB_MODEL_DMG_B:
case GB_MODEL_SGB_NTSC: /* Unverified*/
case GB_MODEL_SGB_PAL: /* Unverified */
case GB_MODEL_SGB2:
for (unsigned i = 0; i < sizeof(gb->hram); i++) {
if (i & 1) {
gb->hram[i] = GB_random() | GB_random() | GB_random();
}
else {
gb->hram[i] = GB_random() & GB_random() & GB_random();
}
}
break;
}
/* OAM */
switch (gb->model) {
case GB_MODEL_CGB_C:
case GB_MODEL_CGB_E:
case GB_MODEL_AGB:
/* Zero'd out by boot ROM anyway*/
break;
case GB_MODEL_DMG_B:
case GB_MODEL_SGB_NTSC: /* Unverified*/
case GB_MODEL_SGB_PAL: /* Unverified */
case GB_MODEL_SGB2:
for (unsigned i = 0; i < 8; i++) {
if (i & 2) {
gb->oam[i] = GB_random() & GB_random() & GB_random();
}
else {
gb->oam[i] = GB_random() | GB_random() | GB_random();
}
}
for (unsigned i = 8; i < sizeof(gb->oam); i++) {
gb->oam[i] = gb->oam[i - 8];
}
break;
}
/* Wave RAM */
switch (gb->model) {
case GB_MODEL_CGB_C:
case GB_MODEL_CGB_E:
case GB_MODEL_AGB:
/* Initialized by CGB-A and newer, 0s in CGB-0*/
break;
case GB_MODEL_DMG_B:
case GB_MODEL_SGB_NTSC: /* Unverified*/
case GB_MODEL_SGB_PAL: /* Unverified */
case GB_MODEL_SGB2: {
uint8_t temp;
for (unsigned i = 0; i < GB_IO_WAV_END - GB_IO_WAV_START; i++) {
if (i & 1) {
temp = GB_random() & GB_random() & GB_random();
}
else {
temp = GB_random() | GB_random() | GB_random();
}
gb->apu.wave_channel.wave_form[i * 2] = temp >> 4;
gb->apu.wave_channel.wave_form[i * 2 + 1] = temp & 0xF;
gb->io_registers[GB_IO_WAV_START + i] = temp;
}
break;
}
}
for (unsigned i = 0; i < sizeof(gb->extra_oam); i++) {
gb->extra_oam[i] = GB_random();
}
if (GB_is_cgb(gb)) {
for (unsigned i = 0; i < 64; i++) {
gb->background_palettes_data[i] = GB_random(); /* Doesn't really matter as the boot ROM overrides it anyway*/
gb->sprite_palettes_data[i] = GB_random();
}
for (unsigned i = 0; i < 32; i++) {
GB_palette_changed(gb, true, i * 2);
GB_palette_changed(gb, false, i * 2);
}
}
}
void GB_reset(GB_gameboy_t *gb)
{
uint32_t mbc_ram_size = gb->mbc_ram_size;
GB_model_t model = gb->model;
memset(gb, 0, (size_t)GB_GET_SECTION((GB_gameboy_t *) 0, unsaved));
gb->model = model;
gb->version = GB_STRUCT_VERSION;
gb->mbc_rom_bank = 1;
gb->last_rtc_second = time(NULL);
gb->cgb_ram_bank = 1;
gb->io_registers[GB_IO_JOYP] = 0xF;
gb->mbc_ram_size = mbc_ram_size;
if (GB_is_cgb(gb)) {
gb->ram_size = 0x1000 * 8;
gb->vram_size = 0x2000 * 2;
memset(gb->vram, 0, gb->vram_size);
gb->cgb_mode = true;
}
else {
gb->ram_size = 0x2000;
gb->vram_size = 0x2000;
memset(gb->vram, 0, gb->vram_size);
if (gb->rgb_encode_callback) {
gb->sprite_palettes_rgb[4] = gb->sprite_palettes_rgb[0] = gb->background_palettes_rgb[0] =
gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF);
gb->sprite_palettes_rgb[5] = gb->sprite_palettes_rgb[1] = gb->background_palettes_rgb[1] =
gb->rgb_encode_callback(gb, 0xAA, 0xAA, 0xAA);
gb->sprite_palettes_rgb[6] = gb->sprite_palettes_rgb[2] = gb->background_palettes_rgb[2] =
gb->rgb_encode_callback(gb, 0x55, 0x55, 0x55);
gb->sprite_palettes_rgb[7] = gb->sprite_palettes_rgb[3] = gb->background_palettes_rgb[3] =
gb->rgb_encode_callback(gb, 0, 0, 0);
}
}
reset_ram(gb);
/* The serial interrupt always occur on the 0xF7th cycle of every 0x100 cycle since boot. */
gb->serial_cycles = 0x100-0xF7;
gb->io_registers[GB_IO_SC] = 0x7E;
/* These are not deterministic, but 00 (CGB) and FF (DMG) are the most common initial values by far */
gb->io_registers[GB_IO_DMA] = gb->io_registers[GB_IO_OBP0] = gb->io_registers[GB_IO_OBP1] = GB_is_cgb(gb)? 0x00 : 0xFF;
gb->accessed_oam_row = -1;
if (GB_is_sgb(gb)) {
if (!gb->sgb) {
gb->sgb = malloc(sizeof(*gb->sgb));
}
memset(gb->sgb, 0, sizeof(*gb->sgb));
memset(gb->sgb_intro_jingle_phases, 0, sizeof(gb->sgb_intro_jingle_phases));
gb->sgb_intro_sweep_phase = 0;
gb->sgb_intro_sweep_previous_sample = 0;
gb->sgb->intro_animation = -10;
gb->sgb->player_count = 1;
GB_sgb_load_default_data(gb);
}
else {
if (gb->sgb) {
free(gb->sgb);
gb->sgb = NULL;
}
}
/* Todo: Ugly, fixme, see comment in the timer state machine */
gb->div_state = 3;
GB_apu_update_cycles_per_sample(gb);
if (gb->nontrivial_jump_state) {
free(gb->nontrivial_jump_state);
gb->nontrivial_jump_state = NULL;
}
gb->magic = (uintptr_t)'SAME';
}
void GB_switch_model_and_reset(GB_gameboy_t *gb, GB_model_t model)
{
gb->model = model;
if (GB_is_cgb(gb)) {
gb->ram = realloc(gb->ram, gb->ram_size = 0x1000 * 8);
gb->vram = realloc(gb->vram, gb->vram_size = 0x2000 * 2);
}
else {
gb->ram = realloc(gb->ram, gb->ram_size = 0x2000);
gb->vram = realloc(gb->vram, gb->vram_size = 0x2000);
}
GB_rewind_free(gb);
GB_reset(gb);
}
void *GB_get_direct_access(GB_gameboy_t *gb, GB_direct_access_t access, size_t *size, uint16_t *bank)
{
/* Set size and bank to dummy pointers if not set */
size_t dummy_size;
uint16_t dummy_bank;
if (!size) {
size = &dummy_size;
}
if (!bank) {
bank = &dummy_bank;
}
switch (access) {
case GB_DIRECT_ACCESS_ROM:
*size = gb->rom_size;
*bank = gb->mbc_rom_bank;
return gb->rom;
case GB_DIRECT_ACCESS_RAM:
*size = gb->ram_size;
*bank = gb->cgb_ram_bank;
return gb->ram;
case GB_DIRECT_ACCESS_CART_RAM:
*size = gb->mbc_ram_size;
*bank = gb->mbc_ram_bank;
return gb->mbc_ram;
case GB_DIRECT_ACCESS_VRAM:
*size = gb->vram_size;
*bank = gb->cgb_vram_bank;
return gb->vram;
case GB_DIRECT_ACCESS_HRAM:
*size = sizeof(gb->hram);
*bank = 0;
return &gb->hram;
case GB_DIRECT_ACCESS_IO:
*size = sizeof(gb->io_registers);
*bank = 0;
return &gb->io_registers;
case GB_DIRECT_ACCESS_BOOTROM:
*size = GB_is_cgb(gb)? sizeof(gb->boot_rom) : 0x100;
*bank = 0;
return &gb->boot_rom;
case GB_DIRECT_ACCESS_OAM:
*size = sizeof(gb->oam);
*bank = 0;
return &gb->oam;
case GB_DIRECT_ACCESS_BGP:
*size = sizeof(gb->background_palettes_data);
*bank = 0;
return &gb->background_palettes_data;
case GB_DIRECT_ACCESS_OBP:
*size = sizeof(gb->sprite_palettes_data);
*bank = 0;
return &gb->sprite_palettes_data;
case GB_DIRECT_ACCESS_IE:
*size = sizeof(gb->interrupt_enable);
*bank = 0;
return &gb->interrupt_enable;
default:
*size = 0;
*bank = 0;
return NULL;
}
}
void GB_set_clock_multiplier(GB_gameboy_t *gb, double multiplier)
{
gb->clock_multiplier = multiplier;
GB_apu_update_cycles_per_sample(gb);
}
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) {
return SGB_PAL_FREQUENCY * gb->clock_multiplier;
}
return CPU_FREQUENCY * gb->clock_multiplier;
}
unsigned GB_get_screen_width(GB_gameboy_t *gb)
{
return GB_is_sgb(gb)? 256 : 160;
}
unsigned GB_get_screen_height(GB_gameboy_t *gb)
{
return GB_is_sgb(gb)? 224 : 144;
}
unsigned GB_get_player_count(GB_gameboy_t *gb)
{
return GB_is_sgb(gb)? gb->sgb->player_count : 1;
}
void GB_set_update_input_hint_callback(GB_gameboy_t *gb, GB_update_input_hint_callback_t callback)
{
gb->update_input_hint_callback = callback;
}
double GB_get_usual_frame_rate(GB_gameboy_t *gb)
{
return GB_get_clock_rate(gb) / (double)LCDC_PERIOD;
}

702
sameboy/Core/gb.h Normal file
View File

@ -0,0 +1,702 @@
#ifndef GB_h
#define GB_h
#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
#ifdef GB_INTERNAL
#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
#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 = 0x1004,
// GB_MODEL_MGB = 0x100,
GB_MODEL_SGB2 = 0x101,
// 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 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;
/* 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);
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 4MHz 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);
int GB_load_rom_from_buffer(GB_gameboy_t* gb, const unsigned char* buffer, size_t size);
int GB_save_battery(GB_gameboy_t *gb, const char *path);
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);
#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

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

77
sameboy/Core/joypad.c Normal file
View File

@ -0,0 +1,77 @@
#include "gb.h"
#include <assert.h>
void GB_update_joyp(GB_gameboy_t *gb)
{
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 : 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;
}
if (previous_state != (gb->io_registers[GB_IO_JOYP] & 0xF)) {
/* The joypad interrupt DOES occur on CGB (Tested on CGB-CPU-06), unlike what some documents say. */
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);
}

24
sameboy/Core/joypad.h Normal file
View File

@ -0,0 +1,24 @@
#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);
#ifdef GB_INTERNAL
void GB_update_joyp(GB_gameboy_t *gb);
#endif
#endif /* joypad_h */

154
sameboy/Core/mbc.c Normal file
View File

@ -0,0 +1,154 @@
#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;
}
}

32
sameboy/Core/mbc.h Normal file
View File

@ -0,0 +1,32 @@
#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 */

993
sameboy/Core/memory.c Normal file
View File

@ -0,0 +1,993 @@
#include <stdio.h>
#include <stdbool.h>
#include "gb.h"
typedef uint8_t GB_read_function_t(GB_gameboy_t *gb, uint16_t addr);
typedef void GB_write_function_t(GB_gameboy_t *gb, uint16_t addr, uint8_t value);
typedef enum {
GB_BUS_MAIN, /* In DMG: Cart and RAM. In CGB: Cart only */
GB_BUS_RAM, /* In CGB only. */
GB_BUS_VRAM,
GB_BUS_INTERNAL, /* Anything in highram. Might not be the most correct name. */
} GB_bus_t;
static GB_bus_t bus_for_addr(GB_gameboy_t *gb, uint16_t addr)
{
if (addr < 0x8000) {
return GB_BUS_MAIN;
}
if (addr < 0xA000) {
return GB_BUS_VRAM;
}
if (addr < 0xC000) {
return GB_BUS_MAIN;
}
if (addr < 0xFE00) {
return GB_is_cgb(gb)? GB_BUS_RAM : GB_BUS_MAIN;
}
return GB_BUS_INTERNAL;
}
static uint8_t bitwise_glitch(uint8_t a, uint8_t b, uint8_t c)
{
return ((a ^ c) & (b ^ c)) ^ c;
}
static uint8_t bitwise_glitch_read(uint8_t a, uint8_t b, uint8_t c)
{
return b | (a & c);
}
static uint8_t bitwise_glitch_read_increase(uint8_t a, uint8_t b, uint8_t c, uint8_t d)
{
return (b & (a | c | d)) | (a & c & d);
}
void GB_trigger_oam_bug(GB_gameboy_t *gb, uint16_t address)
{
if (GB_is_cgb(gb)) return;
if (address >= 0xFE00 && address < 0xFF00) {
if (gb->accessed_oam_row != 0xff && gb->accessed_oam_row >= 8) {
gb->oam[gb->accessed_oam_row] = bitwise_glitch(gb->oam[gb->accessed_oam_row],
gb->oam[gb->accessed_oam_row - 8],
gb->oam[gb->accessed_oam_row - 4]);
gb->oam[gb->accessed_oam_row + 1] = bitwise_glitch(gb->oam[gb->accessed_oam_row + 1],
gb->oam[gb->accessed_oam_row - 7],
gb->oam[gb->accessed_oam_row - 3]);
for (unsigned i = 2; i < 8; i++) {
gb->oam[gb->accessed_oam_row + i] = gb->oam[gb->accessed_oam_row - 8 + i];
}
}
}
}
void GB_trigger_oam_bug_read(GB_gameboy_t *gb, uint16_t address)
{
if (GB_is_cgb(gb)) return;
if (address >= 0xFE00 && address < 0xFF00) {
if (gb->accessed_oam_row != 0xff && gb->accessed_oam_row >= 8) {
gb->oam[gb->accessed_oam_row - 8] =
gb->oam[gb->accessed_oam_row] = bitwise_glitch_read(gb->oam[gb->accessed_oam_row],
gb->oam[gb->accessed_oam_row - 8],
gb->oam[gb->accessed_oam_row - 4]);
gb->oam[gb->accessed_oam_row - 7] =
gb->oam[gb->accessed_oam_row + 1] = bitwise_glitch_read(gb->oam[gb->accessed_oam_row + 1],
gb->oam[gb->accessed_oam_row - 7],
gb->oam[gb->accessed_oam_row - 3]);
for (unsigned i = 2; i < 8; i++) {
gb->oam[gb->accessed_oam_row + i] = gb->oam[gb->accessed_oam_row - 8 + i];
}
}
}
}
void GB_trigger_oam_bug_read_increase(GB_gameboy_t *gb, uint16_t address)
{
if (GB_is_cgb(gb)) return;
if (address >= 0xFE00 && address < 0xFF00) {
if (gb->accessed_oam_row != 0xff && gb->accessed_oam_row >= 0x20 && gb->accessed_oam_row < 0x98) {
gb->oam[gb->accessed_oam_row - 0x8] = bitwise_glitch_read_increase(gb->oam[gb->accessed_oam_row - 0x10],
gb->oam[gb->accessed_oam_row - 0x08],
gb->oam[gb->accessed_oam_row ],
gb->oam[gb->accessed_oam_row - 0x04]
);
gb->oam[gb->accessed_oam_row - 0x7] = bitwise_glitch_read_increase(gb->oam[gb->accessed_oam_row - 0x0f],
gb->oam[gb->accessed_oam_row - 0x07],
gb->oam[gb->accessed_oam_row + 0x01],
gb->oam[gb->accessed_oam_row - 0x03]
);
for (unsigned i = 0; i < 8; i++) {
gb->oam[gb->accessed_oam_row + i] = gb->oam[gb->accessed_oam_row - 0x10 + i] = gb->oam[gb->accessed_oam_row - 0x08 + i];
}
}
}
}
static bool is_addr_in_dma_use(GB_gameboy_t *gb, uint16_t addr)
{
if (!gb->dma_steps_left || (gb->dma_cycles < 0 && !gb->is_dma_restarting) || addr >= 0xFE00) return false;
return bus_for_addr(gb, addr) == bus_for_addr(gb, gb->dma_current_src);
}
static uint8_t read_rom(GB_gameboy_t *gb, uint16_t addr)
{
if (addr < 0x100 && !gb->boot_rom_finished) {
return gb->boot_rom[addr];
}
if (addr >= 0x200 && addr < 0x900 && GB_is_cgb(gb) && !gb->boot_rom_finished) {
return gb->boot_rom[addr];
}
if (!gb->rom_size) {
return 0xFF;
}
unsigned effective_address = (addr & 0x3FFF) + gb->mbc_rom0_bank * 0x4000;
return gb->rom[effective_address & (gb->rom_size - 1)];
}
static uint8_t read_mbc_rom(GB_gameboy_t *gb, uint16_t addr)
{
unsigned effective_address = (addr & 0x3FFF) + gb->mbc_rom_bank * 0x4000;
return gb->rom[effective_address & (gb->rom_size - 1)];
}
static uint8_t read_vram(GB_gameboy_t *gb, uint16_t addr)
{
if (gb->vram_read_blocked) {
return 0xFF;
}
return gb->vram[(addr & 0x1FFF) + (uint16_t) gb->cgb_vram_bank * 0x2000];
}
static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr)
{
if ((!gb->mbc_ram_enable || !gb->mbc_ram_size) &&
gb->cartridge_type->mbc_subtype != GB_CAMERA &&
gb->cartridge_type->mbc_type != GB_HUC1) return 0xFF;
if (gb->cartridge_type->has_rtc && gb->mbc_ram_bank >= 8 && gb->mbc_ram_bank <= 0xC) {
/* RTC read */
gb->rtc_latched.high |= ~0xC1; /* Not all bytes in RTC high are used. */
return gb->rtc_latched.data[gb->mbc_ram_bank - 8];
}
if (gb->camera_registers_mapped) {
return GB_camera_read_register(gb, addr);
}
if (!gb->mbc_ram) {
return 0xFF;
}
if (gb->cartridge_type->mbc_subtype == GB_CAMERA && gb->mbc_ram_bank == 0 && addr >= 0xa100 && addr < 0xaf00) {
return GB_camera_read_image(gb, addr - 0xa100);
}
uint8_t ret = gb->mbc_ram[((addr & 0x1FFF) + gb->mbc_ram_bank * 0x2000) & (gb->mbc_ram_size - 1)];
if (gb->cartridge_type->mbc_type == GB_MBC2) {
ret |= 0xF0;
}
return ret;
}
static uint8_t read_ram(GB_gameboy_t *gb, uint16_t addr)
{
return gb->ram[addr & 0x0FFF];
}
static uint8_t read_banked_ram(GB_gameboy_t *gb, uint16_t addr)
{
return gb->ram[(addr & 0x0FFF) + gb->cgb_ram_bank * 0x1000];
}
static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
{
if (gb->hdma_on) {
return gb->last_opcode_read;
}
if (addr < 0xFE00) {
return read_banked_ram(gb, addr);
}
if (addr < 0xFF00) {
if (gb->oam_write_blocked && !GB_is_cgb(gb)) {
GB_trigger_oam_bug_read(gb, addr);
return 0xff;
}
if ((gb->dma_steps_left && (gb->dma_cycles > 0 || gb->is_dma_restarting))) {
/* Todo: Does reading from OAM during DMA causes the OAM bug? */
return 0xff;
}
if (gb->oam_read_blocked) {
if (!GB_is_cgb(gb)) {
if (addr < 0xFEA0) {
if (gb->accessed_oam_row == 0) {
gb->oam[(addr & 0xf8)] =
gb->oam[0] = bitwise_glitch_read(gb->oam[0],
gb->oam[(addr & 0xf8)],
gb->oam[(addr & 0xfe)]);
gb->oam[(addr & 0xf8) + 1] =
gb->oam[1] = bitwise_glitch_read(gb->oam[1],
gb->oam[(addr & 0xf8) + 1],
gb->oam[(addr & 0xfe) | 1]);
for (unsigned i = 2; i < 8; i++) {
gb->oam[i] = gb->oam[(addr & 0xf8) + i];
}
}
else if (gb->accessed_oam_row == 0xa0) {
gb->oam[0x9e] = bitwise_glitch_read(gb->oam[0x9c],
gb->oam[0x9e],
gb->oam[(addr & 0xf8) | 6]);
gb->oam[0x9f] = bitwise_glitch_read(gb->oam[0x9d],
gb->oam[0x9f],
gb->oam[(addr & 0xf8) | 7]);
for (unsigned i = 0; i < 8; i++) {
gb->oam[(addr & 0xf8) + i] = gb->oam[0x98 + i];
}
}
}
}
return 0xff;
}
if (addr < 0xFEA0) {
return gb->oam[addr & 0xFF];
}
if (gb->oam_read_blocked) {
return 0xFF;
}
switch (gb->model) {
case GB_MODEL_CGB_E:
case GB_MODEL_AGB:
return (addr & 0xF0) | ((addr >> 4) & 0xF);
/*
case GB_MODEL_CGB_D:
if (addr > 0xfec0) {
addr |= 0xf0;
}
return gb->extra_oam[addr - 0xfea0];
*/
case GB_MODEL_CGB_C:
/*
case GB_MODEL_CGB_B:
case GB_MODEL_CGB_A:
case GB_MODEL_CGB_0:
*/
addr &= ~0x18;
return gb->extra_oam[addr - 0xfea0];
case GB_MODEL_DMG_B:
case GB_MODEL_SGB_NTSC:
case GB_MODEL_SGB_PAL:
case GB_MODEL_SGB2:
;
}
}
if (addr < 0xFF00) {
return 0;
}
if (addr < 0xFF80) {
switch (addr & 0xFF) {
case GB_IO_IF:
return gb->io_registers[GB_IO_IF] | 0xE0;
case GB_IO_TAC:
return gb->io_registers[GB_IO_TAC] | 0xF8;
case GB_IO_STAT:
return gb->io_registers[GB_IO_STAT] | 0x80;
case GB_IO_DMG_EMULATION_INDICATION:
if (!gb->cgb_mode) {
return 0xFF;
}
return gb->io_registers[GB_IO_DMG_EMULATION_INDICATION] | 0xFE;
case GB_IO_PCM_12:
if (!GB_is_cgb(gb)) return 0xFF;
return (gb->apu.is_active[GB_SQUARE_2] ? (gb->apu.samples[GB_SQUARE_2] << 4) : 0) |
(gb->apu.is_active[GB_SQUARE_1] ? (gb->apu.samples[GB_SQUARE_1]) : 0);
case GB_IO_PCM_34:
if (!GB_is_cgb(gb)) return 0xFF;
return (gb->apu.is_active[GB_NOISE] ? (gb->apu.samples[GB_NOISE] << 4) : 0) |
(gb->apu.is_active[GB_WAVE] ? (gb->apu.samples[GB_WAVE]) : 0);
case GB_IO_JOYP:
GB_timing_sync(gb);
case GB_IO_TMA:
case GB_IO_LCDC:
case GB_IO_SCY:
case GB_IO_SCX:
case GB_IO_LY:
case GB_IO_LYC:
case GB_IO_BGP:
case GB_IO_OBP0:
case GB_IO_OBP1:
case GB_IO_WY:
case GB_IO_WX:
case GB_IO_SC:
case GB_IO_SB:
case GB_IO_DMA:
return gb->io_registers[addr & 0xFF];
case GB_IO_TIMA:
if (gb->tima_reload_state == GB_TIMA_RELOADING) {
return 0;
}
return gb->io_registers[GB_IO_TIMA];
case GB_IO_DIV:
return gb->div_counter >> 8;
case GB_IO_HDMA5:
if (!gb->cgb_mode) return 0xFF;
return ((gb->hdma_on || gb->hdma_on_hblank)? 0 : 0x80) | ((gb->hdma_steps_left - 1) & 0x7F);
case GB_IO_SVBK:
if (!gb->cgb_mode) {
return 0xFF;
}
return gb->cgb_ram_bank | ~0x7;
case GB_IO_VBK:
if (!GB_is_cgb(gb)) {
return 0xFF;
}
return gb->cgb_vram_bank | ~0x1;
/* Todo: It seems that a CGB in DMG mode can access BGPI and OBPI, but not BGPD and OBPD? */
case GB_IO_BGPI:
case GB_IO_OBPI:
if (!GB_is_cgb(gb)) {
return 0xFF;
}
return gb->io_registers[addr & 0xFF] | 0x40;
case GB_IO_BGPD:
case GB_IO_OBPD:
{
if (!gb->cgb_mode && gb->boot_rom_finished) {
return 0xFF;
}
if (gb->cgb_palettes_blocked) {
return 0xFF;
}
uint8_t index_reg = (addr & 0xFF) - 1;
return ((addr & 0xFF) == GB_IO_BGPD?
gb->background_palettes_data :
gb->sprite_palettes_data)[gb->io_registers[index_reg] & 0x3F];
}
case GB_IO_KEY1:
if (!gb->cgb_mode) {
return 0xFF;
}
return (gb->io_registers[GB_IO_KEY1] & 0x7F) | (gb->cgb_double_speed? 0xFE : 0x7E);
case GB_IO_RP: {
if (!gb->cgb_mode) return 0xFF;
/* You will read your own IR LED if it's on. */
bool read_value = gb->infrared_input || (gb->io_registers[GB_IO_RP] & 1);
uint8_t ret = (gb->io_registers[GB_IO_RP] & 0xC1) | 0x3C;
if ((gb->io_registers[GB_IO_RP] & 0xC0) == 0xC0 && read_value) {
ret |= 2;
}
return ret;
}
case GB_IO_UNKNOWN2:
case GB_IO_UNKNOWN3:
return GB_is_cgb(gb)? gb->io_registers[addr & 0xFF] : 0xFF;
case GB_IO_UNKNOWN4:
return gb->cgb_mode? gb->io_registers[addr & 0xFF] : 0xFF;
case GB_IO_UNKNOWN5:
return GB_is_cgb(gb)? gb->io_registers[addr & 0xFF] | 0x8F : 0xFF;
default:
if ((addr & 0xFF) >= GB_IO_NR10 && (addr & 0xFF) <= GB_IO_WAV_END) {
return GB_apu_read(gb, addr & 0xFF);
}
return 0xFF;
}
/* Hardware registers */
return 0;
}
if (addr == 0xFFFF) {
/* Interrupt Mask */
return gb->interrupt_enable;
}
/* HRAM */
return gb->hram[addr - 0xFF80];
}
static GB_read_function_t * const read_map[] =
{
read_rom, read_rom, read_rom, read_rom, /* 0XXX, 1XXX, 2XXX, 3XXX */
read_mbc_rom, read_mbc_rom, read_mbc_rom, read_mbc_rom, /* 4XXX, 5XXX, 6XXX, 7XXX */
read_vram, read_vram, /* 8XXX, 9XXX */
read_mbc_ram, read_mbc_ram, /* AXXX, BXXX */
read_ram, read_banked_ram, /* CXXX, DXXX */
read_ram, read_high_memory, /* EXXX FXXX */
};
uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr)
{
if (gb->n_watchpoints) {
GB_debugger_test_read_watchpoint(gb, addr);
}
if (is_addr_in_dma_use(gb, addr)) {
addr = gb->dma_current_src;
}
return read_map[addr >> 12](gb, addr);
}
static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
{
switch (gb->cartridge_type->mbc_type) {
case GB_NO_MBC: return;
case GB_MBC1:
switch (addr & 0xF000) {
case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break;
case 0x2000: case 0x3000: gb->mbc1.bank_low = value; break;
case 0x4000: case 0x5000: gb->mbc1.bank_high = value; break;
case 0x6000: case 0x7000: gb->mbc1.mode = value; break;
}
break;
case GB_MBC2:
switch (addr & 0xF000) {
case 0x0000: case 0x1000: if (!(addr & 0x100)) gb->mbc_ram_enable = (value & 0xF) == 0xA; break;
case 0x2000: case 0x3000: if ( addr & 0x100) gb->mbc2.rom_bank = value; break;
}
break;
case GB_MBC3:
switch (addr & 0xF000) {
case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break;
case 0x2000: case 0x3000: gb->mbc3.rom_bank = value; break;
case 0x4000: case 0x5000: gb->mbc3.ram_bank = value; break;
case 0x6000: case 0x7000:
if (!gb->rtc_latch && (value & 1)) { /* Todo: verify condition is correct */
memcpy(&gb->rtc_latched, &gb->rtc_real, sizeof(gb->rtc_real));
}
gb->rtc_latch = value & 1;
break;
}
break;
case GB_MBC5:
switch (addr & 0xF000) {
case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break;
case 0x2000: gb->mbc5.rom_bank_low = value; break;
case 0x3000: gb->mbc5.rom_bank_high = value; break;
case 0x4000: case 0x5000:
if (gb->cartridge_type->has_rumble) {
if (!!(value & 8) != gb->rumble_state) {
gb->rumble_state = !gb->rumble_state;
if (gb->rumble_callback) {
gb->rumble_callback(gb, gb->rumble_state);
}
}
value &= 7;
}
gb->mbc5.ram_bank = value;
gb->camera_registers_mapped = (value & 0x10) && gb->cartridge_type->mbc_subtype == GB_CAMERA;
break;
}
break;
case GB_HUC1:
switch (addr & 0xF000) {
case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break;
case 0x2000: case 0x3000: gb->huc1.bank_low = value; break;
case 0x4000: case 0x5000: gb->huc1.bank_high = value; break;
case 0x6000: case 0x7000: gb->huc1.mode = value; break;
}
break;
case GB_HUC3:
switch (addr & 0xF000) {
case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break;
case 0x2000: case 0x3000: gb->huc3.rom_bank = value; break;
case 0x4000: case 0x5000: gb->huc3.ram_bank = value; break;
}
break;
}
GB_update_mbc_mappings(gb);
}
static void write_vram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
{
if (gb->vram_write_blocked) {
//GB_log(gb, "Wrote %02x to %04x (VRAM) during mode 3\n", value, addr);
return;
}
gb->vram[(addr & 0x1FFF) + (uint16_t) gb->cgb_vram_bank * 0x2000] = value;
}
static void write_mbc_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
{
if (gb->camera_registers_mapped) {
GB_camera_write_register(gb, addr, value);
return;
}
if (!gb->mbc_ram_enable || !gb->mbc_ram_size) return;
if (gb->cartridge_type->has_rtc && gb->mbc_ram_bank >= 8 && gb->mbc_ram_bank <= 0xC) {
gb->rtc_latched.data[gb->mbc_ram_bank - 8] = gb->rtc_real.data[gb->mbc_ram_bank - 8] = value;
return;
}
if (!gb->mbc_ram) {
return;
}
gb->mbc_ram[((addr & 0x1FFF) + gb->mbc_ram_bank * 0x2000) & (gb->mbc_ram_size - 1)] = value;
}
static void write_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
{
gb->ram[addr & 0x0FFF] = value;
}
static void write_banked_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
{
gb->ram[(addr & 0x0FFF) + gb->cgb_ram_bank * 0x1000] = value;
}
static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
{
if (addr < 0xFE00) {
GB_log(gb, "Wrote %02x to %04x (RAM Mirror)\n", value, addr);
write_banked_ram(gb, addr, value);
return;
}
if (addr < 0xFF00) {
if (gb->oam_write_blocked) {
GB_trigger_oam_bug(gb, addr);
return;
}
if ((gb->dma_steps_left && (gb->dma_cycles > 0 || gb->is_dma_restarting))) {
/* Todo: Does writing to OAM during DMA causes the OAM bug? */
return;
}
if (GB_is_cgb(gb)) {
if (addr < 0xFEA0) {
gb->oam[addr & 0xFF] = value;
}
switch (gb->model) {
/*
case GB_MODEL_CGB_D:
if (addr > 0xfec0) {
addr |= 0xf0;
}
gb->extra_oam[addr - 0xfea0] = value;
break;
*/
case GB_MODEL_CGB_C:
/*
case GB_MODEL_CGB_B:
case GB_MODEL_CGB_A:
case GB_MODEL_CGB_0:
*/
addr &= ~0x18;
gb->extra_oam[addr - 0xfea0] = value;
break;
case GB_MODEL_DMG_B:
case GB_MODEL_SGB_NTSC:
case GB_MODEL_SGB_PAL:
case GB_MODEL_SGB2:
case GB_MODEL_CGB_E:
case GB_MODEL_AGB:
break;
}
return;
}
if (addr < 0xFEA0) {
if (gb->accessed_oam_row == 0xa0) {
for (unsigned i = 0; i < 8; i++) {
if ((i & 6) != (addr & 6)) {
gb->oam[(addr & 0xf8) + i] = gb->oam[0x98 + i];
}
else {
gb->oam[(addr & 0xf8) + i] = bitwise_glitch(gb->oam[(addr & 0xf8) + i], gb->oam[0x9c], gb->oam[0x98 + i]);
}
}
}
gb->oam[addr & 0xFF] = value;
if (gb->accessed_oam_row == 0) {
gb->oam[0] = bitwise_glitch(gb->oam[0],
gb->oam[(addr & 0xf8)],
gb->oam[(addr & 0xfe)]);
gb->oam[1] = bitwise_glitch(gb->oam[1],
gb->oam[(addr & 0xf8) + 1],
gb->oam[(addr & 0xfe) | 1]);
for (unsigned i = 2; i < 8; i++) {
gb->oam[i] = gb->oam[(addr & 0xf8) + i];
}
}
}
else if (gb->accessed_oam_row == 0) {
gb->oam[addr & 0x7] = value;
}
return;
}
/* Todo: Clean this code up: use a function table and move relevant code to display.c and timing.c
(APU read and writes are already at apu.c) */
if (addr < 0xFF80) {
/* Hardware registers */
switch (addr & 0xFF) {
case GB_IO_WX:
GB_window_related_write(gb, addr & 0xFF, value);
break;
case GB_IO_IF:
case GB_IO_SCX:
case GB_IO_SCY:
case GB_IO_BGP:
case GB_IO_OBP0:
case GB_IO_OBP1:
case GB_IO_WY:
case GB_IO_SB:
case GB_IO_DMG_EMULATION_INDICATION:
case GB_IO_UNKNOWN2:
case GB_IO_UNKNOWN3:
case GB_IO_UNKNOWN4:
case GB_IO_UNKNOWN5:
gb->io_registers[addr & 0xFF] = value;
return;
case GB_IO_LYC:
/* TODO: Probably completely wrong in double speed mode */
/* TODO: This hack is disgusting */
if (gb->display_state == 29 && GB_is_cgb(gb)) {
gb->ly_for_comparison = 153;
GB_STAT_update(gb);
gb->ly_for_comparison = 0;
}
gb->io_registers[addr & 0xFF] = value;
/* These are the states when LY changes, let the display routine call GB_STAT_update for use
so it correctly handles T-cycle accurate LYC writes */
if (!GB_is_cgb(gb) || (
gb->display_state != 6 &&
gb->display_state != 26 &&
gb->display_state != 15 &&
gb->display_state != 16)) {
/* More hacks to make LYC write conflicts work */
if (gb->display_state == 14 && GB_is_cgb(gb)) {
gb->ly_for_comparison = 153;
GB_STAT_update(gb);
gb->ly_for_comparison = -1;
}
else {
GB_STAT_update(gb);
}
}
return;
case GB_IO_TIMA:
if (gb->tima_reload_state != GB_TIMA_RELOADED) {
gb->io_registers[GB_IO_TIMA] = value;
}
return;
case GB_IO_TMA:
gb->io_registers[GB_IO_TMA] = value;
if (gb->tima_reload_state != GB_TIMA_RUNNING) {
gb->io_registers[GB_IO_TIMA] = value;
}
return;
case GB_IO_TAC:
GB_emulate_timer_glitch(gb, gb->io_registers[GB_IO_TAC], value);
gb->io_registers[GB_IO_TAC] = value;
return;
case GB_IO_LCDC:
if ((value & 0x80) && !(gb->io_registers[GB_IO_LCDC] & 0x80)) {
gb->display_cycles = 0;
gb->display_state = 0;
if (GB_is_sgb(gb)) {
gb->frame_skip_state = GB_FRAMESKIP_SECOND_FRAME_RENDERED;
}
else if (gb->frame_skip_state == GB_FRAMESKIP_SECOND_FRAME_RENDERED) {
gb->frame_skip_state = GB_FRAMESKIP_LCD_TURNED_ON;
}
}
else if (!(value & 0x80) && (gb->io_registers[GB_IO_LCDC] & 0x80)) {
/* Sync after turning off LCD */
GB_timing_sync(gb);
GB_lcd_off(gb);
}
/* Writing to LCDC might enable to disable the window, so we write it via GB_window_related_write */
GB_window_related_write(gb, addr & 0xFF, value);
return;
case GB_IO_STAT:
/* Delete previous R/W bits */
gb->io_registers[GB_IO_STAT] &= 7;
/* Set them by value */
gb->io_registers[GB_IO_STAT] |= value & ~7;
/* Set unused bit to 1 */
gb->io_registers[GB_IO_STAT] |= 0x80;
GB_STAT_update(gb);
return;
case GB_IO_DIV:
/* Reset the div state machine */
gb->div_state = 0;
gb->div_cycles = 0;
return;
case GB_IO_JOYP:
GB_sgb_write(gb, value);
gb->io_registers[GB_IO_JOYP] = value & 0xF0;
GB_update_joyp(gb);
return;
case GB_IO_BIOS:
gb->boot_rom_finished = true;
return;
case GB_IO_DMG_EMULATION:
if (GB_is_cgb(gb) && !gb->boot_rom_finished) {
gb->cgb_mode = !(value & 0xC); /* The real "contents" of this register aren't quite known yet. */
}
return;
case GB_IO_DMA:
if (gb->dma_steps_left) {
/* This is not correct emulation, since we're not really delaying the second DMA.
One write that should have happened in the first DMA will not happen. However,
since that byte will be overwritten by the second DMA before it can actually be
read, it doesn't actually matter. */
gb->is_dma_restarting = true;
}
gb->dma_cycles = -7;
gb->dma_current_dest = 0;
gb->dma_current_src = value << 8;
gb->dma_steps_left = 0xa0;
gb->io_registers[GB_IO_DMA] = value;
return;
case GB_IO_SVBK:
if (!gb->cgb_mode) {
return;
}
gb->cgb_ram_bank = value & 0x7;
if (!gb->cgb_ram_bank) {
gb->cgb_ram_bank++;
}
return;
case GB_IO_VBK:
if (!gb->cgb_mode) {
return;
}
gb->cgb_vram_bank = value & 0x1;
return;
case GB_IO_BGPI:
case GB_IO_OBPI:
if (!GB_is_cgb(gb)) {
return;
}
gb->io_registers[addr & 0xFF] = value;
return;
case GB_IO_BGPD:
case GB_IO_OBPD:
if (!gb->cgb_mode && gb->boot_rom_finished) {
/* Todo: Due to the behavior of a broken Game & Watch Gallery 2 ROM on a real CGB. A proper test ROM
is required. */
return;
}
uint8_t index_reg = (addr & 0xFF) - 1;
if (gb->cgb_palettes_blocked) {
if (gb->io_registers[index_reg] & 0x80) {
gb->io_registers[index_reg]++;
gb->io_registers[index_reg] |= 0x80;
}
return;
}
((addr & 0xFF) == GB_IO_BGPD?
gb->background_palettes_data :
gb->sprite_palettes_data)[gb->io_registers[index_reg] & 0x3F] = value;
GB_palette_changed(gb, (addr & 0xFF) == GB_IO_BGPD, gb->io_registers[index_reg] & 0x3F);
if (gb->io_registers[index_reg] & 0x80) {
gb->io_registers[index_reg]++;
gb->io_registers[index_reg] |= 0x80;
}
return;
case GB_IO_KEY1:
if (!gb->cgb_mode) {
return;
}
gb->io_registers[GB_IO_KEY1] = value;
return;
case GB_IO_HDMA1:
if (gb->cgb_mode) {
gb->hdma_current_src &= 0xF0;
gb->hdma_current_src |= value << 8;
}
return;
case GB_IO_HDMA2:
if (gb->cgb_mode) {
gb->hdma_current_src &= 0xFF00;
gb->hdma_current_src |= value & 0xF0;
}
return;
case GB_IO_HDMA3:
if (gb->cgb_mode) {
gb->hdma_current_dest &= 0xF0;
gb->hdma_current_dest |= value << 8;
}
return;
case GB_IO_HDMA4:
if (gb->cgb_mode) {
gb->hdma_current_dest &= 0x1F00;
gb->hdma_current_dest |= value & 0xF0;
}
return;
case GB_IO_HDMA5:
if (!gb->cgb_mode) return;
if ((value & 0x80) == 0 && gb->hdma_on_hblank) {
gb->hdma_on_hblank = false;
return;
}
gb->hdma_on = (value & 0x80) == 0;
gb->hdma_on_hblank = (value & 0x80) != 0;
if (gb->hdma_on_hblank && (gb->io_registers[GB_IO_STAT] & 3) == 0) {
gb->hdma_on = true;
}
gb->io_registers[GB_IO_HDMA5] = value;
gb->hdma_steps_left = (gb->io_registers[GB_IO_HDMA5] & 0x7F) + 1;
/* Todo: Verify this. Gambatte's DMA tests require this. */
if (gb->hdma_current_dest + (gb->hdma_steps_left << 4) > 0xFFFF) {
gb->hdma_steps_left = (0x10000 - gb->hdma_current_dest) >> 4;
}
gb->hdma_cycles = -12;
return;
/* Todo: what happens when starting a transfer during a transfer?
What happens when starting a transfer during external clock?
*/
case GB_IO_SC:
if (!gb->cgb_mode) {
value |= 2;
}
gb->io_registers[GB_IO_SC] = value | (~0x83);
if ((value & 0x80) && (value & 0x1) ) {
gb->serial_length = gb->cgb_mode && (value & 2)? 16 : 512;
gb->serial_count = 0;
/* Todo: This is probably incorrect for CGB's faster clock mode. */
gb->serial_cycles &= 0xFF;
if (gb->serial_transfer_bit_start_callback) {
gb->serial_transfer_bit_start_callback(gb, gb->io_registers[GB_IO_SB] & 0x80);
}
}
else {
gb->serial_length = 0;
}
return;
case GB_IO_RP: {
if (!GB_is_cgb(gb)) {
return;
}
if ((value & 1) != (gb->io_registers[GB_IO_RP] & 1)) {
if (gb->infrared_callback) {
gb->infrared_callback(gb, value & 1, gb->cycles_since_ir_change);
gb->cycles_since_ir_change = 0;
}
}
gb->io_registers[GB_IO_RP] = value;
return;
}
default:
if ((addr & 0xFF) >= GB_IO_NR10 && (addr & 0xFF) <= GB_IO_WAV_END) {
GB_apu_write(gb, addr & 0xFF, value);
return;
}
GB_log(gb, "Wrote %02x to %04x (HW Register)\n", value, addr);
return;
}
}
if (addr == 0xFFFF) {
/* Interrupt mask */
gb->interrupt_enable = value;
return;
}
/* HRAM */
gb->hram[addr - 0xFF80] = value;
}
static GB_write_function_t * const write_map[] =
{
write_mbc, write_mbc, write_mbc, write_mbc, /* 0XXX, 1XXX, 2XXX, 3XXX */
write_mbc, write_mbc, write_mbc, write_mbc, /* 4XXX, 5XXX, 6XXX, 7XXX */
write_vram, write_vram, /* 8XXX, 9XXX */
write_mbc_ram, write_mbc_ram, /* AXXX, BXXX */
write_ram, write_banked_ram, /* CXXX, DXXX */
write_ram, write_high_memory, /* EXXX FXXX */
};
void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
{
if (gb->n_watchpoints) {
GB_debugger_test_write_watchpoint(gb, addr, value);
}
if (is_addr_in_dma_use(gb, addr)) {
/* Todo: What should happen? Will this affect DMA? Will data be written? What and where? */
return;
}
write_map[addr >> 12](gb, addr, value);
}
void GB_dma_run(GB_gameboy_t *gb)
{
while (gb->dma_cycles >= 4 && gb->dma_steps_left) {
/* Todo: measure this value */
gb->dma_cycles -= 4;
gb->dma_steps_left--;
if (gb->dma_current_src < 0xe000) {
gb->oam[gb->dma_current_dest++] = GB_read_memory(gb, gb->dma_current_src);
}
else {
/* Todo: Not correct on the CGB */
gb->oam[gb->dma_current_dest++] = GB_read_memory(gb, gb->dma_current_src & ~0x2000);
}
/* dma_current_src must be the correct value during GB_read_memory */
gb->dma_current_src++;
if (!gb->dma_steps_left) {
gb->is_dma_restarting = false;
}
}
}
void GB_hdma_run(GB_gameboy_t *gb)
{
if (!gb->hdma_on) return;
while (gb->hdma_cycles >= 0x4) {
gb->hdma_cycles -= 0x4;
GB_write_memory(gb, 0x8000 | (gb->hdma_current_dest++ & 0x1FFF), GB_read_memory(gb, (gb->hdma_current_src++)));
if ((gb->hdma_current_dest & 0xf) == 0) {
if (--gb->hdma_steps_left == 0) {
gb->hdma_on = false;
gb->hdma_on_hblank = false;
gb->hdma_starting = false;
gb->io_registers[GB_IO_HDMA5] &= 0x7F;
break;
}
if (gb->hdma_on_hblank) {
gb->hdma_on = false;
break;
}
}
}
}

15
sameboy/Core/memory.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef memory_h
#define memory_h
#include "gb_struct_def.h"
#include <stdint.h>
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 */

216
sameboy/Core/printer.c Normal file
View File

@ -0,0 +1,216 @@
#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;
}

63
sameboy/Core/printer.h Normal file
View File

@ -0,0 +1,63 @@
#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

38
sameboy/Core/random.c Normal file
View File

@ -0,0 +1,38 @@
#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();
}
}

12
sameboy/Core/random.h Normal file
View File

@ -0,0 +1,12 @@
#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 */

208
sameboy/Core/rewind.c Normal file
View File

@ -0,0 +1,208 @@
#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);
}
}

14
sameboy/Core/rewind.h Normal file
View File

@ -0,0 +1,14 @@
#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

377
sameboy/Core/save_state.c Normal file
View File

@ -0,0 +1,377 @@
#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_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_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_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_sgb(gb) != GB_is_sgb(save)) {
GB_log(gb, "The save state is %sfor a Super Game Boy. Try changing the emulated model.\n", GB_is_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_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_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

24
sameboy/Core/save_state.h Normal file
View File

@ -0,0 +1,24 @@
/* 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 */

756
sameboy/Core/sgb.c Normal file
View File

@ -0,0 +1,756 @@
#include "gb.h"
#include "random.h"
#include <math.h>
#include <assert.h>
#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 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) {
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;
}
if (gb->sgb->command[0] == 0xf9) {
if (gb->sgb->command[0xc] != 3) { // SGB Flag
gb->sgb->disable_commands = true;
}
}
else if (gb->sgb->command[0] == 0xfb) {
if (gb->sgb->command[0x3] != 0x33) { // Old licensee code
gb->sgb->disable_commands = true;
}
}
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:
gb->sgb->player_count = (uint8_t[]){1, 2, 1, 4}[gb->sgb->command[1] & 3];
gb->sgb->current_player = gb->sgb->player_count - 1;
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_is_sgb(gb)) 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;
}
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) {
gb->sgb->current_player++;
gb->sgb->current_player &= gb->sgb->player_count - 1;
}
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 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);
}
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;
r = scale_channel(r);
g = scale_channel(g);
b = scale_channel(b);
return gb->rgb_encode_callback(gb, r, g, b);
}
#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] = 0x639E;
gb->sgb->effective_palettes[1] = 0x263A;
gb->sgb->effective_palettes[2] = 0x10D4;
gb->sgb->effective_palettes[3] = 0x2866;
}
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;
}

60
sameboy/Core/sgb.h Normal file
View File

@ -0,0 +1,60 @@
#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;
};
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

@ -0,0 +1,563 @@
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;

658
sameboy/Core/sgb_border.inc Normal file
View File

@ -0,0 +1,658 @@
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
};

1493
sameboy/Core/sm83_cpu.c Normal file

File diff suppressed because it is too large Load Diff

11
sameboy/Core/sm83_cpu.h Normal file
View File

@ -0,0 +1,11 @@
#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

@ -0,0 +1,788 @@
#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);
}
}

110
sameboy/Core/symbol_hash.c Normal file
View File

@ -0,0 +1,110 @@
#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

@ -0,0 +1,38 @@
#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 */

294
sameboy/Core/timing.c Normal file
View File

@ -0,0 +1,294 @@
#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;
}
}
}
}
}
}
}

44
sameboy/Core/timing.h Normal file
View File

@ -0,0 +1,44 @@
#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 */

21
sameboy/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2015-2019 Lior Halphon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.