Implement bsnes version 115 as a new core (#2740)

* Slam all this shit in here i don't care

* no lzma pls

* un-hack things that i hacked earlier (closer to bsnes source now)

* remove more unused files

* remove more files

* get some stuff working on this weird ass branch

* do this to get actual video (holy shit it works) while palette is incorrect

* make video look correct and hopefully fix stack waterbox allocation size uh ?

* Move the new bsnes core to its own dir as a new core and get input working

* remove leftover files from old bsnes

* fix some shit for now

* make lag frame detection work

* Improve cartridge loading to hopefully make sgb work (haven't tested)

- also changes some audio buffer stuff, might be better or worse than before idk
- need to figure out the saveram stuff
- path requests might actually completely fail atm, no idea how to verify that

* refactor to use a switch instead of some weird array with function pointers

- and implement snes_get_mapper, might be helpful or smth idk

* implement entropy c++-side and delete all this useless code holy

* delete dumb unnecessary code

* implement snes_peek_logical_register c++-side

* normalize all indentation

* attempt to properly support sharprtc and epsonrtc data loading and writing

* Duplicate winforms code to add entropy support c#-side and implement layer_enable functionality

The duplicated code is just so i can have a proper window for the new bsnes core. I do not like duplicating code like this, so this should be improved if possible

* Checkpoint for the start of getting rid of the ugly api wrapper stuff

* Next checkpoint for a full api refactor

* bullshit denied

just no.
every file is either copied or manually edited or written from scratch etc.
do not. force. one indent_style on every single file.
It just does not work.

* remove the entire eMessage_CMD handling and convert to native function calls

* general improvements regarding functionality

- adds hotfixes and fast_ppu core options
- add back the alwaysDoubleSize setting
- use bsnes's own serialize function for savestating now
- generate and use the color palette only in c# cause it doesn't need to be on the c++-side
- and more cleanup like always

* somewhat implement IMemoryDomains

* Implement trace logger and cleanup more unused stuff

* Implement ISaveRam, fix controller mapping for TAStudio and remove the pwrap stuff

* Fix ISaveRam, add a controller configuration box for the new core, (hopefully) fix controller inputs

- also differentiates BG prio0 and prio1 now (with options for it)
- some minor irrelevant edits in bsnes source

* Cleanup some more and optimize a bit

* Support firmware loading and make sgb work (hopefully)

* Remove all unused files

* Add back CropSGBFrame option and cleanup snes_video_refresh logic

* Some hopefully sensible changes

* One more cleanup pass

* Change to new PortedCore attribute

necessary after the changes in 98b07c42d5

Co-authored-by: nattthebear <goyuken@gmail.com>
This commit is contained in:
Moritz Bender 2021-05-15 03:06:13 +02:00 committed by GitHub
parent 68ba190c2f
commit 5d20862f26
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
580 changed files with 86587 additions and 591 deletions

1
.gitignore vendored
View File

@ -2,6 +2,7 @@
/snes9xgit
**/.vs/**
**/.vscode/
**/bin/**
**/obj/**
/output/**

View File

@ -5,6 +5,7 @@ using System.ComponentModel;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES;
using BizHawk.Emulation.Cores.Consoles.Sega.gpgx;
using BizHawk.Emulation.Cores.Nintendo.BSNES;
using BizHawk.Emulation.Cores.Nintendo.NES;
using BizHawk.Emulation.Cores.Nintendo.SNES;
using BizHawk.Emulation.Cores.PCEngine;
@ -214,7 +215,7 @@ namespace BizHawk.Client.Common
public void SetRenderPlanes(params bool[] args)
{
static bool GetSetting(bool[] settings, int index) => index >= settings.Length || settings[index];
void SetBsnes(LibsnesCore core)
void SetLibsnes(LibsnesCore core)
{
var s = core.GetSettings();
s.ShowBG1_0 = s.ShowBG1_1 = GetSetting(args, 0);
@ -227,6 +228,20 @@ namespace BizHawk.Client.Common
s.ShowOBJ_3 = GetSetting(args, 7);
core.PutSettings(s);
}
void SetBsnes(BsnesCore core)
{
var s = core.GetSettings();
// TODO: This should probably support both prios inidividually but I have no idea whether changing this breaks anything
s.ShowBG1_0 = s.ShowBG1_1 = GetSetting(args, 0);
s.ShowBG2_0 = s.ShowBG2_1 = GetSetting(args, 1);
s.ShowBG3_0 = s.ShowBG3_1 = GetSetting(args, 2);
s.ShowBG4_0 = s.ShowBG4_1 = GetSetting(args, 3);
s.ShowOBJ_0 = GetSetting(args, 4);
s.ShowOBJ_1 = GetSetting(args, 5);
s.ShowOBJ_2 = GetSetting(args, 6);
s.ShowOBJ_3 = GetSetting(args, 7);
core.PutSettings(s);
}
void SetCygne(WonderSwan ws)
{
var s = ws.GetSettings();
@ -286,7 +301,10 @@ namespace BizHawk.Client.Common
SetGPGX(gpgx);
break;
case LibsnesCore snes:
SetBsnes(snes);
SetLibsnes(snes);
break;
case BsnesCore bsnes:
SetBsnes(bsnes);
break;
case NES nes:
SetNesHawk(nes);

View File

@ -22,9 +22,9 @@ namespace BizHawk.Client.Common
(new[] { "NES" },
new[] { CoreNames.QuickNes, CoreNames.NesHawk, CoreNames.SubNesHawk }),
(new[] { "SNES" },
new[] { CoreNames.Faust, CoreNames.Snes9X, CoreNames.Bsnes }),
new[] { CoreNames.Faust, CoreNames.Snes9X, CoreNames.Bsnes, CoreNames.Bsnes115 }),
(new[] { "SGB" },
new[] { CoreNames.SameBoy, CoreNames.Bsnes }),
new[] { CoreNames.SameBoy, CoreNames.Bsnes, CoreNames.Bsnes115}),
(new[] { "GB", "GBC" },
new[] { CoreNames.Gambatte, CoreNames.GbHawk, CoreNames.SubGbHawk }),
(new[] { "DGB" },
@ -208,7 +208,7 @@ namespace BizHawk.Client.Common
public int AlertMessageColor { get; set; } = DefaultMessagePositions.AlertMessageColor;
public int LastInputColor { get; set; } = DefaultMessagePositions.LastInputColor;
public int MovieInput { get; set; } = DefaultMessagePositions.MovieInput;
public int DispPrescale { get; set; } = 1;
private static bool DetectDirectX()
@ -347,4 +347,4 @@ namespace BizHawk.Client.Common
public bool UseStaticWindowTitles { get; set; }
}
}
}

View File

@ -22,6 +22,7 @@ using BizHawk.Emulation.Cores.Computers.SinclairSpectrum;
using BizHawk.Emulation.Cores.Consoles.Nintendo.NDS;
using BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES;
using BizHawk.Emulation.Cores.Intellivision;
using BizHawk.Emulation.Cores.Nintendo.BSNES;
using BizHawk.Emulation.Cores.Nintendo.Gameboy;
using BizHawk.Emulation.Cores.Nintendo.N64;
using BizHawk.Emulation.Cores.Nintendo.NES;
@ -1472,7 +1473,7 @@ namespace BizHawk.Client.EmuHawk
using var dlg = new NESSyncSettingsForm(this, sub.GetSyncSettings().Clone(), sub.HasMapperProperties);
dlg.ShowDialog(this);
}
}
private void BarcodeReaderMenuItem_Click(object sender, EventArgs e)
@ -1656,6 +1657,11 @@ namespace BizHawk.Client.EmuHawk
using var form = new SNESControllerSettings(this, bsnes.GetSyncSettings().Clone());
form.ShowDialog();
}
else if (Emulator is BsnesCore bsnesCore)
{
using var form = new BSNESControllerSettings(this, bsnesCore.GetSyncSettings().Clone());
form.ShowDialog();
}
}
private void SnesGfxDebuggerMenuItem_Click(object sender, EventArgs e)
@ -1665,9 +1671,13 @@ namespace BizHawk.Client.EmuHawk
private void SnesOptionsMenuItem_Click(object sender, EventArgs e)
{
if (Emulator is LibsnesCore bsnes)
if (Emulator is LibsnesCore libsnes)
{
SNESOptions.DoSettingsDialog(this, bsnes);
SNESOptions.DoSettingsDialog(this, libsnes);
}
if (Emulator is BsnesCore bsnes)
{
BSNESOptions.DoSettingsDialog(this, bsnes);
}
}
@ -2144,7 +2154,7 @@ namespace BizHawk.Client.EmuHawk
using var form = new AmstradCpcNonSyncSettings(this, cpc.GetSettings().Clone());
form.ShowDialog();
}
}
private void HelpSubMenu_DropDownOpened(object sender, EventArgs e)

View File

@ -22,6 +22,7 @@ using BizHawk.Bizware.BizwareGL;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores;
using BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES;
using BizHawk.Emulation.Cores.Nintendo.BSNES;
using BizHawk.Emulation.Cores.Nintendo.GBA;
using BizHawk.Emulation.Cores.Nintendo.NES;
using BizHawk.Emulation.Cores.Nintendo.SNES;
@ -138,7 +139,7 @@ namespace BizHawk.Client.EmuHawk
tuple.Item1 == requestedExtToolDll
|| Path.GetFileName(tuple.Item1) == requestedExtToolDll
|| Path.GetFileNameWithoutExtension(tuple.Item1) == requestedExtToolDll);
if(foundIndex != -1)
loaded = Tools.LoadExternalToolForm(enabled[foundIndex].Item1, enabled[foundIndex].Item2, skipExtToolWarning: true);
}
@ -672,7 +673,7 @@ namespace BizHawk.Client.EmuHawk
_needsFullscreenOnLoad = false;
ToggleFullscreen();
}
// Simply exit the program if the version is asked for
if (_argParser.printVersion)
{
@ -1425,53 +1426,80 @@ namespace BizHawk.Client.EmuHawk
private void SNES_ToggleBg(int layer)
{
if (!(Emulator is LibsnesCore || Emulator is Snes9x) || !1.RangeTo(4).Contains(layer))
if (Emulator is not (BsnesCore or LibsnesCore or Snes9x) || !1.RangeTo(4).Contains(layer))
{
return;
}
bool result = false;
if (Emulator is LibsnesCore bsnes)
switch (Emulator)
{
var s = bsnes.GetSettings();
switch (layer)
case BsnesCore bsnes:
{
case 1:
result = s.ShowBG1_0 = s.ShowBG1_1 ^= true;
break;
case 2:
result = s.ShowBG2_0 = s.ShowBG2_1 ^= true;
break;
case 3:
result = s.ShowBG3_0 = s.ShowBG3_1 ^= true;
break;
case 4:
result = s.ShowBG4_0 = s.ShowBG4_1 ^= true;
break;
}
var s = bsnes.GetSettings();
switch (layer)
{
case 1:
result = s.ShowBG1_0 = s.ShowBG1_1 ^= true;
break;
case 2:
result = s.ShowBG2_0 = s.ShowBG2_1 ^= true;
break;
case 3:
result = s.ShowBG3_0 = s.ShowBG3_1 ^= true;
break;
case 4:
result = s.ShowBG4_0 = s.ShowBG4_1 ^= true;
break;
}
bsnes.PutSettings(s);
}
else if (Emulator is Snes9x snes9X)
{
var s = snes9X.GetSettings();
switch (layer)
bsnes.PutSettings(s);
break;
}
case LibsnesCore libsnes:
{
case 1:
result = s.ShowBg0 ^= true;
break;
case 2:
result = s.ShowBg1 ^= true;
break;
case 3:
result = s.ShowBg2 ^= true;
break;
case 4:
result = s.ShowBg3 ^= true;
break;
}
var s = libsnes.GetSettings();
switch (layer)
{
case 1:
result = s.ShowBG1_0 = s.ShowBG1_1 ^= true;
break;
case 2:
result = s.ShowBG2_0 = s.ShowBG2_1 ^= true;
break;
case 3:
result = s.ShowBG3_0 = s.ShowBG3_1 ^= true;
break;
case 4:
result = s.ShowBG4_0 = s.ShowBG4_1 ^= true;
break;
}
snes9X.PutSettings(s);
libsnes.PutSettings(s);
break;
}
case Snes9x snes9X:
{
var s = snes9X.GetSettings();
switch (layer)
{
case 1:
result = s.ShowBg0 ^= true;
break;
case 2:
result = s.ShowBg1 ^= true;
break;
case 3:
result = s.ShowBg2 ^= true;
break;
case 4:
result = s.ShowBg3 ^= true;
break;
}
snes9X.PutSettings(s);
break;
}
}
AddOnScreenMessage($"BG {layer} Layer {(result ? "On" : "Off")}");
@ -1982,6 +2010,11 @@ namespace BizHawk.Client.EmuHawk
SNESSubMenu.Text = "&SNES";
SNESSubMenu.Visible = true;
break;
case "SNES" when Emulator is BsnesCore bsnesCore:
SNESSubMenu.Text = bsnesCore.IsSGB ? "&SGB" : "&SNES";
SNESSubMenu.DropDownItems[2].Visible = false;
SNESSubMenu.Visible = true;
break;
default:
DisplayDefaultCoreMenu();
break;
@ -2637,7 +2670,7 @@ namespace BizHawk.Client.EmuHawk
{
if (Config.ClockThrottle)
return true;
AddOnScreenMessage("Unable to change speed, please switch to clock throttle");
return false;
}

View File

@ -0,0 +1,184 @@
using System;
using BizHawk.Emulation.Cores.Nintendo.SNES;
namespace BizHawk.Client.EmuHawk
{
partial class BSNESControllerSettings
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.OkBtn = new System.Windows.Forms.Button();
this.CancelBtn = new System.Windows.Forms.Button();
this.Port2ComboBox = new System.Windows.Forms.ComboBox();
this.Port1ComboBox = new System.Windows.Forms.ComboBox();
this.LimitAnalogChangeCheckBox = new System.Windows.Forms.CheckBox();
this.MouseNagLabel1 = new BizHawk.WinForms.Controls.LocLabelEx();
this.MouseSpeedLabel1 = new BizHawk.WinForms.Controls.LocLabelEx();
this.label5 = new BizHawk.WinForms.Controls.LocLabelEx();
this.label4 = new BizHawk.WinForms.Controls.LocLabelEx();
this.label1 = new BizHawk.WinForms.Controls.LocLabelEx();
this.SuspendLayout();
//
// OkBtn
//
this.OkBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.OkBtn.Location = new System.Drawing.Point(170, 264);
this.OkBtn.Name = "OkBtn";
this.OkBtn.Size = new System.Drawing.Size(60, 23);
this.OkBtn.TabIndex = 4;
this.OkBtn.Text = "&OK";
this.OkBtn.UseVisualStyleBackColor = true;
this.OkBtn.Click += new System.EventHandler(this.OkBtn_Click);
//
// CancelBtn
//
this.CancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.CancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.CancelBtn.Location = new System.Drawing.Point(236, 264);
this.CancelBtn.Name = "CancelBtn";
this.CancelBtn.Size = new System.Drawing.Size(60, 23);
this.CancelBtn.TabIndex = 5;
this.CancelBtn.Text = "&Cancel";
this.CancelBtn.UseVisualStyleBackColor = true;
this.CancelBtn.Click += new System.EventHandler(this.CancelBtn_Click);
//
// Port2ComboBox
//
this.Port2ComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.Port2ComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.Port2ComboBox.FormattingEnabled = true;
this.Port2ComboBox.Items.AddRange(new object[] {
"None",
"Gamepad",
"Mouse"});
this.Port2ComboBox.Location = new System.Drawing.Point(12, 104);
this.Port2ComboBox.Name = "Port2ComboBox";
this.Port2ComboBox.Size = new System.Drawing.Size(284, 21);
this.Port2ComboBox.TabIndex = 20;
this.Port2ComboBox.SelectedIndexChanged += new System.EventHandler(this.PortComboBox_SelectedIndexChanged);
//
// Port1ComboBox
//
this.Port1ComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.Port1ComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.Port1ComboBox.FormattingEnabled = true;
this.Port1ComboBox.Items.AddRange(new object[] {
"None",
"Gamepad",
"Mouse"});
this.Port1ComboBox.Location = new System.Drawing.Point(12, 54);
this.Port1ComboBox.Name = "Port1ComboBox";
this.Port1ComboBox.Size = new System.Drawing.Size(284, 21);
this.Port1ComboBox.TabIndex = 19;
this.Port1ComboBox.SelectedIndexChanged += new System.EventHandler(this.PortComboBox_SelectedIndexChanged);
//
// LimitAnalogChangeCheckBox
//
this.LimitAnalogChangeCheckBox.AutoSize = true;
this.LimitAnalogChangeCheckBox.Location = new System.Drawing.Point(15, 175);
this.LimitAnalogChangeCheckBox.Name = "LimitAnalogChangeCheckBox";
this.LimitAnalogChangeCheckBox.Size = new System.Drawing.Size(173, 17);
this.LimitAnalogChangeCheckBox.TabIndex = 24;
this.LimitAnalogChangeCheckBox.Text = "Limit Analog Change Sensitivity";
this.LimitAnalogChangeCheckBox.UseVisualStyleBackColor = true;
//
// MouseNagLabel1
//
this.MouseNagLabel1.Location = new System.Drawing.Point(12, 135);
this.MouseNagLabel1.MaximumSize = new System.Drawing.Size(300, 0);
this.MouseNagLabel1.Name = "MouseNagLabel1";
this.MouseNagLabel1.Text = "*Note: mouse and scope controls should be bound to an analog stick, not the mouse" +
".";
//
// MouseSpeedLabel1
//
this.MouseSpeedLabel1.Location = new System.Drawing.Point(12, 195);
this.MouseSpeedLabel1.MaximumSize = new System.Drawing.Size(300, 0);
this.MouseSpeedLabel1.Name = "MouseSpeedLabel1";
this.MouseSpeedLabel1.Text = "For casual play this should be checked.\nThe full range of values are rather unusa" +
"ble in normal situations, but good if you need total control";
//
// label5
//
this.label5.Location = new System.Drawing.Point(12, 88);
this.label5.Name = "label5";
this.label5.Text = "Port 2:";
//
// label4
//
this.label4.Location = new System.Drawing.Point(12, 38);
this.label4.Name = "label4";
this.label4.Text = "Port 1:";
//
// label1
//
this.label1.Location = new System.Drawing.Point(12, 9);
this.label1.Name = "label1";
this.label1.Text = "SNES Controller Settings";
//
// BSNESControllerSettings
//
this.AcceptButton = this.OkBtn;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.CancelBtn;
this.ClientSize = new System.Drawing.Size(308, 299);
this.Controls.Add(this.MouseNagLabel1);
this.Controls.Add(this.LimitAnalogChangeCheckBox);
this.Controls.Add(this.MouseSpeedLabel1);
this.Controls.Add(this.label5);
this.Controls.Add(this.label4);
this.Controls.Add(this.Port2ComboBox);
this.Controls.Add(this.Port1ComboBox);
this.Controls.Add(this.label1);
this.Controls.Add(this.CancelBtn);
this.Controls.Add(this.OkBtn);
this.Name = "BSNESControllerSettings";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Controller Settings";
this.Load += new System.EventHandler(this.SNESControllerSettings_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button OkBtn;
private System.Windows.Forms.Button CancelBtn;
private BizHawk.WinForms.Controls.LocLabelEx label1;
private BizHawk.WinForms.Controls.LocLabelEx label5;
private BizHawk.WinForms.Controls.LocLabelEx label4;
private System.Windows.Forms.ComboBox Port2ComboBox;
private System.Windows.Forms.ComboBox Port1ComboBox;
private BizHawk.WinForms.Controls.LocLabelEx MouseSpeedLabel1;
private System.Windows.Forms.CheckBox LimitAnalogChangeCheckBox;
private BizHawk.WinForms.Controls.LocLabelEx MouseNagLabel1;
}
}

View File

@ -0,0 +1,73 @@
using System;
using System.Windows.Forms;
using BizHawk.Emulation.Cores.Nintendo.BSNES;
using BizHawk.Emulation.Cores.Nintendo.SNES;
namespace BizHawk.Client.EmuHawk
{
public partial class BSNESControllerSettings : Form
{
private readonly IMainFormForConfig _mainForm;
private readonly BsnesCore.SnesSyncSettings _syncSettings;
public BSNESControllerSettings(
IMainFormForConfig mainForm,
BsnesCore.SnesSyncSettings syncSettings)
{
_mainForm = mainForm;
_syncSettings = syncSettings;
InitializeComponent();
Icon = Properties.Resources.GameControllerIcon;
}
private void SNESControllerSettings_Load(object sender, EventArgs e)
{
LimitAnalogChangeCheckBox.Checked = _syncSettings.LimitAnalogChangeSensitivity;
Port1ComboBox.SelectedIndex = (int) _syncSettings.LeftPort >= Port1ComboBox.Items.Count ? 0 : (int) _syncSettings.LeftPort;
Port2ComboBox.PopulateFromEnum(_syncSettings.RightPort);
}
private void OkBtn_Click(object sender, EventArgs e)
{
bool changed =
_syncSettings.LeftPort != (BsnesApi.BSNES_INPUT_DEVICE) Port1ComboBox.SelectedIndex
|| _syncSettings.RightPort != (BsnesApi.BSNES_INPUT_DEVICE) Port2ComboBox.SelectedIndex
|| _syncSettings.LimitAnalogChangeSensitivity != LimitAnalogChangeCheckBox.Checked;
if (changed)
{
_syncSettings.LeftPort = (BsnesApi.BSNES_INPUT_DEVICE) Port1ComboBox.SelectedIndex;
_syncSettings.RightPort = (BsnesApi.BSNES_INPUT_DEVICE) Port2ComboBox.SelectedIndex;
_syncSettings.LimitAnalogChangeSensitivity = LimitAnalogChangeCheckBox.Checked;
_mainForm.PutCoreSyncSettings(_syncSettings);
}
DialogResult = DialogResult.OK;
Close();
}
private void CancelBtn_Click(object sender, EventArgs e)
{
_mainForm.AddOnScreenMessage("Controller settings aborted");
DialogResult = DialogResult.Cancel;
Close();
}
private void PortComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
var leftPort = (BsnesApi.BSNES_INPUT_DEVICE) Port1ComboBox.SelectedIndex;
var rightPort = (BsnesApi.BSNES_INPUT_DEVICE) Port2ComboBox.SelectedIndex;
ToggleMouseSection(leftPort == BsnesApi.BSNES_INPUT_DEVICE.Mouse || rightPort == BsnesApi.BSNES_INPUT_DEVICE.Mouse);
}
private void ToggleMouseSection(bool show)
{
LimitAnalogChangeCheckBox.Visible =
MouseSpeedLabel1.Visible =
MouseNagLabel1.Visible =
show;
}
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -0,0 +1,367 @@
namespace BizHawk.Client.EmuHawk
{
partial class BSNESOptions
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.btnOk = new System.Windows.Forms.Button();
this.btnCancel = new System.Windows.Forms.Button();
this.cbDoubleSize = new System.Windows.Forms.CheckBox();
this.lblDoubleSize = new BizHawk.WinForms.Controls.LocLabelEx();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.lblPriority1 = new BizHawk.WinForms.Controls.LocLabelEx();
this.lblPriority0 = new BizHawk.WinForms.Controls.LocLabelEx();
this.Bg4_0Checkbox = new System.Windows.Forms.CheckBox();
this.Bg3_0Checkbox = new System.Windows.Forms.CheckBox();
this.Bg2_0Checkbox = new System.Windows.Forms.CheckBox();
this.Bg1_0Checkbox = new System.Windows.Forms.CheckBox();
this.Bg4_1Checkbox = new System.Windows.Forms.CheckBox();
this.Bg3_1Checkbox = new System.Windows.Forms.CheckBox();
this.Bg2_1Checkbox = new System.Windows.Forms.CheckBox();
this.Bg1_1Checkbox = new System.Windows.Forms.CheckBox();
this.Obj4Checkbox = new System.Windows.Forms.CheckBox();
this.Obj3Checkbox = new System.Windows.Forms.CheckBox();
this.Obj2Checkbox = new System.Windows.Forms.CheckBox();
this.Obj1Checkbox = new System.Windows.Forms.CheckBox();
this.EntropyBox = new System.Windows.Forms.ComboBox();
this.lblEntropy = new BizHawk.WinForms.Controls.LocLabelEx();
this.cbGameHotfixes = new System.Windows.Forms.CheckBox();
this.cbFastPPU = new System.Windows.Forms.CheckBox();
this.cbCropSGBFrame = new System.Windows.Forms.CheckBox();
this.groupBox1.SuspendLayout();
this.SuspendLayout();
//
// btnOk
//
this.btnOk.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnOk.Location = new System.Drawing.Point(136, 303);
this.btnOk.Name = "btnOk";
this.btnOk.Size = new System.Drawing.Size(75, 23);
this.btnOk.TabIndex = 0;
this.btnOk.Text = "OK";
this.btnOk.UseVisualStyleBackColor = true;
this.btnOk.Click += new System.EventHandler(this.BtnOk_Click);
//
// btnCancel
//
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.btnCancel.Location = new System.Drawing.Point(217, 303);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Size = new System.Drawing.Size(75, 23);
this.btnCancel.TabIndex = 1;
this.btnCancel.Text = "Cancel";
this.btnCancel.UseVisualStyleBackColor = true;
this.btnCancel.Click += new System.EventHandler(this.BtnCancel_Click);
//
// cbDoubleSize
//
this.cbDoubleSize.AutoSize = true;
this.cbDoubleSize.Location = new System.Drawing.Point(18, 16);
this.cbDoubleSize.Name = "cbDoubleSize";
this.cbDoubleSize.Size = new System.Drawing.Size(178, 17);
this.cbDoubleSize.TabIndex = 6;
this.cbDoubleSize.Text = "Always Double-Size Framebuffer";
this.cbDoubleSize.UseVisualStyleBackColor = true;
//
// lblDoubleSize
//
this.lblDoubleSize.Location = new System.Drawing.Point(33, 34);
this.lblDoubleSize.MaximumSize = new System.Drawing.Size(260, 0);
this.lblDoubleSize.Name = "lblDoubleSize";
this.lblDoubleSize.Text = "Some games are changing the resolution constantly (e.g. SD3) so this option can f" +
"orce the SNES output to stay double-size always.";
//
// groupBox1
//
this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.groupBox1.Controls.Add(this.lblPriority1);
this.groupBox1.Controls.Add(this.lblPriority0);
this.groupBox1.Controls.Add(this.Bg4_0Checkbox);
this.groupBox1.Controls.Add(this.Bg3_0Checkbox);
this.groupBox1.Controls.Add(this.Bg2_0Checkbox);
this.groupBox1.Controls.Add(this.Bg1_0Checkbox);
this.groupBox1.Controls.Add(this.Bg4_1Checkbox);
this.groupBox1.Controls.Add(this.Bg3_1Checkbox);
this.groupBox1.Controls.Add(this.Bg2_1Checkbox);
this.groupBox1.Controls.Add(this.Bg1_1Checkbox);
this.groupBox1.Controls.Add(this.Obj4Checkbox);
this.groupBox1.Controls.Add(this.Obj3Checkbox);
this.groupBox1.Controls.Add(this.Obj2Checkbox);
this.groupBox1.Controls.Add(this.Obj1Checkbox);
this.groupBox1.Location = new System.Drawing.Point(18, 165);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(274, 132);
this.groupBox1.TabIndex = 11;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "Display";
//
// lblPriority1
//
this.lblPriority1.Location = new System.Drawing.Point(220, 14);
this.lblPriority1.MaximumSize = new System.Drawing.Size(100, 0);
this.lblPriority1.Name = "lblPriority1";
this.lblPriority1.Text = "Priority 1";
//
// lblPriority0
//
this.lblPriority0.Location = new System.Drawing.Point(162, 14);
this.lblPriority0.MaximumSize = new System.Drawing.Size(100, 0);
this.lblPriority0.Name = "lblPriority0";
this.lblPriority0.Text = "Priority 0";
this.lblPriority0.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// Bg4_0Checkbox
//
this.Bg4_0Checkbox.AutoSize = true;
this.Bg4_0Checkbox.CheckAlign = System.Drawing.ContentAlignment.MiddleRight;
this.Bg4_0Checkbox.Location = new System.Drawing.Point(128, 99);
this.Bg4_0Checkbox.Name = "Bg4_0Checkbox";
this.Bg4_0Checkbox.Size = new System.Drawing.Size(62, 17);
this.Bg4_0Checkbox.TabIndex = 11;
this.Bg4_0Checkbox.Text = "BG 4 ";
this.Bg4_0Checkbox.UseVisualStyleBackColor = true;
//
// Bg3_0Checkbox
//
this.Bg3_0Checkbox.AutoSize = true;
this.Bg3_0Checkbox.CheckAlign = System.Drawing.ContentAlignment.MiddleRight;
this.Bg3_0Checkbox.Location = new System.Drawing.Point(128, 76);
this.Bg3_0Checkbox.Name = "Bg3_0Checkbox";
this.Bg3_0Checkbox.Size = new System.Drawing.Size(62, 17);
this.Bg3_0Checkbox.TabIndex = 10;
this.Bg3_0Checkbox.Text = "BG 3 ";
this.Bg3_0Checkbox.UseVisualStyleBackColor = true;
//
// Bg2_0Checkbox
//
this.Bg2_0Checkbox.AutoSize = true;
this.Bg2_0Checkbox.CheckAlign = System.Drawing.ContentAlignment.MiddleRight;
this.Bg2_0Checkbox.Location = new System.Drawing.Point(128, 53);
this.Bg2_0Checkbox.Name = "Bg2_0Checkbox";
this.Bg2_0Checkbox.Size = new System.Drawing.Size(62, 17);
this.Bg2_0Checkbox.TabIndex = 9;
this.Bg2_0Checkbox.Text = "BG 2 ";
this.Bg2_0Checkbox.UseVisualStyleBackColor = true;
//
// Bg1_0Checkbox
//
this.Bg1_0Checkbox.AutoSize = true;
this.Bg1_0Checkbox.CheckAlign = System.Drawing.ContentAlignment.MiddleRight;
this.Bg1_0Checkbox.Location = new System.Drawing.Point(128, 30);
this.Bg1_0Checkbox.Name = "Bg1_0Checkbox";
this.Bg1_0Checkbox.Size = new System.Drawing.Size(62, 17);
this.Bg1_0Checkbox.TabIndex = 8;
this.Bg1_0Checkbox.Text = "BG 1 ";
this.Bg1_0Checkbox.UseVisualStyleBackColor = true;
//
// Bg4_1Checkbox
//
this.Bg4_1Checkbox.AutoSize = true;
this.Bg4_1Checkbox.Location = new System.Drawing.Point(234, 100);
this.Bg4_1Checkbox.Name = "Bg4_1Checkbox";
this.Bg4_1Checkbox.Size = new System.Drawing.Size(15, 14);
this.Bg4_1Checkbox.TabIndex = 7;
this.Bg4_1Checkbox.UseVisualStyleBackColor = true;
//
// Bg3_1Checkbox
//
this.Bg3_1Checkbox.AutoSize = true;
this.Bg3_1Checkbox.Location = new System.Drawing.Point(234, 77);
this.Bg3_1Checkbox.Name = "Bg3_1Checkbox";
this.Bg3_1Checkbox.Size = new System.Drawing.Size(15, 14);
this.Bg3_1Checkbox.TabIndex = 6;
this.Bg3_1Checkbox.UseVisualStyleBackColor = true;
//
// Bg2_1Checkbox
//
this.Bg2_1Checkbox.AutoSize = true;
this.Bg2_1Checkbox.Location = new System.Drawing.Point(234, 54);
this.Bg2_1Checkbox.Name = "Bg2_1Checkbox";
this.Bg2_1Checkbox.Size = new System.Drawing.Size(15, 14);
this.Bg2_1Checkbox.TabIndex = 5;
this.Bg2_1Checkbox.UseVisualStyleBackColor = true;
//
// Bg1_1Checkbox
//
this.Bg1_1Checkbox.AutoSize = true;
this.Bg1_1Checkbox.Location = new System.Drawing.Point(234, 31);
this.Bg1_1Checkbox.Name = "Bg1_1Checkbox";
this.Bg1_1Checkbox.Size = new System.Drawing.Size(15, 14);
this.Bg1_1Checkbox.TabIndex = 4;
this.Bg1_1Checkbox.UseVisualStyleBackColor = true;
//
// Obj4Checkbox
//
this.Obj4Checkbox.AutoSize = true;
this.Obj4Checkbox.Location = new System.Drawing.Point(21, 99);
this.Obj4Checkbox.Name = "Obj4Checkbox";
this.Obj4Checkbox.Size = new System.Drawing.Size(55, 17);
this.Obj4Checkbox.TabIndex = 3;
this.Obj4Checkbox.Text = "OBJ 4";
this.Obj4Checkbox.UseVisualStyleBackColor = true;
//
// Obj3Checkbox
//
this.Obj3Checkbox.AutoSize = true;
this.Obj3Checkbox.Location = new System.Drawing.Point(21, 76);
this.Obj3Checkbox.Name = "Obj3Checkbox";
this.Obj3Checkbox.Size = new System.Drawing.Size(55, 17);
this.Obj3Checkbox.TabIndex = 2;
this.Obj3Checkbox.Text = "OBJ 3";
this.Obj3Checkbox.UseVisualStyleBackColor = true;
//
// Obj2Checkbox
//
this.Obj2Checkbox.AutoSize = true;
this.Obj2Checkbox.Location = new System.Drawing.Point(21, 53);
this.Obj2Checkbox.Name = "Obj2Checkbox";
this.Obj2Checkbox.Size = new System.Drawing.Size(55, 17);
this.Obj2Checkbox.TabIndex = 1;
this.Obj2Checkbox.Text = "OBJ 2";
this.Obj2Checkbox.UseVisualStyleBackColor = true;
//
// Obj1Checkbox
//
this.Obj1Checkbox.AutoSize = true;
this.Obj1Checkbox.Location = new System.Drawing.Point(21, 30);
this.Obj1Checkbox.Name = "Obj1Checkbox";
this.Obj1Checkbox.Size = new System.Drawing.Size(55, 17);
this.Obj1Checkbox.TabIndex = 0;
this.Obj1Checkbox.Text = "OBJ 1";
this.Obj1Checkbox.UseVisualStyleBackColor = true;
//
// EntropyBox
//
this.EntropyBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.EntropyBox.FormattingEnabled = true;
this.EntropyBox.Items.AddRange(new object[] {
"None",
"Low",
"High"});
this.EntropyBox.Location = new System.Drawing.Point(164, 138);
this.EntropyBox.Name = "EntropyBox";
this.EntropyBox.Size = new System.Drawing.Size(128, 21);
this.EntropyBox.TabIndex = 14;
//
// lblEntropy
//
this.lblEntropy.Location = new System.Drawing.Point(249, 117);
this.lblEntropy.Name = "lblEntropy";
this.lblEntropy.Text = "Entropy";
//
// cbGameHotfixes
//
this.cbGameHotfixes.AutoSize = true;
this.cbGameHotfixes.Location = new System.Drawing.Point(18, 111);
this.cbGameHotfixes.Name = "cbGameHotfixes";
this.cbGameHotfixes.Size = new System.Drawing.Size(93, 17);
this.cbGameHotfixes.TabIndex = 22;
this.cbGameHotfixes.Text = "Game hotfixes";
this.cbGameHotfixes.UseVisualStyleBackColor = true;
//
// cbFastPPU
//
this.cbFastPPU.AutoSize = true;
this.cbFastPPU.Location = new System.Drawing.Point(18, 138);
this.cbFastPPU.Name = "cbFastPPU";
this.cbFastPPU.Size = new System.Drawing.Size(90, 17);
this.cbFastPPU.TabIndex = 23;
this.cbFastPPU.Text = "Use fast PPU";
this.cbFastPPU.UseVisualStyleBackColor = true;
this.cbFastPPU.CheckedChanged += new System.EventHandler(this.FastPPU_CheckedChanged);
//
// cbCropSGBFrame
//
this.cbCropSGBFrame.AutoSize = true;
this.cbCropSGBFrame.Location = new System.Drawing.Point(18, 84);
this.cbCropSGBFrame.Name = "cbCropSGBFrame";
this.cbCropSGBFrame.Size = new System.Drawing.Size(105, 17);
this.cbCropSGBFrame.TabIndex = 27;
this.cbCropSGBFrame.Text = "Crop SGB Frame";
this.cbCropSGBFrame.UseVisualStyleBackColor = true;
//
// BSNESOptions
//
this.AcceptButton = this.btnOk;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.btnCancel;
this.ClientSize = new System.Drawing.Size(304, 338);
this.Controls.Add(this.cbCropSGBFrame);
this.Controls.Add(this.cbFastPPU);
this.Controls.Add(this.cbGameHotfixes);
this.Controls.Add(this.lblEntropy);
this.Controls.Add(this.EntropyBox);
this.Controls.Add(this.groupBox1);
this.Controls.Add(this.lblDoubleSize);
this.Controls.Add(this.cbDoubleSize);
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.btnOk);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "BSNESOptions";
this.ShowIcon = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "BSNES Options";
this.groupBox1.ResumeLayout(false);
this.groupBox1.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button btnOk;
private System.Windows.Forms.Button btnCancel;
private System.Windows.Forms.CheckBox cbDoubleSize;
private BizHawk.WinForms.Controls.LocLabelEx lblDoubleSize;
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.CheckBox Bg4_1Checkbox;
private System.Windows.Forms.CheckBox Bg3_1Checkbox;
private System.Windows.Forms.CheckBox Bg2_1Checkbox;
private System.Windows.Forms.CheckBox Bg1_1Checkbox;
private System.Windows.Forms.CheckBox Obj4Checkbox;
private System.Windows.Forms.CheckBox Obj3Checkbox;
private System.Windows.Forms.CheckBox Obj2Checkbox;
private System.Windows.Forms.CheckBox Obj1Checkbox;
private System.Windows.Forms.ComboBox EntropyBox;
private WinForms.Controls.LocLabelEx lblEntropy;
private System.Windows.Forms.CheckBox cbGameHotfixes;
private System.Windows.Forms.CheckBox cbFastPPU;
private System.Windows.Forms.CheckBox Bg1_0Checkbox;
private System.Windows.Forms.CheckBox Bg4_0Checkbox;
private System.Windows.Forms.CheckBox Bg3_0Checkbox;
private System.Windows.Forms.CheckBox Bg2_0Checkbox;
private WinForms.Controls.LocLabelEx lblPriority1;
private WinForms.Controls.LocLabelEx lblPriority0;
private System.Windows.Forms.CheckBox cbCropSGBFrame;
}
}

View File

@ -0,0 +1,126 @@
using System;
using System.Windows.Forms;
using BizHawk.Emulation.Cores.Nintendo.BSNES;
namespace BizHawk.Client.EmuHawk
{
public partial class BSNESOptions : Form
{
private BSNESOptions()
{
InitializeComponent();
}
public static void DoSettingsDialog(IMainFormForConfig mainForm, BsnesCore bsnes)
{
var s = bsnes.GetSettings();
var ss = bsnes.GetSyncSettings();
using var dlg = new BSNESOptions
{
AlwaysDoubleSize = s.AlwaysDoubleSize,
CropSGBFrame = s.CropSGBFrame,
Entropy = ss.Entropy,
Hotfixes = ss.Hotfixes,
FastPPU = ss.FastPPU,
ShowObj1 = s.ShowOBJ_0,
ShowObj2 = s.ShowOBJ_1,
ShowObj3 = s.ShowOBJ_2,
ShowObj4 = s.ShowOBJ_3,
ShowBg1_0 = s.ShowBG1_0,
ShowBg1_1 = s.ShowBG1_1,
ShowBg2_0 = s.ShowBG2_0,
ShowBg2_1 = s.ShowBG2_1,
ShowBg3_0 = s.ShowBG3_0,
ShowBg3_1 = s.ShowBG3_1,
ShowBg4_0 = s.ShowBG4_0,
ShowBg4_1 = s.ShowBG4_1
};
DialogResult result = mainForm.ShowDialogAsChild(dlg);
if (result == DialogResult.OK)
{
s.AlwaysDoubleSize = dlg.AlwaysDoubleSize;
s.CropSGBFrame = dlg.CropSGBFrame;
ss.Entropy = dlg.Entropy;
ss.Hotfixes = dlg.Hotfixes;
ss.FastPPU = dlg.FastPPU;
s.ShowOBJ_0 = dlg.ShowObj1;
s.ShowOBJ_1 = dlg.ShowObj2;
s.ShowOBJ_2 = dlg.ShowObj3;
s.ShowOBJ_3 = dlg.ShowObj4;
s.ShowBG1_0 = dlg.ShowBg1_0;
s.ShowBG1_1 = dlg.ShowBg1_1;
s.ShowBG2_0 = dlg.ShowBg2_0;
s.ShowBG2_1 = dlg.ShowBg2_1;
s.ShowBG3_0 = dlg.ShowBg3_0;
s.ShowBG3_1 = dlg.ShowBg3_1;
s.ShowBG4_0 = dlg.ShowBg4_0;
s.ShowBG4_1 = dlg.ShowBg4_1;
mainForm.PutCoreSettings(s);
mainForm.PutCoreSyncSettings(ss);
}
}
private bool AlwaysDoubleSize
{
get => cbDoubleSize.Checked;
init => cbDoubleSize.Checked = value;
}
private bool CropSGBFrame
{
get => cbCropSGBFrame.Checked;
init => cbCropSGBFrame.Checked = value;
}
private bool Hotfixes
{
get => cbGameHotfixes.Checked;
init => cbGameHotfixes.Checked = value;
}
private bool FastPPU
{
get => cbFastPPU.Checked;
init => cbDoubleSize.Enabled = cbFastPPU.Checked = value;
}
private BsnesApi.ENTROPY Entropy
{
get => (BsnesApi.ENTROPY) EntropyBox.SelectedIndex;
init => EntropyBox.SelectedIndex = (int) value;
}
private bool ShowObj1 { get => Obj1Checkbox.Checked; init => Obj1Checkbox.Checked = value; }
private bool ShowObj2 { get => Obj2Checkbox.Checked; init => Obj2Checkbox.Checked = value; }
private bool ShowObj3 { get => Obj3Checkbox.Checked; init => Obj3Checkbox.Checked = value; }
private bool ShowObj4 { get => Obj4Checkbox.Checked; init => Obj4Checkbox.Checked = value; }
private bool ShowBg1_0 { get => Bg1_0Checkbox.Checked; init => Bg1_0Checkbox.Checked = value; }
private bool ShowBg1_1 { get => Bg1_1Checkbox.Checked; init => Bg1_1Checkbox.Checked = value; }
private bool ShowBg2_0 { get => Bg2_0Checkbox.Checked; init => Bg2_0Checkbox.Checked = value; }
private bool ShowBg2_1 { get => Bg2_1Checkbox.Checked; init => Bg2_1Checkbox.Checked = value; }
private bool ShowBg3_0 { get => Bg3_0Checkbox.Checked; init => Bg3_0Checkbox.Checked = value; }
private bool ShowBg3_1 { get => Bg3_1Checkbox.Checked; init => Bg3_1Checkbox.Checked = value; }
private bool ShowBg4_0 { get => Bg4_0Checkbox.Checked; init => Bg4_0Checkbox.Checked = value; }
private bool ShowBg4_1 { get => Bg4_1Checkbox.Checked; init => Bg4_1Checkbox.Checked = value; }
private void BtnOk_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.OK;
Close();
}
private void BtnCancel_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.Cancel;
Close();
}
private void FastPPU_CheckedChanged(object sender, EventArgs e)
{
cbDoubleSize.Enabled = cbFastPPU.Checked;
}
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -24,7 +24,7 @@
ITraceSink Sink { set; }
/// <summary>
/// Gets a value indicating whether racing is enabled
/// Gets a value indicating whether tracing is enabled
/// This is defined as equivalent to Sink != null
/// It's put here because it's such a common operation to check whether it's enabled, and it's not nice to write Sink != null all over
/// </summary>

View File

@ -0,0 +1,206 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
using BizHawk.Common;
using BizHawk.Emulation.Cores.Waterbox;
using BizHawk.BizInvoke;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.BSNES
{
public abstract unsafe class BsnesCoreImpl
{
[BizImport(CallingConvention.Cdecl)]
public abstract void snes_set_audio_enabled(bool enabled);
[BizImport(CallingConvention.Cdecl)]
public abstract void snes_set_video_enabled(bool enabled);
[BizImport(CallingConvention.Cdecl)]
public abstract void snes_set_layer_enables(BsnesApi.LayerEnables layerEnables);
[BizImport(CallingConvention.Cdecl)]
public abstract void snes_set_trace_enabled(bool enabled);
[BizImport(CallingConvention.Cdecl)]
public abstract BsnesApi.SNES_REGION snes_get_region();
[BizImport(CallingConvention.Cdecl)]
public abstract BsnesApi.SNES_MAPPER snes_get_mapper();
[BizImport(CallingConvention.Cdecl)]
public abstract void* snes_get_memory_region(int id, out int size, out int wordSize);
[BizImport(CallingConvention.Cdecl)]
public abstract byte snes_bus_read(uint address);
[BizImport(CallingConvention.Cdecl)]
public abstract void snes_bus_write(uint address, byte value);
[BizImport(CallingConvention.Cdecl)]
public abstract void snes_set_callbacks(IntPtr[] snesCallbacks);
[BizImport(CallingConvention.Cdecl)]
public abstract void snes_init(BsnesApi.ENTROPY entropy, BsnesApi.BSNES_INPUT_DEVICE left,
BsnesApi.BSNES_INPUT_DEVICE right, ushort mergedBools);// bool hotfixes, bool fastPPU);
[BizImport(CallingConvention.Cdecl)]
public abstract void snes_power();
[BizImport(CallingConvention.Cdecl)]
public abstract void snes_term();
[BizImport(CallingConvention.Cdecl)]
public abstract void snes_reset();
[BizImport(CallingConvention.Cdecl)]
public abstract void snes_run();
[BizImport(CallingConvention.Cdecl)]
public abstract void snes_serialize(byte[] serializedData, int serializedSize);
[BizImport(CallingConvention.Cdecl)]
public abstract void snes_unserialize(byte[] serializedData, int serializedSize);
[BizImport(CallingConvention.Cdecl)]
public abstract int snes_serialized_size();
[BizImport(CallingConvention.Cdecl)]
public abstract void snes_load_cartridge_normal(string baseRomPath, byte[] romData, int romSize);
[BizImport(CallingConvention.Cdecl)]
public abstract void snes_load_cartridge_super_gameboy(string baseRomPath, byte[] romData, byte[] sgbRomData, ulong mergedRomSizes);
}
public unsafe partial class BsnesApi : IDisposable, IMonitor, IStatable
{
internal WaterboxHost exe;
internal BsnesCoreImpl core;
private readonly ICallingConventionAdapter _adapter;
private bool _disposed;
public void Enter()
{
exe.Enter();
}
public void Exit()
{
exe.Exit();
}
private readonly List<string> _readonlyFiles = new();
public void AddReadonlyFile(byte[] data, string name)
{
// current logic potentially requests the same name twice; once for program and once for data
// because this gets mapped to the same file, we only add it once
if (!_readonlyFiles.Contains(name))
{
exe.AddReadonlyFile(data, name);
_readonlyFiles.Add(name);
}
}
public void SetCallbacks(SnesCallbacks callbacks)
{
FieldInfo[] fieldInfos = typeof(SnesCallbacks).GetFields();
IntPtr[] functionPointerArray = new IntPtr[fieldInfos.Length];
for (int i = 0; i < fieldInfos.Length; i++)
{
functionPointerArray[i] = _adapter.GetFunctionPointerForDelegate((Delegate) fieldInfos[i].GetValue(callbacks));
}
core.snes_set_callbacks(functionPointerArray);
}
public BsnesApi(string dllPath, CoreComm comm, IEnumerable<Delegate> allCallbacks)
{
exe = new WaterboxHost(new WaterboxOptions
{
Filename = "bsnes.wbx",
Path = dllPath,
SbrkHeapSizeKB = 14 * 1024,
InvisibleHeapSizeKB = 4,
MmapHeapSizeKB = 105 * 1024, // TODO: check whether this needs to be larger; it depends on the rom size
PlainHeapSizeKB = 0,
SealedHeapSizeKB = 0,
SkipCoreConsistencyCheck = comm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxCoreConsistencyCheck),
SkipMemoryConsistencyCheck = comm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxMemoryConsistencyCheck),
});
using (exe.EnterExit())
{
// Marshal checks that function pointers passed to GetDelegateForFunctionPointer are
// _currently_ valid when created, even though they don't need to be valid until
// the delegate is later invoked. so GetInvoker needs to be acquired within a lock.
_adapter = CallingConventionAdapters.MakeWaterbox(allCallbacks, exe);
this.core = BizInvoker.GetInvoker<BsnesCoreImpl>(exe, exe, _adapter);
}
}
public void Dispose()
{
if (!_disposed)
{
_disposed = true;
exe.Dispose();
exe = null;
core = null;
// serializedSize = 0;
}
}
public delegate void snes_video_frame_t(ushort* data, int width, int height, int pitch);
public delegate void snes_input_poll_t();
public delegate short snes_input_state_t(int port, int index, int id);
public delegate void snes_no_lag_t();
public delegate void snes_audio_sample_t(short left, short right);
public delegate string snes_path_request_t(int slot, string hint, bool required);
public delegate void snes_trace_t(string disassembly, string register_info);
// I cannot use a struct here because marshalling is retarded for bool (4 bytes). I honestly cannot
[StructLayout(LayoutKind.Sequential)]
public class LayerEnables
{
public bool BG1_Prio0, BG1_Prio1;
public bool BG2_Prio0, BG2_Prio1;
public bool BG3_Prio0, BG3_Prio1;
public bool BG4_Prio0, BG4_Prio1;
public bool Obj_Prio0, Obj_Prio1, Obj_Prio2, Obj_Prio3;
}
[StructLayout(LayoutKind.Sequential)]
public class SnesCallbacks
{
public snes_input_poll_t inputPollCb;
public snes_input_state_t inputStateCb;
public snes_no_lag_t noLagCb;
public snes_video_frame_t videoFrameCb;
public snes_audio_sample_t audioSampleCb;
public snes_path_request_t pathRequestCb;
public snes_trace_t snesTraceCb;
}
public void Seal()
{
exe.Seal();
foreach (var s in _readonlyFiles)
{
exe.RemoveReadonlyFile(s);
}
_readonlyFiles.Clear();
}
// TODO: confirm that the serializedSize is CONSTANT for any given game,
// else this might be problematic
// private int serializedSize;// = 284275;
public void SaveStateBinary(BinaryWriter writer)
{
// if (serializedSize == 0)
// serializedSize = _core.snes_serialized_size();
// TODO: do some profiling and testing to check whether this is actually better than _exe.SaveStateBinary(writer);
// re-adding bsnes's own serialization will need to be done once it's confirmed to be deterministic, aka after libco update
// byte[] serializedData = new byte[serializedSize];
// _core.snes_serialize(serializedData, serializedSize);
// writer.Write(serializedData);
exe.SaveStateBinary(writer);
}
public void LoadStateBinary(BinaryReader reader)
{
// byte[] serializedData = reader.ReadBytes(serializedSize);
// _core.snes_unserialize(serializedData, serializedSize);
exe.LoadStateBinary(reader);
}
}
}

View File

@ -0,0 +1,61 @@
namespace BizHawk.Emulation.Cores.Nintendo.BSNES
{
public partial class BsnesApi
{
public enum SNES_MEMORY
{
CARTRIDGE_RAM,
BSX_RAM,
BSX_PRAM,
SUFAMI_TURBO_A_RAM,
SUFAMI_TURBO_B_RAM,
WRAM,
APURAM,
VRAM,
// OAM, // needs some work in the core probably? or we return an objects pointer
CGRAM,
CARTRIDGE_ROM
}
public enum BSNES_INPUT_DEVICE
{
None,
Gamepad,
Mouse,
SuperMultitap,
SuperScope,
Justifier,
Justifiers
}
public enum ENTROPY
{
None,
Low,
High
}
public enum SNES_MAPPER : byte
{
LOROM = 0,
HIROM = 1,
EXLOROM = 2,
EXHIROM = 3,
SUPERFXROM = 4,
SA1ROM = 5,
SPC7110ROM = 6,
BSCLOROM = 7,
BSCHIROM = 8,
BSXROM = 9,
STROM = 10
}
public enum SNES_REGION : uint
{
NTSC = 0,
PAL = 1
}
}
}

View File

@ -0,0 +1,309 @@
using System;
using System.Collections.Generic;
using System.Linq;
using BizHawk.Common;
using BizHawk.Common.NumberExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Nintendo.SNES;
using static BizHawk.Emulation.Cores.Nintendo.BSNES.BsnesApi;
namespace BizHawk.Emulation.Cores.Nintendo.BSNES
{
public class BsnesControllers
{
private static IBsnesController GetController(BSNES_INPUT_DEVICE t, BsnesCore.SnesSyncSettings ss)
{
return t switch
{
BSNES_INPUT_DEVICE.None => new BsnesUnpluggedController(),
BSNES_INPUT_DEVICE.Gamepad => new BsnesController(),
BSNES_INPUT_DEVICE.Mouse => new BsnesMouseController
{
LimitAnalogChangeSensitivity = ss.LimitAnalogChangeSensitivity
},
BSNES_INPUT_DEVICE.SuperMultitap => new BsnesMultitapController(),
BSNES_INPUT_DEVICE.SuperScope => new BsnesSuperScopeController(),
BSNES_INPUT_DEVICE.Justifier => new BsnesJustifierController(false),
BSNES_INPUT_DEVICE.Justifiers => new BsnesJustifierController(true),
_ => throw new InvalidOperationException()
};
}
private readonly IBsnesController[] _ports;
private readonly ControlDefUnMerger[] _mergers;
public ControllerDefinition Definition { get; }
public BsnesControllers(BsnesCore.SnesSyncSettings ss)
{
_ports = new[]
{
GetController(ss.LeftPort, ss),
GetController(ss.RightPort, ss)
};
Definition = ControllerDefinitionMerger.GetMerged(_ports.Select(p => p.Definition), out var tmp);
_mergers = tmp.ToArray();
// add buttons that the core itself will handle
Definition.BoolButtons.Add("Reset");
Definition.BoolButtons.Add("Power");
Definition.Name = "SNES Controller";
}
public void CoreInputPoll(IController controller)
{
// i hope this is correct lol
for (int i = 0; i < 2; i++)
{
_ports[i].UpdateState(_mergers[i].UnMerge(controller));
}
}
public short CoreInputState(int port, int index, int id)
{
return _ports[port].GetState(index, id);
}
}
public interface IBsnesController
{
// Updates the internal state; gets called once per frame from the core
void UpdateState(IController controller);
/// <summary>
/// Returns the internal state; gets called potentially many times per frame
/// </summary>
/// <param name="index">bsnes specific value, sometimes multitap number</param>
/// <param name="id">bsnes specific value, sometimes button number</param>
short GetState(int index, int id);
ControllerDefinition Definition { get; }
}
internal class BsnesController : IBsnesController
{
private readonly bool[] _state = new bool[12];
private static readonly string[] Buttons =
{
"0Up", "0Down", "0Left", "0Right", "0B", "0A", "0Y", "0X", "0L", "0R", "0Select", "0Start"
};
private static readonly Dictionary<string, int> ButtonsOrder = new()
{
["0Up"] = 0,
["0Down"] = 1,
["0Left"] = 2,
["0Right"] = 3,
["0Select"] = 4,
["0Start"] = 5,
["0Y"] = 6,
["0B"] = 7,
["0X"] = 8,
["0A"] = 9,
["0L"] = 10,
["0R"] = 11
};
private static readonly ControllerDefinition _definition = new()
{
BoolButtons = Buttons.OrderBy(b => ButtonsOrder[b]).ToList()
};
public ControllerDefinition Definition => _definition;
public void UpdateState(IController controller)
{
for (int i = 0; i < 12; i++)
{
_state[i] = controller.IsPressed(Buttons[i]);
}
}
public short GetState(int index, int id)
{
if (id >= 12)
return 0;
return (short) (_state[id] ? 1 : 0);
}
}
public class BsnesMultitapController : IBsnesController
{
private readonly bool[,] _state = new bool[4, 12];
private static readonly string[] Buttons =
{
"Up", "Down", "Left", "Right", "B", "A", "Y", "X", "L", "R", "Select", "Start"
};
private static readonly Dictionary<string, int> ButtonsOrder = new()
{
["Up"] = 0,
["Down"] = 1,
["Left"] = 2,
["Right"] = 3,
["Select"] = 4,
["Start"] = 5,
["Y"] = 6,
["B"] = 7,
["X"] = 8,
["A"] = 9,
["R"] = 10,
["L"] = 11
};
private static readonly ControllerDefinition _definition = new()
{
BoolButtons = Enumerable.Range(0, 4)
.SelectMany(i => Buttons.OrderBy(b => ButtonsOrder[b])
.Select(b => i + b))
.ToList()
};
public ControllerDefinition Definition => _definition;
public void UpdateState(IController controller)
{
for (int port = 0; port < 4; port++)
for (int i = 0; i < 12; i++)
{
_state[port, i] = controller.IsPressed(port + Buttons[i]);
}
}
public short GetState(int index, int id)
{
if (id >= 12 || index >= 4)
return 0;
return (short) (_state[index, id] ? 1 : 0);
}
}
public class BsnesUnpluggedController : IBsnesController
{
private static readonly ControllerDefinition _definition = new();
public ControllerDefinition Definition => _definition;
public void UpdateState(IController controller) { }
public short GetState(int index, int id) => 0;
}
public class BsnesMouseController : IBsnesController
{
private readonly short[] _state = new short[4];
private static readonly ControllerDefinition _definition = new ControllerDefinition
{ BoolButtons = { "0Mouse Left", "0Mouse Right" } }
.AddXYPair("0Mouse {0}", AxisPairOrientation.RightAndDown, (-127).RangeTo(127), 0); //TODO verify direction against hardware, R+D inferred from behaviour in Mario Paint
public ControllerDefinition Definition => _definition;
public bool LimitAnalogChangeSensitivity { get; init; } = true;
public void UpdateState(IController controller)
{
int x = controller.AxisValue("0Mouse X");
if (LimitAnalogChangeSensitivity)
{
x = x.Clamp(-10, 10);
}
_state[0] = (short) x;
int y = controller.AxisValue("0Mouse Y");
if (LimitAnalogChangeSensitivity)
{
y = y.Clamp(-10, 10);
}
_state[1] = (short) y;
_state[2] = (short) (controller.IsPressed("0Mouse Left") ? 1 : 0);
_state[3] = (short) (controller.IsPressed("0Mouse Right") ? 1 : 0);
}
public short GetState(int index, int id)
{
if (id >= 4)
return 0;
return _state[id];
}
}
public class BsnesSuperScopeController : IBsnesController
{
private readonly short[] _state = new short[6];
private static readonly ControllerDefinition _definition = new ControllerDefinition
{ BoolButtons = { "0Trigger", "0Cursor", "0Turbo", "0Pause" } }
.AddLightGun("0Scope {0}");
public ControllerDefinition Definition => _definition;
public void UpdateState(IController controller)
{
_state[0] = (short) controller.AxisValue("0Scope X");
_state[1] = (short) controller.AxisValue("0Scope Y");
for (int i = 2; i < 6; i++)
{
_state[i] = (short) (controller.IsPressed(_definition.BoolButtons[i]) ? 1 : 0);
}
}
public short GetState(int index, int id)
{
if (id >= 6)
return 0;
return _state[id];
}
}
public class BsnesJustifierController : IBsnesController
{
public BsnesJustifierController(bool chained)
{
Definition = chained
? new ControllerDefinition
{ BoolButtons = { "0Trigger", "0Start", "1Trigger", "1Start" } }
.AddLightGun("0Justifier {0}")
.AddLightGun("1Justifier {0}")
: new ControllerDefinition
{BoolButtons = { "0Trigger", "0Start"} }
.AddLightGun("0Justifier {0}");
_state = new short[chained ? 8 : 4];
_chained = chained;
}
private readonly bool _chained;
private readonly short[] _state;
public ControllerDefinition Definition { get; }
public void UpdateState(IController controller)
{
_state[0] = (short) controller.AxisValue("0Justifier X");
_state[1] = (short) controller.AxisValue("0Justifier Y");
_state[2] = (short) (controller.IsPressed(Definition.BoolButtons[0]) ? 1 : 0);
_state[3] = (short) (controller.IsPressed(Definition.BoolButtons[1]) ? 1 : 0);
if (_chained)
{
_state[4] = (short) controller.AxisValue("1Justifier X");
_state[5] = (short) controller.AxisValue("1Justifier Y");
_state[6] = (short) (controller.IsPressed(Definition.BoolButtons[2]) ? 1 : 0);
_state[7] = (short) (controller.IsPressed(Definition.BoolButtons[3]) ? 1 : 0);
}
}
public short GetState(int index, int id)
{
if (index >= 2 || id >= 4 || (index == 1 && !_chained))
return 0;
return _state[index * 4 + id];
}
}
}

View File

@ -0,0 +1,92 @@
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.BSNES
{
public partial class BsnesCore : IEmulator
{
public IEmulatorServiceProvider ServiceProvider { get; }
public ControllerDefinition ControllerDefinition => _controllers.Definition;
public bool FrameAdvance(IController controller, bool render, bool renderSound)
{
_controller = controller;
/* if the input poll callback is called, it will set this to false
* this has to be done before we save the per-frame state in deterministic
* mode, because in there, the core actually advances, and might advance
* through the point in time where IsLagFrame gets set to false. makes sense?
*/
IsLagFrame = true;
bool resetSignal = controller.IsPressed("Reset");
if (resetSignal)
{
Api.core.snes_reset();
}
bool powerSignal = controller.IsPressed("Power");
if (powerSignal)
{
Api.core.snes_power();
}
var enables = new BsnesApi.LayerEnables
{
BG1_Prio0 = _settings.ShowBG1_0,
BG1_Prio1 = _settings.ShowBG1_1,
BG2_Prio0 = _settings.ShowBG2_0,
BG2_Prio1 = _settings.ShowBG2_1,
BG3_Prio0 = _settings.ShowBG3_0,
BG3_Prio1 = _settings.ShowBG3_1,
BG4_Prio0 = _settings.ShowBG4_0,
BG4_Prio1 = _settings.ShowBG4_1,
Obj_Prio0 = _settings.ShowOBJ_0,
Obj_Prio1 = _settings.ShowOBJ_1,
Obj_Prio2 = _settings.ShowOBJ_2,
Obj_Prio3 = _settings.ShowOBJ_3
};
// TODO: I really don't think stuff like this should be set every single frame (only on change)
Api.core.snes_set_layer_enables(enables);
Api.core.snes_set_trace_enabled(_tracer.Enabled);
Api.core.snes_set_video_enabled(render);
Api.core.snes_set_audio_enabled(renderSound);
// run the core for one frame
Frame++;
Api.core.snes_run();
if (IsLagFrame)
{
LagCount++;
}
return true;
}
public int Frame { get; private set; }
public string SystemId { get; }
public bool DeterministicEmulation => true;
public void ResetCounters()
{
Frame = 0;
LagCount = 0;
IsLagFrame = false;
}
public void Dispose()
{
if (_disposed)
{
return;
}
Api.Dispose();
_resampler.Dispose();
_disposed = true;
}
}
}

