接入Essgss.Unity到本项目
This commit is contained in:
parent
d4ba9c0ccb
commit
69dd59e0f6
8
AxibugEmuOnline.Client/Assets/Plugins/Essgee.Unity.meta
Normal file
8
AxibugEmuOnline.Client/Assets/Plugins/Essgee.Unity.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b136ca247e0175d48a98c06d18889c44
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,15 @@
|
||||
public static class AppEnvironment
|
||||
{
|
||||
#if DEBUG
|
||||
public static readonly bool DebugMode = true;
|
||||
#else
|
||||
public static readonly bool DebugMode = false;
|
||||
#endif
|
||||
public static readonly bool EnableCustomUnhandledExceptionHandler = true;
|
||||
public static readonly bool TemporaryDisableCustomExceptionForm = false;
|
||||
|
||||
public static readonly bool EnableLogger = false;
|
||||
public static readonly bool EnableSuperSlowCPULogger = false;
|
||||
|
||||
public static readonly bool EnableOpenGLDebug = false;
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ce38d9b5f910b4c4185e3fab7eb45ecf
|
169
AxibugEmuOnline.Client/Assets/Plugins/Essgee.Unity/AxiMemory.cs
Normal file
169
AxibugEmuOnline.Client/Assets/Plugins/Essgee.Unity/AxiMemory.cs
Normal file
@ -0,0 +1,169 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Essgee.Utilities
|
||||
{
|
||||
internal unsafe static class AxiMemoryEx
|
||||
{
|
||||
static HashSet<GCHandle> GCHandles = new HashSet<GCHandle>();
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
FreeAllGCHandle();
|
||||
set_TempBuffer = new byte[0x100000];
|
||||
}
|
||||
|
||||
public static void GetObjectPtr(this object srcObj, ref GCHandle handle, ref uint* ptr)
|
||||
{
|
||||
GetObjectPtr(srcObj, ref handle, out IntPtr intptr);
|
||||
ptr = (uint*)intptr;
|
||||
}
|
||||
|
||||
public static void GetObjectPtr(this object srcObj, ref GCHandle handle, ref short* ptr)
|
||||
{
|
||||
GetObjectPtr(srcObj, ref handle, out IntPtr intptr);
|
||||
ptr = (short*)intptr;
|
||||
}
|
||||
public static void GetObjectPtr(this object srcObj, ref GCHandle handle, ref ushort* ptr)
|
||||
{
|
||||
GetObjectPtr(srcObj, ref handle, out IntPtr intptr);
|
||||
ptr = (ushort*)intptr;
|
||||
}
|
||||
public static void GetObjectPtr(this object srcObj, ref GCHandle handle, ref int* ptr)
|
||||
{
|
||||
GetObjectPtr(srcObj, ref handle, out IntPtr intptr);
|
||||
ptr = (int*)intptr;
|
||||
}
|
||||
public static void GetObjectPtr(this object srcObj, ref GCHandle handle, ref byte* ptr)
|
||||
{
|
||||
GetObjectPtr(srcObj, ref handle, out IntPtr intptr);
|
||||
ptr = (byte*)intptr;
|
||||
}
|
||||
|
||||
static void GetObjectPtr(this object srcObj, ref GCHandle handle, out IntPtr intptr)
|
||||
{
|
||||
ReleaseGCHandle(ref handle);
|
||||
handle = GCHandle.Alloc(srcObj, GCHandleType.Pinned);
|
||||
GCHandles.Add(handle);
|
||||
intptr = handle.AddrOfPinnedObject();
|
||||
}
|
||||
|
||||
public static void ReleaseGCHandle(this ref GCHandle handle)
|
||||
{
|
||||
if (handle.IsAllocated)
|
||||
handle.Free();
|
||||
GCHandles.Remove(handle);
|
||||
}
|
||||
|
||||
public static void FreeAllGCHandle()
|
||||
{
|
||||
foreach (var handle in GCHandles)
|
||||
{
|
||||
if (handle.IsAllocated)
|
||||
handle.Free();
|
||||
}
|
||||
GCHandles.Clear();
|
||||
}
|
||||
|
||||
#region 指针化 TempBuffer
|
||||
static byte[] TempBuffer_src;
|
||||
static GCHandle TempBuffer_handle;
|
||||
public static byte* TempBuffer;
|
||||
public static byte[] set_TempBuffer
|
||||
{
|
||||
set
|
||||
{
|
||||
TempBuffer_handle.ReleaseGCHandle();
|
||||
if (value == null)
|
||||
return;
|
||||
TempBuffer_src = value;
|
||||
TempBuffer_src.GetObjectPtr(ref TempBuffer_handle, ref TempBuffer);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
public static void Write(this BinaryWriter bw, byte* bufferPtr, int offset, int count)
|
||||
{
|
||||
// 使用指针复制数据到临时数组
|
||||
Buffer.MemoryCopy(bufferPtr + offset, TempBuffer, 0, count);
|
||||
// 使用BinaryWriter写入临时数组
|
||||
bw.Write(TempBuffer_src, 0, count);
|
||||
}
|
||||
public static void Write(this FileStream fs, byte* bufferPtr, int offset, int count)
|
||||
{
|
||||
// 使用指针复制数据到临时数组
|
||||
Buffer.MemoryCopy(bufferPtr + offset, TempBuffer, 0, count);
|
||||
// 使用BinaryWriter写入临时数组
|
||||
fs.Write(TempBuffer_src, 0, count);
|
||||
}
|
||||
public static int Read(this FileStream fs, byte* bufferPtr, int offset, int count)
|
||||
{
|
||||
// 使用BinaryWriter写入临时数组
|
||||
count = fs.Read(TempBuffer_src, offset, count);
|
||||
// 使用指针复制数据到临时数组
|
||||
Buffer.MemoryCopy(TempBuffer, bufferPtr + offset, 0, count);
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
internal unsafe static class AxiArray
|
||||
{
|
||||
|
||||
public static void Copy(byte* src, int srcindex, byte* target, int targetindex, int count)
|
||||
{
|
||||
int singlesize = sizeof(byte);
|
||||
long totalBytesToCopy = count * singlesize;
|
||||
Buffer.MemoryCopy(&src[srcindex], &target[targetindex], totalBytesToCopy, totalBytesToCopy);
|
||||
}
|
||||
public static void Copy(short* src, int srcindex, short* target, int targetindex, int count)
|
||||
{
|
||||
int singlesize = sizeof(short);
|
||||
long totalBytesToCopy = count * singlesize;
|
||||
Buffer.MemoryCopy(&src[srcindex], &target[targetindex], totalBytesToCopy, totalBytesToCopy);
|
||||
}
|
||||
public static void Copy(ushort* src, int srcindex, ushort* target, int targetindex, int count)
|
||||
{
|
||||
int singlesize = sizeof(ushort);
|
||||
long totalBytesToCopy = count * singlesize;
|
||||
Buffer.MemoryCopy(&src[srcindex], &target[targetindex], totalBytesToCopy, totalBytesToCopy);
|
||||
}
|
||||
|
||||
public static void Copy(byte* src, byte* target, int index, int count)
|
||||
{
|
||||
int singlesize = sizeof(byte);
|
||||
long totalBytesToCopy = count * singlesize;
|
||||
Buffer.MemoryCopy(&src[index], &target[index], totalBytesToCopy, totalBytesToCopy);
|
||||
}
|
||||
|
||||
public static void Copy(ushort* src, ushort* target, int index, int count)
|
||||
{
|
||||
int singlesize = sizeof(ushort);
|
||||
long totalBytesToCopy = count * singlesize;
|
||||
Buffer.MemoryCopy(&src[index], &target[index], totalBytesToCopy, totalBytesToCopy);
|
||||
}
|
||||
public static void Copy(ushort* src, ushort* target, int count)
|
||||
{
|
||||
int singlesize = sizeof(ushort);
|
||||
long totalBytesToCopy = count * singlesize;
|
||||
Buffer.MemoryCopy(src, target, totalBytesToCopy, totalBytesToCopy);
|
||||
}
|
||||
public static void Copy(byte* src, byte* target, int count)
|
||||
{
|
||||
int singlesize = sizeof(byte);
|
||||
long totalBytesToCopy = count * singlesize;
|
||||
Buffer.MemoryCopy(src, target, totalBytesToCopy, totalBytesToCopy);
|
||||
}
|
||||
public static void Clear(byte* data, int index, int lenght)
|
||||
{
|
||||
for (int i = index; i < lenght; i++, index++)
|
||||
data[index] = 0;
|
||||
}
|
||||
public static void Clear(ushort* data, int index, int lenght)
|
||||
{
|
||||
for (int i = index; i < lenght; i++, index++)
|
||||
data[index] = 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 922bbb45b85e7de4f8bce1c6f407009f
|
@ -0,0 +1,58 @@
|
||||
using Essgee.Emulation.Configuration;
|
||||
using Essgee.Utilities;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
|
||||
namespace Essgee
|
||||
{
|
||||
public class Configuration
|
||||
{
|
||||
public const int RecentFilesCapacity = 15;
|
||||
public const string DefaultShaderName = "Basic";
|
||||
|
||||
public bool LimitFps { get; set; }
|
||||
public bool ShowFps { get; set; }
|
||||
public bool Mute { get; set; }
|
||||
public float Volume { get; set; }
|
||||
public int SampleRate { get; set; }
|
||||
public bool LowPassFilter { get; set; }
|
||||
public int ScreenSize { get; set; }
|
||||
//[JsonConverter(typeof(StringEnumConverter))]
|
||||
public ScreenSizeMode ScreenSizeMode { get; set; }
|
||||
public string LastShader { get; set; }
|
||||
public bool EnableXInput { get; set; }
|
||||
public bool EnableRumble { get; set; }
|
||||
public bool AutoPause { get; set; }
|
||||
|
||||
public List<string> RecentFiles { get; set; }
|
||||
|
||||
//[JsonConverter(typeof(InterfaceDictionaryConverter<IConfiguration>))]
|
||||
public Dictionary<string, IConfiguration> Machines { get; set; }
|
||||
|
||||
public Dictionary<string, Point> DebugWindows { get; set; }
|
||||
|
||||
public Configuration()
|
||||
{
|
||||
LimitFps = true;
|
||||
ShowFps = false;
|
||||
Mute = false;
|
||||
Volume = 1.0f;
|
||||
SampleRate = 44100;
|
||||
LowPassFilter = true;
|
||||
ScreenSize = 2;
|
||||
ScreenSizeMode = ScreenSizeMode.Scale;
|
||||
LastShader = DefaultShaderName;
|
||||
EnableXInput = false;
|
||||
EnableRumble = false;
|
||||
AutoPause = true;
|
||||
|
||||
RecentFiles = new List<string>(RecentFilesCapacity);
|
||||
|
||||
Machines = new Dictionary<string, IConfiguration>();
|
||||
|
||||
DebugWindows = new Dictionary<string, Point>();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1cb6affe10cc31441b6301f115fbc2ab
|
@ -0,0 +1,44 @@
|
||||
|
||||
using Essgee;
|
||||
using System;
|
||||
|
||||
public static class EmuStandInfo
|
||||
{
|
||||
//À´×ÔmetaData
|
||||
//public static string datDirectoryPath;
|
||||
//public static string metadataDatabaseFilePath;
|
||||
|
||||
|
||||
public static string jsonConfigFileName;//= "Config.json";
|
||||
public static string saveDataDirectoryName;//= "Saves";
|
||||
public static string screenshotDirectoryName;//= "Screenshots";
|
||||
public static string saveStateDirectoryName;//= "Savestates";
|
||||
public static string extraDataDirectoryName;//= "Extras";
|
||||
public static string ProductName;//= "";
|
||||
|
||||
public static string programDataDirectory;// = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), ProductName);
|
||||
public static string programConfigPath;// = Path.Combine(programDataDirectory, jsonConfigFileName);
|
||||
|
||||
public static Configuration Configuration { get; set; }
|
||||
|
||||
public static string ShaderPath;//= Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Assets", "Shaders");
|
||||
public static string SaveDataPath;//= Path.Combine(programDataDirectory, saveDataDirectoryName);
|
||||
public static string ScreenshotPath;//= Path.Combine(programDataDirectory, screenshotDirectoryName);
|
||||
public static string SaveStatePath;//= Path.Combine(programDataDirectory, saveStateDirectoryName);
|
||||
public static string ExtraDataPath;//= Path.Combine(programDataDirectory, extraDataDirectoryName);
|
||||
|
||||
static Random mRandom;
|
||||
public static Random Random
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mRandom == null)
|
||||
{
|
||||
mRandom = new Random();
|
||||
}
|
||||
return mRandom;
|
||||
}
|
||||
}
|
||||
|
||||
public static string ProductVersion { get; set; }
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c668ecd37f7dd4e4092fcac10c385c9b
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 267425c59f2efba48a44a3b55a5eb01d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ab3f0235ad62cb046ac1518f29d21e81
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,19 @@
|
||||
namespace Essgee.Emulation.Audio
|
||||
{
|
||||
public partial class CGBAudio
|
||||
{
|
||||
public class CGBWave : Wave, IDMGAudioChannel
|
||||
{
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
|
||||
for (var i = 0; i < sampleBuffer.Length; i += 2)
|
||||
{
|
||||
sampleBuffer[i + 0] = 0x00;
|
||||
sampleBuffer[i + 1] = 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 595436ed6c508904399695fee24279ff
|
@ -0,0 +1,25 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Essgee.Emulation.Audio
|
||||
{
|
||||
public partial class CGBAudio : DMGAudio, IAudio
|
||||
{
|
||||
public CGBAudio()
|
||||
{
|
||||
//channelSampleBuffer = new List<short>[numChannels];
|
||||
//for (int i = 0; i < numChannels; i++) channelSampleBuffer[i] = new List<short>();
|
||||
//mixedSampleBuffer = new List<short>();
|
||||
|
||||
//改为二维数组
|
||||
channelSampleBuffer_Init(numChannels, 1470);
|
||||
mixedSampleBuffer_set = new short[1470];
|
||||
|
||||
channel1 = new Square(true);
|
||||
channel2 = new Square(false);
|
||||
channel3 = new CGBWave();
|
||||
channel4 = new Noise();
|
||||
|
||||
samplesPerFrame = cyclesPerFrame = cyclesPerSample = -1;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 40f2c16e17787974881d5614068c46b7
|
@ -0,0 +1,214 @@
|
||||
using System;
|
||||
|
||||
namespace Essgee.Emulation.Audio
|
||||
{
|
||||
public partial class DMGAudio
|
||||
{
|
||||
public class Noise : IDMGAudioChannel
|
||||
{
|
||||
static readonly int[] divisors = new int[]
|
||||
{
|
||||
8, 16, 32, 48, 64, 80, 96, 112
|
||||
};
|
||||
|
||||
// NR41
|
||||
byte lengthLoad;
|
||||
|
||||
// NR42
|
||||
byte envelopeStartingVolume, envelopePeriodReload;
|
||||
bool envelopeAddMode;
|
||||
|
||||
// NR43
|
||||
byte clockShift, divisorCode;
|
||||
bool lfsrWidthMode;
|
||||
|
||||
// NR44
|
||||
bool trigger, lengthEnable;
|
||||
|
||||
//
|
||||
|
||||
// Noise
|
||||
int noiseCounter;
|
||||
ushort lfsr;
|
||||
|
||||
// Envelope
|
||||
int volume, envelopeCounter;
|
||||
bool isEnvelopeUpdateEnabled;
|
||||
|
||||
// Misc
|
||||
bool isChannelEnabled, isDacEnabled;
|
||||
int lengthCounter;
|
||||
|
||||
public int OutputVolume { get; private set; }
|
||||
|
||||
public bool IsActive { get { return lengthCounter != 0; } }
|
||||
|
||||
public Noise()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
noiseCounter = 0;
|
||||
lfsr = 0;
|
||||
|
||||
volume = 15;
|
||||
envelopeCounter = 0;
|
||||
isEnvelopeUpdateEnabled = false;
|
||||
|
||||
isChannelEnabled = isDacEnabled = false;
|
||||
lengthCounter = 0;
|
||||
|
||||
OutputVolume = volume;
|
||||
}
|
||||
|
||||
public void LengthCounterClock()
|
||||
{
|
||||
if (lengthCounter > 0 && lengthEnable)
|
||||
{
|
||||
lengthCounter--;
|
||||
if (lengthCounter == 0)
|
||||
isChannelEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void SweepClock()
|
||||
{
|
||||
throw new Exception("Channel type does not support sweep");
|
||||
}
|
||||
|
||||
public void VolumeEnvelopeClock()
|
||||
{
|
||||
envelopeCounter--;
|
||||
if (envelopeCounter == 0)
|
||||
{
|
||||
envelopeCounter = envelopePeriodReload;
|
||||
|
||||
if (isEnvelopeUpdateEnabled)
|
||||
{
|
||||
var newVolume = volume;
|
||||
if (envelopeAddMode) newVolume++;
|
||||
else newVolume--;
|
||||
|
||||
if (newVolume >= 0 && newVolume <= 15)
|
||||
volume = newVolume;
|
||||
else
|
||||
isEnvelopeUpdateEnabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Step()
|
||||
{
|
||||
if (!isChannelEnabled) return;
|
||||
|
||||
noiseCounter--;
|
||||
if (noiseCounter == 0)
|
||||
{
|
||||
noiseCounter = divisors[divisorCode] << clockShift;
|
||||
|
||||
var result = (lfsr & 0b1) ^ ((lfsr >> 1) & 0b1);
|
||||
lfsr = (ushort)((lfsr >> 1) | (result << 14));
|
||||
|
||||
if (lfsrWidthMode)
|
||||
lfsr = (ushort)((lfsr & 0b10111111) | (result << 6));
|
||||
}
|
||||
|
||||
OutputVolume = isDacEnabled && ((lfsr & 0b1) == 0) ? volume : 0;
|
||||
}
|
||||
|
||||
private void Trigger()
|
||||
{
|
||||
isChannelEnabled = true;
|
||||
|
||||
if (lengthCounter == 0) lengthCounter = 64;
|
||||
|
||||
noiseCounter = divisors[divisorCode] << clockShift;
|
||||
volume = envelopeStartingVolume;
|
||||
envelopeCounter = envelopePeriodReload;
|
||||
isEnvelopeUpdateEnabled = true;
|
||||
|
||||
lfsr = 0x7FFF;
|
||||
}
|
||||
|
||||
public void WritePort(byte port, byte value)
|
||||
{
|
||||
switch (port)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case 1:
|
||||
lengthLoad = (byte)((value >> 0) & 0b111111);
|
||||
|
||||
lengthCounter = 64 - lengthLoad;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
envelopeStartingVolume = (byte)((value >> 4) & 0b1111);
|
||||
envelopeAddMode = ((value >> 3) & 0b1) == 0b1;
|
||||
envelopePeriodReload = (byte)((value >> 0) & 0b111);
|
||||
|
||||
isDacEnabled = ((value >> 3) & 0b11111) != 0;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
clockShift = (byte)((value >> 4) & 0b1111);
|
||||
lfsrWidthMode = ((value >> 3) & 0b1) == 0b1;
|
||||
divisorCode = (byte)((value >> 0) & 0b111);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
trigger = ((value >> 7) & 0b1) == 0b1;
|
||||
lengthEnable = ((value >> 6) & 0b1) == 0b1;
|
||||
|
||||
if (trigger) Trigger();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public byte ReadPort(byte port)
|
||||
{
|
||||
switch (port)
|
||||
{
|
||||
case 0:
|
||||
return 0xFF;
|
||||
|
||||
case 1:
|
||||
return 0xFF;
|
||||
|
||||
case 2:
|
||||
return (byte)(
|
||||
(envelopeStartingVolume << 4) |
|
||||
(envelopeAddMode ? (1 << 3) : 0) |
|
||||
(envelopePeriodReload << 0));
|
||||
|
||||
case 3:
|
||||
return (byte)(
|
||||
(clockShift << 4) |
|
||||
(lfsrWidthMode ? (1 << 3) : 0) |
|
||||
(divisorCode << 0));
|
||||
|
||||
case 4:
|
||||
return (byte)(
|
||||
0xBF |
|
||||
(lengthEnable ? (1 << 6) : 0));
|
||||
|
||||
default:
|
||||
return 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteWaveRam(byte offset, byte value)
|
||||
{
|
||||
throw new Exception("Channel type does have Wave RAM");
|
||||
}
|
||||
|
||||
public byte ReadWaveRam(byte offset)
|
||||
{
|
||||
throw new Exception("Channel type does have Wave RAM");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0992ef0e8260e124c9667aea4433025c
|
@ -0,0 +1,269 @@
|
||||
using System;
|
||||
|
||||
namespace Essgee.Emulation.Audio
|
||||
{
|
||||
public partial class DMGAudio
|
||||
{
|
||||
public class Square : IDMGAudioChannel
|
||||
{
|
||||
static readonly bool[,] dutyCycleTable = new bool[,]
|
||||
{
|
||||
{ false, false, false, false, false, false, false, true, }, // 00000001 12.5%
|
||||
{ true, false, false, false, false, false, false, true, }, // 10000001 25%
|
||||
{ true, false, false, false, false, true, true, true, }, // 10000111 50%
|
||||
{ false, true, true, true, true, true, true, false, } // 01111110 75%
|
||||
};
|
||||
|
||||
// NR10/20
|
||||
byte sweepPeriodReload, sweepShift;
|
||||
bool sweepNegate;
|
||||
|
||||
// NR11/21
|
||||
byte dutyCycle, lengthLoad;
|
||||
|
||||
// NR12/22
|
||||
byte envelopeStartingVolume, envelopePeriodReload;
|
||||
bool envelopeAddMode;
|
||||
|
||||
// NR13/23
|
||||
byte frequencyLSB;
|
||||
|
||||
// NR14/24
|
||||
bool trigger, lengthEnable;
|
||||
byte frequencyMSB;
|
||||
|
||||
//
|
||||
|
||||
readonly bool channelSupportsSweep;
|
||||
|
||||
// Sweep
|
||||
bool isSweepEnabled;
|
||||
int sweepCounter, sweepFreqShadow;
|
||||
|
||||
// Frequency
|
||||
int frequencyCounter;
|
||||
|
||||
// Envelope
|
||||
int volume, envelopeCounter;
|
||||
bool isEnvelopeUpdateEnabled;
|
||||
|
||||
// Misc
|
||||
bool isChannelEnabled, isDacEnabled;
|
||||
int lengthCounter, dutyCounter;
|
||||
|
||||
public int OutputVolume { get; private set; }
|
||||
|
||||
public bool IsActive { get { return lengthCounter != 0; } }
|
||||
|
||||
public Square(bool hasSweep)
|
||||
{
|
||||
channelSupportsSweep = hasSweep;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
isSweepEnabled = false;
|
||||
sweepCounter = sweepFreqShadow = 0;
|
||||
|
||||
frequencyCounter = 0;
|
||||
|
||||
volume = 15;
|
||||
envelopeCounter = 0;
|
||||
isEnvelopeUpdateEnabled = false;
|
||||
|
||||
isChannelEnabled = isDacEnabled = false;
|
||||
lengthCounter = dutyCounter = 0;
|
||||
|
||||
OutputVolume = volume;
|
||||
}
|
||||
|
||||
public void LengthCounterClock()
|
||||
{
|
||||
if (lengthCounter > 0 && lengthEnable)
|
||||
{
|
||||
lengthCounter--;
|
||||
if (lengthCounter == 0)
|
||||
isChannelEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void SweepClock()
|
||||
{
|
||||
if (!channelSupportsSweep) return;
|
||||
|
||||
sweepCounter--;
|
||||
if (sweepCounter == 0)
|
||||
{
|
||||
sweepCounter = sweepPeriodReload;
|
||||
|
||||
if (isSweepEnabled && sweepPeriodReload != 0)
|
||||
{
|
||||
var newFrequency = PerformSweepCalculations();
|
||||
if (newFrequency <= 2047 && sweepShift != 0)
|
||||
{
|
||||
sweepFreqShadow = newFrequency;
|
||||
frequencyMSB = (byte)((newFrequency >> 8) & 0b111);
|
||||
frequencyLSB = (byte)(newFrequency & 0xFF);
|
||||
PerformSweepCalculations();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void VolumeEnvelopeClock()
|
||||
{
|
||||
envelopeCounter--;
|
||||
if (envelopeCounter == 0)
|
||||
{
|
||||
envelopeCounter = envelopePeriodReload;
|
||||
|
||||
if (isEnvelopeUpdateEnabled)
|
||||
{
|
||||
var newVolume = volume;
|
||||
if (envelopeAddMode) newVolume++;
|
||||
else newVolume--;
|
||||
|
||||
if (newVolume >= 0 && newVolume <= 15)
|
||||
volume = newVolume;
|
||||
else
|
||||
isEnvelopeUpdateEnabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Step()
|
||||
{
|
||||
if (!isChannelEnabled) return;
|
||||
|
||||
frequencyCounter--;
|
||||
if (frequencyCounter == 0)
|
||||
{
|
||||
frequencyCounter = (2048 - ((frequencyMSB << 8) | frequencyLSB)) * 4;
|
||||
dutyCounter++;
|
||||
dutyCounter %= 8;
|
||||
}
|
||||
|
||||
OutputVolume = isDacEnabled && dutyCycleTable[dutyCycle, dutyCounter] ? volume : 0;
|
||||
}
|
||||
|
||||
private void Trigger()
|
||||
{
|
||||
isChannelEnabled = true;
|
||||
|
||||
if (lengthCounter == 0) lengthCounter = 64;
|
||||
|
||||
frequencyCounter = (2048 - ((frequencyMSB << 8) | frequencyLSB)) * 4;
|
||||
volume = envelopeStartingVolume;
|
||||
envelopeCounter = envelopePeriodReload;
|
||||
isEnvelopeUpdateEnabled = true;
|
||||
|
||||
if (channelSupportsSweep)
|
||||
{
|
||||
sweepFreqShadow = (frequencyMSB << 8) | frequencyLSB;
|
||||
sweepCounter = sweepPeriodReload;
|
||||
isSweepEnabled = sweepPeriodReload != 0 || sweepShift != 0;
|
||||
if (sweepShift != 0)
|
||||
PerformSweepCalculations();
|
||||
}
|
||||
}
|
||||
|
||||
private int PerformSweepCalculations()
|
||||
{
|
||||
var newFrequency = sweepFreqShadow >> sweepShift;
|
||||
if (sweepNegate) newFrequency = -newFrequency;
|
||||
newFrequency += sweepFreqShadow;
|
||||
if (newFrequency > 2047) isChannelEnabled = false;
|
||||
return newFrequency;
|
||||
}
|
||||
|
||||
public void WritePort(byte port, byte value)
|
||||
{
|
||||
switch (port)
|
||||
{
|
||||
case 0:
|
||||
if (channelSupportsSweep)
|
||||
{
|
||||
sweepPeriodReload = (byte)((value >> 4) & 0b111);
|
||||
sweepNegate = ((value >> 3) & 0b1) == 0b1;
|
||||
sweepShift = (byte)((value >> 0) & 0b111);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
dutyCycle = (byte)((value >> 6) & 0b11);
|
||||
lengthLoad = (byte)((value >> 0) & 0b111111);
|
||||
|
||||
lengthCounter = 64 - lengthLoad;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
envelopeStartingVolume = (byte)((value >> 4) & 0b1111);
|
||||
envelopeAddMode = ((value >> 3) & 0b1) == 0b1;
|
||||
envelopePeriodReload = (byte)((value >> 0) & 0b111);
|
||||
|
||||
isDacEnabled = ((value >> 3) & 0b11111) != 0;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
frequencyLSB = value;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
trigger = ((value >> 7) & 0b1) == 0b1;
|
||||
lengthEnable = ((value >> 6) & 0b1) == 0b1;
|
||||
frequencyMSB = (byte)((value >> 0) & 0b111);
|
||||
|
||||
if (trigger) Trigger();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public byte ReadPort(byte port)
|
||||
{
|
||||
switch (port)
|
||||
{
|
||||
case 0:
|
||||
if (channelSupportsSweep)
|
||||
{
|
||||
return (byte)(
|
||||
0x80 |
|
||||
(sweepPeriodReload << 4) |
|
||||
(sweepNegate ? (1 << 3) : 0) |
|
||||
(sweepShift << 0));
|
||||
}
|
||||
else
|
||||
return 0xFF;
|
||||
|
||||
case 1:
|
||||
return (byte)(
|
||||
0x3F |
|
||||
(dutyCycle << 6));
|
||||
|
||||
case 2:
|
||||
return (byte)(
|
||||
(envelopeStartingVolume << 4) |
|
||||
(envelopeAddMode ? (1 << 3) : 0) |
|
||||
(envelopePeriodReload << 0));
|
||||
|
||||
case 4:
|
||||
return (byte)(
|
||||
0xBF |
|
||||
(lengthEnable ? (1 << 6) : 0));
|
||||
|
||||
default:
|
||||
return 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteWaveRam(byte offset, byte value)
|
||||
{
|
||||
throw new Exception("Channel type does have Wave RAM");
|
||||
}
|
||||
|
||||
public byte ReadWaveRam(byte offset)
|
||||
{
|
||||
throw new Exception("Channel type does have Wave RAM");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eab50abebc5d5f341889e031ded6a571
|
@ -0,0 +1,186 @@
|
||||
using System;
|
||||
|
||||
namespace Essgee.Emulation.Audio
|
||||
{
|
||||
public partial class DMGAudio
|
||||
{
|
||||
public class Wave : IDMGAudioChannel
|
||||
{
|
||||
// NR30
|
||||
bool isDacEnabled;
|
||||
|
||||
// NR31
|
||||
byte lengthLoad;
|
||||
|
||||
// NR32
|
||||
byte volumeCode;
|
||||
|
||||
// NR33
|
||||
byte frequencyLSB;
|
||||
|
||||
// NR34
|
||||
bool trigger, lengthEnable;
|
||||
byte frequencyMSB;
|
||||
|
||||
// Wave
|
||||
protected byte[] sampleBuffer;
|
||||
int frequencyCounter, positionCounter, volume;
|
||||
|
||||
// Misc
|
||||
bool isChannelEnabled;
|
||||
int lengthCounter;
|
||||
|
||||
public int OutputVolume { get; private set; }
|
||||
|
||||
public bool IsActive { get { return isDacEnabled; } } // TODO: correct? lengthCounter check makes Zelda Oracle games hang
|
||||
|
||||
public Wave()
|
||||
{
|
||||
sampleBuffer = new byte[16];
|
||||
}
|
||||
|
||||
public virtual void Reset()
|
||||
{
|
||||
for (var i = 0; i < sampleBuffer.Length; i++) sampleBuffer[i] = (byte)EmuStandInfo.Random.Next(255);
|
||||
frequencyCounter = positionCounter = 0;
|
||||
volume = 15;
|
||||
|
||||
isChannelEnabled = isDacEnabled = false;
|
||||
lengthCounter = 0;
|
||||
|
||||
OutputVolume = volume;
|
||||
}
|
||||
|
||||
public void LengthCounterClock()
|
||||
{
|
||||
if (lengthCounter > 0 && lengthEnable)
|
||||
{
|
||||
lengthCounter--;
|
||||
if (lengthCounter == 0)
|
||||
isChannelEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void SweepClock()
|
||||
{
|
||||
throw new Exception("Channel type does not support sweep");
|
||||
}
|
||||
|
||||
public void VolumeEnvelopeClock()
|
||||
{
|
||||
throw new Exception("Channel type does not support envelope");
|
||||
}
|
||||
|
||||
public void Step()
|
||||
{
|
||||
if (!isChannelEnabled) return;
|
||||
|
||||
frequencyCounter--;
|
||||
if (frequencyCounter == 0)
|
||||
{
|
||||
frequencyCounter = (2048 - ((frequencyMSB << 8) | frequencyLSB)) * 2;
|
||||
positionCounter++;
|
||||
positionCounter %= 32;
|
||||
|
||||
var value = sampleBuffer[positionCounter / 2];
|
||||
if ((positionCounter & 0b1) == 0) value >>= 4;
|
||||
value &= 0b1111;
|
||||
|
||||
if (volumeCode != 0)
|
||||
volume = value >> (volumeCode - 1);
|
||||
else
|
||||
volume = 0;
|
||||
}
|
||||
|
||||
OutputVolume = isDacEnabled ? volume : 0;
|
||||
}
|
||||
|
||||
private void Trigger()
|
||||
{
|
||||
isChannelEnabled = true;
|
||||
|
||||
if (lengthCounter == 0) lengthCounter = 256;
|
||||
|
||||
frequencyCounter = (2048 - ((frequencyMSB << 8) | frequencyLSB)) * 2;
|
||||
positionCounter = 0;
|
||||
}
|
||||
|
||||
public void WritePort(byte port, byte value)
|
||||
{
|
||||
switch (port)
|
||||
{
|
||||
case 0:
|
||||
isDacEnabled = ((value >> 7) & 0b1) == 0b1;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
lengthLoad = value;
|
||||
|
||||
lengthCounter = 256 - lengthLoad;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
volumeCode = (byte)((value >> 5) & 0b11);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
frequencyLSB = value;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
trigger = ((value >> 7) & 0b1) == 0b1;
|
||||
lengthEnable = ((value >> 6) & 0b1) == 0b1;
|
||||
frequencyMSB = (byte)((value >> 0) & 0b111);
|
||||
|
||||
if (trigger) Trigger();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public byte ReadPort(byte port)
|
||||
{
|
||||
switch (port)
|
||||
{
|
||||
case 0:
|
||||
return (byte)(
|
||||
0x7F |
|
||||
(isDacEnabled ? (1 << 7) : 0));
|
||||
|
||||
case 1:
|
||||
return 0xFF;
|
||||
|
||||
case 2:
|
||||
return (byte)(
|
||||
0x9F |
|
||||
(volumeCode << 5));
|
||||
|
||||
case 4:
|
||||
return (byte)(
|
||||
0xBF |
|
||||
(lengthEnable ? (1 << 6) : 0));
|
||||
|
||||
default:
|
||||
return 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: more details on behavior on access w/ channel enabled
|
||||
|
||||
public void WriteWaveRam(byte offset, byte value)
|
||||
{
|
||||
if (!isDacEnabled)
|
||||
sampleBuffer[offset & (sampleBuffer.Length - 1)] = value;
|
||||
else
|
||||
sampleBuffer[positionCounter & (sampleBuffer.Length - 1)] = value;
|
||||
}
|
||||
|
||||
public byte ReadWaveRam(byte offset)
|
||||
{
|
||||
if (!isDacEnabled)
|
||||
return sampleBuffer[offset & (sampleBuffer.Length - 1)];
|
||||
else
|
||||
return 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a9f4769005789914785d05d63578c931
|
@ -0,0 +1,494 @@
|
||||
using Essgee.EventArguments;
|
||||
using Essgee.Exceptions;
|
||||
using Essgee.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Essgee.Emulation.Audio
|
||||
{
|
||||
public unsafe partial class DMGAudio : IAudio
|
||||
{
|
||||
// https://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware
|
||||
// http://emudev.de/gameboy-emulator/bleeding-ears-time-to-add-audio/
|
||||
// https://github.com/GhostSonic21/GhostBoy/blob/master/GhostBoy/APU.cpp
|
||||
|
||||
protected const int numChannels = 4;
|
||||
|
||||
protected const string channel1OptionName = "AudioEnableCh1Square";
|
||||
protected const string channel2OptionName = "AudioEnableCh2Square";
|
||||
protected const string channel3OptionName = "AudioEnableCh3Wave";
|
||||
protected const string channel4OptionName = "AudioEnableCh4Noise";
|
||||
|
||||
protected IDMGAudioChannel channel1, channel2, channel3, channel4;
|
||||
|
||||
// FF24 - NR50
|
||||
byte[] volumeRightLeft;
|
||||
bool[] vinEnableRightLeft;
|
||||
|
||||
// FF25 - NR51
|
||||
bool[] channel1Enable, channel2Enable, channel3Enable, channel4Enable;
|
||||
|
||||
// FF26 - NR52
|
||||
bool isSoundHwEnabled;
|
||||
|
||||
protected int frameSequencerReload, frameSequencerCounter, frameSequencer;
|
||||
|
||||
//protected List<short>[] channelSampleBuffer;
|
||||
#region //指针化 channelSampleBuffer
|
||||
static short[][] channelSampleBuffer_src;
|
||||
static GCHandle[] channelSampleBuffer_handle;
|
||||
public static short*[] channelSampleBuffer;
|
||||
public static int[] channelSampleBufferLength;
|
||||
public static int channelSampleBuffer_writePos;
|
||||
public static bool channelSampleBuffer_IsNull => channelSampleBuffer == null;
|
||||
public static void channelSampleBuffer_Init(int length1, int Lenght2)
|
||||
{
|
||||
if (channelSampleBuffer_src != null)
|
||||
{
|
||||
for (int i = 0; i < channelSampleBuffer_src.Length; i++)
|
||||
channelSampleBuffer_handle[i].ReleaseGCHandle();
|
||||
}
|
||||
|
||||
channelSampleBuffer_src = new short[length1][];
|
||||
channelSampleBuffer_handle = new GCHandle[length1];
|
||||
channelSampleBuffer = new short*[length1];
|
||||
channelSampleBuffer_writePos = 0;
|
||||
for (int i = 0; i < channelSampleBuffer_src.Length; i++)
|
||||
{
|
||||
channelSampleBuffer_src[i] = new short[Lenght2];
|
||||
channelSampleBuffer_src[i].GetObjectPtr(ref channelSampleBuffer_handle[i], ref channelSampleBuffer[i]);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
//protected List<short> mixedSampleBuffer;
|
||||
|
||||
#region //指针化 mixedSampleBuffer
|
||||
short[] mixedSampleBuffer_src;
|
||||
GCHandle mixedSampleBuffer_handle;
|
||||
public short* mixedSampleBuffer;
|
||||
public int mixedSampleBufferLength;
|
||||
public int mixedSampleBuffer_writePos;
|
||||
public bool mixedSampleBuffer_IsNull => mixedSampleBuffer == null;
|
||||
public short[] mixedSampleBuffer_set
|
||||
{
|
||||
set
|
||||
{
|
||||
mixedSampleBuffer_handle.ReleaseGCHandle();
|
||||
mixedSampleBuffer_src = value;
|
||||
mixedSampleBufferLength = value.Length;
|
||||
mixedSampleBuffer_writePos = 0;
|
||||
mixedSampleBuffer_src.GetObjectPtr(ref mixedSampleBuffer_handle, ref mixedSampleBuffer);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
public virtual event EventHandler<EnqueueSamplesEventArgs> EnqueueSamples;
|
||||
public virtual void OnEnqueueSamples(EnqueueSamplesEventArgs e) { EnqueueSamples?.Invoke(this, e); }
|
||||
|
||||
protected int sampleRate, numOutputChannels;
|
||||
|
||||
//
|
||||
|
||||
double clockRate, refreshRate;
|
||||
protected int samplesPerFrame, cyclesPerFrame, cyclesPerSample;
|
||||
[StateRequired]
|
||||
int sampleCycleCount, frameCycleCount;
|
||||
|
||||
protected bool channel1ForceEnable, channel2ForceEnable, channel3ForceEnable, channel4ForceEnable;
|
||||
|
||||
public (string Name, string Description)[] RuntimeOptions => new (string name, string description)[]
|
||||
{
|
||||
(channel1OptionName, "Channel 1 (Square)"),
|
||||
(channel2OptionName, "Channel 2 (Square)"),
|
||||
(channel3OptionName, "Channel 3 (Wave)"),
|
||||
(channel4OptionName, "Channel 4 (Noise)")
|
||||
};
|
||||
|
||||
public DMGAudio()
|
||||
{
|
||||
//channelSampleBuffer = new List<short>[numChannels];
|
||||
//for (int i = 0; i < numChannels; i++) channelSampleBuffer[i] = new List<short>();
|
||||
//mixedSampleBuffer = new List<short>();
|
||||
|
||||
//改为二维数组
|
||||
channelSampleBuffer_Init(numChannels, 1470);
|
||||
mixedSampleBuffer_set = new short[1470];
|
||||
|
||||
|
||||
channel1 = new Square(true);
|
||||
channel2 = new Square(false);
|
||||
channel3 = new Wave();
|
||||
channel4 = new Noise();
|
||||
|
||||
samplesPerFrame = cyclesPerFrame = cyclesPerSample = -1;
|
||||
|
||||
channel1ForceEnable = true;
|
||||
channel2ForceEnable = true;
|
||||
channel3ForceEnable = true;
|
||||
channel4ForceEnable = true;
|
||||
}
|
||||
|
||||
public object GetRuntimeOption(string name)
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case channel1OptionName: return channel1ForceEnable;
|
||||
case channel2OptionName: return channel2ForceEnable;
|
||||
case channel3OptionName: return channel3ForceEnable;
|
||||
case channel4OptionName: return channel4ForceEnable;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetRuntimeOption(string name, object value)
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case channel1OptionName: channel1ForceEnable = (bool)value; break;
|
||||
case channel2OptionName: channel2ForceEnable = (bool)value; break;
|
||||
case channel3OptionName: channel3ForceEnable = (bool)value; break;
|
||||
case channel4OptionName: channel4ForceEnable = (bool)value; break;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetSampleRate(int rate)
|
||||
{
|
||||
sampleRate = rate;
|
||||
ConfigureTimings();
|
||||
}
|
||||
|
||||
public void SetOutputChannels(int channels)
|
||||
{
|
||||
numOutputChannels = channels;
|
||||
ConfigureTimings();
|
||||
}
|
||||
|
||||
public void SetClockRate(double clock)
|
||||
{
|
||||
clockRate = clock;
|
||||
ConfigureTimings();
|
||||
}
|
||||
|
||||
public void SetRefreshRate(double refresh)
|
||||
{
|
||||
refreshRate = refresh;
|
||||
ConfigureTimings();
|
||||
}
|
||||
|
||||
private void ConfigureTimings()
|
||||
{
|
||||
samplesPerFrame = (int)(sampleRate / refreshRate);
|
||||
cyclesPerFrame = (int)Math.Round(clockRate / refreshRate);
|
||||
cyclesPerSample = (cyclesPerFrame / samplesPerFrame);
|
||||
|
||||
volumeRightLeft = new byte[numOutputChannels];
|
||||
vinEnableRightLeft = new bool[numOutputChannels];
|
||||
|
||||
channel1Enable = new bool[numOutputChannels];
|
||||
channel2Enable = new bool[numOutputChannels];
|
||||
channel3Enable = new bool[numOutputChannels];
|
||||
channel4Enable = new bool[numOutputChannels];
|
||||
|
||||
FlushSamples();
|
||||
}
|
||||
|
||||
public virtual void Startup()
|
||||
{
|
||||
Reset();
|
||||
|
||||
if (samplesPerFrame == -1) throw new EmulationException("GB PSG: Timings not configured, invalid samples per frame");
|
||||
if (cyclesPerFrame == -1) throw new EmulationException("GB PSG: Timings not configured, invalid cycles per frame");
|
||||
if (cyclesPerSample == -1) throw new EmulationException("GB PSG: Timings not configured, invalid cycles per sample");
|
||||
}
|
||||
|
||||
public virtual void Shutdown()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public virtual void Reset()
|
||||
{
|
||||
FlushSamples();
|
||||
|
||||
channel1.Reset();
|
||||
channel2.Reset();
|
||||
channel3.Reset();
|
||||
channel4.Reset();
|
||||
|
||||
for (var i = 0; i < numOutputChannels; i++)
|
||||
{
|
||||
volumeRightLeft[i] = 0;
|
||||
vinEnableRightLeft[i] = false;
|
||||
|
||||
channel1Enable[i] = false;
|
||||
channel2Enable[i] = false;
|
||||
channel3Enable[i] = false;
|
||||
channel4Enable[i] = false;
|
||||
}
|
||||
|
||||
frameSequencerReload = (int)(clockRate / 512);
|
||||
frameSequencerCounter = frameSequencerReload;
|
||||
frameSequencer = 0;
|
||||
|
||||
sampleCycleCount = frameCycleCount = 0;
|
||||
}
|
||||
|
||||
public void Step(int clockCyclesInStep)
|
||||
{
|
||||
if (!isSoundHwEnabled) return;
|
||||
|
||||
sampleCycleCount += clockCyclesInStep;
|
||||
frameCycleCount += clockCyclesInStep;
|
||||
|
||||
for (int i = 0; i < clockCyclesInStep; i++)
|
||||
{
|
||||
frameSequencerCounter--;
|
||||
if (frameSequencerCounter == 0)
|
||||
{
|
||||
frameSequencerCounter = frameSequencerReload;
|
||||
|
||||
switch (frameSequencer)
|
||||
{
|
||||
case 0:
|
||||
channel1.LengthCounterClock();
|
||||
channel2.LengthCounterClock();
|
||||
channel3.LengthCounterClock();
|
||||
channel4.LengthCounterClock();
|
||||
break;
|
||||
|
||||
case 1:
|
||||
break;
|
||||
|
||||
case 2:
|
||||
channel1.SweepClock();
|
||||
channel1.LengthCounterClock();
|
||||
channel2.LengthCounterClock();
|
||||
channel3.LengthCounterClock();
|
||||
channel4.LengthCounterClock();
|
||||
break;
|
||||
|
||||
case 3:
|
||||
break;
|
||||
|
||||
case 4:
|
||||
channel1.LengthCounterClock();
|
||||
channel2.LengthCounterClock();
|
||||
channel3.LengthCounterClock();
|
||||
channel4.LengthCounterClock();
|
||||
break;
|
||||
|
||||
case 5:
|
||||
break;
|
||||
|
||||
case 6:
|
||||
channel1.SweepClock();
|
||||
channel1.LengthCounterClock();
|
||||
channel2.LengthCounterClock();
|
||||
channel3.LengthCounterClock();
|
||||
channel4.LengthCounterClock();
|
||||
break;
|
||||
|
||||
case 7:
|
||||
channel1.VolumeEnvelopeClock();
|
||||
channel2.VolumeEnvelopeClock();
|
||||
channel4.VolumeEnvelopeClock();
|
||||
break;
|
||||
}
|
||||
|
||||
frameSequencer++;
|
||||
if (frameSequencer >= 8)
|
||||
frameSequencer = 0;
|
||||
}
|
||||
|
||||
channel1.Step();
|
||||
channel2.Step();
|
||||
channel3.Step();
|
||||
channel4.Step();
|
||||
}
|
||||
|
||||
if (sampleCycleCount >= cyclesPerSample)
|
||||
{
|
||||
GenerateSample();
|
||||
|
||||
sampleCycleCount -= cyclesPerSample;
|
||||
}
|
||||
|
||||
//if (mixedSampleBuffer.Count >= (samplesPerFrame * numOutputChannels))
|
||||
if (mixedSampleBuffer_writePos >= (samplesPerFrame * numOutputChannels))
|
||||
{
|
||||
//EnqueueSamplesEventArgs eventArgs = EnqueueSamplesEventArgs.Create(
|
||||
// numChannels,
|
||||
// channelSampleBuffer.Select(x => x.ToArray()).ToArray(),
|
||||
// new bool[] { !channel1ForceEnable, !channel2ForceEnable, !channel3ForceEnable, !channel4ForceEnable },
|
||||
// mixedSampleBuffer.ToArray());
|
||||
|
||||
EnqueueSamplesEventArgs eventArgs = EnqueueSamplesEventArgs.Create(
|
||||
numChannels,
|
||||
channelSampleBuffer,
|
||||
new bool[] { !channel1ForceEnable, !channel2ForceEnable, !channel3ForceEnable, !channel4ForceEnable },
|
||||
mixedSampleBuffer,
|
||||
mixedSampleBuffer_writePos);
|
||||
|
||||
OnEnqueueSamples(eventArgs);
|
||||
|
||||
FlushSamples();
|
||||
|
||||
eventArgs.Release();
|
||||
|
||||
}
|
||||
|
||||
if (frameCycleCount >= cyclesPerFrame)
|
||||
{
|
||||
frameCycleCount -= cyclesPerFrame;
|
||||
sampleCycleCount = frameCycleCount;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void GenerateSample()
|
||||
{
|
||||
for (int i = 0; i < numOutputChannels; i++)
|
||||
{
|
||||
/* Generate samples */
|
||||
var ch1 = (short)(((channel1Enable[i] ? channel1.OutputVolume : 0) * (volumeRightLeft[i] + 1)) << 8);
|
||||
var ch2 = (short)(((channel2Enable[i] ? channel2.OutputVolume : 0) * (volumeRightLeft[i] + 1)) << 8);
|
||||
var ch3 = (short)(((channel3Enable[i] ? channel3.OutputVolume : 0) * (volumeRightLeft[i] + 1)) << 8);
|
||||
var ch4 = (short)(((channel4Enable[i] ? channel4.OutputVolume : 0) * (volumeRightLeft[i] + 1)) << 8);
|
||||
|
||||
//废弃旧的数组方式
|
||||
//channelSampleBuffer[0].Add(ch1);
|
||||
//channelSampleBuffer[1].Add(ch2);
|
||||
//channelSampleBuffer[2].Add(ch3);
|
||||
//channelSampleBuffer[3].Add(ch4);
|
||||
|
||||
//二维指针下标
|
||||
channelSampleBuffer_writePos++;
|
||||
channelSampleBuffer[0][channelSampleBuffer_writePos] = ch1;
|
||||
channelSampleBuffer[1][channelSampleBuffer_writePos] = ch2;
|
||||
channelSampleBuffer[2][channelSampleBuffer_writePos] = ch3;
|
||||
channelSampleBuffer[3][channelSampleBuffer_writePos] = ch4;
|
||||
|
||||
/* Mix samples */
|
||||
var mixed = 0;
|
||||
if (channel1ForceEnable) mixed += ch1;
|
||||
if (channel2ForceEnable) mixed += ch2;
|
||||
if (channel3ForceEnable) mixed += ch3;
|
||||
if (channel4ForceEnable) mixed += ch4;
|
||||
mixed /= numChannels;
|
||||
|
||||
//废弃旧的方式
|
||||
//mixedSampleBuffer.Add((short)mixed);
|
||||
//指针下标
|
||||
mixedSampleBuffer_writePos++;
|
||||
mixedSampleBuffer[mixedSampleBuffer_writePos] = (short)mixed;
|
||||
}
|
||||
}
|
||||
|
||||
public void FlushSamples()
|
||||
{
|
||||
//for (int i = 0; i < numChannels; i++)
|
||||
// channelSampleBuffer[i].Clear();
|
||||
channelSampleBuffer_writePos = 0;
|
||||
|
||||
//mixedSampleBuffer.Clear();
|
||||
mixedSampleBuffer_writePos = 0;
|
||||
}
|
||||
|
||||
public virtual byte ReadPort(byte port)
|
||||
{
|
||||
// Channels
|
||||
if (port >= 0x10 && port <= 0x14)
|
||||
return channel1.ReadPort((byte)(port - 0x10));
|
||||
else if (port >= 0x15 && port <= 0x19)
|
||||
return channel2.ReadPort((byte)(port - 0x15));
|
||||
else if (port >= 0x1A && port <= 0x1E)
|
||||
return channel3.ReadPort((byte)(port - 0x1A));
|
||||
else if (port >= 0x1F && port <= 0x23)
|
||||
return channel4.ReadPort((byte)(port - 0x1F));
|
||||
|
||||
// Channel 3 Wave RAM
|
||||
else if (port >= 0x30 && port <= 0x3F)
|
||||
return channel3.ReadWaveRam((byte)(port - 0x30));
|
||||
|
||||
// Control ports
|
||||
else
|
||||
switch (port)
|
||||
{
|
||||
case 0x24:
|
||||
return (byte)(
|
||||
(vinEnableRightLeft[1] ? (1 << 7) : 0) |
|
||||
(volumeRightLeft[1] << 4) |
|
||||
(vinEnableRightLeft[0] ? (1 << 3) : 0) |
|
||||
(volumeRightLeft[0] << 0));
|
||||
|
||||
case 0x25:
|
||||
return (byte)(
|
||||
(channel4Enable[1] ? (1 << 7) : 0) |
|
||||
(channel3Enable[1] ? (1 << 6) : 0) |
|
||||
(channel2Enable[1] ? (1 << 5) : 0) |
|
||||
(channel1Enable[1] ? (1 << 4) : 0) |
|
||||
(channel4Enable[0] ? (1 << 3) : 0) |
|
||||
(channel3Enable[0] ? (1 << 2) : 0) |
|
||||
(channel2Enable[0] ? (1 << 1) : 0) |
|
||||
(channel1Enable[0] ? (1 << 0) : 0));
|
||||
|
||||
case 0x26:
|
||||
return (byte)(
|
||||
0x70 |
|
||||
(isSoundHwEnabled ? (1 << 7) : 0) |
|
||||
(channel4.IsActive ? (1 << 3) : 0) |
|
||||
(channel3.IsActive ? (1 << 2) : 0) |
|
||||
(channel2.IsActive ? (1 << 1) : 0) |
|
||||
(channel1.IsActive ? (1 << 0) : 0));
|
||||
|
||||
default:
|
||||
return 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void WritePort(byte port, byte value)
|
||||
{
|
||||
// Channels
|
||||
if (port >= 0x10 && port <= 0x14)
|
||||
channel1.WritePort((byte)(port - 0x10), value);
|
||||
else if (port >= 0x15 && port <= 0x19)
|
||||
channel2.WritePort((byte)(port - 0x15), value);
|
||||
else if (port >= 0x1A && port <= 0x1E)
|
||||
channel3.WritePort((byte)(port - 0x1A), value);
|
||||
else if (port >= 0x1F && port <= 0x23)
|
||||
channel4.WritePort((byte)(port - 0x1F), value);
|
||||
|
||||
// Channel 3 Wave RAM
|
||||
else if (port >= 0x30 && port <= 0x3F)
|
||||
channel3.WriteWaveRam((byte)(port - 0x30), value);
|
||||
|
||||
// Control ports
|
||||
else
|
||||
switch (port)
|
||||
{
|
||||
case 0x24:
|
||||
vinEnableRightLeft[1] = ((value >> 7) & 0b1) == 0b1;
|
||||
volumeRightLeft[1] = (byte)((value >> 4) & 0b111);
|
||||
vinEnableRightLeft[0] = ((value >> 3) & 0b1) == 0b1;
|
||||
volumeRightLeft[0] = (byte)((value >> 0) & 0b111);
|
||||
break;
|
||||
|
||||
case 0x25:
|
||||
channel4Enable[1] = ((value >> 7) & 0b1) == 0b1;
|
||||
channel3Enable[1] = ((value >> 6) & 0b1) == 0b1;
|
||||
channel2Enable[1] = ((value >> 5) & 0b1) == 0b1;
|
||||
channel1Enable[1] = ((value >> 4) & 0b1) == 0b1;
|
||||
channel4Enable[0] = ((value >> 3) & 0b1) == 0b1;
|
||||
channel3Enable[0] = ((value >> 2) & 0b1) == 0b1;
|
||||
channel2Enable[0] = ((value >> 1) & 0b1) == 0b1;
|
||||
channel1Enable[0] = ((value >> 0) & 0b1) == 0b1;
|
||||
break;
|
||||
|
||||
case 0x26:
|
||||
isSoundHwEnabled = ((value >> 7) & 0b1) == 0b1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b1912726afeeb8a48bd9e35bf55fe8e3
|
@ -0,0 +1,26 @@
|
||||
using Essgee.EventArguments;
|
||||
using System;
|
||||
|
||||
namespace Essgee.Emulation.Audio
|
||||
{
|
||||
interface IAudio
|
||||
{
|
||||
event EventHandler<EnqueueSamplesEventArgs> EnqueueSamples;
|
||||
void OnEnqueueSamples(EnqueueSamplesEventArgs e);
|
||||
|
||||
(string Name, string Description)[] RuntimeOptions { get; }
|
||||
|
||||
object GetRuntimeOption(string name);
|
||||
void SetRuntimeOption(string name, object value);
|
||||
|
||||
void Startup();
|
||||
void Shutdown();
|
||||
void Reset();
|
||||
void Step(int clockCyclesInStep);
|
||||
|
||||
void SetSampleRate(int rate);
|
||||
void SetOutputChannels(int channels);
|
||||
void SetClockRate(double clock);
|
||||
void SetRefreshRate(double refresh);
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c7d71d4c287c7024f88c3e76da01431f
|
@ -0,0 +1,19 @@
|
||||
namespace Essgee.Emulation.Audio
|
||||
{
|
||||
public interface IDMGAudioChannel
|
||||
{
|
||||
int OutputVolume { get; }
|
||||
bool IsActive { get; }
|
||||
|
||||
void Reset();
|
||||
void LengthCounterClock();
|
||||
void SweepClock();
|
||||
void VolumeEnvelopeClock();
|
||||
void Step();
|
||||
|
||||
void WritePort(byte port, byte value);
|
||||
byte ReadPort(byte port);
|
||||
void WriteWaveRam(byte offset, byte value);
|
||||
byte ReadWaveRam(byte offset);
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b23834762d9307449a8e4b923fb2688b
|
@ -0,0 +1,468 @@
|
||||
using Essgee.EventArguments;
|
||||
using Essgee.Exceptions;
|
||||
using Essgee.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using static Essgee.Emulation.Utilities;
|
||||
|
||||
namespace Essgee.Emulation.Audio
|
||||
{
|
||||
public unsafe class SN76489 : IAudio
|
||||
{
|
||||
/* http://www.smspower.org/Development/SN76489 */
|
||||
/* Differences in various system's PSGs: http://forums.nesdev.com/viewtopic.php?p=190216#p190216 */
|
||||
|
||||
protected const int numChannels = 4, numToneChannels = 3, noiseChannelIndex = 3;
|
||||
|
||||
protected const string channel1OptionName = "AudioEnableCh1Square";
|
||||
protected const string channel2OptionName = "AudioEnableCh2Square";
|
||||
protected const string channel3OptionName = "AudioEnableCh3Square";
|
||||
protected const string channel4OptionName = "AudioEnableCh4Noise";
|
||||
|
||||
/* Noise generation constants */
|
||||
protected virtual ushort noiseLfsrMask => 0x7FFF;
|
||||
protected virtual ushort noiseTappedBits => 0x0003; /* Bits 0 and 1 */
|
||||
protected virtual int noiseBitShift => 14;
|
||||
|
||||
/* Sample generation & event handling */
|
||||
//protected List<short>[] channelSampleBuffer;
|
||||
|
||||
|
||||
#region //指针化 channelSampleBuffer
|
||||
static short[][] channelSampleBuffer_src;
|
||||
static GCHandle[] channelSampleBuffer_handle;
|
||||
public static short*[] channelSampleBuffer;
|
||||
public static int[] channelSampleBufferLength;
|
||||
public static int channelSampleBuffer_writePos;
|
||||
public static bool channelSampleBuffer_IsNull => channelSampleBuffer == null;
|
||||
public static void channelSampleBuffer_Init(int length1, int Lenght2)
|
||||
{
|
||||
if (channelSampleBuffer_src != null)
|
||||
{
|
||||
for (int i = 0; i < channelSampleBuffer_src.Length; i++)
|
||||
channelSampleBuffer_handle[i].ReleaseGCHandle();
|
||||
}
|
||||
|
||||
channelSampleBuffer_src = new short[length1][];
|
||||
channelSampleBuffer_handle = new GCHandle[length1];
|
||||
channelSampleBuffer = new short*[length1];
|
||||
channelSampleBuffer_writePos = 0;
|
||||
for (int i = 0; i < channelSampleBuffer_src.Length; i++)
|
||||
{
|
||||
channelSampleBuffer_src[i] = new short[Lenght2];
|
||||
channelSampleBuffer_src[i].GetObjectPtr(ref channelSampleBuffer_handle[i], ref channelSampleBuffer[i]);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
//protected List<short> mixedSampleBuffer;
|
||||
|
||||
#region //指针化 mixedSampleBuffer
|
||||
short[] mixedSampleBuffer_src;
|
||||
GCHandle mixedSampleBuffer_handle;
|
||||
public short* mixedSampleBuffer;
|
||||
public int mixedSampleBufferLength;
|
||||
public int mixedSampleBuffer_writePos;
|
||||
public bool mixedSampleBuffer_IsNull => mixedSampleBuffer == null;
|
||||
public short[] mixedSampleBuffer_set
|
||||
{
|
||||
set
|
||||
{
|
||||
mixedSampleBuffer_handle.ReleaseGCHandle();
|
||||
mixedSampleBuffer_src = value;
|
||||
mixedSampleBufferLength = value.Length;
|
||||
mixedSampleBuffer_writePos = 0;
|
||||
mixedSampleBuffer_src.GetObjectPtr(ref mixedSampleBuffer_handle, ref mixedSampleBuffer);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
public virtual event EventHandler<EnqueueSamplesEventArgs> EnqueueSamples;
|
||||
public virtual void OnEnqueueSamples(EnqueueSamplesEventArgs e) { EnqueueSamples?.Invoke(this, e); }
|
||||
|
||||
/* Audio output variables */
|
||||
protected int sampleRate, numOutputChannels;
|
||||
|
||||
/* Channel registers */
|
||||
[StateRequired]
|
||||
protected ushort[] volumeRegisters; /* Channels 0-3: 4 bits */
|
||||
[StateRequired]
|
||||
protected ushort[] toneRegisters; /* Channels 0-2 (tone): 10 bits; channel 3 (noise): 3 bits */
|
||||
|
||||
/* Channel counters */
|
||||
[StateRequired]
|
||||
protected ushort[] channelCounters; /* 10-bit counters */
|
||||
[StateRequired]
|
||||
protected bool[] channelOutput;
|
||||
|
||||
/* Volume attenuation table */
|
||||
protected short[] volumeTable; /* 2dB change per volume register step */
|
||||
|
||||
/* Latched channel/type */
|
||||
[StateRequired]
|
||||
byte latchedChannel, latchedType;
|
||||
|
||||
/* Linear-feedback shift register, for noise generation */
|
||||
[StateRequired]
|
||||
protected ushort noiseLfsr; /* 15-bit */
|
||||
|
||||
/* Timing variables */
|
||||
double clockRate, refreshRate;
|
||||
int samplesPerFrame, cyclesPerFrame, cyclesPerSample;
|
||||
[StateRequired]
|
||||
int sampleCycleCount, frameCycleCount, dividerCount;
|
||||
|
||||
/* User-facing channel toggles */
|
||||
protected bool channel1ForceEnable, channel2ForceEnable, channel3ForceEnable, channel4ForceEnable;
|
||||
|
||||
public (string Name, string Description)[] RuntimeOptions => new (string name, string description)[]
|
||||
{
|
||||
(channel1OptionName, "Channel 1 (Square)"),
|
||||
(channel2OptionName, "Channel 2 (Square)"),
|
||||
(channel3OptionName, "Channel 3 (Square)"),
|
||||
(channel4OptionName, "Channel 4 (Noise)")
|
||||
};
|
||||
|
||||
public SN76489()
|
||||
{
|
||||
//channelSampleBuffer = new List<short>[numChannels];
|
||||
//for (int i = 0; i < numChannels; i++) channelSampleBuffer[i] = new List<short>();
|
||||
//mixedSampleBuffer = new List<short>();
|
||||
|
||||
//改为二维数组
|
||||
channelSampleBuffer_Init(numChannels, 1470);
|
||||
mixedSampleBuffer_set = new short[1470];
|
||||
|
||||
|
||||
volumeRegisters = new ushort[numChannels];
|
||||
toneRegisters = new ushort[numChannels];
|
||||
|
||||
channelCounters = new ushort[numChannels];
|
||||
channelOutput = new bool[numChannels];
|
||||
|
||||
volumeTable = new short[16];
|
||||
for (int i = 0; i < volumeTable.Length; i++)
|
||||
volumeTable[i] = (short)(short.MaxValue * Math.Pow(2.0, i * -2.0 / 6.0));
|
||||
volumeTable[15] = 0;
|
||||
|
||||
samplesPerFrame = cyclesPerFrame = cyclesPerSample = -1;
|
||||
|
||||
channel1ForceEnable = true;
|
||||
channel2ForceEnable = true;
|
||||
channel3ForceEnable = true;
|
||||
channel4ForceEnable = true;
|
||||
}
|
||||
|
||||
public object GetRuntimeOption(string name)
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case channel1OptionName: return channel1ForceEnable;
|
||||
case channel2OptionName: return channel2ForceEnable;
|
||||
case channel3OptionName: return channel3ForceEnable;
|
||||
case channel4OptionName: return channel4ForceEnable;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetRuntimeOption(string name, object value)
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case channel1OptionName: channel1ForceEnable = (bool)value; break;
|
||||
case channel2OptionName: channel2ForceEnable = (bool)value; break;
|
||||
case channel3OptionName: channel3ForceEnable = (bool)value; break;
|
||||
case channel4OptionName: channel4ForceEnable = (bool)value; break;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetSampleRate(int rate)
|
||||
{
|
||||
sampleRate = rate;
|
||||
ConfigureTimings();
|
||||
}
|
||||
|
||||
public void SetOutputChannels(int channels)
|
||||
{
|
||||
numOutputChannels = channels;
|
||||
ConfigureTimings();
|
||||
}
|
||||
|
||||
public void SetClockRate(double clock)
|
||||
{
|
||||
clockRate = clock;
|
||||
ConfigureTimings();
|
||||
}
|
||||
|
||||
public void SetRefreshRate(double refresh)
|
||||
{
|
||||
refreshRate = refresh;
|
||||
ConfigureTimings();
|
||||
}
|
||||
|
||||
private void ConfigureTimings()
|
||||
{
|
||||
samplesPerFrame = (int)(sampleRate / refreshRate);
|
||||
cyclesPerFrame = (int)(clockRate / refreshRate);
|
||||
cyclesPerSample = (cyclesPerFrame / samplesPerFrame);
|
||||
|
||||
FlushSamples();
|
||||
}
|
||||
|
||||
public virtual void Startup()
|
||||
{
|
||||
Reset();
|
||||
|
||||
if (samplesPerFrame == -1) throw new EmulationException("SN76489: Timings not configured, invalid samples per frame");
|
||||
if (cyclesPerFrame == -1) throw new EmulationException("SN76489: Timings not configured, invalid cycles per frame");
|
||||
if (cyclesPerSample == -1) throw new EmulationException("SN76489: Timings not configured, invalid cycles per sample");
|
||||
}
|
||||
|
||||
public virtual void Shutdown()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public virtual void Reset()
|
||||
{
|
||||
FlushSamples();
|
||||
|
||||
latchedChannel = latchedType = 0x00;
|
||||
noiseLfsr = 0x4000;
|
||||
|
||||
for (int i = 0; i < numChannels; i++)
|
||||
{
|
||||
volumeRegisters[i] = 0x000F;
|
||||
toneRegisters[i] = 0x0000;
|
||||
}
|
||||
|
||||
sampleCycleCount = frameCycleCount = dividerCount = 0;
|
||||
}
|
||||
|
||||
public void Step(int clockCyclesInStep)
|
||||
{
|
||||
sampleCycleCount += clockCyclesInStep;
|
||||
frameCycleCount += clockCyclesInStep;
|
||||
|
||||
for (int i = 0; i < clockCyclesInStep; i++)
|
||||
{
|
||||
dividerCount++;
|
||||
if (dividerCount == 16)
|
||||
{
|
||||
for (int ch = 0; ch < numToneChannels; ch++)
|
||||
StepToneChannel(ch);
|
||||
StepNoiseChannel();
|
||||
|
||||
dividerCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (sampleCycleCount >= cyclesPerSample)
|
||||
{
|
||||
GenerateSample();
|
||||
|
||||
sampleCycleCount -= cyclesPerSample;
|
||||
}
|
||||
|
||||
//if (mixedSampleBuffer.Count >= (samplesPerFrame * numOutputChannels))
|
||||
if (mixedSampleBuffer_writePos >= (samplesPerFrame * numOutputChannels))
|
||||
{
|
||||
//EnqueueSamplesEventArgs eventArgs = EnqueueSamplesEventArgs.Create(
|
||||
// numChannels,
|
||||
// channelSampleBuffer.Select(x => x.ToArray()).ToArray(),
|
||||
// new bool[] { !channel1ForceEnable, !channel2ForceEnable, !channel3ForceEnable, !channel4ForceEnable },
|
||||
// mixedSampleBuffer.ToArray());
|
||||
|
||||
EnqueueSamplesEventArgs eventArgs = EnqueueSamplesEventArgs.Create(
|
||||
numChannels,
|
||||
channelSampleBuffer,
|
||||
new bool[] { !channel1ForceEnable, !channel2ForceEnable, !channel3ForceEnable, !channel4ForceEnable },
|
||||
mixedSampleBuffer,
|
||||
mixedSampleBufferLength);
|
||||
|
||||
OnEnqueueSamples(eventArgs);
|
||||
|
||||
FlushSamples();
|
||||
|
||||
eventArgs.Release();
|
||||
|
||||
}
|
||||
|
||||
if (frameCycleCount >= cyclesPerFrame)
|
||||
{
|
||||
frameCycleCount -= cyclesPerFrame;
|
||||
sampleCycleCount = frameCycleCount;
|
||||
}
|
||||
}
|
||||
|
||||
private void StepToneChannel(int ch)
|
||||
{
|
||||
/* Check for counter underflow */
|
||||
if ((channelCounters[ch] & 0x03FF) > 0)
|
||||
channelCounters[ch]--;
|
||||
|
||||
/* Counter underflowed, reload and flip output bit, then generate sample */
|
||||
if ((channelCounters[ch] & 0x03FF) == 0)
|
||||
{
|
||||
channelCounters[ch] = (ushort)(toneRegisters[ch] & 0x03FF);
|
||||
channelOutput[ch] = !channelOutput[ch];
|
||||
}
|
||||
}
|
||||
|
||||
private void StepNoiseChannel()
|
||||
{
|
||||
int chN = noiseChannelIndex;
|
||||
{
|
||||
/* Check for counter underflow */
|
||||
if ((channelCounters[chN] & 0x03FF) > 0)
|
||||
channelCounters[chN]--;
|
||||
|
||||
/* Counter underflowed, reload and flip output bit */
|
||||
if ((channelCounters[chN] & 0x03FF) == 0)
|
||||
{
|
||||
switch (toneRegisters[chN] & 0x3)
|
||||
{
|
||||
case 0x0: channelCounters[chN] = 0x10; break;
|
||||
case 0x1: channelCounters[chN] = 0x20; break;
|
||||
case 0x2: channelCounters[chN] = 0x40; break;
|
||||
case 0x3: channelCounters[chN] = (ushort)(toneRegisters[2] & 0x03FF); break;
|
||||
}
|
||||
channelOutput[chN] = !channelOutput[chN];
|
||||
|
||||
if (channelOutput[chN])
|
||||
{
|
||||
/* Check noise type, then generate sample */
|
||||
bool isWhiteNoise = (((toneRegisters[chN] >> 2) & 0x1) == 0x1);
|
||||
|
||||
ushort newLfsrBit = (ushort)((isWhiteNoise ? CheckParity((ushort)(noiseLfsr & noiseTappedBits)) : (noiseLfsr & 0x01)) << noiseBitShift);
|
||||
|
||||
noiseLfsr = (ushort)((newLfsrBit | (noiseLfsr >> 1)) & noiseLfsrMask);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void GenerateSample()
|
||||
{
|
||||
for (int i = 0; i < numOutputChannels; i++)
|
||||
{
|
||||
/* Generate samples */
|
||||
var ch1 = (short)(volumeTable[volumeRegisters[0]] * ((toneRegisters[0] < 2 ? true : channelOutput[0]) ? 1.0 : 0.0));
|
||||
var ch2 = (short)(volumeTable[volumeRegisters[1]] * ((toneRegisters[1] < 2 ? true : channelOutput[1]) ? 1.0 : 0.0));
|
||||
var ch3 = (short)(volumeTable[volumeRegisters[2]] * ((toneRegisters[2] < 2 ? true : channelOutput[2]) ? 1.0 : 0.0));
|
||||
var ch4 = (short)(volumeTable[volumeRegisters[3]] * (noiseLfsr & 0x1));
|
||||
|
||||
//废弃旧的数组方式
|
||||
//channelSampleBuffer[0].Add(ch1);
|
||||
//channelSampleBuffer[1].Add(ch2);
|
||||
//channelSampleBuffer[2].Add(ch3);
|
||||
//channelSampleBuffer[3].Add(ch4);
|
||||
|
||||
//二维指针下标
|
||||
channelSampleBuffer_writePos++;
|
||||
channelSampleBuffer[0][channelSampleBuffer_writePos] = ch1;
|
||||
channelSampleBuffer[1][channelSampleBuffer_writePos] = ch2;
|
||||
channelSampleBuffer[2][channelSampleBuffer_writePos] = ch3;
|
||||
channelSampleBuffer[3][channelSampleBuffer_writePos] = ch4;
|
||||
|
||||
|
||||
/* Mix samples */
|
||||
var mixed = 0;
|
||||
if (channel1ForceEnable) mixed += ch1;
|
||||
if (channel2ForceEnable) mixed += ch2;
|
||||
if (channel3ForceEnable) mixed += ch3;
|
||||
if (channel4ForceEnable) mixed += ch4;
|
||||
mixed /= numChannels;
|
||||
|
||||
//废弃旧的方式
|
||||
//mixedSampleBuffer.Add((short)mixed);
|
||||
//指针下标
|
||||
mixedSampleBuffer_writePos++;
|
||||
mixedSampleBuffer[mixedSampleBuffer_writePos] = (short)mixed;
|
||||
}
|
||||
}
|
||||
|
||||
public void FlushSamples()
|
||||
{
|
||||
//for (int i = 0; i < numChannels; i++)
|
||||
// channelSampleBuffer[i].Clear();
|
||||
channelSampleBuffer_writePos = 0;
|
||||
|
||||
//mixedSampleBuffer.Clear();
|
||||
mixedSampleBuffer_writePos = 0;
|
||||
}
|
||||
|
||||
private ushort CheckParity(ushort val)
|
||||
{
|
||||
val ^= (ushort)(val >> 8);
|
||||
val ^= (ushort)(val >> 4);
|
||||
val ^= (ushort)(val >> 2);
|
||||
val ^= (ushort)(val >> 1);
|
||||
return (ushort)(val & 0x1);
|
||||
}
|
||||
|
||||
public virtual byte ReadPort(byte port)
|
||||
{
|
||||
throw new EmulationException("SN76489: Cannot read ports");
|
||||
}
|
||||
|
||||
public virtual void WritePort(byte port, byte data)
|
||||
{
|
||||
if (IsBitSet(data, 7))
|
||||
{
|
||||
/* LATCH/DATA byte; get channel (0-3) and type (0 is tone/noise, 1 is volume) */
|
||||
latchedChannel = (byte)((data >> 5) & 0x03);
|
||||
latchedType = (byte)((data >> 4) & 0x01);
|
||||
|
||||
/* Mask off non-data bits */
|
||||
data &= 0x0F;
|
||||
|
||||
/* If target is channel 3 noise (3 bits), mask off highest bit */
|
||||
if (latchedChannel == 3 && latchedType == 0)
|
||||
data &= 0x07;
|
||||
|
||||
/* Write to register */
|
||||
if (latchedType == 0)
|
||||
{
|
||||
/* Data is tone/noise */
|
||||
toneRegisters[latchedChannel] = (ushort)((toneRegisters[latchedChannel] & 0x03F0) | data);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Data is volume */
|
||||
volumeRegisters[latchedChannel] = data;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* DATA byte; mask off non-data bits */
|
||||
data &= 0x3F;
|
||||
|
||||
/* Write to register */
|
||||
if (latchedType == 0)
|
||||
{
|
||||
/* Data is tone/noise */
|
||||
if (latchedChannel == 3)
|
||||
{
|
||||
/* Target is channel 3 noise, mask off excess bits and write to low bits of register */
|
||||
toneRegisters[latchedChannel] = (ushort)(data & 0x07);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Target is not channel 3 noise, write to high bits of register */
|
||||
toneRegisters[latchedChannel] = (ushort)((toneRegisters[latchedChannel] & 0x000F) | (data << 4));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Data is volume; mask off excess bits and write to low bits of register */
|
||||
volumeRegisters[latchedChannel] = (ushort)(data & 0x0F);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5b081ff484b016a4faafebb32b5d6bd1
|
@ -0,0 +1,88 @@
|
||||
using Essgee.Utilities;
|
||||
|
||||
namespace Essgee.Emulation.Audio
|
||||
{
|
||||
public unsafe class SegaGGPSG : SegaSMSPSG
|
||||
{
|
||||
public const int PortStereoControl = 0x06;
|
||||
|
||||
[StateRequired]
|
||||
readonly bool[] channel0Enable, channel1Enable, channel2Enable, channel3Enable;
|
||||
|
||||
public SegaGGPSG() : base()
|
||||
{
|
||||
channel0Enable = new bool[2];
|
||||
channel1Enable = new bool[2];
|
||||
channel2Enable = new bool[2];
|
||||
channel3Enable = new bool[2];
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
|
||||
WritePort(PortStereoControl, 0xFF);
|
||||
}
|
||||
|
||||
protected override void GenerateSample()
|
||||
{
|
||||
for (int i = 0; i < numOutputChannels; i++)
|
||||
{
|
||||
/* Generate samples */
|
||||
var ch1 = (channel0Enable[i] ? (short)(volumeTable[volumeRegisters[0]] * ((toneRegisters[0] < 2 ? true : channelOutput[0]) ? 1.0 : 0.0)) : (short)0);
|
||||
var ch2 = (channel1Enable[i] ? (short)(volumeTable[volumeRegisters[1]] * ((toneRegisters[1] < 2 ? true : channelOutput[1]) ? 1.0 : 0.0)) : (short)0);
|
||||
var ch3 = (channel2Enable[i] ? (short)(volumeTable[volumeRegisters[2]] * ((toneRegisters[2] < 2 ? true : channelOutput[2]) ? 1.0 : 0.0)) : (short)0);
|
||||
var ch4 = (channel3Enable[i] ? (short)(volumeTable[volumeRegisters[3]] * (noiseLfsr & 0x1)) : (short)0);
|
||||
|
||||
|
||||
//废弃旧的数组方式
|
||||
//channelSampleBuffer[0].Add(ch1);
|
||||
//channelSampleBuffer[1].Add(ch2);
|
||||
//channelSampleBuffer[2].Add(ch3);
|
||||
//channelSampleBuffer[3].Add(ch4);
|
||||
|
||||
//二维指针下标
|
||||
channelSampleBuffer_writePos++;
|
||||
channelSampleBuffer[0][channelSampleBuffer_writePos] = ch1;
|
||||
channelSampleBuffer[1][channelSampleBuffer_writePos] = ch2;
|
||||
channelSampleBuffer[2][channelSampleBuffer_writePos] = ch3;
|
||||
channelSampleBuffer[3][channelSampleBuffer_writePos] = ch4;
|
||||
|
||||
/* Mix samples */
|
||||
var mixed = 0;
|
||||
if (channel1ForceEnable) mixed += ch1;
|
||||
if (channel2ForceEnable) mixed += ch2;
|
||||
if (channel3ForceEnable) mixed += ch3;
|
||||
if (channel4ForceEnable) mixed += ch4;
|
||||
mixed /= numChannels;
|
||||
|
||||
//废弃旧的方式
|
||||
//mixedSampleBuffer.Add((short)mixed);
|
||||
//指针下标
|
||||
mixedSampleBuffer_writePos++;
|
||||
mixedSampleBuffer[mixedSampleBuffer_writePos] = (short)mixed;
|
||||
}
|
||||
}
|
||||
|
||||
public override void WritePort(byte port, byte data)
|
||||
{
|
||||
if (port == PortStereoControl)
|
||||
{
|
||||
/* Stereo control */
|
||||
channel0Enable[0] = ((data & 0x10) != 0); /* Ch1 Left */
|
||||
channel0Enable[1] = ((data & 0x01) != 0); /* Ch1 Right */
|
||||
|
||||
channel1Enable[0] = ((data & 0x20) != 0); /* Ch2 Left */
|
||||
channel1Enable[1] = ((data & 0x02) != 0); /* Ch2 Right */
|
||||
|
||||
channel2Enable[0] = ((data & 0x40) != 0); /* Ch3 Left */
|
||||
channel2Enable[1] = ((data & 0x04) != 0); /* Ch3 Right */
|
||||
|
||||
channel3Enable[0] = ((data & 0x80) != 0); /* Ch4 Left */
|
||||
channel3Enable[1] = ((data & 0x08) != 0); /* Ch4 Right */
|
||||
}
|
||||
else
|
||||
base.WritePort(port, data);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 63b4d9dd3dcd81942aab4e783f7ca197
|
@ -0,0 +1,19 @@
|
||||
namespace Essgee.Emulation.Audio
|
||||
{
|
||||
public class SegaSMSPSG : SN76489
|
||||
{
|
||||
/* LFSR is 16 bits, tapped bits are 0 and 3 (mask 0x0009), going into bit 15 */
|
||||
protected override ushort noiseLfsrMask => 0xFFFF;
|
||||
protected override ushort noiseTappedBits => 0x0009;
|
||||
protected override int noiseBitShift => 15;
|
||||
|
||||
public SegaSMSPSG() : base() { }
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
|
||||
noiseLfsr = 0x8000;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bdf808e342a96d74fbbe4a6ea16509eb
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 11616634c9d44b541b8505293e437778
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,13 @@
|
||||
namespace Essgee.Emulation.CPU
|
||||
{
|
||||
interface ICPU
|
||||
{
|
||||
void Startup();
|
||||
void Shutdown();
|
||||
void Reset();
|
||||
int Step();
|
||||
|
||||
void SetStackPointer(ushort value);
|
||||
void SetProgramCounter(ushort value);
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d1eb29143cb5d8e49bb3ebe09e89a215
|
@ -0,0 +1,53 @@
|
||||
namespace Essgee.Emulation.CPU
|
||||
{
|
||||
public partial class SM83
|
||||
{
|
||||
public static class CycleCounts
|
||||
{
|
||||
public const int AdditionalJumpCond8Taken = 4;
|
||||
public const int AdditionalRetCondTaken = 12;
|
||||
public const int AdditionalCallCondTaken = 12;
|
||||
|
||||
// 32 cycles == dummy
|
||||
public static readonly int[] NoPrefix = new int[]
|
||||
{
|
||||
4, 12, 8, 8, 4, 4, 8, 4, 20, 8, 8, 8, 4, 4, 8, 4, /* 0x00 - 0x0F */
|
||||
4, 12, 8, 8, 4, 4, 8, 4, 12, 8, 8, 8, 4, 4, 8, 4, /* 0x10 - 0x1F */
|
||||
8, 12, 8, 8, 4, 4, 8, 4, 8, 8, 8, 8, 4, 4, 8, 4, /* 0x20 - 0x2F */
|
||||
8, 12, 8, 8, 12, 12, 12, 4, 8, 8, 8, 8, 4, 4, 8, 4, /* 0x30 - 0x3F */
|
||||
4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4, /* 0x40 - 0x4F */
|
||||
4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4, /* 0x50 - 0x5F */
|
||||
4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4, /* 0x60 - 0x6F */
|
||||
8, 8, 8, 8, 8, 8, 4, 8, 4, 4, 4, 4, 4, 4, 8, 4, /* 0x70 - 0x7F */
|
||||
4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4, /* 0x80 - 0x8F */
|
||||
4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4, /* 0x90 - 0x9F */
|
||||
4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4, /* 0xA0 - 0xAF */
|
||||
4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4, /* 0xB0 - 0xBF */
|
||||
8, 12, 12, 16, 12, 16, 8, 16, 8, 16, 12, 32, 12, 24, 8, 16, /* 0xC0 - 0xCF */
|
||||
8, 12, 12, 32, 12, 16, 8, 16, 8, 16, 12, 32, 12, 32, 8, 16, /* 0xD0 - 0xDF */
|
||||
12, 12, 8, 32, 32, 16, 8, 16, 16, 4, 16, 32, 32, 32, 8, 16, /* 0xE0 - 0xEF */
|
||||
12, 12, 8, 4, 32, 16, 8, 16, 12, 8, 16, 4, 32, 32, 8, 16 /* 0xF0 - 0xFF */
|
||||
};
|
||||
|
||||
public static readonly int[] PrefixCB = new int[]
|
||||
{
|
||||
8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 8, 8, 8, 16, 8, /* 0x00 - 0x0F */
|
||||
8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 8, 8, 8, 16, 8, /* 0x10 - 0x1F */
|
||||
8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 8, 8, 8, 16, 8, /* 0x20 - 0x2F */
|
||||
8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 8, 8, 8, 16, 8, /* 0x30 - 0x3F */
|
||||
8, 8, 8, 8, 8, 8, 12, 8, 8, 8, 8, 8, 8, 8, 12, 8, /* 0x40 - 0x4F */
|
||||
8, 8, 8, 8, 8, 8, 12, 8, 8, 8, 8, 8, 8, 8, 12, 8, /* 0x50 - 0x5F */
|
||||
8, 8, 8, 8, 8, 8, 12, 8, 8, 8, 8, 8, 8, 8, 12, 8, /* 0x60 - 0x6F */
|
||||
8, 8, 8, 8, 8, 8, 12, 8, 8, 8, 8, 8, 8, 8, 12, 8, /* 0x70 - 0x7F */
|
||||
8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 8, 8, 8, 16, 8, /* 0x80 - 0x8F */
|
||||
8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 8, 8, 8, 16, 8, /* 0x90 - 0x9F */
|
||||
8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 8, 8, 8, 16, 8, /* 0xA0 - 0xAF */
|
||||
8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 8, 8, 8, 16, 8, /* 0xB0 - 0xBF */
|
||||
8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 8, 8, 8, 16, 8, /* 0xC0 - 0xCF */
|
||||
8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 8, 8, 8, 16, 8, /* 0xD0 - 0xDF */
|
||||
8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 8, 8, 8, 16, 8, /* 0xE0 - 0xEF */
|
||||
8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 8, 8, 8, 16, 8 /* 0xF0 - 0xFF */
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e1d2dab87d28f044ca71114b277aee3a
|
@ -0,0 +1,201 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace Essgee.Emulation.CPU
|
||||
{
|
||||
public partial class SM83
|
||||
{
|
||||
static readonly string[] opcodeMnemonicNoPrefix =
|
||||
{
|
||||
/* +00 +01 +02 +03 +04 +05 +06 +07 */
|
||||
"NOP", "LD BC, 0x{0:X4}", "LD (BC), A", "INC BC", "INC B", "DEC B", "LD B, 0x{0:X2}", "RLCA", /* 0x00 */
|
||||
"LD (0x{0:X4}), SP", "ADD HL, BC", "LD A, (BC)", "DEC BC", "INC C", "DEC C", "LD C, 0x{0:X2}", "RRCA", /* 0x08 */
|
||||
"STOP", "LD DE, 0x{0:X4}", "LD (DE), A", "INC DE", "INC D", "DEC D", "LD D, 0x{0:X2}", "RLA", /* 0x10 */
|
||||
"JR 0x{0:X2}", "ADD HL, DE", "LD A, (DE)", "DEC DE", "INC E", "DEC E", "LD E, 0x{0:X2}", "RRA", /* 0x18 */
|
||||
"JR NZ, 0x{0:X2}", "LD HL, 0x{0:X4}", "LDI (HL), A", "INC HL", "INC H", "DEC H", "LD H, 0x{0:X2}", "DAA", /* 0x20 */
|
||||
"JR Z, 0x{0:X2}", "ADD HL, HL", "LDI A, (HL)", "DEC HL", "INC L", "DEC L", "LD L, 0x{0:X2}", "CPL", /* 0x28 */
|
||||
"JR NC, 0x{0:X2}", "LD SP, 0x{0:X4}", "LDD (HL), A", "INC SP", "INC (HL)", "DEC (HL)", "LD (HL), 0x{0:X2}", "SCF", /* 0x30 */
|
||||
"JR C, 0x{0:X2}", "ADD HL, SP", "LDD A, (HL)", "DEC SP", "INC A", "DEC A", "LD A, 0x{0:X2}", "CCF", /* 0x38 */
|
||||
"LD B, B", "LD B, C", "LD B, D", "LD B, E", "LD B, H", "LD B, L", "LD B, (HL)", "LD B, A", /* 0x40 */
|
||||
"LD C, B", "LD C, C", "LD C, D", "LD C, E", "LD C, H", "LD C, L", "LD C, (HL)", "LD C, A", /* 0x48 */
|
||||
"LD D, B", "LD D, C", "LD D, D", "LD D, E", "LD D, H", "LD D, L", "LD D, (HL)", "LD D, A", /* 0x50 */
|
||||
"LD E, B", "LD E, C", "LD E, D", "LD E, E", "LD E, H", "LD E, L", "LD E, (HL)", "LD E, A", /* 0x58 */
|
||||
"LD H, B", "LD H, C", "LD H, D", "LD H, E", "LD H, H", "LD H, L", "LD H, (HL)", "LD H, A", /* 0x60 */
|
||||
"LD L, B", "LD L, C", "LD L, D", "LD L, E", "LD L, H", "LD L, L", "LD L, (HL)", "LD L, A", /* 0x68 */
|
||||
"LD (HL), B", "LD (HL), C", "LD (HL), D", "LD (HL), E", "LD (HL), H", "LD (HL), L", "HALT", "LD (HL), A", /* 0x70 */
|
||||
"LD A, B", "LD A, C", "LD A, D", "LD A, E", "LD A, H", "LD A, L", "LD A, (HL)", "LD A, A", /* 0x78 */
|
||||
"ADD B", "ADD C", "ADD D", "ADD E", "ADD H", "ADD L", "ADD (HL)", "ADD A", /* 0x80 */
|
||||
"ADC B", "ADC C", "ADC D", "ADC E", "ADC H", "ADC L", "ADC (HL)", "ADC A", /* 0x88 */
|
||||
"SUB B", "SUB C", "SUB D", "SUB E", "SUB H", "SUB L", "SUB (HL)", "SUB A", /* 0x90 */
|
||||
"SBC B", "SBC C", "SBC D", "SBC E", "SBC H", "SBC L", "SBC (HL)", "SBC A", /* 0x98 */
|
||||
"AND B", "AND C", "AND D", "AND E", "AND H", "AND L", "AND (HL)", "AND A", /* 0xA0 */
|
||||
"XOR B", "XOR C", "XOR D", "XOR E", "XOR H", "XOR L", "XOR (HL)", "XOR A", /* 0xA8 */
|
||||
"OR B", "OR C", "OR D", "OR E", "OR H", "OR L", "OR (HL)", "OR A", /* 0xB0 */
|
||||
"CP B", "CP C", "CP D", "CP E", "CP H", "CP L", "CP (HL)", "CP A", /* 0xB8 */
|
||||
"RET NZ", "POP BC", "JP NZ, 0x{0:X4}", "JP 0x{0:X4}", "CALL NZ, 0x{0:X4}", "PUSH BC", "ADD 0x{0:X2}", "RST 00", /* 0xC0 */
|
||||
"RET Z", "RET", "JP Z, 0x{0:X4}", string.Empty, "CALL Z, 0x{0:X4}", "CALL 0x{0:X4}", "ADC 0x{0:X2}", "RST 08", /* 0xC8 */
|
||||
"RET NC", "POP DE", "JP NC, 0x{0:X4}", ".DB 0xD3", "CALL NC, 0x{0:X4}", "PUSH DE", "SUB 0x{0:X2}", "RST 10", /* 0xD0 */
|
||||
"RET C", "RETI", "JP C, 0x{0:X4}", ".DB 0xDB", "CALL C, 0x{0:X4}", ".DB 0xDD", "SBC 0x{0:X2}", "RST 18", /* 0xD8 */
|
||||
"LD (FF00+0x{0:X2}), A", "POP HL", "LD (FF00+C), A", ".DB 0xE3", ".DB 0xE4", "PUSH HL", "AND 0x{0:X2}", "RST 20", /* 0xE0 */
|
||||
"ADD SP, 0x{0:X2}", "LD PC, HL", "LD (0x{0:X4}), A", ".DB 0xEB", ".DB 0xEC", ".DB 0xED", "XOR 0x{0:X2}", "RST 28", /* 0xE8 */
|
||||
"LD A, (FF00+0x{0:X2})", "POP AF", "LD A, (FF00+C)", "DI", ".DB 0xF4", "PUSH AF", "OR 0x{0:X2}", "RST 30", /* 0xF0 */
|
||||
"LD HL, SP+0x{0:X2}", "LD SP, HL", "LD A, (0x{0:X4})", "EI", ".DB 0xFC", ".DB 0xFD", "CP 0x{0:X2}", "RST 38" /* 0xF8 */
|
||||
};
|
||||
|
||||
static readonly int[] opcodeLengthNoPrefix =
|
||||
{
|
||||
1, 3, 1, 1, 1, 1, 2, 1, 3, 1, 1, 1, 1, 1, 2, 1,
|
||||
2, 3, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 2, 1,
|
||||
2, 3, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 2, 1,
|
||||
2, 3, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 2, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 3, 3, 3, 1, 2, 1, 1, 1, 3, 2, 3, 3, 2, 1,
|
||||
1, 1, 3, 1, 3, 1, 2, 1, 1, 1, 3, 1, 3, 1, 2, 1,
|
||||
2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 1, 1, 2, 1,
|
||||
2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 1, 1, 2, 1
|
||||
};
|
||||
|
||||
static readonly string[] opcodeMnemonicPrefixCB = new string[]
|
||||
{
|
||||
/* +00 +01 +02 +03 +04 +05 +06 +07 */
|
||||
"RLC B", "RLC C", "RLC D", "RLC E", "RLC H", "RLC L", "RLC (HL)", "RLC A", /* 0x00 */
|
||||
"RRC B", "RRC C", "RRC D", "RRC E", "RRC H", "RRC L", "RRC (HL)", "RRC A", /* 0x08 */
|
||||
"RL B", "RL C", "RL D", "RL E", "RL H", "RL L", "RL (HL)", "RL A", /* 0x10 */
|
||||
"RR B", "RR C", "RR D", "RR E", "RR H", "RR L", "RR (HL)", "RR A", /* 0x18 */
|
||||
"SLA B", "SLA C", "SLA D", "SLA E", "SLA H", "SLA L", "SLA (HL)", "SLA A", /* 0x20 */
|
||||
"SRA B", "SRA C", "SRA D", "SRA E", "SRA H", "SRA L", "SRA (HL)", "SRA A", /* 0x28 */
|
||||
"SWAP B", "SWAP C", "SWAP D", "SWAP E", "SWAP H", "SWAP L", "SWAP (HL)", "SWAP A", /* 0x30 */
|
||||
"SRL B", "SRL C", "SRL D", "SRL E", "SRL H", "SRL L", "SRL (HL)", "SRL A", /* 0x38 */
|
||||
"BIT 0, B", "BIT 0, C", "BIT 0, D", "BIT 0, E", "BIT 0, H", "BIT 0, L", "BIT 0, (HL)", "BIT 0, A", /* 0x40 */
|
||||
"BIT 1, B", "BIT 1, C", "BIT 1, D", "BIT 1, E", "BIT 1, H", "BIT 1, L", "BIT 1, (HL)", "BIT 1, A", /* 0x48 */
|
||||
"BIT 2, B", "BIT 2, C", "BIT 2, D", "BIT 2, E", "BIT 2, H", "BIT 2, L", "BIT 2, (HL)", "BIT 2, A", /* 0x50 */
|
||||
"BIT 3, B", "BIT 3, C", "BIT 3, D", "BIT 3, E", "BIT 3, H", "BIT 3, L", "BIT 3, (HL)", "BIT 3, A", /* 0x58 */
|
||||
"BIT 4, B", "BIT 4, C", "BIT 4, D", "BIT 4, E", "BIT 4, H", "BIT 4, L", "BIT 4, (HL)", "BIT 4, A", /* 0x60 */
|
||||
"BIT 5, B", "BIT 5, C", "BIT 5, D", "BIT 5, E", "BIT 5, H", "BIT 5, L", "BIT 5, (HL)", "BIT 5, A", /* 0x68 */
|
||||
"BIT 6, B", "BIT 6, C", "BIT 6, D", "BIT 6, E", "BIT 6, H", "BIT 6, L", "BIT 6, (HL)", "BIT 6, A", /* 0x70 */
|
||||
"BIT 7, B", "BIT 7, C", "BIT 7, D", "BIT 7, E", "BIT 7, H", "BIT 7, L", "BIT 7, (HL)", "BIT 7, A", /* 0x78 */
|
||||
"RES 0, B", "RES 0, C", "RES 0, D", "RES 0, E", "RES 0, H", "RES 0, L", "RES 0, (HL)", "RES 0, A", /* 0x80 */
|
||||
"RES 1, B", "RES 1, C", "RES 1, D", "RES 1, E", "RES 1, H", "RES 1, L", "RES 1, (HL)", "RES 1, A", /* 0x88 */
|
||||
"RES 2, B", "RES 2, C", "RES 2, D", "RES 2, E", "RES 2, H", "RES 2, L", "RES 2, (HL)", "RES 2, A", /* 0x90 */
|
||||
"RES 3, B", "RES 3, C", "RES 3, D", "RES 3, E", "RES 3, H", "RES 3, L", "RES 3, (HL)", "RES 3, A", /* 0x98 */
|
||||
"RES 4, B", "RES 4, C", "RES 4, D", "RES 4, E", "RES 4, H", "RES 4, L", "RES 4, (HL)", "RES 4, A", /* 0xA0 */
|
||||
"RES 5, B", "RES 5, C", "RES 5, D", "RES 5, E", "RES 5, H", "RES 5, L", "RES 5, (HL)", "RES 5, A", /* 0xA8 */
|
||||
"RES 6, B", "RES 6, C", "RES 6, D", "RES 6, E", "RES 6, H", "RES 6, L", "RES 6, (HL)", "RES 6, A", /* 0xB0 */
|
||||
"RES 7, B", "RES 7, C", "RES 7, D", "RES 7, E", "RES 7, H", "RES 7, L", "RES 7, (HL)", "RES 7, A", /* 0xB8 */
|
||||
"SET 0, B", "SET 0, C", "SET 0, D", "SET 0, E", "SET 0, H", "SET 0, L", "SET 0, (HL)", "SET 0, A", /* 0xC0 */
|
||||
"SET 1, B", "SET 1, C", "SET 1, D", "SET 1, E", "SET 1, H", "SET 1, L", "SET 1, (HL)", "SET 1, A", /* 0xC8 */
|
||||
"SET 2, B", "SET 2, C", "SET 2, D", "SET 2, E", "SET 2, H", "SET 2, L", "SET 2, (HL)", "SET 2, A", /* 0xD0 */
|
||||
"SET 3, B", "SET 3, C", "SET 3, D", "SET 3, E", "SET 3, H", "SET 3, L", "SET 3, (HL)", "SET 3, A", /* 0xD8 */
|
||||
"SET 4, B", "SET 4, C", "SET 4, D", "SET 4, E", "SET 4, H", "SET 4, L", "SET 4, (HL)", "SET 4, A", /* 0xE0 */
|
||||
"SET 5, B", "SET 5, C", "SET 5, D", "SET 5, E", "SET 5, H", "SET 5, L", "SET 5, (HL)", "SET 5, A", /* 0xE8 */
|
||||
"SET 6, B", "SET 6, C", "SET 6, D", "SET 6, E", "SET 6, H", "SET 6, L", "SET 6, (HL)", "SET 6, A", /* 0xF0 */
|
||||
"SET 7, B", "SET 7, C", "SET 7, D", "SET 7, E", "SET 7, H", "SET 7, L", "SET 7, (HL)", "SET 7, A" /* 0xF8 */
|
||||
};
|
||||
|
||||
static readonly int[] opcodeLengthPrefixCB = new int[]
|
||||
{
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
};
|
||||
|
||||
public static string PrintRegisters(SM83 cpu)
|
||||
{
|
||||
return $"AF:{cpu.af.Word:X4} BC:{cpu.bc.Word:X4} DE:{cpu.de.Word:X4} HL:{cpu.hl.Word:X4} SP:{cpu.sp:X4}";
|
||||
}
|
||||
|
||||
public static string PrintFlags(SM83 cpu)
|
||||
{
|
||||
return $"{(cpu.IsFlagSet(Flags.Zero) ? "Z" : "-")}{(cpu.IsFlagSet(Flags.Subtract) ? "N" : "-")}{(cpu.IsFlagSet(Flags.HalfCarry) ? "H" : "-")}{(cpu.IsFlagSet(Flags.Carry) ? "C" : "-")}";
|
||||
}
|
||||
|
||||
public static string PrintInterrupt(SM83 cpu)
|
||||
{
|
||||
var intFlags = (InterruptSource)cpu.memoryReadDelegate(0xFF0F);
|
||||
var intEnable = (InterruptSource)cpu.memoryReadDelegate(0xFFFF);
|
||||
|
||||
var intFlagsString =
|
||||
$"{((intFlags & InterruptSource.VBlank) != 0 ? "V" : "-")}" +
|
||||
$"{((intFlags & InterruptSource.LCDCStatus) != 0 ? "L" : "-")}" +
|
||||
$"{((intFlags & InterruptSource.TimerOverflow) != 0 ? "T" : "-")}" +
|
||||
$"{((intFlags & InterruptSource.SerialIO) != 0 ? "S" : "-")}" +
|
||||
$"{((intFlags & InterruptSource.Keypad) != 0 ? "K" : "-")}";
|
||||
|
||||
var intEnableString =
|
||||
$"{((intEnable & InterruptSource.VBlank) != 0 ? "V" : "-")}" +
|
||||
$"{((intEnable & InterruptSource.LCDCStatus) != 0 ? "L" : "-")}" +
|
||||
$"{((intEnable & InterruptSource.TimerOverflow) != 0 ? "T" : "-")}" +
|
||||
$"{((intEnable & InterruptSource.SerialIO) != 0 ? "S" : "-")}" +
|
||||
$"{((intEnable & InterruptSource.Keypad) != 0 ? "K" : "-")}";
|
||||
|
||||
return $"IME:{(cpu.ime ? "1" : "0")} IF:{intFlagsString} IE:{intEnableString}";
|
||||
}
|
||||
|
||||
public static string DisassembleOpcode(SM83 cpu, ushort address)
|
||||
{
|
||||
var opcode = DisassembleGetOpcodeBytes(cpu, address);
|
||||
return $"0x{address:X4} | {DisassembleMakeByteString(cpu, opcode).PadRight(15)} | {DisassembleMakeMnemonicString(cpu, opcode)}";
|
||||
}
|
||||
|
||||
public static byte[] DisassembleGetOpcodeBytes(SM83 cpu, ushort address)
|
||||
{
|
||||
var opcode = new byte[3];
|
||||
for (int i = 0; i < opcode.Length; i++)
|
||||
opcode[i] = address + i <= 0xFFFF ? cpu.memoryReadDelegate((ushort)(address + i)) : (byte)0;
|
||||
return opcode;
|
||||
}
|
||||
|
||||
public static int DisassembleGetOpcodeLen(SM83 cpu, byte[] opcode)
|
||||
{
|
||||
if (opcode[0] == 0xCB)
|
||||
return opcodeLengthPrefixCB[opcode[1]];
|
||||
else
|
||||
return opcodeLengthNoPrefix[opcode[0]];
|
||||
}
|
||||
|
||||
public static string DisassembleMakeByteString(SM83 cpu, byte[] opcode)
|
||||
{
|
||||
return string.Join(" ", opcode.Select(x => $"{x:X2}").Take(DisassembleGetOpcodeLen(cpu, opcode)));
|
||||
}
|
||||
|
||||
public static string DisassembleMakeMnemonicString(SM83 cpu, byte[] opcode)
|
||||
{
|
||||
var len = DisassembleGetOpcodeLen(cpu, opcode);
|
||||
var start = opcode[0] == 0xCB ? 1 : 0;
|
||||
var mnemonics = opcode[0] == 0xCB ? opcodeMnemonicPrefixCB : opcodeMnemonicNoPrefix;
|
||||
|
||||
switch (len - start)
|
||||
{
|
||||
case 1: return mnemonics[opcode[start]];
|
||||
case 2: return string.Format(mnemonics[opcode[start]], opcode[start + 1]);
|
||||
case 3: return string.Format(mnemonics[opcode[start]], (opcode[start + 2] << 8 | opcode[start + 1]));
|
||||
default: return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private string MakeUnimplementedOpcodeString(string prefix, ushort address)
|
||||
{
|
||||
var opcode = DisassembleGetOpcodeBytes(this, address);
|
||||
return $"Unimplemented {(prefix != string.Empty ? prefix + " " : prefix)}opcode {DisassembleMakeByteString(this, opcode)} ({DisassembleMakeMnemonicString(this, opcode)})";
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7de4aca2c5f71b94b9509b8ad7b6ef8f
|
@ -0,0 +1,281 @@
|
||||
namespace Essgee.Emulation.CPU
|
||||
{
|
||||
public partial class SM83
|
||||
{
|
||||
static SimpleOpcodeDelegate[] opcodesNoPrefix = new SimpleOpcodeDelegate[]
|
||||
{
|
||||
/* 0x00 */
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP */ }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegisterImmediate16(ref c.bc.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadMemory8(c.bc.Word, c.af.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Increment16(ref c.bc.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Increment8(ref c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Decrement8(ref c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegisterImmediate8(ref c.bc.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeftAccumulatorCircular(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.WriteMemory16(c.ReadMemory16(c.pc), c.sp); c.pc += 2; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add16(ref c.hl, c.bc.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegisterFromMemory8(ref c.af.High, c.bc.Word, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Decrement16(ref c.bc.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Increment8(ref c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Decrement8(ref c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegisterImmediate8(ref c.bc.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRightAccumulatorCircular(); }),
|
||||
/* 0x10 */
|
||||
new SimpleOpcodeDelegate((c) => { c.Stop(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegisterImmediate16(ref c.de.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadMemory8(c.de.Word, c.af.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Increment16(ref c.de.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Increment8(ref c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Decrement8(ref c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegisterImmediate8(ref c.de.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeftAccumulator(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Jump8(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add16(ref c.hl, c.de.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegisterFromMemory8(ref c.af.High, c.de.Word, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Decrement16(ref c.de.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Increment8(ref c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Decrement8(ref c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegisterImmediate8(ref c.de.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRightAccumulator(); }),
|
||||
/* 0x20 */
|
||||
new SimpleOpcodeDelegate((c) => { c.JumpConditional8(!c.IsFlagSet(Flags.Zero)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegisterImmediate16(ref c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.WriteMemory8(c.hl.Word++, c.af.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Increment16(ref c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Increment8(ref c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Decrement8(ref c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegisterImmediate8(ref c.hl.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.DecimalAdjustAccumulator(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.JumpConditional8(c.IsFlagSet(Flags.Zero)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add16(ref c.hl, c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.af.High = c.ReadMemory8(c.hl.Word++); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Decrement16(ref c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Increment8(ref c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Decrement8(ref c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegisterImmediate8(ref c.hl.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.af.High ^= 0xFF; c.SetFlag(Flags.Subtract | Flags.HalfCarry); }),
|
||||
/* 0x30 */
|
||||
new SimpleOpcodeDelegate((c) => { c.JumpConditional8(!c.IsFlagSet(Flags.Carry)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegisterImmediate16(ref c.sp); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.WriteMemory8(c.hl.Word--, c.af.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Increment16(ref c.sp); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.IncrementMemory8(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.DecrementMemory8(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadMemory8(c.hl.Word, c.ReadMemory8(c.pc++)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetFlag(Flags.Carry); c.ClearFlag(Flags.Subtract | Flags.HalfCarry); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.JumpConditional8(c.IsFlagSet(Flags.Carry)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add16(ref c.hl, c.sp); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.af.High = c.ReadMemory8(c.hl.Word--);}),
|
||||
new SimpleOpcodeDelegate((c) => { c.Decrement16(ref c.sp); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Increment8(ref c.af.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Decrement8(ref c.af.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegisterImmediate8(ref c.af.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ClearFlag(Flags.Subtract); c.ClearFlag(Flags.HalfCarry); c.SetClearFlagConditional(Flags.Carry, !c.IsFlagSet(Flags.Carry)); }),
|
||||
/* 0x40 */
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.bc.High, c.bc.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.bc.High, c.bc.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.bc.High, c.de.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.bc.High, c.de.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.bc.High, c.hl.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.bc.High, c.hl.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.bc.High = c.ReadMemory8(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.bc.High, c.af.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.bc.Low, c.bc.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.bc.Low, c.bc.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.bc.Low, c.de.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.bc.Low, c.de.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.bc.Low, c.hl.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.bc.Low, c.hl.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.bc.Low = c.ReadMemory8(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.bc.Low, c.af.High, false); }),
|
||||
/* 0x50 */
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.de.High, c.bc.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.de.High, c.bc.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.de.High, c.de.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.de.High, c.de.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.de.High, c.hl.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.de.High, c.hl.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.de.High = c.ReadMemory8(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.de.High, c.af.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.de.Low, c.bc.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.de.Low, c.bc.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.de.Low, c.de.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.de.Low, c.de.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.de.Low, c.hl.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.de.Low, c.hl.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.de.Low = c.ReadMemory8(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.de.Low, c.af.High, false); }),
|
||||
/* 0x60 */
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.hl.High, c.bc.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.hl.High, c.bc.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.hl.High, c.de.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.hl.High, c.de.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.hl.High, c.hl.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.hl.High, c.hl.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.hl.High = c.ReadMemory8(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.hl.High, c.af.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.hl.Low, c.bc.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.hl.Low, c.bc.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.hl.Low, c.de.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.hl.Low, c.de.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.hl.Low, c.hl.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.hl.Low, c.hl.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.hl.Low = c.ReadMemory8(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.hl.Low, c.af.High, false); }),
|
||||
/* 0x70 */
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadMemory8(c.hl.Word, c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadMemory8(c.hl.Word, c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadMemory8(c.hl.Word, c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadMemory8(c.hl.Word, c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadMemory8(c.hl.Word, c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadMemory8(c.hl.Word, c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.EnterHaltState(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadMemory8(c.hl.Word, c.af.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.af.High, c.bc.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.af.High, c.bc.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.af.High, c.de.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.af.High, c.de.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.af.High, c.hl.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.af.High, c.hl.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.af.High = c.ReadMemory8(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.af.High, c.af.High, false); }),
|
||||
/* 0x80 */
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.bc.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.bc.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.de.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.de.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.hl.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.hl.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.ReadMemory8(c.hl.Word), false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.af.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.bc.High, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.bc.Low, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.de.High, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.de.Low, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.hl.High, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.hl.Low, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.ReadMemory8(c.hl.Word), true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.af.High, true); }),
|
||||
/* 0x90 */
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.bc.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.bc.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.de.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.de.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.hl.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.hl.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.ReadMemory8(c.hl.Word), false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.af.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.bc.High, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.bc.Low, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.de.High, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.de.Low, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.hl.High, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.hl.Low, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.ReadMemory8(c.hl.Word), true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.af.High, true); }),
|
||||
/* 0xA0 */
|
||||
new SimpleOpcodeDelegate((c) => { c.And8(c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.And8(c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.And8(c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.And8(c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.And8(c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.And8(c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.And8(c.ReadMemory8(c.hl.Word)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.And8(c.af.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Xor8(c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Xor8(c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Xor8(c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Xor8(c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Xor8(c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Xor8(c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Xor8(c.ReadMemory8(c.hl.Word)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Xor8(c.af.High); }),
|
||||
/* 0xB0 */
|
||||
new SimpleOpcodeDelegate((c) => { c.Or8(c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Or8(c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Or8(c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Or8(c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Or8(c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Or8(c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Or8(c.ReadMemory8(c.hl.Word)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Or8(c.af.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Cp8(c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Cp8(c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Cp8(c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Cp8(c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Cp8(c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Cp8(c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Cp8(c.ReadMemory8(c.hl.Word)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Cp8(c.af.High); }),
|
||||
/* 0xC0 */
|
||||
new SimpleOpcodeDelegate((c) => { c.ReturnConditional(!c.IsFlagSet(Flags.Zero)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Pop(ref c.bc); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.JumpConditional16(!c.IsFlagSet(Flags.Zero)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.JumpConditional16(true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.CallConditional16(!c.IsFlagSet(Flags.Zero)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Push(c.bc); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.ReadMemory8(c.pc++), false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Restart(0x0000); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ReturnConditional(c.IsFlagSet(Flags.Zero)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Return(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.JumpConditional16(c.IsFlagSet(Flags.Zero)); }),
|
||||
new SimpleOpcodeDelegate((c) => { /* CB - handled elsewhere */ }),
|
||||
new SimpleOpcodeDelegate((c) => { c.CallConditional16(c.IsFlagSet(Flags.Zero)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Call16(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.ReadMemory8(c.pc++), true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Restart(0x0008); }),
|
||||
/* 0xD0 */
|
||||
new SimpleOpcodeDelegate((c) => { c.ReturnConditional(!c.IsFlagSet(Flags.Carry)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Pop(ref c.de); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.JumpConditional16(!c.IsFlagSet(Flags.Carry)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.pc--; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.CallConditional16(!c.IsFlagSet(Flags.Carry)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Push(c.de); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.ReadMemory8(c.pc++), false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Restart(0x0010); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ReturnConditional(c.IsFlagSet(Flags.Carry)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ime = true; c.imeDelay = false; c.Return(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.JumpConditional16(c.IsFlagSet(Flags.Carry)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.pc--; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.CallConditional16(c.IsFlagSet(Flags.Carry)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.pc--; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.ReadMemory8(c.pc++), true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Restart(0x0018); }),
|
||||
/* 0xE0 */
|
||||
new SimpleOpcodeDelegate((c) => { c.WriteMemory8((ushort)(0xFF00 + c.ReadMemory8(c.pc++)), c.af.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Pop(ref c.hl); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.WriteMemory8((ushort)(0xFF00 + c.bc.Low), c.af.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.pc--; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.pc--; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Push(c.hl); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.And8(c.ReadMemory8(c.pc++)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Restart(0x0020); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.AddSPNN(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.pc = c.hl.Word; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.WriteMemory8(c.ReadMemory16(c.pc), c.af.High); c.pc += 2; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.pc--; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.pc--; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.pc--; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Xor8(c.ReadMemory8(c.pc++)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Restart(0x0028); }),
|
||||
/* 0xF0 */
|
||||
new SimpleOpcodeDelegate((c) => { c.af.High = c.ReadMemory8((ushort)(0xFF00 + c.ReadMemory8(c.pc++))); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.PopAF(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.af.High = c.ReadMemory8((ushort)(0xFF00 + c.bc.Low)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.DisableInterrupts(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.pc--; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Push(c.af); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Or8(c.ReadMemory8(c.pc++)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Restart(0x0030); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadHLSPNN(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.sp = c.hl.Word; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.af.High = c.ReadMemory8(c.ReadMemory16(c.pc)); c.pc += 2; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.EnableInterrupts(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.pc--; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.pc--; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Cp8(c.ReadMemory8(c.pc++)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Restart(0x0038); })
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e4fe2d8ddc2d10849b6c7c6d64db0d4f
|
@ -0,0 +1,281 @@
|
||||
namespace Essgee.Emulation.CPU
|
||||
{
|
||||
public partial class SM83
|
||||
{
|
||||
static SimpleOpcodeDelegate[] opcodesPrefixCB = new SimpleOpcodeDelegate[]
|
||||
{
|
||||
/* 0x00 */
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeftCircular(ref c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeftCircular(ref c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeftCircular(ref c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeftCircular(ref c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeftCircular(ref c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeftCircular(ref c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeftCircular(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeftCircular(ref c.af.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRightCircular(ref c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRightCircular(ref c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRightCircular(ref c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRightCircular(ref c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRightCircular(ref c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRightCircular(ref c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRightCircular(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRightCircular(ref c.af.High); }),
|
||||
/* 0x10 */
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeft(ref c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeft(ref c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeft(ref c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeft(ref c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeft(ref c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeft(ref c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeft(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeft(ref c.af.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRight(ref c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRight(ref c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRight(ref c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRight(ref c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRight(ref c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRight(ref c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRight(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRight(ref c.af.High); }),
|
||||
/* 0x20 */
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftLeftArithmetic(ref c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftLeftArithmetic(ref c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftLeftArithmetic(ref c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftLeftArithmetic(ref c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftLeftArithmetic(ref c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftLeftArithmetic(ref c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftLeftArithmetic(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftLeftArithmetic(ref c.af.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightArithmetic(ref c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightArithmetic(ref c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightArithmetic(ref c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightArithmetic(ref c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightArithmetic(ref c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightArithmetic(ref c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightArithmetic(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightArithmetic(ref c.af.High); }),
|
||||
/* 0x30 */
|
||||
new SimpleOpcodeDelegate((c) => { c.Swap(ref c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Swap(ref c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Swap(ref c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Swap(ref c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Swap(ref c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Swap(ref c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Swap(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Swap(ref c.af.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightLogical(ref c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightLogical(ref c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightLogical(ref c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightLogical(ref c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightLogical(ref c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightLogical(ref c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightLogical(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightLogical(ref c.af.High); }),
|
||||
/* 0x40 */
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.High, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.Low, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.High, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.Low, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.High, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Low, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Word, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.af.High, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.High, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.Low, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.High, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.Low, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.High, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Low, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Word, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.af.High, 1); }),
|
||||
/* 0x50 */
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.High, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.Low, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.High, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.Low, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.High, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Low, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Word, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.af.High, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.High, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.Low, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.High, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.Low, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.High, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Low, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Word, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.af.High, 3); }),
|
||||
/* 0x60 */
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.High, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.Low, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.High, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.Low, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.High, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Low, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Word, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.af.High, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.High, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.Low, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.High, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.Low, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.High, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Low, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Word, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.af.High, 5); }),
|
||||
/* 0x70 */
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.High, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.Low, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.High, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.Low, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.High, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Low, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Word, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.af.High, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.High, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.Low, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.High, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.Low, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.High, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Low, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Word, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.af.High, 7); }),
|
||||
/* 0x80 */
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.High, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.Low, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.High, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.Low, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.High, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.Low, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(c.hl.Word, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.af.High, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.High, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.Low, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.High, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.Low, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.High, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.Low, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(c.hl.Word, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.af.High, 1); }),
|
||||
/* 0x90 */
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.High, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.Low, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.High, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.Low, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.High, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.Low, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(c.hl.Word, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.af.High, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.High, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.Low, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.High, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.Low, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.High, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.Low, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(c.hl.Word, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.af.High, 3); }),
|
||||
/* 0xA0 */
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.High, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.Low, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.High, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.Low, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.High, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.Low, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(c.hl.Word, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.af.High, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.High, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.Low, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.High, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.Low, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.High, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.Low, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(c.hl.Word, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.af.High, 5); }),
|
||||
/* 0xB0 */
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.High, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.Low, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.High, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.Low, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.High, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.Low, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(c.hl.Word, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.af.High, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.High, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.Low, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.High, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.Low, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.High, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.Low, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(c.hl.Word, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.af.High, 7); }),
|
||||
/* 0xC0 */
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.High, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.Low, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.High, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.Low, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.High, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.Low, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(c.hl.Word, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.af.High, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.High, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.Low, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.High, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.Low, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.High, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.Low, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(c.hl.Word, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.af.High, 1); }),
|
||||
/* 0xD0 */
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.High, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.Low, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.High, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.Low, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.High, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.Low, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(c.hl.Word, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.af.High, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.High, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.Low, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.High, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.Low, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.High, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.Low, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(c.hl.Word, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.af.High, 3); }),
|
||||
/* 0xE0 */
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.High, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.Low, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.High, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.Low, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.High, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.Low, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(c.hl.Word, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.af.High, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.High, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.Low, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.High, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.Low, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.High, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.Low, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(c.hl.Word, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.af.High, 5); }),
|
||||
/* 0xF0 */
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.High, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.Low, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.High, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.Low, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.High, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.Low, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(c.hl.Word, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.af.High, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.High, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.Low, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.High, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.Low, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.High, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.Low, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(c.hl.Word, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.af.High, 7); }),
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 36c6024a407a43f43bf41f1e38274ad9
|
@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Essgee.Emulation.CPU
|
||||
{
|
||||
public partial class SM83
|
||||
{
|
||||
[DebuggerDisplay("{Word}")]
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
[Serializable]
|
||||
public struct Register
|
||||
{
|
||||
[NonSerialized]
|
||||
[FieldOffset(0)]
|
||||
public byte Low;
|
||||
[NonSerialized]
|
||||
[FieldOffset(1)]
|
||||
public byte High;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public ushort Word;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e29886b06189649459149576fcb83444
|
@ -0,0 +1,972 @@
|
||||
using Essgee.Exceptions;
|
||||
using Essgee.Utilities;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using static Essgee.Emulation.Utilities;
|
||||
|
||||
namespace Essgee.Emulation.CPU
|
||||
{
|
||||
public partial class SM83 : ICPU
|
||||
{
|
||||
[Flags]
|
||||
enum Flags : byte
|
||||
{
|
||||
UnusedBit0 = (1 << 0), /* (0) */
|
||||
UnusedBit1 = (1 << 1), /* (0) */
|
||||
UnusedBit2 = (1 << 2), /* (0) */
|
||||
UnusedBit3 = (1 << 3), /* (0) */
|
||||
Carry = (1 << 4), /* C */
|
||||
HalfCarry = (1 << 5), /* H */
|
||||
Subtract = (1 << 6), /* N */
|
||||
Zero = (1 << 7) /* Z */
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum InterruptSource : byte
|
||||
{
|
||||
VBlank = 0,
|
||||
LCDCStatus = 1,
|
||||
TimerOverflow = 2,
|
||||
SerialIO = 3,
|
||||
Keypad = 4
|
||||
}
|
||||
|
||||
public delegate byte MemoryReadDelegate(ushort address);
|
||||
public delegate void MemoryWriteDelegate(ushort address, byte value);
|
||||
|
||||
public delegate void RequestInterruptDelegate(InterruptSource source);
|
||||
|
||||
delegate void SimpleOpcodeDelegate(SM83 c);
|
||||
|
||||
protected MemoryReadDelegate memoryReadDelegate;
|
||||
protected MemoryWriteDelegate memoryWriteDelegate;
|
||||
|
||||
[StateRequired]
|
||||
protected Register af, bc, de, hl;
|
||||
[StateRequired]
|
||||
protected ushort sp, pc;
|
||||
|
||||
[StateRequired]
|
||||
protected bool ime, imeDelay, halt, doHaltBug;
|
||||
|
||||
[StateRequired]
|
||||
protected byte op;
|
||||
|
||||
[StateRequired]
|
||||
protected int currentCycles;
|
||||
|
||||
string logFile;
|
||||
int numLogEntries;
|
||||
string[] logEntries;
|
||||
|
||||
public SM83(MemoryReadDelegate memoryRead, MemoryWriteDelegate memoryWrite)
|
||||
{
|
||||
af = bc = de = hl = new Register();
|
||||
|
||||
memoryReadDelegate = memoryRead;
|
||||
memoryWriteDelegate = memoryWrite;
|
||||
|
||||
if (AppEnvironment.EnableSuperSlowCPULogger)
|
||||
{
|
||||
logFile = @"D:\Temp\Essgee\log-lr35902.txt";
|
||||
numLogEntries = 0;
|
||||
logEntries = new string[2000];
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Startup()
|
||||
{
|
||||
Reset();
|
||||
|
||||
if (memoryReadDelegate == null) throw new EmulationException("SM83: Memory read method is null");
|
||||
if (memoryWriteDelegate == null) throw new EmulationException("SM83: Memory write method is null");
|
||||
}
|
||||
|
||||
public virtual void Shutdown()
|
||||
{
|
||||
if (AppEnvironment.EnableSuperSlowCPULogger && logEntries != null)
|
||||
{
|
||||
System.IO.File.AppendAllText(logFile, string.Join("", logEntries.Take(numLogEntries)));
|
||||
}
|
||||
|
||||
//
|
||||
}
|
||||
|
||||
public virtual void Reset()
|
||||
{
|
||||
af.Word = bc.Word = de.Word = hl.Word = 0;
|
||||
pc = 0;
|
||||
sp = 0;
|
||||
|
||||
ime = imeDelay = halt = doHaltBug = false;
|
||||
|
||||
currentCycles = 0;
|
||||
}
|
||||
|
||||
public int Step()
|
||||
{
|
||||
currentCycles = 0;
|
||||
|
||||
/* Handle delayed interrupt enable */
|
||||
if (imeDelay)
|
||||
{
|
||||
ime = true;
|
||||
imeDelay = false;
|
||||
}
|
||||
|
||||
/* Check interrupts */
|
||||
HandleInterrupts();
|
||||
|
||||
if (halt)
|
||||
{
|
||||
/* CPU halted */
|
||||
currentCycles = 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AppEnvironment.EnableSuperSlowCPULogger && logEntries != null)
|
||||
{
|
||||
string disasm = string.Format("{0} | {1} | {2} | {3}\n", DisassembleOpcode(this, pc).PadRight(48), PrintRegisters(this), PrintFlags(this), PrintInterrupt(this));
|
||||
logEntries[numLogEntries++] = disasm;
|
||||
if (numLogEntries >= logEntries.Length)
|
||||
{
|
||||
System.IO.File.AppendAllText(logFile, string.Join("", logEntries));
|
||||
numLogEntries = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Do HALT bug */
|
||||
if (doHaltBug)
|
||||
{
|
||||
pc--;
|
||||
doHaltBug = false;
|
||||
}
|
||||
|
||||
/* Fetch and execute opcode */
|
||||
op = ReadMemory8(pc++);
|
||||
switch (op)
|
||||
{
|
||||
case 0xCB: ExecuteOpCB(); break;
|
||||
default: ExecuteOpcodeNoPrefix(op); break;
|
||||
}
|
||||
}
|
||||
|
||||
return currentCycles;
|
||||
}
|
||||
|
||||
#region Opcode Execution and Cycle Management
|
||||
|
||||
private void ExecuteOpcodeNoPrefix(byte op)
|
||||
{
|
||||
opcodesNoPrefix[op](this);
|
||||
currentCycles += CycleCounts.NoPrefix[op];
|
||||
}
|
||||
|
||||
private void ExecuteOpCB()
|
||||
{
|
||||
byte cbOp = ReadMemory8(pc++);
|
||||
opcodesPrefixCB[cbOp](this);
|
||||
currentCycles += CycleCounts.PrefixCB[cbOp];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers (Flags, etc.)
|
||||
|
||||
public void SetStackPointer(ushort value)
|
||||
{
|
||||
sp = value;
|
||||
}
|
||||
|
||||
public void SetProgramCounter(ushort value)
|
||||
{
|
||||
pc = value;
|
||||
}
|
||||
|
||||
public void SetRegisterAF(ushort value)
|
||||
{
|
||||
af.Word = value;
|
||||
}
|
||||
|
||||
public void SetRegisterBC(ushort value)
|
||||
{
|
||||
bc.Word = value;
|
||||
}
|
||||
|
||||
public void SetRegisterDE(ushort value)
|
||||
{
|
||||
de.Word = value;
|
||||
}
|
||||
|
||||
public void SetRegisterHL(ushort value)
|
||||
{
|
||||
hl.Word = value;
|
||||
}
|
||||
|
||||
private void SetFlag(Flags flags)
|
||||
{
|
||||
af.Low |= (byte)flags;
|
||||
}
|
||||
|
||||
private void ClearFlag(Flags flags)
|
||||
{
|
||||
af.Low &= (byte)~flags;
|
||||
}
|
||||
|
||||
private void SetClearFlagConditional(Flags flags, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
af.Low |= (byte)flags;
|
||||
else
|
||||
af.Low &= (byte)~flags;
|
||||
}
|
||||
|
||||
private bool IsFlagSet(Flags flags)
|
||||
{
|
||||
return (((Flags)af.Low & flags) == flags);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Interrupt, Halt and Stop State Handling
|
||||
|
||||
public void RequestInterrupt(InterruptSource source)
|
||||
{
|
||||
memoryWriteDelegate(0xFF0F, (byte)(memoryReadDelegate(0xFF0F) | (byte)(1 << (byte)source)));
|
||||
}
|
||||
|
||||
private void HandleInterrupts()
|
||||
{
|
||||
var intEnable = memoryReadDelegate(0xFFFF);
|
||||
var intFlags = memoryReadDelegate(0xFF0F);
|
||||
|
||||
if ((intEnable & intFlags) != 0)
|
||||
{
|
||||
LeaveHaltState();
|
||||
|
||||
if (ime)
|
||||
{
|
||||
if (ServiceInterrupt(InterruptSource.VBlank, intEnable, intFlags)) return;
|
||||
if (ServiceInterrupt(InterruptSource.LCDCStatus, intEnable, intFlags)) return;
|
||||
if (ServiceInterrupt(InterruptSource.TimerOverflow, intEnable, intFlags)) return;
|
||||
if (ServiceInterrupt(InterruptSource.SerialIO, intEnable, intFlags)) return;
|
||||
if (ServiceInterrupt(InterruptSource.Keypad, intEnable, intFlags)) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool ServiceInterrupt(InterruptSource intSource, byte intEnable, byte intFlags)
|
||||
{
|
||||
var intSourceBit = (byte)(1 << (byte)intSource);
|
||||
|
||||
if (((intEnable & intSourceBit) == intSourceBit) && ((intFlags & intSourceBit) == intSourceBit))
|
||||
{
|
||||
ime = false;
|
||||
|
||||
currentCycles += 20;
|
||||
|
||||
return RestartFromInterrupt(intSource);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected virtual void EnterHaltState()
|
||||
{
|
||||
if (ime)
|
||||
{
|
||||
halt = true;
|
||||
pc--;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((memoryReadDelegate(0xFF0F) & memoryReadDelegate(0xFFFF) & 0x1F) != 0)
|
||||
doHaltBug = true;
|
||||
else
|
||||
halt = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void LeaveHaltState()
|
||||
{
|
||||
if (halt)
|
||||
{
|
||||
halt = false;
|
||||
if (ime)
|
||||
pc++;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Memory Access Functions
|
||||
|
||||
private byte ReadMemory8(ushort address)
|
||||
{
|
||||
return memoryReadDelegate(address);
|
||||
}
|
||||
|
||||
private void WriteMemory8(ushort address, byte value)
|
||||
{
|
||||
memoryWriteDelegate(address, value);
|
||||
}
|
||||
|
||||
private ushort ReadMemory16(ushort address)
|
||||
{
|
||||
return (ushort)((memoryReadDelegate((ushort)(address + 1)) << 8) | memoryReadDelegate(address));
|
||||
}
|
||||
|
||||
private void WriteMemory16(ushort address, ushort value)
|
||||
{
|
||||
memoryWriteDelegate(address, (byte)(value & 0xFF));
|
||||
memoryWriteDelegate((ushort)(address + 1), (byte)(value >> 8));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Opcodes: 8-Bit Load Group
|
||||
|
||||
protected void LoadRegisterFromMemory8(ref byte register, ushort address, bool specialRegs)
|
||||
{
|
||||
LoadRegister8(ref register, ReadMemory8(address), specialRegs);
|
||||
}
|
||||
|
||||
protected void LoadRegisterImmediate8(ref byte register, bool specialRegs)
|
||||
{
|
||||
LoadRegister8(ref register, ReadMemory8(pc++), specialRegs);
|
||||
}
|
||||
|
||||
protected void LoadRegister8(ref byte register, byte value, bool specialRegs)
|
||||
{
|
||||
register = value;
|
||||
}
|
||||
|
||||
protected void LoadMemory8(ushort address, byte value)
|
||||
{
|
||||
WriteMemory8(address, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Opcodes: 16-Bit Load Group
|
||||
|
||||
protected void LoadRegisterImmediate16(ref ushort register)
|
||||
{
|
||||
LoadRegister16(ref register, ReadMemory16(pc));
|
||||
pc += 2;
|
||||
}
|
||||
|
||||
protected void LoadRegister16(ref ushort register, ushort value)
|
||||
{
|
||||
register = value;
|
||||
}
|
||||
|
||||
protected void Push(Register register)
|
||||
{
|
||||
WriteMemory8(--sp, register.High);
|
||||
WriteMemory8(--sp, register.Low);
|
||||
}
|
||||
|
||||
protected void Pop(ref Register register)
|
||||
{
|
||||
register.Low = ReadMemory8(sp++);
|
||||
register.High = ReadMemory8(sp++);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Opcodes: 8-Bit Arithmetic Group
|
||||
|
||||
protected void Add8(byte operand, bool withCarry)
|
||||
{
|
||||
int operandWithCarry = (operand + (withCarry && IsFlagSet(Flags.Carry) ? 1 : 0));
|
||||
int result = (af.High + operandWithCarry);
|
||||
|
||||
SetClearFlagConditional(Flags.Zero, ((result & 0xFF) == 0x00));
|
||||
ClearFlag(Flags.Subtract);
|
||||
SetClearFlagConditional(Flags.HalfCarry, (((af.High ^ result ^ operand) & 0x10) != 0));
|
||||
SetClearFlagConditional(Flags.Carry, (result > 0xFF));
|
||||
|
||||
af.High = (byte)result;
|
||||
}
|
||||
|
||||
protected void Subtract8(byte operand, bool withCarry)
|
||||
{
|
||||
int operandWithCarry = (operand + (withCarry && IsFlagSet(Flags.Carry) ? 1 : 0));
|
||||
int result = (af.High - operandWithCarry);
|
||||
|
||||
SetClearFlagConditional(Flags.Zero, ((result & 0xFF) == 0x00));
|
||||
SetFlag(Flags.Subtract);
|
||||
SetClearFlagConditional(Flags.HalfCarry, (((af.High ^ result ^ operand) & 0x10) != 0));
|
||||
SetClearFlagConditional(Flags.Carry, (af.High < operandWithCarry));
|
||||
|
||||
af.High = (byte)result;
|
||||
}
|
||||
|
||||
protected void And8(byte operand)
|
||||
{
|
||||
int result = (af.High & operand);
|
||||
|
||||
SetClearFlagConditional(Flags.Zero, ((result & 0xFF) == 0x00));
|
||||
ClearFlag(Flags.Subtract);
|
||||
SetFlag(Flags.HalfCarry);
|
||||
ClearFlag(Flags.Carry);
|
||||
|
||||
af.High = (byte)result;
|
||||
}
|
||||
|
||||
protected void Or8(byte operand)
|
||||
{
|
||||
int result = (af.High | operand);
|
||||
|
||||
SetClearFlagConditional(Flags.Zero, ((result & 0xFF) == 0x00));
|
||||
ClearFlag(Flags.Subtract);
|
||||
ClearFlag(Flags.HalfCarry);
|
||||
ClearFlag(Flags.Carry);
|
||||
|
||||
af.High = (byte)result;
|
||||
}
|
||||
|
||||
protected void Xor8(byte operand)
|
||||
{
|
||||
int result = (af.High ^ operand);
|
||||
|
||||
SetClearFlagConditional(Flags.Zero, ((result & 0xFF) == 0x00));
|
||||
ClearFlag(Flags.Subtract);
|
||||
ClearFlag(Flags.HalfCarry);
|
||||
ClearFlag(Flags.Carry);
|
||||
|
||||
af.High = (byte)result;
|
||||
}
|
||||
|
||||
protected void Cp8(byte operand)
|
||||
{
|
||||
int result = (af.High - operand);
|
||||
|
||||
SetClearFlagConditional(Flags.Zero, ((result & 0xFF) == 0x00));
|
||||
SetFlag(Flags.Subtract);
|
||||
SetClearFlagConditional(Flags.HalfCarry, (((af.High ^ result ^ operand) & 0x10) != 0));
|
||||
SetClearFlagConditional(Flags.Carry, (af.High < operand));
|
||||
}
|
||||
|
||||
protected void Increment8(ref byte register)
|
||||
{
|
||||
byte result = (byte)(register + 1);
|
||||
|
||||
SetClearFlagConditional(Flags.Zero, (result == 0x00));
|
||||
ClearFlag(Flags.Subtract);
|
||||
SetClearFlagConditional(Flags.HalfCarry, ((register & 0x0F) == 0x0F));
|
||||
// C
|
||||
|
||||
register = result;
|
||||
}
|
||||
|
||||
protected void IncrementMemory8(ushort address)
|
||||
{
|
||||
byte value = ReadMemory8(address);
|
||||
Increment8(ref value);
|
||||
WriteMemory8(address, value);
|
||||
}
|
||||
|
||||
protected void Decrement8(ref byte register)
|
||||
{
|
||||
byte result = (byte)(register - 1);
|
||||
|
||||
SetClearFlagConditional(Flags.Zero, (result == 0x00));
|
||||
SetFlag(Flags.Subtract);
|
||||
SetClearFlagConditional(Flags.HalfCarry, ((register & 0x0F) == 0x00));
|
||||
// C
|
||||
|
||||
register = result;
|
||||
}
|
||||
|
||||
protected void DecrementMemory8(ushort address)
|
||||
{
|
||||
byte value = ReadMemory8(address);
|
||||
Decrement8(ref value);
|
||||
WriteMemory8(address, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Opcodes: General-Purpose Arithmetic and CPU Control Group
|
||||
|
||||
protected void DecimalAdjustAccumulator()
|
||||
{
|
||||
int value = af.High;
|
||||
|
||||
if (!IsFlagSet(Flags.Subtract))
|
||||
{
|
||||
if (IsFlagSet(Flags.HalfCarry) || ((value & 0x0F) > 9))
|
||||
value += 0x06;
|
||||
if (IsFlagSet(Flags.Carry) || (value > 0x9F))
|
||||
value += 0x60;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsFlagSet(Flags.HalfCarry))
|
||||
value = (value - 0x06) & 0xFF;
|
||||
if (IsFlagSet(Flags.Carry))
|
||||
value -= 0x60;
|
||||
}
|
||||
|
||||
ClearFlag(Flags.HalfCarry);
|
||||
ClearFlag(Flags.Zero);
|
||||
|
||||
if ((value & 0x100) != 0) SetFlag(Flags.Carry);
|
||||
|
||||
value &= 0xFF;
|
||||
|
||||
if (value == 0) SetFlag(Flags.Zero);
|
||||
|
||||
af.High = (byte)value;
|
||||
}
|
||||
|
||||
protected void Negate()
|
||||
{
|
||||
int result = (0 - af.High);
|
||||
|
||||
SetClearFlagConditional(Flags.Zero, ((result & 0xFF) == 0x00));
|
||||
SetFlag(Flags.Subtract);
|
||||
SetClearFlagConditional(Flags.HalfCarry, ((0 - (af.High & 0x0F)) < 0));
|
||||
SetClearFlagConditional(Flags.Carry, (af.High != 0x00));
|
||||
|
||||
af.High = (byte)result;
|
||||
}
|
||||
|
||||
protected void EnableInterrupts()
|
||||
{
|
||||
ime = false;
|
||||
imeDelay = true;
|
||||
}
|
||||
|
||||
protected void DisableInterrupts()
|
||||
{
|
||||
ime = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Opcodes: 16-Bit Arithmetic Group
|
||||
|
||||
protected void Add16(ref Register dest, ushort operand)
|
||||
{
|
||||
int operandWithCarry = (short)operand;
|
||||
int result = (dest.Word + operandWithCarry);
|
||||
|
||||
// Z
|
||||
ClearFlag(Flags.Subtract);
|
||||
SetClearFlagConditional(Flags.HalfCarry, (((dest.Word & 0x0FFF) + (operandWithCarry & 0x0FFF)) > 0x0FFF));
|
||||
SetClearFlagConditional(Flags.Carry, (((dest.Word & 0xFFFF) + (operandWithCarry & 0xFFFF)) > 0xFFFF));
|
||||
|
||||
dest.Word = (ushort)result;
|
||||
}
|
||||
|
||||
protected void Increment16(ref ushort register)
|
||||
{
|
||||
register++;
|
||||
}
|
||||
|
||||
protected void Decrement16(ref ushort register)
|
||||
{
|
||||
register--;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Opcodes: Rotate and Shift Group
|
||||
|
||||
protected byte RotateLeft(ushort address)
|
||||
{
|
||||
byte value = ReadMemory8(address);
|
||||
RotateLeft(ref value);
|
||||
WriteMemory8(address, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
protected void RotateLeft(ref byte value)
|
||||
{
|
||||
bool isCarrySet = IsFlagSet(Flags.Carry);
|
||||
bool isMsbSet = IsBitSet(value, 7);
|
||||
value <<= 1;
|
||||
if (isCarrySet) SetBit(ref value, 0);
|
||||
|
||||
SetClearFlagConditional(Flags.Zero, (value == 0x00));
|
||||
ClearFlag(Flags.Subtract);
|
||||
ClearFlag(Flags.HalfCarry);
|
||||
SetClearFlagConditional(Flags.Carry, isMsbSet);
|
||||
}
|
||||
|
||||
protected byte RotateLeftCircular(ushort address)
|
||||
{
|
||||
byte value = ReadMemory8(address);
|
||||
RotateLeftCircular(ref value);
|
||||
WriteMemory8(address, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
protected void RotateLeftCircular(ref byte value)
|
||||
{
|
||||
bool isMsbSet = IsBitSet(value, 7);
|
||||
value <<= 1;
|
||||
if (isMsbSet) SetBit(ref value, 0);
|
||||
|
||||
SetClearFlagConditional(Flags.Zero, (value == 0x00));
|
||||
ClearFlag(Flags.Subtract);
|
||||
ClearFlag(Flags.HalfCarry);
|
||||
SetClearFlagConditional(Flags.Carry, isMsbSet);
|
||||
}
|
||||
|
||||
protected byte RotateRight(ushort address)
|
||||
{
|
||||
byte value = ReadMemory8(address);
|
||||
RotateRight(ref value);
|
||||
WriteMemory8(address, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
protected void RotateRight(ref byte value)
|
||||
{
|
||||
bool isCarrySet = IsFlagSet(Flags.Carry);
|
||||
bool isLsbSet = IsBitSet(value, 0);
|
||||
value >>= 1;
|
||||
if (isCarrySet) SetBit(ref value, 7);
|
||||
|
||||
SetClearFlagConditional(Flags.Zero, (value == 0x00));
|
||||
ClearFlag(Flags.Subtract);
|
||||
ClearFlag(Flags.HalfCarry);
|
||||
SetClearFlagConditional(Flags.Carry, isLsbSet);
|
||||
}
|
||||
|
||||
protected byte RotateRightCircular(ushort address)
|
||||
{
|
||||
byte value = ReadMemory8(address);
|
||||
RotateRightCircular(ref value);
|
||||
WriteMemory8(address, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
protected void RotateRightCircular(ref byte value)
|
||||
{
|
||||
bool isLsbSet = IsBitSet(value, 0);
|
||||
value >>= 1;
|
||||
if (isLsbSet) SetBit(ref value, 7);
|
||||
|
||||
SetClearFlagConditional(Flags.Zero, (value == 0x00));
|
||||
ClearFlag(Flags.Subtract);
|
||||
ClearFlag(Flags.HalfCarry);
|
||||
SetClearFlagConditional(Flags.Carry, isLsbSet);
|
||||
}
|
||||
|
||||
protected void RotateLeftAccumulator()
|
||||
{
|
||||
bool isCarrySet = IsFlagSet(Flags.Carry);
|
||||
bool isMsbSet = IsBitSet(af.High, 7);
|
||||
af.High <<= 1;
|
||||
if (isCarrySet) SetBit(ref af.High, 0);
|
||||
|
||||
ClearFlag(Flags.Zero);
|
||||
ClearFlag(Flags.Subtract);
|
||||
ClearFlag(Flags.HalfCarry);
|
||||
SetClearFlagConditional(Flags.Carry, isMsbSet);
|
||||
}
|
||||
|
||||
protected void RotateLeftAccumulatorCircular()
|
||||
{
|
||||
bool isMsbSet = IsBitSet(af.High, 7);
|
||||
af.High <<= 1;
|
||||
if (isMsbSet) SetBit(ref af.High, 0);
|
||||
|
||||
ClearFlag(Flags.Zero);
|
||||
ClearFlag(Flags.Subtract);
|
||||
ClearFlag(Flags.HalfCarry);
|
||||
SetClearFlagConditional(Flags.Carry, isMsbSet);
|
||||
}
|
||||
|
||||
protected void RotateRightAccumulator()
|
||||
{
|
||||
bool isCarrySet = IsFlagSet(Flags.Carry);
|
||||
bool isLsbSet = IsBitSet(af.High, 0);
|
||||
af.High >>= 1;
|
||||
if (isCarrySet) SetBit(ref af.High, 7);
|
||||
|
||||
ClearFlag(Flags.Zero);
|
||||
ClearFlag(Flags.Subtract);
|
||||
ClearFlag(Flags.HalfCarry);
|
||||
SetClearFlagConditional(Flags.Carry, isLsbSet);
|
||||
}
|
||||
|
||||
protected void RotateRightAccumulatorCircular()
|
||||
{
|
||||
bool isLsbSet = IsBitSet(af.High, 0);
|
||||
af.High >>= 1;
|
||||
if (isLsbSet) SetBit(ref af.High, 7);
|
||||
|
||||
ClearFlag(Flags.Zero);
|
||||
ClearFlag(Flags.Subtract);
|
||||
ClearFlag(Flags.HalfCarry);
|
||||
SetClearFlagConditional(Flags.Carry, isLsbSet);
|
||||
}
|
||||
|
||||
protected byte ShiftLeftArithmetic(ushort address)
|
||||
{
|
||||
byte value = ReadMemory8(address);
|
||||
ShiftLeftArithmetic(ref value);
|
||||
WriteMemory8(address, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
protected void ShiftLeftArithmetic(ref byte value)
|
||||
{
|
||||
bool isMsbSet = IsBitSet(value, 7);
|
||||
value <<= 1;
|
||||
|
||||
SetClearFlagConditional(Flags.Zero, (value == 0x00));
|
||||
ClearFlag(Flags.Subtract);
|
||||
ClearFlag(Flags.HalfCarry);
|
||||
SetClearFlagConditional(Flags.Carry, isMsbSet);
|
||||
}
|
||||
|
||||
protected byte ShiftRightArithmetic(ushort address)
|
||||
{
|
||||
byte value = ReadMemory8(address);
|
||||
ShiftRightArithmetic(ref value);
|
||||
WriteMemory8(address, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
protected void ShiftRightArithmetic(ref byte value)
|
||||
{
|
||||
bool isLsbSet = IsBitSet(value, 0);
|
||||
bool isMsbSet = IsBitSet(value, 7);
|
||||
value >>= 1;
|
||||
if (isMsbSet) SetBit(ref value, 7);
|
||||
|
||||
SetClearFlagConditional(Flags.Zero, (value == 0x00));
|
||||
ClearFlag(Flags.Subtract);
|
||||
ClearFlag(Flags.HalfCarry);
|
||||
SetClearFlagConditional(Flags.Carry, isLsbSet);
|
||||
}
|
||||
|
||||
protected byte ShiftRightLogical(ushort address)
|
||||
{
|
||||
byte value = ReadMemory8(address);
|
||||
ShiftRightLogical(ref value);
|
||||
WriteMemory8(address, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
protected void ShiftRightLogical(ref byte value)
|
||||
{
|
||||
bool isLsbSet = IsBitSet(value, 0);
|
||||
value >>= 1;
|
||||
|
||||
SetClearFlagConditional(Flags.Zero, (value == 0x00));
|
||||
ClearFlag(Flags.Subtract);
|
||||
ClearFlag(Flags.HalfCarry);
|
||||
SetClearFlagConditional(Flags.Carry, isLsbSet);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Opcodes: Bit Set, Reset and Test Group
|
||||
|
||||
protected byte SetBit(ushort address, int bit)
|
||||
{
|
||||
byte value = ReadMemory8(address);
|
||||
SetBit(ref value, bit);
|
||||
WriteMemory8(address, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
protected void SetBit(ref byte value, int bit)
|
||||
{
|
||||
value |= (byte)(1 << bit);
|
||||
}
|
||||
|
||||
protected byte ResetBit(ushort address, int bit)
|
||||
{
|
||||
byte value = ReadMemory8(address);
|
||||
ResetBit(ref value, bit);
|
||||
WriteMemory8(address, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
protected void ResetBit(ref byte value, int bit)
|
||||
{
|
||||
value &= (byte)~(1 << bit);
|
||||
}
|
||||
|
||||
protected void TestBit(ushort address, int bit)
|
||||
{
|
||||
byte value = ReadMemory8(address);
|
||||
|
||||
TestBit(value, bit);
|
||||
}
|
||||
|
||||
protected void TestBit(byte value, int bit)
|
||||
{
|
||||
bool isBitSet = ((value & (1 << bit)) != 0);
|
||||
|
||||
SetClearFlagConditional(Flags.Zero, !isBitSet);
|
||||
ClearFlag(Flags.Subtract);
|
||||
SetFlag(Flags.HalfCarry);
|
||||
// C
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Opcodes: Jump Group
|
||||
|
||||
protected void Jump8()
|
||||
{
|
||||
pc += (ushort)(((sbyte)ReadMemory8(pc)) + 1);
|
||||
}
|
||||
|
||||
protected void JumpConditional8(bool condition)
|
||||
{
|
||||
if (condition)
|
||||
{
|
||||
Jump8();
|
||||
currentCycles += CycleCounts.AdditionalJumpCond8Taken;
|
||||
}
|
||||
else
|
||||
pc++;
|
||||
}
|
||||
|
||||
protected void JumpConditional16(bool condition)
|
||||
{
|
||||
if (condition)
|
||||
pc = ReadMemory16(pc);
|
||||
else
|
||||
pc += 2;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Opcodes: Call and Return Group
|
||||
|
||||
protected void Call16()
|
||||
{
|
||||
WriteMemory8(--sp, (byte)((pc + 2) >> 8));
|
||||
WriteMemory8(--sp, (byte)((pc + 2) & 0xFF));
|
||||
pc = ReadMemory16(pc);
|
||||
}
|
||||
|
||||
protected void CallConditional16(bool condition)
|
||||
{
|
||||
if (condition)
|
||||
{
|
||||
Call16();
|
||||
currentCycles += CycleCounts.AdditionalCallCondTaken;
|
||||
}
|
||||
else
|
||||
pc += 2;
|
||||
}
|
||||
|
||||
protected void Return()
|
||||
{
|
||||
pc = ReadMemory16(sp);
|
||||
sp += 2;
|
||||
}
|
||||
|
||||
protected void ReturnConditional(bool condition)
|
||||
{
|
||||
if (condition)
|
||||
{
|
||||
Return();
|
||||
currentCycles += CycleCounts.AdditionalRetCondTaken;
|
||||
}
|
||||
}
|
||||
|
||||
protected void Restart(ushort address)
|
||||
{
|
||||
WriteMemory8(--sp, (byte)(pc >> 8));
|
||||
WriteMemory8(--sp, (byte)(pc & 0xFF));
|
||||
pc = address;
|
||||
}
|
||||
|
||||
protected bool RestartFromInterrupt(InterruptSource intSource)
|
||||
{
|
||||
// https://github.com/Gekkio/mooneye-gb/blob/ca7ff30/tests/acceptance/interrupts/ie_push.s
|
||||
|
||||
var address = (ushort)(0x0040 + (byte)((int)intSource << 3));
|
||||
var intSourceBit = (byte)(1 << (byte)intSource);
|
||||
|
||||
WriteMemory8(--sp, (byte)(pc >> 8));
|
||||
|
||||
var newIntEnable = memoryReadDelegate(0xFFFF);
|
||||
var continueRestart = (newIntEnable & intSourceBit) != 0;
|
||||
|
||||
WriteMemory8(--sp, (byte)(pc & 0xFF));
|
||||
|
||||
if (continueRestart)
|
||||
{
|
||||
pc = address;
|
||||
memoryWriteDelegate(0xFF0F, (byte)(memoryReadDelegate(0xFF0F) & (byte)~intSourceBit));
|
||||
}
|
||||
else
|
||||
pc = 0x0000;
|
||||
|
||||
return continueRestart;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Opcodes: SM83-specific Opcodes
|
||||
|
||||
protected void PopAF()
|
||||
{
|
||||
af.Low = (byte)(ReadMemory8(sp++) & 0xF0);
|
||||
af.High = ReadMemory8(sp++);
|
||||
}
|
||||
|
||||
protected void Swap(ushort address)
|
||||
{
|
||||
byte value = ReadMemory8(address);
|
||||
Swap(ref value);
|
||||
WriteMemory8(address, value);
|
||||
}
|
||||
|
||||
protected void Swap(ref byte value)
|
||||
{
|
||||
value = (byte)((value & 0xF0) >> 4 | (value & 0x0F) << 4);
|
||||
|
||||
SetClearFlagConditional(Flags.Zero, (value == 0x00));
|
||||
ClearFlag(Flags.Subtract);
|
||||
ClearFlag(Flags.HalfCarry);
|
||||
ClearFlag(Flags.Carry);
|
||||
}
|
||||
|
||||
protected virtual void Stop()
|
||||
{
|
||||
pc++;
|
||||
}
|
||||
|
||||
private void AddSPNN()
|
||||
{
|
||||
byte offset = ReadMemory8(pc++);
|
||||
|
||||
ClearFlag(Flags.Zero);
|
||||
ClearFlag(Flags.Subtract);
|
||||
SetClearFlagConditional(Flags.HalfCarry, (((sp & 0x0F) + (offset & 0x0F)) > 0x0F));
|
||||
SetClearFlagConditional(Flags.Carry, (((sp & 0xFF) + (byte)(offset & 0xFF)) > 0xFF));
|
||||
|
||||
sp = (ushort)(sp + (sbyte)offset);
|
||||
}
|
||||
|
||||
private void LoadHLSPNN()
|
||||
{
|
||||
byte offset = ReadMemory8(pc++);
|
||||
|
||||
ClearFlag(Flags.Zero);
|
||||
ClearFlag(Flags.Subtract);
|
||||
SetClearFlagConditional(Flags.HalfCarry, (((sp & 0x0F) + (offset & 0x0F)) > 0x0F));
|
||||
SetClearFlagConditional(Flags.Carry, (((sp & 0xFF) + (byte)(offset & 0xFF)) > 0xFF));
|
||||
|
||||
hl.Word = (ushort)(sp + (sbyte)offset);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 18c6dc23d0278e847b2cc8483998c6fc
|
@ -0,0 +1,57 @@
|
||||
namespace Essgee.Emulation.CPU
|
||||
{
|
||||
public class SM83CGB : SM83
|
||||
{
|
||||
// TODO: better way of implementing this?
|
||||
public bool IsDoubleSpeed { get; private set; }
|
||||
|
||||
public SM83CGB(MemoryReadDelegate memoryRead, MemoryWriteDelegate memoryWrite) : base(memoryRead, memoryWrite) { }
|
||||
|
||||
protected override void EnterHaltState()
|
||||
{
|
||||
if (ime)
|
||||
{
|
||||
halt = true;
|
||||
pc--;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((memoryReadDelegate(0xFF0F) & memoryReadDelegate(0xFFFF) & 0x1F) != 0)
|
||||
currentCycles += 8;
|
||||
else
|
||||
halt = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Stop()
|
||||
{
|
||||
pc++;
|
||||
|
||||
// Perform speed switch; get IO register value
|
||||
var key1 = memoryReadDelegate(0xFF4D);
|
||||
|
||||
// Is speed switch pending?
|
||||
if ((key1 & 0b1) != 0)
|
||||
{
|
||||
// Clear pending flag
|
||||
key1 &= 0xFE;
|
||||
|
||||
if (((key1 >> 7) & 0b1) != 0)
|
||||
{
|
||||
// Was double speed, now normal speed
|
||||
key1 &= 0x7F;
|
||||
IsDoubleSpeed = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Was normal speed, now double speed
|
||||
key1 |= 0x80;
|
||||
IsDoubleSpeed = true;
|
||||
}
|
||||
|
||||
// Write register value
|
||||
memoryWriteDelegate(0xFF4D, key1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cf45a7372b4385e429895c45fe9f4db0
|
@ -0,0 +1,95 @@
|
||||
namespace Essgee.Emulation.CPU
|
||||
{
|
||||
public partial class Z80A
|
||||
{
|
||||
public static class CycleCounts
|
||||
{
|
||||
public const int AdditionalJumpCond8Taken = 5;
|
||||
public const int AdditionalRetCondTaken = 6;
|
||||
public const int AdditionalCallCondTaken = 7;
|
||||
public const int AdditionalRepeatByteOps = 5;
|
||||
public const int AdditionalDDFDOps = 4;
|
||||
public const int AdditionalDDFDCBOps = 8;
|
||||
|
||||
public static readonly int[] NoPrefix = new int[]
|
||||
{
|
||||
4, 10, 7, 6, 4, 4, 7, 4, 4, 11, 7, 6, 4, 4, 7, 4, /* 0x00 - 0x0F */
|
||||
8, 10, 7, 6, 4, 4, 7, 4, 12, 11, 7, 6, 4, 4, 7, 4, /* 0x10 - 0x1F */
|
||||
7, 10, 16, 6, 4, 4, 7, 4, 7, 11, 16, 6, 4, 4, 4, 4, /* 0x20 - 0x2F */
|
||||
7, 10, 13, 6, 11, 11, 10, 4, 7, 11, 13, 6, 4, 4, 7, 4, /* 0x30 - 0x3F */
|
||||
4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, /* 0x40 - 0x4F */
|
||||
4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, /* 0x50 - 0x5F */
|
||||
4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, /* 0x60 - 0x6F */
|
||||
7, 7, 7, 7, 7, 7, 4, 7, 4, 4, 4, 4, 4, 4, 7, 4, /* 0x70 - 0x7F */
|
||||
4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, /* 0x80 - 0x8F */
|
||||
4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, /* 0x90 - 0x9F */
|
||||
4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, /* 0xA0 - 0xAF */
|
||||
4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, /* 0xB0 - 0xBF */
|
||||
5, 10, 10, 10, 10, 11, 7, 11, 5, 10, 10, 0, 10, 17, 7, 11, /* 0xC0 - 0xCF */
|
||||
5, 10, 10, 11, 10, 11, 7, 11, 5, 4, 10, 11, 10, 0, 7, 11, /* 0xD0 - 0xDF */
|
||||
5, 10, 10, 19, 10, 11, 7, 11, 5, 4, 10, 4, 10, 0, 7, 11, /* 0xE0 - 0xEF */
|
||||
5, 10, 10, 4, 10, 11, 7, 11, 5, 6, 10, 4, 10, 0, 7, 11 /* 0xF0 - 0xFF */
|
||||
};
|
||||
|
||||
public static readonly int[] PrefixED = new int[]
|
||||
{
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, /* 0x00 - 0x0F */
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, /* 0x10 - 0x1F */
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, /* 0x20 - 0x2F */
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, /* 0x30 - 0x3F */
|
||||
12, 12, 15, 20, 8, 14, 8, 9, 12, 12, 15, 20, 8, 14, 8, 9, /* 0x40 - 0x4F */
|
||||
12, 12, 15, 20, 8, 14, 8, 9, 12, 12, 15, 20, 8, 14, 8, 9, /* 0x50 - 0x5F */
|
||||
12, 12, 15, 20, 8, 14, 8, 18, 12, 12, 15, 20, 8, 14, 8, 18, /* 0x60 - 0x6F */
|
||||
12, 12, 15, 20, 8, 14, 8, 4, 12, 12, 15, 20, 8, 14, 8, 4, /* 0x70 - 0x7F */
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, /* 0x80 - 0x8F */
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, /* 0x90 - 0x9F */
|
||||
16, 16, 16, 16, 8, 8, 8, 8, 16, 16, 16, 16, 8, 8, 8, 8, /* 0xA0 - 0xAF */
|
||||
16, 16, 16, 16, 8, 8, 8, 8, 16, 16, 16, 16, 8, 8, 8, 8, /* 0xB0 - 0xBF */
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, /* 0xC0 - 0xCF */
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, /* 0xD0 - 0xDF */
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, /* 0xE0 - 0xEF */
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 /* 0xF0 - 0xFF */
|
||||
};
|
||||
|
||||
public static readonly int[] PrefixCB = new int[]
|
||||
{
|
||||
8, 8, 8, 8, 8, 8, 15, 8, 8, 8, 8, 8, 8, 8, 15, 8, /* 0x00 - 0x0F */
|
||||
8, 8, 8, 8, 8, 8, 15, 8, 8, 8, 8, 8, 8, 8, 15, 8, /* 0x10 - 0x1F */
|
||||
8, 8, 8, 8, 8, 8, 15, 8, 8, 8, 8, 8, 8, 8, 15, 8, /* 0x20 - 0x2F */
|
||||
8, 8, 8, 8, 8, 8, 15, 8, 8, 8, 8, 8, 8, 8, 15, 8, /* 0x30 - 0x3F */
|
||||
8, 8, 8, 8, 8, 8, 12, 8, 8, 8, 8, 8, 8, 8, 12, 8, /* 0x40 - 0x4F */
|
||||
8, 8, 8, 8, 8, 8, 12, 8, 8, 8, 8, 8, 8, 8, 12, 8, /* 0x50 - 0x5F */
|
||||
8, 8, 8, 8, 8, 8, 12, 8, 8, 8, 8, 8, 8, 8, 12, 8, /* 0x60 - 0x6F */
|
||||
8, 8, 8, 8, 8, 8, 12, 8, 8, 8, 8, 8, 8, 8, 12, 8, /* 0x70 - 0x7F */
|
||||
8, 8, 8, 8, 8, 8, 15, 8, 8, 8, 8, 8, 8, 8, 15, 8, /* 0x80 - 0x8F */
|
||||
8, 8, 8, 8, 8, 8, 15, 8, 8, 8, 8, 8, 8, 8, 15, 8, /* 0x90 - 0x9F */
|
||||
8, 8, 8, 8, 8, 8, 15, 8, 8, 8, 8, 8, 8, 8, 15, 8, /* 0xA0 - 0xAF */
|
||||
8, 8, 8, 8, 8, 8, 15, 8, 8, 8, 8, 8, 8, 8, 15, 8, /* 0xB0 - 0xBF */
|
||||
8, 8, 8, 8, 8, 8, 15, 8, 8, 8, 8, 8, 8, 8, 15, 8, /* 0xC0 - 0xCF */
|
||||
8, 8, 8, 8, 8, 8, 15, 8, 8, 8, 8, 8, 8, 8, 15, 8, /* 0xD0 - 0xDF */
|
||||
8, 8, 8, 8, 8, 8, 15, 8, 8, 8, 8, 8, 8, 8, 15, 8, /* 0xE0 - 0xEF */
|
||||
8, 8, 8, 8, 8, 8, 15, 8, 8, 8, 8, 8, 8, 8, 15, 8 /* 0xF0 - 0xFF */
|
||||
};
|
||||
|
||||
public static readonly int[] PrefixDDFD = new int[]
|
||||
{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, /* 0x00 - 0x0F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1F */
|
||||
0, 14, 20, 10, 0, 0, 0, 0, 0, 15, 20, 10, 0, 0, 0, 0, /* 0x20 - 0x2F */
|
||||
0, 0, 0, 0, 23, 23, 19, 0, 0, 15, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3F */
|
||||
0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 19, 0, /* 0x40 - 0x4F */
|
||||
0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 19, 0, /* 0x50 - 0x5F */
|
||||
0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 19, 0, /* 0x60 - 0x6F */
|
||||
19, 19, 19, 19, 19, 19, 0, 19, 0, 0, 0, 0, 0, 0, 19, 0, /* 0x70 - 0x7F */
|
||||
0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 19, 0, /* 0x80 - 0x8F */
|
||||
0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 19, 0, /* 0x90 - 0x9F */
|
||||
0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 19, 0, /* 0xA0 - 0xAF */
|
||||
0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 19, 0, /* 0xB0 - 0xBF */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xC0 - 0xCF */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xD0 - 0xDF */
|
||||
0, 14, 0, 23, 0, 15, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, /* 0xE0 - 0xEF */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0 /* 0xF0 - 0xFF */
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c40dfcb38d98fe45a313cee0df21449
|
@ -0,0 +1,485 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace Essgee.Emulation.CPU
|
||||
{
|
||||
public partial class Z80A
|
||||
{
|
||||
// TODO: add mnemonics for undocumented opcodes
|
||||
|
||||
static readonly string[] opcodeMnemonicNoPrefix = new string[]
|
||||
{
|
||||
"NOP", "LD BC, 0x{0:X4}", "LD (BC), A", "INC BC", "INC B", "DEC B", "LD B, 0x{0:X2}", "RLCA", /* 0x00 */
|
||||
"EX AF, AF'", "ADD HL, BC", "LD A, (BC)", "DEC BC", "INC C", "DEC C", "LD C, 0x{0:X2}", "RRCA", /* 0x08 */
|
||||
"DJNZ 0x{0:X2}", "LD DE, 0x{0:X4}", "LD (DE), A", "INC DE", "INC D", "DEC D", "LD D, 0x{0:X2}", "RLA", /* 0x10 */
|
||||
"JR 0x{0:X2}", "ADD HL, DE", "LD A, (DE)", "DEC DE", "INC E", "DEC E", "LD E, 0x{0:X2}", "RRA", /* 0x18 */
|
||||
"JR NZ, 0x{0:X2}", "LD HL, 0x{0:X4}", "LD (0x{0:X4}), HL", "INC HL", "INC H", "DEC H", "LD H, 0x{0:X2}", "DAA", /* 0x20 */
|
||||
"JR Z, 0x{0:X2}", "ADD HL, HL", "LD HL, (0x{0:X4})", "DEC HL", "INC L", "DEC L", "LD L, 0x{0:X2}", "CPL", /* 0x28 */
|
||||
"JR NC, 0x{0:X2}", "LD SP, 0x{0:X4}", "LD (0x{0:X4}), A", "INC SP", "INC (HL)", "DEC (HL)", "LD (HL), 0x{0:X2}", "SCF", /* 0x30 */
|
||||
"JR C, 0x{0:X2}", "ADD HL, SP", "LD A, (0x{0:X4})", "DEC SP", "INC A", "DEC A", "LD A, 0x{0:X2}", "CCF", /* 0x38 */
|
||||
"LD B, B", "LD B, C", "LD B, D", "LD B, E", "LD B, H", "LD B, L", "LD B, (HL)", "LD B, A", /* 0x40 */
|
||||
"LD C, B", "LD C, C", "LD C, D", "LD C, E", "LD C, H", "LD C, L", "LD C, (HL)", "LD C, A", /* 0x48 */
|
||||
"LD D, B", "LD D, C", "LD D, D", "LD D, E", "LD D, H", "LD D, L", "LD D, (HL)", "LD D, A", /* 0x50 */
|
||||
"LD E, B", "LD E, C", "LD E, D", "LD E, E", "LD E, H", "LD E, L", "LD E, (HL)", "LD E, A", /* 0x58 */
|
||||
"LD H, B", "LD H, C", "LD H, D", "LD H, E", "LD H, H", "LD H, L", "LD H, (HL)", "LD H, A", /* 0x60 */
|
||||
"LD L, B", "LD L, C", "LD L, D", "LD L, E", "LD L, H", "LD L, L", "LD L, (HL)", "LD L, A", /* 0x68 */
|
||||
"LD (HL), B", "LD (HL), C", "LD (HL), D", "LD (HL), E", "LD (HL), H", "LD (HL), L", "HALT", "LD (HL), A", /* 0x70 */
|
||||
"LD A, B", "LD A, C", "LD A, D", "LD A, E", "LD A, H", "LD A, L", "LD A, (HL)", "LD A, A", /* 0x78 */
|
||||
"ADD A, B", "ADD A, C", "ADD A, D", "ADD A, E", "ADD A, H", "ADD A, L", "ADD A, (HL)", "ADD A, A", /* 0x80 */
|
||||
"ADC A, B", "ADC A, C", "ADC A, D", "ADC A, E", "ADC A, H", "ADC A, L", "ADC A, (HL)", "ADC A, A", /* 0x88 */
|
||||
"SUB B", "SUB C", "SUB D", "SUB E", "SUB H", "SUB L", "SUB (HL)", "SUB A", /* 0x90 */
|
||||
"SBC B", "SBC C", "SBC D", "SBC E", "SBC H", "SBC L", "SBC (HL)", "SBC A", /* 0x98 */
|
||||
"AND B", "AND C", "AND D", "AND E", "AND H", "AND L", "AND (HL)", "AND A", /* 0xA0 */
|
||||
"XOR B", "XOR C", "XOR D", "XOR E", "XOR H", "XOR L", "XOR (HL)", "XOR A", /* 0xA8 */
|
||||
"OR B", "OR C", "OR D", "OR E", "OR H", "OR L", "OR (HL)", "OR A", /* 0xA0 */
|
||||
"CP B", "CP C", "CP D", "CP E", "CP H", "CP L", "CP (HL)", "CP A", /* 0xB8 */
|
||||
"RET NZ", "POP BC", "JP NZ, 0x{0:X4}", "JP 0x{0:X4}", "CALL NZ, 0x{0:X4}", "PUSH BC", "ADD A, 0x{0:X2}", "RST 00", /* 0xC0 */
|
||||
"RET Z", "RET", "JP Z, 0x{0:X4}", string.Empty, "CALL Z, 0x{0:X4}", "CALL 0x{0:X4}", "ADC A, 0x{0:X2}", "RST 08", /* 0xC8 */
|
||||
"RET NC", "POP DE", "JP NC, 0x{0:X4}", "OUT 0x{0:X2}, A", "CALL NC, 0x{0:X4}", "PUSH DE", "SUB 0x{0:X2}", "RST 10", /* 0xD0 */
|
||||
"RET C", "EXX", "JP C, 0x{0:X4}", "IN A, 0x{0:X2}", "CALL C, 0x{0:X4}", string.Empty, "SBC 0x{0:X2}", "RST 18", /* 0xD8 */
|
||||
"RET PO", "POP HL", "JP PO, 0x{0:X4}", "EX (SP), HL", "CALL PO, 0x{0:X4}", "PUSH HL", "AND 0x{0:X2}", "RST 20", /* 0xE0 */
|
||||
"RET PE", "JP (HL)", "JP PE, 0x{0:X4}", "EX DE, HL", "CALL PE, 0x{0:X4}", string.Empty, "XOR 0x{0:X2}", "RST 28", /* 0xE8 */
|
||||
"RET P", "POP AF", "JP P, 0x{0:X4}", "DI", "CALL P, 0x{0:X4}", "PUSH AF", "OR 0x{0:X2}", "RST 30", /* 0xF0 */
|
||||
"RET M", "LD SP, HL", "JP M, 0x{0:X4}", "EI", "CALL M, 0x{0:X4}", string.Empty, "CP 0x{0:X2}", "RST 38" /* 0xF0 */
|
||||
};
|
||||
|
||||
static readonly int[] opcodeLengthNoPrefix = new int[]
|
||||
{
|
||||
1, 3, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
|
||||
2, 3, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 2, 1,
|
||||
2, 3, 3, 1, 1, 1, 2, 1, 2, 1, 3, 1, 1, 1, 2, 1,
|
||||
2, 3, 3, 1, 1, 1, 2, 1, 2, 1, 3, 1, 1, 1, 2, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 3, 3, 3, 1, 2, 1, 1, 1, 3, -1, 3, 3, 2, 1,
|
||||
1, 1, 3, 2, 3, 1, 2, 1, 1, 1, 3, 2, 3, -1, 2, 1,
|
||||
1, 1, 3, 1, 3, 1, 2, 1, 1, 1, 3, 1, 3, -1, 2, 1,
|
||||
1, 1, 3, 1, 3, 1, 2, 1, 1, 1, 3, 1, 3, -1, 2, 1,
|
||||
};
|
||||
|
||||
static readonly string[] opcodeMnemonicPrefixED = new string[]
|
||||
{
|
||||
".DB 0xED, 0x00", ".DB 0xED, 0x01", ".DB 0xED, 0x02", ".DB 0xED, 0x03", ".DB 0xED, 0x04", ".DB 0xED, 0x05", ".DB 0xED, 0x06", ".DB 0xED, 0x07", /* 0x00 */
|
||||
".DB 0xED, 0x08", ".DB 0xED, 0x09", ".DB 0xED, 0x0A", ".DB 0xED, 0x0B", ".DB 0xED, 0x0C", ".DB 0xED, 0x0D", ".DB 0xED, 0x0E", ".DB 0xED, 0x0F", /* 0x08 */
|
||||
".DB 0xED, 0x10", ".DB 0xED, 0x11", ".DB 0xED, 0x12", ".DB 0xED, 0x13", ".DB 0xED, 0x14", ".DB 0xED, 0x15", ".DB 0xED, 0x16", ".DB 0xED, 0x17", /* 0x10 */
|
||||
".DB 0xED, 0x18", ".DB 0xED, 0x19", ".DB 0xED, 0x1A", ".DB 0xED, 0x1B", ".DB 0xED, 0x1C", ".DB 0xED, 0x1D", ".DB 0xED, 0x1E", ".DB 0xED, 0x1F", /* 0x18 */
|
||||
".DB 0xED, 0x20", ".DB 0xED, 0x21", ".DB 0xED, 0x22", ".DB 0xED, 0x23", ".DB 0xED, 0x24", ".DB 0xED, 0x25", ".DB 0xED, 0x26", ".DB 0xED, 0x27", /* 0x20 */
|
||||
".DB 0xED, 0x28", ".DB 0xED, 0x29", ".DB 0xED, 0x2A", ".DB 0xED, 0x2B", ".DB 0xED, 0x2C", ".DB 0xED, 0x2D", ".DB 0xED, 0x2E", ".DB 0xED, 0x2F", /* 0x28 */
|
||||
".DB 0xED, 0x30", ".DB 0xED, 0x31", ".DB 0xED, 0x32", ".DB 0xED, 0x33", ".DB 0xED, 0x34", ".DB 0xED, 0x35", ".DB 0xED, 0x36", ".DB 0xED, 0x37", /* 0x30 */
|
||||
".DB 0xED, 0x38", ".DB 0xED, 0x39", ".DB 0xED, 0x3A", ".DB 0xED, 0x3B", ".DB 0xED, 0x3C", ".DB 0xED, 0x3D", ".DB 0xED, 0x3E", ".DB 0xED, 0x3F", /* 0x38 */
|
||||
"IN B, (C)", "OUT (C), B", "SBC HL, BC", "LD (0x{0:X4}), BC", "NEG", "RETN", "IM 0", "LD I, A", /* 0x40 */
|
||||
"IN C, (C)", "OUT (C), C", "ADC HL, BC", "LD BC, (0x{0:X4})", ".DB 0xED, 0x4C", "RETI", ".DB 0xED, 0x4E", "LD R, A", /* 0x48 */
|
||||
"IN D, (C)", "OUT (C), D", "SBC HL, DE", "LD (0x{0:X4}), DE", ".DB 0xED, 0x54", ".DB 0xED, 0x55", "IM 1", "LD A, I", /* 0x50 */
|
||||
"IN E, (C)", "OUT (C), E", "ADC HL, DE", "LD DE, (0x{0:X4})", ".DB 0xED, 0x5C", ".DB 0xED, 0x5D", "IM 2", "LD A, R", /* 0x58 */
|
||||
"IN H, (C)", "OUT (C), H", "SBC HL, HL", ".DB 0xED, 0x63", ".DB 0xED, 0x64", ".DB 0xED, 0x65", ".DB 0xED, 0x66", "RRD", /* 0x60 */
|
||||
"IN L, (C)", "OUT (C), L", "ADC HL, HL", ".DB 0xED, 0x6B", ".DB 0xED, 0x6C", ".DB 0xED, 0x6D", ".DB 0xED, 0x6E", "RLD", /* 0x68 */
|
||||
".DB 0xED, 0x70", ".DB 0xED, 0x71", "SBC HL, SP", "LD (0x{0:X4}), SP", ".DB 0xED, 0x74", ".DB 0xED, 0x75", ".DB 0xED, 0x76", ".DB 0xED, 0x77", /* 0x70 */
|
||||
"IN A, (C)", "OUT (C), A", "ADC HL, SP", "LD SP, (0x{0:X4})", ".DB 0xED, 0x7C", ".DB 0xED, 0x7D", ".DB 0xED, 0x7E", ".DB 0xED, 0x7F", /* 0x78 */
|
||||
".DB 0xED, 0x80", ".DB 0xED, 0x81", ".DB 0xED, 0x82", ".DB 0xED, 0x83", ".DB 0xED, 0x84", ".DB 0xED, 0x85", ".DB 0xED, 0x86", ".DB 0xED, 0x87", /* 0x80 */
|
||||
".DB 0xED, 0x88", ".DB 0xED, 0x89", ".DB 0xED, 0x8A", ".DB 0xED, 0x8B", ".DB 0xED, 0x8C", ".DB 0xED, 0x8D", ".DB 0xED, 0x8E", ".DB 0xED, 0x8F", /* 0x88 */
|
||||
".DB 0xED, 0x90", ".DB 0xED, 0x91", ".DB 0xED, 0x92", ".DB 0xED, 0x93", ".DB 0xED, 0x94", ".DB 0xED, 0x95", ".DB 0xED, 0x96", ".DB 0xED, 0x97", /* 0x90 */
|
||||
".DB 0xED, 0x98", ".DB 0xED, 0x99", ".DB 0xED, 0x9A", ".DB 0xED, 0x9B", ".DB 0xED, 0x9C", ".DB 0xED, 0x9D", ".DB 0xED, 0x9E", ".DB 0xED, 0x9F", /* 0x98 */
|
||||
"LDI", "CPI", "INI", "OUTI", ".DB 0xED, 0xA4", ".DB 0xED, 0xA5", ".DB 0xED, 0xA6", ".DB 0xED, 0xA7", /* 0xA0 */
|
||||
"LDD", "CPD", "IND", "OUTD", ".DB 0xED, 0xAC", ".DB 0xED, 0xAD", ".DB 0xED, 0xAE", ".DB 0xED, 0xAF", /* 0xA8 */
|
||||
"LDIR", "CPIR", "INIR", "OTIR", ".DB 0xED, 0xB4", ".DB 0xED, 0xB5", ".DB 0xED, 0xB6", ".DB 0xED, 0xB7", /* 0xB0 */
|
||||
"LDDR", "CPDR", "INDR", "OTDR", ".DB 0xED, 0xBC", ".DB 0xED, 0xBD", ".DB 0xED, 0xBE", ".DB 0xED, 0xBF", /* 0xB8 */
|
||||
".DB 0xED, 0xC0", ".DB 0xED, 0xC1", ".DB 0xED, 0xC2", ".DB 0xED, 0xC3", ".DB 0xED, 0xC4", ".DB 0xED, 0xC5", ".DB 0xED, 0xC6", ".DB 0xED, 0xC7", /* 0xC0 */
|
||||
".DB 0xED, 0xC8", ".DB 0xED, 0xC9", ".DB 0xED, 0xCA", ".DB 0xED, 0xCB", ".DB 0xED, 0xCC", ".DB 0xED, 0xCD", ".DB 0xED, 0xCE", ".DB 0xED, 0xCF", /* 0xC8 */
|
||||
".DB 0xED, 0xD0", ".DB 0xED, 0xD1", ".DB 0xED, 0xD2", ".DB 0xED, 0xD3", ".DB 0xED, 0xD4", ".DB 0xED, 0xD5", ".DB 0xED, 0xD6", ".DB 0xED, 0xD7", /* 0xD0 */
|
||||
".DB 0xED, 0xD8", ".DB 0xED, 0xD9", ".DB 0xED, 0xDA", ".DB 0xED, 0xDB", ".DB 0xED, 0xDC", ".DB 0xED, 0xDD", ".DB 0xED, 0xDE", ".DB 0xED, 0xDF", /* 0xD8 */
|
||||
".DB 0xED, 0xE0", ".DB 0xED, 0xE1", ".DB 0xED, 0xE2", ".DB 0xED, 0xE3", ".DB 0xED, 0xE4", ".DB 0xED, 0xE5", ".DB 0xED, 0xE6", ".DB 0xED, 0xE7", /* 0xE0 */
|
||||
".DB 0xED, 0xE8", ".DB 0xED, 0xE9", ".DB 0xED, 0xEA", ".DB 0xED, 0xEB", ".DB 0xED, 0xEC", ".DB 0xED, 0xED", ".DB 0xED, 0xEE", ".DB 0xED, 0xEF", /* 0xE8 */
|
||||
".DB 0xED, 0xF0", ".DB 0xED, 0xF1", ".DB 0xED, 0xF2", ".DB 0xED, 0xF3", ".DB 0xED, 0xF4", ".DB 0xED, 0xF5", ".DB 0xED, 0xF6", ".DB 0xED, 0xF7", /* 0xF0 */
|
||||
".DB 0xED, 0xF8", ".DB 0xED, 0xF9", ".DB 0xED, 0xFA", ".DB 0xED, 0xFB", ".DB 0xED, 0xFC", ".DB 0xED, 0xFD", ".DB 0xED, 0xFE", ".DB 0xED, 0xFF" /* 0xF8 */
|
||||
};
|
||||
|
||||
static readonly int[] opcodeLengthPrefixED = new int[]
|
||||
{
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2,
|
||||
2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
};
|
||||
|
||||
static readonly string[] opcodeMnemonicPrefixCB = new string[]
|
||||
{
|
||||
"RLC B", "RLC C", "RLC D", "RLC E", "RLC H", "RLC L", "RLC (HL)", "RLC A", /* 0x00 */
|
||||
"RRC B", "RRC C", "RRC D", "RRC E", "RRC H", "RRC L", "RRC (HL)", "RRC A", /* 0x08 */
|
||||
"RL B", "RL C", "RL D", "RL E", "RL H", "RL L", "RL (HL)", "RL A", /* 0x10 */
|
||||
"RR B", "RR C", "RR D", "RR E", "RR H", "RR L", "RR (HL)", "RR A", /* 0x18 */
|
||||
"SLA B", "SLA C", "SLA D", "SLA E", "SLA H", "SLA L", "SLA (HL)", "SLA A", /* 0x20 */
|
||||
"SRA B", "SRA C", "SRA D", "SRA E", "SRA H", "SRA L", "SRA (HL)", "SRA A", /* 0x28 */
|
||||
"SLL B", "SLL C", "SLL D", "SLL E", "SLL H", "SLL L", "SLL (HL)", "SLL A", /* 0x30 */
|
||||
"SRL B", "SRL C", "SRL D", "SRL E", "SRL H", "SRL L", "SRL (HL)", "SRL A", /* 0x38 */
|
||||
"BIT 0, B", "BIT 0, C", "BIT 0, D", "BIT 0, E", "BIT 0, H", "BIT 0, L", "BIT 0, (HL)", "BIT 0, A", /* 0x40 */
|
||||
"BIT 1, B", "BIT 1, C", "BIT 1, D", "BIT 1, E", "BIT 1, H", "BIT 1, L", "BIT 1, (HL)", "BIT 1, A", /* 0x48 */
|
||||
"BIT 2, B", "BIT 2, C", "BIT 2, D", "BIT 2, E", "BIT 2, H", "BIT 2, L", "BIT 2, (HL)", "BIT 2, A", /* 0x50 */
|
||||
"BIT 3, B", "BIT 3, C", "BIT 3, D", "BIT 3, E", "BIT 3, H", "BIT 3, L", "BIT 3, (HL)", "BIT 3, A", /* 0x58 */
|
||||
"BIT 4, B", "BIT 4, C", "BIT 4, D", "BIT 4, E", "BIT 4, H", "BIT 4, L", "BIT 4, (HL)", "BIT 4, A", /* 0x60 */
|
||||
"BIT 5, B", "BIT 5, C", "BIT 5, D", "BIT 5, E", "BIT 5, H", "BIT 5, L", "BIT 5, (HL)", "BIT 5, A", /* 0x68 */
|
||||
"BIT 6, B", "BIT 6, C", "BIT 6, D", "BIT 6, E", "BIT 6, H", "BIT 6, L", "BIT 6, (HL)", "BIT 6, A", /* 0x70 */
|
||||
"BIT 7, B", "BIT 7, C", "BIT 7, D", "BIT 7, E", "BIT 7, H", "BIT 7, L", "BIT 7, (HL)", "BIT 7, A", /* 0x78 */
|
||||
"RES 0, B", "RES 0, C", "RES 0, D", "RES 0, E", "RES 0, H", "RES 0, L", "RES 0, (HL)", "RES 0, A", /* 0x80 */
|
||||
"RES 1, B", "RES 1, C", "RES 1, D", "RES 1, E", "RES 1, H", "RES 1, L", "RES 1, (HL)", "RES 1, A", /* 0x88 */
|
||||
"RES 2, B", "RES 2, C", "RES 2, D", "RES 2, E", "RES 2, H", "RES 2, L", "RES 2, (HL)", "RES 2, A", /* 0x90 */
|
||||
"RES 3, B", "RES 3, C", "RES 3, D", "RES 3, E", "RES 3, H", "RES 3, L", "RES 3, (HL)", "RES 3, A", /* 0x98 */
|
||||
"RES 4, B", "RES 4, C", "RES 4, D", "RES 4, E", "RES 4, H", "RES 4, L", "RES 4, (HL)", "RES 4, A", /* 0xA0 */
|
||||
"RES 5, B", "RES 5, C", "RES 5, D", "RES 5, E", "RES 5, H", "RES 5, L", "RES 5, (HL)", "RES 5, A", /* 0xA8 */
|
||||
"RES 6, B", "RES 6, C", "RES 6, D", "RES 6, E", "RES 6, H", "RES 6, L", "RES 6, (HL)", "RES 6, A", /* 0xB0 */
|
||||
"RES 7, B", "RES 7, C", "RES 7, D", "RES 7, E", "RES 7, H", "RES 7, L", "RES 7, (HL)", "RES 7, A", /* 0xB8 */
|
||||
"SET 0, B", "SET 0, C", "SET 0, D", "SET 0, E", "SET 0, H", "SET 0, L", "SET 0, (HL)", "SET 0, A", /* 0xC0 */
|
||||
"SET 1, B", "SET 1, C", "SET 1, D", "SET 1, E", "SET 1, H", "SET 1, L", "SET 1, (HL)", "SET 1, A", /* 0xC8 */
|
||||
"SET 2, B", "SET 2, C", "SET 2, D", "SET 2, E", "SET 2, H", "SET 2, L", "SET 2, (HL)", "SET 2, A", /* 0xD0 */
|
||||
"SET 3, B", "SET 3, C", "SET 3, D", "SET 3, E", "SET 3, H", "SET 3, L", "SET 3, (HL)", "SET 3, A", /* 0xD8 */
|
||||
"SET 4, B", "SET 4, C", "SET 4, D", "SET 4, E", "SET 4, H", "SET 4, L", "SET 4, (HL)", "SET 4, A", /* 0xE0 */
|
||||
"SET 5, B", "SET 5, C", "SET 5, D", "SET 5, E", "SET 5, H", "SET 5, L", "SET 5, (HL)", "SET 5, A", /* 0xE8 */
|
||||
"SET 6, B", "SET 6, C", "SET 6, D", "SET 6, E", "SET 6, H", "SET 6, L", "SET 6, (HL)", "SET 6, A", /* 0xF0 */
|
||||
"SET 7, B", "SET 7, C", "SET 7, D", "SET 7, E", "SET 7, H", "SET 7, L", "SET 7, (HL)", "SET 7, A" /* 0xF8 */
|
||||
};
|
||||
|
||||
static readonly int[] opcodeLength_CB = new int[]
|
||||
{
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
};
|
||||
|
||||
static readonly string[] opcodeMnemonicPrefixDD = new string[]
|
||||
{
|
||||
".DB 0xDD, 0x00", ".DB 0xDD, 0x01", ".DB 0xDD, 0x02", ".DB 0xDD, 0x03", ".DB 0xDD, 0x04", ".DB 0xDD, 0x05", ".DB 0xDD, 0x06", ".DB 0xDD, 0x07", /* 0x00 */
|
||||
".DB 0xDD, 0x08", "ADD IX, BC", ".DB 0xDD, 0x0A", ".DB 0xDD, 0x0B", ".DB 0xDD, 0x0C", ".DB 0xDD, 0x0D", ".DB 0xDD, 0x0E", ".DB 0xDD, 0x0F", /* 0x08 */
|
||||
".DB 0xDD, 0x10", ".DB 0xDD, 0x11", ".DB 0xDD, 0x12", ".DB 0xDD, 0x13", ".DB 0xDD, 0x14", ".DB 0xDD, 0x15", ".DB 0xDD, 0x16", ".DB 0xDD, 0x17", /* 0x10 */
|
||||
".DB 0xDD, 0x18", "ADD IX, DE", ".DB 0xDD, 0x1A", ".DB 0xDD, 0x1B", ".DB 0xDD, 0x1C", ".DB 0xDD, 0x1D", ".DB 0xDD, 0x1E", ".DB 0xDD, 0x1F", /* 0x18 */
|
||||
".DB 0xDD, 0x20", "LD IX, 0x{0:X4}", "LD (0x{0:X4}), IX", "INC IX", "INC IXH", "DEC IXH", "LD IXH, 0x{0:X2}", ".DB 0xDD, 0x27", /* 0x20 */
|
||||
".DB 0xDD, 0x28", "ADD IX, IX", "LD IX, (0x{0:X4})", "DEC IX", "INC IXL", "DEC IXL", "LD IXL, 0x{0:X2}", ".DB 0xDD, 0x2F", /* 0x28 */
|
||||
".DB 0xDD, 0x30", ".DB 0xDD, 0x31", ".DB 0xDD, 0x32", ".DB 0xDD, 0x33", "INC (IX+0x{0:X2})", "DEC (IX+0x{0:X2})", "LD (IX+0x{0:X2}), 0x{0:X2}", ".DB 0xDD, 0x37", /* 0x30 */
|
||||
".DB 0xDD, 0x38", "ADD IX, SP", ".DB 0xDD, 0x3A", ".DB 0xDD, 0x3B", ".DB 0xDD, 0x3C", ".DB 0xDD, 0x3D", ".DB 0xDD, 0x3E", ".DB 0xDD, 0x3F", /* 0x38 */
|
||||
".DB 0xDD, 0x40", ".DB 0xDD, 0x41", ".DB 0xDD, 0x42", ".DB 0xDD, 0x43", ".DB 0xDD, 0x44", ".DB 0xDD, 0x45", "LD B, (IX+0x{0:X2})", ".DB 0xDD, 0x47", /* 0x40 */
|
||||
".DB 0xDD, 0x48", ".DB 0xDD, 0x49", ".DB 0xDD, 0x4A", ".DB 0xDD, 0x4B", ".DB 0xDD, 0x4C", ".DB 0xDD, 0x4D", "LD C, (IX+0x{0:X2})", ".DB 0xDD, 0x4F", /* 0x48 */
|
||||
".DB 0xDD, 0x50", ".DB 0xDD, 0x51", ".DB 0xDD, 0x52", ".DB 0xDD, 0x53", ".DB 0xDD, 0x54", ".DB 0xDD, 0x55", "LD D, (IX+0x{0:X2})", ".DB 0xDD, 0x57", /* 0x50 */
|
||||
".DB 0xDD, 0x58", ".DB 0xDD, 0x59", ".DB 0xDD, 0x5A", ".DB 0xDD, 0x5B", ".DB 0xDD, 0x5C", ".DB 0xDD, 0x5D", "LD E, (IX+0x{0:X2})", ".DB 0xDD, 0x5F", /* 0x58 */
|
||||
".DB 0xDD, 0x60", ".DB 0xDD, 0x61", ".DB 0xDD, 0x62", ".DB 0xDD, 0x63", ".DB 0xDD, 0x64", ".DB 0xDD, 0x65", "LD H, (IX+0x{0:X2})", ".DB 0xDD, 0x67", /* 0x60 */
|
||||
".DB 0xDD, 0x68", ".DB 0xDD, 0x69", ".DB 0xDD, 0x6A", ".DB 0xDD, 0x6B", ".DB 0xDD, 0x6C", ".DB 0xDD, 0x6D", "LD L, (IX+0x{0:X2})", ".DB 0xDD, 0x6F", /* 0x68 */
|
||||
"LD (IX+0x{0:X2}), B", "LD (IX+0x{0:X2}), C", "LD (IX+0x{0:X2}), D", "LD (IX+0x{0:X2}), E", "LD (IX+0x{0:X2}), H", "LD (IX+0x{0:X2}), L", ".DB 0xDD, 0x76", "LD (IX+0x{0:X2}), A", /* 0x70 */
|
||||
".DB 0xDD, 0x78", ".DB 0xDD, 0x79", ".DB 0xDD, 0x7A", ".DB 0xDD, 0x7B", ".DB 0xDD, 0x7C", ".DB 0xDD, 0x7D", "LD A, (IX+0x{0:X2})", ".DB 0xDD, 0x7F", /* 0x78 */
|
||||
".DB 0xDD, 0x80", ".DB 0xDD, 0x81", ".DB 0xDD, 0x82", ".DB 0xDD, 0x83", ".DB 0xDD, 0x84", ".DB 0xDD, 0x85", "ADD A, (IX+0x{0:X2})", ".DB 0xDD, 0x87", /* 0x80 */
|
||||
".DB 0xDD, 0x88", ".DB 0xDD, 0x89", ".DB 0xDD, 0x8A", ".DB 0xDD, 0x8B", ".DB 0xDD, 0x8C", ".DB 0xDD, 0x8D", "ADC A, (IX+0x{0:X2})", ".DB 0xDD, 0x8F", /* 0x88 */
|
||||
".DB 0xDD, 0x90", ".DB 0xDD, 0x91", ".DB 0xDD, 0x92", ".DB 0xDD, 0x93", ".DB 0xDD, 0x94", ".DB 0xDD, 0x95", "SUB (IX+0x{0:X2})", ".DB 0xDD, 0x97", /* 0x90 */
|
||||
".DB 0xDD, 0x98", ".DB 0xDD, 0x99", ".DB 0xDD, 0x9A", ".DB 0xDD, 0x9B", ".DB 0xDD, 0x9C", ".DB 0xDD, 0x9D", "SBC (IX+0x{0:X2})", ".DB 0xDD, 0x9F", /* 0x98 */
|
||||
".DB 0xDD, 0xA0", ".DB 0xDD, 0xA1", ".DB 0xDD, 0xA2", ".DB 0xDD, 0xA3", ".DB 0xDD, 0xA4", ".DB 0xDD, 0xA5", "AND (IX+0x{0:X2})", ".DB 0xDD, 0xA7", /* 0xA0 */
|
||||
".DB 0xDD, 0xA8", ".DB 0xDD, 0xA9", ".DB 0xDD, 0xAA", ".DB 0xDD, 0xAB", ".DB 0xDD, 0xAC", ".DB 0xDD, 0xAD", "XOR (IX+0x{0:X2})", ".DB 0xDD, 0xAF", /* 0xA8 */
|
||||
".DB 0xDD, 0xB0", ".DB 0xDD, 0xB1", ".DB 0xDD, 0xB2", ".DB 0xDD, 0xB3", ".DB 0xDD, 0xB4", ".DB 0xDD, 0xB5", "OR (IX+0x{0:X2})", ".DB 0xDD, 0xB7", /* 0xB0 */
|
||||
".DB 0xDD, 0xB8", ".DB 0xDD, 0xB9", ".DB 0xDD, 0xBA", ".DB 0xDD, 0xBB", ".DB 0xDD, 0xBC", ".DB 0xDD, 0xBD", "CP (IX+0x{0:X2})", ".DB 0xDD, 0xBF", /* 0xB8 */
|
||||
".DB 0xDD, 0xC0", ".DB 0xDD, 0xC1", ".DB 0xDD, 0xC2", ".DB 0xDD, 0xC3", ".DB 0xDD, 0xC4", ".DB 0xDD, 0xC5", ".DB 0xDD, 0xC6", ".DB 0xDD, 0xC7", /* 0xC0 */
|
||||
".DB 0xDD, 0xC8", ".DB 0xDD, 0xC9", ".DB 0xDD, 0xCA", string.Empty, ".DB 0xDD, 0xCC", ".DB 0xDD, 0xCD", ".DB 0xDD, 0xCE", ".DB 0xDD, 0xCF", /* 0xC8 */
|
||||
".DB 0xDD, 0xD0", ".DB 0xDD, 0xD1", ".DB 0xDD, 0xD2", ".DB 0xDD, 0xD3", ".DB 0xDD, 0xD4", ".DB 0xDD, 0xD5", ".DB 0xDD, 0xD6", ".DB 0xDD, 0xD7", /* 0xD0 */
|
||||
".DB 0xDD, 0xD8", ".DB 0xDD, 0xD9", ".DB 0xDD, 0xDA", ".DB 0xDD, 0xDB", ".DB 0xDD, 0xDC", ".DB 0xDD, 0xDD", ".DB 0xDD, 0xDE", ".DB 0xDD, 0xDF", /* 0xD8 */
|
||||
".DB 0xED, 0xE0", "POP IX", ".DB 0xED, 0xE2", "EX (SP), IX", ".DB 0xED, 0xE4", "PUSH IX", ".DB 0xED, 0xE6", ".DB 0xED, 0xE7", /* 0xE0 */
|
||||
".DB 0xED, 0xE8", "JP (IX)", ".DB 0xED, 0xEA", ".DB 0xED, 0xEB", ".DB 0xED, 0xEC", ".DB 0xED, 0xED", ".DB 0xED, 0xEE", ".DB 0xED, 0xEF", /* 0xE8 */
|
||||
".DB 0xFD, 0xF0", ".DB 0xFD, 0xF1", ".DB 0xFD, 0xF2", ".DB 0xFD, 0xF3", ".DB 0xFD, 0xF4", ".DB 0xFD, 0xF5", ".DB 0xFD, 0xF6", ".DB 0xFD, 0xF7", /* 0xF0 */
|
||||
".DB 0xFD, 0xF8", "LD SP, IX", ".DB 0xFD, 0xFA", ".DB 0xFD, 0xFB", ".DB 0xFD, 0xFC", ".DB 0xFD, 0xFD", ".DB 0xFD, 0xFE", ".DB 0xFD, 0xFF" /* 0xF8 */
|
||||
};
|
||||
|
||||
static readonly string[] opcodeMnemonicPrefixFD = new string[]
|
||||
{
|
||||
".DB 0xFD, 0x00", ".DB 0xFD, 0x01", ".DB 0xFD, 0x02", ".DB 0xFD, 0x03", ".DB 0xFD, 0x04", ".DB 0xFD, 0x05", ".DB 0xFD, 0x06", ".DB 0xFD, 0x07", /* 0x00 */
|
||||
".DB 0xFD, 0x08", "ADD IY, BC", ".DB 0xFD, 0x0A", ".DB 0xFD, 0x0B", ".DB 0xFD, 0x0C", ".DB 0xFD, 0x0D", ".DB 0xFD, 0x0E", ".DB 0xFD, 0x0F", /* 0x08 */
|
||||
".DB 0xFD, 0x10", ".DB 0xFD, 0x11", ".DB 0xFD, 0x12", ".DB 0xFD, 0x13", ".DB 0xFD, 0x14", ".DB 0xFD, 0x15", ".DB 0xFD, 0x16", ".DB 0xFD, 0x17", /* 0x10 */
|
||||
".DB 0xFD, 0x18", "ADD IY, DE", ".DB 0xFD, 0x1A", ".DB 0xFD, 0x1B", ".DB 0xFD, 0x1C", ".DB 0xFD, 0x1D", ".DB 0xFD, 0x1E", ".DB 0xFD, 0x1F", /* 0x18 */
|
||||
".DB 0xFD, 0x20", "LD IY, 0x{0:X4}", "LD (0x{0:X4}), IY", "INC IY", ".DB 0xFD, 0x24", ".DB 0xFD, 0x25", ".DB 0xFD, 0x26", ".DB 0xFD, 0x27", /* 0x20 */
|
||||
".DB 0xFD, 0x28", "ADD IY, IY", "LD IY, (0x{0:X4})", "DEC IY", ".DB 0xFD, 0x2C", ".DB 0xFD, 0x2D", ".DB 0xFD, 0x2E", ".DB 0xFD, 0x2F", /* 0x28 */
|
||||
".DB 0xFD, 0x30", ".DB 0xFD, 0x31", ".DB 0xFD, 0x32", ".DB 0xFD, 0x33", "INC (IY+0x{0:X2})", "DEC (IY+0x{0:X2})", "LD (IY+0x{0:X2}), 0x{0:X2}", ".DB 0xFD, 0x37", /* 0x30 */
|
||||
".DB 0xFD, 0x38", "ADD IY, SP", ".DB 0xFD, 0x3A", ".DB 0xFD, 0x3B", ".DB 0xFD, 0x3C", ".DB 0xFD, 0x3D", ".DB 0xFD, 0x3E", ".DB 0xFD, 0x3F", /* 0x38 */
|
||||
".DB 0xFD, 0x40", ".DB 0xFD, 0x41", ".DB 0xFD, 0x42", ".DB 0xFD, 0x43", ".DB 0xFD, 0x44", ".DB 0xFD, 0x45", "LD B, (IY+0x{0:X2})", ".DB 0xFD, 0x47", /* 0x40 */
|
||||
".DB 0xFD, 0x48", ".DB 0xFD, 0x49", ".DB 0xFD, 0x4A", ".DB 0xFD, 0x4B", ".DB 0xFD, 0x4C", ".DB 0xFD, 0x4D", "LD C, (IY+0x{0:X2})", ".DB 0xFD, 0x4F", /* 0x48 */
|
||||
".DB 0xFD, 0x50", ".DB 0xFD, 0x51", ".DB 0xFD, 0x52", ".DB 0xFD, 0x53", ".DB 0xFD, 0x54", ".DB 0xFD, 0x55", "LD D, (IY+0x{0:X2})", ".DB 0xFD, 0x57", /* 0x50 */
|
||||
".DB 0xFD, 0x58", ".DB 0xFD, 0x59", ".DB 0xFD, 0x5A", ".DB 0xFD, 0x5B", ".DB 0xFD, 0x5C", ".DB 0xFD, 0x5D", "LD E, (IY+0x{0:X2})", ".DB 0xFD, 0x5F", /* 0x58 */
|
||||
".DB 0xFD, 0x60", ".DB 0xFD, 0x61", ".DB 0xFD, 0x62", ".DB 0xFD, 0x63", ".DB 0xFD, 0x64", ".DB 0xFD, 0x65", "LD H, (IY+0x{0:X2})", ".DB 0xFD, 0x67", /* 0x60 */
|
||||
".DB 0xFD, 0x68", ".DB 0xFD, 0x69", ".DB 0xFD, 0x6A", ".DB 0xFD, 0x6B", ".DB 0xFD, 0x6C", ".DB 0xFD, 0x6D", "LD L, (IY+0x{0:X2})", ".DB 0xFD, 0x6F", /* 0x68 */
|
||||
"LD (IY+0x{0:X2}), B", "LD (IY+0x{0:X2}), C", "LD (IY+0x{0:X2}), D", "LD (IY+0x{0:X2}), E", "LD (IY+0x{0:X2}), H", "LD (IY+0x{0:X2}), L", ".DB 0xFD, 0x76", "LD (IY+0x{0:X2}), A", /* 0x70 */
|
||||
".DB 0xFD, 0x78", ".DB 0xFD, 0x79", ".DB 0xFD, 0x7A", ".DB 0xFD, 0x7B", ".DB 0xFD, 0x7C", ".DB 0xFD, 0x7D", "LD A, (IY+0x{0:X2})", ".DB 0xFD, 0x7F", /* 0x78 */
|
||||
".DB 0xFD, 0x80", ".DB 0xFD, 0x81", ".DB 0xFD, 0x82", ".DB 0xFD, 0x83", ".DB 0xFD, 0x84", ".DB 0xFD, 0x85", "ADD A, (IY+0x{0:X2})", ".DB 0xFD, 0x87", /* 0x80 */
|
||||
".DB 0xFD, 0x88", ".DB 0xFD, 0x89", ".DB 0xFD, 0x8A", ".DB 0xFD, 0x8B", ".DB 0xFD, 0x8C", ".DB 0xFD, 0x8D", "ADC A, (IY+0x{0:X2})", ".DB 0xFD, 0x8F", /* 0x88 */
|
||||
".DB 0xFD, 0x90", ".DB 0xFD, 0x91", ".DB 0xFD, 0x92", ".DB 0xFD, 0x93", ".DB 0xFD, 0x94", ".DB 0xFD, 0x95", "SUB (IY+0x{0:X2})", ".DB 0xFD, 0x97", /* 0x90 */
|
||||
".DB 0xFD, 0x98", ".DB 0xFD, 0x99", ".DB 0xFD, 0x9A", ".DB 0xFD, 0x9B", ".DB 0xFD, 0x9C", ".DB 0xFD, 0x9D", "SBC (IY+0x{0:X2})", ".DB 0xFD, 0x9F", /* 0x98 */
|
||||
".DB 0xFD, 0xA0", ".DB 0xFD, 0xA1", ".DB 0xFD, 0xA2", ".DB 0xFD, 0xA3", ".DB 0xFD, 0xA4", ".DB 0xFD, 0xA5", "AND (IY+0x{0:X2})", ".DB 0xFD, 0xA7", /* 0xA0 */
|
||||
".DB 0xFD, 0xA8", ".DB 0xFD, 0xA9", ".DB 0xFD, 0xAA", ".DB 0xFD, 0xAB", ".DB 0xFD, 0xAC", ".DB 0xFD, 0xAD", "XOR (IY+0x{0:X2})", ".DB 0xFD, 0xAF", /* 0xA8 */
|
||||
".DB 0xFD, 0xB0", ".DB 0xFD, 0xB1", ".DB 0xFD, 0xB2", ".DB 0xFD, 0xB3", ".DB 0xFD, 0xB4", ".DB 0xFD, 0xB5", "OR (IY+0x{0:X2})", ".DB 0xFD, 0xB7", /* 0xB0 */
|
||||
".DB 0xFD, 0xB8", ".DB 0xFD, 0xB9", ".DB 0xFD, 0xBA", ".DB 0xFD, 0xBB", ".DB 0xFD, 0xBC", ".DB 0xFD, 0xBD", "CP (IY+0x{0:X2})", ".DB 0xFD, 0xBF", /* 0xB8 */
|
||||
".DB 0xFD, 0xC0", ".DB 0xFD, 0xC1", ".DB 0xFD, 0xC2", ".DB 0xFD, 0xC3", ".DB 0xFD, 0xC4", ".DB 0xFD, 0xC5", ".DB 0xFD, 0xC6", ".DB 0xFD, 0xC7", /* 0xC0 */
|
||||
".DB 0xFD, 0xC8", ".DB 0xFD, 0xC9", ".DB 0xFD, 0xCA", string.Empty, ".DB 0xFD, 0xCC", ".DB 0xFD, 0xCD", ".DB 0xFD, 0xCE", ".DB 0xFD, 0xCF", /* 0xC8 */
|
||||
".DB 0xFD, 0xD0", ".DB 0xFD, 0xD1", ".DB 0xFD, 0xD2", ".DB 0xFD, 0xD3", ".DB 0xFD, 0xD4", ".DB 0xFD, 0xD5", ".DB 0xFD, 0xD6", ".DB 0xFD, 0xD7", /* 0xD0 */
|
||||
".DB 0xFD, 0xD8", ".DB 0xFD, 0xD9", ".DB 0xFD, 0xDA", ".DB 0xFD, 0xDB", ".DB 0xFD, 0xDC", ".DB 0xFD, 0xFD", ".DB 0xFD, 0xDE", ".DB 0xFD, 0xDF", /* 0xD8 */
|
||||
".DB 0xED, 0xE0", "POP IY", ".DB 0xED, 0xE2", "EX (SP), IY", ".DB 0xED, 0xE4", "PUSH IY", ".DB 0xED, 0xE6", ".DB 0xED, 0xE7", /* 0xE0 */
|
||||
".DB 0xED, 0xE8", "JP (IY)", ".DB 0xED, 0xEA", ".DB 0xED, 0xEB", ".DB 0xED, 0xEC", ".DB 0xED, 0xED", ".DB 0xED, 0xEE", ".DB 0xED, 0xEF", /* 0xE8 */
|
||||
".DB 0xFD, 0xF0", ".DB 0xFD, 0xF1", ".DB 0xFD, 0xF2", ".DB 0xFD, 0xF3", ".DB 0xFD, 0xF4", ".DB 0xFD, 0xF5", ".DB 0xFD, 0xF6", ".DB 0xFD, 0xF7", /* 0xF0 */
|
||||
".DB 0xFD, 0xF8", "LD SP, IY", ".DB 0xFD, 0xFA", ".DB 0xFD, 0xFB", ".DB 0xFD, 0xFC", ".DB 0xFD, 0xFD", ".DB 0xFD, 0xFE", ".DB 0xFD, 0xFF" /* 0xF8 */
|
||||
};
|
||||
|
||||
static readonly string[] opcodeMnemonicPrefixDDCB = new string[]
|
||||
{
|
||||
".DB 0xDD, 0xCB, 0x00", ".DB 0xDD, 0xCB, 0x01", ".DB 0xDD, 0xCB, 0x02", ".DB 0xDD, 0xCB, 0x03", ".DB 0xDD, 0xCB, 0x04", ".DB 0xDD, 0xCB, 0x05", "RLC (IX+0x{0:X2})", ".DB 0xDD, 0xCB, 0x07", /* 0x00 */
|
||||
".DB 0xDD, 0xCB, 0x08", ".DB 0xDD, 0xCB, 0x09", ".DB 0xDD, 0xCB, 0x0A", ".DB 0xDD, 0xCB, 0x0B", ".DB 0xDD, 0xCB, 0x0C", ".DB 0xDD, 0xCB, 0x0D", "RRC (IX+0x{0:X2})", ".DB 0xDD, 0xCB, 0x0F", /* 0x08 */
|
||||
".DB 0xDD, 0xCB, 0x10", ".DB 0xDD, 0xCB, 0x11", ".DB 0xDD, 0xCB, 0x12", ".DB 0xDD, 0xCB, 0x13", ".DB 0xDD, 0xCB, 0x14", ".DB 0xDD, 0xCB, 0x15", "RL (IX+0x{0:X2})", ".DB 0xDD, 0xCB, 0x17", /* 0x10 */
|
||||
".DB 0xDD, 0xCB, 0x18", ".DB 0xDD, 0xCB, 0x19", ".DB 0xDD, 0xCB, 0x1A", ".DB 0xDD, 0xCB, 0x1B", ".DB 0xDD, 0xCB, 0x1C", ".DB 0xDD, 0xCB, 0x1D", "RR (IX+0x{0:X2})", ".DB 0xDD, 0xCB, 0x1F", /* 0x18 */
|
||||
".DB 0xDD, 0xCB, 0x20", ".DB 0xDD, 0xCB, 0x21", ".DB 0xDD, 0xCB, 0x22", ".DB 0xDD, 0xCB, 0x23", ".DB 0xDD, 0xCB, 0x24", ".DB 0xDD, 0xCB, 0x25", "SLA (IX+0x{0:X2})", ".DB 0xDD, 0xCB, 0x27", /* 0x20 */
|
||||
".DB 0xDD, 0xCB, 0x28", ".DB 0xDD, 0xCB, 0x29", ".DB 0xDD, 0xCB, 0x2A", ".DB 0xDD, 0xCB, 0x2B", ".DB 0xDD, 0xCB, 0x2C", ".DB 0xDD, 0xCB, 0x2D", "SRA (IX+0x{0:X2})", ".DB 0xDD, 0xCB, 0x2F", /* 0x28 */
|
||||
".DB 0xDD, 0xCB, 0x30", ".DB 0xDD, 0xCB, 0x31", ".DB 0xDD, 0xCB, 0x32", ".DB 0xDD, 0xCB, 0x33", ".DB 0xDD, 0xCB, 0x34", ".DB 0xDD, 0xCB, 0x35", "SLL (IX+0x{0:X2})", ".DB 0xDD, 0xCB, 0x37", /* 0x30 */
|
||||
".DB 0xDD, 0xCB, 0x38", ".DB 0xDD, 0xCB, 0x39", ".DB 0xDD, 0xCB, 0x3A", ".DB 0xDD, 0xCB, 0x3B", ".DB 0xDD, 0xCB, 0x3C", ".DB 0xDD, 0xCB, 0x3D", "SRL (IX+0x{0:X2})", ".DB 0xDD, 0xCB, 0x3F", /* 0x38 */
|
||||
".DB 0xDD, 0xCB, 0x40", ".DB 0xDD, 0xCB, 0x41", ".DB 0xDD, 0xCB, 0x42", ".DB 0xDD, 0xCB, 0x43", ".DB 0xDD, 0xCB, 0x44", ".DB 0xDD, 0xCB, 0x45", "BIT 0, (IX+0x{0:X2})", ".DB 0xDD, 0xCB, 0x47", /* 0x40 */
|
||||
".DB 0xDD, 0xCB, 0x48", ".DB 0xDD, 0xCB, 0x49", ".DB 0xDD, 0xCB, 0x4A", ".DB 0xDD, 0xCB, 0x4B", ".DB 0xDD, 0xCB, 0x4C", ".DB 0xDD, 0xCB, 0x4D", "BIT 1, (IX+0x{0:X2})", ".DB 0xDD, 0xCB, 0x4F", /* 0x48 */
|
||||
".DB 0xDD, 0xCB, 0x50", ".DB 0xDD, 0xCB, 0x51", ".DB 0xDD, 0xCB, 0x52", ".DB 0xDD, 0xCB, 0x53", ".DB 0xDD, 0xCB, 0x54", ".DB 0xDD, 0xCB, 0x55", "BIT 2, (IX+0x{0:X2})", ".DB 0xDD, 0xCB, 0x57", /* 0x50 */
|
||||
".DB 0xDD, 0xCB, 0x58", ".DB 0xDD, 0xCB, 0x59", ".DB 0xDD, 0xCB, 0x5A", ".DB 0xDD, 0xCB, 0x5B", ".DB 0xDD, 0xCB, 0x5C", ".DB 0xDD, 0xCB, 0x5D", "BIT 3, (IX+0x{0:X2})", ".DB 0xDD, 0xCB, 0x5F", /* 0x58 */
|
||||
".DB 0xDD, 0xCB, 0x60", ".DB 0xDD, 0xCB, 0x61", ".DB 0xDD, 0xCB, 0x62", ".DB 0xDD, 0xCB, 0x63", ".DB 0xDD, 0xCB, 0x64", ".DB 0xDD, 0xCB, 0x65", "BIT 4, (IX+0x{0:X2})", ".DB 0xDD, 0xCB, 0x67", /* 0x60 */
|
||||
".DB 0xDD, 0xCB, 0x68", ".DB 0xDD, 0xCB, 0x69", ".DB 0xDD, 0xCB, 0x6A", ".DB 0xDD, 0xCB, 0x6B", ".DB 0xDD, 0xCB, 0x6C", ".DB 0xDD, 0xCB, 0x6D", "BIT 5, (IX+0x{0:X2})", ".DB 0xDD, 0xCB, 0x6F", /* 0x68 */
|
||||
".DB 0xDD, 0xCB, 0x70", ".DB 0xDD, 0xCB, 0x71", ".DB 0xDD, 0xCB, 0x72", ".DB 0xDD, 0xCB, 0x73", ".DB 0xDD, 0xCB, 0x74", ".DB 0xDD, 0xCB, 0x75", "BIT 6, (IX+0x{0:X2})", ".DB 0xDD, 0xCB, 0x77", /* 0x70 */
|
||||
".DB 0xDD, 0xCB, 0x78", ".DB 0xDD, 0xCB, 0x79", ".DB 0xDD, 0xCB, 0x7A", ".DB 0xDD, 0xCB, 0x7B", ".DB 0xDD, 0xCB, 0x7C", ".DB 0xDD, 0xCB, 0x7D", "BIT 7, (IX+0x{0:X2})", ".DB 0xDD, 0xCB, 0x7F", /* 0x78 */
|
||||
".DB 0xDD, 0xCB, 0x80", ".DB 0xDD, 0xCB, 0x81", ".DB 0xDD, 0xCB, 0x82", ".DB 0xDD, 0xCB, 0x83", ".DB 0xDD, 0xCB, 0x84", ".DB 0xDD, 0xCB, 0x85", "RES 0, (IX+0x{0:X2})", ".DB 0xDD, 0xCB, 0x87", /* 0x80 */
|
||||
".DB 0xDD, 0xCB, 0x88", ".DB 0xDD, 0xCB, 0x89", ".DB 0xDD, 0xCB, 0x8A", ".DB 0xDD, 0xCB, 0x8B", ".DB 0xDD, 0xCB, 0x8C", ".DB 0xDD, 0xCB, 0x8D", "RES 1, (IX+0x{0:X2})", ".DB 0xDD, 0xCB, 0x8F", /* 0x88 */
|
||||
".DB 0xDD, 0xCB, 0x90", ".DB 0xDD, 0xCB, 0x91", ".DB 0xDD, 0xCB, 0x92", ".DB 0xDD, 0xCB, 0x93", ".DB 0xDD, 0xCB, 0x94", ".DB 0xDD, 0xCB, 0x95", "RES 2, (IX+0x{0:X2})", ".DB 0xDD, 0xCB, 0x97", /* 0x90 */
|
||||
".DB 0xDD, 0xCB, 0x98", ".DB 0xDD, 0xCB, 0x99", ".DB 0xDD, 0xCB, 0x9A", ".DB 0xDD, 0xCB, 0x9B", ".DB 0xDD, 0xCB, 0x9C", ".DB 0xDD, 0xCB, 0x9D", "RES 3, (IX+0x{0:X2})", ".DB 0xDD, 0xCB, 0x9F", /* 0x98 */
|
||||
".DB 0xDD, 0xCB, 0xA0", ".DB 0xDD, 0xCB, 0xA1", ".DB 0xDD, 0xCB, 0xA2", ".DB 0xDD, 0xCB, 0xA3", ".DB 0xDD, 0xCB, 0xA4", ".DB 0xDD, 0xCB, 0xA5", "RES 4, (IX+0x{0:X2})", ".DB 0xDD, 0xCB, 0xA7", /* 0xA0 */
|
||||
".DB 0xDD, 0xCB, 0xA8", ".DB 0xDD, 0xCB, 0xA9", ".DB 0xDD, 0xCB, 0xAA", ".DB 0xDD, 0xCB, 0xAB", ".DB 0xDD, 0xCB, 0xAC", ".DB 0xDD, 0xCB, 0xAD", "RES 5, (IX+0x{0:X2})", ".DB 0xDD, 0xCB, 0xAF", /* 0xA8 */
|
||||
".DB 0xDD, 0xCB, 0xB0", ".DB 0xDD, 0xCB, 0xB1", ".DB 0xDD, 0xCB, 0xB2", ".DB 0xDD, 0xCB, 0xB3", ".DB 0xDD, 0xCB, 0xB4", ".DB 0xDD, 0xCB, 0xB5", "RES 6, (IX+0x{0:X2})", ".DB 0xDD, 0xCB, 0xB7", /* 0xB0 */
|
||||
".DB 0xDD, 0xCB, 0xB8", ".DB 0xDD, 0xCB, 0xB9", ".DB 0xDD, 0xCB, 0xBA", ".DB 0xDD, 0xCB, 0xBB", ".DB 0xDD, 0xCB, 0xBC", ".DB 0xDD, 0xCB, 0xBD", "RES 7, (IX+0x{0:X2})", ".DB 0xDD, 0xCB, 0xBF", /* 0xB8 */
|
||||
".DB 0xDD, 0xCB, 0xC0", ".DB 0xDD, 0xCB, 0xC1", ".DB 0xDD, 0xCB, 0xC2", ".DB 0xDD, 0xCB, 0xC3", ".DB 0xDD, 0xCB, 0xC4", ".DB 0xDD, 0xCB, 0xC5", "SET 0, (IX+0x{0:X2})", ".DB 0xDD, 0xCB, 0xC7", /* 0xC0 */
|
||||
".DB 0xDD, 0xCB, 0xC8", ".DB 0xDD, 0xCB, 0xC9", ".DB 0xDD, 0xCB, 0xCA", ".DB 0xDD, 0xCB, 0xCB", ".DB 0xDD, 0xCB, 0xCC", ".DB 0xDD, 0xCB, 0xCD", "SET 1, (IX+0x{0:X2})", ".DB 0xDD, 0xCB, 0xCF", /* 0xC8 */
|
||||
".DB 0xDD, 0xDB, 0xD0", ".DB 0xDD, 0xDB, 0xD1", ".DB 0xDD, 0xDB, 0xD2", ".DB 0xDD, 0xDB, 0xD3", ".DB 0xDD, 0xDB, 0xD4", ".DB 0xDD, 0xDB, 0xD5", "SET 2, (IX+0x{0:X2})", ".DB 0xDD, 0xDB, 0xD7", /* 0xD0 */
|
||||
".DB 0xDD, 0xDB, 0xD8", ".DB 0xDD, 0xDB, 0xD9", ".DB 0xDD, 0xDB, 0xDA", ".DB 0xDD, 0xDB, 0xDB", ".DB 0xDD, 0xDB, 0xDC", ".DB 0xDD, 0xDB, 0xDD", "SET 3, (IX+0x{0:X2})", ".DB 0xDD, 0xDB, 0xDF", /* 0xD8 */
|
||||
".DB 0xDD, 0xEB, 0xE0", ".DB 0xDD, 0xEB, 0xE1", ".DB 0xDD, 0xEB, 0xE2", ".DB 0xDD, 0xEB, 0xE3", ".DB 0xDD, 0xEB, 0xE4", ".DB 0xDD, 0xEB, 0xE5", "SET 4, (IX+0x{0:X2})", ".DB 0xDD, 0xEB, 0xE7", /* 0xE0 */
|
||||
".DB 0xDD, 0xEB, 0xE8", ".DB 0xDD, 0xEB, 0xE9", ".DB 0xDD, 0xEB, 0xEA", ".DB 0xDD, 0xEB, 0xEB", ".DB 0xDD, 0xEB, 0xEC", ".DB 0xDD, 0xEB, 0xED", "SET 5, (IX+0x{0:X2})", ".DB 0xDD, 0xEB, 0xEF", /* 0xE8 */
|
||||
".DB 0xDD, 0xFB, 0xF0", ".DB 0xDD, 0xFB, 0xF1", ".DB 0xDD, 0xFB, 0xF2", ".DB 0xDD, 0xFB, 0xF3", ".DB 0xDD, 0xFB, 0xF4", ".DB 0xDD, 0xFB, 0xF5", "SET 6, (IX+0x{0:X2})", ".DB 0xDD, 0xFB, 0xF7", /* 0xF0 */
|
||||
".DB 0xDD, 0xFB, 0xF8", ".DB 0xDD, 0xFB, 0xF9", ".DB 0xDD, 0xFB, 0xFA", ".DB 0xDD, 0xFB, 0xFB", ".DB 0xDD, 0xFB, 0xFC", ".DB 0xDD, 0xFB, 0xFD", "SET 7, (IX+0x{0:X2})", ".DB 0xDD, 0xFB, 0xFF" /* 0xF8 */
|
||||
};
|
||||
|
||||
static readonly string[] opcodeMnemonicPrefixFDCB = new string[]
|
||||
{
|
||||
".DB 0xFD, 0xCB, 0x00", ".DB 0xFD, 0xCB, 0x01", ".DB 0xFD, 0xCB, 0x02", ".DB 0xFD, 0xCB, 0x03", ".DB 0xFD, 0xCB, 0x04", ".DB 0xFD, 0xCB, 0x05", "RLC (IX+0x{0:X2})", ".DB 0xFD, 0xCB, 0x07", /* 0x00 */
|
||||
".DB 0xFD, 0xCB, 0x08", ".DB 0xFD, 0xCB, 0x09", ".DB 0xFD, 0xCB, 0x0A", ".DB 0xFD, 0xCB, 0x0B", ".DB 0xFD, 0xCB, 0x0C", ".DB 0xFD, 0xCB, 0x0D", "RRC (IX+0x{0:X2})", ".DB 0xFD, 0xCB, 0x0F", /* 0x08 */
|
||||
".DB 0xFD, 0xCB, 0x10", ".DB 0xFD, 0xCB, 0x11", ".DB 0xFD, 0xCB, 0x12", ".DB 0xFD, 0xCB, 0x13", ".DB 0xFD, 0xCB, 0x14", ".DB 0xFD, 0xCB, 0x15", "RL (IX+0x{0:X2})", ".DB 0xFD, 0xCB, 0x17", /* 0x10 */
|
||||
".DB 0xFD, 0xCB, 0x18", ".DB 0xFD, 0xCB, 0x19", ".DB 0xFD, 0xCB, 0x1A", ".DB 0xFD, 0xCB, 0x1B", ".DB 0xFD, 0xCB, 0x1C", ".DB 0xFD, 0xCB, 0x1D", "RR (IX+0x{0:X2})", ".DB 0xFD, 0xCB, 0x1F", /* 0x18 */
|
||||
".DB 0xFD, 0xCB, 0x20", ".DB 0xFD, 0xCB, 0x21", ".DB 0xFD, 0xCB, 0x22", ".DB 0xFD, 0xCB, 0x23", ".DB 0xFD, 0xCB, 0x24", ".DB 0xFD, 0xCB, 0x25", "SLA (IX+0x{0:X2})", ".DB 0xFD, 0xCB, 0x27", /* 0x20 */
|
||||
".DB 0xFD, 0xCB, 0x28", ".DB 0xFD, 0xCB, 0x29", ".DB 0xFD, 0xCB, 0x2A", ".DB 0xFD, 0xCB, 0x2B", ".DB 0xFD, 0xCB, 0x2C", ".DB 0xFD, 0xCB, 0x2D", "SRA (IX+0x{0:X2})", ".DB 0xFD, 0xCB, 0x2F", /* 0x28 */
|
||||
".DB 0xFD, 0xCB, 0x30", ".DB 0xFD, 0xCB, 0x31", ".DB 0xFD, 0xCB, 0x32", ".DB 0xFD, 0xCB, 0x33", ".DB 0xFD, 0xCB, 0x34", ".DB 0xFD, 0xCB, 0x35", ".DB 0xFD, 0xCB, 0x36", ".DB 0xFD, 0xCB, 0x37", /* 0x30 */
|
||||
".DB 0xFD, 0xCB, 0x38", ".DB 0xFD, 0xCB, 0x39", ".DB 0xFD, 0xCB, 0x3A", ".DB 0xFD, 0xCB, 0x3B", ".DB 0xFD, 0xCB, 0x3C", ".DB 0xFD, 0xCB, 0x3D", "SRL (IX+0x{0:X2})", ".DB 0xFD, 0xCB, 0x3F", /* 0x38 */
|
||||
".DB 0xFD, 0xCB, 0x40", ".DB 0xFD, 0xCB, 0x41", ".DB 0xFD, 0xCB, 0x42", ".DB 0xFD, 0xCB, 0x43", ".DB 0xFD, 0xCB, 0x44", ".DB 0xFD, 0xCB, 0x45", "BIT 0, (IX+0x{0:X2})", ".DB 0xFD, 0xCB, 0x47", /* 0x40 */
|
||||
".DB 0xFD, 0xCB, 0x48", ".DB 0xFD, 0xCB, 0x49", ".DB 0xFD, 0xCB, 0x4A", ".DB 0xFD, 0xCB, 0x4B", ".DB 0xFD, 0xCB, 0x4C", ".DB 0xFD, 0xCB, 0x4D", "BIT 1, (IX+0x{0:X2})", ".DB 0xFD, 0xCB, 0x4F", /* 0x48 */
|
||||
".DB 0xFD, 0xCB, 0x50", ".DB 0xFD, 0xCB, 0x51", ".DB 0xFD, 0xCB, 0x52", ".DB 0xFD, 0xCB, 0x53", ".DB 0xFD, 0xCB, 0x54", ".DB 0xFD, 0xCB, 0x55", "BIT 2, (IX+0x{0:X2})", ".DB 0xFD, 0xCB, 0x57", /* 0x50 */
|
||||
".DB 0xFD, 0xCB, 0x58", ".DB 0xFD, 0xCB, 0x59", ".DB 0xFD, 0xCB, 0x5A", ".DB 0xFD, 0xCB, 0x5B", ".DB 0xFD, 0xCB, 0x5C", ".DB 0xFD, 0xCB, 0x5D", "BIT 3, (IX+0x{0:X2})", ".DB 0xFD, 0xCB, 0x5F", /* 0x58 */
|
||||
".DB 0xFD, 0xCB, 0x60", ".DB 0xFD, 0xCB, 0x61", ".DB 0xFD, 0xCB, 0x62", ".DB 0xFD, 0xCB, 0x63", ".DB 0xFD, 0xCB, 0x64", ".DB 0xFD, 0xCB, 0x65", "BIT 4, (IX+0x{0:X2})", ".DB 0xFD, 0xCB, 0x67", /* 0x60 */
|
||||
".DB 0xFD, 0xCB, 0x68", ".DB 0xFD, 0xCB, 0x69", ".DB 0xFD, 0xCB, 0x6A", ".DB 0xFD, 0xCB, 0x6B", ".DB 0xFD, 0xCB, 0x6C", ".DB 0xFD, 0xCB, 0x6D", "BIT 5, (IX+0x{0:X2})", ".DB 0xFD, 0xCB, 0x6F", /* 0x68 */
|
||||
".DB 0xFD, 0xCB, 0x70", ".DB 0xFD, 0xCB, 0x71", ".DB 0xFD, 0xCB, 0x72", ".DB 0xFD, 0xCB, 0x73", ".DB 0xFD, 0xCB, 0x74", ".DB 0xFD, 0xCB, 0x75", "BIT 6, (IX+0x{0:X2})", ".DB 0xFD, 0xCB, 0x77", /* 0x70 */
|
||||
".DB 0xFD, 0xCB, 0x78", ".DB 0xFD, 0xCB, 0x79", ".DB 0xFD, 0xCB, 0x7A", ".DB 0xFD, 0xCB, 0x7B", ".DB 0xFD, 0xCB, 0x7C", ".DB 0xFD, 0xCB, 0x7D", "BIT 7, (IX+0x{0:X2})", ".DB 0xFD, 0xCB, 0x7F", /* 0x78 */
|
||||
".DB 0xFD, 0xCB, 0x80", ".DB 0xFD, 0xCB, 0x81", ".DB 0xFD, 0xCB, 0x82", ".DB 0xFD, 0xCB, 0x83", ".DB 0xFD, 0xCB, 0x84", ".DB 0xFD, 0xCB, 0x85", "RES 0, (IX+0x{0:X2})", ".DB 0xFD, 0xCB, 0x87", /* 0x80 */
|
||||
".DB 0xFD, 0xCB, 0x88", ".DB 0xFD, 0xCB, 0x89", ".DB 0xFD, 0xCB, 0x8A", ".DB 0xFD, 0xCB, 0x8B", ".DB 0xFD, 0xCB, 0x8C", ".DB 0xFD, 0xCB, 0x8D", "RES 1, (IX+0x{0:X2})", ".DB 0xFD, 0xCB, 0x8F", /* 0x88 */
|
||||
".DB 0xFD, 0xCB, 0x90", ".DB 0xFD, 0xCB, 0x91", ".DB 0xFD, 0xCB, 0x92", ".DB 0xFD, 0xCB, 0x93", ".DB 0xFD, 0xCB, 0x94", ".DB 0xFD, 0xCB, 0x95", "RES 2, (IX+0x{0:X2})", ".DB 0xFD, 0xCB, 0x97", /* 0x90 */
|
||||
".DB 0xFD, 0xCB, 0x98", ".DB 0xFD, 0xCB, 0x99", ".DB 0xFD, 0xCB, 0x9A", ".DB 0xFD, 0xCB, 0x9B", ".DB 0xFD, 0xCB, 0x9C", ".DB 0xFD, 0xCB, 0x9D", "RES 3, (IX+0x{0:X2})", ".DB 0xFD, 0xCB, 0x9F", /* 0x98 */
|
||||
".DB 0xFD, 0xCB, 0xA0", ".DB 0xFD, 0xCB, 0xA1", ".DB 0xFD, 0xCB, 0xA2", ".DB 0xFD, 0xCB, 0xA3", ".DB 0xFD, 0xCB, 0xA4", ".DB 0xFD, 0xCB, 0xA5", "RES 4, (IX+0x{0:X2})", ".DB 0xFD, 0xCB, 0xA7", /* 0xA0 */
|
||||
".DB 0xFD, 0xCB, 0xA8", ".DB 0xFD, 0xCB, 0xA9", ".DB 0xFD, 0xCB, 0xAA", ".DB 0xFD, 0xCB, 0xAB", ".DB 0xFD, 0xCB, 0xAC", ".DB 0xFD, 0xCB, 0xAD", "RES 5, (IX+0x{0:X2})", ".DB 0xFD, 0xCB, 0xAF", /* 0xA8 */
|
||||
".DB 0xFD, 0xCB, 0xB0", ".DB 0xFD, 0xCB, 0xB1", ".DB 0xFD, 0xCB, 0xB2", ".DB 0xFD, 0xCB, 0xB3", ".DB 0xFD, 0xCB, 0xB4", ".DB 0xFD, 0xCB, 0xB5", "RES 6, (IX+0x{0:X2})", ".DB 0xFD, 0xCB, 0xB7", /* 0xB0 */
|
||||
".DB 0xFD, 0xCB, 0xB8", ".DB 0xFD, 0xCB, 0xB9", ".DB 0xFD, 0xCB, 0xBA", ".DB 0xFD, 0xCB, 0xBB", ".DB 0xFD, 0xCB, 0xBC", ".DB 0xFD, 0xCB, 0xBD", "RES 7, (IX+0x{0:X2})", ".DB 0xFD, 0xCB, 0xBF", /* 0xB8 */
|
||||
".DB 0xFD, 0xCB, 0xC0", ".DB 0xFD, 0xCB, 0xC1", ".DB 0xFD, 0xCB, 0xC2", ".DB 0xFD, 0xCB, 0xC3", ".DB 0xFD, 0xCB, 0xC4", ".DB 0xFD, 0xCB, 0xC5", "SET 0, (IX+0x{0:X2})", ".DB 0xFD, 0xCB, 0xC7", /* 0xC0 */
|
||||
".DB 0xFD, 0xCB, 0xC8", ".DB 0xFD, 0xCB, 0xC9", ".DB 0xFD, 0xCB, 0xCA", ".DB 0xFD, 0xCB, 0xCB", ".DB 0xFD, 0xCB, 0xCC", ".DB 0xFD, 0xCB, 0xCD", "SET 1, (IX+0x{0:X2})", ".DB 0xFD, 0xCB, 0xCF", /* 0xC8 */
|
||||
".DB 0xFD, 0xDB, 0xD0", ".DB 0xFD, 0xDB, 0xD1", ".DB 0xFD, 0xDB, 0xD2", ".DB 0xFD, 0xDB, 0xD3", ".DB 0xFD, 0xDB, 0xD4", ".DB 0xFD, 0xDB, 0xD5", "SET 2, (IX+0x{0:X2})", ".DB 0xFD, 0xDB, 0xD7", /* 0xD0 */
|
||||
".DB 0xFD, 0xDB, 0xD8", ".DB 0xFD, 0xDB, 0xD9", ".DB 0xFD, 0xDB, 0xDA", ".DB 0xFD, 0xDB, 0xDB", ".DB 0xFD, 0xDB, 0xDC", ".DB 0xFD, 0xDB, 0xFD", "SET 3, (IX+0x{0:X2})", ".DB 0xFD, 0xDB, 0xDF", /* 0xD8 */
|
||||
".DB 0xFD, 0xEB, 0xE0", ".DB 0xFD, 0xEB, 0xE1", ".DB 0xFD, 0xEB, 0xE2", ".DB 0xFD, 0xEB, 0xE3", ".DB 0xFD, 0xEB, 0xE4", ".DB 0xFD, 0xEB, 0xE5", "SET 4, (IX+0x{0:X2})", ".DB 0xFD, 0xEB, 0xE7", /* 0xE0 */
|
||||
".DB 0xFD, 0xEB, 0xE8", ".DB 0xFD, 0xEB, 0xE9", ".DB 0xFD, 0xEB, 0xEA", ".DB 0xFD, 0xEB, 0xEB", ".DB 0xFD, 0xEB, 0xEC", ".DB 0xFD, 0xEB, 0xED", "SET 5, (IX+0x{0:X2})", ".DB 0xFD, 0xEB, 0xEF", /* 0xE8 */
|
||||
".DB 0xFD, 0xFB, 0xF0", ".DB 0xFD, 0xFB, 0xF1", ".DB 0xFD, 0xFB, 0xF2", ".DB 0xFD, 0xFB, 0xF3", ".DB 0xFD, 0xFB, 0xF4", ".DB 0xFD, 0xFB, 0xF5", "SET 6, (IX+0x{0:X2})", ".DB 0xFD, 0xFB, 0xF7", /* 0xF0 */
|
||||
".DB 0xFD, 0xFB, 0xF8", ".DB 0xFD, 0xFB, 0xF9", ".DB 0xFD, 0xFB, 0xFA", ".DB 0xFD, 0xFB, 0xFB", ".DB 0xFD, 0xFB, 0xFC", ".DB 0xFD, 0xFB, 0xFD", "SET 7, (IX+0x{0:X2})", ".DB 0xFD, 0xFB, 0xFF" /* 0xF8 */
|
||||
};
|
||||
|
||||
static readonly int[] opcodeLengthPrefixDDFD = new int[]
|
||||
{
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 4, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 3, 3, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2,
|
||||
2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2,
|
||||
2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2,
|
||||
3, 3, 3, 3, 3, 3, 2, 3, 2, 2, 2, 2, 2, 2, 3, 2,
|
||||
2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2,
|
||||
2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2,
|
||||
2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2,
|
||||
2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -1, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
};
|
||||
|
||||
static readonly int[] opcodeLengthPrefixDDFDCB = new int[]
|
||||
{
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
};
|
||||
|
||||
public static string PrintRegisters(Z80A cpu)
|
||||
{
|
||||
return string.Format("AF:{0:X4} BC:{1:X4} DE:{2:X4} HL:{3:X4} IX:{4:X4} IY:{5:X4} SP:{6:X4}", cpu.af.Word, cpu.bc.Word, cpu.de.Word, cpu.hl.Word, cpu.ix.Word, cpu.iy.Word, cpu.sp);
|
||||
}
|
||||
|
||||
public static string PrintFlags(Z80A cpu)
|
||||
{
|
||||
return string.Format("[{7}{6}{5}{4}{3}{2}{1}{0}]",
|
||||
cpu.IsFlagSet(Flags.Carry) ? "C" : "-",
|
||||
cpu.IsFlagSet(Flags.Subtract) ? "N" : "-",
|
||||
cpu.IsFlagSet(Flags.ParityOrOverflow) ? "P" : "-",
|
||||
cpu.IsFlagSet(Flags.UnusedBitX) ? "X" : "-",
|
||||
cpu.IsFlagSet(Flags.HalfCarry) ? "H" : "-",
|
||||
cpu.IsFlagSet(Flags.UnusedBitY) ? "Y" : "-",
|
||||
cpu.IsFlagSet(Flags.Zero) ? "Z" : "-",
|
||||
cpu.IsFlagSet(Flags.Sign) ? "S" : "-");
|
||||
}
|
||||
|
||||
public static string PrintInterrupt(Z80A cpu)
|
||||
{
|
||||
return string.Format("[IM{0} {1} {2} {3}]", cpu.im, (cpu.iff1 ? "EI" : "DI"), (cpu.halt ? "HLT" : "---"), (cpu.intState == InterruptState.Assert ? "ASR" : "---"));
|
||||
}
|
||||
|
||||
public static string DisassembleOpcode(Z80A cpu, ushort address)
|
||||
{
|
||||
byte[] opcode = DisassembleGetOpcodeBytes(cpu, address);
|
||||
return string.Format("0x{0:X4} | {1} | {2}", address, DisassembleMakeByteString(cpu, opcode).PadRight(15), DisassembleMakeMnemonicString(cpu, opcode));
|
||||
}
|
||||
|
||||
public static byte[] DisassembleGetOpcodeBytes(Z80A cpu, ushort address)
|
||||
{
|
||||
byte[] opcode = new byte[5];
|
||||
for (int i = 0; i < opcode.Length; i++)
|
||||
opcode[i] = (address + i <= 0xFFFF ? cpu.ReadMemory8((ushort)(address + i)) : (byte)0);
|
||||
return opcode;
|
||||
}
|
||||
|
||||
public static int DisassembleGetOpcodeLen(Z80A cpu, byte[] opcode)
|
||||
{
|
||||
switch (opcode[0])
|
||||
{
|
||||
case 0xCB: return opcodeLength_CB[opcode[1]];
|
||||
case 0xED: return opcodeLengthPrefixED[opcode[1]];
|
||||
|
||||
case 0xDD:
|
||||
case 0xFD:
|
||||
if (opcode[1] == 0xCB)
|
||||
return opcodeLengthPrefixDDFDCB[opcode[3]];
|
||||
else
|
||||
return opcodeLengthPrefixDDFD[opcode[1]];
|
||||
|
||||
default: return opcodeLengthNoPrefix[opcode[0]];
|
||||
}
|
||||
}
|
||||
|
||||
public static string DisassembleMakeByteString(Z80A cpu, byte[] opcode)
|
||||
{
|
||||
return string.Join(" ", opcode.Select(x => string.Format("{0:X2}", x)).Take(DisassembleGetOpcodeLen(cpu, opcode)));
|
||||
}
|
||||
|
||||
public static string DisassembleMakeMnemonicString(Z80A cpu, byte[] opcode)
|
||||
{
|
||||
int len = DisassembleGetOpcodeLen(cpu, opcode);
|
||||
|
||||
int start = 0;
|
||||
string[] mnemonics = opcodeMnemonicNoPrefix;
|
||||
bool isDDFDCB = false;
|
||||
|
||||
switch (opcode[0])
|
||||
{
|
||||
case 0xCB: start = 1; mnemonics = opcodeMnemonicPrefixCB; break;
|
||||
case 0xED: start = 1; mnemonics = opcodeMnemonicPrefixED; break;
|
||||
|
||||
case 0xDD:
|
||||
if (opcode[1] == 0xCB)
|
||||
{
|
||||
mnemonics = opcodeMnemonicPrefixDDCB;
|
||||
isDDFDCB = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
start = 1;
|
||||
mnemonics = opcodeMnemonicPrefixDD;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xFD:
|
||||
if (opcode[1] == 0xCB)
|
||||
{
|
||||
mnemonics = opcodeMnemonicPrefixFDCB;
|
||||
isDDFDCB = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
start = 1;
|
||||
mnemonics = opcodeMnemonicPrefixFD;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (mnemonics == null) return "(unimplemented)";
|
||||
|
||||
if (!isDDFDCB)
|
||||
{
|
||||
switch (len - start)
|
||||
{
|
||||
case 1: return mnemonics[opcode[start]];
|
||||
case 2: return string.Format(mnemonics[opcode[start]], opcode[start + 1]);
|
||||
case 3: return string.Format(mnemonics[opcode[start]], (opcode[start + 2] << 8 | opcode[start + 1]));
|
||||
default: return string.Empty;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.Format(mnemonics[opcode[3]], opcode[2]);
|
||||
}
|
||||
}
|
||||
|
||||
private string MakeUnimplementedOpcodeString(string prefix, ushort address)
|
||||
{
|
||||
byte[] opcode = DisassembleGetOpcodeBytes(this, address);
|
||||
return string.Format("Unimplemented {0}opcode {1} ({2})", (prefix != string.Empty ? prefix + " " : prefix), DisassembleMakeByteString(this, opcode), DisassembleMakeMnemonicString(this, opcode));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9fa152db63418e74ab7f359175172290
|
@ -0,0 +1,283 @@
|
||||
using static Essgee.Emulation.Utilities;
|
||||
|
||||
namespace Essgee.Emulation.CPU
|
||||
{
|
||||
public partial class Z80A
|
||||
{
|
||||
static SimpleOpcodeDelegate[] opcodesNoPrefix = new SimpleOpcodeDelegate[]
|
||||
{
|
||||
/* 0x00 */
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP */ }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegisterImmediate16(ref c.bc.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadMemory8(c.bc.Word, c.af.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Increment16(ref c.bc.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Increment8(ref c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Decrement8(ref c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegisterImmediate8(ref c.bc.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeftAccumulatorCircular(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ExchangeRegisters16(ref c.af, ref c.af_); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add16(ref c.hl, c.bc.Word, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegisterFromMemory8(ref c.af.High, c.bc.Word, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Decrement16(ref c.bc.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Increment8(ref c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Decrement8(ref c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegisterImmediate8(ref c.bc.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRightAccumulatorCircular(); }),
|
||||
/* 0x10 */
|
||||
new SimpleOpcodeDelegate((c) => { c.DecrementJumpNonZero(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegisterImmediate16(ref c.de.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadMemory8(c.de.Word, c.af.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Increment16(ref c.de.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Increment8(ref c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Decrement8(ref c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegisterImmediate8(ref c.de.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeftAccumulator(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Jump8(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add16(ref c.hl, c.de.Word, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegisterFromMemory8(ref c.af.High, c.de.Word, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Decrement16(ref c.de.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Increment8(ref c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Decrement8(ref c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegisterImmediate8(ref c.de.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRightAccumulator(); }),
|
||||
/* 0x20 */
|
||||
new SimpleOpcodeDelegate((c) => { c.JumpConditional8(!c.IsFlagSet(Flags.Zero)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegisterImmediate16(ref c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadMemory16(c.ReadMemory16(c.pc), c.hl.Word); c.pc += 2; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Increment16(ref c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Increment8(ref c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Decrement8(ref c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegisterImmediate8(ref c.hl.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.DecimalAdjustAccumulator(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.JumpConditional8(c.IsFlagSet(Flags.Zero)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add16(ref c.hl, c.hl.Word, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister16(ref c.hl.Word, c.ReadMemory16(c.ReadMemory16(c.pc))); c.pc += 2; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Decrement16(ref c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Increment8(ref c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Decrement8(ref c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegisterImmediate8(ref c.hl.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.af.High ^= 0xFF; c.SetFlag(Flags.Subtract | Flags.HalfCarry); c.SetClearFlagConditional(Flags.UnusedBitY, IsBitSet(c.af.High, 5)); c.SetClearFlagConditional(Flags.UnusedBitX, IsBitSet(c.af.High, 3)); }),
|
||||
/* 0x30 */
|
||||
new SimpleOpcodeDelegate((c) => { c.JumpConditional8(!c.IsFlagSet(Flags.Carry)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegisterImmediate16(ref c.sp); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadMemory8(c.ReadMemory16(c.pc), c.af.High); c.pc += 2; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Increment16(ref c.sp); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.IncrementMemory8(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.DecrementMemory8(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadMemory8(c.hl.Word, c.ReadMemory8(c.pc++)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetFlag(Flags.Carry); c.ClearFlag(Flags.Subtract | Flags.HalfCarry); c.SetClearFlagConditional(Flags.UnusedBitY, IsBitSet(c.af.High, 5)); c.SetClearFlagConditional(Flags.UnusedBitX, IsBitSet(c.af.High, 3)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.JumpConditional8(c.IsFlagSet(Flags.Carry)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add16(ref c.hl, c.sp, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegisterFromMemory8(ref c.af.High, c.ReadMemory16(c.pc), false); c.pc += 2; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Decrement16(ref c.sp); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Increment8(ref c.af.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Decrement8(ref c.af.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegisterImmediate8(ref c.af.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetClearFlagConditional(Flags.HalfCarry, c.IsFlagSet(Flags.Carry)); c.SetClearFlagConditional(Flags.Carry, !c.IsFlagSet(Flags.Carry)); c.ClearFlag(Flags.Subtract); c.SetClearFlagConditional(Flags.UnusedBitY, IsBitSet(c.af.High, 5)); c.SetClearFlagConditional(Flags.UnusedBitX, IsBitSet(c.af.High, 3)); }),
|
||||
/* 0x40 */
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.bc.High, c.bc.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.bc.High, c.bc.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.bc.High, c.de.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.bc.High, c.de.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.bc.High, c.hl.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.bc.High, c.hl.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.bc.High = c.ReadMemory8(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.bc.High, c.af.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.bc.Low, c.bc.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.bc.Low, c.bc.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.bc.Low, c.de.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.bc.Low, c.de.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.bc.Low, c.hl.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.bc.Low, c.hl.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.bc.Low = c.ReadMemory8(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.bc.Low, c.af.High, false); }),
|
||||
/* 0x50 */
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.de.High, c.bc.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.de.High, c.bc.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.de.High, c.de.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.de.High, c.de.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.de.High, c.hl.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.de.High, c.hl.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.de.High = c.ReadMemory8(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.de.High, c.af.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.de.Low, c.bc.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.de.Low, c.bc.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.de.Low, c.de.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.de.Low, c.de.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.de.Low, c.hl.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.de.Low, c.hl.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.de.Low = c.ReadMemory8(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.de.Low, c.af.High, false); }),
|
||||
/* 0x60 */
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.hl.High, c.bc.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.hl.High, c.bc.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.hl.High, c.de.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.hl.High, c.de.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.hl.High, c.hl.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.hl.High, c.hl.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.hl.High = c.ReadMemory8(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.hl.High, c.af.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.hl.Low, c.bc.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.hl.Low, c.bc.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.hl.Low, c.de.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.hl.Low, c.de.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.hl.Low, c.hl.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.hl.Low, c.hl.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.hl.Low = c.ReadMemory8(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.hl.Low, c.af.High, false); }),
|
||||
/* 0x70 */
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadMemory8(c.hl.Word, c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadMemory8(c.hl.Word, c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadMemory8(c.hl.Word, c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadMemory8(c.hl.Word, c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadMemory8(c.hl.Word, c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadMemory8(c.hl.Word, c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.EnterHaltState(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadMemory8(c.hl.Word, c.af.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.af.High, c.bc.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.af.High, c.bc.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.af.High, c.de.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.af.High, c.de.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.af.High, c.hl.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.af.High, c.hl.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.af.High = c.ReadMemory8(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.af.High, c.af.High, false); }),
|
||||
/* 0x80 */
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.bc.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.bc.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.de.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.de.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.hl.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.hl.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.ReadMemory8(c.hl.Word), false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.af.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.bc.High, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.bc.Low, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.de.High, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.de.Low, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.hl.High, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.hl.Low, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.ReadMemory8(c.hl.Word), true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.af.High, true); }),
|
||||
/* 0x90 */
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.bc.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.bc.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.de.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.de.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.hl.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.hl.Low, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.ReadMemory8(c.hl.Word), false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.af.High, false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.bc.High, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.bc.Low, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.de.High, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.de.Low, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.hl.High, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.hl.Low, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.ReadMemory8(c.hl.Word), true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.af.High, true); }),
|
||||
/* 0xA0 */
|
||||
new SimpleOpcodeDelegate((c) => { c.And8(c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.And8(c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.And8(c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.And8(c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.And8(c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.And8(c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.And8(c.ReadMemory8(c.hl.Word)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.And8(c.af.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Xor8(c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Xor8(c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Xor8(c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Xor8(c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Xor8(c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Xor8(c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Xor8(c.ReadMemory8(c.hl.Word)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Xor8(c.af.High); }),
|
||||
/* 0xB0 */
|
||||
new SimpleOpcodeDelegate((c) => { c.Or8(c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Or8(c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Or8(c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Or8(c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Or8(c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Or8(c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Or8(c.ReadMemory8(c.hl.Word)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Or8(c.af.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Cp8(c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Cp8(c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Cp8(c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Cp8(c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Cp8(c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Cp8(c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Cp8(c.ReadMemory8(c.hl.Word)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Cp8(c.af.High); }),
|
||||
/* 0xC0 */
|
||||
new SimpleOpcodeDelegate((c) => { c.ReturnConditional(!c.IsFlagSet(Flags.Zero)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Pop(ref c.bc); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.JumpConditional16(!c.IsFlagSet(Flags.Zero)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.JumpConditional16(true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.CallConditional16(!c.IsFlagSet(Flags.Zero)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Push(c.bc); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.ReadMemory8(c.pc++), false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Restart(0x0000); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ReturnConditional(c.IsFlagSet(Flags.Zero)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Return(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.JumpConditional16(c.IsFlagSet(Flags.Zero)); }),
|
||||
new SimpleOpcodeDelegate((c) => { /* CB - handled elsewhere */ }),
|
||||
new SimpleOpcodeDelegate((c) => { c.CallConditional16(c.IsFlagSet(Flags.Zero)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Call16(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add8(c.ReadMemory8(c.pc++), true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Restart(0x0008); }),
|
||||
/* 0xD0 */
|
||||
new SimpleOpcodeDelegate((c) => { c.ReturnConditional(!c.IsFlagSet(Flags.Carry)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Pop(ref c.de); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.JumpConditional16(!c.IsFlagSet(Flags.Carry)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.WritePort(c.ReadMemory8(c.pc++), c.af.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.CallConditional16(!c.IsFlagSet(Flags.Carry)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Push(c.de); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.ReadMemory8(c.pc++), false); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Restart(0x0010); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ReturnConditional(c.IsFlagSet(Flags.Carry)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ExchangeRegisters16(ref c.bc, ref c.bc_); c.ExchangeRegisters16(ref c.de, ref c.de_); c.ExchangeRegisters16(ref c.hl, ref c.hl_); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.JumpConditional16(c.IsFlagSet(Flags.Carry)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.af.High = c.ReadPort(c.ReadMemory8(c.pc++)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.CallConditional16(c.IsFlagSet(Flags.Carry)); }),
|
||||
new SimpleOpcodeDelegate((c) => { /* DD - handled elsewhere */ }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract8(c.ReadMemory8(c.pc++), true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Restart(0x0018); }),
|
||||
/* 0xE0 */
|
||||
new SimpleOpcodeDelegate((c) => { c.ReturnConditional(!c.IsFlagSet(Flags.ParityOrOverflow)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Pop(ref c.hl); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.JumpConditional16(!c.IsFlagSet(Flags.ParityOrOverflow)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ExchangeStackRegister16(ref c.hl); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.CallConditional16(!c.IsFlagSet(Flags.ParityOrOverflow)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Push(c.hl); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.And8(c.ReadMemory8(c.pc++)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Restart(0x0020); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ReturnConditional(c.IsFlagSet(Flags.ParityOrOverflow)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.pc = c.hl.Word; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.JumpConditional16(c.IsFlagSet(Flags.ParityOrOverflow)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ExchangeRegisters16(ref c.de, ref c.hl); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.CallConditional16(c.IsFlagSet(Flags.ParityOrOverflow)); }),
|
||||
new SimpleOpcodeDelegate((c) => { /* ED - handled elsewhere */ }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Xor8(c.ReadMemory8(c.pc++)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Restart(0x0028); }),
|
||||
/* 0xF0 */
|
||||
new SimpleOpcodeDelegate((c) => { c.ReturnConditional(!c.IsFlagSet(Flags.Sign)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Pop(ref c.af); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.JumpConditional16(!c.IsFlagSet(Flags.Sign)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.iff1 = c.iff2 = false; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.CallConditional16(!c.IsFlagSet(Flags.Sign)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Push(c.af); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Or8(c.ReadMemory8(c.pc++)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Restart(0x0030); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ReturnConditional(c.IsFlagSet(Flags.Sign)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.sp = c.hl.Word; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.JumpConditional16(c.IsFlagSet(Flags.Sign)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.eiDelay = true; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.CallConditional16(c.IsFlagSet(Flags.Sign)); }),
|
||||
new SimpleOpcodeDelegate((c) => { /* FD - handled elsewhere */ }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Cp8(c.ReadMemory8(c.pc++)); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Restart(0x0038); })
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a8c690c6cf2d024418c1412af53e453f
|
@ -0,0 +1,281 @@
|
||||
namespace Essgee.Emulation.CPU
|
||||
{
|
||||
public partial class Z80A
|
||||
{
|
||||
static SimpleOpcodeDelegate[] opcodesPrefixCB = new SimpleOpcodeDelegate[]
|
||||
{
|
||||
/* 0x00 */
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeftCircular(ref c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeftCircular(ref c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeftCircular(ref c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeftCircular(ref c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeftCircular(ref c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeftCircular(ref c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeftCircular(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeftCircular(ref c.af.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRightCircular(ref c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRightCircular(ref c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRightCircular(ref c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRightCircular(ref c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRightCircular(ref c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRightCircular(ref c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRightCircular(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRightCircular(ref c.af.High); }),
|
||||
/* 0x10 */
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeft(ref c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeft(ref c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeft(ref c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeft(ref c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeft(ref c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeft(ref c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeft(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeft(ref c.af.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRight(ref c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRight(ref c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRight(ref c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRight(ref c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRight(ref c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRight(ref c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRight(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRight(ref c.af.High); }),
|
||||
/* 0x20 */
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftLeftArithmetic(ref c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftLeftArithmetic(ref c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftLeftArithmetic(ref c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftLeftArithmetic(ref c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftLeftArithmetic(ref c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftLeftArithmetic(ref c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftLeftArithmetic(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftLeftArithmetic(ref c.af.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightArithmetic(ref c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightArithmetic(ref c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightArithmetic(ref c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightArithmetic(ref c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightArithmetic(ref c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightArithmetic(ref c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightArithmetic(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightArithmetic(ref c.af.High); }),
|
||||
/* 0x30 */
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftLeftLogical(ref c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftLeftLogical(ref c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftLeftLogical(ref c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftLeftLogical(ref c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftLeftLogical(ref c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftLeftLogical(ref c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftLeftLogical(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftLeftLogical(ref c.af.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightLogical(ref c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightLogical(ref c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightLogical(ref c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightLogical(ref c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightLogical(ref c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightLogical(ref c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightLogical(c.hl.Word); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ShiftRightLogical(ref c.af.High); }),
|
||||
/* 0x40 */
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.High, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.Low, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.High, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.Low, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.High, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Low, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Word, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.af.High, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.High, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.Low, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.High, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.Low, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.High, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Low, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Word, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.af.High, 1); }),
|
||||
/* 0x50 */
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.High, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.Low, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.High, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.Low, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.High, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Low, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Word, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.af.High, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.High, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.Low, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.High, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.Low, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.High, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Low, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Word, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.af.High, 3); }),
|
||||
/* 0x60 */
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.High, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.Low, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.High, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.Low, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.High, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Low, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Word, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.af.High, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.High, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.Low, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.High, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.Low, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.High, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Low, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Word, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.af.High, 5); }),
|
||||
/* 0x70 */
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.High, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.Low, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.High, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.Low, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.High, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Low, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Word, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.af.High, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.High, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.bc.Low, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.High, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.de.Low, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.High, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Low, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.hl.Word, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.TestBit(c.af.High, 7); }),
|
||||
/* 0x80 */
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.High, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.Low, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.High, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.Low, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.High, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.Low, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(c.hl.Word, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.af.High, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.High, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.Low, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.High, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.Low, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.High, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.Low, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(c.hl.Word, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.af.High, 1); }),
|
||||
/* 0x90 */
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.High, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.Low, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.High, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.Low, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.High, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.Low, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(c.hl.Word, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.af.High, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.High, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.Low, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.High, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.Low, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.High, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.Low, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(c.hl.Word, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.af.High, 3); }),
|
||||
/* 0xA0 */
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.High, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.Low, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.High, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.Low, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.High, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.Low, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(c.hl.Word, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.af.High, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.High, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.Low, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.High, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.Low, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.High, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.Low, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(c.hl.Word, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.af.High, 5); }),
|
||||
/* 0xB0 */
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.High, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.Low, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.High, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.Low, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.High, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.Low, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(c.hl.Word, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.af.High, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.High, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.bc.Low, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.High, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.de.Low, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.High, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.hl.Low, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(c.hl.Word, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.ResetBit(ref c.af.High, 7); }),
|
||||
/* 0xC0 */
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.High, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.Low, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.High, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.Low, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.High, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.Low, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(c.hl.Word, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.af.High, 0); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.High, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.Low, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.High, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.Low, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.High, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.Low, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(c.hl.Word, 1); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.af.High, 1); }),
|
||||
/* 0xD0 */
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.High, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.Low, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.High, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.Low, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.High, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.Low, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(c.hl.Word, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.af.High, 2); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.High, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.Low, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.High, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.Low, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.High, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.Low, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(c.hl.Word, 3); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.af.High, 3); }),
|
||||
/* 0xE0 */
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.High, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.Low, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.High, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.Low, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.High, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.Low, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(c.hl.Word, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.af.High, 4); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.High, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.Low, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.High, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.Low, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.High, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.Low, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(c.hl.Word, 5); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.af.High, 5); }),
|
||||
/* 0xF0 */
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.High, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.Low, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.High, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.Low, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.High, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.Low, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(c.hl.Word, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.af.High, 6); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.High, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.bc.Low, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.High, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.de.Low, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.High, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.hl.Low, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(c.hl.Word, 7); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.SetBit(ref c.af.High, 7); }),
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1eaf728772256b548a5dd0f76aac95a8
|
@ -0,0 +1,281 @@
|
||||
namespace Essgee.Emulation.CPU
|
||||
{
|
||||
public partial class Z80A
|
||||
{
|
||||
static DDFDOpcodeDelegate[] opcodesPrefixDDFD = new DDFDOpcodeDelegate[]
|
||||
{
|
||||
/* 0x00 */
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { /* NOP */ }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegisterImmediate16(ref c.bc.Word); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadMemory8(c.bc.Word, c.af.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Increment16(ref c.bc.Word); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Increment8(ref c.bc.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Decrement8(ref c.bc.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegisterImmediate8(ref c.bc.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.RotateLeftAccumulatorCircular(); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.ExchangeRegisters16(ref c.af, ref c.af_); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Add16(ref r, c.bc.Word, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegisterFromMemory8(ref c.af.High, c.bc.Word, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Decrement16(ref c.bc.Word); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Increment8(ref c.bc.Low); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Decrement8(ref c.bc.Low); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegisterImmediate8(ref c.bc.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.RotateRightAccumulatorCircular(); }),
|
||||
/* 0x10 */
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.DecrementJumpNonZero(); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegisterImmediate16(ref c.de.Word); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadMemory8(c.de.Word, c.af.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Increment16(ref c.de.Word); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Increment8(ref c.de.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Decrement8(ref c.de.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegisterImmediate8(ref c.de.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.RotateLeftAccumulator(); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Jump8(); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Add16(ref r, c.de.Word, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegisterFromMemory8(ref c.af.High, c.de.Word, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Decrement16(ref c.de.Word); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Increment8(ref c.de.Low); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Decrement8(ref c.de.Low); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegisterImmediate8(ref c.de.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.RotateRightAccumulator(); }),
|
||||
/* 0x20 */
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.JumpConditional8(!c.IsFlagSet(Flags.Zero)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegisterImmediate16(ref r.Word); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadMemory16(c.ReadMemory16(c.pc), r.Word); c.pc += 2; }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Increment16(ref r.Word); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Increment8(ref r.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Decrement8(ref r.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { r.High = c.ReadMemory8(c.pc++); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.DecimalAdjustAccumulator(); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.JumpConditional8(c.IsFlagSet(Flags.Zero)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Add16(ref r, r.Word, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister16(ref r.Word, c.ReadMemory16(c.ReadMemory16(c.pc))); c.pc += 2; }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Decrement16(ref r.Word); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Increment8(ref r.Low); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Decrement8(ref r.Low); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { r.Low = c.ReadMemory8(c.pc++); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.af.High ^= 0xFF; c.SetFlag(Flags.Subtract | Flags.HalfCarry); }),
|
||||
/* 0x30 */
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.JumpConditional8(!c.IsFlagSet(Flags.Carry)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegisterImmediate16(ref c.sp); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadMemory8(c.ReadMemory16(c.pc), c.af.High); c.pc += 2; }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Increment16(ref c.sp); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.IncrementMemory8(c.CalculateIXIYAddress(r)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.DecrementMemory8(c.CalculateIXIYAddress(r)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadMemory8(c.CalculateIXIYAddress(r), c.ReadMemory8(c.pc++)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.SetFlag(Flags.Carry); c.ClearFlag(Flags.Subtract | Flags.HalfCarry); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.JumpConditional8(c.IsFlagSet(Flags.Carry)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Add16(ref r, c.sp, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegisterFromMemory8(ref c.af.High, c.ReadMemory16(c.pc), false); c.pc += 2; }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Decrement16(ref c.sp); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Increment8(ref c.af.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Decrement8(ref c.af.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegisterImmediate8(ref c.af.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.ClearFlag(Flags.Subtract); c.SetClearFlagConditional(Flags.Carry, !c.IsFlagSet(Flags.Carry)); }),
|
||||
/* 0x40 */
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.bc.High, c.bc.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.bc.High, c.bc.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.bc.High, c.de.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.bc.High, c.de.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.bc.High, r.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.bc.High, r.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.bc.High = c.ReadMemory8(c.CalculateIXIYAddress(r)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.bc.High, c.af.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.bc.Low, c.bc.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.bc.Low, c.bc.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.bc.Low, c.de.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.bc.Low, c.de.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.bc.Low, r.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.bc.Low, r.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.bc.Low = c.ReadMemory8(c.CalculateIXIYAddress(r)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.bc.Low, c.af.High, false); }),
|
||||
/* 0x50 */
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.de.High, c.bc.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.de.High, c.bc.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.de.High, c.de.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.de.High, c.de.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.de.High, r.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.de.High, r.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.de.High = c.ReadMemory8(c.CalculateIXIYAddress(r)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.de.High, c.af.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.de.Low, c.bc.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.de.Low, c.bc.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.de.Low, c.de.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.de.Low, c.de.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.de.Low, r.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.de.Low, r.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.de.Low = c.ReadMemory8(c.CalculateIXIYAddress(r)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.de.Low, c.af.High, false); }),
|
||||
/* 0x60 */
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref r.High, c.bc.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref r.High, c.bc.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref r.High, c.de.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref r.High, c.de.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref r.High, r.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref r.High, r.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.hl.High = c.ReadMemory8(c.CalculateIXIYAddress(r)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref r.High, c.af.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref r.Low, c.bc.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref r.Low, c.bc.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref r.Low, c.de.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref r.Low, c.de.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref r.Low, r.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref r.Low, r.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.hl.Low = c.ReadMemory8(c.CalculateIXIYAddress(r)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref r.Low, c.af.High, false); }),
|
||||
/* 0x70 */
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadMemory8(c.CalculateIXIYAddress(r), c.bc.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadMemory8(c.CalculateIXIYAddress(r), c.bc.Low); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadMemory8(c.CalculateIXIYAddress(r), c.de.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadMemory8(c.CalculateIXIYAddress(r), c.de.Low); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadMemory8(c.CalculateIXIYAddress(r), c.hl.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadMemory8(c.CalculateIXIYAddress(r), c.hl.Low); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.EnterHaltState(); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadMemory8(c.CalculateIXIYAddress(r), c.af.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.af.High, c.bc.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.af.High, c.bc.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.af.High, c.de.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.af.High, c.de.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.af.High, r.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.af.High, r.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.af.High = c.ReadMemory8(c.CalculateIXIYAddress(r)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.LoadRegister8(ref c.af.High, c.af.High, false); }),
|
||||
/* 0x80 */
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Add8(c.bc.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Add8(c.bc.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Add8(c.de.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Add8(c.de.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Add8(r.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Add8(r.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Add8(c.ReadMemory8(c.CalculateIXIYAddress(r)), false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Add8(c.af.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Add8(c.bc.High, true); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Add8(c.bc.Low, true); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Add8(c.de.High, true); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Add8(c.de.Low, true); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Add8(r.High, true); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Add8(r.Low, true); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Add8(c.ReadMemory8(c.CalculateIXIYAddress(r)), true); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Add8(c.af.High, true); }),
|
||||
/* 0x90 */
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Subtract8(c.bc.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Subtract8(c.bc.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Subtract8(c.de.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Subtract8(c.de.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Subtract8(r.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Subtract8(r.Low, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Subtract8(c.ReadMemory8(c.CalculateIXIYAddress(r)), false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Subtract8(c.af.High, false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Subtract8(c.bc.High, true); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Subtract8(c.bc.Low, true); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Subtract8(c.de.High, true); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Subtract8(c.de.Low, true); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Subtract8(r.High, true); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Subtract8(r.Low, true); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Subtract8(c.ReadMemory8(c.CalculateIXIYAddress(r)), true); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Subtract8(c.af.High, true); }),
|
||||
/* 0xA0 */
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.And8(c.bc.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.And8(c.bc.Low); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.And8(c.de.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.And8(c.de.Low); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.And8(r.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.And8(r.Low); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.And8(c.ReadMemory8(c.CalculateIXIYAddress(r))); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.And8(c.af.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Xor8(c.bc.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Xor8(c.bc.Low); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Xor8(c.de.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Xor8(c.de.Low); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Xor8(r.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Xor8(r.Low); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Xor8(c.ReadMemory8(c.CalculateIXIYAddress(r))); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Xor8(c.af.High); }),
|
||||
/* 0xB0 */
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Or8(c.bc.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Or8(c.bc.Low); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Or8(c.de.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Or8(c.de.Low); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Or8(r.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Or8(r.Low); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Or8(c.ReadMemory8(c.CalculateIXIYAddress(r))); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Or8(c.af.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Cp8(c.bc.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Cp8(c.bc.Low); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Cp8(c.de.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Cp8(c.de.Low); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Cp8(r.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Cp8(r.Low); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Cp8(c.ReadMemory8(c.CalculateIXIYAddress(r))); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Cp8(c.af.High); }),
|
||||
/* 0xC0 */
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.ReturnConditional(!c.IsFlagSet(Flags.Zero)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Pop(ref c.bc); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.JumpConditional16(!c.IsFlagSet(Flags.Zero)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.JumpConditional16(true); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.CallConditional16(!c.IsFlagSet(Flags.Zero)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Push(c.bc); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Add8(c.ReadMemory8(c.pc++), false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Restart(0x0000); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.ReturnConditional(c.IsFlagSet(Flags.Zero)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Return(); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.JumpConditional16(c.IsFlagSet(Flags.Zero)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.ExecuteOpDDFDCB(c.ReadMemory8((ushort)(c.pc + 1)), ref r); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.CallConditional16(c.IsFlagSet(Flags.Zero)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Call16(); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Add8(c.ReadMemory8(c.pc++), true); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Restart(0x0008); }),
|
||||
/* 0xD0 */
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.ReturnConditional(!c.IsFlagSet(Flags.Carry)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Pop(ref c.de); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.JumpConditional16(!c.IsFlagSet(Flags.Carry)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.WritePort(c.ReadMemory8(c.pc++), c.af.High); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.CallConditional16(!c.IsFlagSet(Flags.Carry)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Push(c.de); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Subtract8(c.ReadMemory8(c.pc++), false); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Restart(0x0010); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.ReturnConditional(c.IsFlagSet(Flags.Carry)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.ExchangeRegisters16(ref c.bc, ref c.bc_); c.ExchangeRegisters16(ref c.de, ref c.de_); c.ExchangeRegisters16(ref c.hl, ref c.hl_); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.JumpConditional16(c.IsFlagSet(Flags.Carry)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.af.High = c.ReadPort(c.ReadMemory8(c.pc++)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.CallConditional16(c.IsFlagSet(Flags.Carry)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { /* DD - treat as NOP */ }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Subtract8(c.ReadMemory8(c.pc++), true); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Restart(0x0018); }),
|
||||
/* 0xE0 */
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.ReturnConditional(!c.IsFlagSet(Flags.ParityOrOverflow)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Pop(ref r); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.JumpConditional16(!c.IsFlagSet(Flags.ParityOrOverflow)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.ExchangeStackRegister16(ref r); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.CallConditional16(!c.IsFlagSet(Flags.ParityOrOverflow)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Push(r); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.And8(c.ReadMemory8(c.pc++)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Restart(0x0020); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.ReturnConditional(c.IsFlagSet(Flags.ParityOrOverflow)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.pc = r.Word; }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.JumpConditional16(c.IsFlagSet(Flags.ParityOrOverflow)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.ExchangeRegisters16(ref c.de, ref c.hl); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.CallConditional16(c.IsFlagSet(Flags.ParityOrOverflow)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { /* ED - treat as NOP */ }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Xor8(c.ReadMemory8(c.pc++)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Restart(0x0028); }),
|
||||
/* 0xF0 */
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.ReturnConditional(!c.IsFlagSet(Flags.Sign)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Pop(ref c.af); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.JumpConditional16(!c.IsFlagSet(Flags.Sign)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.iff1 = c.iff2 = false; }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.CallConditional16(!c.IsFlagSet(Flags.Sign)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Push(c.af); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Or8(c.ReadMemory8(c.pc++)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Restart(0x0030); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.ReturnConditional(c.IsFlagSet(Flags.Sign)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.sp = r.Word; }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.JumpConditional16(c.IsFlagSet(Flags.Sign)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.eiDelay = true; }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.CallConditional16(c.IsFlagSet(Flags.Sign)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { /* FD - treat as NOP */ }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Cp8(c.ReadMemory8(c.pc++)); }),
|
||||
new DDFDOpcodeDelegate((Z80A c, ref Register r) => { c.Restart(0x0038); })
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 59243a6d2ecd14d41816ff23fd5204cb
|
@ -0,0 +1,281 @@
|
||||
namespace Essgee.Emulation.CPU
|
||||
{
|
||||
public partial class Z80A
|
||||
{
|
||||
static DDFDCBOpcodeDelegate[] opcodesPrefixDDFDCB = new DDFDCBOpcodeDelegate[]
|
||||
{
|
||||
/* 0x00 */
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.High = c.RotateLeftCircular(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.Low = c.RotateLeftCircular(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.High = c.RotateLeftCircular(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.Low = c.RotateLeftCircular(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.High = c.RotateLeftCircular(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.Low = c.RotateLeftCircular(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.RotateLeftCircular(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.af.High = c.RotateLeftCircular(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.High = c.RotateRightCircular(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.Low = c.RotateRightCircular(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.High = c.RotateRightCircular(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.Low = c.RotateRightCircular(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.High = c.RotateRightCircular(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.Low = c.RotateRightCircular(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.RotateRightCircular(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.af.High = c.RotateRightCircular(address); }),
|
||||
/* 0x10 */
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.High = c.RotateLeft(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.Low = c.RotateLeft(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.High = c.RotateLeft(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.Low = c.RotateLeft(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.High = c.RotateLeft(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.Low = c.RotateLeft(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.RotateLeft(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.af.High = c.RotateLeft(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.High = c.RotateRight(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.Low = c.RotateRight(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.High = c.RotateRight(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.Low = c.RotateRight(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.High = c.RotateRight(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.Low = c.RotateRight(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.RotateRight(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.af.High = c.RotateRight(address); }),
|
||||
/* 0x20 */
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.High = c.ShiftLeftArithmetic(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.Low = c.ShiftLeftArithmetic(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.High = c.ShiftLeftArithmetic(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.Low = c.ShiftLeftArithmetic(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.High = c.ShiftLeftArithmetic(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.Low = c.ShiftLeftArithmetic(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.ShiftLeftArithmetic(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.af.High = c.ShiftLeftArithmetic(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.High = c.ShiftRightArithmetic(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.Low = c.ShiftRightArithmetic(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.High = c.ShiftRightArithmetic(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.Low = c.ShiftRightArithmetic(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.High = c.ShiftRightArithmetic(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.Low = c.ShiftRightArithmetic(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.ShiftRightArithmetic(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.af.High = c.ShiftRightArithmetic(address); }),
|
||||
/* 0x30 */
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.High = c.ShiftLeftLogical(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.Low = c.ShiftLeftLogical(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.High = c.ShiftLeftLogical(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.Low = c.ShiftLeftLogical(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.High = c.ShiftLeftLogical(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.Low = c.ShiftLeftLogical(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.ShiftLeftLogical(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.af.High = c.ShiftLeftLogical(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.High = c.ShiftRightLogical(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.Low = c.ShiftRightLogical(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.High = c.ShiftRightLogical(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.Low = c.ShiftRightLogical(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.High = c.ShiftRightLogical(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.Low = c.ShiftRightLogical(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.ShiftRightLogical(address); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.af.High = c.ShiftRightLogical(address); }),
|
||||
/* 0x40 */
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 0); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 0); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 0); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 0); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 0); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 0); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 0); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 0); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 1); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 1); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 1); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 1); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 1); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 1); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 1); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 1); }),
|
||||
/* 0x50 */
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 2); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 2); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 2); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 2); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 2); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 2); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 2); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 2); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 3); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 3); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 3); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 3); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 3); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 3); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 3); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 3); }),
|
||||
/* 0x60 */
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 4); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 4); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 4); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 4); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 4); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 4); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 4); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 4); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 5); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 5); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 5); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 5); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 5); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 5); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 5); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 5); }),
|
||||
/* 0x70 */
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 6); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 6); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 6); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 6); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 6); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 6); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 6); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 6); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 7); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 7); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 7); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 7); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 7); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 7); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 7); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.TestBit(address, 7); }),
|
||||
/* 0x80 */
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.High = c.ResetBit(address, 0); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.Low = c.ResetBit(address, 0); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.High = c.ResetBit(address, 0); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.Low = c.ResetBit(address, 0); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.High = c.ResetBit(address, 0); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.Low = c.ResetBit(address, 0); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.ResetBit(address, 0); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.af.High = c.ResetBit(address, 0); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.High = c.ResetBit(address, 1); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.Low = c.ResetBit(address, 1); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.High = c.ResetBit(address, 1); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.Low = c.ResetBit(address, 1); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.High = c.ResetBit(address, 1); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.Low = c.ResetBit(address, 1); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.ResetBit(address, 1); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.af.High = c.ResetBit(address, 1); }),
|
||||
/* 0x90 */
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.High = c.ResetBit(address, 2); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.Low = c.ResetBit(address, 2); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.High = c.ResetBit(address, 2); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.Low = c.ResetBit(address, 2); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.High = c.ResetBit(address, 2); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.Low = c.ResetBit(address, 2); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.ResetBit(address, 2); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.af.High = c.ResetBit(address, 2); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.High = c.ResetBit(address, 3); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.Low = c.ResetBit(address, 3); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.High = c.ResetBit(address, 3); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.Low = c.ResetBit(address, 3); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.High = c.ResetBit(address, 3); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.Low = c.ResetBit(address, 3); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.ResetBit(address, 3); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.af.High = c.ResetBit(address, 3); }),
|
||||
/* 0xA0 */
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.High = c.ResetBit(address, 4); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.Low = c.ResetBit(address, 4); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.High = c.ResetBit(address, 4); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.Low = c.ResetBit(address, 4); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.High = c.ResetBit(address, 4); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.Low = c.ResetBit(address, 4); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.ResetBit(address, 4); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.af.High = c.ResetBit(address, 4); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.High = c.ResetBit(address, 5); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.Low = c.ResetBit(address, 5); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.High = c.ResetBit(address, 5); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.Low = c.ResetBit(address, 5); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.High = c.ResetBit(address, 5); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.Low = c.ResetBit(address, 5); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.ResetBit(address, 5); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.af.High = c.ResetBit(address, 5); }),
|
||||
/* 0xB0 */
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.High = c.ResetBit(address, 6); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.Low = c.ResetBit(address, 6); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.High = c.ResetBit(address, 6); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.Low = c.ResetBit(address, 6); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.High = c.ResetBit(address, 6); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.Low = c.ResetBit(address, 6); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.ResetBit(address, 6); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.af.High = c.ResetBit(address, 6); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.High = c.ResetBit(address, 7); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.Low = c.ResetBit(address, 7); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.High = c.ResetBit(address, 7); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.Low = c.ResetBit(address, 7); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.High = c.ResetBit(address, 7); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.Low = c.ResetBit(address, 7); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.ResetBit(address, 7); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.af.High = c.ResetBit(address, 7); }),
|
||||
/* 0xC0 */
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.High = c.SetBit(address, 0); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.Low = c.SetBit(address, 0); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.High = c.SetBit(address, 0); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.Low = c.SetBit(address, 0); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.High = c.SetBit(address, 0); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.Low = c.SetBit(address, 0); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.SetBit(address, 0); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.af.High = c.SetBit(address, 0); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.High = c.SetBit(address, 1); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.Low = c.SetBit(address, 1); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.High = c.SetBit(address, 1); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.Low = c.SetBit(address, 1); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.High = c.SetBit(address, 1); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.Low = c.SetBit(address, 1); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.SetBit(address, 1); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.af.High = c.SetBit(address, 1); }),
|
||||
/* 0xD0 */
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.High = c.SetBit(address, 2); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.Low = c.SetBit(address, 2); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.High = c.SetBit(address, 2); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.Low = c.SetBit(address, 2); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.High = c.SetBit(address, 2); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.Low = c.SetBit(address, 2); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.SetBit(address, 2); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.af.High = c.SetBit(address, 2); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.High = c.SetBit(address, 3); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.Low = c.SetBit(address, 3); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.High = c.SetBit(address, 3); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.Low = c.SetBit(address, 3); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.High = c.SetBit(address, 3); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.Low = c.SetBit(address, 3); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.SetBit(address, 3); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.af.High = c.SetBit(address, 3); }),
|
||||
/* 0xE0 */
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.High = c.SetBit(address, 4); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.Low = c.SetBit(address, 4); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.High = c.SetBit(address, 4); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.Low = c.SetBit(address, 4); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.High = c.SetBit(address, 4); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.Low = c.SetBit(address, 4); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.SetBit(address, 4); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.af.High = c.SetBit(address, 4); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.High = c.SetBit(address, 5); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.Low = c.SetBit(address, 5); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.High = c.SetBit(address, 5); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.Low = c.SetBit(address, 5); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.High = c.SetBit(address, 5); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.Low = c.SetBit(address, 5); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.SetBit(address, 5); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.af.High = c.SetBit(address, 5); }),
|
||||
/* 0xF0 */
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.High = c.SetBit(address, 6); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.Low = c.SetBit(address, 6); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.High = c.SetBit(address, 6); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.Low = c.SetBit(address, 6); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.High = c.SetBit(address, 6); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.Low = c.SetBit(address, 6); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.SetBit(address, 6); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.af.High = c.SetBit(address, 6); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.High = c.SetBit(address, 7); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.bc.Low = c.SetBit(address, 7); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.High = c.SetBit(address, 7); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.de.Low = c.SetBit(address, 7); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.High = c.SetBit(address, 7); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.hl.Low = c.SetBit(address, 7); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.SetBit(address, 7); }),
|
||||
new DDFDCBOpcodeDelegate((Z80A c, ref Register r, ushort address) => { c.af.High = c.SetBit(address, 7); }),
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f1c07a0773a3a94486e1d50ed7552cb
|
@ -0,0 +1,281 @@
|
||||
namespace Essgee.Emulation.CPU
|
||||
{
|
||||
public partial class Z80A
|
||||
{
|
||||
static SimpleOpcodeDelegate[] opcodesPrefixED = new SimpleOpcodeDelegate[]
|
||||
{
|
||||
/* 0x00 */
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
/* 0x10 */
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
/* 0x20 */
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
/* 0x30 */
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
/* 0x40 */
|
||||
new SimpleOpcodeDelegate((c) => { c.PortInput(ref c.bc.High, c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.WritePort(c.bc.Low, c.bc.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract16(ref c.hl, c.bc.Word, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadMemory16(c.ReadMemory16(c.pc), c.bc.Word); c.pc += 2; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Negate(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.iff1 = c.iff2; c.Return(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.im = 0; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.i = c.af.High; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.PortInput(ref c.bc.Low, c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.WritePort(c.bc.Low, c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add16(ref c.hl, c.bc.Word, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister16(ref c.bc.Word, c.ReadMemory16(c.ReadMemory16(c.pc))); c.pc += 2; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Negate(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Return(); c.iff1 = c.iff2; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.im = 0; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.r = c.af.High; }),
|
||||
/* 0x50 */
|
||||
new SimpleOpcodeDelegate((c) => { c.PortInput(ref c.de.High, c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.WritePort(c.bc.Low, c.de.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract16(ref c.hl, c.de.Word, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadMemory16(c.ReadMemory16(c.pc), c.de.Word); c.pc += 2; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Negate(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.iff1 = c.iff2; c.Return(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.im = 1; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.af.High, c.i, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.PortInput(ref c.de.Low, c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.WritePort(c.bc.Low, c.de.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add16(ref c.hl, c.de.Word, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister16(ref c.de.Word, c.ReadMemory16(c.ReadMemory16(c.pc))); c.pc += 2; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Negate(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.iff1 = c.iff2; c.Return(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.im = 2; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister8(ref c.af.High, c.r, true); }),
|
||||
/* 0x60 */
|
||||
new SimpleOpcodeDelegate((c) => { c.PortInput(ref c.hl.High, c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.WritePort(c.bc.Low, c.hl.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract16(ref c.hl, c.hl.Word, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadMemory16(c.ReadMemory16(c.pc), c.hl.Word); c.pc += 2; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Negate(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.iff1 = c.iff2; c.Return(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.im = 0; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateRight4B(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.PortInput(ref c.hl.Low, c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.WritePort(c.bc.Low, c.hl.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add16(ref c.hl, c.hl.Word, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister16(ref c.hl.Word, c.ReadMemory16(c.ReadMemory16(c.pc))); c.pc += 2; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Negate(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.iff1 = c.iff2; c.Return(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.im = 0; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.RotateLeft4B(); }),
|
||||
/* 0x70 */
|
||||
new SimpleOpcodeDelegate((c) => { c.PortInputFlagsOnly(c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.WritePort(c.bc.Low, 0x00); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Subtract16(ref c.hl, c.sp, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadMemory16(c.ReadMemory16(c.pc), c.sp); c.pc += 2; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Negate(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.iff1 = c.iff2; c.Return(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.im = 1; }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP */ }),
|
||||
new SimpleOpcodeDelegate((c) => { c.PortInput(ref c.af.High, c.bc.Low); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.WritePort(c.bc.Low, c.af.High); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Add16(ref c.hl, c.sp, true); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadRegister16(ref c.sp, c.ReadMemory16(c.ReadMemory16(c.pc))); c.pc += 2; }),
|
||||
new SimpleOpcodeDelegate((c) => { c.Negate(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.iff1 = c.iff2; c.Return(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.im = 2; }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP */ }),
|
||||
/* 0x80 */
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
/* 0x90 */
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
/* 0xA0 */
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadIncrement(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.CompareIncrement(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.PortInputIncrement(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.PortOutputIncrement(); }),
|
||||
new SimpleOpcodeDelegate((c) => { /* A4 - nothing */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* A5 - nothing */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* A6 - nothing */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* A7 - nothing */ }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadDecrement(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.CompareDecrement(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.PortInputDecrement(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.PortOutputDecrement(); }),
|
||||
new SimpleOpcodeDelegate((c) => { /* AC - nothing */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* AD - nothing */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* AE - nothing */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* AF - nothing */ }),
|
||||
/* 0xB0 */
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadIncrementRepeat(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.CompareIncrementRepeat(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.PortInputIncrementRepeat(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.PortOutputIncrementRepeat(); }),
|
||||
new SimpleOpcodeDelegate((c) => { /* B4 - nothing */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* B5 - nothing */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* B6 - nothing */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* B7 - nothing */ }),
|
||||
new SimpleOpcodeDelegate((c) => { c.LoadDecrementRepeat(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.CompareDecrementRepeat(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.PortInputDecrementRepeat(); }),
|
||||
new SimpleOpcodeDelegate((c) => { c.PortOutputDecrementRepeat(); }),
|
||||
new SimpleOpcodeDelegate((c) => { /* BC - nothing */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* BD - nothing */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* BE - nothing */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* BF - nothing */ }),
|
||||
/* 0xC0 */
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
/* 0xD0 */
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
/* 0xE0 */
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
/* 0xF0 */
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ }),
|
||||
new SimpleOpcodeDelegate((c) => { /* NOP (2x) */ })
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5a8dae171d8e67546b93ab06aafbcb53
|
@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Essgee.Emulation.CPU
|
||||
{
|
||||
public partial class Z80A
|
||||
{
|
||||
[DebuggerDisplay("{Word}")]
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
[Serializable]
|
||||
public struct Register
|
||||
{
|
||||
[NonSerialized]
|
||||
[FieldOffset(0)]
|
||||
public byte Low;
|
||||
[NonSerialized]
|
||||
[FieldOffset(1)]
|
||||
public byte High;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public ushort Word;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 09764bf46e9451741a9fa2016022c5ea
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 02ea1ee0691d94f4ba12b23ecea75623
|
@ -0,0 +1,78 @@
|
||||
using Essgee.Emulation.Machines;
|
||||
using Essgee.Exceptions;
|
||||
using Essgee.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Essgee.Emulation
|
||||
{
|
||||
public static class CartridgeLoader
|
||||
{
|
||||
static Dictionary<string, Type> fileExtensionSystemDictionary;
|
||||
|
||||
static CartridgeLoader()
|
||||
{
|
||||
fileExtensionSystemDictionary = new Dictionary<string, Type>();
|
||||
foreach (var machineType in Assembly.GetExecutingAssembly().GetTypes().Where(x => typeof(IMachine).IsAssignableFrom(x) && !x.IsInterface).OrderBy(x => x.GetCustomAttribute<MachineIndexAttribute>()?.Index))
|
||||
{
|
||||
if (machineType == null) continue;
|
||||
|
||||
var instance = (IMachine)Activator.CreateInstance(machineType);
|
||||
foreach (var extension in instance.FileFilter.Extension.Split(';'))
|
||||
fileExtensionSystemDictionary.Add(extension, machineType);
|
||||
}
|
||||
}
|
||||
|
||||
public static (Type, byte[]) Load(string fileName, string fileType)
|
||||
{
|
||||
Type machineType = null;
|
||||
byte[] romData = null;
|
||||
|
||||
if (!File.Exists(fileName))
|
||||
throw new CartridgeLoaderException($"{fileType} file not found.");
|
||||
|
||||
try
|
||||
{
|
||||
var fileExtension = Path.GetExtension(fileName);
|
||||
if (fileExtension == ".zip")
|
||||
{
|
||||
using (var zip = ZipFile.Open(fileName, ZipArchiveMode.Read))
|
||||
{
|
||||
foreach (var entry in zip.Entries)
|
||||
{
|
||||
var entryExtension = Path.GetExtension(entry.Name);
|
||||
if (fileExtensionSystemDictionary.ContainsKey(entryExtension))
|
||||
{
|
||||
machineType = fileExtensionSystemDictionary[entryExtension];
|
||||
using (var stream = entry.Open())
|
||||
{
|
||||
romData = new byte[entry.Length];
|
||||
stream.Read(romData, 0, romData.Length);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (fileExtensionSystemDictionary.ContainsKey(fileExtension))
|
||||
{
|
||||
machineType = fileExtensionSystemDictionary[fileExtension];
|
||||
romData = File.ReadAllBytes(fileName);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (!AppEnvironment.DebugMode)
|
||||
{
|
||||
throw new CartridgeLoaderException("File load error", ex);
|
||||
}
|
||||
|
||||
if (machineType == null)
|
||||
throw new CartridgeLoaderException($"File could not be recognized as {fileType}.");
|
||||
|
||||
return (machineType, romData);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f1b72196f7ea1bf41b9b580316878200
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd37849c4f2dfbb4fa2e82cdc6fc36e3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3f9cb94bc81a41e45a844926b14f89eb
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,78 @@
|
||||
using System;
|
||||
|
||||
namespace Essgee.Emulation.Cartridges.Coleco
|
||||
{
|
||||
public class ColecoCartridge : ICartridge
|
||||
{
|
||||
// TODO: http://atariage.com/forums/topic/210168-colecovision-bank-switching/ ?
|
||||
|
||||
byte[] romData;
|
||||
|
||||
public ColecoCartridge(int romSize, int ramSize)
|
||||
{
|
||||
romData = new byte[romSize];
|
||||
}
|
||||
|
||||
public void LoadRom(byte[] data)
|
||||
{
|
||||
Buffer.BlockCopy(data, 0, romData, 0, Math.Min(data.Length, romData.Length));
|
||||
}
|
||||
|
||||
public void LoadRam(byte[] data)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public byte[] GetRomData()
|
||||
{
|
||||
return romData;
|
||||
}
|
||||
|
||||
public byte[] GetRamData()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsRamSaveNeeded()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public ushort GetLowerBound()
|
||||
{
|
||||
return 0x0000;
|
||||
}
|
||||
|
||||
public ushort GetUpperBound()
|
||||
{
|
||||
return (ushort)(romData.Length - 1);
|
||||
}
|
||||
|
||||
public void Step(int clockCyclesInStep)
|
||||
{
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
public byte Read(ushort address)
|
||||
{
|
||||
if (address <= 0x1FFF)
|
||||
{
|
||||
/* BIOS */
|
||||
return romData[address & 0x1FFF];
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Cartridge */
|
||||
address -= 0x8000;
|
||||
if (address >= romData.Length) address -= (ushort)romData.Length;
|
||||
return romData[address];
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(ushort address, byte value)
|
||||
{
|
||||
/* Cannot write to cartridge */
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9d70811b8aea410499364dc0485491e3
|
@ -0,0 +1,20 @@
|
||||
namespace Essgee.Emulation.Cartridges
|
||||
{
|
||||
public interface ICartridge
|
||||
{
|
||||
void LoadRom(byte[] data);
|
||||
void LoadRam(byte[] data);
|
||||
|
||||
byte[] GetRomData();
|
||||
byte[] GetRamData();
|
||||
bool IsRamSaveNeeded();
|
||||
|
||||
ushort GetLowerBound();
|
||||
ushort GetUpperBound();
|
||||
|
||||
void Step(int clockCyclesInStep);
|
||||
|
||||
byte Read(ushort address);
|
||||
void Write(ushort address, byte value);
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 69d4af746d98c1b4ab29d5be6b2d9f2d
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3eb99a6b5fa5ea348b79aa6dfc080f12
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,421 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace Essgee.Emulation.Cartridges.Nintendo
|
||||
{
|
||||
/* Image processing, etc. based on https://github.com/AntonioND/gbcam-rev-engineer/blob/master/doc/gb_camera_doc_v1_1_1.pdf */
|
||||
|
||||
public class GBCameraCartridge : IGameBoyCartridge
|
||||
{
|
||||
const int camSensorExtraLines = 8;
|
||||
const int camSensorWidth = 128;
|
||||
const int camSensorHeight = 112 + camSensorExtraLines;
|
||||
|
||||
const int camWidth = 128;
|
||||
const int camHeight = 112;
|
||||
|
||||
static readonly float[] edgeRatioLookUpTable = new float[] { 0.50f, 0.75f, 1.00f, 1.25f, 2.00f, 3.00f, 4.00f, 5.00f };
|
||||
|
||||
public enum ImageSources
|
||||
{
|
||||
[Description("Random Noise")]
|
||||
Noise,
|
||||
[Description("Image File")]
|
||||
File
|
||||
}
|
||||
|
||||
ImageSources imageSourceType;
|
||||
//Bitmap scaledImage;
|
||||
|
||||
readonly int[,] webcamOutput, camRetinaOutput;
|
||||
readonly byte[,,] tileBuffer;
|
||||
|
||||
byte[] romData, ramData;
|
||||
bool hasBattery;
|
||||
|
||||
byte romBank, ramBank;
|
||||
bool ramEnable;
|
||||
|
||||
readonly byte[] camRegisters;
|
||||
bool camSelected;
|
||||
|
||||
int cameraCycles, camClocksLeft;
|
||||
|
||||
public GBCameraCartridge(int romSize, int ramSize)
|
||||
{
|
||||
imageSourceType = ImageSources.Noise;
|
||||
//scaledImage = new Bitmap(camSensorWidth, camSensorHeight);
|
||||
|
||||
webcamOutput = new int[camSensorWidth, camSensorHeight];
|
||||
camRetinaOutput = new int[camSensorWidth, camSensorHeight];
|
||||
tileBuffer = new byte[14, 16, 16];
|
||||
|
||||
romData = new byte[romSize];
|
||||
ramData = new byte[ramSize];
|
||||
|
||||
romBank = 1;
|
||||
ramBank = 0;
|
||||
|
||||
ramEnable = false;
|
||||
|
||||
camRegisters = new byte[0x80]; // 0x36 used
|
||||
camSelected = false;
|
||||
|
||||
hasBattery = false;
|
||||
}
|
||||
|
||||
public void LoadRom(byte[] data)
|
||||
{
|
||||
Buffer.BlockCopy(data, 0, romData, 0, Math.Min(data.Length, romData.Length));
|
||||
}
|
||||
|
||||
public void LoadRam(byte[] data)
|
||||
{
|
||||
Buffer.BlockCopy(data, 0, ramData, 0, Math.Min(data.Length, ramData.Length));
|
||||
}
|
||||
|
||||
public byte[] GetRomData()
|
||||
{
|
||||
return romData;
|
||||
}
|
||||
|
||||
public byte[] GetRamData()
|
||||
{
|
||||
return ramData;
|
||||
}
|
||||
|
||||
public bool IsRamSaveNeeded()
|
||||
{
|
||||
return hasBattery;
|
||||
}
|
||||
|
||||
public ushort GetLowerBound()
|
||||
{
|
||||
return 0x0000;
|
||||
}
|
||||
|
||||
public ushort GetUpperBound()
|
||||
{
|
||||
return 0x7FFF;
|
||||
}
|
||||
|
||||
public void SetCartridgeConfig(bool battery, bool rtc, bool rumble)
|
||||
{
|
||||
hasBattery = battery;
|
||||
}
|
||||
|
||||
public void SetImageSource(ImageSources source, string filename)
|
||||
{
|
||||
imageSourceType = source;
|
||||
|
||||
if (imageSourceType == ImageSources.File)
|
||||
{
|
||||
//TODO imageSourceType == ImageSources.File
|
||||
|
||||
//using (var tempImage = new Bitmap(filename))
|
||||
//{
|
||||
// using (var g = System.Drawing.Graphics.FromImage(scaledImage))
|
||||
// {
|
||||
// var ratio = Math.Min(tempImage.Width / (float)camSensorWidth, tempImage.Height / (float)camSensorHeight);
|
||||
// var srcWidth = (int)(camSensorWidth * ratio);
|
||||
// var srcHeight = (int)(camSensorHeight * ratio);
|
||||
// var srcX = (tempImage.Width - srcWidth) / 2;
|
||||
// var srcY = (tempImage.Height - srcHeight) / 2;
|
||||
// var scaledRect = new Rectangle(0, 0, camSensorWidth, camSensorHeight);
|
||||
|
||||
// g.FillRectangle(Brushes.White, scaledRect);
|
||||
// g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
|
||||
// g.DrawImage(tempImage, scaledRect, new Rectangle(srcX, srcY, srcWidth, srcHeight), GraphicsUnit.Pixel);
|
||||
// }
|
||||
//}
|
||||
|
||||
//for (var x = 0; x < camSensorWidth; x++)
|
||||
// for (var y = 0; y < camSensorHeight; y++)
|
||||
// webcamOutput[x, y] = (int)(scaledImage.GetPixel(x, y).GetBrightness() * 255);
|
||||
}
|
||||
}
|
||||
|
||||
public void Step(int clockCyclesInStep)
|
||||
{
|
||||
cameraCycles += clockCyclesInStep;
|
||||
if (cameraCycles >= camClocksLeft)
|
||||
{
|
||||
camRegisters[0x00] &= 0xFE;
|
||||
cameraCycles = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public byte Read(ushort address)
|
||||
{
|
||||
if (address >= 0x0000 && address <= 0x3FFF)
|
||||
{
|
||||
return romData[address & 0x3FFF];
|
||||
}
|
||||
else if (address >= 0x4000 && address <= 0x7FFF)
|
||||
{
|
||||
return romData[(romBank << 14) | (address & 0x3FFF)];
|
||||
}
|
||||
else if (address >= 0xA000 && address <= 0xBFFF)
|
||||
{
|
||||
if (!camSelected)
|
||||
{
|
||||
if ((camRegisters[0x00] & 0b1) == 0)
|
||||
return ramData[(ramBank << 13) | (address & 0x1FFF)];
|
||||
else
|
||||
return 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
var reg = (byte)(address & 0x7F);
|
||||
if (reg == 0x00)
|
||||
return (byte)(camRegisters[reg] & 0x07);
|
||||
else
|
||||
return 0xFF;
|
||||
}
|
||||
}
|
||||
else
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
public void Write(ushort address, byte value)
|
||||
{
|
||||
if (address >= 0x0000 && address <= 0x1FFF)
|
||||
{
|
||||
ramEnable = (value & 0x0F) == 0x0A;
|
||||
}
|
||||
else if (address >= 0x2000 && address <= 0x3FFF)
|
||||
{
|
||||
romBank = (byte)((romBank & 0xC0) | (value & 0x3F));
|
||||
romBank &= (byte)((romData.Length >> 14) - 1);
|
||||
if ((romBank & 0x3F) == 0x00) romBank |= 0x01;
|
||||
}
|
||||
else if (address >= 0x4000 && address <= 0x5FFF)
|
||||
{
|
||||
if ((value & 0x10) != 0)
|
||||
{
|
||||
camSelected = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
camSelected = false;
|
||||
ramBank = (byte)(value & 0x0F);
|
||||
}
|
||||
}
|
||||
else if (address >= 0xA000 && address <= 0xBFFF)
|
||||
{
|
||||
if (!camSelected)
|
||||
{
|
||||
if (ramEnable)
|
||||
ramData[(ramBank << 13) | (address & 0x1FFF)] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
var reg = (byte)(address & 0x7F);
|
||||
if (reg == 0x00 && (value & 0b1) != 0)
|
||||
GenerateImage();
|
||||
|
||||
camRegisters[reg] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int Clamp(int value, int min, int max)
|
||||
{
|
||||
if (value < min) value = min;
|
||||
else if (value > max) value = max;
|
||||
return value;
|
||||
}
|
||||
|
||||
private void GenerateImage()
|
||||
{
|
||||
/* Get configuration -- register 0 */
|
||||
var pBits = 0;
|
||||
var mBits = 0;
|
||||
|
||||
switch ((camRegisters[0x00] >> 1) & 0b11)
|
||||
{
|
||||
case 0: pBits = 0x00; mBits = 0x01; break;
|
||||
case 1: pBits = 0x01; mBits = 0x00; break;
|
||||
case 2:
|
||||
case 3: pBits = 0x01; mBits = 0x02; break;
|
||||
}
|
||||
|
||||
/* Register 1 */
|
||||
var nBit = ((camRegisters[0x01] >> 7) & 0b1) != 0;
|
||||
var vhBits = (camRegisters[0x01] >> 5) & 0b11;
|
||||
|
||||
/* Registers 2 and 3 */
|
||||
var exposureBits = camRegisters[0x02] << 8 | camRegisters[0x03];
|
||||
|
||||
/* Register 4 */
|
||||
var edgeAlpha = edgeRatioLookUpTable[(camRegisters[0x04] >> 4) & 0b111];
|
||||
var e3Bit = ((camRegisters[0x04] >> 7) & 0b1) != 0;
|
||||
var iBit = ((camRegisters[0x04] >> 3) & 0b1) != 0;
|
||||
|
||||
/* Calculate timings */
|
||||
camClocksLeft = 4 * (32446 + (nBit ? 0 : 512) + 16 * exposureBits);
|
||||
|
||||
/* Clear tile buffer */
|
||||
for (var j = 0; j < 14; j++)
|
||||
for (var i = 0; i < 16; i++)
|
||||
for (var k = 0; k < 16; k++)
|
||||
tileBuffer[j, i, k] = 0x00;
|
||||
|
||||
/* Sensor handling */
|
||||
/* Copy webcam buffer to sensor buffer, apply color correction & exposure time */
|
||||
for (var i = 0; i < camSensorWidth; i++)
|
||||
{
|
||||
for (var j = 0; j < camSensorHeight; j++)
|
||||
{
|
||||
var value = 0;
|
||||
switch (imageSourceType)
|
||||
{
|
||||
case ImageSources.File: value = webcamOutput[i, j]; break;
|
||||
case ImageSources.Noise: value = EmuStandInfo.Random.Next(255); break;
|
||||
}
|
||||
|
||||
value = (value * exposureBits) / 0x0300;
|
||||
value = 128 + (((value - 128) * 1) / 8);
|
||||
camRetinaOutput[i, j] = Clamp(value, 0, 255);
|
||||
|
||||
/* Invert */
|
||||
if (iBit)
|
||||
camRetinaOutput[i, j] = 255 - camRetinaOutput[i, j];
|
||||
|
||||
/* Make signed */
|
||||
camRetinaOutput[i, j] = camRetinaOutput[i, j] - 128;
|
||||
}
|
||||
}
|
||||
|
||||
var tempBuffer = new int[camSensorWidth, camSensorHeight];
|
||||
var filteringMode = (nBit ? 8 : 0) | (vhBits << 1) | (e3Bit ? 1 : 0);
|
||||
switch (filteringMode)
|
||||
{
|
||||
case 0x00:
|
||||
/* 1-D filtering */
|
||||
for (var i = 0; i < camSensorWidth; i++)
|
||||
for (var j = 0; j < camSensorHeight; j++)
|
||||
tempBuffer[i, j] = camRetinaOutput[i, j];
|
||||
|
||||
for (var i = 0; i < camSensorWidth; i++)
|
||||
{
|
||||
for (var j = 0; j < camSensorHeight; j++)
|
||||
{
|
||||
var ms = tempBuffer[i, Math.Min(j + 1, camSensorHeight - 1)];
|
||||
var px = tempBuffer[i, j];
|
||||
|
||||
var value = 0;
|
||||
if ((pBits & 0b01) != 0) value += px;
|
||||
if ((pBits & 0b10) != 0) value += ms;
|
||||
if ((mBits & 0b01) != 0) value -= px;
|
||||
if ((mBits & 0b10) != 0) value -= ms;
|
||||
|
||||
camRetinaOutput[i, j] = Clamp(value, -128, 127);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
/* 1-D filtering + Horiz. enhancement : P + {2P-(MW+ME)} * alpha */
|
||||
for (var i = 0; i < camSensorWidth; i++)
|
||||
{
|
||||
for (var j = 0; j < camSensorHeight; j++)
|
||||
{
|
||||
var mw = camRetinaOutput[Math.Max(0, i - 1), j];
|
||||
var me = camRetinaOutput[Math.Min(i + 1, camSensorWidth - 1), j];
|
||||
var px = camRetinaOutput[i, j];
|
||||
|
||||
tempBuffer[i, j] = Clamp((int)(px + ((2 * px - mw - me) * edgeAlpha)), 0, 255);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < camSensorWidth; i++)
|
||||
{
|
||||
for (var j = 0; j < camSensorHeight; j++)
|
||||
{
|
||||
var ms = tempBuffer[i, Math.Min(j + 1, camSensorHeight - 1)];
|
||||
var px = tempBuffer[i, j];
|
||||
|
||||
var value = 0;
|
||||
if ((pBits & 0b01) != 0) value += px;
|
||||
if ((pBits & 0b10) != 0) value += ms;
|
||||
if ((mBits & 0b01) != 0) value -= px;
|
||||
if ((mBits & 0b10) != 0) value -= ms;
|
||||
|
||||
camRetinaOutput[i, j] = Clamp(value, -128, 127);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0E:
|
||||
/* 2D enhancement : P + {4P-(MN+MS+ME+MW)} * alpha */
|
||||
for (var i = 0; i < camSensorWidth; i++)
|
||||
{
|
||||
for (var j = 0; j < camSensorHeight; j++)
|
||||
{
|
||||
var ms = camRetinaOutput[i, Math.Min(j + 1, camSensorHeight - 1)];
|
||||
var mn = camRetinaOutput[i, Math.Max(0, j - 1)];
|
||||
var mw = camRetinaOutput[Math.Max(0, i - 1), j];
|
||||
var me = camRetinaOutput[Math.Min(i + 1, camSensorWidth - 1), j];
|
||||
var px = camRetinaOutput[i, j];
|
||||
|
||||
tempBuffer[i, j] = Clamp((int)(px + ((4 * px - mw - me - mn - ms) * edgeAlpha)), -128, 127);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < camSensorWidth; i++)
|
||||
for (var j = 0; j < camSensorHeight; j++)
|
||||
camRetinaOutput[i, j] = tempBuffer[i, j];
|
||||
break;
|
||||
|
||||
case 0x01:
|
||||
/* Unknown, always same color; sensor datasheet does not document this, maybe a bug? */
|
||||
for (var i = 0; i < camSensorWidth; i++)
|
||||
for (var j = 0; j < camSensorHeight; j++)
|
||||
camRetinaOutput[i, j] = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Unknown; write to log if enabled */
|
||||
if (AppEnvironment.EnableLogger)
|
||||
{
|
||||
EssgeeLogger.WriteLine($"Unsupported GB Camera mode 0x{filteringMode:X2}");
|
||||
EssgeeLogger.WriteLine(string.Join(" ", camRegisters.Take(6).Select(x => $"0x{x:X2}")));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Make unsigned */
|
||||
for (var i = 0; i < camSensorWidth; i++)
|
||||
for (var j = 0; j < camSensorHeight; j++)
|
||||
camRetinaOutput[i, j] = camRetinaOutput[i, j] + 128;
|
||||
|
||||
/* Convert output to GB tiles */
|
||||
for (var i = 0; i < camWidth; i++)
|
||||
{
|
||||
for (var j = 0; j < camHeight; j++)
|
||||
{
|
||||
var sensorValue = camRetinaOutput[i, j + (camSensorExtraLines / 2)];
|
||||
var matrixOffset = 0x06 + ((j % 4) * 12) + ((i % 4) * 3);
|
||||
|
||||
var c = (byte)0;
|
||||
if (sensorValue < camRegisters[matrixOffset + 0]) c = 3;
|
||||
else if (sensorValue < camRegisters[matrixOffset + 1]) c = 2;
|
||||
else if (sensorValue < camRegisters[matrixOffset + 2]) c = 1;
|
||||
else c = 0;
|
||||
|
||||
if ((c & 1) != 0) tileBuffer[j >> 3, i >> 3, ((j & 7) * 2) + 0] |= (byte)(1 << (7 - (7 & i)));
|
||||
if ((c & 2) != 0) tileBuffer[j >> 3, i >> 3, ((j & 7) * 2) + 1] |= (byte)(1 << (7 - (7 & i)));
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy tiles to cartridge RAM */
|
||||
int outputOffset = 0x100;
|
||||
for (var j = 0; j < 14; j++)
|
||||
for (var i = 0; i < 16; i++)
|
||||
for (var k = 0; k < 16; k++)
|
||||
ramData[outputOffset++] = tileBuffer[j, i, k];
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3887c3ff437835449bdf694c3e812afa
|
@ -0,0 +1,7 @@
|
||||
namespace Essgee.Emulation.Cartridges.Nintendo
|
||||
{
|
||||
public interface IGameBoyCartridge : ICartridge
|
||||
{
|
||||
void SetCartridgeConfig(bool battery, bool rtc, bool rumble);
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8f215e1ba8582ab42b21a0dae809cb6e
|
@ -0,0 +1,132 @@
|
||||
using System;
|
||||
|
||||
namespace Essgee.Emulation.Cartridges.Nintendo
|
||||
{
|
||||
public class MBC1Cartridge : IGameBoyCartridge
|
||||
{
|
||||
byte[] romData, ramData;
|
||||
bool hasBattery;
|
||||
|
||||
byte romBank, ramBank;
|
||||
bool ramEnable;
|
||||
|
||||
byte bankingMode;
|
||||
|
||||
public MBC1Cartridge(int romSize, int ramSize)
|
||||
{
|
||||
romData = new byte[romSize];
|
||||
ramData = new byte[ramSize];
|
||||
|
||||
romBank = 1;
|
||||
ramBank = 0;
|
||||
|
||||
ramEnable = false;
|
||||
|
||||
bankingMode = 0;
|
||||
|
||||
hasBattery = false;
|
||||
}
|
||||
|
||||
public void LoadRom(byte[] data)
|
||||
{
|
||||
Buffer.BlockCopy(data, 0, romData, 0, Math.Min(data.Length, romData.Length));
|
||||
}
|
||||
|
||||
public void LoadRam(byte[] data)
|
||||
{
|
||||
Buffer.BlockCopy(data, 0, ramData, 0, Math.Min(data.Length, ramData.Length));
|
||||
}
|
||||
|
||||
public byte[] GetRomData()
|
||||
{
|
||||
return romData;
|
||||
}
|
||||
|
||||
public byte[] GetRamData()
|
||||
{
|
||||
return ramData;
|
||||
}
|
||||
|
||||
public bool IsRamSaveNeeded()
|
||||
{
|
||||
return hasBattery;
|
||||
}
|
||||
|
||||
public ushort GetLowerBound()
|
||||
{
|
||||
return 0x0000;
|
||||
}
|
||||
|
||||
public ushort GetUpperBound()
|
||||
{
|
||||
return 0x7FFF;
|
||||
}
|
||||
|
||||
public void SetCartridgeConfig(bool battery, bool rtc, bool rumble)
|
||||
{
|
||||
hasBattery = battery;
|
||||
}
|
||||
|
||||
public void Step(int clockCyclesInStep)
|
||||
{
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
public byte Read(ushort address)
|
||||
{
|
||||
if (address >= 0x0000 && address <= 0x3FFF)
|
||||
{
|
||||
return romData[address & 0x3FFF];
|
||||
}
|
||||
else if (address >= 0x4000 && address <= 0x7FFF)
|
||||
{
|
||||
return romData[(romBank << 14) | (address & 0x3FFF)];
|
||||
}
|
||||
else if (address >= 0xA000 && address <= 0xBFFF)
|
||||
{
|
||||
if (ramEnable && ramData.Length != 0)
|
||||
return ramData[(ramBank << 13) | (address & 0x1FFF)];
|
||||
else
|
||||
return 0xFF;
|
||||
}
|
||||
else
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
public void Write(ushort address, byte value)
|
||||
{
|
||||
if (address >= 0x0000 && address <= 0x1FFF)
|
||||
{
|
||||
ramEnable = (value & 0x0F) == 0x0A;
|
||||
}
|
||||
else if (address >= 0x2000 && address <= 0x3FFF)
|
||||
{
|
||||
romBank = (byte)((romBank & 0xE0) | (value & 0x1F));
|
||||
romBank &= (byte)((romData.Length >> 14) - 1);
|
||||
if ((romBank & 0x1F) == 0x00) romBank |= 0x01;
|
||||
}
|
||||
else if (address >= 0x4000 && address <= 0x5FFF)
|
||||
{
|
||||
if (bankingMode == 0)
|
||||
{
|
||||
romBank = (byte)((romBank & 0x9F) | ((value & 0x03) << 5));
|
||||
romBank &= (byte)((romData.Length >> 14) - 1);
|
||||
if ((romBank & 0x1F) == 0x00) romBank |= 0x01;
|
||||
}
|
||||
else
|
||||
{
|
||||
ramBank = (byte)(value & 0x03);
|
||||
}
|
||||
}
|
||||
else if (address >= 0x6000 && address <= 0x7FFF)
|
||||
{
|
||||
bankingMode = (byte)(value & 0b1);
|
||||
}
|
||||
else if (address >= 0xA000 && address <= 0xBFFF)
|
||||
{
|
||||
if (ramEnable && ramData.Length != 0)
|
||||
ramData[(ramBank << 13) | (address & 0x1FFF)] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9e0eda098544f8e4aa894031f92b2b57
|
@ -0,0 +1,123 @@
|
||||
using System;
|
||||
|
||||
namespace Essgee.Emulation.Cartridges.Nintendo
|
||||
{
|
||||
public class MBC2Cartridge : IGameBoyCartridge
|
||||
{
|
||||
byte[] romData, ramData;
|
||||
bool hasBattery;
|
||||
|
||||
byte romBank;
|
||||
bool ramEnable;
|
||||
|
||||
public MBC2Cartridge(int romSize, int ramSize)
|
||||
{
|
||||
romData = new byte[romSize];
|
||||
ramData = new byte[ramSize];
|
||||
|
||||
romBank = 1;
|
||||
|
||||
ramEnable = false;
|
||||
|
||||
hasBattery = false;
|
||||
}
|
||||
|
||||
public void LoadRom(byte[] data)
|
||||
{
|
||||
Buffer.BlockCopy(data, 0, romData, 0, Math.Min(data.Length, romData.Length));
|
||||
}
|
||||
|
||||
public void LoadRam(byte[] data)
|
||||
{
|
||||
Buffer.BlockCopy(data, 0, ramData, 0, Math.Min(data.Length, ramData.Length));
|
||||
}
|
||||
|
||||
public byte[] GetRomData()
|
||||
{
|
||||
return romData;
|
||||
}
|
||||
|
||||
public byte[] GetRamData()
|
||||
{
|
||||
return ramData;
|
||||
}
|
||||
|
||||
public bool IsRamSaveNeeded()
|
||||
{
|
||||
return hasBattery;
|
||||
}
|
||||
|
||||
public ushort GetLowerBound()
|
||||
{
|
||||
return 0x0000;
|
||||
}
|
||||
|
||||
public ushort GetUpperBound()
|
||||
{
|
||||
return 0x7FFF;
|
||||
}
|
||||
|
||||
public void SetCartridgeConfig(bool battery, bool rtc, bool rumble)
|
||||
{
|
||||
hasBattery = battery;
|
||||
}
|
||||
|
||||
public void Step(int clockCyclesInStep)
|
||||
{
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
public byte Read(ushort address)
|
||||
{
|
||||
if (address >= 0x0000 && address <= 0x3FFF)
|
||||
{
|
||||
return romData[address & 0x3FFF];
|
||||
}
|
||||
else if (address >= 0x4000 && address <= 0x7FFF)
|
||||
{
|
||||
return romData[(romBank << 14) | (address & 0x3FFF)];
|
||||
}
|
||||
else if (address >= 0xA000 && address <= 0xA1FF)
|
||||
{
|
||||
if (ramEnable)
|
||||
{
|
||||
var ramOffset = (address >> 1) & 0x00FF;
|
||||
var valueShift = (address & 0x01) << 2;
|
||||
return (byte)((ramData[ramOffset] >> valueShift) & 0x0F);
|
||||
}
|
||||
else
|
||||
return 0xFF;
|
||||
}
|
||||
else
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
public void Write(ushort address, byte value)
|
||||
{
|
||||
if (address >= 0x0000 && address <= 0x1FFF)
|
||||
{
|
||||
if ((address & 0x0100) == 0)
|
||||
ramEnable = (value & 0x0F) == 0x0A;
|
||||
}
|
||||
else if (address >= 0x2000 && address <= 0x3FFF)
|
||||
{
|
||||
if ((address & 0x0100) != 0)
|
||||
{
|
||||
romBank = (byte)((romBank & 0xF0) | (value & 0x0F));
|
||||
romBank &= (byte)((romData.Length >> 14) - 1);
|
||||
if ((romBank & 0x0F) == 0x00) romBank |= 0x01;
|
||||
}
|
||||
}
|
||||
else if (address >= 0xA000 && address <= 0xA1FF)
|
||||
{
|
||||
if (ramEnable)
|
||||
{
|
||||
var ramOffset = (address >> 1) & 0x00FF;
|
||||
var valueShift = (address & 0x01) << 2;
|
||||
|
||||
ramData[ramOffset] = (byte)((ramData[ramOffset] & (0x0F << (valueShift ^ 0x04))) | (value << valueShift));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b5795dc3ee1b33746a64186a778e2bd6
|
@ -0,0 +1,261 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Essgee.Emulation.Cartridges.Nintendo
|
||||
{
|
||||
public class MBC3Cartridge : IGameBoyCartridge
|
||||
{
|
||||
// https://thomas.spurden.name/gameboy/#mbc3-real-time-clock-rtc
|
||||
|
||||
public class RTC
|
||||
{
|
||||
public const int NumRegisters = 0x05;
|
||||
|
||||
public byte[] BaseRegisters { get; private set; }
|
||||
public byte[] LatchedRegisters { get; private set; }
|
||||
|
||||
public DateTime BaseTime { get; set; }
|
||||
|
||||
public bool IsSelected { get; set; }
|
||||
public byte SelectedRegister { get; set; }
|
||||
public bool IsLatched { get; set; }
|
||||
|
||||
public RTC()
|
||||
{
|
||||
BaseRegisters = new byte[NumRegisters];
|
||||
LatchedRegisters = new byte[NumRegisters];
|
||||
|
||||
BaseTime = DateTime.Now;
|
||||
|
||||
IsSelected = false;
|
||||
SelectedRegister = 0;
|
||||
IsLatched = false;
|
||||
}
|
||||
|
||||
public void FromSaveData(byte[] ramData)
|
||||
{
|
||||
var rtcOffset = ramData.Length - 0x30;
|
||||
|
||||
// Time
|
||||
BaseRegisters[0x00] = ramData[rtcOffset + 0];
|
||||
BaseRegisters[0x01] = ramData[rtcOffset + 4];
|
||||
BaseRegisters[0x02] = ramData[rtcOffset + 8];
|
||||
BaseRegisters[0x03] = ramData[rtcOffset + 12];
|
||||
BaseRegisters[0x04] = ramData[rtcOffset + 16];
|
||||
|
||||
// Latched time
|
||||
LatchedRegisters[0x00] = ramData[rtcOffset + 20];
|
||||
LatchedRegisters[0x01] = ramData[rtcOffset + 24];
|
||||
LatchedRegisters[0x02] = ramData[rtcOffset + 28];
|
||||
LatchedRegisters[0x03] = ramData[rtcOffset + 32];
|
||||
LatchedRegisters[0x04] = ramData[rtcOffset + 36];
|
||||
|
||||
// Timestamp
|
||||
BaseTime = DateTimeOffset.FromUnixTimeSeconds((long)BitConverter.ToUInt64(ramData, rtcOffset + 40)).UtcDateTime;
|
||||
}
|
||||
|
||||
public byte[] ToSaveData()
|
||||
{
|
||||
var appendData = new byte[0x30];
|
||||
|
||||
// Time
|
||||
appendData[0] = BaseRegisters[0x00];
|
||||
appendData[4] = BaseRegisters[0x01];
|
||||
appendData[8] = BaseRegisters[0x02];
|
||||
appendData[12] = BaseRegisters[0x03];
|
||||
appendData[16] = BaseRegisters[0x04];
|
||||
|
||||
// Latched time
|
||||
appendData[20] = LatchedRegisters[0x00];
|
||||
appendData[24] = LatchedRegisters[0x01];
|
||||
appendData[28] = LatchedRegisters[0x02];
|
||||
appendData[32] = LatchedRegisters[0x03];
|
||||
appendData[36] = LatchedRegisters[0x04];
|
||||
|
||||
// Timestamp
|
||||
var timestamp = BitConverter.GetBytes(((DateTimeOffset)BaseTime).ToUnixTimeSeconds());
|
||||
for (var i = 0; i < timestamp.Length; i++) appendData[40 + i] = timestamp[i];
|
||||
|
||||
return appendData;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// GOLD,38695,3000 == 00931
|
||||
|
||||
|
||||
var currentTime = DateTime.Now;
|
||||
var newTime = currentTime;
|
||||
|
||||
if (((BaseRegisters[0x04] >> 6) & 0b1) == 0 && currentTime > BaseTime)
|
||||
newTime.Add(currentTime - BaseTime);
|
||||
|
||||
newTime.AddSeconds(BaseRegisters[0x00]);
|
||||
newTime.AddMinutes(BaseRegisters[0x01]);
|
||||
newTime.AddHours(BaseRegisters[0x02]);
|
||||
newTime.AddDays(BaseRegisters[0x03]);
|
||||
newTime.AddDays((BaseRegisters[0x04] & 0b1) << 8);
|
||||
|
||||
BaseRegisters[0x00] = (byte)newTime.Second;
|
||||
BaseRegisters[0x01] = (byte)newTime.Minute;
|
||||
BaseRegisters[0x02] = (byte)newTime.Hour;
|
||||
BaseRegisters[0x03] = (byte)(newTime.Day & 0xFF);
|
||||
BaseRegisters[0x04] = (byte)((BaseRegisters[0x04] & 0xFE) | ((newTime.Day >> 8) & 0b1) | ((newTime.Day >> 8) & 0b1) << 7);
|
||||
|
||||
BaseTime = currentTime;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] romData, ramData;
|
||||
bool hasBattery, hasRTC;
|
||||
|
||||
byte romBank, ramBank;
|
||||
bool ramEnable;
|
||||
|
||||
RTC rtc;
|
||||
|
||||
public MBC3Cartridge(int romSize, int ramSize)
|
||||
{
|
||||
romData = new byte[romSize];
|
||||
ramData = new byte[ramSize];
|
||||
|
||||
hasBattery = false;
|
||||
hasRTC = false;
|
||||
|
||||
romBank = 1;
|
||||
ramBank = 0;
|
||||
ramEnable = false;
|
||||
|
||||
rtc = new RTC();
|
||||
}
|
||||
|
||||
public void LoadRom(byte[] data)
|
||||
{
|
||||
Buffer.BlockCopy(data, 0, romData, 0, Math.Min(data.Length, romData.Length));
|
||||
}
|
||||
|
||||
public void LoadRam(byte[] data)
|
||||
{
|
||||
/* Has appended RTC state data? */
|
||||
if ((data.Length & 0x30) == 0x30) rtc.FromSaveData(data);
|
||||
|
||||
Buffer.BlockCopy(data, 0, ramData, 0, Math.Min(data.Length, ramData.Length));
|
||||
}
|
||||
|
||||
public byte[] GetRomData()
|
||||
{
|
||||
return romData;
|
||||
}
|
||||
|
||||
public byte[] GetRamData()
|
||||
{
|
||||
if (hasRTC)
|
||||
return ramData.Concat(rtc.ToSaveData()).ToArray();
|
||||
else
|
||||
return ramData;
|
||||
}
|
||||
|
||||
public bool IsRamSaveNeeded()
|
||||
{
|
||||
return hasBattery;
|
||||
}
|
||||
|
||||
public ushort GetLowerBound()
|
||||
{
|
||||
return 0x0000;
|
||||
}
|
||||
|
||||
public ushort GetUpperBound()
|
||||
{
|
||||
return 0x7FFF;
|
||||
}
|
||||
|
||||
public void SetCartridgeConfig(bool battery, bool rtc, bool rumble)
|
||||
{
|
||||
hasBattery = battery;
|
||||
hasRTC = rtc;
|
||||
}
|
||||
|
||||
public void Step(int clockCyclesInStep)
|
||||
{
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
public byte Read(ushort address)
|
||||
{
|
||||
if (address >= 0x0000 && address <= 0x3FFF)
|
||||
{
|
||||
return romData[address & 0x3FFF];
|
||||
}
|
||||
else if (address >= 0x4000 && address <= 0x7FFF)
|
||||
{
|
||||
return romData[(romBank << 14) | (address & 0x3FFF)];
|
||||
}
|
||||
else if (address >= 0xA000 && address <= 0xBFFF)
|
||||
{
|
||||
if (rtc.IsSelected)
|
||||
{
|
||||
if (rtc.IsLatched)
|
||||
return rtc.LatchedRegisters[rtc.SelectedRegister];
|
||||
else
|
||||
return rtc.BaseRegisters[rtc.SelectedRegister];
|
||||
}
|
||||
else if (ramEnable)
|
||||
return ramData[(ramBank << 13) | (address & 0x1FFF)];
|
||||
else
|
||||
return 0xFF;
|
||||
}
|
||||
else
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
public void Write(ushort address, byte value)
|
||||
{
|
||||
if (address >= 0x0000 && address <= 0x1FFF)
|
||||
{
|
||||
ramEnable = (value & 0x0F) == 0x0A;
|
||||
}
|
||||
else if (address >= 0x2000 && address <= 0x3FFF)
|
||||
{
|
||||
romBank = (byte)((romBank & 0x80) | (value & 0x7F));
|
||||
romBank &= (byte)((romData.Length >> 14) - 1);
|
||||
if (romBank == 0x00) romBank = 0x01;
|
||||
}
|
||||
else if (address >= 0x4000 && address <= 0x5FFF)
|
||||
{
|
||||
if (value >= 0x00 && value <= 0x07)
|
||||
{
|
||||
rtc.IsSelected = false;
|
||||
ramBank = (byte)(value & 0x07);
|
||||
}
|
||||
else if (value >= 0x08 && value <= 0x0C)
|
||||
{
|
||||
rtc.IsSelected = true;
|
||||
rtc.SelectedRegister = (byte)(value - 0x08);
|
||||
}
|
||||
}
|
||||
else if (address >= 0x6000 && address <= 0x7FFF)
|
||||
{
|
||||
if (value == 0x00 && rtc.IsLatched)
|
||||
rtc.IsLatched = false;
|
||||
else if (value == 0x01 && !rtc.IsLatched)
|
||||
{
|
||||
rtc.Update();
|
||||
for (var i = 0; i < RTC.NumRegisters; i++)
|
||||
rtc.LatchedRegisters[i] = rtc.BaseRegisters[i];
|
||||
rtc.IsLatched = true;
|
||||
}
|
||||
}
|
||||
else if (address >= 0xA000 && address <= 0xBFFF)
|
||||
{
|
||||
if (rtc.IsSelected)
|
||||
{
|
||||
rtc.Update();
|
||||
rtc.BaseRegisters[rtc.SelectedRegister] = value;
|
||||
}
|
||||
else if (ramEnable)
|
||||
ramData[(ramBank << 13) | (address & 0x1FFF)] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 739832bbd2b7ce54e8658790d0d1b4ae
|
@ -0,0 +1,137 @@
|
||||
using System;
|
||||
|
||||
namespace Essgee.Emulation.Cartridges.Nintendo
|
||||
{
|
||||
// TODO: rumble?
|
||||
|
||||
public class MBC5Cartridge : IGameBoyCartridge
|
||||
{
|
||||
public event EventHandler<EventArgs> EnableRumble;
|
||||
protected virtual void OnEnableRumble(EventArgs e) { EnableRumble?.Invoke(this, EventArgs.Empty); }
|
||||
|
||||
byte[] romData, ramData;
|
||||
bool hasBattery, hasRumble;
|
||||
|
||||
ushort romBank;
|
||||
byte ramBank;
|
||||
bool ramEnable;
|
||||
|
||||
public MBC5Cartridge(int romSize, int ramSize)
|
||||
{
|
||||
romData = new byte[romSize];
|
||||
ramData = new byte[ramSize];
|
||||
|
||||
romBank = 1;
|
||||
ramBank = 0;
|
||||
|
||||
ramEnable = false;
|
||||
|
||||
hasBattery = false;
|
||||
hasRumble = false;
|
||||
}
|
||||
|
||||
public void LoadRom(byte[] data)
|
||||
{
|
||||
Buffer.BlockCopy(data, 0, romData, 0, Math.Min(data.Length, romData.Length));
|
||||
}
|
||||
|
||||
public void LoadRam(byte[] data)
|
||||
{
|
||||
Buffer.BlockCopy(data, 0, ramData, 0, Math.Min(data.Length, ramData.Length));
|
||||
}
|
||||
|
||||
public byte[] GetRomData()
|
||||
{
|
||||
return romData;
|
||||
}
|
||||
|
||||
public byte[] GetRamData()
|
||||
{
|
||||
return ramData;
|
||||
}
|
||||
|
||||
public bool IsRamSaveNeeded()
|
||||
{
|
||||
return hasBattery;
|
||||
}
|
||||
|
||||
public ushort GetLowerBound()
|
||||
{
|
||||
return 0x0000;
|
||||
}
|
||||
|
||||
public ushort GetUpperBound()
|
||||
{
|
||||
return 0x7FFF;
|
||||
}
|
||||
|
||||
public void SetCartridgeConfig(bool battery, bool rtc, bool rumble)
|
||||
{
|
||||
hasBattery = battery;
|
||||
hasRumble = rumble;
|
||||
}
|
||||
|
||||
public void Step(int clockCyclesInStep)
|
||||
{
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
public byte Read(ushort address)
|
||||
{
|
||||
if (address >= 0x0000 && address <= 0x3FFF)
|
||||
{
|
||||
return romData[address & 0x3FFF];
|
||||
}
|
||||
else if (address >= 0x4000 && address <= 0x7FFF)
|
||||
{
|
||||
return romData[(romBank << 14) | (address & 0x3FFF)];
|
||||
}
|
||||
else if (address >= 0xA000 && address <= 0xBFFF)
|
||||
{
|
||||
if (ramEnable && ramData.Length != 0)
|
||||
return ramData[(ramBank << 13) | (address & 0x1FFF)];
|
||||
else
|
||||
return 0xFF;
|
||||
}
|
||||
else
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
public void Write(ushort address, byte value)
|
||||
{
|
||||
if (address >= 0x0000 && address <= 0x1FFF)
|
||||
{
|
||||
ramEnable = (value & 0x0F) == 0x0A;
|
||||
}
|
||||
else if (address >= 0x2000 && address <= 0x2FFF)
|
||||
{
|
||||
romBank = (ushort)((romBank & 0x0100) | value);
|
||||
romBank &= (ushort)((romData.Length >> 14) - 1);
|
||||
}
|
||||
else if (address >= 0x3000 && address <= 0x3FFF)
|
||||
{
|
||||
romBank = (ushort)((romBank & 0x00FF) | ((value & 0x01) << 8));
|
||||
romBank &= (ushort)((romData.Length >> 14) - 1);
|
||||
}
|
||||
else if (address >= 0x4000 && address <= 0x5FFF)
|
||||
{
|
||||
if (hasRumble)
|
||||
{
|
||||
if ((value & 0x08) == 0x08) OnEnableRumble(EventArgs.Empty);
|
||||
ramBank = (byte)(value & 0x07);
|
||||
ramBank %= (byte)(ramData.Length >> 13);
|
||||
}
|
||||
else
|
||||
{
|
||||
ramBank = (byte)(value & 0x0F);
|
||||
ramBank %= (byte)(ramData.Length >> 13);
|
||||
}
|
||||
}
|
||||
else if (address >= 0xA000 && address <= 0xBFFF)
|
||||
{
|
||||
if (ramEnable && ramData.Length != 0)
|
||||
ramData[(ramBank << 13) | (address & 0x1FFF)] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f083c0cfa9c1cf54ba90f19320c53c16
|
@ -0,0 +1,77 @@
|
||||
using System;
|
||||
|
||||
namespace Essgee.Emulation.Cartridges.Nintendo
|
||||
{
|
||||
public class NoMapperCartridge : IGameBoyCartridge
|
||||
{
|
||||
byte[] romData, ramData;
|
||||
bool hasBattery;
|
||||
|
||||
public NoMapperCartridge(int romSize, int ramSize)
|
||||
{
|
||||
romData = new byte[romSize];
|
||||
ramData = new byte[ramSize];
|
||||
}
|
||||
|
||||
public void LoadRom(byte[] data)
|
||||
{
|
||||
Buffer.BlockCopy(data, 0, romData, 0, Math.Min(data.Length, romData.Length));
|
||||
}
|
||||
|
||||
public void LoadRam(byte[] data)
|
||||
{
|
||||
Buffer.BlockCopy(data, 0, ramData, 0, Math.Min(data.Length, ramData.Length));
|
||||
}
|
||||
|
||||
public byte[] GetRomData()
|
||||
{
|
||||
return romData;
|
||||
}
|
||||
|
||||
public byte[] GetRamData()
|
||||
{
|
||||
return ramData;
|
||||
}
|
||||
|
||||
public bool IsRamSaveNeeded()
|
||||
{
|
||||
return hasBattery;
|
||||
}
|
||||
|
||||
public ushort GetLowerBound()
|
||||
{
|
||||
return 0x0000;
|
||||
}
|
||||
|
||||
public ushort GetUpperBound()
|
||||
{
|
||||
return 0x7FFF;
|
||||
}
|
||||
|
||||
public void Step(int clockCyclesInStep)
|
||||
{
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
public void SetCartridgeConfig(bool battery, bool rtc, bool rumble)
|
||||
{
|
||||
hasBattery = battery;
|
||||
}
|
||||
|
||||
public byte Read(ushort address)
|
||||
{
|
||||
if (address >= 0x0000 && address <= 0x7FFF)
|
||||
return romData[address & 0x7FFF];
|
||||
else if (address >= 0xA000 && address <= 0xBFFF)
|
||||
return ramData[address & 0x1FFF];
|
||||
else
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
public void Write(ushort address, byte value)
|
||||
{
|
||||
if (address >= 0xA000 && address <= 0xBFFF)
|
||||
ramData[address & 0x1FFF] = value;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 90768449a6b38a440b08bb61118b37b7
|
@ -0,0 +1,101 @@
|
||||
using Essgee.Exceptions;
|
||||
using System;
|
||||
|
||||
namespace Essgee.Emulation.Cartridges.Nintendo
|
||||
{
|
||||
public static class SpecializedLoader
|
||||
{
|
||||
public static IGameBoyCartridge CreateCartridgeInstance(byte[] romData, byte[] ramData, Type mapperType)
|
||||
{
|
||||
var romSize = -1;
|
||||
switch (romData[0x0148])
|
||||
{
|
||||
case 0x00: romSize = 32 * 1024; break;
|
||||
case 0x01: romSize = 64 * 1024; break;
|
||||
case 0x02: romSize = 128 * 1024; break;
|
||||
case 0x03: romSize = 256 * 1024; break;
|
||||
case 0x04: romSize = 512 * 1024; break;
|
||||
case 0x05: romSize = 1024 * 1024; break;
|
||||
case 0x06: romSize = 2048 * 1024; break;
|
||||
case 0x07: romSize = 4096 * 1024; break;
|
||||
case 0x08: romSize = 8192 * 1024; break;
|
||||
case 0x52: romSize = 1152 * 1024; break;
|
||||
case 0x53: romSize = 1280 * 1024; break;
|
||||
case 0x54: romSize = 1536 * 1024; break;
|
||||
|
||||
default: romSize = romData.Length; break;
|
||||
}
|
||||
|
||||
var ramSize = -1;
|
||||
switch (romData[0x0149])
|
||||
{
|
||||
case 0x00: ramSize = 0 * 1024; break;
|
||||
case 0x01: ramSize = 2 * 1024; break;
|
||||
case 0x02: ramSize = 8 * 1024; break;
|
||||
case 0x03: ramSize = 32 * 1024; break;
|
||||
case 0x04: ramSize = 128 * 1024; break;
|
||||
case 0x05: ramSize = 64 * 1024; break;
|
||||
|
||||
default: ramSize = 0; break;
|
||||
}
|
||||
|
||||
/* NOTES:
|
||||
* MBC2 internal RAM is not given in header, 512*4b == 256 bytes
|
||||
* GB Camera internal RAM ~seems~ to not be given in header? 128 kbytes
|
||||
*/
|
||||
|
||||
var mapperTypeFromHeader = typeof(NoMapperCartridge);
|
||||
var hasBattery = false;
|
||||
var hasRtc = false;
|
||||
var hasRumble = false;
|
||||
switch (romData[0x0147])
|
||||
{
|
||||
case 0x00: mapperType = typeof(NoMapperCartridge); break;
|
||||
case 0x01: mapperType = typeof(MBC1Cartridge); break;
|
||||
case 0x02: mapperType = typeof(MBC1Cartridge); break;
|
||||
case 0x03: mapperType = typeof(MBC1Cartridge); hasBattery = true; break;
|
||||
case 0x05: mapperType = typeof(MBC2Cartridge); ramSize = 0x100; break;
|
||||
case 0x06: mapperType = typeof(MBC2Cartridge); ramSize = 0x100; hasBattery = true; break;
|
||||
case 0x08: mapperType = typeof(NoMapperCartridge); break;
|
||||
case 0x09: mapperType = typeof(NoMapperCartridge); hasBattery = true; break;
|
||||
// 0B-0D, MMM01
|
||||
case 0x0F: mapperType = typeof(MBC3Cartridge); hasBattery = true; hasRtc = true; break;
|
||||
case 0x10: mapperType = typeof(MBC3Cartridge); hasBattery = true; hasRtc = true; break;
|
||||
case 0x11: mapperType = typeof(MBC3Cartridge); break;
|
||||
case 0x12: mapperType = typeof(MBC3Cartridge); break;
|
||||
case 0x13: mapperType = typeof(MBC3Cartridge); hasBattery = true; break;
|
||||
case 0x19: mapperType = typeof(MBC5Cartridge); break;
|
||||
case 0x1A: mapperType = typeof(MBC5Cartridge); break;
|
||||
case 0x1B: mapperType = typeof(MBC5Cartridge); hasBattery = true; break;
|
||||
case 0x1C: mapperType = typeof(MBC5Cartridge); hasRumble = true; break;
|
||||
case 0x1D: mapperType = typeof(MBC5Cartridge); hasRumble = true; break;
|
||||
case 0x1E: mapperType = typeof(MBC5Cartridge); hasBattery = true; hasRumble = true; break;
|
||||
// 20, MBC6
|
||||
// 22, MBC7
|
||||
case 0xFC: mapperType = typeof(GBCameraCartridge); ramSize = 128 * 1024; break;
|
||||
// FD, BANDAI TAMA5
|
||||
// FE, HuC3
|
||||
// FF, HuC1
|
||||
|
||||
default: throw new EmulationException($"Unimplemented cartridge type 0x{romData[0x0147]:X2}");
|
||||
}
|
||||
|
||||
if (mapperType == null)
|
||||
mapperType = mapperTypeFromHeader;
|
||||
|
||||
if (romSize != romData.Length)
|
||||
{
|
||||
var romSizePadded = 1;
|
||||
while (romSizePadded < romData.Length) romSizePadded <<= 1;
|
||||
romSize = Math.Max(romSizePadded, romData.Length);
|
||||
}
|
||||
|
||||
var cartridge = (IGameBoyCartridge)Activator.CreateInstance(mapperType, new object[] { romSize, ramSize });
|
||||
cartridge.LoadRom(romData);
|
||||
cartridge.LoadRam(ramData);
|
||||
cartridge.SetCartridgeConfig(hasBattery, hasRtc, hasRumble);
|
||||
|
||||
return cartridge;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 40e53d5096e06264a829b803422f047e
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ce378fe5a7c0ff14e8ffd0ad6c86ac65
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,120 @@
|
||||
using Essgee.Exceptions;
|
||||
using Essgee.Utilities;
|
||||
using System;
|
||||
|
||||
namespace Essgee.Emulation.Cartridges.Sega
|
||||
{
|
||||
public class CodemastersCartridge : ICartridge
|
||||
{
|
||||
byte[] romData;
|
||||
|
||||
[StateRequired]
|
||||
byte[] ramData;
|
||||
|
||||
[StateRequired]
|
||||
readonly byte[] pagingRegisters;
|
||||
[StateRequired]
|
||||
readonly byte bankMask;
|
||||
|
||||
[StateRequired]
|
||||
bool isRamEnabled;
|
||||
|
||||
public CodemastersCartridge(int romSize, int ramSize)
|
||||
{
|
||||
pagingRegisters = new byte[3];
|
||||
pagingRegisters[0] = 0x00;
|
||||
pagingRegisters[1] = 0x01;
|
||||
pagingRegisters[2] = 0x02;
|
||||
|
||||
romData = new byte[romSize];
|
||||
ramData = new byte[ramSize];
|
||||
|
||||
bankMask = (byte)((romData.Length >> 14) - 1);
|
||||
|
||||
isRamEnabled = false;
|
||||
}
|
||||
|
||||
public void LoadRom(byte[] data)
|
||||
{
|
||||
Buffer.BlockCopy(data, 0, romData, 0, Math.Min(data.Length, romData.Length));
|
||||
}
|
||||
|
||||
public void LoadRam(byte[] data)
|
||||
{
|
||||
Buffer.BlockCopy(data, 0, ramData, 0, Math.Min(data.Length, ramData.Length));
|
||||
}
|
||||
|
||||
public byte[] GetRomData()
|
||||
{
|
||||
return romData;
|
||||
}
|
||||
|
||||
public byte[] GetRamData()
|
||||
{
|
||||
return ramData;
|
||||
}
|
||||
|
||||
public bool IsRamSaveNeeded()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public ushort GetLowerBound()
|
||||
{
|
||||
return 0x0000;
|
||||
}
|
||||
|
||||
public ushort GetUpperBound()
|
||||
{
|
||||
return 0xBFFF;
|
||||
}
|
||||
|
||||
public void Step(int clockCyclesInStep)
|
||||
{
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
public byte Read(ushort address)
|
||||
{
|
||||
switch (address & 0xC000)
|
||||
{
|
||||
case 0x0000:
|
||||
return romData[((pagingRegisters[0] << 14) | (address & 0x3FFF))];
|
||||
|
||||
case 0x4000:
|
||||
return romData[((pagingRegisters[1] << 14) | (address & 0x3FFF))];
|
||||
|
||||
case 0x8000:
|
||||
if (isRamEnabled && (address >= 0xA000 && address <= 0xBFFF))
|
||||
return ramData[address & 0x1FFF];
|
||||
else
|
||||
return romData[((pagingRegisters[2] << 14) | (address & 0x3FFF))];
|
||||
|
||||
default:
|
||||
throw new EmulationException(string.Format("Codemasters mapper: Cannot read from cartridge address 0x{0:X4}", address));
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(ushort address, byte value)
|
||||
{
|
||||
switch (address)
|
||||
{
|
||||
case 0x0000:
|
||||
pagingRegisters[0] = (byte)(value & bankMask);
|
||||
break;
|
||||
|
||||
case 0x4000:
|
||||
pagingRegisters[1] = (byte)(value & bankMask);
|
||||
isRamEnabled = ((value & 0x80) == 0x80);
|
||||
break;
|
||||
|
||||
case 0x8000:
|
||||
pagingRegisters[2] = (byte)(value & bankMask);
|
||||
break;
|
||||
}
|
||||
|
||||
if (isRamEnabled && ((address & 0xF000) == 0xA000 || (address & 0xF000) == 0xB000))
|
||||
ramData[address & 0x1FFF] = value;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f7cca9db8813d3c45a1ce0cfb7792b71
|
@ -0,0 +1,91 @@
|
||||
using Essgee.Exceptions;
|
||||
using Essgee.Utilities;
|
||||
using System;
|
||||
|
||||
namespace Essgee.Emulation.Cartridges.Sega
|
||||
{
|
||||
public class KoreanMSX8kMapperCartridge : ICartridge
|
||||
{
|
||||
byte[] romData;
|
||||
|
||||
[StateRequired]
|
||||
readonly byte[] pagingRegisters;
|
||||
|
||||
[StateRequired]
|
||||
byte bankMask;
|
||||
|
||||
public KoreanMSX8kMapperCartridge(int romSize, int ramSize)
|
||||
{
|
||||
pagingRegisters = new byte[4];
|
||||
|
||||
romData = new byte[romSize];
|
||||
}
|
||||
|
||||
public void LoadRom(byte[] data)
|
||||
{
|
||||
Buffer.BlockCopy(data, 0, romData, 0, Math.Min(data.Length, romData.Length));
|
||||
|
||||
var romSizeRounded = 1;
|
||||
while (romSizeRounded < romData.Length) romSizeRounded <<= 1;
|
||||
|
||||
bankMask = (byte)((romSizeRounded >> 13) - 1);
|
||||
}
|
||||
|
||||
public void LoadRam(byte[] data)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public byte[] GetRomData()
|
||||
{
|
||||
return romData;
|
||||
}
|
||||
|
||||
public byte[] GetRamData()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsRamSaveNeeded()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public ushort GetLowerBound()
|
||||
{
|
||||
return 0x0000;
|
||||
}
|
||||
|
||||
public ushort GetUpperBound()
|
||||
{
|
||||
return 0xBFFF;
|
||||
}
|
||||
|
||||
public void Step(int clockCyclesInStep)
|
||||
{
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
public byte Read(ushort address)
|
||||
{
|
||||
switch (address & 0xE000)
|
||||
{
|
||||
case 0x0000: return romData[(0x00 << 13) | (address & 0x1FFF)];
|
||||
case 0x2000: return romData[(0x01 << 13) | (address & 0x1FFF)];
|
||||
case 0x4000: return romData[(pagingRegisters[2] << 13) | (address & 0x1FFF)];
|
||||
case 0x6000: return romData[(pagingRegisters[3] << 13) | (address & 0x1FFF)];
|
||||
case 0x8000: return romData[(pagingRegisters[0] << 13) | (address & 0x1FFF)];
|
||||
case 0xA000: return romData[(pagingRegisters[1] << 13) | (address & 0x1FFF)];
|
||||
default: throw new EmulationException(string.Format("Korean MSX 8k mapper: Cannot read from cartridge address 0x{0:X4}", address));
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(ushort address, byte value)
|
||||
{
|
||||
if (address >= 0x0000 && address <= 0x0003)
|
||||
{
|
||||
pagingRegisters[address & 0x0003] = (byte)(value & bankMask);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ec1ba102183679945bca01f524bb1868
|
@ -0,0 +1,94 @@
|
||||
using Essgee.Exceptions;
|
||||
using Essgee.Utilities;
|
||||
using System;
|
||||
|
||||
namespace Essgee.Emulation.Cartridges.Sega
|
||||
{
|
||||
public class KoreanMapperCartridge : ICartridge
|
||||
{
|
||||
byte[] romData;
|
||||
|
||||
[StateRequired]
|
||||
byte bankMask, pagingRegister;
|
||||
|
||||
public KoreanMapperCartridge(int romSize, int ramSize)
|
||||
{
|
||||
pagingRegister = 0x02;
|
||||
|
||||
romData = new byte[romSize];
|
||||
}
|
||||
|
||||
public void LoadRom(byte[] data)
|
||||
{
|
||||
Buffer.BlockCopy(data, 0, romData, 0, Math.Min(data.Length, romData.Length));
|
||||
|
||||
var romSizeRounded = 1;
|
||||
while (romSizeRounded < romData.Length) romSizeRounded <<= 1;
|
||||
|
||||
bankMask = (byte)((romSizeRounded >> 14) - 1);
|
||||
}
|
||||
|
||||
public void LoadRam(byte[] data)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public byte[] GetRomData()
|
||||
{
|
||||
return romData;
|
||||
}
|
||||
|
||||
public byte[] GetRamData()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsRamSaveNeeded()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public ushort GetLowerBound()
|
||||
{
|
||||
return 0x0000;
|
||||
}
|
||||
|
||||
public ushort GetUpperBound()
|
||||
{
|
||||
return 0xBFFF;
|
||||
}
|
||||
|
||||
public void Step(int clockCyclesInStep)
|
||||
{
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
public byte Read(ushort address)
|
||||
{
|
||||
switch (address & 0xC000)
|
||||
{
|
||||
case 0x0000:
|
||||
return romData[address & 0x3FFF];
|
||||
|
||||
case 0x4000:
|
||||
return romData[(0x01 << 14) | (address & 0x3FFF)];
|
||||
|
||||
case 0x8000:
|
||||
return romData[((pagingRegister << 14) | (address & 0x3FFF))];
|
||||
|
||||
default:
|
||||
throw new EmulationException(string.Format("Korean mapper: Cannot read from cartridge address 0x{0:X4}", address));
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(ushort address, byte value)
|
||||
{
|
||||
switch (address)
|
||||
{
|
||||
case 0xA000:
|
||||
pagingRegister = (byte)(value & bankMask);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 510ae156eb8cf4e4fb7c5fa573062f07
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user