diff --git a/Assets/dll/libgambatte.dll b/Assets/dll/libgambatte.dll index 073a8bd5ee..d91f7c7995 100644 Binary files a/Assets/dll/libgambatte.dll and b/Assets/dll/libgambatte.dll differ diff --git a/Assets/dll/libgambatte.so b/Assets/dll/libgambatte.so index 616feee8d9..d1002107dd 100644 Binary files a/Assets/dll/libgambatte.so and b/Assets/dll/libgambatte.so differ diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ILinkable.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ILinkable.cs index e76ed0d7c6..f7e87993c1 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ILinkable.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ILinkable.cs @@ -4,6 +4,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy { public partial class Gameboy : ILinkable { - public bool LinkConnected { get; set; } + private bool _linkConnected; + + public bool LinkConnected + { + get => _linkConnected; + set { return; } + } } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IStatable.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IStatable.cs index db8c68387d..139f2dcc21 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IStatable.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IStatable.cs @@ -9,7 +9,7 @@ using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.Nintendo.Gameboy { - public partial class Gameboy : ITextStatable + public partial class Gameboy : IStatable, ITextStatable { public void SaveStateText(TextWriter writer) { diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IVideoProvider.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IVideoProvider.cs index 17f1ab92bb..d35a96eae1 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IVideoProvider.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IVideoProvider.cs @@ -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 { /// /// buffer of last frame produced diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs index 81ba126db8..dfbf7972dc 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs @@ -13,14 +13,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy /// [PortedCore(CoreNames.Gambatte, "", "Gambatte-Speedrun r717+", "https://github.com/pokemon-speedrunning/gambatte-speedrun")] [ServiceNotApplicable(new[] { typeof(IDriveLight) })] - public partial class Gameboy : IEmulator, IVideoProvider, ISoundProvider, ISaveRam, IStatable, IInputPollable, ICodeDataLogger, - IBoardInfo, IRomInfo, IDebuggable, ISettable, - IGameboyCommon, ICycleTiming, ILinkable + public partial class Gameboy : IInputPollable, IRomInfo, IGameboyCommon, ICycleTiming, ILinkable { [CoreConstructor(VSystemID.Raw.GB)] [CoreConstructor(VSystemID.Raw.GBC)] [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); ser.Register(_disassembler); @@ -707,11 +705,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy if (callback != null) { printer = new GambattePrinter(this, callback); - LinkConnected = true; + _linkConnected = true; } else { - LinkConnected = false; + _linkConnected = false; if (printer != null) // have no idea how this is ever null??? { printer.Disconnect(); diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambatteLink.IEmulator.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambatteLink.IEmulator.cs index 9583d7d21a..36d7751441 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambatteLink.IEmulator.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambatteLink.IEmulator.cs @@ -34,8 +34,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy bool linkDiscoSignalNew = controller.IsPressed("Toggle Link Connection"); if (linkDiscoSignalNew && !_linkDiscoSignal) { - _linkConnected ^= true; - Console.WriteLine("Link connect status to {0}", _linkConnected); + LinkConnected ^= true; + Console.WriteLine("Link connect status to {0}", LinkConnected); } _linkDiscoSignal = linkDiscoSignalNew; @@ -229,29 +229,29 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy 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 - if (LibGambatte.gambatte_linkstatus(_linkedCores[one].GambatteState, 262) != 0) // GetOut + LibGambatte.gambatte_linkstatus(_linkedCores[one].GambatteState, 260); // ack + 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 { - 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 - if (LibGambatte.gambatte_linkstatus(_linkedCores[two].GambatteState, 262) != 0) // GetOut + LibGambatte.gambatte_linkstatus(_linkedCores[two].GambatteState, 260); // ack + 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 { - LibGambatte.gambatte_linkstatus(_linkedCores[one].GambatteState, 264); // ShiftInOff + LibGambatte.gambatte_linkstatus(_linkedCores[one].GambatteState, 263); // ShiftInOff } } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambatteLink.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambatteLink.cs index d33b1a9961..5195003b4d 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambatteLink.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambatteLink.cs @@ -33,7 +33,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy 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); - LibGambatte.gambatte_linkstatus(_linkedCores[i].GambatteState, 259); // connect link cable _linkedCores[i].ConnectInputCallbackSystem(_inputCallbacks); _linkedCores[i].ConnectMemoryCallbackSystem(_memoryCallbacks); _linkedConts[i] = new SaveController(Gameboy.CreateControllerDefinition(false, false)); @@ -76,7 +75,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy public bool 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; diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambattePrinter.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambattePrinter.cs index fde77ce1db..fa44cb0533 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambattePrinter.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambattePrinter.cs @@ -1,311 +1,314 @@ using System; - -using BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy; - -namespace BizHawk.Emulation.Cores.Nintendo.Gameboy -{ - /// - /// Emulate the gameboy printer in managed code - /// - public class GambattePrinter - { - // A loose c->c# port of SameBoy's printer code - - private enum CommandState : byte - { - 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, - } - - private enum CommandID : byte - { - GB_PRINTER_INIT_COMMAND = 1, - GB_PRINTER_START_COMMAND = 2, - GB_PRINTER_DATA_COMMAND = 4, - GB_PRINTER_NOP_COMMAND = 0xF, - } - - private const int GB_PRINTER_MAX_COMMAND_LENGTH = 0x280; - private const int GB_PRINTER_DATA_SIZE = 0x280; - - private const ushort SerialIRQAddress = 0x58; - - private readonly Gameboy gb; - private readonly PrinterCallback callback; - private LibGambatte.LinkCallback linkCallback; - - private CommandState command_state; - private CommandID command_id; - - private bool compression; - private ushort length_left; - private readonly byte[] command_data = new byte[GB_PRINTER_MAX_COMMAND_LENGTH]; - private ushort command_length; - private ushort checksum; - private byte status; - - private readonly byte[] image = new byte[160 * 200]; - private ushort image_offset; - - private byte compression_run_length; - private bool compression_run_is_compressed; - - public GambattePrinter(Gameboy gb, PrinterCallback callback) - { - this.gb = gb; - this.callback = callback; - - linkCallback = OnSerial; - LibGambatte.gambatte_setlinkcallback(gb.GambatteState, linkCallback); - - // connect the cable - LibGambatte.gambatte_linkstatus(gb.GambatteState, 259); - } - - public void Disconnect() - { - if (gb.GambatteState != IntPtr.Zero) - LibGambatte.gambatte_setlinkcallback(gb.GambatteState, null); - } - - private void OnSerial() - { - if (LibGambatte.gambatte_linkstatus(gb.GambatteState, 256) != 0) // ClockTrigger - { - 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; - - switch (command_state) - { - case CommandState.GB_PRINTER_COMMAND_MAGIC1: - if (byte_received != 0x88) - { - return byte_to_send; - } - status &= 254; - command_length = 0; - checksum = 0; - break; - - case CommandState.GB_PRINTER_COMMAND_MAGIC2: - if (byte_received != 0x33) - { - if (byte_received != 0x88) - { - command_state = CommandState.GB_PRINTER_COMMAND_MAGIC1; - } - return byte_to_send; - } - break; - - case CommandState.GB_PRINTER_COMMAND_ID: - command_id = (CommandID)(byte_received & 0xF); - break; - - case CommandState.GB_PRINTER_COMMAND_COMPRESSION: - compression = (byte_received & 1) != 0; - break; - - case CommandState.GB_PRINTER_COMMAND_LENGTH_LOW: - length_left = byte_received; - break; - - case CommandState.GB_PRINTER_COMMAND_LENGTH_HIGH: - length_left |= (ushort)((byte_received & 3) << 8); - break; - - case CommandState.GB_PRINTER_COMMAND_DATA: - if (command_length != GB_PRINTER_MAX_COMMAND_LENGTH) - { - if (compression) - { - if (compression_run_length == 0) - { - compression_run_is_compressed = (byte_received & 0x80) != 0; - compression_run_length = (byte)((byte_received & 0x7F) + 1 + (compression_run_is_compressed ? 1 : 0)); - } - else if (compression_run_is_compressed) - { - while (compression_run_length > 0) - { - command_data[command_length++] = byte_received; - 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; - } - } - length_left--; - break; - - case CommandState.GB_PRINTER_COMMAND_CHECKSUM_LOW: - checksum ^= byte_received; - break; - - case CommandState.GB_PRINTER_COMMAND_CHECKSUM_HIGH: - checksum ^= (ushort)(byte_received << 8); - if (checksum != 0) - { - status |= 1; /* Checksum error*/ - command_state = CommandState.GB_PRINTER_COMMAND_MAGIC1; - return byte_to_send; - } - break; - - case CommandState.GB_PRINTER_COMMAND_ACTIVE: - byte_to_send = 0x81; - break; - - case CommandState.GB_PRINTER_COMMAND_STATUS: - - if (((int)command_id & 0xF) == (byte)CommandID.GB_PRINTER_INIT_COMMAND) - { - /* Games expect INIT commands to return 0? */ - byte_to_send = 0; - } - else - { - byte_to_send = status; - } - - /* Printing is done instantly, but let the game recieve a 6 (Printing) status at least once, for compatibility */ - if (status == 6) - { - status = 4; /* Done */ - } - - command_state = CommandState.GB_PRINTER_COMMAND_MAGIC1; - HandleCommand(); - return byte_to_send; - } - - if (command_state >= CommandState.GB_PRINTER_COMMAND_ID && command_state < CommandState.GB_PRINTER_COMMAND_CHECKSUM_LOW) - { - checksum += byte_received; - } - - if (command_state != CommandState.GB_PRINTER_COMMAND_DATA) - { - command_state++; - } - - if (command_state == CommandState.GB_PRINTER_COMMAND_DATA) - { - if (length_left == 0) - { - command_state++; - } - } - - return byte_to_send; - } - - private void HandleCommand() - { - switch (command_id) - { - case CommandID.GB_PRINTER_INIT_COMMAND: - status = 0; - image_offset = 0; - break; - - case CommandID.GB_PRINTER_START_COMMAND: - if (command_length == 4) - { - status = 6; /* Printing */ - uint[] outputImage = new uint[image_offset]; - - int palette = command_data[2]; - uint[] colors = { - 0xFFFFFFFFU, - 0xFFAAAAAAU, - 0xFF555555U, - 0xFF000000U - }; - for (int i = 0; i < image_offset; i++) - { - outputImage[i] = colors[(palette >> (image[i] * 2)) & 3]; - } - - if (callback != null) - { - // The native-friendly callback almost seems silly now :P - unsafe - { - fixed (uint* imagePtr = outputImage) - { - 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; - - case CommandID.GB_PRINTER_DATA_COMMAND: - if (command_length == GB_PRINTER_DATA_SIZE) - { - image_offset %= (ushort)image.Length; - status = 8; /* Received 0x280 bytes */ - - int data_index = 0; - - for (int row = 2; row > 0; row--) - { - for (int tile_x = 0; tile_x < 160 / 8; tile_x++) - { - for (int y = 0; y < 8; y++, data_index += 2) - { - for (int x_pixel = 0; x_pixel < 8; x_pixel++) - { - 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; - - default: - break; - } - } - } -} + +using BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy; + +namespace BizHawk.Emulation.Cores.Nintendo.Gameboy +{ + /// + /// Emulate the gameboy printer in managed code + /// + public class GambattePrinter + { + // A loose c->c# port of SameBoy's printer code + + private enum CommandState : byte + { + 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, + } + + private enum CommandID : byte + { + GB_PRINTER_INIT_COMMAND = 1, + GB_PRINTER_START_COMMAND = 2, + GB_PRINTER_DATA_COMMAND = 4, + GB_PRINTER_NOP_COMMAND = 0xF, + } + + private const int GB_PRINTER_MAX_COMMAND_LENGTH = 0x280; + private const int GB_PRINTER_DATA_SIZE = 0x280; + + private const ushort SerialIRQAddress = 0x58; + + private readonly Gameboy gb; + private readonly PrinterCallback callback; + private readonly LibGambatte.LinkCallback linkCallback; + + private CommandState command_state; + private CommandID command_id; + + private bool compression; + private ushort length_left; + private readonly byte[] command_data = new byte[GB_PRINTER_MAX_COMMAND_LENGTH]; + private ushort command_length; + private ushort checksum; + private byte status; + + private readonly byte[] image = new byte[160 * 200]; + private ushort image_offset; + + private byte compression_run_length; + private bool compression_run_is_compressed; + + public GambattePrinter(Gameboy gb, PrinterCallback callback) + { + this.gb = gb; + this.callback = callback; + + linkCallback = OnSerial; + LibGambatte.gambatte_setlinkcallback(gb.GambatteState, linkCallback); + + // connect the cable + LibGambatte.gambatte_linkstatus(gb.GambatteState, 264); + } + + public void Disconnect() + { + if (gb.GambatteState != IntPtr.Zero) + { + LibGambatte.gambatte_setlinkcallback(gb.GambatteState, null); + LibGambatte.gambatte_linkstatus(gb.GambatteState, 265); + } + } + + private void OnSerial() + { + if (LibGambatte.gambatte_linkstatus(gb.GambatteState, 256) != 0) // ClockTrigger + { + 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; + + switch (command_state) + { + case CommandState.GB_PRINTER_COMMAND_MAGIC1: + if (byte_received != 0x88) + { + return byte_to_send; + } + status &= 254; + command_length = 0; + checksum = 0; + break; + + case CommandState.GB_PRINTER_COMMAND_MAGIC2: + if (byte_received != 0x33) + { + if (byte_received != 0x88) + { + command_state = CommandState.GB_PRINTER_COMMAND_MAGIC1; + } + return byte_to_send; + } + break; + + case CommandState.GB_PRINTER_COMMAND_ID: + command_id = (CommandID)(byte_received & 0xF); + break; + + case CommandState.GB_PRINTER_COMMAND_COMPRESSION: + compression = (byte_received & 1) != 0; + break; + + case CommandState.GB_PRINTER_COMMAND_LENGTH_LOW: + length_left = byte_received; + break; + + case CommandState.GB_PRINTER_COMMAND_LENGTH_HIGH: + length_left |= (ushort)((byte_received & 3) << 8); + break; + + case CommandState.GB_PRINTER_COMMAND_DATA: + if (command_length != GB_PRINTER_MAX_COMMAND_LENGTH) + { + if (compression) + { + if (compression_run_length == 0) + { + compression_run_is_compressed = (byte_received & 0x80) != 0; + compression_run_length = (byte)((byte_received & 0x7F) + 1 + (compression_run_is_compressed ? 1 : 0)); + } + else if (compression_run_is_compressed) + { + while (compression_run_length > 0) + { + command_data[command_length++] = byte_received; + 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; + } + } + length_left--; + break; + + case CommandState.GB_PRINTER_COMMAND_CHECKSUM_LOW: + checksum ^= byte_received; + break; + + case CommandState.GB_PRINTER_COMMAND_CHECKSUM_HIGH: + checksum ^= (ushort)(byte_received << 8); + if (checksum != 0) + { + status |= 1; /* Checksum error*/ + command_state = CommandState.GB_PRINTER_COMMAND_MAGIC1; + return byte_to_send; + } + break; + + case CommandState.GB_PRINTER_COMMAND_ACTIVE: + byte_to_send = 0x81; + break; + + case CommandState.GB_PRINTER_COMMAND_STATUS: + + if (((int)command_id & 0xF) == (byte)CommandID.GB_PRINTER_INIT_COMMAND) + { + /* Games expect INIT commands to return 0? */ + byte_to_send = 0; + } + else + { + byte_to_send = status; + } + + /* Printing is done instantly, but let the game recieve a 6 (Printing) status at least once, for compatibility */ + if (status == 6) + { + status = 4; /* Done */ + } + + command_state = CommandState.GB_PRINTER_COMMAND_MAGIC1; + HandleCommand(); + return byte_to_send; + } + + if (command_state >= CommandState.GB_PRINTER_COMMAND_ID && command_state < CommandState.GB_PRINTER_COMMAND_CHECKSUM_LOW) + { + checksum += byte_received; + } + + if (command_state != CommandState.GB_PRINTER_COMMAND_DATA) + { + command_state++; + } + + if (command_state == CommandState.GB_PRINTER_COMMAND_DATA) + { + if (length_left == 0) + { + command_state++; + } + } + + return byte_to_send; + } + + private void HandleCommand() + { + switch (command_id) + { + case CommandID.GB_PRINTER_INIT_COMMAND: + status = 0; + image_offset = 0; + break; + + case CommandID.GB_PRINTER_START_COMMAND: + if (command_length == 4) + { + status = 6; /* Printing */ + uint[] outputImage = new uint[image_offset]; + + int palette = command_data[2]; + uint[] colors = { + 0xFFFFFFFFU, + 0xFFAAAAAAU, + 0xFF555555U, + 0xFF000000U + }; + for (int i = 0; i < image_offset; i++) + { + outputImage[i] = colors[(palette >> (image[i] * 2)) & 3]; + } + + if (callback != null) + { + // The native-friendly callback almost seems silly now :P + unsafe + { + fixed (uint* imagePtr = outputImage) + { + 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; + + case CommandID.GB_PRINTER_DATA_COMMAND: + if (command_length == GB_PRINTER_DATA_SIZE) + { + image_offset %= (ushort)image.Length; + status = 8; /* Received 0x280 bytes */ + + int data_index = 0; + + for (int row = 2; row > 0; row--) + { + for (int tile_x = 0; tile_x < 160 / 8; tile_x++) + { + for (int y = 0; y < 8; y++, data_index += 2) + { + for (int x_pixel = 0; x_pixel < 8; x_pixel++) + { + 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; + + default: + break; + } + } + } +} diff --git a/submodules/gambatte b/submodules/gambatte index 72630bfce7..dc50297053 160000 --- a/submodules/gambatte +++ b/submodules/gambatte @@ -1 +1 @@ -Subproject commit 72630bfce765fc8e9fd60e78de3c3fe4aae571a1 +Subproject commit dc50297053a3e8fff285629d735a9618b9406245