View File

@ -0,0 +1,15 @@
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.BSNES
{
public partial class BsnesCore : IInputPollable
{
public int LagCount { get; set; }
public bool IsLagFrame { get; set; }
// TODO: optimize managed to unmanaged using the ActiveChanged event
// ??? no idea what this is
public IInputCallbackSystem InputCallbacks { get; } = new InputCallbackSystem();
}
}

View File

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.BSNES
{
public partial class BsnesCore
{
private IMemoryDomains _memoryDomains;
private unsafe void SetMemoryDomains()
{
List<MemoryDomain> mm = new();
foreach (int i in Enum.GetValues(typeof(BsnesApi.SNES_MEMORY)))
{
void* data = Api.core.snes_get_memory_region(i, out int size, out int wordSize);
if (data == null) continue;
if (Enum.GetName(typeof(BsnesApi.SNES_MEMORY), i) == BsnesApi.SNES_MEMORY.CARTRIDGE_RAM.ToString())
{
_saveRam = (byte*) data;
_saveRamSize = size;
}
mm.Add(new MemoryDomainIntPtr(Enum.GetName(typeof(BsnesApi.SNES_MEMORY), i), MemoryDomain.Endian.Little, (IntPtr) data, size, true, wordSize));
}
mm.Add(new MemoryDomainDelegate(
"System Bus",
0x1000000,
MemoryDomain.Endian.Little,
address => Api.core.snes_bus_read((uint) address),
(address, value) => Api.core.snes_bus_write((uint) address, value), wordSize: 4));
mm.Add(Api.exe.GetPagesDomain());
_memoryDomains = new MemoryDomainList(mm);
((BasicServiceProvider) ServiceProvider).Register(_memoryDomains);
}
}
}

View File

@ -0,0 +1,11 @@
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.BSNES
{
public partial class BsnesCore : IRegionable
{
public DisplayType Region => _region == BsnesApi.SNES_REGION.NTSC
? DisplayType.NTSC
: DisplayType.PAL;
}
}

View File

@ -0,0 +1,39 @@
using System;
using System.Runtime.InteropServices;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.BSNES
{
public unsafe partial class BsnesCore : ISaveRam
{
private byte* _saveRam;
private int _saveRamSize;
// yeah this is not the best. this will basically always return true as long as the saveRam exists.
public bool SaveRamModified => _saveRamSize != 0;
public byte[] CloneSaveRam()
{
if (_saveRamSize == 0) return null;
byte[] saveRamCopy = new byte[_saveRamSize];
using (Api.exe.EnterExit())
{
Marshal.Copy((IntPtr) _saveRam, saveRamCopy, 0, _saveRamSize);
}
return saveRamCopy;
}
public void StoreSaveRam(byte[] data)
{
if (_saveRamSize == 0) return;
using (Api.exe.EnterExit())
{
Marshal.Copy(data, 0, (IntPtr) _saveRam, _saveRamSize);
}
}
}
}

View File

@ -0,0 +1,84 @@
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.BSNES
{
public partial class BsnesCore : ISettable<BsnesCore.SnesSettings, BsnesCore.SnesSyncSettings>
{
public SnesSettings GetSettings()
{
return _settings.Clone();
}
public SnesSyncSettings GetSyncSettings()
{
return _syncSettings.Clone();
}
public PutSettingsDirtyBits PutSettings(SnesSettings o)
{
_settings = o;
return PutSettingsDirtyBits.None;
}
public PutSettingsDirtyBits PutSyncSettings(SnesSyncSettings o)
{
bool ret = o.LeftPort != _syncSettings.LeftPort
|| o.RightPort != _syncSettings.RightPort
|| o.LimitAnalogChangeSensitivity != _syncSettings.LimitAnalogChangeSensitivity
|| o.Entropy != _syncSettings.Entropy
|| o.Hotfixes != _syncSettings.Hotfixes
|| o.FastPPU != _syncSettings.FastPPU;
_syncSettings = o;
return ret ? PutSettingsDirtyBits.RebootCore : PutSettingsDirtyBits.None;
}
private SnesSettings _settings;
private SnesSyncSettings _syncSettings;
public class SnesSettings
{
public bool ShowBG1_0 { get; set; } = true;
public bool ShowBG2_0 { get; set; } = true;
public bool ShowBG3_0 { get; set; } = true;
public bool ShowBG4_0 { get; set; } = true;
public bool ShowBG1_1 { get; set; } = true;
public bool ShowBG2_1 { get; set; } = true;
public bool ShowBG3_1 { get; set; } = true;
public bool ShowBG4_1 { get; set; } = true;
public bool ShowOBJ_0 { get; set; } = true;
public bool ShowOBJ_1 { get; set; } = true;
public bool ShowOBJ_2 { get; set; } = true;
public bool ShowOBJ_3 { get; set; } = true;
public bool AlwaysDoubleSize { get; set; }
public bool CropSGBFrame { get; set; }
public SnesSettings Clone()
{
return (SnesSettings) MemberwiseClone();
}
}
public class SnesSyncSettings
{
public BsnesApi.BSNES_INPUT_DEVICE LeftPort { get; set; } = BsnesApi.BSNES_INPUT_DEVICE.Gamepad;
public BsnesApi.BSNES_INPUT_DEVICE RightPort { get; set; } = BsnesApi.BSNES_INPUT_DEVICE.None;
public bool LimitAnalogChangeSensitivity { get; set; } = true;
public BsnesApi.ENTROPY Entropy { get; set; } = BsnesApi.ENTROPY.Low;
public bool Hotfixes { get; set; } = true;
public bool FastPPU { get; set; } = true;
public SnesSyncSettings Clone()
{
return (SnesSyncSettings) MemberwiseClone();
}
}
}
}

View File

@ -0,0 +1,24 @@
using System.IO;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.BSNES
{
public partial class BsnesCore : IStatable
{
public void SaveStateBinary(BinaryWriter writer)
{
Api.SaveStateBinary(writer);
writer.Write(IsLagFrame);
writer.Write(LagCount);
writer.Write(Frame);
}
public void LoadStateBinary(BinaryReader reader)
{
Api.LoadStateBinary(reader);
IsLagFrame = reader.ReadBoolean();
LagCount = reader.ReadInt32();
Frame = reader.ReadInt32();
}
}
}

View File

@ -0,0 +1,26 @@
using System;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.BSNES
{
public partial class BsnesCore : IVideoProvider
{
// TODO: This should probably be different for PAL?
public int VirtualWidth => (int) Math.Ceiling((double) BufferHeight * 64 / 49);
public int VirtualHeight => BufferHeight;
public int BufferWidth { get; private set; } = 256;
public int BufferHeight { get; private set; } = 224;
public int BackgroundColor => 0;
public int[] GetVideoBuffer() => _videoBuffer;
public int VsyncNumerator { get; }
public int VsyncDenominator { get; }
private int[] _videoBuffer = new int[256 * 224];
}
}

View File

