From b3a80965af6a209e58f5ab74b7a3057130bf4024 Mon Sep 17 00:00:00 2001 From: adelikat Date: Fri, 14 Feb 2020 09:54:35 -0600 Subject: [PATCH] Virtu cleanup (#1846) * virtu - tabs instead of spaces * virtu - use nooget for Newtonsoft.Json * virtu - target .net 4.8 * resharper - target C# 8 * virtu - remove unused usings * virtu - remove some unused code * virtu - cleanups * virtu - trace logging - add flags to trace logs * virtu - cleanups * virtu - misc cleanups * virtu - make some stuff internal and simplify the api some * virtu - clock down more things and simplify api more * virtu - lock api down more * virtu- simplify, breaks older savestates however * virtu - simplify more * virtu - cleanup * virtu- more cleanup * virtu - reorg some files * virtu - more cleanup * virtu - more reorg * virtu - more reorg * virtu - make more things internal instead of public * virtu - make more things internal * virtu - update dll * cleanup * virtu - reorg * virtu - remove unitialize from machine component since nothing was utilizing it * virtu - simplify api * virtu - cleanup * virtu - cleanup and lock things down * virtu - lock down and cleanup * virtu - cleanup * virtu - simplify - breaks savestates * virtu - make PeripheralCard an interface with default implementation, breaks savestates * virtu - minimize use of machine component * virtu - cleanup * virtu - start minimizing dependencies * virtu - cleanup * virtu - simplify * apple II - simplify * virtu - move some biz logic into bizhawk * virtu - git rid of MachineComponent * virtu - delete no longer needed code * virtu - reorg * move serialization logic out of virtu and into bizhawk, this was our shenanigans * virtu - some small cleanups * virtu - simplify * virtu - dependency minimization * virtu - minimize dependencies * minimize dependencies * virtu - move drivelight property into component that controls it * virtu - minimize dependencies * virtu - minimize depenencies * move some machine logic to memory class * move some initialize logic into constructor * move initialize logic to constructor * move logic from Initialize to constructor * move initialize logic to constructor, simplify api to bizhawk * dll * virtu - movie some logic back into bizhawk * virtu - move Lagged property from machine to memory component * move more biz logic from virtu to biz * virtu - slight reorg * virtu - move some reset logic to constructor * virtu - move some stuff around * virtu - declare dependencies in memory class, no more dependencies on the machine class * move slots from machine to memory class * move some properties from machine to memory * move more things into the memory class * remove Machine.cs from virtu and make an equivalent container in bizhawk * virtu - cleanup * interface the cassette class and create a biz empty cassette "implementation" * move some more dummy logic from virtu to biz and put an interface in virtu * virtu - use an interface for a dependency * virtu - interface more things * virtu - more interfacing of things * virtu - interface more things * apple II/virtu - some reorg * virtu - cleanup * virtu - remove unused usages of disk name in disk classes * virtu- cleanup and simplify api * virtu - remove unused BootDrive property * virtu - cleanup and interface more * cleanup * update virtu dll --- .../BizHawk.Emulation.Cores.csproj | 8 +- .../Computers/AppleII/AppleII.IDebuggable.cs | 24 +- .../Computers/AppleII/AppleII.IEmulator.cs | 1 - .../AppleII/AppleII.IInputPollable.cs | 4 +- .../Computers/AppleII/AppleII.ISettable.cs | 20 +- .../AppleII/AppleII.ISoundProvider.cs | 4 +- .../Computers/AppleII/AppleII.IStatable.cs | 39 +- .../AppleII/AppleII.IVideoProvider.cs | 2 +- .../Computers/AppleII/AppleII.cs | 39 +- .../Computers/AppleII/Components.cs | 50 + .../AppleII/Components/CassetteComponent.cs | 19 + .../Components/EmptyPeripheralComponent.cs | 29 + .../AppleII/Components/GamePortComponent.cs | 21 + .../Computers/AppleII/JsonConvert.cs | 639 ++--- .../Computers/AppleII/LBSON.cs | 5 +- ExternalCoreProjects/Virtu/Cassette.cs | 22 - ExternalCoreProjects/Virtu/Cpu.Data.cs | 82 + ExternalCoreProjects/Virtu/Cpu.cs | 893 ++++--- ExternalCoreProjects/Virtu/CpuData.cs | 86 - ExternalCoreProjects/Virtu/Disk525.cs | 85 +- ExternalCoreProjects/Virtu/DiskDsk.cs | 532 ++-- .../Virtu/DiskIIController.cs | 77 +- ExternalCoreProjects/Virtu/DiskIIDrive.cs | 208 +- ExternalCoreProjects/Virtu/DiskNib.cs | 43 +- ExternalCoreProjects/Virtu/GamePort.cs | 256 -- ExternalCoreProjects/Virtu/ICassette.cs | 8 + ExternalCoreProjects/Virtu/IGamePort.cs | 16 + ExternalCoreProjects/Virtu/IPeripheralCard.cs | 23 + ExternalCoreProjects/Virtu/Keyboard.cs | 127 +- .../Virtu/Library/DisposableBase.cs | 26 - .../Virtu/Library/MathHelpers.cs | 15 - .../Virtu/Library/StreamExtensions.cs | 91 - ExternalCoreProjects/Virtu/Machine.cs | 216 -- .../Virtu/MachineComponent.cs | 37 - ExternalCoreProjects/Virtu/MachineEvents.cs | 182 +- ExternalCoreProjects/Virtu/Memory.Data.cs | 102 + ExternalCoreProjects/Virtu/Memory.cs | 500 ++-- ExternalCoreProjects/Virtu/MemoryData.cs | 108 - ExternalCoreProjects/Virtu/NoSlotClock.cs | 343 +-- ExternalCoreProjects/Virtu/PeripheralCard.cs | 49 - .../Virtu/Properties/AssemblyInfo.cs | 1 - .../Virtu/Services/AudioService.cs | 48 - .../Virtu/Services/DebugService.cs | 57 - .../Virtu/Services/VideoService.cs | 26 - ExternalCoreProjects/Virtu/Speaker.cs | 148 +- ExternalCoreProjects/Virtu/Video.Data.cs | 1631 +++++++++++++ ExternalCoreProjects/Virtu/Video.cs | 2135 ++++++++--------- ExternalCoreProjects/Virtu/VideoData.cs | 1644 ------------- ExternalCoreProjects/Virtu/Virtu.csproj | 24 +- .../Virtu/Virtu.csproj.DotSettings | 2 + .../Virtu/Virtu.sln.DotSettings | 79 + ExternalCoreProjects/Virtu/packages.config | 4 + References/Virtu.dll | Bin 156160 -> 139264 bytes 53 files changed, 5099 insertions(+), 5731 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Computers/AppleII/Components.cs create mode 100644 BizHawk.Emulation.Cores/Computers/AppleII/Components/CassetteComponent.cs create mode 100644 BizHawk.Emulation.Cores/Computers/AppleII/Components/EmptyPeripheralComponent.cs create mode 100644 BizHawk.Emulation.Cores/Computers/AppleII/Components/GamePortComponent.cs rename ExternalCoreProjects/Virtu/ExtraConverters.cs => BizHawk.Emulation.Cores/Computers/AppleII/JsonConvert.cs (76%) delete mode 100644 ExternalCoreProjects/Virtu/Cassette.cs create mode 100644 ExternalCoreProjects/Virtu/Cpu.Data.cs delete mode 100644 ExternalCoreProjects/Virtu/CpuData.cs delete mode 100644 ExternalCoreProjects/Virtu/GamePort.cs create mode 100644 ExternalCoreProjects/Virtu/ICassette.cs create mode 100644 ExternalCoreProjects/Virtu/IGamePort.cs create mode 100644 ExternalCoreProjects/Virtu/IPeripheralCard.cs delete mode 100644 ExternalCoreProjects/Virtu/Library/DisposableBase.cs delete mode 100644 ExternalCoreProjects/Virtu/Library/MathHelpers.cs delete mode 100644 ExternalCoreProjects/Virtu/Library/StreamExtensions.cs delete mode 100644 ExternalCoreProjects/Virtu/Machine.cs delete mode 100644 ExternalCoreProjects/Virtu/MachineComponent.cs create mode 100644 ExternalCoreProjects/Virtu/Memory.Data.cs delete mode 100644 ExternalCoreProjects/Virtu/MemoryData.cs delete mode 100644 ExternalCoreProjects/Virtu/PeripheralCard.cs delete mode 100644 ExternalCoreProjects/Virtu/Services/AudioService.cs delete mode 100644 ExternalCoreProjects/Virtu/Services/DebugService.cs delete mode 100644 ExternalCoreProjects/Virtu/Services/VideoService.cs create mode 100644 ExternalCoreProjects/Virtu/Video.Data.cs delete mode 100644 ExternalCoreProjects/Virtu/VideoData.cs create mode 100644 ExternalCoreProjects/Virtu/Virtu.csproj.DotSettings create mode 100644 ExternalCoreProjects/Virtu/Virtu.sln.DotSettings create mode 100644 ExternalCoreProjects/Virtu/packages.config diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index ce4f2dce17..c93ab432c5 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -50,12 +50,8 @@ - - + + diff --git a/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IDebuggable.cs b/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IDebuggable.cs index 1284ab2dd0..b143d24417 100644 --- a/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IDebuggable.cs @@ -9,7 +9,22 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII { public IDictionary GetCpuFlagsAndRegisters() { - var regs = _machine.GetCpuFlagsAndRegisters(); + var regs = new Dictionary + { + ["A"] = _machine.Cpu.RA, + ["X"] = _machine.Cpu.RX, + ["Y"] = _machine.Cpu.RY, + ["S"] = _machine.Cpu.RS, + ["PC"] = _machine.Cpu.RPC, + ["Flag C"] = _machine.Cpu.FlagC ? 1 : 0, + ["Flag Z"] = _machine.Cpu.FlagZ ? 1 : 0, + ["Flag I"] = _machine.Cpu.FlagI ? 1 : 0, + ["Flag D"] = _machine.Cpu.FlagD ? 1 : 0, + ["Flag B"] = _machine.Cpu.FlagB ? 1 : 0, + ["Flag V"] = _machine.Cpu.FlagV ? 1 : 0, + ["Flag N"] = _machine.Cpu.FlagN ? 1 : 0, + ["Flag T"] = _machine.Cpu.FlagT ? 1 : 0 + }; var dic = new Dictionary(); @@ -135,18 +150,19 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII var machineInVblank = _machine.Video.IsVBlank; + _machine.Events.HandleEvents(_machine.Cpu.Execute()); if (!machineInVblank && _machine.Video.IsVBlank) // Check if a frame has passed while stepping { Frame++; - if (_machine.Lagged) + if (_machine.Memory.Lagged) { LagCount++; } - _machine.Lagged = true; - _machine.DriveLight = false; + _machine.Memory.Lagged = true; + _machine.Memory.DiskIIController.DriveLight = false; } } diff --git a/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IEmulator.cs b/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IEmulator.cs index 82c5cda2c4..9e871c553b 100644 --- a/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IEmulator.cs @@ -32,7 +32,6 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII public void Dispose() { - _machine.Dispose(); } } } diff --git a/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IInputPollable.cs b/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IInputPollable.cs index f0ff0e291c..11f71559f8 100644 --- a/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IInputPollable.cs +++ b/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IInputPollable.cs @@ -8,8 +8,8 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII public bool IsLagFrame { - get => _machine.Lagged; - set => _machine.Lagged = value; + get => _machine.Memory.Lagged; + set => _machine.Memory.Lagged = value; } public IInputCallbackSystem InputCallbacks { get; } = new InputCallbackSystem(); diff --git a/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.ISettable.cs b/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.ISettable.cs index 6b61dea2b6..571acaaa9c 100644 --- a/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.ISettable.cs +++ b/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.ISettable.cs @@ -13,21 +13,12 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII [Description("Choose a monochrome monitor.")] public bool Monochrome { get; set; } - public Settings Clone() - { - return (Settings)MemberwiseClone(); - } + public Settings Clone() => (Settings)MemberwiseClone(); } - public Settings GetSettings() - { - return _settings.Clone(); - } + public Settings GetSettings() => _settings.Clone(); - public object GetSyncSettings() - { - return null; - } + public object GetSyncSettings() => null; public bool PutSettings(Settings o) { @@ -39,9 +30,6 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII return false; } - public bool PutSyncSettings(object o) - { - return false; - } + public bool PutSyncSettings(object o) => false; } } diff --git a/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.ISoundProvider.cs b/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.ISoundProvider.cs index 378601f045..a655d5247b 100644 --- a/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.ISoundProvider.cs +++ b/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.ISoundProvider.cs @@ -9,12 +9,12 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII public void GetSamplesSync(out short[] samples, out int nsamp) { - _machine.Speaker.AudioService.GetSamples(out samples, out nsamp); + _machine.Memory.Speaker.GetSamples(out samples, out nsamp); } public void DiscardSamples() { - _machine.Speaker.AudioService.Clear(); + _machine.Memory.Speaker.Clear(); } public SyncSoundMode SyncMode => SyncSoundMode.Sync; diff --git a/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IStatable.cs b/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IStatable.cs index 0da80cbc35..4aacbaadfe 100644 --- a/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IStatable.cs +++ b/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IStatable.cs @@ -2,9 +2,9 @@ using System.IO; using BizHawk.Emulation.Common; - using Jellyfish.Virtu; using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; namespace BizHawk.Emulation.Cores.Computers.AppleII { @@ -14,7 +14,7 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII { public override bool CanConvert(Type objectType) { - return objectType == typeof(Machine); + return objectType == typeof(Components); } public override bool CanRead => true; @@ -23,8 +23,7 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { - // uses its own serialization context: intentional - return Machine.Deserialize(reader); + return CreateSerializer().Deserialize(reader); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) @@ -52,7 +51,7 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII w.WritePropertyName("NextDiskPressed"); w.WriteValue(_nextPressed); w.WritePropertyName("Core"); - _machine.Serialize(w); + CreateSerializer().Serialize(w, _machine); w.WriteEndObject(); } @@ -79,7 +78,7 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII public int CurrentDisk; public bool PreviousDiskPressed; public bool NextDiskPressed; - public Machine Core; + public Components Core; } private void InitSaveStates() @@ -152,5 +151,33 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII writer.Flush(); return stream.ToArray(); } + + private static JsonSerializer CreateSerializer() + { + // TODO: converters could be cached for speedup + + var ser = new JsonSerializer + { + TypeNameHandling = TypeNameHandling.Auto, + PreserveReferencesHandling = PreserveReferencesHandling.All, // leaving out Array is a very important problem, and means that we can't rely on a directly shared array to work. + ReferenceLoopHandling = ReferenceLoopHandling.Serialize, + }; + + ser.Converters.Add(new TypeTypeConverter(new[] + { + // all expected Types to convert are either in this assembly or mscorlib + typeof(Memory).Assembly, + typeof(object).Assembly + })); + + ser.Converters.Add(new DelegateConverter()); + ser.Converters.Add(new ArrayConverter()); + + var cr = new DefaultContractResolver(); + cr.DefaultMembersSearchFlags |= System.Reflection.BindingFlags.NonPublic; + ser.ContractResolver = cr; + + return ser; + } } } diff --git a/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IVideoProvider.cs b/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IVideoProvider.cs index 2b2ed9d145..34e7d47c16 100644 --- a/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IVideoProvider.cs +++ b/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IVideoProvider.cs @@ -4,7 +4,7 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII { public partial class AppleII : IVideoProvider { - public int[] GetVideoBuffer() => _machine.Video.VideoService.fb; + public int[] GetVideoBuffer() => _machine.Video.GetVideoBuffer(); // put together, these describe a metric on the screen // they should define the smallest size that the buffer can be placed inside such that: diff --git a/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.cs b/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.cs index 938e521207..69e356a006 100644 --- a/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.cs +++ b/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.cs @@ -47,11 +47,9 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII _diskIIRom = comm.CoreFileProvider.GetFirmware( SystemId, "DiskII", true, "The DiskII firmware is required"); - _machine = new Machine(_appleIIRom, _diskIIRom); + _machine = new Components(_appleIIRom, _diskIIRom); - _machine.BizInitialize(); - - // make a writeable memory stream cloned from the rom. + // make a writable memory stream cloned from the rom. // for junk.dsk the .dsk is important because it determines the format from that InitDisk(); @@ -69,7 +67,7 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII private readonly List _romSet = new List(); private readonly ITraceable _tracer; - private Machine _machine; + private Components _machine; private byte[] _disk1; private readonly byte[] _appleIIRom; private readonly byte[] _diskIIRom; @@ -109,14 +107,12 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII { _disk1 = _romSet[CurrentDisk]; - // make a writeable memory stream cloned from the rom. + // make a writable memory stream cloned from the rom. // for junk.dsk the .dsk is important because it determines the format from that - _machine.BootDiskII.Drives[0].InsertDisk("junk.dsk", (byte[])_disk1.Clone(), false); + _machine.Memory.DiskIIController.Drive1.InsertDisk("junk.dsk", (byte[])_disk1.Clone(), false); } private static readonly List RealButtons = new List(Keyboard.GetKeyNames() - //.Where(k => k != "White Apple") // Hack because these buttons aren't wired up yet - //.Where(k => k != "Black Apple") .Where(k => k != "Reset")); private static readonly List ExtraButtons = new List @@ -126,7 +122,7 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII }; public bool DriveLightEnabled => true; - public bool DriveLightOn => _machine.DriveLight; + public bool DriveLightOn => _machine.Memory.DiskIIController.DriveLight; private bool _nextPressed; private bool _prevPressed; @@ -140,7 +136,7 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII }); } - private void FrameAdv(IController controller, bool render, bool rendersound) + private void FrameAdv(IController controller, bool render, bool renderSound) { if (_tracer.Enabled) { @@ -172,7 +168,7 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII _prevPressed = false; } - _machine.BizFrameAdvance(RealButtons.Where(controller.IsPressed)); + MachineAdvance(RealButtons.Where(controller.IsPressed)); if (IsLagFrame) { LagCount++; @@ -181,6 +177,25 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII Frame++; } + private void MachineAdvance(IEnumerable buttons) + { + _machine.Memory.Lagged = true; + _machine.Memory.DiskIIController.DriveLight = false; + _machine.Memory.Keyboard.SetKeys(buttons); + + // frame begins at vsync.. beginning of vblank + while (_machine.Video.IsVBlank) + { + _machine.Events.HandleEvents(_machine.Cpu.Execute()); + } + + // now, while not vblank, we're in a frame + while (!_machine.Video.IsVBlank) + { + _machine.Events.HandleEvents(_machine.Cpu.Execute()); + } + } + private void SetCallbacks() { _machine.Memory.ReadCallback = (addr) => diff --git a/BizHawk.Emulation.Cores/Computers/AppleII/Components.cs b/BizHawk.Emulation.Cores/Computers/AppleII/Components.cs new file mode 100644 index 0000000000..fec8a94e2d --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/AppleII/Components.cs @@ -0,0 +1,50 @@ +using Jellyfish.Virtu; + +namespace BizHawk.Emulation.Cores.Computers.AppleII +{ + /// + /// A container class for the individual machine components + /// + public sealed class Components + { + /// + /// for deserialization only!! + /// + public Components() { } + + public Components(byte[] appleIIe, byte[] diskIIRom) + { + Events = new MachineEvents(); + Memory = new Memory(appleIIe); + Cpu = new Cpu(Memory); + Video = new Video(Events, Memory); + + var emptySlot = new EmptyPeripheralCard(Video); + + // Necessary because of tangling dependencies between memory and video classes + Memory.Initialize( + new Keyboard(), + new GamePortComponent(), + new EmptyCassetteComponent(), + new Speaker(Events, Cpu), + Video, + new NoSlotClock(Video), + emptySlot, + emptySlot, + emptySlot, + emptySlot, + emptySlot, + new DiskIIController(Video, diskIIRom), + emptySlot); + + Cpu.Reset(); + Memory.Reset(); + Video.Reset(); + } + + public MachineEvents Events { get; set; } + public Memory Memory { get; private set; } + public Cpu Cpu { get; private set; } + public Video Video { get; private set; } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/AppleII/Components/CassetteComponent.cs b/BizHawk.Emulation.Cores/Computers/AppleII/Components/CassetteComponent.cs new file mode 100644 index 0000000000..b0d27904a8 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/AppleII/Components/CassetteComponent.cs @@ -0,0 +1,19 @@ +using Jellyfish.Virtu; + +namespace BizHawk.Emulation.Cores.Computers.AppleII +{ + /// + /// An empty implementation of ICassette, since we have not current built cassette functionality + /// + public class EmptyCassetteComponent : ICassette + { + // TODO: remove when json serialization is no longer used + public EmptyCassetteComponent() { } + + public bool ReadInput() => false; + + public void ToggleOutput() + { + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/AppleII/Components/EmptyPeripheralComponent.cs b/BizHawk.Emulation.Cores/Computers/AppleII/Components/EmptyPeripheralComponent.cs new file mode 100644 index 0000000000..643fbdd3d6 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/AppleII/Components/EmptyPeripheralComponent.cs @@ -0,0 +1,29 @@ +using Jellyfish.Virtu; + +namespace BizHawk.Emulation.Cores.Computers.AppleII +{ + /// + /// Represents an unused peripheral card + /// + public class EmptyPeripheralCard : IPeripheralCard + { + // TODO: make readonly once json isn't used + private Video _video; + + // TODO: remove when json isn't used + public EmptyPeripheralCard() { } + + public EmptyPeripheralCard(Video video) + { + _video = video; + } + + public int ReadIoRegionC0C0(int address) => _video.ReadFloatingBus(); + public int ReadIoRegionC1C7(int address) => _video.ReadFloatingBus(); + public int ReadIoRegionC8CF(int address) => _video.ReadFloatingBus(); + + public void WriteIoRegionC0C0(int address, int data) { } + public void WriteIoRegionC1C7(int address, int data) { } + public void WriteIoRegionC8CF(int address, int data) { } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/AppleII/Components/GamePortComponent.cs b/BizHawk.Emulation.Cores/Computers/AppleII/Components/GamePortComponent.cs new file mode 100644 index 0000000000..2411e7080d --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/AppleII/Components/GamePortComponent.cs @@ -0,0 +1,21 @@ +using Jellyfish.Virtu; + +namespace BizHawk.Emulation.Cores.Computers.AppleII +{ + /// + /// Currently a default implementation of the GamePort, needs to be built for gamepad support + /// + public class GamePortComponent : IGamePort + { + public bool ReadButton0() => Keyboard.WhiteAppleDown; + public bool ReadButton1() => Keyboard.BlackAppleDown; + public bool ReadButton2() => false; + + public bool Paddle0Strobe => false; + public bool Paddle1Strobe => false; + public bool Paddle2Strobe => false; + public bool Paddle3Strobe => false; + + public void TriggerTimers() { } + } +} diff --git a/ExternalCoreProjects/Virtu/ExtraConverters.cs b/BizHawk.Emulation.Cores/Computers/AppleII/JsonConvert.cs similarity index 76% rename from ExternalCoreProjects/Virtu/ExtraConverters.cs rename to BizHawk.Emulation.Cores/Computers/AppleII/JsonConvert.cs index 389b6148d3..40c9b0e4a5 100644 --- a/ExternalCoreProjects/Virtu/ExtraConverters.cs +++ b/BizHawk.Emulation.Cores/Computers/AppleII/JsonConvert.cs @@ -1,305 +1,334 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using Newtonsoft.Json.Linq; -using System.Reflection; - -namespace Jellyfish.Virtu -{ - public class ArrayConverter : JsonConverter - { - // JSON.NET cannot, when reading, use PreserveReferencesHandling on arrays, although it fully supports it on writing. - // Doing so while being able to fully preserve circular references would require storing the length of the array, - // or reading ahead in the JSON to compute the length. For arrays that could contain reference types, we choose the latter. - // For arrays of primitive types, there is no issue. - - // TODO: on serialization, the type of the object is available, but is the expected type (ie, the one that we'll be fed during deserialization) available? - // need this to at least detect covariance cases... - - public override bool CanConvert(Type objectType) - { - if (!typeof(Array).IsAssignableFrom(objectType)) - return false; - - if (objectType.GetArrayRank() > 1) - throw new NotImplementedException(); - - return true; - } - - public override bool CanRead { get { return true; } } - public override bool CanWrite { get { return true; } } - - private JsonSerializer bareserializer = new JsonSerializer(); // full default settings, separate context - - private static void ReadExpectType(JsonReader reader, JsonToken expected) - { - if (!reader.Read()) - throw new InvalidOperationException(); - if (reader.TokenType != expected) - throw new InvalidOperationException(); - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - if (reader.TokenType == JsonToken.Null) - return null; - else if (reader.TokenType != JsonToken.StartObject) - throw new InvalidOperationException(); - - ReadExpectType(reader, JsonToken.PropertyName); - string prop = reader.Value.ToString(); - ReadExpectType(reader, JsonToken.String); - string id = reader.Value.ToString(); - if (prop == "$ref") - { - object ret = serializer.ReferenceResolver.ResolveReference(serializer, id); - ReadExpectType(reader, JsonToken.EndObject); - return ret; - } - else if (prop == "$id") - { - ReadExpectType(reader, JsonToken.PropertyName); - prop = reader.Value.ToString(); - if (prop == "$length") // complex array - { - ReadExpectType(reader, JsonToken.Integer); - int length = Convert.ToInt32(reader.Value); - ReadExpectType(reader, JsonToken.PropertyName); - if (reader.Value.ToString() != "$values") - throw new InvalidOperationException(); - - Type elementType = objectType.GetElementType(); - - Array ret = Array.CreateInstance(elementType, length); - // must register reference before deserializing elements to handle possible circular references - serializer.ReferenceResolver.AddReference(serializer, id, ret); - int index = 0; - - ReadExpectType(reader, JsonToken.StartArray); - while (true) - { - if (!reader.Read()) - throw new InvalidOperationException(); - if (reader.TokenType == JsonToken.EndArray) - break; - ret.SetValue(serializer.Deserialize(reader, elementType), index++); - } - ReadExpectType(reader, JsonToken.EndObject); - return ret; - } - else if (prop == "$values") // simple array - { - if (!reader.Read()) - throw new InvalidOperationException(); - object ret = bareserializer.Deserialize(reader, objectType); - // OK to add this after deserializing, as arrays of primitive types can't contain backrefs - serializer.ReferenceResolver.AddReference(serializer, id, ret); - ReadExpectType(reader, JsonToken.EndObject); - return ret; - } - else - { - throw new InvalidOperationException(); - } - } - else - { - throw new InvalidOperationException(); - } - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - if (serializer.ReferenceResolver.IsReferenced(serializer, value)) - { - writer.WriteStartObject(); - - writer.WritePropertyName("$ref"); - writer.WriteValue(serializer.ReferenceResolver.GetReference(serializer, value)); - - writer.WriteEndObject(); - } - else - { - writer.WriteStartObject(); - - writer.WritePropertyName("$id"); - writer.WriteValue(serializer.ReferenceResolver.GetReference(serializer, value)); - - var elementType = value.GetType().GetElementType(); - if (elementType.IsPrimitive) - { - writer.WritePropertyName("$values"); - bareserializer.Serialize(writer, value); - } - else - { - var array = (Array)value; - writer.WritePropertyName("$length"); - writer.WriteValue(array.Length); - - writer.WritePropertyName("$values"); - writer.WriteStartArray(); - foreach (object o in array) - { - serializer.Serialize(writer, o, elementType); - } - writer.WriteEndArray(); - } - - writer.WriteEndObject(); - } - } - } - - public class TypeTypeConverter : JsonConverter - { - // serialize and deserialize types, ignoring assembly entirely and only using namespace+typename - // all types, including generic type arguments to supplied types, must be in one of the declared assemblies (only checked on read!) - // the main goal here is to have something with a slight chance of working across versions - - public TypeTypeConverter(IEnumerable ass) - { - assemblies = ass.ToList(); - } - - private List assemblies; - private Dictionary readlookup = new Dictionary(); - - public override bool CanConvert(Type objectType) - { - return typeof(Type).IsAssignableFrom(objectType); - } - - public override bool CanRead { get { return true; } } - public override bool CanWrite { get { return true; } } - - private Type GetType(string name) - { - Type ret; - if (!readlookup.TryGetValue(name, out ret)) - { - ret = assemblies.Select(ass => ass.GetType(name, false)).Where(t => t != null).Single(); - readlookup.Add(name, ret); - } - return ret; - } - - private static string GetName(Type type) - { - return string.Format("{0}.{1}", type.Namespace, type.Name); - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - if (reader.TokenType == JsonToken.Null) - { - return null; - } - else if (reader.TokenType == JsonToken.String) - { - return GetType(reader.Value.ToString()); - } - else if (reader.TokenType == JsonToken.StartArray) // full generic - { - List vals = serializer.Deserialize>(reader); - return GetType(vals[0]).MakeGenericType(vals.Skip(1).Select(GetType).ToArray()); - } - else - { - throw new InvalidOperationException(); - } - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - var type = (Type)value; - if (type.IsGenericType && !type.IsGenericTypeDefinition) - { - writer.WriteStartArray(); - writer.WriteValue(GetName(type)); - foreach (var t in type.GetGenericArguments()) - { - writer.WriteValue(GetName(t)); - } - writer.WriteEndArray(); - } - else - { - writer.WriteValue(GetName(type)); - } - } - } - - public class DelegateConverter : JsonConverter - { - // caveats: if used on anonymous delegates and/or closures, brittle to name changes in the generated classes and methods - // brittle to type name changes in general - // must be serialized in tree with any real classes referred to by closures - - // CAN NOT preserve reference equality of the delegates themselves, because the delegate must be created with - // target in one shot, with no possibility to change the target later. We preserve references to targets, - // and lose the ability to preserve references to delegates. - - - // TODO: much of this could be made somewhat smarter and more resilient - - public override bool CanConvert(Type objectType) - { - return typeof(Delegate).IsAssignableFrom(objectType); - } - - public override bool CanRead { get { return true; } } - public override bool CanWrite { get { return true; } } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - var slug = serializer.Deserialize(reader); - if (slug == null) - return null; - return slug.GetDelegate(); - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - var slug = new Slug((Delegate)value); - serializer.Serialize(writer, slug); - } - - private class Slug - { - public Type DelegateType; - public Type MethodDeclaringType; - public string MethodName; - public List MethodParameters; - public object Target; - - public Delegate GetDelegate() - { - var mi = MethodDeclaringType.GetMethod( - MethodName, - BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static, - null, - MethodParameters.ToArray(), - null); - - return Delegate.CreateDelegate(DelegateType, Target, mi); - } - - public Slug() { } - - public Slug(Delegate d) - { - DelegateType = d.GetType(); - MethodDeclaringType = d.Method.DeclaringType; - MethodName = d.Method.Name; - MethodParameters = d.Method.GetParameters().Select(p => p.ParameterType).ToList(); - Target = d.Target; - } - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Newtonsoft.Json; + +namespace BizHawk.Emulation.Cores.Computers.AppleII +{ + public class ArrayConverter : JsonConverter + { + // JSON.NET cannot, when reading, use PreserveReferencesHandling on arrays, although it fully supports it on writing. + // Doing so while being able to fully preserve circular references would require storing the length of the array, + // or reading ahead in the JSON to compute the length. For arrays that could contain reference types, we choose the latter. + // For arrays of primitive types, there is no issue. + + // TODO: on serialization, the type of the object is available, but is the expected type (ie, the one that we'll be fed during deserialization) available? + // need this to at least detect covariance cases... + + public override bool CanConvert(Type objectType) + { + if (!typeof(Array).IsAssignableFrom(objectType)) + { + return false; + } + + if (objectType.GetArrayRank() > 1) + { + throw new NotImplementedException(); + } + + return true; + } + + public override bool CanRead => true; + public override bool CanWrite => true; + + private readonly JsonSerializer _bareSerializer = new JsonSerializer(); // full default settings, separate context + + // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local + private static void ReadExpectType(JsonReader reader, JsonToken expected) + { + if (!reader.Read()) + { + throw new InvalidOperationException(); + } + + if (reader.TokenType != expected) + { + throw new InvalidOperationException(); + } + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) + { + return null; + } + + if (reader.TokenType != JsonToken.StartObject) + { + throw new InvalidOperationException(); + } + + ReadExpectType(reader, JsonToken.PropertyName); + string prop = reader.Value.ToString(); + ReadExpectType(reader, JsonToken.String); + string id = reader.Value.ToString(); + if (prop == "$ref") + { + object ret = serializer.ReferenceResolver.ResolveReference(serializer, id); + ReadExpectType(reader, JsonToken.EndObject); + return ret; + } + + if (prop == "$id") + { + ReadExpectType(reader, JsonToken.PropertyName); + prop = reader.Value.ToString(); + if (prop == "$length") // complex array + { + ReadExpectType(reader, JsonToken.Integer); + int length = Convert.ToInt32(reader.Value); + ReadExpectType(reader, JsonToken.PropertyName); + if (reader.Value.ToString() != "$values") + throw new InvalidOperationException(); + + Type elementType = objectType.GetElementType(); + + // ReSharper disable once AssignNullToNotNullAttribute + Array ret = Array.CreateInstance(elementType, length); + + // must register reference before deserializing elements to handle possible circular references + serializer.ReferenceResolver.AddReference(serializer, id, ret); + int index = 0; + + ReadExpectType(reader, JsonToken.StartArray); + while (true) + { + if (!reader.Read()) + throw new InvalidOperationException(); + if (reader.TokenType == JsonToken.EndArray) + break; + ret.SetValue(serializer.Deserialize(reader, elementType), index++); + } + ReadExpectType(reader, JsonToken.EndObject); + return ret; + } + + if (prop == "$values") // simple array + { + if (!reader.Read()) + { + throw new InvalidOperationException(); + } + + object ret = _bareSerializer.Deserialize(reader, objectType); + + // OK to add this after deserializing, as arrays of primitive types can't contain backrefs + serializer.ReferenceResolver.AddReference(serializer, id, ret); + ReadExpectType(reader, JsonToken.EndObject); + return ret; + } + + throw new InvalidOperationException(); + } + + throw new InvalidOperationException(); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (serializer.ReferenceResolver.IsReferenced(serializer, value)) + { + writer.WriteStartObject(); + + writer.WritePropertyName("$ref"); + writer.WriteValue(serializer.ReferenceResolver.GetReference(serializer, value)); + + writer.WriteEndObject(); + } + else + { + writer.WriteStartObject(); + + writer.WritePropertyName("$id"); + writer.WriteValue(serializer.ReferenceResolver.GetReference(serializer, value)); + + var elementType = value.GetType().GetElementType(); + if (elementType?.IsPrimitive ?? false) + { + writer.WritePropertyName("$values"); + _bareSerializer.Serialize(writer, value); + } + else + { + var array = (Array)value; + writer.WritePropertyName("$length"); + writer.WriteValue(array.Length); + + writer.WritePropertyName("$values"); + writer.WriteStartArray(); + foreach (object o in array) + { + serializer.Serialize(writer, o, elementType); + } + + writer.WriteEndArray(); + } + + writer.WriteEndObject(); + } + } + } + + public class TypeTypeConverter : JsonConverter + { + // serialize and deserialize types, ignoring assembly entirely and only using namespace+typename + // all types, including generic type arguments to supplied types, must be in one of the declared assemblies (only checked on read!) + // the main goal here is to have something with a slight chance of working across versions + public TypeTypeConverter(IEnumerable ass) + { + _assemblies = ass.ToList(); + } + + private readonly List _assemblies; + private readonly Dictionary _readLookup = new Dictionary(); + + public override bool CanConvert(Type objectType) + { + return typeof(Type).IsAssignableFrom(objectType); + } + + public override bool CanRead => true; + public override bool CanWrite => true; + + private Type GetType(string name) + { + if (!_readLookup.TryGetValue(name, out var ret)) + { + ret = _assemblies.Select(ass => ass.GetType(name, false)).Single(t => t != null); + _readLookup.Add(name, ret); + } + + return ret; + } + + private static string GetName(Type type) + { + return $"{type.Namespace}.{type.Name}"; + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) + { + return null; + } + + if (reader.TokenType == JsonToken.String) + { + return GetType(reader.Value.ToString()); + } + + if (reader.TokenType == JsonToken.StartArray) // full generic + { + List values = serializer.Deserialize>(reader); + return GetType(values[0]).MakeGenericType(values.Skip(1).Select(GetType).ToArray()); + } + + throw new InvalidOperationException(); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var type = (Type)value; + if (type.IsGenericType && !type.IsGenericTypeDefinition) + { + writer.WriteStartArray(); + writer.WriteValue(GetName(type)); + foreach (var t in type.GetGenericArguments()) + { + writer.WriteValue(GetName(t)); + } + writer.WriteEndArray(); + } + else + { + writer.WriteValue(GetName(type)); + } + } + } + + public class DelegateConverter : JsonConverter + { + // caveats: if used on anonymous delegates and/or closures, brittle to name changes in the generated classes and methods + // brittle to type name changes in general + // must be serialized in tree with any real classes referred to by closures + + // CAN NOT preserve reference equality of the delegates themselves, because the delegate must be created with + // target in one shot, with no possibility to change the target later. We preserve references to targets, + // and lose the ability to preserve references to delegates. + + + // TODO: much of this could be made somewhat smarter and more resilient + + public override bool CanConvert(Type objectType) + { + return typeof(Delegate).IsAssignableFrom(objectType); + } + + public override bool CanRead => true; + public override bool CanWrite => true; + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + var slug = serializer.Deserialize(reader); + return slug?.GetDelegate(); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var slug = new Slug((Delegate)value); + serializer.Serialize(writer, slug); + } + + private class Slug + { + // ReSharper disable once MemberCanBePrivate.Local + // ReSharper disable once FieldCanBeMadeReadOnly.Local + public Type DelegateType; + + // ReSharper disable once MemberCanBePrivate.Local + // ReSharper disable once FieldCanBeMadeReadOnly.Local + public Type MethodDeclaringType; + + // ReSharper disable once MemberCanBePrivate.Local + // ReSharper disable once FieldCanBeMadeReadOnly.Local + public string MethodName; + + // ReSharper disable once MemberCanBePrivate.Local + // ReSharper disable once FieldCanBeMadeReadOnly.Local + public List MethodParameters; + + // ReSharper disable once MemberCanBePrivate.Local + // ReSharper disable once FieldCanBeMadeReadOnly.Local + public object Target; + + public Delegate GetDelegate() + { + var mi = MethodDeclaringType.GetMethod( + MethodName, + BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static, + null, + MethodParameters.ToArray(), + null); + + // ReSharper disable once AssignNullToNotNullAttribute + return Delegate.CreateDelegate(DelegateType, Target, mi); + } + + public Slug() { } + + public Slug(Delegate d) + { + DelegateType = d.GetType(); + MethodDeclaringType = d.Method.DeclaringType; + MethodName = d.Method.Name; + MethodParameters = d.Method.GetParameters().Select(p => p.ParameterType).ToList(); + Target = d.Target; + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/AppleII/LBSON.cs b/BizHawk.Emulation.Cores/Computers/AppleII/LBSON.cs index 56efe107b4..c59c7d0c03 100644 --- a/BizHawk.Emulation.Cores/Computers/AppleII/LBSON.cs +++ b/BizHawk.Emulation.Cores/Computers/AppleII/LBSON.cs @@ -49,10 +49,7 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII // as best as I can tell, the serializers refer to depth, but don't actually need to work except when doing certain error recovery public override int Depth => 0; - public override string Path - { - get { throw new NotImplementedException(); } - } + public override string Path => throw new NotImplementedException(); public override Type ValueType => _v?.GetType(); diff --git a/ExternalCoreProjects/Virtu/Cassette.cs b/ExternalCoreProjects/Virtu/Cassette.cs deleted file mode 100644 index 0cde6083b2..0000000000 --- a/ExternalCoreProjects/Virtu/Cassette.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Jellyfish.Virtu -{ - public sealed class Cassette : MachineComponent - { - public Cassette() { } - public Cassette(Machine machine) : - base(machine) - { - } - - public bool ReadInput() - { - return false; - } - - public void ToggleOutput() - { - } - } -} diff --git a/ExternalCoreProjects/Virtu/Cpu.Data.cs b/ExternalCoreProjects/Virtu/Cpu.Data.cs new file mode 100644 index 0000000000..30bc7deda7 --- /dev/null +++ b/ExternalCoreProjects/Virtu/Cpu.Data.cs @@ -0,0 +1,82 @@ +using Newtonsoft.Json; +using System; + +namespace Jellyfish.Virtu +{ + // ReSharper disable once UnusedMember.Global + public partial class Cpu + { + [JsonIgnore] + private Action[] _executeOpCode65N02; + [JsonIgnore] + private Action[] _executeOpCode65C02; + + private const int Pc = 0x01; + private const int Pz = 0x02; + private const int Pi = 0x04; + private const int Pd = 0x08; + private const int Pb = 0x10; + private const int Pv = 0x40; + private const int Pn = 0x80; + + private static readonly int[] DataPn = + { + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, + Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, + Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, + Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, + Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, + Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, + Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, + Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn + }; + + private static readonly int[] DataPz = + { + Pz, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 + }; + + private static readonly int[] DataPnz = + { + Pz, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, + Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, + Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, + Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, + Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, + Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, + Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, + Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn, Pn + }; + } +} diff --git a/ExternalCoreProjects/Virtu/Cpu.cs b/ExternalCoreProjects/Virtu/Cpu.cs index fc25fc43eb..e824ac6b2e 100644 --- a/ExternalCoreProjects/Virtu/Cpu.cs +++ b/ExternalCoreProjects/Virtu/Cpu.cs @@ -1,182 +1,189 @@ using System; -using System.Diagnostics.CodeAnalysis; using System.Globalization; -using System.IO; using Newtonsoft.Json; namespace Jellyfish.Virtu { - public sealed partial class Cpu : MachineComponent + public interface ICpu { + // ReSharper disable once UnusedMember.Global + void Reset(); + + // ReSharper disable once UnusedMember.Global + int Execute(); + long Cycles { get; } + int Multiplier { get; } + } + + // ReSharper disable once UnusedMember.Global + public sealed partial class Cpu : ICpu + { + // ReSharper disable once FieldCanBeMadeReadOnly.Local + private IMemoryBus _memory; + + // ReSharper disable once UnusedMember.Global public Cpu() { InitializeOpCodeDelegates(); } - public Cpu(Machine machine) : - base(machine) + public Cpu(IMemoryBus memory) { + _memory = memory; InitializeOpCodeDelegates(); + + Is65C02 = true; + Multiplier = 1; + RS = 0xFF; } private void InitializeOpCodeDelegates() { - ExecuteOpCode65N02 = new Action[OpCodeCount] - { - Execute65X02Brk00, Execute65X02Ora01, Execute65N02Nop02, Execute65N02Nop03, - Execute65N02Nop04, Execute65X02Ora05, Execute65X02Asl06, Execute65N02Nop07, - Execute65X02Php08, Execute65X02Ora09, Execute65X02Asl0A, Execute65N02Nop0B, - Execute65N02Nop0C, Execute65X02Ora0D, Execute65X02Asl0E, Execute65N02Nop0F, - Execute65X02Bpl10, Execute65X02Ora11, Execute65N02Nop12, Execute65N02Nop13, - Execute65N02Nop14, Execute65X02Ora15, Execute65X02Asl16, Execute65N02Nop17, - Execute65X02Clc18, Execute65X02Ora19, Execute65N02Nop1A, Execute65N02Nop1B, - Execute65N02Nop1C, Execute65X02Ora1D, Execute65N02Asl1E, Execute65N02Nop1F, - Execute65X02Jsr20, Execute65X02And21, Execute65N02Nop22, Execute65N02Nop23, - Execute65X02Bit24, Execute65X02And25, Execute65X02Rol26, Execute65N02Nop27, - Execute65X02Plp28, Execute65X02And29, Execute65X02Rol2A, Execute65N02Nop2B, - Execute65X02Bit2C, Execute65X02And2D, Execute65X02Rol2E, Execute65N02Nop2F, - Execute65X02Bmi30, Execute65X02And31, Execute65N02Nop32, Execute65N02Nop33, - Execute65N02Nop34, Execute65X02And35, Execute65X02Rol36, Execute65N02Nop37, - Execute65X02Sec38, Execute65X02And39, Execute65N02Nop3A, Execute65N02Nop3B, - Execute65N02Nop3C, Execute65X02And3D, Execute65N02Rol3E, Execute65N02Nop3F, - Execute65X02Rti40, Execute65X02Eor41, Execute65N02Nop42, Execute65N02Nop43, - Execute65N02Nop44, Execute65X02Eor45, Execute65X02Lsr46, Execute65N02Nop47, - Execute65X02Pha48, Execute65X02Eor49, Execute65X02Lsr4A, Execute65N02Nop4B, - Execute65X02Jmp4C, Execute65X02Eor4D, Execute65X02Lsr4E, Execute65N02Nop4F, - Execute65X02Bvc50, Execute65X02Eor51, Execute65N02Nop52, Execute65N02Nop53, - Execute65N02Nop54, Execute65X02Eor55, Execute65X02Lsr56, Execute65N02Nop57, - Execute65X02Cli58, Execute65X02Eor59, Execute65N02Nop5A, Execute65N02Nop5B, - Execute65N02Nop5C, Execute65X02Eor5D, Execute65N02Lsr5E, Execute65N02Nop5F, - Execute65X02Rts60, Execute65N02Adc61, Execute65N02Nop62, Execute65N02Nop63, - Execute65N02Nop64, Execute65N02Adc65, Execute65X02Ror66, Execute65N02Nop67, - Execute65X02Pla68, Execute65N02Adc69, Execute65X02Ror6A, Execute65N02Nop6B, - Execute65N02Jmp6C, Execute65N02Adc6D, Execute65X02Ror6E, Execute65N02Nop6F, - Execute65X02Bvs70, Execute65N02Adc71, Execute65N02Nop72, Execute65N02Nop73, - Execute65N02Nop74, Execute65N02Adc75, Execute65X02Ror76, Execute65N02Nop77, - Execute65X02Sei78, Execute65N02Adc79, Execute65N02Nop7A, Execute65N02Nop7B, - Execute65N02Nop7C, Execute65N02Adc7D, Execute65N02Ror7E, Execute65N02Nop7F, - Execute65N02Nop80, Execute65X02Sta81, Execute65N02Nop82, Execute65N02Nop83, - Execute65X02Sty84, Execute65X02Sta85, Execute65X02Stx86, Execute65N02Nop87, - Execute65X02Dey88, Execute65N02Nop89, Execute65X02Txa8A, Execute65N02Nop8B, - Execute65X02Sty8C, Execute65X02Sta8D, Execute65X02Stx8E, Execute65N02Nop8F, - Execute65X02Bcc90, Execute65X02Sta91, Execute65N02Nop92, Execute65N02Nop93, - Execute65X02Sty94, Execute65X02Sta95, Execute65X02Stx96, Execute65N02Nop97, - Execute65X02Tya98, Execute65X02Sta99, Execute65X02Txs9A, Execute65N02Nop9B, - Execute65N02Nop9C, Execute65X02Sta9D, Execute65N02Nop9E, Execute65N02Nop9F, - Execute65X02LdyA0, Execute65X02LdaA1, Execute65X02LdxA2, Execute65N02NopA3, - Execute65X02LdyA4, Execute65X02LdaA5, Execute65X02LdxA6, Execute65N02NopA7, - Execute65X02TayA8, Execute65X02LdaA9, Execute65X02TaxAA, Execute65N02NopAB, - Execute65X02LdyAC, Execute65X02LdaAD, Execute65X02LdxAE, Execute65N02NopAF, - Execute65X02BcsB0, Execute65X02LdaB1, Execute65N02NopB2, Execute65N02NopB3, - Execute65X02LdyB4, Execute65X02LdaB5, Execute65X02LdxB6, Execute65N02NopB7, - Execute65X02ClvB8, Execute65X02LdaB9, Execute65X02TsxBA, Execute65N02NopBB, - Execute65X02LdyBC, Execute65X02LdaBD, Execute65X02LdxBE, Execute65N02NopBF, - Execute65X02CpyC0, Execute65X02CmpC1, Execute65N02NopC2, Execute65N02NopC3, - Execute65X02CpyC4, Execute65X02CmpC5, Execute65X02DecC6, Execute65N02NopC7, - Execute65X02InyC8, Execute65X02CmpC9, Execute65X02DexCA, Execute65N02NopCB, - Execute65X02CpyCC, Execute65X02CmpCD, Execute65X02DecCE, Execute65N02NopCF, - Execute65X02BneD0, Execute65X02CmpD1, Execute65N02NopD2, Execute65N02NopD3, - Execute65N02NopD4, Execute65X02CmpD5, Execute65X02DecD6, Execute65N02NopD7, - Execute65X02CldD8, Execute65X02CmpD9, Execute65N02NopDA, Execute65N02NopDB, - Execute65N02NopDC, Execute65X02CmpDD, Execute65N02DecDE, Execute65N02NopDF, - Execute65X02CpxE0, Execute65N02SbcE1, Execute65N02NopE2, Execute65N02NopE3, - Execute65X02CpxE4, Execute65N02SbcE5, Execute65X02IncE6, Execute65N02NopE7, - Execute65X02InxE8, Execute65N02SbcE9, Execute65X02NopEA, Execute65N02NopEB, - Execute65X02CpxEC, Execute65N02SbcED, Execute65X02IncEE, Execute65N02NopEF, - Execute65X02BeqF0, Execute65N02SbcF1, Execute65N02NopF2, Execute65N02NopF3, - Execute65N02NopF4, Execute65N02SbcF5, Execute65X02IncF6, Execute65N02NopF7, - Execute65X02SedF8, Execute65N02SbcF9, Execute65N02NopFA, Execute65N02NopFB, - Execute65N02NopFC, Execute65N02SbcFD, Execute65N02IncFE, Execute65N02NopFF - }; + _executeOpCode65N02 = new Action[] + { + Execute65X02Brk00, Execute65X02Ora01, Execute65N02Nop02, Execute65N02Nop03, + Execute65N02Nop04, Execute65X02Ora05, Execute65X02Asl06, Execute65N02Nop07, + Execute65X02Php08, Execute65X02Ora09, Execute65X02Asl0A, Execute65N02Nop0B, + Execute65N02Nop0C, Execute65X02Ora0D, Execute65X02Asl0E, Execute65N02Nop0F, + Execute65X02Bpl10, Execute65X02Ora11, Execute65N02Nop12, Execute65N02Nop13, + Execute65N02Nop14, Execute65X02Ora15, Execute65X02Asl16, Execute65N02Nop17, + Execute65X02Clc18, Execute65X02Ora19, Execute65N02Nop1A, Execute65N02Nop1B, + Execute65N02Nop1C, Execute65X02Ora1D, Execute65N02Asl1E, Execute65N02Nop1F, + Execute65X02Jsr20, Execute65X02And21, Execute65N02Nop22, Execute65N02Nop23, + Execute65X02Bit24, Execute65X02And25, Execute65X02Rol26, Execute65N02Nop27, + Execute65X02Plp28, Execute65X02And29, Execute65X02Rol2A, Execute65N02Nop2B, + Execute65X02Bit2C, Execute65X02And2D, Execute65X02Rol2E, Execute65N02Nop2F, + Execute65X02Bmi30, Execute65X02And31, Execute65N02Nop32, Execute65N02Nop33, + Execute65N02Nop34, Execute65X02And35, Execute65X02Rol36, Execute65N02Nop37, + Execute65X02Sec38, Execute65X02And39, Execute65N02Nop3A, Execute65N02Nop3B, + Execute65N02Nop3C, Execute65X02And3D, Execute65N02Rol3E, Execute65N02Nop3F, + Execute65X02Rti40, Execute65X02Eor41, Execute65N02Nop42, Execute65N02Nop43, + Execute65N02Nop44, Execute65X02Eor45, Execute65X02Lsr46, Execute65N02Nop47, + Execute65X02Pha48, Execute65X02Eor49, Execute65X02Lsr4A, Execute65N02Nop4B, + Execute65X02Jmp4C, Execute65X02Eor4D, Execute65X02Lsr4E, Execute65N02Nop4F, + Execute65X02Bvc50, Execute65X02Eor51, Execute65N02Nop52, Execute65N02Nop53, + Execute65N02Nop54, Execute65X02Eor55, Execute65X02Lsr56, Execute65N02Nop57, + Execute65X02Cli58, Execute65X02Eor59, Execute65N02Nop5A, Execute65N02Nop5B, + Execute65N02Nop5C, Execute65X02Eor5D, Execute65N02Lsr5E, Execute65N02Nop5F, + Execute65X02Rts60, Execute65N02Adc61, Execute65N02Nop62, Execute65N02Nop63, + Execute65N02Nop64, Execute65N02Adc65, Execute65X02Ror66, Execute65N02Nop67, + Execute65X02Pla68, Execute65N02Adc69, Execute65X02Ror6A, Execute65N02Nop6B, + Execute65N02Jmp6C, Execute65N02Adc6D, Execute65X02Ror6E, Execute65N02Nop6F, + Execute65X02Bvs70, Execute65N02Adc71, Execute65N02Nop72, Execute65N02Nop73, + Execute65N02Nop74, Execute65N02Adc75, Execute65X02Ror76, Execute65N02Nop77, + Execute65X02Sei78, Execute65N02Adc79, Execute65N02Nop7A, Execute65N02Nop7B, + Execute65N02Nop7C, Execute65N02Adc7D, Execute65N02Ror7E, Execute65N02Nop7F, + Execute65N02Nop80, Execute65X02Sta81, Execute65N02Nop82, Execute65N02Nop83, + Execute65X02Sty84, Execute65X02Sta85, Execute65X02Stx86, Execute65N02Nop87, + Execute65X02Dey88, Execute65N02Nop89, Execute65X02Txa8A, Execute65N02Nop8B, + Execute65X02Sty8C, Execute65X02Sta8D, Execute65X02Stx8E, Execute65N02Nop8F, + Execute65X02Bcc90, Execute65X02Sta91, Execute65N02Nop92, Execute65N02Nop93, + Execute65X02Sty94, Execute65X02Sta95, Execute65X02Stx96, Execute65N02Nop97, + Execute65X02Tya98, Execute65X02Sta99, Execute65X02Txs9A, Execute65N02Nop9B, + Execute65N02Nop9C, Execute65X02Sta9D, Execute65N02Nop9E, Execute65N02Nop9F, + Execute65X02LdyA0, Execute65X02LdaA1, Execute65X02LdxA2, Execute65N02NopA3, + Execute65X02LdyA4, Execute65X02LdaA5, Execute65X02LdxA6, Execute65N02NopA7, + Execute65X02TayA8, Execute65X02LdaA9, Execute65X02TaxAA, Execute65N02NopAB, + Execute65X02LdyAC, Execute65X02LdaAD, Execute65X02LdxAE, Execute65N02NopAF, + Execute65X02BcsB0, Execute65X02LdaB1, Execute65N02NopB2, Execute65N02NopB3, + Execute65X02LdyB4, Execute65X02LdaB5, Execute65X02LdxB6, Execute65N02NopB7, + Execute65X02ClvB8, Execute65X02LdaB9, Execute65X02TsxBA, Execute65N02NopBB, + Execute65X02LdyBC, Execute65X02LdaBD, Execute65X02LdxBE, Execute65N02NopBF, + Execute65X02CpyC0, Execute65X02CmpC1, Execute65N02NopC2, Execute65N02NopC3, + Execute65X02CpyC4, Execute65X02CmpC5, Execute65X02DecC6, Execute65N02NopC7, + Execute65X02InyC8, Execute65X02CmpC9, Execute65X02DexCA, Execute65N02NopCB, + Execute65X02CpyCC, Execute65X02CmpCD, Execute65X02DecCE, Execute65N02NopCF, + Execute65X02BneD0, Execute65X02CmpD1, Execute65N02NopD2, Execute65N02NopD3, + Execute65N02NopD4, Execute65X02CmpD5, Execute65X02DecD6, Execute65N02NopD7, + Execute65X02CldD8, Execute65X02CmpD9, Execute65N02NopDA, Execute65N02NopDB, + Execute65N02NopDC, Execute65X02CmpDD, Execute65N02DecDE, Execute65N02NopDF, + Execute65X02CpxE0, Execute65N02SbcE1, Execute65N02NopE2, Execute65N02NopE3, + Execute65X02CpxE4, Execute65N02SbcE5, Execute65X02IncE6, Execute65N02NopE7, + Execute65X02InxE8, Execute65N02SbcE9, Execute65X02NopEA, Execute65N02NopEB, + Execute65X02CpxEC, Execute65N02SbcED, Execute65X02IncEE, Execute65N02NopEF, + Execute65X02BeqF0, Execute65N02SbcF1, Execute65N02NopF2, Execute65N02NopF3, + Execute65N02NopF4, Execute65N02SbcF5, Execute65X02IncF6, Execute65N02NopF7, + Execute65X02SedF8, Execute65N02SbcF9, Execute65N02NopFA, Execute65N02NopFB, + Execute65N02NopFC, Execute65N02SbcFD, Execute65N02IncFE, Execute65N02NopFF + }; - ExecuteOpCode65C02 = new Action[OpCodeCount] - { - Execute65X02Brk00, Execute65X02Ora01, Execute65C02Nop02, Execute65C02Nop03, - Execute65C02Tsb04, Execute65X02Ora05, Execute65X02Asl06, Execute65C02Nop07, - Execute65X02Php08, Execute65X02Ora09, Execute65X02Asl0A, Execute65C02Nop0B, - Execute65C02Tsb0C, Execute65X02Ora0D, Execute65X02Asl0E, Execute65C02Nop0F, - Execute65X02Bpl10, Execute65X02Ora11, Execute65C02Ora12, Execute65C02Nop13, - Execute65C02Trb14, Execute65X02Ora15, Execute65X02Asl16, Execute65C02Nop17, - Execute65X02Clc18, Execute65X02Ora19, Execute65C02Ina1A, Execute65C02Nop1B, - Execute65C02Trb1C, Execute65X02Ora1D, Execute65C02Asl1E, Execute65C02Nop1F, - Execute65X02Jsr20, Execute65X02And21, Execute65C02Nop22, Execute65C02Nop23, - Execute65X02Bit24, Execute65X02And25, Execute65X02Rol26, Execute65C02Nop27, - Execute65X02Plp28, Execute65X02And29, Execute65X02Rol2A, Execute65C02Nop2B, - Execute65X02Bit2C, Execute65X02And2D, Execute65X02Rol2E, Execute65C02Nop2F, - Execute65X02Bmi30, Execute65X02And31, Execute65C02And32, Execute65C02Nop33, - Execute65C02Bit34, Execute65X02And35, Execute65X02Rol36, Execute65C02Nop37, - Execute65X02Sec38, Execute65X02And39, Execute65C02Dea3A, Execute65C02Nop3B, - Execute65C02Bit3C, Execute65X02And3D, Execute65C02Rol3E, Execute65C02Nop3F, - Execute65X02Rti40, Execute65X02Eor41, Execute65C02Nop42, Execute65C02Nop43, - Execute65C02Nop44, Execute65X02Eor45, Execute65X02Lsr46, Execute65C02Nop47, - Execute65X02Pha48, Execute65X02Eor49, Execute65X02Lsr4A, Execute65C02Nop4B, - Execute65X02Jmp4C, Execute65X02Eor4D, Execute65X02Lsr4E, Execute65C02Nop4F, - Execute65X02Bvc50, Execute65X02Eor51, Execute65C02Eor52, Execute65C02Nop53, - Execute65C02Nop54, Execute65X02Eor55, Execute65X02Lsr56, Execute65C02Nop57, - Execute65X02Cli58, Execute65X02Eor59, Execute65C02Phy5A, Execute65C02Nop5B, - Execute65C02Nop5C, Execute65X02Eor5D, Execute65C02Lsr5E, Execute65C02Nop5F, - Execute65X02Rts60, Execute65C02Adc61, Execute65C02Nop62, Execute65C02Nop63, - Execute65C02Stz64, Execute65C02Adc65, Execute65X02Ror66, Execute65C02Nop67, - Execute65X02Pla68, Execute65C02Adc69, Execute65X02Ror6A, Execute65C02Nop6B, - Execute65C02Jmp6C, Execute65C02Adc6D, Execute65X02Ror6E, Execute65C02Nop6F, - Execute65X02Bvs70, Execute65C02Adc71, Execute65C02Adc72, Execute65C02Nop73, - Execute65C02Stz74, Execute65C02Adc75, Execute65X02Ror76, Execute65C02Nop77, - Execute65X02Sei78, Execute65C02Adc79, Execute65C02Ply7A, Execute65C02Nop7B, - Execute65C02Jmp7C, Execute65C02Adc7D, Execute65C02Ror7E, Execute65C02Nop7F, - Execute65C02Bra80, Execute65X02Sta81, Execute65C02Nop82, Execute65C02Nop83, - Execute65X02Sty84, Execute65X02Sta85, Execute65X02Stx86, Execute65C02Nop87, - Execute65X02Dey88, Execute65C02Bit89, Execute65X02Txa8A, Execute65C02Nop8B, - Execute65X02Sty8C, Execute65X02Sta8D, Execute65X02Stx8E, Execute65C02Nop8F, - Execute65X02Bcc90, Execute65X02Sta91, Execute65C02Sta92, Execute65C02Nop93, - Execute65X02Sty94, Execute65X02Sta95, Execute65X02Stx96, Execute65C02Nop97, - Execute65X02Tya98, Execute65X02Sta99, Execute65X02Txs9A, Execute65C02Nop9B, - Execute65C02Stz9C, Execute65X02Sta9D, Execute65C02Stz9E, Execute65C02Nop9F, - Execute65X02LdyA0, Execute65X02LdaA1, Execute65X02LdxA2, Execute65C02NopA3, - Execute65X02LdyA4, Execute65X02LdaA5, Execute65X02LdxA6, Execute65C02NopA7, - Execute65X02TayA8, Execute65X02LdaA9, Execute65X02TaxAA, Execute65C02NopAB, - Execute65X02LdyAC, Execute65X02LdaAD, Execute65X02LdxAE, Execute65C02NopAF, - Execute65X02BcsB0, Execute65X02LdaB1, Execute65C02LdaB2, Execute65C02NopB3, - Execute65X02LdyB4, Execute65X02LdaB5, Execute65X02LdxB6, Execute65C02NopB7, - Execute65X02ClvB8, Execute65X02LdaB9, Execute65X02TsxBA, Execute65C02NopBB, - Execute65X02LdyBC, Execute65X02LdaBD, Execute65X02LdxBE, Execute65C02NopBF, - Execute65X02CpyC0, Execute65X02CmpC1, Execute65C02NopC2, Execute65C02NopC3, - Execute65X02CpyC4, Execute65X02CmpC5, Execute65X02DecC6, Execute65C02NopC7, - Execute65X02InyC8, Execute65X02CmpC9, Execute65X02DexCA, Execute65C02NopCB, - Execute65X02CpyCC, Execute65X02CmpCD, Execute65X02DecCE, Execute65C02NopCF, - Execute65X02BneD0, Execute65X02CmpD1, Execute65C02CmpD2, Execute65C02NopD3, - Execute65C02NopD4, Execute65X02CmpD5, Execute65X02DecD6, Execute65C02NopD7, - Execute65X02CldD8, Execute65X02CmpD9, Execute65C02PhxDA, Execute65C02NopDB, - Execute65C02NopDC, Execute65X02CmpDD, Execute65C02DecDE, Execute65C02NopDF, - Execute65X02CpxE0, Execute65C02SbcE1, Execute65C02NopE2, Execute65C02NopE3, - Execute65X02CpxE4, Execute65C02SbcE5, Execute65X02IncE6, Execute65C02NopE7, - Execute65X02InxE8, Execute65C02SbcE9, Execute65X02NopEA, Execute65C02NopEB, - Execute65X02CpxEC, Execute65C02SbcED, Execute65X02IncEE, Execute65C02NopEF, - Execute65X02BeqF0, Execute65C02SbcF1, Execute65C02SbcF2, Execute65C02NopF3, - Execute65C02NopF4, Execute65C02SbcF5, Execute65X02IncF6, Execute65C02NopF7, - Execute65X02SedF8, Execute65C02SbcF9, Execute65C02PlxFA, Execute65C02NopFB, - Execute65C02NopFC, Execute65C02SbcFD, Execute65C02IncFE, Execute65C02NopFF - }; + _executeOpCode65C02 = new Action[] + { + Execute65X02Brk00, Execute65X02Ora01, Execute65C02Nop02, Execute65C02Nop03, + Execute65C02Tsb04, Execute65X02Ora05, Execute65X02Asl06, Execute65C02Nop07, + Execute65X02Php08, Execute65X02Ora09, Execute65X02Asl0A, Execute65C02Nop0B, + Execute65C02Tsb0C, Execute65X02Ora0D, Execute65X02Asl0E, Execute65C02Nop0F, + Execute65X02Bpl10, Execute65X02Ora11, Execute65C02Ora12, Execute65C02Nop13, + Execute65C02Trb14, Execute65X02Ora15, Execute65X02Asl16, Execute65C02Nop17, + Execute65X02Clc18, Execute65X02Ora19, Execute65C02Ina1A, Execute65C02Nop1B, + Execute65C02Trb1C, Execute65X02Ora1D, Execute65C02Asl1E, Execute65C02Nop1F, + Execute65X02Jsr20, Execute65X02And21, Execute65C02Nop22, Execute65C02Nop23, + Execute65X02Bit24, Execute65X02And25, Execute65X02Rol26, Execute65C02Nop27, + Execute65X02Plp28, Execute65X02And29, Execute65X02Rol2A, Execute65C02Nop2B, + Execute65X02Bit2C, Execute65X02And2D, Execute65X02Rol2E, Execute65C02Nop2F, + Execute65X02Bmi30, Execute65X02And31, Execute65C02And32, Execute65C02Nop33, + Execute65C02Bit34, Execute65X02And35, Execute65X02Rol36, Execute65C02Nop37, + Execute65X02Sec38, Execute65X02And39, Execute65C02Dea3A, Execute65C02Nop3B, + Execute65C02Bit3C, Execute65X02And3D, Execute65C02Rol3E, Execute65C02Nop3F, + Execute65X02Rti40, Execute65X02Eor41, Execute65C02Nop42, Execute65C02Nop43, + Execute65C02Nop44, Execute65X02Eor45, Execute65X02Lsr46, Execute65C02Nop47, + Execute65X02Pha48, Execute65X02Eor49, Execute65X02Lsr4A, Execute65C02Nop4B, + Execute65X02Jmp4C, Execute65X02Eor4D, Execute65X02Lsr4E, Execute65C02Nop4F, + Execute65X02Bvc50, Execute65X02Eor51, Execute65C02Eor52, Execute65C02Nop53, + Execute65C02Nop54, Execute65X02Eor55, Execute65X02Lsr56, Execute65C02Nop57, + Execute65X02Cli58, Execute65X02Eor59, Execute65C02Phy5A, Execute65C02Nop5B, + Execute65C02Nop5C, Execute65X02Eor5D, Execute65C02Lsr5E, Execute65C02Nop5F, + Execute65X02Rts60, Execute65C02Adc61, Execute65C02Nop62, Execute65C02Nop63, + Execute65C02Stz64, Execute65C02Adc65, Execute65X02Ror66, Execute65C02Nop67, + Execute65X02Pla68, Execute65C02Adc69, Execute65X02Ror6A, Execute65C02Nop6B, + Execute65C02Jmp6C, Execute65C02Adc6D, Execute65X02Ror6E, Execute65C02Nop6F, + Execute65X02Bvs70, Execute65C02Adc71, Execute65C02Adc72, Execute65C02Nop73, + Execute65C02Stz74, Execute65C02Adc75, Execute65X02Ror76, Execute65C02Nop77, + Execute65X02Sei78, Execute65C02Adc79, Execute65C02Ply7A, Execute65C02Nop7B, + Execute65C02Jmp7C, Execute65C02Adc7D, Execute65C02Ror7E, Execute65C02Nop7F, + Execute65C02Bra80, Execute65X02Sta81, Execute65C02Nop82, Execute65C02Nop83, + Execute65X02Sty84, Execute65X02Sta85, Execute65X02Stx86, Execute65C02Nop87, + Execute65X02Dey88, Execute65C02Bit89, Execute65X02Txa8A, Execute65C02Nop8B, + Execute65X02Sty8C, Execute65X02Sta8D, Execute65X02Stx8E, Execute65C02Nop8F, + Execute65X02Bcc90, Execute65X02Sta91, Execute65C02Sta92, Execute65C02Nop93, + Execute65X02Sty94, Execute65X02Sta95, Execute65X02Stx96, Execute65C02Nop97, + Execute65X02Tya98, Execute65X02Sta99, Execute65X02Txs9A, Execute65C02Nop9B, + Execute65C02Stz9C, Execute65X02Sta9D, Execute65C02Stz9E, Execute65C02Nop9F, + Execute65X02LdyA0, Execute65X02LdaA1, Execute65X02LdxA2, Execute65C02NopA3, + Execute65X02LdyA4, Execute65X02LdaA5, Execute65X02LdxA6, Execute65C02NopA7, + Execute65X02TayA8, Execute65X02LdaA9, Execute65X02TaxAA, Execute65C02NopAB, + Execute65X02LdyAC, Execute65X02LdaAD, Execute65X02LdxAE, Execute65C02NopAF, + Execute65X02BcsB0, Execute65X02LdaB1, Execute65C02LdaB2, Execute65C02NopB3, + Execute65X02LdyB4, Execute65X02LdaB5, Execute65X02LdxB6, Execute65C02NopB7, + Execute65X02ClvB8, Execute65X02LdaB9, Execute65X02TsxBA, Execute65C02NopBB, + Execute65X02LdyBC, Execute65X02LdaBD, Execute65X02LdxBE, Execute65C02NopBF, + Execute65X02CpyC0, Execute65X02CmpC1, Execute65C02NopC2, Execute65C02NopC3, + Execute65X02CpyC4, Execute65X02CmpC5, Execute65X02DecC6, Execute65C02NopC7, + Execute65X02InyC8, Execute65X02CmpC9, Execute65X02DexCA, Execute65C02NopCB, + Execute65X02CpyCC, Execute65X02CmpCD, Execute65X02DecCE, Execute65C02NopCF, + Execute65X02BneD0, Execute65X02CmpD1, Execute65C02CmpD2, Execute65C02NopD3, + Execute65C02NopD4, Execute65X02CmpD5, Execute65X02DecD6, Execute65C02NopD7, + Execute65X02CldD8, Execute65X02CmpD9, Execute65C02PhxDA, Execute65C02NopDB, + Execute65C02NopDC, Execute65X02CmpDD, Execute65C02DecDE, Execute65C02NopDF, + Execute65X02CpxE0, Execute65C02SbcE1, Execute65C02NopE2, Execute65C02NopE3, + Execute65X02CpxE4, Execute65C02SbcE5, Execute65X02IncE6, Execute65C02NopE7, + Execute65X02InxE8, Execute65C02SbcE9, Execute65X02NopEA, Execute65C02NopEB, + Execute65X02CpxEC, Execute65C02SbcED, Execute65X02IncEE, Execute65C02NopEF, + Execute65X02BeqF0, Execute65C02SbcF1, Execute65C02SbcF2, Execute65C02NopF3, + Execute65C02NopF4, Execute65C02SbcF5, Execute65X02IncF6, Execute65C02NopF7, + Execute65X02SedF8, Execute65C02SbcF9, Execute65C02PlxFA, Execute65C02NopFB, + Execute65C02NopFC, Execute65C02SbcFD, Execute65C02IncFE, Execute65C02NopFF + }; } - public override void Initialize() - { - _memory = Machine.Memory; - - Is65C02 = true; - IsThrottled = false; - Multiplier = 1; - - RS = 0xFF; - } - - public override void Reset() + public void Reset() { RS = (RS - 3) & 0xFF; // [4-14] RPC = _memory.ReadRomRegionE0FF(0xFFFC) | (_memory.ReadRomRegionE0FF(0xFFFD) << 8); - RP |= (PB | PI); + RP |= (Pb | Pi); if (Is65C02) // [C-10] { - RP &= ~PD; + RP &= ~Pd; } } @@ -186,12 +193,12 @@ namespace Jellyfish.Virtu RA, RX, RY, RP, RS, RPC, EA, CC); } - public string[] TraceState() + private string[] TraceState() { string[] parts = new string[2]; - parts[0] = string.Format("{0:X4} {1:X2} {2} ", RPC, _memory.Read(RPC), ReadOpcode(RPC)); + parts[0] = $"{RPC:X4} {_memory.Read(RPC):X2} {ReadOpcode(RPC)} "; parts[1] = string.Format( - "A:{0:X2} X:{1:X2} Y:{2:X2} P:{3:X2} SP:{4:X2} Cy:{5}", + "A:{0:X2} X:{1:X2} Y:{2:X2} P:{3:X2} SP:{4:X2} {6}{7}{8}{9}{10}{11}{12}{13} Cy:{5}", RA, RX, RY, @@ -213,164 +220,164 @@ namespace Jellyfish.Virtu private string ReadOpcode(int pc) { //It would be so much better if I could just use the MOS6502X's Disassemble Method here. - - if (pc <= 0xFFFD) //sanity check to make sure we don't read from outside address space. + if (pc <= 0xFFFD) //sanity check to make sure we don't read from outside address space. { switch (_memory.Peek(pc)) { - case 0x0C: return string.Format("NOP (${0:X4})", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x0D: return string.Format("ORA ${0:X4}", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x0E: return string.Format("ASL ${0:X4}", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x19: return string.Format("ORA ${0:X4},Y *", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x1D: return string.Format("ORA ${0:X4},X *", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x1E: return string.Format("ASL ${0:X4},X", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x20: return string.Format("JSR ${0:X4}", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x2C: return string.Format("BIT ${0:X4}", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x2D: return string.Format("AND ${0:X4}", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x2E: return string.Format("ROL ${0:X4}", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x39: return string.Format("AND ${0:X4},Y *", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x3D: return string.Format("AND ${0:X4},X *", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x3E: return string.Format("ROL ${0:X4},X", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x4C: return string.Format("JMP ${0:X4}", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x4D: return string.Format("EOR ${0:X4}", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x4E: return string.Format("LSR ${0:X4}", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x59: return string.Format("EOR ${0:X4},Y *", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x5D: return string.Format("EOR ${0:X4},X *", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x5E: return string.Format("LSR ${0:X4},X", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x6C: return string.Format("JMP (${0:X4})", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x6D: return string.Format("ADC ${0:X4}", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x6E: return string.Format("ROR ${0:X4}", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x79: return string.Format("ADC ${0:X4},Y *", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x7D: return string.Format("ADC ${0:X4},X *", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x7E: return string.Format("ROR ${0:X4},X", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x8C: return string.Format("STY ${0:X4}", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x8D: return string.Format("STA ${0:X4}", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x8E: return string.Format("STX ${0:X4}", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x99: return string.Format("STA ${0:X4},Y", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0x9D: return string.Format("STA ${0:X4},X", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0xAC: return string.Format("LDY ${0:X4}", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0xAD: return string.Format("LDA ${0:X4}", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0xAE: return string.Format("LDX ${0:X4}", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0xB9: return string.Format("LDA ${0:X4},Y *", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0xBC: return string.Format("LDY ${0:X4},X *", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0xBD: return string.Format("LDA ${0:X4},X *", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0xBE: return string.Format("LDX ${0:X4},Y *", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0xCC: return string.Format("CPY ${0:X4}", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0xCD: return string.Format("CMP ${0:X4}", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0xCE: return string.Format("DEC ${0:X4}", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0xD9: return string.Format("CMP ${0:X4},Y *", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0xDD: return string.Format("CMP ${0:X4},X *", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0xDE: return string.Format("DEC ${0:X4},X", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0xEC: return string.Format("CPX ${0:X4}", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0xED: return string.Format("SBC ${0:X4}", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0xEE: return string.Format("INC ${0:X4}", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0xF9: return string.Format("SBC ${0:X4},Y *", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0xFD: return string.Format("SBC ${0:X4},X *", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - case 0xFE: return string.Format("INC ${0:X4},X", _memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8); - + case 0x0C: return $"NOP (${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4})"; + case 0x0D: return $"ORA ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4}"; + case 0x0E: return $"ASL ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4}"; + case 0x19: return $"ORA ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4},Y *"; + case 0x1D: return $"ORA ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4},X *"; + case 0x1E: return $"ASL ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4},X"; + case 0x20: return $"JSR ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4}"; + case 0x2C: return $"BIT ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4}"; + case 0x2D: return $"AND ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4}"; + case 0x2E: return $"ROL ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4}"; + case 0x39: return $"AND ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4},Y *"; + case 0x3D: return $"AND ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4},X *"; + case 0x3E: return $"ROL ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4},X"; + case 0x4C: return $"JMP ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4}"; + case 0x4D: return $"EOR ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4}"; + case 0x4E: return $"LSR ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4}"; + case 0x59: return $"EOR ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4},Y *"; + case 0x5D: return $"EOR ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4},X *"; + case 0x5E: return $"LSR ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4},X"; + case 0x6C: return $"JMP (${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4})"; + case 0x6D: return $"ADC ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4}"; + case 0x6E: return $"ROR ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4}"; + case 0x79: return $"ADC ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4},Y *"; + case 0x7D: return $"ADC ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4},X *"; + case 0x7E: return $"ROR ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4},X"; + case 0x8C: return $"STY ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4}"; + case 0x8D: return $"STA ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4}"; + case 0x8E: return $"STX ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4}"; + case 0x99: return $"STA ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4},Y"; + case 0x9D: return $"STA ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4},X"; + case 0xAC: return $"LDY ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4}"; + case 0xAD: return $"LDA ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4}"; + case 0xAE: return $"LDX ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4}"; + case 0xB9: return $"LDA ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4},Y *"; + case 0xBC: return $"LDY ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4},X *"; + case 0xBD: return $"LDA ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4},X *"; + case 0xBE: return $"LDX ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4},Y *"; + case 0xCC: return $"CPY ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4}"; + case 0xCD: return $"CMP ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4}"; + case 0xCE: return $"DEC ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4}"; + case 0xD9: return $"CMP ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4},Y *"; + case 0xDD: return $"CMP ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4},X *"; + case 0xDE: return $"DEC ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4},X"; + case 0xEC: return $"CPX ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4}"; + case 0xED: return $"SBC ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4}"; + case 0xEE: return $"INC ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4}"; + case 0xF9: return $"SBC ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4},Y *"; + case 0xFD: return $"SBC ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4},X *"; + case 0xFE: return $"INC ${_memory.Peek(pc + 1) | _memory.Peek(pc + 2) << 8:X4},X"; } } - if (pc <= 0xFFFE) //read two-byte opcodes here + + if (pc <= 0xFFFE) //read two-byte opcodes here { switch (_memory.Peek(pc)) { - case 0x01: return string.Format("ORA (${0:X2},X)", _memory.Peek(++pc)); - case 0x04: return string.Format("NOP ${0:X2}", _memory.Peek(++pc)); - case 0x05: return string.Format("ORA ${0:X2}", _memory.Peek(++pc)); - case 0x06: return string.Format("ASL ${0:X2}", _memory.Peek(++pc)); - case 0x09: return string.Format("ORA #${0:X2}", _memory.Peek(++pc)); - case 0x10: return string.Format("BPL ${0:X4}", pc + 2 + (sbyte)_memory.Peek(pc + 1)); - case 0x11: return string.Format("ORA (${0:X2}),Y *", _memory.Peek(++pc)); - case 0x14: return string.Format("NOP ${0:X2},X", _memory.Peek(++pc)); - case 0x15: return string.Format("ORA ${0:X2},X", _memory.Peek(++pc)); - case 0x16: return string.Format("ASL ${0:X2},X", _memory.Peek(++pc)); - case 0x1C: return string.Format("NOP (${0:X2},X)", _memory.Peek(++pc)); - case 0x21: return string.Format("AND (${0:X2},X)", _memory.Peek(++pc)); - case 0x24: return string.Format("BIT ${0:X2}", _memory.Peek(++pc)); - case 0x25: return string.Format("AND ${0:X2}", _memory.Peek(++pc)); - case 0x26: return string.Format("ROL ${0:X2}", _memory.Peek(++pc)); - case 0x29: return string.Format("AND #${0:X2}", _memory.Peek(++pc)); - case 0x30: return string.Format("BMI ${0:X4}", pc + 2 + (sbyte)_memory.Peek(pc + 1)); - case 0x31: return string.Format("AND (${0:X2}),Y *", _memory.Peek(++pc)); - case 0x34: return string.Format("NOP ${0:X2},X", _memory.Peek(++pc)); - case 0x35: return string.Format("AND ${0:X2},X", _memory.Peek(++pc)); - case 0x36: return string.Format("ROL ${0:X2},X", _memory.Peek(++pc)); - case 0x3C: return string.Format("NOP (${0:X2},X)", _memory.Peek(++pc)); - case 0x41: return string.Format("EOR (${0:X2},X)", _memory.Peek(++pc)); - case 0x44: return string.Format("NOP ${0:X2}", _memory.Peek(++pc)); - case 0x45: return string.Format("EOR ${0:X2}", _memory.Peek(++pc)); - case 0x46: return string.Format("LSR ${0:X2}", _memory.Peek(++pc)); - case 0x49: return string.Format("EOR #${0:X2}", _memory.Peek(++pc)); - case 0x50: return string.Format("BVC ${0:X4}", pc + 2 + (sbyte)_memory.Peek(pc + 1)); - case 0x51: return string.Format("EOR (${0:X2}),Y *", _memory.Peek(++pc)); - case 0x54: return string.Format("NOP ${0:X2},X", _memory.Peek(++pc)); - case 0x55: return string.Format("EOR ${0:X2},X", _memory.Peek(++pc)); - case 0x56: return string.Format("LSR ${0:X2},X", _memory.Peek(++pc)); - case 0x5C: return string.Format("NOP (${0:X2},X)", _memory.Peek(++pc)); - case 0x61: return string.Format("ADC (${0:X2},X)", _memory.Peek(++pc)); - case 0x64: return string.Format("NOP ${0:X2}", _memory.Peek(++pc)); - case 0x65: return string.Format("ADC ${0:X2}", _memory.Peek(++pc)); - case 0x66: return string.Format("ROR ${0:X2}", _memory.Peek(++pc)); - case 0x69: return string.Format("ADC #${0:X2}", _memory.Peek(++pc)); - case 0x70: return string.Format("BVS ${0:X4}", pc + 2 + (sbyte)_memory.Peek(pc + 1)); - case 0x71: return string.Format("ADC (${0:X2}),Y *", _memory.Peek(++pc)); - case 0x74: return string.Format("NOP ${0:X2},X", _memory.Peek(++pc)); - case 0x75: return string.Format("ADC ${0:X2},X", _memory.Peek(++pc)); - case 0x76: return string.Format("ROR ${0:X2},X", _memory.Peek(++pc)); - case 0x7C: return string.Format("NOP (${0:X2},X)", _memory.Peek(++pc)); - case 0x80: return string.Format("NOP #${0:X2}", _memory.Peek(++pc)); - case 0x81: return string.Format("STA (${0:X2},X)", _memory.Peek(++pc)); - case 0x82: return string.Format("NOP #${0:X2}", _memory.Peek(++pc)); - case 0x84: return string.Format("STY ${0:X2}", _memory.Peek(++pc)); - case 0x85: return string.Format("STA ${0:X2}", _memory.Peek(++pc)); - case 0x86: return string.Format("STX ${0:X2}", _memory.Peek(++pc)); - case 0x89: return string.Format("NOP #${0:X2}", _memory.Peek(++pc)); - case 0x90: return string.Format("BCC ${0:X4}", pc + 2 + (sbyte)_memory.Peek(pc + 1)); - case 0x91: return string.Format("STA (${0:X2}),Y", _memory.Peek(++pc)); - case 0x94: return string.Format("STY ${0:X2},X", _memory.Peek(++pc)); - case 0x95: return string.Format("STA ${0:X2},X", _memory.Peek(++pc)); - case 0x96: return string.Format("STX ${0:X2},Y", _memory.Peek(++pc)); - case 0xA0: return string.Format("LDY #${0:X2}", _memory.Peek(++pc)); - case 0xA1: return string.Format("LDA (${0:X2},X)", _memory.Peek(++pc)); - case 0xA2: return string.Format("LDX #${0:X2}", _memory.Peek(++pc)); - case 0xA4: return string.Format("LDY ${0:X2}", _memory.Peek(++pc)); - case 0xA5: return string.Format("LDA ${0:X2}", _memory.Peek(++pc)); - case 0xA6: return string.Format("LDX ${0:X2}", _memory.Peek(++pc)); - case 0xA9: return string.Format("LDA #${0:X2}", _memory.Peek(++pc)); - case 0xB0: return string.Format("BCS ${0:X4}", pc + 2 + (sbyte)_memory.Peek(pc + 1)); - case 0xB1: return string.Format("LDA (${0:X2}),Y *", _memory.Peek(++pc)); - case 0xB4: return string.Format("LDY ${0:X2},X", _memory.Peek(++pc)); - case 0xB5: return string.Format("LDA ${0:X2},X", _memory.Peek(++pc)); - case 0xB6: return string.Format("LDX ${0:X2},Y", _memory.Peek(++pc)); - case 0xC0: return string.Format("CPY #${0:X2}", _memory.Peek(++pc)); - case 0xC1: return string.Format("CMP (${0:X2},X)", _memory.Peek(++pc)); - case 0xC2: return string.Format("NOP #${0:X2}", _memory.Peek(++pc)); - case 0xC4: return string.Format("CPY ${0:X2}", _memory.Peek(++pc)); - case 0xC5: return string.Format("CMP ${0:X2}", _memory.Peek(++pc)); - case 0xC6: return string.Format("DEC ${0:X2}", _memory.Peek(++pc)); - case 0xC9: return string.Format("CMP #${0:X2}", _memory.Peek(++pc)); - case 0xD0: return string.Format("BNE ${0:X4}", pc + 2 + (sbyte)_memory.Peek(pc + 1)); - case 0xD1: return string.Format("CMP (${0:X2}),Y *", _memory.Peek(++pc)); - case 0xD4: return string.Format("NOP ${0:X2},X", _memory.Peek(++pc)); - case 0xD5: return string.Format("CMP ${0:X2},X", _memory.Peek(++pc)); - case 0xD6: return string.Format("DEC ${0:X2},X", _memory.Peek(++pc)); - case 0xDC: return string.Format("NOP (${0:X2},X)", _memory.Peek(++pc)); - case 0xE0: return string.Format("CPX #${0:X2}", _memory.Peek(++pc)); - case 0xE1: return string.Format("SBC (${0:X2},X)", _memory.Peek(++pc)); - case 0xE2: return string.Format("NOP #${0:X2}", _memory.Peek(++pc)); - case 0xE4: return string.Format("CPX ${0:X2}", _memory.Peek(++pc)); - case 0xE5: return string.Format("SBC ${0:X2}", _memory.Peek(++pc)); - case 0xE6: return string.Format("INC ${0:X2}", _memory.Peek(++pc)); - case 0xE9: return string.Format("SBC #${0:X2}", _memory.Peek(++pc)); - case 0xF0: return string.Format("BEQ ${0:X4}", pc + 2 + (sbyte)_memory.Peek(pc + 1)); - case 0xF1: return string.Format("SBC (${0:X2}),Y *", _memory.Peek(++pc)); - case 0xF4: return string.Format("NOP ${0:X2},X", _memory.Peek(++pc)); - case 0xF5: return string.Format("SBC ${0:X2},X", _memory.Peek(++pc)); - case 0xF6: return string.Format("INC ${0:X2},X", _memory.Peek(++pc)); - case 0xFC: return string.Format("NOP (${0:X2},X)", _memory.Peek(++pc)); + case 0x01: return $"ORA (${_memory.Peek(++pc):X2},X)"; + case 0x04: return $"NOP ${_memory.Peek(++pc):X2}"; + case 0x05: return $"ORA ${_memory.Peek(++pc):X2}"; + case 0x06: return $"ASL ${_memory.Peek(++pc):X2}"; + case 0x09: return $"ORA #${_memory.Peek(++pc):X2}"; + case 0x10: return $"BPL ${pc + 2 + (sbyte) _memory.Peek(pc + 1):X4}"; + case 0x11: return $"ORA (${_memory.Peek(++pc):X2}),Y *"; + case 0x14: return $"NOP ${_memory.Peek(++pc):X2},X"; + case 0x15: return $"ORA ${_memory.Peek(++pc):X2},X"; + case 0x16: return $"ASL ${_memory.Peek(++pc):X2},X"; + case 0x1C: return $"NOP (${_memory.Peek(++pc):X2},X)"; + case 0x21: return $"AND (${_memory.Peek(++pc):X2},X)"; + case 0x24: return $"BIT ${_memory.Peek(++pc):X2}"; + case 0x25: return $"AND ${_memory.Peek(++pc):X2}"; + case 0x26: return $"ROL ${_memory.Peek(++pc):X2}"; + case 0x29: return $"AND #${_memory.Peek(++pc):X2}"; + case 0x30: return $"BMI ${pc + 2 + (sbyte) _memory.Peek(pc + 1):X4}"; + case 0x31: return $"AND (${_memory.Peek(++pc):X2}),Y *"; + case 0x34: return $"NOP ${_memory.Peek(++pc):X2},X"; + case 0x35: return $"AND ${_memory.Peek(++pc):X2},X"; + case 0x36: return $"ROL ${_memory.Peek(++pc):X2},X"; + case 0x3C: return $"NOP (${_memory.Peek(++pc):X2},X)"; + case 0x41: return $"EOR (${_memory.Peek(++pc):X2},X)"; + case 0x44: return $"NOP ${_memory.Peek(++pc):X2}"; + case 0x45: return $"EOR ${_memory.Peek(++pc):X2}"; + case 0x46: return $"LSR ${_memory.Peek(++pc):X2}"; + case 0x49: return $"EOR #${_memory.Peek(++pc):X2}"; + case 0x50: return $"BVC ${pc + 2 + (sbyte) _memory.Peek(pc + 1):X4}"; + case 0x51: return $"EOR (${_memory.Peek(++pc):X2}),Y *"; + case 0x54: return $"NOP ${_memory.Peek(++pc):X2},X"; + case 0x55: return $"EOR ${_memory.Peek(++pc):X2},X"; + case 0x56: return $"LSR ${_memory.Peek(++pc):X2},X"; + case 0x5C: return $"NOP (${_memory.Peek(++pc):X2},X)"; + case 0x61: return $"ADC (${_memory.Peek(++pc):X2},X)"; + case 0x64: return $"NOP ${_memory.Peek(++pc):X2}"; + case 0x65: return $"ADC ${_memory.Peek(++pc):X2}"; + case 0x66: return $"ROR ${_memory.Peek(++pc):X2}"; + case 0x69: return $"ADC #${_memory.Peek(++pc):X2}"; + case 0x70: return $"BVS ${pc + 2 + (sbyte) _memory.Peek(pc + 1):X4}"; + case 0x71: return $"ADC (${_memory.Peek(++pc):X2}),Y *"; + case 0x74: return $"NOP ${_memory.Peek(++pc):X2},X"; + case 0x75: return $"ADC ${_memory.Peek(++pc):X2},X"; + case 0x76: return $"ROR ${_memory.Peek(++pc):X2},X"; + case 0x7C: return $"NOP (${_memory.Peek(++pc):X2},X)"; + case 0x80: return $"NOP #${_memory.Peek(++pc):X2}"; + case 0x81: return $"STA (${_memory.Peek(++pc):X2},X)"; + case 0x82: return $"NOP #${_memory.Peek(++pc):X2}"; + case 0x84: return $"STY ${_memory.Peek(++pc):X2}"; + case 0x85: return $"STA ${_memory.Peek(++pc):X2}"; + case 0x86: return $"STX ${_memory.Peek(++pc):X2}"; + case 0x89: return $"NOP #${_memory.Peek(++pc):X2}"; + case 0x90: return $"BCC ${pc + 2 + (sbyte) _memory.Peek(pc + 1):X4}"; + case 0x91: return $"STA (${_memory.Peek(++pc):X2}),Y"; + case 0x94: return $"STY ${_memory.Peek(++pc):X2},X"; + case 0x95: return $"STA ${_memory.Peek(++pc):X2},X"; + case 0x96: return $"STX ${_memory.Peek(++pc):X2},Y"; + case 0xA0: return $"LDY #${_memory.Peek(++pc):X2}"; + case 0xA1: return $"LDA (${_memory.Peek(++pc):X2},X)"; + case 0xA2: return $"LDX #${_memory.Peek(++pc):X2}"; + case 0xA4: return $"LDY ${_memory.Peek(++pc):X2}"; + case 0xA5: return $"LDA ${_memory.Peek(++pc):X2}"; + case 0xA6: return $"LDX ${_memory.Peek(++pc):X2}"; + case 0xA9: return $"LDA #${_memory.Peek(++pc):X2}"; + case 0xB0: return $"BCS ${pc + 2 + (sbyte) _memory.Peek(pc + 1):X4}"; + case 0xB1: return $"LDA (${_memory.Peek(++pc):X2}),Y *"; + case 0xB4: return $"LDY ${_memory.Peek(++pc):X2},X"; + case 0xB5: return $"LDA ${_memory.Peek(++pc):X2},X"; + case 0xB6: return $"LDX ${_memory.Peek(++pc):X2},Y"; + case 0xC0: return $"CPY #${_memory.Peek(++pc):X2}"; + case 0xC1: return $"CMP (${_memory.Peek(++pc):X2},X)"; + case 0xC2: return $"NOP #${_memory.Peek(++pc):X2}"; + case 0xC4: return $"CPY ${_memory.Peek(++pc):X2}"; + case 0xC5: return $"CMP ${_memory.Peek(++pc):X2}"; + case 0xC6: return $"DEC ${_memory.Peek(++pc):X2}"; + case 0xC9: return $"CMP #${_memory.Peek(++pc):X2}"; + case 0xD0: return $"BNE ${pc + 2 + (sbyte) _memory.Peek(pc + 1):X4}"; + case 0xD1: return $"CMP (${_memory.Peek(++pc):X2}),Y *"; + case 0xD4: return $"NOP ${_memory.Peek(++pc):X2},X"; + case 0xD5: return $"CMP ${_memory.Peek(++pc):X2},X"; + case 0xD6: return $"DEC ${_memory.Peek(++pc):X2},X"; + case 0xDC: return $"NOP (${_memory.Peek(++pc):X2},X)"; + case 0xE0: return $"CPX #${_memory.Peek(++pc):X2}"; + case 0xE1: return $"SBC (${_memory.Peek(++pc):X2},X)"; + case 0xE2: return $"NOP #${_memory.Peek(++pc):X2}"; + case 0xE4: return $"CPX ${_memory.Peek(++pc):X2}"; + case 0xE5: return $"SBC ${_memory.Peek(++pc):X2}"; + case 0xE6: return $"INC ${_memory.Peek(++pc):X2}"; + case 0xE9: return $"SBC #${_memory.Peek(++pc):X2}"; + case 0xF0: return $"BEQ ${pc + 2 + (sbyte) _memory.Peek(pc + 1):X4}"; + case 0xF1: return $"SBC (${_memory.Peek(++pc):X2}),Y *"; + case 0xF4: return $"NOP ${_memory.Peek(++pc):X2},X"; + case 0xF5: return $"SBC ${_memory.Peek(++pc):X2},X"; + case 0xF6: return $"INC ${_memory.Peek(++pc):X2},X"; + case 0xFC: return $"NOP (${_memory.Peek(++pc):X2},X)"; } } - if (pc <= 0xFFFF) //read one-byte opcodes here + + if (pc <= 0xFFFF) // read one-byte opcodes here { switch (_memory.Peek(pc)) { @@ -411,27 +418,24 @@ namespace Jellyfish.Virtu case 0xFA: return "NOP"; } } + return "---"; } public int Execute() { - if (TraceCallback != null) - { - TraceCallback(TraceState()); - } - + TraceCallback?.Invoke(TraceState()); CC = 0; OpCode = _memory.ReadOpcode(RPC); RPC = (RPC + 1) & 0xFFFF; _executeOpCode[OpCode](); Cycles += CC; - return CC; } #region Core Operand Actions + private void GetAddressAbs() // abs { EA = _memory.Read(RPC) | (_memory.Read(RPC + 1) << 8); @@ -517,7 +521,6 @@ namespace Jellyfish.Virtu private int Pull() { RS = (RS + 1) & 0xFF; - return _memory.ReadZeroPage(0x0100 + RS); } @@ -546,7 +549,6 @@ namespace Jellyfish.Virtu { int data = _memory.Read(RPC); RPC = (RPC + 1) & 0xFFFF; - return data; } @@ -624,34 +626,37 @@ namespace Jellyfish.Virtu { _memory.WriteZeroPage(EA, data); } + #endregion #region Core OpCode Actions private void ExecuteAdc65N02(int data, int cc) { - if ((RP & PD) == 0x0) + if ((RP & Pd) == 0x0) { - int ra = RA + data + (RP & PC); - RP = RP & ~(PC | PN | PV | PZ) | ((ra >> 8) & PC) | DataPNZ[ra & 0xFF] | (((~(RA ^ data) & (RA ^ (ra & 0xFF))) >> 1) & PV); + int ra = RA + data + (RP & Pc); + RP = RP & ~(Pc | Pn | Pv | Pz) | ((ra >> 8) & Pc) | DataPnz[ra & 0xFF] | (((~(RA ^ data) & (RA ^ (ra & 0xFF))) >> 1) & Pv); RA = ra & 0xFF; CC += cc; } else // decimal { - int ral = (RA & 0x0F) + (data & 0x0F) + (RP & PC); + int ral = (RA & 0x0F) + (data & 0x0F) + (RP & Pc); int rah = (RA >> 4) + (data >> 4); if (ral >= 10) { ral -= 10; rah++; } + int ra = (ral | (rah << 4)) & 0xFF; - RP = RP & ~(PC | PN | PV | PZ) | DataPN[ra] | (((~(RA ^ data) & (RA ^ ra)) >> 1) & PV) | DataPZ[(RA + data + (RP & PC)) & 0xFF]; + RP = RP & ~(Pc | Pn | Pv | Pz) | DataPn[ra] | (((~(RA ^ data) & (RA ^ ra)) >> 1) & Pv) | DataPz[(RA + data + (RP & Pc)) & 0xFF]; if (rah >= 10) { rah -= 10; - RP |= PC; + RP |= Pc; } + RA = (ral | (rah << 4)) & 0xFF; CC += cc; } @@ -659,30 +664,32 @@ namespace Jellyfish.Virtu private void ExecuteAdc65C02(int data, int cc) { - if ((RP & PD) == 0x0) + if ((RP & Pd) == 0x0) { - int ra = RA + data + (RP & PC); - RP = RP & ~(PC | PN | PV | PZ) | ((ra >> 8) & PC) | DataPNZ[ra & 0xFF] | (((~(RA ^ data) & (RA ^ (ra & 0xFF))) >> 1) & PV); + int ra = RA + data + (RP & Pc); + RP = RP & ~(Pc | Pn | Pv | Pz) | ((ra >> 8) & Pc) | DataPnz[ra & 0xFF] | (((~(RA ^ data) & (RA ^ (ra & 0xFF))) >> 1) & Pv); RA = ra & 0xFF; CC += cc; } else // decimal { - int ral = (RA & 0x0F) + (data & 0x0F) + (RP & PC); + int ral = (RA & 0x0F) + (data & 0x0F) + (RP & Pc); int rah = (RA >> 4) + (data >> 4); if (ral >= 10) { ral -= 10; rah++; } - RP &= ~PC; + + RP &= ~Pc; if (rah >= 10) { rah -= 10; - RP |= PC; + RP |= Pc; } + int ra = (ral | (rah << 4)) & 0xFF; - RP = RP & ~(PN | PV | PZ) | DataPNZ[ra] | (((~(RA ^ data) & (RA ^ ra)) >> 1) & PV); + RP = RP & ~(Pn | Pv | Pz) | DataPnz[ra] | (((~(RA ^ data) & (RA ^ ra)) >> 1) & Pv); RA = ra; CC += cc + 1; } @@ -691,15 +698,15 @@ namespace Jellyfish.Virtu private void ExecuteAnd(int data, int cc) { RA &= data; - RP = RP & ~(PN | PZ) | DataPNZ[RA]; + RP = RP & ~(Pn | Pz) | DataPnz[RA]; CC += cc; } private int ExecuteAsl(int data, int cc) { - RP = RP & ~PC | ((data >> 7) & PC); + RP = RP & ~Pc | ((data >> 7) & Pc); data = (data << 1) & 0xFF; - RP = RP & ~(PN | PZ) | DataPNZ[data]; + RP = RP & ~(Pn | Pz) | DataPnz[data]; CC += cc; return data; @@ -707,15 +714,15 @@ namespace Jellyfish.Virtu private void ExecuteAslImp(int cc) { - RP = RP & ~PC | ((RA >> 7) & PC); + RP = RP & ~Pc | ((RA >> 7) & Pc); RA = (RA << 1) & 0xFF; - RP = RP & ~(PN | PZ) | DataPNZ[RA]; + RP = RP & ~(Pn | Pz) | DataPnz[RA]; CC += cc; } private void ExecuteBcc(int cc) { - if ((RP & PC) == 0x0) + if ((RP & Pc) == 0x0) { int rpc = (RPC + 1) & 0xFFFF; RPC = (RPC + 1 + (sbyte)_memory.Read(RPC)) & 0xFFFF; @@ -730,7 +737,7 @@ namespace Jellyfish.Virtu private void ExecuteBcs(int cc) { - if ((RP & PC) != 0x0) + if ((RP & Pc) != 0x0) { int rpc = (RPC + 1) & 0xFFFF; RPC = (RPC + 1 + (sbyte)_memory.Read(RPC)) & 0xFFFF; @@ -745,7 +752,7 @@ namespace Jellyfish.Virtu private void ExecuteBeq(int cc) { - if ((RP & PZ) != 0x0) + if ((RP & Pz) != 0x0) { int rpc = (RPC + 1) & 0xFFFF; RPC = (RPC + 1 + (sbyte)_memory.Read(RPC)) & 0xFFFF; @@ -760,19 +767,19 @@ namespace Jellyfish.Virtu private void ExecuteBit(int data, int cc) { - RP = RP & ~(PN | PV | PZ) | (data & (PN | PV)) | DataPZ[RA & data]; + RP = RP & ~(Pn | Pv | Pz) | (data & (Pn | Pv)) | DataPz[RA & data]; CC += cc; } private void ExecuteBitImm(int data, int cc) { - RP = RP & ~PZ | DataPZ[RA & data]; + RP = RP & ~Pz | DataPz[RA & data]; CC += cc; } private void ExecuteBmi(int cc) { - if ((RP & PN) != 0x0) + if ((RP & Pn) != 0x0) { int rpc = (RPC + 1) & 0xFFFF; RPC = (RPC + 1 + (sbyte)_memory.Read(RPC)) & 0xFFFF; @@ -787,7 +794,7 @@ namespace Jellyfish.Virtu private void ExecuteBne(int cc) { - if ((RP & PZ) == 0x0) + if ((RP & Pz) == 0x0) { int rpc = (RPC + 1) & 0xFFFF; RPC = (RPC + 1 + (sbyte)_memory.Read(RPC)) & 0xFFFF; @@ -802,7 +809,7 @@ namespace Jellyfish.Virtu private void ExecuteBpl(int cc) { - if ((RP & PN) == 0x0) + if ((RP & Pn) == 0x0) { int rpc = (RPC + 1) & 0xFFFF; RPC = (RPC + 1 + (sbyte)_memory.Read(RPC)) & 0xFFFF; @@ -827,15 +834,15 @@ namespace Jellyfish.Virtu int rpc = (RPC + 1) & 0xFFFF; // [4-18] Push(rpc >> 8); Push(rpc & 0xFF); - Push(RP | PB); - RP |= PI; + Push(RP | Pb); + RP |= Pi; RPC = _memory.Read(0xFFFE) | (_memory.Read(0xFFFF) << 8); CC += cc; } private void ExecuteBvc(int cc) { - if ((RP & PV) == 0x0) + if ((RP & Pv) == 0x0) { int rpc = (RPC + 1) & 0xFFFF; RPC = (RPC + 1 + (sbyte)_memory.Read(RPC)) & 0xFFFF; @@ -850,7 +857,7 @@ namespace Jellyfish.Virtu private void ExecuteBvs(int cc) { - if ((RP & PV) != 0x0) + if ((RP & Pv) != 0x0) { int rpc = (RPC + 1) & 0xFFFF; RPC = (RPC + 1 + (sbyte)_memory.Read(RPC)) & 0xFFFF; @@ -865,60 +872,60 @@ namespace Jellyfish.Virtu private void ExecuteClc(int cc) { - RP &= ~PC; + RP &= ~Pc; CC += cc; } private void ExecuteCld(int cc) { - RP &= ~PD; + RP &= ~Pd; CC += cc; } private void ExecuteCli(int cc) { - RP &= ~PI; + RP &= ~Pi; CC += cc; } private void ExecuteClv(int cc) { - RP &= ~PV; + RP &= ~Pv; CC += cc; } private void ExecuteCmp(int data, int cc) { int diff = RA - data; - RP = RP & ~(PC | PN | PZ) | ((~diff >> 8) & PC) | DataPNZ[diff & 0xFF]; + RP = RP & ~(Pc | Pn | Pz) | ((~diff >> 8) & Pc) | DataPnz[diff & 0xFF]; CC += cc; } private void ExecuteCpx(int data, int cc) { int diff = RX - data; - RP = RP & ~(PC | PN | PZ) | ((~diff >> 8) & PC) | DataPNZ[diff & 0xFF]; + RP = RP & ~(Pc | Pn | Pz) | ((~diff >> 8) & Pc) | DataPnz[diff & 0xFF]; CC += cc; } private void ExecuteCpy(int data, int cc) { int diff = RY - data; - RP = RP & ~(PC | PN | PZ) | ((~diff >> 8) & PC) | DataPNZ[diff & 0xFF]; + RP = RP & ~(Pc | Pn | Pz) | ((~diff >> 8) & Pc) | DataPnz[diff & 0xFF]; CC += cc; } private void ExecuteDea(int cc) { RA = (RA - 1) & 0xFF; - RP = RP & ~(PN | PZ) | DataPNZ[RA]; + RP = RP & ~(Pn | Pz) | DataPnz[RA]; CC += cc; } private int ExecuteDec(int data, int cc) { data = (data - 1) & 0xFF; - RP = RP & ~(PN | PZ) | DataPNZ[data]; + RP = RP & ~(Pn | Pz) | DataPnz[data]; CC += cc; return data; @@ -927,35 +934,35 @@ namespace Jellyfish.Virtu private void ExecuteDex(int cc) { RX = (RX - 1) & 0xFF; - RP = RP & ~(PN | PZ) | DataPNZ[RX]; + RP = RP & ~(Pn | Pz) | DataPnz[RX]; CC += cc; } private void ExecuteDey(int cc) { RY = (RY - 1) & 0xFF; - RP = RP & ~(PN | PZ) | DataPNZ[RY]; + RP = RP & ~(Pn | Pz) | DataPnz[RY]; CC += cc; } private void ExecuteEor(int data, int cc) { RA ^= data; - RP = RP & ~(PN | PZ) | DataPNZ[RA]; + RP = RP & ~(Pn | Pz) | DataPnz[RA]; CC += cc; } private void ExecuteIna(int cc) { RA = (RA + 1) & 0xFF; - RP = RP & ~(PN | PZ) | DataPNZ[RA]; + RP = RP & ~(Pn | Pz) | DataPnz[RA]; CC += cc; } private int ExecuteInc(int data, int cc) { data = (data + 1) & 0xFF; - RP = RP & ~(PN | PZ) | DataPNZ[data]; + RP = RP & ~(Pn | Pz) | DataPnz[data]; CC += cc; return data; @@ -964,28 +971,14 @@ namespace Jellyfish.Virtu private void ExecuteInx(int cc) { RX = (RX + 1) & 0xFF; - RP = RP & ~(PN | PZ) | DataPNZ[RX]; + RP = RP & ~(Pn | Pz) | DataPnz[RX]; CC += cc; } private void ExecuteIny(int cc) { RY = (RY + 1) & 0xFF; - RP = RP & ~(PN | PZ) | DataPNZ[RY]; - CC += cc; - } - - private void ExecuteIrq(int cc) - { - Push(RPC >> 8); - Push(RPC & 0xFF); - Push(RP & ~PB); - RP |= PI; - if (Is65C02) // [C-10] - { - RP &= ~PD; - } - RPC = _memory.Read(0xFFFE) | (_memory.Read(0xFFFF) << 8); + RP = RP & ~(Pn | Pz) | DataPnz[RY]; CC += cc; } @@ -1028,29 +1021,29 @@ namespace Jellyfish.Virtu private void ExecuteLda(int data, int cc) { RA = data; - RP = RP & ~(PN | PZ) | DataPNZ[RA]; + RP = RP & ~(Pn | Pz) | DataPnz[RA]; CC += cc; } private void ExecuteLdx(int data, int cc) { RX = data; - RP = RP & ~(PN | PZ) | DataPNZ[RX]; + RP = RP & ~(Pn | Pz) | DataPnz[RX]; CC += cc; } private void ExecuteLdy(int data, int cc) { RY = data; - RP = RP & ~(PN | PZ) | DataPNZ[RY]; + RP = RP & ~(Pn | Pz) | DataPnz[RY]; CC += cc; } private int ExecuteLsr(int data, int cc) { - RP = RP & ~PC | (data & PC); + RP = RP & ~Pc | (data & Pc); data >>= 1; - RP = RP & ~(PN | PZ) | DataPNZ[data]; + RP = RP & ~(Pn | Pz) | DataPnz[data]; CC += cc; return data; @@ -1058,23 +1051,9 @@ namespace Jellyfish.Virtu private void ExecuteLsrImp(int cc) { - RP = RP & ~PC | (RA & PC); + RP = RP & ~Pc | (RA & Pc); RA >>= 1; - RP = RP & ~(PN | PZ) | DataPNZ[RA]; - CC += cc; - } - - private void ExecuteNmi(int cc) - { - Push(RPC >> 8); - Push(RPC & 0xFF); - Push(RP & ~PB); - RP |= PI; - if (Is65C02) // [C-10] - { - RP &= ~PD; - } - RPC = _memory.Read(0xFFFA) | (_memory.Read(0xFFFB) << 8); + RP = RP & ~(Pn | Pz) | DataPnz[RA]; CC += cc; } @@ -1092,7 +1071,7 @@ namespace Jellyfish.Virtu private void ExecuteOra(int data, int cc) { RA |= data; - RP = RP & ~(PN | PZ) | DataPNZ[RA]; + RP = RP & ~(Pn | Pz) | DataPnz[RA]; CC += cc; } @@ -1104,7 +1083,7 @@ namespace Jellyfish.Virtu private void ExecutePhp(int cc) { - Push(RP | PB); // [4-18] + Push(RP | Pb); // [4-18] CC += cc; } @@ -1123,7 +1102,7 @@ namespace Jellyfish.Virtu private void ExecutePla(int cc) { RA = Pull(); - RP = RP & ~(PN | PZ) | DataPNZ[RA]; + RP = RP & ~(Pn | Pz) | DataPnz[RA]; CC += cc; } @@ -1136,23 +1115,23 @@ namespace Jellyfish.Virtu private void ExecutePlx(int cc) { RX = Pull(); - RP = RP & ~(PN | PZ) | DataPNZ[RX]; + RP = RP & ~(Pn | Pz) | DataPnz[RX]; CC += cc; } private void ExecutePly(int cc) { RY = Pull(); - RP = RP & ~(PN | PZ) | DataPNZ[RY]; + RP = RP & ~(Pn | Pz) | DataPnz[RY]; CC += cc; } private int ExecuteRol(int data, int cc) { - int c = RP & PC; - RP = RP & ~PC | ((data >> 7) & PC); + int c = RP & Pc; + RP = RP & ~Pc | ((data >> 7) & Pc); data = ((data << 1) | c) & 0xFF; - RP = RP & ~(PN | PZ) | DataPNZ[data]; + RP = RP & ~(Pn | Pz) | DataPnz[data]; CC += cc; return data; @@ -1160,19 +1139,19 @@ namespace Jellyfish.Virtu private void ExecuteRolImp(int cc) { - int c = RP & PC; - RP = RP & ~PC | ((RA >> 7) & PC); + int c = RP & Pc; + RP = RP & ~Pc | ((RA >> 7) & Pc); RA = ((RA << 1) | c) & 0xFF; - RP = RP & ~(PN | PZ) | DataPNZ[RA]; + RP = RP & ~(Pn | Pz) | DataPnz[RA]; CC += cc; } private int ExecuteRor(int data, int cc) { - int c = RP & PC; - RP = RP & ~PC | (data & PC); + int c = RP & Pc; + RP = RP & ~Pc | (data & Pc); data = (c << 7) | (data >> 1); - RP = RP & ~(PN | PZ) | DataPNZ[data]; + RP = RP & ~(Pn | Pz) | DataPnz[data]; CC += cc; return data; @@ -1180,10 +1159,10 @@ namespace Jellyfish.Virtu private void ExecuteRorImp(int cc) { - int c = RP & PC; - RP = RP & ~PC | (RA & PC); + int c = RP & Pc; + RP = RP & ~Pc | (RA & Pc); RA = (c << 7) | (RA >> 1); - RP = RP & ~(PN | PZ) | DataPNZ[RA]; + RP = RP & ~(Pn | Pz) | DataPnz[RA]; CC += cc; } @@ -1204,16 +1183,16 @@ namespace Jellyfish.Virtu private void ExecuteSbc65N02(int data, int cc) { - if ((RP & PD) == 0x0) + if ((RP & Pd) == 0x0) { - int ra = RA - data - (~RP & PC); - RP = RP & ~(PC | PN | PV | PZ) | ((~ra >> 8) & PC) | DataPNZ[ra & 0xFF] | ((((RA ^ data) & (RA ^ (ra & 0xFF))) >> 1) & PV); + int ra = RA - data - (~RP & Pc); + RP = RP & ~(Pc | Pn | Pv | Pz) | ((~ra >> 8) & Pc) | DataPnz[ra & 0xFF] | ((((RA ^ data) & (RA ^ (ra & 0xFF))) >> 1) & Pv); RA = ra & 0xFF; CC += cc; } else // decimal { - int ral = (RA & 0x0F) - (data & 0x0F) - (~RP & PC); + int ral = (RA & 0x0F) - (data & 0x0F) - (~RP & Pc); int rah = (RA >> 4) - (data >> 4); if (ral < 0) { @@ -1221,11 +1200,11 @@ namespace Jellyfish.Virtu rah--; } int ra = (ral | (rah << 4)) & 0xFF; - RP = RP & ~(PN | PV | PZ) | PC | DataPN[ra] | ((((RA ^ data) & (RA ^ ra)) >> 1) & PV) | DataPZ[(RA - data - (~RP & PC)) & 0xFF]; + RP = RP & ~(Pn | Pv | Pz) | Pc | DataPn[ra] | ((((RA ^ data) & (RA ^ ra)) >> 1) & Pv) | DataPz[(RA - data - (~RP & Pc)) & 0xFF]; if (rah < 0) { rah += 10; - RP &= ~PC; + RP &= ~Pc; } RA = (ral | (rah << 4)) & 0xFF; CC += cc; @@ -1234,30 +1213,30 @@ namespace Jellyfish.Virtu private void ExecuteSbc65C02(int data, int cc) { - if ((RP & PD) == 0x0) + if ((RP & Pd) == 0x0) { - int ra = RA - data - (~RP & PC); - RP = RP & ~(PC | PN | PV | PZ) | ((~ra >> 8) & PC) | DataPNZ[ra & 0xFF] | ((((RA ^ data) & (RA ^ (ra & 0xFF))) >> 1) & PV); + int ra = RA - data - (~RP & Pc); + RP = RP & ~(Pc | Pn | Pv | Pz) | ((~ra >> 8) & Pc) | DataPnz[ra & 0xFF] | ((((RA ^ data) & (RA ^ (ra & 0xFF))) >> 1) & Pv); RA = ra & 0xFF; CC += cc; } else // decimal { - int ral = (RA & 0x0F) - (data & 0x0F) - (~RP & PC); + int ral = (RA & 0x0F) - (data & 0x0F) - (~RP & Pc); int rah = (RA >> 4) - (data >> 4); if (ral < 0) { ral += 10; rah--; } - RP |= PC; + RP |= Pc; if (rah < 0) { rah += 10; - RP &= ~PC; + RP &= ~Pc; } int ra = (ral | (rah << 4)) & 0xFF; - RP = RP & ~(PN | PV | PZ) | DataPNZ[ra] | ((((RA ^ data) & (RA ^ ra)) >> 1) & PV); + RP = RP & ~(Pn | Pv | Pz) | DataPnz[ra] | ((((RA ^ data) & (RA ^ ra)) >> 1) & Pv); RA = ra; CC += cc + 1; } @@ -1265,19 +1244,19 @@ namespace Jellyfish.Virtu private void ExecuteSec(int cc) { - RP |= PC; + RP |= Pc; CC += cc; } private void ExecuteSed(int cc) { - RP |= PD; + RP |= Pd; CC += cc; } private void ExecuteSei(int cc) { - RP |= PI; + RP |= Pi; CC += cc; } @@ -1304,20 +1283,20 @@ namespace Jellyfish.Virtu private void ExecuteTax(int cc) { RX = RA; - RP = RP & ~(PN | PZ) | DataPNZ[RX]; + RP = RP & ~(Pn | Pz) | DataPnz[RX]; CC += cc; } private void ExecuteTay(int cc) { RY = RA; - RP = RP & ~(PN | PZ) | DataPNZ[RY]; + RP = RP & ~(Pn | Pz) | DataPnz[RY]; CC += cc; } private int ExecuteTrb(int data, int cc) { - RP = RP & ~PZ | DataPZ[RA & data]; + RP = RP & ~Pz | DataPz[RA & data]; data &= ~RA; CC += cc; @@ -1326,7 +1305,7 @@ namespace Jellyfish.Virtu private int ExecuteTsb(int data, int cc) { - RP = RP & ~PZ | DataPZ[RA & data]; + RP = RP & ~Pz | DataPz[RA & data]; data |= RA; CC += cc; @@ -1336,14 +1315,14 @@ namespace Jellyfish.Virtu private void ExecuteTsx(int cc) { RX = RS; - RP = RP & ~(PN | PZ) | DataPNZ[RX]; + RP = RP & ~(Pn | Pz) | DataPnz[RX]; CC += cc; } private void ExecuteTxa(int cc) { RA = RX; - RP = RP & ~(PN | PZ) | DataPNZ[RA]; + RP = RP & ~(Pn | Pz) | DataPnz[RA]; CC += cc; } @@ -1356,7 +1335,7 @@ namespace Jellyfish.Virtu private void ExecuteTya(int cc) { RA = RY; - RP = RP & ~(PN | PZ) | DataPNZ[RA]; + RP = RP & ~(Pn | Pz) | DataPnz[RA]; CC += cc; } #endregion @@ -2094,9 +2073,11 @@ namespace Jellyfish.Virtu { ExecuteTya(2); } + #endregion #region 65N02 OpCode Actions + private void Execute65N02Adc61() // adc (zpg, x) { GetAddressZpgIndX(); @@ -2756,6 +2737,7 @@ namespace Jellyfish.Virtu GetAddressAbsXCC(); ExecuteSbc65N02(ReadAbsX(), 4); } + #endregion #region 65C02 OpCode Actions @@ -3441,10 +3423,15 @@ namespace Jellyfish.Virtu GetAddressAbs(); WriteAbs(ExecuteTsb(ReadAbs(), 6)); } + #endregion - public bool Is65C02 { get { return _is65C02; } set { _is65C02 = value; _executeOpCode = _is65C02 ? ExecuteOpCode65C02 : ExecuteOpCode65N02; } } - public bool IsThrottled { get; set; } + private bool Is65C02 + { + get => _is65C02; + set { _is65C02 = value; _executeOpCode = _is65C02 ? _executeOpCode65C02 : _executeOpCode65N02; } + } + public int Multiplier { get; set; } public int RA { get; set; } @@ -3458,8 +3445,6 @@ namespace Jellyfish.Virtu public int OpCode { get; private set; } public long Cycles { get; private set; } - private Memory _memory; - [JsonIgnore] private bool _is65C02; [JsonIgnore] @@ -3468,68 +3453,68 @@ namespace Jellyfish.Virtu [JsonIgnore] public Action TraceCallback; - /// Carry Flag + /// Carry Flag [JsonIgnore] public bool FlagC { - get { return (RP & 0x01) != 0; } - set { RP = (byte)((RP & ~0x01) | (value ? 0x01 : 0x00)); } + get => (RP & 0x01) != 0; + set => RP = (byte)((RP & ~0x01) | (value ? 0x01 : 0x00)); } /// Zero Flag [JsonIgnore] public bool FlagZ { - get { return (RP & 0x02) != 0; } - set { RP = (byte)((RP & ~0x02) | (value ? 0x02 : 0x00)); } + get => (RP & 0x02) != 0; + set => RP = (byte)((RP & ~0x02) | (value ? 0x02 : 0x00)); } /// Interrupt Disable Flag [JsonIgnore] public bool FlagI { - get { return (RP & 0x04) != 0; } - set { RP = (byte)((RP & ~0x04) | (value ? 0x04 : 0x00)); } + get => (RP & 0x04) != 0; + set => RP = (byte)((RP & ~0x04) | (value ? 0x04 : 0x00)); } /// Decimal Mode Flag [JsonIgnore] public bool FlagD { - get { return (RP & 0x08) != 0; } - set { RP = (byte)((RP & ~0x08) | (value ? 0x08 : 0x00)); } + get => (RP & 0x08) != 0; + set => RP = (byte)((RP & ~0x08) | (value ? 0x08 : 0x00)); } /// Break Flag [JsonIgnore] public bool FlagB { - get { return (RP & 0x10) != 0; } - set { RP = (byte)((RP & ~0x10) | (value ? 0x10 : 0x00)); } + get => (RP & 0x10) != 0; + set => RP = (byte)((RP & ~0x10) | (value ? 0x10 : 0x00)); } /// T... Flag [JsonIgnore] public bool FlagT { - get { return (RP & 0x20) != 0; } - set { RP = (byte)((RP & ~0x20) | (value ? 0x20 : 0x00)); } + get => (RP & 0x20) != 0; + set => RP = (byte)((RP & ~0x20) | (value ? 0x20 : 0x00)); } /// Overflow Flag [JsonIgnore] public bool FlagV { - get { return (RP & 0x40) != 0; } - set { RP = (byte)((RP & ~0x40) | (value ? 0x40 : 0x00)); } + get => (RP & 0x40) != 0; + set => RP = (byte)((RP & ~0x40) | (value ? 0x40 : 0x00)); } /// Negative Flag [JsonIgnore] public bool FlagN { - get { return (RP & 0x80) != 0; } - set { RP = (byte)((RP & ~0x80) | (value ? 0x80 : 0x00)); } + get => (RP & 0x80) != 0; + set => RP = (byte)((RP & ~0x80) | (value ? 0x80 : 0x00)); } } } diff --git a/ExternalCoreProjects/Virtu/CpuData.cs b/ExternalCoreProjects/Virtu/CpuData.cs deleted file mode 100644 index 9fc4bdb615..0000000000 --- a/ExternalCoreProjects/Virtu/CpuData.cs +++ /dev/null @@ -1,86 +0,0 @@ -using Newtonsoft.Json; -using System; - -namespace Jellyfish.Virtu -{ - public partial class Cpu - { - private const int OpCodeCount = 256; - - [JsonIgnore] - private Action[] ExecuteOpCode65N02; - [JsonIgnore] - private Action[] ExecuteOpCode65C02; - - private const int PC = 0x01; - private const int PZ = 0x02; - private const int PI = 0x04; - private const int PD = 0x08; - private const int PB = 0x10; - private const int PR = 0x20; - private const int PV = 0x40; - private const int PN = 0x80; - - private const int DataCount = 256; - - private static readonly int[] DataPN = new int[DataCount] - { - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, - PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, - PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, - PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, - PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, - PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, - PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, - PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN - }; - - private static readonly int[] DataPZ = new int[DataCount] - { - PZ, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 - }; - - private static readonly int[] DataPNZ = new int[DataCount] - { - PZ, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, - PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, - PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, - PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, - PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, - PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, - PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, - PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN, PN - }; - } -} diff --git a/ExternalCoreProjects/Virtu/Disk525.cs b/ExternalCoreProjects/Virtu/Disk525.cs index eb6cde71a6..03012e3383 100644 --- a/ExternalCoreProjects/Virtu/Disk525.cs +++ b/ExternalCoreProjects/Virtu/Disk525.cs @@ -1,56 +1,55 @@ using System; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Text.RegularExpressions; -using Jellyfish.Library; namespace Jellyfish.Virtu { - public abstract class Disk525 - { + internal abstract class Disk525 + { + // ReSharper disable once UnusedMember.Global + // ReSharper disable once PublicConstructorInAbstractClass public Disk525() { } - protected Disk525(string name, byte[] data, bool isWriteProtected) - { - Name = name; - Data = data; - IsWriteProtected = isWriteProtected; - } - public static Disk525 CreateDisk(string name, byte[] data, bool isWriteProtected) - { - if (name == null) - { - throw new ArgumentNullException("name"); - } + protected Disk525(byte[] data, bool isWriteProtected) + { + Data = data; + IsWriteProtected = isWriteProtected; + } - if (name.EndsWith(".do", StringComparison.OrdinalIgnoreCase) || - name.EndsWith(".dsk", StringComparison.OrdinalIgnoreCase)) // assumes dos sector skew - { - return new DiskDsk(name, data, isWriteProtected, SectorSkew.Dos); - } - else if (name.EndsWith(".nib", StringComparison.OrdinalIgnoreCase)) - { - return new DiskNib(name, data, isWriteProtected); - } - else if (name.EndsWith(".po", StringComparison.OrdinalIgnoreCase)) - { - return new DiskDsk(name, data, isWriteProtected, SectorSkew.ProDos); - } + public static Disk525 CreateDisk(string name, byte[] data, bool isWriteProtected) + { + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } - return null; - } + if (name.EndsWith(".do", StringComparison.OrdinalIgnoreCase) || + name.EndsWith(".dsk", StringComparison.OrdinalIgnoreCase)) // assumes dos sector skew + { + return new DiskDsk(data, isWriteProtected, SectorSkew.Dos); + } - public abstract void ReadTrack(int number, int fraction, byte[] buffer); - public abstract void WriteTrack(int number, int fraction, byte[] buffer); + if (name.EndsWith(".nib", StringComparison.OrdinalIgnoreCase)) + { + return new DiskNib(data, isWriteProtected); + } - public string Name { get; private set; } + if (name.EndsWith(".po", StringComparison.OrdinalIgnoreCase)) + { + return new DiskDsk(data, isWriteProtected, SectorSkew.ProDos); + } - public byte[] Data { get; protected set; } - public bool IsWriteProtected { get; private set; } + return null; + } - public const int SectorCount = 16; - public const int SectorSize = 0x100; - public const int TrackCount = 35; - public const int TrackSize = 0x1A00; - } + public abstract void ReadTrack(int number, int fraction, byte[] buffer); + public abstract void WriteTrack(int number, int fraction, byte[] buffer); + + public byte[] Data { get; protected set; } + + // ReSharper disable once AutoPropertyCanBeMadeGetOnly.Local + public bool IsWriteProtected { get; private set; } + + public const int SectorCount = 16; + public const int SectorSize = 0x100; + public const int TrackSize = 0x1A00; + } } diff --git a/ExternalCoreProjects/Virtu/DiskDsk.cs b/ExternalCoreProjects/Virtu/DiskDsk.cs index 11c3a598f6..95b014fc85 100644 --- a/ExternalCoreProjects/Virtu/DiskDsk.cs +++ b/ExternalCoreProjects/Virtu/DiskDsk.cs @@ -1,325 +1,333 @@ using System; -using System.IO; -using Jellyfish.Library; namespace Jellyfish.Virtu { - public enum SectorSkew { None = 0, Dos, ProDos }; + internal enum SectorSkew { None = 0, Dos, ProDos }; - public sealed class DiskDsk : Disk525 - { + internal sealed class DiskDsk : Disk525 + { + // ReSharper disable once UnusedMember.Global public DiskDsk() { } - public DiskDsk(string name, byte[] data, bool isWriteProtected, SectorSkew sectorSkew) : - base(name, data, isWriteProtected) - { - _sectorSkew = SectorSkewMode[(int)sectorSkew]; - } - public DiskDsk(string name, Stream stream, bool isWriteProtected, SectorSkew sectorSkew) : - base(name, new byte[TrackCount * SectorCount * SectorSize], isWriteProtected) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } + public DiskDsk(byte[] data, bool isWriteProtected, SectorSkew sectorSkew) + : base(data, isWriteProtected) + { + _sectorSkew = SectorSkewMode[(int)sectorSkew]; + } - stream.ReadBlock(Data); - _sectorSkew = SectorSkewMode[(int)sectorSkew]; - } + public override void ReadTrack(int number, int fraction, byte[] buffer) + { + int track = number / 2; - public override void ReadTrack(int number, int fraction, byte[] buffer) - { - int track = number / 2; + _trackBuffer = buffer; + _trackOffset = 0; - _trackBuffer = buffer; - _trackOffset = 0; + WriteNibble(0xFF, 48); // gap 0 - WriteNibble(0xFF, 48); // gap 0 + for (int sector = 0; sector < SectorCount; sector++) + { + WriteNibble(0xD5); // address prologue + WriteNibble(0xAA); + WriteNibble(0x96); - for (int sector = 0; sector < SectorCount; sector++) - { - WriteNibble(0xD5); // address prologue - WriteNibble(0xAA); - WriteNibble(0x96); + WriteNibble44(Volume); + WriteNibble44(track); + WriteNibble44(sector); + WriteNibble44(Volume ^ track ^ sector); - WriteNibble44(Volume); - WriteNibble44(track); - WriteNibble44(sector); - WriteNibble44(Volume ^ track ^ sector); + WriteNibble(0xDE); // address epilogue + WriteNibble(0xAA); + WriteNibble(0xEB); + WriteNibble(0xFF, 8); - WriteNibble(0xDE); // address epilogue - WriteNibble(0xAA); - WriteNibble(0xEB); - WriteNibble(0xFF, 8); + WriteNibble(0xD5); // data prologue + WriteNibble(0xAA); + WriteNibble(0xAD); - WriteNibble(0xD5); // data prologue - WriteNibble(0xAA); - WriteNibble(0xAD); + WriteDataNibbles((track * SectorCount + _sectorSkew[sector]) * SectorSize); - WriteDataNibbles((track * SectorCount + _sectorSkew[sector]) * SectorSize); + WriteNibble(0xDE); // data epilogue + WriteNibble(0xAA); + WriteNibble(0xEB); + WriteNibble(0xFF, 16); + } + } - WriteNibble(0xDE); // data epilogue - WriteNibble(0xAA); - WriteNibble(0xEB); - WriteNibble(0xFF, 16); - } - } + public override void WriteTrack(int number, int fraction, byte[] buffer) + { + if (IsWriteProtected) + { + return; + } - public override void WriteTrack(int number, int fraction, byte[] buffer) - { - if (IsWriteProtected) - return; + int track = number / 2; - int track = number / 2; + _trackBuffer = buffer; + _trackOffset = 0; + int sectorsDone = 0; - _trackBuffer = buffer; - _trackOffset = 0; - int sectorsDone = 0; + for (int sector = 0; sector < SectorCount; sector++) + { + if (!Read3Nibbles(0xD5, 0xAA, 0x96, 0x304)) + { + break; // no address prologue + } - for (int sector = 0; sector < SectorCount; sector++) - { - if (!Read3Nibbles(0xD5, 0xAA, 0x96, 0x304)) - break; // no address prologue + /*int readVolume = */ + ReadNibble44(); - /*int readVolume = */ReadNibble44(); + int readTrack = ReadNibble44(); + if (readTrack != track) + { + break; // bad track number + } - int readTrack = ReadNibble44(); - if (readTrack != track) - break; // bad track number + int readSector = ReadNibble44(); + if (readSector > SectorCount) + { + break; // bad sector number + } - int readSector = ReadNibble44(); - if (readSector > SectorCount) - break; // bad sector number - if ((sectorsDone & (0x1 << readSector)) != 0) - break; // already done this sector + if ((sectorsDone & (0x1 << readSector)) != 0) + { + break; // already done this sector + } - if (ReadNibble44() != (Volume ^ readTrack ^ readSector)) - break; // bad address checksum + if (ReadNibble44() != (Volume ^ readTrack ^ readSector)) + { + break; // bad address checksum + } - if ((ReadNibble() != 0xDE) || (ReadNibble() != 0xAA)) - break; // bad address epilogue + if (ReadNibble() != 0xDE || ReadNibble() != 0xAA) + { + break; // bad address epilogue + } - if (!Read3Nibbles(0xD5, 0xAA, 0xAD, 0x20)) - break; // no data prologue + if (!Read3Nibbles(0xD5, 0xAA, 0xAD, 0x20)) + { + break; // no data prologue + } - if (!ReadDataNibbles((track * SectorCount + _sectorSkew[sector]) * SectorSize)) - break; // bad data checksum + if (!ReadDataNibbles((track * SectorCount + _sectorSkew[sector]) * SectorSize)) + { + break; // bad data checksum + } - if ((ReadNibble() != 0xDE) || (ReadNibble() != 0xAA)) - break; // bad data epilogue + if (ReadNibble() != 0xDE || ReadNibble() != 0xAA) + { + break; // bad data epilogue + } - sectorsDone |= 0x1 << sector; - } + sectorsDone |= 0x1 << sector; + } - if (sectorsDone != 0xFFFF) - throw new InvalidOperationException("disk error"); // TODO: we should alert the user and "dump" a NIB - } + if (sectorsDone != 0xFFFF) + { + throw new InvalidOperationException("disk error"); // TODO: we should alert the user and "dump" a NIB + } + } - private byte ReadNibble() - { - byte data = _trackBuffer[_trackOffset]; - if (_trackOffset++ == TrackSize) - { - _trackOffset = 0; - } - return data; - } + private byte ReadNibble() + { + byte data = _trackBuffer[_trackOffset]; + if (_trackOffset++ == TrackSize) + { + _trackOffset = 0; + } + return data; + } - private bool Read3Nibbles(byte data1, byte data2, byte data3, int maxReads) - { - bool result = false; - while (--maxReads > 0) - { - if (ReadNibble() != data1) - continue; + private bool Read3Nibbles(byte data1, byte data2, byte data3, int maxReads) + { + bool result = false; + while (--maxReads > 0) + { + if (ReadNibble() != data1) + { + continue; + } - if (ReadNibble() != data2) - continue; + if (ReadNibble() != data2) + { + continue; + } - if (ReadNibble() != data3) - continue; + if (ReadNibble() != data3) + { + continue; + } - result = true; - break; - } - return result; - } + result = true; + break; + } + return result; + } - private int ReadNibble44() - { - return (((ReadNibble() << 1) | 0x1) & ReadNibble()); - } + private int ReadNibble44() + { + return (((ReadNibble() << 1) | 0x1) & ReadNibble()); + } - private byte ReadTranslatedNibble() - { - byte data = NibbleToByte[ReadNibble()]; - // TODO: check that invalid nibbles aren't used - // (put 0xFFs for invalid nibbles in the table) - //if (data == 0xFF) - //{ - //throw an exception - //} - return data; - } + private byte ReadTranslatedNibble() + { + byte data = NibbleToByte[ReadNibble()]; + return data; + } - private bool ReadDataNibbles(int sectorOffset) - { - byte a, x, y; + private bool ReadDataNibbles(int sectorOffset) + { + byte y = SecondaryBufferLength; + byte a = 0; + do // fill and de-nibblize secondary buffer + { + a = _secondaryBuffer[--y] = (byte)(a ^ ReadTranslatedNibble()); + } + while (y > 0); - y = SecondaryBufferLength; - a = 0; - do // fill and de-nibblize secondary buffer - { - a = _secondaryBuffer[--y] = (byte)(a ^ ReadTranslatedNibble()); - } - while (y > 0); + do // fill and de-nibblize secondary buffer + { + a = _primaryBuffer[y++] = (byte)(a ^ ReadTranslatedNibble()); + } + while (y != 0); - do // fill and de-nibblize secondary buffer - { - a = _primaryBuffer[y++] = (byte)(a ^ ReadTranslatedNibble()); - } - while (y != 0); + int checksum = a ^ ReadTranslatedNibble(); // should be 0 - int checksum = a ^ ReadTranslatedNibble(); // should be 0 + var x = y = 0; + do // decode data + { + if (x == 0) + { + x = SecondaryBufferLength; + } + a = (byte)((_primaryBuffer[y] << 2) | SwapBits[_secondaryBuffer[--x] & 0x03]); + _secondaryBuffer[x] >>= 2; + Data[sectorOffset + y] = a; + } + while (++y != 0); - x = y = 0; - do // decode data - { - if (x == 0) - { - x = SecondaryBufferLength; - } - a = (byte)((_primaryBuffer[y] << 2) | SwapBits[_secondaryBuffer[--x] & 0x03]); - _secondaryBuffer[x] >>= 2; - Data[sectorOffset + y] = a; - } - while (++y != 0); + return (checksum == 0); + } - return (checksum == 0); - } + private void WriteNibble(int data) + { + _trackBuffer[_trackOffset++] = (byte)data; + } - private void WriteNibble(int data) - { - _trackBuffer[_trackOffset++] = (byte)data; - } + private void WriteNibble(int data, int count) + { + while (count-- > 0) + { + WriteNibble(data); + } + } - private void WriteNibble(int data, int count) - { - while (count-- > 0) - { - WriteNibble(data); - } - } + private void WriteNibble44(int data) + { + WriteNibble((data >> 1) | 0xAA); + WriteNibble(data | 0xAA); + } - private void WriteNibble44(int data) - { - WriteNibble((data >> 1) | 0xAA); - WriteNibble(data | 0xAA); - } + private void WriteDataNibbles(int sectorOffset) + { + byte a, x; - private void WriteDataNibbles(int sectorOffset) - { - byte a, x, y; + for (x = 0; x < SecondaryBufferLength; x++) + { + _secondaryBuffer[x] = 0; // zero secondary buffer + } - for (x = 0; x < SecondaryBufferLength; x++) - { - _secondaryBuffer[x] = 0; // zero secondary buffer - } + byte y = 2; + do // fill buffers + { + x = 0; + do + { + a = Data[sectorOffset + --y]; + _secondaryBuffer[x] = (byte)((_secondaryBuffer[x] << 2) | SwapBits[a & 0x03]); // b1,b0 -> secondary buffer + _primaryBuffer[y] = (byte)(a >> 2); // b7-b2 -> primary buffer + } + while (++x < SecondaryBufferLength); + } + while (y != 0); - y = 2; - do // fill buffers - { - x = 0; - do - { - a = Data[sectorOffset + --y]; - _secondaryBuffer[x] = (byte)((_secondaryBuffer[x] << 2) | SwapBits[a & 0x03]); // b1,b0 -> secondary buffer - _primaryBuffer[y] = (byte)(a >> 2); // b7-b2 -> primary buffer - } - while (++x < SecondaryBufferLength); - } - while (y != 0); + y = SecondaryBufferLength; + do // write secondary buffer + { + WriteNibble(ByteToNibble[_secondaryBuffer[y] ^ _secondaryBuffer[y - 1]]); + } + while (--y != 0); - y = SecondaryBufferLength; - do // write secondary buffer - { - WriteNibble(ByteToNibble[_secondaryBuffer[y] ^ _secondaryBuffer[y - 1]]); - } - while (--y != 0); + a = _secondaryBuffer[0]; + do // write primary buffer + { + WriteNibble(ByteToNibble[a ^ _primaryBuffer[y]]); + a = _primaryBuffer[y]; + } + while (++y != 0); - a = _secondaryBuffer[0]; - do // write primary buffer - { - WriteNibble(ByteToNibble[a ^ _primaryBuffer[y]]); - a = _primaryBuffer[y]; - } - while (++y != 0); - - WriteNibble(ByteToNibble[a]); // data checksum - } + WriteNibble(ByteToNibble[a]); // data checksum + } - private byte[] _trackBuffer; - private int _trackOffset; - private byte[] _primaryBuffer = new byte[0x100]; - private const int SecondaryBufferLength = 0x56; - private byte[] _secondaryBuffer = new byte[SecondaryBufferLength + 1]; - private int[] _sectorSkew; - private const int Volume = 0xFE; + private byte[] _trackBuffer; + private int _trackOffset; + private byte[] _primaryBuffer = new byte[0x100]; + private const int SecondaryBufferLength = 0x56; + private byte[] _secondaryBuffer = new byte[SecondaryBufferLength + 1]; + private int[] _sectorSkew; + private const int Volume = 0xFE; - private static readonly byte[] SwapBits = { 0, 2, 1, 3 }; + private static readonly byte[] SwapBits = { 0, 2, 1, 3 }; - private static readonly int[] SectorSkewNone = new int[SectorCount] - { - 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF - }; + private static readonly int[] SectorSkewNone = + { + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF + }; - private static readonly int[] SectorSkewDos = new int[SectorCount] - { - 0x0, 0x7, 0xE, 0x6, 0xD, 0x5, 0xC, 0x4, 0xB, 0x3, 0xA, 0x2, 0x9, 0x1, 0x8, 0xF - }; + private static readonly int[] SectorSkewDos = + { + 0x0, 0x7, 0xE, 0x6, 0xD, 0x5, 0xC, 0x4, 0xB, 0x3, 0xA, 0x2, 0x9, 0x1, 0x8, 0xF + }; - private static readonly int[] SectorSkewProDos = new int[SectorCount] - { - 0x0, 0x8, 0x1, 0x9, 0x2, 0xA, 0x3, 0xB, 0x4, 0xC, 0x5, 0xD, 0x6, 0xE, 0x7, 0xF - }; + private static readonly int[] SectorSkewProDos = + { + 0x0, 0x8, 0x1, 0x9, 0x2, 0xA, 0x3, 0xB, 0x4, 0xC, 0x5, 0xD, 0x6, 0xE, 0x7, 0xF + }; - private const int SectorSkewCount = 3; + private static readonly int[][] SectorSkewMode = + { + SectorSkewNone, SectorSkewDos, SectorSkewProDos + }; - private static readonly int[][] SectorSkewMode = new int[SectorSkewCount][] - { - SectorSkewNone, SectorSkewDos, SectorSkewProDos - }; + private static readonly byte[] ByteToNibble = + { + 0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6, 0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3, + 0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xCB, 0xCD, 0xCE, 0xCF, 0xD3, + 0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE5, 0xE6, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC, + 0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF + }; - private static readonly byte[] ByteToNibble = new byte[] - { - 0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6, 0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3, - 0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xCB, 0xCD, 0xCE, 0xCF, 0xD3, - 0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE5, 0xE6, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC, - 0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF - }; + private static readonly byte[] NibbleToByte = + { + // padding for offset (not used) + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, - private static readonly byte[] NibbleToByte = new byte[] - { - // padding for offset (not used) - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, - 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, - 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, - 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, - 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, - 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, - 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, - - // nibble translate table + // nibble translate table 0x00, 0x01, 0x98, 0x99, 0x02, 0x03, 0x9C, 0x04, 0x05, 0x06, - 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0x07, 0x08, 0xA8, 0xA9, 0xAA, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, - 0xB0, 0xB1, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0xB8, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, - 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0x1B, 0xCC, 0x1C, 0x1D, 0x1E, - 0xD0, 0xD1, 0xD2, 0x1F, 0xD4, 0xD5, 0x20, 0x21, 0xD8, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, - 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0x29, 0x2A, 0x2B, 0xE8, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, - 0xF0, 0xF1, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0xF8, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F - }; - } + 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0x07, 0x08, 0xA8, 0xA9, 0xAA, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0xB0, 0xB1, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0xB8, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, + 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0x1B, 0xCC, 0x1C, 0x1D, 0x1E, + 0xD0, 0xD1, 0xD2, 0x1F, 0xD4, 0xD5, 0x20, 0x21, 0xD8, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0x29, 0x2A, 0x2B, 0xE8, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, + 0xF0, 0xF1, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0xF8, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F + }; + } } diff --git a/ExternalCoreProjects/Virtu/DiskIIController.cs b/ExternalCoreProjects/Virtu/DiskIIController.cs index ae15a6df97..138d4bad01 100644 --- a/ExternalCoreProjects/Virtu/DiskIIController.cs +++ b/ExternalCoreProjects/Virtu/DiskIIController.cs @@ -1,32 +1,30 @@ -using System; -using System.Collections.ObjectModel; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using Jellyfish.Library; -using Jellyfish.Virtu.Services; -using System.Collections.Generic; +using System.Collections.Generic; namespace Jellyfish.Virtu { - public sealed class DiskIIController : PeripheralCard + public interface IDiskIIController : IPeripheralCard { + bool DriveLight { get; set; } + + // ReSharper disable once UnusedMemberInSuper.Global + DiskIIDrive Drive1 { get; } + } + + // ReSharper disable once UnusedMember.Global + public sealed class DiskIIController : IDiskIIController + { + // ReSharper disable once FieldCanBeMadeReadOnly.Local + private IVideo _video; + + // ReSharper disable once UnusedMember.Global public DiskIIController() { } - public DiskIIController(Machine machine, byte[] diskIIRom) : - base(machine) + + public DiskIIController(IVideo video, byte[] diskIIRom) { + _video = video; _romRegionC1C7 = diskIIRom; - Drive1 = new DiskIIDrive(machine); - Drive2 = new DiskIIDrive(machine); - - Drives = new List { Drive1, Drive2 }; - - BootDrive = Drive1; - } - - public override void Initialize() { } - - public override void Reset() - { + Drive1 = new DiskIIDrive(this); + Drive2 = new DiskIIDrive(this); _phaseStates = 0; SetMotorOn(false); SetDriveNumber(0); @@ -34,7 +32,13 @@ namespace Jellyfish.Virtu _writeMode = false; } - public override int ReadIoRegionC0C0(int address) + public bool DriveLight { get; set; } + + public IList Drives => new List { Drive1, Drive2 }; + + public void WriteIoRegionC8CF(int address, int data) => _video.ReadFloatingBus(); + + public int ReadIoRegionC0C0(int address) { switch (address & 0xF) { @@ -73,10 +77,8 @@ namespace Jellyfish.Virtu { return _latch = Drives[_driveNumber].Read(); } - else - { - WriteLatch(); - } + + WriteLatch(); } break; @@ -116,15 +118,17 @@ namespace Jellyfish.Virtu return _driveSpin ? 0x7E : 0x7F; } - return ReadFloatingBus(); + return _video.ReadFloatingBus(); } - public override int ReadIoRegionC1C7(int address) + public int ReadIoRegionC1C7(int address) { return _romRegionC1C7[address & 0xFF]; } - public override void WriteIoRegionC0C0(int address, int data) + public int ReadIoRegionC8CF(int address) => _video.ReadFloatingBus(); + + public void WriteIoRegionC0C0(int address, int data) { switch (address & 0xF) { @@ -186,6 +190,8 @@ namespace Jellyfish.Virtu } } + public void WriteIoRegionC1C7(int address, int data) { } + private void WriteLatch() { // write protect is forced if phase 1 is on [F9.7] @@ -231,17 +237,14 @@ namespace Jellyfish.Virtu } } + // ReSharper disable once AutoPropertyCanBeMadeGetOnly.Local public DiskIIDrive Drive1 { get; private set; } + + + // ReSharper disable once AutoPropertyCanBeMadeGetOnly.Local public DiskIIDrive Drive2 { get; private set; } - public List Drives { get; private set; } - - public DiskIIDrive BootDrive { get; private set; } - - private const int Phase0On = 1 << 0; private const int Phase1On = 1 << 1; - private const int Phase2On = 1 << 2; - private const int Phase3On = 1 << 3; private int _latch; private int _phaseStates; diff --git a/ExternalCoreProjects/Virtu/DiskIIDrive.cs b/ExternalCoreProjects/Virtu/DiskIIDrive.cs index 8a4b95bb9e..ab96889a38 100644 --- a/ExternalCoreProjects/Virtu/DiskIIDrive.cs +++ b/ExternalCoreProjects/Virtu/DiskIIDrive.cs @@ -1,128 +1,124 @@ -using System; -using System.IO; -using Jellyfish.Library; -using Jellyfish.Virtu.Services; +using Newtonsoft.Json; namespace Jellyfish.Virtu { - public sealed class DiskIIDrive : MachineComponent - { + public sealed class DiskIIDrive + { + // ReSharper disable once FieldCanBeMadeReadOnly.Local + private IDiskIIController _diskController; + + // ReSharper disable once UnusedMember.Global public DiskIIDrive() { } - public DiskIIDrive(Machine machine) : - base(machine) - { - DriveArmStepDelta[0] = new int[] { 0, 0, 1, 1, 0, 0, 1, 1, -1, -1, 0, 0, -1, -1, 0, 0 }; // phase 0 - DriveArmStepDelta[1] = new int[] { 0, -1, 0, -1, 1, 0, 1, 0, 0, -1, 0, -1, 1, 0, 1, 0 }; // phase 1 - DriveArmStepDelta[2] = new int[] { 0, 0, -1, -1, 0, 0, -1, -1, 1, 1, 0, 0, 1, 1, 0, 0 }; // phase 2 - DriveArmStepDelta[3] = new int[] { 0, 1, 0, 1, -1, 0, -1, 0, 0, 1, 0, 1, -1, 0, -1, 0 }; // phase 3 - } - public void InsertDisk(string name, byte[] data, bool isWriteProtected) - { - DebugService.WriteMessage("Inserting disk '{0}'", name); - FlushTrack(); - _disk = Disk525.CreateDisk(name, data, isWriteProtected); - _trackLoaded = false; - } + public DiskIIDrive(IDiskIIController diskController) + { + _diskController = diskController; + _driveArmStepDelta[0] = new[] { 0, 0, 1, 1, 0, 0, 1, 1, -1, -1, 0, 0, -1, -1, 0, 0 }; // phase 0 + _driveArmStepDelta[1] = new[] { 0, -1, 0, -1, 1, 0, 1, 0, 0, -1, 0, -1, 1, 0, 1, 0 }; // phase 1 + _driveArmStepDelta[2] = new[] { 0, 0, -1, -1, 0, 0, -1, -1, 1, 1, 0, 0, 1, 1, 0, 0 }; // phase 2 + _driveArmStepDelta[3] = new[] { 0, 1, 0, 1, -1, 0, -1, 0, 0, 1, 0, 1, -1, 0, -1, 0 }; // phase 3 + } - public void RemoveDisk() - { - if (_disk != null) - { - DebugService.WriteMessage("Removing disk '{0}'", _disk.Name); - _trackLoaded = false; - _trackChanged = false; - _trackNumber = 0; - _trackOffset = 0; - _disk = null; - } - } + // ReSharper disable once UnusedMember.Global + public void InsertDisk(string name, byte[] data, bool isWriteProtected) + { + FlushTrack(); + _disk = Disk525.CreateDisk(name, data, isWriteProtected); + _trackLoaded = false; + } - public void ApplyPhaseChange(int phaseState) - { - // step the drive head according to stepper magnet changes - int delta = DriveArmStepDelta[_trackNumber & 0x3][phaseState]; - if (delta != 0) - { - int newTrackNumber = MathHelpers.Clamp(_trackNumber + delta, 0, TrackNumberMax); - if (newTrackNumber != _trackNumber) - { - FlushTrack(); - _trackNumber = newTrackNumber; - _trackOffset = 0; - _trackLoaded = false; - } - } - } + private static int Clamp(int value, int min, int max) + { + return value < min ? min : value > max ? max : value; + } - public int Read() - { - if (LoadTrack()) - { - int data = _trackData[_trackOffset++]; - if (_trackOffset >= Disk525.TrackSize) - { - _trackOffset = 0; - } + internal void ApplyPhaseChange(int phaseState) + { + // step the drive head according to stepper magnet changes + int delta = _driveArmStepDelta[_trackNumber & 0x3][phaseState]; + if (delta != 0) + { + int newTrackNumber = Clamp(_trackNumber + delta, 0, TrackNumberMax); + if (newTrackNumber != _trackNumber) + { + FlushTrack(); + _trackNumber = newTrackNumber; + _trackOffset = 0; + _trackLoaded = false; + } + } + } - Machine.DriveLight = true; - return data; - } + internal int Read() + { + if (LoadTrack()) + { + int data = _trackData[_trackOffset++]; + if (_trackOffset >= Disk525.TrackSize) + { + _trackOffset = 0; + } + + _diskController.DriveLight = true; + return data; + } return 0x80; - // TODO: WTF was this - //return _random.Next(0x01, 0xFF); - } + } - public void Write(int data) - { - if (LoadTrack()) - { - _trackChanged = true; - _trackData[_trackOffset++] = (byte)data; - if (_trackOffset >= Disk525.TrackSize) - { - _trackOffset = 0; - } + internal void Write(int data) + { + if (LoadTrack()) + { + _trackChanged = true; + _trackData[_trackOffset++] = (byte)data; + if (_trackOffset >= Disk525.TrackSize) + { + _trackOffset = 0; + } - Machine.DriveLight = true; - } - } + _diskController.DriveLight = true; + } + } - private bool LoadTrack() - { - if (!_trackLoaded && (_disk != null)) - { - _disk.ReadTrack(_trackNumber, 0, _trackData); - _trackLoaded = true; - } + private bool LoadTrack() + { + if (!_trackLoaded && (_disk != null)) + { + _disk.ReadTrack(_trackNumber, 0, _trackData); + _trackLoaded = true; + } - return _trackLoaded; - } + return _trackLoaded; + } - public void FlushTrack() - { - if (_trackChanged) - { - _disk.WriteTrack(_trackNumber, 0, _trackData); - _trackChanged = false; - } - } + internal void FlushTrack() + { + if (_trackChanged) + { + _disk.WriteTrack(_trackNumber, 0, _trackData); + _trackChanged = false; + } + } - [Newtonsoft.Json.JsonIgnore] - public bool IsWriteProtected { get { return _disk.IsWriteProtected; } } + [JsonIgnore] + public bool IsWriteProtected => _disk.IsWriteProtected; - private const int TrackNumberMax = 0x44; + private const int TrackNumberMax = 0x44; - private const int PhaseCount = 4; + private const int PhaseCount = 4; - private int[][] DriveArmStepDelta = new int[PhaseCount][]; + // ReSharper disable once FieldCanBeMadeReadOnly.Local + private int[][] _driveArmStepDelta = new int[PhaseCount][]; - private bool _trackLoaded; - private bool _trackChanged; - private int _trackNumber; - private int _trackOffset; - private byte[] _trackData = new byte[Disk525.TrackSize]; - private Disk525 _disk; - } + private bool _trackLoaded; + private bool _trackChanged; + private int _trackNumber; + private int _trackOffset; + + // ReSharper disable once FieldCanBeMadeReadOnly.Local + private byte[] _trackData = new byte[Disk525.TrackSize]; + + private Disk525 _disk; + } } diff --git a/ExternalCoreProjects/Virtu/DiskNib.cs b/ExternalCoreProjects/Virtu/DiskNib.cs index e3c0db90c1..301513e692 100644 --- a/ExternalCoreProjects/Virtu/DiskNib.cs +++ b/ExternalCoreProjects/Virtu/DiskNib.cs @@ -1,36 +1,25 @@ using System; -using System.IO; -using Jellyfish.Library; namespace Jellyfish.Virtu { - public sealed class DiskNib : Disk525 - { + internal sealed class DiskNib : Disk525 + { + // ReSharper disable once UnusedMember.Global public DiskNib() { } - public DiskNib(string name, byte[] data, bool isWriteProtected) : - base(name, data, isWriteProtected) - { - } - public DiskNib(string name, Stream stream, bool isWriteProtected) : - base(name, new byte[TrackCount * TrackSize], isWriteProtected) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } + public DiskNib(byte[] data, bool isWriteProtected) + : base(data, isWriteProtected) + { + } - stream.ReadBlock(Data); - } + public override void ReadTrack(int number, int fraction, byte[] buffer) + { + Buffer.BlockCopy(Data, (number / 2) * TrackSize, buffer, 0, TrackSize); + } - public override void ReadTrack(int number, int fraction, byte[] buffer) - { - Buffer.BlockCopy(Data, (number / 2) * TrackSize, buffer, 0, TrackSize); - } - - public override void WriteTrack(int number, int fraction, byte[] buffer) - { - Buffer.BlockCopy(buffer, 0, Data, (number / 2) * TrackSize, TrackSize); - } - } + public override void WriteTrack(int number, int fraction, byte[] buffer) + { + Buffer.BlockCopy(buffer, 0, Data, (number / 2) * TrackSize, TrackSize); + } + } } diff --git a/ExternalCoreProjects/Virtu/GamePort.cs b/ExternalCoreProjects/Virtu/GamePort.cs deleted file mode 100644 index 0f78c4d50d..0000000000 --- a/ExternalCoreProjects/Virtu/GamePort.cs +++ /dev/null @@ -1,256 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using Jellyfish.Library; -using Jellyfish.Virtu.Services; - -namespace Jellyfish.Virtu -{ - public sealed class GamePort : MachineComponent - { - // TODO: ressurect this - public bool ReadButton0() { return Keyboard.WhiteAppleDown; } - public bool ReadButton1() { return Keyboard.BlackAppleDown; } - public bool ReadButton2() { return false; } - - public bool Paddle0Strobe { get { return false; } } - public bool Paddle1Strobe { get { return false; } } - public bool Paddle2Strobe { get { return false; } } - public bool Paddle3Strobe { get { return false; } } - - public void TriggerTimers() { } - - public GamePort() { } - public GamePort(Machine machine) : - base(machine) - { - /* - _resetPaddle0StrobeEvent = ResetPaddle0StrobeEvent; // cache delegates; avoids garbage - _resetPaddle1StrobeEvent = ResetPaddle1StrobeEvent; - _resetPaddle2StrobeEvent = ResetPaddle2StrobeEvent; - _resetPaddle3StrobeEvent = ResetPaddle3StrobeEvent; - */ - } - - /* - public override void Initialize() - { - _keyboardService = Machine.Services.GetService(); - _gamePortService = Machine.Services.GetService(); - - JoystickDeadZone = 0.4f; - - InvertPaddles = true; // Raster Blaster - SwapPaddles = true; - Joystick0TouchX = 0.35f; - Joystick0TouchY = 0.6f; - Joystick0TouchWidth = 0.25f; - Joystick0TouchHeight = 0.4f; - Joystick0TouchRadius = 0.2f; - Joystick0TouchKeepLast = true; - Button0TouchX = 0; - Button0TouchY = 0; - Button0TouchWidth = 0.5f; - Button0TouchHeight = 1; - Button1TouchX = 0.5f; - Button1TouchY = 0; - Button1TouchWidth = 0.5f; - Button1TouchHeight = 1; - Button2TouchX = 0.75f; - Button2TouchY = 0; - Button2TouchWidth = 0.25f; - Button2TouchHeight = 0.25f; - Button2TouchOrder = 1; - } - - public bool ReadButton0() - { - return (_gamePortService.IsButton0Down || _keyboardService.IsOpenAppleKeyDown || - (UseKeyboard && (Button0Key > 0) && _keyboardService.IsKeyDown(Button0Key))); - } - - public bool ReadButton1() - { - return (_gamePortService.IsButton1Down || _keyboardService.IsCloseAppleKeyDown || - (UseKeyboard && (Button1Key > 0) && _keyboardService.IsKeyDown(Button1Key))); - } - - public bool ReadButton2() - { - return (_gamePortService.IsButton2Down || (UseShiftKeyMod && !_keyboardService.IsShiftKeyDown) || // Shift' [TN9] - (UseKeyboard && (Button2Key > 0) && _keyboardService.IsKeyDown(Button2Key))); - } - - [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] - public void TriggerTimers() - { - int paddle0 = _gamePortService.Paddle0; - int paddle1 = _gamePortService.Paddle1; - int paddle2 = _gamePortService.Paddle2; - int paddle3 = _gamePortService.Paddle3; - - if (UseKeyboard) // override - { - if (((Joystick0UpLeftKey > 0) && _keyboardService.IsKeyDown(Joystick0UpLeftKey)) || - ((Joystick0LeftKey > 0) && _keyboardService.IsKeyDown(Joystick0LeftKey)) || - ((Joystick0DownLeftKey > 0) && _keyboardService.IsKeyDown(Joystick0DownLeftKey))) - { - paddle0 -= PaddleScale; - } - if (((Joystick0UpRightKey > 0) && _keyboardService.IsKeyDown(Joystick0UpRightKey)) || - ((Joystick0RightKey > 0) && _keyboardService.IsKeyDown(Joystick0RightKey)) || - ((Joystick0DownRightKey > 0) && _keyboardService.IsKeyDown(Joystick0DownRightKey))) - { - paddle0 += PaddleScale; - } - if (((Joystick0UpLeftKey > 0) && _keyboardService.IsKeyDown(Joystick0UpLeftKey)) || - ((Joystick0UpKey > 0) && _keyboardService.IsKeyDown(Joystick0UpKey)) || - ((Joystick0UpRightKey > 0) && _keyboardService.IsKeyDown(Joystick0UpRightKey))) - { - paddle1 -= PaddleScale; - } - if (((Joystick0DownLeftKey > 0) && _keyboardService.IsKeyDown(Joystick0DownLeftKey)) || - ((Joystick0DownKey > 0) && _keyboardService.IsKeyDown(Joystick0DownKey)) || - ((Joystick0DownRightKey > 0) && _keyboardService.IsKeyDown(Joystick0DownRightKey))) - { - paddle1 += PaddleScale; - } - if (((Joystick1UpLeftKey > 0) && _keyboardService.IsKeyDown(Joystick1UpLeftKey)) || - ((Joystick1LeftKey > 0) && _keyboardService.IsKeyDown(Joystick1LeftKey)) || - ((Joystick1DownLeftKey > 0) && _keyboardService.IsKeyDown(Joystick1DownLeftKey))) - { - paddle2 -= PaddleScale; - } - if (((Joystick1UpRightKey > 0) && _keyboardService.IsKeyDown(Joystick1UpRightKey)) || - ((Joystick1RightKey > 0) && _keyboardService.IsKeyDown(Joystick1RightKey)) || - ((Joystick1DownRightKey > 0) && _keyboardService.IsKeyDown(Joystick1DownRightKey))) - { - paddle2 += PaddleScale; - } - if (((Joystick1UpLeftKey > 0) && _keyboardService.IsKeyDown(Joystick1UpLeftKey)) || - ((Joystick1UpKey > 0) && _keyboardService.IsKeyDown(Joystick1UpKey)) || - ((Joystick1UpRightKey > 0) && _keyboardService.IsKeyDown(Joystick1UpRightKey))) - { - paddle3 -= PaddleScale; - } - if (((Joystick1DownLeftKey > 0) && _keyboardService.IsKeyDown(Joystick1DownLeftKey)) || - ((Joystick1DownKey > 0) && _keyboardService.IsKeyDown(Joystick1DownKey)) || - ((Joystick1DownRightKey > 0) && _keyboardService.IsKeyDown(Joystick1DownRightKey))) - { - paddle3 += PaddleScale; - } - } - if (InvertPaddles) - { - paddle0 = 2 * PaddleScale - paddle0; - paddle1 = 2 * PaddleScale - paddle1; - paddle2 = 2 * PaddleScale - paddle2; - paddle3 = 2 * PaddleScale - paddle3; - } - - Paddle0Strobe = true; - Paddle1Strobe = true; - Paddle2Strobe = true; - Paddle3Strobe = true; - - Machine.Events.AddEvent(MathHelpers.ClampByte(SwapPaddles ? paddle1 : paddle0) * CyclesPerValue, _resetPaddle0StrobeEvent); // [7-29] - Machine.Events.AddEvent(MathHelpers.ClampByte(SwapPaddles ? paddle0 : paddle1) * CyclesPerValue, _resetPaddle1StrobeEvent); - Machine.Events.AddEvent(MathHelpers.ClampByte(SwapPaddles ? paddle3 : paddle2) * CyclesPerValue, _resetPaddle2StrobeEvent); - Machine.Events.AddEvent(MathHelpers.ClampByte(SwapPaddles ? paddle2 : paddle3) * CyclesPerValue, _resetPaddle3StrobeEvent); - } - - private void ResetPaddle0StrobeEvent() - { - Paddle0Strobe = false; - } - - private void ResetPaddle1StrobeEvent() - { - Paddle1Strobe = false; - } - - private void ResetPaddle2StrobeEvent() - { - Paddle2Strobe = false; - } - - private void ResetPaddle3StrobeEvent() - { - Paddle3Strobe = false; - } - - public const int PaddleScale = 128; - - public bool InvertPaddles { get; set; } - public bool SwapPaddles { get; set; } - public bool UseShiftKeyMod { get; set; } - public float JoystickDeadZone { get; set; } - - public bool UseKeyboard { get; set; } - public int Joystick0UpLeftKey { get; set; } - public int Joystick0UpKey { get; set; } - public int Joystick0UpRightKey { get; set; } - public int Joystick0LeftKey { get; set; } - public int Joystick0RightKey { get; set; } - public int Joystick0DownLeftKey { get; set; } - public int Joystick0DownKey { get; set; } - public int Joystick0DownRightKey { get; set; } - public int Joystick1UpLeftKey { get; set; } - public int Joystick1UpKey { get; set; } - public int Joystick1UpRightKey { get; set; } - public int Joystick1LeftKey { get; set; } - public int Joystick1RightKey { get; set; } - public int Joystick1DownLeftKey { get; set; } - public int Joystick1DownKey { get; set; } - public int Joystick1DownRightKey { get; set; } - public int Button0Key { get; set; } - public int Button1Key { get; set; } - public int Button2Key { get; set; } - - public bool UseTouch { get; set; } - public float Joystick0TouchX { get; set; } - public float Joystick0TouchY { get; set; } - public float Joystick0TouchWidth { get; set; } - public float Joystick0TouchHeight { get; set; } - public int Joystick0TouchOrder { get; set; } - public float Joystick0TouchRadius { get; set; } - public bool Joystick0TouchKeepLast { get; set; } - public float Joystick1TouchX { get; set; } - public float Joystick1TouchY { get; set; } - public float Joystick1TouchWidth { get; set; } - public float Joystick1TouchHeight { get; set; } - public int Joystick1TouchOrder { get; set; } - public float Joystick1TouchRadius { get; set; } - public bool Joystick1TouchKeepLast { get; set; } - public float Button0TouchX { get; set; } - public float Button0TouchY { get; set; } - public float Button0TouchWidth { get; set; } - public float Button0TouchHeight { get; set; } - public int Button0TouchOrder { get; set; } - public float Button1TouchX { get; set; } - public float Button1TouchY { get; set; } - public float Button1TouchWidth { get; set; } - public float Button1TouchHeight { get; set; } - public int Button1TouchOrder { get; set; } - public float Button2TouchX { get; set; } - public float Button2TouchY { get; set; } - public float Button2TouchWidth { get; set; } - public float Button2TouchHeight { get; set; } - public int Button2TouchOrder { get; set; } - - public bool Paddle0Strobe { get; private set; } - public bool Paddle1Strobe { get; private set; } - public bool Paddle2Strobe { get; private set; } - public bool Paddle3Strobe { get; private set; } - - private const int CyclesPerValue = 11; - - private Action _resetPaddle0StrobeEvent; - private Action _resetPaddle1StrobeEvent; - private Action _resetPaddle2StrobeEvent; - private Action _resetPaddle3StrobeEvent; - - private KeyboardService _keyboardService; - private GamePortService _gamePortService;*/ - } -} diff --git a/ExternalCoreProjects/Virtu/ICassette.cs b/ExternalCoreProjects/Virtu/ICassette.cs new file mode 100644 index 0000000000..773c0fda69 --- /dev/null +++ b/ExternalCoreProjects/Virtu/ICassette.cs @@ -0,0 +1,8 @@ +namespace Jellyfish.Virtu +{ + public interface ICassette + { + bool ReadInput(); + void ToggleOutput(); + } +} diff --git a/ExternalCoreProjects/Virtu/IGamePort.cs b/ExternalCoreProjects/Virtu/IGamePort.cs new file mode 100644 index 0000000000..8c31d5e179 --- /dev/null +++ b/ExternalCoreProjects/Virtu/IGamePort.cs @@ -0,0 +1,16 @@ +namespace Jellyfish.Virtu +{ + public interface IGamePort + { + bool ReadButton0(); + bool ReadButton1(); + bool ReadButton2(); + + bool Paddle0Strobe { get; } + bool Paddle1Strobe { get; } + bool Paddle2Strobe { get; } + bool Paddle3Strobe { get; } + + void TriggerTimers(); + } +} diff --git a/ExternalCoreProjects/Virtu/IPeripheralCard.cs b/ExternalCoreProjects/Virtu/IPeripheralCard.cs new file mode 100644 index 0000000000..ed40261384 --- /dev/null +++ b/ExternalCoreProjects/Virtu/IPeripheralCard.cs @@ -0,0 +1,23 @@ +namespace Jellyfish.Virtu +{ + public interface IPeripheralCard + { + // read Device Select' address $C0nX; n = slot number + 8 + int ReadIoRegionC0C0(int address); + + // read I/O Select' address $CsXX; s = slot number + int ReadIoRegionC1C7(int address); + + // read I/O Strobe' address $C800-$CFFF + int ReadIoRegionC8CF(int address); + + // write Device Select' address $C0nX; n = slot number + 8 + void WriteIoRegionC0C0(int address, int data); + + // write I/O Select' address $CsXX; s = slot number + void WriteIoRegionC1C7(int address, int data); + + // write I/O Strobe' address $C800-$CFFF + void WriteIoRegionC8CF(int address, int data); + } +} diff --git a/ExternalCoreProjects/Virtu/Keyboard.cs b/ExternalCoreProjects/Virtu/Keyboard.cs index 8adeb1fcac..7802087208 100644 --- a/ExternalCoreProjects/Virtu/Keyboard.cs +++ b/ExternalCoreProjects/Virtu/Keyboard.cs @@ -1,7 +1,5 @@ using System; -using System.IO; using System.Collections.Generic; -using Jellyfish.Virtu.Services; using System.ComponentModel; using System.Linq; @@ -143,10 +141,28 @@ namespace Jellyfish.Virtu Reset = 2305843009213693952UL, } + public sealed class Keyboard + { + // ReSharper disable once UnusedMember.Global + public Keyboard() { } - public sealed class Keyboard : MachineComponent - { - private static readonly uint[] KeyAsciiData = new uint[] + static Keyboard() + { + for (int i = 0; i < 62; i++) + { + // http://stackoverflow.com/questions/2650080/how-to-get-c-sharp-enum-description-from-value + Keys value = (Keys)(1UL << i); + var fi = typeof(Keys).GetField(value.ToString()); + var attr = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false); + string name = attr[0].Description; + DescriptionsToKeys[name] = value; + } + } + + // ReSharper disable once UnusedMember.Global + public static IEnumerable GetKeyNames() => DescriptionsToKeys.Keys.ToList(); + + private static readonly uint[] KeyAsciiData = { // https://archive.org/stream/Apple_IIe_Technical_Reference_Manual#page/n47/mode/2up // 0xNNCCSSBB normal, control, shift both @@ -217,27 +233,9 @@ namespace Jellyfish.Virtu return (int)(KeyAsciiData[key] >> s & 0x7f); } + // ReSharper disable once InconsistentNaming private static Dictionary DescriptionsToKeys = new Dictionary(); - static Keyboard() - { - for (int i = 0; i < 62; i++) - { - // http://stackoverflow.com/questions/2650080/how-to-get-c-sharp-enum-description-from-value - Keys value = (Keys)(1UL << i); - var fi = typeof(Keys).GetField(value.ToString()); - var attr = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false); - string name = attr[0].Description; - DescriptionsToKeys[name] = value; - } - - } - - public static IEnumerable GetKeyNames() - { - return DescriptionsToKeys.Keys.ToList(); - } - private static Keys FromStrings(IEnumerable keynames) { Keys ret = 0; @@ -245,38 +243,23 @@ namespace Jellyfish.Virtu { ret |= DescriptionsToKeys[s]; } + return ret; } - public Keyboard() { } - public Keyboard(Machine machine) : - base(machine) - { - } - - public override void Initialize() - { - } - public static bool WhiteAppleDown; public static bool BlackAppleDown; /// /// Call this at 60hz with all of the currently pressed keys /// + // ReSharper disable once UnusedMember.Global public void SetKeys(IEnumerable keynames) { Keys keys = FromStrings(keynames); - if (keys.HasFlag(Keys.WhiteApple)) - WhiteAppleDown = true; - else - WhiteAppleDown = false; - - if (keys.HasFlag(Keys.BlackApple)) - BlackAppleDown = true; - else - BlackAppleDown = false; + WhiteAppleDown = keys.HasFlag(Keys.WhiteApple); + BlackAppleDown = keys.HasFlag(Keys.BlackApple); if (keys.HasFlag(Keys.Reset) && keys.HasFlag(Keys.Control)) { } // TODO: reset console @@ -284,11 +267,11 @@ namespace Jellyfish.Virtu bool shift = keys.HasFlag(Keys.Shift); bool caps = keys.HasFlag(Keys.CapsLock); - if (caps && !CurrentCapsLockState) // leading edge: toggle capslock + if (caps && !_currentCapsLockState) // leading edge: toggle CapsLock { CapsActive ^= true; } - CurrentCapsLockState = caps; + _currentCapsLockState = caps; shift ^= CapsActive; // work with only the first 56 real keys @@ -298,7 +281,7 @@ namespace Jellyfish.Virtu if (!IsAnyKeyDown) { - CurrentKeyPressed = -1; + _currentKeyPressed = -1; return; } @@ -306,76 +289,68 @@ namespace Jellyfish.Virtu // that would be somehow resolved by the scan pattern. we don't emulate that. // instead, just arbitrarily choose the lowest key in our list - + // BSF - int NewKeyPressed = 0; + int newKeyPressed = 0; while ((k & 1) == 0) { k >>= 1; - NewKeyPressed++; + newKeyPressed++; } - if (NewKeyPressed != CurrentKeyPressed) + if (newKeyPressed != _currentKeyPressed) { // strobe, start new repeat cycle Strobe = true; - Latch = KeyToAscii(NewKeyPressed, control, shift); - //if (Latch >= 0x20 && Latch < 0x7f) - // Console.WriteLine("Latch: {0:x2}, {1}", Latch, (char)Latch); - //else - // Console.WriteLine("Latch: {0:x2}", Latch); - FramesToRepeat = KeyRepeatStart; + Latch = KeyToAscii(newKeyPressed, control, shift); + _framesToRepeat = KeyRepeatStart; } else { // check for repeat - FramesToRepeat--; - if (FramesToRepeat == 0) + _framesToRepeat--; + if (_framesToRepeat == 0) { Strobe = true; - Latch = KeyToAscii(NewKeyPressed, control, shift); - //if (Latch >= 0x20 && Latch < 0x7f) - // Console.WriteLine("Latch: {0:x2}, {1}", Latch, (char)Latch); - //else - // Console.WriteLine("Latch: {0:x2}", Latch); - FramesToRepeat = KeyRepeatRate; + Latch = KeyToAscii(newKeyPressed, control, shift); + _framesToRepeat = KeyRepeatRate; } } - CurrentKeyPressed = NewKeyPressed; + _currentKeyPressed = newKeyPressed; } + public void ResetStrobe() + { + Strobe = false; + } - public void ResetStrobe() - { - Strobe = false; - } - /// /// true if any of the 56 basic keys are pressed /// public bool IsAnyKeyDown { get; private set; } + /// /// the currently latched key; 7 bits. /// public int Latch { get; private set; } - public bool Strobe { get; private set; } + public bool Strobe { get; private set; } /// /// true if caps lock is active /// - public bool CapsActive { get; private set; } + private bool CapsActive { get; set; } - private bool CurrentCapsLockState; + private bool _currentCapsLockState; /// /// 0-55, -1 = none /// - private int CurrentKeyPressed; + private int _currentKeyPressed; - private int FramesToRepeat; + private int _framesToRepeat; private const int KeyRepeatRate = 6; // 10hz private const int KeyRepeatStart = 40; // ~666ms? - } + } } diff --git a/ExternalCoreProjects/Virtu/Library/DisposableBase.cs b/ExternalCoreProjects/Virtu/Library/DisposableBase.cs deleted file mode 100644 index 5b9309e10c..0000000000 --- a/ExternalCoreProjects/Virtu/Library/DisposableBase.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; - -namespace Jellyfish.Library -{ - public abstract class DisposableBase : IDisposable - { - protected DisposableBase() - { - } - - ~DisposableBase() - { - Dispose(false); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - } - } -} diff --git a/ExternalCoreProjects/Virtu/Library/MathHelpers.cs b/ExternalCoreProjects/Virtu/Library/MathHelpers.cs deleted file mode 100644 index 3c318014d7..0000000000 --- a/ExternalCoreProjects/Virtu/Library/MathHelpers.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Jellyfish.Library -{ - public static class MathHelpers - { - public static int Clamp(int value, int min, int max) - { - return (value < min) ? min : (value > max) ? max : value; - } - - public static int ClampByte(int value) - { - return Clamp(value, byte.MinValue, byte.MaxValue); - } - } -} diff --git a/ExternalCoreProjects/Virtu/Library/StreamExtensions.cs b/ExternalCoreProjects/Virtu/Library/StreamExtensions.cs deleted file mode 100644 index 634fbe52f1..0000000000 --- a/ExternalCoreProjects/Virtu/Library/StreamExtensions.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.IO; - -namespace Jellyfish.Library -{ - public static class StreamExtensions - { - public static int ReadBlock(this Stream stream, byte[] buffer, int offset, ref int count) - { - int read = ReadBlock(stream, buffer, offset, count, count); - count -= read; - return read; - } - - public static int ReadBlock(this Stream stream, byte[] buffer, int offset = 0, int count = int.MaxValue, int minCount = int.MaxValue) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - - count = Math.Min(count, buffer.Length - offset); - minCount = Math.Min(minCount, buffer.Length - offset); - - int total = 0; - int read; - do - { - total += read = stream.Read(buffer, offset + total, count - total); - } - while ((read > 0) && (total < count)); - - if (total < minCount) - { - throw new EndOfStreamException(); - } - - return total; - } - - public static int ReadWord(this Stream stream, bool optional = false) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - int lowByte = stream.ReadByte(); - int highByte = stream.ReadByte(); - int word = lowByte | (highByte << 8); - if ((word < 0) && !optional) - { - throw new EndOfStreamException(); - } - - return word; - } - - public static void SkipBlock(this Stream stream, int count) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - if (stream.CanSeek) - { - stream.Seek(count, SeekOrigin.Current); - } - else - { - int total = 0; - int read; - do - { - total += read = stream.Read(_skipBuffer, 0, Math.Min(count - total, SkipBufferSize)); - } - while ((read > 0) && (total < count)); - } - } - - private const int SkipBufferSize = 1024; - - private static byte[] _skipBuffer = new byte[SkipBufferSize]; - } -} diff --git a/ExternalCoreProjects/Virtu/Machine.cs b/ExternalCoreProjects/Virtu/Machine.cs deleted file mode 100644 index c8ed15e4ca..0000000000 --- a/ExternalCoreProjects/Virtu/Machine.cs +++ /dev/null @@ -1,216 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; -using System.Threading; -using Jellyfish.Virtu.Services; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using Newtonsoft.Json.Serialization; - -namespace Jellyfish.Virtu -{ - public sealed class Machine : IDisposable - { - /// - /// for deserialization only!! - /// - public Machine() { } - - public Machine(byte[] appleIIe, byte[] diskIIRom) - { - Events = new MachineEvents(); - - Cpu = new Cpu(this); - Memory = new Memory(this, appleIIe); - Keyboard = new Keyboard(this); - GamePort = new GamePort(this); - Cassette = new Cassette(this); - Speaker = new Speaker(this); - Video = new Video(this); - NoSlotClock = new NoSlotClock(this); - - var emptySlot = new PeripheralCard(this); - Slot1 = emptySlot; - Slot2 = emptySlot; - Slot3 = emptySlot; - Slot4 = emptySlot; - Slot5 = emptySlot; - Slot6 = new DiskIIController(this, diskIIRom); - Slot7 = emptySlot; - - Slots = new List { null, Slot1, Slot2, Slot3, Slot4, Slot5, Slot6, Slot7 }; - Components = new List { Cpu, Memory, Keyboard, GamePort, Cassette, Speaker, Video, NoSlotClock, Slot1, Slot2, Slot3, Slot4, Slot5, Slot6, Slot7 }; - - BootDiskII = Slots.OfType().Last(); - } - - public void Dispose() - { - } - - public void Reset() - { - foreach (var component in Components) - { - DebugService.WriteMessage("Resetting machine '{0}'", component.GetType().Name); - component.Reset(); - //DebugService.WriteMessage("Reset machine '{0}'", component.GetType().Name); - } - } - - - - private void Initialize() - { - foreach (var component in Components) - { - DebugService.WriteMessage("Initializing machine '{0}'", component.GetType().Name); - component.Initialize(); - //DebugService.WriteMessage("Initialized machine '{0}'", component.GetType().Name); - } - } - - private void Uninitialize() - { - foreach (var component in Components) - { - DebugService.WriteMessage("Uninitializing machine '{0}'", component.GetType().Name); - component.Uninitialize(); - //DebugService.WriteMessage("Uninitialized machine '{0}'", component.GetType().Name); - } - } - - public void BizInitialize() - { - Initialize(); - Reset(); - } - - public void BizFrameAdvance(IEnumerable buttons) - { - Lagged = true; - DriveLight = false; - - Keyboard.SetKeys(buttons); - - //frame begins at vsync.. beginning of vblank - while (Video.IsVBlank) - { - /* - var sb = new System.Text.StringBuilder(); - sb.AppendFormat("{0} ", Cpu); - for (int i = 0; i < 256; i++) - sb.AppendFormat("{0:x2} ", Memory.Read(i)); - tw.WriteLine(sb.ToString());*/ - Events.HandleEvents(Cpu.Execute()); - } - //now, while not vblank, we're in a frame - while (!Video.IsVBlank) - { - /* - var sb = new System.Text.StringBuilder(); - sb.AppendFormat("{0} ", Cpu); - for (int i = 0; i < 256; i++) - sb.AppendFormat("{0:x2} ", Memory.Read(i)); - tw.WriteLine(sb.ToString()); */ - - Events.HandleEvents(Cpu.Execute()); - } - } - - public void BizShutdown() - { - Uninitialize(); - } - - private static JsonSerializer CreateSerializer() - { - // TODO: converters could be cached for speedup - - var ser = new JsonSerializer - { - TypeNameHandling = TypeNameHandling.Auto, - PreserveReferencesHandling = PreserveReferencesHandling.All, // leaving out Array is a very important problem, and means that we can't rely on a directly shared array to work. - ReferenceLoopHandling = ReferenceLoopHandling.Serialize, - }; - - ser.Converters.Add(new TypeTypeConverter(new[] - { - // all expected Types to convert are either in this assembly or mscorlib - typeof(Machine).Assembly, - typeof(object).Assembly - })); - - ser.Converters.Add(new DelegateConverter()); - ser.Converters.Add(new ArrayConverter()); - - var cr = new DefaultContractResolver(); - cr.DefaultMembersSearchFlags |= System.Reflection.BindingFlags.NonPublic; - ser.ContractResolver = cr; - - return ser; - } - - public void Serialize(JsonWriter w) - { - CreateSerializer().Serialize(w, this); - } - - public static Machine Deserialize(JsonReader r) - { - return CreateSerializer().Deserialize(r); - } - - public const string Version = "0.9.4.0"; - - public MachineEvents Events { get; private set; } - - public Cpu Cpu { get; private set; } - public Memory Memory { get; private set; } - public Keyboard Keyboard { get; private set; } - public GamePort GamePort { get; private set; } - public Cassette Cassette { get; private set; } - public Speaker Speaker { get; private set; } - public Video Video { get; private set; } - public NoSlotClock NoSlotClock { get; private set; } - - public PeripheralCard Slot1 { get; private set; } - public PeripheralCard Slot2 { get; private set; } - public PeripheralCard Slot3 { get; private set; } - public PeripheralCard Slot4 { get; private set; } - public PeripheralCard Slot5 { get; private set; } - public PeripheralCard Slot6 { get; private set; } - public PeripheralCard Slot7 { get; private set; } - - public IList Slots { get; private set; } - public IList Components { get; private set; } - - public DiskIIController BootDiskII { get; private set; } - - public bool Lagged { get; set; } - public bool DriveLight { get; set; } - - public IDictionary GetCpuFlagsAndRegisters() - { - return new Dictionary - { - { "A", Cpu.RA }, - { "X", Cpu.RX }, - { "Y", Cpu.RY }, - { "S", Cpu.RS }, - { "PC", Cpu.RPC }, - { "Flag C", Cpu.FlagC ? 1 : 0 }, - { "Flag Z", Cpu.FlagZ ? 1 : 0 }, - { "Flag I", Cpu.FlagI ? 1 : 0 }, - { "Flag D", Cpu.FlagD ? 1 : 0 }, - { "Flag B", Cpu.FlagB ? 1 : 0 }, - { "Flag V", Cpu.FlagV ? 1 : 0 }, - { "Flag N", Cpu.FlagN ? 1 : 0 }, - { "Flag T", Cpu.FlagT ? 1 : 0 } - }; - } - } -} diff --git a/ExternalCoreProjects/Virtu/MachineComponent.cs b/ExternalCoreProjects/Virtu/MachineComponent.cs deleted file mode 100644 index cd6d407e6c..0000000000 --- a/ExternalCoreProjects/Virtu/MachineComponent.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.IO; -using Jellyfish.Library; -using Jellyfish.Virtu.Services; - -namespace Jellyfish.Virtu -{ - public abstract class MachineComponent - { - public MachineComponent() { } - protected MachineComponent(Machine machine) - { - _machine = machine; - } - - public virtual void Initialize() - { - } - - public virtual void Reset() - { - } - - public virtual void Uninitialize() - { - } - - [Newtonsoft.Json.JsonIgnore] - private Machine _machine; - - public Machine Machine - { - get { return _machine; } - set { _machine = value; } - } - } -} diff --git a/ExternalCoreProjects/Virtu/MachineEvents.cs b/ExternalCoreProjects/Virtu/MachineEvents.cs index 8cd705a691..588b71c50b 100644 --- a/ExternalCoreProjects/Virtu/MachineEvents.cs +++ b/ExternalCoreProjects/Virtu/MachineEvents.cs @@ -1,124 +1,114 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Linq; namespace Jellyfish.Virtu { - public sealed class MachineEvent - { - public MachineEvent(int delta, Action action) - { - Delta = delta; - Action = action; - } + internal sealed class MachineEvent + { + public MachineEvent(int delta, Action action) + { + Delta = delta; + Action = action; + } - public override string ToString() - { - return string.Format(CultureInfo.InvariantCulture, "Delta = {0} Action = {{{1}.{2}}}", Delta, Action.Method.DeclaringType.Name, Action.Method.Name); - } + public override string ToString() + { + return string.Format(CultureInfo.InvariantCulture, "Delta = {0} Action = {{{1}.{2}}}", Delta, Action.Method.DeclaringType?.Name, Action.Method.Name); + } - public int Delta { get; set; } - public Action Action { get; set; } - } + public int Delta { get; set; } + public Action Action { get; set; } + } - public sealed class MachineEvents - { - public void AddEvent(int delta, Action action) - { - //Console.WriteLine("+{0} @ {1}", action.Method.Name, delta); + public sealed class MachineEvents + { + public void AddEvent(int delta, Action action) + { + var node = _used.First; + for (; node != null; node = node.Next) + { + if (delta < node.Value.Delta) + { + node.Value.Delta -= delta; + break; + } + if (node.Value.Delta > 0) + { + delta -= node.Value.Delta; + } + } - var node = _used.First; - for (; node != null; node = node.Next) - { - if (delta < node.Value.Delta) - { - node.Value.Delta -= delta; - break; - } - if (node.Value.Delta > 0) - { - delta -= node.Value.Delta; - } - } + var newNode = _free.First; + if (newNode != null) + { + _free.RemoveFirst(); + newNode.Value.Delta = delta; + newNode.Value.Action = action; + } + else + { + newNode = new LinkedListNode(new MachineEvent(delta, action)); + } - var newNode = _free.First; - if (newNode != null) - { - _free.RemoveFirst(); - newNode.Value.Delta = delta; - newNode.Value.Action = action; - } - else - { - newNode = new LinkedListNode(new MachineEvent(delta, action)); - } + if (node != null) + { + _used.AddBefore(node, newNode); + } + else + { + _used.AddLast(newNode); + } + } - if (node != null) - { - _used.AddBefore(node, newNode); - } - else - { - _used.AddLast(newNode); - } - } + public int FindEvent(Action action) + { + int delta = 0; - public int FindEvent(Action action) - { - int delta = 0; - - for (var node = _used.First; node != null; node = node.Next) - { - delta += node.Value.Delta; + for (var node = _used.First; node != null; node = node.Next) + { + delta += node.Value.Delta; var other = node.Value.Action; - if (other.Method == action.Method && other.Target == action.Target) + if (other.Method == action.Method && other.Target == action.Target) { - //Console.WriteLine("={0} @ {1}", action.Method.Name, delta); return delta; } + } - // our delegate serializer doesn't preserve reference equality - //if (object.ReferenceEquals(node.Value.Action, action)) // assumes delegate cached - //{ - // return delta; - //} - } - - //Console.WriteLine("=???? @ 0"); return 0; - } + } - public void HandleEvents(int delta) - { - //Console.WriteLine("[{0}]", delta); + // ReSharper disable once UnusedMember.Global + public void HandleEvents(int delta) + { + var node = _used.First; + node.Value.Delta -= delta; - var node = _used.First; - node.Value.Delta -= delta; - - while (node.Value.Delta <= 0) - { - //Console.WriteLine("!{0} @ {1}", node.Value.Action.Method.Name, node.Value.Delta); + while (node.Value.Delta <= 0) + { node.Value.Action(); - RemoveEvent(node); - node = _used.First; - } - } + RemoveEvent(node); + node = _used.First; + } + } - private void RemoveEvent(LinkedListNode node) - { - if (node.Next != null) - { - node.Next.Value.Delta += node.Value.Delta; - } + private void RemoveEvent(LinkedListNode node) + { + if (node.Next != null) + { + node.Next.Value.Delta += node.Value.Delta; + } - _used.Remove(node); - _free.AddFirst(node); // cache node; avoids garbage - } + _used.Remove(node); + _free.AddFirst(node); // cache node; avoids garbage + } - private LinkedList _used = new LinkedList(); - private LinkedList _free = new LinkedList(); - } + // ReSharper disable once FieldCanBeMadeReadOnly.Local + private LinkedList _used = new LinkedList(); + + // ReSharper disable once FieldCanBeMadeReadOnly.Local + private LinkedList _free = new LinkedList(); + } } diff --git a/ExternalCoreProjects/Virtu/Memory.Data.cs b/ExternalCoreProjects/Virtu/Memory.Data.cs new file mode 100644 index 0000000000..b41b6af390 --- /dev/null +++ b/ExternalCoreProjects/Virtu/Memory.Data.cs @@ -0,0 +1,102 @@ +using Newtonsoft.Json; +using System; + +namespace Jellyfish.Virtu +{ + public partial class Memory + { + private const int BankMain = 0; + private const int BankAux = 1; + + private const int RegionCount = 12; + + private const int Region0001 = 0; + private const int Region02BF = 1; + private const int Region0407 = 2; + private const int Region080B = 3; + private const int Region203F = 4; + private const int Region405F = 5; + private const int RegionC0C0 = 6; + private const int RegionC1C7 = 7; + private const int RegionC3C3 = 8; + private const int RegionC8CF = 9; + private const int RegionD0DF = 10; + private const int RegionE0FF = 11; + + private static readonly int[] RegionBaseAddress = + { + 0x0000, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0xC000, 0xC100, 0xC100, 0xC100, 0xD000, 0xE000 + }; + + private static readonly int[] PageRegion = + { + Region0001, Region0001, Region02BF, Region02BF, Region0407, Region0407, Region0407, Region0407, + Region080B, Region080B, Region080B, Region080B, Region02BF, Region02BF, Region02BF, Region02BF, + Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, + Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, + Region203F, Region203F, Region203F, Region203F, Region203F, Region203F, Region203F, Region203F, + Region203F, Region203F, Region203F, Region203F, Region203F, Region203F, Region203F, Region203F, + Region203F, Region203F, Region203F, Region203F, Region203F, Region203F, Region203F, Region203F, + Region203F, Region203F, Region203F, Region203F, Region203F, Region203F, Region203F, Region203F, + Region405F, Region405F, Region405F, Region405F, Region405F, Region405F, Region405F, Region405F, + Region405F, Region405F, Region405F, Region405F, Region405F, Region405F, Region405F, Region405F, + Region405F, Region405F, Region405F, Region405F, Region405F, Region405F, Region405F, Region405F, + Region405F, Region405F, Region405F, Region405F, Region405F, Region405F, Region405F, Region405F, + Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, + Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, + Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, + Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, + Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, + Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, + Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, + Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, + Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, + Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, + Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, + Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, + RegionC0C0, RegionC1C7, RegionC1C7, RegionC3C3, RegionC1C7, RegionC1C7, RegionC1C7, RegionC1C7, + RegionC8CF, RegionC8CF, RegionC8CF, RegionC8CF, RegionC8CF, RegionC8CF, RegionC8CF, RegionC8CF, + RegionD0DF, RegionD0DF, RegionD0DF, RegionD0DF, RegionD0DF, RegionD0DF, RegionD0DF, RegionD0DF, + RegionD0DF, RegionD0DF, RegionD0DF, RegionD0DF, RegionD0DF, RegionD0DF, RegionD0DF, RegionD0DF, + RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, + RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, + RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, + RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF + }; + + private const int State80Col = 0x000001; + private const int StateText = 0x000002; + private const int StateMixed = 0x000004; + private const int StateHires = 0x000008; + private const int StateDRes = 0x000010; + private const int State80Store = 0x000020; + private const int StateAltChrSet = 0x000040; + private const int StateAltZP = 0x000080; + private const int StateBank1 = 0x000100; + private const int StateHRamRd = 0x000200; + private const int StateHRamPreWrt = 0x000400; + private const int StateHRamWrt = 0x000800; + private const int StatePage2 = 0x001000; + private const int StateRamRd = 0x002000; + private const int StateRamWrt = 0x004000; + private const int StateSlotC3Rom = 0x008000; + private const int StateIntC8Rom = 0x010000; // [5-28] + private const int StateIntCXRom = 0x020000; + private const int StateAn0 = 0x040000; + private const int StateAn1 = 0x080000; + private const int StateAn2 = 0x100000; + private const int StateAn3 = 0x200000; + private const int StateVideo = State80Col | StateText | StateMixed | StateHires | StateDRes; + + private static readonly int[] StateVideoMode = + { + Video.Mode0, Video.Mode0, Video.Mode1, Video.Mode2, Video.Mode3, Video.Mode4, Video.Mode1, Video.Mode2, + Video.Mode5, Video.Mode5, Video.Mode1, Video.Mode2, Video.Mode6, Video.Mode7, Video.Mode1, Video.Mode2, + Video.Mode8, Video.Mode9, Video.Mode1, Video.Mode2, Video.ModeA, Video.ModeB, Video.Mode1, Video.Mode2, + Video.ModeC, Video.ModeD, Video.Mode1, Video.Mode2, Video.ModeE, Video.ModeF, Video.Mode1, Video.Mode2 + }; + + [JsonIgnore] + private Action[][][] WriteRamModeBankRegion; + } +} diff --git a/ExternalCoreProjects/Virtu/Memory.cs b/ExternalCoreProjects/Virtu/Memory.cs index 737ea48af3..94fda5d1fa 100644 --- a/ExternalCoreProjects/Virtu/Memory.cs +++ b/ExternalCoreProjects/Virtu/Memory.cs @@ -1,36 +1,154 @@ using System; +using System.Collections.Generic; using System.Linq; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.IO; -using Jellyfish.Library; -using Jellyfish.Virtu.Services; using Newtonsoft.Json; namespace Jellyfish.Virtu { - public enum MonitorType { Unknown, Standard, Enhanced }; + public enum MonitorType { Unknown, Standard, Enhanced } - public sealed partial class Memory : MachineComponent + public interface IMemoryBus { + // ReSharper disable once UnusedMemberInSuper.Global + bool Lagged { get; } + + int ReadRomRegionE0FF(int address); + int ReadRamMainRegion02BF(int address); + int ReadRamAuxRegion02BF(int address); + + int Read(int address); + int Peek(int address); + int ReadOpcode(int address); + int ReadZeroPage(int address); + void WriteZeroPage(int address, int data); + void Write(int address, int data); + + bool IsText { get; } + bool IsMixed { get; } + bool IsHires { get; } + bool IsVideoPage2 { get; } + bool IsCharSetAlternate { get; } + + int VideoMode { get; } + MonitorType Monitor { get; } + } + + public sealed partial class Memory : IMemoryBus + { + private IGamePort _gamePort; + private ICassette _cassette; + private IVideo _video; + private ISlotClock _noSlotClock; + + private IPeripheralCard _slot1; + private IPeripheralCard _slot2; + private IPeripheralCard _slot3; + private IPeripheralCard _slot4; + private IPeripheralCard _slot5; + private IPeripheralCard _slot7; + + // TODO: this shouldn't be in savestates! + // ReSharper disable once FieldCanBeMadeReadOnly.Local + private byte[] _appleIIe; + + // ReSharper disable once UnusedMember.Global public Memory() { InitializeWriteDelegates(); } - public Memory(Machine machine, byte[] appleIIe) : - base(machine) + // ReSharper disable once UnusedMember.Global + public Memory(byte[] appleIIe) { _appleIIe = appleIIe; InitializeWriteDelegates(); } + // ReSharper disable once UnusedMember.Global + public void Initialize( + Keyboard keyboard, + IGamePort gamePort, + ICassette cassette, + ISpeaker speaker, + IVideo video, + ISlotClock noSlotClock, + IPeripheralCard slot1, + IPeripheralCard slot2, + IPeripheralCard slot3, + IPeripheralCard slot4, + IPeripheralCard slot5, + IDiskIIController slot6, + IPeripheralCard slot7) + { + Keyboard = keyboard; + _gamePort = gamePort; + _cassette = cassette; + Speaker = speaker; + _video = video; + _noSlotClock = noSlotClock; + + _slot1 = slot1; + _slot2 = slot2; + _slot3 = slot3; + _slot4 = slot4; + _slot5 = slot5; + DiskIIController = slot6; + _slot7 = slot7; + + // TODO: this is a lazy and more complicated way to do this + _romInternalRegionC1CF = _appleIIe + .Skip(0x100) + .Take(_romInternalRegionC1CF.Length) + .ToArray(); + + _romRegionD0DF = _appleIIe + .Skip(0x100 + _romInternalRegionC1CF.Length) + .Take(_romRegionD0DF.Length) + .ToArray(); + + _romRegionE0FF = _appleIIe + .Skip(0x100 + _romInternalRegionC1CF.Length + _romRegionD0DF.Length) + .Take(_romRegionE0FF.Length) + .ToArray(); + + if (ReadRomRegionE0FF(0xFBB3) == 0x06 && ReadRomRegionE0FF(0xFBBF) == 0xC1) + { + Monitor = MonitorType.Standard; + } + else if (ReadRomRegionE0FF(0xFBB3) == 0x06 + && ReadRomRegionE0FF(0xFBBF) == 0x00 + && ReadRomRegionE0FF(0xFBC0) == 0xE0) + { + Monitor = MonitorType.Enhanced; + } + } + + public bool Lagged { get; set; } + + private IList Slots => new List + { + null, + _slot1, + _slot2, + _slot3, + _slot4, + _slot5, + DiskIIController, + _slot7 + }; + + public IDiskIIController DiskIIController { get; private set; } + + public Keyboard Keyboard { get; private set; } + + public ISpeaker Speaker { get; private set; } + private void InitializeWriteDelegates() { WriteRamModeBankRegion = new Action[Video.ModeCount][][]; for (int mode = 0; mode < Video.ModeCount; mode++) { - WriteRamModeBankRegion[mode] = new Action[BankCount][] + WriteRamModeBankRegion[mode] = new[] { new Action[RegionCount], new Action[RegionCount] }; @@ -99,44 +217,8 @@ namespace Jellyfish.Virtu _writeRomRegionD0FF = WriteRomRegionD0FF; } - private byte[] _appleIIe; - - public override void Initialize() - { - _keyboard = Machine.Keyboard; - _gamePort = Machine.GamePort; - _cassette = Machine.Cassette; - _speaker = Machine.Speaker; - _video = Machine.Video; - _noSlotClock = Machine.NoSlotClock; - - // TODO: this is a lazy and more compicated way to do this - _romInternalRegionC1CF = _appleIIe - .Skip(0x100) - .Take(_romInternalRegionC1CF.Length) - .ToArray(); - - _romRegionD0DF = _appleIIe - .Skip(0x100 + _romInternalRegionC1CF.Length) - .Take(_romRegionD0DF.Length) - .ToArray(); - - _romRegionE0FF = _appleIIe - .Skip(0x100 + _romInternalRegionC1CF.Length + _romRegionD0DF.Length) - .Take(_romRegionE0FF.Length) - .ToArray(); - - if ((ReadRomRegionE0FF(0xFBB3) == 0x06) && (ReadRomRegionE0FF(0xFBBF) == 0xC1)) - { - Monitor = MonitorType.Standard; - } - else if ((ReadRomRegionE0FF(0xFBB3) == 0x06) && (ReadRomRegionE0FF(0xFBBF) == 0x00) && (ReadRomRegionE0FF(0xFBC0) == 0xE0)) - { - Monitor = MonitorType.Enhanced; - } - } - - public override void Reset() // [7-3] + // ReSharper disable once UnusedMember.Global + public void Reset() // [7-3] { ResetState(State80Col | State80Store | StateAltChrSet | StateAltZP | StateBank1 | StateHRamRd | StateHRamPreWrt | StateHRamWrt | // HRamWrt' [5-23] StateHires | StatePage2 | StateRamRd | StateRamWrt | StateIntCXRom | StateSlotC3Rom | StateIntC8Rom | StateAn0 | StateAn1 | StateAn2 | StateAn3); @@ -148,88 +230,38 @@ namespace Jellyfish.Virtu MapRegionD0FF(); } - public void LoadPrg(Stream stream) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - int startAddress = stream.ReadWord(); - SetWarmEntry(startAddress); // assumes autostart monitor - Load(stream, startAddress); - } - - public void LoadXex(Stream stream) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - const int Marker = 0xFFFF; - int marker = stream.ReadWord(); // mandatory marker - if (marker != Marker) - { - throw new InvalidOperationException(string.Format("Marker ${0:X04} not found.", Marker)); - } - int startAddress = stream.ReadWord(); - int endAddress = stream.ReadWord(); - SetWarmEntry(startAddress); // assumes autostart monitor - - do - { - if (startAddress > endAddress) - { - throw new InvalidOperationException(string.Format("Invalid address range ${0:X04}-${1:X04}.", startAddress, endAddress)); - } - Load(stream, startAddress, endAddress - startAddress + 1); - marker = stream.ReadWord(optional: true); // optional marker - startAddress = (marker != Marker) ? marker : stream.ReadWord(optional: true); - endAddress = stream.ReadWord(optional: true); - } - while ((startAddress >= 0) && (endAddress >= 0)); - } - #region Core Read & Write + public int ReadOpcode(int address) { int region = PageRegion[address >> 8]; var result = ((address & 0xF000) != 0xC000) ? _regionRead[region][address - RegionBaseAddress[region]] : ReadIoRegionC0CF(address); - if (ExecuteCallback != null) - { - ExecuteCallback((uint)address); - } - if (ReadCallback != null) - { - ReadCallback((uint)address); - } + ExecuteCallback?.Invoke((uint)address); + ReadCallback?.Invoke((uint)address); return result; } public int Read(int address) { int region = PageRegion[address >> 8]; - var result = ((address & 0xF000) != 0xC000) ? _regionRead[region][address - RegionBaseAddress[region]] : ReadIoRegionC0CF(address); - if (ReadCallback != null) - { - ReadCallback((uint)address); - } + var result = (address & 0xF000) != 0xC000 + ? _regionRead[region][address - RegionBaseAddress[region]] + : ReadIoRegionC0CF(address); + ReadCallback?.Invoke((uint)address); return result; } public int Peek(int address) { int region = PageRegion[address >> 8]; - return ((address & 0xF000) != 0xC000) ? _regionRead[region][address - RegionBaseAddress[region]] : ReadIoRegionC0CF(address); + return (address & 0xF000) != 0xC000 + ? _regionRead[region][address - RegionBaseAddress[region]] + : ReadIoRegionC0CF(address); } public int ReadZeroPage(int address) { - if (ReadCallback != null) - { - ReadCallback((uint)address); - } + ReadCallback?.Invoke((uint)address); return _zeroPage[address]; } @@ -238,30 +270,22 @@ namespace Jellyfish.Virtu int region = PageRegion[address >> 8]; if (_writeRegion[region] == null) { - if (WriteCallback != null) - { - WriteCallback((uint)address); - } + WriteCallback?.Invoke((uint)address); _regionWrite[region][address - RegionBaseAddress[region]] = (byte)data; } else { - if (WriteCallback != null) - { - WriteCallback((uint)address); - } + WriteCallback?.Invoke((uint)address); _writeRegion[region](address, (byte)data); } } public void WriteZeroPage(int address, int data) { - if (WriteCallback != null) - { - WriteCallback((uint)address); - } + WriteCallback?.Invoke((uint)address); _zeroPage[address] = (byte)data; } + #endregion #region Read Actions @@ -300,13 +324,10 @@ namespace Jellyfish.Virtu private int ReadIoRegionC0C0(int address) { - if ((0xC000 <= address && address <= 0xC00F) || (0xC061 <= address && address <= 0xC067) || (0xC069 <= address && address <= 0xC06F)) + if (0xC000 <= address && address <= 0xC00F || 0xC061 <= address && address <= 0xC067 || 0xC069 <= address && address <= 0xC06F) { - Machine.Lagged = false; - if (InputCallback != null) - { - InputCallback(); - } + Lagged = false; + InputCallback?.Invoke(); } switch (address) @@ -327,56 +348,56 @@ namespace Jellyfish.Virtu case 0xC00D: case 0xC00E: case 0xC00F: - return SetBit7(_keyboard.Latch, _keyboard.Strobe); + return SetBit7(Keyboard.Latch, Keyboard.Strobe); case 0xC010: - _keyboard.ResetStrobe(); - return SetBit7(_keyboard.Latch, _keyboard.IsAnyKeyDown); + Keyboard.ResetStrobe(); + return SetBit7(Keyboard.Latch, Keyboard.IsAnyKeyDown); case 0xC011: - return SetBit7(_keyboard.Latch, !IsHighRamBank1); // Bank1' [5-22] + return SetBit7(Keyboard.Latch, !IsHighRamBank1); // Bank1' [5-22] case 0xC012: - return SetBit7(_keyboard.Latch, IsHighRamRead); + return SetBit7(Keyboard.Latch, IsHighRamRead); case 0xC013: - return SetBit7(_keyboard.Latch, IsRamReadAux); + return SetBit7(Keyboard.Latch, IsRamReadAux); case 0xC014: - return SetBit7(_keyboard.Latch, IsRamWriteAux); + return SetBit7(Keyboard.Latch, IsRamWriteAux); case 0xC015: - return SetBit7(_keyboard.Latch, IsRomC1CFInternal); + return SetBit7(Keyboard.Latch, IsRomC1CFInternal); case 0xC016: - return SetBit7(_keyboard.Latch, IsZeroPageAux); + return SetBit7(Keyboard.Latch, IsZeroPageAux); case 0xC017: - return SetBit7(_keyboard.Latch, IsRomC3C3External); + return SetBit7(Keyboard.Latch, IsRomC3C3External); case 0xC018: - return SetBit7(_keyboard.Latch, Is80Store); + return SetBit7(Keyboard.Latch, Is80Store); case 0xC019: - return SetBit7(_keyboard.Latch, !_video.IsVBlank); // Vbl' [7-5] + return SetBit7(Keyboard.Latch, !_video.IsVBlank); // Vbl' [7-5] case 0xC01A: - return SetBit7(_keyboard.Latch, IsText); + return SetBit7(Keyboard.Latch, IsText); case 0xC01B: - return SetBit7(_keyboard.Latch, IsMixed); + return SetBit7(Keyboard.Latch, IsMixed); case 0xC01C: - return SetBit7(_keyboard.Latch, IsPage2); + return SetBit7(Keyboard.Latch, IsPage2); case 0xC01D: - return SetBit7(_keyboard.Latch, IsHires); + return SetBit7(Keyboard.Latch, IsHires); case 0xC01E: - return SetBit7(_keyboard.Latch, IsCharSetAlternate); + return SetBit7(Keyboard.Latch, IsCharSetAlternate); case 0xC01F: - return SetBit7(_keyboard.Latch, Is80Columns); + return SetBit7(Keyboard.Latch, Is80Columns); case 0xC020: case 0xC021: @@ -413,7 +434,7 @@ namespace Jellyfish.Virtu case 0xC03D: case 0xC03E: case 0xC03F: - _speaker.ToggleOutput(); + Speaker.ToggleOutput(); break; case 0xC040: @@ -561,7 +582,7 @@ namespace Jellyfish.Virtu case 0xC09D: case 0xC09E: case 0xC09F: - return Machine.Slot1.ReadIoRegionC0C0(address); + return _slot1.ReadIoRegionC0C0(address); case 0xC0A0: case 0xC0A1: @@ -579,7 +600,7 @@ namespace Jellyfish.Virtu case 0xC0AD: case 0xC0AE: case 0xC0AF: - return Machine.Slot2.ReadIoRegionC0C0(address); + return _slot2.ReadIoRegionC0C0(address); case 0xC0B0: case 0xC0B1: @@ -597,7 +618,7 @@ namespace Jellyfish.Virtu case 0xC0BD: case 0xC0BE: case 0xC0BF: - return Machine.Slot3.ReadIoRegionC0C0(address); + return _slot3.ReadIoRegionC0C0(address); case 0xC0C0: case 0xC0C1: @@ -615,7 +636,7 @@ namespace Jellyfish.Virtu case 0xC0CD: case 0xC0CE: case 0xC0CF: - return Machine.Slot4.ReadIoRegionC0C0(address); + return _slot4.ReadIoRegionC0C0(address); case 0xC0D0: case 0xC0D1: @@ -633,7 +654,7 @@ namespace Jellyfish.Virtu case 0xC0DD: case 0xC0DE: case 0xC0DF: - return Machine.Slot5.ReadIoRegionC0C0(address); + return _slot5.ReadIoRegionC0C0(address); case 0xC0E0: case 0xC0E1: @@ -651,7 +672,7 @@ namespace Jellyfish.Virtu case 0xC0ED: case 0xC0EE: case 0xC0EF: - return Machine.Slot6.ReadIoRegionC0C0(address); + return DiskIIController.ReadIoRegionC0C0(address); case 0xC0F0: case 0xC0F1: @@ -669,10 +690,10 @@ namespace Jellyfish.Virtu case 0xC0FD: case 0xC0FE: case 0xC0FF: - return Machine.Slot7.ReadIoRegionC0C0(address); + return _slot7.ReadIoRegionC0C0(address); default: - throw new ArgumentOutOfRangeException("address"); + throw new ArgumentOutOfRangeException(nameof(address)); } return _video.ReadFloatingBus(); @@ -681,7 +702,7 @@ namespace Jellyfish.Virtu private int ReadIoRegionC1C7(int address) { _slotRegionC8CF = (address >> 8) & 0x07; - return IsRomC1CFInternal ? _romInternalRegionC1CF[address - 0xC100] : Machine.Slots[_slotRegionC8CF].ReadIoRegionC1C7(address); + return IsRomC1CFInternal ? _romInternalRegionC1CF[address - 0xC100] : Slots[_slotRegionC8CF].ReadIoRegionC1C7(address); } private int ReadIoRegionC3C3(int address) @@ -691,7 +712,10 @@ namespace Jellyfish.Virtu { SetRomC8CF(true); // $C3XX sets IntC8Rom; inhibits I/O Strobe' [5-28, 7-21] } - return (IsRomC1CFInternal || !IsRomC3C3External) ? _noSlotClock.Read(address, _romInternalRegionC1CF[address - 0xC100]) : Machine.Slot3.ReadIoRegionC1C7(address); + + return IsRomC1CFInternal || !IsRomC3C3External + ? _noSlotClock.Read(address, _romInternalRegionC1CF[address - 0xC100]) + : _slot3.ReadIoRegionC1C7(address); } private int ReadIoRegionC8CF(int address) @@ -700,7 +724,9 @@ namespace Jellyfish.Virtu { SetRomC8CF(false); // $CFFF resets IntC8Rom [5-28, 7-21] } - return (IsRomC1CFInternal || IsRomC8CFInternal) ? _noSlotClock.Read(address, _romInternalRegionC1CF[address - 0xC100]) : Machine.Slots[_slotRegionC8CF].ReadIoRegionC8CF(address); + return IsRomC1CFInternal || IsRomC8CFInternal + ? _noSlotClock.Read(address, _romInternalRegionC1CF[address - 0xC100]) + : Slots[_slotRegionC8CF].ReadIoRegionC8CF(address); } public int ReadRamMainRegion02BF(int address) @@ -782,7 +808,7 @@ namespace Jellyfish.Virtu case 0xC01D: case 0xC01E: case 0xC01F: - _keyboard.ResetStrobe(); + Keyboard.ResetStrobe(); break; case 0xC020: @@ -820,7 +846,7 @@ namespace Jellyfish.Virtu case 0xC03D: case 0xC03E: case 0xC03F: - _speaker.ToggleOutput(); + Speaker.ToggleOutput(); break; case 0xC040: @@ -954,7 +980,7 @@ namespace Jellyfish.Virtu case 0xC09D: case 0xC09E: case 0xC09F: - Machine.Slot1.WriteIoRegionC0C0(address, data); + _slot1.WriteIoRegionC0C0(address, data); break; case 0xC0A0: @@ -973,7 +999,7 @@ namespace Jellyfish.Virtu case 0xC0AD: case 0xC0AE: case 0xC0AF: - Machine.Slot2.WriteIoRegionC0C0(address, data); + _slot2.WriteIoRegionC0C0(address, data); break; case 0xC0B0: @@ -992,7 +1018,7 @@ namespace Jellyfish.Virtu case 0xC0BD: case 0xC0BE: case 0xC0BF: - Machine.Slot3.WriteIoRegionC0C0(address, data); + _slot3.WriteIoRegionC0C0(address, data); break; case 0xC0C0: @@ -1011,7 +1037,7 @@ namespace Jellyfish.Virtu case 0xC0CD: case 0xC0CE: case 0xC0CF: - Machine.Slot4.WriteIoRegionC0C0(address, data); + _slot4.WriteIoRegionC0C0(address, data); break; case 0xC0D0: @@ -1030,7 +1056,7 @@ namespace Jellyfish.Virtu case 0xC0DD: case 0xC0DE: case 0xC0DF: - Machine.Slot5.WriteIoRegionC0C0(address, data); + _slot5.WriteIoRegionC0C0(address, data); break; case 0xC0E0: @@ -1049,7 +1075,7 @@ namespace Jellyfish.Virtu case 0xC0ED: case 0xC0EE: case 0xC0EF: - Machine.Slot6.WriteIoRegionC0C0(address, data); + DiskIIController.WriteIoRegionC0C0(address, data); break; case 0xC0F0: @@ -1068,11 +1094,11 @@ namespace Jellyfish.Virtu case 0xC0FD: case 0xC0FE: case 0xC0FF: - Machine.Slot7.WriteIoRegionC0C0(address, data); + _slot7.WriteIoRegionC0C0(address, data); break; default: - throw new ArgumentOutOfRangeException("address"); + throw new ArgumentOutOfRangeException(nameof(address)); } } @@ -1081,7 +1107,7 @@ namespace Jellyfish.Virtu _slotRegionC8CF = (address >> 8) & 0x07; if (!IsRomC1CFInternal) { - Machine.Slots[_slotRegionC8CF].WriteIoRegionC1C7(address, data); + Slots[_slotRegionC8CF].WriteIoRegionC1C7(address, data); } } @@ -1098,7 +1124,7 @@ namespace Jellyfish.Virtu } else { - Machine.Slot3.WriteIoRegionC1C7(address, data); + _slot3.WriteIoRegionC1C7(address, data); } } @@ -1114,7 +1140,7 @@ namespace Jellyfish.Virtu } else { - Machine.Slots[_slotRegionC8CF].WriteIoRegionC8CF(address, data); + Slots[_slotRegionC8CF].WriteIoRegionC8CF(address, data); } } @@ -1993,84 +2019,22 @@ namespace Jellyfish.Virtu MapRegionD0FF(); } } + #endregion - private void Load(Stream stream, int startAddress) - { - DebugService.WriteMessage("Loading memory ${0:X04}", startAddress); - int address = startAddress; - if (address < 0x0200) - { - address += stream.ReadBlock(_ramMainRegion0001, address, minCount: 0); - } - if ((0x0200 <= address) && (address < 0xC000)) - { - address += stream.ReadBlock(_ramMainRegion02BF, address - 0x0200, minCount: 0); - } - if ((0xC000 <= address) && (address < 0xD000)) - { - address += stream.ReadBlock(_ramMainBank1RegionD0DF, address - 0xC000, minCount: 0); - } - if ((0xD000 <= address) && (address < 0xE000)) - { - address += stream.ReadBlock(_ramMainBank2RegionD0DF, address - 0xD000, minCount: 0); - } - if (0xE000 <= address) - { - address += stream.ReadBlock(_ramMainRegionE0FF, address - 0xE000, minCount: 0); - } - if (address > startAddress) - { - DebugService.WriteMessage("Loaded memory ${0:X04}-${1:X04} (${2:X04})", startAddress, address - 1, address - startAddress); - } - } - - private void Load(Stream stream, int startAddress, int length) - { - DebugService.WriteMessage("Loading memory ${0:X04}-${1:X04} (${2:X04})", startAddress, startAddress + length - 1, length); - int address = startAddress; - if (address < 0x0200) - { - address += stream.ReadBlock(_ramMainRegion0001, address, ref length); - } - if ((0x0200 <= address) && (address < 0xC000)) - { - address += stream.ReadBlock(_ramMainRegion02BF, address - 0x0200, ref length); - } - if ((0xC000 <= address) && (address < 0xD000)) - { - address += stream.ReadBlock(_ramMainBank1RegionD0DF, address - 0xC000, ref length); - } - if ((0xD000 <= address) && (address < 0xE000)) - { - address += stream.ReadBlock(_ramMainBank2RegionD0DF, address - 0xD000, ref length); - } - if (0xE000 <= address) - { - address += stream.ReadBlock(_ramMainRegionE0FF, address - 0xE000, ref length); - } - } - - private void SetWarmEntry(int address) - { - _ramMainRegion02BF[0x03F2 - 0x0200] = (byte)(address & 0xFF); - _ramMainRegion02BF[0x03F3 - 0x0200] = (byte)(address >> 8); - _ramMainRegion02BF[0x03F4 - 0x0200] = (byte)((address >> 8) ^ 0xA5); - } - private static int SetBit7(int data, bool value) { - return value ? (data | 0x80) : (data & 0x7F); + return value ? data | 0x80 : data & 0x7F; } private static bool TestBit(int data, int bit) { - return ((data & (0x1 << bit)) != 0x0); + return (data & (0x1 << bit)) != 0x0; } private static bool TestMask(int data, int mask, int value) { - return ((data & mask) == value); + return (data & mask) == value; } private void ResetState(int mask) @@ -2097,49 +2061,44 @@ namespace Jellyfish.Virtu private bool TestState(int mask) { - return ((_state & mask) != 0x0); + return (_state & mask) != 0x0; } private bool TestState(int mask, bool value) { - return (((_state & mask) != 0x0) == value); + return ((_state & mask) != 0x0) == value; } private bool TestState(int mask, int value) { - return ((_state & mask) == value); + return (_state & mask) == value; } - public bool Is80Columns { get { return TestState(State80Col); } } - public bool Is80Store { get { return TestState(State80Store); } } - public bool IsAnnunciator0 { get { return TestState(StateAn0); } } - public bool IsAnnunciator1 { get { return TestState(StateAn1); } } - public bool IsAnnunciator2 { get { return TestState(StateAn2); } } - public bool IsAnnunciator3 { get { return TestState(StateAn3); } } - public bool IsCharSetAlternate { get { return TestState(StateAltChrSet); } } - public bool IsDoubleRes { get { return TestState(StateDRes); } } - public bool IsHighRamAux { get { return IsZeroPageAux; } } - public bool IsHighRamBank1 { get { return TestState(StateBank1); } } - public bool IsHighRamRead { get { return TestState(StateHRamRd); } } - public bool IsHighRamWrite { get { return !TestState(StateHRamWrt); } } // HRamWrt' [5-23] - public bool IsHires { get { return TestState(StateHires); } } - public bool IsMixed { get { return TestState(StateMixed); } } - public bool IsPage2 { get { return TestState(StatePage2); } } - public bool IsRamReadAux { get { return TestState(StateRamRd); } } - public bool IsRamReadAuxRegion0407 { get { return Is80Store ? IsPage2 : IsRamReadAux; } } - public bool IsRamReadAuxRegion203F { get { return TestState(State80Store | StateHires, State80Store | StateHires) ? IsPage2 : IsRamReadAux; } } - public bool IsRamWriteAux { get { return TestState(StateRamWrt); } } - public bool IsRamWriteAuxRegion0407 { get { return Is80Store ? IsPage2 : IsRamWriteAux; } } - public bool IsRamWriteAuxRegion203F { get { return TestState(State80Store | StateHires, State80Store | StateHires) ? IsPage2 : IsRamWriteAux; } } - public bool IsRomC1CFInternal { get { return TestState(StateIntCXRom); } } - public bool IsRomC3C3External { get { return TestState(StateSlotC3Rom); } } - public bool IsRomC8CFInternal { get { return TestState(StateIntC8Rom); } } - public bool IsText { get { return TestState(StateText); } } - public bool IsVideoPage2 { get { return TestState(State80Store | StatePage2, StatePage2); } } // 80Store inhibits video Page2 [5-7, 8-19] - public bool IsZeroPageAux { get { return TestState(StateAltZP); } } + private bool Is80Columns => TestState(State80Col); + private bool Is80Store => TestState(State80Store); + public bool IsCharSetAlternate => TestState(StateAltChrSet); + private bool IsHighRamAux => IsZeroPageAux; + private bool IsHighRamBank1 => TestState(StateBank1); + private bool IsHighRamRead => TestState(StateHRamRd); + private bool IsHighRamWrite => !TestState(StateHRamWrt); // HRamWrt' [5-23] + public bool IsHires => TestState(StateHires); + public bool IsMixed => TestState(StateMixed); + internal bool IsPage2 => TestState(StatePage2); + internal bool IsRamReadAux => TestState(StateRamRd); + internal bool IsRamReadAuxRegion0407 => Is80Store ? IsPage2 : IsRamReadAux; + internal bool IsRamReadAuxRegion203F => TestState(State80Store | StateHires, State80Store | StateHires) ? IsPage2 : IsRamReadAux; + internal bool IsRamWriteAux => TestState(StateRamWrt); + internal bool IsRamWriteAuxRegion0407 => Is80Store ? IsPage2 : IsRamWriteAux; + internal bool IsRamWriteAuxRegion203F => TestState(State80Store | StateHires, State80Store | StateHires) ? IsPage2 : IsRamWriteAux; + internal bool IsRomC1CFInternal => TestState(StateIntCXRom); + internal bool IsRomC3C3External => TestState(StateSlotC3Rom); + internal bool IsRomC8CFInternal => TestState(StateIntC8Rom); + public bool IsText => TestState(StateText); + public bool IsVideoPage2 => TestState(State80Store | StatePage2, StatePage2); // 80Store inhibits video Page2 [5-7, 8-19] + internal bool IsZeroPageAux => TestState(StateAltZP); public MonitorType Monitor { get; private set; } - public int VideoMode { get { return StateVideoMode[_state & StateVideo]; } } + public int VideoMode => StateVideoMode[_state & StateVideo]; [JsonIgnore] private Action _writeIoRegionC0C0; @@ -2164,13 +2123,6 @@ namespace Jellyfish.Virtu [JsonIgnore] public Action InputCallback; - private Keyboard _keyboard; - private GamePort _gamePort; - private Cassette _cassette; - private Speaker _speaker; - private Video _video; - private NoSlotClock _noSlotClock; - private int _state; private int _slotRegionC8CF; diff --git a/ExternalCoreProjects/Virtu/MemoryData.cs b/ExternalCoreProjects/Virtu/MemoryData.cs deleted file mode 100644 index e8148a563d..0000000000 --- a/ExternalCoreProjects/Virtu/MemoryData.cs +++ /dev/null @@ -1,108 +0,0 @@ -using Newtonsoft.Json; -using System; - -namespace Jellyfish.Virtu -{ - public partial class Memory - { - private const int BankCount = 2; - - private const int BankMain = 0; - private const int BankAux = 1; - - private const int RegionCount = 12; - - private const int Region0001 = 0; - private const int Region02BF = 1; - private const int Region0407 = 2; - private const int Region080B = 3; - private const int Region203F = 4; - private const int Region405F = 5; - private const int RegionC0C0 = 6; - private const int RegionC1C7 = 7; - private const int RegionC3C3 = 8; - private const int RegionC8CF = 9; - private const int RegionD0DF = 10; - private const int RegionE0FF = 11; - - private static readonly int[] RegionBaseAddress = new int[RegionCount] - { - 0x0000, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0xC000, 0xC100, 0xC100, 0xC100, 0xD000, 0xE000 - }; - - private const int PageCount = 256; - - private static readonly int[] PageRegion = new int[PageCount] - { - Region0001, Region0001, Region02BF, Region02BF, Region0407, Region0407, Region0407, Region0407, - Region080B, Region080B, Region080B, Region080B, Region02BF, Region02BF, Region02BF, Region02BF, - Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, - Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, - Region203F, Region203F, Region203F, Region203F, Region203F, Region203F, Region203F, Region203F, - Region203F, Region203F, Region203F, Region203F, Region203F, Region203F, Region203F, Region203F, - Region203F, Region203F, Region203F, Region203F, Region203F, Region203F, Region203F, Region203F, - Region203F, Region203F, Region203F, Region203F, Region203F, Region203F, Region203F, Region203F, - Region405F, Region405F, Region405F, Region405F, Region405F, Region405F, Region405F, Region405F, - Region405F, Region405F, Region405F, Region405F, Region405F, Region405F, Region405F, Region405F, - Region405F, Region405F, Region405F, Region405F, Region405F, Region405F, Region405F, Region405F, - Region405F, Region405F, Region405F, Region405F, Region405F, Region405F, Region405F, Region405F, - Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, - Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, - Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, - Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, - Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, - Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, - Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, - Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, - Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, - Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, - Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, - Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, Region02BF, - RegionC0C0, RegionC1C7, RegionC1C7, RegionC3C3, RegionC1C7, RegionC1C7, RegionC1C7, RegionC1C7, - RegionC8CF, RegionC8CF, RegionC8CF, RegionC8CF, RegionC8CF, RegionC8CF, RegionC8CF, RegionC8CF, - RegionD0DF, RegionD0DF, RegionD0DF, RegionD0DF, RegionD0DF, RegionD0DF, RegionD0DF, RegionD0DF, - RegionD0DF, RegionD0DF, RegionD0DF, RegionD0DF, RegionD0DF, RegionD0DF, RegionD0DF, RegionD0DF, - RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, - RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, - RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, - RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF, RegionE0FF - }; - - private const int State80Col = 0x000001; - private const int StateText = 0x000002; - private const int StateMixed = 0x000004; - private const int StateHires = 0x000008; - private const int StateDRes = 0x000010; - private const int State80Store = 0x000020; - private const int StateAltChrSet = 0x000040; - private const int StateAltZP = 0x000080; - private const int StateBank1 = 0x000100; - private const int StateHRamRd = 0x000200; - private const int StateHRamPreWrt = 0x000400; - private const int StateHRamWrt = 0x000800; - private const int StatePage2 = 0x001000; - private const int StateRamRd = 0x002000; - private const int StateRamWrt = 0x004000; - private const int StateSlotC3Rom = 0x008000; - private const int StateIntC8Rom = 0x010000; // [5-28] - private const int StateIntCXRom = 0x020000; - private const int StateAn0 = 0x040000; - private const int StateAn1 = 0x080000; - private const int StateAn2 = 0x100000; - private const int StateAn3 = 0x200000; - private const int StateVideo = State80Col | StateText | StateMixed | StateHires | StateDRes; - - private const int StateVideoModeCount = 32; - - private static readonly int[] StateVideoMode = new int[StateVideoModeCount] - { - Video.Mode0, Video.Mode0, Video.Mode1, Video.Mode2, Video.Mode3, Video.Mode4, Video.Mode1, Video.Mode2, - Video.Mode5, Video.Mode5, Video.Mode1, Video.Mode2, Video.Mode6, Video.Mode7, Video.Mode1, Video.Mode2, - Video.Mode8, Video.Mode9, Video.Mode1, Video.Mode2, Video.ModeA, Video.ModeB, Video.Mode1, Video.Mode2, - Video.ModeC, Video.ModeD, Video.Mode1, Video.Mode2, Video.ModeE, Video.ModeF, Video.Mode1, Video.Mode2 - }; - - [JsonIgnore] - private Action[][][] WriteRamModeBankRegion; - } -} diff --git a/ExternalCoreProjects/Virtu/NoSlotClock.cs b/ExternalCoreProjects/Virtu/NoSlotClock.cs index 8ce39a8d32..2e793d0d4e 100644 --- a/ExternalCoreProjects/Virtu/NoSlotClock.cs +++ b/ExternalCoreProjects/Virtu/NoSlotClock.cs @@ -1,201 +1,212 @@ using System; -using System.IO; namespace Jellyfish.Virtu { - public sealed class NoSlotClock : MachineComponent - { + public interface ISlotClock + { + int Read(int address, int value); + void Write(int address); + } + + // ReSharper disable once UnusedMember.Global + public sealed class NoSlotClock : ISlotClock + { + // ReSharper disable once FieldCanBeMadeReadOnly.Local + private Video _video; + + // ReSharper disable once UnusedMember.Global public NoSlotClock() { } - public NoSlotClock(Machine machine) : - base(machine) - { - } - public override void Initialize() - { - _clockEnabled = false; - _writeEnabled = true; - _clockRegister = new RingRegister(0x0, 0x1); - _comparisonRegister = new RingRegister(ClockInitSequence, 0x1); - } + public NoSlotClock(Video video) + { + _video = video; - public int Read(int address, int data) - { - // this may read or write the clock - if ((address & 0x4) != 0) - { - return ReadClock(data); - } + _clockEnabled = false; + _writeEnabled = true; + _clockRegister = new RingRegister(0x0, 0x1); + _comparisonRegister = new RingRegister(ClockInitSequence, 0x1); + } - WriteClock(address); - return data; - } + public int Read(int address, int value) + { + // this may read or write the clock + if ((address & 0x4) != 0) + { + return ReadClock(value); + } - public void Write(int address) - { - // this may read or write the clock - if ((address & 0x4) != 0) - { - ReadClock(0); - } - else - { - WriteClock(address); - } - } + WriteClock(address); + return value; + } - private int ReadClock(int data) - { - // for a ROM, A2 high = read, and data out (if any) is on D0 - if (!_clockEnabled) - { - _comparisonRegister.Reset(); - _writeEnabled = true; - return data; - } + public void Write(int address) + { + // this may read or write the clock + if ((address & 0x4) != 0) + { + ReadClock(0); + } + else + { + WriteClock(address); + } + } - data = _clockRegister.ReadBit(Machine.Video.ReadFloatingBus()); - if (_clockRegister.NextBit()) - { - _clockEnabled = false; - } - return data; - } + private int ReadClock(int data) + { + // for a ROM, A2 high = read, and data out (if any) is on D0 + if (!_clockEnabled) + { + _comparisonRegister.Reset(); + _writeEnabled = true; + return data; + } - private void WriteClock(int address) - { - // for a ROM, A2 low = write, and data in is on A0 - if (!_writeEnabled) - { - return; - } + data = _clockRegister.ReadBit(_video.ReadFloatingBus()); + if (_clockRegister.NextBit()) + { + _clockEnabled = false; + } - if (!_clockEnabled) - { - if ((_comparisonRegister.CompareBit(address))) - { - if (_comparisonRegister.NextBit()) - { - _clockEnabled = true; - PopulateClockRegister(); - } - } - else - { - // mismatch ignores further writes - _writeEnabled = false; - } - } - else if (_clockRegister.NextBit()) - { - // simulate writes, but our clock register is read-only - _clockEnabled = false; - } - } + return data; + } - private void PopulateClockRegister() - { - // all values are in packed BCD format (4 bits per decimal digit) - var now = DateTime.Now; + private void WriteClock(int address) + { + // for a ROM, A2 low = write, and data in is on A0 + if (!_writeEnabled) + { + return; + } - int centisecond = now.Millisecond / 10; // 00-99 - _clockRegister.WriteNibble(centisecond % 10); - _clockRegister.WriteNibble(centisecond / 10); + if (!_clockEnabled) + { + if ((_comparisonRegister.CompareBit(address))) + { + if (_comparisonRegister.NextBit()) + { + _clockEnabled = true; + PopulateClockRegister(); + } + } + else + { + // mismatch ignores further writes + _writeEnabled = false; + } + } + else if (_clockRegister.NextBit()) + { + // simulate writes, but our clock register is read-only + _clockEnabled = false; + } + } - int second = now.Second; // 00-59 - _clockRegister.WriteNibble(second % 10); - _clockRegister.WriteNibble(second / 10); + private void PopulateClockRegister() + { + // all values are in packed BCD format (4 bits per decimal digit) + var now = DateTime.Now; - int minute = now.Minute; // 00-59 - _clockRegister.WriteNibble(minute % 10); - _clockRegister.WriteNibble(minute / 10); + int centisecond = now.Millisecond / 10; // 00-99 + _clockRegister.WriteNibble(centisecond % 10); + _clockRegister.WriteNibble(centisecond / 10); - int hour = now.Hour; // 01-23 - _clockRegister.WriteNibble(hour % 10); - _clockRegister.WriteNibble(hour / 10); + int second = now.Second; // 00-59 + _clockRegister.WriteNibble(second % 10); + _clockRegister.WriteNibble(second / 10); - int day = (int)now.DayOfWeek + 1; // 01-07 (1 = Sunday) - _clockRegister.WriteNibble(day % 10); - _clockRegister.WriteNibble(day / 10); + int minute = now.Minute; // 00-59 + _clockRegister.WriteNibble(minute % 10); + _clockRegister.WriteNibble(minute / 10); - int date = now.Day; // 01-31 - _clockRegister.WriteNibble(date % 10); - _clockRegister.WriteNibble(date / 10); + int hour = now.Hour; // 01-23 + _clockRegister.WriteNibble(hour % 10); + _clockRegister.WriteNibble(hour / 10); - int month = now.Month; // 01-12 - _clockRegister.WriteNibble(month % 10); - _clockRegister.WriteNibble(month / 10); + int day = (int)now.DayOfWeek + 1; // 01-07 (1 = Sunday) + _clockRegister.WriteNibble(day % 10); + _clockRegister.WriteNibble(day / 10); - int year = now.Year % 100; // 00-99 - _clockRegister.WriteNibble(year % 10); - _clockRegister.WriteNibble(year / 10); - } + int date = now.Day; // 01-31 + _clockRegister.WriteNibble(date % 10); + _clockRegister.WriteNibble(date / 10); - private const ulong ClockInitSequence = 0x5CA33AC55CA33AC5; + int month = now.Month; // 01-12 + _clockRegister.WriteNibble(month % 10); + _clockRegister.WriteNibble(month / 10); - private bool _clockEnabled; - private bool _writeEnabled; - private RingRegister _clockRegister; - private RingRegister _comparisonRegister; + int year = now.Year % 100; // 00-99 + _clockRegister.WriteNibble(year % 10); + _clockRegister.WriteNibble(year / 10); + } - private struct RingRegister - { - public RingRegister(ulong data, ulong mask) - { - _data = data; - _mask = mask; - } + private const ulong ClockInitSequence = 0x5CA33AC55CA33AC5; - public void Reset() - { - _mask = 0x1; - } + private bool _clockEnabled; + private bool _writeEnabled; + private RingRegister _clockRegister; + private RingRegister _comparisonRegister; - public void WriteNibble(int data) - { - WriteBits(data, 4); - } + private struct RingRegister + { + public RingRegister(ulong data, ulong mask) + { + _data = data; + _mask = mask; + } - public void WriteBits(int data, int count) - { - for (int i = 1; i <= count; i++) - { - WriteBit(data); - NextBit(); - data >>= 1; - } - } + public void Reset() + { + _mask = 0x1; + } - public void WriteBit(int data) - { - _data = ((data & 0x1) != 0) ? (_data | _mask) : (_data & ~_mask); - } + public void WriteNibble(int data) + { + WriteBits(data, 4); + } - public int ReadBit(int data) - { - return ((_data & _mask) != 0) ? (data | 0x1) : (data & ~0x1); - } + private void WriteBits(int data, int count) + { + for (int i = 1; i <= count; i++) + { + WriteBit(data); + NextBit(); + data >>= 1; + } + } - public bool CompareBit(int data) - { - return (((_data & _mask) != 0) == ((data & 0x1) != 0)); - } + private void WriteBit(int data) + { + _data = (data & 0x1) != 0 + ? _data | _mask + : _data & ~_mask; + } - public bool NextBit() - { - if ((_mask <<= 1) == 0) - { - _mask = 0x1; - return true; // wrap - } - return false; - } + public int ReadBit(int data) + { + return (_data & _mask) != 0 + ? data | 0x1 + : data & ~0x1; + } - public ulong Data { get { return _data; } } // no auto props - public ulong Mask { get { return _mask; } } + public bool CompareBit(int data) + { + return (_data & _mask) != 0 == ((data & 0x1) != 0); + } - private ulong _data; - private ulong _mask; - } - } + public bool NextBit() + { + if ((_mask <<= 1) == 0) + { + _mask = 0x1; + return true; // wrap + } + + return false; + } + + private ulong _data; + private ulong _mask; + } + } } diff --git a/ExternalCoreProjects/Virtu/PeripheralCard.cs b/ExternalCoreProjects/Virtu/PeripheralCard.cs deleted file mode 100644 index 3785c06775..0000000000 --- a/ExternalCoreProjects/Virtu/PeripheralCard.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace Jellyfish.Virtu -{ - public class PeripheralCard : MachineComponent - { - public PeripheralCard() { } - public PeripheralCard(Machine machine) : - base(machine) - { - } - - public virtual int ReadIoRegionC0C0(int address) - { - // read Device Select' address $C0nX; n = slot number + 8 - return ReadFloatingBus(); - } - - public virtual int ReadIoRegionC1C7(int address) - { - // read I/O Select' address $CsXX; s = slot number - return ReadFloatingBus(); - } - - public virtual int ReadIoRegionC8CF(int address) - { - // read I/O Strobe' address $C800-$CFFF - return ReadFloatingBus(); - } - - public virtual void WriteIoRegionC0C0(int address, int data) - { - // write Device Select' address $C0nX; n = slot number + 8 - } - - public virtual void WriteIoRegionC1C7(int address, int data) - { - // write I/O Select' address $CsXX; s = slot number - } - - public virtual void WriteIoRegionC8CF(int address, int data) - { - // write I/O Strobe' address $C800-$CFFF - } - - protected int ReadFloatingBus() - { - return Machine.Video.ReadFloatingBus(); - } - } -} diff --git a/ExternalCoreProjects/Virtu/Properties/AssemblyInfo.cs b/ExternalCoreProjects/Virtu/Properties/AssemblyInfo.cs index 8354598541..b5804fddbb 100644 --- a/ExternalCoreProjects/Virtu/Properties/AssemblyInfo.cs +++ b/ExternalCoreProjects/Virtu/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/ExternalCoreProjects/Virtu/Services/AudioService.cs b/ExternalCoreProjects/Virtu/Services/AudioService.cs deleted file mode 100644 index 936b2e74f2..0000000000 --- a/ExternalCoreProjects/Virtu/Services/AudioService.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Threading; -using Jellyfish.Library; - -namespace Jellyfish.Virtu.Services -{ - /// - /// this isn't really a "service" anymore, just a helper for the speaker class - /// - public class AudioService - { - public AudioService() { } - - public void Output(int data) // machine thread - { - data = (int)(data * 0.2); - if (pos < buff.Length - 2) - { - buff[pos++] = (short)data; - buff[pos++] = (short)data; - } - } - - [Newtonsoft.Json.JsonIgnore] // only relevant if trying to savestate midframe - private short[] buff = new short[4096]; - [Newtonsoft.Json.JsonIgnore] // only relevant if trying to savestate midframe - private int pos = 0; - - [System.Runtime.Serialization.OnDeserialized] - public void OnDeserialized(System.Runtime.Serialization.StreamingContext context) - { - pos = 0; - } - - public void Clear() - { - pos = 0; - } - - public void GetSamples(out short[] samples, out int nsamp) - { - samples = buff; - nsamp = pos / 2; - pos = 0; - } - } -} diff --git a/ExternalCoreProjects/Virtu/Services/DebugService.cs b/ExternalCoreProjects/Virtu/Services/DebugService.cs deleted file mode 100644 index 6b89b2e3ff..0000000000 --- a/ExternalCoreProjects/Virtu/Services/DebugService.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.Diagnostics; -using System.Globalization; -using System.Text; -using System.Threading; -using Jellyfish.Library; - -namespace Jellyfish.Virtu.Services -{ - /// - /// this isn't really a "service" anymore - /// - public static class DebugService - { - public static void WriteMessage(string message) - { - OnWriteMessage(FormatMessage(message)); - } - - public static void WriteMessage(string format, params object[] args) - { - OnWriteMessage(FormatMessage(format, args)); - } - - private static void OnWriteMessage(string message) - { -#if SILVERLIGHT - Debug.WriteLine(message); -#else - Trace.WriteLine(message); -#endif - } - - private static string FormatMessage(string format, params object[] args) - { - var message = new StringBuilder(256); - message.AppendFormat(CultureInfo.InvariantCulture, "[{0} T{1:X3} Virtu] ", DateTime.Now.ToString("HH:mm:ss.fff", CultureInfo.InvariantCulture), Thread.CurrentThread.ManagedThreadId); - if (args.Length > 0) - { - try - { - message.AppendFormat(CultureInfo.InvariantCulture, format, args); - } - catch (FormatException ex) - { - WriteMessage("[DebugService.FormatMessage] format: {0}; args: {1}; exception: {2}", format, string.Join(", ", args), ex.Message); - } - } - else - { - message.Append(format); - } - - return message.ToString(); - } - } -} diff --git a/ExternalCoreProjects/Virtu/Services/VideoService.cs b/ExternalCoreProjects/Virtu/Services/VideoService.cs deleted file mode 100644 index e8490b2182..0000000000 --- a/ExternalCoreProjects/Virtu/Services/VideoService.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Jellyfish.Virtu.Services -{ - /// - /// this isn't really a "service" anymore, just a helper for the video class - /// - public class VideoService - { - public VideoService() - { - fb = new int[560 * 384]; - } - public VideoService(int[] fb) - { - this.fb = fb; - } - - [Newtonsoft.Json.JsonIgnore] // client can serialize framebuffer if it wants to - public int[] fb; - - public void SetPixel(int x, int y, int color) - { - int i = 560 * y + x; - fb[i] = fb[i + 560] = color; - } - } -} diff --git a/ExternalCoreProjects/Virtu/Speaker.cs b/ExternalCoreProjects/Virtu/Speaker.cs index 2a2a89b468..0e3c371a14 100644 --- a/ExternalCoreProjects/Virtu/Speaker.cs +++ b/ExternalCoreProjects/Virtu/Speaker.cs @@ -1,67 +1,115 @@ using System; -using System.IO; -using Jellyfish.Virtu.Services; +using System.Runtime.Serialization; +using Newtonsoft.Json; namespace Jellyfish.Virtu { - public sealed class Speaker : MachineComponent - { + public interface ISpeaker + { + void ToggleOutput(); + + // ReSharper disable once UnusedMember.Global + void Clear(); + + // ReSharper disable once UnusedMember.Global + void GetSamples(out short[] samples, out int nSamp); + } + + public sealed class Speaker : ISpeaker + { + // ReSharper disable once FieldCanBeMadeReadOnly.Local + private MachineEvents _events; + + // ReSharper disable once FieldCanBeMadeReadOnly.Local + private ICpu _cpu; + public Speaker() { } - public Speaker(Machine machine) : - base(machine) - { - _flushOutputEvent = FlushOutputEvent; // cache delegates; avoids garbage - } + public Speaker(MachineEvents events, ICpu cpu) + { + _events = events; + _cpu = cpu; + _flushOutputEvent = FlushOutputEvent; // cache delegates; avoids garbage - public override void Initialize() - { - AudioService = new Services.AudioService(); + _events.AddEvent(CyclesPerFlush * _cpu.Multiplier, _flushOutputEvent); - Machine.Events.AddEvent(CyclesPerFlush * Machine.Cpu.Multiplier, _flushOutputEvent); - } + _isHigh = false; + _highCycles = _totalCycles = 0; + } - public override void Reset() - { - _isHigh = false; - _highCycles = _totalCycles = 0; - } + private const int CyclesPerFlush = 23; - public void ToggleOutput() - { - UpdateCycles(); - _isHigh ^= true; - } + // ReSharper disable once FieldCanBeMadeReadOnly.Local + private Action _flushOutputEvent; - private void FlushOutputEvent() - { - UpdateCycles(); + private bool _isHigh; + private int _highCycles; + private int _totalCycles; + private long _lastCycles; + + [JsonIgnore] // only relevant if trying to savestate mid-frame + private readonly short[] _buffer = new short[4096]; + + [JsonIgnore] // only relevant if trying to savestate mid-frame + private int _position; + + #region Api + + public void Clear() + { + _position = 0; + } + + public void GetSamples(out short[] samples, out int nSamp) + { + samples = _buffer; + nSamp = _position / 2; + _position = 0; + } + + #endregion + + public void ToggleOutput() + { + UpdateCycles(); + _isHigh ^= true; + } + + private void FlushOutputEvent() + { + UpdateCycles(); // TODO: better than simple decimation here!! - AudioService.Output(_highCycles * short.MaxValue / _totalCycles); - _highCycles = _totalCycles = 0; + Output(_highCycles * short.MaxValue / _totalCycles); + _highCycles = _totalCycles = 0; - Machine.Events.AddEvent(CyclesPerFlush * Machine.Cpu.Multiplier, _flushOutputEvent); - } + _events.AddEvent(CyclesPerFlush * _cpu.Multiplier, _flushOutputEvent); + } - private void UpdateCycles() - { - int delta = (int)(Machine.Cpu.Cycles - _lastCycles); - if (_isHigh) - { - _highCycles += delta; - } - _totalCycles += delta; - _lastCycles = Machine.Cpu.Cycles; - } + private void UpdateCycles() + { + int delta = (int)(_cpu.Cycles - _lastCycles); + if (_isHigh) + { + _highCycles += delta; + } + _totalCycles += delta; + _lastCycles = _cpu.Cycles; + } - private const int CyclesPerFlush = 23; + private void Output(int data) // machine thread + { + data = (int)(data * 0.2); + if (_position < _buffer.Length - 2) + { + _buffer[_position++] = (short)data; + _buffer[_position++] = (short)data; + } + } - private Action _flushOutputEvent; - private bool _isHigh; - private int _highCycles; - private int _totalCycles; - private long _lastCycles; - - public AudioService AudioService { get; private set; } - } + [OnDeserialized] + private void OnDeserialized(StreamingContext context) + { + _position = 0; + } + } } diff --git a/ExternalCoreProjects/Virtu/Video.Data.cs b/ExternalCoreProjects/Virtu/Video.Data.cs new file mode 100644 index 0000000000..0ecda3e45f --- /dev/null +++ b/ExternalCoreProjects/Virtu/Video.Data.cs @@ -0,0 +1,1631 @@ +using System; + +namespace Jellyfish.Virtu +{ + public partial class Video + { + private static readonly int[] AddressOffset = + { + 0x0000, 0x0400, 0x0800, 0x0C00, 0x1000, 0x1400, 0x1800, 0x1C00, + 0x0080, 0x0480, 0x0880, 0x0C80, 0x1080, 0x1480, 0x1880, 0x1C80, + 0x0100, 0x0500, 0x0900, 0x0D00, 0x1100, 0x1500, 0x1900, 0x1D00, + 0x0180, 0x0580, 0x0980, 0x0D80, 0x1180, 0x1580, 0x1980, 0x1D80, + 0x0200, 0x0600, 0x0A00, 0x0E00, 0x1200, 0x1600, 0x1A00, 0x1E00, + 0x0280, 0x0680, 0x0A80, 0x0E80, 0x1280, 0x1680, 0x1A80, 0x1E80, + 0x0300, 0x0700, 0x0B00, 0x0F00, 0x1300, 0x1700, 0x1B00, 0x1F00, + 0x0380, 0x0780, 0x0B80, 0x0F80, 0x1380, 0x1780, 0x1B80, 0x1F80, + 0x0028, 0x0428, 0x0828, 0x0C28, 0x1028, 0x1428, 0x1828, 0x1C28, + 0x00A8, 0x04A8, 0x08A8, 0x0CA8, 0x10A8, 0x14A8, 0x18A8, 0x1CA8, + 0x0128, 0x0528, 0x0928, 0x0D28, 0x1128, 0x1528, 0x1928, 0x1D28, + 0x01A8, 0x05A8, 0x09A8, 0x0DA8, 0x11A8, 0x15A8, 0x19A8, 0x1DA8, + 0x0228, 0x0628, 0x0A28, 0x0E28, 0x1228, 0x1628, 0x1A28, 0x1E28, + 0x02A8, 0x06A8, 0x0AA8, 0x0EA8, 0x12A8, 0x16A8, 0x1AA8, 0x1EA8, + 0x0328, 0x0728, 0x0B28, 0x0F28, 0x1328, 0x1728, 0x1B28, 0x1F28, + 0x03A8, 0x07A8, 0x0BA8, 0x0FA8, 0x13A8, 0x17A8, 0x1BA8, 0x1FA8, + 0x0050, 0x0450, 0x0850, 0x0C50, 0x1050, 0x1450, 0x1850, 0x1C50, + 0x00D0, 0x04D0, 0x08D0, 0x0CD0, 0x10D0, 0x14D0, 0x18D0, 0x1CD0, + 0x0150, 0x0550, 0x0950, 0x0D50, 0x1150, 0x1550, 0x1950, 0x1D50, + 0x01D0, 0x05D0, 0x09D0, 0x0DD0, 0x11D0, 0x15D0, 0x19D0, 0x1DD0, + 0x0250, 0x0650, 0x0A50, 0x0E50, 0x1250, 0x1650, 0x1A50, 0x1E50, + 0x02D0, 0x06D0, 0x0AD0, 0x0ED0, 0x12D0, 0x16D0, 0x1AD0, 0x1ED0, + 0x0350, 0x0750, 0x0B50, 0x0F50, 0x1350, 0x1750, 0x1B50, 0x1F50, + 0x03D0, 0x07D0, 0x0BD0, 0x0FD0, 0x13D0, 0x17D0, 0x1BD0, 0x1FD0 + }; + + private const int CellWidth = 14; + private const int CellHeight = 8; + private const int CellColumns = Width / CellWidth; + + private static readonly ushort[] CellIndex = + { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0A00, 0x0A01, 0x0A02, 0x0A03, 0x0A04, 0x0A05, 0x0A06, 0x0A07, + 0x0A08, 0x0A09, 0x0A0A, 0x0A0B, 0x0A0C, 0x0A0D, 0x0A0E, 0x0A0F, + 0x0A10, 0x0A11, 0x0A12, 0x0A13, 0x0A14, 0x0A15, 0x0A16, 0x0A17, + 0x0A18, 0x0A19, 0x0A1A, 0x0A1B, 0x0A1C, 0x0A1D, 0x0A1E, 0x0A1F, + 0x0A20, 0x0A21, 0x0A22, 0x0A23, 0x0A24, 0x0A25, 0x0A26, 0x0A27, + 0x1400, 0x1401, 0x1402, 0x1403, 0x1404, 0x1405, 0x1406, 0x1407, + 0x1408, 0x1409, 0x140A, 0x140B, 0x140C, 0x140D, 0x140E, 0x140F, + 0x1410, 0x1411, 0x1412, 0x1413, 0x1414, 0x1415, 0x1416, 0x1417, + 0x1418, 0x1419, 0x141A, 0x141B, 0x141C, 0x141D, 0x141E, 0x141F, + 0x1420, 0x1421, 0x1422, 0x1423, 0x1424, 0x1425, 0x1426, 0x1427, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0140, 0x0141, 0x0142, 0x0143, 0x0144, 0x0145, 0x0146, 0x0147, + 0x0148, 0x0149, 0x014A, 0x014B, 0x014C, 0x014D, 0x014E, 0x014F, + 0x0150, 0x0151, 0x0152, 0x0153, 0x0154, 0x0155, 0x0156, 0x0157, + 0x0158, 0x0159, 0x015A, 0x015B, 0x015C, 0x015D, 0x015E, 0x015F, + 0x0160, 0x0161, 0x0162, 0x0163, 0x0164, 0x0165, 0x0166, 0x0167, + 0x0B40, 0x0B41, 0x0B42, 0x0B43, 0x0B44, 0x0B45, 0x0B46, 0x0B47, + 0x0B48, 0x0B49, 0x0B4A, 0x0B4B, 0x0B4C, 0x0B4D, 0x0B4E, 0x0B4F, + 0x0B50, 0x0B51, 0x0B52, 0x0B53, 0x0B54, 0x0B55, 0x0B56, 0x0B57, + 0x0B58, 0x0B59, 0x0B5A, 0x0B5B, 0x0B5C, 0x0B5D, 0x0B5E, 0x0B5F, + 0x0B60, 0x0B61, 0x0B62, 0x0B63, 0x0B64, 0x0B65, 0x0B66, 0x0B67, + 0x1540, 0x1541, 0x1542, 0x1543, 0x1544, 0x1545, 0x1546, 0x1547, + 0x1548, 0x1549, 0x154A, 0x154B, 0x154C, 0x154D, 0x154E, 0x154F, + 0x1550, 0x1551, 0x1552, 0x1553, 0x1554, 0x1555, 0x1556, 0x1557, + 0x1558, 0x1559, 0x155A, 0x155B, 0x155C, 0x155D, 0x155E, 0x155F, + 0x1560, 0x1561, 0x1562, 0x1563, 0x1564, 0x1565, 0x1566, 0x1567, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0286, 0x0287, + 0x0288, 0x0289, 0x028A, 0x028B, 0x028C, 0x028D, 0x028E, 0x028F, + 0x0290, 0x0291, 0x0292, 0x0293, 0x0294, 0x0295, 0x0296, 0x0297, + 0x0298, 0x0299, 0x029A, 0x029B, 0x029C, 0x029D, 0x029E, 0x029F, + 0x02A0, 0x02A1, 0x02A2, 0x02A3, 0x02A4, 0x02A5, 0x02A6, 0x02A7, + 0x0C80, 0x0C81, 0x0C82, 0x0C83, 0x0C84, 0x0C85, 0x0C86, 0x0C87, + 0x0C88, 0x0C89, 0x0C8A, 0x0C8B, 0x0C8C, 0x0C8D, 0x0C8E, 0x0C8F, + 0x0C90, 0x0C91, 0x0C92, 0x0C93, 0x0C94, 0x0C95, 0x0C96, 0x0C97, + 0x0C98, 0x0C99, 0x0C9A, 0x0C9B, 0x0C9C, 0x0C9D, 0x0C9E, 0x0C9F, + 0x0CA0, 0x0CA1, 0x0CA2, 0x0CA3, 0x0CA4, 0x0CA5, 0x0CA6, 0x0CA7, + 0x1680, 0x1681, 0x1682, 0x1683, 0x1684, 0x1685, 0x1686, 0x1687, + 0x1688, 0x1689, 0x168A, 0x168B, 0x168C, 0x168D, 0x168E, 0x168F, + 0x1690, 0x1691, 0x1692, 0x1693, 0x1694, 0x1695, 0x1696, 0x1697, + 0x1698, 0x1699, 0x169A, 0x169B, 0x169C, 0x169D, 0x169E, 0x169F, + 0x16A0, 0x16A1, 0x16A2, 0x16A3, 0x16A4, 0x16A5, 0x16A6, 0x16A7, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, + 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x03CF, + 0x03D0, 0x03D1, 0x03D2, 0x03D3, 0x03D4, 0x03D5, 0x03D6, 0x03D7, + 0x03D8, 0x03D9, 0x03DA, 0x03DB, 0x03DC, 0x03DD, 0x03DE, 0x03DF, + 0x03E0, 0x03E1, 0x03E2, 0x03E3, 0x03E4, 0x03E5, 0x03E6, 0x03E7, + 0x0DC0, 0x0DC1, 0x0DC2, 0x0DC3, 0x0DC4, 0x0DC5, 0x0DC6, 0x0DC7, + 0x0DC8, 0x0DC9, 0x0DCA, 0x0DCB, 0x0DCC, 0x0DCD, 0x0DCE, 0x0DCF, + 0x0DD0, 0x0DD1, 0x0DD2, 0x0DD3, 0x0DD4, 0x0DD5, 0x0DD6, 0x0DD7, + 0x0DD8, 0x0DD9, 0x0DDA, 0x0DDB, 0x0DDC, 0x0DDD, 0x0DDE, 0x0DDF, + 0x0DE0, 0x0DE1, 0x0DE2, 0x0DE3, 0x0DE4, 0x0DE5, 0x0DE6, 0x0DE7, + 0x17C0, 0x17C1, 0x17C2, 0x17C3, 0x17C4, 0x17C5, 0x17C6, 0x17C7, + 0x17C8, 0x17C9, 0x17CA, 0x17CB, 0x17CC, 0x17CD, 0x17CE, 0x17CF, + 0x17D0, 0x17D1, 0x17D2, 0x17D3, 0x17D4, 0x17D5, 0x17D6, 0x17D7, + 0x17D8, 0x17D9, 0x17DA, 0x17DB, 0x17DC, 0x17DD, 0x17DE, 0x17DF, + 0x17E0, 0x17E1, 0x17E2, 0x17E3, 0x17E4, 0x17E5, 0x17E6, 0x17E7, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0500, 0x0501, 0x0502, 0x0503, 0x0504, 0x0505, 0x0506, 0x0507, + 0x0508, 0x0509, 0x050A, 0x050B, 0x050C, 0x050D, 0x050E, 0x050F, + 0x0510, 0x0511, 0x0512, 0x0513, 0x0514, 0x0515, 0x0516, 0x0517, + 0x0518, 0x0519, 0x051A, 0x051B, 0x051C, 0x051D, 0x051E, 0x051F, + 0x0520, 0x0521, 0x0522, 0x0523, 0x0524, 0x0525, 0x0526, 0x0527, + 0x0F00, 0x0F01, 0x0F02, 0x0F03, 0x0F04, 0x0F05, 0x0F06, 0x0F07, + 0x0F08, 0x0F09, 0x0F0A, 0x0F0B, 0x0F0C, 0x0F0D, 0x0F0E, 0x0F0F, + 0x0F10, 0x0F11, 0x0F12, 0x0F13, 0x0F14, 0x0F15, 0x0F16, 0x0F17, + 0x0F18, 0x0F19, 0x0F1A, 0x0F1B, 0x0F1C, 0x0F1D, 0x0F1E, 0x0F1F, + 0x0F20, 0x0F21, 0x0F22, 0x0F23, 0x0F24, 0x0F25, 0x0F26, 0x0F27, + 0x1900, 0x1901, 0x1902, 0x1903, 0x1904, 0x1905, 0x1906, 0x1907, + 0x1908, 0x1909, 0x190A, 0x190B, 0x190C, 0x190D, 0x190E, 0x190F, + 0x1910, 0x1911, 0x1912, 0x1913, 0x1914, 0x1915, 0x1916, 0x1917, + 0x1918, 0x1919, 0x191A, 0x191B, 0x191C, 0x191D, 0x191E, 0x191F, + 0x1920, 0x1921, 0x1922, 0x1923, 0x1924, 0x1925, 0x1926, 0x1927, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0640, 0x0641, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, + 0x0648, 0x0649, 0x064A, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, + 0x0650, 0x0651, 0x0652, 0x0653, 0x0654, 0x0655, 0x0656, 0x0657, + 0x0658, 0x0659, 0x065A, 0x065B, 0x065C, 0x065D, 0x065E, 0x065F, + 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, + 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047, + 0x1048, 0x1049, 0x104A, 0x104B, 0x104C, 0x104D, 0x104E, 0x104F, + 0x1050, 0x1051, 0x1052, 0x1053, 0x1054, 0x1055, 0x1056, 0x1057, + 0x1058, 0x1059, 0x105A, 0x105B, 0x105C, 0x105D, 0x105E, 0x105F, + 0x1060, 0x1061, 0x1062, 0x1063, 0x1064, 0x1065, 0x1066, 0x1067, + 0x1A40, 0x1A41, 0x1A42, 0x1A43, 0x1A44, 0x1A45, 0x1A46, 0x1A47, + 0x1A48, 0x1A49, 0x1A4A, 0x1A4B, 0x1A4C, 0x1A4D, 0x1A4E, 0x1A4F, + 0x1A50, 0x1A51, 0x1A52, 0x1A53, 0x1A54, 0x1A55, 0x1A56, 0x1A57, + 0x1A58, 0x1A59, 0x1A5A, 0x1A5B, 0x1A5C, 0x1A5D, 0x1A5E, 0x1A5F, + 0x1A60, 0x1A61, 0x1A62, 0x1A63, 0x1A64, 0x1A65, 0x1A66, 0x1A67, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0786, 0x0787, + 0x0788, 0x0789, 0x078A, 0x078B, 0x078C, 0x078D, 0x078E, 0x078F, + 0x0790, 0x0791, 0x0792, 0x0793, 0x0794, 0x0795, 0x0796, 0x0797, + 0x0798, 0x0799, 0x079A, 0x079B, 0x079C, 0x079D, 0x079E, 0x079F, + 0x07A0, 0x07A1, 0x07A2, 0x07A3, 0x07A4, 0x07A5, 0x07A6, 0x07A7, + 0x1180, 0x1181, 0x1182, 0x1183, 0x1184, 0x1185, 0x1186, 0x1187, + 0x1188, 0x1189, 0x118A, 0x118B, 0x118C, 0x118D, 0x118E, 0x118F, + 0x1190, 0x1191, 0x1192, 0x1193, 0x1194, 0x1195, 0x1196, 0x1197, + 0x1198, 0x1199, 0x119A, 0x119B, 0x119C, 0x119D, 0x119E, 0x119F, + 0x11A0, 0x11A1, 0x11A2, 0x11A3, 0x11A4, 0x11A5, 0x11A6, 0x11A7, + 0x1B80, 0x1B81, 0x1B82, 0x1B83, 0x1B84, 0x1B85, 0x1B86, 0x1B87, + 0x1B88, 0x1B89, 0x1B8A, 0x1B8B, 0x1B8C, 0x1B8D, 0x1B8E, 0x1B8F, + 0x1B90, 0x1B91, 0x1B92, 0x1B93, 0x1B94, 0x1B95, 0x1B96, 0x1B97, + 0x1B98, 0x1B99, 0x1B9A, 0x1B9B, 0x1B9C, 0x1B9D, 0x1B9E, 0x1B9F, + 0x1BA0, 0x1BA1, 0x1BA2, 0x1BA3, 0x1BA4, 0x1BA5, 0x1BA6, 0x1BA7, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x08C0, 0x08C1, 0x08C2, 0x08C3, 0x08C4, 0x08C5, 0x08C6, 0x08C7, + 0x08C8, 0x08C9, 0x08CA, 0x08CB, 0x08CC, 0x08CD, 0x08CE, 0x08CF, + 0x08D0, 0x08D1, 0x08D2, 0x08D3, 0x08D4, 0x08D5, 0x08D6, 0x08D7, + 0x08D8, 0x08D9, 0x08DA, 0x08DB, 0x08DC, 0x08DD, 0x08DE, 0x08DF, + 0x08E0, 0x08E1, 0x08E2, 0x08E3, 0x08E4, 0x08E5, 0x08E6, 0x08E7, + 0x12C0, 0x12C1, 0x12C2, 0x12C3, 0x12C4, 0x12C5, 0x12C6, 0x12C7, + 0x12C8, 0x12C9, 0x12CA, 0x12CB, 0x12CC, 0x12CD, 0x12CE, 0x12CF, + 0x12D0, 0x12D1, 0x12D2, 0x12D3, 0x12D4, 0x12D5, 0x12D6, 0x12D7, + 0x12D8, 0x12D9, 0x12DA, 0x12DB, 0x12DC, 0x12DD, 0x12DE, 0x12DF, + 0x12E0, 0x12E1, 0x12E2, 0x12E3, 0x12E4, 0x12E5, 0x12E6, 0x12E7, + 0x1CC0, 0x1CC1, 0x1CC2, 0x1CC3, 0x1CC4, 0x1CC5, 0x1CC6, 0x1CC7, + 0x1CC8, 0x1CC9, 0x1CCA, 0x1CCB, 0x1CCC, 0x1CCD, 0x1CCE, 0x1CCF, + 0x1CD0, 0x1CD1, 0x1CD2, 0x1CD3, 0x1CD4, 0x1CD5, 0x1CD6, 0x1CD7, + 0x1CD8, 0x1CD9, 0x1CDA, 0x1CDB, 0x1CDC, 0x1CDD, 0x1CDE, 0x1CDF, + 0x1CE0, 0x1CE1, 0x1CE2, 0x1CE3, 0x1CE4, 0x1CE5, 0x1CE6, 0x1CE7, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0A28, 0x0A29, 0x0A2A, 0x0A2B, 0x0A2C, 0x0A2D, 0x0A2E, 0x0A2F, + 0x0A30, 0x0A31, 0x0A32, 0x0A33, 0x0A34, 0x0A35, 0x0A36, 0x0A37, + 0x0A38, 0x0A39, 0x0A3A, 0x0A3B, 0x0A3C, 0x0A3D, 0x0A3E, 0x0A3F, + 0x0A40, 0x0A41, 0x0A42, 0x0A43, 0x0A44, 0x0A45, 0x0A46, 0x0A47, + 0x0A48, 0x0A49, 0x0A4A, 0x0A4B, 0x0A4C, 0x0A4D, 0x0A4E, 0x0A4F, + 0x1428, 0x1429, 0x142A, 0x142B, 0x142C, 0x142D, 0x142E, 0x142F, + 0x1430, 0x1431, 0x1432, 0x1433, 0x1434, 0x1435, 0x1436, 0x1437, + 0x1438, 0x1439, 0x143A, 0x143B, 0x143C, 0x143D, 0x143E, 0x143F, + 0x1440, 0x1441, 0x1442, 0x1443, 0x1444, 0x1445, 0x1446, 0x1447, + 0x1448, 0x1449, 0x144A, 0x144B, 0x144C, 0x144D, 0x144E, 0x144F, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0168, 0x0169, 0x016A, 0x016B, 0x016C, 0x016D, 0x016E, 0x016F, + 0x0170, 0x0171, 0x0172, 0x0173, 0x0174, 0x0175, 0x0176, 0x0177, + 0x0178, 0x0179, 0x017A, 0x017B, 0x017C, 0x017D, 0x017E, 0x017F, + 0x0180, 0x0181, 0x0182, 0x0183, 0x0184, 0x0185, 0x0186, 0x0187, + 0x0188, 0x0189, 0x018A, 0x018B, 0x018C, 0x018D, 0x018E, 0x018F, + 0x0B68, 0x0B69, 0x0B6A, 0x0B6B, 0x0B6C, 0x0B6D, 0x0B6E, 0x0B6F, + 0x0B70, 0x0B71, 0x0B72, 0x0B73, 0x0B74, 0x0B75, 0x0B76, 0x0B77, + 0x0B78, 0x0B79, 0x0B7A, 0x0B7B, 0x0B7C, 0x0B7D, 0x0B7E, 0x0B7F, + 0x0B80, 0x0B81, 0x0B82, 0x0B83, 0x0B84, 0x0B85, 0x0B86, 0x0B87, + 0x0B88, 0x0B89, 0x0B8A, 0x0B8B, 0x0B8C, 0x0B8D, 0x0B8E, 0x0B8F, + 0x1568, 0x1569, 0x156A, 0x156B, 0x156C, 0x156D, 0x156E, 0x156F, + 0x1570, 0x1571, 0x1572, 0x1573, 0x1574, 0x1575, 0x1576, 0x1577, + 0x1578, 0x1579, 0x157A, 0x157B, 0x157C, 0x157D, 0x157E, 0x157F, + 0x1580, 0x1581, 0x1582, 0x1583, 0x1584, 0x1585, 0x1586, 0x1587, + 0x1588, 0x1589, 0x158A, 0x158B, 0x158C, 0x158D, 0x158E, 0x158F, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x02A8, 0x02A9, 0x02AA, 0x02AB, 0x02AC, 0x02AD, 0x02AE, 0x02AF, + 0x02B0, 0x02B1, 0x02B2, 0x02B3, 0x02B4, 0x02B5, 0x02B6, 0x02B7, + 0x02B8, 0x02B9, 0x02BA, 0x02BB, 0x02BC, 0x02BD, 0x02BE, 0x02BF, + 0x02C0, 0x02C1, 0x02C2, 0x02C3, 0x02C4, 0x02C5, 0x02C6, 0x02C7, + 0x02C8, 0x02C9, 0x02CA, 0x02CB, 0x02CC, 0x02CD, 0x02CE, 0x02CF, + 0x0CA8, 0x0CA9, 0x0CAA, 0x0CAB, 0x0CAC, 0x0CAD, 0x0CAE, 0x0CAF, + 0x0CB0, 0x0CB1, 0x0CB2, 0x0CB3, 0x0CB4, 0x0CB5, 0x0CB6, 0x0CB7, + 0x0CB8, 0x0CB9, 0x0CBA, 0x0CBB, 0x0CBC, 0x0CBD, 0x0CBE, 0x0CBF, + 0x0CC0, 0x0CC1, 0x0CC2, 0x0CC3, 0x0CC4, 0x0CC5, 0x0CC6, 0x0CC7, + 0x0CC8, 0x0CC9, 0x0CCA, 0x0CCB, 0x0CCC, 0x0CCD, 0x0CCE, 0x0CCF, + 0x16A8, 0x16A9, 0x16AA, 0x16AB, 0x16AC, 0x16AD, 0x16AE, 0x16AF, + 0x16B0, 0x16B1, 0x16B2, 0x16B3, 0x16B4, 0x16B5, 0x16B6, 0x16B7, + 0x16B8, 0x16B9, 0x16BA, 0x16BB, 0x16BC, 0x16BD, 0x16BE, 0x16BF, + 0x16C0, 0x16C1, 0x16C2, 0x16C3, 0x16C4, 0x16C5, 0x16C6, 0x16C7, + 0x16C8, 0x16C9, 0x16CA, 0x16CB, 0x16CC, 0x16CD, 0x16CE, 0x16CF, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x03E8, 0x03E9, 0x03EA, 0x03EB, 0x03EC, 0x03ED, 0x03EE, 0x03EF, + 0x03F0, 0x03F1, 0x03F2, 0x03F3, 0x03F4, 0x03F5, 0x03F6, 0x03F7, + 0x03F8, 0x03F9, 0x03FA, 0x03FB, 0x03FC, 0x03FD, 0x03FE, 0x03FF, + 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, + 0x0408, 0x0409, 0x040A, 0x040B, 0x040C, 0x040D, 0x040E, 0x040F, + 0x0DE8, 0x0DE9, 0x0DEA, 0x0DEB, 0x0DEC, 0x0DED, 0x0DEE, 0x0DEF, + 0x0DF0, 0x0DF1, 0x0DF2, 0x0DF3, 0x0DF4, 0x0DF5, 0x0DF6, 0x0DF7, + 0x0DF8, 0x0DF9, 0x0DFA, 0x0DFB, 0x0DFC, 0x0DFD, 0x0DFE, 0x0DFF, + 0x0E00, 0x0E01, 0x0E02, 0x0E03, 0x0E04, 0x0E05, 0x0E06, 0x0E07, + 0x0E08, 0x0E09, 0x0E0A, 0x0E0B, 0x0E0C, 0x0E0D, 0x0E0E, 0x0E0F, + 0x17E8, 0x17E9, 0x17EA, 0x17EB, 0x17EC, 0x17ED, 0x17EE, 0x17EF, + 0x17F0, 0x17F1, 0x17F2, 0x17F3, 0x17F4, 0x17F5, 0x17F6, 0x17F7, + 0x17F8, 0x17F9, 0x17FA, 0x17FB, 0x17FC, 0x17FD, 0x17FE, 0x17FF, + 0x1800, 0x1801, 0x1802, 0x1803, 0x1804, 0x1805, 0x1806, 0x1807, + 0x1808, 0x1809, 0x180A, 0x180B, 0x180C, 0x180D, 0x180E, 0x180F, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0528, 0x0529, 0x052A, 0x052B, 0x052C, 0x052D, 0x052E, 0x052F, + 0x0530, 0x0531, 0x0532, 0x0533, 0x0534, 0x0535, 0x0536, 0x0537, + 0x0538, 0x0539, 0x053A, 0x053B, 0x053C, 0x053D, 0x053E, 0x053F, + 0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0546, 0x0547, + 0x0548, 0x0549, 0x054A, 0x054B, 0x054C, 0x054D, 0x054E, 0x054F, + 0x0F28, 0x0F29, 0x0F2A, 0x0F2B, 0x0F2C, 0x0F2D, 0x0F2E, 0x0F2F, + 0x0F30, 0x0F31, 0x0F32, 0x0F33, 0x0F34, 0x0F35, 0x0F36, 0x0F37, + 0x0F38, 0x0F39, 0x0F3A, 0x0F3B, 0x0F3C, 0x0F3D, 0x0F3E, 0x0F3F, + 0x0F40, 0x0F41, 0x0F42, 0x0F43, 0x0F44, 0x0F45, 0x0F46, 0x0F47, + 0x0F48, 0x0F49, 0x0F4A, 0x0F4B, 0x0F4C, 0x0F4D, 0x0F4E, 0x0F4F, + 0x1928, 0x1929, 0x192A, 0x192B, 0x192C, 0x192D, 0x192E, 0x192F, + 0x1930, 0x1931, 0x1932, 0x1933, 0x1934, 0x1935, 0x1936, 0x1937, + 0x1938, 0x1939, 0x193A, 0x193B, 0x193C, 0x193D, 0x193E, 0x193F, + 0x1940, 0x1941, 0x1942, 0x1943, 0x1944, 0x1945, 0x1946, 0x1947, + 0x1948, 0x1949, 0x194A, 0x194B, 0x194C, 0x194D, 0x194E, 0x194F, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0668, 0x0669, 0x066A, 0x066B, 0x066C, 0x066D, 0x066E, 0x066F, + 0x0670, 0x0671, 0x0672, 0x0673, 0x0674, 0x0675, 0x0676, 0x0677, + 0x0678, 0x0679, 0x067A, 0x067B, 0x067C, 0x067D, 0x067E, 0x067F, + 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0686, 0x0687, + 0x0688, 0x0689, 0x068A, 0x068B, 0x068C, 0x068D, 0x068E, 0x068F, + 0x1068, 0x1069, 0x106A, 0x106B, 0x106C, 0x106D, 0x106E, 0x106F, + 0x1070, 0x1071, 0x1072, 0x1073, 0x1074, 0x1075, 0x1076, 0x1077, + 0x1078, 0x1079, 0x107A, 0x107B, 0x107C, 0x107D, 0x107E, 0x107F, + 0x1080, 0x1081, 0x1082, 0x1083, 0x1084, 0x1085, 0x1086, 0x1087, + 0x1088, 0x1089, 0x108A, 0x108B, 0x108C, 0x108D, 0x108E, 0x108F, + 0x1A68, 0x1A69, 0x1A6A, 0x1A6B, 0x1A6C, 0x1A6D, 0x1A6E, 0x1A6F, + 0x1A70, 0x1A71, 0x1A72, 0x1A73, 0x1A74, 0x1A75, 0x1A76, 0x1A77, + 0x1A78, 0x1A79, 0x1A7A, 0x1A7B, 0x1A7C, 0x1A7D, 0x1A7E, 0x1A7F, + 0x1A80, 0x1A81, 0x1A82, 0x1A83, 0x1A84, 0x1A85, 0x1A86, 0x1A87, + 0x1A88, 0x1A89, 0x1A8A, 0x1A8B, 0x1A8C, 0x1A8D, 0x1A8E, 0x1A8F, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x07A8, 0x07A9, 0x07AA, 0x07AB, 0x07AC, 0x07AD, 0x07AE, 0x07AF, + 0x07B0, 0x07B1, 0x07B2, 0x07B3, 0x07B4, 0x07B5, 0x07B6, 0x07B7, + 0x07B8, 0x07B9, 0x07BA, 0x07BB, 0x07BC, 0x07BD, 0x07BE, 0x07BF, + 0x07C0, 0x07C1, 0x07C2, 0x07C3, 0x07C4, 0x07C5, 0x07C6, 0x07C7, + 0x07C8, 0x07C9, 0x07CA, 0x07CB, 0x07CC, 0x07CD, 0x07CE, 0x07CF, + 0x11A8, 0x11A9, 0x11AA, 0x11AB, 0x11AC, 0x11AD, 0x11AE, 0x11AF, + 0x11B0, 0x11B1, 0x11B2, 0x11B3, 0x11B4, 0x11B5, 0x11B6, 0x11B7, + 0x11B8, 0x11B9, 0x11BA, 0x11BB, 0x11BC, 0x11BD, 0x11BE, 0x11BF, + 0x11C0, 0x11C1, 0x11C2, 0x11C3, 0x11C4, 0x11C5, 0x11C6, 0x11C7, + 0x11C8, 0x11C9, 0x11CA, 0x11CB, 0x11CC, 0x11CD, 0x11CE, 0x11CF, + 0x1BA8, 0x1BA9, 0x1BAA, 0x1BAB, 0x1BAC, 0x1BAD, 0x1BAE, 0x1BAF, + 0x1BB0, 0x1BB1, 0x1BB2, 0x1BB3, 0x1BB4, 0x1BB5, 0x1BB6, 0x1BB7, + 0x1BB8, 0x1BB9, 0x1BBA, 0x1BBB, 0x1BBC, 0x1BBD, 0x1BBE, 0x1BBF, + 0x1BC0, 0x1BC1, 0x1BC2, 0x1BC3, 0x1BC4, 0x1BC5, 0x1BC6, 0x1BC7, + 0x1BC8, 0x1BC9, 0x1BCA, 0x1BCB, 0x1BCC, 0x1BCD, 0x1BCE, 0x1BCF, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x08E8, 0x08E9, 0x08EA, 0x08EB, 0x08EC, 0x08ED, 0x08EE, 0x08EF, + 0x08F0, 0x08F1, 0x08F2, 0x08F3, 0x08F4, 0x08F5, 0x08F6, 0x08F7, + 0x08F8, 0x08F9, 0x08FA, 0x08FB, 0x08FC, 0x08FD, 0x08FE, 0x08FF, + 0x0900, 0x0901, 0x0902, 0x0903, 0x0904, 0x0905, 0x0906, 0x0907, + 0x0908, 0x0909, 0x090A, 0x090B, 0x090C, 0x090D, 0x090E, 0x090F, + 0x12E8, 0x12E9, 0x12EA, 0x12EB, 0x12EC, 0x12ED, 0x12EE, 0x12EF, + 0x12F0, 0x12F1, 0x12F2, 0x12F3, 0x12F4, 0x12F5, 0x12F6, 0x12F7, + 0x12F8, 0x12F9, 0x12FA, 0x12FB, 0x12FC, 0x12FD, 0x12FE, 0x12FF, + 0x1300, 0x1301, 0x1302, 0x1303, 0x1304, 0x1305, 0x1306, 0x1307, + 0x1308, 0x1309, 0x130A, 0x130B, 0x130C, 0x130D, 0x130E, 0x130F, + 0x1CE8, 0x1CE9, 0x1CEA, 0x1CEB, 0x1CEC, 0x1CED, 0x1CEE, 0x1CEF, + 0x1CF0, 0x1CF1, 0x1CF2, 0x1CF3, 0x1CF4, 0x1CF5, 0x1CF6, 0x1CF7, + 0x1CF8, 0x1CF9, 0x1CFA, 0x1CFB, 0x1CFC, 0x1CFD, 0x1CFE, 0x1CFF, + 0x1D00, 0x1D01, 0x1D02, 0x1D03, 0x1D04, 0x1D05, 0x1D06, 0x1D07, + 0x1D08, 0x1D09, 0x1D0A, 0x1D0B, 0x1D0C, 0x1D0D, 0x1D0E, 0x1D0F, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0A50, 0x0A51, 0x0A52, 0x0A53, 0x0A54, 0x0A55, 0x0A56, 0x0A57, + 0x0A58, 0x0A59, 0x0A5A, 0x0A5B, 0x0A5C, 0x0A5D, 0x0A5E, 0x0A5F, + 0x0A60, 0x0A61, 0x0A62, 0x0A63, 0x0A64, 0x0A65, 0x0A66, 0x0A67, + 0x0A68, 0x0A69, 0x0A6A, 0x0A6B, 0x0A6C, 0x0A6D, 0x0A6E, 0x0A6F, + 0x0A70, 0x0A71, 0x0A72, 0x0A73, 0x0A74, 0x0A75, 0x0A76, 0x0A77, + 0x1450, 0x1451, 0x1452, 0x1453, 0x1454, 0x1455, 0x1456, 0x1457, + 0x1458, 0x1459, 0x145A, 0x145B, 0x145C, 0x145D, 0x145E, 0x145F, + 0x1460, 0x1461, 0x1462, 0x1463, 0x1464, 0x1465, 0x1466, 0x1467, + 0x1468, 0x1469, 0x146A, 0x146B, 0x146C, 0x146D, 0x146E, 0x146F, + 0x1470, 0x1471, 0x1472, 0x1473, 0x1474, 0x1475, 0x1476, 0x1477, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0190, 0x0191, 0x0192, 0x0193, 0x0194, 0x0195, 0x0196, 0x0197, + 0x0198, 0x0199, 0x019A, 0x019B, 0x019C, 0x019D, 0x019E, 0x019F, + 0x01A0, 0x01A1, 0x01A2, 0x01A3, 0x01A4, 0x01A5, 0x01A6, 0x01A7, + 0x01A8, 0x01A9, 0x01AA, 0x01AB, 0x01AC, 0x01AD, 0x01AE, 0x01AF, + 0x01B0, 0x01B1, 0x01B2, 0x01B3, 0x01B4, 0x01B5, 0x01B6, 0x01B7, + 0x0B90, 0x0B91, 0x0B92, 0x0B93, 0x0B94, 0x0B95, 0x0B96, 0x0B97, + 0x0B98, 0x0B99, 0x0B9A, 0x0B9B, 0x0B9C, 0x0B9D, 0x0B9E, 0x0B9F, + 0x0BA0, 0x0BA1, 0x0BA2, 0x0BA3, 0x0BA4, 0x0BA5, 0x0BA6, 0x0BA7, + 0x0BA8, 0x0BA9, 0x0BAA, 0x0BAB, 0x0BAC, 0x0BAD, 0x0BAE, 0x0BAF, + 0x0BB0, 0x0BB1, 0x0BB2, 0x0BB3, 0x0BB4, 0x0BB5, 0x0BB6, 0x0BB7, + 0x1590, 0x1591, 0x1592, 0x1593, 0x1594, 0x1595, 0x1596, 0x1597, + 0x1598, 0x1599, 0x159A, 0x159B, 0x159C, 0x159D, 0x159E, 0x159F, + 0x15A0, 0x15A1, 0x15A2, 0x15A3, 0x15A4, 0x15A5, 0x15A6, 0x15A7, + 0x15A8, 0x15A9, 0x15AA, 0x15AB, 0x15AC, 0x15AD, 0x15AE, 0x15AF, + 0x15B0, 0x15B1, 0x15B2, 0x15B3, 0x15B4, 0x15B5, 0x15B6, 0x15B7, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x02D0, 0x02D1, 0x02D2, 0x02D3, 0x02D4, 0x02D5, 0x02D6, 0x02D7, + 0x02D8, 0x02D9, 0x02DA, 0x02DB, 0x02DC, 0x02DD, 0x02DE, 0x02DF, + 0x02E0, 0x02E1, 0x02E2, 0x02E3, 0x02E4, 0x02E5, 0x02E6, 0x02E7, + 0x02E8, 0x02E9, 0x02EA, 0x02EB, 0x02EC, 0x02ED, 0x02EE, 0x02EF, + 0x02F0, 0x02F1, 0x02F2, 0x02F3, 0x02F4, 0x02F5, 0x02F6, 0x02F7, + 0x0CD0, 0x0CD1, 0x0CD2, 0x0CD3, 0x0CD4, 0x0CD5, 0x0CD6, 0x0CD7, + 0x0CD8, 0x0CD9, 0x0CDA, 0x0CDB, 0x0CDC, 0x0CDD, 0x0CDE, 0x0CDF, + 0x0CE0, 0x0CE1, 0x0CE2, 0x0CE3, 0x0CE4, 0x0CE5, 0x0CE6, 0x0CE7, + 0x0CE8, 0x0CE9, 0x0CEA, 0x0CEB, 0x0CEC, 0x0CED, 0x0CEE, 0x0CEF, + 0x0CF0, 0x0CF1, 0x0CF2, 0x0CF3, 0x0CF4, 0x0CF5, 0x0CF6, 0x0CF7, + 0x16D0, 0x16D1, 0x16D2, 0x16D3, 0x16D4, 0x16D5, 0x16D6, 0x16D7, + 0x16D8, 0x16D9, 0x16DA, 0x16DB, 0x16DC, 0x16DD, 0x16DE, 0x16DF, + 0x16E0, 0x16E1, 0x16E2, 0x16E3, 0x16E4, 0x16E5, 0x16E6, 0x16E7, + 0x16E8, 0x16E9, 0x16EA, 0x16EB, 0x16EC, 0x16ED, 0x16EE, 0x16EF, + 0x16F0, 0x16F1, 0x16F2, 0x16F3, 0x16F4, 0x16F5, 0x16F6, 0x16F7, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, + 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, + 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, + 0x0E10, 0x0E11, 0x0E12, 0x0E13, 0x0E14, 0x0E15, 0x0E16, 0x0E17, + 0x0E18, 0x0E19, 0x0E1A, 0x0E1B, 0x0E1C, 0x0E1D, 0x0E1E, 0x0E1F, + 0x0E20, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27, + 0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F, + 0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37, + 0x1810, 0x1811, 0x1812, 0x1813, 0x1814, 0x1815, 0x1816, 0x1817, + 0x1818, 0x1819, 0x181A, 0x181B, 0x181C, 0x181D, 0x181E, 0x181F, + 0x1820, 0x1821, 0x1822, 0x1823, 0x1824, 0x1825, 0x1826, 0x1827, + 0x1828, 0x1829, 0x182A, 0x182B, 0x182C, 0x182D, 0x182E, 0x182F, + 0x1830, 0x1831, 0x1832, 0x1833, 0x1834, 0x1835, 0x1836, 0x1837, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0550, 0x0551, 0x0552, 0x0553, 0x0554, 0x0555, 0x0556, 0x0557, + 0x0558, 0x0559, 0x055A, 0x055B, 0x055C, 0x055D, 0x055E, 0x055F, + 0x0560, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567, + 0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F, + 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577, + 0x0F50, 0x0F51, 0x0F52, 0x0F53, 0x0F54, 0x0F55, 0x0F56, 0x0F57, + 0x0F58, 0x0F59, 0x0F5A, 0x0F5B, 0x0F5C, 0x0F5D, 0x0F5E, 0x0F5F, + 0x0F60, 0x0F61, 0x0F62, 0x0F63, 0x0F64, 0x0F65, 0x0F66, 0x0F67, + 0x0F68, 0x0F69, 0x0F6A, 0x0F6B, 0x0F6C, 0x0F6D, 0x0F6E, 0x0F6F, + 0x0F70, 0x0F71, 0x0F72, 0x0F73, 0x0F74, 0x0F75, 0x0F76, 0x0F77, + 0x1950, 0x1951, 0x1952, 0x1953, 0x1954, 0x1955, 0x1956, 0x1957, + 0x1958, 0x1959, 0x195A, 0x195B, 0x195C, 0x195D, 0x195E, 0x195F, + 0x1960, 0x1961, 0x1962, 0x1963, 0x1964, 0x1965, 0x1966, 0x1967, + 0x1968, 0x1969, 0x196A, 0x196B, 0x196C, 0x196D, 0x196E, 0x196F, + 0x1970, 0x1971, 0x1972, 0x1973, 0x1974, 0x1975, 0x1976, 0x1977, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0690, 0x0691, 0x0692, 0x0693, 0x0694, 0x0695, 0x0696, 0x0697, + 0x0698, 0x0699, 0x069A, 0x069B, 0x069C, 0x069D, 0x069E, 0x069F, + 0x06A0, 0x06A1, 0x06A2, 0x06A3, 0x06A4, 0x06A5, 0x06A6, 0x06A7, + 0x06A8, 0x06A9, 0x06AA, 0x06AB, 0x06AC, 0x06AD, 0x06AE, 0x06AF, + 0x06B0, 0x06B1, 0x06B2, 0x06B3, 0x06B4, 0x06B5, 0x06B6, 0x06B7, + 0x1090, 0x1091, 0x1092, 0x1093, 0x1094, 0x1095, 0x1096, 0x1097, + 0x1098, 0x1099, 0x109A, 0x109B, 0x109C, 0x109D, 0x109E, 0x109F, + 0x10A0, 0x10A1, 0x10A2, 0x10A3, 0x10A4, 0x10A5, 0x10A6, 0x10A7, + 0x10A8, 0x10A9, 0x10AA, 0x10AB, 0x10AC, 0x10AD, 0x10AE, 0x10AF, + 0x10B0, 0x10B1, 0x10B2, 0x10B3, 0x10B4, 0x10B5, 0x10B6, 0x10B7, + 0x1A90, 0x1A91, 0x1A92, 0x1A93, 0x1A94, 0x1A95, 0x1A96, 0x1A97, + 0x1A98, 0x1A99, 0x1A9A, 0x1A9B, 0x1A9C, 0x1A9D, 0x1A9E, 0x1A9F, + 0x1AA0, 0x1AA1, 0x1AA2, 0x1AA3, 0x1AA4, 0x1AA5, 0x1AA6, 0x1AA7, + 0x1AA8, 0x1AA9, 0x1AAA, 0x1AAB, 0x1AAC, 0x1AAD, 0x1AAE, 0x1AAF, + 0x1AB0, 0x1AB1, 0x1AB2, 0x1AB3, 0x1AB4, 0x1AB5, 0x1AB6, 0x1AB7, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x07D0, 0x07D1, 0x07D2, 0x07D3, 0x07D4, 0x07D5, 0x07D6, 0x07D7, + 0x07D8, 0x07D9, 0x07DA, 0x07DB, 0x07DC, 0x07DD, 0x07DE, 0x07DF, + 0x07E0, 0x07E1, 0x07E2, 0x07E3, 0x07E4, 0x07E5, 0x07E6, 0x07E7, + 0x07E8, 0x07E9, 0x07EA, 0x07EB, 0x07EC, 0x07ED, 0x07EE, 0x07EF, + 0x07F0, 0x07F1, 0x07F2, 0x07F3, 0x07F4, 0x07F5, 0x07F6, 0x07F7, + 0x11D0, 0x11D1, 0x11D2, 0x11D3, 0x11D4, 0x11D5, 0x11D6, 0x11D7, + 0x11D8, 0x11D9, 0x11DA, 0x11DB, 0x11DC, 0x11DD, 0x11DE, 0x11DF, + 0x11E0, 0x11E1, 0x11E2, 0x11E3, 0x11E4, 0x11E5, 0x11E6, 0x11E7, + 0x11E8, 0x11E9, 0x11EA, 0x11EB, 0x11EC, 0x11ED, 0x11EE, 0x11EF, + 0x11F0, 0x11F1, 0x11F2, 0x11F3, 0x11F4, 0x11F5, 0x11F6, 0x11F7, + 0x1BD0, 0x1BD1, 0x1BD2, 0x1BD3, 0x1BD4, 0x1BD5, 0x1BD6, 0x1BD7, + 0x1BD8, 0x1BD9, 0x1BDA, 0x1BDB, 0x1BDC, 0x1BDD, 0x1BDE, 0x1BDF, + 0x1BE0, 0x1BE1, 0x1BE2, 0x1BE3, 0x1BE4, 0x1BE5, 0x1BE6, 0x1BE7, + 0x1BE8, 0x1BE9, 0x1BEA, 0x1BEB, 0x1BEC, 0x1BED, 0x1BEE, 0x1BEF, + 0x1BF0, 0x1BF1, 0x1BF2, 0x1BF3, 0x1BF4, 0x1BF5, 0x1BF6, 0x1BF7, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0910, 0x0911, 0x0912, 0x0913, 0x0914, 0x0915, 0x0916, 0x0917, + 0x0918, 0x0919, 0x091A, 0x091B, 0x091C, 0x091D, 0x091E, 0x091F, + 0x0920, 0x0921, 0x0922, 0x0923, 0x0924, 0x0925, 0x0926, 0x0927, + 0x0928, 0x0929, 0x092A, 0x092B, 0x092C, 0x092D, 0x092E, 0x092F, + 0x0930, 0x0931, 0x0932, 0x0933, 0x0934, 0x0935, 0x0936, 0x0937, + 0x1310, 0x1311, 0x1312, 0x1313, 0x1314, 0x1315, 0x1316, 0x1317, + 0x1318, 0x1319, 0x131A, 0x131B, 0x131C, 0x131D, 0x131E, 0x131F, + 0x1320, 0x1321, 0x1322, 0x1323, 0x1324, 0x1325, 0x1326, 0x1327, + 0x1328, 0x1329, 0x132A, 0x132B, 0x132C, 0x132D, 0x132E, 0x132F, + 0x1330, 0x1331, 0x1332, 0x1333, 0x1334, 0x1335, 0x1336, 0x1337, + 0x1D10, 0x1D11, 0x1D12, 0x1D13, 0x1D14, 0x1D15, 0x1D16, 0x1D17, + 0x1D18, 0x1D19, 0x1D1A, 0x1D1B, 0x1D1C, 0x1D1D, 0x1D1E, 0x1D1F, + 0x1D20, 0x1D21, 0x1D22, 0x1D23, 0x1D24, 0x1D25, 0x1D26, 0x1D27, + 0x1D28, 0x1D29, 0x1D2A, 0x1D2B, 0x1D2C, 0x1D2D, 0x1D2E, 0x1D2F, + 0x1D30, 0x1D31, 0x1D32, 0x1D33, 0x1D34, 0x1D35, 0x1D36, 0x1D37, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, + 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, + 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + 0x0A78, 0x0A79, 0x0A7A, 0x0A7B, 0x0A7C, 0x0A7D, 0x0A7E, 0x0A7F, + 0x0A80, 0x0A81, 0x0A82, 0x0A83, 0x0A84, 0x0A85, 0x0A86, 0x0A87, + 0x0A88, 0x0A89, 0x0A8A, 0x0A8B, 0x0A8C, 0x0A8D, 0x0A8E, 0x0A8F, + 0x0A90, 0x0A91, 0x0A92, 0x0A93, 0x0A94, 0x0A95, 0x0A96, 0x0A97, + 0x0A98, 0x0A99, 0x0A9A, 0x0A9B, 0x0A9C, 0x0A9D, 0x0A9E, 0x0A9F, + 0x1478, 0x1479, 0x147A, 0x147B, 0x147C, 0x147D, 0x147E, 0x147F, + 0x1480, 0x1481, 0x1482, 0x1483, 0x1484, 0x1485, 0x1486, 0x1487, + 0x1488, 0x1489, 0x148A, 0x148B, 0x148C, 0x148D, 0x148E, 0x148F, + 0x1490, 0x1491, 0x1492, 0x1493, 0x1494, 0x1495, 0x1496, 0x1497, + 0x1498, 0x1499, 0x149A, 0x149B, 0x149C, 0x149D, 0x149E, 0x149F, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x01B8, 0x01B9, 0x01BA, 0x01BB, 0x01BC, 0x01BD, 0x01BE, 0x01BF, + 0x01C0, 0x01C1, 0x01C2, 0x01C3, 0x01C4, 0x01C5, 0x01C6, 0x01C7, + 0x01C8, 0x01C9, 0x01CA, 0x01CB, 0x01CC, 0x01CD, 0x01CE, 0x01CF, + 0x01D0, 0x01D1, 0x01D2, 0x01D3, 0x01D4, 0x01D5, 0x01D6, 0x01D7, + 0x01D8, 0x01D9, 0x01DA, 0x01DB, 0x01DC, 0x01DD, 0x01DE, 0x01DF, + 0x0BB8, 0x0BB9, 0x0BBA, 0x0BBB, 0x0BBC, 0x0BBD, 0x0BBE, 0x0BBF, + 0x0BC0, 0x0BC1, 0x0BC2, 0x0BC3, 0x0BC4, 0x0BC5, 0x0BC6, 0x0BC7, + 0x0BC8, 0x0BC9, 0x0BCA, 0x0BCB, 0x0BCC, 0x0BCD, 0x0BCE, 0x0BCF, + 0x0BD0, 0x0BD1, 0x0BD2, 0x0BD3, 0x0BD4, 0x0BD5, 0x0BD6, 0x0BD7, + 0x0BD8, 0x0BD9, 0x0BDA, 0x0BDB, 0x0BDC, 0x0BDD, 0x0BDE, 0x0BDF, + 0x15B8, 0x15B9, 0x15BA, 0x15BB, 0x15BC, 0x15BD, 0x15BE, 0x15BF, + 0x15C0, 0x15C1, 0x15C2, 0x15C3, 0x15C4, 0x15C5, 0x15C6, 0x15C7, + 0x15C8, 0x15C9, 0x15CA, 0x15CB, 0x15CC, 0x15CD, 0x15CE, 0x15CF, + 0x15D0, 0x15D1, 0x15D2, 0x15D3, 0x15D4, 0x15D5, 0x15D6, 0x15D7, + 0x15D8, 0x15D9, 0x15DA, 0x15DB, 0x15DC, 0x15DD, 0x15DE, 0x15DF, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x02F8, 0x02F9, 0x02FA, 0x02FB, 0x02FC, 0x02FD, 0x02FE, 0x02FF, + 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, + 0x0308, 0x0309, 0x030A, 0x030B, 0x030C, 0x030D, 0x030E, 0x030F, + 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317, + 0x0318, 0x0319, 0x031A, 0x031B, 0x031C, 0x031D, 0x031E, 0x031F, + 0x0CF8, 0x0CF9, 0x0CFA, 0x0CFB, 0x0CFC, 0x0CFD, 0x0CFE, 0x0CFF, + 0x0D00, 0x0D01, 0x0D02, 0x0D03, 0x0D04, 0x0D05, 0x0D06, 0x0D07, + 0x0D08, 0x0D09, 0x0D0A, 0x0D0B, 0x0D0C, 0x0D0D, 0x0D0E, 0x0D0F, + 0x0D10, 0x0D11, 0x0D12, 0x0D13, 0x0D14, 0x0D15, 0x0D16, 0x0D17, + 0x0D18, 0x0D19, 0x0D1A, 0x0D1B, 0x0D1C, 0x0D1D, 0x0D1E, 0x0D1F, + 0x16F8, 0x16F9, 0x16FA, 0x16FB, 0x16FC, 0x16FD, 0x16FE, 0x16FF, + 0x1700, 0x1701, 0x1702, 0x1703, 0x1704, 0x1705, 0x1706, 0x1707, + 0x1708, 0x1709, 0x170A, 0x170B, 0x170C, 0x170D, 0x170E, 0x170F, + 0x1710, 0x1711, 0x1712, 0x1713, 0x1714, 0x1715, 0x1716, 0x1717, + 0x1718, 0x1719, 0x171A, 0x171B, 0x171C, 0x171D, 0x171E, 0x171F, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, + 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, + 0x0450, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, + 0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x045D, 0x045E, 0x045F, + 0x0E38, 0x0E39, 0x0E3A, 0x0E3B, 0x0E3C, 0x0E3D, 0x0E3E, 0x0E3F, + 0x0E40, 0x0E41, 0x0E42, 0x0E43, 0x0E44, 0x0E45, 0x0E46, 0x0E47, + 0x0E48, 0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, 0x0E4E, 0x0E4F, + 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, + 0x0E58, 0x0E59, 0x0E5A, 0x0E5B, 0x0E5C, 0x0E5D, 0x0E5E, 0x0E5F, + 0x1838, 0x1839, 0x183A, 0x183B, 0x183C, 0x183D, 0x183E, 0x183F, + 0x1840, 0x1841, 0x1842, 0x1843, 0x1844, 0x1845, 0x1846, 0x1847, + 0x1848, 0x1849, 0x184A, 0x184B, 0x184C, 0x184D, 0x184E, 0x184F, + 0x1850, 0x1851, 0x1852, 0x1853, 0x1854, 0x1855, 0x1856, 0x1857, + 0x1858, 0x1859, 0x185A, 0x185B, 0x185C, 0x185D, 0x185E, 0x185F, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0578, 0x0579, 0x057A, 0x057B, 0x057C, 0x057D, 0x057E, 0x057F, + 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0587, + 0x0588, 0x0589, 0x058A, 0x058B, 0x058C, 0x058D, 0x058E, 0x058F, + 0x0590, 0x0591, 0x0592, 0x0593, 0x0594, 0x0595, 0x0596, 0x0597, + 0x0598, 0x0599, 0x059A, 0x059B, 0x059C, 0x059D, 0x059E, 0x059F, + 0x0F78, 0x0F79, 0x0F7A, 0x0F7B, 0x0F7C, 0x0F7D, 0x0F7E, 0x0F7F, + 0x0F80, 0x0F81, 0x0F82, 0x0F83, 0x0F84, 0x0F85, 0x0F86, 0x0F87, + 0x0F88, 0x0F89, 0x0F8A, 0x0F8B, 0x0F8C, 0x0F8D, 0x0F8E, 0x0F8F, + 0x0F90, 0x0F91, 0x0F92, 0x0F93, 0x0F94, 0x0F95, 0x0F96, 0x0F97, + 0x0F98, 0x0F99, 0x0F9A, 0x0F9B, 0x0F9C, 0x0F9D, 0x0F9E, 0x0F9F, + 0x1978, 0x1979, 0x197A, 0x197B, 0x197C, 0x197D, 0x197E, 0x197F, + 0x1980, 0x1981, 0x1982, 0x1983, 0x1984, 0x1985, 0x1986, 0x1987, + 0x1988, 0x1989, 0x198A, 0x198B, 0x198C, 0x198D, 0x198E, 0x198F, + 0x1990, 0x1991, 0x1992, 0x1993, 0x1994, 0x1995, 0x1996, 0x1997, + 0x1998, 0x1999, 0x199A, 0x199B, 0x199C, 0x199D, 0x199E, 0x199F, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x06B8, 0x06B9, 0x06BA, 0x06BB, 0x06BC, 0x06BD, 0x06BE, 0x06BF, + 0x06C0, 0x06C1, 0x06C2, 0x06C3, 0x06C4, 0x06C5, 0x06C6, 0x06C7, + 0x06C8, 0x06C9, 0x06CA, 0x06CB, 0x06CC, 0x06CD, 0x06CE, 0x06CF, + 0x06D0, 0x06D1, 0x06D2, 0x06D3, 0x06D4, 0x06D5, 0x06D6, 0x06D7, + 0x06D8, 0x06D9, 0x06DA, 0x06DB, 0x06DC, 0x06DD, 0x06DE, 0x06DF, + 0x10B8, 0x10B9, 0x10BA, 0x10BB, 0x10BC, 0x10BD, 0x10BE, 0x10BF, + 0x10C0, 0x10C1, 0x10C2, 0x10C3, 0x10C4, 0x10C5, 0x10C6, 0x10C7, + 0x10C8, 0x10C9, 0x10CA, 0x10CB, 0x10CC, 0x10CD, 0x10CE, 0x10CF, + 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10D7, + 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10DD, 0x10DE, 0x10DF, + 0x1AB8, 0x1AB9, 0x1ABA, 0x1ABB, 0x1ABC, 0x1ABD, 0x1ABE, 0x1ABF, + 0x1AC0, 0x1AC1, 0x1AC2, 0x1AC3, 0x1AC4, 0x1AC5, 0x1AC6, 0x1AC7, + 0x1AC8, 0x1AC9, 0x1ACA, 0x1ACB, 0x1ACC, 0x1ACD, 0x1ACE, 0x1ACF, + 0x1AD0, 0x1AD1, 0x1AD2, 0x1AD3, 0x1AD4, 0x1AD5, 0x1AD6, 0x1AD7, + 0x1AD8, 0x1AD9, 0x1ADA, 0x1ADB, 0x1ADC, 0x1ADD, 0x1ADE, 0x1ADF, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x07F8, 0x07F9, 0x07FA, 0x07FB, 0x07FC, 0x07FD, 0x07FE, 0x07FF, + 0x0800, 0x0801, 0x0802, 0x0803, 0x0804, 0x0805, 0x0806, 0x0807, + 0x0808, 0x0809, 0x080A, 0x080B, 0x080C, 0x080D, 0x080E, 0x080F, + 0x0810, 0x0811, 0x0812, 0x0813, 0x0814, 0x0815, 0x0816, 0x0817, + 0x0818, 0x0819, 0x081A, 0x081B, 0x081C, 0x081D, 0x081E, 0x081F, + 0x11F8, 0x11F9, 0x11FA, 0x11FB, 0x11FC, 0x11FD, 0x11FE, 0x11FF, + 0x1200, 0x1201, 0x1202, 0x1203, 0x1204, 0x1205, 0x1206, 0x1207, + 0x1208, 0x1209, 0x120A, 0x120B, 0x120C, 0x120D, 0x120E, 0x120F, + 0x1210, 0x1211, 0x1212, 0x1213, 0x1214, 0x1215, 0x1216, 0x1217, + 0x1218, 0x1219, 0x121A, 0x121B, 0x121C, 0x121D, 0x121E, 0x121F, + 0x1BF8, 0x1BF9, 0x1BFA, 0x1BFB, 0x1BFC, 0x1BFD, 0x1BFE, 0x1BFF, + 0x1C00, 0x1C01, 0x1C02, 0x1C03, 0x1C04, 0x1C05, 0x1C06, 0x1C07, + 0x1C08, 0x1C09, 0x1C0A, 0x1C0B, 0x1C0C, 0x1C0D, 0x1C0E, 0x1C0F, + 0x1C10, 0x1C11, 0x1C12, 0x1C13, 0x1C14, 0x1C15, 0x1C16, 0x1C17, + 0x1C18, 0x1C19, 0x1C1A, 0x1C1B, 0x1C1C, 0x1C1D, 0x1C1E, 0x1C1F, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0938, 0x0939, 0x093A, 0x093B, 0x093C, 0x093D, 0x093E, 0x093F, + 0x0940, 0x0941, 0x0942, 0x0943, 0x0944, 0x0945, 0x0946, 0x0947, + 0x0948, 0x0949, 0x094A, 0x094B, 0x094C, 0x094D, 0x094E, 0x094F, + 0x0950, 0x0951, 0x0952, 0x0953, 0x0954, 0x0955, 0x0956, 0x0957, + 0x0958, 0x0959, 0x095A, 0x095B, 0x095C, 0x095D, 0x095E, 0x095F, + 0x1338, 0x1339, 0x133A, 0x133B, 0x133C, 0x133D, 0x133E, 0x133F, + 0x1340, 0x1341, 0x1342, 0x1343, 0x1344, 0x1345, 0x1346, 0x1347, + 0x1348, 0x1349, 0x134A, 0x134B, 0x134C, 0x134D, 0x134E, 0x134F, + 0x1350, 0x1351, 0x1352, 0x1353, 0x1354, 0x1355, 0x1356, 0x1357, + 0x1358, 0x1359, 0x135A, 0x135B, 0x135C, 0x135D, 0x135E, 0x135F, + 0x1D38, 0x1D39, 0x1D3A, 0x1D3B, 0x1D3C, 0x1D3D, 0x1D3E, 0x1D3F, + 0x1D40, 0x1D41, 0x1D42, 0x1D43, 0x1D44, 0x1D45, 0x1D46, 0x1D47, + 0x1D48, 0x1D49, 0x1D4A, 0x1D4B, 0x1D4C, 0x1D4D, 0x1D4E, 0x1D4F, + 0x1D50, 0x1D51, 0x1D52, 0x1D53, 0x1D54, 0x1D55, 0x1D56, 0x1D57, + 0x1D58, 0x1D59, 0x1D5A, 0x1D5B, 0x1D5C, 0x1D5D, 0x1D5E, 0x1D5F, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, + 0x0AA0, 0x0AA1, 0x0AA2, 0x0AA3, 0x0AA4, 0x0AA5, 0x0AA6, 0x0AA7, + 0x0AA8, 0x0AA9, 0x0AAA, 0x0AAB, 0x0AAC, 0x0AAD, 0x0AAE, 0x0AAF, + 0x0AB0, 0x0AB1, 0x0AB2, 0x0AB3, 0x0AB4, 0x0AB5, 0x0AB6, 0x0AB7, + 0x0AB8, 0x0AB9, 0x0ABA, 0x0ABB, 0x0ABC, 0x0ABD, 0x0ABE, 0x0ABF, + 0x0AC0, 0x0AC1, 0x0AC2, 0x0AC3, 0x0AC4, 0x0AC5, 0x0AC6, 0x0AC7, + 0x14A0, 0x14A1, 0x14A2, 0x14A3, 0x14A4, 0x14A5, 0x14A6, 0x14A7, + 0x14A8, 0x14A9, 0x14AA, 0x14AB, 0x14AC, 0x14AD, 0x14AE, 0x14AF, + 0x14B0, 0x14B1, 0x14B2, 0x14B3, 0x14B4, 0x14B5, 0x14B6, 0x14B7, + 0x14B8, 0x14B9, 0x14BA, 0x14BB, 0x14BC, 0x14BD, 0x14BE, 0x14BF, + 0x14C0, 0x14C1, 0x14C2, 0x14C3, 0x14C4, 0x14C5, 0x14C6, 0x14C7, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x01E0, 0x01E1, 0x01E2, 0x01E3, 0x01E4, 0x01E5, 0x01E6, 0x01E7, + 0x01E8, 0x01E9, 0x01EA, 0x01EB, 0x01EC, 0x01ED, 0x01EE, 0x01EF, + 0x01F0, 0x01F1, 0x01F2, 0x01F3, 0x01F4, 0x01F5, 0x01F6, 0x01F7, + 0x01F8, 0x01F9, 0x01FA, 0x01FB, 0x01FC, 0x01FD, 0x01FE, 0x01FF, + 0x0200, 0x0201, 0x0202, 0x0203, 0x0204, 0x0205, 0x0206, 0x0207, + 0x0BE0, 0x0BE1, 0x0BE2, 0x0BE3, 0x0BE4, 0x0BE5, 0x0BE6, 0x0BE7, + 0x0BE8, 0x0BE9, 0x0BEA, 0x0BEB, 0x0BEC, 0x0BED, 0x0BEE, 0x0BEF, + 0x0BF0, 0x0BF1, 0x0BF2, 0x0BF3, 0x0BF4, 0x0BF5, 0x0BF6, 0x0BF7, + 0x0BF8, 0x0BF9, 0x0BFA, 0x0BFB, 0x0BFC, 0x0BFD, 0x0BFE, 0x0BFF, + 0x0C00, 0x0C01, 0x0C02, 0x0C03, 0x0C04, 0x0C05, 0x0C06, 0x0C07, + 0x15E0, 0x15E1, 0x15E2, 0x15E3, 0x15E4, 0x15E5, 0x15E6, 0x15E7, + 0x15E8, 0x15E9, 0x15EA, 0x15EB, 0x15EC, 0x15ED, 0x15EE, 0x15EF, + 0x15F0, 0x15F1, 0x15F2, 0x15F3, 0x15F4, 0x15F5, 0x15F6, 0x15F7, + 0x15F8, 0x15F9, 0x15FA, 0x15FB, 0x15FC, 0x15FD, 0x15FE, 0x15FF, + 0x1600, 0x1601, 0x1602, 0x1603, 0x1604, 0x1605, 0x1606, 0x1607, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327, + 0x0328, 0x0329, 0x032A, 0x032B, 0x032C, 0x032D, 0x032E, 0x032F, + 0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337, + 0x0338, 0x0339, 0x033A, 0x033B, 0x033C, 0x033D, 0x033E, 0x033F, + 0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x0345, 0x0346, 0x0347, + 0x0D20, 0x0D21, 0x0D22, 0x0D23, 0x0D24, 0x0D25, 0x0D26, 0x0D27, + 0x0D28, 0x0D29, 0x0D2A, 0x0D2B, 0x0D2C, 0x0D2D, 0x0D2E, 0x0D2F, + 0x0D30, 0x0D31, 0x0D32, 0x0D33, 0x0D34, 0x0D35, 0x0D36, 0x0D37, + 0x0D38, 0x0D39, 0x0D3A, 0x0D3B, 0x0D3C, 0x0D3D, 0x0D3E, 0x0D3F, + 0x0D40, 0x0D41, 0x0D42, 0x0D43, 0x0D44, 0x0D45, 0x0D46, 0x0D47, + 0x1720, 0x1721, 0x1722, 0x1723, 0x1724, 0x1725, 0x1726, 0x1727, + 0x1728, 0x1729, 0x172A, 0x172B, 0x172C, 0x172D, 0x172E, 0x172F, + 0x1730, 0x1731, 0x1732, 0x1733, 0x1734, 0x1735, 0x1736, 0x1737, + 0x1738, 0x1739, 0x173A, 0x173B, 0x173C, 0x173D, 0x173E, 0x173F, + 0x1740, 0x1741, 0x1742, 0x1743, 0x1744, 0x1745, 0x1746, 0x1747, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0460, 0x0461, 0x0462, 0x0463, 0x0464, 0x0465, 0x0466, 0x0467, + 0x0468, 0x0469, 0x046A, 0x046B, 0x046C, 0x046D, 0x046E, 0x046F, + 0x0470, 0x0471, 0x0472, 0x0473, 0x0474, 0x0475, 0x0476, 0x0477, + 0x0478, 0x0479, 0x047A, 0x047B, 0x047C, 0x047D, 0x047E, 0x047F, + 0x0480, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, + 0x0E60, 0x0E61, 0x0E62, 0x0E63, 0x0E64, 0x0E65, 0x0E66, 0x0E67, + 0x0E68, 0x0E69, 0x0E6A, 0x0E6B, 0x0E6C, 0x0E6D, 0x0E6E, 0x0E6F, + 0x0E70, 0x0E71, 0x0E72, 0x0E73, 0x0E74, 0x0E75, 0x0E76, 0x0E77, + 0x0E78, 0x0E79, 0x0E7A, 0x0E7B, 0x0E7C, 0x0E7D, 0x0E7E, 0x0E7F, + 0x0E80, 0x0E81, 0x0E82, 0x0E83, 0x0E84, 0x0E85, 0x0E86, 0x0E87, + 0x1860, 0x1861, 0x1862, 0x1863, 0x1864, 0x1865, 0x1866, 0x1867, + 0x1868, 0x1869, 0x186A, 0x186B, 0x186C, 0x186D, 0x186E, 0x186F, + 0x1870, 0x1871, 0x1872, 0x1873, 0x1874, 0x1875, 0x1876, 0x1877, + 0x1878, 0x1879, 0x187A, 0x187B, 0x187C, 0x187D, 0x187E, 0x187F, + 0x1880, 0x1881, 0x1882, 0x1883, 0x1884, 0x1885, 0x1886, 0x1887, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x05A0, 0x05A1, 0x05A2, 0x05A3, 0x05A4, 0x05A5, 0x05A6, 0x05A7, + 0x05A8, 0x05A9, 0x05AA, 0x05AB, 0x05AC, 0x05AD, 0x05AE, 0x05AF, + 0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7, + 0x05B8, 0x05B9, 0x05BA, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF, + 0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05C4, 0x05C5, 0x05C6, 0x05C7, + 0x0FA0, 0x0FA1, 0x0FA2, 0x0FA3, 0x0FA4, 0x0FA5, 0x0FA6, 0x0FA7, + 0x0FA8, 0x0FA9, 0x0FAA, 0x0FAB, 0x0FAC, 0x0FAD, 0x0FAE, 0x0FAF, + 0x0FB0, 0x0FB1, 0x0FB2, 0x0FB3, 0x0FB4, 0x0FB5, 0x0FB6, 0x0FB7, + 0x0FB8, 0x0FB9, 0x0FBA, 0x0FBB, 0x0FBC, 0x0FBD, 0x0FBE, 0x0FBF, + 0x0FC0, 0x0FC1, 0x0FC2, 0x0FC3, 0x0FC4, 0x0FC5, 0x0FC6, 0x0FC7, + 0x19A0, 0x19A1, 0x19A2, 0x19A3, 0x19A4, 0x19A5, 0x19A6, 0x19A7, + 0x19A8, 0x19A9, 0x19AA, 0x19AB, 0x19AC, 0x19AD, 0x19AE, 0x19AF, + 0x19B0, 0x19B1, 0x19B2, 0x19B3, 0x19B4, 0x19B5, 0x19B6, 0x19B7, + 0x19B8, 0x19B9, 0x19BA, 0x19BB, 0x19BC, 0x19BD, 0x19BE, 0x19BF, + 0x19C0, 0x19C1, 0x19C2, 0x19C3, 0x19C4, 0x19C5, 0x19C6, 0x19C7, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x06E0, 0x06E1, 0x06E2, 0x06E3, 0x06E4, 0x06E5, 0x06E6, 0x06E7, + 0x06E8, 0x06E9, 0x06EA, 0x06EB, 0x06EC, 0x06ED, 0x06EE, 0x06EF, + 0x06F0, 0x06F1, 0x06F2, 0x06F3, 0x06F4, 0x06F5, 0x06F6, 0x06F7, + 0x06F8, 0x06F9, 0x06FA, 0x06FB, 0x06FC, 0x06FD, 0x06FE, 0x06FF, + 0x0700, 0x0701, 0x0702, 0x0703, 0x0704, 0x0705, 0x0706, 0x0707, + 0x10E0, 0x10E1, 0x10E2, 0x10E3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, + 0x10E8, 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10EF, + 0x10F0, 0x10F1, 0x10F2, 0x10F3, 0x10F4, 0x10F5, 0x10F6, 0x10F7, + 0x10F8, 0x10F9, 0x10FA, 0x10FB, 0x10FC, 0x10FD, 0x10FE, 0x10FF, + 0x1100, 0x1101, 0x1102, 0x1103, 0x1104, 0x1105, 0x1106, 0x1107, + 0x1AE0, 0x1AE1, 0x1AE2, 0x1AE3, 0x1AE4, 0x1AE5, 0x1AE6, 0x1AE7, + 0x1AE8, 0x1AE9, 0x1AEA, 0x1AEB, 0x1AEC, 0x1AED, 0x1AEE, 0x1AEF, + 0x1AF0, 0x1AF1, 0x1AF2, 0x1AF3, 0x1AF4, 0x1AF5, 0x1AF6, 0x1AF7, + 0x1AF8, 0x1AF9, 0x1AFA, 0x1AFB, 0x1AFC, 0x1AFD, 0x1AFE, 0x1AFF, + 0x1B00, 0x1B01, 0x1B02, 0x1B03, 0x1B04, 0x1B05, 0x1B06, 0x1B07, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0820, 0x0821, 0x0822, 0x0823, 0x0824, 0x0825, 0x0826, 0x0827, + 0x0828, 0x0829, 0x082A, 0x082B, 0x082C, 0x082D, 0x082E, 0x082F, + 0x0830, 0x0831, 0x0832, 0x0833, 0x0834, 0x0835, 0x0836, 0x0837, + 0x0838, 0x0839, 0x083A, 0x083B, 0x083C, 0x083D, 0x083E, 0x083F, + 0x0840, 0x0841, 0x0842, 0x0843, 0x0844, 0x0845, 0x0846, 0x0847, + 0x1220, 0x1221, 0x1222, 0x1223, 0x1224, 0x1225, 0x1226, 0x1227, + 0x1228, 0x1229, 0x122A, 0x122B, 0x122C, 0x122D, 0x122E, 0x122F, + 0x1230, 0x1231, 0x1232, 0x1233, 0x1234, 0x1235, 0x1236, 0x1237, + 0x1238, 0x1239, 0x123A, 0x123B, 0x123C, 0x123D, 0x123E, 0x123F, + 0x1240, 0x1241, 0x1242, 0x1243, 0x1244, 0x1245, 0x1246, 0x1247, + 0x1C20, 0x1C21, 0x1C22, 0x1C23, 0x1C24, 0x1C25, 0x1C26, 0x1C27, + 0x1C28, 0x1C29, 0x1C2A, 0x1C2B, 0x1C2C, 0x1C2D, 0x1C2E, 0x1C2F, + 0x1C30, 0x1C31, 0x1C32, 0x1C33, 0x1C34, 0x1C35, 0x1C36, 0x1C37, + 0x1C38, 0x1C39, 0x1C3A, 0x1C3B, 0x1C3C, 0x1C3D, 0x1C3E, 0x1C3F, + 0x1C40, 0x1C41, 0x1C42, 0x1C43, 0x1C44, 0x1C45, 0x1C46, 0x1C47, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0960, 0x0961, 0x0962, 0x0963, 0x0964, 0x0965, 0x0966, 0x0967, + 0x0968, 0x0969, 0x096A, 0x096B, 0x096C, 0x096D, 0x096E, 0x096F, + 0x0970, 0x0971, 0x0972, 0x0973, 0x0974, 0x0975, 0x0976, 0x0977, + 0x0978, 0x0979, 0x097A, 0x097B, 0x097C, 0x097D, 0x097E, 0x097F, + 0x0980, 0x0981, 0x0982, 0x0983, 0x0984, 0x0985, 0x0986, 0x0987, + 0x1360, 0x1361, 0x1362, 0x1363, 0x1364, 0x1365, 0x1366, 0x1367, + 0x1368, 0x1369, 0x136A, 0x136B, 0x136C, 0x136D, 0x136E, 0x136F, + 0x1370, 0x1371, 0x1372, 0x1373, 0x1374, 0x1375, 0x1376, 0x1377, + 0x1378, 0x1379, 0x137A, 0x137B, 0x137C, 0x137D, 0x137E, 0x137F, + 0x1380, 0x1381, 0x1382, 0x1383, 0x1384, 0x1385, 0x1386, 0x1387, + 0x1D60, 0x1D61, 0x1D62, 0x1D63, 0x1D64, 0x1D65, 0x1D66, 0x1D67, + 0x1D68, 0x1D69, 0x1D6A, 0x1D6B, 0x1D6C, 0x1D6D, 0x1D6E, 0x1D6F, + 0x1D70, 0x1D71, 0x1D72, 0x1D73, 0x1D74, 0x1D75, 0x1D76, 0x1D77, + 0x1D78, 0x1D79, 0x1D7A, 0x1D7B, 0x1D7C, 0x1D7D, 0x1D7E, 0x1D7F, + 0x1D80, 0x1D81, 0x1D82, 0x1D83, 0x1D84, 0x1D85, 0x1D86, 0x1D87, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, + 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x0AC8, 0x0AC9, 0x0ACA, 0x0ACB, 0x0ACC, 0x0ACD, 0x0ACE, 0x0ACF, + 0x0AD0, 0x0AD1, 0x0AD2, 0x0AD3, 0x0AD4, 0x0AD5, 0x0AD6, 0x0AD7, + 0x0AD8, 0x0AD9, 0x0ADA, 0x0ADB, 0x0ADC, 0x0ADD, 0x0ADE, 0x0ADF, + 0x0AE0, 0x0AE1, 0x0AE2, 0x0AE3, 0x0AE4, 0x0AE5, 0x0AE6, 0x0AE7, + 0x0AE8, 0x0AE9, 0x0AEA, 0x0AEB, 0x0AEC, 0x0AED, 0x0AEE, 0x0AEF, + 0x14C8, 0x14C9, 0x14CA, 0x14CB, 0x14CC, 0x14CD, 0x14CE, 0x14CF, + 0x14D0, 0x14D1, 0x14D2, 0x14D3, 0x14D4, 0x14D5, 0x14D6, 0x14D7, + 0x14D8, 0x14D9, 0x14DA, 0x14DB, 0x14DC, 0x14DD, 0x14DE, 0x14DF, + 0x14E0, 0x14E1, 0x14E2, 0x14E3, 0x14E4, 0x14E5, 0x14E6, 0x14E7, + 0x14E8, 0x14E9, 0x14EA, 0x14EB, 0x14EC, 0x14ED, 0x14EE, 0x14EF, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0208, 0x0209, 0x020A, 0x020B, 0x020C, 0x020D, 0x020E, 0x020F, + 0x0210, 0x0211, 0x0212, 0x0213, 0x0214, 0x0215, 0x0216, 0x0217, + 0x0218, 0x0219, 0x021A, 0x021B, 0x021C, 0x021D, 0x021E, 0x021F, + 0x0220, 0x0221, 0x0222, 0x0223, 0x0224, 0x0225, 0x0226, 0x0227, + 0x0228, 0x0229, 0x022A, 0x022B, 0x022C, 0x022D, 0x022E, 0x022F, + 0x0C08, 0x0C09, 0x0C0A, 0x0C0B, 0x0C0C, 0x0C0D, 0x0C0E, 0x0C0F, + 0x0C10, 0x0C11, 0x0C12, 0x0C13, 0x0C14, 0x0C15, 0x0C16, 0x0C17, + 0x0C18, 0x0C19, 0x0C1A, 0x0C1B, 0x0C1C, 0x0C1D, 0x0C1E, 0x0C1F, + 0x0C20, 0x0C21, 0x0C22, 0x0C23, 0x0C24, 0x0C25, 0x0C26, 0x0C27, + 0x0C28, 0x0C29, 0x0C2A, 0x0C2B, 0x0C2C, 0x0C2D, 0x0C2E, 0x0C2F, + 0x1608, 0x1609, 0x160A, 0x160B, 0x160C, 0x160D, 0x160E, 0x160F, + 0x1610, 0x1611, 0x1612, 0x1613, 0x1614, 0x1615, 0x1616, 0x1617, + 0x1618, 0x1619, 0x161A, 0x161B, 0x161C, 0x161D, 0x161E, 0x161F, + 0x1620, 0x1621, 0x1622, 0x1623, 0x1624, 0x1625, 0x1626, 0x1627, + 0x1628, 0x1629, 0x162A, 0x162B, 0x162C, 0x162D, 0x162E, 0x162F, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0348, 0x0349, 0x034A, 0x034B, 0x034C, 0x034D, 0x034E, 0x034F, + 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357, + 0x0358, 0x0359, 0x035A, 0x035B, 0x035C, 0x035D, 0x035E, 0x035F, + 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367, + 0x0368, 0x0369, 0x036A, 0x036B, 0x036C, 0x036D, 0x036E, 0x036F, + 0x0D48, 0x0D49, 0x0D4A, 0x0D4B, 0x0D4C, 0x0D4D, 0x0D4E, 0x0D4F, + 0x0D50, 0x0D51, 0x0D52, 0x0D53, 0x0D54, 0x0D55, 0x0D56, 0x0D57, + 0x0D58, 0x0D59, 0x0D5A, 0x0D5B, 0x0D5C, 0x0D5D, 0x0D5E, 0x0D5F, + 0x0D60, 0x0D61, 0x0D62, 0x0D63, 0x0D64, 0x0D65, 0x0D66, 0x0D67, + 0x0D68, 0x0D69, 0x0D6A, 0x0D6B, 0x0D6C, 0x0D6D, 0x0D6E, 0x0D6F, + 0x1748, 0x1749, 0x174A, 0x174B, 0x174C, 0x174D, 0x174E, 0x174F, + 0x1750, 0x1751, 0x1752, 0x1753, 0x1754, 0x1755, 0x1756, 0x1757, + 0x1758, 0x1759, 0x175A, 0x175B, 0x175C, 0x175D, 0x175E, 0x175F, + 0x1760, 0x1761, 0x1762, 0x1763, 0x1764, 0x1765, 0x1766, 0x1767, + 0x1768, 0x1769, 0x176A, 0x176B, 0x176C, 0x176D, 0x176E, 0x176F, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0488, 0x0489, 0x048A, 0x048B, 0x048C, 0x048D, 0x048E, 0x048F, + 0x0490, 0x0491, 0x0492, 0x0493, 0x0494, 0x0495, 0x0496, 0x0497, + 0x0498, 0x0499, 0x049A, 0x049B, 0x049C, 0x049D, 0x049E, 0x049F, + 0x04A0, 0x04A1, 0x04A2, 0x04A3, 0x04A4, 0x04A5, 0x04A6, 0x04A7, + 0x04A8, 0x04A9, 0x04AA, 0x04AB, 0x04AC, 0x04AD, 0x04AE, 0x04AF, + 0x0E88, 0x0E89, 0x0E8A, 0x0E8B, 0x0E8C, 0x0E8D, 0x0E8E, 0x0E8F, + 0x0E90, 0x0E91, 0x0E92, 0x0E93, 0x0E94, 0x0E95, 0x0E96, 0x0E97, + 0x0E98, 0x0E99, 0x0E9A, 0x0E9B, 0x0E9C, 0x0E9D, 0x0E9E, 0x0E9F, + 0x0EA0, 0x0EA1, 0x0EA2, 0x0EA3, 0x0EA4, 0x0EA5, 0x0EA6, 0x0EA7, + 0x0EA8, 0x0EA9, 0x0EAA, 0x0EAB, 0x0EAC, 0x0EAD, 0x0EAE, 0x0EAF, + 0x1888, 0x1889, 0x188A, 0x188B, 0x188C, 0x188D, 0x188E, 0x188F, + 0x1890, 0x1891, 0x1892, 0x1893, 0x1894, 0x1895, 0x1896, 0x1897, + 0x1898, 0x1899, 0x189A, 0x189B, 0x189C, 0x189D, 0x189E, 0x189F, + 0x18A0, 0x18A1, 0x18A2, 0x18A3, 0x18A4, 0x18A5, 0x18A6, 0x18A7, + 0x18A8, 0x18A9, 0x18AA, 0x18AB, 0x18AC, 0x18AD, 0x18AE, 0x18AF, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x05C8, 0x05C9, 0x05CA, 0x05CB, 0x05CC, 0x05CD, 0x05CE, 0x05CF, + 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, + 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, + 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, + 0x05E8, 0x05E9, 0x05EA, 0x05EB, 0x05EC, 0x05ED, 0x05EE, 0x05EF, + 0x0FC8, 0x0FC9, 0x0FCA, 0x0FCB, 0x0FCC, 0x0FCD, 0x0FCE, 0x0FCF, + 0x0FD0, 0x0FD1, 0x0FD2, 0x0FD3, 0x0FD4, 0x0FD5, 0x0FD6, 0x0FD7, + 0x0FD8, 0x0FD9, 0x0FDA, 0x0FDB, 0x0FDC, 0x0FDD, 0x0FDE, 0x0FDF, + 0x0FE0, 0x0FE1, 0x0FE2, 0x0FE3, 0x0FE4, 0x0FE5, 0x0FE6, 0x0FE7, + 0x0FE8, 0x0FE9, 0x0FEA, 0x0FEB, 0x0FEC, 0x0FED, 0x0FEE, 0x0FEF, + 0x19C8, 0x19C9, 0x19CA, 0x19CB, 0x19CC, 0x19CD, 0x19CE, 0x19CF, + 0x19D0, 0x19D1, 0x19D2, 0x19D3, 0x19D4, 0x19D5, 0x19D6, 0x19D7, + 0x19D8, 0x19D9, 0x19DA, 0x19DB, 0x19DC, 0x19DD, 0x19DE, 0x19DF, + 0x19E0, 0x19E1, 0x19E2, 0x19E3, 0x19E4, 0x19E5, 0x19E6, 0x19E7, + 0x19E8, 0x19E9, 0x19EA, 0x19EB, 0x19EC, 0x19ED, 0x19EE, 0x19EF, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0708, 0x0709, 0x070A, 0x070B, 0x070C, 0x070D, 0x070E, 0x070F, + 0x0710, 0x0711, 0x0712, 0x0713, 0x0714, 0x0715, 0x0716, 0x0717, + 0x0718, 0x0719, 0x071A, 0x071B, 0x071C, 0x071D, 0x071E, 0x071F, + 0x0720, 0x0721, 0x0722, 0x0723, 0x0724, 0x0725, 0x0726, 0x0727, + 0x0728, 0x0729, 0x072A, 0x072B, 0x072C, 0x072D, 0x072E, 0x072F, + 0x1108, 0x1109, 0x110A, 0x110B, 0x110C, 0x110D, 0x110E, 0x110F, + 0x1110, 0x1111, 0x1112, 0x1113, 0x1114, 0x1115, 0x1116, 0x1117, + 0x1118, 0x1119, 0x111A, 0x111B, 0x111C, 0x111D, 0x111E, 0x111F, + 0x1120, 0x1121, 0x1122, 0x1123, 0x1124, 0x1125, 0x1126, 0x1127, + 0x1128, 0x1129, 0x112A, 0x112B, 0x112C, 0x112D, 0x112E, 0x112F, + 0x1B08, 0x1B09, 0x1B0A, 0x1B0B, 0x1B0C, 0x1B0D, 0x1B0E, 0x1B0F, + 0x1B10, 0x1B11, 0x1B12, 0x1B13, 0x1B14, 0x1B15, 0x1B16, 0x1B17, + 0x1B18, 0x1B19, 0x1B1A, 0x1B1B, 0x1B1C, 0x1B1D, 0x1B1E, 0x1B1F, + 0x1B20, 0x1B21, 0x1B22, 0x1B23, 0x1B24, 0x1B25, 0x1B26, 0x1B27, + 0x1B28, 0x1B29, 0x1B2A, 0x1B2B, 0x1B2C, 0x1B2D, 0x1B2E, 0x1B2F, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0848, 0x0849, 0x084A, 0x084B, 0x084C, 0x084D, 0x084E, 0x084F, + 0x0850, 0x0851, 0x0852, 0x0853, 0x0854, 0x0855, 0x0856, 0x0857, + 0x0858, 0x0859, 0x085A, 0x085B, 0x085C, 0x085D, 0x085E, 0x085F, + 0x0860, 0x0861, 0x0862, 0x0863, 0x0864, 0x0865, 0x0866, 0x0867, + 0x0868, 0x0869, 0x086A, 0x086B, 0x086C, 0x086D, 0x086E, 0x086F, + 0x1248, 0x1249, 0x124A, 0x124B, 0x124C, 0x124D, 0x124E, 0x124F, + 0x1250, 0x1251, 0x1252, 0x1253, 0x1254, 0x1255, 0x1256, 0x1257, + 0x1258, 0x1259, 0x125A, 0x125B, 0x125C, 0x125D, 0x125E, 0x125F, + 0x1260, 0x1261, 0x1262, 0x1263, 0x1264, 0x1265, 0x1266, 0x1267, + 0x1268, 0x1269, 0x126A, 0x126B, 0x126C, 0x126D, 0x126E, 0x126F, + 0x1C48, 0x1C49, 0x1C4A, 0x1C4B, 0x1C4C, 0x1C4D, 0x1C4E, 0x1C4F, + 0x1C50, 0x1C51, 0x1C52, 0x1C53, 0x1C54, 0x1C55, 0x1C56, 0x1C57, + 0x1C58, 0x1C59, 0x1C5A, 0x1C5B, 0x1C5C, 0x1C5D, 0x1C5E, 0x1C5F, + 0x1C60, 0x1C61, 0x1C62, 0x1C63, 0x1C64, 0x1C65, 0x1C66, 0x1C67, + 0x1C68, 0x1C69, 0x1C6A, 0x1C6B, 0x1C6C, 0x1C6D, 0x1C6E, 0x1C6F, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0988, 0x0989, 0x098A, 0x098B, 0x098C, 0x098D, 0x098E, 0x098F, + 0x0990, 0x0991, 0x0992, 0x0993, 0x0994, 0x0995, 0x0996, 0x0997, + 0x0998, 0x0999, 0x099A, 0x099B, 0x099C, 0x099D, 0x099E, 0x099F, + 0x09A0, 0x09A1, 0x09A2, 0x09A3, 0x09A4, 0x09A5, 0x09A6, 0x09A7, + 0x09A8, 0x09A9, 0x09AA, 0x09AB, 0x09AC, 0x09AD, 0x09AE, 0x09AF, + 0x1388, 0x1389, 0x138A, 0x138B, 0x138C, 0x138D, 0x138E, 0x138F, + 0x1390, 0x1391, 0x1392, 0x1393, 0x1394, 0x1395, 0x1396, 0x1397, + 0x1398, 0x1399, 0x139A, 0x139B, 0x139C, 0x139D, 0x139E, 0x139F, + 0x13A0, 0x13A1, 0x13A2, 0x13A3, 0x13A4, 0x13A5, 0x13A6, 0x13A7, + 0x13A8, 0x13A9, 0x13AA, 0x13AB, 0x13AC, 0x13AD, 0x13AE, 0x13AF, + 0x1D88, 0x1D89, 0x1D8A, 0x1D8B, 0x1D8C, 0x1D8D, 0x1D8E, 0x1D8F, + 0x1D90, 0x1D91, 0x1D92, 0x1D93, 0x1D94, 0x1D95, 0x1D96, 0x1D97, + 0x1D98, 0x1D99, 0x1D9A, 0x1D9B, 0x1D9C, 0x1D9D, 0x1D9E, 0x1D9F, + 0x1DA0, 0x1DA1, 0x1DA2, 0x1DA3, 0x1DA4, 0x1DA5, 0x1DA6, 0x1DA7, + 0x1DA8, 0x1DA9, 0x1DAA, 0x1DAB, 0x1DAC, 0x1DAD, 0x1DAE, 0x1DAF, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, + 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF, + 0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, + 0x0108, 0x0109, 0x010A, 0x010B, 0x010C, 0x010D, 0x010E, 0x010F, + 0x0110, 0x0111, 0x0112, 0x0113, 0x0114, 0x0115, 0x0116, 0x0117, + 0x0AF0, 0x0AF1, 0x0AF2, 0x0AF3, 0x0AF4, 0x0AF5, 0x0AF6, 0x0AF7, + 0x0AF8, 0x0AF9, 0x0AFA, 0x0AFB, 0x0AFC, 0x0AFD, 0x0AFE, 0x0AFF, + 0x0B00, 0x0B01, 0x0B02, 0x0B03, 0x0B04, 0x0B05, 0x0B06, 0x0B07, + 0x0B08, 0x0B09, 0x0B0A, 0x0B0B, 0x0B0C, 0x0B0D, 0x0B0E, 0x0B0F, + 0x0B10, 0x0B11, 0x0B12, 0x0B13, 0x0B14, 0x0B15, 0x0B16, 0x0B17, + 0x14F0, 0x14F1, 0x14F2, 0x14F3, 0x14F4, 0x14F5, 0x14F6, 0x14F7, + 0x14F8, 0x14F9, 0x14FA, 0x14FB, 0x14FC, 0x14FD, 0x14FE, 0x14FF, + 0x1500, 0x1501, 0x1502, 0x1503, 0x1504, 0x1505, 0x1506, 0x1507, + 0x1508, 0x1509, 0x150A, 0x150B, 0x150C, 0x150D, 0x150E, 0x150F, + 0x1510, 0x1511, 0x1512, 0x1513, 0x1514, 0x1515, 0x1516, 0x1517, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0230, 0x0231, 0x0232, 0x0233, 0x0234, 0x0235, 0x0236, 0x0237, + 0x0238, 0x0239, 0x023A, 0x023B, 0x023C, 0x023D, 0x023E, 0x023F, + 0x0240, 0x0241, 0x0242, 0x0243, 0x0244, 0x0245, 0x0246, 0x0247, + 0x0248, 0x0249, 0x024A, 0x024B, 0x024C, 0x024D, 0x024E, 0x024F, + 0x0250, 0x0251, 0x0252, 0x0253, 0x0254, 0x0255, 0x0256, 0x0257, + 0x0C30, 0x0C31, 0x0C32, 0x0C33, 0x0C34, 0x0C35, 0x0C36, 0x0C37, + 0x0C38, 0x0C39, 0x0C3A, 0x0C3B, 0x0C3C, 0x0C3D, 0x0C3E, 0x0C3F, + 0x0C40, 0x0C41, 0x0C42, 0x0C43, 0x0C44, 0x0C45, 0x0C46, 0x0C47, + 0x0C48, 0x0C49, 0x0C4A, 0x0C4B, 0x0C4C, 0x0C4D, 0x0C4E, 0x0C4F, + 0x0C50, 0x0C51, 0x0C52, 0x0C53, 0x0C54, 0x0C55, 0x0C56, 0x0C57, + 0x1630, 0x1631, 0x1632, 0x1633, 0x1634, 0x1635, 0x1636, 0x1637, + 0x1638, 0x1639, 0x163A, 0x163B, 0x163C, 0x163D, 0x163E, 0x163F, + 0x1640, 0x1641, 0x1642, 0x1643, 0x1644, 0x1645, 0x1646, 0x1647, + 0x1648, 0x1649, 0x164A, 0x164B, 0x164C, 0x164D, 0x164E, 0x164F, + 0x1650, 0x1651, 0x1652, 0x1653, 0x1654, 0x1655, 0x1656, 0x1657, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0370, 0x0371, 0x0372, 0x0373, 0x0374, 0x0375, 0x0376, 0x0377, + 0x0378, 0x0379, 0x037A, 0x037B, 0x037C, 0x037D, 0x037E, 0x037F, + 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0386, 0x0387, + 0x0388, 0x0389, 0x038A, 0x038B, 0x038C, 0x038D, 0x038E, 0x038F, + 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, + 0x0D70, 0x0D71, 0x0D72, 0x0D73, 0x0D74, 0x0D75, 0x0D76, 0x0D77, + 0x0D78, 0x0D79, 0x0D7A, 0x0D7B, 0x0D7C, 0x0D7D, 0x0D7E, 0x0D7F, + 0x0D80, 0x0D81, 0x0D82, 0x0D83, 0x0D84, 0x0D85, 0x0D86, 0x0D87, + 0x0D88, 0x0D89, 0x0D8A, 0x0D8B, 0x0D8C, 0x0D8D, 0x0D8E, 0x0D8F, + 0x0D90, 0x0D91, 0x0D92, 0x0D93, 0x0D94, 0x0D95, 0x0D96, 0x0D97, + 0x1770, 0x1771, 0x1772, 0x1773, 0x1774, 0x1775, 0x1776, 0x1777, + 0x1778, 0x1779, 0x177A, 0x177B, 0x177C, 0x177D, 0x177E, 0x177F, + 0x1780, 0x1781, 0x1782, 0x1783, 0x1784, 0x1785, 0x1786, 0x1787, + 0x1788, 0x1789, 0x178A, 0x178B, 0x178C, 0x178D, 0x178E, 0x178F, + 0x1790, 0x1791, 0x1792, 0x1793, 0x1794, 0x1795, 0x1796, 0x1797, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x04B0, 0x04B1, 0x04B2, 0x04B3, 0x04B4, 0x04B5, 0x04B6, 0x04B7, + 0x04B8, 0x04B9, 0x04BA, 0x04BB, 0x04BC, 0x04BD, 0x04BE, 0x04BF, + 0x04C0, 0x04C1, 0x04C2, 0x04C3, 0x04C4, 0x04C5, 0x04C6, 0x04C7, + 0x04C8, 0x04C9, 0x04CA, 0x04CB, 0x04CC, 0x04CD, 0x04CE, 0x04CF, + 0x04D0, 0x04D1, 0x04D2, 0x04D3, 0x04D4, 0x04D5, 0x04D6, 0x04D7, + 0x0EB0, 0x0EB1, 0x0EB2, 0x0EB3, 0x0EB4, 0x0EB5, 0x0EB6, 0x0EB7, + 0x0EB8, 0x0EB9, 0x0EBA, 0x0EBB, 0x0EBC, 0x0EBD, 0x0EBE, 0x0EBF, + 0x0EC0, 0x0EC1, 0x0EC2, 0x0EC3, 0x0EC4, 0x0EC5, 0x0EC6, 0x0EC7, + 0x0EC8, 0x0EC9, 0x0ECA, 0x0ECB, 0x0ECC, 0x0ECD, 0x0ECE, 0x0ECF, + 0x0ED0, 0x0ED1, 0x0ED2, 0x0ED3, 0x0ED4, 0x0ED5, 0x0ED6, 0x0ED7, + 0x18B0, 0x18B1, 0x18B2, 0x18B3, 0x18B4, 0x18B5, 0x18B6, 0x18B7, + 0x18B8, 0x18B9, 0x18BA, 0x18BB, 0x18BC, 0x18BD, 0x18BE, 0x18BF, + 0x18C0, 0x18C1, 0x18C2, 0x18C3, 0x18C4, 0x18C5, 0x18C6, 0x18C7, + 0x18C8, 0x18C9, 0x18CA, 0x18CB, 0x18CC, 0x18CD, 0x18CE, 0x18CF, + 0x18D0, 0x18D1, 0x18D2, 0x18D3, 0x18D4, 0x18D5, 0x18D6, 0x18D7, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x05F0, 0x05F1, 0x05F2, 0x05F3, 0x05F4, 0x05F5, 0x05F6, 0x05F7, + 0x05F8, 0x05F9, 0x05FA, 0x05FB, 0x05FC, 0x05FD, 0x05FE, 0x05FF, + 0x0600, 0x0601, 0x0602, 0x0603, 0x0604, 0x0605, 0x0606, 0x0607, + 0x0608, 0x0609, 0x060A, 0x060B, 0x060C, 0x060D, 0x060E, 0x060F, + 0x0610, 0x0611, 0x0612, 0x0613, 0x0614, 0x0615, 0x0616, 0x0617, + 0x0FF0, 0x0FF1, 0x0FF2, 0x0FF3, 0x0FF4, 0x0FF5, 0x0FF6, 0x0FF7, + 0x0FF8, 0x0FF9, 0x0FFA, 0x0FFB, 0x0FFC, 0x0FFD, 0x0FFE, 0x0FFF, + 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007, + 0x1008, 0x1009, 0x100A, 0x100B, 0x100C, 0x100D, 0x100E, 0x100F, + 0x1010, 0x1011, 0x1012, 0x1013, 0x1014, 0x1015, 0x1016, 0x1017, + 0x19F0, 0x19F1, 0x19F2, 0x19F3, 0x19F4, 0x19F5, 0x19F6, 0x19F7, + 0x19F8, 0x19F9, 0x19FA, 0x19FB, 0x19FC, 0x19FD, 0x19FE, 0x19FF, + 0x1A00, 0x1A01, 0x1A02, 0x1A03, 0x1A04, 0x1A05, 0x1A06, 0x1A07, + 0x1A08, 0x1A09, 0x1A0A, 0x1A0B, 0x1A0C, 0x1A0D, 0x1A0E, 0x1A0F, + 0x1A10, 0x1A11, 0x1A12, 0x1A13, 0x1A14, 0x1A15, 0x1A16, 0x1A17, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0730, 0x0731, 0x0732, 0x0733, 0x0734, 0x0735, 0x0736, 0x0737, + 0x0738, 0x0739, 0x073A, 0x073B, 0x073C, 0x073D, 0x073E, 0x073F, + 0x0740, 0x0741, 0x0742, 0x0743, 0x0744, 0x0745, 0x0746, 0x0747, + 0x0748, 0x0749, 0x074A, 0x074B, 0x074C, 0x074D, 0x074E, 0x074F, + 0x0750, 0x0751, 0x0752, 0x0753, 0x0754, 0x0755, 0x0756, 0x0757, + 0x1130, 0x1131, 0x1132, 0x1133, 0x1134, 0x1135, 0x1136, 0x1137, + 0x1138, 0x1139, 0x113A, 0x113B, 0x113C, 0x113D, 0x113E, 0x113F, + 0x1140, 0x1141, 0x1142, 0x1143, 0x1144, 0x1145, 0x1146, 0x1147, + 0x1148, 0x1149, 0x114A, 0x114B, 0x114C, 0x114D, 0x114E, 0x114F, + 0x1150, 0x1151, 0x1152, 0x1153, 0x1154, 0x1155, 0x1156, 0x1157, + 0x1B30, 0x1B31, 0x1B32, 0x1B33, 0x1B34, 0x1B35, 0x1B36, 0x1B37, + 0x1B38, 0x1B39, 0x1B3A, 0x1B3B, 0x1B3C, 0x1B3D, 0x1B3E, 0x1B3F, + 0x1B40, 0x1B41, 0x1B42, 0x1B43, 0x1B44, 0x1B45, 0x1B46, 0x1B47, + 0x1B48, 0x1B49, 0x1B4A, 0x1B4B, 0x1B4C, 0x1B4D, 0x1B4E, 0x1B4F, + 0x1B50, 0x1B51, 0x1B52, 0x1B53, 0x1B54, 0x1B55, 0x1B56, 0x1B57, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0870, 0x0871, 0x0872, 0x0873, 0x0874, 0x0875, 0x0876, 0x0877, + 0x0878, 0x0879, 0x087A, 0x087B, 0x087C, 0x087D, 0x087E, 0x087F, + 0x0880, 0x0881, 0x0882, 0x0883, 0x0884, 0x0885, 0x0886, 0x0887, + 0x0888, 0x0889, 0x088A, 0x088B, 0x088C, 0x088D, 0x088E, 0x088F, + 0x0890, 0x0891, 0x0892, 0x0893, 0x0894, 0x0895, 0x0896, 0x0897, + 0x1270, 0x1271, 0x1272, 0x1273, 0x1274, 0x1275, 0x1276, 0x1277, + 0x1278, 0x1279, 0x127A, 0x127B, 0x127C, 0x127D, 0x127E, 0x127F, + 0x1280, 0x1281, 0x1282, 0x1283, 0x1284, 0x1285, 0x1286, 0x1287, + 0x1288, 0x1289, 0x128A, 0x128B, 0x128C, 0x128D, 0x128E, 0x128F, + 0x1290, 0x1291, 0x1292, 0x1293, 0x1294, 0x1295, 0x1296, 0x1297, + 0x1C70, 0x1C71, 0x1C72, 0x1C73, 0x1C74, 0x1C75, 0x1C76, 0x1C77, + 0x1C78, 0x1C79, 0x1C7A, 0x1C7B, 0x1C7C, 0x1C7D, 0x1C7E, 0x1C7F, + 0x1C80, 0x1C81, 0x1C82, 0x1C83, 0x1C84, 0x1C85, 0x1C86, 0x1C87, + 0x1C88, 0x1C89, 0x1C8A, 0x1C8B, 0x1C8C, 0x1C8D, 0x1C8E, 0x1C8F, + 0x1C90, 0x1C91, 0x1C92, 0x1C93, 0x1C94, 0x1C95, 0x1C96, 0x1C97, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x09B0, 0x09B1, 0x09B2, 0x09B3, 0x09B4, 0x09B5, 0x09B6, 0x09B7, + 0x09B8, 0x09B9, 0x09BA, 0x09BB, 0x09BC, 0x09BD, 0x09BE, 0x09BF, + 0x09C0, 0x09C1, 0x09C2, 0x09C3, 0x09C4, 0x09C5, 0x09C6, 0x09C7, + 0x09C8, 0x09C9, 0x09CA, 0x09CB, 0x09CC, 0x09CD, 0x09CE, 0x09CF, + 0x09D0, 0x09D1, 0x09D2, 0x09D3, 0x09D4, 0x09D5, 0x09D6, 0x09D7, + 0x13B0, 0x13B1, 0x13B2, 0x13B3, 0x13B4, 0x13B5, 0x13B6, 0x13B7, + 0x13B8, 0x13B9, 0x13BA, 0x13BB, 0x13BC, 0x13BD, 0x13BE, 0x13BF, + 0x13C0, 0x13C1, 0x13C2, 0x13C3, 0x13C4, 0x13C5, 0x13C6, 0x13C7, + 0x13C8, 0x13C9, 0x13CA, 0x13CB, 0x13CC, 0x13CD, 0x13CE, 0x13CF, + 0x13D0, 0x13D1, 0x13D2, 0x13D3, 0x13D4, 0x13D5, 0x13D6, 0x13D7, + 0x1DB0, 0x1DB1, 0x1DB2, 0x1DB3, 0x1DB4, 0x1DB5, 0x1DB6, 0x1DB7, + 0x1DB8, 0x1DB9, 0x1DBA, 0x1DBB, 0x1DBC, 0x1DBD, 0x1DBE, 0x1DBF, + 0x1DC0, 0x1DC1, 0x1DC2, 0x1DC3, 0x1DC4, 0x1DC5, 0x1DC6, 0x1DC7, + 0x1DC8, 0x1DC9, 0x1DCA, 0x1DCB, 0x1DCC, 0x1DCD, 0x1DCE, 0x1DCF, + 0x1DD0, 0x1DD1, 0x1DD2, 0x1DD3, 0x1DD4, 0x1DD5, 0x1DD6, 0x1DD7, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0118, 0x0119, 0x011A, 0x011B, 0x011C, 0x011D, 0x011E, 0x011F, + 0x0120, 0x0121, 0x0122, 0x0123, 0x0124, 0x0125, 0x0126, 0x0127, + 0x0128, 0x0129, 0x012A, 0x012B, 0x012C, 0x012D, 0x012E, 0x012F, + 0x0130, 0x0131, 0x0132, 0x0133, 0x0134, 0x0135, 0x0136, 0x0137, + 0x0138, 0x0139, 0x013A, 0x013B, 0x013C, 0x013D, 0x013E, 0x013F, + 0x0B18, 0x0B19, 0x0B1A, 0x0B1B, 0x0B1C, 0x0B1D, 0x0B1E, 0x0B1F, + 0x0B20, 0x0B21, 0x0B22, 0x0B23, 0x0B24, 0x0B25, 0x0B26, 0x0B27, + 0x0B28, 0x0B29, 0x0B2A, 0x0B2B, 0x0B2C, 0x0B2D, 0x0B2E, 0x0B2F, + 0x0B30, 0x0B31, 0x0B32, 0x0B33, 0x0B34, 0x0B35, 0x0B36, 0x0B37, + 0x0B38, 0x0B39, 0x0B3A, 0x0B3B, 0x0B3C, 0x0B3D, 0x0B3E, 0x0B3F, + 0x1518, 0x1519, 0x151A, 0x151B, 0x151C, 0x151D, 0x151E, 0x151F, + 0x1520, 0x1521, 0x1522, 0x1523, 0x1524, 0x1525, 0x1526, 0x1527, + 0x1528, 0x1529, 0x152A, 0x152B, 0x152C, 0x152D, 0x152E, 0x152F, + 0x1530, 0x1531, 0x1532, 0x1533, 0x1534, 0x1535, 0x1536, 0x1537, + 0x1538, 0x1539, 0x153A, 0x153B, 0x153C, 0x153D, 0x153E, 0x153F, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0258, 0x0259, 0x025A, 0x025B, 0x025C, 0x025D, 0x025E, 0x025F, + 0x0260, 0x0261, 0x0262, 0x0263, 0x0264, 0x0265, 0x0266, 0x0267, + 0x0268, 0x0269, 0x026A, 0x026B, 0x026C, 0x026D, 0x026E, 0x026F, + 0x0270, 0x0271, 0x0272, 0x0273, 0x0274, 0x0275, 0x0276, 0x0277, + 0x0278, 0x0279, 0x027A, 0x027B, 0x027C, 0x027D, 0x027E, 0x027F, + 0x0C58, 0x0C59, 0x0C5A, 0x0C5B, 0x0C5C, 0x0C5D, 0x0C5E, 0x0C5F, + 0x0C60, 0x0C61, 0x0C62, 0x0C63, 0x0C64, 0x0C65, 0x0C66, 0x0C67, + 0x0C68, 0x0C69, 0x0C6A, 0x0C6B, 0x0C6C, 0x0C6D, 0x0C6E, 0x0C6F, + 0x0C70, 0x0C71, 0x0C72, 0x0C73, 0x0C74, 0x0C75, 0x0C76, 0x0C77, + 0x0C78, 0x0C79, 0x0C7A, 0x0C7B, 0x0C7C, 0x0C7D, 0x0C7E, 0x0C7F, + 0x1658, 0x1659, 0x165A, 0x165B, 0x165C, 0x165D, 0x165E, 0x165F, + 0x1660, 0x1661, 0x1662, 0x1663, 0x1664, 0x1665, 0x1666, 0x1667, + 0x1668, 0x1669, 0x166A, 0x166B, 0x166C, 0x166D, 0x166E, 0x166F, + 0x1670, 0x1671, 0x1672, 0x1673, 0x1674, 0x1675, 0x1676, 0x1677, + 0x1678, 0x1679, 0x167A, 0x167B, 0x167C, 0x167D, 0x167E, 0x167F, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, + 0x03A0, 0x03A1, 0x03A2, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, + 0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF, + 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, + 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, + 0x0D98, 0x0D99, 0x0D9A, 0x0D9B, 0x0D9C, 0x0D9D, 0x0D9E, 0x0D9F, + 0x0DA0, 0x0DA1, 0x0DA2, 0x0DA3, 0x0DA4, 0x0DA5, 0x0DA6, 0x0DA7, + 0x0DA8, 0x0DA9, 0x0DAA, 0x0DAB, 0x0DAC, 0x0DAD, 0x0DAE, 0x0DAF, + 0x0DB0, 0x0DB1, 0x0DB2, 0x0DB3, 0x0DB4, 0x0DB5, 0x0DB6, 0x0DB7, + 0x0DB8, 0x0DB9, 0x0DBA, 0x0DBB, 0x0DBC, 0x0DBD, 0x0DBE, 0x0DBF, + 0x1798, 0x1799, 0x179A, 0x179B, 0x179C, 0x179D, 0x179E, 0x179F, + 0x17A0, 0x17A1, 0x17A2, 0x17A3, 0x17A4, 0x17A5, 0x17A6, 0x17A7, + 0x17A8, 0x17A9, 0x17AA, 0x17AB, 0x17AC, 0x17AD, 0x17AE, 0x17AF, + 0x17B0, 0x17B1, 0x17B2, 0x17B3, 0x17B4, 0x17B5, 0x17B6, 0x17B7, + 0x17B8, 0x17B9, 0x17BA, 0x17BB, 0x17BC, 0x17BD, 0x17BE, 0x17BF, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x04D8, 0x04D9, 0x04DA, 0x04DB, 0x04DC, 0x04DD, 0x04DE, 0x04DF, + 0x04E0, 0x04E1, 0x04E2, 0x04E3, 0x04E4, 0x04E5, 0x04E6, 0x04E7, + 0x04E8, 0x04E9, 0x04EA, 0x04EB, 0x04EC, 0x04ED, 0x04EE, 0x04EF, + 0x04F0, 0x04F1, 0x04F2, 0x04F3, 0x04F4, 0x04F5, 0x04F6, 0x04F7, + 0x04F8, 0x04F9, 0x04FA, 0x04FB, 0x04FC, 0x04FD, 0x04FE, 0x04FF, + 0x0ED8, 0x0ED9, 0x0EDA, 0x0EDB, 0x0EDC, 0x0EDD, 0x0EDE, 0x0EDF, + 0x0EE0, 0x0EE1, 0x0EE2, 0x0EE3, 0x0EE4, 0x0EE5, 0x0EE6, 0x0EE7, + 0x0EE8, 0x0EE9, 0x0EEA, 0x0EEB, 0x0EEC, 0x0EED, 0x0EEE, 0x0EEF, + 0x0EF0, 0x0EF1, 0x0EF2, 0x0EF3, 0x0EF4, 0x0EF5, 0x0EF6, 0x0EF7, + 0x0EF8, 0x0EF9, 0x0EFA, 0x0EFB, 0x0EFC, 0x0EFD, 0x0EFE, 0x0EFF, + 0x18D8, 0x18D9, 0x18DA, 0x18DB, 0x18DC, 0x18DD, 0x18DE, 0x18DF, + 0x18E0, 0x18E1, 0x18E2, 0x18E3, 0x18E4, 0x18E5, 0x18E6, 0x18E7, + 0x18E8, 0x18E9, 0x18EA, 0x18EB, 0x18EC, 0x18ED, 0x18EE, 0x18EF, + 0x18F0, 0x18F1, 0x18F2, 0x18F3, 0x18F4, 0x18F5, 0x18F6, 0x18F7, + 0x18F8, 0x18F9, 0x18FA, 0x18FB, 0x18FC, 0x18FD, 0x18FE, 0x18FF, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0618, 0x0619, 0x061A, 0x061B, 0x061C, 0x061D, 0x061E, 0x061F, + 0x0620, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, + 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, + 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637, + 0x0638, 0x0639, 0x063A, 0x063B, 0x063C, 0x063D, 0x063E, 0x063F, + 0x1018, 0x1019, 0x101A, 0x101B, 0x101C, 0x101D, 0x101E, 0x101F, + 0x1020, 0x1021, 0x1022, 0x1023, 0x1024, 0x1025, 0x1026, 0x1027, + 0x1028, 0x1029, 0x102A, 0x102B, 0x102C, 0x102D, 0x102E, 0x102F, + 0x1030, 0x1031, 0x1032, 0x1033, 0x1034, 0x1035, 0x1036, 0x1037, + 0x1038, 0x1039, 0x103A, 0x103B, 0x103C, 0x103D, 0x103E, 0x103F, + 0x1A18, 0x1A19, 0x1A1A, 0x1A1B, 0x1A1C, 0x1A1D, 0x1A1E, 0x1A1F, + 0x1A20, 0x1A21, 0x1A22, 0x1A23, 0x1A24, 0x1A25, 0x1A26, 0x1A27, + 0x1A28, 0x1A29, 0x1A2A, 0x1A2B, 0x1A2C, 0x1A2D, 0x1A2E, 0x1A2F, + 0x1A30, 0x1A31, 0x1A32, 0x1A33, 0x1A34, 0x1A35, 0x1A36, 0x1A37, + 0x1A38, 0x1A39, 0x1A3A, 0x1A3B, 0x1A3C, 0x1A3D, 0x1A3E, 0x1A3F, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0758, 0x0759, 0x075A, 0x075B, 0x075C, 0x075D, 0x075E, 0x075F, + 0x0760, 0x0761, 0x0762, 0x0763, 0x0764, 0x0765, 0x0766, 0x0767, + 0x0768, 0x0769, 0x076A, 0x076B, 0x076C, 0x076D, 0x076E, 0x076F, + 0x0770, 0x0771, 0x0772, 0x0773, 0x0774, 0x0775, 0x0776, 0x0777, + 0x0778, 0x0779, 0x077A, 0x077B, 0x077C, 0x077D, 0x077E, 0x077F, + 0x1158, 0x1159, 0x115A, 0x115B, 0x115C, 0x115D, 0x115E, 0x115F, + 0x1160, 0x1161, 0x1162, 0x1163, 0x1164, 0x1165, 0x1166, 0x1167, + 0x1168, 0x1169, 0x116A, 0x116B, 0x116C, 0x116D, 0x116E, 0x116F, + 0x1170, 0x1171, 0x1172, 0x1173, 0x1174, 0x1175, 0x1176, 0x1177, + 0x1178, 0x1179, 0x117A, 0x117B, 0x117C, 0x117D, 0x117E, 0x117F, + 0x1B58, 0x1B59, 0x1B5A, 0x1B5B, 0x1B5C, 0x1B5D, 0x1B5E, 0x1B5F, + 0x1B60, 0x1B61, 0x1B62, 0x1B63, 0x1B64, 0x1B65, 0x1B66, 0x1B67, + 0x1B68, 0x1B69, 0x1B6A, 0x1B6B, 0x1B6C, 0x1B6D, 0x1B6E, 0x1B6F, + 0x1B70, 0x1B71, 0x1B72, 0x1B73, 0x1B74, 0x1B75, 0x1B76, 0x1B77, + 0x1B78, 0x1B79, 0x1B7A, 0x1B7B, 0x1B7C, 0x1B7D, 0x1B7E, 0x1B7F, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x0898, 0x0899, 0x089A, 0x089B, 0x089C, 0x089D, 0x089E, 0x089F, + 0x08A0, 0x08A1, 0x08A2, 0x08A3, 0x08A4, 0x08A5, 0x08A6, 0x08A7, + 0x08A8, 0x08A9, 0x08AA, 0x08AB, 0x08AC, 0x08AD, 0x08AE, 0x08AF, + 0x08B0, 0x08B1, 0x08B2, 0x08B3, 0x08B4, 0x08B5, 0x08B6, 0x08B7, + 0x08B8, 0x08B9, 0x08BA, 0x08BB, 0x08BC, 0x08BD, 0x08BE, 0x08BF, + 0x1298, 0x1299, 0x129A, 0x129B, 0x129C, 0x129D, 0x129E, 0x129F, + 0x12A0, 0x12A1, 0x12A2, 0x12A3, 0x12A4, 0x12A5, 0x12A6, 0x12A7, + 0x12A8, 0x12A9, 0x12AA, 0x12AB, 0x12AC, 0x12AD, 0x12AE, 0x12AF, + 0x12B0, 0x12B1, 0x12B2, 0x12B3, 0x12B4, 0x12B5, 0x12B6, 0x12B7, + 0x12B8, 0x12B9, 0x12BA, 0x12BB, 0x12BC, 0x12BD, 0x12BE, 0x12BF, + 0x1C98, 0x1C99, 0x1C9A, 0x1C9B, 0x1C9C, 0x1C9D, 0x1C9E, 0x1C9F, + 0x1CA0, 0x1CA1, 0x1CA2, 0x1CA3, 0x1CA4, 0x1CA5, 0x1CA6, 0x1CA7, + 0x1CA8, 0x1CA9, 0x1CAA, 0x1CAB, 0x1CAC, 0x1CAD, 0x1CAE, 0x1CAF, + 0x1CB0, 0x1CB1, 0x1CB2, 0x1CB3, 0x1CB4, 0x1CB5, 0x1CB6, 0x1CB7, + 0x1CB8, 0x1CB9, 0x1CBA, 0x1CBB, 0x1CBC, 0x1CBD, 0x1CBE, 0x1CBF, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, + 0x09D8, 0x09D9, 0x09DA, 0x09DB, 0x09DC, 0x09DD, 0x09DE, 0x09DF, + 0x09E0, 0x09E1, 0x09E2, 0x09E3, 0x09E4, 0x09E5, 0x09E6, 0x09E7, + 0x09E8, 0x09E9, 0x09EA, 0x09EB, 0x09EC, 0x09ED, 0x09EE, 0x09EF, + 0x09F0, 0x09F1, 0x09F2, 0x09F3, 0x09F4, 0x09F5, 0x09F6, 0x09F7, + 0x09F8, 0x09F9, 0x09FA, 0x09FB, 0x09FC, 0x09FD, 0x09FE, 0x09FF, + 0x13D8, 0x13D9, 0x13DA, 0x13DB, 0x13DC, 0x13DD, 0x13DE, 0x13DF, + 0x13E0, 0x13E1, 0x13E2, 0x13E3, 0x13E4, 0x13E5, 0x13E6, 0x13E7, + 0x13E8, 0x13E9, 0x13EA, 0x13EB, 0x13EC, 0x13ED, 0x13EE, 0x13EF, + 0x13F0, 0x13F1, 0x13F2, 0x13F3, 0x13F4, 0x13F5, 0x13F6, 0x13F7, + 0x13F8, 0x13F9, 0x13FA, 0x13FB, 0x13FC, 0x13FD, 0x13FE, 0x13FF, + 0x1DD8, 0x1DD9, 0x1DDA, 0x1DDB, 0x1DDC, 0x1DDD, 0x1DDE, 0x1DDF, + 0x1DE0, 0x1DE1, 0x1DE2, 0x1DE3, 0x1DE4, 0x1DE5, 0x1DE6, 0x1DE7, + 0x1DE8, 0x1DE9, 0x1DEA, 0x1DEB, 0x1DEC, 0x1DED, 0x1DEE, 0x1DEF, + 0x1DF0, 0x1DF1, 0x1DF2, 0x1DF3, 0x1DF4, 0x1DF5, 0x1DF6, 0x1DF7, + 0x1DF8, 0x1DF9, 0x1DFA, 0x1DFB, 0x1DFC, 0x1DFD, 0x1DFE, 0x1DFF, + 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00 + }; + + private const int CharBitmapBytes = 8; + + private static readonly byte[] CharBitmap = + { + 0x43, 0x5D, 0x55, 0x45, 0x65, 0x7D, 0x43, 0x7F, + 0x77, 0x6B, 0x5D, 0x5D, 0x41, 0x5D, 0x5D, 0x7F, + 0x61, 0x5D, 0x5D, 0x61, 0x5D, 0x5D, 0x61, 0x7F, + 0x63, 0x5D, 0x7D, 0x7D, 0x7D, 0x5D, 0x63, 0x7F, + 0x61, 0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x61, 0x7F, + 0x41, 0x7D, 0x7D, 0x61, 0x7D, 0x7D, 0x41, 0x7F, + 0x41, 0x7D, 0x7D, 0x61, 0x7D, 0x7D, 0x7D, 0x7F, + 0x43, 0x7D, 0x7D, 0x7D, 0x4D, 0x5D, 0x43, 0x7F, + 0x5D, 0x5D, 0x5D, 0x41, 0x5D, 0x5D, 0x5D, 0x7F, + 0x63, 0x77, 0x77, 0x77, 0x77, 0x77, 0x63, 0x7F, + 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5D, 0x63, 0x7F, + 0x5D, 0x6D, 0x75, 0x79, 0x75, 0x6D, 0x5D, 0x7F, + 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x41, 0x7F, + 0x5D, 0x49, 0x55, 0x55, 0x5D, 0x5D, 0x5D, 0x7F, + 0x5D, 0x5D, 0x59, 0x55, 0x4D, 0x5D, 0x5D, 0x7F, + 0x63, 0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x63, 0x7F, + 0x61, 0x5D, 0x5D, 0x61, 0x7D, 0x7D, 0x7D, 0x7F, + 0x63, 0x5D, 0x5D, 0x5D, 0x55, 0x6D, 0x53, 0x7F, + 0x61, 0x5D, 0x5D, 0x61, 0x75, 0x6D, 0x5D, 0x7F, + 0x63, 0x5D, 0x7D, 0x63, 0x5F, 0x5D, 0x63, 0x7F, + 0x41, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x7F, + 0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x63, 0x7F, + 0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x6B, 0x77, 0x7F, + 0x5D, 0x5D, 0x5D, 0x55, 0x55, 0x49, 0x5D, 0x7F, + 0x5D, 0x5D, 0x6B, 0x77, 0x6B, 0x5D, 0x5D, 0x7F, + 0x5D, 0x5D, 0x6B, 0x77, 0x77, 0x77, 0x77, 0x7F, + 0x41, 0x5F, 0x6F, 0x77, 0x7B, 0x7D, 0x41, 0x7F, + 0x41, 0x79, 0x79, 0x79, 0x79, 0x79, 0x41, 0x7F, + 0x7F, 0x7D, 0x7B, 0x77, 0x6F, 0x5F, 0x7F, 0x7F, + 0x41, 0x4F, 0x4F, 0x4F, 0x4F, 0x4F, 0x41, 0x7F, + 0x7F, 0x7F, 0x77, 0x6B, 0x5D, 0x7F, 0x7F, 0x7F, + 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x00, + 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, + 0x77, 0x77, 0x77, 0x77, 0x77, 0x7F, 0x77, 0x7F, + 0x6B, 0x6B, 0x6B, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, + 0x6B, 0x6B, 0x00, 0x6B, 0x00, 0x6B, 0x6B, 0x7F, + 0x77, 0x43, 0x75, 0x63, 0x57, 0x61, 0x77, 0x7F, + 0x79, 0x59, 0x6F, 0x77, 0x7B, 0x4D, 0x4F, 0x7F, + 0x7B, 0x75, 0x75, 0x7B, 0x55, 0x6D, 0x53, 0x7F, + 0x77, 0x77, 0x77, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, + 0x77, 0x7B, 0x7D, 0x7D, 0x7D, 0x7B, 0x77, 0x7F, + 0x77, 0x6F, 0x5F, 0x5F, 0x5F, 0x6F, 0x77, 0x7F, + 0x77, 0x55, 0x63, 0x77, 0x63, 0x55, 0x77, 0x7F, + 0x7F, 0x77, 0x77, 0x41, 0x77, 0x77, 0x7F, 0x7F, + 0x7F, 0x7F, 0x7F, 0x7F, 0x77, 0x77, 0x7B, 0x7F, + 0x7F, 0x7F, 0x7F, 0x41, 0x7F, 0x7F, 0x7F, 0x7F, + 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x77, 0x7F, + 0x7F, 0x5F, 0x6F, 0x77, 0x7B, 0x7D, 0x7F, 0x7F, + 0x63, 0x5D, 0x4D, 0x55, 0x59, 0x5D, 0x63, 0x7F, + 0x77, 0x73, 0x77, 0x77, 0x77, 0x77, 0x63, 0x7F, + 0x63, 0x5D, 0x5F, 0x67, 0x7B, 0x7D, 0x41, 0x7F, + 0x41, 0x5F, 0x6F, 0x67, 0x5F, 0x5D, 0x63, 0x7F, + 0x6F, 0x67, 0x6B, 0x6D, 0x41, 0x6F, 0x6F, 0x7F, + 0x41, 0x7D, 0x61, 0x5F, 0x5F, 0x5D, 0x63, 0x7F, + 0x47, 0x7B, 0x7D, 0x61, 0x5D, 0x5D, 0x63, 0x7F, + 0x41, 0x5F, 0x6F, 0x77, 0x7B, 0x7B, 0x7B, 0x7F, + 0x63, 0x5D, 0x5D, 0x63, 0x5D, 0x5D, 0x63, 0x7F, + 0x63, 0x5D, 0x5D, 0x43, 0x5F, 0x6F, 0x71, 0x7F, + 0x7F, 0x7F, 0x77, 0x7F, 0x77, 0x7F, 0x7F, 0x7F, + 0x7F, 0x7F, 0x77, 0x7F, 0x77, 0x77, 0x7B, 0x7F, + 0x6F, 0x77, 0x7B, 0x7D, 0x7B, 0x77, 0x6F, 0x7F, + 0x7F, 0x7F, 0x41, 0x7F, 0x41, 0x7F, 0x7F, 0x7F, + 0x7B, 0x77, 0x6F, 0x5F, 0x6F, 0x77, 0x7B, 0x7F, + 0x63, 0x5D, 0x6F, 0x77, 0x77, 0x7F, 0x77, 0x7F, + 0x43, 0x5D, 0x55, 0x45, 0x65, 0x7D, 0x43, 0x7F, + 0x77, 0x6B, 0x5D, 0x5D, 0x41, 0x5D, 0x5D, 0x7F, + 0x61, 0x5D, 0x5D, 0x61, 0x5D, 0x5D, 0x61, 0x7F, + 0x63, 0x5D, 0x7D, 0x7D, 0x7D, 0x5D, 0x63, 0x7F, + 0x61, 0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x61, 0x7F, + 0x41, 0x7D, 0x7D, 0x61, 0x7D, 0x7D, 0x41, 0x7F, + 0x41, 0x7D, 0x7D, 0x61, 0x7D, 0x7D, 0x7D, 0x7F, + 0x43, 0x7D, 0x7D, 0x7D, 0x4D, 0x5D, 0x43, 0x7F, + 0x5D, 0x5D, 0x5D, 0x41, 0x5D, 0x5D, 0x5D, 0x7F, + 0x63, 0x77, 0x77, 0x77, 0x77, 0x77, 0x63, 0x7F, + 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5D, 0x63, 0x7F, + 0x5D, 0x6D, 0x75, 0x79, 0x75, 0x6D, 0x5D, 0x7F, + 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x41, 0x7F, + 0x5D, 0x49, 0x55, 0x55, 0x5D, 0x5D, 0x5D, 0x7F, + 0x5D, 0x5D, 0x59, 0x55, 0x4D, 0x5D, 0x5D, 0x7F, + 0x63, 0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x63, 0x7F, + 0x61, 0x5D, 0x5D, 0x61, 0x7D, 0x7D, 0x7D, 0x7F, + 0x63, 0x5D, 0x5D, 0x5D, 0x55, 0x6D, 0x53, 0x7F, + 0x61, 0x5D, 0x5D, 0x61, 0x75, 0x6D, 0x5D, 0x7F, + 0x63, 0x5D, 0x7D, 0x63, 0x5F, 0x5D, 0x63, 0x7F, + 0x41, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x7F, + 0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x63, 0x7F, + 0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x6B, 0x77, 0x7F, + 0x5D, 0x5D, 0x5D, 0x55, 0x55, 0x49, 0x5D, 0x7F, + 0x5D, 0x5D, 0x6B, 0x77, 0x6B, 0x5D, 0x5D, 0x7F, + 0x5D, 0x5D, 0x6B, 0x77, 0x77, 0x77, 0x77, 0x7F, + 0x41, 0x5F, 0x6F, 0x77, 0x7B, 0x7D, 0x41, 0x7F, + 0x41, 0x79, 0x79, 0x79, 0x79, 0x79, 0x41, 0x7F, + 0x7F, 0x7D, 0x7B, 0x77, 0x6F, 0x5F, 0x7F, 0x7F, + 0x41, 0x4F, 0x4F, 0x4F, 0x4F, 0x4F, 0x41, 0x7F, + 0x7F, 0x7F, 0x77, 0x6B, 0x5D, 0x7F, 0x7F, 0x7F, + 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x00, + 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, + 0x77, 0x77, 0x77, 0x77, 0x77, 0x7F, 0x77, 0x7F, + 0x6B, 0x6B, 0x6B, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, + 0x6B, 0x6B, 0x00, 0x6B, 0x00, 0x6B, 0x6B, 0x7F, + 0x77, 0x43, 0x75, 0x63, 0x57, 0x61, 0x77, 0x7F, + 0x79, 0x59, 0x6F, 0x77, 0x7B, 0x4D, 0x4F, 0x7F, + 0x7B, 0x75, 0x75, 0x7B, 0x55, 0x6D, 0x53, 0x7F, + 0x77, 0x77, 0x77, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, + 0x77, 0x7B, 0x7D, 0x7D, 0x7D, 0x7B, 0x77, 0x7F, + 0x77, 0x6F, 0x5F, 0x5F, 0x5F, 0x6F, 0x77, 0x7F, + 0x77, 0x55, 0x63, 0x77, 0x63, 0x55, 0x77, 0x7F, + 0x7F, 0x77, 0x77, 0x41, 0x77, 0x77, 0x7F, 0x7F, + 0x7F, 0x7F, 0x7F, 0x7F, 0x77, 0x77, 0x7B, 0x7F, + 0x7F, 0x7F, 0x7F, 0x41, 0x7F, 0x7F, 0x7F, 0x7F, + 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x77, 0x7F, + 0x7F, 0x5F, 0x6F, 0x77, 0x7B, 0x7D, 0x7F, 0x7F, + 0x63, 0x5D, 0x4D, 0x55, 0x59, 0x5D, 0x63, 0x7F, + 0x77, 0x73, 0x77, 0x77, 0x77, 0x77, 0x63, 0x7F, + 0x63, 0x5D, 0x5F, 0x67, 0x7B, 0x7D, 0x41, 0x7F, + 0x41, 0x5F, 0x6F, 0x67, 0x5F, 0x5D, 0x63, 0x7F, + 0x6F, 0x67, 0x6B, 0x6D, 0x41, 0x6F, 0x6F, 0x7F, + 0x41, 0x7D, 0x61, 0x5F, 0x5F, 0x5D, 0x63, 0x7F, + 0x47, 0x7B, 0x7D, 0x61, 0x5D, 0x5D, 0x63, 0x7F, + 0x41, 0x5F, 0x6F, 0x77, 0x7B, 0x7B, 0x7B, 0x7F, + 0x63, 0x5D, 0x5D, 0x63, 0x5D, 0x5D, 0x63, 0x7F, + 0x63, 0x5D, 0x5D, 0x43, 0x5F, 0x6F, 0x71, 0x7F, + 0x7F, 0x7F, 0x77, 0x7F, 0x77, 0x7F, 0x7F, 0x7F, + 0x7F, 0x7F, 0x77, 0x7F, 0x77, 0x77, 0x7B, 0x7F, + 0x6F, 0x77, 0x7B, 0x7D, 0x7B, 0x77, 0x6F, 0x7F, + 0x7F, 0x7F, 0x41, 0x7F, 0x41, 0x7F, 0x7F, 0x7F, + 0x7B, 0x77, 0x6F, 0x5F, 0x6F, 0x77, 0x7B, 0x7F, + 0x63, 0x5D, 0x6F, 0x77, 0x77, 0x7F, 0x77, 0x7F, + 0x3C, 0x22, 0x2A, 0x3A, 0x1A, 0x02, 0x3C, 0x00, + 0x08, 0x14, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x00, + 0x1E, 0x22, 0x22, 0x1E, 0x22, 0x22, 0x1E, 0x00, + 0x1C, 0x22, 0x02, 0x02, 0x02, 0x22, 0x1C, 0x00, + 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, + 0x3E, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x3E, 0x00, + 0x3E, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x00, + 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x3C, 0x00, + 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, + 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x1C, 0x00, + 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x00, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x00, + 0x22, 0x36, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x00, + 0x22, 0x22, 0x26, 0x2A, 0x32, 0x22, 0x22, 0x00, + 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, + 0x1E, 0x22, 0x22, 0x1E, 0x02, 0x02, 0x02, 0x00, + 0x1C, 0x22, 0x22, 0x22, 0x2A, 0x12, 0x2C, 0x00, + 0x1E, 0x22, 0x22, 0x1E, 0x0A, 0x12, 0x22, 0x00, + 0x1C, 0x22, 0x02, 0x1C, 0x20, 0x22, 0x1C, 0x00, + 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x14, 0x08, 0x00, + 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x36, 0x22, 0x00, + 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x00, + 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, 0x00, + 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x00, + 0x3E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x3E, 0x00, + 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x00, + 0x3E, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3E, 0x00, + 0x00, 0x00, 0x08, 0x14, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x00, + 0x14, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x14, 0x14, 0x7F, 0x14, 0x7F, 0x14, 0x14, 0x00, + 0x08, 0x3C, 0x0A, 0x1C, 0x28, 0x1E, 0x08, 0x00, + 0x06, 0x26, 0x10, 0x08, 0x04, 0x32, 0x30, 0x00, + 0x04, 0x0A, 0x0A, 0x04, 0x2A, 0x12, 0x2C, 0x00, + 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08, 0x00, + 0x08, 0x10, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00, + 0x08, 0x2A, 0x1C, 0x08, 0x1C, 0x2A, 0x08, 0x00, + 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00, 0x00, + 0x1C, 0x22, 0x32, 0x2A, 0x26, 0x22, 0x1C, 0x00, + 0x08, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, + 0x1C, 0x22, 0x20, 0x18, 0x04, 0x02, 0x3E, 0x00, + 0x3E, 0x20, 0x10, 0x18, 0x20, 0x22, 0x1C, 0x00, + 0x10, 0x18, 0x14, 0x12, 0x3E, 0x10, 0x10, 0x00, + 0x3E, 0x02, 0x1E, 0x20, 0x20, 0x22, 0x1C, 0x00, + 0x38, 0x04, 0x02, 0x1E, 0x22, 0x22, 0x1C, 0x00, + 0x3E, 0x20, 0x10, 0x08, 0x04, 0x04, 0x04, 0x00, + 0x1C, 0x22, 0x22, 0x1C, 0x22, 0x22, 0x1C, 0x00, + 0x1C, 0x22, 0x22, 0x3C, 0x20, 0x10, 0x0E, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x08, 0x08, 0x04, 0x00, + 0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10, 0x00, + 0x00, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x00, 0x00, + 0x04, 0x08, 0x10, 0x20, 0x10, 0x08, 0x04, 0x00, + 0x1C, 0x22, 0x10, 0x08, 0x08, 0x00, 0x08, 0x00, + 0x3C, 0x22, 0x2A, 0x3A, 0x1A, 0x02, 0x3C, 0x00, + 0x08, 0x14, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x00, + 0x1E, 0x22, 0x22, 0x1E, 0x22, 0x22, 0x1E, 0x00, + 0x1C, 0x22, 0x02, 0x02, 0x02, 0x22, 0x1C, 0x00, + 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, + 0x3E, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x3E, 0x00, + 0x3E, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x00, + 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x3C, 0x00, + 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, + 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x1C, 0x00, + 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x00, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x00, + 0x22, 0x36, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x00, + 0x22, 0x22, 0x26, 0x2A, 0x32, 0x22, 0x22, 0x00, + 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, + 0x1E, 0x22, 0x22, 0x1E, 0x02, 0x02, 0x02, 0x00, + 0x1C, 0x22, 0x22, 0x22, 0x2A, 0x12, 0x2C, 0x00, + 0x1E, 0x22, 0x22, 0x1E, 0x0A, 0x12, 0x22, 0x00, + 0x1C, 0x22, 0x02, 0x1C, 0x20, 0x22, 0x1C, 0x00, + 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x14, 0x08, 0x00, + 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x36, 0x22, 0x00, + 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x00, + 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, 0x00, + 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x00, + 0x3E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x3E, 0x00, + 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x00, + 0x3E, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3E, 0x00, + 0x00, 0x00, 0x08, 0x14, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, + 0x04, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x20, 0x3C, 0x22, 0x3C, 0x00, + 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x3C, 0x00, + 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x3C, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x3E, 0x02, 0x3C, 0x00, + 0x18, 0x24, 0x04, 0x1E, 0x04, 0x04, 0x04, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x00, + 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x1C, 0x00, + 0x10, 0x00, 0x18, 0x10, 0x10, 0x10, 0x12, 0x0C, + 0x02, 0x02, 0x22, 0x12, 0x0E, 0x12, 0x22, 0x00, + 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, + 0x00, 0x00, 0x36, 0x2A, 0x2A, 0x2A, 0x22, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x02, 0x02, + 0x00, 0x00, 0x3C, 0x22, 0x22, 0x3C, 0x20, 0x20, + 0x00, 0x00, 0x3A, 0x06, 0x02, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x1C, 0x20, 0x1E, 0x00, + 0x04, 0x04, 0x1E, 0x04, 0x04, 0x24, 0x18, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x08, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x2A, 0x2A, 0x36, 0x00, + 0x00, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x3E, 0x10, 0x08, 0x04, 0x3E, 0x00, + 0x38, 0x0C, 0x0C, 0x06, 0x0C, 0x0C, 0x38, 0x00, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x0E, 0x18, 0x18, 0x30, 0x18, 0x18, 0x0E, 0x00, + 0x2C, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x2A, 0x14, 0x2A, 0x14, 0x2A, 0x00, 0x00, + 0x10, 0x08, 0x36, 0x7F, 0x3F, 0x3F, 0x7E, 0x36, + 0x10, 0x08, 0x36, 0x49, 0x21, 0x21, 0x4A, 0x36, + 0x00, 0x00, 0x02, 0x06, 0x0E, 0x1E, 0x36, 0x42, + 0x7F, 0x22, 0x14, 0x08, 0x08, 0x14, 0x2A, 0x7F, + 0x00, 0x40, 0x20, 0x11, 0x0A, 0x04, 0x04, 0x00, + 0x7F, 0x3F, 0x5F, 0x6C, 0x75, 0x7B, 0x7B, 0x7F, + 0x3F, 0x3F, 0x3F, 0x3B, 0x39, 0x00, 0x79, 0x7B, + 0x7F, 0x00, 0x7F, 0x00, 0x7F, 0x00, 0x00, 0x7F, + 0x08, 0x04, 0x02, 0x7F, 0x02, 0x04, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, + 0x08, 0x08, 0x08, 0x08, 0x49, 0x2A, 0x1C, 0x08, + 0x08, 0x1C, 0x2A, 0x49, 0x08, 0x08, 0x08, 0x08, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x40, 0x40, 0x44, 0x46, 0x7F, 0x06, 0x04, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x13, 0x18, 0x1C, 0x7E, 0x1C, 0x18, 0x10, 0x6F, + 0x64, 0x0C, 0x1C, 0x3F, 0x1C, 0x0C, 0x04, 0x7B, + 0x40, 0x48, 0x08, 0x7F, 0x3E, 0x1C, 0x48, 0x40, + 0x40, 0x48, 0x1C, 0x3E, 0x7F, 0x08, 0x48, 0x48, + 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x7F, + 0x08, 0x10, 0x20, 0x7F, 0x20, 0x10, 0x08, 0x00, + 0x2A, 0x55, 0x2A, 0x55, 0x2A, 0x55, 0x2A, 0x55, + 0x55, 0x2A, 0x55, 0x2A, 0x55, 0x2A, 0x55, 0x2A, + 0x00, 0x3E, 0x41, 0x01, 0x01, 0x01, 0x7F, 0x00, + 0x00, 0x00, 0x3F, 0x40, 0x40, 0x40, 0x7F, 0x00, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x08, 0x1C, 0x3E, 0x7F, 0x3E, 0x1C, 0x08, 0x00, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, + 0x14, 0x14, 0x77, 0x00, 0x77, 0x14, 0x14, 0x00, + 0x7F, 0x40, 0x40, 0x4C, 0x4C, 0x40, 0x40, 0x7F, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x7B, 0x77, 0x6F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, + 0x7F, 0x7F, 0x63, 0x5F, 0x43, 0x5D, 0x43, 0x7F, + 0x7D, 0x7D, 0x61, 0x5D, 0x5D, 0x5D, 0x61, 0x7F, + 0x7F, 0x7F, 0x43, 0x7D, 0x7D, 0x7D, 0x43, 0x7F, + 0x5F, 0x5F, 0x43, 0x5D, 0x5D, 0x5D, 0x43, 0x7F, + 0x7F, 0x7F, 0x63, 0x5D, 0x41, 0x7D, 0x43, 0x7F, + 0x67, 0x5B, 0x7B, 0x61, 0x7B, 0x7B, 0x7B, 0x7F, + 0x7F, 0x7F, 0x63, 0x5D, 0x5D, 0x43, 0x5F, 0x63, + 0x7D, 0x7D, 0x61, 0x5D, 0x5D, 0x5D, 0x5D, 0x7F, + 0x77, 0x7F, 0x73, 0x77, 0x77, 0x77, 0x63, 0x7F, + 0x6F, 0x7F, 0x67, 0x6F, 0x6F, 0x6F, 0x6D, 0x73, + 0x7D, 0x7D, 0x5D, 0x6D, 0x71, 0x6D, 0x5D, 0x7F, + 0x73, 0x77, 0x77, 0x77, 0x77, 0x77, 0x63, 0x7F, + 0x7F, 0x7F, 0x49, 0x55, 0x55, 0x55, 0x5D, 0x7F, + 0x7F, 0x7F, 0x61, 0x5D, 0x5D, 0x5D, 0x5D, 0x7F, + 0x7F, 0x7F, 0x63, 0x5D, 0x5D, 0x5D, 0x63, 0x7F, + 0x7F, 0x7F, 0x61, 0x5D, 0x5D, 0x61, 0x7D, 0x7D, + 0x7F, 0x7F, 0x43, 0x5D, 0x5D, 0x43, 0x5F, 0x5F, + 0x7F, 0x7F, 0x45, 0x79, 0x7D, 0x7D, 0x7D, 0x7F, + 0x7F, 0x7F, 0x43, 0x7D, 0x63, 0x5F, 0x61, 0x7F, + 0x7B, 0x7B, 0x61, 0x7B, 0x7B, 0x5B, 0x67, 0x7F, + 0x7F, 0x7F, 0x5D, 0x5D, 0x5D, 0x4D, 0x53, 0x7F, + 0x7F, 0x7F, 0x5D, 0x5D, 0x5D, 0x6B, 0x77, 0x7F, + 0x7F, 0x7F, 0x5D, 0x5D, 0x55, 0x55, 0x49, 0x7F, + 0x7F, 0x7F, 0x5D, 0x6B, 0x77, 0x6B, 0x5D, 0x7F, + 0x7F, 0x7F, 0x5D, 0x5D, 0x5D, 0x43, 0x5F, 0x63, + 0x7F, 0x7F, 0x41, 0x6F, 0x77, 0x7B, 0x41, 0x7F, + 0x47, 0x73, 0x73, 0x79, 0x73, 0x73, 0x47, 0x7F, + 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, + 0x71, 0x67, 0x67, 0x4F, 0x67, 0x67, 0x71, 0x7F, + 0x53, 0x65, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, + 0x7F, 0x55, 0x6B, 0x55, 0x6B, 0x55, 0x7F, 0x7F, + 0x70, 0x60, 0x7E, 0x31, 0x79, 0x30, 0x3F, 0x02, + 0x00, 0x18, 0x07, 0x00, 0x07, 0x0C, 0x08, 0x70 + }; + + private static readonly ushort[] CharSetPrimary = + { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, + 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, + 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, + 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, + 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF + }; + + private static readonly ushort[] CharSetSecondaryStandard = + { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0120, 0x0121, 0x0122, 0x0123, 0x0124, 0x0125, 0x0126, 0x0127, + 0x0128, 0x0129, 0x012A, 0x012B, 0x012C, 0x012D, 0x012E, 0x012F, + 0x0130, 0x0131, 0x0132, 0x0133, 0x0134, 0x0135, 0x0136, 0x0137, + 0x0138, 0x0139, 0x013A, 0x013B, 0x013C, 0x013D, 0x013E, 0x013F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, + 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, + 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, + 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, + 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF + }; + + private static readonly ushort[] CharSetSecondaryEnhanced = + { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, + 0x0108, 0x0109, 0x010A, 0x010B, 0x010C, 0x010D, 0x010E, 0x010F, + 0x0110, 0x0111, 0x0112, 0x0113, 0x0114, 0x0115, 0x0116, 0x0117, + 0x0118, 0x0119, 0x011A, 0x011B, 0x011C, 0x011D, 0x011E, 0x011F, + 0x0120, 0x0121, 0x0122, 0x0123, 0x0124, 0x0125, 0x0126, 0x0127, + 0x0128, 0x0129, 0x012A, 0x012B, 0x012C, 0x012D, 0x012E, 0x012F, + 0x0130, 0x0131, 0x0132, 0x0133, 0x0134, 0x0135, 0x0136, 0x0137, + 0x0138, 0x0139, 0x013A, 0x013B, 0x013C, 0x013D, 0x013E, 0x013F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, + 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, + 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, + 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, + 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF + }; + + private const int ColorPaletteCount = 0x210; + + private const int ColorMono00 = 0x000; + private const int ColorMono01 = 0x001; + private const int ColorMono02 = 0x002; + private const int ColorMono04 = 0x004; + private const int ColorMono08 = 0x008; + private const int ColorMono10 = 0x010; + private const int ColorMono20 = 0x020; + private const int ColorMono40 = 0x040; + private const int ColorMono80 = 0x080; + + private const int ColorWhite00 = 0x100; + private const int ColorWhite01 = 0x101; + private const int ColorWhite02 = 0x102; + private const int ColorWhite04 = 0x104; + private const int ColorWhite08 = 0x108; + private const int ColorWhite10 = 0x110; + private const int ColorWhite20 = 0x120; + private const int ColorWhite40 = 0x140; + private const int ColorWhite80 = 0x180; + + private const int ColorDHires0 = 0x200; + private const int ColorDHires1 = 0x201; + private const int ColorDHires2 = 0x202; + private const int ColorDHires3 = 0x203; + private const int ColorDHires4 = 0x204; + private const int ColorDHires5 = 0x205; + private const int ColorDHires6 = 0x206; + private const int ColorDHires7 = 0x207; + private const int ColorDHires8 = 0x208; + private const int ColorDHires9 = 0x209; + private const int ColorDHiresA = 0x20A; + private const int ColorDHiresB = 0x20B; + private const int ColorDHiresC = 0x20C; + private const int ColorDHiresD = 0x20D; + private const int ColorDHiresE = 0x20E; + private const int ColorDHiresF = 0x20F; + + private static readonly int[] ColorLores = + { + ColorDHires0, ColorDHires8, ColorDHires1, ColorDHires9, + ColorDHires2, ColorDHiresA, ColorDHires3, ColorDHiresB, + ColorDHires4, ColorDHiresC, ColorDHires5, ColorDHiresD, + ColorDHires6, ColorDHiresE, ColorDHires7, ColorDHiresF + }; + + private static readonly int[] Color7MLores = + { + ColorDHires0, ColorDHires9, ColorDHires6, ColorDHiresF, // even columns + ColorDHires0, ColorDHires9, ColorDHires6, ColorDHiresF, + ColorDHires0, ColorDHires9, ColorDHires6, ColorDHiresF, + ColorDHires0, ColorDHires9, ColorDHires6, ColorDHiresF, + ColorDHires0, ColorDHires0, ColorDHires0, ColorDHires0, // odd columns + ColorDHires6, ColorDHires6, ColorDHires6, ColorDHires6, + ColorDHires9, ColorDHires9, ColorDHires9, ColorDHires9, + ColorDHiresF, ColorDHiresF, ColorDHiresF, ColorDHiresF + }; + + private static readonly int[] ColorDLores = + { + ColorDHires0, ColorDHires4, ColorDHires8, ColorDHiresC, // even columns + ColorDHires1, ColorDHires5, ColorDHires9, ColorDHiresD, + ColorDHires2, ColorDHires6, ColorDHiresA, ColorDHiresE, + ColorDHires3, ColorDHires7, ColorDHiresB, ColorDHiresF, + ColorDHires0, ColorDHires8, ColorDHires1, ColorDHires9, // odd columns + ColorDHires2, ColorDHiresA, ColorDHires3, ColorDHiresB, + ColorDHires4, ColorDHiresC, ColorDHires5, ColorDHiresD, + ColorDHires6, ColorDHiresE, ColorDHires7, ColorDHiresF + }; + + private static readonly int[] ColorHires = + { + ColorDHires0, ColorDHires0, ColorDHires9, ColorDHiresF, // even columns, high bit off + ColorDHires0, ColorDHires6, ColorDHiresF, ColorDHiresF, + ColorDHires0, ColorDHires0, ColorDHires3, ColorDHiresF, // even columns, high bit on + ColorDHires0, ColorDHiresC, ColorDHiresF, ColorDHiresF, + ColorDHires0, ColorDHires0, ColorDHires6, ColorDHiresF, // odd columns, high bit off + ColorDHires0, ColorDHires9, ColorDHiresF, ColorDHiresF, + ColorDHires0, ColorDHires0, ColorDHiresC, ColorDHiresF, // odd columns, high bit on + ColorDHires0, ColorDHires3, ColorDHiresF, ColorDHiresF + }; + + private const int CyclesPerHSync = 65; + private const int CyclesPerFlush = 8 * CyclesPerHSync; + + private const int HCountPreset = 0x40; // hcount preset after hcount overflows -> HPE' low [3-13] + private const int HCountLeaveHBlank = 0x58; // hcount when leaving hblank [3-15] + private const int VCountPresetNtsc = 0xFA; // vcount preset after vcount overflows (NTSC) [3-13] + private const int VCountPresetPal = 0xC8; // vcount preset after vcount overflows (PAL) [3-17] + private const int VLineEnterVBlank = 192; // vline when entering vblank (NTSC & PAL) [3-17] + private const int VLineTriggerPreset = 256; // vline when vcount overflows and presets (NTSC & PAL) [3-15, 3-16] + private const int VLineLeaveVBlankNtsc = 262; // vline when leaving vblank (NTSC) [3-13] + private const int VLineLeaveVBlankPal = 312; // vline when leaving vblank (PAL) [3-17] + private const int VSyncsPerFlash = 16; // flash count using vcount overflow [3-17] + + public const int ModeCount = 16; + + public const int Mode0 = 0x0; + public const int Mode1 = 0x1; + public const int Mode2 = 0x2; + public const int Mode3 = 0x3; + public const int Mode4 = 0x4; + public const int Mode5 = 0x5; + public const int Mode6 = 0x6; + public const int Mode7 = 0x7; + public const int Mode8 = 0x8; + public const int Mode9 = 0x9; + public const int ModeA = 0xA; + public const int ModeB = 0xB; + public const int ModeC = 0xC; + public const int ModeD = 0xD; + public const int ModeE = 0xE; + public const int ModeF = 0xF; + + // ReSharper disable once FieldCanBeMadeReadOnly.Local + private Action[] _flushRowMode; + + private const int Width = 560; + private const int Height = VLineEnterVBlank; + + private const int TextHeight = 8; + + private const int LoresHeight = 4; + + private const int MixedHeight = 160; + private const int MixedCellIndex = MixedHeight * CellColumns; + } +} diff --git a/ExternalCoreProjects/Virtu/Video.cs b/ExternalCoreProjects/Virtu/Video.cs index 313b1cf652..967b51e69e 100644 --- a/ExternalCoreProjects/Virtu/Video.cs +++ b/ExternalCoreProjects/Virtu/Video.cs @@ -1,43 +1,52 @@ using System; -using System.IO; -using Jellyfish.Virtu.Services; +using System.Runtime.Serialization; +using Newtonsoft.Json; namespace Jellyfish.Virtu { - [Flags] - public enum ScannerOptions { None = 0x0, AppleII = 0x1, Pal = 0x2 } // defaults to AppleIIe, Ntsc + [Flags] + internal enum ScannerOptions { None = 0x0, AppleII = 0x1, Pal = 0x2 } // defaults to AppleIIe, Ntsc + + public interface IVideo + { + int[] GetVideoBuffer(); + void Reset(); + + void DirtyCell(int addressOffset); + void DirtyCellMixed(int addressOffset); + void DirtyCellMixedText(int addressOffset); + void DirtyScreen(); + int ReadFloatingBus(); + void SetCharSet(); + + bool IsVBlank { get; } + } + + public sealed partial class Video : IVideo + { + // ReSharper disable once FieldCanBeMadeReadOnly.Local + private MachineEvents _events; + + // ReSharper disable once FieldCanBeMadeReadOnly.Local + private IMemoryBus _memory; - public sealed partial class Video : MachineComponent - { public Video() { } - public Video(Machine machine) : - base(machine) - { - _flushRowEvent = FlushRowEvent; // cache delegates; avoids garbage - _inverseTextEvent = InverseTextEvent; - _leaveVBlankEvent = LeaveVBlankEvent; - _resetVSyncEvent = ResetVSyncEvent; - - FlushRowMode = new Action[ModeCount] - { - FlushRowMode0, FlushRowMode1, FlushRowMode2, FlushRowMode3, FlushRowMode4, FlushRowMode5, FlushRowMode6, FlushRowMode7, - FlushRowMode8, FlushRowMode9, FlushRowModeA, FlushRowModeB, FlushRowModeC, FlushRowModeD, FlushRowModeE, FlushRowModeF - }; - } - - [System.Runtime.Serialization.OnDeserialized] - public void OnDeserialized(System.Runtime.Serialization.StreamingContext context) + public Video(MachineEvents events, IMemoryBus memory) { - // the videoservice forgets all of its information on loadstate - DirtyScreen(); - } + _events = events; + _memory = memory; - public override void Initialize() - { - _memory = Machine.Memory; - VideoService = new Services.VideoService(); + _flushRowEvent = FlushRowEvent; // cache delegates; avoids garbage + _inverseTextEvent = InverseTextEvent; + _leaveVBlankEvent = LeaveVBlankEvent; + _resetVSyncEvent = ResetVSyncEvent; + + _flushRowMode = new Action[] + { + FlushRowMode0, FlushRowMode1, FlushRowMode2, FlushRowMode3, FlushRowMode4, FlushRowMode5, FlushRowMode6, FlushRowMode7, + FlushRowMode8, FlushRowMode9, FlushRowModeA, FlushRowModeB, FlushRowModeC, FlushRowModeD, FlushRowModeE, FlushRowModeF + }; -//#if SILVERLIGHT || WPF unchecked { _colorBlack = (int)0xFF000000; // BGRA @@ -58,1056 +67,1016 @@ namespace Jellyfish.Virtu _colorWhite = (int)0xFFFFFFFF; _colorMonochrome = (int)0xFF00AA00; } -//#else -// _colorBlack = 0xFF000000; // RGBA -// _colorDarkBlue = 0xFF990000; -// _colorDarkGreen = 0xFF227711; -// _colorMediumBlue = 0xFFFF0000; -// _colorBrown = 0xFF005588; -// _colorLightGrey = 0xFFAAAA99; -// _colorGreen = 0xFF11EE00; -// _colorAquamarine = 0xFFAAFF55; -// _colorDeepRed = 0xFF1111FF; -// _colorPurple = 0xFFDD00DD; -// _colorDarkGrey = 0xFF555544; -// _colorLightBlue = 0xFFFFAA33; -// _colorOrange = 0xFF1144FF; -// _colorPink = 0xFF8899FF; -// _colorYellow = 0xFF11FFFF; -// _colorWhite = 0xFFFFFFFF; -// _colorMonochrome = 0xFF00AA00; -//#endif - SetPalette(); - - IsMonochrome = false; - ScannerOptions = ScannerOptions.None; - - IsVBlank = true; - - Machine.Events.AddEvent(_cyclesPerVBlankPreset, _leaveVBlankEvent); // align flush events with scanner; assumes vcount preset at start of frame [3-15, 3-16] - Machine.Events.AddEvent(_cyclesPerVSync, _resetVSyncEvent); - Machine.Events.AddEvent(_cyclesPerFlash, _inverseTextEvent); - } - - public override void Reset() - { - SetCharSet(); - DirtyScreen(); - FlushScreen(); - } - - public void DirtyCell(int addressOffset) - { - _isCellDirty[CellIndex[addressOffset]] = true; - } - - public void DirtyCellMixed(int addressOffset) - { - int cellIndex = CellIndex[addressOffset]; - if (cellIndex < MixedCellIndex) - { - _isCellDirty[cellIndex] = true; - } - } - - public void DirtyCellMixedText(int addressOffset) - { - int cellIndex = CellIndex[addressOffset]; - if (cellIndex >= MixedCellIndex) - { - _isCellDirty[cellIndex] = true; - } - } - - public void DirtyScreen() - { - for (int i = 0; i < Height * CellColumns; i++) - { - _isCellDirty[i] = true; - } - } - - public void DirtyScreenText() - { - if (_memory.IsText) - { - for (int i = 0; i < MixedHeight * CellColumns; i++) - { - _isCellDirty[i] = true; - } - } - if (_memory.IsText || _memory.IsMixed) - { - for (int i = MixedHeight * CellColumns; i < Height * CellColumns; i++) - { - _isCellDirty[i] = true; - } - } - } - - public int ReadFloatingBus() // [5-40] - { - // derive scanner counters from current cycles into frame; assumes hcount and vcount preset at start of frame [3-13, 3-15, 3-16] - int cycles = _cyclesPerVSync - Machine.Events.FindEvent(_resetVSyncEvent); - int hClock = cycles % CyclesPerHSync; - int hCount = (hClock != 0) ? HCountPreset + hClock - 1 : 0; - int vLine = cycles / CyclesPerHSync; - int vCount = _vCountPreset + vLine; - - // derive scanner address [5-8] - int address = ((vCount << 4) & 0x0380) | ((0x0068 + (hCount & 0x0038) + (((vCount >> 1) & 0x0060) | ((vCount >> 3) & 0x0018))) & 0x0078) | (hCount & 0x0007); - if (_memory.IsHires && !(_memory.IsMixed && ((vCount & 0xA0) == 0xA0))) // hires but not actively mixed [5-13, 5-19] - { - address |= (_memory.IsVideoPage2 ? 0x4000 : 0x2000) | ((vCount << 10) & 0x1C00); - } - else - { - address |= _memory.IsVideoPage2 ? 0x0800 : 0x0400; - if (((_scannerOptions & ScannerOptions.AppleII) != 0) && (hCount < HCountLeaveHBlank)) - { - address |= 0x1000; - } - } - - return _memory.Read(address); - } - - public void SetCharSet() - { - _charSet = !_memory.IsCharSetAlternate ? CharSetPrimary : (_memory.Monitor == MonitorType.Standard) ? CharSetSecondaryStandard : CharSetSecondaryEnhanced; - DirtyScreenText(); - } - - #region Draw Methods - private void DrawText40(int data, int x, int y) - { - int color = IsMonochrome ? ColorMono00 : ColorWhite00; - int index = _charSet[data] * CharBitmapBytes; - int inverseMask = (_isTextInversed && !_memory.IsCharSetAlternate && (0x40 <= data) && (data <= 0x7F)) ? 0x7F : 0x00; - for (int i = 0; i < TextHeight; i++, y++) - { - data = CharBitmap[index + i] ^ inverseMask; - SetPixel(x + 0, y, color | (data & 0x01)); - SetPixel(x + 1, y, color | (data & 0x01)); - SetPixel(x + 2, y, color | (data & 0x02)); - SetPixel(x + 3, y, color | (data & 0x02)); - SetPixel(x + 4, y, color | (data & 0x04)); - SetPixel(x + 5, y, color | (data & 0x04)); - SetPixel(x + 6, y, color | (data & 0x08)); - SetPixel(x + 7, y, color | (data & 0x08)); - SetPixel(x + 8, y, color | (data & 0x10)); - SetPixel(x + 9, y, color | (data & 0x10)); - SetPixel(x + 10, y, color | (data & 0x20)); - SetPixel(x + 11, y, color | (data & 0x20)); - SetPixel(x + 12, y, color | (data & 0x40)); - SetPixel(x + 13, y, color | (data & 0x40)); - } - } - - private void DrawText80(int data, int x, int y) - { - int color = IsMonochrome ? ColorMono00 : ColorWhite00; - int index = _charSet[data] * CharBitmapBytes; - int mask = (_isTextInversed && !_memory.IsCharSetAlternate && (0x40 <= data) && (data <= 0x7F)) ? 0x7F : 0x00; - for (int i = 0; i < TextHeight; i++, y++) - { - data = CharBitmap[index + i] ^ mask; - SetPixel(x + 0, y, color | (data & 0x01)); - SetPixel(x + 1, y, color | (data & 0x02)); - SetPixel(x + 2, y, color | (data & 0x04)); - SetPixel(x + 3, y, color | (data & 0x08)); - SetPixel(x + 4, y, color | (data & 0x10)); - SetPixel(x + 5, y, color | (data & 0x20)); - SetPixel(x + 6, y, color | (data & 0x40)); - } - } - - private void DrawLores(int data, int x, int y) - { - if (IsMonochrome) - { - if ((x & 0x02) == 0x02) // odd cell - { - data = ((data << 2) & 0xCC) | ((data >> 2) & 0x33); - } - for (int i = 0; i < LoresHeight; i++, y++) - { - SetPixel(x + 0, y, data & 0x01); - SetPixel(x + 1, y, data & 0x02); - SetPixel(x + 2, y, data & 0x04); - SetPixel(x + 3, y, data & 0x08); - SetPixel(x + 4, y, data & 0x01); - SetPixel(x + 5, y, data & 0x02); - SetPixel(x + 6, y, data & 0x04); - SetPixel(x + 7, y, data & 0x08); - SetPixel(x + 8, y, data & 0x01); - SetPixel(x + 9, y, data & 0x02); - SetPixel(x + 10, y, data & 0x04); - SetPixel(x + 11, y, data & 0x08); - SetPixel(x + 12, y, data & 0x01); - SetPixel(x + 13, y, data & 0x02); - } - for (int i = 0; i < LoresHeight; i++, y++) - { - SetPixel(x + 0, y, data & 0x10); - SetPixel(x + 1, y, data & 0x20); - SetPixel(x + 2, y, data & 0x40); - SetPixel(x + 3, y, data & 0x80); - SetPixel(x + 4, y, data & 0x10); - SetPixel(x + 5, y, data & 0x20); - SetPixel(x + 6, y, data & 0x40); - SetPixel(x + 7, y, data & 0x80); - SetPixel(x + 8, y, data & 0x10); - SetPixel(x + 9, y, data & 0x20); - SetPixel(x + 10, y, data & 0x40); - SetPixel(x + 11, y, data & 0x80); - SetPixel(x + 12, y, data & 0x10); - SetPixel(x + 13, y, data & 0x20); - } - } - else - { - int color = ColorLores[data & 0x0F]; - for (int i = 0; i < LoresHeight; i++, y++) - { - SetPixel(x + 0, y, color); - SetPixel(x + 1, y, color); - SetPixel(x + 2, y, color); - SetPixel(x + 3, y, color); - SetPixel(x + 4, y, color); - SetPixel(x + 5, y, color); - SetPixel(x + 6, y, color); - SetPixel(x + 7, y, color); - SetPixel(x + 8, y, color); - SetPixel(x + 9, y, color); - SetPixel(x + 10, y, color); - SetPixel(x + 11, y, color); - SetPixel(x + 12, y, color); - SetPixel(x + 13, y, color); - } - color = ColorLores[data >> 4]; - for (int i = 0; i < LoresHeight; i++, y++) - { - SetPixel(x + 0, y, color); - SetPixel(x + 1, y, color); - SetPixel(x + 2, y, color); - SetPixel(x + 3, y, color); - SetPixel(x + 4, y, color); - SetPixel(x + 5, y, color); - SetPixel(x + 6, y, color); - SetPixel(x + 7, y, color); - SetPixel(x + 8, y, color); - SetPixel(x + 9, y, color); - SetPixel(x + 10, y, color); - SetPixel(x + 11, y, color); - SetPixel(x + 12, y, color); - SetPixel(x + 13, y, color); - } - } - } - - private void Draw7MLores(int data, int x, int y) - { - if (IsMonochrome) - { - if ((x & 0x02) == 0x02) // odd cell - { - data = ((data << 2) & 0xCC) | ((data >> 2) & 0x33); - } - for (int i = 0; i < LoresHeight; i++, y++) - { - SetPixel(x + 0, y, data & 0x01); - SetPixel(x + 1, y, data & 0x01); - SetPixel(x + 2, y, data & 0x02); - SetPixel(x + 3, y, data & 0x02); - SetPixel(x + 4, y, data & 0x04); - SetPixel(x + 5, y, data & 0x04); - SetPixel(x + 6, y, data & 0x08); - SetPixel(x + 7, y, data & 0x08); - SetPixel(x + 8, y, data & 0x01); - SetPixel(x + 9, y, data & 0x01); - SetPixel(x + 10, y, data & 0x02); - SetPixel(x + 11, y, data & 0x02); - SetPixel(x + 12, y, data & 0x04); - SetPixel(x + 13, y, data & 0x04); - } - for (int i = 0; i < LoresHeight; i++, y++) - { - SetPixel(x + 0, y, data & 0x10); - SetPixel(x + 1, y, data & 0x10); - SetPixel(x + 2, y, data & 0x20); - SetPixel(x + 3, y, data & 0x20); - SetPixel(x + 4, y, data & 0x40); - SetPixel(x + 5, y, data & 0x40); - SetPixel(x + 6, y, data & 0x80); - SetPixel(x + 7, y, data & 0x80); - SetPixel(x + 8, y, data & 0x10); - SetPixel(x + 9, y, data & 0x10); - SetPixel(x + 10, y, data & 0x20); - SetPixel(x + 11, y, data & 0x20); - SetPixel(x + 12, y, data & 0x40); - SetPixel(x + 13, y, data & 0x40); - } - } - else - { - int color = Color7MLores[((x & 0x02) << 3) | (data & 0x0F)]; - for (int i = 0; i < LoresHeight; i++, y++) - { - SetPixel(x + 0, y, color); - SetPixel(x + 1, y, color); - SetPixel(x + 2, y, color); - SetPixel(x + 3, y, color); - SetPixel(x + 4, y, color); - SetPixel(x + 5, y, color); - SetPixel(x + 6, y, color); - SetPixel(x + 7, y, color); - SetPixel(x + 8, y, color); - SetPixel(x + 9, y, color); - SetPixel(x + 10, y, color); - SetPixel(x + 11, y, color); - SetPixel(x + 12, y, color); - SetPixel(x + 13, y, color); - } - color = Color7MLores[((x & 0x02) << 3) | (data >> 4)]; - for (int i = 0; i < LoresHeight; i++, y++) - { - SetPixel(x + 0, y, color); - SetPixel(x + 1, y, color); - SetPixel(x + 2, y, color); - SetPixel(x + 3, y, color); - SetPixel(x + 4, y, color); - SetPixel(x + 5, y, color); - SetPixel(x + 6, y, color); - SetPixel(x + 7, y, color); - SetPixel(x + 8, y, color); - SetPixel(x + 9, y, color); - SetPixel(x + 10, y, color); - SetPixel(x + 11, y, color); - SetPixel(x + 12, y, color); - SetPixel(x + 13, y, color); - } - } - } - - private void DrawDLores(int data, int x, int y) - { - if (IsMonochrome) - { - if ((x & 0x01) == 0x00) // even half cell - { - data = ((data << 1) & 0xEE) | ((data >> 3) & 0x11); - } - for (int i = 0; i < LoresHeight; i++, y++) - { - SetPixel(x + 0, y, data & 0x01); - SetPixel(x + 1, y, data & 0x02); - SetPixel(x + 2, y, data & 0x04); - SetPixel(x + 3, y, data & 0x08); - SetPixel(x + 4, y, data & 0x01); - SetPixel(x + 5, y, data & 0x02); - SetPixel(x + 6, y, data & 0x04); - } - for (int i = 0; i < LoresHeight; i++, y++) - { - SetPixel(x + 0, y, data & 0x10); - SetPixel(x + 1, y, data & 0x20); - SetPixel(x + 2, y, data & 0x40); - SetPixel(x + 3, y, data & 0x80); - SetPixel(x + 4, y, data & 0x10); - SetPixel(x + 5, y, data & 0x20); - SetPixel(x + 6, y, data & 0x40); - } - } - else - { - int color = ColorDLores[((x & 0x01) << 4) | (data & 0x0F)]; - for (int i = 0; i < LoresHeight; i++, y++) - { - SetPixel(x + 0, y, color); - SetPixel(x + 1, y, color); - SetPixel(x + 2, y, color); - SetPixel(x + 3, y, color); - SetPixel(x + 4, y, color); - SetPixel(x + 5, y, color); - SetPixel(x + 6, y, color); - } - color = ColorDLores[((x & 0x01) << 4) | (data >> 4)]; - for (int i = 0; i < LoresHeight; i++, y++) - { - SetPixel(x + 0, y, color); - SetPixel(x + 1, y, color); - SetPixel(x + 2, y, color); - SetPixel(x + 3, y, color); - SetPixel(x + 4, y, color); - SetPixel(x + 5, y, color); - SetPixel(x + 6, y, color); - } - } - } - - private void DrawHires(int address, int x, int y) - { - if (IsMonochrome) - { - int data = _memory.ReadRamMainRegion02BF(address); - SetPixel(x + 0, y, data & 0x01); - SetPixel(x + 1, y, data & 0x01); - SetPixel(x + 2, y, data & 0x02); - SetPixel(x + 3, y, data & 0x02); - SetPixel(x + 4, y, data & 0x04); - SetPixel(x + 5, y, data & 0x04); - SetPixel(x + 6, y, data & 0x08); - SetPixel(x + 7, y, data & 0x08); - SetPixel(x + 8, y, data & 0x10); - SetPixel(x + 9, y, data & 0x10); - SetPixel(x + 10, y, data & 0x20); - SetPixel(x + 11, y, data & 0x20); - SetPixel(x + 12, y, data & 0x40); - SetPixel(x + 13, y, data & 0x40); - } - else - { - // 3 2 1 0 - // 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 - // - // - - - - - - - - 0 0 0 0 0 0 0 0 + + + + + + + + - // H 1 0 H 6 5 4 3 2 1 0 H 1 0 - - int data = _memory.ReadRamMainRegion02BF(address) << 8; - if (x < Width - CellWidth) - { - data |= _memory.ReadRamMainRegion02BF(address + 1); - SetPixel(x + 14, y, ColorHires[((~x & 0x02) << 3) | ((data >> 4) & 0x08) | ((data << 1) & 0x06) | ((data >> 14) & 0x01)]); - SetPixel(x + 15, y, ColorHires[((~x & 0x02) << 3) | ((data >> 4) & 0x08) | ((data << 1) & 0x06) | ((data >> 14) & 0x01)]); - } - if (x > 0) - { - data |= _memory.ReadRamMainRegion02BF(address - 1) << 16; - SetPixel(x - 2, y, ColorHires[((~x & 0x02) << 3) | ((data >> 20) & 0x08) | ((data >> 6) & 0x04) | ((data >> 21) & 0x03)]); - SetPixel(x - 1, y, ColorHires[((~x & 0x02) << 3) | ((data >> 20) & 0x08) | ((data >> 6) & 0x04) | ((data >> 21) & 0x03)]); - } - SetPixel(x + 0, y, ColorHires[((x & 0x02) << 3) | ((data >> 12) & 0x08) | ((data >> 7) & 0x06) | ((data >> 22) & 0x01)]); - SetPixel(x + 1, y, ColorHires[((x & 0x02) << 3) | ((data >> 12) & 0x08) | ((data >> 7) & 0x06) | ((data >> 22) & 0x01)]); - SetPixel(x + 2, y, ColorHires[((~x & 0x02) << 3) | ((data >> 12) & 0x08) | ((data >> 8) & 0x07)]); - SetPixel(x + 3, y, ColorHires[((~x & 0x02) << 3) | ((data >> 12) & 0x08) | ((data >> 8) & 0x07)]); - SetPixel(x + 4, y, ColorHires[((x & 0x02) << 3) | ((data >> 12) & 0x08) | ((data >> 9) & 0x07)]); - SetPixel(x + 5, y, ColorHires[((x & 0x02) << 3) | ((data >> 12) & 0x08) | ((data >> 9) & 0x07)]); - SetPixel(x + 6, y, ColorHires[((~x & 0x02) << 3) | ((data >> 12) & 0x08) | ((data >> 10) & 0x07)]); - SetPixel(x + 7, y, ColorHires[((~x & 0x02) << 3) | ((data >> 12) & 0x08) | ((data >> 10) & 0x07)]); - SetPixel(x + 8, y, ColorHires[((x & 0x02) << 3) | ((data >> 12) & 0x08) | ((data >> 11) & 0x07)]); - SetPixel(x + 9, y, ColorHires[((x & 0x02) << 3) | ((data >> 12) & 0x08) | ((data >> 11) & 0x07)]); - SetPixel(x + 10, y, ColorHires[((~x & 0x02) << 3) | ((data >> 12) & 0x0F)]); - SetPixel(x + 11, y, ColorHires[((~x & 0x02) << 3) | ((data >> 12) & 0x0F)]); - SetPixel(x + 12, y, ColorHires[((x & 0x02) << 3) | ((data >> 12) & 0x08) | ((data << 2) & 0x04) | ((data >> 13) & 0x03)]); - SetPixel(x + 13, y, ColorHires[((x & 0x02) << 3) | ((data >> 12) & 0x08) | ((data << 2) & 0x04) | ((data >> 13) & 0x03)]); - } - } - - private void DrawNDHires(int address, int x, int y) - { - if (IsMonochrome) - { - int data = _memory.ReadRamMainRegion02BF(address); - SetPixel(x + 0, y, data & 0x01); - SetPixel(x + 1, y, data & 0x01); - SetPixel(x + 2, y, data & 0x02); - SetPixel(x + 3, y, data & 0x02); - SetPixel(x + 4, y, data & 0x04); - SetPixel(x + 5, y, data & 0x04); - SetPixel(x + 6, y, data & 0x08); - SetPixel(x + 7, y, data & 0x08); - SetPixel(x + 8, y, data & 0x10); - SetPixel(x + 9, y, data & 0x10); - SetPixel(x + 10, y, data & 0x20); - SetPixel(x + 11, y, data & 0x20); - SetPixel(x + 12, y, data & 0x40); - SetPixel(x + 13, y, data & 0x40); - } - else - { - // 3 2 1 0 - // 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 - // - // - - - - - - - - 0 0 0 0 0 0 0 0 + + + + + + + + - // X 1 0 X 6 5 4 3 2 1 0 X 1 0 - - int data = _memory.ReadRamMainRegion02BF(address) << 8; - if (x < Width - CellWidth) - { - data |= _memory.ReadRamMainRegion02BF(address + 1); - SetPixel(x + 14, y, ColorHires[((~x & 0x02) << 3) | ((data << 1) & 0x06) | ((data >> 14) & 0x01)]); - SetPixel(x + 15, y, ColorHires[((~x & 0x02) << 3) | ((data << 1) & 0x06) | ((data >> 14) & 0x01)]); - } - if (x > 0) - { - data |= _memory.ReadRamMainRegion02BF(address - 1) << 16; - SetPixel(x - 2, y, ColorHires[((~x & 0x02) << 3) | ((data >> 6) & 0x04) | ((data >> 21) & 0x03)]); - SetPixel(x - 1, y, ColorHires[((~x & 0x02) << 3) | ((data >> 6) & 0x04) | ((data >> 21) & 0x03)]); - } - SetPixel(x + 0, y, ColorHires[((x & 0x02) << 3) | ((data >> 7) & 0x06) | ((data >> 22) & 0x01)]); - SetPixel(x + 1, y, ColorHires[((x & 0x02) << 3) | ((data >> 7) & 0x06) | ((data >> 22) & 0x01)]); - SetPixel(x + 2, y, ColorHires[((~x & 0x02) << 3) | ((data >> 8) & 0x07)]); - SetPixel(x + 3, y, ColorHires[((~x & 0x02) << 3) | ((data >> 8) & 0x07)]); - SetPixel(x + 4, y, ColorHires[((x & 0x02) << 3) | ((data >> 9) & 0x07)]); - SetPixel(x + 5, y, ColorHires[((x & 0x02) << 3) | ((data >> 9) & 0x07)]); - SetPixel(x + 6, y, ColorHires[((~x & 0x02) << 3) | ((data >> 10) & 0x07)]); - SetPixel(x + 7, y, ColorHires[((~x & 0x02) << 3) | ((data >> 10) & 0x07)]); - SetPixel(x + 8, y, ColorHires[((x & 0x02) << 3) | ((data >> 11) & 0x07)]); - SetPixel(x + 9, y, ColorHires[((x & 0x02) << 3) | ((data >> 11) & 0x07)]); - SetPixel(x + 10, y, ColorHires[((~x & 0x02) << 3) | ((data >> 12) & 0x07)]); - SetPixel(x + 11, y, ColorHires[((~x & 0x02) << 3) | ((data >> 12) & 0x07)]); - SetPixel(x + 12, y, ColorHires[((x & 0x02) << 3) | ((data << 2) & 0x04) | ((data >> 13) & 0x03)]); - SetPixel(x + 13, y, ColorHires[((x & 0x02) << 3) | ((data << 2) & 0x04) | ((data >> 13) & 0x03)]); - } - } - - private void DrawDHiresA(int address, int x, int y) - { - if (IsMonochrome) - { - if ((x & 0x2) == 0x00) // even cell - { - int data = ((_memory.ReadRamMainRegion02BF(address) << 7) & 0x80) | (_memory.ReadRamAuxRegion02BF(address) & 0x7F); - SetPixel(x + 0, y, data & 0x01); - SetPixel(x + 1, y, data & 0x02); - SetPixel(x + 2, y, data & 0x04); - SetPixel(x + 3, y, data & 0x08); - SetPixel(x + 4, y, data & 0x10); - SetPixel(x + 5, y, data & 0x20); - SetPixel(x + 6, y, data & 0x40); - SetPixel(x + 7, y, data & 0x80); - } - else - { - int data = ((_memory.ReadRamMainRegion02BF(address) << 9) & 0xE00) | ((_memory.ReadRamAuxRegion02BF(address) << 2) & 0x1FC) | - ((_memory.ReadRamMainRegion02BF(address - 1) >> 5) & 0x003); - SetPixel(x - 2, y, data & 0x01); - SetPixel(x - 1, y, data & 0x02); - SetPixel(x + 0, y, data & 0x04); - SetPixel(x + 1, y, data & 0x08); - SetPixel(x + 2, y, data & 0x10); - SetPixel(x + 3, y, data & 0x20); - SetPixel(x + 4, y, data & 0x40); - SetPixel(x + 5, y, data & 0x80); - SetPixel(x + 6, y, (data >> 8) & 0x01); - SetPixel(x + 7, y, (data >> 8) & 0x02); - SetPixel(x + 8, y, (data >> 8) & 0x04); - SetPixel(x + 9, y, (data >> 8) & 0x08); - } - } - else - { - if ((x & 0x2) == 0x00) // even cell - { - int data = ((_memory.ReadRamMainRegion02BF(address) << 7) & 0x80) | (_memory.ReadRamAuxRegion02BF(address) & 0x7F); - SetPixel(x + 0, y, ColorDHires0 | (data & 0x0F)); - SetPixel(x + 1, y, ColorDHires0 | (data & 0x0F)); - SetPixel(x + 2, y, ColorDHires0 | (data & 0x0F)); - SetPixel(x + 3, y, ColorDHires0 | (data & 0x0F)); - SetPixel(x + 4, y, ColorDHires0 | (data >> 4)); - SetPixel(x + 5, y, ColorDHires0 | (data >> 4)); - SetPixel(x + 6, y, ColorDHires0 | (data >> 4)); - SetPixel(x + 7, y, ColorDHires0 | (data >> 4)); - } - else - { - int data = ((_memory.ReadRamMainRegion02BF(address) << 9) & 0xE00) | ((_memory.ReadRamAuxRegion02BF(address) << 2) & 0x1FC) | - ((_memory.ReadRamMainRegion02BF(address - 1) >> 5) & 0x003); - SetPixel(x - 2, y, ColorDHires0 | (data & 0x0F)); - SetPixel(x - 1, y, ColorDHires0 | (data & 0x0F)); - SetPixel(x + 0, y, ColorDHires0 | (data & 0x0F)); - SetPixel(x + 1, y, ColorDHires0 | (data & 0x0F)); - SetPixel(x + 2, y, ColorDHires0 | ((data >> 4) & 0x0F)); - SetPixel(x + 3, y, ColorDHires0 | ((data >> 4) & 0x0F)); - SetPixel(x + 4, y, ColorDHires0 | ((data >> 4) & 0x0F)); - SetPixel(x + 5, y, ColorDHires0 | ((data >> 4) & 0x0F)); - SetPixel(x + 6, y, ColorDHires0 | (data >> 8)); - SetPixel(x + 7, y, ColorDHires0 | (data >> 8)); - SetPixel(x + 8, y, ColorDHires0 | (data >> 8)); - SetPixel(x + 9, y, ColorDHires0 | (data >> 8)); - } - } - } - - private void DrawDHiresM(int address, int x, int y) - { - if (IsMonochrome) - { - if ((x & 0x2) == 0x02) // odd cell - { - int data = ((_memory.ReadRamMainRegion02BF(address) << 1) & 0xFE) | ((_memory.ReadRamAuxRegion02BF(address) >> 6) & 0x01); - SetPixel(x + 6, y, data & 0x01); - SetPixel(x + 7, y, data & 0x02); - SetPixel(x + 8, y, data & 0x04); - SetPixel(x + 9, y, data & 0x08); - SetPixel(x + 10, y, data & 0x10); - SetPixel(x + 11, y, data & 0x20); - SetPixel(x + 12, y, data & 0x40); - SetPixel(x + 13, y, data & 0x80); - } - else - { - int data = ((_memory.ReadRamAuxRegion02BF(address + 1) << 10) & 0xC00) | ((_memory.ReadRamMainRegion02BF(address) << 3) & 0x3F8) | - ((_memory.ReadRamAuxRegion02BF(address) >> 4) & 0x007); - SetPixel(x + 4, y, data & 0x01); - SetPixel(x + 5, y, data & 0x02); - SetPixel(x + 6, y, data & 0x04); - SetPixel(x + 7, y, data & 0x08); - SetPixel(x + 8, y, data & 0x10); - SetPixel(x + 9, y, data & 0x20); - SetPixel(x + 10, y, data & 0x40); - SetPixel(x + 11, y, data & 0x80); - SetPixel(x + 12, y, (data >> 8) & 0x01); - SetPixel(x + 13, y, (data >> 8) & 0x02); - SetPixel(x + 14, y, (data >> 8) & 0x04); - SetPixel(x + 15, y, (data >> 8) & 0x08); - } - } - else - { - if ((x & 0x2) == 0x02) // odd cell - { - int data = ((_memory.ReadRamMainRegion02BF(address) << 1) & 0xFE) | ((_memory.ReadRamAuxRegion02BF(address) >> 6) & 0x01); - SetPixel(x + 6, y, ColorDHires0 | (data & 0x0F)); - SetPixel(x + 7, y, ColorDHires0 | (data & 0x0F)); - SetPixel(x + 8, y, ColorDHires0 | (data & 0x0F)); - SetPixel(x + 9, y, ColorDHires0 | (data & 0x0F)); - SetPixel(x + 10, y, ColorDHires0 | (data >> 4)); - SetPixel(x + 11, y, ColorDHires0 | (data >> 4)); - SetPixel(x + 12, y, ColorDHires0 | (data >> 4)); - SetPixel(x + 13, y, ColorDHires0 | (data >> 4)); - } - else - { - int data = ((_memory.ReadRamAuxRegion02BF(address + 1) << 10) & 0xC00) | ((_memory.ReadRamMainRegion02BF(address) << 3) & 0x3F8) | - ((_memory.ReadRamAuxRegion02BF(address) >> 4) & 0x007); - SetPixel(x + 4, y, ColorDHires0 | (data & 0x0F)); - SetPixel(x + 5, y, ColorDHires0 | (data & 0x0F)); - SetPixel(x + 6, y, ColorDHires0 | (data & 0x0F)); - SetPixel(x + 7, y, ColorDHires0 | (data & 0x0F)); - SetPixel(x + 8, y, ColorDHires0 | ((data >> 4) & 0x0F)); - SetPixel(x + 9, y, ColorDHires0 | ((data >> 4) & 0x0F)); - SetPixel(x + 10, y, ColorDHires0 | ((data >> 4) & 0x0F)); - SetPixel(x + 11, y, ColorDHires0 | ((data >> 4) & 0x0F)); - SetPixel(x + 12, y, ColorDHires0 | (data >> 8)); - SetPixel(x + 13, y, ColorDHires0 | (data >> 8)); - SetPixel(x + 14, y, ColorDHires0 | (data >> 8)); - SetPixel(x + 15, y, ColorDHires0 | (data >> 8)); - } - } - } - #endregion - - #region Flush Methods - private void FlushRowMode0(int y) - { - int address = (_memory.IsVideoPage2 ? 0x0800 : 0x0400) + AddressOffset[y]; - for (int x = 0; x < CellColumns; x++) - { - if (_isCellDirty[CellColumns * y + x]) - { - _isCellDirty[CellColumns * y + x] = false; - DrawLores(_memory.ReadRamMainRegion02BF(address + x), CellWidth * x, y); // lores - } - } - } - - private void FlushRowMode1(int y) - { - int address = (_memory.IsVideoPage2 ? 0x0800 : 0x0400) + AddressOffset[y]; - for (int x = 0; x < CellColumns; x++) - { - if (_isCellDirty[CellColumns * y + x]) - { - _isCellDirty[CellColumns * y + x] = false; - DrawText40(_memory.ReadRamMainRegion02BF(address + x), CellWidth * x, y); // text40 - } - } - } - - private void FlushRowMode2(int y) - { - int address = (_memory.IsVideoPage2 ? 0x0800 : 0x0400) + AddressOffset[y]; - for (int x = 0; x < 2 * CellColumns; x += 2) - { - if (_isCellDirty[CellColumns * y + x / 2]) - { - _isCellDirty[CellColumns * y + x / 2] = false; - DrawText80(_memory.ReadRamAuxRegion02BF(address + x / 2), CellWidth / 2 * (x + 0), y); // text80 - DrawText80(_memory.ReadRamMainRegion02BF(address + x / 2), CellWidth / 2 * (x + 1), y); - } - } - } - - private void FlushRowMode3(int y) - { - if (y < MixedHeight) - { - FlushRowMode0(y); // lores - } - else - { - FlushRowMode1(y); // text40 - } - } - - private void FlushRowMode4(int y) - { - if (y < MixedHeight) - { - FlushRowMode0(y); // lores - } - else - { - FlushRowMode2(y); // text80 - } - } - - private void FlushRowMode5(int y) - { - int address = _memory.IsVideoPage2 ? 0x4000 : 0x2000; - for (int i = 0; i < CellHeight; i++, y++) - { - for (int x = 0; x < CellColumns; x++) - { - if (_isCellDirty[CellColumns * y + x]) - { - _isCellDirty[CellColumns * y + x] = false; - DrawHires(address + AddressOffset[y] + x, CellWidth * x, y); // hires - } - } - } - } - - private void FlushRowMode6(int y) - { - if (y < MixedHeight) - { - FlushRowMode5(y); // hires - } - else - { - FlushRowMode1(y); // text40 - } - } - - private void FlushRowMode7(int y) - { - if (y < MixedHeight) - { - FlushRowMode5(y); // hires - } - else - { - FlushRowMode2(y); // text80 - } - } - - private void FlushRowMode8(int y) - { - int address = (_memory.IsVideoPage2 ? 0x0800 : 0x0400) + AddressOffset[y]; - for (int x = 0; x < CellColumns; x++) - { - if (_isCellDirty[CellColumns * y + x]) - { - _isCellDirty[CellColumns * y + x] = false; - Draw7MLores(_memory.ReadRamMainRegion02BF(address + x), CellWidth * x, y); // 7mlores - } - } - } - - private void FlushRowMode9(int y) - { - int address = (_memory.IsVideoPage2 ? 0x0800 : 0x0400) + AddressOffset[y]; - for (int x = 0; x < 2 * CellColumns; x += 2) - { - if (_isCellDirty[CellColumns * y + x / 2]) - { - _isCellDirty[CellColumns * y + x / 2] = false; - DrawDLores(_memory.ReadRamAuxRegion02BF(address + x / 2), CellWidth / 2 * (x + 0), y); // dlores - DrawDLores(_memory.ReadRamMainRegion02BF(address + x / 2), CellWidth / 2 * (x + 1), y); - } - } - } - - private void FlushRowModeA(int y) - { - if (y < MixedHeight) - { - FlushRowMode8(y); // 7mlores - } - else - { - FlushRowMode1(y); // text40 - } - } - - private void FlushRowModeB(int y) - { - if (y < MixedHeight) - { - FlushRowMode9(y); // dlores - } - else - { - FlushRowMode2(y); // text80 - } - } - - private void FlushRowModeC(int y) - { - int address = _memory.IsVideoPage2 ? 0x4000 : 0x2000; - for (int i = 0; i < CellHeight; i++, y++) - { - for (int x = 0; x < CellColumns; x++) - { - if (_isCellDirty[CellColumns * y + x]) - { - _isCellDirty[CellColumns * y + x] = false; - DrawNDHires(address + AddressOffset[y] + x, CellWidth * x, y); // ndhires - } - } - } - } - - private void FlushRowModeD(int y) - { - int address = _memory.IsVideoPage2 ? 0x4000 : 0x2000; - for (int i = 0; i < CellHeight; i++, y++) - { - for (int x = 0; x < CellColumns; x++) - { - if (_isCellDirty[CellColumns * y + x]) - { - _isCellDirty[CellColumns * y + x] = false; - DrawDHiresA(address + AddressOffset[y] + x, CellWidth * x, y); // dhires - DrawDHiresM(address + AddressOffset[y] + x, CellWidth * x, y); - } - } - } - } - - private void FlushRowModeE(int y) - { - if (y < MixedHeight) - { - FlushRowModeC(y); // ndhires - } - else - { - FlushRowMode1(y); // text40 - } - } - - private void FlushRowModeF(int y) - { - if (y < MixedHeight) - { - FlushRowModeD(y); // dhires - } - else - { - FlushRowMode2(y); // text80 - } - } - #endregion - - private void FlushRowEvent() - { - int y = (_cyclesPerVSync - _cyclesPerVBlankPreset - Machine.Events.FindEvent(_resetVSyncEvent)) / CyclesPerHSync; - - FlushRowMode[_memory.VideoMode](y - CellHeight); // in arrears - - if (y < Height) - { - Machine.Events.AddEvent(CyclesPerFlush, _flushRowEvent); - } - else - { - IsVBlank = true; - - Machine.Events.AddEvent(_cyclesPerVBlank, _leaveVBlankEvent); - } - } - - private void FlushScreen() - { - var flushRowMode = FlushRowMode[_memory.VideoMode]; - - for (int y = 0; y < Height; y += CellHeight) - { - flushRowMode(y); - } - } - - private void InverseTextEvent() - { - _isTextInversed = !_isTextInversed; - - DirtyScreenText(); - - Machine.Events.AddEvent(_cyclesPerFlash, _inverseTextEvent); - } - - private void LeaveVBlankEvent() - { - IsVBlank = false; - - Machine.Events.AddEvent(CyclesPerFlush, _flushRowEvent); - } - - private void ResetVSyncEvent() - { - Machine.Events.AddEvent(_cyclesPerVSync, _resetVSyncEvent); - } - - private void SetPalette() - { - _colorPalette[ColorMono00] = _colorBlack; - _colorPalette[ColorMono01] = _colorMonochrome; - _colorPalette[ColorMono02] = _colorMonochrome; - _colorPalette[ColorMono04] = _colorMonochrome; - _colorPalette[ColorMono08] = _colorMonochrome; - _colorPalette[ColorMono10] = _colorMonochrome; - _colorPalette[ColorMono20] = _colorMonochrome; - _colorPalette[ColorMono40] = _colorMonochrome; - _colorPalette[ColorMono80] = _colorMonochrome; - - _colorPalette[ColorWhite00] = _colorBlack; - _colorPalette[ColorWhite01] = _colorWhite; - _colorPalette[ColorWhite02] = _colorWhite; - _colorPalette[ColorWhite04] = _colorWhite; - _colorPalette[ColorWhite08] = _colorWhite; - _colorPalette[ColorWhite10] = _colorWhite; - _colorPalette[ColorWhite20] = _colorWhite; - _colorPalette[ColorWhite40] = _colorWhite; - _colorPalette[ColorWhite80] = _colorWhite; - - _colorPalette[ColorDHires0] = _colorBlack; - _colorPalette[ColorDHires1] = _colorDarkBlue; - _colorPalette[ColorDHires2] = _colorDarkGreen; - _colorPalette[ColorDHires3] = _colorMediumBlue; - _colorPalette[ColorDHires4] = _colorBrown; - _colorPalette[ColorDHires5] = _colorLightGrey; - _colorPalette[ColorDHires6] = _colorGreen; - _colorPalette[ColorDHires7] = _colorAquamarine; - _colorPalette[ColorDHires8] = _colorDeepRed; - _colorPalette[ColorDHires9] = _colorPurple; - _colorPalette[ColorDHiresA] = _colorDarkGrey; - _colorPalette[ColorDHiresB] = _colorLightBlue; - _colorPalette[ColorDHiresC] = _colorOrange; - _colorPalette[ColorDHiresD] = _colorPink; - _colorPalette[ColorDHiresE] = _colorYellow; - _colorPalette[ColorDHiresF] = _colorWhite; - - DirtyScreen(); - } - - private void SetPixel(int x, int y, int color) - { - VideoService.SetPixel(x, 2 * y, _colorPalette[color]); - } - - private void SetScanner() - { - if ((_scannerOptions & ScannerOptions.Pal) != 0) - { - _vCountPreset = VCountPresetPal; - _vLineLeaveVBlank = VLineLeaveVBlankPal; - } - else - { - _vCountPreset = VCountPresetNtsc; - _vLineLeaveVBlank = VLineLeaveVBlankNtsc; - } - - _cyclesPerVBlank = (_vLineLeaveVBlank - VLineEnterVBlank) * CyclesPerHSync; - _cyclesPerVBlankPreset = (_vLineLeaveVBlank - VLineTriggerPreset) * CyclesPerHSync; // cycles during vblank after vcount preset [3-15, 3-16] - _cyclesPerVSync = _vLineLeaveVBlank * CyclesPerHSync; - _cyclesPerFlash = VSyncsPerFlash * _cyclesPerVSync; - } - - [Newtonsoft.Json.JsonIgnore] - public int ColorBlack { get { return _colorBlack; } set { _colorBlack = value; SetPalette(); } } - [Newtonsoft.Json.JsonIgnore] - public int ColorDarkBlue { get { return _colorDarkBlue; } set { _colorDarkBlue = value; SetPalette(); } } - [Newtonsoft.Json.JsonIgnore] - public int ColorDarkGreen { get { return _colorDarkGreen; } set { _colorDarkGreen = value; SetPalette(); } } - [Newtonsoft.Json.JsonIgnore] - public int ColorMediumBlue { get { return _colorMediumBlue; } set { _colorMediumBlue = value; SetPalette(); } } - [Newtonsoft.Json.JsonIgnore] - public int ColorBrown { get { return _colorBrown; } set { _colorBrown = value; SetPalette(); } } - [Newtonsoft.Json.JsonIgnore] - public int ColorLightGrey { get { return _colorLightGrey; } set { _colorLightGrey = value; SetPalette(); } } - [Newtonsoft.Json.JsonIgnore] - public int ColorGreen { get { return _colorGreen; } set { _colorGreen = value; SetPalette(); } } - [Newtonsoft.Json.JsonIgnore] - public int ColorAquamarine { get { return _colorAquamarine; } set { _colorAquamarine = value; SetPalette(); } } - [Newtonsoft.Json.JsonIgnore] - public int ColorDeepRed { get { return _colorDeepRed; } set { _colorDeepRed = value; SetPalette(); } } - [Newtonsoft.Json.JsonIgnore] - public int ColorPurple { get { return _colorPurple; } set { _colorPurple = value; SetPalette(); } } - [Newtonsoft.Json.JsonIgnore] - public int ColorDarkGrey { get { return _colorDarkGrey; } set { _colorDarkGrey = value; SetPalette(); } } - [Newtonsoft.Json.JsonIgnore] - public int ColorLightBlue { get { return _colorLightBlue; } set { _colorLightBlue = value; SetPalette(); } } - [Newtonsoft.Json.JsonIgnore] - public int ColorOrange { get { return _colorOrange; } set { _colorOrange = value; SetPalette(); } } - [Newtonsoft.Json.JsonIgnore] - public int ColorPink { get { return _colorPink; } set { _colorPink = value; SetPalette(); } } - [Newtonsoft.Json.JsonIgnore] - public int ColorYellow { get { return _colorYellow; } set { _colorYellow = value; SetPalette(); } } - [Newtonsoft.Json.JsonIgnore] - public int ColorWhite { get { return _colorWhite; } set { _colorWhite = value; SetPalette(); } } - [Newtonsoft.Json.JsonIgnore] - public int ColorMonochrome { get { return _colorMonochrome; } set { _colorMonochrome = value; SetPalette(); } } - [Newtonsoft.Json.JsonIgnore] - public bool IsMonochrome { get { return _isMonochrome; } set { _isMonochrome = value; DirtyScreen(); } } - public ScannerOptions ScannerOptions { get { return _scannerOptions; } set { _scannerOptions = value; SetScanner(); } } - - public bool IsVBlank { get; private set; } - - private Action _flushRowEvent; - private Action _inverseTextEvent; - private Action _leaveVBlankEvent; - private Action _resetVSyncEvent; - - private Memory _memory; - public VideoService VideoService { get; private set; } - - private int _colorBlack; - private int _colorDarkBlue; - private int _colorDarkGreen; - private int _colorMediumBlue; - private int _colorBrown; - private int _colorLightGrey; - private int _colorGreen; - private int _colorAquamarine; - private int _colorDeepRed; - private int _colorPurple; - private int _colorDarkGrey; - private int _colorLightBlue; - private int _colorOrange; - private int _colorPink; - private int _colorYellow; - private int _colorWhite; - private int _colorMonochrome; - [Newtonsoft.Json.JsonIgnore] // not sync relevant - private bool _isMonochrome; - private bool _isTextInversed; - private ScannerOptions _scannerOptions; - private int _cyclesPerVBlank; - private int _cyclesPerVBlankPreset; - private int _cyclesPerVSync; - private int _cyclesPerFlash; - private int _vCountPreset; - private int _vLineLeaveVBlank; - - private ushort[] _charSet; - private int[] _colorPalette = new int[ColorPaletteCount]; - - [Newtonsoft.Json.JsonIgnore] // everything is automatically dirtied on load, so no need to save - private bool[] _isCellDirty = new bool[Height * CellColumns + 1]; // includes sentinel - } + + SetPalette(); + + IsMonochrome = false; + ScannerOptions = ScannerOptions.None; + + IsVBlank = true; + + _events.AddEvent(_cyclesPerVBlankPreset, _leaveVBlankEvent); // align flush events with scanner; assumes vcount preset at start of frame [3-15, 3-16] + _events.AddEvent(_cyclesPerVSync, _resetVSyncEvent); + _events.AddEvent(_cyclesPerFlash, _inverseTextEvent); + } + + // ReSharper disable once UnusedMember.Global + public int[] GetVideoBuffer() => Framebuffer; + + [OnDeserialized] + private void OnDeserialized(StreamingContext context) + { + // the VideoService forgets all of its information on LoadState + DirtyScreen(); + } + + public void Reset() + { + SetCharSet(); + DirtyScreen(); + FlushScreen(); + } + + void IVideo.DirtyCell(int addressOffset) + { + _isCellDirty[CellIndex[addressOffset]] = true; + } + + void IVideo.DirtyCellMixed(int addressOffset) + { + int cellIndex = CellIndex[addressOffset]; + if (cellIndex < MixedCellIndex) + { + _isCellDirty[cellIndex] = true; + } + } + + void IVideo.DirtyCellMixedText(int addressOffset) + { + int cellIndex = CellIndex[addressOffset]; + if (cellIndex >= MixedCellIndex) + { + _isCellDirty[cellIndex] = true; + } + } + + public void DirtyScreen() + { + for (int i = 0; i < Height * CellColumns; i++) + { + _isCellDirty[i] = true; + } + } + + private void DirtyScreenText() + { + if (_memory.IsText) + { + for (int i = 0; i < MixedHeight * CellColumns; i++) + { + _isCellDirty[i] = true; + } + } + if (_memory.IsText || _memory.IsMixed) + { + for (int i = MixedHeight * CellColumns; i < Height * CellColumns; i++) + { + _isCellDirty[i] = true; + } + } + } + + public int ReadFloatingBus() // [5-40] + { + // derive scanner counters from current cycles into frame; assumes hcount and vcount preset at start of frame [3-13, 3-15, 3-16] + int cycles = _cyclesPerVSync - _events.FindEvent(_resetVSyncEvent); + int hClock = cycles % CyclesPerHSync; + int hCount = (hClock != 0) ? HCountPreset + hClock - 1 : 0; + int vLine = cycles / CyclesPerHSync; + int vCount = _vCountPreset + vLine; + + // derive scanner address [5-8] + int address = ((vCount << 4) & 0x0380) | ((0x0068 + (hCount & 0x0038) + (((vCount >> 1) & 0x0060) | ((vCount >> 3) & 0x0018))) & 0x0078) | (hCount & 0x0007); + if (_memory.IsHires && !(_memory.IsMixed && ((vCount & 0xA0) == 0xA0))) // hires but not actively mixed [5-13, 5-19] + { + address |= (_memory.IsVideoPage2 ? 0x4000 : 0x2000) | ((vCount << 10) & 0x1C00); + } + else + { + address |= _memory.IsVideoPage2 ? 0x0800 : 0x0400; + if (((_scannerOptions & ScannerOptions.AppleII) != 0) && (hCount < HCountLeaveHBlank)) + { + address |= 0x1000; + } + } + + return _memory.Read(address); + } + + public void SetCharSet() + { + _charSet = !_memory.IsCharSetAlternate ? CharSetPrimary : (_memory.Monitor == MonitorType.Standard) ? CharSetSecondaryStandard : CharSetSecondaryEnhanced; + DirtyScreenText(); + } + + #region Draw Methods + + private void DrawText40(int data, int x, int y) + { + int color = IsMonochrome ? ColorMono00 : ColorWhite00; + int index = _charSet[data] * CharBitmapBytes; + int inverseMask = (_isTextInversed && !_memory.IsCharSetAlternate && (0x40 <= data) && (data <= 0x7F)) ? 0x7F : 0x00; + for (int i = 0; i < TextHeight; i++, y++) + { + data = CharBitmap[index + i] ^ inverseMask; + SetPixel(x + 0, y, color | (data & 0x01)); + SetPixel(x + 1, y, color | (data & 0x01)); + SetPixel(x + 2, y, color | (data & 0x02)); + SetPixel(x + 3, y, color | (data & 0x02)); + SetPixel(x + 4, y, color | (data & 0x04)); + SetPixel(x + 5, y, color | (data & 0x04)); + SetPixel(x + 6, y, color | (data & 0x08)); + SetPixel(x + 7, y, color | (data & 0x08)); + SetPixel(x + 8, y, color | (data & 0x10)); + SetPixel(x + 9, y, color | (data & 0x10)); + SetPixel(x + 10, y, color | (data & 0x20)); + SetPixel(x + 11, y, color | (data & 0x20)); + SetPixel(x + 12, y, color | (data & 0x40)); + SetPixel(x + 13, y, color | (data & 0x40)); + } + } + + private void DrawText80(int data, int x, int y) + { + int color = IsMonochrome ? ColorMono00 : ColorWhite00; + int index = _charSet[data] * CharBitmapBytes; + int mask = (_isTextInversed && !_memory.IsCharSetAlternate && (0x40 <= data) && (data <= 0x7F)) ? 0x7F : 0x00; + for (int i = 0; i < TextHeight; i++, y++) + { + data = CharBitmap[index + i] ^ mask; + SetPixel(x + 0, y, color | (data & 0x01)); + SetPixel(x + 1, y, color | (data & 0x02)); + SetPixel(x + 2, y, color | (data & 0x04)); + SetPixel(x + 3, y, color | (data & 0x08)); + SetPixel(x + 4, y, color | (data & 0x10)); + SetPixel(x + 5, y, color | (data & 0x20)); + SetPixel(x + 6, y, color | (data & 0x40)); + } + } + + private void DrawLores(int data, int x, int y) + { + if (IsMonochrome) + { + if ((x & 0x02) == 0x02) // odd cell + { + data = ((data << 2) & 0xCC) | ((data >> 2) & 0x33); + } + for (int i = 0; i < LoresHeight; i++, y++) + { + SetPixel(x + 0, y, data & 0x01); + SetPixel(x + 1, y, data & 0x02); + SetPixel(x + 2, y, data & 0x04); + SetPixel(x + 3, y, data & 0x08); + SetPixel(x + 4, y, data & 0x01); + SetPixel(x + 5, y, data & 0x02); + SetPixel(x + 6, y, data & 0x04); + SetPixel(x + 7, y, data & 0x08); + SetPixel(x + 8, y, data & 0x01); + SetPixel(x + 9, y, data & 0x02); + SetPixel(x + 10, y, data & 0x04); + SetPixel(x + 11, y, data & 0x08); + SetPixel(x + 12, y, data & 0x01); + SetPixel(x + 13, y, data & 0x02); + } + for (int i = 0; i < LoresHeight; i++, y++) + { + SetPixel(x + 0, y, data & 0x10); + SetPixel(x + 1, y, data & 0x20); + SetPixel(x + 2, y, data & 0x40); + SetPixel(x + 3, y, data & 0x80); + SetPixel(x + 4, y, data & 0x10); + SetPixel(x + 5, y, data & 0x20); + SetPixel(x + 6, y, data & 0x40); + SetPixel(x + 7, y, data & 0x80); + SetPixel(x + 8, y, data & 0x10); + SetPixel(x + 9, y, data & 0x20); + SetPixel(x + 10, y, data & 0x40); + SetPixel(x + 11, y, data & 0x80); + SetPixel(x + 12, y, data & 0x10); + SetPixel(x + 13, y, data & 0x20); + } + } + else + { + int color = ColorLores[data & 0x0F]; + for (int i = 0; i < LoresHeight; i++, y++) + { + SetPixel(x + 0, y, color); + SetPixel(x + 1, y, color); + SetPixel(x + 2, y, color); + SetPixel(x + 3, y, color); + SetPixel(x + 4, y, color); + SetPixel(x + 5, y, color); + SetPixel(x + 6, y, color); + SetPixel(x + 7, y, color); + SetPixel(x + 8, y, color); + SetPixel(x + 9, y, color); + SetPixel(x + 10, y, color); + SetPixel(x + 11, y, color); + SetPixel(x + 12, y, color); + SetPixel(x + 13, y, color); + } + color = ColorLores[data >> 4]; + for (int i = 0; i < LoresHeight; i++, y++) + { + SetPixel(x + 0, y, color); + SetPixel(x + 1, y, color); + SetPixel(x + 2, y, color); + SetPixel(x + 3, y, color); + SetPixel(x + 4, y, color); + SetPixel(x + 5, y, color); + SetPixel(x + 6, y, color); + SetPixel(x + 7, y, color); + SetPixel(x + 8, y, color); + SetPixel(x + 9, y, color); + SetPixel(x + 10, y, color); + SetPixel(x + 11, y, color); + SetPixel(x + 12, y, color); + SetPixel(x + 13, y, color); + } + } + } + + private void Draw7MLores(int data, int x, int y) + { + if (IsMonochrome) + { + if ((x & 0x02) == 0x02) // odd cell + { + data = ((data << 2) & 0xCC) | ((data >> 2) & 0x33); + } + for (int i = 0; i < LoresHeight; i++, y++) + { + SetPixel(x + 0, y, data & 0x01); + SetPixel(x + 1, y, data & 0x01); + SetPixel(x + 2, y, data & 0x02); + SetPixel(x + 3, y, data & 0x02); + SetPixel(x + 4, y, data & 0x04); + SetPixel(x + 5, y, data & 0x04); + SetPixel(x + 6, y, data & 0x08); + SetPixel(x + 7, y, data & 0x08); + SetPixel(x + 8, y, data & 0x01); + SetPixel(x + 9, y, data & 0x01); + SetPixel(x + 10, y, data & 0x02); + SetPixel(x + 11, y, data & 0x02); + SetPixel(x + 12, y, data & 0x04); + SetPixel(x + 13, y, data & 0x04); + } + for (int i = 0; i < LoresHeight; i++, y++) + { + SetPixel(x + 0, y, data & 0x10); + SetPixel(x + 1, y, data & 0x10); + SetPixel(x + 2, y, data & 0x20); + SetPixel(x + 3, y, data & 0x20); + SetPixel(x + 4, y, data & 0x40); + SetPixel(x + 5, y, data & 0x40); + SetPixel(x + 6, y, data & 0x80); + SetPixel(x + 7, y, data & 0x80); + SetPixel(x + 8, y, data & 0x10); + SetPixel(x + 9, y, data & 0x10); + SetPixel(x + 10, y, data & 0x20); + SetPixel(x + 11, y, data & 0x20); + SetPixel(x + 12, y, data & 0x40); + SetPixel(x + 13, y, data & 0x40); + } + } + else + { + int color = Color7MLores[((x & 0x02) << 3) | (data & 0x0F)]; + for (int i = 0; i < LoresHeight; i++, y++) + { + SetPixel(x + 0, y, color); + SetPixel(x + 1, y, color); + SetPixel(x + 2, y, color); + SetPixel(x + 3, y, color); + SetPixel(x + 4, y, color); + SetPixel(x + 5, y, color); + SetPixel(x + 6, y, color); + SetPixel(x + 7, y, color); + SetPixel(x + 8, y, color); + SetPixel(x + 9, y, color); + SetPixel(x + 10, y, color); + SetPixel(x + 11, y, color); + SetPixel(x + 12, y, color); + SetPixel(x + 13, y, color); + } + color = Color7MLores[((x & 0x02) << 3) | (data >> 4)]; + for (int i = 0; i < LoresHeight; i++, y++) + { + SetPixel(x + 0, y, color); + SetPixel(x + 1, y, color); + SetPixel(x + 2, y, color); + SetPixel(x + 3, y, color); + SetPixel(x + 4, y, color); + SetPixel(x + 5, y, color); + SetPixel(x + 6, y, color); + SetPixel(x + 7, y, color); + SetPixel(x + 8, y, color); + SetPixel(x + 9, y, color); + SetPixel(x + 10, y, color); + SetPixel(x + 11, y, color); + SetPixel(x + 12, y, color); + SetPixel(x + 13, y, color); + } + } + } + + private void DrawDLores(int data, int x, int y) + { + if (IsMonochrome) + { + if ((x & 0x01) == 0x00) // even half cell + { + data = ((data << 1) & 0xEE) | ((data >> 3) & 0x11); + } + for (int i = 0; i < LoresHeight; i++, y++) + { + SetPixel(x + 0, y, data & 0x01); + SetPixel(x + 1, y, data & 0x02); + SetPixel(x + 2, y, data & 0x04); + SetPixel(x + 3, y, data & 0x08); + SetPixel(x + 4, y, data & 0x01); + SetPixel(x + 5, y, data & 0x02); + SetPixel(x + 6, y, data & 0x04); + } + for (int i = 0; i < LoresHeight; i++, y++) + { + SetPixel(x + 0, y, data & 0x10); + SetPixel(x + 1, y, data & 0x20); + SetPixel(x + 2, y, data & 0x40); + SetPixel(x + 3, y, data & 0x80); + SetPixel(x + 4, y, data & 0x10); + SetPixel(x + 5, y, data & 0x20); + SetPixel(x + 6, y, data & 0x40); + } + } + else + { + int color = ColorDLores[((x & 0x01) << 4) | (data & 0x0F)]; + for (int i = 0; i < LoresHeight; i++, y++) + { + SetPixel(x + 0, y, color); + SetPixel(x + 1, y, color); + SetPixel(x + 2, y, color); + SetPixel(x + 3, y, color); + SetPixel(x + 4, y, color); + SetPixel(x + 5, y, color); + SetPixel(x + 6, y, color); + } + color = ColorDLores[((x & 0x01) << 4) | (data >> 4)]; + for (int i = 0; i < LoresHeight; i++, y++) + { + SetPixel(x + 0, y, color); + SetPixel(x + 1, y, color); + SetPixel(x + 2, y, color); + SetPixel(x + 3, y, color); + SetPixel(x + 4, y, color); + SetPixel(x + 5, y, color); + SetPixel(x + 6, y, color); + } + } + } + + private void DrawHires(int address, int x, int y) + { + if (IsMonochrome) + { + int data = _memory.ReadRamMainRegion02BF(address); + SetPixel(x + 0, y, data & 0x01); + SetPixel(x + 1, y, data & 0x01); + SetPixel(x + 2, y, data & 0x02); + SetPixel(x + 3, y, data & 0x02); + SetPixel(x + 4, y, data & 0x04); + SetPixel(x + 5, y, data & 0x04); + SetPixel(x + 6, y, data & 0x08); + SetPixel(x + 7, y, data & 0x08); + SetPixel(x + 8, y, data & 0x10); + SetPixel(x + 9, y, data & 0x10); + SetPixel(x + 10, y, data & 0x20); + SetPixel(x + 11, y, data & 0x20); + SetPixel(x + 12, y, data & 0x40); + SetPixel(x + 13, y, data & 0x40); + } + else + { + // 3 2 1 0 + // 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + // + // - - - - - - - - 0 0 0 0 0 0 0 0 + + + + + + + + + // H 1 0 H 6 5 4 3 2 1 0 H 1 0 + + int data = _memory.ReadRamMainRegion02BF(address) << 8; + if (x < Width - CellWidth) + { + data |= _memory.ReadRamMainRegion02BF(address + 1); + SetPixel(x + 14, y, ColorHires[((~x & 0x02) << 3) | ((data >> 4) & 0x08) | ((data << 1) & 0x06) | ((data >> 14) & 0x01)]); + SetPixel(x + 15, y, ColorHires[((~x & 0x02) << 3) | ((data >> 4) & 0x08) | ((data << 1) & 0x06) | ((data >> 14) & 0x01)]); + } + if (x > 0) + { + data |= _memory.ReadRamMainRegion02BF(address - 1) << 16; + SetPixel(x - 2, y, ColorHires[((~x & 0x02) << 3) | ((data >> 20) & 0x08) | ((data >> 6) & 0x04) | ((data >> 21) & 0x03)]); + SetPixel(x - 1, y, ColorHires[((~x & 0x02) << 3) | ((data >> 20) & 0x08) | ((data >> 6) & 0x04) | ((data >> 21) & 0x03)]); + } + SetPixel(x + 0, y, ColorHires[((x & 0x02) << 3) | ((data >> 12) & 0x08) | ((data >> 7) & 0x06) | ((data >> 22) & 0x01)]); + SetPixel(x + 1, y, ColorHires[((x & 0x02) << 3) | ((data >> 12) & 0x08) | ((data >> 7) & 0x06) | ((data >> 22) & 0x01)]); + SetPixel(x + 2, y, ColorHires[((~x & 0x02) << 3) | ((data >> 12) & 0x08) | ((data >> 8) & 0x07)]); + SetPixel(x + 3, y, ColorHires[((~x & 0x02) << 3) | ((data >> 12) & 0x08) | ((data >> 8) & 0x07)]); + SetPixel(x + 4, y, ColorHires[((x & 0x02) << 3) | ((data >> 12) & 0x08) | ((data >> 9) & 0x07)]); + SetPixel(x + 5, y, ColorHires[((x & 0x02) << 3) | ((data >> 12) & 0x08) | ((data >> 9) & 0x07)]); + SetPixel(x + 6, y, ColorHires[((~x & 0x02) << 3) | ((data >> 12) & 0x08) | ((data >> 10) & 0x07)]); + SetPixel(x + 7, y, ColorHires[((~x & 0x02) << 3) | ((data >> 12) & 0x08) | ((data >> 10) & 0x07)]); + SetPixel(x + 8, y, ColorHires[((x & 0x02) << 3) | ((data >> 12) & 0x08) | ((data >> 11) & 0x07)]); + SetPixel(x + 9, y, ColorHires[((x & 0x02) << 3) | ((data >> 12) & 0x08) | ((data >> 11) & 0x07)]); + SetPixel(x + 10, y, ColorHires[((~x & 0x02) << 3) | ((data >> 12) & 0x0F)]); + SetPixel(x + 11, y, ColorHires[((~x & 0x02) << 3) | ((data >> 12) & 0x0F)]); + SetPixel(x + 12, y, ColorHires[((x & 0x02) << 3) | ((data >> 12) & 0x08) | ((data << 2) & 0x04) | ((data >> 13) & 0x03)]); + SetPixel(x + 13, y, ColorHires[((x & 0x02) << 3) | ((data >> 12) & 0x08) | ((data << 2) & 0x04) | ((data >> 13) & 0x03)]); + } + } + + private void DrawNDHires(int address, int x, int y) + { + if (IsMonochrome) + { + int data = _memory.ReadRamMainRegion02BF(address); + SetPixel(x + 0, y, data & 0x01); + SetPixel(x + 1, y, data & 0x01); + SetPixel(x + 2, y, data & 0x02); + SetPixel(x + 3, y, data & 0x02); + SetPixel(x + 4, y, data & 0x04); + SetPixel(x + 5, y, data & 0x04); + SetPixel(x + 6, y, data & 0x08); + SetPixel(x + 7, y, data & 0x08); + SetPixel(x + 8, y, data & 0x10); + SetPixel(x + 9, y, data & 0x10); + SetPixel(x + 10, y, data & 0x20); + SetPixel(x + 11, y, data & 0x20); + SetPixel(x + 12, y, data & 0x40); + SetPixel(x + 13, y, data & 0x40); + } + else + { + // 3 2 1 0 + // 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + // + // - - - - - - - - 0 0 0 0 0 0 0 0 + + + + + + + + + // X 1 0 X 6 5 4 3 2 1 0 X 1 0 + + int data = _memory.ReadRamMainRegion02BF(address) << 8; + if (x < Width - CellWidth) + { + data |= _memory.ReadRamMainRegion02BF(address + 1); + SetPixel(x + 14, y, ColorHires[((~x & 0x02) << 3) | ((data << 1) & 0x06) | ((data >> 14) & 0x01)]); + SetPixel(x + 15, y, ColorHires[((~x & 0x02) << 3) | ((data << 1) & 0x06) | ((data >> 14) & 0x01)]); + } + if (x > 0) + { + data |= _memory.ReadRamMainRegion02BF(address - 1) << 16; + SetPixel(x - 2, y, ColorHires[((~x & 0x02) << 3) | ((data >> 6) & 0x04) | ((data >> 21) & 0x03)]); + SetPixel(x - 1, y, ColorHires[((~x & 0x02) << 3) | ((data >> 6) & 0x04) | ((data >> 21) & 0x03)]); + } + SetPixel(x + 0, y, ColorHires[((x & 0x02) << 3) | ((data >> 7) & 0x06) | ((data >> 22) & 0x01)]); + SetPixel(x + 1, y, ColorHires[((x & 0x02) << 3) | ((data >> 7) & 0x06) | ((data >> 22) & 0x01)]); + SetPixel(x + 2, y, ColorHires[((~x & 0x02) << 3) | ((data >> 8) & 0x07)]); + SetPixel(x + 3, y, ColorHires[((~x & 0x02) << 3) | ((data >> 8) & 0x07)]); + SetPixel(x + 4, y, ColorHires[((x & 0x02) << 3) | ((data >> 9) & 0x07)]); + SetPixel(x + 5, y, ColorHires[((x & 0x02) << 3) | ((data >> 9) & 0x07)]); + SetPixel(x + 6, y, ColorHires[((~x & 0x02) << 3) | ((data >> 10) & 0x07)]); + SetPixel(x + 7, y, ColorHires[((~x & 0x02) << 3) | ((data >> 10) & 0x07)]); + SetPixel(x + 8, y, ColorHires[((x & 0x02) << 3) | ((data >> 11) & 0x07)]); + SetPixel(x + 9, y, ColorHires[((x & 0x02) << 3) | ((data >> 11) & 0x07)]); + SetPixel(x + 10, y, ColorHires[((~x & 0x02) << 3) | ((data >> 12) & 0x07)]); + SetPixel(x + 11, y, ColorHires[((~x & 0x02) << 3) | ((data >> 12) & 0x07)]); + SetPixel(x + 12, y, ColorHires[((x & 0x02) << 3) | ((data << 2) & 0x04) | ((data >> 13) & 0x03)]); + SetPixel(x + 13, y, ColorHires[((x & 0x02) << 3) | ((data << 2) & 0x04) | ((data >> 13) & 0x03)]); + } + } + + private void DrawDHiresA(int address, int x, int y) + { + if (IsMonochrome) + { + if ((x & 0x2) == 0x00) // even cell + { + int data = ((_memory.ReadRamMainRegion02BF(address) << 7) & 0x80) | (_memory.ReadRamAuxRegion02BF(address) & 0x7F); + SetPixel(x + 0, y, data & 0x01); + SetPixel(x + 1, y, data & 0x02); + SetPixel(x + 2, y, data & 0x04); + SetPixel(x + 3, y, data & 0x08); + SetPixel(x + 4, y, data & 0x10); + SetPixel(x + 5, y, data & 0x20); + SetPixel(x + 6, y, data & 0x40); + SetPixel(x + 7, y, data & 0x80); + } + else + { + int data = ((_memory.ReadRamMainRegion02BF(address) << 9) & 0xE00) | ((_memory.ReadRamAuxRegion02BF(address) << 2) & 0x1FC) | + ((_memory.ReadRamMainRegion02BF(address - 1) >> 5) & 0x003); + SetPixel(x - 2, y, data & 0x01); + SetPixel(x - 1, y, data & 0x02); + SetPixel(x + 0, y, data & 0x04); + SetPixel(x + 1, y, data & 0x08); + SetPixel(x + 2, y, data & 0x10); + SetPixel(x + 3, y, data & 0x20); + SetPixel(x + 4, y, data & 0x40); + SetPixel(x + 5, y, data & 0x80); + SetPixel(x + 6, y, (data >> 8) & 0x01); + SetPixel(x + 7, y, (data >> 8) & 0x02); + SetPixel(x + 8, y, (data >> 8) & 0x04); + SetPixel(x + 9, y, (data >> 8) & 0x08); + } + } + else + { + if ((x & 0x2) == 0x00) // even cell + { + int data = ((_memory.ReadRamMainRegion02BF(address) << 7) & 0x80) | (_memory.ReadRamAuxRegion02BF(address) & 0x7F); + SetPixel(x + 0, y, ColorDHires0 | (data & 0x0F)); + SetPixel(x + 1, y, ColorDHires0 | (data & 0x0F)); + SetPixel(x + 2, y, ColorDHires0 | (data & 0x0F)); + SetPixel(x + 3, y, ColorDHires0 | (data & 0x0F)); + SetPixel(x + 4, y, ColorDHires0 | (data >> 4)); + SetPixel(x + 5, y, ColorDHires0 | (data >> 4)); + SetPixel(x + 6, y, ColorDHires0 | (data >> 4)); + SetPixel(x + 7, y, ColorDHires0 | (data >> 4)); + } + else + { + int data = ((_memory.ReadRamMainRegion02BF(address) << 9) & 0xE00) | ((_memory.ReadRamAuxRegion02BF(address) << 2) & 0x1FC) | + ((_memory.ReadRamMainRegion02BF(address - 1) >> 5) & 0x003); + SetPixel(x - 2, y, ColorDHires0 | (data & 0x0F)); + SetPixel(x - 1, y, ColorDHires0 | (data & 0x0F)); + SetPixel(x + 0, y, ColorDHires0 | (data & 0x0F)); + SetPixel(x + 1, y, ColorDHires0 | (data & 0x0F)); + SetPixel(x + 2, y, ColorDHires0 | ((data >> 4) & 0x0F)); + SetPixel(x + 3, y, ColorDHires0 | ((data >> 4) & 0x0F)); + SetPixel(x + 4, y, ColorDHires0 | ((data >> 4) & 0x0F)); + SetPixel(x + 5, y, ColorDHires0 | ((data >> 4) & 0x0F)); + SetPixel(x + 6, y, ColorDHires0 | (data >> 8)); + SetPixel(x + 7, y, ColorDHires0 | (data >> 8)); + SetPixel(x + 8, y, ColorDHires0 | (data >> 8)); + SetPixel(x + 9, y, ColorDHires0 | (data >> 8)); + } + } + } + + private void DrawDHiresM(int address, int x, int y) + { + if (IsMonochrome) + { + if ((x & 0x2) == 0x02) // odd cell + { + int data = ((_memory.ReadRamMainRegion02BF(address) << 1) & 0xFE) | ((_memory.ReadRamAuxRegion02BF(address) >> 6) & 0x01); + SetPixel(x + 6, y, data & 0x01); + SetPixel(x + 7, y, data & 0x02); + SetPixel(x + 8, y, data & 0x04); + SetPixel(x + 9, y, data & 0x08); + SetPixel(x + 10, y, data & 0x10); + SetPixel(x + 11, y, data & 0x20); + SetPixel(x + 12, y, data & 0x40); + SetPixel(x + 13, y, data & 0x80); + } + else + { + int data = ((_memory.ReadRamAuxRegion02BF(address + 1) << 10) & 0xC00) | ((_memory.ReadRamMainRegion02BF(address) << 3) & 0x3F8) | + ((_memory.ReadRamAuxRegion02BF(address) >> 4) & 0x007); + SetPixel(x + 4, y, data & 0x01); + SetPixel(x + 5, y, data & 0x02); + SetPixel(x + 6, y, data & 0x04); + SetPixel(x + 7, y, data & 0x08); + SetPixel(x + 8, y, data & 0x10); + SetPixel(x + 9, y, data & 0x20); + SetPixel(x + 10, y, data & 0x40); + SetPixel(x + 11, y, data & 0x80); + SetPixel(x + 12, y, (data >> 8) & 0x01); + SetPixel(x + 13, y, (data >> 8) & 0x02); + SetPixel(x + 14, y, (data >> 8) & 0x04); + SetPixel(x + 15, y, (data >> 8) & 0x08); + } + } + else + { + if ((x & 0x2) == 0x02) // odd cell + { + int data = ((_memory.ReadRamMainRegion02BF(address) << 1) & 0xFE) | ((_memory.ReadRamAuxRegion02BF(address) >> 6) & 0x01); + SetPixel(x + 6, y, ColorDHires0 | (data & 0x0F)); + SetPixel(x + 7, y, ColorDHires0 | (data & 0x0F)); + SetPixel(x + 8, y, ColorDHires0 | (data & 0x0F)); + SetPixel(x + 9, y, ColorDHires0 | (data & 0x0F)); + SetPixel(x + 10, y, ColorDHires0 | (data >> 4)); + SetPixel(x + 11, y, ColorDHires0 | (data >> 4)); + SetPixel(x + 12, y, ColorDHires0 | (data >> 4)); + SetPixel(x + 13, y, ColorDHires0 | (data >> 4)); + } + else + { + int data = ((_memory.ReadRamAuxRegion02BF(address + 1) << 10) & 0xC00) | ((_memory.ReadRamMainRegion02BF(address) << 3) & 0x3F8) | + ((_memory.ReadRamAuxRegion02BF(address) >> 4) & 0x007); + SetPixel(x + 4, y, ColorDHires0 | (data & 0x0F)); + SetPixel(x + 5, y, ColorDHires0 | (data & 0x0F)); + SetPixel(x + 6, y, ColorDHires0 | (data & 0x0F)); + SetPixel(x + 7, y, ColorDHires0 | (data & 0x0F)); + SetPixel(x + 8, y, ColorDHires0 | ((data >> 4) & 0x0F)); + SetPixel(x + 9, y, ColorDHires0 | ((data >> 4) & 0x0F)); + SetPixel(x + 10, y, ColorDHires0 | ((data >> 4) & 0x0F)); + SetPixel(x + 11, y, ColorDHires0 | ((data >> 4) & 0x0F)); + SetPixel(x + 12, y, ColorDHires0 | (data >> 8)); + SetPixel(x + 13, y, ColorDHires0 | (data >> 8)); + SetPixel(x + 14, y, ColorDHires0 | (data >> 8)); + SetPixel(x + 15, y, ColorDHires0 | (data >> 8)); + } + } + } + + #endregion + + #region Flush Methods + private void FlushRowMode0(int y) + { + int address = (_memory.IsVideoPage2 ? 0x0800 : 0x0400) + AddressOffset[y]; + for (int x = 0; x < CellColumns; x++) + { + if (_isCellDirty[CellColumns * y + x]) + { + _isCellDirty[CellColumns * y + x] = false; + DrawLores(_memory.ReadRamMainRegion02BF(address + x), CellWidth * x, y); // lores + } + } + } + + private void FlushRowMode1(int y) + { + int address = (_memory.IsVideoPage2 ? 0x0800 : 0x0400) + AddressOffset[y]; + for (int x = 0; x < CellColumns; x++) + { + if (_isCellDirty[CellColumns * y + x]) + { + _isCellDirty[CellColumns * y + x] = false; + DrawText40(_memory.ReadRamMainRegion02BF(address + x), CellWidth * x, y); // text40 + } + } + } + + private void FlushRowMode2(int y) + { + int address = (_memory.IsVideoPage2 ? 0x0800 : 0x0400) + AddressOffset[y]; + for (int x = 0; x < 2 * CellColumns; x += 2) + { + if (_isCellDirty[CellColumns * y + x / 2]) + { + _isCellDirty[CellColumns * y + x / 2] = false; + DrawText80(_memory.ReadRamAuxRegion02BF(address + x / 2), CellWidth / 2 * (x + 0), y); // text80 + DrawText80(_memory.ReadRamMainRegion02BF(address + x / 2), CellWidth / 2 * (x + 1), y); + } + } + } + + private void FlushRowMode3(int y) + { + if (y < MixedHeight) + { + FlushRowMode0(y); // lores + } + else + { + FlushRowMode1(y); // text40 + } + } + + private void FlushRowMode4(int y) + { + if (y < MixedHeight) + { + FlushRowMode0(y); // lores + } + else + { + FlushRowMode2(y); // text80 + } + } + + private void FlushRowMode5(int y) + { + int address = _memory.IsVideoPage2 ? 0x4000 : 0x2000; + for (int i = 0; i < CellHeight; i++, y++) + { + for (int x = 0; x < CellColumns; x++) + { + if (_isCellDirty[CellColumns * y + x]) + { + _isCellDirty[CellColumns * y + x] = false; + DrawHires(address + AddressOffset[y] + x, CellWidth * x, y); // hires + } + } + } + } + + private void FlushRowMode6(int y) + { + if (y < MixedHeight) + { + FlushRowMode5(y); // hires + } + else + { + FlushRowMode1(y); // text40 + } + } + + private void FlushRowMode7(int y) + { + if (y < MixedHeight) + { + FlushRowMode5(y); // hires + } + else + { + FlushRowMode2(y); // text80 + } + } + + private void FlushRowMode8(int y) + { + int address = (_memory.IsVideoPage2 ? 0x0800 : 0x0400) + AddressOffset[y]; + for (int x = 0; x < CellColumns; x++) + { + if (_isCellDirty[CellColumns * y + x]) + { + _isCellDirty[CellColumns * y + x] = false; + Draw7MLores(_memory.ReadRamMainRegion02BF(address + x), CellWidth * x, y); // 7mlores + } + } + } + + private void FlushRowMode9(int y) + { + int address = (_memory.IsVideoPage2 ? 0x0800 : 0x0400) + AddressOffset[y]; + for (int x = 0; x < 2 * CellColumns; x += 2) + { + if (_isCellDirty[CellColumns * y + x / 2]) + { + _isCellDirty[CellColumns * y + x / 2] = false; + DrawDLores(_memory.ReadRamAuxRegion02BF(address + x / 2), CellWidth / 2 * (x + 0), y); // dlores + DrawDLores(_memory.ReadRamMainRegion02BF(address + x / 2), CellWidth / 2 * (x + 1), y); + } + } + } + + private void FlushRowModeA(int y) + { + if (y < MixedHeight) + { + FlushRowMode8(y); // 7mlores + } + else + { + FlushRowMode1(y); // text40 + } + } + + private void FlushRowModeB(int y) + { + if (y < MixedHeight) + { + FlushRowMode9(y); // dlores + } + else + { + FlushRowMode2(y); // text80 + } + } + + private void FlushRowModeC(int y) + { + int address = _memory.IsVideoPage2 ? 0x4000 : 0x2000; + for (int i = 0; i < CellHeight; i++, y++) + { + for (int x = 0; x < CellColumns; x++) + { + if (_isCellDirty[CellColumns * y + x]) + { + _isCellDirty[CellColumns * y + x] = false; + DrawNDHires(address + AddressOffset[y] + x, CellWidth * x, y); // ndhires + } + } + } + } + + private void FlushRowModeD(int y) + { + int address = _memory.IsVideoPage2 ? 0x4000 : 0x2000; + for (int i = 0; i < CellHeight; i++, y++) + { + for (int x = 0; x < CellColumns; x++) + { + if (_isCellDirty[CellColumns * y + x]) + { + _isCellDirty[CellColumns * y + x] = false; + DrawDHiresA(address + AddressOffset[y] + x, CellWidth * x, y); // dhires + DrawDHiresM(address + AddressOffset[y] + x, CellWidth * x, y); + } + } + } + } + + private void FlushRowModeE(int y) + { + if (y < MixedHeight) + { + FlushRowModeC(y); // ndhires + } + else + { + FlushRowMode1(y); // text40 + } + } + + private void FlushRowModeF(int y) + { + if (y < MixedHeight) + { + FlushRowModeD(y); // dhires + } + else + { + FlushRowMode2(y); // text80 + } + } + #endregion + + private void FlushRowEvent() + { + int y = (_cyclesPerVSync - _cyclesPerVBlankPreset - _events.FindEvent(_resetVSyncEvent)) / CyclesPerHSync; + + _flushRowMode[_memory.VideoMode](y - CellHeight); // in arrears + + if (y < Height) + { + _events.AddEvent(CyclesPerFlush, _flushRowEvent); + } + else + { + IsVBlank = true; + _events.AddEvent(_cyclesPerVBlank, _leaveVBlankEvent); + } + } + + private void FlushScreen() + { + var flushRowMode = _flushRowMode[_memory.VideoMode]; + for (int y = 0; y < Height; y += CellHeight) + { + flushRowMode(y); + } + } + + private void InverseTextEvent() + { + _isTextInversed = !_isTextInversed; + DirtyScreenText(); + _events.AddEvent(_cyclesPerFlash, _inverseTextEvent); + } + + private void LeaveVBlankEvent() + { + IsVBlank = false; + _events.AddEvent(CyclesPerFlush, _flushRowEvent); + } + + private void ResetVSyncEvent() + { + _events.AddEvent(_cyclesPerVSync, _resetVSyncEvent); + } + + private void SetPalette() + { + _colorPalette[ColorMono00] = _colorBlack; + _colorPalette[ColorMono01] = _colorMonochrome; + _colorPalette[ColorMono02] = _colorMonochrome; + _colorPalette[ColorMono04] = _colorMonochrome; + _colorPalette[ColorMono08] = _colorMonochrome; + _colorPalette[ColorMono10] = _colorMonochrome; + _colorPalette[ColorMono20] = _colorMonochrome; + _colorPalette[ColorMono40] = _colorMonochrome; + _colorPalette[ColorMono80] = _colorMonochrome; + + _colorPalette[ColorWhite00] = _colorBlack; + _colorPalette[ColorWhite01] = _colorWhite; + _colorPalette[ColorWhite02] = _colorWhite; + _colorPalette[ColorWhite04] = _colorWhite; + _colorPalette[ColorWhite08] = _colorWhite; + _colorPalette[ColorWhite10] = _colorWhite; + _colorPalette[ColorWhite20] = _colorWhite; + _colorPalette[ColorWhite40] = _colorWhite; + _colorPalette[ColorWhite80] = _colorWhite; + + _colorPalette[ColorDHires0] = _colorBlack; + _colorPalette[ColorDHires1] = _colorDarkBlue; + _colorPalette[ColorDHires2] = _colorDarkGreen; + _colorPalette[ColorDHires3] = _colorMediumBlue; + _colorPalette[ColorDHires4] = _colorBrown; + _colorPalette[ColorDHires5] = _colorLightGrey; + _colorPalette[ColorDHires6] = _colorGreen; + _colorPalette[ColorDHires7] = _colorAquamarine; + _colorPalette[ColorDHires8] = _colorDeepRed; + _colorPalette[ColorDHires9] = _colorPurple; + _colorPalette[ColorDHiresA] = _colorDarkGrey; + _colorPalette[ColorDHiresB] = _colorLightBlue; + _colorPalette[ColorDHiresC] = _colorOrange; + _colorPalette[ColorDHiresD] = _colorPink; + _colorPalette[ColorDHiresE] = _colorYellow; + _colorPalette[ColorDHiresF] = _colorWhite; + + DirtyScreen(); + } + + // ReSharper disable once AutoPropertyCanBeMadeGetOnly.Local + private int[] Framebuffer { get; set; } = new int[560 * 384]; + + private void SetPixel(int x, int y, int color) + { + int i = 560 * (2 * y) + x; + Framebuffer[i] = Framebuffer[i + 560] = _colorPalette[color]; + } + + private void SetScanner() + { + if ((_scannerOptions & ScannerOptions.Pal) != 0) + { + _vCountPreset = VCountPresetPal; + _vLineLeaveVBlank = VLineLeaveVBlankPal; + } + else + { + _vCountPreset = VCountPresetNtsc; + _vLineLeaveVBlank = VLineLeaveVBlankNtsc; + } + + _cyclesPerVBlank = (_vLineLeaveVBlank - VLineEnterVBlank) * CyclesPerHSync; + _cyclesPerVBlankPreset = (_vLineLeaveVBlank - VLineTriggerPreset) * CyclesPerHSync; // cycles during vblank after vcount preset [3-15, 3-16] + _cyclesPerVSync = _vLineLeaveVBlank * CyclesPerHSync; + _cyclesPerFlash = VSyncsPerFlash * _cyclesPerVSync; + } + + [JsonIgnore] + public bool IsMonochrome { get => _isMonochrome; set { _isMonochrome = value; DirtyScreen(); } } + + [JsonIgnore] + internal ScannerOptions ScannerOptions { get => _scannerOptions; set { _scannerOptions = value; SetScanner(); } } + + public bool IsVBlank { get; private set; } + + private Action _flushRowEvent; + private Action _inverseTextEvent; + private Action _leaveVBlankEvent; + private Action _resetVSyncEvent; + + private int _colorBlack; + private int _colorDarkBlue; + private int _colorDarkGreen; + private int _colorMediumBlue; + private int _colorBrown; + private int _colorLightGrey; + private int _colorGreen; + private int _colorAquamarine; + private int _colorDeepRed; + private int _colorPurple; + private int _colorDarkGrey; + private int _colorLightBlue; + private int _colorOrange; + private int _colorPink; + private int _colorYellow; + private int _colorWhite; + private int _colorMonochrome; + + [JsonIgnore] // not sync relevant + private bool _isMonochrome; + + private bool _isTextInversed; + private ScannerOptions _scannerOptions; + private int _cyclesPerVBlank; + private int _cyclesPerVBlankPreset; + private int _cyclesPerVSync; + private int _cyclesPerFlash; + private int _vCountPreset; + private int _vLineLeaveVBlank; + + private ushort[] _charSet; + private int[] _colorPalette = new int[ColorPaletteCount]; + + [JsonIgnore] // everything is automatically dirtied on load, so no need to save + private bool[] _isCellDirty = new bool[Height * CellColumns + 1]; // includes sentinel + } } diff --git a/ExternalCoreProjects/Virtu/VideoData.cs b/ExternalCoreProjects/Virtu/VideoData.cs deleted file mode 100644 index 0579e26d0b..0000000000 --- a/ExternalCoreProjects/Virtu/VideoData.cs +++ /dev/null @@ -1,1644 +0,0 @@ -using System; - -namespace Jellyfish.Virtu -{ - public partial class Video - { - private static readonly int[] AddressOffset = new int[Height] - { - 0x0000, 0x0400, 0x0800, 0x0C00, 0x1000, 0x1400, 0x1800, 0x1C00, - 0x0080, 0x0480, 0x0880, 0x0C80, 0x1080, 0x1480, 0x1880, 0x1C80, - 0x0100, 0x0500, 0x0900, 0x0D00, 0x1100, 0x1500, 0x1900, 0x1D00, - 0x0180, 0x0580, 0x0980, 0x0D80, 0x1180, 0x1580, 0x1980, 0x1D80, - 0x0200, 0x0600, 0x0A00, 0x0E00, 0x1200, 0x1600, 0x1A00, 0x1E00, - 0x0280, 0x0680, 0x0A80, 0x0E80, 0x1280, 0x1680, 0x1A80, 0x1E80, - 0x0300, 0x0700, 0x0B00, 0x0F00, 0x1300, 0x1700, 0x1B00, 0x1F00, - 0x0380, 0x0780, 0x0B80, 0x0F80, 0x1380, 0x1780, 0x1B80, 0x1F80, - 0x0028, 0x0428, 0x0828, 0x0C28, 0x1028, 0x1428, 0x1828, 0x1C28, - 0x00A8, 0x04A8, 0x08A8, 0x0CA8, 0x10A8, 0x14A8, 0x18A8, 0x1CA8, - 0x0128, 0x0528, 0x0928, 0x0D28, 0x1128, 0x1528, 0x1928, 0x1D28, - 0x01A8, 0x05A8, 0x09A8, 0x0DA8, 0x11A8, 0x15A8, 0x19A8, 0x1DA8, - 0x0228, 0x0628, 0x0A28, 0x0E28, 0x1228, 0x1628, 0x1A28, 0x1E28, - 0x02A8, 0x06A8, 0x0AA8, 0x0EA8, 0x12A8, 0x16A8, 0x1AA8, 0x1EA8, - 0x0328, 0x0728, 0x0B28, 0x0F28, 0x1328, 0x1728, 0x1B28, 0x1F28, - 0x03A8, 0x07A8, 0x0BA8, 0x0FA8, 0x13A8, 0x17A8, 0x1BA8, 0x1FA8, - 0x0050, 0x0450, 0x0850, 0x0C50, 0x1050, 0x1450, 0x1850, 0x1C50, - 0x00D0, 0x04D0, 0x08D0, 0x0CD0, 0x10D0, 0x14D0, 0x18D0, 0x1CD0, - 0x0150, 0x0550, 0x0950, 0x0D50, 0x1150, 0x1550, 0x1950, 0x1D50, - 0x01D0, 0x05D0, 0x09D0, 0x0DD0, 0x11D0, 0x15D0, 0x19D0, 0x1DD0, - 0x0250, 0x0650, 0x0A50, 0x0E50, 0x1250, 0x1650, 0x1A50, 0x1E50, - 0x02D0, 0x06D0, 0x0AD0, 0x0ED0, 0x12D0, 0x16D0, 0x1AD0, 0x1ED0, - 0x0350, 0x0750, 0x0B50, 0x0F50, 0x1350, 0x1750, 0x1B50, 0x1F50, - 0x03D0, 0x07D0, 0x0BD0, 0x0FD0, 0x13D0, 0x17D0, 0x1BD0, 0x1FD0 - }; - - private const int CellWidth = 14; - private const int CellHeight = 8; - private const int CellRows = Height / CellHeight; - private const int CellColumns = Width / CellWidth; - - private const int CellIndexCount = 0x2000; - - private static readonly ushort[] CellIndex = new ushort[CellIndexCount] - { - 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, - 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, - 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, - 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, - 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, - 0x0A00, 0x0A01, 0x0A02, 0x0A03, 0x0A04, 0x0A05, 0x0A06, 0x0A07, - 0x0A08, 0x0A09, 0x0A0A, 0x0A0B, 0x0A0C, 0x0A0D, 0x0A0E, 0x0A0F, - 0x0A10, 0x0A11, 0x0A12, 0x0A13, 0x0A14, 0x0A15, 0x0A16, 0x0A17, - 0x0A18, 0x0A19, 0x0A1A, 0x0A1B, 0x0A1C, 0x0A1D, 0x0A1E, 0x0A1F, - 0x0A20, 0x0A21, 0x0A22, 0x0A23, 0x0A24, 0x0A25, 0x0A26, 0x0A27, - 0x1400, 0x1401, 0x1402, 0x1403, 0x1404, 0x1405, 0x1406, 0x1407, - 0x1408, 0x1409, 0x140A, 0x140B, 0x140C, 0x140D, 0x140E, 0x140F, - 0x1410, 0x1411, 0x1412, 0x1413, 0x1414, 0x1415, 0x1416, 0x1417, - 0x1418, 0x1419, 0x141A, 0x141B, 0x141C, 0x141D, 0x141E, 0x141F, - 0x1420, 0x1421, 0x1422, 0x1423, 0x1424, 0x1425, 0x1426, 0x1427, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0140, 0x0141, 0x0142, 0x0143, 0x0144, 0x0145, 0x0146, 0x0147, - 0x0148, 0x0149, 0x014A, 0x014B, 0x014C, 0x014D, 0x014E, 0x014F, - 0x0150, 0x0151, 0x0152, 0x0153, 0x0154, 0x0155, 0x0156, 0x0157, - 0x0158, 0x0159, 0x015A, 0x015B, 0x015C, 0x015D, 0x015E, 0x015F, - 0x0160, 0x0161, 0x0162, 0x0163, 0x0164, 0x0165, 0x0166, 0x0167, - 0x0B40, 0x0B41, 0x0B42, 0x0B43, 0x0B44, 0x0B45, 0x0B46, 0x0B47, - 0x0B48, 0x0B49, 0x0B4A, 0x0B4B, 0x0B4C, 0x0B4D, 0x0B4E, 0x0B4F, - 0x0B50, 0x0B51, 0x0B52, 0x0B53, 0x0B54, 0x0B55, 0x0B56, 0x0B57, - 0x0B58, 0x0B59, 0x0B5A, 0x0B5B, 0x0B5C, 0x0B5D, 0x0B5E, 0x0B5F, - 0x0B60, 0x0B61, 0x0B62, 0x0B63, 0x0B64, 0x0B65, 0x0B66, 0x0B67, - 0x1540, 0x1541, 0x1542, 0x1543, 0x1544, 0x1545, 0x1546, 0x1547, - 0x1548, 0x1549, 0x154A, 0x154B, 0x154C, 0x154D, 0x154E, 0x154F, - 0x1550, 0x1551, 0x1552, 0x1553, 0x1554, 0x1555, 0x1556, 0x1557, - 0x1558, 0x1559, 0x155A, 0x155B, 0x155C, 0x155D, 0x155E, 0x155F, - 0x1560, 0x1561, 0x1562, 0x1563, 0x1564, 0x1565, 0x1566, 0x1567, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0286, 0x0287, - 0x0288, 0x0289, 0x028A, 0x028B, 0x028C, 0x028D, 0x028E, 0x028F, - 0x0290, 0x0291, 0x0292, 0x0293, 0x0294, 0x0295, 0x0296, 0x0297, - 0x0298, 0x0299, 0x029A, 0x029B, 0x029C, 0x029D, 0x029E, 0x029F, - 0x02A0, 0x02A1, 0x02A2, 0x02A3, 0x02A4, 0x02A5, 0x02A6, 0x02A7, - 0x0C80, 0x0C81, 0x0C82, 0x0C83, 0x0C84, 0x0C85, 0x0C86, 0x0C87, - 0x0C88, 0x0C89, 0x0C8A, 0x0C8B, 0x0C8C, 0x0C8D, 0x0C8E, 0x0C8F, - 0x0C90, 0x0C91, 0x0C92, 0x0C93, 0x0C94, 0x0C95, 0x0C96, 0x0C97, - 0x0C98, 0x0C99, 0x0C9A, 0x0C9B, 0x0C9C, 0x0C9D, 0x0C9E, 0x0C9F, - 0x0CA0, 0x0CA1, 0x0CA2, 0x0CA3, 0x0CA4, 0x0CA5, 0x0CA6, 0x0CA7, - 0x1680, 0x1681, 0x1682, 0x1683, 0x1684, 0x1685, 0x1686, 0x1687, - 0x1688, 0x1689, 0x168A, 0x168B, 0x168C, 0x168D, 0x168E, 0x168F, - 0x1690, 0x1691, 0x1692, 0x1693, 0x1694, 0x1695, 0x1696, 0x1697, - 0x1698, 0x1699, 0x169A, 0x169B, 0x169C, 0x169D, 0x169E, 0x169F, - 0x16A0, 0x16A1, 0x16A2, 0x16A3, 0x16A4, 0x16A5, 0x16A6, 0x16A7, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, - 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x03CF, - 0x03D0, 0x03D1, 0x03D2, 0x03D3, 0x03D4, 0x03D5, 0x03D6, 0x03D7, - 0x03D8, 0x03D9, 0x03DA, 0x03DB, 0x03DC, 0x03DD, 0x03DE, 0x03DF, - 0x03E0, 0x03E1, 0x03E2, 0x03E3, 0x03E4, 0x03E5, 0x03E6, 0x03E7, - 0x0DC0, 0x0DC1, 0x0DC2, 0x0DC3, 0x0DC4, 0x0DC5, 0x0DC6, 0x0DC7, - 0x0DC8, 0x0DC9, 0x0DCA, 0x0DCB, 0x0DCC, 0x0DCD, 0x0DCE, 0x0DCF, - 0x0DD0, 0x0DD1, 0x0DD2, 0x0DD3, 0x0DD4, 0x0DD5, 0x0DD6, 0x0DD7, - 0x0DD8, 0x0DD9, 0x0DDA, 0x0DDB, 0x0DDC, 0x0DDD, 0x0DDE, 0x0DDF, - 0x0DE0, 0x0DE1, 0x0DE2, 0x0DE3, 0x0DE4, 0x0DE5, 0x0DE6, 0x0DE7, - 0x17C0, 0x17C1, 0x17C2, 0x17C3, 0x17C4, 0x17C5, 0x17C6, 0x17C7, - 0x17C8, 0x17C9, 0x17CA, 0x17CB, 0x17CC, 0x17CD, 0x17CE, 0x17CF, - 0x17D0, 0x17D1, 0x17D2, 0x17D3, 0x17D4, 0x17D5, 0x17D6, 0x17D7, - 0x17D8, 0x17D9, 0x17DA, 0x17DB, 0x17DC, 0x17DD, 0x17DE, 0x17DF, - 0x17E0, 0x17E1, 0x17E2, 0x17E3, 0x17E4, 0x17E5, 0x17E6, 0x17E7, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0500, 0x0501, 0x0502, 0x0503, 0x0504, 0x0505, 0x0506, 0x0507, - 0x0508, 0x0509, 0x050A, 0x050B, 0x050C, 0x050D, 0x050E, 0x050F, - 0x0510, 0x0511, 0x0512, 0x0513, 0x0514, 0x0515, 0x0516, 0x0517, - 0x0518, 0x0519, 0x051A, 0x051B, 0x051C, 0x051D, 0x051E, 0x051F, - 0x0520, 0x0521, 0x0522, 0x0523, 0x0524, 0x0525, 0x0526, 0x0527, - 0x0F00, 0x0F01, 0x0F02, 0x0F03, 0x0F04, 0x0F05, 0x0F06, 0x0F07, - 0x0F08, 0x0F09, 0x0F0A, 0x0F0B, 0x0F0C, 0x0F0D, 0x0F0E, 0x0F0F, - 0x0F10, 0x0F11, 0x0F12, 0x0F13, 0x0F14, 0x0F15, 0x0F16, 0x0F17, - 0x0F18, 0x0F19, 0x0F1A, 0x0F1B, 0x0F1C, 0x0F1D, 0x0F1E, 0x0F1F, - 0x0F20, 0x0F21, 0x0F22, 0x0F23, 0x0F24, 0x0F25, 0x0F26, 0x0F27, - 0x1900, 0x1901, 0x1902, 0x1903, 0x1904, 0x1905, 0x1906, 0x1907, - 0x1908, 0x1909, 0x190A, 0x190B, 0x190C, 0x190D, 0x190E, 0x190F, - 0x1910, 0x1911, 0x1912, 0x1913, 0x1914, 0x1915, 0x1916, 0x1917, - 0x1918, 0x1919, 0x191A, 0x191B, 0x191C, 0x191D, 0x191E, 0x191F, - 0x1920, 0x1921, 0x1922, 0x1923, 0x1924, 0x1925, 0x1926, 0x1927, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0640, 0x0641, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, - 0x0648, 0x0649, 0x064A, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, - 0x0650, 0x0651, 0x0652, 0x0653, 0x0654, 0x0655, 0x0656, 0x0657, - 0x0658, 0x0659, 0x065A, 0x065B, 0x065C, 0x065D, 0x065E, 0x065F, - 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, - 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047, - 0x1048, 0x1049, 0x104A, 0x104B, 0x104C, 0x104D, 0x104E, 0x104F, - 0x1050, 0x1051, 0x1052, 0x1053, 0x1054, 0x1055, 0x1056, 0x1057, - 0x1058, 0x1059, 0x105A, 0x105B, 0x105C, 0x105D, 0x105E, 0x105F, - 0x1060, 0x1061, 0x1062, 0x1063, 0x1064, 0x1065, 0x1066, 0x1067, - 0x1A40, 0x1A41, 0x1A42, 0x1A43, 0x1A44, 0x1A45, 0x1A46, 0x1A47, - 0x1A48, 0x1A49, 0x1A4A, 0x1A4B, 0x1A4C, 0x1A4D, 0x1A4E, 0x1A4F, - 0x1A50, 0x1A51, 0x1A52, 0x1A53, 0x1A54, 0x1A55, 0x1A56, 0x1A57, - 0x1A58, 0x1A59, 0x1A5A, 0x1A5B, 0x1A5C, 0x1A5D, 0x1A5E, 0x1A5F, - 0x1A60, 0x1A61, 0x1A62, 0x1A63, 0x1A64, 0x1A65, 0x1A66, 0x1A67, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0786, 0x0787, - 0x0788, 0x0789, 0x078A, 0x078B, 0x078C, 0x078D, 0x078E, 0x078F, - 0x0790, 0x0791, 0x0792, 0x0793, 0x0794, 0x0795, 0x0796, 0x0797, - 0x0798, 0x0799, 0x079A, 0x079B, 0x079C, 0x079D, 0x079E, 0x079F, - 0x07A0, 0x07A1, 0x07A2, 0x07A3, 0x07A4, 0x07A5, 0x07A6, 0x07A7, - 0x1180, 0x1181, 0x1182, 0x1183, 0x1184, 0x1185, 0x1186, 0x1187, - 0x1188, 0x1189, 0x118A, 0x118B, 0x118C, 0x118D, 0x118E, 0x118F, - 0x1190, 0x1191, 0x1192, 0x1193, 0x1194, 0x1195, 0x1196, 0x1197, - 0x1198, 0x1199, 0x119A, 0x119B, 0x119C, 0x119D, 0x119E, 0x119F, - 0x11A0, 0x11A1, 0x11A2, 0x11A3, 0x11A4, 0x11A5, 0x11A6, 0x11A7, - 0x1B80, 0x1B81, 0x1B82, 0x1B83, 0x1B84, 0x1B85, 0x1B86, 0x1B87, - 0x1B88, 0x1B89, 0x1B8A, 0x1B8B, 0x1B8C, 0x1B8D, 0x1B8E, 0x1B8F, - 0x1B90, 0x1B91, 0x1B92, 0x1B93, 0x1B94, 0x1B95, 0x1B96, 0x1B97, - 0x1B98, 0x1B99, 0x1B9A, 0x1B9B, 0x1B9C, 0x1B9D, 0x1B9E, 0x1B9F, - 0x1BA0, 0x1BA1, 0x1BA2, 0x1BA3, 0x1BA4, 0x1BA5, 0x1BA6, 0x1BA7, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x08C0, 0x08C1, 0x08C2, 0x08C3, 0x08C4, 0x08C5, 0x08C6, 0x08C7, - 0x08C8, 0x08C9, 0x08CA, 0x08CB, 0x08CC, 0x08CD, 0x08CE, 0x08CF, - 0x08D0, 0x08D1, 0x08D2, 0x08D3, 0x08D4, 0x08D5, 0x08D6, 0x08D7, - 0x08D8, 0x08D9, 0x08DA, 0x08DB, 0x08DC, 0x08DD, 0x08DE, 0x08DF, - 0x08E0, 0x08E1, 0x08E2, 0x08E3, 0x08E4, 0x08E5, 0x08E6, 0x08E7, - 0x12C0, 0x12C1, 0x12C2, 0x12C3, 0x12C4, 0x12C5, 0x12C6, 0x12C7, - 0x12C8, 0x12C9, 0x12CA, 0x12CB, 0x12CC, 0x12CD, 0x12CE, 0x12CF, - 0x12D0, 0x12D1, 0x12D2, 0x12D3, 0x12D4, 0x12D5, 0x12D6, 0x12D7, - 0x12D8, 0x12D9, 0x12DA, 0x12DB, 0x12DC, 0x12DD, 0x12DE, 0x12DF, - 0x12E0, 0x12E1, 0x12E2, 0x12E3, 0x12E4, 0x12E5, 0x12E6, 0x12E7, - 0x1CC0, 0x1CC1, 0x1CC2, 0x1CC3, 0x1CC4, 0x1CC5, 0x1CC6, 0x1CC7, - 0x1CC8, 0x1CC9, 0x1CCA, 0x1CCB, 0x1CCC, 0x1CCD, 0x1CCE, 0x1CCF, - 0x1CD0, 0x1CD1, 0x1CD2, 0x1CD3, 0x1CD4, 0x1CD5, 0x1CD6, 0x1CD7, - 0x1CD8, 0x1CD9, 0x1CDA, 0x1CDB, 0x1CDC, 0x1CDD, 0x1CDE, 0x1CDF, - 0x1CE0, 0x1CE1, 0x1CE2, 0x1CE3, 0x1CE4, 0x1CE5, 0x1CE6, 0x1CE7, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, - 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, - 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, - 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, - 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, - 0x0A28, 0x0A29, 0x0A2A, 0x0A2B, 0x0A2C, 0x0A2D, 0x0A2E, 0x0A2F, - 0x0A30, 0x0A31, 0x0A32, 0x0A33, 0x0A34, 0x0A35, 0x0A36, 0x0A37, - 0x0A38, 0x0A39, 0x0A3A, 0x0A3B, 0x0A3C, 0x0A3D, 0x0A3E, 0x0A3F, - 0x0A40, 0x0A41, 0x0A42, 0x0A43, 0x0A44, 0x0A45, 0x0A46, 0x0A47, - 0x0A48, 0x0A49, 0x0A4A, 0x0A4B, 0x0A4C, 0x0A4D, 0x0A4E, 0x0A4F, - 0x1428, 0x1429, 0x142A, 0x142B, 0x142C, 0x142D, 0x142E, 0x142F, - 0x1430, 0x1431, 0x1432, 0x1433, 0x1434, 0x1435, 0x1436, 0x1437, - 0x1438, 0x1439, 0x143A, 0x143B, 0x143C, 0x143D, 0x143E, 0x143F, - 0x1440, 0x1441, 0x1442, 0x1443, 0x1444, 0x1445, 0x1446, 0x1447, - 0x1448, 0x1449, 0x144A, 0x144B, 0x144C, 0x144D, 0x144E, 0x144F, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0168, 0x0169, 0x016A, 0x016B, 0x016C, 0x016D, 0x016E, 0x016F, - 0x0170, 0x0171, 0x0172, 0x0173, 0x0174, 0x0175, 0x0176, 0x0177, - 0x0178, 0x0179, 0x017A, 0x017B, 0x017C, 0x017D, 0x017E, 0x017F, - 0x0180, 0x0181, 0x0182, 0x0183, 0x0184, 0x0185, 0x0186, 0x0187, - 0x0188, 0x0189, 0x018A, 0x018B, 0x018C, 0x018D, 0x018E, 0x018F, - 0x0B68, 0x0B69, 0x0B6A, 0x0B6B, 0x0B6C, 0x0B6D, 0x0B6E, 0x0B6F, - 0x0B70, 0x0B71, 0x0B72, 0x0B73, 0x0B74, 0x0B75, 0x0B76, 0x0B77, - 0x0B78, 0x0B79, 0x0B7A, 0x0B7B, 0x0B7C, 0x0B7D, 0x0B7E, 0x0B7F, - 0x0B80, 0x0B81, 0x0B82, 0x0B83, 0x0B84, 0x0B85, 0x0B86, 0x0B87, - 0x0B88, 0x0B89, 0x0B8A, 0x0B8B, 0x0B8C, 0x0B8D, 0x0B8E, 0x0B8F, - 0x1568, 0x1569, 0x156A, 0x156B, 0x156C, 0x156D, 0x156E, 0x156F, - 0x1570, 0x1571, 0x1572, 0x1573, 0x1574, 0x1575, 0x1576, 0x1577, - 0x1578, 0x1579, 0x157A, 0x157B, 0x157C, 0x157D, 0x157E, 0x157F, - 0x1580, 0x1581, 0x1582, 0x1583, 0x1584, 0x1585, 0x1586, 0x1587, - 0x1588, 0x1589, 0x158A, 0x158B, 0x158C, 0x158D, 0x158E, 0x158F, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x02A8, 0x02A9, 0x02AA, 0x02AB, 0x02AC, 0x02AD, 0x02AE, 0x02AF, - 0x02B0, 0x02B1, 0x02B2, 0x02B3, 0x02B4, 0x02B5, 0x02B6, 0x02B7, - 0x02B8, 0x02B9, 0x02BA, 0x02BB, 0x02BC, 0x02BD, 0x02BE, 0x02BF, - 0x02C0, 0x02C1, 0x02C2, 0x02C3, 0x02C4, 0x02C5, 0x02C6, 0x02C7, - 0x02C8, 0x02C9, 0x02CA, 0x02CB, 0x02CC, 0x02CD, 0x02CE, 0x02CF, - 0x0CA8, 0x0CA9, 0x0CAA, 0x0CAB, 0x0CAC, 0x0CAD, 0x0CAE, 0x0CAF, - 0x0CB0, 0x0CB1, 0x0CB2, 0x0CB3, 0x0CB4, 0x0CB5, 0x0CB6, 0x0CB7, - 0x0CB8, 0x0CB9, 0x0CBA, 0x0CBB, 0x0CBC, 0x0CBD, 0x0CBE, 0x0CBF, - 0x0CC0, 0x0CC1, 0x0CC2, 0x0CC3, 0x0CC4, 0x0CC5, 0x0CC6, 0x0CC7, - 0x0CC8, 0x0CC9, 0x0CCA, 0x0CCB, 0x0CCC, 0x0CCD, 0x0CCE, 0x0CCF, - 0x16A8, 0x16A9, 0x16AA, 0x16AB, 0x16AC, 0x16AD, 0x16AE, 0x16AF, - 0x16B0, 0x16B1, 0x16B2, 0x16B3, 0x16B4, 0x16B5, 0x16B6, 0x16B7, - 0x16B8, 0x16B9, 0x16BA, 0x16BB, 0x16BC, 0x16BD, 0x16BE, 0x16BF, - 0x16C0, 0x16C1, 0x16C2, 0x16C3, 0x16C4, 0x16C5, 0x16C6, 0x16C7, - 0x16C8, 0x16C9, 0x16CA, 0x16CB, 0x16CC, 0x16CD, 0x16CE, 0x16CF, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x03E8, 0x03E9, 0x03EA, 0x03EB, 0x03EC, 0x03ED, 0x03EE, 0x03EF, - 0x03F0, 0x03F1, 0x03F2, 0x03F3, 0x03F4, 0x03F5, 0x03F6, 0x03F7, - 0x03F8, 0x03F9, 0x03FA, 0x03FB, 0x03FC, 0x03FD, 0x03FE, 0x03FF, - 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, - 0x0408, 0x0409, 0x040A, 0x040B, 0x040C, 0x040D, 0x040E, 0x040F, - 0x0DE8, 0x0DE9, 0x0DEA, 0x0DEB, 0x0DEC, 0x0DED, 0x0DEE, 0x0DEF, - 0x0DF0, 0x0DF1, 0x0DF2, 0x0DF3, 0x0DF4, 0x0DF5, 0x0DF6, 0x0DF7, - 0x0DF8, 0x0DF9, 0x0DFA, 0x0DFB, 0x0DFC, 0x0DFD, 0x0DFE, 0x0DFF, - 0x0E00, 0x0E01, 0x0E02, 0x0E03, 0x0E04, 0x0E05, 0x0E06, 0x0E07, - 0x0E08, 0x0E09, 0x0E0A, 0x0E0B, 0x0E0C, 0x0E0D, 0x0E0E, 0x0E0F, - 0x17E8, 0x17E9, 0x17EA, 0x17EB, 0x17EC, 0x17ED, 0x17EE, 0x17EF, - 0x17F0, 0x17F1, 0x17F2, 0x17F3, 0x17F4, 0x17F5, 0x17F6, 0x17F7, - 0x17F8, 0x17F9, 0x17FA, 0x17FB, 0x17FC, 0x17FD, 0x17FE, 0x17FF, - 0x1800, 0x1801, 0x1802, 0x1803, 0x1804, 0x1805, 0x1806, 0x1807, - 0x1808, 0x1809, 0x180A, 0x180B, 0x180C, 0x180D, 0x180E, 0x180F, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0528, 0x0529, 0x052A, 0x052B, 0x052C, 0x052D, 0x052E, 0x052F, - 0x0530, 0x0531, 0x0532, 0x0533, 0x0534, 0x0535, 0x0536, 0x0537, - 0x0538, 0x0539, 0x053A, 0x053B, 0x053C, 0x053D, 0x053E, 0x053F, - 0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0546, 0x0547, - 0x0548, 0x0549, 0x054A, 0x054B, 0x054C, 0x054D, 0x054E, 0x054F, - 0x0F28, 0x0F29, 0x0F2A, 0x0F2B, 0x0F2C, 0x0F2D, 0x0F2E, 0x0F2F, - 0x0F30, 0x0F31, 0x0F32, 0x0F33, 0x0F34, 0x0F35, 0x0F36, 0x0F37, - 0x0F38, 0x0F39, 0x0F3A, 0x0F3B, 0x0F3C, 0x0F3D, 0x0F3E, 0x0F3F, - 0x0F40, 0x0F41, 0x0F42, 0x0F43, 0x0F44, 0x0F45, 0x0F46, 0x0F47, - 0x0F48, 0x0F49, 0x0F4A, 0x0F4B, 0x0F4C, 0x0F4D, 0x0F4E, 0x0F4F, - 0x1928, 0x1929, 0x192A, 0x192B, 0x192C, 0x192D, 0x192E, 0x192F, - 0x1930, 0x1931, 0x1932, 0x1933, 0x1934, 0x1935, 0x1936, 0x1937, - 0x1938, 0x1939, 0x193A, 0x193B, 0x193C, 0x193D, 0x193E, 0x193F, - 0x1940, 0x1941, 0x1942, 0x1943, 0x1944, 0x1945, 0x1946, 0x1947, - 0x1948, 0x1949, 0x194A, 0x194B, 0x194C, 0x194D, 0x194E, 0x194F, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0668, 0x0669, 0x066A, 0x066B, 0x066C, 0x066D, 0x066E, 0x066F, - 0x0670, 0x0671, 0x0672, 0x0673, 0x0674, 0x0675, 0x0676, 0x0677, - 0x0678, 0x0679, 0x067A, 0x067B, 0x067C, 0x067D, 0x067E, 0x067F, - 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0686, 0x0687, - 0x0688, 0x0689, 0x068A, 0x068B, 0x068C, 0x068D, 0x068E, 0x068F, - 0x1068, 0x1069, 0x106A, 0x106B, 0x106C, 0x106D, 0x106E, 0x106F, - 0x1070, 0x1071, 0x1072, 0x1073, 0x1074, 0x1075, 0x1076, 0x1077, - 0x1078, 0x1079, 0x107A, 0x107B, 0x107C, 0x107D, 0x107E, 0x107F, - 0x1080, 0x1081, 0x1082, 0x1083, 0x1084, 0x1085, 0x1086, 0x1087, - 0x1088, 0x1089, 0x108A, 0x108B, 0x108C, 0x108D, 0x108E, 0x108F, - 0x1A68, 0x1A69, 0x1A6A, 0x1A6B, 0x1A6C, 0x1A6D, 0x1A6E, 0x1A6F, - 0x1A70, 0x1A71, 0x1A72, 0x1A73, 0x1A74, 0x1A75, 0x1A76, 0x1A77, - 0x1A78, 0x1A79, 0x1A7A, 0x1A7B, 0x1A7C, 0x1A7D, 0x1A7E, 0x1A7F, - 0x1A80, 0x1A81, 0x1A82, 0x1A83, 0x1A84, 0x1A85, 0x1A86, 0x1A87, - 0x1A88, 0x1A89, 0x1A8A, 0x1A8B, 0x1A8C, 0x1A8D, 0x1A8E, 0x1A8F, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x07A8, 0x07A9, 0x07AA, 0x07AB, 0x07AC, 0x07AD, 0x07AE, 0x07AF, - 0x07B0, 0x07B1, 0x07B2, 0x07B3, 0x07B4, 0x07B5, 0x07B6, 0x07B7, - 0x07B8, 0x07B9, 0x07BA, 0x07BB, 0x07BC, 0x07BD, 0x07BE, 0x07BF, - 0x07C0, 0x07C1, 0x07C2, 0x07C3, 0x07C4, 0x07C5, 0x07C6, 0x07C7, - 0x07C8, 0x07C9, 0x07CA, 0x07CB, 0x07CC, 0x07CD, 0x07CE, 0x07CF, - 0x11A8, 0x11A9, 0x11AA, 0x11AB, 0x11AC, 0x11AD, 0x11AE, 0x11AF, - 0x11B0, 0x11B1, 0x11B2, 0x11B3, 0x11B4, 0x11B5, 0x11B6, 0x11B7, - 0x11B8, 0x11B9, 0x11BA, 0x11BB, 0x11BC, 0x11BD, 0x11BE, 0x11BF, - 0x11C0, 0x11C1, 0x11C2, 0x11C3, 0x11C4, 0x11C5, 0x11C6, 0x11C7, - 0x11C8, 0x11C9, 0x11CA, 0x11CB, 0x11CC, 0x11CD, 0x11CE, 0x11CF, - 0x1BA8, 0x1BA9, 0x1BAA, 0x1BAB, 0x1BAC, 0x1BAD, 0x1BAE, 0x1BAF, - 0x1BB0, 0x1BB1, 0x1BB2, 0x1BB3, 0x1BB4, 0x1BB5, 0x1BB6, 0x1BB7, - 0x1BB8, 0x1BB9, 0x1BBA, 0x1BBB, 0x1BBC, 0x1BBD, 0x1BBE, 0x1BBF, - 0x1BC0, 0x1BC1, 0x1BC2, 0x1BC3, 0x1BC4, 0x1BC5, 0x1BC6, 0x1BC7, - 0x1BC8, 0x1BC9, 0x1BCA, 0x1BCB, 0x1BCC, 0x1BCD, 0x1BCE, 0x1BCF, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x08E8, 0x08E9, 0x08EA, 0x08EB, 0x08EC, 0x08ED, 0x08EE, 0x08EF, - 0x08F0, 0x08F1, 0x08F2, 0x08F3, 0x08F4, 0x08F5, 0x08F6, 0x08F7, - 0x08F8, 0x08F9, 0x08FA, 0x08FB, 0x08FC, 0x08FD, 0x08FE, 0x08FF, - 0x0900, 0x0901, 0x0902, 0x0903, 0x0904, 0x0905, 0x0906, 0x0907, - 0x0908, 0x0909, 0x090A, 0x090B, 0x090C, 0x090D, 0x090E, 0x090F, - 0x12E8, 0x12E9, 0x12EA, 0x12EB, 0x12EC, 0x12ED, 0x12EE, 0x12EF, - 0x12F0, 0x12F1, 0x12F2, 0x12F3, 0x12F4, 0x12F5, 0x12F6, 0x12F7, - 0x12F8, 0x12F9, 0x12FA, 0x12FB, 0x12FC, 0x12FD, 0x12FE, 0x12FF, - 0x1300, 0x1301, 0x1302, 0x1303, 0x1304, 0x1305, 0x1306, 0x1307, - 0x1308, 0x1309, 0x130A, 0x130B, 0x130C, 0x130D, 0x130E, 0x130F, - 0x1CE8, 0x1CE9, 0x1CEA, 0x1CEB, 0x1CEC, 0x1CED, 0x1CEE, 0x1CEF, - 0x1CF0, 0x1CF1, 0x1CF2, 0x1CF3, 0x1CF4, 0x1CF5, 0x1CF6, 0x1CF7, - 0x1CF8, 0x1CF9, 0x1CFA, 0x1CFB, 0x1CFC, 0x1CFD, 0x1CFE, 0x1CFF, - 0x1D00, 0x1D01, 0x1D02, 0x1D03, 0x1D04, 0x1D05, 0x1D06, 0x1D07, - 0x1D08, 0x1D09, 0x1D0A, 0x1D0B, 0x1D0C, 0x1D0D, 0x1D0E, 0x1D0F, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, - 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, - 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, - 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, - 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, - 0x0A50, 0x0A51, 0x0A52, 0x0A53, 0x0A54, 0x0A55, 0x0A56, 0x0A57, - 0x0A58, 0x0A59, 0x0A5A, 0x0A5B, 0x0A5C, 0x0A5D, 0x0A5E, 0x0A5F, - 0x0A60, 0x0A61, 0x0A62, 0x0A63, 0x0A64, 0x0A65, 0x0A66, 0x0A67, - 0x0A68, 0x0A69, 0x0A6A, 0x0A6B, 0x0A6C, 0x0A6D, 0x0A6E, 0x0A6F, - 0x0A70, 0x0A71, 0x0A72, 0x0A73, 0x0A74, 0x0A75, 0x0A76, 0x0A77, - 0x1450, 0x1451, 0x1452, 0x1453, 0x1454, 0x1455, 0x1456, 0x1457, - 0x1458, 0x1459, 0x145A, 0x145B, 0x145C, 0x145D, 0x145E, 0x145F, - 0x1460, 0x1461, 0x1462, 0x1463, 0x1464, 0x1465, 0x1466, 0x1467, - 0x1468, 0x1469, 0x146A, 0x146B, 0x146C, 0x146D, 0x146E, 0x146F, - 0x1470, 0x1471, 0x1472, 0x1473, 0x1474, 0x1475, 0x1476, 0x1477, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0190, 0x0191, 0x0192, 0x0193, 0x0194, 0x0195, 0x0196, 0x0197, - 0x0198, 0x0199, 0x019A, 0x019B, 0x019C, 0x019D, 0x019E, 0x019F, - 0x01A0, 0x01A1, 0x01A2, 0x01A3, 0x01A4, 0x01A5, 0x01A6, 0x01A7, - 0x01A8, 0x01A9, 0x01AA, 0x01AB, 0x01AC, 0x01AD, 0x01AE, 0x01AF, - 0x01B0, 0x01B1, 0x01B2, 0x01B3, 0x01B4, 0x01B5, 0x01B6, 0x01B7, - 0x0B90, 0x0B91, 0x0B92, 0x0B93, 0x0B94, 0x0B95, 0x0B96, 0x0B97, - 0x0B98, 0x0B99, 0x0B9A, 0x0B9B, 0x0B9C, 0x0B9D, 0x0B9E, 0x0B9F, - 0x0BA0, 0x0BA1, 0x0BA2, 0x0BA3, 0x0BA4, 0x0BA5, 0x0BA6, 0x0BA7, - 0x0BA8, 0x0BA9, 0x0BAA, 0x0BAB, 0x0BAC, 0x0BAD, 0x0BAE, 0x0BAF, - 0x0BB0, 0x0BB1, 0x0BB2, 0x0BB3, 0x0BB4, 0x0BB5, 0x0BB6, 0x0BB7, - 0x1590, 0x1591, 0x1592, 0x1593, 0x1594, 0x1595, 0x1596, 0x1597, - 0x1598, 0x1599, 0x159A, 0x159B, 0x159C, 0x159D, 0x159E, 0x159F, - 0x15A0, 0x15A1, 0x15A2, 0x15A3, 0x15A4, 0x15A5, 0x15A6, 0x15A7, - 0x15A8, 0x15A9, 0x15AA, 0x15AB, 0x15AC, 0x15AD, 0x15AE, 0x15AF, - 0x15B0, 0x15B1, 0x15B2, 0x15B3, 0x15B4, 0x15B5, 0x15B6, 0x15B7, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x02D0, 0x02D1, 0x02D2, 0x02D3, 0x02D4, 0x02D5, 0x02D6, 0x02D7, - 0x02D8, 0x02D9, 0x02DA, 0x02DB, 0x02DC, 0x02DD, 0x02DE, 0x02DF, - 0x02E0, 0x02E1, 0x02E2, 0x02E3, 0x02E4, 0x02E5, 0x02E6, 0x02E7, - 0x02E8, 0x02E9, 0x02EA, 0x02EB, 0x02EC, 0x02ED, 0x02EE, 0x02EF, - 0x02F0, 0x02F1, 0x02F2, 0x02F3, 0x02F4, 0x02F5, 0x02F6, 0x02F7, - 0x0CD0, 0x0CD1, 0x0CD2, 0x0CD3, 0x0CD4, 0x0CD5, 0x0CD6, 0x0CD7, - 0x0CD8, 0x0CD9, 0x0CDA, 0x0CDB, 0x0CDC, 0x0CDD, 0x0CDE, 0x0CDF, - 0x0CE0, 0x0CE1, 0x0CE2, 0x0CE3, 0x0CE4, 0x0CE5, 0x0CE6, 0x0CE7, - 0x0CE8, 0x0CE9, 0x0CEA, 0x0CEB, 0x0CEC, 0x0CED, 0x0CEE, 0x0CEF, - 0x0CF0, 0x0CF1, 0x0CF2, 0x0CF3, 0x0CF4, 0x0CF5, 0x0CF6, 0x0CF7, - 0x16D0, 0x16D1, 0x16D2, 0x16D3, 0x16D4, 0x16D5, 0x16D6, 0x16D7, - 0x16D8, 0x16D9, 0x16DA, 0x16DB, 0x16DC, 0x16DD, 0x16DE, 0x16DF, - 0x16E0, 0x16E1, 0x16E2, 0x16E3, 0x16E4, 0x16E5, 0x16E6, 0x16E7, - 0x16E8, 0x16E9, 0x16EA, 0x16EB, 0x16EC, 0x16ED, 0x16EE, 0x16EF, - 0x16F0, 0x16F1, 0x16F2, 0x16F3, 0x16F4, 0x16F5, 0x16F6, 0x16F7, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, - 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, - 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, - 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, - 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, - 0x0E10, 0x0E11, 0x0E12, 0x0E13, 0x0E14, 0x0E15, 0x0E16, 0x0E17, - 0x0E18, 0x0E19, 0x0E1A, 0x0E1B, 0x0E1C, 0x0E1D, 0x0E1E, 0x0E1F, - 0x0E20, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27, - 0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F, - 0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37, - 0x1810, 0x1811, 0x1812, 0x1813, 0x1814, 0x1815, 0x1816, 0x1817, - 0x1818, 0x1819, 0x181A, 0x181B, 0x181C, 0x181D, 0x181E, 0x181F, - 0x1820, 0x1821, 0x1822, 0x1823, 0x1824, 0x1825, 0x1826, 0x1827, - 0x1828, 0x1829, 0x182A, 0x182B, 0x182C, 0x182D, 0x182E, 0x182F, - 0x1830, 0x1831, 0x1832, 0x1833, 0x1834, 0x1835, 0x1836, 0x1837, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0550, 0x0551, 0x0552, 0x0553, 0x0554, 0x0555, 0x0556, 0x0557, - 0x0558, 0x0559, 0x055A, 0x055B, 0x055C, 0x055D, 0x055E, 0x055F, - 0x0560, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567, - 0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F, - 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577, - 0x0F50, 0x0F51, 0x0F52, 0x0F53, 0x0F54, 0x0F55, 0x0F56, 0x0F57, - 0x0F58, 0x0F59, 0x0F5A, 0x0F5B, 0x0F5C, 0x0F5D, 0x0F5E, 0x0F5F, - 0x0F60, 0x0F61, 0x0F62, 0x0F63, 0x0F64, 0x0F65, 0x0F66, 0x0F67, - 0x0F68, 0x0F69, 0x0F6A, 0x0F6B, 0x0F6C, 0x0F6D, 0x0F6E, 0x0F6F, - 0x0F70, 0x0F71, 0x0F72, 0x0F73, 0x0F74, 0x0F75, 0x0F76, 0x0F77, - 0x1950, 0x1951, 0x1952, 0x1953, 0x1954, 0x1955, 0x1956, 0x1957, - 0x1958, 0x1959, 0x195A, 0x195B, 0x195C, 0x195D, 0x195E, 0x195F, - 0x1960, 0x1961, 0x1962, 0x1963, 0x1964, 0x1965, 0x1966, 0x1967, - 0x1968, 0x1969, 0x196A, 0x196B, 0x196C, 0x196D, 0x196E, 0x196F, - 0x1970, 0x1971, 0x1972, 0x1973, 0x1974, 0x1975, 0x1976, 0x1977, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0690, 0x0691, 0x0692, 0x0693, 0x0694, 0x0695, 0x0696, 0x0697, - 0x0698, 0x0699, 0x069A, 0x069B, 0x069C, 0x069D, 0x069E, 0x069F, - 0x06A0, 0x06A1, 0x06A2, 0x06A3, 0x06A4, 0x06A5, 0x06A6, 0x06A7, - 0x06A8, 0x06A9, 0x06AA, 0x06AB, 0x06AC, 0x06AD, 0x06AE, 0x06AF, - 0x06B0, 0x06B1, 0x06B2, 0x06B3, 0x06B4, 0x06B5, 0x06B6, 0x06B7, - 0x1090, 0x1091, 0x1092, 0x1093, 0x1094, 0x1095, 0x1096, 0x1097, - 0x1098, 0x1099, 0x109A, 0x109B, 0x109C, 0x109D, 0x109E, 0x109F, - 0x10A0, 0x10A1, 0x10A2, 0x10A3, 0x10A4, 0x10A5, 0x10A6, 0x10A7, - 0x10A8, 0x10A9, 0x10AA, 0x10AB, 0x10AC, 0x10AD, 0x10AE, 0x10AF, - 0x10B0, 0x10B1, 0x10B2, 0x10B3, 0x10B4, 0x10B5, 0x10B6, 0x10B7, - 0x1A90, 0x1A91, 0x1A92, 0x1A93, 0x1A94, 0x1A95, 0x1A96, 0x1A97, - 0x1A98, 0x1A99, 0x1A9A, 0x1A9B, 0x1A9C, 0x1A9D, 0x1A9E, 0x1A9F, - 0x1AA0, 0x1AA1, 0x1AA2, 0x1AA3, 0x1AA4, 0x1AA5, 0x1AA6, 0x1AA7, - 0x1AA8, 0x1AA9, 0x1AAA, 0x1AAB, 0x1AAC, 0x1AAD, 0x1AAE, 0x1AAF, - 0x1AB0, 0x1AB1, 0x1AB2, 0x1AB3, 0x1AB4, 0x1AB5, 0x1AB6, 0x1AB7, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x07D0, 0x07D1, 0x07D2, 0x07D3, 0x07D4, 0x07D5, 0x07D6, 0x07D7, - 0x07D8, 0x07D9, 0x07DA, 0x07DB, 0x07DC, 0x07DD, 0x07DE, 0x07DF, - 0x07E0, 0x07E1, 0x07E2, 0x07E3, 0x07E4, 0x07E5, 0x07E6, 0x07E7, - 0x07E8, 0x07E9, 0x07EA, 0x07EB, 0x07EC, 0x07ED, 0x07EE, 0x07EF, - 0x07F0, 0x07F1, 0x07F2, 0x07F3, 0x07F4, 0x07F5, 0x07F6, 0x07F7, - 0x11D0, 0x11D1, 0x11D2, 0x11D3, 0x11D4, 0x11D5, 0x11D6, 0x11D7, - 0x11D8, 0x11D9, 0x11DA, 0x11DB, 0x11DC, 0x11DD, 0x11DE, 0x11DF, - 0x11E0, 0x11E1, 0x11E2, 0x11E3, 0x11E4, 0x11E5, 0x11E6, 0x11E7, - 0x11E8, 0x11E9, 0x11EA, 0x11EB, 0x11EC, 0x11ED, 0x11EE, 0x11EF, - 0x11F0, 0x11F1, 0x11F2, 0x11F3, 0x11F4, 0x11F5, 0x11F6, 0x11F7, - 0x1BD0, 0x1BD1, 0x1BD2, 0x1BD3, 0x1BD4, 0x1BD5, 0x1BD6, 0x1BD7, - 0x1BD8, 0x1BD9, 0x1BDA, 0x1BDB, 0x1BDC, 0x1BDD, 0x1BDE, 0x1BDF, - 0x1BE0, 0x1BE1, 0x1BE2, 0x1BE3, 0x1BE4, 0x1BE5, 0x1BE6, 0x1BE7, - 0x1BE8, 0x1BE9, 0x1BEA, 0x1BEB, 0x1BEC, 0x1BED, 0x1BEE, 0x1BEF, - 0x1BF0, 0x1BF1, 0x1BF2, 0x1BF3, 0x1BF4, 0x1BF5, 0x1BF6, 0x1BF7, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0910, 0x0911, 0x0912, 0x0913, 0x0914, 0x0915, 0x0916, 0x0917, - 0x0918, 0x0919, 0x091A, 0x091B, 0x091C, 0x091D, 0x091E, 0x091F, - 0x0920, 0x0921, 0x0922, 0x0923, 0x0924, 0x0925, 0x0926, 0x0927, - 0x0928, 0x0929, 0x092A, 0x092B, 0x092C, 0x092D, 0x092E, 0x092F, - 0x0930, 0x0931, 0x0932, 0x0933, 0x0934, 0x0935, 0x0936, 0x0937, - 0x1310, 0x1311, 0x1312, 0x1313, 0x1314, 0x1315, 0x1316, 0x1317, - 0x1318, 0x1319, 0x131A, 0x131B, 0x131C, 0x131D, 0x131E, 0x131F, - 0x1320, 0x1321, 0x1322, 0x1323, 0x1324, 0x1325, 0x1326, 0x1327, - 0x1328, 0x1329, 0x132A, 0x132B, 0x132C, 0x132D, 0x132E, 0x132F, - 0x1330, 0x1331, 0x1332, 0x1333, 0x1334, 0x1335, 0x1336, 0x1337, - 0x1D10, 0x1D11, 0x1D12, 0x1D13, 0x1D14, 0x1D15, 0x1D16, 0x1D17, - 0x1D18, 0x1D19, 0x1D1A, 0x1D1B, 0x1D1C, 0x1D1D, 0x1D1E, 0x1D1F, - 0x1D20, 0x1D21, 0x1D22, 0x1D23, 0x1D24, 0x1D25, 0x1D26, 0x1D27, - 0x1D28, 0x1D29, 0x1D2A, 0x1D2B, 0x1D2C, 0x1D2D, 0x1D2E, 0x1D2F, - 0x1D30, 0x1D31, 0x1D32, 0x1D33, 0x1D34, 0x1D35, 0x1D36, 0x1D37, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, - 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, - 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, - 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, - 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, - 0x0A78, 0x0A79, 0x0A7A, 0x0A7B, 0x0A7C, 0x0A7D, 0x0A7E, 0x0A7F, - 0x0A80, 0x0A81, 0x0A82, 0x0A83, 0x0A84, 0x0A85, 0x0A86, 0x0A87, - 0x0A88, 0x0A89, 0x0A8A, 0x0A8B, 0x0A8C, 0x0A8D, 0x0A8E, 0x0A8F, - 0x0A90, 0x0A91, 0x0A92, 0x0A93, 0x0A94, 0x0A95, 0x0A96, 0x0A97, - 0x0A98, 0x0A99, 0x0A9A, 0x0A9B, 0x0A9C, 0x0A9D, 0x0A9E, 0x0A9F, - 0x1478, 0x1479, 0x147A, 0x147B, 0x147C, 0x147D, 0x147E, 0x147F, - 0x1480, 0x1481, 0x1482, 0x1483, 0x1484, 0x1485, 0x1486, 0x1487, - 0x1488, 0x1489, 0x148A, 0x148B, 0x148C, 0x148D, 0x148E, 0x148F, - 0x1490, 0x1491, 0x1492, 0x1493, 0x1494, 0x1495, 0x1496, 0x1497, - 0x1498, 0x1499, 0x149A, 0x149B, 0x149C, 0x149D, 0x149E, 0x149F, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x01B8, 0x01B9, 0x01BA, 0x01BB, 0x01BC, 0x01BD, 0x01BE, 0x01BF, - 0x01C0, 0x01C1, 0x01C2, 0x01C3, 0x01C4, 0x01C5, 0x01C6, 0x01C7, - 0x01C8, 0x01C9, 0x01CA, 0x01CB, 0x01CC, 0x01CD, 0x01CE, 0x01CF, - 0x01D0, 0x01D1, 0x01D2, 0x01D3, 0x01D4, 0x01D5, 0x01D6, 0x01D7, - 0x01D8, 0x01D9, 0x01DA, 0x01DB, 0x01DC, 0x01DD, 0x01DE, 0x01DF, - 0x0BB8, 0x0BB9, 0x0BBA, 0x0BBB, 0x0BBC, 0x0BBD, 0x0BBE, 0x0BBF, - 0x0BC0, 0x0BC1, 0x0BC2, 0x0BC3, 0x0BC4, 0x0BC5, 0x0BC6, 0x0BC7, - 0x0BC8, 0x0BC9, 0x0BCA, 0x0BCB, 0x0BCC, 0x0BCD, 0x0BCE, 0x0BCF, - 0x0BD0, 0x0BD1, 0x0BD2, 0x0BD3, 0x0BD4, 0x0BD5, 0x0BD6, 0x0BD7, - 0x0BD8, 0x0BD9, 0x0BDA, 0x0BDB, 0x0BDC, 0x0BDD, 0x0BDE, 0x0BDF, - 0x15B8, 0x15B9, 0x15BA, 0x15BB, 0x15BC, 0x15BD, 0x15BE, 0x15BF, - 0x15C0, 0x15C1, 0x15C2, 0x15C3, 0x15C4, 0x15C5, 0x15C6, 0x15C7, - 0x15C8, 0x15C9, 0x15CA, 0x15CB, 0x15CC, 0x15CD, 0x15CE, 0x15CF, - 0x15D0, 0x15D1, 0x15D2, 0x15D3, 0x15D4, 0x15D5, 0x15D6, 0x15D7, - 0x15D8, 0x15D9, 0x15DA, 0x15DB, 0x15DC, 0x15DD, 0x15DE, 0x15DF, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x02F8, 0x02F9, 0x02FA, 0x02FB, 0x02FC, 0x02FD, 0x02FE, 0x02FF, - 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, - 0x0308, 0x0309, 0x030A, 0x030B, 0x030C, 0x030D, 0x030E, 0x030F, - 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317, - 0x0318, 0x0319, 0x031A, 0x031B, 0x031C, 0x031D, 0x031E, 0x031F, - 0x0CF8, 0x0CF9, 0x0CFA, 0x0CFB, 0x0CFC, 0x0CFD, 0x0CFE, 0x0CFF, - 0x0D00, 0x0D01, 0x0D02, 0x0D03, 0x0D04, 0x0D05, 0x0D06, 0x0D07, - 0x0D08, 0x0D09, 0x0D0A, 0x0D0B, 0x0D0C, 0x0D0D, 0x0D0E, 0x0D0F, - 0x0D10, 0x0D11, 0x0D12, 0x0D13, 0x0D14, 0x0D15, 0x0D16, 0x0D17, - 0x0D18, 0x0D19, 0x0D1A, 0x0D1B, 0x0D1C, 0x0D1D, 0x0D1E, 0x0D1F, - 0x16F8, 0x16F9, 0x16FA, 0x16FB, 0x16FC, 0x16FD, 0x16FE, 0x16FF, - 0x1700, 0x1701, 0x1702, 0x1703, 0x1704, 0x1705, 0x1706, 0x1707, - 0x1708, 0x1709, 0x170A, 0x170B, 0x170C, 0x170D, 0x170E, 0x170F, - 0x1710, 0x1711, 0x1712, 0x1713, 0x1714, 0x1715, 0x1716, 0x1717, - 0x1718, 0x1719, 0x171A, 0x171B, 0x171C, 0x171D, 0x171E, 0x171F, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, - 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, - 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, - 0x0450, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, - 0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x045D, 0x045E, 0x045F, - 0x0E38, 0x0E39, 0x0E3A, 0x0E3B, 0x0E3C, 0x0E3D, 0x0E3E, 0x0E3F, - 0x0E40, 0x0E41, 0x0E42, 0x0E43, 0x0E44, 0x0E45, 0x0E46, 0x0E47, - 0x0E48, 0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, 0x0E4E, 0x0E4F, - 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, - 0x0E58, 0x0E59, 0x0E5A, 0x0E5B, 0x0E5C, 0x0E5D, 0x0E5E, 0x0E5F, - 0x1838, 0x1839, 0x183A, 0x183B, 0x183C, 0x183D, 0x183E, 0x183F, - 0x1840, 0x1841, 0x1842, 0x1843, 0x1844, 0x1845, 0x1846, 0x1847, - 0x1848, 0x1849, 0x184A, 0x184B, 0x184C, 0x184D, 0x184E, 0x184F, - 0x1850, 0x1851, 0x1852, 0x1853, 0x1854, 0x1855, 0x1856, 0x1857, - 0x1858, 0x1859, 0x185A, 0x185B, 0x185C, 0x185D, 0x185E, 0x185F, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0578, 0x0579, 0x057A, 0x057B, 0x057C, 0x057D, 0x057E, 0x057F, - 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0587, - 0x0588, 0x0589, 0x058A, 0x058B, 0x058C, 0x058D, 0x058E, 0x058F, - 0x0590, 0x0591, 0x0592, 0x0593, 0x0594, 0x0595, 0x0596, 0x0597, - 0x0598, 0x0599, 0x059A, 0x059B, 0x059C, 0x059D, 0x059E, 0x059F, - 0x0F78, 0x0F79, 0x0F7A, 0x0F7B, 0x0F7C, 0x0F7D, 0x0F7E, 0x0F7F, - 0x0F80, 0x0F81, 0x0F82, 0x0F83, 0x0F84, 0x0F85, 0x0F86, 0x0F87, - 0x0F88, 0x0F89, 0x0F8A, 0x0F8B, 0x0F8C, 0x0F8D, 0x0F8E, 0x0F8F, - 0x0F90, 0x0F91, 0x0F92, 0x0F93, 0x0F94, 0x0F95, 0x0F96, 0x0F97, - 0x0F98, 0x0F99, 0x0F9A, 0x0F9B, 0x0F9C, 0x0F9D, 0x0F9E, 0x0F9F, - 0x1978, 0x1979, 0x197A, 0x197B, 0x197C, 0x197D, 0x197E, 0x197F, - 0x1980, 0x1981, 0x1982, 0x1983, 0x1984, 0x1985, 0x1986, 0x1987, - 0x1988, 0x1989, 0x198A, 0x198B, 0x198C, 0x198D, 0x198E, 0x198F, - 0x1990, 0x1991, 0x1992, 0x1993, 0x1994, 0x1995, 0x1996, 0x1997, - 0x1998, 0x1999, 0x199A, 0x199B, 0x199C, 0x199D, 0x199E, 0x199F, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x06B8, 0x06B9, 0x06BA, 0x06BB, 0x06BC, 0x06BD, 0x06BE, 0x06BF, - 0x06C0, 0x06C1, 0x06C2, 0x06C3, 0x06C4, 0x06C5, 0x06C6, 0x06C7, - 0x06C8, 0x06C9, 0x06CA, 0x06CB, 0x06CC, 0x06CD, 0x06CE, 0x06CF, - 0x06D0, 0x06D1, 0x06D2, 0x06D3, 0x06D4, 0x06D5, 0x06D6, 0x06D7, - 0x06D8, 0x06D9, 0x06DA, 0x06DB, 0x06DC, 0x06DD, 0x06DE, 0x06DF, - 0x10B8, 0x10B9, 0x10BA, 0x10BB, 0x10BC, 0x10BD, 0x10BE, 0x10BF, - 0x10C0, 0x10C1, 0x10C2, 0x10C3, 0x10C4, 0x10C5, 0x10C6, 0x10C7, - 0x10C8, 0x10C9, 0x10CA, 0x10CB, 0x10CC, 0x10CD, 0x10CE, 0x10CF, - 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10D7, - 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10DD, 0x10DE, 0x10DF, - 0x1AB8, 0x1AB9, 0x1ABA, 0x1ABB, 0x1ABC, 0x1ABD, 0x1ABE, 0x1ABF, - 0x1AC0, 0x1AC1, 0x1AC2, 0x1AC3, 0x1AC4, 0x1AC5, 0x1AC6, 0x1AC7, - 0x1AC8, 0x1AC9, 0x1ACA, 0x1ACB, 0x1ACC, 0x1ACD, 0x1ACE, 0x1ACF, - 0x1AD0, 0x1AD1, 0x1AD2, 0x1AD3, 0x1AD4, 0x1AD5, 0x1AD6, 0x1AD7, - 0x1AD8, 0x1AD9, 0x1ADA, 0x1ADB, 0x1ADC, 0x1ADD, 0x1ADE, 0x1ADF, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x07F8, 0x07F9, 0x07FA, 0x07FB, 0x07FC, 0x07FD, 0x07FE, 0x07FF, - 0x0800, 0x0801, 0x0802, 0x0803, 0x0804, 0x0805, 0x0806, 0x0807, - 0x0808, 0x0809, 0x080A, 0x080B, 0x080C, 0x080D, 0x080E, 0x080F, - 0x0810, 0x0811, 0x0812, 0x0813, 0x0814, 0x0815, 0x0816, 0x0817, - 0x0818, 0x0819, 0x081A, 0x081B, 0x081C, 0x081D, 0x081E, 0x081F, - 0x11F8, 0x11F9, 0x11FA, 0x11FB, 0x11FC, 0x11FD, 0x11FE, 0x11FF, - 0x1200, 0x1201, 0x1202, 0x1203, 0x1204, 0x1205, 0x1206, 0x1207, - 0x1208, 0x1209, 0x120A, 0x120B, 0x120C, 0x120D, 0x120E, 0x120F, - 0x1210, 0x1211, 0x1212, 0x1213, 0x1214, 0x1215, 0x1216, 0x1217, - 0x1218, 0x1219, 0x121A, 0x121B, 0x121C, 0x121D, 0x121E, 0x121F, - 0x1BF8, 0x1BF9, 0x1BFA, 0x1BFB, 0x1BFC, 0x1BFD, 0x1BFE, 0x1BFF, - 0x1C00, 0x1C01, 0x1C02, 0x1C03, 0x1C04, 0x1C05, 0x1C06, 0x1C07, - 0x1C08, 0x1C09, 0x1C0A, 0x1C0B, 0x1C0C, 0x1C0D, 0x1C0E, 0x1C0F, - 0x1C10, 0x1C11, 0x1C12, 0x1C13, 0x1C14, 0x1C15, 0x1C16, 0x1C17, - 0x1C18, 0x1C19, 0x1C1A, 0x1C1B, 0x1C1C, 0x1C1D, 0x1C1E, 0x1C1F, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0938, 0x0939, 0x093A, 0x093B, 0x093C, 0x093D, 0x093E, 0x093F, - 0x0940, 0x0941, 0x0942, 0x0943, 0x0944, 0x0945, 0x0946, 0x0947, - 0x0948, 0x0949, 0x094A, 0x094B, 0x094C, 0x094D, 0x094E, 0x094F, - 0x0950, 0x0951, 0x0952, 0x0953, 0x0954, 0x0955, 0x0956, 0x0957, - 0x0958, 0x0959, 0x095A, 0x095B, 0x095C, 0x095D, 0x095E, 0x095F, - 0x1338, 0x1339, 0x133A, 0x133B, 0x133C, 0x133D, 0x133E, 0x133F, - 0x1340, 0x1341, 0x1342, 0x1343, 0x1344, 0x1345, 0x1346, 0x1347, - 0x1348, 0x1349, 0x134A, 0x134B, 0x134C, 0x134D, 0x134E, 0x134F, - 0x1350, 0x1351, 0x1352, 0x1353, 0x1354, 0x1355, 0x1356, 0x1357, - 0x1358, 0x1359, 0x135A, 0x135B, 0x135C, 0x135D, 0x135E, 0x135F, - 0x1D38, 0x1D39, 0x1D3A, 0x1D3B, 0x1D3C, 0x1D3D, 0x1D3E, 0x1D3F, - 0x1D40, 0x1D41, 0x1D42, 0x1D43, 0x1D44, 0x1D45, 0x1D46, 0x1D47, - 0x1D48, 0x1D49, 0x1D4A, 0x1D4B, 0x1D4C, 0x1D4D, 0x1D4E, 0x1D4F, - 0x1D50, 0x1D51, 0x1D52, 0x1D53, 0x1D54, 0x1D55, 0x1D56, 0x1D57, - 0x1D58, 0x1D59, 0x1D5A, 0x1D5B, 0x1D5C, 0x1D5D, 0x1D5E, 0x1D5F, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, - 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, - 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, - 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, - 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, - 0x0AA0, 0x0AA1, 0x0AA2, 0x0AA3, 0x0AA4, 0x0AA5, 0x0AA6, 0x0AA7, - 0x0AA8, 0x0AA9, 0x0AAA, 0x0AAB, 0x0AAC, 0x0AAD, 0x0AAE, 0x0AAF, - 0x0AB0, 0x0AB1, 0x0AB2, 0x0AB3, 0x0AB4, 0x0AB5, 0x0AB6, 0x0AB7, - 0x0AB8, 0x0AB9, 0x0ABA, 0x0ABB, 0x0ABC, 0x0ABD, 0x0ABE, 0x0ABF, - 0x0AC0, 0x0AC1, 0x0AC2, 0x0AC3, 0x0AC4, 0x0AC5, 0x0AC6, 0x0AC7, - 0x14A0, 0x14A1, 0x14A2, 0x14A3, 0x14A4, 0x14A5, 0x14A6, 0x14A7, - 0x14A8, 0x14A9, 0x14AA, 0x14AB, 0x14AC, 0x14AD, 0x14AE, 0x14AF, - 0x14B0, 0x14B1, 0x14B2, 0x14B3, 0x14B4, 0x14B5, 0x14B6, 0x14B7, - 0x14B8, 0x14B9, 0x14BA, 0x14BB, 0x14BC, 0x14BD, 0x14BE, 0x14BF, - 0x14C0, 0x14C1, 0x14C2, 0x14C3, 0x14C4, 0x14C5, 0x14C6, 0x14C7, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x01E0, 0x01E1, 0x01E2, 0x01E3, 0x01E4, 0x01E5, 0x01E6, 0x01E7, - 0x01E8, 0x01E9, 0x01EA, 0x01EB, 0x01EC, 0x01ED, 0x01EE, 0x01EF, - 0x01F0, 0x01F1, 0x01F2, 0x01F3, 0x01F4, 0x01F5, 0x01F6, 0x01F7, - 0x01F8, 0x01F9, 0x01FA, 0x01FB, 0x01FC, 0x01FD, 0x01FE, 0x01FF, - 0x0200, 0x0201, 0x0202, 0x0203, 0x0204, 0x0205, 0x0206, 0x0207, - 0x0BE0, 0x0BE1, 0x0BE2, 0x0BE3, 0x0BE4, 0x0BE5, 0x0BE6, 0x0BE7, - 0x0BE8, 0x0BE9, 0x0BEA, 0x0BEB, 0x0BEC, 0x0BED, 0x0BEE, 0x0BEF, - 0x0BF0, 0x0BF1, 0x0BF2, 0x0BF3, 0x0BF4, 0x0BF5, 0x0BF6, 0x0BF7, - 0x0BF8, 0x0BF9, 0x0BFA, 0x0BFB, 0x0BFC, 0x0BFD, 0x0BFE, 0x0BFF, - 0x0C00, 0x0C01, 0x0C02, 0x0C03, 0x0C04, 0x0C05, 0x0C06, 0x0C07, - 0x15E0, 0x15E1, 0x15E2, 0x15E3, 0x15E4, 0x15E5, 0x15E6, 0x15E7, - 0x15E8, 0x15E9, 0x15EA, 0x15EB, 0x15EC, 0x15ED, 0x15EE, 0x15EF, - 0x15F0, 0x15F1, 0x15F2, 0x15F3, 0x15F4, 0x15F5, 0x15F6, 0x15F7, - 0x15F8, 0x15F9, 0x15FA, 0x15FB, 0x15FC, 0x15FD, 0x15FE, 0x15FF, - 0x1600, 0x1601, 0x1602, 0x1603, 0x1604, 0x1605, 0x1606, 0x1607, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327, - 0x0328, 0x0329, 0x032A, 0x032B, 0x032C, 0x032D, 0x032E, 0x032F, - 0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337, - 0x0338, 0x0339, 0x033A, 0x033B, 0x033C, 0x033D, 0x033E, 0x033F, - 0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x0345, 0x0346, 0x0347, - 0x0D20, 0x0D21, 0x0D22, 0x0D23, 0x0D24, 0x0D25, 0x0D26, 0x0D27, - 0x0D28, 0x0D29, 0x0D2A, 0x0D2B, 0x0D2C, 0x0D2D, 0x0D2E, 0x0D2F, - 0x0D30, 0x0D31, 0x0D32, 0x0D33, 0x0D34, 0x0D35, 0x0D36, 0x0D37, - 0x0D38, 0x0D39, 0x0D3A, 0x0D3B, 0x0D3C, 0x0D3D, 0x0D3E, 0x0D3F, - 0x0D40, 0x0D41, 0x0D42, 0x0D43, 0x0D44, 0x0D45, 0x0D46, 0x0D47, - 0x1720, 0x1721, 0x1722, 0x1723, 0x1724, 0x1725, 0x1726, 0x1727, - 0x1728, 0x1729, 0x172A, 0x172B, 0x172C, 0x172D, 0x172E, 0x172F, - 0x1730, 0x1731, 0x1732, 0x1733, 0x1734, 0x1735, 0x1736, 0x1737, - 0x1738, 0x1739, 0x173A, 0x173B, 0x173C, 0x173D, 0x173E, 0x173F, - 0x1740, 0x1741, 0x1742, 0x1743, 0x1744, 0x1745, 0x1746, 0x1747, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0460, 0x0461, 0x0462, 0x0463, 0x0464, 0x0465, 0x0466, 0x0467, - 0x0468, 0x0469, 0x046A, 0x046B, 0x046C, 0x046D, 0x046E, 0x046F, - 0x0470, 0x0471, 0x0472, 0x0473, 0x0474, 0x0475, 0x0476, 0x0477, - 0x0478, 0x0479, 0x047A, 0x047B, 0x047C, 0x047D, 0x047E, 0x047F, - 0x0480, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, - 0x0E60, 0x0E61, 0x0E62, 0x0E63, 0x0E64, 0x0E65, 0x0E66, 0x0E67, - 0x0E68, 0x0E69, 0x0E6A, 0x0E6B, 0x0E6C, 0x0E6D, 0x0E6E, 0x0E6F, - 0x0E70, 0x0E71, 0x0E72, 0x0E73, 0x0E74, 0x0E75, 0x0E76, 0x0E77, - 0x0E78, 0x0E79, 0x0E7A, 0x0E7B, 0x0E7C, 0x0E7D, 0x0E7E, 0x0E7F, - 0x0E80, 0x0E81, 0x0E82, 0x0E83, 0x0E84, 0x0E85, 0x0E86, 0x0E87, - 0x1860, 0x1861, 0x1862, 0x1863, 0x1864, 0x1865, 0x1866, 0x1867, - 0x1868, 0x1869, 0x186A, 0x186B, 0x186C, 0x186D, 0x186E, 0x186F, - 0x1870, 0x1871, 0x1872, 0x1873, 0x1874, 0x1875, 0x1876, 0x1877, - 0x1878, 0x1879, 0x187A, 0x187B, 0x187C, 0x187D, 0x187E, 0x187F, - 0x1880, 0x1881, 0x1882, 0x1883, 0x1884, 0x1885, 0x1886, 0x1887, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x05A0, 0x05A1, 0x05A2, 0x05A3, 0x05A4, 0x05A5, 0x05A6, 0x05A7, - 0x05A8, 0x05A9, 0x05AA, 0x05AB, 0x05AC, 0x05AD, 0x05AE, 0x05AF, - 0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7, - 0x05B8, 0x05B9, 0x05BA, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF, - 0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05C4, 0x05C5, 0x05C6, 0x05C7, - 0x0FA0, 0x0FA1, 0x0FA2, 0x0FA3, 0x0FA4, 0x0FA5, 0x0FA6, 0x0FA7, - 0x0FA8, 0x0FA9, 0x0FAA, 0x0FAB, 0x0FAC, 0x0FAD, 0x0FAE, 0x0FAF, - 0x0FB0, 0x0FB1, 0x0FB2, 0x0FB3, 0x0FB4, 0x0FB5, 0x0FB6, 0x0FB7, - 0x0FB8, 0x0FB9, 0x0FBA, 0x0FBB, 0x0FBC, 0x0FBD, 0x0FBE, 0x0FBF, - 0x0FC0, 0x0FC1, 0x0FC2, 0x0FC3, 0x0FC4, 0x0FC5, 0x0FC6, 0x0FC7, - 0x19A0, 0x19A1, 0x19A2, 0x19A3, 0x19A4, 0x19A5, 0x19A6, 0x19A7, - 0x19A8, 0x19A9, 0x19AA, 0x19AB, 0x19AC, 0x19AD, 0x19AE, 0x19AF, - 0x19B0, 0x19B1, 0x19B2, 0x19B3, 0x19B4, 0x19B5, 0x19B6, 0x19B7, - 0x19B8, 0x19B9, 0x19BA, 0x19BB, 0x19BC, 0x19BD, 0x19BE, 0x19BF, - 0x19C0, 0x19C1, 0x19C2, 0x19C3, 0x19C4, 0x19C5, 0x19C6, 0x19C7, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x06E0, 0x06E1, 0x06E2, 0x06E3, 0x06E4, 0x06E5, 0x06E6, 0x06E7, - 0x06E8, 0x06E9, 0x06EA, 0x06EB, 0x06EC, 0x06ED, 0x06EE, 0x06EF, - 0x06F0, 0x06F1, 0x06F2, 0x06F3, 0x06F4, 0x06F5, 0x06F6, 0x06F7, - 0x06F8, 0x06F9, 0x06FA, 0x06FB, 0x06FC, 0x06FD, 0x06FE, 0x06FF, - 0x0700, 0x0701, 0x0702, 0x0703, 0x0704, 0x0705, 0x0706, 0x0707, - 0x10E0, 0x10E1, 0x10E2, 0x10E3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, - 0x10E8, 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10EF, - 0x10F0, 0x10F1, 0x10F2, 0x10F3, 0x10F4, 0x10F5, 0x10F6, 0x10F7, - 0x10F8, 0x10F9, 0x10FA, 0x10FB, 0x10FC, 0x10FD, 0x10FE, 0x10FF, - 0x1100, 0x1101, 0x1102, 0x1103, 0x1104, 0x1105, 0x1106, 0x1107, - 0x1AE0, 0x1AE1, 0x1AE2, 0x1AE3, 0x1AE4, 0x1AE5, 0x1AE6, 0x1AE7, - 0x1AE8, 0x1AE9, 0x1AEA, 0x1AEB, 0x1AEC, 0x1AED, 0x1AEE, 0x1AEF, - 0x1AF0, 0x1AF1, 0x1AF2, 0x1AF3, 0x1AF4, 0x1AF5, 0x1AF6, 0x1AF7, - 0x1AF8, 0x1AF9, 0x1AFA, 0x1AFB, 0x1AFC, 0x1AFD, 0x1AFE, 0x1AFF, - 0x1B00, 0x1B01, 0x1B02, 0x1B03, 0x1B04, 0x1B05, 0x1B06, 0x1B07, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0820, 0x0821, 0x0822, 0x0823, 0x0824, 0x0825, 0x0826, 0x0827, - 0x0828, 0x0829, 0x082A, 0x082B, 0x082C, 0x082D, 0x082E, 0x082F, - 0x0830, 0x0831, 0x0832, 0x0833, 0x0834, 0x0835, 0x0836, 0x0837, - 0x0838, 0x0839, 0x083A, 0x083B, 0x083C, 0x083D, 0x083E, 0x083F, - 0x0840, 0x0841, 0x0842, 0x0843, 0x0844, 0x0845, 0x0846, 0x0847, - 0x1220, 0x1221, 0x1222, 0x1223, 0x1224, 0x1225, 0x1226, 0x1227, - 0x1228, 0x1229, 0x122A, 0x122B, 0x122C, 0x122D, 0x122E, 0x122F, - 0x1230, 0x1231, 0x1232, 0x1233, 0x1234, 0x1235, 0x1236, 0x1237, - 0x1238, 0x1239, 0x123A, 0x123B, 0x123C, 0x123D, 0x123E, 0x123F, - 0x1240, 0x1241, 0x1242, 0x1243, 0x1244, 0x1245, 0x1246, 0x1247, - 0x1C20, 0x1C21, 0x1C22, 0x1C23, 0x1C24, 0x1C25, 0x1C26, 0x1C27, - 0x1C28, 0x1C29, 0x1C2A, 0x1C2B, 0x1C2C, 0x1C2D, 0x1C2E, 0x1C2F, - 0x1C30, 0x1C31, 0x1C32, 0x1C33, 0x1C34, 0x1C35, 0x1C36, 0x1C37, - 0x1C38, 0x1C39, 0x1C3A, 0x1C3B, 0x1C3C, 0x1C3D, 0x1C3E, 0x1C3F, - 0x1C40, 0x1C41, 0x1C42, 0x1C43, 0x1C44, 0x1C45, 0x1C46, 0x1C47, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0960, 0x0961, 0x0962, 0x0963, 0x0964, 0x0965, 0x0966, 0x0967, - 0x0968, 0x0969, 0x096A, 0x096B, 0x096C, 0x096D, 0x096E, 0x096F, - 0x0970, 0x0971, 0x0972, 0x0973, 0x0974, 0x0975, 0x0976, 0x0977, - 0x0978, 0x0979, 0x097A, 0x097B, 0x097C, 0x097D, 0x097E, 0x097F, - 0x0980, 0x0981, 0x0982, 0x0983, 0x0984, 0x0985, 0x0986, 0x0987, - 0x1360, 0x1361, 0x1362, 0x1363, 0x1364, 0x1365, 0x1366, 0x1367, - 0x1368, 0x1369, 0x136A, 0x136B, 0x136C, 0x136D, 0x136E, 0x136F, - 0x1370, 0x1371, 0x1372, 0x1373, 0x1374, 0x1375, 0x1376, 0x1377, - 0x1378, 0x1379, 0x137A, 0x137B, 0x137C, 0x137D, 0x137E, 0x137F, - 0x1380, 0x1381, 0x1382, 0x1383, 0x1384, 0x1385, 0x1386, 0x1387, - 0x1D60, 0x1D61, 0x1D62, 0x1D63, 0x1D64, 0x1D65, 0x1D66, 0x1D67, - 0x1D68, 0x1D69, 0x1D6A, 0x1D6B, 0x1D6C, 0x1D6D, 0x1D6E, 0x1D6F, - 0x1D70, 0x1D71, 0x1D72, 0x1D73, 0x1D74, 0x1D75, 0x1D76, 0x1D77, - 0x1D78, 0x1D79, 0x1D7A, 0x1D7B, 0x1D7C, 0x1D7D, 0x1D7E, 0x1D7F, - 0x1D80, 0x1D81, 0x1D82, 0x1D83, 0x1D84, 0x1D85, 0x1D86, 0x1D87, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, - 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, - 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, - 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, - 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, - 0x0AC8, 0x0AC9, 0x0ACA, 0x0ACB, 0x0ACC, 0x0ACD, 0x0ACE, 0x0ACF, - 0x0AD0, 0x0AD1, 0x0AD2, 0x0AD3, 0x0AD4, 0x0AD5, 0x0AD6, 0x0AD7, - 0x0AD8, 0x0AD9, 0x0ADA, 0x0ADB, 0x0ADC, 0x0ADD, 0x0ADE, 0x0ADF, - 0x0AE0, 0x0AE1, 0x0AE2, 0x0AE3, 0x0AE4, 0x0AE5, 0x0AE6, 0x0AE7, - 0x0AE8, 0x0AE9, 0x0AEA, 0x0AEB, 0x0AEC, 0x0AED, 0x0AEE, 0x0AEF, - 0x14C8, 0x14C9, 0x14CA, 0x14CB, 0x14CC, 0x14CD, 0x14CE, 0x14CF, - 0x14D0, 0x14D1, 0x14D2, 0x14D3, 0x14D4, 0x14D5, 0x14D6, 0x14D7, - 0x14D8, 0x14D9, 0x14DA, 0x14DB, 0x14DC, 0x14DD, 0x14DE, 0x14DF, - 0x14E0, 0x14E1, 0x14E2, 0x14E3, 0x14E4, 0x14E5, 0x14E6, 0x14E7, - 0x14E8, 0x14E9, 0x14EA, 0x14EB, 0x14EC, 0x14ED, 0x14EE, 0x14EF, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0208, 0x0209, 0x020A, 0x020B, 0x020C, 0x020D, 0x020E, 0x020F, - 0x0210, 0x0211, 0x0212, 0x0213, 0x0214, 0x0215, 0x0216, 0x0217, - 0x0218, 0x0219, 0x021A, 0x021B, 0x021C, 0x021D, 0x021E, 0x021F, - 0x0220, 0x0221, 0x0222, 0x0223, 0x0224, 0x0225, 0x0226, 0x0227, - 0x0228, 0x0229, 0x022A, 0x022B, 0x022C, 0x022D, 0x022E, 0x022F, - 0x0C08, 0x0C09, 0x0C0A, 0x0C0B, 0x0C0C, 0x0C0D, 0x0C0E, 0x0C0F, - 0x0C10, 0x0C11, 0x0C12, 0x0C13, 0x0C14, 0x0C15, 0x0C16, 0x0C17, - 0x0C18, 0x0C19, 0x0C1A, 0x0C1B, 0x0C1C, 0x0C1D, 0x0C1E, 0x0C1F, - 0x0C20, 0x0C21, 0x0C22, 0x0C23, 0x0C24, 0x0C25, 0x0C26, 0x0C27, - 0x0C28, 0x0C29, 0x0C2A, 0x0C2B, 0x0C2C, 0x0C2D, 0x0C2E, 0x0C2F, - 0x1608, 0x1609, 0x160A, 0x160B, 0x160C, 0x160D, 0x160E, 0x160F, - 0x1610, 0x1611, 0x1612, 0x1613, 0x1614, 0x1615, 0x1616, 0x1617, - 0x1618, 0x1619, 0x161A, 0x161B, 0x161C, 0x161D, 0x161E, 0x161F, - 0x1620, 0x1621, 0x1622, 0x1623, 0x1624, 0x1625, 0x1626, 0x1627, - 0x1628, 0x1629, 0x162A, 0x162B, 0x162C, 0x162D, 0x162E, 0x162F, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0348, 0x0349, 0x034A, 0x034B, 0x034C, 0x034D, 0x034E, 0x034F, - 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357, - 0x0358, 0x0359, 0x035A, 0x035B, 0x035C, 0x035D, 0x035E, 0x035F, - 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367, - 0x0368, 0x0369, 0x036A, 0x036B, 0x036C, 0x036D, 0x036E, 0x036F, - 0x0D48, 0x0D49, 0x0D4A, 0x0D4B, 0x0D4C, 0x0D4D, 0x0D4E, 0x0D4F, - 0x0D50, 0x0D51, 0x0D52, 0x0D53, 0x0D54, 0x0D55, 0x0D56, 0x0D57, - 0x0D58, 0x0D59, 0x0D5A, 0x0D5B, 0x0D5C, 0x0D5D, 0x0D5E, 0x0D5F, - 0x0D60, 0x0D61, 0x0D62, 0x0D63, 0x0D64, 0x0D65, 0x0D66, 0x0D67, - 0x0D68, 0x0D69, 0x0D6A, 0x0D6B, 0x0D6C, 0x0D6D, 0x0D6E, 0x0D6F, - 0x1748, 0x1749, 0x174A, 0x174B, 0x174C, 0x174D, 0x174E, 0x174F, - 0x1750, 0x1751, 0x1752, 0x1753, 0x1754, 0x1755, 0x1756, 0x1757, - 0x1758, 0x1759, 0x175A, 0x175B, 0x175C, 0x175D, 0x175E, 0x175F, - 0x1760, 0x1761, 0x1762, 0x1763, 0x1764, 0x1765, 0x1766, 0x1767, - 0x1768, 0x1769, 0x176A, 0x176B, 0x176C, 0x176D, 0x176E, 0x176F, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0488, 0x0489, 0x048A, 0x048B, 0x048C, 0x048D, 0x048E, 0x048F, - 0x0490, 0x0491, 0x0492, 0x0493, 0x0494, 0x0495, 0x0496, 0x0497, - 0x0498, 0x0499, 0x049A, 0x049B, 0x049C, 0x049D, 0x049E, 0x049F, - 0x04A0, 0x04A1, 0x04A2, 0x04A3, 0x04A4, 0x04A5, 0x04A6, 0x04A7, - 0x04A8, 0x04A9, 0x04AA, 0x04AB, 0x04AC, 0x04AD, 0x04AE, 0x04AF, - 0x0E88, 0x0E89, 0x0E8A, 0x0E8B, 0x0E8C, 0x0E8D, 0x0E8E, 0x0E8F, - 0x0E90, 0x0E91, 0x0E92, 0x0E93, 0x0E94, 0x0E95, 0x0E96, 0x0E97, - 0x0E98, 0x0E99, 0x0E9A, 0x0E9B, 0x0E9C, 0x0E9D, 0x0E9E, 0x0E9F, - 0x0EA0, 0x0EA1, 0x0EA2, 0x0EA3, 0x0EA4, 0x0EA5, 0x0EA6, 0x0EA7, - 0x0EA8, 0x0EA9, 0x0EAA, 0x0EAB, 0x0EAC, 0x0EAD, 0x0EAE, 0x0EAF, - 0x1888, 0x1889, 0x188A, 0x188B, 0x188C, 0x188D, 0x188E, 0x188F, - 0x1890, 0x1891, 0x1892, 0x1893, 0x1894, 0x1895, 0x1896, 0x1897, - 0x1898, 0x1899, 0x189A, 0x189B, 0x189C, 0x189D, 0x189E, 0x189F, - 0x18A0, 0x18A1, 0x18A2, 0x18A3, 0x18A4, 0x18A5, 0x18A6, 0x18A7, - 0x18A8, 0x18A9, 0x18AA, 0x18AB, 0x18AC, 0x18AD, 0x18AE, 0x18AF, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x05C8, 0x05C9, 0x05CA, 0x05CB, 0x05CC, 0x05CD, 0x05CE, 0x05CF, - 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, - 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, - 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, - 0x05E8, 0x05E9, 0x05EA, 0x05EB, 0x05EC, 0x05ED, 0x05EE, 0x05EF, - 0x0FC8, 0x0FC9, 0x0FCA, 0x0FCB, 0x0FCC, 0x0FCD, 0x0FCE, 0x0FCF, - 0x0FD0, 0x0FD1, 0x0FD2, 0x0FD3, 0x0FD4, 0x0FD5, 0x0FD6, 0x0FD7, - 0x0FD8, 0x0FD9, 0x0FDA, 0x0FDB, 0x0FDC, 0x0FDD, 0x0FDE, 0x0FDF, - 0x0FE0, 0x0FE1, 0x0FE2, 0x0FE3, 0x0FE4, 0x0FE5, 0x0FE6, 0x0FE7, - 0x0FE8, 0x0FE9, 0x0FEA, 0x0FEB, 0x0FEC, 0x0FED, 0x0FEE, 0x0FEF, - 0x19C8, 0x19C9, 0x19CA, 0x19CB, 0x19CC, 0x19CD, 0x19CE, 0x19CF, - 0x19D0, 0x19D1, 0x19D2, 0x19D3, 0x19D4, 0x19D5, 0x19D6, 0x19D7, - 0x19D8, 0x19D9, 0x19DA, 0x19DB, 0x19DC, 0x19DD, 0x19DE, 0x19DF, - 0x19E0, 0x19E1, 0x19E2, 0x19E3, 0x19E4, 0x19E5, 0x19E6, 0x19E7, - 0x19E8, 0x19E9, 0x19EA, 0x19EB, 0x19EC, 0x19ED, 0x19EE, 0x19EF, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0708, 0x0709, 0x070A, 0x070B, 0x070C, 0x070D, 0x070E, 0x070F, - 0x0710, 0x0711, 0x0712, 0x0713, 0x0714, 0x0715, 0x0716, 0x0717, - 0x0718, 0x0719, 0x071A, 0x071B, 0x071C, 0x071D, 0x071E, 0x071F, - 0x0720, 0x0721, 0x0722, 0x0723, 0x0724, 0x0725, 0x0726, 0x0727, - 0x0728, 0x0729, 0x072A, 0x072B, 0x072C, 0x072D, 0x072E, 0x072F, - 0x1108, 0x1109, 0x110A, 0x110B, 0x110C, 0x110D, 0x110E, 0x110F, - 0x1110, 0x1111, 0x1112, 0x1113, 0x1114, 0x1115, 0x1116, 0x1117, - 0x1118, 0x1119, 0x111A, 0x111B, 0x111C, 0x111D, 0x111E, 0x111F, - 0x1120, 0x1121, 0x1122, 0x1123, 0x1124, 0x1125, 0x1126, 0x1127, - 0x1128, 0x1129, 0x112A, 0x112B, 0x112C, 0x112D, 0x112E, 0x112F, - 0x1B08, 0x1B09, 0x1B0A, 0x1B0B, 0x1B0C, 0x1B0D, 0x1B0E, 0x1B0F, - 0x1B10, 0x1B11, 0x1B12, 0x1B13, 0x1B14, 0x1B15, 0x1B16, 0x1B17, - 0x1B18, 0x1B19, 0x1B1A, 0x1B1B, 0x1B1C, 0x1B1D, 0x1B1E, 0x1B1F, - 0x1B20, 0x1B21, 0x1B22, 0x1B23, 0x1B24, 0x1B25, 0x1B26, 0x1B27, - 0x1B28, 0x1B29, 0x1B2A, 0x1B2B, 0x1B2C, 0x1B2D, 0x1B2E, 0x1B2F, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0848, 0x0849, 0x084A, 0x084B, 0x084C, 0x084D, 0x084E, 0x084F, - 0x0850, 0x0851, 0x0852, 0x0853, 0x0854, 0x0855, 0x0856, 0x0857, - 0x0858, 0x0859, 0x085A, 0x085B, 0x085C, 0x085D, 0x085E, 0x085F, - 0x0860, 0x0861, 0x0862, 0x0863, 0x0864, 0x0865, 0x0866, 0x0867, - 0x0868, 0x0869, 0x086A, 0x086B, 0x086C, 0x086D, 0x086E, 0x086F, - 0x1248, 0x1249, 0x124A, 0x124B, 0x124C, 0x124D, 0x124E, 0x124F, - 0x1250, 0x1251, 0x1252, 0x1253, 0x1254, 0x1255, 0x1256, 0x1257, - 0x1258, 0x1259, 0x125A, 0x125B, 0x125C, 0x125D, 0x125E, 0x125F, - 0x1260, 0x1261, 0x1262, 0x1263, 0x1264, 0x1265, 0x1266, 0x1267, - 0x1268, 0x1269, 0x126A, 0x126B, 0x126C, 0x126D, 0x126E, 0x126F, - 0x1C48, 0x1C49, 0x1C4A, 0x1C4B, 0x1C4C, 0x1C4D, 0x1C4E, 0x1C4F, - 0x1C50, 0x1C51, 0x1C52, 0x1C53, 0x1C54, 0x1C55, 0x1C56, 0x1C57, - 0x1C58, 0x1C59, 0x1C5A, 0x1C5B, 0x1C5C, 0x1C5D, 0x1C5E, 0x1C5F, - 0x1C60, 0x1C61, 0x1C62, 0x1C63, 0x1C64, 0x1C65, 0x1C66, 0x1C67, - 0x1C68, 0x1C69, 0x1C6A, 0x1C6B, 0x1C6C, 0x1C6D, 0x1C6E, 0x1C6F, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0988, 0x0989, 0x098A, 0x098B, 0x098C, 0x098D, 0x098E, 0x098F, - 0x0990, 0x0991, 0x0992, 0x0993, 0x0994, 0x0995, 0x0996, 0x0997, - 0x0998, 0x0999, 0x099A, 0x099B, 0x099C, 0x099D, 0x099E, 0x099F, - 0x09A0, 0x09A1, 0x09A2, 0x09A3, 0x09A4, 0x09A5, 0x09A6, 0x09A7, - 0x09A8, 0x09A9, 0x09AA, 0x09AB, 0x09AC, 0x09AD, 0x09AE, 0x09AF, - 0x1388, 0x1389, 0x138A, 0x138B, 0x138C, 0x138D, 0x138E, 0x138F, - 0x1390, 0x1391, 0x1392, 0x1393, 0x1394, 0x1395, 0x1396, 0x1397, - 0x1398, 0x1399, 0x139A, 0x139B, 0x139C, 0x139D, 0x139E, 0x139F, - 0x13A0, 0x13A1, 0x13A2, 0x13A3, 0x13A4, 0x13A5, 0x13A6, 0x13A7, - 0x13A8, 0x13A9, 0x13AA, 0x13AB, 0x13AC, 0x13AD, 0x13AE, 0x13AF, - 0x1D88, 0x1D89, 0x1D8A, 0x1D8B, 0x1D8C, 0x1D8D, 0x1D8E, 0x1D8F, - 0x1D90, 0x1D91, 0x1D92, 0x1D93, 0x1D94, 0x1D95, 0x1D96, 0x1D97, - 0x1D98, 0x1D99, 0x1D9A, 0x1D9B, 0x1D9C, 0x1D9D, 0x1D9E, 0x1D9F, - 0x1DA0, 0x1DA1, 0x1DA2, 0x1DA3, 0x1DA4, 0x1DA5, 0x1DA6, 0x1DA7, - 0x1DA8, 0x1DA9, 0x1DAA, 0x1DAB, 0x1DAC, 0x1DAD, 0x1DAE, 0x1DAF, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, - 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF, - 0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, - 0x0108, 0x0109, 0x010A, 0x010B, 0x010C, 0x010D, 0x010E, 0x010F, - 0x0110, 0x0111, 0x0112, 0x0113, 0x0114, 0x0115, 0x0116, 0x0117, - 0x0AF0, 0x0AF1, 0x0AF2, 0x0AF3, 0x0AF4, 0x0AF5, 0x0AF6, 0x0AF7, - 0x0AF8, 0x0AF9, 0x0AFA, 0x0AFB, 0x0AFC, 0x0AFD, 0x0AFE, 0x0AFF, - 0x0B00, 0x0B01, 0x0B02, 0x0B03, 0x0B04, 0x0B05, 0x0B06, 0x0B07, - 0x0B08, 0x0B09, 0x0B0A, 0x0B0B, 0x0B0C, 0x0B0D, 0x0B0E, 0x0B0F, - 0x0B10, 0x0B11, 0x0B12, 0x0B13, 0x0B14, 0x0B15, 0x0B16, 0x0B17, - 0x14F0, 0x14F1, 0x14F2, 0x14F3, 0x14F4, 0x14F5, 0x14F6, 0x14F7, - 0x14F8, 0x14F9, 0x14FA, 0x14FB, 0x14FC, 0x14FD, 0x14FE, 0x14FF, - 0x1500, 0x1501, 0x1502, 0x1503, 0x1504, 0x1505, 0x1506, 0x1507, - 0x1508, 0x1509, 0x150A, 0x150B, 0x150C, 0x150D, 0x150E, 0x150F, - 0x1510, 0x1511, 0x1512, 0x1513, 0x1514, 0x1515, 0x1516, 0x1517, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0230, 0x0231, 0x0232, 0x0233, 0x0234, 0x0235, 0x0236, 0x0237, - 0x0238, 0x0239, 0x023A, 0x023B, 0x023C, 0x023D, 0x023E, 0x023F, - 0x0240, 0x0241, 0x0242, 0x0243, 0x0244, 0x0245, 0x0246, 0x0247, - 0x0248, 0x0249, 0x024A, 0x024B, 0x024C, 0x024D, 0x024E, 0x024F, - 0x0250, 0x0251, 0x0252, 0x0253, 0x0254, 0x0255, 0x0256, 0x0257, - 0x0C30, 0x0C31, 0x0C32, 0x0C33, 0x0C34, 0x0C35, 0x0C36, 0x0C37, - 0x0C38, 0x0C39, 0x0C3A, 0x0C3B, 0x0C3C, 0x0C3D, 0x0C3E, 0x0C3F, - 0x0C40, 0x0C41, 0x0C42, 0x0C43, 0x0C44, 0x0C45, 0x0C46, 0x0C47, - 0x0C48, 0x0C49, 0x0C4A, 0x0C4B, 0x0C4C, 0x0C4D, 0x0C4E, 0x0C4F, - 0x0C50, 0x0C51, 0x0C52, 0x0C53, 0x0C54, 0x0C55, 0x0C56, 0x0C57, - 0x1630, 0x1631, 0x1632, 0x1633, 0x1634, 0x1635, 0x1636, 0x1637, - 0x1638, 0x1639, 0x163A, 0x163B, 0x163C, 0x163D, 0x163E, 0x163F, - 0x1640, 0x1641, 0x1642, 0x1643, 0x1644, 0x1645, 0x1646, 0x1647, - 0x1648, 0x1649, 0x164A, 0x164B, 0x164C, 0x164D, 0x164E, 0x164F, - 0x1650, 0x1651, 0x1652, 0x1653, 0x1654, 0x1655, 0x1656, 0x1657, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0370, 0x0371, 0x0372, 0x0373, 0x0374, 0x0375, 0x0376, 0x0377, - 0x0378, 0x0379, 0x037A, 0x037B, 0x037C, 0x037D, 0x037E, 0x037F, - 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0386, 0x0387, - 0x0388, 0x0389, 0x038A, 0x038B, 0x038C, 0x038D, 0x038E, 0x038F, - 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, - 0x0D70, 0x0D71, 0x0D72, 0x0D73, 0x0D74, 0x0D75, 0x0D76, 0x0D77, - 0x0D78, 0x0D79, 0x0D7A, 0x0D7B, 0x0D7C, 0x0D7D, 0x0D7E, 0x0D7F, - 0x0D80, 0x0D81, 0x0D82, 0x0D83, 0x0D84, 0x0D85, 0x0D86, 0x0D87, - 0x0D88, 0x0D89, 0x0D8A, 0x0D8B, 0x0D8C, 0x0D8D, 0x0D8E, 0x0D8F, - 0x0D90, 0x0D91, 0x0D92, 0x0D93, 0x0D94, 0x0D95, 0x0D96, 0x0D97, - 0x1770, 0x1771, 0x1772, 0x1773, 0x1774, 0x1775, 0x1776, 0x1777, - 0x1778, 0x1779, 0x177A, 0x177B, 0x177C, 0x177D, 0x177E, 0x177F, - 0x1780, 0x1781, 0x1782, 0x1783, 0x1784, 0x1785, 0x1786, 0x1787, - 0x1788, 0x1789, 0x178A, 0x178B, 0x178C, 0x178D, 0x178E, 0x178F, - 0x1790, 0x1791, 0x1792, 0x1793, 0x1794, 0x1795, 0x1796, 0x1797, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x04B0, 0x04B1, 0x04B2, 0x04B3, 0x04B4, 0x04B5, 0x04B6, 0x04B7, - 0x04B8, 0x04B9, 0x04BA, 0x04BB, 0x04BC, 0x04BD, 0x04BE, 0x04BF, - 0x04C0, 0x04C1, 0x04C2, 0x04C3, 0x04C4, 0x04C5, 0x04C6, 0x04C7, - 0x04C8, 0x04C9, 0x04CA, 0x04CB, 0x04CC, 0x04CD, 0x04CE, 0x04CF, - 0x04D0, 0x04D1, 0x04D2, 0x04D3, 0x04D4, 0x04D5, 0x04D6, 0x04D7, - 0x0EB0, 0x0EB1, 0x0EB2, 0x0EB3, 0x0EB4, 0x0EB5, 0x0EB6, 0x0EB7, - 0x0EB8, 0x0EB9, 0x0EBA, 0x0EBB, 0x0EBC, 0x0EBD, 0x0EBE, 0x0EBF, - 0x0EC0, 0x0EC1, 0x0EC2, 0x0EC3, 0x0EC4, 0x0EC5, 0x0EC6, 0x0EC7, - 0x0EC8, 0x0EC9, 0x0ECA, 0x0ECB, 0x0ECC, 0x0ECD, 0x0ECE, 0x0ECF, - 0x0ED0, 0x0ED1, 0x0ED2, 0x0ED3, 0x0ED4, 0x0ED5, 0x0ED6, 0x0ED7, - 0x18B0, 0x18B1, 0x18B2, 0x18B3, 0x18B4, 0x18B5, 0x18B6, 0x18B7, - 0x18B8, 0x18B9, 0x18BA, 0x18BB, 0x18BC, 0x18BD, 0x18BE, 0x18BF, - 0x18C0, 0x18C1, 0x18C2, 0x18C3, 0x18C4, 0x18C5, 0x18C6, 0x18C7, - 0x18C8, 0x18C9, 0x18CA, 0x18CB, 0x18CC, 0x18CD, 0x18CE, 0x18CF, - 0x18D0, 0x18D1, 0x18D2, 0x18D3, 0x18D4, 0x18D5, 0x18D6, 0x18D7, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x05F0, 0x05F1, 0x05F2, 0x05F3, 0x05F4, 0x05F5, 0x05F6, 0x05F7, - 0x05F8, 0x05F9, 0x05FA, 0x05FB, 0x05FC, 0x05FD, 0x05FE, 0x05FF, - 0x0600, 0x0601, 0x0602, 0x0603, 0x0604, 0x0605, 0x0606, 0x0607, - 0x0608, 0x0609, 0x060A, 0x060B, 0x060C, 0x060D, 0x060E, 0x060F, - 0x0610, 0x0611, 0x0612, 0x0613, 0x0614, 0x0615, 0x0616, 0x0617, - 0x0FF0, 0x0FF1, 0x0FF2, 0x0FF3, 0x0FF4, 0x0FF5, 0x0FF6, 0x0FF7, - 0x0FF8, 0x0FF9, 0x0FFA, 0x0FFB, 0x0FFC, 0x0FFD, 0x0FFE, 0x0FFF, - 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007, - 0x1008, 0x1009, 0x100A, 0x100B, 0x100C, 0x100D, 0x100E, 0x100F, - 0x1010, 0x1011, 0x1012, 0x1013, 0x1014, 0x1015, 0x1016, 0x1017, - 0x19F0, 0x19F1, 0x19F2, 0x19F3, 0x19F4, 0x19F5, 0x19F6, 0x19F7, - 0x19F8, 0x19F9, 0x19FA, 0x19FB, 0x19FC, 0x19FD, 0x19FE, 0x19FF, - 0x1A00, 0x1A01, 0x1A02, 0x1A03, 0x1A04, 0x1A05, 0x1A06, 0x1A07, - 0x1A08, 0x1A09, 0x1A0A, 0x1A0B, 0x1A0C, 0x1A0D, 0x1A0E, 0x1A0F, - 0x1A10, 0x1A11, 0x1A12, 0x1A13, 0x1A14, 0x1A15, 0x1A16, 0x1A17, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0730, 0x0731, 0x0732, 0x0733, 0x0734, 0x0735, 0x0736, 0x0737, - 0x0738, 0x0739, 0x073A, 0x073B, 0x073C, 0x073D, 0x073E, 0x073F, - 0x0740, 0x0741, 0x0742, 0x0743, 0x0744, 0x0745, 0x0746, 0x0747, - 0x0748, 0x0749, 0x074A, 0x074B, 0x074C, 0x074D, 0x074E, 0x074F, - 0x0750, 0x0751, 0x0752, 0x0753, 0x0754, 0x0755, 0x0756, 0x0757, - 0x1130, 0x1131, 0x1132, 0x1133, 0x1134, 0x1135, 0x1136, 0x1137, - 0x1138, 0x1139, 0x113A, 0x113B, 0x113C, 0x113D, 0x113E, 0x113F, - 0x1140, 0x1141, 0x1142, 0x1143, 0x1144, 0x1145, 0x1146, 0x1147, - 0x1148, 0x1149, 0x114A, 0x114B, 0x114C, 0x114D, 0x114E, 0x114F, - 0x1150, 0x1151, 0x1152, 0x1153, 0x1154, 0x1155, 0x1156, 0x1157, - 0x1B30, 0x1B31, 0x1B32, 0x1B33, 0x1B34, 0x1B35, 0x1B36, 0x1B37, - 0x1B38, 0x1B39, 0x1B3A, 0x1B3B, 0x1B3C, 0x1B3D, 0x1B3E, 0x1B3F, - 0x1B40, 0x1B41, 0x1B42, 0x1B43, 0x1B44, 0x1B45, 0x1B46, 0x1B47, - 0x1B48, 0x1B49, 0x1B4A, 0x1B4B, 0x1B4C, 0x1B4D, 0x1B4E, 0x1B4F, - 0x1B50, 0x1B51, 0x1B52, 0x1B53, 0x1B54, 0x1B55, 0x1B56, 0x1B57, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0870, 0x0871, 0x0872, 0x0873, 0x0874, 0x0875, 0x0876, 0x0877, - 0x0878, 0x0879, 0x087A, 0x087B, 0x087C, 0x087D, 0x087E, 0x087F, - 0x0880, 0x0881, 0x0882, 0x0883, 0x0884, 0x0885, 0x0886, 0x0887, - 0x0888, 0x0889, 0x088A, 0x088B, 0x088C, 0x088D, 0x088E, 0x088F, - 0x0890, 0x0891, 0x0892, 0x0893, 0x0894, 0x0895, 0x0896, 0x0897, - 0x1270, 0x1271, 0x1272, 0x1273, 0x1274, 0x1275, 0x1276, 0x1277, - 0x1278, 0x1279, 0x127A, 0x127B, 0x127C, 0x127D, 0x127E, 0x127F, - 0x1280, 0x1281, 0x1282, 0x1283, 0x1284, 0x1285, 0x1286, 0x1287, - 0x1288, 0x1289, 0x128A, 0x128B, 0x128C, 0x128D, 0x128E, 0x128F, - 0x1290, 0x1291, 0x1292, 0x1293, 0x1294, 0x1295, 0x1296, 0x1297, - 0x1C70, 0x1C71, 0x1C72, 0x1C73, 0x1C74, 0x1C75, 0x1C76, 0x1C77, - 0x1C78, 0x1C79, 0x1C7A, 0x1C7B, 0x1C7C, 0x1C7D, 0x1C7E, 0x1C7F, - 0x1C80, 0x1C81, 0x1C82, 0x1C83, 0x1C84, 0x1C85, 0x1C86, 0x1C87, - 0x1C88, 0x1C89, 0x1C8A, 0x1C8B, 0x1C8C, 0x1C8D, 0x1C8E, 0x1C8F, - 0x1C90, 0x1C91, 0x1C92, 0x1C93, 0x1C94, 0x1C95, 0x1C96, 0x1C97, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x09B0, 0x09B1, 0x09B2, 0x09B3, 0x09B4, 0x09B5, 0x09B6, 0x09B7, - 0x09B8, 0x09B9, 0x09BA, 0x09BB, 0x09BC, 0x09BD, 0x09BE, 0x09BF, - 0x09C0, 0x09C1, 0x09C2, 0x09C3, 0x09C4, 0x09C5, 0x09C6, 0x09C7, - 0x09C8, 0x09C9, 0x09CA, 0x09CB, 0x09CC, 0x09CD, 0x09CE, 0x09CF, - 0x09D0, 0x09D1, 0x09D2, 0x09D3, 0x09D4, 0x09D5, 0x09D6, 0x09D7, - 0x13B0, 0x13B1, 0x13B2, 0x13B3, 0x13B4, 0x13B5, 0x13B6, 0x13B7, - 0x13B8, 0x13B9, 0x13BA, 0x13BB, 0x13BC, 0x13BD, 0x13BE, 0x13BF, - 0x13C0, 0x13C1, 0x13C2, 0x13C3, 0x13C4, 0x13C5, 0x13C6, 0x13C7, - 0x13C8, 0x13C9, 0x13CA, 0x13CB, 0x13CC, 0x13CD, 0x13CE, 0x13CF, - 0x13D0, 0x13D1, 0x13D2, 0x13D3, 0x13D4, 0x13D5, 0x13D6, 0x13D7, - 0x1DB0, 0x1DB1, 0x1DB2, 0x1DB3, 0x1DB4, 0x1DB5, 0x1DB6, 0x1DB7, - 0x1DB8, 0x1DB9, 0x1DBA, 0x1DBB, 0x1DBC, 0x1DBD, 0x1DBE, 0x1DBF, - 0x1DC0, 0x1DC1, 0x1DC2, 0x1DC3, 0x1DC4, 0x1DC5, 0x1DC6, 0x1DC7, - 0x1DC8, 0x1DC9, 0x1DCA, 0x1DCB, 0x1DCC, 0x1DCD, 0x1DCE, 0x1DCF, - 0x1DD0, 0x1DD1, 0x1DD2, 0x1DD3, 0x1DD4, 0x1DD5, 0x1DD6, 0x1DD7, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0118, 0x0119, 0x011A, 0x011B, 0x011C, 0x011D, 0x011E, 0x011F, - 0x0120, 0x0121, 0x0122, 0x0123, 0x0124, 0x0125, 0x0126, 0x0127, - 0x0128, 0x0129, 0x012A, 0x012B, 0x012C, 0x012D, 0x012E, 0x012F, - 0x0130, 0x0131, 0x0132, 0x0133, 0x0134, 0x0135, 0x0136, 0x0137, - 0x0138, 0x0139, 0x013A, 0x013B, 0x013C, 0x013D, 0x013E, 0x013F, - 0x0B18, 0x0B19, 0x0B1A, 0x0B1B, 0x0B1C, 0x0B1D, 0x0B1E, 0x0B1F, - 0x0B20, 0x0B21, 0x0B22, 0x0B23, 0x0B24, 0x0B25, 0x0B26, 0x0B27, - 0x0B28, 0x0B29, 0x0B2A, 0x0B2B, 0x0B2C, 0x0B2D, 0x0B2E, 0x0B2F, - 0x0B30, 0x0B31, 0x0B32, 0x0B33, 0x0B34, 0x0B35, 0x0B36, 0x0B37, - 0x0B38, 0x0B39, 0x0B3A, 0x0B3B, 0x0B3C, 0x0B3D, 0x0B3E, 0x0B3F, - 0x1518, 0x1519, 0x151A, 0x151B, 0x151C, 0x151D, 0x151E, 0x151F, - 0x1520, 0x1521, 0x1522, 0x1523, 0x1524, 0x1525, 0x1526, 0x1527, - 0x1528, 0x1529, 0x152A, 0x152B, 0x152C, 0x152D, 0x152E, 0x152F, - 0x1530, 0x1531, 0x1532, 0x1533, 0x1534, 0x1535, 0x1536, 0x1537, - 0x1538, 0x1539, 0x153A, 0x153B, 0x153C, 0x153D, 0x153E, 0x153F, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0258, 0x0259, 0x025A, 0x025B, 0x025C, 0x025D, 0x025E, 0x025F, - 0x0260, 0x0261, 0x0262, 0x0263, 0x0264, 0x0265, 0x0266, 0x0267, - 0x0268, 0x0269, 0x026A, 0x026B, 0x026C, 0x026D, 0x026E, 0x026F, - 0x0270, 0x0271, 0x0272, 0x0273, 0x0274, 0x0275, 0x0276, 0x0277, - 0x0278, 0x0279, 0x027A, 0x027B, 0x027C, 0x027D, 0x027E, 0x027F, - 0x0C58, 0x0C59, 0x0C5A, 0x0C5B, 0x0C5C, 0x0C5D, 0x0C5E, 0x0C5F, - 0x0C60, 0x0C61, 0x0C62, 0x0C63, 0x0C64, 0x0C65, 0x0C66, 0x0C67, - 0x0C68, 0x0C69, 0x0C6A, 0x0C6B, 0x0C6C, 0x0C6D, 0x0C6E, 0x0C6F, - 0x0C70, 0x0C71, 0x0C72, 0x0C73, 0x0C74, 0x0C75, 0x0C76, 0x0C77, - 0x0C78, 0x0C79, 0x0C7A, 0x0C7B, 0x0C7C, 0x0C7D, 0x0C7E, 0x0C7F, - 0x1658, 0x1659, 0x165A, 0x165B, 0x165C, 0x165D, 0x165E, 0x165F, - 0x1660, 0x1661, 0x1662, 0x1663, 0x1664, 0x1665, 0x1666, 0x1667, - 0x1668, 0x1669, 0x166A, 0x166B, 0x166C, 0x166D, 0x166E, 0x166F, - 0x1670, 0x1671, 0x1672, 0x1673, 0x1674, 0x1675, 0x1676, 0x1677, - 0x1678, 0x1679, 0x167A, 0x167B, 0x167C, 0x167D, 0x167E, 0x167F, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, - 0x03A0, 0x03A1, 0x03A2, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, - 0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF, - 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, - 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, - 0x0D98, 0x0D99, 0x0D9A, 0x0D9B, 0x0D9C, 0x0D9D, 0x0D9E, 0x0D9F, - 0x0DA0, 0x0DA1, 0x0DA2, 0x0DA3, 0x0DA4, 0x0DA5, 0x0DA6, 0x0DA7, - 0x0DA8, 0x0DA9, 0x0DAA, 0x0DAB, 0x0DAC, 0x0DAD, 0x0DAE, 0x0DAF, - 0x0DB0, 0x0DB1, 0x0DB2, 0x0DB3, 0x0DB4, 0x0DB5, 0x0DB6, 0x0DB7, - 0x0DB8, 0x0DB9, 0x0DBA, 0x0DBB, 0x0DBC, 0x0DBD, 0x0DBE, 0x0DBF, - 0x1798, 0x1799, 0x179A, 0x179B, 0x179C, 0x179D, 0x179E, 0x179F, - 0x17A0, 0x17A1, 0x17A2, 0x17A3, 0x17A4, 0x17A5, 0x17A6, 0x17A7, - 0x17A8, 0x17A9, 0x17AA, 0x17AB, 0x17AC, 0x17AD, 0x17AE, 0x17AF, - 0x17B0, 0x17B1, 0x17B2, 0x17B3, 0x17B4, 0x17B5, 0x17B6, 0x17B7, - 0x17B8, 0x17B9, 0x17BA, 0x17BB, 0x17BC, 0x17BD, 0x17BE, 0x17BF, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x04D8, 0x04D9, 0x04DA, 0x04DB, 0x04DC, 0x04DD, 0x04DE, 0x04DF, - 0x04E0, 0x04E1, 0x04E2, 0x04E3, 0x04E4, 0x04E5, 0x04E6, 0x04E7, - 0x04E8, 0x04E9, 0x04EA, 0x04EB, 0x04EC, 0x04ED, 0x04EE, 0x04EF, - 0x04F0, 0x04F1, 0x04F2, 0x04F3, 0x04F4, 0x04F5, 0x04F6, 0x04F7, - 0x04F8, 0x04F9, 0x04FA, 0x04FB, 0x04FC, 0x04FD, 0x04FE, 0x04FF, - 0x0ED8, 0x0ED9, 0x0EDA, 0x0EDB, 0x0EDC, 0x0EDD, 0x0EDE, 0x0EDF, - 0x0EE0, 0x0EE1, 0x0EE2, 0x0EE3, 0x0EE4, 0x0EE5, 0x0EE6, 0x0EE7, - 0x0EE8, 0x0EE9, 0x0EEA, 0x0EEB, 0x0EEC, 0x0EED, 0x0EEE, 0x0EEF, - 0x0EF0, 0x0EF1, 0x0EF2, 0x0EF3, 0x0EF4, 0x0EF5, 0x0EF6, 0x0EF7, - 0x0EF8, 0x0EF9, 0x0EFA, 0x0EFB, 0x0EFC, 0x0EFD, 0x0EFE, 0x0EFF, - 0x18D8, 0x18D9, 0x18DA, 0x18DB, 0x18DC, 0x18DD, 0x18DE, 0x18DF, - 0x18E0, 0x18E1, 0x18E2, 0x18E3, 0x18E4, 0x18E5, 0x18E6, 0x18E7, - 0x18E8, 0x18E9, 0x18EA, 0x18EB, 0x18EC, 0x18ED, 0x18EE, 0x18EF, - 0x18F0, 0x18F1, 0x18F2, 0x18F3, 0x18F4, 0x18F5, 0x18F6, 0x18F7, - 0x18F8, 0x18F9, 0x18FA, 0x18FB, 0x18FC, 0x18FD, 0x18FE, 0x18FF, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0618, 0x0619, 0x061A, 0x061B, 0x061C, 0x061D, 0x061E, 0x061F, - 0x0620, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, - 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, - 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637, - 0x0638, 0x0639, 0x063A, 0x063B, 0x063C, 0x063D, 0x063E, 0x063F, - 0x1018, 0x1019, 0x101A, 0x101B, 0x101C, 0x101D, 0x101E, 0x101F, - 0x1020, 0x1021, 0x1022, 0x1023, 0x1024, 0x1025, 0x1026, 0x1027, - 0x1028, 0x1029, 0x102A, 0x102B, 0x102C, 0x102D, 0x102E, 0x102F, - 0x1030, 0x1031, 0x1032, 0x1033, 0x1034, 0x1035, 0x1036, 0x1037, - 0x1038, 0x1039, 0x103A, 0x103B, 0x103C, 0x103D, 0x103E, 0x103F, - 0x1A18, 0x1A19, 0x1A1A, 0x1A1B, 0x1A1C, 0x1A1D, 0x1A1E, 0x1A1F, - 0x1A20, 0x1A21, 0x1A22, 0x1A23, 0x1A24, 0x1A25, 0x1A26, 0x1A27, - 0x1A28, 0x1A29, 0x1A2A, 0x1A2B, 0x1A2C, 0x1A2D, 0x1A2E, 0x1A2F, - 0x1A30, 0x1A31, 0x1A32, 0x1A33, 0x1A34, 0x1A35, 0x1A36, 0x1A37, - 0x1A38, 0x1A39, 0x1A3A, 0x1A3B, 0x1A3C, 0x1A3D, 0x1A3E, 0x1A3F, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0758, 0x0759, 0x075A, 0x075B, 0x075C, 0x075D, 0x075E, 0x075F, - 0x0760, 0x0761, 0x0762, 0x0763, 0x0764, 0x0765, 0x0766, 0x0767, - 0x0768, 0x0769, 0x076A, 0x076B, 0x076C, 0x076D, 0x076E, 0x076F, - 0x0770, 0x0771, 0x0772, 0x0773, 0x0774, 0x0775, 0x0776, 0x0777, - 0x0778, 0x0779, 0x077A, 0x077B, 0x077C, 0x077D, 0x077E, 0x077F, - 0x1158, 0x1159, 0x115A, 0x115B, 0x115C, 0x115D, 0x115E, 0x115F, - 0x1160, 0x1161, 0x1162, 0x1163, 0x1164, 0x1165, 0x1166, 0x1167, - 0x1168, 0x1169, 0x116A, 0x116B, 0x116C, 0x116D, 0x116E, 0x116F, - 0x1170, 0x1171, 0x1172, 0x1173, 0x1174, 0x1175, 0x1176, 0x1177, - 0x1178, 0x1179, 0x117A, 0x117B, 0x117C, 0x117D, 0x117E, 0x117F, - 0x1B58, 0x1B59, 0x1B5A, 0x1B5B, 0x1B5C, 0x1B5D, 0x1B5E, 0x1B5F, - 0x1B60, 0x1B61, 0x1B62, 0x1B63, 0x1B64, 0x1B65, 0x1B66, 0x1B67, - 0x1B68, 0x1B69, 0x1B6A, 0x1B6B, 0x1B6C, 0x1B6D, 0x1B6E, 0x1B6F, - 0x1B70, 0x1B71, 0x1B72, 0x1B73, 0x1B74, 0x1B75, 0x1B76, 0x1B77, - 0x1B78, 0x1B79, 0x1B7A, 0x1B7B, 0x1B7C, 0x1B7D, 0x1B7E, 0x1B7F, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x0898, 0x0899, 0x089A, 0x089B, 0x089C, 0x089D, 0x089E, 0x089F, - 0x08A0, 0x08A1, 0x08A2, 0x08A3, 0x08A4, 0x08A5, 0x08A6, 0x08A7, - 0x08A8, 0x08A9, 0x08AA, 0x08AB, 0x08AC, 0x08AD, 0x08AE, 0x08AF, - 0x08B0, 0x08B1, 0x08B2, 0x08B3, 0x08B4, 0x08B5, 0x08B6, 0x08B7, - 0x08B8, 0x08B9, 0x08BA, 0x08BB, 0x08BC, 0x08BD, 0x08BE, 0x08BF, - 0x1298, 0x1299, 0x129A, 0x129B, 0x129C, 0x129D, 0x129E, 0x129F, - 0x12A0, 0x12A1, 0x12A2, 0x12A3, 0x12A4, 0x12A5, 0x12A6, 0x12A7, - 0x12A8, 0x12A9, 0x12AA, 0x12AB, 0x12AC, 0x12AD, 0x12AE, 0x12AF, - 0x12B0, 0x12B1, 0x12B2, 0x12B3, 0x12B4, 0x12B5, 0x12B6, 0x12B7, - 0x12B8, 0x12B9, 0x12BA, 0x12BB, 0x12BC, 0x12BD, 0x12BE, 0x12BF, - 0x1C98, 0x1C99, 0x1C9A, 0x1C9B, 0x1C9C, 0x1C9D, 0x1C9E, 0x1C9F, - 0x1CA0, 0x1CA1, 0x1CA2, 0x1CA3, 0x1CA4, 0x1CA5, 0x1CA6, 0x1CA7, - 0x1CA8, 0x1CA9, 0x1CAA, 0x1CAB, 0x1CAC, 0x1CAD, 0x1CAE, 0x1CAF, - 0x1CB0, 0x1CB1, 0x1CB2, 0x1CB3, 0x1CB4, 0x1CB5, 0x1CB6, 0x1CB7, - 0x1CB8, 0x1CB9, 0x1CBA, 0x1CBB, 0x1CBC, 0x1CBD, 0x1CBE, 0x1CBF, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, - 0x09D8, 0x09D9, 0x09DA, 0x09DB, 0x09DC, 0x09DD, 0x09DE, 0x09DF, - 0x09E0, 0x09E1, 0x09E2, 0x09E3, 0x09E4, 0x09E5, 0x09E6, 0x09E7, - 0x09E8, 0x09E9, 0x09EA, 0x09EB, 0x09EC, 0x09ED, 0x09EE, 0x09EF, - 0x09F0, 0x09F1, 0x09F2, 0x09F3, 0x09F4, 0x09F5, 0x09F6, 0x09F7, - 0x09F8, 0x09F9, 0x09FA, 0x09FB, 0x09FC, 0x09FD, 0x09FE, 0x09FF, - 0x13D8, 0x13D9, 0x13DA, 0x13DB, 0x13DC, 0x13DD, 0x13DE, 0x13DF, - 0x13E0, 0x13E1, 0x13E2, 0x13E3, 0x13E4, 0x13E5, 0x13E6, 0x13E7, - 0x13E8, 0x13E9, 0x13EA, 0x13EB, 0x13EC, 0x13ED, 0x13EE, 0x13EF, - 0x13F0, 0x13F1, 0x13F2, 0x13F3, 0x13F4, 0x13F5, 0x13F6, 0x13F7, - 0x13F8, 0x13F9, 0x13FA, 0x13FB, 0x13FC, 0x13FD, 0x13FE, 0x13FF, - 0x1DD8, 0x1DD9, 0x1DDA, 0x1DDB, 0x1DDC, 0x1DDD, 0x1DDE, 0x1DDF, - 0x1DE0, 0x1DE1, 0x1DE2, 0x1DE3, 0x1DE4, 0x1DE5, 0x1DE6, 0x1DE7, - 0x1DE8, 0x1DE9, 0x1DEA, 0x1DEB, 0x1DEC, 0x1DED, 0x1DEE, 0x1DEF, - 0x1DF0, 0x1DF1, 0x1DF2, 0x1DF3, 0x1DF4, 0x1DF5, 0x1DF6, 0x1DF7, - 0x1DF8, 0x1DF9, 0x1DFA, 0x1DFB, 0x1DFC, 0x1DFD, 0x1DFE, 0x1DFF, - 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00, 0x1E00 - }; - - private const int CharBitmapCount = 322; - private const int CharBitmapBytes = 8; - - private static readonly byte[] CharBitmap = new byte[CharBitmapCount * CharBitmapBytes] - { - 0x43, 0x5D, 0x55, 0x45, 0x65, 0x7D, 0x43, 0x7F, - 0x77, 0x6B, 0x5D, 0x5D, 0x41, 0x5D, 0x5D, 0x7F, - 0x61, 0x5D, 0x5D, 0x61, 0x5D, 0x5D, 0x61, 0x7F, - 0x63, 0x5D, 0x7D, 0x7D, 0x7D, 0x5D, 0x63, 0x7F, - 0x61, 0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x61, 0x7F, - 0x41, 0x7D, 0x7D, 0x61, 0x7D, 0x7D, 0x41, 0x7F, - 0x41, 0x7D, 0x7D, 0x61, 0x7D, 0x7D, 0x7D, 0x7F, - 0x43, 0x7D, 0x7D, 0x7D, 0x4D, 0x5D, 0x43, 0x7F, - 0x5D, 0x5D, 0x5D, 0x41, 0x5D, 0x5D, 0x5D, 0x7F, - 0x63, 0x77, 0x77, 0x77, 0x77, 0x77, 0x63, 0x7F, - 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5D, 0x63, 0x7F, - 0x5D, 0x6D, 0x75, 0x79, 0x75, 0x6D, 0x5D, 0x7F, - 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x41, 0x7F, - 0x5D, 0x49, 0x55, 0x55, 0x5D, 0x5D, 0x5D, 0x7F, - 0x5D, 0x5D, 0x59, 0x55, 0x4D, 0x5D, 0x5D, 0x7F, - 0x63, 0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x63, 0x7F, - 0x61, 0x5D, 0x5D, 0x61, 0x7D, 0x7D, 0x7D, 0x7F, - 0x63, 0x5D, 0x5D, 0x5D, 0x55, 0x6D, 0x53, 0x7F, - 0x61, 0x5D, 0x5D, 0x61, 0x75, 0x6D, 0x5D, 0x7F, - 0x63, 0x5D, 0x7D, 0x63, 0x5F, 0x5D, 0x63, 0x7F, - 0x41, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x7F, - 0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x63, 0x7F, - 0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x6B, 0x77, 0x7F, - 0x5D, 0x5D, 0x5D, 0x55, 0x55, 0x49, 0x5D, 0x7F, - 0x5D, 0x5D, 0x6B, 0x77, 0x6B, 0x5D, 0x5D, 0x7F, - 0x5D, 0x5D, 0x6B, 0x77, 0x77, 0x77, 0x77, 0x7F, - 0x41, 0x5F, 0x6F, 0x77, 0x7B, 0x7D, 0x41, 0x7F, - 0x41, 0x79, 0x79, 0x79, 0x79, 0x79, 0x41, 0x7F, - 0x7F, 0x7D, 0x7B, 0x77, 0x6F, 0x5F, 0x7F, 0x7F, - 0x41, 0x4F, 0x4F, 0x4F, 0x4F, 0x4F, 0x41, 0x7F, - 0x7F, 0x7F, 0x77, 0x6B, 0x5D, 0x7F, 0x7F, 0x7F, - 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x00, - 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, - 0x77, 0x77, 0x77, 0x77, 0x77, 0x7F, 0x77, 0x7F, - 0x6B, 0x6B, 0x6B, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, - 0x6B, 0x6B, 0x00, 0x6B, 0x00, 0x6B, 0x6B, 0x7F, - 0x77, 0x43, 0x75, 0x63, 0x57, 0x61, 0x77, 0x7F, - 0x79, 0x59, 0x6F, 0x77, 0x7B, 0x4D, 0x4F, 0x7F, - 0x7B, 0x75, 0x75, 0x7B, 0x55, 0x6D, 0x53, 0x7F, - 0x77, 0x77, 0x77, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, - 0x77, 0x7B, 0x7D, 0x7D, 0x7D, 0x7B, 0x77, 0x7F, - 0x77, 0x6F, 0x5F, 0x5F, 0x5F, 0x6F, 0x77, 0x7F, - 0x77, 0x55, 0x63, 0x77, 0x63, 0x55, 0x77, 0x7F, - 0x7F, 0x77, 0x77, 0x41, 0x77, 0x77, 0x7F, 0x7F, - 0x7F, 0x7F, 0x7F, 0x7F, 0x77, 0x77, 0x7B, 0x7F, - 0x7F, 0x7F, 0x7F, 0x41, 0x7F, 0x7F, 0x7F, 0x7F, - 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x77, 0x7F, - 0x7F, 0x5F, 0x6F, 0x77, 0x7B, 0x7D, 0x7F, 0x7F, - 0x63, 0x5D, 0x4D, 0x55, 0x59, 0x5D, 0x63, 0x7F, - 0x77, 0x73, 0x77, 0x77, 0x77, 0x77, 0x63, 0x7F, - 0x63, 0x5D, 0x5F, 0x67, 0x7B, 0x7D, 0x41, 0x7F, - 0x41, 0x5F, 0x6F, 0x67, 0x5F, 0x5D, 0x63, 0x7F, - 0x6F, 0x67, 0x6B, 0x6D, 0x41, 0x6F, 0x6F, 0x7F, - 0x41, 0x7D, 0x61, 0x5F, 0x5F, 0x5D, 0x63, 0x7F, - 0x47, 0x7B, 0x7D, 0x61, 0x5D, 0x5D, 0x63, 0x7F, - 0x41, 0x5F, 0x6F, 0x77, 0x7B, 0x7B, 0x7B, 0x7F, - 0x63, 0x5D, 0x5D, 0x63, 0x5D, 0x5D, 0x63, 0x7F, - 0x63, 0x5D, 0x5D, 0x43, 0x5F, 0x6F, 0x71, 0x7F, - 0x7F, 0x7F, 0x77, 0x7F, 0x77, 0x7F, 0x7F, 0x7F, - 0x7F, 0x7F, 0x77, 0x7F, 0x77, 0x77, 0x7B, 0x7F, - 0x6F, 0x77, 0x7B, 0x7D, 0x7B, 0x77, 0x6F, 0x7F, - 0x7F, 0x7F, 0x41, 0x7F, 0x41, 0x7F, 0x7F, 0x7F, - 0x7B, 0x77, 0x6F, 0x5F, 0x6F, 0x77, 0x7B, 0x7F, - 0x63, 0x5D, 0x6F, 0x77, 0x77, 0x7F, 0x77, 0x7F, - 0x43, 0x5D, 0x55, 0x45, 0x65, 0x7D, 0x43, 0x7F, - 0x77, 0x6B, 0x5D, 0x5D, 0x41, 0x5D, 0x5D, 0x7F, - 0x61, 0x5D, 0x5D, 0x61, 0x5D, 0x5D, 0x61, 0x7F, - 0x63, 0x5D, 0x7D, 0x7D, 0x7D, 0x5D, 0x63, 0x7F, - 0x61, 0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x61, 0x7F, - 0x41, 0x7D, 0x7D, 0x61, 0x7D, 0x7D, 0x41, 0x7F, - 0x41, 0x7D, 0x7D, 0x61, 0x7D, 0x7D, 0x7D, 0x7F, - 0x43, 0x7D, 0x7D, 0x7D, 0x4D, 0x5D, 0x43, 0x7F, - 0x5D, 0x5D, 0x5D, 0x41, 0x5D, 0x5D, 0x5D, 0x7F, - 0x63, 0x77, 0x77, 0x77, 0x77, 0x77, 0x63, 0x7F, - 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5D, 0x63, 0x7F, - 0x5D, 0x6D, 0x75, 0x79, 0x75, 0x6D, 0x5D, 0x7F, - 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x41, 0x7F, - 0x5D, 0x49, 0x55, 0x55, 0x5D, 0x5D, 0x5D, 0x7F, - 0x5D, 0x5D, 0x59, 0x55, 0x4D, 0x5D, 0x5D, 0x7F, - 0x63, 0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x63, 0x7F, - 0x61, 0x5D, 0x5D, 0x61, 0x7D, 0x7D, 0x7D, 0x7F, - 0x63, 0x5D, 0x5D, 0x5D, 0x55, 0x6D, 0x53, 0x7F, - 0x61, 0x5D, 0x5D, 0x61, 0x75, 0x6D, 0x5D, 0x7F, - 0x63, 0x5D, 0x7D, 0x63, 0x5F, 0x5D, 0x63, 0x7F, - 0x41, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x7F, - 0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x63, 0x7F, - 0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x6B, 0x77, 0x7F, - 0x5D, 0x5D, 0x5D, 0x55, 0x55, 0x49, 0x5D, 0x7F, - 0x5D, 0x5D, 0x6B, 0x77, 0x6B, 0x5D, 0x5D, 0x7F, - 0x5D, 0x5D, 0x6B, 0x77, 0x77, 0x77, 0x77, 0x7F, - 0x41, 0x5F, 0x6F, 0x77, 0x7B, 0x7D, 0x41, 0x7F, - 0x41, 0x79, 0x79, 0x79, 0x79, 0x79, 0x41, 0x7F, - 0x7F, 0x7D, 0x7B, 0x77, 0x6F, 0x5F, 0x7F, 0x7F, - 0x41, 0x4F, 0x4F, 0x4F, 0x4F, 0x4F, 0x41, 0x7F, - 0x7F, 0x7F, 0x77, 0x6B, 0x5D, 0x7F, 0x7F, 0x7F, - 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x00, - 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, - 0x77, 0x77, 0x77, 0x77, 0x77, 0x7F, 0x77, 0x7F, - 0x6B, 0x6B, 0x6B, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, - 0x6B, 0x6B, 0x00, 0x6B, 0x00, 0x6B, 0x6B, 0x7F, - 0x77, 0x43, 0x75, 0x63, 0x57, 0x61, 0x77, 0x7F, - 0x79, 0x59, 0x6F, 0x77, 0x7B, 0x4D, 0x4F, 0x7F, - 0x7B, 0x75, 0x75, 0x7B, 0x55, 0x6D, 0x53, 0x7F, - 0x77, 0x77, 0x77, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, - 0x77, 0x7B, 0x7D, 0x7D, 0x7D, 0x7B, 0x77, 0x7F, - 0x77, 0x6F, 0x5F, 0x5F, 0x5F, 0x6F, 0x77, 0x7F, - 0x77, 0x55, 0x63, 0x77, 0x63, 0x55, 0x77, 0x7F, - 0x7F, 0x77, 0x77, 0x41, 0x77, 0x77, 0x7F, 0x7F, - 0x7F, 0x7F, 0x7F, 0x7F, 0x77, 0x77, 0x7B, 0x7F, - 0x7F, 0x7F, 0x7F, 0x41, 0x7F, 0x7F, 0x7F, 0x7F, - 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x77, 0x7F, - 0x7F, 0x5F, 0x6F, 0x77, 0x7B, 0x7D, 0x7F, 0x7F, - 0x63, 0x5D, 0x4D, 0x55, 0x59, 0x5D, 0x63, 0x7F, - 0x77, 0x73, 0x77, 0x77, 0x77, 0x77, 0x63, 0x7F, - 0x63, 0x5D, 0x5F, 0x67, 0x7B, 0x7D, 0x41, 0x7F, - 0x41, 0x5F, 0x6F, 0x67, 0x5F, 0x5D, 0x63, 0x7F, - 0x6F, 0x67, 0x6B, 0x6D, 0x41, 0x6F, 0x6F, 0x7F, - 0x41, 0x7D, 0x61, 0x5F, 0x5F, 0x5D, 0x63, 0x7F, - 0x47, 0x7B, 0x7D, 0x61, 0x5D, 0x5D, 0x63, 0x7F, - 0x41, 0x5F, 0x6F, 0x77, 0x7B, 0x7B, 0x7B, 0x7F, - 0x63, 0x5D, 0x5D, 0x63, 0x5D, 0x5D, 0x63, 0x7F, - 0x63, 0x5D, 0x5D, 0x43, 0x5F, 0x6F, 0x71, 0x7F, - 0x7F, 0x7F, 0x77, 0x7F, 0x77, 0x7F, 0x7F, 0x7F, - 0x7F, 0x7F, 0x77, 0x7F, 0x77, 0x77, 0x7B, 0x7F, - 0x6F, 0x77, 0x7B, 0x7D, 0x7B, 0x77, 0x6F, 0x7F, - 0x7F, 0x7F, 0x41, 0x7F, 0x41, 0x7F, 0x7F, 0x7F, - 0x7B, 0x77, 0x6F, 0x5F, 0x6F, 0x77, 0x7B, 0x7F, - 0x63, 0x5D, 0x6F, 0x77, 0x77, 0x7F, 0x77, 0x7F, - 0x3C, 0x22, 0x2A, 0x3A, 0x1A, 0x02, 0x3C, 0x00, - 0x08, 0x14, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x00, - 0x1E, 0x22, 0x22, 0x1E, 0x22, 0x22, 0x1E, 0x00, - 0x1C, 0x22, 0x02, 0x02, 0x02, 0x22, 0x1C, 0x00, - 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, - 0x3E, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x3E, 0x00, - 0x3E, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x00, - 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x3C, 0x00, - 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, - 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x1C, 0x00, - 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x00, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x00, - 0x22, 0x36, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x00, - 0x22, 0x22, 0x26, 0x2A, 0x32, 0x22, 0x22, 0x00, - 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, - 0x1E, 0x22, 0x22, 0x1E, 0x02, 0x02, 0x02, 0x00, - 0x1C, 0x22, 0x22, 0x22, 0x2A, 0x12, 0x2C, 0x00, - 0x1E, 0x22, 0x22, 0x1E, 0x0A, 0x12, 0x22, 0x00, - 0x1C, 0x22, 0x02, 0x1C, 0x20, 0x22, 0x1C, 0x00, - 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x14, 0x08, 0x00, - 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x36, 0x22, 0x00, - 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x00, - 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, 0x00, - 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x00, - 0x3E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x3E, 0x00, - 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x00, - 0x3E, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3E, 0x00, - 0x00, 0x00, 0x08, 0x14, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x00, - 0x14, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x14, 0x14, 0x7F, 0x14, 0x7F, 0x14, 0x14, 0x00, - 0x08, 0x3C, 0x0A, 0x1C, 0x28, 0x1E, 0x08, 0x00, - 0x06, 0x26, 0x10, 0x08, 0x04, 0x32, 0x30, 0x00, - 0x04, 0x0A, 0x0A, 0x04, 0x2A, 0x12, 0x2C, 0x00, - 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08, 0x00, - 0x08, 0x10, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00, - 0x08, 0x2A, 0x1C, 0x08, 0x1C, 0x2A, 0x08, 0x00, - 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, - 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00, 0x00, - 0x1C, 0x22, 0x32, 0x2A, 0x26, 0x22, 0x1C, 0x00, - 0x08, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, - 0x1C, 0x22, 0x20, 0x18, 0x04, 0x02, 0x3E, 0x00, - 0x3E, 0x20, 0x10, 0x18, 0x20, 0x22, 0x1C, 0x00, - 0x10, 0x18, 0x14, 0x12, 0x3E, 0x10, 0x10, 0x00, - 0x3E, 0x02, 0x1E, 0x20, 0x20, 0x22, 0x1C, 0x00, - 0x38, 0x04, 0x02, 0x1E, 0x22, 0x22, 0x1C, 0x00, - 0x3E, 0x20, 0x10, 0x08, 0x04, 0x04, 0x04, 0x00, - 0x1C, 0x22, 0x22, 0x1C, 0x22, 0x22, 0x1C, 0x00, - 0x1C, 0x22, 0x22, 0x3C, 0x20, 0x10, 0x0E, 0x00, - 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x08, 0x00, 0x08, 0x08, 0x04, 0x00, - 0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10, 0x00, - 0x00, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x00, 0x00, - 0x04, 0x08, 0x10, 0x20, 0x10, 0x08, 0x04, 0x00, - 0x1C, 0x22, 0x10, 0x08, 0x08, 0x00, 0x08, 0x00, - 0x3C, 0x22, 0x2A, 0x3A, 0x1A, 0x02, 0x3C, 0x00, - 0x08, 0x14, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x00, - 0x1E, 0x22, 0x22, 0x1E, 0x22, 0x22, 0x1E, 0x00, - 0x1C, 0x22, 0x02, 0x02, 0x02, 0x22, 0x1C, 0x00, - 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, - 0x3E, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x3E, 0x00, - 0x3E, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x00, - 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x3C, 0x00, - 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, - 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x1C, 0x00, - 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x00, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x00, - 0x22, 0x36, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x00, - 0x22, 0x22, 0x26, 0x2A, 0x32, 0x22, 0x22, 0x00, - 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, - 0x1E, 0x22, 0x22, 0x1E, 0x02, 0x02, 0x02, 0x00, - 0x1C, 0x22, 0x22, 0x22, 0x2A, 0x12, 0x2C, 0x00, - 0x1E, 0x22, 0x22, 0x1E, 0x0A, 0x12, 0x22, 0x00, - 0x1C, 0x22, 0x02, 0x1C, 0x20, 0x22, 0x1C, 0x00, - 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x14, 0x08, 0x00, - 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x36, 0x22, 0x00, - 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x00, - 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, 0x00, - 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x00, - 0x3E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x3E, 0x00, - 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x00, - 0x3E, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3E, 0x00, - 0x00, 0x00, 0x08, 0x14, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, - 0x04, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x1C, 0x20, 0x3C, 0x22, 0x3C, 0x00, - 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x00, - 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x3C, 0x00, - 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x3C, 0x00, - 0x00, 0x00, 0x1C, 0x22, 0x3E, 0x02, 0x3C, 0x00, - 0x18, 0x24, 0x04, 0x1E, 0x04, 0x04, 0x04, 0x00, - 0x00, 0x00, 0x1C, 0x22, 0x22, 0x3C, 0x20, 0x1C, - 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x00, - 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x1C, 0x00, - 0x10, 0x00, 0x18, 0x10, 0x10, 0x10, 0x12, 0x0C, - 0x02, 0x02, 0x22, 0x12, 0x0E, 0x12, 0x22, 0x00, - 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, - 0x00, 0x00, 0x36, 0x2A, 0x2A, 0x2A, 0x22, 0x00, - 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x00, - 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x00, - 0x00, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x02, 0x02, - 0x00, 0x00, 0x3C, 0x22, 0x22, 0x3C, 0x20, 0x20, - 0x00, 0x00, 0x3A, 0x06, 0x02, 0x02, 0x02, 0x00, - 0x00, 0x00, 0x3C, 0x02, 0x1C, 0x20, 0x1E, 0x00, - 0x04, 0x04, 0x1E, 0x04, 0x04, 0x24, 0x18, 0x00, - 0x00, 0x00, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x00, - 0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x08, 0x00, - 0x00, 0x00, 0x22, 0x22, 0x2A, 0x2A, 0x36, 0x00, - 0x00, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, 0x00, - 0x00, 0x00, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, - 0x00, 0x00, 0x3E, 0x10, 0x08, 0x04, 0x3E, 0x00, - 0x38, 0x0C, 0x0C, 0x06, 0x0C, 0x0C, 0x38, 0x00, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x0E, 0x18, 0x18, 0x30, 0x18, 0x18, 0x0E, 0x00, - 0x2C, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x2A, 0x14, 0x2A, 0x14, 0x2A, 0x00, 0x00, - 0x10, 0x08, 0x36, 0x7F, 0x3F, 0x3F, 0x7E, 0x36, - 0x10, 0x08, 0x36, 0x49, 0x21, 0x21, 0x4A, 0x36, - 0x00, 0x00, 0x02, 0x06, 0x0E, 0x1E, 0x36, 0x42, - 0x7F, 0x22, 0x14, 0x08, 0x08, 0x14, 0x2A, 0x7F, - 0x00, 0x40, 0x20, 0x11, 0x0A, 0x04, 0x04, 0x00, - 0x7F, 0x3F, 0x5F, 0x6C, 0x75, 0x7B, 0x7B, 0x7F, - 0x3F, 0x3F, 0x3F, 0x3B, 0x39, 0x00, 0x79, 0x7B, - 0x7F, 0x00, 0x7F, 0x00, 0x7F, 0x00, 0x00, 0x7F, - 0x08, 0x04, 0x02, 0x7F, 0x02, 0x04, 0x08, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, - 0x08, 0x08, 0x08, 0x08, 0x49, 0x2A, 0x1C, 0x08, - 0x08, 0x1C, 0x2A, 0x49, 0x08, 0x08, 0x08, 0x08, - 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x40, 0x40, 0x40, 0x44, 0x46, 0x7F, 0x06, 0x04, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x13, 0x18, 0x1C, 0x7E, 0x1C, 0x18, 0x10, 0x6F, - 0x64, 0x0C, 0x1C, 0x3F, 0x1C, 0x0C, 0x04, 0x7B, - 0x40, 0x48, 0x08, 0x7F, 0x3E, 0x1C, 0x48, 0x40, - 0x40, 0x48, 0x1C, 0x3E, 0x7F, 0x08, 0x48, 0x48, - 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x7F, - 0x08, 0x10, 0x20, 0x7F, 0x20, 0x10, 0x08, 0x00, - 0x2A, 0x55, 0x2A, 0x55, 0x2A, 0x55, 0x2A, 0x55, - 0x55, 0x2A, 0x55, 0x2A, 0x55, 0x2A, 0x55, 0x2A, - 0x00, 0x3E, 0x41, 0x01, 0x01, 0x01, 0x7F, 0x00, - 0x00, 0x00, 0x3F, 0x40, 0x40, 0x40, 0x7F, 0x00, - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, - 0x08, 0x1C, 0x3E, 0x7F, 0x3E, 0x1C, 0x08, 0x00, - 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, - 0x14, 0x14, 0x77, 0x00, 0x77, 0x14, 0x14, 0x00, - 0x7F, 0x40, 0x40, 0x4C, 0x4C, 0x40, 0x40, 0x7F, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x7B, 0x77, 0x6F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, - 0x7F, 0x7F, 0x63, 0x5F, 0x43, 0x5D, 0x43, 0x7F, - 0x7D, 0x7D, 0x61, 0x5D, 0x5D, 0x5D, 0x61, 0x7F, - 0x7F, 0x7F, 0x43, 0x7D, 0x7D, 0x7D, 0x43, 0x7F, - 0x5F, 0x5F, 0x43, 0x5D, 0x5D, 0x5D, 0x43, 0x7F, - 0x7F, 0x7F, 0x63, 0x5D, 0x41, 0x7D, 0x43, 0x7F, - 0x67, 0x5B, 0x7B, 0x61, 0x7B, 0x7B, 0x7B, 0x7F, - 0x7F, 0x7F, 0x63, 0x5D, 0x5D, 0x43, 0x5F, 0x63, - 0x7D, 0x7D, 0x61, 0x5D, 0x5D, 0x5D, 0x5D, 0x7F, - 0x77, 0x7F, 0x73, 0x77, 0x77, 0x77, 0x63, 0x7F, - 0x6F, 0x7F, 0x67, 0x6F, 0x6F, 0x6F, 0x6D, 0x73, - 0x7D, 0x7D, 0x5D, 0x6D, 0x71, 0x6D, 0x5D, 0x7F, - 0x73, 0x77, 0x77, 0x77, 0x77, 0x77, 0x63, 0x7F, - 0x7F, 0x7F, 0x49, 0x55, 0x55, 0x55, 0x5D, 0x7F, - 0x7F, 0x7F, 0x61, 0x5D, 0x5D, 0x5D, 0x5D, 0x7F, - 0x7F, 0x7F, 0x63, 0x5D, 0x5D, 0x5D, 0x63, 0x7F, - 0x7F, 0x7F, 0x61, 0x5D, 0x5D, 0x61, 0x7D, 0x7D, - 0x7F, 0x7F, 0x43, 0x5D, 0x5D, 0x43, 0x5F, 0x5F, - 0x7F, 0x7F, 0x45, 0x79, 0x7D, 0x7D, 0x7D, 0x7F, - 0x7F, 0x7F, 0x43, 0x7D, 0x63, 0x5F, 0x61, 0x7F, - 0x7B, 0x7B, 0x61, 0x7B, 0x7B, 0x5B, 0x67, 0x7F, - 0x7F, 0x7F, 0x5D, 0x5D, 0x5D, 0x4D, 0x53, 0x7F, - 0x7F, 0x7F, 0x5D, 0x5D, 0x5D, 0x6B, 0x77, 0x7F, - 0x7F, 0x7F, 0x5D, 0x5D, 0x55, 0x55, 0x49, 0x7F, - 0x7F, 0x7F, 0x5D, 0x6B, 0x77, 0x6B, 0x5D, 0x7F, - 0x7F, 0x7F, 0x5D, 0x5D, 0x5D, 0x43, 0x5F, 0x63, - 0x7F, 0x7F, 0x41, 0x6F, 0x77, 0x7B, 0x41, 0x7F, - 0x47, 0x73, 0x73, 0x79, 0x73, 0x73, 0x47, 0x7F, - 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, - 0x71, 0x67, 0x67, 0x4F, 0x67, 0x67, 0x71, 0x7F, - 0x53, 0x65, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, - 0x7F, 0x55, 0x6B, 0x55, 0x6B, 0x55, 0x7F, 0x7F, - 0x70, 0x60, 0x7E, 0x31, 0x79, 0x30, 0x3F, 0x02, - 0x00, 0x18, 0x07, 0x00, 0x07, 0x0C, 0x08, 0x70 - }; - - private const int CharSetCharCount = 256; - - private static readonly ushort[] CharSetPrimary = new ushort[CharSetCharCount] - { - 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, - 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, - 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, - 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, - 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, - 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, - 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, - 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, - 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, - 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, - 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, - 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, - 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, - 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, - 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, - 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, - 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, - 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, - 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, - 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, - 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, - 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, - 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, - 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, - 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, - 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, - 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, - 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, - 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, - 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, - 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, - 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF - }; - - private static readonly ushort[] CharSetSecondaryStandard = new ushort[CharSetCharCount] - { - 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, - 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, - 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, - 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, - 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, - 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, - 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, - 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, - 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, - 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, - 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, - 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, - 0x0120, 0x0121, 0x0122, 0x0123, 0x0124, 0x0125, 0x0126, 0x0127, - 0x0128, 0x0129, 0x012A, 0x012B, 0x012C, 0x012D, 0x012E, 0x012F, - 0x0130, 0x0131, 0x0132, 0x0133, 0x0134, 0x0135, 0x0136, 0x0137, - 0x0138, 0x0139, 0x013A, 0x013B, 0x013C, 0x013D, 0x013E, 0x013F, - 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, - 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, - 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, - 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, - 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, - 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, - 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, - 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, - 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, - 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, - 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, - 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, - 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, - 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, - 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, - 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF - }; - - private static readonly ushort[] CharSetSecondaryEnhanced = new ushort[CharSetCharCount] - { - 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, - 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, - 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, - 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, - 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, - 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, - 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, - 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, - 0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, - 0x0108, 0x0109, 0x010A, 0x010B, 0x010C, 0x010D, 0x010E, 0x010F, - 0x0110, 0x0111, 0x0112, 0x0113, 0x0114, 0x0115, 0x0116, 0x0117, - 0x0118, 0x0119, 0x011A, 0x011B, 0x011C, 0x011D, 0x011E, 0x011F, - 0x0120, 0x0121, 0x0122, 0x0123, 0x0124, 0x0125, 0x0126, 0x0127, - 0x0128, 0x0129, 0x012A, 0x012B, 0x012C, 0x012D, 0x012E, 0x012F, - 0x0130, 0x0131, 0x0132, 0x0133, 0x0134, 0x0135, 0x0136, 0x0137, - 0x0138, 0x0139, 0x013A, 0x013B, 0x013C, 0x013D, 0x013E, 0x013F, - 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, - 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, - 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, - 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, - 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, - 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, - 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, - 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, - 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, - 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, - 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, - 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, - 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, - 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, - 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, - 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF - }; - - private const int ColorPaletteCount = 0x210; - - private const int ColorMono00 = 0x000; - private const int ColorMono01 = 0x001; - private const int ColorMono02 = 0x002; - private const int ColorMono04 = 0x004; - private const int ColorMono08 = 0x008; - private const int ColorMono10 = 0x010; - private const int ColorMono20 = 0x020; - private const int ColorMono40 = 0x040; - private const int ColorMono80 = 0x080; - - private const int ColorWhite00 = 0x100; - private const int ColorWhite01 = 0x101; - private const int ColorWhite02 = 0x102; - private const int ColorWhite04 = 0x104; - private const int ColorWhite08 = 0x108; - private const int ColorWhite10 = 0x110; - private const int ColorWhite20 = 0x120; - private const int ColorWhite40 = 0x140; - private const int ColorWhite80 = 0x180; - - private const int ColorDHires0 = 0x200; - private const int ColorDHires1 = 0x201; - private const int ColorDHires2 = 0x202; - private const int ColorDHires3 = 0x203; - private const int ColorDHires4 = 0x204; - private const int ColorDHires5 = 0x205; - private const int ColorDHires6 = 0x206; - private const int ColorDHires7 = 0x207; - private const int ColorDHires8 = 0x208; - private const int ColorDHires9 = 0x209; - private const int ColorDHiresA = 0x20A; - private const int ColorDHiresB = 0x20B; - private const int ColorDHiresC = 0x20C; - private const int ColorDHiresD = 0x20D; - private const int ColorDHiresE = 0x20E; - private const int ColorDHiresF = 0x20F; - - private const int ColorLoresDataCount = 16; - - private static readonly int[] ColorLores = new int[ColorLoresDataCount] - { - ColorDHires0, ColorDHires8, ColorDHires1, ColorDHires9, - ColorDHires2, ColorDHiresA, ColorDHires3, ColorDHiresB, - ColorDHires4, ColorDHiresC, ColorDHires5, ColorDHiresD, - ColorDHires6, ColorDHiresE, ColorDHires7, ColorDHiresF - }; - - private static readonly int[] Color7MLores = new int[2 * ColorLoresDataCount] - { - ColorDHires0, ColorDHires9, ColorDHires6, ColorDHiresF, // even columns - ColorDHires0, ColorDHires9, ColorDHires6, ColorDHiresF, - ColorDHires0, ColorDHires9, ColorDHires6, ColorDHiresF, - ColorDHires0, ColorDHires9, ColorDHires6, ColorDHiresF, - ColorDHires0, ColorDHires0, ColorDHires0, ColorDHires0, // odd columns - ColorDHires6, ColorDHires6, ColorDHires6, ColorDHires6, - ColorDHires9, ColorDHires9, ColorDHires9, ColorDHires9, - ColorDHiresF, ColorDHiresF, ColorDHiresF, ColorDHiresF - }; - - private static readonly int[] ColorDLores = new int[2 * ColorLoresDataCount] - { - ColorDHires0, ColorDHires4, ColorDHires8, ColorDHiresC, // even columns - ColorDHires1, ColorDHires5, ColorDHires9, ColorDHiresD, - ColorDHires2, ColorDHires6, ColorDHiresA, ColorDHiresE, - ColorDHires3, ColorDHires7, ColorDHiresB, ColorDHiresF, - ColorDHires0, ColorDHires8, ColorDHires1, ColorDHires9, // odd columns - ColorDHires2, ColorDHiresA, ColorDHires3, ColorDHiresB, - ColorDHires4, ColorDHiresC, ColorDHires5, ColorDHiresD, - ColorDHires6, ColorDHiresE, ColorDHires7, ColorDHiresF - }; - - private const int ColorHiresDataCount = 8; - - private static readonly int[] ColorHires = new int[4 * ColorHiresDataCount] - { - ColorDHires0, ColorDHires0, ColorDHires9, ColorDHiresF, // even columns, high bit off - ColorDHires0, ColorDHires6, ColorDHiresF, ColorDHiresF, - ColorDHires0, ColorDHires0, ColorDHires3, ColorDHiresF, // even columns, high bit on - ColorDHires0, ColorDHiresC, ColorDHiresF, ColorDHiresF, - ColorDHires0, ColorDHires0, ColorDHires6, ColorDHiresF, // odd columns, high bit off - ColorDHires0, ColorDHires9, ColorDHiresF, ColorDHiresF, - ColorDHires0, ColorDHires0, ColorDHiresC, ColorDHiresF, // odd columns, high bit on - ColorDHires0, ColorDHires3, ColorDHiresF, ColorDHiresF - }; - - private const int CyclesPerHBlank = 25; - private const int CyclesPerHSync = 65; - private const int CyclesPerFlush = 8 * CyclesPerHSync; - private const int CyclesPerSecond = 1022730; - - private const int HCountPreset = 0x40; // hcount preset after hcount overflows -> HPE' low [3-13] - private const int HCountLeaveHBlank = 0x58; // hcount when leaving hblank [3-15] - private const int VCountPresetNtsc = 0xFA; // vcount preset after vcount overflows (NTSC) [3-13] - private const int VCountPresetPal = 0xC8; // vcount preset after vcount overflows (PAL) [3-17] - private const int VLineEnterVBlank = 192; // vline when entering vblank (NTSC & PAL) [3-17] - private const int VLineTriggerPreset = 256; // vline when vcount overflows and presets (NTSC & PAL) [3-15, 3-16] - private const int VLineLeaveVBlankNtsc = 262; // vline when leaving vblank (NTSC) [3-13] - private const int VLineLeaveVBlankPal = 312; // vline when leaving vblank (PAL) [3-17] - private const int VSyncsPerFlash = 16; // flash count using vcount overflow [3-17] - - public const int ModeCount = 16; - - public const int Mode0 = 0x0; - public const int Mode1 = 0x1; - public const int Mode2 = 0x2; - public const int Mode3 = 0x3; - public const int Mode4 = 0x4; - public const int Mode5 = 0x5; - public const int Mode6 = 0x6; - public const int Mode7 = 0x7; - public const int Mode8 = 0x8; - public const int Mode9 = 0x9; - public const int ModeA = 0xA; - public const int ModeB = 0xB; - public const int ModeC = 0xC; - public const int ModeD = 0xD; - public const int ModeE = 0xE; - public const int ModeF = 0xF; - - private Action[] FlushRowMode; - - private const int Width = 560; - private const int Height = VLineEnterVBlank; - - private const int TextHeight = 8; - private const int TextRows = Height / TextHeight; - - private const int LoresHeight = 4; - private const int LoresRows = Height / LoresHeight; - - private const int MixedHeight = 160; - private const int MixedCellIndex = MixedHeight * CellColumns; - } -} diff --git a/ExternalCoreProjects/Virtu/Virtu.csproj b/ExternalCoreProjects/Virtu/Virtu.csproj index b8275a61c8..1dded9d752 100644 --- a/ExternalCoreProjects/Virtu/Virtu.csproj +++ b/ExternalCoreProjects/Virtu/Virtu.csproj @@ -9,7 +9,7 @@ Properties Virtu Virtu - v4.6.1 + v4.8 512 @@ -45,35 +45,27 @@ - + - - + - + - - - - - - + - + - - - - + + copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\references\$(TargetFileName)" diff --git a/ExternalCoreProjects/Virtu/Virtu.csproj.DotSettings b/ExternalCoreProjects/Virtu/Virtu.csproj.DotSettings new file mode 100644 index 0000000000..b9fd6ee4f5 --- /dev/null +++ b/ExternalCoreProjects/Virtu/Virtu.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp80 \ No newline at end of file diff --git a/ExternalCoreProjects/Virtu/Virtu.sln.DotSettings b/ExternalCoreProjects/Virtu/Virtu.sln.DotSettings new file mode 100644 index 0000000000..3211634121 --- /dev/null +++ b/ExternalCoreProjects/Virtu/Virtu.sln.DotSettings @@ -0,0 +1,79 @@ + + DO_NOT_SHOW + AA + AB + AC + AD + AE + AF + BA + BB + BC + BD + BE + BF + CA + CB + CC + CD + CE + CF + DA + DB + DC + DD + DE + DF + EA + EB + EC + ED + EE + EF + FA + FB + FC + FD + FE + FF + II + ND + RA + RP + RPC + RS + RX + RY + XCC + YCC + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True \ No newline at end of file diff --git a/ExternalCoreProjects/Virtu/packages.config b/ExternalCoreProjects/Virtu/packages.config new file mode 100644 index 0000000000..8ab13a9023 --- /dev/null +++ b/ExternalCoreProjects/Virtu/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/References/Virtu.dll b/References/Virtu.dll index 3957bd6435827ad8ae8c71840b2df5469a3c2512..a9e41c53f18db9e7e044a25a589a8992ab1cf19d 100644 GIT binary patch literal 139264 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 H7lHo;S-%@+ literal 156160 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%0AL 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�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