From 0ee1832d5767389b6461e6720f18a85cc2e9ffcb Mon Sep 17 00:00:00 2001 From: nattthebear Date: Sun, 9 Jul 2017 11:17:42 -0400 Subject: [PATCH] Adult Video Games --- ...izHawkSystemIdToCoreSystemEnumConverter.cs | 1 + BizHawk.Client.Common/RomLoader.cs | 8 + .../Database/FirmwareDatabase.cs | 58 +- .../BizHawk.Emulation.Cores.csproj | 2 + .../Consoles/NEC/PCFX/LibTst.cs | 48 + .../Consoles/NEC/PCFX/Tst.cs | 113 + .../Consoles/Sega/Saturn/Saturnus.cs | 8 +- .../DiscIdentifier.cs | 95 +- waterbox/pcfx/.vscode/settings.json | 10 + waterbox/pcfx/Makefile | 44 + waterbox/pcfx/cdrom/CDUtility.cpp | 430 ++ waterbox/pcfx/cdrom/CDUtility.h | 237 + waterbox/pcfx/cdrom/SimpleFIFO.h | 114 + waterbox/pcfx/cdrom/cdromif.cpp | 131 + waterbox/pcfx/cdrom/cdromif.h | 58 + waterbox/pcfx/cdrom/crc32.cpp | 130 + waterbox/pcfx/cdrom/dvdisaster.h | 171 + waterbox/pcfx/cdrom/galois-inlines.h | 40 + waterbox/pcfx/cdrom/galois.cpp | 156 + waterbox/pcfx/cdrom/l-ec.cpp | 478 ++ waterbox/pcfx/cdrom/lec.cpp | 691 +++ waterbox/pcfx/cdrom/lec.h | 77 + waterbox/pcfx/cdrom/recover-raw.cpp | 210 + waterbox/pcfx/cdrom/scsicd-pce-commands.inc | 259 ++ waterbox/pcfx/cdrom/scsicd.cpp | 3081 +++++++++++++ waterbox/pcfx/cdrom/scsicd.h | 97 + waterbox/pcfx/cdrom/scsicd_cdda_filter.inc | 69 + waterbox/pcfx/defs.h | 114 + waterbox/pcfx/endian.h | 494 ++ waterbox/pcfx/fxscsi.cpp | 47 + waterbox/pcfx/fxscsi.h | 33 + waterbox/pcfx/gamedb.inc | 1731 +++++++ waterbox/pcfx/huc6270/vdc.cpp | 1796 ++++++++ waterbox/pcfx/huc6270/vdc.h | 530 +++ waterbox/pcfx/huc6273.cpp | 260 ++ waterbox/pcfx/huc6273.h | 38 + waterbox/pcfx/input.cpp | 340 ++ waterbox/pcfx/input.h | 82 + waterbox/pcfx/input/gamepad.cpp | 80 + waterbox/pcfx/input/gamepad.h | 30 + waterbox/pcfx/input/mouse.cpp | 88 + waterbox/pcfx/input/mouse.h | 30 + waterbox/pcfx/interrupt.cpp | 212 + waterbox/pcfx/interrupt.h | 55 + waterbox/pcfx/io-handler.inc | 290 ++ waterbox/pcfx/jrevdct.cpp | 284 ++ waterbox/pcfx/jrevdct.h | 14 + waterbox/pcfx/king-bgfast-blit.inc | 124 + waterbox/pcfx/king-bgfast.inc | 270 ++ waterbox/pcfx/king.cpp | 3842 ++++++++++++++++ waterbox/pcfx/king.h | 212 + waterbox/pcfx/king_mix_body.inc | 83 + waterbox/pcfx/math_ops.h | 278 ++ waterbox/pcfx/mem-handler.inc | 328 ++ waterbox/pcfx/notes/GAME_NOTES | 140 + waterbox/pcfx/pce_psg/pce_psg.cpp | 821 ++++ waterbox/pcfx/pce_psg/pce_psg.h | 187 + waterbox/pcfx/pcfx.cpp | 841 ++++ waterbox/pcfx/pcfx.h | 67 + waterbox/pcfx/rainbow.cpp | 819 ++++ waterbox/pcfx/rainbow.h | 56 + waterbox/pcfx/rainbow_acuv.inc | 4096 +++++++++++++++++ waterbox/pcfx/rainbow_acy.inc | 4096 +++++++++++++++++ waterbox/pcfx/rainbow_dcuv.inc | 256 ++ waterbox/pcfx/rainbow_dcy.inc | 512 +++ waterbox/pcfx/sound/Blip_Buffer.cpp | 457 ++ waterbox/pcfx/sound/Blip_Buffer.h | 498 ++ waterbox/pcfx/sound/OwlResampler.cpp | 803 ++++ waterbox/pcfx/sound/OwlResampler.h | 159 + waterbox/pcfx/sound/Stereo_Buffer.cpp | 146 + waterbox/pcfx/sound/Stereo_Buffer.h | 69 + waterbox/pcfx/soundbox.cpp | 677 +++ waterbox/pcfx/soundbox.h | 45 + waterbox/pcfx/timer.cpp | 175 + waterbox/pcfx/timer.h | 49 + waterbox/pcfx/v810/v810_cpu.cpp | 1540 +++++++ waterbox/pcfx/v810/v810_cpu.h | 362 ++ waterbox/pcfx/v810/v810_cpuD.cpp | 385 ++ waterbox/pcfx/v810/v810_cpuD.h | 15 + waterbox/pcfx/v810/v810_do_am.h | 72 + waterbox/pcfx/v810/v810_fp_ops.cpp | 408 ++ waterbox/pcfx/v810/v810_fp_ops.h | 77 + waterbox/pcfx/v810/v810_oploop.inc | 1130 +++++ waterbox/pcfx/v810/v810_opt.h | 170 + 84 files changed, 37573 insertions(+), 56 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Consoles/NEC/PCFX/LibTst.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/NEC/PCFX/Tst.cs create mode 100644 waterbox/pcfx/.vscode/settings.json create mode 100644 waterbox/pcfx/Makefile create mode 100644 waterbox/pcfx/cdrom/CDUtility.cpp create mode 100644 waterbox/pcfx/cdrom/CDUtility.h create mode 100644 waterbox/pcfx/cdrom/SimpleFIFO.h create mode 100644 waterbox/pcfx/cdrom/cdromif.cpp create mode 100644 waterbox/pcfx/cdrom/cdromif.h create mode 100644 waterbox/pcfx/cdrom/crc32.cpp create mode 100644 waterbox/pcfx/cdrom/dvdisaster.h create mode 100644 waterbox/pcfx/cdrom/galois-inlines.h create mode 100644 waterbox/pcfx/cdrom/galois.cpp create mode 100644 waterbox/pcfx/cdrom/l-ec.cpp create mode 100644 waterbox/pcfx/cdrom/lec.cpp create mode 100644 waterbox/pcfx/cdrom/lec.h create mode 100644 waterbox/pcfx/cdrom/recover-raw.cpp create mode 100644 waterbox/pcfx/cdrom/scsicd-pce-commands.inc create mode 100644 waterbox/pcfx/cdrom/scsicd.cpp create mode 100644 waterbox/pcfx/cdrom/scsicd.h create mode 100644 waterbox/pcfx/cdrom/scsicd_cdda_filter.inc create mode 100644 waterbox/pcfx/defs.h create mode 100644 waterbox/pcfx/endian.h create mode 100644 waterbox/pcfx/fxscsi.cpp create mode 100644 waterbox/pcfx/fxscsi.h create mode 100644 waterbox/pcfx/gamedb.inc create mode 100644 waterbox/pcfx/huc6270/vdc.cpp create mode 100644 waterbox/pcfx/huc6270/vdc.h create mode 100644 waterbox/pcfx/huc6273.cpp create mode 100644 waterbox/pcfx/huc6273.h create mode 100644 waterbox/pcfx/input.cpp create mode 100644 waterbox/pcfx/input.h create mode 100644 waterbox/pcfx/input/gamepad.cpp create mode 100644 waterbox/pcfx/input/gamepad.h create mode 100644 waterbox/pcfx/input/mouse.cpp create mode 100644 waterbox/pcfx/input/mouse.h create mode 100644 waterbox/pcfx/interrupt.cpp create mode 100644 waterbox/pcfx/interrupt.h create mode 100644 waterbox/pcfx/io-handler.inc create mode 100644 waterbox/pcfx/jrevdct.cpp create mode 100644 waterbox/pcfx/jrevdct.h create mode 100644 waterbox/pcfx/king-bgfast-blit.inc create mode 100644 waterbox/pcfx/king-bgfast.inc create mode 100644 waterbox/pcfx/king.cpp create mode 100644 waterbox/pcfx/king.h create mode 100644 waterbox/pcfx/king_mix_body.inc create mode 100644 waterbox/pcfx/math_ops.h create mode 100644 waterbox/pcfx/mem-handler.inc create mode 100644 waterbox/pcfx/notes/GAME_NOTES create mode 100644 waterbox/pcfx/pce_psg/pce_psg.cpp create mode 100644 waterbox/pcfx/pce_psg/pce_psg.h create mode 100644 waterbox/pcfx/pcfx.cpp create mode 100644 waterbox/pcfx/pcfx.h create mode 100644 waterbox/pcfx/rainbow.cpp create mode 100644 waterbox/pcfx/rainbow.h create mode 100644 waterbox/pcfx/rainbow_acuv.inc create mode 100644 waterbox/pcfx/rainbow_acy.inc create mode 100644 waterbox/pcfx/rainbow_dcuv.inc create mode 100644 waterbox/pcfx/rainbow_dcy.inc create mode 100644 waterbox/pcfx/sound/Blip_Buffer.cpp create mode 100644 waterbox/pcfx/sound/Blip_Buffer.h create mode 100644 waterbox/pcfx/sound/OwlResampler.cpp create mode 100644 waterbox/pcfx/sound/OwlResampler.h create mode 100644 waterbox/pcfx/sound/Stereo_Buffer.cpp create mode 100644 waterbox/pcfx/sound/Stereo_Buffer.h create mode 100644 waterbox/pcfx/soundbox.cpp create mode 100644 waterbox/pcfx/soundbox.h create mode 100644 waterbox/pcfx/timer.cpp create mode 100644 waterbox/pcfx/timer.h create mode 100644 waterbox/pcfx/v810/v810_cpu.cpp create mode 100644 waterbox/pcfx/v810/v810_cpu.h create mode 100644 waterbox/pcfx/v810/v810_cpuD.cpp create mode 100644 waterbox/pcfx/v810/v810_cpuD.h create mode 100644 waterbox/pcfx/v810/v810_do_am.h create mode 100644 waterbox/pcfx/v810/v810_fp_ops.cpp create mode 100644 waterbox/pcfx/v810/v810_fp_ops.h create mode 100644 waterbox/pcfx/v810/v810_oploop.inc create mode 100644 waterbox/pcfx/v810/v810_opt.h diff --git a/BizHawk.Client.ApiHawk/Classes/BizHawkSystemIdToCoreSystemEnumConverter.cs b/BizHawk.Client.ApiHawk/Classes/BizHawkSystemIdToCoreSystemEnumConverter.cs index 3d70009e06..9db622672e 100644 --- a/BizHawk.Client.ApiHawk/Classes/BizHawkSystemIdToCoreSystemEnumConverter.cs +++ b/BizHawk.Client.ApiHawk/Classes/BizHawkSystemIdToCoreSystemEnumConverter.cs @@ -105,6 +105,7 @@ namespace BizHawk.Client.ApiHawk case "O2": case "SGB": case "UZE": + case "PCFX": return 0; // like I give a shit default: diff --git a/BizHawk.Client.Common/RomLoader.cs b/BizHawk.Client.Common/RomLoader.cs index 8533bc7552..bf48e04e7b 100644 --- a/BizHawk.Client.Common/RomLoader.cs +++ b/BizHawk.Client.Common/RomLoader.cs @@ -23,6 +23,7 @@ using BizHawk.Emulation.DiscSystem; using GPGX64 = BizHawk.Emulation.Cores.Consoles.Sega.gpgx; using BizHawk.Emulation.Cores.Consoles.Sega.Saturn; +using BizHawk.Emulation.Cores.Consoles.NEC.PCFX; namespace BizHawk.Client.Common { @@ -484,6 +485,10 @@ namespace BizHawk.Client.Common case DiscType.MegaCD: game.System = "GEN"; break; + case DiscType.PCFX: + game.System = "PCFX"; + break; + case DiscType.AudioDisc: case DiscType.TurboCD: case DiscType.UnknownCDFS: @@ -532,6 +537,9 @@ namespace BizHawk.Client.Common nextEmulator.CoreComm.RomStatusDetails = sw.ToString(); } + break; + case "PCFX": + nextEmulator = new Tst(nextComm, new[] { disc }); break; case "PCE": case "PCECD": diff --git a/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs b/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs index 06282b6579..6297b9add2 100644 --- a/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs +++ b/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs @@ -208,20 +208,28 @@ namespace BizHawk.Emulation.Common FirmwareAndOption("b2e1955d957a475de2411770452eff4ea19f4cee", 1024, "O2", "BIOS", "Odyssey2.bin", "Odyssey 2 Bios"); FirmwareAndOption("a6120aed50831c9c0d95dbdf707820f601d9452e", 1024, "O2", "BIOS-C52", "PhillipsC52.bin", "Phillips C52 Bios"); - + FirmwareAndOption("4ED31EC6B0B175BB109C0EB5FD3D193DA823339F", 256, "GB", "World", "GB_boot_ROM.gb", "Game Boy BIOS"); FirmwareAndOption("1293D68BF9643BC4F36954C1E80E38F39864528D", 2304, "GBC", "World", "GBC_boot_ROM.gb", "Game Boy Color BIOS"); + + Firmware("PCFX", "BIOS", "pcfxbios.bin"); + Option("1a77fd83e337f906aecab27a1604db064cf10074", 1024 * 1024, "PCFX", "BIOS"); + Option("8b662f7548078be52a871565e19511ccca28c5c8", 1024 * 1024, "PCFX", "BIOS"); + + Firmware("PCFX", "SCSIROM", "fx-scsi.rom"); + Option("65482a23ac5c10a6095aee1db5824cca54ead6e5", 512 * 1024, "PCFX", "SCSIROM"); + } // adds a defined firmware ID to the database private static void Firmware(string systemId, string id, string descr) { var fr = new FirmwareRecord - { - SystemId = systemId, - FirmwareId = id, - Descr = descr - }; + { + SystemId = systemId, + FirmwareId = id, + Descr = descr + }; FirmwareRecords.Add(fr); } @@ -230,22 +238,22 @@ namespace BizHawk.Emulation.Common private static FirmwareOption Option(string hash, long size, string systemId, string id, FirmwareOptionStatus status = FirmwareOptionStatus.Acceptable) { var fo = new FirmwareOption - { - SystemId = systemId, - FirmwareId = id, - Hash = hash, - Status = status, - Size = size - }; + { + SystemId = systemId, + FirmwareId = id, + Hash = hash, + Status = status, + Size = size + }; FirmwareOptions.Add(fo); - + // first option is automatically ideal if (FirmwareOptions.Count == 1 && fo.Status == FirmwareOptionStatus.Acceptable) { fo.Status = FirmwareOptionStatus.Ideal; } - + return fo; } @@ -269,13 +277,13 @@ namespace BizHawk.Emulation.Common string hashfix = hash.ToUpperInvariant(); var ff = new FirmwareFile - { - Hash = hashfix, - Size = size, - RecommendedName = recommendedName, - Description = descr, - Info = additionalInfo - }; + { + Hash = hashfix, + Size = size, + RecommendedName = recommendedName, + Description = descr, + Info = additionalInfo + }; FirmwareFiles.Add(ff); FirmwareFilesByHash[hashfix] = ff; return ff; @@ -335,9 +343,9 @@ namespace BizHawk.Emulation.Common { var found = from fr in FirmwareRecords - where fr.FirmwareId == firmwareId - && fr.SystemId == sysId - select fr; + where fr.FirmwareId == firmwareId + && fr.SystemId == sysId + select fr; return found.FirstOrDefault(); } diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 3d7c6d2f8b..9be03c184d 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -468,6 +468,8 @@ + + Gambatte.cs diff --git a/BizHawk.Emulation.Cores/Consoles/NEC/PCFX/LibTst.cs b/BizHawk.Emulation.Cores/Consoles/NEC/PCFX/LibTst.cs new file mode 100644 index 0000000000..12aa7eb160 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/NEC/PCFX/LibTst.cs @@ -0,0 +1,48 @@ +using BizHawk.Common.BizInvoke; +using BizHawk.Emulation.Cores.Consoles.Sega.Saturn; +using BizHawk.Emulation.Cores.Waterbox; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Consoles.NEC.PCFX +{ + public abstract class LibTst : LibWaterboxCore + { + [StructLayout(LayoutKind.Sequential)] + public new class FrameInfo : LibWaterboxCore.FrameInfo + { + public uint Port1Buttons; + public uint Port2Buttons; + public uint ConsoleButtons; + } + + [Flags] + public enum Layers : int + { + None = 0, + BG0 = 1, + BG1 = 2, + BG2 = 4, + BG3 = 8, + VDCA_BG = 16, + VDCA_SPR = 32, + VDCB_BG = 64, + VDCB_SPR = 128, + RAINBOW = 256 + } + + [BizImport(CC)] + public abstract void SetCDCallbacks(LibSaturnus.CDTOCCallback toccallback, + LibSaturnus.CDSectorCallback sectorcallback); + + [BizImport(CC)] + public abstract bool Init(int numDisks, byte[] bios); + + [BizImport(CC)] + public abstract void EnableLayers(Layers mask); + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/NEC/PCFX/Tst.cs b/BizHawk.Emulation.Cores/Consoles/NEC/PCFX/Tst.cs new file mode 100644 index 0000000000..72ecbf2f27 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/NEC/PCFX/Tst.cs @@ -0,0 +1,113 @@ +using BizHawk.Emulation.Common; +using BizHawk.Emulation.Cores.Consoles.Sega.Saturn; +using BizHawk.Emulation.Cores.Waterbox; +using BizHawk.Emulation.DiscSystem; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using System.IO; + +namespace BizHawk.Emulation.Cores.Consoles.NEC.PCFX +{ + [CoreAttributes("T. S. T.", "Ryphecha", true, false, "0.9.44.1", + "https://mednafen.github.io/releases/", false)] + public class Tst : WaterboxCore, IDriveLight + { + private static readonly DiscSectorReaderPolicy _diskPolicy = new DiscSectorReaderPolicy + { + DeinterleavedSubcode = false + }; + + private LibTst _core; + private Disc[] _disks; + private DiscSectorReader[] _diskReaders; + private LibSaturnus.CDTOCCallback _cdTocCallback; + private LibSaturnus.CDSectorCallback _cdSectorCallback; + + [CoreConstructor("PCFX")] + public Tst(CoreComm comm, byte[] rom) + :base(comm, new Configuration()) + { + throw new InvalidOperationException("To load a PC-FX game, please load the CUE file and not the BIN file."); + } + + // MDFNGameInfo->fps = (uint32)((double)7159090.90909090 / 455 / 263 * 65536 * 256); + public Tst(CoreComm comm, IEnumerable disks) + :base(comm, new Configuration + { + DefaultFpsNumerator = 7159091, + DefaultFpsDenominator = 455 * 263, + DefaultWidth = 256, + DefaultHeight = 240, + MaxWidth = 1024, + MaxHeight = 240, + MaxSamples = 2048, + SystemId = "PCFX" + }) + { + var bios = comm.CoreFileProvider.GetFirmware("PCFX", "BIOS", true); + if (bios.Length != 1024 * 1024) + throw new InvalidOperationException("Wrong size BIOS file!"); + + _disks = disks.ToArray(); + _diskReaders = disks.Select(d => new DiscSectorReader(d) { Policy = _diskPolicy }).ToArray(); + _cdTocCallback = CDTOCCallback; + _cdSectorCallback = CDSectorCallback; + + _core = PreInit(new PeRunnerOptions + { + Filename = "pcfx.wbx", + SbrkHeapSizeKB = 1024, + SealedHeapSizeKB = 4 * 1024, + InvisibleHeapSizeKB = 256, + PlainHeapSizeKB = 256, + MmapHeapSizeKB = 32 * 1024 + }); + + SetCdCallbacks(); + if (!_core.Init(_disks.Length, bios)) + throw new InvalidOperationException("Core rejected the CDs!"); + ClearCdCallbacks(); + + PostInit(); + SetCdCallbacks(); + } + + protected override void LoadStateBinaryInternal(BinaryReader reader) + { + SetCdCallbacks(); + } + + protected override LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller, bool render, bool rendersound) + { + return new LibTst.FrameInfo(); + } + + private void CDTOCCallback(int disk, [In, Out]LibSaturnus.TOC t) + { + Saturnus.SetupTOC(t, _disks[disk].TOC); + } + private void CDSectorCallback(int disk, int lba, IntPtr dest) + { + var buff = new byte[2448]; + _diskReaders[disk].ReadLBA_2448(lba, buff, 0); + Marshal.Copy(buff, 0, dest, 2448); + DriveLightOn = true; + } + + private void SetCdCallbacks() + { + _core.SetCDCallbacks(_cdTocCallback, _cdSectorCallback); + } + private void ClearCdCallbacks() + { + _core.SetCDCallbacks(null, null); + } + + public bool DriveLightEnabled => true; + public bool DriveLightOn { get; private set; } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/Saturnus.cs b/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/Saturnus.cs index d09a332bdb..78da22e14c 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/Saturnus.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/Saturnus.cs @@ -574,10 +574,9 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.Saturn Marshal.Copy(data, 0, dest, data.Length); } - private void CDTOCCallback(int disk, [In, Out]LibSaturnus.TOC t) + public static void SetupTOC(LibSaturnus.TOC t, DiscTOC tin) { // everything that's not commented, we're sure about - var tin = _disks[disk].TOC; t.FirstTrack = tin.FirstRecordedTrackNumber; t.LastTrack = tin.LastRecordedTrackNumber; t.DiskType = (int)tin.Session1Format; @@ -589,6 +588,11 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.Saturn t.Tracks[i].Valid = tin.TOCItems[i].Exists ? 1 : 0; } } + + private void CDTOCCallback(int disk, [In, Out]LibSaturnus.TOC t) + { + SetupTOC(t, _disks[disk].TOC); + } private void CDSectorCallback(int disk, int lba, IntPtr dest) { var buff = new byte[2448]; diff --git a/BizHawk.Emulation.DiscSystem/DiscIdentifier.cs b/BizHawk.Emulation.DiscSystem/DiscIdentifier.cs index 9918b7c470..63cca7450c 100644 --- a/BizHawk.Emulation.DiscSystem/DiscIdentifier.cs +++ b/BizHawk.Emulation.DiscSystem/DiscIdentifier.cs @@ -21,22 +21,22 @@ namespace BizHawk.Emulation.DiscSystem /// This is definitely a CDFS disc, but we can't identify anything more about it /// UnknownCDFS, - + /// /// Sony PSX /// SonyPSX, - + /// /// Sony PSP /// SonyPSP, - + /// /// Sega Saturn /// SegaSaturn, - + /// /// Its not clear whether we can ever have enough info to ID a turboCD disc (we're using hashes) /// @@ -45,24 +45,29 @@ namespace BizHawk.Emulation.DiscSystem /// /// MegaDrive addon /// - MegaCD + MegaCD, + + /// + /// By NEC. + /// + PCFX } public class DiscIdentifier { public DiscIdentifier(Disc disc) { - this.disc = disc; - dsr = new DiscSectorReader(disc); - + _disc = disc; + _dsr = new DiscSectorReader(disc); + //the first check for mode 0 should be sufficient for blocking attempts to read audio sectors, so dont do this //dsr.Policy.ThrowExceptions2048 = false; } - Disc disc; - DiscSectorReader dsr; - Dictionary sectorCache = new Dictionary(); - + private readonly Disc _disc; + private readonly DiscSectorReader _dsr; + private readonly Dictionary _sectorCache = new Dictionary(); + /// /// Attempts to determine the type of the disc. /// In the future, we might return a struct or a class with more detailed information @@ -72,40 +77,53 @@ namespace BizHawk.Emulation.DiscSystem //check track 1's data type. if it's an audio track, further data-track testing is useless //furthermore, it's probably senseless (no binary data there to read) //however a sector could mark itself as audio without actually being.. we'll just wait for that one. - if (dsr.ReadLBA_Mode(disc.TOC.TOCItems[1].LBA) == 0) return DiscType.AudioDisc; - - //sega doesnt put anything identifying in the cdfs volume info. but its consistent about putting its own header here in sector 0 - if (DetectSegaSaturn()) return DiscType.SegaSaturn; // not fully tested yet - if (DetectMegaCD()) return DiscType.MegaCD; + if (DetectPCFX()) + return DiscType.PCFX; + + if (!_disc.TOC.TOCItems[1].IsData) + return DiscType.AudioDisc; + + // if (_dsr.ReadLBA_Mode(_disc.TOC.TOCItems[1].LBA) == 0) + // return DiscType.AudioDisc; + + // sega doesnt put anything identifying in the cdfs volume info. but its consistent about putting its own header here in sector 0 + if (DetectSegaSaturn()) + return DiscType.SegaSaturn; + // not fully tested yet - if (DetectPSX()) return DiscType.SonyPSX; + if (DetectMegaCD()) + return DiscType.MegaCD; + + // not fully tested yet + if (DetectPSX()) + return DiscType.SonyPSX; //we dont know how to detect TurboCD. //an emulator frontend will likely just guess TurboCD if the disc is UnknownFormat //(we can also have a gameDB!) var discView = EDiscStreamView.DiscStreamView_Mode1_2048; - if (disc.TOC.Session1Format == SessionFormat.Type20_CDXA) + if (_disc.TOC.Session1Format == SessionFormat.Type20_CDXA) discView = EDiscStreamView.DiscStreamView_Mode2_Form1_2048; var iso = new ISOFile(); - bool isIso = iso.Parse(new DiscStream(disc, discView, 0)); + bool isIso = iso.Parse(new DiscStream(_disc, discView, 0)); if (isIso) { var appId = System.Text.Encoding.ASCII.GetString(iso.VolumeDescriptors[0].ApplicationIdentifier).TrimEnd('\0', ' '); - + //for example: PSX magical drop F (JP SLPS_02337) doesn't have the correct iso PVD fields //but, some PSX games (junky rips) don't have the 'licensed by string' so we'll hope they get caught here if (appId == "PLAYSTATION") - return DiscType.SonyPSX; + return DiscType.SonyPSX; - if(appId == "PSP GAME") + if (appId == "PSP GAME") return DiscType.SonyPSP; - + return DiscType.UnknownCDFS; } @@ -130,7 +148,7 @@ namespace BizHawk.Emulation.DiscSystem bool DetectPSX() { - if (!StringAt(" Licensed by ",0, 4)) return false; + if (!StringAt(" Licensed by ", 0, 4)) return false; return (StringAt("Sony Computer Entertainment Euro", 32, 4) || StringAt("Sony Computer Entertainment Inc.", 32, 4) || StringAt("Sony Computer Entertainment Amer", 32, 4) @@ -138,19 +156,38 @@ namespace BizHawk.Emulation.DiscSystem ); } - private bool StringAt(string s, int n, int lba = 0) + bool DetectPCFX() + { + var toc = _disc.TOC; + for (int t = toc.FirstRecordedTrackNumber; + t <= toc.LastRecordedTrackNumber; + t++) + { + var track = _disc.TOC.TOCItems[t]; + if (track.IsData && StringAt("PC-FX:Hu_CD-ROM", 0, track.LBA)) + return true; + } + return false; + } + + private byte[] ReadSectorCached(int lba) { //read it if we dont have it cached //we wont be caching very much here, it's no big deal //identification is not something we want to take a long time byte[] data; - if (!sectorCache.TryGetValue(lba, out data)) + if (!_sectorCache.TryGetValue(lba, out data)) { data = new byte[2048]; - dsr.ReadLBA_2048(lba, data, 0); - sectorCache[lba] = data; + _dsr.ReadLBA_2048(lba, data, 0); + _sectorCache[lba] = data; } + return data; + } + private bool StringAt(string s, int n, int lba = 0) + { + var data = ReadSectorCached(lba); byte[] cmp = System.Text.Encoding.ASCII.GetBytes(s); byte[] cmp2 = new byte[cmp.Length]; Buffer.BlockCopy(data, n, cmp2, 0, cmp.Length); diff --git a/waterbox/pcfx/.vscode/settings.json b/waterbox/pcfx/.vscode/settings.json new file mode 100644 index 0000000000..68987969ed --- /dev/null +++ b/waterbox/pcfx/.vscode/settings.json @@ -0,0 +1,10 @@ +// Place your settings in this file to overwrite default and user settings. +{ + "editor.tabSize": 4, + "editor.insertSpaces": false, + "editor.detectIndentation": false, + "files.associations": { + "*.inc": "cpp", + "system_error": "cpp" + } +} \ No newline at end of file diff --git a/waterbox/pcfx/Makefile b/waterbox/pcfx/Makefile new file mode 100644 index 0000000000..3ff8fb18d8 --- /dev/null +++ b/waterbox/pcfx/Makefile @@ -0,0 +1,44 @@ +CC = x86_64-nt64-midipix-g++ + +CCFLAGS:= -I. -I../emulibc \ + -Wall -Werror=pointer-to-int-cast -Werror=int-to-pointer-cast -Werror=implicit-function-declaration \ + -std=c++0x -fomit-frame-pointer -fvisibility=hidden -fno-exceptions -fno-rtti \ + -DLSB_FIRST \ + -O0 -g + +TARGET = pcfx.wbx + +LDFLAGS = -Wl,--dynamicbase,--export-all-symbols + +ROOT_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) +SRCS:=$(shell find $(ROOT_DIR) -type f -name '*.cpp') +OBJ_DIR:=$(ROOT_DIR)/obj + +_OBJS:=$(SRCS:.cpp=.o) +OBJS:=$(patsubst $(ROOT_DIR)%,$(OBJ_DIR)%,$(_OBJS)) + +$(OBJ_DIR)/%.o: %.cpp + @mkdir -p $(@D) + @$(CC) -c -o $@ $< $(CCFLAGS) + +all: $(TARGET) + +.PHONY: clean all + +$(TARGET).in: $(OBJS) + @$(CC) -o $@ $(LDFLAGS) $(CCFLAGS) $(OBJS) ../emulibc/libemuhost.so + +$(TARGET): $(TARGET).in +# strip $< -o $@ -R /4 -R /14 -R /29 -R /41 -R /55 -R /67 -R /78 -R /89 -R /104 + cp $< $@ + +clean: + rm -rf $(OBJ_DIR) + rm -f $(TARGET).in + rm -f $(TARGET) + +print-%: + @echo $* = $($*) + +#install: +# $(CP) $(TARGET) $(DEST_$(ARCH)) diff --git a/waterbox/pcfx/cdrom/CDUtility.cpp b/waterbox/pcfx/cdrom/CDUtility.cpp new file mode 100644 index 0000000000..5d097e016d --- /dev/null +++ b/waterbox/pcfx/cdrom/CDUtility.cpp @@ -0,0 +1,430 @@ +/* Mednafen - Multi-system Emulator + * + * Subchannel Q CRC Code: Copyright (C) 1998 Andreas Mueller + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "defs.h" +#include "CDUtility.h" +#include "dvdisaster.h" +#include "lec.h" + +#include + +namespace CDUtility +{ + +// lookup table for crc calculation +static uint16 subq_crctab[256] = +{ + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108, + 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210, + 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B, + 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401, + 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, + 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, + 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, + 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5, + 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC, + 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4, + 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, + 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, + 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, + 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, + 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1, + 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB, + 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0, + 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8, + 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, + 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, + 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, + 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E, + 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07, + 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D, + 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74, + 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 +}; + + +static uint8 scramble_table[2352 - 12]; + +static bool CDUtility_Inited = false; + +static void InitScrambleTable(void) +{ + unsigned cv = 1; + + for(unsigned i = 12; i < 2352; i++) + { + unsigned char z = 0; + + for(int b = 0; b < 8; b++) + { + z |= (cv & 1) << b; + + int feedback = ((cv >> 1) & 1) ^ (cv & 1); + cv = (cv >> 1) | (feedback << 14); + } + + scramble_table[i - 12] = z; + } + + //for(int i = 0; i < 2352 - 12; i++) + // printf("0x%02x, ", scramble_table[i]); +} + +void CDUtility_Init(void) +{ + if(!CDUtility_Inited) + { + //Init_LEC_Correct(); + + InitScrambleTable(); + + CDUtility_Inited = true; + } +} + +void encode_mode0_sector(uint32 aba, uint8 *sector_data) +{ + CDUtility_Init(); + + lec_encode_mode0_sector(aba, sector_data); +} + +void encode_mode1_sector(uint32 aba, uint8 *sector_data) +{ + CDUtility_Init(); + + lec_encode_mode1_sector(aba, sector_data); +} + +void encode_mode2_sector(uint32 aba, uint8 *sector_data) +{ + CDUtility_Init(); + + lec_encode_mode2_sector(aba, sector_data); +} + +void encode_mode2_form1_sector(uint32 aba, uint8 *sector_data) +{ + CDUtility_Init(); + + lec_encode_mode2_form1_sector(aba, sector_data); +} + +void encode_mode2_form2_sector(uint32 aba, uint8 *sector_data) +{ + CDUtility_Init(); + + lec_encode_mode2_form2_sector(aba, sector_data); +} + +bool edc_check(const uint8 *sector_data, bool xa) +{ + CDUtility_Init(); + + return(CheckEDC(sector_data, xa)); +} + +bool edc_lec_check_and_correct(uint8 *sector_data, bool xa) +{ + CDUtility_Init(); + + return(ValidateRawSector(sector_data, xa)); +} + + +bool subq_check_checksum(const uint8 *SubQBuf) +{ + uint16 crc = 0; + uint16 stored_crc = 0; + + stored_crc = SubQBuf[0xA] << 8; + stored_crc |= SubQBuf[0xB]; + + for(int i = 0; i < 0xA; i++) + crc = subq_crctab[(crc >> 8) ^ SubQBuf[i]] ^ (crc << 8); + + crc = ~crc; + + return(crc == stored_crc); +} + +void subq_generate_checksum(uint8 *buf) +{ + uint16 crc = 0; + + for(int i = 0; i < 0xA; i++) + crc = subq_crctab[(crc >> 8) ^ buf[i]] ^ (crc << 8); + + // Checksum + buf[0xa] = ~(crc >> 8); + buf[0xb] = ~(crc); +} + +void subq_deinterleave(const uint8 *SubPWBuf, uint8 *qbuf) +{ + memset(qbuf, 0, 0xC); + + for(int i = 0; i < 96; i++) + { + qbuf[i >> 3] |= ((SubPWBuf[i] >> 6) & 0x1) << (7 - (i & 0x7)); + } +} + + +// Deinterleaves 96 bytes of subchannel P-W data from 96 bytes of interleaved subchannel PW data. +void subpw_deinterleave(const uint8 *in_buf, uint8 *out_buf) +{ + assert(in_buf != out_buf); + + memset(out_buf, 0, 96); + + for(unsigned ch = 0; ch < 8; ch++) + { + for(unsigned i = 0; i < 96; i++) + { + out_buf[(ch * 12) + (i >> 3)] |= ((in_buf[i] >> (7 - ch)) & 0x1) << (7 - (i & 0x7)); + } + } + +} + +// Interleaves 96 bytes of subchannel P-W data from 96 bytes of uninterleaved subchannel PW data. +void subpw_interleave(const uint8 *in_buf, uint8 *out_buf) +{ + assert(in_buf != out_buf); + + for(unsigned d = 0; d < 12; d++) + { + for(unsigned bitpoodle = 0; bitpoodle < 8; bitpoodle++) + { + uint8 rawb = 0; + + for(unsigned ch = 0; ch < 8; ch++) + { + rawb |= ((in_buf[ch * 12 + d] >> (7 - bitpoodle)) & 1) << (7 - ch); + } + out_buf[(d << 3) + bitpoodle] = rawb; + } + } +} + +// NOTES ON LEADOUT AREA SYNTHESIS +// +// I'm not trusting that the "control" field for the TOC leadout entry will always be set properly, so | the control fields for the last track entry +// and the leadout entry together before extracting the D2 bit. Audio track->data leadout is fairly benign though maybe noisy(especially if we ever implement +// data scrambling properly), but data track->audio leadout could break things in an insidious manner for the more accurate drive emulation code). +// +void subpw_synth_leadout_lba(const TOC& toc, const int32 lba, uint8* SubPWBuf) +{ + uint8 buf[0xC]; + uint32 lba_relative; + uint32 ma, sa, fa; + uint32 m, s, f; + + lba_relative = lba - toc.tracks[100].lba; + + f = (lba_relative % 75); + s = ((lba_relative / 75) % 60); + m = (lba_relative / 75 / 60); + + fa = (lba + 150) % 75; + sa = ((lba + 150) / 75) % 60; + ma = ((lba + 150) / 75 / 60); + + uint8 adr = 0x1; // Q channel data encodes position + uint8 control = toc.tracks[100].control; + + if(toc.tracks[toc.last_track].valid) + control |= toc.tracks[toc.last_track].control & 0x4; + else if(toc.disc_type == DISC_TYPE_CD_I) + control |= 0x4; + + memset(buf, 0, 0xC); + buf[0] = (adr << 0) | (control << 4); + buf[1] = 0xAA; + buf[2] = 0x01; + + // Track relative MSF address + buf[3] = U8_to_BCD(m); + buf[4] = U8_to_BCD(s); + buf[5] = U8_to_BCD(f); + + buf[6] = 0; // Zerroooo + + // Absolute MSF address + buf[7] = U8_to_BCD(ma); + buf[8] = U8_to_BCD(sa); + buf[9] = U8_to_BCD(fa); + + subq_generate_checksum(buf); + + for(int i = 0; i < 96; i++) + SubPWBuf[i] = (((buf[i >> 3] >> (7 - (i & 0x7))) & 1) ? 0x40 : 0x00) | 0x80; +} + +void synth_leadout_sector_lba(uint8 mode, const TOC& toc, const int32 lba, uint8* out_buf) +{ + memset(out_buf, 0, 2352 + 96); + subpw_synth_leadout_lba(toc, lba, out_buf + 2352); + + if(out_buf[2352 + 1] & 0x40) + { + if(mode == 0xFF) + { + if(toc.disc_type == DISC_TYPE_CD_XA || toc.disc_type == DISC_TYPE_CD_I) + mode = 0x02; + else + mode = 0x01; + } + + switch(mode) + { + default: + encode_mode0_sector(LBA_to_ABA(lba), out_buf); + break; + + case 0x01: + encode_mode1_sector(LBA_to_ABA(lba), out_buf); + break; + + case 0x02: + out_buf[12 + 6] = 0x20; + out_buf[12 + 10] = 0x20; + encode_mode2_form2_sector(LBA_to_ABA(lba), out_buf); + break; + } + } +} + +// ISO/IEC 10149:1995 (E): 20.2 +// +void subpw_synth_udapp_lba(const TOC& toc, const int32 lba, const int32 lba_subq_relative_offs, uint8* SubPWBuf) +{ + uint8 buf[0xC]; + uint32 lba_relative; + uint32 ma, sa, fa; + uint32 m, s, f; + + if(lba < -150 || lba >= 0) + printf("[BUG] subpw_synth_udapp_lba() lba out of range --- %d\n", lba); + + { + int32 lba_tmp = lba + lba_subq_relative_offs; + + if(lba_tmp < 0) + lba_relative = 0 - 1 - lba_tmp; + else + lba_relative = lba_tmp - 0; + } + + f = (lba_relative % 75); + s = ((lba_relative / 75) % 60); + m = (lba_relative / 75 / 60); + + fa = (lba + 150) % 75; + sa = ((lba + 150) / 75) % 60; + ma = ((lba + 150) / 75 / 60); + + uint8 adr = 0x1; // Q channel data encodes position + uint8 control; + + if(toc.disc_type == DISC_TYPE_CD_I && toc.first_track > 1) + control = 0x4; + else if(toc.tracks[toc.first_track].valid) + control = toc.tracks[toc.first_track].control; + else + control = 0x0; + + memset(buf, 0, 0xC); + buf[0] = (adr << 0) | (control << 4); + buf[1] = U8_to_BCD(toc.first_track); + buf[2] = U8_to_BCD(0x00); + + // Track relative MSF address + buf[3] = U8_to_BCD(m); + buf[4] = U8_to_BCD(s); + buf[5] = U8_to_BCD(f); + + buf[6] = 0; // Zerroooo + + // Absolute MSF address + buf[7] = U8_to_BCD(ma); + buf[8] = U8_to_BCD(sa); + buf[9] = U8_to_BCD(fa); + + subq_generate_checksum(buf); + + for(int i = 0; i < 96; i++) + SubPWBuf[i] = (((buf[i >> 3] >> (7 - (i & 0x7))) & 1) ? 0x40 : 0x00) | 0x80; +} + +void synth_udapp_sector_lba(uint8 mode, const TOC& toc, const int32 lba, int32 lba_subq_relative_offs, uint8* out_buf) +{ + memset(out_buf, 0, 2352 + 96); + subpw_synth_udapp_lba(toc, lba, lba_subq_relative_offs, out_buf + 2352); + + if(out_buf[2352 + 1] & 0x40) + { + if(mode == 0xFF) + { + if(toc.disc_type == DISC_TYPE_CD_XA || toc.disc_type == DISC_TYPE_CD_I) + mode = 0x02; + else + mode = 0x01; + } + + switch(mode) + { + default: + encode_mode0_sector(LBA_to_ABA(lba), out_buf); + break; + + case 0x01: + encode_mode1_sector(LBA_to_ABA(lba), out_buf); + break; + + case 0x02: + out_buf[12 + 6] = 0x20; + out_buf[12 + 10] = 0x20; + encode_mode2_form2_sector(LBA_to_ABA(lba), out_buf); + break; + } + } +} + +#if 0 +bool subq_extrapolate(const uint8 *subq_input, int32 position_delta, uint8 *subq_output) +{ + assert(subq_check_checksum(subq_input)); + + + subq_generate_checksum(subq_output); +} +#endif + +void scrambleize_data_sector(uint8 *sector_data) +{ + for(unsigned i = 12; i < 2352; i++) + sector_data[i] ^= scramble_table[i - 12]; +} + +} diff --git a/waterbox/pcfx/cdrom/CDUtility.h b/waterbox/pcfx/cdrom/CDUtility.h new file mode 100644 index 0000000000..6539dc622d --- /dev/null +++ b/waterbox/pcfx/cdrom/CDUtility.h @@ -0,0 +1,237 @@ +#ifndef __MDFN_CDROM_CDUTILITY_H +#define __MDFN_CDROM_CDUTILITY_H + +namespace CDUtility +{ + // Call once at app startup before creating any threads that could potentially cause re-entrancy to these functions. + // It will also be called automatically if needed for the first time a function in this namespace that requires + // the initialization function to be called is called, for potential + // usage in constructors of statically-declared objects. + void CDUtility_Init(void) MDFN_COLD; + + // Quick definitions here: + // + // ABA - Absolute block address, synonymous to absolute MSF + // aba = (m_a * 60 * 75) + (s_a * 75) + f_a + // + // LBA - Logical block address(related: data CDs are required to have a pregap of 2 seconds, IE 150 frames/sectors) + // lba = aba - 150 + + + enum + { + ADR_NOQINFO = 0x00, + ADR_CURPOS = 0x01, + ADR_MCN = 0x02, + ADR_ISRC = 0x03 + }; + + + struct TOC_Track + { + uint8 adr; + uint8 control; + uint32 lba; + bool valid; // valid/present; oh CD-i... + }; + + // SubQ control field flags. + enum + { + SUBQ_CTRLF_PRE = 0x01, // With 50/15us pre-emphasis. + SUBQ_CTRLF_DCP = 0x02, // Digital copy permitted. + SUBQ_CTRLF_DATA = 0x04, // Data track. + SUBQ_CTRLF_4CH = 0x08, // 4-channel CD-DA. + }; + + enum + { + DISC_TYPE_CDDA_OR_M1 = 0x00, + DISC_TYPE_CD_I = 0x10, + DISC_TYPE_CD_XA = 0x20 + }; + + struct TOC + { + INLINE TOC() + { + Clear(); + } + + INLINE void Clear(void) + { + first_track = last_track = 0; + disc_type = 0; + + memset(tracks, 0, sizeof(tracks)); // FIXME if we change TOC_Track to non-POD type. + } + + INLINE int FindTrackByLBA(uint32 LBA) const + { + int32 lvt = 0; + + for(int32 track = 1; track <= 100; track++) + { + if(!tracks[track].valid) + continue; + + if(LBA < tracks[track].lba) + break; + + lvt = track; + } + + return(lvt); + } + + uint8 first_track; + uint8 last_track; + uint8 disc_type; + TOC_Track tracks[100 + 1]; // [0] is unused, [100] is for the leadout track. + }; + + // + // Address conversion functions. + // + static INLINE uint32 AMSF_to_ABA(int32 m_a, int32 s_a, int32 f_a) + { + return(f_a + 75 * s_a + 75 * 60 * m_a); + } + + static INLINE void ABA_to_AMSF(uint32 aba, uint8 *m_a, uint8 *s_a, uint8 *f_a) + { + *m_a = aba / 75 / 60; + *s_a = (aba - *m_a * 75 * 60) / 75; + *f_a = aba - (*m_a * 75 * 60) - (*s_a * 75); + } + + static INLINE int32 ABA_to_LBA(uint32 aba) + { + return(aba - 150); + } + + static INLINE uint32 LBA_to_ABA(int32 lba) + { + return(lba + 150); + } + + static INLINE int32 AMSF_to_LBA(uint8 m_a, uint8 s_a, uint8 f_a) + { + return(ABA_to_LBA(AMSF_to_ABA(m_a, s_a, f_a))); + } + + static INLINE void LBA_to_AMSF(int32 lba, uint8 *m_a, uint8 *s_a, uint8 *f_a) + { + ABA_to_AMSF(LBA_to_ABA(lba), m_a, s_a, f_a); + } + + // + // BCD conversion functions + // + static INLINE bool BCD_is_valid(uint8 bcd_number) + { + if((bcd_number & 0xF0) >= 0xA0) + return(false); + + if((bcd_number & 0x0F) >= 0x0A) + return(false); + + return(true); + } + + static INLINE uint8 BCD_to_U8(uint8 bcd_number) + { + return( ((bcd_number >> 4) * 10) + (bcd_number & 0x0F) ); + } + + static INLINE uint8 U8_to_BCD(uint8 num) + { + return( ((num / 10) << 4) + (num % 10) ); + } + + // should always perform the conversion, even if the bcd number is invalid. + static INLINE bool BCD_to_U8_check(uint8 bcd_number, uint8 *out_number) + { + *out_number = BCD_to_U8(bcd_number); + + if(!BCD_is_valid(bcd_number)) + return(false); + + return(true); + } + + // + // Sector data encoding functions(to full 2352 bytes raw sector). + // + // sector_data must be able to contain at least 2352 bytes. + void encode_mode0_sector(uint32 aba, uint8 *sector_data); + void encode_mode1_sector(uint32 aba, uint8 *sector_data); // 2048 bytes of user data at offset 16 + void encode_mode2_sector(uint32 aba, uint8 *sector_data); // 2336 bytes of user data at offset 16 + void encode_mode2_form1_sector(uint32 aba, uint8 *sector_data); // 2048+8 bytes of user data at offset 16 + void encode_mode2_form2_sector(uint32 aba, uint8 *sector_data); // 2324+8 bytes of user data at offset 16 + + + // User data area pre-pause(MSF 00:00:00 through 00:01:74), lba -150 through -1 + // out_buf must be able to contain 2352+96 bytes. + // "mode" is not used if the area is to be encoded as audio. + // pass 0xFF for "mode" for "don't know", and to make guess based on the TOC. + void synth_udapp_sector_lba(uint8 mode, const TOC& toc, const int32 lba, int32 lba_subq_relative_offs, uint8* out_buf); + void subpw_synth_udapp_lba(const TOC& toc, const int32 lba, const int32 lba_subq_relative_offs, uint8* SubPWBuf); + + // out_buf must be able to contain 2352+96 bytes. + // "mode" is not used if the area is to be encoded as audio. + // pass 0xFF for "mode" for "don't know", and to make guess based on the TOC. + void synth_leadout_sector_lba(uint8 mode, const TOC& toc, const int32 lba, uint8* out_buf); + void subpw_synth_leadout_lba(const TOC& toc, const int32 lba, uint8* SubPWBuf); + + + // + // User data error detection and correction + // + + // Check EDC of a mode 1 or mode 2 form 1 sector. + // Returns "true" if checksum is ok(matches). + // Returns "false" if checksum mismatch. + // sector_data should contain 2352 bytes of raw sector data. + bool edc_check(const uint8 *sector_data, bool xa); + + // Check EDC and L-EC data of a mode 1 or mode 2 form 1 sector, and correct bit errors if any exist. + // Returns "true" if errors weren't detected, or they were corrected succesfully. + // Returns "false" if errors couldn't be corrected. + // sector_data should contain 2352 bytes of raw sector data. + // + // Note: mode 2 form 1 L-EC data can't correct errors in the 4-byte sector header(address + mode), + // but the error(s) will still be detected by EDC. + bool edc_lec_check_and_correct(uint8 *sector_data, bool xa); + + // + // Subchannel(Q in particular) functions + // + + // Returns false on checksum mismatch, true on match. + bool subq_check_checksum(const uint8 *subq_buf); + + // Calculates the checksum of Q subchannel data(not including the checksum bytes of course ;)) from subq_buf, and stores it into the appropriate position + // in subq_buf. + void subq_generate_checksum(uint8 *subq_buf); + + // Deinterleaves 12 bytes of subchannel Q data from 96 bytes of interleaved subchannel PW data. + void subq_deinterleave(const uint8 *subpw_buf, uint8 *subq_buf); + + // Deinterleaves 96 bytes of subchannel P-W data from 96 bytes of interleaved subchannel PW data. + void subpw_deinterleave(const uint8 *in_buf, uint8 *out_buf); + + // Interleaves 96 bytes of subchannel P-W data from 96 bytes of uninterleaved subchannel PW data. + void subpw_interleave(const uint8 *in_buf, uint8 *out_buf); + + // Extrapolates Q subchannel current position data from subq_input, with frame/sector delta position_delta, and writes to subq_output. + // Only valid for ADR_CURPOS. + // subq_input must pass subq_check_checksum(). + // TODO + //void subq_extrapolate(const uint8 *subq_input, int32 position_delta, uint8 *subq_output); + + // (De)Scrambles data sector. + void scrambleize_data_sector(uint8 *sector_data); +} + +#endif diff --git a/waterbox/pcfx/cdrom/SimpleFIFO.h b/waterbox/pcfx/cdrom/SimpleFIFO.h new file mode 100644 index 0000000000..b12082b312 --- /dev/null +++ b/waterbox/pcfx/cdrom/SimpleFIFO.h @@ -0,0 +1,114 @@ +#ifndef __MDFN_SIMPLEFIFO_H +#define __MDFN_SIMPLEFIFO_H + +#include +#include + +#include "../math_ops.h" + +template +class SimpleFIFO +{ + public: + + // Constructor + SimpleFIFO(uint32 the_size) // Size should be a power of 2! + { + data.resize(round_up_pow2(the_size)); + size = the_size; + read_pos = 0; + write_pos = 0; + in_count = 0; + } + + // Destructor + INLINE ~SimpleFIFO() + { + + } + + INLINE void SaveStatePostLoad(void) + { + read_pos %= data.size(); + write_pos %= data.size(); + in_count %= (data.size() + 1); + } + + INLINE uint32 CanRead(void) + { + return(in_count); + } + + INLINE uint32 CanWrite(void) + { + return(size - in_count); + } + + INLINE T ReadUnit(bool peek = false) + { + T ret; + + assert(in_count > 0); + + ret = data[read_pos]; + + if(!peek) + { + read_pos = (read_pos + 1) & (data.size() - 1); + in_count--; + } + + return(ret); + } + + INLINE uint8 ReadByte(bool peek = false) + { + assert(sizeof(T) == 1); + + return(ReadUnit(peek)); + } + + INLINE void Write(const T *happy_data, uint32 happy_count) + { + assert(CanWrite() >= happy_count); + + while(happy_count) + { + data[write_pos] = *happy_data; + + write_pos = (write_pos + 1) & (data.size() - 1); + in_count++; + happy_data++; + happy_count--; + } + } + + INLINE void WriteUnit(const T& wr_data) + { + Write(&wr_data, 1); + } + + INLINE void WriteByte(const T& wr_data) + { + assert(sizeof(T) == 1); + Write(&wr_data, 1); + } + + + INLINE void Flush(void) + { + read_pos = 0; + write_pos = 0; + in_count = 0; + } + + //private: + std::vector data; + uint32 size; + uint32 read_pos; // Read position + uint32 write_pos; // Write position + uint32 in_count; // Number of units in the FIFO +}; + + +#endif diff --git a/waterbox/pcfx/cdrom/cdromif.cpp b/waterbox/pcfx/cdrom/cdromif.cpp new file mode 100644 index 0000000000..940708aa57 --- /dev/null +++ b/waterbox/pcfx/cdrom/cdromif.cpp @@ -0,0 +1,131 @@ +/* Mednafen - Multi-system Emulator + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "../defs.h" +#include +#include +#include "cdromif.h" +//#include "CDAccess.h" +//#include "../general.h" + +#include + +using namespace CDUtility; + +enum +{ + // Status/Error messages + CDIF_MSG_DONE = 0, // Read -> emu. args: No args. + CDIF_MSG_INFO, // Read -> emu. args: str_message + CDIF_MSG_FATAL_ERROR, // Read -> emu. args: *TODO ARGS* + + // + // Command messages. + // + CDIF_MSG_DIEDIEDIE, // Emu -> read + + CDIF_MSG_READ_SECTOR, /* Emu -> read + args[0] = lba + */ +}; + + +typedef struct +{ + bool valid; + bool error; + int32 lba; + uint8 data[2352 + 96]; +} CDIF_Sector_Buffer; + + +CDIF::CDIF() : UnrecoverableError(false) +{ + +} + +CDIF::~CDIF() +{ + +} + +bool CDIF::ValidateRawSector(uint8 *buf) +{ + int mode = buf[12 + 3]; + + if(mode != 0x1 && mode != 0x2) + return(false); + + if(!edc_lec_check_and_correct(buf, mode == 2)) + return(false); + + return(true); +} + +int CDIF::ReadSector(uint8* buf, int32 lba, uint32 sector_count, bool suppress_uncorrectable_message) +{ + int ret = 0; + + if(UnrecoverableError) + return(false); + + while(sector_count--) + { + uint8 tmpbuf[2352 + 96]; + + if(!ReadRawSector(tmpbuf, lba)) + { + puts("CDIF Raw Read error"); + return(FALSE); + } + + if(!ValidateRawSector(tmpbuf)) + { + /*if(!suppress_uncorrectable_message) + { + MDFN_DispMessage(_("Uncorrectable data at sector %d"), lba); + MDFN_PrintError(_("Uncorrectable data at sector %d"), lba); + }*/ + + return(false); + } + + const int mode = tmpbuf[12 + 3]; + + if(!ret) + ret = mode; + + if(mode == 1) + { + memcpy(buf, &tmpbuf[12 + 4], 2048); + } + else if(mode == 2) + { + memcpy(buf, &tmpbuf[12 + 4 + 8], 2048); + } + else + { + printf("CDIF_ReadSector() invalid sector type at LBA=%u\n", (unsigned int)lba); + return(false); + } + + buf += 2048; + lba++; + } + + return(ret); +} diff --git a/waterbox/pcfx/cdrom/cdromif.h b/waterbox/pcfx/cdrom/cdromif.h new file mode 100644 index 0000000000..aa07a4f189 --- /dev/null +++ b/waterbox/pcfx/cdrom/cdromif.h @@ -0,0 +1,58 @@ +/* Mednafen - Multi-system Emulator + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __MDFN_CDROM_CDROMIF_H +#define __MDFN_CDROM_CDROMIF_H + +#include "CDUtility.h" + +#include + +typedef CDUtility::TOC CD_TOC; + +class CDIF +{ + public: + + CDIF(); + virtual ~CDIF(); + + static const int32 LBA_Read_Minimum = -150; + static const int32 LBA_Read_Maximum = 449849; // 100 * 75 * 60 - 150 - 1 + + inline void ReadTOC(CDUtility::TOC *read_target) + { + *read_target = disc_toc; + } + + virtual void HintReadSector(int32 lba) = 0; + virtual bool ReadRawSector(uint8 *buf, int32 lba) = 0; // Reads 2352+96 bytes of data into buf. + + // Call for mode 1 or mode 2 form 1 only. + bool ValidateRawSector(uint8 *buf); + + // Utility/Wrapped functions + // Reads mode 1 and mode2 form 1 sectors(2048 bytes per sector returned) + // Will return the type(1, 2) of the first sector read to the buffer supplied, 0 on error + int ReadSector(uint8* buf, int32 lba, uint32 sector_count, bool suppress_uncorrectable_message = false); + + protected: + bool UnrecoverableError; + CDUtility::TOC disc_toc; +}; + +#endif diff --git a/waterbox/pcfx/cdrom/crc32.cpp b/waterbox/pcfx/cdrom/crc32.cpp new file mode 100644 index 0000000000..2aa4ee14a3 --- /dev/null +++ b/waterbox/pcfx/cdrom/crc32.cpp @@ -0,0 +1,130 @@ +/* dvdisaster: Additional error correction for optical media. + * Copyright (C) 2004-2007 Carsten Gnoerlich. + * Project home page: http://www.dvdisaster.com + * Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org + * + * CRC32 code based upon public domain code by Ross Williams (see notes below) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA, + * or direct your browser at http://www.gnu.org. + */ + +#include "dvdisaster.h" + +/*** + *** EDC checksum used in CDROM sectors + ***/ + +/*****************************************************************/ +/* */ +/* CRC LOOKUP TABLE */ +/* ================ */ +/* The following CRC lookup table was generated automagically */ +/* by the Rocksoft^tm Model CRC Algorithm Table Generation */ +/* Program V1.0 using the following model parameters: */ +/* */ +/* Width : 4 bytes. */ +/* Poly : 0x8001801BL */ +/* Reverse : TRUE. */ +/* */ +/* For more information on the Rocksoft^tm Model CRC Algorithm, */ +/* see the document titled "A Painless Guide to CRC Error */ +/* Detection Algorithms" by Ross Williams */ +/* (ross@guest.adelaide.edu.au.). This document is likely to be */ +/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft". */ +/* */ +/*****************************************************************/ + +static const unsigned long edctable[256] = +{ + 0x00000000L, 0x90910101L, 0x91210201L, 0x01B00300L, + 0x92410401L, 0x02D00500L, 0x03600600L, 0x93F10701L, + 0x94810801L, 0x04100900L, 0x05A00A00L, 0x95310B01L, + 0x06C00C00L, 0x96510D01L, 0x97E10E01L, 0x07700F00L, + 0x99011001L, 0x09901100L, 0x08201200L, 0x98B11301L, + 0x0B401400L, 0x9BD11501L, 0x9A611601L, 0x0AF01700L, + 0x0D801800L, 0x9D111901L, 0x9CA11A01L, 0x0C301B00L, + 0x9FC11C01L, 0x0F501D00L, 0x0EE01E00L, 0x9E711F01L, + 0x82012001L, 0x12902100L, 0x13202200L, 0x83B12301L, + 0x10402400L, 0x80D12501L, 0x81612601L, 0x11F02700L, + 0x16802800L, 0x86112901L, 0x87A12A01L, 0x17302B00L, + 0x84C12C01L, 0x14502D00L, 0x15E02E00L, 0x85712F01L, + 0x1B003000L, 0x8B913101L, 0x8A213201L, 0x1AB03300L, + 0x89413401L, 0x19D03500L, 0x18603600L, 0x88F13701L, + 0x8F813801L, 0x1F103900L, 0x1EA03A00L, 0x8E313B01L, + 0x1DC03C00L, 0x8D513D01L, 0x8CE13E01L, 0x1C703F00L, + 0xB4014001L, 0x24904100L, 0x25204200L, 0xB5B14301L, + 0x26404400L, 0xB6D14501L, 0xB7614601L, 0x27F04700L, + 0x20804800L, 0xB0114901L, 0xB1A14A01L, 0x21304B00L, + 0xB2C14C01L, 0x22504D00L, 0x23E04E00L, 0xB3714F01L, + 0x2D005000L, 0xBD915101L, 0xBC215201L, 0x2CB05300L, + 0xBF415401L, 0x2FD05500L, 0x2E605600L, 0xBEF15701L, + 0xB9815801L, 0x29105900L, 0x28A05A00L, 0xB8315B01L, + 0x2BC05C00L, 0xBB515D01L, 0xBAE15E01L, 0x2A705F00L, + 0x36006000L, 0xA6916101L, 0xA7216201L, 0x37B06300L, + 0xA4416401L, 0x34D06500L, 0x35606600L, 0xA5F16701L, + 0xA2816801L, 0x32106900L, 0x33A06A00L, 0xA3316B01L, + 0x30C06C00L, 0xA0516D01L, 0xA1E16E01L, 0x31706F00L, + 0xAF017001L, 0x3F907100L, 0x3E207200L, 0xAEB17301L, + 0x3D407400L, 0xADD17501L, 0xAC617601L, 0x3CF07700L, + 0x3B807800L, 0xAB117901L, 0xAAA17A01L, 0x3A307B00L, + 0xA9C17C01L, 0x39507D00L, 0x38E07E00L, 0xA8717F01L, + 0xD8018001L, 0x48908100L, 0x49208200L, 0xD9B18301L, + 0x4A408400L, 0xDAD18501L, 0xDB618601L, 0x4BF08700L, + 0x4C808800L, 0xDC118901L, 0xDDA18A01L, 0x4D308B00L, + 0xDEC18C01L, 0x4E508D00L, 0x4FE08E00L, 0xDF718F01L, + 0x41009000L, 0xD1919101L, 0xD0219201L, 0x40B09300L, + 0xD3419401L, 0x43D09500L, 0x42609600L, 0xD2F19701L, + 0xD5819801L, 0x45109900L, 0x44A09A00L, 0xD4319B01L, + 0x47C09C00L, 0xD7519D01L, 0xD6E19E01L, 0x46709F00L, + 0x5A00A000L, 0xCA91A101L, 0xCB21A201L, 0x5BB0A300L, + 0xC841A401L, 0x58D0A500L, 0x5960A600L, 0xC9F1A701L, + 0xCE81A801L, 0x5E10A900L, 0x5FA0AA00L, 0xCF31AB01L, + 0x5CC0AC00L, 0xCC51AD01L, 0xCDE1AE01L, 0x5D70AF00L, + 0xC301B001L, 0x5390B100L, 0x5220B200L, 0xC2B1B301L, + 0x5140B400L, 0xC1D1B501L, 0xC061B601L, 0x50F0B700L, + 0x5780B800L, 0xC711B901L, 0xC6A1BA01L, 0x5630BB00L, + 0xC5C1BC01L, 0x5550BD00L, 0x54E0BE00L, 0xC471BF01L, + 0x6C00C000L, 0xFC91C101L, 0xFD21C201L, 0x6DB0C300L, + 0xFE41C401L, 0x6ED0C500L, 0x6F60C600L, 0xFFF1C701L, + 0xF881C801L, 0x6810C900L, 0x69A0CA00L, 0xF931CB01L, + 0x6AC0CC00L, 0xFA51CD01L, 0xFBE1CE01L, 0x6B70CF00L, + 0xF501D001L, 0x6590D100L, 0x6420D200L, 0xF4B1D301L, + 0x6740D400L, 0xF7D1D501L, 0xF661D601L, 0x66F0D700L, + 0x6180D800L, 0xF111D901L, 0xF0A1DA01L, 0x6030DB00L, + 0xF3C1DC01L, 0x6350DD00L, 0x62E0DE00L, 0xF271DF01L, + 0xEE01E001L, 0x7E90E100L, 0x7F20E200L, 0xEFB1E301L, + 0x7C40E400L, 0xECD1E501L, 0xED61E601L, 0x7DF0E700L, + 0x7A80E800L, 0xEA11E901L, 0xEBA1EA01L, 0x7B30EB00L, + 0xE8C1EC01L, 0x7850ED00L, 0x79E0EE00L, 0xE971EF01L, + 0x7700F000L, 0xE791F101L, 0xE621F201L, 0x76B0F300L, + 0xE541F401L, 0x75D0F500L, 0x7460F600L, 0xE4F1F701L, + 0xE381F801L, 0x7310F900L, 0x72A0FA00L, 0xE231FB01L, + 0x71C0FC00L, 0xE151FD01L, 0xE0E1FE01L, 0x7070FF00L +}; + +/* + * CDROM EDC calculation + */ + +uint32 EDCCrc32(const unsigned char *data, int len) +{ + uint32 crc = 0; + + while(len--) + crc = edctable[(crc ^ *data++) & 0xFF] ^ (crc >> 8); + + return crc; +} diff --git a/waterbox/pcfx/cdrom/dvdisaster.h b/waterbox/pcfx/cdrom/dvdisaster.h new file mode 100644 index 0000000000..bed440fca3 --- /dev/null +++ b/waterbox/pcfx/cdrom/dvdisaster.h @@ -0,0 +1,171 @@ +/* dvdisaster: Additional error correction for optical media. + * Copyright (C) 2004-2007 Carsten Gnoerlich. + * Project home page: http://www.dvdisaster.com + * Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA, + * or direct your browser at http://www.gnu.org. + */ + +#ifndef DVDISASTER_H +#define DVDISASTER_H + +/* "Dare to be gorgeous and unique. + * But don't ever be cryptic or otherwise unfathomable. + * Make it unforgettably great." + * + * From "A Final Note on Style", + * Amiga Intuition Reference Manual, 1986, p. 231 + */ + +/*** + *** I'm too lazy to mess with #include dependencies. + *** Everything #includeable is rolled up herein... + */ + +#include "../defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*** + *** dvdisaster.c + ***/ + +void PrepareDeadSector(void); + +void CreateEcc(void); +void FixEcc(void); +void Verify(void); + +uint32 EDCCrc32(const unsigned char*, int); + +/*** + *** galois.c + *** + * This is currently the hardcoded GF(2**8). + * int32 gives abundant space for the GF. + * Squeezing it down to uint8 won't probably gain much, + * so we implement this defensively here. + * + * Note that some performance critical stuff needs to + * be #included from galois-inlines.h + */ + +/* Galois field parameters for 8bit symbol Reed-Solomon code */ + +#define GF_SYMBOLSIZE 8 +#define GF_FIELDSIZE (1<= GF_FIELDMAX) + { + x -= GF_FIELDMAX; + x = (x >> GF_SYMBOLSIZE) + (x & GF_FIELDMAX); + } + + return x; +} diff --git a/waterbox/pcfx/cdrom/galois.cpp b/waterbox/pcfx/cdrom/galois.cpp new file mode 100644 index 0000000000..2792cfc341 --- /dev/null +++ b/waterbox/pcfx/cdrom/galois.cpp @@ -0,0 +1,156 @@ +/* dvdisaster: Additional error correction for optical media. + * Copyright (C) 2004-2007 Carsten Gnoerlich. + * Project home page: http://www.dvdisaster.com + * Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org + * + * The Reed-Solomon error correction draws a lot of inspiration - and even code - + * from Phil Karn's excellent Reed-Solomon library: http://www.ka9q.net/code/fec/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA, + * or direct your browser at http://www.gnu.org. + */ + +#include "dvdisaster.h" + +#include "galois-inlines.h" + +/*** + *** Galois field arithmetic. + *** + * Calculations are done over the extension field GF(2**n). + * Be careful not to overgeneralize these arithmetics; + * they only work for the case of GF(p**n) with p being prime. + */ + +/* Initialize the Galois field tables */ + + +GaloisTables* CreateGaloisTables(int32 gf_generator) +{ + GaloisTables *gt = (GaloisTables *)calloc(1, sizeof(GaloisTables)); + int32 b,log; + + /* Allocate the tables. + The encoder uses a special version of alpha_to which has the mod_fieldmax() + folded into the table. */ + + gt->gfGenerator = gf_generator; + + gt->indexOf = (int32 *)calloc(GF_FIELDSIZE, sizeof(int32)); + gt->alphaTo = (int32 *)calloc(GF_FIELDSIZE, sizeof(int32)); + gt->encAlphaTo = (int32 *)calloc(2*GF_FIELDSIZE, sizeof(int32)); + + /* create the log/ilog values */ + + for(b=1, log=0; logindexOf[b] = log; + gt->alphaTo[log] = b; + b = b << 1; + if(b & GF_FIELDSIZE) + b = b ^ gf_generator; + } + + if(b!=1) + { + printf("Failed to create the Galois field log tables!\n"); + exit(1); + } + + /* we're even closed using infinity (makes things easier) */ + + gt->indexOf[0] = GF_ALPHA0; /* log(0) = inf */ + gt->alphaTo[GF_ALPHA0] = 0; /* and the other way around */ + + for(b=0; b<2*GF_FIELDSIZE; b++) + gt->encAlphaTo[b] = gt->alphaTo[mod_fieldmax(b)]; + + return gt; +} + +void FreeGaloisTables(GaloisTables *gt) +{ + if(gt->indexOf) free(gt->indexOf); + if(gt->alphaTo) free(gt->alphaTo); + if(gt->encAlphaTo) free(gt->encAlphaTo); + + free(gt); +} + +/*** + *** Create the the Reed-Solomon generator polynomial + *** and some auxiliary data structures. + */ + +ReedSolomonTables *CreateReedSolomonTables(GaloisTables *gt, + int32 first_consecutive_root, + int32 prim_elem, + int nroots_in) +{ ReedSolomonTables *rt = (ReedSolomonTables *)calloc(1, sizeof(ReedSolomonTables)); + int32 i,j,root; + + rt->gfTables = gt; + rt->fcr = first_consecutive_root; + rt->primElem = prim_elem; + rt->nroots = nroots_in; + rt->ndata = GF_FIELDMAX - rt->nroots; + + rt->gpoly = (int32 *)calloc((rt->nroots+1), sizeof(int32)); + + /* Create the RS code generator polynomial */ + + rt->gpoly[0] = 1; + + for(i=0, root=first_consecutive_root*prim_elem; inroots; i++, root+=prim_elem) + { rt->gpoly[i+1] = 1; + + /* Multiply gpoly by alpha**(root+x) */ + + for(j=i; j>0; j--) + { + if(rt->gpoly[j] != 0) + rt->gpoly[j] = rt->gpoly[j-1] ^ gt->alphaTo[mod_fieldmax(gt->indexOf[rt->gpoly[j]] + root)]; + else + rt->gpoly[j] = rt->gpoly[j-1]; + } + + rt->gpoly[0] = gt->alphaTo[mod_fieldmax(gt->indexOf[rt->gpoly[0]] + root)]; + } + + /* Store the polynomials index for faster encoding */ + + for(i=0; i<=rt->nroots; i++) + rt->gpoly[i] = gt->indexOf[rt->gpoly[i]]; + +#if 0 + /* for the precalculated unrolled loops only */ + + for(i=gt->nroots-1; i>0; i--) + PrintCLI( + " par_idx[((++spk)&%d)] ^= enc_alpha_to[feedback + %3d];\n", + nroots-1,gt->gpoly[i]); + + PrintCLI(" par_idx[sp] = enc_alpha_to[feedback + %3d];\n", + gt->gpoly[0]); +#endif + + return rt; +} + +void FreeReedSolomonTables(ReedSolomonTables *rt) +{ + if(rt->gpoly) free(rt->gpoly); + + free(rt); +} diff --git a/waterbox/pcfx/cdrom/l-ec.cpp b/waterbox/pcfx/cdrom/l-ec.cpp new file mode 100644 index 0000000000..5c035ce4ab --- /dev/null +++ b/waterbox/pcfx/cdrom/l-ec.cpp @@ -0,0 +1,478 @@ +/* dvdisaster: Additional error correction for optical media. + * Copyright (C) 2004-2007 Carsten Gnoerlich. + * Project home page: http://www.dvdisaster.com + * Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org + * + * The Reed-Solomon error correction draws a lot of inspiration - and even code - + * from Phil Karn's excellent Reed-Solomon library: http://www.ka9q.net/code/fec/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA, + * or direct your browser at http://www.gnu.org. + */ + +#include "dvdisaster.h" + +#include "galois-inlines.h" + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +/*** + *** Mapping between cd frame and parity vectors + ***/ + +/* + * Mapping of frame bytes to P/Q Vectors + */ + +int PToByteIndex(int p, int i) +{ return 12 + p + i*86; +} + +void ByteIndexToP(int b, int *p, int *i) +{ *p = (b-12)%86; + *i = (b-12)/86; +} + +int QToByteIndex(int q, int i) +{ int offset = 12 + (q & 1); + + if(i == 43) return 2248+q; + if(i == 44) return 2300+q; + + q&=~1; + return offset + (q*43 + i*88) % 2236; +} + +void ByteIndexToQ(int b, int *q, int *i) +{ int x,y,offset; + + if(b >= 2300) + { *i = 44; + *q = (b-2300); + return; + } + + if(b >= 2248) + { *i = 43; + *q = (b-2248); + return; + } + + offset = b&1; + b = (b-12)/2; + x = b/43; + y = (b-(x*43))%26; + *i = b-(x*43); + *q = 2*((x+26-y)%26)+offset; +} + +/* + * There are 86 vectors of P-parity, yielding a RS(26,24) code. + */ + +void GetPVector(unsigned char *frame, unsigned char *data, int n) +{ int i; + int w_idx = n+12; + + for(i=0; i<26; i++, w_idx+=86) + data[i] = frame[w_idx]; +} + +void SetPVector(unsigned char *frame, unsigned char *data, int n) +{ int i; + int w_idx = n+12; + + for(i=0; i<26; i++, w_idx+=86) + frame[w_idx] = data[i]; +} + +void FillPVector(unsigned char *frame, unsigned char data, int n) +{ int i; + int w_idx = n+12; + + for(i=0; i<26; i++, w_idx+=86) + frame[w_idx] = data; +} + +void OrPVector(unsigned char *frame, unsigned char value, int n) +{ int i; + int w_idx = n+12; + + for(i=0; i<26; i++, w_idx+=86) + frame[w_idx] |= value; +} + +void AndPVector(unsigned char *frame, unsigned char value, int n) +{ int i; + int w_idx = n+12; + + for(i=0; i<26; i++, w_idx+=86) + frame[w_idx] &= value; +} + +/* + * There are 52 vectors of Q-parity, yielding a RS(45,43) code. + */ + +void GetQVector(unsigned char *frame, unsigned char *data, int n) +{ int offset = 12 + (n & 1); + int w_idx = (n&~1) * 43; + int i; + + for(i=0; i<43; i++, w_idx+=88) + data[i] = frame[(w_idx % 2236) + offset]; + + data[43] = frame[2248 + n]; + data[44] = frame[2300 + n]; +} + +void SetQVector(unsigned char *frame, unsigned char *data, int n) +{ int offset = 12 + (n & 1); + int w_idx = (n&~1) * 43; + int i; + + for(i=0; i<43; i++, w_idx+=88) + frame[(w_idx % 2236) + offset] = data[i]; + + frame[2248 + n] = data[43]; + frame[2300 + n] = data[44]; +} + +void FillQVector(unsigned char *frame, unsigned char data, int n) +{ int offset = 12 + (n & 1); + int w_idx = (n&~1) * 43; + int i; + + for(i=0; i<43; i++, w_idx+=88) + frame[(w_idx % 2236) + offset] = data; + + frame[2248 + n] = data; + frame[2300 + n] = data; +} + +void OrQVector(unsigned char *frame, unsigned char data, int n) +{ int offset = 12 + (n & 1); + int w_idx = (n&~1) * 43; + int i; + + for(i=0; i<43; i++, w_idx+=88) + frame[(w_idx % 2236) + offset] |= data; + + frame[2248 + n] |= data; + frame[2300 + n] |= data; +} + +void AndQVector(unsigned char *frame, unsigned char data, int n) +{ int offset = 12 + (n & 1); + int w_idx = (n&~1) * 43; + int i; + + for(i=0; i<43; i++, w_idx+=88) + frame[(w_idx % 2236) + offset] &= data; + + frame[2248 + n] &= data; + frame[2300 + n] &= data; +} + +/*** + *** C2 error counting + ***/ + +int CountC2Errors(unsigned char *frame) +{ int i,count = 0; + frame += 2352; + + for(i=0; i<294; i++, frame++) + { if(*frame & 0x01) count++; + if(*frame & 0x02) count++; + if(*frame & 0x04) count++; + if(*frame & 0x08) count++; + if(*frame & 0x10) count++; + if(*frame & 0x20) count++; + if(*frame & 0x40) count++; + if(*frame & 0x80) count++; + } + + return count; +} + +/*** + *** L-EC error correction for CD raw data sectors + ***/ + +/* + * These could be used from ReedSolomonTables, + * but hardcoding them is faster. + */ + +#define NROOTS 2 +#define LEC_FIRST_ROOT 0 //GF_ALPHA0 +#define LEC_PRIM_ELEM 1 +#define LEC_PRIMTH_ROOT 1 + +/* + * Calculate the error syndrome + */ + +int DecodePQ(ReedSolomonTables *rt, unsigned char *data, int padding, + int *erasure_list, int erasure_count) +{ GaloisTables *gt = rt->gfTables; + int syndrome[NROOTS]; + int lambda[NROOTS+1]; + int omega[NROOTS+1]; + int b[NROOTS+1]; + int reg[NROOTS+1]; + int root[NROOTS]; + int loc[NROOTS]; + int syn_error; + int deg_lambda,lambda_roots; + int deg_omega; + int shortened_size = GF_FIELDMAX - padding; + int corrected = 0; + int i,j,k; + int r,el; + + /*** Form the syndromes: Evaluate data(x) at roots of g(x) */ + + for(i=0; ialphaTo[mod_fieldmax(gt->indexOf[syndrome[i]] + + (LEC_FIRST_ROOT+i)*LEC_PRIM_ELEM)]; + + /*** Convert syndrome to index form, check for nonzero condition. */ + + syn_error = 0; + for(i=0; iindexOf[syndrome[i]]; + } + + /*** If the syndrome is zero, everything is fine. */ + + if(!syn_error) + return 0; + + /*** Initialize lambda to be the erasure locator polynomial */ + + lambda[0] = 1; + lambda[1] = lambda[2] = 0; + + erasure_list[0] += padding; + erasure_list[1] += padding; + + if(erasure_count > 2) /* sanity check */ + erasure_count = 0; + + if(erasure_count > 0) + { lambda[1] = gt->alphaTo[mod_fieldmax(LEC_PRIM_ELEM*(GF_FIELDMAX-1-erasure_list[0]))]; + + for(i=1; i0; j--) + { int tmp = gt->indexOf[lambda[j-1]]; + + if(tmp != GF_ALPHA0) + lambda[j] ^= gt->alphaTo[mod_fieldmax(u + tmp)]; + } + } + } + + for(i=0; iindexOf[lambda[i]]; + + /*** Berlekamp-Massey algorithm to determine error+erasure locator polynomial */ + + r = erasure_count; /* r is the step number */ + el = erasure_count; + + /* Compute discrepancy at the r-th step in poly-form */ + + while(++r <= NROOTS) + { int discr_r = 0; + + for(i=0; ialphaTo[mod_fieldmax(gt->indexOf[lambda[i]] + syndrome[r-i-1])]; + + discr_r = gt->indexOf[discr_r]; + + if(discr_r == GF_ALPHA0) + { /* B(x) = x*B(x) */ + memmove(b+1, b, NROOTS*sizeof(b[0])); + b[0] = GF_ALPHA0; + } + else + { int t[NROOTS+1]; + + /* T(x) = lambda(x) - discr_r*x*b(x) */ + t[0] = lambda[0]; + for(i=0; ialphaTo[mod_fieldmax(discr_r + b[i])]; + else t[i+1] = lambda[i+1]; + } + + if(2*el <= r+erasure_count-1) + { el = r + erasure_count - el; + + /* B(x) <-- inv(discr_r) * lambda(x) */ + for(i=0; i<=NROOTS; i++) + b[i] = (lambda[i] == 0) ? GF_ALPHA0 + : mod_fieldmax(gt->indexOf[lambda[i]] - discr_r + GF_FIELDMAX); + } + else + { /* 2 lines below: B(x) <-- x*B(x) */ + memmove(b+1, b, NROOTS*sizeof(b[0])); + b[0] = GF_ALPHA0; + } + + memcpy(lambda, t, (NROOTS+1)*sizeof(t[0])); + } + } + + /*** Convert lambda to index form and compute deg(lambda(x)) */ + + deg_lambda = 0; + for(i=0; iindexOf[lambda[i]]; + if(lambda[i] != GF_ALPHA0) + deg_lambda = i; + } + + /*** Find roots of the error+erasure locator polynomial by Chien search */ + + memcpy(reg+1, lambda+1, NROOTS*sizeof(reg[0])); + lambda_roots = 0; /* Number of roots of lambda(x) */ + + for(i=1, k=LEC_PRIMTH_ROOT-1; i<=GF_FIELDMAX; i++, k=mod_fieldmax(k+LEC_PRIMTH_ROOT)) + { int q=1; /* lambda[0] is always 0 */ + + for(j=deg_lambda; j>0; j--) + { if(reg[j] != GF_ALPHA0) + { reg[j] = mod_fieldmax(reg[j] + j); + q ^= gt->alphaTo[reg[j]]; + } + } + + if(q != 0) continue; /* Not a root */ + + /* store root in index-form and the error location number */ + + root[lambda_roots] = i; + loc[lambda_roots] = k; + + /* If we've already found max possible roots, abort the search to save time */ + + if(++lambda_roots == deg_lambda) break; + } + + /* deg(lambda) unequal to number of roots => uncorrectable error detected + This is not reliable for very small numbers of roots, e.g. nroots = 2 */ + + if(deg_lambda != lambda_roots) + { return -1; + } + + /* Compute err+eras evaluator poly omega(x) = syn(x)*lambda(x) + (modulo x**nroots). in index form. Also find deg(omega). */ + + deg_omega = deg_lambda-1; + + for(i=0; i<=deg_omega; i++) + { int tmp = 0; + + for(j=i; j>=0; j--) + { if((syndrome[i - j] != GF_ALPHA0) && (lambda[j] != GF_ALPHA0)) + tmp ^= gt->alphaTo[mod_fieldmax(syndrome[i - j] + lambda[j])]; + } + + omega[i] = gt->indexOf[tmp]; + } + + /* Compute error values in poly-form. + num1 = omega(inv(X(l))), + num2 = inv(X(l))**(FIRST_ROOT-1) and + den = lambda_pr(inv(X(l))) all in poly-form. */ + + for(j=lambda_roots-1; j>=0; j--) + { int num1 = 0; + int num2; + int den; + int location = loc[j]; + + for(i=deg_omega; i>=0; i--) + { if(omega[i] != GF_ALPHA0) + num1 ^= gt->alphaTo[mod_fieldmax(omega[i] + i * root[j])]; + } + + num2 = gt->alphaTo[mod_fieldmax(root[j] * (LEC_FIRST_ROOT - 1) + GF_FIELDMAX)]; + den = 0; + + /* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */ + + for(i=MIN(deg_lambda, NROOTS-1) & ~1; i>=0; i-=2) + { if(lambda[i+1] != GF_ALPHA0) + den ^= gt->alphaTo[mod_fieldmax(lambda[i+1] + i * root[j])]; + } + + /* Apply error to data */ + + if(num1 != 0 && location >= padding) + { + corrected++; + data[location-padding] ^= gt->alphaTo[mod_fieldmax(gt->indexOf[num1] + gt->indexOf[num2] + + GF_FIELDMAX - gt->indexOf[den])]; + + /* If no erasures were given, at most one error was corrected. + Return its position in erasure_list[0]. */ + + if(!erasure_count) + erasure_list[0] = location-padding; + } +#if 1 + else return -3; +#endif + } + + /*** Form the syndromes: Evaluate data(x) at roots of g(x) */ + + for(i=0; ialphaTo[mod_fieldmax(gt->indexOf[syndrome[i]] + + (LEC_FIRST_ROOT+i)*LEC_PRIM_ELEM)]; + } + + /*** Convert syndrome to index form, check for nonzero condition. */ +#if 1 + for(i=0; i + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "lec.h" + +#define GF8_PRIM_POLY 0x11d /* x^8 + x^4 + x^3 + x^2 + 1 */ + +#define EDC_POLY 0x8001801b /* (x^16 + x^15 + x^2 + 1) (x^16 + x^2 + x + 1) */ + +#define LEC_HEADER_OFFSET 12 +#define LEC_DATA_OFFSET 16 +#define LEC_MODE1_DATA_LEN 2048 +#define LEC_MODE1_EDC_OFFSET 2064 +#define LEC_MODE1_INTERMEDIATE_OFFSET 2068 +#define LEC_MODE1_P_PARITY_OFFSET 2076 +#define LEC_MODE1_Q_PARITY_OFFSET 2248 +#define LEC_MODE2_FORM1_DATA_LEN (2048+8) +#define LEC_MODE2_FORM1_EDC_OFFSET 2072 +#define LEC_MODE2_FORM2_DATA_LEN (2324+8) +#define LEC_MODE2_FORM2_EDC_OFFSET 2348 + + +typedef u_int8_t gf8_t; + +static u_int8_t GF8_LOG[256]; +static gf8_t GF8_ILOG[256]; + +static const class Gf8_Q_Coeffs_Results_01 { +private: + u_int16_t table[43][256]; +public: + Gf8_Q_Coeffs_Results_01(); + ~Gf8_Q_Coeffs_Results_01() {} + const u_int16_t *operator[] (int i) const { return &table[i][0]; } + operator const u_int16_t *() const { return &table[0][0]; } +} CF8_Q_COEFFS_RESULTS_01; + +static const class CrcTable { +private: + u_int32_t table[256]; +public: + CrcTable(); + ~CrcTable() {} + u_int32_t operator[](int i) const { return table[i]; } + operator const u_int32_t *() const { return table; } +} CRCTABLE; + +static const class ScrambleTable { +private: + u_int8_t table[2340]; +public: + ScrambleTable(); + ~ScrambleTable() {} + u_int8_t operator[](int i) const { return table[i]; } + operator const u_int8_t *() const { return table; } +} SCRAMBLE_TABLE; + +/* Creates the logarithm and inverse logarithm table that is required + * for performing multiplication in the GF(8) domain. + */ +static void gf8_create_log_tables() +{ + u_int8_t log; + u_int16_t b; + + for (b = 0; b <= 255; b++) { + GF8_LOG[b] = 0; + GF8_ILOG[b] = 0; + } + + b = 1; + + for (log = 0; log < 255; log++) { + GF8_LOG[(u_int8_t)b] = log; + GF8_ILOG[log] = (u_int8_t)b; + + b <<= 1; + + if ((b & 0x100) != 0) + b ^= GF8_PRIM_POLY; + } +} + +/* Addition in the GF(8) domain: just the XOR of the values. + */ +#define gf8_add(a, b) (a) ^ (b) + + +/* Multiplication in the GF(8) domain: add the logarithms (modulo 255) + * and return the inverse logarithm. Not used! + */ +#if 0 +static gf8_t gf8_mult(gf8_t a, gf8_t b) +{ + int16_t sum; + + if (a == 0 || b == 0) + return 0; + + sum = GF8_LOG[a] + GF8_LOG[b]; + + if (sum >= 255) + sum -= 255; + + return GF8_ILOG[sum]; +} +#endif + +/* Division in the GF(8) domain: Like multiplication but logarithms a + * subtracted. + */ +static gf8_t gf8_div(gf8_t a, gf8_t b) +{ + int16_t sum; + + assert(b != 0); + + if (a == 0) + return 0; + + sum = GF8_LOG[a] - GF8_LOG[b]; + + if (sum < 0) + sum += 255; + + return GF8_ILOG[sum]; +} + +Gf8_Q_Coeffs_Results_01::Gf8_Q_Coeffs_Results_01() +{ + int i, j; + u_int16_t c; + gf8_t GF8_COEFFS_HELP[2][45]; + u_int8_t GF8_Q_COEFFS[2][45]; + + + gf8_create_log_tables(); + + /* build matrix H: + * 1 1 ... 1 1 + * a^44 a^43 ... a^1 a^0 + * + * + */ + + for (j = 0; j < 45; j++) { + GF8_COEFFS_HELP[0][j] = 1; /* e0 */ + GF8_COEFFS_HELP[1][j] = GF8_ILOG[44-j]; /* e1 */ + } + + + /* resolve equation system for parity byte 0 and 1 */ + + /* e1' = e1 + e0 */ + for (j = 0; j < 45; j++) { + GF8_Q_COEFFS[1][j] = gf8_add(GF8_COEFFS_HELP[1][j], + GF8_COEFFS_HELP[0][j]); + } + + /* e1'' = e1' / (a^1 + 1) */ + for (j = 0; j < 45; j++) { + GF8_Q_COEFFS[1][j] = gf8_div(GF8_Q_COEFFS[1][j], GF8_Q_COEFFS[1][43]); + } + + /* e0' = e0 + e1 / a^1 */ + for (j = 0; j < 45; j++) { + GF8_Q_COEFFS[0][j] = gf8_add(GF8_COEFFS_HELP[0][j], + gf8_div(GF8_COEFFS_HELP[1][j], + GF8_ILOG[1])); + } + + /* e0'' = e0' / (1 + 1 / a^1) */ + for (j = 0; j < 45; j++) { + GF8_Q_COEFFS[0][j] = gf8_div(GF8_Q_COEFFS[0][j], GF8_Q_COEFFS[0][44]); + } + + /* + * Compute the products of 0..255 with all of the Q coefficients in + * advance. When building the scalar product between the data vectors + * and the P/Q vectors the individual products can be looked up in + * this table + * + * The P parity coefficients are just a subset of the Q coefficients so + * that we do not need to create a separate table for them. + */ + + for (j = 0; j < 43; j++) { + + table[j][0] = 0; + + for (i = 1; i < 256; i++) { + c = GF8_LOG[i] + GF8_LOG[GF8_Q_COEFFS[0][j]]; + if (c >= 255) c -= 255; + table[j][i] = GF8_ILOG[c]; + + c = GF8_LOG[i] + GF8_LOG[GF8_Q_COEFFS[1][j]]; + if (c >= 255) c -= 255; + table[j][i] |= GF8_ILOG[c]<<8; + } + } +} + +/* Reverses the bits in 'd'. 'bits' defines the bit width of 'd'. + */ +static u_int32_t mirror_bits(u_int32_t d, int bits) +{ + int i; + u_int32_t r = 0; + + for (i = 0; i < bits; i++) { + r <<= 1; + + if ((d & 0x1) != 0) + r |= 0x1; + + d >>= 1; + } + + return r; +} + +/* Build the CRC lookup table for EDC_POLY poly. The CRC is 32 bit wide + * and reversed (i.e. the bit stream is divided by the EDC_POLY with the + * LSB first order). + */ +CrcTable::CrcTable () +{ + u_int32_t i, j; + u_int32_t r; + + for (i = 0; i < 256; i++) { + r = mirror_bits(i, 8); + + r <<= 24; + + for (j = 0; j < 8; j++) { + if ((r & 0x80000000) != 0) { + r <<= 1; + r ^= EDC_POLY; + } + else { + r <<= 1; + } + } + + r = mirror_bits(r, 32); + + table[i] = r; + } +} + +/* Calculates the CRC of given data with given lengths based on the + * table lookup algorithm. + */ +static u_int32_t calc_edc(u_int8_t *data, int len) +{ + u_int32_t crc = 0; + + while (len--) { + crc = CRCTABLE[(int)(crc ^ *data++) & 0xff] ^ (crc >> 8); + } + + return crc; +} + +/* Build the scramble table as defined in the yellow book. The bytes + 12 to 2351 of a sector will be XORed with the data of this table. + */ +ScrambleTable::ScrambleTable() +{ + u_int16_t i, j; + u_int16_t reg = 1; + u_int8_t d; + + for (i = 0; i < 2340; i++) { + d = 0; + + for (j = 0; j < 8; j++) { + d >>= 1; + + if ((reg & 0x1) != 0) + d |= 0x80; + + if ((reg & 0x1) != ((reg >> 1) & 0x1)) { + reg >>= 1; + reg |= 0x4000; /* 15-bit register */ + } + else { + reg >>= 1; + } + } + + table[i] = d; + } +} + +/* Calc EDC for a MODE 1 sector + */ +static void calc_mode1_edc(u_int8_t *sector) +{ + u_int32_t crc = calc_edc(sector, LEC_MODE1_DATA_LEN + 16); + + sector[LEC_MODE1_EDC_OFFSET] = crc & 0xffL; + sector[LEC_MODE1_EDC_OFFSET + 1] = (crc >> 8) & 0xffL; + sector[LEC_MODE1_EDC_OFFSET + 2] = (crc >> 16) & 0xffL; + sector[LEC_MODE1_EDC_OFFSET + 3] = (crc >> 24) & 0xffL; +} + +/* Calc EDC for a XA form 1 sector + */ +static void calc_mode2_form1_edc(u_int8_t *sector) +{ + u_int32_t crc = calc_edc(sector + LEC_DATA_OFFSET, + LEC_MODE2_FORM1_DATA_LEN); + + sector[LEC_MODE2_FORM1_EDC_OFFSET] = crc & 0xffL; + sector[LEC_MODE2_FORM1_EDC_OFFSET + 1] = (crc >> 8) & 0xffL; + sector[LEC_MODE2_FORM1_EDC_OFFSET + 2] = (crc >> 16) & 0xffL; + sector[LEC_MODE2_FORM1_EDC_OFFSET + 3] = (crc >> 24) & 0xffL; +} + +/* Calc EDC for a XA form 2 sector + */ +static void calc_mode2_form2_edc(u_int8_t *sector) +{ + u_int32_t crc = calc_edc(sector + LEC_DATA_OFFSET, + LEC_MODE2_FORM2_DATA_LEN); + + sector[LEC_MODE2_FORM2_EDC_OFFSET] = crc & 0xffL; + sector[LEC_MODE2_FORM2_EDC_OFFSET + 1] = (crc >> 8) & 0xffL; + sector[LEC_MODE2_FORM2_EDC_OFFSET + 2] = (crc >> 16) & 0xffL; + sector[LEC_MODE2_FORM2_EDC_OFFSET + 3] = (crc >> 24) & 0xffL; +} + +/* Writes the sync pattern to the given sector. + */ +static void set_sync_pattern(u_int8_t *sector) +{ + sector[0] = 0; + + sector[1] = sector[2] = sector[3] = sector[4] = sector[5] = + sector[6] = sector[7] = sector[8] = sector[9] = sector[10] = 0xff; + + sector[11] = 0; +} + + +static u_int8_t bin2bcd(u_int8_t b) +{ + return (((b/10) << 4) & 0xf0) | ((b%10) & 0x0f); +} + +/* Builds the sector header. + */ +static void set_sector_header(u_int8_t mode, u_int32_t adr, u_int8_t *sector) +{ + sector[LEC_HEADER_OFFSET] = bin2bcd(adr / (60*75)); + sector[LEC_HEADER_OFFSET + 1] = bin2bcd((adr / 75) % 60); + sector[LEC_HEADER_OFFSET + 2] = bin2bcd(adr % 75); + sector[LEC_HEADER_OFFSET + 3] = mode; +} + +/* Calculate the P parities for the sector. + * The 43 P vectors of length 24 are combined with the GF8_P_COEFFS. + */ +static void calc_P_parity(u_int8_t *sector) +{ + int i, j; + u_int16_t p01_msb, p01_lsb; + u_int8_t *p_lsb_start; + u_int8_t *p_lsb; + u_int8_t *p0, *p1; + u_int8_t d0,d1; + + p_lsb_start = sector + LEC_HEADER_OFFSET; + + p1 = sector + LEC_MODE1_P_PARITY_OFFSET; + p0 = sector + LEC_MODE1_P_PARITY_OFFSET + 2 * 43; + + for (i = 0; i <= 42; i++) { + p_lsb = p_lsb_start; + + p01_lsb = p01_msb = 0; + + for (j = 19; j <= 42; j++) { + d0 = *p_lsb; + d1 = *(p_lsb+1); + + p01_lsb ^= CF8_Q_COEFFS_RESULTS_01[j][d0]; + p01_msb ^= CF8_Q_COEFFS_RESULTS_01[j][d1]; + + p_lsb += 2 * 43; + } + + *p0 = p01_lsb; + *(p0 + 1) = p01_msb; + + *p1 = p01_lsb>>8; + *(p1 + 1) = p01_msb>>8; + + p0 += 2; + p1 += 2; + + p_lsb_start += 2; + } +} + +/* Calculate the Q parities for the sector. + * The 26 Q vectors of length 43 are combined with the GF8_Q_COEFFS. + */ +static void calc_Q_parity(u_int8_t *sector) +{ + int i, j; + u_int16_t q01_lsb, q01_msb; + u_int8_t *q_lsb_start; + u_int8_t *q_lsb; + u_int8_t *q0, *q1, *q_start; + u_int8_t d0,d1; + + q_lsb_start = sector + LEC_HEADER_OFFSET; + + q_start = sector + LEC_MODE1_Q_PARITY_OFFSET; + q1 = sector + LEC_MODE1_Q_PARITY_OFFSET; + q0 = sector + LEC_MODE1_Q_PARITY_OFFSET + 2 * 26; + + for (i = 0; i <= 25; i++) { + q_lsb = q_lsb_start; + + q01_lsb = q01_msb = 0; + + for (j = 0; j <= 42; j++) { + d0 = *q_lsb; + d1 = *(q_lsb+1); + + q01_lsb ^= CF8_Q_COEFFS_RESULTS_01[j][d0]; + q01_msb ^= CF8_Q_COEFFS_RESULTS_01[j][d1]; + + q_lsb += 2 * 44; + + if (q_lsb >= q_start) { + q_lsb -= 2 * 1118; + } + } + + *q0 = q01_lsb; + *(q0 + 1) = q01_msb; + + *q1 = q01_lsb>>8; + *(q1 + 1) = q01_msb>>8; + + q0 += 2; + q1 += 2; + + q_lsb_start += 2 * 43; + } +} + +/* Encodes a MODE 0 sector. + * 'adr' is the current physical sector address + * 'sector' must be 2352 byte wide + */ +void lec_encode_mode0_sector(u_int32_t adr, u_int8_t *sector) +{ + u_int16_t i; + + set_sync_pattern(sector); + set_sector_header(0, adr, sector); + + sector += 16; + + for (i = 0; i < 2336; i++) + *sector++ = 0; +} + +/* Encodes a MODE 1 sector. + * 'adr' is the current physical sector address + * 'sector' must be 2352 byte wide containing 2048 bytes user data at + * offset 16 + */ +void lec_encode_mode1_sector(u_int32_t adr, u_int8_t *sector) +{ + set_sync_pattern(sector); + set_sector_header(1, adr, sector); + + calc_mode1_edc(sector); + + /* clear the intermediate field */ + sector[LEC_MODE1_INTERMEDIATE_OFFSET] = + sector[LEC_MODE1_INTERMEDIATE_OFFSET + 1] = + sector[LEC_MODE1_INTERMEDIATE_OFFSET + 2] = + sector[LEC_MODE1_INTERMEDIATE_OFFSET + 3] = + sector[LEC_MODE1_INTERMEDIATE_OFFSET + 4] = + sector[LEC_MODE1_INTERMEDIATE_OFFSET + 5] = + sector[LEC_MODE1_INTERMEDIATE_OFFSET + 6] = + sector[LEC_MODE1_INTERMEDIATE_OFFSET + 7] = 0; + + calc_P_parity(sector); + calc_Q_parity(sector); +} + +/* Encodes a MODE 2 sector. + * 'adr' is the current physical sector address + * 'sector' must be 2352 byte wide containing 2336 bytes user data at + * offset 16 + */ +void lec_encode_mode2_sector(u_int32_t adr, u_int8_t *sector) +{ + set_sync_pattern(sector); + set_sector_header(2, adr, sector); +} + +/* Encodes a XA form 1 sector. + * 'adr' is the current physical sector address + * 'sector' must be 2352 byte wide containing 2048+8 bytes user data at + * offset 16 + */ +void lec_encode_mode2_form1_sector(u_int32_t adr, u_int8_t *sector) +{ + set_sync_pattern(sector); + + calc_mode2_form1_edc(sector); + + /* P/Q partiy must not contain the sector header so clear it */ + sector[LEC_HEADER_OFFSET] = + sector[LEC_HEADER_OFFSET + 1] = + sector[LEC_HEADER_OFFSET + 2] = + sector[LEC_HEADER_OFFSET + 3] = 0; + + calc_P_parity(sector); + calc_Q_parity(sector); + + /* finally add the sector header */ + set_sector_header(2, adr, sector); +} + +/* Encodes a XA form 2 sector. + * 'adr' is the current physical sector address + * 'sector' must be 2352 byte wide containing 2324+8 bytes user data at + * offset 16 + */ +void lec_encode_mode2_form2_sector(u_int32_t adr, u_int8_t *sector) +{ + set_sync_pattern(sector); + + calc_mode2_form2_edc(sector); + + set_sector_header(2, adr, sector); +} + +/* Scrambles and byte swaps an encoded sector. + * 'sector' must be 2352 byte wide. + */ +void lec_scramble(u_int8_t *sector) +{ + u_int16_t i; + const u_int8_t *stable = SCRAMBLE_TABLE; + u_int8_t *p = sector; + u_int8_t tmp; + + + for (i = 0; i < 6; i++) { + /* just swap bytes of sector sync */ + tmp = *p; + *p = *(p + 1); + p++; + *p++ = tmp; + } + for (;i < (2352 / 2); i++) { + /* scramble and swap bytes */ + tmp = *p ^ *stable++; + *p = *(p + 1) ^ *stable++; + p++; + *p++ = tmp; + } +} + +#if 0 +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + char *infile; + char *outfile; + int fd_in, fd_out; + u_int8_t buffer1[2352]; + u_int8_t buffer2[2352]; + u_int32_t lba; + int i; + +#if 0 + for (i = 0; i < 2048; i++) + buffer1[i + 16] = 234; + + lba = 150; + + for (i = 0; i < 100000; i++) { + lec_encode_mode1_sector(lba, buffer1); + lec_scramble(buffer2); + lba++; + } + +#else + + if (argc != 3) + return 1; + + infile = argv[1]; + outfile = argv[2]; + + + if ((fd_in = open(infile, O_RDONLY)) < 0) { + perror("Cannot open input file"); + return 1; + } + + if ((fd_out = open(outfile, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) { + perror("Cannot open output file"); + return 1; + } + + lba = 150; + + do { + if (read(fd_in, buffer1, 2352) != 2352) + break; + + switch (*(buffer1 + 12 + 3)) { + case 1: + memcpy(buffer2 + 16, buffer1 + 16, 2048); + + lec_encode_mode1_sector(lba, buffer2); + break; + + case 2: + if ((*(buffer1 + 12 + 4 + 2) & 0x20) != 0) { + /* form 2 sector */ + memcpy(buffer2 + 16, buffer1 + 16, 2324 + 8); + lec_encode_mode2_form2_sector(lba, buffer2); + } + else { + /* form 1 sector */ + memcpy(buffer2 + 16, buffer1 + 16, 2048 + 8); + lec_encode_mode2_form1_sector(lba, buffer2); + } + break; + } + + if (memcmp(buffer1, buffer2, 2352) != 0) { + printf("Verify error at lba %ld\n", lba); + } + + lec_scramble(buffer2); + write(fd_out, buffer2, 2352); + + lba++; + } while (1); + + close(fd_in); + close(fd_out); + +#endif + + return 0; +} +#endif diff --git a/waterbox/pcfx/cdrom/lec.h b/waterbox/pcfx/cdrom/lec.h new file mode 100644 index 0000000000..c5e874c3f3 --- /dev/null +++ b/waterbox/pcfx/cdrom/lec.h @@ -0,0 +1,77 @@ +/* cdrdao - write audio CD-Rs in disc-at-once mode + * + * Copyright (C) 1998-2002 Andreas Mueller + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __LEC_H__ +#define __LEC_H__ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +typedef uint32_t u_int32_t; +typedef uint16_t u_int16_t; +typedef uint8_t u_int8_t; + +#ifndef TRUE +#define TRUE 1 +#endif + +/* Encodes a MODE 0 sector. + * 'adr' is the current physical sector address + * 'sector' must be 2352 byte wide + */ +void lec_encode_mode0_sector(u_int32_t adr, u_int8_t *sector); + +/* Encodes a MODE 1 sector. + * 'adr' is the current physical sector address + * 'sector' must be 2352 byte wide containing 2048 bytes user data at + * offset 16 + */ +void lec_encode_mode1_sector(u_int32_t adr, u_int8_t *sector); + +/* Encodes a MODE 2 sector. + * 'adr' is the current physical sector address + * 'sector' must be 2352 byte wide containing 2336 bytes user data at + * offset 16 + */ +void lec_encode_mode2_sector(u_int32_t adr, u_int8_t *sector); + +/* Encodes a XA form 1 sector. + * 'adr' is the current physical sector address + * 'sector' must be 2352 byte wide containing 2048+8 bytes user data at + * offset 16 + */ +void lec_encode_mode2_form1_sector(u_int32_t adr, u_int8_t *sector); + +/* Encodes a XA form 2 sector. + * 'adr' is the current physical sector address + * 'sector' must be 2352 byte wide containing 2324+8 bytes user data at + * offset 16 + */ +void lec_encode_mode2_form2_sector(u_int32_t adr, u_int8_t *sector); + +/* Scrambles and byte swaps an encoded sector. + * 'sector' must be 2352 byte wide. + */ +void lec_scramble(u_int8_t *sector); + +#endif diff --git a/waterbox/pcfx/cdrom/recover-raw.cpp b/waterbox/pcfx/cdrom/recover-raw.cpp new file mode 100644 index 0000000000..689f1f3c8f --- /dev/null +++ b/waterbox/pcfx/cdrom/recover-raw.cpp @@ -0,0 +1,210 @@ +/* dvdisaster: Additional error correction for optical media. + * Copyright (C) 2004-2007 Carsten Gnoerlich. + * Project home page: http://www.dvdisaster.com + * Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA, + * or direct your browser at http://www.gnu.org. + */ + +#include "dvdisaster.h" + +static GaloisTables *gt = NULL; /* for L-EC Reed-Solomon */ +static ReedSolomonTables *rt = NULL; + +bool Init_LEC_Correct(void) +{ + gt = CreateGaloisTables(0x11d); + rt = CreateReedSolomonTables(gt, 0, 1, 10); + + return(1); +} + + + +/*** + *** CD level CRC calculation + ***/ + +/* + * Test raw sector against its 32bit CRC. + * Returns TRUE if frame is good. + */ + +int CheckEDC(const unsigned char *cd_frame, bool xa_mode) +{ + unsigned int expected_crc, real_crc; + unsigned int crc_base = xa_mode ? 2072 : 2064; + + expected_crc = cd_frame[crc_base + 0] << 0; + expected_crc |= cd_frame[crc_base + 1] << 8; + expected_crc |= cd_frame[crc_base + 2] << 16; + expected_crc |= cd_frame[crc_base + 3] << 24; + + if(xa_mode) + real_crc = EDCCrc32(cd_frame+16, 2056); + else + real_crc = EDCCrc32(cd_frame, 2064); + + if(expected_crc == real_crc) + return(1); + else + { + //printf("Bad EDC CRC: Calculated: %08x, Recorded: %08x\n", real_crc, expected_crc); + return(0); + } +} + +/*** + *** A very simple L-EC error correction. + *** + * Perform just one pass over the Q and P vectors to see if everything + * is okay respectively correct minor errors. This is pretty much the + * same stuff the drive is supposed to do in the final L-EC stage. + */ + +static int simple_lec(unsigned char *frame) +{ + unsigned char byte_state[2352]; + unsigned char p_vector[P_VECTOR_SIZE]; + unsigned char q_vector[Q_VECTOR_SIZE]; + unsigned char p_state[P_VECTOR_SIZE]; + int erasures[Q_VECTOR_SIZE], erasure_count; + int ignore[2]; + int p_failures, q_failures; + int p_corrected, q_corrected; + int p,q; + + /* Setup */ + + memset(byte_state, 0, 2352); + + p_failures = q_failures = 0; + p_corrected = q_corrected = 0; + + /* Perform Q-Parity error correction */ + + for(q=0; q 2) + { GetPVector(byte_state, p_state, p); + erasure_count = 0; + + for(i=0; i 0 && erasure_count <= 2) + { GetPVector(frame, p_vector, p); + err = DecodePQ(rt, p_vector, P_PADDING, erasures, erasure_count); + } + } + + /* See what we've got */ + + if(err < 0) /* Uncorrectable. */ + { p_failures++; + } + else /* Correctable. */ + { if(err == 1 || err == 2) /* Store back corrected vector */ + { SetPVector(frame, p_vector, p); + p_corrected++; + } + } + } + + /* Sum up */ + + if(q_failures || p_failures || q_corrected || p_corrected) + { + return 1; + } + + return 0; +} + +/*** + *** Validate CD raw sector + ***/ + +int ValidateRawSector(unsigned char *frame, bool xaMode) +{ + int lec_did_sth = FALSE; + + /* Do simple L-EC. + It seems that drives stop their internal L-EC as soon as the + EDC is okay, so we may see uncorrected errors in the parity bytes. + Since we are also interested in the user data only and doing the + L-EC is expensive, we skip our L-EC as well when the EDC is fine. */ + + if(!CheckEDC(frame, xaMode)) + { + unsigned char header[4]; + + if(xaMode) + { + memcpy(header, frame + 12, 4); + memset(frame + 12, 0, 4); + } + + lec_did_sth = simple_lec(frame); + + if(xaMode) + memcpy(frame + 12, header, 4); + } + + /* Test internal sector checksum again */ + if(!CheckEDC(frame, xaMode)) + { + /* EDC failure in RAW sector */ + return FALSE; + } + + return TRUE; +} + diff --git a/waterbox/pcfx/cdrom/scsicd-pce-commands.inc b/waterbox/pcfx/cdrom/scsicd-pce-commands.inc new file mode 100644 index 0000000000..4441e9304b --- /dev/null +++ b/waterbox/pcfx/cdrom/scsicd-pce-commands.inc @@ -0,0 +1,259 @@ +/******************************************************** +* * +* PC Engine CD Command 0xD8 - SAPSP * +* * +********************************************************/ +static void DoNEC_PCE_SAPSP(const uint8 *cdb) +{ + uint32 new_read_sec_start; + + //printf("Set audio start: %02x %02x %02x %02x %02x %02x %02x\n", cdb[9], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6]); + switch (cdb[9] & 0xc0) + { + default: //SCSIDBG("Unknown SAPSP 9: %02x\n", cdb[9]); + case 0x00: + new_read_sec_start = (cdb[3] << 16) | (cdb[4] << 8) | cdb[5]; + break; + + case 0x40: + new_read_sec_start = AMSF_to_LBA(BCD_to_U8(cdb[2]), BCD_to_U8(cdb[3]), BCD_to_U8(cdb[4])); + break; + + case 0x80: + { + int track = BCD_to_U8(cdb[2]); + + if(!track) + track = 1; + else if(track >= toc.last_track + 1) + track = 100; + new_read_sec_start = toc.tracks[track].lba; + } + break; + } + + //printf("%lld\n", (long long)(monotonic_timestamp - pce_lastsapsp_timestamp) * 1000 / System_Clock); + if(cdda.CDDAStatus == CDDASTATUS_PLAYING && new_read_sec_start == read_sec_start && ((int64)(monotonic_timestamp - pce_lastsapsp_timestamp) * 1000 / System_Clock) < 190) + { + pce_lastsapsp_timestamp = monotonic_timestamp; + + SendStatusAndMessage(STATUS_GOOD, 0x00); + CDIRQCallback(SCSICD_IRQ_DATA_TRANSFER_DONE); + return; + } + + pce_lastsapsp_timestamp = monotonic_timestamp; + + read_sec = read_sec_start = new_read_sec_start; + read_sec_end = toc.tracks[100].lba; + + + cdda.CDDAReadPos = 588; + + cdda.CDDAStatus = CDDASTATUS_PAUSED; + cdda.PlayMode = PLAYMODE_SILENT; + + if(cdb[1]) + { + cdda.PlayMode = PLAYMODE_NORMAL; + cdda.CDDAStatus = CDDASTATUS_PLAYING; + } + + if(read_sec < toc.tracks[100].lba) + Cur_CDIF->HintReadSector(read_sec); + + SendStatusAndMessage(STATUS_GOOD, 0x00); + CDIRQCallback(SCSICD_IRQ_DATA_TRANSFER_DONE); +} + + + +/******************************************************** +* * +* PC Engine CD Command 0xD9 - SAPEP * +* * +********************************************************/ +static void DoNEC_PCE_SAPEP(const uint8 *cdb) +{ + uint32 new_read_sec_end; + + //printf("Set audio end: %02x %02x %02x %02x %02x %02x %02x\n", cdb[9], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6]); + + switch (cdb[9] & 0xc0) + { + default: //SCSIDBG("Unknown SAPEP 9: %02x\n", cdb[9]); + + case 0x00: + new_read_sec_end = (cdb[3] << 16) | (cdb[4] << 8) | cdb[5]; + break; + + case 0x40: + new_read_sec_end = BCD_to_U8(cdb[4]) + 75 * (BCD_to_U8(cdb[3]) + 60 * BCD_to_U8(cdb[2])); + new_read_sec_end -= 150; + break; + + case 0x80: + { + int track = BCD_to_U8(cdb[2]); + + if(!track) + track = 1; + else if(track >= toc.last_track + 1) + track = 100; + new_read_sec_end = toc.tracks[track].lba; + } + break; + } + + read_sec_end = new_read_sec_end; + + switch(cdb[1]) // PCE CD(TODO: Confirm these, and check the mode mask): + { + default: + case 0x03: cdda.PlayMode = PLAYMODE_NORMAL; + cdda.CDDAStatus = CDDASTATUS_PLAYING; + break; + + case 0x02: cdda.PlayMode = PLAYMODE_INTERRUPT; + cdda.CDDAStatus = CDDASTATUS_PLAYING; + break; + + case 0x01: cdda.PlayMode = PLAYMODE_LOOP; + cdda.CDDAStatus = CDDASTATUS_PLAYING; + break; + + case 0x00: cdda.PlayMode = PLAYMODE_SILENT; + cdda.CDDAStatus = CDDASTATUS_STOPPED; + break; + } + + SendStatusAndMessage(STATUS_GOOD, 0x00); +} + + + +/******************************************************** +* * +* PC Engine CD Command 0xDA - Pause * +* * +********************************************************/ +static void DoNEC_PCE_PAUSE(const uint8 *cdb) +{ + if(cdda.CDDAStatus != CDDASTATUS_STOPPED) // Hmm, should we give an error if it tries to pause and it's already paused? + { + cdda.CDDAStatus = CDDASTATUS_PAUSED; + SendStatusAndMessage(STATUS_GOOD, 0x00); + } + else // Definitely give an error if it tries to pause when no track is playing! + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_AUDIO_NOT_PLAYING); + } +} + + + +/******************************************************** +* * +* PC Engine CD Command 0xDD - Read Subchannel Q * +* * +********************************************************/ +static void DoNEC_PCE_READSUBQ(const uint8 *cdb) +{ + uint8 *SubQBuf = cd.SubQBuf[QMode_Time]; + uint8 data_in[8192]; + + memset(data_in, 0x00, 10); + + data_in[2] = SubQBuf[1]; // Track + data_in[3] = SubQBuf[2]; // Index + data_in[4] = SubQBuf[3]; // M(rel) + data_in[5] = SubQBuf[4]; // S(rel) + data_in[6] = SubQBuf[5]; // F(rel) + data_in[7] = SubQBuf[7]; // M(abs) + data_in[8] = SubQBuf[8]; // S(abs) + data_in[9] = SubQBuf[9]; // F(abs) + + if(cdda.CDDAStatus == CDDASTATUS_PAUSED) + data_in[0] = 2; // Pause + else if(cdda.CDDAStatus == CDDASTATUS_PLAYING || cdda.CDDAStatus == CDDASTATUS_SCANNING) // FIXME: Is this the correct status code for scanning playback? + data_in[0] = 0; // Playing + else + data_in[0] = 3; // Stopped + + DoSimpleDataIn(data_in, 10); +} + + + +/******************************************************** +* * +* PC Engine CD Command 0xDE - Get Directory Info * +* * +********************************************************/ +static void DoNEC_PCE_GETDIRINFO(const uint8 *cdb) +{ + // Problems: + // Returned data lengths on real PCE are not confirmed. + // Mode 0x03 behavior not tested on real PCE + + uint8 data_in[2048]; + uint32 data_in_size = 0; + + memset(data_in, 0, sizeof(data_in)); + + switch(cdb[1]) + { + default: //MDFN_DispMessage("Unknown GETDIRINFO Mode: %02x", cdb[1]); + //printf("Unknown GETDIRINFO Mode: %02x", cdb[1]); + case 0x0: + data_in[0] = U8_to_BCD(toc.first_track); + data_in[1] = U8_to_BCD(toc.last_track); + + data_in_size = 2; + break; + + case 0x1: + { + uint8 m, s, f; + + LBA_to_AMSF(toc.tracks[100].lba, &m, &s, &f); + + data_in[0] = U8_to_BCD(m); + data_in[1] = U8_to_BCD(s); + data_in[2] = U8_to_BCD(f); + + data_in_size = 3; + } + break; + + case 0x2: + { + uint8 m, s, f; + int track = BCD_to_U8(cdb[2]); + + if(!track) + track = 1; + else if(cdb[2] == 0xAA) + { + track = 100; + } + else if(track > 99) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + LBA_to_AMSF(toc.tracks[track].lba, &m, &s, &f); + + data_in[0] = U8_to_BCD(m); + data_in[1] = U8_to_BCD(s); + data_in[2] = U8_to_BCD(f); + data_in[3] = toc.tracks[track].control; + data_in_size = 4; + } + break; + } + + DoSimpleDataIn(data_in, data_in_size); +} + diff --git a/waterbox/pcfx/cdrom/scsicd.cpp b/waterbox/pcfx/cdrom/scsicd.cpp new file mode 100644 index 0000000000..c5a7667883 --- /dev/null +++ b/waterbox/pcfx/cdrom/scsicd.cpp @@ -0,0 +1,3081 @@ +/* Mednafen - Multi-system Emulator + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "../defs.h" +#include +#include +#include "cdromif.h" +#include "scsicd.h" +#include "SimpleFIFO.h" + +#if defined(__SSE2__) +#include +#include +#endif + +//#define SCSIDBG(format, ...) { printf("[SCSICD] " format "\n", ## __VA_ARGS__); } +//#define SCSIDBG(format, ...) { } + +using namespace CDUtility; + +static uint32 CD_DATA_TRANSFER_RATE; +static uint32 System_Clock; +static void (*CDIRQCallback)(int); +static void (*CDStuffSubchannels)(uint8, int); +static int32 *HRBufs[2]; +static int WhichSystem; + +static CDIF *Cur_CDIF; +static bool TrayOpen; + +// Internal operation to the SCSI CD unit. Only pass 1 or 0 to these macros! +#define SetIOP(mask, set) \ + { \ + cd_bus.signals &= ~mask; \ + if (set) \ + cd_bus.signals |= mask; \ + } + +#define SetBSY(set) SetIOP(SCSICD_BSY_mask, set) +#define SetIO(set) SetIOP(SCSICD_IO_mask, set) +#define SetCD(set) SetIOP(SCSICD_CD_mask, set) +#define SetMSG(set) SetIOP(SCSICD_MSG_mask, set) + +static INLINE void SetREQ(bool set) +{ + if (set && !REQ_signal) + CDIRQCallback(SCSICD_IRQ_MAGICAL_REQ); + + SetIOP(SCSICD_REQ_mask, set); +} + +#define SetkingACK(set) SetIOP(SCSICD_kingACK_mask, set) +#define SetkingRST(set) SetIOP(SCSICD_kingRST_mask, set) +#define SetkingSEL(set) SetIOP(SCSICD_kingSEL_mask, set) +#define SetkingATN(set) SetIOP(SCSICD_kingATN_mask, set) + +enum +{ + QMode_Zero = 0, + QMode_Time = 1, + QMode_MCN = 2, // Media Catalog Number + QMode_ISRC = 3 // International Standard Recording Code +}; + +typedef struct +{ + bool last_RST_signal; + + // The pending message to send(in the message phase) + uint8 message_pending; + + bool status_sent, message_sent; + + // Pending error codes + uint8 key_pending, asc_pending, ascq_pending, fru_pending; + + uint8 command_buffer[256]; + uint8 command_buffer_pos; + uint8 command_size_left; + + // FALSE if not all pending data is in the FIFO, TRUE if it is. + // Used for multiple sector CD reads. + bool data_transfer_done; + + // To target(the cd unit); for "MODE SELECT". + uint8 data_out[256]; // Technically it only needs to be 255, but powers of 2 are better than those degenerate powers of 2 minus one goons. + uint8 data_out_pos; // Current index for writing into data_out. + uint8 data_out_want; // Total number of bytes to buffer into data_out. + + bool DiscChanged; + + uint8 SubQBuf[4][0xC]; // One for each of the 4 most recent q-Modes. + uint8 SubQBuf_Last[0xC]; // The most recent q subchannel data, regardless of q-mode. + + uint8 SubPWBuf[96]; + +} scsicd_t; + +enum +{ + CDDASTATUS_PAUSED = -1, + CDDASTATUS_STOPPED = 0, + CDDASTATUS_PLAYING = 1, + CDDASTATUS_SCANNING = 2, +}; + +enum +{ + PLAYMODE_SILENT = 0x00, + PLAYMODE_NORMAL, + PLAYMODE_INTERRUPT, + PLAYMODE_LOOP, +}; + +typedef struct +{ + uint32 CDDADivAcc; + uint8 CDDADivAccVolFudge; // For PC-FX CD-DA rate control RE impulses and resampling; 100 = 1.0. + uint32 scan_sec_end; + + uint8 PlayMode; + int32 CDDAVolume[2]; // 65536 = 1.0, the maximum. + int16 CDDASectorBuffer[1176]; + uint32 CDDAReadPos; + + int8 CDDAStatus; + uint8 ScanMode; + int64 CDDADiv; + int CDDATimeDiv; + + int16 OversampleBuffer[2][0x10 * 2]; // *2 so our MAC loop can blast through without masking the index. + unsigned OversamplePos; + + int16 sr[2]; + + uint8 OutPortChSelect[2]; + uint32 OutPortChSelectCache[2]; + int32 OutPortVolumeCache[2]; + + float DeemphState[2][2]; +} cdda_t; + +void MakeSense(uint8 *target, uint8 key, uint8 asc, uint8 ascq, uint8 fru) +{ + memset(target, 0, 18); + + target[0] = 0x70; // Current errors and sense data is not SCSI compliant + target[2] = key; + target[7] = 0x0A; + target[12] = asc; // Additional Sense Code + target[13] = ascq; // Additional Sense Code Qualifier + target[14] = fru; // Field Replaceable Unit code +} + +static void (*SCSILog)(const char *, const char *format, ...); +static void InitModePages(void); + +static scsicd_timestamp_t lastts; +static int64 monotonic_timestamp; +static int64 pce_lastsapsp_timestamp; + +scsicd_t cd; +scsicd_bus_t cd_bus; +static cdda_t cdda; + +static SimpleFIFO *din = NULL; + +static CDUtility::TOC toc; + +static uint32 read_sec_start; +static uint32 read_sec; +static uint32 read_sec_end; + +static int32 CDReadTimer; +static uint32 SectorAddr; +static uint32 SectorCount; + +enum +{ + PHASE_BUS_FREE = 0, + PHASE_COMMAND, + PHASE_DATA_IN, + PHASE_DATA_OUT, + PHASE_STATUS, + PHASE_MESSAGE_IN, + PHASE_MESSAGE_OUT +}; +static unsigned int CurrentPhase; +static void ChangePhase(const unsigned int new_phase); + +static void FixOPV(void) +{ + for (int port = 0; port < 2; port++) + { + int32 tmpvol = cdda.CDDAVolume[port] * 100 / (2 * cdda.CDDADivAccVolFudge); + + //printf("TV: %d\n", tmpvol); + + cdda.OutPortVolumeCache[port] = tmpvol; + + if (cdda.OutPortChSelect[port] & 0x01) + cdda.OutPortChSelectCache[port] = 0; + else if (cdda.OutPortChSelect[port] & 0x02) + cdda.OutPortChSelectCache[port] = 1; + else + { + cdda.OutPortChSelectCache[port] = 0; + cdda.OutPortVolumeCache[port] = 0; + } + } +} + +static void VirtualReset(void) +{ + InitModePages(); + + din->Flush(); + + CDReadTimer = 0; + + pce_lastsapsp_timestamp = monotonic_timestamp; + + SectorAddr = SectorCount = 0; + read_sec_start = read_sec = 0; + read_sec_end = ~0; + + cdda.PlayMode = PLAYMODE_SILENT; + cdda.CDDAReadPos = 0; + cdda.CDDAStatus = CDDASTATUS_STOPPED; + cdda.CDDADiv = 1; + + cdda.ScanMode = 0; + cdda.scan_sec_end = 0; + + cdda.OversamplePos = 0; + memset(cdda.sr, 0, sizeof(cdda.sr)); + memset(cdda.OversampleBuffer, 0, sizeof(cdda.OversampleBuffer)); + memset(cdda.DeemphState, 0, sizeof(cdda.DeemphState)); + + memset(cd.data_out, 0, sizeof(cd.data_out)); + cd.data_out_pos = 0; + cd.data_out_want = 0; + + FixOPV(); + + ChangePhase(PHASE_BUS_FREE); +} + +void SCSICD_Power(scsicd_timestamp_t system_timestamp) +{ + memset(&cd, 0, sizeof(scsicd_t)); + memset(&cd_bus, 0, sizeof(scsicd_bus_t)); + + monotonic_timestamp = system_timestamp; + + cd.DiscChanged = false; + + if (Cur_CDIF && !TrayOpen) + Cur_CDIF->ReadTOC(&toc); + + CurrentPhase = PHASE_BUS_FREE; + + VirtualReset(); +} + +void SCSICD_SetDB(uint8 data) +{ + cd_bus.DB = data; + //printf("Set DB: %02x\n", data); +} + +void SCSICD_SetACK(bool set) +{ + SetkingACK(set); + //printf("Set ACK: %d\n", set); +} + +void SCSICD_SetSEL(bool set) +{ + SetkingSEL(set); + //printf("Set SEL: %d\n", set); +} + +void SCSICD_SetRST(bool set) +{ + SetkingRST(set); + //printf("Set RST: %d\n", set); +} + +void SCSICD_SetATN(bool set) +{ + SetkingATN(set); + //printf("Set ATN: %d\n", set); +} + +static void GenSubQFromSubPW(void) +{ + uint8 SubQBuf[0xC]; + + memset(SubQBuf, 0, 0xC); + + for (int i = 0; i < 96; i++) + SubQBuf[i >> 3] |= ((cd.SubPWBuf[i] & 0x40) >> 6) << (7 - (i & 7)); + + //printf("Real %d/ SubQ %d - ", read_sec, BCD_to_U8(SubQBuf[7]) * 75 * 60 + BCD_to_U8(SubQBuf[8]) * 75 + BCD_to_U8(SubQBuf[9]) - 150); + // Debug code, remove me. + //for(int i = 0; i < 0xC; i++) + // printf("%02x ", SubQBuf[i]); + //printf("\n"); + + if (!subq_check_checksum(SubQBuf)) + { + //SCSIDBG("SubQ checksum error!"); + } + else + { + memcpy(cd.SubQBuf_Last, SubQBuf, 0xC); + + uint8 adr = SubQBuf[0] & 0xF; + + if (adr <= 0x3) + memcpy(cd.SubQBuf[adr], SubQBuf, 0xC); + + //if(adr == 0x02) + //for(int i = 0; i < 12; i++) + // printf("%02x\n", cd.SubQBuf[0x2][i]); + } +} + +#define STATUS_GOOD 0 +#define STATUS_CHECK_CONDITION 1 +#define STATUS_CONDITION_MET 2 +#define STATUS_BUSY 4 +#define STATUS_INTERMEDIATE 8 + +#define SENSEKEY_NO_SENSE 0x0 +#define SENSEKEY_NOT_READY 0x2 +#define SENSEKEY_MEDIUM_ERROR 0x3 +#define SENSEKEY_HARDWARE_ERROR 0x4 +#define SENSEKEY_ILLEGAL_REQUEST 0x5 +#define SENSEKEY_UNIT_ATTENTION 0x6 +#define SENSEKEY_ABORTED_COMMAND 0xB + +#define ASC_MEDIUM_NOT_PRESENT 0x3A + +// NEC sub-errors(ASC), no ASCQ. +#define NSE_NO_DISC 0x0B // Used with SENSEKEY_NOT_READY - This condition occurs when tray is closed with no disc present. +#define NSE_TRAY_OPEN 0x0D // Used with SENSEKEY_NOT_READY +#define NSE_SEEK_ERROR 0x15 +#define NSE_HEADER_READ_ERROR 0x16 // Used with SENSEKEY_MEDIUM_ERROR +#define NSE_NOT_AUDIO_TRACK 0x1C // Used with SENSEKEY_MEDIUM_ERROR +#define NSE_NOT_DATA_TRACK 0x1D // Used with SENSEKEY_MEDIUM_ERROR +#define NSE_INVALID_COMMAND 0x20 +#define NSE_INVALID_ADDRESS 0x21 +#define NSE_INVALID_PARAMETER 0x22 +#define NSE_END_OF_VOLUME 0x25 +#define NSE_INVALID_REQUEST_IN_CDB 0x27 +#define NSE_DISC_CHANGED 0x28 // Used with SENSEKEY_UNIT_ATTENTION +#define NSE_AUDIO_NOT_PLAYING 0x2C + +// ASC, ASCQ pair +#define AP_UNRECOVERED_READ_ERROR 0x11, 0x00 +#define AP_LEC_UNCORRECTABLE_ERROR 0x11, 0x05 +#define AP_CIRC_UNRECOVERED_ERROR 0x11, 0x06 + +#define AP_UNKNOWN_MEDIUM_FORMAT 0x30, 0x01 +#define AP_INCOMPAT_MEDIUM_FORMAT 0x30, 0x02 + +static void ChangePhase(const unsigned int new_phase) +{ + //printf("New phase: %d %lld\n", new_phase, monotonic_timestamp); + switch (new_phase) + { + case PHASE_BUS_FREE: + SetBSY(false); + SetMSG(false); + SetCD(false); + SetIO(false); + SetREQ(false); + + CDIRQCallback(0x8000 | SCSICD_IRQ_DATA_TRANSFER_DONE); + break; + + case PHASE_DATA_IN: // Us to them + SetBSY(true); + SetMSG(false); + SetCD(false); + SetIO(true); + //SetREQ(true); + SetREQ(false); + break; + + case PHASE_STATUS: // Us to them + SetBSY(true); + SetMSG(false); + SetCD(true); + SetIO(true); + SetREQ(true); + break; + + case PHASE_MESSAGE_IN: // Us to them + SetBSY(true); + SetMSG(true); + SetCD(true); + SetIO(true); + SetREQ(true); + break; + + case PHASE_DATA_OUT: // Them to us + SetBSY(true); + SetMSG(false); + SetCD(false); + SetIO(false); + SetREQ(true); + break; + + case PHASE_COMMAND: // Them to us + SetBSY(true); + SetMSG(false); + SetCD(true); + SetIO(false); + SetREQ(true); + break; + + case PHASE_MESSAGE_OUT: // Them to us + SetBSY(true); + SetMSG(true); + SetCD(true); + SetIO(false); + SetREQ(true); + break; + } + CurrentPhase = new_phase; +} + +static void SendStatusAndMessage(uint8 status, uint8 message) +{ + // This should never ever happen, but that doesn't mean it won't. ;) + if (din->CanRead()) + { + //printf("[SCSICD] BUG: %d bytes still in SCSI CD FIFO\n", din->CanRead()); + din->Flush(); + } + + cd.message_pending = message; + + cd.status_sent = FALSE; + cd.message_sent = FALSE; + + if (WhichSystem == SCSICD_PCE) + { + if (status == STATUS_GOOD || status == STATUS_CONDITION_MET) + cd_bus.DB = 0x00; + else + cd_bus.DB = 0x01; + } + else + cd_bus.DB = status << 1; + + ChangePhase(PHASE_STATUS); +} + +static void DoSimpleDataIn(const uint8 *data_in, uint32 len) +{ + din->Write(data_in, len); + + cd.data_transfer_done = true; + + ChangePhase(PHASE_DATA_IN); +} + +void SCSICD_SetDisc(bool new_tray_open, CDIF *cdif, bool no_emu_side_effects) +{ + Cur_CDIF = cdif; + + // Closing the tray. + if (TrayOpen && !new_tray_open) + { + TrayOpen = false; + + if (cdif) + { + cdif->ReadTOC(&toc); + + if (!no_emu_side_effects) + { + memset(cd.SubQBuf, 0, sizeof(cd.SubQBuf)); + memset(cd.SubQBuf_Last, 0, sizeof(cd.SubQBuf_Last)); + cd.DiscChanged = true; + } + } + } + else if (!TrayOpen && new_tray_open) // Opening the tray + { + TrayOpen = true; + } +} + +static void CommandCCError(int key, int asc = 0, int ascq = 0) +{ + //printf("[SCSICD] CC Error: %02x %02x %02x\n", key, asc, ascq); + + cd.key_pending = key; + cd.asc_pending = asc; + cd.ascq_pending = ascq; + cd.fru_pending = 0x00; + + SendStatusAndMessage(STATUS_CHECK_CONDITION, 0x00); +} + +static bool ValidateRawDataSector(uint8 *data, const uint32 lba) +{ + if (!Cur_CDIF->ValidateRawSector(data)) + { + printf("Uncorrectable data at sector %d", lba); + + din->Flush(); + cd.data_transfer_done = false; + + CommandCCError(SENSEKEY_MEDIUM_ERROR, AP_LEC_UNCORRECTABLE_ERROR); + return (false); + } + + return (true); +} + +static void DoMODESELECT6(const uint8 *cdb) +{ + if (cdb[4]) + { + cd.data_out_pos = 0; + cd.data_out_want = cdb[4]; + //printf("Switch to DATA OUT phase, len: %d\n", cd.data_out_want); + + ChangePhase(PHASE_DATA_OUT); + } + else + SendStatusAndMessage(STATUS_GOOD, 0x00); +} + +/* + All Japan Female Pro Wrestle: + Datumama: 10, 00 00 00 00 00 00 00 00 00 0a + + Kokuu Hyouryuu Nirgends: + Datumama: 10, 00 00 00 00 00 00 00 00 00 0f + Datumama: 10, 00 00 00 00 00 00 00 00 00 0f + + Last Imperial Prince: + Datumama: 10, 00 00 00 00 00 00 00 00 00 0f + Datumama: 10, 00 00 00 00 00 00 00 00 00 0f + + Megami Paradise II: + Datumama: 10, 00 00 00 00 00 00 00 00 00 0a + + Miraculum: + Datumama: 7, 00 00 00 00 29 01 00 + Datumama: 10, 00 00 00 00 00 00 00 00 00 0f + Datumama: 7, 00 00 00 00 29 01 00 + Datumama: 10, 00 00 00 00 00 00 00 00 00 00 + Datumama: 7, 00 00 00 00 29 01 00 + + Pachio Kun FX: + Datumama: 10, 00 00 00 00 00 00 00 00 00 14 + + Return to Zork: + Datumama: 10, 00 00 00 00 00 00 00 00 00 00 + + Sotsugyou II: + Datumama: 10, 00 00 00 00 01 00 00 00 00 01 + + Tokimeki Card Paradise: + Datumama: 10, 00 00 00 00 00 00 00 00 00 14 + Datumama: 10, 00 00 00 00 00 00 00 00 00 07 + + Tonari no Princess Rolfee: + Datumama: 10, 00 00 00 00 00 00 00 00 00 00 + + Zoku Hakutoi Monogatari: + Datumama: 10, 00 00 00 00 00 00 00 00 00 14 +*/ + +// Page 151: MODE SENSE(6) +// PC = 0 current +// PC = 1 Changeable +// PC = 2 Default +// PC = 3 Saved +// Page 183: Mode parameter header. +// Page 363: CD-ROM density codes. +// Page 364: CD-ROM mode page codes. +// Page 469: ASC and ASCQ table + +struct ModePageParam +{ + uint8 default_value; + uint8 alterable_mask; // Alterable mask reported when PC == 1 + uint8 real_mask; // Real alterable mask. +}; + +struct ModePage +{ + const uint8 code; + const uint8 param_length; + const ModePageParam params[64]; // 64 should be more than enough + uint8 current_value[64]; +}; + +/* + Mode pages present: + 0x00: + 0x0E: + 0x28: + 0x29: + 0x2A: + 0x2B: + 0x3F(Yes, not really a mode page but a fetch method) +*/ +static const int NumModePages = 5; +static ModePage ModePages[NumModePages] = + { + // Unknown + {0x28, + 0x04, + { + {0x00, 0x00, 0xFF}, + {0x00, 0x00, 0xFF}, + {0x00, 0x00, 0xFF}, + {0x00, 0x00, 0xFF}, + }}, + + // Unknown + {0x29, + 0x01, + { + {0x00, 0x00, 0xFF}, + }}, + + // Unknown + {0x2a, + 0x02, + { + {0x00, 0x00, 0xFF}, + {0x11, 0x00, 0xFF}, + }}, + + // CD-DA playback speed modifier + {0x2B, + 0x01, + { + {0x00, 0x00, 0xFF}, + }}, + + // 0x0E goes last, for correct order of return data when page code == 0x3F + // Real mask values are probably not right; some functionality not emulated yet. + // CD-ROM audio control parameters + {0x0E, + 0x0E, + { + {0x04, 0x04, 0x04}, // Immed + {0x00, 0x00, 0x00}, // Reserved + {0x00, 0x00, 0x00}, // Reserved + {0x00, 0x01, 0x01}, // Reserved? + {0x00, 0x00, 0x00}, // MSB of LBA per second. + {0x00, 0x00, 0x00}, // LSB of LBA per second. + {0x01, 0x01, 0x03}, // Outport port 0 channel selection. + {0xFF, 0x00, 0x00}, // Outport port 0 volume. + {0x02, 0x02, 0x03}, // Outport port 1 channel selection. + {0xFF, 0x00, 0x00}, // Outport port 1 volume. + {0x00, 0x00, 0x00}, // Outport port 2 channel selection. + {0x00, 0x00, 0x00}, // Outport port 2 volume. + {0x00, 0x00, 0x00}, // Outport port 3 channel selection. + {0x00, 0x00, 0x00}, // Outport port 3 volume. + }}, +}; + +static void UpdateMPCacheP(const ModePage *mp) +{ + switch (mp->code) + { + case 0x0E: + { + const uint8 *pd = &mp->current_value[0]; + + for (int i = 0; i < 2; i++) + cdda.OutPortChSelect[i] = pd[6 + i * 2]; + FixOPV(); + } + break; + + case 0x28: + break; + + case 0x29: + break; + + case 0x2A: + break; + + case 0x2B: + { + int speed; + int rate; + + // + // Not sure what the actual limits are, or what happens when exceeding them, but these will at least keep the + // CD-DA playback system from imploding in on itself. + // + // The range of speed values accessible via the BIOS CD-DA player is apparently -10 to 10. + // + // No game is known to use the CD-DA playback speed control. It may be useful in homebrew to lower the rate for fitting more CD-DA onto the disc, + // is implemented on the PC-FX in such a way that it degrades audio quality, so it wouldn't really make sense to increase the rate in homebrew. + // + // Due to performance considerations, we only partially emulate the CD-DA oversampling filters used on the PC Engine and PC-FX, and instead + // blast impulses into the 1.78MHz buffer, relying on the final sound resampler to kill spectrum mirrors. This is less than ideal, but generally + // works well in practice, except when lowering CD-DA playback rate...which causes the spectrum mirrors to enter the non-murder zone, causing + // the sound output amplitude to approach overflow levels. + // But, until there's a killer PC-FX homebrew game that necessitates more computationally-expensive CD-DA handling, + // I don't see a good reason to change how CD-DA resampling is currently implemented. + // + speed = std::max(-32, std::min(32, (int8)mp->current_value[0])); + rate = 44100 + 441 * speed; + + //printf("[SCSICD] Speed: %d(pre-clamped=%d) %d\n", speed, (int8)mp->current_value[0], rate); + cdda.CDDADivAcc = ((int64)System_Clock * (1024 * 1024) / (2 * rate)); + cdda.CDDADivAccVolFudge = 100 + speed; + FixOPV(); // Resampler impulse amplitude volume adjustment(call after setting cdda.CDDADivAccVolFudge) + } + break; + } +} + +static void UpdateMPCache(uint8 code) +{ + for (int pi = 0; pi < NumModePages; pi++) + { + const ModePage *mp = &ModePages[pi]; + + if (mp->code == code) + { + UpdateMPCacheP(mp); + break; + } + } +} + +static void InitModePages(void) +{ + for (int pi = 0; pi < NumModePages; pi++) + { + ModePage *mp = &ModePages[pi]; + const ModePageParam *params = &ModePages[pi].params[0]; + + for (int parami = 0; parami < mp->param_length; parami++) + mp->current_value[parami] = params[parami].default_value; + + UpdateMPCacheP(mp); + } +} + +static void FinishMODESELECT6(const uint8 *data, const uint8 data_len) +{ + uint8 mode_data_length, medium_type, device_specific, block_descriptor_length; + uint32 offset = 0; + + //printf("[SCSICD] Mode Select (6) Data: Length=0x%02x, ", data_len); + //for(uint32 i = 0; i < data_len; i++) + // printf("0x%02x ", data[i]); + //printf("\n"); + + if (data_len < 4) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + mode_data_length = data[offset++]; + medium_type = data[offset++]; + device_specific = data[offset++]; + block_descriptor_length = data[offset++]; + + // For now, shut up gcc. + (void)mode_data_length; + (void)medium_type; + (void)device_specific; + + if (block_descriptor_length & 0x7) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + if ((offset + block_descriptor_length) > data_len) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + // TODO: block descriptors. + offset += block_descriptor_length; + + // Now handle mode pages + while (offset < data_len) + { + const uint8 code = data[offset++]; + uint8 param_len = 0; + bool page_found = false; + + if (code == 0x00) + { + if ((offset + 0x5) > data_len) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + UpdateMPCache(0x00); + + offset += 0x5; + continue; + } + + if (offset >= data_len) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + param_len = data[offset++]; + + for (int pi = 0; pi < NumModePages; pi++) + { + ModePage *mp = &ModePages[pi]; + + if (code == mp->code) + { + page_found = true; + + if (param_len != mp->param_length) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + if ((param_len + offset) > data_len) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + for (int parami = 0; parami < mp->param_length; parami++) + { + mp->current_value[parami] &= ~mp->params[parami].real_mask; + mp->current_value[parami] |= (data[offset++]) & mp->params[parami].real_mask; + } + + UpdateMPCacheP(mp); + break; + } + } + + if (!page_found) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + } + + SendStatusAndMessage(STATUS_GOOD, 0x00); +} + +static void DoMODESENSE6(const uint8 *cdb) +{ + unsigned int PC = (cdb[2] >> 6) & 0x3; + unsigned int PageCode = cdb[2] & 0x3F; + bool DBD = cdb[1] & 0x08; + int AllocSize = cdb[4]; + int index = 0; + uint8 data_in[8192]; + uint8 PageMatchOR = 0x00; + bool AnyPageMatch = false; + + //SCSIDBG("Mode sense 6: %02x %d %d %d", PageCode, PC, DBD, AllocSize); + + if (!AllocSize) + { + SendStatusAndMessage(STATUS_GOOD, 0x00); + return; + } + + if (PC == 3) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + if (PageCode == 0x00) // Special weird case. + { + if (DBD || PC) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + memset(data_in, 0, 0xA); + data_in[0] = 0x09; + data_in[2] = 0x80; + data_in[9] = 0x0F; + + if (AllocSize > 0xA) + AllocSize = 0xA; + + DoSimpleDataIn(data_in, AllocSize); + return; + } + + data_in[0] = 0x00; // Fill this in later. + data_in[1] = 0x00; // Medium type + data_in[2] = 0x00; // Device-specific parameter. + data_in[3] = DBD ? 0x00 : 0x08; // Block descriptor length. + index += 4; + + if (!DBD) + { + data_in[index++] = 0x00; // Density code. + MDFN_en24msb(&data_in[index], 0x6E); // FIXME: Number of blocks? + index += 3; + + data_in[index++] = 0x00; // Reserved + MDFN_en24msb(&data_in[index], 0x800); // Block length; + index += 3; + } + + PageMatchOR = 0x00; + if (PageCode == 0x3F) + PageMatchOR = 0x3F; + + for (int pi = 0; pi < NumModePages; pi++) + { + const ModePage *mp = &ModePages[pi]; + const ModePageParam *params = &ModePages[pi].params[0]; + + if ((mp->code | PageMatchOR) != PageCode) + continue; + + AnyPageMatch = true; + + data_in[index++] = mp->code; + data_in[index++] = mp->param_length; + + for (int parami = 0; parami < mp->param_length; parami++) + { + uint8 data; + + if (PC == 0x02) + data = params[parami].default_value; + else if (PC == 0x01) + data = params[parami].alterable_mask; + else + data = mp->current_value[parami]; + + data_in[index++] = data; + } + } + + if (!AnyPageMatch) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + if (AllocSize > index) + AllocSize = index; + + data_in[0] = AllocSize - 1; + + DoSimpleDataIn(data_in, AllocSize); +} + +static void DoSTARTSTOPUNIT6(const uint8 *cdb) +{ + //bool Immed = cdb[1] & 0x01; + //bool LoEj = cdb[4] & 0x02; + //bool Start = cdb[4] & 0x01; + + //SCSIDBG("Do start stop unit 6: %d %d %d\n", Immed, LoEj, Start); + + SendStatusAndMessage(STATUS_GOOD, 0x00); +} + +static void DoREZEROUNIT(const uint8 *cdb) +{ + //SCSIDBG("Rezero Unit: %02x\n", cdb[5]); + SendStatusAndMessage(STATUS_GOOD, 0x00); +} + +// This data was originally taken from a PC-FXGA software loader, but +// while it was mostly correct(maybe it is correct for the FXGA, but not for the PC-FX?), +// it was 3 bytes too long, and the last real byte was 0x45 instead of 0x20. +// TODO: Investigate this discrepancy by testing an FXGA with the official loader software. +#if 0 +static const uint8 InqData[0x24] = +{ + // Standard + 0x05, 0x80, 0x02, 0x00, + + // Additional Length + 0x1F, + + // Vendor Specific + 0x00, 0x00, 0x00, 0x4E, 0x45, 0x43, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x43, 0x44, 0x2D, 0x52, 0x4F, + 0x4D, 0x20, 0x44, 0x52, 0x49, 0x56, 0x45, 0x3A, + 0x46, 0x58, 0x20, 0x31, 0x2E, 0x30, 0x20 +}; +#endif + +// Miraculum behaves differently if the last byte(offset 0x23) of the inquiry data is 0x45(ASCII character 'E'). Relavent code is at PC=0x3E382 +// If it's = 0x45, it will run MODE SELECT, and transfer this data to the CD unit: 00 00 00 00 29 01 00 +static const uint8 InqData[0x24] = + { + // Peripheral device-type: CD-ROM/read-only direct access device + 0x05, + + // Removable media: yes + // Device-type qualifier: 0 + 0x80, + + // ISO version: 0 + // ECMA version: 0 + // ANSI version: 2 (SCSI-2? ORLY?) + 0x02, + + // Supports asynchronous event notification: no + // Supports the terminate I/O process message: no + // Response data format: 0 (not exactly correct, not exactly incorrect, meh. :b) + 0x00, + + // Additional Length + 0x1F, + + // Reserved + 0x00, 0x00, + + // Yay, no special funky features. + 0x00, + + // 8-15, vendor ID + // NEC + 0x4E, 0x45, 0x43, 0x20, 0x20, 0x20, 0x20, 0x20, + + // 16-31, product ID + // CD-ROM DRIVE:FX + 0x43, 0x44, 0x2D, 0x52, 0x4F, 0x4D, 0x20, 0x44, 0x52, 0x49, 0x56, 0x45, 0x3A, 0x46, 0x58, 0x20, + + // 32-35, product revision level + // 1.0 + 0x31, 0x2E, 0x30, 0x20}; + +static void DoINQUIRY(const uint8 *cdb) +{ + unsigned int AllocSize = (cdb[4] < sizeof(InqData)) ? cdb[4] : sizeof(InqData); + + if (AllocSize) + DoSimpleDataIn(InqData, AllocSize); + else + SendStatusAndMessage(STATUS_GOOD, 0x00); +} + +static void DoNEC_NOP(const uint8 *cdb) +{ + SendStatusAndMessage(STATUS_GOOD, 0x00); +} + +/******************************************************** +* * +* PC-FX CD Command 0xDC - EJECT * +* * +********************************************************/ +static void DoNEC_EJECT(const uint8 *cdb) +{ + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_REQUEST_IN_CDB); +} + +static void DoREQUESTSENSE(const uint8 *cdb) +{ + uint8 data_in[8192]; + + MakeSense(data_in, cd.key_pending, cd.asc_pending, cd.ascq_pending, cd.fru_pending); + + DoSimpleDataIn(data_in, 18); + + cd.key_pending = 0; + cd.asc_pending = 0; + cd.ascq_pending = 0; + cd.fru_pending = 0; +} + +static void EncodeM3TOC(uint8 *buf, uint8 POINTER_RAW, int32 LBA, uint32 PLBA, uint8 control) +{ + uint8 MIN, SEC, FRAC; + uint8 PMIN, PSEC, PFRAC; + + LBA_to_AMSF(LBA, &MIN, &SEC, &FRAC); + LBA_to_AMSF(PLBA, &PMIN, &PSEC, &PFRAC); + + buf[0x0] = control << 4; + buf[0x1] = 0x00; // TNO + buf[0x2] = POINTER_RAW; + buf[0x3] = U8_to_BCD(MIN); + buf[0x4] = U8_to_BCD(SEC); + buf[0x5] = U8_to_BCD(FRAC); + buf[0x6] = 0x00; // Zero + buf[0x7] = U8_to_BCD(PMIN); + buf[0x8] = U8_to_BCD(PSEC); + buf[0x9] = U8_to_BCD(PFRAC); +} + +/******************************************************** +* * +* PC-FX CD Command 0xDE - Get Directory Info * +* * +********************************************************/ +static void DoNEC_GETDIRINFO(const uint8 *cdb) +{ + // Problems: + // Mode 0x03 has a few semi-indeterminate(but within a range, and they only change when the disc is reloaded) fields on a real PC-FX, that correspond to where in the lead-in area the data + // was read, that we don't bother to handle here. + // Mode 0x03 returns weird/wrong control field data for the "last track" and "leadout" entries in the "Blue Breaker" TOC. + // A bug in the PC-FX CD firmware, or an oddity of the disc(maybe other PC-FX discs are similar)? Or maybe it's an undefined field in that context? + // "Match" value of 0xB0 is probably not handled properly. Is it to return the catalog number, or something else? + + uint8 data_in[2048]; + uint32 data_in_size = 0; + + memset(data_in, 0, sizeof(data_in)); + + switch (cdb[1] & 0x03) + { + // This commands returns relevant raw TOC data as encoded in the Q subchannel(sans the CRC bytes). + case 0x3: + { + int offset = 0; + int32 lilba = -150; + uint8 match = cdb[2]; + + if (match != 0x00 && match != 0xA0 && match != 0xA1 && match != 0xA2 && match != 0xB0) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_ADDRESS); + return; + } + memset(data_in, 0, sizeof(data_in)); + + data_in[0] = 0x00; // Size MSB??? + data_in[1] = 0x00; // Total Size - 2(we'll fill it in later). + offset = 2; + + if (!match || match == 0xA0) + { + EncodeM3TOC(&data_in[offset], 0xA0, lilba, toc.first_track * 75 * 60 - 150, toc.tracks[toc.first_track].control); + lilba++; + offset += 0xA; + } + + if (!match || match == 0xA1) + { + EncodeM3TOC(&data_in[offset], 0xA1, lilba, toc.last_track * 75 * 60 - 150, toc.tracks[toc.last_track].control); + lilba++; + offset += 0xA; + } + + if (!match || match == 0xA2) + { + EncodeM3TOC(&data_in[offset], 0xA2, lilba, toc.tracks[100].lba, toc.tracks[100].control); + lilba++; + offset += 0xA; + } + + if (!match) + for (int track = toc.first_track; track <= toc.last_track; track++) + { + EncodeM3TOC(&data_in[offset], U8_to_BCD(track), lilba, toc.tracks[track].lba, toc.tracks[track].control); + lilba++; + offset += 0xA; + } + + if (match == 0xB0) + { + memset(&data_in[offset], 0, 0x14); + offset += 0x14; + } + + assert((unsigned int)offset <= sizeof(data_in)); + data_in_size = offset; + MDFN_en16msb(&data_in[0], offset - 2); + } + break; + + case 0x0: + data_in[0] = U8_to_BCD(toc.first_track); + data_in[1] = U8_to_BCD(toc.last_track); + + data_in_size = 4; + break; + + case 0x1: + { + uint8 m, s, f; + + LBA_to_AMSF(toc.tracks[100].lba, &m, &s, &f); + + data_in[0] = U8_to_BCD(m); + data_in[1] = U8_to_BCD(s); + data_in[2] = U8_to_BCD(f); + + data_in_size = 4; + } + break; + + case 0x2: + { + uint8 m, s, f; + int track = BCD_to_U8(cdb[2]); + + if (track < toc.first_track || track > toc.last_track) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_ADDRESS); + return; + } + + LBA_to_AMSF(toc.tracks[track].lba, &m, &s, &f); + + data_in[0] = U8_to_BCD(m); + data_in[1] = U8_to_BCD(s); + data_in[2] = U8_to_BCD(f); + data_in[3] = toc.tracks[track].control; + data_in_size = 4; + } + break; + } + + DoSimpleDataIn(data_in, data_in_size); +} + +static void DoREADTOC(const uint8 *cdb) +{ + uint8 data_in[8192]; + int FirstTrack = toc.first_track; + int LastTrack = toc.last_track; + int StartingTrack = cdb[6]; + unsigned int AllocSize = (cdb[7] << 8) | cdb[8]; + unsigned int RealSize = 0; + const bool WantInMSF = cdb[1] & 0x2; + + if (!AllocSize) + { + SendStatusAndMessage(STATUS_GOOD, 0x00); + return; + } + + if ((cdb[1] & ~0x2) || cdb[2] || cdb[3] || cdb[4] || cdb[5] || cdb[9]) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + if (!StartingTrack) + StartingTrack = 1; + else if (StartingTrack == 0xAA) + { + StartingTrack = LastTrack + 1; + } + else if (StartingTrack > toc.last_track) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + data_in[2] = FirstTrack; + data_in[3] = LastTrack; + + RealSize += 4; + + // Read leadout track too LastTrack + 1 ??? + for (int track = StartingTrack; track <= (LastTrack + 1); track++) + { + uint8 *subptr = &data_in[RealSize]; + uint32 lba; + uint8 m, s, f; + uint32 eff_track; + + if (track == (LastTrack + 1)) + eff_track = 100; + else + eff_track = track; + + lba = toc.tracks[eff_track].lba; + LBA_to_AMSF(lba, &m, &s, &f); + + subptr[0] = 0; + subptr[1] = toc.tracks[eff_track].control | (toc.tracks[eff_track].adr << 4); + + if (eff_track == 100) + subptr[2] = 0xAA; + else + subptr[2] = track; + + subptr[3] = 0; + + if (WantInMSF) + { + subptr[4] = 0; + subptr[5] = m; // Min + subptr[6] = s; // Sec + subptr[7] = f; // Frames + } + else + { + subptr[4] = lba >> 24; + subptr[5] = lba >> 16; + subptr[6] = lba >> 8; + subptr[7] = lba >> 0; + } + RealSize += 8; + } + + // PC-FX: AllocSize too small doesn't reflect in this. + data_in[0] = (RealSize - 2) >> 8; + data_in[1] = (RealSize - 2) >> 0; + + DoSimpleDataIn(data_in, (AllocSize < RealSize) ? AllocSize : RealSize); +} + +/******************************************************** +* * +* SCSI-2 CD Command 0x25 - READ CD-ROM CAPACITY * +* * +********************************************************/ +static void DoREADCDCAP10(const uint8 *cdb) +{ + bool pmi = cdb[8] & 0x1; + uint32 lba = MDFN_de32msb(cdb + 0x2); + uint32 ret_lba; + uint32 ret_bl; + uint8 data_in[8]; + + memset(data_in, 0, sizeof(data_in)); + + if (lba > 0x05FF69) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME); + return; + } + + ret_lba = toc.tracks[100].lba - 1; + + if (pmi) + { + // Look for the track containing the LBA specified, then search for the first track afterwards that has a different track type(audio, data), + // and set the returned LBA to the sector preceding that track. + // + // If the specified LBA is >= leadout track, return the LBA of the sector immediately before the leadout track. + // + // If the specified LBA is < than the LBA of the first track, then return the LBA of sector preceding the first track. (I don't know if PC-FX can even handle discs like this, though) + if (lba >= toc.tracks[100].lba) + ret_lba = toc.tracks[100].lba - 1; + else if (lba < toc.tracks[toc.first_track].lba) + ret_lba = toc.tracks[toc.first_track].lba - 1; + else + { + const int track = toc.FindTrackByLBA(lba); + + for (int st = track + 1; st <= toc.last_track; st++) + { + if ((toc.tracks[st].control ^ toc.tracks[track].control) & 0x4) + { + ret_lba = toc.tracks[st].lba - 1; + break; + } + } + } + } + + ret_bl = 2048; + + MDFN_en32msb(&data_in[0], ret_lba); + MDFN_en32msb(&data_in[4], ret_bl); + + cdda.CDDAStatus = CDDASTATUS_STOPPED; + + DoSimpleDataIn(data_in, 8); +} + +static void DoREADHEADER10(const uint8 *cdb) +{ + uint8 data_in[8192]; + bool WantInMSF = cdb[1] & 0x2; + uint32 HeaderLBA = MDFN_de32msb(cdb + 0x2); + int AllocSize = MDFN_de16msb(cdb + 0x7); + uint8 raw_buf[2352 + 96]; + uint8 mode; + int m, s, f; + uint32 lba; + + // Don't run command at all if AllocSize == 0(FIXME: On a real PC-FX, this command will return success + // if there's no CD when AllocSize == 0, implement this here, might require refactoring). + if (!AllocSize) + { + SendStatusAndMessage(STATUS_GOOD, 0x00); + return; + } + + if (HeaderLBA >= toc.tracks[100].lba) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + if (HeaderLBA < toc.tracks[toc.first_track].lba) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + Cur_CDIF->ReadRawSector(raw_buf, HeaderLBA); //, HeaderLBA + 1); + if (!ValidateRawDataSector(raw_buf, HeaderLBA)) + return; + + m = BCD_to_U8(raw_buf[12 + 0]); + s = BCD_to_U8(raw_buf[12 + 1]); + f = BCD_to_U8(raw_buf[12 + 2]); + mode = raw_buf[12 + 3]; + lba = AMSF_to_LBA(m, s, f); + + //printf("%d:%d:%d(LBA=%08x) %02x\n", m, s, f, lba, mode); + + data_in[0] = mode; + data_in[1] = 0; + data_in[2] = 0; + data_in[3] = 0; + + if (WantInMSF) + { + data_in[4] = 0; + data_in[5] = m; // Min + data_in[6] = s; // Sec + data_in[7] = f; // Frames + } + else + { + data_in[4] = lba >> 24; + data_in[5] = lba >> 16; + data_in[6] = lba >> 8; + data_in[7] = lba >> 0; + } + + cdda.CDDAStatus = CDDASTATUS_STOPPED; + + DoSimpleDataIn(data_in, 8); +} + +static void DoNEC_SST(const uint8 *cdb) // Command 0xDB, Set Stop Time +{ + SendStatusAndMessage(STATUS_GOOD, 0x00); +} + +static void DoPABase(const uint32 lba, const uint32 length, unsigned int status = CDDASTATUS_PLAYING, unsigned int mode = PLAYMODE_NORMAL) +{ + if (lba > toc.tracks[100].lba) // > is not a typo, it's a PC-FX bug apparently. + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + if (lba < toc.tracks[toc.first_track].lba) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + if (!length) // FIXME to return good status in this case even if no CD is present + { + SendStatusAndMessage(STATUS_GOOD, 0x00); + return; + } + else + { + if (toc.tracks[toc.FindTrackByLBA(lba)].control & 0x04) + { + CommandCCError(SENSEKEY_MEDIUM_ERROR, NSE_NOT_AUDIO_TRACK); + return; + } + + cdda.CDDAReadPos = 588; + read_sec = read_sec_start = lba; + read_sec_end = read_sec_start + length; + + cdda.CDDAStatus = status; + cdda.PlayMode = mode; + + if (read_sec < toc.tracks[100].lba) + { + Cur_CDIF->HintReadSector(read_sec); //, read_sec_end, read_sec_start); + } + } + + SendStatusAndMessage(STATUS_GOOD, 0x00); +} + +/******************************************************** +* * +* PC-FX CD Command 0xD8 - SAPSP * +* * +********************************************************/ +static void DoNEC_SAPSP(const uint8 *cdb) +{ + uint32 lba; + + switch (cdb[9] & 0xc0) + { + default: + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + break; + + case 0x00: + lba = MDFN_de24msb(&cdb[3]); + break; + + case 0x40: + { + uint8 m, s, f; + + if (!BCD_to_U8_check(cdb[2], &m) || !BCD_to_U8_check(cdb[3], &s) || !BCD_to_U8_check(cdb[4], &f)) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + lba = AMSF_to_LBA(m, s, f); + } + break; + + case 0x80: + { + uint8 track; + + if (!cdb[2] || !BCD_to_U8_check(cdb[2], &track)) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + if (track == toc.last_track + 1) + track = 100; + else if (track > toc.last_track) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME); + return; + } + lba = toc.tracks[track].lba; + } + break; + } + + if (cdb[1] & 0x01) + DoPABase(lba, toc.tracks[100].lba - lba, CDDASTATUS_PLAYING, PLAYMODE_NORMAL); + else + DoPABase(lba, toc.tracks[100].lba - lba, CDDASTATUS_PAUSED, PLAYMODE_SILENT); +} + +/******************************************************** +* * +* PC-FX CD Command 0xD9 - SAPEP * +* * +********************************************************/ +static void DoNEC_SAPEP(const uint8 *cdb) +{ + uint32 lba; + + if (cdda.CDDAStatus == CDDASTATUS_STOPPED) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_AUDIO_NOT_PLAYING); + return; + } + + switch (cdb[9] & 0xc0) + { + default: + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + break; + + case 0x00: + lba = MDFN_de24msb(&cdb[3]); + break; + + case 0x40: + { + uint8 m, s, f; + + if (!BCD_to_U8_check(cdb[2], &m) || !BCD_to_U8_check(cdb[3], &s) || !BCD_to_U8_check(cdb[4], &f)) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + lba = AMSF_to_LBA(m, s, f); + } + break; + + case 0x80: + { + uint8 track; + + if (!cdb[2] || !BCD_to_U8_check(cdb[2], &track)) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + if (track == toc.last_track + 1) + track = 100; + else if (track > toc.last_track) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME); + return; + } + lba = toc.tracks[track].lba; + } + break; + } + + switch (cdb[1] & 0x7) + { + case 0x00: + cdda.PlayMode = PLAYMODE_SILENT; + break; + + case 0x04: + cdda.PlayMode = PLAYMODE_LOOP; + break; + + default: + cdda.PlayMode = PLAYMODE_NORMAL; + break; + } + cdda.CDDAStatus = CDDASTATUS_PLAYING; + + read_sec_end = lba; + + SendStatusAndMessage(STATUS_GOOD, 0x00); +} + +/******************************************************** +* * +* SCSI-2 CD Command 0x45 - PLAY AUDIO(10) * +* * +********************************************************/ +static void DoPA10(const uint8 *cdb) +{ + // Real PC-FX Bug: Error out on LBA >(not >=) leadout sector number + const uint32 lba = MDFN_de32msb(cdb + 0x2); + const uint16 length = MDFN_de16msb(cdb + 0x7); + + DoPABase(lba, length); +} + +/******************************************************** +* * +* SCSI-2 CD Command 0xA5 - PLAY AUDIO(12) * +* * +********************************************************/ +static void DoPA12(const uint8 *cdb) +{ + // Real PC-FX Bug: Error out on LBA >(not >=) leadout sector number + const uint32 lba = MDFN_de32msb(cdb + 0x2); + const uint32 length = MDFN_de32msb(cdb + 0x6); + + DoPABase(lba, length); +} + +/******************************************************** +* * +* SCSI-2 CD Command 0x47 - PLAY AUDIO MSF * +* * +********************************************************/ +static void DoPAMSF(const uint8 *cdb) +{ + int32 lba_start, lba_end; + + lba_start = AMSF_to_LBA(cdb[3], cdb[4], cdb[5]); + lba_end = AMSF_to_LBA(cdb[6], cdb[7], cdb[8]); + + if (lba_start < 0 || lba_end < 0 || lba_start >= (int32)toc.tracks[100].lba) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME); + return; + } + + if (lba_start == lba_end) + { + SendStatusAndMessage(STATUS_GOOD, 0x00); + return; + } + else if (lba_start > lba_end) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_ADDRESS); + return; + } + + cdda.CDDAReadPos = 588; + read_sec = read_sec_start = lba_start; + read_sec_end = lba_end; + + cdda.CDDAStatus = CDDASTATUS_PLAYING; + cdda.PlayMode = PLAYMODE_NORMAL; + + SendStatusAndMessage(STATUS_GOOD, 0x00); +} + +static void DoPATI(const uint8 *cdb) +{ + // "Boundary Gate" uses this command. + // Problems: + // The index fields aren't handled. The ending index wouldn't be too bad, but the starting index would require a bit of work and code uglyfying(to scan for the index), and may be highly + // problematic when Mednafen is used with a physical CD. + int StartTrack = cdb[4]; + int EndTrack = cdb[7]; + //int StartIndex = cdb[5]; + //int EndIndex = cdb[8]; + + if (!StartTrack || StartTrack < toc.first_track || StartTrack > toc.last_track) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + //printf("PATI: %d %d %d SI: %d, EI: %d\n", StartTrack, EndTrack, Cur_CDIF->GetTrackStartPositionLBA(StartTrack), StartIndex, EndIndex); + + DoPABase(toc.tracks[StartTrack].lba, toc.tracks[EndTrack].lba - toc.tracks[StartTrack].lba); +} + +static void DoPATRBase(const uint32 lba, const uint32 length) +{ + if (lba >= toc.tracks[100].lba) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + if (lba < toc.tracks[toc.first_track].lba) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + if (!length) // FIXME to return good status in this case even if no CD is present + { + SendStatusAndMessage(STATUS_GOOD, 0x00); + return; + } + else + { + if (toc.tracks[toc.FindTrackByLBA(lba)].control & 0x04) + { + CommandCCError(SENSEKEY_MEDIUM_ERROR, NSE_NOT_AUDIO_TRACK); + return; + } + + cdda.CDDAReadPos = 588; + read_sec = read_sec_start = lba; + read_sec_end = read_sec_start + length; + + cdda.CDDAStatus = CDDASTATUS_PLAYING; + cdda.PlayMode = PLAYMODE_NORMAL; + } + + SendStatusAndMessage(STATUS_GOOD, 0x00); +} + +/******************************************************** +* * +* SCSI-2 CD Command 0x49 - PLAY AUDIO TRACK * +* RELATIVE(10) * +********************************************************/ +static void DoPATR10(const uint8 *cdb) +{ + const int32 rel_lba = MDFN_de32msb(cdb + 0x2); + const int StartTrack = cdb[6]; + const uint16 length = MDFN_de16msb(cdb + 0x7); + + if (!StartTrack || StartTrack < toc.first_track || StartTrack > toc.last_track) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + DoPATRBase(toc.tracks[StartTrack].lba + rel_lba, length); +} + +/******************************************************** +* * +* SCSI-2 CD Command 0xA9 - PLAY AUDIO TRACK * +* RELATIVE(12) * +********************************************************/ +static void DoPATR12(const uint8 *cdb) +{ + const int32 rel_lba = MDFN_de32msb(cdb + 0x2); + const int StartTrack = cdb[10]; + const uint32 length = MDFN_de32msb(cdb + 0x6); + + if (!StartTrack || StartTrack < toc.first_track || StartTrack > toc.last_track) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + DoPATRBase(toc.tracks[StartTrack].lba + rel_lba, length); +} + +static void DoPAUSERESUME(const uint8 *cdb) +{ + // Pause/resume + // "It shall not be considered an error to request a pause when a pause is already in effect, + // or to request a resume when a play operation is in progress." + + if (cdda.CDDAStatus == CDDASTATUS_STOPPED) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_AUDIO_NOT_PLAYING); + return; + } + + if (cdb[8] & 1) // Resume + cdda.CDDAStatus = CDDASTATUS_PLAYING; + else + cdda.CDDAStatus = CDDASTATUS_PAUSED; + + SendStatusAndMessage(STATUS_GOOD, 0x00); +} + +static void DoREADBase(uint32 sa, uint32 sc) +{ + int track; + + if (sa > toc.tracks[100].lba) // Another one of those off-by-one PC-FX CD bugs. + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME); + return; + } + + if ((track = toc.FindTrackByLBA(sa)) == 0) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME); + return; + } + + if (!(toc.tracks[track].control) & 0x4) + { + CommandCCError(SENSEKEY_MEDIUM_ERROR, NSE_NOT_DATA_TRACK); + return; + } + + // Case for READ(10) and READ(12) where sc == 0, and sa == toc.tracks[100].lba + if (!sc && sa == toc.tracks[100].lba) + { + CommandCCError(SENSEKEY_MEDIUM_ERROR, NSE_HEADER_READ_ERROR); + return; + } + + if (SCSILog) + { + int Track = toc.FindTrackByLBA(sa); + uint32 Offset = sa - toc.tracks[Track].lba; //Cur_CDIF->GetTrackStartPositionLBA(Track); + SCSILog("SCSI", "Read: start=0x%08x(track=%d, offs=0x%08x), cnt=0x%08x", sa, Track, Offset, sc); + } + + SectorAddr = sa; + SectorCount = sc; + if (SectorCount) + { + Cur_CDIF->HintReadSector(sa); //, sa + sc); + + CDReadTimer = (uint64)((WhichSystem == SCSICD_PCE) ? 3 : 1) * 2048 * System_Clock / CD_DATA_TRANSFER_RATE; + } + else + { + CDReadTimer = 0; + SendStatusAndMessage(STATUS_GOOD, 0x00); + } + cdda.CDDAStatus = CDDASTATUS_STOPPED; +} + +/******************************************************** +* * +* SCSI-2 CD Command 0x08 - READ(6) * +* * +********************************************************/ +static void DoREAD6(const uint8 *cdb) +{ + uint32 sa = ((cdb[1] & 0x1F) << 16) | (cdb[2] << 8) | (cdb[3] << 0); + uint32 sc = cdb[4]; + + // TODO: confirm real PCE does this(PC-FX does at least). + if (!sc) + { + //SCSIDBG("READ(6) with count == 0.\n"); + sc = 256; + } + + DoREADBase(sa, sc); +} + +/******************************************************** +* * +* SCSI-2 CD Command 0x28 - READ(10) * +* * +********************************************************/ +static void DoREAD10(const uint8 *cdb) +{ + uint32 sa = MDFN_de32msb(cdb + 0x2); + uint32 sc = MDFN_de16msb(cdb + 0x7); + + DoREADBase(sa, sc); +} + +/******************************************************** +* * +* SCSI-2 CD Command 0xA8 - READ(12) * +* * +********************************************************/ +static void DoREAD12(const uint8 *cdb) +{ + uint32 sa = MDFN_de32msb(cdb + 0x2); + uint32 sc = MDFN_de32msb(cdb + 0x6); + + DoREADBase(sa, sc); +} + +/******************************************************** +* * +* SCSI-2 CD Command 0x34 - PREFETCH(10) * +* * +********************************************************/ +static void DoPREFETCH(const uint8 *cdb) +{ + uint32 lba = MDFN_de32msb(cdb + 0x2); + //uint32 len = MDFN_de16msb(cdb + 0x7); + //bool reladdr = cdb[1] & 0x1; + //bool immed = cdb[1] & 0x2; + + // Note: This command appears to lock up the CD unit to some degree on a real PC-FX if the (lba + len) >= leadout_track_lba, + // more testing is needed if we ever try to fully emulate this command. + if (lba >= toc.tracks[100].lba) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME); + return; + } + + //printf("Prefetch: %08x %08x %d %d %d %d\n", lba, len, link, flag, reladdr, immed); + //SendStatusAndMessage(STATUS_GOOD, 0x00); + SendStatusAndMessage(STATUS_CONDITION_MET, 0x00); +} + +// SEEK functions are mostly just stubs for now, until(if) we emulate seek delays. +static void DoSEEKBase(uint32 lba) +{ + if (lba >= toc.tracks[100].lba) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME); + return; + } + + cdda.CDDAStatus = CDDASTATUS_STOPPED; + SendStatusAndMessage(STATUS_GOOD, 0x00); +} + +/******************************************************** +* * +* SCSI-2 CD Command 0x0B - SEEK(6) * +* * +********************************************************/ +static void DoSEEK6(const uint8 *cdb) +{ + uint32 lba = ((cdb[1] & 0x1F) << 16) | (cdb[2] << 8) | cdb[3]; + + DoSEEKBase(lba); +} + +/******************************************************** +* * +* SCSI-2 CD Command 0x2B - SEEK(10) * +* * +********************************************************/ +static void DoSEEK10(const uint8 *cdb) +{ + uint32 lba = MDFN_de32msb(cdb + 0x2); + + DoSEEKBase(lba); +} + +// 353 +/******************************************************** +* * +* SCSI-2 CD Command 0x42 - READ SUB-CHANNEL(10) * +* * +********************************************************/ +static void DoREADSUBCHANNEL(const uint8 *cdb) +{ + uint8 data_in[8192]; + int DataFormat = cdb[3]; + int TrackNum = cdb[6]; + unsigned AllocSize = (cdb[7] << 8) | cdb[8]; + bool WantQ = cdb[2] & 0x40; + bool WantMSF = cdb[1] & 0x02; + uint32 offset = 0; + + if (!AllocSize) + { + SendStatusAndMessage(STATUS_GOOD, 0x00); + return; + } + + if (DataFormat > 0x3) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + if (DataFormat == 0x3 && (TrackNum < toc.first_track || TrackNum > toc.last_track)) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + data_in[offset++] = 0; + + // FIXME: Is this audio status code correct for scanning playback?? + if (cdda.CDDAStatus == CDDASTATUS_PLAYING || cdda.CDDAStatus == CDDASTATUS_SCANNING) + data_in[offset++] = 0x11; // Audio play operation in progress + else if (cdda.CDDAStatus == CDDASTATUS_PAUSED) + data_in[offset++] = 0x12; // Audio play operation paused + else + data_in[offset++] = 0x13; // 0x13(audio play operation completed successfully) or 0x15(no current audio status to return)? :( + + // Subchannel data length(at data_in[0x2], filled out at the end of the function) + data_in[offset++] = 0x00; + data_in[offset++] = 0x00; + + //printf("42Read SubChannel: %02x %02x %d %d %d\n", DataFormat, TrackNum, AllocSize, WantQ, WantMSF); + if (WantQ) + { + // Sub-channel format code + data_in[offset++] = DataFormat; + if (!DataFormat || DataFormat == 0x01) + { + uint8 *SubQBuf = cd.SubQBuf[QMode_Time]; + + data_in[offset++] = ((SubQBuf[0] & 0x0F) << 4) | ((SubQBuf[0] & 0xF0) >> 4); // Control/adr + data_in[offset++] = SubQBuf[1]; // Track + data_in[offset++] = SubQBuf[2]; // Index + + // Absolute CD-ROM address + if (WantMSF) + { + data_in[offset++] = 0; + data_in[offset++] = BCD_to_U8(SubQBuf[7]); // M + data_in[offset++] = BCD_to_U8(SubQBuf[8]); // S + data_in[offset++] = BCD_to_U8(SubQBuf[9]); // F + } + else + { + uint32 tmp_lba = BCD_to_U8(SubQBuf[7]) * 60 * 75 + BCD_to_U8(SubQBuf[8]) * 75 + BCD_to_U8(SubQBuf[9]) - 150; + + data_in[offset++] = tmp_lba >> 24; + data_in[offset++] = tmp_lba >> 16; + data_in[offset++] = tmp_lba >> 8; + data_in[offset++] = tmp_lba >> 0; + } + + // Relative CD-ROM address + if (WantMSF) + { + data_in[offset++] = 0; + data_in[offset++] = BCD_to_U8(SubQBuf[3]); // M + data_in[offset++] = BCD_to_U8(SubQBuf[4]); // S + data_in[offset++] = BCD_to_U8(SubQBuf[5]); // F + } + else + { + uint32 tmp_lba = BCD_to_U8(SubQBuf[3]) * 60 * 75 + BCD_to_U8(SubQBuf[4]) * 75 + BCD_to_U8(SubQBuf[5]); // Don't subtract 150 in the conversion! + + data_in[offset++] = tmp_lba >> 24; + data_in[offset++] = tmp_lba >> 16; + data_in[offset++] = tmp_lba >> 8; + data_in[offset++] = tmp_lba >> 0; + } + } + + if (!DataFormat || DataFormat == 0x02) + { + if (DataFormat == 0x02) + { + data_in[offset++] = 0x00; + data_in[offset++] = 0x00; + data_in[offset++] = 0x00; + } + data_in[offset++] = 0x00; // MCVal and reserved. + for (int i = 0; i < 15; i++) + data_in[offset++] = 0x00; + } + + // Track ISRC + if (!DataFormat || DataFormat == 0x03) + { + if (DataFormat == 0x03) + { + uint8 *SubQBuf = cd.SubQBuf[QMode_Time]; // FIXME + data_in[offset++] = ((SubQBuf[0] & 0x0F) << 4) | ((SubQBuf[0] & 0xF0) >> 4); // Control/adr + data_in[offset++] = TrackNum; // From sub Q or from parameter? + data_in[offset++] = 0x00; // Reserved. + } + data_in[offset++] = 0x00; // TCVal and reserved + for (int i = 0; i < 15; i++) + data_in[offset++] = 0x00; + } + } + + MDFN_en16msb(&data_in[0x2], offset - 0x4); + + DoSimpleDataIn(data_in, (AllocSize > offset) ? offset : AllocSize); +} + +/******************************************************** +* * +* PC-FX CD Command 0xDD - READ SUB Q * +* * +********************************************************/ +static void DoNEC_READSUBQ(const uint8 *cdb) +{ + uint8 *SubQBuf = cd.SubQBuf[QMode_Time]; + uint8 data_in[10]; + const uint8 alloc_size = (cdb[1] < 10) ? cdb[1] : 10; + + memset(data_in, 0x00, 10); + + if (cdda.CDDAStatus == CDDASTATUS_PAUSED) + data_in[0] = 2; // Pause + else if (cdda.CDDAStatus == CDDASTATUS_PLAYING || cdda.CDDAStatus == CDDASTATUS_SCANNING) // FIXME: Is this the correct status code for scanning playback? + data_in[0] = 0; // Playing + else + data_in[0] = 3; // Stopped + + data_in[1] = SubQBuf[0]; // Control/adr + data_in[2] = SubQBuf[1]; // Track + data_in[3] = SubQBuf[2]; // Index + data_in[4] = SubQBuf[3]; // M(rel) + data_in[5] = SubQBuf[4]; // S(rel) + data_in[6] = SubQBuf[5]; // F(rel) + data_in[7] = SubQBuf[7]; // M(abs) + data_in[8] = SubQBuf[8]; // S(abs) + data_in[9] = SubQBuf[9]; // F(abs) + + DoSimpleDataIn(data_in, alloc_size); +} + +static void DoTESTUNITREADY(const uint8 *cdb) +{ + SendStatusAndMessage(STATUS_GOOD, 0x00); +} + +static void DoNEC_PAUSE(const uint8 *cdb) +{ + if (cdda.CDDAStatus != CDDASTATUS_STOPPED) // Hmm, should we give an error if it tries to pause and it's already paused? + { + cdda.CDDAStatus = CDDASTATUS_PAUSED; + SendStatusAndMessage(STATUS_GOOD, 0x00); + } + else // Definitely give an error if it tries to pause when no track is playing! + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_AUDIO_NOT_PLAYING); + } +} + +static void DoNEC_SCAN(const uint8 *cdb) +{ + uint32 sector_tmp = 0; + + // 0: 0xD2 + // 1: 0x03 = reverse scan, 0x02 = forward scan + // 2: End M + // 3: End S + // 4: End F + + switch (cdb[9] & 0xc0) + { + default: + //SCSIDBG("Unknown NECSCAN format"); + break; + + case 0x00: + sector_tmp = (cdb[3] << 16) | (cdb[4] << 8) | cdb[5]; + break; + + case 0x40: + sector_tmp = AMSF_to_LBA(BCD_to_U8(cdb[2]), BCD_to_U8(cdb[3]), BCD_to_U8(cdb[4])); + break; + + case 0x80: // FIXME: error on invalid track number??? + sector_tmp = toc.tracks[BCD_to_U8(cdb[2])].lba; + break; + } + + cdda.ScanMode = cdb[1] & 0x3; + cdda.scan_sec_end = sector_tmp; + + if (cdda.CDDAStatus != CDDASTATUS_STOPPED) + { + if (cdda.ScanMode) + { + cdda.CDDAStatus = CDDASTATUS_SCANNING; + } + } + SendStatusAndMessage(STATUS_GOOD, 0x00); +} + +/******************************************************** +* * +* SCSI-2 CD Command 0x1E - PREVENT/ALLOW MEDIUM * +* REMOVAL * +********************************************************/ +static void DoPREVENTALLOWREMOVAL(const uint8 *cdb) +{ + //bool prevent = cdb[4] & 0x01; + //const int logical_unit = cdb[1] >> 5; + //SCSIDBG("PREVENT ALLOW MEDIUM REMOVAL: %d for %d\n", cdb[4] & 0x1, logical_unit); + //SendStatusAndMessage(STATUS_GOOD, 0x00); + + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_REQUEST_IN_CDB); +} + +// +// +// +#include "scsicd-pce-commands.inc" + +#define SCF_REQUIRES_MEDIUM 0x0001 +#define SCF_INCOMPLETE 0x4000 +#define SCF_UNTESTED 0x8000 + +typedef struct +{ + uint8 cmd; + uint32 flags; + void (*func)(const uint8 *cdb); + const char *pretty_name; + const char *format_string; +} SCSICH; + +static const int32 RequiredCDBLen[16] = + { + 6, // 0x0n + 6, // 0x1n + 10, // 0x2n + 10, // 0x3n + 10, // 0x4n + 10, // 0x5n + 10, // 0x6n + 10, // 0x7n + 10, // 0x8n + 10, // 0x9n + 12, // 0xAn + 12, // 0xBn + 10, // 0xCn + 10, // 0xDn + 10, // 0xEn + 10, // 0xFn +}; + +static SCSICH PCFXCommandDefs[] = + { + {0x00, SCF_REQUIRES_MEDIUM, DoTESTUNITREADY, "Test Unit Ready"}, + {0x01, 0 /* ? */, DoREZEROUNIT, "Rezero Unit"}, + {0x03, 0, DoREQUESTSENSE, "Request Sense"}, + {0x08, SCF_REQUIRES_MEDIUM, DoREAD6, "Read(6)"}, + {0x0B, SCF_REQUIRES_MEDIUM, DoSEEK6, "Seek(6)"}, + {0x0D, 0, DoNEC_NOP, "No Operation"}, + {0x12, 0, DoINQUIRY, "Inquiry"}, + {0x15, 0, DoMODESELECT6, "Mode Select(6)"}, + // TODO: { 0x16, 0 /* ? */, DoRESERVE, "Reserve" }, // 9.2.12 + // TODO: { 0x17, 0 /* ? */, DoRELEASE, "Release" }, // 9.2.11 + {0x1A, 0, DoMODESENSE6, "Mode Sense(6)"}, + {0x1B, SCF_REQUIRES_MEDIUM, DoSTARTSTOPUNIT6, "Start/Stop Unit"}, // 9.2.17 + // TODO: { 0x1D, , DoSENDDIAG, "Send Diagnostic" }, // 8.2.15 + {0x1E, 0, DoPREVENTALLOWREMOVAL, "Prevent/Allow Media Removal"}, + + {0x25, SCF_REQUIRES_MEDIUM, DoREADCDCAP10, "Read CD-ROM Capacity"}, // 14.2.8 + {0x28, SCF_REQUIRES_MEDIUM, DoREAD10, "Read(10)"}, + {0x2B, SCF_REQUIRES_MEDIUM, DoSEEK10, "Seek(10)"}, + + // TODO: { 0x2F, SCF_REQUIRES_MEDIUM, DoVERIFY10, "Verify(10)" }, // 16.2.11 + + {0x34, SCF_REQUIRES_MEDIUM, DoPREFETCH, "Prefetch"}, + // TODO: { 0x3B, 0, 10, DoWRITEBUFFER, "Write Buffer" }, // 8.2.17 + // TODO: { 0x3C, 0, 10, DoREADBUFFER, "Read Buffer" }, // 8.2.12 + + {0x42, SCF_REQUIRES_MEDIUM, DoREADSUBCHANNEL, "Read Subchannel"}, + {0x43, SCF_REQUIRES_MEDIUM, DoREADTOC, "Read TOC"}, + {0x44, SCF_REQUIRES_MEDIUM, DoREADHEADER10, "Read Header"}, + + {0x45, SCF_REQUIRES_MEDIUM, DoPA10, "Play Audio(10)"}, + {0x47, SCF_REQUIRES_MEDIUM, DoPAMSF, "Play Audio MSF"}, + {0x48, SCF_REQUIRES_MEDIUM, DoPATI, "Play Audio Track Index"}, + {0x49, SCF_REQUIRES_MEDIUM, DoPATR10, "Play Audio Track Relative(10)"}, + {0x4B, SCF_REQUIRES_MEDIUM, DoPAUSERESUME, "Pause/Resume"}, + + {0xA5, SCF_REQUIRES_MEDIUM, DoPA12, "Play Audio(12)"}, + {0xA8, SCF_REQUIRES_MEDIUM, DoREAD12, "Read(12)"}, + {0xA9, SCF_REQUIRES_MEDIUM, DoPATR12, "Play Audio Track Relative(12)"}, + + // TODO: { 0xAF, SCF_REQUIRES_MEDIUM, DoVERIFY12, "Verify(12)" }, // 16.2.12 + + {0xD2, SCF_REQUIRES_MEDIUM, DoNEC_SCAN, "Scan"}, + {0xD8, SCF_REQUIRES_MEDIUM, DoNEC_SAPSP, "Set Audio Playback Start Position"}, // "Audio track search" + {0xD9, SCF_REQUIRES_MEDIUM, DoNEC_SAPEP, "Set Audio Playback End Position"}, // "Play" + {0xDA, SCF_REQUIRES_MEDIUM, DoNEC_PAUSE, "Pause"}, // "Still" + {0xDB, SCF_REQUIRES_MEDIUM | SCF_UNTESTED, DoNEC_SST, "Set Stop Time"}, + {0xDC, SCF_REQUIRES_MEDIUM, DoNEC_EJECT, "Eject"}, + {0xDD, SCF_REQUIRES_MEDIUM, DoNEC_READSUBQ, "Read Subchannel Q"}, + {0xDE, SCF_REQUIRES_MEDIUM, DoNEC_GETDIRINFO, "Get Dir Info"}, + + {0xFF, 0, 0, NULL, NULL}, +}; + +static SCSICH PCECommandDefs[] = + { + {0x00, SCF_REQUIRES_MEDIUM, DoTESTUNITREADY, "Test Unit Ready"}, + {0x03, 0, DoREQUESTSENSE, "Request Sense"}, + {0x08, SCF_REQUIRES_MEDIUM, DoREAD6, "Read(6)"}, + //{ 0x15, DoMODESELECT6, "Mode Select(6)" }, + {0xD8, SCF_REQUIRES_MEDIUM, DoNEC_PCE_SAPSP, "Set Audio Playback Start Position"}, + {0xD9, SCF_REQUIRES_MEDIUM, DoNEC_PCE_SAPEP, "Set Audio Playback End Position"}, + {0xDA, SCF_REQUIRES_MEDIUM, DoNEC_PCE_PAUSE, "Pause"}, + {0xDD, SCF_REQUIRES_MEDIUM, DoNEC_PCE_READSUBQ, "Read Subchannel Q"}, + {0xDE, SCF_REQUIRES_MEDIUM, DoNEC_PCE_GETDIRINFO, "Get Dir Info"}, + + {0xFF, 0, 0, NULL, NULL}, +}; + +void SCSICD_ResetTS(uint32 ts_base) +{ + lastts = ts_base; +} + +void SCSICD_GetCDDAValues(int16 &left, int16 &right) +{ + if (cdda.CDDAStatus) + { + left = cdda.sr[0]; + right = cdda.sr[1]; + } + else + left = right = 0; +} + +#define CDDA_FILTER_NUMCONVOLUTIONS 7 +#define CDDA_FILTER_NUMCONVOLUTIONS_PADDED 8 + +#define CDDA_FILTER_NUMPHASES_SHIFT 6 +#define CDDA_FILTER_NUMPHASES (1 << CDDA_FILTER_NUMPHASES_SHIFT) + +alignas(16) static const int16 CDDA_Filter[1 + CDDA_FILTER_NUMPHASES + 1][CDDA_FILTER_NUMCONVOLUTIONS_PADDED] = + { +#include "scsicd_cdda_filter.inc" +}; + +alignas(16) static const int16 OversampleFilter[2][0x10] = + { + { + -82, 217, -463, 877, -1562, 2783, -5661, 29464, 9724, -3844, 2074, -1176, 645, -323, 138, -43, + }, /* sum=32768, sum_abs=59076 */ + { + -43, 138, -323, 645, -1176, 2074, -3844, 9724, 29464, -5661, 2783, -1562, 877, -463, 217, -82, + }, /* sum=32768, sum_abs=59076 */ +}; + +static INLINE void RunCDDA(uint32 system_timestamp, int32 run_time) +{ + if (cdda.CDDAStatus == CDDASTATUS_PLAYING || cdda.CDDAStatus == CDDASTATUS_SCANNING) + { + cdda.CDDADiv -= (int64)run_time << 20; + + while (cdda.CDDADiv <= 0) + { + const uint32 synthtime_ex = (((uint64)system_timestamp << 20) + (int64)cdda.CDDADiv) / cdda.CDDATimeDiv; + const int synthtime = (synthtime_ex >> 16) & 0xFFFF; // & 0xFFFF(or equivalent) to prevent overflowing HRBufs[] + const int synthtime_phase = (int)(synthtime_ex & 0xFFFF) - 0x80; + const int synthtime_phase_int = synthtime_phase >> (16 - CDDA_FILTER_NUMPHASES_SHIFT); + const int synthtime_phase_fract = synthtime_phase & ((1 << (16 - CDDA_FILTER_NUMPHASES_SHIFT)) - 1); + int32 sample_va[2]; + + cdda.CDDADiv += cdda.CDDADivAcc; + + if (!(cdda.OversamplePos & 1)) + { + if (cdda.CDDAReadPos == 588) + { + if (read_sec >= read_sec_end || (cdda.CDDAStatus == CDDASTATUS_SCANNING && read_sec == cdda.scan_sec_end)) + { + switch (cdda.PlayMode) + { + case PLAYMODE_SILENT: + case PLAYMODE_NORMAL: + cdda.CDDAStatus = CDDASTATUS_STOPPED; + break; + + case PLAYMODE_INTERRUPT: + cdda.CDDAStatus = CDDASTATUS_STOPPED; + CDIRQCallback(SCSICD_IRQ_DATA_TRANSFER_DONE); + break; + + case PLAYMODE_LOOP: + read_sec = read_sec_start; + break; + } + + // If CDDA playback is stopped, break out of our while(CDDADiv ...) loop and don't play any more sound! + if (cdda.CDDAStatus == CDDASTATUS_STOPPED) + break; + } + + // Don't play past the user area of the disc. + if (read_sec >= toc.tracks[100].lba) + { + cdda.CDDAStatus = CDDASTATUS_STOPPED; + break; + } + + if (TrayOpen || !Cur_CDIF) + { + cdda.CDDAStatus = CDDASTATUS_STOPPED; + +#if 0 + cd.data_transfer_done = FALSE; + cd.key_pending = SENSEKEY_NOT_READY; + cd.asc_pending = ASC_MEDIUM_NOT_PRESENT; + cd.ascq_pending = 0x00; + cd.fru_pending = 0x00; + SendStatusAndMessage(STATUS_CHECK_CONDITION, 0x00); +#endif + + break; + } + + cdda.CDDAReadPos = 0; + + { + uint8 tmpbuf[2352 + 96]; + + Cur_CDIF->ReadRawSector(tmpbuf, read_sec); //, read_sec_end, read_sec_start); + + for (int i = 0; i < 588 * 2; i++) + cdda.CDDASectorBuffer[i] = MDFN_de16lsb(&tmpbuf[i * 2]); + + memcpy(cd.SubPWBuf, tmpbuf + 2352, 96); + } + GenSubQFromSubPW(); + + if (!(cd.SubQBuf_Last[0] & 0x10)) + { + // Not using de-emphasis, so clear the de-emphasis filter state. + memset(cdda.DeemphState, 0, sizeof(cdda.DeemphState)); + } + + if (cdda.CDDAStatus == CDDASTATUS_SCANNING) + { + int64 tmp_read_sec = read_sec; + + if (cdda.ScanMode & 1) + { + tmp_read_sec -= 24; + if (tmp_read_sec < cdda.scan_sec_end) + tmp_read_sec = cdda.scan_sec_end; + } + else + { + tmp_read_sec += 24; + if (tmp_read_sec > cdda.scan_sec_end) + tmp_read_sec = cdda.scan_sec_end; + } + read_sec = tmp_read_sec; + } + else + read_sec++; + } // End if(CDDAReadPos == 588) + + if (!(cdda.CDDAReadPos % 6)) + { + int subindex = cdda.CDDAReadPos / 6 - 2; + + if (subindex >= 0) + CDStuffSubchannels(cd.SubPWBuf[subindex], subindex); + else // The system-specific emulation code should handle what value the sync bytes are. + CDStuffSubchannels(0x00, subindex); + } + + // If the last valid sub-Q data decoded indicate that the corresponding sector is a data sector, don't output the + // current sector as audio. + if (!(cd.SubQBuf_Last[0] & 0x40) && cdda.PlayMode != PLAYMODE_SILENT) + { + cdda.sr[0] = cdda.CDDASectorBuffer[cdda.CDDAReadPos * 2 + cdda.OutPortChSelectCache[0]]; + cdda.sr[1] = cdda.CDDASectorBuffer[cdda.CDDAReadPos * 2 + cdda.OutPortChSelectCache[1]]; + } + +#if 0 + { + static int16 wv = 0x7FFF; //0x5000; + static unsigned counter = 0; + static double phase = 0; + static double phase_inc = 0; + static const double phase_inc_inc = 0.000003 / 2; + + cdda.sr[0] = 32767 * sin(phase); + cdda.sr[1] = 32767 * sin(phase); + + //cdda.sr[0] = wv; + //cdda.sr[1] = wv; + + if(counter == 0) + wv = -wv; + counter = (counter + 1) & 1; + phase += phase_inc; + phase_inc += phase_inc_inc; + } +#endif + + { + const unsigned obwp = cdda.OversamplePos >> 1; + cdda.OversampleBuffer[0][obwp] = cdda.OversampleBuffer[0][0x10 + obwp] = cdda.sr[0]; + cdda.OversampleBuffer[1][obwp] = cdda.OversampleBuffer[1][0x10 + obwp] = cdda.sr[1]; + } + + cdda.CDDAReadPos++; + } // End if(!(cdda.OversamplePos & 1)) + + { + const int16 *f = OversampleFilter[cdda.OversamplePos & 1]; +#if defined(__SSE2__) + __m128i f0 = _mm_load_si128((__m128i *)&f[0]); + __m128i f1 = _mm_load_si128((__m128i *)&f[8]); +#endif + + for (unsigned lr = 0; lr < 2; lr++) + { + const int16 *b = &cdda.OversampleBuffer[lr][((cdda.OversamplePos >> 1) + 1) & 0xF]; +#if defined(__SSE2__) + union { + int32 accum; + float accum_f; + //__m128i accum_m128; + }; + + { + __m128i b0; + __m128i b1; + __m128i sum; + + b0 = _mm_loadu_si128((__m128i *)&b[0]); + b1 = _mm_loadu_si128((__m128i *)&b[8]); + + sum = _mm_add_epi32(_mm_madd_epi16(f0, b0), _mm_madd_epi16(f1, b1)); + sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, (3 << 0) | (2 << 2) | (1 << 4) | (0 << 6))); + sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, (1 << 0) | (0 << 2) | (3 << 4) | (2 << 6))); + _mm_store_ss(&accum_f, (__m128)sum); + //_mm_store_si128(&accum_m128, sum); + } +#else + int32 accum = 0; + + for (unsigned i = 0; i < 0x10; i++) + accum += f[i] * b[i]; +#endif + // sum_abs * cdda_min = + // 59076 * -32768 = -1935802368 + // OPVC can have a maximum value of 65536. + // -1935802368 * 65536 = -126864743989248 + // + // -126864743989248 / 65536 = -1935802368 + sample_va[lr] = ((int64)accum * cdda.OutPortVolumeCache[lr]) >> 16; + // Output of this stage will be (approximate max ranges) -2147450880 through 2147385345. + } + } + + // + // This de-emphasis filter's frequency response isn't totally correct, but it's much better than nothing(and it's not like any known PCE CD/TG16 CD/PC-FX games + // utilize pre-emphasis anyway). + // + if (MDFN_UNLIKELY(cd.SubQBuf_Last[0] & 0x10)) + { + //puts("Deemph"); + for (unsigned lr = 0; lr < 2; lr++) + { + float inv = sample_va[lr] * 0.35971507338824012f; + + cdda.DeemphState[lr][1] = (cdda.DeemphState[lr][0] - 0.4316395666f * inv) + (0.7955522347f * cdda.DeemphState[lr][1]); + cdda.DeemphState[lr][0] = inv; + + sample_va[lr] = std::max(-2147483648.0, std::min(2147483647.0, cdda.DeemphState[lr][1])); + //printf("%u: %f, %d\n", lr, cdda.DeemphState[lr][1], sample_va[lr]); + } + } + + if (HRBufs[0] && HRBufs[1]) + { +// +// FINAL_OUT_SHIFT should be 32 so we can take advantage of 32x32->64 multipliers on 32-bit CPUs. +// +#define FINAL_OUT_SHIFT 32 +#define MULT_SHIFT_ADJ (32 - (26 + (8 - CDDA_FILTER_NUMPHASES_SHIFT))) + +#if (((1 << (16 - CDDA_FILTER_NUMPHASES_SHIFT)) - 0) << MULT_SHIFT_ADJ) > 32767 +#error "COEFF MULT OVERFLOW" +#endif + + const int16 mult_a = ((1 << (16 - CDDA_FILTER_NUMPHASES_SHIFT)) - synthtime_phase_fract) << MULT_SHIFT_ADJ; + const int16 mult_b = synthtime_phase_fract << MULT_SHIFT_ADJ; + int32 coeff[CDDA_FILTER_NUMCONVOLUTIONS]; + + //if(synthtime_phase_fract == 0) + // printf("%5d: %d %d\n", synthtime_phase_fract, mult_a, mult_b); + + for (unsigned c = 0; c < CDDA_FILTER_NUMCONVOLUTIONS; c++) + { + coeff[c] = (CDDA_Filter[1 + synthtime_phase_int + 0][c] * mult_a + + CDDA_Filter[1 + synthtime_phase_int + 1][c] * mult_b); + } + + int32 *tb0 = &HRBufs[0][synthtime]; + int32 *tb1 = &HRBufs[1][synthtime]; + + for (unsigned c = 0; c < CDDA_FILTER_NUMCONVOLUTIONS; c++) + { + tb0[c] += ((int64)coeff[c] * sample_va[0]) >> FINAL_OUT_SHIFT; + tb1[c] += ((int64)coeff[c] * sample_va[1]) >> FINAL_OUT_SHIFT; + } +#undef FINAL_OUT_SHIFT +#undef MULT_SHIFT_ADJ + } + + cdda.OversamplePos = (cdda.OversamplePos + 1) & 0x1F; + } // end while(cdda.CDDADiv <= 0) + } +} + +static INLINE void RunCDRead(uint32 system_timestamp, int32 run_time) +{ + if (CDReadTimer > 0) + { + CDReadTimer -= run_time; + + if (CDReadTimer <= 0) + { + if (din->CanWrite() < ((WhichSystem == SCSICD_PCFX) ? 2352 : 2048)) // +96 if we find out the PC-FX can read subchannel data along with raw data too. ;) + { + //printf("Carp: %d %d %d\n", din->CanWrite(), SectorCount, CDReadTimer); + //CDReadTimer = (cd.data_in_size - cd.data_in_pos) * 10; + + CDReadTimer += (uint64)1 * 2048 * System_Clock / CD_DATA_TRANSFER_RATE; + + //CDReadTimer += (uint64) 1 * 128 * System_Clock / CD_DATA_TRANSFER_RATE; + } + else + { + uint8 tmp_read_buf[2352 + 96]; + + if (TrayOpen) + { + din->Flush(); + cd.data_transfer_done = FALSE; + + CommandCCError(SENSEKEY_NOT_READY, NSE_TRAY_OPEN); + } + else if (!Cur_CDIF) + { + CommandCCError(SENSEKEY_NOT_READY, NSE_NO_DISC); + } + else if (SectorAddr >= toc.tracks[100].lba) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME); + } + else if (!Cur_CDIF->ReadRawSector(tmp_read_buf, SectorAddr)) //, SectorAddr + SectorCount)) + { + cd.data_transfer_done = FALSE; + + CommandCCError(SENSEKEY_ILLEGAL_REQUEST); + } + else if (ValidateRawDataSector(tmp_read_buf, SectorAddr)) + { + memcpy(cd.SubPWBuf, tmp_read_buf + 2352, 96); + + if (tmp_read_buf[12 + 3] == 0x2) + din->Write(tmp_read_buf + 24, 2048); + else + din->Write(tmp_read_buf + 16, 2048); + + GenSubQFromSubPW(); + + CDIRQCallback(SCSICD_IRQ_DATA_TRANSFER_READY); + + SectorAddr++; + SectorCount--; + + if (CurrentPhase != PHASE_DATA_IN) + ChangePhase(PHASE_DATA_IN); + + if (SectorCount) + { + cd.data_transfer_done = FALSE; + CDReadTimer += (uint64)1 * 2048 * System_Clock / CD_DATA_TRANSFER_RATE; + } + else + { + cd.data_transfer_done = TRUE; + } + } + } // end else to if(!Cur_CDIF->ReadSector + } + } +} + +uint32 SCSICD_Run(scsicd_timestamp_t system_timestamp) +{ + int32 run_time = system_timestamp - lastts; + + if (system_timestamp < lastts) + { + fprintf(stderr, "Meow: %d %d\n", system_timestamp, lastts); + assert(system_timestamp >= lastts); + } + + monotonic_timestamp += run_time; + + lastts = system_timestamp; + + RunCDRead(system_timestamp, run_time); + RunCDDA(system_timestamp, run_time); + + bool ResetNeeded = false; + + if (RST_signal && !cd.last_RST_signal) + ResetNeeded = true; + + cd.last_RST_signal = RST_signal; + + if (ResetNeeded) + { + //puts("RST"); + VirtualReset(); + } + else if (CurrentPhase == PHASE_BUS_FREE) + { + if (SEL_signal) + { + if (WhichSystem == SCSICD_PCFX) + { + //if(cd_bus.DB == 0x84) + { + ChangePhase(PHASE_COMMAND); + } + } + else // PCE + { + ChangePhase(PHASE_COMMAND); + } + } + } + else if (ATN_signal && !REQ_signal && !ACK_signal) + { + //printf("Yay: %d %d\n", REQ_signal, ACK_signal); + ChangePhase(PHASE_MESSAGE_OUT); + } + else + switch (CurrentPhase) + { + case PHASE_COMMAND: + if (REQ_signal && ACK_signal) // Data bus is valid nowww + { + //printf("Command Phase Byte I->T: %02x, %d\n", cd_bus.DB, cd.command_buffer_pos); + cd.command_buffer[cd.command_buffer_pos++] = cd_bus.DB; + SetREQ(FALSE); + } + + if (!REQ_signal && !ACK_signal && cd.command_buffer_pos) // Received at least one byte, what should we do? + { + if (cd.command_buffer_pos == RequiredCDBLen[cd.command_buffer[0] >> 4]) + { + const SCSICH *cmd_info_ptr; + + if (WhichSystem == SCSICD_PCFX) + cmd_info_ptr = PCFXCommandDefs; + else + cmd_info_ptr = PCECommandDefs; + + while (cmd_info_ptr->pretty_name && cmd_info_ptr->cmd != cd.command_buffer[0]) + cmd_info_ptr++; + + if (SCSILog) + { + char log_buffer[1024]; + int lb_pos; + + log_buffer[0] = 0; + + lb_pos = trio_snprintf(log_buffer, 1024, "Command: %02x, %s%s ", cd.command_buffer[0], cmd_info_ptr->pretty_name ? cmd_info_ptr->pretty_name : "!!BAD COMMAND!!", + (cmd_info_ptr->flags & SCF_UNTESTED) ? "(UNTESTED)" : ""); + + for (int i = 0; i < RequiredCDBLen[cd.command_buffer[0] >> 4]; i++) + lb_pos += trio_snprintf(log_buffer + lb_pos, 1024 - lb_pos, "%02x ", cd.command_buffer[i]); + + SCSILog("SCSI", "%s", log_buffer); + //puts(log_buffer); + } + + if (cmd_info_ptr->pretty_name == NULL) // Command not found! + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_COMMAND); + + //SCSIDBG("Bad Command: %02x\n", cd.command_buffer[0]); + + if (SCSILog) + SCSILog("SCSI", "Bad Command: %02x", cd.command_buffer[0]); + + cd.command_buffer_pos = 0; + } + else + { + if (cmd_info_ptr->flags & SCF_UNTESTED) + { + //SCSIDBG("Untested SCSI command: %02x, %s", cd.command_buffer[0], cmd_info_ptr->pretty_name); + } + + if (TrayOpen && (cmd_info_ptr->flags & SCF_REQUIRES_MEDIUM)) + { + CommandCCError(SENSEKEY_NOT_READY, NSE_TRAY_OPEN); + } + else if (!Cur_CDIF && (cmd_info_ptr->flags & SCF_REQUIRES_MEDIUM)) + { + CommandCCError(SENSEKEY_NOT_READY, NSE_NO_DISC); + } + else if (cd.DiscChanged && (cmd_info_ptr->flags & SCF_REQUIRES_MEDIUM)) + { + CommandCCError(SENSEKEY_UNIT_ATTENTION, NSE_DISC_CHANGED); + cd.DiscChanged = false; + } + else + { + bool prev_ps = (cdda.CDDAStatus == CDDASTATUS_PLAYING || cdda.CDDAStatus == CDDASTATUS_SCANNING); + + cmd_info_ptr->func(cd.command_buffer); + + bool new_ps = (cdda.CDDAStatus == CDDASTATUS_PLAYING || cdda.CDDAStatus == CDDASTATUS_SCANNING); + + // A bit kludgey, but ehhhh. + if (!prev_ps && new_ps) + { + memset(cdda.sr, 0, sizeof(cdda.sr)); + memset(cdda.OversampleBuffer, 0, sizeof(cdda.OversampleBuffer)); + memset(cdda.DeemphState, 0, sizeof(cdda.DeemphState)); + //printf("CLEAR BUFFERS LALALA\n"); + } + } + + cd.command_buffer_pos = 0; + } + } // end if(cd.command_buffer_pos == RequiredCDBLen[cd.command_buffer[0] >> 4]) + else // Otherwise, get more data for the command! + SetREQ(TRUE); + } + break; + + case PHASE_DATA_OUT: + if (REQ_signal && ACK_signal) // Data bus is valid nowww + { + //printf("DATAOUT-SCSIIN: %d %02x\n", cd.data_out_pos, cd_bus.DB); + cd.data_out[cd.data_out_pos++] = cd_bus.DB; + SetREQ(FALSE); + } + else if (!REQ_signal && !ACK_signal && cd.data_out_pos) + { + if (cd.data_out_pos == cd.data_out_want) + { + cd.data_out_pos = 0; + + if (cd.command_buffer[0] == 0x15) + FinishMODESELECT6(cd.data_out, cd.data_out_want); + else // Error out here? It shouldn't be reached: + SendStatusAndMessage(STATUS_GOOD, 0x00); + } + else + SetREQ(TRUE); + } + break; + + case PHASE_MESSAGE_OUT: + //printf("%d %d, %02x\n", REQ_signal, ACK_signal, cd_bus.DB); + if (REQ_signal && ACK_signal) + { + SetREQ(FALSE); + + // ABORT message is 0x06, but the code isn't set up to be able to recover from a MESSAGE OUT phase back to the previous phase, so we treat any message as an ABORT. + // Real tests are needed on the PC-FX to determine its behavior. + // (Previously, ATN emulation was a bit broken, which resulted in the wrong data on the data bus in this code path in at least "Battle Heat", but it's fixed now and 0x06 is on the data bus). + //if(cd_bus.DB == 0x6) // ABORT message! + if (1) + { + //printf("[SCSICD] Abort Received(DB=0x%02x)\n", cd_bus.DB); + din->Flush(); + cd.data_out_pos = cd.data_out_want = 0; + + CDReadTimer = 0; + cdda.CDDAStatus = CDDASTATUS_STOPPED; + ChangePhase(PHASE_BUS_FREE); + } + //else + // printf("[SCSICD] Message to target: 0x%02x\n", cd_bus.DB); + } + break; + + case PHASE_STATUS: + if (REQ_signal && ACK_signal) + { + SetREQ(FALSE); + cd.status_sent = TRUE; + } + + if (!REQ_signal && !ACK_signal && cd.status_sent) + { + // Status sent, so get ready to send the message! + cd.status_sent = FALSE; + cd_bus.DB = cd.message_pending; + + ChangePhase(PHASE_MESSAGE_IN); + } + break; + + case PHASE_DATA_IN: + if (!REQ_signal && !ACK_signal) + { + //puts("REQ and ACK false"); + if (din->CanRead() == 0) // aaand we're done! + { + CDIRQCallback(0x8000 | SCSICD_IRQ_DATA_TRANSFER_READY); + + if (cd.data_transfer_done) + { + SendStatusAndMessage(STATUS_GOOD, 0x00); + cd.data_transfer_done = FALSE; + CDIRQCallback(SCSICD_IRQ_DATA_TRANSFER_DONE); + } + } + else + { + cd_bus.DB = din->ReadByte(); + SetREQ(TRUE); + } + } + if (REQ_signal && ACK_signal) + { + //puts("REQ and ACK true"); + SetREQ(FALSE); + } + break; + + case PHASE_MESSAGE_IN: + if (REQ_signal && ACK_signal) + { + SetREQ(FALSE); + cd.message_sent = TRUE; + } + + if (!REQ_signal && !ACK_signal && cd.message_sent) + { + cd.message_sent = FALSE; + ChangePhase(PHASE_BUS_FREE); + } + break; + } + + int32 next_time = 0x7fffffff; + + if (CDReadTimer > 0 && CDReadTimer < next_time) + next_time = CDReadTimer; + + if (cdda.CDDAStatus == CDDASTATUS_PLAYING || cdda.CDDAStatus == CDDASTATUS_SCANNING) + { + int32 cdda_div_sexytime = (cdda.CDDADiv + (cdda.CDDADivAcc * (cdda.OversamplePos & 1)) + ((1 << 20) - 1)) >> 20; + if (cdda_div_sexytime > 0 && cdda_div_sexytime < next_time) + next_time = cdda_div_sexytime; + } + + assert(next_time >= 0); + + return (next_time); +} + +void SCSICD_SetLog(void (*logfunc)(const char *, const char *, ...)) +{ + SCSILog = logfunc; +} + +void SCSICD_SetTransferRate(uint32 TransferRate) +{ + CD_DATA_TRANSFER_RATE = TransferRate; +} + +void SCSICD_Init(int type, int cdda_time_div, int32 *left_hrbuf, int32 *right_hrbuf, uint32 TransferRate, uint32 SystemClock, void (*IRQFunc)(int), void (*SSCFunc)(uint8, int)) +{ + Cur_CDIF = NULL; + TrayOpen = true; + + assert(SystemClock < 30000000); // 30 million, sanity check. + + monotonic_timestamp = 0; + lastts = 0; + + SCSILog = NULL; + + if (type == SCSICD_PCFX) + din = new SimpleFIFO(65536); //4096); + else + din = new SimpleFIFO(2048); //8192); //1024); /2048); + + WhichSystem = type; + + cdda.CDDADivAcc = (int64)System_Clock * (1024 * 1024) / 88200; + cdda.CDDADivAccVolFudge = 100; + cdda.CDDATimeDiv = cdda_time_div * (1 << (4 + 2)); + + cdda.CDDAVolume[0] = 65536; + cdda.CDDAVolume[1] = 65536; + + FixOPV(); + + HRBufs[0] = left_hrbuf; + HRBufs[1] = right_hrbuf; + + CD_DATA_TRANSFER_RATE = TransferRate; + System_Clock = SystemClock; + CDIRQCallback = IRQFunc; + CDStuffSubchannels = SSCFunc; +} + +void SCSICD_SetCDDAVolume(double left, double right) +{ + cdda.CDDAVolume[0] = 65536 * left; + cdda.CDDAVolume[1] = 65536 * right; + + for (int i = 0; i < 2; i++) + { + if (cdda.CDDAVolume[i] > 65536) + { + printf("[SCSICD] Debug Warning: CD-DA volume %d too large: %d\n", i, cdda.CDDAVolume[i]); + cdda.CDDAVolume[i] = 65536; + } + } + + FixOPV(); +} diff --git a/waterbox/pcfx/cdrom/scsicd.h b/waterbox/pcfx/cdrom/scsicd.h new file mode 100644 index 0000000000..fe168da6e9 --- /dev/null +++ b/waterbox/pcfx/cdrom/scsicd.h @@ -0,0 +1,97 @@ +#ifndef __PCFX_SCSICD_H +#define __PCFX_SCSICD_H + +typedef int32 scsicd_timestamp_t; + +typedef struct +{ + // Data bus(FIXME: we should have a variable for the target and the initiator, and OR them together to be truly accurate). + uint8 DB; + + uint32 signals; + + // Signals under our(the "target") control. + //bool BSY, MSG, CD, REQ, IO; + + // Signals under the control of the initiator(not us!) + //bool kingACK, kingRST, kingSEL, kingATN; +} scsicd_bus_t; + +extern scsicd_bus_t cd_bus; // Don't access this structure directly by name outside of scsicd.c, but use the macros below. + +// Signals under our(the "target") control. +#define SCSICD_IO_mask 0x001 +#define SCSICD_CD_mask 0x002 +#define SCSICD_MSG_mask 0x004 +#define SCSICD_REQ_mask 0x008 +#define SCSICD_BSY_mask 0x010 + +// Signals under the control of the initiator(not us!) +#define SCSICD_kingRST_mask 0x020 +#define SCSICD_kingACK_mask 0x040 +#define SCSICD_kingATN_mask 0x080 +#define SCSICD_kingSEL_mask 0x100 + +#define BSY_signal ((const bool)(cd_bus.signals & SCSICD_BSY_mask)) +#define ACK_signal ((const bool)(cd_bus.signals & SCSICD_kingACK_mask)) +#define RST_signal ((const bool)(cd_bus.signals & SCSICD_kingRST_mask)) +#define MSG_signal ((const bool)(cd_bus.signals & SCSICD_MSG_mask)) +#define SEL_signal ((const bool)(cd_bus.signals & SCSICD_kingSEL_mask)) +#define REQ_signal ((const bool)(cd_bus.signals & SCSICD_REQ_mask)) +#define IO_signal ((const bool)(cd_bus.signals & SCSICD_IO_mask)) +#define CD_signal ((const bool)(cd_bus.signals & SCSICD_CD_mask)) +#define ATN_signal ((const bool)(cd_bus.signals & SCSICD_kingATN_mask)) + +#define DB_signal ((const uint8)cd_bus.DB) + +#define SCSICD_GetDB() DB_signal +#define SCSICD_GetBSY() BSY_signal +#define SCSICD_GetIO() IO_signal +#define SCSICD_GetCD() CD_signal +#define SCSICD_GetMSG() MSG_signal +#define SCSICD_GetREQ() REQ_signal + +// Should we phase out getting these initiator-driven signals like this(the initiator really should keep track of them itself)? +#define SCSICD_GetACK() ACK_signal +#define SCSICD_GetRST() RST_signal +#define SCSICD_GetSEL() SEL_signal +#define SCSICD_GetATN() ATN_signal + +void SCSICD_Power(scsicd_timestamp_t system_timestamp); +void SCSICD_SetDB(uint8 data); + +// These SCSICD_Set* functions are kind of misnomers, at least in comparison to the SCSICD_Get* functions... +// They will set/clear the bits corresponding to the KING's side of the bus. +void SCSICD_SetACK(bool set); +void SCSICD_SetSEL(bool set); +void SCSICD_SetRST(bool set); +void SCSICD_SetATN(bool set); + +uint32 SCSICD_Run(scsicd_timestamp_t); +void SCSICD_ResetTS(uint32 ts_base); + +enum +{ + SCSICD_PCE = 1, + SCSICD_PCFX +}; + +enum +{ + SCSICD_IRQ_DATA_TRANSFER_DONE = 1, + SCSICD_IRQ_DATA_TRANSFER_READY, + SCSICD_IRQ_MAGICAL_REQ, +}; + +void SCSICD_GetCDDAValues(int16 &left, int16 &right); + +void SCSICD_SetLog(void (*logfunc)(const char *, const char *, ...)); + +void SCSICD_Init(int type, int CDDATimeDiv, int32* left_hrbuf, int32* right_hrbuf, uint32 TransferRate, uint32 SystemClock, void (*IRQFunc)(int), void (*SSCFunc)(uint8, int)) MDFN_COLD; + +void SCSICD_SetTransferRate(uint32 TransferRate); +void SCSICD_SetCDDAVolume(double left, double right); + +void SCSICD_SetDisc(bool tray_open, CDIF *cdif, bool no_emu_side_effects = false); + +#endif diff --git a/waterbox/pcfx/cdrom/scsicd_cdda_filter.inc b/waterbox/pcfx/cdrom/scsicd_cdda_filter.inc new file mode 100644 index 0000000000..bef57166d3 --- /dev/null +++ b/waterbox/pcfx/cdrom/scsicd_cdda_filter.inc @@ -0,0 +1,69 @@ +// WARNING: Check resampling algorithm in scsicd.cpp for overflows if any value in here is negative. + + /* -1 */ { 1777, 12211, 27812, 27640, 11965, 1703, 9, 0 }, // 83117 83119.332059(diff = 2.332059) + /* 0 */ { 1702, 11965, 27640, 27811, 12211, 1777, 11, 0 }, // 83117 83121.547903(diff = 4.547903) + /* 1 */ { 1630, 11720, 27463, 27977, 12459, 1854, 14, 0 }, // 83117 83123.444392(diff = 6.444392) + /* 2 */ { 1560, 11478, 27282, 28139, 12708, 1933, 17, 0 }, // 83117 83125.036510(diff = 8.036510) + /* 3 */ { 1492, 11238, 27098, 28296, 12959, 2014, 20, 0 }, // 83117 83126.338722(diff = 9.338722) + /* 4 */ { 1427, 11000, 26909, 28448, 13212, 2098, 23, 0 }, // 83117 83127.364983(diff = 10.364983) + /* 5 */ { 1363, 10764, 26716, 28595, 13467, 2185, 27, 0 }, // 83117 83128.128743(diff = 11.128743) + /* 6 */ { 1302, 10530, 26519, 28738, 13723, 2274, 31, 0 }, // 83117 83128.642956(diff = 11.642956) + /* 7 */ { 1242, 10299, 26319, 28876, 13981, 2365, 35, 0 }, // 83117 83128.920096(diff = 11.920096) + /* 8 */ { 1185, 10071, 26115, 29009, 14239, 2459, 39, 0 }, // 83117 83128.972128(diff = 11.972128) + /* 9 */ { 1129, 9844, 25907, 29137, 14499, 2556, 45, 0 }, // 83117 83128.810568(diff = 11.810568) + /* 10 */ { 1076, 9620, 25695, 29260, 14761, 2655, 50, 0 }, // 83117 83128.446456(diff = 11.446456) + /* 11 */ { 1024, 9399, 25481, 29377, 15023, 2757, 56, 0 }, // 83117 83127.890369(diff = 10.890369) + /* 12 */ { 975, 9180, 25263, 29489, 15287, 2861, 62, 0 }, // 83117 83127.152431(diff = 10.152431) + /* 13 */ { 927, 8964, 25041, 29596, 15552, 2968, 69, 0 }, // 83117 83126.242312(diff = 9.242312) + /* 14 */ { 880, 8750, 24817, 29698, 15818, 3078, 76, 0 }, // 83117 83125.169251(diff = 8.169251) + /* 15 */ { 836, 8539, 24590, 29794, 16083, 3191, 84, 0 }, // 83117 83123.942037(diff = 6.942037) + /* 16 */ { 793, 8331, 24359, 29884, 16350, 3307, 93, 0 }, // 83117 83122.569034(diff = 5.569034) + /* 17 */ { 752, 8125, 24126, 29969, 16618, 3425, 102, 0 }, // 83117 83121.058175(diff = 4.058175) + /* 18 */ { 712, 7923, 23890, 30049, 16886, 3546, 111, 0 }, // 83117 83119.416975(diff = 2.416975) + /* 19 */ { 674, 7723, 23651, 30123, 17154, 3670, 122, 0 }, // 83117 83117.652622(diff = 0.652622) + /* 20 */ { 638, 7526, 23410, 30191, 17422, 3797, 133, 0 }, // 83117 83115.771622(diff = 1.228378) + /* 21 */ { 603, 7331, 23167, 30254, 17691, 3927, 144, 0 }, // 83117 83113.780335(diff = 3.219665) + /* 22 */ { 569, 7140, 22922, 30310, 17960, 4059, 157, 0 }, // 83117 83111.684630(diff = 5.315370) + /* 23 */ { 537, 6951, 22674, 30361, 18229, 4195, 170, 0 }, // 83117 83109.489972(diff = 7.510028) + /* 24 */ { 506, 6766, 22424, 30407, 18497, 4334, 183, 0 }, // 83117 83107.201429(diff = 9.798571) + /* 25 */ { 477, 6583, 22172, 30446, 18766, 4475, 198, 0 }, // 83117 83104.823668(diff = 12.176332) + /* 26 */ { 449, 6403, 21919, 30479, 19034, 4619, 214, 0 }, // 83117 83102.360963(diff = 14.639037) + /* 27 */ { 422, 6226, 21664, 30507, 19301, 4767, 230, 0 }, // 83117 83099.817193(diff = 17.182807) + /* 28 */ { 396, 6053, 21407, 30529, 19568, 4917, 247, 0 }, // 83117 83097.195820(diff = 19.804180) + /* 29 */ { 372, 5882, 21148, 30545, 19834, 5071, 265, 0 }, // 83117 83094.499993(diff = 22.500007) + /* 30 */ { 348, 5714, 20888, 30555, 20100, 5227, 285, 0 }, // 83117 83091.732389(diff = 25.267611) + /* 31 */ { 326, 5549, 20627, 30559, 20365, 5386, 305, 0 }, // 83117 83088.895321(diff = 28.104679) + /* 32 */ { 305, 5386, 20365, 30559, 20627, 5549, 326, 0 }, // 83117 83088.895321(diff = 28.104679) + /* 33 */ { 285, 5227, 20100, 30555, 20888, 5714, 348, 0 }, // 83117 83091.732389(diff = 25.267611) + /* 34 */ { 265, 5071, 19834, 30545, 21148, 5882, 372, 0 }, // 83117 83094.499993(diff = 22.500007) + /* 35 */ { 247, 4917, 19568, 30529, 21407, 6053, 396, 0 }, // 83117 83097.195820(diff = 19.804180) + /* 36 */ { 230, 4767, 19301, 30507, 21664, 6226, 422, 0 }, // 83117 83099.817193(diff = 17.182807) + /* 37 */ { 214, 4619, 19034, 30479, 21919, 6403, 449, 0 }, // 83117 83102.360963(diff = 14.639037) + /* 38 */ { 198, 4475, 18766, 30446, 22172, 6583, 477, 0 }, // 83117 83104.823668(diff = 12.176332) + /* 39 */ { 183, 4334, 18497, 30407, 22424, 6766, 506, 0 }, // 83117 83107.201429(diff = 9.798571) + /* 40 */ { 170, 4195, 18229, 30361, 22674, 6951, 537, 0 }, // 83117 83109.489972(diff = 7.510028) + /* 41 */ { 157, 4059, 17960, 30310, 22922, 7140, 569, 0 }, // 83117 83111.684630(diff = 5.315370) + /* 42 */ { 144, 3927, 17691, 30254, 23167, 7331, 603, 0 }, // 83117 83113.780335(diff = 3.219665) + /* 43 */ { 133, 3797, 17422, 30191, 23410, 7526, 638, 0 }, // 83117 83115.771622(diff = 1.228378) + /* 44 */ { 122, 3670, 17154, 30123, 23651, 7723, 674, 0 }, // 83117 83117.652622(diff = 0.652622) + /* 45 */ { 111, 3546, 16886, 30049, 23890, 7923, 712, 0 }, // 83117 83119.416975(diff = 2.416975) + /* 46 */ { 102, 3425, 16618, 29969, 24126, 8125, 752, 0 }, // 83117 83121.058175(diff = 4.058175) + /* 47 */ { 93, 3307, 16350, 29884, 24359, 8331, 793, 0 }, // 83117 83122.569034(diff = 5.569034) + /* 48 */ { 84, 3191, 16083, 29794, 24590, 8539, 836, 0 }, // 83117 83123.942037(diff = 6.942037) + /* 49 */ { 76, 3078, 15818, 29698, 24817, 8750, 880, 0 }, // 83117 83125.169251(diff = 8.169251) + /* 50 */ { 69, 2968, 15552, 29596, 25041, 8964, 927, 0 }, // 83117 83126.242312(diff = 9.242312) + /* 51 */ { 62, 2861, 15287, 29489, 25263, 9180, 975, 0 }, // 83117 83127.152431(diff = 10.152431) + /* 52 */ { 56, 2757, 15023, 29377, 25481, 9399, 1024, 0 }, // 83117 83127.890369(diff = 10.890369) + /* 53 */ { 50, 2655, 14761, 29260, 25695, 9620, 1076, 0 }, // 83117 83128.446456(diff = 11.446456) + /* 54 */ { 45, 2556, 14499, 29137, 25907, 9844, 1129, 0 }, // 83117 83128.810568(diff = 11.810568) + /* 55 */ { 39, 2459, 14239, 29009, 26115, 10071, 1185, 0 }, // 83117 83128.972128(diff = 11.972128) + /* 56 */ { 35, 2365, 13981, 28876, 26319, 10299, 1242, 0 }, // 83117 83128.920096(diff = 11.920096) + /* 57 */ { 31, 2274, 13723, 28738, 26519, 10530, 1302, 0 }, // 83117 83128.642956(diff = 11.642956) + /* 58 */ { 27, 2185, 13467, 28595, 26716, 10764, 1363, 0 }, // 83117 83128.128743(diff = 11.128743) + /* 59 */ { 23, 2098, 13212, 28448, 26909, 11000, 1427, 0 }, // 83117 83127.364983(diff = 10.364983) + /* 60 */ { 20, 2014, 12959, 28296, 27098, 11238, 1492, 0 }, // 83117 83126.338722(diff = 9.338722) + /* 61 */ { 17, 1933, 12708, 28139, 27282, 11478, 1560, 0 }, // 83117 83125.036510(diff = 8.036510) + /* 62 */ { 14, 1854, 12459, 27977, 27463, 11720, 1630, 0 }, // 83117 83123.444392(diff = 6.444392) + /* 63 */ { 11, 1777, 12211, 27811, 27640, 11965, 1702, 0 }, // 83117 83121.547903(diff = 4.547903) + /* 64 */ { 9, 1703, 11965, 27640, 27812, 12211, 1777, 0 }, // 83117 83119.332059(diff = 2.332059) + diff --git a/waterbox/pcfx/defs.h b/waterbox/pcfx/defs.h new file mode 100644 index 0000000000..5156219336 --- /dev/null +++ b/waterbox/pcfx/defs.h @@ -0,0 +1,114 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +typedef uint8_t uint8; +typedef uint16_t uint16; +typedef uint32_t uint32; +typedef uint64_t uint64; +typedef int8_t int8; +typedef int16_t int16; +typedef int32_t int32; +typedef int64_t int64; + +#define MDFN_FASTCALL +#define INLINE inline +#define MDFN_COLD +#define MDFN_HOT +#define NO_INLINE +#define NO_CLONE +#define MDFN_WARN_UNUSED_RESULT +#define MDFN_NOWARN_UNUSED __attribute__((unused)) +#define MDFN_UNLIKELY(p) (p) +#define MDFN_LIKELY(p) (p) +//#define MDFN_ASSUME_ALIGNED(p, align) ((decltype(p))__builtin_assume_aligned((p), (align))) +#define MDFN_ASSUME_ALIGNED(p, align) (p) +#define trio_snprintf snprintf +#define trio_vprintf vprintf +#define trio_printf printf +#define trio_sprintf sprintf +#define TRUE true +#define FALSE false +#ifndef __alignas_is_defined +#define alignas(p) +#endif +#define override // remove for gcc 4.7 +#define final +#define gettext_noop(s) (s) +#define MDFN_MASTERCLOCK_FIXED(n) ((int64)((double)(n) * (1LL << 32))) +#define MDFN_FastArraySet memset +#define _(a) (a) + +typedef struct +{ + // Pitch(32-bit) must be equal to width and >= the "fb_width" specified in the MDFNGI struct for the emulated system. + // Height must be >= to the "fb_height" specified in the MDFNGI struct for the emulated system. + // The framebuffer pointed to by surface->pixels is written to by the system emulation code. + uint32 *pixels; + int pitch32; + + // Pointer to an array of int32, number of elements = fb_height, set by the driver code. Individual elements written + // to by system emulation code. If the emulated system doesn't support multiple screen widths per frame, or if you handle + // such a situation by outputting at a constant width-per-frame that is the least-common-multiple of the screen widths, then + // you can ignore this. If you do wish to use this, you must set all elements every frame. + int32 *LineWidths; + + // Pointer to sound buffer, set by the driver code, that the emulation code should render sound to. + int16 *SoundBuf; + + // Number of cycles that this frame consumed, using MDFNGI::MasterClock as a time base. + // Set by emulation code. + int64 MasterCycles; + + // Maximum size of the sound buffer, in frames. Set by the driver code. + int32 SoundBufMaxSize; + + // Number of frames currently in internal sound buffer. Set by the system emulation code, to be read by the driver code. + int32 SoundBufSize; + + // Set by the system emulation code every frame, to denote the horizontal and vertical offsets of the image, and the size + // of the image. If the emulated system sets the elements of LineWidths, then the width(w) of this structure + // is ignored while drawing the image. + int32 x, y, w, h; + + // Set(optionally) by emulation code. If InterlaceOn is true, then assume field height is 1/2 DisplayRect.h, and + // only every other line in surface (with the start line defined by InterlacedField) has valid data + // (it's up to internal Mednafen code to deinterlace it). + bool InterlaceOn; + bool InterlaceField; + + // if true, sip rendering + bool skip; +} EmulateSpecStruct; + +#define MDFN_printf printf +#define MDFN_PrintError(...) printf + +#include "endian.h" + +#include "math_ops.h" + +#include "../emulibc/emulibc.h" +#include "../emulibc/waterboxcore.h" + +// settings +extern int Setting_HighDotclockWidth; +extern int Setting_CdSpeed; +extern int Setting_SlStart; +extern int Setting_SlEnd; + +extern double Setting_ResampRateError; +extern int Setting_ResampQuality; + +extern int Setting_CpuEmulation; // 0 = fast, 1 = accurate, 2 = auto +extern bool Setting_NoSpriteLimit; +extern bool Setting_AdpcmBuggy; +extern bool Setting_AdpcmNoClicks; +extern bool Setting_ChromaInterpolate; + diff --git a/waterbox/pcfx/endian.h b/waterbox/pcfx/endian.h new file mode 100644 index 0000000000..e78a0c577f --- /dev/null +++ b/waterbox/pcfx/endian.h @@ -0,0 +1,494 @@ +/******************************************************************************/ +/* Mednafen - Multi-system Emulator */ +/******************************************************************************/ +/* endian.h: +** Copyright (C) 2006-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef __MDFN_ENDIAN_H +#define __MDFN_ENDIAN_H + +void Endian_A16_Swap(void *src, uint32 nelements); +void Endian_A32_Swap(void *src, uint32 nelements); +void Endian_A64_Swap(void *src, uint32 nelements); + +void Endian_A16_NE_LE(void *src, uint32 nelements); +void Endian_A32_NE_LE(void *src, uint32 nelements); +void Endian_A64_NE_LE(void *src, uint32 nelements); + +void Endian_A16_NE_BE(void *src, uint32 nelements); +void Endian_A32_NE_BE(void *src, uint32 nelements); +void Endian_A64_NE_BE(void *src, uint32 nelements); + +void Endian_V_NE_LE(void* p, size_t len); +void Endian_V_NE_BE(void* p, size_t len); + +// +// +// + +static INLINE uint32 BitsExtract(const uint8* ptr, const size_t bit_offset, const size_t bit_count) +{ + uint32 ret = 0; + + for(size_t x = 0; x < bit_count; x++) + { + size_t co = bit_offset + x; + bool b = (ptr[co >> 3] >> (co & 7)) & 1; + + ret |= (uint64)b << x; + } + + return ret; +} + +static INLINE void BitsIntract(uint8* ptr, const size_t bit_offset, const size_t bit_count, uint32 value) +{ + for(size_t x = 0; x < bit_count; x++) + { + size_t co = bit_offset + x; + bool b = (value >> x) & 1; + uint8 tmp = ptr[co >> 3]; + + tmp &= ~(1 << (co & 7)); + tmp |= b << (co & 7); + + ptr[co >> 3] = tmp; + } +} + +/* + Regarding safety of calling MDFN_*sb on dynamically-allocated memory with new uint8[], see C++ standard 3.7.3.1(i.e. it should be + safe provided the offsets into the memory are aligned/multiples of the MDFN_*sb access type). malloc()'d and calloc()'d + memory should be safe as well. + + Statically-allocated arrays/memory should be unioned with a big POD type or C++11 "alignas"'d. (May need to audit code to ensure + this is being done). +*/ + +static INLINE uint16 MDFN_bswap16(uint16 v) +{ + return (v << 8) | (v >> 8); +} + +static INLINE uint32 MDFN_bswap32(uint32 v) +{ + return (v << 24) | ((v & 0xFF00) << 8) | ((v >> 8) & 0xFF00) | (v >> 24); +} + +static INLINE uint64 MDFN_bswap64(uint64 v) +{ + return (v << 56) | (v >> 56) | ((v & 0xFF00) << 40) | ((v >> 40) & 0xFF00) | ((uint64)MDFN_bswap32(v >> 16) << 16); +} + +#ifdef LSB_FIRST + #define MDFN_ENDIANH_IS_BIGENDIAN 0 +#else + #define MDFN_ENDIANH_IS_BIGENDIAN 1 +#endif + +// +// X endian. +// +template +static INLINE T MDFN_deXsb(const void* ptr) +{ + T tmp; + + memcpy(&tmp, MDFN_ASSUME_ALIGNED(ptr, (aligned ? sizeof(T) : 1)), sizeof(T)); + + if(isbigendian != -1 && isbigendian != MDFN_ENDIANH_IS_BIGENDIAN) + { + static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8, "Gummy penguins."); + + if(sizeof(T) == 8) + return MDFN_bswap64(tmp); + else if(sizeof(T) == 4) + return MDFN_bswap32(tmp); + else if(sizeof(T) == 2) + return MDFN_bswap16(tmp); + } + + return tmp; +} + +// +// Native endian. +// +template +static INLINE T MDFN_densb(const void* ptr) +{ + return MDFN_deXsb<-1, T, aligned>(ptr); +} + +// +// Little endian. +// +template +static INLINE T MDFN_delsb(const void* ptr) +{ + return MDFN_deXsb<0, T, aligned>(ptr); +} + +template +static INLINE uint16 MDFN_de16lsb(const void* ptr) +{ + return MDFN_delsb(ptr); +} + +static INLINE uint32 MDFN_de24lsb(const void* ptr) +{ + const uint8* ptr_u8 = (const uint8*)ptr; + + return (ptr_u8[0] << 0) | (ptr_u8[1] << 8) | (ptr_u8[2] << 16); +} + +template +static INLINE uint32 MDFN_de32lsb(const void* ptr) +{ + return MDFN_delsb(ptr); +} + +template +static INLINE uint64 MDFN_de64lsb(const void* ptr) +{ + return MDFN_delsb(ptr); +} + +// +// Big endian. +// +template +static INLINE T MDFN_demsb(const void* ptr) +{ + return MDFN_deXsb<1, T, aligned>(ptr); +} + +template +static INLINE uint16 MDFN_de16msb(const void* ptr) +{ + return MDFN_demsb(ptr); +} + +static INLINE uint32 MDFN_de24msb(const void* ptr) +{ + const uint8* ptr_u8 = (const uint8*)ptr; + + return (ptr_u8[0] << 16) | (ptr_u8[1] << 8) | (ptr_u8[2] << 0); +} + +template +static INLINE uint32 MDFN_de32msb(const void* ptr) +{ + return MDFN_demsb(ptr); +} + +template +static INLINE uint64 MDFN_de64msb(const void* ptr) +{ + return MDFN_demsb(ptr); +} + +// +// +// +// +// +// +// +// + +// +// X endian. +// +template +static INLINE void MDFN_enXsb(void* ptr, T value) +{ + T tmp = value; + + if(isbigendian != -1 && isbigendian != MDFN_ENDIANH_IS_BIGENDIAN) + { + static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8, "Gummy penguins."); + + if(sizeof(T) == 8) + tmp = MDFN_bswap64(value); + else if(sizeof(T) == 4) + tmp = MDFN_bswap32(value); + else if(sizeof(T) == 2) + tmp = MDFN_bswap16(value); + } + + memcpy(MDFN_ASSUME_ALIGNED(ptr, (aligned ? sizeof(T) : 1)), &tmp, sizeof(T)); +} + +// +// Native endian. +// +template +static INLINE void MDFN_ennsb(void* ptr, T value) +{ + MDFN_enXsb<-1, T, aligned>(ptr, value); +} + +// +// Little endian. +// +template +static INLINE void MDFN_enlsb(void* ptr, T value) +{ + MDFN_enXsb<0, T, aligned>(ptr, value); +} + +template +static INLINE void MDFN_en16lsb(void* ptr, uint16 value) +{ + MDFN_enlsb(ptr, value); +} + +static INLINE void MDFN_en24lsb(void* ptr, uint32 value) +{ + uint8* ptr_u8 = (uint8*)ptr; + + ptr_u8[0] = value >> 0; + ptr_u8[1] = value >> 8; + ptr_u8[2] = value >> 16; +} + +template +static INLINE void MDFN_en32lsb(void* ptr, uint32 value) +{ + MDFN_enlsb(ptr, value); +} + +template +static INLINE void MDFN_en64lsb(void* ptr, uint64 value) +{ + MDFN_enlsb(ptr, value); +} + + +// +// Big endian. +// +template +static INLINE void MDFN_enmsb(void* ptr, T value) +{ + MDFN_enXsb<1, T, aligned>(ptr, value); +} + +template +static INLINE void MDFN_en16msb(void* ptr, uint16 value) +{ + MDFN_enmsb(ptr, value); +} + +static INLINE void MDFN_en24msb(void* ptr, uint32 value) +{ + uint8* ptr_u8 = (uint8*)ptr; + + ptr_u8[0] = value >> 16; + ptr_u8[1] = value >> 8; + ptr_u8[2] = value >> 0; +} + +template +static INLINE void MDFN_en32msb(void* ptr, uint32 value) +{ + MDFN_enmsb(ptr, value); +} + +template +static INLINE void MDFN_en64msb(void* ptr, uint64 value) +{ + MDFN_enmsb(ptr, value); +} + + +// +// +// +// +// +// + +template +static INLINE uint8* ne16_ptr_be(BT* const base, const size_t byte_offset) +{ +#ifdef MSB_FIRST + return (uint8*)base + (byte_offset &~ (sizeof(T) - 1)); +#else + return (uint8*)base + (((byte_offset &~ (sizeof(T) - 1)) ^ (2 - std::min(2, sizeof(T))))); +#endif +} + +template +static INLINE void ne16_wbo_be(uint16* const base, const size_t byte_offset, const T value) +{ + uint8* const ptr = ne16_ptr_be(base, byte_offset); + + static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4, "Unsupported type size"); + + if(sizeof(T) == 4) + { + uint16* const ptr16 = (uint16*)ptr; + + ptr16[0] = value >> 16; + ptr16[1] = value; + } + else + *(T*)ptr = value; +} + +template +static INLINE T ne16_rbo_be(const uint16* const base, const size_t byte_offset) +{ + uint8* const ptr = ne16_ptr_be(base, byte_offset); + + static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4, "Unsupported type size"); + + if(sizeof(T) == 4) + { + uint16* const ptr16 = (uint16*)ptr; + T tmp; + + tmp = ptr16[0] << 16; + tmp |= ptr16[1]; + + return tmp; + } + else + return *(T*)ptr; +} + +template +static INLINE void ne16_rwbo_be(uint16* const base, const size_t byte_offset, T* value) +{ + if(IsWrite) + ne16_wbo_be(base, byte_offset, *value); + else + *value = ne16_rbo_be(base, byte_offset); +} + +// +// +// + +template +static INLINE uint8* ne16_ptr_le(BT* const base, const size_t byte_offset) +{ +#ifdef LSB_FIRST + return (uint8*)base + (byte_offset &~ (sizeof(T) - 1)); +#else + return (uint8*)base + (((byte_offset &~ (sizeof(T) - 1)) ^ (2 - std::min(2, sizeof(T))))); +#endif +} + +template +static INLINE void ne16_wbo_le(uint16* const base, const size_t byte_offset, const T value) +{ + uint8* const ptr = ne16_ptr_le(base, byte_offset); + + static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4, "Unsupported type size"); + + if(sizeof(T) == 4) + { + uint16* const ptr16 = (uint16*)ptr; + + ptr16[0] = value; + ptr16[1] = value >> 16; + } + else + *(T*)ptr = value; +} + +template +static INLINE T ne16_rbo_le(const uint16* const base, const size_t byte_offset) +{ + uint8* const ptr = ne16_ptr_le(base, byte_offset); + + static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4, "Unsupported type size"); + + if(sizeof(T) == 4) + { + uint16* const ptr16 = (uint16*)ptr; + T tmp; + + tmp = ptr16[0]; + tmp |= ptr16[1] << 16; + + return tmp; + } + else + return *(T*)ptr; +} + + +template +static INLINE void ne16_rwbo_le(uint16* const base, const size_t byte_offset, T* value) +{ + if(IsWrite) + ne16_wbo_le(base, byte_offset, *value); + else + *value = ne16_rbo_le(base, byte_offset); +} + +// +// +// +template +static INLINE uint8* ne64_ptr_be(uint64* const base, const size_t byte_offset) +{ +#ifdef MSB_FIRST + return (uint8*)base + (byte_offset &~ (sizeof(T) - 1)); +#else + return (uint8*)base + (((byte_offset &~ (sizeof(T) - 1)) ^ (8 - sizeof(T)))); +#endif +} + +template +static INLINE void ne64_wbo_be(uint64* const base, const size_t byte_offset, const T value) +{ + uint8* const ptr = ne64_ptr_be(base, byte_offset); + + static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8, "Unsupported type size"); + + memcpy(MDFN_ASSUME_ALIGNED(ptr, sizeof(T)), &value, sizeof(T)); +} + +template +static INLINE T ne64_rbo_be(uint64* const base, const size_t byte_offset) +{ + uint8* const ptr = ne64_ptr_be(base, byte_offset); + T ret; + + static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4, "Unsupported type size"); + + memcpy(&ret, MDFN_ASSUME_ALIGNED(ptr, sizeof(T)), sizeof(T)); + + return ret; +} + +template +static INLINE void ne64_rwbo_be(uint64* const base, const size_t byte_offset, T* value) +{ + if(IsWrite) + ne64_wbo_be(base, byte_offset, *value); + else + *value = ne64_rbo_be(base, byte_offset); +} + +#endif diff --git a/waterbox/pcfx/fxscsi.cpp b/waterbox/pcfx/fxscsi.cpp new file mode 100644 index 0000000000..bf516c90d1 --- /dev/null +++ b/waterbox/pcfx/fxscsi.cpp @@ -0,0 +1,47 @@ +/******************************************************************************/ +/* Mednafen NEC PC-FX Emulation Module */ +/******************************************************************************/ +/* fxscsi.cpp: +** Copyright (C) 2009-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "pcfx.h" +#include "fxscsi.h" + +namespace MDFN_IEN_PCFX +{ + +bool FXSCSI_Init(void) +{ + + return true; +} + +uint8 FXSCSI_CtrlRead(uint32 A) +{ + uint8 ret = 0; //rand(); + //printf("FXSCSI: %08x(ret=%02x)\n", A, ret); + return(ret); +} + + +void FXSCSI_CtrlWrite(uint32 A, uint8 V) +{ + printf("FXSCSI Write: %08x %02x\n", A, V); +} + +} diff --git a/waterbox/pcfx/fxscsi.h b/waterbox/pcfx/fxscsi.h new file mode 100644 index 0000000000..5741e3afcb --- /dev/null +++ b/waterbox/pcfx/fxscsi.h @@ -0,0 +1,33 @@ +/******************************************************************************/ +/* Mednafen NEC PC-FX Emulation Module */ +/******************************************************************************/ +/* fxscsi.h: +** Copyright (C) 2009-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef __MDFN_FXSCSI_H +#define __MDFN_FXSCSI_H + +namespace MDFN_IEN_PCFX +{ + +bool FXSCSI_Init(void) MDFN_COLD; +uint8 FXSCSI_CtrlRead(uint32 A); +void FXSCSI_CtrlWrite(uint32 A, uint8 V); + +} +#endif diff --git a/waterbox/pcfx/gamedb.inc b/waterbox/pcfx/gamedb.inc new file mode 100644 index 0000000000..20a4db091b --- /dev/null +++ b/waterbox/pcfx/gamedb.inc @@ -0,0 +1,1731 @@ +// Names taken from http://pcecp.com/ and http://www.necstasy.net/ + +{ + "Aa! Megami Sama", + "ああっ女神さまっ", + 0, + 2, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 10504 }, + { 3, CDGE_FORMAT_DATA, 10890 }, + { -1, -1, 240939 }, + }, + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 10163 }, + { 3, CDGE_FORMAT_DATA, 10890 }, + { -1, -1, 211853 }, + } + } +}, + +{ + "Akazukin Cha Cha", + "赤ずきんちゃちゃ ーお騒かせ!パニックレース!ー", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 5088 }, + { 3, CDGE_FORMAT_AUDIO, 203157 }, + { 4, CDGE_FORMAT_AUDIO, 207261 }, + { 5, CDGE_FORMAT_AUDIO, 209849 }, + { 6, CDGE_FORMAT_AUDIO, 216868 }, + { -1, -1, 220342 }, + } + } +}, + +{ + "All Japan Female Pro Wrestle - Queen of Queens", + "全日本女子プロレス Queen of Queens", + 0, + 2, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 2368 }, + { -1, -1, 254165 }, + }, + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 2368 }, + { -1, -1, 326478 }, + } + } +}, + +{ + "Angelique Special", + "アンジェリークスペシャル", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_DATA, 4829 }, + { 4, CDGE_FORMAT_DATA, 8963 }, + { 5, CDGE_FORMAT_DATA, 66446 }, + { 6, CDGE_FORMAT_AUDIO, 194334 }, + { -1, -1, 198505 }, + } + } +}, + +{ + "Angelique Special 2", + "アンジェリークスペシャル2", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_DATA, 4737 }, + { -1, -1, 313941 }, + } + } +}, + +{ + "Angelique Tenkuu No Requim", + "アンジェリーク天空のレクイエム", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_DATA, 4737 }, + { 4, CDGE_FORMAT_DATA, 110846 }, + { -1, -1, 263180 }, + } + } +}, + +{ + "Anime Freak FX Volume 1", + "アニメフリークFX Vol.1", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 5140 }, + { -1, -1, 323367 }, + } + } +}, + +{ + "Anime Freak FX Volume 2", + "アニメフリークFX Vol.2", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 1523 }, + { -1, -1, 306483 }, + } + } +}, + +{ + "Anime Freak FX Volume 2 (Sample)", + NULL, + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 5140 }, + { -1, -1, 309756 }, + } + } +}, + +{ + "Anime Freak FX Volume 3", + "アニメフリークFX Vol.3", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 1964 }, + { -1, -1, 322351 }, + } + } +}, + +{ + "Anime Freak FX Volume 4", + "アニメフリークFX Vol.4", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 3286 }, + { -1, -1, 312713 }, + } + } +}, + +{ + "Anime Freak FX Volume 5", + "アニメフリークFX Vol.5", + 0, + 2, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 2943 }, + { -1, -1, 231075 }, + }, + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 5268 }, + { -1, -1, 267842 }, + } + } +}, + +{ + "Anime Freak FX Volume 6", + "アニメフリークFX Vol.6", + 0, + 2, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4732 }, + { -1, -1, 290461 }, + }, + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 17408 }, + { -1, -1, 306629 }, + } + } +}, + +{ + "Aruberea's Maiden", + "アルバレアの乙女", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 2381 }, + { 3, CDGE_FORMAT_DATA, 3729 }, + { 4, CDGE_FORMAT_DATA, 11919 }, + { 5, CDGE_FORMAT_DATA, 67010 }, + { 6, CDGE_FORMAT_DATA, 68706 }, + { 7, CDGE_FORMAT_DATA, 69119 }, + { 8, CDGE_FORMAT_DATA, 69538 }, + { 9, CDGE_FORMAT_DATA, 69958 }, + { 10, CDGE_FORMAT_DATA, 70425 }, + { 11, CDGE_FORMAT_DATA, 72558 }, + { 12, CDGE_FORMAT_DATA, 74956 }, + { 13, CDGE_FORMAT_DATA, 77246 }, + { 14, CDGE_FORMAT_DATA, 83332 }, + { 15, CDGE_FORMAT_DATA, 101273 }, + { 16, CDGE_FORMAT_DATA, 113096 }, + { 17, CDGE_FORMAT_DATA, 125676 }, + { 18, CDGE_FORMAT_DATA, 144119 }, + { 19, CDGE_FORMAT_DATA, 163556 }, + { -1, -1, 234498 }, + } + } +}, + +{ + "Battle Heat", + "バトルヒート", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_AUDIO, 6253 }, + { 4, CDGE_FORMAT_AUDIO, 16083 }, + { 5, CDGE_FORMAT_DATA, 35549 }, + { 6, CDGE_FORMAT_DATA, 39544 }, + { 7, CDGE_FORMAT_DATA, 40834 }, + { 8, CDGE_FORMAT_DATA, 101620 }, + { 9, CDGE_FORMAT_DATA, 120156 }, + { 10, CDGE_FORMAT_DATA, 142691 }, + { 11, CDGE_FORMAT_DATA, 154690 }, + { 12, CDGE_FORMAT_DATA, 164535 }, + { 13, CDGE_FORMAT_DATA, 178064 }, + { 14, CDGE_FORMAT_DATA, 194891 }, + { 15, CDGE_FORMAT_DATA, 205807 }, + { 16, CDGE_FORMAT_DATA, 216414 }, + { 17, CDGE_FORMAT_DATA, 234950 }, + { 18, CDGE_FORMAT_DATA, 257485 }, + { 19, CDGE_FORMAT_DATA, 269642 }, + { 20, CDGE_FORMAT_DATA, 280175 }, + { 21, CDGE_FORMAT_DATA, 293704 }, + { 22, CDGE_FORMAT_DATA, 310531 }, + { -1, -1, 311822 }, + } + } +}, + +{ + "Blue Breaker", + "ブルーブレイカー 剣よりも微笑みを", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 5804 }, + { -1, -1, 314954 }, + } + } +}, + +{ + "Blue Chicago Blues", + "J.B. Harold ブルーシカゴブルース", + 0, + 2, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_AUDIO, 241908 }, + { -1, -1, 242250 }, + }, + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_AUDIO, 308890 }, + { 3, CDGE_FORMAT_AUDIO, 322135 }, + { -1, -1, 322477 }, + } + } +}, + +{ + "Boundary Gate - Daughter of Kingdom", + "バウンダリーゲート Daughter of Kingdom", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_AUDIO, 31797 }, + { 4, CDGE_FORMAT_AUDIO, 34840 }, + { 5, CDGE_FORMAT_AUDIO, 38383 }, + { 6, CDGE_FORMAT_AUDIO, 38898 }, + { 7, CDGE_FORMAT_AUDIO, 40365 }, + { 8, CDGE_FORMAT_AUDIO, 41223 }, + { 9, CDGE_FORMAT_AUDIO, 41543 }, + { 10, CDGE_FORMAT_AUDIO, 44293 }, + { 11, CDGE_FORMAT_AUDIO, 47722 }, + { 12, CDGE_FORMAT_AUDIO, 48042 }, + { 13, CDGE_FORMAT_AUDIO, 50396 }, + { 14, CDGE_FORMAT_AUDIO, 50716 }, + { 15, CDGE_FORMAT_AUDIO, 51078 }, + { 16, CDGE_FORMAT_AUDIO, 51477 }, + { 17, CDGE_FORMAT_AUDIO, 52240 }, + { 18, CDGE_FORMAT_AUDIO, 53430 }, + { 19, CDGE_FORMAT_AUDIO, 54573 }, + { 20, CDGE_FORMAT_AUDIO, 56949 }, + { 21, CDGE_FORMAT_AUDIO, 59248 }, + { 22, CDGE_FORMAT_AUDIO, 61753 }, + { 23, CDGE_FORMAT_AUDIO, 62577 }, + { 24, CDGE_FORMAT_AUDIO, 63746 }, + { 25, CDGE_FORMAT_AUDIO, 64827 }, + { 26, CDGE_FORMAT_AUDIO, 66673 }, + { 27, CDGE_FORMAT_AUDIO, 69156 }, + { 28, CDGE_FORMAT_AUDIO, 71742 }, + { 29, CDGE_FORMAT_AUDIO, 74427 }, + { 30, CDGE_FORMAT_AUDIO, 76953 }, + { 31, CDGE_FORMAT_AUDIO, 78144 }, + { 32, CDGE_FORMAT_AUDIO, 78464 }, + { 33, CDGE_FORMAT_AUDIO, 83018 }, + { 34, CDGE_FORMAT_AUDIO, 83528 }, + { 35, CDGE_FORMAT_AUDIO, 87939 }, + { 36, CDGE_FORMAT_AUDIO, 88861 }, + { 37, CDGE_FORMAT_AUDIO, 91647 }, + { 38, CDGE_FORMAT_AUDIO, 92172 }, + { 39, CDGE_FORMAT_AUDIO, 94556 }, + { 40, CDGE_FORMAT_AUDIO, 97820 }, + { 41, CDGE_FORMAT_AUDIO, 98140 }, + { 42, CDGE_FORMAT_AUDIO, 98614 }, + { 43, CDGE_FORMAT_AUDIO, 98951 }, + { 44, CDGE_FORMAT_AUDIO, 99691 }, + { 45, CDGE_FORMAT_AUDIO, 100379 }, + { 46, CDGE_FORMAT_AUDIO, 100699 }, + { 47, CDGE_FORMAT_AUDIO, 101019 }, + { 48, CDGE_FORMAT_AUDIO, 101339 }, + { 49, CDGE_FORMAT_AUDIO, 103185 }, + { 50, CDGE_FORMAT_AUDIO, 103500 }, + { 51, CDGE_FORMAT_AUDIO, 107791 }, + { 52, CDGE_FORMAT_AUDIO, 108111 }, + { 53, CDGE_FORMAT_AUDIO, 108583 }, + { 54, CDGE_FORMAT_AUDIO, 109161 }, + { 55, CDGE_FORMAT_AUDIO, 113732 }, + { 56, CDGE_FORMAT_AUDIO, 114657 }, + { 57, CDGE_FORMAT_AUDIO, 115080 }, + { 58, CDGE_FORMAT_AUDIO, 115645 }, + { 59, CDGE_FORMAT_AUDIO, 115996 }, + { 60, CDGE_FORMAT_AUDIO, 117096 }, + { 61, CDGE_FORMAT_AUDIO, 117649 }, + { 62, CDGE_FORMAT_AUDIO, 117969 }, + { 63, CDGE_FORMAT_AUDIO, 118832 }, + { 64, CDGE_FORMAT_AUDIO, 120283 }, + { 65, CDGE_FORMAT_AUDIO, 122653 }, + { 66, CDGE_FORMAT_AUDIO, 122973 }, + { 67, CDGE_FORMAT_AUDIO, 123340 }, + { 68, CDGE_FORMAT_AUDIO, 123676 }, + { 69, CDGE_FORMAT_AUDIO, 123996 }, + { 70, CDGE_FORMAT_AUDIO, 126275 }, + { 71, CDGE_FORMAT_AUDIO, 130217 }, + { 72, CDGE_FORMAT_AUDIO, 137425 }, + { 73, CDGE_FORMAT_AUDIO, 143256 }, + { 74, CDGE_FORMAT_AUDIO, 145854 }, + { 75, CDGE_FORMAT_AUDIO, 149728 }, + { 76, CDGE_FORMAT_AUDIO, 151589 }, + { 77, CDGE_FORMAT_AUDIO, 151909 }, + { 78, CDGE_FORMAT_AUDIO, 152356 }, + { 79, CDGE_FORMAT_AUDIO, 156214 }, + { 80, CDGE_FORMAT_AUDIO, 158157 }, + { 81, CDGE_FORMAT_AUDIO, 159612 }, + { 82, CDGE_FORMAT_AUDIO, 162307 }, + { 83, CDGE_FORMAT_AUDIO, 163569 }, + { 84, CDGE_FORMAT_AUDIO, 166452 }, + { 85, CDGE_FORMAT_AUDIO, 169513 }, + { 86, CDGE_FORMAT_AUDIO, 174406 }, + { 87, CDGE_FORMAT_AUDIO, 191526 }, + { 88, CDGE_FORMAT_AUDIO, 197689 }, + { 89, CDGE_FORMAT_AUDIO, 197990 }, + { 90, CDGE_FORMAT_AUDIO, 198291 }, + { 91, CDGE_FORMAT_AUDIO, 201334 }, + { -1, -1, 201636 }, + } + } +}, + +{ + "Can Can Bunny Extra DX", + "きゃんきゃんバニーエクストラDX", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 2936 }, + { 3, CDGE_FORMAT_DATA, 3322 }, + { 4, CDGE_FORMAT_AUDIO, 279473 }, + { 5, CDGE_FORMAT_AUDIO, 291019 }, + { 6, CDGE_FORMAT_AUDIO, 303173 }, + { 7, CDGE_FORMAT_AUDIO, 309557 }, + { -1, -1, 316447 }, + } + } +}, + +{ + "Chip Chan Kick", + "チップきゃんキイーック", + CDGE_FLAG_ACCURATE_V810, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 5128 }, + { 3, CDGE_FORMAT_AUDIO, 66911 }, + { 4, CDGE_FORMAT_AUDIO, 68734 }, + { 5, CDGE_FORMAT_AUDIO, 73420 }, + { 6, CDGE_FORMAT_AUDIO, 78046 }, + { 7, CDGE_FORMAT_AUDIO, 83671 }, + { 8, CDGE_FORMAT_AUDIO, 103981 }, + { 9, CDGE_FORMAT_AUDIO, 123333 }, + { 10, CDGE_FORMAT_AUDIO, 139735 }, + { 11, CDGE_FORMAT_AUDIO, 143207 }, + { 12, CDGE_FORMAT_AUDIO, 145857 }, + { 13, CDGE_FORMAT_AUDIO, 158775 }, + { 14, CDGE_FORMAT_AUDIO, 177806 }, + { 15, CDGE_FORMAT_AUDIO, 194978 }, + { 16, CDGE_FORMAT_AUDIO, 195667 }, + { 17, CDGE_FORMAT_AUDIO, 197608 }, + { 18, CDGE_FORMAT_AUDIO, 198320 }, + { 19, CDGE_FORMAT_AUDIO, 203445 }, + { 20, CDGE_FORMAT_AUDIO, 221182 }, + { 21, CDGE_FORMAT_AUDIO, 237322 }, + { 22, CDGE_FORMAT_AUDIO, 256767 }, + { 23, CDGE_FORMAT_AUDIO, 257949 }, + { -1, -1, 274921 }, + } + } +}, + +{ + "Comic Road", + "こみっくろーど", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4232 }, + { 3, CDGE_FORMAT_DATA, 5258 }, + { -1, -1, 330932 }, + } + } +}, + +{ + "Cutey Honey FX", + "キューテイーハニー FX", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { -1, -1, 283315 }, + } + } +}, + +{ + "Deep Blue Fleet", + "紺碧の艦隊", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 2284 }, + { 3, CDGE_FORMAT_AUDIO, 183701 }, + { 4, CDGE_FORMAT_AUDIO, 184477 }, + { 5, CDGE_FORMAT_AUDIO, 191254 }, + { 6, CDGE_FORMAT_AUDIO, 197407 }, + { 7, CDGE_FORMAT_AUDIO, 206666 }, + { 8, CDGE_FORMAT_AUDIO, 211927 }, + { 9, CDGE_FORMAT_AUDIO, 217685 }, + { 10, CDGE_FORMAT_AUDIO, 225936 }, + { 11, CDGE_FORMAT_AUDIO, 229784 }, + { -1, -1, 230085 }, + } + } +}, + +{ + "Der Langrisser FX", + "デアラングリッサーFX", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 1971 }, + { 3, CDGE_FORMAT_AUDIO, 323127 }, + { -1, -1, 330256 }, + } + } +}, + +{ + "Doukyusei II", + "同級生2", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_DATA, 4909 }, + { 4, CDGE_FORMAT_DATA, 5941 }, + { 5, CDGE_FORMAT_DATA, 42059 }, + { 6, CDGE_FORMAT_AUDIO, 220795 }, + { 7, CDGE_FORMAT_AUDIO, 225646 }, + { 8, CDGE_FORMAT_AUDIO, 235498 }, + { -1, -1, 246680 }, + } + } +}, + +{ + "Dragon Knight IV", + "ドラゴンナイト4", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_AUDIO, 177212 }, + { 4, CDGE_FORMAT_AUDIO, 196193 }, + { -1, -1, 218037 }, + } + } +}, + +{ + "Farland Story FX", + "ファーランドストーリー", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_AUDIO, 232538 }, + { 4, CDGE_FORMAT_AUDIO, 236403 }, + { 5, CDGE_FORMAT_AUDIO, 238396 }, + { -1, -1, 242433 }, + } + } +}, + +{ + "Fire Woman Matoi-gumi", + "ファイアーウーマン纏組", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 5119 }, + { -1, -1, 257764 }, + } + } +}, + +{ + "First Kiss Monogatari", + "ファーストKiss物語", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 1356 }, + { 3, CDGE_FORMAT_AUDIO, 318011 }, + { 4, CDGE_FORMAT_AUDIO, 324526 }, + { -1, -1, 325039 }, + } + } +}, + +{ + "Fushigi No Kuni No Angelique", + "ふしぎの国のアンジェリーク", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_DATA, 4829 }, + { 4, CDGE_FORMAT_DATA, 5202 }, + { 5, CDGE_FORMAT_DATA, 7172 }, + { 6, CDGE_FORMAT_DATA, 53781 }, + { -1, -1, 142498 }, + } + } +}, + +{ + "Ginga Ojousama Densetsu Yuna FX", + "銀河お嬢様伝説ユナFX", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 2276 }, + { -1, -1, 301707 }, + } + } +}, + +{ + "Ginga Ojousama Densetsu Yuna FX Special Edition", + "銀河お嬢様伝説ユナFX Special Edition", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_DATA, 52049 }, + { -1, -1, 61102 }, + } + } +}, + +{ + "Graduation REAL", + "卒業R Graduation Real", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_DATA, 5165 }, + { -1, -1, 213907 }, + } + } +}, + +{ + "Kishin Douji Zenki FX", + "鬼神童子 ZENKI FX", + CDGE_FLAG_ACCURATE_V810, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 7171 }, + { 3, CDGE_FORMAT_AUDIO, 107872 }, + { 4, CDGE_FORMAT_AUDIO, 110438 }, + { 5, CDGE_FORMAT_AUDIO, 112940 }, + { 6, CDGE_FORMAT_AUDIO, 131362 }, + { 7, CDGE_FORMAT_AUDIO, 147425 }, + { 8, CDGE_FORMAT_AUDIO, 163839 }, + { 9, CDGE_FORMAT_AUDIO, 174752 }, + { 10, CDGE_FORMAT_AUDIO, 190392 }, + { 11, CDGE_FORMAT_AUDIO, 201880 }, + { 12, CDGE_FORMAT_AUDIO, 220717 }, + { 13, CDGE_FORMAT_AUDIO, 230587 }, + { 14, CDGE_FORMAT_AUDIO, 247515 }, + { 15, CDGE_FORMAT_AUDIO, 263803 }, + { 16, CDGE_FORMAT_AUDIO, 265987 }, + { 17, CDGE_FORMAT_AUDIO, 272735 }, + { 18, CDGE_FORMAT_AUDIO, 290382 }, + { 19, CDGE_FORMAT_AUDIO, 305690 }, + { 20, CDGE_FORMAT_AUDIO, 322430 }, + { -1, -1, 329141 }, + } + } +}, + +{ + "Kokuu Hyouryuu Nirgends", + "虚空漂流ニルゲンツ", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4492 }, + { -1, -1, 330162 }, + } + } +}, + + +{ + "Last Imperial Prince", + "ラストインペリアルプリンス", + 0, + 2, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_AUDIO, 10187 }, + { 4, CDGE_FORMAT_AUDIO, 24124 }, + { 5, CDGE_FORMAT_AUDIO, 40686 }, + { 6, CDGE_FORMAT_AUDIO, 56583 }, + { 7, CDGE_FORMAT_AUDIO, 63025 }, + { 8, CDGE_FORMAT_AUDIO, 69646 }, + { 9, CDGE_FORMAT_AUDIO, 75144 }, + { 10, CDGE_FORMAT_AUDIO, 81208 }, + { 11, CDGE_FORMAT_AUDIO, 86132 }, + { 12, CDGE_FORMAT_AUDIO, 101745 }, + { 13, CDGE_FORMAT_AUDIO, 107871 }, + { 14, CDGE_FORMAT_AUDIO, 121487 }, + { 15, CDGE_FORMAT_AUDIO, 134835 }, + { 16, CDGE_FORMAT_AUDIO, 148385 }, + { 17, CDGE_FORMAT_AUDIO, 163372 }, + { 18, CDGE_FORMAT_AUDIO, 176996 }, + { 19, CDGE_FORMAT_AUDIO, 216456 }, + { 20, CDGE_FORMAT_AUDIO, 238672 }, + { 21, CDGE_FORMAT_AUDIO, 241036 }, + { 22, CDGE_FORMAT_AUDIO, 242389 }, + { 23, CDGE_FORMAT_DATA, 246767 }, + { 24, CDGE_FORMAT_DATA, 277470 }, + { -1, -1, 290164 }, + }, + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_AUDIO, 10187 }, + { 4, CDGE_FORMAT_AUDIO, 24124 }, + { 5, CDGE_FORMAT_AUDIO, 40686 }, + { 6, CDGE_FORMAT_AUDIO, 56583 }, + { 7, CDGE_FORMAT_AUDIO, 63025 }, + { 8, CDGE_FORMAT_AUDIO, 69646 }, + { 9, CDGE_FORMAT_AUDIO, 75144 }, + { 10, CDGE_FORMAT_AUDIO, 81208 }, + { 11, CDGE_FORMAT_AUDIO, 86132 }, + { 12, CDGE_FORMAT_AUDIO, 100599 }, + { 13, CDGE_FORMAT_AUDIO, 114101 }, + { 14, CDGE_FORMAT_AUDIO, 128505 }, + { 15, CDGE_FORMAT_AUDIO, 142903 }, + { 16, CDGE_FORMAT_AUDIO, 156648 }, + { 17, CDGE_FORMAT_AUDIO, 170424 }, + { 18, CDGE_FORMAT_AUDIO, 188071 }, + { 19, CDGE_FORMAT_AUDIO, 230603 }, + { 20, CDGE_FORMAT_AUDIO, 254521 }, + { 21, CDGE_FORMAT_AUDIO, 259922 }, + { 22, CDGE_FORMAT_DATA, 262475 }, + { -1, -1, 318842 }, + } + } +}, + +{ + "Lunatic Dawn", + "ルナテイックドーンFX", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_AUDIO, 69072 }, + { 4, CDGE_FORMAT_AUDIO, 73053 }, + { 5, CDGE_FORMAT_AUDIO, 81372 }, + { 6, CDGE_FORMAT_AUDIO, 95658 }, + { 7, CDGE_FORMAT_AUDIO, 107310 }, + { 8, CDGE_FORMAT_AUDIO, 113597 }, + { 9, CDGE_FORMAT_AUDIO, 123146 }, + { 10, CDGE_FORMAT_AUDIO, 133358 }, + { 11, CDGE_FORMAT_AUDIO, 144357 }, + { 12, CDGE_FORMAT_AUDIO, 154059 }, + { 13, CDGE_FORMAT_AUDIO, 164189 }, + { 14, CDGE_FORMAT_AUDIO, 172197 }, + { 15, CDGE_FORMAT_AUDIO, 182157 }, + { 16, CDGE_FORMAT_AUDIO, 186336 }, + { 17, CDGE_FORMAT_AUDIO, 192222 }, + { 18, CDGE_FORMAT_AUDIO, 202203 }, + { 19, CDGE_FORMAT_AUDIO, 208274 }, + { 20, CDGE_FORMAT_AUDIO, 212338 }, + { 21, CDGE_FORMAT_AUDIO, 220499 }, + { 22, CDGE_FORMAT_AUDIO, 229850 }, + { 23, CDGE_FORMAT_AUDIO, 240178 }, + { 24, CDGE_FORMAT_AUDIO, 241376 }, + { 25, CDGE_FORMAT_AUDIO, 242050 }, + { 26, CDGE_FORMAT_AUDIO, 246855 }, + { 27, CDGE_FORMAT_AUDIO, 253026 }, + { 28, CDGE_FORMAT_AUDIO, 260509 }, + { 29, CDGE_FORMAT_AUDIO, 260844 }, + { 30, CDGE_FORMAT_AUDIO, 261554 }, + { 31, CDGE_FORMAT_AUDIO, 262591 }, + { -1, -1, 263219 }, + } + } +}, + +{ + "Mahjong Goku Tenjiku", + "麻雀悟空天竺", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_AUDIO, 8376 }, + { 4, CDGE_FORMAT_AUDIO, 11607 }, + { 5, CDGE_FORMAT_AUDIO, 19873 }, + { 6, CDGE_FORMAT_AUDIO, 24171 }, + { 7, CDGE_FORMAT_AUDIO, 29994 }, + { 8, CDGE_FORMAT_AUDIO, 34978 }, + { 9, CDGE_FORMAT_AUDIO, 41359 }, + { 10, CDGE_FORMAT_AUDIO, 47803 }, + { 11, CDGE_FORMAT_AUDIO, 54204 }, + { 12, CDGE_FORMAT_AUDIO, 60619 }, + { 13, CDGE_FORMAT_AUDIO, 67006 }, + { 14, CDGE_FORMAT_AUDIO, 73397 }, + { 15, CDGE_FORMAT_AUDIO, 79790 }, + { 16, CDGE_FORMAT_AUDIO, 86179 }, + { 17, CDGE_FORMAT_AUDIO, 92574 }, + { 18, CDGE_FORMAT_AUDIO, 115478 }, + { 19, CDGE_FORMAT_AUDIO, 138537 }, + { 20, CDGE_FORMAT_AUDIO, 161434 }, + { 21, CDGE_FORMAT_AUDIO, 184314 }, + { 22, CDGE_FORMAT_AUDIO, 197815 }, + { 23, CDGE_FORMAT_AUDIO, 216216 }, + { 24, CDGE_FORMAT_AUDIO, 230140 }, + { 25, CDGE_FORMAT_AUDIO, 239554 }, + { 26, CDGE_FORMAT_AUDIO, 240045 }, + { 27, CDGE_FORMAT_AUDIO, 240661 }, + { 28, CDGE_FORMAT_AUDIO, 241040 }, + { 29, CDGE_FORMAT_AUDIO, 241632 }, + { 30, CDGE_FORMAT_AUDIO, 242579 }, + { 31, CDGE_FORMAT_AUDIO, 243054 }, + { 32, CDGE_FORMAT_AUDIO, 243354 }, + { 33, CDGE_FORMAT_AUDIO, 243796 }, + { 34, CDGE_FORMAT_AUDIO, 245294 }, + { 35, CDGE_FORMAT_AUDIO, 247298 }, + { 36, CDGE_FORMAT_AUDIO, 249208 }, + { 37, CDGE_FORMAT_AUDIO, 250388 }, + { 38, CDGE_FORMAT_AUDIO, 251862 }, + { 39, CDGE_FORMAT_AUDIO, 253227 }, + { 40, CDGE_FORMAT_AUDIO, 254796 }, + { 41, CDGE_FORMAT_AUDIO, 256032 }, + { -1, -1, 264625 }, + } + } +}, + +{ + "Makeruna! Makendou Z", + "負けるな!魔剣道Z", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_DATA, 5165 }, + { 4, CDGE_FORMAT_DATA, 5677 }, + { 5, CDGE_FORMAT_DATA, 6477 }, + { 6, CDGE_FORMAT_DATA, 7645 }, + { 7, CDGE_FORMAT_DATA, 8157 }, + { 8, CDGE_FORMAT_DATA, 16349 }, + { 9, CDGE_FORMAT_DATA, 52294 }, + { 10, CDGE_FORMAT_AUDIO, 174749 }, + { 11, CDGE_FORMAT_DATA, 191888 }, + { -1, -1, 192401 }, + } + } +}, + +{ + "Megami Paradise II", + "女神天国II", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 3381 }, + { 3, CDGE_FORMAT_AUDIO, 296237 }, + { 4, CDGE_FORMAT_DATA, 306322 }, + { -1, -1, 306835 }, + } + } +}, + +{ + "Minimum Nanonic", + "みにまむなのにっく", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_AUDIO, 208813 }, + { 4, CDGE_FORMAT_AUDIO, 227341 }, + { 5, CDGE_FORMAT_AUDIO, 245834 }, + { -1, -1, 250005 }, + } + } +}, + +{ + "Miraculum: The Last Revelation", + "ミラークルム ーサ・ラスト・レベレーションー", + 0, //CDGE_FLAG_ACCURATE_V810, // 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { -1, -1, 315726 }, + } + } +}, + +{ + "Nnyuu", + NULL, + CDGE_FLAG_FXGA, + 2, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_AUDIO, 13517 }, + { 4, CDGE_FORMAT_AUDIO, 25819 }, + { 5, CDGE_FORMAT_AUDIO, 32862 }, + { 6, CDGE_FORMAT_AUDIO, 42340 }, + { 7, CDGE_FORMAT_AUDIO, 51288 }, + { 8, CDGE_FORMAT_AUDIO, 63538 }, + { 9, CDGE_FORMAT_AUDIO, 72521 }, + { 10, CDGE_FORMAT_AUDIO, 83392 }, + { -1, -1, 91836 }, + }, + { + { 1, CDGE_FORMAT_DATA, 0 }, + { -1, -1, 9495 }, + } + } +}, + +{ + "Ojousama Sousamou", + "お嬢様捜査網", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 9083 }, + { 3, CDGE_FORMAT_DATA, 10935 }, + { 4, CDGE_FORMAT_DATA, 12489 }, + { 5, CDGE_FORMAT_DATA, 13054 }, + { 6, CDGE_FORMAT_DATA, 38195 }, + { 7, CDGE_FORMAT_AUDIO, 120164 }, + { 8, CDGE_FORMAT_AUDIO, 127204 }, + { 9, CDGE_FORMAT_AUDIO, 134263 }, + { 10, CDGE_FORMAT_AUDIO, 134597 }, + { 11, CDGE_FORMAT_AUDIO, 136291 }, + { 12, CDGE_FORMAT_AUDIO, 137881 }, + { 13, CDGE_FORMAT_AUDIO, 143424 }, + { 14, CDGE_FORMAT_AUDIO, 150212 }, + { 15, CDGE_FORMAT_AUDIO, 156807 }, + { 16, CDGE_FORMAT_AUDIO, 163988 }, + { 17, CDGE_FORMAT_AUDIO, 170768 }, + { 18, CDGE_FORMAT_AUDIO, 177616 }, + { 19, CDGE_FORMAT_AUDIO, 183555 }, + { 20, CDGE_FORMAT_AUDIO, 185138 }, + { 21, CDGE_FORMAT_AUDIO, 185557 }, + { 22, CDGE_FORMAT_AUDIO, 187160 }, + { 23, CDGE_FORMAT_AUDIO, 187507 }, + { 24, CDGE_FORMAT_AUDIO, 188122 }, + { 25, CDGE_FORMAT_AUDIO, 196158 }, + { 26, CDGE_FORMAT_AUDIO, 204161 }, + { 27, CDGE_FORMAT_AUDIO, 206297 }, + { 28, CDGE_FORMAT_AUDIO, 224106 }, + { 29, CDGE_FORMAT_AUDIO, 227826 }, + { 30, CDGE_FORMAT_AUDIO, 235934 }, + { 31, CDGE_FORMAT_AUDIO, 237516 }, + { -1, -1, 244557 }, + } + } +}, + +{ + "Pachio-kun FX", + "パチ夫くんFX", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_DATA, 5037 }, + { 4, CDGE_FORMAT_DATA, 7830 }, + { 5, CDGE_FORMAT_DATA, 157279 }, + { 6, CDGE_FORMAT_DATA, 158364 }, + { 7, CDGE_FORMAT_DATA, 180499 }, + { 8, CDGE_FORMAT_AUDIO, 205401 }, + { 9, CDGE_FORMAT_AUDIO, 206401 }, + { 10, CDGE_FORMAT_AUDIO, 219890 }, + { 11, CDGE_FORMAT_AUDIO, 224061 }, + { 12, CDGE_FORMAT_AUDIO, 228389 }, + { 13, CDGE_FORMAT_AUDIO, 232550 }, + { 14, CDGE_FORMAT_AUDIO, 239291 }, + { -1, -1, 253825 }, + } + } +}, + +{ + "PCE Fan Special CD-Rom Vol. 2", + "PC Engine Fan Special CDROM Vol.2", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 2938 }, + { 3, CDGE_FORMAT_DATA, 4778 }, + { 4, CDGE_FORMAT_DATA, 62548 }, + { 5, CDGE_FORMAT_DATA, 125244 }, + { 6, CDGE_FORMAT_AUDIO, 144311 }, + { 7, CDGE_FORMAT_AUDIO, 150877 }, + { 8, CDGE_FORMAT_AUDIO, 210027 }, + { 9, CDGE_FORMAT_AUDIO, 213928 }, + { 10, CDGE_FORMAT_AUDIO, 217629 }, + { 11, CDGE_FORMAT_AUDIO, 220644 }, + { 12, CDGE_FORMAT_AUDIO, 224512 }, + { 13, CDGE_FORMAT_AUDIO, 231187 }, + { 14, CDGE_FORMAT_AUDIO, 234309 }, + { 15, CDGE_FORMAT_AUDIO, 239072 }, + { -1, -1, 240786 }, + } + } +}, + +{ + "PCE Fan Special CD-Rom Vol. 3", + "PC Engine Fan Special CDROM Vol.3", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 1831 }, + { 3, CDGE_FORMAT_DATA, 5239 }, + { 4, CDGE_FORMAT_DATA, 12377 }, + { 5, CDGE_FORMAT_AUDIO, 25381 }, + { 6, CDGE_FORMAT_AUDIO, 29358 }, + { 7, CDGE_FORMAT_AUDIO, 35924 }, + { 8, CDGE_FORMAT_AUDIO, 93272 }, + { 9, CDGE_FORMAT_DATA, 144485 }, + { -1, -1, 146604 }, + } + } +}, + +{ + "Pia Carrot He Youkoso", + "PIAキャロットへようこそ!!", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 6923 }, + { 3, CDGE_FORMAT_AUDIO, 294030 }, + { 4, CDGE_FORMAT_AUDIO, 300770 }, + { 5, CDGE_FORMAT_AUDIO, 307627 }, + { -1, -1, 314486 }, + } + } +}, + +{ + "Power Dolls FX", + "パワードールFX", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_DATA, 5165 }, + { 4, CDGE_FORMAT_DATA, 5507 }, + { 5, CDGE_FORMAT_DATA, 6279 }, + { 6, CDGE_FORMAT_DATA, 6647 }, + { 7, CDGE_FORMAT_DATA, 7079 }, + { 8, CDGE_FORMAT_DATA, 9480 }, + { 9, CDGE_FORMAT_DATA, 9854 }, + { 10, CDGE_FORMAT_DATA, 10366 }, + { 11, CDGE_FORMAT_DATA, 11184 }, + { 12, CDGE_FORMAT_DATA, 11952 }, + { 13, CDGE_FORMAT_DATA, 12499 }, + { 14, CDGE_FORMAT_AUDIO, 33090 }, + { 15, CDGE_FORMAT_AUDIO, 47464 }, + { 16, CDGE_FORMAT_DATA, 67672 }, + { 17, CDGE_FORMAT_DATA, 68440 }, + { 18, CDGE_FORMAT_DATA, 73582 }, + { 19, CDGE_FORMAT_AUDIO, 74650 }, + { 20, CDGE_FORMAT_AUDIO, 86120 }, + { 21, CDGE_FORMAT_AUDIO, 115719 }, + { 22, CDGE_FORMAT_AUDIO, 130093 }, + { 23, CDGE_FORMAT_AUDIO, 143954 }, + { 24, CDGE_FORMAT_AUDIO, 162477 }, + { 25, CDGE_FORMAT_AUDIO, 180206 }, + { 26, CDGE_FORMAT_AUDIO, 192167 }, + { 27, CDGE_FORMAT_AUDIO, 208417 }, + { 28, CDGE_FORMAT_AUDIO, 218607 }, + { 29, CDGE_FORMAT_AUDIO, 238590 }, + { 30, CDGE_FORMAT_AUDIO, 258179 }, + { -1, -1, 258734 }, + } + } +}, + +{ + "Return to Zork", + "Return to Zork", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { -1, -1, 166265 }, + } + } +}, + +{ + "Ruruli Ra Rura", + "ルルリ・ラ・ルラ", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 3230 }, + { 3, CDGE_FORMAT_AUDIO, 158649 }, + { 4, CDGE_FORMAT_AUDIO, 169395 }, + { 5, CDGE_FORMAT_AUDIO, 184829 }, + { 6, CDGE_FORMAT_AUDIO, 197368 }, + { 7, CDGE_FORMAT_AUDIO, 210123 }, + { 8, CDGE_FORMAT_AUDIO, 223314 }, + { 9, CDGE_FORMAT_AUDIO, 237257 }, + { 10, CDGE_FORMAT_AUDIO, 251315 }, + { 11, CDGE_FORMAT_AUDIO, 266763 }, + { 12, CDGE_FORMAT_AUDIO, 281719 }, + { 13, CDGE_FORMAT_AUDIO, 295240 }, + { 14, CDGE_FORMAT_AUDIO, 295721 }, + { 15, CDGE_FORMAT_AUDIO, 296390 }, + { 16, CDGE_FORMAT_AUDIO, 297080 }, + { 17, CDGE_FORMAT_AUDIO, 297655 }, + { 18, CDGE_FORMAT_AUDIO, 298209 }, + { 19, CDGE_FORMAT_AUDIO, 301612 }, + { 20, CDGE_FORMAT_AUDIO, 303020 }, + { 21, CDGE_FORMAT_AUDIO, 303964 }, + { -1, -1, 322241 }, + } + } +}, + +{ + "Same Game FX", + NULL, + CDGE_FLAG_FXGA, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 3270 }, + { -1, -1, 3859 }, + } + } +}, + +{ + "Shanghai: The Great Wall", + "上海 万里の長城", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_AUDIO, 9611 }, + { 4, CDGE_FORMAT_AUDIO, 15872 }, + { 5, CDGE_FORMAT_AUDIO, 21570 }, + { 6, CDGE_FORMAT_AUDIO, 27001 }, + { 7, CDGE_FORMAT_AUDIO, 32506 }, + { 8, CDGE_FORMAT_AUDIO, 38675 }, + { 9, CDGE_FORMAT_AUDIO, 44503 }, + { 10, CDGE_FORMAT_AUDIO, 49867 }, + { 11, CDGE_FORMAT_AUDIO, 55328 }, + { 12, CDGE_FORMAT_AUDIO, 61187 }, + { 13, CDGE_FORMAT_AUDIO, 66669 }, + { 14, CDGE_FORMAT_AUDIO, 72147 }, + { 15, CDGE_FORMAT_AUDIO, 77654 }, + { 16, CDGE_FORMAT_AUDIO, 83325 }, + { 17, CDGE_FORMAT_AUDIO, 89000 }, + { 18, CDGE_FORMAT_AUDIO, 94305 }, + { 19, CDGE_FORMAT_AUDIO, 97879 }, + { 20, CDGE_FORMAT_AUDIO, 103439 }, + { 21, CDGE_FORMAT_AUDIO, 108929 }, + { 22, CDGE_FORMAT_AUDIO, 114689 }, + { 23, CDGE_FORMAT_AUDIO, 115109 }, + { 24, CDGE_FORMAT_AUDIO, 115553 }, + { 25, CDGE_FORMAT_AUDIO, 117222 }, + { 26, CDGE_FORMAT_AUDIO, 117672 }, + { 27, CDGE_FORMAT_AUDIO, 122812 }, + { 28, CDGE_FORMAT_AUDIO, 123948 }, + { 29, CDGE_FORMAT_AUDIO, 124631 }, + { -1, -1, 130162 }, + } + } +}, + +{ + "Neo Generation II FX", + "卒業II FX", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 13481 }, + { 3, CDGE_FORMAT_AUDIO, 209349 }, + { 4, CDGE_FORMAT_AUDIO, 215587 }, + { 5, CDGE_FORMAT_AUDIO, 227412 }, + { 6, CDGE_FORMAT_AUDIO, 239413 }, + { 7, CDGE_FORMAT_AUDIO, 243045 }, + { -1, -1, 243387 }, + } + } +}, + +{ + "Sparkling Feather", + "スパークリングフェザー", + CDGE_FLAG_ACCURATE_V810, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 1661 }, + { 3, CDGE_FORMAT_DATA, 182640 }, + { 4, CDGE_FORMAT_DATA, 183177 }, + { 5, CDGE_FORMAT_DATA, 183670 }, + { 6, CDGE_FORMAT_DATA, 184717 }, + { 7, CDGE_FORMAT_DATA, 185671 }, + { 8, CDGE_FORMAT_DATA, 187043 }, + { 9, CDGE_FORMAT_DATA, 187983 }, + { 10, CDGE_FORMAT_DATA, 188881 }, + { 11, CDGE_FORMAT_DATA, 189642 }, + { 12, CDGE_FORMAT_DATA, 194036 }, + { 13, CDGE_FORMAT_DATA, 195380 }, + { 14, CDGE_FORMAT_DATA, 196675 }, + { 15, CDGE_FORMAT_DATA, 197976 }, + { 16, CDGE_FORMAT_DATA, 199266 }, + { 17, CDGE_FORMAT_DATA, 200580 }, + { 18, CDGE_FORMAT_DATA, 201926 }, + { 19, CDGE_FORMAT_DATA, 203260 }, + { 20, CDGE_FORMAT_DATA, 204619 }, + { 21, CDGE_FORMAT_DATA, 206050 }, + { 22, CDGE_FORMAT_DATA, 209018 }, + { 23, CDGE_FORMAT_DATA, 211978 }, + { 24, CDGE_FORMAT_DATA, 214198 }, + { 25, CDGE_FORMAT_DATA, 215837 }, + { 26, CDGE_FORMAT_DATA, 217272 }, + { -1, -1, 218870 }, + } + } +}, + +{ + "Super PCE Fan Deluxe Special CD-Rom Vol. 1", + "SUPER PCエンジンファン DELUXE VOL.1", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_DATA, 52049 }, + { 4, CDGE_FORMAT_DATA, 61101 }, + { 5, CDGE_FORMAT_DATA, 62381 }, + { 6, CDGE_FORMAT_DATA, 156844 }, + { 7, CDGE_FORMAT_DATA, 228449 }, + { 8, CDGE_FORMAT_DATA, 249299 }, + { 9, CDGE_FORMAT_DATA, 254167 }, + { 10, CDGE_FORMAT_DATA, 266033 }, + { 11, CDGE_FORMAT_DATA, 276018 }, + { 12, CDGE_FORMAT_DATA, 282558 }, + { 13, CDGE_FORMAT_AUDIO, 299212 }, + { -1, -1, 305779 }, + } + } +}, + +{ + "Super PCE Fan Deluxe Special CD-Rom Vol. 2", + "SUPER PCエンジンファン DELUXE VOL.2", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_DATA, 10251 }, + { 4, CDGE_FORMAT_DATA, 26494 }, + { 5, CDGE_FORMAT_DATA, 51756 }, + { 6, CDGE_FORMAT_DATA, 57179 }, + { 7, CDGE_FORMAT_DATA, 68238 }, + { 8, CDGE_FORMAT_DATA, 76683 }, + { 9, CDGE_FORMAT_DATA, 82991 }, + { 10, CDGE_FORMAT_DATA, 89072 }, + { 11, CDGE_FORMAT_DATA, 96938 }, + { 12, CDGE_FORMAT_DATA, 104587 }, + { 13, CDGE_FORMAT_DATA, 115120 }, + { 14, CDGE_FORMAT_DATA, 137766 }, + { 15, CDGE_FORMAT_DATA, 169372 }, + { 16, CDGE_FORMAT_DATA, 191451 }, + { 17, CDGE_FORMAT_AUDIO, 193018 }, + { 18, CDGE_FORMAT_AUDIO, 206955 }, + { 19, CDGE_FORMAT_AUDIO, 223517 }, + { 20, CDGE_FORMAT_AUDIO, 229959 }, + { 21, CDGE_FORMAT_AUDIO, 236580 }, + { 22, CDGE_FORMAT_AUDIO, 242078 }, + { 23, CDGE_FORMAT_AUDIO, 247002 }, + { 24, CDGE_FORMAT_DATA, 260851 }, + { 25, CDGE_FORMAT_DATA, 273544 }, + { 26, CDGE_FORMAT_AUDIO, 304547 }, + { -1, -1, 311114 }, + } + } +}, + +{ + "Super Power League FX", + "スーパーパワーリーグFX", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 3590 }, + { 3, CDGE_FORMAT_DATA, 4344 }, + { 4, CDGE_FORMAT_AUDIO, 14637 }, + { 5, CDGE_FORMAT_DATA, 102512 }, + { -1, -1, 166532 }, + } + } +}, + +{ + "Super Real Mahjong P V FX", + "スーパーリアル麻雀P5", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 2590 }, + { 3, CDGE_FORMAT_DATA, 2976 }, + { 4, CDGE_FORMAT_DATA, 3350 }, + { 5, CDGE_FORMAT_DATA, 89885 }, + { 6, CDGE_FORMAT_DATA, 90695 }, + { 7, CDGE_FORMAT_AUDIO, 94331 }, + { 8, CDGE_FORMAT_AUDIO, 105090 }, + { 9, CDGE_FORMAT_AUDIO, 114148 }, + { 10, CDGE_FORMAT_AUDIO, 125335 }, + { 11, CDGE_FORMAT_AUDIO, 136932 }, + { 12, CDGE_FORMAT_AUDIO, 147480 }, + { 13, CDGE_FORMAT_AUDIO, 160406 }, + { 14, CDGE_FORMAT_AUDIO, 171875 }, + { 15, CDGE_FORMAT_AUDIO, 178636 }, + { 16, CDGE_FORMAT_AUDIO, 180766 }, + { -1, -1, 183547 }, + } + } +}, + + +{ + "Team Innocent", + "チームイノセント The point of no return", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_AUDIO, 218041 }, + { 4, CDGE_FORMAT_AUDIO, 241208 }, + { 5, CDGE_FORMAT_AUDIO, 247705 }, + { 6, CDGE_FORMAT_AUDIO, 254586 }, + { 7, CDGE_FORMAT_AUDIO, 260797 }, + { 8, CDGE_FORMAT_AUDIO, 267971 }, + { 9, CDGE_FORMAT_AUDIO, 275080 }, + { 10, CDGE_FORMAT_AUDIO, 285655 }, + { 11, CDGE_FORMAT_AUDIO, 289111 }, + { 12, CDGE_FORMAT_AUDIO, 300772 }, + { 13, CDGE_FORMAT_AUDIO, 305057 }, + { 14, CDGE_FORMAT_AUDIO, 317734 }, + { -1, -1, 324695 }, + } + } +}, + +{ + "Tekipaki Working Love", + "はたらく☆少女 てきぱきワーキンラブ FX", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 2474 }, + { 3, CDGE_FORMAT_DATA, 3500 }, + { 4, CDGE_FORMAT_AUDIO, 181612 }, + { 5, CDGE_FORMAT_AUDIO, 194776 }, + { -1, -1, 211869 }, + }, + } +}, + +{ + "Tenchi Muyo FX", + "天地無用! 魎皇鬼 FX", + 0, + 2, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4135 }, + { -1, -1, 301916 }, + }, + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4621 }, + { -1, -1, 74055 }, + } + } +}, + +{ + "Tengai Makyou Karakuri Kakutouden", + "天外魔境電脳結繰格闘伝", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_DATA, 9515 }, + { 4, CDGE_FORMAT_DATA, 27125 }, + { 5, CDGE_FORMAT_DATA, 49259 }, + { 6, CDGE_FORMAT_DATA, 74712 }, + { 7, CDGE_FORMAT_DATA, 101125 }, + { 8, CDGE_FORMAT_DATA, 129079 }, + { 9, CDGE_FORMAT_DATA, 156743 }, + { 10, CDGE_FORMAT_DATA, 192741 }, + { 11, CDGE_FORMAT_DATA, 215240 }, + { 12, CDGE_FORMAT_DATA, 240217 }, + { 13, CDGE_FORMAT_DATA, 260898 }, + { -1, -1, 279663 }, + } + } +}, + +{ + "Tokimeki Card Paradise", + "ときめきカードパラダイス", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_AUDIO, 9731 }, + { 4, CDGE_FORMAT_AUDIO, 10223 }, + { 5, CDGE_FORMAT_AUDIO, 10676 }, + { 6, CDGE_FORMAT_AUDIO, 11182 }, + { 7, CDGE_FORMAT_AUDIO, 11813 }, + { 8, CDGE_FORMAT_AUDIO, 12472 }, + { 9, CDGE_FORMAT_AUDIO, 18522 }, + { 10, CDGE_FORMAT_AUDIO, 31943 }, + { 11, CDGE_FORMAT_AUDIO, 45364 }, + { 12, CDGE_FORMAT_AUDIO, 58258 }, + { 13, CDGE_FORMAT_AUDIO, 69409 }, + { 14, CDGE_FORMAT_AUDIO, 80560 }, + { 15, CDGE_FORMAT_AUDIO, 91711 }, + { 16, CDGE_FORMAT_AUDIO, 95691 }, + { 17, CDGE_FORMAT_DATA, 99896 }, + { 18, CDGE_FORMAT_DATA, 105396 }, + { 19, CDGE_FORMAT_DATA, 106280 }, + { 20, CDGE_FORMAT_DATA, 107391 }, + { 21, CDGE_FORMAT_DATA, 108375 }, + { 22, CDGE_FORMAT_DATA, 109360 }, + { 23, CDGE_FORMAT_DATA, 110659 }, + { 24, CDGE_FORMAT_DATA, 111616 }, + { 25, CDGE_FORMAT_DATA, 113281 }, + { 26, CDGE_FORMAT_DATA, 113745 }, + { 27, CDGE_FORMAT_DATA, 115386 }, + { 28, CDGE_FORMAT_DATA, 119952 }, + { 29, CDGE_FORMAT_DATA, 121377 }, + { 30, CDGE_FORMAT_DATA, 123944 }, + { 31, CDGE_FORMAT_DATA, 125477 }, + { 32, CDGE_FORMAT_DATA, 126472 }, + { 33, CDGE_FORMAT_DATA, 127545 }, + { 34, CDGE_FORMAT_DATA, 128993 }, + { 35, CDGE_FORMAT_DATA, 130345 }, + { 36, CDGE_FORMAT_DATA, 132774 }, + { 37, CDGE_FORMAT_DATA, 133299 }, + { 38, CDGE_FORMAT_DATA, 134847 }, + { 39, CDGE_FORMAT_DATA, 139225 }, + { 40, CDGE_FORMAT_DATA, 142224 }, + { 41, CDGE_FORMAT_DATA, 143967 }, + { 42, CDGE_FORMAT_DATA, 145100 }, + { 43, CDGE_FORMAT_DATA, 146002 }, + { 44, CDGE_FORMAT_DATA, 147556 }, + { 45, CDGE_FORMAT_DATA, 148650 }, + { 46, CDGE_FORMAT_DATA, 150461 }, + { 47, CDGE_FORMAT_DATA, 151891 }, + { 48, CDGE_FORMAT_DATA, 152514 }, + { 49, CDGE_FORMAT_DATA, 153988 }, + { 50, CDGE_FORMAT_DATA, 157541 }, + { 51, CDGE_FORMAT_DATA, 158440 }, + { 52, CDGE_FORMAT_DATA, 161036 }, + { 53, CDGE_FORMAT_DATA, 162032 }, + { 54, CDGE_FORMAT_DATA, 162793 }, + { 55, CDGE_FORMAT_DATA, 164308 }, + { 56, CDGE_FORMAT_DATA, 165420 }, + { 57, CDGE_FORMAT_DATA, 167152 }, + { 58, CDGE_FORMAT_DATA, 168284 }, + { 59, CDGE_FORMAT_DATA, 169303 }, + { 60, CDGE_FORMAT_DATA, 169718 }, + { 61, CDGE_FORMAT_DATA, 173432 }, + { 62, CDGE_FORMAT_DATA, 177351 }, + { 63, CDGE_FORMAT_DATA, 178477 }, + { 64, CDGE_FORMAT_DATA, 179599 }, + { 65, CDGE_FORMAT_DATA, 180174 }, + { 66, CDGE_FORMAT_DATA, 181222 }, + { 67, CDGE_FORMAT_DATA, 182108 }, + { 68, CDGE_FORMAT_DATA, 182838 }, + { 69, CDGE_FORMAT_DATA, 183745 }, + { 70, CDGE_FORMAT_DATA, 184759 }, + { 71, CDGE_FORMAT_DATA, 185526 }, + { -1, -1, 186254 }, + } + } +}, + +{ + "Tonari no Princess Rolfee", + "となりのプリンセス ロルフィー", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 1431 }, + { -1, -1, 330127 }, + } + } +}, + +{ + "Tyoushin Heiki Zeroigar", + "超神兵器セロイガー", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 1982 }, + { 3, CDGE_FORMAT_AUDIO, 180819 }, + { 4, CDGE_FORMAT_AUDIO, 193570 }, + { 5, CDGE_FORMAT_AUDIO, 213671 }, + { -1, -1, 269144 }, + } + } +}, + + +{ + "Voice Paradise", + "ボイスパラダイス", + 0, + 2, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_DATA, 5165 }, + { 4, CDGE_FORMAT_DATA, 19592 }, + { 5, CDGE_FORMAT_AUDIO, 149410 }, + { 6, CDGE_FORMAT_AUDIO, 159592 }, + { 7, CDGE_FORMAT_AUDIO, 164141 }, + { 8, CDGE_FORMAT_AUDIO, 180174 }, + { 9, CDGE_FORMAT_AUDIO, 195993 }, + { 10, CDGE_FORMAT_AUDIO, 212538 }, + { 11, CDGE_FORMAT_AUDIO, 217086 }, + { 12, CDGE_FORMAT_AUDIO, 232175 }, + { 13, CDGE_FORMAT_AUDIO, 253002 }, + { 14, CDGE_FORMAT_AUDIO, 268287 }, + { 15, CDGE_FORMAT_AUDIO, 283159 }, + { 16, CDGE_FORMAT_AUDIO, 284331 }, + { 17, CDGE_FORMAT_AUDIO, 285878 }, + { 18, CDGE_FORMAT_AUDIO, 300515 }, + { -1, -1, 300890 }, + }, + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { 3, CDGE_FORMAT_DATA, 5165 }, + { 4, CDGE_FORMAT_DATA, 19592 }, + { 5, CDGE_FORMAT_AUDIO, 153285 }, + { 6, CDGE_FORMAT_AUDIO, 163467 }, + { 7, CDGE_FORMAT_AUDIO, 168016 }, + { 8, CDGE_FORMAT_AUDIO, 184049 }, + { 9, CDGE_FORMAT_AUDIO, 199868 }, + { 10, CDGE_FORMAT_AUDIO, 216413 }, + { 11, CDGE_FORMAT_AUDIO, 220961 }, + { 12, CDGE_FORMAT_AUDIO, 236050 }, + { 13, CDGE_FORMAT_AUDIO, 256877 }, + { 14, CDGE_FORMAT_AUDIO, 272162 }, + { 15, CDGE_FORMAT_AUDIO, 287034 }, + { 16, CDGE_FORMAT_AUDIO, 288206 }, + { 17, CDGE_FORMAT_AUDIO, 289753 }, + { 18, CDGE_FORMAT_AUDIO, 304390 }, + { -1, -1, 304765 }, + } + } +}, + +{ + "Wakusei-koukitai Little Cats", + "惑星攻機隊りとるキャッツ", + 0, + 1, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 5163 }, + { 3, CDGE_FORMAT_DATA, 5933 }, + { 4, CDGE_FORMAT_DATA, 95878 }, + { 5, CDGE_FORMAT_AUDIO, 209401 }, + { 6, CDGE_FORMAT_AUDIO, 213777 }, + { 7, CDGE_FORMAT_AUDIO, 219527 }, + { 8, CDGE_FORMAT_AUDIO, 225871 }, + { 9, CDGE_FORMAT_AUDIO, 232693 }, + { 10, CDGE_FORMAT_AUDIO, 242452 }, + { 11, CDGE_FORMAT_AUDIO, 248960 }, + { 12, CDGE_FORMAT_AUDIO, 255249 }, + { 13, CDGE_FORMAT_AUDIO, 265891 }, + { 14, CDGE_FORMAT_AUDIO, 268646 }, + { 15, CDGE_FORMAT_AUDIO, 282287 }, + { 16, CDGE_FORMAT_AUDIO, 282743 }, + { -1, -1, 283044 }, + } + } +}, + +{ + "Zoku Hakutoi Monogatari", + "続初恋物語", + 0, + 2, + { + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { -1, -1, 332106 }, + }, + { + { 1, CDGE_FORMAT_AUDIO, 0 }, + { 2, CDGE_FORMAT_DATA, 4395 }, + { -1, -1, 333025 }, + } + } +}, + diff --git a/waterbox/pcfx/huc6270/vdc.cpp b/waterbox/pcfx/huc6270/vdc.cpp new file mode 100644 index 0000000000..ffe33ecfdd --- /dev/null +++ b/waterbox/pcfx/huc6270/vdc.cpp @@ -0,0 +1,1796 @@ +/* Mednafen - Multi-system Emulator + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* VDC emulation */ + +#include "../defs.h" +//#include + +#include +#include "vdc.h" + +#define VDC_DEBUG(x, ...) { } +//#define VDC_DEBUG(x, ...) printf(x ": HPhase=%d, HPhaseCounter=%d, RCRCount=%d\n", ## __VA_ARGS__, HPhase, HPhaseCounter, RCRCount); + +#define VDC_UNDEFINED(format, ...) { } +//#define VDC_UNDEFINED(format, ...) printf(format " RCRCount=%d" "\n", ## __VA_ARGS__, RCRCount) + +#define VDC_WARNING(format, ...) { } +//#define VDC_WARNING(format, ...) { printf(format "\n", ## __VA_ARGS__); } + +#define ULE_BG 1 +#define ULE_SPR 2 + +static const unsigned int bat_width_tab[4] = { 32, 64, 128, 128 }; +static const unsigned int bat_width_shift_tab[4] = { 5, 6, 7, 7 }; +static const unsigned int bat_height_tab[2] = { 32, 64 }; + +void VDC::FixTileCache(uint16 A) +{ + uint32 charname = (A >> 4); + uint32 y = (A & 0x7); + uint8 *tc = bg_tile_cache[charname][y]; + + uint32 bitplane01 = VRAM[y + charname * 16]; + uint32 bitplane23 = VRAM[y+ 8 + charname * 16]; + + for(int x = 0; x < 8; x++) + { + uint32 raw_pixel = ((bitplane01 >> x) & 1); + raw_pixel |= ((bitplane01 >> (x + 8)) & 1) << 1; + raw_pixel |= ((bitplane23 >> x) & 1) << 2; + raw_pixel |= ((bitplane23 >> (x + 8)) & 1) << 3; + tc[7 - x] = raw_pixel; + } +} + +// Some virtual vdc macros to make code simpler to read +#define M_vdc_HSW (HSR & 0x1F) // Horizontal Synchro Width +#define M_vdc_HDS ((HSR >> 8) & 0x7F) // Horizontal Display Start +#define M_vdc_HDW (HDR & 0x7F) // Horizontal Display Width +#define M_vdc_HDE ((HDR >> 8) & 0x7F) // Horizontal Display End + +#define M_vdc_VSW (VSR & 0x1F) // Vertical synchro width +#define M_vdc_VDS ((VSR >> 8) & 0xFF) // Vertical Display Start +#define M_vdc_VDW (VDR & 0x1FF) // Vertical Display Width(Height? :b) +#define M_vdc_VCR (VCR & 0xFF) + +#define M_vdc_EX ((CR >> 4) & 0x3) +#define M_vdc_TE ((CR >> 8) & 0x3) + +#define VDCS_CR 0x01 // Sprite #0 collision interrupt occurred +#define VDCS_OR 0x02 // sprite overflow "" "" +#define VDCS_RR 0x04 // RCR "" "" +#define VDCS_DS 0x08 // VRAM to SAT DMA completion interrupt occurred +#define VDCS_DV 0x10 // VRAM to VRAM DMA completion interrupt occurred +#define VDCS_VD 0x20 // Vertical blank interrupt occurred +#define VDCS_BSY 0x40 // VDC is waiting for a CPU access slot during the active display area?? + +uint32 VDC::GetRegister(const unsigned int id, char *special, const uint32 special_len) +{ + uint32 value = 0xDEADBEEF; + + switch(id) + { + case GSREG_SELECT: + value = select; + break; + + case GSREG_STATUS: + value = status; + break; + + case GSREG_MAWR: + value = MAWR; + break; + + case GSREG_MARR: + value = MARR; + break; + + case GSREG_CR: + value = CR; + + if(special) + { + trio_snprintf(special, special_len, "Sprite Hit IRQ: %s, Sprite Overflow IRQ: %s, RCR IRQ: %s, VBlank IRQ: %s, Sprites: %s, Background: %s", (value & 1) ? "On" : "Off", (value & 2) ? "On" : "Off", + (value & 4) ? "On" : "Off", (value & 8) ? "On" : "Off", (value & 0x40) ? "On" : "Off", (value & 0x80) ? "On" : "Off"); + } + break; + + case GSREG_RCR: + value = RCR; + break; + + case GSREG_BXR: + value = BXR; + break; + + case GSREG_BYR: + value = BYR; + break; + + case GSREG_MWR: + value = MWR; + + if(special) + { + trio_snprintf(special, special_len, "CG Mode: %d, BAT Width: %d(tiles), BAT Height: %d(tiles)", (int)(bool)(value & 0x80), + bat_width_tab[(value >> 4) & 0x3], + bat_height_tab[(value >> 6) & 0x1]); + } + break; + + case GSREG_HSR: + value = HSR; + if(special) + { + trio_snprintf(special, special_len, "HSW: %02x, HDS: %02x", value & 0x1F, (value >> 8) & 0x7F); + } + break; + + case GSREG_HDR: + value = HDR; + if(special) + { + trio_snprintf(special, special_len, "HDW: %02x, HDE: %02x", value & 0x7F, (value >> 8) & 0x7F); + } + break; + + + case GSREG_VSR: + value = VSR; + if(special) + { + trio_snprintf(special, special_len, "VSW: %02x, VDS: %02x", value & 0x1F, (value >> 8) & 0xFF); + } + break; + + case GSREG_VDR: + value = VDR; + break; + + case GSREG_VCR: + value = VCR; + break; + + case GSREG_DCR: + value = DCR; + if(special) + { + trio_snprintf(special, special_len, "SATB DMA IRQ: %s, VRAM DMA IRQ: %s, DMA Source Address: %s, DMA Dest Address: %s, Auto SATB DMA: %s", + (DCR & 0x1) ? "On" : "Off", (DCR & 0x2) ? "On" : "Off", (DCR & 0x4) ? "Decrement" : "Increment", (DCR & 0x8) ? "Decrement" : "Increment", + (DCR & 0x10) ? "On" : "Off"); + } + break; + + case GSREG_SOUR: + value = SOUR; + break; + + case GSREG_DESR: + value = DESR; + break; + + case GSREG_LENR: + value = LENR; + break; + + case GSREG_DVSSR: + value = DVSSR; + break; + } + + return(value); +} + +void VDC::SetRegister(const unsigned int id, const uint32 value) +{ + switch(id) + { + default: break; + + case GSREG_STATUS: + status = value & 0x3F; + break; + + case GSREG_SELECT: + select = value & 0x1F; + break; + + case GSREG_MAWR: + MAWR = value & 0xFFFF; + break; + + case GSREG_MARR: + MARR = value & 0xFFFF; + break; + + case GSREG_CR: + CR = value & 0xFFFF; + break; + + case GSREG_RCR: + RCR = value & 0x3FF; + break; + + case GSREG_BXR: + BXR = value & 0x3FF; + break; + + case GSREG_BYR: + BYR = value & 0x1FF; + break; + + case GSREG_MWR: + MWR = value & 0xFFFF; + break; + + case GSREG_HSR: + HSR = value & 0xFFFF; + break; + + case GSREG_HDR: + HDR = value & 0xFFFF; + break; + + case GSREG_VSR: + VSR = value & 0xFFFF; + break; + + case GSREG_VDR: + VDR = value & 0xFFFF; + break; + + case GSREG_VCR: + VCR = value & 0xFFFF; + break; + + case GSREG_DCR: + DCR = value & 0xFFFF; + break; + + case GSREG_SOUR: + SOUR = value & 0xFFFF; + break; + + case GSREG_DESR: + DESR = value & 0xFFFF; + break; + + case GSREG_LENR: + LENR = value & 0xFFFF; + break; + + case GSREG_DVSSR: + DVSSR = value & 0xFFFF; + break; + } +} + +void VDC::SetLayerEnableMask(uint64 mask) +{ + userle = mask; +} + +void VDC::RunSATDMA(int32 cycles, bool force_completion) +{ + assert(sat_dma_counter > 0); + + if(force_completion) + cycles = sat_dma_counter; + + sat_dma_counter -= cycles; + if(sat_dma_counter <= 0) + { + if(DCR & 0x01) + { + VDC_DEBUG("Sprite DMA IRQ"); + status |= VDCS_DS; + IRQHook(TRUE); + } + CheckAndCommitPending(); + burst_mode = true; + } +} + +void VDC::RunDMA(int32 cycles, bool force_completion) +{ + int num_transfers = 0; + + if(force_completion) + { + VDMA_CycleCounter = 0; + + num_transfers = 65536 * 2; + } + else + { + VDMA_CycleCounter += cycles; + num_transfers = VDMA_CycleCounter >> 1; + VDMA_CycleCounter -= num_transfers << 1; + } + + while(num_transfers--) + { + if(!DMAReadWrite) + { + if(SOUR >= VRAM_Size) + VDC_UNDEFINED("Unmapped VRAM DMA read"); + + DMAReadBuffer = VRAM[SOUR]; + //printf("DMA Read: %04x, %04x\n", SOUR, DMAReadBuffer); + } + else + { + if(DESR < VRAM_Size) + { + VRAM[DESR] = DMAReadBuffer; + FixTileCache(DESR); + } + + SOUR += (((DCR & 0x4) >> 1) ^ 2) - 1; + DESR += (((DCR & 0x8) >> 2) ^ 2) - 1; + LENR--; + + if(LENR == 0xFFFF) // DMA is done. + { + DMARunning = 0; // Clear this BEFORE CheckAndCommitPending() + + CheckAndCommitPending(); + + if(DCR & 0x02) + { + status |= VDCS_DV; + IRQHook(TRUE); + VDC_DEBUG("DMA IRQ"); + } + break; + } + } + DMAReadWrite ^= 1; + } +} + +/* + ChrlyMac: Was it you who determined exactly how many VDC clocks the SAT DMA took? + I know someone did, but I can't remember the results... + 1024 + It happens at the VDW->VCR transition +*/ + +void VDC::IncRCR(void) +{ + if(NeedBGYInc) + { + NeedBGYInc = false; + if(0 == RCRCount) + BG_YMoo = BYR; + else + BG_YMoo++; + } + + NeedBGYInc = true; + RCRCount++; + + VPhaseCounter--; + + if(VPhaseCounter <= 0) + { + VPhase = (VPhase + 1) % VPHASE_COUNT; + switch(VPhase) + { + case VPHASE_VDS: VPhaseCounter = VDS_cache + 2; + break; + + case VPHASE_VDW: VPhaseCounter = VDW_cache + 1; + //BG_YMoo = BYR - 1; + RCRCount = 0; + burst_mode = !(CR & 0xC0); + NeedVBIRQTest = true; + NeedSATDMATest = true; + + if(!burst_mode) + { + if(sat_dma_counter > 0) + { + printf("SAT DMA cancelled???\n"); + sat_dma_counter = 0; + CheckAndCommitPending(); + } + if(DMARunning) + { + printf("DMA Running Cancelled\n"); + DMARunning = false; + CheckAndCommitPending(); + } + } + break; + + case VPHASE_VCR: VPhaseCounter = VCR_cache; + break; + + case VPHASE_VSW: VPhaseCounter = VSW_cache + 1; + MWR_cache = MWR; + VDS_cache = M_vdc_VDS; + VSW_cache = M_vdc_VSW; + VDW_cache = M_vdc_VDW; + VCR_cache = M_vdc_VCR; + //VDC_WARNING("VSW Started"); + break; + } + } + + if(VPhase == VPHASE_VDW && !burst_mode) + { + FetchSpriteData(); + } + + if((int)RCRCount == ((int)RCR - 0x40) && (CR & 0x04)) + { + VDC_DEBUG("RCR IRQ"); + status |= VDCS_RR; + IRQHook(TRUE); + } +} + +void VDC::DoVBIRQTest(void) +{ + if(CR & 0x08) + { + VDC_DEBUG("VBlank IRQ"); + status |= VDCS_VD; + IRQHook(TRUE); + } +} + +static const int Cycles_Between_RCRIRQ_And_HDWEnd = 4; + +int VDC::TimeFromHDSStartToBYRLatch(void) +{ + int ret = 1; + + if(HDS_cache > 2) + ret += ((HDS_cache + 1) * 8) - 24 + 2; + + + //printf("%d, %d\n", HDS_cache, ret); + + return(ret); +} + +int VDC::TimeFromBYRLatchToBXRLatch(void) +{ + int ret = 2; + + if(HDS_cache > 2) + ret = 1; + + return(ret); +} + +void VDC::HDS_Start(void) +{ + if(NeedRCRInc) + { + IncRCR(); + NeedRCRInc = false; + } + + if(sprite_cg_fetch_counter > 0) + { + VDC_WARNING("Sprite truncation on %d. Wanted sprites: %d, cycles needed but not left: %d\n", RCRCount, active_sprites, sprite_cg_fetch_counter); + sprite_cg_fetch_counter = 0; + CheckAndCommitPending(); + } + + HSW_cache = M_vdc_HSW; + HDS_cache = M_vdc_HDS; + HDW_cache = M_vdc_HDW; + HDE_cache = M_vdc_HDE; + + VDC_DEBUG("HDS Start! HSW: %d, HDW: %d, HDW: %d, HDE: %d\n", HSW_cache, HDS_cache, HDW_cache, HDE_cache); + + CR_cache = CR; + + HPhase = HPHASE_HDS; + HPhaseCounter = TimeFromHDSStartToBYRLatch(); +} + +int32 VDC::HSync(bool hb) +{ + if(M_vdc_EX) + { + in_exhsync = 0; + return(CalcNextEvent()); + } + in_exhsync = hb; + + if(hb) // Going into hsync + { + mystery_counter = 48; + mystery_phase = false; + } + else // Leaving hsync + { + HPhase = HPHASE_HSW; + HPhaseCounter = 8; + + //HDS_Start(); + //HPhaseCounter += 8; + + pixel_copy_count = 0; + } + + + return(CalcNextEvent()); +} + +int32 VDC::VSync(bool vb) +{ + if(M_vdc_EX >= 0x2) + { + in_exvsync = 0; + return(CalcNextEvent()); + } + in_exvsync = vb; + + //printf("VBlank: %d\n", vb); + if(vb) // Going into vsync + { + NeedRCRInc = false; + NeedBGYInc = false; +/* if(NeedRCRInc) + { + IncRCR(); + NeedRCRInc = false; + } +*/ + MWR_cache = MWR; + + VDS_cache = M_vdc_VDS; + VSW_cache = M_vdc_VSW; + VDW_cache = M_vdc_VDW; + VCR_cache = M_vdc_VCR; + + VPhase = VPHASE_VSW; + VPhaseCounter = VSW_cache + 1; + } + else // Leaving vsync + { + + } + return(CalcNextEvent()); +} + +//int32 VDC::Run(int32 clocks, bool hs, bool vs, uint16 *pixels, bool skip) +int32 VDC::Run(int32 clocks, uint16 *pixels, bool skip) +{ + //uint16 *spixels = pixels; + + //puts("Run begin"); + //fflush(stdout); + + while(clocks > 0) + { + int32 chunk_clocks = clocks; + + if(chunk_clocks > HPhaseCounter) + { + chunk_clocks = HPhaseCounter; + } + + if(sat_dma_counter > 0 && chunk_clocks > sat_dma_counter) + chunk_clocks = sat_dma_counter; + + if(sprite_cg_fetch_counter > 0 && chunk_clocks > sprite_cg_fetch_counter) + chunk_clocks = sprite_cg_fetch_counter; + + if(mystery_counter > 0 && chunk_clocks > mystery_counter) + chunk_clocks = mystery_counter; + + if(mystery_counter > 0) + { + mystery_counter -= chunk_clocks; + if(mystery_counter <= 0) + { + mystery_phase = !mystery_phase; + if(mystery_phase) + mystery_counter = 16; + else + CheckAndCommitPending(); + } + } + + if(sprite_cg_fetch_counter > 0) + { + sprite_cg_fetch_counter -= chunk_clocks; + if(sprite_cg_fetch_counter <= 0) + CheckAndCommitPending(); + } + + if(VPhase != VPHASE_VDW) + { + if(NeedSATDMATest) + { + NeedSATDMATest = false; + if(SATBPending || (DCR & 0x10)) + { + SATBPending = 0; + + sat_dma_counter = 1024; + + if(DVSSR > (VRAM_Size - 0x100)) + VDC_UNDEFINED("Unmapped VRAM DVSSR DMA read"); + + if(DVSSR < VRAM_Size) + { + uint32 len = 256; + if(DVSSR > (VRAM_Size - 0x100)) + len = VRAM_Size - DVSSR; + memcpy(SAT, &VRAM[DVSSR], len * sizeof(uint16)); + } + } + else + burst_mode = true; + } + } + + + + + if(DMAPending && burst_mode) + { + VDC_DEBUG("DMA Started"); + DMAPending = false; + DMARunning = true; + VDMA_CycleCounter = 0; + DMAReadWrite = 0; + } + + if(sat_dma_counter > 0) + RunSATDMA(chunk_clocks); + else if(DMARunning) + RunDMA(chunk_clocks); + + if(pixel_copy_count > 0) + { + if(!skip) + { + for(int i = 0; i < chunk_clocks; i++) + pixels[i] = linebuf[pixel_desu + i]; + //memcpy(pixels, linebuf + pixel_desu, chunk_clocks * sizeof(uint16)); + + if(M_vdc_TE == 0x1) + for(int i = 0; i < chunk_clocks; i++) + pixels[i] |= VDC_DISP_OUT_MASK; + } + + pixel_desu += chunk_clocks; + pixel_copy_count -= chunk_clocks; + } + else + { + uint16 pix = 0x100; + + if(M_vdc_TE == 0x1) + { + if(HPhase != HPHASE_HDS && HPhase != HPHASE_HDS_PART2 && HPhase != HPHASE_HDS_PART3) + pix |= VDC_DISP_OUT_MASK; + } + + if(HPhase == HPHASE_HSW) + { + if(M_vdc_EX >= 0x1) + pix |= VDC_HSYNC_OUT_MASK; + + if(M_vdc_TE >= 0x2) + pix |= VDC_DISP_OUT_MASK; + } + if(VPhase == VPHASE_VSW && M_vdc_EX >= 0x2) + pix |= VDC_VSYNC_OUT_MASK; + + if(!(userle & 1)) + pix |= VDC_BGDISABLE_OUT_MASK; + + if(!skip) + { + for(int i = 0; i < chunk_clocks; i++) + pixels[i] = pix; + } + } + + HPhaseCounter -= chunk_clocks; + + assert(HPhaseCounter >= 0); + + while(HPhaseCounter <= 0) + { + HPhase = (HPhase + 1) % HPHASE_COUNT; + + switch(HPhase) + { + case HPHASE_HDS: HDS_Start(); + break; + + + case HPHASE_HDS_PART2: + HPhaseCounter = TimeFromBYRLatchToBXRLatch(); + + if(NeedBGYInc && !in_exhsync) + { + NeedBGYInc = false; + + if(0 == RCRCount) + BG_YMoo = BYR; + else + BG_YMoo++; + } + BG_YOffset = BG_YMoo; + break; + + case HPHASE_HDS_PART3: + HPhaseCounter = (HDS_cache + 1) * 8 - TimeFromHDSStartToBYRLatch() - TimeFromBYRLatchToBXRLatch(); + + assert(HPhaseCounter > 0); + + BG_XOffset = BXR; + break; + + case HPHASE_HDW: + NeedRCRInc = true; + if(VPhase != VPHASE_VDW && NeedVBIRQTest) + { + DoVBIRQTest(); + NeedVBIRQTest = false; + } + CheckAndCommitPending(); + + HPhaseCounter = (HDW_cache + 1) * 8 - Cycles_Between_RCRIRQ_And_HDWEnd; + if(VPhase == VPHASE_VDW) + { + if(!burst_mode) + { + pixel_desu = 0; + pixel_copy_count = (HDW_cache + 1) * 8; + + // BG off, sprite on: fill = 0x100. bg off, sprite off: fill = 0x000 + if(!(CR_cache & 0x80)) + { + uint16 fill_val; + + if(!(CR_cache & 0xC0)) // Sprites and BG off + fill_val = 0x000; + else // Only BG off + fill_val = 0x100 | ((userle & ULE_BG) ? 0 : VDC_BGDISABLE_OUT_MASK); + + if(!(userle & ULE_BG)) + fill_val |= VDC_BGDISABLE_OUT_MASK; + + for(int i = 0; i < pixel_copy_count; i++) + linebuf[i] = fill_val; + } + + if(!skip) + if(CR_cache & 0x80) + { + DrawBG(linebuf, userle & ULE_BG); + } + //printf("%d %02x %02x\n", RCRCount, CR, CR_cache); + if(CR_cache & 0x40) + DrawSprites(linebuf, (userle & ULE_SPR) && !skip); + } + } + break; + + case HPHASE_HDW_FINAL: + if(NeedRCRInc) + { + IncRCR(); + NeedRCRInc = false; + } + HPhaseCounter = Cycles_Between_RCRIRQ_And_HDWEnd; + break; + + case HPHASE_HDE: //if(!burst_mode) //if(VPhase == VPHASE_VDW) //if(!burst_mode) + // lastats = 16; // + 16; + //else + // lastats = 16; + HPhaseCounter = (HDE_cache + 1) * 8; + break; + + case HPHASE_HSW: HPhaseCounter = (HSW_cache + 1) * 8; break; + } + } + pixels += chunk_clocks; + clocks -= chunk_clocks; + } + + //puts("Run end"); + //fflush(stdout); + + return(CalcNextEvent()); +} + + +void VDC::CalcWidthStartEnd(uint32 &display_width, uint32 &start, uint32 &end) +{ + display_width = (M_vdc_HDW + 1) * 8; + + start = 0; + end = start + display_width; +} + +void VDC::DrawBG(uint16 *target, int enabled) +{ + uint32 width; + uint32 start; + uint32 end; + int bat_width = bat_width_tab[(MWR_cache >> 4) & 3]; + int bat_width_mask = bat_width - 1; + int bat_width_shift = bat_width_shift_tab[(MWR_cache >> 4) & 3]; + int bat_height_mask = bat_height_tab[(MWR_cache >> 6) & 1] - 1; + + CalcWidthStartEnd(width, start, end); + + if(!enabled) + { + for(uint32 x = start; x < end; x++) + target[x] = 0x000 | VDC_BGDISABLE_OUT_MASK; + return; + } + + { + int bat_y = ((BG_YOffset >> 3) & bat_height_mask) << bat_width_shift; + uint32 first_end = start + 8 - (BG_XOffset & 7); + uint32 dohmask = 0xFFFFFFFF; + + if((MWR_cache & 0x3) == 0x3) + { + if(MWR_cache & 0x80) + dohmask = 0xCCCCCCCC; + else + dohmask = 0x33333333; + } + + // Draw the first pixels of the first tile, depending on the lower 3 bits of the xscroll/xoffset register, to + // we can render the rest of the line in 8x1 chunks, which is faster. + for(uint32 x = start; x < first_end; x++) + { + int bat_x = (BG_XOffset >> 3) & bat_width_mask; + uint16 bat = VRAM[bat_x | bat_y]; + const uint8 pal_or = ((bat >> 8) & 0xF0); + int palette_index = ((bat >> 12) & 0x0F) << 4; + uint32 raw_pixel; + + raw_pixel = bg_tile_cache[bat & 0xFFF][BG_YOffset & 7][BG_XOffset & 0x7] & dohmask; + target[x] = palette_index | raw_pixel | pal_or; + + if((bat & 0xFFF) > VRAM_BGTileNoMask) + VDC_UNDEFINED("Unmapped BG tile read"); + + BG_XOffset++; + } + + int bat_boom = (BG_XOffset >> 3) & bat_width_mask; + int line_sub = BG_YOffset & 7; + + if((MWR_cache & 0x3) == 0x3) + { + for(uint32 x = first_end; x < end; x+=8) + { + const uint16 bat = VRAM[bat_boom | bat_y]; + const uint8 pal_or = ((bat >> 8) & 0xF0); + uint8 *pix_lut = bg_tile_cache[bat & 0xFFF][line_sub]; + + if((bat & 0xFFF) > VRAM_BGTileNoMask) + VDC_UNDEFINED("Unmapped BG tile read"); + + + (target + 0)[x] = (pix_lut[0] & dohmask) | pal_or; + (target + 1)[x] = (pix_lut[1] & dohmask) | pal_or; + (target + 2)[x] = (pix_lut[2] & dohmask) | pal_or; + (target + 3)[x] = (pix_lut[3] & dohmask) | pal_or; + (target + 4)[x] = (pix_lut[4] & dohmask) | pal_or; + (target + 5)[x] = (pix_lut[5] & dohmask) | pal_or; + (target + 6)[x] = (pix_lut[6] & dohmask) | pal_or; + (target + 7)[x] = (pix_lut[7] & dohmask) | pal_or; + + bat_boom = (bat_boom + 1) & bat_width_mask; + BG_XOffset++; + + } + } + else + for(uint32 x = first_end; x < end; x+=8) // This will draw past the right side of the buffer, but since our pitch is 1024, and max width is ~512, we're safe. Also, + // any overflow that is on the visible screen are will be hidden by the overscan color code below this code. + { + const uint16 bat = VRAM[bat_boom | bat_y]; + const uint8 pal_or = ((bat >> 8) & 0xF0); + uint8 *pix_lut = bg_tile_cache[bat & 0xFFF][line_sub]; + + if((bat & 0xFFF) > VRAM_BGTileNoMask) + VDC_UNDEFINED("Unmapped BG tile read"); + + (target + 0)[x] = pix_lut[0] | pal_or; + (target + 1)[x] = pix_lut[1] | pal_or; + (target + 2)[x] = pix_lut[2] | pal_or; + (target + 3)[x] = pix_lut[3] | pal_or; + (target + 4)[x] = pix_lut[4] | pal_or; + (target + 5)[x] = pix_lut[5] | pal_or; + (target + 6)[x] = pix_lut[6] | pal_or; + (target + 7)[x] = pix_lut[7] | pal_or; + + bat_boom = (bat_boom + 1) & bat_width_mask; + BG_XOffset++; + } + } +} + +#define SPRF_PRIORITY 0x00080 +#define SPRF_HFLIP 0x00800 +#define SPRF_VFLIP 0x08000 +#define SPRF_SPRITE0 0x10000 + +static const unsigned int sprite_height_tab[4] = { 16, 32, 64, 64 }; +static const unsigned int sprite_height_no_mask[4] = { ~0U, ~2U, ~6U, ~6U }; +static const unsigned int sprite_width_tab[2] = { 16, 32 }; + +void VDC::FetchSpriteData(void) +{ + active_sprites = 0; + + // First, grab the up to 16 sprites. + for(int i = 0; i < 64; i++) + { + int16 y = (SAT[i * 4 + 0] & 0x3FF) - 0x40; + uint16 x = (SAT[i * 4 + 1] & 0x3FF); + uint16 no = (SAT[i * 4 + 2] >> 1) & 0x3FF; // Todo, cg mode bit + uint16 flags = (SAT[i * 4 + 3]); + + uint32 palette_index = (flags & 0xF) << 4; + uint32 height = sprite_height_tab[(flags >> 12) & 3]; + uint32 width = sprite_width_tab[(flags >> 8) & 1]; + + if((int32)RCRCount >= y && (int32)RCRCount < (int32)(y + height)) + { + bool second_half = 0; + uint32 y_offset = RCRCount - y; + if(y_offset > height) continue; + + + breepbreep: + + if(active_sprites == 16) + { + if(CR & 0x2) + { + status |= VDCS_OR; + IRQHook(TRUE); + VDC_DEBUG("Overflow IRQ"); + } + if(!unlimited_sprites) + break; + } + + + { + if(flags & SPRF_VFLIP) + y_offset = height - 1 - y_offset; + + no &= sprite_height_no_mask[(flags >> 12) & 3]; + no |= (y_offset & 0x30) >> 3; + if(width == 32) no &= ~1; + if(second_half) + no |= 1; + + SpriteList[active_sprites].flags = flags; + + if(flags & SPRF_HFLIP && width == 32) + no ^= 1; + //printf("Found: %d %d\n", RCRCount, x); + SpriteList[active_sprites].x = x; + SpriteList[active_sprites].palette_index = palette_index; + + if((no * 64) >= VRAM_Size) + VDC_UNDEFINED("Unmapped VRAM sprite tile read"); + + if((MWR_cache & 0xC) == 4) + { + if(SAT[i * 4 + 2] & 1) + { + SpriteList[active_sprites].pattern_data[0] = VRAM[no * 64 + (y_offset & 15) + 32]; + SpriteList[active_sprites].pattern_data[1] = VRAM[no * 64 + (y_offset & 15) + 48]; + SpriteList[active_sprites].pattern_data[2] = 0; + SpriteList[active_sprites].pattern_data[3] = 0; + } + else + { + SpriteList[active_sprites].pattern_data[0] = VRAM[no * 64 + (y_offset & 15) ]; + SpriteList[active_sprites].pattern_data[1] = VRAM[no * 64 + (y_offset & 15) + 16]; + SpriteList[active_sprites].pattern_data[2] = 0; + SpriteList[active_sprites].pattern_data[3] = 0; + } + } + else + { + SpriteList[active_sprites].pattern_data[0] = VRAM[no * 64 + (y_offset & 15) ]; + SpriteList[active_sprites].pattern_data[1] = VRAM[no * 64 + (y_offset & 15) + 16]; + SpriteList[active_sprites].pattern_data[2] = VRAM[no * 64 + (y_offset & 15) + 32]; + SpriteList[active_sprites].pattern_data[3] = VRAM[no * 64 + (y_offset & 15) + 48]; + } + + SpriteList[active_sprites].flags |= i ? 0 : SPRF_SPRITE0; + + active_sprites++; + + if(width == 32 && !second_half) + { + second_half = 1; + x += 16; + y_offset = RCRCount - y; // Fix the y offset so that sprites that are hflipped + vflipped display properly + goto breepbreep; + } + } + } + } + + sprite_cg_fetch_counter = ((active_sprites < 16) ? active_sprites : 16) * 4; +} + +void VDC::DrawSprites(uint16 *target, int enabled) +{ + alignas(16) uint16 sprite_line_buf[1024]; + + uint32 display_width, start, end; + + CalcWidthStartEnd(display_width, start, end); + + for(unsigned int i = start; i < end; i++) + sprite_line_buf[i] = 0; + + for(int i = (active_sprites - 1) ; i >= 0; i--) + { + int32 pos = SpriteList[i].x - 0x20 + start; + uint32 prio_or = 0; + + if(SpriteList[i].flags & SPRF_PRIORITY) + prio_or = 0x200; + + if((SpriteList[i].flags & SPRF_SPRITE0) && (CR & 0x01)) + { + for(uint32 x = 0; x < 16; x++) + { + uint32 raw_pixel; + uint32 pi = SpriteList[i].palette_index; + uint32 rev_x = 15 - x; + + if(SpriteList[i].flags & SPRF_HFLIP) + rev_x = x; + + raw_pixel = (SpriteList[i].pattern_data[0] >> rev_x) & 1; + raw_pixel |= ((SpriteList[i].pattern_data[1] >> rev_x) & 1) << 1; + raw_pixel |= ((SpriteList[i].pattern_data[2] >> rev_x) & 1) << 2; + raw_pixel |= ((SpriteList[i].pattern_data[3] >> rev_x) & 1) << 3; + + if(raw_pixel) + { + pi |= 0x100; + uint32 tx = pos + x; + + if(tx >= end) // Covers negative and overflowing the right side. + continue; + + if(sprite_line_buf[tx] & 0xF) + { + status |= VDCS_CR; + VDC_DEBUG("Sprite hit IRQ"); + IRQHook(TRUE); + } + sprite_line_buf[tx] = pi | raw_pixel | prio_or; + } + } + } // End sprite hit loop + else + { + for(uint32 x = 0; x < 16; x++) + { + uint32 raw_pixel; + uint32 pi = SpriteList[i].palette_index; + uint32 rev_x = 15 - x; + + if(SpriteList[i].flags & SPRF_HFLIP) + rev_x = x; + + raw_pixel = (SpriteList[i].pattern_data[0] >> rev_x) & 1; + raw_pixel |= ((SpriteList[i].pattern_data[1] >> rev_x) & 1) << 1; + raw_pixel |= ((SpriteList[i].pattern_data[2] >> rev_x) & 1) << 2; + raw_pixel |= ((SpriteList[i].pattern_data[3] >> rev_x) & 1) << 3; + + if(raw_pixel) + { + pi |= 0x100; + uint32 tx = pos + x; + + if(tx >= end) // Covers negative and overflowing the right side. + continue; + sprite_line_buf[tx] = pi | raw_pixel | prio_or; + } + } + } // End non-sprite-hit loop + } + + if(enabled) + { + for(unsigned int x = start; x < end; x++) + { + if(sprite_line_buf[x] & 0x0F) + { + if(!(target[x] & 0x0F) || (sprite_line_buf[x] & 0x200)) + target[x] = sprite_line_buf[x] & 0x1FF; + } + } + } + active_sprites = 0; +} + +/* + Caution: If we ever add something to Write() or Read() that will affect the timing of the next event, make sure + to set the passed-by-reference next_event BEFORE calling this function, or otherwise re-engineer this convoluted setup. +*/ +void VDC::DoWaitStates(void) +{ + //bool did_wait = VDC_IS_BSY; + + while(VDC_IS_BSY) + { + //int32 to_wait = CalcNextEvent(); + //if(!WSHook || !WSHook(to_wait)) + if(!WSHook || !WSHook(-1)) // Event-counter-based wait-stating + { + if(DMARunning) + { + VDC_WARNING("VRAM DMA completion forced."); + RunDMA(0, TRUE); + } + + if(sat_dma_counter > 0) + { + VDC_WARNING("SAT DMA completion forced."); + RunSATDMA(0, TRUE); + } + + if(mystery_phase) + { + bool backup_mystery_phase = mystery_phase; + mystery_phase = false; + CheckAndCommitPending(); + mystery_phase = backup_mystery_phase; + } + + break; + } + } + + //if(did_wait) + // printf("End of wait stating: %d %d\n", VDMA_CycleCounter, sat_dma_counter); + + assert(!pending_read); + assert(!pending_write); +} + +uint8 VDC::Read(uint32 A, int32 &next_event, bool peek) +{ + uint8 ret = 0; + int msb = A & 1; + + A &= 0x3; + + switch(A) + { + case 0x0: ret = status | (VDC_IS_BSY ? 0x40 : 0x00); + + if(!peek) + { + status &= ~0x3F; + IRQHook(FALSE); + } + break; + + case 0x2: + case 0x3: + if(!peek) + { + // Should we only wait on MSB reads... + DoWaitStates(); + } + + ret = VDC_REGGETP(read_buffer, msb); + + if(select == 0x2) // VRR - VRAM Read Register + { + if(msb) + { + if(!peek) + { + pending_read = TRUE; + pending_read_addr = MARR; + MARR += vram_inc_tab[(CR >> 11) & 0x3]; + + CheckAndCommitPending(); + } + } + } + break; + } + + return(ret); +} + +uint16 VDC::Read16(bool A, bool peek) +{ + uint16 ret = 0; + + if(!A) + { + ret = status | (VDC_IS_BSY ? 0x40 : 0x00); + + if(!peek) + { + status &= ~0x3F; + IRQHook(FALSE); + } + } + else + { + if(!peek) + DoWaitStates(); + + ret = read_buffer; + + if(select == 0x2) // VRR - VRAM Read Register + { + if(!peek) + { + pending_read = TRUE; + pending_read_addr = MARR; + MARR += vram_inc_tab[(CR >> 11) & 0x3]; + + CheckAndCommitPending(); + } + } + } + + return(ret); +} + + +void VDC::CheckAndCommitPending(void) +{ + if(sat_dma_counter <= 0 && !DMARunning /* && sprite_cg_fetch_counter <= 0*/ && !mystery_phase) + { + if(pending_write) + { + if(pending_write_addr < VRAM_Size) + { + VRAM[pending_write_addr] = pending_write_latch; + FixTileCache(pending_write_addr); + } + //else + // VDC_UNDEFINED("Unmapped VRAM write"); + + pending_write = FALSE; + } + + if(pending_read) + { + if(pending_read_addr >= VRAM_Size) + VDC_UNDEFINED("Unmapped VRAM VRR read"); + + read_buffer = VRAM[pending_read_addr]; + pending_read = FALSE; + } + } +} + + +void VDC::Write(uint32 A, uint8 V, int32 &next_event) +{ + int msb = A & 1; + + A &= 0x3; + + //if((A == 0x2 || A == 0x3) && (select >= 0xF && select <= 0x12)) + //if((A == 2 || A == 3) && select != 2) + // printf("VDC Write(RCRCount=%d): A=%02x, Select=%02x, V=%02x\n", RCRCount, A, select, V); + + switch(A) + { + case 0x0: select = V & 0x1F; + break; + + case 0x2: + case 0x3: + //if((select & 0x1F) >= 0x9 && (select & 0x1F) <= 0x1F) + // VDC_DEBUG("%02x %d, %02x", select & 0x1F, msb, V); + + switch(select & 0x1F) + { + case 0x00: VDC_REGSETP(MAWR, V, msb); + break; + + case 0x01: VDC_REGSETP(MARR, V, msb); + if(msb) + { + DoWaitStates(); + + pending_read = TRUE; + pending_read_addr = MARR; + MARR += vram_inc_tab[(CR >> 11) & 0x3]; + + CheckAndCommitPending(); + } + break; + + case 0x02: if(!msb) + { + write_latch = V; + } + else + { + // We must call CommitPendingWrite at the end of SAT/VRAM DMA for this to work! + DoWaitStates(); + + pending_write = TRUE; + pending_write_addr = MAWR; + pending_write_latch = write_latch | (V << 8); + MAWR += vram_inc_tab[(CR >> 11) & 0x3]; + + CheckAndCommitPending(); + } + break; + + case 0x05: VDC_REGSETP(CR, V, msb); + //printf("CR: %04x, %d\n", CR, msb); + break; + + case 0x06: VDC_REGSETP(RCR, V, msb); + RCR &= 0x3FF; + break; + + case 0x07: VDC_REGSETP(BXR, V, msb); + BXR &= 0x3FF; + //VDC_DEBUG("BXR Set"); + break; + + case 0x08: VDC_REGSETP(BYR, V, msb); + BYR &= 0x1FF; + BG_YMoo = BYR; // Set it on LSB and MSB writes(only changing on MSB breaks Youkai Douchuuki) + //VDC_DEBUG("BYR Set"); + break; + + case 0x09: VDC_REGSETP(MWR, V, msb); break; + case 0x0a: VDC_REGSETP(HSR, V, msb); break; + case 0x0b: VDC_REGSETP(HDR, V, msb); break; + case 0x0c: VDC_REGSETP(VSR, V, msb); break; + case 0x0d: VDC_REGSETP(VDR, V, msb); break; + case 0x0e: VDC_REGSETP(VCR, V, msb); break; + case 0x0f: VDC_REGSETP(DCR, V, msb); + if(DMARunning) + { + VDC_UNDEFINED("Set DCR during DMA: %04x\n", DCR); + } + + if(DMAPending) + { + VDC_UNDEFINED("Set DCR while DMAPending: %04x\n", DCR); + } + + break; + + case 0x10: VDC_REGSETP(SOUR, V, msb); + if(DMARunning) + { + VDC_UNDEFINED("Set SOUR during DMA: %04x\n", SOUR); + } + + if(DMAPending) + { + VDC_UNDEFINED("Set SOUR while DMAPending: %04x\n", SOUR); + } + break; + + case 0x11: VDC_REGSETP(DESR, V, msb); + if(DMARunning) + { + VDC_UNDEFINED("Set DESR during DMA: %04x\n", DESR); + } + if(DMAPending) + { + VDC_UNDEFINED("Set DESR while DMAPending: %04x\n", DESR); + } + break; + + case 0x12: VDC_REGSETP(LENR, V, msb); + if(DMARunning) + { + VDC_UNDEFINED("Set LENR during DMA: %04x\n", LENR); + } + + if(DMAPending) + { + VDC_UNDEFINED("Set LENR while DMAPending: %04x\n", LENR); + } + + if(msb) + { + VDC_DEBUG("DMA: %04x %04x %04x, %02x", SOUR, DESR, LENR, DCR); + DMAPending = 1; + } + break; + + case 0x13: VDC_REGSETP(DVSSR, V, msb); + SATBPending = 1; + break; + + default: VDC_WARNING("Unknown VDC register write: %04x %02x", select, V); + break; + } + break; + } +} + + +void VDC::Write16(bool A, uint16 V) +{ + if(!A) + select = V & 0x1F; + else + { + switch(select & 0x1F) + { + case 0x00: MAWR = V; + break; + + + case 0x01: MARR = V; + + DoWaitStates(); + + pending_read = TRUE; + pending_read_addr = MARR; + + MARR += vram_inc_tab[(CR >> 11) & 0x3]; + + CheckAndCommitPending(); + break; + + + case 0x02: // We must call CommitPendingWrite at the end of SAT/VRAM DMA for this to work! + DoWaitStates(); + + pending_write = TRUE; + pending_write_addr = MAWR; + pending_write_latch = V; + MAWR += vram_inc_tab[(CR >> 11) & 0x3]; + + CheckAndCommitPending(); + break; + + case 0x05: CR = V; + break; + + case 0x06: RCR = V & 0x3FF; + break; + + case 0x07: BXR = V & 0x3FF; + //VDC_DEBUG("BXR Set"); + break; + + case 0x08: BYR = V & 0x1FF; + BG_YMoo = BYR; + //VDC_DEBUG("BYR Set"); + break; + + case 0x09: MWR = V; break; + case 0x0a: HSR = V; break; + case 0x0b: HDR = V; break; + case 0x0c: VSR = V; break; + case 0x0d: VDR = V; break; + case 0x0e: VCR = V; break; + + case 0x0f: DCR = V; + if(DMARunning) + { + VDC_UNDEFINED("Set DCR during DMA: %04x\n", DCR); + } + + if(DMAPending) + { + VDC_UNDEFINED("Set DCR while DMAPending: %04x\n", DCR); + } + + break; + + case 0x10: SOUR = V; + if(DMARunning) + { + VDC_UNDEFINED("Set SOUR during DMA: %04x\n", SOUR); + } + + if(DMAPending) + { + VDC_UNDEFINED("Set SOUR while DMAPending: %04x\n", SOUR); + } + break; + + case 0x11: DESR = V; + if(DMARunning) + { + VDC_UNDEFINED("Set DESR during DMA: %04x\n", DESR); + } + if(DMAPending) + { + VDC_UNDEFINED("Set DESR while DMAPending: %04x\n", DESR); + } + break; + + case 0x12: LENR = V; + if(DMARunning) + { + VDC_UNDEFINED("Set LENR during DMA: %04x\n", LENR); + } + + if(DMAPending) + { + VDC_UNDEFINED("Set LENR while DMAPending: %04x\n", LENR); + } + + VDC_DEBUG("DMA: %04x %04x %04x, %02x", SOUR, DESR, LENR, DCR); + + DMAPending = 1; + break; + + case 0x13: DVSSR = V; + SATBPending = 1; + break; + + default: VDC_WARNING("Oops 2: %04x %02x", select, V); + break; + } + } + +} + + + +int32 VDC::Reset(void) +{ + memset(VRAM, 0, sizeof(VRAM)); + memset(SAT, 0, sizeof(SAT)); + memset(SpriteList, 0, sizeof(SpriteList)); + + for(uint32 A = 0; A < 65536; A += 16) + FixTileCache(A); + + pending_read = false; + pending_read_addr = 0xFFFF; + read_buffer = 0xFFFF; + write_latch = 0; + + pending_write = false; + pending_write_addr = 0xFFFF; + pending_write_latch = 0xFFFF; + + status = 0; + + HSR = 0; + HDR = 0; + VSR = 0; + VDR = 0; + VCR = 0; + + HSW_cache = M_vdc_HSW; + HDS_cache = M_vdc_HDS; + HDW_cache = M_vdc_HDW; + HDE_cache = M_vdc_HDE; + + VDS_cache = M_vdc_VDS; + VSW_cache = M_vdc_VSW; + VDW_cache = M_vdc_VDW; + VCR_cache = M_vdc_VCR; + + + + MAWR = 0; + MARR = 0; + + CR = CR_cache = 0; + RCR = 0; + BXR = 0; + BYR = 0; + MWR = 0; + MWR_cache = 0; + + DCR = 0; + SOUR = 0; + DESR = 0; + LENR = 0; + DVSSR = 0; + + VDMA_CycleCounter = 0; + + RCRCount = 0; + + DMAReadBuffer = 0; + DMAReadWrite = 0; + DMARunning = 0; + DMAPending = 0; + SATBPending = 0; + burst_mode = 0; + + BG_XOffset = 0; + BG_YOffset = 0; + BG_YMoo = 0; + + sat_dma_counter = 0; + select = 0; + + pixel_copy_count = 0; + + + NeedRCRInc = false; + NeedVBIRQTest = false; + NeedSATDMATest = false; + NeedBGYInc = false; + + HPhase = 0; + VPhase = 0; + HPhaseCounter = 1; + VPhaseCounter = 1; + + sprite_cg_fetch_counter = 0; + + mystery_counter = 0; + mystery_phase = false; + + pixel_desu = 0; + pixel_copy_count = 0; + active_sprites = 0; + + return(CalcNextEvent()); +} + +VDC::VDC() +{ + SetUnlimitedSprites(false); + SetVRAMSize(65536); + userle = ~0; + + WSHook = NULL; + IRQHook = NULL; + + in_exhsync = false; + in_exvsync = false; +} + +void VDC::SetUnlimitedSprites(const bool nospritelimit) +{ + unlimited_sprites = nospritelimit; +} + +void VDC::SetVRAMSize(const uint32 par_VRAM_Size) +{ + //const uint32 old_VRAM_Size; + + assert(par_VRAM_Size == round_up_pow2(par_VRAM_Size)); + assert(par_VRAM_Size >= 16 && par_VRAM_Size <= 65536); + + VRAM_Size = par_VRAM_Size; + VRAM_SizeMask = VRAM_Size - 1; + VRAM_BGTileNoMask = VRAM_SizeMask / 16; + + // for(uint32 A = std::min(old_VRAM_Size, par_VRAM_Size); A < 65536; A += 16) + // FixTileCache(A); +} + +VDC::~VDC() +{ + +} + +#ifdef WANT_DEBUGGER +bool VDC::DoGfxDecode(uint32 *target, const uint32 *color_table, const uint32 TransparentColor, bool DecodeSprites, + int32 w, int32 h, int32 scroll) +{ + const uint32 *palette_ptr = color_table; + + if(DecodeSprites) + { + for(int y = 0; y < h; y++) + { + for(int x = 0; x < w; x += 16) + { + int which_tile = (x / 16) + (scroll + (y / 16)) * (w / 16); + + if(which_tile >= VRAM_Size / 64) + { + for(int sx = 0; sx < 16; sx++) + { + target[x + sx] = TransparentColor; + target[x + w * 1 + sx] = 0; + target[x + w * 2 + sx] = 0; + } + continue; + } + + uint16 cg[4]; + cg[0] = VRAM[which_tile * 64 + (y & 15)]; + cg[1] = VRAM[which_tile * 64 + (y & 15) + 16]; + cg[2] = VRAM[which_tile * 64 + (y & 15) + 32]; + cg[3] = VRAM[which_tile * 64 + (y & 15) + 48]; + for(int sx = 0; sx < 16; sx++) + { + int rev_sx = 15 - sx; + target[x + sx] = palette_ptr[(((cg[0] >> rev_sx) & 1) << 0) | + (((cg[1] >> rev_sx) & 1) << 1) | (((cg[2] >> rev_sx) & 1) << 2) | (((cg[3] >> rev_sx) & 1) << 3)]; + target[x + w * 1 + sx] = which_tile; + target[x + w * 2 + sx] = which_tile * 64; + } + } + target += w * 3; + } + } + else for(int y = 0; y < h; y++) + { + for(int x = 0; x < w; x+=8) + { + int which_tile = (x / 8) + (scroll + (y / 8)) * (w / 8); + + if(which_tile >= (VRAM_Size / 16)) + { + for(int sx = 0; sx < 8; sx++) + { + target[x + sx] = TransparentColor; + target[x + w * 1 + sx] = 0; + target[x + w * 2 + sx] = 0; + } + continue; + } + + target[x + 0] = palette_ptr[ bg_tile_cache[which_tile][y & 0x7][0]]; + target[x + 1] = palette_ptr[ bg_tile_cache[which_tile][y & 0x7][1]]; + target[x + 2] = palette_ptr[ bg_tile_cache[which_tile][y & 0x7][2]]; + target[x + 3] = palette_ptr[ bg_tile_cache[which_tile][y & 0x7][3]]; + target[x + 4] = palette_ptr[ bg_tile_cache[which_tile][y & 0x7][4]]; + target[x + 5] = palette_ptr[ bg_tile_cache[which_tile][y & 0x7][5]]; + target[x + 6] = palette_ptr[ bg_tile_cache[which_tile][y & 0x7][6]]; + target[x + 7] = palette_ptr[ bg_tile_cache[which_tile][y & 0x7][7]]; + + target[x + w*1 + 0]=target[x + w*1 + 1]=target[x + w*1 + 2]=target[x + w*1 + 3] = + target[x + w*1 + 4]=target[x + w*1 + 5]=target[x + w*1 + 6]=target[x + w*1 + 7] = which_tile; + + target[x + w*2 + 0]=target[x + w*2 + 1]=target[x + w*2 + 2]=target[x + w*2 + 3] = + target[x + w*2 + 4]=target[x + w*2 + 5]=target[x + w*2 + 6]=target[x + w*2 + 7] = which_tile * 16; + } + target += w * 3; + } + + return(1); +} +#endif + diff --git a/waterbox/pcfx/huc6270/vdc.h b/waterbox/pcfx/huc6270/vdc.h new file mode 100644 index 0000000000..28a6895cba --- /dev/null +++ b/waterbox/pcfx/huc6270/vdc.h @@ -0,0 +1,530 @@ +#ifndef __PCE_VDC_H +#define __PCE_VDC_H + +#define VDC_PIXEL_OUT_MASK 0x01FF + +// This bit will be set for a non-sprite pixel if the BG layer is disabled(via ToggleLayer()), +#define VDC_BGDISABLE_OUT_MASK 0x0200 + +// HSync and VSync out bits are only valid when the EX bits in VDC's CR +// are set so that the VDC will output sync signals rather than +// input them. If it is not configured in this manner, the bit(s) shall always be 0. +#define VDC_HSYNC_OUT_MASK 0x2000 +#define VDC_VSYNC_OUT_MASK 0x4000 + +// The DISP bit can either denote active display area(1 = active, 0 = inactive), +// colorburst insertion period(0 = insert colorburst, 1 = not in colorburst period; may not be emulated correctly), +// or "internal horizontal synchronous signal"(may not be emulated correctly), depending on the TE +// bits in the VDC's CR. +#define VDC_DISP_OUT_MASK 0x8000 + +#define VDC_REGSETP(_reg, _data, _msb) \ + { \ + _reg &= 0xFF << ((_msb) ? 0 : 8); \ + _reg |= (_data) << ((_msb) ? 8 : 0); \ + } +#define VDC_REGGETP(_reg, _msb) ((_reg >> ((_msb) ? 8 : 0)) & 0xFF) + +static const unsigned int vram_inc_tab[4] = {1, 32, 64, 128}; + +#define VDC_IS_BSY (pending_read || pending_write) + +typedef struct +{ + uint32 x; + uint32 flags; + uint8 palette_index; + uint16 pattern_data[4]; +} SPRLE; + +typedef struct +{ + // In the case the VDC access doesn't cause a VRAM read/write, only ReadCount/WriteCount will be set to 0. + uint32 ReadStart; + uint32 ReadCount; + uint32 WriteStart; + uint32 WriteCount; + + uint32 RegRWIndex; + bool RegWriteDone; + bool RegReadDone; +} VDC_SimulateResult; + +class VDC +{ + public: + VDC() + MDFN_COLD; + ~VDC() MDFN_COLD; + + // Default false. + void SetUnlimitedSprites(const bool nospritelimit); + + // The VRAM size is specified in 16-bit words. Default 65536. + // Reset() should be called after changing this setting, otherwise things may be broken. + void SetVRAMSize(const uint32 par_VRAM_size); + + int32 Reset(void) MDFN_WARN_UNUSED_RESULT; + + // ResetSimulate(), SimulateWrite(), and SimulateRead() are intended to handle VRAM read/write breakpoints. + // SimulateWrite() and SimulateRead() will return the VRAM address that will EVENTUALLY be written(upper 32-bits) and/or read(lower 32-bits) to + // due to the access, or 0xFFFFFFFF in the upper or lower 32-bits if no VRAM access of that type occurs. + // + // The feature is intended to support block moves to VRAM in a single instruction. It may not function properly if the address passed to SimulateRead() + // or SimulateWrite() alternates(even if just once) between the data port high byte and control port between calls to ResetSimulate() + // Call to reset simulation state. + + INLINE void ResetSimulate(void) + { + Simulate_MAWR = MAWR; + Simulate_MARR = MARR; + + Simulate_select = select; + Simulate_CR = CR; + + Simulate_LENR = LENR; + } + + INLINE void SimulateRead(uint32 A, VDC_SimulateResult *result) + { + result->ReadCount = 0; + result->WriteCount = 0; + result->RegReadDone = false; + result->RegWriteDone = false; + + if (A & 0x2) + { + result->RegReadDone = true; + result->RegRWIndex = Simulate_select; + } + + if ((A & 0x3) == 0x3 && Simulate_select == 0x02) + { + Simulate_MARR += vram_inc_tab[(Simulate_CR >> 11) & 0x3]; + + result->ReadStart = Simulate_MARR; + result->ReadCount = 1; + } + } + + INLINE void SimulateWrite(uint32 A, uint8 V, VDC_SimulateResult *result) + { + result->ReadCount = 0; + result->WriteCount = 0; + result->RegReadDone = false; + result->RegWriteDone = false; + + const unsigned int msb = A & 1; + + switch (A & 0x3) + { + case 0x00: + Simulate_select = V & 0x1F; + break; + + case 0x02: + case 0x03: + result->RegWriteDone = true; + result->RegRWIndex = Simulate_select; + + switch (Simulate_select) + { + case 0x00: + VDC_REGSETP(Simulate_MAWR, V, msb); + break; + + case 0x01: + VDC_REGSETP(Simulate_MARR, V, msb); + Simulate_MARR += vram_inc_tab[(Simulate_CR >> 11) & 0x3]; + + result->ReadStart = Simulate_MARR; + result->ReadCount = 1; + break; + + case 0x02: + if (msb) + { + result->WriteStart = Simulate_MAWR; + result->WriteCount = 1; + + Simulate_MAWR += vram_inc_tab[(Simulate_CR >> 11) & 0x3]; + } + break; + + case 0x12: + VDC_REGSETP(Simulate_LENR, V, msb); + if (msb) + { + result->ReadStart = SOUR; + result->ReadCount = Simulate_LENR + 1; + + if (DCR & 0x4) + result->ReadStart = (result->ReadStart - (result->ReadCount - 1)) & 0xFFFF; + + result->WriteStart = DESR; + result->WriteCount = Simulate_LENR + 1; + + if (DCR & 0x8) + result->WriteStart = (result->WriteStart - (result->WriteCount - 1)) & 0xFFFF; + } + break; + } + break; + } + } + + INLINE void SimulateRead16(bool A, VDC_SimulateResult *result) + { + result->ReadCount = 0; + result->WriteCount = 0; + result->RegReadDone = false; + result->RegWriteDone = false; + + if (A & 0x2) + { + result->RegReadDone = true; + result->RegRWIndex = Simulate_select; + } + + if (A && Simulate_select == 0x02) + { + Simulate_MARR += vram_inc_tab[(Simulate_CR >> 11) & 0x3]; + + result->ReadStart = Simulate_MARR; + result->ReadCount = 1; + } + } + + INLINE void SimulateWrite16(bool A, uint16 V, VDC_SimulateResult *result) + { + result->ReadCount = 0; + result->WriteCount = 0; + result->RegReadDone = false; + result->RegWriteDone = false; + + if (!A) + Simulate_select = V & 0x1F; + else + { + result->RegWriteDone = true; + result->RegRWIndex = Simulate_select; + + switch (Simulate_select) + { + case 0x00: + Simulate_MAWR = V; + break; + + case 0x01: + Simulate_MARR = V; + Simulate_MARR += vram_inc_tab[(Simulate_CR >> 11) & 0x3]; + + result->ReadStart = Simulate_MARR; + result->ReadCount = 1; + break; + + case 0x02: + result->WriteStart = Simulate_MAWR; + result->WriteCount = 1; + + Simulate_MAWR += vram_inc_tab[(Simulate_CR >> 11) & 0x3]; + break; + + case 0x12: + Simulate_LENR = V; + result->ReadStart = SOUR; + result->ReadCount = Simulate_LENR + 1; + + if (DCR & 0x4) + result->ReadStart = (result->ReadStart - (result->ReadCount - 1)) & 0xFFFF; + + result->WriteStart = DESR; + result->WriteCount = Simulate_LENR + 1; + + if (DCR & 0x8) + result->WriteStart = (result->WriteStart - (result->WriteCount - 1)) & 0xFFFF; + break; + } + } + } + + int32 HSync(bool); + int32 VSync(bool); + + void Write(uint32 A, uint8 V, int32 &next_event); + uint8 Read(uint32 A, int32 &next_event, bool peek = FALSE); + + void Write16(bool A, uint16 V); + uint16 Read16(bool A, bool peek = FALSE); + + int32 Run(int32 clocks, /*bool hs, bool vs,*/ uint16 *pixels, bool skip); + + void FixTileCache(uint16); + void SetLayerEnableMask(uint64 mask); + + void RunDMA(int32, bool force_completion = FALSE); + void RunSATDMA(int32, bool force_completion = FALSE); + + void IncRCR(void); + void DoVBIRQTest(void); + void HDS_Start(void); + + // Peek(VRAM/SAT) and Poke(VRAM/SAT) work in 16-bit VRAM word units. + INLINE uint16 PeekVRAM(uint16 Address) + { + if (Address < VRAM_Size) + return (VRAM[Address]); + else + return (0); + } + + INLINE uint16 PeekSAT(uint8 Address) + { + return (SAT[Address]); + } + + INLINE void PokeVRAM(uint16 Address, const uint16 Data) + { + if (Address < VRAM_Size) + { + VRAM[Address] = Data; + FixTileCache(Address); + } + } + + INLINE void PokeSAT(uint8 Address, const uint16 Data) + { + SAT[Address] = Data; + } + + // Register enums for GetRegister() and SetRegister() + enum + { + GSREG_MAWR = 0, + GSREG_MARR, + GSREG_CR, + GSREG_RCR, + GSREG_BXR, + GSREG_BYR, + GSREG_MWR, + GSREG_HSR, + GSREG_HDR, + GSREG_VSR, + GSREG_VDR, + GSREG_VCR, + GSREG_DCR, + GSREG_SOUR, + GSREG_DESR, + GSREG_LENR, + GSREG_DVSSR, + + GSREG_SELECT, + GSREG_STATUS, + + __GSREG_COUNT + }; + + // Pass NULL if you don't want more information about the special meaning of the value in the specified + // register. Otherwise, pass a buffer of at least 256 bytes in size. + uint32 GetRegister(const unsigned int id, char *special, const uint32 special_len); + void SetRegister(const unsigned int id, const uint32 value); + +#ifdef WANT_DEBUGGER + bool DoGfxDecode(uint32 *target, const uint32 *color_table, const uint32 TransparentColor, bool DecodeSprites, + int32 w, int32 h, int32 scroll); +#endif + + INLINE bool PeekIRQ(void) + { + return ((bool)(status & 0x3F)); + } + + INLINE void SetIRQHook(void (*irqh)(bool)) + { + IRQHook = irqh; + } + + INLINE void SetWSHook(bool (*wsh)(int32)) + { + WSHook = wsh; + } + + INLINE uint16_t* GetVramPointer() + { + return VRAM; + } + INLINE int GetVramByteSize() + { + return VRAM_Size * 2; + } + + private: + int TimeFromHDSStartToBYRLatch(void); + int TimeFromBYRLatchToBXRLatch(void); + + enum + { + HPHASE_HDS = 0, + HPHASE_HDS_PART2, + HPHASE_HDS_PART3, + HPHASE_HDW, + HPHASE_HDW_FINAL, + HPHASE_HDE, + HPHASE_HSW, + HPHASE_COUNT + }; + + enum + { + VPHASE_VDS = 0, + VPHASE_VDW, + VPHASE_VCR, + VPHASE_VSW, + VPHASE_COUNT + }; + + int VRAM_Size; // = 0x8000; + int VRAM_SizeMask; // = VRAM_Size - 1; //0x7FFF; + int VRAM_BGTileNoMask; // = VRAM_SizeMask / 16; //0x7FF; + + void (*IRQHook)(bool); + bool (*WSHook)(int32); + + void DoWaitStates(void); + void CheckAndCommitPending(void); + + INLINE int32 CalcNextEvent(void) + { + int32 next_event = HPhaseCounter; + + if (sat_dma_counter > 0 && sat_dma_counter < next_event) + next_event = sat_dma_counter; + + if (sprite_cg_fetch_counter > 0 && sprite_cg_fetch_counter < next_event) + next_event = sprite_cg_fetch_counter; + + if (DMARunning) + { + assert(VDMA_CycleCounter < 2); + + int32 next_vram_dma_event = ((LENR + 1) * 4) - (DMAReadWrite * 2) - VDMA_CycleCounter; + + assert(next_vram_dma_event > 0); + + if (next_vram_dma_event > 0 && next_vram_dma_event < next_event) + next_event = next_vram_dma_event; + + //printf("Next VRAM DMA event: %d(LENR = %d)\n", next_vram_dma_event, LENR); + } + + assert(next_event > 0); + return (next_event); + } + + bool in_exhsync, in_exvsync; + + void CalcWidthStartEnd(uint32 &display_width, uint32 &start, uint32 &end); + void DrawBG(uint16 *target, int enabled); + void DrawSprites(uint16 *target, int enabled); + void FetchSpriteData(void); + + uint8 Simulate_select; + uint16 Simulate_MAWR; + uint16 Simulate_MARR; + uint16 Simulate_CR; + uint16 Simulate_LENR; + + int32 sat_dma_counter; + + uint8 select; + uint16 MAWR; // Memory Address Write Register + uint16 MARR; // Memory Address Read Register + + uint16 CR; // Control Register + uint16 CR_cache; // Cache for BG/SPR enable + uint16 RCR; // Raster Compare Register + uint16 BXR; // Background X-Scroll Register + uint16 BYR; // Background Y-Scroll Register + uint16 MWR; // Memory Width Register + + uint16 HSR; // Horizontal Sync Register + uint16 HDR; // Horizontal Display Register + uint16 VSR; + uint16 VDR; + + uint16 VCR; + uint16 DCR; + uint16 SOUR; + uint16 DESR; + uint16 LENR; + uint16 DVSSR; + + // Internal SAT DMA transfer variables. + //uint16 SAT_SOUR; + //uint16 SAT_DESR; + //uint16 SAT_LENR; + + int32 VDMA_CycleCounter; + + uint32 RCRCount; + + bool pending_read; + uint16 pending_read_addr; + uint16 read_buffer; + + uint8 write_latch; // LSB + + bool pending_write; + uint16 pending_write_addr; + uint16 pending_write_latch; + + uint8 status; + + uint16 SAT[0x100]; + + uint16 VRAM[65536]; //VRAM_Size]; + + union { + uint64 bg_tile_cache64[65536 / 16][8]; // Tile, y, x + uint8 bg_tile_cache[65536 / 16][8][8]; + }; + + uint16 DMAReadBuffer; + bool DMAReadWrite; + bool DMARunning; + bool DMAPending; + bool SATBPending; + bool burst_mode; + + uint32 BG_YOffset; // Reloaded from BYR at start of display area? + uint32 BG_XOffset; // Reloaded from BXR at each scanline, methinks. + + uint32 HSW_cache, HDS_cache, HDW_cache, HDE_cache; + + uint32 VDS_cache; + uint32 VSW_cache; + uint32 VDW_cache; + uint32 VCR_cache; + uint16 MWR_cache; + + uint32 BG_YMoo; + bool NeedRCRInc, NeedVBIRQTest, NeedSATDMATest, NeedBGYInc; + int HPhase, VPhase; + int32 HPhaseCounter, VPhaseCounter; + + int32 sprite_cg_fetch_counter; + + int32 mystery_counter; + bool mystery_phase; + + uint16 linebuf[1024 + 512]; + uint32 pixel_desu; + int32 pixel_copy_count; + uint32 userle; // User layer enable. + bool unlimited_sprites; + + int active_sprites; + SPRLE SpriteList[64 * 2]; // (see unlimited_sprites option, *2 to accommodate 32-pixel-width sprites ) //16]; +}; + +#endif diff --git a/waterbox/pcfx/huc6273.cpp b/waterbox/pcfx/huc6273.cpp new file mode 100644 index 0000000000..04f8eae954 --- /dev/null +++ b/waterbox/pcfx/huc6273.cpp @@ -0,0 +1,260 @@ +/******************************************************************************/ +/* Mednafen NEC PC-FX Emulation Module */ +/******************************************************************************/ +/* huc6273.cpp - Unfinished emulation of the HuC6273, 3D chip in the PC-FXGA +** Copyright (C) 2007-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/* Definitions: + CMT = Command Macro Table +*/ + +#include "pcfx.h" +#include "huc6273.h" + +namespace MDFN_IEN_PCFX +{ + +enum +{ + OP_NOP = 0x00, + OP_TRIANGLE_STRIP = 0x01, + OP_TRIANGLE_LIST = 0x02, + OP_POLY_LINE = 0x03, + OP_LINE_LIST = 0x04, + OP_RESERVED0 = 0x05, + OP_PUT_IMAGE = 0x06, + OP_READ_PIXEL = 0x07, + OP_WRITE_TE_REGISTERS = 0x08, + OP_WRITE_PE_REGISTERS = 0x09, + OP_MISC = 0x0A, + OP_RESERVED1 = 0x0B, + OP_READ_TE_REGISTERS = 0x0C, + OP_READ_PE_REGISTERS = 0x0D, + OP_WRITE_LUT = 0x0E, + OP_READ_LUT = 0x0F, +}; + +enum +{ + INT_RHIT = 15, // Raster Hit + INT_HBL = 14, // H blank + INT_VBL = 13, // V blank + INT_HSY = 12, // H sync + INT_VSY = 11, // V sync + INT_RBDONE = 10, // Read back done + INT_TESYNC = 9, // TE Sync + INT_SPDONE = 8, // Sprite done + INT_CMDONE = 7, // Command macro done + INT_PESYNC = 6, // PE sync + INT_AEMP = 5, // Almost empty + INT_CSE = 4, // Command sync error + INT_AFL = 3, // Almost full + INT_OVF = 2, // Overflow + INT_FSY = 1, // Frame sync +}; + +static uint16 FIFO[0x20]; +static uint8 InFIFO; +#define AFW (0x20 - InFIFO) + +#define AEMPWD ((FIFOControl >> 4) & 0xFF) +#define AFLWD (FIFOControl & 0xF) +static uint16 FIFOControl; // 0x00004 + +static uint8 CMTBankSelect; +static uint16 CMTStartAddress; +static uint16 CMTByteCount; + +static uint16 InterruptMask; +static uint16 InterruptStatus; + +static uint16 ReadBack; + +static uint16 HorizontalTiming, VerticalTiming; + +static uint16 SCTAddressHi; +static uint16 SpriteControl; +static uint16 CDResult[2]; +static uint16 SPWindowX[2]; // left and right +static uint16 SPWindowY[2]; // top and bottom +static uint16 MiscStatus; +static uint16 ErrorStatus; // Read only! +static uint16 DisplayControl; +static uint16 StatusControl; +static uint16 Config; + +static uint16 RasterHit; + + +static uint16 Results[0x10]; + +static void CheckIRQ(void) +{ + //uint16 MaskedResults = InterruptStatus & InterruptMask; + + + +} + + +static void ProcessFIFO(void) +{ + uint8 length = FIFO[0] & 0xFF; + + if(length > 0x20) + { + length = 0x20; + puts("Length too long"); + } + + if(InFIFO >= length) + { + int opcode = FIFO[0] >> 12; + int option = (FIFO[0] >> 8) & 0x0F; + + printf("Op: %02x, option: %02x\n", opcode, option); + + InFIFO -= length; + for(int i = 0; i < InFIFO; i++) + FIFO[i] = FIFO[length + i]; + } +} + + +static void StoreInFIFO(uint16 V) +{ + if(AFW > 0) + { + FIFO[InFIFO] = V; + InFIFO++; + + ProcessFIFO(); + } +} + +uint8 HuC6273_Read8(uint32 A) +{ + puts("73 Read8"); + return(0); +} + +uint16 HuC6273_Read16(uint32 A) +{ + A &= 0xfffff; + + printf("HuC6273 Read: %04x\n", A); + + switch(A) + { + case 0x00000: + case 0x00002: return(AFW); // Command FIFO status + + case 0x00004: return(FIFOControl); + case 0x00006: return(CMTBankSelect); + case 0x00008: return(CMTStartAddress); + case 0x0000A: return(CMTByteCount); + case 0x0000C: return(InterruptMask); + case 0x0000E: return(0); + case 0x00010: return(InterruptStatus); + case 0x00012: return(ReadBack); + case 0x00014: return(HorizontalTiming); + case 0x00016: return(VerticalTiming); + case 0x00018: return(SCTAddressHi); + case 0x0001A: return(SpriteControl); + case 0x0001C: return(CDResult[0]); + case 0x0001E: return(CDResult[1]); + case 0x00020: return(SPWindowX[0]); + case 0x00022: return(SPWindowY[0]); + case 0x00024: return(SPWindowX[1]); + case 0x00026: return(SPWindowY[1]); + case 0x00028: return(MiscStatus); + case 0x0002A: return(ErrorStatus); + case 0x0002C: return(DisplayControl); + case 0x0002E: return(Config); + } + if(A >= 0x00060 && A <= 0x0007E) + { + return(Results[(A >> 1) & 0xF]); + } + return(0); +} + + +void HuC6273_Write16(uint32 A, uint16 V) +{ + A &= 0xfffff; + + printf("HuC6273 Write: %04x:%04x\n", A, V); + + switch(A) + { + case 0x00000: + case 0x00002: StoreInFIFO(V); break; + + case 0x00004: FIFOControl = V; break; + case 0x00006: CMTBankSelect = V & 0x1F; break; + case 0x00008: CMTStartAddress = V & 0xFFFE; break; + case 0x0000A: CMTByteCount = V & 0xFFFE; break; + case 0x0000C: InterruptMask = V; + CheckIRQ(); + break; + case 0x0000E: // Interrupt Clear + CheckIRQ(); + break; + case 0x00010: InterruptStatus = V; + CheckIRQ(); + break; + case 0x00012: ReadBack = V; break; + case 0x00014: HorizontalTiming = V; break; + case 0x00016: VerticalTiming = V; break; + case 0x00018: SCTAddressHi = V; break; + case 0x0001A: SpriteControl = V; break; + case 0x0001C: CDResult[0] = V; break; + case 0x0001E: CDResult[1] = V; break; + case 0x00020: SPWindowX[0] = V; break; // X Left + case 0x00022: SPWindowY[0] = V; break; // Y Top + case 0x00024: SPWindowX[1] = V; break; // X Right + case 0x00026: SPWindowY[1] = V; break; // Y Bottom + case 0x00028: MiscStatus = V; break; + case 0x0002C: DisplayControl = V; break; + case 0x0002E: StatusControl = V; break; + + case 0x0003C: RasterHit = V; break; + } +} + +void HuC6273_Write8(uint32 A, uint8 V) +{ + puts("73 Write8"); +} + +void HuC6273_Reset(void) +{ + InFIFO = 0; + FIFOControl = 0x5 | (0x20 << 4); +} + + + +bool HuC6273_Init(void) +{ + + return(TRUE); +} + +} diff --git a/waterbox/pcfx/huc6273.h b/waterbox/pcfx/huc6273.h new file mode 100644 index 0000000000..ab4ef73fda --- /dev/null +++ b/waterbox/pcfx/huc6273.h @@ -0,0 +1,38 @@ +/******************************************************************************/ +/* Mednafen NEC PC-FX Emulation Module */ +/******************************************************************************/ +/* huc6273.h: +** Copyright (C) 2007-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef __PCFX_HUC6273_H +#define __PCFX_HUC6273_H + +namespace MDFN_IEN_PCFX +{ + +bool HuC6273_Init(void) MDFN_COLD; + +uint8 HuC6273_Read8(uint32 A); +uint16 HuC6273_Read16(uint32 A); +void HuC6273_Write16(uint32 A, uint16 V); +void HuC6273_Write8(uint32 A, uint8 V); +void HuC6273_Reset(void) MDFN_COLD; + +} + +#endif diff --git a/waterbox/pcfx/input.cpp b/waterbox/pcfx/input.cpp new file mode 100644 index 0000000000..183e0c6d89 --- /dev/null +++ b/waterbox/pcfx/input.cpp @@ -0,0 +1,340 @@ +/******************************************************************************/ +/* Mednafen NEC PC-FX Emulation Module */ +/******************************************************************************/ +/* input.cpp: +** Copyright (C) 2006-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "pcfx.h" +#include "interrupt.h" +#include "input.h" + +#include "input/gamepad.h" +#include "input/mouse.h" + +namespace MDFN_IEN_PCFX +{ + +#define PCFX_PORTS 2 +#define TOTAL_PORTS 8 + +#define TAP_PORTS 4 + +static const int TapMap[2][TAP_PORTS] = + { + {0, 2, 3, 4}, + {1, 5, 6, 7}, +}; + +static void RemakeDevices(int which = -1); +static uint8 MultiTapEnabled; + +// Mednafen-specific input type numerics +enum +{ + FXIT_NONE = 0, + FXIT_GAMEPAD = 1, + FXIT_MOUSE = 2, +}; + +PCFX_Input_Device::~PCFX_Input_Device() +{ +} + +uint32 PCFX_Input_Device::ReadTransferTime(void) +{ + return (1536); +} + +uint32 PCFX_Input_Device::WriteTransferTime(void) +{ + return (1536); +} + +uint32 PCFX_Input_Device::Read(void) +{ + return (0); +} + +void PCFX_Input_Device::Write(uint32 data) +{ +} + +void PCFX_Input_Device::Power(void) +{ +} + +void PCFX_Input_Device::Frame(uint32_t data) +{ +} + +static PCFX_Input_Device *devices[TOTAL_PORTS] = {NULL}; + +// D0 = TRG, trigger bit +// D1 = MOD, multi-tap clear mode? +// D2 = IOS, data direction. 0 = output, 1 = input + +static uint8 TapCounter[PCFX_PORTS]; +static uint8 control[PCFX_PORTS]; +static bool latched[PCFX_PORTS]; +static int32 LatchPending[PCFX_PORTS]; + +static int InputTypes[TOTAL_PORTS]; +static const uint32_t *data_ptr[TOTAL_PORTS]; +static uint32 data_latch[TOTAL_PORTS]; + +void FXINPUT_Init(void) +{ + RemakeDevices(); +} + +#ifdef WANT_DEBUGGER +uint32 FXINPUT_GetRegister(const unsigned int id, char *special, const uint32 special_len) +{ + uint32 value = 0xDEADBEEF; + + switch (id) + { + case FXINPUT_GSREG_KPCTRL0: + case FXINPUT_GSREG_KPCTRL1: + value = control[id == FXINPUT_GSREG_KPCTRL1]; + if (special) + { + trio_snprintf(special, special_len, "Trigger: %d, MOD: %d, IOS: %s", value & 0x1, value & 0x2, (value & 0x4) ? "Input" : "Output"); + } + break; + } + + return value; +} + +void FXINPUT_SetRegister(const unsigned int id, uint32 value) +{ +} + +#endif + +static INLINE int32 min(int32 a, int32 b, int32 c) +{ + int32 ret = a; + + if (b < ret) + ret = b; + if (c < ret) + ret = c; + + return (ret); +} + +static INLINE int32 CalcNextEventTS(const v810_timestamp_t timestamp) +{ + return (min(LatchPending[0] > 0 ? (timestamp + LatchPending[0]) : PCFX_EVENT_NONONO, LatchPending[1] > 0 ? (timestamp + LatchPending[1]) : PCFX_EVENT_NONONO, PCFX_EVENT_NONONO)); +} + +static uint32_t Dummy; + +static void RemakeDevices(int which) +{ + int s = 0; + int e = TOTAL_PORTS; + + if (which != -1) + { + s = which; + e = which + 1; + } + + for (int i = s; i < e; i++) + { + if (devices[i]) + delete devices[i]; + devices[i] = NULL; + + switch (InputTypes[i]) + { + default: + case FXIT_NONE: + devices[i] = new PCFX_Input_Device(); + data_ptr[i] = &Dummy; + break; + case FXIT_GAMEPAD: + devices[i] = PCFXINPUT_MakeGamepad(); + break; + case FXIT_MOUSE: + devices[i] = PCFXINPUT_MakeMouse(i); + break; + } + } +} + +void FXINPUT_SetInput(unsigned port, int type, const uint32_t* ptr) +{ + data_ptr[port] = ptr; + InputTypes[port] = type; // FXIT_NONE, FXIT_GAMEPAD, FXIT_MOUSE + RemakeDevices(port); +} + +void FXINPUT_SetMultitap(bool port1, bool port2) +{ + MultiTapEnabled = port1 | port2 << 1; +} + + +uint8 FXINPUT_Read8(uint32 A, const v810_timestamp_t timestamp) +{ + //printf("Read8: %04x\n", A); + + return (FXINPUT_Read16(A & ~1, timestamp) >> ((A & 1) * 8)); +} + +uint16 FXINPUT_Read16(uint32 A, const v810_timestamp_t timestamp) +{ + FXINPUT_Update(timestamp); + + uint16 ret = 0; + + A &= 0xC2; + + //printf("Read: %04x\n", A); + + if (A == 0x00 || A == 0x80) + { + int w = (A & 0x80) >> 7; + + if (latched[w]) + ret = 0x8; + else + ret = 0x0; + } + else + { + int which = (A >> 7) & 1; + + ret = data_latch[which] >> ((A & 2) ? 16 : 0); + + // Which way is correct? Clear on low reads, or both? Official docs only say low... + if (!(A & 0x2)) + latched[which] = FALSE; + } + + if (!latched[0] && !latched[1]) + PCFXIRQ_Assert(PCFXIRQ_SOURCE_INPUT, FALSE); + + return (ret); +} + +void FXINPUT_Write16(uint32 A, uint16 V, const v810_timestamp_t timestamp) +{ + FXINPUT_Update(timestamp); + + //printf("Write16: %04x:%02x, %d\n", A, V, timestamp / 1365); + + //PCFXIRQ_Assert(PCFXIRQ_SOURCE_INPUT, FALSE); + //if(V != 7 && V != 5) + //printf("PAD Write16: %04x %04x %d\n", A, V, timestamp); + + switch (A & 0xC0) + { + case 0x80: + case 0x00: + { + int w = (A & 0x80) >> 7; + + if ((V & 0x1) && !(control[w] & 0x1)) + { + //printf("Start: %d\n", w); + if (MultiTapEnabled & (1 << w)) + { + if (V & 0x2) + TapCounter[w] = 0; + } + LatchPending[w] = 1536; + PCFX_SetEvent(PCFX_EVENT_PAD, CalcNextEventTS(timestamp)); + } + control[w] = V & 0x7; + } + break; + } +} + +void FXINPUT_Write8(uint32 A, uint8 V, const v810_timestamp_t timestamp) +{ + FXINPUT_Write16(A, V, timestamp); +} + +void FXINPUT_Frame(void) +{ + for (int i = 0; i < TOTAL_PORTS; i++) + { + devices[i]->Frame(data_ptr[i][0]); + } +} + +static v810_timestamp_t lastts; + +v810_timestamp_t FXINPUT_Update(const v810_timestamp_t timestamp) +{ + int32 run_time = timestamp - lastts; + + for (int i = 0; i < 2; i++) + { + if (LatchPending[i] > 0) + { + LatchPending[i] -= run_time; + if (LatchPending[i] <= 0) + { + //printf("Update: %d, %d\n", i, timestamp / 1365); + + if (MultiTapEnabled & (1 << i)) + { + if (TapCounter[i] >= TAP_PORTS) + data_latch[i] = FX_SIG_TAP << 28; + else + { + data_latch[i] = devices[TapMap[i][TapCounter[i]]]->Read(); + } + } + else + { + data_latch[i] = devices[i]->Read(); + } + // printf("Moo: %d, %d, %08x\n", i, TapCounter[i], data_latch[i]); + latched[i] = TRUE; + control[i] &= ~1; + PCFXIRQ_Assert(PCFXIRQ_SOURCE_INPUT, TRUE); + + if (MultiTapEnabled & (1 << i)) + { + if (TapCounter[i] < TAP_PORTS) + { + TapCounter[i]++; + } + } + } + } + } + + lastts = timestamp; + + return CalcNextEventTS(timestamp); +} + +void FXINPUT_ResetTS(int32 ts_base) +{ + lastts = ts_base; +} +} diff --git a/waterbox/pcfx/input.h b/waterbox/pcfx/input.h new file mode 100644 index 0000000000..731158390b --- /dev/null +++ b/waterbox/pcfx/input.h @@ -0,0 +1,82 @@ +/******************************************************************************/ +/* Mednafen NEC PC-FX Emulation Module */ +/******************************************************************************/ +/* input.h: +** Copyright (C) 2006-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef __MDFN_PCFX_INPUT_H +#define __MDFN_PCFX_INPUT_H + +namespace MDFN_IEN_PCFX +{ + +enum +{ + FX_SIG_MOUSE = 0xD, + FX_SIG_TAP = 0xE, + FX_SIG_PAD = 0xF +}; + +class PCFX_Input_Device +{ + public: + // PCFX_Input_Device(int which); // "which" is advisory and only should be used in status messages. + + virtual ~PCFX_Input_Device(); + + virtual uint32 ReadTransferTime(void); + virtual uint32 WriteTransferTime(void); + + virtual uint32 Read(void); + virtual void Write(uint32 data); + + virtual void Power(void); + + virtual void Frame(uint32_t data); +}; + +void FXINPUT_Init(void) MDFN_COLD; + +void FXINPUT_SetInput(unsigned port, int type, const uint32_t* ptr); +void FXINPUT_SetMultitap(bool port1, bool port2); + +uint16 FXINPUT_Read16(uint32 A, const v810_timestamp_t timestamp); +uint8 FXINPUT_Read8(uint32 A, const v810_timestamp_t timestamp); + +void FXINPUT_Write8(uint32 A, uint8 V, const v810_timestamp_t timestamp); +void FXINPUT_Write16(uint32 A, uint16 V, const v810_timestamp_t timestamp); + +void FXINPUT_Frame(void); + +v810_timestamp_t FXINPUT_Update(const v810_timestamp_t timestamp); +void FXINPUT_ResetTS(int32 ts_base); + +#ifdef WANT_DEBUGGER + +enum +{ + FXINPUT_GSREG_KPCTRL0 = 0, + FXINPUT_GSREG_KPCTRL1 +}; + +uint32 FXINPUT_GetRegister(const unsigned int id, char *special, const uint32 special_len); +void FXINPUT_SetRegister(const unsigned int id, uint32 value); +#endif +} + +#endif diff --git a/waterbox/pcfx/input/gamepad.cpp b/waterbox/pcfx/input/gamepad.cpp new file mode 100644 index 0000000000..7d6a10c32b --- /dev/null +++ b/waterbox/pcfx/input/gamepad.cpp @@ -0,0 +1,80 @@ +/******************************************************************************/ +/* Mednafen NEC PC-FX Emulation Module */ +/******************************************************************************/ +/* gamepad.cpp: +** Copyright (C) 2006-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "../pcfx.h" +#include "../input.h" +#include "gamepad.h" + +namespace MDFN_IEN_PCFX +{ + +class PCFX_Input_Gamepad : public PCFX_Input_Device +{ + public: + PCFX_Input_Gamepad() + { + buttons = 0; + } + + virtual ~PCFX_Input_Gamepad() override + { + } + + virtual uint32 ReadTransferTime(void) override + { + return 1536; + } + + virtual uint32 WriteTransferTime(void) override + { + return 1536; + } + + virtual uint32 Read(void) override + { + return buttons | FX_SIG_PAD << 28; + } + + virtual void Write(uint32 data) override + { + } + + virtual void Power(void) override + { + buttons = 0; + } + + virtual void Frame(uint32_t data) override + { + buttons = data; + } + + private: + // 5....098 7......0 + // m mldru rs654321 + uint16 buttons; +}; + +PCFX_Input_Device *PCFXINPUT_MakeGamepad(void) +{ + return new PCFX_Input_Gamepad(); +} +} diff --git a/waterbox/pcfx/input/gamepad.h b/waterbox/pcfx/input/gamepad.h new file mode 100644 index 0000000000..a29dcdbe75 --- /dev/null +++ b/waterbox/pcfx/input/gamepad.h @@ -0,0 +1,30 @@ +/******************************************************************************/ +/* Mednafen NEC PC-FX Emulation Module */ +/******************************************************************************/ +/* gamepad.h: +** Copyright (C) 2006-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef __PCFX_INPUT_GAMEPAD_H +#define __PCFX_INPUT_GAMEPAD_H + +namespace MDFN_IEN_PCFX +{ +PCFX_Input_Device *PCFXINPUT_MakeGamepad(void); +} + +#endif diff --git a/waterbox/pcfx/input/mouse.cpp b/waterbox/pcfx/input/mouse.cpp new file mode 100644 index 0000000000..13e675b8fa --- /dev/null +++ b/waterbox/pcfx/input/mouse.cpp @@ -0,0 +1,88 @@ +/******************************************************************************/ +/* Mednafen NEC PC-FX Emulation Module */ +/******************************************************************************/ +/* mouse.cpp: +** Copyright (C) 2007-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "../pcfx.h" +#include "../input.h" +#include "mouse.h" + +namespace MDFN_IEN_PCFX +{ + +class PCFX_Input_Mouse : public PCFX_Input_Device +{ + public: + PCFX_Input_Mouse(int which) + { + dx = 0; + dy = 0; + button = 0; + } + + virtual ~PCFX_Input_Mouse() override + { + } + + virtual uint32 ReadTransferTime(void) override + { + return (1536); + } + + virtual uint32 WriteTransferTime(void) override + { + return (1536); + } + + virtual uint32 Read(void) override + { + return FX_SIG_MOUSE << 28 | button << 16 | dx << 8 | dy; + } + + virtual void Write(uint32 data) override + { + } + + virtual void Power(void) override + { + button = 0; + dx = 0; + dy = 0; + } + + virtual void Frame(uint32_t data) override + { + dx = data; + dy = data >> 8; + button = data >> 16 & 3; + } + + private: + int8 dx, dy; + + // 76543210 + // ......RL + uint8 button; +}; + +PCFX_Input_Device *PCFXINPUT_MakeMouse(int which) +{ + return new PCFX_Input_Mouse(which); +} +} diff --git a/waterbox/pcfx/input/mouse.h b/waterbox/pcfx/input/mouse.h new file mode 100644 index 0000000000..19837873b3 --- /dev/null +++ b/waterbox/pcfx/input/mouse.h @@ -0,0 +1,30 @@ +/******************************************************************************/ +/* Mednafen NEC PC-FX Emulation Module */ +/******************************************************************************/ +/* mouse.h: +** Copyright (C) 2007-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef __PCFX_INPUT_MOUSE_H +#define __PCFX_INPUT_MOUSE_H + +namespace MDFN_IEN_PCFX +{ +PCFX_Input_Device *PCFXINPUT_MakeMouse(int which); +} + +#endif diff --git a/waterbox/pcfx/interrupt.cpp b/waterbox/pcfx/interrupt.cpp new file mode 100644 index 0000000000..4820b8cdf9 --- /dev/null +++ b/waterbox/pcfx/interrupt.cpp @@ -0,0 +1,212 @@ +/******************************************************************************/ +/* Mednafen NEC PC-FX Emulation Module */ +/******************************************************************************/ +/* interrupt.cpp: +** Copyright (C) 2006-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "pcfx.h" +#include "interrupt.h" + +namespace MDFN_IEN_PCFX +{ + +static uint16 InterruptAsserted; +static uint16 InterruptMask; +static uint16 InterruptPriority[2]; + +static void BuildInterruptCache(void) +{ + uint32 iwithmask = InterruptAsserted &~ InterruptMask; + int InterruptCache = -1; + int last_prio = -1; + + for(int level = 8; level < 16; level++) + if(iwithmask & (1 << (15 - level))) + { + int tmp_prio; + + if(level >= 12) + tmp_prio = (InterruptPriority[0] >> ((15 - level) * 3)) & 0x7; + else + tmp_prio = (InterruptPriority[1] >> ((11 - level) * 3)) & 0x7; + + if(tmp_prio >= last_prio) + { + if(tmp_prio == last_prio) + { + FXDBG("Undefined IRQ behavior: %d, %d\n", level, tmp_prio); + } + InterruptCache = 8 + tmp_prio; + last_prio = tmp_prio; + } + } + + PCFX_V810.SetInt(InterruptCache); +} + +void PCFXIRQ_Assert(int source, bool assert) +{ + assert(source >= 0 && source <= 7); + + InterruptAsserted &= ~(1 << (7 - source)); + + if(assert) + InterruptAsserted |= (1 << (7 - source)); + + BuildInterruptCache(); +} + + +uint16 PCFXIRQ_Read16(uint32 A) +{ + uint32 ret = 0x00; + + switch(A & 0xC0) + { + case 0x00: ret = InterruptAsserted; break; + case 0x40: ret = InterruptMask; break; + case 0x80: ret = InterruptPriority[0]; break; + case 0xC0: ret = InterruptPriority[1]; break; + } + + return(ret); +} + +uint8 PCFXIRQ_Read8(uint32 A) +{ + return(PCFXIRQ_Read16(A&~1) >> ((A & 1) * 8)); +} + +void PCFXIRQ_Write16(uint32 A, uint16 V) +{ +// printf("IRQ Controller Write: %08x %04x\n", A, V); + switch(A & 0xC0) + { + case 0x00: puts("Address error clear"); + break; + + case 0x40: InterruptMask = V & 0x7F; + BuildInterruptCache(); + break; + + case 0x80: if(InterruptMask == 0x7F) + { + InterruptPriority[0] = V & 0xFFF; + BuildInterruptCache(); + } + break; + + case 0xC0: if(InterruptMask == 0x7F) + { + InterruptPriority[1] = V & 0x1FF; + BuildInterruptCache(); + } + break; + } +} + +void PCFXIRQ_SetRegister(const unsigned int id, uint32 value) +{ + switch(id) + { + case PCFXIRQ_GSREG_IMASK: + InterruptMask = value & 0x7F; + BuildInterruptCache(); + break; + + case PCFXIRQ_GSREG_IPRIO0: + InterruptPriority[0] = value & 0xFFF; + BuildInterruptCache(); + break; + + case PCFXIRQ_GSREG_IPRIO1: + InterruptPriority[1] = value & 0x1FF; + BuildInterruptCache(); + break; + + case PCFXIRQ_GSREG_IPEND: + InterruptAsserted = value; + BuildInterruptCache(); + break; + } +} + +uint32 PCFXIRQ_GetRegister(const unsigned int id, char *special, const uint32 special_len) +{ + uint32 value = 0xDEADBEEF; + + switch(id) + { + case PCFXIRQ_GSREG_IMASK: + value = InterruptMask; + if(special) + { + trio_snprintf(special, special_len, "IRQ Allowed; HuC6273: %s, HuC6270-B: %s, HuC6272: %s, HuC6270-A: %s, Pad: %s, Timer: %s, Reset: %s", + (InterruptMask & (1 << 0)) ? "No" : "Yes", (InterruptMask & (1 << 1)) ? "No" : "Yes", + (InterruptMask & (1 << 2)) ? "No" : "Yes", (InterruptMask & (1 << 3)) ? "No" : "Yes", + (InterruptMask & (1 << 4)) ? "No" : "Yes", (InterruptMask & (1 << 6)) ? "No" : "Yes", + (InterruptMask & (1 << 7)) ? "No" : "Yes"); + } + break; + + case PCFXIRQ_GSREG_IPRIO0: + value = InterruptPriority[0]; + if(special) + { + trio_snprintf(special, special_len, "HuC6273: %d, HuC6270-B: %d, HuC6272: %d, HuC6270-A: %d", + (InterruptPriority[0] >> 0) & 0x7, (InterruptPriority[0] >> 3) & 0x7, + (InterruptPriority[0] >> 6) & 0x7, (InterruptPriority[0] >> 9) & 0x7); + } + break; + + case PCFXIRQ_GSREG_IPRIO1: + value = InterruptPriority[1]; + if(special) + { + trio_snprintf(special, special_len, "Pad: %d, ??: %d, Timer: %d, Reset: %d", + (InterruptPriority[1] >> 0) & 0x7, (InterruptPriority[1] >> 3) & 0x7, + (InterruptPriority[1] >> 6) & 0x7, (InterruptPriority[1] >> 9) & 0x7); + } + break; + + case PCFXIRQ_GSREG_IPEND: + value = InterruptAsserted; + if(special) + { + trio_snprintf(special, special_len, "HuC6273: %d, HuC6270-B: %d, HuC6272: %d, HuC6270-A: %d, Pad: %d, ??: %d, Timer: %d, Reset: %d", (int)(bool)(value & 0x01), (int)(bool)(value & 0x02), + (int)(bool)(value & 0x04), (int)(bool)(value & 0x08), (int)(bool)(value & 0x10), (int)(bool)(value & 0x20), + (int)(bool)(value & 0x40), (int)(bool)(value & 0x80)); + } + break; + } + + return value; +} + +void PCFXIRQ_Reset(void) +{ + InterruptAsserted = 0; + InterruptMask = 0xFFFF; + + InterruptPriority[0] = 0; + InterruptPriority[1] = 0; + + BuildInterruptCache(); +} + +} diff --git a/waterbox/pcfx/interrupt.h b/waterbox/pcfx/interrupt.h new file mode 100644 index 0000000000..da844bb0ed --- /dev/null +++ b/waterbox/pcfx/interrupt.h @@ -0,0 +1,55 @@ +/******************************************************************************/ +/* Mednafen NEC PC-FX Emulation Module */ +/******************************************************************************/ +/* interrupt.h: +** Copyright (C) 2006-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef __PCFX_INTERRUPT_H +#define __PCFX_INTERRUPT_H + +namespace MDFN_IEN_PCFX +{ +#define PCFXIRQ_SOURCE_TIMER 1 +#define PCFXIRQ_SOURCE_EX 2 +#define PCFXIRQ_SOURCE_INPUT 3 +#define PCFXIRQ_SOURCE_VDCA 4 +#define PCFXIRQ_SOURCE_KING 5 +#define PCFXIRQ_SOURCE_VDCB 6 +#define PCFXIRQ_SOURCE_HUC6273 7 + +void PCFXIRQ_Assert(int source, bool assert); +void PCFXIRQ_Write16(uint32 A, uint16 V); +uint16 PCFXIRQ_Read16(uint32 A); +uint8 PCFXIRQ_Read8(uint32 A); + +void PCFXIRQ_Reset(void) MDFN_COLD; + +enum +{ + PCFXIRQ_GSREG_IMASK = 0, + PCFXIRQ_GSREG_IPRIO0, + PCFXIRQ_GSREG_IPRIO1, + PCFXIRQ_GSREG_IPEND +}; + +uint32 PCFXIRQ_GetRegister(const unsigned int id, char *special, const uint32 special_len); +void PCFXIRQ_SetRegister(const unsigned int id, uint32 value); + +} + +#endif diff --git a/waterbox/pcfx/io-handler.inc b/waterbox/pcfx/io-handler.inc new file mode 100644 index 0000000000..58bd16790d --- /dev/null +++ b/waterbox/pcfx/io-handler.inc @@ -0,0 +1,290 @@ +static uint8 MDFN_FASTCALL port_rbyte(v810_timestamp_t ×tamp, uint32 A) +{ + if(A >= 0x000 && A <= 0x0FF) + return(FXINPUT_Read8(A, timestamp)); + else if(A >= 0x100 && A <= 0x1FF) // SOUNDBOX dummy + { + timestamp += 4; + } + else if(A >= 0x200 && A <= 0x2FF) // RAINBOW dummy + { + timestamp += 4; + } + else if(A >= 0x300 && A <= 0x3FF) // FXVCE + { + timestamp += 4; + return(FXVCE_Read16(A)); + } + else if(A >= 0x400 && A <= 0x5FF) // 0x400-0x4FF: VDC-A ; 0x500-0x5FF: VDC-B + { + timestamp += 4; + return(fx_vdc_chips[(A >> 8) & 0x1]->Read16((A & 4) >> 2)); + } + else if(A >= 0x600 && A <= 0x6FF) + { + timestamp += 4; + return(KING_Read8(timestamp, A)); + } + else if(A >= 0x700 && A <= 0x7FF) + { + if(!(A & 1)) + { + FXDBG("ExBusReset B Read"); + return(ExBusReset); + } + return(0); + } + else if(A >= 0xc00 && A <= 0xCFF) // Backup memory control + { + switch(A & 0xC0) + { + case 0x80: return(BackupControl); + case 0x00: return(Last_VDC_AR[0]); + case 0x40: return(Last_VDC_AR[1]); + } + } + else if(A >= 0xe00 && A <= 0xeff) + { + return(PCFXIRQ_Read8(A)); + } + else if(A >= 0xf00 && A <= 0xfff) + { + return(FXTIMER_Read8(A, timestamp)); + } + else if((A & 0x7FFFFFFF) >= 0x500000 && (A & 0x7FFFFFFF) <= 0x52ffff) + { + if(WantHuC6273) + return(HuC6273_Read8(A)); + } + else if(FXSCSIROM && A >= 0x780000 && A <= 0x7FFFFF) + { + return(FXSCSIROM[A & 0x7FFFF]); + } + else if(FXSCSIROM && A >= 0x600000 && A <= 0x6FFFFF) + { + return(FXSCSI_CtrlRead(A)); + } + FXDBG("Unknown 8-bit port read: %08x", A); + + return(0x00); +} + +static uint16 MDFN_FASTCALL port_rhword(v810_timestamp_t ×tamp, uint32 A) +{ + if(A >= 0x000 && A <= 0x0FF) + return(FXINPUT_Read16(A, timestamp)); + else if(A >= 0x100 && A <= 0x1FF) // SOUNDBOX dummy + { + timestamp += 4; + } + else if(A >= 0x200 && A <= 0x2FF) // RAINBOW dummy + { + timestamp += 4; + } + else if(A >= 0x300 && A <= 0x3FF) + { + timestamp += 4; + return(FXVCE_Read16(A)); + } + else if(A >= 0x400 && A <= 0x5FF) // 0x400-0x4FF: VDC-A ; 0x500-0x5FF: VDC-B + { + timestamp += 4; + return(fx_vdc_chips[(A >> 8) & 0x1]->Read16((A & 4) >> 2)); + } + else if(A >= 0x600 && A <= 0x6FF) + { + timestamp += 4; + return(KING_Read16(timestamp, A)); + } + else if(A >= 0x700 && A <= 0x7FF) + { + FXDBG("ExBusReset H Read"); + + return(ExBusReset); + } + else if(A >= 0xc00 && A <= 0xCFF) // Backup memory control + { + switch(A & 0xC0) + { + case 0x80: return(BackupControl); + case 0x00: return(Last_VDC_AR[0]); + case 0x40: return(Last_VDC_AR[1]); + } + } + else if(A >= 0xe00 && A <= 0xeff) + { + return(PCFXIRQ_Read16(A)); + } + else if(A >= 0xf00 && A <= 0xfff) + { + return(FXTIMER_Read16(A, timestamp)); + } + else if((A & 0x7FFFFFFF) >= 0x500000 && (A & 0x7FFFFFFF) <= 0x52ffff) + { + if(WantHuC6273) + return(HuC6273_Read16(A)); + } + else if(FXSCSIROM && A >= 0x780000 && A <= 0x7FFFFF) + { + return MDFN_de16lsb(&FXSCSIROM[A & 0x7FFFF]); + } + else if(FXSCSIROM && A >= 0x600000 && A <= 0x6FFFFF) + { + puts("FXSCSI 16-bit:"); + return(FXSCSI_CtrlRead(A)); + } + + FXDBG("Unknown 16-bit port read: %08x", A); + + return(0x00); +} + +static void MDFN_FASTCALL port_wbyte(v810_timestamp_t ×tamp, uint32 A, uint8 V) +{ + if(A >= 0x000 && A <= 0x0FF) + FXINPUT_Write8(A, V, timestamp); + else if(A >= 0x100 && A <= 0x1FF) + { + timestamp += 2; + SoundBox_Write(A, V, timestamp); + } + else if(A >= 0x200 && A <= 0x2FF) + { + timestamp += 2; + RAINBOW_Write8(A, V); + } + else if(A >= 0x300 && A <= 0x3FF) // FXVCE + { + timestamp += 2; + FXVCE_Write16(A, V); + } + else if(A >= 0x400 && A <= 0x5FF) // 0x400-0x4FF: VDC-A ; 0x500-0x5FF: VDC-B + { + timestamp += 2; + + if(!(A & 4)) + Last_VDC_AR[(A >> 8) & 0x1] = V; + + fx_vdc_chips[(A >> 8) & 0x1]->Write16((A & 4) >> 2, V); + } + else if(A >= 0x600 && A <= 0x6FF) + { + timestamp += 2; + KING_Write8(timestamp, A, V); + } + else if(A >= 0x700 && A <= 0x7FF) + { + if(!(A & 1)) + { + FXDBG("ExBusReset B Write: %02x", V & 1); + ExBusReset = V & 1; + } + } + else if(A >= 0xc00 && A <= 0xCFF) + { + switch(A & 0xC1) + { + case 0x80: BackupControl = V & 0x3; + break; + + default: FXDBG("Port 8-bit write: %08x %02x", A, V); + break; + } + } + else if(A >= 0xe00 && A <= 0xeff) + { + FXDBG("IRQ write8: %08x %02x", A, V); + PCFXIRQ_Write16(A, V); + } + else if((A & 0x7FFFFFFF) >= 0x500000 && (A & 0x7FFFFFFF) <= 0x52ffff) + { + if(WantHuC6273) + HuC6273_Write16(A, V); + } + else if(FXSCSIROM && A >= 0x600000 && A <= 0x6FFFFF) + { + FXSCSI_CtrlWrite(A, V); + } + else + { + FXDBG("Port 8-bit write: %08x %02x", A, V); + } +} + +static void MDFN_FASTCALL port_whword(v810_timestamp_t ×tamp, uint32 A, uint16 V) +{ + if(A >= 0x000 && A <= 0x0FF) + FXINPUT_Write16(A, V, timestamp); + else if(A >= 0x100 && A <= 0x1FF) + { + timestamp += 2; + SoundBox_Write(A, V, timestamp); + } + else if(A >= 0x200 && A <= 0x2FF) + { + timestamp += 2; + RAINBOW_Write16(A, V); + } + else if(A >= 0x300 && A <= 0x3FF) + { + timestamp += 2; + FXVCE_Write16(A, V); + } + else if(A >= 0x400 && A <= 0x5FF) // 0x400-0x4FF: VDC-A ; 0x500-0x5FF: VDC-B + { + timestamp += 2; + + if(!(A & 4)) + Last_VDC_AR[(A >> 8) & 0x1] = V; + + fx_vdc_chips[(A >> 8) & 0x1]->Write16((A & 4) >> 2, V); + } + else if(A >= 0x600 && A <= 0x6FF) + { + timestamp += 2; + KING_Write16(timestamp, A, V); + } + else if(A >= 0x700 && A <= 0x7FF) + { + ExBusReset = V & 1; + FXDBG("ExBusReset H Write: %04x", V); + } + else if(A >= 0x800 && A <= 0x8FF) // ?? LIP writes here + { + FXDBG("Port 16-bit write: %08x %04x", A, V); + } + else if(A >= 0xc00 && A <= 0xCFF) + { + switch(A & 0xC0) + { + case 0x80: BackupControl = V & 0x3; + break; + + default: FXDBG("Port 16-bit write: %08x %04x", A, V); + break; + } + } + else if(A >= 0xe00 && A <= 0xeff) + { + PCFXIRQ_Write16(A, V); + } + else if(A >= 0xF00 && A <= 0xFFF) + { + FXTIMER_Write16(A, V, timestamp); + } + else if((A & 0x7FFFFFFF) >= 0x500000 && (A & 0x7FFFFFFF) <= 0x52ffff) + { + if(WantHuC6273) + HuC6273_Write16(A, V); + } + else if(FXSCSIROM && A >= 0x600000 && A <= 0x6FFFFF) + { + puts("FXSCSI 16-bit:"); + FXSCSI_CtrlWrite(A, V); + } + else + { + FXDBG("Port 16-bit write: %08x %04x", A, V); + } +} + diff --git a/waterbox/pcfx/jrevdct.cpp b/waterbox/pcfx/jrevdct.cpp new file mode 100644 index 0000000000..c1164f5aba --- /dev/null +++ b/waterbox/pcfx/jrevdct.cpp @@ -0,0 +1,284 @@ +/* + * jrevdct.c + * + * Copyright (C) 1991, 1992, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the basic inverse-DCT transformation subroutine. + * + * This implementation is based on an algorithm described in + * C. Loeffler, A. Ligtenberg and G. Moschytz, "Practical Fast 1-D DCT + * Algorithms with 11 Multiplications", Proc. Int'l. Conf. on Acoustics, + * Speech, and Signal Processing 1989 (ICASSP '89), pp. 988-991. + * The primary algorithm described there uses 11 multiplies and 29 adds. + * We use their alternate method with 12 multiplies and 32 adds. + * The advantage of this method is that no data path contains more than one + * multiplication; this allows a very simple and accurate implementation in + * scaled fixed-point arithmetic, with a minimal number of shifts. + */ + +/* Modified 2007-2016 for usage in Mednafen */ + +#include "defs.h" +#include "jrevdct.h" + +namespace MDFN_IEN_PCFX +{ +/* + * This routine is specialized to the case DCTSIZE = 8. + */ +#define DCTSIZE 8 + +/* + * A 2-D IDCT can be done by 1-D IDCT on each row followed by 1-D IDCT + * on each column. Direct algorithms are also available, but they are + * much more complex and seem not to be any faster when reduced to code. + * + * The poop on this scaling stuff is as follows: + * + * Each 1-D IDCT step produces outputs which are a factor of sqrt(N) + * larger than the true IDCT outputs. The final outputs are therefore + * a factor of N larger than desired; since N=8 this can be cured by + * a simple right shift at the end of the algorithm. The advantage of + * this arrangement is that we save two multiplications per 1-D IDCT, + * because the y0 and y4 inputs need not be divided by sqrt(N). + * + * We have to do addition and subtraction of the integer inputs, which + * is no problem, and multiplication by fractional constants, which is + * a problem to do in integer arithmetic. We multiply all the constants + * by CONST_SCALE and convert them to integer constants (thus retaining + * CONST_BITS bits of precision in the constants). After doing a + * multiplication we have to divide the product by CONST_SCALE, with proper + * rounding, to produce the correct output. This division can be done + * cheaply as a right shift of CONST_BITS bits. We postpone shifting + * as long as possible so that partial sums can be added together with + * full fractional precision. + * + * The outputs of the first pass are scaled up by PASS1_BITS bits so that + * they are represented to better-than-integral precision. These outputs + * require BITS_IN_JSAMPLE + PASS1_BITS + 3 bits; this fits in a 16-bit word + * with the recommended scaling. (To scale up 12-bit sample data further, an + * intermediate int32 array would be needed.) + * + * To avoid overflow of the 32-bit intermediate results in pass 2, we must + * have BITS_IN_JSAMPLE + CONST_BITS + PASS1_BITS <= 26. Error analysis + * shows that the values given below are the most effective. + */ + +#define CONST_BITS 13 +#define PASS1_BITS 2 + +#if ((8 + CONST_BITS + PASS1_BITS) > 26) + #error "Too many bits1!" +#endif + +#define ONE ((int32) 1) + +#define CONST_SCALE (ONE << CONST_BITS) + +/* Convert a positive real constant to an integer scaled by CONST_SCALE. */ + +#define FIX(x) ((int32) ((x) * CONST_SCALE + 0.5)) + +/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus + * causing a lot of useless floating-point operations at run time. + * To get around this we use the following pre-calculated constants. + * If you change CONST_BITS you may want to add appropriate values. + * (With a reasonable C compiler, you can just rely on the FIX() macro...) + */ + +#if CONST_BITS == 13 +#define FIX_0_298631336 ((int32) 2446) /* FIX(0.298631336) */ +#define FIX_0_390180644 ((int32) 3196) /* FIX(0.390180644) */ +#define FIX_0_541196100 ((int32) 4433) /* FIX(0.541196100) */ +#define FIX_0_765366865 ((int32) 6270) /* FIX(0.765366865) */ +#define FIX_0_899976223 ((int32) 7373) /* FIX(0.899976223) */ +#define FIX_1_175875602 ((int32) 9633) /* FIX(1.175875602) */ +#define FIX_1_501321110 ((int32) 12299) /* FIX(1.501321110) */ +#define FIX_1_847759065 ((int32) 15137) /* FIX(1.847759065) */ +#define FIX_1_961570560 ((int32) 16069) /* FIX(1.961570560) */ +#define FIX_2_053119869 ((int32) 16819) /* FIX(2.053119869) */ +#define FIX_2_562915447 ((int32) 20995) /* FIX(2.562915447) */ +#define FIX_3_072711026 ((int32) 25172) /* FIX(3.072711026) */ +#else +#define FIX_0_298631336 FIX(0.298631336) +#define FIX_0_390180644 FIX(0.390180644) +#define FIX_0_541196100 FIX(0.541196100) +#define FIX_0_765366865 FIX(0.765366865) +#define FIX_0_899976223 FIX(0.899976223) +#define FIX_1_175875602 FIX(1.175875602) +#define FIX_1_501321110 FIX(1.501321110) +#define FIX_1_847759065 FIX(1.847759065) +#define FIX_1_961570560 FIX(1.961570560) +#define FIX_2_053119869 FIX(2.053119869) +#define FIX_2_562915447 FIX(2.562915447) +#define FIX_3_072711026 FIX(3.072711026) +#endif + + +/* Descale and correctly round an int32 value that's scaled by N bits. + * We assume RIGHT_SHIFT rounds towards minus infinity, so adding + * the fudge factor is correct for either sign of X. + */ + +#define RIGHT_SHIFT(x,shft) ((x) >> (shft)) +#define DESCALE(x,n) RIGHT_SHIFT((x) + (ONE << ((n)-1)), n) +#define MULTIPLY(var,const) ((var) * (const)) + + +/* + * Perform the inverse DCT on one block of coefficients. + */ + +void j_rev_dct(DCTBLOCK data) +{ + int32 tmp0, tmp1, tmp2, tmp3; + int32 tmp10, tmp11, tmp12, tmp13; + int32 z1, z2, z3, z4, z5; + register DCTELEM *dataptr; + int rowctr; + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true IDCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + + dataptr = data; + for (rowctr = DCTSIZE-1; rowctr >= 0; rowctr--) + { + /* Even part: reverse the even part of the forward DCT. */ + /* The rotator is sqrt(2)*c(-6). */ + + z2 = (int32) dataptr[2]; + z3 = (int32) dataptr[6]; + + z1 = MULTIPLY(z2 + z3, FIX_0_541196100); + tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065); + tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865); + + tmp0 = ((uint32) dataptr[0] + (uint32) dataptr[4]) << CONST_BITS; + tmp1 = ((uint32) dataptr[0] - (uint32) dataptr[4]) << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + /* Odd part per figure 8; the matrix is unitary and hence its + * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. + */ + + tmp0 = (int32) dataptr[7]; + tmp1 = (int32) dataptr[5]; + tmp2 = (int32) dataptr[3]; + tmp3 = (int32) dataptr[1]; + + z1 = tmp0 + tmp3; + z2 = tmp1 + tmp2; + z3 = tmp0 + tmp2; + z4 = tmp1 + tmp3; + z5 = MULTIPLY(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */ + + tmp0 = MULTIPLY(tmp0, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ + tmp1 = MULTIPLY(tmp1, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ + tmp2 = MULTIPLY(tmp2, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ + tmp3 = MULTIPLY(tmp3, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ + z1 = MULTIPLY(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ + z2 = MULTIPLY(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ + z3 = MULTIPLY(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ + z4 = MULTIPLY(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 += z2 + z4; + tmp2 += z2 + z3; + tmp3 += z1 + z4; + + /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ + + dataptr[0] = (DCTELEM) DESCALE(tmp10 + tmp3, CONST_BITS-PASS1_BITS); + dataptr[7] = (DCTELEM) DESCALE(tmp10 - tmp3, CONST_BITS-PASS1_BITS); + dataptr[1] = (DCTELEM) DESCALE(tmp11 + tmp2, CONST_BITS-PASS1_BITS); + dataptr[6] = (DCTELEM) DESCALE(tmp11 - tmp2, CONST_BITS-PASS1_BITS); + dataptr[2] = (DCTELEM) DESCALE(tmp12 + tmp1, CONST_BITS-PASS1_BITS); + dataptr[5] = (DCTELEM) DESCALE(tmp12 - tmp1, CONST_BITS-PASS1_BITS); + dataptr[3] = (DCTELEM) DESCALE(tmp13 + tmp0, CONST_BITS-PASS1_BITS); + dataptr[4] = (DCTELEM) DESCALE(tmp13 - tmp0, CONST_BITS-PASS1_BITS); + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. */ + /* Note that we must descale the results by a factor of 8 == 2**3, */ + /* and also undo the PASS1_BITS scaling. */ + + dataptr = data; + for (rowctr = DCTSIZE-1; rowctr >= 0; rowctr--) { + /* Even part: reverse the even part of the forward DCT. */ + /* The rotator is sqrt(2)*c(-6). */ + + z2 = (int32) dataptr[DCTSIZE*2]; + z3 = (int32) dataptr[DCTSIZE*6]; + + z1 = MULTIPLY(z2 + z3, FIX_0_541196100); + tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065); + tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865); + + tmp0 = ((uint32) dataptr[DCTSIZE*0] + (uint32) dataptr[DCTSIZE*4]) << CONST_BITS; + tmp1 = ((uint32) dataptr[DCTSIZE*0] - (uint32) dataptr[DCTSIZE*4]) << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + /* Odd part per figure 8; the matrix is unitary and hence its + * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. + */ + + tmp0 = (int32) dataptr[DCTSIZE*7]; + tmp1 = (int32) dataptr[DCTSIZE*5]; + tmp2 = (int32) dataptr[DCTSIZE*3]; + tmp3 = (int32) dataptr[DCTSIZE*1]; + + z1 = tmp0 + tmp3; + z2 = tmp1 + tmp2; + z3 = tmp0 + tmp2; + z4 = tmp1 + tmp3; + z5 = MULTIPLY(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */ + + tmp0 = MULTIPLY(tmp0, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ + tmp1 = MULTIPLY(tmp1, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ + tmp2 = MULTIPLY(tmp2, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ + tmp3 = MULTIPLY(tmp3, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ + z1 = MULTIPLY(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ + z2 = MULTIPLY(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ + z3 = MULTIPLY(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ + z4 = MULTIPLY(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 += z2 + z4; + tmp2 += z2 + z3; + tmp3 += z1 + z4; + + /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ + + dataptr[DCTSIZE*0] = (DCTELEM) DESCALE(tmp10 + tmp3, CONST_BITS+PASS1_BITS+1); + dataptr[DCTSIZE*7] = (DCTELEM) DESCALE(tmp10 - tmp3, CONST_BITS+PASS1_BITS+1); + dataptr[DCTSIZE*1] = (DCTELEM) DESCALE(tmp11 + tmp2, CONST_BITS+PASS1_BITS+1); + dataptr[DCTSIZE*6] = (DCTELEM) DESCALE(tmp11 - tmp2, CONST_BITS+PASS1_BITS+1); + dataptr[DCTSIZE*2] = (DCTELEM) DESCALE(tmp12 + tmp1, CONST_BITS+PASS1_BITS+1); + dataptr[DCTSIZE*5] = (DCTELEM) DESCALE(tmp12 - tmp1, CONST_BITS+PASS1_BITS+1); + dataptr[DCTSIZE*3] = (DCTELEM) DESCALE(tmp13 + tmp0, CONST_BITS+PASS1_BITS+1); + dataptr[DCTSIZE*4] = (DCTELEM) DESCALE(tmp13 - tmp0, CONST_BITS+PASS1_BITS+1); + + dataptr++; /* advance pointer to next column */ + } +} + +} diff --git a/waterbox/pcfx/jrevdct.h b/waterbox/pcfx/jrevdct.h new file mode 100644 index 0000000000..64a8ad0882 --- /dev/null +++ b/waterbox/pcfx/jrevdct.h @@ -0,0 +1,14 @@ +#ifndef __MDFN_PCFX_JREVDCT_H +#define __MDFN_PCFX_JREVDCT_H + +namespace MDFN_IEN_PCFX +{ + +typedef int32* DCTBLOCK; +typedef int32 DCTELEM; + +void j_rev_dct(DCTBLOCK data); + +} + +#endif diff --git a/waterbox/pcfx/king-bgfast-blit.inc b/waterbox/pcfx/king-bgfast-blit.inc new file mode 100644 index 0000000000..6e365722ad --- /dev/null +++ b/waterbox/pcfx/king-bgfast-blit.inc @@ -0,0 +1,124 @@ +/******************************************************************************/ +/* Mednafen NEC PC-FX Emulation Module */ +/******************************************************************************/ +/* king-bgfast-blit.inc: +** Copyright (C) 2006-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + + switch(bgmode & 0x7) + { + case 0x01: // 4 color, 1/4 byte per pixel :b + sexy_y_pos >>= 3; + for(int x = 0; x < 256 + 8; x+= 8) + { + DRAWBG8x1_LPRE(); + const uint16 *cgptr; + uint32 pbn = 0; + + if(BGFAST_BATMODE) + { + uint16 bat = king_bat_base[(bat_offset + (bat_x + bat_y)) & 0x1FFFF]; + pbn = (bat >> 12) << 2; + bat &= 0x0FFF; + cgptr = &king_cg_base[(cg_offset + (bat * 8) + ysmall) & 0x1FFFF]; + } + else + cgptr = &king_cg_base[(cg_offset + (bat_x * 1) + sexy_y_pos) & 0x1FFFF]; + + DRAWBG8x1_4(target + x, cgptr, palette_ptr + pbn, layer_or); + DRAWBG8x1_LPOST(); + } + break; + case 0x02: // 16 color, 1/2 byte per pixel + sexy_y_pos >>= 2; + for(int x = 0; x < 256 + 8; x+= 8) + { + DRAWBG8x1_LPRE(); + const uint16 *cgptr; + uint32 pbn = 0; + + if(BGFAST_BATMODE) + { + uint16 bat = king_bat_base[(bat_offset + (bat_x + bat_y)) & 0x1FFFF]; + pbn = ((bat >> 12) << 4); + bat &= 0x0FFF; + cgptr = &king_cg_base[(cg_offset + (bat * 16) + ysmall * 2) & 0x1FFFF]; + } + else + cgptr = &king_cg_base[(cg_offset + (bat_x * 2) + sexy_y_pos) & 0x1FFFF]; + + DRAWBG8x1_16(target + x, cgptr, palette_ptr + pbn, layer_or); + DRAWBG8x1_LPOST(); + } + break; + case 0x03: // 256 color, 1 byte per pixel palettized - OK + sexy_y_pos >>= 1; + for(int x = 0; x < 256 + 8; x+= 8) + { + DRAWBG8x1_LPRE(); + const uint16 *cgptr; + + if(BGFAST_BATMODE) + { + uint16 bat = king_bat_base[(bat_offset + (bat_x + bat_y)) & 0x1FFFF]; + cgptr = &king_cg_base[(cg_offset + (bat * 32) + ysmall * 4) & 0x1FFFF]; + } + else + cgptr = &king_cg_base[(cg_offset + (bat_x * 4) + sexy_y_pos) & 0x1FFFF]; + + DRAWBG8x1_256(target + x, cgptr, palette_ptr, layer_or); + DRAWBG8x1_LPOST(); + } + break; + + case 0x04: // 64K color, 2 bytes per pixel - OK + for(int x = 0; x < 256 + 8; x+=8) + { + DRAWBG8x1_LPRE(); + const uint16 *cgptr; + + if(BGFAST_BATMODE) + { + uint16 bat = king_bat_base[(bat_offset + (bat_x + bat_y)) & 0x1FFFF]; + cgptr = &king_cg_base[(cg_offset + (bat * 64) + ysmall * 8) & 0x1FFFF]; + } + else + cgptr = &king_cg_base[(cg_offset + (bat_x * 8) + sexy_y_pos) & 0x1FFFF]; + + DRAWBG8x1_64K(target + x, cgptr, palette_ptr, layer_or); + DRAWBG8x1_LPOST(); + } + break; + + case 0x05: // 16M color, 2 bytes per pixel - OK + for(int x = 0; x < 256 + 8; x+=8) + { + DRAWBG8x1_LPRE(); + const uint16 *cgptr; + if(BGFAST_BATMODE) + { + uint16 bat = king_bat_base[(bat_offset + (bat_x + bat_y)) & 0x1FFFF]; + cgptr = &king_cg_base[(cg_offset + (bat * 64) + ysmall * 8) & 0x1FFFF]; + } + else + cgptr = &king_cg_base[(cg_offset + (bat_x * 8) + sexy_y_pos) & 0x1FFFF]; + + DRAWBG8x1_16M(target + x, cgptr, palette_ptr, layer_or); + DRAWBG8x1_LPOST(); + } + break; + } diff --git a/waterbox/pcfx/king-bgfast.inc b/waterbox/pcfx/king-bgfast.inc new file mode 100644 index 0000000000..e1ef41896f --- /dev/null +++ b/waterbox/pcfx/king-bgfast.inc @@ -0,0 +1,270 @@ +/******************************************************************************/ +/* Mednafen NEC PC-FX Emulation Module */ +/******************************************************************************/ +/* king-bgfast.inc: +** Copyright (C) 2006-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +// Loop prefix non-endless +#define DRAWBG8x1_LPRE() if(bat_x < bat_width) { + +// Loop postfix non-endless +#define DRAWBG8x1_LPOST() } bat_x = (bat_x + 1) & bat_width_mask; + +#define CDBG_REASON(format, ...) +//printf("BG%d Reason: " format "\n", n, ## __VA_ARGS__); +static bool CanDrawBG_Fast(int n) +{ + static const int cg_per_mode[0x8] = + { + 0, // Invalid mode + 1, // 2-bit mode + 2, // 4-bit mode + 4, // 8-bit mode + 8, // 16-bit mode + 8, // 16-bit mode + 8, // 16-bit mode + 8, // 16-bit mode + }; + + const unsigned int bgmode = (king->bgmode >> (n * 4)) & 0xF; + const uint32 bat_offset = king->BGBATAddr[n] * 1024; + const uint32 cg_offset = king->BGCGAddr[n] * 1024; + const uint32 bg_width = (king->BGSize[n] & 0xF0) >> 4; + const uint32 bg_height = king->BGSize[n] & 0x0F; + + // If the bgmode is 0, or 6/7(EXT DOT modes), abort. + if(!(bgmode & 0x7) || ((bgmode & 0x7) >= 6)) + { + CDBG_REASON("Mode %02x out of range", bgmode); + return(FALSE); + } + + // BG width out of range? + if(bg_width < 0x3 || bg_width > 0xA) + { + CDBG_REASON("Width %02x out of range", bg_width); + return(FALSE); + } + + // BG height out of range? + if(bg_height < 0x3 || bg_height > 0xA) + { + CDBG_REASON("Height %02x out of range", bg_height); + return(FALSE); + } + + // Time for very stringent checks for BG0! Don't draw if the sub-screen is a different size than the main screen, + // or the subscreen CG base != the main screen CG base, or the subscreen BAT base != main screen BAT base(when bgmode & 0x8 is TRUE) + if(!n) + { + if(king->priority & 0x1000) + { + CDBG_REASON("Affine transform enabled"); + return(FALSE); + } + + if((king->BGSize[0] & 0xFF) != (king->BGSize[0] >> 8)) + { + CDBG_REASON("Main Screen/Sub Screen Size Mismatch"); + return(FALSE); + } + + // Since we already made sure the main screen/sub screen sizes match, we only + // care if BG base or CG base don't match if endless/repeat scrolling is enabled. + // Otherwise, the subscreen won't show at all. + if(king->BGScrollMode & 0x1) + { + if(king->BGCGAddr[0] != king->BG0SubCGAddr) + { + CDBG_REASON("Main Screen/Sub Screen CG base mismatch"); + return(FALSE); + } + + if(bgmode & 0x8) + { + if(king->BGBATAddr[0] != king->BG0SubBATAddr) + { + CDBG_REASON("Main Screen/Sub Screen BAT base mismatch"); + return(FALSE); + } + } + } + } + + + // If microprogram fetching is disabled, abort. + if(!(king->MPROGControl & 0x1)) + { + CDBG_REASON("Microprogram disabled"); + return(FALSE); + } + else + { + int remap_thing = 0; + bool bat_fetch = FALSE; + + for(int x = 0; x < 16; x++) + { + uint16 mpd; + + // Forcing CG and BAT to 0 if the affine bit != rotate_mode is not technically correct. + // If there is a mismatch, it's more likely the effective CG and BAT address for the pixel/segment + // being drawn won't be calculated correctly, likely being just the initial offsets. + + mpd = king->MPROGData[x]; + + // Fetching for this BG number? + if(((mpd >> 6) & 0x3) != n) + continue; + + // NOP + if(mpd & 0x100) + continue; + + // Affine bit. + if(mpd & 0x20) + { + CDBG_REASON("Affine bit set"); + return(FALSE); + } + + // BAT fetch + if(mpd & 0x10) + { + if(((bat_offset & 0x20000) >> 14) != (x & 8)) + { + CDBG_REASON("BAT MPROG/base bank mismatch"); + return(FALSE); + } + + bat_fetch = TRUE; + } + else // CG fetch: + { + // CG offset/bank mismatch... + if(((cg_offset & 0x20000) >> 14) != (x & 8)) + { + CDBG_REASON("CG MPROG/base bank mismatch"); + return(FALSE); + } + + // Mismatch between CG fetch type and BG mode! + if((mpd & 0x8) != (bgmode & 0x8)) + { + CDBG_REASON("Mismatch between CG fetch type and bg mode"); + return(FALSE); + } + + // Skewed CG fetch order + if((mpd & 0x7) != remap_thing) + { + CDBG_REASON("Skewed CG fetch order"); + return(FALSE); + } + remap_thing++; + } + } + + // CG fetch count mismatch + if(remap_thing != cg_per_mode[bgmode & 0x7]) + { + CDBG_REASON("CG fetch count mismatch"); + return(FALSE); + } + + // BG mode demands a BAT fetch, but we don't have one! + if((bgmode & 0x8) && !bat_fetch) + { + CDBG_REASON("Missing BAT fetch"); + return(FALSE); + } + } + + + return(TRUE); +} + +static void DrawBG_Fast(uint32 *target, int n) +{ + const uint16 bgmode = (king->bgmode >> (n * 4)) & 0xF; + const bool endless = (king->BGScrollMode >> n) & 0x1; + const uint32 XScroll = sign_x_to_s32((n ? 10 : 11), king->BGXScroll[n]); + const uint32 YScroll = sign_x_to_s32((n ? 10 : 11), king->BGYScroll[n]); + const uint32 bat_offset = king->BGBATAddr[n] * 1024; + const uint32 cg_offset = king->BGCGAddr[n] * 1024; + const uint32 bat_and_cg_page = (king->PageSetting & 0x0010) ? 1 : 0; + + const uint32 YOffset = (YScroll + (fx_vce.raster_counter - 22)) & 0xFFFF; + const uint32 layer_or = (LAYER_BG0 + n) << 28; + const int ysmall = YOffset & 0x7; + + const unsigned int bat_width_shift = (king->BGSize[n] & 0xF0) >> 4; + const unsigned int bat_width = (1 << bat_width_shift) >> 3; + const unsigned int bat_width_mask = endless ? (bat_width - 1) : ((((1 << 0xA) * 2) / 8) - 1); + + const int bat_height_shift = king->BGSize[n] & 0x0F; + const int bat_height = (1 << bat_height_shift) >> 3; + const int bat_height_mask = endless ? (bat_height - 1) : ((((1 << 0xA) * 2) / 8) - 1); + + // We don't need to &= cg_offset and bat_offset with 0x1ffff after here, as the effective addresses + // calculated with them are anded with 0x1ffff in the rendering code already. + const uint16 * const king_cg_base = &king->KRAM[bat_and_cg_page][cg_offset & 0x20000]; + const uint16 * const king_bat_base = &king->KRAM[bat_and_cg_page][bat_offset & 0x20000]; + + int bat_y = (YOffset >> 3) & bat_height_mask; + uint32 bat_x = (XScroll >> 3) & bat_width_mask; + + target += 8 - (XScroll & 0x7); + + // If we're in non-endless scroll mode, and we've scrolled past our visible area in the vertical direction, so return. + if(!endless && bat_y >= bat_height) // Draw this line as transparency and return? + { + return; + } + + // Adjust/corrupt bat_y to be faster in our blitting code + bat_y = (bat_y << bat_width_shift) >> 3; + + const uint32 palette_offset = ((vce_rendercache.palette_offset[1 + (n >> 1)] >> ((n & 1) ? 8 : 0)) << 1) & 0x1FF; + const uint32 * const palette_ptr = &vce_rendercache.palette_table_cache[palette_offset]; + + { + int wmul = (1 << bat_width_shift), wmask = (1 << bat_height_shift) - 1; + int sexy_y_pos = (YOffset & wmask) * wmul; + + #if 0 + #define BGFAST_BATMODE (bgmode & 0x8) + #include "king-bgfast-blit.inc" + + #else + if(bgmode & 0x8) + { + #define BGFAST_BATMODE 1 + #include "king-bgfast-blit.inc" + #undef BGFAST_BATMODE + } + else + { + #define BGFAST_BATMODE 0 + #include "king-bgfast-blit.inc" + #undef BGFAST_BATMODE + } + #endif + } +} + diff --git a/waterbox/pcfx/king.cpp b/waterbox/pcfx/king.cpp new file mode 100644 index 0000000000..6a691a83ff --- /dev/null +++ b/waterbox/pcfx/king.cpp @@ -0,0 +1,3842 @@ +/******************************************************************************/ +/* Mednafen NEC PC-FX Emulation Module */ +/******************************************************************************/ +/* king.cpp - Emulation of HuC6261(VCE descendant) and the HuC6272(KING) +** Copyright (C) 2006-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/* Note: Some technical comments may be outdated */ + +/* + Current issues: + VCE "natural" priorities for the layers when their priorities are the same(technically an illegal condition) are probably not correct. A game test case: "Miraculum" erroneously sets + up the priority registers like this after exiting the airship(I believe). + + SCSI RST interrupt timing is guessed(and with nothing to go on), so it definitely needs to be tested on the real thing. + + The data bus is not handled/asserted properly. Excluding pseudo-DMA and DMA modes(which I'd need to test), the data bus will only be asserted if the lower bit of register 0x1 is set, and the + phase match bits must match the state of the C/D, I/O, and MSG signals(IE there isn't a bus mismatch state). + + Raw subchannel reading timing is probably wrong. + + KRAM mode register is not emulated(I'm not even sure what it does exactly). +*/ + +#include "pcfx.h" +#include "king.h" +#include "cdrom/cdromif.h" +#include "cdrom/scsicd.h" +#include "interrupt.h" +#include "rainbow.h" +#include "soundbox.h" +#include "input.h" +#include "timer.h" +#include +//#include +#include "sound/OwlResampler.h" + +#ifdef __MMX__ +#include +#endif + +namespace MDFN_IEN_PCFX +{ + +#define KINGDBG(format, ...) (void)0 +//#define KINGDBG FXDBG +#define KING_UNDEF FXDBG +#define ADPCMDBG(format, ...) (void)0 +//FXDBG + +/* + SCSI Questions(this list needs to be revised more and merged into the issues list at the beginning of the file): + + What happens when there is no more data to transfer during DMA and the status SCSI bus phase is entered(before the DMA count reaches 0)? + + Why is the "sequential DMA" bit needed? + + Which SCSI registers return the values of the SCSI bus, and which return latched values(from previous writes or pseudo-DMA)? + + Is real DMA layered on top of pseudo-DMA? Reading the developer documents, it looks that way. + + What triggers the setting of ACK during pseudo-DMA? A timer? Reading from the upper 16-bits of KING register 0x05? The lower bits(in which case, + the value would be latched..)? + +*/ + +// 16 bit YUV format: upper 8 bits Y, next 4 bits U, lower 4 bits V, transformed to 8-bit U and 8-bit V by shifting in 0 in lower bits. + +typedef struct +{ + uint8 AR; + + uint16 priority[2]; /* uint16 0: + bit 3-0: Legacy VDC BG priority? + bit 7-4: Legacy VDC SPR priority? + bit 11-8: RAINBOW(MJPEG) priority + uint16 1: + bit 3-0: KING BG0 priority + bit 7-4: KING BG1 priority + bit 11-8: KING BG2 priority + bit 15-12: KING BG3 priority + */ + + bool odd_field; /* TRUE if interlaced mode is enabled and we're in the odd field, FALSE otherwise. */ + + bool in_hblank; /* TRUE if we're in H-blank */ + bool in_vdc_hsync; + + bool frame_interlaced; + + uint16 picture_mode; + + bool dot_clock; // Cached from picture_mode in hblank + uint32 dot_clock_ratio; // Cached from picture mode in hblank + int32 clock_divider; + + int32 vdc_event[2]; + + uint32 raster_counter; + + uint16 palette_rw_offset; // Read/write offset + uint16 palette_rw_latch; + + uint16 palette_offset[4]; // + // BMG1 and BMG 0 in [1](BMG1 in upper 8 bits, BMG0 in lower), BMG2 and 3 in [2] + // RAINBOW in lower(?) 8 bits of [3]? + + uint16 palette_table[512]; // The YUV palette, woohoo! + + // Chroma keys, minimum value in lower 8 bits, maximum value in upper 8 bits + uint16 ChromaKeyY; // Register 0xA + uint16 ChromaKeyU; // Register 0xB + uint16 ChromaKeyV; // register 0xC + + uint16 CCR; // Register 0x0D, fixed color register, 16-bit YUV + uint16 BLE; // Register 0x0E, cellophane setting register + // 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00 + // F/B |E/D | IDU |CTRL BMG3|CTRL BMG2|CTRL BMG1|CTRL BMG0| VDP SP | VDP BG + // IDU is AKA Rainbow + // F/B + // 0 = Back cellophane. + // 1 = Front cellophane. + // E/D + // 0 = Back/Front cellophane off + // 1 = Back/Front cellophane on + // Ctrl format: + // 00 = Cellophane disabled. + // 01 = Cellophane with 1A/1B(Coeffs 0, 1?) + // 02 = Cellophane with 2A/2B(Coeffs 2, 3?) + // 03 = Cellophane with 3A/3B(Coeffs 4, 5?) + + uint16 SPBL; // Register 0x0F, Sprite cellophane setting register + // Specifies a color palette bank for which cellophane will always be disabled. + + uint16 coefficients[6]; // Cellophane coefficients, YUV, 4-bits each, on the least-significant end(xxxxYYYYUUUUVVVV). + // Valid settings: 0(cellophane disabled for layer), 1-8. 9-F are "unsupported". +} fx_vce_t; + +fx_vce_t fx_vce; + +// +// VCE render cache, including registers cached at hblank +// +typedef struct +{ + uint16 priority[2]; + uint16 picture_mode; + + uint16 palette_offset[4]; + uint32 palette_table_cache[512 * 2]; // 24-bit YUV cache for SPEED(HAH), * 2 to remove need for & 0x1FF in rendering code + + uint16 ChromaKeyY; + uint16 ChromaKeyU; + uint16 ChromaKeyV; + + uint16 CCR; + uint16 BLE; + + uint16 SPBL; + + uint16 coefficients[6]; + + uint8 coefficient_mul_table_y[16][256]; + int8 coefficient_mul_table_uv[16][256]; + + uint32 LayerPriority[8]; // [LAYER_n] = ordered_priority_for_n(real priority 0-15 mapped to 1-7) + // priority = 0, layer is disabled(via the layer enable bit not being set) +} vce_rendercache_t; + +static vce_rendercache_t vce_rendercache; + +static int32 scsicd_ne; + +enum +{ + HPHASE_ACTIVE = 0, + HPHASE_HBLANK_PART1, + HPHASE_HBLANK_PART3, + HPHASE_HBLANK_PART4, + HPHASE_COUNT +}; + +static int32 HPhase; +static int32 HPhaseCounter; +static uint32 vdc_lb_pos; + +alignas(8) static uint16 vdc_linebuffers[2][512]; +alignas(8) static uint32 vdc_linebuffer[512]; +alignas(8) static uint32 vdc_linebuffer_yuved[512]; +alignas(8) static uint32 rainbow_linebuffer[256]; + +// 8 * 2 for left + right padding for scrolling +alignas(8) static uint32 bg_linebuffer[256 + 8 + 8]; + +// Don't change these enums, there are some hardcoded values still used(particularly, LAYER_NONE). +enum +{ + LAYER_NONE = 0, + LAYER_BG0, + LAYER_BG1, + LAYER_BG2, + LAYER_BG3, + LAYER_VDC_BG, + LAYER_VDC_SPR, + LAYER_RAINBOW +}; + +static uint8 VCEPrioMap[8][8][8][4]; // [n][n][n][3] is dummy, for padding to a power of 2. + +static void BuildCMT(void) +{ + for (int coeff = 0; coeff < 16; coeff++) + { + for (int value = 0; value < 256; value++) + { + vce_rendercache.coefficient_mul_table_y[coeff][value] = (value * coeff / 8); // Y + vce_rendercache.coefficient_mul_table_uv[coeff][value] = ((value - 128) * coeff / 8); // UV + } + } +} + +static INLINE void RebuildLayerPrioCache(void) +{ + vce_rendercache_t *vr = &vce_rendercache; + + vr->LayerPriority[LAYER_NONE] = 0; + + for (int n = 0; n < 4; n++) + { + if (((fx_vce.picture_mode >> (10 + n)) & 1)) + { + vr->LayerPriority[LAYER_BG0 + n] = (((vce_rendercache.priority[1] >> (n * 4)) & 0xF) + 1); + if (vr->LayerPriority[LAYER_BG0 + n] > 8) + { + printf("KING BG%d Priority Too Large: %d\n", n, vr->LayerPriority[LAYER_BG0 + n] - 1); + vr->LayerPriority[LAYER_BG0 + n] = 0; + } + } + else + vr->LayerPriority[LAYER_BG0 + n] = 0; + } + + if (fx_vce.picture_mode & 0x0100) + { + vr->LayerPriority[LAYER_VDC_BG] = ((vce_rendercache.priority[0] & 0xF) + 1); + if (vr->LayerPriority[LAYER_VDC_BG] > 8) + { + printf("VDC BG Priority Too Large: %d\n", vr->LayerPriority[LAYER_VDC_BG] - 1); + vr->LayerPriority[LAYER_VDC_BG] = 0; + } + } + else + vr->LayerPriority[LAYER_VDC_BG] = 0; + + if (fx_vce.picture_mode & 0x0200) + { + vr->LayerPriority[LAYER_VDC_SPR] = (((vce_rendercache.priority[0] >> 4) & 0xF) + 1); + if (vr->LayerPriority[LAYER_VDC_SPR] > 8) + { + printf("VDC SPR Priority Too Large: %d\n", vr->LayerPriority[LAYER_VDC_SPR] - 1); + vr->LayerPriority[LAYER_VDC_SPR] = 0; + } + } + else + vr->LayerPriority[LAYER_VDC_SPR] = 0; + + if (fx_vce.picture_mode & 0x4000) + { + vr->LayerPriority[LAYER_RAINBOW] = (((vce_rendercache.priority[0] >> 8) & 0xF) + 1); + if (vr->LayerPriority[LAYER_RAINBOW] > 8) + { + printf("RAINBOW Priority Too Large: %d\n", vr->LayerPriority[LAYER_RAINBOW] - 1); + vr->LayerPriority[LAYER_RAINBOW] = 0; + } + } + else + vr->LayerPriority[LAYER_RAINBOW] = 0; + + // At this point, all entries in vr->LayerPriority should be one of 0 through 8(inclusive). + + int RemapPriority = 1; + bool Done[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + + for (unsigned int i = 1; i < (1 + 8); i++) + { + for (int n = 0; n < 4; n++) + { + if (vr->LayerPriority[LAYER_BG0 + n] == i && !Done[LAYER_BG0 + n]) + { + vr->LayerPriority[LAYER_BG0 + n] = RemapPriority++; + Done[LAYER_BG0 + n] = true; + } + } + + if (vr->LayerPriority[LAYER_VDC_BG] == i && !Done[LAYER_VDC_BG]) + { + vr->LayerPriority[LAYER_VDC_BG] = RemapPriority++; + Done[LAYER_VDC_BG] = true; + } + + if (vr->LayerPriority[LAYER_VDC_SPR] == i && !Done[LAYER_VDC_SPR]) + { + vr->LayerPriority[LAYER_VDC_SPR] = RemapPriority++; + Done[LAYER_VDC_SPR] = true; + } + + if (vr->LayerPriority[LAYER_RAINBOW] == i && !Done[LAYER_RAINBOW]) + { + vr->LayerPriority[LAYER_RAINBOW] = RemapPriority++; + Done[LAYER_RAINBOW] = true; + } + } + assert(RemapPriority <= 8); + + //if(fx_vce.raster_counter == 50) + // MDFN_DispMessage("%d BG0: %d %d %d %d, VBG: %d, VSPR: %d, RAIN: %d", vr->LayerPriority[0], vr->LayerPriority[1], vr->LayerPriority[2], vr->LayerPriority[3], + // vr->LayerPriority[4], vr->LayerPriority[5], vr->LayerPriority[6], vr->LayerPriority[7]); +} + +// Call this function in FX VCE hblank(or at the end/immediate start of active display) +static void DoHBlankVCECaching(void) +{ + const fx_vce_t *source = &fx_vce; + vce_rendercache_t *dest = &vce_rendercache; + + dest->picture_mode = source->picture_mode; + + fx_vce.dot_clock = (bool)(fx_vce.picture_mode & 0x08); + fx_vce.dot_clock_ratio = (fx_vce.picture_mode & 0x08) ? 3 : 4; + + for (int i = 0; i < 2; i++) + dest->priority[i] = source->priority[i]; + + for (int i = 0; i < 4; i++) + dest->palette_offset[i] = source->palette_offset[i]; + + dest->ChromaKeyY = source->ChromaKeyY; + dest->ChromaKeyU = source->ChromaKeyU; + dest->ChromaKeyV = source->ChromaKeyV; + + dest->CCR = source->CCR; + dest->BLE = source->BLE; + dest->SPBL = source->SPBL; + + for (int i = 0; i < 6; i++) + dest->coefficients[i] = source->coefficients[i]; + + RebuildLayerPrioCache(); +} + +static INLINE void RedoPaletteCache(int n) +{ + uint32 YUV = fx_vce.palette_table[n]; + uint8 Y = (YUV >> 8) & 0xFF; + uint8 U = (YUV & 0xF0); + uint8 V = (YUV & 0x0F) << 4; + + vce_rendercache.palette_table_cache[n] = + vce_rendercache.palette_table_cache[0x200 | n] = (Y << 16) | (U << 8) | (V << 0); +} + +enum +{ + BGMODE_INVALID = 0, + BGMODE_4 = 1, + BGMODE_16 = 2, + BGMODE_256 = 3, + BGMODE_64K = 4, + BGMODE_16M = 5, + BGMODE_64K_EXTDOT = 6, + BGMODE_16M_EXTDOT = 7, +}; + +typedef struct +{ + uint8 AR; + + uint32 KRAMRA, KRAMWA; + uint8 KRAM_Mode; + + uint32 PageSetting; + uint16 *RainbowPagePtr, *DMAPagePtr; // Calculated off of PageSetting + + uint16 bgmode; // 4 bits each BG: 3333 2222 1111 0000 + /* Possible settings: + 0x0: Invalid? + 0x1: 4-color palette, 1 byte for 4 pixels, transparent on entry 0 + 0x2: 16-color palette, 1 byte for 2 pixels, transparent on entry 0 + 0x3: 256-color palette, 1 byte for 1 pixel, transparent on entry 0 + 0x4: 64K color(Y-8, U-4, V-4), 1 halfword for 1 pixel, transparent on Y=0 + 0x5: 16M colors(Y-8, Y-8, U-8, V-8, 4 bytes for 2 pixels), transparent on Y=0 + + If & 8, enable palette bank mode(only for 4 and 16-colors)??? + BAT format would be PPPPCCCCCCCCCCCC in this mode. + 4 color: 00PPPPnn 16 color: PPPPnnnn, where "n" is the 2 or 4-bit pixel data + */ + uint16 priority; + + uint16 BGScrollMode; // Register 0x16 + uint16 BGSize[4]; + + uint8 BGBATAddr[4]; + uint8 BGCGAddr[4]; + uint8 BG0SubBATAddr, BG0SubCGAddr; + + uint16 BGXScroll[4]; + uint16 BGYScroll[4]; + + uint16 BGAffinA, BGAffinB, BGAffinC, BGAffinD; + uint16 BGAffinCenterX, BGAffinCenterY; + + uint16 ADPCMControl; + uint16 ADPCMBufferMode[2]; + + uint16 ADPCMSAL[2]; + + uint32 ADPCMEndAddress[2]; + uint32 ADPCMPlayAddress[2]; + uint16 ADPCMIntermediateAddress[2]; + uint16 ADPCMStatus[2]; // Register 0x53, a bit maimed :) + bool ADPCMIRQPending; + + uint16 RAINBOWTransferControl; // Register 0x40 + uint32 RAINBOWKRAMA; // Register 0x41 + uint16 RAINBOWTransferStartPosition; // Register 0x42, line number(0-262) + uint16 RAINBOWTransferBlockCount; // Register 0x43 + + bool RAINBOWStartPending; + int32 RAINBOWBusyCount, RAINBOWBlockCount; + + uint16 RasterIRQLine; // Register 0x44 + bool RasterIRQPending; + + uint32 RAINBOWKRAMReadPos; + + bool DMATransferFlipFlop; + uint32 DMATransferAddr; // Register 0x09 + uint32 DMATransferSize; // Register 0x0A + uint16 DMAStatus; // Register 0x0B + uint8 DMALatch; + + uint16 MPROGControl; // register 0x15 + uint16 MPROGControlCache; + uint16 MPROGAddress; + uint16 MPROGData[0x10]; + + bool DMAInterrupt; + uint8 Reg00; + uint8 Reg01; + uint8 Reg02; + uint8 Reg03; + + uint8 SubChannelControl; + + bool CDInterrupt, SubChannelInterrupt; + uint8 SubChannelBuf; + uint8 data_cache; + + bool DRQ; + bool dma_receive_active; + bool dma_send_active; + int32 dma_cycle_counter; + int32 lastts; + + uint16 KRAM[2][262144]; + +#define KING_MAGIC_INTERVAL 10 //4 //32 //10 +} king_t; + +static king_t *king = NULL; + +static uint8 BGLayerDisable; +static bool RAINBOWLayerDisable; + +static void RedoKINGIRQCheck(void); + +static INLINE void REGSETP(uint16 ®, const uint8 data, const bool msb) +{ + reg &= 0xFF << (msb ? 0 : 8); + reg |= data << (msb ? 8 : 0); +} + +#ifdef WANT_DEBUGGER +static bool KRAMReadBPE = FALSE; +static bool KRAMWriteBPE = FALSE; + +void KING_NotifyOfBPE(bool read, bool write) +{ + KRAMReadBPE = read; + KRAMWriteBPE = write; + + //FXVDC_SetAux0BPBpase(fx_vdc_chips[0], (read || write) ? 0x80000 : ~0); + //FXVDC_SetAux0BPBpase(fx_vdc_chips[1], (read || write) ? 0x90000 : ~0); +} + +static void (*KINGLog)(const char *, const char *, ...) = NULL; +void KING_SetLogFunc(void (*logfunc)(const char *, const char *, ...)) +{ + KINGLog = logfunc; +} + +uint8 KING_MemPeek(uint32 A) +{ + uint8 ret = king->KRAM[(A & 0x80000) ? 1 : 0][A >> 1] >> ((A & 1) * 8); + + return (ret); +} + +static void Do16BitGet(const char *name, uint32 Address, uint32 Length, uint8 *Buffer) +{ + int wc = 0; + + if (!strcmp(name, "vdcvram0")) + wc = 0; + else if (!strcmp(name, "vdcvram1")) + wc = 1; + + while (Length) + { + uint16 data; + + data = fx_vdc_chips[wc & 1]->PeekVRAM((Address >> 1) & 0xFFFF); + + if ((Address & 1) || Length == 1) + { + *Buffer = data >> ((Address & 1) << 3); + Buffer++; + Address++; + Length--; + } + else + { + Buffer[0] = data & 0xFF; + Buffer[1] = data >> 8; + + Buffer += 2; + Address += 2; + Length -= 2; + } + } +} + +static void Do16BitPut(const char *name, uint32 Address, uint32 Length, uint32 Granularity, bool hl, const uint8 *Buffer) +{ + int wc = 0; + + if (!strcmp(name, "vdcvram0")) + wc = 0; + else if (!strcmp(name, "vdcvram1")) + wc = 1; + + while (Length) + { + uint16 data; + int inc_amount; + + if ((Address & 1) || Length == 1) + { + data = fx_vdc_chips[wc & 1]->PeekVRAM((Address >> 1) & 0xFFFF); + + data &= ~(0xFF << ((Address & 1) << 3)); + data |= *Buffer << ((Address & 1) << 3); + + inc_amount = 1; + } + else + { + data = Buffer[0] | (Buffer[1] << 8); + inc_amount = 2; + } + + fx_vdc_chips[wc & 1]->PokeVRAM((Address >> 1) & 0xFFFF, data); + + Buffer += inc_amount; + Address += inc_amount; + Length -= inc_amount; + } +} + +static void KING_GetAddressSpaceBytes(const char *name, uint32 Address, uint32 Length, uint8 *Buffer) +{ + if (!strcmp(name, "kram0") || !strcmp(name, "kram1")) + { + int wk = name[4] - '0'; + + while (Length--) + { + *Buffer = king->KRAM[wk][(Address >> 1) & 0x3ffff] >> ((Address & 1) * 8); + Address++; + Buffer++; + } + } + else if (!strcmp(name, "vce")) + { + while (Length--) + { + Address &= 0x3FF; + *Buffer = fx_vce.palette_table[Address >> 1] >> ((Address & 1) * 8); + Address++; + Buffer++; + } + } +} + +static void KING_PutAddressSpaceBytes(const char *name, uint32 Address, uint32 Length, uint32 Granularity, bool hl, const uint8 *Buffer) +{ + if (!strcmp(name, "kram0") || !strcmp(name, "kram1")) + { + int wk = name[4] - '0'; + + while (Length--) + { + REGSETP(king->KRAM[wk][(Address >> 1) & 0x3ffff], *Buffer, Address & 1); + Address++; + Buffer++; + } + } + else if (!strcmp(name, "vce")) + { + while (Length--) + { + Address &= 0x3FF; + REGSETP(fx_vce.palette_table[Address >> 1], *Buffer, Address & 1); + RedoPaletteCache(Address >> 1); + Address++; + Buffer++; + } + } +} +#endif + +static void RecalcKRAMPagePtrs(void) +{ + king->RainbowPagePtr = king->KRAM[(king->PageSetting & 0x1000) ? 1 : 0]; + king->DMAPagePtr = king->KRAM[king->PageSetting & 1]; +} + +uint8 KING_RB_Fetch(void) +{ + uint8 ret = king->RainbowPagePtr[(king->RAINBOWKRAMReadPos >> 1) & 0x3FFFF] >> ((king->RAINBOWKRAMReadPos & 1) * 8); + + king->RAINBOWKRAMReadPos = ((king->RAINBOWKRAMReadPos + 1) & 0x3FFFF) | (king->RAINBOWKRAMReadPos & 0x40000); + + return (ret); +} + +static void DoRealDMA(uint8 db) +{ + if (!king->DMATransferFlipFlop) + king->DMALatch = db; + else + { + king->DMAPagePtr[king->DMATransferAddr & 0x3FFFF] = king->DMALatch | (db << 8); + king->DMATransferAddr = ((king->DMATransferAddr + 1) & 0x1FFFF) | (king->DMATransferAddr & 0x20000); + king->DMATransferSize = (king->DMATransferSize - 2) & 0x3FFFF; + if (!king->DMATransferSize) + { + KINGDBG("DMA Done\n"); + king->DMAInterrupt = TRUE; + RedoKINGIRQCheck(); + king->DMAStatus &= ~1; + return; + } + } + king->DMATransferFlipFlop ^= 1; +} + +uint16 FXVCE_Read16(uint32 A) +{ + // bit 4-0: Register number + // bit 13-5: Raster counter + // bit 14: In interlace mode and when on odd fields set bit. + // bit 15: "0" during blanking(h or v), "1" during active screen area + // Raster counter increments a few cycles after the start of hblank. + + if (!(A & 0x4)) + { + uint16 fullret = 0; + + fullret |= fx_vce.AR; + fullret |= fx_vce.odd_field ? 0x4000 : 0x0000; + fullret |= fx_vce.raster_counter << 5; + + if (fx_vce.in_hblank || fx_vce.raster_counter < 22 || fx_vce.raster_counter == 262) + fullret |= 0 << 15; // Clear on blanking + else + fullret |= 1 << 15; // Set on active display. + + return (fullret); + } + else + { + switch (fx_vce.AR) // No idea which registers are readable, so make them all readable :b + { + default: + break; + case 0x00: + return (fx_vce.picture_mode); + case 0x01: + return (fx_vce.palette_rw_offset); + case 0x03: // Boundary Gate reads from 0x03 expecting palette data... + case 0x02: + { + uint16 ret = fx_vce.palette_rw_latch; + fx_vce.palette_rw_offset = (fx_vce.palette_rw_offset + 1) & 0x1FF; + fx_vce.palette_rw_latch = fx_vce.palette_table[fx_vce.palette_rw_offset]; + return (ret); + } + case 0x04: + return (fx_vce.palette_offset[0]); + case 0x05: + return (fx_vce.palette_offset[1]); + case 0x06: + return (fx_vce.palette_offset[2]); + case 0x07: + return (fx_vce.palette_offset[3]); + case 0x08: + return (fx_vce.priority[0]); + case 0x09: + return (fx_vce.priority[1]); + case 0x0a: + return (fx_vce.ChromaKeyY); + case 0x0b: + return (fx_vce.ChromaKeyU); + case 0x0c: + return (fx_vce.ChromaKeyV); + + case 0x0d: + return (fx_vce.CCR); + case 0x0e: + return (fx_vce.BLE); + case 0x0f: + return (fx_vce.SPBL); + case 0x10: + return (fx_vce.coefficients[0]); + case 0x11: + return (fx_vce.coefficients[1]); + + case 0x12: + return (fx_vce.coefficients[2]); + case 0x13: + return (fx_vce.coefficients[3]); + + case 0x14: + return (fx_vce.coefficients[4]); + case 0x15: + return (fx_vce.coefficients[5]); + } + } + + return (0); +} + +void FXVCE_Write16(uint32 A, uint16 V) +{ + if (!(A & 0x4)) + { + fx_vce.AR = V & 0x1F; + } + else + { + switch (fx_vce.AR) + { + case 0x00: + fx_vce.picture_mode = V; + break; + + case 0x01: + fx_vce.palette_rw_offset = V & 0x1FF; + fx_vce.palette_rw_latch = fx_vce.palette_table[fx_vce.palette_rw_offset]; + break; + + case 0x02: + fx_vce.palette_rw_latch = V; + fx_vce.palette_table[fx_vce.palette_rw_offset] = fx_vce.palette_rw_latch; + RedoPaletteCache(fx_vce.palette_rw_offset); + fx_vce.palette_rw_offset = (fx_vce.palette_rw_offset + 1) & 0x1FF; + break; + + case 0x04: + fx_vce.palette_offset[0] = V; + break; + case 0x05: + fx_vce.palette_offset[1] = V; + break; + case 0x06: + fx_vce.palette_offset[2] = V; + break; + case 0x07: + fx_vce.palette_offset[3] = V & 0x00FF; + break; + case 0x08: + fx_vce.priority[0] = V & 0x0777; + break; + case 0x09: + fx_vce.priority[1] = V & 0x7777; + break; + + case 0x0a: + fx_vce.ChromaKeyY = V; + break; + case 0x0b: + fx_vce.ChromaKeyU = V; + break; + case 0x0c: + fx_vce.ChromaKeyV = V; + break; + + case 0x0d: + fx_vce.CCR = V; + break; + case 0x0e: + fx_vce.BLE = V; + break; + case 0x0f: + fx_vce.SPBL = V; + break; + + case 0x10: + fx_vce.coefficients[0] = V & 0xFFF; + break; + case 0x11: + fx_vce.coefficients[1] = V & 0xFFF; + break; + + case 0x12: + fx_vce.coefficients[2] = V & 0xFFF; + break; + case 0x13: + fx_vce.coefficients[3] = V & 0xFFF; + break; + + case 0x14: + fx_vce.coefficients[4] = V & 0xFFF; + break; + case 0x15: + fx_vce.coefficients[5] = V & 0xFFF; + break; + } + } +} + +static void RedoKINGIRQCheck(void) +{ + bool asserted = 0; + + if (king->ADPCMIRQPending) + { + asserted = 1; + } + + if (king->DMAInterrupt && (king->DMAStatus & 0x2)) + { + asserted = 1; + } + + if (king->CDInterrupt) + { + asserted = 1; + } + + if (king->SubChannelInterrupt) + asserted = 1; + + if (king->RasterIRQPending) + asserted = 1; + + PCFXIRQ_Assert(PCFXIRQ_SOURCE_KING, asserted); +} + +void KING_CDIRQ(int type) +{ + if (type == SCSICD_IRQ_MAGICAL_REQ) + { + if (king->Reg02 & 0x2) + { + if (SCSICD_GetIO() != ((king->Reg03 >> 0) & 1) || + SCSICD_GetCD() != ((king->Reg03 >> 1) & 1) || + SCSICD_GetMSG() != ((king->Reg03 >> 2) & 1)) + { + KINGDBG("Phase mismatch interrupt asserted.\n"); + king->CDInterrupt = TRUE; + RedoKINGIRQCheck(); + } + } + } +} + +void KING_StuffSubchannels(uint8 subchannels, int subindex) +{ + if (king->SubChannelControl & 0x1) + { + if (subindex == -2) + king->SubChannelBuf = 0x00; + else if (subindex == -1) + king->SubChannelBuf = 0x80; + else + king->SubChannelBuf = (subchannels & 0x7F); + + if (king->SubChannelControl & 0x2) + { + king->SubChannelInterrupt = TRUE; + RedoKINGIRQCheck(); + } + } +} + +uint8 KING_Read8(const v810_timestamp_t timestamp, uint32 A) +{ + uint8 ret = KING_Read16(timestamp, A & ~1) >> ((A & 1) * 8); + + //printf("Read8: %04x\n", A); + return (ret); +} + +void KING_EndFrame(v810_timestamp_t timestamp) +{ + PCFX_SetEvent(PCFX_EVENT_KING, KING_Update(timestamp)); + scsicd_ne = SCSICD_Run(timestamp); +} + +void KING_ResetTS(v810_timestamp_t ts_base) +{ + SCSICD_ResetTS(ts_base); + + king->lastts = ts_base; + + if (king->dma_cycle_counter & 0x40000000) + { + king->dma_cycle_counter = 0x7FFFFFFF; + } +} + +//static INLINE void StartKingMagic(void) +//{ +// king->lastts = v810_timestamp; +// king->dma_cycle_counter = KING_MAGIC_INTERVAL; +// PCFX_SetEvent(PCFX_EVENT_KING, KING_MAGIC_INTERVAL); +//} + +static INLINE int32 CalcNextEvent(int32 next_event) +{ + if (king->dma_cycle_counter < next_event) + next_event = king->dma_cycle_counter; + + if (scsicd_ne < next_event) + next_event = scsicd_ne; + + return (next_event); +} + +static int32 CalcNextExternalEvent(int32 next_event) +{ + // 100 = Hack to make the emulator go faster during CD DMA transfers. + if (king->dma_cycle_counter < next_event) + next_event = 100; //king->dma_cycle_counter; + + if (scsicd_ne < next_event) + next_event = scsicd_ne; + + if (next_event > HPhaseCounter) + next_event = HPhaseCounter; + + //printf("KING: %d %d %d; %d\n", king->dma_cycle_counter, scsicd_ne, HPhaseCounter, next_event); + + for (int chip = 0; chip < 2; chip++) + { + int fwoom = (fx_vce.vdc_event[chip] * fx_vce.dot_clock_ratio - fx_vce.clock_divider); + + if (fwoom < 1) + fwoom = 1; + + if (next_event > fwoom) + next_event = fwoom; + } + + return (next_event); +} + +static void MDFN_FASTCALL KING_RunGfx(int32 clocks); + +v810_timestamp_t MDFN_FASTCALL KING_Update(const v810_timestamp_t timestamp) +{ + int32 clocks = timestamp - king->lastts; + uint32 running_timestamp = king->lastts; + + //printf("KING Run for: %d\n", clocks); + + king->lastts = timestamp; + + KING_RunGfx(clocks); + + while (clocks > 0) + { + int32 chunk_clocks = CalcNextEvent(clocks); + + running_timestamp += chunk_clocks; + clocks -= chunk_clocks; + + scsicd_ne -= chunk_clocks; + if (scsicd_ne <= 0) + scsicd_ne = SCSICD_Run(running_timestamp); + + king->dma_cycle_counter -= chunk_clocks; + if (king->dma_cycle_counter <= 0) + { + //assert(king->dma_receive_active || king->dma_send_active); + king->dma_cycle_counter += KING_MAGIC_INTERVAL; + if (king->dma_receive_active) + { + if (!SCSICD_GetCD() && SCSICD_GetIO()) + { + if (SCSICD_GetREQ() && !SCSICD_GetACK()) + { + if (!king->DRQ) + { + king->DRQ = TRUE; + king->data_cache = SCSICD_GetDB(); + //SCSICD_SetACK(TRUE); + //PCFX_SetEvent(PCFX_EVENT_SCSI, SCSICD_Run(timestamp)); + + if (king->DMAStatus & 0x1) + { + king->DRQ = FALSE; + DoRealDMA(king->data_cache); + SCSICD_SetACK(TRUE); + scsicd_ne = SCSICD_Run(running_timestamp); + } + } + } + else if (SCSICD_GetACK() && !SCSICD_GetREQ()) + { + SCSICD_SetACK(FALSE); + scsicd_ne = SCSICD_Run(running_timestamp); + } + } + } + else if (king->dma_send_active) + { + if (!SCSICD_GetIO()) + { + if (SCSICD_GetREQ() && !SCSICD_GetACK()) + { + if (!king->DRQ) + { + //KINGDBG("Did write: %02x\n", king->data_cache); + SCSICD_SetDB(king->data_cache); + SCSICD_SetACK(TRUE); + scsicd_ne = SCSICD_Run(running_timestamp); + king->DRQ = TRUE; + } + } + else if (SCSICD_GetACK() && !SCSICD_GetREQ()) + { + SCSICD_SetACK(FALSE); + scsicd_ne = SCSICD_Run(running_timestamp); + } + } + } + } + } // end while(clocks > 0) + + return (timestamp + CalcNextExternalEvent(0x4FFFFFFF)); +} + +uint16 KING_Read16(const v810_timestamp_t timestamp, uint32 A) +{ + int msh = A & 2; + uint16 ret = 0; + + KING_Update(timestamp); + + //printf("KRead16: %08x, %d; %04x\n", A, timestamp, king->AR); + + switch (A & 0x704) + { + case 0x600: // ?CDSRP?? AAAAAAAA + // C = 0x4000, SCSI interrupt + // D = 0x2000, DMA IRQ + // S = 0x1000, CD Subchannel IRQ? + // R = 0x0800, Raster IRQ + // P = 0x0400, ADPCM IRQ + + if (!msh) + { + ret = king->AR; + + if (king->ADPCMIRQPending) + ret |= 0x400; + + if (king->SubChannelInterrupt) + ret |= 0x1000; + + // Gaaah, this is probably a hack...Anime Freak FX Vol 4 gets confused and crashes + // if both bits are set at once. + if (king->DMAInterrupt && (king->DMAStatus & 0x2)) + ret |= 0x2000; + else if (king->CDInterrupt) + ret |= 0x4000; + + if (king->RasterIRQPending) + ret |= 0x800; + + king->SubChannelInterrupt = FALSE; + king->RasterIRQPending = FALSE; + RedoKINGIRQCheck(); + } + else + { + ret |= SCSICD_GetSEL() ? 0x02 : 0x00; + ret |= SCSICD_GetIO() ? 0x04 : 0x00; + ret |= SCSICD_GetCD() ? 0x08 : 0x00; + ret |= SCSICD_GetMSG() ? 0x10 : 0x00; + ret |= SCSICD_GetREQ() ? 0x20 : 0x00; + ret |= SCSICD_GetBSY() ? 0x40 : 0x00; + ret |= SCSICD_GetRST() ? 0x80 : 0x00; + + ret |= king->SubChannelBuf << 8; + } + break; // status... + + case 0x604: + switch (king->AR) + { + default: + KINGDBG("Unknown 16-bit register read: %02x\n", king->AR); + break; + + case 0x00: + ret = SCSICD_GetDB(); + break; + + case 0x01: + ret = REGGETHW(king->Reg01, msh); + break; + + case 0x02: + ret = REGGETHW(king->Reg02, msh); + break; + + case 0x03: + ret = REGGETHW(king->Reg03, msh); + break; + + case 0x04: + if (!msh) + { + ret |= SCSICD_GetSEL() ? 0x02 : 0x00; + ret |= SCSICD_GetIO() ? 0x04 : 0x00; + ret |= SCSICD_GetCD() ? 0x08 : 0x00; + ret |= SCSICD_GetMSG() ? 0x10 : 0x00; + ret |= SCSICD_GetREQ() ? 0x20 : 0x00; + ret |= SCSICD_GetBSY() ? 0x40 : 0x00; + ret |= SCSICD_GetRST() ? 0x80 : 0x00; + } + break; + + case 0x05: + if (king->Reg01 & 0x80) + { + ret = 0x00; + break; + } + + if (msh) + { + ret = king->data_cache; + //printf("Fooball: %02x\n", ret); + if (king->dma_receive_active) + { + king->DRQ = FALSE; + SCSICD_SetACK(TRUE); + scsicd_ne = 1; + } + } + else + { + ret |= SCSICD_GetACK() ? 0x01 : 0x00; + ret |= SCSICD_GetATN() ? 0x02 : 0x00; + + if (king->dma_receive_active || king->dma_send_active) + if (king->DRQ) + ret |= 0x40; + + // Gaaah, this is probably a hack...Anime Freak FX Vol 4 gets confused and crashes + // if both bits are set at once. + if (!king->DMAInterrupt) + ret |= king->CDInterrupt ? 0x10 : 0x00; + + if (SCSICD_GetIO() == ((king->Reg03 >> 0) & 1) && + SCSICD_GetCD() == ((king->Reg03 >> 1) & 1) && + SCSICD_GetMSG() == ((king->Reg03 >> 2) & 1)) + { + ret |= 0x8; // Phase match + } + } + break; + + case 0x06: // SCSI Input Data Register, same value returned as reading D16-D23 of register 0x05? + KINGDBG("Input data for...?\n"); + ret = king->data_cache; + break; + + case 0x07: + // SCSI IRQ acknowledge/reset + KINGDBG("SCSI IRQ acknowledge\n"); + king->CDInterrupt = FALSE; + RedoKINGIRQCheck(); + ret = 0xFF; + break; + + case 0x08: // Sub-channel data + if (!msh) + { + ret = king->SubChannelBuf; + king->SubChannelBuf = 0; + //puts("Sub-channel data read."); + } + break; + + case 0x09: + ret = REGGETHW(king->DMATransferAddr, msh); + break; + + case 0x0A: + ret = REGGETHW(king->DMATransferSize, msh); + break; + + case 0x0B: // Value read in the BIOS always seems to be discarded... DMA IRQ acknowledge? + if (!msh) + { + ret = king->DMAInterrupt ? 1 : 0; + KINGDBG("DMA IRQ Acknowledge: %d\n", ret); + king->DMAInterrupt = 0; + RedoKINGIRQCheck(); + } + break; + + case 0x0C: + ret = REGGETHW(king->KRAMRA, msh); + break; + + case 0x0D: + ret = REGGETHW(king->KRAMWA, msh); + break; + + case 0x0E: + { + unsigned int page = (king->KRAMRA & 0x80000000) ? 1 : 0; + int32 inc_amount = ((int32)((king->KRAMRA & (0x3FF << 18)) << 4)) >> 22; // Convert from 10-bit signed 2's complement + + ret = king->KRAM[page][king->KRAMRA & 0x3FFFF]; + +#ifdef WANT_DEBUGGER + if (KRAMReadBPE) + PCFXDBG_CheckBP(BPOINT_AUX_READ, (king->KRAMRA & 0x3FFFF) | (page ? 0x40000 : 0), 0, 1); +#endif + + king->KRAMRA = (king->KRAMRA & ~0x1FFFF) | ((king->KRAMRA + inc_amount) & 0x1FFFF); + } + break; + + case 0x0F: + ret = king->PageSetting; + break; + + case 0x10: + ret = REGGETHW(king->bgmode, msh); + break; + + case 0x15: + ret = king->MPROGControl; + break; + + //case 0x40: break; // Super Power League FX reads this, but I think it's write-only. + + case 0x53: + { + ret = king->ADPCMStatus[0] | (king->ADPCMStatus[1] << 2); + + king->ADPCMStatus[0] = king->ADPCMStatus[1] = 0; + king->ADPCMIRQPending = 0; + + RedoKINGIRQCheck(); + + ADPCMDBG("Status read: %02x\n", ret); + } + break; + } + break; + } + + PCFX_SetEvent(PCFX_EVENT_KING, timestamp + CalcNextExternalEvent(0x4FFFFFFF)); // TODO: Optimize this to only be called when necessary. + + return (ret); +} + +void KING_Write8(const v810_timestamp_t timestamp, uint32 A, uint8 V) +{ + KING_Write16(timestamp, A & 0x706, V << ((A & 1) ? 8 : 0)); +} + +static INLINE void SCSI_Reg0_Write(const v810_timestamp_t timestamp, uint8 V, bool delay_run = 0) +{ + king->Reg00 = V; + SCSICD_SetDB(V); + + KINGDBG("WriteDB: %02x\n", V); + + if (!delay_run) + { + scsicd_ne = 1; //SCSICD_Run(timestamp); + } +} + +static INLINE void SCSI_Reg2_Write(const v810_timestamp_t timestamp, uint8 V, bool delay_run = 0) +{ + KINGDBG("SCSI Mode: %04x\n", V); + + /* SCSI Mode Register + D0 = SED: When using with sequential DMA mode, you use. + (It sets the normal DMA mode time to "0".) + Sequential DMA, the number of transfer data bytes which are set to the SCSI device (m) HuC6272 (REG.A) the number + of transfer bytes (n) it sets more largely, abbreviates the status message command phase after the n byte + transferring and being something which makes the transfer after the n + 1 byte possible, it is possible to + increase the transfer performance from the slow SCSI device of CD-ROM and the like. + Sequential DMA cannot use with imitation DMA. When this bit is designated as 1, because -ACK the signal + mandatorily ネゲート it is done, other than the stipulated sequence please do not use. + + D1 = DMA Mode: + When using the SCSI control section with normal DMA mode, "1" is set. + */ + if (!(V & 0x2) && (king->Reg02 & 0x2)) + { + + { // HACK(probably) + king->CDInterrupt = FALSE; + RedoKINGIRQCheck(); + } + + SCSICD_SetACK(0); + + if (!delay_run) + { + scsicd_ne = 1; //SCSICD_Run(timestamp); + } + king->DRQ = FALSE; + + king->dma_receive_active = FALSE; + king->dma_send_active = FALSE; + king->dma_cycle_counter = 0x7FFFFFFF; + } + + king->Reg02 = V; +} + +static INLINE void SCSI_Reg3_Write(const v810_timestamp_t timestamp, uint8 V, bool delay_run = 0) +{ + KINGDBG("Set phase match SCSI bus bits: IO: %d, CD: %d, MSG: %d\n", (int)(bool)(V & 1), (int)(bool)(V & 2), (int)(bool)(V & 4)); + king->Reg03 = V & 0x7; + + if (!delay_run) + { + scsicd_ne = 1; //SCSICD_Run(timestamp); + } +} + +void KING_Write16(const v810_timestamp_t timestamp, uint32 A, uint16 V) +{ + int msh = A & 0x2; + + //printf("Write16: %08x %04x\n", A, V); + + if (!(A & 0x4)) + { + if (!msh) + king->AR = V & 0x7F; + } + else + { + //if(king->AR != 0x0E) + // printf("KING: %02x %04x, %d\n", king->AR, V, fx_vce.raster_counter); + KING_Update(timestamp); + + if (king->AR >= 0x50 && king->AR <= 0x5E) + { + //ADPCMDBG("Write: %02x(%d), %04x", king->AR, msh, V); + } + + switch (king->AR) + { + default: + KINGDBG("Unknown 16-bit register write: %02x %04x %d\n", king->AR, V, msh); + break; + + case 0x00: + if (king->Reg01 & 0x80) + break; + + if (!msh) + { + SCSI_Reg0_Write(timestamp, V); + } + break; + + case 0x01: + if (!msh) + { + KINGDBG("Set SCSI BUS bits; Assert DB: %d, ATN: %d, SEL: %d, ACK: %d, RST: %d, %02x\n", + (int)(bool)(V & 1), (int)(bool)(V & 2), (int)(bool)(V & 4), + (int)(bool)(V & 0x10), (int)(bool)(V & 0x80), SCSICD_GetDB()); + + if (V & 0x80) // RST, silly KING, resets SCSI internal control registers too! + { + if (!(king->Reg01 & 0x80)) + { + SCSI_Reg0_Write(timestamp, 0, TRUE); + SCSI_Reg2_Write(timestamp, 0, TRUE); + SCSI_Reg3_Write(timestamp, 0, TRUE); + king->data_cache = 0x00; + + //king->CDInterrupt = true; + //RedoKINGIRQCheck(); + //puts("KING RST IRQ"); + } + + king->Reg01 = V & 0x80; // Only this bit remains...how lonely. + } + else + { + king->Reg01 = V & (1 | 2 | 4 | 0x10 | 0x80); + + SCSICD_SetATN(V & 2); + SCSICD_SetSEL(V & 4); + SCSICD_SetACK(V & 0x10); + } + SCSICD_SetRST(V & 0x80); + scsicd_ne = 1; + } + break; + + case 0x02: + if (king->Reg01 & 0x80) + break; + + if (!msh) + { + SCSI_Reg2_Write(timestamp, V); + } + break; + + case 0x03: + if (king->Reg01 & 0x80) + break; + + if (!msh) + { + SCSI_Reg3_Write(timestamp, V); + } + break; + + case 0x05: + if (king->Reg01 & 0x80) + break; + + if (!msh) // Start DMA target receive + { + KINGDBG("DMA target receive: %04x, %d\n", V, msh); + king->dma_receive_active = FALSE; + king->dma_send_active = TRUE; + king->DRQ = TRUE; + //StartKingMagic(); + king->dma_cycle_counter = KING_MAGIC_INTERVAL; + } + else + { + if (king->dma_send_active && king->DRQ) + { + //KINGDBG("%02x\n", V); + king->data_cache = V; + king->DRQ = FALSE; + } + } + break; + + case 0x06: + break; // Not used for writes? + + case 0x07: + if (king->Reg01 & 0x80) + break; + + KINGDBG("Start DMA initiator receive: %04x\n", V); + + if (king->Reg02 & 0x2) + { + king->dma_receive_active = TRUE; + king->dma_send_active = FALSE; + //StartKingMagic(); + king->dma_cycle_counter = KING_MAGIC_INTERVAL; + } + break; + + case 0x08: // Sub-channel control + KINGDBG("Sub-channel control: %02x\n", V); + + king->SubChannelControl = V & 0x3; + king->SubChannelInterrupt = FALSE; + RedoKINGIRQCheck(); + break; + + case 0x09: + REGSETHW(king->DMATransferAddr, V, msh); + king->DMATransferAddr &= 0x3FFFF; + break; + case 0x0A: + REGSETHW(king->DMATransferSize, V, msh); + king->DMATransferSize &= 0x3FFFE; + king->DMATransferFlipFlop = 0; + break; + case 0x0B: + REGSETHW(king->DMAStatus, V, msh); + king->DMAStatus &= 0x3; + king->DMAInterrupt = 0; + RedoKINGIRQCheck(); + + king->DMATransferFlipFlop = 0; + + KINGDBG("SCSI DMA: %04x, dest=%06x, page=%d, size=%06x(16-bit words)\n", V, king->DMATransferAddr, king->PageSetting & 1, king->DMATransferSize >> 1); + +#ifdef WANT_DEBUGGER + if (KINGLog) + { + //if(V & 1) + // KINGLog("KING", "SCSI DMA: dest=%06x, page=%d, size=%06x(16-bit words)", king->DMATransferAddr, king->PageSetting & 1, king->DMATransferSize >> 1); + } +#endif + break; + case 0x0C: + REGSETHW(king->KRAMRA, V, msh); + break; + case 0x0D: + REGSETHW(king->KRAMWA, V, msh); + break; + case 0x0E: + { + unsigned int page = (king->KRAMWA & 0x80000000) ? 1 : 0; + int32 inc_amount = ((int32)((king->KRAMWA & (0x3FF << 18)) << 4)) >> 22; // Convert from 10-bit signed 2's complement + +#ifdef WANT_DEBUGGER + if (KRAMWriteBPE) + PCFXDBG_CheckBP(BPOINT_AUX_WRITE, (king->KRAMWA & 0x3FFFF) | (page ? 0x40000 : 0), V, 1); +#endif + + king->KRAM[page][king->KRAMWA & 0x3FFFF] = V; + king->KRAMWA = (king->KRAMWA & ~0x1FFFF) | ((king->KRAMWA + inc_amount) & 0x1FFFF); + } + break; + + // Page settings(0/1) for BG, DMA, ADPCM, and RAINBOW transfers. + case 0x0F: + REGSETHW(king->PageSetting, V, msh); + RecalcKRAMPagePtrs(); + break; + + // Background Modes + case 0x10: + REGSETHW(king->bgmode, V, msh); + break; + + // Background priorities and affine transform master enable. + case 0x12: + if (!msh) + { + king->priority = V; + if (king->priority & ~0x1FFF) + { + KING_UNDEF("Invalid priority bits set: %04x\n", king->priority); + } + } + break; + + // Microprogram Address + case 0x13: + if (!msh) + { + king->MPROGAddress = V & 0xF; + } + break; + + // Microprogram Data Port + case 0x14: + if (!msh) + { + king->MPROGData[king->MPROGAddress] = V; + king->MPROGAddress = (king->MPROGAddress + 1) & 0xF; + } + break; + + case 0x15: + REGSETHW(king->MPROGControl, V, msh); + king->MPROGControl &= 0x1; + break; + + case 0x16: + REGSETHW(king->BGScrollMode, V, msh); + king->BGScrollMode &= 0xF; + break; + + case 0x20: + REGSETHW(king->BGBATAddr[0], V, msh); + break; + case 0x21: + REGSETHW(king->BGCGAddr[0], V, msh); + break; + case 0x22: + REGSETHW(king->BG0SubBATAddr, V, msh); + break; + case 0x23: + REGSETHW(king->BG0SubCGAddr, V, msh); + break; + + case 0x24: + REGSETHW(king->BGBATAddr[1], V, msh); + break; + case 0x25: + REGSETHW(king->BGCGAddr[1], V, msh); + break; + case 0x28: + REGSETHW(king->BGBATAddr[2], V, msh); + break; + case 0x29: + REGSETHW(king->BGCGAddr[2], V, msh); + break; + case 0x2A: + REGSETHW(king->BGBATAddr[3], V, msh); + break; + case 0x2B: + REGSETHW(king->BGCGAddr[3], V, msh); + break; + + case 0x2C: + REGSETHW(king->BGSize[0], V, msh); + break; + case 0x2D: + REGSETHW(king->BGSize[1], V, msh); + king->BGSize[1] &= 0x00FF; + break; + case 0x2E: + REGSETHW(king->BGSize[2], V, msh); + king->BGSize[2] &= 0x00FF; + break; + case 0x2F: + REGSETHW(king->BGSize[3], V, msh); + king->BGSize[3] &= 0x00FF; + break; + + case 0x30: + REGSETHW(king->BGXScroll[0], V, msh); + king->BGXScroll[0] &= 0x7FF; + break; + case 0x31: + REGSETHW(king->BGYScroll[0], V, msh); + king->BGYScroll[0] &= 0x7FF; + break; + + case 0x32: + REGSETHW(king->BGXScroll[1], V, msh); + king->BGXScroll[1] &= 0x3FF; + break; + case 0x33: + REGSETHW(king->BGYScroll[1], V, msh); + king->BGYScroll[1] &= 0x3FF; + break; + + case 0x34: + REGSETHW(king->BGXScroll[2], V, msh); + king->BGXScroll[2] &= 0x3FF; + break; + case 0x35: + REGSETHW(king->BGYScroll[2], V, msh); + king->BGYScroll[2] &= 0x3FF; + break; + + case 0x36: + REGSETHW(king->BGXScroll[3], V, msh); + king->BGXScroll[3] &= 0x3FF; + break; + case 0x37: + REGSETHW(king->BGYScroll[3], V, msh); + king->BGYScroll[3] &= 0x3FF; + break; + + case 0x38: + REGSETHW(king->BGAffinA, V, msh); + break; + case 0x39: + REGSETHW(king->BGAffinB, V, msh); + break; + case 0x3a: + REGSETHW(king->BGAffinC, V, msh); + break; + case 0x3b: + REGSETHW(king->BGAffinD, V, msh); + break; + case 0x3c: + REGSETHW(king->BGAffinCenterX, V, msh); + break; + case 0x3d: + REGSETHW(king->BGAffinCenterY, V, msh); + break; + + case 0x40: // ------IE + // I = 1, interrupt enable?? + // E = 1, rainbow transfer enable + if (!msh) + { + king->RAINBOWTransferControl = V & 0x3; + if (!(V & 1)) + { + //if(king->RAINBOWBusyCount || king->RAINBOWBlockCount) + // puts("RAINBOW transfer reset"); + // Not sure if this is completely correct or not. Test cases: "Tonari no Princess Rolfee", (others?) + //king->RAINBOWBusyCount = 0; + //king->RAINBOWBlockCount = 0; + //RAINBOW_ForceTransferReset(); + king->RAINBOWBlockCount = 0; + } + } + king->RasterIRQPending = FALSE; + RedoKINGIRQCheck(); + //printf("Transfer Control: %d, %08x\n", fx_vce.raster_counter, king->RAINBOWTransferControl); + break; + + // Rainbow transfer address + case 0x41: + REGSETHW(king->RAINBOWKRAMA, V, msh); + king->RAINBOWKRAMA &= 0x3FFFF; + //printf("KRAM Transfer Addr: %d, %08x\n", fx_vce.raster_counter, king->RAINBOWKRAMA); + break; + + // 0-262 + case 0x42: + if (!msh) + { + king->RAINBOWTransferStartPosition = V & 0x1FF; + //fprintf(stderr, "%d\n", king->RAINBOWTransferStartPosition); + //printf("RAINBOW Start Line: %d, %08x\n", fx_vce.raster_counter, king->RAINBOWTransferStartPosition); + } + break; + + case 0x43: + REGSETHW(king->RAINBOWTransferBlockCount, V, msh); + king->RAINBOWTransferBlockCount &= 0x1F; + //printf("KRAM Transfer Block Count: %d, %08x\n", fx_vce.raster_counter, king->RAINBOWTransferBlockCount); + break; + + // Raster IRQ line + case 0x44: + if (!msh) + { + king->RasterIRQLine = V & 0x1FF; + //printf("Raster IRQ scanline: %d, %08x\n", fx_vce.raster_counter, king->RasterIRQLine); + } + break; + + case 0x50: + if (!msh) + { + for (int ch = 0; ch < 2; ch++) + { + if (!(king->ADPCMControl & (1 << ch)) && (V & (1 << ch))) + { + king->ADPCMPlayAddress[ch] = king->ADPCMSAL[ch] * 256; + } + } + king->ADPCMControl = V; + RedoKINGIRQCheck(); + SoundBox_SetKINGADPCMControl(king->ADPCMControl); + } + break; + + case 0x51: + REGSETHW(king->ADPCMBufferMode[0], V, msh); + RedoKINGIRQCheck(); + break; + + case 0x52: + REGSETHW(king->ADPCMBufferMode[1], V, msh); + RedoKINGIRQCheck(); + break; + + case 0x58: + REGSETHW(king->ADPCMSAL[0], V, msh); + king->ADPCMSAL[0] &= 0x3FF; + break; + case 0x59: + REGSETHW(king->ADPCMEndAddress[0], V, msh); + king->ADPCMEndAddress[0] &= 0x3FFFF; + break; + case 0x5A: + REGSETHW(king->ADPCMIntermediateAddress[0], V, msh); + king->ADPCMIntermediateAddress[0] &= 0xFFF; + break; + + case 0x5C: + REGSETHW(king->ADPCMSAL[1], V, msh); + king->ADPCMSAL[1] &= 0x3FF; + break; + case 0x5D: + REGSETHW(king->ADPCMEndAddress[1], V, msh); + king->ADPCMEndAddress[1] &= 0x3FFFF; + break; + case 0x5E: + REGSETHW(king->ADPCMIntermediateAddress[1], V, msh); + king->ADPCMIntermediateAddress[1] &= 0xFFF; + break; + + case 0x61: + if (king->KRAM_Mode ^ V) + { + KINGDBG("KRAM Mode Change To: %02x\n", V & 1); + king->KRAM_Mode = V & 0x1; + } + break; + } + + PCFX_SetEvent(PCFX_EVENT_KING, timestamp + CalcNextExternalEvent(0x4FFFFFFF)); // TODO: Optimize this to only be called when necessary. + } +} + +uint16 KING_GetADPCMHalfWord(int ch) +{ + int page = (king->PageSetting & 0x0100) ? 1 : 0; + uint16 ret = king->KRAM[page][king->ADPCMPlayAddress[ch] & 0x3FFFF]; + + king->ADPCMPlayAddress[ch] = (king->ADPCMPlayAddress[ch] & 0x20000) | ((king->ADPCMPlayAddress[ch] + 1) & 0x1FFFF); + + if (!(king->ADPCMPlayAddress[ch] & 0x1FFFF)) + { + ADPCMDBG("Ch %d Wrapped", ch); + } + + if (king->ADPCMPlayAddress[ch] == (((king->ADPCMEndAddress[ch] + 1) & 0x1FFFF) | (king->ADPCMEndAddress[ch] & 0x20000))) + { + ADPCMDBG("Ch %d End", ch); + + if (!(king->ADPCMBufferMode[ch] & 1)) + { + king->ADPCMControl &= ~(1 << ch); + SoundBox_SetKINGADPCMControl(king->ADPCMControl); + } + else + { + king->ADPCMPlayAddress[ch] = king->ADPCMSAL[ch] << 8; + } + + king->ADPCMStatus[ch] |= 1; + + if (king->ADPCMBufferMode[ch] & (0x1 << 1)) + { + king->ADPCMIRQPending = TRUE; + RedoKINGIRQCheck(); + } + } + else if (king->ADPCMPlayAddress[ch] == ((uint32)king->ADPCMIntermediateAddress[ch] << 6)) + { + ADPCMDBG("Ch %d Intermediate", ch); + king->ADPCMStatus[ch] |= 2; + + if (king->ADPCMBufferMode[ch] & (0x2 << 1)) + { + king->ADPCMIRQPending = TRUE; + RedoKINGIRQCheck(); + } + } + + return (ret); +} + +static uint32 HighDotClockWidth; +extern RavenBuffer *FXCDDABufs[2]; // FIXME, externals are evil! + +void KING_Init(void) +{ + king = new king_t(); + memset(king, 0, sizeof(king_t)); + + king->lastts = 0; + + HighDotClockWidth = Setting_HighDotclockWidth; + BGLayerDisable = 0; + + BuildCMT(); + + // Build VCE priority map. + // Don't change this unless you know what you're doing! + // There may appear to be a bug in the pixel mixing + // code elsewhere, because it accesses this array like [vdc][bg][rainbow], but it's not a bug. + // This multi-dimensional array has no concept of bg, vdc, rainbow, or their orders per-se, it just + // contains priority information for 3 different layers. + + for (int bg_prio = 0; bg_prio < 8; bg_prio++) + { + for (int vdc_prio = 0; vdc_prio < 8; vdc_prio++) + { + for (int rainbow_prio = 0; rainbow_prio < 8; rainbow_prio++) + { + int bg_prio_test = bg_prio ? bg_prio : 0x10; + int vdc_prio_test = vdc_prio ? vdc_prio : 0x10; + int rainbow_prio_test = rainbow_prio ? rainbow_prio : 0x10; + + if (bg_prio_test >= 8) + VCEPrioMap[bg_prio][vdc_prio][rainbow_prio][0] = 3; + else + { + if (bg_prio_test < vdc_prio_test && bg_prio_test < rainbow_prio_test) + VCEPrioMap[bg_prio][vdc_prio][rainbow_prio][0] = 0; + else if (bg_prio_test > vdc_prio_test && bg_prio_test > rainbow_prio_test) + VCEPrioMap[bg_prio][vdc_prio][rainbow_prio][0] = 2; + else + VCEPrioMap[bg_prio][vdc_prio][rainbow_prio][0] = 1; + } + + if (vdc_prio_test >= 8) + VCEPrioMap[bg_prio][vdc_prio][rainbow_prio][1] = 3; + else + { + if (vdc_prio_test < bg_prio_test && vdc_prio_test < rainbow_prio_test) + VCEPrioMap[bg_prio][vdc_prio][rainbow_prio][1] = 0; + else if (vdc_prio_test > bg_prio_test && vdc_prio_test > rainbow_prio_test) + VCEPrioMap[bg_prio][vdc_prio][rainbow_prio][1] = 2; + else + VCEPrioMap[bg_prio][vdc_prio][rainbow_prio][1] = 1; + } + + if (rainbow_prio_test >= 8) + VCEPrioMap[bg_prio][vdc_prio][rainbow_prio][2] = 3; + else + { + if (rainbow_prio_test < bg_prio_test && rainbow_prio_test < vdc_prio_test) + VCEPrioMap[bg_prio][vdc_prio][rainbow_prio][2] = 0; + else if (rainbow_prio_test > bg_prio_test && rainbow_prio_test > vdc_prio_test) + VCEPrioMap[bg_prio][vdc_prio][rainbow_prio][2] = 2; + else + VCEPrioMap[bg_prio][vdc_prio][rainbow_prio][2] = 1; + } + } + } + } + + /*#ifdef WANT_DEBUGGER + ASpace_Add(KING_GetAddressSpaceBytes, KING_PutAddressSpaceBytes, "kram0", "KRAM Page 0", 19); + ASpace_Add(KING_GetAddressSpaceBytes, KING_PutAddressSpaceBytes, "kram1", "KRAM Page 1", 19); + ASpace_Add(Do16BitGet, Do16BitPut, "vdcvram0", "VDC-A VRAM", 17); + ASpace_Add(Do16BitGet, Do16BitPut, "vdcvram1", "VDC-B VRAM", 17); + ASpace_Add(KING_GetAddressSpaceBytes, KING_PutAddressSpaceBytes, "vce", "VCE Palette RAM", 10); +#endif*/ + + SCSICD_Init(SCSICD_PCFX, 3, FXCDDABufs[0]->Buf(), FXCDDABufs[1]->Buf(), 153600 * Setting_CdSpeed, 21477273, KING_CDIRQ, KING_StuffSubchannels); +} + +void KING_Reset(const v810_timestamp_t timestamp) +{ + KING_Update(timestamp); + + memset(&fx_vce, 0, sizeof(fx_vce)); + + int32 ltssave = king->lastts; + memset(king, 0, sizeof(king_t)); + king->lastts = ltssave; + + king->Reg00 = 0; + king->Reg01 = 0; + king->Reg02 = 0; + king->Reg03 = 0; + king->dma_receive_active = FALSE; + king->dma_send_active = FALSE; + king->dma_cycle_counter = 0x7FFFFFFF; + + RecalcKRAMPagePtrs(); + + HPhase = HPHASE_HBLANK_PART1; + HPhaseCounter = 1; + vdc_lb_pos = 0; + + memset(vdc_linebuffers, 0, sizeof(vdc_linebuffers)); + memset(vdc_linebuffer, 0, sizeof(vdc_linebuffer)); + memset(vdc_linebuffer_yuved, 0, sizeof(vdc_linebuffer_yuved)); + memset(rainbow_linebuffer, 0, sizeof(rainbow_linebuffer)); + memset(bg_linebuffer, 0, sizeof(bg_linebuffer)); + + king->dma_cycle_counter = 0x7FFFFFFF; + scsicd_ne = 1; // FIXME + + RedoKINGIRQCheck(); + + for (unsigned int x = 0; x < 0x200; x++) + RedoPaletteCache(x); + + DoHBlankVCECaching(); + + SoundBox_SetKINGADPCMControl(0); + + SCSICD_Power(timestamp); + + memset(king->KRAM, 0xFF, sizeof(king->KRAM)); +} + +static INLINE void DRAWBG8x1_4(uint32 *target, const uint16 *cg, const uint32 *palette_ptr, const uint32 layer_or) +{ + if (*cg >> 14) + target[0] = palette_ptr[(*cg >> 14)] | layer_or; + if ((*cg >> 12) & 0x3) + target[1] = palette_ptr[((*cg >> 12) & 0x3)] | layer_or; + if ((*cg >> 10) & 0x3) + target[2] = palette_ptr[((*cg >> 10) & 0x3)] | layer_or; + if ((*cg >> 8) & 0x3) + target[3] = palette_ptr[((*cg >> 8) & 0x3)] | layer_or; + if ((*cg >> 6) & 0x3) + target[4] = palette_ptr[((*cg >> 6) & 0x3)] | layer_or; + if ((*cg >> 4) & 0x3) + target[5] = palette_ptr[((*cg >> 4) & 0x3)] | layer_or; + if ((*cg >> 2) & 0x3) + target[6] = palette_ptr[((*cg >> 2) & 0x3)] | layer_or; + if ((*cg >> 0) & 0x3) + target[7] = palette_ptr[((*cg >> 0) & 0x3)] | layer_or; +} + +static INLINE void DRAWBG8x1_16(uint32 *target, const uint16 *cgptr, const uint32 *palette_ptr, const uint32 layer_or) +{ + if (cgptr[0] >> 12) + target[0] = palette_ptr[((cgptr[0] >> 12))] | layer_or; + if ((cgptr[0] >> 8) & 0xF) + target[1] = palette_ptr[(((cgptr[0] >> 8) & 0xF))] | layer_or; + if ((cgptr[0] >> 4) & 0xF) + target[2] = palette_ptr[(((cgptr[0] >> 4) & 0xF))] | layer_or; + if ((cgptr[0] >> 0) & 0xF) + target[3] = palette_ptr[(((cgptr[0] >> 0) & 0xF))] | layer_or; + + if (cgptr[1] >> 12) + target[4] = palette_ptr[((cgptr[1] >> 12))] | layer_or; + if ((cgptr[1] >> 8) & 0xF) + target[5] = palette_ptr[(((cgptr[1] >> 8) & 0xF))] | layer_or; + if ((cgptr[1] >> 4) & 0xF) + target[6] = palette_ptr[(((cgptr[1] >> 4) & 0xF))] | layer_or; + if ((cgptr[1] >> 0) & 0xF) + target[7] = palette_ptr[(((cgptr[1] >> 0) & 0xF))] | layer_or; +} + +static INLINE void DRAWBG8x1_256(uint32 *target, const uint16 *cgptr, const uint32 *palette_ptr, const uint32 layer_or) +{ + if (cgptr[0] >> 8) + target[0] = palette_ptr[(cgptr[0] >> 0x8)] | layer_or; + if (cgptr[0] & 0xFF) + target[1] = palette_ptr[(cgptr[0] & 0xFF)] | layer_or; + if (cgptr[1] >> 8) + target[2] = palette_ptr[(cgptr[1] >> 0x8)] | layer_or; + if (cgptr[1] & 0xFF) + target[3] = palette_ptr[(cgptr[1] & 0xFF)] | layer_or; + if (cgptr[2] >> 8) + target[4] = palette_ptr[(cgptr[2] >> 0x8)] | layer_or; + if (cgptr[2] & 0xFF) + target[5] = palette_ptr[(cgptr[2] & 0xFF)] | layer_or; + if (cgptr[3] >> 8) + target[6] = palette_ptr[(cgptr[3] >> 0x8)] | layer_or; + if (cgptr[3] & 0xFF) + target[7] = palette_ptr[(cgptr[3] & 0xFF)] | layer_or; +} + +static INLINE void DRAWBG8x1_64K(uint32 *target, const uint16 *cgptr, const uint32 *palette_ptr, const uint32 layer_or) +{ + if (cgptr[0] & 0xFF00) + target[0] = ((cgptr[0x0] & 0x00F0) << 8) | ((cgptr[0] & 0x000F) << 4) | ((cgptr[0] & 0xFF00) << 8) | layer_or; + if (cgptr[1] & 0xFF00) + target[1] = ((cgptr[0x1] & 0x00F0) << 8) | ((cgptr[1] & 0x000F) << 4) | ((cgptr[1] & 0xFF00) << 8) | layer_or; + if (cgptr[2] & 0xFF00) + target[2] = ((cgptr[0x2] & 0x00F0) << 8) | ((cgptr[2] & 0x000F) << 4) | ((cgptr[2] & 0xFF00) << 8) | layer_or; + if (cgptr[3] & 0xFF00) + target[3] = ((cgptr[0x3] & 0x00F0) << 8) | ((cgptr[3] & 0x000F) << 4) | ((cgptr[3] & 0xFF00) << 8) | layer_or; + if (cgptr[4] & 0xFF00) + target[4] = ((cgptr[0x4] & 0x00F0) << 8) | ((cgptr[4] & 0x000F) << 4) | ((cgptr[4] & 0xFF00) << 8) | layer_or; + if (cgptr[5] & 0xFF00) + target[5] = ((cgptr[0x5] & 0x00F0) << 8) | ((cgptr[5] & 0x000F) << 4) | ((cgptr[5] & 0xFF00) << 8) | layer_or; + if (cgptr[6] & 0xFF00) + target[6] = ((cgptr[0x6] & 0x00F0) << 8) | ((cgptr[6] & 0x000F) << 4) | ((cgptr[6] & 0xFF00) << 8) | layer_or; + if (cgptr[7] & 0xFF00) + target[7] = ((cgptr[0x7] & 0x00F0) << 8) | ((cgptr[7] & 0x000F) << 4) | ((cgptr[7] & 0xFF00) << 8) | layer_or; +} + +static INLINE void DRAWBG8x1_16M(uint32 *target, const uint16 *cgptr, const uint32 *palette_ptr, const uint32 layer_or) +{ + if (cgptr[0] >> 8) + target[0] = ((cgptr[0x0] & 0xFF00) << 8) | (cgptr[1] & 0xFF00) | (cgptr[1] & 0xFF) | layer_or; + if (cgptr[0] & 0xFF) + target[1] = ((cgptr[0x0] & 0x00FF) << 16) | (cgptr[1] & 0xFF00) | (cgptr[1] & 0xFF) | layer_or; + if (cgptr[2] >> 8) + target[2] = ((cgptr[0x2] & 0xFF00) << 8) | (cgptr[3] & 0xFF00) | (cgptr[3] & 0xFF) | layer_or; + if (cgptr[2] & 0xFF) + target[3] = ((cgptr[0x2] & 0x00FF) << 16) | (cgptr[3] & 0xFF00) | (cgptr[3] & 0xFF) | layer_or; + if (cgptr[4] >> 8) + target[4] = ((cgptr[0x4] & 0xFF00) << 8) | (cgptr[5] & 0xFF00) | (cgptr[5] & 0xFF) | layer_or; + if (cgptr[4] & 0xFF) + target[5] = ((cgptr[0x4] & 0x00FF) << 16) | (cgptr[5] & 0xFF00) | (cgptr[5] & 0xFF) | layer_or; + if (cgptr[6] >> 8) + target[6] = ((cgptr[0x6] & 0xFF00) << 8) | (cgptr[7] & 0xFF00) | (cgptr[7] & 0xFF) | layer_or; + if (cgptr[6] & 0xFF) + target[7] = ((cgptr[0x6] & 0x00FF) << 16) | (cgptr[7] & 0xFF00) | (cgptr[7] & 0xFF) | layer_or; +} + +static bool bgmode_warning = 0; // Debug + +#include "king-bgfast.inc" + +static INLINE int32 max(int32 a, int32 b) +{ + if (a > b) + return (a); + + return (b); +} + +static void DrawBG(uint32 *target, int n, bool sub) +{ + // Size out-of-bounds behaves as if the size is at its minimum, with caveats(TO BE INVESTIGATED). + const uint32 bg_ss_table[0x10] = + { + 0x3, 0x3, 0x3, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0x3, 0x3, 0x3, 0x3, 0x3}; + + const bool bg_ss_invalid_table[0x10] = + { + 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1}; + +#if 0 + const uint32 cg_per_mode[0x8] = + { + 0, // Invalid mode + 1, // 2-bit mode + 2, // 4-bit mode + 4, // 8-bit mode + 8, // 16-bit mode + 8, // 16-bit mode + 8, // 16-bit mode + 8, // 16-bit mode + }; +#endif + const uint32 layer_or = (LAYER_BG0 + n) << 28; + + const uint32 palette_offset = ((fx_vce.palette_offset[1 + (n >> 1)] >> ((n & 1) ? 8 : 0)) << 1) & 0x1FF; + const uint32 *palette_ptr = &vce_rendercache.palette_table_cache[palette_offset]; + const uint32 bat_and_cg_page = (king->PageSetting & 0x0010) ? 1 : 0; + + const uint16 bgmode = (king->bgmode >> (n * 4)) & 0xF; + const bool endless = (king->BGScrollMode >> n) & 0x1; + const uint32 XScroll = sign_x_to_s32((n ? 10 : 11), king->BGXScroll[n]); + const uint32 YScroll = sign_x_to_s32((n ? 10 : 11), king->BGYScroll[n]); + + const uint32 YOffset = (YScroll + (fx_vce.raster_counter - 22)) & 0xFFFF; + + const uint32 bat_offset = king->BGBATAddr[n] * 1024; + const uint32 bat_sub_offset = n ? bat_offset : (king->BG0SubBATAddr * 1024); + const uint16 *bat_base = &king->KRAM[bat_and_cg_page][bat_offset & 0x20000]; + const uint16 *bat_sub_base = &king->KRAM[bat_and_cg_page][bat_sub_offset & 0x20000]; + + const uint32 cg_offset = king->BGCGAddr[n] * 1024; + const uint32 cg_sub_offset = n ? cg_offset : (king->BG0SubCGAddr * 1024); + const uint16 *cg_base = &king->KRAM[bat_and_cg_page][cg_offset & 0x20000]; + const uint16 *cg_sub_base = &king->KRAM[bat_and_cg_page][cg_sub_offset & 0x20000]; + + const int bat_bitsize_mask = 0x7FF >> 3; + + const uint32 bat_width_shift = bg_ss_table[(king->BGSize[n] & 0xF0) >> 4]; + const bool bat_width_invalid = bg_ss_invalid_table[(king->BGSize[n] & 0xF0) >> 4]; + const uint32 bat_width = (1 << bat_width_shift) >> 3; + + const int32 bat_height_shift = bg_ss_table[king->BGSize[n] & 0x0F]; + //const bool bat_height_invalid = bg_ss_invalid_table[king->BGSize[n] & 0x0F]; + const int32 bat_height = (1 << bat_height_shift) >> 3; + + const bool bat_sub_width_invalid = n ? bat_width_invalid : bg_ss_invalid_table[(king->BGSize[n] & 0xF000) >> 12]; + const uint32 bat_sub_width_shift = n ? bat_width_shift : bg_ss_table[(king->BGSize[n] & 0xF000) >> 12]; + const uint32 bat_sub_width = (1 << bat_sub_width_shift) >> 3; + const uint32 bat_sub_width_mask = bat_sub_width - 1; + const uint32 bat_sub_width_test = endless ? (bat_bitsize_mask + 1) : max(bat_width, bat_sub_width); + + const int32 bat_sub_height_shift = n ? bat_height_shift : bg_ss_table[(king->BGSize[n] & 0x0F00) >> 8]; + const int32 bat_sub_height = (1 << bat_sub_height_shift) >> 3; + const int32 bat_sub_height_mask = bat_sub_height - 1; + const int32 bat_sub_height_test = endless ? (bat_bitsize_mask + 1) : max(bat_height, bat_sub_height); + + uint16 cg_mask[8]; + uint16 cg_remap[8]; + bool cg_ofbat[8]; + + uint16 cg_sub_mask[8]; + uint16 cg_sub_remap[8]; + bool cg_sub_ofbat[8]; + + bool BATFetchCycle = FALSE; + bool BATSubFetchCycle = FALSE; + + const bool rotate_mode = (n == 0) && (king->priority & 0x1000); + + // If the bg mode is invalid, don't draw this layer, duuhhhh + if (!(bgmode & 0x7)) + return; + + if ((bgmode & 0x7) >= 6) + { + if (!bgmode_warning) + { + printf("Unsupported KING BG Mode for KING BG %d: %02x\n", n, bgmode); + bgmode_warning = TRUE; + } + return; + } + + memset(cg_mask, 0, sizeof(cg_mask)); + memset(cg_remap, 0, sizeof(cg_remap)); + memset(cg_ofbat, 0, sizeof(cg_ofbat)); + memset(cg_sub_mask, 0, sizeof(cg_sub_mask)); + memset(cg_sub_remap, 0, sizeof(cg_sub_remap)); + memset(cg_sub_ofbat, 0, sizeof(cg_sub_ofbat)); + + if (king->MPROGControl & 0x1) + { + int remap_thing = 0; + int remap_sub_thing = 0; + + for (int x = 0; x < 8; x++) + { + uint16 mpd; + + // Forcing CG and BAT to 0 if the affine bit != rotate_mode is not technically correct. + // If there is a mismatch, it's more likely the effective CG and BAT address for the pixel/segment + // being drawn won't be calculated correctly, likely being just the initial offsets. + + mpd = king->MPROGData[((cg_offset & 0x20000) ? 0x8 : 0x0) + x]; + if (((mpd >> 6) & 0x3) == n && !(mpd & 0x100) && !(mpd & 0x010)) + { + cg_mask[remap_thing] = 0xFFFF; + cg_remap[remap_thing] = mpd & 0x7; + cg_ofbat[remap_thing] = mpd & 0x8; + + if ((bool)(mpd & 0x20) != rotate_mode) + { + KINGDBG("Affine bit != rotate_mode?"); + cg_mask[remap_thing] = 0; + } + remap_thing++; + } + + mpd = king->MPROGData[((cg_sub_offset & 0x20000) ? 0x8 : 0x0) + x]; + if (((mpd >> 6) & 0x3) == n && !(mpd & 0x100) && !(mpd & 0x010)) + { + cg_sub_mask[remap_sub_thing] = 0xFFFF; + cg_sub_remap[remap_sub_thing] = mpd & 0x7; + cg_sub_ofbat[remap_sub_thing] = mpd & 0x8; + + if ((bool)(mpd & 0x20) != rotate_mode) + { + KINGDBG("Affine bit != rotate_mode? (SUB)"); + cg_sub_mask[remap_sub_thing] = 0; + } + remap_sub_thing++; + } + } + + for (int x = 0; x < 8; x++) + { + uint16 mpd; + + mpd = king->MPROGData[((bat_offset & 0x20000) ? 0x8 : 0x0) + x]; + if (((mpd >> 6) & 0x3) == n && !(mpd & 0x100) && (mpd & 0x010) && (bool)(mpd & 0x020) == rotate_mode) + BATFetchCycle = TRUE; + + mpd = king->MPROGData[((bat_sub_offset & 0x20000) ? 0x8 : 0x0) + x]; + if (((mpd >> 6) & 0x3) == n && !(mpd & 0x100) && (mpd & 0x010) && (bool)(mpd & 0x020) == rotate_mode) + BATSubFetchCycle = TRUE; + } + } + + int bat_y = (YOffset >> 3) & bat_bitsize_mask; + uint32 bat_x = (XScroll >> 3) & bat_bitsize_mask; + int ysmall = YOffset & 0x7; + + const uint32 bat_invalid_y_mask = bat_width_invalid ? 0 : 0xFFFFFFFF; + const uint32 bat_invalid_sub_y_mask = bat_sub_width_invalid ? 0 : 0xFFFFFFFF; + + if (rotate_mode) + target += 8; + else + target += 8 - (XScroll & 0x7); + + { + int32 wmul = (1 << bat_width_shift), wmask = ((1 << bat_height_shift) - 1) & bat_invalid_y_mask; + int32 sexy_y_pos = (YOffset & wmask) * wmul; + + int32 wmul_sub = (1 << bat_sub_width_shift), wmask_sub = ((1 << bat_sub_height_shift) - 1) & bat_invalid_sub_y_mask; + int32 sexy_y_sub_pos = (YOffset & wmask_sub) * wmul_sub; + +#define ROTCODE_PRE \ + const int32 bat_width_mask = endless ? (bat_width - 1) : 0xFFFF; \ + const int32 bat_height_mask = endless ? (bat_height - 1) : 0xFFFF; \ + int32 a, b, c, d; \ + int32 raw_x_coord = (int32)sign_11_to_s16(XScroll) - (int16)king->BGAffinCenterX; \ + int32 raw_y_coord = fx_vce.raster_counter + (int32)sign_11_to_s16(YScroll) - 22 - (int16)king->BGAffinCenterY; \ + int32 xaccum; \ + int32 yaccum; \ + \ + a = (int16)king->BGAffinA; \ + b = (int16)king->BGAffinB; \ + c = (int16)king->BGAffinC; \ + d = (int16)king->BGAffinD; \ + \ + xaccum = raw_x_coord * a + raw_y_coord * b; \ + yaccum = raw_y_coord * d + raw_x_coord * c; \ + xaccum += (int16)king->BGAffinCenterX << 8; \ + yaccum += (int16)king->BGAffinCenterY << 8; + +#define ROTCODE_LOOP_PRE \ + for (int x = 0; x < 256; x++) \ + { \ + uint16 new_x = (xaccum >> 8); \ + uint16 new_y = (yaccum >> 8); \ + const uint16 *cgptr; \ + bat_x = (new_x >> 3) & bat_width_mask; \ + bat_y = (new_y >> 3) & bat_height_mask; \ + ysmall = new_y & 0x7; + +#define ROTCODE_LOOP_POST \ + xaccum += a; \ + yaccum += c; \ + } + + if ((bgmode & 0x7) == BGMODE_4 && rotate_mode) + { + ROTCODE_PRE; + + ROTCODE_LOOP_PRE; + uint32 pbn = 0; + if (bgmode & 0x8) + { + uint16 bat = bat_base[(bat_offset + (bat_x + ((bat_y << bat_width_shift) >> 3))) & 0x1FFFF]; + pbn = ((bat >> 12) << 2); + bat &= 0x0FFF; + cgptr = &cg_base[(cg_offset + (bat * 8) + ysmall) & 0x1FFFF]; + } + else + { + cgptr = &cg_base[(cg_offset + bat_x + ((new_y & wmask) * wmul / 8)) & 0x1FFFF]; + } + uint8 ze_cg = (cgptr[0] >> ((7 - (new_x & 7)) << 1)) & 0x03; + + if (endless || (bat_x < bat_width && bat_y < bat_height)) + { + if (ze_cg) + target[x] = palette_ptr[pbn + ze_cg] | layer_or; + } + ROTCODE_LOOP_POST; + } + else if ((bgmode & 0x7) == BGMODE_16 && rotate_mode) + { + ROTCODE_PRE; + + ROTCODE_LOOP_PRE; + uint32 pbn = 0; + + if (bgmode & 0x8) + { + uint16 bat = bat_base[(bat_offset + (bat_x + ((bat_y << bat_width_shift) >> 3))) & 0x1FFFF]; + pbn = ((bat >> 12) << 4); + bat &= 0x0FFF; + cgptr = &cg_base[(cg_offset + (bat * 16) + ysmall * 2) & 0x1FFFF]; + } + else + { + cgptr = &cg_base[(cg_offset + (bat_x * 2) + ((new_y & wmask) * wmul / 4)) & 0x1FFFF]; + } + uint8 ze_cg = (cgptr[(new_x >> 2) & 0x1] >> ((3 - (new_x & 3)) << 2)) & 0x0F; + + if (endless || (bat_x < bat_width && bat_y < bat_height)) + { + if (ze_cg) + target[x] = palette_ptr[pbn + ze_cg] | layer_or; + } + ROTCODE_LOOP_POST; + } + else if ((bgmode & 0x7) == BGMODE_256 && rotate_mode) + { + ROTCODE_PRE; + + ROTCODE_LOOP_PRE; + if (bgmode & 0x8) + { + uint16 bat = bat_base[(bat_offset + (bat_x + ((bat_y << bat_width_shift) >> 3))) & 0x1FFFF]; + cgptr = &cg_base[(cg_offset + (bat * 32) + ysmall * 4) & 0x1FFFF]; + } + else + { + cgptr = &cg_base[(cg_offset + (bat_x * 4) + ((new_y & wmask) * wmul / 2)) & 0x1FFFF]; + } + uint8 ze_cg = cgptr[(new_x >> 1) & 0x3] >> (((new_x & 1) ^ 1) << 3); + + if (endless || (bat_x < bat_width && bat_y < bat_height)) + { + if (ze_cg) + target[x] = palette_ptr[ze_cg] | layer_or; + } + ROTCODE_LOOP_POST; + } + else if ((bgmode & 0x7) == BGMODE_64K && rotate_mode) + { + ROTCODE_PRE; + ROTCODE_LOOP_PRE; + if (bgmode & 0x8) + { + uint16 bat = bat_base[(bat_offset + (bat_x + ((bat_y << bat_width_shift) >> 3))) & 0x1FFFF]; + cgptr = &cg_base[(cg_offset + (bat * 64) + ysmall * 8) & 0x1FFFF]; + } + else + { + cgptr = &cg_base[(cg_offset + (bat_x * 8) + ((new_y & wmask) * wmul)) & 0x1FFFF]; + } + uint16 ze_cg = cgptr[new_x & 0x7]; + + if (endless || (bat_x < bat_width && bat_y < bat_height)) + { + if (ze_cg >> 8) + target[x] = ((ze_cg & 0x00F0) << 8) | ((ze_cg & 0x000F) << 4) | ((ze_cg & 0xFF00) << 8) | layer_or; + } + ROTCODE_LOOP_POST; + } + else + switch (bgmode & 0x7) + { +#define DRAWBG8x1_MAC(cg_needed, blit_suffix, pbn_arg) \ + for (int x = 0; x < 256 + 8; x += 8) \ + { \ + if (bat_x < bat_width && bat_y < bat_height) \ + { \ + uint32 eff_bat_loc = bat_offset; \ + uint16 bat = 0; \ + uint16 pbn MDFN_NOWARN_UNUSED = 0; \ + const uint16 *cgptr[2]; \ + uint16 cg[cg_needed]; \ + \ + if (bgmode & 0x8) \ + eff_bat_loc += bat_x + (((bat_y & bat_invalid_y_mask) << bat_width_shift) >> 3); \ + \ + eff_bat_loc &= 0x1FFFF; \ + \ + if (BATFetchCycle) \ + bat = bat_base[eff_bat_loc]; \ + \ + if (bgmode & 0x08) \ + pbn = bat >> 12; \ + bat &= 0xFFF; \ + \ + cgptr[0] = &cg_base[(cg_offset + (bat_x * cg_needed) + sexy_y_pos) & 0x1FFFF]; \ + cgptr[1] = &cg_base[(cg_offset + (bat * 8 * cg_needed) + ysmall * cg_needed) & 0x1FFFF]; \ + \ + for (int cow = 0; cow < cg_needed; cow++) \ + cg[cow] = cgptr[cg_ofbat[cow]][cg_remap[cow]] & cg_mask[cow]; \ + \ + DRAWBG8x1_##blit_suffix(target + x, cg, palette_ptr + pbn_arg, layer_or); \ + } \ + else if (bat_x < bat_sub_width_test && bat_y < bat_sub_height_test) \ + { \ + uint32 eff_bat_loc = bat_sub_offset; \ + uint16 bat = 0; \ + uint16 pbn MDFN_NOWARN_UNUSED = 0; \ + const uint16 *cgptr[2]; \ + uint16 cg[cg_needed]; \ + \ + if (bgmode & 0x8) \ + eff_bat_loc += (bat_x & bat_sub_width_mask) + (((bat_y & bat_invalid_sub_y_mask & bat_sub_height_mask) << bat_sub_width_shift) >> 3); \ + \ + eff_bat_loc &= 0x1FFFF; \ + \ + if (BATSubFetchCycle) \ + bat = bat_sub_base[eff_bat_loc]; \ + \ + if (bgmode & 0x08) \ + pbn = bat >> 12; \ + bat &= 0xFFF; \ + \ + cgptr[0] = &cg_sub_base[(cg_sub_offset + ((bat_x & bat_sub_width_mask) * cg_needed) + sexy_y_sub_pos) & 0x1FFFF]; \ + cgptr[1] = &cg_sub_base[(cg_sub_offset + (bat * 8 * cg_needed) + ysmall * cg_needed) & 0x1FFFF]; \ + \ + for (int cow = 0; cow < cg_needed; cow++) \ + cg[cow] = cgptr[cg_sub_ofbat[cow]][cg_sub_remap[cow]] & cg_sub_mask[cow]; \ + \ + DRAWBG8x1_##blit_suffix(target + x, cg, palette_ptr + pbn_arg, layer_or); \ + } \ + bat_x = (bat_x + 1) & bat_bitsize_mask; \ + } + + case 0x01: // 4 color, 1/4 byte per pixel :b + sexy_y_pos >>= 3; + sexy_y_sub_pos >>= 3; + + DRAWBG8x1_MAC(1, 4, (pbn << 2)); + break; + + case 0x02: // 16 color, 1/2 byte per pixel + sexy_y_pos >>= 2; + sexy_y_sub_pos >>= 2; + + DRAWBG8x1_MAC(2, 16, (pbn << 4)); + break; + + case 0x03: // 256 color, 1 byte per pixel palettized - OK + sexy_y_pos >>= 1; + sexy_y_sub_pos >>= 1; + + DRAWBG8x1_MAC(4, 256, 0); + break; + + case 0x04: // 64K color, 2 bytes per pixel - OK + DRAWBG8x1_MAC(8, 64K, 0); + break; + + case 0x05: // 16M color, 2 bytes per pixel - OK + DRAWBG8x1_MAC(8, 16M, 0); + break; +#if 0 + case BGMODE_64K_EXTDOT: + break; + + case BGMODE_16M_EXTDOT: + { + uint32 fat_bat = ((YOffset & ((1 << bat_height_shift) - 1)) << bat_width_shift) + (((XScroll & ~ 7) & ((1 << bat_width_shift) - 1)); + } + break; +#endif + } + } +} + +static int16 UVLUT[65536][3]; +static uint8 RGBDeflower[1152]; // 0 is at 384 + +static void RebuildUVLUT() +{ + for (int ur = 0; ur < 256; ur++) + { + for (int vr = 0; vr < 256; vr++) + { + int r, g, b; + int u, v; + + u = ur - 128; + v = vr - 128; + + // FIXME: Use lrint() ? + r = (int)(0 - 0.000039457070707 * u + 1.139827967171717 * v); + g = (int)(0 - 0.394610164141414 * u - 0.580500315656566 * v); + b = (int)(0 + 2.031999684343434 * u - 0.000481376262626 * v); + + UVLUT[vr + ur * 256][0] = r; + UVLUT[vr + ur * 256][1] = g; + UVLUT[vr + ur * 256][2] = b; + } + } + for (int x = 0; x < 1152; x++) + { + if (x < 384) + RGBDeflower[x] = 0; + else if (x > (384 + 255)) + RGBDeflower[x] = 255; + else + RGBDeflower[x] = x - 384; + } +} + +// FIXME +static uint32 INLINE YUV888_TO_RGB888(uint32 yuv) +{ + int32 r, g, b; + uint8 y = yuv >> 16; + + r = y + UVLUT[yuv & 0xFFFF][0]; + g = y + UVLUT[yuv & 0xFFFF][1]; + b = y + UVLUT[yuv & 0xFFFF][2]; + + r = clamp_to_u8(r); + g = clamp_to_u8(g); + b = clamp_to_u8(b); + + return b | g << 8 | r << 16 | 0xff000000; + ; +} + +// FIXME: +//static unsigned int lines_per_frame; //= (fx_vce.picture_mode & 0x1) ? 262 : 263; +static VDC **vdc_chips; + +static EmulateSpecStruct *Ess; + +static int32 *LineWidths; +static int skip; + +void KING_StartFrame(VDC **arg_vdc_chips, EmulateSpecStruct *espec) +{ + MDFN_IEN_PCFX::vdc_chips = arg_vdc_chips; + MDFN_IEN_PCFX::LineWidths = espec->LineWidths; + MDFN_IEN_PCFX::skip = espec->skip; + + //MDFN_DispMessage("P0:%06x P1:%06x; I0: %06x I1: %06x", king->ADPCMPlayAddress[0], king->ADPCMPlayAddress[1], king->ADPCMIntermediateAddress[0] << 6, king->ADPCMIntermediateAddress[1] << 6); + //MDFN_DispMessage("%d %d\n", SCSICD_GetACK(), SCSICD_GetREQ()); + + // For the case of interlaced mode(clear ~0 state) + LineWidths[0] = 0; + + // These 2 should be overwritten in the big loop below. + espec->x = 0; + espec->w = 256; + + espec->y = Setting_SlStart; + espec->h = Setting_SlEnd - espec->y + 1; + + if (fx_vce.frame_interlaced) + { + skip = false; + + espec->InterlaceOn = true; + espec->InterlaceField = fx_vce.odd_field; + espec->y *= 2; + espec->h *= 2; + } + + Ess = espec; +} + +static int rb_type; +// unsigned int width = (fx_vce.picture_mode & 0x08) ? 341 : 256; + +static void DrawActive(void) +{ + rb_type = -1; + + if (fx_vce.raster_counter == king->RAINBOWTransferStartPosition && (king->RAINBOWTransferControl & 1)) + { + king->RAINBOWStartPending = TRUE; + + //printf("Rainbow start pending: line=%d, busycount=%d, blockcount=%d\n", fx_vce.raster_counter, king->RAINBOWBusyCount, king->RAINBOWBlockCount); + + //if(fx_vce.raster_counter == 262) + // puts("MOOO"); + } + + if (fx_vce.raster_counter < 262) + { + if (king->RAINBOWBusyCount) + { + king->RAINBOWBusyCount--; + if (!king->RAINBOWBusyCount) + RAINBOW_SwapBuffers(); + } + + if (!king->RAINBOWBusyCount) + { + bool WantDecode = FALSE; + bool FirstDecode = FALSE; + + if (!king->RAINBOWBlockCount && king->RAINBOWStartPending) + { + //printf("Rainbow start real: %d %d\n", fx_vce.raster_counter, king->RAINBOWTransferBlockCount); + king->RAINBOWBlockCount = king->RAINBOWTransferBlockCount; + if (king->RAINBOWBlockCount) + { + king->RAINBOWKRAMReadPos = king->RAINBOWKRAMA << 1; + FirstDecode = TRUE; + } + } + + if (king->RAINBOWBlockCount) + { + king->RAINBOWBlockCount--; + WantDecode = TRUE; + } + + if (WantDecode) + { + king->RAINBOWBusyCount = 16; + + if (fx_vce.raster_counter == 262) + king->RAINBOWBusyCount++; + + // If we ever change the emulation time range from the current 0 through 262/263, we will need to readjust this + // statement to prevent the previous frame's skip value to mess up the current frame's graphics data, since + // RAINBOW data is delayed by 16 scanlines from when it's decoded(16 + 15 maximum delay). + RAINBOW_DecodeBlock(FirstDecode, skip && fx_vce.raster_counter < 246); + } + } + + rb_type = RAINBOW_FetchRaster(skip ? NULL : rainbow_linebuffer, LAYER_RAINBOW << 28, &vce_rendercache.palette_table_cache[((fx_vce.palette_offset[3] >> 0) & 0xFF) << 1]); + + king->RAINBOWStartPending = FALSE; + } // end if(fx_vce.raster_counter < 262) + + if (fx_vce.raster_counter >= 22 && fx_vce.raster_counter < 262) + { + if (!skip) + { + if (rb_type == 1) // YUV + { + // Only chroma key when we're not in 7.16MHz pixel mode + if (!(fx_vce.picture_mode & 0x08)) + { + const unsigned int ymin = fx_vce.ChromaKeyY & 0xFF; + const unsigned int ymax = fx_vce.ChromaKeyY >> 8; + const unsigned int umin = fx_vce.ChromaKeyU & 0xFF; + const unsigned int umax = fx_vce.ChromaKeyU >> 8; + const unsigned int vmin = fx_vce.ChromaKeyV & 0xFF; + const unsigned int vmax = fx_vce.ChromaKeyV >> 8; + + if ((fx_vce.ChromaKeyY | fx_vce.ChromaKeyU | fx_vce.ChromaKeyV) == 0) + { + //puts("Opt: 0 chroma key"); + for (int x = 0; x < 256; x++) + { + if (!(rainbow_linebuffer[x] & 0xFFFFFF)) + rainbow_linebuffer[x] = 0; + } + } + else if (ymin == ymax && umin == umax && vmin == vmax) + { + const uint32 compare_color = (ymin << 16) | (umin << 8) | (vmin << 0); + + //puts("Opt: Single color chroma key"); + + for (int x = 0; x < 256; x++) + { + if ((rainbow_linebuffer[x] & 0xFFFFFF) == compare_color) + rainbow_linebuffer[x] = 0; + } + } + else if (ymin <= ymax && umin <= umax && vmin <= vmax) + { + const uint32 yv_min_sub = (ymin << 16) | vmin; + const uint32 u_min_sub = umin << 8; + const uint32 yv_max_add = ((0xFF - ymax) << 16) | (0xFF - vmax); + const uint32 u_max_add = (0xFF - umax) << 8; + + for (int x = 0; x < 256; x++) + { + const uint32 pixel = rainbow_linebuffer[x]; + const uint32 yv = pixel & 0xFF00FF; + const uint32 u = pixel & 0x00FF00; + uint32 testie; + + testie = ((yv - yv_min_sub) | (yv + yv_max_add)) & 0xFF00FF00; + testie |= ((u - u_min_sub) | (u + u_max_add)) & 0x00FF00FF; + + if (!testie) + rainbow_linebuffer[x] = 0; + } + } + else + { + //puts("Opt: color keying off\n"); + } + } + } + + /* + 4 = Foremost + 1 = Hindmost + 0 = Hidden + */ + + MDFN_FastArraySet(bg_linebuffer + 8, 0, 256); + + // Only bother to draw the BGs if the microprogram is enabled. + if (king->MPROGControl & 0x1) + { + for (int prio = 1; prio <= 7; prio++) + { + for (int x = 0; x < 4; x++) + { + int thisprio = (king->priority >> (x * 3)) & 0x7; + + if (BGLayerDisable & (1 << x)) + continue; + + if (thisprio == prio) + { + //if(fx_vce.raster_counter == 50) + // CanDrawBG_Fast(x); + + // TODO/FIXME: TEST MORE + if (CanDrawBG_Fast(x)) // && (rand() & 1)) + DrawBG_Fast(bg_linebuffer, x); + else + DrawBG(bg_linebuffer, x, 0); + } + } + } + } + + } // end if(!skip) + } // end if(fx_vce.raster_counter >= 22 && fx_vce.raster_counter < 262) +} + +static INLINE void VDC_PIXELMIX(bool SPRCOMBO_ON, bool BGCOMBO_ON) +{ + static const uint32 vdc_layer_num[2] = {LAYER_VDC_BG << 28, LAYER_VDC_SPR << 28}; + const uint32 vdc_poffset[2] = { + (((uint32)fx_vce.palette_offset[0] >> 0) & 0xFF) << 1, // BG + (((uint32)fx_vce.palette_offset[0] >> 8) & 0xFF) << 1 // SPR + }; + + const int width = fx_vce.dot_clock ? 342 : 256; // 342, not 341, to prevent garbage pixels in high dot clock mode. + + for (int x = 0; x < width; x++) + { + const uint32 zort[2] = {vdc_linebuffers[0][x], vdc_linebuffers[1][x]}; + uint32 tmp_pixel; + + /* SPR combination */ + if (SPRCOMBO_ON && (zort[1] & 0x18F) > 0x180) + tmp_pixel = (zort[1] & 0xF) | ((zort[0] & 0xF) << 4) | 0x100; + /* BG combination */ + else if (BGCOMBO_ON && ((zort[1] ^ 0x100) & 0x18F) > 0x180) + tmp_pixel = (zort[1] & 0xF) | ((zort[0] & 0xF) << 4); + else + tmp_pixel = (zort[1] & 0xF) ? zort[1] : zort[0]; + + vdc_linebuffer[x] = tmp_pixel; + vdc_linebuffer_yuved[x] = 0; + if (tmp_pixel & 0xF) + vdc_linebuffer_yuved[x] = vce_rendercache.palette_table_cache[(tmp_pixel & 0xFF) + vdc_poffset[(tmp_pixel >> 8) & 1]] | vdc_layer_num[(tmp_pixel >> 8) & 1]; + } +} + +static void MixVDC(void) NO_INLINE; +static void MixVDC(void) +{ + // Optimization for when both layers are disabled in the VCE. + if (!vce_rendercache.LayerPriority[LAYER_VDC_BG] && !vce_rendercache.LayerPriority[LAYER_VDC_SPR]) + { + MDFN_FastArraySet(vdc_linebuffer_yuved, 0, 512); + } + else + switch (fx_vce.picture_mode & 0xC0) + { + case 0x00: + VDC_PIXELMIX(0, 0); + break; // None on + case 0x40: + VDC_PIXELMIX(0, 1); + break; // BG combo on + case 0x80: + VDC_PIXELMIX(1, 0); + break; // SPR combo on + case 0xC0: + VDC_PIXELMIX(1, 1); + break; // Both on + } +} + +static void MixLayers(void) +{ + uint32 *pXBuf = Ess->pixels; + + // Now we have to mix everything together... I'm scared, mommy. + // We have, vdc_linebuffer[0] and bg_linebuffer + // Which layer is specified in bits 28-31(check the enum earlier on) + uint32 priority_remap[8]; + uint32 ble_cache[8]; + bool ble_cache_any = FALSE; + + for (int n = 0; n < 8; n++) + { + priority_remap[n] = vce_rendercache.LayerPriority[n]; + //printf("%d: %d\n", n, priority_remap[n]); + } + + // Rainbow layer disabled? + if (rb_type == -1 || RAINBOWLayerDisable) + priority_remap[LAYER_RAINBOW] = 0; + + ble_cache[LAYER_NONE] = 0; + for (int x = 0; x < 4; x++) + ble_cache[LAYER_BG0 + x] = (vce_rendercache.BLE >> (4 + x * 2)) & 0x3; + + ble_cache[LAYER_VDC_BG] = (vce_rendercache.BLE >> 0) & 0x3; + ble_cache[LAYER_VDC_SPR] = (vce_rendercache.BLE >> 2) & 0x3; + ble_cache[LAYER_RAINBOW] = (vce_rendercache.BLE >> 12) & 0x3; + + for (int x = 0; x < 8; x++) + if (ble_cache[x]) + { + ble_cache_any = TRUE; + break; + } + + uint8 *coeff_cache_y_back[3]; + int8 *coeff_cache_u_back[3], *coeff_cache_v_back[3]; + uint8 *coeff_cache_y_fore[3]; + int8 *coeff_cache_u_fore[3], *coeff_cache_v_fore[3]; + + for (int x = 0; x < 3; x++) + { + coeff_cache_y_fore[x] = vce_rendercache.coefficient_mul_table_y[(vce_rendercache.coefficients[x * 2 + 0] >> 8) & 0xF]; + coeff_cache_u_fore[x] = vce_rendercache.coefficient_mul_table_uv[(vce_rendercache.coefficients[x * 2 + 0] >> 4) & 0xF]; + coeff_cache_v_fore[x] = vce_rendercache.coefficient_mul_table_uv[(vce_rendercache.coefficients[x * 2 + 0] >> 0) & 0xF]; + + coeff_cache_y_back[x] = vce_rendercache.coefficient_mul_table_y[(vce_rendercache.coefficients[x * 2 + 1] >> 8) & 0xF]; + coeff_cache_u_back[x] = vce_rendercache.coefficient_mul_table_uv[(vce_rendercache.coefficients[x * 2 + 1] >> 4) & 0xF]; + coeff_cache_v_back[x] = vce_rendercache.coefficient_mul_table_uv[(vce_rendercache.coefficients[x * 2 + 1] >> 0) & 0xF]; + } + + uint32 *target; + uint32 BPC_Cache = (LAYER_NONE << 28); // Backmost pixel color(cache) + + if (fx_vce.frame_interlaced) + target = pXBuf + Ess->pitch32 * ((fx_vce.raster_counter - 22) * 2 + fx_vce.odd_field); + else + target = pXBuf + Ess->pitch32 * (fx_vce.raster_counter - 22); + + // If at least one layer is enabled with the HuC6261, hindmost color is palette[0] + // If no layers are on, this color is black. + // If front cellophane is enabled, this color is forced to black(TODO: Confirm on a real system. Black or from CCR). + // If back cellophane is enabled, this color is forced to the value in CCR + // (back and front conditions are handled closer to the pixel mixing loops down below) + // TODO: Test on a real PC-FX to see if CCR is used or not if back cellophane is enabled even if all layers are disabled in the HuC6261, + // or if it just outputs black. + // TODO: See if enabling front/back cellophane in high dot-clock mode will set the hindmost color, even though the cellophane color mixing + // is disabled in high dot-clock mode. + if (vce_rendercache.picture_mode & 0x7F00) + BPC_Cache |= vce_rendercache.palette_table_cache[0]; + else + BPC_Cache |= 0x008080; + +#define DOCELLO(pixpoo) \ + if ((pixel[pixpoo] >> 28) != LAYER_VDC_SPR || ((vce_rendercache.SPBL >> ((vdc_linebuffer[x] & 0xF0) >> 4)) & 1)) \ + { \ + int which_co = (ble_cache[pixel[pixpoo] >> 28] - 1); \ + uint8 back_y = coeff_cache_y_back[which_co][(zeout >> 16) & 0xFF]; \ + int8 back_u = coeff_cache_u_back[which_co][(zeout >> 8) & 0xFF]; \ + int8 back_v = coeff_cache_v_back[which_co][(zeout >> 0) & 0xFF]; \ + uint8 fore_y = coeff_cache_y_fore[which_co][(pixel[pixpoo] >> 16) & 0xFF]; \ + int8 fore_u = coeff_cache_u_fore[which_co][(pixel[pixpoo] >> 8) & 0xFF]; \ + int8 fore_v = coeff_cache_v_fore[which_co][(pixel[pixpoo] >> 0) & 0xFF]; \ + zeout = (pixel[pixpoo] & 0xFF000000) | ((RGBDeflower + 384)[back_y + fore_y] << 16) | ((RGBDeflower + 384)[back_u + fore_u + 128] << 8) | ((RGBDeflower + 384)[back_v + fore_v + 128] << 0); \ + } \ + else \ + zeout = pixel[pixpoo]; + +#define DOCELLOSPECIALFRONT() \ + { \ + uint8 y = coeff_cache_y_back[0][(zeout >> 16) & 0xFF]; \ + int8 u = coeff_cache_u_back[0][(zeout >> 8) & 0xFF]; \ + int8 v = coeff_cache_v_back[0][(zeout >> 0) & 0xFF]; \ + zeout = (zeout & 0xFF000000) | ((RGBDeflower + 384)[CCR_Y_front + y] << 16) | ((RGBDeflower + 384)[CCR_U_front + u + 128] << 8) | \ + ((RGBDeflower + 384)[CCR_V_front + v + 128] << 0); \ + } + +#define LAYER_MIX_BODY(index_256, index_341) \ + { \ + uint32 pixel[4]; \ + uint32 prio[3]; \ + uint32 zeout = BPC_Cache; \ + prio[0] = priority_remap[vdc_linebuffer_yuved[index_341] >> 28]; \ + prio[1] = priority_remap[(bg_linebuffer + 8)[index_256] >> 28]; \ + prio[2] = priority_remap[rainbow_linebuffer[index_256] >> 28]; \ + pixel[0] = 0; \ + pixel[1] = 0; \ + pixel[2] = 0; \ + { \ + uint8 pi0 = VCEPrioMap[prio[0]][prio[1]][prio[2]][0]; \ + uint8 pi1 = VCEPrioMap[prio[0]][prio[1]][prio[2]][1]; \ + uint8 pi2 = VCEPrioMap[prio[0]][prio[1]][prio[2]][2]; \ + /*assert(pi0 == 3 || !pixel[pi0]);*/ pixel[pi0] = vdc_linebuffer_yuved[index_341]; \ + /*assert(pi1 == 3 || !pixel[pi1]);*/ pixel[pi1] = (bg_linebuffer + 8)[index_256]; \ + /*assert(pi2 == 3 || !pixel[pi2]);*/ pixel[pi2] = rainbow_linebuffer[index_256]; \ + } + +#define LAYER_MIX_FINAL_NOCELLO \ + if (pixel[0]) \ + zeout = pixel[0]; \ + if (pixel[1]) \ + zeout = pixel[1]; \ + if (pixel[2]) \ + zeout = pixel[2]; \ + target[x] = YUV888_TO_xxx(zeout); \ + } + +// For back cellophane, the hindmost pixel is always a valid pixel to mix with, a "layer" in its own right, +// so we don't need to check the current pixel value before mixing. +#define LAYER_MIX_FINAL_BACK_CELLO \ + if (pixel[0]) \ + { \ + if (ble_cache[pixel[0] >> 28]) \ + { \ + DOCELLO(0); \ + } \ + else \ + zeout = pixel[0]; \ + } \ + if (pixel[1]) \ + { \ + if (ble_cache[pixel[1] >> 28]) \ + { \ + DOCELLO(1); \ + } \ + else \ + zeout = pixel[1]; \ + } \ + if (pixel[2]) \ + { \ + if (ble_cache[pixel[2] >> 28]) \ + { \ + DOCELLO(2); \ + } \ + else \ + zeout = pixel[2]; \ + } \ + target[x] = YUV888_TO_xxx(zeout); \ + } + +// ..however, for front and "normal" cellophane, we need to make sure that the +// layer is indeed a real layer(KBG, VDC, RAINBOW) before mixing. +// Note: We need to check the upper 4 bits in determining whether the previous pixel is from a real layer or not, because the default +// hindmost non-layer color in front cellophane and normal cellophane modes is black, and black is represented in YUV as non-zero. We COULD bias/XOR each of U/V +// by 0x80 in the rendering code so that it would work if we just tested for the non-zeroness of the previous pixel, and adjust the YUV->RGB +// to compensate...TODO as a future possible optimization(MAYBE, it would obfuscate things more than they already are). +// +// Also, since the hindmost real layer pixel will never mix with anything behind it, we can leave +// out a few checks for the first possible hindmost real pixel. +// +// Also, the front cellophane effect itself doesn't need to check if the effective pixel output is a real layer (TODO: Confirm on real hardware!) +// +#define LAYER_MIX_FINAL_FRONT_CELLO \ + if (pixel[0]) \ + zeout = pixel[0]; \ + if (pixel[1]) \ + { \ + if (ble_cache[pixel[1] >> 28] && (zeout & (0xF << 28))) \ + { \ + DOCELLO(1); \ + } \ + else \ + zeout = pixel[1]; \ + } \ + if (pixel[2]) \ + { \ + if (ble_cache[pixel[2] >> 28] && (zeout & (0xF << 28))) \ + { \ + DOCELLO(2); \ + } \ + else \ + zeout = pixel[2]; \ + } \ + DOCELLOSPECIALFRONT(); \ + target[x] = YUV888_TO_xxx(zeout); \ + } + +#define LAYER_MIX_FINAL_CELLO \ + if (pixel[0]) \ + zeout = pixel[0]; \ + if (pixel[1]) \ + { \ + if (ble_cache[pixel[1] >> 28] && (zeout & (0xF << 28))) \ + { \ + DOCELLO(1); \ + } \ + else \ + zeout = pixel[1]; \ + } \ + if (pixel[2]) \ + { \ + if (ble_cache[pixel[2] >> 28] && (zeout & (0xF << 28))) \ + { \ + DOCELLO(2); \ + } \ + else \ + zeout = pixel[2]; \ + } \ + target[x] = YUV888_TO_xxx(zeout); \ + } + + if (1) + { +#define YUV888_TO_xxx YUV888_TO_RGB888 +#include "king_mix_body.inc" +#undef YUV888_TO_xxx + } + Ess->w = fx_vce.dot_clock ? HighDotClockWidth : 256; + Ess->x = 0; + + // FIXME + if (fx_vce.frame_interlaced) + LineWidths[(fx_vce.raster_counter - 22) * 2 + fx_vce.odd_field] = Ess->w; + else + LineWidths[fx_vce.raster_counter - 22] = Ess->w; +} + +static INLINE void RunVDCs(const int master_cycles, uint16 *pixels0, uint16 *pixels1) +{ + int32 div_clocks; + + fx_vce.clock_divider += master_cycles; + div_clocks = fx_vce.clock_divider / fx_vce.dot_clock_ratio; + fx_vce.clock_divider -= div_clocks * fx_vce.dot_clock_ratio; + + if (pixels0) + pixels0 += vdc_lb_pos; + + if (pixels1) + pixels1 += vdc_lb_pos; + + if (MDFN_UNLIKELY(((uint64)vdc_lb_pos + div_clocks) > 512)) + { + //puts("Bug"); + pixels0 = pixels1 = NULL; + } + + fx_vce.vdc_event[0] = vdc_chips[0]->Run(div_clocks, pixels0, pixels0 ? false : true); + fx_vce.vdc_event[1] = vdc_chips[1]->Run(div_clocks, pixels1, pixels1 ? false : true); + + vdc_lb_pos += div_clocks; + + // printf("%d\n", vdc_lb_pos); + // if(fx_vce.dot_clock) + // assert(vdc_lb_pos <= 342); + // else + // assert(vdc_lb_pos <= 257); +} + +static void MDFN_FASTCALL KING_RunGfx(int32 clocks) +{ + while (clocks > 0) + { + int32 chunk_clocks = clocks; + + if (chunk_clocks > HPhaseCounter) + chunk_clocks = HPhaseCounter; + + clocks -= chunk_clocks; + HPhaseCounter -= chunk_clocks; + + if (skip) + RunVDCs(chunk_clocks, NULL, NULL); + else if (fx_vce.in_hblank) + { + static uint16 dummybuf[1024]; + RunVDCs(chunk_clocks, dummybuf, dummybuf); + } + else + { + RunVDCs(chunk_clocks, vdc_linebuffers[0], vdc_linebuffers[1]); + } + + assert(HPhaseCounter >= 0); + + while (HPhaseCounter <= 0) + { + HPhase = (HPhase + 1) % HPHASE_COUNT; + switch (HPhase) + { + case HPHASE_ACTIVE: + vdc_lb_pos = 0; + fx_vce.in_hblank = false; + DoHBlankVCECaching(); + DrawActive(); + HPhaseCounter += 1024; + break; + + case HPHASE_HBLANK_PART1: + if (!skip) + { + if (fx_vce.raster_counter >= 22 && fx_vce.raster_counter < 262) + { + MixVDC(); + MixLayers(); + } + } + fx_vce.in_hblank = true; + fx_vce.in_vdc_hsync = true; + + for (int chip = 0; chip < 2; chip++) + vdc_chips[chip]->HSync(true); + + HPhaseCounter += 48; + break; + + case HPHASE_HBLANK_PART3: + fx_vce.raster_counter = (fx_vce.raster_counter + 1) % ((fx_vce.picture_mode & 0x1) ? 262 : 263); //lines_per_frame; + + if (fx_vce.raster_counter == 0) + for (int chip = 0; chip < 2; chip++) + vdc_chips[chip]->VSync(true); + + if (fx_vce.raster_counter == 3) + for (int chip = 0; chip < 2; chip++) + vdc_chips[chip]->VSync(false); + + if (!fx_vce.raster_counter) + { + bool previous_interlaced = fx_vce.frame_interlaced; + + fx_vce.frame_interlaced = fx_vce.picture_mode & 2; + + if (fx_vce.frame_interlaced && previous_interlaced) + fx_vce.odd_field ^= 1; + + if (!fx_vce.frame_interlaced) + fx_vce.odd_field = 0; + + PCFX_V810.Exit(); + } + + if (fx_vce.raster_counter == king->RasterIRQLine && (king->RAINBOWTransferControl & 0x2)) + { + //printf("Wovely: %d, %d, %d\n", fx_vce.raster_counter, king->RAINBOWRasterCounter, king->RAINBOWTransferControl); + + //if(fx_vce.raster_counter == 262) + //{ + // printf("Rainbow raster IRQ on line 262?\n"); + //} + //else + { + king->RasterIRQPending = TRUE; + RedoKINGIRQCheck(); + } + } + + // This -18(and +18) may or may not be correct in regards to how a real PC-FX adjusts the VDC layer horizontal position + // versus the KING and RAINBOW layers. + + if (fx_vce.dot_clock) + HPhaseCounter += 173 - 18; + else + HPhaseCounter += 165; + break; + + case HPHASE_HBLANK_PART4: + fx_vce.in_vdc_hsync = false; + for (int chip = 0; chip < 2; chip++) + vdc_chips[chip]->HSync(false); + + if (fx_vce.dot_clock) + HPhaseCounter += 120 + 18; + else + HPhaseCounter += 128; + break; + + } // end: switch(HPhase) + } // end: while(HPhaseCounter <= 0) + } // end: while(clocks > 0) +} // end KING_RunGfx() + +void KING_SetPixelFormat() +{ + RebuildUVLUT(); +} + +void KING_SetLayerEnableMask(uint64 mask) +{ + uint64 ms = mask; + // "BG0\0BG1\0BG2\0BG3\0VDC-A BG\0VDC-A SPR\0VDC-B BG\0VDC-B SPR\0RAINBOW\0", + + BGLayerDisable = (~ms) & 0xF; + ms >>= 4; + + for (unsigned chip = 0; chip < 2; chip++) + { + fx_vdc_chips[chip]->SetLayerEnableMask(ms & 0x3); + ms >>= 2; + } + + RAINBOWLayerDisable = (~ms) & 0x1; + ms >>= 1; + +#if 0 + if(which < 4) + { + BGLayerDisable ^= 1 << which; + return( !((BGLayerDisable >> which) & 1)); + } + else if(which == 4 || which == 5) + { + return(fx_vdc_chips[0]->ToggleLayer(which - 4)); + } + else if(which == 6 || which == 7) + { + return(fx_vdc_chips[1]->ToggleLayer(which - 6)); + } + else if(which == 8) + { + RAINBOWLayerDisable = !RAINBOWLayerDisable; + return(!RAINBOWLayerDisable); + } + else + return(0); +#endif +} + +#ifdef WANT_DEBUGGER +void KING_SetRegister(const unsigned int id, uint32 value) +{ + if (id == KING_GSREG_AR) + king->AR = value & 0x7F; + else if (id == KING_GSREG_MPROGADDR) + king->MPROGAddress = value & 0xF; + else if (id == KING_GSREG_MPROGCTRL) + king->MPROGControl = value & 0x1; + else if (id == KING_GSREG_MPROG0 || id == KING_GSREG_MPROG1 || id == KING_GSREG_MPROG2 || id == KING_GSREG_MPROG3 || id == KING_GSREG_MPROG4 || id == KING_GSREG_MPROG5 || id == KING_GSREG_MPROG6 || id == KING_GSREG_MPROG7 || + id == KING_GSREG_MPROG8 || id == KING_GSREG_MPROG9 || id == KING_GSREG_MPROGA || id == KING_GSREG_MPROGB || id == KING_GSREG_MPROGC || id == KING_GSREG_MPROGD || id == KING_GSREG_MPROGE || id == KING_GSREG_MPROGF) + { + king->MPROGData[id - KING_GSREG_MPROG0] = value; + } + else if (id == KING_GSREG_PAGESET) + { + king->PageSetting = value; + RecalcKRAMPagePtrs(); + } + else if (id == KING_GSREG_BGMODE) + { + king->bgmode = value; + } + else if (id == KING_GSREG_BGPRIO) + { + king->priority = value; + } + else if (id == KING_GSREG_BGSCRM) + { + king->BGScrollMode = value; + } + else if (id == KING_GSREG_BGSIZ0) + king->BGSize[0] = value; + else if (id == KING_GSREG_BGSIZ1) + king->BGSize[1] = value & 0x00FF; + else if (id == KING_GSREG_BGSIZ2) + king->BGSize[2] = value & 0x00FF; + else if (id == KING_GSREG_BGSIZ3) + king->BGSize[3] = value & 0x00FF; + else if (id == KING_GSREG_BGXSC0) + king->BGXScroll[0] = value & 0x7FF; + else if (id == KING_GSREG_BGXSC1) + king->BGXScroll[1] = value & 0x3FF; + else if (id == KING_GSREG_BGXSC2) + king->BGXScroll[2] = value & 0x3FF; + else if (id == KING_GSREG_BGXSC3) + king->BGXScroll[3] = value & 0x3FF; + else if (id == KING_GSREG_BGYSC0) + king->BGYScroll[0] = value & 0x7FF; + else if (id == KING_GSREG_BGYSC1) + king->BGYScroll[1] = value & 0x3FF; + else if (id == KING_GSREG_BGYSC2) + king->BGYScroll[2] = value & 0x3FF; + else if (id == KING_GSREG_BGYSC3) + king->BGYScroll[3] = value & 0x3FF; + else if (id == KING_GSREG_AFFINA) + king->BGAffinA = value; + else if (id == KING_GSREG_AFFINB) + king->BGAffinB = value; + else if (id == KING_GSREG_AFFINC) + king->BGAffinC = value; + else if (id == KING_GSREG_AFFIND) + king->BGAffinD = value; + else if (id == KING_GSREG_AFFINX) + king->BGAffinCenterX = value; + else if (id == KING_GSREG_AFFINY) + king->BGAffinCenterY = value; + else if (id == KING_GSREG_BGBATS || id == KING_GSREG_BGBAT0 || id == KING_GSREG_BGBAT1 || id == KING_GSREG_BGBAT2 || id == KING_GSREG_BGBAT3) + { + if (id == KING_GSREG_BGBATS) + king->BG0SubBATAddr = value; + else + king->BGBATAddr[id - KING_GSREG_BGBAT0] = value; + } + else if (id == KING_GSREG_BGCGS || id == KING_GSREG_BGCG0 || id == KING_GSREG_BGCG1 || id == KING_GSREG_BGCG2 || id == KING_GSREG_BGCG3) + { + if (id == KING_GSREG_BGCGS) + king->BG0SubCGAddr = value; + else + king->BGCGAddr[id - KING_GSREG_BGCG0] = value; + } + else if (id == KING_GSREG_RTCTRL) + king->RAINBOWTransferControl = value & 0x3; + else if (id == KING_GSREG_RKRAMA) + king->RAINBOWKRAMA = value & 0x3FFFF; + else if (id == KING_GSREG_RSTART) + king->RAINBOWTransferStartPosition = value & 0x1FF; + else if (id == KING_GSREG_RCOUNT) + king->RAINBOWTransferBlockCount = value & 0x1F; + else if (id == KING_GSREG_RIRQLINE) + king->RasterIRQLine = value; + else if (id == KING_GSREG_KRAMRA) + { + king->KRAMRA = value; + } + else if (id == KING_GSREG_KRAMWA) + { + king->KRAMWA = value; + } + else if (id == KING_GSREG_DMATA) + king->DMATransferAddr = value; + else if (id == KING_GSREG_DMATS) + king->DMATransferSize = value; + else if (id == KING_GSREG_DMASTT) + king->DMAStatus = value; + else if (id == KING_GSREG_ADPCMCTRL) + { + king->ADPCMControl = value; + SoundBox_SetKINGADPCMControl(king->ADPCMControl); + } + else if (id == KING_GSREG_ADPCMBM0 || id == KING_GSREG_ADPCMBM1) + { + king->ADPCMBufferMode[id - KING_GSREG_ADPCMBM0] = value; + } + else if (id == KING_GSREG_ADPCMPA0 || id == KING_GSREG_ADPCMPA1) + { + king->ADPCMPlayAddress[id - KING_GSREG_ADPCMPA0] = value; + } + else if (id == KING_GSREG_ADPCMSA0 || id == KING_GSREG_ADPCMSA1) + { + king->ADPCMSAL[id - KING_GSREG_ADPCMSA0] = value; + } + else if (id == KING_GSREG_ADPCMIA0 || id == KING_GSREG_ADPCMIA1) + { + king->ADPCMIntermediateAddress[id - KING_GSREG_ADPCMIA0] = value; + } + else if (id == KING_GSREG_ADPCMEA0 || id == KING_GSREG_ADPCMEA1) + { + king->ADPCMEndAddress[id - KING_GSREG_ADPCMEA0] = value; + } + else if (id == KING_GSREG_ADPCMStat) + { + } + else if (id == KING_GSREG_Reg01) + { + king->Reg01 = value; + } + else if (id == KING_GSREG_Reg02) + { + king->Reg02 = value; + } + else if (id == KING_GSREG_Reg03) + { + king->Reg03 = value; + } + else if (id == KING_GSREG_SUBCC) + { + king->SubChannelControl = value & 0x3; + } +} + +uint32 KING_GetRegister(const unsigned int id, char *special, const uint32 special_len) +{ + uint32 value = 0xDEADBEEF; + + if (id == KING_GSREG_AR) + { + value = king->AR; + } + else if (id == KING_GSREG_MPROGADDR) + value = king->MPROGAddress; + else if (id == KING_GSREG_MPROGCTRL) + { + value = king->MPROGControl; + } + else if (id == KING_GSREG_MPROG0 || id == KING_GSREG_MPROG1 || id == KING_GSREG_MPROG2 || id == KING_GSREG_MPROG3 || id == KING_GSREG_MPROG4 || id == KING_GSREG_MPROG5 || id == KING_GSREG_MPROG6 || id == KING_GSREG_MPROG7 || + id == KING_GSREG_MPROG8 || id == KING_GSREG_MPROG9 || id == KING_GSREG_MPROGA || id == KING_GSREG_MPROGB || id == KING_GSREG_MPROGC || id == KING_GSREG_MPROGD || id == KING_GSREG_MPROGE || id == KING_GSREG_MPROGF) + { + value = king->MPROGData[id - KING_GSREG_MPROG0]; + + if (special) + { + static const char *atypes[4] = {"CG", "CG of BAT", "BAT", "???"}; + + trio_snprintf(special, special_len, "Offset: %d, Access Type: %s, Rotation: %d, BG Number: %d, NOP: %d", + value & 0x7, atypes[(value >> 3) & 0x3], (int)(bool)(value & 0x20), + (value >> 6) & 0x3, (int)(bool)(value & 0x100)); + } + } + else if (id == KING_GSREG_PAGESET) + { + value = king->PageSetting; + + if (special) + { + trio_snprintf(special, special_len, "SCSI: %d, BG: %d, ADPCM: %d, RAINBOW: %d", (int)(bool)(value & 0x1), (int)(bool)(value & 0x10), (int)(bool)(value & 0x100), (int)(bool)(value & 0x1000)); + } + } + else if (id == KING_GSREG_BGMODE) + { + value = king->bgmode; + if (special) + { + static const char *bgmodes[16] = {"Disabled", "4-color", "16-color", "256-color", "64K-color", "16M-color", "Undefined", "Undefined", + "Disabled", "4-color w/BAT", "16-color w/BAT", "256-color w/BAT", "64K-color w/BAT", "16M-color w/BAT", "Undefined", "Undefined"}; + trio_snprintf(special, special_len, "BG0: %2d(%s), BG1: %2d(%s), BG2: %2d(%s), BG3: %2d(%s)", value & 0xF, bgmodes[value & 0xF], (value >> 4) & 0xF, bgmodes[(value >> 4) & 0xF], (value >> 8) & 0xF, bgmodes[(value >> 8) & 0xf], (value >> 12) & 0xF, bgmodes[(value >> 12) & 0xf]); + } + } + else if (id == KING_GSREG_BGPRIO) + { + value = king->priority; + if (special) + { + trio_snprintf(special, special_len, "Affine enable: %s - BG0: %2d, BG1: %2d, BG2: %2d, BG3: %2d", (value & (1 << 12)) ? "Yes" : "No", value & 0x7, (value >> 3) & 0x7, (value >> 6) & 0x7, (value >> 9) & 0x7); + } + } + else if (id == KING_GSREG_BGSCRM) + { + value = king->BGScrollMode; + if (special) + { + trio_snprintf(special, special_len, "BG0: %s, BG1: %s, BG2: %s, BG3: %s", (value & 1) ? "Endless" : "Non-endless", (value & 2) ? "Endless" : "Non-endless", + (value & 4) ? "Endless" : "Non-endless", (value & 8) ? "Endless" : "Non-endless"); + } + } + else if (id == KING_GSREG_BGSIZ0 || id == KING_GSREG_BGSIZ1 || id == KING_GSREG_BGSIZ2 || id == KING_GSREG_BGSIZ3) + { + value = king->BGSize[id - KING_GSREG_BGSIZ0]; + } + else if (id == KING_GSREG_BGXSC0) + value = king->BGXScroll[0]; + else if (id == KING_GSREG_BGXSC1) + value = king->BGXScroll[1]; + else if (id == KING_GSREG_BGXSC2) + value = king->BGXScroll[2]; + else if (id == KING_GSREG_BGXSC3) + value = king->BGXScroll[3]; + + else if (id == KING_GSREG_BGYSC0) + value = king->BGYScroll[0]; + else if (id == KING_GSREG_BGYSC1) + value = king->BGYScroll[1]; + else if (id == KING_GSREG_BGYSC2) + value = king->BGYScroll[2]; + else if (id == KING_GSREG_BGYSC3) + value = king->BGYScroll[3]; + else if (id == KING_GSREG_AFFINA || id == KING_GSREG_AFFINB || id == KING_GSREG_AFFINC || id == KING_GSREG_AFFIND) + { + const uint16 *coeffs[4] = {&king->BGAffinA, &king->BGAffinB, &king->BGAffinC, &king->BGAffinD}; + value = *coeffs[id - KING_GSREG_AFFINA]; + if (special) + { + trio_snprintf(special, special_len, "%f", (double)(int16)value / 256); + } + } + else if (id == KING_GSREG_AFFINX) + value = king->BGAffinCenterX; + else if (id == KING_GSREG_AFFINY) + value = king->BGAffinCenterY; + else if (id == KING_GSREG_BGBATS || id == KING_GSREG_BGBAT0 || id == KING_GSREG_BGBAT1 || id == KING_GSREG_BGBAT2 || id == KING_GSREG_BGBAT3) + { + if (id == KING_GSREG_BGBATS) + value = king->BG0SubBATAddr; + else + value = king->BGBATAddr[id - KING_GSREG_BGBAT0]; + + if (special) + { + trio_snprintf(special, special_len, "0x%04x * 1024 = 0x%05x", value, (value * 1024) & 0x3FFFF); + } + } + else if (id == KING_GSREG_BGCGS || id == KING_GSREG_BGCG0 || id == KING_GSREG_BGCG1 || id == KING_GSREG_BGCG2 || id == KING_GSREG_BGCG3) + { + if (id == KING_GSREG_BGCGS) + value = king->BG0SubCGAddr; + else + value = king->BGCGAddr[id - KING_GSREG_BGCG0]; + if (special) + { + trio_snprintf(special, special_len, "0x%04x * 1024 = 0x%05x", value, (value * 1024) & 0x3FFFF); + } + } + else if (id == KING_GSREG_RTCTRL) + { + value = king->RAINBOWTransferControl; + if (special) + { + trio_snprintf(special, special_len, "Raster Interrupt: %s, Rainbow Transfer: %s", (value & 2) ? "On" : "Off", (value & 1) ? "On" : "Off"); + } + } + else if (id == KING_GSREG_RKRAMA) + value = king->RAINBOWKRAMA; + else if (id == KING_GSREG_RSTART) + value = king->RAINBOWTransferStartPosition; + else if (id == KING_GSREG_RCOUNT) + value = king->RAINBOWTransferBlockCount; + else if (id == KING_GSREG_RIRQLINE) + value = king->RasterIRQLine; + else if (id == KING_GSREG_KRAMRA) + { + value = king->KRAMRA; + } + else if (id == KING_GSREG_KRAMWA) + { + value = king->KRAMWA; + } + else if (id == KING_GSREG_DMATA) + value = king->DMATransferAddr; + else if (id == KING_GSREG_DMATS) + value = king->DMATransferSize; + else if (id == KING_GSREG_DMASTT) + value = king->DMAStatus; + else if (id == KING_GSREG_ADPCMCTRL) + { + value = king->ADPCMControl; + } + else if (id == KING_GSREG_ADPCMBM0 || id == KING_GSREG_ADPCMBM1) + { + value = king->ADPCMBufferMode[id - KING_GSREG_ADPCMBM0]; + } + else if (id == KING_GSREG_ADPCMPA0 || id == KING_GSREG_ADPCMPA1) + { + value = king->ADPCMPlayAddress[id - KING_GSREG_ADPCMPA0]; + } + else if (id == KING_GSREG_ADPCMSA0 || id == KING_GSREG_ADPCMSA1) + { + value = king->ADPCMSAL[id - KING_GSREG_ADPCMSA0]; + } + else if (id == KING_GSREG_ADPCMIA0 || id == KING_GSREG_ADPCMIA1) + { + value = king->ADPCMIntermediateAddress[id - KING_GSREG_ADPCMIA0]; + + if (special) + { + trio_snprintf(special, special_len, "0x%03x * 64 = 0x%08x", value, value << 6); + } + } + else if (id == KING_GSREG_ADPCMEA0 || id == KING_GSREG_ADPCMEA1) + { + value = king->ADPCMEndAddress[id - KING_GSREG_ADPCMEA0]; + } + else if (id == KING_GSREG_ADPCMStat) + { + value = king->ADPCMStatus[0] | (king->ADPCMStatus[1] << 2); + if (special) + { + trio_snprintf(special, special_len, "Ch0 End: %d, Ch0 Intermediate: %d, Ch1 End: %d, Ch1 Intermediate: %d", (int)(bool)(value & 0x1), + (int)(bool)(value & 0x2), + (int)(bool)(value & 0x4), + (int)(bool)(value & 0x8)); + } + } + else if (id == KING_GSREG_Reg01) + { + value = king->Reg01; + if (special) + { + trio_snprintf(special, special_len, "BSY: %d, ATN: %d, SEL: %d, ACK: %d, RST: %d", (int)(bool)(value & 1), (int)(bool)(value & 2), (int)(bool)(value & 4), + (int)(bool)(value & 0x10), (int)(bool)(value & 0x80)); + } + } + else if (id == KING_GSREG_Reg02) + { + value = king->Reg02; + } + else if (id == KING_GSREG_Reg03) + { + value = king->Reg03; + if (special) + { + + trio_snprintf(special, special_len, "I/O: %d, C/D: %d, MSG: %d", (int)(bool)(value & 1), (int)(bool)(value & 2), (int)(bool)(value & 4)); + } + } + else if (id == KING_GSREG_SUBCC) + { + value = king->SubChannelControl; + if (special) + { + + trio_snprintf(special, special_len, "Subchannel reading: %s, Subchannel read IRQ: %s", (value & 0x1) ? "Enabled" : "Disabled", (value & 0x2) ? "On" : "Off"); + } + } + else if (id == KING_GSREG_DB) + value = SCSICD_GetDB(); + else if (id == KING_GSREG_BSY) + value = SCSICD_GetBSY(); + else if (id == KING_GSREG_REQ) + value = SCSICD_GetREQ(); + else if (id == KING_GSREG_ACK) + value = SCSICD_GetACK(); + else if (id == KING_GSREG_MSG) + value = SCSICD_GetMSG(); + else if (id == KING_GSREG_CD) + value = SCSICD_GetCD(); + else if (id == KING_GSREG_IO) + value = SCSICD_GetIO(); + else if (id == KING_GSREG_SEL) + value = SCSICD_GetSEL(); + + return (value); +} + +void FXVCE_SetRegister(const unsigned int id, uint32 value) +{ + if (id == FXVCE_GSREG_PRIO0) + { + fx_vce.priority[0] = value & 0x0777; + } + else if (id == FXVCE_GSREG_PRIO1) + { + fx_vce.priority[1] = value & 0x7777; + } + else if (id == FXVCE_GSREG_PICMODE) + { + fx_vce.picture_mode = value; + } + else if (id == FXVCE_GSREG_PALRWOF) + { + fx_vce.palette_rw_offset = value; + fx_vce.palette_rw_offset &= 0x1FF; + } + else if (id == FXVCE_GSREG_PALRWLA) + fx_vce.palette_rw_latch = value; + else if (id == FXVCE_GSREG_PALOFS0) + fx_vce.palette_offset[0] = value; + else if (id == FXVCE_GSREG_PALOFS1) + fx_vce.palette_offset[1] = value; + else if (id == FXVCE_GSREG_PALOFS2) + fx_vce.palette_offset[2] = value; + else if (id == FXVCE_GSREG_PALOFS3) + { + fx_vce.palette_offset[3] = value; + fx_vce.palette_offset[3] &= 0x00FF; + } + else if (id == FXVCE_GSREG_CKeyY) + fx_vce.ChromaKeyY = value; + else if (id == FXVCE_GSREG_CKeyU) + fx_vce.ChromaKeyU = value; + else if (id == FXVCE_GSREG_CKeyV) + fx_vce.ChromaKeyV = value; + else if (id == FXVCE_GSREG_CCR) + fx_vce.CCR = value; + else if (id == FXVCE_GSREG_BLE) + fx_vce.BLE = value; + else if (id == FXVCE_GSREG_SPBL) + fx_vce.SPBL = value; + else if (id == FXVCE_GSREG_COEFF0 || id == FXVCE_GSREG_COEFF1 || id == FXVCE_GSREG_COEFF2 || id == FXVCE_GSREG_COEFF3 || id == FXVCE_GSREG_COEFF4 || id == FXVCE_GSREG_COEFF5) + fx_vce.coefficients[id - FXVCE_GSREG_COEFF0] = value & 0xFFF; +} + +uint32 FXVCE_GetRegister(const unsigned int id, char *special, const uint32 special_len) +{ + uint32 value = 0xDEADBEEF; + + if (id == FXVCE_GSREG_PRIO0) + { + value = fx_vce.priority[0]; + if (special) + { + trio_snprintf(special, special_len, "VDC BG: %2d, VDC SPR: %2d, RAINBOW: %2d", value & 0xF, (value >> 4) & 0xF, (value >> 8) & 0xF); + } + } + else if (id == FXVCE_GSREG_PRIO1) + { + value = fx_vce.priority[1]; + if (special) + { + trio_snprintf(special, special_len, "BG0: %2d, BG1: %2d, BG2: %2d, BG3: %2d", value & 0xF, (value >> 4) & 0xF, (value >> 8) & 0xF, (value >> 12) & 0xF); + } + } + else if (id == FXVCE_GSREG_PICMODE) + { + value = fx_vce.picture_mode; + if (special) + { + static const char *DCCModes[4] = + { + "263 lines/frame", "262 lines/frame", "Interlaced", "Interlaced+1/2 dot shift"}; + + trio_snprintf(special, special_len, "BG0: %s, BG1: %s, BG2: %s, BG3: %s, VDC BG: %s%s, VDC SPR: %s%s, RAINBOW: %s, VDC Clk: %sMHz, %s", + (value & (1 << 10)) ? "On" : "Off", (value & (1 << 11)) ? "On" : "Off", + (value & (1 << 12)) ? "On" : "Off", (value & (1 << 13)) ? "On" : "Off", + (value & 0x0100) ? "On" : "Off", (value & 0x0040) ? "+merge mode" : "", (value & 0x0200) ? "On" : "Off", (value & 0x0080) ? "+merge mode" : "", + (value & 0x4000) ? "On" : "Off", (value & 0x0008) ? "7.16" : "5.37", DCCModes[value & 0x3]); + } + } + else if (id == FXVCE_GSREG_Line) + value = fx_vce.raster_counter; + else if (id == FXVCE_GSREG_PALRWOF) + value = fx_vce.palette_rw_offset; + else if (id == FXVCE_GSREG_PALRWLA) + value = fx_vce.palette_rw_latch; + else if (id == FXVCE_GSREG_PALOFS0) + value = fx_vce.palette_offset[0]; + else if (id == FXVCE_GSREG_PALOFS1) + value = fx_vce.palette_offset[1]; + else if (id == FXVCE_GSREG_PALOFS2) + value = fx_vce.palette_offset[2]; + else if (id == FXVCE_GSREG_PALOFS3) + value = fx_vce.palette_offset[3]; + else if (id == FXVCE_GSREG_CKeyY) + value = fx_vce.ChromaKeyY; + else if (id == FXVCE_GSREG_CKeyU) + value = fx_vce.ChromaKeyU; + else if (id == FXVCE_GSREG_CKeyV) + value = fx_vce.ChromaKeyV; + + else if (id == FXVCE_GSREG_CCR) + value = fx_vce.CCR; + else if (id == FXVCE_GSREG_BLE) + { + value = fx_vce.BLE; + if (special) + { + trio_snprintf(special, special_len, "%s(%s), Rainbow: %d, BG3: %d, BG2: %d, BG1: %d, BG0: %d, VDC SP: %d, VDC BG: %d", (value & 0x8000) ? "Front" : "Back", (value & 0x4000) ? "On" : "Off", (value >> 12) & 0x3, + (value >> 10) & 3, (value >> 8) & 3, (value >> 6) & 3, (value >> 4) & 3, (value >> 2) & 3, value & 3); + } + } + else if (id == FXVCE_GSREG_SPBL) + value = fx_vce.SPBL; + else if (id == FXVCE_GSREG_COEFF0 || id == FXVCE_GSREG_COEFF1 || id == FXVCE_GSREG_COEFF2 || id == FXVCE_GSREG_COEFF3 || id == FXVCE_GSREG_COEFF4 || id == FXVCE_GSREG_COEFF5) + { + value = fx_vce.coefficients[id - FXVCE_GSREG_COEFF0]; + + if (special) + { + trio_snprintf(special, special_len, "Y: %1d, U: %1d, V: %1d", (value >> 8) & 0xF, (value >> 4) & 0xf, value & 0xf); + } + } + + return (value); +} + +#endif +} diff --git a/waterbox/pcfx/king.h b/waterbox/pcfx/king.h new file mode 100644 index 0000000000..3e5df4edde --- /dev/null +++ b/waterbox/pcfx/king.h @@ -0,0 +1,212 @@ +/******************************************************************************/ +/* Mednafen NEC PC-FX Emulation Module */ +/******************************************************************************/ +/* king.h: +** Copyright (C) 2006-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef __PCFX_KING_H +#define __PCFX_KING_H + +namespace MDFN_IEN_PCFX +{ + +// +// Be sure to keep the numbered/lettered *_GSREG_* registers(MPROG0*, AFFIN*, etc.) here in contiguous sequential order(since we do stuff like "- KING_GSREG_MPROG0" +// in king.cpp. +// + +void KING_StartFrame(VDC **, EmulateSpecStruct *espec); +void KING_SetPixelFormat(); +uint16 FXVCE_Read16(uint32 A); +void FXVCE_Write16(uint32 A, uint16 V); + +#ifdef WANT_DEBUGGER +enum +{ + FXVCE_GSREG_Line = 0, + + FXVCE_GSREG_PRIO0, + FXVCE_GSREG_PRIO1, + + FXVCE_GSREG_PICMODE, + FXVCE_GSREG_PALRWOF, + FXVCE_GSREG_PALRWLA, + + FXVCE_GSREG_PALOFS0, + FXVCE_GSREG_PALOFS1, + FXVCE_GSREG_PALOFS2, + FXVCE_GSREG_PALOFS3, + + FXVCE_GSREG_CCR, + FXVCE_GSREG_BLE, + FXVCE_GSREG_SPBL, + + FXVCE_GSREG_COEFF0, + FXVCE_GSREG_COEFF1, + FXVCE_GSREG_COEFF2, + FXVCE_GSREG_COEFF3, + FXVCE_GSREG_COEFF4, + FXVCE_GSREG_COEFF5, + + FXVCE_GSREG_CKeyY, + FXVCE_GSREG_CKeyU, + FXVCE_GSREG_CKeyV +}; + +uint32 FXVCE_GetRegister(const unsigned int id, char* special, const uint32 special_len); +void FXVCE_SetRegister(const unsigned int id, uint32 value); +#endif + +uint8 KING_Read8(const v810_timestamp_t timestamp, uint32 A); +uint16 KING_Read16(const v810_timestamp_t timestamp, uint32 A); + +void KING_Write8(const v810_timestamp_t timestamp, uint32 A, uint8 V); +void KING_Write16(const v810_timestamp_t timestamp, uint32 A, uint16 V); +void KING_Init(void) MDFN_COLD; +void KING_Reset(const v810_timestamp_t timestamp) MDFN_COLD; + +uint16 KING_GetADPCMHalfWord(int ch); + +uint8 KING_MemPeek(uint32 A); + +uint8 KING_RB_Fetch(); + +void KING_SetLayerEnableMask(uint64 mask); + +#ifdef WANT_DEBUGGER +enum +{ + KING_GSREG_AR = 0, + KING_GSREG_MPROGADDR, + KING_GSREG_MPROGCTRL, + + KING_GSREG_PAGESET, + KING_GSREG_RTCTRL, + KING_GSREG_RKRAMA, + KING_GSREG_RSTART, + KING_GSREG_RCOUNT, + KING_GSREG_RIRQLINE, + KING_GSREG_KRAMWA, + KING_GSREG_KRAMRA, + KING_GSREG_DMATA, + KING_GSREG_DMATS, + KING_GSREG_DMASTT, + KING_GSREG_ADPCMCTRL, + + KING_GSREG_ADPCMBM0, + KING_GSREG_ADPCMBM1, + + KING_GSREG_ADPCMPA0, + KING_GSREG_ADPCMPA1, + + KING_GSREG_ADPCMSA0, + KING_GSREG_ADPCMSA1, + + KING_GSREG_ADPCMIA0, + KING_GSREG_ADPCMIA1, + + KING_GSREG_ADPCMEA0, + KING_GSREG_ADPCMEA1, + + KING_GSREG_ADPCMStat, + KING_GSREG_Reg01, + KING_GSREG_Reg02, + KING_GSREG_Reg03, + KING_GSREG_SUBCC, + KING_GSREG_DB, + KING_GSREG_BSY, + KING_GSREG_REQ, + KING_GSREG_ACK, + KING_GSREG_MSG, + KING_GSREG_IO, + KING_GSREG_CD, + KING_GSREG_SEL, + + KING_GSREG_BGMODE, + KING_GSREG_BGPRIO, + KING_GSREG_BGSCRM, + + KING_GSREG_BGSIZ0, + KING_GSREG_BGSIZ1, + KING_GSREG_BGSIZ2, + KING_GSREG_BGSIZ3, + + KING_GSREG_BGXSC0, + KING_GSREG_BGXSC1, + KING_GSREG_BGXSC2, + KING_GSREG_BGXSC3, + + KING_GSREG_BGYSC0, + KING_GSREG_BGYSC1, + KING_GSREG_BGYSC2, + KING_GSREG_BGYSC3, + + KING_GSREG_BGBATS, + KING_GSREG_BGBAT0, + KING_GSREG_BGBAT1, + KING_GSREG_BGBAT2, + KING_GSREG_BGBAT3, + + KING_GSREG_BGCGS, + KING_GSREG_BGCG0, + KING_GSREG_BGCG1, + KING_GSREG_BGCG2, + KING_GSREG_BGCG3, + + KING_GSREG_AFFINA, + KING_GSREG_AFFINB, + KING_GSREG_AFFINC, + KING_GSREG_AFFIND, + + KING_GSREG_AFFINX, + KING_GSREG_AFFINY, + + KING_GSREG_MPROG0, + KING_GSREG_MPROG1, + KING_GSREG_MPROG2, + KING_GSREG_MPROG3, + KING_GSREG_MPROG4, + KING_GSREG_MPROG5, + KING_GSREG_MPROG6, + KING_GSREG_MPROG7, + KING_GSREG_MPROG8, + KING_GSREG_MPROG9, + KING_GSREG_MPROGA, + KING_GSREG_MPROGB, + KING_GSREG_MPROGC, + KING_GSREG_MPROGD, + KING_GSREG_MPROGE, + KING_GSREG_MPROGF +}; + +uint32 KING_GetRegister(const unsigned int id, char* special, const uint32 special_len); +void KING_SetRegister(const unsigned int id, uint32 value); +#endif + +void KING_NotifyOfBPE(bool read, bool write); + +void KING_SetLogFunc(void (*logfunc)(const char *, const char *, ...)); + +void KING_EndFrame(v810_timestamp_t timestamp); +void KING_ResetTS(v810_timestamp_t ts_base); + +v810_timestamp_t MDFN_FASTCALL KING_Update(const v810_timestamp_t timestamp); + +} + +#endif diff --git a/waterbox/pcfx/king_mix_body.inc b/waterbox/pcfx/king_mix_body.inc new file mode 100644 index 0000000000..6ce370c991 --- /dev/null +++ b/waterbox/pcfx/king_mix_body.inc @@ -0,0 +1,83 @@ +/******************************************************************************/ +/* Mednafen NEC PC-FX Emulation Module */ +/******************************************************************************/ +/* king_mix_body.inc: +** Copyright (C) 2006-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + + if(fx_vce.dot_clock) // No cellophane in 7.16MHz pixel mode + { + if(HighDotClockWidth == 341) + for(unsigned int x = 0; x < 341; x++) + { + LAYER_MIX_BODY(x * 256 / 341, x); + LAYER_MIX_FINAL_NOCELLO; + } + else if(HighDotClockWidth == 256) + for(unsigned int x = 0; x < 256; x++) + { + LAYER_MIX_BODY(x, x * 341 / 256); + LAYER_MIX_FINAL_NOCELLO; + } + else + for(unsigned int x = 0; x < 1024; x++) + { + LAYER_MIX_BODY(x / 4, x / 3); + LAYER_MIX_FINAL_NOCELLO; + } + } + else if((vce_rendercache.BLE & 0xC000) == 0xC000) // Front cellophane + { + uint8 CCR_Y_front = vce_rendercache.coefficient_mul_table_y[(vce_rendercache.coefficients[0] >> 8) & 0xF][(vce_rendercache.CCR >> 8) & 0xFF]; + int8 CCR_U_front = vce_rendercache.coefficient_mul_table_uv[(vce_rendercache.coefficients[0] >> 4) & 0xF][(vce_rendercache.CCR & 0xF0)]; + int8 CCR_V_front = vce_rendercache.coefficient_mul_table_uv[(vce_rendercache.coefficients[0] >> 0) & 0xF][(vce_rendercache.CCR << 4) & 0xF0]; + + BPC_Cache = 0x008080 | (LAYER_NONE << 28); + + for(unsigned int x = 0; x < 256; x++) + { + LAYER_MIX_BODY(x, x); + LAYER_MIX_FINAL_FRONT_CELLO; + } + } + else if((vce_rendercache.BLE & 0xC000) == 0x4000) // Back cellophane + { + BPC_Cache = ((vce_rendercache.CCR & 0xFF00) << 8) | ((vce_rendercache.CCR & 0xF0) << 8) | ((vce_rendercache.CCR & 0x0F) << 4) | (LAYER_NONE << 28); + + for(unsigned int x = 0; x < 256; x++) + { + LAYER_MIX_BODY(x, x); + LAYER_MIX_FINAL_BACK_CELLO; + } + } + else if(ble_cache_any) // No front/back cello, but cellophane on at least 1 layer + { + for(unsigned int x = 0; x < 256; x++) + { + LAYER_MIX_BODY(x, x); + LAYER_MIX_FINAL_CELLO + } + } + else // No cellophane at all + { + for(unsigned int x = 0; x < 256; x++) + { + LAYER_MIX_BODY(x, x); + LAYER_MIX_FINAL_NOCELLO + } + } + diff --git a/waterbox/pcfx/math_ops.h b/waterbox/pcfx/math_ops.h new file mode 100644 index 0000000000..4154f2d49b --- /dev/null +++ b/waterbox/pcfx/math_ops.h @@ -0,0 +1,278 @@ +/******************************************************************************/ +/* Mednafen - Multi-system Emulator */ +/******************************************************************************/ +/* math_ops.h: +** Copyright (C) 2007-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/* +** Some ideas from: +** blargg +** http://graphics.stanford.edu/~seander/bithacks.html +*/ + +#ifndef __MDFN_MATH_OPS_H +#define __MDFN_MATH_OPS_H + +#if defined(_MSC_VER) + #include +#endif + +static INLINE unsigned MDFN_lzcount16_0UD(uint16 v) +{ + #if defined(__GNUC__) || defined(__clang__) || defined(__ICC) || defined(__INTEL_COMPILER) + return 15 ^ 31 ^ __builtin_clz(v); + #elif defined(_MSC_VER) + unsigned long idx; + + _BitScanReverse(&idx, v); + + return 15 ^ idx; + #else + unsigned ret = 0; + unsigned tmp; + + tmp = !(v & 0xFF00) << 3; v <<= tmp; ret += tmp; + tmp = !(v & 0xF000) << 2; v <<= tmp; ret += tmp; + tmp = !(v & 0xC000) << 1; v <<= tmp; ret += tmp; + tmp = !(v & 0x8000) << 0; ret += tmp; + + return(ret); + #endif +} + +static INLINE unsigned MDFN_lzcount32_0UD(uint32 v) +{ + #if defined(__GNUC__) || defined(__clang__) || defined(__ICC) || defined(__INTEL_COMPILER) + return __builtin_clz(v); + #elif defined(_MSC_VER) + unsigned long idx; + + _BitScanReverse(&idx, v); + + return 31 ^ idx; + #else + unsigned ret = 0; + unsigned tmp; + + tmp = !(v & 0xFFFF0000) << 4; v <<= tmp; ret += tmp; + tmp = !(v & 0xFF000000) << 3; v <<= tmp; ret += tmp; + tmp = !(v & 0xF0000000) << 2; v <<= tmp; ret += tmp; + tmp = !(v & 0xC0000000) << 1; v <<= tmp; ret += tmp; + tmp = !(v & 0x80000000) << 0; ret += tmp; + + return(ret); + #endif +} + +static INLINE unsigned MDFN_lzcount64_0UD(uint64 v) +{ + #if defined(__GNUC__) || defined(__clang__) || defined(__ICC) || defined(__INTEL_COMPILER) + return __builtin_clzll(v); + #elif defined(_MSC_VER) + #if defined(_WIN64) + unsigned long idx; + _BitScanReverse64(&idx, v); + return 63 ^ idx; + #else + unsigned long idx0; + unsigned long idx1; + + _BitScanReverse(&idx1, v >> 0); + idx1 -= 32; + if(!_BitScanReverse(&idx0, v >> 32)) + idx0 = idx1; + + idx0 += 32; + + return 63 ^ idx0; + #endif + #else + unsigned ret = 0; + unsigned tmp; + + tmp = !(v & 0xFFFFFFFF00000000ULL) << 5; v <<= tmp; ret += tmp; + tmp = !(v & 0xFFFF000000000000ULL) << 4; v <<= tmp; ret += tmp; + tmp = !(v & 0xFF00000000000000ULL) << 3; v <<= tmp; ret += tmp; + tmp = !(v & 0xF000000000000000ULL) << 2; v <<= tmp; ret += tmp; + tmp = !(v & 0xC000000000000000ULL) << 1; v <<= tmp; ret += tmp; + tmp = !(v & 0x8000000000000000ULL) << 0; ret += tmp; + + return(ret); + #endif +} + +static INLINE unsigned MDFN_tzcount16_0UD(uint16 v) +{ + #if defined(__GNUC__) || defined(__clang__) || defined(__ICC) || defined(__INTEL_COMPILER) + return __builtin_ctz(v); + #elif defined(_MSC_VER) + unsigned long idx; + + _BitScanForward(&idx, v); + + return idx; + #else + unsigned ret = 0; + unsigned tmp; + + tmp = !( (uint8)v) << 3; v >>= tmp; ret += tmp; + tmp = !(v & 0x000F) << 2; v >>= tmp; ret += tmp; + tmp = !(v & 0x0003) << 1; v >>= tmp; ret += tmp; + tmp = !(v & 0x0001) << 0; ret += tmp; + + return ret; + #endif +} + +static INLINE unsigned MDFN_tzcount32_0UD(uint32 v) +{ + #if defined(__GNUC__) || defined(__clang__) || defined(__ICC) || defined(__INTEL_COMPILER) + return __builtin_ctz(v); + #elif defined(_MSC_VER) + unsigned long idx; + + _BitScanForward(&idx, v); + + return idx; + #else + unsigned ret = 0; + unsigned tmp; + + tmp = !((uint16)v) << 4; v >>= tmp; ret += tmp; + tmp = !( (uint8)v) << 3; v >>= tmp; ret += tmp; + tmp = !(v & 0x000F) << 2; v >>= tmp; ret += tmp; + tmp = !(v & 0x0003) << 1; v >>= tmp; ret += tmp; + tmp = !(v & 0x0001) << 0; ret += tmp; + + return ret; + #endif +} + +static INLINE unsigned MDFN_tzcount64_0UD(uint64 v) +{ + #if defined(__GNUC__) || defined(__clang__) || defined(__ICC) || defined(__INTEL_COMPILER) + return __builtin_ctzll(v); + #elif defined(_MSC_VER) + #if defined(_WIN64) + unsigned long idx; + _BitScanForward64(&idx, v); + return idx; + #else + unsigned long idx0, idx1; + + _BitScanForward(&idx1, v >> 32); + idx1 += 32; + if(!_BitScanForward(&idx0, v)) + idx0 = idx1; + + return idx0; + #endif + #else + unsigned ret = 0; + unsigned tmp; + + tmp = !((uint32)v) << 5; v >>= tmp; ret += tmp; + tmp = !((uint16)v) << 4; v >>= tmp; ret += tmp; + tmp = !( (uint8)v) << 3; v >>= tmp; ret += tmp; + tmp = !(v & 0x000F) << 2; v >>= tmp; ret += tmp; + tmp = !(v & 0x0003) << 1; v >>= tmp; ret += tmp; + tmp = !(v & 0x0001) << 0; ret += tmp; + + return ret; + #endif +} + +// +// Result is defined for all possible inputs(including 0). +// +static INLINE unsigned MDFN_lzcount16(uint16 v) { return !v ? 16 : MDFN_lzcount16_0UD(v); } +static INLINE unsigned MDFN_lzcount32(uint32 v) { return !v ? 32 : MDFN_lzcount32_0UD(v); } +static INLINE unsigned MDFN_lzcount64(uint64 v) { return !v ? 64 : MDFN_lzcount64_0UD(v); } + +static INLINE unsigned MDFN_tzcount16(uint16 v) { return !v ? 16 : MDFN_tzcount16_0UD(v); } +static INLINE unsigned MDFN_tzcount32(uint32 v) { return !v ? 32 : MDFN_tzcount32_0UD(v); } +static INLINE unsigned MDFN_tzcount64(uint64 v) { return !v ? 64 : MDFN_tzcount64_0UD(v); } + +static INLINE unsigned MDFN_log2(uint32 v) { return 31 ^ MDFN_lzcount32_0UD(v | 1); } +static INLINE unsigned MDFN_log2(uint64 v) { return 63 ^ MDFN_lzcount64_0UD(v | 1); } + +static INLINE unsigned MDFN_log2(int32 v) { return MDFN_log2((uint32)v); } +static INLINE unsigned MDFN_log2(int64 v) { return MDFN_log2((uint64)v); } + +// Rounds up to the nearest power of 2(treats input as unsigned to a degree, but be aware of integer promotion rules). +// Returns 0 on overflow. +static INLINE uint64 round_up_pow2(uint32 v) { uint64 tmp = (uint64)1 << MDFN_log2(v); return tmp << (tmp < v); } +static INLINE uint64 round_up_pow2(uint64 v) { uint64 tmp = (uint64)1 << MDFN_log2(v); return tmp << (tmp < v); } + +static INLINE uint64 round_up_pow2(int32 v) { return round_up_pow2((uint32)v); } +static INLINE uint64 round_up_pow2(int64 v) { return round_up_pow2((uint64)v); } + +// Rounds to the nearest power of 2(treats input as unsigned to a degree, but be aware of integer promotion rules). +static INLINE uint64 round_nearest_pow2(uint32 v, bool round_half_up = true) { uint64 tmp = (uint64)1 << MDFN_log2(v); return tmp << (v && (((v - tmp) << 1) >= (tmp + !round_half_up))); } +static INLINE uint64 round_nearest_pow2(uint64 v, bool round_half_up = true) { uint64 tmp = (uint64)1 << MDFN_log2(v); return tmp << (v && (((v - tmp) << 1) >= (tmp + !round_half_up))); } + +static INLINE uint64 round_nearest_pow2(int32 v, bool round_half_up = true) { return round_nearest_pow2((uint32)v, round_half_up); } +static INLINE uint64 round_nearest_pow2(int64 v, bool round_half_up = true) { return round_nearest_pow2((uint64)v, round_half_up); } + +// Some compilers' optimizers and some platforms might fubar the generated code from these macros, +// so some tests are run in...tests.cpp +#define sign_8_to_s16(_value) ((int16)(int8)(_value)) +#define sign_9_to_s16(_value) (((int16)((unsigned int)(_value) << 7)) >> 7) +#define sign_10_to_s16(_value) (((int16)((uint32)(_value) << 6)) >> 6) +#define sign_11_to_s16(_value) (((int16)((uint32)(_value) << 5)) >> 5) +#define sign_12_to_s16(_value) (((int16)((uint32)(_value) << 4)) >> 4) +#define sign_13_to_s16(_value) (((int16)((uint32)(_value) << 3)) >> 3) +#define sign_14_to_s16(_value) (((int16)((uint32)(_value) << 2)) >> 2) +#define sign_15_to_s16(_value) (((int16)((uint32)(_value) << 1)) >> 1) + +// This obviously won't convert higher-than-32 bit numbers to signed 32-bit ;) +// Also, this shouldn't be used for 8-bit and 16-bit signed numbers, since you can +// convert those faster with typecasts... +#define sign_x_to_s32(_bits, _value) (((int32)((uint32)(_value) << (32 - _bits))) >> (32 - _bits)) + +static INLINE int32 clamp_to_u8(int32 i) +{ + if(i & 0xFFFFFF00) + i = (((~i) >> 30) & 0xFF); + + return(i); +} + +static INLINE int32 clamp_to_u16(int32 i) +{ + if(i & 0xFFFF0000) + i = (((~i) >> 31) & 0xFFFF); + + return(i); +} + +template static INLINE void clamp(T *val, U minimum, V maximum) +{ + if(*val < minimum) + { + //printf("Warning: clamping to minimum(%d)\n", (int)minimum); + *val = minimum; + } + if(*val > maximum) + { + //printf("Warning: clamping to maximum(%d)\n", (int)maximum); + *val = maximum; + } +} + +#endif diff --git a/waterbox/pcfx/mem-handler.inc b/waterbox/pcfx/mem-handler.inc new file mode 100644 index 0000000000..ad32196fbf --- /dev/null +++ b/waterbox/pcfx/mem-handler.inc @@ -0,0 +1,328 @@ +/******************************************************************************/ +/* Mednafen NEC PC-FX Emulation Module */ +/******************************************************************************/ +/* mem-handler.inc: +** Copyright (C) 2006-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +uint8 MDFN_FASTCALL mem_peekbyte(const v810_timestamp_t timestamp, const uint32 A) +{ + if (A <= 0x001FFFFF) + return (RAM[A]); + else if (A <= 0x00FFFFFF) + return (0xFF); + else if (A >= 0xF0000000) + return (BIOSROM[A & 0xFFFFF]); + else if (FXSCSIROM && A >= 0x80780000 && A <= 0x807FFFFF) + { + return (FXSCSIROM[A & 0x7FFFF]); + } + return (0xFF); +} + +uint16 MDFN_FASTCALL mem_peekhword(const v810_timestamp_t timestamp, const uint32 A) // TODO: Full memory map peeking. +{ + if (A <= 0x001FFFFF) + return MDFN_de16lsb(&RAM[A]); + else if (A <= 0x00FFFFFF) + return 0xFFFF; + else if (A >= 0xF0000000) + return MDFN_de16lsb(&BIOSROM[A & 0xFFFFF]); + else if (FXSCSIROM && A >= 0x80780000 && A <= 0x807FFFFF) + return MDFN_de16lsb(&FXSCSIROM[A & 0x7FFFF]); + + return 0xFFFF; +} + +static uint8 MDFN_FASTCALL mem_rbyte(v810_timestamp_t ×tamp, uint32 A) +{ + if (A <= 0x001FFFFF) + { + RAMLPCHECK; + return (RAM[A]); + } + else if (A <= 0x00FFFFFF) + { + RAMLPCHECK; + return (0xFF); + } + else if (A >= 0xF0000000) // BIOS ROM mirrored throughout 0xF0000000-0xFFFFFFFF, the "official" location + // is at 0xFFF00000(what about on a PC-FXGA??) + { + timestamp += 2; + return (BIOSROM[A & 0xFFFFF]); + } + else if (A >= 0xE0000000 && A <= 0xE7FFFFFF && !(A & 1)) + { + //printf("%d\n", (A - 0xE0000000) >> 1); + return (BackupRAM[(A & 0xFFFF) >> 1]); + } + else if (A >= 0xE8000000 && A <= 0xE9FFFFFF) + { + if (!(BackupControl & 0x2)) + { + FXDBG("Read8 from external BRAM when not enabled."); + } + + return (ExBackupRAM[(A & 0xFFFF) >> 1]); + } + else if (A >= 0x80000000 && A <= 0x807FFFFF) + { + //FXDBG("Mem->IO B Read Translation: %08x -> %08x", A, A & 0x7FFFFF); + return (port_rbyte(timestamp, A & 0x7FFFFF)); + } + FXDBG("Unknown byte read: %08x", A); + return (0xFF); +} + +static uint16 MDFN_FASTCALL mem_rhword(v810_timestamp_t ×tamp, uint32 A) +{ + if (A <= 0x001FFFFF) + { + RAMLPCHECK; + return MDFN_de16lsb(&RAM[A]); + } + else if (A <= 0x00FFFFFF) + { + RAMLPCHECK; + return (0xFFFF); + } + else if (A >= 0xF0000000) // BIOS ROM mirrored throughout 0xF0000000-0xFFFFFFFF, the "official" location + // is at 0xFFF00000 + { + timestamp += 2; + return MDFN_de16lsb(&BIOSROM[A & 0xFFFFF]); + } + else if (A >= 0xA0000000 && A <= 0xA3FFFFFF) + { + timestamp += 4; + return (FXVCE_Read16(0x4)); + } + else if (A >= 0xA4000000 && A <= 0xA7FFFFFF) + { + timestamp += 4; + return (fx_vdc_chips[0]->Read16(1)); + } + else if (A >= 0xA8000000 && A <= 0xABFFFFFF) + { + timestamp += 4; + return (fx_vdc_chips[1]->Read16(1)); + } + else if (A >= 0xAC000000 && A <= 0xAFFFFFFF) + { + timestamp += 4; + return (KING_Read16(timestamp, 0x604)); + } + else if (A >= 0xB0000000 && A <= 0xBFFFFFFF) // Write only + { + return (0); + } + else if (A >= 0xE0000000 && A <= 0xE7FFFFFF) + { + //printf("%d\n", (A - 0xE0000000) >> 1); + return (BackupRAM[(A & 0xFFFF) >> 1]); + } + else if (A >= 0xE8000000 && A <= 0xE9FFFFFF) + { + if (!(BackupControl & 0x2)) + { + FXDBG("Read16 from external BRAM when not enabled."); + } + + return (ExBackupRAM[(A & 0xFFFF) >> 1]); + } + else if (A >= 0xF8000000 && A <= 0xFFEFFFFF) // PIO + { + FXDBG("PIO H Read: %08x", A); + + return (0x00); + } + else if (A >= 0x80000000 && A <= 0x807FFFFF) + { + //FXDBG("Mem->IO H Read Translation: %08x -> %08x", A, A & 0x7FFFFF); + + return (port_rhword(timestamp, A & 0x7FFFFF)); + } + FXDBG("Unknown hword read: %08x", A); + + return (0xFFFF); +} + +static uint32 MDFN_FASTCALL mem_rword(v810_timestamp_t ×tamp, uint32 A) +{ + if (A <= 0x001FFFFF) + { + RAMLPCHECK; + return MDFN_de32lsb(&RAM[A]); + } + else if (A <= 0x00FFFFFF) + { + RAMLPCHECK; + return (0xFFFFFFFF); + } + else if (A >= 0xB0000000 && A <= 0xBFFFFFFF) // Write only + { + return (0); + } + else + { + uint32 ret; + + FXDBG("Warning: mem_rword() called for 16-bit bus access: %08x", A); + + ret = mem_rhword(timestamp, A); + ret |= mem_rhword(timestamp, A | 2) << 16; + + return (ret); + } + + FXDBG("Unknown word read: %08x", A); + + return (0xFFFFFFFF); +} + +static void MDFN_FASTCALL mem_wbyte(v810_timestamp_t ×tamp, uint32 A, uint8 V) +{ + if (A <= 0x001FFFFF) + { + RAMLPCHECK; + RAM[A] = V; + } + else if (A <= 0x00FFFFFF) + { + RAMLPCHECK; + } + else if (A >= 0xE0000000 && A <= 0xE7FFFFFF && !(A & 1)) + { + if (BackupControl & 0x1) + { + //BackupSignalDirty |= (BackupRAM[(A & 0xFFFF) >> 1] != V); + BackupRAM[(A & 0xFFFF) >> 1] = V; + } + } + else if (A >= 0xE8000000 && A <= 0xE9FFFFFF) + { + //printf("ExWrite: %08x", A); + if (BackupControl & 0x2) + { + //BackupSignalDirty |= (ExBackupRAM[(A & 0xFFFF) >> 1] != V); + ExBackupRAM[(A & 0xFFFF) >> 1] = V; + } + } + else if (A >= 0xF8000000 && A <= 0xFFEFFFFF) + { + FXDBG("PIO B Write: %08x %02x", A, V); + + // PIO? + } + else if (A >= 0x80000000 && A <= 0x807FFFFF) + { + //FXDBG("Mem->IO B Write Translation: %08x %02x -> %08x", A, V, A & 0x7FFFFF); + port_wbyte(timestamp, A & 0x7FFFFF, V); + } +} + +static void MDFN_FASTCALL mem_whword(v810_timestamp_t ×tamp, uint32 A, uint16 V) +{ + if (A <= 0x001FFFFF) + { + RAMLPCHECK; + MDFN_en16lsb(&RAM[A], V); + } + else if (A <= 0x00FFFFFF) + { + RAMLPCHECK; + } + else if (A >= 0xE0000000 && A <= 0xE7FFFFFF) + { + if (BackupControl & 0x1) + { + //BackupSignalDirty |= (BackupRAM[(A & 0xFFFF) >> 1] != (uint8)V); + BackupRAM[(A & 0xFFFF) >> 1] = (uint8)V; + } + } + else if (A >= 0xE8000000 && A <= 0xE9FFFFFF) + { + //printf("ExWrite16: %08x", A); + if (BackupControl & 0x2) + { + //BackupSignalDirty |= (ExBackupRAM[(A & 0xFFFF) >> 1] != (uint8)V); + ExBackupRAM[(A & 0xFFFF) >> 1] = (uint8)V; + } + } + else if (A >= 0xF8000000 && A <= 0xFFEFFFFF) + { + FXDBG("PIO H Write: %08x %04x", A, V); + + // PIO? + } + else if (A >= 0xA0000000 && A <= 0xAFFFFFFF) // Read only + { + } + else if (A >= 0xB0000000 && A <= 0xB3FFFFFF) + { + timestamp += 2; + FXVCE_Write16(0x4, V); + } + else if (A >= 0xB4000000 && A <= 0xB7FFFFFF) + { + timestamp += 2; + fx_vdc_chips[0]->Write16(1, V); + } + else if (A >= 0xB8000000 && A <= 0xBBFFFFFF) + { + timestamp += 2; + fx_vdc_chips[1]->Write16(1, V); + } + else if (A >= 0xBC000000 && A <= 0xBFFFFFFF) + { + timestamp += 2; + KING_Write16(timestamp, 0x604, V); + } + else if (A >= 0x80000000 && A <= 0x807FFFFF) + { + //FXDBG("Mem->IO H Write Translation: %08x %04x -> %08x", A, V, A & 0x7FFFFF); + + port_whword(timestamp, A & 0x7FFFFF, V); + } + else + { + FXDBG("Unknown hword write: %08x %04x", A, V); + } +} + +static void MDFN_FASTCALL mem_wword(v810_timestamp_t ×tamp, uint32 A, uint32 V) +{ + if (A <= 0x001FFFFF) + { + RAMLPCHECK; + MDFN_en32lsb(&RAM[A], V); + } + else if (A <= 0x00FFFFFF) + { + RAMLPCHECK; + } + else if (A >= 0xA0000000 && A <= 0xAFFFFFFF) // Read only + { + } + else + { + FXDBG("Warning: mem_wword() called for 16-bit bus access: %08x", A); + mem_whword(timestamp, A, V); + mem_whword(timestamp, A | 2, V >> 16); + } +} diff --git a/waterbox/pcfx/notes/GAME_NOTES b/waterbox/pcfx/notes/GAME_NOTES new file mode 100644 index 0000000000..9c5343cce4 --- /dev/null +++ b/waterbox/pcfx/notes/GAME_NOTES @@ -0,0 +1,140 @@ +------------------------- +Hardware scaling+rotation +------------------------- + +All Japan Female Pro Wrestling + Crazy zoom effects in the intro. + +Anime Freak FX Vol. 1 + Staff List text special effects. + +Anime Freak FX Vol. 2 + Crazy zoom effects in the intro. + +Anime Freak FX Vol. 3 + Staff List text special effects. + +Chip Chan Kick + Name entry screen. + +Cutey Honey FX + Map zoomin/zoomout. + +Der Langrisser FX + Progress/Map(?) when starting a new scenario. + +First Kiss Monogatari + Billboard current-day transition screen. + +Kishin Douji Zenki Vajura Fight FX + Screen blorping after defeating Marubasu. Outside level where you fight the + possessed frog for effects on one of the backgrounds. (Other places too???) + +Kokuu Hyouryuu Nirgends + While flying. + +Konpeki no Kantai + Overhead map screen. + +Last Imperial Prince + Title screen is flipped into place. + +Lunatic Dawn + Map screen. + +Megami Paradise II + Area transitions. + +Miraculum + Title screen, and in the air ship. + +Return to Zork + Background scaling. + +Super Power League FX + Baseball field. + +Tonari no Princess Rolfee + Scene transitions(sometimes). + +Tyoushin Heiki Zeroigar + Second stage boss. + + + +------------------ +256 color VDC mode +------------------ + +Angelique Tenkuu no Requim + +Angelique Special 2 + +Chip Chan Kick + Cybertech Custom screen, cutscenes. + +Ojousama Sousamou + +Tokimeki Card Paradise + + + +--------------- +Back cellophane +--------------- + +Last Imperial Prince + Screen darkening during dialogue, and upper/lower screen greenish tint bug afterwards...uses a non-black CCR value! + +Lunatic Dawn + Background fadeouts. + +PCE Fan Special CDROM Vol 2 + Fadeouts and fadeins. + +Sotsuguyou 2 + Fadeouts and fadeins. + +Team Innocent + Background fadeins. + + + +------------------------------------------------ +Bitstring Hardware I/O Map Mirrors in Memory Map +------------------------------------------------ + +Boundary Gate + +Super Power League FX + + + +---------------------------------- +KING BG mode/microprogram mismatch +---------------------------------- + +Megami Paradise II + +Tonari no Princess Rolfee + + + +------------------ +KING BG0 Subscreen +------------------ + +Angelique Special + Overhead map(probably a coding error on the game developers' part, but it's still + used to the extent that if it's not emulated, the map won't show). + + + +---------- +BIOS Notes +---------- + +CD+G Playback + Pausing/unpausing playback when playing a CD+G disc will cause corruption of the CD+G graphics. + + Using the scan forward/reverse when playing a CD+G disc will cause massive corruption of the CD+G graphics. diff --git a/waterbox/pcfx/pce_psg/pce_psg.cpp b/waterbox/pcfx/pce_psg/pce_psg.cpp new file mode 100644 index 0000000000..193153d238 --- /dev/null +++ b/waterbox/pcfx/pce_psg/pce_psg.cpp @@ -0,0 +1,821 @@ +/* Mednafen - Multi-system Emulator + * + * Original skeleton write handler and PSG structure definition: + * Copyright (C) 2001 Charles MacDonald + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "../defs.h" +#include "pce_psg.h" + +#include +#include + +// Frequency cache cutoff optimization threshold (<= FREQC7M_COT) +#define FREQC7M_COT 0x7 //0xA + +void PCE_PSG::SetVolume(double new_volume) +{ + for(int vl = 0; vl < 32; vl++) + { + double flub = 1.0 * new_volume * 8 / 6; + + if(vl) + flub /= pow(2, (double)1 / 4 * vl); // ~1.5dB reduction per increment of vl + + if(vl == 0x1F) + flub = 0; + + for(int samp = 0; samp < 32; samp++) + { + int eff_samp; + + if(revision == REVISION_HUC6280) + eff_samp = samp * 2; + else + eff_samp = samp * 2 - 0x1F; + + dbtable[vl][samp] = (int32)(flub * eff_samp * 128); // * 256); + dbtable_volonly[vl] = (int32)(flub * 65536); + + // dbtable[vl][samp] = (int32)(flub * eff_samp * 128); + // dbtable_volonly[vl] = (int32)(flub * 65536); + } + } +} + +// Note: Changing the 0x1F(not that there should be) would require changing the channel pseudo-off volume check logic later on. +static const int scale_tab[] = +{ + 0x00, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F, + 0x10, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F +}; + +#define CLOCK_LFSR(lfsr) { unsigned int newbit = ((lfsr >> 0) ^ (lfsr >> 1) ^ (lfsr >> 11) ^ (lfsr >> 12) ^ (lfsr >> 17)) & 1; lfsr = (lfsr >> 1) | (newbit << 17); } + +static const int16 Phase_Filter[2][7] = +{ + /* 0 */ { 35, 250, 579, 641, 425, 112, 6 }, // 2048 + /* 1 */ { 6, 112, 425, 641, 579, 250, 35 }, // 2048 +}; + +INLINE void PCE_PSG::UpdateOutputSub(const int32 timestamp, psg_channel *ch, const int32 samp0, const int32 samp1) +{ + int32 delta[2]; + + delta[0] = samp0 - ch->blip_prev_samp[0]; + delta[1] = samp1 - ch->blip_prev_samp[1]; + + const int16* c = Phase_Filter[(timestamp >> 1) & 1]; + const int32 l = (timestamp >> 2) & 0xFFFF; + + HRBufs[0][l + 0] += delta[0] * c[0]; + HRBufs[0][l + 1] += delta[0] * c[1]; + HRBufs[0][l + 2] += delta[0] * c[2]; + HRBufs[0][l + 3] += delta[0] * c[3]; + HRBufs[0][l + 4] += delta[0] * c[4]; + HRBufs[0][l + 5] += delta[0] * c[5]; + HRBufs[0][l + 6] += delta[0] * c[6]; + + HRBufs[1][l + 0] += delta[1] * c[0]; + HRBufs[1][l + 1] += delta[1] * c[1]; + HRBufs[1][l + 2] += delta[1] * c[2]; + HRBufs[1][l + 3] += delta[1] * c[3]; + HRBufs[1][l + 4] += delta[1] * c[4]; + HRBufs[1][l + 5] += delta[1] * c[5]; + HRBufs[1][l + 6] += delta[1] * c[6]; + + ch->blip_prev_samp[0] = samp0; + ch->blip_prev_samp[1] = samp1; +} + +void PCE_PSG::UpdateOutput_Norm(const int32 timestamp, psg_channel *ch) +{ + int sv = ch->dda; + + UpdateOutputSub(timestamp, ch, dbtable[ch->vl[0]][sv], + dbtable[ch->vl[1]][sv]); +} + +void PCE_PSG::UpdateOutput_Noise(const int32 timestamp, psg_channel *ch) +{ + int sv = ((ch->lfsr & 1) << 5) - (ch->lfsr & 1); //(ch->lfsr & 0x1) ? 0x1F : 0; + + UpdateOutputSub(timestamp, ch, dbtable[ch->vl[0]][sv], + dbtable[ch->vl[1]][sv]); +} + +void PCE_PSG::UpdateOutput_Off(const int32 timestamp, psg_channel *ch) +{ + UpdateOutputSub(timestamp, ch, 0, 0); +} + +void PCE_PSG::UpdateOutput_Accum_HuC6280A(const int32 timestamp, psg_channel *ch) +{ + int32 samp[2]; + + // 31(5-bit max) * 32 samples = 992 + // 992 / 2 = 496 + // + // 8 + 5 = 13 + // 13 - 12 = 1 + + samp[0] = ((int32)dbtable_volonly[ch->vl[0]] * ((int32)ch->samp_accum - 496)) >> (8 + 5); + samp[1] = ((int32)dbtable_volonly[ch->vl[1]] * ((int32)ch->samp_accum - 496)) >> (8 + 5); + + UpdateOutputSub(timestamp, ch, samp[0], samp[1]); +} + +void PCE_PSG::UpdateOutput_Accum_HuC6280(const int32 timestamp, psg_channel *ch) +{ + int32 samp[2]; + + samp[0] = ((int32)dbtable_volonly[ch->vl[0]] * (int32)ch->samp_accum) >> (8 + 5); + samp[1] = ((int32)dbtable_volonly[ch->vl[1]] * (int32)ch->samp_accum) >> (8 + 5); + + UpdateOutputSub(timestamp, ch, samp[0], samp[1]); +} + + +// This function should always be called after RecalcFreqCache() (it's not called from RecalcFreqCache to avoid redundant code) +void PCE_PSG::RecalcUOFunc(int chnum) +{ + psg_channel *ch = &channel[chnum]; + + //printf("UO Update: %d, %02x\n", chnum, ch->control); + + if((revision != REVISION_HUC6280 && !(ch->control & 0xC0)) || (revision == REVISION_HUC6280 && !(ch->control & 0x80))) + ch->UpdateOutput = &PCE_PSG::UpdateOutput_Off; + else if(ch->noisectrl & ch->control & 0x80) + ch->UpdateOutput = &PCE_PSG::UpdateOutput_Noise; + // If the control for the channel is in waveform play mode, and the (real) playback frequency is too high, and the channel is either not the LFO modulator channel or + // if the LFO trigger bit(which halts the LFO modulator channel's waveform incrementing when set) is clear + else if((ch->control & 0xC0) == 0x80 && ch->freq_cache <= FREQC7M_COT && (chnum != 1 || !(lfoctrl & 0x80)) ) + ch->UpdateOutput = UpdateOutput_Accum; + else + ch->UpdateOutput = &PCE_PSG::UpdateOutput_Norm; +} + + +void PCE_PSG::RecalcFreqCache(int chnum) +{ + psg_channel *ch = &channel[chnum]; + + if(chnum == 0 && (lfoctrl & 0x03)) + { + const uint32 shift = (((lfoctrl & 0x3) - 1) << 1); + uint8 la = channel[1].dda; + uint32 tmp_freq = (ch->frequency + ((uint32)(la - 0x10) << shift)) & 0xFFF; + + ch->freq_cache = (tmp_freq ? tmp_freq : 4096) << 1; + } + else + { + ch->freq_cache = (ch->frequency ? ch->frequency : 4096) << 1; + + if(chnum == 1 && (lfoctrl & 0x03)) + ch->freq_cache *= lfofreq ? lfofreq : 256; + } +} + +void PCE_PSG::RecalcNoiseFreqCache(int chnum) +{ + psg_channel *ch = &channel[chnum]; + int32 freq = 0x1F - (ch->noisectrl & 0x1F); + + if(!freq) + freq = 0x20; + else + freq <<= 6; + + freq <<= 1; + + ch->noise_freq_cache = freq; +} + +void PCE_PSG::PeekWave(const unsigned int ch, uint32 Address, uint32 Length, uint8 *Buffer) +{ + assert(ch <= 5); + + while(Length--) + { + Address &= 0x1F; + *Buffer = channel[ch].waveform[Address]; + Address++; + Buffer++; + } +} + +void PCE_PSG::PokeWave(const unsigned int ch, uint32 Address, uint32 Length, const uint8 *Buffer) +{ + assert(ch <= 5); + + while(Length--) + { + Address &= 0x1F; + channel[ch].samp_accum -= channel[ch].waveform[Address]; + channel[ch].waveform[Address] = *Buffer & 0x1F; + channel[ch].samp_accum += channel[ch].waveform[Address]; + Address++; + Buffer++; + } +} + +uint32 PCE_PSG::GetRegister(const unsigned int id, char *special, const uint32 special_len) +{ + uint32 value = 0xDEADBEEF; + const int ch = (id >> 8) & 0xF; + + switch(id & 0xF0FF) + { + default: break; + + case PSG_GSREG_SELECT: + value = select; + break; + + case PSG_GSREG_GBALANCE: + value = globalbalance; + break; + + case PSG_GSREG_LFOFREQ: + value = lfofreq; + break; + + case PSG_GSREG_LFOCTRL: + value = lfoctrl; + break; + + case PSG_GSREG_CH0_FREQ: + value = channel[ch].frequency; + break; + + case PSG_GSREG_CH0_CTRL: + value = channel[ch].control; + break; + + case PSG_GSREG_CH0_BALANCE: + value = channel[ch].balance; + break; + + case PSG_GSREG_CH0_WINDEX: + value = channel[ch].waveform_index; + break; + + case PSG_GSREG_CH0_SCACHE: + value = channel[ch].dda; + break; + + case PSG_GSREG_CH0_NCTRL: + value = channel[ch].noisectrl; + break; + + case PSG_GSREG_CH0_LFSR: + value = channel[ch].lfsr & 0x3FFFF; + break; + } + return(value); +} + + +void PCE_PSG::SetRegister(const unsigned int id, const uint32 value) +{ + const int ch = (id >> 8) & 0xF; + + switch(id & 0xF0FF) + { + default: break; + + case PSG_GSREG_SELECT: + select = value & 0x07; + break; + + case PSG_GSREG_GBALANCE: + globalbalance = value & 0xFF; + break; + + case PSG_GSREG_LFOFREQ: + lfofreq = value & 0xFF; + break; + + case PSG_GSREG_LFOCTRL: + lfoctrl = value & 0x83; + RecalcFreqCache(0); + RecalcUOFunc(0); + RecalcFreqCache(1); + RecalcUOFunc(1); + break; + + case PSG_GSREG_CH0_FREQ: + channel[ch].frequency = value & 0xFFF; + RecalcFreqCache(ch); + RecalcUOFunc(ch); + break; + + case PSG_GSREG_CH0_CTRL: + channel[ch].control = value & 0xFF; + RecalcFreqCache(ch); + RecalcUOFunc(ch); + break; + + case PSG_GSREG_CH0_BALANCE: + channel[ch].balance = value & 0xFF; + break; + + case PSG_GSREG_CH0_WINDEX: + channel[ch].waveform_index = value & 0x1F; + break; + + case PSG_GSREG_CH0_SCACHE: + channel[ch].dda = value & 0x1F; + break; + + case PSG_GSREG_CH0_NCTRL: + channel[ch].noisectrl = value & 0xFF; + RecalcNoiseFreqCache(ch); + RecalcUOFunc(ch); + break; + + case PSG_GSREG_CH0_LFSR: + channel[ch].lfsr = value & 0x3FFFF; + break; + } +} + + +#if 0 +void PSG_SetRegister(const unsigned int id, const uint32 value) +{ + + + if(name == "Select") + PSG_Write(0x00, V); + else if(name == "GBalance") + PSG_Write(0x01, V); + else if(name == "LFOFreq") + { + PSG_Write(0x08, V); + } + else if(name == "LFOCtrl") + PSG_Write(0x09, V); + else if(!strncmp(name.c_str(), "CH", 2)) + { + unsigned int psg_sel_save = select; + int ch = name[2] - '0'; + char moomoo[64]; + strncpy(moomoo, name.c_str() + 3, 63); + + PSG_Write(0x00, ch); + + if(!strcmp(moomoo, "Freq")) + { + PSG_Write(0x02, V); + PSG_Write(0x03, V >> 8); + } + else if(!strcmp(moomoo, "Ctrl")) + PSG_Write(0x04, V); + else if(!strcmp(moomoo, "Balance")) + PSG_Write(0x05, V); + else if(!strcmp(moomoo, "WIndex")) + psg.channel[ch].waveform_index = V & 0x1F; + else if(!strcmp(moomoo, "SCache")) + psg.channel[ch].dda = V & 0x1F; + else if(!strcmp(moomoo, "NCtrl") && ch < 4) + psg.channel[ch].noisectrl = V; + else if(!strcmp(moomoo, "LFSR") && ch < 4) + psg.channel[ch].lfsr = V & 0x3FFFF; + + PSG_Write(0x00, psg_sel_save); + } +} +#endif + +PCE_PSG::PCE_PSG(int32* hr_l, int32* hr_r, int want_revision) +{ + //printf("Test: %u, %u\n", sizeof(psg_channel), (uint8*)&channel[0].balance - (uint8*)&channel[0].waveform[0]); + + revision = want_revision; + switch(revision) + { + default: + abort(); + break; + + case REVISION_HUC6280: + UpdateOutput_Accum = &PCE_PSG::UpdateOutput_Accum_HuC6280; + break; + + case REVISION_HUC6280A: + UpdateOutput_Accum = &PCE_PSG::UpdateOutput_Accum_HuC6280A; + break; + } + HRBufs[0] = hr_l; + HRBufs[1] = hr_r; + + lastts = 0; + for(int ch = 0; ch < 6; ch++) + { + channel[ch].blip_prev_samp[0] = 0; + channel[ch].blip_prev_samp[1] = 0; + channel[ch].lastts = 0; + } + + SetVolume(1.0); // Will build dbtable in the process. + Power(0); +} + +PCE_PSG::~PCE_PSG() +{ + + +} + +int32 PCE_PSG::GetVL(const int chnum, const int lr) +{ + psg_channel *ch = &channel[chnum]; + + const int gbal = 0x1F - scale_tab[(globalbalance >> (lr ? 0 : 4)) & 0xF]; + const int bal = 0x1F - scale_tab[(ch->balance >> (lr ? 0 : 4)) & 0xF]; + const int al = 0x1F - (ch->control & 0x1F); + int vol_reduction; + + vol_reduction = gbal + bal + al; + + if(vol_reduction > 0x1F) + vol_reduction = 0x1F; + + return(vol_reduction); +} + +void PCE_PSG::Write(int32 timestamp, uint8 A, uint8 V) +{ + A &= 0x0F; + + if(A == 0x00) + { + select = (V & 0x07); + return; + } + + Update(timestamp); + + psg_channel *ch = &channel[select]; + + //if(A == 0x01 || select == 5) + // printf("Write Ch: %d %04x %02x, %d\n", select, A, V, timestamp); + + switch(A) + { + default: break; + + case 0x01: /* Global sound balance */ + globalbalance = V; + vol_pending = true; + break; + + case 0x02: /* Channel frequency (LSB) */ + if(select > 5) return; // no more than 6 channels, silly game. + + ch->frequency = (ch->frequency & 0x0F00) | V; + RecalcFreqCache(select); + RecalcUOFunc(select); + break; + + case 0x03: /* Channel frequency (MSB) */ + if(select > 5) return; // no more than 6 channels, silly game. + + ch->frequency = (ch->frequency & 0x00FF) | ((V & 0x0F) << 8); + RecalcFreqCache(select); + RecalcUOFunc(select); + break; + + case 0x04: /* Channel enable, DDA, volume */ + if(select > 5) return; // no more than 6 channels, silly game. + + if((ch->control & 0x40) && !(V & 0x40)) + { + ch->waveform_index = 0; + ch->dda = ch->waveform[ch->waveform_index]; + ch->counter = ch->freq_cache; + } + + if(!(ch->control & 0x80) && (V & 0x80)) + { + if(!(V & 0x40)) + { + ch->waveform_index = (ch->waveform_index + 1) & 0x1F; + ch->dda = ch->waveform[ch->waveform_index]; + } + } + + ch->control = V; + RecalcFreqCache(select); + RecalcUOFunc(select); + + vol_pending = true; + break; + + case 0x05: /* Channel balance */ + if(select > 5) return; // no more than 6 channels, silly game. + ch->balance = V; + + vol_pending = true; + break; + + case 0x06: /* Channel waveform data */ + if(select > 5) return; // no more than 6 channels, silly game. + V &= 0x1F; + + if(!(ch->control & 0x40)) + { + ch->samp_accum -= ch->waveform[ch->waveform_index]; + ch->waveform[ch->waveform_index] = V; + ch->samp_accum += ch->waveform[ch->waveform_index]; + } + + if((ch->control & 0xC0) == 0x00) + ch->waveform_index = ((ch->waveform_index + 1) & 0x1F); + + if(ch->control & 0x80) + { + // According to my tests(on SuperGrafx), writing to this channel + // will update the waveform value cache/latch regardless of DDA mode being enabled. + ch->dda = V; + } + break; + + case 0x07: /* Noise enable and frequency */ + if(select > 5) return; // no more than 6 channels, silly game. + if(select >= 4) + { + ch->noisectrl = V; + RecalcNoiseFreqCache(select); + RecalcUOFunc(select); + } + break; + + case 0x08: /* LFO frequency */ + lfofreq = V & 0xFF; + //printf("LFO Freq: %02x\n", V); + break; + + case 0x09: /* LFO trigger and control */ + //printf("LFO Ctrl: %02x\n", V); + if(V & 0x80) + { + channel[1].waveform_index = 0; + channel[1].dda = channel[1].waveform[channel[1].waveform_index]; + channel[1].counter = channel[1].freq_cache; + } + lfoctrl = V; + RecalcFreqCache(0); + RecalcUOFunc(0); + RecalcFreqCache(1); + RecalcUOFunc(1); + break; + } +} + +// Don't use INLINE, which has always_inline in it, due to gcc's inability to cope with the type of recursion +// used in this function. +void PCE_PSG::RunChannel(int chc, int32 timestamp, const bool LFO_On) +{ + psg_channel *ch = &channel[chc]; + int32 running_timestamp = ch->lastts; + int32 run_time = timestamp - ch->lastts; + + ch->lastts = timestamp; + + if(!run_time) + return; + + (this->*ch->UpdateOutput)(running_timestamp, ch); + + if(chc >= 4) + { + int32 freq = ch->noise_freq_cache; + + ch->noisecount -= run_time; + + if(&PCE_PSG::UpdateOutput_Noise == ch->UpdateOutput) + while(ch->noisecount <= 0) + { + CLOCK_LFSR(ch->lfsr); + UpdateOutput_Noise(timestamp + ch->noisecount, ch); + ch->noisecount += freq; + } + else + while(ch->noisecount <= 0) + { + CLOCK_LFSR(ch->lfsr); + ch->noisecount += freq; + } + } + + // D7 of control is 0, don't clock the counter at all. + // D7 of lfocontrol is 1(and chc == 1), don't clock the counter at all(not sure about this) + // In DDA mode, don't clock the counter. + // (Noise being enabled isn't handled here since AFAIK it doesn't disable clocking of the waveform portion, its sound just overrides the sound from + // the waveform portion when the noise enable bit is set, which is handled in our RecalcUOFunc). + if(!(ch->control & 0x80) || (chc == 1 && (lfoctrl & 0x80)) || (ch->control & 0x40)) + return; + + ch->counter -= run_time; + + if(!LFO_On && ch->freq_cache <= FREQC7M_COT) + { + if(ch->counter <= 0) + { + const int32 inc_count = ((0 - ch->counter) / ch->freq_cache) + 1; + + ch->counter += inc_count * ch->freq_cache; + + ch->waveform_index = (ch->waveform_index + inc_count) & 0x1F; + ch->dda = ch->waveform[ch->waveform_index]; + } + } + + while(ch->counter <= 0) + { + ch->waveform_index = (ch->waveform_index + 1) & 0x1F; + ch->dda = ch->waveform[ch->waveform_index]; + + (this->*ch->UpdateOutput)(timestamp + ch->counter, ch); + + if(LFO_On) + { + RunChannel(1, timestamp + ch->counter, false); + RecalcFreqCache(0); + RecalcUOFunc(0); + + ch->counter += (ch->freq_cache <= FREQC7M_COT) ? FREQC7M_COT : ch->freq_cache; // Not particularly accurate, but faster. + } + else + ch->counter += ch->freq_cache; + } +} + +void PCE_PSG::UpdateSubLFO(int32 timestamp) +{ + for(int chc = 0; chc < 6; chc++) + RunChannel(chc, timestamp, chc == 0); +} + +void PCE_PSG::UpdateSubNonLFO(int32 timestamp) +{ + for(int chc = 0; chc < 6; chc++) + RunChannel(chc, timestamp, false); +} + +void PCE_PSG::Update(int32 timestamp) +{ + int32 run_time = timestamp - lastts; + + if(vol_pending && !vol_update_counter && !vol_update_which) + { + vol_update_counter = 1; + vol_pending = false; + } + + bool lfo_on = (bool)(lfoctrl & 0x03); + + if(lfo_on) + { + if(!(channel[1].control & 0x80) || (lfoctrl & 0x80)) + { + lfo_on = 0; + RecalcFreqCache(0); + RecalcUOFunc(0); + } + } + + int32 clocks = run_time; + int32 running_timestamp = lastts; + + while(clocks > 0) + { + int32 chunk_clocks = clocks; + + if(vol_update_counter > 0 && chunk_clocks > vol_update_counter) + chunk_clocks = vol_update_counter; + + running_timestamp += chunk_clocks; + clocks -= chunk_clocks; + + if(lfo_on) + UpdateSubLFO(running_timestamp); + else + UpdateSubNonLFO(running_timestamp); + + if(vol_update_counter > 0) + { + vol_update_counter -= chunk_clocks; + if(!vol_update_counter) + { + const int phase = vol_update_which & 1; + const int lr = ((vol_update_which >> 1) & 1) ^ 1; + const int chnum = vol_update_which >> 2; + + if(!phase) + { + //printf("Volume update(Read, %d since last): ch=%d, lr=%d, ts=%d\n", running_timestamp - last_read, chnum, lr, running_timestamp); + + if(chnum < 6) + { + vol_update_vllatch = GetVL(chnum, lr); + } + //last_read = running_timestamp; + } + else + { + // printf("Volume update(Apply): ch=%d, lr=%d, ts=%d\n", chnum, lr, running_timestamp); + if(chnum < 6) + { + channel[chnum].vl[lr] = vol_update_vllatch; + } + //last_apply = running_timestamp; + } + vol_update_which = (vol_update_which + 1) & 0x1F; + + if(vol_update_which) + vol_update_counter = phase ? 1 : 255; + else if(vol_pending) + { + vol_update_counter = phase ? 1 : 255; + vol_pending = false; + } + } + } + + lastts = running_timestamp; + } +} + +void PCE_PSG::ResetTS(int32 ts_base) +{ + lastts = ts_base; + + for(int chc = 0; chc < 6; chc++) + channel[chc].lastts = ts_base; +} + +void PCE_PSG::Power(const int32 timestamp) +{ + // Not sure about power-on values, these are mostly just intuitive guesses(with some laziness thrown in). + if(timestamp != lastts) + Update(timestamp); + + // Don't memset channel to 0, there's stuff like lastts and blip_prev_samp that shouldn't be altered on Power(). + + select = 0; + globalbalance = 0; + lfofreq = 0; + lfoctrl = 0; + + for(int ch = 0; ch < 6; ch++) + { + channel[ch].frequency = 0; + channel[ch].control = 0x00; + channel[ch].balance = 0; + memset(channel[ch].waveform, 0, 32); + channel[ch].samp_accum = 0; + + channel[ch].waveform_index = 0; + channel[ch].dda = 0x00; + channel[ch].noisectrl = 0x00; + + channel[ch].vl[0] = 0x1F; + channel[ch].vl[1] = 0x1F; + + channel[ch].samp_accum = 0; + + RecalcFreqCache(ch); + RecalcUOFunc(ch); + + channel[ch].counter = channel[ch].freq_cache; + + if(ch >= 4) + { + RecalcNoiseFreqCache(ch); + } + channel[ch].noisecount = 1; + channel[ch].lfsr = 1; + } + + vol_pending = false; + vol_update_counter = 0; + vol_update_which = 0; +} diff --git a/waterbox/pcfx/pce_psg/pce_psg.h b/waterbox/pcfx/pce_psg/pce_psg.h new file mode 100644 index 0000000000..96bb65ef3c --- /dev/null +++ b/waterbox/pcfx/pce_psg/pce_psg.h @@ -0,0 +1,187 @@ +/* Mednafen - Multi-system Emulator + * + * Original skeleton write handler and PSG structure definition: + * Copyright (C) 2001 Charles MacDonald + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _PCE_PSG_H +#define _PCE_PSG_H + +class PCE_PSG; + +struct psg_channel +{ + uint8 waveform[32]; /* Waveform data */ + uint8 waveform_index; /* Waveform data index */ + uint8 dda; + uint8 control; /* Channel enable, DDA, volume */ + uint8 noisectrl; /* Noise enable/ctrl (channels 4,5 only) */ + + int32 vl[2]; //vll, vlr; + + int32 counter; + + void (PCE_PSG::*UpdateOutput)(const int32 timestamp, psg_channel *ch); + + uint32 freq_cache; + uint32 noise_freq_cache; // Channel 4,5 only + int32 noisecount; + uint32 lfsr; + + int32 samp_accum; // The result of adding up all the samples in the waveform buffer(part of an optimization for high-frequency playback). + int32 blip_prev_samp[2]; + int32 lastts; + + uint16 frequency; /* Channel frequency */ + uint8 balance; /* Channel balance */ +}; + +// Only CH4 and CH5 have NCTRL and LFSR, but it's here for the other channels for "consistency". +enum +{ + PSG_GSREG_CH0_FREQ = 0x000, + // PSG_GSREG_CH0_COUNTER, + PSG_GSREG_CH0_CTRL, + PSG_GSREG_CH0_BALANCE, + PSG_GSREG_CH0_WINDEX, + PSG_GSREG_CH0_SCACHE, + PSG_GSREG_CH0_NCTRL, + PSG_GSREG_CH0_LFSR, + + PSG_GSREG_CH1_FREQ = 0x100, + // PSG_GSREG_CH1_COUNTER, + PSG_GSREG_CH1_CTRL, + PSG_GSREG_CH1_BALANCE, + PSG_GSREG_CH1_WINDEX, + PSG_GSREG_CH1_SCACHE, + PSG_GSREG_CH1_NCTRL, + PSG_GSREG_CH1_LFSR, + + PSG_GSREG_CH2_FREQ = 0x200, + // PSG_GSREG_CH2_COUNTER, + PSG_GSREG_CH2_CTRL, + PSG_GSREG_CH2_BALANCE, + PSG_GSREG_CH2_WINDEX, + PSG_GSREG_CH2_SCACHE, + PSG_GSREG_CH2_NCTRL, + PSG_GSREG_CH2_LFSR, + + PSG_GSREG_CH3_FREQ = 0x300, + // PSG_GSREG_CH3_COUNTER, + PSG_GSREG_CH3_CTRL, + PSG_GSREG_CH3_BALANCE, + PSG_GSREG_CH3_WINDEX, + PSG_GSREG_CH3_SCACHE, + PSG_GSREG_CH3_NCTRL, + PSG_GSREG_CH3_LFSR, + + PSG_GSREG_CH4_FREQ = 0x400, + // PSG_GSREG_CH4_COUNTER, + PSG_GSREG_CH4_CTRL, + PSG_GSREG_CH4_BALANCE, + PSG_GSREG_CH4_WINDEX, + PSG_GSREG_CH4_SCACHE, + PSG_GSREG_CH4_NCTRL, + PSG_GSREG_CH4_LFSR, + + PSG_GSREG_CH5_FREQ = 0x500, + // PSG_GSREG_CH5_COUNTER, + PSG_GSREG_CH5_CTRL, + PSG_GSREG_CH5_BALANCE, + PSG_GSREG_CH5_WINDEX, + PSG_GSREG_CH5_SCACHE, + PSG_GSREG_CH5_NCTRL, + PSG_GSREG_CH5_LFSR, + + PSG_GSREG_SELECT = 0x1000, + PSG_GSREG_GBALANCE, + PSG_GSREG_LFOFREQ, + PSG_GSREG_LFOCTRL, + _PSG_GSREG_COUNT +}; + +class PCE_PSG +{ + public: + enum + { + REVISION_HUC6280 = 0, + REVISION_HUC6280A, + _REVISION_COUNT + }; + + PCE_PSG(int32 *hr_l, int32 *hr_r, int want_revision) + MDFN_COLD; + ~PCE_PSG() MDFN_COLD; + + void Power(const int32 timestamp) MDFN_COLD; + void Write(int32 timestamp, uint8 A, uint8 V); + + void SetVolume(double new_volume); + + void Update(int32 timestamp); + void ResetTS(int32 ts_base = 0); + + // TODO: timestamp + uint32 GetRegister(const unsigned int id, char *special, const uint32 special_len); + void SetRegister(const unsigned int id, const uint32 value); + + void PeekWave(const unsigned int ch, uint32 Address, uint32 Length, uint8 *Buffer); + void PokeWave(const unsigned int ch, uint32 Address, uint32 Length, const uint8 *Buffer); + + private: + void UpdateSubLFO(int32 timestamp); + void UpdateSubNonLFO(int32 timestamp); + + void RecalcUOFunc(int chnum); + void UpdateOutputSub(const int32 timestamp, psg_channel *ch, const int32 samp0, const int32 samp1); + void UpdateOutput_Off(const int32 timestamp, psg_channel *ch); + void UpdateOutput_Accum_HuC6280(const int32 timestamp, psg_channel *ch); + void UpdateOutput_Accum_HuC6280A(const int32 timestamp, psg_channel *ch); + void UpdateOutput_Norm(const int32 timestamp, psg_channel *ch); + void UpdateOutput_Noise(const int32 timestamp, psg_channel *ch); + void (PCE_PSG::*UpdateOutput_Accum)(const int32 timestamp, psg_channel *ch); + + int32 GetVL(const int chnum, const int lr); + + void RecalcFreqCache(int chnum); + void RecalcNoiseFreqCache(int chnum); + void RunChannel(int chc, int32 timestamp, bool LFO_On); + + uint8 select; /* Selected channel (0-5) */ + uint8 globalbalance; /* Global sound balance */ + uint8 lfofreq; /* LFO frequency */ + uint8 lfoctrl; /* LFO control */ + + int32 vol_update_counter; + int32 vol_update_which; + int32 vol_update_vllatch; + bool vol_pending; + + psg_channel channel[6]; + + int32 lastts; + int revision; + + int32 *HRBufs[2]; + + int32 dbtable_volonly[32]; + + int32 dbtable[32][32]; +}; + +#endif diff --git a/waterbox/pcfx/pcfx.cpp b/waterbox/pcfx/pcfx.cpp new file mode 100644 index 0000000000..75d8307d67 --- /dev/null +++ b/waterbox/pcfx/pcfx.cpp @@ -0,0 +1,841 @@ +/******************************************************************************/ +/* Mednafen NEC PC-FX Emulation Module */ +/******************************************************************************/ +/* pcfx.cpp: +** Copyright (C) 2006-2017 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "pcfx.h" +#include "soundbox.h" +#include "input.h" +#include "king.h" +#include "timer.h" +#include "interrupt.h" +#include "rainbow.h" +#include "huc6273.h" +#include "fxscsi.h" +#include "cdrom/cdromif.h" +#include "cdrom/scsicd.h" +//#include + +#include +#include +#include + +#include "../emulibc/emulibc.h" +#include "../emulibc/waterboxcore.h" + +namespace MDFN_IEN_PCFX +{ + +/* FIXME: soundbox, vce, vdc, rainbow, and king store wait states should be 4, not 2, but V810 has write buffers which can mask wait state penalties. + This is a hack to somewhat address the issue, but to really fix it, we need to handle write buffer emulation in the V810 emulation core itself. +*/ +static std::vector *cdifs = NULL; + +V810 PCFX_V810; + +static uint8 *BIOSROM = NULL; // 1MB +static uint8 *RAM = NULL; // 2MB +static uint8 *FXSCSIROM = NULL; // 512KiB + +static uint32 RAM_LPA; // Last page access + +static const int RAM_PageSize = 2048; +static const int RAM_PageNOTMask = ~(RAM_PageSize - 1); + +static uint16 Last_VDC_AR[2]; + +static bool WantHuC6273 = FALSE; + +//static +VDC *fx_vdc_chips[2]; + +static uint16 BackupControl; +static uint8 BackupRAM[0x8000], ExBackupRAM[0x8000]; +static uint8 ExBusReset; // I/O Register at 0x0700 + +// Checks to see if this main-RAM-area access +// is in the same DRAM page as the last access. +#define RAMLPCHECK \ + { \ + if ((A & RAM_PageNOTMask) != RAM_LPA) \ + { \ + timestamp += 3; \ + RAM_LPA = A & RAM_PageNOTMask; \ + } \ + } + +static v810_timestamp_t next_pad_ts, next_timer_ts, next_adpcm_ts, next_king_ts; + +void PCFX_FixNonEvents(void) +{ + if (next_pad_ts & 0x40000000) + next_pad_ts = PCFX_EVENT_NONONO; + + if (next_timer_ts & 0x40000000) + next_timer_ts = PCFX_EVENT_NONONO; + + if (next_adpcm_ts & 0x40000000) + next_adpcm_ts = PCFX_EVENT_NONONO; + + if (next_king_ts & 0x40000000) + next_king_ts = PCFX_EVENT_NONONO; +} + +void PCFX_Event_Reset(void) +{ + next_pad_ts = PCFX_EVENT_NONONO; + next_timer_ts = PCFX_EVENT_NONONO; + next_adpcm_ts = PCFX_EVENT_NONONO; + next_king_ts = PCFX_EVENT_NONONO; +} + +static INLINE uint32 CalcNextTS(void) +{ + v810_timestamp_t next_timestamp = next_king_ts; + + if (next_timestamp > next_pad_ts) + next_timestamp = next_pad_ts; + + if (next_timestamp > next_timer_ts) + next_timestamp = next_timer_ts; + + if (next_timestamp > next_adpcm_ts) + next_timestamp = next_adpcm_ts; + + return (next_timestamp); +} + +static void RebaseTS(const v810_timestamp_t timestamp, const v810_timestamp_t new_base_timestamp) +{ + assert(next_pad_ts > timestamp); + assert(next_timer_ts > timestamp); + assert(next_adpcm_ts > timestamp); + assert(next_king_ts > timestamp); + + next_pad_ts -= (timestamp - new_base_timestamp); + next_timer_ts -= (timestamp - new_base_timestamp); + next_adpcm_ts -= (timestamp - new_base_timestamp); + next_king_ts -= (timestamp - new_base_timestamp); + + //printf("RTS: %d %d %d %d\n", next_pad_ts, next_timer_ts, next_adpcm_ts, next_king_ts); +} + +void PCFX_SetEvent(const int type, const v810_timestamp_t next_timestamp) +{ + //assert(next_timestamp > PCFX_V810.v810_timestamp); + + if (type == PCFX_EVENT_PAD) + next_pad_ts = next_timestamp; + else if (type == PCFX_EVENT_TIMER) + next_timer_ts = next_timestamp; + else if (type == PCFX_EVENT_ADPCM) + next_adpcm_ts = next_timestamp; + else if (type == PCFX_EVENT_KING) + next_king_ts = next_timestamp; + + if (next_timestamp < PCFX_V810.GetEventNT()) + PCFX_V810.SetEventNT(next_timestamp); +} + +int32 MDFN_FASTCALL pcfx_event_handler(const v810_timestamp_t timestamp) +{ + if (timestamp >= next_king_ts) + next_king_ts = KING_Update(timestamp); + + if (timestamp >= next_pad_ts) + next_pad_ts = FXINPUT_Update(timestamp); + + if (timestamp >= next_timer_ts) + next_timer_ts = FXTIMER_Update(timestamp); + + if (timestamp >= next_adpcm_ts) + next_adpcm_ts = SoundBox_ADPCMUpdate(timestamp); + +#if 1 + assert(next_king_ts > timestamp); + assert(next_pad_ts > timestamp); + assert(next_timer_ts > timestamp); + assert(next_adpcm_ts > timestamp); +#endif + return (CalcNextTS()); +} + +static void ForceEventUpdates(const uint32 timestamp) +{ + next_king_ts = KING_Update(timestamp); + next_pad_ts = FXINPUT_Update(timestamp); + next_timer_ts = FXTIMER_Update(timestamp); + next_adpcm_ts = SoundBox_ADPCMUpdate(timestamp); + + //printf("Meow: %d\n", CalcNextTS()); + PCFX_V810.SetEventNT(CalcNextTS()); + + //printf("FEU: %d %d %d %d\n", next_pad_ts, next_timer_ts, next_adpcm_ts, next_king_ts); +} + +#include "io-handler.inc" +#include "mem-handler.inc" + +typedef struct +{ + int8 tracknum; + int8 format; + uint32 lba; +} CDGameEntryTrack; + +typedef struct +{ + const char *name; + const char *name_original; // Original non-Romanized text. + const uint32 flags; // Emulation flags. + const unsigned int discs; // Number of discs for this game. + CDGameEntryTrack tracks[2][100]; // 99 tracks and 1 leadout track +} CDGameEntry; + +#define CDGE_FORMAT_AUDIO 0 +#define CDGE_FORMAT_DATA 1 + +#define CDGE_FLAG_ACCURATE_V810 0x01 +#define CDGE_FLAG_FXGA 0x02 + +static uint32 EmuFlags; + +static const CDGameEntry GameList[] = + { +#include "gamedb.inc" +}; + +static void Emulate(EmulateSpecStruct *espec) +{ + FXINPUT_Frame(); + + KING_StartFrame(fx_vdc_chips, espec); //espec->surface, &espec->DisplayRect, espec->LineWidths, espec->skip); + + v810_timestamp_t v810_timestamp; + v810_timestamp = PCFX_V810.Run(pcfx_event_handler); + + PCFX_FixNonEvents(); + + // Call before resetting v810_timestamp + ForceEventUpdates(v810_timestamp); + + // + // Call KING_EndFrame() before SoundBox_Flush(), otherwise CD-DA audio distortion will occur due to sound data being updated + // after it was needed instead of before. + // + KING_EndFrame(v810_timestamp); + + // + // new_base_ts is guaranteed to be <= v810_timestamp + // + v810_timestamp_t new_base_ts; + espec->SoundBufSize = SoundBox_Flush(v810_timestamp, &new_base_ts, espec->SoundBuf, espec->SoundBufMaxSize, false); + + KING_ResetTS(new_base_ts); + FXTIMER_ResetTS(new_base_ts); + FXINPUT_ResetTS(new_base_ts); + SoundBox_ResetTS(new_base_ts); + + // Call this AFTER all the EndFrame/Flush/ResetTS stuff + RebaseTS(v810_timestamp, new_base_ts); + + espec->MasterCycles = v810_timestamp - new_base_ts; + + PCFX_V810.ResetTS(new_base_ts); +} + +static void PCFX_Reset(void) +{ + const uint32 timestamp = PCFX_V810.v810_timestamp; + + //printf("Reset: %d\n", timestamp); + + // Make sure all devices are synched to current timestamp before calling their Reset()/Power()(though devices should already do this sort of thing on their + // own, but it's not implemented for all of them yet, and even if it was all implemented this is also INSURANCE). + ForceEventUpdates(timestamp); + + PCFX_Event_Reset(); + + RAM_LPA = 0; + + ExBusReset = 0; + BackupControl = 0; + + Last_VDC_AR[0] = 0; + Last_VDC_AR[1] = 0; + + memset(RAM, 0x00, 2048 * 1024); + + for (int i = 0; i < 2; i++) + { + int32 dummy_ne MDFN_NOWARN_UNUSED; + + dummy_ne = fx_vdc_chips[i]->Reset(); + } + + KING_Reset(timestamp); // SCSICD_Power() is called from KING_Reset() + SoundBox_Reset(timestamp); + RAINBOW_Reset(); + + if (WantHuC6273) + HuC6273_Reset(); + + PCFXIRQ_Reset(); + FXTIMER_Reset(); + PCFX_V810.Reset(); + + // Force device updates so we can get new next event timestamp values. + ForceEventUpdates(timestamp); +} + +static void PCFX_Power(void) +{ + PCFX_Reset(); +} + +static void VDCA_IRQHook(bool asserted) +{ + PCFXIRQ_Assert(PCFXIRQ_SOURCE_VDCA, asserted); +} + +static void VDCB_IRQHook(bool asserted) +{ + PCFXIRQ_Assert(PCFXIRQ_SOURCE_VDCB, asserted); +} + +static MDFN_COLD void LoadCommon(std::vector *CDInterfaces, const uint8_t *bios) +{ + V810_Emu_Mode cpu_mode; + + cpu_mode = (V810_Emu_Mode)Setting_CpuEmulation; + if (cpu_mode == _V810_EMU_MODE_COUNT) + { + cpu_mode = (EmuFlags & CDGE_FLAG_ACCURATE_V810) ? V810_EMU_MODE_ACCURATE : V810_EMU_MODE_FAST; + } + + if (EmuFlags & CDGE_FLAG_FXGA) + { + //WantHuC6273 = TRUE; + } + + MDFN_printf(_("V810 Emulation Mode: %s\n"), (cpu_mode == V810_EMU_MODE_ACCURATE) ? _("Accurate") : _("Fast")); + PCFX_V810.Init(cpu_mode, false); + + uint32 RAM_Map_Addresses[1] = {0x00000000}; + uint32 BIOSROM_Map_Addresses[1] = {0xFFF00000}; + + RAM = PCFX_V810.SetFastMap(RAM_Map_Addresses, 0x00200000, 1, _("RAM")); + BIOSROM = PCFX_V810.SetFastMap(BIOSROM_Map_Addresses, 0x00100000, 1, _("BIOS ROM")); + + memcpy(BIOSROM, bios, 1024 * 1024); + + /*{ + std::string fxscsi_path = MDFN_GetSettingS("pcfx.fxscsi"); // For developers only, so don't make it convenient. + + if (fxscsi_path != "0" && fxscsi_path != "" && fxscsi_path != "none") + { + FileStream FXSCSIFile(fxscsi_path, FileStream::MODE_READ); + uint32 FXSCSI_Map_Addresses[1] = {0x80780000}; + + FXSCSIROM = PCFX_V810.SetFastMap(FXSCSI_Map_Addresses, 0x0080000, 1, _("FX-SCSI ROM")); + + FXSCSIFile.read(FXSCSIROM, 1024 * 512); + } + }*/ + + for (int i = 0; i < 2; i++) + { + fx_vdc_chips[i] = new VDC(); + fx_vdc_chips[i]->SetUnlimitedSprites(Setting_NoSpriteLimit); + fx_vdc_chips[i]->SetVRAMSize(65536); + fx_vdc_chips[i]->SetWSHook(NULL); + fx_vdc_chips[i]->SetIRQHook(i ? VDCB_IRQHook : VDCA_IRQHook); + + //fx_vdc_chips[0] = FXVDC_Init(PCFXIRQ_SOURCE_VDCA, Setting_NoSpriteLimit); + //fx_vdc_chips[1] = FXVDC_Init(PCFXIRQ_SOURCE_VDCB, Setting_NoSpriteLimit); + } + + SoundBox_Init(Setting_AdpcmBuggy, Setting_AdpcmNoClicks); + RAINBOW_Init(Setting_ChromaInterpolate); + FXINPUT_Init(); + FXTIMER_Init(); + + if (WantHuC6273) + HuC6273_Init(); + + KING_Init(); + + SCSICD_SetDisc(true, NULL, true); + +#ifdef WANT_DEBUGGER + for (unsigned disc = 0; disc < CDInterfaces->size(); disc++) + { + CDUtility::TOC toc; + + (*CDInterfaces)[disc]->ReadTOC(&toc); + + for (int32 track = toc.first_track; track <= toc.last_track; track++) + { + if (toc.tracks[track].control & 0x4) + { + char tmpn[256], tmpln[256]; + uint32 sectors; + + trio_snprintf(tmpn, 256, "track%d-%d-%d", disc, track, toc.tracks[track].lba); + trio_snprintf(tmpln, 256, "CD - Disc %d/%d - Track %d/%d", disc + 1, (int)CDInterfaces->size(), track, toc.last_track - toc.first_track + 1); + + sectors = toc.tracks[(track == toc.last_track) ? 100 : track + 1].lba - toc.tracks[track].lba; + ASpace_Add(PCFXDBG_GetAddressSpaceBytes, PCFXDBG_PutAddressSpaceBytes, tmpn, tmpln, 0, sectors * 2048); + } + } + } +#endif + + // MDFNGameInfo->fps = (uint32)((double)7159090.90909090 / 455 / 263 * 65536 * 256); + + //BackupSignalDirty = false; + //BackupSaveDelay = 0; + + // Initialize backup RAM + memset(BackupRAM, 0, sizeof(BackupRAM)); + memset(ExBackupRAM, 0, sizeof(ExBackupRAM)); + + static const uint8 BRInit00[] = {0x24, 0x8A, 0xDF, 0x50, 0x43, 0x46, 0x58, 0x53, 0x72, 0x61, 0x6D, 0x80, + 0x00, 0x01, 0x01, 0x00, 0x01, 0x40, 0x00, 0x00, 0x01, 0xF9, 0x03, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}; + static const uint8 BRInit80[] = {0xF9, 0xFF, 0xFF}; + + memcpy(BackupRAM + 0x00, BRInit00, sizeof(BRInit00)); + memcpy(BackupRAM + 0x80, BRInit80, sizeof(BRInit80)); + + static const uint8 ExBRInit00[] = {0x24, 0x8A, 0xDF, 0x50, 0x43, 0x46, 0x58, 0x43, 0x61, 0x72, 0x64, 0x80, + 0x00, 0x01, 0x01, 0x00, 0x01, 0x40, 0x00, 0x00, 0x01, 0xF9, 0x03, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}; + static const uint8 ExBRInit80[] = {0xF9, 0xFF, 0xFF}; + + memcpy(ExBackupRAM + 0x00, ExBRInit00, sizeof(ExBRInit00)); + memcpy(ExBackupRAM + 0x80, ExBRInit80, sizeof(ExBRInit80)); + + // Default to 16-bit bus. + for (int i = 0; i < 256; i++) + { + PCFX_V810.SetMemReadBus32(i, FALSE); + PCFX_V810.SetMemWriteBus32(i, FALSE); + } + + // 16MiB RAM area. + PCFX_V810.SetMemReadBus32(0, TRUE); + PCFX_V810.SetMemWriteBus32(0, TRUE); + + // Bitstring read range + for (int i = 0xA0; i <= 0xAF; i++) + { + PCFX_V810.SetMemReadBus32(i, FALSE); // Reads to the read range are 16-bit, and + PCFX_V810.SetMemWriteBus32(i, TRUE); // writes are 32-bit. + } + + // Bitstring write range + for (int i = 0xB0; i <= 0xBF; i++) + { + PCFX_V810.SetMemReadBus32(i, TRUE); // Reads to the write range are 32-bit, + PCFX_V810.SetMemWriteBus32(i, FALSE); // but writes are 16-bit! + } + + // BIOS area + for (int i = 0xF0; i <= 0xFF; i++) + { + PCFX_V810.SetMemReadBus32(i, FALSE); + PCFX_V810.SetMemWriteBus32(i, FALSE); + } + + PCFX_V810.SetMemReadHandlers(mem_rbyte, mem_rhword, mem_rword); + PCFX_V810.SetMemWriteHandlers(mem_wbyte, mem_whword, mem_wword); + + PCFX_V810.SetIOReadHandlers(port_rbyte, port_rhword, NULL); + PCFX_V810.SetIOWriteHandlers(port_wbyte, port_whword, NULL); +} + +static void DoMD5CDVoodoo(std::vector *CDInterfaces) +{ + const CDGameEntry *found_entry = NULL; + CDUtility::TOC toc; + +#if 0 + puts("{"); + puts(" ,"); + puts(" ,"); + puts(" 0,"); + puts(" 1,"); + puts(" {"); + puts(" {"); + + for(int i = CDIF_GetFirstTrack(); i <= CDIF_GetLastTrack(); i++) + { + CDIF_Track_Format tf; + + CDIF_GetTrackFormat(i, tf); + + printf(" { %d, %s, %d },\n", i, (tf == CDIF_FORMAT_AUDIO) ? "CDIF_FORMAT_AUDIO" : "CDIF_FORMAT_MODE1", CDIF_GetTrackStartPositionLBA(i)); + } + printf(" { -1, (CDIF_Track_Format)-1, %d },\n", CDIF_GetSectorCountLBA()); + puts(" }"); + puts(" }"); + puts("},"); + //exit(1); +#endif + + for (unsigned if_disc = 0; if_disc < CDInterfaces->size(); if_disc++) + { + (*CDInterfaces)[if_disc]->ReadTOC(&toc); + + if (toc.first_track == 1) + { + for (unsigned int g = 0; g < sizeof(GameList) / sizeof(CDGameEntry); g++) + { + const CDGameEntry *entry = &GameList[g]; + + assert(entry->discs == 1 || entry->discs == 2); + + for (unsigned int disc = 0; disc < entry->discs; disc++) + { + const CDGameEntryTrack *et = entry->tracks[disc]; + bool GameFound = TRUE; + + while (et->tracknum != -1 && GameFound) + { + assert(et->tracknum > 0 && et->tracknum < 100); + + if (toc.tracks[et->tracknum].lba != et->lba) + GameFound = FALSE; + + if (((et->format == CDGE_FORMAT_DATA) ? 0x4 : 0x0) != (toc.tracks[et->tracknum].control & 0x4)) + GameFound = FALSE; + + et++; + } + + if (et->tracknum == -1) + { + if ((et - 1)->tracknum != toc.last_track) + GameFound = FALSE; + + if (et->lba != toc.tracks[100].lba) + GameFound = FALSE; + } + + if (GameFound) + { + found_entry = entry; + goto FoundIt; + } + } // End disc count loop + } + } + + FoundIt:; + + if (found_entry) + { + EmuFlags = found_entry->flags; + + printf("%s\n", found_entry->name); + printf("%s\n", found_entry->name_original); + break; + } + } // end: for(unsigned if_disc = 0; if_disc < CDInterfaces->size(); if_disc++) +} + +// PC-FX BIOS will look at all data tracks(not just the first one), in contrast to the PCE CD BIOS, which only looks +// at the first data track. +static bool TestMagicCD(std::vector *CDInterfaces) +{ + CDIF *cdiface = (*CDInterfaces)[0]; + CDUtility::TOC toc; + uint8 sector_buffer[2048]; + + memset(sector_buffer, 0, sizeof(sector_buffer)); + + cdiface->ReadTOC(&toc); + + for (int32 track = toc.first_track; track <= toc.last_track; track++) + { + if (toc.tracks[track].control & 0x4) + { + cdiface->ReadSector(sector_buffer, toc.tracks[track].lba, 1); + if (!strncmp("PC-FX:Hu_CD-ROM", (char *)sector_buffer, strlen("PC-FX:Hu_CD-ROM"))) + { + return (TRUE); + } + + if (!strncmp((char *)sector_buffer + 64, "PPPPHHHHOOOOTTTTOOOO____CCCCDDDD", 32)) + return (true); + } + } + return (FALSE); +} + +static MDFN_COLD void LoadCD(std::vector *CDInterfaces, const uint8_t *bios) +{ + EmuFlags = 0; + + cdifs = CDInterfaces; + + DoMD5CDVoodoo(CDInterfaces); + + LoadCommon(CDInterfaces, bios); + + MDFN_printf(_("Emulated CD-ROM drive speed: %ux\n"), (unsigned int)Setting_CdSpeed); + + PCFX_Power(); +} +} + +using namespace MDFN_IEN_PCFX; + +#define EXPORT extern "C" ECL_EXPORT + +struct FrontendTOC +{ + int32 FirstTrack; + int32 LastTrack; + int32 DiskType; + struct + { + int32 Adr; + int32 Control; + int32 Lba; + int32 Valid; + } Tracks[101]; +}; + +static void (*ReadTOCCallback)(int disk, FrontendTOC *dest); +static void (*ReadSector2448Callback)(int disk, int lba, uint8 *dest); + +EXPORT void SetCDCallbacks(void (*toccallback)(int disk, FrontendTOC *dest), void (*sectorcallback)(int disk, int lba, uint8 *dest)) +{ + ReadTOCCallback = toccallback; + ReadSector2448Callback = sectorcallback; +} + +class MyCDIF : public CDIF +{ + private: + int disk; + + public: + MyCDIF(int disk) : disk(disk) + { + FrontendTOC t; + ReadTOCCallback(disk, &t); + disc_toc.first_track = t.FirstTrack; + disc_toc.last_track = t.LastTrack; + disc_toc.disc_type = t.DiskType; + for (int i = 0; i < 101; i++) + { + disc_toc.tracks[i].adr = t.Tracks[i].Adr; + disc_toc.tracks[i].control = t.Tracks[i].Control; + disc_toc.tracks[i].lba = t.Tracks[i].Lba; + disc_toc.tracks[i].valid = t.Tracks[i].Valid; + } + } + + virtual void HintReadSector(int32 lba) {} + virtual bool ReadRawSector(uint8 *buf, int32 lba) + { + ReadSector2448Callback(disk, lba, buf); + return true; + } +}; + +static std::vector CDInterfaces; +static uint32_t InputData[8]; + +struct MyFrameInfo: public FrameInfo +{ + uint32_t Buttons[3]; // port 1, port 2, console +}; +static EmulateSpecStruct Ess; +static int32_t LineWidths[256]; +static uint32_t FrameBuffer[1024 * 512]; + +EXPORT bool Init(int numDisks, const uint8_t *bios) +{ + for (int i = 0; i < numDisks; i++) + CDInterfaces.push_back(new MyCDIF(i)); + + if (!TestMagicCD(&CDInterfaces)) + return false; + + LoadCD(&CDInterfaces, bios); + KING_SetPixelFormat(); + SoundBox_SetSoundRate(44100); + SCSICD_SetDisc(false, CDInterfaces[0]); + // multitap is experimental emulation for a never release peripheral, so let's ignore it for now + FXINPUT_SetMultitap(false, false); + for (int i = 0; i < 2; i++) + FXINPUT_SetInput(i, 1, &InputData[i]); // FXIT_GAMEPAD + + PCFX_Power(); + Ess.pixels = FrameBuffer; + Ess.pitch32 = 1024; + Ess.LineWidths = LineWidths; + Ess.SoundBufMaxSize = 2048; + + return true; +} + +static int ActiveDisk; +static uint32_t PrevConsoleButtons; + +EXPORT void FrameAdvance(MyFrameInfo& f) +{ + for (int i = 0; i < 2; i++) + InputData[i] = f.Buttons[i]; + + uint32_t ConsoleButtons = f.Buttons[3]; + int NewActiveDisk = ActiveDisk; + #define ROSE(n) ((ConsoleButtons & 1 << (n)) > (PrevConsoleButtons & 1 << (n))) + if (ROSE(0)) + PCFX_Power(); + if (ROSE(1)) + PCFX_Reset(); + if (ROSE(2)) + NewActiveDisk++; + if (ROSE(3)) + NewActiveDisk--; + #undef ROSE + NewActiveDisk = std::max(NewActiveDisk, -1); + NewActiveDisk = std::min(NewActiveDisk, CDInterfaces.size() - 1); + if (NewActiveDisk != ActiveDisk) + SCSICD_SetDisc(NewActiveDisk == -1, NewActiveDisk == -1 ? nullptr : CDInterfaces[NewActiveDisk]); + ActiveDisk = NewActiveDisk; + PrevConsoleButtons = ConsoleButtons; + + Ess.SoundBuf = f.SoundBuffer; + Emulate(&Ess); + f.Cycles = Ess.MasterCycles; + f.Width = Ess.w; + f.Height = Ess.h; + f.Samples = Ess.SoundBufSize; + + const uint32_t* src = FrameBuffer; + uint32_t* dst = f.VideoBuffer; + const int srcp = 1024; + const int dstp = Ess.w; + src += Ess.y * srcp + Ess.x; + for (int j = 0; j < Ess.h; j++, src += srcp, dst += dstp) + { + memcpy(dst, src, LineWidths[j + Ess.y] * sizeof(uint32_t)); + } +} + +EXPORT void GetMemoryAreas(MemoryArea *m) +{ + m[0].Data = BackupRAM; + m[0].Name = "Backup RAM"; + m[0].Size = sizeof(BackupRAM); + m[0].Flags = MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_SAVERAMMABLE; + + m[1].Data = ExBackupRAM; + m[1].Name = "Extra Backup RAM"; + m[1].Size = sizeof(ExBackupRAM); + m[1].Flags = MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_SAVERAMMABLE; + + m[2].Data = BIOSROM; + m[2].Name = "BIOS ROM"; + m[2].Size = 1024 * 1024; + m[2].Flags = MEMORYAREA_FLAGS_WORDSIZE4; + + m[3].Data = RAM; + m[3].Name = "Main RAM"; + m[3].Size = 2 * 1024 * 1024; + m[3].Flags = MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_PRIMARY; + + // m[4].Data = FXSCSIROM; + // m[4].Name = "Scsi Rom"; + // m[4].Size = 512 * 1024; + // m[4].Flags = MEMORYAREA_FLAGS_WORDSIZE4; + + for (int i = 0; i < 2; i++) + { + m[i + 5].Data = fx_vdc_chips[i]->GetVramPointer(); + m[i + 5].Name = i == 0 ? "VDC A VRAM" : "VDC B VRAM"; + m[i + 5].Size = fx_vdc_chips[i]->GetVramByteSize(); + m[i + 5].Flags = MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_WORDSIZE2; + } +} + +EXPORT void EnableLayers(int mask) +{ + // BG0 = 0 + // BG1 + // BG2 + // BG3 + // VDC-A BG + // VDC-A SPR + // VDC-B BG + // VDC-B SPR + // RAINBOW + KING_SetLayerEnableMask(mask); +} + +EXPORT void SetInputCallback(void (*callback)()) +{ + // TODO +} + +// settings +int Setting_HighDotclockWidth = 341; +int Setting_CdSpeed = 2; +int Setting_SlStart = 4; +int Setting_SlEnd = 235; + +double Setting_ResampRateError = 0.0000009; +int Setting_ResampQuality = 3; + +int Setting_CpuEmulation = 2; // 0 = fast, 1 = accurate, 2 = auto +bool Setting_NoSpriteLimit; +bool Setting_AdpcmBuggy = false; +bool Setting_AdpcmNoClicks = true; +bool Setting_ChromaInterpolate = false; + + +/*MDFNGI EmulatedPCFX = + { + FXINPUT_SetInput, + SetMedia, + DoSimpleCommand, + NULL, + PCFXSettings, + MDFN_MASTERCLOCK_FIXED(PCFX_MASTER_CLOCK), + 0, + TRUE, // Multires possible? + + 288, // Nominal width + 240, // Nominal height + + 1024, // Framebuffer width + 512, // Framebuffer height + +};*/ + +int main() +{ + return 0; +} diff --git a/waterbox/pcfx/pcfx.h b/waterbox/pcfx/pcfx.h new file mode 100644 index 0000000000..4a67c84313 --- /dev/null +++ b/waterbox/pcfx/pcfx.h @@ -0,0 +1,67 @@ +/******************************************************************************/ +/* Mednafen NEC PC-FX Emulation Module */ +/******************************************************************************/ +/* pcfx.h: +** Copyright (C) 2006-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef __PCFX_PCFX_H +#define __PCFX_PCFX_H + +#include "defs.h" + +#include "v810/v810_cpu.h" +#include "huc6270/vdc.h" + +namespace MDFN_IEN_PCFX +{ + +#define PCFX_MASTER_CLOCK 21477272.72 + +#if 0 + #define FXDBG(format, ...) MDFN_DebugPrint(format, ## __VA_ARGS__) +#else + #define FXDBG(format, ...) ((void)0) +#endif + +extern V810 PCFX_V810; + +uint8 MDFN_FASTCALL mem_peekbyte(const v810_timestamp_t timestamp, const uint32 A); +uint16 MDFN_FASTCALL mem_peekhword(const v810_timestamp_t timestamp, const uint32 A); + +int32 MDFN_FASTCALL pcfx_event_handler(const v810_timestamp_t timestamp); + +extern VDC *fx_vdc_chips[2]; + +#define REGSETHW(_reg, _data, _msh) { _reg &= 0xFFFF << (_msh ? 0 : 16); _reg |= _data << (_msh ? 16 : 0); } +#define REGGETHW(_reg, _msh) ((_reg >> (_msh ? 16 : 0)) & 0xFFFF) + +enum +{ + PCFX_EVENT_PAD = 0, + PCFX_EVENT_TIMER, + PCFX_EVENT_KING, + PCFX_EVENT_ADPCM +}; + +#define PCFX_EVENT_NONONO 0x7fffffff + +void PCFX_SetEvent(const int type, const v810_timestamp_t next_timestamp); + +} + +#endif diff --git a/waterbox/pcfx/rainbow.cpp b/waterbox/pcfx/rainbow.cpp new file mode 100644 index 0000000000..5a887d0ecf --- /dev/null +++ b/waterbox/pcfx/rainbow.cpp @@ -0,0 +1,819 @@ +/******************************************************************************/ +/* Mednafen NEC PC-FX Emulation Module */ +/******************************************************************************/ +/* rainbow.cpp: +** Copyright (C) 2006-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/* +** MJPEG-like decoder based on the algorithm and Huffman data tables provided by David Michel +** of MagicEngine and MagicEngine-FX. +*/ + +#include "pcfx.h" +#include "rainbow.h" +#include "king.h" +#include "interrupt.h" +#include "jrevdct.h" + +namespace MDFN_IEN_PCFX +{ + +static bool ChromaIP; // Bilinearly interpolate chroma channel + +/* Y = luminance/luma, UV = chrominance/chroma */ + +struct HuffmanQuickLUTPair +{ + uint8 val; + uint8 bitc; // Bit count for the code. +}; + +static const uint8 zigzag[63] = + { + 0x01, 0x08, 0x10, 0x09, 0x02, 0x03, 0x0A, 0x11, + 0x18, 0x20, 0x19, 0x12, 0x0B, 0x04, 0x05, 0x0C, + 0x13, 0x1A, 0x21, 0x28, 0x30, 0x29, 0x22, 0x1B, + 0x14, 0x0D, 0x06, 0x07, 0x0E, 0x15, 0x1C, 0x23, + 0x2A, 0x31, 0x38, 0x39, 0x32, 0x2B, 0x24, 0x1D, + 0x16, 0x0F, 0x17, 0x1E, 0x25, 0x2C, 0x33, 0x3A, + 0x3B, 0x34, 0x2D, 0x26, 0x1F, 0x27, 0x2E, 0x35, + 0x3C, 0x3D, 0x36, 0x2F, 0x37, 0x3E, 0x3F}; + +static const HuffmanQuickLUTPair ac_y_qlut[1 << 12] = + { +#include "rainbow_acy.inc" +}; + +static const HuffmanQuickLUTPair ac_uv_qlut[1 << 12] = + { +#include "rainbow_acuv.inc" +}; + +static const HuffmanQuickLUTPair dc_y_qlut[1 << 9] = + { +#include "rainbow_dcy.inc" +}; + +static const HuffmanQuickLUTPair dc_uv_qlut[1 << 8] = + { +#include "rainbow_dcuv.inc" +}; + +static uint8 *DecodeBuffer[2] = {NULL, NULL}; +static int32 DecodeFormat[2]; // The format each buffer is in(-1 = invalid, 0 = palettized 8-bit, 1 = YUV) +static uint32 QuantTables[2][64], QuantTablesBase[2][64]; // 0 = Y, 1 = UV + +static uint32 DecodeBufferWhichRead; + +static int32 RasterReadPos; +static uint16 Control; +static uint16 NullRunY, NullRunU, NullRunV, HSync; +static uint16 HScroll; + +static uint32 bits_buffer; +static uint32 bits_buffered_bits; +static int32 bits_bytes_left; + +static void InitBits(int32 bcount) +{ + bits_bytes_left = bcount; + bits_buffer = 0; + bits_buffered_bits = 0; +} + +static INLINE uint8 FetchWidgywabbit(void) +{ + if (bits_bytes_left <= 0) + return (0); + + uint8 ret = KING_RB_Fetch(); + if (ret == 0xFF) + KING_RB_Fetch(); + + bits_bytes_left--; + + return (ret); +} + +enum +{ + MDFNBITS_PEEK = 1, + MDFNBITS_FUNNYSIGN = 2, +}; + +static INLINE uint32 GetBits(const unsigned int count, const unsigned int how = 0) +{ + uint32 ret; + + while (bits_buffered_bits < count) + { + bits_buffer <<= 8; + bits_buffer |= FetchWidgywabbit(); + bits_buffered_bits += 8; + } + + ret = (bits_buffer >> (bits_buffered_bits - count)) & ((1 << count) - 1); + + if (!(how & MDFNBITS_PEEK)) + bits_buffered_bits -= count; + + if ((how & MDFNBITS_FUNNYSIGN) && count) + { + if (ret < (1U << (count - 1))) + ret += 1 - (1 << count); + } + + return (ret); +} + +// Note: SkipBits is intended to be called right after GetBits() with "how" MDFNBITS_PEEK, +// and the count pass to SkipBits must be less than or equal to the count passed to GetBits(). +static INLINE void SkipBits(const unsigned int count) +{ + bits_buffered_bits -= count; +} + +static uint32 HappyColor; // Cached, calculated from null run yuv registers; +static void CalcHappyColor(void) +{ + uint8 y_c = (int16)NullRunY + 0x80; + uint8 u_c = (int16)NullRunU + 0x80; + uint8 v_c = (int16)NullRunV + 0x80; + + HappyColor = (y_c << 16) | (u_c << 8) | (v_c << 0); +} + +static uint32 get_ac_coeff(const HuffmanQuickLUTPair *table, int32 *zeroes) +{ + unsigned int numbits; + uint32 rawbits; + uint32 code; + + rawbits = GetBits(12, MDFNBITS_PEEK); + code = table[rawbits].val; + SkipBits(table[rawbits].bitc); + + numbits = code & 0xF; + *zeroes = code >> 4; + + return (GetBits(numbits, MDFNBITS_FUNNYSIGN)); +} + +static uint32 get_dc_coeff(const HuffmanQuickLUTPair *table, int32 *zeroes, int maxbits) +{ + uint32 code; + + for (;;) + { + uint32 rawbits; + + rawbits = GetBits(maxbits, MDFNBITS_PEEK); + code = table[rawbits].val; + SkipBits(table[rawbits].bitc); + + if (code < 0xF) + { + *zeroes = 0; + return (GetBits(code, MDFNBITS_FUNNYSIGN)); + } + else if (code == 0xF) + { + get_ac_coeff(ac_y_qlut, zeroes); + (*zeroes)++; + return (0); + } + else if (code >= 0x10) + { + code -= 0x10; + + for (int i = 0; i < 64; i++) + { + // Y + uint32 coeff = (QuantTablesBase[0][i] * code) >> 2; + + if (coeff < 1) + coeff = 1; + else if (coeff > 0xFE) + coeff = 0xFE; + + QuantTables[0][i] = coeff; + + // UV + if (i) + coeff = (QuantTablesBase[1][i] * code) >> 2; + else + coeff = (QuantTablesBase[1][i]) >> 2; + + if (coeff < 1) + coeff = 1; + else if (coeff > 0xFE) + coeff = 0xFE; + + QuantTables[1][i] = coeff; + } + } + } +} + +static INLINE uint32 get_dc_y_coeff(int32 *zeroes) +{ + return (get_dc_coeff(dc_y_qlut, zeroes, 9)); +} + +static uint32 get_dc_uv_coeff(void) +{ + const HuffmanQuickLUTPair *table = dc_uv_qlut; + uint32 code; + uint32 rawbits = GetBits(8, MDFNBITS_PEEK); + + code = table[rawbits].val; + SkipBits(table[rawbits].bitc); + + return (GetBits(code, MDFNBITS_FUNNYSIGN)); +} + +static void decode(int32 *dct, const uint32 *QuantTable, const int32 dc, const HuffmanQuickLUTPair *table) +{ + int32 coeff; + int zeroes; + int count; + int index; + + dct[0] = (int16)(QuantTable[0] * dc); + count = 0; + + do + { + coeff = get_ac_coeff(table, &zeroes); + if (!coeff) + { + if (!zeroes) + { + while (count < 63) + { + dct[zigzag[count++]] = 0; + } + break; + } + else if (zeroes == 1) + zeroes = 0xF; + } + + while (zeroes-- && count < 63) + { + dct[zigzag[count++]] = 0; + } + zeroes = 0; + if (count < 63) + { + index = zigzag[count++]; + dct[index] = (int16)(QuantTable[index] * coeff); + } + } while (count < 63); +} + +#ifdef WANT_DEBUGGER +uint32 RAINBOW_GetRegister(const unsigned int id, char *special, const uint32 special_len) +{ + uint32 value = 0xDEADBEEF; + + if (id == RAINBOW_GSREG_RSCRLL) + value = HScroll; + else if (id == RAINBOW_GSREG_RCTRL) + value = Control; + else if (id == RAINBOW_GSREG_RNRY) + value = NullRunY; + else if (id == RAINBOW_GSREG_RNRU) + value = NullRunU; + else if (id == RAINBOW_GSREG_RNRV) + value = NullRunV; + else if (id == RAINBOW_GSREG_RHSYNC) + value = HSync; + + return (value); +} + +void RAINBOW_SetRegister(const unsigned int id, uint32 value) +{ + if (id == RAINBOW_GSREG_RSCRLL) + HScroll = value & 0x1FF; + else if (id == RAINBOW_GSREG_RCTRL) + Control = value; + else if (id == RAINBOW_GSREG_RNRY) + NullRunY = value; + else if (id == RAINBOW_GSREG_RNRU) + NullRunU = value; + else if (id == RAINBOW_GSREG_RNRV) + NullRunV = value; +} +#endif + +static uint32 LastLine[256]; +static bool FirstDecode; +static bool GarbageData; + +void RAINBOW_Init(bool arg_ChromaIP) +{ + ChromaIP = arg_ChromaIP; + + for (int i = 0; i < 2; i++) + { + DecodeBuffer[i] = new uint8[0x2000 * 4]; + memset(DecodeBuffer[i], 0, 0x2000 * 4); + } + + DecodeFormat[0] = DecodeFormat[1] = -1; + DecodeBufferWhichRead = 0; + GarbageData = FALSE; + FirstDecode = TRUE; + RasterReadPos = 0; +} + +// RAINBOW base I/O port address: 0x200 + +#define REGSETBOFW(_reg, _data, _wb) \ + { \ + (_reg) &= ~(0xFF << ((_wb)*8)); \ + (_reg) |= (_data) << ((_wb)*8); \ + } +#define REGSETBOFH REGSETBOFW + +// The horizontal scroll register is set up kind of weird...D0-D7 of writes to $200 set the lower byte, D0-D7 of writes to $202 set the upper byte + +void RAINBOW_Write8(uint32 A, uint8 V) +{ + //printf("RAINBOW Wr8: %08x %02x\n", A, V); + switch (A & 0x1C) + { + case 0x00: + REGSETBOFH(HScroll, V, (A & 0x2) >> 1); + HScroll &= 0x1FF; + break; + case 0x04: + REGSETBOFH(Control, V, A & 0x3); + break; + case 0x08: + REGSETBOFH(NullRunY, V, A & 0x3); + CalcHappyColor(); + break; + case 0x0C: + REGSETBOFH(NullRunU, V, A & 0x3); + CalcHappyColor(); + break; + case 0x10: + REGSETBOFH(NullRunV, V, A & 0x3); + CalcHappyColor(); + break; + case 0x14: + REGSETBOFH(HSync, V, A & 0x3); + break; + } +} + +void RAINBOW_Write16(uint32 A, uint16 V) +{ + int msh = A & 0x2; + + //printf("RAINBOW Wr16: %08x %04x\n", A, V); + switch (A & 0x1C) + { + case 0x00: + REGSETBOFH(HScroll, V & 0xFF, (A & 0x2) >> 1); + HScroll &= 0x1FF; + break; + case 0x04: + REGSETHW(Control, V, msh); + break; + case 0x08: + REGSETHW(NullRunY, V, msh); + CalcHappyColor(); + break; + case 0x0C: + REGSETHW(NullRunU, V, msh); + CalcHappyColor(); + break; + case 0x10: + REGSETHW(NullRunV, V, msh); + CalcHappyColor(); + break; + case 0x14: + REGSETHW(HSync, V, msh); + break; + } +} + +void RAINBOW_ForceTransferReset(void) +{ + RasterReadPos = 0; + DecodeFormat[0] = DecodeFormat[1] = -1; +} + +void RAINBOW_SwapBuffers(void) +{ + DecodeBufferWhichRead ^= 1; + RasterReadPos = 0; +} + +void RAINBOW_DecodeBlock(bool arg_FirstDecode, bool Skip) +{ + uint8 block_type; + int32 block_size; + int icount; + int which_buffer = DecodeBufferWhichRead ^ 1; + + if (!(Control & 0x01)) + { + puts("Rainbow decode when disabled!!"); + return; + } + + if (arg_FirstDecode) + { + FirstDecode = TRUE; + GarbageData = FALSE; + } + + if (GarbageData) + icount = 0; + else + icount = 0x200; + + do + { + do + { + while (KING_RB_Fetch() != 0xFF && icount > 0) + icount--; + + block_type = KING_RB_Fetch(); + //if(icount > 0 && block_type != 0xF0 && block_type != 0xF1 && block_type != 0xF2 && block_type != 0xF3 && block_type != 0xF8 && block_type != 0xFF) + //if(icount > 0 && block_type == 0x11) + // printf("%02x\n", block_type); + icount--; + } while (block_type != 0xF0 && block_type != 0xF1 && block_type != 0xF2 && block_type != 0xF3 && block_type != 0xF8 && block_type != 0xFF && icount > 0); + + { + uint16 tmp; + + tmp = KING_RB_Fetch() << 8; + tmp |= KING_RB_Fetch() << 0; + + block_size = (int16)tmp; + } + + block_size -= 2; + if (block_type == 0xFF && block_size <= 0) + for (int i = 0; i < 128; i++, icount--) + KING_RB_Fetch(); + + //fprintf(stderr, "Block: %d\n", block_size); + } while (block_size <= 0 && icount > 0); + + //if(!GarbageData && icount < 500) + //{ + // FXDBG("Partial garbage data. %d", icount); + //} + + //printf("%d\n", icount); + if (icount <= 0) + { + FXDBG("Garbage data."); + GarbageData = TRUE; + //printf("Dooom: %d\n"); + DecodeFormat[which_buffer] = 0; + memset(DecodeBuffer[which_buffer], 0, 0x2000); + goto BufferNoDecode; + } + + if (block_type == 0xf8 || block_type == 0xff) + DecodeFormat[which_buffer] = 1; + else + DecodeFormat[which_buffer] = 0; + + if (block_type == 0xF8 || block_type == 0xFF) + { + if (block_type == 0xFF) + { + for (int q = 0; q < 2; q++) + for (int i = 0; i < 64; i++) + { + uint8 meow = KING_RB_Fetch(); + + QuantTables[q][i] = meow; + QuantTablesBase[q][i] = meow; + } + block_size -= 128; + } + + InitBits(block_size); + + int32 dc_y = 0, dc_u = 0, dc_v = 0; + uint32 *dest_base = (uint32 *)DecodeBuffer[which_buffer]; + for (int column = 0; column < 16; column++) + { + uint32 *dest_base_column = &dest_base[column * 16]; + int zeroes = 0; + + dc_y += get_dc_y_coeff(&zeroes); + + if (zeroes) // If set, clear the number of columns + { + do + { + if (column < 16) + { + dest_base_column = &dest_base[column * 16]; + + for (int y = 0; y < 16; y++) + for (int x = 0; x < 16; x++) + dest_base_column[y * 256 + x] = HappyColor; + } + column++; + zeroes--; + } while (zeroes); + column--; // Fix for the column autoincrement in the while(zeroes) loop + dc_y = dc_u = dc_v = 0; + } + else + { + int32 dct_y[256]; + int32 dct_u[64]; + int32 dct_v[64]; + + // Y/Luma, 16x16 components + // --------- + // | A | C | + // |-------| + // | B | D | + // --------- + // A (0, 0) + decode(&dct_y[0x00], QuantTables[0], dc_y, ac_y_qlut); + + // B (0, 1) + dc_y += get_dc_y_coeff(&zeroes); + decode(&dct_y[0x40], QuantTables[0], dc_y, ac_y_qlut); + + // C (1, 0) + dc_y += get_dc_y_coeff(&zeroes); + decode(&dct_y[0x80], QuantTables[0], dc_y, ac_y_qlut); + + // D (1, 1) + dc_y += get_dc_y_coeff(&zeroes); + decode(&dct_y[0xC0], QuantTables[0], dc_y, ac_y_qlut); + + // U, 8x8 components + dc_u += get_dc_uv_coeff(); + decode(&dct_u[0x00], QuantTables[1], dc_u, ac_uv_qlut); + + // V, 8x8 components + dc_v += get_dc_uv_coeff(); + decode(&dct_v[0x00], QuantTables[1], dc_v, ac_uv_qlut); + + if (Skip) + continue; + + j_rev_dct(&dct_y[0x00]); + j_rev_dct(&dct_y[0x40]); + j_rev_dct(&dct_y[0x80]); + j_rev_dct(&dct_y[0xC0]); + j_rev_dct(&dct_u[0x00]); + j_rev_dct(&dct_v[0x00]); + + for (int y = 0; y < 16; y++) + for (int x = 0; x < 16; x++) + dest_base_column[y * 256 + x] = clamp_to_u8(dct_y[y * 8 + (x & 0x7) + ((x & 0x8) << 4)] + 0x80) << 16; + + if (!ChromaIP) + { + for (int y = 0; y < 8; y++) + { + for (int x = 0; x < 8; x++) + { + uint32 component_uv = (clamp_to_u8(dct_u[y * 8 + x] + 0x80) << 8) | clamp_to_u8(dct_v[y * 8 + x] + 0x80); + dest_base_column[y * 512 + (256 * 0) + x * 2 + 0] |= component_uv; + dest_base_column[y * 512 + (256 * 0) + x * 2 + 1] |= component_uv; + dest_base_column[y * 512 + (256 * 1) + x * 2 + 0] |= component_uv; + dest_base_column[y * 512 + (256 * 1) + x * 2 + 1] |= component_uv; + } + } + } + else + { + for (int y = 0; y < 8; y++) + { + for (int x = 0; x < 8; x++) + { + uint32 component_uv = (clamp_to_u8(dct_u[y * 8 + x] + 0x80) << 8) | clamp_to_u8(dct_v[y * 8 + x] + 0x80); + dest_base_column[y * 512 + (256 * 1) + x * 2 + 0] |= component_uv; + } + } + } + } + } + + // Do bilinear interpolation on the chroma channels: + if (!Skip && ChromaIP) + { + for (int y = 0; y < 16; y += 2) + { + uint32 *linebase = &dest_base[y * 256]; + uint32 *linebase1 = &dest_base[(y + 1) * 256]; + + for (int x = 0; x < 254; x += 2) + { + unsigned int u, v; + + u = (((linebase1[x] >> 8) & 0xFF) + ((linebase1[x + 2] >> 8) & 0xFF)) >> 1; + v = (((linebase1[x] >> 0) & 0xFF) + ((linebase1[x + 2] >> 0) & 0xFF)) >> 1; + + linebase1[x + 1] = (linebase1[x + 1] & ~0xFFFF) | (u << 8) | v; + } + + linebase1[0xFF] = (linebase1[0xFF] & ~0xFFFF) | (linebase1[0xFE] & 0xFFFF); + + if (FirstDecode) + { + for (int x = 0; x < 256; x++) + linebase[x] = (linebase[x] & ~0xFFFF) | (linebase1[x] & 0xFFFF); + FirstDecode = 0; + } + else + for (int x = 0; x < 256; x++) + { + unsigned int u, v; + + u = (((LastLine[x] >> 8) & 0xFF) + ((linebase1[x] >> 8) & 0xFF)) >> 1; + v = (((LastLine[x] >> 0) & 0xFF) + ((linebase1[x] >> 0) & 0xFF)) >> 1; + + linebase[x] = (linebase[x] & ~0xFFFF) | (u << 8) | v; + } + + memcpy(LastLine, linebase1, 256 * 4); + } + } // End chroma interpolation + } // end jpeg-like decoding + else + { + // Earlier code confines the set to F0,F1,F2, and F3. + // F0 = (4, 0xF), F1 = (3, 0x7), F2 = (0x2, 0x3), F3 = 0x1, 0x1) + const unsigned int plt_shift = 4 - (block_type & 0x3); + const unsigned int crl_mask = (1 << plt_shift) - 1; + int x = 0; + + while (block_size > 0) + { + uint8 boot; + unsigned int rle_count; + + boot = KING_RB_Fetch(); + block_size--; + + if (boot == 0xFF) + { + KING_RB_Fetch(); + block_size--; + } + + if (!(boot & crl_mask)) // Expand mode? + { + rle_count = KING_RB_Fetch(); + block_size--; + if (rle_count == 0xFF) + { + KING_RB_Fetch(); + block_size--; + } + rle_count++; + } + else + rle_count = boot & crl_mask; + + for (unsigned int i = 0; i < rle_count; i++) + { + if (x >= 0x2000) + { + //puts("Oops"); + break; // Don't overflow our decode buffer! + } + DecodeBuffer[which_buffer][x] = (boot >> plt_shift); + x++; + } + } + } // end RLE decoding + +//for(int i = 0; i < 8 + block_size; i++) +// KING_RB_Fetch(); + +BufferNoDecode:; +} + +void KING_Moo(void); + +// NOTE: layer_or and palette_ptr are optimizations, the real RAINBOW chip knows not of such things. +int RAINBOW_FetchRaster(uint32 *linebuffer, uint32 layer_or, uint32 *palette_ptr) +{ + int ret; + + ret = DecodeFormat[DecodeBufferWhichRead]; + + if (linebuffer) + { + linebuffer = MDFN_ASSUME_ALIGNED(linebuffer, 8); + // + if (DecodeFormat[DecodeBufferWhichRead] == -1) // None + { + MDFN_FastArraySet(linebuffer, 0, 256); + } + else if (DecodeFormat[DecodeBufferWhichRead] == 1) // YUV + { + uint32 *in_ptr = (uint32 *)&DecodeBuffer[DecodeBufferWhichRead][RasterReadPos * 256 * 4]; + + if (Control & 0x2) // Endless scroll mode: + { + uint8 tmpss = HScroll; + + for (int x = 0; x < 256; x++) + { + linebuffer[x] = in_ptr[tmpss] | layer_or; + tmpss++; + } + } + else // Non-endless + { + uint16 tmpss = HScroll & 0x1FF; + + for (int x = 0; x < 256; x++) + { + linebuffer[x] = (tmpss < 256) ? (in_ptr[tmpss] | layer_or) : 0; + tmpss = (tmpss + 1) & 0x1FF; + } + } + MDFN_FastArraySet(in_ptr, 0, 256); + } + else if (DecodeFormat[DecodeBufferWhichRead] == 0) // Palette + { + uint8 *in_ptr = &DecodeBuffer[DecodeBufferWhichRead][RasterReadPos * 256]; + + if (Control & 0x2) // Endless scroll mode: + { + uint8 tmpss = HScroll; + + for (int x = 0; x < 256; x++) + { + linebuffer[x] = in_ptr[tmpss] ? (palette_ptr[in_ptr[tmpss]] | layer_or) : 0; + tmpss++; + } + } + else // Non-endless + { + uint16 tmpss = HScroll & 0x1FF; + + for (int x = 0; x < 256; x++) + { + linebuffer[x] = (tmpss < 256 && in_ptr[tmpss]) ? (palette_ptr[in_ptr[tmpss]] | layer_or) : 0; + tmpss = (tmpss + 1) & 0x1FF; + } + } + //MDFN_FastU32MemsetM8((uint32 *)in_ptr, 0, 256 / 4); + } + } + + RasterReadPos = (RasterReadPos + 1) & 0xF; + + if (!RasterReadPos) + DecodeFormat[DecodeBufferWhichRead] = -1; // Invalidate this buffer. + + //printf("Fetch: %d, buffer: %d\n", RasterReadPos, DecodeBufferWhichRead); + return (ret); +} + +void RAINBOW_Reset(void) +{ + Control = 0; + NullRunY = NullRunU = NullRunV = 0; + HScroll = 0; + RasterReadPos = 0; + DecodeBufferWhichRead = 0; + + memset(QuantTables, 0, sizeof(QuantTables)); + memset(QuantTablesBase, 0, sizeof(QuantTablesBase)); + DecodeFormat[0] = DecodeFormat[1] = -1; + + CalcHappyColor(); +} +} diff --git a/waterbox/pcfx/rainbow.h b/waterbox/pcfx/rainbow.h new file mode 100644 index 0000000000..64949a7e45 --- /dev/null +++ b/waterbox/pcfx/rainbow.h @@ -0,0 +1,56 @@ +/******************************************************************************/ +/* Mednafen NEC PC-FX Emulation Module */ +/******************************************************************************/ +/* rainbow.h: +** Copyright (C) 2006-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef __PCFX_RAINBOW_H +#define __PCFX_RAINBOW_H + +namespace MDFN_IEN_PCFX +{ + +void RAINBOW_Write8(uint32 A, uint8 V); +void RAINBOW_Write16(uint32 A, uint16 V); + +void RAINBOW_ForceTransferReset(void); +void RAINBOW_SwapBuffers(void); +void RAINBOW_DecodeBlock(bool arg_FirstDecode, bool Skip); + +int RAINBOW_FetchRaster(uint32 *, uint32 layer_or, uint32 *palette_ptr); + +void RAINBOW_Init(bool arg_ChromaIP) MDFN_COLD; +void RAINBOW_Reset(void) MDFN_COLD; + +#ifdef WANT_DEBUGGER +enum +{ + RAINBOW_GSREG_RSCRLL, + RAINBOW_GSREG_RCTRL, + RAINBOW_GSREG_RNRY, + RAINBOW_GSREG_RNRU, + RAINBOW_GSREG_RNRV, + RAINBOW_GSREG_RHSYNC, +}; +uint32 RAINBOW_GetRegister(const unsigned int id, char* special, const uint32 special_len); +void RAINBOW_SetRegister(const unsigned int id, uint32 value); +#endif + +} + +#endif diff --git a/waterbox/pcfx/rainbow_acuv.inc b/waterbox/pcfx/rainbow_acuv.inc new file mode 100644 index 0000000000..b3e29391e7 --- /dev/null +++ b/waterbox/pcfx/rainbow_acuv.inc @@ -0,0 +1,4096 @@ + /* 000 */ { 0x01, 0x02 }, + /* 001 */ { 0x01, 0x02 }, + /* 002 */ { 0x01, 0x02 }, + /* 003 */ { 0x01, 0x02 }, + /* 004 */ { 0x01, 0x02 }, + /* 005 */ { 0x01, 0x02 }, + /* 006 */ { 0x01, 0x02 }, + /* 007 */ { 0x01, 0x02 }, + /* 008 */ { 0x01, 0x02 }, + /* 009 */ { 0x01, 0x02 }, + /* 00a */ { 0x01, 0x02 }, + /* 00b */ { 0x01, 0x02 }, + /* 00c */ { 0x01, 0x02 }, + /* 00d */ { 0x01, 0x02 }, + /* 00e */ { 0x01, 0x02 }, + /* 00f */ { 0x01, 0x02 }, + /* 010 */ { 0x01, 0x02 }, + /* 011 */ { 0x01, 0x02 }, + /* 012 */ { 0x01, 0x02 }, + /* 013 */ { 0x01, 0x02 }, + /* 014 */ { 0x01, 0x02 }, + /* 015 */ { 0x01, 0x02 }, + /* 016 */ { 0x01, 0x02 }, + /* 017 */ { 0x01, 0x02 }, + /* 018 */ { 0x01, 0x02 }, + /* 019 */ { 0x01, 0x02 }, + /* 01a */ { 0x01, 0x02 }, + /* 01b */ { 0x01, 0x02 }, + /* 01c */ { 0x01, 0x02 }, + /* 01d */ { 0x01, 0x02 }, + /* 01e */ { 0x01, 0x02 }, + /* 01f */ { 0x01, 0x02 }, + /* 020 */ { 0x01, 0x02 }, + /* 021 */ { 0x01, 0x02 }, + /* 022 */ { 0x01, 0x02 }, + /* 023 */ { 0x01, 0x02 }, + /* 024 */ { 0x01, 0x02 }, + /* 025 */ { 0x01, 0x02 }, + /* 026 */ { 0x01, 0x02 }, + /* 027 */ { 0x01, 0x02 }, + /* 028 */ { 0x01, 0x02 }, + /* 029 */ { 0x01, 0x02 }, + /* 02a */ { 0x01, 0x02 }, + /* 02b */ { 0x01, 0x02 }, + /* 02c */ { 0x01, 0x02 }, + /* 02d */ { 0x01, 0x02 }, + /* 02e */ { 0x01, 0x02 }, + /* 02f */ { 0x01, 0x02 }, + /* 030 */ { 0x01, 0x02 }, + /* 031 */ { 0x01, 0x02 }, + /* 032 */ { 0x01, 0x02 }, + /* 033 */ { 0x01, 0x02 }, + /* 034 */ { 0x01, 0x02 }, + /* 035 */ { 0x01, 0x02 }, + /* 036 */ { 0x01, 0x02 }, + /* 037 */ { 0x01, 0x02 }, + /* 038 */ { 0x01, 0x02 }, + /* 039 */ { 0x01, 0x02 }, + /* 03a */ { 0x01, 0x02 }, + /* 03b */ { 0x01, 0x02 }, + /* 03c */ { 0x01, 0x02 }, + /* 03d */ { 0x01, 0x02 }, + /* 03e */ { 0x01, 0x02 }, + /* 03f */ { 0x01, 0x02 }, + /* 040 */ { 0x01, 0x02 }, + /* 041 */ { 0x01, 0x02 }, + /* 042 */ { 0x01, 0x02 }, + /* 043 */ { 0x01, 0x02 }, + /* 044 */ { 0x01, 0x02 }, + /* 045 */ { 0x01, 0x02 }, + /* 046 */ { 0x01, 0x02 }, + /* 047 */ { 0x01, 0x02 }, + /* 048 */ { 0x01, 0x02 }, + /* 049 */ { 0x01, 0x02 }, + /* 04a */ { 0x01, 0x02 }, + /* 04b */ { 0x01, 0x02 }, + /* 04c */ { 0x01, 0x02 }, + /* 04d */ { 0x01, 0x02 }, + /* 04e */ { 0x01, 0x02 }, + /* 04f */ { 0x01, 0x02 }, + /* 050 */ { 0x01, 0x02 }, + /* 051 */ { 0x01, 0x02 }, + /* 052 */ { 0x01, 0x02 }, + /* 053 */ { 0x01, 0x02 }, + /* 054 */ { 0x01, 0x02 }, + /* 055 */ { 0x01, 0x02 }, + /* 056 */ { 0x01, 0x02 }, + /* 057 */ { 0x01, 0x02 }, + /* 058 */ { 0x01, 0x02 }, + /* 059 */ { 0x01, 0x02 }, + /* 05a */ { 0x01, 0x02 }, + /* 05b */ { 0x01, 0x02 }, + /* 05c */ { 0x01, 0x02 }, + /* 05d */ { 0x01, 0x02 }, + /* 05e */ { 0x01, 0x02 }, + /* 05f */ { 0x01, 0x02 }, + /* 060 */ { 0x01, 0x02 }, + /* 061 */ { 0x01, 0x02 }, + /* 062 */ { 0x01, 0x02 }, + /* 063 */ { 0x01, 0x02 }, + /* 064 */ { 0x01, 0x02 }, + /* 065 */ { 0x01, 0x02 }, + /* 066 */ { 0x01, 0x02 }, + /* 067 */ { 0x01, 0x02 }, + /* 068 */ { 0x01, 0x02 }, + /* 069 */ { 0x01, 0x02 }, + /* 06a */ { 0x01, 0x02 }, + /* 06b */ { 0x01, 0x02 }, + /* 06c */ { 0x01, 0x02 }, + /* 06d */ { 0x01, 0x02 }, + /* 06e */ { 0x01, 0x02 }, + /* 06f */ { 0x01, 0x02 }, + /* 070 */ { 0x01, 0x02 }, + /* 071 */ { 0x01, 0x02 }, + /* 072 */ { 0x01, 0x02 }, + /* 073 */ { 0x01, 0x02 }, + /* 074 */ { 0x01, 0x02 }, + /* 075 */ { 0x01, 0x02 }, + /* 076 */ { 0x01, 0x02 }, + /* 077 */ { 0x01, 0x02 }, + /* 078 */ { 0x01, 0x02 }, + /* 079 */ { 0x01, 0x02 }, + /* 07a */ { 0x01, 0x02 }, + /* 07b */ { 0x01, 0x02 }, + /* 07c */ { 0x01, 0x02 }, + /* 07d */ { 0x01, 0x02 }, + /* 07e */ { 0x01, 0x02 }, + /* 07f */ { 0x01, 0x02 }, + /* 080 */ { 0x01, 0x02 }, + /* 081 */ { 0x01, 0x02 }, + /* 082 */ { 0x01, 0x02 }, + /* 083 */ { 0x01, 0x02 }, + /* 084 */ { 0x01, 0x02 }, + /* 085 */ { 0x01, 0x02 }, + /* 086 */ { 0x01, 0x02 }, + /* 087 */ { 0x01, 0x02 }, + /* 088 */ { 0x01, 0x02 }, + /* 089 */ { 0x01, 0x02 }, + /* 08a */ { 0x01, 0x02 }, + /* 08b */ { 0x01, 0x02 }, + /* 08c */ { 0x01, 0x02 }, + /* 08d */ { 0x01, 0x02 }, + /* 08e */ { 0x01, 0x02 }, + /* 08f */ { 0x01, 0x02 }, + /* 090 */ { 0x01, 0x02 }, + /* 091 */ { 0x01, 0x02 }, + /* 092 */ { 0x01, 0x02 }, + /* 093 */ { 0x01, 0x02 }, + /* 094 */ { 0x01, 0x02 }, + /* 095 */ { 0x01, 0x02 }, + /* 096 */ { 0x01, 0x02 }, + /* 097 */ { 0x01, 0x02 }, + /* 098 */ { 0x01, 0x02 }, + /* 099 */ { 0x01, 0x02 }, + /* 09a */ { 0x01, 0x02 }, + /* 09b */ { 0x01, 0x02 }, + /* 09c */ { 0x01, 0x02 }, + /* 09d */ { 0x01, 0x02 }, + /* 09e */ { 0x01, 0x02 }, + /* 09f */ { 0x01, 0x02 }, + /* 0a0 */ { 0x01, 0x02 }, + /* 0a1 */ { 0x01, 0x02 }, + /* 0a2 */ { 0x01, 0x02 }, + /* 0a3 */ { 0x01, 0x02 }, + /* 0a4 */ { 0x01, 0x02 }, + /* 0a5 */ { 0x01, 0x02 }, + /* 0a6 */ { 0x01, 0x02 }, + /* 0a7 */ { 0x01, 0x02 }, + /* 0a8 */ { 0x01, 0x02 }, + /* 0a9 */ { 0x01, 0x02 }, + /* 0aa */ { 0x01, 0x02 }, + /* 0ab */ { 0x01, 0x02 }, + /* 0ac */ { 0x01, 0x02 }, + /* 0ad */ { 0x01, 0x02 }, + /* 0ae */ { 0x01, 0x02 }, + /* 0af */ { 0x01, 0x02 }, + /* 0b0 */ { 0x01, 0x02 }, + /* 0b1 */ { 0x01, 0x02 }, + /* 0b2 */ { 0x01, 0x02 }, + /* 0b3 */ { 0x01, 0x02 }, + /* 0b4 */ { 0x01, 0x02 }, + /* 0b5 */ { 0x01, 0x02 }, + /* 0b6 */ { 0x01, 0x02 }, + /* 0b7 */ { 0x01, 0x02 }, + /* 0b8 */ { 0x01, 0x02 }, + /* 0b9 */ { 0x01, 0x02 }, + /* 0ba */ { 0x01, 0x02 }, + /* 0bb */ { 0x01, 0x02 }, + /* 0bc */ { 0x01, 0x02 }, + /* 0bd */ { 0x01, 0x02 }, + /* 0be */ { 0x01, 0x02 }, + /* 0bf */ { 0x01, 0x02 }, + /* 0c0 */ { 0x01, 0x02 }, + /* 0c1 */ { 0x01, 0x02 }, + /* 0c2 */ { 0x01, 0x02 }, + /* 0c3 */ { 0x01, 0x02 }, + /* 0c4 */ { 0x01, 0x02 }, + /* 0c5 */ { 0x01, 0x02 }, + /* 0c6 */ { 0x01, 0x02 }, + /* 0c7 */ { 0x01, 0x02 }, + /* 0c8 */ { 0x01, 0x02 }, + /* 0c9 */ { 0x01, 0x02 }, + /* 0ca */ { 0x01, 0x02 }, + /* 0cb */ { 0x01, 0x02 }, + /* 0cc */ { 0x01, 0x02 }, + /* 0cd */ { 0x01, 0x02 }, + /* 0ce */ { 0x01, 0x02 }, + /* 0cf */ { 0x01, 0x02 }, + /* 0d0 */ { 0x01, 0x02 }, + /* 0d1 */ { 0x01, 0x02 }, + /* 0d2 */ { 0x01, 0x02 }, + /* 0d3 */ { 0x01, 0x02 }, + /* 0d4 */ { 0x01, 0x02 }, + /* 0d5 */ { 0x01, 0x02 }, + /* 0d6 */ { 0x01, 0x02 }, + /* 0d7 */ { 0x01, 0x02 }, + /* 0d8 */ { 0x01, 0x02 }, + /* 0d9 */ { 0x01, 0x02 }, + /* 0da */ { 0x01, 0x02 }, + /* 0db */ { 0x01, 0x02 }, + /* 0dc */ { 0x01, 0x02 }, + /* 0dd */ { 0x01, 0x02 }, + /* 0de */ { 0x01, 0x02 }, + /* 0df */ { 0x01, 0x02 }, + /* 0e0 */ { 0x01, 0x02 }, + /* 0e1 */ { 0x01, 0x02 }, + /* 0e2 */ { 0x01, 0x02 }, + /* 0e3 */ { 0x01, 0x02 }, + /* 0e4 */ { 0x01, 0x02 }, + /* 0e5 */ { 0x01, 0x02 }, + /* 0e6 */ { 0x01, 0x02 }, + /* 0e7 */ { 0x01, 0x02 }, + /* 0e8 */ { 0x01, 0x02 }, + /* 0e9 */ { 0x01, 0x02 }, + /* 0ea */ { 0x01, 0x02 }, + /* 0eb */ { 0x01, 0x02 }, + /* 0ec */ { 0x01, 0x02 }, + /* 0ed */ { 0x01, 0x02 }, + /* 0ee */ { 0x01, 0x02 }, + /* 0ef */ { 0x01, 0x02 }, + /* 0f0 */ { 0x01, 0x02 }, + /* 0f1 */ { 0x01, 0x02 }, + /* 0f2 */ { 0x01, 0x02 }, + /* 0f3 */ { 0x01, 0x02 }, + /* 0f4 */ { 0x01, 0x02 }, + /* 0f5 */ { 0x01, 0x02 }, + /* 0f6 */ { 0x01, 0x02 }, + /* 0f7 */ { 0x01, 0x02 }, + /* 0f8 */ { 0x01, 0x02 }, + /* 0f9 */ { 0x01, 0x02 }, + /* 0fa */ { 0x01, 0x02 }, + /* 0fb */ { 0x01, 0x02 }, + /* 0fc */ { 0x01, 0x02 }, + /* 0fd */ { 0x01, 0x02 }, + /* 0fe */ { 0x01, 0x02 }, + /* 0ff */ { 0x01, 0x02 }, + /* 100 */ { 0x01, 0x02 }, + /* 101 */ { 0x01, 0x02 }, + /* 102 */ { 0x01, 0x02 }, + /* 103 */ { 0x01, 0x02 }, + /* 104 */ { 0x01, 0x02 }, + /* 105 */ { 0x01, 0x02 }, + /* 106 */ { 0x01, 0x02 }, + /* 107 */ { 0x01, 0x02 }, + /* 108 */ { 0x01, 0x02 }, + /* 109 */ { 0x01, 0x02 }, + /* 10a */ { 0x01, 0x02 }, + /* 10b */ { 0x01, 0x02 }, + /* 10c */ { 0x01, 0x02 }, + /* 10d */ { 0x01, 0x02 }, + /* 10e */ { 0x01, 0x02 }, + /* 10f */ { 0x01, 0x02 }, + /* 110 */ { 0x01, 0x02 }, + /* 111 */ { 0x01, 0x02 }, + /* 112 */ { 0x01, 0x02 }, + /* 113 */ { 0x01, 0x02 }, + /* 114 */ { 0x01, 0x02 }, + /* 115 */ { 0x01, 0x02 }, + /* 116 */ { 0x01, 0x02 }, + /* 117 */ { 0x01, 0x02 }, + /* 118 */ { 0x01, 0x02 }, + /* 119 */ { 0x01, 0x02 }, + /* 11a */ { 0x01, 0x02 }, + /* 11b */ { 0x01, 0x02 }, + /* 11c */ { 0x01, 0x02 }, + /* 11d */ { 0x01, 0x02 }, + /* 11e */ { 0x01, 0x02 }, + /* 11f */ { 0x01, 0x02 }, + /* 120 */ { 0x01, 0x02 }, + /* 121 */ { 0x01, 0x02 }, + /* 122 */ { 0x01, 0x02 }, + /* 123 */ { 0x01, 0x02 }, + /* 124 */ { 0x01, 0x02 }, + /* 125 */ { 0x01, 0x02 }, + /* 126 */ { 0x01, 0x02 }, + /* 127 */ { 0x01, 0x02 }, + /* 128 */ { 0x01, 0x02 }, + /* 129 */ { 0x01, 0x02 }, + /* 12a */ { 0x01, 0x02 }, + /* 12b */ { 0x01, 0x02 }, + /* 12c */ { 0x01, 0x02 }, + /* 12d */ { 0x01, 0x02 }, + /* 12e */ { 0x01, 0x02 }, + /* 12f */ { 0x01, 0x02 }, + /* 130 */ { 0x01, 0x02 }, + /* 131 */ { 0x01, 0x02 }, + /* 132 */ { 0x01, 0x02 }, + /* 133 */ { 0x01, 0x02 }, + /* 134 */ { 0x01, 0x02 }, + /* 135 */ { 0x01, 0x02 }, + /* 136 */ { 0x01, 0x02 }, + /* 137 */ { 0x01, 0x02 }, + /* 138 */ { 0x01, 0x02 }, + /* 139 */ { 0x01, 0x02 }, + /* 13a */ { 0x01, 0x02 }, + /* 13b */ { 0x01, 0x02 }, + /* 13c */ { 0x01, 0x02 }, + /* 13d */ { 0x01, 0x02 }, + /* 13e */ { 0x01, 0x02 }, + /* 13f */ { 0x01, 0x02 }, + /* 140 */ { 0x01, 0x02 }, + /* 141 */ { 0x01, 0x02 }, + /* 142 */ { 0x01, 0x02 }, + /* 143 */ { 0x01, 0x02 }, + /* 144 */ { 0x01, 0x02 }, + /* 145 */ { 0x01, 0x02 }, + /* 146 */ { 0x01, 0x02 }, + /* 147 */ { 0x01, 0x02 }, + /* 148 */ { 0x01, 0x02 }, + /* 149 */ { 0x01, 0x02 }, + /* 14a */ { 0x01, 0x02 }, + /* 14b */ { 0x01, 0x02 }, + /* 14c */ { 0x01, 0x02 }, + /* 14d */ { 0x01, 0x02 }, + /* 14e */ { 0x01, 0x02 }, + /* 14f */ { 0x01, 0x02 }, + /* 150 */ { 0x01, 0x02 }, + /* 151 */ { 0x01, 0x02 }, + /* 152 */ { 0x01, 0x02 }, + /* 153 */ { 0x01, 0x02 }, + /* 154 */ { 0x01, 0x02 }, + /* 155 */ { 0x01, 0x02 }, + /* 156 */ { 0x01, 0x02 }, + /* 157 */ { 0x01, 0x02 }, + /* 158 */ { 0x01, 0x02 }, + /* 159 */ { 0x01, 0x02 }, + /* 15a */ { 0x01, 0x02 }, + /* 15b */ { 0x01, 0x02 }, + /* 15c */ { 0x01, 0x02 }, + /* 15d */ { 0x01, 0x02 }, + /* 15e */ { 0x01, 0x02 }, + /* 15f */ { 0x01, 0x02 }, + /* 160 */ { 0x01, 0x02 }, + /* 161 */ { 0x01, 0x02 }, + /* 162 */ { 0x01, 0x02 }, + /* 163 */ { 0x01, 0x02 }, + /* 164 */ { 0x01, 0x02 }, + /* 165 */ { 0x01, 0x02 }, + /* 166 */ { 0x01, 0x02 }, + /* 167 */ { 0x01, 0x02 }, + /* 168 */ { 0x01, 0x02 }, + /* 169 */ { 0x01, 0x02 }, + /* 16a */ { 0x01, 0x02 }, + /* 16b */ { 0x01, 0x02 }, + /* 16c */ { 0x01, 0x02 }, + /* 16d */ { 0x01, 0x02 }, + /* 16e */ { 0x01, 0x02 }, + /* 16f */ { 0x01, 0x02 }, + /* 170 */ { 0x01, 0x02 }, + /* 171 */ { 0x01, 0x02 }, + /* 172 */ { 0x01, 0x02 }, + /* 173 */ { 0x01, 0x02 }, + /* 174 */ { 0x01, 0x02 }, + /* 175 */ { 0x01, 0x02 }, + /* 176 */ { 0x01, 0x02 }, + /* 177 */ { 0x01, 0x02 }, + /* 178 */ { 0x01, 0x02 }, + /* 179 */ { 0x01, 0x02 }, + /* 17a */ { 0x01, 0x02 }, + /* 17b */ { 0x01, 0x02 }, + /* 17c */ { 0x01, 0x02 }, + /* 17d */ { 0x01, 0x02 }, + /* 17e */ { 0x01, 0x02 }, + /* 17f */ { 0x01, 0x02 }, + /* 180 */ { 0x01, 0x02 }, + /* 181 */ { 0x01, 0x02 }, + /* 182 */ { 0x01, 0x02 }, + /* 183 */ { 0x01, 0x02 }, + /* 184 */ { 0x01, 0x02 }, + /* 185 */ { 0x01, 0x02 }, + /* 186 */ { 0x01, 0x02 }, + /* 187 */ { 0x01, 0x02 }, + /* 188 */ { 0x01, 0x02 }, + /* 189 */ { 0x01, 0x02 }, + /* 18a */ { 0x01, 0x02 }, + /* 18b */ { 0x01, 0x02 }, + /* 18c */ { 0x01, 0x02 }, + /* 18d */ { 0x01, 0x02 }, + /* 18e */ { 0x01, 0x02 }, + /* 18f */ { 0x01, 0x02 }, + /* 190 */ { 0x01, 0x02 }, + /* 191 */ { 0x01, 0x02 }, + /* 192 */ { 0x01, 0x02 }, + /* 193 */ { 0x01, 0x02 }, + /* 194 */ { 0x01, 0x02 }, + /* 195 */ { 0x01, 0x02 }, + /* 196 */ { 0x01, 0x02 }, + /* 197 */ { 0x01, 0x02 }, + /* 198 */ { 0x01, 0x02 }, + /* 199 */ { 0x01, 0x02 }, + /* 19a */ { 0x01, 0x02 }, + /* 19b */ { 0x01, 0x02 }, + /* 19c */ { 0x01, 0x02 }, + /* 19d */ { 0x01, 0x02 }, + /* 19e */ { 0x01, 0x02 }, + /* 19f */ { 0x01, 0x02 }, + /* 1a0 */ { 0x01, 0x02 }, + /* 1a1 */ { 0x01, 0x02 }, + /* 1a2 */ { 0x01, 0x02 }, + /* 1a3 */ { 0x01, 0x02 }, + /* 1a4 */ { 0x01, 0x02 }, + /* 1a5 */ { 0x01, 0x02 }, + /* 1a6 */ { 0x01, 0x02 }, + /* 1a7 */ { 0x01, 0x02 }, + /* 1a8 */ { 0x01, 0x02 }, + /* 1a9 */ { 0x01, 0x02 }, + /* 1aa */ { 0x01, 0x02 }, + /* 1ab */ { 0x01, 0x02 }, + /* 1ac */ { 0x01, 0x02 }, + /* 1ad */ { 0x01, 0x02 }, + /* 1ae */ { 0x01, 0x02 }, + /* 1af */ { 0x01, 0x02 }, + /* 1b0 */ { 0x01, 0x02 }, + /* 1b1 */ { 0x01, 0x02 }, + /* 1b2 */ { 0x01, 0x02 }, + /* 1b3 */ { 0x01, 0x02 }, + /* 1b4 */ { 0x01, 0x02 }, + /* 1b5 */ { 0x01, 0x02 }, + /* 1b6 */ { 0x01, 0x02 }, + /* 1b7 */ { 0x01, 0x02 }, + /* 1b8 */ { 0x01, 0x02 }, + /* 1b9 */ { 0x01, 0x02 }, + /* 1ba */ { 0x01, 0x02 }, + /* 1bb */ { 0x01, 0x02 }, + /* 1bc */ { 0x01, 0x02 }, + /* 1bd */ { 0x01, 0x02 }, + /* 1be */ { 0x01, 0x02 }, + /* 1bf */ { 0x01, 0x02 }, + /* 1c0 */ { 0x01, 0x02 }, + /* 1c1 */ { 0x01, 0x02 }, + /* 1c2 */ { 0x01, 0x02 }, + /* 1c3 */ { 0x01, 0x02 }, + /* 1c4 */ { 0x01, 0x02 }, + /* 1c5 */ { 0x01, 0x02 }, + /* 1c6 */ { 0x01, 0x02 }, + /* 1c7 */ { 0x01, 0x02 }, + /* 1c8 */ { 0x01, 0x02 }, + /* 1c9 */ { 0x01, 0x02 }, + /* 1ca */ { 0x01, 0x02 }, + /* 1cb */ { 0x01, 0x02 }, + /* 1cc */ { 0x01, 0x02 }, + /* 1cd */ { 0x01, 0x02 }, + /* 1ce */ { 0x01, 0x02 }, + /* 1cf */ { 0x01, 0x02 }, + /* 1d0 */ { 0x01, 0x02 }, + /* 1d1 */ { 0x01, 0x02 }, + /* 1d2 */ { 0x01, 0x02 }, + /* 1d3 */ { 0x01, 0x02 }, + /* 1d4 */ { 0x01, 0x02 }, + /* 1d5 */ { 0x01, 0x02 }, + /* 1d6 */ { 0x01, 0x02 }, + /* 1d7 */ { 0x01, 0x02 }, + /* 1d8 */ { 0x01, 0x02 }, + /* 1d9 */ { 0x01, 0x02 }, + /* 1da */ { 0x01, 0x02 }, + /* 1db */ { 0x01, 0x02 }, + /* 1dc */ { 0x01, 0x02 }, + /* 1dd */ { 0x01, 0x02 }, + /* 1de */ { 0x01, 0x02 }, + /* 1df */ { 0x01, 0x02 }, + /* 1e0 */ { 0x01, 0x02 }, + /* 1e1 */ { 0x01, 0x02 }, + /* 1e2 */ { 0x01, 0x02 }, + /* 1e3 */ { 0x01, 0x02 }, + /* 1e4 */ { 0x01, 0x02 }, + /* 1e5 */ { 0x01, 0x02 }, + /* 1e6 */ { 0x01, 0x02 }, + /* 1e7 */ { 0x01, 0x02 }, + /* 1e8 */ { 0x01, 0x02 }, + /* 1e9 */ { 0x01, 0x02 }, + /* 1ea */ { 0x01, 0x02 }, + /* 1eb */ { 0x01, 0x02 }, + /* 1ec */ { 0x01, 0x02 }, + /* 1ed */ { 0x01, 0x02 }, + /* 1ee */ { 0x01, 0x02 }, + /* 1ef */ { 0x01, 0x02 }, + /* 1f0 */ { 0x01, 0x02 }, + /* 1f1 */ { 0x01, 0x02 }, + /* 1f2 */ { 0x01, 0x02 }, + /* 1f3 */ { 0x01, 0x02 }, + /* 1f4 */ { 0x01, 0x02 }, + /* 1f5 */ { 0x01, 0x02 }, + /* 1f6 */ { 0x01, 0x02 }, + /* 1f7 */ { 0x01, 0x02 }, + /* 1f8 */ { 0x01, 0x02 }, + /* 1f9 */ { 0x01, 0x02 }, + /* 1fa */ { 0x01, 0x02 }, + /* 1fb */ { 0x01, 0x02 }, + /* 1fc */ { 0x01, 0x02 }, + /* 1fd */ { 0x01, 0x02 }, + /* 1fe */ { 0x01, 0x02 }, + /* 1ff */ { 0x01, 0x02 }, + /* 200 */ { 0x01, 0x02 }, + /* 201 */ { 0x01, 0x02 }, + /* 202 */ { 0x01, 0x02 }, + /* 203 */ { 0x01, 0x02 }, + /* 204 */ { 0x01, 0x02 }, + /* 205 */ { 0x01, 0x02 }, + /* 206 */ { 0x01, 0x02 }, + /* 207 */ { 0x01, 0x02 }, + /* 208 */ { 0x01, 0x02 }, + /* 209 */ { 0x01, 0x02 }, + /* 20a */ { 0x01, 0x02 }, + /* 20b */ { 0x01, 0x02 }, + /* 20c */ { 0x01, 0x02 }, + /* 20d */ { 0x01, 0x02 }, + /* 20e */ { 0x01, 0x02 }, + /* 20f */ { 0x01, 0x02 }, + /* 210 */ { 0x01, 0x02 }, + /* 211 */ { 0x01, 0x02 }, + /* 212 */ { 0x01, 0x02 }, + /* 213 */ { 0x01, 0x02 }, + /* 214 */ { 0x01, 0x02 }, + /* 215 */ { 0x01, 0x02 }, + /* 216 */ { 0x01, 0x02 }, + /* 217 */ { 0x01, 0x02 }, + /* 218 */ { 0x01, 0x02 }, + /* 219 */ { 0x01, 0x02 }, + /* 21a */ { 0x01, 0x02 }, + /* 21b */ { 0x01, 0x02 }, + /* 21c */ { 0x01, 0x02 }, + /* 21d */ { 0x01, 0x02 }, + /* 21e */ { 0x01, 0x02 }, + /* 21f */ { 0x01, 0x02 }, + /* 220 */ { 0x01, 0x02 }, + /* 221 */ { 0x01, 0x02 }, + /* 222 */ { 0x01, 0x02 }, + /* 223 */ { 0x01, 0x02 }, + /* 224 */ { 0x01, 0x02 }, + /* 225 */ { 0x01, 0x02 }, + /* 226 */ { 0x01, 0x02 }, + /* 227 */ { 0x01, 0x02 }, + /* 228 */ { 0x01, 0x02 }, + /* 229 */ { 0x01, 0x02 }, + /* 22a */ { 0x01, 0x02 }, + /* 22b */ { 0x01, 0x02 }, + /* 22c */ { 0x01, 0x02 }, + /* 22d */ { 0x01, 0x02 }, + /* 22e */ { 0x01, 0x02 }, + /* 22f */ { 0x01, 0x02 }, + /* 230 */ { 0x01, 0x02 }, + /* 231 */ { 0x01, 0x02 }, + /* 232 */ { 0x01, 0x02 }, + /* 233 */ { 0x01, 0x02 }, + /* 234 */ { 0x01, 0x02 }, + /* 235 */ { 0x01, 0x02 }, + /* 236 */ { 0x01, 0x02 }, + /* 237 */ { 0x01, 0x02 }, + /* 238 */ { 0x01, 0x02 }, + /* 239 */ { 0x01, 0x02 }, + /* 23a */ { 0x01, 0x02 }, + /* 23b */ { 0x01, 0x02 }, + /* 23c */ { 0x01, 0x02 }, + /* 23d */ { 0x01, 0x02 }, + /* 23e */ { 0x01, 0x02 }, + /* 23f */ { 0x01, 0x02 }, + /* 240 */ { 0x01, 0x02 }, + /* 241 */ { 0x01, 0x02 }, + /* 242 */ { 0x01, 0x02 }, + /* 243 */ { 0x01, 0x02 }, + /* 244 */ { 0x01, 0x02 }, + /* 245 */ { 0x01, 0x02 }, + /* 246 */ { 0x01, 0x02 }, + /* 247 */ { 0x01, 0x02 }, + /* 248 */ { 0x01, 0x02 }, + /* 249 */ { 0x01, 0x02 }, + /* 24a */ { 0x01, 0x02 }, + /* 24b */ { 0x01, 0x02 }, + /* 24c */ { 0x01, 0x02 }, + /* 24d */ { 0x01, 0x02 }, + /* 24e */ { 0x01, 0x02 }, + /* 24f */ { 0x01, 0x02 }, + /* 250 */ { 0x01, 0x02 }, + /* 251 */ { 0x01, 0x02 }, + /* 252 */ { 0x01, 0x02 }, + /* 253 */ { 0x01, 0x02 }, + /* 254 */ { 0x01, 0x02 }, + /* 255 */ { 0x01, 0x02 }, + /* 256 */ { 0x01, 0x02 }, + /* 257 */ { 0x01, 0x02 }, + /* 258 */ { 0x01, 0x02 }, + /* 259 */ { 0x01, 0x02 }, + /* 25a */ { 0x01, 0x02 }, + /* 25b */ { 0x01, 0x02 }, + /* 25c */ { 0x01, 0x02 }, + /* 25d */ { 0x01, 0x02 }, + /* 25e */ { 0x01, 0x02 }, + /* 25f */ { 0x01, 0x02 }, + /* 260 */ { 0x01, 0x02 }, + /* 261 */ { 0x01, 0x02 }, + /* 262 */ { 0x01, 0x02 }, + /* 263 */ { 0x01, 0x02 }, + /* 264 */ { 0x01, 0x02 }, + /* 265 */ { 0x01, 0x02 }, + /* 266 */ { 0x01, 0x02 }, + /* 267 */ { 0x01, 0x02 }, + /* 268 */ { 0x01, 0x02 }, + /* 269 */ { 0x01, 0x02 }, + /* 26a */ { 0x01, 0x02 }, + /* 26b */ { 0x01, 0x02 }, + /* 26c */ { 0x01, 0x02 }, + /* 26d */ { 0x01, 0x02 }, + /* 26e */ { 0x01, 0x02 }, + /* 26f */ { 0x01, 0x02 }, + /* 270 */ { 0x01, 0x02 }, + /* 271 */ { 0x01, 0x02 }, + /* 272 */ { 0x01, 0x02 }, + /* 273 */ { 0x01, 0x02 }, + /* 274 */ { 0x01, 0x02 }, + /* 275 */ { 0x01, 0x02 }, + /* 276 */ { 0x01, 0x02 }, + /* 277 */ { 0x01, 0x02 }, + /* 278 */ { 0x01, 0x02 }, + /* 279 */ { 0x01, 0x02 }, + /* 27a */ { 0x01, 0x02 }, + /* 27b */ { 0x01, 0x02 }, + /* 27c */ { 0x01, 0x02 }, + /* 27d */ { 0x01, 0x02 }, + /* 27e */ { 0x01, 0x02 }, + /* 27f */ { 0x01, 0x02 }, + /* 280 */ { 0x01, 0x02 }, + /* 281 */ { 0x01, 0x02 }, + /* 282 */ { 0x01, 0x02 }, + /* 283 */ { 0x01, 0x02 }, + /* 284 */ { 0x01, 0x02 }, + /* 285 */ { 0x01, 0x02 }, + /* 286 */ { 0x01, 0x02 }, + /* 287 */ { 0x01, 0x02 }, + /* 288 */ { 0x01, 0x02 }, + /* 289 */ { 0x01, 0x02 }, + /* 28a */ { 0x01, 0x02 }, + /* 28b */ { 0x01, 0x02 }, + /* 28c */ { 0x01, 0x02 }, + /* 28d */ { 0x01, 0x02 }, + /* 28e */ { 0x01, 0x02 }, + /* 28f */ { 0x01, 0x02 }, + /* 290 */ { 0x01, 0x02 }, + /* 291 */ { 0x01, 0x02 }, + /* 292 */ { 0x01, 0x02 }, + /* 293 */ { 0x01, 0x02 }, + /* 294 */ { 0x01, 0x02 }, + /* 295 */ { 0x01, 0x02 }, + /* 296 */ { 0x01, 0x02 }, + /* 297 */ { 0x01, 0x02 }, + /* 298 */ { 0x01, 0x02 }, + /* 299 */ { 0x01, 0x02 }, + /* 29a */ { 0x01, 0x02 }, + /* 29b */ { 0x01, 0x02 }, + /* 29c */ { 0x01, 0x02 }, + /* 29d */ { 0x01, 0x02 }, + /* 29e */ { 0x01, 0x02 }, + /* 29f */ { 0x01, 0x02 }, + /* 2a0 */ { 0x01, 0x02 }, + /* 2a1 */ { 0x01, 0x02 }, + /* 2a2 */ { 0x01, 0x02 }, + /* 2a3 */ { 0x01, 0x02 }, + /* 2a4 */ { 0x01, 0x02 }, + /* 2a5 */ { 0x01, 0x02 }, + /* 2a6 */ { 0x01, 0x02 }, + /* 2a7 */ { 0x01, 0x02 }, + /* 2a8 */ { 0x01, 0x02 }, + /* 2a9 */ { 0x01, 0x02 }, + /* 2aa */ { 0x01, 0x02 }, + /* 2ab */ { 0x01, 0x02 }, + /* 2ac */ { 0x01, 0x02 }, + /* 2ad */ { 0x01, 0x02 }, + /* 2ae */ { 0x01, 0x02 }, + /* 2af */ { 0x01, 0x02 }, + /* 2b0 */ { 0x01, 0x02 }, + /* 2b1 */ { 0x01, 0x02 }, + /* 2b2 */ { 0x01, 0x02 }, + /* 2b3 */ { 0x01, 0x02 }, + /* 2b4 */ { 0x01, 0x02 }, + /* 2b5 */ { 0x01, 0x02 }, + /* 2b6 */ { 0x01, 0x02 }, + /* 2b7 */ { 0x01, 0x02 }, + /* 2b8 */ { 0x01, 0x02 }, + /* 2b9 */ { 0x01, 0x02 }, + /* 2ba */ { 0x01, 0x02 }, + /* 2bb */ { 0x01, 0x02 }, + /* 2bc */ { 0x01, 0x02 }, + /* 2bd */ { 0x01, 0x02 }, + /* 2be */ { 0x01, 0x02 }, + /* 2bf */ { 0x01, 0x02 }, + /* 2c0 */ { 0x01, 0x02 }, + /* 2c1 */ { 0x01, 0x02 }, + /* 2c2 */ { 0x01, 0x02 }, + /* 2c3 */ { 0x01, 0x02 }, + /* 2c4 */ { 0x01, 0x02 }, + /* 2c5 */ { 0x01, 0x02 }, + /* 2c6 */ { 0x01, 0x02 }, + /* 2c7 */ { 0x01, 0x02 }, + /* 2c8 */ { 0x01, 0x02 }, + /* 2c9 */ { 0x01, 0x02 }, + /* 2ca */ { 0x01, 0x02 }, + /* 2cb */ { 0x01, 0x02 }, + /* 2cc */ { 0x01, 0x02 }, + /* 2cd */ { 0x01, 0x02 }, + /* 2ce */ { 0x01, 0x02 }, + /* 2cf */ { 0x01, 0x02 }, + /* 2d0 */ { 0x01, 0x02 }, + /* 2d1 */ { 0x01, 0x02 }, + /* 2d2 */ { 0x01, 0x02 }, + /* 2d3 */ { 0x01, 0x02 }, + /* 2d4 */ { 0x01, 0x02 }, + /* 2d5 */ { 0x01, 0x02 }, + /* 2d6 */ { 0x01, 0x02 }, + /* 2d7 */ { 0x01, 0x02 }, + /* 2d8 */ { 0x01, 0x02 }, + /* 2d9 */ { 0x01, 0x02 }, + /* 2da */ { 0x01, 0x02 }, + /* 2db */ { 0x01, 0x02 }, + /* 2dc */ { 0x01, 0x02 }, + /* 2dd */ { 0x01, 0x02 }, + /* 2de */ { 0x01, 0x02 }, + /* 2df */ { 0x01, 0x02 }, + /* 2e0 */ { 0x01, 0x02 }, + /* 2e1 */ { 0x01, 0x02 }, + /* 2e2 */ { 0x01, 0x02 }, + /* 2e3 */ { 0x01, 0x02 }, + /* 2e4 */ { 0x01, 0x02 }, + /* 2e5 */ { 0x01, 0x02 }, + /* 2e6 */ { 0x01, 0x02 }, + /* 2e7 */ { 0x01, 0x02 }, + /* 2e8 */ { 0x01, 0x02 }, + /* 2e9 */ { 0x01, 0x02 }, + /* 2ea */ { 0x01, 0x02 }, + /* 2eb */ { 0x01, 0x02 }, + /* 2ec */ { 0x01, 0x02 }, + /* 2ed */ { 0x01, 0x02 }, + /* 2ee */ { 0x01, 0x02 }, + /* 2ef */ { 0x01, 0x02 }, + /* 2f0 */ { 0x01, 0x02 }, + /* 2f1 */ { 0x01, 0x02 }, + /* 2f2 */ { 0x01, 0x02 }, + /* 2f3 */ { 0x01, 0x02 }, + /* 2f4 */ { 0x01, 0x02 }, + /* 2f5 */ { 0x01, 0x02 }, + /* 2f6 */ { 0x01, 0x02 }, + /* 2f7 */ { 0x01, 0x02 }, + /* 2f8 */ { 0x01, 0x02 }, + /* 2f9 */ { 0x01, 0x02 }, + /* 2fa */ { 0x01, 0x02 }, + /* 2fb */ { 0x01, 0x02 }, + /* 2fc */ { 0x01, 0x02 }, + /* 2fd */ { 0x01, 0x02 }, + /* 2fe */ { 0x01, 0x02 }, + /* 2ff */ { 0x01, 0x02 }, + /* 300 */ { 0x01, 0x02 }, + /* 301 */ { 0x01, 0x02 }, + /* 302 */ { 0x01, 0x02 }, + /* 303 */ { 0x01, 0x02 }, + /* 304 */ { 0x01, 0x02 }, + /* 305 */ { 0x01, 0x02 }, + /* 306 */ { 0x01, 0x02 }, + /* 307 */ { 0x01, 0x02 }, + /* 308 */ { 0x01, 0x02 }, + /* 309 */ { 0x01, 0x02 }, + /* 30a */ { 0x01, 0x02 }, + /* 30b */ { 0x01, 0x02 }, + /* 30c */ { 0x01, 0x02 }, + /* 30d */ { 0x01, 0x02 }, + /* 30e */ { 0x01, 0x02 }, + /* 30f */ { 0x01, 0x02 }, + /* 310 */ { 0x01, 0x02 }, + /* 311 */ { 0x01, 0x02 }, + /* 312 */ { 0x01, 0x02 }, + /* 313 */ { 0x01, 0x02 }, + /* 314 */ { 0x01, 0x02 }, + /* 315 */ { 0x01, 0x02 }, + /* 316 */ { 0x01, 0x02 }, + /* 317 */ { 0x01, 0x02 }, + /* 318 */ { 0x01, 0x02 }, + /* 319 */ { 0x01, 0x02 }, + /* 31a */ { 0x01, 0x02 }, + /* 31b */ { 0x01, 0x02 }, + /* 31c */ { 0x01, 0x02 }, + /* 31d */ { 0x01, 0x02 }, + /* 31e */ { 0x01, 0x02 }, + /* 31f */ { 0x01, 0x02 }, + /* 320 */ { 0x01, 0x02 }, + /* 321 */ { 0x01, 0x02 }, + /* 322 */ { 0x01, 0x02 }, + /* 323 */ { 0x01, 0x02 }, + /* 324 */ { 0x01, 0x02 }, + /* 325 */ { 0x01, 0x02 }, + /* 326 */ { 0x01, 0x02 }, + /* 327 */ { 0x01, 0x02 }, + /* 328 */ { 0x01, 0x02 }, + /* 329 */ { 0x01, 0x02 }, + /* 32a */ { 0x01, 0x02 }, + /* 32b */ { 0x01, 0x02 }, + /* 32c */ { 0x01, 0x02 }, + /* 32d */ { 0x01, 0x02 }, + /* 32e */ { 0x01, 0x02 }, + /* 32f */ { 0x01, 0x02 }, + /* 330 */ { 0x01, 0x02 }, + /* 331 */ { 0x01, 0x02 }, + /* 332 */ { 0x01, 0x02 }, + /* 333 */ { 0x01, 0x02 }, + /* 334 */ { 0x01, 0x02 }, + /* 335 */ { 0x01, 0x02 }, + /* 336 */ { 0x01, 0x02 }, + /* 337 */ { 0x01, 0x02 }, + /* 338 */ { 0x01, 0x02 }, + /* 339 */ { 0x01, 0x02 }, + /* 33a */ { 0x01, 0x02 }, + /* 33b */ { 0x01, 0x02 }, + /* 33c */ { 0x01, 0x02 }, + /* 33d */ { 0x01, 0x02 }, + /* 33e */ { 0x01, 0x02 }, + /* 33f */ { 0x01, 0x02 }, + /* 340 */ { 0x01, 0x02 }, + /* 341 */ { 0x01, 0x02 }, + /* 342 */ { 0x01, 0x02 }, + /* 343 */ { 0x01, 0x02 }, + /* 344 */ { 0x01, 0x02 }, + /* 345 */ { 0x01, 0x02 }, + /* 346 */ { 0x01, 0x02 }, + /* 347 */ { 0x01, 0x02 }, + /* 348 */ { 0x01, 0x02 }, + /* 349 */ { 0x01, 0x02 }, + /* 34a */ { 0x01, 0x02 }, + /* 34b */ { 0x01, 0x02 }, + /* 34c */ { 0x01, 0x02 }, + /* 34d */ { 0x01, 0x02 }, + /* 34e */ { 0x01, 0x02 }, + /* 34f */ { 0x01, 0x02 }, + /* 350 */ { 0x01, 0x02 }, + /* 351 */ { 0x01, 0x02 }, + /* 352 */ { 0x01, 0x02 }, + /* 353 */ { 0x01, 0x02 }, + /* 354 */ { 0x01, 0x02 }, + /* 355 */ { 0x01, 0x02 }, + /* 356 */ { 0x01, 0x02 }, + /* 357 */ { 0x01, 0x02 }, + /* 358 */ { 0x01, 0x02 }, + /* 359 */ { 0x01, 0x02 }, + /* 35a */ { 0x01, 0x02 }, + /* 35b */ { 0x01, 0x02 }, + /* 35c */ { 0x01, 0x02 }, + /* 35d */ { 0x01, 0x02 }, + /* 35e */ { 0x01, 0x02 }, + /* 35f */ { 0x01, 0x02 }, + /* 360 */ { 0x01, 0x02 }, + /* 361 */ { 0x01, 0x02 }, + /* 362 */ { 0x01, 0x02 }, + /* 363 */ { 0x01, 0x02 }, + /* 364 */ { 0x01, 0x02 }, + /* 365 */ { 0x01, 0x02 }, + /* 366 */ { 0x01, 0x02 }, + /* 367 */ { 0x01, 0x02 }, + /* 368 */ { 0x01, 0x02 }, + /* 369 */ { 0x01, 0x02 }, + /* 36a */ { 0x01, 0x02 }, + /* 36b */ { 0x01, 0x02 }, + /* 36c */ { 0x01, 0x02 }, + /* 36d */ { 0x01, 0x02 }, + /* 36e */ { 0x01, 0x02 }, + /* 36f */ { 0x01, 0x02 }, + /* 370 */ { 0x01, 0x02 }, + /* 371 */ { 0x01, 0x02 }, + /* 372 */ { 0x01, 0x02 }, + /* 373 */ { 0x01, 0x02 }, + /* 374 */ { 0x01, 0x02 }, + /* 375 */ { 0x01, 0x02 }, + /* 376 */ { 0x01, 0x02 }, + /* 377 */ { 0x01, 0x02 }, + /* 378 */ { 0x01, 0x02 }, + /* 379 */ { 0x01, 0x02 }, + /* 37a */ { 0x01, 0x02 }, + /* 37b */ { 0x01, 0x02 }, + /* 37c */ { 0x01, 0x02 }, + /* 37d */ { 0x01, 0x02 }, + /* 37e */ { 0x01, 0x02 }, + /* 37f */ { 0x01, 0x02 }, + /* 380 */ { 0x01, 0x02 }, + /* 381 */ { 0x01, 0x02 }, + /* 382 */ { 0x01, 0x02 }, + /* 383 */ { 0x01, 0x02 }, + /* 384 */ { 0x01, 0x02 }, + /* 385 */ { 0x01, 0x02 }, + /* 386 */ { 0x01, 0x02 }, + /* 387 */ { 0x01, 0x02 }, + /* 388 */ { 0x01, 0x02 }, + /* 389 */ { 0x01, 0x02 }, + /* 38a */ { 0x01, 0x02 }, + /* 38b */ { 0x01, 0x02 }, + /* 38c */ { 0x01, 0x02 }, + /* 38d */ { 0x01, 0x02 }, + /* 38e */ { 0x01, 0x02 }, + /* 38f */ { 0x01, 0x02 }, + /* 390 */ { 0x01, 0x02 }, + /* 391 */ { 0x01, 0x02 }, + /* 392 */ { 0x01, 0x02 }, + /* 393 */ { 0x01, 0x02 }, + /* 394 */ { 0x01, 0x02 }, + /* 395 */ { 0x01, 0x02 }, + /* 396 */ { 0x01, 0x02 }, + /* 397 */ { 0x01, 0x02 }, + /* 398 */ { 0x01, 0x02 }, + /* 399 */ { 0x01, 0x02 }, + /* 39a */ { 0x01, 0x02 }, + /* 39b */ { 0x01, 0x02 }, + /* 39c */ { 0x01, 0x02 }, + /* 39d */ { 0x01, 0x02 }, + /* 39e */ { 0x01, 0x02 }, + /* 39f */ { 0x01, 0x02 }, + /* 3a0 */ { 0x01, 0x02 }, + /* 3a1 */ { 0x01, 0x02 }, + /* 3a2 */ { 0x01, 0x02 }, + /* 3a3 */ { 0x01, 0x02 }, + /* 3a4 */ { 0x01, 0x02 }, + /* 3a5 */ { 0x01, 0x02 }, + /* 3a6 */ { 0x01, 0x02 }, + /* 3a7 */ { 0x01, 0x02 }, + /* 3a8 */ { 0x01, 0x02 }, + /* 3a9 */ { 0x01, 0x02 }, + /* 3aa */ { 0x01, 0x02 }, + /* 3ab */ { 0x01, 0x02 }, + /* 3ac */ { 0x01, 0x02 }, + /* 3ad */ { 0x01, 0x02 }, + /* 3ae */ { 0x01, 0x02 }, + /* 3af */ { 0x01, 0x02 }, + /* 3b0 */ { 0x01, 0x02 }, + /* 3b1 */ { 0x01, 0x02 }, + /* 3b2 */ { 0x01, 0x02 }, + /* 3b3 */ { 0x01, 0x02 }, + /* 3b4 */ { 0x01, 0x02 }, + /* 3b5 */ { 0x01, 0x02 }, + /* 3b6 */ { 0x01, 0x02 }, + /* 3b7 */ { 0x01, 0x02 }, + /* 3b8 */ { 0x01, 0x02 }, + /* 3b9 */ { 0x01, 0x02 }, + /* 3ba */ { 0x01, 0x02 }, + /* 3bb */ { 0x01, 0x02 }, + /* 3bc */ { 0x01, 0x02 }, + /* 3bd */ { 0x01, 0x02 }, + /* 3be */ { 0x01, 0x02 }, + /* 3bf */ { 0x01, 0x02 }, + /* 3c0 */ { 0x01, 0x02 }, + /* 3c1 */ { 0x01, 0x02 }, + /* 3c2 */ { 0x01, 0x02 }, + /* 3c3 */ { 0x01, 0x02 }, + /* 3c4 */ { 0x01, 0x02 }, + /* 3c5 */ { 0x01, 0x02 }, + /* 3c6 */ { 0x01, 0x02 }, + /* 3c7 */ { 0x01, 0x02 }, + /* 3c8 */ { 0x01, 0x02 }, + /* 3c9 */ { 0x01, 0x02 }, + /* 3ca */ { 0x01, 0x02 }, + /* 3cb */ { 0x01, 0x02 }, + /* 3cc */ { 0x01, 0x02 }, + /* 3cd */ { 0x01, 0x02 }, + /* 3ce */ { 0x01, 0x02 }, + /* 3cf */ { 0x01, 0x02 }, + /* 3d0 */ { 0x01, 0x02 }, + /* 3d1 */ { 0x01, 0x02 }, + /* 3d2 */ { 0x01, 0x02 }, + /* 3d3 */ { 0x01, 0x02 }, + /* 3d4 */ { 0x01, 0x02 }, + /* 3d5 */ { 0x01, 0x02 }, + /* 3d6 */ { 0x01, 0x02 }, + /* 3d7 */ { 0x01, 0x02 }, + /* 3d8 */ { 0x01, 0x02 }, + /* 3d9 */ { 0x01, 0x02 }, + /* 3da */ { 0x01, 0x02 }, + /* 3db */ { 0x01, 0x02 }, + /* 3dc */ { 0x01, 0x02 }, + /* 3dd */ { 0x01, 0x02 }, + /* 3de */ { 0x01, 0x02 }, + /* 3df */ { 0x01, 0x02 }, + /* 3e0 */ { 0x01, 0x02 }, + /* 3e1 */ { 0x01, 0x02 }, + /* 3e2 */ { 0x01, 0x02 }, + /* 3e3 */ { 0x01, 0x02 }, + /* 3e4 */ { 0x01, 0x02 }, + /* 3e5 */ { 0x01, 0x02 }, + /* 3e6 */ { 0x01, 0x02 }, + /* 3e7 */ { 0x01, 0x02 }, + /* 3e8 */ { 0x01, 0x02 }, + /* 3e9 */ { 0x01, 0x02 }, + /* 3ea */ { 0x01, 0x02 }, + /* 3eb */ { 0x01, 0x02 }, + /* 3ec */ { 0x01, 0x02 }, + /* 3ed */ { 0x01, 0x02 }, + /* 3ee */ { 0x01, 0x02 }, + /* 3ef */ { 0x01, 0x02 }, + /* 3f0 */ { 0x01, 0x02 }, + /* 3f1 */ { 0x01, 0x02 }, + /* 3f2 */ { 0x01, 0x02 }, + /* 3f3 */ { 0x01, 0x02 }, + /* 3f4 */ { 0x01, 0x02 }, + /* 3f5 */ { 0x01, 0x02 }, + /* 3f6 */ { 0x01, 0x02 }, + /* 3f7 */ { 0x01, 0x02 }, + /* 3f8 */ { 0x01, 0x02 }, + /* 3f9 */ { 0x01, 0x02 }, + /* 3fa */ { 0x01, 0x02 }, + /* 3fb */ { 0x01, 0x02 }, + /* 3fc */ { 0x01, 0x02 }, + /* 3fd */ { 0x01, 0x02 }, + /* 3fe */ { 0x01, 0x02 }, + /* 3ff */ { 0x01, 0x02 }, + /* 400 */ { 0x02, 0x02 }, + /* 401 */ { 0x02, 0x02 }, + /* 402 */ { 0x02, 0x02 }, + /* 403 */ { 0x02, 0x02 }, + /* 404 */ { 0x02, 0x02 }, + /* 405 */ { 0x02, 0x02 }, + /* 406 */ { 0x02, 0x02 }, + /* 407 */ { 0x02, 0x02 }, + /* 408 */ { 0x02, 0x02 }, + /* 409 */ { 0x02, 0x02 }, + /* 40a */ { 0x02, 0x02 }, + /* 40b */ { 0x02, 0x02 }, + /* 40c */ { 0x02, 0x02 }, + /* 40d */ { 0x02, 0x02 }, + /* 40e */ { 0x02, 0x02 }, + /* 40f */ { 0x02, 0x02 }, + /* 410 */ { 0x02, 0x02 }, + /* 411 */ { 0x02, 0x02 }, + /* 412 */ { 0x02, 0x02 }, + /* 413 */ { 0x02, 0x02 }, + /* 414 */ { 0x02, 0x02 }, + /* 415 */ { 0x02, 0x02 }, + /* 416 */ { 0x02, 0x02 }, + /* 417 */ { 0x02, 0x02 }, + /* 418 */ { 0x02, 0x02 }, + /* 419 */ { 0x02, 0x02 }, + /* 41a */ { 0x02, 0x02 }, + /* 41b */ { 0x02, 0x02 }, + /* 41c */ { 0x02, 0x02 }, + /* 41d */ { 0x02, 0x02 }, + /* 41e */ { 0x02, 0x02 }, + /* 41f */ { 0x02, 0x02 }, + /* 420 */ { 0x02, 0x02 }, + /* 421 */ { 0x02, 0x02 }, + /* 422 */ { 0x02, 0x02 }, + /* 423 */ { 0x02, 0x02 }, + /* 424 */ { 0x02, 0x02 }, + /* 425 */ { 0x02, 0x02 }, + /* 426 */ { 0x02, 0x02 }, + /* 427 */ { 0x02, 0x02 }, + /* 428 */ { 0x02, 0x02 }, + /* 429 */ { 0x02, 0x02 }, + /* 42a */ { 0x02, 0x02 }, + /* 42b */ { 0x02, 0x02 }, + /* 42c */ { 0x02, 0x02 }, + /* 42d */ { 0x02, 0x02 }, + /* 42e */ { 0x02, 0x02 }, + /* 42f */ { 0x02, 0x02 }, + /* 430 */ { 0x02, 0x02 }, + /* 431 */ { 0x02, 0x02 }, + /* 432 */ { 0x02, 0x02 }, + /* 433 */ { 0x02, 0x02 }, + /* 434 */ { 0x02, 0x02 }, + /* 435 */ { 0x02, 0x02 }, + /* 436 */ { 0x02, 0x02 }, + /* 437 */ { 0x02, 0x02 }, + /* 438 */ { 0x02, 0x02 }, + /* 439 */ { 0x02, 0x02 }, + /* 43a */ { 0x02, 0x02 }, + /* 43b */ { 0x02, 0x02 }, + /* 43c */ { 0x02, 0x02 }, + /* 43d */ { 0x02, 0x02 }, + /* 43e */ { 0x02, 0x02 }, + /* 43f */ { 0x02, 0x02 }, + /* 440 */ { 0x02, 0x02 }, + /* 441 */ { 0x02, 0x02 }, + /* 442 */ { 0x02, 0x02 }, + /* 443 */ { 0x02, 0x02 }, + /* 444 */ { 0x02, 0x02 }, + /* 445 */ { 0x02, 0x02 }, + /* 446 */ { 0x02, 0x02 }, + /* 447 */ { 0x02, 0x02 }, + /* 448 */ { 0x02, 0x02 }, + /* 449 */ { 0x02, 0x02 }, + /* 44a */ { 0x02, 0x02 }, + /* 44b */ { 0x02, 0x02 }, + /* 44c */ { 0x02, 0x02 }, + /* 44d */ { 0x02, 0x02 }, + /* 44e */ { 0x02, 0x02 }, + /* 44f */ { 0x02, 0x02 }, + /* 450 */ { 0x02, 0x02 }, + /* 451 */ { 0x02, 0x02 }, + /* 452 */ { 0x02, 0x02 }, + /* 453 */ { 0x02, 0x02 }, + /* 454 */ { 0x02, 0x02 }, + /* 455 */ { 0x02, 0x02 }, + /* 456 */ { 0x02, 0x02 }, + /* 457 */ { 0x02, 0x02 }, + /* 458 */ { 0x02, 0x02 }, + /* 459 */ { 0x02, 0x02 }, + /* 45a */ { 0x02, 0x02 }, + /* 45b */ { 0x02, 0x02 }, + /* 45c */ { 0x02, 0x02 }, + /* 45d */ { 0x02, 0x02 }, + /* 45e */ { 0x02, 0x02 }, + /* 45f */ { 0x02, 0x02 }, + /* 460 */ { 0x02, 0x02 }, + /* 461 */ { 0x02, 0x02 }, + /* 462 */ { 0x02, 0x02 }, + /* 463 */ { 0x02, 0x02 }, + /* 464 */ { 0x02, 0x02 }, + /* 465 */ { 0x02, 0x02 }, + /* 466 */ { 0x02, 0x02 }, + /* 467 */ { 0x02, 0x02 }, + /* 468 */ { 0x02, 0x02 }, + /* 469 */ { 0x02, 0x02 }, + /* 46a */ { 0x02, 0x02 }, + /* 46b */ { 0x02, 0x02 }, + /* 46c */ { 0x02, 0x02 }, + /* 46d */ { 0x02, 0x02 }, + /* 46e */ { 0x02, 0x02 }, + /* 46f */ { 0x02, 0x02 }, + /* 470 */ { 0x02, 0x02 }, + /* 471 */ { 0x02, 0x02 }, + /* 472 */ { 0x02, 0x02 }, + /* 473 */ { 0x02, 0x02 }, + /* 474 */ { 0x02, 0x02 }, + /* 475 */ { 0x02, 0x02 }, + /* 476 */ { 0x02, 0x02 }, + /* 477 */ { 0x02, 0x02 }, + /* 478 */ { 0x02, 0x02 }, + /* 479 */ { 0x02, 0x02 }, + /* 47a */ { 0x02, 0x02 }, + /* 47b */ { 0x02, 0x02 }, + /* 47c */ { 0x02, 0x02 }, + /* 47d */ { 0x02, 0x02 }, + /* 47e */ { 0x02, 0x02 }, + /* 47f */ { 0x02, 0x02 }, + /* 480 */ { 0x02, 0x02 }, + /* 481 */ { 0x02, 0x02 }, + /* 482 */ { 0x02, 0x02 }, + /* 483 */ { 0x02, 0x02 }, + /* 484 */ { 0x02, 0x02 }, + /* 485 */ { 0x02, 0x02 }, + /* 486 */ { 0x02, 0x02 }, + /* 487 */ { 0x02, 0x02 }, + /* 488 */ { 0x02, 0x02 }, + /* 489 */ { 0x02, 0x02 }, + /* 48a */ { 0x02, 0x02 }, + /* 48b */ { 0x02, 0x02 }, + /* 48c */ { 0x02, 0x02 }, + /* 48d */ { 0x02, 0x02 }, + /* 48e */ { 0x02, 0x02 }, + /* 48f */ { 0x02, 0x02 }, + /* 490 */ { 0x02, 0x02 }, + /* 491 */ { 0x02, 0x02 }, + /* 492 */ { 0x02, 0x02 }, + /* 493 */ { 0x02, 0x02 }, + /* 494 */ { 0x02, 0x02 }, + /* 495 */ { 0x02, 0x02 }, + /* 496 */ { 0x02, 0x02 }, + /* 497 */ { 0x02, 0x02 }, + /* 498 */ { 0x02, 0x02 }, + /* 499 */ { 0x02, 0x02 }, + /* 49a */ { 0x02, 0x02 }, + /* 49b */ { 0x02, 0x02 }, + /* 49c */ { 0x02, 0x02 }, + /* 49d */ { 0x02, 0x02 }, + /* 49e */ { 0x02, 0x02 }, + /* 49f */ { 0x02, 0x02 }, + /* 4a0 */ { 0x02, 0x02 }, + /* 4a1 */ { 0x02, 0x02 }, + /* 4a2 */ { 0x02, 0x02 }, + /* 4a3 */ { 0x02, 0x02 }, + /* 4a4 */ { 0x02, 0x02 }, + /* 4a5 */ { 0x02, 0x02 }, + /* 4a6 */ { 0x02, 0x02 }, + /* 4a7 */ { 0x02, 0x02 }, + /* 4a8 */ { 0x02, 0x02 }, + /* 4a9 */ { 0x02, 0x02 }, + /* 4aa */ { 0x02, 0x02 }, + /* 4ab */ { 0x02, 0x02 }, + /* 4ac */ { 0x02, 0x02 }, + /* 4ad */ { 0x02, 0x02 }, + /* 4ae */ { 0x02, 0x02 }, + /* 4af */ { 0x02, 0x02 }, + /* 4b0 */ { 0x02, 0x02 }, + /* 4b1 */ { 0x02, 0x02 }, + /* 4b2 */ { 0x02, 0x02 }, + /* 4b3 */ { 0x02, 0x02 }, + /* 4b4 */ { 0x02, 0x02 }, + /* 4b5 */ { 0x02, 0x02 }, + /* 4b6 */ { 0x02, 0x02 }, + /* 4b7 */ { 0x02, 0x02 }, + /* 4b8 */ { 0x02, 0x02 }, + /* 4b9 */ { 0x02, 0x02 }, + /* 4ba */ { 0x02, 0x02 }, + /* 4bb */ { 0x02, 0x02 }, + /* 4bc */ { 0x02, 0x02 }, + /* 4bd */ { 0x02, 0x02 }, + /* 4be */ { 0x02, 0x02 }, + /* 4bf */ { 0x02, 0x02 }, + /* 4c0 */ { 0x02, 0x02 }, + /* 4c1 */ { 0x02, 0x02 }, + /* 4c2 */ { 0x02, 0x02 }, + /* 4c3 */ { 0x02, 0x02 }, + /* 4c4 */ { 0x02, 0x02 }, + /* 4c5 */ { 0x02, 0x02 }, + /* 4c6 */ { 0x02, 0x02 }, + /* 4c7 */ { 0x02, 0x02 }, + /* 4c8 */ { 0x02, 0x02 }, + /* 4c9 */ { 0x02, 0x02 }, + /* 4ca */ { 0x02, 0x02 }, + /* 4cb */ { 0x02, 0x02 }, + /* 4cc */ { 0x02, 0x02 }, + /* 4cd */ { 0x02, 0x02 }, + /* 4ce */ { 0x02, 0x02 }, + /* 4cf */ { 0x02, 0x02 }, + /* 4d0 */ { 0x02, 0x02 }, + /* 4d1 */ { 0x02, 0x02 }, + /* 4d2 */ { 0x02, 0x02 }, + /* 4d3 */ { 0x02, 0x02 }, + /* 4d4 */ { 0x02, 0x02 }, + /* 4d5 */ { 0x02, 0x02 }, + /* 4d6 */ { 0x02, 0x02 }, + /* 4d7 */ { 0x02, 0x02 }, + /* 4d8 */ { 0x02, 0x02 }, + /* 4d9 */ { 0x02, 0x02 }, + /* 4da */ { 0x02, 0x02 }, + /* 4db */ { 0x02, 0x02 }, + /* 4dc */ { 0x02, 0x02 }, + /* 4dd */ { 0x02, 0x02 }, + /* 4de */ { 0x02, 0x02 }, + /* 4df */ { 0x02, 0x02 }, + /* 4e0 */ { 0x02, 0x02 }, + /* 4e1 */ { 0x02, 0x02 }, + /* 4e2 */ { 0x02, 0x02 }, + /* 4e3 */ { 0x02, 0x02 }, + /* 4e4 */ { 0x02, 0x02 }, + /* 4e5 */ { 0x02, 0x02 }, + /* 4e6 */ { 0x02, 0x02 }, + /* 4e7 */ { 0x02, 0x02 }, + /* 4e8 */ { 0x02, 0x02 }, + /* 4e9 */ { 0x02, 0x02 }, + /* 4ea */ { 0x02, 0x02 }, + /* 4eb */ { 0x02, 0x02 }, + /* 4ec */ { 0x02, 0x02 }, + /* 4ed */ { 0x02, 0x02 }, + /* 4ee */ { 0x02, 0x02 }, + /* 4ef */ { 0x02, 0x02 }, + /* 4f0 */ { 0x02, 0x02 }, + /* 4f1 */ { 0x02, 0x02 }, + /* 4f2 */ { 0x02, 0x02 }, + /* 4f3 */ { 0x02, 0x02 }, + /* 4f4 */ { 0x02, 0x02 }, + /* 4f5 */ { 0x02, 0x02 }, + /* 4f6 */ { 0x02, 0x02 }, + /* 4f7 */ { 0x02, 0x02 }, + /* 4f8 */ { 0x02, 0x02 }, + /* 4f9 */ { 0x02, 0x02 }, + /* 4fa */ { 0x02, 0x02 }, + /* 4fb */ { 0x02, 0x02 }, + /* 4fc */ { 0x02, 0x02 }, + /* 4fd */ { 0x02, 0x02 }, + /* 4fe */ { 0x02, 0x02 }, + /* 4ff */ { 0x02, 0x02 }, + /* 500 */ { 0x02, 0x02 }, + /* 501 */ { 0x02, 0x02 }, + /* 502 */ { 0x02, 0x02 }, + /* 503 */ { 0x02, 0x02 }, + /* 504 */ { 0x02, 0x02 }, + /* 505 */ { 0x02, 0x02 }, + /* 506 */ { 0x02, 0x02 }, + /* 507 */ { 0x02, 0x02 }, + /* 508 */ { 0x02, 0x02 }, + /* 509 */ { 0x02, 0x02 }, + /* 50a */ { 0x02, 0x02 }, + /* 50b */ { 0x02, 0x02 }, + /* 50c */ { 0x02, 0x02 }, + /* 50d */ { 0x02, 0x02 }, + /* 50e */ { 0x02, 0x02 }, + /* 50f */ { 0x02, 0x02 }, + /* 510 */ { 0x02, 0x02 }, + /* 511 */ { 0x02, 0x02 }, + /* 512 */ { 0x02, 0x02 }, + /* 513 */ { 0x02, 0x02 }, + /* 514 */ { 0x02, 0x02 }, + /* 515 */ { 0x02, 0x02 }, + /* 516 */ { 0x02, 0x02 }, + /* 517 */ { 0x02, 0x02 }, + /* 518 */ { 0x02, 0x02 }, + /* 519 */ { 0x02, 0x02 }, + /* 51a */ { 0x02, 0x02 }, + /* 51b */ { 0x02, 0x02 }, + /* 51c */ { 0x02, 0x02 }, + /* 51d */ { 0x02, 0x02 }, + /* 51e */ { 0x02, 0x02 }, + /* 51f */ { 0x02, 0x02 }, + /* 520 */ { 0x02, 0x02 }, + /* 521 */ { 0x02, 0x02 }, + /* 522 */ { 0x02, 0x02 }, + /* 523 */ { 0x02, 0x02 }, + /* 524 */ { 0x02, 0x02 }, + /* 525 */ { 0x02, 0x02 }, + /* 526 */ { 0x02, 0x02 }, + /* 527 */ { 0x02, 0x02 }, + /* 528 */ { 0x02, 0x02 }, + /* 529 */ { 0x02, 0x02 }, + /* 52a */ { 0x02, 0x02 }, + /* 52b */ { 0x02, 0x02 }, + /* 52c */ { 0x02, 0x02 }, + /* 52d */ { 0x02, 0x02 }, + /* 52e */ { 0x02, 0x02 }, + /* 52f */ { 0x02, 0x02 }, + /* 530 */ { 0x02, 0x02 }, + /* 531 */ { 0x02, 0x02 }, + /* 532 */ { 0x02, 0x02 }, + /* 533 */ { 0x02, 0x02 }, + /* 534 */ { 0x02, 0x02 }, + /* 535 */ { 0x02, 0x02 }, + /* 536 */ { 0x02, 0x02 }, + /* 537 */ { 0x02, 0x02 }, + /* 538 */ { 0x02, 0x02 }, + /* 539 */ { 0x02, 0x02 }, + /* 53a */ { 0x02, 0x02 }, + /* 53b */ { 0x02, 0x02 }, + /* 53c */ { 0x02, 0x02 }, + /* 53d */ { 0x02, 0x02 }, + /* 53e */ { 0x02, 0x02 }, + /* 53f */ { 0x02, 0x02 }, + /* 540 */ { 0x02, 0x02 }, + /* 541 */ { 0x02, 0x02 }, + /* 542 */ { 0x02, 0x02 }, + /* 543 */ { 0x02, 0x02 }, + /* 544 */ { 0x02, 0x02 }, + /* 545 */ { 0x02, 0x02 }, + /* 546 */ { 0x02, 0x02 }, + /* 547 */ { 0x02, 0x02 }, + /* 548 */ { 0x02, 0x02 }, + /* 549 */ { 0x02, 0x02 }, + /* 54a */ { 0x02, 0x02 }, + /* 54b */ { 0x02, 0x02 }, + /* 54c */ { 0x02, 0x02 }, + /* 54d */ { 0x02, 0x02 }, + /* 54e */ { 0x02, 0x02 }, + /* 54f */ { 0x02, 0x02 }, + /* 550 */ { 0x02, 0x02 }, + /* 551 */ { 0x02, 0x02 }, + /* 552 */ { 0x02, 0x02 }, + /* 553 */ { 0x02, 0x02 }, + /* 554 */ { 0x02, 0x02 }, + /* 555 */ { 0x02, 0x02 }, + /* 556 */ { 0x02, 0x02 }, + /* 557 */ { 0x02, 0x02 }, + /* 558 */ { 0x02, 0x02 }, + /* 559 */ { 0x02, 0x02 }, + /* 55a */ { 0x02, 0x02 }, + /* 55b */ { 0x02, 0x02 }, + /* 55c */ { 0x02, 0x02 }, + /* 55d */ { 0x02, 0x02 }, + /* 55e */ { 0x02, 0x02 }, + /* 55f */ { 0x02, 0x02 }, + /* 560 */ { 0x02, 0x02 }, + /* 561 */ { 0x02, 0x02 }, + /* 562 */ { 0x02, 0x02 }, + /* 563 */ { 0x02, 0x02 }, + /* 564 */ { 0x02, 0x02 }, + /* 565 */ { 0x02, 0x02 }, + /* 566 */ { 0x02, 0x02 }, + /* 567 */ { 0x02, 0x02 }, + /* 568 */ { 0x02, 0x02 }, + /* 569 */ { 0x02, 0x02 }, + /* 56a */ { 0x02, 0x02 }, + /* 56b */ { 0x02, 0x02 }, + /* 56c */ { 0x02, 0x02 }, + /* 56d */ { 0x02, 0x02 }, + /* 56e */ { 0x02, 0x02 }, + /* 56f */ { 0x02, 0x02 }, + /* 570 */ { 0x02, 0x02 }, + /* 571 */ { 0x02, 0x02 }, + /* 572 */ { 0x02, 0x02 }, + /* 573 */ { 0x02, 0x02 }, + /* 574 */ { 0x02, 0x02 }, + /* 575 */ { 0x02, 0x02 }, + /* 576 */ { 0x02, 0x02 }, + /* 577 */ { 0x02, 0x02 }, + /* 578 */ { 0x02, 0x02 }, + /* 579 */ { 0x02, 0x02 }, + /* 57a */ { 0x02, 0x02 }, + /* 57b */ { 0x02, 0x02 }, + /* 57c */ { 0x02, 0x02 }, + /* 57d */ { 0x02, 0x02 }, + /* 57e */ { 0x02, 0x02 }, + /* 57f */ { 0x02, 0x02 }, + /* 580 */ { 0x02, 0x02 }, + /* 581 */ { 0x02, 0x02 }, + /* 582 */ { 0x02, 0x02 }, + /* 583 */ { 0x02, 0x02 }, + /* 584 */ { 0x02, 0x02 }, + /* 585 */ { 0x02, 0x02 }, + /* 586 */ { 0x02, 0x02 }, + /* 587 */ { 0x02, 0x02 }, + /* 588 */ { 0x02, 0x02 }, + /* 589 */ { 0x02, 0x02 }, + /* 58a */ { 0x02, 0x02 }, + /* 58b */ { 0x02, 0x02 }, + /* 58c */ { 0x02, 0x02 }, + /* 58d */ { 0x02, 0x02 }, + /* 58e */ { 0x02, 0x02 }, + /* 58f */ { 0x02, 0x02 }, + /* 590 */ { 0x02, 0x02 }, + /* 591 */ { 0x02, 0x02 }, + /* 592 */ { 0x02, 0x02 }, + /* 593 */ { 0x02, 0x02 }, + /* 594 */ { 0x02, 0x02 }, + /* 595 */ { 0x02, 0x02 }, + /* 596 */ { 0x02, 0x02 }, + /* 597 */ { 0x02, 0x02 }, + /* 598 */ { 0x02, 0x02 }, + /* 599 */ { 0x02, 0x02 }, + /* 59a */ { 0x02, 0x02 }, + /* 59b */ { 0x02, 0x02 }, + /* 59c */ { 0x02, 0x02 }, + /* 59d */ { 0x02, 0x02 }, + /* 59e */ { 0x02, 0x02 }, + /* 59f */ { 0x02, 0x02 }, + /* 5a0 */ { 0x02, 0x02 }, + /* 5a1 */ { 0x02, 0x02 }, + /* 5a2 */ { 0x02, 0x02 }, + /* 5a3 */ { 0x02, 0x02 }, + /* 5a4 */ { 0x02, 0x02 }, + /* 5a5 */ { 0x02, 0x02 }, + /* 5a6 */ { 0x02, 0x02 }, + /* 5a7 */ { 0x02, 0x02 }, + /* 5a8 */ { 0x02, 0x02 }, + /* 5a9 */ { 0x02, 0x02 }, + /* 5aa */ { 0x02, 0x02 }, + /* 5ab */ { 0x02, 0x02 }, + /* 5ac */ { 0x02, 0x02 }, + /* 5ad */ { 0x02, 0x02 }, + /* 5ae */ { 0x02, 0x02 }, + /* 5af */ { 0x02, 0x02 }, + /* 5b0 */ { 0x02, 0x02 }, + /* 5b1 */ { 0x02, 0x02 }, + /* 5b2 */ { 0x02, 0x02 }, + /* 5b3 */ { 0x02, 0x02 }, + /* 5b4 */ { 0x02, 0x02 }, + /* 5b5 */ { 0x02, 0x02 }, + /* 5b6 */ { 0x02, 0x02 }, + /* 5b7 */ { 0x02, 0x02 }, + /* 5b8 */ { 0x02, 0x02 }, + /* 5b9 */ { 0x02, 0x02 }, + /* 5ba */ { 0x02, 0x02 }, + /* 5bb */ { 0x02, 0x02 }, + /* 5bc */ { 0x02, 0x02 }, + /* 5bd */ { 0x02, 0x02 }, + /* 5be */ { 0x02, 0x02 }, + /* 5bf */ { 0x02, 0x02 }, + /* 5c0 */ { 0x02, 0x02 }, + /* 5c1 */ { 0x02, 0x02 }, + /* 5c2 */ { 0x02, 0x02 }, + /* 5c3 */ { 0x02, 0x02 }, + /* 5c4 */ { 0x02, 0x02 }, + /* 5c5 */ { 0x02, 0x02 }, + /* 5c6 */ { 0x02, 0x02 }, + /* 5c7 */ { 0x02, 0x02 }, + /* 5c8 */ { 0x02, 0x02 }, + /* 5c9 */ { 0x02, 0x02 }, + /* 5ca */ { 0x02, 0x02 }, + /* 5cb */ { 0x02, 0x02 }, + /* 5cc */ { 0x02, 0x02 }, + /* 5cd */ { 0x02, 0x02 }, + /* 5ce */ { 0x02, 0x02 }, + /* 5cf */ { 0x02, 0x02 }, + /* 5d0 */ { 0x02, 0x02 }, + /* 5d1 */ { 0x02, 0x02 }, + /* 5d2 */ { 0x02, 0x02 }, + /* 5d3 */ { 0x02, 0x02 }, + /* 5d4 */ { 0x02, 0x02 }, + /* 5d5 */ { 0x02, 0x02 }, + /* 5d6 */ { 0x02, 0x02 }, + /* 5d7 */ { 0x02, 0x02 }, + /* 5d8 */ { 0x02, 0x02 }, + /* 5d9 */ { 0x02, 0x02 }, + /* 5da */ { 0x02, 0x02 }, + /* 5db */ { 0x02, 0x02 }, + /* 5dc */ { 0x02, 0x02 }, + /* 5dd */ { 0x02, 0x02 }, + /* 5de */ { 0x02, 0x02 }, + /* 5df */ { 0x02, 0x02 }, + /* 5e0 */ { 0x02, 0x02 }, + /* 5e1 */ { 0x02, 0x02 }, + /* 5e2 */ { 0x02, 0x02 }, + /* 5e3 */ { 0x02, 0x02 }, + /* 5e4 */ { 0x02, 0x02 }, + /* 5e5 */ { 0x02, 0x02 }, + /* 5e6 */ { 0x02, 0x02 }, + /* 5e7 */ { 0x02, 0x02 }, + /* 5e8 */ { 0x02, 0x02 }, + /* 5e9 */ { 0x02, 0x02 }, + /* 5ea */ { 0x02, 0x02 }, + /* 5eb */ { 0x02, 0x02 }, + /* 5ec */ { 0x02, 0x02 }, + /* 5ed */ { 0x02, 0x02 }, + /* 5ee */ { 0x02, 0x02 }, + /* 5ef */ { 0x02, 0x02 }, + /* 5f0 */ { 0x02, 0x02 }, + /* 5f1 */ { 0x02, 0x02 }, + /* 5f2 */ { 0x02, 0x02 }, + /* 5f3 */ { 0x02, 0x02 }, + /* 5f4 */ { 0x02, 0x02 }, + /* 5f5 */ { 0x02, 0x02 }, + /* 5f6 */ { 0x02, 0x02 }, + /* 5f7 */ { 0x02, 0x02 }, + /* 5f8 */ { 0x02, 0x02 }, + /* 5f9 */ { 0x02, 0x02 }, + /* 5fa */ { 0x02, 0x02 }, + /* 5fb */ { 0x02, 0x02 }, + /* 5fc */ { 0x02, 0x02 }, + /* 5fd */ { 0x02, 0x02 }, + /* 5fe */ { 0x02, 0x02 }, + /* 5ff */ { 0x02, 0x02 }, + /* 600 */ { 0x02, 0x02 }, + /* 601 */ { 0x02, 0x02 }, + /* 602 */ { 0x02, 0x02 }, + /* 603 */ { 0x02, 0x02 }, + /* 604 */ { 0x02, 0x02 }, + /* 605 */ { 0x02, 0x02 }, + /* 606 */ { 0x02, 0x02 }, + /* 607 */ { 0x02, 0x02 }, + /* 608 */ { 0x02, 0x02 }, + /* 609 */ { 0x02, 0x02 }, + /* 60a */ { 0x02, 0x02 }, + /* 60b */ { 0x02, 0x02 }, + /* 60c */ { 0x02, 0x02 }, + /* 60d */ { 0x02, 0x02 }, + /* 60e */ { 0x02, 0x02 }, + /* 60f */ { 0x02, 0x02 }, + /* 610 */ { 0x02, 0x02 }, + /* 611 */ { 0x02, 0x02 }, + /* 612 */ { 0x02, 0x02 }, + /* 613 */ { 0x02, 0x02 }, + /* 614 */ { 0x02, 0x02 }, + /* 615 */ { 0x02, 0x02 }, + /* 616 */ { 0x02, 0x02 }, + /* 617 */ { 0x02, 0x02 }, + /* 618 */ { 0x02, 0x02 }, + /* 619 */ { 0x02, 0x02 }, + /* 61a */ { 0x02, 0x02 }, + /* 61b */ { 0x02, 0x02 }, + /* 61c */ { 0x02, 0x02 }, + /* 61d */ { 0x02, 0x02 }, + /* 61e */ { 0x02, 0x02 }, + /* 61f */ { 0x02, 0x02 }, + /* 620 */ { 0x02, 0x02 }, + /* 621 */ { 0x02, 0x02 }, + /* 622 */ { 0x02, 0x02 }, + /* 623 */ { 0x02, 0x02 }, + /* 624 */ { 0x02, 0x02 }, + /* 625 */ { 0x02, 0x02 }, + /* 626 */ { 0x02, 0x02 }, + /* 627 */ { 0x02, 0x02 }, + /* 628 */ { 0x02, 0x02 }, + /* 629 */ { 0x02, 0x02 }, + /* 62a */ { 0x02, 0x02 }, + /* 62b */ { 0x02, 0x02 }, + /* 62c */ { 0x02, 0x02 }, + /* 62d */ { 0x02, 0x02 }, + /* 62e */ { 0x02, 0x02 }, + /* 62f */ { 0x02, 0x02 }, + /* 630 */ { 0x02, 0x02 }, + /* 631 */ { 0x02, 0x02 }, + /* 632 */ { 0x02, 0x02 }, + /* 633 */ { 0x02, 0x02 }, + /* 634 */ { 0x02, 0x02 }, + /* 635 */ { 0x02, 0x02 }, + /* 636 */ { 0x02, 0x02 }, + /* 637 */ { 0x02, 0x02 }, + /* 638 */ { 0x02, 0x02 }, + /* 639 */ { 0x02, 0x02 }, + /* 63a */ { 0x02, 0x02 }, + /* 63b */ { 0x02, 0x02 }, + /* 63c */ { 0x02, 0x02 }, + /* 63d */ { 0x02, 0x02 }, + /* 63e */ { 0x02, 0x02 }, + /* 63f */ { 0x02, 0x02 }, + /* 640 */ { 0x02, 0x02 }, + /* 641 */ { 0x02, 0x02 }, + /* 642 */ { 0x02, 0x02 }, + /* 643 */ { 0x02, 0x02 }, + /* 644 */ { 0x02, 0x02 }, + /* 645 */ { 0x02, 0x02 }, + /* 646 */ { 0x02, 0x02 }, + /* 647 */ { 0x02, 0x02 }, + /* 648 */ { 0x02, 0x02 }, + /* 649 */ { 0x02, 0x02 }, + /* 64a */ { 0x02, 0x02 }, + /* 64b */ { 0x02, 0x02 }, + /* 64c */ { 0x02, 0x02 }, + /* 64d */ { 0x02, 0x02 }, + /* 64e */ { 0x02, 0x02 }, + /* 64f */ { 0x02, 0x02 }, + /* 650 */ { 0x02, 0x02 }, + /* 651 */ { 0x02, 0x02 }, + /* 652 */ { 0x02, 0x02 }, + /* 653 */ { 0x02, 0x02 }, + /* 654 */ { 0x02, 0x02 }, + /* 655 */ { 0x02, 0x02 }, + /* 656 */ { 0x02, 0x02 }, + /* 657 */ { 0x02, 0x02 }, + /* 658 */ { 0x02, 0x02 }, + /* 659 */ { 0x02, 0x02 }, + /* 65a */ { 0x02, 0x02 }, + /* 65b */ { 0x02, 0x02 }, + /* 65c */ { 0x02, 0x02 }, + /* 65d */ { 0x02, 0x02 }, + /* 65e */ { 0x02, 0x02 }, + /* 65f */ { 0x02, 0x02 }, + /* 660 */ { 0x02, 0x02 }, + /* 661 */ { 0x02, 0x02 }, + /* 662 */ { 0x02, 0x02 }, + /* 663 */ { 0x02, 0x02 }, + /* 664 */ { 0x02, 0x02 }, + /* 665 */ { 0x02, 0x02 }, + /* 666 */ { 0x02, 0x02 }, + /* 667 */ { 0x02, 0x02 }, + /* 668 */ { 0x02, 0x02 }, + /* 669 */ { 0x02, 0x02 }, + /* 66a */ { 0x02, 0x02 }, + /* 66b */ { 0x02, 0x02 }, + /* 66c */ { 0x02, 0x02 }, + /* 66d */ { 0x02, 0x02 }, + /* 66e */ { 0x02, 0x02 }, + /* 66f */ { 0x02, 0x02 }, + /* 670 */ { 0x02, 0x02 }, + /* 671 */ { 0x02, 0x02 }, + /* 672 */ { 0x02, 0x02 }, + /* 673 */ { 0x02, 0x02 }, + /* 674 */ { 0x02, 0x02 }, + /* 675 */ { 0x02, 0x02 }, + /* 676 */ { 0x02, 0x02 }, + /* 677 */ { 0x02, 0x02 }, + /* 678 */ { 0x02, 0x02 }, + /* 679 */ { 0x02, 0x02 }, + /* 67a */ { 0x02, 0x02 }, + /* 67b */ { 0x02, 0x02 }, + /* 67c */ { 0x02, 0x02 }, + /* 67d */ { 0x02, 0x02 }, + /* 67e */ { 0x02, 0x02 }, + /* 67f */ { 0x02, 0x02 }, + /* 680 */ { 0x02, 0x02 }, + /* 681 */ { 0x02, 0x02 }, + /* 682 */ { 0x02, 0x02 }, + /* 683 */ { 0x02, 0x02 }, + /* 684 */ { 0x02, 0x02 }, + /* 685 */ { 0x02, 0x02 }, + /* 686 */ { 0x02, 0x02 }, + /* 687 */ { 0x02, 0x02 }, + /* 688 */ { 0x02, 0x02 }, + /* 689 */ { 0x02, 0x02 }, + /* 68a */ { 0x02, 0x02 }, + /* 68b */ { 0x02, 0x02 }, + /* 68c */ { 0x02, 0x02 }, + /* 68d */ { 0x02, 0x02 }, + /* 68e */ { 0x02, 0x02 }, + /* 68f */ { 0x02, 0x02 }, + /* 690 */ { 0x02, 0x02 }, + /* 691 */ { 0x02, 0x02 }, + /* 692 */ { 0x02, 0x02 }, + /* 693 */ { 0x02, 0x02 }, + /* 694 */ { 0x02, 0x02 }, + /* 695 */ { 0x02, 0x02 }, + /* 696 */ { 0x02, 0x02 }, + /* 697 */ { 0x02, 0x02 }, + /* 698 */ { 0x02, 0x02 }, + /* 699 */ { 0x02, 0x02 }, + /* 69a */ { 0x02, 0x02 }, + /* 69b */ { 0x02, 0x02 }, + /* 69c */ { 0x02, 0x02 }, + /* 69d */ { 0x02, 0x02 }, + /* 69e */ { 0x02, 0x02 }, + /* 69f */ { 0x02, 0x02 }, + /* 6a0 */ { 0x02, 0x02 }, + /* 6a1 */ { 0x02, 0x02 }, + /* 6a2 */ { 0x02, 0x02 }, + /* 6a3 */ { 0x02, 0x02 }, + /* 6a4 */ { 0x02, 0x02 }, + /* 6a5 */ { 0x02, 0x02 }, + /* 6a6 */ { 0x02, 0x02 }, + /* 6a7 */ { 0x02, 0x02 }, + /* 6a8 */ { 0x02, 0x02 }, + /* 6a9 */ { 0x02, 0x02 }, + /* 6aa */ { 0x02, 0x02 }, + /* 6ab */ { 0x02, 0x02 }, + /* 6ac */ { 0x02, 0x02 }, + /* 6ad */ { 0x02, 0x02 }, + /* 6ae */ { 0x02, 0x02 }, + /* 6af */ { 0x02, 0x02 }, + /* 6b0 */ { 0x02, 0x02 }, + /* 6b1 */ { 0x02, 0x02 }, + /* 6b2 */ { 0x02, 0x02 }, + /* 6b3 */ { 0x02, 0x02 }, + /* 6b4 */ { 0x02, 0x02 }, + /* 6b5 */ { 0x02, 0x02 }, + /* 6b6 */ { 0x02, 0x02 }, + /* 6b7 */ { 0x02, 0x02 }, + /* 6b8 */ { 0x02, 0x02 }, + /* 6b9 */ { 0x02, 0x02 }, + /* 6ba */ { 0x02, 0x02 }, + /* 6bb */ { 0x02, 0x02 }, + /* 6bc */ { 0x02, 0x02 }, + /* 6bd */ { 0x02, 0x02 }, + /* 6be */ { 0x02, 0x02 }, + /* 6bf */ { 0x02, 0x02 }, + /* 6c0 */ { 0x02, 0x02 }, + /* 6c1 */ { 0x02, 0x02 }, + /* 6c2 */ { 0x02, 0x02 }, + /* 6c3 */ { 0x02, 0x02 }, + /* 6c4 */ { 0x02, 0x02 }, + /* 6c5 */ { 0x02, 0x02 }, + /* 6c6 */ { 0x02, 0x02 }, + /* 6c7 */ { 0x02, 0x02 }, + /* 6c8 */ { 0x02, 0x02 }, + /* 6c9 */ { 0x02, 0x02 }, + /* 6ca */ { 0x02, 0x02 }, + /* 6cb */ { 0x02, 0x02 }, + /* 6cc */ { 0x02, 0x02 }, + /* 6cd */ { 0x02, 0x02 }, + /* 6ce */ { 0x02, 0x02 }, + /* 6cf */ { 0x02, 0x02 }, + /* 6d0 */ { 0x02, 0x02 }, + /* 6d1 */ { 0x02, 0x02 }, + /* 6d2 */ { 0x02, 0x02 }, + /* 6d3 */ { 0x02, 0x02 }, + /* 6d4 */ { 0x02, 0x02 }, + /* 6d5 */ { 0x02, 0x02 }, + /* 6d6 */ { 0x02, 0x02 }, + /* 6d7 */ { 0x02, 0x02 }, + /* 6d8 */ { 0x02, 0x02 }, + /* 6d9 */ { 0x02, 0x02 }, + /* 6da */ { 0x02, 0x02 }, + /* 6db */ { 0x02, 0x02 }, + /* 6dc */ { 0x02, 0x02 }, + /* 6dd */ { 0x02, 0x02 }, + /* 6de */ { 0x02, 0x02 }, + /* 6df */ { 0x02, 0x02 }, + /* 6e0 */ { 0x02, 0x02 }, + /* 6e1 */ { 0x02, 0x02 }, + /* 6e2 */ { 0x02, 0x02 }, + /* 6e3 */ { 0x02, 0x02 }, + /* 6e4 */ { 0x02, 0x02 }, + /* 6e5 */ { 0x02, 0x02 }, + /* 6e6 */ { 0x02, 0x02 }, + /* 6e7 */ { 0x02, 0x02 }, + /* 6e8 */ { 0x02, 0x02 }, + /* 6e9 */ { 0x02, 0x02 }, + /* 6ea */ { 0x02, 0x02 }, + /* 6eb */ { 0x02, 0x02 }, + /* 6ec */ { 0x02, 0x02 }, + /* 6ed */ { 0x02, 0x02 }, + /* 6ee */ { 0x02, 0x02 }, + /* 6ef */ { 0x02, 0x02 }, + /* 6f0 */ { 0x02, 0x02 }, + /* 6f1 */ { 0x02, 0x02 }, + /* 6f2 */ { 0x02, 0x02 }, + /* 6f3 */ { 0x02, 0x02 }, + /* 6f4 */ { 0x02, 0x02 }, + /* 6f5 */ { 0x02, 0x02 }, + /* 6f6 */ { 0x02, 0x02 }, + /* 6f7 */ { 0x02, 0x02 }, + /* 6f8 */ { 0x02, 0x02 }, + /* 6f9 */ { 0x02, 0x02 }, + /* 6fa */ { 0x02, 0x02 }, + /* 6fb */ { 0x02, 0x02 }, + /* 6fc */ { 0x02, 0x02 }, + /* 6fd */ { 0x02, 0x02 }, + /* 6fe */ { 0x02, 0x02 }, + /* 6ff */ { 0x02, 0x02 }, + /* 700 */ { 0x02, 0x02 }, + /* 701 */ { 0x02, 0x02 }, + /* 702 */ { 0x02, 0x02 }, + /* 703 */ { 0x02, 0x02 }, + /* 704 */ { 0x02, 0x02 }, + /* 705 */ { 0x02, 0x02 }, + /* 706 */ { 0x02, 0x02 }, + /* 707 */ { 0x02, 0x02 }, + /* 708 */ { 0x02, 0x02 }, + /* 709 */ { 0x02, 0x02 }, + /* 70a */ { 0x02, 0x02 }, + /* 70b */ { 0x02, 0x02 }, + /* 70c */ { 0x02, 0x02 }, + /* 70d */ { 0x02, 0x02 }, + /* 70e */ { 0x02, 0x02 }, + /* 70f */ { 0x02, 0x02 }, + /* 710 */ { 0x02, 0x02 }, + /* 711 */ { 0x02, 0x02 }, + /* 712 */ { 0x02, 0x02 }, + /* 713 */ { 0x02, 0x02 }, + /* 714 */ { 0x02, 0x02 }, + /* 715 */ { 0x02, 0x02 }, + /* 716 */ { 0x02, 0x02 }, + /* 717 */ { 0x02, 0x02 }, + /* 718 */ { 0x02, 0x02 }, + /* 719 */ { 0x02, 0x02 }, + /* 71a */ { 0x02, 0x02 }, + /* 71b */ { 0x02, 0x02 }, + /* 71c */ { 0x02, 0x02 }, + /* 71d */ { 0x02, 0x02 }, + /* 71e */ { 0x02, 0x02 }, + /* 71f */ { 0x02, 0x02 }, + /* 720 */ { 0x02, 0x02 }, + /* 721 */ { 0x02, 0x02 }, + /* 722 */ { 0x02, 0x02 }, + /* 723 */ { 0x02, 0x02 }, + /* 724 */ { 0x02, 0x02 }, + /* 725 */ { 0x02, 0x02 }, + /* 726 */ { 0x02, 0x02 }, + /* 727 */ { 0x02, 0x02 }, + /* 728 */ { 0x02, 0x02 }, + /* 729 */ { 0x02, 0x02 }, + /* 72a */ { 0x02, 0x02 }, + /* 72b */ { 0x02, 0x02 }, + /* 72c */ { 0x02, 0x02 }, + /* 72d */ { 0x02, 0x02 }, + /* 72e */ { 0x02, 0x02 }, + /* 72f */ { 0x02, 0x02 }, + /* 730 */ { 0x02, 0x02 }, + /* 731 */ { 0x02, 0x02 }, + /* 732 */ { 0x02, 0x02 }, + /* 733 */ { 0x02, 0x02 }, + /* 734 */ { 0x02, 0x02 }, + /* 735 */ { 0x02, 0x02 }, + /* 736 */ { 0x02, 0x02 }, + /* 737 */ { 0x02, 0x02 }, + /* 738 */ { 0x02, 0x02 }, + /* 739 */ { 0x02, 0x02 }, + /* 73a */ { 0x02, 0x02 }, + /* 73b */ { 0x02, 0x02 }, + /* 73c */ { 0x02, 0x02 }, + /* 73d */ { 0x02, 0x02 }, + /* 73e */ { 0x02, 0x02 }, + /* 73f */ { 0x02, 0x02 }, + /* 740 */ { 0x02, 0x02 }, + /* 741 */ { 0x02, 0x02 }, + /* 742 */ { 0x02, 0x02 }, + /* 743 */ { 0x02, 0x02 }, + /* 744 */ { 0x02, 0x02 }, + /* 745 */ { 0x02, 0x02 }, + /* 746 */ { 0x02, 0x02 }, + /* 747 */ { 0x02, 0x02 }, + /* 748 */ { 0x02, 0x02 }, + /* 749 */ { 0x02, 0x02 }, + /* 74a */ { 0x02, 0x02 }, + /* 74b */ { 0x02, 0x02 }, + /* 74c */ { 0x02, 0x02 }, + /* 74d */ { 0x02, 0x02 }, + /* 74e */ { 0x02, 0x02 }, + /* 74f */ { 0x02, 0x02 }, + /* 750 */ { 0x02, 0x02 }, + /* 751 */ { 0x02, 0x02 }, + /* 752 */ { 0x02, 0x02 }, + /* 753 */ { 0x02, 0x02 }, + /* 754 */ { 0x02, 0x02 }, + /* 755 */ { 0x02, 0x02 }, + /* 756 */ { 0x02, 0x02 }, + /* 757 */ { 0x02, 0x02 }, + /* 758 */ { 0x02, 0x02 }, + /* 759 */ { 0x02, 0x02 }, + /* 75a */ { 0x02, 0x02 }, + /* 75b */ { 0x02, 0x02 }, + /* 75c */ { 0x02, 0x02 }, + /* 75d */ { 0x02, 0x02 }, + /* 75e */ { 0x02, 0x02 }, + /* 75f */ { 0x02, 0x02 }, + /* 760 */ { 0x02, 0x02 }, + /* 761 */ { 0x02, 0x02 }, + /* 762 */ { 0x02, 0x02 }, + /* 763 */ { 0x02, 0x02 }, + /* 764 */ { 0x02, 0x02 }, + /* 765 */ { 0x02, 0x02 }, + /* 766 */ { 0x02, 0x02 }, + /* 767 */ { 0x02, 0x02 }, + /* 768 */ { 0x02, 0x02 }, + /* 769 */ { 0x02, 0x02 }, + /* 76a */ { 0x02, 0x02 }, + /* 76b */ { 0x02, 0x02 }, + /* 76c */ { 0x02, 0x02 }, + /* 76d */ { 0x02, 0x02 }, + /* 76e */ { 0x02, 0x02 }, + /* 76f */ { 0x02, 0x02 }, + /* 770 */ { 0x02, 0x02 }, + /* 771 */ { 0x02, 0x02 }, + /* 772 */ { 0x02, 0x02 }, + /* 773 */ { 0x02, 0x02 }, + /* 774 */ { 0x02, 0x02 }, + /* 775 */ { 0x02, 0x02 }, + /* 776 */ { 0x02, 0x02 }, + /* 777 */ { 0x02, 0x02 }, + /* 778 */ { 0x02, 0x02 }, + /* 779 */ { 0x02, 0x02 }, + /* 77a */ { 0x02, 0x02 }, + /* 77b */ { 0x02, 0x02 }, + /* 77c */ { 0x02, 0x02 }, + /* 77d */ { 0x02, 0x02 }, + /* 77e */ { 0x02, 0x02 }, + /* 77f */ { 0x02, 0x02 }, + /* 780 */ { 0x02, 0x02 }, + /* 781 */ { 0x02, 0x02 }, + /* 782 */ { 0x02, 0x02 }, + /* 783 */ { 0x02, 0x02 }, + /* 784 */ { 0x02, 0x02 }, + /* 785 */ { 0x02, 0x02 }, + /* 786 */ { 0x02, 0x02 }, + /* 787 */ { 0x02, 0x02 }, + /* 788 */ { 0x02, 0x02 }, + /* 789 */ { 0x02, 0x02 }, + /* 78a */ { 0x02, 0x02 }, + /* 78b */ { 0x02, 0x02 }, + /* 78c */ { 0x02, 0x02 }, + /* 78d */ { 0x02, 0x02 }, + /* 78e */ { 0x02, 0x02 }, + /* 78f */ { 0x02, 0x02 }, + /* 790 */ { 0x02, 0x02 }, + /* 791 */ { 0x02, 0x02 }, + /* 792 */ { 0x02, 0x02 }, + /* 793 */ { 0x02, 0x02 }, + /* 794 */ { 0x02, 0x02 }, + /* 795 */ { 0x02, 0x02 }, + /* 796 */ { 0x02, 0x02 }, + /* 797 */ { 0x02, 0x02 }, + /* 798 */ { 0x02, 0x02 }, + /* 799 */ { 0x02, 0x02 }, + /* 79a */ { 0x02, 0x02 }, + /* 79b */ { 0x02, 0x02 }, + /* 79c */ { 0x02, 0x02 }, + /* 79d */ { 0x02, 0x02 }, + /* 79e */ { 0x02, 0x02 }, + /* 79f */ { 0x02, 0x02 }, + /* 7a0 */ { 0x02, 0x02 }, + /* 7a1 */ { 0x02, 0x02 }, + /* 7a2 */ { 0x02, 0x02 }, + /* 7a3 */ { 0x02, 0x02 }, + /* 7a4 */ { 0x02, 0x02 }, + /* 7a5 */ { 0x02, 0x02 }, + /* 7a6 */ { 0x02, 0x02 }, + /* 7a7 */ { 0x02, 0x02 }, + /* 7a8 */ { 0x02, 0x02 }, + /* 7a9 */ { 0x02, 0x02 }, + /* 7aa */ { 0x02, 0x02 }, + /* 7ab */ { 0x02, 0x02 }, + /* 7ac */ { 0x02, 0x02 }, + /* 7ad */ { 0x02, 0x02 }, + /* 7ae */ { 0x02, 0x02 }, + /* 7af */ { 0x02, 0x02 }, + /* 7b0 */ { 0x02, 0x02 }, + /* 7b1 */ { 0x02, 0x02 }, + /* 7b2 */ { 0x02, 0x02 }, + /* 7b3 */ { 0x02, 0x02 }, + /* 7b4 */ { 0x02, 0x02 }, + /* 7b5 */ { 0x02, 0x02 }, + /* 7b6 */ { 0x02, 0x02 }, + /* 7b7 */ { 0x02, 0x02 }, + /* 7b8 */ { 0x02, 0x02 }, + /* 7b9 */ { 0x02, 0x02 }, + /* 7ba */ { 0x02, 0x02 }, + /* 7bb */ { 0x02, 0x02 }, + /* 7bc */ { 0x02, 0x02 }, + /* 7bd */ { 0x02, 0x02 }, + /* 7be */ { 0x02, 0x02 }, + /* 7bf */ { 0x02, 0x02 }, + /* 7c0 */ { 0x02, 0x02 }, + /* 7c1 */ { 0x02, 0x02 }, + /* 7c2 */ { 0x02, 0x02 }, + /* 7c3 */ { 0x02, 0x02 }, + /* 7c4 */ { 0x02, 0x02 }, + /* 7c5 */ { 0x02, 0x02 }, + /* 7c6 */ { 0x02, 0x02 }, + /* 7c7 */ { 0x02, 0x02 }, + /* 7c8 */ { 0x02, 0x02 }, + /* 7c9 */ { 0x02, 0x02 }, + /* 7ca */ { 0x02, 0x02 }, + /* 7cb */ { 0x02, 0x02 }, + /* 7cc */ { 0x02, 0x02 }, + /* 7cd */ { 0x02, 0x02 }, + /* 7ce */ { 0x02, 0x02 }, + /* 7cf */ { 0x02, 0x02 }, + /* 7d0 */ { 0x02, 0x02 }, + /* 7d1 */ { 0x02, 0x02 }, + /* 7d2 */ { 0x02, 0x02 }, + /* 7d3 */ { 0x02, 0x02 }, + /* 7d4 */ { 0x02, 0x02 }, + /* 7d5 */ { 0x02, 0x02 }, + /* 7d6 */ { 0x02, 0x02 }, + /* 7d7 */ { 0x02, 0x02 }, + /* 7d8 */ { 0x02, 0x02 }, + /* 7d9 */ { 0x02, 0x02 }, + /* 7da */ { 0x02, 0x02 }, + /* 7db */ { 0x02, 0x02 }, + /* 7dc */ { 0x02, 0x02 }, + /* 7dd */ { 0x02, 0x02 }, + /* 7de */ { 0x02, 0x02 }, + /* 7df */ { 0x02, 0x02 }, + /* 7e0 */ { 0x02, 0x02 }, + /* 7e1 */ { 0x02, 0x02 }, + /* 7e2 */ { 0x02, 0x02 }, + /* 7e3 */ { 0x02, 0x02 }, + /* 7e4 */ { 0x02, 0x02 }, + /* 7e5 */ { 0x02, 0x02 }, + /* 7e6 */ { 0x02, 0x02 }, + /* 7e7 */ { 0x02, 0x02 }, + /* 7e8 */ { 0x02, 0x02 }, + /* 7e9 */ { 0x02, 0x02 }, + /* 7ea */ { 0x02, 0x02 }, + /* 7eb */ { 0x02, 0x02 }, + /* 7ec */ { 0x02, 0x02 }, + /* 7ed */ { 0x02, 0x02 }, + /* 7ee */ { 0x02, 0x02 }, + /* 7ef */ { 0x02, 0x02 }, + /* 7f0 */ { 0x02, 0x02 }, + /* 7f1 */ { 0x02, 0x02 }, + /* 7f2 */ { 0x02, 0x02 }, + /* 7f3 */ { 0x02, 0x02 }, + /* 7f4 */ { 0x02, 0x02 }, + /* 7f5 */ { 0x02, 0x02 }, + /* 7f6 */ { 0x02, 0x02 }, + /* 7f7 */ { 0x02, 0x02 }, + /* 7f8 */ { 0x02, 0x02 }, + /* 7f9 */ { 0x02, 0x02 }, + /* 7fa */ { 0x02, 0x02 }, + /* 7fb */ { 0x02, 0x02 }, + /* 7fc */ { 0x02, 0x02 }, + /* 7fd */ { 0x02, 0x02 }, + /* 7fe */ { 0x02, 0x02 }, + /* 7ff */ { 0x02, 0x02 }, + /* 800 */ { 0x11, 0x03 }, + /* 801 */ { 0x11, 0x03 }, + /* 802 */ { 0x11, 0x03 }, + /* 803 */ { 0x11, 0x03 }, + /* 804 */ { 0x11, 0x03 }, + /* 805 */ { 0x11, 0x03 }, + /* 806 */ { 0x11, 0x03 }, + /* 807 */ { 0x11, 0x03 }, + /* 808 */ { 0x11, 0x03 }, + /* 809 */ { 0x11, 0x03 }, + /* 80a */ { 0x11, 0x03 }, + /* 80b */ { 0x11, 0x03 }, + /* 80c */ { 0x11, 0x03 }, + /* 80d */ { 0x11, 0x03 }, + /* 80e */ { 0x11, 0x03 }, + /* 80f */ { 0x11, 0x03 }, + /* 810 */ { 0x11, 0x03 }, + /* 811 */ { 0x11, 0x03 }, + /* 812 */ { 0x11, 0x03 }, + /* 813 */ { 0x11, 0x03 }, + /* 814 */ { 0x11, 0x03 }, + /* 815 */ { 0x11, 0x03 }, + /* 816 */ { 0x11, 0x03 }, + /* 817 */ { 0x11, 0x03 }, + /* 818 */ { 0x11, 0x03 }, + /* 819 */ { 0x11, 0x03 }, + /* 81a */ { 0x11, 0x03 }, + /* 81b */ { 0x11, 0x03 }, + /* 81c */ { 0x11, 0x03 }, + /* 81d */ { 0x11, 0x03 }, + /* 81e */ { 0x11, 0x03 }, + /* 81f */ { 0x11, 0x03 }, + /* 820 */ { 0x11, 0x03 }, + /* 821 */ { 0x11, 0x03 }, + /* 822 */ { 0x11, 0x03 }, + /* 823 */ { 0x11, 0x03 }, + /* 824 */ { 0x11, 0x03 }, + /* 825 */ { 0x11, 0x03 }, + /* 826 */ { 0x11, 0x03 }, + /* 827 */ { 0x11, 0x03 }, + /* 828 */ { 0x11, 0x03 }, + /* 829 */ { 0x11, 0x03 }, + /* 82a */ { 0x11, 0x03 }, + /* 82b */ { 0x11, 0x03 }, + /* 82c */ { 0x11, 0x03 }, + /* 82d */ { 0x11, 0x03 }, + /* 82e */ { 0x11, 0x03 }, + /* 82f */ { 0x11, 0x03 }, + /* 830 */ { 0x11, 0x03 }, + /* 831 */ { 0x11, 0x03 }, + /* 832 */ { 0x11, 0x03 }, + /* 833 */ { 0x11, 0x03 }, + /* 834 */ { 0x11, 0x03 }, + /* 835 */ { 0x11, 0x03 }, + /* 836 */ { 0x11, 0x03 }, + /* 837 */ { 0x11, 0x03 }, + /* 838 */ { 0x11, 0x03 }, + /* 839 */ { 0x11, 0x03 }, + /* 83a */ { 0x11, 0x03 }, + /* 83b */ { 0x11, 0x03 }, + /* 83c */ { 0x11, 0x03 }, + /* 83d */ { 0x11, 0x03 }, + /* 83e */ { 0x11, 0x03 }, + /* 83f */ { 0x11, 0x03 }, + /* 840 */ { 0x11, 0x03 }, + /* 841 */ { 0x11, 0x03 }, + /* 842 */ { 0x11, 0x03 }, + /* 843 */ { 0x11, 0x03 }, + /* 844 */ { 0x11, 0x03 }, + /* 845 */ { 0x11, 0x03 }, + /* 846 */ { 0x11, 0x03 }, + /* 847 */ { 0x11, 0x03 }, + /* 848 */ { 0x11, 0x03 }, + /* 849 */ { 0x11, 0x03 }, + /* 84a */ { 0x11, 0x03 }, + /* 84b */ { 0x11, 0x03 }, + /* 84c */ { 0x11, 0x03 }, + /* 84d */ { 0x11, 0x03 }, + /* 84e */ { 0x11, 0x03 }, + /* 84f */ { 0x11, 0x03 }, + /* 850 */ { 0x11, 0x03 }, + /* 851 */ { 0x11, 0x03 }, + /* 852 */ { 0x11, 0x03 }, + /* 853 */ { 0x11, 0x03 }, + /* 854 */ { 0x11, 0x03 }, + /* 855 */ { 0x11, 0x03 }, + /* 856 */ { 0x11, 0x03 }, + /* 857 */ { 0x11, 0x03 }, + /* 858 */ { 0x11, 0x03 }, + /* 859 */ { 0x11, 0x03 }, + /* 85a */ { 0x11, 0x03 }, + /* 85b */ { 0x11, 0x03 }, + /* 85c */ { 0x11, 0x03 }, + /* 85d */ { 0x11, 0x03 }, + /* 85e */ { 0x11, 0x03 }, + /* 85f */ { 0x11, 0x03 }, + /* 860 */ { 0x11, 0x03 }, + /* 861 */ { 0x11, 0x03 }, + /* 862 */ { 0x11, 0x03 }, + /* 863 */ { 0x11, 0x03 }, + /* 864 */ { 0x11, 0x03 }, + /* 865 */ { 0x11, 0x03 }, + /* 866 */ { 0x11, 0x03 }, + /* 867 */ { 0x11, 0x03 }, + /* 868 */ { 0x11, 0x03 }, + /* 869 */ { 0x11, 0x03 }, + /* 86a */ { 0x11, 0x03 }, + /* 86b */ { 0x11, 0x03 }, + /* 86c */ { 0x11, 0x03 }, + /* 86d */ { 0x11, 0x03 }, + /* 86e */ { 0x11, 0x03 }, + /* 86f */ { 0x11, 0x03 }, + /* 870 */ { 0x11, 0x03 }, + /* 871 */ { 0x11, 0x03 }, + /* 872 */ { 0x11, 0x03 }, + /* 873 */ { 0x11, 0x03 }, + /* 874 */ { 0x11, 0x03 }, + /* 875 */ { 0x11, 0x03 }, + /* 876 */ { 0x11, 0x03 }, + /* 877 */ { 0x11, 0x03 }, + /* 878 */ { 0x11, 0x03 }, + /* 879 */ { 0x11, 0x03 }, + /* 87a */ { 0x11, 0x03 }, + /* 87b */ { 0x11, 0x03 }, + /* 87c */ { 0x11, 0x03 }, + /* 87d */ { 0x11, 0x03 }, + /* 87e */ { 0x11, 0x03 }, + /* 87f */ { 0x11, 0x03 }, + /* 880 */ { 0x11, 0x03 }, + /* 881 */ { 0x11, 0x03 }, + /* 882 */ { 0x11, 0x03 }, + /* 883 */ { 0x11, 0x03 }, + /* 884 */ { 0x11, 0x03 }, + /* 885 */ { 0x11, 0x03 }, + /* 886 */ { 0x11, 0x03 }, + /* 887 */ { 0x11, 0x03 }, + /* 888 */ { 0x11, 0x03 }, + /* 889 */ { 0x11, 0x03 }, + /* 88a */ { 0x11, 0x03 }, + /* 88b */ { 0x11, 0x03 }, + /* 88c */ { 0x11, 0x03 }, + /* 88d */ { 0x11, 0x03 }, + /* 88e */ { 0x11, 0x03 }, + /* 88f */ { 0x11, 0x03 }, + /* 890 */ { 0x11, 0x03 }, + /* 891 */ { 0x11, 0x03 }, + /* 892 */ { 0x11, 0x03 }, + /* 893 */ { 0x11, 0x03 }, + /* 894 */ { 0x11, 0x03 }, + /* 895 */ { 0x11, 0x03 }, + /* 896 */ { 0x11, 0x03 }, + /* 897 */ { 0x11, 0x03 }, + /* 898 */ { 0x11, 0x03 }, + /* 899 */ { 0x11, 0x03 }, + /* 89a */ { 0x11, 0x03 }, + /* 89b */ { 0x11, 0x03 }, + /* 89c */ { 0x11, 0x03 }, + /* 89d */ { 0x11, 0x03 }, + /* 89e */ { 0x11, 0x03 }, + /* 89f */ { 0x11, 0x03 }, + /* 8a0 */ { 0x11, 0x03 }, + /* 8a1 */ { 0x11, 0x03 }, + /* 8a2 */ { 0x11, 0x03 }, + /* 8a3 */ { 0x11, 0x03 }, + /* 8a4 */ { 0x11, 0x03 }, + /* 8a5 */ { 0x11, 0x03 }, + /* 8a6 */ { 0x11, 0x03 }, + /* 8a7 */ { 0x11, 0x03 }, + /* 8a8 */ { 0x11, 0x03 }, + /* 8a9 */ { 0x11, 0x03 }, + /* 8aa */ { 0x11, 0x03 }, + /* 8ab */ { 0x11, 0x03 }, + /* 8ac */ { 0x11, 0x03 }, + /* 8ad */ { 0x11, 0x03 }, + /* 8ae */ { 0x11, 0x03 }, + /* 8af */ { 0x11, 0x03 }, + /* 8b0 */ { 0x11, 0x03 }, + /* 8b1 */ { 0x11, 0x03 }, + /* 8b2 */ { 0x11, 0x03 }, + /* 8b3 */ { 0x11, 0x03 }, + /* 8b4 */ { 0x11, 0x03 }, + /* 8b5 */ { 0x11, 0x03 }, + /* 8b6 */ { 0x11, 0x03 }, + /* 8b7 */ { 0x11, 0x03 }, + /* 8b8 */ { 0x11, 0x03 }, + /* 8b9 */ { 0x11, 0x03 }, + /* 8ba */ { 0x11, 0x03 }, + /* 8bb */ { 0x11, 0x03 }, + /* 8bc */ { 0x11, 0x03 }, + /* 8bd */ { 0x11, 0x03 }, + /* 8be */ { 0x11, 0x03 }, + /* 8bf */ { 0x11, 0x03 }, + /* 8c0 */ { 0x11, 0x03 }, + /* 8c1 */ { 0x11, 0x03 }, + /* 8c2 */ { 0x11, 0x03 }, + /* 8c3 */ { 0x11, 0x03 }, + /* 8c4 */ { 0x11, 0x03 }, + /* 8c5 */ { 0x11, 0x03 }, + /* 8c6 */ { 0x11, 0x03 }, + /* 8c7 */ { 0x11, 0x03 }, + /* 8c8 */ { 0x11, 0x03 }, + /* 8c9 */ { 0x11, 0x03 }, + /* 8ca */ { 0x11, 0x03 }, + /* 8cb */ { 0x11, 0x03 }, + /* 8cc */ { 0x11, 0x03 }, + /* 8cd */ { 0x11, 0x03 }, + /* 8ce */ { 0x11, 0x03 }, + /* 8cf */ { 0x11, 0x03 }, + /* 8d0 */ { 0x11, 0x03 }, + /* 8d1 */ { 0x11, 0x03 }, + /* 8d2 */ { 0x11, 0x03 }, + /* 8d3 */ { 0x11, 0x03 }, + /* 8d4 */ { 0x11, 0x03 }, + /* 8d5 */ { 0x11, 0x03 }, + /* 8d6 */ { 0x11, 0x03 }, + /* 8d7 */ { 0x11, 0x03 }, + /* 8d8 */ { 0x11, 0x03 }, + /* 8d9 */ { 0x11, 0x03 }, + /* 8da */ { 0x11, 0x03 }, + /* 8db */ { 0x11, 0x03 }, + /* 8dc */ { 0x11, 0x03 }, + /* 8dd */ { 0x11, 0x03 }, + /* 8de */ { 0x11, 0x03 }, + /* 8df */ { 0x11, 0x03 }, + /* 8e0 */ { 0x11, 0x03 }, + /* 8e1 */ { 0x11, 0x03 }, + /* 8e2 */ { 0x11, 0x03 }, + /* 8e3 */ { 0x11, 0x03 }, + /* 8e4 */ { 0x11, 0x03 }, + /* 8e5 */ { 0x11, 0x03 }, + /* 8e6 */ { 0x11, 0x03 }, + /* 8e7 */ { 0x11, 0x03 }, + /* 8e8 */ { 0x11, 0x03 }, + /* 8e9 */ { 0x11, 0x03 }, + /* 8ea */ { 0x11, 0x03 }, + /* 8eb */ { 0x11, 0x03 }, + /* 8ec */ { 0x11, 0x03 }, + /* 8ed */ { 0x11, 0x03 }, + /* 8ee */ { 0x11, 0x03 }, + /* 8ef */ { 0x11, 0x03 }, + /* 8f0 */ { 0x11, 0x03 }, + /* 8f1 */ { 0x11, 0x03 }, + /* 8f2 */ { 0x11, 0x03 }, + /* 8f3 */ { 0x11, 0x03 }, + /* 8f4 */ { 0x11, 0x03 }, + /* 8f5 */ { 0x11, 0x03 }, + /* 8f6 */ { 0x11, 0x03 }, + /* 8f7 */ { 0x11, 0x03 }, + /* 8f8 */ { 0x11, 0x03 }, + /* 8f9 */ { 0x11, 0x03 }, + /* 8fa */ { 0x11, 0x03 }, + /* 8fb */ { 0x11, 0x03 }, + /* 8fc */ { 0x11, 0x03 }, + /* 8fd */ { 0x11, 0x03 }, + /* 8fe */ { 0x11, 0x03 }, + /* 8ff */ { 0x11, 0x03 }, + /* 900 */ { 0x11, 0x03 }, + /* 901 */ { 0x11, 0x03 }, + /* 902 */ { 0x11, 0x03 }, + /* 903 */ { 0x11, 0x03 }, + /* 904 */ { 0x11, 0x03 }, + /* 905 */ { 0x11, 0x03 }, + /* 906 */ { 0x11, 0x03 }, + /* 907 */ { 0x11, 0x03 }, + /* 908 */ { 0x11, 0x03 }, + /* 909 */ { 0x11, 0x03 }, + /* 90a */ { 0x11, 0x03 }, + /* 90b */ { 0x11, 0x03 }, + /* 90c */ { 0x11, 0x03 }, + /* 90d */ { 0x11, 0x03 }, + /* 90e */ { 0x11, 0x03 }, + /* 90f */ { 0x11, 0x03 }, + /* 910 */ { 0x11, 0x03 }, + /* 911 */ { 0x11, 0x03 }, + /* 912 */ { 0x11, 0x03 }, + /* 913 */ { 0x11, 0x03 }, + /* 914 */ { 0x11, 0x03 }, + /* 915 */ { 0x11, 0x03 }, + /* 916 */ { 0x11, 0x03 }, + /* 917 */ { 0x11, 0x03 }, + /* 918 */ { 0x11, 0x03 }, + /* 919 */ { 0x11, 0x03 }, + /* 91a */ { 0x11, 0x03 }, + /* 91b */ { 0x11, 0x03 }, + /* 91c */ { 0x11, 0x03 }, + /* 91d */ { 0x11, 0x03 }, + /* 91e */ { 0x11, 0x03 }, + /* 91f */ { 0x11, 0x03 }, + /* 920 */ { 0x11, 0x03 }, + /* 921 */ { 0x11, 0x03 }, + /* 922 */ { 0x11, 0x03 }, + /* 923 */ { 0x11, 0x03 }, + /* 924 */ { 0x11, 0x03 }, + /* 925 */ { 0x11, 0x03 }, + /* 926 */ { 0x11, 0x03 }, + /* 927 */ { 0x11, 0x03 }, + /* 928 */ { 0x11, 0x03 }, + /* 929 */ { 0x11, 0x03 }, + /* 92a */ { 0x11, 0x03 }, + /* 92b */ { 0x11, 0x03 }, + /* 92c */ { 0x11, 0x03 }, + /* 92d */ { 0x11, 0x03 }, + /* 92e */ { 0x11, 0x03 }, + /* 92f */ { 0x11, 0x03 }, + /* 930 */ { 0x11, 0x03 }, + /* 931 */ { 0x11, 0x03 }, + /* 932 */ { 0x11, 0x03 }, + /* 933 */ { 0x11, 0x03 }, + /* 934 */ { 0x11, 0x03 }, + /* 935 */ { 0x11, 0x03 }, + /* 936 */ { 0x11, 0x03 }, + /* 937 */ { 0x11, 0x03 }, + /* 938 */ { 0x11, 0x03 }, + /* 939 */ { 0x11, 0x03 }, + /* 93a */ { 0x11, 0x03 }, + /* 93b */ { 0x11, 0x03 }, + /* 93c */ { 0x11, 0x03 }, + /* 93d */ { 0x11, 0x03 }, + /* 93e */ { 0x11, 0x03 }, + /* 93f */ { 0x11, 0x03 }, + /* 940 */ { 0x11, 0x03 }, + /* 941 */ { 0x11, 0x03 }, + /* 942 */ { 0x11, 0x03 }, + /* 943 */ { 0x11, 0x03 }, + /* 944 */ { 0x11, 0x03 }, + /* 945 */ { 0x11, 0x03 }, + /* 946 */ { 0x11, 0x03 }, + /* 947 */ { 0x11, 0x03 }, + /* 948 */ { 0x11, 0x03 }, + /* 949 */ { 0x11, 0x03 }, + /* 94a */ { 0x11, 0x03 }, + /* 94b */ { 0x11, 0x03 }, + /* 94c */ { 0x11, 0x03 }, + /* 94d */ { 0x11, 0x03 }, + /* 94e */ { 0x11, 0x03 }, + /* 94f */ { 0x11, 0x03 }, + /* 950 */ { 0x11, 0x03 }, + /* 951 */ { 0x11, 0x03 }, + /* 952 */ { 0x11, 0x03 }, + /* 953 */ { 0x11, 0x03 }, + /* 954 */ { 0x11, 0x03 }, + /* 955 */ { 0x11, 0x03 }, + /* 956 */ { 0x11, 0x03 }, + /* 957 */ { 0x11, 0x03 }, + /* 958 */ { 0x11, 0x03 }, + /* 959 */ { 0x11, 0x03 }, + /* 95a */ { 0x11, 0x03 }, + /* 95b */ { 0x11, 0x03 }, + /* 95c */ { 0x11, 0x03 }, + /* 95d */ { 0x11, 0x03 }, + /* 95e */ { 0x11, 0x03 }, + /* 95f */ { 0x11, 0x03 }, + /* 960 */ { 0x11, 0x03 }, + /* 961 */ { 0x11, 0x03 }, + /* 962 */ { 0x11, 0x03 }, + /* 963 */ { 0x11, 0x03 }, + /* 964 */ { 0x11, 0x03 }, + /* 965 */ { 0x11, 0x03 }, + /* 966 */ { 0x11, 0x03 }, + /* 967 */ { 0x11, 0x03 }, + /* 968 */ { 0x11, 0x03 }, + /* 969 */ { 0x11, 0x03 }, + /* 96a */ { 0x11, 0x03 }, + /* 96b */ { 0x11, 0x03 }, + /* 96c */ { 0x11, 0x03 }, + /* 96d */ { 0x11, 0x03 }, + /* 96e */ { 0x11, 0x03 }, + /* 96f */ { 0x11, 0x03 }, + /* 970 */ { 0x11, 0x03 }, + /* 971 */ { 0x11, 0x03 }, + /* 972 */ { 0x11, 0x03 }, + /* 973 */ { 0x11, 0x03 }, + /* 974 */ { 0x11, 0x03 }, + /* 975 */ { 0x11, 0x03 }, + /* 976 */ { 0x11, 0x03 }, + /* 977 */ { 0x11, 0x03 }, + /* 978 */ { 0x11, 0x03 }, + /* 979 */ { 0x11, 0x03 }, + /* 97a */ { 0x11, 0x03 }, + /* 97b */ { 0x11, 0x03 }, + /* 97c */ { 0x11, 0x03 }, + /* 97d */ { 0x11, 0x03 }, + /* 97e */ { 0x11, 0x03 }, + /* 97f */ { 0x11, 0x03 }, + /* 980 */ { 0x11, 0x03 }, + /* 981 */ { 0x11, 0x03 }, + /* 982 */ { 0x11, 0x03 }, + /* 983 */ { 0x11, 0x03 }, + /* 984 */ { 0x11, 0x03 }, + /* 985 */ { 0x11, 0x03 }, + /* 986 */ { 0x11, 0x03 }, + /* 987 */ { 0x11, 0x03 }, + /* 988 */ { 0x11, 0x03 }, + /* 989 */ { 0x11, 0x03 }, + /* 98a */ { 0x11, 0x03 }, + /* 98b */ { 0x11, 0x03 }, + /* 98c */ { 0x11, 0x03 }, + /* 98d */ { 0x11, 0x03 }, + /* 98e */ { 0x11, 0x03 }, + /* 98f */ { 0x11, 0x03 }, + /* 990 */ { 0x11, 0x03 }, + /* 991 */ { 0x11, 0x03 }, + /* 992 */ { 0x11, 0x03 }, + /* 993 */ { 0x11, 0x03 }, + /* 994 */ { 0x11, 0x03 }, + /* 995 */ { 0x11, 0x03 }, + /* 996 */ { 0x11, 0x03 }, + /* 997 */ { 0x11, 0x03 }, + /* 998 */ { 0x11, 0x03 }, + /* 999 */ { 0x11, 0x03 }, + /* 99a */ { 0x11, 0x03 }, + /* 99b */ { 0x11, 0x03 }, + /* 99c */ { 0x11, 0x03 }, + /* 99d */ { 0x11, 0x03 }, + /* 99e */ { 0x11, 0x03 }, + /* 99f */ { 0x11, 0x03 }, + /* 9a0 */ { 0x11, 0x03 }, + /* 9a1 */ { 0x11, 0x03 }, + /* 9a2 */ { 0x11, 0x03 }, + /* 9a3 */ { 0x11, 0x03 }, + /* 9a4 */ { 0x11, 0x03 }, + /* 9a5 */ { 0x11, 0x03 }, + /* 9a6 */ { 0x11, 0x03 }, + /* 9a7 */ { 0x11, 0x03 }, + /* 9a8 */ { 0x11, 0x03 }, + /* 9a9 */ { 0x11, 0x03 }, + /* 9aa */ { 0x11, 0x03 }, + /* 9ab */ { 0x11, 0x03 }, + /* 9ac */ { 0x11, 0x03 }, + /* 9ad */ { 0x11, 0x03 }, + /* 9ae */ { 0x11, 0x03 }, + /* 9af */ { 0x11, 0x03 }, + /* 9b0 */ { 0x11, 0x03 }, + /* 9b1 */ { 0x11, 0x03 }, + /* 9b2 */ { 0x11, 0x03 }, + /* 9b3 */ { 0x11, 0x03 }, + /* 9b4 */ { 0x11, 0x03 }, + /* 9b5 */ { 0x11, 0x03 }, + /* 9b6 */ { 0x11, 0x03 }, + /* 9b7 */ { 0x11, 0x03 }, + /* 9b8 */ { 0x11, 0x03 }, + /* 9b9 */ { 0x11, 0x03 }, + /* 9ba */ { 0x11, 0x03 }, + /* 9bb */ { 0x11, 0x03 }, + /* 9bc */ { 0x11, 0x03 }, + /* 9bd */ { 0x11, 0x03 }, + /* 9be */ { 0x11, 0x03 }, + /* 9bf */ { 0x11, 0x03 }, + /* 9c0 */ { 0x11, 0x03 }, + /* 9c1 */ { 0x11, 0x03 }, + /* 9c2 */ { 0x11, 0x03 }, + /* 9c3 */ { 0x11, 0x03 }, + /* 9c4 */ { 0x11, 0x03 }, + /* 9c5 */ { 0x11, 0x03 }, + /* 9c6 */ { 0x11, 0x03 }, + /* 9c7 */ { 0x11, 0x03 }, + /* 9c8 */ { 0x11, 0x03 }, + /* 9c9 */ { 0x11, 0x03 }, + /* 9ca */ { 0x11, 0x03 }, + /* 9cb */ { 0x11, 0x03 }, + /* 9cc */ { 0x11, 0x03 }, + /* 9cd */ { 0x11, 0x03 }, + /* 9ce */ { 0x11, 0x03 }, + /* 9cf */ { 0x11, 0x03 }, + /* 9d0 */ { 0x11, 0x03 }, + /* 9d1 */ { 0x11, 0x03 }, + /* 9d2 */ { 0x11, 0x03 }, + /* 9d3 */ { 0x11, 0x03 }, + /* 9d4 */ { 0x11, 0x03 }, + /* 9d5 */ { 0x11, 0x03 }, + /* 9d6 */ { 0x11, 0x03 }, + /* 9d7 */ { 0x11, 0x03 }, + /* 9d8 */ { 0x11, 0x03 }, + /* 9d9 */ { 0x11, 0x03 }, + /* 9da */ { 0x11, 0x03 }, + /* 9db */ { 0x11, 0x03 }, + /* 9dc */ { 0x11, 0x03 }, + /* 9dd */ { 0x11, 0x03 }, + /* 9de */ { 0x11, 0x03 }, + /* 9df */ { 0x11, 0x03 }, + /* 9e0 */ { 0x11, 0x03 }, + /* 9e1 */ { 0x11, 0x03 }, + /* 9e2 */ { 0x11, 0x03 }, + /* 9e3 */ { 0x11, 0x03 }, + /* 9e4 */ { 0x11, 0x03 }, + /* 9e5 */ { 0x11, 0x03 }, + /* 9e6 */ { 0x11, 0x03 }, + /* 9e7 */ { 0x11, 0x03 }, + /* 9e8 */ { 0x11, 0x03 }, + /* 9e9 */ { 0x11, 0x03 }, + /* 9ea */ { 0x11, 0x03 }, + /* 9eb */ { 0x11, 0x03 }, + /* 9ec */ { 0x11, 0x03 }, + /* 9ed */ { 0x11, 0x03 }, + /* 9ee */ { 0x11, 0x03 }, + /* 9ef */ { 0x11, 0x03 }, + /* 9f0 */ { 0x11, 0x03 }, + /* 9f1 */ { 0x11, 0x03 }, + /* 9f2 */ { 0x11, 0x03 }, + /* 9f3 */ { 0x11, 0x03 }, + /* 9f4 */ { 0x11, 0x03 }, + /* 9f5 */ { 0x11, 0x03 }, + /* 9f6 */ { 0x11, 0x03 }, + /* 9f7 */ { 0x11, 0x03 }, + /* 9f8 */ { 0x11, 0x03 }, + /* 9f9 */ { 0x11, 0x03 }, + /* 9fa */ { 0x11, 0x03 }, + /* 9fb */ { 0x11, 0x03 }, + /* 9fc */ { 0x11, 0x03 }, + /* 9fd */ { 0x11, 0x03 }, + /* 9fe */ { 0x11, 0x03 }, + /* 9ff */ { 0x11, 0x03 }, + /* a00 */ { 0x03, 0x04 }, + /* a01 */ { 0x03, 0x04 }, + /* a02 */ { 0x03, 0x04 }, + /* a03 */ { 0x03, 0x04 }, + /* a04 */ { 0x03, 0x04 }, + /* a05 */ { 0x03, 0x04 }, + /* a06 */ { 0x03, 0x04 }, + /* a07 */ { 0x03, 0x04 }, + /* a08 */ { 0x03, 0x04 }, + /* a09 */ { 0x03, 0x04 }, + /* a0a */ { 0x03, 0x04 }, + /* a0b */ { 0x03, 0x04 }, + /* a0c */ { 0x03, 0x04 }, + /* a0d */ { 0x03, 0x04 }, + /* a0e */ { 0x03, 0x04 }, + /* a0f */ { 0x03, 0x04 }, + /* a10 */ { 0x03, 0x04 }, + /* a11 */ { 0x03, 0x04 }, + /* a12 */ { 0x03, 0x04 }, + /* a13 */ { 0x03, 0x04 }, + /* a14 */ { 0x03, 0x04 }, + /* a15 */ { 0x03, 0x04 }, + /* a16 */ { 0x03, 0x04 }, + /* a17 */ { 0x03, 0x04 }, + /* a18 */ { 0x03, 0x04 }, + /* a19 */ { 0x03, 0x04 }, + /* a1a */ { 0x03, 0x04 }, + /* a1b */ { 0x03, 0x04 }, + /* a1c */ { 0x03, 0x04 }, + /* a1d */ { 0x03, 0x04 }, + /* a1e */ { 0x03, 0x04 }, + /* a1f */ { 0x03, 0x04 }, + /* a20 */ { 0x03, 0x04 }, + /* a21 */ { 0x03, 0x04 }, + /* a22 */ { 0x03, 0x04 }, + /* a23 */ { 0x03, 0x04 }, + /* a24 */ { 0x03, 0x04 }, + /* a25 */ { 0x03, 0x04 }, + /* a26 */ { 0x03, 0x04 }, + /* a27 */ { 0x03, 0x04 }, + /* a28 */ { 0x03, 0x04 }, + /* a29 */ { 0x03, 0x04 }, + /* a2a */ { 0x03, 0x04 }, + /* a2b */ { 0x03, 0x04 }, + /* a2c */ { 0x03, 0x04 }, + /* a2d */ { 0x03, 0x04 }, + /* a2e */ { 0x03, 0x04 }, + /* a2f */ { 0x03, 0x04 }, + /* a30 */ { 0x03, 0x04 }, + /* a31 */ { 0x03, 0x04 }, + /* a32 */ { 0x03, 0x04 }, + /* a33 */ { 0x03, 0x04 }, + /* a34 */ { 0x03, 0x04 }, + /* a35 */ { 0x03, 0x04 }, + /* a36 */ { 0x03, 0x04 }, + /* a37 */ { 0x03, 0x04 }, + /* a38 */ { 0x03, 0x04 }, + /* a39 */ { 0x03, 0x04 }, + /* a3a */ { 0x03, 0x04 }, + /* a3b */ { 0x03, 0x04 }, + /* a3c */ { 0x03, 0x04 }, + /* a3d */ { 0x03, 0x04 }, + /* a3e */ { 0x03, 0x04 }, + /* a3f */ { 0x03, 0x04 }, + /* a40 */ { 0x03, 0x04 }, + /* a41 */ { 0x03, 0x04 }, + /* a42 */ { 0x03, 0x04 }, + /* a43 */ { 0x03, 0x04 }, + /* a44 */ { 0x03, 0x04 }, + /* a45 */ { 0x03, 0x04 }, + /* a46 */ { 0x03, 0x04 }, + /* a47 */ { 0x03, 0x04 }, + /* a48 */ { 0x03, 0x04 }, + /* a49 */ { 0x03, 0x04 }, + /* a4a */ { 0x03, 0x04 }, + /* a4b */ { 0x03, 0x04 }, + /* a4c */ { 0x03, 0x04 }, + /* a4d */ { 0x03, 0x04 }, + /* a4e */ { 0x03, 0x04 }, + /* a4f */ { 0x03, 0x04 }, + /* a50 */ { 0x03, 0x04 }, + /* a51 */ { 0x03, 0x04 }, + /* a52 */ { 0x03, 0x04 }, + /* a53 */ { 0x03, 0x04 }, + /* a54 */ { 0x03, 0x04 }, + /* a55 */ { 0x03, 0x04 }, + /* a56 */ { 0x03, 0x04 }, + /* a57 */ { 0x03, 0x04 }, + /* a58 */ { 0x03, 0x04 }, + /* a59 */ { 0x03, 0x04 }, + /* a5a */ { 0x03, 0x04 }, + /* a5b */ { 0x03, 0x04 }, + /* a5c */ { 0x03, 0x04 }, + /* a5d */ { 0x03, 0x04 }, + /* a5e */ { 0x03, 0x04 }, + /* a5f */ { 0x03, 0x04 }, + /* a60 */ { 0x03, 0x04 }, + /* a61 */ { 0x03, 0x04 }, + /* a62 */ { 0x03, 0x04 }, + /* a63 */ { 0x03, 0x04 }, + /* a64 */ { 0x03, 0x04 }, + /* a65 */ { 0x03, 0x04 }, + /* a66 */ { 0x03, 0x04 }, + /* a67 */ { 0x03, 0x04 }, + /* a68 */ { 0x03, 0x04 }, + /* a69 */ { 0x03, 0x04 }, + /* a6a */ { 0x03, 0x04 }, + /* a6b */ { 0x03, 0x04 }, + /* a6c */ { 0x03, 0x04 }, + /* a6d */ { 0x03, 0x04 }, + /* a6e */ { 0x03, 0x04 }, + /* a6f */ { 0x03, 0x04 }, + /* a70 */ { 0x03, 0x04 }, + /* a71 */ { 0x03, 0x04 }, + /* a72 */ { 0x03, 0x04 }, + /* a73 */ { 0x03, 0x04 }, + /* a74 */ { 0x03, 0x04 }, + /* a75 */ { 0x03, 0x04 }, + /* a76 */ { 0x03, 0x04 }, + /* a77 */ { 0x03, 0x04 }, + /* a78 */ { 0x03, 0x04 }, + /* a79 */ { 0x03, 0x04 }, + /* a7a */ { 0x03, 0x04 }, + /* a7b */ { 0x03, 0x04 }, + /* a7c */ { 0x03, 0x04 }, + /* a7d */ { 0x03, 0x04 }, + /* a7e */ { 0x03, 0x04 }, + /* a7f */ { 0x03, 0x04 }, + /* a80 */ { 0x03, 0x04 }, + /* a81 */ { 0x03, 0x04 }, + /* a82 */ { 0x03, 0x04 }, + /* a83 */ { 0x03, 0x04 }, + /* a84 */ { 0x03, 0x04 }, + /* a85 */ { 0x03, 0x04 }, + /* a86 */ { 0x03, 0x04 }, + /* a87 */ { 0x03, 0x04 }, + /* a88 */ { 0x03, 0x04 }, + /* a89 */ { 0x03, 0x04 }, + /* a8a */ { 0x03, 0x04 }, + /* a8b */ { 0x03, 0x04 }, + /* a8c */ { 0x03, 0x04 }, + /* a8d */ { 0x03, 0x04 }, + /* a8e */ { 0x03, 0x04 }, + /* a8f */ { 0x03, 0x04 }, + /* a90 */ { 0x03, 0x04 }, + /* a91 */ { 0x03, 0x04 }, + /* a92 */ { 0x03, 0x04 }, + /* a93 */ { 0x03, 0x04 }, + /* a94 */ { 0x03, 0x04 }, + /* a95 */ { 0x03, 0x04 }, + /* a96 */ { 0x03, 0x04 }, + /* a97 */ { 0x03, 0x04 }, + /* a98 */ { 0x03, 0x04 }, + /* a99 */ { 0x03, 0x04 }, + /* a9a */ { 0x03, 0x04 }, + /* a9b */ { 0x03, 0x04 }, + /* a9c */ { 0x03, 0x04 }, + /* a9d */ { 0x03, 0x04 }, + /* a9e */ { 0x03, 0x04 }, + /* a9f */ { 0x03, 0x04 }, + /* aa0 */ { 0x03, 0x04 }, + /* aa1 */ { 0x03, 0x04 }, + /* aa2 */ { 0x03, 0x04 }, + /* aa3 */ { 0x03, 0x04 }, + /* aa4 */ { 0x03, 0x04 }, + /* aa5 */ { 0x03, 0x04 }, + /* aa6 */ { 0x03, 0x04 }, + /* aa7 */ { 0x03, 0x04 }, + /* aa8 */ { 0x03, 0x04 }, + /* aa9 */ { 0x03, 0x04 }, + /* aaa */ { 0x03, 0x04 }, + /* aab */ { 0x03, 0x04 }, + /* aac */ { 0x03, 0x04 }, + /* aad */ { 0x03, 0x04 }, + /* aae */ { 0x03, 0x04 }, + /* aaf */ { 0x03, 0x04 }, + /* ab0 */ { 0x03, 0x04 }, + /* ab1 */ { 0x03, 0x04 }, + /* ab2 */ { 0x03, 0x04 }, + /* ab3 */ { 0x03, 0x04 }, + /* ab4 */ { 0x03, 0x04 }, + /* ab5 */ { 0x03, 0x04 }, + /* ab6 */ { 0x03, 0x04 }, + /* ab7 */ { 0x03, 0x04 }, + /* ab8 */ { 0x03, 0x04 }, + /* ab9 */ { 0x03, 0x04 }, + /* aba */ { 0x03, 0x04 }, + /* abb */ { 0x03, 0x04 }, + /* abc */ { 0x03, 0x04 }, + /* abd */ { 0x03, 0x04 }, + /* abe */ { 0x03, 0x04 }, + /* abf */ { 0x03, 0x04 }, + /* ac0 */ { 0x03, 0x04 }, + /* ac1 */ { 0x03, 0x04 }, + /* ac2 */ { 0x03, 0x04 }, + /* ac3 */ { 0x03, 0x04 }, + /* ac4 */ { 0x03, 0x04 }, + /* ac5 */ { 0x03, 0x04 }, + /* ac6 */ { 0x03, 0x04 }, + /* ac7 */ { 0x03, 0x04 }, + /* ac8 */ { 0x03, 0x04 }, + /* ac9 */ { 0x03, 0x04 }, + /* aca */ { 0x03, 0x04 }, + /* acb */ { 0x03, 0x04 }, + /* acc */ { 0x03, 0x04 }, + /* acd */ { 0x03, 0x04 }, + /* ace */ { 0x03, 0x04 }, + /* acf */ { 0x03, 0x04 }, + /* ad0 */ { 0x03, 0x04 }, + /* ad1 */ { 0x03, 0x04 }, + /* ad2 */ { 0x03, 0x04 }, + /* ad3 */ { 0x03, 0x04 }, + /* ad4 */ { 0x03, 0x04 }, + /* ad5 */ { 0x03, 0x04 }, + /* ad6 */ { 0x03, 0x04 }, + /* ad7 */ { 0x03, 0x04 }, + /* ad8 */ { 0x03, 0x04 }, + /* ad9 */ { 0x03, 0x04 }, + /* ada */ { 0x03, 0x04 }, + /* adb */ { 0x03, 0x04 }, + /* adc */ { 0x03, 0x04 }, + /* add */ { 0x03, 0x04 }, + /* ade */ { 0x03, 0x04 }, + /* adf */ { 0x03, 0x04 }, + /* ae0 */ { 0x03, 0x04 }, + /* ae1 */ { 0x03, 0x04 }, + /* ae2 */ { 0x03, 0x04 }, + /* ae3 */ { 0x03, 0x04 }, + /* ae4 */ { 0x03, 0x04 }, + /* ae5 */ { 0x03, 0x04 }, + /* ae6 */ { 0x03, 0x04 }, + /* ae7 */ { 0x03, 0x04 }, + /* ae8 */ { 0x03, 0x04 }, + /* ae9 */ { 0x03, 0x04 }, + /* aea */ { 0x03, 0x04 }, + /* aeb */ { 0x03, 0x04 }, + /* aec */ { 0x03, 0x04 }, + /* aed */ { 0x03, 0x04 }, + /* aee */ { 0x03, 0x04 }, + /* aef */ { 0x03, 0x04 }, + /* af0 */ { 0x03, 0x04 }, + /* af1 */ { 0x03, 0x04 }, + /* af2 */ { 0x03, 0x04 }, + /* af3 */ { 0x03, 0x04 }, + /* af4 */ { 0x03, 0x04 }, + /* af5 */ { 0x03, 0x04 }, + /* af6 */ { 0x03, 0x04 }, + /* af7 */ { 0x03, 0x04 }, + /* af8 */ { 0x03, 0x04 }, + /* af9 */ { 0x03, 0x04 }, + /* afa */ { 0x03, 0x04 }, + /* afb */ { 0x03, 0x04 }, + /* afc */ { 0x03, 0x04 }, + /* afd */ { 0x03, 0x04 }, + /* afe */ { 0x03, 0x04 }, + /* aff */ { 0x03, 0x04 }, + /* b00 */ { 0x21, 0x04 }, + /* b01 */ { 0x21, 0x04 }, + /* b02 */ { 0x21, 0x04 }, + /* b03 */ { 0x21, 0x04 }, + /* b04 */ { 0x21, 0x04 }, + /* b05 */ { 0x21, 0x04 }, + /* b06 */ { 0x21, 0x04 }, + /* b07 */ { 0x21, 0x04 }, + /* b08 */ { 0x21, 0x04 }, + /* b09 */ { 0x21, 0x04 }, + /* b0a */ { 0x21, 0x04 }, + /* b0b */ { 0x21, 0x04 }, + /* b0c */ { 0x21, 0x04 }, + /* b0d */ { 0x21, 0x04 }, + /* b0e */ { 0x21, 0x04 }, + /* b0f */ { 0x21, 0x04 }, + /* b10 */ { 0x21, 0x04 }, + /* b11 */ { 0x21, 0x04 }, + /* b12 */ { 0x21, 0x04 }, + /* b13 */ { 0x21, 0x04 }, + /* b14 */ { 0x21, 0x04 }, + /* b15 */ { 0x21, 0x04 }, + /* b16 */ { 0x21, 0x04 }, + /* b17 */ { 0x21, 0x04 }, + /* b18 */ { 0x21, 0x04 }, + /* b19 */ { 0x21, 0x04 }, + /* b1a */ { 0x21, 0x04 }, + /* b1b */ { 0x21, 0x04 }, + /* b1c */ { 0x21, 0x04 }, + /* b1d */ { 0x21, 0x04 }, + /* b1e */ { 0x21, 0x04 }, + /* b1f */ { 0x21, 0x04 }, + /* b20 */ { 0x21, 0x04 }, + /* b21 */ { 0x21, 0x04 }, + /* b22 */ { 0x21, 0x04 }, + /* b23 */ { 0x21, 0x04 }, + /* b24 */ { 0x21, 0x04 }, + /* b25 */ { 0x21, 0x04 }, + /* b26 */ { 0x21, 0x04 }, + /* b27 */ { 0x21, 0x04 }, + /* b28 */ { 0x21, 0x04 }, + /* b29 */ { 0x21, 0x04 }, + /* b2a */ { 0x21, 0x04 }, + /* b2b */ { 0x21, 0x04 }, + /* b2c */ { 0x21, 0x04 }, + /* b2d */ { 0x21, 0x04 }, + /* b2e */ { 0x21, 0x04 }, + /* b2f */ { 0x21, 0x04 }, + /* b30 */ { 0x21, 0x04 }, + /* b31 */ { 0x21, 0x04 }, + /* b32 */ { 0x21, 0x04 }, + /* b33 */ { 0x21, 0x04 }, + /* b34 */ { 0x21, 0x04 }, + /* b35 */ { 0x21, 0x04 }, + /* b36 */ { 0x21, 0x04 }, + /* b37 */ { 0x21, 0x04 }, + /* b38 */ { 0x21, 0x04 }, + /* b39 */ { 0x21, 0x04 }, + /* b3a */ { 0x21, 0x04 }, + /* b3b */ { 0x21, 0x04 }, + /* b3c */ { 0x21, 0x04 }, + /* b3d */ { 0x21, 0x04 }, + /* b3e */ { 0x21, 0x04 }, + /* b3f */ { 0x21, 0x04 }, + /* b40 */ { 0x21, 0x04 }, + /* b41 */ { 0x21, 0x04 }, + /* b42 */ { 0x21, 0x04 }, + /* b43 */ { 0x21, 0x04 }, + /* b44 */ { 0x21, 0x04 }, + /* b45 */ { 0x21, 0x04 }, + /* b46 */ { 0x21, 0x04 }, + /* b47 */ { 0x21, 0x04 }, + /* b48 */ { 0x21, 0x04 }, + /* b49 */ { 0x21, 0x04 }, + /* b4a */ { 0x21, 0x04 }, + /* b4b */ { 0x21, 0x04 }, + /* b4c */ { 0x21, 0x04 }, + /* b4d */ { 0x21, 0x04 }, + /* b4e */ { 0x21, 0x04 }, + /* b4f */ { 0x21, 0x04 }, + /* b50 */ { 0x21, 0x04 }, + /* b51 */ { 0x21, 0x04 }, + /* b52 */ { 0x21, 0x04 }, + /* b53 */ { 0x21, 0x04 }, + /* b54 */ { 0x21, 0x04 }, + /* b55 */ { 0x21, 0x04 }, + /* b56 */ { 0x21, 0x04 }, + /* b57 */ { 0x21, 0x04 }, + /* b58 */ { 0x21, 0x04 }, + /* b59 */ { 0x21, 0x04 }, + /* b5a */ { 0x21, 0x04 }, + /* b5b */ { 0x21, 0x04 }, + /* b5c */ { 0x21, 0x04 }, + /* b5d */ { 0x21, 0x04 }, + /* b5e */ { 0x21, 0x04 }, + /* b5f */ { 0x21, 0x04 }, + /* b60 */ { 0x21, 0x04 }, + /* b61 */ { 0x21, 0x04 }, + /* b62 */ { 0x21, 0x04 }, + /* b63 */ { 0x21, 0x04 }, + /* b64 */ { 0x21, 0x04 }, + /* b65 */ { 0x21, 0x04 }, + /* b66 */ { 0x21, 0x04 }, + /* b67 */ { 0x21, 0x04 }, + /* b68 */ { 0x21, 0x04 }, + /* b69 */ { 0x21, 0x04 }, + /* b6a */ { 0x21, 0x04 }, + /* b6b */ { 0x21, 0x04 }, + /* b6c */ { 0x21, 0x04 }, + /* b6d */ { 0x21, 0x04 }, + /* b6e */ { 0x21, 0x04 }, + /* b6f */ { 0x21, 0x04 }, + /* b70 */ { 0x21, 0x04 }, + /* b71 */ { 0x21, 0x04 }, + /* b72 */ { 0x21, 0x04 }, + /* b73 */ { 0x21, 0x04 }, + /* b74 */ { 0x21, 0x04 }, + /* b75 */ { 0x21, 0x04 }, + /* b76 */ { 0x21, 0x04 }, + /* b77 */ { 0x21, 0x04 }, + /* b78 */ { 0x21, 0x04 }, + /* b79 */ { 0x21, 0x04 }, + /* b7a */ { 0x21, 0x04 }, + /* b7b */ { 0x21, 0x04 }, + /* b7c */ { 0x21, 0x04 }, + /* b7d */ { 0x21, 0x04 }, + /* b7e */ { 0x21, 0x04 }, + /* b7f */ { 0x21, 0x04 }, + /* b80 */ { 0x21, 0x04 }, + /* b81 */ { 0x21, 0x04 }, + /* b82 */ { 0x21, 0x04 }, + /* b83 */ { 0x21, 0x04 }, + /* b84 */ { 0x21, 0x04 }, + /* b85 */ { 0x21, 0x04 }, + /* b86 */ { 0x21, 0x04 }, + /* b87 */ { 0x21, 0x04 }, + /* b88 */ { 0x21, 0x04 }, + /* b89 */ { 0x21, 0x04 }, + /* b8a */ { 0x21, 0x04 }, + /* b8b */ { 0x21, 0x04 }, + /* b8c */ { 0x21, 0x04 }, + /* b8d */ { 0x21, 0x04 }, + /* b8e */ { 0x21, 0x04 }, + /* b8f */ { 0x21, 0x04 }, + /* b90 */ { 0x21, 0x04 }, + /* b91 */ { 0x21, 0x04 }, + /* b92 */ { 0x21, 0x04 }, + /* b93 */ { 0x21, 0x04 }, + /* b94 */ { 0x21, 0x04 }, + /* b95 */ { 0x21, 0x04 }, + /* b96 */ { 0x21, 0x04 }, + /* b97 */ { 0x21, 0x04 }, + /* b98 */ { 0x21, 0x04 }, + /* b99 */ { 0x21, 0x04 }, + /* b9a */ { 0x21, 0x04 }, + /* b9b */ { 0x21, 0x04 }, + /* b9c */ { 0x21, 0x04 }, + /* b9d */ { 0x21, 0x04 }, + /* b9e */ { 0x21, 0x04 }, + /* b9f */ { 0x21, 0x04 }, + /* ba0 */ { 0x21, 0x04 }, + /* ba1 */ { 0x21, 0x04 }, + /* ba2 */ { 0x21, 0x04 }, + /* ba3 */ { 0x21, 0x04 }, + /* ba4 */ { 0x21, 0x04 }, + /* ba5 */ { 0x21, 0x04 }, + /* ba6 */ { 0x21, 0x04 }, + /* ba7 */ { 0x21, 0x04 }, + /* ba8 */ { 0x21, 0x04 }, + /* ba9 */ { 0x21, 0x04 }, + /* baa */ { 0x21, 0x04 }, + /* bab */ { 0x21, 0x04 }, + /* bac */ { 0x21, 0x04 }, + /* bad */ { 0x21, 0x04 }, + /* bae */ { 0x21, 0x04 }, + /* baf */ { 0x21, 0x04 }, + /* bb0 */ { 0x21, 0x04 }, + /* bb1 */ { 0x21, 0x04 }, + /* bb2 */ { 0x21, 0x04 }, + /* bb3 */ { 0x21, 0x04 }, + /* bb4 */ { 0x21, 0x04 }, + /* bb5 */ { 0x21, 0x04 }, + /* bb6 */ { 0x21, 0x04 }, + /* bb7 */ { 0x21, 0x04 }, + /* bb8 */ { 0x21, 0x04 }, + /* bb9 */ { 0x21, 0x04 }, + /* bba */ { 0x21, 0x04 }, + /* bbb */ { 0x21, 0x04 }, + /* bbc */ { 0x21, 0x04 }, + /* bbd */ { 0x21, 0x04 }, + /* bbe */ { 0x21, 0x04 }, + /* bbf */ { 0x21, 0x04 }, + /* bc0 */ { 0x21, 0x04 }, + /* bc1 */ { 0x21, 0x04 }, + /* bc2 */ { 0x21, 0x04 }, + /* bc3 */ { 0x21, 0x04 }, + /* bc4 */ { 0x21, 0x04 }, + /* bc5 */ { 0x21, 0x04 }, + /* bc6 */ { 0x21, 0x04 }, + /* bc7 */ { 0x21, 0x04 }, + /* bc8 */ { 0x21, 0x04 }, + /* bc9 */ { 0x21, 0x04 }, + /* bca */ { 0x21, 0x04 }, + /* bcb */ { 0x21, 0x04 }, + /* bcc */ { 0x21, 0x04 }, + /* bcd */ { 0x21, 0x04 }, + /* bce */ { 0x21, 0x04 }, + /* bcf */ { 0x21, 0x04 }, + /* bd0 */ { 0x21, 0x04 }, + /* bd1 */ { 0x21, 0x04 }, + /* bd2 */ { 0x21, 0x04 }, + /* bd3 */ { 0x21, 0x04 }, + /* bd4 */ { 0x21, 0x04 }, + /* bd5 */ { 0x21, 0x04 }, + /* bd6 */ { 0x21, 0x04 }, + /* bd7 */ { 0x21, 0x04 }, + /* bd8 */ { 0x21, 0x04 }, + /* bd9 */ { 0x21, 0x04 }, + /* bda */ { 0x21, 0x04 }, + /* bdb */ { 0x21, 0x04 }, + /* bdc */ { 0x21, 0x04 }, + /* bdd */ { 0x21, 0x04 }, + /* bde */ { 0x21, 0x04 }, + /* bdf */ { 0x21, 0x04 }, + /* be0 */ { 0x21, 0x04 }, + /* be1 */ { 0x21, 0x04 }, + /* be2 */ { 0x21, 0x04 }, + /* be3 */ { 0x21, 0x04 }, + /* be4 */ { 0x21, 0x04 }, + /* be5 */ { 0x21, 0x04 }, + /* be6 */ { 0x21, 0x04 }, + /* be7 */ { 0x21, 0x04 }, + /* be8 */ { 0x21, 0x04 }, + /* be9 */ { 0x21, 0x04 }, + /* bea */ { 0x21, 0x04 }, + /* beb */ { 0x21, 0x04 }, + /* bec */ { 0x21, 0x04 }, + /* bed */ { 0x21, 0x04 }, + /* bee */ { 0x21, 0x04 }, + /* bef */ { 0x21, 0x04 }, + /* bf0 */ { 0x21, 0x04 }, + /* bf1 */ { 0x21, 0x04 }, + /* bf2 */ { 0x21, 0x04 }, + /* bf3 */ { 0x21, 0x04 }, + /* bf4 */ { 0x21, 0x04 }, + /* bf5 */ { 0x21, 0x04 }, + /* bf6 */ { 0x21, 0x04 }, + /* bf7 */ { 0x21, 0x04 }, + /* bf8 */ { 0x21, 0x04 }, + /* bf9 */ { 0x21, 0x04 }, + /* bfa */ { 0x21, 0x04 }, + /* bfb */ { 0x21, 0x04 }, + /* bfc */ { 0x21, 0x04 }, + /* bfd */ { 0x21, 0x04 }, + /* bfe */ { 0x21, 0x04 }, + /* bff */ { 0x21, 0x04 }, + /* c00 */ { 0x04, 0x05 }, + /* c01 */ { 0x04, 0x05 }, + /* c02 */ { 0x04, 0x05 }, + /* c03 */ { 0x04, 0x05 }, + /* c04 */ { 0x04, 0x05 }, + /* c05 */ { 0x04, 0x05 }, + /* c06 */ { 0x04, 0x05 }, + /* c07 */ { 0x04, 0x05 }, + /* c08 */ { 0x04, 0x05 }, + /* c09 */ { 0x04, 0x05 }, + /* c0a */ { 0x04, 0x05 }, + /* c0b */ { 0x04, 0x05 }, + /* c0c */ { 0x04, 0x05 }, + /* c0d */ { 0x04, 0x05 }, + /* c0e */ { 0x04, 0x05 }, + /* c0f */ { 0x04, 0x05 }, + /* c10 */ { 0x04, 0x05 }, + /* c11 */ { 0x04, 0x05 }, + /* c12 */ { 0x04, 0x05 }, + /* c13 */ { 0x04, 0x05 }, + /* c14 */ { 0x04, 0x05 }, + /* c15 */ { 0x04, 0x05 }, + /* c16 */ { 0x04, 0x05 }, + /* c17 */ { 0x04, 0x05 }, + /* c18 */ { 0x04, 0x05 }, + /* c19 */ { 0x04, 0x05 }, + /* c1a */ { 0x04, 0x05 }, + /* c1b */ { 0x04, 0x05 }, + /* c1c */ { 0x04, 0x05 }, + /* c1d */ { 0x04, 0x05 }, + /* c1e */ { 0x04, 0x05 }, + /* c1f */ { 0x04, 0x05 }, + /* c20 */ { 0x04, 0x05 }, + /* c21 */ { 0x04, 0x05 }, + /* c22 */ { 0x04, 0x05 }, + /* c23 */ { 0x04, 0x05 }, + /* c24 */ { 0x04, 0x05 }, + /* c25 */ { 0x04, 0x05 }, + /* c26 */ { 0x04, 0x05 }, + /* c27 */ { 0x04, 0x05 }, + /* c28 */ { 0x04, 0x05 }, + /* c29 */ { 0x04, 0x05 }, + /* c2a */ { 0x04, 0x05 }, + /* c2b */ { 0x04, 0x05 }, + /* c2c */ { 0x04, 0x05 }, + /* c2d */ { 0x04, 0x05 }, + /* c2e */ { 0x04, 0x05 }, + /* c2f */ { 0x04, 0x05 }, + /* c30 */ { 0x04, 0x05 }, + /* c31 */ { 0x04, 0x05 }, + /* c32 */ { 0x04, 0x05 }, + /* c33 */ { 0x04, 0x05 }, + /* c34 */ { 0x04, 0x05 }, + /* c35 */ { 0x04, 0x05 }, + /* c36 */ { 0x04, 0x05 }, + /* c37 */ { 0x04, 0x05 }, + /* c38 */ { 0x04, 0x05 }, + /* c39 */ { 0x04, 0x05 }, + /* c3a */ { 0x04, 0x05 }, + /* c3b */ { 0x04, 0x05 }, + /* c3c */ { 0x04, 0x05 }, + /* c3d */ { 0x04, 0x05 }, + /* c3e */ { 0x04, 0x05 }, + /* c3f */ { 0x04, 0x05 }, + /* c40 */ { 0x04, 0x05 }, + /* c41 */ { 0x04, 0x05 }, + /* c42 */ { 0x04, 0x05 }, + /* c43 */ { 0x04, 0x05 }, + /* c44 */ { 0x04, 0x05 }, + /* c45 */ { 0x04, 0x05 }, + /* c46 */ { 0x04, 0x05 }, + /* c47 */ { 0x04, 0x05 }, + /* c48 */ { 0x04, 0x05 }, + /* c49 */ { 0x04, 0x05 }, + /* c4a */ { 0x04, 0x05 }, + /* c4b */ { 0x04, 0x05 }, + /* c4c */ { 0x04, 0x05 }, + /* c4d */ { 0x04, 0x05 }, + /* c4e */ { 0x04, 0x05 }, + /* c4f */ { 0x04, 0x05 }, + /* c50 */ { 0x04, 0x05 }, + /* c51 */ { 0x04, 0x05 }, + /* c52 */ { 0x04, 0x05 }, + /* c53 */ { 0x04, 0x05 }, + /* c54 */ { 0x04, 0x05 }, + /* c55 */ { 0x04, 0x05 }, + /* c56 */ { 0x04, 0x05 }, + /* c57 */ { 0x04, 0x05 }, + /* c58 */ { 0x04, 0x05 }, + /* c59 */ { 0x04, 0x05 }, + /* c5a */ { 0x04, 0x05 }, + /* c5b */ { 0x04, 0x05 }, + /* c5c */ { 0x04, 0x05 }, + /* c5d */ { 0x04, 0x05 }, + /* c5e */ { 0x04, 0x05 }, + /* c5f */ { 0x04, 0x05 }, + /* c60 */ { 0x04, 0x05 }, + /* c61 */ { 0x04, 0x05 }, + /* c62 */ { 0x04, 0x05 }, + /* c63 */ { 0x04, 0x05 }, + /* c64 */ { 0x04, 0x05 }, + /* c65 */ { 0x04, 0x05 }, + /* c66 */ { 0x04, 0x05 }, + /* c67 */ { 0x04, 0x05 }, + /* c68 */ { 0x04, 0x05 }, + /* c69 */ { 0x04, 0x05 }, + /* c6a */ { 0x04, 0x05 }, + /* c6b */ { 0x04, 0x05 }, + /* c6c */ { 0x04, 0x05 }, + /* c6d */ { 0x04, 0x05 }, + /* c6e */ { 0x04, 0x05 }, + /* c6f */ { 0x04, 0x05 }, + /* c70 */ { 0x04, 0x05 }, + /* c71 */ { 0x04, 0x05 }, + /* c72 */ { 0x04, 0x05 }, + /* c73 */ { 0x04, 0x05 }, + /* c74 */ { 0x04, 0x05 }, + /* c75 */ { 0x04, 0x05 }, + /* c76 */ { 0x04, 0x05 }, + /* c77 */ { 0x04, 0x05 }, + /* c78 */ { 0x04, 0x05 }, + /* c79 */ { 0x04, 0x05 }, + /* c7a */ { 0x04, 0x05 }, + /* c7b */ { 0x04, 0x05 }, + /* c7c */ { 0x04, 0x05 }, + /* c7d */ { 0x04, 0x05 }, + /* c7e */ { 0x04, 0x05 }, + /* c7f */ { 0x04, 0x05 }, + /* c80 */ { 0x12, 0x05 }, + /* c81 */ { 0x12, 0x05 }, + /* c82 */ { 0x12, 0x05 }, + /* c83 */ { 0x12, 0x05 }, + /* c84 */ { 0x12, 0x05 }, + /* c85 */ { 0x12, 0x05 }, + /* c86 */ { 0x12, 0x05 }, + /* c87 */ { 0x12, 0x05 }, + /* c88 */ { 0x12, 0x05 }, + /* c89 */ { 0x12, 0x05 }, + /* c8a */ { 0x12, 0x05 }, + /* c8b */ { 0x12, 0x05 }, + /* c8c */ { 0x12, 0x05 }, + /* c8d */ { 0x12, 0x05 }, + /* c8e */ { 0x12, 0x05 }, + /* c8f */ { 0x12, 0x05 }, + /* c90 */ { 0x12, 0x05 }, + /* c91 */ { 0x12, 0x05 }, + /* c92 */ { 0x12, 0x05 }, + /* c93 */ { 0x12, 0x05 }, + /* c94 */ { 0x12, 0x05 }, + /* c95 */ { 0x12, 0x05 }, + /* c96 */ { 0x12, 0x05 }, + /* c97 */ { 0x12, 0x05 }, + /* c98 */ { 0x12, 0x05 }, + /* c99 */ { 0x12, 0x05 }, + /* c9a */ { 0x12, 0x05 }, + /* c9b */ { 0x12, 0x05 }, + /* c9c */ { 0x12, 0x05 }, + /* c9d */ { 0x12, 0x05 }, + /* c9e */ { 0x12, 0x05 }, + /* c9f */ { 0x12, 0x05 }, + /* ca0 */ { 0x12, 0x05 }, + /* ca1 */ { 0x12, 0x05 }, + /* ca2 */ { 0x12, 0x05 }, + /* ca3 */ { 0x12, 0x05 }, + /* ca4 */ { 0x12, 0x05 }, + /* ca5 */ { 0x12, 0x05 }, + /* ca6 */ { 0x12, 0x05 }, + /* ca7 */ { 0x12, 0x05 }, + /* ca8 */ { 0x12, 0x05 }, + /* ca9 */ { 0x12, 0x05 }, + /* caa */ { 0x12, 0x05 }, + /* cab */ { 0x12, 0x05 }, + /* cac */ { 0x12, 0x05 }, + /* cad */ { 0x12, 0x05 }, + /* cae */ { 0x12, 0x05 }, + /* caf */ { 0x12, 0x05 }, + /* cb0 */ { 0x12, 0x05 }, + /* cb1 */ { 0x12, 0x05 }, + /* cb2 */ { 0x12, 0x05 }, + /* cb3 */ { 0x12, 0x05 }, + /* cb4 */ { 0x12, 0x05 }, + /* cb5 */ { 0x12, 0x05 }, + /* cb6 */ { 0x12, 0x05 }, + /* cb7 */ { 0x12, 0x05 }, + /* cb8 */ { 0x12, 0x05 }, + /* cb9 */ { 0x12, 0x05 }, + /* cba */ { 0x12, 0x05 }, + /* cbb */ { 0x12, 0x05 }, + /* cbc */ { 0x12, 0x05 }, + /* cbd */ { 0x12, 0x05 }, + /* cbe */ { 0x12, 0x05 }, + /* cbf */ { 0x12, 0x05 }, + /* cc0 */ { 0x12, 0x05 }, + /* cc1 */ { 0x12, 0x05 }, + /* cc2 */ { 0x12, 0x05 }, + /* cc3 */ { 0x12, 0x05 }, + /* cc4 */ { 0x12, 0x05 }, + /* cc5 */ { 0x12, 0x05 }, + /* cc6 */ { 0x12, 0x05 }, + /* cc7 */ { 0x12, 0x05 }, + /* cc8 */ { 0x12, 0x05 }, + /* cc9 */ { 0x12, 0x05 }, + /* cca */ { 0x12, 0x05 }, + /* ccb */ { 0x12, 0x05 }, + /* ccc */ { 0x12, 0x05 }, + /* ccd */ { 0x12, 0x05 }, + /* cce */ { 0x12, 0x05 }, + /* ccf */ { 0x12, 0x05 }, + /* cd0 */ { 0x12, 0x05 }, + /* cd1 */ { 0x12, 0x05 }, + /* cd2 */ { 0x12, 0x05 }, + /* cd3 */ { 0x12, 0x05 }, + /* cd4 */ { 0x12, 0x05 }, + /* cd5 */ { 0x12, 0x05 }, + /* cd6 */ { 0x12, 0x05 }, + /* cd7 */ { 0x12, 0x05 }, + /* cd8 */ { 0x12, 0x05 }, + /* cd9 */ { 0x12, 0x05 }, + /* cda */ { 0x12, 0x05 }, + /* cdb */ { 0x12, 0x05 }, + /* cdc */ { 0x12, 0x05 }, + /* cdd */ { 0x12, 0x05 }, + /* cde */ { 0x12, 0x05 }, + /* cdf */ { 0x12, 0x05 }, + /* ce0 */ { 0x12, 0x05 }, + /* ce1 */ { 0x12, 0x05 }, + /* ce2 */ { 0x12, 0x05 }, + /* ce3 */ { 0x12, 0x05 }, + /* ce4 */ { 0x12, 0x05 }, + /* ce5 */ { 0x12, 0x05 }, + /* ce6 */ { 0x12, 0x05 }, + /* ce7 */ { 0x12, 0x05 }, + /* ce8 */ { 0x12, 0x05 }, + /* ce9 */ { 0x12, 0x05 }, + /* cea */ { 0x12, 0x05 }, + /* ceb */ { 0x12, 0x05 }, + /* cec */ { 0x12, 0x05 }, + /* ced */ { 0x12, 0x05 }, + /* cee */ { 0x12, 0x05 }, + /* cef */ { 0x12, 0x05 }, + /* cf0 */ { 0x12, 0x05 }, + /* cf1 */ { 0x12, 0x05 }, + /* cf2 */ { 0x12, 0x05 }, + /* cf3 */ { 0x12, 0x05 }, + /* cf4 */ { 0x12, 0x05 }, + /* cf5 */ { 0x12, 0x05 }, + /* cf6 */ { 0x12, 0x05 }, + /* cf7 */ { 0x12, 0x05 }, + /* cf8 */ { 0x12, 0x05 }, + /* cf9 */ { 0x12, 0x05 }, + /* cfa */ { 0x12, 0x05 }, + /* cfb */ { 0x12, 0x05 }, + /* cfc */ { 0x12, 0x05 }, + /* cfd */ { 0x12, 0x05 }, + /* cfe */ { 0x12, 0x05 }, + /* cff */ { 0x12, 0x05 }, + /* d00 */ { 0x31, 0x05 }, + /* d01 */ { 0x31, 0x05 }, + /* d02 */ { 0x31, 0x05 }, + /* d03 */ { 0x31, 0x05 }, + /* d04 */ { 0x31, 0x05 }, + /* d05 */ { 0x31, 0x05 }, + /* d06 */ { 0x31, 0x05 }, + /* d07 */ { 0x31, 0x05 }, + /* d08 */ { 0x31, 0x05 }, + /* d09 */ { 0x31, 0x05 }, + /* d0a */ { 0x31, 0x05 }, + /* d0b */ { 0x31, 0x05 }, + /* d0c */ { 0x31, 0x05 }, + /* d0d */ { 0x31, 0x05 }, + /* d0e */ { 0x31, 0x05 }, + /* d0f */ { 0x31, 0x05 }, + /* d10 */ { 0x31, 0x05 }, + /* d11 */ { 0x31, 0x05 }, + /* d12 */ { 0x31, 0x05 }, + /* d13 */ { 0x31, 0x05 }, + /* d14 */ { 0x31, 0x05 }, + /* d15 */ { 0x31, 0x05 }, + /* d16 */ { 0x31, 0x05 }, + /* d17 */ { 0x31, 0x05 }, + /* d18 */ { 0x31, 0x05 }, + /* d19 */ { 0x31, 0x05 }, + /* d1a */ { 0x31, 0x05 }, + /* d1b */ { 0x31, 0x05 }, + /* d1c */ { 0x31, 0x05 }, + /* d1d */ { 0x31, 0x05 }, + /* d1e */ { 0x31, 0x05 }, + /* d1f */ { 0x31, 0x05 }, + /* d20 */ { 0x31, 0x05 }, + /* d21 */ { 0x31, 0x05 }, + /* d22 */ { 0x31, 0x05 }, + /* d23 */ { 0x31, 0x05 }, + /* d24 */ { 0x31, 0x05 }, + /* d25 */ { 0x31, 0x05 }, + /* d26 */ { 0x31, 0x05 }, + /* d27 */ { 0x31, 0x05 }, + /* d28 */ { 0x31, 0x05 }, + /* d29 */ { 0x31, 0x05 }, + /* d2a */ { 0x31, 0x05 }, + /* d2b */ { 0x31, 0x05 }, + /* d2c */ { 0x31, 0x05 }, + /* d2d */ { 0x31, 0x05 }, + /* d2e */ { 0x31, 0x05 }, + /* d2f */ { 0x31, 0x05 }, + /* d30 */ { 0x31, 0x05 }, + /* d31 */ { 0x31, 0x05 }, + /* d32 */ { 0x31, 0x05 }, + /* d33 */ { 0x31, 0x05 }, + /* d34 */ { 0x31, 0x05 }, + /* d35 */ { 0x31, 0x05 }, + /* d36 */ { 0x31, 0x05 }, + /* d37 */ { 0x31, 0x05 }, + /* d38 */ { 0x31, 0x05 }, + /* d39 */ { 0x31, 0x05 }, + /* d3a */ { 0x31, 0x05 }, + /* d3b */ { 0x31, 0x05 }, + /* d3c */ { 0x31, 0x05 }, + /* d3d */ { 0x31, 0x05 }, + /* d3e */ { 0x31, 0x05 }, + /* d3f */ { 0x31, 0x05 }, + /* d40 */ { 0x31, 0x05 }, + /* d41 */ { 0x31, 0x05 }, + /* d42 */ { 0x31, 0x05 }, + /* d43 */ { 0x31, 0x05 }, + /* d44 */ { 0x31, 0x05 }, + /* d45 */ { 0x31, 0x05 }, + /* d46 */ { 0x31, 0x05 }, + /* d47 */ { 0x31, 0x05 }, + /* d48 */ { 0x31, 0x05 }, + /* d49 */ { 0x31, 0x05 }, + /* d4a */ { 0x31, 0x05 }, + /* d4b */ { 0x31, 0x05 }, + /* d4c */ { 0x31, 0x05 }, + /* d4d */ { 0x31, 0x05 }, + /* d4e */ { 0x31, 0x05 }, + /* d4f */ { 0x31, 0x05 }, + /* d50 */ { 0x31, 0x05 }, + /* d51 */ { 0x31, 0x05 }, + /* d52 */ { 0x31, 0x05 }, + /* d53 */ { 0x31, 0x05 }, + /* d54 */ { 0x31, 0x05 }, + /* d55 */ { 0x31, 0x05 }, + /* d56 */ { 0x31, 0x05 }, + /* d57 */ { 0x31, 0x05 }, + /* d58 */ { 0x31, 0x05 }, + /* d59 */ { 0x31, 0x05 }, + /* d5a */ { 0x31, 0x05 }, + /* d5b */ { 0x31, 0x05 }, + /* d5c */ { 0x31, 0x05 }, + /* d5d */ { 0x31, 0x05 }, + /* d5e */ { 0x31, 0x05 }, + /* d5f */ { 0x31, 0x05 }, + /* d60 */ { 0x31, 0x05 }, + /* d61 */ { 0x31, 0x05 }, + /* d62 */ { 0x31, 0x05 }, + /* d63 */ { 0x31, 0x05 }, + /* d64 */ { 0x31, 0x05 }, + /* d65 */ { 0x31, 0x05 }, + /* d66 */ { 0x31, 0x05 }, + /* d67 */ { 0x31, 0x05 }, + /* d68 */ { 0x31, 0x05 }, + /* d69 */ { 0x31, 0x05 }, + /* d6a */ { 0x31, 0x05 }, + /* d6b */ { 0x31, 0x05 }, + /* d6c */ { 0x31, 0x05 }, + /* d6d */ { 0x31, 0x05 }, + /* d6e */ { 0x31, 0x05 }, + /* d6f */ { 0x31, 0x05 }, + /* d70 */ { 0x31, 0x05 }, + /* d71 */ { 0x31, 0x05 }, + /* d72 */ { 0x31, 0x05 }, + /* d73 */ { 0x31, 0x05 }, + /* d74 */ { 0x31, 0x05 }, + /* d75 */ { 0x31, 0x05 }, + /* d76 */ { 0x31, 0x05 }, + /* d77 */ { 0x31, 0x05 }, + /* d78 */ { 0x31, 0x05 }, + /* d79 */ { 0x31, 0x05 }, + /* d7a */ { 0x31, 0x05 }, + /* d7b */ { 0x31, 0x05 }, + /* d7c */ { 0x31, 0x05 }, + /* d7d */ { 0x31, 0x05 }, + /* d7e */ { 0x31, 0x05 }, + /* d7f */ { 0x31, 0x05 }, + /* d80 */ { 0x41, 0x05 }, + /* d81 */ { 0x41, 0x05 }, + /* d82 */ { 0x41, 0x05 }, + /* d83 */ { 0x41, 0x05 }, + /* d84 */ { 0x41, 0x05 }, + /* d85 */ { 0x41, 0x05 }, + /* d86 */ { 0x41, 0x05 }, + /* d87 */ { 0x41, 0x05 }, + /* d88 */ { 0x41, 0x05 }, + /* d89 */ { 0x41, 0x05 }, + /* d8a */ { 0x41, 0x05 }, + /* d8b */ { 0x41, 0x05 }, + /* d8c */ { 0x41, 0x05 }, + /* d8d */ { 0x41, 0x05 }, + /* d8e */ { 0x41, 0x05 }, + /* d8f */ { 0x41, 0x05 }, + /* d90 */ { 0x41, 0x05 }, + /* d91 */ { 0x41, 0x05 }, + /* d92 */ { 0x41, 0x05 }, + /* d93 */ { 0x41, 0x05 }, + /* d94 */ { 0x41, 0x05 }, + /* d95 */ { 0x41, 0x05 }, + /* d96 */ { 0x41, 0x05 }, + /* d97 */ { 0x41, 0x05 }, + /* d98 */ { 0x41, 0x05 }, + /* d99 */ { 0x41, 0x05 }, + /* d9a */ { 0x41, 0x05 }, + /* d9b */ { 0x41, 0x05 }, + /* d9c */ { 0x41, 0x05 }, + /* d9d */ { 0x41, 0x05 }, + /* d9e */ { 0x41, 0x05 }, + /* d9f */ { 0x41, 0x05 }, + /* da0 */ { 0x41, 0x05 }, + /* da1 */ { 0x41, 0x05 }, + /* da2 */ { 0x41, 0x05 }, + /* da3 */ { 0x41, 0x05 }, + /* da4 */ { 0x41, 0x05 }, + /* da5 */ { 0x41, 0x05 }, + /* da6 */ { 0x41, 0x05 }, + /* da7 */ { 0x41, 0x05 }, + /* da8 */ { 0x41, 0x05 }, + /* da9 */ { 0x41, 0x05 }, + /* daa */ { 0x41, 0x05 }, + /* dab */ { 0x41, 0x05 }, + /* dac */ { 0x41, 0x05 }, + /* dad */ { 0x41, 0x05 }, + /* dae */ { 0x41, 0x05 }, + /* daf */ { 0x41, 0x05 }, + /* db0 */ { 0x41, 0x05 }, + /* db1 */ { 0x41, 0x05 }, + /* db2 */ { 0x41, 0x05 }, + /* db3 */ { 0x41, 0x05 }, + /* db4 */ { 0x41, 0x05 }, + /* db5 */ { 0x41, 0x05 }, + /* db6 */ { 0x41, 0x05 }, + /* db7 */ { 0x41, 0x05 }, + /* db8 */ { 0x41, 0x05 }, + /* db9 */ { 0x41, 0x05 }, + /* dba */ { 0x41, 0x05 }, + /* dbb */ { 0x41, 0x05 }, + /* dbc */ { 0x41, 0x05 }, + /* dbd */ { 0x41, 0x05 }, + /* dbe */ { 0x41, 0x05 }, + /* dbf */ { 0x41, 0x05 }, + /* dc0 */ { 0x41, 0x05 }, + /* dc1 */ { 0x41, 0x05 }, + /* dc2 */ { 0x41, 0x05 }, + /* dc3 */ { 0x41, 0x05 }, + /* dc4 */ { 0x41, 0x05 }, + /* dc5 */ { 0x41, 0x05 }, + /* dc6 */ { 0x41, 0x05 }, + /* dc7 */ { 0x41, 0x05 }, + /* dc8 */ { 0x41, 0x05 }, + /* dc9 */ { 0x41, 0x05 }, + /* dca */ { 0x41, 0x05 }, + /* dcb */ { 0x41, 0x05 }, + /* dcc */ { 0x41, 0x05 }, + /* dcd */ { 0x41, 0x05 }, + /* dce */ { 0x41, 0x05 }, + /* dcf */ { 0x41, 0x05 }, + /* dd0 */ { 0x41, 0x05 }, + /* dd1 */ { 0x41, 0x05 }, + /* dd2 */ { 0x41, 0x05 }, + /* dd3 */ { 0x41, 0x05 }, + /* dd4 */ { 0x41, 0x05 }, + /* dd5 */ { 0x41, 0x05 }, + /* dd6 */ { 0x41, 0x05 }, + /* dd7 */ { 0x41, 0x05 }, + /* dd8 */ { 0x41, 0x05 }, + /* dd9 */ { 0x41, 0x05 }, + /* dda */ { 0x41, 0x05 }, + /* ddb */ { 0x41, 0x05 }, + /* ddc */ { 0x41, 0x05 }, + /* ddd */ { 0x41, 0x05 }, + /* dde */ { 0x41, 0x05 }, + /* ddf */ { 0x41, 0x05 }, + /* de0 */ { 0x41, 0x05 }, + /* de1 */ { 0x41, 0x05 }, + /* de2 */ { 0x41, 0x05 }, + /* de3 */ { 0x41, 0x05 }, + /* de4 */ { 0x41, 0x05 }, + /* de5 */ { 0x41, 0x05 }, + /* de6 */ { 0x41, 0x05 }, + /* de7 */ { 0x41, 0x05 }, + /* de8 */ { 0x41, 0x05 }, + /* de9 */ { 0x41, 0x05 }, + /* dea */ { 0x41, 0x05 }, + /* deb */ { 0x41, 0x05 }, + /* dec */ { 0x41, 0x05 }, + /* ded */ { 0x41, 0x05 }, + /* dee */ { 0x41, 0x05 }, + /* def */ { 0x41, 0x05 }, + /* df0 */ { 0x41, 0x05 }, + /* df1 */ { 0x41, 0x05 }, + /* df2 */ { 0x41, 0x05 }, + /* df3 */ { 0x41, 0x05 }, + /* df4 */ { 0x41, 0x05 }, + /* df5 */ { 0x41, 0x05 }, + /* df6 */ { 0x41, 0x05 }, + /* df7 */ { 0x41, 0x05 }, + /* df8 */ { 0x41, 0x05 }, + /* df9 */ { 0x41, 0x05 }, + /* dfa */ { 0x41, 0x05 }, + /* dfb */ { 0x41, 0x05 }, + /* dfc */ { 0x41, 0x05 }, + /* dfd */ { 0x41, 0x05 }, + /* dfe */ { 0x41, 0x05 }, + /* dff */ { 0x41, 0x05 }, + /* e00 */ { 0x51, 0x06 }, + /* e01 */ { 0x51, 0x06 }, + /* e02 */ { 0x51, 0x06 }, + /* e03 */ { 0x51, 0x06 }, + /* e04 */ { 0x51, 0x06 }, + /* e05 */ { 0x51, 0x06 }, + /* e06 */ { 0x51, 0x06 }, + /* e07 */ { 0x51, 0x06 }, + /* e08 */ { 0x51, 0x06 }, + /* e09 */ { 0x51, 0x06 }, + /* e0a */ { 0x51, 0x06 }, + /* e0b */ { 0x51, 0x06 }, + /* e0c */ { 0x51, 0x06 }, + /* e0d */ { 0x51, 0x06 }, + /* e0e */ { 0x51, 0x06 }, + /* e0f */ { 0x51, 0x06 }, + /* e10 */ { 0x51, 0x06 }, + /* e11 */ { 0x51, 0x06 }, + /* e12 */ { 0x51, 0x06 }, + /* e13 */ { 0x51, 0x06 }, + /* e14 */ { 0x51, 0x06 }, + /* e15 */ { 0x51, 0x06 }, + /* e16 */ { 0x51, 0x06 }, + /* e17 */ { 0x51, 0x06 }, + /* e18 */ { 0x51, 0x06 }, + /* e19 */ { 0x51, 0x06 }, + /* e1a */ { 0x51, 0x06 }, + /* e1b */ { 0x51, 0x06 }, + /* e1c */ { 0x51, 0x06 }, + /* e1d */ { 0x51, 0x06 }, + /* e1e */ { 0x51, 0x06 }, + /* e1f */ { 0x51, 0x06 }, + /* e20 */ { 0x51, 0x06 }, + /* e21 */ { 0x51, 0x06 }, + /* e22 */ { 0x51, 0x06 }, + /* e23 */ { 0x51, 0x06 }, + /* e24 */ { 0x51, 0x06 }, + /* e25 */ { 0x51, 0x06 }, + /* e26 */ { 0x51, 0x06 }, + /* e27 */ { 0x51, 0x06 }, + /* e28 */ { 0x51, 0x06 }, + /* e29 */ { 0x51, 0x06 }, + /* e2a */ { 0x51, 0x06 }, + /* e2b */ { 0x51, 0x06 }, + /* e2c */ { 0x51, 0x06 }, + /* e2d */ { 0x51, 0x06 }, + /* e2e */ { 0x51, 0x06 }, + /* e2f */ { 0x51, 0x06 }, + /* e30 */ { 0x51, 0x06 }, + /* e31 */ { 0x51, 0x06 }, + /* e32 */ { 0x51, 0x06 }, + /* e33 */ { 0x51, 0x06 }, + /* e34 */ { 0x51, 0x06 }, + /* e35 */ { 0x51, 0x06 }, + /* e36 */ { 0x51, 0x06 }, + /* e37 */ { 0x51, 0x06 }, + /* e38 */ { 0x51, 0x06 }, + /* e39 */ { 0x51, 0x06 }, + /* e3a */ { 0x51, 0x06 }, + /* e3b */ { 0x51, 0x06 }, + /* e3c */ { 0x51, 0x06 }, + /* e3d */ { 0x51, 0x06 }, + /* e3e */ { 0x51, 0x06 }, + /* e3f */ { 0x51, 0x06 }, + /* e40 */ { 0x05, 0x07 }, + /* e41 */ { 0x05, 0x07 }, + /* e42 */ { 0x05, 0x07 }, + /* e43 */ { 0x05, 0x07 }, + /* e44 */ { 0x05, 0x07 }, + /* e45 */ { 0x05, 0x07 }, + /* e46 */ { 0x05, 0x07 }, + /* e47 */ { 0x05, 0x07 }, + /* e48 */ { 0x05, 0x07 }, + /* e49 */ { 0x05, 0x07 }, + /* e4a */ { 0x05, 0x07 }, + /* e4b */ { 0x05, 0x07 }, + /* e4c */ { 0x05, 0x07 }, + /* e4d */ { 0x05, 0x07 }, + /* e4e */ { 0x05, 0x07 }, + /* e4f */ { 0x05, 0x07 }, + /* e50 */ { 0x05, 0x07 }, + /* e51 */ { 0x05, 0x07 }, + /* e52 */ { 0x05, 0x07 }, + /* e53 */ { 0x05, 0x07 }, + /* e54 */ { 0x05, 0x07 }, + /* e55 */ { 0x05, 0x07 }, + /* e56 */ { 0x05, 0x07 }, + /* e57 */ { 0x05, 0x07 }, + /* e58 */ { 0x05, 0x07 }, + /* e59 */ { 0x05, 0x07 }, + /* e5a */ { 0x05, 0x07 }, + /* e5b */ { 0x05, 0x07 }, + /* e5c */ { 0x05, 0x07 }, + /* e5d */ { 0x05, 0x07 }, + /* e5e */ { 0x05, 0x07 }, + /* e5f */ { 0x05, 0x07 }, + /* e60 */ { 0x13, 0x07 }, + /* e61 */ { 0x13, 0x07 }, + /* e62 */ { 0x13, 0x07 }, + /* e63 */ { 0x13, 0x07 }, + /* e64 */ { 0x13, 0x07 }, + /* e65 */ { 0x13, 0x07 }, + /* e66 */ { 0x13, 0x07 }, + /* e67 */ { 0x13, 0x07 }, + /* e68 */ { 0x13, 0x07 }, + /* e69 */ { 0x13, 0x07 }, + /* e6a */ { 0x13, 0x07 }, + /* e6b */ { 0x13, 0x07 }, + /* e6c */ { 0x13, 0x07 }, + /* e6d */ { 0x13, 0x07 }, + /* e6e */ { 0x13, 0x07 }, + /* e6f */ { 0x13, 0x07 }, + /* e70 */ { 0x13, 0x07 }, + /* e71 */ { 0x13, 0x07 }, + /* e72 */ { 0x13, 0x07 }, + /* e73 */ { 0x13, 0x07 }, + /* e74 */ { 0x13, 0x07 }, + /* e75 */ { 0x13, 0x07 }, + /* e76 */ { 0x13, 0x07 }, + /* e77 */ { 0x13, 0x07 }, + /* e78 */ { 0x13, 0x07 }, + /* e79 */ { 0x13, 0x07 }, + /* e7a */ { 0x13, 0x07 }, + /* e7b */ { 0x13, 0x07 }, + /* e7c */ { 0x13, 0x07 }, + /* e7d */ { 0x13, 0x07 }, + /* e7e */ { 0x13, 0x07 }, + /* e7f */ { 0x13, 0x07 }, + /* e80 */ { 0x22, 0x07 }, + /* e81 */ { 0x22, 0x07 }, + /* e82 */ { 0x22, 0x07 }, + /* e83 */ { 0x22, 0x07 }, + /* e84 */ { 0x22, 0x07 }, + /* e85 */ { 0x22, 0x07 }, + /* e86 */ { 0x22, 0x07 }, + /* e87 */ { 0x22, 0x07 }, + /* e88 */ { 0x22, 0x07 }, + /* e89 */ { 0x22, 0x07 }, + /* e8a */ { 0x22, 0x07 }, + /* e8b */ { 0x22, 0x07 }, + /* e8c */ { 0x22, 0x07 }, + /* e8d */ { 0x22, 0x07 }, + /* e8e */ { 0x22, 0x07 }, + /* e8f */ { 0x22, 0x07 }, + /* e90 */ { 0x22, 0x07 }, + /* e91 */ { 0x22, 0x07 }, + /* e92 */ { 0x22, 0x07 }, + /* e93 */ { 0x22, 0x07 }, + /* e94 */ { 0x22, 0x07 }, + /* e95 */ { 0x22, 0x07 }, + /* e96 */ { 0x22, 0x07 }, + /* e97 */ { 0x22, 0x07 }, + /* e98 */ { 0x22, 0x07 }, + /* e99 */ { 0x22, 0x07 }, + /* e9a */ { 0x22, 0x07 }, + /* e9b */ { 0x22, 0x07 }, + /* e9c */ { 0x22, 0x07 }, + /* e9d */ { 0x22, 0x07 }, + /* e9e */ { 0x22, 0x07 }, + /* e9f */ { 0x22, 0x07 }, + /* ea0 */ { 0x61, 0x07 }, + /* ea1 */ { 0x61, 0x07 }, + /* ea2 */ { 0x61, 0x07 }, + /* ea3 */ { 0x61, 0x07 }, + /* ea4 */ { 0x61, 0x07 }, + /* ea5 */ { 0x61, 0x07 }, + /* ea6 */ { 0x61, 0x07 }, + /* ea7 */ { 0x61, 0x07 }, + /* ea8 */ { 0x61, 0x07 }, + /* ea9 */ { 0x61, 0x07 }, + /* eaa */ { 0x61, 0x07 }, + /* eab */ { 0x61, 0x07 }, + /* eac */ { 0x61, 0x07 }, + /* ead */ { 0x61, 0x07 }, + /* eae */ { 0x61, 0x07 }, + /* eaf */ { 0x61, 0x07 }, + /* eb0 */ { 0x61, 0x07 }, + /* eb1 */ { 0x61, 0x07 }, + /* eb2 */ { 0x61, 0x07 }, + /* eb3 */ { 0x61, 0x07 }, + /* eb4 */ { 0x61, 0x07 }, + /* eb5 */ { 0x61, 0x07 }, + /* eb6 */ { 0x61, 0x07 }, + /* eb7 */ { 0x61, 0x07 }, + /* eb8 */ { 0x61, 0x07 }, + /* eb9 */ { 0x61, 0x07 }, + /* eba */ { 0x61, 0x07 }, + /* ebb */ { 0x61, 0x07 }, + /* ebc */ { 0x61, 0x07 }, + /* ebd */ { 0x61, 0x07 }, + /* ebe */ { 0x61, 0x07 }, + /* ebf */ { 0x61, 0x07 }, + /* ec0 */ { 0x71, 0x07 }, + /* ec1 */ { 0x71, 0x07 }, + /* ec2 */ { 0x71, 0x07 }, + /* ec3 */ { 0x71, 0x07 }, + /* ec4 */ { 0x71, 0x07 }, + /* ec5 */ { 0x71, 0x07 }, + /* ec6 */ { 0x71, 0x07 }, + /* ec7 */ { 0x71, 0x07 }, + /* ec8 */ { 0x71, 0x07 }, + /* ec9 */ { 0x71, 0x07 }, + /* eca */ { 0x71, 0x07 }, + /* ecb */ { 0x71, 0x07 }, + /* ecc */ { 0x71, 0x07 }, + /* ecd */ { 0x71, 0x07 }, + /* ece */ { 0x71, 0x07 }, + /* ecf */ { 0x71, 0x07 }, + /* ed0 */ { 0x71, 0x07 }, + /* ed1 */ { 0x71, 0x07 }, + /* ed2 */ { 0x71, 0x07 }, + /* ed3 */ { 0x71, 0x07 }, + /* ed4 */ { 0x71, 0x07 }, + /* ed5 */ { 0x71, 0x07 }, + /* ed6 */ { 0x71, 0x07 }, + /* ed7 */ { 0x71, 0x07 }, + /* ed8 */ { 0x71, 0x07 }, + /* ed9 */ { 0x71, 0x07 }, + /* eda */ { 0x71, 0x07 }, + /* edb */ { 0x71, 0x07 }, + /* edc */ { 0x71, 0x07 }, + /* edd */ { 0x71, 0x07 }, + /* ede */ { 0x71, 0x07 }, + /* edf */ { 0x71, 0x07 }, + /* ee0 */ { 0x32, 0x09 }, + /* ee1 */ { 0x32, 0x09 }, + /* ee2 */ { 0x32, 0x09 }, + /* ee3 */ { 0x32, 0x09 }, + /* ee4 */ { 0x32, 0x09 }, + /* ee5 */ { 0x32, 0x09 }, + /* ee6 */ { 0x32, 0x09 }, + /* ee7 */ { 0x32, 0x09 }, + /* ee8 */ { 0x81, 0x09 }, + /* ee9 */ { 0x81, 0x09 }, + /* eea */ { 0x81, 0x09 }, + /* eeb */ { 0x81, 0x09 }, + /* eec */ { 0x81, 0x09 }, + /* eed */ { 0x81, 0x09 }, + /* eee */ { 0x81, 0x09 }, + /* eef */ { 0x81, 0x09 }, + /* ef0 */ { 0x09, 0x0b }, + /* ef1 */ { 0x09, 0x0b }, + /* ef2 */ { 0x19, 0x0b }, + /* ef3 */ { 0x19, 0x0b }, + /* ef4 */ { 0x29, 0x0b }, + /* ef5 */ { 0x29, 0x0b }, + /* ef6 */ { 0x39, 0x0b }, + /* ef7 */ { 0x39, 0x0b }, + /* ef8 */ { 0x49, 0x0b }, + /* ef9 */ { 0x49, 0x0b }, + /* efa */ { 0x59, 0x0b }, + /* efb */ { 0x59, 0x0b }, + /* efc */ { 0x69, 0x0b }, + /* efd */ { 0x69, 0x0b }, + /* efe */ { 0x79, 0x0b }, + /* eff */ { 0x79, 0x0b }, + /* f00 */ { 0x89, 0x0b }, + /* f01 */ { 0x89, 0x0b }, + /* f02 */ { 0x99, 0x0b }, + /* f03 */ { 0x99, 0x0b }, + /* f04 */ { 0xa9, 0x0b }, + /* f05 */ { 0xa9, 0x0b }, + /* f06 */ { 0xb9, 0x0b }, + /* f07 */ { 0xb9, 0x0b }, + /* f08 */ { 0xc9, 0x0b }, + /* f09 */ { 0xc9, 0x0b }, + /* f0a */ { 0xd9, 0x0b }, + /* f0b */ { 0xd9, 0x0b }, + /* f0c */ { 0xe9, 0x0b }, + /* f0d */ { 0xe9, 0x0b }, + /* f0e */ { 0xf9, 0x0b }, + /* f0f */ { 0xf9, 0x0b }, + /* f10 */ { 0x06, 0x0c }, + /* f11 */ { 0x07, 0x0c }, + /* f12 */ { 0x08, 0x0c }, + /* f13 */ { 0x14, 0x0c }, + /* f14 */ { 0x15, 0x0c }, + /* f15 */ { 0x16, 0x0c }, + /* f16 */ { 0x17, 0x0c }, + /* f17 */ { 0x18, 0x0c }, + /* f18 */ { 0x23, 0x0c }, + /* f19 */ { 0x24, 0x0c }, + /* f1a */ { 0x25, 0x0c }, + /* f1b */ { 0x26, 0x0c }, + /* f1c */ { 0x27, 0x0c }, + /* f1d */ { 0x28, 0x0c }, + /* f1e */ { 0x33, 0x0c }, + /* f1f */ { 0x34, 0x0c }, + /* f20 */ { 0x35, 0x0c }, + /* f21 */ { 0x36, 0x0c }, + /* f22 */ { 0x37, 0x0c }, + /* f23 */ { 0x38, 0x0c }, + /* f24 */ { 0x42, 0x0c }, + /* f25 */ { 0x43, 0x0c }, + /* f26 */ { 0x44, 0x0c }, + /* f27 */ { 0x45, 0x0c }, + /* f28 */ { 0x46, 0x0c }, + /* f29 */ { 0x47, 0x0c }, + /* f2a */ { 0x48, 0x0c }, + /* f2b */ { 0x52, 0x0c }, + /* f2c */ { 0x53, 0x0c }, + /* f2d */ { 0x54, 0x0c }, + /* f2e */ { 0x55, 0x0c }, + /* f2f */ { 0x56, 0x0c }, + /* f30 */ { 0x57, 0x0c }, + /* f31 */ { 0x58, 0x0c }, + /* f32 */ { 0x62, 0x0c }, + /* f33 */ { 0x63, 0x0c }, + /* f34 */ { 0x64, 0x0c }, + /* f35 */ { 0x65, 0x0c }, + /* f36 */ { 0x66, 0x0c }, + /* f37 */ { 0x67, 0x0c }, + /* f38 */ { 0x68, 0x0c }, + /* f39 */ { 0x72, 0x0c }, + /* f3a */ { 0x73, 0x0c }, + /* f3b */ { 0x74, 0x0c }, + /* f3c */ { 0x75, 0x0c }, + /* f3d */ { 0x76, 0x0c }, + /* f3e */ { 0x77, 0x0c }, + /* f3f */ { 0x78, 0x0c }, + /* f40 */ { 0x82, 0x0c }, + /* f41 */ { 0x83, 0x0c }, + /* f42 */ { 0x84, 0x0c }, + /* f43 */ { 0x85, 0x0c }, + /* f44 */ { 0x86, 0x0c }, + /* f45 */ { 0x87, 0x0c }, + /* f46 */ { 0x88, 0x0c }, + /* f47 */ { 0x91, 0x0c }, + /* f48 */ { 0x92, 0x0c }, + /* f49 */ { 0x93, 0x0c }, + /* f4a */ { 0x94, 0x0c }, + /* f4b */ { 0x95, 0x0c }, + /* f4c */ { 0x96, 0x0c }, + /* f4d */ { 0x97, 0x0c }, + /* f4e */ { 0x98, 0x0c }, + /* f4f */ { 0xa1, 0x0c }, + /* f50 */ { 0xa2, 0x0c }, + /* f51 */ { 0xa3, 0x0c }, + /* f52 */ { 0xa4, 0x0c }, + /* f53 */ { 0xa5, 0x0c }, + /* f54 */ { 0xa6, 0x0c }, + /* f55 */ { 0xa7, 0x0c }, + /* f56 */ { 0xa8, 0x0c }, + /* f57 */ { 0xb1, 0x0c }, + /* f58 */ { 0xb2, 0x0c }, + /* f59 */ { 0xb3, 0x0c }, + /* f5a */ { 0xb4, 0x0c }, + /* f5b */ { 0xb5, 0x0c }, + /* f5c */ { 0xb6, 0x0c }, + /* f5d */ { 0xb7, 0x0c }, + /* f5e */ { 0xb8, 0x0c }, + /* f5f */ { 0xc1, 0x0c }, + /* f60 */ { 0xc2, 0x0c }, + /* f61 */ { 0xc3, 0x0c }, + /* f62 */ { 0xc4, 0x0c }, + /* f63 */ { 0xc5, 0x0c }, + /* f64 */ { 0xc6, 0x0c }, + /* f65 */ { 0xc7, 0x0c }, + /* f66 */ { 0xc8, 0x0c }, + /* f67 */ { 0xd1, 0x0c }, + /* f68 */ { 0xd2, 0x0c }, + /* f69 */ { 0xd3, 0x0c }, + /* f6a */ { 0xd4, 0x0c }, + /* f6b */ { 0xd5, 0x0c }, + /* f6c */ { 0xd6, 0x0c }, + /* f6d */ { 0xd7, 0x0c }, + /* f6e */ { 0xd8, 0x0c }, + /* f6f */ { 0xe1, 0x0c }, + /* f70 */ { 0xe2, 0x0c }, + /* f71 */ { 0xe3, 0x0c }, + /* f72 */ { 0xe4, 0x0c }, + /* f73 */ { 0xe5, 0x0c }, + /* f74 */ { 0xe6, 0x0c }, + /* f75 */ { 0xe7, 0x0c }, + /* f76 */ { 0xe8, 0x0c }, + /* f77 */ { 0xf1, 0x0c }, + /* f78 */ { 0xf2, 0x0c }, + /* f79 */ { 0xf3, 0x0c }, + /* f7a */ { 0xf4, 0x0c }, + /* f7b */ { 0xf5, 0x0c }, + /* f7c */ { 0xf6, 0x0c }, + /* f7d */ { 0xf7, 0x0c }, + /* f7e */ { 0xf8, 0x0c }, + /* f7f */ { 0x10, 0x0c }, + /* f80 */ { 0x00, 0x05 }, + /* f81 */ { 0x00, 0x05 }, + /* f82 */ { 0x00, 0x05 }, + /* f83 */ { 0x00, 0x05 }, + /* f84 */ { 0x00, 0x05 }, + /* f85 */ { 0x00, 0x05 }, + /* f86 */ { 0x00, 0x05 }, + /* f87 */ { 0x00, 0x05 }, + /* f88 */ { 0x00, 0x05 }, + /* f89 */ { 0x00, 0x05 }, + /* f8a */ { 0x00, 0x05 }, + /* f8b */ { 0x00, 0x05 }, + /* f8c */ { 0x00, 0x05 }, + /* f8d */ { 0x00, 0x05 }, + /* f8e */ { 0x00, 0x05 }, + /* f8f */ { 0x00, 0x05 }, + /* f90 */ { 0x00, 0x05 }, + /* f91 */ { 0x00, 0x05 }, + /* f92 */ { 0x00, 0x05 }, + /* f93 */ { 0x00, 0x05 }, + /* f94 */ { 0x00, 0x05 }, + /* f95 */ { 0x00, 0x05 }, + /* f96 */ { 0x00, 0x05 }, + /* f97 */ { 0x00, 0x05 }, + /* f98 */ { 0x00, 0x05 }, + /* f99 */ { 0x00, 0x05 }, + /* f9a */ { 0x00, 0x05 }, + /* f9b */ { 0x00, 0x05 }, + /* f9c */ { 0x00, 0x05 }, + /* f9d */ { 0x00, 0x05 }, + /* f9e */ { 0x00, 0x05 }, + /* f9f */ { 0x00, 0x05 }, + /* fa0 */ { 0x00, 0x05 }, + /* fa1 */ { 0x00, 0x05 }, + /* fa2 */ { 0x00, 0x05 }, + /* fa3 */ { 0x00, 0x05 }, + /* fa4 */ { 0x00, 0x05 }, + /* fa5 */ { 0x00, 0x05 }, + /* fa6 */ { 0x00, 0x05 }, + /* fa7 */ { 0x00, 0x05 }, + /* fa8 */ { 0x00, 0x05 }, + /* fa9 */ { 0x00, 0x05 }, + /* faa */ { 0x00, 0x05 }, + /* fab */ { 0x00, 0x05 }, + /* fac */ { 0x00, 0x05 }, + /* fad */ { 0x00, 0x05 }, + /* fae */ { 0x00, 0x05 }, + /* faf */ { 0x00, 0x05 }, + /* fb0 */ { 0x00, 0x05 }, + /* fb1 */ { 0x00, 0x05 }, + /* fb2 */ { 0x00, 0x05 }, + /* fb3 */ { 0x00, 0x05 }, + /* fb4 */ { 0x00, 0x05 }, + /* fb5 */ { 0x00, 0x05 }, + /* fb6 */ { 0x00, 0x05 }, + /* fb7 */ { 0x00, 0x05 }, + /* fb8 */ { 0x00, 0x05 }, + /* fb9 */ { 0x00, 0x05 }, + /* fba */ { 0x00, 0x05 }, + /* fbb */ { 0x00, 0x05 }, + /* fbc */ { 0x00, 0x05 }, + /* fbd */ { 0x00, 0x05 }, + /* fbe */ { 0x00, 0x05 }, + /* fbf */ { 0x00, 0x05 }, + /* fc0 */ { 0x00, 0x05 }, + /* fc1 */ { 0x00, 0x05 }, + /* fc2 */ { 0x00, 0x05 }, + /* fc3 */ { 0x00, 0x05 }, + /* fc4 */ { 0x00, 0x05 }, + /* fc5 */ { 0x00, 0x05 }, + /* fc6 */ { 0x00, 0x05 }, + /* fc7 */ { 0x00, 0x05 }, + /* fc8 */ { 0x00, 0x05 }, + /* fc9 */ { 0x00, 0x05 }, + /* fca */ { 0x00, 0x05 }, + /* fcb */ { 0x00, 0x05 }, + /* fcc */ { 0x00, 0x05 }, + /* fcd */ { 0x00, 0x05 }, + /* fce */ { 0x00, 0x05 }, + /* fcf */ { 0x00, 0x05 }, + /* fd0 */ { 0x00, 0x05 }, + /* fd1 */ { 0x00, 0x05 }, + /* fd2 */ { 0x00, 0x05 }, + /* fd3 */ { 0x00, 0x05 }, + /* fd4 */ { 0x00, 0x05 }, + /* fd5 */ { 0x00, 0x05 }, + /* fd6 */ { 0x00, 0x05 }, + /* fd7 */ { 0x00, 0x05 }, + /* fd8 */ { 0x00, 0x05 }, + /* fd9 */ { 0x00, 0x05 }, + /* fda */ { 0x00, 0x05 }, + /* fdb */ { 0x00, 0x05 }, + /* fdc */ { 0x00, 0x05 }, + /* fdd */ { 0x00, 0x05 }, + /* fde */ { 0x00, 0x05 }, + /* fdf */ { 0x00, 0x05 }, + /* fe0 */ { 0x00, 0x05 }, + /* fe1 */ { 0x00, 0x05 }, + /* fe2 */ { 0x00, 0x05 }, + /* fe3 */ { 0x00, 0x05 }, + /* fe4 */ { 0x00, 0x05 }, + /* fe5 */ { 0x00, 0x05 }, + /* fe6 */ { 0x00, 0x05 }, + /* fe7 */ { 0x00, 0x05 }, + /* fe8 */ { 0x00, 0x05 }, + /* fe9 */ { 0x00, 0x05 }, + /* fea */ { 0x00, 0x05 }, + /* feb */ { 0x00, 0x05 }, + /* fec */ { 0x00, 0x05 }, + /* fed */ { 0x00, 0x05 }, + /* fee */ { 0x00, 0x05 }, + /* fef */ { 0x00, 0x05 }, + /* ff0 */ { 0x00, 0x05 }, + /* ff1 */ { 0x00, 0x05 }, + /* ff2 */ { 0x00, 0x05 }, + /* ff3 */ { 0x00, 0x05 }, + /* ff4 */ { 0x00, 0x05 }, + /* ff5 */ { 0x00, 0x05 }, + /* ff6 */ { 0x00, 0x05 }, + /* ff7 */ { 0x00, 0x05 }, + /* ff8 */ { 0x00, 0x05 }, + /* ff9 */ { 0x00, 0x05 }, + /* ffa */ { 0x00, 0x05 }, + /* ffb */ { 0x00, 0x05 }, + /* ffc */ { 0x00, 0x05 }, + /* ffd */ { 0x00, 0x05 }, + /* ffe */ { 0x00, 0x05 }, + /* fff */ { 0x00, 0x05 }, diff --git a/waterbox/pcfx/rainbow_acy.inc b/waterbox/pcfx/rainbow_acy.inc new file mode 100644 index 0000000000..8c36f55de6 --- /dev/null +++ b/waterbox/pcfx/rainbow_acy.inc @@ -0,0 +1,4096 @@ + /* 000 */ { 0x01, 0x02 }, + /* 001 */ { 0x01, 0x02 }, + /* 002 */ { 0x01, 0x02 }, + /* 003 */ { 0x01, 0x02 }, + /* 004 */ { 0x01, 0x02 }, + /* 005 */ { 0x01, 0x02 }, + /* 006 */ { 0x01, 0x02 }, + /* 007 */ { 0x01, 0x02 }, + /* 008 */ { 0x01, 0x02 }, + /* 009 */ { 0x01, 0x02 }, + /* 00a */ { 0x01, 0x02 }, + /* 00b */ { 0x01, 0x02 }, + /* 00c */ { 0x01, 0x02 }, + /* 00d */ { 0x01, 0x02 }, + /* 00e */ { 0x01, 0x02 }, + /* 00f */ { 0x01, 0x02 }, + /* 010 */ { 0x01, 0x02 }, + /* 011 */ { 0x01, 0x02 }, + /* 012 */ { 0x01, 0x02 }, + /* 013 */ { 0x01, 0x02 }, + /* 014 */ { 0x01, 0x02 }, + /* 015 */ { 0x01, 0x02 }, + /* 016 */ { 0x01, 0x02 }, + /* 017 */ { 0x01, 0x02 }, + /* 018 */ { 0x01, 0x02 }, + /* 019 */ { 0x01, 0x02 }, + /* 01a */ { 0x01, 0x02 }, + /* 01b */ { 0x01, 0x02 }, + /* 01c */ { 0x01, 0x02 }, + /* 01d */ { 0x01, 0x02 }, + /* 01e */ { 0x01, 0x02 }, + /* 01f */ { 0x01, 0x02 }, + /* 020 */ { 0x01, 0x02 }, + /* 021 */ { 0x01, 0x02 }, + /* 022 */ { 0x01, 0x02 }, + /* 023 */ { 0x01, 0x02 }, + /* 024 */ { 0x01, 0x02 }, + /* 025 */ { 0x01, 0x02 }, + /* 026 */ { 0x01, 0x02 }, + /* 027 */ { 0x01, 0x02 }, + /* 028 */ { 0x01, 0x02 }, + /* 029 */ { 0x01, 0x02 }, + /* 02a */ { 0x01, 0x02 }, + /* 02b */ { 0x01, 0x02 }, + /* 02c */ { 0x01, 0x02 }, + /* 02d */ { 0x01, 0x02 }, + /* 02e */ { 0x01, 0x02 }, + /* 02f */ { 0x01, 0x02 }, + /* 030 */ { 0x01, 0x02 }, + /* 031 */ { 0x01, 0x02 }, + /* 032 */ { 0x01, 0x02 }, + /* 033 */ { 0x01, 0x02 }, + /* 034 */ { 0x01, 0x02 }, + /* 035 */ { 0x01, 0x02 }, + /* 036 */ { 0x01, 0x02 }, + /* 037 */ { 0x01, 0x02 }, + /* 038 */ { 0x01, 0x02 }, + /* 039 */ { 0x01, 0x02 }, + /* 03a */ { 0x01, 0x02 }, + /* 03b */ { 0x01, 0x02 }, + /* 03c */ { 0x01, 0x02 }, + /* 03d */ { 0x01, 0x02 }, + /* 03e */ { 0x01, 0x02 }, + /* 03f */ { 0x01, 0x02 }, + /* 040 */ { 0x01, 0x02 }, + /* 041 */ { 0x01, 0x02 }, + /* 042 */ { 0x01, 0x02 }, + /* 043 */ { 0x01, 0x02 }, + /* 044 */ { 0x01, 0x02 }, + /* 045 */ { 0x01, 0x02 }, + /* 046 */ { 0x01, 0x02 }, + /* 047 */ { 0x01, 0x02 }, + /* 048 */ { 0x01, 0x02 }, + /* 049 */ { 0x01, 0x02 }, + /* 04a */ { 0x01, 0x02 }, + /* 04b */ { 0x01, 0x02 }, + /* 04c */ { 0x01, 0x02 }, + /* 04d */ { 0x01, 0x02 }, + /* 04e */ { 0x01, 0x02 }, + /* 04f */ { 0x01, 0x02 }, + /* 050 */ { 0x01, 0x02 }, + /* 051 */ { 0x01, 0x02 }, + /* 052 */ { 0x01, 0x02 }, + /* 053 */ { 0x01, 0x02 }, + /* 054 */ { 0x01, 0x02 }, + /* 055 */ { 0x01, 0x02 }, + /* 056 */ { 0x01, 0x02 }, + /* 057 */ { 0x01, 0x02 }, + /* 058 */ { 0x01, 0x02 }, + /* 059 */ { 0x01, 0x02 }, + /* 05a */ { 0x01, 0x02 }, + /* 05b */ { 0x01, 0x02 }, + /* 05c */ { 0x01, 0x02 }, + /* 05d */ { 0x01, 0x02 }, + /* 05e */ { 0x01, 0x02 }, + /* 05f */ { 0x01, 0x02 }, + /* 060 */ { 0x01, 0x02 }, + /* 061 */ { 0x01, 0x02 }, + /* 062 */ { 0x01, 0x02 }, + /* 063 */ { 0x01, 0x02 }, + /* 064 */ { 0x01, 0x02 }, + /* 065 */ { 0x01, 0x02 }, + /* 066 */ { 0x01, 0x02 }, + /* 067 */ { 0x01, 0x02 }, + /* 068 */ { 0x01, 0x02 }, + /* 069 */ { 0x01, 0x02 }, + /* 06a */ { 0x01, 0x02 }, + /* 06b */ { 0x01, 0x02 }, + /* 06c */ { 0x01, 0x02 }, + /* 06d */ { 0x01, 0x02 }, + /* 06e */ { 0x01, 0x02 }, + /* 06f */ { 0x01, 0x02 }, + /* 070 */ { 0x01, 0x02 }, + /* 071 */ { 0x01, 0x02 }, + /* 072 */ { 0x01, 0x02 }, + /* 073 */ { 0x01, 0x02 }, + /* 074 */ { 0x01, 0x02 }, + /* 075 */ { 0x01, 0x02 }, + /* 076 */ { 0x01, 0x02 }, + /* 077 */ { 0x01, 0x02 }, + /* 078 */ { 0x01, 0x02 }, + /* 079 */ { 0x01, 0x02 }, + /* 07a */ { 0x01, 0x02 }, + /* 07b */ { 0x01, 0x02 }, + /* 07c */ { 0x01, 0x02 }, + /* 07d */ { 0x01, 0x02 }, + /* 07e */ { 0x01, 0x02 }, + /* 07f */ { 0x01, 0x02 }, + /* 080 */ { 0x01, 0x02 }, + /* 081 */ { 0x01, 0x02 }, + /* 082 */ { 0x01, 0x02 }, + /* 083 */ { 0x01, 0x02 }, + /* 084 */ { 0x01, 0x02 }, + /* 085 */ { 0x01, 0x02 }, + /* 086 */ { 0x01, 0x02 }, + /* 087 */ { 0x01, 0x02 }, + /* 088 */ { 0x01, 0x02 }, + /* 089 */ { 0x01, 0x02 }, + /* 08a */ { 0x01, 0x02 }, + /* 08b */ { 0x01, 0x02 }, + /* 08c */ { 0x01, 0x02 }, + /* 08d */ { 0x01, 0x02 }, + /* 08e */ { 0x01, 0x02 }, + /* 08f */ { 0x01, 0x02 }, + /* 090 */ { 0x01, 0x02 }, + /* 091 */ { 0x01, 0x02 }, + /* 092 */ { 0x01, 0x02 }, + /* 093 */ { 0x01, 0x02 }, + /* 094 */ { 0x01, 0x02 }, + /* 095 */ { 0x01, 0x02 }, + /* 096 */ { 0x01, 0x02 }, + /* 097 */ { 0x01, 0x02 }, + /* 098 */ { 0x01, 0x02 }, + /* 099 */ { 0x01, 0x02 }, + /* 09a */ { 0x01, 0x02 }, + /* 09b */ { 0x01, 0x02 }, + /* 09c */ { 0x01, 0x02 }, + /* 09d */ { 0x01, 0x02 }, + /* 09e */ { 0x01, 0x02 }, + /* 09f */ { 0x01, 0x02 }, + /* 0a0 */ { 0x01, 0x02 }, + /* 0a1 */ { 0x01, 0x02 }, + /* 0a2 */ { 0x01, 0x02 }, + /* 0a3 */ { 0x01, 0x02 }, + /* 0a4 */ { 0x01, 0x02 }, + /* 0a5 */ { 0x01, 0x02 }, + /* 0a6 */ { 0x01, 0x02 }, + /* 0a7 */ { 0x01, 0x02 }, + /* 0a8 */ { 0x01, 0x02 }, + /* 0a9 */ { 0x01, 0x02 }, + /* 0aa */ { 0x01, 0x02 }, + /* 0ab */ { 0x01, 0x02 }, + /* 0ac */ { 0x01, 0x02 }, + /* 0ad */ { 0x01, 0x02 }, + /* 0ae */ { 0x01, 0x02 }, + /* 0af */ { 0x01, 0x02 }, + /* 0b0 */ { 0x01, 0x02 }, + /* 0b1 */ { 0x01, 0x02 }, + /* 0b2 */ { 0x01, 0x02 }, + /* 0b3 */ { 0x01, 0x02 }, + /* 0b4 */ { 0x01, 0x02 }, + /* 0b5 */ { 0x01, 0x02 }, + /* 0b6 */ { 0x01, 0x02 }, + /* 0b7 */ { 0x01, 0x02 }, + /* 0b8 */ { 0x01, 0x02 }, + /* 0b9 */ { 0x01, 0x02 }, + /* 0ba */ { 0x01, 0x02 }, + /* 0bb */ { 0x01, 0x02 }, + /* 0bc */ { 0x01, 0x02 }, + /* 0bd */ { 0x01, 0x02 }, + /* 0be */ { 0x01, 0x02 }, + /* 0bf */ { 0x01, 0x02 }, + /* 0c0 */ { 0x01, 0x02 }, + /* 0c1 */ { 0x01, 0x02 }, + /* 0c2 */ { 0x01, 0x02 }, + /* 0c3 */ { 0x01, 0x02 }, + /* 0c4 */ { 0x01, 0x02 }, + /* 0c5 */ { 0x01, 0x02 }, + /* 0c6 */ { 0x01, 0x02 }, + /* 0c7 */ { 0x01, 0x02 }, + /* 0c8 */ { 0x01, 0x02 }, + /* 0c9 */ { 0x01, 0x02 }, + /* 0ca */ { 0x01, 0x02 }, + /* 0cb */ { 0x01, 0x02 }, + /* 0cc */ { 0x01, 0x02 }, + /* 0cd */ { 0x01, 0x02 }, + /* 0ce */ { 0x01, 0x02 }, + /* 0cf */ { 0x01, 0x02 }, + /* 0d0 */ { 0x01, 0x02 }, + /* 0d1 */ { 0x01, 0x02 }, + /* 0d2 */ { 0x01, 0x02 }, + /* 0d3 */ { 0x01, 0x02 }, + /* 0d4 */ { 0x01, 0x02 }, + /* 0d5 */ { 0x01, 0x02 }, + /* 0d6 */ { 0x01, 0x02 }, + /* 0d7 */ { 0x01, 0x02 }, + /* 0d8 */ { 0x01, 0x02 }, + /* 0d9 */ { 0x01, 0x02 }, + /* 0da */ { 0x01, 0x02 }, + /* 0db */ { 0x01, 0x02 }, + /* 0dc */ { 0x01, 0x02 }, + /* 0dd */ { 0x01, 0x02 }, + /* 0de */ { 0x01, 0x02 }, + /* 0df */ { 0x01, 0x02 }, + /* 0e0 */ { 0x01, 0x02 }, + /* 0e1 */ { 0x01, 0x02 }, + /* 0e2 */ { 0x01, 0x02 }, + /* 0e3 */ { 0x01, 0x02 }, + /* 0e4 */ { 0x01, 0x02 }, + /* 0e5 */ { 0x01, 0x02 }, + /* 0e6 */ { 0x01, 0x02 }, + /* 0e7 */ { 0x01, 0x02 }, + /* 0e8 */ { 0x01, 0x02 }, + /* 0e9 */ { 0x01, 0x02 }, + /* 0ea */ { 0x01, 0x02 }, + /* 0eb */ { 0x01, 0x02 }, + /* 0ec */ { 0x01, 0x02 }, + /* 0ed */ { 0x01, 0x02 }, + /* 0ee */ { 0x01, 0x02 }, + /* 0ef */ { 0x01, 0x02 }, + /* 0f0 */ { 0x01, 0x02 }, + /* 0f1 */ { 0x01, 0x02 }, + /* 0f2 */ { 0x01, 0x02 }, + /* 0f3 */ { 0x01, 0x02 }, + /* 0f4 */ { 0x01, 0x02 }, + /* 0f5 */ { 0x01, 0x02 }, + /* 0f6 */ { 0x01, 0x02 }, + /* 0f7 */ { 0x01, 0x02 }, + /* 0f8 */ { 0x01, 0x02 }, + /* 0f9 */ { 0x01, 0x02 }, + /* 0fa */ { 0x01, 0x02 }, + /* 0fb */ { 0x01, 0x02 }, + /* 0fc */ { 0x01, 0x02 }, + /* 0fd */ { 0x01, 0x02 }, + /* 0fe */ { 0x01, 0x02 }, + /* 0ff */ { 0x01, 0x02 }, + /* 100 */ { 0x01, 0x02 }, + /* 101 */ { 0x01, 0x02 }, + /* 102 */ { 0x01, 0x02 }, + /* 103 */ { 0x01, 0x02 }, + /* 104 */ { 0x01, 0x02 }, + /* 105 */ { 0x01, 0x02 }, + /* 106 */ { 0x01, 0x02 }, + /* 107 */ { 0x01, 0x02 }, + /* 108 */ { 0x01, 0x02 }, + /* 109 */ { 0x01, 0x02 }, + /* 10a */ { 0x01, 0x02 }, + /* 10b */ { 0x01, 0x02 }, + /* 10c */ { 0x01, 0x02 }, + /* 10d */ { 0x01, 0x02 }, + /* 10e */ { 0x01, 0x02 }, + /* 10f */ { 0x01, 0x02 }, + /* 110 */ { 0x01, 0x02 }, + /* 111 */ { 0x01, 0x02 }, + /* 112 */ { 0x01, 0x02 }, + /* 113 */ { 0x01, 0x02 }, + /* 114 */ { 0x01, 0x02 }, + /* 115 */ { 0x01, 0x02 }, + /* 116 */ { 0x01, 0x02 }, + /* 117 */ { 0x01, 0x02 }, + /* 118 */ { 0x01, 0x02 }, + /* 119 */ { 0x01, 0x02 }, + /* 11a */ { 0x01, 0x02 }, + /* 11b */ { 0x01, 0x02 }, + /* 11c */ { 0x01, 0x02 }, + /* 11d */ { 0x01, 0x02 }, + /* 11e */ { 0x01, 0x02 }, + /* 11f */ { 0x01, 0x02 }, + /* 120 */ { 0x01, 0x02 }, + /* 121 */ { 0x01, 0x02 }, + /* 122 */ { 0x01, 0x02 }, + /* 123 */ { 0x01, 0x02 }, + /* 124 */ { 0x01, 0x02 }, + /* 125 */ { 0x01, 0x02 }, + /* 126 */ { 0x01, 0x02 }, + /* 127 */ { 0x01, 0x02 }, + /* 128 */ { 0x01, 0x02 }, + /* 129 */ { 0x01, 0x02 }, + /* 12a */ { 0x01, 0x02 }, + /* 12b */ { 0x01, 0x02 }, + /* 12c */ { 0x01, 0x02 }, + /* 12d */ { 0x01, 0x02 }, + /* 12e */ { 0x01, 0x02 }, + /* 12f */ { 0x01, 0x02 }, + /* 130 */ { 0x01, 0x02 }, + /* 131 */ { 0x01, 0x02 }, + /* 132 */ { 0x01, 0x02 }, + /* 133 */ { 0x01, 0x02 }, + /* 134 */ { 0x01, 0x02 }, + /* 135 */ { 0x01, 0x02 }, + /* 136 */ { 0x01, 0x02 }, + /* 137 */ { 0x01, 0x02 }, + /* 138 */ { 0x01, 0x02 }, + /* 139 */ { 0x01, 0x02 }, + /* 13a */ { 0x01, 0x02 }, + /* 13b */ { 0x01, 0x02 }, + /* 13c */ { 0x01, 0x02 }, + /* 13d */ { 0x01, 0x02 }, + /* 13e */ { 0x01, 0x02 }, + /* 13f */ { 0x01, 0x02 }, + /* 140 */ { 0x01, 0x02 }, + /* 141 */ { 0x01, 0x02 }, + /* 142 */ { 0x01, 0x02 }, + /* 143 */ { 0x01, 0x02 }, + /* 144 */ { 0x01, 0x02 }, + /* 145 */ { 0x01, 0x02 }, + /* 146 */ { 0x01, 0x02 }, + /* 147 */ { 0x01, 0x02 }, + /* 148 */ { 0x01, 0x02 }, + /* 149 */ { 0x01, 0x02 }, + /* 14a */ { 0x01, 0x02 }, + /* 14b */ { 0x01, 0x02 }, + /* 14c */ { 0x01, 0x02 }, + /* 14d */ { 0x01, 0x02 }, + /* 14e */ { 0x01, 0x02 }, + /* 14f */ { 0x01, 0x02 }, + /* 150 */ { 0x01, 0x02 }, + /* 151 */ { 0x01, 0x02 }, + /* 152 */ { 0x01, 0x02 }, + /* 153 */ { 0x01, 0x02 }, + /* 154 */ { 0x01, 0x02 }, + /* 155 */ { 0x01, 0x02 }, + /* 156 */ { 0x01, 0x02 }, + /* 157 */ { 0x01, 0x02 }, + /* 158 */ { 0x01, 0x02 }, + /* 159 */ { 0x01, 0x02 }, + /* 15a */ { 0x01, 0x02 }, + /* 15b */ { 0x01, 0x02 }, + /* 15c */ { 0x01, 0x02 }, + /* 15d */ { 0x01, 0x02 }, + /* 15e */ { 0x01, 0x02 }, + /* 15f */ { 0x01, 0x02 }, + /* 160 */ { 0x01, 0x02 }, + /* 161 */ { 0x01, 0x02 }, + /* 162 */ { 0x01, 0x02 }, + /* 163 */ { 0x01, 0x02 }, + /* 164 */ { 0x01, 0x02 }, + /* 165 */ { 0x01, 0x02 }, + /* 166 */ { 0x01, 0x02 }, + /* 167 */ { 0x01, 0x02 }, + /* 168 */ { 0x01, 0x02 }, + /* 169 */ { 0x01, 0x02 }, + /* 16a */ { 0x01, 0x02 }, + /* 16b */ { 0x01, 0x02 }, + /* 16c */ { 0x01, 0x02 }, + /* 16d */ { 0x01, 0x02 }, + /* 16e */ { 0x01, 0x02 }, + /* 16f */ { 0x01, 0x02 }, + /* 170 */ { 0x01, 0x02 }, + /* 171 */ { 0x01, 0x02 }, + /* 172 */ { 0x01, 0x02 }, + /* 173 */ { 0x01, 0x02 }, + /* 174 */ { 0x01, 0x02 }, + /* 175 */ { 0x01, 0x02 }, + /* 176 */ { 0x01, 0x02 }, + /* 177 */ { 0x01, 0x02 }, + /* 178 */ { 0x01, 0x02 }, + /* 179 */ { 0x01, 0x02 }, + /* 17a */ { 0x01, 0x02 }, + /* 17b */ { 0x01, 0x02 }, + /* 17c */ { 0x01, 0x02 }, + /* 17d */ { 0x01, 0x02 }, + /* 17e */ { 0x01, 0x02 }, + /* 17f */ { 0x01, 0x02 }, + /* 180 */ { 0x01, 0x02 }, + /* 181 */ { 0x01, 0x02 }, + /* 182 */ { 0x01, 0x02 }, + /* 183 */ { 0x01, 0x02 }, + /* 184 */ { 0x01, 0x02 }, + /* 185 */ { 0x01, 0x02 }, + /* 186 */ { 0x01, 0x02 }, + /* 187 */ { 0x01, 0x02 }, + /* 188 */ { 0x01, 0x02 }, + /* 189 */ { 0x01, 0x02 }, + /* 18a */ { 0x01, 0x02 }, + /* 18b */ { 0x01, 0x02 }, + /* 18c */ { 0x01, 0x02 }, + /* 18d */ { 0x01, 0x02 }, + /* 18e */ { 0x01, 0x02 }, + /* 18f */ { 0x01, 0x02 }, + /* 190 */ { 0x01, 0x02 }, + /* 191 */ { 0x01, 0x02 }, + /* 192 */ { 0x01, 0x02 }, + /* 193 */ { 0x01, 0x02 }, + /* 194 */ { 0x01, 0x02 }, + /* 195 */ { 0x01, 0x02 }, + /* 196 */ { 0x01, 0x02 }, + /* 197 */ { 0x01, 0x02 }, + /* 198 */ { 0x01, 0x02 }, + /* 199 */ { 0x01, 0x02 }, + /* 19a */ { 0x01, 0x02 }, + /* 19b */ { 0x01, 0x02 }, + /* 19c */ { 0x01, 0x02 }, + /* 19d */ { 0x01, 0x02 }, + /* 19e */ { 0x01, 0x02 }, + /* 19f */ { 0x01, 0x02 }, + /* 1a0 */ { 0x01, 0x02 }, + /* 1a1 */ { 0x01, 0x02 }, + /* 1a2 */ { 0x01, 0x02 }, + /* 1a3 */ { 0x01, 0x02 }, + /* 1a4 */ { 0x01, 0x02 }, + /* 1a5 */ { 0x01, 0x02 }, + /* 1a6 */ { 0x01, 0x02 }, + /* 1a7 */ { 0x01, 0x02 }, + /* 1a8 */ { 0x01, 0x02 }, + /* 1a9 */ { 0x01, 0x02 }, + /* 1aa */ { 0x01, 0x02 }, + /* 1ab */ { 0x01, 0x02 }, + /* 1ac */ { 0x01, 0x02 }, + /* 1ad */ { 0x01, 0x02 }, + /* 1ae */ { 0x01, 0x02 }, + /* 1af */ { 0x01, 0x02 }, + /* 1b0 */ { 0x01, 0x02 }, + /* 1b1 */ { 0x01, 0x02 }, + /* 1b2 */ { 0x01, 0x02 }, + /* 1b3 */ { 0x01, 0x02 }, + /* 1b4 */ { 0x01, 0x02 }, + /* 1b5 */ { 0x01, 0x02 }, + /* 1b6 */ { 0x01, 0x02 }, + /* 1b7 */ { 0x01, 0x02 }, + /* 1b8 */ { 0x01, 0x02 }, + /* 1b9 */ { 0x01, 0x02 }, + /* 1ba */ { 0x01, 0x02 }, + /* 1bb */ { 0x01, 0x02 }, + /* 1bc */ { 0x01, 0x02 }, + /* 1bd */ { 0x01, 0x02 }, + /* 1be */ { 0x01, 0x02 }, + /* 1bf */ { 0x01, 0x02 }, + /* 1c0 */ { 0x01, 0x02 }, + /* 1c1 */ { 0x01, 0x02 }, + /* 1c2 */ { 0x01, 0x02 }, + /* 1c3 */ { 0x01, 0x02 }, + /* 1c4 */ { 0x01, 0x02 }, + /* 1c5 */ { 0x01, 0x02 }, + /* 1c6 */ { 0x01, 0x02 }, + /* 1c7 */ { 0x01, 0x02 }, + /* 1c8 */ { 0x01, 0x02 }, + /* 1c9 */ { 0x01, 0x02 }, + /* 1ca */ { 0x01, 0x02 }, + /* 1cb */ { 0x01, 0x02 }, + /* 1cc */ { 0x01, 0x02 }, + /* 1cd */ { 0x01, 0x02 }, + /* 1ce */ { 0x01, 0x02 }, + /* 1cf */ { 0x01, 0x02 }, + /* 1d0 */ { 0x01, 0x02 }, + /* 1d1 */ { 0x01, 0x02 }, + /* 1d2 */ { 0x01, 0x02 }, + /* 1d3 */ { 0x01, 0x02 }, + /* 1d4 */ { 0x01, 0x02 }, + /* 1d5 */ { 0x01, 0x02 }, + /* 1d6 */ { 0x01, 0x02 }, + /* 1d7 */ { 0x01, 0x02 }, + /* 1d8 */ { 0x01, 0x02 }, + /* 1d9 */ { 0x01, 0x02 }, + /* 1da */ { 0x01, 0x02 }, + /* 1db */ { 0x01, 0x02 }, + /* 1dc */ { 0x01, 0x02 }, + /* 1dd */ { 0x01, 0x02 }, + /* 1de */ { 0x01, 0x02 }, + /* 1df */ { 0x01, 0x02 }, + /* 1e0 */ { 0x01, 0x02 }, + /* 1e1 */ { 0x01, 0x02 }, + /* 1e2 */ { 0x01, 0x02 }, + /* 1e3 */ { 0x01, 0x02 }, + /* 1e4 */ { 0x01, 0x02 }, + /* 1e5 */ { 0x01, 0x02 }, + /* 1e6 */ { 0x01, 0x02 }, + /* 1e7 */ { 0x01, 0x02 }, + /* 1e8 */ { 0x01, 0x02 }, + /* 1e9 */ { 0x01, 0x02 }, + /* 1ea */ { 0x01, 0x02 }, + /* 1eb */ { 0x01, 0x02 }, + /* 1ec */ { 0x01, 0x02 }, + /* 1ed */ { 0x01, 0x02 }, + /* 1ee */ { 0x01, 0x02 }, + /* 1ef */ { 0x01, 0x02 }, + /* 1f0 */ { 0x01, 0x02 }, + /* 1f1 */ { 0x01, 0x02 }, + /* 1f2 */ { 0x01, 0x02 }, + /* 1f3 */ { 0x01, 0x02 }, + /* 1f4 */ { 0x01, 0x02 }, + /* 1f5 */ { 0x01, 0x02 }, + /* 1f6 */ { 0x01, 0x02 }, + /* 1f7 */ { 0x01, 0x02 }, + /* 1f8 */ { 0x01, 0x02 }, + /* 1f9 */ { 0x01, 0x02 }, + /* 1fa */ { 0x01, 0x02 }, + /* 1fb */ { 0x01, 0x02 }, + /* 1fc */ { 0x01, 0x02 }, + /* 1fd */ { 0x01, 0x02 }, + /* 1fe */ { 0x01, 0x02 }, + /* 1ff */ { 0x01, 0x02 }, + /* 200 */ { 0x01, 0x02 }, + /* 201 */ { 0x01, 0x02 }, + /* 202 */ { 0x01, 0x02 }, + /* 203 */ { 0x01, 0x02 }, + /* 204 */ { 0x01, 0x02 }, + /* 205 */ { 0x01, 0x02 }, + /* 206 */ { 0x01, 0x02 }, + /* 207 */ { 0x01, 0x02 }, + /* 208 */ { 0x01, 0x02 }, + /* 209 */ { 0x01, 0x02 }, + /* 20a */ { 0x01, 0x02 }, + /* 20b */ { 0x01, 0x02 }, + /* 20c */ { 0x01, 0x02 }, + /* 20d */ { 0x01, 0x02 }, + /* 20e */ { 0x01, 0x02 }, + /* 20f */ { 0x01, 0x02 }, + /* 210 */ { 0x01, 0x02 }, + /* 211 */ { 0x01, 0x02 }, + /* 212 */ { 0x01, 0x02 }, + /* 213 */ { 0x01, 0x02 }, + /* 214 */ { 0x01, 0x02 }, + /* 215 */ { 0x01, 0x02 }, + /* 216 */ { 0x01, 0x02 }, + /* 217 */ { 0x01, 0x02 }, + /* 218 */ { 0x01, 0x02 }, + /* 219 */ { 0x01, 0x02 }, + /* 21a */ { 0x01, 0x02 }, + /* 21b */ { 0x01, 0x02 }, + /* 21c */ { 0x01, 0x02 }, + /* 21d */ { 0x01, 0x02 }, + /* 21e */ { 0x01, 0x02 }, + /* 21f */ { 0x01, 0x02 }, + /* 220 */ { 0x01, 0x02 }, + /* 221 */ { 0x01, 0x02 }, + /* 222 */ { 0x01, 0x02 }, + /* 223 */ { 0x01, 0x02 }, + /* 224 */ { 0x01, 0x02 }, + /* 225 */ { 0x01, 0x02 }, + /* 226 */ { 0x01, 0x02 }, + /* 227 */ { 0x01, 0x02 }, + /* 228 */ { 0x01, 0x02 }, + /* 229 */ { 0x01, 0x02 }, + /* 22a */ { 0x01, 0x02 }, + /* 22b */ { 0x01, 0x02 }, + /* 22c */ { 0x01, 0x02 }, + /* 22d */ { 0x01, 0x02 }, + /* 22e */ { 0x01, 0x02 }, + /* 22f */ { 0x01, 0x02 }, + /* 230 */ { 0x01, 0x02 }, + /* 231 */ { 0x01, 0x02 }, + /* 232 */ { 0x01, 0x02 }, + /* 233 */ { 0x01, 0x02 }, + /* 234 */ { 0x01, 0x02 }, + /* 235 */ { 0x01, 0x02 }, + /* 236 */ { 0x01, 0x02 }, + /* 237 */ { 0x01, 0x02 }, + /* 238 */ { 0x01, 0x02 }, + /* 239 */ { 0x01, 0x02 }, + /* 23a */ { 0x01, 0x02 }, + /* 23b */ { 0x01, 0x02 }, + /* 23c */ { 0x01, 0x02 }, + /* 23d */ { 0x01, 0x02 }, + /* 23e */ { 0x01, 0x02 }, + /* 23f */ { 0x01, 0x02 }, + /* 240 */ { 0x01, 0x02 }, + /* 241 */ { 0x01, 0x02 }, + /* 242 */ { 0x01, 0x02 }, + /* 243 */ { 0x01, 0x02 }, + /* 244 */ { 0x01, 0x02 }, + /* 245 */ { 0x01, 0x02 }, + /* 246 */ { 0x01, 0x02 }, + /* 247 */ { 0x01, 0x02 }, + /* 248 */ { 0x01, 0x02 }, + /* 249 */ { 0x01, 0x02 }, + /* 24a */ { 0x01, 0x02 }, + /* 24b */ { 0x01, 0x02 }, + /* 24c */ { 0x01, 0x02 }, + /* 24d */ { 0x01, 0x02 }, + /* 24e */ { 0x01, 0x02 }, + /* 24f */ { 0x01, 0x02 }, + /* 250 */ { 0x01, 0x02 }, + /* 251 */ { 0x01, 0x02 }, + /* 252 */ { 0x01, 0x02 }, + /* 253 */ { 0x01, 0x02 }, + /* 254 */ { 0x01, 0x02 }, + /* 255 */ { 0x01, 0x02 }, + /* 256 */ { 0x01, 0x02 }, + /* 257 */ { 0x01, 0x02 }, + /* 258 */ { 0x01, 0x02 }, + /* 259 */ { 0x01, 0x02 }, + /* 25a */ { 0x01, 0x02 }, + /* 25b */ { 0x01, 0x02 }, + /* 25c */ { 0x01, 0x02 }, + /* 25d */ { 0x01, 0x02 }, + /* 25e */ { 0x01, 0x02 }, + /* 25f */ { 0x01, 0x02 }, + /* 260 */ { 0x01, 0x02 }, + /* 261 */ { 0x01, 0x02 }, + /* 262 */ { 0x01, 0x02 }, + /* 263 */ { 0x01, 0x02 }, + /* 264 */ { 0x01, 0x02 }, + /* 265 */ { 0x01, 0x02 }, + /* 266 */ { 0x01, 0x02 }, + /* 267 */ { 0x01, 0x02 }, + /* 268 */ { 0x01, 0x02 }, + /* 269 */ { 0x01, 0x02 }, + /* 26a */ { 0x01, 0x02 }, + /* 26b */ { 0x01, 0x02 }, + /* 26c */ { 0x01, 0x02 }, + /* 26d */ { 0x01, 0x02 }, + /* 26e */ { 0x01, 0x02 }, + /* 26f */ { 0x01, 0x02 }, + /* 270 */ { 0x01, 0x02 }, + /* 271 */ { 0x01, 0x02 }, + /* 272 */ { 0x01, 0x02 }, + /* 273 */ { 0x01, 0x02 }, + /* 274 */ { 0x01, 0x02 }, + /* 275 */ { 0x01, 0x02 }, + /* 276 */ { 0x01, 0x02 }, + /* 277 */ { 0x01, 0x02 }, + /* 278 */ { 0x01, 0x02 }, + /* 279 */ { 0x01, 0x02 }, + /* 27a */ { 0x01, 0x02 }, + /* 27b */ { 0x01, 0x02 }, + /* 27c */ { 0x01, 0x02 }, + /* 27d */ { 0x01, 0x02 }, + /* 27e */ { 0x01, 0x02 }, + /* 27f */ { 0x01, 0x02 }, + /* 280 */ { 0x01, 0x02 }, + /* 281 */ { 0x01, 0x02 }, + /* 282 */ { 0x01, 0x02 }, + /* 283 */ { 0x01, 0x02 }, + /* 284 */ { 0x01, 0x02 }, + /* 285 */ { 0x01, 0x02 }, + /* 286 */ { 0x01, 0x02 }, + /* 287 */ { 0x01, 0x02 }, + /* 288 */ { 0x01, 0x02 }, + /* 289 */ { 0x01, 0x02 }, + /* 28a */ { 0x01, 0x02 }, + /* 28b */ { 0x01, 0x02 }, + /* 28c */ { 0x01, 0x02 }, + /* 28d */ { 0x01, 0x02 }, + /* 28e */ { 0x01, 0x02 }, + /* 28f */ { 0x01, 0x02 }, + /* 290 */ { 0x01, 0x02 }, + /* 291 */ { 0x01, 0x02 }, + /* 292 */ { 0x01, 0x02 }, + /* 293 */ { 0x01, 0x02 }, + /* 294 */ { 0x01, 0x02 }, + /* 295 */ { 0x01, 0x02 }, + /* 296 */ { 0x01, 0x02 }, + /* 297 */ { 0x01, 0x02 }, + /* 298 */ { 0x01, 0x02 }, + /* 299 */ { 0x01, 0x02 }, + /* 29a */ { 0x01, 0x02 }, + /* 29b */ { 0x01, 0x02 }, + /* 29c */ { 0x01, 0x02 }, + /* 29d */ { 0x01, 0x02 }, + /* 29e */ { 0x01, 0x02 }, + /* 29f */ { 0x01, 0x02 }, + /* 2a0 */ { 0x01, 0x02 }, + /* 2a1 */ { 0x01, 0x02 }, + /* 2a2 */ { 0x01, 0x02 }, + /* 2a3 */ { 0x01, 0x02 }, + /* 2a4 */ { 0x01, 0x02 }, + /* 2a5 */ { 0x01, 0x02 }, + /* 2a6 */ { 0x01, 0x02 }, + /* 2a7 */ { 0x01, 0x02 }, + /* 2a8 */ { 0x01, 0x02 }, + /* 2a9 */ { 0x01, 0x02 }, + /* 2aa */ { 0x01, 0x02 }, + /* 2ab */ { 0x01, 0x02 }, + /* 2ac */ { 0x01, 0x02 }, + /* 2ad */ { 0x01, 0x02 }, + /* 2ae */ { 0x01, 0x02 }, + /* 2af */ { 0x01, 0x02 }, + /* 2b0 */ { 0x01, 0x02 }, + /* 2b1 */ { 0x01, 0x02 }, + /* 2b2 */ { 0x01, 0x02 }, + /* 2b3 */ { 0x01, 0x02 }, + /* 2b4 */ { 0x01, 0x02 }, + /* 2b5 */ { 0x01, 0x02 }, + /* 2b6 */ { 0x01, 0x02 }, + /* 2b7 */ { 0x01, 0x02 }, + /* 2b8 */ { 0x01, 0x02 }, + /* 2b9 */ { 0x01, 0x02 }, + /* 2ba */ { 0x01, 0x02 }, + /* 2bb */ { 0x01, 0x02 }, + /* 2bc */ { 0x01, 0x02 }, + /* 2bd */ { 0x01, 0x02 }, + /* 2be */ { 0x01, 0x02 }, + /* 2bf */ { 0x01, 0x02 }, + /* 2c0 */ { 0x01, 0x02 }, + /* 2c1 */ { 0x01, 0x02 }, + /* 2c2 */ { 0x01, 0x02 }, + /* 2c3 */ { 0x01, 0x02 }, + /* 2c4 */ { 0x01, 0x02 }, + /* 2c5 */ { 0x01, 0x02 }, + /* 2c6 */ { 0x01, 0x02 }, + /* 2c7 */ { 0x01, 0x02 }, + /* 2c8 */ { 0x01, 0x02 }, + /* 2c9 */ { 0x01, 0x02 }, + /* 2ca */ { 0x01, 0x02 }, + /* 2cb */ { 0x01, 0x02 }, + /* 2cc */ { 0x01, 0x02 }, + /* 2cd */ { 0x01, 0x02 }, + /* 2ce */ { 0x01, 0x02 }, + /* 2cf */ { 0x01, 0x02 }, + /* 2d0 */ { 0x01, 0x02 }, + /* 2d1 */ { 0x01, 0x02 }, + /* 2d2 */ { 0x01, 0x02 }, + /* 2d3 */ { 0x01, 0x02 }, + /* 2d4 */ { 0x01, 0x02 }, + /* 2d5 */ { 0x01, 0x02 }, + /* 2d6 */ { 0x01, 0x02 }, + /* 2d7 */ { 0x01, 0x02 }, + /* 2d8 */ { 0x01, 0x02 }, + /* 2d9 */ { 0x01, 0x02 }, + /* 2da */ { 0x01, 0x02 }, + /* 2db */ { 0x01, 0x02 }, + /* 2dc */ { 0x01, 0x02 }, + /* 2dd */ { 0x01, 0x02 }, + /* 2de */ { 0x01, 0x02 }, + /* 2df */ { 0x01, 0x02 }, + /* 2e0 */ { 0x01, 0x02 }, + /* 2e1 */ { 0x01, 0x02 }, + /* 2e2 */ { 0x01, 0x02 }, + /* 2e3 */ { 0x01, 0x02 }, + /* 2e4 */ { 0x01, 0x02 }, + /* 2e5 */ { 0x01, 0x02 }, + /* 2e6 */ { 0x01, 0x02 }, + /* 2e7 */ { 0x01, 0x02 }, + /* 2e8 */ { 0x01, 0x02 }, + /* 2e9 */ { 0x01, 0x02 }, + /* 2ea */ { 0x01, 0x02 }, + /* 2eb */ { 0x01, 0x02 }, + /* 2ec */ { 0x01, 0x02 }, + /* 2ed */ { 0x01, 0x02 }, + /* 2ee */ { 0x01, 0x02 }, + /* 2ef */ { 0x01, 0x02 }, + /* 2f0 */ { 0x01, 0x02 }, + /* 2f1 */ { 0x01, 0x02 }, + /* 2f2 */ { 0x01, 0x02 }, + /* 2f3 */ { 0x01, 0x02 }, + /* 2f4 */ { 0x01, 0x02 }, + /* 2f5 */ { 0x01, 0x02 }, + /* 2f6 */ { 0x01, 0x02 }, + /* 2f7 */ { 0x01, 0x02 }, + /* 2f8 */ { 0x01, 0x02 }, + /* 2f9 */ { 0x01, 0x02 }, + /* 2fa */ { 0x01, 0x02 }, + /* 2fb */ { 0x01, 0x02 }, + /* 2fc */ { 0x01, 0x02 }, + /* 2fd */ { 0x01, 0x02 }, + /* 2fe */ { 0x01, 0x02 }, + /* 2ff */ { 0x01, 0x02 }, + /* 300 */ { 0x01, 0x02 }, + /* 301 */ { 0x01, 0x02 }, + /* 302 */ { 0x01, 0x02 }, + /* 303 */ { 0x01, 0x02 }, + /* 304 */ { 0x01, 0x02 }, + /* 305 */ { 0x01, 0x02 }, + /* 306 */ { 0x01, 0x02 }, + /* 307 */ { 0x01, 0x02 }, + /* 308 */ { 0x01, 0x02 }, + /* 309 */ { 0x01, 0x02 }, + /* 30a */ { 0x01, 0x02 }, + /* 30b */ { 0x01, 0x02 }, + /* 30c */ { 0x01, 0x02 }, + /* 30d */ { 0x01, 0x02 }, + /* 30e */ { 0x01, 0x02 }, + /* 30f */ { 0x01, 0x02 }, + /* 310 */ { 0x01, 0x02 }, + /* 311 */ { 0x01, 0x02 }, + /* 312 */ { 0x01, 0x02 }, + /* 313 */ { 0x01, 0x02 }, + /* 314 */ { 0x01, 0x02 }, + /* 315 */ { 0x01, 0x02 }, + /* 316 */ { 0x01, 0x02 }, + /* 317 */ { 0x01, 0x02 }, + /* 318 */ { 0x01, 0x02 }, + /* 319 */ { 0x01, 0x02 }, + /* 31a */ { 0x01, 0x02 }, + /* 31b */ { 0x01, 0x02 }, + /* 31c */ { 0x01, 0x02 }, + /* 31d */ { 0x01, 0x02 }, + /* 31e */ { 0x01, 0x02 }, + /* 31f */ { 0x01, 0x02 }, + /* 320 */ { 0x01, 0x02 }, + /* 321 */ { 0x01, 0x02 }, + /* 322 */ { 0x01, 0x02 }, + /* 323 */ { 0x01, 0x02 }, + /* 324 */ { 0x01, 0x02 }, + /* 325 */ { 0x01, 0x02 }, + /* 326 */ { 0x01, 0x02 }, + /* 327 */ { 0x01, 0x02 }, + /* 328 */ { 0x01, 0x02 }, + /* 329 */ { 0x01, 0x02 }, + /* 32a */ { 0x01, 0x02 }, + /* 32b */ { 0x01, 0x02 }, + /* 32c */ { 0x01, 0x02 }, + /* 32d */ { 0x01, 0x02 }, + /* 32e */ { 0x01, 0x02 }, + /* 32f */ { 0x01, 0x02 }, + /* 330 */ { 0x01, 0x02 }, + /* 331 */ { 0x01, 0x02 }, + /* 332 */ { 0x01, 0x02 }, + /* 333 */ { 0x01, 0x02 }, + /* 334 */ { 0x01, 0x02 }, + /* 335 */ { 0x01, 0x02 }, + /* 336 */ { 0x01, 0x02 }, + /* 337 */ { 0x01, 0x02 }, + /* 338 */ { 0x01, 0x02 }, + /* 339 */ { 0x01, 0x02 }, + /* 33a */ { 0x01, 0x02 }, + /* 33b */ { 0x01, 0x02 }, + /* 33c */ { 0x01, 0x02 }, + /* 33d */ { 0x01, 0x02 }, + /* 33e */ { 0x01, 0x02 }, + /* 33f */ { 0x01, 0x02 }, + /* 340 */ { 0x01, 0x02 }, + /* 341 */ { 0x01, 0x02 }, + /* 342 */ { 0x01, 0x02 }, + /* 343 */ { 0x01, 0x02 }, + /* 344 */ { 0x01, 0x02 }, + /* 345 */ { 0x01, 0x02 }, + /* 346 */ { 0x01, 0x02 }, + /* 347 */ { 0x01, 0x02 }, + /* 348 */ { 0x01, 0x02 }, + /* 349 */ { 0x01, 0x02 }, + /* 34a */ { 0x01, 0x02 }, + /* 34b */ { 0x01, 0x02 }, + /* 34c */ { 0x01, 0x02 }, + /* 34d */ { 0x01, 0x02 }, + /* 34e */ { 0x01, 0x02 }, + /* 34f */ { 0x01, 0x02 }, + /* 350 */ { 0x01, 0x02 }, + /* 351 */ { 0x01, 0x02 }, + /* 352 */ { 0x01, 0x02 }, + /* 353 */ { 0x01, 0x02 }, + /* 354 */ { 0x01, 0x02 }, + /* 355 */ { 0x01, 0x02 }, + /* 356 */ { 0x01, 0x02 }, + /* 357 */ { 0x01, 0x02 }, + /* 358 */ { 0x01, 0x02 }, + /* 359 */ { 0x01, 0x02 }, + /* 35a */ { 0x01, 0x02 }, + /* 35b */ { 0x01, 0x02 }, + /* 35c */ { 0x01, 0x02 }, + /* 35d */ { 0x01, 0x02 }, + /* 35e */ { 0x01, 0x02 }, + /* 35f */ { 0x01, 0x02 }, + /* 360 */ { 0x01, 0x02 }, + /* 361 */ { 0x01, 0x02 }, + /* 362 */ { 0x01, 0x02 }, + /* 363 */ { 0x01, 0x02 }, + /* 364 */ { 0x01, 0x02 }, + /* 365 */ { 0x01, 0x02 }, + /* 366 */ { 0x01, 0x02 }, + /* 367 */ { 0x01, 0x02 }, + /* 368 */ { 0x01, 0x02 }, + /* 369 */ { 0x01, 0x02 }, + /* 36a */ { 0x01, 0x02 }, + /* 36b */ { 0x01, 0x02 }, + /* 36c */ { 0x01, 0x02 }, + /* 36d */ { 0x01, 0x02 }, + /* 36e */ { 0x01, 0x02 }, + /* 36f */ { 0x01, 0x02 }, + /* 370 */ { 0x01, 0x02 }, + /* 371 */ { 0x01, 0x02 }, + /* 372 */ { 0x01, 0x02 }, + /* 373 */ { 0x01, 0x02 }, + /* 374 */ { 0x01, 0x02 }, + /* 375 */ { 0x01, 0x02 }, + /* 376 */ { 0x01, 0x02 }, + /* 377 */ { 0x01, 0x02 }, + /* 378 */ { 0x01, 0x02 }, + /* 379 */ { 0x01, 0x02 }, + /* 37a */ { 0x01, 0x02 }, + /* 37b */ { 0x01, 0x02 }, + /* 37c */ { 0x01, 0x02 }, + /* 37d */ { 0x01, 0x02 }, + /* 37e */ { 0x01, 0x02 }, + /* 37f */ { 0x01, 0x02 }, + /* 380 */ { 0x01, 0x02 }, + /* 381 */ { 0x01, 0x02 }, + /* 382 */ { 0x01, 0x02 }, + /* 383 */ { 0x01, 0x02 }, + /* 384 */ { 0x01, 0x02 }, + /* 385 */ { 0x01, 0x02 }, + /* 386 */ { 0x01, 0x02 }, + /* 387 */ { 0x01, 0x02 }, + /* 388 */ { 0x01, 0x02 }, + /* 389 */ { 0x01, 0x02 }, + /* 38a */ { 0x01, 0x02 }, + /* 38b */ { 0x01, 0x02 }, + /* 38c */ { 0x01, 0x02 }, + /* 38d */ { 0x01, 0x02 }, + /* 38e */ { 0x01, 0x02 }, + /* 38f */ { 0x01, 0x02 }, + /* 390 */ { 0x01, 0x02 }, + /* 391 */ { 0x01, 0x02 }, + /* 392 */ { 0x01, 0x02 }, + /* 393 */ { 0x01, 0x02 }, + /* 394 */ { 0x01, 0x02 }, + /* 395 */ { 0x01, 0x02 }, + /* 396 */ { 0x01, 0x02 }, + /* 397 */ { 0x01, 0x02 }, + /* 398 */ { 0x01, 0x02 }, + /* 399 */ { 0x01, 0x02 }, + /* 39a */ { 0x01, 0x02 }, + /* 39b */ { 0x01, 0x02 }, + /* 39c */ { 0x01, 0x02 }, + /* 39d */ { 0x01, 0x02 }, + /* 39e */ { 0x01, 0x02 }, + /* 39f */ { 0x01, 0x02 }, + /* 3a0 */ { 0x01, 0x02 }, + /* 3a1 */ { 0x01, 0x02 }, + /* 3a2 */ { 0x01, 0x02 }, + /* 3a3 */ { 0x01, 0x02 }, + /* 3a4 */ { 0x01, 0x02 }, + /* 3a5 */ { 0x01, 0x02 }, + /* 3a6 */ { 0x01, 0x02 }, + /* 3a7 */ { 0x01, 0x02 }, + /* 3a8 */ { 0x01, 0x02 }, + /* 3a9 */ { 0x01, 0x02 }, + /* 3aa */ { 0x01, 0x02 }, + /* 3ab */ { 0x01, 0x02 }, + /* 3ac */ { 0x01, 0x02 }, + /* 3ad */ { 0x01, 0x02 }, + /* 3ae */ { 0x01, 0x02 }, + /* 3af */ { 0x01, 0x02 }, + /* 3b0 */ { 0x01, 0x02 }, + /* 3b1 */ { 0x01, 0x02 }, + /* 3b2 */ { 0x01, 0x02 }, + /* 3b3 */ { 0x01, 0x02 }, + /* 3b4 */ { 0x01, 0x02 }, + /* 3b5 */ { 0x01, 0x02 }, + /* 3b6 */ { 0x01, 0x02 }, + /* 3b7 */ { 0x01, 0x02 }, + /* 3b8 */ { 0x01, 0x02 }, + /* 3b9 */ { 0x01, 0x02 }, + /* 3ba */ { 0x01, 0x02 }, + /* 3bb */ { 0x01, 0x02 }, + /* 3bc */ { 0x01, 0x02 }, + /* 3bd */ { 0x01, 0x02 }, + /* 3be */ { 0x01, 0x02 }, + /* 3bf */ { 0x01, 0x02 }, + /* 3c0 */ { 0x01, 0x02 }, + /* 3c1 */ { 0x01, 0x02 }, + /* 3c2 */ { 0x01, 0x02 }, + /* 3c3 */ { 0x01, 0x02 }, + /* 3c4 */ { 0x01, 0x02 }, + /* 3c5 */ { 0x01, 0x02 }, + /* 3c6 */ { 0x01, 0x02 }, + /* 3c7 */ { 0x01, 0x02 }, + /* 3c8 */ { 0x01, 0x02 }, + /* 3c9 */ { 0x01, 0x02 }, + /* 3ca */ { 0x01, 0x02 }, + /* 3cb */ { 0x01, 0x02 }, + /* 3cc */ { 0x01, 0x02 }, + /* 3cd */ { 0x01, 0x02 }, + /* 3ce */ { 0x01, 0x02 }, + /* 3cf */ { 0x01, 0x02 }, + /* 3d0 */ { 0x01, 0x02 }, + /* 3d1 */ { 0x01, 0x02 }, + /* 3d2 */ { 0x01, 0x02 }, + /* 3d3 */ { 0x01, 0x02 }, + /* 3d4 */ { 0x01, 0x02 }, + /* 3d5 */ { 0x01, 0x02 }, + /* 3d6 */ { 0x01, 0x02 }, + /* 3d7 */ { 0x01, 0x02 }, + /* 3d8 */ { 0x01, 0x02 }, + /* 3d9 */ { 0x01, 0x02 }, + /* 3da */ { 0x01, 0x02 }, + /* 3db */ { 0x01, 0x02 }, + /* 3dc */ { 0x01, 0x02 }, + /* 3dd */ { 0x01, 0x02 }, + /* 3de */ { 0x01, 0x02 }, + /* 3df */ { 0x01, 0x02 }, + /* 3e0 */ { 0x01, 0x02 }, + /* 3e1 */ { 0x01, 0x02 }, + /* 3e2 */ { 0x01, 0x02 }, + /* 3e3 */ { 0x01, 0x02 }, + /* 3e4 */ { 0x01, 0x02 }, + /* 3e5 */ { 0x01, 0x02 }, + /* 3e6 */ { 0x01, 0x02 }, + /* 3e7 */ { 0x01, 0x02 }, + /* 3e8 */ { 0x01, 0x02 }, + /* 3e9 */ { 0x01, 0x02 }, + /* 3ea */ { 0x01, 0x02 }, + /* 3eb */ { 0x01, 0x02 }, + /* 3ec */ { 0x01, 0x02 }, + /* 3ed */ { 0x01, 0x02 }, + /* 3ee */ { 0x01, 0x02 }, + /* 3ef */ { 0x01, 0x02 }, + /* 3f0 */ { 0x01, 0x02 }, + /* 3f1 */ { 0x01, 0x02 }, + /* 3f2 */ { 0x01, 0x02 }, + /* 3f3 */ { 0x01, 0x02 }, + /* 3f4 */ { 0x01, 0x02 }, + /* 3f5 */ { 0x01, 0x02 }, + /* 3f6 */ { 0x01, 0x02 }, + /* 3f7 */ { 0x01, 0x02 }, + /* 3f8 */ { 0x01, 0x02 }, + /* 3f9 */ { 0x01, 0x02 }, + /* 3fa */ { 0x01, 0x02 }, + /* 3fb */ { 0x01, 0x02 }, + /* 3fc */ { 0x01, 0x02 }, + /* 3fd */ { 0x01, 0x02 }, + /* 3fe */ { 0x01, 0x02 }, + /* 3ff */ { 0x01, 0x02 }, + /* 400 */ { 0x02, 0x02 }, + /* 401 */ { 0x02, 0x02 }, + /* 402 */ { 0x02, 0x02 }, + /* 403 */ { 0x02, 0x02 }, + /* 404 */ { 0x02, 0x02 }, + /* 405 */ { 0x02, 0x02 }, + /* 406 */ { 0x02, 0x02 }, + /* 407 */ { 0x02, 0x02 }, + /* 408 */ { 0x02, 0x02 }, + /* 409 */ { 0x02, 0x02 }, + /* 40a */ { 0x02, 0x02 }, + /* 40b */ { 0x02, 0x02 }, + /* 40c */ { 0x02, 0x02 }, + /* 40d */ { 0x02, 0x02 }, + /* 40e */ { 0x02, 0x02 }, + /* 40f */ { 0x02, 0x02 }, + /* 410 */ { 0x02, 0x02 }, + /* 411 */ { 0x02, 0x02 }, + /* 412 */ { 0x02, 0x02 }, + /* 413 */ { 0x02, 0x02 }, + /* 414 */ { 0x02, 0x02 }, + /* 415 */ { 0x02, 0x02 }, + /* 416 */ { 0x02, 0x02 }, + /* 417 */ { 0x02, 0x02 }, + /* 418 */ { 0x02, 0x02 }, + /* 419 */ { 0x02, 0x02 }, + /* 41a */ { 0x02, 0x02 }, + /* 41b */ { 0x02, 0x02 }, + /* 41c */ { 0x02, 0x02 }, + /* 41d */ { 0x02, 0x02 }, + /* 41e */ { 0x02, 0x02 }, + /* 41f */ { 0x02, 0x02 }, + /* 420 */ { 0x02, 0x02 }, + /* 421 */ { 0x02, 0x02 }, + /* 422 */ { 0x02, 0x02 }, + /* 423 */ { 0x02, 0x02 }, + /* 424 */ { 0x02, 0x02 }, + /* 425 */ { 0x02, 0x02 }, + /* 426 */ { 0x02, 0x02 }, + /* 427 */ { 0x02, 0x02 }, + /* 428 */ { 0x02, 0x02 }, + /* 429 */ { 0x02, 0x02 }, + /* 42a */ { 0x02, 0x02 }, + /* 42b */ { 0x02, 0x02 }, + /* 42c */ { 0x02, 0x02 }, + /* 42d */ { 0x02, 0x02 }, + /* 42e */ { 0x02, 0x02 }, + /* 42f */ { 0x02, 0x02 }, + /* 430 */ { 0x02, 0x02 }, + /* 431 */ { 0x02, 0x02 }, + /* 432 */ { 0x02, 0x02 }, + /* 433 */ { 0x02, 0x02 }, + /* 434 */ { 0x02, 0x02 }, + /* 435 */ { 0x02, 0x02 }, + /* 436 */ { 0x02, 0x02 }, + /* 437 */ { 0x02, 0x02 }, + /* 438 */ { 0x02, 0x02 }, + /* 439 */ { 0x02, 0x02 }, + /* 43a */ { 0x02, 0x02 }, + /* 43b */ { 0x02, 0x02 }, + /* 43c */ { 0x02, 0x02 }, + /* 43d */ { 0x02, 0x02 }, + /* 43e */ { 0x02, 0x02 }, + /* 43f */ { 0x02, 0x02 }, + /* 440 */ { 0x02, 0x02 }, + /* 441 */ { 0x02, 0x02 }, + /* 442 */ { 0x02, 0x02 }, + /* 443 */ { 0x02, 0x02 }, + /* 444 */ { 0x02, 0x02 }, + /* 445 */ { 0x02, 0x02 }, + /* 446 */ { 0x02, 0x02 }, + /* 447 */ { 0x02, 0x02 }, + /* 448 */ { 0x02, 0x02 }, + /* 449 */ { 0x02, 0x02 }, + /* 44a */ { 0x02, 0x02 }, + /* 44b */ { 0x02, 0x02 }, + /* 44c */ { 0x02, 0x02 }, + /* 44d */ { 0x02, 0x02 }, + /* 44e */ { 0x02, 0x02 }, + /* 44f */ { 0x02, 0x02 }, + /* 450 */ { 0x02, 0x02 }, + /* 451 */ { 0x02, 0x02 }, + /* 452 */ { 0x02, 0x02 }, + /* 453 */ { 0x02, 0x02 }, + /* 454 */ { 0x02, 0x02 }, + /* 455 */ { 0x02, 0x02 }, + /* 456 */ { 0x02, 0x02 }, + /* 457 */ { 0x02, 0x02 }, + /* 458 */ { 0x02, 0x02 }, + /* 459 */ { 0x02, 0x02 }, + /* 45a */ { 0x02, 0x02 }, + /* 45b */ { 0x02, 0x02 }, + /* 45c */ { 0x02, 0x02 }, + /* 45d */ { 0x02, 0x02 }, + /* 45e */ { 0x02, 0x02 }, + /* 45f */ { 0x02, 0x02 }, + /* 460 */ { 0x02, 0x02 }, + /* 461 */ { 0x02, 0x02 }, + /* 462 */ { 0x02, 0x02 }, + /* 463 */ { 0x02, 0x02 }, + /* 464 */ { 0x02, 0x02 }, + /* 465 */ { 0x02, 0x02 }, + /* 466 */ { 0x02, 0x02 }, + /* 467 */ { 0x02, 0x02 }, + /* 468 */ { 0x02, 0x02 }, + /* 469 */ { 0x02, 0x02 }, + /* 46a */ { 0x02, 0x02 }, + /* 46b */ { 0x02, 0x02 }, + /* 46c */ { 0x02, 0x02 }, + /* 46d */ { 0x02, 0x02 }, + /* 46e */ { 0x02, 0x02 }, + /* 46f */ { 0x02, 0x02 }, + /* 470 */ { 0x02, 0x02 }, + /* 471 */ { 0x02, 0x02 }, + /* 472 */ { 0x02, 0x02 }, + /* 473 */ { 0x02, 0x02 }, + /* 474 */ { 0x02, 0x02 }, + /* 475 */ { 0x02, 0x02 }, + /* 476 */ { 0x02, 0x02 }, + /* 477 */ { 0x02, 0x02 }, + /* 478 */ { 0x02, 0x02 }, + /* 479 */ { 0x02, 0x02 }, + /* 47a */ { 0x02, 0x02 }, + /* 47b */ { 0x02, 0x02 }, + /* 47c */ { 0x02, 0x02 }, + /* 47d */ { 0x02, 0x02 }, + /* 47e */ { 0x02, 0x02 }, + /* 47f */ { 0x02, 0x02 }, + /* 480 */ { 0x02, 0x02 }, + /* 481 */ { 0x02, 0x02 }, + /* 482 */ { 0x02, 0x02 }, + /* 483 */ { 0x02, 0x02 }, + /* 484 */ { 0x02, 0x02 }, + /* 485 */ { 0x02, 0x02 }, + /* 486 */ { 0x02, 0x02 }, + /* 487 */ { 0x02, 0x02 }, + /* 488 */ { 0x02, 0x02 }, + /* 489 */ { 0x02, 0x02 }, + /* 48a */ { 0x02, 0x02 }, + /* 48b */ { 0x02, 0x02 }, + /* 48c */ { 0x02, 0x02 }, + /* 48d */ { 0x02, 0x02 }, + /* 48e */ { 0x02, 0x02 }, + /* 48f */ { 0x02, 0x02 }, + /* 490 */ { 0x02, 0x02 }, + /* 491 */ { 0x02, 0x02 }, + /* 492 */ { 0x02, 0x02 }, + /* 493 */ { 0x02, 0x02 }, + /* 494 */ { 0x02, 0x02 }, + /* 495 */ { 0x02, 0x02 }, + /* 496 */ { 0x02, 0x02 }, + /* 497 */ { 0x02, 0x02 }, + /* 498 */ { 0x02, 0x02 }, + /* 499 */ { 0x02, 0x02 }, + /* 49a */ { 0x02, 0x02 }, + /* 49b */ { 0x02, 0x02 }, + /* 49c */ { 0x02, 0x02 }, + /* 49d */ { 0x02, 0x02 }, + /* 49e */ { 0x02, 0x02 }, + /* 49f */ { 0x02, 0x02 }, + /* 4a0 */ { 0x02, 0x02 }, + /* 4a1 */ { 0x02, 0x02 }, + /* 4a2 */ { 0x02, 0x02 }, + /* 4a3 */ { 0x02, 0x02 }, + /* 4a4 */ { 0x02, 0x02 }, + /* 4a5 */ { 0x02, 0x02 }, + /* 4a6 */ { 0x02, 0x02 }, + /* 4a7 */ { 0x02, 0x02 }, + /* 4a8 */ { 0x02, 0x02 }, + /* 4a9 */ { 0x02, 0x02 }, + /* 4aa */ { 0x02, 0x02 }, + /* 4ab */ { 0x02, 0x02 }, + /* 4ac */ { 0x02, 0x02 }, + /* 4ad */ { 0x02, 0x02 }, + /* 4ae */ { 0x02, 0x02 }, + /* 4af */ { 0x02, 0x02 }, + /* 4b0 */ { 0x02, 0x02 }, + /* 4b1 */ { 0x02, 0x02 }, + /* 4b2 */ { 0x02, 0x02 }, + /* 4b3 */ { 0x02, 0x02 }, + /* 4b4 */ { 0x02, 0x02 }, + /* 4b5 */ { 0x02, 0x02 }, + /* 4b6 */ { 0x02, 0x02 }, + /* 4b7 */ { 0x02, 0x02 }, + /* 4b8 */ { 0x02, 0x02 }, + /* 4b9 */ { 0x02, 0x02 }, + /* 4ba */ { 0x02, 0x02 }, + /* 4bb */ { 0x02, 0x02 }, + /* 4bc */ { 0x02, 0x02 }, + /* 4bd */ { 0x02, 0x02 }, + /* 4be */ { 0x02, 0x02 }, + /* 4bf */ { 0x02, 0x02 }, + /* 4c0 */ { 0x02, 0x02 }, + /* 4c1 */ { 0x02, 0x02 }, + /* 4c2 */ { 0x02, 0x02 }, + /* 4c3 */ { 0x02, 0x02 }, + /* 4c4 */ { 0x02, 0x02 }, + /* 4c5 */ { 0x02, 0x02 }, + /* 4c6 */ { 0x02, 0x02 }, + /* 4c7 */ { 0x02, 0x02 }, + /* 4c8 */ { 0x02, 0x02 }, + /* 4c9 */ { 0x02, 0x02 }, + /* 4ca */ { 0x02, 0x02 }, + /* 4cb */ { 0x02, 0x02 }, + /* 4cc */ { 0x02, 0x02 }, + /* 4cd */ { 0x02, 0x02 }, + /* 4ce */ { 0x02, 0x02 }, + /* 4cf */ { 0x02, 0x02 }, + /* 4d0 */ { 0x02, 0x02 }, + /* 4d1 */ { 0x02, 0x02 }, + /* 4d2 */ { 0x02, 0x02 }, + /* 4d3 */ { 0x02, 0x02 }, + /* 4d4 */ { 0x02, 0x02 }, + /* 4d5 */ { 0x02, 0x02 }, + /* 4d6 */ { 0x02, 0x02 }, + /* 4d7 */ { 0x02, 0x02 }, + /* 4d8 */ { 0x02, 0x02 }, + /* 4d9 */ { 0x02, 0x02 }, + /* 4da */ { 0x02, 0x02 }, + /* 4db */ { 0x02, 0x02 }, + /* 4dc */ { 0x02, 0x02 }, + /* 4dd */ { 0x02, 0x02 }, + /* 4de */ { 0x02, 0x02 }, + /* 4df */ { 0x02, 0x02 }, + /* 4e0 */ { 0x02, 0x02 }, + /* 4e1 */ { 0x02, 0x02 }, + /* 4e2 */ { 0x02, 0x02 }, + /* 4e3 */ { 0x02, 0x02 }, + /* 4e4 */ { 0x02, 0x02 }, + /* 4e5 */ { 0x02, 0x02 }, + /* 4e6 */ { 0x02, 0x02 }, + /* 4e7 */ { 0x02, 0x02 }, + /* 4e8 */ { 0x02, 0x02 }, + /* 4e9 */ { 0x02, 0x02 }, + /* 4ea */ { 0x02, 0x02 }, + /* 4eb */ { 0x02, 0x02 }, + /* 4ec */ { 0x02, 0x02 }, + /* 4ed */ { 0x02, 0x02 }, + /* 4ee */ { 0x02, 0x02 }, + /* 4ef */ { 0x02, 0x02 }, + /* 4f0 */ { 0x02, 0x02 }, + /* 4f1 */ { 0x02, 0x02 }, + /* 4f2 */ { 0x02, 0x02 }, + /* 4f3 */ { 0x02, 0x02 }, + /* 4f4 */ { 0x02, 0x02 }, + /* 4f5 */ { 0x02, 0x02 }, + /* 4f6 */ { 0x02, 0x02 }, + /* 4f7 */ { 0x02, 0x02 }, + /* 4f8 */ { 0x02, 0x02 }, + /* 4f9 */ { 0x02, 0x02 }, + /* 4fa */ { 0x02, 0x02 }, + /* 4fb */ { 0x02, 0x02 }, + /* 4fc */ { 0x02, 0x02 }, + /* 4fd */ { 0x02, 0x02 }, + /* 4fe */ { 0x02, 0x02 }, + /* 4ff */ { 0x02, 0x02 }, + /* 500 */ { 0x02, 0x02 }, + /* 501 */ { 0x02, 0x02 }, + /* 502 */ { 0x02, 0x02 }, + /* 503 */ { 0x02, 0x02 }, + /* 504 */ { 0x02, 0x02 }, + /* 505 */ { 0x02, 0x02 }, + /* 506 */ { 0x02, 0x02 }, + /* 507 */ { 0x02, 0x02 }, + /* 508 */ { 0x02, 0x02 }, + /* 509 */ { 0x02, 0x02 }, + /* 50a */ { 0x02, 0x02 }, + /* 50b */ { 0x02, 0x02 }, + /* 50c */ { 0x02, 0x02 }, + /* 50d */ { 0x02, 0x02 }, + /* 50e */ { 0x02, 0x02 }, + /* 50f */ { 0x02, 0x02 }, + /* 510 */ { 0x02, 0x02 }, + /* 511 */ { 0x02, 0x02 }, + /* 512 */ { 0x02, 0x02 }, + /* 513 */ { 0x02, 0x02 }, + /* 514 */ { 0x02, 0x02 }, + /* 515 */ { 0x02, 0x02 }, + /* 516 */ { 0x02, 0x02 }, + /* 517 */ { 0x02, 0x02 }, + /* 518 */ { 0x02, 0x02 }, + /* 519 */ { 0x02, 0x02 }, + /* 51a */ { 0x02, 0x02 }, + /* 51b */ { 0x02, 0x02 }, + /* 51c */ { 0x02, 0x02 }, + /* 51d */ { 0x02, 0x02 }, + /* 51e */ { 0x02, 0x02 }, + /* 51f */ { 0x02, 0x02 }, + /* 520 */ { 0x02, 0x02 }, + /* 521 */ { 0x02, 0x02 }, + /* 522 */ { 0x02, 0x02 }, + /* 523 */ { 0x02, 0x02 }, + /* 524 */ { 0x02, 0x02 }, + /* 525 */ { 0x02, 0x02 }, + /* 526 */ { 0x02, 0x02 }, + /* 527 */ { 0x02, 0x02 }, + /* 528 */ { 0x02, 0x02 }, + /* 529 */ { 0x02, 0x02 }, + /* 52a */ { 0x02, 0x02 }, + /* 52b */ { 0x02, 0x02 }, + /* 52c */ { 0x02, 0x02 }, + /* 52d */ { 0x02, 0x02 }, + /* 52e */ { 0x02, 0x02 }, + /* 52f */ { 0x02, 0x02 }, + /* 530 */ { 0x02, 0x02 }, + /* 531 */ { 0x02, 0x02 }, + /* 532 */ { 0x02, 0x02 }, + /* 533 */ { 0x02, 0x02 }, + /* 534 */ { 0x02, 0x02 }, + /* 535 */ { 0x02, 0x02 }, + /* 536 */ { 0x02, 0x02 }, + /* 537 */ { 0x02, 0x02 }, + /* 538 */ { 0x02, 0x02 }, + /* 539 */ { 0x02, 0x02 }, + /* 53a */ { 0x02, 0x02 }, + /* 53b */ { 0x02, 0x02 }, + /* 53c */ { 0x02, 0x02 }, + /* 53d */ { 0x02, 0x02 }, + /* 53e */ { 0x02, 0x02 }, + /* 53f */ { 0x02, 0x02 }, + /* 540 */ { 0x02, 0x02 }, + /* 541 */ { 0x02, 0x02 }, + /* 542 */ { 0x02, 0x02 }, + /* 543 */ { 0x02, 0x02 }, + /* 544 */ { 0x02, 0x02 }, + /* 545 */ { 0x02, 0x02 }, + /* 546 */ { 0x02, 0x02 }, + /* 547 */ { 0x02, 0x02 }, + /* 548 */ { 0x02, 0x02 }, + /* 549 */ { 0x02, 0x02 }, + /* 54a */ { 0x02, 0x02 }, + /* 54b */ { 0x02, 0x02 }, + /* 54c */ { 0x02, 0x02 }, + /* 54d */ { 0x02, 0x02 }, + /* 54e */ { 0x02, 0x02 }, + /* 54f */ { 0x02, 0x02 }, + /* 550 */ { 0x02, 0x02 }, + /* 551 */ { 0x02, 0x02 }, + /* 552 */ { 0x02, 0x02 }, + /* 553 */ { 0x02, 0x02 }, + /* 554 */ { 0x02, 0x02 }, + /* 555 */ { 0x02, 0x02 }, + /* 556 */ { 0x02, 0x02 }, + /* 557 */ { 0x02, 0x02 }, + /* 558 */ { 0x02, 0x02 }, + /* 559 */ { 0x02, 0x02 }, + /* 55a */ { 0x02, 0x02 }, + /* 55b */ { 0x02, 0x02 }, + /* 55c */ { 0x02, 0x02 }, + /* 55d */ { 0x02, 0x02 }, + /* 55e */ { 0x02, 0x02 }, + /* 55f */ { 0x02, 0x02 }, + /* 560 */ { 0x02, 0x02 }, + /* 561 */ { 0x02, 0x02 }, + /* 562 */ { 0x02, 0x02 }, + /* 563 */ { 0x02, 0x02 }, + /* 564 */ { 0x02, 0x02 }, + /* 565 */ { 0x02, 0x02 }, + /* 566 */ { 0x02, 0x02 }, + /* 567 */ { 0x02, 0x02 }, + /* 568 */ { 0x02, 0x02 }, + /* 569 */ { 0x02, 0x02 }, + /* 56a */ { 0x02, 0x02 }, + /* 56b */ { 0x02, 0x02 }, + /* 56c */ { 0x02, 0x02 }, + /* 56d */ { 0x02, 0x02 }, + /* 56e */ { 0x02, 0x02 }, + /* 56f */ { 0x02, 0x02 }, + /* 570 */ { 0x02, 0x02 }, + /* 571 */ { 0x02, 0x02 }, + /* 572 */ { 0x02, 0x02 }, + /* 573 */ { 0x02, 0x02 }, + /* 574 */ { 0x02, 0x02 }, + /* 575 */ { 0x02, 0x02 }, + /* 576 */ { 0x02, 0x02 }, + /* 577 */ { 0x02, 0x02 }, + /* 578 */ { 0x02, 0x02 }, + /* 579 */ { 0x02, 0x02 }, + /* 57a */ { 0x02, 0x02 }, + /* 57b */ { 0x02, 0x02 }, + /* 57c */ { 0x02, 0x02 }, + /* 57d */ { 0x02, 0x02 }, + /* 57e */ { 0x02, 0x02 }, + /* 57f */ { 0x02, 0x02 }, + /* 580 */ { 0x02, 0x02 }, + /* 581 */ { 0x02, 0x02 }, + /* 582 */ { 0x02, 0x02 }, + /* 583 */ { 0x02, 0x02 }, + /* 584 */ { 0x02, 0x02 }, + /* 585 */ { 0x02, 0x02 }, + /* 586 */ { 0x02, 0x02 }, + /* 587 */ { 0x02, 0x02 }, + /* 588 */ { 0x02, 0x02 }, + /* 589 */ { 0x02, 0x02 }, + /* 58a */ { 0x02, 0x02 }, + /* 58b */ { 0x02, 0x02 }, + /* 58c */ { 0x02, 0x02 }, + /* 58d */ { 0x02, 0x02 }, + /* 58e */ { 0x02, 0x02 }, + /* 58f */ { 0x02, 0x02 }, + /* 590 */ { 0x02, 0x02 }, + /* 591 */ { 0x02, 0x02 }, + /* 592 */ { 0x02, 0x02 }, + /* 593 */ { 0x02, 0x02 }, + /* 594 */ { 0x02, 0x02 }, + /* 595 */ { 0x02, 0x02 }, + /* 596 */ { 0x02, 0x02 }, + /* 597 */ { 0x02, 0x02 }, + /* 598 */ { 0x02, 0x02 }, + /* 599 */ { 0x02, 0x02 }, + /* 59a */ { 0x02, 0x02 }, + /* 59b */ { 0x02, 0x02 }, + /* 59c */ { 0x02, 0x02 }, + /* 59d */ { 0x02, 0x02 }, + /* 59e */ { 0x02, 0x02 }, + /* 59f */ { 0x02, 0x02 }, + /* 5a0 */ { 0x02, 0x02 }, + /* 5a1 */ { 0x02, 0x02 }, + /* 5a2 */ { 0x02, 0x02 }, + /* 5a3 */ { 0x02, 0x02 }, + /* 5a4 */ { 0x02, 0x02 }, + /* 5a5 */ { 0x02, 0x02 }, + /* 5a6 */ { 0x02, 0x02 }, + /* 5a7 */ { 0x02, 0x02 }, + /* 5a8 */ { 0x02, 0x02 }, + /* 5a9 */ { 0x02, 0x02 }, + /* 5aa */ { 0x02, 0x02 }, + /* 5ab */ { 0x02, 0x02 }, + /* 5ac */ { 0x02, 0x02 }, + /* 5ad */ { 0x02, 0x02 }, + /* 5ae */ { 0x02, 0x02 }, + /* 5af */ { 0x02, 0x02 }, + /* 5b0 */ { 0x02, 0x02 }, + /* 5b1 */ { 0x02, 0x02 }, + /* 5b2 */ { 0x02, 0x02 }, + /* 5b3 */ { 0x02, 0x02 }, + /* 5b4 */ { 0x02, 0x02 }, + /* 5b5 */ { 0x02, 0x02 }, + /* 5b6 */ { 0x02, 0x02 }, + /* 5b7 */ { 0x02, 0x02 }, + /* 5b8 */ { 0x02, 0x02 }, + /* 5b9 */ { 0x02, 0x02 }, + /* 5ba */ { 0x02, 0x02 }, + /* 5bb */ { 0x02, 0x02 }, + /* 5bc */ { 0x02, 0x02 }, + /* 5bd */ { 0x02, 0x02 }, + /* 5be */ { 0x02, 0x02 }, + /* 5bf */ { 0x02, 0x02 }, + /* 5c0 */ { 0x02, 0x02 }, + /* 5c1 */ { 0x02, 0x02 }, + /* 5c2 */ { 0x02, 0x02 }, + /* 5c3 */ { 0x02, 0x02 }, + /* 5c4 */ { 0x02, 0x02 }, + /* 5c5 */ { 0x02, 0x02 }, + /* 5c6 */ { 0x02, 0x02 }, + /* 5c7 */ { 0x02, 0x02 }, + /* 5c8 */ { 0x02, 0x02 }, + /* 5c9 */ { 0x02, 0x02 }, + /* 5ca */ { 0x02, 0x02 }, + /* 5cb */ { 0x02, 0x02 }, + /* 5cc */ { 0x02, 0x02 }, + /* 5cd */ { 0x02, 0x02 }, + /* 5ce */ { 0x02, 0x02 }, + /* 5cf */ { 0x02, 0x02 }, + /* 5d0 */ { 0x02, 0x02 }, + /* 5d1 */ { 0x02, 0x02 }, + /* 5d2 */ { 0x02, 0x02 }, + /* 5d3 */ { 0x02, 0x02 }, + /* 5d4 */ { 0x02, 0x02 }, + /* 5d5 */ { 0x02, 0x02 }, + /* 5d6 */ { 0x02, 0x02 }, + /* 5d7 */ { 0x02, 0x02 }, + /* 5d8 */ { 0x02, 0x02 }, + /* 5d9 */ { 0x02, 0x02 }, + /* 5da */ { 0x02, 0x02 }, + /* 5db */ { 0x02, 0x02 }, + /* 5dc */ { 0x02, 0x02 }, + /* 5dd */ { 0x02, 0x02 }, + /* 5de */ { 0x02, 0x02 }, + /* 5df */ { 0x02, 0x02 }, + /* 5e0 */ { 0x02, 0x02 }, + /* 5e1 */ { 0x02, 0x02 }, + /* 5e2 */ { 0x02, 0x02 }, + /* 5e3 */ { 0x02, 0x02 }, + /* 5e4 */ { 0x02, 0x02 }, + /* 5e5 */ { 0x02, 0x02 }, + /* 5e6 */ { 0x02, 0x02 }, + /* 5e7 */ { 0x02, 0x02 }, + /* 5e8 */ { 0x02, 0x02 }, + /* 5e9 */ { 0x02, 0x02 }, + /* 5ea */ { 0x02, 0x02 }, + /* 5eb */ { 0x02, 0x02 }, + /* 5ec */ { 0x02, 0x02 }, + /* 5ed */ { 0x02, 0x02 }, + /* 5ee */ { 0x02, 0x02 }, + /* 5ef */ { 0x02, 0x02 }, + /* 5f0 */ { 0x02, 0x02 }, + /* 5f1 */ { 0x02, 0x02 }, + /* 5f2 */ { 0x02, 0x02 }, + /* 5f3 */ { 0x02, 0x02 }, + /* 5f4 */ { 0x02, 0x02 }, + /* 5f5 */ { 0x02, 0x02 }, + /* 5f6 */ { 0x02, 0x02 }, + /* 5f7 */ { 0x02, 0x02 }, + /* 5f8 */ { 0x02, 0x02 }, + /* 5f9 */ { 0x02, 0x02 }, + /* 5fa */ { 0x02, 0x02 }, + /* 5fb */ { 0x02, 0x02 }, + /* 5fc */ { 0x02, 0x02 }, + /* 5fd */ { 0x02, 0x02 }, + /* 5fe */ { 0x02, 0x02 }, + /* 5ff */ { 0x02, 0x02 }, + /* 600 */ { 0x02, 0x02 }, + /* 601 */ { 0x02, 0x02 }, + /* 602 */ { 0x02, 0x02 }, + /* 603 */ { 0x02, 0x02 }, + /* 604 */ { 0x02, 0x02 }, + /* 605 */ { 0x02, 0x02 }, + /* 606 */ { 0x02, 0x02 }, + /* 607 */ { 0x02, 0x02 }, + /* 608 */ { 0x02, 0x02 }, + /* 609 */ { 0x02, 0x02 }, + /* 60a */ { 0x02, 0x02 }, + /* 60b */ { 0x02, 0x02 }, + /* 60c */ { 0x02, 0x02 }, + /* 60d */ { 0x02, 0x02 }, + /* 60e */ { 0x02, 0x02 }, + /* 60f */ { 0x02, 0x02 }, + /* 610 */ { 0x02, 0x02 }, + /* 611 */ { 0x02, 0x02 }, + /* 612 */ { 0x02, 0x02 }, + /* 613 */ { 0x02, 0x02 }, + /* 614 */ { 0x02, 0x02 }, + /* 615 */ { 0x02, 0x02 }, + /* 616 */ { 0x02, 0x02 }, + /* 617 */ { 0x02, 0x02 }, + /* 618 */ { 0x02, 0x02 }, + /* 619 */ { 0x02, 0x02 }, + /* 61a */ { 0x02, 0x02 }, + /* 61b */ { 0x02, 0x02 }, + /* 61c */ { 0x02, 0x02 }, + /* 61d */ { 0x02, 0x02 }, + /* 61e */ { 0x02, 0x02 }, + /* 61f */ { 0x02, 0x02 }, + /* 620 */ { 0x02, 0x02 }, + /* 621 */ { 0x02, 0x02 }, + /* 622 */ { 0x02, 0x02 }, + /* 623 */ { 0x02, 0x02 }, + /* 624 */ { 0x02, 0x02 }, + /* 625 */ { 0x02, 0x02 }, + /* 626 */ { 0x02, 0x02 }, + /* 627 */ { 0x02, 0x02 }, + /* 628 */ { 0x02, 0x02 }, + /* 629 */ { 0x02, 0x02 }, + /* 62a */ { 0x02, 0x02 }, + /* 62b */ { 0x02, 0x02 }, + /* 62c */ { 0x02, 0x02 }, + /* 62d */ { 0x02, 0x02 }, + /* 62e */ { 0x02, 0x02 }, + /* 62f */ { 0x02, 0x02 }, + /* 630 */ { 0x02, 0x02 }, + /* 631 */ { 0x02, 0x02 }, + /* 632 */ { 0x02, 0x02 }, + /* 633 */ { 0x02, 0x02 }, + /* 634 */ { 0x02, 0x02 }, + /* 635 */ { 0x02, 0x02 }, + /* 636 */ { 0x02, 0x02 }, + /* 637 */ { 0x02, 0x02 }, + /* 638 */ { 0x02, 0x02 }, + /* 639 */ { 0x02, 0x02 }, + /* 63a */ { 0x02, 0x02 }, + /* 63b */ { 0x02, 0x02 }, + /* 63c */ { 0x02, 0x02 }, + /* 63d */ { 0x02, 0x02 }, + /* 63e */ { 0x02, 0x02 }, + /* 63f */ { 0x02, 0x02 }, + /* 640 */ { 0x02, 0x02 }, + /* 641 */ { 0x02, 0x02 }, + /* 642 */ { 0x02, 0x02 }, + /* 643 */ { 0x02, 0x02 }, + /* 644 */ { 0x02, 0x02 }, + /* 645 */ { 0x02, 0x02 }, + /* 646 */ { 0x02, 0x02 }, + /* 647 */ { 0x02, 0x02 }, + /* 648 */ { 0x02, 0x02 }, + /* 649 */ { 0x02, 0x02 }, + /* 64a */ { 0x02, 0x02 }, + /* 64b */ { 0x02, 0x02 }, + /* 64c */ { 0x02, 0x02 }, + /* 64d */ { 0x02, 0x02 }, + /* 64e */ { 0x02, 0x02 }, + /* 64f */ { 0x02, 0x02 }, + /* 650 */ { 0x02, 0x02 }, + /* 651 */ { 0x02, 0x02 }, + /* 652 */ { 0x02, 0x02 }, + /* 653 */ { 0x02, 0x02 }, + /* 654 */ { 0x02, 0x02 }, + /* 655 */ { 0x02, 0x02 }, + /* 656 */ { 0x02, 0x02 }, + /* 657 */ { 0x02, 0x02 }, + /* 658 */ { 0x02, 0x02 }, + /* 659 */ { 0x02, 0x02 }, + /* 65a */ { 0x02, 0x02 }, + /* 65b */ { 0x02, 0x02 }, + /* 65c */ { 0x02, 0x02 }, + /* 65d */ { 0x02, 0x02 }, + /* 65e */ { 0x02, 0x02 }, + /* 65f */ { 0x02, 0x02 }, + /* 660 */ { 0x02, 0x02 }, + /* 661 */ { 0x02, 0x02 }, + /* 662 */ { 0x02, 0x02 }, + /* 663 */ { 0x02, 0x02 }, + /* 664 */ { 0x02, 0x02 }, + /* 665 */ { 0x02, 0x02 }, + /* 666 */ { 0x02, 0x02 }, + /* 667 */ { 0x02, 0x02 }, + /* 668 */ { 0x02, 0x02 }, + /* 669 */ { 0x02, 0x02 }, + /* 66a */ { 0x02, 0x02 }, + /* 66b */ { 0x02, 0x02 }, + /* 66c */ { 0x02, 0x02 }, + /* 66d */ { 0x02, 0x02 }, + /* 66e */ { 0x02, 0x02 }, + /* 66f */ { 0x02, 0x02 }, + /* 670 */ { 0x02, 0x02 }, + /* 671 */ { 0x02, 0x02 }, + /* 672 */ { 0x02, 0x02 }, + /* 673 */ { 0x02, 0x02 }, + /* 674 */ { 0x02, 0x02 }, + /* 675 */ { 0x02, 0x02 }, + /* 676 */ { 0x02, 0x02 }, + /* 677 */ { 0x02, 0x02 }, + /* 678 */ { 0x02, 0x02 }, + /* 679 */ { 0x02, 0x02 }, + /* 67a */ { 0x02, 0x02 }, + /* 67b */ { 0x02, 0x02 }, + /* 67c */ { 0x02, 0x02 }, + /* 67d */ { 0x02, 0x02 }, + /* 67e */ { 0x02, 0x02 }, + /* 67f */ { 0x02, 0x02 }, + /* 680 */ { 0x02, 0x02 }, + /* 681 */ { 0x02, 0x02 }, + /* 682 */ { 0x02, 0x02 }, + /* 683 */ { 0x02, 0x02 }, + /* 684 */ { 0x02, 0x02 }, + /* 685 */ { 0x02, 0x02 }, + /* 686 */ { 0x02, 0x02 }, + /* 687 */ { 0x02, 0x02 }, + /* 688 */ { 0x02, 0x02 }, + /* 689 */ { 0x02, 0x02 }, + /* 68a */ { 0x02, 0x02 }, + /* 68b */ { 0x02, 0x02 }, + /* 68c */ { 0x02, 0x02 }, + /* 68d */ { 0x02, 0x02 }, + /* 68e */ { 0x02, 0x02 }, + /* 68f */ { 0x02, 0x02 }, + /* 690 */ { 0x02, 0x02 }, + /* 691 */ { 0x02, 0x02 }, + /* 692 */ { 0x02, 0x02 }, + /* 693 */ { 0x02, 0x02 }, + /* 694 */ { 0x02, 0x02 }, + /* 695 */ { 0x02, 0x02 }, + /* 696 */ { 0x02, 0x02 }, + /* 697 */ { 0x02, 0x02 }, + /* 698 */ { 0x02, 0x02 }, + /* 699 */ { 0x02, 0x02 }, + /* 69a */ { 0x02, 0x02 }, + /* 69b */ { 0x02, 0x02 }, + /* 69c */ { 0x02, 0x02 }, + /* 69d */ { 0x02, 0x02 }, + /* 69e */ { 0x02, 0x02 }, + /* 69f */ { 0x02, 0x02 }, + /* 6a0 */ { 0x02, 0x02 }, + /* 6a1 */ { 0x02, 0x02 }, + /* 6a2 */ { 0x02, 0x02 }, + /* 6a3 */ { 0x02, 0x02 }, + /* 6a4 */ { 0x02, 0x02 }, + /* 6a5 */ { 0x02, 0x02 }, + /* 6a6 */ { 0x02, 0x02 }, + /* 6a7 */ { 0x02, 0x02 }, + /* 6a8 */ { 0x02, 0x02 }, + /* 6a9 */ { 0x02, 0x02 }, + /* 6aa */ { 0x02, 0x02 }, + /* 6ab */ { 0x02, 0x02 }, + /* 6ac */ { 0x02, 0x02 }, + /* 6ad */ { 0x02, 0x02 }, + /* 6ae */ { 0x02, 0x02 }, + /* 6af */ { 0x02, 0x02 }, + /* 6b0 */ { 0x02, 0x02 }, + /* 6b1 */ { 0x02, 0x02 }, + /* 6b2 */ { 0x02, 0x02 }, + /* 6b3 */ { 0x02, 0x02 }, + /* 6b4 */ { 0x02, 0x02 }, + /* 6b5 */ { 0x02, 0x02 }, + /* 6b6 */ { 0x02, 0x02 }, + /* 6b7 */ { 0x02, 0x02 }, + /* 6b8 */ { 0x02, 0x02 }, + /* 6b9 */ { 0x02, 0x02 }, + /* 6ba */ { 0x02, 0x02 }, + /* 6bb */ { 0x02, 0x02 }, + /* 6bc */ { 0x02, 0x02 }, + /* 6bd */ { 0x02, 0x02 }, + /* 6be */ { 0x02, 0x02 }, + /* 6bf */ { 0x02, 0x02 }, + /* 6c0 */ { 0x02, 0x02 }, + /* 6c1 */ { 0x02, 0x02 }, + /* 6c2 */ { 0x02, 0x02 }, + /* 6c3 */ { 0x02, 0x02 }, + /* 6c4 */ { 0x02, 0x02 }, + /* 6c5 */ { 0x02, 0x02 }, + /* 6c6 */ { 0x02, 0x02 }, + /* 6c7 */ { 0x02, 0x02 }, + /* 6c8 */ { 0x02, 0x02 }, + /* 6c9 */ { 0x02, 0x02 }, + /* 6ca */ { 0x02, 0x02 }, + /* 6cb */ { 0x02, 0x02 }, + /* 6cc */ { 0x02, 0x02 }, + /* 6cd */ { 0x02, 0x02 }, + /* 6ce */ { 0x02, 0x02 }, + /* 6cf */ { 0x02, 0x02 }, + /* 6d0 */ { 0x02, 0x02 }, + /* 6d1 */ { 0x02, 0x02 }, + /* 6d2 */ { 0x02, 0x02 }, + /* 6d3 */ { 0x02, 0x02 }, + /* 6d4 */ { 0x02, 0x02 }, + /* 6d5 */ { 0x02, 0x02 }, + /* 6d6 */ { 0x02, 0x02 }, + /* 6d7 */ { 0x02, 0x02 }, + /* 6d8 */ { 0x02, 0x02 }, + /* 6d9 */ { 0x02, 0x02 }, + /* 6da */ { 0x02, 0x02 }, + /* 6db */ { 0x02, 0x02 }, + /* 6dc */ { 0x02, 0x02 }, + /* 6dd */ { 0x02, 0x02 }, + /* 6de */ { 0x02, 0x02 }, + /* 6df */ { 0x02, 0x02 }, + /* 6e0 */ { 0x02, 0x02 }, + /* 6e1 */ { 0x02, 0x02 }, + /* 6e2 */ { 0x02, 0x02 }, + /* 6e3 */ { 0x02, 0x02 }, + /* 6e4 */ { 0x02, 0x02 }, + /* 6e5 */ { 0x02, 0x02 }, + /* 6e6 */ { 0x02, 0x02 }, + /* 6e7 */ { 0x02, 0x02 }, + /* 6e8 */ { 0x02, 0x02 }, + /* 6e9 */ { 0x02, 0x02 }, + /* 6ea */ { 0x02, 0x02 }, + /* 6eb */ { 0x02, 0x02 }, + /* 6ec */ { 0x02, 0x02 }, + /* 6ed */ { 0x02, 0x02 }, + /* 6ee */ { 0x02, 0x02 }, + /* 6ef */ { 0x02, 0x02 }, + /* 6f0 */ { 0x02, 0x02 }, + /* 6f1 */ { 0x02, 0x02 }, + /* 6f2 */ { 0x02, 0x02 }, + /* 6f3 */ { 0x02, 0x02 }, + /* 6f4 */ { 0x02, 0x02 }, + /* 6f5 */ { 0x02, 0x02 }, + /* 6f6 */ { 0x02, 0x02 }, + /* 6f7 */ { 0x02, 0x02 }, + /* 6f8 */ { 0x02, 0x02 }, + /* 6f9 */ { 0x02, 0x02 }, + /* 6fa */ { 0x02, 0x02 }, + /* 6fb */ { 0x02, 0x02 }, + /* 6fc */ { 0x02, 0x02 }, + /* 6fd */ { 0x02, 0x02 }, + /* 6fe */ { 0x02, 0x02 }, + /* 6ff */ { 0x02, 0x02 }, + /* 700 */ { 0x02, 0x02 }, + /* 701 */ { 0x02, 0x02 }, + /* 702 */ { 0x02, 0x02 }, + /* 703 */ { 0x02, 0x02 }, + /* 704 */ { 0x02, 0x02 }, + /* 705 */ { 0x02, 0x02 }, + /* 706 */ { 0x02, 0x02 }, + /* 707 */ { 0x02, 0x02 }, + /* 708 */ { 0x02, 0x02 }, + /* 709 */ { 0x02, 0x02 }, + /* 70a */ { 0x02, 0x02 }, + /* 70b */ { 0x02, 0x02 }, + /* 70c */ { 0x02, 0x02 }, + /* 70d */ { 0x02, 0x02 }, + /* 70e */ { 0x02, 0x02 }, + /* 70f */ { 0x02, 0x02 }, + /* 710 */ { 0x02, 0x02 }, + /* 711 */ { 0x02, 0x02 }, + /* 712 */ { 0x02, 0x02 }, + /* 713 */ { 0x02, 0x02 }, + /* 714 */ { 0x02, 0x02 }, + /* 715 */ { 0x02, 0x02 }, + /* 716 */ { 0x02, 0x02 }, + /* 717 */ { 0x02, 0x02 }, + /* 718 */ { 0x02, 0x02 }, + /* 719 */ { 0x02, 0x02 }, + /* 71a */ { 0x02, 0x02 }, + /* 71b */ { 0x02, 0x02 }, + /* 71c */ { 0x02, 0x02 }, + /* 71d */ { 0x02, 0x02 }, + /* 71e */ { 0x02, 0x02 }, + /* 71f */ { 0x02, 0x02 }, + /* 720 */ { 0x02, 0x02 }, + /* 721 */ { 0x02, 0x02 }, + /* 722 */ { 0x02, 0x02 }, + /* 723 */ { 0x02, 0x02 }, + /* 724 */ { 0x02, 0x02 }, + /* 725 */ { 0x02, 0x02 }, + /* 726 */ { 0x02, 0x02 }, + /* 727 */ { 0x02, 0x02 }, + /* 728 */ { 0x02, 0x02 }, + /* 729 */ { 0x02, 0x02 }, + /* 72a */ { 0x02, 0x02 }, + /* 72b */ { 0x02, 0x02 }, + /* 72c */ { 0x02, 0x02 }, + /* 72d */ { 0x02, 0x02 }, + /* 72e */ { 0x02, 0x02 }, + /* 72f */ { 0x02, 0x02 }, + /* 730 */ { 0x02, 0x02 }, + /* 731 */ { 0x02, 0x02 }, + /* 732 */ { 0x02, 0x02 }, + /* 733 */ { 0x02, 0x02 }, + /* 734 */ { 0x02, 0x02 }, + /* 735 */ { 0x02, 0x02 }, + /* 736 */ { 0x02, 0x02 }, + /* 737 */ { 0x02, 0x02 }, + /* 738 */ { 0x02, 0x02 }, + /* 739 */ { 0x02, 0x02 }, + /* 73a */ { 0x02, 0x02 }, + /* 73b */ { 0x02, 0x02 }, + /* 73c */ { 0x02, 0x02 }, + /* 73d */ { 0x02, 0x02 }, + /* 73e */ { 0x02, 0x02 }, + /* 73f */ { 0x02, 0x02 }, + /* 740 */ { 0x02, 0x02 }, + /* 741 */ { 0x02, 0x02 }, + /* 742 */ { 0x02, 0x02 }, + /* 743 */ { 0x02, 0x02 }, + /* 744 */ { 0x02, 0x02 }, + /* 745 */ { 0x02, 0x02 }, + /* 746 */ { 0x02, 0x02 }, + /* 747 */ { 0x02, 0x02 }, + /* 748 */ { 0x02, 0x02 }, + /* 749 */ { 0x02, 0x02 }, + /* 74a */ { 0x02, 0x02 }, + /* 74b */ { 0x02, 0x02 }, + /* 74c */ { 0x02, 0x02 }, + /* 74d */ { 0x02, 0x02 }, + /* 74e */ { 0x02, 0x02 }, + /* 74f */ { 0x02, 0x02 }, + /* 750 */ { 0x02, 0x02 }, + /* 751 */ { 0x02, 0x02 }, + /* 752 */ { 0x02, 0x02 }, + /* 753 */ { 0x02, 0x02 }, + /* 754 */ { 0x02, 0x02 }, + /* 755 */ { 0x02, 0x02 }, + /* 756 */ { 0x02, 0x02 }, + /* 757 */ { 0x02, 0x02 }, + /* 758 */ { 0x02, 0x02 }, + /* 759 */ { 0x02, 0x02 }, + /* 75a */ { 0x02, 0x02 }, + /* 75b */ { 0x02, 0x02 }, + /* 75c */ { 0x02, 0x02 }, + /* 75d */ { 0x02, 0x02 }, + /* 75e */ { 0x02, 0x02 }, + /* 75f */ { 0x02, 0x02 }, + /* 760 */ { 0x02, 0x02 }, + /* 761 */ { 0x02, 0x02 }, + /* 762 */ { 0x02, 0x02 }, + /* 763 */ { 0x02, 0x02 }, + /* 764 */ { 0x02, 0x02 }, + /* 765 */ { 0x02, 0x02 }, + /* 766 */ { 0x02, 0x02 }, + /* 767 */ { 0x02, 0x02 }, + /* 768 */ { 0x02, 0x02 }, + /* 769 */ { 0x02, 0x02 }, + /* 76a */ { 0x02, 0x02 }, + /* 76b */ { 0x02, 0x02 }, + /* 76c */ { 0x02, 0x02 }, + /* 76d */ { 0x02, 0x02 }, + /* 76e */ { 0x02, 0x02 }, + /* 76f */ { 0x02, 0x02 }, + /* 770 */ { 0x02, 0x02 }, + /* 771 */ { 0x02, 0x02 }, + /* 772 */ { 0x02, 0x02 }, + /* 773 */ { 0x02, 0x02 }, + /* 774 */ { 0x02, 0x02 }, + /* 775 */ { 0x02, 0x02 }, + /* 776 */ { 0x02, 0x02 }, + /* 777 */ { 0x02, 0x02 }, + /* 778 */ { 0x02, 0x02 }, + /* 779 */ { 0x02, 0x02 }, + /* 77a */ { 0x02, 0x02 }, + /* 77b */ { 0x02, 0x02 }, + /* 77c */ { 0x02, 0x02 }, + /* 77d */ { 0x02, 0x02 }, + /* 77e */ { 0x02, 0x02 }, + /* 77f */ { 0x02, 0x02 }, + /* 780 */ { 0x02, 0x02 }, + /* 781 */ { 0x02, 0x02 }, + /* 782 */ { 0x02, 0x02 }, + /* 783 */ { 0x02, 0x02 }, + /* 784 */ { 0x02, 0x02 }, + /* 785 */ { 0x02, 0x02 }, + /* 786 */ { 0x02, 0x02 }, + /* 787 */ { 0x02, 0x02 }, + /* 788 */ { 0x02, 0x02 }, + /* 789 */ { 0x02, 0x02 }, + /* 78a */ { 0x02, 0x02 }, + /* 78b */ { 0x02, 0x02 }, + /* 78c */ { 0x02, 0x02 }, + /* 78d */ { 0x02, 0x02 }, + /* 78e */ { 0x02, 0x02 }, + /* 78f */ { 0x02, 0x02 }, + /* 790 */ { 0x02, 0x02 }, + /* 791 */ { 0x02, 0x02 }, + /* 792 */ { 0x02, 0x02 }, + /* 793 */ { 0x02, 0x02 }, + /* 794 */ { 0x02, 0x02 }, + /* 795 */ { 0x02, 0x02 }, + /* 796 */ { 0x02, 0x02 }, + /* 797 */ { 0x02, 0x02 }, + /* 798 */ { 0x02, 0x02 }, + /* 799 */ { 0x02, 0x02 }, + /* 79a */ { 0x02, 0x02 }, + /* 79b */ { 0x02, 0x02 }, + /* 79c */ { 0x02, 0x02 }, + /* 79d */ { 0x02, 0x02 }, + /* 79e */ { 0x02, 0x02 }, + /* 79f */ { 0x02, 0x02 }, + /* 7a0 */ { 0x02, 0x02 }, + /* 7a1 */ { 0x02, 0x02 }, + /* 7a2 */ { 0x02, 0x02 }, + /* 7a3 */ { 0x02, 0x02 }, + /* 7a4 */ { 0x02, 0x02 }, + /* 7a5 */ { 0x02, 0x02 }, + /* 7a6 */ { 0x02, 0x02 }, + /* 7a7 */ { 0x02, 0x02 }, + /* 7a8 */ { 0x02, 0x02 }, + /* 7a9 */ { 0x02, 0x02 }, + /* 7aa */ { 0x02, 0x02 }, + /* 7ab */ { 0x02, 0x02 }, + /* 7ac */ { 0x02, 0x02 }, + /* 7ad */ { 0x02, 0x02 }, + /* 7ae */ { 0x02, 0x02 }, + /* 7af */ { 0x02, 0x02 }, + /* 7b0 */ { 0x02, 0x02 }, + /* 7b1 */ { 0x02, 0x02 }, + /* 7b2 */ { 0x02, 0x02 }, + /* 7b3 */ { 0x02, 0x02 }, + /* 7b4 */ { 0x02, 0x02 }, + /* 7b5 */ { 0x02, 0x02 }, + /* 7b6 */ { 0x02, 0x02 }, + /* 7b7 */ { 0x02, 0x02 }, + /* 7b8 */ { 0x02, 0x02 }, + /* 7b9 */ { 0x02, 0x02 }, + /* 7ba */ { 0x02, 0x02 }, + /* 7bb */ { 0x02, 0x02 }, + /* 7bc */ { 0x02, 0x02 }, + /* 7bd */ { 0x02, 0x02 }, + /* 7be */ { 0x02, 0x02 }, + /* 7bf */ { 0x02, 0x02 }, + /* 7c0 */ { 0x02, 0x02 }, + /* 7c1 */ { 0x02, 0x02 }, + /* 7c2 */ { 0x02, 0x02 }, + /* 7c3 */ { 0x02, 0x02 }, + /* 7c4 */ { 0x02, 0x02 }, + /* 7c5 */ { 0x02, 0x02 }, + /* 7c6 */ { 0x02, 0x02 }, + /* 7c7 */ { 0x02, 0x02 }, + /* 7c8 */ { 0x02, 0x02 }, + /* 7c9 */ { 0x02, 0x02 }, + /* 7ca */ { 0x02, 0x02 }, + /* 7cb */ { 0x02, 0x02 }, + /* 7cc */ { 0x02, 0x02 }, + /* 7cd */ { 0x02, 0x02 }, + /* 7ce */ { 0x02, 0x02 }, + /* 7cf */ { 0x02, 0x02 }, + /* 7d0 */ { 0x02, 0x02 }, + /* 7d1 */ { 0x02, 0x02 }, + /* 7d2 */ { 0x02, 0x02 }, + /* 7d3 */ { 0x02, 0x02 }, + /* 7d4 */ { 0x02, 0x02 }, + /* 7d5 */ { 0x02, 0x02 }, + /* 7d6 */ { 0x02, 0x02 }, + /* 7d7 */ { 0x02, 0x02 }, + /* 7d8 */ { 0x02, 0x02 }, + /* 7d9 */ { 0x02, 0x02 }, + /* 7da */ { 0x02, 0x02 }, + /* 7db */ { 0x02, 0x02 }, + /* 7dc */ { 0x02, 0x02 }, + /* 7dd */ { 0x02, 0x02 }, + /* 7de */ { 0x02, 0x02 }, + /* 7df */ { 0x02, 0x02 }, + /* 7e0 */ { 0x02, 0x02 }, + /* 7e1 */ { 0x02, 0x02 }, + /* 7e2 */ { 0x02, 0x02 }, + /* 7e3 */ { 0x02, 0x02 }, + /* 7e4 */ { 0x02, 0x02 }, + /* 7e5 */ { 0x02, 0x02 }, + /* 7e6 */ { 0x02, 0x02 }, + /* 7e7 */ { 0x02, 0x02 }, + /* 7e8 */ { 0x02, 0x02 }, + /* 7e9 */ { 0x02, 0x02 }, + /* 7ea */ { 0x02, 0x02 }, + /* 7eb */ { 0x02, 0x02 }, + /* 7ec */ { 0x02, 0x02 }, + /* 7ed */ { 0x02, 0x02 }, + /* 7ee */ { 0x02, 0x02 }, + /* 7ef */ { 0x02, 0x02 }, + /* 7f0 */ { 0x02, 0x02 }, + /* 7f1 */ { 0x02, 0x02 }, + /* 7f2 */ { 0x02, 0x02 }, + /* 7f3 */ { 0x02, 0x02 }, + /* 7f4 */ { 0x02, 0x02 }, + /* 7f5 */ { 0x02, 0x02 }, + /* 7f6 */ { 0x02, 0x02 }, + /* 7f7 */ { 0x02, 0x02 }, + /* 7f8 */ { 0x02, 0x02 }, + /* 7f9 */ { 0x02, 0x02 }, + /* 7fa */ { 0x02, 0x02 }, + /* 7fb */ { 0x02, 0x02 }, + /* 7fc */ { 0x02, 0x02 }, + /* 7fd */ { 0x02, 0x02 }, + /* 7fe */ { 0x02, 0x02 }, + /* 7ff */ { 0x02, 0x02 }, + /* 800 */ { 0x03, 0x03 }, + /* 801 */ { 0x03, 0x03 }, + /* 802 */ { 0x03, 0x03 }, + /* 803 */ { 0x03, 0x03 }, + /* 804 */ { 0x03, 0x03 }, + /* 805 */ { 0x03, 0x03 }, + /* 806 */ { 0x03, 0x03 }, + /* 807 */ { 0x03, 0x03 }, + /* 808 */ { 0x03, 0x03 }, + /* 809 */ { 0x03, 0x03 }, + /* 80a */ { 0x03, 0x03 }, + /* 80b */ { 0x03, 0x03 }, + /* 80c */ { 0x03, 0x03 }, + /* 80d */ { 0x03, 0x03 }, + /* 80e */ { 0x03, 0x03 }, + /* 80f */ { 0x03, 0x03 }, + /* 810 */ { 0x03, 0x03 }, + /* 811 */ { 0x03, 0x03 }, + /* 812 */ { 0x03, 0x03 }, + /* 813 */ { 0x03, 0x03 }, + /* 814 */ { 0x03, 0x03 }, + /* 815 */ { 0x03, 0x03 }, + /* 816 */ { 0x03, 0x03 }, + /* 817 */ { 0x03, 0x03 }, + /* 818 */ { 0x03, 0x03 }, + /* 819 */ { 0x03, 0x03 }, + /* 81a */ { 0x03, 0x03 }, + /* 81b */ { 0x03, 0x03 }, + /* 81c */ { 0x03, 0x03 }, + /* 81d */ { 0x03, 0x03 }, + /* 81e */ { 0x03, 0x03 }, + /* 81f */ { 0x03, 0x03 }, + /* 820 */ { 0x03, 0x03 }, + /* 821 */ { 0x03, 0x03 }, + /* 822 */ { 0x03, 0x03 }, + /* 823 */ { 0x03, 0x03 }, + /* 824 */ { 0x03, 0x03 }, + /* 825 */ { 0x03, 0x03 }, + /* 826 */ { 0x03, 0x03 }, + /* 827 */ { 0x03, 0x03 }, + /* 828 */ { 0x03, 0x03 }, + /* 829 */ { 0x03, 0x03 }, + /* 82a */ { 0x03, 0x03 }, + /* 82b */ { 0x03, 0x03 }, + /* 82c */ { 0x03, 0x03 }, + /* 82d */ { 0x03, 0x03 }, + /* 82e */ { 0x03, 0x03 }, + /* 82f */ { 0x03, 0x03 }, + /* 830 */ { 0x03, 0x03 }, + /* 831 */ { 0x03, 0x03 }, + /* 832 */ { 0x03, 0x03 }, + /* 833 */ { 0x03, 0x03 }, + /* 834 */ { 0x03, 0x03 }, + /* 835 */ { 0x03, 0x03 }, + /* 836 */ { 0x03, 0x03 }, + /* 837 */ { 0x03, 0x03 }, + /* 838 */ { 0x03, 0x03 }, + /* 839 */ { 0x03, 0x03 }, + /* 83a */ { 0x03, 0x03 }, + /* 83b */ { 0x03, 0x03 }, + /* 83c */ { 0x03, 0x03 }, + /* 83d */ { 0x03, 0x03 }, + /* 83e */ { 0x03, 0x03 }, + /* 83f */ { 0x03, 0x03 }, + /* 840 */ { 0x03, 0x03 }, + /* 841 */ { 0x03, 0x03 }, + /* 842 */ { 0x03, 0x03 }, + /* 843 */ { 0x03, 0x03 }, + /* 844 */ { 0x03, 0x03 }, + /* 845 */ { 0x03, 0x03 }, + /* 846 */ { 0x03, 0x03 }, + /* 847 */ { 0x03, 0x03 }, + /* 848 */ { 0x03, 0x03 }, + /* 849 */ { 0x03, 0x03 }, + /* 84a */ { 0x03, 0x03 }, + /* 84b */ { 0x03, 0x03 }, + /* 84c */ { 0x03, 0x03 }, + /* 84d */ { 0x03, 0x03 }, + /* 84e */ { 0x03, 0x03 }, + /* 84f */ { 0x03, 0x03 }, + /* 850 */ { 0x03, 0x03 }, + /* 851 */ { 0x03, 0x03 }, + /* 852 */ { 0x03, 0x03 }, + /* 853 */ { 0x03, 0x03 }, + /* 854 */ { 0x03, 0x03 }, + /* 855 */ { 0x03, 0x03 }, + /* 856 */ { 0x03, 0x03 }, + /* 857 */ { 0x03, 0x03 }, + /* 858 */ { 0x03, 0x03 }, + /* 859 */ { 0x03, 0x03 }, + /* 85a */ { 0x03, 0x03 }, + /* 85b */ { 0x03, 0x03 }, + /* 85c */ { 0x03, 0x03 }, + /* 85d */ { 0x03, 0x03 }, + /* 85e */ { 0x03, 0x03 }, + /* 85f */ { 0x03, 0x03 }, + /* 860 */ { 0x03, 0x03 }, + /* 861 */ { 0x03, 0x03 }, + /* 862 */ { 0x03, 0x03 }, + /* 863 */ { 0x03, 0x03 }, + /* 864 */ { 0x03, 0x03 }, + /* 865 */ { 0x03, 0x03 }, + /* 866 */ { 0x03, 0x03 }, + /* 867 */ { 0x03, 0x03 }, + /* 868 */ { 0x03, 0x03 }, + /* 869 */ { 0x03, 0x03 }, + /* 86a */ { 0x03, 0x03 }, + /* 86b */ { 0x03, 0x03 }, + /* 86c */ { 0x03, 0x03 }, + /* 86d */ { 0x03, 0x03 }, + /* 86e */ { 0x03, 0x03 }, + /* 86f */ { 0x03, 0x03 }, + /* 870 */ { 0x03, 0x03 }, + /* 871 */ { 0x03, 0x03 }, + /* 872 */ { 0x03, 0x03 }, + /* 873 */ { 0x03, 0x03 }, + /* 874 */ { 0x03, 0x03 }, + /* 875 */ { 0x03, 0x03 }, + /* 876 */ { 0x03, 0x03 }, + /* 877 */ { 0x03, 0x03 }, + /* 878 */ { 0x03, 0x03 }, + /* 879 */ { 0x03, 0x03 }, + /* 87a */ { 0x03, 0x03 }, + /* 87b */ { 0x03, 0x03 }, + /* 87c */ { 0x03, 0x03 }, + /* 87d */ { 0x03, 0x03 }, + /* 87e */ { 0x03, 0x03 }, + /* 87f */ { 0x03, 0x03 }, + /* 880 */ { 0x03, 0x03 }, + /* 881 */ { 0x03, 0x03 }, + /* 882 */ { 0x03, 0x03 }, + /* 883 */ { 0x03, 0x03 }, + /* 884 */ { 0x03, 0x03 }, + /* 885 */ { 0x03, 0x03 }, + /* 886 */ { 0x03, 0x03 }, + /* 887 */ { 0x03, 0x03 }, + /* 888 */ { 0x03, 0x03 }, + /* 889 */ { 0x03, 0x03 }, + /* 88a */ { 0x03, 0x03 }, + /* 88b */ { 0x03, 0x03 }, + /* 88c */ { 0x03, 0x03 }, + /* 88d */ { 0x03, 0x03 }, + /* 88e */ { 0x03, 0x03 }, + /* 88f */ { 0x03, 0x03 }, + /* 890 */ { 0x03, 0x03 }, + /* 891 */ { 0x03, 0x03 }, + /* 892 */ { 0x03, 0x03 }, + /* 893 */ { 0x03, 0x03 }, + /* 894 */ { 0x03, 0x03 }, + /* 895 */ { 0x03, 0x03 }, + /* 896 */ { 0x03, 0x03 }, + /* 897 */ { 0x03, 0x03 }, + /* 898 */ { 0x03, 0x03 }, + /* 899 */ { 0x03, 0x03 }, + /* 89a */ { 0x03, 0x03 }, + /* 89b */ { 0x03, 0x03 }, + /* 89c */ { 0x03, 0x03 }, + /* 89d */ { 0x03, 0x03 }, + /* 89e */ { 0x03, 0x03 }, + /* 89f */ { 0x03, 0x03 }, + /* 8a0 */ { 0x03, 0x03 }, + /* 8a1 */ { 0x03, 0x03 }, + /* 8a2 */ { 0x03, 0x03 }, + /* 8a3 */ { 0x03, 0x03 }, + /* 8a4 */ { 0x03, 0x03 }, + /* 8a5 */ { 0x03, 0x03 }, + /* 8a6 */ { 0x03, 0x03 }, + /* 8a7 */ { 0x03, 0x03 }, + /* 8a8 */ { 0x03, 0x03 }, + /* 8a9 */ { 0x03, 0x03 }, + /* 8aa */ { 0x03, 0x03 }, + /* 8ab */ { 0x03, 0x03 }, + /* 8ac */ { 0x03, 0x03 }, + /* 8ad */ { 0x03, 0x03 }, + /* 8ae */ { 0x03, 0x03 }, + /* 8af */ { 0x03, 0x03 }, + /* 8b0 */ { 0x03, 0x03 }, + /* 8b1 */ { 0x03, 0x03 }, + /* 8b2 */ { 0x03, 0x03 }, + /* 8b3 */ { 0x03, 0x03 }, + /* 8b4 */ { 0x03, 0x03 }, + /* 8b5 */ { 0x03, 0x03 }, + /* 8b6 */ { 0x03, 0x03 }, + /* 8b7 */ { 0x03, 0x03 }, + /* 8b8 */ { 0x03, 0x03 }, + /* 8b9 */ { 0x03, 0x03 }, + /* 8ba */ { 0x03, 0x03 }, + /* 8bb */ { 0x03, 0x03 }, + /* 8bc */ { 0x03, 0x03 }, + /* 8bd */ { 0x03, 0x03 }, + /* 8be */ { 0x03, 0x03 }, + /* 8bf */ { 0x03, 0x03 }, + /* 8c0 */ { 0x03, 0x03 }, + /* 8c1 */ { 0x03, 0x03 }, + /* 8c2 */ { 0x03, 0x03 }, + /* 8c3 */ { 0x03, 0x03 }, + /* 8c4 */ { 0x03, 0x03 }, + /* 8c5 */ { 0x03, 0x03 }, + /* 8c6 */ { 0x03, 0x03 }, + /* 8c7 */ { 0x03, 0x03 }, + /* 8c8 */ { 0x03, 0x03 }, + /* 8c9 */ { 0x03, 0x03 }, + /* 8ca */ { 0x03, 0x03 }, + /* 8cb */ { 0x03, 0x03 }, + /* 8cc */ { 0x03, 0x03 }, + /* 8cd */ { 0x03, 0x03 }, + /* 8ce */ { 0x03, 0x03 }, + /* 8cf */ { 0x03, 0x03 }, + /* 8d0 */ { 0x03, 0x03 }, + /* 8d1 */ { 0x03, 0x03 }, + /* 8d2 */ { 0x03, 0x03 }, + /* 8d3 */ { 0x03, 0x03 }, + /* 8d4 */ { 0x03, 0x03 }, + /* 8d5 */ { 0x03, 0x03 }, + /* 8d6 */ { 0x03, 0x03 }, + /* 8d7 */ { 0x03, 0x03 }, + /* 8d8 */ { 0x03, 0x03 }, + /* 8d9 */ { 0x03, 0x03 }, + /* 8da */ { 0x03, 0x03 }, + /* 8db */ { 0x03, 0x03 }, + /* 8dc */ { 0x03, 0x03 }, + /* 8dd */ { 0x03, 0x03 }, + /* 8de */ { 0x03, 0x03 }, + /* 8df */ { 0x03, 0x03 }, + /* 8e0 */ { 0x03, 0x03 }, + /* 8e1 */ { 0x03, 0x03 }, + /* 8e2 */ { 0x03, 0x03 }, + /* 8e3 */ { 0x03, 0x03 }, + /* 8e4 */ { 0x03, 0x03 }, + /* 8e5 */ { 0x03, 0x03 }, + /* 8e6 */ { 0x03, 0x03 }, + /* 8e7 */ { 0x03, 0x03 }, + /* 8e8 */ { 0x03, 0x03 }, + /* 8e9 */ { 0x03, 0x03 }, + /* 8ea */ { 0x03, 0x03 }, + /* 8eb */ { 0x03, 0x03 }, + /* 8ec */ { 0x03, 0x03 }, + /* 8ed */ { 0x03, 0x03 }, + /* 8ee */ { 0x03, 0x03 }, + /* 8ef */ { 0x03, 0x03 }, + /* 8f0 */ { 0x03, 0x03 }, + /* 8f1 */ { 0x03, 0x03 }, + /* 8f2 */ { 0x03, 0x03 }, + /* 8f3 */ { 0x03, 0x03 }, + /* 8f4 */ { 0x03, 0x03 }, + /* 8f5 */ { 0x03, 0x03 }, + /* 8f6 */ { 0x03, 0x03 }, + /* 8f7 */ { 0x03, 0x03 }, + /* 8f8 */ { 0x03, 0x03 }, + /* 8f9 */ { 0x03, 0x03 }, + /* 8fa */ { 0x03, 0x03 }, + /* 8fb */ { 0x03, 0x03 }, + /* 8fc */ { 0x03, 0x03 }, + /* 8fd */ { 0x03, 0x03 }, + /* 8fe */ { 0x03, 0x03 }, + /* 8ff */ { 0x03, 0x03 }, + /* 900 */ { 0x03, 0x03 }, + /* 901 */ { 0x03, 0x03 }, + /* 902 */ { 0x03, 0x03 }, + /* 903 */ { 0x03, 0x03 }, + /* 904 */ { 0x03, 0x03 }, + /* 905 */ { 0x03, 0x03 }, + /* 906 */ { 0x03, 0x03 }, + /* 907 */ { 0x03, 0x03 }, + /* 908 */ { 0x03, 0x03 }, + /* 909 */ { 0x03, 0x03 }, + /* 90a */ { 0x03, 0x03 }, + /* 90b */ { 0x03, 0x03 }, + /* 90c */ { 0x03, 0x03 }, + /* 90d */ { 0x03, 0x03 }, + /* 90e */ { 0x03, 0x03 }, + /* 90f */ { 0x03, 0x03 }, + /* 910 */ { 0x03, 0x03 }, + /* 911 */ { 0x03, 0x03 }, + /* 912 */ { 0x03, 0x03 }, + /* 913 */ { 0x03, 0x03 }, + /* 914 */ { 0x03, 0x03 }, + /* 915 */ { 0x03, 0x03 }, + /* 916 */ { 0x03, 0x03 }, + /* 917 */ { 0x03, 0x03 }, + /* 918 */ { 0x03, 0x03 }, + /* 919 */ { 0x03, 0x03 }, + /* 91a */ { 0x03, 0x03 }, + /* 91b */ { 0x03, 0x03 }, + /* 91c */ { 0x03, 0x03 }, + /* 91d */ { 0x03, 0x03 }, + /* 91e */ { 0x03, 0x03 }, + /* 91f */ { 0x03, 0x03 }, + /* 920 */ { 0x03, 0x03 }, + /* 921 */ { 0x03, 0x03 }, + /* 922 */ { 0x03, 0x03 }, + /* 923 */ { 0x03, 0x03 }, + /* 924 */ { 0x03, 0x03 }, + /* 925 */ { 0x03, 0x03 }, + /* 926 */ { 0x03, 0x03 }, + /* 927 */ { 0x03, 0x03 }, + /* 928 */ { 0x03, 0x03 }, + /* 929 */ { 0x03, 0x03 }, + /* 92a */ { 0x03, 0x03 }, + /* 92b */ { 0x03, 0x03 }, + /* 92c */ { 0x03, 0x03 }, + /* 92d */ { 0x03, 0x03 }, + /* 92e */ { 0x03, 0x03 }, + /* 92f */ { 0x03, 0x03 }, + /* 930 */ { 0x03, 0x03 }, + /* 931 */ { 0x03, 0x03 }, + /* 932 */ { 0x03, 0x03 }, + /* 933 */ { 0x03, 0x03 }, + /* 934 */ { 0x03, 0x03 }, + /* 935 */ { 0x03, 0x03 }, + /* 936 */ { 0x03, 0x03 }, + /* 937 */ { 0x03, 0x03 }, + /* 938 */ { 0x03, 0x03 }, + /* 939 */ { 0x03, 0x03 }, + /* 93a */ { 0x03, 0x03 }, + /* 93b */ { 0x03, 0x03 }, + /* 93c */ { 0x03, 0x03 }, + /* 93d */ { 0x03, 0x03 }, + /* 93e */ { 0x03, 0x03 }, + /* 93f */ { 0x03, 0x03 }, + /* 940 */ { 0x03, 0x03 }, + /* 941 */ { 0x03, 0x03 }, + /* 942 */ { 0x03, 0x03 }, + /* 943 */ { 0x03, 0x03 }, + /* 944 */ { 0x03, 0x03 }, + /* 945 */ { 0x03, 0x03 }, + /* 946 */ { 0x03, 0x03 }, + /* 947 */ { 0x03, 0x03 }, + /* 948 */ { 0x03, 0x03 }, + /* 949 */ { 0x03, 0x03 }, + /* 94a */ { 0x03, 0x03 }, + /* 94b */ { 0x03, 0x03 }, + /* 94c */ { 0x03, 0x03 }, + /* 94d */ { 0x03, 0x03 }, + /* 94e */ { 0x03, 0x03 }, + /* 94f */ { 0x03, 0x03 }, + /* 950 */ { 0x03, 0x03 }, + /* 951 */ { 0x03, 0x03 }, + /* 952 */ { 0x03, 0x03 }, + /* 953 */ { 0x03, 0x03 }, + /* 954 */ { 0x03, 0x03 }, + /* 955 */ { 0x03, 0x03 }, + /* 956 */ { 0x03, 0x03 }, + /* 957 */ { 0x03, 0x03 }, + /* 958 */ { 0x03, 0x03 }, + /* 959 */ { 0x03, 0x03 }, + /* 95a */ { 0x03, 0x03 }, + /* 95b */ { 0x03, 0x03 }, + /* 95c */ { 0x03, 0x03 }, + /* 95d */ { 0x03, 0x03 }, + /* 95e */ { 0x03, 0x03 }, + /* 95f */ { 0x03, 0x03 }, + /* 960 */ { 0x03, 0x03 }, + /* 961 */ { 0x03, 0x03 }, + /* 962 */ { 0x03, 0x03 }, + /* 963 */ { 0x03, 0x03 }, + /* 964 */ { 0x03, 0x03 }, + /* 965 */ { 0x03, 0x03 }, + /* 966 */ { 0x03, 0x03 }, + /* 967 */ { 0x03, 0x03 }, + /* 968 */ { 0x03, 0x03 }, + /* 969 */ { 0x03, 0x03 }, + /* 96a */ { 0x03, 0x03 }, + /* 96b */ { 0x03, 0x03 }, + /* 96c */ { 0x03, 0x03 }, + /* 96d */ { 0x03, 0x03 }, + /* 96e */ { 0x03, 0x03 }, + /* 96f */ { 0x03, 0x03 }, + /* 970 */ { 0x03, 0x03 }, + /* 971 */ { 0x03, 0x03 }, + /* 972 */ { 0x03, 0x03 }, + /* 973 */ { 0x03, 0x03 }, + /* 974 */ { 0x03, 0x03 }, + /* 975 */ { 0x03, 0x03 }, + /* 976 */ { 0x03, 0x03 }, + /* 977 */ { 0x03, 0x03 }, + /* 978 */ { 0x03, 0x03 }, + /* 979 */ { 0x03, 0x03 }, + /* 97a */ { 0x03, 0x03 }, + /* 97b */ { 0x03, 0x03 }, + /* 97c */ { 0x03, 0x03 }, + /* 97d */ { 0x03, 0x03 }, + /* 97e */ { 0x03, 0x03 }, + /* 97f */ { 0x03, 0x03 }, + /* 980 */ { 0x03, 0x03 }, + /* 981 */ { 0x03, 0x03 }, + /* 982 */ { 0x03, 0x03 }, + /* 983 */ { 0x03, 0x03 }, + /* 984 */ { 0x03, 0x03 }, + /* 985 */ { 0x03, 0x03 }, + /* 986 */ { 0x03, 0x03 }, + /* 987 */ { 0x03, 0x03 }, + /* 988 */ { 0x03, 0x03 }, + /* 989 */ { 0x03, 0x03 }, + /* 98a */ { 0x03, 0x03 }, + /* 98b */ { 0x03, 0x03 }, + /* 98c */ { 0x03, 0x03 }, + /* 98d */ { 0x03, 0x03 }, + /* 98e */ { 0x03, 0x03 }, + /* 98f */ { 0x03, 0x03 }, + /* 990 */ { 0x03, 0x03 }, + /* 991 */ { 0x03, 0x03 }, + /* 992 */ { 0x03, 0x03 }, + /* 993 */ { 0x03, 0x03 }, + /* 994 */ { 0x03, 0x03 }, + /* 995 */ { 0x03, 0x03 }, + /* 996 */ { 0x03, 0x03 }, + /* 997 */ { 0x03, 0x03 }, + /* 998 */ { 0x03, 0x03 }, + /* 999 */ { 0x03, 0x03 }, + /* 99a */ { 0x03, 0x03 }, + /* 99b */ { 0x03, 0x03 }, + /* 99c */ { 0x03, 0x03 }, + /* 99d */ { 0x03, 0x03 }, + /* 99e */ { 0x03, 0x03 }, + /* 99f */ { 0x03, 0x03 }, + /* 9a0 */ { 0x03, 0x03 }, + /* 9a1 */ { 0x03, 0x03 }, + /* 9a2 */ { 0x03, 0x03 }, + /* 9a3 */ { 0x03, 0x03 }, + /* 9a4 */ { 0x03, 0x03 }, + /* 9a5 */ { 0x03, 0x03 }, + /* 9a6 */ { 0x03, 0x03 }, + /* 9a7 */ { 0x03, 0x03 }, + /* 9a8 */ { 0x03, 0x03 }, + /* 9a9 */ { 0x03, 0x03 }, + /* 9aa */ { 0x03, 0x03 }, + /* 9ab */ { 0x03, 0x03 }, + /* 9ac */ { 0x03, 0x03 }, + /* 9ad */ { 0x03, 0x03 }, + /* 9ae */ { 0x03, 0x03 }, + /* 9af */ { 0x03, 0x03 }, + /* 9b0 */ { 0x03, 0x03 }, + /* 9b1 */ { 0x03, 0x03 }, + /* 9b2 */ { 0x03, 0x03 }, + /* 9b3 */ { 0x03, 0x03 }, + /* 9b4 */ { 0x03, 0x03 }, + /* 9b5 */ { 0x03, 0x03 }, + /* 9b6 */ { 0x03, 0x03 }, + /* 9b7 */ { 0x03, 0x03 }, + /* 9b8 */ { 0x03, 0x03 }, + /* 9b9 */ { 0x03, 0x03 }, + /* 9ba */ { 0x03, 0x03 }, + /* 9bb */ { 0x03, 0x03 }, + /* 9bc */ { 0x03, 0x03 }, + /* 9bd */ { 0x03, 0x03 }, + /* 9be */ { 0x03, 0x03 }, + /* 9bf */ { 0x03, 0x03 }, + /* 9c0 */ { 0x03, 0x03 }, + /* 9c1 */ { 0x03, 0x03 }, + /* 9c2 */ { 0x03, 0x03 }, + /* 9c3 */ { 0x03, 0x03 }, + /* 9c4 */ { 0x03, 0x03 }, + /* 9c5 */ { 0x03, 0x03 }, + /* 9c6 */ { 0x03, 0x03 }, + /* 9c7 */ { 0x03, 0x03 }, + /* 9c8 */ { 0x03, 0x03 }, + /* 9c9 */ { 0x03, 0x03 }, + /* 9ca */ { 0x03, 0x03 }, + /* 9cb */ { 0x03, 0x03 }, + /* 9cc */ { 0x03, 0x03 }, + /* 9cd */ { 0x03, 0x03 }, + /* 9ce */ { 0x03, 0x03 }, + /* 9cf */ { 0x03, 0x03 }, + /* 9d0 */ { 0x03, 0x03 }, + /* 9d1 */ { 0x03, 0x03 }, + /* 9d2 */ { 0x03, 0x03 }, + /* 9d3 */ { 0x03, 0x03 }, + /* 9d4 */ { 0x03, 0x03 }, + /* 9d5 */ { 0x03, 0x03 }, + /* 9d6 */ { 0x03, 0x03 }, + /* 9d7 */ { 0x03, 0x03 }, + /* 9d8 */ { 0x03, 0x03 }, + /* 9d9 */ { 0x03, 0x03 }, + /* 9da */ { 0x03, 0x03 }, + /* 9db */ { 0x03, 0x03 }, + /* 9dc */ { 0x03, 0x03 }, + /* 9dd */ { 0x03, 0x03 }, + /* 9de */ { 0x03, 0x03 }, + /* 9df */ { 0x03, 0x03 }, + /* 9e0 */ { 0x03, 0x03 }, + /* 9e1 */ { 0x03, 0x03 }, + /* 9e2 */ { 0x03, 0x03 }, + /* 9e3 */ { 0x03, 0x03 }, + /* 9e4 */ { 0x03, 0x03 }, + /* 9e5 */ { 0x03, 0x03 }, + /* 9e6 */ { 0x03, 0x03 }, + /* 9e7 */ { 0x03, 0x03 }, + /* 9e8 */ { 0x03, 0x03 }, + /* 9e9 */ { 0x03, 0x03 }, + /* 9ea */ { 0x03, 0x03 }, + /* 9eb */ { 0x03, 0x03 }, + /* 9ec */ { 0x03, 0x03 }, + /* 9ed */ { 0x03, 0x03 }, + /* 9ee */ { 0x03, 0x03 }, + /* 9ef */ { 0x03, 0x03 }, + /* 9f0 */ { 0x03, 0x03 }, + /* 9f1 */ { 0x03, 0x03 }, + /* 9f2 */ { 0x03, 0x03 }, + /* 9f3 */ { 0x03, 0x03 }, + /* 9f4 */ { 0x03, 0x03 }, + /* 9f5 */ { 0x03, 0x03 }, + /* 9f6 */ { 0x03, 0x03 }, + /* 9f7 */ { 0x03, 0x03 }, + /* 9f8 */ { 0x03, 0x03 }, + /* 9f9 */ { 0x03, 0x03 }, + /* 9fa */ { 0x03, 0x03 }, + /* 9fb */ { 0x03, 0x03 }, + /* 9fc */ { 0x03, 0x03 }, + /* 9fd */ { 0x03, 0x03 }, + /* 9fe */ { 0x03, 0x03 }, + /* 9ff */ { 0x03, 0x03 }, + /* a00 */ { 0x04, 0x04 }, + /* a01 */ { 0x04, 0x04 }, + /* a02 */ { 0x04, 0x04 }, + /* a03 */ { 0x04, 0x04 }, + /* a04 */ { 0x04, 0x04 }, + /* a05 */ { 0x04, 0x04 }, + /* a06 */ { 0x04, 0x04 }, + /* a07 */ { 0x04, 0x04 }, + /* a08 */ { 0x04, 0x04 }, + /* a09 */ { 0x04, 0x04 }, + /* a0a */ { 0x04, 0x04 }, + /* a0b */ { 0x04, 0x04 }, + /* a0c */ { 0x04, 0x04 }, + /* a0d */ { 0x04, 0x04 }, + /* a0e */ { 0x04, 0x04 }, + /* a0f */ { 0x04, 0x04 }, + /* a10 */ { 0x04, 0x04 }, + /* a11 */ { 0x04, 0x04 }, + /* a12 */ { 0x04, 0x04 }, + /* a13 */ { 0x04, 0x04 }, + /* a14 */ { 0x04, 0x04 }, + /* a15 */ { 0x04, 0x04 }, + /* a16 */ { 0x04, 0x04 }, + /* a17 */ { 0x04, 0x04 }, + /* a18 */ { 0x04, 0x04 }, + /* a19 */ { 0x04, 0x04 }, + /* a1a */ { 0x04, 0x04 }, + /* a1b */ { 0x04, 0x04 }, + /* a1c */ { 0x04, 0x04 }, + /* a1d */ { 0x04, 0x04 }, + /* a1e */ { 0x04, 0x04 }, + /* a1f */ { 0x04, 0x04 }, + /* a20 */ { 0x04, 0x04 }, + /* a21 */ { 0x04, 0x04 }, + /* a22 */ { 0x04, 0x04 }, + /* a23 */ { 0x04, 0x04 }, + /* a24 */ { 0x04, 0x04 }, + /* a25 */ { 0x04, 0x04 }, + /* a26 */ { 0x04, 0x04 }, + /* a27 */ { 0x04, 0x04 }, + /* a28 */ { 0x04, 0x04 }, + /* a29 */ { 0x04, 0x04 }, + /* a2a */ { 0x04, 0x04 }, + /* a2b */ { 0x04, 0x04 }, + /* a2c */ { 0x04, 0x04 }, + /* a2d */ { 0x04, 0x04 }, + /* a2e */ { 0x04, 0x04 }, + /* a2f */ { 0x04, 0x04 }, + /* a30 */ { 0x04, 0x04 }, + /* a31 */ { 0x04, 0x04 }, + /* a32 */ { 0x04, 0x04 }, + /* a33 */ { 0x04, 0x04 }, + /* a34 */ { 0x04, 0x04 }, + /* a35 */ { 0x04, 0x04 }, + /* a36 */ { 0x04, 0x04 }, + /* a37 */ { 0x04, 0x04 }, + /* a38 */ { 0x04, 0x04 }, + /* a39 */ { 0x04, 0x04 }, + /* a3a */ { 0x04, 0x04 }, + /* a3b */ { 0x04, 0x04 }, + /* a3c */ { 0x04, 0x04 }, + /* a3d */ { 0x04, 0x04 }, + /* a3e */ { 0x04, 0x04 }, + /* a3f */ { 0x04, 0x04 }, + /* a40 */ { 0x04, 0x04 }, + /* a41 */ { 0x04, 0x04 }, + /* a42 */ { 0x04, 0x04 }, + /* a43 */ { 0x04, 0x04 }, + /* a44 */ { 0x04, 0x04 }, + /* a45 */ { 0x04, 0x04 }, + /* a46 */ { 0x04, 0x04 }, + /* a47 */ { 0x04, 0x04 }, + /* a48 */ { 0x04, 0x04 }, + /* a49 */ { 0x04, 0x04 }, + /* a4a */ { 0x04, 0x04 }, + /* a4b */ { 0x04, 0x04 }, + /* a4c */ { 0x04, 0x04 }, + /* a4d */ { 0x04, 0x04 }, + /* a4e */ { 0x04, 0x04 }, + /* a4f */ { 0x04, 0x04 }, + /* a50 */ { 0x04, 0x04 }, + /* a51 */ { 0x04, 0x04 }, + /* a52 */ { 0x04, 0x04 }, + /* a53 */ { 0x04, 0x04 }, + /* a54 */ { 0x04, 0x04 }, + /* a55 */ { 0x04, 0x04 }, + /* a56 */ { 0x04, 0x04 }, + /* a57 */ { 0x04, 0x04 }, + /* a58 */ { 0x04, 0x04 }, + /* a59 */ { 0x04, 0x04 }, + /* a5a */ { 0x04, 0x04 }, + /* a5b */ { 0x04, 0x04 }, + /* a5c */ { 0x04, 0x04 }, + /* a5d */ { 0x04, 0x04 }, + /* a5e */ { 0x04, 0x04 }, + /* a5f */ { 0x04, 0x04 }, + /* a60 */ { 0x04, 0x04 }, + /* a61 */ { 0x04, 0x04 }, + /* a62 */ { 0x04, 0x04 }, + /* a63 */ { 0x04, 0x04 }, + /* a64 */ { 0x04, 0x04 }, + /* a65 */ { 0x04, 0x04 }, + /* a66 */ { 0x04, 0x04 }, + /* a67 */ { 0x04, 0x04 }, + /* a68 */ { 0x04, 0x04 }, + /* a69 */ { 0x04, 0x04 }, + /* a6a */ { 0x04, 0x04 }, + /* a6b */ { 0x04, 0x04 }, + /* a6c */ { 0x04, 0x04 }, + /* a6d */ { 0x04, 0x04 }, + /* a6e */ { 0x04, 0x04 }, + /* a6f */ { 0x04, 0x04 }, + /* a70 */ { 0x04, 0x04 }, + /* a71 */ { 0x04, 0x04 }, + /* a72 */ { 0x04, 0x04 }, + /* a73 */ { 0x04, 0x04 }, + /* a74 */ { 0x04, 0x04 }, + /* a75 */ { 0x04, 0x04 }, + /* a76 */ { 0x04, 0x04 }, + /* a77 */ { 0x04, 0x04 }, + /* a78 */ { 0x04, 0x04 }, + /* a79 */ { 0x04, 0x04 }, + /* a7a */ { 0x04, 0x04 }, + /* a7b */ { 0x04, 0x04 }, + /* a7c */ { 0x04, 0x04 }, + /* a7d */ { 0x04, 0x04 }, + /* a7e */ { 0x04, 0x04 }, + /* a7f */ { 0x04, 0x04 }, + /* a80 */ { 0x04, 0x04 }, + /* a81 */ { 0x04, 0x04 }, + /* a82 */ { 0x04, 0x04 }, + /* a83 */ { 0x04, 0x04 }, + /* a84 */ { 0x04, 0x04 }, + /* a85 */ { 0x04, 0x04 }, + /* a86 */ { 0x04, 0x04 }, + /* a87 */ { 0x04, 0x04 }, + /* a88 */ { 0x04, 0x04 }, + /* a89 */ { 0x04, 0x04 }, + /* a8a */ { 0x04, 0x04 }, + /* a8b */ { 0x04, 0x04 }, + /* a8c */ { 0x04, 0x04 }, + /* a8d */ { 0x04, 0x04 }, + /* a8e */ { 0x04, 0x04 }, + /* a8f */ { 0x04, 0x04 }, + /* a90 */ { 0x04, 0x04 }, + /* a91 */ { 0x04, 0x04 }, + /* a92 */ { 0x04, 0x04 }, + /* a93 */ { 0x04, 0x04 }, + /* a94 */ { 0x04, 0x04 }, + /* a95 */ { 0x04, 0x04 }, + /* a96 */ { 0x04, 0x04 }, + /* a97 */ { 0x04, 0x04 }, + /* a98 */ { 0x04, 0x04 }, + /* a99 */ { 0x04, 0x04 }, + /* a9a */ { 0x04, 0x04 }, + /* a9b */ { 0x04, 0x04 }, + /* a9c */ { 0x04, 0x04 }, + /* a9d */ { 0x04, 0x04 }, + /* a9e */ { 0x04, 0x04 }, + /* a9f */ { 0x04, 0x04 }, + /* aa0 */ { 0x04, 0x04 }, + /* aa1 */ { 0x04, 0x04 }, + /* aa2 */ { 0x04, 0x04 }, + /* aa3 */ { 0x04, 0x04 }, + /* aa4 */ { 0x04, 0x04 }, + /* aa5 */ { 0x04, 0x04 }, + /* aa6 */ { 0x04, 0x04 }, + /* aa7 */ { 0x04, 0x04 }, + /* aa8 */ { 0x04, 0x04 }, + /* aa9 */ { 0x04, 0x04 }, + /* aaa */ { 0x04, 0x04 }, + /* aab */ { 0x04, 0x04 }, + /* aac */ { 0x04, 0x04 }, + /* aad */ { 0x04, 0x04 }, + /* aae */ { 0x04, 0x04 }, + /* aaf */ { 0x04, 0x04 }, + /* ab0 */ { 0x04, 0x04 }, + /* ab1 */ { 0x04, 0x04 }, + /* ab2 */ { 0x04, 0x04 }, + /* ab3 */ { 0x04, 0x04 }, + /* ab4 */ { 0x04, 0x04 }, + /* ab5 */ { 0x04, 0x04 }, + /* ab6 */ { 0x04, 0x04 }, + /* ab7 */ { 0x04, 0x04 }, + /* ab8 */ { 0x04, 0x04 }, + /* ab9 */ { 0x04, 0x04 }, + /* aba */ { 0x04, 0x04 }, + /* abb */ { 0x04, 0x04 }, + /* abc */ { 0x04, 0x04 }, + /* abd */ { 0x04, 0x04 }, + /* abe */ { 0x04, 0x04 }, + /* abf */ { 0x04, 0x04 }, + /* ac0 */ { 0x04, 0x04 }, + /* ac1 */ { 0x04, 0x04 }, + /* ac2 */ { 0x04, 0x04 }, + /* ac3 */ { 0x04, 0x04 }, + /* ac4 */ { 0x04, 0x04 }, + /* ac5 */ { 0x04, 0x04 }, + /* ac6 */ { 0x04, 0x04 }, + /* ac7 */ { 0x04, 0x04 }, + /* ac8 */ { 0x04, 0x04 }, + /* ac9 */ { 0x04, 0x04 }, + /* aca */ { 0x04, 0x04 }, + /* acb */ { 0x04, 0x04 }, + /* acc */ { 0x04, 0x04 }, + /* acd */ { 0x04, 0x04 }, + /* ace */ { 0x04, 0x04 }, + /* acf */ { 0x04, 0x04 }, + /* ad0 */ { 0x04, 0x04 }, + /* ad1 */ { 0x04, 0x04 }, + /* ad2 */ { 0x04, 0x04 }, + /* ad3 */ { 0x04, 0x04 }, + /* ad4 */ { 0x04, 0x04 }, + /* ad5 */ { 0x04, 0x04 }, + /* ad6 */ { 0x04, 0x04 }, + /* ad7 */ { 0x04, 0x04 }, + /* ad8 */ { 0x04, 0x04 }, + /* ad9 */ { 0x04, 0x04 }, + /* ada */ { 0x04, 0x04 }, + /* adb */ { 0x04, 0x04 }, + /* adc */ { 0x04, 0x04 }, + /* add */ { 0x04, 0x04 }, + /* ade */ { 0x04, 0x04 }, + /* adf */ { 0x04, 0x04 }, + /* ae0 */ { 0x04, 0x04 }, + /* ae1 */ { 0x04, 0x04 }, + /* ae2 */ { 0x04, 0x04 }, + /* ae3 */ { 0x04, 0x04 }, + /* ae4 */ { 0x04, 0x04 }, + /* ae5 */ { 0x04, 0x04 }, + /* ae6 */ { 0x04, 0x04 }, + /* ae7 */ { 0x04, 0x04 }, + /* ae8 */ { 0x04, 0x04 }, + /* ae9 */ { 0x04, 0x04 }, + /* aea */ { 0x04, 0x04 }, + /* aeb */ { 0x04, 0x04 }, + /* aec */ { 0x04, 0x04 }, + /* aed */ { 0x04, 0x04 }, + /* aee */ { 0x04, 0x04 }, + /* aef */ { 0x04, 0x04 }, + /* af0 */ { 0x04, 0x04 }, + /* af1 */ { 0x04, 0x04 }, + /* af2 */ { 0x04, 0x04 }, + /* af3 */ { 0x04, 0x04 }, + /* af4 */ { 0x04, 0x04 }, + /* af5 */ { 0x04, 0x04 }, + /* af6 */ { 0x04, 0x04 }, + /* af7 */ { 0x04, 0x04 }, + /* af8 */ { 0x04, 0x04 }, + /* af9 */ { 0x04, 0x04 }, + /* afa */ { 0x04, 0x04 }, + /* afb */ { 0x04, 0x04 }, + /* afc */ { 0x04, 0x04 }, + /* afd */ { 0x04, 0x04 }, + /* afe */ { 0x04, 0x04 }, + /* aff */ { 0x04, 0x04 }, + /* b00 */ { 0x11, 0x04 }, + /* b01 */ { 0x11, 0x04 }, + /* b02 */ { 0x11, 0x04 }, + /* b03 */ { 0x11, 0x04 }, + /* b04 */ { 0x11, 0x04 }, + /* b05 */ { 0x11, 0x04 }, + /* b06 */ { 0x11, 0x04 }, + /* b07 */ { 0x11, 0x04 }, + /* b08 */ { 0x11, 0x04 }, + /* b09 */ { 0x11, 0x04 }, + /* b0a */ { 0x11, 0x04 }, + /* b0b */ { 0x11, 0x04 }, + /* b0c */ { 0x11, 0x04 }, + /* b0d */ { 0x11, 0x04 }, + /* b0e */ { 0x11, 0x04 }, + /* b0f */ { 0x11, 0x04 }, + /* b10 */ { 0x11, 0x04 }, + /* b11 */ { 0x11, 0x04 }, + /* b12 */ { 0x11, 0x04 }, + /* b13 */ { 0x11, 0x04 }, + /* b14 */ { 0x11, 0x04 }, + /* b15 */ { 0x11, 0x04 }, + /* b16 */ { 0x11, 0x04 }, + /* b17 */ { 0x11, 0x04 }, + /* b18 */ { 0x11, 0x04 }, + /* b19 */ { 0x11, 0x04 }, + /* b1a */ { 0x11, 0x04 }, + /* b1b */ { 0x11, 0x04 }, + /* b1c */ { 0x11, 0x04 }, + /* b1d */ { 0x11, 0x04 }, + /* b1e */ { 0x11, 0x04 }, + /* b1f */ { 0x11, 0x04 }, + /* b20 */ { 0x11, 0x04 }, + /* b21 */ { 0x11, 0x04 }, + /* b22 */ { 0x11, 0x04 }, + /* b23 */ { 0x11, 0x04 }, + /* b24 */ { 0x11, 0x04 }, + /* b25 */ { 0x11, 0x04 }, + /* b26 */ { 0x11, 0x04 }, + /* b27 */ { 0x11, 0x04 }, + /* b28 */ { 0x11, 0x04 }, + /* b29 */ { 0x11, 0x04 }, + /* b2a */ { 0x11, 0x04 }, + /* b2b */ { 0x11, 0x04 }, + /* b2c */ { 0x11, 0x04 }, + /* b2d */ { 0x11, 0x04 }, + /* b2e */ { 0x11, 0x04 }, + /* b2f */ { 0x11, 0x04 }, + /* b30 */ { 0x11, 0x04 }, + /* b31 */ { 0x11, 0x04 }, + /* b32 */ { 0x11, 0x04 }, + /* b33 */ { 0x11, 0x04 }, + /* b34 */ { 0x11, 0x04 }, + /* b35 */ { 0x11, 0x04 }, + /* b36 */ { 0x11, 0x04 }, + /* b37 */ { 0x11, 0x04 }, + /* b38 */ { 0x11, 0x04 }, + /* b39 */ { 0x11, 0x04 }, + /* b3a */ { 0x11, 0x04 }, + /* b3b */ { 0x11, 0x04 }, + /* b3c */ { 0x11, 0x04 }, + /* b3d */ { 0x11, 0x04 }, + /* b3e */ { 0x11, 0x04 }, + /* b3f */ { 0x11, 0x04 }, + /* b40 */ { 0x11, 0x04 }, + /* b41 */ { 0x11, 0x04 }, + /* b42 */ { 0x11, 0x04 }, + /* b43 */ { 0x11, 0x04 }, + /* b44 */ { 0x11, 0x04 }, + /* b45 */ { 0x11, 0x04 }, + /* b46 */ { 0x11, 0x04 }, + /* b47 */ { 0x11, 0x04 }, + /* b48 */ { 0x11, 0x04 }, + /* b49 */ { 0x11, 0x04 }, + /* b4a */ { 0x11, 0x04 }, + /* b4b */ { 0x11, 0x04 }, + /* b4c */ { 0x11, 0x04 }, + /* b4d */ { 0x11, 0x04 }, + /* b4e */ { 0x11, 0x04 }, + /* b4f */ { 0x11, 0x04 }, + /* b50 */ { 0x11, 0x04 }, + /* b51 */ { 0x11, 0x04 }, + /* b52 */ { 0x11, 0x04 }, + /* b53 */ { 0x11, 0x04 }, + /* b54 */ { 0x11, 0x04 }, + /* b55 */ { 0x11, 0x04 }, + /* b56 */ { 0x11, 0x04 }, + /* b57 */ { 0x11, 0x04 }, + /* b58 */ { 0x11, 0x04 }, + /* b59 */ { 0x11, 0x04 }, + /* b5a */ { 0x11, 0x04 }, + /* b5b */ { 0x11, 0x04 }, + /* b5c */ { 0x11, 0x04 }, + /* b5d */ { 0x11, 0x04 }, + /* b5e */ { 0x11, 0x04 }, + /* b5f */ { 0x11, 0x04 }, + /* b60 */ { 0x11, 0x04 }, + /* b61 */ { 0x11, 0x04 }, + /* b62 */ { 0x11, 0x04 }, + /* b63 */ { 0x11, 0x04 }, + /* b64 */ { 0x11, 0x04 }, + /* b65 */ { 0x11, 0x04 }, + /* b66 */ { 0x11, 0x04 }, + /* b67 */ { 0x11, 0x04 }, + /* b68 */ { 0x11, 0x04 }, + /* b69 */ { 0x11, 0x04 }, + /* b6a */ { 0x11, 0x04 }, + /* b6b */ { 0x11, 0x04 }, + /* b6c */ { 0x11, 0x04 }, + /* b6d */ { 0x11, 0x04 }, + /* b6e */ { 0x11, 0x04 }, + /* b6f */ { 0x11, 0x04 }, + /* b70 */ { 0x11, 0x04 }, + /* b71 */ { 0x11, 0x04 }, + /* b72 */ { 0x11, 0x04 }, + /* b73 */ { 0x11, 0x04 }, + /* b74 */ { 0x11, 0x04 }, + /* b75 */ { 0x11, 0x04 }, + /* b76 */ { 0x11, 0x04 }, + /* b77 */ { 0x11, 0x04 }, + /* b78 */ { 0x11, 0x04 }, + /* b79 */ { 0x11, 0x04 }, + /* b7a */ { 0x11, 0x04 }, + /* b7b */ { 0x11, 0x04 }, + /* b7c */ { 0x11, 0x04 }, + /* b7d */ { 0x11, 0x04 }, + /* b7e */ { 0x11, 0x04 }, + /* b7f */ { 0x11, 0x04 }, + /* b80 */ { 0x11, 0x04 }, + /* b81 */ { 0x11, 0x04 }, + /* b82 */ { 0x11, 0x04 }, + /* b83 */ { 0x11, 0x04 }, + /* b84 */ { 0x11, 0x04 }, + /* b85 */ { 0x11, 0x04 }, + /* b86 */ { 0x11, 0x04 }, + /* b87 */ { 0x11, 0x04 }, + /* b88 */ { 0x11, 0x04 }, + /* b89 */ { 0x11, 0x04 }, + /* b8a */ { 0x11, 0x04 }, + /* b8b */ { 0x11, 0x04 }, + /* b8c */ { 0x11, 0x04 }, + /* b8d */ { 0x11, 0x04 }, + /* b8e */ { 0x11, 0x04 }, + /* b8f */ { 0x11, 0x04 }, + /* b90 */ { 0x11, 0x04 }, + /* b91 */ { 0x11, 0x04 }, + /* b92 */ { 0x11, 0x04 }, + /* b93 */ { 0x11, 0x04 }, + /* b94 */ { 0x11, 0x04 }, + /* b95 */ { 0x11, 0x04 }, + /* b96 */ { 0x11, 0x04 }, + /* b97 */ { 0x11, 0x04 }, + /* b98 */ { 0x11, 0x04 }, + /* b99 */ { 0x11, 0x04 }, + /* b9a */ { 0x11, 0x04 }, + /* b9b */ { 0x11, 0x04 }, + /* b9c */ { 0x11, 0x04 }, + /* b9d */ { 0x11, 0x04 }, + /* b9e */ { 0x11, 0x04 }, + /* b9f */ { 0x11, 0x04 }, + /* ba0 */ { 0x11, 0x04 }, + /* ba1 */ { 0x11, 0x04 }, + /* ba2 */ { 0x11, 0x04 }, + /* ba3 */ { 0x11, 0x04 }, + /* ba4 */ { 0x11, 0x04 }, + /* ba5 */ { 0x11, 0x04 }, + /* ba6 */ { 0x11, 0x04 }, + /* ba7 */ { 0x11, 0x04 }, + /* ba8 */ { 0x11, 0x04 }, + /* ba9 */ { 0x11, 0x04 }, + /* baa */ { 0x11, 0x04 }, + /* bab */ { 0x11, 0x04 }, + /* bac */ { 0x11, 0x04 }, + /* bad */ { 0x11, 0x04 }, + /* bae */ { 0x11, 0x04 }, + /* baf */ { 0x11, 0x04 }, + /* bb0 */ { 0x11, 0x04 }, + /* bb1 */ { 0x11, 0x04 }, + /* bb2 */ { 0x11, 0x04 }, + /* bb3 */ { 0x11, 0x04 }, + /* bb4 */ { 0x11, 0x04 }, + /* bb5 */ { 0x11, 0x04 }, + /* bb6 */ { 0x11, 0x04 }, + /* bb7 */ { 0x11, 0x04 }, + /* bb8 */ { 0x11, 0x04 }, + /* bb9 */ { 0x11, 0x04 }, + /* bba */ { 0x11, 0x04 }, + /* bbb */ { 0x11, 0x04 }, + /* bbc */ { 0x11, 0x04 }, + /* bbd */ { 0x11, 0x04 }, + /* bbe */ { 0x11, 0x04 }, + /* bbf */ { 0x11, 0x04 }, + /* bc0 */ { 0x11, 0x04 }, + /* bc1 */ { 0x11, 0x04 }, + /* bc2 */ { 0x11, 0x04 }, + /* bc3 */ { 0x11, 0x04 }, + /* bc4 */ { 0x11, 0x04 }, + /* bc5 */ { 0x11, 0x04 }, + /* bc6 */ { 0x11, 0x04 }, + /* bc7 */ { 0x11, 0x04 }, + /* bc8 */ { 0x11, 0x04 }, + /* bc9 */ { 0x11, 0x04 }, + /* bca */ { 0x11, 0x04 }, + /* bcb */ { 0x11, 0x04 }, + /* bcc */ { 0x11, 0x04 }, + /* bcd */ { 0x11, 0x04 }, + /* bce */ { 0x11, 0x04 }, + /* bcf */ { 0x11, 0x04 }, + /* bd0 */ { 0x11, 0x04 }, + /* bd1 */ { 0x11, 0x04 }, + /* bd2 */ { 0x11, 0x04 }, + /* bd3 */ { 0x11, 0x04 }, + /* bd4 */ { 0x11, 0x04 }, + /* bd5 */ { 0x11, 0x04 }, + /* bd6 */ { 0x11, 0x04 }, + /* bd7 */ { 0x11, 0x04 }, + /* bd8 */ { 0x11, 0x04 }, + /* bd9 */ { 0x11, 0x04 }, + /* bda */ { 0x11, 0x04 }, + /* bdb */ { 0x11, 0x04 }, + /* bdc */ { 0x11, 0x04 }, + /* bdd */ { 0x11, 0x04 }, + /* bde */ { 0x11, 0x04 }, + /* bdf */ { 0x11, 0x04 }, + /* be0 */ { 0x11, 0x04 }, + /* be1 */ { 0x11, 0x04 }, + /* be2 */ { 0x11, 0x04 }, + /* be3 */ { 0x11, 0x04 }, + /* be4 */ { 0x11, 0x04 }, + /* be5 */ { 0x11, 0x04 }, + /* be6 */ { 0x11, 0x04 }, + /* be7 */ { 0x11, 0x04 }, + /* be8 */ { 0x11, 0x04 }, + /* be9 */ { 0x11, 0x04 }, + /* bea */ { 0x11, 0x04 }, + /* beb */ { 0x11, 0x04 }, + /* bec */ { 0x11, 0x04 }, + /* bed */ { 0x11, 0x04 }, + /* bee */ { 0x11, 0x04 }, + /* bef */ { 0x11, 0x04 }, + /* bf0 */ { 0x11, 0x04 }, + /* bf1 */ { 0x11, 0x04 }, + /* bf2 */ { 0x11, 0x04 }, + /* bf3 */ { 0x11, 0x04 }, + /* bf4 */ { 0x11, 0x04 }, + /* bf5 */ { 0x11, 0x04 }, + /* bf6 */ { 0x11, 0x04 }, + /* bf7 */ { 0x11, 0x04 }, + /* bf8 */ { 0x11, 0x04 }, + /* bf9 */ { 0x11, 0x04 }, + /* bfa */ { 0x11, 0x04 }, + /* bfb */ { 0x11, 0x04 }, + /* bfc */ { 0x11, 0x04 }, + /* bfd */ { 0x11, 0x04 }, + /* bfe */ { 0x11, 0x04 }, + /* bff */ { 0x11, 0x04 }, + /* c00 */ { 0x05, 0x05 }, + /* c01 */ { 0x05, 0x05 }, + /* c02 */ { 0x05, 0x05 }, + /* c03 */ { 0x05, 0x05 }, + /* c04 */ { 0x05, 0x05 }, + /* c05 */ { 0x05, 0x05 }, + /* c06 */ { 0x05, 0x05 }, + /* c07 */ { 0x05, 0x05 }, + /* c08 */ { 0x05, 0x05 }, + /* c09 */ { 0x05, 0x05 }, + /* c0a */ { 0x05, 0x05 }, + /* c0b */ { 0x05, 0x05 }, + /* c0c */ { 0x05, 0x05 }, + /* c0d */ { 0x05, 0x05 }, + /* c0e */ { 0x05, 0x05 }, + /* c0f */ { 0x05, 0x05 }, + /* c10 */ { 0x05, 0x05 }, + /* c11 */ { 0x05, 0x05 }, + /* c12 */ { 0x05, 0x05 }, + /* c13 */ { 0x05, 0x05 }, + /* c14 */ { 0x05, 0x05 }, + /* c15 */ { 0x05, 0x05 }, + /* c16 */ { 0x05, 0x05 }, + /* c17 */ { 0x05, 0x05 }, + /* c18 */ { 0x05, 0x05 }, + /* c19 */ { 0x05, 0x05 }, + /* c1a */ { 0x05, 0x05 }, + /* c1b */ { 0x05, 0x05 }, + /* c1c */ { 0x05, 0x05 }, + /* c1d */ { 0x05, 0x05 }, + /* c1e */ { 0x05, 0x05 }, + /* c1f */ { 0x05, 0x05 }, + /* c20 */ { 0x05, 0x05 }, + /* c21 */ { 0x05, 0x05 }, + /* c22 */ { 0x05, 0x05 }, + /* c23 */ { 0x05, 0x05 }, + /* c24 */ { 0x05, 0x05 }, + /* c25 */ { 0x05, 0x05 }, + /* c26 */ { 0x05, 0x05 }, + /* c27 */ { 0x05, 0x05 }, + /* c28 */ { 0x05, 0x05 }, + /* c29 */ { 0x05, 0x05 }, + /* c2a */ { 0x05, 0x05 }, + /* c2b */ { 0x05, 0x05 }, + /* c2c */ { 0x05, 0x05 }, + /* c2d */ { 0x05, 0x05 }, + /* c2e */ { 0x05, 0x05 }, + /* c2f */ { 0x05, 0x05 }, + /* c30 */ { 0x05, 0x05 }, + /* c31 */ { 0x05, 0x05 }, + /* c32 */ { 0x05, 0x05 }, + /* c33 */ { 0x05, 0x05 }, + /* c34 */ { 0x05, 0x05 }, + /* c35 */ { 0x05, 0x05 }, + /* c36 */ { 0x05, 0x05 }, + /* c37 */ { 0x05, 0x05 }, + /* c38 */ { 0x05, 0x05 }, + /* c39 */ { 0x05, 0x05 }, + /* c3a */ { 0x05, 0x05 }, + /* c3b */ { 0x05, 0x05 }, + /* c3c */ { 0x05, 0x05 }, + /* c3d */ { 0x05, 0x05 }, + /* c3e */ { 0x05, 0x05 }, + /* c3f */ { 0x05, 0x05 }, + /* c40 */ { 0x05, 0x05 }, + /* c41 */ { 0x05, 0x05 }, + /* c42 */ { 0x05, 0x05 }, + /* c43 */ { 0x05, 0x05 }, + /* c44 */ { 0x05, 0x05 }, + /* c45 */ { 0x05, 0x05 }, + /* c46 */ { 0x05, 0x05 }, + /* c47 */ { 0x05, 0x05 }, + /* c48 */ { 0x05, 0x05 }, + /* c49 */ { 0x05, 0x05 }, + /* c4a */ { 0x05, 0x05 }, + /* c4b */ { 0x05, 0x05 }, + /* c4c */ { 0x05, 0x05 }, + /* c4d */ { 0x05, 0x05 }, + /* c4e */ { 0x05, 0x05 }, + /* c4f */ { 0x05, 0x05 }, + /* c50 */ { 0x05, 0x05 }, + /* c51 */ { 0x05, 0x05 }, + /* c52 */ { 0x05, 0x05 }, + /* c53 */ { 0x05, 0x05 }, + /* c54 */ { 0x05, 0x05 }, + /* c55 */ { 0x05, 0x05 }, + /* c56 */ { 0x05, 0x05 }, + /* c57 */ { 0x05, 0x05 }, + /* c58 */ { 0x05, 0x05 }, + /* c59 */ { 0x05, 0x05 }, + /* c5a */ { 0x05, 0x05 }, + /* c5b */ { 0x05, 0x05 }, + /* c5c */ { 0x05, 0x05 }, + /* c5d */ { 0x05, 0x05 }, + /* c5e */ { 0x05, 0x05 }, + /* c5f */ { 0x05, 0x05 }, + /* c60 */ { 0x05, 0x05 }, + /* c61 */ { 0x05, 0x05 }, + /* c62 */ { 0x05, 0x05 }, + /* c63 */ { 0x05, 0x05 }, + /* c64 */ { 0x05, 0x05 }, + /* c65 */ { 0x05, 0x05 }, + /* c66 */ { 0x05, 0x05 }, + /* c67 */ { 0x05, 0x05 }, + /* c68 */ { 0x05, 0x05 }, + /* c69 */ { 0x05, 0x05 }, + /* c6a */ { 0x05, 0x05 }, + /* c6b */ { 0x05, 0x05 }, + /* c6c */ { 0x05, 0x05 }, + /* c6d */ { 0x05, 0x05 }, + /* c6e */ { 0x05, 0x05 }, + /* c6f */ { 0x05, 0x05 }, + /* c70 */ { 0x05, 0x05 }, + /* c71 */ { 0x05, 0x05 }, + /* c72 */ { 0x05, 0x05 }, + /* c73 */ { 0x05, 0x05 }, + /* c74 */ { 0x05, 0x05 }, + /* c75 */ { 0x05, 0x05 }, + /* c76 */ { 0x05, 0x05 }, + /* c77 */ { 0x05, 0x05 }, + /* c78 */ { 0x05, 0x05 }, + /* c79 */ { 0x05, 0x05 }, + /* c7a */ { 0x05, 0x05 }, + /* c7b */ { 0x05, 0x05 }, + /* c7c */ { 0x05, 0x05 }, + /* c7d */ { 0x05, 0x05 }, + /* c7e */ { 0x05, 0x05 }, + /* c7f */ { 0x05, 0x05 }, + /* c80 */ { 0x12, 0x05 }, + /* c81 */ { 0x12, 0x05 }, + /* c82 */ { 0x12, 0x05 }, + /* c83 */ { 0x12, 0x05 }, + /* c84 */ { 0x12, 0x05 }, + /* c85 */ { 0x12, 0x05 }, + /* c86 */ { 0x12, 0x05 }, + /* c87 */ { 0x12, 0x05 }, + /* c88 */ { 0x12, 0x05 }, + /* c89 */ { 0x12, 0x05 }, + /* c8a */ { 0x12, 0x05 }, + /* c8b */ { 0x12, 0x05 }, + /* c8c */ { 0x12, 0x05 }, + /* c8d */ { 0x12, 0x05 }, + /* c8e */ { 0x12, 0x05 }, + /* c8f */ { 0x12, 0x05 }, + /* c90 */ { 0x12, 0x05 }, + /* c91 */ { 0x12, 0x05 }, + /* c92 */ { 0x12, 0x05 }, + /* c93 */ { 0x12, 0x05 }, + /* c94 */ { 0x12, 0x05 }, + /* c95 */ { 0x12, 0x05 }, + /* c96 */ { 0x12, 0x05 }, + /* c97 */ { 0x12, 0x05 }, + /* c98 */ { 0x12, 0x05 }, + /* c99 */ { 0x12, 0x05 }, + /* c9a */ { 0x12, 0x05 }, + /* c9b */ { 0x12, 0x05 }, + /* c9c */ { 0x12, 0x05 }, + /* c9d */ { 0x12, 0x05 }, + /* c9e */ { 0x12, 0x05 }, + /* c9f */ { 0x12, 0x05 }, + /* ca0 */ { 0x12, 0x05 }, + /* ca1 */ { 0x12, 0x05 }, + /* ca2 */ { 0x12, 0x05 }, + /* ca3 */ { 0x12, 0x05 }, + /* ca4 */ { 0x12, 0x05 }, + /* ca5 */ { 0x12, 0x05 }, + /* ca6 */ { 0x12, 0x05 }, + /* ca7 */ { 0x12, 0x05 }, + /* ca8 */ { 0x12, 0x05 }, + /* ca9 */ { 0x12, 0x05 }, + /* caa */ { 0x12, 0x05 }, + /* cab */ { 0x12, 0x05 }, + /* cac */ { 0x12, 0x05 }, + /* cad */ { 0x12, 0x05 }, + /* cae */ { 0x12, 0x05 }, + /* caf */ { 0x12, 0x05 }, + /* cb0 */ { 0x12, 0x05 }, + /* cb1 */ { 0x12, 0x05 }, + /* cb2 */ { 0x12, 0x05 }, + /* cb3 */ { 0x12, 0x05 }, + /* cb4 */ { 0x12, 0x05 }, + /* cb5 */ { 0x12, 0x05 }, + /* cb6 */ { 0x12, 0x05 }, + /* cb7 */ { 0x12, 0x05 }, + /* cb8 */ { 0x12, 0x05 }, + /* cb9 */ { 0x12, 0x05 }, + /* cba */ { 0x12, 0x05 }, + /* cbb */ { 0x12, 0x05 }, + /* cbc */ { 0x12, 0x05 }, + /* cbd */ { 0x12, 0x05 }, + /* cbe */ { 0x12, 0x05 }, + /* cbf */ { 0x12, 0x05 }, + /* cc0 */ { 0x12, 0x05 }, + /* cc1 */ { 0x12, 0x05 }, + /* cc2 */ { 0x12, 0x05 }, + /* cc3 */ { 0x12, 0x05 }, + /* cc4 */ { 0x12, 0x05 }, + /* cc5 */ { 0x12, 0x05 }, + /* cc6 */ { 0x12, 0x05 }, + /* cc7 */ { 0x12, 0x05 }, + /* cc8 */ { 0x12, 0x05 }, + /* cc9 */ { 0x12, 0x05 }, + /* cca */ { 0x12, 0x05 }, + /* ccb */ { 0x12, 0x05 }, + /* ccc */ { 0x12, 0x05 }, + /* ccd */ { 0x12, 0x05 }, + /* cce */ { 0x12, 0x05 }, + /* ccf */ { 0x12, 0x05 }, + /* cd0 */ { 0x12, 0x05 }, + /* cd1 */ { 0x12, 0x05 }, + /* cd2 */ { 0x12, 0x05 }, + /* cd3 */ { 0x12, 0x05 }, + /* cd4 */ { 0x12, 0x05 }, + /* cd5 */ { 0x12, 0x05 }, + /* cd6 */ { 0x12, 0x05 }, + /* cd7 */ { 0x12, 0x05 }, + /* cd8 */ { 0x12, 0x05 }, + /* cd9 */ { 0x12, 0x05 }, + /* cda */ { 0x12, 0x05 }, + /* cdb */ { 0x12, 0x05 }, + /* cdc */ { 0x12, 0x05 }, + /* cdd */ { 0x12, 0x05 }, + /* cde */ { 0x12, 0x05 }, + /* cdf */ { 0x12, 0x05 }, + /* ce0 */ { 0x12, 0x05 }, + /* ce1 */ { 0x12, 0x05 }, + /* ce2 */ { 0x12, 0x05 }, + /* ce3 */ { 0x12, 0x05 }, + /* ce4 */ { 0x12, 0x05 }, + /* ce5 */ { 0x12, 0x05 }, + /* ce6 */ { 0x12, 0x05 }, + /* ce7 */ { 0x12, 0x05 }, + /* ce8 */ { 0x12, 0x05 }, + /* ce9 */ { 0x12, 0x05 }, + /* cea */ { 0x12, 0x05 }, + /* ceb */ { 0x12, 0x05 }, + /* cec */ { 0x12, 0x05 }, + /* ced */ { 0x12, 0x05 }, + /* cee */ { 0x12, 0x05 }, + /* cef */ { 0x12, 0x05 }, + /* cf0 */ { 0x12, 0x05 }, + /* cf1 */ { 0x12, 0x05 }, + /* cf2 */ { 0x12, 0x05 }, + /* cf3 */ { 0x12, 0x05 }, + /* cf4 */ { 0x12, 0x05 }, + /* cf5 */ { 0x12, 0x05 }, + /* cf6 */ { 0x12, 0x05 }, + /* cf7 */ { 0x12, 0x05 }, + /* cf8 */ { 0x12, 0x05 }, + /* cf9 */ { 0x12, 0x05 }, + /* cfa */ { 0x12, 0x05 }, + /* cfb */ { 0x12, 0x05 }, + /* cfc */ { 0x12, 0x05 }, + /* cfd */ { 0x12, 0x05 }, + /* cfe */ { 0x12, 0x05 }, + /* cff */ { 0x12, 0x05 }, + /* d00 */ { 0x21, 0x05 }, + /* d01 */ { 0x21, 0x05 }, + /* d02 */ { 0x21, 0x05 }, + /* d03 */ { 0x21, 0x05 }, + /* d04 */ { 0x21, 0x05 }, + /* d05 */ { 0x21, 0x05 }, + /* d06 */ { 0x21, 0x05 }, + /* d07 */ { 0x21, 0x05 }, + /* d08 */ { 0x21, 0x05 }, + /* d09 */ { 0x21, 0x05 }, + /* d0a */ { 0x21, 0x05 }, + /* d0b */ { 0x21, 0x05 }, + /* d0c */ { 0x21, 0x05 }, + /* d0d */ { 0x21, 0x05 }, + /* d0e */ { 0x21, 0x05 }, + /* d0f */ { 0x21, 0x05 }, + /* d10 */ { 0x21, 0x05 }, + /* d11 */ { 0x21, 0x05 }, + /* d12 */ { 0x21, 0x05 }, + /* d13 */ { 0x21, 0x05 }, + /* d14 */ { 0x21, 0x05 }, + /* d15 */ { 0x21, 0x05 }, + /* d16 */ { 0x21, 0x05 }, + /* d17 */ { 0x21, 0x05 }, + /* d18 */ { 0x21, 0x05 }, + /* d19 */ { 0x21, 0x05 }, + /* d1a */ { 0x21, 0x05 }, + /* d1b */ { 0x21, 0x05 }, + /* d1c */ { 0x21, 0x05 }, + /* d1d */ { 0x21, 0x05 }, + /* d1e */ { 0x21, 0x05 }, + /* d1f */ { 0x21, 0x05 }, + /* d20 */ { 0x21, 0x05 }, + /* d21 */ { 0x21, 0x05 }, + /* d22 */ { 0x21, 0x05 }, + /* d23 */ { 0x21, 0x05 }, + /* d24 */ { 0x21, 0x05 }, + /* d25 */ { 0x21, 0x05 }, + /* d26 */ { 0x21, 0x05 }, + /* d27 */ { 0x21, 0x05 }, + /* d28 */ { 0x21, 0x05 }, + /* d29 */ { 0x21, 0x05 }, + /* d2a */ { 0x21, 0x05 }, + /* d2b */ { 0x21, 0x05 }, + /* d2c */ { 0x21, 0x05 }, + /* d2d */ { 0x21, 0x05 }, + /* d2e */ { 0x21, 0x05 }, + /* d2f */ { 0x21, 0x05 }, + /* d30 */ { 0x21, 0x05 }, + /* d31 */ { 0x21, 0x05 }, + /* d32 */ { 0x21, 0x05 }, + /* d33 */ { 0x21, 0x05 }, + /* d34 */ { 0x21, 0x05 }, + /* d35 */ { 0x21, 0x05 }, + /* d36 */ { 0x21, 0x05 }, + /* d37 */ { 0x21, 0x05 }, + /* d38 */ { 0x21, 0x05 }, + /* d39 */ { 0x21, 0x05 }, + /* d3a */ { 0x21, 0x05 }, + /* d3b */ { 0x21, 0x05 }, + /* d3c */ { 0x21, 0x05 }, + /* d3d */ { 0x21, 0x05 }, + /* d3e */ { 0x21, 0x05 }, + /* d3f */ { 0x21, 0x05 }, + /* d40 */ { 0x21, 0x05 }, + /* d41 */ { 0x21, 0x05 }, + /* d42 */ { 0x21, 0x05 }, + /* d43 */ { 0x21, 0x05 }, + /* d44 */ { 0x21, 0x05 }, + /* d45 */ { 0x21, 0x05 }, + /* d46 */ { 0x21, 0x05 }, + /* d47 */ { 0x21, 0x05 }, + /* d48 */ { 0x21, 0x05 }, + /* d49 */ { 0x21, 0x05 }, + /* d4a */ { 0x21, 0x05 }, + /* d4b */ { 0x21, 0x05 }, + /* d4c */ { 0x21, 0x05 }, + /* d4d */ { 0x21, 0x05 }, + /* d4e */ { 0x21, 0x05 }, + /* d4f */ { 0x21, 0x05 }, + /* d50 */ { 0x21, 0x05 }, + /* d51 */ { 0x21, 0x05 }, + /* d52 */ { 0x21, 0x05 }, + /* d53 */ { 0x21, 0x05 }, + /* d54 */ { 0x21, 0x05 }, + /* d55 */ { 0x21, 0x05 }, + /* d56 */ { 0x21, 0x05 }, + /* d57 */ { 0x21, 0x05 }, + /* d58 */ { 0x21, 0x05 }, + /* d59 */ { 0x21, 0x05 }, + /* d5a */ { 0x21, 0x05 }, + /* d5b */ { 0x21, 0x05 }, + /* d5c */ { 0x21, 0x05 }, + /* d5d */ { 0x21, 0x05 }, + /* d5e */ { 0x21, 0x05 }, + /* d5f */ { 0x21, 0x05 }, + /* d60 */ { 0x21, 0x05 }, + /* d61 */ { 0x21, 0x05 }, + /* d62 */ { 0x21, 0x05 }, + /* d63 */ { 0x21, 0x05 }, + /* d64 */ { 0x21, 0x05 }, + /* d65 */ { 0x21, 0x05 }, + /* d66 */ { 0x21, 0x05 }, + /* d67 */ { 0x21, 0x05 }, + /* d68 */ { 0x21, 0x05 }, + /* d69 */ { 0x21, 0x05 }, + /* d6a */ { 0x21, 0x05 }, + /* d6b */ { 0x21, 0x05 }, + /* d6c */ { 0x21, 0x05 }, + /* d6d */ { 0x21, 0x05 }, + /* d6e */ { 0x21, 0x05 }, + /* d6f */ { 0x21, 0x05 }, + /* d70 */ { 0x21, 0x05 }, + /* d71 */ { 0x21, 0x05 }, + /* d72 */ { 0x21, 0x05 }, + /* d73 */ { 0x21, 0x05 }, + /* d74 */ { 0x21, 0x05 }, + /* d75 */ { 0x21, 0x05 }, + /* d76 */ { 0x21, 0x05 }, + /* d77 */ { 0x21, 0x05 }, + /* d78 */ { 0x21, 0x05 }, + /* d79 */ { 0x21, 0x05 }, + /* d7a */ { 0x21, 0x05 }, + /* d7b */ { 0x21, 0x05 }, + /* d7c */ { 0x21, 0x05 }, + /* d7d */ { 0x21, 0x05 }, + /* d7e */ { 0x21, 0x05 }, + /* d7f */ { 0x21, 0x05 }, + /* d80 */ { 0x06, 0x06 }, + /* d81 */ { 0x06, 0x06 }, + /* d82 */ { 0x06, 0x06 }, + /* d83 */ { 0x06, 0x06 }, + /* d84 */ { 0x06, 0x06 }, + /* d85 */ { 0x06, 0x06 }, + /* d86 */ { 0x06, 0x06 }, + /* d87 */ { 0x06, 0x06 }, + /* d88 */ { 0x06, 0x06 }, + /* d89 */ { 0x06, 0x06 }, + /* d8a */ { 0x06, 0x06 }, + /* d8b */ { 0x06, 0x06 }, + /* d8c */ { 0x06, 0x06 }, + /* d8d */ { 0x06, 0x06 }, + /* d8e */ { 0x06, 0x06 }, + /* d8f */ { 0x06, 0x06 }, + /* d90 */ { 0x06, 0x06 }, + /* d91 */ { 0x06, 0x06 }, + /* d92 */ { 0x06, 0x06 }, + /* d93 */ { 0x06, 0x06 }, + /* d94 */ { 0x06, 0x06 }, + /* d95 */ { 0x06, 0x06 }, + /* d96 */ { 0x06, 0x06 }, + /* d97 */ { 0x06, 0x06 }, + /* d98 */ { 0x06, 0x06 }, + /* d99 */ { 0x06, 0x06 }, + /* d9a */ { 0x06, 0x06 }, + /* d9b */ { 0x06, 0x06 }, + /* d9c */ { 0x06, 0x06 }, + /* d9d */ { 0x06, 0x06 }, + /* d9e */ { 0x06, 0x06 }, + /* d9f */ { 0x06, 0x06 }, + /* da0 */ { 0x06, 0x06 }, + /* da1 */ { 0x06, 0x06 }, + /* da2 */ { 0x06, 0x06 }, + /* da3 */ { 0x06, 0x06 }, + /* da4 */ { 0x06, 0x06 }, + /* da5 */ { 0x06, 0x06 }, + /* da6 */ { 0x06, 0x06 }, + /* da7 */ { 0x06, 0x06 }, + /* da8 */ { 0x06, 0x06 }, + /* da9 */ { 0x06, 0x06 }, + /* daa */ { 0x06, 0x06 }, + /* dab */ { 0x06, 0x06 }, + /* dac */ { 0x06, 0x06 }, + /* dad */ { 0x06, 0x06 }, + /* dae */ { 0x06, 0x06 }, + /* daf */ { 0x06, 0x06 }, + /* db0 */ { 0x06, 0x06 }, + /* db1 */ { 0x06, 0x06 }, + /* db2 */ { 0x06, 0x06 }, + /* db3 */ { 0x06, 0x06 }, + /* db4 */ { 0x06, 0x06 }, + /* db5 */ { 0x06, 0x06 }, + /* db6 */ { 0x06, 0x06 }, + /* db7 */ { 0x06, 0x06 }, + /* db8 */ { 0x06, 0x06 }, + /* db9 */ { 0x06, 0x06 }, + /* dba */ { 0x06, 0x06 }, + /* dbb */ { 0x06, 0x06 }, + /* dbc */ { 0x06, 0x06 }, + /* dbd */ { 0x06, 0x06 }, + /* dbe */ { 0x06, 0x06 }, + /* dbf */ { 0x06, 0x06 }, + /* dc0 */ { 0x31, 0x06 }, + /* dc1 */ { 0x31, 0x06 }, + /* dc2 */ { 0x31, 0x06 }, + /* dc3 */ { 0x31, 0x06 }, + /* dc4 */ { 0x31, 0x06 }, + /* dc5 */ { 0x31, 0x06 }, + /* dc6 */ { 0x31, 0x06 }, + /* dc7 */ { 0x31, 0x06 }, + /* dc8 */ { 0x31, 0x06 }, + /* dc9 */ { 0x31, 0x06 }, + /* dca */ { 0x31, 0x06 }, + /* dcb */ { 0x31, 0x06 }, + /* dcc */ { 0x31, 0x06 }, + /* dcd */ { 0x31, 0x06 }, + /* dce */ { 0x31, 0x06 }, + /* dcf */ { 0x31, 0x06 }, + /* dd0 */ { 0x31, 0x06 }, + /* dd1 */ { 0x31, 0x06 }, + /* dd2 */ { 0x31, 0x06 }, + /* dd3 */ { 0x31, 0x06 }, + /* dd4 */ { 0x31, 0x06 }, + /* dd5 */ { 0x31, 0x06 }, + /* dd6 */ { 0x31, 0x06 }, + /* dd7 */ { 0x31, 0x06 }, + /* dd8 */ { 0x31, 0x06 }, + /* dd9 */ { 0x31, 0x06 }, + /* dda */ { 0x31, 0x06 }, + /* ddb */ { 0x31, 0x06 }, + /* ddc */ { 0x31, 0x06 }, + /* ddd */ { 0x31, 0x06 }, + /* dde */ { 0x31, 0x06 }, + /* ddf */ { 0x31, 0x06 }, + /* de0 */ { 0x31, 0x06 }, + /* de1 */ { 0x31, 0x06 }, + /* de2 */ { 0x31, 0x06 }, + /* de3 */ { 0x31, 0x06 }, + /* de4 */ { 0x31, 0x06 }, + /* de5 */ { 0x31, 0x06 }, + /* de6 */ { 0x31, 0x06 }, + /* de7 */ { 0x31, 0x06 }, + /* de8 */ { 0x31, 0x06 }, + /* de9 */ { 0x31, 0x06 }, + /* dea */ { 0x31, 0x06 }, + /* deb */ { 0x31, 0x06 }, + /* dec */ { 0x31, 0x06 }, + /* ded */ { 0x31, 0x06 }, + /* dee */ { 0x31, 0x06 }, + /* def */ { 0x31, 0x06 }, + /* df0 */ { 0x31, 0x06 }, + /* df1 */ { 0x31, 0x06 }, + /* df2 */ { 0x31, 0x06 }, + /* df3 */ { 0x31, 0x06 }, + /* df4 */ { 0x31, 0x06 }, + /* df5 */ { 0x31, 0x06 }, + /* df6 */ { 0x31, 0x06 }, + /* df7 */ { 0x31, 0x06 }, + /* df8 */ { 0x31, 0x06 }, + /* df9 */ { 0x31, 0x06 }, + /* dfa */ { 0x31, 0x06 }, + /* dfb */ { 0x31, 0x06 }, + /* dfc */ { 0x31, 0x06 }, + /* dfd */ { 0x31, 0x06 }, + /* dfe */ { 0x31, 0x06 }, + /* dff */ { 0x31, 0x06 }, + /* e00 */ { 0x41, 0x06 }, + /* e01 */ { 0x41, 0x06 }, + /* e02 */ { 0x41, 0x06 }, + /* e03 */ { 0x41, 0x06 }, + /* e04 */ { 0x41, 0x06 }, + /* e05 */ { 0x41, 0x06 }, + /* e06 */ { 0x41, 0x06 }, + /* e07 */ { 0x41, 0x06 }, + /* e08 */ { 0x41, 0x06 }, + /* e09 */ { 0x41, 0x06 }, + /* e0a */ { 0x41, 0x06 }, + /* e0b */ { 0x41, 0x06 }, + /* e0c */ { 0x41, 0x06 }, + /* e0d */ { 0x41, 0x06 }, + /* e0e */ { 0x41, 0x06 }, + /* e0f */ { 0x41, 0x06 }, + /* e10 */ { 0x41, 0x06 }, + /* e11 */ { 0x41, 0x06 }, + /* e12 */ { 0x41, 0x06 }, + /* e13 */ { 0x41, 0x06 }, + /* e14 */ { 0x41, 0x06 }, + /* e15 */ { 0x41, 0x06 }, + /* e16 */ { 0x41, 0x06 }, + /* e17 */ { 0x41, 0x06 }, + /* e18 */ { 0x41, 0x06 }, + /* e19 */ { 0x41, 0x06 }, + /* e1a */ { 0x41, 0x06 }, + /* e1b */ { 0x41, 0x06 }, + /* e1c */ { 0x41, 0x06 }, + /* e1d */ { 0x41, 0x06 }, + /* e1e */ { 0x41, 0x06 }, + /* e1f */ { 0x41, 0x06 }, + /* e20 */ { 0x41, 0x06 }, + /* e21 */ { 0x41, 0x06 }, + /* e22 */ { 0x41, 0x06 }, + /* e23 */ { 0x41, 0x06 }, + /* e24 */ { 0x41, 0x06 }, + /* e25 */ { 0x41, 0x06 }, + /* e26 */ { 0x41, 0x06 }, + /* e27 */ { 0x41, 0x06 }, + /* e28 */ { 0x41, 0x06 }, + /* e29 */ { 0x41, 0x06 }, + /* e2a */ { 0x41, 0x06 }, + /* e2b */ { 0x41, 0x06 }, + /* e2c */ { 0x41, 0x06 }, + /* e2d */ { 0x41, 0x06 }, + /* e2e */ { 0x41, 0x06 }, + /* e2f */ { 0x41, 0x06 }, + /* e30 */ { 0x41, 0x06 }, + /* e31 */ { 0x41, 0x06 }, + /* e32 */ { 0x41, 0x06 }, + /* e33 */ { 0x41, 0x06 }, + /* e34 */ { 0x41, 0x06 }, + /* e35 */ { 0x41, 0x06 }, + /* e36 */ { 0x41, 0x06 }, + /* e37 */ { 0x41, 0x06 }, + /* e38 */ { 0x41, 0x06 }, + /* e39 */ { 0x41, 0x06 }, + /* e3a */ { 0x41, 0x06 }, + /* e3b */ { 0x41, 0x06 }, + /* e3c */ { 0x41, 0x06 }, + /* e3d */ { 0x41, 0x06 }, + /* e3e */ { 0x41, 0x06 }, + /* e3f */ { 0x41, 0x06 }, + /* e40 */ { 0x51, 0x06 }, + /* e41 */ { 0x51, 0x06 }, + /* e42 */ { 0x51, 0x06 }, + /* e43 */ { 0x51, 0x06 }, + /* e44 */ { 0x51, 0x06 }, + /* e45 */ { 0x51, 0x06 }, + /* e46 */ { 0x51, 0x06 }, + /* e47 */ { 0x51, 0x06 }, + /* e48 */ { 0x51, 0x06 }, + /* e49 */ { 0x51, 0x06 }, + /* e4a */ { 0x51, 0x06 }, + /* e4b */ { 0x51, 0x06 }, + /* e4c */ { 0x51, 0x06 }, + /* e4d */ { 0x51, 0x06 }, + /* e4e */ { 0x51, 0x06 }, + /* e4f */ { 0x51, 0x06 }, + /* e50 */ { 0x51, 0x06 }, + /* e51 */ { 0x51, 0x06 }, + /* e52 */ { 0x51, 0x06 }, + /* e53 */ { 0x51, 0x06 }, + /* e54 */ { 0x51, 0x06 }, + /* e55 */ { 0x51, 0x06 }, + /* e56 */ { 0x51, 0x06 }, + /* e57 */ { 0x51, 0x06 }, + /* e58 */ { 0x51, 0x06 }, + /* e59 */ { 0x51, 0x06 }, + /* e5a */ { 0x51, 0x06 }, + /* e5b */ { 0x51, 0x06 }, + /* e5c */ { 0x51, 0x06 }, + /* e5d */ { 0x51, 0x06 }, + /* e5e */ { 0x51, 0x06 }, + /* e5f */ { 0x51, 0x06 }, + /* e60 */ { 0x51, 0x06 }, + /* e61 */ { 0x51, 0x06 }, + /* e62 */ { 0x51, 0x06 }, + /* e63 */ { 0x51, 0x06 }, + /* e64 */ { 0x51, 0x06 }, + /* e65 */ { 0x51, 0x06 }, + /* e66 */ { 0x51, 0x06 }, + /* e67 */ { 0x51, 0x06 }, + /* e68 */ { 0x51, 0x06 }, + /* e69 */ { 0x51, 0x06 }, + /* e6a */ { 0x51, 0x06 }, + /* e6b */ { 0x51, 0x06 }, + /* e6c */ { 0x51, 0x06 }, + /* e6d */ { 0x51, 0x06 }, + /* e6e */ { 0x51, 0x06 }, + /* e6f */ { 0x51, 0x06 }, + /* e70 */ { 0x51, 0x06 }, + /* e71 */ { 0x51, 0x06 }, + /* e72 */ { 0x51, 0x06 }, + /* e73 */ { 0x51, 0x06 }, + /* e74 */ { 0x51, 0x06 }, + /* e75 */ { 0x51, 0x06 }, + /* e76 */ { 0x51, 0x06 }, + /* e77 */ { 0x51, 0x06 }, + /* e78 */ { 0x51, 0x06 }, + /* e79 */ { 0x51, 0x06 }, + /* e7a */ { 0x51, 0x06 }, + /* e7b */ { 0x51, 0x06 }, + /* e7c */ { 0x51, 0x06 }, + /* e7d */ { 0x51, 0x06 }, + /* e7e */ { 0x51, 0x06 }, + /* e7f */ { 0x51, 0x06 }, + /* e80 */ { 0x13, 0x07 }, + /* e81 */ { 0x13, 0x07 }, + /* e82 */ { 0x13, 0x07 }, + /* e83 */ { 0x13, 0x07 }, + /* e84 */ { 0x13, 0x07 }, + /* e85 */ { 0x13, 0x07 }, + /* e86 */ { 0x13, 0x07 }, + /* e87 */ { 0x13, 0x07 }, + /* e88 */ { 0x13, 0x07 }, + /* e89 */ { 0x13, 0x07 }, + /* e8a */ { 0x13, 0x07 }, + /* e8b */ { 0x13, 0x07 }, + /* e8c */ { 0x13, 0x07 }, + /* e8d */ { 0x13, 0x07 }, + /* e8e */ { 0x13, 0x07 }, + /* e8f */ { 0x13, 0x07 }, + /* e90 */ { 0x13, 0x07 }, + /* e91 */ { 0x13, 0x07 }, + /* e92 */ { 0x13, 0x07 }, + /* e93 */ { 0x13, 0x07 }, + /* e94 */ { 0x13, 0x07 }, + /* e95 */ { 0x13, 0x07 }, + /* e96 */ { 0x13, 0x07 }, + /* e97 */ { 0x13, 0x07 }, + /* e98 */ { 0x13, 0x07 }, + /* e99 */ { 0x13, 0x07 }, + /* e9a */ { 0x13, 0x07 }, + /* e9b */ { 0x13, 0x07 }, + /* e9c */ { 0x13, 0x07 }, + /* e9d */ { 0x13, 0x07 }, + /* e9e */ { 0x13, 0x07 }, + /* e9f */ { 0x13, 0x07 }, + /* ea0 */ { 0x22, 0x07 }, + /* ea1 */ { 0x22, 0x07 }, + /* ea2 */ { 0x22, 0x07 }, + /* ea3 */ { 0x22, 0x07 }, + /* ea4 */ { 0x22, 0x07 }, + /* ea5 */ { 0x22, 0x07 }, + /* ea6 */ { 0x22, 0x07 }, + /* ea7 */ { 0x22, 0x07 }, + /* ea8 */ { 0x22, 0x07 }, + /* ea9 */ { 0x22, 0x07 }, + /* eaa */ { 0x22, 0x07 }, + /* eab */ { 0x22, 0x07 }, + /* eac */ { 0x22, 0x07 }, + /* ead */ { 0x22, 0x07 }, + /* eae */ { 0x22, 0x07 }, + /* eaf */ { 0x22, 0x07 }, + /* eb0 */ { 0x22, 0x07 }, + /* eb1 */ { 0x22, 0x07 }, + /* eb2 */ { 0x22, 0x07 }, + /* eb3 */ { 0x22, 0x07 }, + /* eb4 */ { 0x22, 0x07 }, + /* eb5 */ { 0x22, 0x07 }, + /* eb6 */ { 0x22, 0x07 }, + /* eb7 */ { 0x22, 0x07 }, + /* eb8 */ { 0x22, 0x07 }, + /* eb9 */ { 0x22, 0x07 }, + /* eba */ { 0x22, 0x07 }, + /* ebb */ { 0x22, 0x07 }, + /* ebc */ { 0x22, 0x07 }, + /* ebd */ { 0x22, 0x07 }, + /* ebe */ { 0x22, 0x07 }, + /* ebf */ { 0x22, 0x07 }, + /* ec0 */ { 0x61, 0x07 }, + /* ec1 */ { 0x61, 0x07 }, + /* ec2 */ { 0x61, 0x07 }, + /* ec3 */ { 0x61, 0x07 }, + /* ec4 */ { 0x61, 0x07 }, + /* ec5 */ { 0x61, 0x07 }, + /* ec6 */ { 0x61, 0x07 }, + /* ec7 */ { 0x61, 0x07 }, + /* ec8 */ { 0x61, 0x07 }, + /* ec9 */ { 0x61, 0x07 }, + /* eca */ { 0x61, 0x07 }, + /* ecb */ { 0x61, 0x07 }, + /* ecc */ { 0x61, 0x07 }, + /* ecd */ { 0x61, 0x07 }, + /* ece */ { 0x61, 0x07 }, + /* ecf */ { 0x61, 0x07 }, + /* ed0 */ { 0x61, 0x07 }, + /* ed1 */ { 0x61, 0x07 }, + /* ed2 */ { 0x61, 0x07 }, + /* ed3 */ { 0x61, 0x07 }, + /* ed4 */ { 0x61, 0x07 }, + /* ed5 */ { 0x61, 0x07 }, + /* ed6 */ { 0x61, 0x07 }, + /* ed7 */ { 0x61, 0x07 }, + /* ed8 */ { 0x61, 0x07 }, + /* ed9 */ { 0x61, 0x07 }, + /* eda */ { 0x61, 0x07 }, + /* edb */ { 0x61, 0x07 }, + /* edc */ { 0x61, 0x07 }, + /* edd */ { 0x61, 0x07 }, + /* ede */ { 0x61, 0x07 }, + /* edf */ { 0x61, 0x07 }, + /* ee0 */ { 0x07, 0x09 }, + /* ee1 */ { 0x07, 0x09 }, + /* ee2 */ { 0x07, 0x09 }, + /* ee3 */ { 0x07, 0x09 }, + /* ee4 */ { 0x07, 0x09 }, + /* ee5 */ { 0x07, 0x09 }, + /* ee6 */ { 0x07, 0x09 }, + /* ee7 */ { 0x07, 0x09 }, + /* ee8 */ { 0x71, 0x09 }, + /* ee9 */ { 0x71, 0x09 }, + /* eea */ { 0x71, 0x09 }, + /* eeb */ { 0x71, 0x09 }, + /* eec */ { 0x71, 0x09 }, + /* eed */ { 0x71, 0x09 }, + /* eee */ { 0x71, 0x09 }, + /* eef */ { 0x71, 0x09 }, + /* ef0 */ { 0x09, 0x0b }, + /* ef1 */ { 0x09, 0x0b }, + /* ef2 */ { 0x19, 0x0b }, + /* ef3 */ { 0x19, 0x0b }, + /* ef4 */ { 0x29, 0x0b }, + /* ef5 */ { 0x29, 0x0b }, + /* ef6 */ { 0x39, 0x0b }, + /* ef7 */ { 0x39, 0x0b }, + /* ef8 */ { 0x49, 0x0b }, + /* ef9 */ { 0x49, 0x0b }, + /* efa */ { 0x59, 0x0b }, + /* efb */ { 0x59, 0x0b }, + /* efc */ { 0x69, 0x0b }, + /* efd */ { 0x69, 0x0b }, + /* efe */ { 0x79, 0x0b }, + /* eff */ { 0x79, 0x0b }, + /* f00 */ { 0x89, 0x0b }, + /* f01 */ { 0x89, 0x0b }, + /* f02 */ { 0x99, 0x0b }, + /* f03 */ { 0x99, 0x0b }, + /* f04 */ { 0xa9, 0x0b }, + /* f05 */ { 0xa9, 0x0b }, + /* f06 */ { 0xb9, 0x0b }, + /* f07 */ { 0xb9, 0x0b }, + /* f08 */ { 0xc9, 0x0b }, + /* f09 */ { 0xc9, 0x0b }, + /* f0a */ { 0xd9, 0x0b }, + /* f0b */ { 0xd9, 0x0b }, + /* f0c */ { 0xe9, 0x0b }, + /* f0d */ { 0xe9, 0x0b }, + /* f0e */ { 0xf9, 0x0b }, + /* f0f */ { 0xf9, 0x0b }, + /* f10 */ { 0x08, 0x0c }, + /* f11 */ { 0x14, 0x0c }, + /* f12 */ { 0x15, 0x0c }, + /* f13 */ { 0x16, 0x0c }, + /* f14 */ { 0x17, 0x0c }, + /* f15 */ { 0x18, 0x0c }, + /* f16 */ { 0x23, 0x0c }, + /* f17 */ { 0x24, 0x0c }, + /* f18 */ { 0x25, 0x0c }, + /* f19 */ { 0x26, 0x0c }, + /* f1a */ { 0x27, 0x0c }, + /* f1b */ { 0x28, 0x0c }, + /* f1c */ { 0x32, 0x0c }, + /* f1d */ { 0x33, 0x0c }, + /* f1e */ { 0x34, 0x0c }, + /* f1f */ { 0x35, 0x0c }, + /* f20 */ { 0x36, 0x0c }, + /* f21 */ { 0x37, 0x0c }, + /* f22 */ { 0x38, 0x0c }, + /* f23 */ { 0x42, 0x0c }, + /* f24 */ { 0x43, 0x0c }, + /* f25 */ { 0x44, 0x0c }, + /* f26 */ { 0x45, 0x0c }, + /* f27 */ { 0x46, 0x0c }, + /* f28 */ { 0x47, 0x0c }, + /* f29 */ { 0x48, 0x0c }, + /* f2a */ { 0x52, 0x0c }, + /* f2b */ { 0x53, 0x0c }, + /* f2c */ { 0x54, 0x0c }, + /* f2d */ { 0x55, 0x0c }, + /* f2e */ { 0x56, 0x0c }, + /* f2f */ { 0x57, 0x0c }, + /* f30 */ { 0x58, 0x0c }, + /* f31 */ { 0x62, 0x0c }, + /* f32 */ { 0x63, 0x0c }, + /* f33 */ { 0x64, 0x0c }, + /* f34 */ { 0x65, 0x0c }, + /* f35 */ { 0x66, 0x0c }, + /* f36 */ { 0x67, 0x0c }, + /* f37 */ { 0x68, 0x0c }, + /* f38 */ { 0x72, 0x0c }, + /* f39 */ { 0x73, 0x0c }, + /* f3a */ { 0x74, 0x0c }, + /* f3b */ { 0x75, 0x0c }, + /* f3c */ { 0x76, 0x0c }, + /* f3d */ { 0x77, 0x0c }, + /* f3e */ { 0x78, 0x0c }, + /* f3f */ { 0x81, 0x0c }, + /* f40 */ { 0x82, 0x0c }, + /* f41 */ { 0x83, 0x0c }, + /* f42 */ { 0x84, 0x0c }, + /* f43 */ { 0x85, 0x0c }, + /* f44 */ { 0x86, 0x0c }, + /* f45 */ { 0x87, 0x0c }, + /* f46 */ { 0x88, 0x0c }, + /* f47 */ { 0x91, 0x0c }, + /* f48 */ { 0x92, 0x0c }, + /* f49 */ { 0x93, 0x0c }, + /* f4a */ { 0x94, 0x0c }, + /* f4b */ { 0x95, 0x0c }, + /* f4c */ { 0x96, 0x0c }, + /* f4d */ { 0x97, 0x0c }, + /* f4e */ { 0x98, 0x0c }, + /* f4f */ { 0xa1, 0x0c }, + /* f50 */ { 0xa2, 0x0c }, + /* f51 */ { 0xa3, 0x0c }, + /* f52 */ { 0xa4, 0x0c }, + /* f53 */ { 0xa5, 0x0c }, + /* f54 */ { 0xa6, 0x0c }, + /* f55 */ { 0xa7, 0x0c }, + /* f56 */ { 0xa8, 0x0c }, + /* f57 */ { 0xb1, 0x0c }, + /* f58 */ { 0xb2, 0x0c }, + /* f59 */ { 0xb3, 0x0c }, + /* f5a */ { 0xb4, 0x0c }, + /* f5b */ { 0xb5, 0x0c }, + /* f5c */ { 0xb6, 0x0c }, + /* f5d */ { 0xb7, 0x0c }, + /* f5e */ { 0xb8, 0x0c }, + /* f5f */ { 0xc1, 0x0c }, + /* f60 */ { 0xc2, 0x0c }, + /* f61 */ { 0xc3, 0x0c }, + /* f62 */ { 0xc4, 0x0c }, + /* f63 */ { 0xc5, 0x0c }, + /* f64 */ { 0xc6, 0x0c }, + /* f65 */ { 0xc7, 0x0c }, + /* f66 */ { 0xc8, 0x0c }, + /* f67 */ { 0xd1, 0x0c }, + /* f68 */ { 0xd2, 0x0c }, + /* f69 */ { 0xd3, 0x0c }, + /* f6a */ { 0xd4, 0x0c }, + /* f6b */ { 0xd5, 0x0c }, + /* f6c */ { 0xd6, 0x0c }, + /* f6d */ { 0xd7, 0x0c }, + /* f6e */ { 0xd8, 0x0c }, + /* f6f */ { 0xe1, 0x0c }, + /* f70 */ { 0xe2, 0x0c }, + /* f71 */ { 0xe3, 0x0c }, + /* f72 */ { 0xe4, 0x0c }, + /* f73 */ { 0xe5, 0x0c }, + /* f74 */ { 0xe6, 0x0c }, + /* f75 */ { 0xe7, 0x0c }, + /* f76 */ { 0xe8, 0x0c }, + /* f77 */ { 0xf1, 0x0c }, + /* f78 */ { 0xf2, 0x0c }, + /* f79 */ { 0xf3, 0x0c }, + /* f7a */ { 0xf4, 0x0c }, + /* f7b */ { 0xf5, 0x0c }, + /* f7c */ { 0xf6, 0x0c }, + /* f7d */ { 0xf7, 0x0c }, + /* f7e */ { 0xf8, 0x0c }, + /* f7f */ { 0x10, 0x0c }, + /* f80 */ { 0x00, 0x05 }, + /* f81 */ { 0x00, 0x05 }, + /* f82 */ { 0x00, 0x05 }, + /* f83 */ { 0x00, 0x05 }, + /* f84 */ { 0x00, 0x05 }, + /* f85 */ { 0x00, 0x05 }, + /* f86 */ { 0x00, 0x05 }, + /* f87 */ { 0x00, 0x05 }, + /* f88 */ { 0x00, 0x05 }, + /* f89 */ { 0x00, 0x05 }, + /* f8a */ { 0x00, 0x05 }, + /* f8b */ { 0x00, 0x05 }, + /* f8c */ { 0x00, 0x05 }, + /* f8d */ { 0x00, 0x05 }, + /* f8e */ { 0x00, 0x05 }, + /* f8f */ { 0x00, 0x05 }, + /* f90 */ { 0x00, 0x05 }, + /* f91 */ { 0x00, 0x05 }, + /* f92 */ { 0x00, 0x05 }, + /* f93 */ { 0x00, 0x05 }, + /* f94 */ { 0x00, 0x05 }, + /* f95 */ { 0x00, 0x05 }, + /* f96 */ { 0x00, 0x05 }, + /* f97 */ { 0x00, 0x05 }, + /* f98 */ { 0x00, 0x05 }, + /* f99 */ { 0x00, 0x05 }, + /* f9a */ { 0x00, 0x05 }, + /* f9b */ { 0x00, 0x05 }, + /* f9c */ { 0x00, 0x05 }, + /* f9d */ { 0x00, 0x05 }, + /* f9e */ { 0x00, 0x05 }, + /* f9f */ { 0x00, 0x05 }, + /* fa0 */ { 0x00, 0x05 }, + /* fa1 */ { 0x00, 0x05 }, + /* fa2 */ { 0x00, 0x05 }, + /* fa3 */ { 0x00, 0x05 }, + /* fa4 */ { 0x00, 0x05 }, + /* fa5 */ { 0x00, 0x05 }, + /* fa6 */ { 0x00, 0x05 }, + /* fa7 */ { 0x00, 0x05 }, + /* fa8 */ { 0x00, 0x05 }, + /* fa9 */ { 0x00, 0x05 }, + /* faa */ { 0x00, 0x05 }, + /* fab */ { 0x00, 0x05 }, + /* fac */ { 0x00, 0x05 }, + /* fad */ { 0x00, 0x05 }, + /* fae */ { 0x00, 0x05 }, + /* faf */ { 0x00, 0x05 }, + /* fb0 */ { 0x00, 0x05 }, + /* fb1 */ { 0x00, 0x05 }, + /* fb2 */ { 0x00, 0x05 }, + /* fb3 */ { 0x00, 0x05 }, + /* fb4 */ { 0x00, 0x05 }, + /* fb5 */ { 0x00, 0x05 }, + /* fb6 */ { 0x00, 0x05 }, + /* fb7 */ { 0x00, 0x05 }, + /* fb8 */ { 0x00, 0x05 }, + /* fb9 */ { 0x00, 0x05 }, + /* fba */ { 0x00, 0x05 }, + /* fbb */ { 0x00, 0x05 }, + /* fbc */ { 0x00, 0x05 }, + /* fbd */ { 0x00, 0x05 }, + /* fbe */ { 0x00, 0x05 }, + /* fbf */ { 0x00, 0x05 }, + /* fc0 */ { 0x00, 0x05 }, + /* fc1 */ { 0x00, 0x05 }, + /* fc2 */ { 0x00, 0x05 }, + /* fc3 */ { 0x00, 0x05 }, + /* fc4 */ { 0x00, 0x05 }, + /* fc5 */ { 0x00, 0x05 }, + /* fc6 */ { 0x00, 0x05 }, + /* fc7 */ { 0x00, 0x05 }, + /* fc8 */ { 0x00, 0x05 }, + /* fc9 */ { 0x00, 0x05 }, + /* fca */ { 0x00, 0x05 }, + /* fcb */ { 0x00, 0x05 }, + /* fcc */ { 0x00, 0x05 }, + /* fcd */ { 0x00, 0x05 }, + /* fce */ { 0x00, 0x05 }, + /* fcf */ { 0x00, 0x05 }, + /* fd0 */ { 0x00, 0x05 }, + /* fd1 */ { 0x00, 0x05 }, + /* fd2 */ { 0x00, 0x05 }, + /* fd3 */ { 0x00, 0x05 }, + /* fd4 */ { 0x00, 0x05 }, + /* fd5 */ { 0x00, 0x05 }, + /* fd6 */ { 0x00, 0x05 }, + /* fd7 */ { 0x00, 0x05 }, + /* fd8 */ { 0x00, 0x05 }, + /* fd9 */ { 0x00, 0x05 }, + /* fda */ { 0x00, 0x05 }, + /* fdb */ { 0x00, 0x05 }, + /* fdc */ { 0x00, 0x05 }, + /* fdd */ { 0x00, 0x05 }, + /* fde */ { 0x00, 0x05 }, + /* fdf */ { 0x00, 0x05 }, + /* fe0 */ { 0x00, 0x05 }, + /* fe1 */ { 0x00, 0x05 }, + /* fe2 */ { 0x00, 0x05 }, + /* fe3 */ { 0x00, 0x05 }, + /* fe4 */ { 0x00, 0x05 }, + /* fe5 */ { 0x00, 0x05 }, + /* fe6 */ { 0x00, 0x05 }, + /* fe7 */ { 0x00, 0x05 }, + /* fe8 */ { 0x00, 0x05 }, + /* fe9 */ { 0x00, 0x05 }, + /* fea */ { 0x00, 0x05 }, + /* feb */ { 0x00, 0x05 }, + /* fec */ { 0x00, 0x05 }, + /* fed */ { 0x00, 0x05 }, + /* fee */ { 0x00, 0x05 }, + /* fef */ { 0x00, 0x05 }, + /* ff0 */ { 0x00, 0x05 }, + /* ff1 */ { 0x00, 0x05 }, + /* ff2 */ { 0x00, 0x05 }, + /* ff3 */ { 0x00, 0x05 }, + /* ff4 */ { 0x00, 0x05 }, + /* ff5 */ { 0x00, 0x05 }, + /* ff6 */ { 0x00, 0x05 }, + /* ff7 */ { 0x00, 0x05 }, + /* ff8 */ { 0x00, 0x05 }, + /* ff9 */ { 0x00, 0x05 }, + /* ffa */ { 0x00, 0x05 }, + /* ffb */ { 0x00, 0x05 }, + /* ffc */ { 0x00, 0x05 }, + /* ffd */ { 0x00, 0x05 }, + /* ffe */ { 0x00, 0x05 }, + /* fff */ { 0x00, 0x05 }, diff --git a/waterbox/pcfx/rainbow_dcuv.inc b/waterbox/pcfx/rainbow_dcuv.inc new file mode 100644 index 0000000000..dc9a92477e --- /dev/null +++ b/waterbox/pcfx/rainbow_dcuv.inc @@ -0,0 +1,256 @@ + /* 00 */ { 0x00, 0x02 }, + /* 01 */ { 0x00, 0x02 }, + /* 02 */ { 0x00, 0x02 }, + /* 03 */ { 0x00, 0x02 }, + /* 04 */ { 0x00, 0x02 }, + /* 05 */ { 0x00, 0x02 }, + /* 06 */ { 0x00, 0x02 }, + /* 07 */ { 0x00, 0x02 }, + /* 08 */ { 0x00, 0x02 }, + /* 09 */ { 0x00, 0x02 }, + /* 0a */ { 0x00, 0x02 }, + /* 0b */ { 0x00, 0x02 }, + /* 0c */ { 0x00, 0x02 }, + /* 0d */ { 0x00, 0x02 }, + /* 0e */ { 0x00, 0x02 }, + /* 0f */ { 0x00, 0x02 }, + /* 10 */ { 0x00, 0x02 }, + /* 11 */ { 0x00, 0x02 }, + /* 12 */ { 0x00, 0x02 }, + /* 13 */ { 0x00, 0x02 }, + /* 14 */ { 0x00, 0x02 }, + /* 15 */ { 0x00, 0x02 }, + /* 16 */ { 0x00, 0x02 }, + /* 17 */ { 0x00, 0x02 }, + /* 18 */ { 0x00, 0x02 }, + /* 19 */ { 0x00, 0x02 }, + /* 1a */ { 0x00, 0x02 }, + /* 1b */ { 0x00, 0x02 }, + /* 1c */ { 0x00, 0x02 }, + /* 1d */ { 0x00, 0x02 }, + /* 1e */ { 0x00, 0x02 }, + /* 1f */ { 0x00, 0x02 }, + /* 20 */ { 0x00, 0x02 }, + /* 21 */ { 0x00, 0x02 }, + /* 22 */ { 0x00, 0x02 }, + /* 23 */ { 0x00, 0x02 }, + /* 24 */ { 0x00, 0x02 }, + /* 25 */ { 0x00, 0x02 }, + /* 26 */ { 0x00, 0x02 }, + /* 27 */ { 0x00, 0x02 }, + /* 28 */ { 0x00, 0x02 }, + /* 29 */ { 0x00, 0x02 }, + /* 2a */ { 0x00, 0x02 }, + /* 2b */ { 0x00, 0x02 }, + /* 2c */ { 0x00, 0x02 }, + /* 2d */ { 0x00, 0x02 }, + /* 2e */ { 0x00, 0x02 }, + /* 2f */ { 0x00, 0x02 }, + /* 30 */ { 0x00, 0x02 }, + /* 31 */ { 0x00, 0x02 }, + /* 32 */ { 0x00, 0x02 }, + /* 33 */ { 0x00, 0x02 }, + /* 34 */ { 0x00, 0x02 }, + /* 35 */ { 0x00, 0x02 }, + /* 36 */ { 0x00, 0x02 }, + /* 37 */ { 0x00, 0x02 }, + /* 38 */ { 0x00, 0x02 }, + /* 39 */ { 0x00, 0x02 }, + /* 3a */ { 0x00, 0x02 }, + /* 3b */ { 0x00, 0x02 }, + /* 3c */ { 0x00, 0x02 }, + /* 3d */ { 0x00, 0x02 }, + /* 3e */ { 0x00, 0x02 }, + /* 3f */ { 0x00, 0x02 }, + /* 40 */ { 0x01, 0x02 }, + /* 41 */ { 0x01, 0x02 }, + /* 42 */ { 0x01, 0x02 }, + /* 43 */ { 0x01, 0x02 }, + /* 44 */ { 0x01, 0x02 }, + /* 45 */ { 0x01, 0x02 }, + /* 46 */ { 0x01, 0x02 }, + /* 47 */ { 0x01, 0x02 }, + /* 48 */ { 0x01, 0x02 }, + /* 49 */ { 0x01, 0x02 }, + /* 4a */ { 0x01, 0x02 }, + /* 4b */ { 0x01, 0x02 }, + /* 4c */ { 0x01, 0x02 }, + /* 4d */ { 0x01, 0x02 }, + /* 4e */ { 0x01, 0x02 }, + /* 4f */ { 0x01, 0x02 }, + /* 50 */ { 0x01, 0x02 }, + /* 51 */ { 0x01, 0x02 }, + /* 52 */ { 0x01, 0x02 }, + /* 53 */ { 0x01, 0x02 }, + /* 54 */ { 0x01, 0x02 }, + /* 55 */ { 0x01, 0x02 }, + /* 56 */ { 0x01, 0x02 }, + /* 57 */ { 0x01, 0x02 }, + /* 58 */ { 0x01, 0x02 }, + /* 59 */ { 0x01, 0x02 }, + /* 5a */ { 0x01, 0x02 }, + /* 5b */ { 0x01, 0x02 }, + /* 5c */ { 0x01, 0x02 }, + /* 5d */ { 0x01, 0x02 }, + /* 5e */ { 0x01, 0x02 }, + /* 5f */ { 0x01, 0x02 }, + /* 60 */ { 0x01, 0x02 }, + /* 61 */ { 0x01, 0x02 }, + /* 62 */ { 0x01, 0x02 }, + /* 63 */ { 0x01, 0x02 }, + /* 64 */ { 0x01, 0x02 }, + /* 65 */ { 0x01, 0x02 }, + /* 66 */ { 0x01, 0x02 }, + /* 67 */ { 0x01, 0x02 }, + /* 68 */ { 0x01, 0x02 }, + /* 69 */ { 0x01, 0x02 }, + /* 6a */ { 0x01, 0x02 }, + /* 6b */ { 0x01, 0x02 }, + /* 6c */ { 0x01, 0x02 }, + /* 6d */ { 0x01, 0x02 }, + /* 6e */ { 0x01, 0x02 }, + /* 6f */ { 0x01, 0x02 }, + /* 70 */ { 0x01, 0x02 }, + /* 71 */ { 0x01, 0x02 }, + /* 72 */ { 0x01, 0x02 }, + /* 73 */ { 0x01, 0x02 }, + /* 74 */ { 0x01, 0x02 }, + /* 75 */ { 0x01, 0x02 }, + /* 76 */ { 0x01, 0x02 }, + /* 77 */ { 0x01, 0x02 }, + /* 78 */ { 0x01, 0x02 }, + /* 79 */ { 0x01, 0x02 }, + /* 7a */ { 0x01, 0x02 }, + /* 7b */ { 0x01, 0x02 }, + /* 7c */ { 0x01, 0x02 }, + /* 7d */ { 0x01, 0x02 }, + /* 7e */ { 0x01, 0x02 }, + /* 7f */ { 0x01, 0x02 }, + /* 80 */ { 0x02, 0x02 }, + /* 81 */ { 0x02, 0x02 }, + /* 82 */ { 0x02, 0x02 }, + /* 83 */ { 0x02, 0x02 }, + /* 84 */ { 0x02, 0x02 }, + /* 85 */ { 0x02, 0x02 }, + /* 86 */ { 0x02, 0x02 }, + /* 87 */ { 0x02, 0x02 }, + /* 88 */ { 0x02, 0x02 }, + /* 89 */ { 0x02, 0x02 }, + /* 8a */ { 0x02, 0x02 }, + /* 8b */ { 0x02, 0x02 }, + /* 8c */ { 0x02, 0x02 }, + /* 8d */ { 0x02, 0x02 }, + /* 8e */ { 0x02, 0x02 }, + /* 8f */ { 0x02, 0x02 }, + /* 90 */ { 0x02, 0x02 }, + /* 91 */ { 0x02, 0x02 }, + /* 92 */ { 0x02, 0x02 }, + /* 93 */ { 0x02, 0x02 }, + /* 94 */ { 0x02, 0x02 }, + /* 95 */ { 0x02, 0x02 }, + /* 96 */ { 0x02, 0x02 }, + /* 97 */ { 0x02, 0x02 }, + /* 98 */ { 0x02, 0x02 }, + /* 99 */ { 0x02, 0x02 }, + /* 9a */ { 0x02, 0x02 }, + /* 9b */ { 0x02, 0x02 }, + /* 9c */ { 0x02, 0x02 }, + /* 9d */ { 0x02, 0x02 }, + /* 9e */ { 0x02, 0x02 }, + /* 9f */ { 0x02, 0x02 }, + /* a0 */ { 0x02, 0x02 }, + /* a1 */ { 0x02, 0x02 }, + /* a2 */ { 0x02, 0x02 }, + /* a3 */ { 0x02, 0x02 }, + /* a4 */ { 0x02, 0x02 }, + /* a5 */ { 0x02, 0x02 }, + /* a6 */ { 0x02, 0x02 }, + /* a7 */ { 0x02, 0x02 }, + /* a8 */ { 0x02, 0x02 }, + /* a9 */ { 0x02, 0x02 }, + /* aa */ { 0x02, 0x02 }, + /* ab */ { 0x02, 0x02 }, + /* ac */ { 0x02, 0x02 }, + /* ad */ { 0x02, 0x02 }, + /* ae */ { 0x02, 0x02 }, + /* af */ { 0x02, 0x02 }, + /* b0 */ { 0x02, 0x02 }, + /* b1 */ { 0x02, 0x02 }, + /* b2 */ { 0x02, 0x02 }, + /* b3 */ { 0x02, 0x02 }, + /* b4 */ { 0x02, 0x02 }, + /* b5 */ { 0x02, 0x02 }, + /* b6 */ { 0x02, 0x02 }, + /* b7 */ { 0x02, 0x02 }, + /* b8 */ { 0x02, 0x02 }, + /* b9 */ { 0x02, 0x02 }, + /* ba */ { 0x02, 0x02 }, + /* bb */ { 0x02, 0x02 }, + /* bc */ { 0x02, 0x02 }, + /* bd */ { 0x02, 0x02 }, + /* be */ { 0x02, 0x02 }, + /* bf */ { 0x02, 0x02 }, + /* c0 */ { 0x03, 0x03 }, + /* c1 */ { 0x03, 0x03 }, + /* c2 */ { 0x03, 0x03 }, + /* c3 */ { 0x03, 0x03 }, + /* c4 */ { 0x03, 0x03 }, + /* c5 */ { 0x03, 0x03 }, + /* c6 */ { 0x03, 0x03 }, + /* c7 */ { 0x03, 0x03 }, + /* c8 */ { 0x03, 0x03 }, + /* c9 */ { 0x03, 0x03 }, + /* ca */ { 0x03, 0x03 }, + /* cb */ { 0x03, 0x03 }, + /* cc */ { 0x03, 0x03 }, + /* cd */ { 0x03, 0x03 }, + /* ce */ { 0x03, 0x03 }, + /* cf */ { 0x03, 0x03 }, + /* d0 */ { 0x03, 0x03 }, + /* d1 */ { 0x03, 0x03 }, + /* d2 */ { 0x03, 0x03 }, + /* d3 */ { 0x03, 0x03 }, + /* d4 */ { 0x03, 0x03 }, + /* d5 */ { 0x03, 0x03 }, + /* d6 */ { 0x03, 0x03 }, + /* d7 */ { 0x03, 0x03 }, + /* d8 */ { 0x03, 0x03 }, + /* d9 */ { 0x03, 0x03 }, + /* da */ { 0x03, 0x03 }, + /* db */ { 0x03, 0x03 }, + /* dc */ { 0x03, 0x03 }, + /* dd */ { 0x03, 0x03 }, + /* de */ { 0x03, 0x03 }, + /* df */ { 0x03, 0x03 }, + /* e0 */ { 0x04, 0x04 }, + /* e1 */ { 0x04, 0x04 }, + /* e2 */ { 0x04, 0x04 }, + /* e3 */ { 0x04, 0x04 }, + /* e4 */ { 0x04, 0x04 }, + /* e5 */ { 0x04, 0x04 }, + /* e6 */ { 0x04, 0x04 }, + /* e7 */ { 0x04, 0x04 }, + /* e8 */ { 0x04, 0x04 }, + /* e9 */ { 0x04, 0x04 }, + /* ea */ { 0x04, 0x04 }, + /* eb */ { 0x04, 0x04 }, + /* ec */ { 0x04, 0x04 }, + /* ed */ { 0x04, 0x04 }, + /* ee */ { 0x04, 0x04 }, + /* ef */ { 0x04, 0x04 }, + /* f0 */ { 0x05, 0x05 }, + /* f1 */ { 0x05, 0x05 }, + /* f2 */ { 0x05, 0x05 }, + /* f3 */ { 0x05, 0x05 }, + /* f4 */ { 0x05, 0x05 }, + /* f5 */ { 0x05, 0x05 }, + /* f6 */ { 0x05, 0x05 }, + /* f7 */ { 0x05, 0x05 }, + /* f8 */ { 0x06, 0x06 }, + /* f9 */ { 0x06, 0x06 }, + /* fa */ { 0x06, 0x06 }, + /* fb */ { 0x06, 0x06 }, + /* fc */ { 0x07, 0x07 }, + /* fd */ { 0x07, 0x07 }, + /* fe */ { 0x08, 0x08 }, + /* ff */ { 0x09, 0x08 }, diff --git a/waterbox/pcfx/rainbow_dcy.inc b/waterbox/pcfx/rainbow_dcy.inc new file mode 100644 index 0000000000..8f4c9fc244 --- /dev/null +++ b/waterbox/pcfx/rainbow_dcy.inc @@ -0,0 +1,512 @@ + /* 000 */ { 0x00, 0x03 }, + /* 001 */ { 0x00, 0x03 }, + /* 002 */ { 0x00, 0x03 }, + /* 003 */ { 0x00, 0x03 }, + /* 004 */ { 0x00, 0x03 }, + /* 005 */ { 0x00, 0x03 }, + /* 006 */ { 0x00, 0x03 }, + /* 007 */ { 0x00, 0x03 }, + /* 008 */ { 0x00, 0x03 }, + /* 009 */ { 0x00, 0x03 }, + /* 00a */ { 0x00, 0x03 }, + /* 00b */ { 0x00, 0x03 }, + /* 00c */ { 0x00, 0x03 }, + /* 00d */ { 0x00, 0x03 }, + /* 00e */ { 0x00, 0x03 }, + /* 00f */ { 0x00, 0x03 }, + /* 010 */ { 0x00, 0x03 }, + /* 011 */ { 0x00, 0x03 }, + /* 012 */ { 0x00, 0x03 }, + /* 013 */ { 0x00, 0x03 }, + /* 014 */ { 0x00, 0x03 }, + /* 015 */ { 0x00, 0x03 }, + /* 016 */ { 0x00, 0x03 }, + /* 017 */ { 0x00, 0x03 }, + /* 018 */ { 0x00, 0x03 }, + /* 019 */ { 0x00, 0x03 }, + /* 01a */ { 0x00, 0x03 }, + /* 01b */ { 0x00, 0x03 }, + /* 01c */ { 0x00, 0x03 }, + /* 01d */ { 0x00, 0x03 }, + /* 01e */ { 0x00, 0x03 }, + /* 01f */ { 0x00, 0x03 }, + /* 020 */ { 0x00, 0x03 }, + /* 021 */ { 0x00, 0x03 }, + /* 022 */ { 0x00, 0x03 }, + /* 023 */ { 0x00, 0x03 }, + /* 024 */ { 0x00, 0x03 }, + /* 025 */ { 0x00, 0x03 }, + /* 026 */ { 0x00, 0x03 }, + /* 027 */ { 0x00, 0x03 }, + /* 028 */ { 0x00, 0x03 }, + /* 029 */ { 0x00, 0x03 }, + /* 02a */ { 0x00, 0x03 }, + /* 02b */ { 0x00, 0x03 }, + /* 02c */ { 0x00, 0x03 }, + /* 02d */ { 0x00, 0x03 }, + /* 02e */ { 0x00, 0x03 }, + /* 02f */ { 0x00, 0x03 }, + /* 030 */ { 0x00, 0x03 }, + /* 031 */ { 0x00, 0x03 }, + /* 032 */ { 0x00, 0x03 }, + /* 033 */ { 0x00, 0x03 }, + /* 034 */ { 0x00, 0x03 }, + /* 035 */ { 0x00, 0x03 }, + /* 036 */ { 0x00, 0x03 }, + /* 037 */ { 0x00, 0x03 }, + /* 038 */ { 0x00, 0x03 }, + /* 039 */ { 0x00, 0x03 }, + /* 03a */ { 0x00, 0x03 }, + /* 03b */ { 0x00, 0x03 }, + /* 03c */ { 0x00, 0x03 }, + /* 03d */ { 0x00, 0x03 }, + /* 03e */ { 0x00, 0x03 }, + /* 03f */ { 0x00, 0x03 }, + /* 040 */ { 0x02, 0x03 }, + /* 041 */ { 0x02, 0x03 }, + /* 042 */ { 0x02, 0x03 }, + /* 043 */ { 0x02, 0x03 }, + /* 044 */ { 0x02, 0x03 }, + /* 045 */ { 0x02, 0x03 }, + /* 046 */ { 0x02, 0x03 }, + /* 047 */ { 0x02, 0x03 }, + /* 048 */ { 0x02, 0x03 }, + /* 049 */ { 0x02, 0x03 }, + /* 04a */ { 0x02, 0x03 }, + /* 04b */ { 0x02, 0x03 }, + /* 04c */ { 0x02, 0x03 }, + /* 04d */ { 0x02, 0x03 }, + /* 04e */ { 0x02, 0x03 }, + /* 04f */ { 0x02, 0x03 }, + /* 050 */ { 0x02, 0x03 }, + /* 051 */ { 0x02, 0x03 }, + /* 052 */ { 0x02, 0x03 }, + /* 053 */ { 0x02, 0x03 }, + /* 054 */ { 0x02, 0x03 }, + /* 055 */ { 0x02, 0x03 }, + /* 056 */ { 0x02, 0x03 }, + /* 057 */ { 0x02, 0x03 }, + /* 058 */ { 0x02, 0x03 }, + /* 059 */ { 0x02, 0x03 }, + /* 05a */ { 0x02, 0x03 }, + /* 05b */ { 0x02, 0x03 }, + /* 05c */ { 0x02, 0x03 }, + /* 05d */ { 0x02, 0x03 }, + /* 05e */ { 0x02, 0x03 }, + /* 05f */ { 0x02, 0x03 }, + /* 060 */ { 0x02, 0x03 }, + /* 061 */ { 0x02, 0x03 }, + /* 062 */ { 0x02, 0x03 }, + /* 063 */ { 0x02, 0x03 }, + /* 064 */ { 0x02, 0x03 }, + /* 065 */ { 0x02, 0x03 }, + /* 066 */ { 0x02, 0x03 }, + /* 067 */ { 0x02, 0x03 }, + /* 068 */ { 0x02, 0x03 }, + /* 069 */ { 0x02, 0x03 }, + /* 06a */ { 0x02, 0x03 }, + /* 06b */ { 0x02, 0x03 }, + /* 06c */ { 0x02, 0x03 }, + /* 06d */ { 0x02, 0x03 }, + /* 06e */ { 0x02, 0x03 }, + /* 06f */ { 0x02, 0x03 }, + /* 070 */ { 0x02, 0x03 }, + /* 071 */ { 0x02, 0x03 }, + /* 072 */ { 0x02, 0x03 }, + /* 073 */ { 0x02, 0x03 }, + /* 074 */ { 0x02, 0x03 }, + /* 075 */ { 0x02, 0x03 }, + /* 076 */ { 0x02, 0x03 }, + /* 077 */ { 0x02, 0x03 }, + /* 078 */ { 0x02, 0x03 }, + /* 079 */ { 0x02, 0x03 }, + /* 07a */ { 0x02, 0x03 }, + /* 07b */ { 0x02, 0x03 }, + /* 07c */ { 0x02, 0x03 }, + /* 07d */ { 0x02, 0x03 }, + /* 07e */ { 0x02, 0x03 }, + /* 07f */ { 0x02, 0x03 }, + /* 080 */ { 0x03, 0x03 }, + /* 081 */ { 0x03, 0x03 }, + /* 082 */ { 0x03, 0x03 }, + /* 083 */ { 0x03, 0x03 }, + /* 084 */ { 0x03, 0x03 }, + /* 085 */ { 0x03, 0x03 }, + /* 086 */ { 0x03, 0x03 }, + /* 087 */ { 0x03, 0x03 }, + /* 088 */ { 0x03, 0x03 }, + /* 089 */ { 0x03, 0x03 }, + /* 08a */ { 0x03, 0x03 }, + /* 08b */ { 0x03, 0x03 }, + /* 08c */ { 0x03, 0x03 }, + /* 08d */ { 0x03, 0x03 }, + /* 08e */ { 0x03, 0x03 }, + /* 08f */ { 0x03, 0x03 }, + /* 090 */ { 0x03, 0x03 }, + /* 091 */ { 0x03, 0x03 }, + /* 092 */ { 0x03, 0x03 }, + /* 093 */ { 0x03, 0x03 }, + /* 094 */ { 0x03, 0x03 }, + /* 095 */ { 0x03, 0x03 }, + /* 096 */ { 0x03, 0x03 }, + /* 097 */ { 0x03, 0x03 }, + /* 098 */ { 0x03, 0x03 }, + /* 099 */ { 0x03, 0x03 }, + /* 09a */ { 0x03, 0x03 }, + /* 09b */ { 0x03, 0x03 }, + /* 09c */ { 0x03, 0x03 }, + /* 09d */ { 0x03, 0x03 }, + /* 09e */ { 0x03, 0x03 }, + /* 09f */ { 0x03, 0x03 }, + /* 0a0 */ { 0x03, 0x03 }, + /* 0a1 */ { 0x03, 0x03 }, + /* 0a2 */ { 0x03, 0x03 }, + /* 0a3 */ { 0x03, 0x03 }, + /* 0a4 */ { 0x03, 0x03 }, + /* 0a5 */ { 0x03, 0x03 }, + /* 0a6 */ { 0x03, 0x03 }, + /* 0a7 */ { 0x03, 0x03 }, + /* 0a8 */ { 0x03, 0x03 }, + /* 0a9 */ { 0x03, 0x03 }, + /* 0aa */ { 0x03, 0x03 }, + /* 0ab */ { 0x03, 0x03 }, + /* 0ac */ { 0x03, 0x03 }, + /* 0ad */ { 0x03, 0x03 }, + /* 0ae */ { 0x03, 0x03 }, + /* 0af */ { 0x03, 0x03 }, + /* 0b0 */ { 0x03, 0x03 }, + /* 0b1 */ { 0x03, 0x03 }, + /* 0b2 */ { 0x03, 0x03 }, + /* 0b3 */ { 0x03, 0x03 }, + /* 0b4 */ { 0x03, 0x03 }, + /* 0b5 */ { 0x03, 0x03 }, + /* 0b6 */ { 0x03, 0x03 }, + /* 0b7 */ { 0x03, 0x03 }, + /* 0b8 */ { 0x03, 0x03 }, + /* 0b9 */ { 0x03, 0x03 }, + /* 0ba */ { 0x03, 0x03 }, + /* 0bb */ { 0x03, 0x03 }, + /* 0bc */ { 0x03, 0x03 }, + /* 0bd */ { 0x03, 0x03 }, + /* 0be */ { 0x03, 0x03 }, + /* 0bf */ { 0x03, 0x03 }, + /* 0c0 */ { 0x04, 0x03 }, + /* 0c1 */ { 0x04, 0x03 }, + /* 0c2 */ { 0x04, 0x03 }, + /* 0c3 */ { 0x04, 0x03 }, + /* 0c4 */ { 0x04, 0x03 }, + /* 0c5 */ { 0x04, 0x03 }, + /* 0c6 */ { 0x04, 0x03 }, + /* 0c7 */ { 0x04, 0x03 }, + /* 0c8 */ { 0x04, 0x03 }, + /* 0c9 */ { 0x04, 0x03 }, + /* 0ca */ { 0x04, 0x03 }, + /* 0cb */ { 0x04, 0x03 }, + /* 0cc */ { 0x04, 0x03 }, + /* 0cd */ { 0x04, 0x03 }, + /* 0ce */ { 0x04, 0x03 }, + /* 0cf */ { 0x04, 0x03 }, + /* 0d0 */ { 0x04, 0x03 }, + /* 0d1 */ { 0x04, 0x03 }, + /* 0d2 */ { 0x04, 0x03 }, + /* 0d3 */ { 0x04, 0x03 }, + /* 0d4 */ { 0x04, 0x03 }, + /* 0d5 */ { 0x04, 0x03 }, + /* 0d6 */ { 0x04, 0x03 }, + /* 0d7 */ { 0x04, 0x03 }, + /* 0d8 */ { 0x04, 0x03 }, + /* 0d9 */ { 0x04, 0x03 }, + /* 0da */ { 0x04, 0x03 }, + /* 0db */ { 0x04, 0x03 }, + /* 0dc */ { 0x04, 0x03 }, + /* 0dd */ { 0x04, 0x03 }, + /* 0de */ { 0x04, 0x03 }, + /* 0df */ { 0x04, 0x03 }, + /* 0e0 */ { 0x04, 0x03 }, + /* 0e1 */ { 0x04, 0x03 }, + /* 0e2 */ { 0x04, 0x03 }, + /* 0e3 */ { 0x04, 0x03 }, + /* 0e4 */ { 0x04, 0x03 }, + /* 0e5 */ { 0x04, 0x03 }, + /* 0e6 */ { 0x04, 0x03 }, + /* 0e7 */ { 0x04, 0x03 }, + /* 0e8 */ { 0x04, 0x03 }, + /* 0e9 */ { 0x04, 0x03 }, + /* 0ea */ { 0x04, 0x03 }, + /* 0eb */ { 0x04, 0x03 }, + /* 0ec */ { 0x04, 0x03 }, + /* 0ed */ { 0x04, 0x03 }, + /* 0ee */ { 0x04, 0x03 }, + /* 0ef */ { 0x04, 0x03 }, + /* 0f0 */ { 0x04, 0x03 }, + /* 0f1 */ { 0x04, 0x03 }, + /* 0f2 */ { 0x04, 0x03 }, + /* 0f3 */ { 0x04, 0x03 }, + /* 0f4 */ { 0x04, 0x03 }, + /* 0f5 */ { 0x04, 0x03 }, + /* 0f6 */ { 0x04, 0x03 }, + /* 0f7 */ { 0x04, 0x03 }, + /* 0f8 */ { 0x04, 0x03 }, + /* 0f9 */ { 0x04, 0x03 }, + /* 0fa */ { 0x04, 0x03 }, + /* 0fb */ { 0x04, 0x03 }, + /* 0fc */ { 0x04, 0x03 }, + /* 0fd */ { 0x04, 0x03 }, + /* 0fe */ { 0x04, 0x03 }, + /* 0ff */ { 0x04, 0x03 }, + /* 100 */ { 0x05, 0x03 }, + /* 101 */ { 0x05, 0x03 }, + /* 102 */ { 0x05, 0x03 }, + /* 103 */ { 0x05, 0x03 }, + /* 104 */ { 0x05, 0x03 }, + /* 105 */ { 0x05, 0x03 }, + /* 106 */ { 0x05, 0x03 }, + /* 107 */ { 0x05, 0x03 }, + /* 108 */ { 0x05, 0x03 }, + /* 109 */ { 0x05, 0x03 }, + /* 10a */ { 0x05, 0x03 }, + /* 10b */ { 0x05, 0x03 }, + /* 10c */ { 0x05, 0x03 }, + /* 10d */ { 0x05, 0x03 }, + /* 10e */ { 0x05, 0x03 }, + /* 10f */ { 0x05, 0x03 }, + /* 110 */ { 0x05, 0x03 }, + /* 111 */ { 0x05, 0x03 }, + /* 112 */ { 0x05, 0x03 }, + /* 113 */ { 0x05, 0x03 }, + /* 114 */ { 0x05, 0x03 }, + /* 115 */ { 0x05, 0x03 }, + /* 116 */ { 0x05, 0x03 }, + /* 117 */ { 0x05, 0x03 }, + /* 118 */ { 0x05, 0x03 }, + /* 119 */ { 0x05, 0x03 }, + /* 11a */ { 0x05, 0x03 }, + /* 11b */ { 0x05, 0x03 }, + /* 11c */ { 0x05, 0x03 }, + /* 11d */ { 0x05, 0x03 }, + /* 11e */ { 0x05, 0x03 }, + /* 11f */ { 0x05, 0x03 }, + /* 120 */ { 0x05, 0x03 }, + /* 121 */ { 0x05, 0x03 }, + /* 122 */ { 0x05, 0x03 }, + /* 123 */ { 0x05, 0x03 }, + /* 124 */ { 0x05, 0x03 }, + /* 125 */ { 0x05, 0x03 }, + /* 126 */ { 0x05, 0x03 }, + /* 127 */ { 0x05, 0x03 }, + /* 128 */ { 0x05, 0x03 }, + /* 129 */ { 0x05, 0x03 }, + /* 12a */ { 0x05, 0x03 }, + /* 12b */ { 0x05, 0x03 }, + /* 12c */ { 0x05, 0x03 }, + /* 12d */ { 0x05, 0x03 }, + /* 12e */ { 0x05, 0x03 }, + /* 12f */ { 0x05, 0x03 }, + /* 130 */ { 0x05, 0x03 }, + /* 131 */ { 0x05, 0x03 }, + /* 132 */ { 0x05, 0x03 }, + /* 133 */ { 0x05, 0x03 }, + /* 134 */ { 0x05, 0x03 }, + /* 135 */ { 0x05, 0x03 }, + /* 136 */ { 0x05, 0x03 }, + /* 137 */ { 0x05, 0x03 }, + /* 138 */ { 0x05, 0x03 }, + /* 139 */ { 0x05, 0x03 }, + /* 13a */ { 0x05, 0x03 }, + /* 13b */ { 0x05, 0x03 }, + /* 13c */ { 0x05, 0x03 }, + /* 13d */ { 0x05, 0x03 }, + /* 13e */ { 0x05, 0x03 }, + /* 13f */ { 0x05, 0x03 }, + /* 140 */ { 0x06, 0x03 }, + /* 141 */ { 0x06, 0x03 }, + /* 142 */ { 0x06, 0x03 }, + /* 143 */ { 0x06, 0x03 }, + /* 144 */ { 0x06, 0x03 }, + /* 145 */ { 0x06, 0x03 }, + /* 146 */ { 0x06, 0x03 }, + /* 147 */ { 0x06, 0x03 }, + /* 148 */ { 0x06, 0x03 }, + /* 149 */ { 0x06, 0x03 }, + /* 14a */ { 0x06, 0x03 }, + /* 14b */ { 0x06, 0x03 }, + /* 14c */ { 0x06, 0x03 }, + /* 14d */ { 0x06, 0x03 }, + /* 14e */ { 0x06, 0x03 }, + /* 14f */ { 0x06, 0x03 }, + /* 150 */ { 0x06, 0x03 }, + /* 151 */ { 0x06, 0x03 }, + /* 152 */ { 0x06, 0x03 }, + /* 153 */ { 0x06, 0x03 }, + /* 154 */ { 0x06, 0x03 }, + /* 155 */ { 0x06, 0x03 }, + /* 156 */ { 0x06, 0x03 }, + /* 157 */ { 0x06, 0x03 }, + /* 158 */ { 0x06, 0x03 }, + /* 159 */ { 0x06, 0x03 }, + /* 15a */ { 0x06, 0x03 }, + /* 15b */ { 0x06, 0x03 }, + /* 15c */ { 0x06, 0x03 }, + /* 15d */ { 0x06, 0x03 }, + /* 15e */ { 0x06, 0x03 }, + /* 15f */ { 0x06, 0x03 }, + /* 160 */ { 0x06, 0x03 }, + /* 161 */ { 0x06, 0x03 }, + /* 162 */ { 0x06, 0x03 }, + /* 163 */ { 0x06, 0x03 }, + /* 164 */ { 0x06, 0x03 }, + /* 165 */ { 0x06, 0x03 }, + /* 166 */ { 0x06, 0x03 }, + /* 167 */ { 0x06, 0x03 }, + /* 168 */ { 0x06, 0x03 }, + /* 169 */ { 0x06, 0x03 }, + /* 16a */ { 0x06, 0x03 }, + /* 16b */ { 0x06, 0x03 }, + /* 16c */ { 0x06, 0x03 }, + /* 16d */ { 0x06, 0x03 }, + /* 16e */ { 0x06, 0x03 }, + /* 16f */ { 0x06, 0x03 }, + /* 170 */ { 0x06, 0x03 }, + /* 171 */ { 0x06, 0x03 }, + /* 172 */ { 0x06, 0x03 }, + /* 173 */ { 0x06, 0x03 }, + /* 174 */ { 0x06, 0x03 }, + /* 175 */ { 0x06, 0x03 }, + /* 176 */ { 0x06, 0x03 }, + /* 177 */ { 0x06, 0x03 }, + /* 178 */ { 0x06, 0x03 }, + /* 179 */ { 0x06, 0x03 }, + /* 17a */ { 0x06, 0x03 }, + /* 17b */ { 0x06, 0x03 }, + /* 17c */ { 0x06, 0x03 }, + /* 17d */ { 0x06, 0x03 }, + /* 17e */ { 0x06, 0x03 }, + /* 17f */ { 0x06, 0x03 }, + /* 180 */ { 0x07, 0x03 }, + /* 181 */ { 0x07, 0x03 }, + /* 182 */ { 0x07, 0x03 }, + /* 183 */ { 0x07, 0x03 }, + /* 184 */ { 0x07, 0x03 }, + /* 185 */ { 0x07, 0x03 }, + /* 186 */ { 0x07, 0x03 }, + /* 187 */ { 0x07, 0x03 }, + /* 188 */ { 0x07, 0x03 }, + /* 189 */ { 0x07, 0x03 }, + /* 18a */ { 0x07, 0x03 }, + /* 18b */ { 0x07, 0x03 }, + /* 18c */ { 0x07, 0x03 }, + /* 18d */ { 0x07, 0x03 }, + /* 18e */ { 0x07, 0x03 }, + /* 18f */ { 0x07, 0x03 }, + /* 190 */ { 0x07, 0x03 }, + /* 191 */ { 0x07, 0x03 }, + /* 192 */ { 0x07, 0x03 }, + /* 193 */ { 0x07, 0x03 }, + /* 194 */ { 0x07, 0x03 }, + /* 195 */ { 0x07, 0x03 }, + /* 196 */ { 0x07, 0x03 }, + /* 197 */ { 0x07, 0x03 }, + /* 198 */ { 0x07, 0x03 }, + /* 199 */ { 0x07, 0x03 }, + /* 19a */ { 0x07, 0x03 }, + /* 19b */ { 0x07, 0x03 }, + /* 19c */ { 0x07, 0x03 }, + /* 19d */ { 0x07, 0x03 }, + /* 19e */ { 0x07, 0x03 }, + /* 19f */ { 0x07, 0x03 }, + /* 1a0 */ { 0x07, 0x03 }, + /* 1a1 */ { 0x07, 0x03 }, + /* 1a2 */ { 0x07, 0x03 }, + /* 1a3 */ { 0x07, 0x03 }, + /* 1a4 */ { 0x07, 0x03 }, + /* 1a5 */ { 0x07, 0x03 }, + /* 1a6 */ { 0x07, 0x03 }, + /* 1a7 */ { 0x07, 0x03 }, + /* 1a8 */ { 0x07, 0x03 }, + /* 1a9 */ { 0x07, 0x03 }, + /* 1aa */ { 0x07, 0x03 }, + /* 1ab */ { 0x07, 0x03 }, + /* 1ac */ { 0x07, 0x03 }, + /* 1ad */ { 0x07, 0x03 }, + /* 1ae */ { 0x07, 0x03 }, + /* 1af */ { 0x07, 0x03 }, + /* 1b0 */ { 0x07, 0x03 }, + /* 1b1 */ { 0x07, 0x03 }, + /* 1b2 */ { 0x07, 0x03 }, + /* 1b3 */ { 0x07, 0x03 }, + /* 1b4 */ { 0x07, 0x03 }, + /* 1b5 */ { 0x07, 0x03 }, + /* 1b6 */ { 0x07, 0x03 }, + /* 1b7 */ { 0x07, 0x03 }, + /* 1b8 */ { 0x07, 0x03 }, + /* 1b9 */ { 0x07, 0x03 }, + /* 1ba */ { 0x07, 0x03 }, + /* 1bb */ { 0x07, 0x03 }, + /* 1bc */ { 0x07, 0x03 }, + /* 1bd */ { 0x07, 0x03 }, + /* 1be */ { 0x07, 0x03 }, + /* 1bf */ { 0x07, 0x03 }, + /* 1c0 */ { 0x01, 0x04 }, + /* 1c1 */ { 0x01, 0x04 }, + /* 1c2 */ { 0x01, 0x04 }, + /* 1c3 */ { 0x01, 0x04 }, + /* 1c4 */ { 0x01, 0x04 }, + /* 1c5 */ { 0x01, 0x04 }, + /* 1c6 */ { 0x01, 0x04 }, + /* 1c7 */ { 0x01, 0x04 }, + /* 1c8 */ { 0x01, 0x04 }, + /* 1c9 */ { 0x01, 0x04 }, + /* 1ca */ { 0x01, 0x04 }, + /* 1cb */ { 0x01, 0x04 }, + /* 1cc */ { 0x01, 0x04 }, + /* 1cd */ { 0x01, 0x04 }, + /* 1ce */ { 0x01, 0x04 }, + /* 1cf */ { 0x01, 0x04 }, + /* 1d0 */ { 0x01, 0x04 }, + /* 1d1 */ { 0x01, 0x04 }, + /* 1d2 */ { 0x01, 0x04 }, + /* 1d3 */ { 0x01, 0x04 }, + /* 1d4 */ { 0x01, 0x04 }, + /* 1d5 */ { 0x01, 0x04 }, + /* 1d6 */ { 0x01, 0x04 }, + /* 1d7 */ { 0x01, 0x04 }, + /* 1d8 */ { 0x01, 0x04 }, + /* 1d9 */ { 0x01, 0x04 }, + /* 1da */ { 0x01, 0x04 }, + /* 1db */ { 0x01, 0x04 }, + /* 1dc */ { 0x01, 0x04 }, + /* 1dd */ { 0x01, 0x04 }, + /* 1de */ { 0x01, 0x04 }, + /* 1df */ { 0x01, 0x04 }, + /* 1e0 */ { 0x08, 0x06 }, + /* 1e1 */ { 0x08, 0x06 }, + /* 1e2 */ { 0x08, 0x06 }, + /* 1e3 */ { 0x08, 0x06 }, + /* 1e4 */ { 0x08, 0x06 }, + /* 1e5 */ { 0x08, 0x06 }, + /* 1e6 */ { 0x08, 0x06 }, + /* 1e7 */ { 0x08, 0x06 }, + /* 1e8 */ { 0x09, 0x07 }, + /* 1e9 */ { 0x09, 0x07 }, + /* 1ea */ { 0x09, 0x07 }, + /* 1eb */ { 0x09, 0x07 }, + /* 1ec */ { 0x0f, 0x07 }, + /* 1ed */ { 0x0f, 0x07 }, + /* 1ee */ { 0x0f, 0x07 }, + /* 1ef */ { 0x0f, 0x07 }, + /* 1f0 */ { 0x10, 0x09 }, + /* 1f1 */ { 0x11, 0x09 }, + /* 1f2 */ { 0x12, 0x09 }, + /* 1f3 */ { 0x13, 0x09 }, + /* 1f4 */ { 0x14, 0x09 }, + /* 1f5 */ { 0x15, 0x09 }, + /* 1f6 */ { 0x16, 0x09 }, + /* 1f7 */ { 0x17, 0x09 }, + /* 1f8 */ { 0x18, 0x09 }, + /* 1f9 */ { 0x19, 0x09 }, + /* 1fa */ { 0x1a, 0x09 }, + /* 1fb */ { 0x1b, 0x09 }, + /* 1fc */ { 0x1c, 0x09 }, + /* 1fd */ { 0x1d, 0x09 }, + /* 1fe */ { 0x1e, 0x09 }, + /* 1ff */ { 0x1f, 0x09 }, diff --git a/waterbox/pcfx/sound/Blip_Buffer.cpp b/waterbox/pcfx/sound/Blip_Buffer.cpp new file mode 100644 index 0000000000..f04a1fc599 --- /dev/null +++ b/waterbox/pcfx/sound/Blip_Buffer.cpp @@ -0,0 +1,457 @@ +// Blip_Buffer 0.4.1. http://www.slack.net/~ant/ + +#include "Blip_Buffer.h" + +#include +#include +#include +#include +#include +#include + +/* Copyright (C) 2003-2006 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +int const silent_buf_size = 1; // size used for Silent_Blip_Buffer + +Blip_Buffer::Blip_Buffer() +{ + factor_ = (blip_u64)ULLONG_MAX; + offset_ = 0; + buffer_ = 0; + buffer_size_ = 0; + sample_rate_ = 0; + reader_accum_ = 0; + bass_shift_ = 0; + clock_rate_ = 0; + bass_freq_ = 16; + length_ = 0; + + // assumptions code makes about implementation-defined features + #ifndef NDEBUG + // right shift of negative value preserves sign + buf_t_ i = -0x7FFFFFFE; + assert( (i >> 1) == -0x3FFFFFFF ); + + // casting to short truncates to 16 bits and sign-extends + i = 0x18000; + assert( (short) i == -0x8000 ); + #endif +} + +Blip_Buffer::~Blip_Buffer() +{ + if ( buffer_size_ != silent_buf_size ) + free( buffer_ ); +} + +Silent_Blip_Buffer::Silent_Blip_Buffer() +{ + factor_ = 0; + buffer_ = buf; + buffer_size_ = silent_buf_size; + memset( buf, 0, sizeof buf ); // in case machine takes exception for signed overflow +} + +void Blip_Buffer::clear( int entire_buffer ) +{ + offset_ = 0; + reader_accum_ = 0; + modified_ = 0; + if ( buffer_ ) + { + long count = (entire_buffer ? buffer_size_ : samples_avail()); + memset( buffer_, 0, (count + blip_buffer_extra_) * sizeof (buf_t_) ); + } +} + +Blip_Buffer::blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec ) +{ + if ( buffer_size_ == silent_buf_size ) + { + assert( 0 ); + return "Internal (tried to resize Silent_Blip_Buffer)"; + } + + // start with maximum length that resampled time can represent + blip_s64 new_size = (ULLONG_MAX >> BLIP_BUFFER_ACCURACY) - blip_buffer_extra_ - 64; + + // simple safety check, since code elsewhere may not be safe for sizes approaching (2 ^ 31). + if(new_size > ((1LL << 30) - 1)) + new_size = (1LL << 30) - 1; + + if ( msec != blip_max_length ) + { + blip_s64 s = ((blip_s64)new_rate * (msec + 1) + 999) / 1000; + if ( s < new_size ) + new_size = s; + else + assert( 0 ); // fails if requested buffer length exceeds limit + } + + if ( buffer_size_ != new_size ) + { + void* p = realloc( buffer_, (new_size + blip_buffer_extra_) * sizeof *buffer_ ); + if ( !p ) + return "Out of memory"; + + //if(new_size > buffer_size_) + // memset(buffer_ + buffer_size_, 0, (new_size + blip_buffer_extra_) * sizeof *buffer_ + + buffer_ = (buf_t_*) p; + } + + buffer_size_ = new_size; + assert( buffer_size_ != silent_buf_size ); + + // update things based on the sample rate + sample_rate_ = new_rate; + length_ = new_size * 1000 / new_rate - 1; + if ( msec ) + assert( length_ == msec ); // ensure length is same as that passed in + if ( clock_rate_ ) + clock_rate( clock_rate_ ); + bass_freq( bass_freq_ ); + + clear(); + + return 0; // success +} + +blip_resampled_time_t Blip_Buffer::clock_rate_factor( long rate ) const +{ + double ratio = (double) sample_rate_ / rate; + blip_s64 factor = (blip_s64) floor( ratio * (1LL << BLIP_BUFFER_ACCURACY) + 0.5 ); + assert( factor > 0 || !sample_rate_ ); // fails if clock/output ratio is too large + return (blip_resampled_time_t) factor; +} + +void Blip_Buffer::bass_freq( int freq ) +{ + bass_freq_ = freq; + int shift = 31; + if ( freq > 0 ) + { + shift = 13; + long f = (freq << 16) / sample_rate_; + while ( (f >>= 1) && --shift ) { } + } + bass_shift_ = shift; + //printf("%d\n", bass_shift_); +} + +void Blip_Buffer::end_frame( blip_time_t t ) +{ + offset_ += t * factor_; + assert( samples_avail() <= (long) buffer_size_ ); // time outside buffer length +} + +void Blip_Buffer::remove_silence( long count ) +{ + assert( count <= samples_avail() ); // tried to remove more samples than available + offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; +} + +long Blip_Buffer::count_samples( blip_time_t t ) const +{ + unsigned long last_sample = resampled_time( t ) >> BLIP_BUFFER_ACCURACY; + unsigned long first_sample = offset_ >> BLIP_BUFFER_ACCURACY; + return (long) (last_sample - first_sample); +} + +blip_time_t Blip_Buffer::count_clocks( long count ) const +{ + if ( !factor_ ) + { + assert( 0 ); // sample rate and clock rates must be set first + return 0; + } + + if ( count > buffer_size_ ) + count = buffer_size_; + blip_resampled_time_t time = (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; + return (blip_time_t) ((time - offset_ + factor_ - 1) / factor_); +} + +void Blip_Buffer::remove_samples( long count ) +{ + if ( count ) + { + remove_silence( count ); + + // copy remaining samples to beginning and clear old samples + long remain = samples_avail() + blip_buffer_extra_; + memmove( buffer_, buffer_ + count, remain * sizeof *buffer_ ); + memset( buffer_ + remain, 0, count * sizeof *buffer_ ); + } +} + +// Blip_Synth_ + +Blip_Synth_Fast_::Blip_Synth_Fast_() +{ + buf = 0; + last_amp = 0; + delta_factor = 0; +} + +void Blip_Synth_Fast_::volume_unit( double new_unit ) +{ + delta_factor = int (new_unit * (1L << blip_sample_bits) + 0.5); +} + +#if !BLIP_BUFFER_FAST + +Blip_Synth_::Blip_Synth_( short* p, int w ) : + impulses( p ), + width( w ) +{ + volume_unit_ = 0.0; + kernel_unit = 0; + buf = 0; + last_amp = 0; + delta_factor = 0; +} + +#undef PI +#define PI 3.1415926535897932384626433832795029 + +static void gen_sinc( float* out, int count, double oversample, double treble, double cutoff ) +{ + if ( cutoff >= 0.999 ) + cutoff = 0.999; + + if ( treble < -300.0 ) + treble = -300.0; + if ( treble > 5.0 ) + treble = 5.0; + + double const maxh = 4096.0; + double const rolloff = pow( 10.0, 1.0 / (maxh * 20.0) * treble / (1.0 - cutoff) ); + double const pow_a_n = pow( rolloff, maxh - maxh * cutoff ); + double const to_angle = PI / 2 / maxh / oversample; + for ( int i = 0; i < count; i++ ) + { + double angle = ((i - count) * 2 + 1) * to_angle; + double c = rolloff * cos( (maxh - 1.0) * angle ) - cos( maxh * angle ); + double cos_nc_angle = cos( maxh * cutoff * angle ); + double cos_nc1_angle = cos( (maxh * cutoff - 1.0) * angle ); + double cos_angle = cos( angle ); + + c = c * pow_a_n - rolloff * cos_nc1_angle + cos_nc_angle; + double d = 1.0 + rolloff * (rolloff - cos_angle - cos_angle); + double b = 2.0 - cos_angle - cos_angle; + double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle; + + out [i] = (float) ((a * d + c * b) / (b * d)); // a / b + c / d + } +} + +void blip_eq_t::generate( float* out, int count ) const +{ + // lower cutoff freq for narrow kernels with their wider transition band + // (8 points->1.49, 16 points->1.15) + double oversample = blip_res * 2.25 / count + 0.85; + double half_rate = sample_rate * 0.5; + if ( cutoff_freq ) + oversample = half_rate / cutoff_freq; + double cutoff = rolloff_freq * oversample / half_rate; + + gen_sinc( out, count, blip_res * oversample, treble, cutoff ); + + // apply (half of) hamming window + double to_fraction = PI / (count - 1); + for ( int i = count; i--; ) + out [i] *= 0.54f - 0.46f * (float) cos( i * to_fraction ); +} + +void Blip_Synth_::adjust_impulse() +{ + // sum pairs for each phase and add error correction to end of first half + int const size = impulses_size(); + for ( int p = blip_res; p-- >= blip_res / 2; ) + { + int p2 = blip_res - 2 - p; + long error = kernel_unit; + for ( int i = 1; i < size; i += blip_res ) + { + error -= impulses [i + p ]; + error -= impulses [i + p2]; + } + if ( p == p2 ) + error /= 2; // phase = 0.5 impulse uses same half for both sides + impulses [size - blip_res + p] += (short) error; + //printf( "error: %ld\n", error ); + } + + //for ( int i = blip_res; i--; printf( "\n" ) ) + // for ( int j = 0; j < width / 2; j++ ) + // printf( "%5ld,", impulses [j * blip_res + i + 1] ); +} + +void Blip_Synth_::treble_eq( blip_eq_t const& eq ) +{ + float fimpulse [blip_res / 2 * (blip_widest_impulse_ - 1) + blip_res * 2]; + + int const half_size = blip_res / 2 * (width - 1); + eq.generate( &fimpulse [blip_res], half_size ); + + int i; + + // need mirror slightly past center for calculation + for ( i = blip_res; i--; ) + fimpulse [blip_res + half_size + i] = fimpulse [blip_res + half_size - 1 - i]; + + // starts at 0 + for ( i = 0; i < blip_res; i++ ) + fimpulse [i] = 0.0f; + + // find rescale factor + double total = 0.0; + for ( i = 0; i < half_size; i++ ) + total += fimpulse [blip_res + i]; + + //double const base_unit = 44800.0 - 128 * 18; // allows treble up to +0 dB + //double const base_unit = 37888.0; // allows treble to +5 dB + double const base_unit = 32768.0; // necessary for blip_unscaled to work + double rescale = base_unit / 2 / total; + kernel_unit = (long) base_unit; + + // integrate, first difference, rescale, convert to int + double sum = 0.0; + double next = 0.0; + int const impulses_size_local = this->impulses_size(); + for ( i = 0; i < impulses_size_local; i++ ) + { + impulses [i] = (short) floor( (next - sum) * rescale + 0.5 ); + sum += fimpulse [i]; + next += fimpulse [i + blip_res]; + } + adjust_impulse(); + + // volume might require rescaling + double vol = volume_unit_; + if ( vol ) + { + volume_unit_ = 0.0; + volume_unit( vol ); + } +} + +void Blip_Synth_::volume_unit( double new_unit ) +{ + if ( new_unit != volume_unit_ ) + { + // use default eq if it hasn't been set yet + if ( !kernel_unit ) + treble_eq( -8.0 ); + + volume_unit_ = new_unit; + double factor = new_unit * (1L << blip_sample_bits) / kernel_unit; + + if ( factor > 0.0 ) + { + int shift = 0; + + // if unit is really small, might need to attenuate kernel + while ( factor < 2.0 ) + { + shift++; + factor *= 2.0; + } + + if ( shift ) + { + kernel_unit >>= shift; + assert( kernel_unit > 0 ); // fails if volume unit is too low + + // keep values positive to avoid round-towards-zero of sign-preserving + // right shift for negative values + long offset = 0x8000 + (1 << (shift - 1)); + long offset2 = 0x8000 >> shift; + for ( int i = impulses_size(); i--; ) + impulses [i] = (short) (((impulses [i] + offset) >> shift) - offset2); + adjust_impulse(); + } + } + delta_factor = (int) floor( factor + 0.5 ); + //printf( "delta_factor: %d, kernel_unit: %d\n", delta_factor, kernel_unit ); + } +} +#endif + +long Blip_Buffer::read_samples( blip_sample_t* BLIP_RESTRICT out, long max_samples, int stereo ) +{ + long count = samples_avail(); + if ( count > max_samples ) + count = max_samples; + + if ( count ) + { + int const bass = BLIP_READER_BASS( *this ); + BLIP_READER_BEGIN( reader, *this ); + + if ( !stereo ) + { + for ( blip_long n = count; n; --n ) + { + blip_long s = BLIP_READER_READ( reader ); + if ( (blip_sample_t) s != s ) + s = 0x7FFF - (s >> 24); + *out++ = (blip_sample_t) s; + BLIP_READER_NEXT( reader, bass ); + } + } + else + { + for ( blip_long n = count; n; --n ) + { + blip_long s = BLIP_READER_READ( reader ); + if ( (blip_sample_t) s != s ) + s = 0x7FFF - (s >> 24); + *out = (blip_sample_t) s; + out += 2; + BLIP_READER_NEXT( reader, bass ); + } + } + BLIP_READER_END( reader, *this ); + + remove_samples( count ); + } + return count; +} + +void Blip_Buffer::mix_samples( blip_sample_t const* in, long count ) +{ + if ( buffer_size_ == silent_buf_size ) + { + assert( 0 ); + return; + } + + buf_t_* out = buffer_ + (offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2; + + int const sample_shift = blip_sample_bits - 16; + int prev = 0; + while ( count-- ) + { + blip_long s = (blip_long) *in++ << sample_shift; + *out += s - prev; + prev = s; + ++out; + } + *out -= prev; +} + diff --git a/waterbox/pcfx/sound/Blip_Buffer.h b/waterbox/pcfx/sound/Blip_Buffer.h new file mode 100644 index 0000000000..a8e90ee053 --- /dev/null +++ b/waterbox/pcfx/sound/Blip_Buffer.h @@ -0,0 +1,498 @@ +// Band-limited sound synthesis buffer +// Various changes and hacks for use in Mednafen. + +#ifdef __GNUC__ + #define blip_inline inline __attribute__((always_inline)) +#else + #define blip_inline inline +#endif + +#include +#include + +// Blip_Buffer 0.4.1 +#ifndef BLIP_BUFFER_H +#define BLIP_BUFFER_H + +// Internal +typedef int32_t blip_long; +typedef uint32_t blip_ulong; +typedef int64_t blip_s64; +typedef uint64_t blip_u64; + +// Time unit at source clock rate +typedef blip_long blip_time_t; + +// Output samples are 16-bit signed, with a range of -32768 to 32767 +typedef short blip_sample_t; +enum { blip_sample_max = 32767 }; + +class Blip_Buffer { +public: + typedef const char* blargg_err_t; + + // Set output sample rate and buffer length in milliseconds (1/1000 sec, defaults + // to 1/4 second), then clear buffer. Returns NULL on success, otherwise if there + // isn't enough memory, returns error without affecting current buffer setup. + blargg_err_t set_sample_rate( long samples_per_sec, int msec_length = 1000 / 4 ); + + // Set number of source time units per second + void clock_rate( long ); + + // End current time frame of specified duration and make its samples available + // (along with any still-unread samples) for reading with read_samples(). Begins + // a new time frame at the end of the current frame. + void end_frame( blip_time_t time ); + + // Read at most 'max_samples' out of buffer into 'dest', removing them from from + // the buffer. Returns number of samples actually read and removed. If stereo is + // true, increments 'dest' one extra time after writing each sample, to allow + // easy interleving of two channels into a stereo output buffer. + long read_samples( blip_sample_t* dest, long max_samples, int stereo = 0 ); + +// Additional optional features + + // Current output sample rate + long sample_rate() const; + + // Length of buffer, in milliseconds + int length() const; + + // Number of source time units per second + long clock_rate() const; + + // Set frequency high-pass filter frequency, where higher values reduce bass more + void bass_freq( int frequency ); + + // Number of samples delay from synthesis to samples read out + int output_latency() const; + + // Remove all available samples and clear buffer to silence. If 'entire_buffer' is + // false, just clears out any samples waiting rather than the entire buffer. + void clear( int entire_buffer = 1 ); + + // Number of samples available for reading with read_samples() + long samples_avail() const; + + // Remove 'count' samples from those waiting to be read + void remove_samples( long count ); + +// Experimental features + + // Count number of clocks needed until 'count' samples will be available. + // If buffer can't even hold 'count' samples, returns number of clocks until + // buffer becomes full. + blip_time_t count_clocks( long count ) const; + + // Number of raw samples that can be mixed within frame of specified duration. + long count_samples( blip_time_t duration ) const; + + // Mix 'count' samples from 'buf' into buffer. + void mix_samples( blip_sample_t const* buf, long count ); + + // not documented yet + void set_modified() { modified_ = 1; } + int clear_modified() { int b = modified_; modified_ = 0; return b; } + typedef blip_u64 blip_resampled_time_t; + void remove_silence( long count ); + blip_resampled_time_t resampled_duration( int t ) const { return t * factor_; } + blip_resampled_time_t resampled_time( blip_time_t t ) const { return t * factor_ + offset_; } + blip_resampled_time_t clock_rate_factor( long clock_rate ) const; +public: + Blip_Buffer(); + ~Blip_Buffer(); + + // Deprecated + typedef blip_resampled_time_t resampled_time_t; + blargg_err_t sample_rate( long r ) { return set_sample_rate( r ); } + blargg_err_t sample_rate( long r, int msec ) { return set_sample_rate( r, msec ); } +private: + // noncopyable + Blip_Buffer( const Blip_Buffer& ); + Blip_Buffer& operator = ( const Blip_Buffer& ); +public: + typedef blip_time_t buf_t_; + blip_u64 factor_; + blip_resampled_time_t offset_; + buf_t_* buffer_; + blip_long buffer_size_; + blip_long reader_accum_; + int bass_shift_; +private: + long sample_rate_; + long clock_rate_; + int bass_freq_; + int length_; + int modified_; + friend class Blip_Reader; +}; + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#define BLIP_BUFFER_ACCURACY 32 +#define BLIP_PHASE_BITS 8 + +// Number of bits in resample ratio fraction. Higher values give a more accurate ratio +// but reduce maximum buffer size. +//#ifndef BLIP_BUFFER_ACCURACY +// #define BLIP_BUFFER_ACCURACY 16 +//#endif + +// Number bits in phase offset. Fewer than 6 bits (64 phase offsets) results in +// noticeable broadband noise when synthesizing high frequency square waves. +// Affects size of Blip_Synth objects since they store the waveform directly. +//#ifndef BLIP_PHASE_BITS +// #if BLIP_BUFFER_FAST +// #define BLIP_PHASE_BITS 8 +// #else +// #define BLIP_PHASE_BITS 6 +// #endif +//#endif + + // Internal + typedef blip_u64 blip_resampled_time_t; + int const blip_widest_impulse_ = 16; + int const blip_buffer_extra_ = blip_widest_impulse_ + 2; + int const blip_res = 1 << BLIP_PHASE_BITS; + class blip_eq_t; + + class Blip_Synth_Fast_ { + public: + Blip_Buffer* buf; + int last_amp; + int delta_factor; + + void volume_unit( double ); + Blip_Synth_Fast_(); + void treble_eq( blip_eq_t const& ) { } + }; + + class Blip_Synth_ { + public: + Blip_Buffer* buf; + int last_amp; + int delta_factor; + + void volume_unit( double ); + Blip_Synth_( short* impulses, int width ); + void treble_eq( blip_eq_t const& ); + private: + double volume_unit_; + short* const impulses; + int const width; + blip_long kernel_unit; + int impulses_size() const { return blip_res / 2 * width + 1; } + void adjust_impulse(); + }; + +// Quality level. Start with blip_good_quality. +const int blip_med_quality = 8; +const int blip_good_quality = 12; +const int blip_high_quality = 16; + +// Range specifies the greatest expected change in amplitude. Calculate it +// by finding the difference between the maximum and minimum expected +// amplitudes (max - min). +template +class Blip_Synth { +public: + // Set overall volume of waveform + void volume( double v ) { impl.volume_unit( v * (1.0 / (range < 0 ? -range : range)) ); } + + // Configure low-pass filter (see blip_buffer.txt) + void treble_eq( blip_eq_t const& eq ) { impl.treble_eq( eq ); } + + // Get/set Blip_Buffer used for output + Blip_Buffer* output() const { return impl.buf; } + void output( Blip_Buffer* b ) { impl.buf = b; impl.last_amp = 0; } + + // Update amplitude of waveform at given time. Using this requires a separate + // Blip_Synth for each waveform. + void update( blip_time_t time, int amplitude ); + +// Low-level interface + + // Add an amplitude transition of specified delta, optionally into specified buffer + // rather than the one set with output(). Delta can be positive or negative. + // The actual change in amplitude is delta * (volume / range) + void offset( blip_time_t, int delta, Blip_Buffer* ) const; + void offset( blip_time_t t, int delta ) const { offset( t, delta, impl.buf ); } + + // Works directly in terms of fractional output samples. Contact author for more info. + void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const; + + // Same as offset(), except code is inlined for higher performance + void offset_inline( blip_time_t t, int delta, Blip_Buffer* buf ) const { + offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); + } + void offset_inline( blip_time_t t, int delta ) const { + offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); + } + +private: +#if BLIP_BUFFER_FAST + Blip_Synth_Fast_ impl; +#else + Blip_Synth_ impl; + typedef short imp_t; + imp_t impulses [blip_res * (quality / 2) + 1]; +public: + Blip_Synth() : impl( impulses, quality ) { } +#endif +}; + +// Low-pass equalization parameters +class blip_eq_t { +public: + // Logarithmic rolloff to treble dB at half sampling rate. Negative values reduce + // treble, small positive values (0 to 5.0) increase treble. + blip_eq_t( double treble_db = 0 ); + + // See blip_buffer.txt + blip_eq_t( double treble, long rolloff_freq, long sample_rate, long cutoff_freq = 0 ); + +private: + double treble; + long rolloff_freq; + long sample_rate; + long cutoff_freq; + void generate( float* out, int count ) const; + friend class Blip_Synth_; +}; + +int const blip_sample_bits = 30; + +// Dummy Blip_Buffer to direct sound output to, for easy muting without +// having to stop sound code. +class Silent_Blip_Buffer : public Blip_Buffer { + buf_t_ buf [blip_buffer_extra_ + 1]; +public: + // The following cannot be used (an assertion will fail if attempted): + blargg_err_t set_sample_rate( long samples_per_sec, int msec_length ); + blip_time_t count_clocks( long count ) const; + void mix_samples( blip_sample_t const* buf, long count ); + + Silent_Blip_Buffer(); +}; + + #if defined (__GNUC__) || _MSC_VER >= 1100 + #define BLIP_RESTRICT __restrict + #else + #define BLIP_RESTRICT + #endif + +// Optimized reading from Blip_Buffer, for use in custom sample output + +// Begin reading from buffer. Name should be unique to the current block. +#define BLIP_READER_BEGIN( name, blip_buffer ) \ + const Blip_Buffer::buf_t_* BLIP_RESTRICT name##_reader_buf = (blip_buffer).buffer_;\ + blip_long name##_reader_accum = (blip_buffer).reader_accum_ + +// Get value to pass to BLIP_READER_NEXT() +#define BLIP_READER_BASS( blip_buffer ) ((blip_buffer).bass_shift_) + +// Constant value to use instead of BLIP_READER_BASS(), for slightly more optimal +// code at the cost of having no bass control +int const blip_reader_default_bass = 9; + +// Current sample +#define BLIP_READER_READ( name ) (name##_reader_accum >> (blip_sample_bits - 16)) + +// Current raw sample in full internal resolution +#define BLIP_READER_READ_RAW( name ) (name##_reader_accum) + +// Advance to next sample +#define BLIP_READER_NEXT( name, bass ) \ + (void) (name##_reader_accum += *name##_reader_buf++ - (name##_reader_accum >> (bass))) + +// End reading samples from buffer. The number of samples read must now be removed +// using Blip_Buffer::remove_samples(). +#define BLIP_READER_END( name, blip_buffer ) \ + (void) ((blip_buffer).reader_accum_ = name##_reader_accum) + + +// Compatibility with older version +const long blip_unscaled = 65535; +const int blip_low_quality = blip_med_quality; +const int blip_best_quality = blip_high_quality; + +// Deprecated; use BLIP_READER macros as follows: +// Blip_Reader r; r.begin( buf ); -> BLIP_READER_BEGIN( r, buf ); +// int bass = r.begin( buf ) -> BLIP_READER_BEGIN( r, buf ); int bass = BLIP_READER_BASS( buf ); +// r.read() -> BLIP_READER_READ( r ) +// r.read_raw() -> BLIP_READER_READ_RAW( r ) +// r.next( bass ) -> BLIP_READER_NEXT( r, bass ) +// r.next() -> BLIP_READER_NEXT( r, blip_reader_default_bass ) +// r.end( buf ) -> BLIP_READER_END( r, buf ) +class Blip_Reader { +public: + int begin( Blip_Buffer& ); + blip_long read() const { return accum >> (blip_sample_bits - 16); } + blip_long read_raw() const { return accum; } + void next( int bass_shift = 9 ) { accum += *buf++ - (accum >> bass_shift); } + void end( Blip_Buffer& b ) { b.reader_accum_ = accum; } + +private: + const Blip_Buffer::buf_t_* buf; + blip_long accum; +}; + +// End of public interface + +#include + +template +blip_inline void Blip_Synth::offset_resampled( blip_resampled_time_t time, + int delta, Blip_Buffer* blip_buf ) const +{ + // Fails if time is beyond end of Blip_Buffer, due to a bug in caller code or the + // need for a longer buffer as set by set_sample_rate(). + assert( (blip_long) (time >> BLIP_BUFFER_ACCURACY) < blip_buf->buffer_size_ ); + delta *= impl.delta_factor; + blip_long* BLIP_RESTRICT buf = blip_buf->buffer_ + (time >> BLIP_BUFFER_ACCURACY); + int phase = (int) (time >> (BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS) & (blip_res - 1)); + +#if BLIP_BUFFER_FAST + blip_long left = buf [0] + delta; + + // Kind of crappy, but doing shift after multiply results in overflow. + // Alternate way of delaying multiply by delta_factor results in worse + // sub-sample resolution. + blip_long right = (delta >> BLIP_PHASE_BITS) * phase; + left -= right; + right += buf [1]; + + buf [0] = left; + buf [1] = right; +#else + + int const fwd = (blip_widest_impulse_ - quality) / 2; + int const rev = fwd + quality - 2; + int const mid = quality / 2 - 1; + + imp_t const* BLIP_RESTRICT imp = impulses + blip_res - phase; + + #if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \ + defined (__x86_64__) || defined (__ia64__) || defined (__i386__) + + // straight forward implementation resulted in better code on GCC for x86 + + #define ADD_IMP( out, in ) \ + buf [out] += (blip_long) imp [blip_res * (in)] * delta + + #define BLIP_FWD( i ) {\ + ADD_IMP( fwd + i, i );\ + ADD_IMP( fwd + 1 + i, i + 1 );\ + } + #define BLIP_REV( r ) {\ + ADD_IMP( rev - r, r + 1 );\ + ADD_IMP( rev + 1 - r, r );\ + } + + BLIP_FWD( 0 ) + if ( quality > 8 ) BLIP_FWD( 2 ) + if ( quality > 12 ) BLIP_FWD( 4 ) + { + ADD_IMP( fwd + mid - 1, mid - 1 ); + ADD_IMP( fwd + mid , mid ); + imp = impulses + phase; + } + if ( quality > 12 ) BLIP_REV( 6 ) + if ( quality > 8 ) BLIP_REV( 4 ) + BLIP_REV( 2 ) + + ADD_IMP( rev , 1 ); + ADD_IMP( rev + 1, 0 ); + + #else + + // for RISC processors, help compiler by reading ahead of writes + + #define BLIP_FWD( i ) {\ + blip_long t0 = i0 * delta + buf [fwd + i];\ + blip_long t1 = imp [blip_res * (i + 1)] * delta + buf [fwd + 1 + i];\ + i0 = imp [blip_res * (i + 2)];\ + buf [fwd + i] = t0;\ + buf [fwd + 1 + i] = t1;\ + } + #define BLIP_REV( r ) {\ + blip_long t0 = i0 * delta + buf [rev - r];\ + blip_long t1 = imp [blip_res * r] * delta + buf [rev + 1 - r];\ + i0 = imp [blip_res * (r - 1)];\ + buf [rev - r] = t0;\ + buf [rev + 1 - r] = t1;\ + } + + blip_long i0 = *imp; + BLIP_FWD( 0 ) + if ( quality > 8 ) BLIP_FWD( 2 ) + if ( quality > 12 ) BLIP_FWD( 4 ) + { + blip_long t0 = i0 * delta + buf [fwd + mid - 1]; + blip_long t1 = imp [blip_res * mid] * delta + buf [fwd + mid ]; + imp = impulses + phase; + i0 = imp [blip_res * mid]; + buf [fwd + mid - 1] = t0; + buf [fwd + mid ] = t1; + } + if ( quality > 12 ) BLIP_REV( 6 ) + if ( quality > 8 ) BLIP_REV( 4 ) + BLIP_REV( 2 ) + + blip_long t0 = i0 * delta + buf [rev ]; + blip_long t1 = *imp * delta + buf [rev + 1]; + buf [rev ] = t0; + buf [rev + 1] = t1; + #endif + +#endif +} + +#undef BLIP_FWD +#undef BLIP_REV + +template +#if BLIP_BUFFER_FAST + blip_inline +#endif +void Blip_Synth::offset( blip_time_t t, int delta, Blip_Buffer* buf ) const +{ + offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); +} + +template +#if BLIP_BUFFER_FAST + blip_inline +#endif +void Blip_Synth::update( blip_time_t t, int amp ) +{ + int delta = amp - impl.last_amp; + impl.last_amp = amp; + offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); +} + +blip_inline blip_eq_t::blip_eq_t( double t ) : + treble( t ), rolloff_freq( 0 ), sample_rate( 44100 ), cutoff_freq( 0 ) { } +blip_inline blip_eq_t::blip_eq_t( double t, long rf, long sr, long cf ) : + treble( t ), rolloff_freq( rf ), sample_rate( sr ), cutoff_freq( cf ) { } + +blip_inline int Blip_Buffer::length() const { return length_; } +blip_inline long Blip_Buffer::samples_avail() const { return (long) (offset_ >> BLIP_BUFFER_ACCURACY); } +blip_inline long Blip_Buffer::sample_rate() const { return sample_rate_; } +blip_inline int Blip_Buffer::output_latency() const { return blip_widest_impulse_ / 2; } +blip_inline long Blip_Buffer::clock_rate() const { return clock_rate_; } +blip_inline void Blip_Buffer::clock_rate( long cps ) { factor_ = clock_rate_factor( clock_rate_ = cps ); } + +blip_inline int Blip_Reader::begin( Blip_Buffer& blip_buf ) +{ + buf = blip_buf.buffer_; + accum = blip_buf.reader_accum_; + return blip_buf.bass_shift_; +} + +int const blip_max_length = 0; +int const blip_default_length = 250; + +#endif diff --git a/waterbox/pcfx/sound/OwlResampler.cpp b/waterbox/pcfx/sound/OwlResampler.cpp new file mode 100644 index 0000000000..ea559b7176 --- /dev/null +++ b/waterbox/pcfx/sound/OwlResampler.cpp @@ -0,0 +1,803 @@ +/* Mednafen - Multi-system Emulator + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +// Don't pass more than about 40ms worth of audio data to Resample() +// at a time. + +#define _GNU_SOURCE +#include "../defs.h" +#include +#include +#include +#include "OwlResampler.h" +//#include "../cputest/cputest.h" + +#if defined(ARCH_POWERPC_ALTIVEC) && defined(HAVE_ALTIVEC_H) + #include +#endif + +#ifdef __ARM_NEON__ + #include +#endif + +#ifdef __FAST_MATH__ + #error "OwlResampler.cpp not compatible with unsafe math optimizations!" +#endif + +OwlBuffer::OwlBuffer() +{ + assert(sizeof(I32_F_Pudding) == 4); + assert(sizeof(float) == 4); + + memset(HRBuf, 0, sizeof(HRBuf)); + + accum = 0; + filter_state[0] = 0; + filter_state[1] = 0; + + leftover = 0; + + InputIndex = 0; + InputPhase = 0; + + debias = 0; +} + +OwlBuffer::~OwlBuffer() +{ +} + +template +static int32 ProcessLoop(unsigned count, int32 a, int32* b, int32* exmix0 = NULL, int32* exmix1 = NULL, unsigned lp_shift = 0, unsigned hp_shift = 0, int64* f_in = NULL) +{ + int64 lp_f; + int64 hp_f; + + if(Lowpass) + { + lp_f = f_in[0]; + } + + if(Highpass) + { + hp_f = f_in[1]; + } + + while(count--) + { + int32 tmp; + + if(Integrate) + { + a += *b; + tmp = a >> IntegrateShift; + } + else + tmp = *b; + + if(Lowpass) + { + lp_f += ((int64)((uint64)(int64)tmp << 16) - lp_f) >> lp_shift; + tmp = lp_f >> 16; + } + + if(Highpass) + { + hp_f += ((int64)((uint64)(int64)tmp << 16) - hp_f) >> hp_shift; + tmp = tmp - (hp_f >> 16); + } + + if(DoExMix >= 1) + { + tmp += *exmix0; + exmix0++; + } + + if(DoExMix >= 2) + { + tmp += *exmix1; + exmix1++; + } + + if(FloatOutput) + *(float*)b = tmp; + else + *b = tmp; + + b++; + } + + if(Lowpass) + f_in[0] = lp_f; + + if(Highpass) + f_in[1] = hp_f; + + return(a); +} + +void OwlBuffer::ResampleSkipped(unsigned count) +{ + memmove(HRBuf, &HRBuf[count], HRBUF_OVERFLOW_PADDING * sizeof(HRBuf[0])); + memset(&HRBuf[HRBUF_OVERFLOW_PADDING], 0, count * sizeof(HRBuf[0])); +} + +void OwlBuffer::Integrate(unsigned count, unsigned lp_shift, unsigned hp_shift, RavenBuffer* mixin0, RavenBuffer* mixin1) +{ + //lp_shift = hp_shift = 0; + if(lp_shift != 0 || hp_shift != 0) + { + if(mixin0 && mixin1) + accum = ProcessLoop<2, true, 3, true, true, true>(count, accum, Buf(), mixin0->Buf(), mixin1->Buf(), lp_shift, hp_shift, filter_state); + else if(mixin0) + accum = ProcessLoop<1, true, 3, true, true, true>(count, accum, Buf(), mixin0->Buf(), NULL, lp_shift, hp_shift, filter_state); + else + accum = ProcessLoop<0, true, 3, true, true, true>(count, accum, Buf(), NULL, NULL, lp_shift, hp_shift, filter_state); + } + else + { + if(mixin0 && mixin1) + accum = ProcessLoop<2, true, 3, false, false, true>(count, accum, Buf(), mixin0->Buf(), mixin1->Buf()); + else if(mixin0) + accum = ProcessLoop<1, true, 3, false, false, true>(count, accum, Buf(), mixin0->Buf()); + else + accum = ProcessLoop<0, true, 3, false, false, true>(count, accum, Buf()); + } + + if(accum >= 32767 * 256 * 8 || accum <= -32767 * 256 * 8) + { + //printf("Possible delta sample loss; accum=%d\n", accum); + } +} + +// +// +// + +RavenBuffer::RavenBuffer() +{ + memset(BB, 0, sizeof(BB)); + + accum = 0; + + filter_state[0] = 0; + filter_state[1] = 0; +} + + +RavenBuffer::~RavenBuffer() +{ + + + +} + + + +void RavenBuffer::Process(unsigned count, bool integrate, unsigned lp_shift) +{ + if(integrate) + { + if(lp_shift != 0) + accum = ProcessLoop<0, true, 3, true, false, false>(count, accum, Buf(), NULL, NULL, lp_shift, 0, filter_state); + else + accum = ProcessLoop<0, true, 3, false, false, false>(count, accum, Buf(), NULL, NULL, lp_shift, 0); + } + else + { + if(lp_shift != 0) + accum = ProcessLoop<0, false, 0, true, false, false>(count, accum, Buf(), NULL, NULL, lp_shift, 0, filter_state); + else + accum = ProcessLoop<0, false, 0, false, false, false>(count, accum, Buf(), NULL, NULL, lp_shift, 0); + } +} + +void RavenBuffer::Finish(unsigned count) +{ + memmove(BB, &BB[count], OwlBuffer::HRBUF_OVERFLOW_PADDING * sizeof(BB[0])); + memset(&BB[OwlBuffer::HRBUF_OVERFLOW_PADDING], 0, count * sizeof(BB[0])); +} + + + +static void kaiser_window( double* io, int count, double beta ) +{ + int const accuracy = 16; //12; + + double* end = io + count; + + double beta2 = beta * beta * (double) -0.25; + double to_fract = beta2 / ((double) count * count); + double i = 0; + double rescale = 0; // Doesn't need an initializer, to shut up gcc + + for ( ; io < end; ++io, i += 1 ) + { + double x = i * i * to_fract - beta2; + double u = x; + double k = x + 1; + + double n = 2; + do + { + u *= x / (n * n); + n += 1; + k += u; + } + while ( k <= u * (1 << accuracy) ); + + if ( !i ) + rescale = 1 / k; // otherwise values get large + + *io *= k * rescale; + } +} + +static void gen_sinc( double* out, int size, double cutoff, double kaiser ) +{ + assert( size % 2 == 0 ); // size must be enev + + int const half_size = size / 2; + double* const mid = &out [half_size]; + + // Generate right half of sinc + for ( int i = 0; i < half_size; i++ ) + { + double angle = (i * 2 + 1) * (M_PI / 2); + mid [i] = sin( angle * cutoff ) / angle; + } + + kaiser_window( mid, half_size, kaiser ); + + // Mirror for left half + for ( int i = 0; i < half_size; i++ ) + out [i] = mid [half_size - 1 - i]; +} + +static void normalize( double* io, int size, double gain = 1.0 ) +{ + double sum = 0; + for ( int i = 0; i < size; i++ ) + sum += io [i]; + + double scale = gain / sum; + for ( int i = 0; i < size; i++ ) + io [i] *= scale; +} + + +static INLINE void DoMAC(float *wave, float *coeffs, int32 count, int32 *accum_output) +{ + float acc[4] = { 0, 0, 0, 0 }; + + for(int c = 0; MDFN_LIKELY(c < count); c += 4) + { + acc[0] += wave[c + 0] * coeffs[c + 0]; + acc[1] += wave[c + 1] * coeffs[c + 1]; + acc[2] += wave[c + 2] * coeffs[c + 2]; + acc[3] += wave[c + 3] * coeffs[c + 3]; + } + + *accum_output = (acc[0] + acc[2]) + (acc[1] + acc[3]); +} + + + +#ifdef ARCH_X86 + #include "OwlResampler_x86.inc" +#endif + +#ifdef ARCH_POWERPC_ALTIVEC + #include "OwlResampler_altivec.inc" +#endif + +#ifdef __ARM_NEON__ + #include "OwlResampler_neon.inc" +#endif + + +template +static T SDP2(T v) +{ + T tmp; + + tmp = (v >> ((sizeof(T) * 8) - 1)) & (((T)1 << sa) - 1); + + return ((v + tmp) >> sa); +} + +enum +{ + SIMD_NONE = 0, + +#ifdef ARCH_X86 + SIMD_SSE_16X, + +#ifdef HAVE_INLINEASM_AVX + SIMD_AVX_32X, + SIMD_AVX_32X_P16, +#else + #warning "Compiling without AVX inline assembly." +#endif +#elif defined(ARCH_POWERPC_ALTIVEC) + SIMD_ALTIVEC, +#elif defined __ARM_NEON__ + SIMD_NEON +#endif +}; + +template +NO_INLINE int32 OwlResampler::T_Resample(OwlBuffer* in, const uint32 in_count, int16* out, const uint32 max_out_count, const bool reverse) +{ + if(reverse) + { + int32* a = in->Buf(); + int32* b = in->Buf() + in_count - 1; + + while(MDFN_LIKELY(a < b)) + { + std::swap(*a, *b); + a++; + b--; + } + } + + // + // + // + uint32 count = 0; + int32* I32Out = &IntermediateBuffer[0]; + const uint32 in_count_WLO = in->leftover + in_count; + const uint32 max = std::max(0, (int64)in_count_WLO - NumCoeffs); + uint32 InputPhase = in->InputPhase; + uint32 InputIndex = in->InputIndex; + OwlBuffer::I32_F_Pudding* InSamps = in->BufPudding() - in->leftover; + int32 leftover; + + if(MDFN_UNLIKELY(InputPhase >= NumPhases)) + { + fprintf(stderr, "[BUG] InputPhase >= NumPhases\n"); // Save states can also trigger this. + InputPhase = 0; + } + + while(InputIndex < max) + { + float* wave = &InSamps[InputIndex].f; + float* coeffs = &PInfos[InputPhase].Coeffs[0]; + int32 coeff_count = NumCoeffs; + + switch(TA_SIMD_Type) + { + default: + case SIMD_NONE: + DoMAC(wave, coeffs, coeff_count, I32Out); + break; + +#ifdef ARCH_X86 + case SIMD_SSE_16X: + DoMAC_SSE_16X(wave, coeffs, coeff_count, I32Out); + break; +#ifdef HAVE_INLINEASM_AVX + case SIMD_AVX_32X: + DoMAC_AVX_32X(wave, coeffs, coeff_count, I32Out); + break; + + case SIMD_AVX_32X_P16: + DoMAC_AVX_32X_P16(wave, coeffs, coeff_count, I32Out); + break; +#endif + +#elif defined(ARCH_POWERPC_ALTIVEC) + case SIMD_ALTIVEC: + DoMAC_AltiVec(wave, coeffs, coeff_count, I32Out); + break; +#elif defined __ARM_NEON__ + case SIMD_NEON: + DoMAC_NEON(wave, coeffs, coeff_count, I32Out); + break; +#endif + } + + I32Out++; + count++; + + InputPhase = PInfos[InputPhase].Next; + InputIndex += PInfos[InputPhase].Step; + } + +#if defined(ARCH_X86) && defined(HAVE_INLINEASM_AVX) + if(TA_SIMD_Type == SIMD_AVX_32X || TA_SIMD_Type == SIMD_AVX_32X_P16) + { + asm volatile("vzeroupper\n\t" : : : + #if defined(__AVX__) + "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7" + #if defined(__x86_64__) + , "ymm8", "ymm9", "ymm10", "ymm11", "ymm12", "ymm13", "ymm14", "ymm15" + #endif + #endif + ); + } +#endif + + if(InputIndex > in_count_WLO) + { + leftover = 0; + InputIndex -= in_count_WLO; + } + else + { + leftover = (int32)in_count_WLO - (int32)InputIndex; + InputIndex = 0; + } + +#if 0 + for(uint32 x = 0; x < count; x++) + { + int s = IntermediateBuffer[x] >> 8; + + if(s < -32768 || s > 32767) + { + //printf("Flow: %6d\n", s); + if(s < -32768) + s = -32768; + else if(s > 32767) + s = 32767; + } + out[x * 2] = s; + } +#else + { + int64 debias = in->debias; + + for(uint32 x = 0; x < count; x++) + { + int32 sample = IntermediateBuffer[x]; + int32 s; + + debias += (((int64)((uint64)(int64)sample << 16) - debias) * debias_multiplier) >> 16; + s = SDP2(sample - (debias >> 16)); + if(s < -32768 || s > 32767) + { + //printf("Flow: %6d\n", s); + if(s < -32768) + s = -32768; + else if(s > 32767) + s = 32767; + } + out[x * 2] = s; + } + + in->debias = debias; + } +#endif + memmove(in->Buf() - leftover, + in->Buf() + in_count - leftover, + sizeof(int32) * (leftover + OwlBuffer::HRBUF_OVERFLOW_PADDING)); + + memset(in->Buf() + OwlBuffer::HRBUF_OVERFLOW_PADDING, 0, sizeof(int32) * in_count); + + in->leftover = leftover; + in->InputPhase = InputPhase; + in->InputIndex = InputIndex; + + return count; +} + +void OwlResampler::ResetBufResampState(OwlBuffer* buf) +{ + memset(buf->HRBuf, 0, sizeof(buf->HRBuf[0]) * OwlBuffer::HRBUF_LEFTOVER_PADDING); + buf->InputPhase = 0; +} + + +OwlResampler::~OwlResampler() +{ + +} + +// +// Flush denormals, and coefficients that could lead to denormals, to zero. +// +static float FilterDenormal(float v) +{ + union + { + float f; + uint32 i; + } cat_pun; + + cat_pun.f = v; + + if(((cat_pun.i >> 23) & 0xFF) <= 24) // Maybe < 24 is more correct? + { + MDFN_printf("Small FP coefficient detected: 0x%08x --- raw_sign=%d, raw_exp=0x%02x, raw_mantissa=0x%06x\n", cat_pun.i, cat_pun.i >> 31, (cat_pun.i >> 23) & 0xFF, cat_pun.i & ((1U << 23) - 1)); + return(0); + } + + return(v); +} + +OwlResampler::OwlResampler(double input_rate, double output_rate, double rate_error, double debias_corner, int quality, double nyq_fudge) +{ + std::unique_ptr FilterBuf; + double ratio = (double)output_rate / input_rate; + double cutoff; + double required_bandwidth; + double k_beta; + double k_d; + + for(int i = 0; i < 256; i++) + { + int a = SDP2(i); + int b = SDP2(-i); + int c = i / (1 << 3); + + assert(a == -b && a == c); + } + + assert(sizeof(OwlBuffer::I32_F_Pudding) == 4); + + InputRate = input_rate; + OutputRate = output_rate; + RateError = rate_error; + DebiasCorner = debias_corner; + Quality = quality; + + IntermediateBuffer.resize(OutputRate * 4 / 50); // *4 for safety padding, / min(50,60), an approximate calculation + + const uint32 cpuext = 0; //cputest_get_flags(); + + MDFN_printf("OwlResampler.cpp debug info:\n"); + //MDFN_indent(1); + + // Get the number of phases required, and adjust ratio. + { + double s_ratio = (double)input_rate / output_rate; + double findo = 0; + uint32 count = 0; + uint32 findo_i; + + do + { + count++; + findo += s_ratio; + } while( fabs(1.0 - ((floor(0.5 + findo) / count) / s_ratio)) > rate_error); + + s_ratio = floor(0.5 + findo) / count; + findo_i = (uint32) floor(0.5 + findo); + ratio = 1 / s_ratio; + NumPhases = count; + + PInfos.resize(NumPhases); + + uint32 last_indoo = 0; + for(unsigned int i = 0; i < NumPhases; i++) + { + uint32 index_pos = i * findo_i / NumPhases; + + PInfos[i].Next = (i + 1) % (NumPhases); + PInfos[i].Step = index_pos - last_indoo; + last_indoo = index_pos; + } + PInfos[0].Step = findo_i - last_indoo; + + Ratio_Dividend = findo_i; + Ratio_Divisor = NumPhases; + + MDFN_printf("Phases: %d, Output rate: %f, %d %d\n", NumPhases, input_rate * ratio, Ratio_Dividend, Ratio_Divisor); + + MDFN_printf("Desired maximum rate error: %.10f, Actual rate error: %.10f\n", rate_error, fabs((double)input_rate / output_rate * ratio - 1)); + } + + static const struct + { + double beta; + double d; + double obw; + } QualityTable[7] = + { + { 5.658, 3.62, 0.65 }, + { 6.764, 4.32, 0.70 }, + { 7.865, 5.00, 0.75 }, + { 8.960, 5.70, 0.80 }, + { 10.056, 6.40, 0.85 }, + { 10.056, 6.40, 0.90 }, + + { 10.056, 6.40, 0.9333 }, // 1.0 - (6.40 / 96) + }; + + assert(quality >= 0 && quality <= 6); + + k_beta = QualityTable[quality].beta; + k_d = QualityTable[quality].d; + + + // + // As far as filter frequency response design goes, we clamp the output rate parameter + // to keep PCE CD and PC-FX CD-DA sample amplitudes from going wild since we're not resampling CD-DA totally properly. + // +#define OWLRESAMP_FCALC_RATE_CLAMP 128000.0 //192000.0 //96000.0 //48000.0 + + // A little SOMETHING to widen the transition band a bit to reduce computational complexity with higher output rates. + const double something = std::min(OWLRESAMP_FCALC_RATE_CLAMP, (48000.0 + std::min(OWLRESAMP_FCALC_RATE_CLAMP, output_rate)) / 2 / QualityTable[quality].obw); + + // + // Note: Cutoff calculation is performed again(though slightly differently) down below after the SIMD check. + // + cutoff = QualityTable[quality].obw * (std::min(something, std::min(input_rate, output_rate)) / input_rate); + + required_bandwidth = (std::min(OWLRESAMP_FCALC_RATE_CLAMP, std::min(input_rate, output_rate)) / input_rate) - cutoff; + + NumCoeffs = ceil(k_d / required_bandwidth); + + MDFN_printf("Initial number of coefficients per phase: %u\n", NumCoeffs); + MDFN_printf("Initial nominal cutoff frequency: %f\n", InputRate * cutoff / 2); + + // + // Put this lower limit BEFORE the SIMD stuff, otherwise the NumCoeffs calculation will be off. + // + if(NumCoeffs < 16) + NumCoeffs = 16; + + if(0) + { + abort(); // The sky is falling AAAAAAAAAAAAA + } + #ifdef ARCH_X86 + #ifdef HAVE_INLINEASM_AVX + else if((cpuext & CPUTEST_FLAG_AVX) && (NumCoeffs + 0xF) >= 32) + { + MDFN_printf("SIMD: AVX\n"); + + // AVX loop can't handle less than 32 MACs properly. + NumCoeffs = std::max(32, NumCoeffs); + + // Past 32 MACs, AVX loop granularity is 16 MACs(with some ugly maaaagic~) + NumCoeffs = (NumCoeffs + 0xF) &~ 0xF; + + if(NumCoeffs & 0x10) + Resample_ = &OwlResampler::T_Resample; + else + Resample_ = &OwlResampler::T_Resample; + } + #endif + else if(cpuext & CPUTEST_FLAG_SSE) + { + MDFN_printf("SIMD: SSE\n"); + + // SSE loop does 16 MACs per iteration. + NumCoeffs = (NumCoeffs + 0xF) &~ 0xF; + Resample_ = &OwlResampler::T_Resample; + } + #endif + #ifdef ARCH_POWERPC_ALTIVEC + else if(1) + { + MDFN_printf("SIMD: AltiVec\n"); + + // AltiVec loop does 16 MACs per iteration. + NumCoeffs = (NumCoeffs + 0xF) &~ 0xF; + Resample_ = &OwlResampler::T_Resample; + } + #endif + #ifdef __ARM_NEON__ + else if(1) + { + MDFN_printf("SIMD: NEON\n"); + + // NEON loop does 16 MACs per iteration. + NumCoeffs = (NumCoeffs + 0xF) &~ 0xF; + Resample_ = &OwlResampler::T_Resample; + } + #endif + else + { + // Default loop does 4 MACs per iteration. + NumCoeffs = (NumCoeffs + 3) &~ 3; + Resample_ = &OwlResampler::T_Resample; + } + // + // Don't alter NumCoeffs anymore from here on. + // + + #if !defined(ARCH_X86) && !defined(ARCH_POWERPC_ALTIVEC) && !defined(__ARM_NEON__) + #warning "OwlResampler is being compiled without SIMD support." + #endif + + // + // Adjust cutoff now that NumCoeffs may have been increased. + // + cutoff = std::min(QualityTable[quality].obw * something / input_rate, (std::min(input_rate, output_rate) / input_rate - ((double)k_d / NumCoeffs))); + + cutoff *= nyq_fudge; + if(ceil(cutoff) > 1.0) + cutoff = 1.0; + + + MDFN_printf("Adjusted number of coefficients per phase: %u\n", NumCoeffs); + MDFN_printf("Adjusted nominal cutoff frequency: %f\n", InputRate * cutoff / 2); + + assert(NumCoeffs <= OwlBuffer::HRBUF_LEFTOVER_PADDING); + + CoeffsBuffer.resize((256 / sizeof(float)) + NumCoeffs * NumPhases); + + for(unsigned int i = 0; i < NumPhases; i++) + PInfos[i].Coeffs = (float *)(((uintptr_t)&CoeffsBuffer[0] + 0xFF) &~ 0xFF) + (i * NumCoeffs); + + MDFN_printf("Impulse response table memory usage: %zu bytes\n", CoeffsBuffer.size() * sizeof(float)); + + FilterBuf.reset(new double[NumCoeffs * NumPhases]); + gen_sinc(&FilterBuf[0], NumCoeffs * NumPhases, cutoff / NumPhases, k_beta); + normalize(&FilterBuf[0], NumCoeffs * NumPhases); + + #if 0 + for(int i = 0; i < NumCoeffs * NumPhases; i++) + fprintf(stderr, "%.20f\n", FilterBuf[i]); + #endif + + for(unsigned int phase = 0; phase < NumPhases; phase++) + { + //double sum_d = 0; + //float sum_f4[4] = { 0, 0, 0, 0 }; + + const unsigned sp = (NumPhases - 1 - (((uint64)phase * Ratio_Dividend) % NumPhases)); + const unsigned tp = phase; + + for(unsigned int i = 0; i < NumCoeffs; i++) + { + double tmpcod = FilterBuf[i * NumPhases + sp] * NumPhases; // Tasty cod. + + PInfos[tp].Coeffs[i] = FilterDenormal(tmpcod); + //sum_d += PInfos[tp].Coeffs[i]; + //sum_f4[i % 4] += PInfos[tp].Coeffs[i]; + } + +#if 0 + { + double sf4t = (sum_f4[0] + sum_f4[2]) + (sum_f4[1] + sum_f4[3]); + double sd_div_sf4t = sum_d / sf4t; + + MDFN_printf("Phase %4u: sum_d=%.10f, sum_f4t=%.10f, sum_d div sum_f4t=%.10f(*65536=%f, dB=%.8f)\n", sp, sum_d, (double)sf4t, sd_div_sf4t, 65536.0 * sd_div_sf4t, fabs(20 * log10(sum_d / sf4t))); + } +#endif + } + + assert(debias_corner < (output_rate / 16)); + debias_multiplier = (uint32)(((uint64)1 << 16) * debias_corner / output_rate); + + //MDFN_indent(-1); + + //abort(); + + #if 0 + { + static float dummy_wave[1024]; + static float dummy_coeffs[1024]; + int32 dummy_out; + uint32 begin_time = MDFND_GetTime(); + + for(int i = 0; i < 1024 * 1024; i++) + { + DoMAC_AVX_32X(dummy_wave, dummy_coeffs, 1024, &dummy_out); + //DoMAC_SSE_16X(dummy_wave, dummy_coeffs, 1024, &dummy_out); + } + + printf("%u\n", MDFND_GetTime() - begin_time); + abort(); + } + #endif +} diff --git a/waterbox/pcfx/sound/OwlResampler.h b/waterbox/pcfx/sound/OwlResampler.h new file mode 100644 index 0000000000..607c7c690b --- /dev/null +++ b/waterbox/pcfx/sound/OwlResampler.h @@ -0,0 +1,159 @@ +#ifndef __NES_FILTER_H +#define __NES_FILTER_H + +#include + +class OwlResampler; +class RavenBuffer; + +struct StateMem; + +class OwlBuffer +{ + public: + + enum { HRBUF_LEFTOVER_PADDING = 8192 }; + enum { HRBUF_OVERFLOW_PADDING = 32 }; // For deltas and impulse responses and whatnot that are dangling off the end(>= final timestamp) sorta. + + union I32_F_Pudding + { + int32 i32; + float f; + }; + + OwlBuffer(); + ~OwlBuffer(); + + INLINE int32* Buf(void) + { + return &HRBuf[HRBUF_LEFTOVER_PADDING].i32; + } + + INLINE I32_F_Pudding* BufPudding(void) + { + return &HRBuf[HRBUF_LEFTOVER_PADDING]; + } + + void Integrate(unsigned count, unsigned lp_shift = 0, unsigned hp_shift = 0, RavenBuffer* mixin0 = NULL, RavenBuffer* mixin1 = NULL); // Convenience function. + void ResampleSkipped(unsigned count); + + void ZeroLeftover(void); + + private: + + I32_F_Pudding HRBuf[HRBUF_LEFTOVER_PADDING + 65536 + HRBUF_OVERFLOW_PADDING]; + int32 accum; + int64 filter_state[2]; + + // + // Resampler state: + // + int32 leftover; + + // Index into the input buffer + uint32 InputIndex; + + // Current input phase + uint32 InputPhase; + + // DC bias removal filter thingy + int64 debias; + + friend class OwlResampler; +}; + +class RavenBuffer +{ + public: + + RavenBuffer(); + ~RavenBuffer(); + + INLINE int32* Buf(void) + { + return &BB[0]; + } + + void Process(unsigned count, bool integrate = true, uint32 lp_shift = 0); + void Finish(unsigned count); + + friend class OwlBuffer; + + private: + int32 BB[65536 + OwlBuffer::HRBUF_OVERFLOW_PADDING]; + int32 accum; + int64 filter_state[2]; +}; + +class OwlResampler +{ + public: + + // Resamples from input_rate to output_rate, allowing for rate_error(output_rate +/- output_rate*rate_error) + // error in the resample ratio. + // + // debias_corner is the cheap high-pass DC bias removal filter coefficient. Higher values will result in more bias removal(and + // case a high-pass filter effect), while lower values will lower this effect. It should be <= output_rate / 64, to be on the safe side(prevent + // multiplication overflow). A value of 0 will disable its effect. + // + // quality is an arbitrary control of quality(0 for lowest quality, 5 for highest quality) + // + // nyq_fudge may be a tasty sleep drug. + // + OwlResampler(double input_rate, double output_rate, double rate_error, double debias_corner, int quality, double nyq_fudge = 1.0) MDFN_COLD; + OwlResampler(const OwlResampler &resamp) MDFN_COLD; + ~OwlResampler() MDFN_COLD; + + INLINE int32 Resample(OwlBuffer* in, const uint32 in_count, int16* out, const uint32 max_out_count, const bool reverse = false) + { + return (this->*OwlResampler::Resample_)(in, in_count, out, max_out_count, reverse); + } + void ResetBufResampState(OwlBuffer* buf); + + // Get the InputRate / OutputRate ratio, expressed as a / b + void GetRatio(int32 *a, int32 *b) + { + *a = Ratio_Dividend; + *b = Ratio_Divisor; + } + + private: + + // Copy of the parameters passed to the constructor + double InputRate, OutputRate, RateError, DebiasCorner; + int Quality; + + // Number of phases. + uint32 NumPhases; + uint32 NumPhases_Padded; + + // Coefficients(in each phase, not total) + uint32 NumCoeffs; + + struct PhaseInfo + { + // One pointer for each phase + float* Coeffs; + + // In the FIR loop: InputPhase = PInfos[InputPhase].Next + uint32 Next; + + // Incrementor for InputIndex. In the FIR loop, after updating InputPhase: InputIndex += PInfos[InputPhase].Step + uint32 Step; + }; + + std::vector PInfos; + std::vector CoeffsBuffer; + std::vector IntermediateBuffer; //int32 boobuf[8192]; + + template + int32 T_Resample(OwlBuffer* in, const uint32 in_count, int16* out, const uint32 max_out_count, const bool reverse); + int32 (OwlResampler::*Resample_)(OwlBuffer* in, const uint32 in_count, int16* out, const uint32 max_out_count, const bool reverse); + + uint16 debias_multiplier; + + // for GetRatio() + int32 Ratio_Dividend; + int32 Ratio_Divisor; +}; +#endif diff --git a/waterbox/pcfx/sound/Stereo_Buffer.cpp b/waterbox/pcfx/sound/Stereo_Buffer.cpp new file mode 100644 index 0000000000..6e65464685 --- /dev/null +++ b/waterbox/pcfx/sound/Stereo_Buffer.cpp @@ -0,0 +1,146 @@ + +// Blip_Buffer 0.3.0. http://www.slack.net/~ant/nes-emu/ + +#include "Stereo_Buffer.h" + +/* Library Copyright (C) 2004 Shay Green. Blip_Buffer is free software; +you can redistribute it and/or modify it under the terms of the GNU +General Public License as published by the Free Software Foundation; +either version 2 of the License, or (at your option) any later version. +Stereo_Buffer is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. You should have received a copy of the GNU General +Public License along with Stereo_Buffer; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +Stereo_Buffer::Stereo_Buffer() { + stereo_added = false; + was_stereo = false; +} + +Stereo_Buffer::~Stereo_Buffer() { +} + +bool Stereo_Buffer::set_sample_rate( long rate, int msec ) +{ + for ( int i = 0; i < buf_count; i++ ) { + if ( bufs [i].set_sample_rate( rate, msec ) ) + { + return false; + } + } + + return true; +} + +void Stereo_Buffer::clock_rate( long rate ) +{ + for ( int i = 0; i < buf_count; i++ ) + bufs [i].clock_rate( rate ); +} + +void Stereo_Buffer::bass_freq( int bass ) +{ + for ( unsigned i = 0; i < buf_count; i++ ) + bufs [i].bass_freq( bass ); +} + +void Stereo_Buffer::clear() +{ + stereo_added = false; + was_stereo = false; + for ( int i = 0; i < buf_count; i++ ) + bufs [i].clear(); +} + +void Stereo_Buffer::end_frame( blip_time_t clock_count, bool stereo ) +{ + for ( unsigned i = 0; i < buf_count; i++ ) + { + bufs [i].end_frame( clock_count ); + } + stereo_added |= stereo; +} + + + +long Stereo_Buffer::read_samples( blip_sample_t* out, long max_samples ) +{ + long count = bufs [0].samples_avail(); + if ( count > max_samples / 2 ) + count = max_samples / 2; + if ( count ) + { + if ( stereo_added || was_stereo ) + { + mix_stereo( out, count ); + + bufs [0].remove_samples( count ); + bufs [1].remove_samples( count ); + bufs [2].remove_samples( count ); + } + else + { + mix_mono( out, count ); + + bufs [0].remove_samples( count ); + + bufs [1].remove_silence( count ); + bufs [2].remove_silence( count ); + } + + // to do: this might miss opportunities for optimization + if ( !bufs [0].samples_avail() ) { + was_stereo = stereo_added; + stereo_added = false; + } + } + + return count * 2; +} + +void Stereo_Buffer::mix_stereo( blip_sample_t* out, long count ) +{ + Blip_Reader l_left; + Blip_Reader l_right; + Blip_Reader l_center; + + l_left.begin( bufs [1] ); + l_right.begin( bufs [2] ); + int bass = l_center.begin( bufs [0] ); + + while ( count-- ) + { + int c = l_center.read(); + out [0] = c + l_left.read(); + out [1] = c + l_right.read(); + out += 2; + + l_center.next( bass ); + l_left.next( bass ); + l_right.next( bass ); + } + + l_center.end( bufs [0] ); + l_right.end( bufs [2] ); + l_left.end( bufs [1] ); +} + +void Stereo_Buffer::mix_mono( blip_sample_t* out, long count ) +{ + Blip_Reader in; + int bass = in.begin( bufs [0] ); + + while ( count-- ) + { + int sample = in.read(); + out [0] = sample; + out [1] = sample; + out += 2; + in.next( bass ); + } + + in.end( bufs [0] ); +} + diff --git a/waterbox/pcfx/sound/Stereo_Buffer.h b/waterbox/pcfx/sound/Stereo_Buffer.h new file mode 100644 index 0000000000..3d907fac54 --- /dev/null +++ b/waterbox/pcfx/sound/Stereo_Buffer.h @@ -0,0 +1,69 @@ + +// Simple stereo Blip_Buffer for sound emulators whose oscillators output +// either on the left only, center, or right only. + +// Blip_Buffer 0.3.0. Copyright (C) 2003-2004 Shay Green. GNU GPL license. + +#ifndef STEREO_BUFFER_H +#define STEREO_BUFFER_H + +#include "Blip_Buffer.h" + +class Stereo_Buffer { +public: + Stereo_Buffer(); + ~Stereo_Buffer(); + + // Same as in Blip_Buffer (see Blip_Buffer.h) + bool set_sample_rate( long, int msec = 0 ); + void clock_rate( long ); + void bass_freq( int ); + void clear(); + + // Buffers to output synthesis to + Blip_Buffer* left(); + Blip_Buffer* center(); + Blip_Buffer* right(); + + // Same as in Blip_Buffer. For more efficient operation, pass false + // for was_stereo if the left and right buffers had nothing added + // to them for this frame. + void end_frame( blip_time_t, bool was_stereo = true ); + + // Output is stereo with channels interleved, left before right. Counts + // are in samples, *not* pairs. + long samples_avail() const; + long read_samples( blip_sample_t*, long ); + +private: + // noncopyable + Stereo_Buffer( const Stereo_Buffer& ); + Stereo_Buffer& operator = ( const Stereo_Buffer& ); + + enum { buf_count = 3 }; + Blip_Buffer bufs [buf_count]; + bool stereo_added; + bool was_stereo; + + void mix_stereo( blip_sample_t*, long ); + void mix_mono( blip_sample_t*, long ); +}; + + inline Blip_Buffer* Stereo_Buffer::left() { + return &bufs [1]; + } + + inline Blip_Buffer* Stereo_Buffer::center() { + return &bufs [0]; + } + + inline Blip_Buffer* Stereo_Buffer::right() { + return &bufs [2]; + } + + inline long Stereo_Buffer::samples_avail() const { + return bufs [0].samples_avail(); + } + +#endif + diff --git a/waterbox/pcfx/soundbox.cpp b/waterbox/pcfx/soundbox.cpp new file mode 100644 index 0000000000..6a56b0a5ef --- /dev/null +++ b/waterbox/pcfx/soundbox.cpp @@ -0,0 +1,677 @@ +/******************************************************************************/ +/* Mednafen NEC PC-FX Emulation Module */ +/******************************************************************************/ +/* soundbox.cpp: +** Copyright (C) 2006-2017 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "pcfx.h" +#include "soundbox.h" +#include "king.h" +#include "cdrom/cdromif.h" +#include "cdrom/scsicd.h" +#include "pce_psg/pce_psg.h" +#include "sound/OwlResampler.h" + +#include +#include + +namespace MDFN_IEN_PCFX +{ + +static const int StepSizes[49] = + { + 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, + 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, + 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, + 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552}; + +static const int StepIndexDeltas[16] = + { + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8}; + +static OwlResampler *FXres = NULL; +static OwlBuffer *FXsbuf[2] = {NULL, NULL}; +RavenBuffer *FXCDDABufs[2] = {NULL, NULL}; // Used in the CDROM code + +static PCE_PSG *pce_psg = NULL; + +static bool SoundEnabled; +static uint32 adpcm_lastts; + +struct SoundBox +{ + uint16 ADPCMControl; + uint8 ADPCMVolume[2][2]; // ADPCMVolume[channel(0 or 1)][left(0) or right(1)] + uint8 CDDAVolume[2]; + int32 bigdiv; + int32 smalldiv; + + int64 ResetAntiClick[2]; + double VolumeFiltered[2][2]; + double vf_xv[2][2][1 + 1], vf_yv[2][2][1 + 1]; + + int32 ADPCMDelta[2]; + int32 ADPCMHaveDelta[2]; + + int32 ADPCMPredictor[2]; + int32 StepSizeIndex[2]; + + uint32 ADPCMWhichNibble[2]; + uint16 ADPCMHalfWord[2]; + bool ADPCMHaveHalfWord[2]; + + int32 ADPCM_last[2][2]; +}; + +static SoundBox sbox; +static double ADPCMVolTable[0x40]; + +static bool EmulateBuggyCodec; // If true, emulate the buggy codec/algorithm used by an official PC-FX ADPCM encoder, rather than how the + // hardware actually works. +static bool ResetAntiClickEnabled; // = true; + +#ifdef WANT_DEBUGGER + +enum +{ + GSREG_ADPCM_CTRL = _PSG_GSREG_COUNT, + GSREG_ADPCM0_LVOL, + GSREG_ADPCM0_RVOL, + + GSREG_ADPCM1_LVOL, + GSREG_ADPCM1_RVOL, + + GSREG_ADPCM0_CUR, + GSREG_ADPCM1_CUR, + + GSREG_CDDA_LVOL, + GSREG_CDDA_RVOL +}; + +#define CHPDMOO(n) \ + {0, "--CH" #n "--:", "", 0xFFFF}, \ + {PSG_GSREG_CH0_FREQ | (n << 8), "Freq", "PSG Ch" #n " Frequency(Period)", 2}, \ + {PSG_GSREG_CH0_CTRL | (n << 8), "Ctrl", "PSG Ch" #n " Control", 1}, \ + {PSG_GSREG_CH0_BALANCE | (n << 8), "Balance", "PSG Ch" #n " Balance", 1}, \ + {PSG_GSREG_CH0_WINDEX | (n << 8), "WIndex", "PSG Ch" #n " Waveform Index", 1}, \ + { \ + PSG_GSREG_CH0_SCACHE | (n << 8), "SCache", "PSG Ch" #n " Sample Cache", 1 \ + } + +static const RegType SBoxRegs[] = + { + {PSG_GSREG_SELECT, "Select", "PSG Channel Select", 1}, + {PSG_GSREG_GBALANCE, "GBal", "PSG Global Balance", 1}, + {PSG_GSREG_LFOFREQ, "LFOFreq", "PSG LFO Freq", 1}, + {PSG_GSREG_LFOCTRL, "LFOCtrl", "PSG LFO Control", 1}, + + CHPDMOO(0), + CHPDMOO(1), + CHPDMOO(2), + CHPDMOO(3), + CHPDMOO(4), + {PSG_GSREG_CH4_NCTRL, "NCtrl", "PSG Ch4 Noise Control", 1}, + {PSG_GSREG_CH4_LFSR, "LFSR", "PSG Ch4 Noise LFSR", 0x100 | 18}, + CHPDMOO(5), + {PSG_GSREG_CH5_NCTRL, "NCtrl", "PSG Ch5 Noise Control", 1}, + {PSG_GSREG_CH5_LFSR, "LFSR", "PSG Ch5 Noise LFSR", 0x100 | 18}, + + {0, "--ADPCM:--", "", 0xFFFF}, + + {GSREG_ADPCM_CTRL, "Ctrl", "ADPCM Control", 2}, + {GSREG_ADPCM0_LVOL, "CH0LVol", "ADPCM Ch0 Left Volume", 1}, + {GSREG_ADPCM0_RVOL, "CH0RVol", "ADPCM Ch0 Right Volume", 1}, + {GSREG_ADPCM1_LVOL, "CH1LVol", "ADPCM Ch1 Left Volume", 1}, + {GSREG_ADPCM1_RVOL, "CH1RVol", "ADPCM Ch1 Right Volume", 1}, + + {GSREG_ADPCM0_CUR, "CH0Prc", "ADPCM Ch0 Predictor Value", 2}, + {GSREG_ADPCM1_CUR, "CH1Prc", "ADPCM Ch1 Predictor Value", 2}, + + {0, "--CD-DA:--", "", 0xFFFF}, + {GSREG_CDDA_LVOL, "CDLVol", "CD-DA Left Volume", 1}, + {GSREG_CDDA_RVOL, "CDRVol", "CD-DA Right Volume", 1}, + {0, "", "", 0}, +}; + +static uint32 SBoxDBG_GetRegister(const unsigned int id, char *special, const uint32 special_len) +{ + uint32 value = 0xDEADBEEF; + + switch (id) + { + case GSREG_ADPCM_CTRL: + value = sbox.ADPCMControl; + if (special) + { + int tmp_freq = 32 / (1 << (value & 0x3)); + trio_snprintf(special, special_len, "Frequency: ~%dKHz, Ch0 Interpolation: %s, Ch1 Interpolation: %s, Ch0 Reset: %d, Ch1 Reset: %d", tmp_freq, (value & 0x4) ? "On" : "Off", (value & 0x8) ? "On" : "Off", + (int)(bool)(value & 0x10), (int)(bool)(value & 0x20)); + } + break; + + case GSREG_ADPCM0_LVOL: + value = sbox.ADPCMVolume[0][0]; + break; + + case GSREG_ADPCM0_RVOL: + value = sbox.ADPCMVolume[0][1]; + break; + + case GSREG_ADPCM1_LVOL: + value = sbox.ADPCMVolume[1][0]; + break; + + case GSREG_ADPCM1_RVOL: + value = sbox.ADPCMVolume[1][1]; + break; + + case GSREG_CDDA_LVOL: + value = sbox.CDDAVolume[0]; + break; + + case GSREG_CDDA_RVOL: + value = sbox.CDDAVolume[1]; + break; + + case GSREG_ADPCM0_CUR: + value = sbox.ADPCMPredictor[0] + 0x4000; + break; + + case GSREG_ADPCM1_CUR: + value = sbox.ADPCMPredictor[1] + 0x4000; + break; + + default: + value = pce_psg->GetRegister(id, special, special_len); + break; + } + return (value); +} + +static void SBoxDBG_SetRegister(const unsigned int id, uint32 value) +{ + if (id < _PSG_GSREG_COUNT) + pce_psg->SetRegister(id, value); + else + switch (id) + { + case GSREG_ADPCM_CTRL: + sbox.ADPCMControl = value & 0xFFFF; + break; + + case GSREG_ADPCM0_LVOL: + sbox.ADPCMVolume[0][0] = value & 0x3F; + break; + + case GSREG_ADPCM0_RVOL: + sbox.ADPCMVolume[0][1] = value & 0x3F; + break; + + case GSREG_ADPCM1_LVOL: + sbox.ADPCMVolume[1][0] = value & 0x3F; + break; + + case GSREG_ADPCM1_RVOL: + sbox.ADPCMVolume[1][1] = value & 0x3F; + break; + + case GSREG_CDDA_LVOL: + sbox.CDDAVolume[0] = value & 0x3F; + SCSICD_SetCDDAVolume(0.50f * sbox.CDDAVolume[0] / 63, 0.50f * sbox.CDDAVolume[1] / 63); + break; + + case GSREG_CDDA_RVOL: + sbox.CDDAVolume[1] = value & 0x3F; + SCSICD_SetCDDAVolume(0.50f * sbox.CDDAVolume[0] / 63, 0.50f * sbox.CDDAVolume[1] / 63); + break; + + case GSREG_ADPCM0_CUR: + sbox.ADPCMPredictor[0] = ((int32)value & 0x7FFF) - 0x4000; + break; + + case GSREG_ADPCM1_CUR: + sbox.ADPCMPredictor[1] = ((int32)value & 0x7FFF) - 0x4000; + break; + } +} + +static const RegGroupType SBoxRegsGroup = + { + "SndBox", + SBoxRegs, + SBoxDBG_GetRegister, + SBoxDBG_SetRegister}; + +#endif + +static void RedoVolume(void) +{ + pce_psg->SetVolume(0.681); //0.227 * 0.50); + //ADPCMSynth.volume(0.50); +} + +bool SoundBox_SetSoundRate(uint32 rate) +{ + SoundEnabled = (bool)rate; + + if (FXres) + { + delete FXres; + FXres = NULL; + } + + if (rate > 0) + { + FXres = new OwlResampler(PCFX_MASTER_CLOCK / 12, rate, Setting_ResampRateError, 20, Setting_ResampQuality); + + for (unsigned i = 0; i < 2; i++) + FXres->ResetBufResampState(FXsbuf[i]); + } + + RedoVolume(); + + return (TRUE); +} + +void SoundBox_Init(bool arg_EmulateBuggyCodec, bool arg_ResetAntiClickEnabled) +{ + adpcm_lastts = 0; + SoundEnabled = false; + + EmulateBuggyCodec = arg_EmulateBuggyCodec; + ResetAntiClickEnabled = arg_ResetAntiClickEnabled; + + for (unsigned i = 0; i < 2; i++) + { + FXsbuf[i] = new OwlBuffer(); + FXCDDABufs[i] = new RavenBuffer(); + } + + pce_psg = new PCE_PSG(FXsbuf[0]->Buf(), FXsbuf[1]->Buf(), PCE_PSG::REVISION_HUC6280A); + +#ifdef WANT_DEBUGGER + MDFNDBG_AddRegGroup(&SBoxRegsGroup); +#endif + + memset(&sbox, 0, sizeof(sbox)); + + // Build ADPCM volume table, 1.5dB per step, ADPCM volume settings of 0x0 through 0x1B result in silence. + for (int x = 0; x < 0x40; x++) + { + double flub = 1; + int vti = 0x3F - x; + + if (x) + flub /= pow(2, (double)1 / 4 * x); + + if (vti <= 0x1B) + ADPCMVolTable[vti] = 0; + else + ADPCMVolTable[vti] = flub; + } +} + +/* Macro to access currently selected PSG channel */ +void SoundBox_Write(uint32 A, uint16 V, const v810_timestamp_t timestamp) +{ + A &= 0x3F; + + if (A < 0x20) + { + pce_psg->Write(timestamp / 3, A >> 1, V); + } + else + { + //printf("%04x %04x %d\n", A, V, timestamp); + switch (A & 0x3F) + { + //default: printf("HARUM: %04x %04x\n", A, V); break; + case 0x20: + SoundBox_ADPCMUpdate(timestamp); + for (int ch = 0; ch < 2; ch++) + { + if (!(sbox.ADPCMControl & (0x10 << ch)) && (V & (0x10 << ch))) + { + //printf("Reset: %d\n", ch); + + if (ResetAntiClickEnabled) + { + sbox.ResetAntiClick[ch] += (int64)((uint64)sbox.ADPCMPredictor[ch] << 32); + if (sbox.ResetAntiClick[ch] > ((int64)0x3FFF << 32)) + sbox.ResetAntiClick[ch] = (int64)0x3FFF << 32; + if (sbox.ResetAntiClick[ch] < -((int64)0x4000 << 32)) + sbox.ResetAntiClick[ch] = -((int64)0x4000 << 32); + } + + sbox.ADPCMPredictor[ch] = 0; + sbox.StepSizeIndex[ch] = 0; + } + } + sbox.ADPCMControl = V; + break; + + case 0x22: + SoundBox_ADPCMUpdate(timestamp); + sbox.ADPCMVolume[0][0] = V & 0x3F; + break; + + case 0x24: + SoundBox_ADPCMUpdate(timestamp); + sbox.ADPCMVolume[0][1] = V & 0x3F; + break; + + case 0x26: + SoundBox_ADPCMUpdate(timestamp); + sbox.ADPCMVolume[1][0] = V & 0x3F; + break; + + case 0x28: + SoundBox_ADPCMUpdate(timestamp); + sbox.ADPCMVolume[1][1] = V & 0x3F; + break; + + case 0x2A: + sbox.CDDAVolume[0] = V & 0x3F; + SCSICD_SetCDDAVolume(0.50f * sbox.CDDAVolume[0] / 63, 0.50f * sbox.CDDAVolume[1] / 63); + break; + + case 0x2C: + sbox.CDDAVolume[1] = V & 0x3F; + SCSICD_SetCDDAVolume(0.50f * sbox.CDDAVolume[0] / 63, 0.50f * sbox.CDDAVolume[1] / 63); + break; + } + } +} + +static uint32 KINGADPCMControl; + +void SoundBox_SetKINGADPCMControl(uint32 value) +{ + KINGADPCMControl = value; +} + +/* Digital filter designed by mkfilter/mkshape/gencode A.J. Fisher + Command line: /www/usr/fisher/helpers/mkfilter -Bu -Lp -o 1 -a 1.5888889125e-04 0.0000000000e+00 -l */ +static void DoVolumeFilter(int ch, int lr) +{ + sbox.vf_xv[ch][lr][0] = sbox.vf_xv[ch][lr][1]; + sbox.vf_xv[ch][lr][1] = (double)ADPCMVolTable[sbox.ADPCMVolume[ch][lr]] / 2.004348738e+03; + + sbox.vf_yv[ch][lr][0] = sbox.vf_yv[ch][lr][1]; + sbox.vf_yv[ch][lr][1] = (sbox.vf_xv[ch][lr][0] + sbox.vf_xv[ch][lr][1]) + (0.9990021696 * sbox.vf_yv[ch][lr][0]); + sbox.VolumeFiltered[ch][lr] = sbox.vf_yv[ch][lr][1]; +} + +static const int16 ADPCM_PhaseFilter[8][7] = + { + /* 0 */ {40, 283, 654, 683, 331, 56, 1}, // 2048 + /* 1 */ {28, 238, 618, 706, 381, 75, 2}, // 2048 + /* 2 */ {19, 197, 577, 720, 432, 99, 4}, // 2048 + /* 3 */ {12, 160, 532, 726, 483, 128, 7}, // 2048 + /* 4 */ {7, 128, 483, 726, 532, 160, 12}, // 2048 + /* 5 */ {4, 99, 432, 720, 577, 197, 19}, // 2048 + /* 6 */ {2, 75, 381, 706, 618, 238, 28}, // 2048 + /* 7 */ {1, 56, 331, 683, 654, 283, 40}, // 2048 +}; + +v810_timestamp_t SoundBox_ADPCMUpdate(const v810_timestamp_t timestamp) +{ + int32 run_time = timestamp - adpcm_lastts; + + adpcm_lastts = timestamp; + + sbox.bigdiv -= run_time * 2; + + while (sbox.bigdiv <= 0) + { + sbox.smalldiv--; + while (sbox.smalldiv <= 0) + { + sbox.smalldiv += 1 << ((KINGADPCMControl >> 2) & 0x3); + for (int ch = 0; ch < 2; ch++) + { + // Keep playing our last halfword fetched even if KING ADPCM is disabled + if (sbox.ADPCMHaveHalfWord[ch] || KINGADPCMControl & (1 << ch)) + { + if (!sbox.ADPCMWhichNibble[ch]) + { + sbox.ADPCMHalfWord[ch] = KING_GetADPCMHalfWord(ch); + sbox.ADPCMHaveHalfWord[ch] = TRUE; + } + + // If the channel's reset bit is set, don't update its ADPCM state. + if (sbox.ADPCMControl & (0x10 << ch)) + { + sbox.ADPCMDelta[ch] = 0; + } + else + { + uint8 nibble = (sbox.ADPCMHalfWord[ch] >> (sbox.ADPCMWhichNibble[ch])) & 0xF; + int32 BaseStepSize = StepSizes[sbox.StepSizeIndex[ch]]; + + //if(!ch) + //printf("Nibble: %02x\n", nibble); + + if (EmulateBuggyCodec) + { + if (BaseStepSize == 1552) + BaseStepSize = 1522; + + sbox.ADPCMDelta[ch] = BaseStepSize * ((nibble & 0x7) + 1) * 2; + } + else + sbox.ADPCMDelta[ch] = BaseStepSize * ((nibble & 0x7) + 1); + + // Linear interpolation turned on? + if (sbox.ADPCMControl & (0x4 << ch)) + sbox.ADPCMDelta[ch] >>= (KINGADPCMControl >> 2) & 0x3; + + if (nibble & 0x8) + sbox.ADPCMDelta[ch] = -sbox.ADPCMDelta[ch]; + + sbox.StepSizeIndex[ch] += StepIndexDeltas[nibble]; + + if (sbox.StepSizeIndex[ch] < 0) + sbox.StepSizeIndex[ch] = 0; + + if (sbox.StepSizeIndex[ch] > 48) + sbox.StepSizeIndex[ch] = 48; + } + sbox.ADPCMHaveDelta[ch] = 1; + + // Linear interpolation turned on? + if (sbox.ADPCMControl & (0x4 << ch)) + sbox.ADPCMHaveDelta[ch] = 1 << ((KINGADPCMControl >> 2) & 0x3); + + sbox.ADPCMWhichNibble[ch] = (sbox.ADPCMWhichNibble[ch] + 4) & 0xF; + + if (!sbox.ADPCMWhichNibble[ch]) + sbox.ADPCMHaveHalfWord[ch] = FALSE; + } + } // for(int ch...) + } // while(sbox.smalldiv <= 0) + + const uint32 synthtime42 = (timestamp << 1) + sbox.bigdiv; + const uint32 synthtime14 = synthtime42 / 3; + const uint32 synthtime = synthtime14 >> 3; + const unsigned synthtime_phase = synthtime14 & 7; + + //printf("Phase: %d, %d\n", synthtime42 % 24, (synthtime42 / 3) & 7); + + for (int ch = 0; ch < 2; ch++) + { + //if(!ch) + //{ + // printf("%d\n", synthtime - last_synthtime); + // last_synthtime = synthtime; + //} + + if (sbox.ADPCMHaveDelta[ch]) + { + sbox.ADPCMPredictor[ch] += sbox.ADPCMDelta[ch]; + + sbox.ADPCMHaveDelta[ch]--; + + if (sbox.ADPCMPredictor[ch] > 0x3FFF) + { + sbox.ADPCMPredictor[ch] = 0x3FFF; /*printf("Overflow: %d\n", ch);*/ + } + if (sbox.ADPCMPredictor[ch] < -0x4000) + { + sbox.ADPCMPredictor[ch] = -0x4000; /*printf("Underflow: %d\n", ch);*/ + } + } + else + { + } + + if (SoundEnabled) + { + int32 samp[2]; + + if (EmulateBuggyCodec) + { + samp[0] = (int32)(((sbox.ADPCMPredictor[ch] >> 1) + (sbox.ResetAntiClick[ch] >> 33)) * sbox.VolumeFiltered[ch][0]); + samp[1] = (int32)(((sbox.ADPCMPredictor[ch] >> 1) + (sbox.ResetAntiClick[ch] >> 33)) * sbox.VolumeFiltered[ch][1]); + } + else + { + samp[0] = (int32)((sbox.ADPCMPredictor[ch] + (sbox.ResetAntiClick[ch] >> 32)) * sbox.VolumeFiltered[ch][0]); + samp[1] = (int32)((sbox.ADPCMPredictor[ch] + (sbox.ResetAntiClick[ch] >> 32)) * sbox.VolumeFiltered[ch][1]); + } +#if 0 + printf("%d, %f %f\n", ch, sbox.VolumeFiltered[ch][0], sbox.VolumeFiltered[ch][1]); + + { + static int inv = 0x1FFF; + + samp[0] = samp[1] = inv; + + if(ch == 1) + inv = -inv; + } +#endif + for (unsigned y = 0; y < 2; y++) + { + const int32 delta = samp[y] - sbox.ADPCM_last[ch][y]; + int32 *tb = FXsbuf[y]->Buf() + (synthtime & 0xFFFF); + const int16 *coeffs = ADPCM_PhaseFilter[synthtime_phase]; + + for (unsigned c = 0; c < 7; c++) + { + int32 tmp = delta * coeffs[c]; + + tb[c] += tmp; + } + } + + sbox.ADPCM_last[ch][0] = samp[0]; + sbox.ADPCM_last[ch][1] = samp[1]; + } + } + + for (int ch = 0; ch < 2; ch++) + { + sbox.ResetAntiClick[ch] -= sbox.ResetAntiClick[ch] >> 8; + //if(ch) + // MDFN_DispMessage("%d", (int)(sbox.ResetAntiClick[ch] >> 32)); + } + + for (int ch = 0; ch < 2; ch++) + for (int lr = 0; lr < 2; lr++) + { + DoVolumeFilter(ch, lr); + } + sbox.bigdiv += 1365 * 2 / 2; + } + + return (timestamp + (sbox.bigdiv + 1) / 2); +} + +int32 SoundBox_Flush(const v810_timestamp_t end_timestamp, v810_timestamp_t *new_base_timestamp, int16 *SoundBuf, const int32 MaxSoundFrames, const bool reverse) +{ + const uint32 end_timestamp_div3 = end_timestamp / 3; + const uint32 end_timestamp_div12 = end_timestamp / 12; + const uint32 end_timestamp_mod12 = end_timestamp % 12; + const unsigned rsc = std::min(65536, end_timestamp_div12); + int32 FrameCount = 0; + + *new_base_timestamp = end_timestamp_mod12; + + pce_psg->Update(end_timestamp_div3); + + for (unsigned y = 0; y < 2; y++) + { + if (SoundEnabled && FXres) + { + FXsbuf[y]->Integrate(rsc, 0, 0, FXCDDABufs[y]); + FrameCount = FXres->Resample(FXsbuf[y], rsc, SoundBuf + y, MaxSoundFrames, reverse); + } + else + FXsbuf[y]->ResampleSkipped(rsc); + + FXCDDABufs[y]->Finish(rsc); + } + + return (FrameCount); +} + +void SoundBox_ResetTS(const v810_timestamp_t ts_base) +{ + pce_psg->ResetTS(ts_base / 3); + adpcm_lastts = ts_base; +} + +void SoundBox_Reset(const v810_timestamp_t timestamp) +{ + SoundBox_ADPCMUpdate(timestamp); + pce_psg->Power(timestamp / 3); + + sbox.ADPCMControl = 0; + + memset(&sbox.vf_xv, 0, sizeof(sbox.vf_xv)); + memset(&sbox.vf_yv, 0, sizeof(sbox.vf_yv)); + + for (int lr = 0; lr < 2; lr++) + { + for (int ch = 0; ch < 2; ch++) + { + sbox.ADPCMVolume[ch][lr] = 0; + sbox.VolumeFiltered[ch][lr] = 0; + } + + sbox.CDDAVolume[lr] = 0; + } + + for (int ch = 0; ch < 2; ch++) + { + sbox.ADPCMPredictor[ch] = 0; + sbox.StepSizeIndex[ch] = 0; + } + + memset(sbox.ADPCMWhichNibble, 0, sizeof(sbox.ADPCMWhichNibble)); + memset(sbox.ADPCMHalfWord, 0, sizeof(sbox.ADPCMHalfWord)); + memset(sbox.ADPCMHaveHalfWord, 0, sizeof(sbox.ADPCMHaveHalfWord)); + + SCSICD_SetCDDAVolume(0.50f * sbox.CDDAVolume[0] / 63, 0.50f * sbox.CDDAVolume[1] / 63); + + sbox.bigdiv = 2; // TODO: KING->SBOX ADPCM Synch //(1365 - 85 * 4) * 2; //1365 * 2 / 2; + sbox.smalldiv = 0; +} +} diff --git a/waterbox/pcfx/soundbox.h b/waterbox/pcfx/soundbox.h new file mode 100644 index 0000000000..bd5e4564d0 --- /dev/null +++ b/waterbox/pcfx/soundbox.h @@ -0,0 +1,45 @@ +/******************************************************************************/ +/* Mednafen NEC PC-FX Emulation Module */ +/******************************************************************************/ +/* soundbox.h: +** Copyright (C) 2006-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef _PCFX_SOUNDBOX_H +#define _PCFX_SOUNDBOX_H + +namespace MDFN_IEN_PCFX +{ + +bool SoundBox_SetSoundRate(uint32 rate); +int32 SoundBox_Flush(const v810_timestamp_t timestamp, v810_timestamp_t* new_base_timestamp, int16 *SoundBuf, const int32 MaxSoundFrames, const bool reverse); +void SoundBox_Write(uint32 A, uint16 V, const v810_timestamp_t timestamp); +void SoundBox_Init(bool arg_EmulateBuggyCodec, bool arg_ResetAntiClickEnabled) MDFN_COLD; + +void SoundBox_Reset(const v810_timestamp_t timestamp) MDFN_COLD; + +void SoundBox_SetKINGADPCMControl(uint32); + +v810_timestamp_t SoundBox_ADPCMUpdate(const v810_timestamp_t timestamp); + +void SoundBox_ResetTS(const v810_timestamp_t ts_base); + +} + +#include "sound/Blip_Buffer.h" +#include "sound/Stereo_Buffer.h" +#endif diff --git a/waterbox/pcfx/timer.cpp b/waterbox/pcfx/timer.cpp new file mode 100644 index 0000000000..1308d05d8a --- /dev/null +++ b/waterbox/pcfx/timer.cpp @@ -0,0 +1,175 @@ +/******************************************************************************/ +/* Mednafen NEC PC-FX Emulation Module */ +/******************************************************************************/ +/* timer.cpp: +** Copyright (C) 2006-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/* + TODO: Determine if the interrupt request bit can be manually set even if timer enable and/or timer int enable bits are 0. +*/ + + +#include "pcfx.h" +#include "interrupt.h" +#include "timer.h" + +namespace MDFN_IEN_PCFX +{ + +static uint16 control; +static uint16 period; +static int32 counter; + +static int32 lastts; + +static INLINE v810_timestamp_t CalcNextEventTS(const v810_timestamp_t timestamp) +{ + return((control & 0x2) ? (timestamp + counter) : PCFX_EVENT_NONONO); +} + +#define EFF_PERIOD ((period ? period : 0x10000) * 15) + +v810_timestamp_t FXTIMER_Update(const v810_timestamp_t timestamp) +{ + if(control & 0x2) + { + int32 cycles = timestamp - lastts; + counter -= cycles; + while(counter <= 0) + { + counter += EFF_PERIOD; + if(control & 0x1) + { + control |= 0x4; + PCFXIRQ_Assert(PCFXIRQ_SOURCE_TIMER, TRUE); + } + } + } + + lastts = timestamp; + + return(CalcNextEventTS(timestamp)); +} + +void FXTIMER_ResetTS(int32 ts_base) +{ + lastts = ts_base; +} + +uint16 FXTIMER_Read16(uint32 A, const v810_timestamp_t timestamp) +{ + FXTIMER_Update(timestamp); + switch(A & 0xFC0) + { + default: return(0); + + case 0xF00: return(control); + + case 0xF80: return(period); + + case 0xFC0: return((counter + 14) / 15); + } + return(0); +} + +uint8 FXTIMER_Read8(uint32 A, const v810_timestamp_t timestamp) +{ + FXTIMER_Update(timestamp); + return(FXTIMER_Read16(A&~1, timestamp) >> ((A & 1) * 8)); +} + +void FXTIMER_Write16(uint32 A, uint16 V, const v810_timestamp_t timestamp) +{ + FXTIMER_Update(timestamp); + + switch(A & 0xFC0) + { + default: break; + + case 0xF00: if(!(control & 0x2) && (V & 0x2)) + counter = EFF_PERIOD; + control = V & 0x7; + + if(V & 0x4) + FXDBG("Timer control write with D2 set?"); + + PCFXIRQ_Assert(PCFXIRQ_SOURCE_TIMER, (bool)(control & 0x4)); + PCFX_SetEvent(PCFX_EVENT_TIMER, CalcNextEventTS(timestamp)); + break; + + case 0xF80: period = V; + PCFX_SetEvent(PCFX_EVENT_TIMER, CalcNextEventTS(timestamp)); + break; + } +} + +uint32 FXTIMER_GetRegister(const unsigned int id, char *special, const uint32 special_len) +{ + uint32 value = 0xDEADBEEF; + + switch(id) + { + case FXTIMER_GSREG_TCTRL: + value = control; + if(special) + { + trio_snprintf(special, special_len, "Counting Enabled: %d, IRQ Enabled: %d, IRQ Asserted: %d", (int)(bool)(control & 2), (int)(bool)(control & 1), (int)(bool)(control & 4)); + } + break; + + case FXTIMER_GSREG_TPRD: + value = period; + if(special) + { + trio_snprintf(special, special_len, "Effective Period: %d; 21477272 / %d = %fHz", EFF_PERIOD, EFF_PERIOD, (double)21477272 / (EFF_PERIOD)); + } + break; + + case FXTIMER_GSREG_TCNTR: + value = counter; + if(special) + { + //trio_snprintf(buf, 256, "Pad: %d, ??: %d, Timer: %d, Reset: %d", + //*special = std::string(buf); + } + break; + } + + return value; +} + +void FXTIMER_SetRegister(const unsigned int id, uint32 value) +{ + + +} + +void FXTIMER_Reset(void) +{ + control = 0; + period = 0; + counter = 0; +} + +void FXTIMER_Init(void) +{ + lastts = 0; + FXTIMER_Reset(); +} + +} diff --git a/waterbox/pcfx/timer.h b/waterbox/pcfx/timer.h new file mode 100644 index 0000000000..253e6c5a29 --- /dev/null +++ b/waterbox/pcfx/timer.h @@ -0,0 +1,49 @@ +/******************************************************************************/ +/* Mednafen NEC PC-FX Emulation Module */ +/******************************************************************************/ +/* timer.h: +** Copyright (C) 2006-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef __PCFX_TIMER_H +#define __PCFX_TIMER_H + +namespace MDFN_IEN_PCFX +{ + +void FXTIMER_Write16(uint32 A, uint16 V, const v810_timestamp_t timestamp); +uint16 FXTIMER_Read16(uint32 A, const v810_timestamp_t timestamp); +uint8 FXTIMER_Read8(uint32 A, const v810_timestamp_t timestamp); +v810_timestamp_t FXTIMER_Update(const v810_timestamp_t timestamp); +void FXTIMER_ResetTS(int32 ts_base); +void FXTIMER_Reset(void) MDFN_COLD; + +void FXTIMER_Init(void) MDFN_COLD; + +enum +{ + FXTIMER_GSREG_TCTRL = 0, + FXTIMER_GSREG_TPRD, + FXTIMER_GSREG_TCNTR +}; + +uint32 FXTIMER_GetRegister(const unsigned int id, char *special, const uint32 special_len); +void FXTIMER_SetRegister(const unsigned int id, uint32 value); + +} + +#endif diff --git a/waterbox/pcfx/v810/v810_cpu.cpp b/waterbox/pcfx/v810/v810_cpu.cpp new file mode 100644 index 0000000000..94d326708b --- /dev/null +++ b/waterbox/pcfx/v810/v810_cpu.cpp @@ -0,0 +1,1540 @@ +/* V810 Emulator + * + * Copyright (C) 2006 David Tucker + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Alternatively, the V810 emulator code(and all V810 emulation header files) can be used/distributed under the following license(you can adopt either + license exclusively for your changes by removing one of these license headers, but it's STRONGLY preferable + to keep your changes dual-licensed as well): + +This Reality Boy emulator is copyright (C) David Tucker 1997-2008, all rights +reserved. You may use this code as long as you make no money from the use of +this code and you acknowledge the original author (Me). I reserve the right to +dictate who can use this code and how (Just so you don't do something stupid +with it). + Most Importantly, this code is swap ware. If you use It send along your new +program (with code) or some other interesting tidbits you wrote, that I might be +interested in. + This code is in beta, there are bugs! I am not responsible for any damage +done to your computer, reputation, ego, dog, or family life due to the use of +this code. All source is provided as is, I make no guaranties, and am not +responsible for anything you do with the code (legal or otherwise). + Virtual Boy is a trademark of Nintendo, and V810 is a trademark of NEC. I am +in no way affiliated with either party and all information contained hear was +found freely through public domain sources. +*/ + +////////////////////////////////////////////////////////// +// CPU routines + +#include "../defs.h" + +//#include "pcfx.h" +//#include "debug.h" + +#include +#include + +#include "v810_opt.h" +#include "v810_cpu.h" +#include "v810_cpuD.h" + +V810::V810() +{ +#ifdef WANT_DEBUGGER + CPUHook = NULL; + ADDBT = NULL; +#endif + + MemRead8 = NULL; + MemRead16 = NULL; + MemRead32 = NULL; + + IORead8 = NULL; + IORead16 = NULL; + IORead32 = NULL; + + MemWrite8 = NULL; + MemWrite16 = NULL; + MemWrite32 = NULL; + + IOWrite8 = NULL; + IOWrite16 = NULL; + IOWrite32 = NULL; + + memset(FastMap, 0, sizeof(FastMap)); + + memset(MemReadBus32, 0, sizeof(MemReadBus32)); + memset(MemWriteBus32, 0, sizeof(MemWriteBus32)); + + v810_timestamp = 0; + next_event_ts = 0x7FFFFFFF; +} + +V810::~V810() +{ + Kill(); +} + +INLINE void V810::RecalcIPendingCache(void) +{ + IPendingCache = 0; + + // Of course don't generate an interrupt if there's not one pending! + if (ilevel < 0) + return; + + // If CPU is halted because of a fatal exception, don't let an interrupt + // take us out of this halted status. + if (Halted == HALT_FATAL_EXCEPTION) + return; + + // If the NMI pending, exception pending, and/or interrupt disabled bit + // is set, don't accept any interrupts. + if (S_REG[PSW] & (PSW_NP | PSW_EP | PSW_ID)) + return; + + // If the interrupt level is lower than the interrupt enable level, don't + // accept it. + if (ilevel < (int)((S_REG[PSW] & PSW_IA) >> 16)) + return; + + IPendingCache = 0xFF; +} + +// TODO: "An interrupt that occurs during restore/dump/clear operation is internally held and is accepted after the +// operation in progress is finished. The maskable interrupt is held internally only when the EP, NP, and ID flags +// of PSW are all 0." +// +// This behavior probably doesn't have any relevance on the PC-FX, unless we're sadistic +// and try to restore cache from an interrupt acknowledge register or dump it to a register +// controlling interrupt masks... I wanna be sadistic~ + +void V810::CacheClear(v810_timestamp_t ×tamp, uint32 start, uint32 count) +{ + //printf("Cache clear: %08x %08x\n", start, count); + for (uint32 i = 0; i < count && (i + start) < 128; i++) + memset(&Cache[i + start], 0, sizeof(V810_CacheEntry_t)); +} + +INLINE void V810::CacheOpMemStore(v810_timestamp_t ×tamp, uint32 A, uint32 V) +{ + if (MemWriteBus32[A >> 24]) + { + timestamp += 2; + MemWrite32(timestamp, A, V); + } + else + { + timestamp += 2; + MemWrite16(timestamp, A, V & 0xFFFF); + + timestamp += 2; + MemWrite16(timestamp, A | 2, V >> 16); + } +} + +INLINE uint32 V810::CacheOpMemLoad(v810_timestamp_t ×tamp, uint32 A) +{ + if (MemReadBus32[A >> 24]) + { + timestamp += 2; + return (MemRead32(timestamp, A)); + } + else + { + uint32 ret; + + timestamp += 2; + ret = MemRead16(timestamp, A); + + timestamp += 2; + ret |= MemRead16(timestamp, A | 2) << 16; + return (ret); + } +} + +void V810::CacheDump(v810_timestamp_t ×tamp, const uint32 SA) +{ + printf("Cache dump: %08x\n", SA); + + for (int i = 0; i < 128; i++) + { + CacheOpMemStore(timestamp, SA + i * 8 + 0, Cache[i].data[0]); + CacheOpMemStore(timestamp, SA + i * 8 + 4, Cache[i].data[1]); + } + + for (int i = 0; i < 128; i++) + { + uint32 icht = Cache[i].tag | ((int)Cache[i].data_valid[0] << 22) | ((int)Cache[i].data_valid[1] << 23); + + CacheOpMemStore(timestamp, SA + 1024 + i * 4, icht); + } +} + +void V810::CacheRestore(v810_timestamp_t ×tamp, const uint32 SA) +{ + printf("Cache restore: %08x\n", SA); + + for (int i = 0; i < 128; i++) + { + Cache[i].data[0] = CacheOpMemLoad(timestamp, SA + i * 8 + 0); + Cache[i].data[1] = CacheOpMemLoad(timestamp, SA + i * 8 + 4); + } + + for (int i = 0; i < 128; i++) + { + uint32 icht; + + icht = CacheOpMemLoad(timestamp, SA + 1024 + i * 4); + + Cache[i].tag = icht & ((1 << 22) - 1); + Cache[i].data_valid[0] = (icht >> 22) & 1; + Cache[i].data_valid[1] = (icht >> 23) & 1; + } +} + +INLINE uint32 V810::RDCACHE(v810_timestamp_t ×tamp, uint32 addr) +{ + const int CI = (addr >> 3) & 0x7F; + const int SBI = (addr & 4) >> 2; + + if (Cache[CI].tag == (addr >> 10)) + { + if (!Cache[CI].data_valid[SBI]) + { + timestamp += 2; // or higher? Penalty for cache miss seems to be higher than having cache disabled. + if (MemReadBus32[addr >> 24]) + Cache[CI].data[SBI] = MemRead32(timestamp, addr & ~0x3); + else + { + timestamp++; + + uint32 tmp; + + tmp = MemRead16(timestamp, addr & ~0x3); + tmp |= MemRead16(timestamp, (addr & ~0x3) | 0x2) << 16; + + Cache[CI].data[SBI] = tmp; + } + Cache[CI].data_valid[SBI] = TRUE; + } + } + else + { + Cache[CI].tag = addr >> 10; + + timestamp += 2; // or higher? Penalty for cache miss seems to be higher than having cache disabled. + if (MemReadBus32[addr >> 24]) + Cache[CI].data[SBI] = MemRead32(timestamp, addr & ~0x3); + else + { + timestamp++; + + uint32 tmp; + + tmp = MemRead16(timestamp, addr & ~0x3); + tmp |= MemRead16(timestamp, (addr & ~0x3) | 0x2) << 16; + + Cache[CI].data[SBI] = tmp; + } + //Cache[CI].data[SBI] = MemRead32(timestamp, addr & ~0x3); + Cache[CI].data_valid[SBI] = TRUE; + Cache[CI].data_valid[SBI ^ 1] = FALSE; + } + + //{ + // // Caution: This can mess up DRAM page change penalty timings + // uint32 dummy_timestamp = 0; + // if(Cache[CI].data[SBI] != mem_rword(addr & ~0x3, dummy_timestamp)) + // { + // printf("Cache/Real Memory Mismatch: %08x %08x/%08x\n", addr & ~0x3, Cache[CI].data[SBI], mem_rword(addr & ~0x3, dummy_timestamp)); + // } + //} + + return (Cache[CI].data[SBI]); +} + +INLINE uint16 V810::RDOP(v810_timestamp_t ×tamp, uint32 addr, uint32 meow) +{ + uint16 ret; + + if (S_REG[CHCW] & 0x2) + { + uint32 d32 = RDCACHE(timestamp, addr); + ret = d32 >> ((addr & 2) * 8); + } + else + { + timestamp += meow; //++; + ret = MemRead16(timestamp, addr); + } + return (ret); +} + +#define BRANCH_ALIGN_CHECK(x) \ + { \ + if ((S_REG[CHCW] & 0x2) && (x & 0x2)) \ + { \ + ADDCLOCK(1); \ + } \ + } + +// Reinitialize the defaults in the CPU +void V810::Reset() +{ +#ifdef WANT_DEBUGGER + if (ADDBT) + ADDBT(GetPC(), 0xFFFFFFF0, 0xFFF0); +#endif + memset(&Cache, 0, sizeof(Cache)); + + memset(P_REG, 0, sizeof(P_REG)); + memset(S_REG, 0, sizeof(S_REG)); + memset(Cache, 0, sizeof(Cache)); + + P_REG[0] = 0x00000000; + SetPC(0xFFFFFFF0); + + S_REG[ECR] = 0x0000FFF0; + S_REG[PSW] = 0x00008000; + + if (VBMode) + S_REG[PIR] = 0x00005346; + else + S_REG[PIR] = 0x00008100; + + S_REG[TKCW] = 0x000000E0; + Halted = HALT_NONE; + ilevel = -1; + + lastop = 0; + + in_bstr = FALSE; + + RecalcIPendingCache(); +} + +bool V810::Init(V810_Emu_Mode mode, bool vb_mode) +{ + EmuMode = mode; + VBMode = vb_mode; + + in_bstr = FALSE; + in_bstr_to = 0; + + if (mode == V810_EMU_MODE_FAST) + { + memset(DummyRegion, 0, V810_FAST_MAP_PSIZE); + + for (unsigned int i = V810_FAST_MAP_PSIZE; i < V810_FAST_MAP_PSIZE + V810_FAST_MAP_TRAMPOLINE_SIZE; i += 2) + { + DummyRegion[i + 0] = 0; + DummyRegion[i + 1] = 0x36 << 2; + } + + for (uint64 A = 0; A < (1ULL << 32); A += V810_FAST_MAP_PSIZE) + FastMap[A / V810_FAST_MAP_PSIZE] = DummyRegion - A; + } + + return (TRUE); +} + +void V810::Kill(void) +{ + FastMapAllocList.clear(); +} + +void V810::SetInt(int level) +{ + assert(level >= -1 && level <= 15); + + ilevel = level; + RecalcIPendingCache(); +} + +uint8 *V810::SetFastMap(uint32 addresses[], uint32 length, unsigned int num_addresses, const char *name) +{ + for (unsigned int i = 0; i < num_addresses; i++) + { + assert((addresses[i] & (V810_FAST_MAP_PSIZE - 1)) == 0); + } + assert((length & (V810_FAST_MAP_PSIZE - 1)) == 0); + + FastMapAllocList.emplace_back(std::unique_ptr(new uint8[length + V810_FAST_MAP_TRAMPOLINE_SIZE])); + uint8 *ret = FastMapAllocList.back().get(); + + for (unsigned int i = length; i < length + V810_FAST_MAP_TRAMPOLINE_SIZE; i += 2) + { + ret[i + 0] = 0; + ret[i + 1] = 0x36 << 2; + } + + for (unsigned int i = 0; i < num_addresses; i++) + { + for (uint64 addr = addresses[i]; addr != (uint64)addresses[i] + length; addr += V810_FAST_MAP_PSIZE) + { + //printf("%08x, %d, %s\n", addr, length, name); + + FastMap[addr / V810_FAST_MAP_PSIZE] = ret - addresses[i]; + } + } + + return ret; +} + +void V810::SetMemReadBus32(uint8 A, bool value) +{ + MemReadBus32[A] = value; +} + +void V810::SetMemWriteBus32(uint8 A, bool value) +{ + MemWriteBus32[A] = value; +} + +void V810::SetMemReadHandlers(uint8 MDFN_FASTCALL (*read8)(v810_timestamp_t &, uint32), uint16 MDFN_FASTCALL (*read16)(v810_timestamp_t &, uint32), uint32 MDFN_FASTCALL (*read32)(v810_timestamp_t &, uint32)) +{ + MemRead8 = read8; + MemRead16 = read16; + MemRead32 = read32; +} + +void V810::SetMemWriteHandlers(void MDFN_FASTCALL (*write8)(v810_timestamp_t &, uint32, uint8), void MDFN_FASTCALL (*write16)(v810_timestamp_t &, uint32, uint16), void MDFN_FASTCALL (*write32)(v810_timestamp_t &, uint32, uint32)) +{ + MemWrite8 = write8; + MemWrite16 = write16; + MemWrite32 = write32; +} + +void V810::SetIOReadHandlers(uint8 MDFN_FASTCALL (*read8)(v810_timestamp_t &, uint32), uint16 MDFN_FASTCALL (*read16)(v810_timestamp_t &, uint32), uint32 MDFN_FASTCALL (*read32)(v810_timestamp_t &, uint32)) +{ + IORead8 = read8; + IORead16 = read16; + IORead32 = read32; +} + +void V810::SetIOWriteHandlers(void MDFN_FASTCALL (*write8)(v810_timestamp_t &, uint32, uint8), void MDFN_FASTCALL (*write16)(v810_timestamp_t &, uint32, uint16), void MDFN_FASTCALL (*write32)(v810_timestamp_t &, uint32, uint32)) +{ + IOWrite8 = write8; + IOWrite16 = write16; + IOWrite32 = write32; +} + +INLINE void V810::SetFlag(uint32 n, bool condition) +{ + S_REG[PSW] &= ~n; + + if (condition) + S_REG[PSW] |= n; +} + +INLINE void V810::SetSZ(uint32 value) +{ + SetFlag(PSW_Z, !value); + SetFlag(PSW_S, value & 0x80000000); +} + +#ifdef WANT_DEBUGGER +void V810::CheckBreakpoints(void (*callback)(int type, uint32 address, uint32 value, unsigned int len), uint16 MDFN_FASTCALL (*peek16)(const v810_timestamp_t, uint32), uint32 MDFN_FASTCALL (*peek32)(const v810_timestamp_t, uint32)) +{ + unsigned int opcode; + uint16 tmpop; + uint16 tmpop_high; + int32 ws_dummy = v810_timestamp; + uint32 tmp_PC = GetPC(); + + tmpop = peek16(ws_dummy, tmp_PC); + tmpop_high = peek16(ws_dummy, tmp_PC + 2); + + opcode = tmpop >> 10; + + // Uncomment this out later if necessary. + //if((tmpop & 0xE000) == 0x8000) // Special opcode format for + // opcode = (tmpop >> 9) & 0x7F; // type III instructions. + + switch (opcode) + { + case CAXI: + break; + + default: + break; + + case LD_B: + callback(BPOINT_READ, (sign_16(tmpop_high) + P_REG[tmpop & 0x1F]) & 0xFFFFFFFF, 0, 1); + break; + case LD_H: + callback(BPOINT_READ, (sign_16(tmpop_high) + P_REG[tmpop & 0x1F]) & 0xFFFFFFFE, 0, 2); + break; + case LD_W: + callback(BPOINT_READ, (sign_16(tmpop_high) + P_REG[tmpop & 0x1F]) & 0xFFFFFFFC, 0, 4); + break; + + case ST_B: + callback(BPOINT_WRITE, (sign_16(tmpop_high) + P_REG[tmpop & 0x1F]) & 0xFFFFFFFF, P_REG[(tmpop >> 5) & 0x1F] & 0x00FF, 1); + break; + case ST_H: + callback(BPOINT_WRITE, (sign_16(tmpop_high) + P_REG[tmpop & 0x1F]) & 0xFFFFFFFE, P_REG[(tmpop >> 5) & 0x1F] & 0xFFFF, 2); + break; + case ST_W: + callback(BPOINT_WRITE, (sign_16(tmpop_high) + P_REG[tmpop & 0x1F]) & 0xFFFFFFFC, P_REG[(tmpop >> 5) & 0x1F], 4); + break; + + case IN_B: + callback(BPOINT_IO_READ, (sign_16(tmpop_high) + P_REG[tmpop & 0x1F]) & 0xFFFFFFFF, 0, 1); + break; + case IN_H: + callback(BPOINT_IO_READ, (sign_16(tmpop_high) + P_REG[tmpop & 0x1F]) & 0xFFFFFFFE, 0, 2); + break; + case IN_W: + callback(BPOINT_IO_READ, (sign_16(tmpop_high) + P_REG[tmpop & 0x1F]) & 0xFFFFFFFC, 0, 4); + break; + + case OUT_B: + callback(BPOINT_IO_WRITE, (sign_16(tmpop_high) + P_REG[tmpop & 0x1F]) & 0xFFFFFFFF, P_REG[(tmpop >> 5) & 0x1F] & 0xFF, 1); + break; + case OUT_H: + callback(BPOINT_IO_WRITE, (sign_16(tmpop_high) + P_REG[tmpop & 0x1F]) & 0xFFFFFFFE, P_REG[(tmpop >> 5) & 0x1F] & 0xFFFF, 2); + break; + case OUT_W: + callback(BPOINT_IO_WRITE, (sign_16(tmpop_high) + P_REG[tmpop & 0x1F]) & 0xFFFFFFFC, P_REG[(tmpop >> 5) & 0x1F], 4); + break; + } +} +#endif + +#define SetPREG(n, val) \ + { \ + P_REG[n] = val; \ + } + +INLINE void V810::SetSREG(v810_timestamp_t ×tamp, unsigned int which, uint32 value) +{ + switch (which) + { + default: // Reserved + printf("LDSR to reserved system register: 0x%02x : 0x%08x\n", which, value); + break; + + case ECR: // Read-only + break; + + case PIR: // Read-only (obviously) + break; + + case TKCW: // Read-only + break; + + case EIPSW: + case FEPSW: + S_REG[which] = value & 0xFF3FF; + break; + + case PSW: + S_REG[which] = value & 0xFF3FF; + RecalcIPendingCache(); + break; + + case EIPC: + case FEPC: + S_REG[which] = value & 0xFFFFFFFE; + break; + + case ADDTRE: + S_REG[ADDTRE] = value & 0xFFFFFFFE; + printf("Address trap(unemulated): %08x\n", value); + break; + + case CHCW: + S_REG[CHCW] = value & 0x2; + + switch (value & 0x31) + { + default: + printf("Undefined cache control bit combination: %08x\n", value); + break; + + case 0x00: + break; + + case 0x01: + CacheClear(timestamp, (value >> 20) & 0xFFF, (value >> 8) & 0xFFF); + break; + + case 0x10: + CacheDump(timestamp, value & ~0xFF); + break; + + case 0x20: + CacheRestore(timestamp, value & ~0xFF); + break; + } + break; + } +} + +INLINE uint32 V810::GetSREG(unsigned int which) +{ + uint32 ret; + + if (which != 24 && which != 25 && which >= 8) + { + printf("STSR from reserved system register: 0x%02x", which); + } + + ret = S_REG[which]; + + return (ret); +} + +#define RB_SETPC(new_pc_raw) \ + { \ + const uint32 new_pc = new_pc_raw; /* So RB_SETPC(RB_GETPC()) won't mess up */ \ + if (RB_AccurateMode) \ + PC = new_pc; \ + else \ + { \ + PC_ptr = &FastMap[(new_pc) >> V810_FAST_MAP_SHIFT][(new_pc)]; \ + PC_base = PC_ptr - (new_pc); \ + } \ + } + +#define RB_PCRELCHANGE(delta) \ + { \ + if (RB_AccurateMode) \ + PC += (delta); \ + else \ + { \ + uint32 PC_tmp = RB_GETPC(); \ + PC_tmp += (delta); \ + RB_SETPC(PC_tmp); \ + } \ + } + +#define RB_INCPCBY2() \ + { \ + if (RB_AccurateMode) \ + PC += 2; \ + else \ + PC_ptr += 2; \ + } +#define RB_INCPCBY4() \ + { \ + if (RB_AccurateMode) \ + PC += 4; \ + else \ + PC_ptr += 4; \ + } + +#define RB_DECPCBY2() \ + { \ + if (RB_AccurateMode) \ + PC -= 2; \ + else \ + PC_ptr -= 2; \ + } +#define RB_DECPCBY4() \ + { \ + if (RB_AccurateMode) \ + PC -= 4; \ + else \ + PC_ptr -= 4; \ + } + +// Define accurate mode defines +#define RB_GETPC() PC +#define RB_RDOP(PC_offset, ...) RDOP(timestamp, PC + PC_offset, ##__VA_ARGS__) + +void V810::Run_Accurate(int32 MDFN_FASTCALL (*event_handler)(const v810_timestamp_t timestamp)) +{ + const bool RB_AccurateMode = true; + +#define RB_ADDBT(n, o, p) +#define RB_CPUHOOK(n) + +#include "v810_oploop.inc" + +#undef RB_CPUHOOK +#undef RB_ADDBT +} + +#ifdef WANT_DEBUGGER + +/* Make sure class member variable v810_timestamp is synchronized to our local copy, since we'll read it externally if a system + reset/power occurs when in step mode or similar. +*/ +#define RB_CPUHOOK_DBG(n) \ + { \ + if (CPUHook) \ + { \ + v810_timestamp = timestamp_rl; \ + CPUHook(timestamp_rl, n); \ + } \ + } + +void V810::Run_Accurate_Debug(int32 MDFN_FASTCALL (*event_handler)(const v810_timestamp_t timestamp)) +{ + const bool RB_AccurateMode = true; + +#define RB_ADDBT(n, o, p) \ + { \ + if (ADDBT) \ + ADDBT(n, o, p); \ + } +/* Make sure class member variable v810_timestamp is synchronized to our local copy, since we'll read it externally if a system + reset/power occurs when in step mode or similar. + */ +#define RB_CPUHOOK(n) RB_CPUHOOK_DBG(n) +#define RB_DEBUGMODE + +#include "v810_oploop.inc" + +#undef RB_DEBUGMODE +#undef RB_CPUHOOK +#undef RB_ADDBT +} +#endif + +// +// Undefine accurate mode defines +// +#undef RB_GETPC +#undef RB_RDOP + +// +// Define fast mode defines +// +#define RB_GETPC() ((uint32)(PC_ptr - PC_base)) + +#define RB_RDOP(PC_offset, ...) MDFN_de16lsb(&PC_ptr[PC_offset]) + +void V810::Run_Fast(int32 MDFN_FASTCALL (*event_handler)(const v810_timestamp_t timestamp)) +{ + const bool RB_AccurateMode = false; + +#define RB_ADDBT(n, o, p) +#define RB_CPUHOOK(n) + +#include "v810_oploop.inc" + +#undef RB_CPUHOOK +#undef RB_ADDBT +} + +#ifdef WANT_DEBUGGER +void V810::Run_Fast_Debug(int32 MDFN_FASTCALL (*event_handler)(const v810_timestamp_t timestamp)) +{ + const bool RB_AccurateMode = false; + +#define RB_ADDBT(n, o, p) \ + { \ + if (ADDBT) \ + ADDBT(n, o, p); \ + } +#define RB_CPUHOOK(n) RB_CPUHOOK_DBG(n) +#define RB_DEBUGMODE + +#include "v810_oploop.inc" + +#undef RB_DEBUGMODE +#undef RB_CPUHOOK +#undef RB_ADDBT +} +#endif + +// +// Undefine fast mode defines +// +#undef RB_GETPC +#undef RB_RDOP + +v810_timestamp_t V810::Run(int32 MDFN_FASTCALL (*event_handler)(const v810_timestamp_t timestamp)) +{ + Running = true; + +#ifdef WANT_DEBUGGER + if (CPUHook || ADDBT) + { + if (EmuMode == V810_EMU_MODE_FAST) + Run_Fast_Debug(event_handler); + else + Run_Accurate_Debug(event_handler); + } + else +#endif + { + if (EmuMode == V810_EMU_MODE_FAST) + Run_Fast(event_handler); + else + Run_Accurate(event_handler); + } + return (v810_timestamp); +} + +void V810::Exit(void) +{ + Running = false; +} + +#ifdef WANT_DEBUGGER +void V810::SetCPUHook(void (*newhook)(const v810_timestamp_t timestamp, uint32 PC), void (*new_ADDBT)(uint32 old_PC, uint32 new_PC, uint32)) +{ + CPUHook = newhook; + ADDBT = new_ADDBT; +} +#endif + +uint32 V810::GetRegister(unsigned int which, char *special, const uint32 special_len) +{ + if (which >= GSREG_PR && which <= GSREG_PR + 31) + { + return GetPR(which - GSREG_PR); + } + else if (which >= GSREG_SR && which <= GSREG_SR + 31) + { + uint32 val = GetSREG(which - GSREG_SR); + + if (special && which == GSREG_SR + PSW) + { + trio_snprintf(special, special_len, "Z: %d, S: %d, OV: %d, CY: %d, ID: %d, AE: %d, EP: %d, NP: %d, IA: %2d", + (int)(bool)(val & PSW_Z), (int)(bool)(val & PSW_S), (int)(bool)(val & PSW_OV), (int)(bool)(val & PSW_CY), + (int)(bool)(val & PSW_ID), (int)(bool)(val & PSW_AE), (int)(bool)(val & PSW_EP), (int)(bool)(val & PSW_NP), + (val & PSW_IA) >> 16); + } + + return val; + } + else if (which == GSREG_PC) + { + return GetPC(); + } + else if (which == GSREG_TIMESTAMP) + { + return v810_timestamp; + } + + return 0xDEADBEEF; +} + +void V810::SetRegister(unsigned int which, uint32 value) +{ + if (which >= GSREG_PR && which <= GSREG_PR + 31) + { + if (which) + P_REG[which - GSREG_PR] = value; + } + else if (which >= GSREG_SR && which <= GSREG_SR + 31) + { + // SetSREG(timestamp, which - GSREG_SR, value); + } + else if (which == GSREG_PC) + { + SetPC(value & ~1); + } + else if (which == GSREG_TIMESTAMP) + { + //v810_timestamp = value; + } +} + +uint32 V810::GetPC(void) +{ + if (EmuMode == V810_EMU_MODE_ACCURATE) + return (PC); + else + { + return (PC_ptr - PC_base); + } +} + +void V810::SetPC(uint32 new_pc) +{ + if (EmuMode == V810_EMU_MODE_ACCURATE) + PC = new_pc; + else + { + PC_ptr = &FastMap[new_pc >> V810_FAST_MAP_SHIFT][new_pc]; + PC_base = PC_ptr - new_pc; + } +} + +#define BSTR_OP_MOV \ + dst_cache &= ~(1 << dstoff); \ + dst_cache |= ((src_cache >> srcoff) & 1) << dstoff; +#define BSTR_OP_NOT \ + dst_cache &= ~(1 << dstoff); \ + dst_cache |= (((src_cache >> srcoff) & 1) ^ 1) << dstoff; + +#define BSTR_OP_XOR dst_cache ^= ((src_cache >> srcoff) & 1) << dstoff; +#define BSTR_OP_OR dst_cache |= ((src_cache >> srcoff) & 1) << dstoff; +#define BSTR_OP_AND dst_cache &= ~((((src_cache >> srcoff) & 1) ^ 1) << dstoff); + +#define BSTR_OP_XORN dst_cache ^= (((src_cache >> srcoff) & 1) ^ 1) << dstoff; +#define BSTR_OP_ORN dst_cache |= (((src_cache >> srcoff) & 1) ^ 1) << dstoff; +#define BSTR_OP_ANDN dst_cache &= ~(((src_cache >> srcoff) & 1) << dstoff); + +INLINE uint32 V810::BSTR_RWORD(v810_timestamp_t ×tamp, uint32 A) +{ + if (MemReadBus32[A >> 24]) + { + timestamp += 2; + return (MemRead32(timestamp, A)); + } + else + { + uint32 ret; + + timestamp += 2; + ret = MemRead16(timestamp, A); + + timestamp += 2; + ret |= MemRead16(timestamp, A | 2) << 16; + return (ret); + } +} + +INLINE void V810::BSTR_WWORD(v810_timestamp_t ×tamp, uint32 A, uint32 V) +{ + if (MemWriteBus32[A >> 24]) + { + timestamp += 2; + MemWrite32(timestamp, A, V); + } + else + { + timestamp += 2; + MemWrite16(timestamp, A, V & 0xFFFF); + + timestamp += 2; + MemWrite16(timestamp, A | 2, V >> 16); + } +} + +#define DO_BSTR(op) \ + { \ + while (len) \ + { \ + if (!have_src_cache) \ + { \ + have_src_cache = TRUE; \ + src_cache = BSTR_RWORD(timestamp, src); \ + } \ + \ + if (!have_dst_cache) \ + { \ + have_dst_cache = TRUE; \ + dst_cache = BSTR_RWORD(timestamp, dst); \ + } \ + \ + op; \ + srcoff = (srcoff + 1) & 0x1F; \ + dstoff = (dstoff + 1) & 0x1F; \ + len--; \ + \ + if (!srcoff) \ + { \ + src += 4; \ + have_src_cache = FALSE; \ + } \ + \ + if (!dstoff) \ + { \ + BSTR_WWORD(timestamp, dst, dst_cache); \ + dst += 4; \ + have_dst_cache = FALSE; \ + if (timestamp >= next_event_ts) \ + break; \ + } \ + } \ + if (have_dst_cache) \ + BSTR_WWORD(timestamp, dst, dst_cache); \ + } + +INLINE bool V810::Do_BSTR_Search(v810_timestamp_t ×tamp, const int inc_mul, unsigned int bit_test) +{ + uint32 srcoff = (P_REG[27] & 0x1F); + uint32 len = P_REG[28]; + uint32 bits_skipped = P_REG[29]; + uint32 src = (P_REG[30] & 0xFFFFFFFC); + bool found = false; + +#if 0 + // TODO: Better timing. + if(!in_bstr) // If we're just starting the execution of this instruction(kind of spaghetti-code), so FIXME if we change + // bstr handling in v810_oploop.inc + { + timestamp += 13 - 1; + } +#endif + + while (len) + { + if (!have_src_cache) + { + have_src_cache = TRUE; + timestamp++; + src_cache = BSTR_RWORD(timestamp, src); + } + + if (((src_cache >> srcoff) & 1) == bit_test) + { + found = true; + + /* Fix the bit offset and word address to "1 bit before" it was found */ + srcoff -= inc_mul * 1; + if (srcoff & 0x20) /* Handles 0x1F->0x20(0x00) and 0x00->0xFFFF... */ + { + src -= inc_mul * 4; + srcoff &= 0x1F; + } + break; + } + srcoff = (srcoff + inc_mul * 1) & 0x1F; + bits_skipped++; + len--; + + if (!srcoff) + { + have_src_cache = FALSE; + src += inc_mul * 4; + if (timestamp >= next_event_ts) + break; + } + } + + P_REG[27] = srcoff; + P_REG[28] = len; + P_REG[29] = bits_skipped; + P_REG[30] = src; + + if (found) // Set Z flag to 0 if the bit was found + SetFlag(PSW_Z, 0); + else if (!len) // ...and if the search is over, and the bit was not found, set it to 1 + SetFlag(PSW_Z, 1); + + if (found) // Bit found, so don't continue the search. + return (false); + + return ((bool)len); // Continue the search if any bits are left to search. +} + +bool V810::bstr_subop(v810_timestamp_t ×tamp, int sub_op, int arg1) +{ + if ((sub_op >= 0x10) || (!(sub_op & 0x8) && sub_op >= 0x4)) + { + printf("%08x\tBSR Error: %04x\n", PC, sub_op); + + SetPC(GetPC() - 2); + Exception(INVALID_OP_HANDLER_ADDR, ECODE_INVALID_OP); + + return (false); + } + + // printf("BSTR: %02x, %02x %02x; src: %08x, dst: %08x, len: %08x\n", sub_op, P_REG[27], P_REG[26], P_REG[30], P_REG[29], P_REG[28]); + + if (sub_op & 0x08) + { + uint32 dstoff = (P_REG[26] & 0x1F); + uint32 srcoff = (P_REG[27] & 0x1F); + uint32 len = P_REG[28]; + uint32 dst = (P_REG[29] & 0xFFFFFFFC); + uint32 src = (P_REG[30] & 0xFFFFFFFC); + +#if 0 + // Be careful not to cause 32-bit integer overflow, and careful about not shifting by 32. + // TODO: + + // Read src[0], src[4] into shifter. + // Read dest[0]. + DO_BSTR_PROLOGUE(); // if(len) { blah blah blah masking blah } + src_cache = BSTR_RWORD(timestamp, src); + + if((uint64)(srcoff + len) > 0x20) + src_cache |= (uint64)BSTR_RWORD(timestamp, src + 4) << 32; + + dst_cache = BSTR_RWORD(timestamp, dst); + + if(len) + { + uint32 dst_preserve_mask; + uint32 dst_change_mask; + + dst_preserve_mask = (1U << dstoff) - 1; + + if((uint64)(dstoff + len) < 0x20) + dst_preserve_mask |= ((1U << ((0x20 - (dstoff + len)) & 0x1F)) - 1) << (dstoff + len); + + dst_change_mask = ~dst_preserve_mask; + + src_cache = BSTR_RWORD(timestamp, src); + src_cache |= (uint64)BSTR_RWORD(timestamp, src + 4) << 32; + dst_cache = BSTR_RWORD(timestamp, dst); + + dst_cache = (dst_cache & dst_preserve_mask) | ((dst_cache OP_THINGY_HERE (src_cache >> srcoff)) & dst_change_mask); + BSTR_WWORD(timestamp, dst, dst_cache); + + if((uint64)(dstoff + len) < 0x20) + { + srcoff += len; + dstoff += len; + len = 0; + } + else + { + srcoff += (0x20 - dstoff); + dstoff = 0; + len -= (0x20 - dstoff); + dst += 4; + } + + if(srcoff >= 0x20) + { + srcoff &= 0x1F; + src += 4; + + if(len) + { + src_cache >>= 32; + src_cache |= (uint64)BSTR_RWORD(timestamp, src + 4) << 32; + } + } + } + + DO_BSTR_PRIMARY(); // while(len >= 32) (do allow interruption; interrupt and emulator-return - + // they must be handled differently!) + while(len >= 32) + { + dst_cache = BSTR_RWORD(timestamp, dst); + dst_cache = OP_THINGY_HERE(dst_cache, src_cache >> srcoff); + BSTR_WWORD(timestamp, dst, dst_cache); + len -= 32; + dst += 4; + src += 4; + src_cache >>= 32; + src_cache |= (uint64)BSTR_RWORD(timestamp, src + 4) << 32; + } + + DO_BSTR_EPILOGUE(); // if(len) { blah blah blah masking blah } + if(len) + { + uint32 dst_preserve_mask; + uint32 dst_change_mask; + + dst_preserve_mask = (1U << ((0x20 - len) & 0x1F) << len; + dst_change_mask = ~dst_preserve_mask; + + dst_cache = BSTR_RWORD(timestamp, dst); + dst_cache = OP_THINGY_HERE(dst_cache, src_cache >> srcoff); + BSTR_WWORD(timestamp, dst, dst_cache); + dstoff += len; + srcoff += len; + + if(srcoff >= 0x20) + { + srcoff &= 0x1F; + src += 4; + } + len = 0; + } +#endif + + switch (sub_op) + { + case ORBSU: + DO_BSTR(BSTR_OP_OR); + break; + + case ANDBSU: + DO_BSTR(BSTR_OP_AND); + break; + + case XORBSU: + DO_BSTR(BSTR_OP_XOR); + break; + + case MOVBSU: + DO_BSTR(BSTR_OP_MOV); + break; + + case ORNBSU: + DO_BSTR(BSTR_OP_ORN); + break; + + case ANDNBSU: + DO_BSTR(BSTR_OP_ANDN); + break; + + case XORNBSU: + DO_BSTR(BSTR_OP_XORN); + break; + + case NOTBSU: + DO_BSTR(BSTR_OP_NOT); + break; + } + + P_REG[26] = dstoff; + P_REG[27] = srcoff; + P_REG[28] = len; + P_REG[29] = dst; + P_REG[30] = src; + + return ((bool)P_REG[28]); + } + else + { + printf("BSTR Search: %02x\n", sub_op); + return (Do_BSTR_Search(timestamp, ((sub_op & 1) ? -1 : 1), (sub_op & 0x2) >> 1)); + } + assert(0); + return (false); +} + +INLINE void V810::SetFPUOPNonFPUFlags(uint32 result) +{ + // Now, handle flag setting + SetFlag(PSW_OV, 0); + + if (!(result & 0x7FFFFFFF)) // Check to see if exponent and mantissa are 0 + { + // If Z flag is set, S and CY should be clear, even if it's negative 0(confirmed on real thing with subf.s, at least). + SetFlag(PSW_Z, 1); + SetFlag(PSW_S, 0); + SetFlag(PSW_CY, 0); + } + else + { + SetFlag(PSW_Z, 0); + SetFlag(PSW_S, result & 0x80000000); + SetFlag(PSW_CY, result & 0x80000000); + } + //printf("MEOW: %08x\n", S_REG[PSW] & (PSW_S | PSW_CY)); +} + +bool V810::FPU_DoesExceptionKillResult(void) +{ + const uint32 float_exception_flags = fpo.get_flags(); + + if (float_exception_flags & V810_FP_Ops::flag_reserved) + return (true); + + if (float_exception_flags & V810_FP_Ops::flag_invalid) + return (true); + + if (float_exception_flags & V810_FP_Ops::flag_divbyzero) + return (true); + + // Return false here, so that the result of this calculation IS put in the output register. + // Wrap the exponent on overflow, rather than generating an infinity. The wrapping behavior is specified in IEE 754 AFAIK, + // and is useful in cases where you divide a huge number + // by another huge number, and fix the result afterwards based on the number of overflows that occurred. Probably requires some custom assembly code, + // though. And it's the kind of thing you'd see in an engineering or physics program, not in a perverted video game :b). + if (float_exception_flags & V810_FP_Ops::flag_overflow) + return (false); + + return (false); +} + +void V810::FPU_DoException(void) +{ + const uint32 float_exception_flags = fpo.get_flags(); + + if (float_exception_flags & V810_FP_Ops::flag_reserved) + { + S_REG[PSW] |= PSW_FRO; + + SetPC(GetPC() - 4); + Exception(FPU_HANDLER_ADDR, ECODE_FRO); + + return; + } + + if (float_exception_flags & V810_FP_Ops::flag_invalid) + { + S_REG[PSW] |= PSW_FIV; + + SetPC(GetPC() - 4); + Exception(FPU_HANDLER_ADDR, ECODE_FIV); + + return; + } + + if (float_exception_flags & V810_FP_Ops::flag_divbyzero) + { + S_REG[PSW] |= PSW_FZD; + + SetPC(GetPC() - 4); + Exception(FPU_HANDLER_ADDR, ECODE_FZD); + + return; + } + + if (float_exception_flags & V810_FP_Ops::flag_underflow) + { + S_REG[PSW] |= PSW_FUD; + } + + if (float_exception_flags & V810_FP_Ops::flag_inexact) + { + S_REG[PSW] |= PSW_FPR; + } + + // + // FPR can be set along with overflow, so put the overflow exception handling at the end here(for Exception() messes with PSW). + // + if (float_exception_flags & V810_FP_Ops::flag_overflow) + { + S_REG[PSW] |= PSW_FOV; + + SetPC(GetPC() - 4); + Exception(FPU_HANDLER_ADDR, ECODE_FOV); + } +} + +bool V810::IsSubnormal(uint32 fpval) +{ + if (((fpval >> 23) & 0xFF) == 0 && (fpval & ((1 << 23) - 1))) + return (true); + + return (false); +} + +INLINE void V810::FPU_Math_Template(uint32 (V810_FP_Ops::*func)(uint32, uint32), uint32 arg1, uint32 arg2) +{ + uint32 result; + + fpo.clear_flags(); + result = (fpo.*func)(P_REG[arg1], P_REG[arg2]); + + if (!FPU_DoesExceptionKillResult()) + { + SetFPUOPNonFPUFlags(result); + SetPREG(arg1, result); + } + FPU_DoException(); +} + +void V810::fpu_subop(v810_timestamp_t ×tamp, int sub_op, int arg1, int arg2) +{ + //printf("FPU: %02x\n", sub_op); + if (VBMode) + { + switch (sub_op) + { + case XB: + timestamp++; // Unknown + P_REG[arg1] = (P_REG[arg1] & 0xFFFF0000) | ((P_REG[arg1] & 0xFF) << 8) | ((P_REG[arg1] & 0xFF00) >> 8); + return; + + case XH: + timestamp++; // Unknown + P_REG[arg1] = (P_REG[arg1] << 16) | (P_REG[arg1] >> 16); + return; + + // Does REV use arg1 or arg2 for the source register? + case REV: + timestamp++; // Unknown + printf("Revvie bits\n"); + { + // Public-domain code snippet from: http://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel + uint32 v = P_REG[arg2]; // 32-bit word to reverse bit order + + // swap odd and even bits + v = ((v >> 1) & 0x55555555) | ((v & 0x55555555) << 1); + // swap consecutive pairs + v = ((v >> 2) & 0x33333333) | ((v & 0x33333333) << 2); + // swap nibbles ... + v = ((v >> 4) & 0x0F0F0F0F) | ((v & 0x0F0F0F0F) << 4); + // swap bytes + v = ((v >> 8) & 0x00FF00FF) | ((v & 0x00FF00FF) << 8); + // swap 2-byte long pairs + v = (v >> 16) | (v << 16); + + P_REG[arg1] = v; + } + return; + + case MPYHW: + timestamp += 9 - 1; // Unknown? + P_REG[arg1] = (int32)(int16)(P_REG[arg1] & 0xFFFF) * (int32)(int16)(P_REG[arg2] & 0xFFFF); + return; + } + } + + switch (sub_op) + { + // Virtual-Boy specific(probably!) + default: + { + SetPC(GetPC() - 4); + Exception(INVALID_OP_HANDLER_ADDR, ECODE_INVALID_OP); + } + break; + + case CVT_WS: + timestamp += 5; + { + uint32 result; + + fpo.clear_flags(); + result = fpo.itof(P_REG[arg2]); + + if (!FPU_DoesExceptionKillResult()) + { + SetPREG(arg1, result); + SetFPUOPNonFPUFlags(result); + } + FPU_DoException(); + } + break; // End CVT.WS + + case CVT_SW: + timestamp += 8; + { + int32 result; + + fpo.clear_flags(); + result = fpo.ftoi(P_REG[arg2], false); + + if (!FPU_DoesExceptionKillResult()) + { + SetPREG(arg1, result); + SetFlag(PSW_OV, 0); + SetSZ(result); + } + FPU_DoException(); + } + break; // End CVT.SW + + case ADDF_S: + timestamp += 8; + FPU_Math_Template(&V810_FP_Ops::add, arg1, arg2); + break; + + case SUBF_S: + timestamp += 11; + FPU_Math_Template(&V810_FP_Ops::sub, arg1, arg2); + break; + + case CMPF_S: + timestamp += 6; + // Don't handle this like subf.s because the flags + // have slightly different semantics(mostly regarding underflow/subnormal results) (confirmed on real V810). + fpo.clear_flags(); + { + int32 result; + + result = fpo.cmp(P_REG[arg1], P_REG[arg2]); + + if (!FPU_DoesExceptionKillResult()) + { + SetFPUOPNonFPUFlags(result); + } + FPU_DoException(); + } + break; + + case MULF_S: + timestamp += 7; + FPU_Math_Template(&V810_FP_Ops::mul, arg1, arg2); + break; + + case DIVF_S: + timestamp += 43; + FPU_Math_Template(&V810_FP_Ops::div, arg1, arg2); + break; + + case TRNC_SW: + timestamp += 7; + { + int32 result; + + fpo.clear_flags(); + result = fpo.ftoi(P_REG[arg2], true); + + if (!FPU_DoesExceptionKillResult()) + { + SetPREG(arg1, result); + SetFlag(PSW_OV, 0); + SetSZ(result); + } + FPU_DoException(); + } + break; // end TRNC.SW + } +} + +// Generate exception +void V810::Exception(uint32 handler, uint16 eCode) +{ +// Exception overhead is unknown. + +#ifdef WANT_DEBUGGER + if (ADDBT) + { + uint32 old_PC = GetPC(); + + if ((eCode & 0xFFE0) == 0xFFA0) // Trap instruction(PC is pointing to next instruction at this point) + old_PC -= 2; + + ADDBT(old_PC, handler, eCode); + } +#endif + + printf("Exception: %08x %04x\n", handler, eCode); + + // Invalidate our bitstring state(forces the instruction to be re-read, and the r/w buffers reloaded). + in_bstr = FALSE; + have_src_cache = FALSE; + have_dst_cache = FALSE; + + if (S_REG[PSW] & PSW_NP) // Fatal exception + { + printf("Fatal exception; Code: %08x, ECR: %08x, PSW: %08x, PC: %08x\n", eCode, S_REG[ECR], S_REG[PSW], PC); + Halted = HALT_FATAL_EXCEPTION; + IPendingCache = 0; + return; + } + else if (S_REG[PSW] & PSW_EP) //Double Exception + { + S_REG[FEPC] = GetPC(); + S_REG[FEPSW] = S_REG[PSW]; + + S_REG[ECR] = (S_REG[ECR] & 0xFFFF) | (eCode << 16); + S_REG[PSW] |= PSW_NP; + S_REG[PSW] |= PSW_ID; + S_REG[PSW] &= ~PSW_AE; + + SetPC(0xFFFFFFD0); + IPendingCache = 0; + return; + } + else // Regular exception + { + S_REG[EIPC] = GetPC(); + S_REG[EIPSW] = S_REG[PSW]; + S_REG[ECR] = (S_REG[ECR] & 0xFFFF0000) | eCode; + S_REG[PSW] |= PSW_EP; + S_REG[PSW] |= PSW_ID; + S_REG[PSW] &= ~PSW_AE; + + SetPC(handler); + IPendingCache = 0; + return; + } +} diff --git a/waterbox/pcfx/v810/v810_cpu.h b/waterbox/pcfx/v810/v810_cpu.h new file mode 100644 index 0000000000..dff584bbe4 --- /dev/null +++ b/waterbox/pcfx/v810/v810_cpu.h @@ -0,0 +1,362 @@ +//////////////////////////////////////////////////////////////// +// Defines for the V810 CPU + +#ifndef V810_CPU_H_ +#define V810_CPU_H_ + +#include + +#include "v810_fp_ops.h" + +typedef int32 v810_timestamp_t; + +#define V810_FAST_MAP_SHIFT 16 +#define V810_FAST_MAP_PSIZE (1 << V810_FAST_MAP_SHIFT) +#define V810_FAST_MAP_TRAMPOLINE_SIZE 1024 + +// Exception codes +enum +{ + ECODE_TRAP_BASE = 0xFFA0, + ECODE_INVALID_OP = 0xFF90, + ECODE_ZERO_DIV = 0xFF80, // Integer divide by 0 + ECODE_FIV = 0xFF70, // Floating point invalid operation + ECODE_FZD = 0xFF68, // Floating point zero division + ECODE_FOV = 0xFF64, // Floating point overflow +//#define ECODE_FUD 0xFF62 // Floating point underflow(unused on V810) +//#define ECODE_FPR 0xFF61 // Floating point precision degradation(unused on V810) + ECODE_FRO = 0xFF60 // Floating point reserved operand +}; + +enum +{ + INVALID_OP_HANDLER_ADDR = 0xFFFFFF90, // Invalid opcode/instruction code! + ZERO_DIV_HANDLER_ADDR = 0xFFFFFF80, // Integer divide by 0 exception + FPU_HANDLER_ADDR = 0xFFFFFF60, // FPU exception + TRAP_HANDLER_BASE = 0xFFFFFFA0 // TRAP instruction +}; + +//System Register Defines (these are the only valid system registers!) +#define EIPC 0 //Exeption/Interupt PC +#define EIPSW 1 //Exeption/Interupt PSW + +#define FEPC 2 //Fatal Error PC +#define FEPSW 3 //Fatal Error PSW + +#define ECR 4 //Exception Cause Register +#define PSW 5 //Program Status Word +#define PIR 6 //Processor ID Register +#define TKCW 7 //Task Controll Word +#define CHCW 24 //Cashe Controll Word +#define ADDTRE 25 //ADDTRE + +//PSW Specifics +#define PSW_IA 0xF0000 // All Interupt bits... +#define PSW_I3 0x80000 +#define PSW_I2 0x40000 +#define PSW_I1 0x20000 +#define PSW_I0 0x10000 + +#define PSW_NP 0x08000 +#define PSW_EP 0x04000 + +#define PSW_AE 0x02000 + +#define PSW_ID 0x01000 + +#define PSW_FRO 0x00200 // Floating point reserved operand(set on denormal, NaN, or indefinite) +#define PSW_FIV 0x00100 // Floating point invalid operation(set when trying to convert a number too large to an (un)signed integer) + +#define PSW_FZD 0x00080 // Floating point divide by zero +#define PSW_FOV 0x00040 // Floating point overflow +#define PSW_FUD 0x00020 // Floating point underflow +#define PSW_FPR 0x00010 // Floating point precision degradation + +#define PSW_CY 0x00008 +#define PSW_OV 0x00004 +#define PSW_S 0x00002 +#define PSW_Z 0x00001 + +//condition codes +#define COND_V 0 +#define COND_C 1 +#define COND_Z 2 +#define COND_NH 3 +#define COND_S 4 +#define COND_T 5 +#define COND_LT 6 +#define COND_LE 7 +#define COND_NV 8 +#define COND_NC 9 +#define COND_NZ 10 +#define COND_H 11 +#define COND_NS 12 +#define COND_F 13 +#define COND_GE 14 +#define COND_GT 15 + +#define TESTCOND_V (S_REG[PSW]&PSW_OV) + +#define TESTCOND_L (S_REG[PSW]&PSW_CY) +#define TESTCOND_C TESTCOND_L + +#define TESTCOND_E (S_REG[PSW]&PSW_Z) +#define TESTCOND_Z TESTCOND_E + +#define TESTCOND_NH ( (S_REG[PSW]&PSW_Z) || (S_REG[PSW]&PSW_CY) ) +#define TESTCOND_N (S_REG[PSW]&PSW_S) +#define TESTCOND_S TESTCOND_N + +#define TESTCOND_LT ( (!!(S_REG[PSW]&PSW_S)) ^ (!!(S_REG[PSW]&PSW_OV)) ) +#define TESTCOND_LE ( ((!!(S_REG[PSW]&PSW_S)) ^ (!!(S_REG[PSW]&PSW_OV))) || (S_REG[PSW]&PSW_Z) ) +#define TESTCOND_NV (!(S_REG[PSW]&PSW_OV)) + +#define TESTCOND_NL (!(S_REG[PSW]&PSW_CY)) +#define TESTCOND_NC TESTCOND_NL + +#define TESTCOND_NE (!(S_REG[PSW]&PSW_Z)) +#define TESTCOND_NZ TESTCOND_NE + +#define TESTCOND_H ( !((S_REG[PSW]&PSW_Z) || (S_REG[PSW]&PSW_CY)) ) +#define TESTCOND_P (!(S_REG[PSW] & PSW_S)) +#define TESTCOND_NS TESTCOND_P + +#define TESTCOND_GE (!((!!(S_REG[PSW]&PSW_S))^(!!(S_REG[PSW]&PSW_OV)))) +#define TESTCOND_GT (! (((!!(S_REG[PSW]&PSW_S))^(!!(S_REG[PSW]&PSW_OV))) || (S_REG[PSW]&PSW_Z)) ) + +// Tag layout +// Bit 0-21: TAG31-TAG10 +// Bit 22-23: Validity bits(one for each 4-byte subblock) +// Bit 24-27: NECRV("Reserved") +// Bit 28-31: 0 + +typedef enum +{ + V810_EMU_MODE_FAST = 0, + V810_EMU_MODE_ACCURATE = 1, + _V810_EMU_MODE_COUNT +} V810_Emu_Mode; + +class V810 +{ + public: + + V810() MDFN_COLD; + ~V810() MDFN_COLD; + + // Pass TRUE for vb_mode if we're emulating a VB-specific enhanced V810 CPU core + bool Init(V810_Emu_Mode mode, bool vb_mode) MDFN_COLD; + void Kill(void) MDFN_COLD; + + void SetInt(int level); + + void SetMemWriteBus32(uint8 A, bool value) MDFN_COLD; + void SetMemReadBus32(uint8 A, bool value) MDFN_COLD; + + void SetMemReadHandlers(uint8 MDFN_FASTCALL (*read8)(v810_timestamp_t &, uint32), uint16 MDFN_FASTCALL (*read16)(v810_timestamp_t &, uint32), uint32 MDFN_FASTCALL (*read32)(v810_timestamp_t &, uint32)) MDFN_COLD; + void SetMemWriteHandlers(void MDFN_FASTCALL (*write8)(v810_timestamp_t &, uint32, uint8), void MDFN_FASTCALL (*write16)(v810_timestamp_t &, uint32, uint16), void MDFN_FASTCALL (*write32)(v810_timestamp_t &, uint32, uint32)) MDFN_COLD; + + void SetIOReadHandlers(uint8 MDFN_FASTCALL (*read8)(v810_timestamp_t &, uint32), uint16 MDFN_FASTCALL (*read16)(v810_timestamp_t &, uint32), uint32 MDFN_FASTCALL (*read32)(v810_timestamp_t &, uint32)) MDFN_COLD; + void SetIOWriteHandlers(void MDFN_FASTCALL (*write8)(v810_timestamp_t &, uint32, uint8), void MDFN_FASTCALL (*write16)(v810_timestamp_t &, uint32, uint16), void MDFN_FASTCALL (*write32)(v810_timestamp_t &, uint32, uint32)) MDFN_COLD; + + // Length specifies the number of bytes to map in, at each location specified by addresses[] (for mirroring) + uint8 *SetFastMap(uint32 addresses[], uint32 length, unsigned int num_addresses, const char *name) MDFN_COLD; + + INLINE void ResetTS(v810_timestamp_t new_base_timestamp) + { + assert(next_event_ts > v810_timestamp); + + next_event_ts -= (v810_timestamp - new_base_timestamp); + v810_timestamp = new_base_timestamp; + } + + INLINE void SetEventNT(const v810_timestamp_t timestamp) + { + next_event_ts = timestamp; + } + + INLINE v810_timestamp_t GetEventNT(void) + { + return(next_event_ts); + } + + v810_timestamp_t Run(int32 MDFN_FASTCALL (*event_handler)(const v810_timestamp_t timestamp)); + void Exit(void); + + void Reset(void) MDFN_COLD; + + #ifdef WANT_DEBUGGER + void CheckBreakpoints(void (*callback)(int type, uint32 address, uint32 value, unsigned int len), uint16 MDFN_FASTCALL (*peek16)(const v810_timestamp_t, uint32), uint32 MDFN_FASTCALL (*peek32)(const v810_timestamp_t, uint32)); + void SetCPUHook(void (*newhook)(const v810_timestamp_t timestamp, uint32 PC), void (*new_ADDBT)(uint32, uint32, uint32)); + #endif + + enum + { + GSREG_PR = 0, + GSREG_SR = 32, + GSREG_PC = 64, + GSREG_TIMESTAMP + }; + + uint32 GetRegister(unsigned int which, char *special, const uint32 special_len); + void SetRegister(unsigned int which, uint32 value); + + uint32 GetPC(void); + void SetPC(uint32); + + INLINE uint32 GetPR(unsigned int which) + { + return which ? P_REG[which] : 0; + } + + private: + + // Make sure P_REG[] is the first variable/array in this class, so non-zerfo offset encoding(at assembly level) isn't necessary to access it. + uint32 P_REG[32]; // Program registers pr0-pr31 + uint32 S_REG[32]; // System registers sr0-sr31 + uint32 PC; + uint8 *PC_ptr; + uint8 *PC_base; + + uint32 IPendingCache; + void RecalcIPendingCache(void); + + public: + v810_timestamp_t v810_timestamp; // Will never be less than 0. + + private: + v810_timestamp_t next_event_ts; + + enum + { + LASTOP_NORMAL = 0, + LASTOP_LOAD = 1, + LASTOP_STORE = 2, + LASTOP_IN = 3, + LASTOP_OUT = 4, + LASTOP_HEAVY_MATH = 5 + }; + + V810_Emu_Mode EmuMode; + bool VBMode; + + void Run_Fast(int32 MDFN_FASTCALL (*event_handler)(const v810_timestamp_t timestamp)) NO_INLINE; + void Run_Accurate(int32 MDFN_FASTCALL (*event_handler)(const v810_timestamp_t timestamp)) NO_INLINE; + + #ifdef WANT_DEBUGGER + void Run_Fast_Debug(int32 MDFN_FASTCALL (*event_handler)(const v810_timestamp_t timestamp)) NO_INLINE; + void Run_Accurate_Debug(int32 MDFN_FASTCALL (*event_handler)(const v810_timestamp_t timestamp)) NO_INLINE; + #endif + + uint8 MDFN_FASTCALL (*MemRead8)(v810_timestamp_t ×tamp, uint32 A); + uint16 MDFN_FASTCALL (*MemRead16)(v810_timestamp_t ×tamp, uint32 A); + uint32 MDFN_FASTCALL (*MemRead32)(v810_timestamp_t ×tamp, uint32 A); + + void MDFN_FASTCALL (*MemWrite8)(v810_timestamp_t ×tamp, uint32 A, uint8 V); + void MDFN_FASTCALL (*MemWrite16)(v810_timestamp_t ×tamp, uint32 A, uint16 V); + void MDFN_FASTCALL (*MemWrite32)(v810_timestamp_t ×tamp, uint32 A, uint32 V); + + uint8 MDFN_FASTCALL (*IORead8)(v810_timestamp_t ×tamp, uint32 A); + uint16 MDFN_FASTCALL (*IORead16)(v810_timestamp_t ×tamp, uint32 A); + uint32 MDFN_FASTCALL (*IORead32)(v810_timestamp_t ×tamp, uint32 A); + + void MDFN_FASTCALL (*IOWrite8)(v810_timestamp_t ×tamp, uint32 A, uint8 V); + void MDFN_FASTCALL (*IOWrite16)(v810_timestamp_t ×tamp, uint32 A, uint16 V); + void MDFN_FASTCALL (*IOWrite32)(v810_timestamp_t ×tamp, uint32 A, uint32 V); + + bool MemReadBus32[256]; // Corresponding to the upper 8 bits of the memory address map. + bool MemWriteBus32[256]; + + int32 lastop; // Set to -1 on FP/MUL/DIV, 0x100 on LD, 0x200 on ST, 0x400 on in, 0x800 on out, and the actual opcode * 2(or >= 0) on everything else. + + #define LASTOP_LD 0x100 + #define LASTOP_ST 0x200 + #define LASTOP_IN 0x400 + #define LASTOP_OUT 0x800 + + enum + { + HALT_NONE = 0, + HALT_HALT = 1, + HALT_FATAL_EXCEPTION = 2 + }; + + uint8 Halted; + + bool Running; + + int ilevel; + + bool in_bstr; + uint16 in_bstr_to; + + bool bstr_subop(v810_timestamp_t ×tamp, int sub_op, int arg1); + void fpu_subop(v810_timestamp_t ×tamp, int sub_op, int arg1, int arg2); + + void Exception(uint32 handler, uint16 eCode); + + // Caching-related: + typedef struct + { + uint32 tag; + uint32 data[2]; + bool data_valid[2]; + } V810_CacheEntry_t; + + V810_CacheEntry_t Cache[128]; + + // Bitstring variables. + uint32 src_cache; + uint32 dst_cache; + bool have_src_cache, have_dst_cache; + + uint8 *FastMap[(1ULL << 32) / V810_FAST_MAP_PSIZE]; + std::vector> FastMapAllocList; + + + #ifdef WANT_DEBUGGER + void (*CPUHook)(const v810_timestamp_t timestamp, uint32 PC); + void (*ADDBT)(uint32 old_PC, uint32 new_PC, uint32); + #endif + + + // For CacheDump and CacheRestore + void CacheOpMemStore(v810_timestamp_t ×tamp, uint32 A, uint32 V); + uint32 CacheOpMemLoad(v810_timestamp_t ×tamp, uint32 A); + + void CacheClear(v810_timestamp_t ×tamp, uint32 start, uint32 count); + void CacheDump(v810_timestamp_t ×tamp, const uint32 SA); + void CacheRestore(v810_timestamp_t ×tamp, const uint32 SA); + + uint32 RDCACHE(v810_timestamp_t ×tamp, uint32 addr); + // + // End caching related + // + + uint16 RDOP(v810_timestamp_t ×tamp, uint32 addr, uint32 meow = 2); + void SetFlag(uint32 n, bool condition); + void SetSZ(uint32 value); + + void SetSREG(v810_timestamp_t ×tamp, unsigned int which, uint32 value); + uint32 GetSREG(unsigned int which); + + + bool IsSubnormal(uint32 fpval); + void FPU_Math_Template(uint32 (V810_FP_Ops::*func)(uint32, uint32), uint32 arg1, uint32 arg2); + void FPU_DoException(void); + bool CheckFPInputException(uint32 fpval); + bool FPU_DoesExceptionKillResult(void); + void SetFPUOPNonFPUFlags(uint32 result); + + + uint32 BSTR_RWORD(v810_timestamp_t ×tamp, uint32 A); + void BSTR_WWORD(v810_timestamp_t ×tamp, uint32 A, uint32 V); + bool Do_BSTR_Search(v810_timestamp_t ×tamp, const int inc_mul, unsigned int bit_test); + + V810_FP_Ops fpo; + + uint8 DummyRegion[V810_FAST_MAP_PSIZE + V810_FAST_MAP_TRAMPOLINE_SIZE]; +}; + +#endif + diff --git a/waterbox/pcfx/v810/v810_cpuD.cpp b/waterbox/pcfx/v810/v810_cpuD.cpp new file mode 100644 index 0000000000..6d204659b9 --- /dev/null +++ b/waterbox/pcfx/v810/v810_cpuD.cpp @@ -0,0 +1,385 @@ +/* V810 Emulator + * + * Copyright (C) 2006 David Tucker + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +////////////////////////////////////////////////////////// +// CPU Debug routines + +//what do we realy need? +//#include +//#include +//#include +//#include + +#include "../defs.h" +#include "v810_opt.h" +#include "v810_cpu.h" +#include "v810_cpuD.h" + + +//////////////////////////////////////////////////////////// +// Defines + +//Structure to store an element in our linked list +// used to dynamicaly dissasemble a rom +typedef struct dasms { + int offset; + uint32 PC; + uint32 jump; + struct dasms * nextElement; +} dasmS; + +typedef struct { + int addr_mode; // Addressing mode + const char * opname; // Opcode name (string) +} operation; + +static const operation optable[80] = { + { AM_I, "mov " }, // 0x00 + { AM_I, "add " }, // 0x01 + { AM_I, "sub " }, // 0x02 + { AM_I, "cmp " }, // 0x03 + { AM_I, "shl " }, // 0x04 + { AM_I, "shr " }, // 0x05 + { AM_I, "jmp " }, // 0x06 + { AM_I, "sar " }, // 0x07 + { AM_I, "mul " }, // 0x08 + { AM_I, "div " }, // 0x09 + { AM_I, "mulu " }, // 0x0A + { AM_I, "divu " }, // 0x0B + { AM_I, "or " }, // 0x0C + { AM_I, "and " }, // 0x0D + { AM_I, "xor " }, // 0x0E + { AM_I, "not " }, // 0x0F + + { AM_II, "mov " }, // 0x10 // Imediate + { AM_II, "add " }, // 0x11 + { AM_II, "setf " }, // 0x12 + { AM_II, "cmp " }, // 0x13 + { AM_II, "shl " }, // 0x14 + { AM_II, "shr " }, // 0x15 + {0x8000|AM_IX,"ei " }, // 0x16 // EI(VB only) + { AM_II, "sar " }, // 0x17 + { AM_II, "trap " }, // 0x18 + + { AM_IX, "reti " }, // 0x19 //BRKRETI + { AM_IX, "halt " }, // 0x1A //STBY + + {AM_UDEF, "??? " }, // 0x1B // Unknown + { AM_II, "ldsr " }, // 0x1C + { AM_II, "stsr " }, // 0x1D + {0x8000|AM_IX,"di " }, // 0x1E // DI(VB only) + {AM_BSTR, "BSTR " }, // 0x1F // Special Bit String Instructions + + {AM_UDEF, "??? " }, // 0x20 // Unknown // This is a fudg on our part + {AM_UDEF, "??? " }, // 0x21 // Unknown // We have 6 and 7 bit instructions + {AM_UDEF, "??? " }, // 0x22 // Unknown // this is filld in by the Conditional Branch Instructions + {AM_UDEF, "??? " }, // 0x23 // Unknown + {AM_UDEF, "??? " }, // 0x24 // Unknown + {AM_UDEF, "??? " }, // 0x25 // Unknown + {AM_UDEF, "??? " }, // 0x26 // Unknown + {AM_UDEF, "??? " }, // 0x27 // Unknown + + { AM_V, "movea" }, // 0x28 + { AM_V, "addi " }, // 0x29 + { AM_IV, "jr " }, // 0x2A + { AM_IV, "jal " }, // 0x2B + { AM_V, "ori " }, // 0x2C + { AM_V, "andi " }, // 0x2D + { AM_V, "xori " }, // 0x2E + { AM_V, "movhi" }, // 0x2F + + { AM_VIa, "ld.b " }, // 0x30 + { AM_VIa, "ld.h " }, // 0x31 + {AM_UDEF, "??? " }, // 0x32 // Unknown + { AM_VIa, "ld.w " }, // 0x33 + { AM_VIb, "st.b " }, // 0x34 + { AM_VIb, "st.h " }, // 0x35 + {AM_UDEF, "??? " }, // 0x36 // Unknown + { AM_VIb, "st.w " }, // 0x37 + { AM_VIa, "in.b " }, // 0x38 + { AM_VIa, "in.h " }, // 0x39 + { AM_VIa, "caxi " }, // 0x3A + { AM_VIa, "in.w " }, // 0x3B + { AM_VIb, "out.b" }, // 0x3C + { AM_VIb, "out.h" }, // 0x3D + { AM_FPP, "FPP " }, // 0x3E //Floating Point Instruction, Special Case + { AM_VIb, "out.w" }, // 0x3F + + { AM_III, "bv " }, // 0x40 + { AM_III, "bl " }, // 0x41 //BC 0x41 + { AM_III, "be " }, // 0x42 //BZ 0x42 + { AM_III, "bnh " }, // 0x43 + { AM_III, "bn " }, // 0x44 + { AM_III, "br " }, // 0x45 + { AM_III, "blt " }, // 0x46 + { AM_III, "ble " }, // 0x47 + { AM_III, "bnv " }, // 0x48 + { AM_III, "bnl " }, // 0x49 //BNC 0x49 + { AM_III, "bne " }, // 0x4A //BNZ 0x4A + { AM_III, "bh " }, // 0x4B + { AM_III, "bp " }, // 0x4C + { AM_III, "nop " }, // 0x4D + { AM_III, "bge " }, // 0x4E + { AM_III, "bgt " } // 0x4F +}; +// All instructions greater than 0x50 are undefined (this should not be posible of cource) + + +//Structure for holding the SubOpcodes, Same as above, without the InsType. +typedef struct { + const char * opname; // Opcode name (string) +} suboperation; + + +// Bit String Subopcodes +static const suboperation bssuboptable[16] = { + { "SCH0BSU" }, // 0x00 + { "SCH0BSD" }, // 0x01 + { "SCH1BSU" }, // 0x02 + { "SCH1BSD" }, // 0x03 + { "BError4" }, // 0x04 // Unknown + { "BError5" }, // 0x05 // Unknown + { "BError6" }, // 0x06 // Unknown + { "BError7" }, // 0x07 // Unknown + { "ORBSU " }, // 0x08 + { "ANDBSU " }, // 0x09 + { "XORBSU " }, // 0x0A + { "MOVBSU " }, // 0x0B + { "ORNBSU " }, // 0x0C + { "ANDNBSU" }, // 0x0D + { "XORNBSU" }, // 0x0E + { "NOTBSU " } // 0x0F +}; + +// Floating Point Subopcodes +static const suboperation fpsuboptable[16] = { + { "cmpf.s " }, // 0x00 + { "FError1" }, // 0x01 // Unknown + { "cvt.ws " }, // 0x02 + { "cvt.sw " }, // 0x03 + { "addf.s " }, // 0x04 + { "subf.s " }, // 0x05 + { "mulf.s " }, // 0x06 + { "divf.s " }, // 0x07 + + { "FError8" }, // 0x08 // Invalid + { "FError9" }, // 0x09 // Invalid + { "FErrorA" }, // 0x0A // Invalid + { "FErrorB" }, // 0x0B // Invalid + { "FErrorC" }, // 0x0C // Invalid + { "FErrorD" }, // 0x0D // Invalid + { "FErrorE" }, // 0x0E // Invalid + { "FErrorF" } // 0x0F // Invalid +}; + +static const suboperation fpsuboptable_vb[16] = { + { "cmpf.s " }, // 0x00 + { "FError1" }, // 0x01 // Unknown + { "cvt.ws " }, // 0x02 + { "cvt.sw " }, // 0x03 + { "addf.s " }, // 0x04 + { "subf.s " }, // 0x05 + { "mulf.s " }, // 0x06 + { "divf.s " }, // 0x07 + { "XB" }, // 0x08 // undocumented + { "XH" }, // 0x09 // undocumented //VFishing??? + { "REV" }, // 0x0A // undocumented + { "trnc.sw" }, // 0x0B + { "MPYHW" }, // 0x0C // undocumented + { "FErrorD" }, // 0x0D // Unknown + { "FErrorE" }, // 0x0E // Unknown + { "FErrorF" } // 0x0F // Unknown +}; + +static const char *pretty_preg_names[32] = +{ + "r0", "r1", "hsp", "sp", "gp", "tp", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "r28", "r29", "r30", "lp", +}; + +static const char *pretty_sreg_names[32] = +{ + "sr0(eipc)", "sr1(eipsw)", "sr2(fepc)", "sr3(fepsw)", "sr4(ecr)", "sr5(psw)", "sr6(pir)", "sr7(tkcw)", + "sr8(invalid)", "sr9(invalid)", "sr10(invalid)", "sr11(invalid)", "sr12(invalid)", "sr13(invalid)", + "sr14(invalid)", "sr15(invalid)", "sr16(invalid)", "sr17(invalid)", "sr18(invalid)", "sr19(invalid)", + "sr20(invalid)", "sr21(invalid)", "sr22(invalid)", "sr23(invalid)", + "sr24(chcw)", "sr25(adtre)", "sr26(invalid)", "sr27(invalid)", "sr28(invalid)", "sr29(invalid)", "sr30(invalid)", + "sr31(invalid)" +}; + +void v810_dis(uint32 &tPC, int num, char *buf, uint16 (*rhword)(uint32), bool vbmode) +{ + int lowB, highB, lowB2, highB2; // up to 4 bytes for instruction (either 16 or 32 bits) + int opcode, arg1, arg2, arg3; + int i = 0; + + buf[0] = 0; + + for(i = 0; i< num; i++) + { + const uint16 hw0 = rhword(tPC); + const uint16 hw1 = rhword(tPC + 2); + + lowB = hw0 & 0xFF; + highB = hw0 >> 8; + + lowB2 = hw1 & 0xFF; + highB2 = hw1 >> 8; + + opcode = highB >> 2; + if((highB & 0xE0) == 0x80) // Special opcode format for + opcode = (highB >> 1); // type III instructions. + + if((opcode > 0x4F) | (opcode < 0)) { + //Error Invalid opcode! + sprintf(&buf[strlen(buf)], "0x%04x", hw0); + tPC += 2; + } + + int am = optable[opcode].addr_mode; + + if((am & 0x8000) && !vbmode) + am = AM_UDEF; + + am &= ~0x8000; + + switch(am) { + case AM_I: // Do the same Ither way =) + arg1 = (lowB >> 5) + ((highB & 0x3) << 3); + arg2 = (lowB & 0x1F); + if (opcode == JMP) { + sprintf(&buf[strlen(buf)],"%s [%s]", optable[opcode].opname, pretty_preg_names[arg2]); + } else { + sprintf(&buf[strlen(buf)],"%s %s, %s", optable[opcode].opname, pretty_preg_names[arg2], pretty_preg_names[arg1]); + } + tPC += 2; // 16 bit instruction + break; + case AM_II: + arg1 = (lowB >> 5) + ((highB & 0x3) << 3); + arg2 = (lowB & 0x1F); + if(opcode == LDSR) { + sprintf(&buf[strlen(buf)],"%s %s, %s", optable[opcode].opname, pretty_preg_names[arg1], pretty_sreg_names[arg2]); + } else if(opcode == STSR) { + sprintf(&buf[strlen(buf)],"%s %s, %s", optable[opcode].opname, pretty_sreg_names[arg2], pretty_preg_names[arg1]); + } else if(opcode == ADD_I || opcode == CMP_I || opcode == MOV_I) { + sprintf(&buf[strlen(buf)],"%s %d, %s", optable[opcode].opname, sign_5(arg2), pretty_preg_names[arg1]); + } else { + sprintf(&buf[strlen(buf)],"%s %d, %s", optable[opcode].opname, arg2, pretty_preg_names[arg1]); + } + tPC += 2; // 16 bit instruction + break; + case AM_III: + arg1 = ((highB & 0x1) << 8) + (lowB & 0xFE); + if(opcode == NOP) + sprintf(&buf[strlen(buf)],"%s", optable[opcode].opname); + else + sprintf(&buf[strlen(buf)],"%s %08x", optable[opcode].opname, tPC + sign_9(arg1)); + tPC += 2; // 16 bit instruction + break; + case AM_IV: + arg1 = ((highB & 0x3) << 24) + (lowB << 16) + (highB2 << 8) + lowB2; + + sprintf(&buf[strlen(buf)],"%s %08x", optable[opcode].opname, tPC + sign_26(arg1)); + + tPC += 4; // 32 bit instruction + break; + case AM_V: + arg1 = (lowB >> 5) + ((highB & 0x3) << 3); + arg2 = (lowB & 0x1F); + arg3 = (highB2 << 8) + lowB2; + + // TODO: What would be the best way to disassemble the MOVEA instruction? + //if(opcode == MOVEA) + // sprintf(&buf[strlen(buf)],"%s 0x%X, %s, %s", optable[opcode].opname, (uint32)(int32)(int16)(uint16)arg3, pretty_preg_names[arg2], pretty_preg_names[arg1] ); + //else + sprintf(&buf[strlen(buf)],"%s 0x%X, %s, %s", optable[opcode].opname, arg3, pretty_preg_names[arg2], pretty_preg_names[arg1] ); + tPC += 4; // 32 bit instruction + break; + case AM_VIa: // Mode6 form1 + arg1 = (lowB >> 5) + ((highB & 0x3) << 3); + arg2 = (lowB & 0x1F); + arg3 = (highB2 << 8) + lowB2; + + if(!arg3) // Don't bother printing offset if it's 0 + sprintf(&buf[strlen(buf)],"%s [%s], %s", optable[opcode].opname, pretty_preg_names[arg2], pretty_preg_names[arg1]); + else if(sign_16(arg3) >= 0) // Make disassembly prettier if it's a positive offset + sprintf(&buf[strlen(buf)],"%s 0x%04x[%s], %s", optable[opcode].opname, sign_16(arg3), pretty_preg_names[arg2], pretty_preg_names[arg1]); + else + sprintf(&buf[strlen(buf)],"%s %d[%s], %s", optable[opcode].opname, sign_16(arg3), pretty_preg_names[arg2], pretty_preg_names[arg1]); + + tPC += 4; // 32 bit instruction + break; + case AM_VIb: // Mode6 form2 + arg1 = (lowB >> 5) + ((highB & 0x3) << 3); + arg2 = (lowB & 0x1F); + arg3 = (highB2 << 8) + lowB2; // whats the order??? 2,3,1 or 1,3,2 + + if(!arg3) // Don't bother printing offset if it's 0 + sprintf(&buf[strlen(buf)],"%s %s, [%s]", optable[opcode].opname, pretty_preg_names[arg1], pretty_preg_names[arg2]); + else if(sign_16(arg3) >= 0) // Make disassembly prettier if it's a positive offset + sprintf(&buf[strlen(buf)],"%s %s, 0x%04x[%s]", optable[opcode].opname, pretty_preg_names[arg1], sign_16(arg3), pretty_preg_names[arg2]); + else + sprintf(&buf[strlen(buf)],"%s %s, %d[%s]", optable[opcode].opname, pretty_preg_names[arg1], sign_16(arg3), pretty_preg_names[arg2]); + tPC += 4; // 32 bit instruction + break; + case AM_VII: // Unhandled + sprintf(&buf[strlen(buf)],"0x%2x 0x%2x 0x%2x 0x%2x", lowB, highB, lowB2, highB2); + tPC +=4; // 32 bit instruction + break; + case AM_VIII: // Unhandled + sprintf(&buf[strlen(buf)],"0x%2x 0x%2x 0x%2x 0x%2x", lowB, highB, lowB2, highB2); + tPC += 4; // 32 bit instruction + break; + case AM_IX: + arg1 = (lowB & 0x1); // Mode ID, Ignore for now + sprintf(&buf[strlen(buf)],"%s", optable[opcode].opname); + tPC += 2; // 16 bit instruction + break; + case AM_BSTR: // Bit String Subopcodes + arg1 = (lowB >> 5) + ((highB & 0x3) << 3); + arg2 = (lowB & 0x1F); + if(arg2 > 15) { + sprintf(&buf[strlen(buf)],"BError"); + } else { + //sprintf(&buf[strlen(buf)],"%s, $%d", bssuboptable[arg2].opname,arg1); + sprintf(&buf[strlen(buf)], "%s", bssuboptable[arg2].opname); + } + tPC += 2; // 16 bit instruction + break; + case AM_FPP: // Floating Point Subcode + arg1 = (lowB >> 5) + ((highB & 0x3) << 3); + arg2 = (lowB & 0x1F); + arg3 = (highB2 >> 2); + + if(arg3 > 15) { + sprintf(&buf[strlen(buf)],"(Invalid FPU: 0x%02x)", arg3); + } else { + sprintf(&buf[strlen(buf)],"%s %s, %s", (vbmode ? fpsuboptable_vb[arg3].opname : fpsuboptable[arg3].opname), pretty_preg_names[arg2], pretty_preg_names[arg1]); + } + tPC += 4; // 32 bit instruction + break; + case AM_UDEF: // Invalid opcode. + default: // Invalid opcode. + sprintf(&buf[strlen(buf)],"0x%04x", hw0); + tPC += 2; + } + } +} diff --git a/waterbox/pcfx/v810/v810_cpuD.h b/waterbox/pcfx/v810/v810_cpuD.h new file mode 100644 index 0000000000..6169f418b8 --- /dev/null +++ b/waterbox/pcfx/v810/v810_cpuD.h @@ -0,0 +1,15 @@ +//////////////////////////////////////////////////////////////// +// Defines for the V810 CPU debug + +#ifndef V810_CPU_D_H_ +#define V810_CPU_D_H_ + +#include +#include +#include "v810_opt.h" + +// Dissasemble num lines of code starting at tPC (If tPC == -1 start at PC) +void v810_dis(uint32 &PC, int num, char *, uint16 (*rhword)(uint32), bool vbmode = false); + +#endif + diff --git a/waterbox/pcfx/v810/v810_do_am.h b/waterbox/pcfx/v810/v810_do_am.h new file mode 100644 index 0000000000..9cc01568d3 --- /dev/null +++ b/waterbox/pcfx/v810/v810_do_am.h @@ -0,0 +1,72 @@ +#define DO_MOV_AM(); DO_AM_I(); +#define DO_ADD_AM(); DO_AM_I(); +#define DO_SUB_AM(); DO_AM_I(); +#define DO_CMP_AM(); DO_AM_I(); +#define DO_SHL_AM(); DO_AM_I(); +#define DO_SHR_AM(); DO_AM_I(); +#define DO_JMP_AM(); DO_AM_I(); +#define DO_SAR_AM(); DO_AM_I(); +#define DO_MUL_AM(); DO_AM_I(); +#define DO_DIV_AM(); DO_AM_I(); +#define DO_MULU_AM(); DO_AM_I(); +#define DO_DIVU_AM(); DO_AM_I(); +#define DO_OR_AM(); DO_AM_I(); +#define DO_AND_AM(); DO_AM_I(); +#define DO_XOR_AM(); DO_AM_I(); +#define DO_NOT_AM(); DO_AM_I(); +#define DO_MOV_I_AM(); DO_AM_II(); +#define DO_ADD_I_AM(); DO_AM_II(); +#define DO_SETF_AM(); DO_AM_II(); +#define DO_CMP_I_AM(); DO_AM_II(); +#define DO_SHL_I_AM(); DO_AM_II(); +#define DO_SHR_I_AM(); DO_AM_II(); +#define DO_EI_AM(); DO_AM_II(); +#define DO_SAR_I_AM(); DO_AM_II(); +#define DO_TRAP_AM(); DO_AM_II(); +#define DO_RETI_AM(); DO_AM_IX(); +#define DO_HALT_AM(); DO_AM_IX(); +#define DO_LDSR_AM(); DO_AM_II(); +#define DO_STSR_AM(); DO_AM_II(); +#define DO_DI_AM(); DO_AM_II(); +#define DO_BSTR_AM(); DO_AM_BSTR(); +#define DO_MOVEA_AM(); DO_AM_V(); +#define DO_ADDI_AM(); DO_AM_V(); +#define DO_JR_AM(); DO_AM_IV(); +#define DO_JAL_AM(); DO_AM_IV(); +#define DO_ORI_AM(); DO_AM_V(); +#define DO_ANDI_AM(); DO_AM_V(); +#define DO_XORI_AM(); DO_AM_V(); +#define DO_MOVHI_AM(); DO_AM_V(); +#define DO_LD_B_AM(); DO_AM_VIa(); +#define DO_LD_H_AM(); DO_AM_VIa(); +#define DO_LD_W_AM(); DO_AM_VIa(); +#define DO_ST_B_AM(); DO_AM_VIb(); +#define DO_ST_H_AM(); DO_AM_VIb(); +#define DO_ST_W_AM(); DO_AM_VIb(); +#define DO_IN_B_AM(); DO_AM_VIa(); +#define DO_IN_H_AM(); DO_AM_VIa(); +#define DO_CAXI_AM(); DO_AM_VIa(); +#define DO_IN_W_AM(); DO_AM_VIa(); +#define DO_OUT_B_AM(); DO_AM_VIb(); +#define DO_OUT_H_AM(); DO_AM_VIb(); +#define DO_FPP_AM(); DO_AM_FPP(); +#define DO_OUT_W_AM(); DO_AM_VIb(); +#define DO_BV_AM(); DO_AM_III(); +#define DO_BL_AM(); DO_AM_III(); +#define DO_BE_AM(); DO_AM_III(); +#define DO_BNH_AM(); DO_AM_III(); +#define DO_BN_AM(); DO_AM_III(); +#define DO_BR_AM(); DO_AM_III(); +#define DO_BLT_AM(); DO_AM_III(); +#define DO_BLE_AM(); DO_AM_III(); +#define DO_BNV_AM(); DO_AM_III(); +#define DO_BNL_AM(); DO_AM_III(); +#define DO_BNE_AM(); DO_AM_III(); +#define DO_BH_AM(); DO_AM_III(); +#define DO_BP_AM(); DO_AM_III(); +#define DO_NOP_AM(); DO_AM_III(); +#define DO_BGE_AM(); DO_AM_III(); +#define DO_BGT_AM(); DO_AM_III(); + + +#define DO_INVALID_AM(); DO_AM_UDEF(); diff --git a/waterbox/pcfx/v810/v810_fp_ops.cpp b/waterbox/pcfx/v810/v810_fp_ops.cpp new file mode 100644 index 0000000000..2817c004dd --- /dev/null +++ b/waterbox/pcfx/v810/v810_fp_ops.cpp @@ -0,0 +1,408 @@ +/******************************************************************************/ +/* Mednafen - Multi-system Emulator */ +/******************************************************************************/ +/* v810_fp_ops.cpp: +** Copyright (C) 2014-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "v810_fp_ops.h" + +bool V810_FP_Ops::fp_is_zero(uint32 v) +{ + return((v & 0x7FFFFFFF) == 0); +} + +#if 0 +bool V810_FP_Ops::fp_is_nan(uint32 v) +{ + return((v & 0x7FFFFFFF) > (255 << 23)); +} + +bool V810_FP_Ops::fp_is_inf(uint32 v) +{ + return((v & 0x7FFFFFFF) == (255 << 23)); +} +#endif + +bool V810_FP_Ops::fp_is_inf_nan_sub(uint32 v) +{ + if((v & 0x7FFFFFFF) == 0) + return(false); + + switch((v >> 23) & 0xFF) + { + case 0x00: + case 0xff: + return(true); + } + return(false); +} + +void V810_FP_Ops::fpim_decode(fpim* df, uint32 v) +{ + df->exp = ((v >> 23) & 0xFF) - 127; + df->f = (v & 0x7FFFFF) | ((v & 0x7FFFFFFF) ? 0x800000 : 0); + df->sign = v >> 31; +} + +void V810_FP_Ops::fpim_round(fpim* df) +{ + int vbc = 64 - MDFN_lzcount64(df->f); + + if(vbc > 24) + { + const unsigned sa = vbc - 24; + + if(1) // round to nearest + { + uint64 old_f = df->f; + + df->f = (df->f + ((df->f >> sa) & 1) + ((1ULL << (sa - 1)) - 1)) & ~((1ULL << sa) - 1); + + if(df->f != old_f) + { + //printf("Inexact mr\n"); + exception_flags |= flag_inexact; + } + } + else + abort(); + } +} + +void V810_FP_Ops::fpim_round_int(fpim* df, bool truncate) +{ + if(df->exp < 23) + { + const unsigned sa = 23 - df->exp; + uint64 old_f = df->f; + + //if(sa >= 2) + // printf("RI: %lld, %d\n", df->f, sa); + + // round to nearest + if(sa > 24) + df->f = 0; + else + { + if(truncate) + df->f = df->f & ~((1ULL << sa) - 1); + else + df->f = (df->f + ((df->f >> sa) & 1) + ((1ULL << (sa - 1)) - 1)) & ~((1ULL << sa) - 1); + } + + if(df->f != old_f) + { + //printf("Inexact\n"); + exception_flags |= flag_inexact; + } + } +} + +uint32 V810_FP_Ops::fpim_encode(fpim* df) +{ + const int lzc = MDFN_lzcount64(df->f); + int tmp_exp = df->exp - lzc; + uint64 tmp_walrus = df->f << (lzc & 0x3F); + int tmp_sign = df->sign; + + tmp_exp += 40; + tmp_walrus >>= 40; + + if(tmp_walrus == 0) + tmp_exp = -127; + else if(tmp_exp <= -127) + { + exception_flags |= flag_underflow | flag_inexact; + //printf("Subnormal: %lld. %d\n", tmp_walrus, tmp_exp); + if(1) + { + tmp_exp = -127; + tmp_walrus = 0; + } + else + { + tmp_walrus >>= -(tmp_exp + 126); + tmp_exp = -127; + } + } + else if(tmp_exp >= 128) + { + exception_flags |= flag_overflow; + //printf("Overflow!\n"); + + if(1) + tmp_exp -= 192; + else + { + tmp_exp = 128; + tmp_walrus = 0; + } + + } + return (tmp_sign << 31) | ((tmp_exp + 127) << 23) | (tmp_walrus & 0x7FFFFF); +} + +uint32 V810_FP_Ops::mul(uint32 a, uint32 b) +{ + fpim ins[2]; + fpim res; + + if(fp_is_inf_nan_sub(a) || fp_is_inf_nan_sub(b)) + { + exception_flags |= flag_reserved; + return(~0U); + } + + fpim_decode(&ins[0], a); + fpim_decode(&ins[1], b); + + //printf("%08x %08x - %d %d %d - %d %d %d\n", a, b, a_exp, a_walrus, a_sign, b_exp, b_walrus, b_sign); + + res.exp = ins[0].exp + ins[1].exp - 23; + res.f = ins[0].f * ins[1].f; + res.sign = ins[0].sign ^ ins[1].sign; + + fpim_round(&res); + + return fpim_encode(&res); +} + +uint32 V810_FP_Ops::add(uint32 a, uint32 b) +{ + fpim ins[2]; + fpim res; + int64 ft[2]; + int64 tr; + int max_exp; + + if(fp_is_inf_nan_sub(a) || fp_is_inf_nan_sub(b)) + { + exception_flags |= flag_reserved; + return(~0U); + } + + if(a == b && !(a & 0x7FFFFFFF)) + { + return(a & 0x80000000); + } + + fpim_decode(&ins[0], a); + fpim_decode(&ins[1], b); + + max_exp = std::max(ins[0].exp, ins[1].exp); + + //printf("%d:%08llx %d:%08llx\n", ins[0].exp, ins[0].f, ins[1].exp, ins[1].f); + + for(unsigned i = 0; i < 2; i++) + { + unsigned sd = (max_exp - ins[i].exp); + + ft[i] = ins[i].f << 24; + + if(sd >= 48) + { + if(ft[i] != 0) + ft[i] = 1; + } + else + { + int64 nft = ft[i] >> sd; + + if(ft[i] != (nft << sd)) + { + nft |= 1; + } + //{ + // puts("FPR"); + // } + + ft[i] = nft; + } + + if(ins[i].sign) + ft[i] = -ft[i]; + } + + //printf("SOON: %08llx %08llx\n", ft[0], ft[1]); + + tr = ft[0] + ft[1]; + if(tr < 0) + { + tr = -tr; + res.sign = true; + } + else + res.sign = false; + + res.f = tr; + res.exp = max_exp - 24; + + fpim_round(&res); + + return fpim_encode(&res); +} + +uint32 V810_FP_Ops::sub(uint32 a, uint32 b) +{ + return add(a, b ^ 0x80000000); +} + +uint32 V810_FP_Ops::div(uint32 a, uint32 b) +{ + fpim ins[2]; + fpim res; + uint64 mtmp; + + if(fp_is_inf_nan_sub(a) || fp_is_inf_nan_sub(b)) + { + exception_flags |= flag_reserved; + return(~0U); + } + + if(fp_is_zero(a) && fp_is_zero(b)) + { + exception_flags |= flag_invalid; + return(~0U); + } + + fpim_decode(&ins[0], a); + fpim_decode(&ins[1], b); + + res.sign = ins[0].sign ^ ins[1].sign; + + if(ins[1].f == 0) + { + //puts("Divide by zero!"); + exception_flags |= flag_divbyzero; + return((res.sign << 31) | (255 << 23)); + } + else + { + res.exp = ins[0].exp - ins[1].exp - 2 - 1; // + 23 - 2; + res.f = ((ins[0].f << 24) / ins[1].f) << 2; + mtmp = ((ins[0].f << 24) % ins[1].f) << 1; + + //printf("%lld %lld\n", (ins[0].f << 23) % ins[1].f, ins[1].f); + + if(mtmp > ins[1].f) + res.f |= 3; + else if(mtmp == ins[1].f) + res.f |= 2; + else if(mtmp > 0) + res.f |= 1; + } + + fpim_round(&res); + + return fpim_encode(&res); +} + +int V810_FP_Ops::cmp(uint32 a, uint32 b) +{ + fpim ins[2]; + + if(fp_is_inf_nan_sub(a) || fp_is_inf_nan_sub(b)) + { + exception_flags |= flag_reserved; + return(~0U); + } + + fpim_decode(&ins[0], a); + fpim_decode(&ins[1], b); + + if(ins[0].exp > ins[1].exp) + return(ins[0].sign ? -1 : 1); + + if(ins[0].exp < ins[1].exp) + return(ins[1].sign ? 1 : -1); + + if(ins[0].f > ins[1].f) + return(ins[0].sign ? -1 : 1); + + if(ins[0].f < ins[1].f) + return(ins[1].sign ? 1 : -1); + + if((ins[0].sign ^ ins[1].sign) && ins[0].f != 0) + return(ins[0].sign ? -1 : 1); + + return(0); +} + +uint32 V810_FP_Ops::itof(uint32 v) +{ + fpim res; + + res.sign = (bool)(v & 0x80000000); + res.exp = 23; + res.f = res.sign ? (0x80000000 - (v & 0x7FFFFFFF)) : (v & 0x7FFFFFFF); + + fpim_round(&res); + + return fpim_encode(&res); +} + + +uint32 V810_FP_Ops::ftoi(uint32 v, bool truncate) +{ + fpim ins; + int sa; + int ret; + + if(fp_is_inf_nan_sub(v)) + { + exception_flags |= flag_reserved; + return(~0U); + } + + fpim_decode(&ins, v); + fpim_round_int(&ins, truncate); + + sa = ins.exp - 23; + + if(sa < 0) + { + if(sa <= -32) + ret = 0; + else + ret = ins.f >> -sa; + } + else + { + if(sa >= 8) + { + if(sa == 8 && ins.f == 0x800000 && ins.sign) + return(0x80000000); + else + { + ret = ~0U; + exception_flags |= flag_invalid; + } + } + else + { + ret = ins.f << sa; + } + } + //printf("%d\n", sa); + + if(ins.sign) + ret = -ret; + + return(ret); +} + diff --git a/waterbox/pcfx/v810/v810_fp_ops.h b/waterbox/pcfx/v810/v810_fp_ops.h new file mode 100644 index 0000000000..9c2c026587 --- /dev/null +++ b/waterbox/pcfx/v810/v810_fp_ops.h @@ -0,0 +1,77 @@ +/******************************************************************************/ +/* Mednafen - Multi-system Emulator */ +/******************************************************************************/ +/* v810_fp_ops.h: +** Copyright (C) 2014-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "../defs.h" + +class V810_FP_Ops +{ + public: + + uint32 mul(uint32 a, uint32 b); + uint32 div(uint32 a, uint32 b); + uint32 add(uint32 a, uint32 b); + uint32 sub(uint32 a, uint32 b); + int cmp(uint32 a, uint32 b); + + uint32 itof(uint32 v); + uint32 ftoi(uint32 v, bool truncate); + + enum + { + flag_invalid = 0x0001, + flag_divbyzero = 0x0002, + flag_overflow = 0x0004, + flag_underflow = 0x0008, + flag_inexact = 0x0010, + flag_reserved = 0x0020 + }; + + inline uint32 get_flags(void) + { + return exception_flags; + } + + inline void clear_flags(void) + { + exception_flags = 0; + } + + private: + + unsigned exception_flags; + + struct fpim + { + uint64 f; + int exp; + bool sign; + }; + + bool fp_is_zero(uint32 v); + bool fp_is_inf_nan_sub(uint32 v); + + unsigned clz64(uint64 v); + void fpim_decode(fpim* df, uint32 v); + void fpim_round(fpim* df); + void fpim_round_int(fpim* df, bool truncate = false); + uint32 fpim_encode(fpim* df); +}; + diff --git a/waterbox/pcfx/v810/v810_oploop.inc b/waterbox/pcfx/v810/v810_oploop.inc new file mode 100644 index 0000000000..6b5015c744 --- /dev/null +++ b/waterbox/pcfx/v810/v810_oploop.inc @@ -0,0 +1,1130 @@ +/* V810 Emulator + * + * Copyright (C) 2006 David Tucker + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + + // Macro test taken from http://gcc.gnu.org/viewcvs/trunk/gcc/testsuite/gcc.dg/20020919-1.c?view=markup&pathrev=142696 + //#if defined (__powerpc__) || defined (__PPC__) || defined (__ppc__) || defined (__POWERPC__) || defined (PPC) || defined (_IBMR2) + // register v810_timestamp_t timestamp_rl asm("15") = v810_timestamp; + //#elif defined(__x86_64__) + // register v810_timestamp_t timestamp_rl asm("r11") = v810_timestamp; + //#else + register v810_timestamp_t timestamp_rl = v810_timestamp; + //#endif + + uint32 opcode; + uint32 tmp2; + int val = 0; + + + #define ADDCLOCK(__n) { timestamp += __n; } + + #define CHECK_HALTED(); { if(Halted && timestamp < next_event_ts) { timestamp = next_event_ts; } } + + while(Running) + { + #ifdef RB_DEBUGMODE + uint32 old_PC = RB_GETPC(); + #endif + uint32 tmpop; + + assert(timestamp_rl <= next_event_ts); + + if(!IPendingCache) + { + if(Halted) + { + timestamp_rl = next_event_ts; + } + else if(in_bstr) + { + tmpop = in_bstr_to; + opcode = tmpop >> 9; + goto op_BSTR; + } + } + + while(timestamp_rl < next_event_ts) + { + #ifdef RB_DEBUGMODE + old_PC = RB_GETPC(); + #endif + + P_REG[0] = 0; //Zero the Zero Reg!!! + + RB_CPUHOOK(RB_GETPC()); + + { + //printf("%08x\n", RB_GETPC()); + { + v810_timestamp_t timestamp = timestamp_rl; + + tmpop = RB_RDOP(0, 0); + + timestamp_rl = timestamp; + } + + opcode = (tmpop >> 9) | IPendingCache; + + //printf("%02x\n", opcode >> 1); +#if HAVE_COMPUTED_GOTO + #define CGBEGIN static const void *const op_goto_table[256] = { + #define CGE(l) &&l, + #define CGEND }; goto *op_goto_table[opcode]; +#else + /* (uint8) cast for cheaper alternative to generated branch+compare bounds check instructions, but still more + expensive than computed goto which needs no masking nor bounds checking. + */ + #define CGBEGIN { enum { CGESB = 1 + __COUNTER__ }; switch((uint8)opcode) { + #define CGE(l) case __COUNTER__ - CGESB: goto l; + #define CGEND } } +#endif + + CGBEGIN + CGE(op_MOV) CGE(op_MOV) CGE(op_ADD) CGE(op_ADD) CGE(op_SUB) CGE(op_SUB) CGE(op_CMP) CGE(op_CMP) + CGE(op_SHL) CGE(op_SHL) CGE(op_SHR) CGE(op_SHR) CGE(op_JMP) CGE(op_JMP) CGE(op_SAR) CGE(op_SAR) + CGE(op_MUL) CGE(op_MUL) CGE(op_DIV) CGE(op_DIV) CGE(op_MULU) CGE(op_MULU) CGE(op_DIVU) CGE(op_DIVU) + CGE(op_OR) CGE(op_OR) CGE(op_AND) CGE(op_AND) CGE(op_XOR) CGE(op_XOR) CGE(op_NOT) CGE(op_NOT) + CGE(op_MOV_I) CGE(op_MOV_I) CGE(op_ADD_I) CGE(op_ADD_I) CGE(op_SETF) CGE(op_SETF) CGE(op_CMP_I) CGE(op_CMP_I) + CGE(op_SHL_I) CGE(op_SHL_I) CGE(op_SHR_I) CGE(op_SHR_I) CGE(op_EI) CGE(op_EI) CGE(op_SAR_I) CGE(op_SAR_I) + CGE(op_TRAP) CGE(op_TRAP) CGE(op_RETI) CGE(op_RETI) CGE(op_HALT) CGE(op_HALT) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_LDSR) CGE(op_LDSR) CGE(op_STSR) CGE(op_STSR) CGE(op_DI) CGE(op_DI) CGE(op_BSTR) CGE(op_BSTR) + CGE(op_BV) CGE(op_BL) CGE(op_BE) CGE(op_BNH) CGE(op_BN) CGE(op_BR) CGE(op_BLT) CGE(op_BLE) + CGE(op_BNV) CGE(op_BNL) CGE(op_BNE) CGE(op_BH) CGE(op_BP) CGE(op_NOP) CGE(op_BGE) CGE(op_BGT) + CGE(op_MOVEA) CGE(op_MOVEA) CGE(op_ADDI) CGE(op_ADDI) CGE(op_JR) CGE(op_JR) CGE(op_JAL) CGE(op_JAL) + CGE(op_ORI) CGE(op_ORI) CGE(op_ANDI) CGE(op_ANDI) CGE(op_XORI) CGE(op_XORI) CGE(op_MOVHI) CGE(op_MOVHI) + CGE(op_LD_B) CGE(op_LD_B) CGE(op_LD_H) CGE(op_LD_H) CGE(op_INVALID) CGE(op_INVALID) CGE(op_LD_W) CGE(op_LD_W) + CGE(op_ST_B) CGE(op_ST_B) CGE(op_ST_H) CGE(op_ST_H) CGE(op_INVALID) CGE(op_INVALID) CGE(op_ST_W) CGE(op_ST_W) + CGE(op_IN_B) CGE(op_IN_B) CGE(op_IN_H) CGE(op_IN_H) CGE(op_CAXI) CGE(op_CAXI) CGE(op_IN_W) CGE(op_IN_W) + CGE(op_OUT_B) CGE(op_OUT_B) CGE(op_OUT_H) CGE(op_OUT_H) CGE(op_FPP) CGE(op_FPP) CGE(op_OUT_W) CGE(op_OUT_W) + + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) + CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INT_HANDLER) + CGEND + + // Bit string subopcodes + #define DO_AM_BSTR() \ + const uint32 arg1 = (tmpop >> 5) & 0x1F; \ + const uint32 arg2 = (tmpop & 0x1F); \ + RB_INCPCBY2(); + + + #define DO_AM_FPP() \ + const uint32 arg1 = (tmpop >> 5) & 0x1F; \ + const uint32 arg2 = (tmpop & 0x1F); \ + const uint32 arg3 = ((RB_RDOP(2) >> 10)&0x3F); \ + RB_INCPCBY4(); + + + #define DO_AM_UDEF() \ + RB_INCPCBY2(); + + #define DO_AM_I() \ + const uint32 arg1 = tmpop & 0x1F; \ + const uint32 arg2 = (tmpop >> 5) & 0x1F; \ + RB_INCPCBY2(); + + #define DO_AM_II() DO_AM_I(); + + + #define DO_AM_IV() \ + const uint32 arg1 = ((tmpop & 0x000003FF) << 16) | RB_RDOP(2); \ + + + #define DO_AM_V() \ + const uint32 arg3 = (tmpop >> 5) & 0x1F; \ + const uint32 arg2 = tmpop & 0x1F; \ + const uint32 arg1 = RB_RDOP(2); \ + RB_INCPCBY4(); + + + #define DO_AM_VIa() \ + const uint32 arg1 = RB_RDOP(2); \ + const uint32 arg2 = tmpop & 0x1F; \ + const uint32 arg3 = (tmpop >> 5) & 0x1F; \ + RB_INCPCBY4(); \ + + + #define DO_AM_VIb() \ + const uint32 arg1 = (tmpop >> 5) & 0x1F; \ + const uint32 arg2 = RB_RDOP(2); \ + const uint32 arg3 = (tmpop & 0x1F); \ + RB_INCPCBY4(); \ + + #define DO_AM_IX() \ + const uint32 arg1 = (tmpop & 0x1); \ + RB_INCPCBY2(); \ + + #define DO_AM_III() \ + const uint32 arg1 = tmpop & 0x1FE; + + #include "v810_do_am.h" + + #define BEGIN_OP(meowtmpop) { op_##meowtmpop: v810_timestamp_t timestamp = timestamp_rl; DO_##meowtmpop ##_AM(); + #define END_OP() timestamp_rl = timestamp; goto OpFinished; } + #define END_OP_SKIPLO() timestamp_rl = timestamp; goto OpFinishedSkipLO; } + + BEGIN_OP(MOV); + ADDCLOCK(1); + SetPREG(arg2, P_REG[arg1]); + END_OP(); + + + BEGIN_OP(ADD); + ADDCLOCK(1); + uint32 temp = P_REG[arg2] + P_REG[arg1]; + + SetFlag(PSW_OV, ((P_REG[arg2]^(~P_REG[arg1]))&(P_REG[arg2]^temp))&0x80000000); + SetFlag(PSW_CY, temp < P_REG[arg2]); + + SetPREG(arg2, temp); + SetSZ(P_REG[arg2]); + END_OP(); + + + BEGIN_OP(SUB); + ADDCLOCK(1); + uint32 temp = P_REG[arg2] - P_REG[arg1]; + + SetFlag(PSW_OV, ((P_REG[arg2]^P_REG[arg1])&(P_REG[arg2]^temp))&0x80000000); + SetFlag(PSW_CY, temp > P_REG[arg2]); + + SetPREG(arg2, temp); + SetSZ(P_REG[arg2]); + END_OP(); + + + BEGIN_OP(CMP); + ADDCLOCK(1); + uint32 temp = P_REG[arg2] - P_REG[arg1]; + + SetSZ(temp); + SetFlag(PSW_OV, ((P_REG[arg2]^P_REG[arg1])&(P_REG[arg2]^temp))&0x80000000); + SetFlag(PSW_CY, temp > P_REG[arg2]); + END_OP(); + + + BEGIN_OP(SHL); + ADDCLOCK(1); + val = P_REG[arg1] & 0x1F; + + // set CY before we destroy the regisrer info.... + SetFlag(PSW_CY, (val != 0) && ((P_REG[arg2] >> (32 - val))&0x01) ); + SetFlag(PSW_OV, FALSE); + SetPREG(arg2, P_REG[arg2] << val); + SetSZ(P_REG[arg2]); + END_OP(); + + BEGIN_OP(SHR); + ADDCLOCK(1); + val = P_REG[arg1] & 0x1F; + // set CY before we destroy the regisrer info.... + SetFlag(PSW_CY, (val) && ((P_REG[arg2] >> (val-1))&0x01)); + SetFlag(PSW_OV, FALSE); + SetPREG(arg2, P_REG[arg2] >> val); + SetSZ(P_REG[arg2]); + END_OP(); + + BEGIN_OP(JMP); + + (void)arg2; // arg2 is unused. + + ADDCLOCK(3); + RB_SETPC((P_REG[arg1] & 0xFFFFFFFE)); + if(RB_AccurateMode) + { + BRANCH_ALIGN_CHECK(PC); + } + RB_ADDBT(old_PC, RB_GETPC(), 0); + END_OP(); + + BEGIN_OP(SAR); + ADDCLOCK(1); + val = P_REG[arg1] & 0x1F; + + SetFlag(PSW_CY, (val) && ((P_REG[arg2]>>(val-1))&0x01) ); + SetFlag(PSW_OV, FALSE); + + SetPREG(arg2, (uint32) ((int32)P_REG[arg2] >> val)); + + SetSZ(P_REG[arg2]); + END_OP(); + + BEGIN_OP(OR); + ADDCLOCK(1); + SetPREG(arg2, P_REG[arg1] | P_REG[arg2]); + SetFlag(PSW_OV, FALSE); + SetSZ(P_REG[arg2]); + END_OP(); + + BEGIN_OP(AND); + ADDCLOCK(1); + SetPREG(arg2, P_REG[arg1] & P_REG[arg2]); + SetFlag(PSW_OV, FALSE); + SetSZ(P_REG[arg2]); + END_OP(); + + BEGIN_OP(XOR); + ADDCLOCK(1); + SetPREG(arg2, P_REG[arg1] ^ P_REG[arg2]); + SetFlag(PSW_OV, FALSE); + SetSZ(P_REG[arg2]); + END_OP(); + + BEGIN_OP(NOT); + ADDCLOCK(1); + SetPREG(arg2, ~P_REG[arg1]); + SetFlag(PSW_OV, FALSE); + SetSZ(P_REG[arg2]); + END_OP(); + + BEGIN_OP(MOV_I); + ADDCLOCK(1); + SetPREG(arg2,sign_5(arg1)); + END_OP(); + + BEGIN_OP(ADD_I); + ADDCLOCK(1); + uint32 temp = P_REG[arg2] + sign_5(arg1); + + SetFlag(PSW_OV, ((P_REG[arg2]^(~sign_5(arg1)))&(P_REG[arg2]^temp))&0x80000000); + SetFlag(PSW_CY, (uint32)temp < P_REG[arg2]); + + SetPREG(arg2, (uint32)temp); + SetSZ(P_REG[arg2]); + END_OP(); + + + BEGIN_OP(SETF); + ADDCLOCK(1); + + P_REG[arg2] = 0; + + switch (arg1 & 0x0F) + { + case COND_V: + if (TESTCOND_V) P_REG[arg2] = 1; + break; + case COND_C: + if (TESTCOND_C) P_REG[arg2] = 1; + break; + case COND_Z: + if (TESTCOND_Z) P_REG[arg2] = 1; + break; + case COND_NH: + if (TESTCOND_NH) P_REG[arg2] = 1; + break; + case COND_S: + if (TESTCOND_S) P_REG[arg2] = 1; + break; + case COND_T: + P_REG[arg2] = 1; + break; + case COND_LT: + if (TESTCOND_LT) P_REG[arg2] = 1; + break; + case COND_LE: + if (TESTCOND_LE) P_REG[arg2] = 1; + break; + case COND_NV: + if (TESTCOND_NV) P_REG[arg2] = 1; + break; + case COND_NC: + if (TESTCOND_NC) P_REG[arg2] = 1; + break; + case COND_NZ: + if (TESTCOND_NZ) P_REG[arg2] = 1; + break; + case COND_H: + if (TESTCOND_H) P_REG[arg2] = 1; + break; + case COND_NS: + if (TESTCOND_NS) P_REG[arg2] = 1; + break; + case COND_F: + //always false! do nothing more + break; + case COND_GE: + if (TESTCOND_GE) P_REG[arg2] = 1; + break; + case COND_GT: + if (TESTCOND_GT) P_REG[arg2] = 1; + break; + } + END_OP(); + + BEGIN_OP(CMP_I); + ADDCLOCK(1); + uint32 temp = P_REG[arg2] - sign_5(arg1); + + SetSZ(temp); + SetFlag(PSW_OV, ((P_REG[arg2]^(sign_5(arg1)))&(P_REG[arg2]^temp))&0x80000000); + SetFlag(PSW_CY, temp > P_REG[arg2]); + END_OP(); + + BEGIN_OP(SHR_I); + ADDCLOCK(1); + SetFlag(PSW_CY, arg1 && ((P_REG[arg2] >> (arg1-1))&0x01) ); + // set CY before we destroy the regisrer info.... + SetPREG(arg2, P_REG[arg2] >> arg1); + SetFlag(PSW_OV, FALSE); + SetSZ(P_REG[arg2]); + END_OP(); + + BEGIN_OP(SHL_I); + ADDCLOCK(1); + SetFlag(PSW_CY, arg1 && ((P_REG[arg2] >> (32 - arg1))&0x01) ); + // set CY before we destroy the regisrer info.... + + SetPREG(arg2, P_REG[arg2] << arg1); + SetFlag(PSW_OV, FALSE); + SetSZ(P_REG[arg2]); + END_OP(); + + BEGIN_OP(SAR_I); + ADDCLOCK(1); + SetFlag(PSW_CY, arg1 && ((P_REG[arg2]>>(arg1-1))&0x01) ); + + SetPREG(arg2, (uint32) ((int32)P_REG[arg2] >> arg1)); + + SetFlag(PSW_OV, FALSE); + SetSZ(P_REG[arg2]); + END_OP(); + + BEGIN_OP(LDSR); // Loads a Sys Reg with the value in specified PR + ADDCLOCK(1); // ? + + SetSREG(timestamp, arg1 & 0x1F, P_REG[arg2 & 0x1F]); + END_OP(); + + BEGIN_OP(STSR); // Loads a PR with the value in specified Sys Reg + ADDCLOCK(1); // ? + P_REG[arg2 & 0x1F] = GetSREG(arg1 & 0x1F); + END_OP(); + + BEGIN_OP(EI); + (void)arg1; // arg1 is unused. + (void)arg2; // arg2 is unused. + + if(VBMode) + { + ADDCLOCK(1); + S_REG[PSW] = S_REG[PSW] &~ PSW_ID; + RecalcIPendingCache(); + } + else + { + ADDCLOCK(1); + RB_DECPCBY2(); + Exception(INVALID_OP_HANDLER_ADDR, ECODE_INVALID_OP); + CHECK_HALTED(); + } + END_OP(); + + BEGIN_OP(DI); + (void)arg1; // arg1 is unused. + (void)arg2; // arg2 is unused. + + if(VBMode) + { + ADDCLOCK(1); + S_REG[PSW] |= PSW_ID; + IPendingCache = 0; + } + else + { + ADDCLOCK(1); + RB_DECPCBY2(); + Exception(INVALID_OP_HANDLER_ADDR, ECODE_INVALID_OP); + CHECK_HALTED(); + } + END_OP(); + + + #define COND_BRANCH(cond) \ + if(cond) \ + { \ + ADDCLOCK(3); \ + RB_PCRELCHANGE(sign_9(arg1) & 0xFFFFFFFE); \ + if(RB_AccurateMode) \ + { \ + BRANCH_ALIGN_CHECK(PC); \ + } \ + RB_ADDBT(old_PC, RB_GETPC(), 0); \ + } \ + else \ + { \ + ADDCLOCK(1); \ + RB_INCPCBY2(); \ + } + + BEGIN_OP(BV); + COND_BRANCH(TESTCOND_V); + END_OP(); + + + BEGIN_OP(BL); + COND_BRANCH(TESTCOND_L); + END_OP(); + + BEGIN_OP(BE); + COND_BRANCH(TESTCOND_E); + END_OP(); + + BEGIN_OP(BNH); + COND_BRANCH(TESTCOND_NH); + END_OP(); + + BEGIN_OP(BN); + COND_BRANCH(TESTCOND_N); + END_OP(); + + BEGIN_OP(BR); + COND_BRANCH(TRUE); + END_OP(); + + BEGIN_OP(BLT); + COND_BRANCH(TESTCOND_LT); + END_OP(); + + BEGIN_OP(BLE); + COND_BRANCH(TESTCOND_LE); + END_OP(); + + BEGIN_OP(BNV); + COND_BRANCH(TESTCOND_NV); + END_OP(); + + BEGIN_OP(BNL); + COND_BRANCH(TESTCOND_NL); + END_OP(); + + BEGIN_OP(BNE); + COND_BRANCH(TESTCOND_NE); + END_OP(); + + BEGIN_OP(BH); + COND_BRANCH(TESTCOND_H); + END_OP(); + + BEGIN_OP(BP); + COND_BRANCH(TESTCOND_P); + END_OP(); + + BEGIN_OP(BGE); + COND_BRANCH(TESTCOND_GE); + END_OP(); + + BEGIN_OP(BGT); + COND_BRANCH(TESTCOND_GT); + END_OP(); + + BEGIN_OP(JR); + ADDCLOCK(3); + RB_PCRELCHANGE(sign_26(arg1) & 0xFFFFFFFE); + if(RB_AccurateMode) + { + BRANCH_ALIGN_CHECK(PC); + } + RB_ADDBT(old_PC, RB_GETPC(), 0); + END_OP(); + + BEGIN_OP(JAL); + ADDCLOCK(3); + P_REG[31] = RB_GETPC() + 4; + RB_PCRELCHANGE(sign_26(arg1) & 0xFFFFFFFE); + if(RB_AccurateMode) + { + BRANCH_ALIGN_CHECK(PC); + } + RB_ADDBT(old_PC, RB_GETPC(), 0); + END_OP(); + + BEGIN_OP(MOVEA); + ADDCLOCK(1); + SetPREG(arg3, P_REG[arg2] + sign_16(arg1)); + END_OP(); + + BEGIN_OP(ADDI); + ADDCLOCK(1); + uint32 temp = P_REG[arg2] + sign_16(arg1); + + SetFlag(PSW_OV, ((P_REG[arg2]^(~sign_16(arg1)))&(P_REG[arg2]^temp))&0x80000000); + SetFlag(PSW_CY, (uint32)temp < P_REG[arg2]); + + SetPREG(arg3, (uint32)temp); + SetSZ(P_REG[arg3]); + END_OP(); + + BEGIN_OP(ORI); + ADDCLOCK(1); + SetPREG(arg3, arg1 | P_REG[arg2]); + SetFlag(PSW_OV, FALSE); + SetSZ(P_REG[arg3]); + END_OP(); + + BEGIN_OP(ANDI); + ADDCLOCK(1); + SetPREG(arg3, (arg1 & P_REG[arg2])); + SetFlag(PSW_OV, FALSE); + SetSZ(P_REG[arg3]); + END_OP(); + + BEGIN_OP(XORI); + ADDCLOCK(1); + SetPREG(arg3, arg1 ^ P_REG[arg2]); + SetFlag(PSW_OV, FALSE); + SetSZ(P_REG[arg3]); + END_OP(); + + BEGIN_OP(MOVHI); + ADDCLOCK(1); + SetPREG(arg3, (arg1 << 16) + P_REG[arg2]); + END_OP(); + + // LD.B + BEGIN_OP(LD_B); + ADDCLOCK(1); + tmp2 = (sign_16(arg1)+P_REG[arg2])&0xFFFFFFFF; + + SetPREG(arg3, sign_8(MemRead8(timestamp, tmp2))); + + //should be 3 clocks when executed alone, 2 when precedes another LD, or 1 + //when precedes an instruction with many clocks (I'm guessing FP, MUL, DIV, etc) + if(lastop >= 0) + { + if(lastop == LASTOP_LD) + { + ADDCLOCK(1); + } + else + { + ADDCLOCK(2); + } + } + lastop = LASTOP_LD; + END_OP_SKIPLO(); + + // LD.H + BEGIN_OP(LD_H); + ADDCLOCK(1); + tmp2 = (sign_16(arg1)+P_REG[arg2]) & 0xFFFFFFFE; + SetPREG(arg3, sign_16(MemRead16(timestamp, tmp2))); + + if(lastop >= 0) + { + if(lastop == LASTOP_LD) + { + ADDCLOCK(1); + } + else + { + ADDCLOCK(2); + } + } + lastop = LASTOP_LD; + END_OP_SKIPLO(); + + + // LD.W + BEGIN_OP(LD_W); + ADDCLOCK(1); + + tmp2 = (sign_16(arg1)+P_REG[arg2]) & 0xFFFFFFFC; + + if(MemReadBus32[tmp2 >> 24]) + { + SetPREG(arg3, MemRead32(timestamp, tmp2)); + + if(lastop >= 0) + { + if(lastop == LASTOP_LD) + { + ADDCLOCK(1); + } + else + { + ADDCLOCK(2); + } + } + } + else + { + uint32 rv; + + rv = MemRead16(timestamp, tmp2); + rv |= MemRead16(timestamp, tmp2 | 2) << 16; + + SetPREG(arg3, rv); + + if(lastop >= 0) + { + if(lastop == LASTOP_LD) + { + ADDCLOCK(3); + } + else + { + ADDCLOCK(4); + } + } + } + lastop = LASTOP_LD; + END_OP_SKIPLO(); + + // ST.B + BEGIN_OP(ST_B); + ADDCLOCK(1); + MemWrite8(timestamp, sign_16(arg2)+P_REG[arg3], P_REG[arg1] & 0xFF); + + if(lastop == LASTOP_ST) + { + ADDCLOCK(1); + } + lastop = LASTOP_ST; + END_OP_SKIPLO(); + + // ST.H + BEGIN_OP(ST_H); + ADDCLOCK(1); + + MemWrite16(timestamp, (sign_16(arg2)+P_REG[arg3])&0xFFFFFFFE, P_REG[arg1] & 0xFFFF); + + if(lastop == LASTOP_ST) + { + ADDCLOCK(1); + } + lastop = LASTOP_ST; + END_OP_SKIPLO(); + + // ST.W + BEGIN_OP(ST_W); + ADDCLOCK(1); + tmp2 = (sign_16(arg2)+P_REG[arg3]) & 0xFFFFFFFC; + + if(MemWriteBus32[tmp2 >> 24]) + { + MemWrite32(timestamp, tmp2, P_REG[arg1]); + + if(lastop == LASTOP_ST) + { + ADDCLOCK(1); + } + } + else + { + MemWrite16(timestamp, tmp2, P_REG[arg1] & 0xFFFF); + MemWrite16(timestamp, tmp2 | 2, P_REG[arg1] >> 16); + + if(lastop == LASTOP_ST) + { + ADDCLOCK(3); + } + } + lastop = LASTOP_ST; + END_OP_SKIPLO(); + + // IN.B + BEGIN_OP(IN_B); + { + ADDCLOCK(3); + SetPREG(arg3, IORead8(timestamp, sign_16(arg1)+P_REG[arg2])); + } + lastop = LASTOP_IN; + END_OP_SKIPLO(); + + + // IN.H + BEGIN_OP(IN_H); + { + ADDCLOCK(3); + SetPREG(arg3, IORead16(timestamp, (sign_16(arg1)+P_REG[arg2]) & 0xFFFFFFFE)); + } + lastop = LASTOP_IN; + END_OP_SKIPLO(); + + + // IN.W + BEGIN_OP(IN_W); + if(IORead32) + { + ADDCLOCK(3); + SetPREG(arg3, IORead32(timestamp, (sign_16(arg1)+P_REG[arg2]) & 0xFFFFFFFC)); + } + else + { + uint32 eff_addr = (sign_16(arg1) + P_REG[arg2]) & 0xFFFFFFFC; + uint32 rv; + + ADDCLOCK(5); + + rv = IORead16(timestamp, eff_addr); + rv |= IORead16(timestamp, eff_addr | 2) << 16; + + SetPREG(arg3, rv); + } + lastop = LASTOP_IN; + END_OP_SKIPLO(); + + + // OUT.B + BEGIN_OP(OUT_B); + ADDCLOCK(1); + IOWrite8(timestamp, sign_16(arg2)+P_REG[arg3],P_REG[arg1]&0xFF); + + if(lastop == LASTOP_OUT) + { + ADDCLOCK(1); + } + lastop = LASTOP_OUT; + END_OP_SKIPLO(); + + + // OUT.H + BEGIN_OP(OUT_H); + ADDCLOCK(1); + IOWrite16(timestamp, (sign_16(arg2)+P_REG[arg3])&0xFFFFFFFE,P_REG[arg1]&0xFFFF); + + if(lastop == LASTOP_OUT) + { + ADDCLOCK(1); + } + lastop = LASTOP_OUT; + END_OP_SKIPLO(); + + + // OUT.W + BEGIN_OP(OUT_W); + ADDCLOCK(1); + + if(IOWrite32) + IOWrite32(timestamp, (sign_16(arg2)+P_REG[arg3])&0xFFFFFFFC,P_REG[arg1]); + else + { + uint32 eff_addr = (sign_16(arg2)+P_REG[arg3])&0xFFFFFFFC; + IOWrite16(timestamp, eff_addr, P_REG[arg1] & 0xFFFF); + IOWrite16(timestamp, eff_addr | 2, P_REG[arg1] >> 16); + } + + if(lastop == LASTOP_OUT) + { + if(IOWrite32) + { + ADDCLOCK(1); + } + else + { + ADDCLOCK(3); + } + } + lastop = LASTOP_OUT; + END_OP_SKIPLO(); + + BEGIN_OP(NOP); + (void)arg1; // arg1 is unused. + + ADDCLOCK(1); + RB_INCPCBY2(); + END_OP(); + + BEGIN_OP(RETI); + (void)arg1; // arg1 is unused. + + ADDCLOCK(10); + + //Return from Trap/Interupt + if(S_REG[PSW] & PSW_NP) { // Read the FE Reg + RB_SETPC(S_REG[FEPC] & 0xFFFFFFFE); + S_REG[PSW] = S_REG[FEPSW]; + } else { //Read the EI Reg Interupt + RB_SETPC(S_REG[EIPC] & 0xFFFFFFFE); + S_REG[PSW] = S_REG[EIPSW]; + } + RecalcIPendingCache(); + + RB_ADDBT(old_PC, RB_GETPC(), 0); + END_OP(); + + BEGIN_OP(MUL); + ADDCLOCK(13); + + uint64 temp = (int64)(int32)P_REG[arg1] * (int32)P_REG[arg2]; + + SetPREG(30, (uint32)(temp >> 32)); + SetPREG(arg2, temp); + SetSZ(P_REG[arg2]); + SetFlag(PSW_OV, temp != (uint64)(int64)(int32)(uint32)temp); + lastop = -1; + END_OP_SKIPLO(); + + BEGIN_OP(MULU); + ADDCLOCK(13); + uint64 temp = (uint64)P_REG[arg1] * (uint64)P_REG[arg2]; + + SetPREG(30, (uint32)(temp >> 32)); + SetPREG(arg2, (uint32)temp); + + SetSZ(P_REG[arg2]); + SetFlag(PSW_OV, temp != (uint32)temp); + lastop = -1; + END_OP_SKIPLO(); + + BEGIN_OP(DIVU); + ADDCLOCK(36); + if(P_REG[arg1] == 0) // Divide by zero! + { + RB_DECPCBY2(); + Exception(ZERO_DIV_HANDLER_ADDR, ECODE_ZERO_DIV); + CHECK_HALTED(); + } + else + { + // Careful here, since arg2 can be == 30 + uint32 quotient = (uint32)P_REG[arg2] / (uint32)P_REG[arg1]; + uint32 remainder = (uint32)P_REG[arg2] % (uint32)P_REG[arg1]; + + SetPREG(30, remainder); + SetPREG(arg2, quotient); + + SetFlag(PSW_OV, FALSE); + SetSZ(quotient); + } + lastop = -1; + END_OP_SKIPLO(); + + BEGIN_OP(DIV); + //if(P_REG[arg1] & P_REG[arg2] & 0x80000000) + //{ + // printf("Div: %08x %08x\n", P_REG[arg1], P_REG[arg2]); + //} + + ADDCLOCK(38); + if((uint32)P_REG[arg1] == 0) // Divide by zero! + { + RB_DECPCBY2(); + Exception(ZERO_DIV_HANDLER_ADDR, ECODE_ZERO_DIV); + CHECK_HALTED(); + } + else + { + if((P_REG[arg2]==0x80000000)&&(P_REG[arg1]==0xFFFFFFFF)) + { + SetFlag(PSW_OV, TRUE); + P_REG[30]=0; + SetPREG(arg2, 0x80000000); + SetSZ(P_REG[arg2]); + } + else + { + // Careful here, since arg2 can be == 30 + uint32 quotient = (int32)P_REG[arg2] / (int32)P_REG[arg1]; + uint32 remainder = (int32)P_REG[arg2] % (int32)P_REG[arg1]; + + SetPREG(30, remainder); + SetPREG(arg2, quotient); + + SetFlag(PSW_OV, FALSE); + SetSZ(quotient); + } + } + lastop = -1; + END_OP_SKIPLO(); + + BEGIN_OP(FPP); + ADDCLOCK(1); + fpu_subop(timestamp, arg3, arg1, arg2); + lastop = -1; + CHECK_HALTED(); + END_OP_SKIPLO(); + + BEGIN_OP(BSTR); + if(!in_bstr) + { + ADDCLOCK(1); + } + + if(bstr_subop(timestamp, arg2, arg1)) + { + RB_DECPCBY2(); + in_bstr = TRUE; + in_bstr_to = tmpop; + } + else + { + in_bstr = FALSE; + have_src_cache = have_dst_cache = FALSE; + } + END_OP(); + + BEGIN_OP(HALT); + (void)arg1; // arg1 is unused. + + ADDCLOCK(1); + Halted = HALT_HALT; + //printf("Untested opcode: HALT\n"); + END_OP(); + + BEGIN_OP(TRAP); + (void)arg2; // arg2 is unused. + + ADDCLOCK(15); + + Exception(TRAP_HANDLER_BASE + (arg1 & 0x10), ECODE_TRAP_BASE + (arg1 & 0x1F)); + CHECK_HALTED(); + END_OP(); + + BEGIN_OP(CAXI); + //printf("Untested opcode: caxi\n"); + + // Lock bus(N/A) + + ADDCLOCK(26); + + { + uint32 addr, tmp, compare_temp; + uint32 to_write; + + addr = sign_16(arg1) + P_REG[arg2]; + addr &= ~3; + + if(MemReadBus32[addr >> 24]) + tmp = MemRead32(timestamp, addr); + else + { + tmp = MemRead16(timestamp, addr); + tmp |= MemRead16(timestamp, addr | 2) << 16; + } + + compare_temp = P_REG[arg3] - tmp; + + SetSZ(compare_temp); + SetFlag(PSW_OV, ((P_REG[arg3]^tmp)&(P_REG[arg3]^compare_temp))&0x80000000); + SetFlag(PSW_CY, compare_temp > P_REG[arg3]); + + if(!compare_temp) // If they're equal... + to_write = P_REG[30]; + else + to_write = tmp; + + if(MemWriteBus32[addr >> 24]) + MemWrite32(timestamp, addr, to_write); + else + { + MemWrite16(timestamp, addr, to_write & 0xFFFF); + MemWrite16(timestamp, addr | 2, to_write >> 16); + } + P_REG[arg3] = tmp; + } + + // Unlock bus(N/A) + + END_OP(); + + + + op_INT_HANDLER: + { + int iNum = ilevel; + + S_REG[EIPC] = GetPC(); + S_REG[EIPSW] = S_REG[PSW]; + + SetPC(0xFFFFFE00 | (iNum << 4)); + + RB_ADDBT(old_PC, RB_GETPC(), 0xFE00 | (iNum << 4)); + + S_REG[ECR] = 0xFE00 | (iNum << 4); + + S_REG[PSW] |= PSW_EP; + S_REG[PSW] |= PSW_ID; + S_REG[PSW] &= ~PSW_AE; + + // Now, set need to set the interrupt enable level to he level that is being processed + 1, + // saturating at 15. + iNum++; + + if(iNum > 0x0F) + iNum = 0x0F; + + S_REG[PSW] &= ~PSW_IA; + S_REG[PSW] |= iNum << 16; + + // Accepting an interrupt takes us out of normal HALT status, of course! + Halted = HALT_NONE; + + // Invalidate our bitstring state(forces the instruction to be re-read, and the r/w buffers reloaded). + in_bstr = FALSE; + have_src_cache = FALSE; + have_dst_cache = FALSE; + + IPendingCache = 0; + + goto OpFinished; + } + + + BEGIN_OP(INVALID); + RB_DECPCBY2(); + if(!RB_AccurateMode) + { + RB_SETPC(RB_GETPC()); + if((uint32)(RB_RDOP(0, 0) >> 9) != opcode) + { + //printf("Trampoline: %08x %02x\n", RB_GETPC(), opcode >> 1); + } + else + { + ADDCLOCK(1); + Exception(INVALID_OP_HANDLER_ADDR, ECODE_INVALID_OP); + CHECK_HALTED(); + } + } + else + { + ADDCLOCK(1); + Exception(INVALID_OP_HANDLER_ADDR, ECODE_INVALID_OP); + CHECK_HALTED(); + } + END_OP(); + + } + + OpFinished: ; + lastop = opcode; + OpFinishedSkipLO: ; + } // end while(timestamp_rl < next_event_ts) + next_event_ts = event_handler(timestamp_rl); + //printf("Next: %d, Cur: %d\n", next_event_ts, timestamp); + } + +v810_timestamp = timestamp_rl; diff --git a/waterbox/pcfx/v810/v810_opt.h b/waterbox/pcfx/v810/v810_opt.h new file mode 100644 index 0000000000..585c50b269 --- /dev/null +++ b/waterbox/pcfx/v810/v810_opt.h @@ -0,0 +1,170 @@ +/////////////////////////////////////////////////////////////// +// File: v810_opt.h +// +// Description: Defines used in v810_dis.cpp +// + +#ifndef V810_OPT_H_ +#define V810_OPT_H_ + +#define sign_26(num) ((uint32)sign_x_to_s32(26, num)) +#define sign_16(num) ((uint32)(int16)(num)) +#define sign_14(num) ((uint32)sign_x_to_s32(14, num)) +#define sign_12(num) ((uint32)sign_x_to_s32(12, num)) +#define sign_9(num) ((uint32)sign_x_to_s32(9, num)) +#define sign_8(_value) ((uint32)(int8)(_value)) +#define sign_5(num) ((uint32)sign_x_to_s32(5, num)) + +/////////////////////////////////////////////////////////////////// +// Define Modes +#define AM_I 0x01 +#define AM_II 0x02 +#define AM_III 0x03 +#define AM_IV 0x04 +#define AM_V 0x05 +#define AM_VIa 0x06 // Mode6 form1 +#define AM_VIb 0x0A // Mode6 form2 +#define AM_VII 0x07 +#define AM_VIII 0x08 +#define AM_IX 0x09 +#define AM_BSTR 0x0B // Bit String Instructions +#define AM_FPP 0x0C // Floating Point Instructions +#define AM_UDEF 0x0D // Unknown/Undefined Instructions + +/////////////////////////////////////////////////////////////////// +// Table of Instructions Address Modes + +static const int addr_mode[80] = { + AM_I, AM_I, AM_I, AM_I, AM_I, AM_I, AM_I, AM_I, + AM_I, AM_I, AM_I, AM_I, AM_I, AM_I, AM_I, AM_I, + AM_II, AM_II, AM_II, AM_II, AM_II, AM_II, AM_II, AM_II, + AM_II, AM_IX, AM_IX, AM_UDEF, AM_II, AM_II, AM_II, AM_BSTR, + AM_UDEF, AM_UDEF, AM_UDEF, AM_UDEF, AM_UDEF, AM_UDEF, AM_UDEF, AM_UDEF, + AM_V, AM_V, AM_IV, AM_IV, AM_V, AM_V, AM_V, AM_V, + AM_VIa, AM_VIa, AM_UDEF, AM_VIa, AM_VIb, AM_VIb, AM_UDEF, AM_VIb, + AM_VIa, AM_VIa, AM_VIa, AM_VIa, AM_VIb, AM_VIb, AM_FPP, AM_VIb, + AM_III, AM_III, AM_III, AM_III, AM_III, AM_III, AM_III, AM_III, + AM_III, AM_III, AM_III, AM_III, AM_III, AM_III, AM_III, AM_III +}; +// All instructions greater than 0x50 are undefined (this should not be posible of cource) + + +/////////////////////////////////////////////////////////////////// +// Opcodes for V810 Instruction set +#define MOV 0x00 +#define ADD 0x01 +#define SUB 0x02 +#define CMP 0x03 +#define SHL 0x04 +#define SHR 0x05 +#define JMP 0x06 +#define SAR 0x07 +#define MUL 0x08 +#define DIV 0x09 +#define MULU 0x0A +#define DIVU 0x0B +#define OR 0x0C +#define AND 0x0D +#define XOR 0x0E +#define NOT 0x0F +#define MOV_I 0x10 +#define ADD_I 0x11 +#define SETF 0x12 +#define CMP_I 0x13 +#define SHL_I 0x14 +#define SHR_I 0x15 +#define EI 0x16 +#define SAR_I 0x17 +#define TRAP 0x18 +#define RETI 0x19 +#define HALT 0x1A + //0x1B +#define LDSR 0x1C +#define STSR 0x1D +#define DI 0x1E +#define BSTR 0x1F //Special Bit String Inst + //0x20 - 0x27 // Lost to Branch Instructions +#define MOVEA 0x28 +#define ADDI 0x29 +#define JR 0x2A +#define JAL 0x2B +#define ORI 0x2C +#define ANDI 0x2D +#define XORI 0x2E +#define MOVHI 0x2F +#define LD_B 0x30 +#define LD_H 0x31 + //0x32 +#define LD_W 0x33 +#define ST_B 0x34 +#define ST_H 0x35 + //0x36 +#define ST_W 0x37 +#define IN_B 0x38 +#define IN_H 0x39 +#define CAXI 0x3A +#define IN_W 0x3B +#define OUT_B 0x3C +#define OUT_H 0x3D +#define FPP 0x3E //Special Float Inst +#define OUT_W 0x3F + + +// Branch Instructions ( Extended opcode only for Branch command) +// Common instrcutions commented out + +#define BV 0x40 +#define BL 0x41 +#define BE 0x42 +#define BNH 0x43 +#define BN 0x44 +#define BR 0x45 +#define BLT 0x46 +#define BLE 0x47 +#define BNV 0x48 +#define BNL 0x49 +#define BNE 0x4A +#define BH 0x4B +#define BP 0x4C +#define NOP 0x4D +#define BGE 0x4E +#define BGT 0x4F + +//#define BC 0x41 +//#define BZ 0x42 +//#define BNC 0x49 +//#define BNZ 0x4A + +// Bit String Subopcodes +#define SCH0BSU 0x00 +#define SCH0BSD 0x01 +#define SCH1BSU 0x02 +#define SCH1BSD 0x03 + +#define ORBSU 0x08 +#define ANDBSU 0x09 +#define XORBSU 0x0A +#define MOVBSU 0x0B +#define ORNBSU 0x0C +#define ANDNBSU 0x0D +#define XORNBSU 0x0E +#define NOTBSU 0x0F + + +// Floating Point Subopcodes +#define CMPF_S 0x00 + +#define CVT_WS 0x02 +#define CVT_SW 0x03 +#define ADDF_S 0x04 +#define SUBF_S 0x05 +#define MULF_S 0x06 +#define DIVF_S 0x07 +#define XB 0x08 +#define XH 0x09 +#define REV 0x0A +#define TRNC_SW 0x0B +#define MPYHW 0x0C + +#endif //DEFINE_H +