@ -0,0 +1,391 @@
using System;
using System.Linq;
using System.Xml;
using System.IO;
using BizHawk.Common.BufferExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Components.W65816;
// http://wiki.superfamicom.org/snes/show/Backgrounds
namespace BizHawk.Emulation.Cores.Nintendo.BSNES
{
[PortedCore(CoreNames.Bsnes115, "bsnes team", "v115+", "https://bsnes.dev", isReleased: false)]
[ServiceNotApplicable(new[] { typeof(IDriveLight) })]
public unsafe partial class BsnesCore : IEmulator, IVideoProvider, ISaveRam, IStatable, IInputPollable, IRegionable, ISettable<BsnesCore.SnesSettings, BsnesCore.SnesSyncSettings>
{
private BsnesApi.SNES_REGION _region;
[CoreConstructor("SGB")]
[CoreConstructor("SNES")]
public BsnesCore(GameInfo game, byte[] rom, CoreComm comm,
SnesSettings settings, SnesSyncSettings syncSettings)
:this(game, rom, null, null, comm, settings, syncSettings)
{}
public BsnesCore(GameInfo game, byte[] romData, byte[] xmlData, string baseRomPath, CoreComm comm,
SnesSettings settings, SnesSyncSettings syncSettings)
{
_baseRomPath = baseRomPath;
var ser = new BasicServiceProvider(this);
ServiceProvider = ser;
CoreComm = comm;
byte[] sgbRomData = null;
if (game.System == "SGB")
{
if ((romData[0x143] & 0xc0) == 0xc0)
{
throw new CGBNotSupportedException();
}
sgbRomData = CoreComm.CoreFileProvider.GetFirmware("SNES", "Rom_SGB", true, "SGB Rom is required for SGB emulation.");
game.FirmwareHash = sgbRomData.HashSHA1();
}
_settings = settings ?? new SnesSettings();
_syncSettings = syncSettings ?? new SnesSyncSettings();
BsnesApi.SnesCallbacks callbacks = new()
{
inputPollCb = snes_input_poll,
inputStateCb = snes_input_state,
noLagCb = snes_no_lag,
videoFrameCb = snes_video_refresh,
audioSampleCb = snes_audio_sample,
pathRequestCb = snes_path_request,
snesTraceCb = snes_trace
};
Api = new BsnesApi(CoreComm.CoreFileProvider.DllPath(), CoreComm, new Delegate[]
{
callbacks.inputPollCb,
callbacks.inputStateCb,
callbacks.noLagCb,
callbacks.videoFrameCb,
callbacks.audioSampleCb,
callbacks.pathRequestCb,
callbacks.snesTraceCb
});
_controllers = new BsnesControllers(_syncSettings);
generate_palette();
// TODO: massive random hack till waterboxhost gets fixed to support 5+ args
ushort mergedBools = (ushort) ((_syncSettings.Hotfixes ? 1 << 8 : 0) | (_syncSettings.FastPPU ? 1 : 0));
Api.core.snes_init(_syncSettings.Entropy, _syncSettings.LeftPort, _syncSettings.RightPort, mergedBools);
Api.SetCallbacks(callbacks);
// start up audio resampler
InitAudio();
ser.Register<ISoundProvider>(_resampler);
if (game.System == "SGB")
{
IsSGB = true;
SystemId = "SNES";
ser.Register<IBoardInfo>(new SGBBoardInfo());
_currLoadParams = new LoadParams
{
type = LoadParamType.SuperGameBoy,
baseRomPath = baseRomPath,
romData = sgbRomData,
sgbRomData = romData
};
}
else
{
// we may need to get some information out of the cart, even during the following bootup/load process
if (xmlData != null)
{
_romxml = new XmlDocument();
_romxml.Load(new MemoryStream(xmlData));
// bsnes wont inspect the xml to load the necessary sfc file.
// so, we have to do that here and pass it in as the romData :/
// TODO: uhh i have no idea what the xml is or whether this below code is needed
if (_romxml["cartridge"]?["rom"] != null)
{
romData = File.ReadAllBytes(PathSubfile(_romxml["cartridge"]["rom"].Attributes["name"].Value));
}
else
{
throw new Exception("Could not find rom file specification in xml file. Please check the integrity of your xml file");
}
}
SystemId = "SNES";
_currLoadParams = new LoadParams
{
type = LoadParamType.Normal,
baseRomPath = baseRomPath,
romData = romData
};
}
LoadCurrent();
if (_region == BsnesApi.SNES_REGION.NTSC)
{
// taken from bsnes source
VsyncNumerator = 21477272;
VsyncDenominator = 357366;
}
else
{
// http://forums.nesdev.com/viewtopic.php?t=5367&start=19
VsyncNumerator = 21281370;
VsyncDenominator = 4 * 341 * 312;
}
SetMemoryDomains();
_tracer = new TraceBuffer
{
Header = "65816: PC, mnemonic, operands, registers (A, X, Y, S, D, B, flags (NVMXDIZC), V, H)"
};
ser.Register<IDisassemblable>(new W65816_DisassemblerService());
ser.Register(_tracer);
Api.Seal();
}
private CoreComm CoreComm { get; }
private readonly string _baseRomPath;
private string PathSubfile(string fname) => Path.Combine(_baseRomPath, fname);
private readonly BsnesControllers _controllers;
private readonly ITraceable _tracer;
private readonly XmlDocument _romxml;
private IController _controller;
private readonly LoadParams _currLoadParams;
private SpeexResampler _resampler;
private bool _disposed;
public bool IsSGB { get; }
private class SGBBoardInfo : IBoardInfo
{
public string BoardName => "SGB";
}
private BsnesApi Api { get; }
private string snes_path_request(int slot, string hint, bool required)
{
// TODO: this msu1 handling code is outdated and needs to be remade from someone with knowledge.
// every rom requests msu1.rom... why? who knows.
// also handle msu-1 pcm files here
bool isMsu1Rom = hint == "msu1/data.rom";
bool isMsu1Pcm = Path.GetExtension(hint).ToLower() == ".pcm";
if (isMsu1Rom || isMsu1Pcm)
{
// well, check if we have an msu-1 xml
if (_romxml?["cartridge"]?["msu1"] != null)
{
var msu1 = _romxml["cartridge"]["msu1"];
if (isMsu1Rom && msu1["rom"]?.Attributes["name"] != null)
{
return PathSubfile(msu1["rom"].Attributes["name"].Value);
}
if (isMsu1Pcm)
{
// return @"D:\roms\snes\SuperRoadBlaster\SuperRoadBlaster-1.pcm";
// return "";
int wantsTrackNumber = int.Parse(hint.Replace("track-", "").Replace(".pcm", ""));
wantsTrackNumber++;
string wantsTrackString = wantsTrackNumber.ToString();
foreach (var child in msu1.ChildNodes.Cast<XmlNode>())
{
if (child.Name == "track" && child.Attributes["number"].Value == wantsTrackString)
{
return PathSubfile(child.Attributes["name"].Value);
}
}
}
}
// not found.. what to do? (every rom will get here when msu1.rom is requested)
return "";
}
// not MSU-1. ok.
if (hint == "save.ram")
{
// core asked for saveram, but the interface isn't designed to be able to handle this.
// so, we'll just return nothing and the frontend will set the saveram itself later
return null;
}
string firmwareId;
switch (hint)
{
case "cx4": firmwareId = "CX4"; break;
case "dsp1": firmwareId = "DSP1"; break;
case "dsp1b": firmwareId = "DSP1b"; break;
case "dsp2": firmwareId = "DSP2"; break;
case "dsp3": firmwareId = "DSP3"; break;
case "dsp4": firmwareId = "DSP4"; break;
case "st010": firmwareId = "ST010"; break;
case "st011": firmwareId = "ST011"; break;
case "st018": firmwareId = "ST018"; break;
default:
CoreComm.ShowMessage($"Unrecognized SNES firmware request \"{hint}\".");
return "";
}
string ret = "";
var data = CoreComm.CoreFileProvider.GetFirmware("SNES", firmwareId, required, "Game may function incorrectly without the requested firmware.");
if (data != null)
{
ret = hint;
Api.AddReadonlyFile(data, hint);
}
Console.WriteLine("Served bsnescore request for firmware \"{0}\"", hint);
// return the path we built
return ret;
}
private enum LoadParamType
{
Normal, SuperGameBoy
}
private struct LoadParams
{
public LoadParamType type;
public string baseRomPath;
public byte[] romData;
public byte[] sgbRomData;
}
private void LoadCurrent()
{
if (_currLoadParams.type == LoadParamType.Normal)
Api.core.snes_load_cartridge_normal(_currLoadParams.baseRomPath, _currLoadParams.romData, _currLoadParams.romData.Length);
else
Api.core.snes_load_cartridge_super_gameboy(_currLoadParams.baseRomPath, _currLoadParams.romData,
_currLoadParams.sgbRomData, (ulong) _currLoadParams.romData.Length << 32 | (uint)_currLoadParams.sgbRomData.Length);
_region = Api.core.snes_get_region();
}
// poll which updates the controller state
private void snes_input_poll()
{
_controllers.CoreInputPoll(_controller);
}
/// <param name="port">0 or 1, corresponding to L and R physical ports on the snes</param>
/// <param name="index">meaningless for most controllers. for multitap, 0-3 for which multitap controller</param>
/// <param name="id">button ID enum; in the case of a regular controller, this corresponds to shift register position</param>
/// <returns>for regular controllers, one bit D0 of button status. for other controls, varying ranges depending on id</returns>
private short snes_input_state(int port, int index, int id)
{
return _controllers.CoreInputState(port, index, id);
}
private void snes_no_lag()
{
// gets called whenever there was input polled, aka no lag
IsLagFrame = false;
}
private readonly int[] palette = new int[32768];
private void generate_palette()
{
for (int color = 0; color < 32768; color++) {
int r = (color >> 10) & 31;
int g = (color >> 5) & 31;
int b = (color >> 0) & 31;
r = r << 3 | r >> 2; r = r << 8 | r << 0;
g = g << 3 | g >> 2; g = g << 8 | g << 0;
b = b << 3 | b >> 2; b = b << 8 | b << 0;
palette[color] = r >> 8 << 16 | g >> 8 << 8 | b >> 8 << 0;
}
}
private void snes_video_refresh(ushort* data, int width, int height, int pitch)
{
int widthMultiplier = 1;
int heightMultiplier = 1;
if (_settings.CropSGBFrame && IsSGB)
{
BufferWidth = 160;
BufferHeight = 144;
}
else
{
if (_settings.AlwaysDoubleSize)
{
if (width == 256) widthMultiplier = 2;
if (height == 224) heightMultiplier = 2;
}
BufferWidth = width * widthMultiplier;
BufferHeight = height * heightMultiplier;
}
int size = BufferWidth * BufferHeight;
if (_videoBuffer.Length != size)
{
_videoBuffer = new int[size];
}
int di = 0;
if (_settings.CropSGBFrame && IsSGB)
{
for (int y = 39; y < 39 + 144; y++)
{
ushort* sp = data + y * pitch + 48;
for (int x = 0; x < 160; x++)
{
_videoBuffer[di++] = palette[*sp++];
}
}
return;
}
for (int y = 0; y < height * heightMultiplier; y++)
{
int si = y / heightMultiplier * pitch;
for (int x = 0; x < width * widthMultiplier; x++)
{
_videoBuffer[di++] = palette[data[si + x / widthMultiplier]];
}
}
}
private void InitAudio()
{
_resampler = new SpeexResampler(SpeexResampler.Quality.QUALITY_DESKTOP, 64080, 88200, 32040, 44100);
}
private void snes_audio_sample(short left, short right)
{
_resampler.EnqueueSample(left, right);
}
private void snes_trace(string disassembly, string registerInfo)
{
_tracer.Put(new TraceInfo
{
Disassembly = disassembly,
RegisterInfo = registerInfo
});
}
}
}

View File

@ -12,6 +12,7 @@ namespace BizHawk.Emulation.Cores
public const string A7800Hawk = "A7800Hawk";
public const string Atari2600Hawk = "Atari2600Hawk";
public const string Bsnes = "BSNES";
public const string Bsnes115 = "BSNESv115+";
public const string C64Hawk = "C64Hawk";
public const string ChannelFHawk = "ChannelFHawk";
public const string ColecoHawk = "ColecoHawk";

View File

@ -0,0 +1,6 @@
root = false
[*]
# let it auto-detect indent_style for each file
# i'm not gonna let my workflow get tampered with
indent_style =

View File

@ -0,0 +1,60 @@
NEED_LIBCO := 1
CXXFLAGS := -std=c++17 \
-I../libco -I./bsnes \
-Werror=int-to-pointer-cast \
-Wno-parentheses -Wno-sign-compare -Wno-unused-variable -Wno-trigraphs -Wno-switch -Wno-reorder -Wno-misleading-indentation \
-fno-threadsafe-statics -fno-strict-aliasing -fwrapv
CCFLAGS := -std=c11 -DGB_INTERNAL -DGB_DISABLE_DEBUGGER -DGB_DISABLE_CHEATS -D_GNU_SOURCE \
-Werror \
-Wall -Wno-multichar -Wno-int-in-bool-context \
-fno-strict-aliasing
TARGET = bsnes.wbx
SRCS_PROCESSORS = \
$(ROOT_DIR)/bsnes/processor/spc700/spc700.cpp \
$(ROOT_DIR)/bsnes/processor/wdc65816/wdc65816.cpp \
$(ROOT_DIR)/bsnes/processor/arm7tdmi/arm7tdmi.cpp
SRCS_EMULATOR = \
$(ROOT_DIR)/bsnes/emulator/emulator.cpp
SRCS_GB = \
$(ROOT_DIR)/bsnes/gb/Core/apu.c \
$(ROOT_DIR)/bsnes/gb/Core/camera.c \
$(ROOT_DIR)/bsnes/gb/Core/rumble.c \
$(ROOT_DIR)/bsnes/gb/Core/display.c \
$(ROOT_DIR)/bsnes/gb/Core/gb.c \
$(ROOT_DIR)/bsnes/gb/Core/joypad.c \
$(ROOT_DIR)/bsnes/gb/Core/mbc.c \
$(ROOT_DIR)/bsnes/gb/Core/memory.c \
$(ROOT_DIR)/bsnes/gb/Core/printer.c \
$(ROOT_DIR)/bsnes/gb/Core/random.c \
$(ROOT_DIR)/bsnes/gb/Core/rewind.c \
$(ROOT_DIR)/bsnes/gb/Core/save_state.c \
$(ROOT_DIR)/bsnes/gb/Core/sgb.c \
$(ROOT_DIR)/bsnes/gb/Core/sm83_cpu.c \
$(ROOT_DIR)/bsnes/gb/Core/symbol_hash.c \
$(ROOT_DIR)/bsnes/gb/Core/timing.c
SRCS_SFC = \
$(ROOT_DIR)/bsnes/sfc/interface/interface.cpp \
$(ROOT_DIR)/bsnes/sfc/system/system.cpp \
$(ROOT_DIR)/bsnes/sfc/controller/controller.cpp \
$(ROOT_DIR)/bsnes/sfc/cartridge/cartridge.cpp \
$(ROOT_DIR)/bsnes/sfc/memory/memory.cpp \
$(ROOT_DIR)/bsnes/sfc/cpu/cpu.cpp \
$(ROOT_DIR)/bsnes/sfc/smp/smp.cpp \
$(ROOT_DIR)/bsnes/sfc/dsp/dsp.cpp \
$(ROOT_DIR)/bsnes/sfc/ppu/ppu.cpp \
$(ROOT_DIR)/bsnes/sfc/ppu-fast/ppu.cpp \
$(ROOT_DIR)/bsnes/sfc/expansion/expansion.cpp \
$(ROOT_DIR)/bsnes/sfc/coprocessor/coprocessor.cpp \
$(ROOT_DIR)/bsnes/sfc/slot/slot.cpp \
$(ROOT_DIR)/bsnes/target-bsnescore/bsnescore.cpp
SRCS = $(SRCS_PROCESSORS) $(SRCS_EMULATOR) $(SRCS_GB) $(SRCS_SFC)
include ../common.mak

View File

@ -0,0 +1,73 @@
namespace Emulator {
#include "stream.cpp"
Audio audio;
Audio::~Audio() {
reset(nullptr);
}
auto Audio::reset(Interface* interface) -> void {
_interface = interface;
_streams.reset();
_channels = 0;
}
auto Audio::setFrequency(double frequency) -> void {
_frequency = frequency;
for(auto& stream : _streams) {
stream->setFrequency(stream->inputFrequency, frequency);
}
}
auto Audio::setVolume(double volume) -> void {
_volume = volume;
}
auto Audio::setBalance(double balance) -> void {
_balance = balance;
}
auto Audio::createStream(uint channels, double frequency) -> shared_pointer<Stream> {
_channels = max(_channels, channels);
shared_pointer<Stream> stream = new Stream;
stream->reset(channels, frequency, _frequency);
_streams.append(stream);
return stream;
}
auto Audio::process() -> void {
while(_streams) {
for(auto& stream : _streams) {
if(!stream->pending()) return;
}
double samples[_channels];
for(auto& sample : samples) sample = 0.0;
for(auto& stream : _streams) {
double buffer[_channels];
uint length = stream->read(buffer), offset = 0;
for(auto& sample : samples) {
sample += buffer[offset];
if(++offset >= length) offset = 0;
}
}
for(auto c : range(_channels)) {
samples[c] = max(-1.0, min(+1.0, samples[c] * _volume));
}
if(_channels == 2) {
if(_balance < 0.0) samples[1] *= 1.0 + _balance;
if(_balance > 0.0) samples[0] *= 1.0 - _balance;
}
platform->audioFrame(samples, _channels);
}
}
}
#undef double

View File

@ -0,0 +1,94 @@
#pragma once
#include <nall/dsp/iir/dc-removal.hpp>
#include <nall/dsp/iir/one-pole.hpp>
#include <nall/dsp/iir/biquad.hpp>
#include <nall/dsp/resampler/cubic.hpp>
namespace Emulator {
struct Interface;
struct Audio;
struct Filter;
struct Stream;
struct Audio {
~Audio();
auto reset(Interface* interface) -> void;
inline auto channels() const -> uint { return _channels; }
inline auto frequency() const -> double { return _frequency; }
inline auto volume() const -> double { return _volume; }
inline auto balance() const -> double { return _balance; }
auto setFrequency(double frequency) -> void;
auto setVolume(double volume) -> void;
auto setBalance(double balance) -> void;
auto createStream(uint channels, double frequency) -> shared_pointer<Stream>;
private:
auto process() -> void;
Interface* _interface = nullptr;
vector<shared_pointer<Stream>> _streams;
uint _channels = 0;
double _frequency = 48000.0;
double _volume = 1.0;
double _balance = 0.0;
friend class Stream;
};
struct Filter {
enum class Mode : uint { DCRemoval, OnePole, Biquad } mode;
enum class Type : uint { None, LowPass, HighPass } type;
enum class Order : uint { None, First, Second } order;
DSP::IIR::DCRemoval dcRemoval;
DSP::IIR::OnePole onePole;
DSP::IIR::Biquad biquad;
};
struct Stream {
auto reset(uint channels, double inputFrequency, double outputFrequency) -> void;
auto reset() -> void;
auto frequency() const -> double;
auto setFrequency(double inputFrequency, maybe<double> outputFrequency = nothing) -> void;
auto addDCRemovalFilter() -> void;
auto addLowPassFilter(double cutoffFrequency, Filter::Order order, uint passes = 1) -> void;
auto addHighPassFilter(double cutoffFrequency, Filter::Order order, uint passes = 1) -> void;
auto pending() const -> uint;
auto read(double samples[]) -> uint;
auto write(const double samples[]) -> void;
template<typename... P> auto sample(P&&... p) -> void {
double samples[sizeof...(P)] = {forward<P>(p)...};
write(samples);
}
auto serialize(serializer&) -> void;
private:
struct Channel {
vector<Filter> filters;
vector<DSP::IIR::Biquad> nyquist;
DSP::Resampler::Cubic resampler;
};
vector<Channel> channels;
double inputFrequency;
double outputFrequency;
friend class Audio;
};
extern Audio audio;
}
#undef double

View File

@ -0,0 +1,125 @@
auto Stream::reset(uint channelCount, double inputFrequency, double outputFrequency) -> void {
channels.reset();
channels.resize(channelCount);
for(auto& channel : channels) {
channel.filters.reset();
}
setFrequency(inputFrequency, outputFrequency);
}
auto Stream::reset() -> void {
for(auto& channel : channels) {
channel.resampler.reset(this->inputFrequency, this->outputFrequency);
}
}
auto Stream::frequency() const -> double {
return inputFrequency;
}
auto Stream::setFrequency(double inputFrequency, maybe<double> outputFrequency) -> void {
this->inputFrequency = inputFrequency;
if(outputFrequency) this->outputFrequency = outputFrequency();
for(auto& channel : channels) {
channel.nyquist.reset();
channel.resampler.reset(this->inputFrequency, this->outputFrequency);
}
if(this->inputFrequency >= this->outputFrequency * 2) {
//add a low-pass filter to prevent aliasing during resampling
double cutoffFrequency = min(25000.0, this->outputFrequency / 2.0 - 2000.0);
for(auto& channel : channels) {
uint passes = 3;
for(uint pass : range(passes)) {
DSP::IIR::Biquad filter;
double q = DSP::IIR::Biquad::butterworth(passes * 2, pass);
filter.reset(DSP::IIR::Biquad::Type::LowPass, cutoffFrequency, this->inputFrequency, q);
channel.nyquist.append(filter);
}
}
}
}
auto Stream::addDCRemovalFilter() -> void {
return; //todo: test to ensure this is desirable before enabling
for(auto& channel : channels) {
Filter filter{Filter::Mode::DCRemoval, Filter::Type::None, Filter::Order::None};
channel.filters.append(filter);
}
}
auto Stream::addLowPassFilter(double cutoffFrequency, Filter::Order order, uint passes) -> void {
for(auto& channel : channels) {
for(uint pass : range(passes)) {
if(order == Filter::Order::First) {
Filter filter{Filter::Mode::OnePole, Filter::Type::LowPass, Filter::Order::First};
filter.onePole.reset(DSP::IIR::OnePole::Type::LowPass, cutoffFrequency, inputFrequency);
channel.filters.append(filter);
}
if(order == Filter::Order::Second) {
Filter filter{Filter::Mode::Biquad, Filter::Type::LowPass, Filter::Order::Second};
double q = DSP::IIR::Biquad::butterworth(passes * 2, pass);
filter.biquad.reset(DSP::IIR::Biquad::Type::LowPass, cutoffFrequency, inputFrequency, q);
channel.filters.append(filter);
}
}
}
}
auto Stream::addHighPassFilter(double cutoffFrequency, Filter::Order order, uint passes) -> void {
for(auto& channel : channels) {
for(uint pass : range(passes)) {
if(order == Filter::Order::First) {
Filter filter{Filter::Mode::OnePole, Filter::Type::HighPass, Filter::Order::First};
filter.onePole.reset(DSP::IIR::OnePole::Type::HighPass, cutoffFrequency, inputFrequency);
channel.filters.append(filter);
}
if(order == Filter::Order::Second) {
Filter filter{Filter::Mode::Biquad, Filter::Type::HighPass, Filter::Order::Second};
double q = DSP::IIR::Biquad::butterworth(passes * 2, pass);
filter.biquad.reset(DSP::IIR::Biquad::Type::HighPass, cutoffFrequency, inputFrequency, q);
channel.filters.append(filter);
}
}
}
}
auto Stream::pending() const -> uint {
if(!channels) return 0;
return channels[0].resampler.pending();
}
auto Stream::read(double samples[]) -> uint {
for(uint c : range(channels.size())) samples[c] = channels[c].resampler.read();
return channels.size();
}
auto Stream::write(const double samples[]) -> void {
for(auto c : range(channels.size())) {
double sample = samples[c] + 1e-25; //constant offset used to suppress denormals
for(auto& filter : channels[c].filters) {
switch(filter.mode) {
case Filter::Mode::DCRemoval: sample = filter.dcRemoval.process(sample); break;
case Filter::Mode::OnePole: sample = filter.onePole.process(sample); break;
case Filter::Mode::Biquad: sample = filter.biquad.process(sample); break;
}
}
for(auto& filter : channels[c].nyquist) {
sample = filter.process(sample);
}
channels[c].resampler.write(sample);
}
audio.process();
}
auto Stream::serialize(serializer& s) -> void {
for(auto& channel : channels) {
channel.resampler.serialize(s);
}
s.real(inputFrequency);
s.real(outputFrequency);
}

View File

@ -0,0 +1,57 @@
#pragma once
namespace Emulator {
struct Cheat {
struct Code {
auto operator==(const Code& code) const -> bool {
if(address != code.address) return false;
if(data != code.data) return false;
if((bool)compare != (bool)code.compare) return false;
if(compare && code.compare && compare() != code.compare()) return false;
return true;
}
uint address;
uint data;
maybe<uint> compare;
bool enable;
uint restore;
};
explicit operator bool() const {
return codes.size() > 0;
}
auto reset() -> void {
codes.reset();
}
auto append(uint address, uint data, maybe<uint> compare = {}) -> void {
codes.append({address, data, compare});
}
auto assign(const vector<string>& list) -> void {
reset();
for(auto& entry : list) {
for(auto code : entry.split("+")) {
auto part = code.transform("=?", "//").split("/");
if(part.size() == 2) append(part[0].hex(), part[1].hex());
if(part.size() == 3) append(part[0].hex(), part[2].hex(), part[1].hex());
}
}
}
auto find(uint address, uint compare) -> maybe<uint> {
for(auto& code : codes) {
if(code.address == address && (!code.compare || code.compare() == compare)) {
return code.data;
}
}
return nothing;
}
vector<Code> codes;
};
}

View File

@ -0,0 +1,8 @@
#include <emulator/emulator.hpp>
#include <emulator/audio/audio.cpp>
namespace Emulator {
Platform* platform = nullptr;
}

View File

@ -0,0 +1,58 @@
#pragma once
#include <libco.h>
#include <nall/platform.hpp>
#include <nall/adaptive-array.hpp>
#include <nall/any.hpp>
#include <nall/chrono.hpp>
#include <nall/dl.hpp>
#include <nall/endian.hpp>
#include <nall/image.hpp>
#include <nall/literals.hpp>
#include <nall/random.hpp>
#include <nall/serializer.hpp>
#include <nall/shared-pointer.hpp>
#include <nall/string.hpp>
#include <nall/traits.hpp>
#include <nall/unique-pointer.hpp>
#include <nall/vector.hpp>
#include <nall/vfs.hpp>
#include <nall/hash/crc32.hpp>
#include <nall/hash/sha256.hpp>
using namespace nall;
#include <emulator/types.hpp>
#include <emulator/memory/readable.hpp>
#include <emulator/memory/writable.hpp>
#include <emulator/audio/audio.hpp>
namespace Emulator {
static const string Name = "bsnes";
static const string Version = "115";
static const string Copyright = "bsnes team";
static const string License = "GPLv3 or later";
static const string Website = "https://bsnes.dev";
//incremented only when serialization format changes
static const string SerializerVersion = "115";
namespace Constants {
namespace Colorburst {
static constexpr double NTSC = 315.0 / 88.0 * 1'000'000.0;
static constexpr double PAL = 283.75 * 15'625.0 + 25.0;
}
}
//nall/vfs shorthand constants for open(), load()
namespace File {
static const auto Read = vfs::file::mode::read;
static const auto Write = vfs::file::mode::write;
static const auto Optional = false;
static const auto Required = true;
};
}
#include "platform.hpp"
#include "interface.hpp"
#include "game.hpp"

View File

@ -0,0 +1,112 @@
#pragma once
namespace Emulator {
struct Game {
struct Memory;
struct Oscillator;
inline auto load(string_view) -> void;
inline auto memory(Markup::Node) -> maybe<Memory>;
inline auto oscillator(natural = 0) -> maybe<Oscillator>;
struct Memory {
Memory() = default;
inline Memory(Markup::Node);
explicit operator bool() const { return (bool)type; }
inline auto name() const -> string;
string type;
natural size;
string content;
string manufacturer;
string architecture;
string identifier;
boolean nonVolatile;
};
struct Oscillator {
Oscillator() = default;
inline Oscillator(Markup::Node);
explicit operator bool() const { return frequency; }
natural frequency;
};
Markup::Node document;
string sha256;
string label;
string name;
string title;
string region;
string revision;
string board;
vector<Memory> memoryList;
vector<Oscillator> oscillatorList;
};
auto Game::load(string_view text) -> void {
document = BML::unserialize(text);
sha256 = document["game/sha256"].text();
label = document["game/label"].text();
name = document["game/name"].text();
title = document["game/title"].text();
region = document["game/region"].text();
revision = document["game/revision"].text();
board = document["game/board"].text();
for(auto node : document.find("game/board/memory")) {
memoryList.append(Memory{node});
}
for(auto node : document.find("game/board/oscillator")) {
oscillatorList.append(Oscillator{node});
}
}
auto Game::memory(Markup::Node node) -> maybe<Memory> {
if(!node) return nothing;
for(auto& memory : memoryList) {
auto type = node["type"].text();
auto size = node["size"].natural();
auto content = node["content"].text();
auto manufacturer = node["manufacturer"].text();
auto architecture = node["architecture"].text();
auto identifier = node["identifier"].text();
if(type && type != memory.type) continue;
if(size && size != memory.size) continue;
if(content && content != memory.content) continue;
if(manufacturer && manufacturer != memory.manufacturer) continue;
if(architecture && architecture != memory.architecture) continue;
if(identifier && identifier != memory.identifier) continue;
return memory;
}
return nothing;
}
auto Game::oscillator(natural index) -> maybe<Oscillator> {
if(index < oscillatorList.size()) return oscillatorList[index];
return nothing;
}
Game::Memory::Memory(Markup::Node node) {
type = node["type"].text();
size = node["size"].natural();
content = node["content"].text();
manufacturer = node["manufacturer"].text();
architecture = node["architecture"].text();
identifier = node["identifier"].text();
nonVolatile = !(bool)node["volatile"];
}
auto Game::Memory::name() const -> string {
if(architecture) return string{architecture, ".", content, ".", type}.downcase();
return string{content, ".", type}.downcase();
}
Game::Oscillator::Oscillator(Markup::Node node) {
frequency = node["frequency"].natural();
}
}

View File

@ -0,0 +1,109 @@
#pragma once
namespace Emulator {
struct Interface {
struct Information {
string manufacturer;
string name;
string extension;
bool resettable = false;
};
struct Display {
struct Type { enum : uint {
CRT,
LCD,
};};
uint id = 0;
string name;
uint type = 0;
uint colors = 0;
uint width = 0;
uint height = 0;
uint internalWidth = 0;
uint internalHeight = 0;
double aspectCorrection = 0;
};
struct Port {
uint id;
string name;
};
struct Device {
uint id;
string name;
};
struct Input {
struct Type { enum : uint {
Hat,
Button,
Trigger,
Control,
Axis,
Rumble,
};};
uint type;
string name;
};
//information
virtual auto information() -> Information { return {}; }
virtual auto display() -> Display { return {}; }
virtual auto color(uint32 color) -> uint64 { return 0; }
//game interface
virtual auto loaded() -> bool { return false; }
virtual auto hashes() -> vector<string> { return {}; }
virtual auto manifests() -> vector<string> { return {}; }
virtual auto titles() -> vector<string> { return {}; }
virtual auto title() -> string { return {}; }
virtual auto load() -> bool { return false; }
virtual auto save() -> void {}
virtual auto unload() -> void {}
//system interface
virtual auto ports() -> vector<Port> { return {}; }
virtual auto devices(uint port) -> vector<Device> { return {}; }
virtual auto inputs(uint device) -> vector<Input> { return {}; }
virtual auto connected(uint port) -> uint { return 0; }
virtual auto connect(uint port, uint device) -> void {}
virtual auto power() -> void {}
virtual auto reset() -> void {}
virtual auto run() -> void {}
//time functions
virtual auto rtc() -> bool { return false; }
virtual auto synchronize(uint64 timestamp = 0) -> void {}
//state functions
virtual auto serialize(bool synchronize = true) -> serializer { return {}; }
virtual auto unserialize(serializer&) -> bool { return false; }
//cheat functions
virtual auto read(uint24 address) -> uint8 { return 0; }
virtual auto cheats(const vector<string>& = {}) -> void {}
//configuration
virtual auto configuration() -> string { return {}; }
virtual auto configuration(string name) -> string { return {}; }
virtual auto configure(string configuration = "") -> bool { return false; }
virtual auto configure(string name, string value) -> bool { return false; }
//settings
virtual auto cap(const string& name) -> bool { return false; }
virtual auto get(const string& name) -> any { return {}; }
virtual auto set(const string& name, const any& value) -> bool { return false; }
virtual auto frameSkip() -> uint { return 0; }
virtual auto setFrameSkip(uint frameSkip) -> void {}
virtual auto runAhead() -> bool { return false; }
virtual auto setRunAhead(bool runAhead) -> void {}
};
}

View File

@ -0,0 +1,30 @@
#pragma once
namespace Emulator::Memory {
inline auto mirror(uint address, uint size) -> uint {
if(size == 0) return 0;
uint base = 0;
uint mask = 1 << 31;
while(address >= size) {
while(!(address & mask)) mask >>= 1;
address -= mask;
if(size > mask) {
size -= mask;
base += mask;
}
mask >>= 1;
}
return base + address;
}
inline auto reduce(uint address, uint mask) -> uint {
while(mask) {
uint bits = (mask & -mask) - 1;
address = address >> 1 & ~bits | address & bits;
mask = (mask & mask - 1) >> 1;
}
return address;
}
}

View File

@ -0,0 +1,63 @@
#pragma once
#include <emulator/memory/memory.hpp>
namespace Emulator::Memory {
template<typename T>
struct Readable {
~Readable() { reset(); }
inline auto reset() -> void {
delete[] self.data;
self.data = nullptr;
self.size = 0;
self.mask = 0;
}
inline auto allocate(uint size, T fill = ~0ull) -> void {
if(!size) return reset();
delete[] self.data;
self.size = size;
self.mask = bit::round(self.size) - 1;
self.data = new T[self.mask + 1];
memory::fill<T>(self.data, self.mask + 1, fill);
}
inline auto load(shared_pointer<vfs::file> fp) -> void {
fp->read(self.data, min(fp->size(), self.size * sizeof(T)));
for(uint address = self.size; address <= self.mask; address++) {
self.data[address] = self.data[mirror(address, self.size)];
}
}
inline auto save(shared_pointer<vfs::file> fp) -> void {
fp->write(self.data, self.size * sizeof(T));
}
explicit operator bool() const { return (bool)self.data; }
inline auto data() const -> const T* { return self.data; }
inline auto size() const -> uint { return self.size; }
inline auto mask() const -> uint { return self.mask; }
inline auto operator[](uint address) const -> T { return self.data[address & self.mask]; }
inline auto read(uint address) const -> T { return self.data[address & self.mask]; }
inline auto write(uint address, T data) const -> void {}
auto serialize(serializer& s) -> void {
const uint size = self.size;
s.integer(self.size);
s.integer(self.mask);
if(self.size != size) allocate(self.size);
s.array(self.data, self.size);
}
private:
struct {
T* data = nullptr;
uint size = 0;
uint mask = 0;
} self;
};
}

View File

@ -0,0 +1,65 @@
#pragma once
#include <emulator/memory/memory.hpp>
namespace Emulator::Memory {
template<typename T>
struct Writable {
~Writable() { reset(); }
inline auto reset() -> void {
delete[] self.data;
self.data = nullptr;
self.size = 0;
self.mask = 0;
}
inline auto allocate(uint size, T fill = ~0ull) -> void {
if(!size) return reset();
delete[] self.data;
self.size = size;
self.mask = bit::round(self.size) - 1;
self.data = new T[self.mask + 1];
memory::fill<T>(self.data, self.mask + 1, fill);
}
inline auto load(shared_pointer<vfs::file> fp) -> void {
fp->read(self.data, min(fp->size(), self.size * sizeof(T)));
for(uint address = self.size; address <= self.mask; address++) {
self.data[address] = self.data[mirror(address, self.size)];
}
}
inline auto save(shared_pointer<vfs::file> fp) -> void {
fp->write(self.data, self.size * sizeof(T));
}
explicit operator bool() const { return (bool)self.data; }
inline auto data() -> T* { return self.data; }
inline auto data() const -> const T* { return self.data; }
inline auto size() const -> uint { return self.size; }
inline auto mask() const -> uint { return self.mask; }
inline auto operator[](uint address) -> T& { return self.data[address & self.mask]; }
inline auto operator[](uint address) const -> T { return self.data[address & self.mask]; }
inline auto read(uint address) const -> T { return self.data[address & self.mask]; }
inline auto write(uint address, T data) -> void { self.data[address & self.mask] = data; }
auto serialize(serializer& s) -> void {
const uint size = self.size;
s.integer(self.size);
s.integer(self.mask);
if(self.size != size) allocate(self.size);
s.array(self.data, self.size);
}
private:
struct {
T* data = nullptr;
uint size = 0;
uint mask = 0;
} self;
};
}

View File

@ -0,0 +1,34 @@
#pragma once
namespace Emulator {
struct Platform {
struct Load {
Load() = default;
Load(uint pathID, string option = "") : valid(true), pathID(pathID), option(option) {}
explicit operator bool() const { return valid; }
bool valid = false;
uint pathID = 0;
string option;
};
virtual auto path(uint id) -> string { return ""; }
virtual auto open(uint id, string name, vfs::file::mode mode, bool required = false) -> shared_pointer<vfs::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 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; }
virtual auto notify(string text) -> void {}
// 03-may-2021 manual addition. unused currently but let's hope for the best
virtual auto getBackdropColor() -> uint16 { return 0; }
bool traceEnabled = false;
virtual auto cpuTrace(vector<string>) -> void {}
};
extern Platform* platform;
}

View File

@ -0,0 +1,96 @@
#pragma once
namespace Emulator {
struct Random {
enum class Entropy : uint { None, Low, High };
auto operator()() -> uint64 {
return random();
}
auto entropy(Entropy entropy) -> void {
_entropy = entropy;
seed();
}
auto seed(maybe<uint32> seed = nothing, maybe<uint32> sequence = nothing) -> void {
if(!seed) seed = (uint32)clock();
if(!sequence) sequence = 0;
_state = 0;
_increment = sequence() << 1 | 1;
step();
_state += seed();
step();
}
auto random() -> uint64 {
if(_entropy == Entropy::None) return 0;
return (uint64)step() << 32 | (uint64)step() << 0;
}
auto bias(uint64 bias) -> uint64 {
if(_entropy == Entropy::None) return bias;
return random();
}
auto bound(uint64 bound) -> uint64 {
uint64 threshold = -bound % bound;
while(true) {
uint64 result = random();
if(result >= threshold) return result % bound;
}
}
auto array(uint8* data, uint32 size) -> void {
if(_entropy == Entropy::None) {
memory::fill(data, size);
return;
}
if(_entropy == Entropy::High) {
for(uint32 address : range(size)) {
data[address] = random();
}
return;
}
//Entropy::Low
uint lobit = random() & 3;
uint hibit = (lobit + 8 + (random() & 3)) & 15;
uint lovalue = random() & 255;
uint hivalue = random() & 255;
if((random() & 3) == 0) lovalue = 0;
if((random() & 1) == 0) hivalue = ~lovalue;
for(uint32 address : range(size)) {
uint8 value = (address & 1ull << lobit) ? lovalue : hivalue;
if((address & 1ull << hibit)) value = ~value;
if((random() & 511) == 0) value ^= 1 << (random() & 7);
if((random() & 2047) == 0) value ^= 1 << (random() & 7);
data[address] = value;
}
}
auto serialize(serializer& s) -> void {
s.integer((uint&)_entropy);
s.integer(_state);
s.integer(_increment);
}
private:
auto step() -> uint32 {
uint64 state = _state;
_state = state * 6364136223846793005ull + _increment;
uint32 xorshift = (state >> 18 ^ state) >> 27;
uint32 rotate = state >> 59;
return xorshift >> rotate | xorshift << (-rotate & 31);
}
Entropy _entropy = Entropy::High;
uint64 _state;
uint64 _increment;
};
}

View File

@ -0,0 +1,72 @@
#pragma once
using int1 = nall::Integer< 1>;
using int2 = nall::Integer< 2>;
using int3 = nall::Integer< 3>;
using int4 = nall::Integer< 4>;
using int5 = nall::Integer< 5>;
using int6 = nall::Integer< 6>;
using int7 = nall::Integer< 7>;
using int8 = int8_t;
using int9 = nall::Integer< 9>;
using int10 = nall::Integer<10>;
using int11 = nall::Integer<11>;
using int12 = nall::Integer<12>;
using int13 = nall::Integer<13>;
using int14 = nall::Integer<14>;
using int15 = nall::Integer<15>;
using int16 = int16_t;
using int17 = nall::Integer<17>;
using int18 = nall::Integer<18>;
using int19 = nall::Integer<19>;
using int20 = nall::Integer<20>;
using int21 = nall::Integer<21>;
using int22 = nall::Integer<22>;
using int23 = nall::Integer<23>;
using int24 = nall::Integer<24>;
using int25 = nall::Integer<25>;
using int26 = nall::Integer<26>;
using int27 = nall::Integer<27>;
using int28 = nall::Integer<28>;
using int29 = nall::Integer<29>;
using int30 = nall::Integer<30>;
using int31 = nall::Integer<31>;
using int32 = int32_t;
using int48 = nall::Integer<48>; //Cx4
using int64 = int64_t;
using uint1 = nall::Natural< 1>;
using uint2 = nall::Natural< 2>;
using uint3 = nall::Natural< 3>;
using uint4 = nall::Natural< 4>;
using uint5 = nall::Natural< 5>;
using uint6 = nall::Natural< 6>;
using uint7 = nall::Natural< 7>;
using uint8 = uint8_t;
using uint9 = nall::Natural< 9>;
using uint10 = nall::Natural<10>;
using uint11 = nall::Natural<11>;
using uint12 = nall::Natural<12>;
using uint13 = nall::Natural<13>;
using uint14 = nall::Natural<14>;
using uint15 = nall::Natural<15>;
using uint16 = uint16_t;
using uint17 = nall::Natural<17>;
using uint18 = nall::Natural<18>;
using uint19 = nall::Natural<19>;
using uint20 = nall::Natural<20>;
using uint21 = nall::Natural<21>;
using uint22 = nall::Natural<22>;
using uint23 = nall::Natural<23>;
using uint24 = nall::Natural<24>;
using uint25 = nall::Natural<25>;
using uint26 = nall::Natural<26>;
using uint27 = nall::Natural<27>;
using uint28 = nall::Natural<28>;
using uint29 = nall::Natural<29>;
using uint30 = nall::Natural<30>;
using uint31 = nall::Natural<31>;
using uint32 = uint32_t;
using uint40 = nall::Natural<40>; //SA1
using uint48 = nall::Natural<48>; //Cx4
using uint64 = uint64_t;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,169 @@
#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_length;
uint16_t shadow_sweep_sample_length;
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;
#define GB_SKIP_DIV_EVENT_INACTIVE 0
#define GB_SKIP_DIV_EVENT_SKIPPED 1
#define GB_SKIP_DIV_EVENT_SKIP 2
uint8_t skip_div_event;
bool current_lfsr_sample;
uint8_t pcm_mask[2]; // For CGB-0 to CGB-C PCM read glitch
} GB_apu_t;
typedef enum {
GB_HIGHPASS_OFF, // Do not apply any filter, keep DC offset
GB_HIGHPASS_ACCURATE, // Apply a highpass filter similar to the one used on hardware
GB_HIGHPASS_REMOVE_DC_OFFSET, // Remove DC Offset without affecting the waveform
GB_HIGHPASS_MAX
} GB_highpass_mode_t;
typedef struct {
unsigned sample_rate;
double sample_cycles; // In 8 MHz units
double cycles_per_sample;
// Samples are NOT normalized to MAX_CH_AMP * 4 at this stage!
unsigned cycles_since_render;
unsigned last_update[GB_N_CHANNELS];
GB_sample_t current_sample[GB_N_CHANNELS];
GB_sample_t summed_samples[GB_N_CHANNELS];
double dac_discharge[GB_N_CHANNELS];
GB_highpass_mode_t highpass_mode;
double highpass_rate;
GB_double_sample_t highpass_diff;
GB_sample_callback_t sample_callback;
bool rate_set_in_clocks;
} GB_apu_output_t;
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate);
void GB_set_sample_rate_by_clocks(GB_gameboy_t *gb, double cycles_per_sample); /* Cycles are in 8MHz units */
void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode);
void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback);
#ifdef GB_INTERNAL
bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, unsigned index);
void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value);
uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg);
void GB_apu_div_event(GB_gameboy_t *gb);
void GB_apu_init(GB_gameboy_t *gb);
void GB_apu_run(GB_gameboy_t *gb);
void GB_apu_update_cycles_per_sample(GB_gameboy_t *gb);
void GB_borrow_sgb_border(GB_gameboy_t *gb);
#endif
#endif /* apu_h */

