mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
52f34ea470
commit
bd8e94a7c7
|
@ -1,3 +1,5 @@
|
|||
#define double float
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
#include "stream.cpp"
|
||||
|
@ -69,3 +71,5 @@ auto Audio::process() -> void {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
#undef double
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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/";
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#pragma once
|
||||
#define uintmax uint64
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
|
@ -59,3 +60,5 @@ protected:
|
|||
};
|
||||
|
||||
}
|
||||
|
||||
#undef uintmax
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
};
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ auto MSU1::main() -> void {
|
|||
}
|
||||
}
|
||||
|
||||
stream->sample(left, right);
|
||||
stream->sample(float(left), float(right));
|
||||
step(1);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 */
|
||||
}
|
||||
}
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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 */
|
File diff suppressed because it is too large
Load Diff
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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 */
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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;
|
|
@ -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
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -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 */
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 */
|
|
@ -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.
|
Loading…
Reference in New Issue