[GambatteLink] changes in link API, misc cleanup

This commit is contained in:
CasualPokePlayer 2021-11-27 22:39:42 -08:00
parent de8748d6d1
commit f7936a34f8
10 changed files with 350 additions and 335 deletions

Binary file not shown.

Binary file not shown.

View File

@ -4,6 +4,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
{ {
public partial class Gameboy : ILinkable public partial class Gameboy : ILinkable
{ {
public bool LinkConnected { get; set; } private bool _linkConnected;
public bool LinkConnected
{
get => _linkConnected;
set { return; }
}
} }
} }

View File

@ -9,7 +9,7 @@ using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.Gameboy namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
{ {
public partial class Gameboy : ITextStatable public partial class Gameboy : IStatable, ITextStatable
{ {
public void SaveStateText(TextWriter writer) public void SaveStateText(TextWriter writer)
{ {

View File

@ -1,6 +1,8 @@
namespace BizHawk.Emulation.Cores.Nintendo.Gameboy using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
{ {
public partial class Gameboy public partial class Gameboy : IVideoProvider
{ {
/// <summary> /// <summary>
/// buffer of last frame produced /// buffer of last frame produced

View File

@ -13,14 +13,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
/// </summary> /// </summary>
[PortedCore(CoreNames.Gambatte, "", "Gambatte-Speedrun r717+", "https://github.com/pokemon-speedrunning/gambatte-speedrun")] [PortedCore(CoreNames.Gambatte, "", "Gambatte-Speedrun r717+", "https://github.com/pokemon-speedrunning/gambatte-speedrun")]
[ServiceNotApplicable(new[] { typeof(IDriveLight) })] [ServiceNotApplicable(new[] { typeof(IDriveLight) })]
public partial class Gameboy : IEmulator, IVideoProvider, ISoundProvider, ISaveRam, IStatable, IInputPollable, ICodeDataLogger, public partial class Gameboy : IInputPollable, IRomInfo, IGameboyCommon, ICycleTiming, ILinkable
IBoardInfo, IRomInfo, IDebuggable, ISettable<Gameboy.GambatteSettings, Gameboy.GambatteSyncSettings>,
IGameboyCommon, ICycleTiming, ILinkable
{ {
[CoreConstructor(VSystemID.Raw.GB)] [CoreConstructor(VSystemID.Raw.GB)]
[CoreConstructor(VSystemID.Raw.GBC)] [CoreConstructor(VSystemID.Raw.GBC)]
[CoreConstructor(VSystemID.Raw.SGB)] [CoreConstructor(VSystemID.Raw.SGB)]
public Gameboy(CoreComm comm, GameInfo game, byte[] file, Gameboy.GambatteSettings settings, Gameboy.GambatteSyncSettings syncSettings, bool deterministic) public Gameboy(CoreComm comm, GameInfo game, byte[] file, GambatteSettings settings, GambatteSyncSettings syncSettings, bool deterministic)
{ {
var ser = new BasicServiceProvider(this); var ser = new BasicServiceProvider(this);
ser.Register<IDisassemblable>(_disassembler); ser.Register<IDisassemblable>(_disassembler);
@ -707,11 +705,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
if (callback != null) if (callback != null)
{ {
printer = new GambattePrinter(this, callback); printer = new GambattePrinter(this, callback);
LinkConnected = true; _linkConnected = true;
} }
else else
{ {
LinkConnected = false; _linkConnected = false;
if (printer != null) // have no idea how this is ever null??? if (printer != null) // have no idea how this is ever null???
{ {
printer.Disconnect(); printer.Disconnect();

View File

@ -34,8 +34,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
bool linkDiscoSignalNew = controller.IsPressed("Toggle Link Connection"); bool linkDiscoSignalNew = controller.IsPressed("Toggle Link Connection");
if (linkDiscoSignalNew && !_linkDiscoSignal) if (linkDiscoSignalNew && !_linkDiscoSignal)
{ {
_linkConnected ^= true; LinkConnected ^= true;
Console.WriteLine("Link connect status to {0}", _linkConnected); Console.WriteLine("Link connect status to {0}", LinkConnected);
} }
_linkDiscoSignal = linkDiscoSignalNew; _linkDiscoSignal = linkDiscoSignalNew;
@ -229,29 +229,29 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
if (CanIR(one, two)) if (CanIR(one, two))
{ {
if (LibGambatte.gambatte_linkstatus(_linkedCores[one].GambatteState, 260) != 0) // InfraredTrigger if (LibGambatte.gambatte_linkstatus(_linkedCores[one].GambatteState, 259) != 0) // InfraredTrigger
{ {
LibGambatte.gambatte_linkstatus(_linkedCores[one].GambatteState, 261); // ack LibGambatte.gambatte_linkstatus(_linkedCores[one].GambatteState, 260); // ack
if (LibGambatte.gambatte_linkstatus(_linkedCores[one].GambatteState, 262) != 0) // GetOut if (LibGambatte.gambatte_linkstatus(_linkedCores[one].GambatteState, 261) != 0) // GetOut
{ {
LibGambatte.gambatte_linkstatus(_linkedCores[two].GambatteState, 263); // ShiftInOn LibGambatte.gambatte_linkstatus(_linkedCores[two].GambatteState, 262); // ShiftInOn
} }
else else
{ {
LibGambatte.gambatte_linkstatus(_linkedCores[two].GambatteState, 264); // ShiftInOff LibGambatte.gambatte_linkstatus(_linkedCores[two].GambatteState, 263); // ShiftInOff
} }
} }
if (LibGambatte.gambatte_linkstatus(_linkedCores[two].GambatteState, 260) != 0) // InfraredTrigger if (LibGambatte.gambatte_linkstatus(_linkedCores[two].GambatteState, 259) != 0) // InfraredTrigger
{ {
LibGambatte.gambatte_linkstatus(_linkedCores[two].GambatteState, 261); // ack LibGambatte.gambatte_linkstatus(_linkedCores[two].GambatteState, 260); // ack
if (LibGambatte.gambatte_linkstatus(_linkedCores[two].GambatteState, 262) != 0) // GetOut if (LibGambatte.gambatte_linkstatus(_linkedCores[two].GambatteState, 261) != 0) // GetOut
{ {
LibGambatte.gambatte_linkstatus(_linkedCores[one].GambatteState, 263); // ShiftInOn LibGambatte.gambatte_linkstatus(_linkedCores[one].GambatteState, 262); // ShiftInOn
} }
else else
{ {
LibGambatte.gambatte_linkstatus(_linkedCores[one].GambatteState, 264); // ShiftInOff LibGambatte.gambatte_linkstatus(_linkedCores[one].GambatteState, 263); // ShiftInOff
} }
} }
} }

View File

@ -33,7 +33,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
for (int i = 0; i < _numCores; i++) for (int i = 0; i < _numCores; i++)
{ {
_linkedCores[i] = new Gameboy(lp.Comm, lp.Roms[i].Game, lp.Roms[i].RomData, _settings._linkedSettings[i], _syncSettings._linkedSyncSettings[i], lp.DeterministicEmulationRequested); _linkedCores[i] = new Gameboy(lp.Comm, lp.Roms[i].Game, lp.Roms[i].RomData, _settings._linkedSettings[i], _syncSettings._linkedSyncSettings[i], lp.DeterministicEmulationRequested);
LibGambatte.gambatte_linkstatus(_linkedCores[i].GambatteState, 259); // connect link cable
_linkedCores[i].ConnectInputCallbackSystem(_inputCallbacks); _linkedCores[i].ConnectInputCallbackSystem(_inputCallbacks);
_linkedCores[i].ConnectMemoryCallbackSystem(_memoryCallbacks); _linkedCores[i].ConnectMemoryCallbackSystem(_memoryCallbacks);
_linkedConts[i] = new SaveController(Gameboy.CreateControllerDefinition(false, false)); _linkedConts[i] = new SaveController(Gameboy.CreateControllerDefinition(false, false));
@ -76,7 +75,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
public bool LinkConnected public bool LinkConnected
{ {
get => _linkConnected; get => _linkConnected;
set => _linkConnected = value; set
{
_linkConnected = value;
for (int i = 0; i < _numCores; i++)
{
LibGambatte.gambatte_linkstatus(_linkedCores[i].GambatteState, _linkConnected ? 264 : 265);
}
}
} }
private int _numCores = 0; private int _numCores = 0;

View File

@ -1,311 +1,314 @@
using System; using System;
using BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy; using BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy;
namespace BizHawk.Emulation.Cores.Nintendo.Gameboy namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
{ {
/// <summary> /// <summary>
/// Emulate the gameboy printer in managed code /// Emulate the gameboy printer in managed code
/// </summary> /// </summary>
public class GambattePrinter public class GambattePrinter
{ {
// A loose c->c# port of SameBoy's printer code // A loose c->c# port of SameBoy's printer code
private enum CommandState : byte private enum CommandState : byte
{ {
GB_PRINTER_COMMAND_MAGIC1, GB_PRINTER_COMMAND_MAGIC1,
GB_PRINTER_COMMAND_MAGIC2, GB_PRINTER_COMMAND_MAGIC2,
GB_PRINTER_COMMAND_ID, GB_PRINTER_COMMAND_ID,
GB_PRINTER_COMMAND_COMPRESSION, GB_PRINTER_COMMAND_COMPRESSION,
GB_PRINTER_COMMAND_LENGTH_LOW, GB_PRINTER_COMMAND_LENGTH_LOW,
GB_PRINTER_COMMAND_LENGTH_HIGH, GB_PRINTER_COMMAND_LENGTH_HIGH,
GB_PRINTER_COMMAND_DATA, GB_PRINTER_COMMAND_DATA,
GB_PRINTER_COMMAND_CHECKSUM_LOW, GB_PRINTER_COMMAND_CHECKSUM_LOW,
GB_PRINTER_COMMAND_CHECKSUM_HIGH, GB_PRINTER_COMMAND_CHECKSUM_HIGH,
GB_PRINTER_COMMAND_ACTIVE, GB_PRINTER_COMMAND_ACTIVE,
GB_PRINTER_COMMAND_STATUS, GB_PRINTER_COMMAND_STATUS,
} }
private enum CommandID : byte private enum CommandID : byte
{ {
GB_PRINTER_INIT_COMMAND = 1, GB_PRINTER_INIT_COMMAND = 1,
GB_PRINTER_START_COMMAND = 2, GB_PRINTER_START_COMMAND = 2,
GB_PRINTER_DATA_COMMAND = 4, GB_PRINTER_DATA_COMMAND = 4,
GB_PRINTER_NOP_COMMAND = 0xF, GB_PRINTER_NOP_COMMAND = 0xF,
} }
private const int GB_PRINTER_MAX_COMMAND_LENGTH = 0x280; private const int GB_PRINTER_MAX_COMMAND_LENGTH = 0x280;
private const int GB_PRINTER_DATA_SIZE = 0x280; private const int GB_PRINTER_DATA_SIZE = 0x280;
private const ushort SerialIRQAddress = 0x58; private const ushort SerialIRQAddress = 0x58;
private readonly Gameboy gb; private readonly Gameboy gb;
private readonly PrinterCallback callback; private readonly PrinterCallback callback;
private LibGambatte.LinkCallback linkCallback; private readonly LibGambatte.LinkCallback linkCallback;
private CommandState command_state; private CommandState command_state;
private CommandID command_id; private CommandID command_id;
private bool compression; private bool compression;
private ushort length_left; private ushort length_left;
private readonly byte[] command_data = new byte[GB_PRINTER_MAX_COMMAND_LENGTH]; private readonly byte[] command_data = new byte[GB_PRINTER_MAX_COMMAND_LENGTH];
private ushort command_length; private ushort command_length;
private ushort checksum; private ushort checksum;
private byte status; private byte status;
private readonly byte[] image = new byte[160 * 200]; private readonly byte[] image = new byte[160 * 200];
private ushort image_offset; private ushort image_offset;
private byte compression_run_length; private byte compression_run_length;
private bool compression_run_is_compressed; private bool compression_run_is_compressed;
public GambattePrinter(Gameboy gb, PrinterCallback callback) public GambattePrinter(Gameboy gb, PrinterCallback callback)
{ {
this.gb = gb; this.gb = gb;
this.callback = callback; this.callback = callback;
linkCallback = OnSerial; linkCallback = OnSerial;
LibGambatte.gambatte_setlinkcallback(gb.GambatteState, linkCallback); LibGambatte.gambatte_setlinkcallback(gb.GambatteState, linkCallback);
// connect the cable // connect the cable
LibGambatte.gambatte_linkstatus(gb.GambatteState, 259); LibGambatte.gambatte_linkstatus(gb.GambatteState, 264);
} }
public void Disconnect() public void Disconnect()
{ {
if (gb.GambatteState != IntPtr.Zero) if (gb.GambatteState != IntPtr.Zero)
LibGambatte.gambatte_setlinkcallback(gb.GambatteState, null); {
} LibGambatte.gambatte_setlinkcallback(gb.GambatteState, null);
LibGambatte.gambatte_linkstatus(gb.GambatteState, 265);
private void OnSerial() }
{ }
if (LibGambatte.gambatte_linkstatus(gb.GambatteState, 256) != 0) // ClockTrigger
{ private void OnSerial()
LibGambatte.gambatte_linkstatus(gb.GambatteState, 257); // ack {
if (LibGambatte.gambatte_linkstatus(gb.GambatteState, 256) != 0) // ClockTrigger
byte output = HandleSerial((byte)LibGambatte.gambatte_linkstatus(gb.GambatteState, 258)); // GetOut {
LibGambatte.gambatte_linkstatus(gb.GambatteState, output); // ShiftIn LibGambatte.gambatte_linkstatus(gb.GambatteState, 257); // ack
}
} byte output = HandleSerial((byte)LibGambatte.gambatte_linkstatus(gb.GambatteState, 258)); // GetOut
LibGambatte.gambatte_linkstatus(gb.GambatteState, output); // ShiftIn
private byte HandleSerial(byte byte_received) }
{ }
byte byte_to_send = 0;
private byte HandleSerial(byte byte_received)
switch (command_state) {
{ byte byte_to_send = 0;
case CommandState.GB_PRINTER_COMMAND_MAGIC1:
if (byte_received != 0x88) switch (command_state)
{ {
return byte_to_send; case CommandState.GB_PRINTER_COMMAND_MAGIC1:
} if (byte_received != 0x88)
status &= 254; {
command_length = 0; return byte_to_send;
checksum = 0; }
break; status &= 254;
command_length = 0;
case CommandState.GB_PRINTER_COMMAND_MAGIC2: checksum = 0;
if (byte_received != 0x33) break;
{
if (byte_received != 0x88) case CommandState.GB_PRINTER_COMMAND_MAGIC2:
{ if (byte_received != 0x33)
command_state = CommandState.GB_PRINTER_COMMAND_MAGIC1; {
} if (byte_received != 0x88)
return byte_to_send; {
} command_state = CommandState.GB_PRINTER_COMMAND_MAGIC1;
break; }
return byte_to_send;
case CommandState.GB_PRINTER_COMMAND_ID: }
command_id = (CommandID)(byte_received & 0xF); break;
break;
case CommandState.GB_PRINTER_COMMAND_ID:
case CommandState.GB_PRINTER_COMMAND_COMPRESSION: command_id = (CommandID)(byte_received & 0xF);
compression = (byte_received & 1) != 0; break;
break;
case CommandState.GB_PRINTER_COMMAND_COMPRESSION:
case CommandState.GB_PRINTER_COMMAND_LENGTH_LOW: compression = (byte_received & 1) != 0;
length_left = byte_received; break;
break;
case CommandState.GB_PRINTER_COMMAND_LENGTH_LOW:
case CommandState.GB_PRINTER_COMMAND_LENGTH_HIGH: length_left = byte_received;
length_left |= (ushort)((byte_received & 3) << 8); break;
break;
case CommandState.GB_PRINTER_COMMAND_LENGTH_HIGH:
case CommandState.GB_PRINTER_COMMAND_DATA: length_left |= (ushort)((byte_received & 3) << 8);
if (command_length != GB_PRINTER_MAX_COMMAND_LENGTH) break;
{
if (compression) case CommandState.GB_PRINTER_COMMAND_DATA:
{ if (command_length != GB_PRINTER_MAX_COMMAND_LENGTH)
if (compression_run_length == 0) {
{ if (compression)
compression_run_is_compressed = (byte_received & 0x80) != 0; {
compression_run_length = (byte)((byte_received & 0x7F) + 1 + (compression_run_is_compressed ? 1 : 0)); if (compression_run_length == 0)
} {
else if (compression_run_is_compressed) compression_run_is_compressed = (byte_received & 0x80) != 0;
{ compression_run_length = (byte)((byte_received & 0x7F) + 1 + (compression_run_is_compressed ? 1 : 0));
while (compression_run_length > 0) }
{ else if (compression_run_is_compressed)
command_data[command_length++] = byte_received; {
compression_run_length--; while (compression_run_length > 0)
if (command_length == GB_PRINTER_MAX_COMMAND_LENGTH) {
{ command_data[command_length++] = byte_received;
compression_run_length = 0; compression_run_length--;
} if (command_length == GB_PRINTER_MAX_COMMAND_LENGTH)
} {
} compression_run_length = 0;
else }
{ }
command_data[command_length++] = byte_received; }
compression_run_length--; else
} {
} command_data[command_length++] = byte_received;
else compression_run_length--;
{ }
command_data[command_length++] = byte_received; }
} else
} {
length_left--; command_data[command_length++] = byte_received;
break; }
}
case CommandState.GB_PRINTER_COMMAND_CHECKSUM_LOW: length_left--;
checksum ^= byte_received; break;
break;
case CommandState.GB_PRINTER_COMMAND_CHECKSUM_LOW:
case CommandState.GB_PRINTER_COMMAND_CHECKSUM_HIGH: checksum ^= byte_received;
checksum ^= (ushort)(byte_received << 8); break;
if (checksum != 0)
{ case CommandState.GB_PRINTER_COMMAND_CHECKSUM_HIGH:
status |= 1; /* Checksum error*/ checksum ^= (ushort)(byte_received << 8);
command_state = CommandState.GB_PRINTER_COMMAND_MAGIC1; if (checksum != 0)
return byte_to_send; {
} status |= 1; /* Checksum error*/
break; command_state = CommandState.GB_PRINTER_COMMAND_MAGIC1;
return byte_to_send;
case CommandState.GB_PRINTER_COMMAND_ACTIVE: }
byte_to_send = 0x81; break;
break;
case CommandState.GB_PRINTER_COMMAND_ACTIVE:
case CommandState.GB_PRINTER_COMMAND_STATUS: byte_to_send = 0x81;
break;
if (((int)command_id & 0xF) == (byte)CommandID.GB_PRINTER_INIT_COMMAND)
{ case CommandState.GB_PRINTER_COMMAND_STATUS:
/* Games expect INIT commands to return 0? */
byte_to_send = 0; if (((int)command_id & 0xF) == (byte)CommandID.GB_PRINTER_INIT_COMMAND)
} {
else /* Games expect INIT commands to return 0? */
{ byte_to_send = 0;
byte_to_send = status; }
} else
{
/* Printing is done instantly, but let the game recieve a 6 (Printing) status at least once, for compatibility */ byte_to_send = status;
if (status == 6) }
{
status = 4; /* Done */ /* Printing is done instantly, but let the game recieve a 6 (Printing) status at least once, for compatibility */
} if (status == 6)
{
command_state = CommandState.GB_PRINTER_COMMAND_MAGIC1; status = 4; /* Done */
HandleCommand(); }
return byte_to_send;
} command_state = CommandState.GB_PRINTER_COMMAND_MAGIC1;
HandleCommand();
if (command_state >= CommandState.GB_PRINTER_COMMAND_ID && command_state < CommandState.GB_PRINTER_COMMAND_CHECKSUM_LOW) return byte_to_send;
{ }
checksum += byte_received;
} if (command_state >= CommandState.GB_PRINTER_COMMAND_ID && command_state < CommandState.GB_PRINTER_COMMAND_CHECKSUM_LOW)
{
if (command_state != CommandState.GB_PRINTER_COMMAND_DATA) checksum += byte_received;
{ }
command_state++;
} if (command_state != CommandState.GB_PRINTER_COMMAND_DATA)
{
if (command_state == CommandState.GB_PRINTER_COMMAND_DATA) command_state++;
{ }
if (length_left == 0)
{ if (command_state == CommandState.GB_PRINTER_COMMAND_DATA)
command_state++; {
} if (length_left == 0)
} {
command_state++;
return byte_to_send; }
} }
private void HandleCommand() return byte_to_send;
{ }
switch (command_id)
{ private void HandleCommand()
case CommandID.GB_PRINTER_INIT_COMMAND: {
status = 0; switch (command_id)
image_offset = 0; {
break; case CommandID.GB_PRINTER_INIT_COMMAND:
status = 0;
case CommandID.GB_PRINTER_START_COMMAND: image_offset = 0;
if (command_length == 4) break;
{
status = 6; /* Printing */ case CommandID.GB_PRINTER_START_COMMAND:
uint[] outputImage = new uint[image_offset]; if (command_length == 4)
{
int palette = command_data[2]; status = 6; /* Printing */
uint[] colors = { uint[] outputImage = new uint[image_offset];
0xFFFFFFFFU,
0xFFAAAAAAU, int palette = command_data[2];
0xFF555555U, uint[] colors = {
0xFF000000U 0xFFFFFFFFU,
}; 0xFFAAAAAAU,
for (int i = 0; i < image_offset; i++) 0xFF555555U,
{ 0xFF000000U
outputImage[i] = colors[(palette >> (image[i] * 2)) & 3]; };
} for (int i = 0; i < image_offset; i++)
{
if (callback != null) outputImage[i] = colors[(palette >> (image[i] * 2)) & 3];
{ }
// The native-friendly callback almost seems silly now :P
unsafe if (callback != null)
{ {
fixed (uint* imagePtr = outputImage) // The native-friendly callback almost seems silly now :P
{ unsafe
callback((IntPtr)imagePtr, (byte)(image_offset / 160), {
(byte)(command_data[1] >> 4), (byte)(command_data[1] & 7), fixed (uint* imagePtr = outputImage)
(byte)(command_data[3] & 0x7F)); {
} callback((IntPtr)imagePtr, (byte)(image_offset / 160),
} (byte)(command_data[1] >> 4), (byte)(command_data[1] & 7),
} (byte)(command_data[3] & 0x7F));
}
image_offset = 0; }
} }
break;
image_offset = 0;
case CommandID.GB_PRINTER_DATA_COMMAND: }
if (command_length == GB_PRINTER_DATA_SIZE) break;
{
image_offset %= (ushort)image.Length; case CommandID.GB_PRINTER_DATA_COMMAND:
status = 8; /* Received 0x280 bytes */ if (command_length == GB_PRINTER_DATA_SIZE)
{
int data_index = 0; image_offset %= (ushort)image.Length;
status = 8; /* Received 0x280 bytes */
for (int row = 2; row > 0; row--)
{ int data_index = 0;
for (int tile_x = 0; tile_x < 160 / 8; tile_x++)
{ for (int row = 2; row > 0; row--)
for (int y = 0; y < 8; y++, data_index += 2) {
{ for (int tile_x = 0; tile_x < 160 / 8; tile_x++)
for (int x_pixel = 0; x_pixel < 8; x_pixel++) {
{ for (int y = 0; y < 8; y++, data_index += 2)
image[image_offset + tile_x * 8 + x_pixel + y * 160] = {
(byte)((command_data[data_index] >> 7) | ((command_data[data_index + 1] >> 7) << 1)); for (int x_pixel = 0; x_pixel < 8; x_pixel++)
command_data[data_index] <<= 1; {
command_data[data_index + 1] <<= 1; image[image_offset + tile_x * 8 + x_pixel + y * 160] =
} (byte)((command_data[data_index] >> 7) | ((command_data[data_index + 1] >> 7) << 1));
} command_data[data_index] <<= 1;
} command_data[data_index + 1] <<= 1;
}
image_offset += 8 * 160; }
} }
}
break; image_offset += 8 * 160;
}
default: }
break; break;
}
} default:
} break;
} }
}
}
}

@ -1 +1 @@
Subproject commit 72630bfce765fc8e9fd60e78de3c3fe4aae571a1 Subproject commit dc50297053a3e8fff285629d735a9618b9406245