View File

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

View File

@ -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

View File

@ -0,0 +1,313 @@
#include "gb.h"
#include "cheats.h"
#include <stdio.h>
#include <assert.h>
#include <errno.h>
static inline uint8_t hash_addr(uint16_t addr)
{
return addr;
}
static uint16_t bank_for_addr(GB_gameboy_t *gb, uint16_t addr)
{
if (addr < 0x4000) {
return gb->mbc_rom0_bank;
}
if (addr < 0x8000) {
return gb->mbc_rom_bank;
}
if (addr < 0xD000) {
return 0;
}
if (addr < 0xE000) {
return gb->cgb_ram_bank;
}
return 0;
}
void GB_apply_cheat(GB_gameboy_t *gb, uint16_t address, uint8_t *value)
{
if (!gb->cheat_enabled) return;
if (!gb->boot_rom_finished) return;
const GB_cheat_hash_t *hash = gb->cheat_hash[hash_addr(address)];
if (hash) {
for (unsigned i = 0; i < hash->size; i++) {
GB_cheat_t *cheat = hash->cheats[i];
if (cheat->address == address && cheat->enabled && (!cheat->use_old_value || cheat->old_value == *value)) {
if (cheat->bank == GB_CHEAT_ANY_BANK || cheat->bank == bank_for_addr(gb, address)) {
*value = cheat->value;
break;
}
}
}
}
}
bool GB_cheats_enabled(GB_gameboy_t *gb)
{
return gb->cheat_enabled;
}
void GB_set_cheats_enabled(GB_gameboy_t *gb, bool enabled)
{
gb->cheat_enabled = enabled;
}
void GB_add_cheat(GB_gameboy_t *gb, const char *description, uint16_t address, uint16_t bank, uint8_t value, uint8_t old_value, bool use_old_value, bool enabled)
{
GB_cheat_t *cheat = malloc(sizeof(*cheat));
cheat->address = address;
cheat->bank = bank;
cheat->value = value;
cheat->old_value = old_value;
cheat->use_old_value = use_old_value;
cheat->enabled = enabled;
strncpy(cheat->description, description, sizeof(cheat->description));
cheat->description[sizeof(cheat->description) - 1] = 0;
gb->cheats = realloc(gb->cheats, (++gb->cheat_count) * sizeof(*cheat));
gb->cheats[gb->cheat_count - 1] = cheat;
GB_cheat_hash_t **hash = &gb->cheat_hash[hash_addr(address)];
if (!*hash) {
*hash = malloc(sizeof(GB_cheat_hash_t) + sizeof(cheat));
(*hash)->size = 1;
(*hash)->cheats[0] = cheat;
}
else {
(*hash)->size++;
*hash = realloc(*hash, sizeof(GB_cheat_hash_t) + sizeof(cheat) * (*hash)->size);
(*hash)->cheats[(*hash)->size - 1] = cheat;
}
}
const GB_cheat_t *const *GB_get_cheats(GB_gameboy_t *gb, size_t *size)
{
*size = gb->cheat_count;
return (void *)gb->cheats;
}
void GB_remove_cheat(GB_gameboy_t *gb, const GB_cheat_t *cheat)
{
for (unsigned i = 0; i < gb->cheat_count; i++) {
if (gb->cheats[i] == cheat) {
gb->cheats[i] = gb->cheats[--gb->cheat_count];
if (gb->cheat_count == 0) {
free(gb->cheats);
gb->cheats = NULL;
}
else {
gb->cheats = realloc(gb->cheats, gb->cheat_count * sizeof(*cheat));
}
break;
}
}
GB_cheat_hash_t **hash = &gb->cheat_hash[hash_addr(cheat->address)];
for (unsigned i = 0; i < (*hash)->size; i++) {
if ((*hash)->cheats[i] == cheat) {
(*hash)->cheats[i] = (*hash)->cheats[(*hash)->size--];
if ((*hash)->size == 0) {
free(*hash);
*hash = NULL;
}
else {
*hash = malloc(sizeof(GB_cheat_hash_t) + sizeof(cheat) * (*hash)->size);
}
break;
}
}
free((void *)cheat);
}
bool GB_import_cheat(GB_gameboy_t *gb, const char *cheat, const char *description, bool enabled)
{
uint8_t dummy;
/* GameShark */
{
uint8_t bank;
uint8_t value;
uint16_t address;
if (sscanf(cheat, "%02hhx%02hhx%04hx%c", &bank, &value, &address, &dummy) == 3) {
if (bank >= 0x80) {
bank &= 0xF;
}
GB_add_cheat(gb, description, address, bank, value, 0, false, enabled);
return true;
}
}
/* GameGenie */
{
char stripped_cheat[10] = {0,};
for (unsigned i = 0; i < 9 && *cheat; i++) {
stripped_cheat[i] = *(cheat++);
while (*cheat == '-') {
cheat++;
}
}
// Delete the 7th character;
stripped_cheat[7] = stripped_cheat[8];
stripped_cheat[8] = 0;
uint8_t old_value;
uint8_t value;
uint16_t address;
if (sscanf(stripped_cheat, "%02hhx%04hx%02hhx%c", &value, &address, &old_value, &dummy) == 3) {
address = (uint16_t)(address >> 4) | (uint16_t)(address << 12);
address ^= 0xF000;
if (address > 0x7FFF) {
return false;
}
old_value = (uint8_t)(old_value >> 2) | (uint8_t)(old_value << 6);
old_value ^= 0xBA;
GB_add_cheat(gb, description, address, GB_CHEAT_ANY_BANK, value, old_value, true, enabled);
return true;
}
if (sscanf(stripped_cheat, "%02hhx%04hx%c", &value, &address, &dummy) == 2) {
address = (uint16_t)(address >> 4) | (uint16_t)(address << 12);
address ^= 0xF000;
if (address > 0x7FFF) {
return false;
}
GB_add_cheat(gb, description, address, GB_CHEAT_ANY_BANK, value, false, true, enabled);
return true;
}
}
return false;
}
void GB_update_cheat(GB_gameboy_t *gb, const GB_cheat_t *_cheat, const char *description, uint16_t address, uint16_t bank, uint8_t value, uint8_t old_value, bool use_old_value, bool enabled)
{
GB_cheat_t *cheat = NULL;
for (unsigned i = 0; i < gb->cheat_count; i++) {
if (gb->cheats[i] == _cheat) {
cheat = gb->cheats[i];
break;
}
}
assert(cheat);
if (cheat->address != address) {
/* Remove from old bucket */
GB_cheat_hash_t **hash = &gb->cheat_hash[hash_addr(cheat->address)];
for (unsigned i = 0; i < (*hash)->size; i++) {
if ((*hash)->cheats[i] == cheat) {
(*hash)->cheats[i] = (*hash)->cheats[(*hash)->size--];
if ((*hash)->size == 0) {
free(*hash);
*hash = NULL;
}
else {
*hash = malloc(sizeof(GB_cheat_hash_t) + sizeof(cheat) * (*hash)->size);
}
break;
}
}
cheat->address = address;
/* Add to new bucket */
hash = &gb->cheat_hash[hash_addr(address)];
if (!*hash) {
*hash = malloc(sizeof(GB_cheat_hash_t) + sizeof(cheat));
(*hash)->size = 1;
(*hash)->cheats[0] = cheat;
}
else {
(*hash)->size++;
*hash = malloc(sizeof(GB_cheat_hash_t) + sizeof(cheat) * (*hash)->size);
(*hash)->cheats[(*hash)->size - 1] = cheat;
}
}
cheat->bank = bank;
cheat->value = value;
cheat->old_value = old_value;
cheat->use_old_value = use_old_value;
cheat->enabled = enabled;
if (description != cheat->description) {
strncpy(cheat->description, description, sizeof(cheat->description));
cheat->description[sizeof(cheat->description) - 1] = 0;
}
}
#define CHEAT_MAGIC 'SBCh'
void GB_load_cheats(GB_gameboy_t *gb, const char *path)
{
FILE *f = fopen(path, "rb");
if (!f) {
return;
}
uint32_t magic = 0;
uint32_t struct_size = 0;
fread(&magic, sizeof(magic), 1, f);
fread(&struct_size, sizeof(struct_size), 1, f);
if (magic != CHEAT_MAGIC && magic != __builtin_bswap32(CHEAT_MAGIC)) {
GB_log(gb, "The file is not a SameBoy cheat database");
return;
}
if (struct_size != sizeof(GB_cheat_t)) {
GB_log(gb, "This cheat database is not compatible with this version of SameBoy");
return;
}
// Remove all cheats first
while (gb->cheats) {
GB_remove_cheat(gb, gb->cheats[0]);
}
GB_cheat_t cheat;
while (fread(&cheat, sizeof(cheat), 1, f)) {
if (magic == __builtin_bswap32(CHEAT_MAGIC)) {
cheat.address = __builtin_bswap16(cheat.address);
cheat.bank = __builtin_bswap16(cheat.bank);
}
cheat.description[sizeof(cheat.description) - 1] = 0;
GB_add_cheat(gb, cheat.description, cheat.address, cheat.bank, cheat.value, cheat.old_value, cheat.use_old_value, cheat.enabled);
}
return;
}
int GB_save_cheats(GB_gameboy_t *gb, const char *path)
{
if (!gb->cheat_count) return 0; // Nothing to save.
FILE *f = fopen(path, "wb");
if (!f) {
GB_log(gb, "Could not dump cheat database: %s.\n", strerror(errno));
return errno;
}
uint32_t magic = CHEAT_MAGIC;
uint32_t struct_size = sizeof(GB_cheat_t);
if (fwrite(&magic, sizeof(magic), 1, f) != 1) {
fclose(f);
return EIO;
}
if (fwrite(&struct_size, sizeof(struct_size), 1, f) != 1) {
fclose(f);
return EIO;
}
for (size_t i = 0; i <gb->cheat_count; i++) {
if (fwrite(gb->cheats[i], sizeof(*gb->cheats[i]), 1, f) != 1) {
fclose(f);
return EIO;
}
}
errno = 0;
fclose(f);
return errno;
}

View File

@ -0,0 +1,42 @@
#ifndef cheats_h
#define cheats_h
#include "gb_struct_def.h"
#define GB_CHEAT_ANY_BANK 0xFFFF
typedef struct GB_cheat_s GB_cheat_t;
void GB_add_cheat(GB_gameboy_t *gb, const char *description, uint16_t address, uint16_t bank, uint8_t value, uint8_t old_value, bool use_old_value, bool enabled);
void GB_update_cheat(GB_gameboy_t *gb, const GB_cheat_t *cheat, const char *description, uint16_t address, uint16_t bank, uint8_t value, uint8_t old_value, bool use_old_value, bool enabled);
bool GB_import_cheat(GB_gameboy_t *gb, const char *cheat, const char *description, bool enabled);
const GB_cheat_t *const *GB_get_cheats(GB_gameboy_t *gb, size_t *size);
void GB_remove_cheat(GB_gameboy_t *gb, const GB_cheat_t *cheat);
bool GB_cheats_enabled(GB_gameboy_t *gb);
void GB_set_cheats_enabled(GB_gameboy_t *gb, bool enabled);
void GB_load_cheats(GB_gameboy_t *gb, const char *path);
int GB_save_cheats(GB_gameboy_t *gb, const char *path);
#ifdef GB_INTERNAL
#ifdef GB_DISABLE_CHEATS
#define GB_apply_cheat(...)
#else
void GB_apply_cheat(GB_gameboy_t *gb, uint16_t address, uint8_t *value);
#endif
#endif
typedef struct {
size_t size;
GB_cheat_t *cheats[];
} GB_cheat_hash_t;
struct GB_cheat_s {
uint16_t address;
uint16_t bank;
uint8_t value;
uint8_t old_value;
bool use_old_value;
bool enabled;
char description[128];
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,46 @@
#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 GB_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
#define GB_debugger_add_symbol(gb, bank, address, symbol) ((void)bank, (void)address, (void)symbol)
#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);
void GB_debugger_add_symbol(GB_gameboy_t *gb, uint16_t bank, uint16_t address, const char *symbol);
#endif /* GB_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. */
char *GB_debugger_complete_substring(GB_gameboy_t *gb, char *input, uintptr_t *context); /* Destroys input, result requires free */
void GB_debugger_load_symbol_file(GB_gameboy_t *gb, const char *path);
const char *GB_debugger_name_for_address(GB_gameboy_t *gb, uint16_t addr);
bool GB_debugger_evaluate(GB_gameboy_t *gb, const char *string, uint16_t *result, uint16_t *result_bank); /* result_bank is -1 if unused. */
void GB_debugger_break(GB_gameboy_t *gb);
bool GB_debugger_is_stopped(GB_gameboy_t *gb);
void GB_debugger_set_disabled(GB_gameboy_t *gb, bool disabled);
void GB_debugger_clear_symbols(GB_gameboy_t *gb);
#endif /* debugger_h */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,62 @@
#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_STAT_update(GB_gameboy_t *gb);
void GB_lcd_off(GB_gameboy_t *gb);
enum {
GB_OBJECT_PRIORITY_UNDEFINED, // For save state compatibility
GB_OBJECT_PRIORITY_X,
GB_OBJECT_PRIORITY_INDEX,
};
#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_REDUCE_CONTRAST,
} 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, bool for_border);
void GB_set_color_correction_mode(GB_gameboy_t *gb, GB_color_correction_mode_t mode);
bool GB_is_odd_frame(GB_gameboy_t *gb);
#endif /* display_h */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,816 @@
#ifndef GB_h
#define GB_h
#define typeof __typeof__
#include <stdbool.h>
#include <stdint.h>
#include <time.h>
#include "gb_struct_def.h"
#include "save_state.h"
#include "apu.h"
#include "camera.h"
#include "debugger.h"
#include "display.h"
#include "joypad.h"
#include "mbc.h"
#include "memory.h"
#include "printer.h"
#include "timing.h"
#include "rewind.h"
#include "sm83_cpu.h"
#include "symbol_hash.h"
#include "sgb.h"
#include "cheats.h"
#include "rumble.h"
#include "workboy.h"
#define GB_STRUCT_VERSION 13
#define GB_MODEL_FAMILY_MASK 0xF00
#define GB_MODEL_DMG_FAMILY 0x000
#define GB_MODEL_MGB_FAMILY 0x100
#define GB_MODEL_CGB_FAMILY 0x200
#define GB_MODEL_PAL_BIT 0x1000
#define GB_MODEL_NO_SFC_BIT 0x2000
#ifdef GB_INTERNAL
#if __clang__
#define UNROLL _Pragma("unroll")
#elif __GNUC__ >= 8
#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
#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8)
#define __builtin_bswap16(x) ({ typeof(x) _x = (x); _x >> 8 | _x << 8; })
#endif
typedef struct {
struct {
uint8_t r, g, b;
} colors[5];
} GB_palette_t;
extern const GB_palette_t GB_PALETTE_GREY;
extern const GB_palette_t GB_PALETTE_DMG;
extern const GB_palette_t GB_PALETTE_MGB;
extern const GB_palette_t GB_PALETTE_GBL;
typedef union {
struct {
uint8_t seconds;
uint8_t minutes;
uint8_t hours;
uint8_t days;
uint8_t high;
};
uint8_t data[5];
} GB_rtc_time_t;
typedef enum {
// GB_MODEL_DMG_0 = 0x000,
// GB_MODEL_DMG_A = 0x001,
GB_MODEL_DMG_B = 0x002,
// GB_MODEL_DMG_C = 0x003,
GB_MODEL_SGB = 0x004,
GB_MODEL_SGB_NTSC = GB_MODEL_SGB,
GB_MODEL_SGB_PAL = GB_MODEL_SGB | GB_MODEL_PAL_BIT,
GB_MODEL_SGB_NTSC_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT,
GB_MODEL_SGB_NO_SFC = GB_MODEL_SGB_NTSC_NO_SFC,
GB_MODEL_SGB_PAL_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT | GB_MODEL_PAL_BIT,
// GB_MODEL_MGB = 0x100,
GB_MODEL_SGB2 = 0x101,
GB_MODEL_SGB2_NO_SFC = GB_MODEL_SGB2 | GB_MODEL_NO_SFC_BIT,
// GB_MODEL_CGB_0 = 0x200,
// GB_MODEL_CGB_A = 0x201,
// GB_MODEL_CGB_B = 0x202,
GB_MODEL_CGB_C = 0x203,
// GB_MODEL_CGB_D = 0x204,
GB_MODEL_CGB_E = 0x205,
GB_MODEL_AGB = 0x206,
} GB_model_t;
enum {
GB_REGISTER_AF,
GB_REGISTER_BC,
GB_REGISTER_DE,
GB_REGISTER_HL,
GB_REGISTER_SP,
GB_REGISTERS_16_BIT /* Count */
};
/* Todo: Actually use these! */
enum {
GB_CARRY_FLAG = 16,
GB_HALF_CARRY_FLAG = 32,
GB_SUBTRACT_FLAG = 64,
GB_ZERO_FLAG = 128,
};
typedef enum {
GB_BORDER_SGB,
GB_BORDER_NEVER,
GB_BORDER_ALWAYS,
} GB_border_mode_t;
#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_KEY0 = 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_BANK = 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
GB_IO_OPRI = 0x6c, // Affects object priority (X based or index based)
/* 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;
typedef enum {
GB_BOOT_ROM_DMG0,
GB_BOOT_ROM_DMG,
GB_BOOT_ROM_MGB,
GB_BOOT_ROM_SGB,
GB_BOOT_ROM_SGB2,
GB_BOOT_ROM_CGB0,
GB_BOOT_ROM_CGB,
GB_BOOT_ROM_AGB,
} GB_boot_rom_t;
#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, uint64_t cycles_since_last_update);
typedef void (*GB_rumble_callback_t)(GB_gameboy_t *gb, double rumble_amplitude);
typedef void (*GB_serial_transfer_bit_start_callback_t)(GB_gameboy_t *gb, bool bit_to_send);
typedef bool (*GB_serial_transfer_bit_end_callback_t)(GB_gameboy_t *gb);
typedef void (*GB_update_input_hint_callback_t)(GB_gameboy_t *gb);
typedef void (*GB_joyp_write_callback_t)(GB_gameboy_t *gb, uint8_t value);
typedef void (*GB_icd_pixel_callback_t)(GB_gameboy_t *gb, uint8_t row);
typedef void (*GB_icd_hreset_callback_t)(GB_gameboy_t *gb);
typedef void (*GB_icd_vreset_callback_t)(GB_gameboy_t *gb);
typedef void (*GB_boot_rom_load_callback_t)(GB_gameboy_t *gb, GB_boot_rom_t type);
typedef struct {
bool state;
uint64_t 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. */
#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
GB_workboy_t workboy;
);
/* 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:8;
uint8_t ram_bank:3;
} 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;
bool mode:1;
bool ir_mode:1;
} huc1;
struct {
uint8_t rom_bank:7;
uint8_t padding:1;
uint8_t ram_bank:4;
} huc3;
};
uint16_t mbc_rom0_bank; /* For some MBC1 wirings. */
bool camera_registers_mapped;
uint8_t camera_registers[0x36];
bool rumble_state;
bool cart_ir;
// TODO: move to huc3/mbc3 struct when breaking save compat
uint8_t huc3_mode;
uint8_t huc3_access_index;
uint16_t huc3_minutes, huc3_days;
uint16_t huc3_alarm_minutes, huc3_alarm_days;
bool huc3_alarm_enabled;
uint8_t huc3_read;
uint8_t huc3_access_flags;
bool mbc3_rtc_mapped;
);
/* 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 window_y;
/* 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 fifo_insertion_glitch;
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 window_is_being_fetched;
bool wx166_glitch;
bool wx_triggered;
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;
uint8_t object_priority;
bool oam_ppu_blocked;
bool vram_ppu_blocked;
bool cgb_palettes_ppu_blocked;
bool object_fetch_aborted;
bool during_object_fetch;
uint16_t object_low_line_address;
bool wy_triggered;
uint8_t window_tile_x;
uint8_t lcd_x; // The LCD can go out of sync since the push signal is skipped in some cases.
bool is_odd_frame;
uint16_t last_tile_data_address;
uint16_t last_tile_index_address;
bool cgb_repeated_a_frame;
);
/* 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;
bool is_mbc30;
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];
const GB_palette_t *dmg_palette;
GB_color_correction_mode_t color_correction_mode;
bool keys[4][GB_KEY_MAX];
GB_border_mode_t border_mode;
GB_sgb_border_t borrowed_border;
bool tried_loading_sgb_border;
bool has_sgb_border;
/* Timing */
uint64_t last_sync;
uint64_t cycles_since_last_sync; // In 8MHz units
/* Audio */
GB_apu_output_t apu_output;
/* Callbacks */
void *user_data;
GB_log_callback_t log_callback;
GB_input_callback_t input_callback;
GB_input_callback_t async_input_callback;
GB_rgb_encode_callback_t rgb_encode_callback;
GB_vblank_callback_t vblank_callback;
GB_infrared_callback_t infrared_callback;
GB_camera_get_pixel_callback_t camera_get_pixel_callback;
GB_camera_update_request_callback_t camera_update_request_callback;
GB_rumble_callback_t rumble_callback;
GB_serial_transfer_bit_start_callback_t serial_transfer_bit_start_callback;
GB_serial_transfer_bit_end_callback_t serial_transfer_bit_end_callback;
GB_update_input_hint_callback_t update_input_hint_callback;
GB_joyp_write_callback_t joyp_write_callback;
GB_icd_pixel_callback_t icd_pixel_callback;
GB_icd_vreset_callback_t icd_hreset_callback;
GB_icd_vreset_callback_t icd_vreset_callback;
GB_read_memory_callback_t read_memory_callback;
GB_boot_rom_load_callback_t boot_rom_load_callback;
GB_print_image_callback_t printer_callback;
GB_workboy_set_time_callback workboy_set_time_callback;
GB_workboy_get_time_callback workboy_get_time_callback;
/* IR */
uint64_t cycles_since_ir_change; // In 8MHz units
uint64_t 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, has_software_breakpoints;
void *nontrivial_jump_state;
bool non_trivial_jump_breakpoint_occured;
/* SLD (Todo: merge with backtrace) */
bool stack_leak_detection;
signed 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 */
uint64_t 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;
/* Cheats */
bool cheat_enabled;
size_t cheat_count;
GB_cheat_t **cheats;
GB_cheat_hash_t *cheat_hash[256];
/* 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;
GB_rumble_mode_t rumble_mode;
uint32_t rumble_on_cycles;
uint32_t rumble_off_cycles;
/* Temporary state */
bool wx_just_changed;
);
};
#ifndef GB_INTERNAL
struct GB_gameboy_s {
char __internal[sizeof(struct GB_gameboy_internal_s)];
};
#endif
#ifndef __printflike
/* Missing from Linux headers. */
#define __printflike(fmtarg, firstvararg) \
__attribute__((__format__ (__printf__, fmtarg, firstvararg)))
#endif
void GB_init(GB_gameboy_t *gb, GB_model_t model);
bool GB_is_inited(GB_gameboy_t *gb);
bool GB_is_cgb(GB_gameboy_t *gb);
bool GB_is_sgb(GB_gameboy_t *gb); // Returns true if the model is SGB or SGB2
bool GB_is_hle_sgb(GB_gameboy_t *gb); // Returns true if the model is SGB or SGB2 and the SFC/SNES side is HLE'd
GB_model_t GB_get_model(GB_gameboy_t *gb);
void GB_free(GB_gameboy_t *gb);
void GB_reset(GB_gameboy_t *gb);
void GB_switch_model_and_reset(GB_gameboy_t *gb, GB_model_t model);
/* Returns the time passed, in 8MHz ticks. */
uint8_t GB_run(GB_gameboy_t *gb);
/* Returns the time passed since the last frame, in nanoseconds */
uint64_t GB_run_frame(GB_gameboy_t *gb);
typedef enum {
GB_DIRECT_ACCESS_ROM,
GB_DIRECT_ACCESS_RAM,
GB_DIRECT_ACCESS_CART_RAM,
GB_DIRECT_ACCESS_VRAM,
GB_DIRECT_ACCESS_HRAM,
GB_DIRECT_ACCESS_IO, /* Warning: Some registers can only be read/written correctly via GB_memory_read/write. */
GB_DIRECT_ACCESS_BOOTROM,
GB_DIRECT_ACCESS_OAM,
GB_DIRECT_ACCESS_BGP,
GB_DIRECT_ACCESS_OBP,
GB_DIRECT_ACCESS_IE,
} GB_direct_access_t;
/* Returns a mutable pointer to various hardware memories. If that memory is banked, the current bank
is returned at *bank, even if only a portion of the memory is banked. */
void *GB_get_direct_access(GB_gameboy_t *gb, GB_direct_access_t access, size_t *size, uint16_t *bank);
void *GB_get_user_data(GB_gameboy_t *gb);
void GB_set_user_data(GB_gameboy_t *gb, void *data);
int GB_load_boot_rom(GB_gameboy_t *gb, const char *path);
void GB_load_boot_rom_from_buffer(GB_gameboy_t *gb, const unsigned char *buffer, size_t size);
int GB_load_rom(GB_gameboy_t *gb, const char *path);
void GB_load_rom_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size);
int GB_load_isx(GB_gameboy_t *gb, const char *path);
int GB_save_battery_size(GB_gameboy_t *gb);
int GB_save_battery_to_buffer(GB_gameboy_t *gb, uint8_t *buffer, size_t size);
int GB_save_battery(GB_gameboy_t *gb, const char *path);
void GB_load_battery_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size);
void GB_load_battery(GB_gameboy_t *gb, const char *path);
void GB_set_turbo_mode(GB_gameboy_t *gb, bool on, bool no_frame_skip);
void GB_set_rendering_disabled(GB_gameboy_t *gb, bool disabled);
void GB_log(GB_gameboy_t *gb, const char *fmt, ...) __printflike(2, 3);
void GB_attributed_log(GB_gameboy_t *gb, GB_log_attributes attributes, const char *fmt, ...) __printflike(3, 4);
void GB_set_pixels_output(GB_gameboy_t *gb, uint32_t *output);
void GB_set_border_mode(GB_gameboy_t *gb, GB_border_mode_t border_mode);
void GB_set_infrared_input(GB_gameboy_t *gb, bool state);
void GB_queue_infrared_input(GB_gameboy_t *gb, bool state, uint64_t 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);
/* Called when a new boot ROM is needed. The callback should call GB_load_boot_rom or GB_load_boot_rom_from_buffer */
void GB_set_boot_rom_load_callback(GB_gameboy_t *gb, GB_boot_rom_load_callback_t callback);
void GB_set_palette(GB_gameboy_t *gb, const GB_palette_t *palette);
/* These APIs are used when using internal clock */
void GB_set_serial_transfer_bit_start_callback(GB_gameboy_t *gb, GB_serial_transfer_bit_start_callback_t callback);
void GB_set_serial_transfer_bit_end_callback(GB_gameboy_t *gb, GB_serial_transfer_bit_end_callback_t callback);
/* These APIs are used when using external clock */
bool GB_serial_get_data_bit(GB_gameboy_t *gb);
void GB_serial_set_data_bit(GB_gameboy_t *gb, bool data);
void GB_disconnect_serial(GB_gameboy_t *gb);
/* For cartridges with an alarm clock */
unsigned GB_time_to_alarm(GB_gameboy_t *gb); // 0 if no alarm
/* For integration with SFC/SNES emulators */
void GB_set_joyp_write_callback(GB_gameboy_t *gb, GB_joyp_write_callback_t callback);
void GB_set_icd_pixel_callback(GB_gameboy_t *gb, GB_icd_pixel_callback_t callback);
void GB_set_icd_hreset_callback(GB_gameboy_t *gb, GB_icd_hreset_callback_t callback);
void GB_set_icd_vreset_callback(GB_gameboy_t *gb, GB_icd_vreset_callback_t callback);
#ifdef GB_INTERNAL
uint32_t GB_get_clock_rate(GB_gameboy_t *gb);
#endif
void GB_set_clock_multiplier(GB_gameboy_t *gb, double multiplier);
unsigned GB_get_screen_width(GB_gameboy_t *gb);
unsigned GB_get_screen_height(GB_gameboy_t *gb);
double GB_get_usual_frame_rate(GB_gameboy_t *gb);
unsigned GB_get_player_count(GB_gameboy_t *gb);
#endif /* GB_h */

View File

@ -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

View File

@ -0,0 +1,522 @@
static const uint16_t palette[] = {
0x410A, 0x0421, 0x35AD, 0x4A52, 0x7FFF, 0x2D49, 0x0C42, 0x1484,
0x18A5, 0x20C6, 0x6718, 0x5D6E, 0x0000, 0x0000, 0x0000, 0x0000,
};
static const uint16_t tilemap[] = {
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0001, 0x0002, 0x0003, 0x0003, 0x0003, 0x0004, 0x0004,
0x0004, 0x0004, 0x0004, 0x0004, 0x0005, 0x0005, 0x0005, 0x0005,
0x0005, 0x0005, 0x0005, 0x0005, 0x0004, 0x0004, 0x0004, 0x0004,
0x0004, 0x0004, 0x0003, 0x0003, 0x0003, 0x4002, 0x4001, 0x0000,
0x0000, 0x0006, 0x0007, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008,
0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008,
0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008,
0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x4007, 0x4006, 0x0000,
0x0000, 0x0009, 0x0008, 0x0008, 0x0008, 0x000A, 0x000B, 0x000B,
0x000B, 0x000B, 0x000B, 0x000B, 0x000B, 0x000B, 0x000B, 0x000B,
0x000B, 0x000B, 0x000B, 0x000B, 0x000B, 0x000B, 0x000B, 0x000B,
0x000B, 0x000B, 0x400A, 0x0008, 0x0008, 0x0008, 0xC009, 0x0000,
0x0000, 0x000C, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0x400C, 0x0000,
0x0000, 0x000E, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0xC00E, 0x0000,
0x0000, 0x000F, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0x400F, 0x0000,
0x0000, 0x0010, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0xC010, 0x0000,
0x0000, 0x0010, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0xC010, 0x0000,
0x0000, 0x0011, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0xC011, 0x0000,
0x0000, 0x0011, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0xC011, 0x0000,
0x0000, 0x0012, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0x4012, 0x0000,
0x0000, 0x0013, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0xC013, 0x0000,
0x0014, 0x0015, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0x4015, 0x4014,
0x0016, 0x0017, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0xC017, 0xC016,
0x0016, 0x0017, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0xC017, 0xC016,
0x0018, 0x0019, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0x4019, 0x4018,
0x001A, 0x001B, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0xC01B, 0xC01A,
0x001C, 0x001D, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0x401D, 0x401C,
0x001E, 0x0008, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0x0008, 0xC01E,
0x001E, 0x0008, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0x0008, 0xC01E,
0x001E, 0x0008, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0x0008, 0xC01E,
0x001F, 0x801D, 0x0008, 0x0008, 0x0008, 0x0020, 0x0021, 0x0022,
0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A,
0x002B, 0x002C, 0x002D, 0x002D, 0x002D, 0x002D, 0x002D, 0x002D,
0x002E, 0x0021, 0x4020, 0x0008, 0x0008, 0x0008, 0xC01D, 0x401F,
0x002F, 0x0030, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0031,
0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039,
0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, 0x0040, 0x0041,
0x0042, 0x0043, 0x0008, 0x0008, 0x0008, 0x0008, 0x4030, 0x402F,
0x0044, 0x0045, 0x0046, 0x0047, 0x0008, 0x0008, 0x0048, 0x0049,
0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051,
0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059,
0x005A, 0x005B, 0x0008, 0x0008, 0x4047, 0x4046, 0x4045, 0x4044,
0x0000, 0x0000, 0x005C, 0x005D, 0x005E, 0x005F, 0x0060, 0x0061,
0x0061, 0x0062, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063,
0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x4062, 0x0061,
0x0061, 0x4060, 0x405F, 0x405E, 0x405D, 0x405C, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
};
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1F, 0x01,
0x7F, 0x1F, 0xFF, 0x7E, 0xFF, 0xE1, 0xFF, 0x9F,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x1E,
0x1F, 0x60, 0x7F, 0x80, 0xFF, 0x00, 0xBF, 0x40,
0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x03, 0x01, 0x07, 0x03, 0x07, 0x03, 0x07, 0x06,
0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06,
0x01, 0x02, 0x03, 0x04, 0x03, 0x04, 0x06, 0x01,
0x06, 0x01, 0x06, 0x01, 0x06, 0x01, 0x06, 0x01,
0xFF, 0xBF, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xBF, 0x40, 0x7F, 0x80, 0x7F, 0x80, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x0F, 0x0E, 0x0F, 0x0E, 0x0F, 0x0E, 0x0F, 0x0E,
0x0F, 0x0E, 0x0F, 0x0E, 0x0F, 0x0E, 0x0F, 0x0E,
0x0E, 0x01, 0x0E, 0x01, 0x0E, 0x01, 0x0E, 0x01,
0x0E, 0x01, 0x0E, 0x01, 0x0E, 0x01, 0x0E, 0x01,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFE, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00,
0x0F, 0x0E, 0x0F, 0x0E, 0x1F, 0x1B, 0x1F, 0x1B,
0x1F, 0x1B, 0x1F, 0x1B, 0x1F, 0x1B, 0x1F, 0x1B,
0x0E, 0x01, 0x0E, 0x01, 0x1B, 0x04, 0x1B, 0x04,
0x1B, 0x04, 0x1B, 0x04, 0x1B, 0x04, 0x1B, 0x04,
0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE,
0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE,
0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00,
0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00,
0x1F, 0x1B, 0x1F, 0x1B, 0x1F, 0x1B, 0x1F, 0x1B,
0x1F, 0x1B, 0x1F, 0x1B, 0x1F, 0x1B, 0x1F, 0x1B,
0x1B, 0x04, 0x1B, 0x04, 0x1B, 0x04, 0x1B, 0x04,
0x1B, 0x04, 0x1B, 0x04, 0x1B, 0x04, 0x1B, 0x04,
0x1F, 0x1B, 0x1F, 0x1B, 0x1F, 0x1B, 0x1F, 0x1B,
0x1F, 0x1B, 0x3F, 0x37, 0x3F, 0x37, 0x3F, 0x37,
0x1B, 0x04, 0x1B, 0x04, 0x1B, 0x04, 0x1B, 0x04,
0x1B, 0x04, 0x37, 0x08, 0x37, 0x08, 0x37, 0x08,
0x3F, 0x37, 0x3F, 0x37, 0x3F, 0x37, 0x3F, 0x37,
0x3F, 0x37, 0x3F, 0x37, 0x3F, 0x37, 0x3F, 0x37,
0x37, 0x08, 0x37, 0x08, 0x37, 0x08, 0x37, 0x08,
0x37, 0x08, 0x37, 0x08, 0x37, 0x08, 0x37, 0x08,
0x7F, 0x6F, 0x7F, 0x6F, 0x7F, 0x6F, 0x7F, 0x6F,
0x7F, 0x6F, 0x7F, 0x6F, 0x7F, 0x6F, 0x7F, 0x6F,
0x6F, 0x10, 0x6F, 0x10, 0x6F, 0x10, 0x6F, 0x10,
0x6F, 0x10, 0x6F, 0x10, 0x6F, 0x10, 0x6F, 0x10,
0x7F, 0x6F, 0x7F, 0x6F, 0x7F, 0x6F, 0xFF, 0xDF,
0xFF, 0xDF, 0xFF, 0xDF, 0xFF, 0xDF, 0xFF, 0xDF,
0x6F, 0x10, 0x6F, 0x10, 0x6F, 0x10, 0xDF, 0x20,
0xDF, 0x20, 0xDF, 0x20, 0xDF, 0x20, 0xDF, 0x20,
0xFF, 0xDF, 0xFF, 0xDF, 0xFF, 0xDF, 0xFF, 0xDF,
0xFF, 0xDF, 0xFF, 0xDF, 0xFF, 0xDF, 0xFF, 0xDF,
0xDF, 0x20, 0xDF, 0x20, 0xDF, 0x20, 0xDF, 0x20,
0xDF, 0x20, 0xDF, 0x20, 0xDF, 0x20, 0xDF, 0x20,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
0xFF, 0xDF, 0xFF, 0xDF, 0xFF, 0xDF, 0xFF, 0xDF,
0xFF, 0xDF, 0xFF, 0xDF, 0xFF, 0xBF, 0xFF, 0xBF,
0xDF, 0x20, 0xDF, 0x20, 0xDF, 0x20, 0xDF, 0x20,
0xDF, 0x20, 0xDF, 0x20, 0xBF, 0x40, 0xBF, 0x40,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xBF, 0x40, 0xBF, 0x40, 0xBF, 0x40, 0xBF, 0x40,
0xBF, 0x40, 0xBF, 0x40, 0xBF, 0x40, 0xBF, 0x40,
0x01, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x01, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00,
0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00,
0xFF, 0xBF, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F,
0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F,
0xBF, 0x40, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00,
0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00,
0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F,
0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06,
0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00,
0x06, 0x01, 0x06, 0x01, 0x06, 0x01, 0x06, 0x01,
0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06,
0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06,
0x06, 0x01, 0x06, 0x01, 0x06, 0x01, 0x06, 0x01,
0x06, 0x01, 0x06, 0x01, 0x06, 0x01, 0x06, 0x01,
0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06,
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
0x06, 0x01, 0x06, 0x01, 0x06, 0x01, 0x06, 0x01,
0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04,
0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFE, 0x01, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xF8, 0xFF, 0xE7, 0xF8, 0xDF, 0xE3,
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xF8, 0x00, 0xE4, 0x00,
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x3F, 0xFF, 0xCE, 0x3F, 0xF5, 0x8E,
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0x3F, 0x00, 0x4E, 0x00,
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x1F, 0xFF, 0xEE, 0x1F, 0xB5, 0x4E,
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0x1F, 0x00, 0x0E, 0xA0,
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x07, 0xFF, 0xFB, 0x07, 0x04, 0x73,
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0x07, 0x00, 0x03, 0x88,
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x80, 0xFF, 0x7F, 0x80, 0x82, 0x39,
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0x80, 0x00, 0x01, 0x44,
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x01, 0xFE,
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01,
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x83, 0x7C,
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x42, 0x01,
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFE, 0xFF, 0xBB, 0x7C, 0x4F, 0xB0,
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0x7C, 0x00, 0xB1, 0x00,
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x07, 0xFF, 0xF9, 0x06, 0xE7, 0xF8,
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0x06, 0x00, 0x08, 0x00,
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x0E, 0xFF, 0xF5, 0x0E, 0x9B, 0x74,
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0x0E, 0x00, 0x94, 0x00,
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x1F, 0xFF, 0xF7, 0x0F, 0xBF, 0x47,
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0x0F, 0x00, 0xA7, 0x00,
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xFF,
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
0x07, 0x07, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01,
0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04,
0x03, 0x04, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01,
0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F,
0xFF, 0x7F, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xDF,
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
0x7F, 0x80, 0xBF, 0x40, 0xBF, 0x40, 0xDF, 0x20,
0xB0, 0xD8, 0xA0, 0xD3, 0x67, 0x84, 0x47, 0xA4,
0x61, 0x81, 0xA0, 0xD0, 0xB4, 0xCA, 0x7E, 0x81,
0xD7, 0x08, 0xCC, 0x13, 0x98, 0x20, 0x98, 0x00,
0x9E, 0x20, 0xCF, 0x00, 0xCD, 0x02, 0x80, 0x01,
0x32, 0x2D, 0x13, 0x6D, 0x34, 0x48, 0xFC, 0x02,
0x7C, 0x00, 0x78, 0x05, 0x30, 0x49, 0x20, 0x50,
0xCD, 0x00, 0xAC, 0x40, 0x49, 0x82, 0x01, 0x02,
0x07, 0x80, 0xC2, 0x05, 0x86, 0x41, 0x9F, 0x40,
0x15, 0x2E, 0x09, 0x06, 0x09, 0x16, 0x0B, 0xD4,
0xC6, 0x49, 0x8E, 0x40, 0xCF, 0xC8, 0x06, 0x01,
0xCE, 0x20, 0xE6, 0x10, 0xE6, 0x00, 0x24, 0xD0,
0x39, 0x80, 0x38, 0x01, 0x31, 0x00, 0xF8, 0x00,
0x0C, 0x8B, 0x85, 0x8A, 0x03, 0x84, 0x27, 0x20,
0x22, 0x35, 0x12, 0x34, 0x20, 0x12, 0x10, 0x20,
0x73, 0x00, 0x72, 0x08, 0x7C, 0x80, 0xDC, 0x01,
0xC8, 0x11, 0xC9, 0x06, 0xCD, 0x22, 0xEF, 0x10,
0x83, 0x44, 0x86, 0x01, 0x03, 0x85, 0x26, 0x21,
0x46, 0x69, 0x46, 0x68, 0x8E, 0xCA, 0x86, 0x88,
0x39, 0x40, 0x78, 0x84, 0x7C, 0x80, 0xD8, 0x01,
0x90, 0x29, 0xD1, 0x28, 0x73, 0x00, 0xB3, 0x40,
0x00, 0x01, 0x01, 0x00, 0x3F, 0x00, 0x3F, 0x40,
0x03, 0x02, 0x01, 0x02, 0x41, 0x7C, 0x7F, 0x00,
0xFE, 0x00, 0xFF, 0x00, 0xC0, 0x00, 0x80, 0x40,
0xFC, 0x00, 0xFC, 0x00, 0x80, 0x02, 0xC0, 0x00,
0xC0, 0x00, 0x80, 0x4C, 0xCC, 0x43, 0x8E, 0x52,
0x80, 0x4C, 0x80, 0x00, 0x12, 0x1E, 0x9E, 0x00,
0x7F, 0x00, 0x33, 0x0C, 0x32, 0x01, 0x23, 0x50,
0x33, 0x4C, 0x7F, 0x00, 0x61, 0x80, 0xF1, 0x00,
0x7C, 0x02, 0x30, 0x48, 0x31, 0x40, 0x61, 0x50,
0x87, 0xE4, 0xE3, 0x84, 0x23, 0x44, 0x43, 0x44,
0x85, 0x42, 0x87, 0x40, 0x8F, 0x50, 0x8C, 0x12,
0x78, 0x00, 0x18, 0x20, 0xB8, 0x00, 0x98, 0x24,
0x03, 0x04, 0x03, 0xE0, 0xF1, 0x12, 0xF0, 0x09,
0xF9, 0x09, 0xF9, 0x08, 0xE1, 0x12, 0xF1, 0x12,
0xF8, 0x00, 0x1E, 0xE0, 0x0C, 0x02, 0x07, 0x08,
0x07, 0x00, 0x06, 0x00, 0x1C, 0x02, 0x0C, 0x00,
0x9F, 0x91, 0x86, 0x88, 0xC4, 0x4C, 0x80, 0x4C,
0xE1, 0x20, 0xC1, 0x22, 0x23, 0xD4, 0x22, 0xD5,
0x60, 0x00, 0xFB, 0x00, 0x37, 0x00, 0x73, 0x0C,
0x1F, 0x00, 0x3C, 0x00, 0xC8, 0x14, 0xC9, 0x14,
0x16, 0x2F, 0x76, 0x4F, 0x2D, 0xDE, 0xDD, 0xBE,
0xBA, 0x7D, 0x7A, 0xFD, 0x7A, 0xFD, 0xF4, 0xF8,
0xCF, 0x00, 0x8F, 0x00, 0x5E, 0x80, 0xBE, 0x00,
0x7D, 0x00, 0xFC, 0x00, 0xFC, 0x01, 0xF9, 0x02,
0xFF, 0x00, 0xBF, 0x78, 0x86, 0x09, 0x86, 0x89,
0x06, 0x25, 0x02, 0x25, 0x42, 0x45, 0x60, 0x11,
0x00, 0x00, 0x09, 0x00, 0x70, 0x81, 0x70, 0x09,
0xDC, 0x21, 0xD8, 0x01, 0x98, 0x25, 0xCC, 0x13,
0xFF, 0x00, 0xF3, 0xF8, 0x02, 0x03, 0x01, 0x30,
0x39, 0x09, 0x30, 0x09, 0x31, 0x09, 0x20, 0x19,
0x00, 0x00, 0x01, 0x04, 0xFC, 0x00, 0xCF, 0x30,
0xE6, 0x00, 0xE6, 0x01, 0xE6, 0x00, 0xF6, 0x08,
0xFF, 0x00, 0xFA, 0xC7, 0x18, 0x21, 0x09, 0x10,
0x88, 0x99, 0x93, 0x1A, 0x83, 0x11, 0xC2, 0x41,
0x00, 0x00, 0x00, 0x20, 0xC6, 0x21, 0xFF, 0x00,
0x67, 0x00, 0xE4, 0x08, 0x6F, 0x10, 0x3C, 0x00,
0xFD, 0x02, 0xB5, 0x3A, 0xC7, 0x44, 0x03, 0x84,
0x83, 0x24, 0x21, 0xB0, 0x21, 0x12, 0x21, 0x02,
0x02, 0x00, 0x02, 0x40, 0x3C, 0x00, 0xF8, 0x00,
0xD8, 0x24, 0x4C, 0x92, 0xEC, 0x00, 0xCC, 0x12,
0xFF, 0x00, 0xFF, 0xF3, 0x1C, 0x14, 0x0C, 0x04,
0x00, 0x0C, 0x04, 0x24, 0x00, 0x24, 0x10, 0x30,
0x00, 0x00, 0x10, 0x04, 0xE3, 0x00, 0xFB, 0x00,
0xF3, 0x08, 0xDB, 0x20, 0xDB, 0x04, 0xCF, 0x00,
0xFF, 0x00, 0xEC, 0x3E, 0xC1, 0x01, 0x01, 0x8E,
0x8F, 0x10, 0x0F, 0x90, 0x0F, 0x90, 0x0D, 0x09,
0x00, 0x00, 0x20, 0x01, 0x7E, 0x00, 0xF1, 0x0E,
0xE0, 0x10, 0x60, 0x10, 0x60, 0x10, 0x79, 0x82,
0xFF, 0x00, 0x7F, 0xFC, 0x03, 0x82, 0x01, 0x9E,
0x13, 0x80, 0x03, 0x80, 0x03, 0x9C, 0x0F, 0x90,
0x00, 0x00, 0x02, 0x00, 0x7C, 0x80, 0x60, 0x9C,
0x60, 0x9C, 0x7C, 0x80, 0x60, 0x9C, 0x70, 0x80,
0xFF, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF,
0x7F, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xEF, 0xFF, 0xF7, 0x7F, 0x7B, 0x3F, 0x3C,
0x1F, 0x1F, 0x0F, 0x0F, 0x03, 0x03, 0x00, 0x00,
0xEF, 0x10, 0x77, 0x88, 0x3B, 0x44, 0x1C, 0x23,
0x0F, 0x10, 0x03, 0x0C, 0x00, 0x03, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x3F, 0xFF, 0xC3, 0xFF, 0xFC, 0xFF, 0xFF,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x3F, 0xC0, 0xC3, 0x3C, 0xFC, 0x03, 0x3F, 0xC0,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xC1,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0x3F, 0xC0, 0xC1, 0x3E,
0xFF, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xEA, 0x14, 0xC0, 0x00, 0x80, 0x21, 0x7F, 0x92,
0x9F, 0xE0, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x27, 0x18, 0x7F, 0x00, 0x1E, 0x61, 0x9A, 0x04,
0xE0, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x73, 0x53, 0x47, 0x44, 0x46, 0x25, 0xFD, 0x03,
0xF9, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x8C, 0x00, 0xD8, 0x20, 0x1D, 0xA0, 0x03, 0x00,
0x07, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xC7, 0xE1, 0xE6, 0x05, 0x42, 0xA5, 0xBF, 0xC0,
0x9F, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x18, 0x24, 0x38, 0x01, 0xB8, 0x05, 0xC0, 0x00,
0xE0, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x21, 0x11, 0x31, 0x49, 0x33, 0x4A, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xDE, 0x00, 0x87, 0x48, 0x84, 0x48, 0x00, 0x00,
0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xCC, 0x02, 0x8E, 0x4A, 0xCC, 0x42, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x71, 0x08, 0x39, 0x00, 0x31, 0x02, 0x00, 0x00,
0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x3D, 0x40, 0x03, 0x02, 0x03, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xBC, 0x02, 0xFC, 0x00, 0xFE, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x12, 0x82, 0x80, 0x80, 0x01, 0x83, 0xFF, 0x00,
0xFE, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x61, 0x1C, 0x7F, 0x00, 0x7C, 0x82, 0x00, 0x00,
0x01, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x22, 0x52, 0x30, 0xC0, 0x58, 0xA4, 0x8F, 0x72,
0x73, 0xFC, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x8C, 0x11, 0x4F, 0x90, 0xA3, 0x0C, 0x73, 0x00,
0xFC, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x23, 0xA4, 0x06, 0x0D, 0x05, 0x1B, 0xBB, 0x07,
0xE7, 0x1F, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x98, 0x44, 0xF5, 0x08, 0xEB, 0x00, 0x87, 0x40,
0x1F, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x66, 0x85, 0xE2, 0xA5, 0x66, 0x81, 0xBF, 0xC1,
0x99, 0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x99, 0x00, 0xB9, 0x00, 0x9D, 0x20, 0xC1, 0x00,
0xE7, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xF6, 0xFA, 0xFC, 0xF2, 0xF7, 0xF8, 0xFB, 0xFC,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xF9, 0x00, 0xF1, 0x02, 0xF8, 0x00, 0xFC, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x52, 0x53, 0x30, 0x23, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x8C, 0x21, 0xCC, 0x13, 0x00, 0x00, 0x00, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x00, 0x03, 0x03, 0x06, 0xFE, 0x01, 0xF9, 0x07,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFD, 0x02, 0xFA, 0x04, 0x01, 0x00, 0x07, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x86, 0x05, 0x46, 0xA0, 0x5F, 0xB8, 0xBF, 0xC0,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x38, 0x41, 0x99, 0x26, 0xB8, 0x00, 0xC0, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x30, 0x28, 0x09, 0x09, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xC6, 0x09, 0xE6, 0x10, 0x00, 0x00, 0x00, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x20, 0x38, 0x38, 0x20, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xD7, 0x08, 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x80, 0x41, 0xA1, 0x61, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x3E, 0x40, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x01, 0x82, 0x01, 0x82, 0xFF, 0x00, 0xFC, 0x03,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x7C, 0x82, 0x7C, 0x82, 0x00, 0x00, 0x03, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x7F, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x3F, 0x3F, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x3C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFE, 0xFF, 0xFF, 0x3F, 0x3F, 0x01, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFE, 0x01, 0x3F, 0xC0, 0x01, 0x3E, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0x3F, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF,
0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x3F, 0xC0, 0xC0, 0x3F, 0xFF, 0x00, 0x3F, 0xC0,
0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xC0, 0xFF, 0xFF,
0xFF, 0xFF, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00,
0xFF, 0x00, 0x3F, 0xC0, 0xC0, 0x3F, 0xFF, 0x00,
0x3F, 0xC0, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFC,
0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x03, 0x00, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0x03, 0xFC, 0xFC, 0x03,
0xFF, 0x00, 0x03, 0xFC, 0x00, 0x03, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0xFF,
0xFF, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03,
0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x03,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x03, 0xFC,
0xFC, 0x03, 0xFF, 0x00, 0x03, 0xFC, 0x00, 0x03,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0xFF,
};

View File

@ -0,0 +1,446 @@
static const uint16_t palette[] = {
0x7C1A, 0x0000, 0x0011, 0x3DEF, 0x6318, 0x7FFF, 0x1EBA, 0x19AF,
0x1EAF, 0x4648, 0x7FC0, 0x2507, 0x1484, 0x5129, 0x5010, 0x2095,
};
static const uint16_t tilemap[] = {
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0001, 0x0002, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003,
0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003,
0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003,
0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x4002, 0x4001, 0x0000,
0x0000, 0x0004, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005,
0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005,
0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005,
0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x4004, 0x0000,
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0007, 0x0008, 0x0008,
0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008,
0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008,
0x0008, 0x0008, 0x4007, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
0x0000, 0x0006, 0x000A, 0x000B, 0x400A, 0x0009, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
0x0000, 0x0006, 0x800A, 0x000C, 0xC00A, 0x0009, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
0x0000, 0x000D, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x000E,
0x000F, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016,
0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E,
0x001F, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x400D, 0x0000,
0x0000, 0x0020, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0021,
0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029,
0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x0030, 0x0031,
0x0032, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x4020, 0x0000,
0x0000, 0x0033, 0x0034, 0x0035, 0x0036, 0x0005, 0x0005, 0x0037,
0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
0x0040, 0x0005, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046,
0x0047, 0x0005, 0x0005, 0x4036, 0x4035, 0x4034, 0x4033, 0x0000,
0x0000, 0x0000, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D,
0x004E, 0x004E, 0x004F, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050,
0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x404F, 0x004E, 0x004E,
0x404D, 0x004C, 0x404B, 0x404A, 0x4049, 0x4048, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
};
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, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x08,
0x01, 0x11, 0x06, 0x26, 0x04, 0x24, 0x08, 0x48,
0x00, 0x00, 0x01, 0x01, 0x07, 0x07, 0x0F, 0x0F,
0x1E, 0x1F, 0x39, 0x3F, 0x3B, 0x3F, 0x77, 0x7F,
0x00, 0x7F, 0x00, 0x80, 0x00, 0x00, 0x7F, 0x7F,
0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0xFF,
0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x08, 0x48, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x77, 0x7F, 0x6F, 0x7F, 0x6F, 0x7F, 0x6F, 0x7F,
0x6F, 0x7F, 0x6F, 0x7F, 0x6F, 0x7F, 0x6F, 0x7F,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x6F, 0x7F, 0x6F, 0x7F, 0x6F, 0x7F, 0x6F, 0x7F,
0x6F, 0x7F, 0x6F, 0x7F, 0x6F, 0x7F, 0x6F, 0x7F,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x42,
0xBD, 0xBD, 0x7E, 0x66, 0x7E, 0xFF, 0x7E, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBD, 0xFF,
0x7E, 0xFF, 0xFF, 0xE7, 0x7E, 0x7E, 0x7E, 0x7E,
0x7E, 0xFF, 0x3C, 0xFF, 0x81, 0xFF, 0xC3, 0xFF,
0x7E, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7E, 0x7E, 0x3C, 0x3C, 0x00, 0x00, 0x00, 0x81,
0x81, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x08, 0x48, 0x08, 0x48, 0x08, 0x48, 0x08, 0x48,
0x6F, 0x7F, 0x6F, 0x7F, 0x6F, 0x7F, 0x6F, 0x7F,
0x37, 0x7F, 0x37, 0x7F, 0x37, 0x7F, 0x37, 0x7F,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFE,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0x3F,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x70,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, 0xDF,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x78,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0xC7,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x3C,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0xE3,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x7F,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x80,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x9F,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x60, 0xE1,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0xD8,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x27, 0x8F,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x70,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, 0x8F,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x70,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, 0x9F,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, 0xC2,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBD, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x10,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x3F,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDF, 0xE0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x84,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x08,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xFF,
0x08, 0x48, 0x08, 0x48, 0x08, 0x48, 0x08, 0x48,
0x08, 0x48, 0x04, 0x24, 0x04, 0x24, 0x02, 0x12,
0x37, 0x7F, 0x37, 0x7F, 0x37, 0x7F, 0x37, 0x7F,
0x37, 0x7F, 0x1B, 0x3F, 0x1B, 0x3F, 0x0D, 0x1F,
0x0F, 0x08, 0x0E, 0x00, 0x1E, 0x12, 0x1E, 0x12,
0x1F, 0x10, 0x0F, 0x08, 0x02, 0x02, 0x00, 0x00,
0xF7, 0xF8, 0xFF, 0xF0, 0xED, 0xE3, 0xED, 0xE1,
0xEF, 0xE0, 0xF7, 0xF0, 0xFD, 0xFC, 0xFF, 0xFF,
0xF0, 0x10, 0x40, 0x00, 0x41, 0x41, 0x00, 0x00,
0x83, 0x82, 0xE3, 0x20, 0xC7, 0x04, 0xC7, 0x00,
0xEF, 0x1F, 0xFF, 0x1F, 0xBE, 0xFF, 0xFF, 0xFE,
0x7D, 0x7E, 0xDF, 0x3C, 0xFB, 0x18, 0xFF, 0x18,
0x60, 0x00, 0x70, 0x00, 0xF8, 0x08, 0xB0, 0x00,
0xD8, 0x40, 0x3C, 0x24, 0x5C, 0x44, 0xFC, 0x00,
0xFF, 0x8F, 0xFF, 0x0F, 0xF7, 0x07, 0xFF, 0x07,
0xBF, 0x47, 0xDB, 0x47, 0xBB, 0x03, 0xFF, 0x03,
0x3C, 0x04, 0x78, 0x00, 0x78, 0x00, 0xEC, 0x80,
0xFE, 0x92, 0xE7, 0x83, 0xE5, 0x80, 0x4F, 0x08,
0xFB, 0x83, 0xFF, 0x83, 0xFF, 0x83, 0x7F, 0x83,
0x6D, 0x93, 0x7C, 0x10, 0x7F, 0x10, 0xF7, 0x10,
0x3C, 0x00, 0x7C, 0x40, 0x78, 0x00, 0xC8, 0x80,
0xFC, 0x24, 0xBC, 0x24, 0xFD, 0x65, 0x3D, 0x25,
0xFF, 0xC3, 0xBF, 0x83, 0xFF, 0x83, 0x7F, 0x03,
0xDB, 0x23, 0xDB, 0x23, 0x9A, 0x67, 0xDA, 0x47,
0xFF, 0x80, 0xFF, 0x80, 0xE0, 0x80, 0x40, 0x00,
0xFF, 0x01, 0xFF, 0x01, 0xDF, 0x1F, 0xE0, 0x20,
0x7F, 0x80, 0x7F, 0x00, 0x7F, 0x1F, 0xFF, 0x1F,
0xFE, 0x00, 0xFE, 0x00, 0xE0, 0x01, 0xDF, 0x3F,
0xBF, 0xA0, 0xB9, 0xA0, 0x10, 0x00, 0x11, 0x01,
0x3B, 0x00, 0x3F, 0x00, 0x7E, 0x4E, 0x78, 0x48,
0x5F, 0x40, 0x5F, 0xC0, 0xFF, 0xC7, 0xFE, 0xC7,
0xFF, 0xC0, 0xFF, 0xC0, 0xB1, 0xC4, 0xB7, 0x8F,
0xE3, 0x22, 0xC7, 0x04, 0xCE, 0x08, 0xE6, 0x20,
0xCE, 0x42, 0xDE, 0x52, 0xFE, 0x32, 0xFC, 0x30,
0xDD, 0x3E, 0xFB, 0x18, 0xF7, 0x18, 0xDF, 0x31,
0xBD, 0x31, 0xAD, 0x23, 0xCD, 0x31, 0xCF, 0x11,
0xFE, 0x02, 0x9E, 0x00, 0x86, 0x80, 0x06, 0x00,
0x03, 0x00, 0x02, 0x00, 0x07, 0x01, 0x07, 0x01,
0xFD, 0x03, 0xFF, 0x01, 0x7F, 0xF0, 0xFF, 0xF8,
0xFF, 0xF8, 0xFF, 0xF8, 0xFE, 0xF8, 0xFE, 0xF1,
0x38, 0x08, 0x71, 0x41, 0x1F, 0x06, 0x39, 0x20,
0x0F, 0x00, 0x0F, 0x01, 0x04, 0x00, 0x0C, 0x00,
0xF7, 0x87, 0xBE, 0xC6, 0xF9, 0xC6, 0xDF, 0xE0,
0xFF, 0xE0, 0xFE, 0xF1, 0xFF, 0xF1, 0xFF, 0xF1,
0x70, 0x10, 0xE0, 0x20, 0x80, 0x00, 0x80, 0x00,
0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xEF, 0x1F, 0xDF, 0x1F, 0xFF, 0x3F, 0xFF, 0x7F,
0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x3F, 0x3F, 0x7F, 0x7F, 0x78, 0x78, 0xF0, 0xF0,
0xF0, 0xF0, 0xE0, 0xE0, 0xE0, 0xE0, 0xF0, 0xF0,
0xDF, 0xFF, 0xBD, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF,
0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF,
0xE7, 0xE0, 0xFF, 0xF0, 0xFE, 0xF0, 0x1C, 0x00,
0x3C, 0x20, 0x3C, 0x24, 0x3C, 0x24, 0x3C, 0x20,
0xFF, 0xFF, 0xEF, 0xFF, 0x6F, 0xFF, 0xFF, 0xFF,
0xDF, 0xFF, 0xDB, 0xFF, 0xDB, 0xFF, 0xDF, 0xFF,
0xF8, 0x00, 0xFC, 0xC0, 0x1F, 0x03, 0x1F, 0x13,
0x1F, 0x13, 0x1E, 0x02, 0x1E, 0x02, 0x3E, 0x02,
0xFF, 0xFF, 0x3F, 0xFF, 0xFC, 0xFF, 0xEC, 0xFF,
0xED, 0xFE, 0xFC, 0xFF, 0xFC, 0xFF, 0xFC, 0xFF,
0x90, 0x90, 0x00, 0x00, 0x00, 0x00, 0x21, 0x21,
0x20, 0x21, 0x00, 0x01, 0x00, 0x01, 0x40, 0x41,
0x8F, 0x7F, 0x1F, 0xFF, 0x1F, 0xFF, 0x3F, 0xDE,
0x1F, 0xFE, 0x3F, 0xFE, 0x3F, 0xFE, 0x7F, 0xBE,
0x40, 0x7F, 0x84, 0xFF, 0x11, 0xF1, 0x20, 0xE0,
0x20, 0xE0, 0x01, 0xC1, 0x01, 0xC1, 0x22, 0xE3,
0xFF, 0x80, 0xFF, 0x00, 0xFF, 0x0E, 0xFF, 0x1F,
0xDF, 0x3F, 0xFE, 0x3F, 0xFF, 0x3E, 0xFD, 0x1E,
0x47, 0xC0, 0x27, 0xE0, 0x2F, 0xE8, 0x0F, 0xE9,
0x0F, 0xE1, 0x0F, 0xE0, 0x3F, 0xF0, 0x3F, 0xF1,
0xF8, 0x3F, 0xF8, 0x1F, 0xF0, 0x1F, 0xF0, 0x1F,
0xF0, 0x1F, 0xF0, 0x1F, 0xE0, 0x1F, 0xC0, 0x3F,
0xFC, 0x00, 0xFE, 0xE2, 0x1E, 0x12, 0x1E, 0x12,
0x3E, 0x22, 0xFC, 0x00, 0xF8, 0x08, 0xF0, 0x10,
0x03, 0xFF, 0x01, 0xFF, 0xE1, 0xFF, 0xE1, 0xFF,
0xC1, 0xFF, 0x03, 0xFF, 0x07, 0xFF, 0x0F, 0xFF,
0x01, 0x11, 0x00, 0x08, 0x00, 0x04, 0x00, 0x02,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0E, 0x1F, 0x07, 0x0F, 0x03, 0x07, 0x01, 0x03,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x80, 0x80, 0x40, 0x40, 0x30, 0x30,
0x0C, 0x0C, 0x03, 0xC3, 0x00, 0x30, 0x00, 0x0C,
0xFF, 0xFF, 0x7F, 0xFF, 0xBF, 0xFF, 0xCF, 0xFF,
0xF3, 0xFF, 0x3C, 0xFF, 0x0F, 0x3F, 0x03, 0x0F,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xC0, 0xC0, 0x3C, 0x3C, 0x03, 0x03,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x3F, 0xFF, 0xC3, 0xFF, 0xFC, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xE0,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF,
0x15, 0x15, 0x3F, 0x20, 0x2F, 0x20, 0x06, 0x06,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xEA, 0xE6, 0xDF, 0xC0, 0xDF, 0xE0, 0xF9, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xE6, 0x20, 0x9E, 0x12, 0x0C, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xDF, 0x30, 0xED, 0x31, 0xFF, 0x63, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x0E, 0x02, 0x1E, 0x12, 0x0D, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFD, 0x03, 0xED, 0xE1, 0xFE, 0xF1, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x4F, 0x08, 0xE6, 0x20, 0xE7, 0x21, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xF7, 0x18, 0xDF, 0x18, 0xDE, 0x18, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xB9, 0xA1, 0x11, 0x01, 0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x5E, 0x46, 0xFE, 0xC6, 0xFF, 0xC6, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x3F, 0xFF, 0x01, 0xFF, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC0, 0x01, 0xFE, 0x00, 0xFE, 0x00, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x7E, 0x4E, 0x3F, 0x00, 0x3E, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xB1, 0x8E, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xEE, 0x20, 0x8F, 0x08, 0x85, 0x84, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xDF, 0x30, 0xF7, 0x38, 0x7B, 0x7C, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xAE, 0xA2, 0xF8, 0x00, 0xE8, 0x08, 0xC0, 0xC0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x5D, 0xE1, 0xFF, 0x03, 0xF7, 0x0F, 0x3F, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x0E, 0x02, 0x1E, 0x12, 0x1E, 0x12, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFD, 0xF1, 0xED, 0xF1, 0xED, 0xE3, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFB, 0xFB, 0x7F, 0x7F, 0x3F, 0x3F, 0x0C, 0x0C,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x75, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xDF, 0xC1, 0xDF, 0xD0, 0x8F, 0x88, 0x01, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFE, 0xFF, 0xEF, 0xFF, 0x77, 0xFF, 0xFE, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFA, 0x82, 0xF8, 0x08, 0xE0, 0x00, 0x81, 0x81,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7E, 0xFD, 0xF4, 0xFF, 0xFC, 0xFF, 0x7E, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x7D, 0x7D, 0x02, 0x02, 0x02, 0x02, 0xFC, 0xFC,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0xFE, 0x01, 0xFF, 0x01, 0xFF, 0x03, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x1C, 0xFF, 0x00, 0xFF, 0x41, 0x7F, 0x18, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xF7, 0x08, 0xFF, 0x00, 0xFF, 0x80, 0xE7, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x5E, 0xC2, 0x9C, 0x80, 0x1C, 0x04, 0x08, 0x08,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xE1, 0x3F, 0xE3, 0x7F, 0xE3, 0xFF, 0xF7, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xF0, 0x80, 0x78, 0x08, 0x78, 0x48, 0x10, 0x10,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0F, 0xFF, 0x87, 0xFF, 0x87, 0xFF, 0xEF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xC0, 0x00, 0x3C, 0x00, 0x03, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x3F, 0xFF, 0x03, 0x3F, 0x00, 0x03, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x1C, 0x1C, 0x03, 0x03, 0x00, 0xE0, 0x00, 0x1C,
0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xE3, 0xFF, 0xFC, 0xFF, 0x1F, 0xFF, 0x03, 0x1F,
0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFC, 0xFC, 0x03, 0x03, 0x00, 0x00,
0x00, 0xFC, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0x03, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF,
0x03, 0xFF, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0x3F, 0x3F,
0x00, 0x00, 0x00, 0xC0, 0x00, 0x3F, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xC0, 0xFF,
0xFF, 0xFF, 0x3F, 0xFF, 0x00, 0x3F, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0,
0x3F, 0x3F, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x3F,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF,
0xC0, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0x00, 0x3F,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF
};

View File

@ -0,0 +1,558 @@
static const uint16_t palette[] = {
0x0000, 0x0011, 0x18C6, 0x001A, 0x318C, 0x39CE, 0x5294, 0x5AD6,
0x739C, 0x45A8, 0x4520, 0x18A5, 0x4A32, 0x2033, 0x20EC, 0x0000,
};
static const uint16_t tilemap[] = {
0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002,
0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002,
0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002,
0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002,
0x0001, 0x0003, 0x0004, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005,
0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005,
0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005,
0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x4004, 0x4003, 0x0001,
0x0001, 0x0006, 0x0007, 0x0007, 0x0007, 0x0008, 0x0009, 0x000A,
0x000B, 0x000C, 0x000D, 0x000E, 0x000F, 0x0010, 0x0011, 0x0012,
0x0013, 0x0014, 0x0015, 0x000E, 0x0016, 0x0017, 0x0018, 0x0019,
0x001A, 0x001B, 0x001C, 0x0007, 0x0007, 0x0007, 0x4006, 0x0001,
0x0001, 0x001D, 0x001E, 0x001E, 0x001E, 0x001F, 0x0020, 0x0021,
0x0022, 0x0023, 0x0024, 0x0025, 0x4024, 0x0026, 0x0025, 0x0025,
0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E,
0x002F, 0x0030, 0x0031, 0x001E, 0x001E, 0x001E, 0xC01D, 0x0001,
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
0x0001, 0x001D, 0x0034, 0x0035, 0x4034, 0x0033, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
0x0001, 0x001D, 0x8034, 0x0036, 0xC034, 0x0033, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0x0037, 0x0001,
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0x0038, 0x0001,
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0039, 0x003A, 0x0001,
0x0001, 0x003B, 0x003C, 0x0032, 0x0032, 0xC03C, 0x003D, 0x003D,
0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D,
0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D,
0x003D, 0x003D, 0x003E, 0x003F, 0x0040, 0x0041, 0x0001, 0x0001,
0x0001, 0x0042, 0x0043, 0x0044, 0x0044, 0x0044, 0x0044, 0x0044,
0x0044, 0x0044, 0x0044, 0x0044, 0x0044, 0x0044, 0x0044, 0x0044,
0x0044, 0x0044, 0x0044, 0x0044, 0x0044, 0x0044, 0x0044, 0x0044,
0x0044, 0x0044, 0x0045, 0x0046, 0x0001, 0x0001, 0x0001, 0x0001,
0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
0x0001, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D,
0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0001,
0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
0x0001, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B,
0x005C, 0x005D, 0x005E, 0x005F, 0x0060, 0x0061, 0x0001, 0x0001,
0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
0x0001, 0x0001, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
0x0068, 0x0069, 0x006A, 0x006B, 0x0001, 0x006C, 0x0001, 0x0001,
0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
};
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,
0x00, 0xFF, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01,
0x00, 0xFF, 0x00, 0xFF, 0x21, 0xDE, 0x00, 0x7F,
0x0C, 0xF3, 0x19, 0xE0, 0x10, 0xEE, 0x08, 0xF7,
0xFF, 0x00, 0xFF, 0x00, 0xC0, 0x3F, 0x80, 0xFF,
0x00, 0xFF, 0x0E, 0xF7, 0x1F, 0xE1, 0x07, 0xF8,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0x7F, 0x00, 0xBF,
0x40, 0xBE, 0x80, 0x3F, 0x02, 0xFD, 0x00, 0xFB,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x80, 0x7F, 0xC0,
0x7F, 0x81, 0xFE, 0x41, 0xFC, 0x03, 0xFC, 0x07,
0x00, 0xFF, 0x00, 0xFF, 0x08, 0xF7, 0x00, 0xFF,
0x00, 0xFB, 0x04, 0xFB, 0x24, 0xDB, 0x64, 0x9B,
0xFF, 0x00, 0xFF, 0x00, 0x87, 0x78, 0x07, 0xF8,
0x07, 0xFC, 0x07, 0xF8, 0x03, 0xFC, 0x43, 0xBC,
0x00, 0xFF, 0x00, 0xFF, 0x01, 0xDE, 0x20, 0xDF,
0x20, 0xDF, 0x00, 0xFF, 0x04, 0xFB, 0x04, 0xFB,
0xFF, 0x00, 0xFF, 0x00, 0xE0, 0x3F, 0xC0, 0x3F,
0xC0, 0x3F, 0xC0, 0x3F, 0xC0, 0x3F, 0xC0, 0x3F,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFB, 0x00, 0xFF,
0x00, 0x77, 0x00, 0x7F, 0x80, 0x6F, 0x82, 0x7D,
0xFF, 0x00, 0xFF, 0x00, 0xFC, 0x07, 0xF8, 0x07,
0xF8, 0x8F, 0xF0, 0x8F, 0x70, 0x9F, 0x60, 0x9F,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFB, 0x24, 0xCB,
0x24, 0xDB, 0x20, 0xDF, 0x20, 0xDF, 0x00, 0xDF,
0xFF, 0x00, 0xFF, 0x00, 0x1C, 0xE7, 0x18, 0xF7,
0x18, 0xE7, 0x18, 0xE7, 0x38, 0xC7, 0x38, 0xE7,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x02, 0xFC,
0x7E, 0x81, 0x80, 0x01, 0x80, 0x7F, 0xF8, 0x03,
0xFF, 0x00, 0xFF, 0x00, 0x01, 0xFE, 0x01, 0xFF,
0x01, 0xFE, 0x7F, 0xFE, 0x7F, 0x80, 0x07, 0xFC,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xBF, 0x40, 0xBF,
0x47, 0xB8, 0x08, 0xF0, 0x08, 0xF7, 0x0E, 0xF1,
0xFF, 0x00, 0xFF, 0x00, 0xC0, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x87, 0x7F, 0x87, 0x78, 0x80, 0x7F,
0x00, 0xFF, 0x00, 0xFF, 0x40, 0x9F, 0x00, 0xFF,
0x10, 0xEF, 0x90, 0x6F, 0x10, 0xEB, 0x14, 0xEB,
0xFF, 0x00, 0xFF, 0x00, 0x3F, 0xE0, 0x1F, 0xE0,
0x0E, 0xF1, 0x0C, 0xF3, 0x0C, 0xF7, 0x18, 0xE7,
0x00, 0xFF, 0x00, 0xFF, 0x20, 0x9F, 0x00, 0xFF,
0x0C, 0xF3, 0x31, 0xC0, 0x60, 0x9F, 0x40, 0xBF,
0xFF, 0x00, 0xFF, 0x00, 0xC0, 0x7F, 0x00, 0xFF,
0x00, 0xFF, 0x1E, 0xEF, 0x3F, 0xC0, 0x7F, 0x80,
0x00, 0xFF, 0x00, 0xFF, 0x80, 0x77, 0x04, 0xDB,
0x00, 0xFB, 0x10, 0xEF, 0x00, 0xFD, 0x80, 0x77,
0xFF, 0x00, 0xFF, 0x00, 0x78, 0x8F, 0x38, 0xE7,
0x1C, 0xE7, 0x0C, 0xF3, 0x0E, 0xF3, 0x0E, 0xF9,
0x00, 0xFF, 0x00, 0xFF, 0x80, 0x7F, 0x00, 0xFF,
0x40, 0xB7, 0x00, 0xEF, 0x01, 0xDE, 0x02, 0xFC,
0xFF, 0x00, 0xFF, 0x00, 0x7C, 0x83, 0x78, 0x87,
0x38, 0xCF, 0x30, 0xDF, 0x21, 0xFE, 0x03, 0xFD,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xDF, 0x60, 0x9F,
0xC0, 0x3F, 0x80, 0x7F, 0x00, 0x7F, 0x00, 0xFF,
0xFF, 0x00, 0xFF, 0x00, 0x3F, 0xE0, 0x3F, 0xC0,
0x7F, 0x80, 0xFF, 0x00, 0xFF, 0x80, 0xFF, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFB, 0x01, 0xFC,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFE, 0x01, 0xFC, 0x03, 0xFC, 0x07, 0xFE, 0x03,
0x00, 0xFF, 0x40, 0x3F, 0x30, 0x8F, 0x00, 0xF7,
0x80, 0x7F, 0x30, 0xCF, 0x01, 0xFE, 0x87, 0x78,
0x01, 0xFE, 0x80, 0xFF, 0xE0, 0x5F, 0xF8, 0x0F,
0x78, 0x87, 0x00, 0xFF, 0x00, 0xFF, 0x03, 0xFC,
0x00, 0xFF, 0x08, 0xF7, 0x80, 0x6F, 0x80, 0x7F,
0x80, 0x5F, 0x87, 0x78, 0x04, 0x7B, 0x08, 0x73,
0xF8, 0x07, 0xF0, 0x0F, 0x70, 0x9F, 0x60, 0x9F,
0x60, 0xBF, 0xC3, 0x3C, 0x87, 0xF8, 0x87, 0xFC,
0xA0, 0x1F, 0x80, 0x7D, 0xE2, 0x1D, 0x02, 0xFD,
0x02, 0xFD, 0xF0, 0x0F, 0x10, 0xEE, 0x11, 0xEE,
0x43, 0xFC, 0xE3, 0x1E, 0x03, 0xFC, 0x01, 0xFE,
0x01, 0xFE, 0xE1, 0x1E, 0xE1, 0x1F, 0xE1, 0x1E,
0x44, 0xBB, 0x48, 0xB3, 0x48, 0xB7, 0x08, 0xF7,
0x0A, 0xF5, 0x02, 0xF5, 0x80, 0x77, 0x90, 0x67,
0x84, 0x7B, 0x84, 0x7F, 0x84, 0x7B, 0x84, 0x7B,
0x8C, 0x73, 0x8C, 0x7B, 0x0E, 0xF9, 0x0E, 0xF9,
0x86, 0x59, 0x06, 0xF9, 0x48, 0xB3, 0x08, 0xF7,
0x10, 0xE7, 0x14, 0xEB, 0x24, 0xCB, 0x20, 0xDF,
0x60, 0xBF, 0x44, 0xBB, 0x04, 0xFF, 0x0C, 0xF3,
0x0C, 0xFB, 0x18, 0xE7, 0x18, 0xF7, 0x38, 0xC7,
0x08, 0xD7, 0x48, 0x97, 0x48, 0xB7, 0x41, 0xBE,
0x41, 0xBE, 0x01, 0xBE, 0x10, 0xAF, 0x90, 0x2F,
0x30, 0xEF, 0x30, 0xEF, 0x30, 0xCF, 0x30, 0xCF,
0x70, 0x8F, 0x70, 0xCF, 0x60, 0xDF, 0x60, 0xDF,
0x04, 0xFB, 0x04, 0xFB, 0xFC, 0x03, 0x00, 0xFF,
0x00, 0xFF, 0xF8, 0x02, 0x05, 0xFA, 0x05, 0xFA,
0x03, 0xFC, 0x03, 0xFC, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0x07, 0xFD, 0x02, 0xFD, 0x06, 0xF9,
0x80, 0x7F, 0x80, 0x7F, 0x0F, 0xF0, 0x10, 0xE7,
0x10, 0xEE, 0x1E, 0xE1, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x0E, 0xF1, 0x0F, 0xF8,
0x0F, 0xF1, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x60, 0x8F, 0x00, 0xDF, 0x00, 0xFF, 0x00, 0xEF,
0x04, 0xEB, 0x20, 0xCF, 0x22, 0xDD, 0xC1, 0x1E,
0x38, 0xD7, 0x38, 0xE7, 0x18, 0xE7, 0x18, 0xF7,
0x18, 0xF7, 0x1C, 0xF3, 0x3E, 0xC1, 0x7F, 0xA0,
0x80, 0x3F, 0x80, 0x7F, 0x80, 0x7F, 0x01, 0xFE,
0x00, 0xBD, 0x18, 0xE7, 0x00, 0xFF, 0x83, 0x7C,
0x7F, 0xC0, 0x7F, 0x80, 0x7F, 0x80, 0x7E, 0x81,
0x7E, 0xC3, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x81, 0x76, 0x80, 0x77, 0x10, 0xE7, 0x10, 0xEF,
0x10, 0xEF, 0x21, 0xCE, 0x41, 0x9E, 0x81, 0x3E,
0x0E, 0xF9, 0x0F, 0xF8, 0x0F, 0xF8, 0x0F, 0xF0,
0x1F, 0xE0, 0x3E, 0xD1, 0x7E, 0xA1, 0xFE, 0x41,
0x04, 0xF9, 0x08, 0xF3, 0x18, 0xE7, 0x10, 0xEF,
0x10, 0xEF, 0x10, 0xEF, 0x00, 0xEF, 0x20, 0xCF,
0x07, 0xFA, 0x07, 0xFC, 0x0F, 0xF0, 0x0F, 0xF0,
0x0F, 0xF0, 0x1F, 0xE0, 0x1F, 0xF0, 0x1F, 0xF0,
0x7C, 0x01, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x82, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x78, 0x87, 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,
0x0F, 0xF0, 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,
0x70, 0x8E, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x01, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xC3, 0x18, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x24, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x8F, 0x70, 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,
0xF8, 0x03, 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,
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,
0x00, 0x7F, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x80, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x3E, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0xC1, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xE0, 0x1F, 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,
};

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,167 @@
#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 , true, false}, // FEh HuC3
{ GB_HUC1 , GB_STANDARD_MBC, true , true , false, false}, // FFh HuC1+RAM+BATTERY
};
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->is_mbc30) {
gb->mbc_rom_bank &= 0x7F;
}
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 unsigned ram_sizes[256] = {0, 0x800, 0x2000, 0x8000, 0x20000, 0x10000};
gb->mbc_ram_size = ram_sizes[gb->rom[0x149]];
}
if (gb->mbc_ram_size) {
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;
}
}
/* Detect MBC30 */
if (gb->cartridge_type->mbc_type == GB_MBC3) {
if (gb->rom_size > 0x200000 || gb->mbc_ram_size > 0x8000) {
gb->is_mbc30 = true;
}
}
/* Set MBC5's bank to 1 correctly */
if (gb->cartridge_type->mbc_type == GB_MBC5) {
gb->mbc5.rom_bank_low = 1;
}
}

View File

@ -0,0 +1,32 @@
#ifndef MBC_h
#define MBC_h
#include "gb_struct_def.h"
#include <stdbool.h>
typedef struct {
enum {
GB_NO_MBC,
GB_MBC1,
GB_MBC2,
GB_MBC3,
GB_MBC5,
GB_HUC1,
GB_HUC3,
} mbc_type;
enum {
GB_STANDARD_MBC,
GB_CAMERA,
} mbc_subtype;
bool has_ram;
bool has_battery;
bool has_rtc;
bool has_rumble;
} GB_cartridge_t;
#ifdef GB_INTERNAL
extern const GB_cartridge_t GB_cart_defs[256];
void GB_update_mbc_mappings(GB_gameboy_t *gb);
void GB_configure_cart(GB_gameboy_t *gb);
#endif
#endif /* MBC_h */

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -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_received <<= 1;
gb->printer.byte_being_received |= bit_received;
gb->printer.bits_received++;
if (gb->printer.bits_received == 8) {
byte_reieve_completed(gb, gb->printer.byte_being_received);
gb->printer.bits_received = 0;
gb->printer.byte_being_received = 0;
}
}
static bool serial_end(GB_gameboy_t *gb)
{
bool ret = gb->printer.bit_to_send;
gb->printer.bit_to_send = gb->printer.byte_to_send & 0x80;
gb->printer.byte_to_send <<= 1;
return ret;
}
void GB_connect_printer(GB_gameboy_t *gb, GB_print_image_callback_t callback)
{
memset(&gb->printer, 0, sizeof(gb->printer));
GB_set_serial_transfer_bit_start_callback(gb, serial_start);
GB_set_serial_transfer_bit_end_callback(gb, serial_end);
gb->printer_callback = callback;
}

View File

@ -0,0 +1,64 @@
#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;
/* TODO: Delete me. */
uint64_t padding;
uint8_t compression_run_lenth;
bool compression_run_is_compressed;
uint8_t bits_received;
uint8_t byte_being_received;
bool bit_to_send;
} GB_printer_t;
void GB_connect_printer(GB_gameboy_t *gb, GB_print_image_callback_t callback);
#endif

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,53 @@
#include "rumble.h"
#include "gb.h"
void GB_set_rumble_mode(GB_gameboy_t *gb, GB_rumble_mode_t mode)
{
gb->rumble_mode = mode;
if (gb->rumble_callback) {
gb->rumble_callback(gb, 0);
}
}
void GB_handle_rumble(GB_gameboy_t *gb)
{
if (gb->rumble_callback) {
if (gb->rumble_mode == GB_RUMBLE_DISABLED) {
return;
}
if (gb->cartridge_type->has_rumble) {
if (gb->rumble_on_cycles + gb->rumble_off_cycles) {
gb->rumble_callback(gb, gb->rumble_on_cycles / (double)(gb->rumble_on_cycles + gb->rumble_off_cycles));
gb->rumble_on_cycles = gb->rumble_off_cycles = 0;
}
}
else if (gb->rumble_mode == GB_RUMBLE_ALL_GAMES) {
unsigned volume = (gb->io_registers[GB_IO_NR50] & 7) + 1 + ((gb->io_registers[GB_IO_NR50] >> 4) & 7) + 1;
unsigned ch4_volume = volume * (!!(gb->io_registers[GB_IO_NR51] & 8) + !!(gb->io_registers[GB_IO_NR51] & 0x80));
unsigned ch1_volume = volume * (!!(gb->io_registers[GB_IO_NR51] & 1) + !!(gb->io_registers[GB_IO_NR51] & 0x10));
double ch4_rumble = (MIN(gb->apu.noise_channel.sample_length * (gb->apu.noise_channel.narrow? 8 : 1) , 4096) * ((signed) gb->apu.noise_channel.current_volume * gb->apu.noise_channel.current_volume * ch4_volume / 32.0 - 50) - 2048) / 2048.0;
ch4_rumble = MIN(ch4_rumble, 1.0);
ch4_rumble = MAX(ch4_rumble, 0.0);
double ch1_rumble = 0;
if (gb->apu.sweep_enabled && ((gb->io_registers[GB_IO_NR10] >> 4) & 7)) {
double sweep_speed = (gb->io_registers[GB_IO_NR10] & 7) / (double)((gb->io_registers[GB_IO_NR10] >> 4) & 7);
ch1_rumble = gb->apu.square_channels[GB_SQUARE_1].current_volume * ch1_volume / 32.0 * sweep_speed / 8.0 - 0.5;
ch1_rumble = MIN(ch1_rumble, 1.0);
ch1_rumble = MAX(ch1_rumble, 0.0);
}
if (!gb->apu.is_active[GB_NOISE]) {
ch4_rumble = 0;
}
if (!gb->apu.is_active[GB_SQUARE_1]) {
ch1_rumble = 0;
}
gb->rumble_callback(gb, MIN(MAX(ch1_rumble / 2 + ch4_rumble, 0.0), 1.0));
}
}
}

View File

@ -0,0 +1,17 @@
#ifndef rumble_h
#define rumble_h
#include "gb_struct_def.h"
typedef enum {
GB_RUMBLE_DISABLED,
GB_RUMBLE_CARTRIDGE_ONLY,
GB_RUMBLE_ALL_GAMES
} GB_rumble_mode_t;
#ifdef GB_INTERNAL
void GB_handle_rumble(GB_gameboy_t *gb);
#endif
void GB_set_rumble_mode(GB_gameboy_t *gb, GB_rumble_mode_t mode);
#endif /* rumble_h */

View File

@ -0,0 +1,413 @@
#include "gb.h"
#include <stdio.h>
#include <errno.h>
static bool dump_section(FILE *f, const void *src, uint32_t size)
{
if (fwrite(&size, 1, sizeof(size), f) != sizeof(size)) {
return false;
}
if (fwrite(src, 1, size, f) != size) {
return false;
}
return true;
}
#define DUMP_SECTION(gb, f, section) dump_section(f, GB_GET_SECTION(gb, section), GB_SECTION_SIZE(section))
/* Todo: we need a sane and protable save state format. */
int GB_save_state(GB_gameboy_t *gb, const char *path)
{
FILE *f = fopen(path, "wb");
if (!f) {
GB_log(gb, "Could not open save state: %s.\n", strerror(errno));
return errno;
}
if (fwrite(GB_GET_SECTION(gb, header), 1, GB_SECTION_SIZE(header), f) != GB_SECTION_SIZE(header)) goto error;
if (!DUMP_SECTION(gb, f, core_state)) goto error;
if (!DUMP_SECTION(gb, f, dma )) goto error;
if (!DUMP_SECTION(gb, f, mbc )) goto error;
if (!DUMP_SECTION(gb, f, hram )) goto error;
if (!DUMP_SECTION(gb, f, timing )) goto error;
if (!DUMP_SECTION(gb, f, apu )) goto error;
if (!DUMP_SECTION(gb, f, rtc )) goto error;
if (!DUMP_SECTION(gb, f, video )) goto error;
if (GB_is_hle_sgb(gb)) {
if (!dump_section(f, gb->sgb, sizeof(*gb->sgb))) goto error;
}
if (fwrite(gb->mbc_ram, 1, gb->mbc_ram_size, f) != gb->mbc_ram_size) {
goto error;
}
if (fwrite(gb->ram, 1, gb->ram_size, f) != gb->ram_size) {
goto error;
}
if (fwrite(gb->vram, 1, gb->vram_size, f) != gb->vram_size) {
goto error;
}
errno = 0;
error:
fclose(f);
return errno;
}
#undef DUMP_SECTION
size_t GB_get_save_state_size(GB_gameboy_t *gb)
{
return GB_SECTION_SIZE(header)
+ GB_SECTION_SIZE(core_state) + sizeof(uint32_t)
+ GB_SECTION_SIZE(dma ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(mbc ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(hram ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(timing ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(apu ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(rtc ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(video ) + sizeof(uint32_t)
+ (GB_is_hle_sgb(gb)? sizeof(*gb->sgb) + sizeof(uint32_t) : 0)
+ gb->mbc_ram_size
+ gb->ram_size
+ gb->vram_size;
}
/* A write-line function for memory copying */
static void buffer_write(const void *src, size_t size, uint8_t **dest)
{
memcpy(*dest, src, size);
*dest += size;
}
static void buffer_dump_section(uint8_t **buffer, const void *src, uint32_t size)
{
buffer_write(&size, sizeof(size), buffer);
buffer_write(src, size, buffer);
}
#define DUMP_SECTION(gb, buffer, section) buffer_dump_section(&buffer, GB_GET_SECTION(gb, section), GB_SECTION_SIZE(section))
void GB_save_state_to_buffer(GB_gameboy_t *gb, uint8_t *buffer)
{
buffer_write(GB_GET_SECTION(gb, header), GB_SECTION_SIZE(header), &buffer);
DUMP_SECTION(gb, buffer, core_state);
DUMP_SECTION(gb, buffer, dma );
DUMP_SECTION(gb, buffer, mbc );
DUMP_SECTION(gb, buffer, hram );
DUMP_SECTION(gb, buffer, timing );
DUMP_SECTION(gb, buffer, apu );
DUMP_SECTION(gb, buffer, rtc );
DUMP_SECTION(gb, buffer, video );
if (GB_is_hle_sgb(gb)) {
buffer_dump_section(&buffer, gb->sgb, sizeof(*gb->sgb));
}
buffer_write(gb->mbc_ram, gb->mbc_ram_size, &buffer);
buffer_write(gb->ram, gb->ram_size, &buffer);
buffer_write(gb->vram, gb->vram_size, &buffer);
}
/* Best-effort read function for maximum future compatibility. */
static bool read_section(FILE *f, void *dest, uint32_t size, bool fix_broken_windows_saves)
{
uint32_t saved_size = 0;
if (fread(&saved_size, 1, sizeof(size), f) != sizeof(size)) {
return false;
}
if (fix_broken_windows_saves) {
if (saved_size < 4) {
return false;
}
saved_size -= 4;
fseek(f, 4, SEEK_CUR);
}
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_and_update_state_compatibility(GB_gameboy_t *gb, GB_gameboy_t *save)
{
if (save->ram_size == 0 && (&save->ram_size)[-1] == gb->ram_size) {
/* This is a save state with a bad printer struct from a 32-bit OS */
memcpy(save->extra_oam + 4, save->extra_oam, (uintptr_t)&save->ram_size - (uintptr_t)&save->extra_oam);
}
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 (gb->version != save->version) {
GB_log(gb, "The save state is for a different version of SameBoy.\n");
return false;
}
if (gb->mbc_ram_size < save->mbc_ram_size) {
GB_log(gb, "The save state has non-matching MBC RAM size.\n");
return false;
}
if (gb->vram_size != save->vram_size) {
GB_log(gb, "The save state has non-matching VRAM size. Try changing the emulated model.\n");
return false;
}
if (GB_is_hle_sgb(gb) != GB_is_hle_sgb(save)) {
GB_log(gb, "The save state is %sfor a Super Game Boy. Try changing the emulated model.\n", GB_is_hle_sgb(save)? "" : "not ");
return false;
}
if (gb->ram_size != save->ram_size) {
if (gb->ram_size == 0x1000 * 8 && save->ram_size == 0x2000 * 8) {
/* A bug in versions prior to 0.12 made CGB instances allocate twice the ammount of RAM.
Ignore this issue to retain compatibility with older, 0.11, save states. */
}
else {
GB_log(gb, "The save state has non-matching RAM size. Try changing the emulated model.\n");
return false;
}
}
return true;
}
static void sanitize_state(GB_gameboy_t *gb)
{
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;
gb->object_low_line_address &= gb->vram_size & ~1;
gb->fetcher_x &= 0x1f;
if (gb->lcd_x > gb->position_in_line) {
gb->lcd_x = gb->position_in_line;
}
if (gb->object_priority == GB_OBJECT_PRIORITY_UNDEFINED) {
gb->object_priority = gb->cgb_mode? GB_OBJECT_PRIORITY_INDEX : GB_OBJECT_PRIORITY_X;
}
}
#define READ_SECTION(gb, f, section) read_section(f, GB_GET_SECTION(gb, section), GB_SECTION_SIZE(section), fix_broken_windows_saves)
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;
}
bool fix_broken_windows_saves = false;
if (fread(GB_GET_SECTION(&save, header), 1, GB_SECTION_SIZE(header), f) != GB_SECTION_SIZE(header)) goto error;
if (save.magic == 0) {
/* Potentially legacy, broken Windows save state */
fseek(f, 4, SEEK_SET);
if (fread(GB_GET_SECTION(&save, header), 1, GB_SECTION_SIZE(header), f) != GB_SECTION_SIZE(header)) goto error;
fix_broken_windows_saves = true;
}
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 (!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 (!verify_and_update_state_compatibility(gb, &save)) {
errno = -1;
goto error;
}
if (GB_is_hle_sgb(gb)) {
if (!read_section(f, gb->sgb, sizeof(*gb->sgb), false)) 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;
sanitize_state(gb);
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, bool fix_broken_windows_saves)
{
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 (fix_broken_windows_saves) {
if (saved_size < 4) {
return false;
}
saved_size -= 4;
*buffer += 4;
}
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), fix_broken_windows_saves)
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));
bool fix_broken_windows_saves = false;
if (buffer_read(GB_GET_SECTION(&save, header), GB_SECTION_SIZE(header), &buffer, &length) != GB_SECTION_SIZE(header)) return -1;
if (save.magic == 0) {
/* Potentially legacy, broken Windows save state*/
buffer -= GB_SECTION_SIZE(header) - 4;
length += GB_SECTION_SIZE(header) - 4;
if (buffer_read(GB_GET_SECTION(&save, header), GB_SECTION_SIZE(header), &buffer, &length) != GB_SECTION_SIZE(header)) return -1;
fix_broken_windows_saves = true;
}
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 (!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_and_update_state_compatibility(gb, &save)) {
return -1;
}
if (GB_is_hle_sgb(gb)) {
if (!buffer_read_section(&buffer, &length, gb->sgb, sizeof(*gb->sgb), false)) 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));
sanitize_state(gb);
return 0;
}
#undef READ_SECTION

View File

@ -0,0 +1,30 @@
/* 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
#ifdef __cplusplus
/* For bsnes integration. C++ code does not need section information, and throws a fit over certain types such
as anonymous enums inside unions */
#define GB_SECTION(name, ...) __attribute__ ((aligned (8))) __VA_ARGS__
#else
#define GB_SECTION(name, ...) __attribute__ ((aligned (8))) union {uint8_t name##_section_start; struct {__VA_ARGS__};}; uint8_t name##_section_end[0]
#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))
#endif
#define GB_aligned_double __attribute__ ((aligned (8))) double
/* Public calls related to save states */
int GB_save_state(GB_gameboy_t *gb, const char *path);
size_t GB_get_save_state_size(GB_gameboy_t *gb);
/* Assumes buffer is big enough to contain the save state. Use with GB_get_save_state_size(). */
void GB_save_state_to_buffer(GB_gameboy_t *gb, uint8_t *buffer);
int GB_load_state(GB_gameboy_t *gb, const char *path);
int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t length);
#endif /* save_state_h */

View File

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

View File

@ -0,0 +1,67 @@
#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;
typedef 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];
};
} GB_sgb_border_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 */
GB_sgb_border_t border, pending_border;
uint8_t border_animation;
/* Colorization */
uint16_t effective_palettes[4 * 4];
uint16_t ram_palettes[4 * 512];
uint8_t attribute_map[20 * 18];
uint8_t attribute_files[0xFE0];
/* Intro */
int16_t intro_animation;
/* GB Header */
uint8_t received_header[0x54];
/* Multiplayer (cont) */
bool mlt_lock;
};
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value);
void GB_sgb_render(GB_gameboy_t *gb);
void GB_sgb_load_default_data(GB_gameboy_t *gb);
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -0,0 +1,108 @@
#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);
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 unsigned hash_name(const char *name)
{
unsigned 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)
{
unsigned 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)
{
unsigned hash = hash_name(name);
GB_symbol_t *symbol = map->buckets[hash];
while (symbol) {
if (strcmp(symbol->name, name) == 0) return symbol;
symbol = symbol->next;
}
return NULL;
}

View File

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

View File

@ -0,0 +1,327 @@
#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 GB_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)
{
if (gb->stopped) {
gb->apu.apu_cycles += 4 << !gb->cgb_double_speed;
return;
}
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)
{
gb->apu.pcm_mask[0] = gb->apu.pcm_mask[1] = 0xFF; // Sort of hacky, but too many cross-component interactions to do it right
// Affected by speed boost
gb->dma_cycles += cycles;
GB_timers_run(gb, cycles);
if (!gb->stopped) {
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->rumble_state) {
gb->rumble_on_cycles++;
}
else {
gb->rumble_off_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->cartridge_type->mbc_type == GB_HUC3) {
time_t current_time = time(NULL);
while (gb->last_rtc_second / 60 < current_time / 60) {
gb->last_rtc_second += 60;
gb->huc3_minutes++;
if (gb->huc3_minutes == 60 * 24) {
gb->huc3_days++;
gb->huc3_minutes = 0;
}
}
return;
}
if ((gb->rtc_real.high & 0x40) == 0) { /* is timer running? */
time_t current_time = time(NULL);
while (gb->last_rtc_second + 60 * 60 * 24 < current_time) {
gb->last_rtc_second += 60 * 60 * 24;
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;
}
}
while (gb->last_rtc_second < current_time) {
gb->last_rtc_second++;
if (++gb->rtc_real.seconds == 60) {
gb->rtc_real.seconds = 0;
if (++gb->rtc_real.minutes == 60) {
gb->rtc_real.minutes = 0;
if (++gb->rtc_real.hours == 24) {
gb->rtc_real.hours = 0;
if (++gb->rtc_real.days == 0) {
if (gb->rtc_real.high & 1) { /* Bit 8 of days*/
gb->rtc_real.high |= 0x80; /* Overflow bit */
}
gb->rtc_real.high ^= 1;
}
}
}
}
}
}
}

View File

@ -0,0 +1,41 @@
#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_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_STATE_MACHINE(gb, unit, cycles, divisor) \
static const int __state_machine_divisor = divisor;\
(gb)->unit##_cycles += cycles; \
if ((gb)->unit##_cycles <= 0) {\
return;\
}\
switch ((gb)->unit##_state)
#endif
#define GB_STATE(gb, unit, state) case state: goto unit##state
#define GB_UNIT(unit) int32_t unit##_cycles, unit##_state
#endif /* timing_h */

View File

@ -0,0 +1,169 @@
#include "gb.h"
#include <time.h>
static inline uint8_t int_to_bcd(uint8_t i)
{
return (i % 10) + ((i / 10) << 4);
}
static inline uint8_t bcd_to_int(uint8_t i)
{
return (i & 0xF) + (i >> 4) * 10;
}
/*
Note: This peripheral was never released. This is a hacky software reimplementation of it that allows
reaccessing all of the features present in Workboy's ROM. Some of the implementation details are
obviously wrong, but without access to the actual hardware, this is the best I can do.
*/
static void serial_start(GB_gameboy_t *gb, bool bit_received)
{
gb->workboy.byte_being_received <<= 1;
gb->workboy.byte_being_received |= bit_received;
gb->workboy.bits_received++;
if (gb->workboy.bits_received == 8) {
gb->workboy.byte_to_send = 0;
if (gb->workboy.mode != 'W' && gb->workboy.byte_being_received == 'R') {
gb->workboy.byte_to_send = 'D';
gb->workboy.key = GB_WORKBOY_NONE;
gb->workboy.mode = gb->workboy.byte_being_received;
gb->workboy.buffer_index = 1;
time_t time = gb->workboy_get_time_callback(gb);
struct tm tm;
tm = *localtime(&time);
memset(gb->workboy.buffer, 0, sizeof(gb->workboy.buffer));
gb->workboy.buffer[0] = 4; // Unknown, unused, but appears to be expected to be 4
gb->workboy.buffer[2] = int_to_bcd(tm.tm_sec); // Seconds, BCD
gb->workboy.buffer[3] = int_to_bcd(tm.tm_min); // Minutes, BCD
gb->workboy.buffer[4] = int_to_bcd(tm.tm_hour); // Hours, BCD
gb->workboy.buffer[5] = int_to_bcd(tm.tm_mday); // Days, BCD. Upper most 2 bits are added to Year for some reason
gb->workboy.buffer[6] = int_to_bcd(tm.tm_mon + 1); // Months, BCD
gb->workboy.buffer[0xF] = tm.tm_year; // Years, plain number, since 1900
}
else if (gb->workboy.mode != 'W' && gb->workboy.byte_being_received == 'W') {
gb->workboy.byte_to_send = 'D'; // It is actually unknown what this value should be
gb->workboy.key = GB_WORKBOY_NONE;
gb->workboy.mode = gb->workboy.byte_being_received;
gb->workboy.buffer_index = 0;
}
else if (gb->workboy.mode != 'W' && (gb->workboy.byte_being_received == 'O' || gb->workboy.mode == 'O')) {
gb->workboy.mode = 'O';
gb->workboy.byte_to_send = gb->workboy.key;
if (gb->workboy.key != GB_WORKBOY_NONE) {
if (gb->workboy.key & GB_WORKBOY_REQUIRE_SHIFT) {
gb->workboy.key &= ~GB_WORKBOY_REQUIRE_SHIFT;
if (gb->workboy.shift_down) {
gb->workboy.byte_to_send = gb->workboy.key;
gb->workboy.key = GB_WORKBOY_NONE;
}
else {
gb->workboy.byte_to_send = GB_WORKBOY_SHIFT_DOWN;
gb->workboy.shift_down = true;
}
}
else if (gb->workboy.key & GB_WORKBOY_FORBID_SHIFT) {
gb->workboy.key &= ~GB_WORKBOY_FORBID_SHIFT;
if (!gb->workboy.shift_down) {
gb->workboy.byte_to_send = gb->workboy.key;
gb->workboy.key = GB_WORKBOY_NONE;
}
else {
gb->workboy.byte_to_send = GB_WORKBOY_SHIFT_UP;
gb->workboy.shift_down = false;
}
}
else {
if (gb->workboy.key == GB_WORKBOY_SHIFT_DOWN) {
gb->workboy.shift_down = true;
gb->workboy.user_shift_down = true;
}
else if (gb->workboy.key == GB_WORKBOY_SHIFT_UP) {
gb->workboy.shift_down = false;
gb->workboy.user_shift_down = false;
}
gb->workboy.byte_to_send = gb->workboy.key;
gb->workboy.key = GB_WORKBOY_NONE;
}
}
}
else if (gb->workboy.mode == 'R') {
if (gb->workboy.buffer_index / 2 >= sizeof(gb->workboy.buffer)) {
gb->workboy.byte_to_send = 0;
}
else {
if (gb->workboy.buffer_index & 1) {
gb->workboy.byte_to_send = "0123456789ABCDEF"[gb->workboy.buffer[gb->workboy.buffer_index / 2] & 0xF];
}
else {
gb->workboy.byte_to_send = "0123456789ABCDEF"[gb->workboy.buffer[gb->workboy.buffer_index / 2] >> 4];
}
gb->workboy.buffer_index++;
}
}
else if (gb->workboy.mode == 'W') {
gb->workboy.byte_to_send = 'D';
if (gb->workboy.buffer_index < 2) {
gb->workboy.buffer_index++;
}
else if ((gb->workboy.buffer_index - 2) < sizeof(gb->workboy.buffer)) {
gb->workboy.buffer[gb->workboy.buffer_index - 2] = gb->workboy.byte_being_received;
gb->workboy.buffer_index++;
if (gb->workboy.buffer_index - 2 == sizeof(gb->workboy.buffer)) {
struct tm tm = {0,};
tm.tm_sec = bcd_to_int(gb->workboy.buffer[7]);
tm.tm_min = bcd_to_int(gb->workboy.buffer[8]);
tm.tm_hour = bcd_to_int(gb->workboy.buffer[9]);
tm.tm_mday = bcd_to_int(gb->workboy.buffer[0xA]);
tm.tm_mon = bcd_to_int(gb->workboy.buffer[0xB] & 0x3F) - 1;
tm.tm_year = (uint8_t)(gb->workboy.buffer[0x14] + (gb->workboy.buffer[0xA] >> 6)); // What were they thinking?
gb->workboy_set_time_callback(gb, mktime(&tm));
gb->workboy.mode = 'O';
}
}
}
gb->workboy.bits_received = 0;
gb->workboy.byte_being_received = 0;
}
}
static bool serial_end(GB_gameboy_t *gb)
{
bool ret = gb->workboy.bit_to_send;
gb->workboy.bit_to_send = gb->workboy.byte_to_send & 0x80;
gb->workboy.byte_to_send <<= 1;
return ret;
}
void GB_connect_workboy(GB_gameboy_t *gb,
GB_workboy_set_time_callback set_time_callback,
GB_workboy_get_time_callback get_time_callback)
{
memset(&gb->workboy, 0, sizeof(gb->workboy));
GB_set_serial_transfer_bit_start_callback(gb, serial_start);
GB_set_serial_transfer_bit_end_callback(gb, serial_end);
gb->workboy_set_time_callback = set_time_callback;
gb->workboy_get_time_callback = get_time_callback;
}
bool GB_workboy_is_enabled(GB_gameboy_t *gb)
{
return gb->workboy.mode;
}
void GB_workboy_set_key(GB_gameboy_t *gb, uint8_t key)
{
if (gb->workboy.user_shift_down != gb->workboy.shift_down &&
(key & (GB_WORKBOY_REQUIRE_SHIFT | GB_WORKBOY_FORBID_SHIFT)) == 0) {
if (gb->workboy.user_shift_down) {
key |= GB_WORKBOY_REQUIRE_SHIFT;
}
else {
key |= GB_WORKBOY_FORBID_SHIFT;
}
}
gb->workboy.key = key;
}

View File

@ -0,0 +1,118 @@
#ifndef workboy_h
#define workboy_h
#include <stdint.h>
#include <stdbool.h>
#include <time.h>
#include "gb_struct_def.h"
typedef struct {
uint8_t byte_to_send;
bool bit_to_send;
uint8_t byte_being_received;
uint8_t bits_received;
uint8_t mode;
uint8_t key;
bool shift_down;
bool user_shift_down;
uint8_t buffer[0x15];
uint8_t buffer_index; // In nibbles during read, in bytes during write
} GB_workboy_t;
typedef void (*GB_workboy_set_time_callback)(GB_gameboy_t *gb, time_t time);
typedef time_t (*GB_workboy_get_time_callback)(GB_gameboy_t *gb);
enum {
GB_WORKBOY_NONE = 0xFF,
GB_WORKBOY_REQUIRE_SHIFT = 0x40,
GB_WORKBOY_FORBID_SHIFT = 0x80,
GB_WORKBOY_CLOCK = 1,
GB_WORKBOY_TEMPERATURE = 2,
GB_WORKBOY_MONEY = 3,
GB_WORKBOY_CALCULATOR = 4,
GB_WORKBOY_DATE = 5,
GB_WORKBOY_CONVERSION = 6,
GB_WORKBOY_RECORD = 7,
GB_WORKBOY_WORLD = 8,
GB_WORKBOY_PHONE = 9,
GB_WORKBOY_ESCAPE = 10,
GB_WORKBOY_BACKSPACE = 11,
GB_WORKBOY_UNKNOWN = 12,
GB_WORKBOY_LEFT = 13,
GB_WORKBOY_Q = 17,
GB_WORKBOY_1 = 17 | GB_WORKBOY_REQUIRE_SHIFT,
GB_WORKBOY_W = 18,
GB_WORKBOY_2 = 18 | GB_WORKBOY_REQUIRE_SHIFT,
GB_WORKBOY_E = 19,
GB_WORKBOY_3 = 19 | GB_WORKBOY_REQUIRE_SHIFT,
GB_WORKBOY_R = 20,
GB_WORKBOY_T = 21,
GB_WORKBOY_Y = 22 ,
GB_WORKBOY_U = 23 ,
GB_WORKBOY_I = 24,
GB_WORKBOY_EXCLAMATION_MARK = 24 | GB_WORKBOY_REQUIRE_SHIFT,
GB_WORKBOY_O = 25,
GB_WORKBOY_TILDE = 25 | GB_WORKBOY_REQUIRE_SHIFT,
GB_WORKBOY_P = 26,
GB_WORKBOY_ASTERISK = 26 | GB_WORKBOY_REQUIRE_SHIFT,
GB_WORKBOY_DOLLAR = 27 | GB_WORKBOY_FORBID_SHIFT,
GB_WORKBOY_HASH = 27 | GB_WORKBOY_REQUIRE_SHIFT,
GB_WORKBOY_A = 28,
GB_WORKBOY_4 = 28 | GB_WORKBOY_REQUIRE_SHIFT,
GB_WORKBOY_S = 29,
GB_WORKBOY_5 = 29 | GB_WORKBOY_REQUIRE_SHIFT,
GB_WORKBOY_D = 30,
GB_WORKBOY_6 = 30 | GB_WORKBOY_REQUIRE_SHIFT,
GB_WORKBOY_F = 31,
GB_WORKBOY_PLUS = 31 | GB_WORKBOY_REQUIRE_SHIFT,
GB_WORKBOY_G = 32,
GB_WORKBOY_MINUS = 32 | GB_WORKBOY_REQUIRE_SHIFT,
GB_WORKBOY_H = 33,
GB_WORKBOY_J = 34,
GB_WORKBOY_K = 35,
GB_WORKBOY_LEFT_PARENTHESIS = 35 | GB_WORKBOY_REQUIRE_SHIFT,
GB_WORKBOY_L = 36,
GB_WORKBOY_RIGHT_PARENTHESIS = 36 | GB_WORKBOY_REQUIRE_SHIFT,
GB_WORKBOY_SEMICOLON = 37 | GB_WORKBOY_FORBID_SHIFT,
GB_WORKBOY_COLON = 37,
GB_WORKBOY_ENTER = 38,
GB_WORKBOY_SHIFT_DOWN = 39,
GB_WORKBOY_Z = 40,
GB_WORKBOY_7 = 40 | GB_WORKBOY_REQUIRE_SHIFT,
GB_WORKBOY_X = 41,
GB_WORKBOY_8 = 41 | GB_WORKBOY_REQUIRE_SHIFT,
GB_WORKBOY_C = 42,
GB_WORKBOY_9 = 42 | GB_WORKBOY_REQUIRE_SHIFT,
GB_WORKBOY_V = 43,
GB_WORKBOY_DECIMAL_POINT = 43 | GB_WORKBOY_REQUIRE_SHIFT,
GB_WORKBOY_B = 44,
GB_WORKBOY_PERCENT = 44 | GB_WORKBOY_REQUIRE_SHIFT,
GB_WORKBOY_N = 45,
GB_WORKBOY_EQUAL = 45 | GB_WORKBOY_REQUIRE_SHIFT,
GB_WORKBOY_M = 46,
GB_WORKBOY_COMMA = 47 | GB_WORKBOY_FORBID_SHIFT,
GB_WORKBOY_LT = 47 | GB_WORKBOY_REQUIRE_SHIFT,
GB_WORKBOY_DOT = 48 | GB_WORKBOY_FORBID_SHIFT,
GB_WORKBOY_GT = 48 | GB_WORKBOY_REQUIRE_SHIFT,
GB_WORKBOY_SLASH = 49 | GB_WORKBOY_FORBID_SHIFT,
GB_WORKBOY_QUESTION_MARK = 49 | GB_WORKBOY_REQUIRE_SHIFT,
GB_WORKBOY_SHIFT_UP = 50,
GB_WORKBOY_0 = 51 | GB_WORKBOY_REQUIRE_SHIFT,
GB_WORKBOY_UMLAUT = 51,
GB_WORKBOY_SPACE = 52,
GB_WORKBOY_QUOTE = 53 | GB_WORKBOY_FORBID_SHIFT,
GB_WORKBOY_AT = 53 | GB_WORKBOY_REQUIRE_SHIFT,
GB_WORKBOY_UP = 54,
GB_WORKBOY_DOWN = 55,
GB_WORKBOY_RIGHT = 56,
};
void GB_connect_workboy(GB_gameboy_t *gb,
GB_workboy_set_time_callback set_time_callback,
GB_workboy_get_time_callback get_time_callback);
bool GB_workboy_is_enabled(GB_gameboy_t *gb);
void GB_workboy_set_key(GB_gameboy_t *gb, uint8_t key);
#endif

View File

@ -0,0 +1,33 @@
namespace Heuristics {
struct BSMemory {
BSMemory(vector<uint8_t>& data, string location);
explicit operator bool() const;
auto manifest() const -> string;
private:
vector<uint8_t>& data;
string location;
};
BSMemory::BSMemory(vector<uint8_t>& data, string location) : data(data), location(location) {
}
BSMemory::operator bool() const {
return data.size() >= 0x8000;
}
auto BSMemory::manifest() const -> string {
if(!operator bool()) return {};
string output;
output.append("game\n");
output.append(" sha256: ", Hash::SHA256(data).digest(), "\n");
output.append(" label: ", Location::prefix(location), "\n");
output.append(" name: ", Location::prefix(location), "\n");
output.append(" board\n");
output.append(Memory{}.type("Flash").size(data.size()).content("Program").text());
return output;
}
}

View File

@ -0,0 +1,298 @@
namespace Heuristics {
struct GameBoy {
GameBoy(vector<uint8_t>& data, string location);
explicit operator bool() const;
auto manifest() const -> string;
private:
auto read(uint offset) const -> uint8_t { return data[headerAddress + offset]; }
vector<uint8_t>& data;
string location;
uint headerAddress = 0;
};
GameBoy::GameBoy(vector<uint8_t>& data, string location) : data(data), location(location) {
headerAddress = data.size() < 0x8000 ? data.size() : data.size() - 0x8000;
if(read(0x0104) == 0xce && read(0x0105) == 0xed && read(0x0106) == 0x66 && read(0x0107) == 0x66
&& read(0x0108) == 0xcc && read(0x0109) == 0x0d && read(0x0147) >= 0x0b && read(0x0147) <= 0x0d
) { //MMM01 stores header at bottom of data[]
} else { //all other mappers store header at top of data[]
headerAddress = 0;
}
}
GameBoy::operator bool() const {
return data.size() >= 0x4000;
}
auto GameBoy::manifest() const -> string {
if(!operator bool()) return {};
bool black = (read(0x0143) & 0xc0) == 0x80; //cartridge works in DMG+CGB mode
bool clear = (read(0x0143) & 0xc0) == 0xc0; //cartridge works in CGB mode only
bool ram = false;
bool battery = false;
bool eeprom = false;
bool flash = false;
bool rtc = false;
bool accelerometer = false;
bool rumble = false;
uint romSize = 0;
uint ramSize = 0;
uint eepromSize = 0;
uint flashSize = 0;
uint rtcSize = 0;
string mapper = "MBC0";
switch(read(0x0147)) {
case 0x00:
mapper = "MBC0";
break;
case 0x01:
mapper = "MBC1";
break;
case 0x02:
mapper = "MBC1";
ram = true;
break;
case 0x03:
mapper = "MBC1";
battery = true;
ram = true;
break;
case 0x05:
mapper = "MBC2";
ram = true;
break;
case 0x06:
mapper = "MBC2";
battery = true;
ram = true;
break;
case 0x08:
mapper = "MBC0";
ram = true;
break;
case 0x09:
mapper = "MBC0";
battery = true;
ram = true;
break;
case 0x0b:
mapper = "MMM01";
break;
case 0x0c:
mapper = "MMM01";
ram = true;
break;
case 0x0d:
mapper = "MMM01";
battery = true;
ram = true;
break;
case 0x0f:
mapper = "MBC3";
battery = true;
rtc = true;
break;
case 0x10:
mapper = "MBC3";
battery = true;
ram = true;
rtc = true;
break;
case 0x11:
mapper = "MBC3";
break;
case 0x12:
mapper = "MBC3";
ram = true;
break;
case 0x13:
mapper = "MBC3";
battery = true;
ram = true;
break;
case 0x19:
mapper = "MBC5";
break;
case 0x1a:
mapper = "MBC5";
ram = true;
break;
case 0x1b:
mapper = "MBC5";
battery = true;
ram = true;
break;
case 0x1c:
mapper = "MBC5";
rumble = true;
break;
case 0x1d:
mapper = "MBC5";
ram = true;
rumble = true;
break;
case 0x1e:
mapper = "MBC5";
battery = true;
ram = true;
rumble = true;
break;
case 0x20:
mapper = "MBC6";
flash = true;
battery = true;
ram = true;
break;
case 0x22:
mapper = "MBC7";
battery = true;
eeprom = true;
accelerometer = true;
rumble = true;
break;
case 0xfc:
mapper = "CAMERA";
break;
case 0xfd:
mapper = "TAMA";
battery = true;
ram = true;
rtc = true;
break;
case 0xfe:
mapper = "HuC3";
break;
case 0xff:
mapper = "HuC1";
battery = true;
ram = true;
break;
}
//Game Boy: title = $0134-0143
//Game Boy Color (early games): title = $0134-0142; model = $0143
//Game Boy Color (later games): title = $0134-013e; serial = $013f-0142; model = $0143
string title;
for(uint n : range(black || clear ? 15 : 16)) {
char byte = read(0x0134 + n);
if(byte < 0x20 || byte > 0x7e) byte = ' ';
title.append(byte);
}
string serial = title.slice(-4);
if(!black && !clear) serial = "";
for(auto& byte : serial) {
if(byte >= 'A' && byte <= 'Z') continue;
//invalid serial
serial = "";
break;
}
title.trimRight(serial, 1L); //remove the serial from the title, if it exists
title.strip(); //remove any excess whitespace from the title
switch(read(0x0148)) { default:
case 0x00: romSize = 2 * 16 * 1024; break;
case 0x01: romSize = 4 * 16 * 1024; break;
case 0x02: romSize = 8 * 16 * 1024; break;
case 0x03: romSize = 16 * 16 * 1024; break;
case 0x04: romSize = 32 * 16 * 1024; break;
case 0x05: romSize = 64 * 16 * 1024; break;
case 0x06: romSize = 128 * 16 * 1024; break;
case 0x07: romSize = 256 * 16 * 1024; break;
case 0x52: romSize = 72 * 16 * 1024; break;
case 0x53: romSize = 80 * 16 * 1024; break;
case 0x54: romSize = 96 * 16 * 1024; break;
}
switch(read(0x0149)) { default:
case 0x00: ramSize = 0 * 1024; break;
case 0x01: ramSize = 2 * 1024; break;
case 0x02: ramSize = 8 * 1024; break;
case 0x03: ramSize = 32 * 1024; break;
}
if(mapper == "MBC2" && ram) ramSize = 256;
if(mapper == "MBC6" && ram) ramSize = 32 * 1024;
if(mapper == "TAMA" && ram) ramSize = 32;
if(mapper == "MBC6" && flash) flashSize = 1024 * 1024;
//Game Boy header does not specify EEPROM size: detect via game title instead
//Command Master: EEPROM = 512 bytes
//Kirby Tilt 'n' Tumble: EEPROM = 256 bytes
//Korokoro Kirby: EEPROM = 256 bytes
if(mapper == "MBC7" && eeprom) {
eepromSize = 256; //fallback guess; supported values are 128, 256, 512
if(title == "CMASTER" && serial == "KCEJ") eepromSize = 512;
if(title == "KIRBY TNT" && serial == "KTNE") eepromSize = 256;
if(title == "KORO2 KIRBY" && serial == "KKKJ") eepromSize = 256;
}
if(mapper == "MBC3" && rtc) rtcSize = 13;
if(mapper == "TAMA" && rtc) rtcSize = 21;
string output;
output.append("game\n");
output.append(" sha256: ", Hash::SHA256(data).digest(), "\n");
output.append(" label: ", Location::prefix(location), "\n");
output.append(" name: ", Location::prefix(location), "\n");
output.append(" title: ", title, "\n");
if(serial)
output.append(" serial: ", serial, "\n");
output.append(" board: ", mapper, "\n");
output.append(Memory{}.type("ROM").size(data.size()).content("Program").text());
if(ram && ramSize && battery)
output.append(Memory{}.type("RAM").size(ramSize).content("Save").text());
if(ram && ramSize && !battery)
output.append(Memory{}.type("RAM").size(ramSize).content("Save").isVolatile().text());
if(eeprom && eepromSize)
output.append(Memory{}.type("EEPROM").size(eepromSize).content("Save").text());
if(flash && flashSize)
output.append(Memory{}.type("Flash").size(flashSize).content("Download").text());
if(rtc && rtcSize)
output.append(Memory{}.type("RTC").size(rtcSize).content("Time").text());
if(accelerometer)
output.append(" accelerometer\n");
if(rumble)
output.append(" rumble\n");
return output;
}
}

View File

@ -0,0 +1,34 @@
namespace Heuristics {
auto Memory::text() const -> string {
string output;
output.append(" memory\n");
output.append(" type: ", _type, "\n");
output.append(" size: 0x", hex(_size), "\n");
output.append(" content: ", _content, "\n");
if(_manufacturer)
output.append(" manufacturer: ", _manufacturer, "\n");
if(_architecture)
output.append(" architecture: ", _architecture, "\n");
if(_identifier)
output.append(" identifier: ", _identifier, "\n");
if(_volatile)
output.append(" volatile\n");
return output;
}
auto Oscillator::text() const -> string {
string output;
output.append(" oscillator\n");
output.append(" frequency: ", _frequency, "\n");
return output;
}
auto Slot::text() const -> string {
string output;
output.append(" slot\n");
output.append(" type: ", _type, "\n");
return output;
}
}

View File

@ -0,0 +1,37 @@
namespace Heuristics {
struct Memory {
auto& type(string type) { _type = type; return *this; }
auto& size(natural size) { _size = size; return *this; }
auto& content(string content) { _content = content; return *this; }
auto& manufacturer(string manufacturer) { _manufacturer = manufacturer; return *this; }
auto& architecture(string architecture) { _architecture = architecture; return *this; }
auto& identifier(string identifier) { _identifier = identifier; return *this; }
auto& isVolatile() { _volatile = true; return *this; }
auto text() const -> string;
string _type;
boolean _battery;
natural _size;
string _content;
string _manufacturer;
string _architecture;
string _identifier;
boolean _volatile;
};
struct Oscillator {
auto& frequency(natural frequency) { _frequency = frequency; return *this; }
auto text() const -> string;
natural _frequency;
};
struct Slot {
auto& type(string type) { _type = type; return *this; }
auto text() const -> string;
string _type;
};
}

View File

@ -0,0 +1,605 @@
namespace Heuristics {
struct SuperFamicom {
SuperFamicom(vector<uint8_t>& data, string location);
explicit operator bool() const;
auto manifest() const -> string;
auto region() const -> string;
auto videoRegion() const -> string;
auto revision() const -> string;
auto board() const -> string;
auto title() const -> string;
auto serial() const -> string;
auto romSize() const -> uint;
auto programRomSize() const -> uint;
auto dataRomSize() const -> uint;
auto expansionRomSize() const -> uint;
auto firmwareRomSize() const -> uint;
auto ramSize() const -> uint;
auto expansionRamSize() const -> uint;
auto nonVolatile() const -> bool;
private:
auto size() const -> uint { return data.size(); }
auto scoreHeader(uint address) -> uint;
auto firmwareARM() const -> string;
auto firmwareEXNEC() const -> string;
auto firmwareGB() const -> string;
auto firmwareHITACHI() const -> string;
auto firmwareNEC() const -> string;
vector<uint8_t>& data;
string location;
uint headerAddress = 0;
};
SuperFamicom::SuperFamicom(vector<uint8_t>& data, string location) : data(data), location(location) {
if((size() & 0x7fff) == 512) {
//remove header if present
memory::move(&data[0], &data[512], size() - 512);
data.resize(size() - 512);
}
if(size() < 0x8000) return; //ignore images too small to be valid
uint LoROM = scoreHeader( 0x7fb0);
uint HiROM = scoreHeader( 0xffb0);
uint ExLoROM = scoreHeader(0x407fb0);
uint ExHiROM = scoreHeader(0x40ffb0);
if(ExLoROM) ExLoROM += 4;
if(ExHiROM) ExHiROM += 4;
if(LoROM >= HiROM && LoROM >= ExLoROM && LoROM >= ExHiROM) headerAddress = 0x7fb0;
else if(HiROM >= ExLoROM && HiROM >= ExHiROM) headerAddress = 0xffb0;
else if(ExLoROM >= ExHiROM) headerAddress = 0x407fb0;
else headerAddress = 0x40ffb0;
}
SuperFamicom::operator bool() const {
return headerAddress;
}
auto SuperFamicom::manifest() const -> string {
if(!operator bool()) return {};
string output;
output.append("game\n");
output.append(" sha256: ", Hash::SHA256(data).digest(), "\n");
output.append(" label: ", Location::prefix(location), "\n");
output.append(" name: ", Location::prefix(location), "\n");
output.append(" title: ", title(), "\n");
output.append(" region: ", region(), "\n");
output.append(" revision: ", revision(), "\n");
output.append(" board: ", board(), "\n");
auto board = this->board().trimRight("#A", 1L).split("-");
if(auto size = romSize()) {
if(board(0) == "SPC7110" && size > 0x100000) {
output.append(Memory{}.type("ROM").size(0x100000).content("Program").text());
output.append(Memory{}.type("ROM").size(size - 0x100000).content("Data").text());
} else if(board(0) == "EXSPC7110" && size == 0x700000) {
//Tengai Maykou Zero (fan translation)
output.append(Memory{}.type("ROM").size(0x100000).content("Program").text());
output.append(Memory{}.type("ROM").size(0x500000).content("Data").text());
output.append(Memory{}.type("ROM").size(0x100000).content("Expansion").text());
} else {
output.append(Memory{}.type("ROM").size(size).content("Program").text());
}
}
if(auto size = ramSize()) {
output.append(Memory{}.type("RAM").size(size).content("Save").text());
}
if(auto size = expansionRamSize()) {
output.append(Memory{}.type("RAM").size(size).content("Save").text());
}
if(0) {
} else if(board(0) == "ARM") {
output.append(Memory{}.type("ROM").size(0x20000).content("Program").manufacturer("SETA").architecture("ARM6").identifier(firmwareARM()).text());
output.append(Memory{}.type("ROM").size( 0x8000).content("Data" ).manufacturer("SETA").architecture("ARM6").identifier(firmwareARM()).text());
output.append(Memory{}.type("RAM").size( 0x4000).content("Data" ).manufacturer("SETA").architecture("ARM6").identifier(firmwareARM()).isVolatile().text());
output.append(Oscillator{}.frequency(21'440'000).text());
} else if(board(0) == "BS" && board(1) == "MCC") {
output.append(Memory{}.type("RAM").size(0x80000).content("Download").text());
} else if(board(0) == "EXNEC") {
output.append(Memory{}.type("ROM").size(0xc000).content("Program").manufacturer("NEC").architecture("uPD96050").identifier(firmwareEXNEC()).text());
output.append(Memory{}.type("ROM").size(0x1000).content("Data" ).manufacturer("NEC").architecture("uPD96050").identifier(firmwareEXNEC()).text());
output.append(Memory{}.type("RAM").size(0x1000).content("Data" ).manufacturer("NEC").architecture("uPD96050").identifier(firmwareEXNEC()).text());
output.append(Oscillator{}.frequency(firmwareEXNEC() == "ST010" ? 11'000'000 : 15'000'000).text());
} else if(board(0) == "GB") {
output.append(Memory{}.type("ROM").size(0x100).content("Boot").manufacturer("Nintendo").architecture("LR35902").identifier(firmwareGB()).text());
if(firmwareGB() == "SGB2")
output.append(Oscillator{}.frequency(20'971'520).text());
} else if(board(0) == "GSU") {
//todo: MARIO CHIP 1 uses CPU oscillator
output.append(Oscillator{}.frequency(21'440'000).text());
} else if(board(0) == "HITACHI") {
output.append(Memory{}.type("ROM").size(0xc00).content("Data").manufacturer("Hitachi").architecture("HG51BS169").identifier(firmwareHITACHI()).text());
output.append(Memory{}.type("RAM").size(0xc00).content("Data").manufacturer("Hitachi").architecture("HG51BS169").identifier(firmwareHITACHI()).isVolatile().text());
output.append(Oscillator{}.frequency(20'000'000).text());
} else if(board(0) == "NEC") {
output.append(Memory{}.type("ROM").size(0x1800).content("Program").manufacturer("NEC").architecture("uPD7725").identifier(firmwareNEC()).text());
output.append(Memory{}.type("ROM").size( 0x800).content("Data" ).manufacturer("NEC").architecture("uPD7725").identifier(firmwareNEC()).text());
output.append(Memory{}.type("RAM").size( 0x200).content("Data" ).manufacturer("NEC").architecture("uPD7725").identifier(firmwareNEC()).isVolatile().text());
output.append(Oscillator{}.frequency(7'600'000).text());
} else if(board(0) == "SA1" || board(1) == "SA1") { //SA1-* or BS-SA1-*
output.append(Memory{}.type("RAM").size(0x800).content("Internal").isVolatile().text());
}
if(board.right() == "EPSONRTC") {
output.append(Memory{}.type("RTC").size(0x10).content("Time").manufacturer("Epson").text());
} else if(board.right() == "SHARPRTC") {
output.append(Memory{}.type("RTC").size(0x10).content("Time").manufacturer("Sharp").text());
}
return output;
}
auto SuperFamicom::region() const -> string {
//Unlicensed software (homebrew, ROM hacks, etc) often change the standard region code,
//and then neglect to change the extended header region code. Thanks to that, we can't
//decode and display the full game serial + region code.
return videoRegion();
string region;
char A = data[headerAddress + 0x02]; //game type
char B = data[headerAddress + 0x03]; //game code
char C = data[headerAddress + 0x04]; //game code
char D = data[headerAddress + 0x05]; //region code (new; sometimes ambiguous)
auto E = data[headerAddress + 0x29]; //region code (old)
auto valid = [](char n) { return (n >= '0' && n <= '9') || (n >= 'A' && n <= 'Z'); };
if(data[headerAddress + 0x2a] == 0x33 && valid(A) && valid(B) & valid(C) & valid(D)) {
string code{A, B, C, D};
if(D == 'B') region = {"SNS-", code, "-BRA"};
if(D == 'C') region = {"SNSN-", code, "-ROC"};
if(D == 'D') region = {"SNSP-", code, "-NOE"};
if(D == 'E') region = {"SNS-", code, "-USA"};
if(D == 'F') region = {"SNSP-", code, "-FRA"};
if(D == 'H') region = {"SNSP-", code, "-HOL"};
if(D == 'I') region = {"SNSP-", code, "-ITA"};
if(D == 'J') region = {"SHVC-", code, "-JPN"};
if(D == 'K') region = {"SNSN-", code, "-KOR"};
if(D == 'N') region = {"SNS-", code, "-CAN"};
if(D == 'P') region = {"SNSP-", code, "-EUR"};
if(D == 'S') region = {"SNSP-", code, "-ESP"};
if(D == 'U') region = {"SNSP-", code, "-AUS"};
if(D == 'X') region = {"SNSP-", code, "-SCN"};
}
if(!region) {
if(E == 0x00) region = {"JPN"};
if(E == 0x01) region = {"USA"};
if(E == 0x02) region = {"EUR"};
if(E == 0x03) region = {"SCN"};
if(E == 0x06) region = {"FRA"};
if(E == 0x07) region = {"HOL"};
if(E == 0x08) region = {"ESP"};
if(E == 0x09) region = {"NOE"};
if(E == 0x0a) region = {"ITA"};
if(E == 0x0b) region = {"ROC"};
if(E == 0x0d) region = {"KOR"};
if(E == 0x0f) region = {"CAN"};
if(E == 0x10) region = {"BRA"};
if(E == 0x11) region = {"AUS"};
if(E == 0x12) region = {"SCN"};
}
return region ? region : "NTSC";
}
auto SuperFamicom::videoRegion() const -> string {
auto region = data[headerAddress + 0x29];
if(region == 0x00) return "NTSC"; //JPN
if(region == 0x01) return "NTSC"; //USA
if(region == 0x0b) return "NTSC"; //ROC
if(region == 0x0d) return "NTSC"; //KOR
if(region == 0x0f) return "NTSC"; //CAN
if(region == 0x10) return "NTSC"; //BRA
return "PAL";
}
auto SuperFamicom::revision() const -> string {
string revision;
char A = data[headerAddress + 0x02]; //game type
char B = data[headerAddress + 0x03]; //game code
char C = data[headerAddress + 0x04]; //game code
char D = data[headerAddress + 0x05]; //region code (new; sometimes ambiguous)
auto E = data[headerAddress + 0x29]; //region code (old)
uint F = data[headerAddress + 0x2b]; //revision code
auto valid = [](char n) { return (n >= '0' && n <= '9') || (n >= 'A' && n <= 'Z'); };
if(data[headerAddress + 0x2a] == 0x33 && valid(A) && valid(B) & valid(C) & valid(D)) {
string code{A, B, C, D};
if(D == 'B') revision = {"SNS-", code, "-", F};
if(D == 'C') revision = {"SNSN-", code, "-", F};
if(D == 'D') revision = {"SNSP-", code, "-", F};
if(D == 'E') revision = {"SNS-", code, "-", F};
if(D == 'F') revision = {"SNSP-", code, "-", F};
if(D == 'H') revision = {"SNSP-", code, "-", F};
if(D == 'I') revision = {"SNSP-", code, "-", F};
if(D == 'J') revision = {"SHVC-", code, "-", F};
if(D == 'K') revision = {"SNSN-", code, "-", F};
if(D == 'N') revision = {"SNS-", code, "-", F};
if(D == 'P') revision = {"SNSP-", code, "-", F};
if(D == 'S') revision = {"SNSP-", code, "-", F};
if(D == 'U') revision = {"SNSP-", code, "-", F};
if(D == 'X') revision = {"SNSP-", code, "-", F};
}
if(!revision) {
revision = {"1.", F};
}
return revision ? revision : string{"1.", F};
}
//format: [slot]-[coprocessor]-[mapper]-[ram]-[rtc]
auto SuperFamicom::board() const -> string {
string board;
auto mapMode = data[headerAddress + 0x25];
auto cartridgeTypeLo = data[headerAddress + 0x26] & 15;
auto cartridgeTypeHi = data[headerAddress + 0x26] >> 4;
auto cartridgeSubType = data[headerAddress + 0x0f];
string mode;
if(mapMode == 0x20 || mapMode == 0x30) mode = "LOROM-";
if(mapMode == 0x21 || mapMode == 0x31) mode = "HIROM-";
if(mapMode == 0x22 || mapMode == 0x32) mode = "SDD1-";
if(mapMode == 0x23 || mapMode == 0x33) mode = "SA1-";
if(mapMode == 0x25 || mapMode == 0x35) mode = "EXHIROM-";
if(mapMode == 0x2a || mapMode == 0x3a) mode = "SPC7110-";
//many games will store an extra title character, overwriting the map mode
//further, ExLoROM mode is unofficial, and lacks a mapping mode value
if(!mode) {
if(headerAddress == 0x7fb0) mode = "LOROM-";
if(headerAddress == 0xffb0) mode = "HIROM-";
if(headerAddress == 0x407fb0) mode = "EXLOROM-";
if(headerAddress == 0x40ffb0) mode = "EXHIROM-";
}
//this game's title ovewrites the map mode with '!' (0x21), but is a LOROM game
if(title() == "YUYU NO QUIZ DE GO!GO") mode = "LOROM-";
if(mode == "LOROM-" && headerAddress == 0x407fb0) mode = "EXLOROM-";
bool epsonRTC = false;
bool sharpRTC = false;
if(serial() == "A9PJ") {
//Sufami Turbo (JPN)
board.append("ST-", mode);
} else if(serial() == "ZBSJ") {
//BS-X: Sore wa Namae o Nusumareta Machi no Monogatari (JPN)
board.append("BS-MCC-");
} else if(serial() == "042J") {
//Super Game Boy 2
board.append("GB-", mode);
} else if(serial().match("Z??J")) {
board.append("BS-", mode);
} else if(cartridgeTypeLo >= 0x3) {
if(cartridgeTypeHi == 0x0) board.append("NEC-", mode);
if(cartridgeTypeHi == 0x1) board.append("GSU-");
if(cartridgeTypeHi == 0x2) board.append("OBC1-", mode);
if(cartridgeTypeHi == 0x3) board.append("SA1-");
if(cartridgeTypeHi == 0x4) board.append("SDD1-");
if(cartridgeTypeHi == 0x5) board.append(mode), sharpRTC = true;
if(cartridgeTypeHi == 0xe && cartridgeTypeLo == 0x3) board.append("GB-", mode);
if(cartridgeTypeHi == 0xf && cartridgeTypeLo == 0x5 && cartridgeSubType == 0x00) board.append("SPC7110-");
if(cartridgeTypeHi == 0xf && cartridgeTypeLo == 0x9 && cartridgeSubType == 0x00) board.append("SPC7110-"), epsonRTC = true;
if(cartridgeTypeHi == 0xf && cartridgeSubType == 0x01) board.append("EXNEC-", mode);
if(cartridgeTypeHi == 0xf && cartridgeSubType == 0x02) board.append("ARM-", mode);
if(cartridgeTypeHi == 0xf && cartridgeSubType == 0x10) board.append("HITACHI-", mode);
}
if(!board) board.append(mode);
if(ramSize() || expansionRamSize()) board.append("RAM-");
if(epsonRTC) board.append("EPSONRTC-");
if(sharpRTC) board.append("SHARPRTC-");
board.trimRight("-", 1L);
if(board.beginsWith( "LOROM-RAM") && romSize() <= 0x200000) board.append("#A");
if(board.beginsWith("NEC-LOROM-RAM") && romSize() <= 0x100000) board.append("#A");
//Tengai Makyou Zero (fan translation)
if(board.beginsWith("SPC7110-") && data.size() == 0x700000) board.prepend("EX");
return board;
}
auto SuperFamicom::title() const -> string {
string label;
for(uint n = 0; n < 0x15; n++) {
auto x = data[headerAddress + 0x10 + n];
auto y = n == 0x14 ? 0 : data[headerAddress + 0x11 + n];
//null terminator (padding)
if(x == 0x00 || x == 0xff);
//ASCII
else if(x >= 0x20 && x <= 0x7e) label.append((char)x);
//Shift-JIS (half-width katakana)
else if(x == 0xa1) label.append("");
else if(x == 0xa2) label.append("");
else if(x == 0xa3) label.append("");
else if(x == 0xa4) label.append("");
else if(x == 0xa5) label.append("");
else if(x == 0xa6) label.append("");
else if(x == 0xa7) label.append("");
else if(x == 0xa8) label.append("");
else if(x == 0xa9) label.append("");
else if(x == 0xaa) label.append("");
else if(x == 0xab) label.append("");
else if(x == 0xac) label.append("");
else if(x == 0xad) label.append("");
else if(x == 0xae) label.append("");
else if(x == 0xaf) label.append("");
else if(x == 0xb0) label.append("");
else if(x == 0xb1) label.append( "");
else if(x == 0xb2) label.append( "");
else if(x == 0xb3) label.append(y == 0xde ? "" : "");
else if(x == 0xb4) label.append( "");
else if(x == 0xb5) label.append( "");
else if(x == 0xb6) label.append(y == 0xde ? "" : "");
else if(x == 0xb7) label.append(y == 0xde ? "" : "");
else if(x == 0xb8) label.append(y == 0xde ? "" : "");
else if(x == 0xb9) label.append(y == 0xde ? "" : "");
else if(x == 0xba) label.append(y == 0xde ? "" : "");
else if(x == 0xbb) label.append(y == 0xde ? "" : "");
else if(x == 0xbc) label.append(y == 0xde ? "" : "");
else if(x == 0xbd) label.append(y == 0xde ? "" : "");
else if(x == 0xbe) label.append(y == 0xde ? "" : "");
else if(x == 0xbf) label.append(y == 0xde ? "" : "");
else if(x == 0xc0) label.append(y == 0xde ? "" : "");
else if(x == 0xc1) label.append(y == 0xde ? "" : "");
else if(x == 0xc2) label.append(y == 0xde ? "" : "");
else if(x == 0xc3) label.append(y == 0xde ? "" : "");
else if(x == 0xc4) label.append(y == 0xde ? "" : "");
else if(x == 0xc5) label.append("");
else if(x == 0xc6) label.append("");
else if(x == 0xc7) label.append("");
else if(x == 0xc8) label.append("");
else if(x == 0xc9) label.append("");
else if(x == 0xca) label.append(y == 0xdf ? "" : y == 0xde ? "" : "");
else if(x == 0xcb) label.append(y == 0xdf ? "" : y == 0xde ? "" : "");
else if(x == 0xcc) label.append(y == 0xdf ? "" : y == 0xde ? "" : "");
else if(x == 0xcd) label.append(y == 0xdf ? "" : y == 0xde ? "" : "");
else if(x == 0xce) label.append(y == 0xdf ? "" : y == 0xde ? "" : "");
else if(x == 0xcf) label.append("");
else if(x == 0xd0) label.append("");
else if(x == 0xd1) label.append("");
else if(x == 0xd2) label.append("");
else if(x == 0xd3) label.append("");
else if(x == 0xd4) label.append("");
else if(x == 0xd5) label.append("");
else if(x == 0xd6) label.append("");
else if(x == 0xd7) label.append("");
else if(x == 0xd8) label.append("");
else if(x == 0xd9) label.append("");
else if(x == 0xda) label.append("");
else if(x == 0xdb) label.append("");
else if(x == 0xdc) label.append("");
else if(x == 0xdd) label.append("");
else if(x == 0xde) label.append("\xef\xbe\x9e"); //dakuten
else if(x == 0xdf) label.append("\xef\xbe\x9f"); //handakuten
//unknown
else label.append("?");
//(han)dakuten skip
if(y == 0xde && x == 0xb3) n++;
if(y == 0xde && x >= 0xb6 && x <= 0xc4) n++;
if(y == 0xde && x >= 0xca && x <= 0xce) n++;
if(y == 0xdf && x >= 0xca && y <= 0xce) n++;
}
return label.strip();
}
auto SuperFamicom::serial() const -> string {
char A = data[headerAddress + 0x02]; //game type
char B = data[headerAddress + 0x03]; //game code
char C = data[headerAddress + 0x04]; //game code
char D = data[headerAddress + 0x05]; //region code (new; sometimes ambiguous)
auto valid = [](char n) { return (n >= '0' && n <= '9') || (n >= 'A' && n <= 'Z'); };
if(data[headerAddress + 0x2a] == 0x33 && valid(A) && valid(B) & valid(C) & valid(D)) {
return {A, B, C, D};
}
return "";
}
auto SuperFamicom::romSize() const -> uint {
return size() - firmwareRomSize();
}
auto SuperFamicom::programRomSize() const -> uint {
if(board().beginsWith("SPC7110-")) return 0x100000;
if(board().beginsWith("EXSPC7110-")) return 0x100000;
return romSize();
}
auto SuperFamicom::dataRomSize() const -> uint {
if(board().beginsWith("SPC7110-")) return romSize() - 0x100000;
if(board().beginsWith("EXSPC7110-")) return 0x500000;
return 0;
}
auto SuperFamicom::expansionRomSize() const -> uint {
if(board().beginsWith("EXSPC7110-")) return 0x100000;
return 0;
}
//detect if any firmware is appended to the ROM image, and return its size if so
auto SuperFamicom::firmwareRomSize() const -> uint {
auto cartridgeTypeLo = data[headerAddress + 0x26] & 15;
auto cartridgeTypeHi = data[headerAddress + 0x26] >> 4;
auto cartridgeSubType = data[headerAddress + 0x0f];
if(serial() == "042J" || (cartridgeTypeLo == 0x3 && cartridgeTypeHi == 0xe)) {
//Game Boy
if((size() & 0x7fff) == 0x100) return 0x100;
}
if(cartridgeTypeLo >= 0x3 && cartridgeTypeHi == 0xf && cartridgeSubType == 0x10) {
//Hitachi HG51BS169
if((size() & 0x7fff) == 0xc00) return 0xc00;
}
if(cartridgeTypeLo >= 0x3 && cartridgeTypeHi == 0x0) {
//NEC uPD7725
if((size() & 0x7fff) == 0x2000) return 0x2000;
}
if(cartridgeTypeLo >= 0x3 && cartridgeTypeHi == 0xf && cartridgeSubType == 0x01) {
//NEC uPD96050
if((size() & 0xffff) == 0xd000) return 0xd000;
}
if(cartridgeTypeLo >= 0x3 && cartridgeTypeHi == 0xf && cartridgeSubType == 0x02) {
//ARM6
if((size() & 0x3ffff) == 0x28000) return 0x28000;
}
return 0;
}
auto SuperFamicom::ramSize() const -> uint {
auto ramSize = data[headerAddress + 0x28] & 15;
if(ramSize > 8) ramSize = 8;
if(ramSize > 0) return 1024 << ramSize;
return 0;
}
auto SuperFamicom::expansionRamSize() const -> uint {
if(data[headerAddress + 0x2a] == 0x33) {
auto ramSize = data[headerAddress + 0x0d] & 15;
if(ramSize > 8) ramSize = 8;
if(ramSize > 0) return 1024 << ramSize;
}
if((data[headerAddress + 0x26] >> 4) == 1) {
//GSU: Starfox / Starwing lacks an extended header; but still has expansion RAM
return 0x8000;
}
return 0;
}
auto SuperFamicom::nonVolatile() const -> bool {
auto cartridgeTypeLo = data[headerAddress + 0x26] & 15;
return cartridgeTypeLo == 0x2 || cartridgeTypeLo == 0x5 || cartridgeTypeLo == 0x6;
}
auto SuperFamicom::scoreHeader(uint address) -> uint {
int score = 0;
if(size() < address + 0x50) return score;
uint8_t mapMode = data[address + 0x25] & ~0x10; //ignore FastROM bit
uint16_t complement = data[address + 0x2c] << 0 | data[address + 0x2d] << 8;
uint16_t checksum = data[address + 0x2e] << 0 | data[address + 0x2f] << 8;
uint16_t resetVector = data[address + 0x4c] << 0 | data[address + 0x4d] << 8;
if(resetVector < 0x8000) return score; //$00:0000-7fff is never ROM data
uint8_t opcode = data[(address & ~0x7fff) | (resetVector & 0x7fff)]; //first instruction executed
//most likely opcodes
if(opcode == 0x78 //sei
|| opcode == 0x18 //clc (clc; xce)
|| opcode == 0x38 //sec (sec; xce)
|| opcode == 0x9c //stz $nnnn (stz $4200)
|| opcode == 0x4c //jmp $nnnn
|| opcode == 0x5c //jml $nnnnnn
) score += 8;
//plausible opcodes
if(opcode == 0xc2 //rep #$nn
|| opcode == 0xe2 //sep #$nn
|| opcode == 0xad //lda $nnnn
|| opcode == 0xae //ldx $nnnn
|| opcode == 0xac //ldy $nnnn
|| opcode == 0xaf //lda $nnnnnn
|| opcode == 0xa9 //lda #$nn
|| opcode == 0xa2 //ldx #$nn
|| opcode == 0xa0 //ldy #$nn
|| opcode == 0x20 //jsr $nnnn
|| opcode == 0x22 //jsl $nnnnnn
) score += 4;
//implausible opcodes
if(opcode == 0x40 //rti
|| opcode == 0x60 //rts
|| opcode == 0x6b //rtl
|| opcode == 0xcd //cmp $nnnn
|| opcode == 0xec //cpx $nnnn
|| opcode == 0xcc //cpy $nnnn
) score -= 4;
//least likely opcodes
if(opcode == 0x00 //brk #$nn
|| opcode == 0x02 //cop #$nn
|| opcode == 0xdb //stp
|| opcode == 0x42 //wdm
|| opcode == 0xff //sbc $nnnnnn,x
) score -= 8;
if(checksum + complement == 0xffff) score += 4;
if(address == 0x7fb0 && mapMode == 0x20) score += 2;
if(address == 0xffb0 && mapMode == 0x21) score += 2;
return max(0, score);
}
auto SuperFamicom::firmwareARM() const -> string {
return "ST018";
}
auto SuperFamicom::firmwareEXNEC() const -> string {
if(title() == "EXHAUST HEAT2") return "ST010";
if(title() == "F1 ROC II") return "ST010";
if(title() == "2DAN MORITA SHOUGI") return "ST011";
return "ST010";
}
auto SuperFamicom::firmwareGB() const -> string {
if(title() == "Super GAMEBOY") return "SGB1";
if(title() == "Super GAMEBOY2") return "SGB2";
return "SGB1";
}
auto SuperFamicom::firmwareHITACHI() const -> string {
return "Cx4";
}
auto SuperFamicom::firmwareNEC() const -> string {
if(title() == "PILOTWINGS") return "DSP1";
if(title() == "DUNGEON MASTER") return "DSP2";
if(title() == "SDガンダムGX") return "DSP3";
if(title() == "PLANETS CHAMP TG3000") return "DSP4";
if(title() == "TOP GEAR 3000") return "DSP4";
return "DSP1B";
}
}

View File

@ -0,0 +1,64 @@
//deprecated
#pragma once
#include <nall/range.hpp>
#include <nall/traits.hpp>
namespace nall {
template<typename T, uint Capacity>
struct adaptive_array {
auto capacity() const -> uint { return Capacity; }
auto size() const -> uint { return _size; }
auto reset() -> void {
for(uint n : range(_size)) _pool.t[n].~T();
_size = 0;
}
auto operator[](uint index) -> T& {
#ifdef DEBUG
struct out_of_bounds {};
if(index >= Capacity) throw out_of_bounds{};
#endif
return _pool.t[index];
}
auto operator[](uint index) const -> const T& {
#ifdef DEBUG
struct out_of_bounds {};
if(index >= Capacity) throw out_of_bounds{};
#endif
return _pool.t[index];
}
auto append() -> T& {
new(_pool.t + _size) T;
return _pool.t[_size++];
}
auto append(const T& value) -> void {
new(_pool.t + _size++) T(value);
}
auto append(T&& value) -> void {
new(_pool.t + _size++) T(move(value));
}
auto begin() { return &_pool.t[0]; }
auto end() { return &_pool.t[_size]; }
auto begin() const { return &_pool.t[0]; }
auto end() const { return &_pool.t[_size]; }
private:
union U {
U() {}
~U() {}
T t[Capacity];
} _pool;
uint _size = 0;
};
}

View File

@ -0,0 +1,26 @@
#pragma once
#include <nall/traits.hpp>
#undef min
#undef max
namespace nall {
template<typename T, typename U> constexpr auto min(const T& t, const U& u) -> T {
return t < u ? t : (T)u;
}
template<typename T, typename U, typename... P> constexpr auto min(const T& t, const U& u, P&&... p) -> T {
return t < u ? min(t, forward<P>(p)...) : min(u, forward<P>(p)...);
}
template<typename T, typename U> constexpr auto max(const T& t, const U& u) -> T {
return t > u ? t : (T)u;
}
template<typename T, typename U, typename... P> constexpr auto max(const T& t, const U& u, P&&... p) -> T {
return t > u ? max(t, forward<P>(p)...) : max(u, forward<P>(p)...);
}
}

View File

@ -0,0 +1,83 @@
#pragma once
#include <typeinfo>
#include <nall/traits.hpp>
namespace nall {
struct any {
any() = default;
any(const any& source) { operator=(source); }
any(any&& source) { operator=(move(source)); }
template<typename T> any(const T& value) { operator=(value); }
~any() { reset(); }
explicit operator bool() const { return container; }
auto reset() -> void { if(container) { delete container; container = nullptr; } }
auto type() const -> const std::type_info& {
return container ? container->type() : typeid(void);
}
template<typename T> auto is() const -> bool {
return type() == typeid(typename remove_reference<T>::type);
}
template<typename T> auto get() -> T& {
if(!is<T>()) throw;
return static_cast<holder<typename remove_reference<T>::type>*>(container)->value;
}
template<typename T> auto get() const -> const T& {
if(!is<T>()) throw;
return static_cast<holder<typename remove_reference<T>::type>*>(container)->value;
}
template<typename T> auto get(const T& fallback) const -> const T& {
if(!is<T>()) return fallback;
return static_cast<holder<typename remove_reference<T>::type>*>(container)->value;
}
template<typename T> auto operator=(const T& value) -> any& {
using auto_t = typename conditional<is_array<T>::value, typename remove_extent<typename add_const<T>::type>::type*, T>::type;
if(type() == typeid(auto_t)) {
static_cast<holder<auto_t>*>(container)->value = (auto_t)value;
} else {
if(container) delete container;
container = new holder<auto_t>((auto_t)value);
}
return *this;
}
auto operator=(const any& source) -> any& {
if(container) { delete container; container = nullptr; }
if(source.container) container = source.container->copy();
return *this;
}
auto operator=(any&& source) -> any& {
if(container) delete container;
container = source.container;
source.container = nullptr;
return *this;
}
private:
struct placeholder {
virtual ~placeholder() = default;
virtual auto type() const -> const std::type_info& = 0;
virtual auto copy() const -> placeholder* = 0;
};
placeholder* container = nullptr;
template<typename T> struct holder : placeholder {
holder(const T& value) : value(value) {}
auto type() const -> const std::type_info& { return typeid(T); }
auto copy() const -> placeholder* { return new holder(value); }
T value;
};
};
}

View File

@ -0,0 +1,161 @@
#pragma once
#include <nall/location.hpp>
#include <nall/path.hpp>
#include <nall/string.hpp>
#include <nall/vector.hpp>
namespace nall {
struct Arguments {
Arguments(int argc, char** argv);
Arguments(vector<string> arguments);
explicit operator bool() const { return (bool)arguments; }
auto size() const -> uint { return arguments.size(); }
auto operator[](uint index) -> string& { return arguments[index]; }
auto operator[](uint index) const -> const string& { return arguments[index]; }
auto programPath() const -> string;
auto programName() const -> string;
auto programLocation() const -> string;
auto find(string_view name) const -> bool;
auto find(string_view name, bool& argument) const -> bool;
auto find(string_view name, string& argument) const -> bool;
auto begin() const { return arguments.begin(); }
auto end() const { return arguments.end(); }
auto rbegin() const { return arguments.rbegin(); }
auto rend() const { return arguments.rend(); }
auto take() -> string;
auto take(string_view name) -> bool;
auto take(string_view name, bool& argument) -> bool;
auto take(string_view name, string& argument) -> bool;
auto begin() { return arguments.begin(); }
auto end() { return arguments.end(); }
auto rbegin() { return arguments.rbegin(); }
auto rend() { return arguments.rend(); }
private:
auto construct() -> void;
string programArgument;
vector<string> arguments;
};
inline auto Arguments::construct() -> void {
if(!arguments) return;
//extract and pre-process program argument
programArgument = arguments.takeFirst();
programArgument = {Path::real(programArgument), Location::file(programArgument)};
//normalize path and file arguments
for(auto& argument : arguments) {
if(directory::exists(argument)) argument.transform("\\", "/").trimRight("/").append("/");
else if(file::exists(argument)) argument.transform("\\", "/").trimRight("/");
}
}
inline Arguments::Arguments(int argc, char** argv) {
#if defined(PLATFORM_WINDOWS)
utf8_arguments(argc, argv);
#endif
for(uint index : range(argc)) arguments.append(argv[index]);
construct();
}
inline Arguments::Arguments(vector<string> arguments) {
this->arguments = arguments;
construct();
}
inline auto Arguments::programPath() const -> string {
return Location::path(programArgument);
}
inline auto Arguments::programName() const -> string {
return Location::file(programArgument);
}
inline auto Arguments::programLocation() const -> string {
return programArgument;
}
inline auto Arguments::find(string_view name) const -> bool {
for(uint index : range(arguments.size())) {
if(arguments[index].match(name)) {
return true;
}
}
return false;
}
inline auto Arguments::find(string_view name, bool& argument) const -> bool {
for(uint index : range(arguments.size())) {
if(arguments[index].match(name) && arguments.size() >= index
&& (arguments[index + 1] == "true" || arguments[index + 1] == "false")) {
argument = arguments[index + 1] == "true";
return true;
}
}
return false;
}
inline auto Arguments::find(string_view name, string& argument) const -> bool {
for(uint index : range(arguments.size())) {
if(arguments[index].match(name) && arguments.size() >= index) {
argument = arguments[index + 1];
return true;
}
}
return false;
}
//
inline auto Arguments::take() -> string {
if(!arguments) return {};
return arguments.takeFirst();
}
inline auto Arguments::take(string_view name) -> bool {
for(uint index : range(arguments.size())) {
if(arguments[index].match(name)) {
arguments.remove(index);
return true;
}
}
return false;
}
inline auto Arguments::take(string_view name, bool& argument) -> bool {
for(uint index : range(arguments.size())) {
if(arguments[index].match(name) && arguments.size() > index + 1
&& (arguments[index + 1] == "true" || arguments[index + 1] == "false")) {
arguments.remove(index);
argument = arguments.take(index) == "true";
return true;
}
}
return false;
}
inline auto Arguments::take(string_view name, string& argument) -> bool {
for(uint index : range(arguments.size())) {
if(arguments[index].match(name) && arguments.size() > index + 1) {
arguments.remove(index);
argument = arguments.take(index);
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,89 @@
#pragma once
//multi-precision arithmetic
//warning: each size is quadratically more expensive than the size before it!
#include <nall/stdint.hpp>
#include <nall/string.hpp>
#include <nall/range.hpp>
#include <nall/traits.hpp>
#include <nall/arithmetic/unsigned.hpp>
namespace nall {
template<uint Bits> struct ArithmeticNatural;
template<> struct ArithmeticNatural< 8> { using type = uint8_t; };
template<> struct ArithmeticNatural< 16> { using type = uint16_t; };
template<> struct ArithmeticNatural< 32> { using type = uint32_t; };
template<> struct ArithmeticNatural< 64> { using type = uint64_t; };
#if INTMAX_BITS >= 128
template<> struct ArithmeticNatural<128> { using type = uint128_t; };
#endif
}
#if INTMAX_BITS < 128
#define PairBits 128
#define TypeBits 64
#define HalfBits 32
#include <nall/arithmetic/natural.hpp>
#undef PairBits
#undef TypeBits
#undef HalfBits
#endif
#define PairBits 256
#define TypeBits 128
#define HalfBits 64
#include <nall/arithmetic/natural.hpp>
#undef PairBits
#undef TypeBits
#undef HalfBits
#define PairBits 512
#define TypeBits 256
#define HalfBits 128
#include <nall/arithmetic/natural.hpp>
#undef PairBits
#undef TypeBits
#undef HalfBits
#define PairBits 1024
#define TypeBits 512
#define HalfBits 256
#include <nall/arithmetic/natural.hpp>
#undef PairBits
#undef TypeBits
#undef HalfBits
#define PairBits 2048
#define TypeBits 1024
#define HalfBits 512
#include <nall/arithmetic/natural.hpp>
#undef PairBits
#undef TypeBits
#undef HalfBits
#define PairBits 4096
#define TypeBits 2048
#define HalfBits 1024
#include <nall/arithmetic/natural.hpp>
#undef PairBits
#undef TypeBits
#undef HalfBits
#define PairBits 8192
#define TypeBits 4096
#define HalfBits 2048
#include <nall/arithmetic/natural.hpp>
#undef PairBits
#undef TypeBits
#undef HalfBits
namespace nall {
//TODO: these types are for expressing smaller bit ranges in class interfaces
//for instance, XChaCha20 taking a 192-bit nonce
//however, they still allow more bits than expressed ...
//some sort of wrapper needs to be devised to ensure these sizes are masked and wrap appropriately
using uint192_t = uint256_t;
}

View File

@ -0,0 +1,28 @@
#pragma once
namespace nall {
template<uint Bits> struct BarrettReduction {
using type = typename ArithmeticNatural<1 * Bits>::type;
using pair = typename ArithmeticNatural<2 * Bits>::type;
explicit BarrettReduction(type modulo) : modulo(modulo), factor(pair(1) + -pair(modulo) / modulo) {}
//return => value % modulo
inline auto operator()(pair value) const -> type {
pair hi, lo;
mul(value, factor, hi, lo);
pair remainder = value - hi * modulo;
return remainder < modulo ? remainder : remainder - modulo;
}
private:
const pair modulo;
const pair factor;
};
template<typename T, uint Bits> auto operator%(T value, const BarrettReduction<Bits>& modulo) {
return modulo(value);
}
}

View File

@ -0,0 +1,342 @@
#define ConcatenateType(Size) uint##Size##_t
#define DeclareType(Size) ConcatenateType(Size)
#define Pair DeclareType(PairBits)
#define Type DeclareType(TypeBits)
#define Half DeclareType(HalfBits)
//pick the larger of two types to prevent unnecessary data clamping
#define Cast (typename conditional<sizeof(Pair) >= sizeof(T), Pair, T>::type)
namespace nall {
//namespace Arithmetic {
struct Pair {
Pair() = default;
explicit constexpr Pair(const Pair& source) : hi(source.hi), lo(source.lo) {}
template<typename Hi, typename Lo> constexpr Pair(const Hi& hi, const Lo& lo) : hi(hi), lo(lo) {}
template<typename T> Pair(const T& source) { _set(*this, source); }
explicit operator bool() const { return hi | lo; }
template<typename T> operator T() const { T value; _get(*this, value); return value; }
auto operator+() const -> Pair { return *this; }
auto operator-() const -> Pair { return Pair(0) - *this; }
auto operator~() const -> Pair { return {~hi, ~lo}; }
auto operator!() const -> bool { return !(hi || lo); }
auto operator++() -> Pair& { lo++; hi += lo == 0; return *this; }
auto operator--() -> Pair& { hi -= lo == 0; lo--; return *this; }
auto operator++(int) -> Pair { Pair r = *this; lo++; hi += lo == 0; return r; }
auto operator--(int) -> Pair { Pair r = *this; hi -= lo == 0; lo--; return r; }
auto operator* (const Pair& rhs) const -> Pair { return mul(*this, rhs); }
auto operator/ (const Pair& rhs) const -> Pair { Pair q, r; div(*this, rhs, q, r); return q; }
auto operator% (const Pair& rhs) const -> Pair { Pair q, r; div(*this, rhs, q, r); return r; }
auto operator+ (const Pair& rhs) const -> Pair { return {hi + rhs.hi + (lo + rhs.lo < lo), lo + rhs.lo}; }
auto operator- (const Pair& rhs) const -> Pair { return {hi - rhs.hi - (lo - rhs.lo > lo), lo - rhs.lo}; }
auto operator<<(const Pair& rhs) const -> Pair { return shl(*this, rhs); }
auto operator>>(const Pair& rhs) const -> Pair { return shr(*this, rhs); }
auto operator& (const Pair& rhs) const -> Pair { return {hi & rhs.hi, lo & rhs.lo}; }
auto operator| (const Pair& rhs) const -> Pair { return {hi | rhs.hi, lo | rhs.lo}; }
auto operator^ (const Pair& rhs) const -> Pair { return {hi ^ rhs.hi, lo ^ rhs.lo}; }
auto operator==(const Pair& rhs) const -> bool { return hi == rhs.hi && lo == rhs.lo; }
auto operator!=(const Pair& rhs) const -> bool { return hi != rhs.hi || lo != rhs.lo; }
auto operator>=(const Pair& rhs) const -> bool { return hi > rhs.hi || (hi == rhs.hi && lo >= rhs.lo); }
auto operator<=(const Pair& rhs) const -> bool { return hi < rhs.hi || (hi == rhs.hi && lo <= rhs.lo); }
auto operator> (const Pair& rhs) const -> bool { return hi > rhs.hi || (hi == rhs.hi && lo > rhs.lo); }
auto operator< (const Pair& rhs) const -> bool { return hi < rhs.hi || (hi == rhs.hi && lo < rhs.lo); }
template<typename T> auto& operator*= (const T& rhs) { return *this = *this * Pair(rhs); }
template<typename T> auto& operator/= (const T& rhs) { return *this = *this / Pair(rhs); }
template<typename T> auto& operator%= (const T& rhs) { return *this = *this % Pair(rhs); }
template<typename T> auto& operator+= (const T& rhs) { return *this = *this + Pair(rhs); }
template<typename T> auto& operator-= (const T& rhs) { return *this = *this - Pair(rhs); }
template<typename T> auto& operator<<=(const T& rhs) { return *this = *this << Pair(rhs); }
template<typename T> auto& operator>>=(const T& rhs) { return *this = *this >> Pair(rhs); }
template<typename T> auto& operator&= (const T& rhs) { return *this = *this & Pair(rhs); }
template<typename T> auto& operator|= (const T& rhs) { return *this = *this | Pair(rhs); }
template<typename T> auto& operator^= (const T& rhs) { return *this = *this ^ Pair(rhs); }
template<typename T> auto operator* (const T& rhs) const { return Cast(*this) * Cast(rhs); }
template<typename T> auto operator/ (const T& rhs) const { return Cast(*this) / Cast(rhs); }
template<typename T> auto operator% (const T& rhs) const { return Cast(*this) % Cast(rhs); }
template<typename T> auto operator+ (const T& rhs) const { return Cast(*this) + Cast(rhs); }
template<typename T> auto operator- (const T& rhs) const { return Cast(*this) - Cast(rhs); }
template<typename T> auto operator<<(const T& rhs) const { return Cast(*this) << Cast(rhs); }
template<typename T> auto operator>>(const T& rhs) const { return Cast(*this) >> Cast(rhs); }
template<typename T> auto operator& (const T& rhs) const { return Cast(*this) & Cast(rhs); }
template<typename T> auto operator| (const T& rhs) const { return Cast(*this) | Cast(rhs); }
template<typename T> auto operator^ (const T& rhs) const { return Cast(*this) ^ Cast(rhs); }
template<typename T> auto operator==(const T& rhs) const -> bool { return Cast(*this) == Cast(rhs); }
template<typename T> auto operator!=(const T& rhs) const -> bool { return Cast(*this) != Cast(rhs); }
template<typename T> auto operator>=(const T& rhs) const -> bool { return Cast(*this) >= Cast(rhs); }
template<typename T> auto operator<=(const T& rhs) const -> bool { return Cast(*this) <= Cast(rhs); }
template<typename T> auto operator> (const T& rhs) const -> bool { return Cast(*this) > Cast(rhs); }
template<typename T> auto operator< (const T& rhs) const -> bool { return Cast(*this) < Cast(rhs); }
private:
Type lo;
Type hi;
friend auto upper(const Pair&) -> Type;
friend auto lower(const Pair&) -> Type;
friend auto bits(Pair) -> uint;
friend auto square(const Pair&) -> Pair;
friend auto square(const Pair&, Pair&, Pair&) -> void;
friend auto mul(const Pair&, const Pair&) -> Pair;
friend auto mul(const Pair&, const Pair&, Pair&, Pair&) -> void;
friend auto div(const Pair&, const Pair&, Pair&, Pair&) -> void;
template<typename T> friend auto shl(const Pair&, const T&) -> Pair;
template<typename T> friend auto shr(const Pair&, const T&) -> Pair;
};
template<> struct ArithmeticNatural<PairBits> {
using type = Pair;
};
#define ConcatenateUDL(Size) _u##Size
#define DeclareUDL(Size) ConcatenateUDL(Size)
alwaysinline auto operator"" DeclareUDL(PairBits)(const char* s) -> Pair {
Pair p = 0;
if(s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
s += 2;
while(*s) {
auto c = *s++;
if(c == '\'');
else if(c >= '0' && c <= '9') p = (p << 4) + (c - '0');
else if(c >= 'a' && c <= 'f') p = (p << 4) + (c - 'a' + 10);
else if(c >= 'A' && c <= 'F') p = (p << 4) + (c - 'A' + 10);
else break;
}
} else {
while(*s) {
auto c = *s++;
if(c == '\'');
else if(c >= '0' && c <= '9') p = (p << 3) + (p << 1) + (c - '0');
else break;
}
}
return p;
}
#undef ConcatenateUDL
#undef DeclareUDL
template<typename T> alwaysinline auto _set(Pair& lhs, const T& rhs) -> enable_if_t<(sizeof(Pair) == sizeof(T))> {
lhs = rhs;
}
template<typename T> alwaysinline auto _set(Pair& lhs, const T& rhs) -> enable_if_t<(sizeof(Pair) > sizeof(T))> {
lhs = {0, rhs};
}
template<typename T> alwaysinline auto _set(Pair& lhs, const T& rhs) -> enable_if_t<(sizeof(Pair) < sizeof(T))> {
lhs = {lower(rhs) >> TypeBits, lower(rhs)};
}
template<typename T> alwaysinline auto _get(const Pair& lhs, T& rhs) -> enable_if_t<(sizeof(T) == sizeof(Pair))> {
rhs = lhs;
}
template<typename T> alwaysinline auto _get(const Pair& lhs, T& rhs) -> enable_if_t<(sizeof(T) > sizeof(Pair))> {
rhs = {0, lhs};
}
template<typename T> alwaysinline auto _get(const Pair& lhs, T& rhs) -> enable_if_t<(sizeof(T) < sizeof(Pair))> {
rhs = lower(lhs);
}
alwaysinline auto upper(const Pair& value) -> Type { return value.hi; }
alwaysinline auto lower(const Pair& value) -> Type { return value.lo; }
alwaysinline auto bits(Pair value) -> uint {
if(value.hi) {
uint bits = TypeBits;
while(value.hi) value.hi >>= 1, bits++;
return bits;
} else {
uint bits = 0;
while(value.lo) value.lo >>= 1, bits++;
return bits;
}
}
//Bits * Bits => Bits
inline auto square(const Pair& lhs) -> Pair {
static const Type Mask = (Type(0) - 1) >> HalfBits;
Type a = lhs.hi >> HalfBits, b = lhs.hi & Mask, c = lhs.lo >> HalfBits, d = lhs.lo & Mask;
Type dd = square(d), dc = d * c, db = d * b, da = d * a;
Type cc = square(c), cb = c * b;
Pair r0 = Pair(dd);
Pair r1 = Pair(dc) + Pair(dc) + Pair(r0 >> HalfBits);
Pair r2 = Pair(db) + Pair(cc) + Pair(db) + Pair(r1 >> HalfBits);
Pair r3 = Pair(da) + Pair(cb) + Pair(cb) + Pair(da) + Pair(r2 >> HalfBits);
return {(r3.lo & Mask) << HalfBits | (r2.lo & Mask), (r1.lo & Mask) << HalfBits | (r0.lo & Mask)};
}
//Bits * Bits => 2 * Bits
inline auto square(const Pair& lhs, Pair& hi, Pair& lo) -> void {
static const Type Mask = (Type(0) - 1) >> HalfBits;
Type a = lhs.hi >> HalfBits, b = lhs.hi & Mask, c = lhs.lo >> HalfBits, d = lhs.lo & Mask;
Type dd = square(d), dc = d * c, db = d * b, da = d * a;
Type cc = square(c), cb = c * b, ca = c * a;
Type bb = square(b), ba = b * a;
Type aa = square(a);
Pair r0 = Pair(dd);
Pair r1 = Pair(dc) + Pair(dc) + Pair(r0 >> HalfBits);
Pair r2 = Pair(db) + Pair(cc) + Pair(db) + Pair(r1 >> HalfBits);
Pair r3 = Pair(da) + Pair(cb) + Pair(cb) + Pair(da) + Pair(r2 >> HalfBits);
Pair r4 = Pair(ca) + Pair(bb) + Pair(ca) + Pair(r3 >> HalfBits);
Pair r5 = Pair(ba) + Pair(ba) + Pair(r4 >> HalfBits);
Pair r6 = Pair(aa) + Pair(r5 >> HalfBits);
Pair r7 = Pair(r6 >> HalfBits);
hi = {(r7.lo & Mask) << HalfBits | (r6.lo & Mask), (r5.lo & Mask) << HalfBits | (r4.lo & Mask)};
lo = {(r3.lo & Mask) << HalfBits | (r2.lo & Mask), (r1.lo & Mask) << HalfBits | (r0.lo & Mask)};
}
//Bits * Bits => Bits
alwaysinline auto mul(const Pair& lhs, const Pair& rhs) -> Pair {
static const Type Mask = (Type(0) - 1) >> HalfBits;
Type a = lhs.hi >> HalfBits, b = lhs.hi & Mask, c = lhs.lo >> HalfBits, d = lhs.lo & Mask;
Type e = rhs.hi >> HalfBits, f = rhs.hi & Mask, g = rhs.lo >> HalfBits, h = rhs.lo & Mask;
Pair r0 = Pair(d * h);
Pair r1 = Pair(c * h) + Pair(d * g) + Pair(r0 >> HalfBits);
Pair r2 = Pair(b * h) + Pair(c * g) + Pair(d * f) + Pair(r1 >> HalfBits);
Pair r3 = Pair(a * h) + Pair(b * g) + Pair(c * f) + Pair(d * e) + Pair(r2 >> HalfBits);
return {(r3.lo & Mask) << HalfBits | (r2.lo & Mask), (r1.lo & Mask) << HalfBits | (r0.lo & Mask)};
}
//Bits * Bits => 2 * Bits
alwaysinline auto mul(const Pair& lhs, const Pair& rhs, Pair& hi, Pair& lo) -> void {
static const Type Mask = (Type(0) - 1) >> HalfBits;
Type a = lhs.hi >> HalfBits, b = lhs.hi & Mask, c = lhs.lo >> HalfBits, d = lhs.lo & Mask;
Type e = rhs.hi >> HalfBits, f = rhs.hi & Mask, g = rhs.lo >> HalfBits, h = rhs.lo & Mask;
Pair r0 = Pair(d * h);
Pair r1 = Pair(c * h) + Pair(d * g) + Pair(r0 >> HalfBits);
Pair r2 = Pair(b * h) + Pair(c * g) + Pair(d * f) + Pair(r1 >> HalfBits);
Pair r3 = Pair(a * h) + Pair(b * g) + Pair(c * f) + Pair(d * e) + Pair(r2 >> HalfBits);
Pair r4 = Pair(a * g) + Pair(b * f) + Pair(c * e) + Pair(r3 >> HalfBits);
Pair r5 = Pair(a * f) + Pair(b * e) + Pair(r4 >> HalfBits);
Pair r6 = Pair(a * e) + Pair(r5 >> HalfBits);
Pair r7 = Pair(r6 >> HalfBits);
hi = {(r7.lo & Mask) << HalfBits | (r6.lo & Mask), (r5.lo & Mask) << HalfBits | (r4.lo & Mask)};
lo = {(r3.lo & Mask) << HalfBits | (r2.lo & Mask), (r1.lo & Mask) << HalfBits | (r0.lo & Mask)};
}
alwaysinline auto div(const Pair& lhs, const Pair& rhs, Pair& quotient, Pair& remainder) -> void {
if(!rhs) throw std::runtime_error("division by zero");
quotient = 0, remainder = lhs;
if(!lhs || lhs < rhs) return;
auto count = bits(lhs) - bits(rhs);
Pair x = rhs << count;
Pair y = Pair(1) << count;
if(x > remainder) x >>= 1, y >>= 1;
while(remainder >= rhs) {
if(remainder >= x) remainder -= x, quotient |= y;
x >>= 1, y >>= 1;
}
}
template<typename T> alwaysinline auto shl(const Pair& lhs, const T& rhs) -> Pair {
if(!rhs) return lhs;
auto shift = (uint)rhs;
if(shift < TypeBits) {
return {lhs.hi << shift | lhs.lo >> (TypeBits - shift), lhs.lo << shift};
} else {
return {lhs.lo << (shift - TypeBits), 0};
}
}
template<typename T> alwaysinline auto shr(const Pair& lhs, const T& rhs) -> Pair {
if(!rhs) return lhs;
auto shift = (uint)rhs;
if(shift < TypeBits) {
return {lhs.hi >> shift, lhs.hi << (TypeBits - shift) | lhs.lo >> shift};
} else {
return {0, lhs.hi >> (shift - TypeBits)};
}
}
template<typename T> alwaysinline auto rol(const Pair& lhs, const T& rhs) -> Pair {
return lhs << rhs | lhs >> (PairBits - rhs);
}
template<typename T> alwaysinline auto ror(const Pair& lhs, const T& rhs) -> Pair {
return lhs >> rhs | lhs << (PairBits - rhs);
}
#define EI enable_if_t<is_integral<T>::value>
template<typename T, EI> auto& operator*= (T& lhs, const Pair& rhs) { return lhs = lhs * T(rhs); }
template<typename T, EI> auto& operator/= (T& lhs, const Pair& rhs) { return lhs = lhs / T(rhs); }
template<typename T, EI> auto& operator%= (T& lhs, const Pair& rhs) { return lhs = lhs % T(rhs); }
template<typename T, EI> auto& operator+= (T& lhs, const Pair& rhs) { return lhs = lhs + T(rhs); }
template<typename T, EI> auto& operator-= (T& lhs, const Pair& rhs) { return lhs = lhs - T(rhs); }
template<typename T, EI> auto& operator<<=(T& lhs, const Pair& rhs) { return lhs = lhs << T(rhs); }
template<typename T, EI> auto& operator>>=(T& lhs, const Pair& rhs) { return lhs = lhs >> T(rhs); }
template<typename T, EI> auto& operator&= (T& lhs, const Pair& rhs) { return lhs = lhs & T(rhs); }
template<typename T, EI> auto& operator|= (T& lhs, const Pair& rhs) { return lhs = lhs | T(rhs); }
template<typename T, EI> auto& operator^= (T& lhs, const Pair& rhs) { return lhs = lhs ^ T(rhs); }
template<typename T, EI> auto operator* (const T& lhs, const Pair& rhs) { return Cast(lhs) * Cast(rhs); }
template<typename T, EI> auto operator/ (const T& lhs, const Pair& rhs) { return Cast(lhs) / Cast(rhs); }
template<typename T, EI> auto operator% (const T& lhs, const Pair& rhs) { return Cast(lhs) % Cast(rhs); }
template<typename T, EI> auto operator+ (const T& lhs, const Pair& rhs) { return Cast(lhs) + Cast(rhs); }
template<typename T, EI> auto operator- (const T& lhs, const Pair& rhs) { return Cast(lhs) - Cast(rhs); }
template<typename T, EI> auto operator<<(const T& lhs, const Pair& rhs) { return Cast(lhs) << Cast(rhs); }
template<typename T, EI> auto operator>>(const T& lhs, const Pair& rhs) { return Cast(lhs) >> Cast(rhs); }
template<typename T, EI> auto operator& (const T& lhs, const Pair& rhs) { return Cast(lhs) & Cast(rhs); }
template<typename T, EI> auto operator| (const T& lhs, const Pair& rhs) { return Cast(lhs) | Cast(rhs); }
template<typename T, EI> auto operator^ (const T& lhs, const Pair& rhs) { return Cast(lhs) ^ Cast(rhs); }
template<typename T, EI> auto operator==(const T& lhs, const Pair& rhs) { return Cast(lhs) == Cast(rhs); }
template<typename T, EI> auto operator!=(const T& lhs, const Pair& rhs) { return Cast(lhs) != Cast(rhs); }
template<typename T, EI> auto operator>=(const T& lhs, const Pair& rhs) { return Cast(lhs) >= Cast(rhs); }
template<typename T, EI> auto operator<=(const T& lhs, const Pair& rhs) { return Cast(lhs) <= Cast(rhs); }
template<typename T, EI> auto operator> (const T& lhs, const Pair& rhs) { return Cast(lhs) > Cast(rhs); }
template<typename T, EI> auto operator< (const T& lhs, const Pair& rhs) { return Cast(lhs) < Cast(rhs); }
#undef EI
template<> struct stringify<Pair> {
stringify(Pair source) {
char _output[1 + sizeof(Pair) * 3];
auto p = (char*)&_output;
do {
Pair quotient, remainder;
div(source, 10, quotient, remainder);
*p++ = remainder + '0';
source = quotient;
} while(source);
_size = p - _output;
*p = 0;
for(int x = _size - 1, y = 0; x >= 0 && y < _size; x--, y++) _data[x] = _output[y];
}
auto data() const -> const char* { return _data; }
auto size() const -> uint { return _size; }
char _data[1 + sizeof(Pair) * 3];
uint _size;
};
}
#undef ConcatenateType
#undef DeclareType
#undef Pair
#undef Type
#undef Half
#undef Cast

View File

@ -0,0 +1,61 @@
#pragma once
namespace nall {
template<typename T, enable_if_t<is_unsigned<T>::value>>
inline auto upper(T value) -> T {
return value >> sizeof(T) * 4;
}
template<typename T, enable_if_t<is_unsigned<T>::value>>
inline auto lower(T value) -> T {
static const T Mask = ~T(0) >> sizeof(T) * 4;
return value & Mask;
}
template<typename T, typename U, enable_if_t<is_unsigned<T>::value>, enable_if_t<is_unsigned<U>::value>>
inline auto mul(T lhs, U rhs) -> uintmax {
return lhs * rhs;
}
template<typename T, enable_if_t<is_unsigned<T>::value>>
inline auto square(T value) -> uintmax {
return value * value;
}
template<typename T, typename U>
inline auto rol(T lhs, U rhs, enable_if_t<is_unsigned<T>::value>* = 0) -> T {
return lhs << rhs | lhs >> sizeof(T) * 8 - rhs;
}
template<typename T, typename U>
inline auto ror(T lhs, U rhs, enable_if_t<is_unsigned<T>::value>* = 0) -> T {
return lhs >> rhs | lhs << sizeof(T) * 8 - rhs;
}
#if INTMAX_BITS >= 128
inline auto operator"" _u128(const char* s) -> uint128_t {
uint128_t p = 0;
if(s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
s += 2;
while(*s) {
auto c = *s++;
if(c == '\'');
else if(c >= '0' && c <= '9') p = (p << 4) + (c - '0');
else if(c >= 'a' && c <= 'f') p = (p << 4) + (c - 'a' + 10);
else if(c >= 'A' && c <= 'F') p = (p << 4) + (c - 'A' + 10);
else break;
}
} else {
while(*s) {
auto c = *s++;
if(c == '\'');
else if(c >= '0' && c <= '9') p = (p << 3) + (p << 1) + (c - '0');
else break;
}
}
return p;
}
#endif
}

View File

@ -0,0 +1,92 @@
#pragma once
#include <nall/array-view.hpp>
namespace nall {
template<typename T> struct array_span : array_view<T> {
using type = array_span;
using super = array_view<T>;
inline array_span() {
super::_data = nullptr;
super::_size = 0;
}
inline array_span(nullptr_t) {
super::_data = nullptr;
super::_size = 0;
}
inline array_span(void* data, uint64_t size) {
super::_data = (T*)data;
super::_size = (int)size;
}
inline operator T*() { return (T*)super::operator const T*(); }
inline auto operator[](uint index) -> T& { return (T&)super::operator[](index); }
template<typename U = T> inline auto data() -> U* { return (U*)super::_data; }
inline auto begin() -> iterator<T> { return {(T*)super::_data, (uint)0}; }
inline auto end() -> iterator<T> { return {(T*)super::_data, (uint)super::_size}; }
inline auto rbegin() -> reverse_iterator<T> { return {(T*)super::_data, (uint)super::_size - 1}; }
inline auto rend() -> reverse_iterator<T> { return {(T*)super::_data, (uint)-1}; }
auto write(T value) -> void {
operator[](0) = value;
super::_data++;
super::_size--;
}
auto span(uint offset, uint length) const -> type {
#ifdef DEBUG
struct out_of_bounds {};
if(offset + length >= super::_size) throw out_of_bounds{};
#endif
return {super::_data + offset, length};
}
//array_span<uint8_t> specializations
template<typename U> auto writel(U value, uint size) -> void;
template<typename U> auto writem(U value, uint size) -> void;
template<typename U> auto writevn(U value, uint size) -> void;
template<typename U> auto writevi(U value, uint size) -> void;
};
//array_span<uint8_t>
template<> inline auto array_span<uint8_t>::write(uint8_t value) -> void {
operator[](0) = value;
_data++;
_size--;
}
template<> template<typename U> inline auto array_span<uint8_t>::writel(U value, uint size) -> void {
for(uint byte : range(size)) write(value >> byte * 8);
}
template<> template<typename U> inline auto array_span<uint8_t>::writem(U value, uint size) -> void {
for(uint byte : reverse(range(size))) write(value >> byte * 8);
}
template<> template<typename U> inline auto array_span<uint8_t>::writevn(U value, uint size) -> void {
while(true) {
auto byte = value & 0x7f;
value >>= 7;
if(value == 0) return write(0x80 | byte);
write(byte);
value--;
}
}
template<> template<typename U> inline auto array_span<uint8_t>::writevi(U value, uint size) -> void {
bool negate = value < 0;
if(negate) value = ~value;
value = value << 1 | negate;
writevn(value);
}
}

Some files were not shown because too many files have changed in this diff Show More