This commit is contained in:
commit
89e4c5a674
|
@ -0,0 +1,161 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>9.0.21022</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{197D4314-8A9F-49BA-977D-54ACEFAEB6BA}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>BizHawk.Emulation</RootNamespace>
|
||||
<AssemblyName>BizHawk.Emulation</AssemblyName>
|
||||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml.Linq">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Data.DataSetExtensions">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Consoles\Sega\SMS\MemoryMap.CodeMasters.cs" />
|
||||
<Compile Include="Consoles\Sega\SMS\MemoryMap.Sega.cs" />
|
||||
<Compile Include="CPUs\68000\Diassembler.cs" />
|
||||
<Compile Include="CPUs\68000\Instructions\BitArithemetic.cs" />
|
||||
<Compile Include="CPUs\68000\Instructions\DataMovement.cs" />
|
||||
<Compile Include="CPUs\68000\Instructions\IntegerMath.cs" />
|
||||
<Compile Include="CPUs\68000\Instructions\ProgramFlow.cs" />
|
||||
<Compile Include="CPUs\68000\Instructions\Supervisor.cs" />
|
||||
<Compile Include="CPUs\68000\M68000.cs" />
|
||||
<Compile Include="CPUs\68000\Memory.cs" />
|
||||
<Compile Include="CPUs\68000\OpcodeTable.cs" />
|
||||
<Compile Include="CPUs\68000\Tables.cs" />
|
||||
<Compile Include="CPUs\HuC6280\Disassembler.cs" />
|
||||
<Compile Include="CPUs\HuC6280\Execute.cs" />
|
||||
<Compile Include="CPUs\HuC6280\HuC6280.cs" />
|
||||
<Compile Include="CPUs\MC68000\Helpers.cs" />
|
||||
<Compile Include="CPUs\MC68000\IMemoryController.cs" />
|
||||
<Compile Include="CPUs\MC68000\MC68K.cs" />
|
||||
<Compile Include="CPUs\MC68000\MemoryAccess.cs" />
|
||||
<Compile Include="CPUs\MC68000\Operations\BitManipulation.cs" />
|
||||
<Compile Include="CPUs\MC68000\Operations\DataMovement.cs" />
|
||||
<Compile Include="CPUs\MC68000\Operations\IntegerArithmetic.cs" />
|
||||
<Compile Include="CPUs\MC68000\Operations\Logical.cs" />
|
||||
<Compile Include="CPUs\MC68000\Operations\Multiprocessor.cs" />
|
||||
<Compile Include="CPUs\MC68000\Operations\ProgramControl.cs" />
|
||||
<Compile Include="CPUs\MC68000\Operations\ShiftRotate.cs" />
|
||||
<Compile Include="CPUs\MC68000\Operations\SystemControl.cs" />
|
||||
<Compile Include="CPUs\MC68000\OpTable.cs" />
|
||||
<Compile Include="CPUs\MOS 6502\Disassembler.cs" />
|
||||
<Compile Include="CPUs\MOS 6502\Execute.cs" />
|
||||
<Compile Include="CPUs\MOS 6502\MOS6502.cs" />
|
||||
<Compile Include="CPUs\x86\Disassembler.cs" />
|
||||
<Compile Include="CPUs\x86\Execute.cs" />
|
||||
<Compile Include="CPUs\x86\Timing.cs" />
|
||||
<Compile Include="CPUs\x86\x86.cs" />
|
||||
<Compile Include="CPUs\Z80-GB\Disassembler.cs" />
|
||||
<Compile Include="CPUs\Z80-GB\Execute.cs" />
|
||||
<Compile Include="CPUs\Z80-GB\Interrupts.cs" />
|
||||
<Compile Include="CPUs\Z80-GB\Registers.cs" />
|
||||
<Compile Include="CPUs\Z80-GB\Tables.cs" />
|
||||
<Compile Include="CPUs\Z80-GB\Z80.cs" />
|
||||
<Compile Include="CPUs\Z80\Disassembler.cs" />
|
||||
<Compile Include="CPUs\Z80\Execute.cs" />
|
||||
<Compile Include="CPUs\Z80\Interrupts.cs" />
|
||||
<Compile Include="CPUs\Z80\Registers.cs" />
|
||||
<Compile Include="CPUs\Z80\Tables.cs" />
|
||||
<Compile Include="CPUs\Z80\Z80A.cs" />
|
||||
<Compile Include="Consoles\Gameboy\Bios.cs" />
|
||||
<Compile Include="Consoles\Gameboy\Gameboy.cs" />
|
||||
<Compile Include="Consoles\Gameboy\Input.cs" />
|
||||
<Compile Include="Consoles\Gameboy\Mappers.cs" />
|
||||
<Compile Include="Database\CRC32.cs" />
|
||||
<Compile Include="Database\Database.cs" />
|
||||
<Compile Include="Interfaces\Base Implementations\Game.cs" />
|
||||
<Compile Include="Interfaces\Base Implementations\IPS.cs" />
|
||||
<Compile Include="Interfaces\Base Implementations\Movies.cs" />
|
||||
<Compile Include="Interfaces\Base Implementations\NullController.cs" />
|
||||
<Compile Include="Interfaces\Base Implementations\NullEmulator.cs" />
|
||||
<Compile Include="Interfaces\Base Implementations\SmdGame.cs" />
|
||||
<Compile Include="Sound\Utilities\BufferedAsync.cs" />
|
||||
<Compile Include="Sound\Utilities\Metaspu.cs" />
|
||||
<Compile Include="Interfaces\IController.cs" />
|
||||
<Compile Include="Interfaces\IEmulator.cs" />
|
||||
<Compile Include="Interfaces\IGame.cs" />
|
||||
<Compile Include="Interfaces\ISoundProvider.cs" />
|
||||
<Compile Include="Interfaces\IVideoProvider.cs" />
|
||||
<Compile Include="Log.cs" />
|
||||
<Compile Include="Consoles\PC Engine\Input.cs" />
|
||||
<Compile Include="Consoles\PC Engine\MemoryMap.cs" />
|
||||
<Compile Include="Consoles\PC Engine\MemoryMap.SF2.cs" />
|
||||
<Compile Include="Consoles\PC Engine\MemoryMap.SuperGrafx.cs" />
|
||||
<Compile Include="Consoles\PC Engine\PCEngine.cs" />
|
||||
<Compile Include="Sound\HuC6280PSG.cs" />
|
||||
<Compile Include="Consoles\PC Engine\VCE.cs" />
|
||||
<Compile Include="Consoles\PC Engine\VDC.cs" />
|
||||
<Compile Include="Consoles\PC Engine\VDC.Render.cs" />
|
||||
<Compile Include="Consoles\PC Engine\VPC.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Consoles\Sega\Genesis\Genesis.cs" />
|
||||
<Compile Include="Consoles\Sega\Genesis\Genesis.Input.cs" />
|
||||
<Compile Include="Consoles\Sega\Genesis\GenVDP.cs" />
|
||||
<Compile Include="Consoles\Sega\Genesis\GenVDP.DMA.cs" />
|
||||
<Compile Include="Consoles\Sega\Genesis\GenVDP.Render.cs" />
|
||||
<Compile Include="Consoles\Sega\Genesis\IO.cs" />
|
||||
<Compile Include="Consoles\Sega\Genesis\MemoryMap.68000.cs" />
|
||||
<Compile Include="Consoles\Sega\Genesis\MemoryMap.Z80.cs" />
|
||||
<Compile Include="Sound\YM2612.cs" />
|
||||
<Compile Include="Consoles\Sega\SMS\BIOS.cs" />
|
||||
<Compile Include="Sound\Utilities\SoundMixer.cs" />
|
||||
<Compile Include="Consoles\Sega\SMS\Input.cs" />
|
||||
<Compile Include="Sound\SN76489.cs" />
|
||||
<Compile Include="Consoles\Sega\SMS\SMS.cs" />
|
||||
<Compile Include="Consoles\Sega\SMS\VDP.cs" />
|
||||
<Compile Include="Sound\YM2413.cs" />
|
||||
<Compile Include="Util.cs" />
|
||||
<Compile Include="Sound\Utilities\Waves.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Consoles\PC Engine\Compat.txt" />
|
||||
<Content Include="Consoles\Sega\SMS\Compat.txt" />
|
||||
<Content Include="Notes.txt" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,85 @@
|
|||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.CPUs.M68K
|
||||
{
|
||||
public class DisassemblyInfo
|
||||
{
|
||||
public int PC;
|
||||
public string Mnemonic;
|
||||
public string Args;
|
||||
public string RawBytes;
|
||||
public int Length;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0:X6} {3,-20} {1,-8} {2}", PC, Mnemonic, Args, RawBytes);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class M68000
|
||||
{
|
||||
public DisassemblyInfo Disassemble(int pc)
|
||||
{
|
||||
var info = new DisassemblyInfo { Mnemonic = "UNKNOWN", PC = pc, Length = 2 };
|
||||
op = (ushort)ReadWord(pc);
|
||||
|
||||
if (Opcodes[op] == MOVE) MOVE_Disasm(info);
|
||||
else if (Opcodes[op] == MOVEA) MOVEA_Disasm(info);
|
||||
else if (Opcodes[op] == MOVEQ) MOVEQ_Disasm(info);
|
||||
else if (Opcodes[op] == MOVEM0) MOVEM0_Disasm(info);
|
||||
else if (Opcodes[op] == MOVEM1) MOVEM1_Disasm(info);
|
||||
else if (Opcodes[op] == LEA) LEA_Disasm(info);
|
||||
else if (Opcodes[op] == CLR) CLR_Disasm(info);
|
||||
else if (Opcodes[op] == EXT) EXT_Disasm(info);
|
||||
|
||||
else if (Opcodes[op] == ANDI) ANDI_Disasm(info);
|
||||
else if (Opcodes[op] == ORI) ORI_Disasm(info);
|
||||
else if (Opcodes[op] == LSLd) LSLd_Disasm(info);
|
||||
else if (Opcodes[op] == LSRd) LSRd_Disasm(info);
|
||||
else if (Opcodes[op] == ASLd) ASLd_Disasm(info);
|
||||
else if (Opcodes[op] == ASRd) ASRd_Disasm(info);
|
||||
else if (Opcodes[op] == ROLd) ROLd_Disasm(info);
|
||||
else if (Opcodes[op] == RORd) RORd_Disasm(info);
|
||||
else if (Opcodes[op] == SWAP) SWAP_Disasm(info);
|
||||
|
||||
else if (Opcodes[op] == JMP) JMP_Disasm(info);
|
||||
else if (Opcodes[op] == JSR) JSR_Disasm(info);
|
||||
else if (Opcodes[op] == Bcc) Bcc_Disasm(info);
|
||||
else if (Opcodes[op] == BRA) BRA_Disasm(info);
|
||||
else if (Opcodes[op] == BSR) BSR_Disasm(info);
|
||||
else if (Opcodes[op] == DBcc) DBcc_Disasm(info);
|
||||
else if (Opcodes[op] == RTS) RTS_Disasm(info);
|
||||
else if (Opcodes[op] == TST) TST_Disasm(info);
|
||||
else if (Opcodes[op] == BTSTi) BTSTi_Disasm(info);
|
||||
else if (Opcodes[op] == BTSTr) BTSTr_Disasm(info);
|
||||
else if (Opcodes[op] == LINK) LINK_Disasm(info);
|
||||
else if (Opcodes[op] == NOP) NOP_Disasm(info);
|
||||
|
||||
else if (Opcodes[op] == ADD0) ADD_Disasm(info);
|
||||
else if (Opcodes[op] == ADD1) ADD_Disasm(info);
|
||||
else if (Opcodes[op] == ADDA) ADDA_Disasm(info);
|
||||
else if (Opcodes[op] == ADDI) ADDI_Disasm(info);
|
||||
else if (Opcodes[op] == ADDQ) ADDQ_Disasm(info);
|
||||
else if (Opcodes[op] == SUB0) SUB_Disasm(info);
|
||||
else if (Opcodes[op] == SUB1) SUB_Disasm(info);
|
||||
else if (Opcodes[op] == SUBA) SUBA_Disasm(info);
|
||||
else if (Opcodes[op] == SUBI) SUBI_Disasm(info);
|
||||
else if (Opcodes[op] == SUBQ) SUBQ_Disasm(info);
|
||||
else if (Opcodes[op] == CMP) CMP_Disasm(info);
|
||||
else if (Opcodes[op] == CMPA) CMPA_Disasm(info);
|
||||
else if (Opcodes[op] == CMPI) CMPI_Disasm(info);
|
||||
|
||||
else if (Opcodes[op] == MOVEtSR) MOVEtSR_Disasm(info);
|
||||
else if (Opcodes[op] == MOVEfSR) MOVEfSR_Disasm(info);
|
||||
else if (Opcodes[op] == MOVEUSP) MOVEUSP_Disasm(info);
|
||||
else if (Opcodes[op] == ORI_SR) ORI_SR_Disasm(info);
|
||||
|
||||
var sb = new StringBuilder();
|
||||
for (int p = info.PC; p < info.PC + info.Length; p++)
|
||||
sb.AppendFormat("{0:X2}", ReadByte(p));
|
||||
info.RawBytes = sb.ToString();
|
||||
|
||||
return info;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,608 @@
|
|||
using System;
|
||||
|
||||
namespace BizHawk.Emulation.CPUs.M68K
|
||||
{
|
||||
public partial class M68000
|
||||
{
|
||||
private void ANDI() // AND immediate
|
||||
{
|
||||
int size = ((op >> 6) & 0x03);
|
||||
int dstMode = ((op >> 3) & 0x07);
|
||||
int dstReg = (op & 0x07);
|
||||
|
||||
V = false;
|
||||
C = false;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // Byte
|
||||
{
|
||||
sbyte imm = (sbyte) ReadWord(PC); PC += 2;
|
||||
sbyte arg = PeekValueB(dstMode, dstReg);
|
||||
sbyte result = (sbyte) (imm & arg);
|
||||
WriteValueB(dstMode, dstReg, result);
|
||||
PendingCycles -= (dstMode == 0) ? 8 : 12 + EACyclesBW[dstMode, dstReg];
|
||||
N = (result < 0);
|
||||
Z = (result == 0);
|
||||
return;
|
||||
}
|
||||
case 1: // Word
|
||||
{
|
||||
short imm = ReadWord(PC); PC += 2;
|
||||
short arg = PeekValueW(dstMode, dstReg);
|
||||
short result = (short) (imm & arg);
|
||||
WriteValueW(dstMode, dstReg, result);
|
||||
PendingCycles -= (dstMode == 0) ? 8 : 12 + EACyclesBW[dstMode, dstReg];
|
||||
N = (result < 0);
|
||||
Z = (result == 0);
|
||||
return;
|
||||
}
|
||||
case 2: // Long
|
||||
{
|
||||
int imm = ReadLong(PC); PC += 2;
|
||||
int arg = PeekValueL(dstMode, dstReg);
|
||||
int result = imm & arg;
|
||||
WriteValueL(dstMode, dstReg, result);
|
||||
PendingCycles -= (dstMode == 0) ? 8 : 12 + EACyclesL[dstMode, dstReg];
|
||||
N = (result < 0);
|
||||
Z = (result == 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ANDI_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int size = ((op >> 6) & 0x03);
|
||||
int dstMode = ((op >> 3) & 0x07);
|
||||
int dstReg = (op & 0x07);
|
||||
|
||||
int pc = info.PC + 2;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // Byte
|
||||
{
|
||||
info.Mnemonic = "andi.b";
|
||||
sbyte imm = (sbyte)ReadWord(pc); pc += 2;
|
||||
info.Args = string.Format("${0:X}, ", imm);
|
||||
info.Args += DisassembleValue(dstMode, dstReg, 1, ref pc);
|
||||
break;
|
||||
}
|
||||
case 1: // Word
|
||||
{
|
||||
info.Mnemonic = "andi.w";
|
||||
short imm = ReadWord(pc); pc += 2;
|
||||
info.Args = string.Format("${0:X}, ", imm);
|
||||
info.Args += DisassembleValue(dstMode, dstReg, 2, ref pc);
|
||||
break;
|
||||
}
|
||||
case 2: // Long
|
||||
{
|
||||
info.Mnemonic = "andi.l";
|
||||
int imm = ReadLong(pc); pc += 4;
|
||||
info.Args = string.Format("${0:X}, ", imm);
|
||||
info.Args += DisassembleValue(dstMode, dstReg, 4, ref pc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void ORI()
|
||||
{
|
||||
int size = (op >> 6) & 3;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
|
||||
V = C = false;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // byte
|
||||
{
|
||||
sbyte immed = (sbyte) ReadWord(PC); PC += 2;
|
||||
sbyte value = (sbyte) (PeekValueB(mode, reg) | immed);
|
||||
WriteValueB(mode, reg, value);
|
||||
N = value < 0;
|
||||
Z = value == 0;
|
||||
PendingCycles -= mode == 0 ? 8 : 12 + EACyclesBW[mode, reg];
|
||||
return;
|
||||
}
|
||||
case 1: // word
|
||||
{
|
||||
short immed = ReadWord(PC); PC += 2;
|
||||
short value = (short)(PeekValueW(mode, reg) | immed);
|
||||
WriteValueW(mode, reg, value);
|
||||
N = value < 0;
|
||||
Z = value == 0;
|
||||
PendingCycles -= mode == 0 ? 8 : 12 + EACyclesBW[mode, reg];
|
||||
return;
|
||||
}
|
||||
case 2: // long
|
||||
{
|
||||
int immed = ReadLong(PC); PC += 4;
|
||||
int value = PeekValueL(mode, reg) | immed;
|
||||
WriteValueL(mode, reg, value);
|
||||
N = value < 0;
|
||||
Z = value == 0;
|
||||
PendingCycles -= mode == 0 ? 17 : 20 + EACyclesL[mode, reg];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ORI_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
int size = (op >> 6) & 3;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // byte
|
||||
{
|
||||
info.Mnemonic = "ori.b";
|
||||
sbyte immed = (sbyte) ReadWord(pc); pc += 2;
|
||||
info.Args = String.Format("${0:X}, {1}", immed, DisassembleValue(mode, reg, 1, ref pc));
|
||||
break;
|
||||
}
|
||||
case 1: // word
|
||||
{
|
||||
info.Mnemonic = "ori.w";
|
||||
short immed = ReadWord(pc); pc += 2;
|
||||
info.Args = String.Format("${0:X}, {1}", immed, DisassembleValue(mode, reg, 2, ref pc));
|
||||
break;
|
||||
}
|
||||
case 2: // long
|
||||
{
|
||||
info.Mnemonic = "ori.l";
|
||||
int immed = ReadLong(pc); pc += 4;
|
||||
info.Args = String.Format("${0:X}, {1}", immed, DisassembleValue(mode, reg, 4, ref pc));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void LSLd()
|
||||
{
|
||||
int rot = (op >> 9) & 7;
|
||||
int size = (op >> 6) & 3;
|
||||
int m = (op >> 5) & 1;
|
||||
int reg = op & 7;
|
||||
|
||||
if (m == 0 && rot == 0) rot = 8;
|
||||
else if (m == 1) rot = D[rot].s32 & 63;
|
||||
|
||||
V = false;
|
||||
C = false;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // byte
|
||||
for (int i=0; i<rot; i++)
|
||||
{
|
||||
C = X = (D[reg].u8 & 0x80) != 0;
|
||||
D[reg].u8 <<= 1;
|
||||
}
|
||||
N = D[reg].s8 < 0;
|
||||
Z = D[reg].u8 == 0;
|
||||
PendingCycles -= 6 + (rot * 2);
|
||||
return;
|
||||
case 1: // word
|
||||
for (int i = 0; i < rot; i++)
|
||||
{
|
||||
C = X = (D[reg].u16 & 0x8000) != 0;
|
||||
D[reg].u16 <<= 1;
|
||||
}
|
||||
N = D[reg].s16 < 0;
|
||||
Z = D[reg].u16 == 0;
|
||||
PendingCycles -= 6 + (rot * 2);
|
||||
return;
|
||||
case 2: // long
|
||||
for (int i = 0; i < rot; i++)
|
||||
{
|
||||
C = X = (D[reg].u32 & 0x80000000) != 0;
|
||||
D[reg].u32 <<= 1;
|
||||
}
|
||||
N = D[reg].s32 < 0;
|
||||
Z = D[reg].u32 == 0;
|
||||
PendingCycles -= 8 + (rot * 2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void LSLd_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
int rot = (op >> 9) & 7;
|
||||
int size = (op >> 6) & 3;
|
||||
int m = (op >> 5) & 1;
|
||||
int reg = op & 7;
|
||||
|
||||
if (m == 0 && rot == 0) rot = 8;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: info.Mnemonic = "lsl.b"; break;
|
||||
case 1: info.Mnemonic = "lsl.w"; break;
|
||||
case 2: info.Mnemonic = "lsl.l"; break;
|
||||
}
|
||||
if (m==0) info.Args = rot+", D"+reg;
|
||||
else info.Args = "D"+rot+", D"+reg;
|
||||
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void LSRd()
|
||||
{
|
||||
int rot = (op >> 9) & 7;
|
||||
int size = (op >> 6) & 3;
|
||||
int m = (op >> 5) & 1;
|
||||
int reg = op & 7;
|
||||
|
||||
if (m == 0 && rot == 0) rot = 8;
|
||||
else if (m == 1) rot = D[rot].s32 & 63;
|
||||
|
||||
V = false;
|
||||
C = false;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // byte
|
||||
for (int i = 0; i < rot; i++)
|
||||
{
|
||||
C = X = (D[reg].u8 & 1) != 0;
|
||||
D[reg].u8 >>= 1;
|
||||
}
|
||||
N = D[reg].s8 < 0;
|
||||
Z = D[reg].u8 == 0;
|
||||
PendingCycles -= 6 + (rot * 2);
|
||||
return;
|
||||
case 1: // word
|
||||
for (int i = 0; i < rot; i++)
|
||||
{
|
||||
C = X = (D[reg].u16 & 1) != 0;
|
||||
D[reg].u16 >>= 1;
|
||||
}
|
||||
N = D[reg].s16 < 0;
|
||||
Z = D[reg].u16 == 0;
|
||||
PendingCycles -= 6 + (rot * 2);
|
||||
return;
|
||||
case 2: // long
|
||||
for (int i = 0; i < rot; i++)
|
||||
{
|
||||
C = X = (D[reg].u32 & 1) != 0;
|
||||
D[reg].u32 >>= 1;
|
||||
}
|
||||
N = D[reg].s32 < 0;
|
||||
Z = D[reg].u32 == 0;
|
||||
PendingCycles -= 8 + (rot * 2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void LSRd_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
int rot = (op >> 9) & 7;
|
||||
int size = (op >> 6) & 3;
|
||||
int m = (op >> 5) & 1;
|
||||
int reg = op & 7;
|
||||
|
||||
if (m == 0 && rot == 0) rot = 8;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: info.Mnemonic = "lsr.b"; break;
|
||||
case 1: info.Mnemonic = "lsr.w"; break;
|
||||
case 2: info.Mnemonic = "lsr.l"; break;
|
||||
}
|
||||
if (m == 0) info.Args = rot + ", D" + reg;
|
||||
else info.Args = "D" + rot + ", D" + reg;
|
||||
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void ASLd()
|
||||
{
|
||||
int rot = (op >> 9) & 7;
|
||||
int size = (op >> 6) & 3;
|
||||
int m = (op >> 5) & 1;
|
||||
int reg = op & 7;
|
||||
|
||||
if (m == 0 && rot == 0) rot = 8;
|
||||
else if (m == 1) rot = D[rot].s32 & 63;
|
||||
|
||||
V = false;
|
||||
C = false;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // byte
|
||||
for (int i = 0; i < rot; i++)
|
||||
{
|
||||
C = X = (D[reg].u8 & 0x80) != 0;
|
||||
D[reg].s8 <<= 1;
|
||||
}
|
||||
N = D[reg].s8 < 0;
|
||||
Z = D[reg].u8 == 0;
|
||||
PendingCycles -= 6 + (rot * 2);
|
||||
return;
|
||||
case 1: // word
|
||||
for (int i = 0; i < rot; i++)
|
||||
{
|
||||
C = X = (D[reg].u16 & 0x8000) != 0;
|
||||
D[reg].s16 <<= 1;
|
||||
}
|
||||
N = D[reg].s16 < 0;
|
||||
Z = D[reg].u16 == 0;
|
||||
PendingCycles -= 6 + (rot * 2);
|
||||
return;
|
||||
case 2: // long
|
||||
for (int i = 0; i < rot; i++)
|
||||
{
|
||||
C = X = (D[reg].u32 & 0x80000000) != 0;
|
||||
D[reg].s32 <<= 1;
|
||||
}
|
||||
N = D[reg].s32 < 0;
|
||||
Z = D[reg].u32 == 0;
|
||||
PendingCycles -= 8 + (rot * 2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void ASLd_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
int rot = (op >> 9) & 7;
|
||||
int size = (op >> 6) & 3;
|
||||
int m = (op >> 5) & 1;
|
||||
int reg = op & 7;
|
||||
|
||||
if (m == 0 && rot == 0) rot = 8;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: info.Mnemonic = "asl.b"; break;
|
||||
case 1: info.Mnemonic = "asl.w"; break;
|
||||
case 2: info.Mnemonic = "asl.l"; break;
|
||||
}
|
||||
if (m == 0) info.Args = rot + ", D" + reg;
|
||||
else info.Args = "D" + rot + ", D" + reg;
|
||||
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void ASRd()
|
||||
{
|
||||
int rot = (op >> 9) & 7;
|
||||
int size = (op >> 6) & 3;
|
||||
int m = (op >> 5) & 1;
|
||||
int reg = op & 7;
|
||||
|
||||
if (m == 0 && rot == 0) rot = 8;
|
||||
else if (m == 1) rot = D[rot].s32 & 63;
|
||||
|
||||
V = false;
|
||||
C = false;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // byte
|
||||
for (int i = 0; i < rot; i++)
|
||||
{
|
||||
C = X = (D[reg].u8 & 1) != 0;
|
||||
D[reg].s8 >>= 1;
|
||||
}
|
||||
N = D[reg].s8 < 0;
|
||||
Z = D[reg].u8 == 0;
|
||||
PendingCycles -= 6 + (rot * 2);
|
||||
return;
|
||||
case 1: // word
|
||||
for (int i = 0; i < rot; i++)
|
||||
{
|
||||
C = X = (D[reg].u16 & 1) != 0;
|
||||
D[reg].s16 >>= 1;
|
||||
}
|
||||
N = D[reg].s16 < 0;
|
||||
Z = D[reg].u16 == 0;
|
||||
PendingCycles -= 6 + (rot * 2);
|
||||
return;
|
||||
case 2: // long
|
||||
for (int i = 0; i < rot; i++)
|
||||
{
|
||||
C = X = (D[reg].u32 & 1) != 0;
|
||||
D[reg].s32 >>= 1;
|
||||
}
|
||||
N = D[reg].s32 < 0;
|
||||
Z = D[reg].u32 == 0;
|
||||
PendingCycles -= 8 + (rot * 2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void ASRd_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
int rot = (op >> 9) & 7;
|
||||
int size = (op >> 6) & 3;
|
||||
int m = (op >> 5) & 1;
|
||||
int reg = op & 7;
|
||||
|
||||
if (m == 0 && rot == 0) rot = 8;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: info.Mnemonic = "asr.b"; break;
|
||||
case 1: info.Mnemonic = "asr.w"; break;
|
||||
case 2: info.Mnemonic = "asr.l"; break;
|
||||
}
|
||||
if (m == 0) info.Args = rot + ", D" + reg;
|
||||
else info.Args = "D" + rot + ", D" + reg;
|
||||
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void ROLd()
|
||||
{
|
||||
int rot = (op >> 9) & 7;
|
||||
int size = (op >> 6) & 3;
|
||||
int m = (op >> 5) & 1;
|
||||
int reg = op & 7;
|
||||
|
||||
if (m == 0 && rot == 0) rot = 8;
|
||||
else if (m == 1) rot = D[rot].s32 & 63;
|
||||
|
||||
V = false;
|
||||
C = false;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // byte
|
||||
for (int i = 0; i < rot; i++)
|
||||
{
|
||||
C = (D[reg].u8 & 0x80) != 0;
|
||||
D[reg].u8 = (byte) ((D[reg].u8 << 1) | (D[reg].u8 >> 7));
|
||||
}
|
||||
N = D[reg].s8 < 0;
|
||||
Z = D[reg].u8 == 0;
|
||||
PendingCycles -= 6 + (rot * 2);
|
||||
return;
|
||||
case 1: // word
|
||||
for (int i = 0; i < rot; i++)
|
||||
{
|
||||
C = (D[reg].u16 & 0x8000) != 0;
|
||||
D[reg].u16 = (ushort) ((D[reg].u16 << 1) | (D[reg].u16 >> 15));
|
||||
}
|
||||
N = D[reg].s16 < 0;
|
||||
Z = D[reg].u16 == 0;
|
||||
PendingCycles -= 6 + (rot * 2);
|
||||
return;
|
||||
case 2: // long
|
||||
for (int i = 0; i < rot; i++)
|
||||
{
|
||||
C = (D[reg].u32 & 0x80000000) != 0;
|
||||
D[reg].u32 = ((D[reg].u32 << 1) | (D[reg].u32 >> 31));
|
||||
}
|
||||
N = D[reg].s32 < 0;
|
||||
Z = D[reg].u32 == 0;
|
||||
PendingCycles -= 8 + (rot * 2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void ROLd_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
int rot = (op >> 9) & 7;
|
||||
int size = (op >> 6) & 3;
|
||||
int m = (op >> 5) & 1;
|
||||
int reg = op & 7;
|
||||
|
||||
if (m == 0 && rot == 0) rot = 8;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: info.Mnemonic = "rol.b"; break;
|
||||
case 1: info.Mnemonic = "rol.w"; break;
|
||||
case 2: info.Mnemonic = "rol.l"; break;
|
||||
}
|
||||
if (m == 0) info.Args = rot + ", D" + reg;
|
||||
else info.Args = "D" + rot + ", D" + reg;
|
||||
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void RORd()
|
||||
{
|
||||
int rot = (op >> 9) & 7;
|
||||
int size = (op >> 6) & 3;
|
||||
int m = (op >> 5) & 1;
|
||||
int reg = op & 7;
|
||||
|
||||
if (m == 0 && rot == 0) rot = 8;
|
||||
else if (m == 1) rot = D[rot].s32 & 63;
|
||||
|
||||
V = false;
|
||||
C = false;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // byte
|
||||
for (int i = 0; i < rot; i++)
|
||||
{
|
||||
C = (D[reg].u8 & 1) != 0;
|
||||
D[reg].u8 = (byte)((D[reg].u8 >> 1) | (D[reg].u8 << 7));
|
||||
}
|
||||
N = D[reg].s8 < 0;
|
||||
Z = D[reg].u8 == 0;
|
||||
PendingCycles -= 6 + (rot * 2);
|
||||
return;
|
||||
case 1: // word
|
||||
for (int i = 0; i < rot; i++)
|
||||
{
|
||||
C = (D[reg].u16 & 1) != 0;
|
||||
D[reg].u16 = (ushort)((D[reg].u16 >> 1) | (D[reg].u16 << 15));
|
||||
}
|
||||
N = D[reg].s16 < 0;
|
||||
Z = D[reg].u16 == 0;
|
||||
PendingCycles -= 6 + (rot * 2);
|
||||
return;
|
||||
case 2: // long
|
||||
for (int i = 0; i < rot; i++)
|
||||
{
|
||||
C = (D[reg].u32 & 1) != 0;
|
||||
D[reg].u32 = ((D[reg].u32 >> 1) | (D[reg].u32 << 31));
|
||||
}
|
||||
N = D[reg].s32 < 0;
|
||||
Z = D[reg].u32 == 0;
|
||||
PendingCycles -= 8 + (rot * 2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void RORd_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
int rot = (op >> 9) & 7;
|
||||
int size = (op >> 6) & 3;
|
||||
int m = (op >> 5) & 1;
|
||||
int reg = op & 7;
|
||||
|
||||
if (m == 0 && rot == 0) rot = 8;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: info.Mnemonic = "ror.b"; break;
|
||||
case 1: info.Mnemonic = "ror.w"; break;
|
||||
case 2: info.Mnemonic = "ror.l"; break;
|
||||
}
|
||||
if (m == 0) info.Args = rot + ", D" + reg;
|
||||
else info.Args = "D" + rot + ", D" + reg;
|
||||
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void SWAP()
|
||||
{
|
||||
int reg = op & 7;
|
||||
D[reg].u32 = (D[reg].u32 << 16) | (D[reg].u32 >> 16);
|
||||
V = C = false;
|
||||
Z = D[reg].u32 == 0;
|
||||
N = D[reg].s32 < 0;
|
||||
PendingCycles -= 4;
|
||||
}
|
||||
|
||||
private void SWAP_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int reg = op & 7;
|
||||
info.Mnemonic = "swap";
|
||||
info.Args = "D" + reg;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,518 @@
|
|||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.CPUs.M68K
|
||||
{
|
||||
public partial class M68000
|
||||
{
|
||||
private void MOVE()
|
||||
{
|
||||
int size = ((op >> 12) & 0x03);
|
||||
int dstMode = ((op >> 6) & 0x07);
|
||||
int dstReg = ((op >> 9) & 0x07);
|
||||
int srcMode = ((op >> 3) & 0x07);
|
||||
int srcReg = (op & 0x07);
|
||||
|
||||
int value = 0;
|
||||
switch(size)
|
||||
{
|
||||
case 1: // Byte
|
||||
value = ReadValueB(srcMode, srcReg);
|
||||
WriteValueB(dstMode, dstReg, (sbyte) value);
|
||||
PendingCycles -= MoveCyclesBW[srcMode + (srcMode == 7 ? srcReg : 0), dstMode + (dstMode == 7 ? dstReg : 0)];
|
||||
N = (value < 0);
|
||||
break;
|
||||
case 3: // Word
|
||||
value = ReadValueW(srcMode, srcReg);
|
||||
WriteValueW(dstMode, dstReg, (short)value);
|
||||
PendingCycles -= MoveCyclesBW[srcMode + (srcMode == 7 ? srcReg : 0), dstMode + (dstMode == 7 ? dstReg : 0)];
|
||||
N = (value < 0);
|
||||
break;
|
||||
case 2: // Long
|
||||
value = ReadValueL(srcMode, srcReg);
|
||||
WriteValueL(dstMode, dstReg, value);
|
||||
PendingCycles -= MoveCyclesL[srcMode + (srcMode == 7 ? srcReg : 0), dstMode + (dstMode == 7 ? dstReg : 0)];
|
||||
N = (value < 0);
|
||||
break;
|
||||
}
|
||||
|
||||
V = false;
|
||||
C = false;
|
||||
Z = (value == 0);
|
||||
}
|
||||
|
||||
private void MOVE_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int size = ((op >> 12) & 0x03);
|
||||
int dstMode = ((op >> 6) & 0x07);
|
||||
int dstReg = ((op >> 9) & 0x07);
|
||||
int srcMode = ((op >> 3) & 0x07);
|
||||
int srcReg = (op & 0x07);
|
||||
|
||||
int pc = info.PC + 2;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 1:
|
||||
info.Mnemonic = "move.b";
|
||||
info.Args = DisassembleValue(srcMode, srcReg, 1, ref pc) +", ";
|
||||
info.Args += DisassembleValue(dstMode, dstReg, 1, ref pc);
|
||||
break;
|
||||
case 3:
|
||||
info.Mnemonic = "move.w";
|
||||
info.Args = DisassembleValue(srcMode, srcReg, 2, ref pc) + ", ";
|
||||
info.Args += DisassembleValue(dstMode, dstReg, 2, ref pc);
|
||||
break;
|
||||
case 2:
|
||||
info.Mnemonic = "move.l";
|
||||
info.Args = DisassembleValue(srcMode, srcReg, 4, ref pc) + ", ";
|
||||
info.Args += DisassembleValue(dstMode, dstReg, 4, ref pc);
|
||||
break;
|
||||
}
|
||||
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void MOVEA()
|
||||
{
|
||||
int size = ((op >> 12) & 0x03);
|
||||
int dstReg = ((op >> 9) & 0x07);
|
||||
int srcMode = ((op >> 3) & 0x07);
|
||||
int srcReg = (op & 0x07);
|
||||
|
||||
if (size == 3) // Word
|
||||
{
|
||||
A[dstReg].s32 = ReadValueW(srcMode, srcReg);
|
||||
PendingCycles -= EACyclesBW[srcMode, srcReg]; // TODO this is wrong, check pg 957
|
||||
} else { // Long
|
||||
A[dstReg].s32 = ReadValueL(srcMode, srcReg);
|
||||
PendingCycles -= EACyclesL[srcMode, srcReg]; // TODO this is wrong, check pg 957
|
||||
}
|
||||
}
|
||||
|
||||
private void MOVEA_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int size = ((op >> 12) & 0x03);
|
||||
int dstReg = ((op >> 9) & 0x07);
|
||||
int srcMode = ((op >> 3) & 0x07);
|
||||
int srcReg = (op & 0x07);
|
||||
int pc = info.PC + 2;
|
||||
|
||||
if (size == 3)
|
||||
{
|
||||
info.Mnemonic = "movea.w";
|
||||
info.Args = DisassembleValue(srcMode, srcReg, 2, ref pc) + ", A" + dstReg;
|
||||
} else {
|
||||
info.Mnemonic = "movea.l";
|
||||
info.Args = DisassembleValue(srcMode, srcReg, 4, ref pc) + ", A" + dstReg;
|
||||
}
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void MOVEQ()
|
||||
{
|
||||
int value = (sbyte) op; // 8-bit data payload is sign-extended to 32-bits.
|
||||
N = (value < 0);
|
||||
Z = (value == 0);
|
||||
V = false;
|
||||
C = false;
|
||||
D[(op >> 9) & 7].s32 = value;
|
||||
PendingCycles -= 4;
|
||||
}
|
||||
|
||||
private void MOVEQ_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
info.Mnemonic = "moveq";
|
||||
info.Args = String.Format("{0}, D{1}", (sbyte) op, (op >> 9) & 7);
|
||||
}
|
||||
|
||||
private void MOVEM0()
|
||||
{
|
||||
// Move register to memory
|
||||
int size = (op >> 6) & 1;
|
||||
int dstMode = (op >> 3) & 7;
|
||||
int dstReg = (op >> 0) & 7;
|
||||
|
||||
ushort registers = (ushort) ReadWord(PC); PC += 2;
|
||||
int address = ReadAddress(dstMode, dstReg);
|
||||
int regCount = 0;
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
// word-assign
|
||||
if (dstMode == 4) // decrement address
|
||||
{
|
||||
for (int i = 7; i >= 0; i--)
|
||||
{
|
||||
if ((registers & 1) == 1)
|
||||
{
|
||||
address -= 2;
|
||||
WriteWord(address, A[i].s16);
|
||||
regCount++;
|
||||
}
|
||||
registers >>= 1;
|
||||
}
|
||||
for (int i = 7; i >= 0; i--)
|
||||
{
|
||||
if ((registers & 1) == 1)
|
||||
{
|
||||
address -= 2;
|
||||
WriteWord(address, D[i].s16);
|
||||
regCount++;
|
||||
}
|
||||
registers >>= 1;
|
||||
}
|
||||
A[dstReg].s32 = address;
|
||||
}
|
||||
else
|
||||
{ // increment address
|
||||
for (int i = 7; i >= 0; i--)
|
||||
{
|
||||
if ((registers & 1) == 1)
|
||||
{
|
||||
WriteWord(address, A[i].s16);
|
||||
address += 2;
|
||||
regCount++;
|
||||
}
|
||||
registers >>= 1;
|
||||
}
|
||||
for (int i = 7; i >= 0; i--)
|
||||
{
|
||||
if ((registers & 1) == 1)
|
||||
{
|
||||
WriteWord(address, D[i].s16);
|
||||
address += 2;
|
||||
regCount++;
|
||||
}
|
||||
registers >>= 1;
|
||||
}
|
||||
}
|
||||
PendingCycles -= regCount*4;
|
||||
} else {
|
||||
// long-assign
|
||||
if (dstMode == 4) // decrement address
|
||||
{
|
||||
for (int i=7; i>= 0; i--)
|
||||
{
|
||||
if ((registers & 1) == 1)
|
||||
{
|
||||
address -= 4;
|
||||
WriteLong(address, A[i].s32);
|
||||
regCount++;
|
||||
}
|
||||
registers >>= 1;
|
||||
}
|
||||
for (int i = 7; i >= 0; i--)
|
||||
{
|
||||
if ((registers & 1) == 1)
|
||||
{
|
||||
address -= 4;
|
||||
WriteLong(address, D[i].s32);
|
||||
regCount++;
|
||||
}
|
||||
registers >>= 1;
|
||||
}
|
||||
A[dstReg].s32 = address;
|
||||
} else { // increment address
|
||||
for (int i = 7; i >= 0; i--)
|
||||
{
|
||||
if ((registers & 1) == 1)
|
||||
{
|
||||
WriteLong(address, A[i].s32);
|
||||
address += 4;
|
||||
regCount++;
|
||||
}
|
||||
registers >>= 1;
|
||||
}
|
||||
for (int i = 7; i >= 0; i--)
|
||||
{
|
||||
if ((registers & 1) == 1)
|
||||
{
|
||||
WriteLong(address, D[i].s32);
|
||||
address += 4;
|
||||
regCount++;
|
||||
}
|
||||
registers >>= 1;
|
||||
}
|
||||
}
|
||||
PendingCycles -= regCount * 8;
|
||||
}
|
||||
|
||||
switch (dstMode)
|
||||
{
|
||||
case 2: PendingCycles -= 8; break;
|
||||
case 4: PendingCycles -= 8; break;
|
||||
case 5: PendingCycles -= 12; break;
|
||||
case 6: PendingCycles -= 14; break;
|
||||
case 7:
|
||||
switch (dstReg)
|
||||
{
|
||||
case 0: PendingCycles -= 12; break;
|
||||
case 1: PendingCycles -= 16; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void MOVEM1()
|
||||
{
|
||||
// Move memory to register
|
||||
int size = (op >> 6) & 1;
|
||||
int srcMode = (op >> 3) & 7;
|
||||
int srcReg = (op >> 0) & 7;
|
||||
|
||||
ushort registers = (ushort)ReadWord(PC); PC += 2;
|
||||
int address = ReadAddress(srcMode, srcReg);
|
||||
int regCount = 0;
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
// word-assign
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if ((registers & 1) == 1)
|
||||
{
|
||||
D[i].s32 = ReadWord(address);
|
||||
address += 2;
|
||||
regCount++;
|
||||
}
|
||||
registers >>= 1;
|
||||
}
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if ((registers & 1) == 1)
|
||||
{
|
||||
A[i].s32 = ReadWord(address);
|
||||
address += 2;
|
||||
regCount++;
|
||||
}
|
||||
registers >>= 1;
|
||||
}
|
||||
PendingCycles -= regCount * 4;
|
||||
if (srcMode == 3)
|
||||
A[srcReg].s32 = address;
|
||||
} else {
|
||||
// long-assign
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if ((registers & 1) == 1)
|
||||
{
|
||||
D[i].s32 = ReadLong(address);
|
||||
address += 4;
|
||||
regCount++;
|
||||
}
|
||||
registers >>= 1;
|
||||
}
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if ((registers & 1) == 1)
|
||||
{
|
||||
A[i].s32 = ReadLong(address);
|
||||
address += 4;
|
||||
regCount++;
|
||||
}
|
||||
registers >>= 1;
|
||||
}
|
||||
PendingCycles -= regCount * 8;
|
||||
if (srcMode == 3)
|
||||
A[srcReg].s32 = address;
|
||||
}
|
||||
|
||||
switch (srcMode)
|
||||
{
|
||||
case 2: PendingCycles -= 12; break;
|
||||
case 4: PendingCycles -= 12; break;
|
||||
case 5: PendingCycles -= 16; break;
|
||||
case 6: PendingCycles -= 18; break;
|
||||
case 7:
|
||||
switch (srcReg)
|
||||
{
|
||||
case 0: PendingCycles -= 16; break;
|
||||
case 1: PendingCycles -= 20; break;
|
||||
case 2: PendingCycles -= 16; break;
|
||||
case 3: PendingCycles -= 18; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static string DisassembleRegisterList0(ushort registers)
|
||||
{
|
||||
var str = new StringBuilder();
|
||||
int count = 0;
|
||||
for (int i = 0; i<8; i++)
|
||||
{
|
||||
if ((registers & 0x8000) != 0)
|
||||
{
|
||||
if (count > 0) str.Append(",");
|
||||
str.Append("D"+i);
|
||||
count++;
|
||||
}
|
||||
registers <<= 1;
|
||||
}
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if ((registers & 0x8000) != 0)
|
||||
{
|
||||
if (count > 0) str.Append("/");
|
||||
str.Append("A"+i);
|
||||
count++;
|
||||
}
|
||||
registers <<= 1;
|
||||
}
|
||||
return str.ToString();
|
||||
}
|
||||
|
||||
private static string DisassembleRegisterList1(ushort registers)
|
||||
{
|
||||
var str = new StringBuilder();
|
||||
int count = 0;
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if ((registers & 1) != 0)
|
||||
{
|
||||
if (count > 0) str.Append(",");
|
||||
str.Append("D" + i);
|
||||
count++;
|
||||
}
|
||||
registers >>= 1;
|
||||
}
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if ((registers & 1) != 0)
|
||||
{
|
||||
if (count > 0) str.Append("/");
|
||||
str.Append("A" + i);
|
||||
count++;
|
||||
}
|
||||
registers >>= 1;
|
||||
}
|
||||
return str.ToString();
|
||||
}
|
||||
|
||||
private void MOVEM0_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int size = (op >> 6) & 1;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
int pc = info.PC + 2;
|
||||
|
||||
string address = DisassembleAddress(mode, reg, ref pc);
|
||||
ushort registers = (ushort) ReadWord(pc); pc += 2;
|
||||
|
||||
info.Mnemonic = size == 0 ? "movem.w" : "movem.l";
|
||||
info.Args = DisassembleRegisterList0(registers) + ", " + address;
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void MOVEM1_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int size = (op >> 6) & 1;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
int pc = info.PC + 2;
|
||||
|
||||
string address = DisassembleAddress(mode, reg, ref pc);
|
||||
ushort registers = (ushort)ReadWord(pc); pc += 2;
|
||||
|
||||
info.Mnemonic = size == 0 ? "movem.w" : "movem.l";
|
||||
info.Args = address + ", " + DisassembleRegisterList1(registers);
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void LEA()
|
||||
{
|
||||
int mode = (op >> 3) & 7;
|
||||
int sReg = (op >> 0) & 7;
|
||||
int dReg = (op >> 9) & 7;
|
||||
|
||||
A[dReg].u32 = (uint)ReadAddress(mode, sReg);
|
||||
switch (mode)
|
||||
{
|
||||
case 2: PendingCycles -= 4; break;
|
||||
case 5: PendingCycles -= 8; break;
|
||||
case 6: PendingCycles -= 12; break;
|
||||
case 7:
|
||||
switch (sReg)
|
||||
{
|
||||
case 0: PendingCycles -= 8; break;
|
||||
case 1: PendingCycles -= 12; break;
|
||||
case 2: PendingCycles -= 8; break;
|
||||
case 3: PendingCycles -= 12; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void LEA_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
|
||||
int mode = (op >> 3) & 7;
|
||||
int sReg = (op >> 0) & 7;
|
||||
int dReg = (op >> 9) & 7;
|
||||
|
||||
info.Mnemonic = "lea";
|
||||
info.Args = DisassembleAddress(mode, sReg, ref pc);
|
||||
info.Args += ", A"+dReg;
|
||||
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void CLR()
|
||||
{
|
||||
int size = (op >> 6) & 3;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: WriteValueB(mode, reg, 0); PendingCycles -= mode == 0 ? 4 : 8 + EACyclesBW[mode, reg]; break;
|
||||
case 1: WriteValueW(mode, reg, 0); PendingCycles -= mode == 0 ? 4 : 8 + EACyclesBW[mode, reg]; break;
|
||||
case 2: WriteValueL(mode, reg, 0); PendingCycles -= mode == 0 ? 6 : 12 + EACyclesL[mode, reg]; break;
|
||||
}
|
||||
|
||||
N = V = C = false;
|
||||
Z = true;
|
||||
}
|
||||
|
||||
private void CLR_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
int size = (op >> 6) & 3;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: info.Mnemonic = "clr.b"; info.Args = DisassembleValue(mode, reg, 1, ref pc); break;
|
||||
case 1: info.Mnemonic = "clr.w"; info.Args = DisassembleValue(mode, reg, 2, ref pc); break;
|
||||
case 2: info.Mnemonic = "clr.l"; info.Args = DisassembleValue(mode, reg, 4, ref pc); break;
|
||||
}
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void EXT()
|
||||
{
|
||||
int size = (op >> 6) & 1;
|
||||
int reg = op & 7;
|
||||
switch (size)
|
||||
{
|
||||
case 0: D[reg].s16 = D[reg].s8; break;
|
||||
case 1: D[reg].s32 = D[reg].s16; break;
|
||||
}
|
||||
PendingCycles -= 4;
|
||||
}
|
||||
|
||||
private void EXT_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int size = (op >> 6) & 1;
|
||||
int reg = op & 7;
|
||||
switch (size)
|
||||
{
|
||||
case 0: info.Mnemonic = "ext.w"; info.Args = "D" + reg; break;
|
||||
case 1: info.Mnemonic = "ext.l"; info.Args = "D" + reg; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,812 @@
|
|||
using System;
|
||||
|
||||
namespace BizHawk.Emulation.CPUs.M68K
|
||||
{
|
||||
public partial class M68000
|
||||
{
|
||||
private void ADD0()
|
||||
{
|
||||
int Dreg = (op >> 9) & 7;
|
||||
int size = (op >> 6) & 3;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // byte
|
||||
{
|
||||
int result = D[Dreg].s8 + ReadValueB(mode, reg);
|
||||
X = C = (result & 0x100) != 0;
|
||||
V = result > sbyte.MaxValue || result < sbyte.MinValue;
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
D[Dreg].s8 = (sbyte)result;
|
||||
PendingCycles -= 4 + EACyclesBW[mode, reg];
|
||||
return;
|
||||
}
|
||||
case 1: // word
|
||||
{
|
||||
int result = D[Dreg].s16 + ReadValueW(mode, reg);
|
||||
X = C = (result & 0x10000) != 0;
|
||||
V = result > short.MaxValue || result < short.MinValue;
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
D[Dreg].s16 = (short)result;
|
||||
PendingCycles -= 4 + EACyclesBW[mode, reg];
|
||||
return;
|
||||
}
|
||||
case 2: // long
|
||||
{
|
||||
long result = D[Dreg].s32 + ReadValueL(mode, reg);
|
||||
X = C = (result & 0x100000000) != 0;
|
||||
V = result > int.MaxValue || result < int.MinValue;
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
D[Dreg].s32 = (int)result;
|
||||
PendingCycles -= 6 + EACyclesL[mode, reg];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ADD1()
|
||||
{
|
||||
int Dreg = (op >> 9) & 7;
|
||||
int size = (op >> 6) & 3;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // byte
|
||||
{
|
||||
int result = PeekValueB(mode, reg) + D[Dreg].s8;
|
||||
X = C = (result & 0x100) != 0;
|
||||
V = result > sbyte.MaxValue || result < sbyte.MinValue;
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
WriteValueB(mode, reg, (sbyte)result);
|
||||
PendingCycles -= 8 + EACyclesBW[mode, reg];
|
||||
return;
|
||||
}
|
||||
case 1: // word
|
||||
{
|
||||
int result = PeekValueW(mode, reg) + D[Dreg].s16;
|
||||
X = C = (result & 0x10000) != 0;
|
||||
V = result > short.MaxValue || result < short.MinValue;
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
WriteValueW(mode, reg, (short)result);
|
||||
PendingCycles -= 8 + EACyclesBW[mode, reg];
|
||||
return;
|
||||
}
|
||||
case 2: // long
|
||||
{
|
||||
long result = PeekValueL(mode, reg) + D[Dreg].s32;
|
||||
X = C = (result & 0x100000000) != 0;
|
||||
V = result > int.MaxValue || result < int.MinValue;
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
WriteValueL(mode, reg, (int)result);
|
||||
PendingCycles -= 12 + EACyclesL[mode, reg];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ADD_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
|
||||
int Dreg = (op >> 9) & 7;
|
||||
int dir = (op >> 8) & 1;
|
||||
int size = (op >> 6) & 3;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
|
||||
string op1 = "D" + Dreg;
|
||||
string op2;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: info.Mnemonic = "add.b"; op2 = DisassembleValue(mode, reg, 1, ref pc); break;
|
||||
case 1: info.Mnemonic = "add.w"; op2 = DisassembleValue(mode, reg, 2, ref pc); break;
|
||||
default: info.Mnemonic = "add.l"; op2 = DisassembleValue(mode, reg, 4, ref pc); break;
|
||||
}
|
||||
info.Args = dir == 0 ? (op2 + ", " + op1) : (op1 + ", " + op2);
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void ADDI()
|
||||
{
|
||||
int size = (op >> 6) & 3;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // byte
|
||||
{
|
||||
int immed = (sbyte) ReadWord(PC); PC += 2;
|
||||
int result = PeekValueB(mode, reg) + immed;
|
||||
X = C = (result & 0x100) != 0;
|
||||
V = result > sbyte.MaxValue || result < sbyte.MinValue;
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
WriteValueB(mode, reg, (sbyte)result);
|
||||
if (mode == 0) PendingCycles -= 8;
|
||||
else PendingCycles -= 12 + EACyclesBW[mode, reg];
|
||||
return;
|
||||
}
|
||||
case 1: // word
|
||||
{
|
||||
int immed = ReadWord(PC); PC += 2;
|
||||
int result = PeekValueW(mode, reg) + immed;
|
||||
X = C = (result & 0x10000) != 0;
|
||||
V = result > short.MaxValue || result < short.MinValue;
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
WriteValueW(mode, reg, (short)result);
|
||||
if (mode == 0) PendingCycles -= 8;
|
||||
else PendingCycles -= 12 + EACyclesBW[mode, reg];
|
||||
return;
|
||||
}
|
||||
case 2: // long
|
||||
{
|
||||
int immed = ReadLong(PC); PC += 2;
|
||||
long result = PeekValueL(mode, reg) + immed;
|
||||
X = C = (result & 0x100000000) != 0;
|
||||
V = result > int.MaxValue || result < int.MinValue;
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
WriteValueL(mode, reg, (int)result);
|
||||
if (mode == 0) PendingCycles -= 16;
|
||||
else PendingCycles -= 20 + EACyclesBW[mode, reg];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ADDI_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
|
||||
int size = (op >> 6) & 3;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 3;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
info.Mnemonic = "addi.b";
|
||||
info.Args = DisassembleImmediate(1, ref pc) + ", " + DisassembleValue(mode, reg, 1, ref pc);
|
||||
break;
|
||||
case 1:
|
||||
info.Mnemonic = "addi.w";
|
||||
info.Args = DisassembleImmediate(2, ref pc) + ", " + DisassembleValue(mode, reg, 2, ref pc);
|
||||
break;
|
||||
case 2:
|
||||
info.Mnemonic = "addi.l";
|
||||
info.Args = DisassembleImmediate(4, ref pc) + ", " + DisassembleValue(mode, reg, 4, ref pc);
|
||||
break;
|
||||
}
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void ADDQ()
|
||||
{
|
||||
int data = (op >> 9) & 7;
|
||||
int size = (op >> 6) & 3;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
|
||||
data = data == 0 ? 8 : data; // range is 1-8; 0 represents 8
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // byte
|
||||
{
|
||||
if (mode == 1) throw new Exception("ADDQ.B on address reg is invalid");
|
||||
int result = PeekValueB(mode, reg) + data;
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
V = result > sbyte.MaxValue || result < sbyte.MinValue;
|
||||
C = X = (result & 0x100) != 0;
|
||||
WriteValueB(mode, reg, (sbyte) result);
|
||||
if (mode == 0) PendingCycles -= 4;
|
||||
else PendingCycles -= 8 + EACyclesBW[mode, reg];
|
||||
return;
|
||||
}
|
||||
case 1: // word
|
||||
{
|
||||
int result;
|
||||
if (mode == 1)
|
||||
{
|
||||
result = PeekValueL(mode, reg) + data;
|
||||
WriteValueL(mode, reg, (short) result);
|
||||
} else {
|
||||
result = PeekValueW(mode, reg) + data;
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
V = result > short.MaxValue || result < short.MinValue;
|
||||
C = X = (result & 0x10000) != 0;
|
||||
WriteValueW(mode, reg, (short)result);
|
||||
}
|
||||
if (mode <= 1) PendingCycles -= 4;
|
||||
else PendingCycles -= 8 + EACyclesBW[mode, reg];
|
||||
return;
|
||||
}
|
||||
default: // long
|
||||
{
|
||||
long result = PeekValueL(mode, reg) + data;
|
||||
if (mode != 1)
|
||||
{
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
V = result > int.MaxValue || result < int.MinValue;
|
||||
C = X = (result & 0x100000000) != 0;
|
||||
}
|
||||
WriteValueL(mode, reg, (int)result);
|
||||
if (mode <= 1) PendingCycles -= 8;
|
||||
else PendingCycles -= 12 + EACyclesL[mode, reg];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ADDQ_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
int data = (op >> 9) & 7;
|
||||
int size = (op >> 6) & 3;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
|
||||
data = data == 0 ? 8 : data; // range is 1-8; 0 represents 8
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: info.Mnemonic = "addq.b"; info.Args = data+", "+DisassembleValue(mode, reg, 1, ref pc); break;
|
||||
case 1: info.Mnemonic = "addq.w"; info.Args = data+", "+DisassembleValue(mode, reg, 2, ref pc); break;
|
||||
case 2: info.Mnemonic = "addq.l"; info.Args = data+", "+DisassembleValue(mode, reg, 4, ref pc); break;
|
||||
}
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void ADDA()
|
||||
{
|
||||
int aReg = (op >> 9) & 7;
|
||||
int size = (op >> 8) & 1;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
|
||||
if (size == 0) // word
|
||||
{
|
||||
int value = ReadValueW(mode, reg);
|
||||
A[aReg].s32 += value;
|
||||
PendingCycles -= 8 + EACyclesBW[mode, reg];
|
||||
} else { // long
|
||||
int value = ReadValueL(mode, reg);
|
||||
A[aReg].s32 -= value;
|
||||
PendingCycles += 6 + EACyclesL[mode, reg];
|
||||
}
|
||||
}
|
||||
|
||||
private void ADDA_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
|
||||
int aReg = (op >> 9) & 7;
|
||||
int size = (op >> 8) & 1;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
|
||||
info.Mnemonic = (size == 0) ? "adda.w" : "adda.l";
|
||||
info.Args = DisassembleValue(mode, reg, (size == 0) ? 2 : 4, ref pc) + ", A" + aReg;
|
||||
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void SUB0()
|
||||
{
|
||||
int Dreg = (op >> 9) & 7;
|
||||
int size = (op >> 6) & 3;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // byte
|
||||
{
|
||||
int result = D[Dreg].s8 - ReadValueB(mode, reg);
|
||||
X = C = (result & 0x100) != 0;
|
||||
V = result > sbyte.MaxValue || result < sbyte.MinValue;
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
D[Dreg].s8 = (sbyte) result;
|
||||
PendingCycles -= 4 + EACyclesBW[mode, reg];
|
||||
return;
|
||||
}
|
||||
case 1: // word
|
||||
{
|
||||
int result = D[Dreg].s16 - ReadValueW(mode, reg);
|
||||
X = C = (result & 0x10000) != 0;
|
||||
V = result > short.MaxValue || result < short.MinValue;
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
D[Dreg].s16 = (short) result;
|
||||
PendingCycles -= 4 + EACyclesBW[mode, reg];
|
||||
return;
|
||||
}
|
||||
case 2: // long
|
||||
{
|
||||
long result = D[Dreg].s32 - ReadValueL(mode, reg);
|
||||
X = C = (result & 0x100000000) != 0;
|
||||
V = result > int.MaxValue || result < int.MinValue;
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
D[Dreg].s32 = (int)result;
|
||||
PendingCycles -= 6 + EACyclesL[mode, reg];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SUB1()
|
||||
{
|
||||
int Dreg = (op >> 9) & 7;
|
||||
int size = (op >> 6) & 3;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // byte
|
||||
{
|
||||
int result = PeekValueB(mode, reg) - D[Dreg].s8;
|
||||
X = C = (result & 0x100) != 0;
|
||||
V = result > sbyte.MaxValue || result < sbyte.MinValue;
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
WriteValueB(mode, reg, (sbyte) result);
|
||||
PendingCycles -= 8 + EACyclesBW[mode, reg];
|
||||
return;
|
||||
}
|
||||
case 1: // word
|
||||
{
|
||||
int result = PeekValueW(mode, reg) - D[Dreg].s16;
|
||||
X = C = (result & 0x10000) != 0;
|
||||
V = result > short.MaxValue || result < short.MinValue;
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
WriteValueW(mode, reg, (short) result);
|
||||
PendingCycles -= 8 + EACyclesBW[mode, reg];
|
||||
return;
|
||||
}
|
||||
case 2: // long
|
||||
{
|
||||
long result = PeekValueL(mode, reg) - D[Dreg].s32;
|
||||
X = C = (result & 0x100000000) != 0;
|
||||
V = result > int.MaxValue || result < int.MinValue;
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
WriteValueL(mode, reg, (int) result);
|
||||
PendingCycles -= 12 + EACyclesL[mode, reg];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SUB_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
|
||||
int Dreg = (op >> 9) & 7;
|
||||
int dir = (op >> 8) & 1;
|
||||
int size = (op >> 6) & 3;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
|
||||
string op1 = "D" + Dreg;
|
||||
string op2;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: info.Mnemonic = "sub.b"; op2 = DisassembleValue(mode, reg, 1, ref pc); break;
|
||||
case 1: info.Mnemonic = "sub.w"; op2 = DisassembleValue(mode, reg, 2, ref pc); break;
|
||||
default: info.Mnemonic = "sub.l"; op2 = DisassembleValue(mode, reg, 4, ref pc); break;
|
||||
}
|
||||
info.Args = dir == 0 ? (op2 + ", " + op1) : (op1 + ", " + op2);
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void SUBI()
|
||||
{
|
||||
int size = (op >> 6) & 3;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // byte
|
||||
{
|
||||
int immed = (sbyte) ReadWord(PC); PC += 2;
|
||||
int result = PeekValueB(mode, reg) - immed;
|
||||
X = C = (result & 0x100) != 0;
|
||||
V = result > sbyte.MaxValue || result < sbyte.MinValue;
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
WriteValueB(mode, reg, (sbyte)result);
|
||||
if (mode == 0) PendingCycles -= 8;
|
||||
else PendingCycles -= 12 + EACyclesBW[mode, reg];
|
||||
return;
|
||||
}
|
||||
case 1: // word
|
||||
{
|
||||
int immed = ReadWord(PC); PC += 2;
|
||||
int result = PeekValueW(mode, reg) - immed;
|
||||
X = C = (result & 0x10000) != 0;
|
||||
V = result > short.MaxValue || result < short.MinValue;
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
WriteValueW(mode, reg, (short)result);
|
||||
if (mode == 0) PendingCycles -= 8;
|
||||
else PendingCycles -= 12 + EACyclesBW[mode, reg];
|
||||
return;
|
||||
}
|
||||
case 2: // long
|
||||
{
|
||||
int immed = ReadLong(PC); PC += 2;
|
||||
long result = PeekValueL(mode, reg) - immed;
|
||||
X = C = (result & 0x100000000) != 0;
|
||||
V = result > int.MaxValue || result < int.MinValue;
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
WriteValueL(mode, reg, (int)result);
|
||||
if (mode == 0) PendingCycles -= 16;
|
||||
else PendingCycles -= 20 + EACyclesBW[mode, reg];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SUBI_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
|
||||
int size = (op >> 6) & 3;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 3;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
info.Mnemonic = "subi.b";
|
||||
info.Args = DisassembleImmediate(1, ref pc) + ", " + DisassembleValue(mode, reg, 1, ref pc);
|
||||
break;
|
||||
case 1:
|
||||
info.Mnemonic = "subi.w";
|
||||
info.Args = DisassembleImmediate(2, ref pc) + ", " + DisassembleValue(mode, reg, 2, ref pc);
|
||||
break;
|
||||
case 2:
|
||||
info.Mnemonic = "subi.l";
|
||||
info.Args = DisassembleImmediate(4, ref pc) + ", " + DisassembleValue(mode, reg, 4, ref pc);
|
||||
break;
|
||||
}
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void SUBQ()
|
||||
{
|
||||
int data = (op >> 9) & 7;
|
||||
int size = (op >> 6) & 3;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
|
||||
data = data == 0 ? 8 : data; // range is 1-8; 0 represents 8
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // byte
|
||||
{
|
||||
if (mode == 1) throw new Exception("SUBQ.B on address reg is invalid");
|
||||
int result = PeekValueB(mode, reg) - data;
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
V = result > sbyte.MaxValue || result < sbyte.MinValue;
|
||||
C = X = (result & 0x100) != 0;
|
||||
WriteValueB(mode, reg, (sbyte) result);
|
||||
if (mode == 0) PendingCycles -= 4;
|
||||
else PendingCycles -= 8 + EACyclesBW[mode, reg];
|
||||
return;
|
||||
}
|
||||
case 1: // word
|
||||
{
|
||||
int result = PeekValueW(mode, reg) - data;
|
||||
if (mode != 1)
|
||||
{
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
V = result > short.MaxValue || result < short.MinValue;
|
||||
C = X = (result & 0x10000) != 0;
|
||||
}
|
||||
WriteValueW(mode, reg, (short)result);
|
||||
if (mode <= 1) PendingCycles -= 4;
|
||||
else PendingCycles -= 8 + EACyclesBW[mode, reg];
|
||||
return;
|
||||
}
|
||||
default: // long
|
||||
{
|
||||
long result = PeekValueL(mode, reg) - data;
|
||||
if (mode != 1)
|
||||
{
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
V = result > int.MaxValue || result < int.MinValue;
|
||||
C = X = (result & 0x100000000) != 0;
|
||||
}
|
||||
WriteValueL(mode, reg, (int)result);
|
||||
if (mode <= 1) PendingCycles -= 8;
|
||||
else PendingCycles -= 12 + EACyclesL[mode, reg];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SUBQ_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
int data = (op >> 9) & 7;
|
||||
int size = (op >> 6) & 3;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
|
||||
data = data == 0 ? 8 : data; // range is 1-8; 0 represents 8
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: info.Mnemonic = "subq.b"; info.Args = data+", "+DisassembleValue(mode, reg, 1, ref pc); break;
|
||||
case 1: info.Mnemonic = "subq.w"; info.Args = data+", "+DisassembleValue(mode, reg, 2, ref pc); break;
|
||||
case 2: info.Mnemonic = "subq.l"; info.Args = data+", "+DisassembleValue(mode, reg, 4, ref pc); break;
|
||||
}
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void SUBA()
|
||||
{
|
||||
int aReg = (op >> 9) & 7;
|
||||
int size = (op >> 8) & 1;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
|
||||
if (size == 0) // word
|
||||
{
|
||||
int value = ReadValueW(mode, reg);
|
||||
A[aReg].s32 -= value;
|
||||
PendingCycles -= 8 + EACyclesBW[mode, reg];
|
||||
} else { // long
|
||||
int value = ReadValueL(mode, reg);
|
||||
A[aReg].s32 -= value;
|
||||
PendingCycles -= 6 + EACyclesL[mode, reg];
|
||||
}
|
||||
}
|
||||
|
||||
private void SUBA_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
|
||||
int aReg = (op >> 9) & 7;
|
||||
int size = (op >> 8) & 1;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
|
||||
info.Mnemonic = (size == 0) ? "suba.w" : "suba.l";
|
||||
info.Args = DisassembleValue(mode, reg, (size == 0) ? 2 : 4, ref pc) + ", A"+aReg;
|
||||
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void CMP()
|
||||
{
|
||||
int dReg = (op >> 9) & 7;
|
||||
int size = (op >> 6) & 3;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // byte
|
||||
{
|
||||
int result = ReadValueB(mode, reg) - D[dReg].s8;
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
V = result > sbyte.MaxValue || result < sbyte.MinValue;
|
||||
C = (result & 0x100) != 0;
|
||||
if (mode == 0) PendingCycles -= 8;
|
||||
PendingCycles -= 4 + EACyclesBW[mode, reg];
|
||||
return;
|
||||
}
|
||||
case 1: // word
|
||||
{
|
||||
int result = ReadValueW(mode, reg) - D[dReg].s16;
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
V = result > short.MaxValue || result < short.MinValue;
|
||||
C = (result & 0x10000) != 0;
|
||||
if (mode == 0) PendingCycles -= 8;
|
||||
PendingCycles -= 4 + EACyclesBW[mode, reg];
|
||||
return;
|
||||
}
|
||||
case 2: // long
|
||||
{
|
||||
long result = ReadValueL(mode, reg) - D[dReg].s32;
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
V = result > int.MaxValue || result < int.MinValue;
|
||||
C = (result & 0x100000000) != 0;
|
||||
PendingCycles -= 6 + EACyclesL[mode, reg];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CMP_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
|
||||
int dReg = (op >> 9) & 7;
|
||||
int size = (op >> 6) & 3;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
info.Mnemonic = "cmp.b";
|
||||
info.Args = DisassembleValue(mode, reg, 1, ref pc) + ", D" + dReg;
|
||||
break;
|
||||
case 1:
|
||||
info.Mnemonic = "cmp.w";
|
||||
info.Args = DisassembleValue(mode, reg, 2, ref pc) + ", D" + dReg;
|
||||
break;
|
||||
case 2:
|
||||
info.Mnemonic = "cmp.l";
|
||||
info.Args = DisassembleValue(mode, reg, 4, ref pc) + ", D" + dReg;
|
||||
break;
|
||||
}
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void CMPA()
|
||||
{
|
||||
int aReg = (op >> 9) & 7;
|
||||
int size = (op >> 8) & 1;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // word
|
||||
{
|
||||
long result = A[aReg].s32 - ReadValueW(mode, reg);
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
V = result > int.MaxValue || result < int.MinValue;
|
||||
C = (result & 0x100000000) != 0;
|
||||
PendingCycles -= 6 + EACyclesBW[mode, reg];
|
||||
return;
|
||||
}
|
||||
case 1: // long
|
||||
{
|
||||
long result = A[aReg].s32 - ReadValueL(mode, reg);
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
V = result > int.MaxValue || result < int.MinValue;
|
||||
C = (result & 0x100000000) != 0;
|
||||
PendingCycles -= 6 + EACyclesL[mode, reg];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CMPA_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
|
||||
int aReg = (op >> 9) & 7;
|
||||
int size = (op >> 8) & 1;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
info.Mnemonic = "cmpa.w";
|
||||
info.Args = DisassembleValue(mode, reg, 2, ref pc) + ", A" + aReg;
|
||||
break;
|
||||
case 1:
|
||||
info.Mnemonic = "cmpa.l";
|
||||
info.Args = DisassembleValue(mode, reg, 4, ref pc) + ", A" + aReg;
|
||||
break;
|
||||
}
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
|
||||
private void CMPI()
|
||||
{
|
||||
int size = (op >> 6) & 3;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // byte
|
||||
{
|
||||
int immed = (sbyte) ReadWord(PC); PC += 2;
|
||||
int result = ReadValueB(mode, reg) - immed;
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
V = result > sbyte.MaxValue || result < sbyte.MinValue;
|
||||
C = (result & 0x100) != 0;
|
||||
if (mode == 0) PendingCycles -= 8;
|
||||
else PendingCycles -= 8 + EACyclesBW[mode, reg];
|
||||
return;
|
||||
}
|
||||
case 1: // word
|
||||
{
|
||||
int immed = ReadWord(PC); PC += 2;
|
||||
int result = ReadValueW(mode, reg) - immed;
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
V = result > short.MaxValue || result < short.MinValue;
|
||||
C = (result & 0x10000) != 0;
|
||||
if (mode == 0) PendingCycles -= 8;
|
||||
else PendingCycles -= 8 + EACyclesBW[mode, reg];
|
||||
return;
|
||||
}
|
||||
case 2: // long
|
||||
{
|
||||
int immed = ReadLong(PC); PC += 4;
|
||||
long result = ReadValueL(mode, reg) - immed;
|
||||
N = result < 0;
|
||||
Z = result == 0;
|
||||
V = result > int.MaxValue || result < int.MinValue;
|
||||
C = (result & 0x100000000) != 0;
|
||||
if (mode == 0) PendingCycles -= 14;
|
||||
else PendingCycles -= 12 + EACyclesL[mode, reg];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CMPI_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
int size = (op >> 6) & 3;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
int immediate;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
immediate = (byte)ReadWord(pc); pc += 2;
|
||||
info.Mnemonic = "cmpi.b";
|
||||
info.Args = String.Format("${0:X}, {1}", immediate, DisassembleValue(mode, reg, 1, ref pc));
|
||||
break;
|
||||
case 1:
|
||||
immediate = ReadWord(pc); pc += 2;
|
||||
info.Mnemonic = "cmpi.w";
|
||||
info.Args = String.Format("${0:X}, {1}", immediate, DisassembleValue(mode, reg, 2, ref pc));
|
||||
break;
|
||||
case 2:
|
||||
immediate = ReadLong(pc); pc += 4;
|
||||
info.Mnemonic = "cmpi.l";
|
||||
info.Args = String.Format("${0:X}, {1}", immediate, DisassembleValue(mode, reg, 4, ref pc));
|
||||
break;
|
||||
}
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,387 @@
|
|||
|
||||
using System;
|
||||
|
||||
namespace BizHawk.Emulation.CPUs.M68K
|
||||
{
|
||||
public partial class M68000
|
||||
{
|
||||
private bool TestCondition(int condition)
|
||||
{
|
||||
switch (condition)
|
||||
{
|
||||
case 0x00: return true; // True
|
||||
case 0x01: return false; // False
|
||||
case 0x02: return !C && !Z; // High (Unsigned)
|
||||
case 0x03: return C || Z; // Less or Same (Unsigned)
|
||||
case 0x04: return !C; // Carry Clear (High or Same)
|
||||
case 0x05: return C; // Carry Set (Lower)
|
||||
case 0x06: return !Z; // Not Equal
|
||||
case 0x07: return Z; // Equal
|
||||
case 0x08: return !V; // Overflow Clear
|
||||
case 0x09: return V; // Overflow Set
|
||||
case 0x0A: return !N; // Plus (Positive)
|
||||
case 0x0B: return N; // Minus (Negative)
|
||||
case 0x0C: return N && V || !N && !V; // Greater or Equal
|
||||
case 0x0D: return N && !V || !N && V; // Less Than
|
||||
case 0x0E: return N && V && !Z || !N && !V && !Z; // Greater Than
|
||||
case 0x0F: return Z || N && !V || !N && V; // Less or Equal
|
||||
default:
|
||||
throw new Exception("Invalid condition "+condition);
|
||||
}
|
||||
}
|
||||
|
||||
private string DisassembleCondition(int condition)
|
||||
{
|
||||
switch (condition)
|
||||
{
|
||||
case 0x00: return "t"; // True
|
||||
case 0x01: return "f"; // False
|
||||
case 0x02: return "hi"; // High (Unsigned)
|
||||
case 0x03: return "ls"; // Less or Same (Unsigned)
|
||||
case 0x04: return "cc"; // Carry Clear (High or Same)
|
||||
case 0x05: return "cs"; // Carry Set (Lower)
|
||||
case 0x06: return "ne"; // Not Equal
|
||||
case 0x07: return "eq"; // Equal
|
||||
case 0x08: return "vc"; // Overflow Clear
|
||||
case 0x09: return "vs"; // Overflow Set
|
||||
case 0x0A: return "pl"; // Plus (Positive)
|
||||
case 0x0B: return "mi"; // Minus (Negative)
|
||||
case 0x0C: return "ge"; // Greater or Equal
|
||||
case 0x0D: return "lt"; // Less Than
|
||||
case 0x0E: return "gt"; // Greater Than
|
||||
case 0x0F: return "le"; // Less or Equal
|
||||
default: return "??"; // Invalid condition
|
||||
}
|
||||
}
|
||||
|
||||
private void Bcc() // Branch on condition
|
||||
{
|
||||
sbyte displacement8 = (sbyte) op;
|
||||
int cond = (op >> 8) & 0x0F;
|
||||
|
||||
if (TestCondition(cond) == true)
|
||||
{
|
||||
if (displacement8 != 0)
|
||||
{
|
||||
// use opcode-embedded displacement
|
||||
PC += displacement8;
|
||||
PendingCycles -= 10;
|
||||
} else {
|
||||
// use extension word displacement
|
||||
PC += ReadWord(PC);
|
||||
PendingCycles -= 10;
|
||||
}
|
||||
} else { // false
|
||||
if (displacement8 != 0)
|
||||
PendingCycles -= 8;
|
||||
else {
|
||||
PC += 2;
|
||||
PendingCycles -= 12;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Bcc_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
sbyte displacement8 = (sbyte)op;
|
||||
int cond = (op >> 8) & 0x0F;
|
||||
|
||||
info.Mnemonic = "b" + DisassembleCondition(cond);
|
||||
if (displacement8 != 0)
|
||||
{
|
||||
info.Args = string.Format("${0:X}", pc + displacement8);
|
||||
} else {
|
||||
info.Args = string.Format("${0:X}", pc + ReadWord(pc));
|
||||
pc += 2;
|
||||
}
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void BRA()
|
||||
{
|
||||
sbyte displacement8 = (sbyte)op;
|
||||
|
||||
if (displacement8 != 0)
|
||||
PC += displacement8;
|
||||
else
|
||||
PC += ReadWord(PC);
|
||||
PendingCycles -= 10;
|
||||
}
|
||||
|
||||
private void BRA_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
info.Mnemonic = "bra";
|
||||
|
||||
sbyte displacement8 = (sbyte)op;
|
||||
if (displacement8 != 0)
|
||||
info.Args = String.Format("${0:X}", pc + displacement8);
|
||||
else
|
||||
{
|
||||
info.Args = String.Format("${0:X}", pc + ReadWord(pc));
|
||||
pc += 2;
|
||||
}
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void BSR()
|
||||
{
|
||||
sbyte displacement8 = (sbyte)op;
|
||||
|
||||
A[7].s32 -= 4;
|
||||
if (displacement8 != 0)
|
||||
{
|
||||
// use embedded displacement
|
||||
WriteLong(A[7].s32, PC);
|
||||
PC += displacement8;
|
||||
} else {
|
||||
// use extension word displacement
|
||||
WriteLong(A[7].s32, PC + 2);
|
||||
PC += ReadWord(PC);
|
||||
}
|
||||
PendingCycles -= 18;
|
||||
}
|
||||
|
||||
private void BSR_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
info.Mnemonic = "bsr";
|
||||
|
||||
sbyte displacement8 = (sbyte)op;
|
||||
if (displacement8 != 0)
|
||||
info.Args = String.Format("${0:X}", pc + displacement8);
|
||||
else {
|
||||
info.Args = String.Format("${0:X}", pc + ReadWord(pc));
|
||||
pc += 2;
|
||||
}
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void DBcc()
|
||||
{
|
||||
if (TestCondition((op >> 8) & 0x0F) == true)
|
||||
{
|
||||
// break out of loop
|
||||
PC += 2;
|
||||
PendingCycles -= 12;
|
||||
} else {
|
||||
int reg = op & 7;
|
||||
D[reg].u16--;
|
||||
|
||||
if (D[reg].u16 == 0xFFFF)
|
||||
{
|
||||
PC += 2;
|
||||
PendingCycles -= 14;
|
||||
} else {
|
||||
PC += ReadWord(PC);
|
||||
TotalExecutedCycles -= 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DBcc_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int cond = (op >> 8) & 0x0F;
|
||||
if (cond == 1)
|
||||
info.Mnemonic = "dbra";
|
||||
else
|
||||
info.Mnemonic = "db" + DisassembleCondition(cond);
|
||||
|
||||
int pc = info.PC + 2;
|
||||
info.Args = String.Format("D{0}, ${1:X}", op & 7, pc + ReadWord(pc));
|
||||
info.Length = 4;
|
||||
}
|
||||
|
||||
private void RTS()
|
||||
{
|
||||
PC = ReadLong(A[7].s32);
|
||||
A[7].s32 += 4;
|
||||
PendingCycles -= 16;
|
||||
}
|
||||
|
||||
private void RTS_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
info.Mnemonic = "rts";
|
||||
info.Args = "";
|
||||
}
|
||||
|
||||
private void TST()
|
||||
{
|
||||
int size = (op >> 6) & 3;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
|
||||
int value;
|
||||
switch (size)
|
||||
{
|
||||
case 0: value = ReadValueB(mode, reg); PendingCycles -= 4 + EACyclesBW[mode, reg]; break;
|
||||
case 1: value = ReadValueW(mode, reg); PendingCycles -= 4 + EACyclesBW[mode, reg]; break;
|
||||
default: value = ReadValueL(mode, reg); PendingCycles -= 4 + EACyclesL[mode, reg]; break;
|
||||
}
|
||||
V = false;
|
||||
C = false;
|
||||
N = (value < 0);
|
||||
Z = (value == 0);
|
||||
}
|
||||
|
||||
private void TST_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
int size = (op >> 6) & 3;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: info.Mnemonic = "tst.b"; info.Args = DisassembleValue(mode, reg, 1, ref pc); break;
|
||||
case 1: info.Mnemonic = "tst.w"; info.Args = DisassembleValue(mode, reg, 2, ref pc); break;
|
||||
case 2: info.Mnemonic = "tst.l"; info.Args = DisassembleValue(mode, reg, 4, ref pc); break;
|
||||
}
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void BTSTi()
|
||||
{
|
||||
int bit = ReadWord(PC);
|
||||
PC += 2;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = op & 7;
|
||||
|
||||
if (mode == 0)
|
||||
{
|
||||
bit &= 31;
|
||||
int mask = 1 << bit;
|
||||
Z = (D[reg].s32 & mask) == 0;
|
||||
PendingCycles -= 10;
|
||||
} else {
|
||||
bit &= 7;
|
||||
int mask = 1 << bit;
|
||||
Z = (ReadValueB(mode, reg) & mask) == 0;
|
||||
PendingCycles -= 8 + EACyclesBW[mode, reg];
|
||||
}
|
||||
}
|
||||
|
||||
private void BTSTi_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
int bit = ReadWord(pc); pc += 2;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = op & 7;
|
||||
|
||||
info.Mnemonic = "btst";
|
||||
info.Args = String.Format("${0:X}, {1}", bit, DisassembleValue(mode, reg, 1, ref pc));
|
||||
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void BTSTr()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private void BTSTr_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private void JMP()
|
||||
{
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
PC = ReadAddress(mode, reg);
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case 2: PendingCycles -= 8; break;
|
||||
case 5: PendingCycles -= 10; break;
|
||||
case 6: PendingCycles -= 14; break;
|
||||
case 7:
|
||||
switch (reg)
|
||||
{
|
||||
case 0: PendingCycles -= 10; break;
|
||||
case 1: PendingCycles -= 12; break;
|
||||
case 2: PendingCycles -= 10; break;
|
||||
case 3: PendingCycles -= 14; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void JMP_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
info.Mnemonic = "jmp";
|
||||
info.Args = DisassembleValue(mode, reg, 1, ref pc);
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void JSR()
|
||||
{
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
int addr = ReadAddress(mode, reg);
|
||||
|
||||
A[7].s32 -= 4;
|
||||
WriteLong(A[7].s32, PC);
|
||||
PC = addr;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case 2: PendingCycles -= 16; break;
|
||||
case 5: PendingCycles -= 18; break;
|
||||
case 6: PendingCycles -= 22; break;
|
||||
case 7:
|
||||
switch (reg)
|
||||
{
|
||||
case 0: PendingCycles -= 18; break;
|
||||
case 1: PendingCycles -= 20; break;
|
||||
case 2: PendingCycles -= 18; break;
|
||||
case 3: PendingCycles -= 22; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void JSR_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
info.Mnemonic = "jsr";
|
||||
info.Args = DisassembleAddress(mode, reg, ref pc);
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void LINK()
|
||||
{
|
||||
int reg = op & 7;
|
||||
A[7].s32 -= 4;
|
||||
short offset = ReadWord(PC); PC += 2;
|
||||
WriteLong(A[7].s32, A[reg].s32);
|
||||
A[reg].s32 = A[7].s32 + offset;
|
||||
PendingCycles -= 16;
|
||||
}
|
||||
|
||||
private void LINK_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
int reg = op & 7;
|
||||
info.Mnemonic = "link";
|
||||
info.Args = "A"+reg+", "+DisassembleImmediate(2, ref pc); // TODO need a DisassembleSigned or something
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void NOP()
|
||||
{
|
||||
PendingCycles -= 4;
|
||||
}
|
||||
|
||||
private void NOP_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
info.Mnemonic = "nop";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
using System;
|
||||
|
||||
namespace BizHawk.Emulation.CPUs.M68K
|
||||
{
|
||||
public partial class M68000
|
||||
{
|
||||
private void MOVEtSR()
|
||||
{
|
||||
if (S == false)
|
||||
throw new Exception("Write to SR when not in supervisor mode. supposed to trap or something...");
|
||||
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
SR = ReadValueW(mode, reg);
|
||||
PendingCycles -= (mode == 0) ? 12 : 12 + EACyclesBW[mode, reg];
|
||||
}
|
||||
|
||||
private void MOVEtSR_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
info.Mnemonic = "move";
|
||||
info.Args = DisassembleValue(mode, reg, 2, ref pc) + ", SR";
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void MOVEfSR()
|
||||
{
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
WriteValueW(mode, reg, (short) SR);
|
||||
PendingCycles -= (mode == 0) ? 6 : 8 + EACyclesBW[mode, reg];
|
||||
}
|
||||
|
||||
private void MOVEfSR_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
int mode = (op >> 3) & 7;
|
||||
int reg = (op >> 0) & 7;
|
||||
info.Mnemonic = "move";
|
||||
info.Args = "SR, " + DisassembleValue(mode, reg, 2, ref pc);
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void MOVEUSP()
|
||||
{
|
||||
if (S == false)
|
||||
throw new Exception("MOVE to USP when not supervisor. needs to trap");
|
||||
|
||||
int dir = (op >> 3) & 1;
|
||||
int reg = op & 7;
|
||||
|
||||
if (dir == 0) usp = A[reg].s32;
|
||||
else A[reg].s32 = usp;
|
||||
|
||||
PendingCycles -= 4;
|
||||
}
|
||||
|
||||
private void MOVEUSP_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
int dir = (op >> 3) & 1;
|
||||
int reg = op & 7;
|
||||
info.Mnemonic = "move";
|
||||
info.Args = (dir == 0) ? ("A" + reg + ", USP") : ("USP, A" + reg);
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
|
||||
private void ORI_SR()
|
||||
{
|
||||
if (S == false)
|
||||
throw new Exception("trap!");
|
||||
SR |= ReadWord(PC); PC += 2;
|
||||
PendingCycles -= 20;
|
||||
}
|
||||
|
||||
private void ORI_SR_Disasm(DisassemblyInfo info)
|
||||
{
|
||||
int pc = info.PC + 2;
|
||||
info.Mnemonic = "ori";
|
||||
info.Args = DisassembleImmediate(2, ref pc) + ", SR";
|
||||
info.Length = pc - info.PC;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace BizHawk.Emulation.CPUs.M68K
|
||||
{
|
||||
public sealed partial class M68000
|
||||
{
|
||||
// Machine State
|
||||
public Register[] D = new Register[8];
|
||||
public Register[] A = new Register[8];
|
||||
public int PC;
|
||||
|
||||
public int TotalExecutedCycles;
|
||||
public int PendingCycles;
|
||||
|
||||
// Status Registers
|
||||
private int InterruptMaskLevel;
|
||||
|
||||
private bool s, m;
|
||||
private int usp, ssp;
|
||||
|
||||
/// <summary>Machine/Interrupt mode</summary>
|
||||
public bool M { get { return m; } set { m = value; } } // TODO probably have some switch logic maybe
|
||||
|
||||
/// <summary>Supervisor/User mode</summary>
|
||||
public bool S
|
||||
{
|
||||
get { return s; }
|
||||
set
|
||||
{
|
||||
if (value == s) return;
|
||||
if (value == true) // entering supervisor mode
|
||||
{
|
||||
Console.WriteLine("&^&^&^&^& ENTER SUPERVISOR MODE");
|
||||
usp = A[7].s32;
|
||||
A[7].s32 = ssp;
|
||||
s = true;
|
||||
} else { // exiting supervisor mode
|
||||
Console.WriteLine("&^&^&^&^& LEAVE SUPERVISOR MODE");
|
||||
ssp = A[7].s32;
|
||||
A[7].s32 = usp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Extend Flag</summary>
|
||||
public bool X;
|
||||
/// <summary>Negative Flag</summary>
|
||||
public bool N;
|
||||
/// <summary>Zero Flag</summary>
|
||||
public bool Z;
|
||||
/// <summary>Overflow Flag</summary>
|
||||
public bool V;
|
||||
/// <summary>Carry Flag</summary>
|
||||
public bool C;
|
||||
|
||||
/// <summary>Status Register</summary>
|
||||
public int SR
|
||||
{
|
||||
get
|
||||
{
|
||||
int value = 0;
|
||||
if (C) value |= 0x0001;
|
||||
if (V) value |= 0x0002;
|
||||
if (Z) value |= 0x0004;
|
||||
if (N) value |= 0x0008;
|
||||
if (X) value |= 0x0010;
|
||||
if (M) value |= 0x1000;
|
||||
if (S) value |= 0x2000;
|
||||
value |= (InterruptMaskLevel & 7) << 8;
|
||||
return value;
|
||||
}
|
||||
set
|
||||
{
|
||||
C = (value & 0x0001) != 0;
|
||||
V = (value & 0x0002) != 0;
|
||||
Z = (value & 0x0004) != 0;
|
||||
N = (value & 0x0008) != 0;
|
||||
X = (value & 0x0010) != 0;
|
||||
M = (value & 0x1000) != 0;
|
||||
S = (value & 0x2000) != 0;
|
||||
InterruptMaskLevel = (value >> 8) & 7;
|
||||
}
|
||||
}
|
||||
|
||||
// Memory Access
|
||||
public Func<int, sbyte> ReadByte;
|
||||
public Func<int, short> ReadWord;
|
||||
public Func<int, int> ReadLong;
|
||||
|
||||
public Action<int, sbyte> WriteByte;
|
||||
public Action<int, short> WriteWord;
|
||||
public Action<int, int> WriteLong;
|
||||
|
||||
// Initialization
|
||||
|
||||
public M68000()
|
||||
{
|
||||
BuildOpcodeTable();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
s = true;
|
||||
A[7].s32 = ReadLong(0);
|
||||
PC = ReadLong(4);
|
||||
}
|
||||
|
||||
public Action[] Opcodes = new Action[0x10000];
|
||||
public ushort op;
|
||||
|
||||
public void Exec()
|
||||
{
|
||||
Console.WriteLine(Disassemble(PC));
|
||||
|
||||
op = (ushort) ReadWord(PC);
|
||||
PC += 2;
|
||||
Opcodes[op]();
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct Register
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public uint u32;
|
||||
[FieldOffset(0)]
|
||||
public int s32;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public ushort u16;
|
||||
[FieldOffset(0)]
|
||||
public short s16;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public byte u8;
|
||||
[FieldOffset(0)]
|
||||
public sbyte s8;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("{0:X8}", u32);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,626 @@
|
|||
using System;
|
||||
|
||||
namespace BizHawk.Emulation.CPUs.M68K
|
||||
{
|
||||
public partial class M68000
|
||||
{
|
||||
private sbyte ReadValueB(int mode, int reg)
|
||||
{
|
||||
sbyte value;
|
||||
switch (mode)
|
||||
{
|
||||
case 0: // Dn
|
||||
return D[reg].s8;
|
||||
case 1: // An
|
||||
return A[reg].s8;
|
||||
case 2: // (An)
|
||||
return ReadByte(A[reg].s32);
|
||||
case 3: // (An)+
|
||||
value = ReadByte(A[reg].s32);
|
||||
A[reg].s32 += reg == 7 ? 2 : 1;
|
||||
return value;
|
||||
case 4: // -(An)
|
||||
A[reg].s32 -= reg == 7 ? 2 : 1;
|
||||
return ReadByte(A[reg].s32);
|
||||
case 5: // (d16,An)
|
||||
value = ReadByte((A[reg].s32 + ReadWord(PC))); PC += 2;
|
||||
return value;
|
||||
case 6: // (d8,An,Xn)
|
||||
return ReadByte(A[reg].s32 + GetIndex());
|
||||
case 7:
|
||||
switch (reg)
|
||||
{
|
||||
case 0: // (imm).W
|
||||
value = ReadByte(ReadWord(PC)); PC += 2;
|
||||
return value;
|
||||
case 1: // (imm).L
|
||||
value = ReadByte(ReadLong(PC)); PC += 4;
|
||||
return value;
|
||||
case 2: // (d16,PC)
|
||||
value = ReadByte(PC + ReadWord(PC)); PC += 2;
|
||||
return value;
|
||||
case 3: // (d8,PC,Xn)
|
||||
int pc = PC;
|
||||
value = ReadByte((pc + GetIndex()));
|
||||
return value;
|
||||
case 4: // immediate
|
||||
value = (sbyte) ReadWord(PC); PC += 2;
|
||||
return value;
|
||||
default:
|
||||
throw new Exception("Invalid addressing mode!");
|
||||
}
|
||||
}
|
||||
throw new Exception("Invalid addressing mode!");
|
||||
}
|
||||
|
||||
private short ReadValueW(int mode, int reg)
|
||||
{
|
||||
short value;
|
||||
switch (mode)
|
||||
{
|
||||
case 0: // Dn
|
||||
return D[reg].s16;
|
||||
case 1: // An
|
||||
return A[reg].s16;
|
||||
case 2: // (An)
|
||||
return ReadWord(A[reg].s32);
|
||||
case 3: // (An)+
|
||||
value = ReadWord(A[reg].s32);
|
||||
A[reg].s32 += 2;
|
||||
return value;
|
||||
case 4: // -(An)
|
||||
A[reg].s32 -= 2;
|
||||
return ReadWord(A[reg].s32);
|
||||
case 5: // (d16,An)
|
||||
value = ReadWord((A[reg].s32 + ReadWord(PC))); PC += 2;
|
||||
return value;
|
||||
case 6: // (d8,An,Xn)
|
||||
return ReadWord(A[reg].s32 + GetIndex());
|
||||
case 7:
|
||||
switch (reg)
|
||||
{
|
||||
case 0: // (imm).W
|
||||
value = ReadWord(ReadWord(PC)); PC += 2;
|
||||
return value;
|
||||
case 1: // (imm).L
|
||||
value = ReadWord(ReadLong(PC)); PC += 4;
|
||||
return value;
|
||||
case 2: // (d16,PC)
|
||||
value = ReadWord(PC + ReadWord(PC)); PC += 2;
|
||||
return value;
|
||||
case 3: // (d8,PC,Xn)
|
||||
int pc = PC;
|
||||
value = ReadWord((pc + GetIndex()));
|
||||
return value;
|
||||
case 4: // immediate
|
||||
value = ReadWord(PC); PC += 2;
|
||||
return value;
|
||||
default:
|
||||
throw new Exception("Invalid addressing mode!");
|
||||
}
|
||||
}
|
||||
throw new Exception("Invalid addressing mode!");
|
||||
}
|
||||
|
||||
private int ReadValueL(int mode, int reg)
|
||||
{
|
||||
int value;
|
||||
switch (mode)
|
||||
{
|
||||
case 0: // Dn
|
||||
return D[reg].s32;
|
||||
case 1: // An
|
||||
return A[reg].s32;
|
||||
case 2: // (An)
|
||||
return ReadLong(A[reg].s32);
|
||||
case 3: // (An)+
|
||||
value = ReadLong(A[reg].s32);
|
||||
A[reg].s32 += 4;
|
||||
return value;
|
||||
case 4: // -(An)
|
||||
A[reg].s32 -= 4;
|
||||
return ReadLong(A[reg].s32);
|
||||
case 5: // (d16,An)
|
||||
value = ReadLong((A[reg].s32 + ReadWord(PC))); PC += 2;
|
||||
return value;
|
||||
case 6: // (d8,An,Xn)
|
||||
return ReadLong(A[reg].s32 + GetIndex());
|
||||
case 7:
|
||||
switch (reg)
|
||||
{
|
||||
case 0: // (imm).W
|
||||
value = ReadLong(ReadWord(PC)); PC += 2;
|
||||
return value;
|
||||
case 1: // (imm).L
|
||||
value = ReadLong(ReadLong(PC)); PC += 4;
|
||||
return value;
|
||||
case 2: // (d16,PC)
|
||||
value = ReadLong(PC + ReadWord(PC)); PC += 2;
|
||||
return value;
|
||||
case 3: // (d8,PC,Xn)
|
||||
int pc = PC;
|
||||
value = ReadLong((pc + GetIndex()));
|
||||
return value;
|
||||
case 4: // immediate
|
||||
value = ReadLong(PC); PC += 4;
|
||||
return value;
|
||||
default:
|
||||
throw new Exception("Invalid addressing mode!");
|
||||
}
|
||||
}
|
||||
throw new Exception("Invalid addressing mode!");
|
||||
}
|
||||
|
||||
private sbyte PeekValueB(int mode, int reg)
|
||||
{
|
||||
sbyte value;
|
||||
switch (mode)
|
||||
{
|
||||
case 0: // Dn
|
||||
return D[reg].s8;
|
||||
case 1: // An
|
||||
return A[reg].s8;
|
||||
case 2: // (An)
|
||||
return ReadByte(A[reg].s32);
|
||||
case 3: // (An)+
|
||||
value = ReadByte(A[reg].s32);
|
||||
A[reg].s32 += reg == 7 ? 2 : 1;
|
||||
return value;
|
||||
case 4: // -(An)
|
||||
A[reg].s32 -= reg == 7 ? 2 : 1;
|
||||
return ReadByte(A[reg].s32);
|
||||
case 5: // (d16,An)
|
||||
value = ReadByte((A[reg].s32 + ReadWord(PC)));
|
||||
return value;
|
||||
case 6: // (d8,An,Xn)
|
||||
return ReadByte(A[reg].s32 + GetIndex());
|
||||
case 7:
|
||||
switch (reg)
|
||||
{
|
||||
case 0: // (imm).W
|
||||
value = ReadByte(ReadWord(PC));
|
||||
return value;
|
||||
case 1: // (imm).L
|
||||
value = ReadByte(ReadLong(PC));
|
||||
return value;
|
||||
case 2: // (d16,PC)
|
||||
value = ReadByte(PC + ReadWord(PC));
|
||||
return value;
|
||||
case 3: // (d8,PC,Xn)
|
||||
value = ReadByte((PC + PeekIndex()));
|
||||
return value;
|
||||
case 4: // immediate
|
||||
return (sbyte) ReadWord(PC);
|
||||
default:
|
||||
throw new Exception("Invalid addressing mode!");
|
||||
}
|
||||
}
|
||||
throw new Exception("Invalid addressing mode!");
|
||||
}
|
||||
|
||||
private short PeekValueW(int mode, int reg)
|
||||
{
|
||||
short value;
|
||||
switch (mode)
|
||||
{
|
||||
case 0: // Dn
|
||||
return D[reg].s16;
|
||||
case 1: // An
|
||||
return A[reg].s16;
|
||||
case 2: // (An)
|
||||
return ReadWord(A[reg].s32);
|
||||
case 3: // (An)+
|
||||
value = ReadWord(A[reg].s32);
|
||||
A[reg].s32 += 2;
|
||||
return value;
|
||||
case 4: // -(An)
|
||||
A[reg].s32 -= 2;
|
||||
return ReadWord(A[reg].s32);
|
||||
case 5: // (d16,An)
|
||||
value = ReadWord((A[reg].s32 + ReadWord(PC)));
|
||||
return value;
|
||||
case 6: // (d8,An,Xn)
|
||||
return ReadWord(A[reg].s32 + PeekIndex());
|
||||
case 7:
|
||||
switch (reg)
|
||||
{
|
||||
case 0: // (imm).W
|
||||
value = ReadWord(ReadWord(PC));
|
||||
return value;
|
||||
case 1: // (imm).L
|
||||
value = ReadWord(ReadLong(PC));
|
||||
return value;
|
||||
case 2: // (d16,PC)
|
||||
value = ReadWord(PC + ReadWord(PC));
|
||||
return value;
|
||||
case 3: // (d8,PC,Xn)
|
||||
value = ReadWord((PC + PeekIndex()));
|
||||
return value;
|
||||
case 4: // immediate
|
||||
return ReadWord(PC);
|
||||
default:
|
||||
throw new Exception("Invalid addressing mode!");
|
||||
}
|
||||
}
|
||||
throw new Exception("Invalid addressing mode!");
|
||||
}
|
||||
|
||||
private int PeekValueL(int mode, int reg)
|
||||
{
|
||||
int value;
|
||||
switch (mode)
|
||||
{
|
||||
case 0: // Dn
|
||||
return D[reg].s32;
|
||||
case 1: // An
|
||||
return A[reg].s32;
|
||||
case 2: // (An)
|
||||
return ReadLong(A[reg].s32);
|
||||
case 3: // (An)+
|
||||
value = ReadLong(A[reg].s32);
|
||||
A[reg].s32 += 4;
|
||||
return value;
|
||||
case 4: // -(An)
|
||||
A[reg].s32 -= 4;
|
||||
return ReadLong(A[reg].s32);
|
||||
case 5: // (d16,An)
|
||||
value = ReadLong((A[reg].s32 + ReadWord(PC)));
|
||||
return value;
|
||||
case 6: // (d8,An,Xn)
|
||||
return ReadLong(A[reg].s32 + PeekIndex());
|
||||
case 7:
|
||||
switch (reg)
|
||||
{
|
||||
case 0: // (imm).W
|
||||
value = ReadLong(ReadWord(PC));
|
||||
return value;
|
||||
case 1: // (imm).L
|
||||
value = ReadLong(ReadLong(PC));
|
||||
return value;
|
||||
case 2: // (d16,PC)
|
||||
value = ReadLong(PC + ReadWord(PC));
|
||||
return value;
|
||||
case 3: // (d8,PC,Xn)
|
||||
value = ReadLong((PC + PeekIndex()));
|
||||
return value;
|
||||
case 4: // immediate
|
||||
return ReadLong(PC);
|
||||
default:
|
||||
throw new Exception("Invalid addressing mode!");
|
||||
}
|
||||
}
|
||||
throw new Exception("Invalid addressing mode!");
|
||||
}
|
||||
|
||||
private int ReadAddress(int mode, int reg)
|
||||
{
|
||||
int addr;
|
||||
switch (mode)
|
||||
{
|
||||
case 0: throw new Exception("Invalid addressing mode!"); // Dn
|
||||
case 1: throw new Exception("Invalid addressing mode!"); // An
|
||||
case 2: return A[reg].s32; // (An)
|
||||
case 3: return A[reg].s32; // (An)+
|
||||
case 4: return A[reg].s32; // -(An)
|
||||
case 5: // (d16,An)
|
||||
addr = A[reg].s32 + ReadWord(PC);
|
||||
PC += 2;
|
||||
return addr;
|
||||
case 6: return A[reg].s32 + GetIndex(); // (d8,An,Xn)
|
||||
case 7:
|
||||
switch (reg)
|
||||
{
|
||||
case 0: addr = ReadWord(PC); PC += 2; return addr; // (imm).w
|
||||
case 1: addr = ReadLong(PC); PC += 4; return addr; // (imm).l
|
||||
case 2: addr = PC; addr += ReadWord(PC); PC += 2; return addr; // (d16,PC)
|
||||
case 3: addr = PC; addr += GetIndex(); return addr; // (d8,PC,Xn)
|
||||
case 4: throw new Exception("Invalid addressing mode!"); // immediate
|
||||
}
|
||||
break;
|
||||
}
|
||||
throw new Exception("Invalid addressing mode!");
|
||||
}
|
||||
|
||||
private string DisassembleValue(int mode, int reg, int size, ref int pc)
|
||||
{
|
||||
string value;
|
||||
switch (mode)
|
||||
{
|
||||
case 0: return "D"+reg; // Dn
|
||||
case 1: return "A"+reg; // An
|
||||
case 2: return "(A"+reg+")"; // (An)
|
||||
case 3: return "(A"+reg+")+"; // (An)+
|
||||
case 4: return "-(A"+reg+")"; // -(An)
|
||||
case 5: // (d16,An)
|
||||
// TODO need to figure out how to print signed-hex
|
||||
value = string.Format("(${0:X},A{1})", ReadWord(pc), reg);
|
||||
pc += 2;
|
||||
return value;
|
||||
case 6: return "NOT IMPLEMENTED"; // (d8,An,Xn)
|
||||
//return ReadByte(A[reg].Long + GetIndex());
|
||||
case 7:
|
||||
switch (reg)
|
||||
{
|
||||
case 0: // (imm).W
|
||||
value = String.Format("(${0:X})", ReadWord(pc));
|
||||
pc += 2;
|
||||
return value;
|
||||
case 1: // (imm).L
|
||||
value = String.Format("(${0:X})", ReadLong(pc));
|
||||
pc += 4;
|
||||
return value;
|
||||
case 2: // (d16,PC)
|
||||
value = String.Format("(${0:X})", pc + ReadWord(pc));
|
||||
pc += 2;
|
||||
return value;
|
||||
case 3: // (d8,PC,Xn)
|
||||
return "NOT IMPLEMENTED";
|
||||
/* uint _pc = PC;
|
||||
value = ReadByte((_pc + GetIndex()));
|
||||
return value;*/
|
||||
case 4:
|
||||
switch (size)
|
||||
{
|
||||
case 1: value = String.Format("${0:X}", (byte)ReadWord(pc)); pc += 2; return value;
|
||||
case 2: value = String.Format("${0:X}", ReadWord(pc)); pc += 2; return value;
|
||||
case 4: value = String.Format("${0:X}", ReadLong(pc)); pc += 4; return value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
throw new Exception("Invalid addressing mode!");
|
||||
}
|
||||
|
||||
private string DisassembleImmediate(int size, ref int pc)
|
||||
{
|
||||
int immed;
|
||||
switch (size)
|
||||
{
|
||||
case 1:
|
||||
immed = (byte)ReadWord(pc); pc += 2;
|
||||
return String.Format("${0:X}", immed);
|
||||
case 2:
|
||||
immed = (ushort) ReadWord(pc); pc += 2;
|
||||
return String.Format("${0:X}", immed);
|
||||
case 4:
|
||||
immed = ReadLong(pc); pc += 4;
|
||||
return String.Format("${0:X}", immed);
|
||||
}
|
||||
throw new ArgumentException("Invalid size");
|
||||
}
|
||||
|
||||
private string DisassembleAddress(int mode, int reg, ref int pc)
|
||||
{
|
||||
int addr;
|
||||
switch (mode)
|
||||
{
|
||||
case 0: return "INVALID"; // Dn
|
||||
case 1: return "INVALID"; // An
|
||||
case 2: return "(A"+reg+")"; // (An)
|
||||
case 3: return "(A"+reg+")+"; // (An)+
|
||||
case 4: return "-(A"+reg+")"; // -(An)
|
||||
case 5: // (d16,An)
|
||||
addr = ReadWord(pc);
|
||||
pc += 2;
|
||||
return String.Format("({0},A{1})", addr, reg);
|
||||
case 6: return "NOT IMPLEMENTED"; // (d8,An,Xn)
|
||||
case 7:
|
||||
switch (reg)
|
||||
{
|
||||
case 0: addr = ReadWord(pc); pc += 2; return String.Format("${0:X}.w",addr); // (imm).w
|
||||
case 1: addr = ReadLong(pc); pc += 4; return String.Format("${0:X}.l",addr); // (imm).l
|
||||
case 2: addr = ReadWord(pc); pc += 2; return String.Format("(${0:X},PC)",addr); // (d16,PC)
|
||||
case 3: return "NOT IMPLEMENTED"; // (d8,PC,Xn)
|
||||
case 4: return "INVALID"; // immediate
|
||||
}
|
||||
break;
|
||||
}
|
||||
throw new Exception("Invalid addressing mode!");
|
||||
}
|
||||
|
||||
private void WriteValueB(int mode, int reg, sbyte value)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case 0x00: // Dn
|
||||
D[reg].s8 = value;
|
||||
return;
|
||||
case 0x01: // An
|
||||
A[reg].s32 = value;
|
||||
return;
|
||||
case 0x02: // (An)
|
||||
WriteByte(A[reg].s32, value);
|
||||
return;
|
||||
case 0x03: // (An)+
|
||||
WriteByte(A[reg].s32, value);
|
||||
A[reg].s32 += reg == 7 ? 2 : 1;
|
||||
return;
|
||||
case 0x04: // -(An)
|
||||
A[reg].s32 -= reg == 7 ? 2 : 1;
|
||||
WriteByte(A[reg].s32, value);
|
||||
return;
|
||||
case 0x05: // (d16,An)
|
||||
WriteByte(A[reg].s32 + ReadWord(PC), value); PC += 2;
|
||||
return;
|
||||
case 0x06: // (d8,An,Xn)
|
||||
WriteByte(A[reg].s32 + GetIndex(), value);
|
||||
return;
|
||||
case 0x07:
|
||||
switch (reg)
|
||||
{
|
||||
case 0x00: // (imm).W
|
||||
WriteByte(ReadWord(PC), value); PC += 2;
|
||||
return;
|
||||
case 0x01: // (imm).L
|
||||
WriteByte(ReadLong(PC), value); PC += 4;
|
||||
return;
|
||||
case 0x02: // (d16,PC)
|
||||
WriteByte(PC + ReadWord(PC), value); PC += 2;
|
||||
return;
|
||||
case 0x03: // (d8,PC,Xn)
|
||||
int pc = PC;
|
||||
WriteByte(pc + PeekIndex(), value);
|
||||
PC += 2;
|
||||
return;
|
||||
default: throw new Exception("Invalid addressing mode!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteValueW(int mode, int reg, short value)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case 0x00: // Dn
|
||||
D[reg].s16 = value;
|
||||
return;
|
||||
case 0x01: // An
|
||||
A[reg].s32 = value;
|
||||
return;
|
||||
case 0x02: // (An)
|
||||
WriteWord(A[reg].s32, value);
|
||||
return;
|
||||
case 0x03: // (An)+
|
||||
WriteWord(A[reg].s32, value);
|
||||
A[reg].s32 += 2;
|
||||
return;
|
||||
case 0x04: // -(An)
|
||||
A[reg].s32 -= 2;
|
||||
WriteWord(A[reg].s32, value);
|
||||
return;
|
||||
case 0x05: // (d16,An)
|
||||
WriteWord(A[reg].s32 + ReadWord(PC), value); PC += 2;
|
||||
return;
|
||||
case 0x06: // (d8,An,Xn)
|
||||
WriteWord(A[reg].s32 + GetIndex(), value);
|
||||
return;
|
||||
case 0x07:
|
||||
switch (reg)
|
||||
{
|
||||
case 0x00: // (imm).W
|
||||
WriteWord(ReadWord(PC), value); PC += 2;
|
||||
return;
|
||||
case 0x01: // (imm).L
|
||||
WriteWord(ReadLong(PC), value); PC += 4;
|
||||
return;
|
||||
case 0x02: // (d16,PC)
|
||||
WriteWord(PC + ReadWord(PC), value); PC += 2;
|
||||
return;
|
||||
case 0x03: // (d8,PC,Xn)
|
||||
int pc = PC;
|
||||
WriteWord(pc + PeekIndex(), value);
|
||||
PC += 2;
|
||||
return;
|
||||
default: throw new Exception("Invalid addressing mode!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteValueL(int mode, int reg, int value)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case 0x00: // Dn
|
||||
D[reg].s32 = value;
|
||||
return;
|
||||
case 0x01: // An
|
||||
A[reg].s32 = value;
|
||||
return;
|
||||
case 0x02: // (An)
|
||||
WriteLong(A[reg].s32, value);
|
||||
return;
|
||||
case 0x03: // (An)+
|
||||
WriteLong(A[reg].s32, value);
|
||||
A[reg].s32 += 4;
|
||||
return;
|
||||
case 0x04: // -(An)
|
||||
A[reg].s32 -= 4;
|
||||
WriteLong(A[reg].s32, value);
|
||||
return;
|
||||
case 0x05: // (d16,An)
|
||||
WriteLong(A[reg].s32 + ReadWord(PC), value); PC += 2;
|
||||
return;
|
||||
case 0x06: // (d8,An,Xn)
|
||||
WriteLong(A[reg].s32 + GetIndex(), value);
|
||||
return;
|
||||
case 0x07:
|
||||
switch (reg)
|
||||
{
|
||||
case 0x00: // (imm).W
|
||||
WriteLong(ReadWord(PC), value); PC += 2;
|
||||
return;
|
||||
case 0x01: // (imm).L
|
||||
WriteLong(ReadLong(PC), value); PC += 4;
|
||||
return;
|
||||
case 0x02: // (d16,PC)
|
||||
WriteLong(PC + ReadWord(PC), value); PC += 2;
|
||||
return;
|
||||
case 0x03: // (d8,PC,Xn)
|
||||
int pc = PC;
|
||||
WriteLong(pc + PeekIndex(), value);
|
||||
PC += 2;
|
||||
return;
|
||||
default: throw new Exception("Invalid addressing mode!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int GetIndex()
|
||||
{
|
||||
Console.WriteLine("IN INDEX PORTION - NOT VERIFIED!!!");
|
||||
// TODO kid chameleon triggers this in startup sequence
|
||||
|
||||
short extension = ReadWord(PC); PC += 2;
|
||||
|
||||
int da = (extension >> 15) & 0x1;
|
||||
int reg = (extension >> 12) & 0x7;
|
||||
int size = (extension >> 11) & 0x1;
|
||||
int scale = (extension >> 9) & 0x3;
|
||||
sbyte displacement = (sbyte)extension;
|
||||
|
||||
int indexReg;
|
||||
switch (scale)
|
||||
{
|
||||
case 0: indexReg = 1; break;
|
||||
case 1: indexReg = 2; break;
|
||||
case 2: indexReg = 4; break;
|
||||
default: indexReg = 8; break;
|
||||
}
|
||||
if (da == 0)
|
||||
indexReg *= size == 0 ? D[reg].s16 : D[reg].s32;
|
||||
else
|
||||
indexReg *= size == 0 ? A[reg].s16 : A[reg].s32;
|
||||
|
||||
return displacement + indexReg;
|
||||
}
|
||||
|
||||
private int PeekIndex()
|
||||
{
|
||||
Console.WriteLine("IN INDEX PORTION - NOT VERIFIED!!!");
|
||||
|
||||
short extension = ReadWord(PC);
|
||||
|
||||
int da = (extension >> 15) & 0x1;
|
||||
int reg = (extension >> 12) & 0x7;
|
||||
int size = (extension >> 11) & 0x1;
|
||||
int scale = (extension >> 9) & 0x3;
|
||||
sbyte displacement = (sbyte)extension;
|
||||
|
||||
int indexReg;
|
||||
switch (scale)
|
||||
{
|
||||
case 0: indexReg = 1; break;
|
||||
case 1: indexReg = 2; break;
|
||||
case 2: indexReg = 4; break;
|
||||
default: indexReg = 8; break;
|
||||
}
|
||||
if (da == 0)
|
||||
indexReg *= size == 0 ? D[reg].s16 : D[reg].s32;
|
||||
else
|
||||
indexReg *= size == 0 ? A[reg].s16 : A[reg].s32;
|
||||
|
||||
return displacement + indexReg;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,323 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BizHawk.Emulation.CPUs.M68K
|
||||
{
|
||||
public partial class M68000
|
||||
{
|
||||
private void BuildOpcodeTable()
|
||||
{
|
||||
// NOTE: Do not change the order of these assigns without testing. There is
|
||||
// some overwriting of less-specific opcodes with more-specific opcodes.
|
||||
// * MOVEA overwrites MOVE.
|
||||
// * EXT overwrites MOVEM0
|
||||
// * DBcc overwrites Scc
|
||||
// * ORI to SR overwrites ORI
|
||||
|
||||
Assign("move", MOVE, "00", "Size2_0", "XnAm", "AmXn");
|
||||
Assign("movea", MOVEA, "00", "Size2_0", "Xn", "001", "AmXn");
|
||||
Assign("moveq", MOVEQ, "0111", "Xn", "0", "Data8");
|
||||
Assign("movem", MOVEM0,"010010001", "Size1", "AmXn");
|
||||
Assign("movem", MOVEM1,"010011001", "Size1", "AmXn");
|
||||
Assign("lea", LEA, "0100", "Xn", "111", "AmXn");
|
||||
Assign("clr", CLR, "01000010", "Size2_1", "AmXn");
|
||||
Assign("ext", EXT, "010010001", "Size1", "000", "Xn");
|
||||
|
||||
Assign("andi", ANDI, "00000010", "Size2_1", "AmXn");
|
||||
Assign("ori", ORI, "00000000", "Size2_1", "AmXn");
|
||||
Assign("lsl", LSLd, "1110", "Data3", "1", "Size2_1", "Data1", "01", "Xn");
|
||||
Assign("lsr", LSRd, "1110", "Data3", "0", "Size2_1", "Data1", "01", "Xn");
|
||||
Assign("asl", ASLd, "1110", "Data3", "1", "Size2_1", "Data1", "00", "Xn");
|
||||
Assign("asr", ASRd, "1110", "Data3", "0", "Size2_1", "Data1", "00", "Xn");
|
||||
Assign("rol", ROLd, "1110", "Data3", "1", "Size2_1", "Data1", "11", "Xn");
|
||||
Assign("ror", RORd, "1110", "Data3", "0", "Size2_1", "Data1", "11", "Xn");
|
||||
Assign("swap", SWAP, "0100100001000","Xn");
|
||||
|
||||
Assign("jmp", JMP, "0100111011", "AmXn");
|
||||
Assign("jsr", JSR, "0100111010", "AmXn");
|
||||
Assign("bcc", Bcc, "0110", "CondMain", "Data8");
|
||||
Assign("bra", BRA, "01100000", "Data8");
|
||||
Assign("bsr", BSR, "01100001", "Data8");
|
||||
// NOTE: Scc must be assigned before DBcc
|
||||
Assign("dbcc", DBcc, "0101", "CondAll", "11001", "Xn");
|
||||
Assign("rts", RTS, "0100111001110101");
|
||||
Assign("tst", TST, "01001010", "Size2_1", "AmXn");
|
||||
Assign("btst", BTSTi, "0000100000", "AmXn");
|
||||
Assign("btst", BTSTr, "0000", "Xn", "100", "AmXn");
|
||||
Assign("link", LINK, "0100111001010", "Xn");
|
||||
Assign("nop", NOP, "0100111001110001");
|
||||
|
||||
Assign("add", ADD0, "1101", "Xn", "0", "Size2_1", "AmXn");
|
||||
Assign("add", ADD1, "1101", "Xn", "1", "Size2_1", "AmXn");
|
||||
Assign("adda", ADDA, "1101", "Xn", "Size1", "11", "AmXn");
|
||||
Assign("addi", ADDI, "00000110", "Size2_1", "AmXn");
|
||||
Assign("addq", ADDQ, "0101", "Data3", "0", "Size2_1", "AmXn");
|
||||
Assign("sub", SUB0, "1001", "Xn", "0", "Size2_1", "AmXn");
|
||||
Assign("sub", SUB1, "1001", "Xn", "1", "Size2_1", "AmXn");
|
||||
Assign("suba", SUBA, "1001", "Xn", "Size1", "11", "AmXn");
|
||||
Assign("subi", SUBI, "00000100", "Size2_1", "AmXn");
|
||||
Assign("subq", SUBQ, "0101", "Data3", "1", "Size2_1", "AmXn");
|
||||
Assign("cmp", CMP, "1011", "Xn", "0", "Size2_1", "AmXn");
|
||||
Assign("cmpa", CMPA, "1011", "Xn", "Size1", "11", "AmXn");
|
||||
Assign("cmpi", CMPI, "00001100", "Size2_1", "AmXn");
|
||||
|
||||
Assign("move2sr", MOVEtSR, "0100011011", "AmXn");
|
||||
Assign("movefsr", MOVEfSR, "0100000011", "AmXn");
|
||||
Assign("moveusp", MOVEUSP, "010011100110", "Data1", "Xn");
|
||||
Assign("ori2sr", ORI_SR, "0000000001111100");
|
||||
}
|
||||
|
||||
private void Assign(string instr, Action exec, string root, params string[] bitfield)
|
||||
{
|
||||
List<string> opList = new List<string>();
|
||||
opList.Add(root);
|
||||
|
||||
foreach (var component in bitfield)
|
||||
{
|
||||
if (component.IsBinary()) AppendConstant(opList, component);
|
||||
else if (component == "Size1") opList = AppendPermutations(opList, Size1);
|
||||
else if (component == "Size2_0") opList = AppendPermutations(opList, Size2_0);
|
||||
else if (component == "Size2_1") opList = AppendPermutations(opList, Size2_1);
|
||||
else if (component == "XnAm") opList = AppendPermutations(opList, Xn3Am3);
|
||||
else if (component == "AmXn") opList = AppendPermutations(opList, Am3Xn3);
|
||||
else if (component == "Xn") opList = AppendPermutations(opList, Xn3);
|
||||
else if (component == "CondMain") opList = AppendPermutations(opList, ConditionMain);
|
||||
else if (component == "CondAll") opList = AppendPermutations(opList, ConditionAll);
|
||||
else if (component == "Data1") opList = AppendData(opList, 1);
|
||||
else if (component == "Data3") opList = AppendData(opList, 3);
|
||||
else if (component == "Data8") opList = AppendData(opList, 8);
|
||||
}
|
||||
|
||||
foreach (var opcode in opList)
|
||||
{
|
||||
int opc = Convert.ToInt32(opcode, 2);
|
||||
if (Opcodes[opc] != null && instr.NotIn("movea","ori2sr","ext"))
|
||||
Console.WriteLine("Setting opcode for {0}, a handler is already set. overwriting. {1:X4}", instr, opc);
|
||||
Opcodes[opc] = exec;
|
||||
}
|
||||
}
|
||||
|
||||
private void AppendConstant(List<string> ops, string constant)
|
||||
{
|
||||
for (int i=0; i<ops.Count; i++)
|
||||
ops[i] = ops[i] + constant;
|
||||
}
|
||||
|
||||
private List<string> AppendPermutations(List<string> ops, string[] permutations)
|
||||
{
|
||||
List<string> output = new List<string>();
|
||||
|
||||
foreach (var input in ops)
|
||||
foreach (var perm in permutations)
|
||||
output.Add(input + perm);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
private List<string> AppendData(List<string> ops, int bits)
|
||||
{
|
||||
List<string> output = new List<string>();
|
||||
|
||||
foreach (var input in ops)
|
||||
for (int i = 0; i < BinaryExp(bits); i++)
|
||||
output.Add(input+Convert.ToString(i, 2).PadLeft(bits, '0'));
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
private int BinaryExp(int bits)
|
||||
{
|
||||
int res = 1;
|
||||
for (int i = 0; i < bits; i++)
|
||||
res *= 2;
|
||||
return res;
|
||||
}
|
||||
|
||||
#region Tables
|
||||
|
||||
private static readonly string[] Size2_0 = {"01", "11", "10"};
|
||||
private static readonly string[] Size2_1 = {"00", "01", "10"};
|
||||
private static readonly string[] Size1 = {"0", "1" };
|
||||
private static readonly string[] Xn3 = {"000","001","010","011","100","101","110","111"};
|
||||
|
||||
private static readonly string[] Xn3Am3 = {
|
||||
"000000", // Dn Data register
|
||||
"001000",
|
||||
"010000",
|
||||
"011000",
|
||||
"100000",
|
||||
"101000",
|
||||
"110000",
|
||||
"111000",
|
||||
|
||||
"000001", // An Address register
|
||||
"001001",
|
||||
"010001",
|
||||
"011001",
|
||||
"100001",
|
||||
"101001",
|
||||
"110001",
|
||||
"111001",
|
||||
|
||||
"000010", // (An) Address
|
||||
"001010",
|
||||
"010010",
|
||||
"011010",
|
||||
"100010",
|
||||
"101010",
|
||||
"110010",
|
||||
"111010",
|
||||
|
||||
"000011", // (An)+ Address with Postincrement
|
||||
"001011",
|
||||
"010011",
|
||||
"011011",
|
||||
"100011",
|
||||
"101011",
|
||||
"110011",
|
||||
"111011",
|
||||
|
||||
"000100", // -(An) Address with Predecrement
|
||||
"001100",
|
||||
"010100",
|
||||
"011100",
|
||||
"100100",
|
||||
"101100",
|
||||
"110100",
|
||||
"111100",
|
||||
|
||||
"000101", // (d16, An) Address with Displacement
|
||||
"001101",
|
||||
"010101",
|
||||
"011101",
|
||||
"100101",
|
||||
"101101",
|
||||
"110101",
|
||||
"111101",
|
||||
|
||||
"000110", // (d8, An, Xn) Address with Index
|
||||
"001110",
|
||||
"010110",
|
||||
"011110",
|
||||
"100110",
|
||||
"101110",
|
||||
"110110",
|
||||
"111110",
|
||||
|
||||
"010111", // (d16, PC) PC with Displacement
|
||||
"011111", // (d8, PC, Xn) PC with Index
|
||||
"000111", // (xxx).W Absolute Short
|
||||
"001111", // (xxx).L Absolute Long
|
||||
"100111", // #imm Immediate
|
||||
};
|
||||
|
||||
private static readonly string[] Am3Xn3 = {
|
||||
"000000", // Dn Data register
|
||||
"000001",
|
||||
"000010",
|
||||
"000011",
|
||||
"000100",
|
||||
"000101",
|
||||
"000110",
|
||||
"000111",
|
||||
|
||||
"001000", // An Address register
|
||||
"001001",
|
||||
"001010",
|
||||
"001011",
|
||||
"001100",
|
||||
"001101",
|
||||
"001110",
|
||||
"001111",
|
||||
|
||||
"010000", // (An) Address
|
||||
"010001",
|
||||
"010010",
|
||||
"010011",
|
||||
"010100",
|
||||
"010101",
|
||||
"010110",
|
||||
"010111",
|
||||
|
||||
"011000", // (An)+ Address with Postincrement
|
||||
"011001",
|
||||
"011010",
|
||||
"011011",
|
||||
"011100",
|
||||
"011101",
|
||||
"011110",
|
||||
"011111",
|
||||
|
||||
"100000", // -(An) Address with Predecrement
|
||||
"100001",
|
||||
"100010",
|
||||
"100011",
|
||||
"100100",
|
||||
"100101",
|
||||
"100110",
|
||||
"100111",
|
||||
|
||||
"101000", // (d16, An) Address with Displacement
|
||||
"101001",
|
||||
"101010",
|
||||
"101011",
|
||||
"101100",
|
||||
"101101",
|
||||
"101110",
|
||||
"101111",
|
||||
|
||||
"110000", // (d8, An, Xn) Address with Index
|
||||
"110001",
|
||||
"110010",
|
||||
"110011",
|
||||
"110100",
|
||||
"110101",
|
||||
"110110",
|
||||
"110111",
|
||||
|
||||
"111010", // (d16, PC) PC with Displacement
|
||||
"111011", // (d8, PC, Xn) PC with Index
|
||||
"111000", // (xxx).W Absolute Short
|
||||
"111001", // (xxx).L Absolute Long
|
||||
"111100", // #imm Immediate
|
||||
};
|
||||
|
||||
private static readonly string[] ConditionMain = {
|
||||
"0010", // HI Higher (unsigned)
|
||||
"0011", // LS Lower or Same (unsigned)
|
||||
"0100", // CC Carry Clear (aka Higher or Same, unsigned)
|
||||
"0101", // CS Carry Set (aka Lower, unsigned)
|
||||
"0110", // NE Not Equal
|
||||
"0111", // EQ Equal
|
||||
"1000", // VC Overflow Clear
|
||||
"1001", // VS Overflow Set
|
||||
"1010", // PL Plus
|
||||
"1011", // MI Minus
|
||||
"1100", // GE Greater or Equal (signed)
|
||||
"1101", // LT Less Than (signed)
|
||||
"1110", // GT Greater Than (signed)
|
||||
"1111" // LE Less or Equal (signed)
|
||||
};
|
||||
|
||||
private static readonly string[] ConditionAll = {
|
||||
"0000", // T True
|
||||
"0001", // F False
|
||||
"0010", // HI Higher (unsigned)
|
||||
"0011", // LS Lower or Same (unsigned)
|
||||
"0100", // CC Carry Clear (aka Higher or Same, unsigned)
|
||||
"0101", // CS Carry Set (aka Lower, unsigned)
|
||||
"0110", // NE Not Equal
|
||||
"0111", // EQ Equal
|
||||
"1000", // VC Overflow Clear
|
||||
"1001", // VS Overflow Set
|
||||
"1010", // PL Plus
|
||||
"1011", // MI Minus
|
||||
"1100", // GE Greater or Equal (signed)
|
||||
"1101", // LT Less Than (signed)
|
||||
"1110", // GT Greater Than (signed)
|
||||
"1111" // LE Less or Equal (signed)
|
||||
};
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
namespace BizHawk.Emulation.CPUs.M68K
|
||||
{
|
||||
public partial class M68000
|
||||
{
|
||||
private static readonly int[,] MoveCyclesBW = new int[12,9]
|
||||
{
|
||||
{ 4, 4, 8, 8, 8, 12, 14, 12, 16 },
|
||||
{ 4, 4, 8, 8, 8, 12, 14, 12, 16 },
|
||||
{ 8, 8, 12, 12, 12, 16, 18, 16, 20 },
|
||||
{ 8, 8, 12, 12, 12, 16, 18, 16, 20 },
|
||||
{ 10, 10, 14, 14, 14, 18, 20, 18, 22 },
|
||||
{ 12, 12, 16, 16, 16, 20, 22, 20, 24 },
|
||||
{ 14, 14, 18, 18, 18, 22, 24, 22, 26 },
|
||||
{ 12, 12, 16, 16, 16, 20, 22, 20, 24 },
|
||||
{ 16, 16, 20, 20, 20, 24, 26, 24, 28 },
|
||||
{ 12, 12, 16, 16, 16, 20, 22, 20, 24 },
|
||||
{ 14, 14, 18, 18, 18, 22, 24, 22, 26 },
|
||||
{ 8, 8, 12, 12, 12, 16, 18, 16, 20 }
|
||||
};
|
||||
|
||||
private static readonly int[,] MoveCyclesL = new int[12, 9]
|
||||
{
|
||||
{ 4, 4, 12, 12, 12, 16, 18, 16, 20 },
|
||||
{ 4, 4, 12, 12, 12, 16, 18, 16, 20 },
|
||||
{ 12, 12, 20, 20, 20, 24, 26, 24, 28 },
|
||||
{ 12, 12, 20, 20, 20, 24, 26, 24, 28 },
|
||||
{ 14, 14, 22, 22, 22, 26, 28, 26, 30 },
|
||||
{ 16, 16, 24, 24, 24, 28, 30, 28, 32 },
|
||||
{ 18, 18, 26, 26, 26, 30, 32, 30, 34 },
|
||||
{ 16, 16, 24, 24, 24, 28, 30, 28, 32 },
|
||||
{ 20, 20, 28, 28, 28, 32, 34, 32, 36 },
|
||||
{ 16, 16, 24, 24, 24, 28, 30, 28, 32 },
|
||||
{ 18, 18, 26, 26, 26, 30, 32, 30, 34 },
|
||||
{ 12, 12, 20, 20, 20, 24, 26, 24, 28 }
|
||||
};
|
||||
|
||||
private static readonly int[,] EACyclesBW = new int[8, 9]
|
||||
{
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 4, 4, 4, 4, 4, 4, 4, 4, 4 },
|
||||
{ 4, 4, 4, 4, 4, 4, 4, 4, 4 },
|
||||
{ 6, 6, 6, 6, 6, 6, 6, 6, 6 },
|
||||
{ 8, 8, 8, 8, 8, 8, 8, 8, 8 },
|
||||
{ 10, 10, 10, 10, 10, 10, 10, 10, 10 },
|
||||
{ 8, 12, 8, 10, 4, 99, 99, 99, 99 }
|
||||
};
|
||||
|
||||
private static readonly int[,] EACyclesL = new int[8, 9]
|
||||
{
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 8, 8, 8, 8, 8, 8, 8, 8, 8 },
|
||||
{ 8, 8, 8, 8, 8, 8, 8, 8, 8 },
|
||||
{ 10, 10, 10, 10, 10, 10, 10, 10, 10 },
|
||||
{ 12, 12, 12, 12, 12, 12, 12, 12, 12 },
|
||||
{ 14, 14, 14, 14, 14, 14, 14, 14, 14 },
|
||||
{ 12, 16, 12, 14, 8, 99, 99, 99, 99 }
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,253 @@
|
|||
namespace BizHawk.Emulation.CPUs.H6280
|
||||
|
||||
// Do not modify this file directly! This is GENERATED code.
|
||||
// Please open the CpuCoreGenerator solution and make your modifications there.
|
||||
|
||||
{
|
||||
public partial class HuC6280
|
||||
{
|
||||
public string Disassemble(ushort pc, out int bytesToAdvance)
|
||||
{
|
||||
byte op = ReadMemory(pc);
|
||||
switch (op)
|
||||
{
|
||||
case 0x00: bytesToAdvance = 1; return "BRK";
|
||||
case 0x01: bytesToAdvance = 2; return string.Format("ORA (${0:X2},X)", ReadMemory(++pc));
|
||||
case 0x02: bytesToAdvance = 1; return "SXY";
|
||||
case 0x03: bytesToAdvance = 2; return string.Format("ST0 #${0:X2}", ReadMemory(++pc));
|
||||
case 0x04: bytesToAdvance = 2; return string.Format("TSB ${0:X2}", ReadMemory(++pc));
|
||||
case 0x05: bytesToAdvance = 2; return string.Format("ORA ${0:X2}", ReadMemory(++pc));
|
||||
case 0x06: bytesToAdvance = 2; return string.Format("ASL ${0:X2}", ReadMemory(++pc));
|
||||
case 0x07: bytesToAdvance = 2; return string.Format("RMB0 ${0:X2}", ReadMemory(++pc));
|
||||
case 0x08: bytesToAdvance = 1; return "PHP";
|
||||
case 0x09: bytesToAdvance = 2; return string.Format("ORA #${0:X2}", ReadMemory(++pc));
|
||||
case 0x0A: bytesToAdvance = 1; return "ASL A";
|
||||
case 0x0C: bytesToAdvance = 3; return string.Format("TSB ${0:X4}", ReadWord(++pc));
|
||||
case 0x0D: bytesToAdvance = 3; return string.Format("ORA ${0:X4}", ReadWord(++pc));
|
||||
case 0x0E: bytesToAdvance = 3; return string.Format("ASL ${0:X4}", ReadWord(++pc));
|
||||
case 0x0F: bytesToAdvance = 3; return string.Format("BBR0 ${0:X2},{1}", ReadMemory(++pc), (sbyte)ReadMemory(++pc));
|
||||
case 0x10: bytesToAdvance = 2; return string.Format("BPL {0}", (sbyte)ReadMemory(++pc));
|
||||
case 0x11: bytesToAdvance = 2; return string.Format("ORA (${0:X2}),Y", ReadMemory(++pc));
|
||||
case 0x12: bytesToAdvance = 2; return string.Format("ORA (${0:X2})", ReadMemory(++pc));
|
||||
case 0x13: bytesToAdvance = 2; return string.Format("ST1 #${0:X2}", ReadMemory(++pc));
|
||||
case 0x14: bytesToAdvance = 2; return string.Format("TRB ${0:X2}", ReadMemory(++pc));
|
||||
case 0x15: bytesToAdvance = 2; return string.Format("ORA ${0:X2},X", ReadMemory(++pc));
|
||||
case 0x16: bytesToAdvance = 2; return string.Format("ASL ${0:X2},X", ReadMemory(++pc));
|
||||
case 0x17: bytesToAdvance = 2; return string.Format("RMB1 ${0:X2}", ReadMemory(++pc));
|
||||
case 0x18: bytesToAdvance = 1; return "CLC";
|
||||
case 0x19: bytesToAdvance = 3; return string.Format("ORA ${0:X4},Y", ReadWord(++pc));
|
||||
case 0x1A: bytesToAdvance = 1; return "INC A";
|
||||
case 0x1C: bytesToAdvance = 3; return string.Format("TRB ${0:X4}", ReadWord(++pc));
|
||||
case 0x1D: bytesToAdvance = 3; return string.Format("ORA ${0:X4},X", ReadWord(++pc));
|
||||
case 0x1E: bytesToAdvance = 3; return string.Format("ASL ${0:X4},X", ReadWord(++pc));
|
||||
case 0x1F: bytesToAdvance = 3; return string.Format("BBR1 ${0:X2},{1}", ReadMemory(++pc), (sbyte)ReadMemory(++pc));
|
||||
case 0x20: bytesToAdvance = 3; return string.Format("JSR ${0:X4}", ReadWord(++pc));
|
||||
case 0x21: bytesToAdvance = 2; return string.Format("AND (${0:X2},X)", ReadMemory(++pc));
|
||||
case 0x22: bytesToAdvance = 1; return "SAX";
|
||||
case 0x23: bytesToAdvance = 2; return string.Format("ST2 #${0:X2}", ReadMemory(++pc));
|
||||
case 0x24: bytesToAdvance = 2; return string.Format("BIT ${0:X2}", ReadMemory(++pc));
|
||||
case 0x25: bytesToAdvance = 2; return string.Format("AND ${0:X2}", ReadMemory(++pc));
|
||||
case 0x26: bytesToAdvance = 2; return string.Format("ROL ${0:X2}", ReadMemory(++pc));
|
||||
case 0x27: bytesToAdvance = 2; return string.Format("RMB2 ${0:X2}", ReadMemory(++pc));
|
||||
case 0x28: bytesToAdvance = 1; return "PLP";
|
||||
case 0x29: bytesToAdvance = 2; return string.Format("AND #${0:X2}", ReadMemory(++pc));
|
||||
case 0x2A: bytesToAdvance = 1; return "ROL A";
|
||||
case 0x2C: bytesToAdvance = 3; return string.Format("BIT ${0:X4}", ReadWord(++pc));
|
||||
case 0x2D: bytesToAdvance = 3; return string.Format("AND ${0:X4}", ReadWord(++pc));
|
||||
case 0x2E: bytesToAdvance = 3; return string.Format("ROL ${0:X4}", ReadWord(++pc));
|
||||
case 0x2F: bytesToAdvance = 3; return string.Format("BBR2 ${0:X2},{1}", ReadMemory(++pc), (sbyte)ReadMemory(++pc));
|
||||
case 0x30: bytesToAdvance = 2; return string.Format("BMI {0}", (sbyte)ReadMemory(++pc));
|
||||
case 0x31: bytesToAdvance = 2; return string.Format("AND (${0:X2}),Y", ReadMemory(++pc));
|
||||
case 0x32: bytesToAdvance = 2; return string.Format("AND (${0:X2})", ReadMemory(++pc));
|
||||
case 0x34: bytesToAdvance = 2; return string.Format("BIT ${0:X2},X", ReadMemory(++pc));
|
||||
case 0x35: bytesToAdvance = 2; return string.Format("AND ${0:X2},X", ReadMemory(++pc));
|
||||
case 0x36: bytesToAdvance = 2; return string.Format("ROL ${0:X2},X", ReadMemory(++pc));
|
||||
case 0x37: bytesToAdvance = 2; return string.Format("RMB3 ${0:X2}", ReadMemory(++pc));
|
||||
case 0x38: bytesToAdvance = 1; return "SEC";
|
||||
case 0x39: bytesToAdvance = 3; return string.Format("AND ${0:X4},Y", ReadWord(++pc));
|
||||
case 0x3A: bytesToAdvance = 1; return "DEC A";
|
||||
case 0x3C: bytesToAdvance = 3; return string.Format("BIT ${0:X4},X", ReadWord(++pc));
|
||||
case 0x3D: bytesToAdvance = 3; return string.Format("AND ${0:X4},X", ReadWord(++pc));
|
||||
case 0x3E: bytesToAdvance = 3; return string.Format("ROL ${0:X4},X", ReadWord(++pc));
|
||||
case 0x3F: bytesToAdvance = 3; return string.Format("BBR3 ${0:X2},{1}", ReadMemory(++pc), (sbyte)ReadMemory(++pc));
|
||||
case 0x40: bytesToAdvance = 1; return "RTI";
|
||||
case 0x41: bytesToAdvance = 2; return string.Format("EOR (${0:X2},X)", ReadMemory(++pc));
|
||||
case 0x42: bytesToAdvance = 1; return "SAY";
|
||||
case 0x43: bytesToAdvance = 2; return string.Format("TMA #${0:X2}", ReadMemory(++pc));
|
||||
case 0x44: bytesToAdvance = 2; return string.Format("BSR {0}", (sbyte)ReadMemory(++pc));
|
||||
case 0x45: bytesToAdvance = 2; return string.Format("EOR ${0:X2}", ReadMemory(++pc));
|
||||
case 0x46: bytesToAdvance = 2; return string.Format("LSR ${0:X2}", ReadMemory(++pc));
|
||||
case 0x47: bytesToAdvance = 2; return string.Format("RMB4 ${0:X2}", ReadMemory(++pc));
|
||||
case 0x48: bytesToAdvance = 1; return "PHA";
|
||||
case 0x49: bytesToAdvance = 2; return string.Format("EOR #${0:X2}", ReadMemory(++pc));
|
||||
case 0x4A: bytesToAdvance = 1; return "LSR A";
|
||||
case 0x4C: bytesToAdvance = 3; return string.Format("JMP ${0:X4}", ReadWord(++pc));
|
||||
case 0x4D: bytesToAdvance = 3; return string.Format("EOR ${0:X4}", ReadWord(++pc));
|
||||
case 0x4E: bytesToAdvance = 3; return string.Format("LSR ${0:X4}", ReadWord(++pc));
|
||||
case 0x4F: bytesToAdvance = 3; return string.Format("BBR4 ${0:X2},{1}", ReadMemory(++pc), (sbyte)ReadMemory(++pc));
|
||||
case 0x50: bytesToAdvance = 2; return string.Format("BVC {0}", (sbyte)ReadMemory(++pc));
|
||||
case 0x51: bytesToAdvance = 2; return string.Format("EOR (${0:X2}),Y", ReadMemory(++pc));
|
||||
case 0x52: bytesToAdvance = 2; return string.Format("EOR (${0:X2})", ReadMemory(++pc));
|
||||
case 0x53: bytesToAdvance = 2; return string.Format("TAM #${0:X2}", ReadMemory(++pc));
|
||||
case 0x54: bytesToAdvance = 1; return "CSL";
|
||||
case 0x55: bytesToAdvance = 2; return string.Format("EOR ${0:X2},X", ReadMemory(++pc));
|
||||
case 0x56: bytesToAdvance = 2; return string.Format("LSR ${0:X2},X", ReadMemory(++pc));
|
||||
case 0x57: bytesToAdvance = 2; return string.Format("RMB5 ${0:X2}", ReadMemory(++pc));
|
||||
case 0x58: bytesToAdvance = 1; return "CLI";
|
||||
case 0x59: bytesToAdvance = 3; return string.Format("EOR ${0:X4},Y", ReadWord(++pc));
|
||||
case 0x5A: bytesToAdvance = 1; return "PHY";
|
||||
case 0x5D: bytesToAdvance = 3; return string.Format("EOR ${0:X4},X", ReadWord(++pc));
|
||||
case 0x5E: bytesToAdvance = 3; return string.Format("LSR ${0:X4},X", ReadWord(++pc));
|
||||
case 0x5F: bytesToAdvance = 3; return string.Format("BBR5 ${0:X2},{1}", ReadMemory(++pc), (sbyte)ReadMemory(++pc));
|
||||
case 0x60: bytesToAdvance = 1; return "RTS";
|
||||
case 0x61: bytesToAdvance = 2; return string.Format("ADC (${0:X2},X)", ReadMemory(++pc));
|
||||
case 0x62: bytesToAdvance = 1; return "CLA";
|
||||
case 0x64: bytesToAdvance = 2; return string.Format("STZ ${0:X2}", ReadMemory(++pc));
|
||||
case 0x65: bytesToAdvance = 2; return string.Format("ADC ${0:X2}", ReadMemory(++pc));
|
||||
case 0x66: bytesToAdvance = 2; return string.Format("ROR ${0:X2}", ReadMemory(++pc));
|
||||
case 0x67: bytesToAdvance = 2; return string.Format("RMB6 ${0:X2}", ReadMemory(++pc));
|
||||
case 0x68: bytesToAdvance = 1; return "PLA";
|
||||
case 0x69: bytesToAdvance = 2; return string.Format("ADC #${0:X2}", ReadMemory(++pc));
|
||||
case 0x6A: bytesToAdvance = 1; return "ROR A";
|
||||
case 0x6C: bytesToAdvance = 3; return string.Format("JMP (${0:X4})", ReadWord(++pc));
|
||||
case 0x6D: bytesToAdvance = 3; return string.Format("ADC ${0:X4}", ReadWord(++pc));
|
||||
case 0x6E: bytesToAdvance = 3; return string.Format("ROR ${0:X4}", ReadWord(++pc));
|
||||
case 0x6F: bytesToAdvance = 3; return string.Format("BBR6 ${0:X2},{1}", ReadMemory(++pc), (sbyte)ReadMemory(++pc));
|
||||
case 0x70: bytesToAdvance = 2; return string.Format("BVS {0}", (sbyte)ReadMemory(++pc));
|
||||
case 0x71: bytesToAdvance = 2; return string.Format("ADC (${0:X2}),Y", ReadMemory(++pc));
|
||||
case 0x72: bytesToAdvance = 2; return string.Format("ADC (${0:X2})", ReadMemory(++pc));
|
||||
case 0x73: bytesToAdvance = 7; return string.Format("TII {0:X4},{1:X4},{2:X4}", ReadWord((ushort)(pc+1)),ReadWord((ushort)(pc+3)),ReadWord((ushort)(pc+5)));
|
||||
case 0x74: bytesToAdvance = 2; return string.Format("STZ ${0:X2},X", ReadMemory(++pc));
|
||||
case 0x75: bytesToAdvance = 2; return string.Format("ADC ${0:X2},X", ReadMemory(++pc));
|
||||
case 0x76: bytesToAdvance = 2; return string.Format("ROR ${0:X2},X", ReadMemory(++pc));
|
||||
case 0x77: bytesToAdvance = 2; return string.Format("RMB7 ${0:X2}", ReadMemory(++pc));
|
||||
case 0x78: bytesToAdvance = 1; return "SEI";
|
||||
case 0x79: bytesToAdvance = 3; return string.Format("ADC ${0:X4},Y", ReadWord(++pc));
|
||||
case 0x7A: bytesToAdvance = 1; return "PLY";
|
||||
case 0x7C: bytesToAdvance = 3; return string.Format("JMP (${0:X4},X)", ReadWord(++pc));
|
||||
case 0x7D: bytesToAdvance = 3; return string.Format("ADC ${0:X4},X", ReadWord(++pc));
|
||||
case 0x7E: bytesToAdvance = 3; return string.Format("ROR ${0:X4},X", ReadWord(++pc));
|
||||
case 0x7F: bytesToAdvance = 3; return string.Format("BBR7 ${0:X2},{1}", ReadMemory(++pc), (sbyte)ReadMemory(++pc));
|
||||
case 0x80: bytesToAdvance = 2; return string.Format("BRA {0}", (sbyte)ReadMemory(++pc));
|
||||
case 0x81: bytesToAdvance = 2; return string.Format("STA (${0:X2},X)", ReadMemory(++pc));
|
||||
case 0x82: bytesToAdvance = 1; return "CLX";
|
||||
case 0x83: bytesToAdvance = 3; return string.Format("TST #${0:X2}, ${1:X2}", ReadMemory(++pc), ReadMemory(++pc));
|
||||
case 0x84: bytesToAdvance = 2; return string.Format("STY ${0:X2}", ReadMemory(++pc));
|
||||
case 0x85: bytesToAdvance = 2; return string.Format("STA ${0:X2}", ReadMemory(++pc));
|
||||
case 0x86: bytesToAdvance = 2; return string.Format("STX ${0:X2}", ReadMemory(++pc));
|
||||
case 0x87: bytesToAdvance = 2; return string.Format("SMB0 ${0:X2}", ReadMemory(++pc));
|
||||
case 0x88: bytesToAdvance = 1; return "DEY";
|
||||
case 0x89: bytesToAdvance = 2; return string.Format("BIT #${0:X2}", ReadMemory(++pc));
|
||||
case 0x8A: bytesToAdvance = 1; return "TXA";
|
||||
case 0x8C: bytesToAdvance = 3; return string.Format("STY ${0:X4}", ReadWord(++pc));
|
||||
case 0x8D: bytesToAdvance = 3; return string.Format("STA ${0:X4}", ReadWord(++pc));
|
||||
case 0x8E: bytesToAdvance = 3; return string.Format("STX ${0:X4}", ReadWord(++pc));
|
||||
case 0x8F: bytesToAdvance = 3; return string.Format("BBS0 ${0:X2},{1}", ReadMemory(++pc), (sbyte)ReadMemory(++pc));
|
||||
case 0x90: bytesToAdvance = 2; return string.Format("BCC {0}", (sbyte)ReadMemory(++pc));
|
||||
case 0x91: bytesToAdvance = 2; return string.Format("STA (${0:X2}),Y", ReadMemory(++pc));
|
||||
case 0x92: bytesToAdvance = 2; return string.Format("STA (${0:X2})", ReadMemory(++pc));
|
||||
case 0x93: bytesToAdvance = 4; return string.Format("TST #${0:X2}, ${1:X4}", ReadMemory(++pc), ReadWord(++pc));
|
||||
case 0x94: bytesToAdvance = 2; return string.Format("STY ${0:X2},X", ReadMemory(++pc));
|
||||
case 0x95: bytesToAdvance = 2; return string.Format("STA ${0:X2},X", ReadMemory(++pc));
|
||||
case 0x96: bytesToAdvance = 2; return string.Format("STX ${0:X2},Y", ReadMemory(++pc));
|
||||
case 0x97: bytesToAdvance = 2; return string.Format("SMB1 ${0:X2}", ReadMemory(++pc));
|
||||
case 0x98: bytesToAdvance = 1; return "TYA";
|
||||
case 0x99: bytesToAdvance = 3; return string.Format("STA ${0:X4},Y", ReadWord(++pc));
|
||||
case 0x9A: bytesToAdvance = 1; return "TXS";
|
||||
case 0x9C: bytesToAdvance = 3; return string.Format("STZ ${0:X4}", ReadWord(++pc));
|
||||
case 0x9D: bytesToAdvance = 3; return string.Format("STA ${0:X4},X", ReadWord(++pc));
|
||||
case 0x9E: bytesToAdvance = 3; return string.Format("STZ ${0:X4},X", ReadWord(++pc));
|
||||
case 0x9F: bytesToAdvance = 3; return string.Format("BBS1 ${0:X2},{1}", ReadMemory(++pc), (sbyte)ReadMemory(++pc));
|
||||
case 0xA0: bytesToAdvance = 2; return string.Format("LDY #${0:X2}", ReadMemory(++pc));
|
||||
case 0xA1: bytesToAdvance = 2; return string.Format("LDA (${0:X2},X)", ReadMemory(++pc));
|
||||
case 0xA2: bytesToAdvance = 2; return string.Format("LDX #${0:X2}", ReadMemory(++pc));
|
||||
case 0xA3: bytesToAdvance = 3; return string.Format("TST #${0:X2}, ${1:X2},X", ReadMemory(++pc), ReadMemory(++pc));
|
||||
case 0xA4: bytesToAdvance = 2; return string.Format("LDY ${0:X2}", ReadMemory(++pc));
|
||||
case 0xA5: bytesToAdvance = 2; return string.Format("LDA ${0:X2}", ReadMemory(++pc));
|
||||
case 0xA6: bytesToAdvance = 2; return string.Format("LDX ${0:X2}", ReadMemory(++pc));
|
||||
case 0xA7: bytesToAdvance = 2; return string.Format("SMB2 ${0:X2}", ReadMemory(++pc));
|
||||
case 0xA8: bytesToAdvance = 1; return "TAY";
|
||||
case 0xA9: bytesToAdvance = 2; return string.Format("LDA #${0:X2}", ReadMemory(++pc));
|
||||
case 0xAA: bytesToAdvance = 1; return "TAX";
|
||||
case 0xAC: bytesToAdvance = 3; return string.Format("LDY ${0:X4}", ReadWord(++pc));
|
||||
case 0xAD: bytesToAdvance = 3; return string.Format("LDA ${0:X4}", ReadWord(++pc));
|
||||
case 0xAE: bytesToAdvance = 3; return string.Format("LDX ${0:X4}", ReadWord(++pc));
|
||||
case 0xAF: bytesToAdvance = 3; return string.Format("BBS2 ${0:X2},{1}", ReadMemory(++pc), (sbyte)ReadMemory(++pc));
|
||||
case 0xB0: bytesToAdvance = 2; return string.Format("BCS {0}", (sbyte)ReadMemory(++pc));
|
||||
case 0xB1: bytesToAdvance = 2; return string.Format("LDA (${0:X2}),Y", ReadMemory(++pc));
|
||||
case 0xB2: bytesToAdvance = 2; return string.Format("LDA (${0:X2})", ReadMemory(++pc));
|
||||
case 0xB3: bytesToAdvance = 4; return string.Format("TST #${0:X2}, ${1:X4},X", ReadMemory(++pc), ReadWord(++pc));
|
||||
case 0xB4: bytesToAdvance = 2; return string.Format("LDY ${0:X2},X", ReadMemory(++pc));
|
||||
case 0xB5: bytesToAdvance = 2; return string.Format("LDA ${0:X2},X", ReadMemory(++pc));
|
||||
case 0xB6: bytesToAdvance = 2; return string.Format("LDX ${0:X2},Y", ReadMemory(++pc));
|
||||
case 0xB7: bytesToAdvance = 2; return string.Format("SMB3 ${0:X2}", ReadMemory(++pc));
|
||||
case 0xB8: bytesToAdvance = 1; return "CLV";
|
||||
case 0xB9: bytesToAdvance = 3; return string.Format("LDA ${0:X4},Y", ReadWord(++pc));
|
||||
case 0xBA: bytesToAdvance = 1; return "TSX";
|
||||
case 0xBC: bytesToAdvance = 3; return string.Format("LDY ${0:X4},X", ReadWord(++pc));
|
||||
case 0xBD: bytesToAdvance = 3; return string.Format("LDA ${0:X4},X", ReadWord(++pc));
|
||||
case 0xBE: bytesToAdvance = 3; return string.Format("LDX ${0:X4},Y", ReadWord(++pc));
|
||||
case 0xBF: bytesToAdvance = 3; return string.Format("BBS3 ${0:X2},{1}", ReadMemory(++pc), (sbyte)ReadMemory(++pc));
|
||||
case 0xC0: bytesToAdvance = 2; return string.Format("CPY #${0:X2}", ReadMemory(++pc));
|
||||
case 0xC1: bytesToAdvance = 2; return string.Format("CMP (${0:X2},X)", ReadMemory(++pc));
|
||||
case 0xC2: bytesToAdvance = 1; return "CLY";
|
||||
case 0xC3: bytesToAdvance = 7; return string.Format("TDD {0:X4},{1:X4},{2:X4}", ReadWord((ushort)(pc+1)),ReadWord((ushort)(pc+3)),ReadWord((ushort)(pc+5)));
|
||||
case 0xC4: bytesToAdvance = 2; return string.Format("CPY ${0:X2}", ReadMemory(++pc));
|
||||
case 0xC5: bytesToAdvance = 2; return string.Format("CMP ${0:X2}", ReadMemory(++pc));
|
||||
case 0xC6: bytesToAdvance = 2; return string.Format("DEC ${0:X2}", ReadMemory(++pc));
|
||||
case 0xC7: bytesToAdvance = 2; return string.Format("SMB4 ${0:X2}", ReadMemory(++pc));
|
||||
case 0xC8: bytesToAdvance = 1; return "INY";
|
||||
case 0xC9: bytesToAdvance = 2; return string.Format("CMP #${0:X2}", ReadMemory(++pc));
|
||||
case 0xCA: bytesToAdvance = 1; return "DEX";
|
||||
case 0xCC: bytesToAdvance = 3; return string.Format("CPY ${0:X4}", ReadWord(++pc));
|
||||
case 0xCD: bytesToAdvance = 3; return string.Format("CMP ${0:X4}", ReadWord(++pc));
|
||||
case 0xCE: bytesToAdvance = 3; return string.Format("DEC ${0:X4}", ReadWord(++pc));
|
||||
case 0xCF: bytesToAdvance = 3; return string.Format("BBS4 ${0:X2},{1}", ReadMemory(++pc), (sbyte)ReadMemory(++pc));
|
||||
case 0xD0: bytesToAdvance = 2; return string.Format("BNE {0}", (sbyte)ReadMemory(++pc));
|
||||
case 0xD1: bytesToAdvance = 2; return string.Format("CMP (${0:X2}),Y", ReadMemory(++pc));
|
||||
case 0xD2: bytesToAdvance = 2; return string.Format("CMP (${0:X2})", ReadMemory(++pc));
|
||||
case 0xD3: bytesToAdvance = 7; return string.Format("TIN {0:X4},{1:X4},{2:X4}", ReadWord((ushort)(pc+1)),ReadWord((ushort)(pc+3)),ReadWord((ushort)(pc+5)));
|
||||
case 0xD4: bytesToAdvance = 1; return "CSH";
|
||||
case 0xD5: bytesToAdvance = 2; return string.Format("CMP ${0:X2},X", ReadMemory(++pc));
|
||||
case 0xD6: bytesToAdvance = 2; return string.Format("DEC ${0:X2},X", ReadMemory(++pc));
|
||||
case 0xD7: bytesToAdvance = 2; return string.Format("SMB5 ${0:X2}", ReadMemory(++pc));
|
||||
case 0xD8: bytesToAdvance = 1; return "CLD";
|
||||
case 0xD9: bytesToAdvance = 3; return string.Format("CMP ${0:X4},Y", ReadWord(++pc));
|
||||
case 0xDA: bytesToAdvance = 1; return "PHX";
|
||||
case 0xDD: bytesToAdvance = 3; return string.Format("CMP ${0:X4},X", ReadWord(++pc));
|
||||
case 0xDE: bytesToAdvance = 3; return string.Format("DEC ${0:X4},X", ReadWord(++pc));
|
||||
case 0xDF: bytesToAdvance = 3; return string.Format("BBS5 ${0:X2},{1}", ReadMemory(++pc), (sbyte)ReadMemory(++pc));
|
||||
case 0xE0: bytesToAdvance = 2; return string.Format("CPX #${0:X2}", ReadMemory(++pc));
|
||||
case 0xE1: bytesToAdvance = 2; return string.Format("SBC (${0:X2},X)", ReadMemory(++pc));
|
||||
case 0xE3: bytesToAdvance = 7; return string.Format("TIA {0:X4},{1:X4},{2:X4}", ReadWord((ushort)(pc+1)),ReadWord((ushort)(pc+3)),ReadWord((ushort)(pc+5)));
|
||||
case 0xE4: bytesToAdvance = 2; return string.Format("CPX ${0:X2}", ReadMemory(++pc));
|
||||
case 0xE5: bytesToAdvance = 2; return string.Format("SBC ${0:X2}", ReadMemory(++pc));
|
||||
case 0xE6: bytesToAdvance = 2; return string.Format("INC ${0:X2}", ReadMemory(++pc));
|
||||
case 0xE7: bytesToAdvance = 2; return string.Format("SMB6 ${0:X2}", ReadMemory(++pc));
|
||||
case 0xE8: bytesToAdvance = 1; return "INX";
|
||||
case 0xE9: bytesToAdvance = 2; return string.Format("SBC #${0:X2}", ReadMemory(++pc));
|
||||
case 0xEA: bytesToAdvance = 1; return "NOP";
|
||||
case 0xEC: bytesToAdvance = 3; return string.Format("CPX ${0:X4}", ReadWord(++pc));
|
||||
case 0xED: bytesToAdvance = 3; return string.Format("SBC ${0:X4}", ReadWord(++pc));
|
||||
case 0xEE: bytesToAdvance = 3; return string.Format("INC ${0:X4}", ReadWord(++pc));
|
||||
case 0xEF: bytesToAdvance = 3; return string.Format("BBS6 ${0:X2},{1}", ReadMemory(++pc), (sbyte)ReadMemory(++pc));
|
||||
case 0xF0: bytesToAdvance = 2; return string.Format("BEQ {0}", (sbyte)ReadMemory(++pc));
|
||||
case 0xF1: bytesToAdvance = 2; return string.Format("SBC (${0:X2}),Y", ReadMemory(++pc));
|
||||
case 0xF2: bytesToAdvance = 2; return string.Format("SBC (${0:X2})", ReadMemory(++pc));
|
||||
case 0xF3: bytesToAdvance = 7; return string.Format("TAI {0:X4},{1:X4},{2:X4}", ReadWord((ushort)(pc+1)),ReadWord((ushort)(pc+3)),ReadWord((ushort)(pc+5)));
|
||||
case 0xF4: bytesToAdvance = 1; return "SET";
|
||||
case 0xF5: bytesToAdvance = 2; return string.Format("SBC ${0:X2},X", ReadMemory(++pc));
|
||||
case 0xF6: bytesToAdvance = 2; return string.Format("INC ${0:X2},X", ReadMemory(++pc));
|
||||
case 0xF7: bytesToAdvance = 2; return string.Format("SMB7 ${0:X2}", ReadMemory(++pc));
|
||||
case 0xF8: bytesToAdvance = 1; return "SED";
|
||||
case 0xF9: bytesToAdvance = 3; return string.Format("SBC ${0:X4},Y", ReadWord(++pc));
|
||||
case 0xFA: bytesToAdvance = 1; return "PLX";
|
||||
case 0xFD: bytesToAdvance = 3; return string.Format("SBC ${0:X4},X", ReadWord(++pc));
|
||||
case 0xFE: bytesToAdvance = 3; return string.Format("INC ${0:X4},X", ReadWord(++pc));
|
||||
case 0xFF: bytesToAdvance = 3; return string.Format("BBS7 ${0:X2},{1}", ReadMemory(++pc), (sbyte)ReadMemory(++pc));
|
||||
}
|
||||
bytesToAdvance = 1;
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,353 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
|
||||
namespace BizHawk.Emulation.CPUs.H6280
|
||||
{
|
||||
public sealed partial class HuC6280
|
||||
{
|
||||
public HuC6280()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
A = 0;
|
||||
X = 0;
|
||||
Y = 0;
|
||||
//P = 0x14; // Set I and B
|
||||
P = 0x04; // Set I
|
||||
S = 0;
|
||||
PC = 0;
|
||||
PendingCycles = 0;
|
||||
TotalExecutedCycles = 0;
|
||||
LagIFlag = true;
|
||||
}
|
||||
|
||||
public void ResetPC()
|
||||
{
|
||||
PC = ReadWord(ResetVector);
|
||||
}
|
||||
|
||||
// ==== CPU State ====
|
||||
|
||||
public byte A;
|
||||
public byte X;
|
||||
public byte Y;
|
||||
public byte P;
|
||||
public ushort PC;
|
||||
public byte S;
|
||||
public byte[] MPR = new byte[8];
|
||||
|
||||
public bool LagIFlag;
|
||||
public bool IRQ1Assert;
|
||||
public bool TimerAssert;
|
||||
public byte IRQControlByte, IRQNextControlByte;
|
||||
|
||||
public int TotalExecutedCycles;
|
||||
public int PendingCycles;
|
||||
|
||||
public bool debug = true;
|
||||
|
||||
// -- Timer Support --
|
||||
|
||||
public int TimerTickCounter;
|
||||
public byte TimerReloadValue;
|
||||
public byte TimerValue;
|
||||
public bool TimerEnabled;
|
||||
|
||||
public void SaveStateText(TextWriter writer)
|
||||
{
|
||||
writer.WriteLine("[HuC6280]");
|
||||
writer.WriteLine("A {0:X2}", A);
|
||||
writer.WriteLine("X {0:X2}", X);
|
||||
writer.WriteLine("Y {0:X2}", Y);
|
||||
writer.WriteLine("P {0:X2}", P);
|
||||
writer.WriteLine("PC {0:X4}", PC);
|
||||
writer.WriteLine("S {0:X2}", S);
|
||||
writer.Write("MPR ");
|
||||
MPR.SaveAsHex(writer);
|
||||
writer.WriteLine("IRQ1Assert {0}", IRQ1Assert);
|
||||
writer.WriteLine("TimerAssert {0}", TimerAssert);
|
||||
writer.WriteLine("IRQControlByte {0:X2}", IRQControlByte);
|
||||
writer.WriteLine("IRQNextControlByte {0:X2}", IRQNextControlByte);
|
||||
writer.WriteLine("ExecutedCycles {0}", TotalExecutedCycles);
|
||||
writer.WriteLine("PendingCycles {0}", PendingCycles);
|
||||
writer.WriteLine("TimerTickCounter {0}", TimerTickCounter);
|
||||
writer.WriteLine("TimerReloadValue {0}", TimerReloadValue);
|
||||
writer.WriteLine("TimerValue {0}", TimerValue);
|
||||
writer.WriteLine("TimerEnabled {0}", TimerEnabled);
|
||||
writer.WriteLine("[/HuC6280]\n");
|
||||
}
|
||||
|
||||
public void LoadStateText(TextReader reader)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
string[] args = reader.ReadLine().Split(' ');
|
||||
if (args[0].Trim() == "") continue;
|
||||
if (args[0] == "[/HuC6280]") break;
|
||||
if (args[0] == "A")
|
||||
A = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "X")
|
||||
X = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "Y")
|
||||
Y = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "P")
|
||||
P = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "PC")
|
||||
PC = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "S")
|
||||
S = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "MPR")
|
||||
MPR.ReadFromHex(args[1]);
|
||||
else if (args[0] == "IRQ1Assert")
|
||||
IRQ1Assert = bool.Parse(args[1]);
|
||||
else if (args[0] == "TimerAssert")
|
||||
TimerAssert = bool.Parse(args[1]);
|
||||
else if (args[0] == "IRQControlByte")
|
||||
IRQControlByte = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "IRQNextControlByte")
|
||||
IRQNextControlByte = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "ExecutedCycles")
|
||||
TotalExecutedCycles = int.Parse(args[1]);
|
||||
else if (args[0] == "PendingCycles")
|
||||
PendingCycles = int.Parse(args[1]);
|
||||
else if (args[0] == "TimerTickCounter")
|
||||
TimerTickCounter = int.Parse(args[1]);
|
||||
else if (args[0] == "TimerReloadValue")
|
||||
TimerReloadValue = byte.Parse(args[1]);
|
||||
else if (args[0] == "TimerValue")
|
||||
TimerValue = byte.Parse(args[1]);
|
||||
else if (args[0] == "TimerEnabled")
|
||||
TimerEnabled = bool.Parse(args[1]);
|
||||
else
|
||||
Console.WriteLine("Skipping unrecognized identifier " + args[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveStateBinary(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(A);
|
||||
writer.Write(X);
|
||||
writer.Write(Y);
|
||||
writer.Write(P);
|
||||
writer.Write(PC);
|
||||
writer.Write(S);
|
||||
writer.Write(MPR);
|
||||
writer.Write(IRQ1Assert);
|
||||
writer.Write(TimerAssert);
|
||||
writer.Write(IRQControlByte);
|
||||
writer.Write(IRQNextControlByte);
|
||||
writer.Write(TotalExecutedCycles);
|
||||
writer.Write(PendingCycles);
|
||||
|
||||
writer.Write(TimerTickCounter);
|
||||
writer.Write(TimerReloadValue);
|
||||
writer.Write(TimerValue);
|
||||
writer.Write(TimerEnabled);
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader reader)
|
||||
{
|
||||
A = reader.ReadByte();
|
||||
X = reader.ReadByte();
|
||||
Y = reader.ReadByte();
|
||||
P = reader.ReadByte();
|
||||
PC = reader.ReadUInt16();
|
||||
S = reader.ReadByte();
|
||||
MPR = reader.ReadBytes(8);
|
||||
IRQ1Assert = reader.ReadBoolean();
|
||||
TimerAssert = reader.ReadBoolean();
|
||||
IRQControlByte = reader.ReadByte();
|
||||
IRQNextControlByte = reader.ReadByte();
|
||||
TotalExecutedCycles = reader.ReadInt32();
|
||||
PendingCycles = reader.ReadInt32();
|
||||
|
||||
TimerTickCounter = reader.ReadInt32();
|
||||
TimerReloadValue = reader.ReadByte();
|
||||
TimerValue = reader.ReadByte();
|
||||
TimerEnabled = reader.ReadBoolean();
|
||||
}
|
||||
|
||||
// ==== Interrupts ====
|
||||
|
||||
private const ushort ResetVector = 0xFFFE;
|
||||
private const ushort NMIVector = 0xFFFC;
|
||||
private const ushort TimerVector = 0xFFFA;
|
||||
private const ushort IRQ1Vector = 0xFFF8;
|
||||
private const ushort IRQ2Vector = 0xFFF6;
|
||||
|
||||
private const byte IRQ2Selector = 0x01;
|
||||
private const byte IRQ1Selector = 0x02;
|
||||
private const byte TimerSelector = 0x04;
|
||||
|
||||
public void WriteIrqControl(byte value)
|
||||
{
|
||||
// There is a single-instruction delay before writes to the IRQ Control Byte take effect.
|
||||
// After Burner requires this to function, as it ACKs the timer interrupt AFTER un-masking
|
||||
// the interrupt.
|
||||
|
||||
value &= 7;
|
||||
IRQNextControlByte = value;
|
||||
//Console.WriteLine("WROTTEN TO IRQ COTRL {0:X2}", value);
|
||||
}
|
||||
|
||||
public void WriteIrqStatus()
|
||||
{
|
||||
//Console.WriteLine("ACKNOWLDGED TIMER INT");
|
||||
TimerAssert = false;
|
||||
}
|
||||
|
||||
public byte ReadIrqStatus()
|
||||
{
|
||||
byte status = 0;
|
||||
//if (IRQ2Assert) status |= 1;
|
||||
if (IRQ1Assert) status |= 2;
|
||||
if (TimerAssert) status |= 4;
|
||||
//Console.WriteLine("READING IRQ STATUS BYTE {0:X2}",status);
|
||||
return status;
|
||||
}
|
||||
|
||||
public void WriteTimer(byte value)
|
||||
{
|
||||
value &= 0x7F;
|
||||
TimerReloadValue = value;
|
||||
//Console.WriteLine("*** Timer Countdown Set {0}, {1} per sec",value,7160000/1024/(TimerReloadValue+1));
|
||||
}
|
||||
|
||||
public void WriteTimerEnable(byte value)
|
||||
{
|
||||
if (TimerEnabled == false && (value & 1) == 1)
|
||||
{
|
||||
TimerValue = TimerReloadValue; // timer value is reset when toggled from off to on
|
||||
TimerTickCounter = 0;
|
||||
}
|
||||
TimerEnabled = (value & 1) == 1;
|
||||
//Console.WriteLine("*** Timer "+(TimerEnabled?"Enabled":"Disabled"));
|
||||
}
|
||||
|
||||
// ==== Flags ====
|
||||
|
||||
/// <summary>Carry Flag</summary>
|
||||
private bool FlagC
|
||||
{
|
||||
get { return (P & 0x01) != 0; }
|
||||
set { P = (byte)((P & ~0x01) | (value ? 0x01 : 0x00)); }
|
||||
}
|
||||
|
||||
/// <summary>Zero Flag</summary>
|
||||
private bool FlagZ
|
||||
{
|
||||
get { return (P & 0x02) != 0; }
|
||||
set { P = (byte)((P & ~0x02) | (value ? 0x02 : 0x00)); }
|
||||
}
|
||||
|
||||
/// <summary>Interrupt Disable Flag</summary>
|
||||
private bool FlagI
|
||||
{
|
||||
get { return (P & 0x04) != 0; }
|
||||
set { P = (byte)((P & ~0x04) | (value ? 0x04 : 0x00)); }
|
||||
}
|
||||
|
||||
/// <summary>Decimal Mode Flag</summary>
|
||||
private bool FlagD
|
||||
{
|
||||
get { return (P & 0x08) != 0; }
|
||||
set { P = (byte)((P & ~0x08) | (value ? 0x08 : 0x00)); }
|
||||
}
|
||||
|
||||
/// <summary>Break Flag</summary>
|
||||
private bool FlagB
|
||||
{
|
||||
get { return (P & 0x10) != 0; }
|
||||
set { P = (byte)((P & ~0x10) | (value ? 0x10 : 0x00)); }
|
||||
}
|
||||
|
||||
/// <summary>T... Flag</summary>
|
||||
private bool FlagT
|
||||
{
|
||||
get { return (P & 0x20) != 0; }
|
||||
set { P = (byte)((P & ~0x20) | (value ? 0x20 : 0x00)); }
|
||||
}
|
||||
|
||||
/// <summary>Overflow Flag</summary>
|
||||
private bool FlagV
|
||||
{
|
||||
get { return (P & 0x40) != 0; }
|
||||
set { P = (byte)((P & ~0x40) | (value ? 0x40 : 0x00)); }
|
||||
}
|
||||
|
||||
/// <summary>Negative Flag</summary>
|
||||
private bool FlagN
|
||||
{
|
||||
get { return (P & 0x80) != 0; }
|
||||
set { P = (byte)((P & ~0x80) | (value ? 0x80 : 0x00)); }
|
||||
}
|
||||
|
||||
// ==== Memory ====
|
||||
|
||||
public Func<ushort, byte> ReadMemory;
|
||||
public Action<ushort, byte> WriteMemory;
|
||||
public Action<int, byte> WriteVDC;
|
||||
|
||||
private ushort ReadWord(ushort address)
|
||||
{
|
||||
byte l = ReadMemory(address);
|
||||
byte h = ReadMemory(++address);
|
||||
return (ushort)((h << 8) | l);
|
||||
}
|
||||
|
||||
private void WriteWord(ushort address, ushort value)
|
||||
{
|
||||
byte l = (byte)(value & 0xFF);
|
||||
byte h = (byte)(value >> 8);
|
||||
WriteMemory(address, l);
|
||||
WriteMemory(++address, h);
|
||||
}
|
||||
|
||||
private ushort ReadWordPageWrap(ushort address)
|
||||
{
|
||||
ushort highAddress = (ushort)((address & 0xFF00) + ((address + 1) & 0xFF));
|
||||
return (ushort)(ReadMemory(address) | (ReadMemory(highAddress) << 8));
|
||||
}
|
||||
|
||||
public string State()
|
||||
{
|
||||
int notused;
|
||||
string a = string.Format("{0:X4} {1:X2} {2} ", PC, ReadMemory(PC), Disassemble(PC, out notused)).PadRight(41);
|
||||
string b = string.Format("A:{0:X2} X:{1:X2} Y:{2:X2} P:{3:X2} SP:{4:X2} Cy:{5}", A, X, Y, P, S, TotalExecutedCycles);
|
||||
string val = a + b + " ";
|
||||
if (FlagN) val = val + "N";
|
||||
if (FlagV) val = val + "V";
|
||||
if (FlagT) val = val + "T";
|
||||
if (FlagB) val = val + "B";
|
||||
if (FlagD) val = val + "D";
|
||||
if (FlagI) val = val + "I";
|
||||
if (FlagZ) val = val + "Z";
|
||||
if (FlagC) val = val + "C";
|
||||
return val;
|
||||
}
|
||||
|
||||
private static readonly byte[] TableNZ =
|
||||
{
|
||||
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace MC68000
|
||||
{
|
||||
public enum ShiftDirection
|
||||
{
|
||||
Right = 0,
|
||||
Left = 1
|
||||
}
|
||||
|
||||
public class Helpers
|
||||
{
|
||||
public static int[,] MOVECyclesBW;
|
||||
public static int[,] MOVECyclesL;
|
||||
|
||||
static Helpers()
|
||||
{
|
||||
MOVECyclesBW = new int[12,9] {
|
||||
{ 4, 4, 8, 8, 8, 12, 14, 12, 16 },
|
||||
{ 4, 4, 8, 8, 8, 12, 14, 12, 16 },
|
||||
{ 8, 8, 12, 12, 12, 16, 18, 16, 20 },
|
||||
{ 8, 8, 12, 12, 12, 16, 18, 16, 20 },
|
||||
{ 10, 10, 14, 14, 14, 18, 20, 18, 22 },
|
||||
{ 12, 12, 16, 16, 16, 20, 22, 20, 24 },
|
||||
{ 14, 14, 18, 18, 18, 22, 24, 22, 26 },
|
||||
{ 12, 12, 16, 16, 16, 20, 22, 20, 24 },
|
||||
{ 16, 16, 20, 20, 20, 24, 26, 24, 28 },
|
||||
{ 12, 12, 16, 16, 16, 20, 22, 20, 24 },
|
||||
{ 14, 14, 18, 18, 18, 22, 24, 22, 26 },
|
||||
{ 8, 8, 12, 12, 12, 16, 18, 16, 20 }
|
||||
};
|
||||
|
||||
MOVECyclesL = new int[12,9] {
|
||||
{ 4, 4, 12, 12, 12, 16, 18, 16, 20 },
|
||||
{ 4, 4, 12, 12, 12, 16, 18, 16, 20 },
|
||||
{ 12, 12, 20, 20, 20, 24, 26, 24, 28 },
|
||||
{ 12, 12, 20, 20, 20, 24, 26, 24, 28 },
|
||||
{ 14, 14, 22, 22, 22, 26, 28, 26, 30 },
|
||||
{ 16, 16, 24, 24, 24, 28, 30, 28, 32 },
|
||||
{ 18, 18, 26, 26, 26, 30, 32, 30, 34 },
|
||||
{ 16, 16, 24, 24, 24, 28, 30, 28, 32 },
|
||||
{ 20, 20, 28, 28, 28, 32, 34, 32, 36 },
|
||||
{ 16, 16, 24, 24, 24, 28, 30, 28, 32 },
|
||||
{ 18, 18, 26, 26, 26, 30, 32, 30, 34 },
|
||||
{ 12, 12, 20, 20, 20, 24, 26, 24, 28 }
|
||||
};
|
||||
}
|
||||
|
||||
#region Inject
|
||||
public static void Inject(ref int register, byte value)
|
||||
{
|
||||
register = (register & -0x100) | value;
|
||||
}
|
||||
|
||||
public static void Inject(ref int register, sbyte value)
|
||||
{
|
||||
register = (register & -0x100) | (byte)value;
|
||||
}
|
||||
|
||||
public static void Inject(ref int register, ushort value)
|
||||
{
|
||||
register = (register & -0x10000) | value;
|
||||
}
|
||||
|
||||
public static void Inject(ref int register, short value)
|
||||
{
|
||||
register = (register & -0x10000) | (ushort)value;
|
||||
}
|
||||
#endregion Inject
|
||||
|
||||
public static void Swap(ref int a, ref int b)
|
||||
{
|
||||
int c = a;
|
||||
a = b;
|
||||
b = c;
|
||||
}
|
||||
|
||||
public static int EACalcTimeBW(int mode, int register)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case 0: return 0;
|
||||
case 1: return 0;
|
||||
case 2: return 4;
|
||||
case 3: return 4;
|
||||
case 4: return 6;
|
||||
case 5: return 8;
|
||||
case 6: return 10;
|
||||
case 7:
|
||||
switch (register)
|
||||
{
|
||||
case 0: return 8;
|
||||
case 1: return 12;
|
||||
case 2: return 8;
|
||||
case 3: return 10;
|
||||
case 4: return 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
throw new ArgumentException();
|
||||
}
|
||||
|
||||
public static int EACalcTimeL(int mode, int register)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case 0: return 0;
|
||||
case 1: return 0;
|
||||
case 2: return 8;
|
||||
case 3: return 8;
|
||||
case 4: return 10;
|
||||
case 5: return 12;
|
||||
case 6: return 14;
|
||||
case 7:
|
||||
switch (register)
|
||||
{
|
||||
case 0: return 12;
|
||||
case 1: return 16;
|
||||
case 2: return 12;
|
||||
case 3: return 14;
|
||||
case 4: return 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
throw new ArgumentException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace MC68000
|
||||
{
|
||||
public interface IMemoryController
|
||||
{
|
||||
sbyte ReadB(int address);
|
||||
short ReadW(int address);
|
||||
int ReadL(int address);
|
||||
|
||||
void WriteB(int address, sbyte value);
|
||||
void WriteW(int address, short value);
|
||||
void WriteL(int address, int value);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,238 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
// BIG NOTICE!!!!!!
|
||||
|
||||
// This is the 68000 core from Sega360.
|
||||
// IT IS GPL! It is a starter core so I can work on Genesis hardware emulation first.
|
||||
// This core MUST BE, and WILL BE replaced with new code at a later date.
|
||||
|
||||
namespace MC68000
|
||||
{
|
||||
public sealed partial class MC68K
|
||||
{
|
||||
public int m_PC;
|
||||
private int m_SR;
|
||||
private ushort m_IR;
|
||||
|
||||
public int[] m_D = new int[8];
|
||||
public int[] m_A = new int[8];
|
||||
|
||||
private int m_Usp;
|
||||
private int m_Ssp;
|
||||
|
||||
private IMemoryController m_Controller;
|
||||
|
||||
public long m_Cycles;
|
||||
|
||||
private int m_InterruptFlag;
|
||||
|
||||
public MC68K(IMemoryController controller)
|
||||
{
|
||||
this.m_Controller = controller;
|
||||
this.BuildOpTable();
|
||||
}
|
||||
|
||||
public void Interrupt(int vector)
|
||||
{
|
||||
this.m_InterruptFlag = vector;
|
||||
}
|
||||
|
||||
private void HandleInterrupt()
|
||||
{
|
||||
int vector = 24 + this.m_InterruptFlag;
|
||||
this.m_InterruptFlag = 0;
|
||||
Trap(vector);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
this.m_SR = 0x2000;
|
||||
this.SP = ReadL(0x0);
|
||||
this.m_PC = ReadL(0x4);
|
||||
this.m_Cycles = 0;
|
||||
}
|
||||
|
||||
private class OpHistory
|
||||
{
|
||||
public int PC;
|
||||
public Operation Operation;
|
||||
public OpHistory(int pc, Operation op)
|
||||
{
|
||||
this.PC = pc;
|
||||
this.Operation = op;
|
||||
}
|
||||
}
|
||||
|
||||
List<OpHistory> m_OpList = new List<OpHistory>(200);
|
||||
public void Execute(long cycles)
|
||||
{
|
||||
this.m_Cycles = 0;
|
||||
do
|
||||
{
|
||||
// if (m_PC == 0x02DD8)
|
||||
//m_PC = 0x02DD8;
|
||||
if (this.m_Cycles >= cycles)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.m_InterruptFlag > 0)
|
||||
{
|
||||
HandleInterrupt();
|
||||
}
|
||||
|
||||
//if (this.m_PC == 0x4752)
|
||||
//{ }
|
||||
|
||||
// Fetch
|
||||
this.m_IR = (ushort)FetchW();
|
||||
|
||||
// Decode
|
||||
Operation op = this.m_OpTable[this.m_IR];
|
||||
|
||||
//if (op == null)
|
||||
//{
|
||||
// ShowOpList();
|
||||
//}
|
||||
|
||||
//if (m_OpList.Count == m_OpList.Capacity)
|
||||
//{
|
||||
// m_OpList.RemoveAt(0);
|
||||
//}
|
||||
//m_OpList.Add(new OpHistory(this.m_PC - 2, op));
|
||||
|
||||
// Execute
|
||||
op();
|
||||
} while (true);
|
||||
}
|
||||
|
||||
public void Step()
|
||||
{
|
||||
if (this.m_InterruptFlag > 0)
|
||||
{
|
||||
HandleInterrupt();
|
||||
}
|
||||
|
||||
this.m_IR = (ushort)FetchW();
|
||||
Operation op = this.m_OpTable[this.m_IR];
|
||||
op();
|
||||
}
|
||||
|
||||
private void ShowOpList()
|
||||
{
|
||||
for (int i = 0; i < m_OpList.Count; i++)
|
||||
{
|
||||
Console.WriteLine(Convert.ToString(m_OpList[i].PC, 16) + "\t" + m_OpList[i].Operation.Method.Name);
|
||||
}
|
||||
}
|
||||
|
||||
public bool C // Carry
|
||||
{
|
||||
get { return (this.m_SR & 1) > 0; }
|
||||
set
|
||||
{
|
||||
if (value) { this.m_SR |= 1; }
|
||||
else { this.m_SR &= -2; }
|
||||
}
|
||||
}
|
||||
|
||||
public bool V // Overflow
|
||||
{
|
||||
get { return (this.m_SR & 2) > 0; }
|
||||
set
|
||||
{
|
||||
if (value) { this.m_SR |= 2; }
|
||||
else { this.m_SR &= -3; }
|
||||
}
|
||||
}
|
||||
|
||||
public bool Z // Zero
|
||||
{
|
||||
get { return (this.m_SR & 4) > 0; }
|
||||
set
|
||||
{
|
||||
if (value) { this.m_SR |= 4; }
|
||||
else { this.m_SR &= -5; }
|
||||
}
|
||||
}
|
||||
|
||||
public bool N // Negative
|
||||
{
|
||||
get { return (this.m_SR & 8) > 0; }
|
||||
set
|
||||
{
|
||||
if (value) { this.m_SR |= 8; }
|
||||
else { this.m_SR &= -9; }
|
||||
}
|
||||
}
|
||||
|
||||
public bool X // Extend
|
||||
{
|
||||
get { return (this.m_SR & 16) > 0; }
|
||||
set
|
||||
{
|
||||
if (value) { this.m_SR |= 16; }
|
||||
else { this.m_SR &= -17; }
|
||||
}
|
||||
}
|
||||
|
||||
public bool S // Supervisor Mode
|
||||
{
|
||||
get { return (this.m_SR & 8192) > 0; }
|
||||
set
|
||||
{
|
||||
if (value) { this.m_SR |= 8192; }
|
||||
else { this.m_SR &= -8193; }
|
||||
}
|
||||
}
|
||||
|
||||
private int SP
|
||||
{
|
||||
get { return this.m_A[7]; }
|
||||
set { this.m_A[7] = value; }
|
||||
}
|
||||
|
||||
private bool TestCondition(int cCode)
|
||||
{
|
||||
switch (cCode)
|
||||
{
|
||||
case 0x0: // T
|
||||
return true;
|
||||
case 0x1: // F
|
||||
return false;
|
||||
case 0x2: // HI
|
||||
return !C && !Z;
|
||||
case 0x3: // LS
|
||||
return C || Z;
|
||||
case 0x4: // CC(HI)
|
||||
return !C;
|
||||
case 0x5: // CS(LO)
|
||||
return C;
|
||||
case 0x6: // NE
|
||||
return !Z;
|
||||
case 0x7: // EQ
|
||||
return Z;
|
||||
case 0x8: // VC
|
||||
return !V;
|
||||
case 0x9: // VS
|
||||
return V;
|
||||
case 0xA: // PL
|
||||
return !N;
|
||||
case 0xB: // MI
|
||||
return N;
|
||||
case 0xC: // GE
|
||||
return N && V || !N && !V;
|
||||
case 0xD: // LT
|
||||
return N && !V || !N && V;
|
||||
case 0xE: // GT
|
||||
return N && V && !Z || !N && !V && !Z;
|
||||
case 0xF: // LE
|
||||
return Z || N && !V || !N && V;
|
||||
default:
|
||||
throw new ArgumentException("Bad condition code");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,819 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace MC68000
|
||||
{
|
||||
public partial class MC68K
|
||||
{
|
||||
#region Read
|
||||
private sbyte ReadB(int address)
|
||||
{
|
||||
return this.m_Controller.ReadB(address &= 0x00FFFFFF);
|
||||
}
|
||||
|
||||
private short ReadW(int address)
|
||||
{
|
||||
return this.m_Controller.ReadW(address &= 0x00FFFFFF);
|
||||
}
|
||||
|
||||
private int ReadL(int address)
|
||||
{
|
||||
return this.m_Controller.ReadL(address &= 0x00FFFFFF);
|
||||
}
|
||||
#endregion Read
|
||||
|
||||
#region Write
|
||||
private void WriteB(int address, sbyte value)
|
||||
{
|
||||
this.m_Controller.WriteB(address &= 0x00FFFFFF, value);
|
||||
}
|
||||
|
||||
private void WriteW(int address, short value)
|
||||
{
|
||||
this.m_Controller.WriteW(address &= 0x00FFFFFF, value);
|
||||
}
|
||||
|
||||
private void WriteL(int address, int value)
|
||||
{
|
||||
this.m_Controller.WriteL(address &= 0x00FFFFFF, value);
|
||||
}
|
||||
#endregion Write
|
||||
|
||||
#region Fetch
|
||||
private sbyte FetchB()
|
||||
{
|
||||
return ReadB(this.m_PC++);
|
||||
}
|
||||
|
||||
private short FetchW()
|
||||
{
|
||||
short data = ReadW(this.m_PC);
|
||||
this.m_PC += 2;
|
||||
return data;
|
||||
}
|
||||
|
||||
private int FetchL()
|
||||
{
|
||||
int data = ReadL(this.m_PC);
|
||||
this.m_PC += 4;
|
||||
return data;
|
||||
}
|
||||
#endregion Fetch
|
||||
|
||||
#region Peek
|
||||
private sbyte PeekB()
|
||||
{
|
||||
return ReadB(this.m_PC);
|
||||
}
|
||||
|
||||
private short PeekW()
|
||||
{
|
||||
return ReadW(this.m_PC);
|
||||
}
|
||||
|
||||
private int PeekL()
|
||||
{
|
||||
return ReadL(this.m_PC);
|
||||
}
|
||||
#endregion Peek
|
||||
|
||||
private int FetchIndex()
|
||||
{
|
||||
short extension = FetchW();
|
||||
int da = (extension >> 15) & 0x1;
|
||||
int reg = (extension >> 12) & 0x7;
|
||||
int wl = (extension >> 11) & 0x1;
|
||||
int scale = (extension >> 9) & 0x3;
|
||||
sbyte displacement = (sbyte)extension;
|
||||
|
||||
int indexReg = (scale == 0) ? 1 : ((scale == 1) ? 2 : ((scale == 2) ? 4 : 8));
|
||||
if (da == 0)
|
||||
{
|
||||
indexReg *= (wl == 0) ? (short)this.m_D[reg] : this.m_D[reg];
|
||||
}
|
||||
else
|
||||
{
|
||||
indexReg *= (wl == 0) ? (short)this.m_A[reg] : this.m_A[reg];
|
||||
}
|
||||
|
||||
return displacement + indexReg;
|
||||
}
|
||||
|
||||
private int PeekIndex()
|
||||
{
|
||||
short extension = PeekW();
|
||||
int da = extension >> 15 & 0x1;
|
||||
int reg = extension >> 12 & 0x7;
|
||||
int wl = extension >> 11 & 0x1;
|
||||
int scale = extension >> 9 & 0x3;
|
||||
sbyte displacement = (sbyte)extension;
|
||||
|
||||
int indexReg = (scale == 0) ? 1 : ((scale == 1) ? 2 : ((scale == 2) ? 4 : 8));
|
||||
if (da == 0)
|
||||
{
|
||||
indexReg *= (wl == 0) ? (short)this.m_D[reg] : this.m_D[reg];
|
||||
}
|
||||
else
|
||||
{
|
||||
indexReg *= (wl == 0) ? (short)this.m_A[reg] : this.m_A[reg];
|
||||
}
|
||||
|
||||
return displacement + indexReg;
|
||||
}
|
||||
|
||||
#region FetchOperand
|
||||
private sbyte FetchOperandB(int mode, int register)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case 0x0: // Dn
|
||||
{
|
||||
return (sbyte)this.m_D[register];
|
||||
}
|
||||
case 0x1: // An
|
||||
{
|
||||
return (sbyte)this.m_A[register];
|
||||
}
|
||||
case 0x2: // (An)
|
||||
{
|
||||
return ReadB(this.m_A[register]);
|
||||
}
|
||||
case 0x3: // (An)+
|
||||
{
|
||||
sbyte operand = ReadB(this.m_A[register]);
|
||||
this.m_A[register] += (register == 7) ? 2 : 1;
|
||||
return operand;
|
||||
}
|
||||
case 0x4: // -(An)
|
||||
{
|
||||
this.m_A[register] -= (register == 7) ? 2 : 1;
|
||||
return ReadB(this.m_A[register]);
|
||||
}
|
||||
case 0x5: //(d16,An)
|
||||
{
|
||||
return ReadB(this.m_A[register] + FetchW());
|
||||
}
|
||||
case 0x6: // (d8,An,Xn)
|
||||
{
|
||||
return this.ReadB(this.m_A[register] + FetchIndex());
|
||||
}
|
||||
case 0x7:
|
||||
switch (register)
|
||||
{
|
||||
case 0x0: // (xxx).W
|
||||
{
|
||||
return ReadB(FetchW());
|
||||
}
|
||||
case 0x1: // (xxx).L
|
||||
{
|
||||
return ReadB(FetchL());
|
||||
}
|
||||
case 0x2: // (d16,PC)
|
||||
{
|
||||
return ReadB(this.m_PC + FetchW());
|
||||
}
|
||||
case 0x3: // (d8,PC,Xn)
|
||||
{
|
||||
sbyte operand = ReadB(this.m_PC + PeekIndex());
|
||||
this.m_PC += 2;
|
||||
return operand;
|
||||
}
|
||||
case 0x4: // #<data>
|
||||
{
|
||||
return (sbyte)FetchW();
|
||||
}
|
||||
default:
|
||||
throw new ArgumentException("Addressing mode doesn't exist!");
|
||||
}
|
||||
default:
|
||||
throw new ArgumentException("Addressing mode doesn't exist!");
|
||||
}
|
||||
}
|
||||
|
||||
private short FetchOperandW(int mode, int register)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case 0x0: // Dn
|
||||
{
|
||||
return (short)this.m_D[register];
|
||||
}
|
||||
case 0x1: // An
|
||||
{
|
||||
return (short)this.m_A[register];
|
||||
}
|
||||
case 0x2: // (An)
|
||||
{
|
||||
return ReadW(this.m_A[register]);
|
||||
}
|
||||
case 0x3: // (An)+
|
||||
{
|
||||
short operand = ReadW(this.m_A[register]);
|
||||
this.m_A[register] += 2;
|
||||
return operand;
|
||||
}
|
||||
case 0x4: // -(An)
|
||||
{
|
||||
this.m_A[register] -= 2;
|
||||
return ReadW(this.m_A[register]);
|
||||
}
|
||||
case 0x5: //(d16,An)
|
||||
{
|
||||
return ReadW(this.m_A[register] + FetchW());
|
||||
}
|
||||
case 0x6: // (d8,An,Xn)
|
||||
{
|
||||
return ReadW(this.m_A[register] + FetchIndex());
|
||||
}
|
||||
case 0x7:
|
||||
switch (register)
|
||||
{
|
||||
case 0x0: // (xxx).W
|
||||
{
|
||||
return ReadW(FetchW());
|
||||
}
|
||||
case 0x1: // (xxx).L
|
||||
{
|
||||
return ReadW(FetchL());
|
||||
}
|
||||
case 0x4: // #<data>
|
||||
{
|
||||
return FetchW();
|
||||
}
|
||||
case 0x2: // (d16,PC)
|
||||
{
|
||||
return ReadW(this.m_PC + FetchW());
|
||||
}
|
||||
case 0x3: // (d8,PC,Xn)
|
||||
{
|
||||
short operand = ReadW(this.m_PC + PeekIndex());
|
||||
this.m_PC += 2;
|
||||
return operand;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentException("Addressing mode doesn't exist!");
|
||||
}
|
||||
default:
|
||||
throw new ArgumentException("Addressing mode doesn't exist!");
|
||||
}
|
||||
}
|
||||
|
||||
private int FetchOperandL(int mode, int register)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case 0x0: // Dn
|
||||
{
|
||||
return this.m_D[register];
|
||||
}
|
||||
case 0x1: // An
|
||||
{
|
||||
return this.m_A[register];
|
||||
}
|
||||
case 0x2: // (An)
|
||||
{
|
||||
return ReadL(this.m_A[register]);
|
||||
}
|
||||
case 0x3: // (An)+
|
||||
{
|
||||
int operand = ReadL(this.m_A[register]);
|
||||
this.m_A[register] += 4;
|
||||
return operand;
|
||||
}
|
||||
case 0x4: // -(An)
|
||||
{
|
||||
this.m_A[register] -= 4;
|
||||
return ReadL(this.m_A[register]);
|
||||
}
|
||||
case 0x5: //(d16,An)
|
||||
{
|
||||
return ReadL(this.m_A[register] + FetchW());
|
||||
}
|
||||
case 0x6: // (d8,An,Xn)
|
||||
{
|
||||
return ReadL(this.m_A[register] + FetchIndex());
|
||||
}
|
||||
case 0x7:
|
||||
switch (register)
|
||||
{
|
||||
case 0x0: // (xxx).W
|
||||
{
|
||||
return ReadL(FetchW());
|
||||
}
|
||||
case 0x1: // (xxx).L
|
||||
{
|
||||
return ReadL(FetchL());
|
||||
}
|
||||
case 0x4: // #<data>
|
||||
{
|
||||
return FetchL();
|
||||
}
|
||||
case 0x2: // (d16,PC)
|
||||
{
|
||||
return ReadL(this.m_PC + FetchW());
|
||||
}
|
||||
case 0x3: // (d8,PC,Xn)
|
||||
{
|
||||
int operand = ReadL(this.m_PC + PeekIndex());
|
||||
this.m_PC += 2;
|
||||
return operand;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentException("Addressing mode doesn't exist!");
|
||||
}
|
||||
default:
|
||||
throw new ArgumentException("Addressing mode doesn't exist!");
|
||||
}
|
||||
}
|
||||
#endregion FetchOperand
|
||||
|
||||
#region FetchAddress
|
||||
private int FetchAddress(int mode, int register)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case 0x0: // Dn
|
||||
{
|
||||
throw new ArgumentException("Invalid mode!");
|
||||
}
|
||||
case 0x1: // An
|
||||
{
|
||||
throw new ArgumentException("Invalid mode!");
|
||||
}
|
||||
case 0x2: // (An)
|
||||
{
|
||||
return this.m_A[register];
|
||||
}
|
||||
case 0x3: // (An)+
|
||||
{
|
||||
return this.m_A[register];
|
||||
}
|
||||
case 0x4: // -(An)
|
||||
{
|
||||
return this.m_A[register];
|
||||
}
|
||||
case 0x5: //(d16,An)
|
||||
{
|
||||
return (this.m_A[register]);
|
||||
}
|
||||
case 0x6: // (d8,An,Xn)
|
||||
{
|
||||
return (this.m_A[register] + FetchIndex());
|
||||
}
|
||||
case 0x7:
|
||||
switch (register)
|
||||
{
|
||||
case 0x0: // (xxx).W
|
||||
{
|
||||
return FetchW();
|
||||
}
|
||||
case 0x1: // (xxx).L
|
||||
{
|
||||
return FetchL();
|
||||
}
|
||||
case 0x4: // #<data>
|
||||
{
|
||||
throw new ArgumentException("Invalid mode!");
|
||||
}
|
||||
case 0x2: // (d16,PC)
|
||||
{
|
||||
return (this.m_PC + FetchW());
|
||||
}
|
||||
case 0x3: // (d8,PC,Xn)
|
||||
{
|
||||
int address = (this.m_PC + PeekIndex());
|
||||
this.m_PC += 2;
|
||||
return address;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentException("Addressing mode doesn't exist!");
|
||||
}
|
||||
default:
|
||||
throw new NotImplementedException("Addressing mode doesn't exist!");
|
||||
}
|
||||
}
|
||||
#endregion FetchAddress
|
||||
|
||||
#region SetOperand
|
||||
private void SetOperandB(int mode, int register, sbyte value)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case 0x0: // Dn
|
||||
{
|
||||
Helpers.Inject(ref this.m_D[register], value);
|
||||
return;
|
||||
}
|
||||
case 0x1: // An
|
||||
{
|
||||
this.m_A[register] = value;
|
||||
return;
|
||||
}
|
||||
case 0x2: // (An)
|
||||
{
|
||||
WriteB(this.m_A[register], value);
|
||||
return;
|
||||
}
|
||||
case 0x3: // (An)+
|
||||
{
|
||||
WriteB(this.m_A[register]++, value);
|
||||
return;
|
||||
}
|
||||
case 0x4: // -(An)
|
||||
{
|
||||
WriteB(--this.m_A[register], value);
|
||||
return;
|
||||
}
|
||||
case 0x5: //(d16,An)
|
||||
{
|
||||
WriteB(this.m_A[register] + FetchW(), value);
|
||||
return;
|
||||
}
|
||||
case 0x6: // (d8,An,Xn)
|
||||
{
|
||||
WriteB(this.m_A[register] + FetchIndex(), value);
|
||||
return;
|
||||
}
|
||||
case 0x7:
|
||||
switch (register)
|
||||
{
|
||||
case 0x0: // (xxx).W
|
||||
{
|
||||
WriteB(FetchW(), value);
|
||||
return;
|
||||
}
|
||||
case 0x1: // (xxx).L
|
||||
{
|
||||
WriteB(FetchL(), value);
|
||||
return;
|
||||
}
|
||||
case 0x4: // #<data>
|
||||
{
|
||||
throw new ArgumentException("Invalid mode!");
|
||||
}
|
||||
case 0x2: // (d16,PC)
|
||||
{
|
||||
WriteB(this.m_PC + FetchW(), value);
|
||||
return;
|
||||
}
|
||||
case 0x3: // (d8,PC,Xn)
|
||||
{
|
||||
WriteB(this.m_PC + PeekIndex(), value);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentException("Addressing mode doesn't exist!");
|
||||
}
|
||||
default:
|
||||
throw new NotImplementedException("Addressing mode doesn't exist!");
|
||||
}
|
||||
}
|
||||
|
||||
private void SetOperandW(int mode, int register, short value)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case 0x0: // Dn
|
||||
{
|
||||
Helpers.Inject(ref this.m_D[register], value);
|
||||
break;
|
||||
}
|
||||
case 0x1: // An
|
||||
{
|
||||
this.m_A[register] = value;
|
||||
return;
|
||||
}
|
||||
case 0x2: // (An)
|
||||
{
|
||||
WriteW(m_A[register], value);
|
||||
return;
|
||||
}
|
||||
case 0x3: // (An)+
|
||||
{
|
||||
WriteW(m_A[register], value);
|
||||
m_A[register] += 2;
|
||||
return;
|
||||
}
|
||||
case 0x4: // -(An)
|
||||
{
|
||||
m_A[register] -= 2;
|
||||
WriteW(m_A[register], value);
|
||||
return;
|
||||
}
|
||||
case 0x5: //(d16,An)
|
||||
{
|
||||
WriteW(this.m_A[register] + FetchW(), value);
|
||||
return;
|
||||
}
|
||||
case 0x6: // (d8,An,Xn)
|
||||
{
|
||||
WriteW(this.m_A[register] + FetchIndex(), value);
|
||||
return;
|
||||
}
|
||||
case 0x7:
|
||||
switch (register)
|
||||
{
|
||||
case 0x0: // (xxx).W
|
||||
{
|
||||
WriteW(FetchW(), value);
|
||||
return;
|
||||
}
|
||||
case 0x1: // (xxx).L
|
||||
{
|
||||
WriteW(FetchL(), value);
|
||||
return;
|
||||
}
|
||||
case 0x4: // #<data>
|
||||
{
|
||||
throw new ArgumentException("Invalid mode!");
|
||||
}
|
||||
case 0x2: // (d16,PC)
|
||||
{
|
||||
WriteW(this.m_PC + FetchW(), value);
|
||||
return;
|
||||
}
|
||||
case 0x3: // (d8,PC,Xn)
|
||||
{
|
||||
WriteW(this.m_PC + PeekIndex(), value);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentException("Addressing mode doesn't exist!");
|
||||
}
|
||||
default:
|
||||
throw new NotImplementedException("Addressing mode doesn't exist!");
|
||||
}
|
||||
}
|
||||
|
||||
private void SetOperandL(int mode, int register, int value)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case 0x0: // Dn
|
||||
{
|
||||
this.m_D[register] = value;
|
||||
return;
|
||||
}
|
||||
case 0x1:
|
||||
{
|
||||
// When setting address registers, need to fill whole byte
|
||||
this.m_A[register] = value;
|
||||
return;
|
||||
}
|
||||
case 0x2: // (An)
|
||||
{
|
||||
WriteL(this.m_A[register], value);
|
||||
return;
|
||||
}
|
||||
case 0x3: // (An)+
|
||||
{
|
||||
WriteL(this.m_A[register], value);
|
||||
this.m_A[register] += 4;
|
||||
return;
|
||||
}
|
||||
case 0x4: // -(An)
|
||||
{
|
||||
this.m_A[register] -= 4;
|
||||
WriteL(this.m_A[register], value);
|
||||
return;
|
||||
}
|
||||
case 0x5: //(d16,An)
|
||||
{
|
||||
WriteL(this.m_A[register] + FetchW(), value);
|
||||
return;
|
||||
}
|
||||
case 0x6: // (d8,An,Xn)
|
||||
{
|
||||
WriteL(this.m_A[register] + FetchIndex(), value);
|
||||
return;
|
||||
}
|
||||
case 0x7:
|
||||
switch (register)
|
||||
{
|
||||
case 0x0: // (xxx).W
|
||||
{
|
||||
WriteL(FetchW(), value);
|
||||
return;
|
||||
}
|
||||
case 0x1: // (xxx).L
|
||||
{
|
||||
WriteL(FetchL(), value);
|
||||
return;
|
||||
}
|
||||
case 0x4: // #<data>
|
||||
{
|
||||
throw new ArgumentException("Invalid mode!");
|
||||
}
|
||||
case 0x2: // (d16,PC)
|
||||
{
|
||||
WriteL(this.m_PC + FetchW(), value);
|
||||
return;
|
||||
}
|
||||
case 0x3: // (d8,PC,Xn)
|
||||
{
|
||||
WriteL(this.m_A[register] + PeekIndex(), value);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentException("Addressing mode doesn't exist!");
|
||||
}
|
||||
default:
|
||||
throw new NotImplementedException("Addressing mode doesn't exist!");
|
||||
}
|
||||
}
|
||||
#endregion SetOperand
|
||||
|
||||
#region PeekOperand
|
||||
private sbyte PeekOperandB(int mode, int register)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case 0x0: // Dn
|
||||
{
|
||||
return (sbyte)this.m_D[register];
|
||||
}
|
||||
case 0x1: // An
|
||||
{
|
||||
return (sbyte)this.m_A[register];
|
||||
}
|
||||
case 0x2: // (An)
|
||||
{
|
||||
return ReadB(this.m_A[register]);
|
||||
}
|
||||
case 0x3: // (An)+
|
||||
{
|
||||
return ReadB(this.m_A[register]);
|
||||
}
|
||||
case 0x4: // -(An)
|
||||
{
|
||||
return ReadB(this.m_A[register]);
|
||||
}
|
||||
case 0x5: //(d16,An)
|
||||
{
|
||||
return ReadB(this.m_A[register] + PeekW());
|
||||
}
|
||||
case 0x6: // (d8,An,Xn)
|
||||
{
|
||||
return this.ReadB(this.m_A[register] + PeekIndex());
|
||||
}
|
||||
case 0x7:
|
||||
switch (register)
|
||||
{
|
||||
case 0x0: // (xxx).W
|
||||
{
|
||||
return ReadB(PeekW());
|
||||
}
|
||||
case 0x1: // (xxx).L
|
||||
{
|
||||
return ReadB(PeekL());
|
||||
}
|
||||
case 0x4: // #<data>
|
||||
{
|
||||
return (sbyte)PeekW();
|
||||
}
|
||||
case 0x2: // (d16,PC)
|
||||
{
|
||||
return ReadB(this.m_PC + PeekW());
|
||||
}
|
||||
case 0x3: // (d8,PC,Xn)
|
||||
{
|
||||
return this.ReadB(this.m_PC + FetchIndex());
|
||||
}
|
||||
default:
|
||||
throw new ArgumentException("Addressing mode doesn't exist!");
|
||||
}
|
||||
default:
|
||||
throw new ArgumentException("Addressing mode doesn't exist!");
|
||||
}
|
||||
}
|
||||
|
||||
private short PeekOperandW(int mode, int register)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case 0x0: // Dn
|
||||
{
|
||||
return (short)this.m_D[register];
|
||||
}
|
||||
case 0x1: // An
|
||||
{
|
||||
return (short)this.m_A[register];
|
||||
}
|
||||
case 0x2: // (An)
|
||||
{
|
||||
return ReadW(this.m_A[register]);
|
||||
}
|
||||
case 0x3: // (An)+
|
||||
{
|
||||
return ReadW(this.m_A[register]);
|
||||
}
|
||||
case 0x4: // -(An)
|
||||
{
|
||||
return ReadW(this.m_A[register]);
|
||||
}
|
||||
case 0x5: //(d16,An)
|
||||
{
|
||||
return ReadW(this.m_A[register] + PeekW());
|
||||
}
|
||||
case 0x6: // (d8,An,Xn)
|
||||
{
|
||||
return ReadW(this.m_A[register] + PeekIndex());
|
||||
}
|
||||
case 0x7:
|
||||
switch (register)
|
||||
{
|
||||
case 0x0: // (xxx).W
|
||||
{
|
||||
return ReadW(PeekW());
|
||||
}
|
||||
case 0x1: // (xxx).L
|
||||
{
|
||||
return ReadW(PeekL());
|
||||
}
|
||||
case 0x4: // #<data>
|
||||
{
|
||||
return PeekW();
|
||||
}
|
||||
case 0x2: // (d16,PC)
|
||||
{
|
||||
return ReadW(this.m_PC + PeekW());
|
||||
}
|
||||
case 0x3: // (d8,PC,Xn)
|
||||
{
|
||||
return ReadW(this.m_PC + PeekIndex());
|
||||
}
|
||||
default:
|
||||
throw new ArgumentException("Addressing mode doesn't exist!");
|
||||
}
|
||||
default:
|
||||
throw new ArgumentException("Addressing mode doesn't exist!");
|
||||
}
|
||||
}
|
||||
|
||||
private int PeekOperandL(int mode, int register)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case 0x0: // Dn
|
||||
{
|
||||
return this.m_D[register];
|
||||
}
|
||||
case 0x1: // An
|
||||
{
|
||||
return this.m_A[register];
|
||||
}
|
||||
case 0x2: // (An)
|
||||
{
|
||||
return ReadL(this.m_A[register]);
|
||||
}
|
||||
case 0x3: // (An)+
|
||||
{
|
||||
return ReadL(this.m_A[register]);
|
||||
}
|
||||
case 0x4: // -(An)
|
||||
{
|
||||
return ReadL(this.m_A[register]);
|
||||
}
|
||||
case 0x5: //(d16,An)
|
||||
{
|
||||
return ReadL(this.m_A[register] + PeekW());
|
||||
}
|
||||
case 0x6: // (d8,An,Xn)
|
||||
{
|
||||
return this.ReadL(this.m_A[register] + FetchIndex());
|
||||
}
|
||||
case 0x7:
|
||||
switch (register)
|
||||
{
|
||||
case 0x0: // (xxx).W
|
||||
{
|
||||
return ReadL(PeekW());
|
||||
}
|
||||
case 0x1: // (xxx).L
|
||||
{
|
||||
return ReadL(PeekL());
|
||||
}
|
||||
case 0x4: // #<data>
|
||||
{
|
||||
return PeekL();
|
||||
}
|
||||
case 0x2: // (d16,PC)
|
||||
{
|
||||
return ReadL(this.m_PC + PeekW());
|
||||
}
|
||||
case 0x3: // (d8,PC,Xn)
|
||||
{
|
||||
return this.ReadB(this.m_PC + FetchIndex());
|
||||
}
|
||||
default:
|
||||
throw new ArgumentException("Addressing mode doesn't exist!");
|
||||
}
|
||||
default:
|
||||
throw new ArgumentException("Addressing mode doesn't exist!");
|
||||
}
|
||||
}
|
||||
#endregion PeekOperand
|
||||
}
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace MC68000
|
||||
{
|
||||
internal delegate void Operation();
|
||||
|
||||
public partial class MC68K
|
||||
{
|
||||
private Operation[] m_OpTable = new Operation[0x10000];
|
||||
|
||||
private void BuildOpTable()
|
||||
{
|
||||
// First, define regular expressions for each operation
|
||||
Dictionary<Operation, Regex> opIndex = new Dictionary<Operation, Regex>();
|
||||
|
||||
// Data Movement
|
||||
opIndex.Add(new Operation(EXG), new Regex("1100" + "[0-1]{3}" + "1" + "(01000|01001|10001)" + "[0-1]{3}"));
|
||||
opIndex.Add(new Operation(LEA), new Regex("0100" + "[0-1]{3}" + "111" + "(((010|101|110)[0-1]{3})|(111(000|001|010|011)))"));
|
||||
opIndex.Add(new Operation(LINK), new Regex("0100111001010" + "[0-1]{3}"));
|
||||
opIndex.Add(new Operation(MOVE), new Regex("00" + "(01|11|10)" + "(([0-1]{3}(000|010|011|100|101|110))|((000|001)111))" + "(((000|001|010|011|100|101|110)[0-1]{3})|(111(000|001|100|010|011)))"));
|
||||
opIndex.Add(new Operation(MOVEA), new Regex("00" + "(11|10)" + "[0-1]{3}" + "001" + "(((000|001|010|011|100|101|110)[0-1]{3})|(111(000|001|100|010|011)))"));
|
||||
opIndex.Add(new Operation(MOVEM_Mem2Reg), new Regex("01001" + "1" + "001" + "[0-1]" + "((010|011|101|110)[0-1]{3}|(111(000|001|010|011)))"));
|
||||
opIndex.Add(new Operation(MOVEM_Reg2Mem), new Regex("01001" + "0" + "001" + "[0-1]" + "((010|100|101|110)[0-1]{3}|(111(000|001)))"));
|
||||
opIndex.Add(new Operation(MOVEP), new Regex("0000" + "[0-1]{3}" + "(100|101|110|111)" + "001" + "[0-1]{3}"));
|
||||
opIndex.Add(new Operation(MOVEQ), new Regex("0111" + "[0-1]{3}" + "0" + "[0-1]{8}"));
|
||||
opIndex.Add(new Operation(PEA), new Regex("0100100001" + "(((010|101|110)[0-1]{3})|(111(000|001|010|011)))"));
|
||||
opIndex.Add(new Operation(UNLK), new Regex("0100111001011" + "[0-1]{3}"));
|
||||
|
||||
// Integer Arithmetic
|
||||
opIndex.Add(new Operation(ADD_Dest), new Regex("1101" + "[0-1]{3}" + "1" + "(00|01|10)" + "(((010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
opIndex.Add(new Operation(ADD_Source), new Regex("1101" + "[0-1]{3}" + "0" + "(00|01|10)" + "(((000|001|010|011|100|101|110)[0-1]{3})|(111(000|001|100|010|011)))"));
|
||||
opIndex.Add(new Operation(ADDA), new Regex("1101" + "[0-1]{3}" + "(011|111)" + "(((000|001|010|011|100|101|110)[0-1]{3})|(111(000|001|100|010|011)))"));
|
||||
opIndex.Add(new Operation(ADDI), new Regex("00000110" + "(00|01|10)" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
opIndex.Add(new Operation(ADDQ), new Regex("0101" + "[0-1]{3}" + "0" + "(00|01|10)" + "(((000|001|010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
opIndex.Add(new Operation(ADDX), new Regex("1101" + "[0-1]{3}" + "1" + "(00|01|10)" + "00" + "[0-1]" + "[0-1]{3}"));
|
||||
opIndex.Add(new Operation(CLR), new Regex("01000010" + "(00|01|10)" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
opIndex.Add(new Operation(CMP), new Regex("1011" + "[0-1]{3}" + "(000|001|010)" + "(((000|001|010|011|100|101|110)[0-1]{3})|(111(000|001|100|010|011)))"));
|
||||
opIndex.Add(new Operation(CMPA), new Regex("1011" + "[0-1]{3}" + "(011|111)" + "(((000|001|010|011|100|101|110)[0-1]{3})|(111(000|001|100|010|011)))"));
|
||||
opIndex.Add(new Operation(CMPI), new Regex("00001100" + "(00|01|10)" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001|010|011)))"));
|
||||
opIndex.Add(new Operation(CMPM), new Regex("1011" + "[0-1]{3}" + "1" + "(00|01|10)" + "001" + "[0-1]{3}"));
|
||||
opIndex.Add(new Operation(DIVS), new Regex("1000" + "[0-1]{3}" + "111" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001|100|010|011)))"));
|
||||
opIndex.Add(new Operation(DIVU), new Regex("1000" + "[0-1]{3}" + "011" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001|100|010|011)))"));
|
||||
opIndex.Add(new Operation(EXT), new Regex("0100100" + "(010|011|111)" + "000" + "[0-1]{3}"));
|
||||
opIndex.Add(new Operation(MULS), new Regex("1100" + "[0-1]{3}" + "111" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001|100|010|011)))"));
|
||||
opIndex.Add(new Operation(MULU), new Regex("1100" + "[0-1]{3}" + "011" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001|100|010|011)))"));
|
||||
opIndex.Add(new Operation(NEG), new Regex("01000100" + "(00|01|10)" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
opIndex.Add(new Operation(NEGX), new Regex("01000000" + "(00|01|10)" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
opIndex.Add(new Operation(SUB_Dest), new Regex("1001" + "[0-1]{3}" + "1" + "(00|01|10)" + "(((010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
opIndex.Add(new Operation(SUB_Source), new Regex("1001" + "[0-1]{3}" + "0" + "(00|01|10)" + "(((000|001|010|011|100|101|110)[0-1]{3})|(111(000|001|100|010|011)))"));
|
||||
opIndex.Add(new Operation(SUBA), new Regex("1001" + "[0-1]{3}" + "(011|111)" + "(((000|001|010|011|100|101|110)[0-1]{3})|(111(000|001|100|010|011)))"));
|
||||
opIndex.Add(new Operation(SUBI), new Regex("00000100" + "(00|01|10)" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
opIndex.Add(new Operation(SUBQ), new Regex("0101" + "[0-1]{3}" + "1" + "(00|01|10)" + "(((000|001|010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
opIndex.Add(new Operation(SUBX), new Regex("1001" + "[0-1]{3}" + "1" + "(00|01|10)" + "00" + "[0-1]" + "[0-1]{3}"));
|
||||
|
||||
// Logical
|
||||
opIndex.Add(new Operation(AND_Dest), new Regex("1100" + "[0-1]{3}" + "1" + "(00|01|10)" + "(((010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
opIndex.Add(new Operation(AND_Source), new Regex("1100" + "[0-1]{3}" + "0" + "(00|01|10)" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001|100|010|011)))"));
|
||||
opIndex.Add(new Operation(ANDI), new Regex("00000010" + "(00|01|10)" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
opIndex.Add(new Operation(EOR), new Regex("1011" + "[0-1]{3}" + "(100|101|110)" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
opIndex.Add(new Operation(EORI), new Regex("00001010" + "(00|01|10)" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
opIndex.Add(new Operation(OR_Dest), new Regex("1000" + "[0-1]{3}" + "1" + "(00|01|10)" + "(((010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
opIndex.Add(new Operation(OR_Source), new Regex("1000" + "[0-1]{3}" + "0" + "(00|01|10)" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001|100|010|011)))"));
|
||||
opIndex.Add(new Operation(ORI), new Regex("00000000" + "(00|01|10)" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
opIndex.Add(new Operation(NOT), new Regex("01000110" + "(00|01|10)" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
|
||||
// Shift and Rotate
|
||||
opIndex.Add(new Operation(ASL), new Regex("1110" + "[0-1]{3}" + "1" + "(00|01|10)" + "[0-1]" + "00" + "[0-1]{3}"));
|
||||
opIndex.Add(new Operation(ASR), new Regex("1110" + "[0-1]{3}" + "0" + "(00|01|10)" + "[0-1]" + "00" + "[0-1]{3}"));
|
||||
opIndex.Add(new Operation(ASL_ASR_Memory), new Regex("1110000" + "[0-1]" + "11" + "(((010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
opIndex.Add(new Operation(LSL), new Regex("1110" + "[0-1]{3}" + "1" + "(00|01|10)" + "[0-1]" + "01" + "[0-1]{3}"));
|
||||
opIndex.Add(new Operation(LSR), new Regex("1110" + "[0-1]{3}" + "0" + "(00|01|10)" + "[0-1]" + "01" + "[0-1]{3}"));
|
||||
opIndex.Add(new Operation(LSL_LSR_Memory), new Regex("1110001" + "[0-1]" + "11" + "(((010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
opIndex.Add(new Operation(ROL), new Regex("1110" + "[0-1]{3}" + "1" + "(00|01|10)" + "[0-1]" + "11" + "[0-1]{3}"));
|
||||
opIndex.Add(new Operation(ROR), new Regex("1110" + "[0-1]{3}" + "0" + "(00|01|10)" + "[0-1]" + "11" + "[0-1]{3}"));
|
||||
opIndex.Add(new Operation(ROL_ROR_Memory), new Regex("1110011" + "[0-1]" + "11" + "(((010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
opIndex.Add(new Operation(ROXL), new Regex("1110" + "[0-1]{3}" + "1" + "(00|01|10)" + "[0-1]" + "10" + "[0-1]{3}"));
|
||||
opIndex.Add(new Operation(ROXR), new Regex("1110" + "[0-1]{3}" + "0" + "(00|01|10)" + "[0-1]" + "10" + "[0-1]{3}"));
|
||||
opIndex.Add(new Operation(ROXL_ROXR_Memory), new Regex("1110010" + "[0-1]" + "11" + "(((010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
opIndex.Add(new Operation(SWAP), new Regex("0100100001000" + "[0-1]{3}"));
|
||||
|
||||
// Bit Manipulation
|
||||
opIndex.Add(new Operation(BTST_Dynamic), new Regex("0000" + "[0-1]{3}" + "100" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001|100|010|011)))"));
|
||||
opIndex.Add(new Operation(BTST_Static), new Regex("0000100000" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001|010|011)))"));
|
||||
|
||||
opIndex.Add(new Operation(BSET_Dynamic), new Regex("0000" + "[0-1]{3}" + "111" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
opIndex.Add(new Operation(BSET_Static), new Regex("0000100011" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
|
||||
opIndex.Add(new Operation(BCLR_Dynamic), new Regex("0000" + "[0-1]{3}" + "110" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
opIndex.Add(new Operation(BCLR_Static), new Regex("0000100010" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
|
||||
opIndex.Add(new Operation(BCHG_Dynamic), new Regex("0000" + "[0-1]{3}" + "101" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
opIndex.Add(new Operation(BCHG_Static), new Regex("0000100001" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
|
||||
// Program Control
|
||||
opIndex.Add(new Operation(Bcc), new Regex("0110" + "(001|010|011|100|101|110|111)[0-1]" + "[0-1]{8}"));
|
||||
opIndex.Add(new Operation(DBcc), new Regex("0101" + "[0-1]{4}" + "11001" + "[0-1]{3}"));
|
||||
opIndex.Add(new Operation(Scc), new Regex("0101" + "[0-1]{4}" + "11" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
|
||||
opIndex.Add(new Operation(BRA), new Regex("01100000" + "[0-1]{8}"));
|
||||
opIndex.Add(new Operation(BSR), new Regex("01100001" + "[0-1]{8}"));
|
||||
opIndex.Add(new Operation(JMP), new Regex("0100111011" + "(((010|101|110)[0-1]{3})|(111(000|001|010|011)))"));
|
||||
opIndex.Add(new Operation(JSR), new Regex("0100111010" + "(((010|101|110)[0-1]{3})|(111(000|001|010|011)))"));
|
||||
opIndex.Add(new Operation(NOP), new Regex("0100111001110001"));
|
||||
|
||||
opIndex.Add(new Operation(RTS), new Regex("0100111001110101"));
|
||||
|
||||
opIndex.Add(new Operation(TST), new Regex("01001010" + "(00|01|10)" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
|
||||
// System Control
|
||||
opIndex.Add(new Operation(ANDI_to_CCR), new Regex("0000001000111100"));
|
||||
opIndex.Add(new Operation(ANDI_to_SR), new Regex("0000001001111100"));
|
||||
opIndex.Add(new Operation(CHK), new Regex("0100" + "[0-1]{3}" + "(11|10)" + "0" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001|100|010|011)))"));
|
||||
opIndex.Add(new Operation(EORI_to_CCR), new Regex("0000101000111100"));
|
||||
opIndex.Add(new Operation(EORI_to_SR), new Regex("0000101001111100"));
|
||||
opIndex.Add(new Operation(ILLEGAL), new Regex("0100101011111100"));
|
||||
opIndex.Add(new Operation(MOVE_from_SR), new Regex("0100000011" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001)))"));
|
||||
opIndex.Add(new Operation(MOVE_to_CCR), new Regex("0100010011" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001|100|010|011)))"));
|
||||
opIndex.Add(new Operation(MOVE_to_SR), new Regex("0100011011" + "(((000|010|011|100|101|110)[0-1]{3})|(111(000|001|100|010|011)))"));
|
||||
opIndex.Add(new Operation(MOVE_USP), new Regex("010011100110" + "[0-1]" + "[0-1]{3}"));
|
||||
opIndex.Add(new Operation(ORI_to_CCR), new Regex("0000000000111100"));
|
||||
opIndex.Add(new Operation(ORI_to_SR), new Regex("0000000001111100"));
|
||||
opIndex.Add(new Operation(RESET), new Regex("0100111001110000"));
|
||||
opIndex.Add(new Operation(RTE), new Regex("0100111001110011"));
|
||||
opIndex.Add(new Operation(RTR), new Regex("0100111001110111"));
|
||||
opIndex.Add(new Operation(STOP), new Regex("0100111001110010"));
|
||||
opIndex.Add(new Operation(TRAP), new Regex("010011100100" + "[0-1]{4}"));
|
||||
opIndex.Add(new Operation(TRAPV), new Regex("0100111001110110"));
|
||||
|
||||
// Now, run through every possible 16-bit binary number,
|
||||
// find the matching expression, and add that code to the table
|
||||
for (int i = 0; i < 0x10000; i++)
|
||||
{
|
||||
string binaryString = Convert.ToString(i, 2);
|
||||
binaryString = binaryString.PadLeft(16, '0');
|
||||
Dictionary<Operation, Regex>.Enumerator enumerator = opIndex.GetEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
if (enumerator.Current.Value.IsMatch(binaryString))
|
||||
{
|
||||
if (this.m_OpTable[i] != null)
|
||||
{
|
||||
throw new Exception("Two operations with clashing codes!");
|
||||
}
|
||||
this.m_OpTable[i] = enumerator.Current.Key;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,207 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace MC68000
|
||||
{
|
||||
public partial class MC68K
|
||||
{
|
||||
private void Btst(int mode, int register, int mask)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case 0: // Destination is data register
|
||||
{
|
||||
int operand = FetchOperandL(mode, register);
|
||||
this.Z = ((operand & mask) == 0);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
{
|
||||
byte operand = (byte)FetchOperandB(mode, register);
|
||||
this.Z = ((operand & mask) == 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void BTST_Dynamic()
|
||||
{
|
||||
int bitNumberRegister = (this.m_IR >> 9) & 0x7;
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
// Need to convert bit number into a mask
|
||||
int bitNumber = this.m_D[bitNumberRegister];
|
||||
bitNumber %= (mode == 0) ? 32 : 8;
|
||||
int mask = 1 << bitNumber;
|
||||
|
||||
Btst(mode, register, mask);
|
||||
this.m_Cycles += (mode == 0) ? 6 : 4 + Helpers.EACalcTimeBW(mode, register);
|
||||
}
|
||||
|
||||
private void BTST_Static()
|
||||
{
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
// Need to convert bit number into a mask
|
||||
int bitNumber = (byte)(FetchW() & 0x00FF);
|
||||
bitNumber %= (mode == 0) ? 32 : 8;
|
||||
int mask = 1 << bitNumber;
|
||||
|
||||
Btst(mode, register, mask);
|
||||
this.m_Cycles += (mode == 0) ? 10 : 8 + Helpers.EACalcTimeBW(mode, register);
|
||||
}
|
||||
|
||||
private void Bset(int mode, int register, int mask)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case 0: // Destination is data register
|
||||
{
|
||||
int operand = PeekOperandL(mode, register);
|
||||
this.Z = ((operand & mask) == 0);
|
||||
SetOperandL(mode, register, (operand | mask));
|
||||
return;
|
||||
}
|
||||
default:
|
||||
{
|
||||
byte operand = (byte)PeekOperandB(mode, register);
|
||||
this.Z = ((operand & mask) == 0);
|
||||
SetOperandB(mode, register, (sbyte)(operand | mask));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void BSET_Dynamic()
|
||||
{
|
||||
int bitNumberRegister = (this.m_IR >> 9) & 0x7;
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
// Need to convert bit number into a mask
|
||||
int bitNumber = this.m_D[bitNumberRegister];
|
||||
bitNumber %= (mode == 0) ? 32 : 8;
|
||||
int mask = 1 << bitNumber;
|
||||
|
||||
Bset(mode, register, mask);
|
||||
this.m_Cycles += (mode == 0) ? 8 : 8 + Helpers.EACalcTimeBW(mode, register);
|
||||
}
|
||||
|
||||
private void BSET_Static()
|
||||
{
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
// Need to convert bit number into a mask
|
||||
int bitNumber = (byte)(FetchW() & 0x00FF);
|
||||
bitNumber %= (mode == 0) ? 32 : 8;
|
||||
int mask = 1 << bitNumber;
|
||||
|
||||
Bset(mode, register, mask);
|
||||
this.m_Cycles += (mode == 0) ? 12 : 12 + Helpers.EACalcTimeBW(mode, register);
|
||||
}
|
||||
|
||||
private void Bclr(int mode, int register, int mask)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case 0: // Destination is data register
|
||||
{
|
||||
int operand = PeekOperandL(mode, register);
|
||||
this.Z = ((operand & mask) > 0);
|
||||
SetOperandL(mode, register, (operand & ~mask));
|
||||
return;
|
||||
}
|
||||
default:
|
||||
{
|
||||
byte operand = (byte)PeekOperandB(mode, register);
|
||||
this.Z = ((operand & mask) > 0);
|
||||
SetOperandB(mode, register, (sbyte)(operand & ~mask));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void BCLR_Dynamic()
|
||||
{
|
||||
int bitNumberRegister = (this.m_IR >> 9) & 0x7;
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
// Need to convert bit number into a mask
|
||||
int bitNumber = this.m_D[bitNumberRegister];
|
||||
bitNumber %= (mode == 0) ? 32 : 8;
|
||||
int mask = 1 << bitNumber;
|
||||
|
||||
Bclr(mode, register, mask);
|
||||
this.m_Cycles += (mode == 0) ? 10 : 8 + Helpers.EACalcTimeBW(mode, register);
|
||||
}
|
||||
|
||||
private void BCLR_Static()
|
||||
{
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
// Need to convert bit number into a mask
|
||||
int bitNumber = (byte)(FetchW() & 0x00FF);
|
||||
bitNumber %= (mode == 0) ? 32 : 8;
|
||||
int mask = 1 << bitNumber;
|
||||
|
||||
Bclr(mode, register, mask);
|
||||
this.m_Cycles += (mode == 0) ? 14 : 12 + Helpers.EACalcTimeBW(mode, register);
|
||||
}
|
||||
|
||||
private void Bchg(int mode, int register, int mask)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case 0: // Destination is data register
|
||||
{
|
||||
int operand = PeekOperandL(mode, register);
|
||||
this.Z = ((operand & mask) > 0);
|
||||
SetOperandL(mode, register, (operand ^ mask));
|
||||
return;
|
||||
}
|
||||
default:
|
||||
{
|
||||
byte operand = (byte)PeekOperandB(mode, register);
|
||||
this.Z = ((operand & mask) > 0);
|
||||
SetOperandB(mode, register, (sbyte)(operand ^ mask));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void BCHG_Dynamic()
|
||||
{
|
||||
int bitNumberRegister = (this.m_IR >> 9) & 0x7;
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
// Need to convert bit number into a mask
|
||||
int bitNumber = this.m_D[bitNumberRegister];
|
||||
bitNumber %= (mode == 0) ? 32 : 8;
|
||||
int mask = 1 << bitNumber;
|
||||
|
||||
Bchg(mode, register, mask);
|
||||
this.m_Cycles += (mode == 0) ? 8 : 8 + Helpers.EACalcTimeBW(mode, register);
|
||||
}
|
||||
|
||||
private void BCHG_Static()
|
||||
{
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
// Need to convert bit number into a mask
|
||||
int bitNumber = (byte)(FetchW() & 0x00FF);
|
||||
bitNumber %= (mode == 0) ? 32 : 8;
|
||||
int mask = 1 << bitNumber;
|
||||
|
||||
Bchg(mode, register, mask);
|
||||
this.m_Cycles += (mode == 0) ? 12 : 12 + Helpers.EACalcTimeBW(mode, register);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,396 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace MC68000
|
||||
{
|
||||
public partial class MC68K
|
||||
{
|
||||
private void EXG() // Exchange registers
|
||||
{
|
||||
this.m_Cycles += 6;
|
||||
|
||||
switch ((this.m_IR >> 3) & 0x31)
|
||||
{
|
||||
case 8:
|
||||
Helpers.Swap(ref this.m_D[(this.m_IR >> 9) & 0x7], ref this.m_D[this.m_IR & 0x7]);
|
||||
return;
|
||||
|
||||
case 9:
|
||||
Helpers.Swap(ref this.m_A[(this.m_IR >> 9) & 0x7], ref this.m_A[this.m_IR & 0x7]);
|
||||
return;
|
||||
|
||||
case 17:
|
||||
Helpers.Swap(ref this.m_D[(this.m_IR >> 9) & 0x7], ref this.m_A[this.m_IR & 0x7]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void LEA() // Load effective address
|
||||
{
|
||||
this.m_A[(this.m_IR >> 9) & 0x7] =
|
||||
FetchAddress((this.m_IR >> 3) & 0x7, this.m_IR & 0x7);
|
||||
|
||||
switch ((this.m_IR >> 3) & 0x7)
|
||||
{
|
||||
case 0x2: this.m_Cycles += 4; break;
|
||||
case 0x5: this.m_Cycles += 8; break;
|
||||
case 0x6: this.m_Cycles += 12; break;
|
||||
case 0x7:
|
||||
switch (this.m_IR & 0x7)
|
||||
{
|
||||
case 0x0: this.m_Cycles += 8; break;
|
||||
case 0x1: this.m_Cycles += 12; break;
|
||||
case 0x2: this.m_Cycles += 8; break;
|
||||
case 0x3: this.m_Cycles += 12; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void LINK()
|
||||
{
|
||||
this.SP -= 4;
|
||||
WriteL(this.SP, this.m_A[this.m_IR & 0x7]);
|
||||
this.m_A[this.m_IR & 0x7] = this.SP;
|
||||
this.SP += FetchW();
|
||||
|
||||
this.m_Cycles += 16;
|
||||
}
|
||||
|
||||
private void MOVE() // Move data from source to destination
|
||||
{
|
||||
int src_mode = (this.m_IR >> 3) & 0x7;
|
||||
int src_reg = this.m_IR & 0x7;
|
||||
int dest_mode = (this.m_IR >> 6) & 0x7;
|
||||
int dest_reg = (this.m_IR >> 9) & 0x7;
|
||||
|
||||
int operand = 0;
|
||||
switch ((this.m_IR >> 12) & 0x3)
|
||||
{
|
||||
case 1: // B
|
||||
operand = FetchOperandB(src_mode, src_reg);
|
||||
SetOperandB(dest_mode, dest_reg, (sbyte)operand);
|
||||
this.m_Cycles += Helpers.MOVECyclesBW[src_mode + ((src_mode == 7) ? src_reg : 0),
|
||||
dest_mode + ((dest_mode == 7) ? dest_reg : 0)];
|
||||
break;
|
||||
case 3: // W
|
||||
operand = FetchOperandW(src_mode, src_reg);
|
||||
SetOperandW(dest_mode, dest_reg, (short)operand);
|
||||
this.m_Cycles += Helpers.MOVECyclesBW[src_mode + ((src_mode == 7) ? src_reg : 0),
|
||||
dest_mode + ((dest_mode == 7) ? dest_reg : 0)];
|
||||
break;
|
||||
case 2: // L
|
||||
operand = FetchOperandL(src_mode, src_reg);
|
||||
SetOperandL(dest_mode, dest_reg, operand);
|
||||
this.m_Cycles += Helpers.MOVECyclesL[src_mode + ((src_mode == 7) ? src_reg : 0),
|
||||
dest_mode + ((dest_mode == 7) ? dest_reg : 0)];
|
||||
break;
|
||||
}
|
||||
this.V = this.C = false;
|
||||
this.N = (operand < 0);
|
||||
this.Z = (operand == 0);
|
||||
}
|
||||
|
||||
private void MOVEA() // Move Address
|
||||
{
|
||||
// W
|
||||
if ((this.m_IR >> 12 & 0x3) == 3)
|
||||
{
|
||||
this.m_A[this.m_IR >> 9 & 0x7] =
|
||||
FetchOperandW(this.m_IR >> 3 & 0x7, this.m_IR & 0x7);
|
||||
// TODO Need to check these clock cycles
|
||||
this.m_Cycles += Helpers.MOVECyclesBW[(this.m_IR >> 3 & 0x7) + (((this.m_IR >> 3 & 0x7) == 7) ? (this.m_IR & 0x7) : 0), 1];
|
||||
}
|
||||
// L
|
||||
else
|
||||
{
|
||||
this.m_A[this.m_IR >> 9 & 0x7] =
|
||||
FetchOperandL(this.m_IR >> 3 & 0x7, this.m_IR & 0x7);
|
||||
// TODO Need to check these clock cycles
|
||||
this.m_Cycles += Helpers.MOVECyclesL[(this.m_IR >> 3 & 0x7) + (((this.m_IR >> 3 & 0x7) == 7) ? (this.m_IR & 0x7) : 0), 1];
|
||||
}
|
||||
}
|
||||
|
||||
private void MOVEM_Mem2Reg()
|
||||
{
|
||||
int size = (this.m_IR >> 6) & 0x1;
|
||||
int src_mode = (this.m_IR >> 3) & 0x7;
|
||||
int src_register = this.m_IR & 0x7;
|
||||
|
||||
ushort regMap = (ushort)FetchW();
|
||||
int count = 0;
|
||||
|
||||
int address = FetchAddress(src_mode, src_register);
|
||||
switch (size)
|
||||
{
|
||||
case 0: // W
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if ((regMap & 0x1) > 0)
|
||||
{
|
||||
this.m_D[i] = ReadW(address);
|
||||
address += 2;
|
||||
count++;
|
||||
}
|
||||
regMap = (ushort)(regMap >> 1);
|
||||
}
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if ((regMap & 0x1) > 0)
|
||||
{
|
||||
this.m_A[i] = ReadW(address);
|
||||
address += 2;
|
||||
count++;
|
||||
}
|
||||
regMap = (ushort)(regMap >> 1);
|
||||
}
|
||||
if (src_mode == 3) // Postincrement mode
|
||||
{
|
||||
this.m_A[src_register] = address;
|
||||
}
|
||||
|
||||
this.m_Cycles += count * 4;
|
||||
break;
|
||||
}
|
||||
case 1: // L
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if ((regMap & 0x1) > 0)
|
||||
{
|
||||
this.m_D[i] = (int)ReadL(address);
|
||||
address += 4;
|
||||
count++;
|
||||
}
|
||||
regMap = (ushort)(regMap >> 1);
|
||||
}
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if ((regMap & 0x1) > 0)
|
||||
{
|
||||
this.m_A[i] = (int)ReadL(address);
|
||||
address += 4;
|
||||
count++;
|
||||
}
|
||||
regMap = (ushort)(regMap >> 1);
|
||||
}
|
||||
if (src_mode == 3) // Postincrement mode
|
||||
{
|
||||
this.m_A[src_register] = address;
|
||||
}
|
||||
|
||||
this.m_Cycles += count * 8;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (src_mode)
|
||||
{
|
||||
case 0x2: this.m_Cycles += 12; break;
|
||||
case 0x3: this.m_Cycles += 12; break;
|
||||
case 0x5: this.m_Cycles += 16; break;
|
||||
case 0x6: this.m_Cycles += 18; break;
|
||||
case 0x7:
|
||||
switch (src_register)
|
||||
{
|
||||
case 0x0: this.m_Cycles += 16; break;
|
||||
case 0x1: this.m_Cycles += 20; break;
|
||||
case 0x2: this.m_Cycles += 16; break;
|
||||
case 0x3: this.m_Cycles += 18; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void MOVEM_Reg2Mem()
|
||||
{
|
||||
int size = (this.m_IR >> 6) & 0x1;
|
||||
int src_mode = (this.m_IR >> 3) & 0x7;
|
||||
int src_register = this.m_IR & 0x7;
|
||||
|
||||
ushort regMap = (ushort)FetchW();
|
||||
|
||||
int count = 0;
|
||||
int address = FetchAddress(src_mode, src_register);
|
||||
switch (size)
|
||||
{
|
||||
case 0: // W
|
||||
{
|
||||
if (src_mode == 4) // Pre-decrement mode
|
||||
{
|
||||
for (int i = 7; i >= 0; i--)
|
||||
{
|
||||
if ((regMap & 0x1) > 0)
|
||||
{
|
||||
address -= 2;
|
||||
WriteW(address, (sbyte)this.m_A[i]);
|
||||
count++;
|
||||
}
|
||||
regMap = (ushort)(regMap >> 1);
|
||||
}
|
||||
for (int i = 7; i >= 0; i--)
|
||||
{
|
||||
if ((regMap & 0x1) > 0)
|
||||
{
|
||||
address -= 2;
|
||||
WriteW(address, (sbyte)this.m_D[i]);
|
||||
count++;
|
||||
}
|
||||
regMap = (ushort)(regMap >> 1);
|
||||
}
|
||||
this.m_A[src_register] = address;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if ((regMap & 0x1) > 0)
|
||||
{
|
||||
WriteW(address, (sbyte)this.m_D[i]);
|
||||
address += 2;
|
||||
count++;
|
||||
}
|
||||
regMap = (ushort)(regMap >> 1);
|
||||
}
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if ((regMap & 0x1) > 0)
|
||||
{
|
||||
WriteW(address, (sbyte)this.m_A[i]);
|
||||
address += 2;
|
||||
count++;
|
||||
}
|
||||
regMap = (ushort)(regMap >> 1);
|
||||
}
|
||||
}
|
||||
this.m_Cycles += 4 * count;
|
||||
break;
|
||||
}
|
||||
case 1: // L
|
||||
{
|
||||
if (src_mode == 4) // Pre-decrement mode
|
||||
{
|
||||
for (int i = 7; i >= 0; i--)
|
||||
{
|
||||
if ((regMap & 0x1) > 0)
|
||||
{
|
||||
address -= 4;
|
||||
WriteL(address, this.m_A[i]);
|
||||
count++;
|
||||
}
|
||||
regMap = (ushort)(regMap >> 1);
|
||||
}
|
||||
for (int i = 7; i >= 0; i--)
|
||||
{
|
||||
if ((regMap & 0x1) > 0)
|
||||
{
|
||||
address -= 4;
|
||||
WriteL(address, this.m_D[i]);
|
||||
count++;
|
||||
}
|
||||
regMap = (ushort)(regMap >> 1);
|
||||
}
|
||||
this.m_A[src_register] = address;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if ((regMap & 0x1) > 0)
|
||||
{
|
||||
WriteL(address, this.m_D[i]);
|
||||
address += 4;
|
||||
count++;
|
||||
}
|
||||
regMap = (ushort)(regMap >> 1);
|
||||
}
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if ((regMap & 0x1) > 0)
|
||||
{
|
||||
WriteL(address, this.m_A[i]);
|
||||
address += 4;
|
||||
count++;
|
||||
}
|
||||
regMap = (ushort)(regMap >> 1);
|
||||
}
|
||||
}
|
||||
this.m_Cycles += 8 * count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (src_mode)
|
||||
{
|
||||
case 0x2: this.m_Cycles += 8; break;
|
||||
case 0x4: this.m_Cycles += 8; break;
|
||||
case 0x5: this.m_Cycles += 12; break;
|
||||
case 0x6: this.m_Cycles += 14; break;
|
||||
case 0x7:
|
||||
switch (src_register)
|
||||
{
|
||||
case 0x0: this.m_Cycles += 12; break;
|
||||
case 0x1: this.m_Cycles += 16; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void MOVEP()
|
||||
{
|
||||
int dataregister = (this.m_IR >> 9) & 0x7;
|
||||
int opmode = (this.m_IR >> 6) & 0x7;
|
||||
int addressregister = this.m_IR & 0x7;
|
||||
short displacement = FetchW();
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private void MOVEQ() // Move quick
|
||||
{
|
||||
// Data byte is sign-extended to 32 bits
|
||||
int data = (sbyte)this.m_IR;
|
||||
|
||||
this.N = (data < 0);
|
||||
this.Z = (data == 0);
|
||||
this.V = this.C = false;
|
||||
|
||||
this.m_D[(this.m_IR >> 9) & 0x7] = data;
|
||||
|
||||
this.m_Cycles += 4;
|
||||
}
|
||||
|
||||
private void PEA() // Push effective address
|
||||
{
|
||||
this.SP -= 4;
|
||||
WriteL(this.SP, FetchAddress((this.m_IR >> 3) & 0x7, this.m_IR & 0x7));
|
||||
|
||||
switch ((this.m_IR >> 3) & 0x7)
|
||||
{
|
||||
case 0x2: this.m_Cycles += 12; break;
|
||||
case 0x5: this.m_Cycles += 16; break;
|
||||
case 0x6: this.m_Cycles += 20; break;
|
||||
case 0x7:
|
||||
switch (this.m_IR & 0x7)
|
||||
{
|
||||
case 0x0: this.m_Cycles += 16; break;
|
||||
case 0x1: this.m_Cycles += 20; break;
|
||||
case 0x2: this.m_Cycles += 16; break;
|
||||
case 0x3: this.m_Cycles += 20; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void UNLK()
|
||||
{
|
||||
this.SP = this.m_A[this.m_IR & 0x7];
|
||||
this.m_A[this.m_IR & 0x7] = ReadL(this.SP);
|
||||
this.SP += 4;
|
||||
|
||||
this.m_Cycles += 12;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,869 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace MC68000
|
||||
{
|
||||
public partial class MC68K
|
||||
{
|
||||
#region Add Helper Functions
|
||||
private sbyte Add(sbyte a, sbyte b, bool updateConditions, bool useX)
|
||||
{
|
||||
int result = useX ? (int)a + (int)b + (this.X ? 1 : 0) : (int)a + (int)b;
|
||||
|
||||
if (updateConditions)
|
||||
{
|
||||
this.C = this.X = (result & 0x100) > 0;
|
||||
this.V = result > sbyte.MaxValue || result < sbyte.MinValue;
|
||||
this.N = result < 0;
|
||||
if (!useX) { this.Z = result == 0; }
|
||||
}
|
||||
|
||||
return (sbyte)result;
|
||||
}
|
||||
|
||||
private short Add(short a, short b, bool updateConditions, bool useX)
|
||||
{
|
||||
int result = useX ? (int)a + (int)b + (this.X ? 1 : 0) : (int)a + (int)b;
|
||||
|
||||
if (updateConditions)
|
||||
{
|
||||
this.C = this.X = (result & 0x10000) > 0;
|
||||
this.V = result > short.MaxValue || result < short.MinValue;
|
||||
this.N = result < 0;
|
||||
if (!useX) { this.Z = result == 0; }
|
||||
}
|
||||
|
||||
return (short)result;
|
||||
}
|
||||
|
||||
private int Add(int a, int b, bool updateConditions, bool useX)
|
||||
{
|
||||
long result = useX ? (long)a + (long)b + (this.X ? 1 : 0) : (long)a + (long)b;
|
||||
|
||||
if (updateConditions)
|
||||
{
|
||||
this.C = this.X = (result & 0x100000000) > 0;
|
||||
this.V = result > int.MaxValue || result < int.MinValue;
|
||||
this.N = result < 0;
|
||||
if (!useX) { this.Z = result == 0; }
|
||||
}
|
||||
|
||||
return (int)result;
|
||||
}
|
||||
#endregion Add Helper Functions
|
||||
|
||||
#region Sub Helper Functions
|
||||
private sbyte Sub(sbyte a, sbyte b, bool updateConditions, bool setX, bool useX)
|
||||
{
|
||||
int result = useX ? (int)b - (int)a - (this.X ? 1 : 0) : (int)b - (int)a;
|
||||
|
||||
if (updateConditions)
|
||||
{
|
||||
this.C = (result & 0x100) > 0;
|
||||
this.V = result > sbyte.MaxValue || result < sbyte.MinValue;
|
||||
this.N = result < 0;
|
||||
if (!useX) { this.Z = result == 0; }
|
||||
if (setX) { this.X = this.C; }
|
||||
}
|
||||
|
||||
return (sbyte)result;
|
||||
}
|
||||
|
||||
private short Sub(short a, short b, bool updateConditions, bool setX, bool useX)
|
||||
{
|
||||
int result = useX ? (int)b - (int)a - (this.X ? 1 : 0) : (int)b - (int)a;
|
||||
|
||||
if (updateConditions)
|
||||
{
|
||||
this.C = (result & 0x10000) > 0;
|
||||
this.V = result > short.MaxValue || result < short.MinValue;
|
||||
this.N = result < 0;
|
||||
if (!useX) { this.Z = result == 0; }
|
||||
if (setX) { this.X = this.C; }
|
||||
}
|
||||
|
||||
return (short)result;
|
||||
}
|
||||
|
||||
private int Sub(int a, int b, bool updateConditions, bool setX, bool useX)
|
||||
{
|
||||
long result = useX ? (long)b - (long)a - (this.X ? 1 : 0) : (long)b - (long)a;
|
||||
|
||||
if (updateConditions)
|
||||
{
|
||||
this.C = (result & 0x100000000) > 0;
|
||||
this.V = result > int.MaxValue || result < int.MinValue;
|
||||
this.N = result < 0;
|
||||
if (!useX) { this.Z = result == 0; }
|
||||
if (setX) { this.X = this.C; }
|
||||
}
|
||||
|
||||
return (int)result;
|
||||
}
|
||||
#endregion Sub Helper Functions
|
||||
|
||||
private void ADD_Dest()
|
||||
{
|
||||
int src_register = (this.m_IR >> 9) & 0x7;
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
sbyte result = Add((sbyte)this.m_D[src_register], PeekOperandB(mode, register), true, false);
|
||||
SetOperandB(mode, register, result);
|
||||
this.m_Cycles += 8 + Helpers.EACalcTimeBW(mode, register);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
short result = Add((short)this.m_D[src_register], PeekOperandW(mode, register), true, false);
|
||||
SetOperandW(mode, register, result);
|
||||
this.m_Cycles += 8 + Helpers.EACalcTimeBW(mode, register);
|
||||
return;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
int result = Add(this.m_D[src_register], PeekOperandL(mode, register), true, false);
|
||||
SetOperandL(mode, register, result);
|
||||
this.m_Cycles += 12 + Helpers.EACalcTimeL(mode, register);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ADD_Source()
|
||||
{
|
||||
int dest_register = (this.m_IR >> 9) & 0x7;
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int src_mode = (this.m_IR >> 3) & 0x7;
|
||||
int src_register = this.m_IR & 0x7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
sbyte result = Add((sbyte)this.m_D[dest_register], FetchOperandB(src_mode, src_register), true, false);
|
||||
Helpers.Inject(ref this.m_D[dest_register], result);
|
||||
this.m_Cycles += 4 + Helpers.EACalcTimeBW(src_mode, src_register);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
short result = Add((short)this.m_D[dest_register], FetchOperandW(src_mode, src_register), true, false);
|
||||
Helpers.Inject(ref this.m_D[dest_register], result);
|
||||
this.m_Cycles += 4 + Helpers.EACalcTimeBW(src_mode, src_register);
|
||||
return;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
int result = Add(this.m_D[dest_register], FetchOperandL(src_mode, src_register), true, false);
|
||||
this.m_D[dest_register] = result;
|
||||
this.m_Cycles += 6 + Helpers.EACalcTimeL(src_mode, src_register);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ADDA()
|
||||
{
|
||||
int register = (this.m_IR >> 9) & 0x7;
|
||||
int opmode = (this.m_IR >> 6) & 0x7;
|
||||
int src_mode = (this.m_IR >> 3) & 0x7;
|
||||
int src_register = this.m_IR & 0x7;
|
||||
|
||||
switch (opmode)
|
||||
{
|
||||
case 3: // W
|
||||
this.m_A[register] += FetchOperandW(src_mode, src_register);
|
||||
this.m_Cycles += 8 + Helpers.EACalcTimeBW(src_mode, src_register);
|
||||
return;
|
||||
case 7: // L
|
||||
this.m_A[register] += FetchOperandL(src_mode, src_register);
|
||||
this.m_Cycles += 6 + Helpers.EACalcTimeBW(src_mode, src_register);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void ADDI()
|
||||
{
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // B
|
||||
{
|
||||
sbyte result = Add((sbyte)FetchW(), PeekOperandB(mode, register), true, false);
|
||||
SetOperandB(mode, register, result);
|
||||
this.m_Cycles += (mode == 0) ? 8 : 12 + Helpers.EACalcTimeBW(mode, register);
|
||||
return;
|
||||
}
|
||||
case 1: // W
|
||||
{
|
||||
short result = Add(FetchW(), PeekOperandW(mode, register), true, false);
|
||||
SetOperandW(mode, register, result);
|
||||
this.m_Cycles += (mode == 0) ? 8 : 12 + Helpers.EACalcTimeBW(mode, register);
|
||||
return;
|
||||
}
|
||||
case 2: // L
|
||||
{
|
||||
int result = Add(FetchL(), PeekOperandL(mode, register), true, false);
|
||||
SetOperandL(mode, register, result);
|
||||
this.m_Cycles += (mode == 0) ? 16 : 20 + Helpers.EACalcTimeL(mode, register);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ADDQ() // Add Quick
|
||||
{
|
||||
int data = (this.m_IR >> 9) & 0x7;
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
// With data, 0 means 8
|
||||
data = (data == 0) ? 8 : data;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // B
|
||||
{
|
||||
if (mode == 1)
|
||||
{
|
||||
throw new ArgumentException("Byte operation not allowed on address registers");
|
||||
}
|
||||
sbyte result = Add(PeekOperandB(mode, register), (sbyte)data, (mode != 1), false);
|
||||
SetOperandB(mode, register, result);
|
||||
switch (mode) {
|
||||
case 0: this.m_Cycles += 4; break;
|
||||
case 1: this.m_Cycles += 4; break;
|
||||
default: this.m_Cycles += 8 + Helpers.EACalcTimeBW(mode, register); break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
case 1: // W
|
||||
{
|
||||
if (mode == 1)
|
||||
{
|
||||
int result = Add(PeekOperandL(mode, register), data, false, false);
|
||||
SetOperandL(mode, register, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
short result = Add(PeekOperandW(mode, register), (short)data, true, false);
|
||||
SetOperandW(mode, register, result);
|
||||
}
|
||||
switch (mode)
|
||||
{
|
||||
case 0: this.m_Cycles += 4; break;
|
||||
case 1: this.m_Cycles += 4; break;
|
||||
default: this.m_Cycles += 8 + Helpers.EACalcTimeBW(mode, register); break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
case 2: // L
|
||||
{
|
||||
int result = Add(PeekOperandL(mode, register), data, (mode != 1), false);
|
||||
SetOperandL(mode, register, result);
|
||||
switch (mode)
|
||||
{
|
||||
case 0: this.m_Cycles += 8; break;
|
||||
case 1: this.m_Cycles += 8; break;
|
||||
default: this.m_Cycles += 12 + Helpers.EACalcTimeL(mode, register); break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ADDX()
|
||||
{
|
||||
int regRx = (this.m_IR >> 9) & 0x7;
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int rm = (this.m_IR >> 3) & 0x1;
|
||||
int regRy = this.m_IR & 0x7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
if (rm == 0)
|
||||
{
|
||||
this.m_D[regRy] = Add((sbyte)this.m_D[regRx], (sbyte)this.m_D[regRy], true, true);
|
||||
this.m_Cycles += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteB(this.m_A[regRy], Add(ReadB(this.m_A[regRx]), ReadB(this.m_A[regRy]), true, true));
|
||||
this.m_Cycles += 18;
|
||||
}
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
if (rm == 0)
|
||||
{
|
||||
this.m_D[regRy] = Add((short)this.m_D[regRx], (short)this.m_D[regRy], true, true);
|
||||
this.m_Cycles += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteW(this.m_A[regRy], Add(ReadW(this.m_A[regRx]), ReadW(this.m_A[regRy]), true, true));
|
||||
this.m_Cycles += 18;
|
||||
}
|
||||
return;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
if (rm == 0)
|
||||
{
|
||||
this.m_D[regRy] = Add(this.m_D[regRx], this.m_D[regRy], true, true);
|
||||
this.m_Cycles += 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteL(this.m_A[regRy], Add(ReadB(this.m_A[regRx]), ReadB(this.m_A[regRy]), true, true));
|
||||
this.m_Cycles += 30;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CLR() // Clear an operand
|
||||
{
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
this.C = this.N = this.V = false;
|
||||
this.Z = true;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // B
|
||||
SetOperandB(mode, register, 0);
|
||||
this.m_Cycles += (mode == 0) ? 4 : 8 + Helpers.EACalcTimeBW(mode, register);
|
||||
return;
|
||||
case 1: // W
|
||||
SetOperandW(mode, register, 0);
|
||||
this.m_Cycles += (mode == 0) ? 4 : 8 + Helpers.EACalcTimeBW(mode, register);
|
||||
return;
|
||||
case 2: // L
|
||||
SetOperandL(mode, register, 0);
|
||||
this.m_Cycles += (mode == 0) ? 6 : 12 + Helpers.EACalcTimeL(mode, register);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void CMP() // Compare
|
||||
{
|
||||
int dest_register = (this.m_IR >> 9) & 0x7;
|
||||
int opmode = (this.m_IR >> 6) & 0x7;
|
||||
int src_mode = (this.m_IR >> 3) & 0x7;
|
||||
int src_register = this.m_IR & 0x7;
|
||||
|
||||
switch (opmode)
|
||||
{
|
||||
case 0: // B
|
||||
Sub(FetchOperandB(src_mode, src_register), (sbyte)this.m_D[dest_register], true, false, false);
|
||||
this.m_Cycles += 4 + Helpers.EACalcTimeBW(src_mode, src_register);
|
||||
return;
|
||||
case 1: // W
|
||||
Sub(FetchOperandW(src_mode, src_register), (short)this.m_D[dest_register], true, false, false);
|
||||
this.m_Cycles += 4 + Helpers.EACalcTimeBW(src_mode, src_register);
|
||||
return;
|
||||
case 2: // L
|
||||
Sub(FetchOperandL(src_mode, src_register), this.m_D[dest_register], true, false, false);
|
||||
this.m_Cycles += 6 + Helpers.EACalcTimeL(src_mode, src_register);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void CMPA()
|
||||
{
|
||||
int dest_register = (this.m_IR >> 9) & 0x7;
|
||||
int opmode = (this.m_IR >> 6) & 0x7;
|
||||
int src_mode = (this.m_IR >> 3) & 0x7;
|
||||
int src_register = this.m_IR & 0x7;
|
||||
|
||||
switch (opmode)
|
||||
{
|
||||
case 3: // W
|
||||
Sub((int)FetchOperandW(src_mode, src_register), this.m_A[dest_register], true, false, false);
|
||||
this.m_Cycles += 6 + Helpers.EACalcTimeBW(src_mode, src_register);
|
||||
return;
|
||||
case 7: // L
|
||||
Sub(FetchOperandL(src_mode, src_register), this.m_A[dest_register], true, false, false);
|
||||
this.m_Cycles += 6 + Helpers.EACalcTimeL(src_mode, src_register);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void CMPI()
|
||||
{
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // B
|
||||
Sub((sbyte)FetchW(), FetchOperandB(mode, register), true, false, false);
|
||||
this.m_Cycles += (mode == 0) ? 8 : 8 + Helpers.EACalcTimeBW(mode, register);
|
||||
return;
|
||||
case 1: // W
|
||||
Sub((short)FetchW(), FetchOperandW(mode, register), true, false, false);
|
||||
this.m_Cycles += (mode == 0) ? 8 : 8 + Helpers.EACalcTimeBW(mode, register);
|
||||
return;
|
||||
case 2: // L
|
||||
Sub(FetchL(), FetchOperandL(mode, register), true, false, false);
|
||||
this.m_Cycles += (mode == 0) ? 14 : 12 + Helpers.EACalcTimeL(mode, register);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void CMPM()
|
||||
{
|
||||
int registerAx = (this.m_IR >> 9) & 0x7;
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int registerAy = this.m_IR & 0x7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // B
|
||||
Sub((sbyte)this.m_A[registerAy], (sbyte)this.m_A[registerAy], true, false, false);
|
||||
this.m_Cycles += 12;
|
||||
return;
|
||||
case 1: // W
|
||||
Sub((short)this.m_A[registerAy], (short)this.m_A[registerAy], true, false, false);
|
||||
this.m_Cycles += 12;
|
||||
return;
|
||||
case 2: // L
|
||||
Sub(this.m_A[registerAy], this.m_A[registerAy], true, false, false);
|
||||
this.m_Cycles += 20;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void DIVS() // Unsigned multiply
|
||||
{
|
||||
int dest_register = (this.m_IR >> 9) & 0x7;
|
||||
int src_mode = (this.m_IR >> 3) & 0x7;
|
||||
int src_register = this.m_IR & 0x7;
|
||||
|
||||
// On 68000, only allowable size is Word
|
||||
int source = (short)FetchOperandW(src_mode, src_register);
|
||||
int dest = this.m_D[dest_register];
|
||||
|
||||
this.C = false;
|
||||
if (source == 0)
|
||||
{
|
||||
throw new ArgumentException("Divide by zero...");
|
||||
}
|
||||
|
||||
int quotient = dest / source;
|
||||
int remainder = dest % source;
|
||||
|
||||
// Detect overflow
|
||||
if (quotient < short.MinValue || quotient > short.MaxValue)
|
||||
{
|
||||
this.V = true;
|
||||
throw new ArgumentException("Division overflow");
|
||||
}
|
||||
this.m_D[dest_register] = (remainder << 16) | quotient;
|
||||
|
||||
this.m_Cycles += 158 + Helpers.EACalcTimeBW(src_mode, src_register);
|
||||
|
||||
this.N = quotient < 0;
|
||||
this.Z = quotient == 0;
|
||||
this.V = false;
|
||||
}
|
||||
|
||||
private void DIVU() // Unsigned multiply
|
||||
{
|
||||
int dest_register = (this.m_IR >> 9) & 0x7;
|
||||
int src_mode = (this.m_IR >> 3) & 0x7;
|
||||
int src_register = this.m_IR & 0x7;
|
||||
|
||||
// On 68000, only allowable size is Word
|
||||
uint source = (uint)FetchOperandW(src_mode, src_register);
|
||||
uint dest = (uint)this.m_D[dest_register];
|
||||
|
||||
this.C = false;
|
||||
if (source == 0)
|
||||
{
|
||||
throw new ArgumentException("Divide by zero...");
|
||||
}
|
||||
|
||||
uint quotient = dest / source;
|
||||
uint remainder = dest % source;
|
||||
|
||||
// Detect overflow
|
||||
if (quotient < ushort.MinValue || quotient > ushort.MaxValue ||
|
||||
remainder < ushort.MinValue || remainder > ushort.MaxValue)
|
||||
{
|
||||
this.V = true;
|
||||
throw new ArgumentException("Division overflow");
|
||||
}
|
||||
this.m_D[dest_register] = (int)((remainder << 16) | quotient);
|
||||
|
||||
this.m_Cycles += 140 + Helpers.EACalcTimeBW(src_mode, src_register);
|
||||
|
||||
this.N = quotient < 0;
|
||||
this.Z = quotient == 0;
|
||||
this.V = false;
|
||||
}
|
||||
|
||||
private void EXT() // Sign extend
|
||||
{
|
||||
this.m_Cycles += 4;
|
||||
|
||||
switch ((this.m_IR >> 6) & 0x7)
|
||||
{
|
||||
case 2: // Byte to word
|
||||
Helpers.Inject(ref this.m_D[this.m_IR & 0x7], (short)((sbyte)this.m_D[this.m_IR & 0x7]));
|
||||
break;
|
||||
case 3: // Word to long
|
||||
this.m_D[this.m_IR & 0x7] = (short)this.m_D[this.m_IR & 0x7];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void MULS() // Unsigned multiply
|
||||
{
|
||||
int dest_register = (this.m_IR >> 9) & 0x7;
|
||||
int src_mode = (this.m_IR >> 3) & 0x7;
|
||||
int src_register = this.m_IR & 0x7;
|
||||
|
||||
// On 68000, only allowable size is Word
|
||||
short operand = FetchOperandW(src_mode, src_register);
|
||||
short currentValue = (short)this.m_D[dest_register];
|
||||
|
||||
int newValue = operand * currentValue;
|
||||
this.m_D[dest_register] = newValue;
|
||||
|
||||
this.m_Cycles += 70 + Helpers.EACalcTimeBW(src_mode, src_register);
|
||||
|
||||
this.N = newValue < 0;
|
||||
this.Z = newValue == 0;
|
||||
this.V = false; // Can't get an overflow
|
||||
this.C = false;
|
||||
}
|
||||
|
||||
private void MULU() // Unsigned multiply
|
||||
{
|
||||
int dest_register = (this.m_IR >> 9) & 0x7;
|
||||
int src_mode = (this.m_IR >> 3) & 0x7;
|
||||
int src_register = this.m_IR & 0x7;
|
||||
|
||||
// On 68000, only allowable size is Word
|
||||
ushort operand = (ushort)FetchOperandW(src_mode, src_register);
|
||||
ushort currentValue = (ushort)this.m_D[dest_register];
|
||||
|
||||
uint newValue = (uint)(operand * currentValue);
|
||||
this.m_D[dest_register] = (int)newValue;
|
||||
|
||||
this.m_Cycles += 70 + Helpers.EACalcTimeBW(src_mode, src_register);
|
||||
|
||||
this.N = (int)newValue < 0;
|
||||
this.Z = (newValue == 0);
|
||||
this.V = false; // Can't get an overflow
|
||||
this.C = false;
|
||||
}
|
||||
|
||||
private void NEG()
|
||||
{
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
SetOperandB(mode, register, Sub(PeekOperandB(mode, register), (sbyte)0, true, true, false));
|
||||
this.m_Cycles += (mode == 0) ? 4 : 8 + Helpers.EACalcTimeBW(mode, register);
|
||||
return;
|
||||
case 1:
|
||||
SetOperandW(mode, register, Sub(PeekOperandW(mode, register), (short)0, true, true, false));
|
||||
this.m_Cycles += (mode == 0) ? 4 : 8 + Helpers.EACalcTimeBW(mode, register);
|
||||
return;
|
||||
case 2:
|
||||
SetOperandL(mode, register, Sub(PeekOperandL(mode, register), (int)0, true, true, false));
|
||||
this.m_Cycles += (mode == 0) ? 6 : 12 + Helpers.EACalcTimeL(mode, register);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void NEGX()
|
||||
{
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
SetOperandB(mode, register, Sub(PeekOperandB(mode, register), (sbyte)0, true, true, false));
|
||||
this.m_Cycles += (mode == 0) ? 4 : 8 + Helpers.EACalcTimeBW(mode, register);
|
||||
return;
|
||||
case 1:
|
||||
SetOperandW(mode, register, Sub(PeekOperandW(mode, register), (short)0, true, true, false));
|
||||
this.m_Cycles += (mode == 0) ? 4 : 8 + Helpers.EACalcTimeBW(mode, register);
|
||||
return;
|
||||
case 2:
|
||||
SetOperandL(mode, register, Sub(PeekOperandL(mode, register), (int)0, true, true, false));
|
||||
this.m_Cycles += (mode == 0) ? 6 : 12 + Helpers.EACalcTimeL(mode, register);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void SUB_Dest()
|
||||
{
|
||||
int src_register = (this.m_IR >> 9) & 0x7;
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
sbyte result = Sub((sbyte)this.m_D[src_register], PeekOperandB(mode, register), true, true, false);
|
||||
SetOperandB(mode, register, result);
|
||||
this.m_Cycles += 8 + Helpers.EACalcTimeBW(mode, src_register);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
short result = Sub((short)this.m_D[src_register], PeekOperandW(mode, register), true, true, false);
|
||||
SetOperandW(mode, register, result);
|
||||
this.m_Cycles += 8 + Helpers.EACalcTimeBW(mode, src_register);
|
||||
return;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
int result = Sub(this.m_D[src_register], PeekOperandL(mode, register), true, true, false);
|
||||
SetOperandL(mode, register, result);
|
||||
this.m_Cycles += 12 + Helpers.EACalcTimeL(mode, src_register);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SUB_Source()
|
||||
{
|
||||
int dest_register = (this.m_IR >> 9) & 0x7;
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int src_mode = (this.m_IR >> 3) & 0x7;
|
||||
int src_register = this.m_IR & 0x7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
sbyte result = Sub(FetchOperandB(src_mode, src_register), (sbyte)this.m_D[dest_register], true, true, false);
|
||||
Helpers.Inject(ref this.m_D[dest_register], result);
|
||||
this.m_Cycles += 4 + Helpers.EACalcTimeBW(src_mode, src_register);
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
short result = Sub(FetchOperandW(src_mode, src_register), (short)this.m_D[dest_register], true, true, false);
|
||||
Helpers.Inject(ref this.m_D[dest_register], result);
|
||||
this.m_Cycles += 4 + Helpers.EACalcTimeBW(src_mode, src_register);
|
||||
return;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
int result = Sub(FetchOperandL(src_mode, src_register), this.m_D[dest_register], true, true, false);
|
||||
this.m_D[dest_register] = (int)result;
|
||||
this.m_Cycles += 6 + Helpers.EACalcTimeL(src_mode, src_register);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SUBA()
|
||||
{
|
||||
int dest_register = (this.m_IR >> 9) & 0x7;
|
||||
int opmode = (this.m_IR >> 6) & 0x7;
|
||||
int src_mode = (this.m_IR >> 3) & 0x7;
|
||||
int src_register = this.m_IR & 0x7;
|
||||
|
||||
switch (opmode)
|
||||
{
|
||||
case 3: // W
|
||||
{
|
||||
int operand = FetchOperandW(src_mode, src_register); // Sign-extended
|
||||
this.m_A[dest_register] -= operand;
|
||||
this.m_Cycles += 8 + Helpers.EACalcTimeBW(src_mode, src_register);
|
||||
break;
|
||||
}
|
||||
case 7: // L
|
||||
{
|
||||
int operand = FetchOperandL(src_mode, src_register);
|
||||
this.m_A[dest_register] -= operand;
|
||||
this.m_Cycles += 6 + Helpers.EACalcTimeL(src_mode, src_register);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SUBI()
|
||||
{
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // B
|
||||
{
|
||||
sbyte result = Sub((sbyte)FetchW(), PeekOperandB(mode, register), true, true, false);
|
||||
SetOperandB(mode, register, result);
|
||||
this.m_Cycles += (mode == 0) ? 8 : 12 + Helpers.EACalcTimeBW(mode, register);
|
||||
return;
|
||||
}
|
||||
case 1: // W
|
||||
{
|
||||
short result = Sub(FetchW(), PeekOperandW(mode, register), true, true, false);
|
||||
SetOperandW(mode, register, result);
|
||||
this.m_Cycles += (mode == 0) ? 8 : 12 + Helpers.EACalcTimeBW(mode, register);
|
||||
return;
|
||||
}
|
||||
case 2: // L
|
||||
{
|
||||
int result = Sub(FetchL(), PeekOperandL(mode, register), true, true, false);
|
||||
SetOperandL(mode, register, result);
|
||||
this.m_Cycles += (mode == 0) ? 16 : 20 + Helpers.EACalcTimeL(mode, register);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SUBQ() // Add Quick
|
||||
{
|
||||
int data = (this.m_IR >> 9) & 0x7;
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
// With data, 0 means 8
|
||||
data = (data == 0) ? 8 : data;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // B
|
||||
{
|
||||
if (mode == 1)
|
||||
{
|
||||
throw new ArgumentException("Byte operation not allowed on address registers");
|
||||
}
|
||||
sbyte result = Sub((sbyte)data, PeekOperandB(mode, register), (mode != 1), true, false);
|
||||
SetOperandB(mode, register, result);
|
||||
switch (mode)
|
||||
{
|
||||
case 0: this.m_Cycles += 4; break;
|
||||
case 1: this.m_Cycles += 8; break;
|
||||
default: this.m_Cycles += 8 + Helpers.EACalcTimeBW(mode, register); break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
case 1: // W
|
||||
{
|
||||
if (mode == 1)
|
||||
{
|
||||
int result = Sub(data, PeekOperandL(mode, register), false, true, false);
|
||||
SetOperandL(mode, register, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
short result = Sub((short)data, PeekOperandW(mode, register), true, true, false);
|
||||
SetOperandW(mode, register, result);
|
||||
}
|
||||
switch (mode)
|
||||
{
|
||||
case 0: this.m_Cycles += 4; break;
|
||||
case 1: this.m_Cycles += 8; break;
|
||||
default: this.m_Cycles += 8 + Helpers.EACalcTimeBW(mode, register); break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
case 2: // L
|
||||
{
|
||||
int result = Sub(data, PeekOperandL(mode, register), (mode != 1), true, false);
|
||||
SetOperandL(mode, register, result);
|
||||
switch (mode)
|
||||
{
|
||||
case 0: this.m_Cycles += 8; break;
|
||||
case 1: this.m_Cycles += 8; break;
|
||||
default: this.m_Cycles += 12 + Helpers.EACalcTimeL(mode, register); break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SUBX()
|
||||
{
|
||||
int regRx = (this.m_IR >> 9) & 0x7;
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int rm = (this.m_IR >> 3) & 0x1;
|
||||
int regRy = this.m_IR & 0x7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
if (rm == 0)
|
||||
{
|
||||
this.m_D[regRy] = Sub((sbyte)this.m_D[regRx], (sbyte)this.m_D[regRy], true, true, true);
|
||||
this.m_Cycles += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteB(this.m_A[regRy], Sub(ReadB(this.m_A[regRx]), ReadB(this.m_A[regRy]), true, true, true));
|
||||
this.m_Cycles += 18;
|
||||
}
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
if (rm == 0)
|
||||
{
|
||||
this.m_D[regRy] = Sub((short)this.m_D[regRx], (short)this.m_D[regRy], true, true, true);
|
||||
this.m_Cycles += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteW(this.m_A[regRy], Sub(ReadW(this.m_A[regRx]), ReadW(this.m_A[regRy]), true, true, true));
|
||||
this.m_Cycles += 18;
|
||||
}
|
||||
return;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
if (rm == 0)
|
||||
{
|
||||
this.m_D[regRy] = Sub(this.m_D[regRx], this.m_D[regRy], true, true, true);
|
||||
this.m_Cycles += 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteL(this.m_A[regRy], Sub(ReadB(this.m_A[regRx]), ReadB(this.m_A[regRy]), true, true, true));
|
||||
this.m_Cycles += 30;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,405 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace MC68000
|
||||
{
|
||||
public partial class MC68K
|
||||
{
|
||||
#region AND Helpers
|
||||
private sbyte And(sbyte a, sbyte b)
|
||||
{
|
||||
sbyte result = (sbyte)(a & b);
|
||||
|
||||
this.V = this.C = false;
|
||||
this.N = result < 0;
|
||||
this.Z = result == 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private short And(short a, short b)
|
||||
{
|
||||
short result = (short)(a & b);
|
||||
|
||||
this.V = this.C = false;
|
||||
this.N = result < 0;
|
||||
this.Z = result == 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private int And(int a, int b)
|
||||
{
|
||||
int result = (int)(a & b);
|
||||
|
||||
this.V = this.C = false;
|
||||
this.N = result < 0;
|
||||
this.Z = result == 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
#endregion AND Helpers
|
||||
|
||||
private void AND_Dest()
|
||||
{
|
||||
int src_register = (this.m_IR >> 9) & 0x7;
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int dest_mode = (this.m_IR >> 3) & 0x7;
|
||||
int dest_register = this.m_IR & 0x7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // B
|
||||
{
|
||||
sbyte result = And((sbyte)this.m_D[src_register], PeekOperandB(dest_mode, dest_register));
|
||||
SetOperandB(dest_mode, dest_register, result);
|
||||
this.m_Cycles += 4 + Helpers.EACalcTimeBW(dest_mode, dest_register);
|
||||
return;
|
||||
}
|
||||
case 1: // W
|
||||
{
|
||||
short result = And((short)this.m_D[src_register], PeekOperandW(dest_mode, dest_register));
|
||||
SetOperandW(dest_mode, dest_register, result);
|
||||
this.m_Cycles += 4 + Helpers.EACalcTimeBW(dest_mode, dest_register);
|
||||
return;
|
||||
}
|
||||
case 2: // L
|
||||
{
|
||||
int result = And(this.m_D[src_register], PeekOperandL(dest_mode, dest_register));
|
||||
SetOperandL(dest_mode, dest_register, result);
|
||||
this.m_Cycles += 6 + Helpers.EACalcTimeL(dest_mode, dest_register);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AND_Source()
|
||||
{
|
||||
int dest_register = (this.m_IR >> 9) & 0x7;
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int src_mode = (this.m_IR >> 3) & 0x7;
|
||||
int src_register = this.m_IR & 0x7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // B
|
||||
{
|
||||
sbyte result = And((sbyte)this.m_D[dest_register], FetchOperandB(src_mode, src_register));
|
||||
Helpers.Inject(ref this.m_D[dest_register], result);
|
||||
this.m_Cycles += 8 + Helpers.EACalcTimeBW(src_mode, src_register);
|
||||
return;
|
||||
}
|
||||
case 1: // W
|
||||
{
|
||||
short result = And((short)this.m_D[dest_register], FetchOperandW(src_mode, src_register));
|
||||
Helpers.Inject(ref this.m_D[dest_register], result);
|
||||
this.m_Cycles += 8 + Helpers.EACalcTimeBW(src_mode, src_register);
|
||||
return;
|
||||
}
|
||||
case 2: // L
|
||||
{
|
||||
int result = And(this.m_D[dest_register], FetchOperandL(src_mode, src_register));
|
||||
this.m_D[dest_register] = result;
|
||||
this.m_Cycles += 12 + Helpers.EACalcTimeL(src_mode, src_register);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ANDI()
|
||||
{
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // B
|
||||
{
|
||||
sbyte result = And((sbyte)FetchW(), PeekOperandB(mode, register));
|
||||
SetOperandB(mode, register, result);
|
||||
this.m_Cycles += (mode == 0) ? 8 : 12 + Helpers.EACalcTimeBW(mode, register);
|
||||
return;
|
||||
}
|
||||
case 1: // W
|
||||
{
|
||||
short result = And(FetchW(), PeekOperandW(mode, register));
|
||||
SetOperandW(mode, register, result);
|
||||
this.m_Cycles += (mode == 0) ? 8 : 12 + Helpers.EACalcTimeBW(mode, register);
|
||||
return;
|
||||
}
|
||||
case 2: // L
|
||||
{
|
||||
int result = And(FetchL(), PeekOperandL(mode, register));
|
||||
SetOperandL(mode, register, result);
|
||||
this.m_Cycles += (mode == 0) ? 14 : 20 + Helpers.EACalcTimeL(mode, register);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region EOR Helpers
|
||||
private sbyte Eor(sbyte a, sbyte b)
|
||||
{
|
||||
sbyte result = (sbyte)(a ^ b);
|
||||
|
||||
this.V = this.C = false;
|
||||
this.N = result < 0;
|
||||
this.Z = result == 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private short Eor(short a, short b)
|
||||
{
|
||||
short result = (short)(a ^ b);
|
||||
|
||||
this.V = this.C = false;
|
||||
this.N = result < 0;
|
||||
this.Z = result == 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private int Eor(int a, int b)
|
||||
{
|
||||
int result = (int)(a ^ b);
|
||||
|
||||
this.V = this.C = false;
|
||||
this.N = result < 0;
|
||||
this.Z = result == 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
#endregion EOR Helpers
|
||||
|
||||
private void EOR()
|
||||
{
|
||||
int src_register = (this.m_IR >> 9) & 0x7;
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int dest_mode = (this.m_IR >> 3) & 0x7;
|
||||
int dest_register = this.m_IR & 0x7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // B
|
||||
{
|
||||
sbyte result = Eor((sbyte)this.m_D[src_register], PeekOperandB(dest_mode, dest_register));
|
||||
SetOperandB(dest_mode, dest_register, result);
|
||||
this.m_Cycles += (dest_mode == 0) ? 4 : 8 + Helpers.EACalcTimeBW(dest_mode, dest_register);
|
||||
return;
|
||||
}
|
||||
case 1: // W
|
||||
{
|
||||
short result = Eor((short)this.m_D[src_register], PeekOperandW(dest_mode, dest_register));
|
||||
SetOperandW(dest_mode, dest_register, result);
|
||||
this.m_Cycles += (dest_mode == 0) ? 4 : 8 + Helpers.EACalcTimeBW(dest_mode, dest_register);
|
||||
return;
|
||||
}
|
||||
case 2: // L
|
||||
{
|
||||
int result = Eor(this.m_D[src_register], PeekOperandL(dest_mode, dest_register));
|
||||
SetOperandL(dest_mode, dest_register, result);
|
||||
this.m_Cycles += (dest_mode == 0) ? 8 : 12 + Helpers.EACalcTimeL(dest_mode, dest_register);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EORI()
|
||||
{
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // B
|
||||
{
|
||||
sbyte result = Eor((sbyte)FetchW(), PeekOperandB(mode, register));
|
||||
SetOperandB(mode, register, result);
|
||||
this.m_Cycles += (mode == 0) ? 8 : 12 + Helpers.EACalcTimeBW(mode, register);
|
||||
return;
|
||||
}
|
||||
case 1: // W
|
||||
{
|
||||
short result = Eor(FetchW(), PeekOperandW(mode, register));
|
||||
SetOperandW(mode, register, result);
|
||||
this.m_Cycles += (mode == 0) ? 8 : 12 + Helpers.EACalcTimeBW(mode, register);
|
||||
return;
|
||||
}
|
||||
case 2: // L
|
||||
{
|
||||
int result = Eor(FetchL(), PeekOperandL(mode, register));
|
||||
SetOperandL(mode, register, result);
|
||||
this.m_Cycles += (mode == 0) ? 16 : 20 + Helpers.EACalcTimeL(mode, register);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region OR Helpers
|
||||
private sbyte Or(sbyte a, sbyte b)
|
||||
{
|
||||
sbyte result = (sbyte)(a | b);
|
||||
|
||||
this.V = this.C = false;
|
||||
this.N = result < 0;
|
||||
this.Z = result == 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private short Or(short a, short b)
|
||||
{
|
||||
short result = (short)(a | b);
|
||||
|
||||
this.V = this.C = false;
|
||||
this.N = result < 0;
|
||||
this.Z = result == 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private int Or(int a, int b)
|
||||
{
|
||||
int result = (int)(a | b);
|
||||
|
||||
this.V = this.C = false;
|
||||
this.N = result < 0;
|
||||
this.Z = result == 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
#endregion OR Helpers
|
||||
|
||||
private void OR_Dest()
|
||||
{
|
||||
int src_register = (this.m_IR >> 9) & 0x7;
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int dest_mode = (this.m_IR >> 3) & 0x7;
|
||||
int dest_register = this.m_IR & 0x7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // B
|
||||
{
|
||||
sbyte result = Or((sbyte)this.m_D[src_register], PeekOperandB(dest_mode, dest_register));
|
||||
SetOperandB(dest_mode, dest_register, result);
|
||||
this.m_Cycles += 8 + Helpers.EACalcTimeBW(dest_mode, dest_register);
|
||||
return;
|
||||
}
|
||||
case 1: // W
|
||||
{
|
||||
short result = Or((short)this.m_D[src_register], PeekOperandW(dest_mode, dest_register));
|
||||
SetOperandW(dest_mode, dest_register, result);
|
||||
this.m_Cycles += 8 + Helpers.EACalcTimeBW(dest_mode, dest_register);
|
||||
return;
|
||||
}
|
||||
case 2: // L
|
||||
{
|
||||
int result = Or(this.m_D[src_register], PeekOperandL(dest_mode, dest_register));
|
||||
SetOperandL(dest_mode, dest_register, result);
|
||||
this.m_Cycles += 12 + Helpers.EACalcTimeL(dest_mode, dest_register);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OR_Source()
|
||||
{
|
||||
int dest_register = (this.m_IR >> 9) & 0x7;
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int src_mode = (this.m_IR >> 3) & 0x7;
|
||||
int src_register = this.m_IR & 0x7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // B
|
||||
{
|
||||
sbyte result = Or((sbyte)this.m_D[dest_register], FetchOperandB(src_mode, src_register));
|
||||
Helpers.Inject(ref this.m_D[dest_register], result);
|
||||
this.m_Cycles += 4 + Helpers.EACalcTimeBW(src_mode, src_register);
|
||||
return;
|
||||
}
|
||||
case 1: // W
|
||||
{
|
||||
short result = Or((short)this.m_D[dest_register], FetchOperandW(src_mode, src_register));
|
||||
Helpers.Inject(ref this.m_D[dest_register], result);
|
||||
this.m_Cycles += 4 + Helpers.EACalcTimeBW(src_mode, src_register);
|
||||
return;
|
||||
}
|
||||
case 2: // L
|
||||
{
|
||||
int result = Or(this.m_D[dest_register], FetchOperandL(src_mode, src_register));
|
||||
this.m_D[dest_register] = result;
|
||||
this.m_Cycles += 6 + Helpers.EACalcTimeL(src_mode, src_register);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ORI()
|
||||
{
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: // B
|
||||
{
|
||||
sbyte result = Or((sbyte)FetchW(), PeekOperandB(mode, register));
|
||||
SetOperandB(mode, register, result);
|
||||
this.m_Cycles += (mode == 0) ? 8 : 12 + Helpers.EACalcTimeBW(mode, register);
|
||||
return;
|
||||
}
|
||||
case 1: // W
|
||||
{
|
||||
short result = Or(FetchW(), PeekOperandW(mode, register));
|
||||
SetOperandW(mode, register, result);
|
||||
this.m_Cycles += (mode == 0) ? 8 : 12 + Helpers.EACalcTimeBW(mode, register);
|
||||
return;
|
||||
}
|
||||
case 2: // L
|
||||
{
|
||||
int result = Or(FetchL(), PeekOperandL(mode, register));
|
||||
SetOperandL(mode, register, result);
|
||||
this.m_Cycles += (mode == 0) ? 16 : 20 + Helpers.EACalcTimeL(mode, register);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void NOT()
|
||||
{
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
long result = 0;
|
||||
this.V = this.C = false;
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
result = ~PeekOperandB(mode, register);
|
||||
SetOperandB(mode, register, (sbyte)result);
|
||||
this.m_Cycles += (mode == 0) ? 4 : 8 + Helpers.EACalcTimeBW(mode, register);
|
||||
break;
|
||||
case 1:
|
||||
result = ~PeekOperandW(mode, register);
|
||||
SetOperandW(mode, register, (short)result);
|
||||
this.m_Cycles += (mode == 0) ? 4 : 8 + Helpers.EACalcTimeBW(mode, register);
|
||||
break;
|
||||
case 2:
|
||||
result = ~PeekOperandL(mode, register);
|
||||
SetOperandL(mode, register, (int)result);
|
||||
this.m_Cycles += (mode == 0) ? 6 : 12 + Helpers.EACalcTimeL(mode, register);
|
||||
break;
|
||||
}
|
||||
this.N = result < 0;
|
||||
this.Z = result == 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace MC68000
|
||||
{
|
||||
public partial class MC68K
|
||||
{
|
||||
private void TAS() // Test and set
|
||||
{
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
sbyte operand = PeekOperandB(mode, register);
|
||||
|
||||
this.N = (operand < 0);
|
||||
this.Z = (operand == 0);
|
||||
this.V = false;
|
||||
this.C = false;
|
||||
|
||||
this.m_Cycles += (mode == 0) ? 4 : 14 + Helpers.EACalcTimeBW(mode, register);
|
||||
|
||||
// Set the 7th bit
|
||||
byte uOperand = (byte)operand;
|
||||
uOperand |= 0x80;
|
||||
SetOperandB(mode, register, (sbyte)uOperand);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,208 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace MC68000
|
||||
{
|
||||
public partial class MC68K
|
||||
{
|
||||
private void Bcc() // Branch conditionally
|
||||
{
|
||||
if ((sbyte)this.m_IR == 0)
|
||||
{
|
||||
if (TestCondition((this.m_IR >> 8) & 0xF))
|
||||
{
|
||||
this.m_PC += FetchW();
|
||||
this.m_Cycles += 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.m_PC += 2;
|
||||
this.m_Cycles += 12;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TestCondition((this.m_IR >> 8) & 0xF))
|
||||
{
|
||||
this.m_PC += (sbyte)this.m_IR;
|
||||
this.m_Cycles += 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.m_Cycles += 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DBcc() // Test condition, decrement, branch
|
||||
{
|
||||
if (TestCondition((this.m_IR >> 8) & 0xF))
|
||||
{
|
||||
// Need to move PC on...
|
||||
this.m_PC += 2;
|
||||
this.m_Cycles += 12;
|
||||
}
|
||||
else
|
||||
{
|
||||
short counter = (short)this.m_D[this.m_IR & 0x7];
|
||||
Helpers.Inject(ref this.m_D[this.m_IR & 0x7], --counter);
|
||||
|
||||
if (counter == -1)
|
||||
{
|
||||
this.m_PC += 2;
|
||||
this.m_Cycles += 14;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.m_PC += FetchW();
|
||||
this.m_Cycles += 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Scc() // Set according to condition
|
||||
{
|
||||
int cCode = (this.m_IR >> 8) & 0xF;
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
if (TestCondition(cCode))
|
||||
{
|
||||
// Set all the bits
|
||||
SetOperandB(mode, register, -1);
|
||||
this.m_Cycles += (mode == 0) ? 6 : 8 + Helpers.EACalcTimeBW(mode, register);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clear all the bits
|
||||
SetOperandB(mode, register, 0);
|
||||
this.m_Cycles += (mode == 0) ? 4 : 8 + Helpers.EACalcTimeBW(mode, register);
|
||||
}
|
||||
}
|
||||
|
||||
private void BRA() // Branch Always
|
||||
{
|
||||
this.m_Cycles += 10;
|
||||
|
||||
if ((sbyte)this.m_IR == 0)
|
||||
{
|
||||
this.m_PC += PeekW();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.m_PC += (sbyte)this.m_IR;
|
||||
}
|
||||
}
|
||||
|
||||
private void BSR() // Branch to subroutine
|
||||
{
|
||||
this.SP -= 4;
|
||||
|
||||
// 16-bit displacement
|
||||
if ((sbyte)this.m_IR == 0)
|
||||
{
|
||||
WriteL(this.SP, this.m_PC + 2);
|
||||
this.m_PC += PeekW();
|
||||
}
|
||||
|
||||
// 8-bit displacement
|
||||
else
|
||||
{
|
||||
WriteL(this.SP, this.m_PC);
|
||||
this.m_PC += (sbyte)this.m_IR;
|
||||
}
|
||||
|
||||
this.m_Cycles += 18;
|
||||
}
|
||||
|
||||
private void JMP() // Jump
|
||||
{
|
||||
this.m_PC = FetchAddress((this.m_IR >> 3) & 0x7, this.m_IR & 0x7);
|
||||
|
||||
switch ((this.m_IR >> 3) & 0x7)
|
||||
{
|
||||
case 0x2: this.m_Cycles += 8; break;
|
||||
case 0x5: this.m_Cycles += 10; break;
|
||||
case 0x6: this.m_Cycles += 14; break;
|
||||
case 0x7:
|
||||
switch (this.m_IR & 0x7)
|
||||
{
|
||||
case 0x0: this.m_Cycles += 10; break;
|
||||
case 0x1: this.m_Cycles += 12; break;
|
||||
case 0x2: this.m_Cycles += 10; break;
|
||||
case 0x3: this.m_Cycles += 14; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void JSR() // Jump to subroutine
|
||||
{
|
||||
int address = FetchAddress((this.m_IR >> 3) & 0x7, this.m_IR & 0x7) & 0x00FFFFFF;
|
||||
this.SP -= 4;
|
||||
WriteL(this.SP, this.m_PC);
|
||||
this.m_PC = address;
|
||||
|
||||
switch ((this.m_IR >> 3) & 0x7)
|
||||
{
|
||||
case 0x2: this.m_Cycles += 16; break;
|
||||
case 0x5: this.m_Cycles += 18; break;
|
||||
case 0x6: this.m_Cycles += 22; break;
|
||||
case 0x7:
|
||||
switch (this.m_IR & 0x7)
|
||||
{
|
||||
case 0x0: this.m_Cycles += 18; break;
|
||||
case 0x1: this.m_Cycles += 20; break;
|
||||
case 0x2: this.m_Cycles += 18; break;
|
||||
case 0x3: this.m_Cycles += 22; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void NOP() // No operation
|
||||
{
|
||||
// Doesn't do anything, it's there to help flush the integer pipeline
|
||||
this.m_Cycles += 4;
|
||||
}
|
||||
|
||||
private void RTS() // Return from Subroutine
|
||||
{
|
||||
this.m_PC = ReadL(this.SP);
|
||||
this.SP += 4;
|
||||
this.m_Cycles += 16;
|
||||
}
|
||||
|
||||
private void TST() // Test an operand
|
||||
{
|
||||
// Use an integer operand, it gets sign-extended and we can check it afterwards
|
||||
int operand = 0;
|
||||
switch ((this.m_IR >> 6) & 0x3)
|
||||
{
|
||||
case 0: // B
|
||||
{
|
||||
operand = FetchOperandB((this.m_IR >> 3) & 0x7, this.m_IR & 0x7);
|
||||
this.m_Cycles += 4 + Helpers.EACalcTimeBW((this.m_IR >> 3) & 0x7, this.m_IR & 0x7);
|
||||
break;
|
||||
}
|
||||
case 1: // W
|
||||
{
|
||||
operand = FetchOperandW((this.m_IR >> 3) & 0x7, this.m_IR & 0x7);
|
||||
this.m_Cycles += 4 + Helpers.EACalcTimeBW((this.m_IR >> 3) & 0x7, this.m_IR & 0x7);
|
||||
break;
|
||||
}
|
||||
case 2: // L
|
||||
{
|
||||
operand = FetchOperandL((this.m_IR >> 3) & 0x7, this.m_IR & 0x7);
|
||||
this.m_Cycles += 4 + Helpers.EACalcTimeL((this.m_IR >> 3) & 0x7, this.m_IR & 0x7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.V = this.C = false;
|
||||
this.N = (operand < 0);
|
||||
this.Z = (operand == 0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,622 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace MC68000
|
||||
{
|
||||
public partial class MC68K
|
||||
{
|
||||
#region Arithmetic Shift
|
||||
private void ASL()
|
||||
{
|
||||
int count_register = (this.m_IR >> 9) & 0x7;
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int ir = (this.m_IR >> 5) & 0x1;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
int shift_count = (ir == 0) ?
|
||||
((count_register == 0) ? 8 : count_register) :
|
||||
(this.m_D[count_register] % 64);
|
||||
|
||||
this.C = this.V = false;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
sbyte value = (sbyte)this.m_D[register];
|
||||
bool msbit = value < 0;
|
||||
for (int i = 0; i < shift_count; i++)
|
||||
{
|
||||
this.C = this.X = value < 0;
|
||||
value <<= 1;
|
||||
this.V |= ((value < 0) != msbit);
|
||||
}
|
||||
Helpers.Inject(ref this.m_D[register], value);
|
||||
this.N = value < 0;
|
||||
this.Z = value == 0;
|
||||
this.m_Cycles += 6 + 2 * shift_count;
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
short value = (short)this.m_D[register];
|
||||
bool msbit = value < 0;
|
||||
for (int i = 0; i < shift_count; i++)
|
||||
{
|
||||
this.C = this.X = value < 0;
|
||||
value <<= 1;
|
||||
this.V |= ((value < 0) != msbit);
|
||||
}
|
||||
Helpers.Inject(ref this.m_D[register], value);
|
||||
this.N = value < 0;
|
||||
this.Z = value == 0;
|
||||
this.m_Cycles += 6 + 2 * shift_count;
|
||||
return;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
int value = this.m_D[register];
|
||||
bool msbit = value < 0;
|
||||
for (int i = 0; i < shift_count; i++)
|
||||
{
|
||||
this.C = this.X = value < 0;
|
||||
value <<= 1;
|
||||
this.V |= ((value < 0) != msbit);
|
||||
}
|
||||
this.m_D[register] = value;
|
||||
this.N = value < 0;
|
||||
this.Z = value == 0;
|
||||
this.m_Cycles += 8 + 2 * shift_count;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ASR()
|
||||
{
|
||||
int count_register = (this.m_IR >> 9) & 0x7;
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int ir = (this.m_IR >> 5) & 0x1;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
int shift_count = (ir == 0) ?
|
||||
((count_register == 0) ? 8 : count_register) :
|
||||
(this.m_D[count_register] % 64);
|
||||
|
||||
this.C = this.V = false;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
sbyte value = (sbyte)this.m_D[register];
|
||||
for (int i = 0; i < shift_count; i++)
|
||||
{
|
||||
this.C = this.X = (value & 1) > 0;
|
||||
value >>= 1;
|
||||
}
|
||||
Helpers.Inject(ref this.m_D[register], value);
|
||||
this.N = value < 0;
|
||||
this.Z = value == 0;
|
||||
this.m_Cycles += 6 + 2 * shift_count;
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
short value = (short)this.m_D[register];
|
||||
for (int i = 0; i < shift_count; i++)
|
||||
{
|
||||
this.C = this.X = (value & 1) > 0;
|
||||
value >>= 1;
|
||||
}
|
||||
Helpers.Inject(ref this.m_D[register], value);
|
||||
this.N = value < 0;
|
||||
this.Z = value == 0;
|
||||
this.m_Cycles += 6 + 2 * shift_count;
|
||||
return;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
int value = this.m_D[register];
|
||||
for (int i = 0; i < shift_count; i++)
|
||||
{
|
||||
this.C = this.X = (value & 1) > 0;
|
||||
value >>= 1;
|
||||
}
|
||||
this.m_D[register] = value;
|
||||
this.N = value < 0;
|
||||
this.Z = value == 0;
|
||||
this.m_Cycles += 8 + 2 * shift_count;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ASL_ASR_Memory()
|
||||
{
|
||||
int direction = (this.m_IR >> 8) & 0x1; // 0 = Right, 1 = Left
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
short value = PeekOperandW(mode, register);
|
||||
if (direction == 0)
|
||||
{
|
||||
this.C = this.X = (value & 1) > 0;
|
||||
this.V = false; // For right shift, MSB can't change
|
||||
value >>= 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool msbit = (value < 0);
|
||||
this.C = this.X = value < 0;
|
||||
value <<= 1;
|
||||
this.V |= ((value < 0) != msbit);
|
||||
}
|
||||
this.N = value < 0;
|
||||
this.Z = value == 0;
|
||||
SetOperandW(mode, register, value);
|
||||
this.m_Cycles += 8 + Helpers.EACalcTimeBW(mode, register);
|
||||
}
|
||||
#endregion Arithmetic Shift
|
||||
|
||||
#region Logical Shift
|
||||
private void LSL()
|
||||
{
|
||||
int count_register = (this.m_IR >> 9) & 0x7;
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int ir = (this.m_IR >> 5) & 0x1;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
int shift_count = (ir == 0) ?
|
||||
((count_register == 0) ? 8 : count_register) :
|
||||
(this.m_D[count_register] % 64);
|
||||
|
||||
this.C = this.V = false;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
byte value = (byte)this.m_D[register];
|
||||
for (int i = 0; i < shift_count; i++)
|
||||
{
|
||||
this.C = this.X = (value & 0x80) > 0;
|
||||
value <<= 1;
|
||||
}
|
||||
Helpers.Inject(ref this.m_D[register], value);
|
||||
this.N = (value & 0x80) > 0;
|
||||
this.Z = value == 0;
|
||||
this.m_Cycles += 6 + 2 * shift_count;
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
ushort value = (ushort)this.m_D[register];
|
||||
for (int i = 0; i < shift_count; i++)
|
||||
{
|
||||
this.C = this.X = (value & 0x8000) > 0;
|
||||
value <<= 1;
|
||||
}
|
||||
Helpers.Inject(ref this.m_D[register], value);
|
||||
this.N = (value & 0x8000) > 0;
|
||||
this.Z = value == 0;
|
||||
this.m_Cycles += 6 + 2 * shift_count;
|
||||
return;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
uint value = (uint)this.m_D[register];
|
||||
for (int i = 0; i < shift_count; i++)
|
||||
{
|
||||
this.C = this.X = (value & 0x80000000) > 0;
|
||||
value <<= 1;
|
||||
}
|
||||
this.m_D[register] = (int)value;
|
||||
this.N = (value & 0x80000000) > 0;
|
||||
this.Z = value == 0;
|
||||
this.m_Cycles += 8 + 2 * shift_count;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void LSR()
|
||||
{
|
||||
int count_register = (this.m_IR >> 9) & 0x7;
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int ir = (this.m_IR >> 5) & 0x1;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
int shift_count = (ir == 0) ?
|
||||
((count_register == 0) ? 8 : count_register) :
|
||||
(this.m_D[count_register] % 64);
|
||||
|
||||
this.C = this.V = false;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
byte value = (byte)this.m_D[register];
|
||||
for (int i = 0; i < shift_count; i++)
|
||||
{
|
||||
this.C = this.X = (value & 1) > 0;
|
||||
value >>= 1;
|
||||
}
|
||||
Helpers.Inject(ref this.m_D[register], value);
|
||||
this.N = (value & 0x80) > 0;
|
||||
this.Z = value == 0;
|
||||
this.m_Cycles += 6 + 2 * shift_count;
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
ushort value = (ushort)this.m_D[register];
|
||||
for (int i = 0; i < shift_count; i++)
|
||||
{
|
||||
this.C = this.X = (value & 1) > 0;
|
||||
value >>= 1;
|
||||
}
|
||||
Helpers.Inject(ref this.m_D[register], value);
|
||||
this.N = (value & 0x8000) > 0;
|
||||
this.Z = value == 0;
|
||||
this.m_Cycles += 6 + 2 * shift_count;
|
||||
return;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
uint value = (uint)this.m_D[register];
|
||||
for (int i = 0; i < shift_count; i++)
|
||||
{
|
||||
this.C = this.X = (value & 1) > 0;
|
||||
value >>= 1;
|
||||
}
|
||||
this.m_D[register] = (int)value;
|
||||
this.N = (value & 0x80000000) > 0;
|
||||
this.Z = value == 0;
|
||||
this.m_Cycles += 8 + 2 * shift_count;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void LSL_LSR_Memory()
|
||||
{
|
||||
int direction = (this.m_IR >> 8) & 0x1; // 0 = Right, 1 = Left
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
ushort value = (ushort)PeekOperandW(mode, register);
|
||||
if (direction == 0)
|
||||
{
|
||||
this.C = this.X = (value & 1) > 0;
|
||||
value >>= 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.C = this.X = (value & 0x8000) > 0;
|
||||
value <<= 1;
|
||||
}
|
||||
this.V = false;
|
||||
this.N = (value & 0x8000) > 0;
|
||||
this.Z = value == 0;
|
||||
SetOperandW(mode, register, (short)value);
|
||||
this.m_Cycles += 8 + Helpers.EACalcTimeBW(mode, register);
|
||||
}
|
||||
#endregion Logical Shift
|
||||
|
||||
#region Rotate (without extend)
|
||||
private void ROL()
|
||||
{
|
||||
int count_register = (this.m_IR >> 9) & 0x7;
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int ir = (this.m_IR >> 5) & 0x1;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
int shift_count = (ir == 0) ?
|
||||
((count_register == 0) ? 8 : count_register) :
|
||||
(this.m_D[count_register] % 64);
|
||||
|
||||
this.C = this.V = false;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
byte value = (byte)this.m_D[register];
|
||||
for (int i = 0; i < shift_count; i++)
|
||||
{
|
||||
this.C = (value & 0x80) > 0;
|
||||
value = (byte)((value >> 7) | (value << 1));
|
||||
}
|
||||
Helpers.Inject(ref this.m_D[register], value);
|
||||
this.N = (value & 0x80) > 0;
|
||||
this.Z = value == 0;
|
||||
this.m_Cycles += 6 + 2 * shift_count;
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
ushort value = (ushort)this.m_D[register];
|
||||
for (int i = 0; i < shift_count; i++)
|
||||
{
|
||||
this.C = (value & 0x8000) > 0;
|
||||
value = (ushort)((value >> 15) | (value << 1));
|
||||
}
|
||||
Helpers.Inject(ref this.m_D[register], value);
|
||||
this.N = (value & 0x8000) > 0;
|
||||
this.Z = value == 0;
|
||||
this.m_Cycles += 6 + 2 * shift_count;
|
||||
return;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
uint value = (uint)this.m_D[register];
|
||||
for (int i = 0; i < shift_count; i++)
|
||||
{
|
||||
this.C = (value & 0x80000000) > 0;
|
||||
value = (uint)((value >> 31) | (value << 1));
|
||||
}
|
||||
this.m_D[register] = (int)value;
|
||||
this.N = (value & 0x80000000) > 0;
|
||||
this.Z = value == 0;
|
||||
this.m_Cycles += 8 + 2 * shift_count;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ROR()
|
||||
{
|
||||
int count_register = (this.m_IR >> 9) & 0x7;
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int ir = (this.m_IR >> 5) & 0x1;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
int shift_count = (ir == 0) ?
|
||||
((count_register == 0) ? 8 : count_register) :
|
||||
(this.m_D[count_register] % 64);
|
||||
|
||||
this.C = this.V = false;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
byte value = (byte)this.m_D[register];
|
||||
for (int i = 0; i < shift_count; i++)
|
||||
{
|
||||
this.C = (value & 1) > 0;
|
||||
value = (byte)((value << 7) | (value >> 1));
|
||||
}
|
||||
Helpers.Inject(ref this.m_D[register], value);
|
||||
this.N = (value & 0x80) > 0;
|
||||
this.Z = value == 0;
|
||||
this.m_Cycles += 6 + 2 * shift_count;
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
ushort value = (ushort)this.m_D[register];
|
||||
for (int i = 0; i < shift_count; i++)
|
||||
{
|
||||
this.C = (value & 1) > 0;
|
||||
value = (ushort)((value << 15) | (value >> 1));
|
||||
}
|
||||
Helpers.Inject(ref this.m_D[register], value);
|
||||
this.N = (value & 0x8000) > 0;
|
||||
this.Z = value == 0;
|
||||
this.m_Cycles += 6 + 2 * shift_count;
|
||||
return;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
uint value = (uint)this.m_D[register];
|
||||
for (int i = 0; i < shift_count; i++)
|
||||
{
|
||||
this.C = (value & 1) > 0;
|
||||
value = (uint)((value << 31) | (value >> 1));
|
||||
}
|
||||
this.m_D[register] = (int)value;
|
||||
this.N = (value & 0x80000000) > 0;
|
||||
this.Z = value == 0;
|
||||
this.m_Cycles += 8 + 2 * shift_count;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ROL_ROR_Memory()
|
||||
{
|
||||
int direction = (this.m_IR >> 8) & 0x1; // 0 = Right, 1 = Left
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
ushort value = (ushort)PeekOperandW(mode, register);
|
||||
if (direction == 0)
|
||||
{
|
||||
this.C = (value & 1) > 0;
|
||||
value = (ushort)((value >> 1) | (value << 15));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.C = (value & 0x8000) > 0;
|
||||
value = (ushort)((value >> 15) | (value << 1));
|
||||
}
|
||||
this.V = false;
|
||||
this.N = (value & 0x8000) > 0;
|
||||
this.Z = value == 0;
|
||||
SetOperandW(mode, register, (short)value);
|
||||
this.m_Cycles += 8 + Helpers.EACalcTimeBW(mode, register);
|
||||
}
|
||||
#endregion Rotate (without extend)
|
||||
|
||||
#region Rotate (with extend)
|
||||
private void ROXL()
|
||||
{
|
||||
int count_register = (this.m_IR >> 9) & 0x7;
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int ir = (this.m_IR >> 5) & 0x1;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
int shift_count = (ir == 0) ?
|
||||
((count_register == 0) ? 8 : count_register) :
|
||||
(this.m_D[count_register] % 64);
|
||||
|
||||
this.V = false;
|
||||
this.C = this.X;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
byte value = (byte)this.m_D[register];
|
||||
for (int i = 0; i < shift_count; i++)
|
||||
{
|
||||
this.C = (value & 0x80) > 0;
|
||||
value = (byte)((value << 1) | ((this.X) ? 1: 0));
|
||||
this.X = this.C;
|
||||
}
|
||||
Helpers.Inject(ref this.m_D[register], value);
|
||||
this.N = (value & 0x80) > 0;
|
||||
this.Z = value == 0;
|
||||
this.m_Cycles += 6 + 2 * shift_count;
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
ushort value = (ushort)this.m_D[register];
|
||||
for (int i = 0; i < shift_count; i++)
|
||||
{
|
||||
this.C = (value & 0x8000) > 0;
|
||||
value = (ushort)((value << 1) | ((this.X) ? 1 : 0));
|
||||
this.X = this.C;
|
||||
}
|
||||
Helpers.Inject(ref this.m_D[register], value);
|
||||
this.N = (value & 0x8000) > 0;
|
||||
this.Z = value == 0;
|
||||
this.m_Cycles += 6 + 2 * shift_count;
|
||||
return;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
uint value = (uint)this.m_D[register];
|
||||
for (int i = 0; i < shift_count; i++)
|
||||
{
|
||||
this.C = (value & 0x80000000) > 0;
|
||||
value = (uint)((value << 1) | ((this.X) ? (uint)1 : (uint)0));
|
||||
this.X = this.C;
|
||||
}
|
||||
this.m_D[register] = (int)value;
|
||||
this.N = (value & 0x80000000) > 0;
|
||||
this.Z = value == 0;
|
||||
this.m_Cycles += 8 + 2 * shift_count;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ROXR()
|
||||
{
|
||||
int count_register = (this.m_IR >> 9) & 0x7;
|
||||
int size = (this.m_IR >> 6) & 0x3;
|
||||
int ir = (this.m_IR >> 5) & 0x1;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
int shift_count = (ir == 0) ?
|
||||
((count_register == 0) ? 8 : count_register) :
|
||||
(this.m_D[count_register] % 64);
|
||||
|
||||
this.V = false;
|
||||
this.C = this.X;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
byte value = (byte)this.m_D[register];
|
||||
for (int i = 0; i < shift_count; i++)
|
||||
{
|
||||
this.C = (value & 1) > 0;
|
||||
value = (byte)(((this.X) ? 0x80 : 0) | (value >> 1));
|
||||
this.X = this.C;
|
||||
}
|
||||
Helpers.Inject(ref this.m_D[register], value);
|
||||
this.N = (value & 0x80) > 0;
|
||||
this.Z = value == 0;
|
||||
this.m_Cycles += 6 + 2 * shift_count;
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
ushort value = (ushort)this.m_D[register];
|
||||
for (int i = 0; i < shift_count; i++)
|
||||
{
|
||||
this.C = (value & 1) > 0;
|
||||
value = (ushort)(((this.X) ? 0x8000 : 0) | (value >> 1));
|
||||
}
|
||||
Helpers.Inject(ref this.m_D[register], value);
|
||||
this.N = (value & 0x8000) > 0;
|
||||
this.Z = value == 0;
|
||||
this.m_Cycles += 6 + 2 * shift_count;
|
||||
return;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
uint value = (uint)this.m_D[register];
|
||||
for (int i = 0; i < shift_count; i++)
|
||||
{
|
||||
this.C = (value & 1) > 0;
|
||||
value = (uint)(((this.X) ? 0x80000000 : 0) | (value >> 1));
|
||||
}
|
||||
this.m_D[register] = (int)value;
|
||||
this.N = (value & 0x80000000) > 0;
|
||||
this.Z = value == 0;
|
||||
this.m_Cycles += 8 + 2 * shift_count;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ROXL_ROXR_Memory()
|
||||
{
|
||||
int direction = (this.m_IR >> 8) & 0x1; // 0 = Right, 1 = Left
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
ushort value = (ushort)PeekOperandW(mode, register);
|
||||
if (direction == 0)
|
||||
{
|
||||
this.C = (value & 1) > 0;
|
||||
value = (ushort)(((this.X) ? 0x8000 : 0) | (value >> 1));
|
||||
this.X = this.C;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.C = (value & 0x8000) > 0;
|
||||
value = (ushort)((value << 1) | ((this.X) ? 1 : 0));
|
||||
this.X = this.C;
|
||||
}
|
||||
this.V = false;
|
||||
this.N = (value & 0x8000) > 0;
|
||||
this.Z = value == 0;
|
||||
SetOperandW(mode, register, (short)value);
|
||||
this.m_Cycles += 8 + Helpers.EACalcTimeBW(mode, register);
|
||||
}
|
||||
#endregion Rotate (with extend)
|
||||
|
||||
private void SWAP() // Swap halves of a register
|
||||
{
|
||||
int register = this.m_IR & 0x7;
|
||||
this.m_D[register] = (int)(((uint)this.m_D[register] << 16) |
|
||||
((uint)this.m_D[register] >> 16));
|
||||
|
||||
this.N = (this.m_D[register] < 0);
|
||||
this.Z = (this.m_D[register] == 0);
|
||||
this.V = this.C = false;
|
||||
|
||||
this.m_Cycles += 4;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,271 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace MC68000
|
||||
{
|
||||
public partial class MC68K
|
||||
{
|
||||
private void ANDI_to_CCR()
|
||||
{
|
||||
Helpers.Inject(ref this.m_SR, (byte)(this.m_SR & FetchW()));
|
||||
this.m_Cycles += 20;
|
||||
}
|
||||
|
||||
private void ANDI_to_SR()
|
||||
{
|
||||
if (this.S)
|
||||
{
|
||||
this.m_SR &= FetchW();
|
||||
|
||||
// Might not be in supervisor mode any more...
|
||||
if (!this.S)
|
||||
{
|
||||
this.m_Ssp = this.SP;
|
||||
this.SP = this.m_Usp;
|
||||
}
|
||||
|
||||
this.m_Cycles += 20;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO - cycle counter
|
||||
Trap(8);
|
||||
}
|
||||
}
|
||||
|
||||
private void CHK()
|
||||
{
|
||||
int registerToCheck = (this.m_IR >> 9) & 0x7;
|
||||
int size = (this.m_IR >> 7) & 0x3;
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
// Only word size is legal on the 68000
|
||||
|
||||
if ((short)this.m_D[registerToCheck] < 0)
|
||||
{
|
||||
this.N = true;
|
||||
Trap(6);
|
||||
}
|
||||
else if ((short)this.m_D[registerToCheck] > FetchOperandW(mode, size))
|
||||
{
|
||||
this.N = false;
|
||||
Trap(6);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.m_Cycles += 10 + Helpers.EACalcTimeBW(mode, register);
|
||||
}
|
||||
}
|
||||
|
||||
private void EORI_to_CCR()
|
||||
{
|
||||
Helpers.Inject(ref this.m_SR, (byte)(this.m_SR ^ FetchW()));
|
||||
this.m_Cycles += 20;
|
||||
}
|
||||
|
||||
private void EORI_to_SR()
|
||||
{
|
||||
if (this.S)
|
||||
{
|
||||
this.m_SR ^= FetchW();
|
||||
|
||||
// Might not be in supervisor mode any more...
|
||||
if (!this.S)
|
||||
{
|
||||
this.m_Ssp = this.SP;
|
||||
this.SP = this.m_Usp;
|
||||
}
|
||||
|
||||
this.m_Cycles += 20;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO - cycle counter
|
||||
Trap(8);
|
||||
}
|
||||
}
|
||||
|
||||
private void ILLEGAL()
|
||||
{
|
||||
Trap(4);
|
||||
}
|
||||
|
||||
private void MOVE_from_SR()
|
||||
{
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
SetOperandW(mode, register, (short)this.m_SR);
|
||||
this.m_Cycles += (mode == 0) ? 6 : 8 + Helpers.EACalcTimeBW(mode, register);
|
||||
}
|
||||
|
||||
private void MOVE_to_CCR()
|
||||
{
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
this.m_SR &= 0xFF00;
|
||||
this.m_SR |= (ushort)(FetchOperandW(mode, register) & 0x00FF);
|
||||
this.m_Cycles += (mode == 0) ? 12 : 12 + Helpers.EACalcTimeBW(mode, register);
|
||||
}
|
||||
|
||||
private void MOVE_to_SR() // Move to the Status Register
|
||||
{
|
||||
int mode = (this.m_IR >> 3) & 0x7;
|
||||
int register = this.m_IR & 0x7;
|
||||
|
||||
if (this.S)
|
||||
{
|
||||
this.m_SR = (ushort)FetchOperandW(mode, register);
|
||||
this.m_Cycles += (mode == 0) ? 12 : 12 + Helpers.EACalcTimeBW(mode, register);
|
||||
|
||||
// Might not be in supervisor mode now...
|
||||
if (!this.S)
|
||||
{
|
||||
this.m_Ssp = this.SP;
|
||||
this.SP = this.m_Usp;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Trap(8);
|
||||
}
|
||||
}
|
||||
|
||||
private void MOVE_USP() // Move User Stack Pointer
|
||||
{
|
||||
if (this.S)
|
||||
{
|
||||
if (((this.m_IR >> 3) & 0x1) == 0)
|
||||
{
|
||||
this.m_Usp = this.m_A[this.m_IR & 0x7];
|
||||
}
|
||||
else
|
||||
{
|
||||
this.m_A[this.m_IR & 0x7] = this.m_Usp;
|
||||
}
|
||||
|
||||
this.m_Cycles += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO Cycles
|
||||
Trap(8);
|
||||
}
|
||||
}
|
||||
|
||||
private void ORI_to_CCR()
|
||||
{
|
||||
Helpers.Inject(ref this.m_SR, (byte)(this.m_SR | (ushort)FetchW()));
|
||||
this.m_Cycles += 20;
|
||||
}
|
||||
|
||||
private void ORI_to_SR()
|
||||
{
|
||||
if (this.S)
|
||||
{
|
||||
this.m_SR |= (ushort)FetchW();
|
||||
|
||||
// Might not be in supervisor mode any more...
|
||||
if (!this.S)
|
||||
{
|
||||
this.m_Ssp = this.SP;
|
||||
this.SP = this.m_Usp;
|
||||
}
|
||||
|
||||
this.m_Cycles += 20;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO - TRAP, cycle counter
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private void RESET()
|
||||
{
|
||||
this.m_Cycles += 132;
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private void RTE()
|
||||
{
|
||||
if (this.S)
|
||||
{
|
||||
this.m_SR = (ushort)ReadW(this.SP);
|
||||
this.SP += 2;
|
||||
this.m_PC = ReadL(this.SP);
|
||||
this.SP += 4;
|
||||
|
||||
// Might not be in supervisor mode any more...
|
||||
if (!this.S)
|
||||
{
|
||||
this.m_Ssp = this.SP;
|
||||
this.SP = this.m_Usp;
|
||||
}
|
||||
|
||||
this.m_Cycles += 20;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Privilege exception
|
||||
Trap(8);
|
||||
}
|
||||
}
|
||||
|
||||
private void RTR()
|
||||
{
|
||||
// Seems a bit like RTE, but only affects condition codes
|
||||
this.m_SR = (ushort)((0x00FF & ReadW(this.m_Ssp)) | (0xFF00 & this.m_SR));
|
||||
this.SP += 2;
|
||||
this.m_PC = ReadL(this.SP);
|
||||
this.SP += 4;
|
||||
this.m_Cycles += 20;
|
||||
}
|
||||
|
||||
private void STOP()
|
||||
{
|
||||
this.m_Cycles += 4;
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private void Trap(int trapVector)
|
||||
{
|
||||
// Make sure we're in supervisor mode
|
||||
if (!this.S)
|
||||
{
|
||||
this.m_Usp = this.SP;
|
||||
this.SP = this.m_Ssp;
|
||||
}
|
||||
|
||||
// Add stack frame
|
||||
this.SP -= 4;
|
||||
WriteL(this.SP, this.m_PC);
|
||||
this.SP -= 2;
|
||||
WriteW(this.SP, (short)this.m_SR);
|
||||
|
||||
// Enter supervisor mode
|
||||
this.S = true;
|
||||
|
||||
// Get vector address from ROM header
|
||||
this.m_PC = ReadL(trapVector * 4);
|
||||
}
|
||||
|
||||
private void TRAP()
|
||||
{
|
||||
int trapVector = (this.m_IR & 0x000F);
|
||||
Trap(trapVector + 32);
|
||||
}
|
||||
|
||||
private void TRAPV()
|
||||
{
|
||||
this.m_Cycles += 4;
|
||||
if (this.V)
|
||||
{
|
||||
Trap(7);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,171 @@
|
|||
using System;
|
||||
|
||||
// Do not modify this file directly! This is GENERATED code.
|
||||
// Please open the CpuCoreGenerator solution and make your modifications there.
|
||||
|
||||
namespace BizHawk.Emulation.CPUs.M6502
|
||||
{
|
||||
public partial class MOS6502
|
||||
{
|
||||
public string Disassemble(ushort pc, out int bytesToAdvance)
|
||||
{
|
||||
byte op = ReadMemory(pc);
|
||||
switch (op)
|
||||
{
|
||||
case 0x00: bytesToAdvance = 1; return "BRK";
|
||||
case 0x01: bytesToAdvance = 2; return string.Format("ORA (${0:X2},X)", ReadMemory(++pc));
|
||||
case 0x05: bytesToAdvance = 2; return string.Format("ORA ${0:X2}", ReadMemory(++pc));
|
||||
case 0x06: bytesToAdvance = 2; return string.Format("ASL ${0:X2}", ReadMemory(++pc));
|
||||
case 0x08: bytesToAdvance = 1; return "PHP";
|
||||
case 0x09: bytesToAdvance = 2; return string.Format("ORA #${0:X2}", ReadMemory(++pc));
|
||||
case 0x0A: bytesToAdvance = 1; return "ASL A";
|
||||
case 0x0D: bytesToAdvance = 3; return string.Format("ORA ${0:X4}", ReadWord(++pc));
|
||||
case 0x0E: bytesToAdvance = 3; return string.Format("ASL ${0:X4}", ReadWord(++pc));
|
||||
case 0x10: bytesToAdvance = 2; return string.Format("BPL {0}", (sbyte)ReadMemory(++pc));
|
||||
case 0x11: bytesToAdvance = 2; return string.Format("ORA (${0:X2}),Y", ReadMemory(++pc));
|
||||
case 0x15: bytesToAdvance = 2; return string.Format("ORA ${0:X2},X", ReadMemory(++pc));
|
||||
case 0x16: bytesToAdvance = 2; return string.Format("ASL ${0:X2},X", ReadMemory(++pc));
|
||||
case 0x18: bytesToAdvance = 1; return "CLC";
|
||||
case 0x19: bytesToAdvance = 3; return string.Format("ORA ${0:X4},Y", ReadWord(++pc));
|
||||
case 0x1D: bytesToAdvance = 3; return string.Format("ORA ${0:X4},X", ReadWord(++pc));
|
||||
case 0x1E: bytesToAdvance = 3; return string.Format("ASL ${0:X4},X", ReadWord(++pc));
|
||||
case 0x20: bytesToAdvance = 3; return string.Format("JSR ${0:X4}", ReadWord(++pc));
|
||||
case 0x21: bytesToAdvance = 2; return string.Format("AND (${0:X2},X)", ReadMemory(++pc));
|
||||
case 0x24: bytesToAdvance = 2; return string.Format("BIT ${0:X2}", ReadMemory(++pc));
|
||||
case 0x25: bytesToAdvance = 2; return string.Format("AND ${0:X2}", ReadMemory(++pc));
|
||||
case 0x26: bytesToAdvance = 2; return string.Format("ROL ${0:X2}", ReadMemory(++pc));
|
||||
case 0x28: bytesToAdvance = 1; return "PLP";
|
||||
case 0x29: bytesToAdvance = 2; return string.Format("AND #${0:X2}", ReadMemory(++pc));
|
||||
case 0x2A: bytesToAdvance = 1; return "ROL A";
|
||||
case 0x2C: bytesToAdvance = 3; return string.Format("BIT ${0:X4}", ReadWord(++pc));
|
||||
case 0x2D: bytesToAdvance = 3; return string.Format("AND ${0:X4}", ReadWord(++pc));
|
||||
case 0x2E: bytesToAdvance = 3; return string.Format("ROL ${0:X4}", ReadWord(++pc));
|
||||
case 0x30: bytesToAdvance = 2; return string.Format("BMI {0}", (sbyte)ReadMemory(++pc));
|
||||
case 0x31: bytesToAdvance = 2; return string.Format("AND (${0:X2}),Y", ReadMemory(++pc));
|
||||
case 0x35: bytesToAdvance = 2; return string.Format("AND ${0:X2},X", ReadMemory(++pc));
|
||||
case 0x36: bytesToAdvance = 2; return string.Format("ROL ${0:X2},X", ReadMemory(++pc));
|
||||
case 0x38: bytesToAdvance = 1; return "SEC";
|
||||
case 0x39: bytesToAdvance = 3; return string.Format("AND ${0:X4},Y", ReadWord(++pc));
|
||||
case 0x3D: bytesToAdvance = 3; return string.Format("AND ${0:X4},X", ReadWord(++pc));
|
||||
case 0x3E: bytesToAdvance = 3; return string.Format("ROL ${0:X4},X", ReadWord(++pc));
|
||||
case 0x40: bytesToAdvance = 1; return "RTI";
|
||||
case 0x41: bytesToAdvance = 2; return string.Format("EOR (${0:X2},X)", ReadMemory(++pc));
|
||||
case 0x45: bytesToAdvance = 2; return string.Format("EOR ${0:X2}", ReadMemory(++pc));
|
||||
case 0x46: bytesToAdvance = 2; return string.Format("LSR ${0:X2}", ReadMemory(++pc));
|
||||
case 0x48: bytesToAdvance = 1; return "PHA";
|
||||
case 0x49: bytesToAdvance = 2; return string.Format("EOR #${0:X2}", ReadMemory(++pc));
|
||||
case 0x4A: bytesToAdvance = 1; return "LSR A";
|
||||
case 0x4C: bytesToAdvance = 3; return string.Format("JMP ${0:X4}", ReadWord(++pc));
|
||||
case 0x4D: bytesToAdvance = 3; return string.Format("EOR ${0:X4}", ReadWord(++pc));
|
||||
case 0x4E: bytesToAdvance = 3; return string.Format("LSR ${0:X4}", ReadWord(++pc));
|
||||
case 0x50: bytesToAdvance = 2; return string.Format("BVC {0}", (sbyte)ReadMemory(++pc));
|
||||
case 0x51: bytesToAdvance = 2; return string.Format("EOR (${0:X2}),Y", ReadMemory(++pc));
|
||||
case 0x55: bytesToAdvance = 2; return string.Format("EOR ${0:X2},X", ReadMemory(++pc));
|
||||
case 0x56: bytesToAdvance = 2; return string.Format("LSR ${0:X2},X", ReadMemory(++pc));
|
||||
case 0x58: bytesToAdvance = 1; return "CLI";
|
||||
case 0x59: bytesToAdvance = 3; return string.Format("EOR ${0:X4},Y", ReadWord(++pc));
|
||||
case 0x5D: bytesToAdvance = 3; return string.Format("EOR ${0:X4},X", ReadWord(++pc));
|
||||
case 0x5E: bytesToAdvance = 3; return string.Format("LSR ${0:X4},X", ReadWord(++pc));
|
||||
case 0x60: bytesToAdvance = 1; return "RTS";
|
||||
case 0x61: bytesToAdvance = 2; return string.Format("ADC (${0:X2},X)", ReadMemory(++pc));
|
||||
case 0x65: bytesToAdvance = 2; return string.Format("ADC ${0:X2}", ReadMemory(++pc));
|
||||
case 0x66: bytesToAdvance = 2; return string.Format("ROR ${0:X2}", ReadMemory(++pc));
|
||||
case 0x68: bytesToAdvance = 1; return "PLA";
|
||||
case 0x69: bytesToAdvance = 2; return string.Format("ADC #${0:X2}", ReadMemory(++pc));
|
||||
case 0x6A: bytesToAdvance = 1; return "ROR A";
|
||||
case 0x6C: bytesToAdvance = 3; return string.Format("JMP (${0:X4})", ReadWord(++pc));
|
||||
case 0x6D: bytesToAdvance = 3; return string.Format("ADC ${0:X4}", ReadWord(++pc));
|
||||
case 0x6E: bytesToAdvance = 3; return string.Format("ROR ${0:X4}", ReadWord(++pc));
|
||||
case 0x70: bytesToAdvance = 2; return string.Format("BVS {0}", (sbyte)ReadMemory(++pc));
|
||||
case 0x71: bytesToAdvance = 2; return string.Format("ADC (${0:X2}),Y", ReadMemory(++pc));
|
||||
case 0x75: bytesToAdvance = 2; return string.Format("ADC ${0:X2},X", ReadMemory(++pc));
|
||||
case 0x76: bytesToAdvance = 2; return string.Format("ROR ${0:X2},X", ReadMemory(++pc));
|
||||
case 0x78: bytesToAdvance = 1; return "SEI";
|
||||
case 0x79: bytesToAdvance = 3; return string.Format("ADC ${0:X4},Y", ReadWord(++pc));
|
||||
case 0x7D: bytesToAdvance = 3; return string.Format("ADC ${0:X4},X", ReadWord(++pc));
|
||||
case 0x7E: bytesToAdvance = 3; return string.Format("ROR ${0:X4},X", ReadWord(++pc));
|
||||
case 0x81: bytesToAdvance = 2; return string.Format("STA (${0:X2},X)", ReadMemory(++pc));
|
||||
case 0x84: bytesToAdvance = 2; return string.Format("STY ${0:X2}", ReadMemory(++pc));
|
||||
case 0x85: bytesToAdvance = 2; return string.Format("STA ${0:X2}", ReadMemory(++pc));
|
||||
case 0x86: bytesToAdvance = 2; return string.Format("STX ${0:X2}", ReadMemory(++pc));
|
||||
case 0x88: bytesToAdvance = 1; return "DEY";
|
||||
case 0x8A: bytesToAdvance = 1; return "TXA";
|
||||
case 0x8C: bytesToAdvance = 3; return string.Format("STY ${0:X4}", ReadWord(++pc));
|
||||
case 0x8D: bytesToAdvance = 3; return string.Format("STA ${0:X4}", ReadWord(++pc));
|
||||
case 0x8E: bytesToAdvance = 3; return string.Format("STX ${0:X4}", ReadWord(++pc));
|
||||
case 0x90: bytesToAdvance = 2; return string.Format("BCC {0}", (sbyte)ReadMemory(++pc));
|
||||
case 0x91: bytesToAdvance = 2; return string.Format("STA (${0:X2}),Y", ReadMemory(++pc));
|
||||
case 0x94: bytesToAdvance = 2; return string.Format("STY ${0:X2},X", ReadMemory(++pc));
|
||||
case 0x95: bytesToAdvance = 2; return string.Format("STA ${0:X2},X", ReadMemory(++pc));
|
||||
case 0x96: bytesToAdvance = 2; return string.Format("STX ${0:X2},Y", ReadMemory(++pc));
|
||||
case 0x98: bytesToAdvance = 1; return "TYA";
|
||||
case 0x99: bytesToAdvance = 3; return string.Format("STA ${0:X4},Y", ReadWord(++pc));
|
||||
case 0x9A: bytesToAdvance = 1; return "TXS";
|
||||
case 0x9D: bytesToAdvance = 3; return string.Format("STA ${0:X4},X", ReadWord(++pc));
|
||||
case 0xA0: bytesToAdvance = 2; return string.Format("LDY #${0:X2}", ReadMemory(++pc));
|
||||
case 0xA1: bytesToAdvance = 2; return string.Format("LDA (${0:X2},X)", ReadMemory(++pc));
|
||||
case 0xA2: bytesToAdvance = 2; return string.Format("LDX #${0:X2}", ReadMemory(++pc));
|
||||
case 0xA4: bytesToAdvance = 2; return string.Format("LDY ${0:X2}", ReadMemory(++pc));
|
||||
case 0xA5: bytesToAdvance = 2; return string.Format("LDA ${0:X2}", ReadMemory(++pc));
|
||||
case 0xA6: bytesToAdvance = 2; return string.Format("LDX ${0:X2}", ReadMemory(++pc));
|
||||
case 0xA8: bytesToAdvance = 1; return "TAY";
|
||||
case 0xA9: bytesToAdvance = 2; return string.Format("LDA #${0:X2}", ReadMemory(++pc));
|
||||
case 0xAA: bytesToAdvance = 1; return "TAX";
|
||||
case 0xAC: bytesToAdvance = 3; return string.Format("LDY ${0:X4}", ReadWord(++pc));
|
||||
case 0xAD: bytesToAdvance = 3; return string.Format("LDA ${0:X4}", ReadWord(++pc));
|
||||
case 0xAE: bytesToAdvance = 3; return string.Format("LDX ${0:X4}", ReadWord(++pc));
|
||||
case 0xB0: bytesToAdvance = 2; return string.Format("BCS {0}", (sbyte)ReadMemory(++pc));
|
||||
case 0xB1: bytesToAdvance = 2; return string.Format("LDA (${0:X2}),Y", ReadMemory(++pc));
|
||||
case 0xB4: bytesToAdvance = 2; return string.Format("LDY ${0:X2},X", ReadMemory(++pc));
|
||||
case 0xB5: bytesToAdvance = 2; return string.Format("LDA ${0:X2},X", ReadMemory(++pc));
|
||||
case 0xB6: bytesToAdvance = 2; return string.Format("LDX ${0:X2},Y", ReadMemory(++pc));
|
||||
case 0xB8: bytesToAdvance = 1; return "CLV";
|
||||
case 0xB9: bytesToAdvance = 3; return string.Format("LDA ${0:X4},Y", ReadWord(++pc));
|
||||
case 0xBA: bytesToAdvance = 1; return "TSX";
|
||||
case 0xBC: bytesToAdvance = 3; return string.Format("LDY ${0:X4},X", ReadWord(++pc));
|
||||
case 0xBD: bytesToAdvance = 3; return string.Format("LDA ${0:X4},X", ReadWord(++pc));
|
||||
case 0xBE: bytesToAdvance = 3; return string.Format("LDX ${0:X4},Y", ReadWord(++pc));
|
||||
case 0xC0: bytesToAdvance = 2; return string.Format("CPY #${0:X2}", ReadMemory(++pc));
|
||||
case 0xC1: bytesToAdvance = 2; return string.Format("CMP (${0:X2},X)", ReadMemory(++pc));
|
||||
case 0xC4: bytesToAdvance = 2; return string.Format("CPY ${0:X2}", ReadMemory(++pc));
|
||||
case 0xC5: bytesToAdvance = 2; return string.Format("CMP ${0:X2}", ReadMemory(++pc));
|
||||
case 0xC6: bytesToAdvance = 2; return string.Format("DEC ${0:X2}", ReadMemory(++pc));
|
||||
case 0xC8: bytesToAdvance = 1; return "INY";
|
||||
case 0xC9: bytesToAdvance = 2; return string.Format("CMP #${0:X2}", ReadMemory(++pc));
|
||||
case 0xCA: bytesToAdvance = 1; return "DEX";
|
||||
case 0xCC: bytesToAdvance = 3; return string.Format("CPY ${0:X4}", ReadWord(++pc));
|
||||
case 0xCD: bytesToAdvance = 3; return string.Format("CMP ${0:X4}", ReadWord(++pc));
|
||||
case 0xCE: bytesToAdvance = 3; return string.Format("DEC ${0:X4}", ReadWord(++pc));
|
||||
case 0xD0: bytesToAdvance = 2; return string.Format("BNE {0}", (sbyte)ReadMemory(++pc));
|
||||
case 0xD1: bytesToAdvance = 2; return string.Format("CMP (${0:X2}),Y", ReadMemory(++pc));
|
||||
case 0xD5: bytesToAdvance = 2; return string.Format("CMP ${0:X2},X", ReadMemory(++pc));
|
||||
case 0xD6: bytesToAdvance = 2; return string.Format("DEC ${0:X2},X", ReadMemory(++pc));
|
||||
case 0xD8: bytesToAdvance = 1; return "CLD";
|
||||
case 0xD9: bytesToAdvance = 3; return string.Format("CMP ${0:X4},Y", ReadWord(++pc));
|
||||
case 0xDD: bytesToAdvance = 3; return string.Format("CMP ${0:X4},X", ReadWord(++pc));
|
||||
case 0xDE: bytesToAdvance = 3; return string.Format("DEC ${0:X4},X", ReadWord(++pc));
|
||||
case 0xE0: bytesToAdvance = 2; return string.Format("CPX #${0:X2}", ReadMemory(++pc));
|
||||
case 0xE1: bytesToAdvance = 2; return string.Format("SBC (${0:X2},X)", ReadMemory(++pc));
|
||||
case 0xE4: bytesToAdvance = 2; return string.Format("CPX ${0:X2}", ReadMemory(++pc));
|
||||
case 0xE5: bytesToAdvance = 2; return string.Format("SBC ${0:X2}", ReadMemory(++pc));
|
||||
case 0xE6: bytesToAdvance = 2; return string.Format("INC ${0:X2}", ReadMemory(++pc));
|
||||
case 0xE8: bytesToAdvance = 1; return "INX";
|
||||
case 0xE9: bytesToAdvance = 2; return string.Format("SBC #${0:X2}", ReadMemory(++pc));
|
||||
case 0xEA: bytesToAdvance = 1; return "NOP";
|
||||
case 0xEC: bytesToAdvance = 3; return string.Format("CPX ${0:X4}", ReadWord(++pc));
|
||||
case 0xED: bytesToAdvance = 3; return string.Format("SBC ${0:X4}", ReadWord(++pc));
|
||||
case 0xEE: bytesToAdvance = 3; return string.Format("INC ${0:X4}", ReadWord(++pc));
|
||||
case 0xF0: bytesToAdvance = 2; return string.Format("BEQ {0}", (sbyte)ReadMemory(++pc));
|
||||
case 0xF1: bytesToAdvance = 2; return string.Format("SBC (${0:X2}),Y", ReadMemory(++pc));
|
||||
case 0xF5: bytesToAdvance = 2; return string.Format("SBC ${0:X2},X", ReadMemory(++pc));
|
||||
case 0xF6: bytesToAdvance = 2; return string.Format("INC ${0:X2},X", ReadMemory(++pc));
|
||||
case 0xF8: bytesToAdvance = 1; return "SED";
|
||||
case 0xF9: bytesToAdvance = 3; return string.Format("SBC ${0:X4},Y", ReadWord(++pc));
|
||||
case 0xFD: bytesToAdvance = 3; return string.Format("SBC ${0:X4},X", ReadWord(++pc));
|
||||
case 0xFE: bytesToAdvance = 3; return string.Format("INC ${0:X4},X", ReadWord(++pc));
|
||||
}
|
||||
bytesToAdvance = 1;
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,184 @@
|
|||
using System;
|
||||
|
||||
namespace BizHawk.Emulation.CPUs.M6502
|
||||
{
|
||||
public sealed partial class MOS6502
|
||||
{
|
||||
public MOS6502()
|
||||
{
|
||||
//InitTableNZ();
|
||||
Reset();
|
||||
}
|
||||
/*
|
||||
private byte[] TableNZ;
|
||||
private void InitTableNZ()
|
||||
{
|
||||
TableNZ = new byte[256];
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
byte b = 0;
|
||||
if (i == 0) b |= 0x02;
|
||||
if (i > 127) b |= 0x80;
|
||||
TableNZ[i] = b;
|
||||
}
|
||||
}*/
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
A = 0;
|
||||
X = 0;
|
||||
Y = 0;
|
||||
P = 0;
|
||||
S = 0;
|
||||
PC = 0;
|
||||
PendingCycles = 0;
|
||||
TotalExecutedCycles = 0;
|
||||
}
|
||||
|
||||
public void ResetPC()
|
||||
{
|
||||
PC = ReadWord(0xFFFE);
|
||||
}
|
||||
|
||||
public string State()
|
||||
{
|
||||
int notused;
|
||||
string a = string.Format("{0:X4} {1:X2} {2} ", PC, ReadMemory(PC), Disassemble(PC, out notused)).PadRight(41);
|
||||
string b = string.Format("A:{0:X2} X:{1:X2} Y:{2:X2} P:{3:X2} SP:{4:X2} Cy:{5}", A, X, Y, P, S, TotalExecutedCycles);
|
||||
string val = a + b + " ";
|
||||
if (FlagN) val = val + "N";
|
||||
if (FlagV) val = val + "V";
|
||||
if (FlagT) val = val + "T";
|
||||
if (FlagB) val = val + "B";
|
||||
if (FlagD) val = val + "D";
|
||||
if (FlagI) val = val + "I";
|
||||
if (FlagZ) val = val + "Z";
|
||||
if (FlagC) val = val + "C";
|
||||
return val;
|
||||
}
|
||||
|
||||
// ==== CPU State ====
|
||||
|
||||
public byte A;
|
||||
public byte X;
|
||||
public byte Y;
|
||||
public byte P;
|
||||
public ushort PC;
|
||||
public byte S;
|
||||
|
||||
// TODO IRQ, NMI functions
|
||||
public bool Interrupt;
|
||||
public bool NMI;//
|
||||
|
||||
// ==== End State ====
|
||||
|
||||
/// <summary>Carry Flag</summary>
|
||||
private bool FlagC
|
||||
{
|
||||
get { return (P & 0x01) != 0; }
|
||||
set { P = (byte)((P & ~0x01) | (value ? 0x01 : 0x00)); }
|
||||
}
|
||||
|
||||
/// <summary>Zero Flag</summary>
|
||||
private bool FlagZ
|
||||
{
|
||||
get { return (P & 0x02) != 0; }
|
||||
set { P = (byte)((P & ~0x02) | (value ? 0x02 : 0x00)); }
|
||||
}
|
||||
|
||||
/// <summary>Interrupt Disable Flag</summary>
|
||||
private bool FlagI
|
||||
{
|
||||
get { return (P & 0x04) != 0; }
|
||||
set { P = (byte)((P & ~0x04) | (value ? 0x04 : 0x00)); }
|
||||
}
|
||||
|
||||
/// <summary>Decimal Mode Flag</summary>
|
||||
private bool FlagD
|
||||
{
|
||||
get { return (P & 0x08) != 0; }
|
||||
set { P = (byte)((P & ~0x08) | (value ? 0x08 : 0x00)); }
|
||||
}
|
||||
|
||||
/// <summary>Break Flag</summary>
|
||||
private bool FlagB
|
||||
{
|
||||
get { return (P & 0x10) != 0; }
|
||||
set { P = (byte)((P & ~0x10) | (value ? 0x10 : 0x00)); }
|
||||
}
|
||||
|
||||
/// <summary>T... Flag</summary>
|
||||
private bool FlagT
|
||||
{
|
||||
get { return (P & 0x20) != 0; }
|
||||
set { P = (byte)((P & ~0x20) | (value ? 0x20 : 0x00)); }
|
||||
}
|
||||
|
||||
/// <summary>Overflow Flag</summary>
|
||||
private bool FlagV
|
||||
{
|
||||
get { return (P & 0x40) != 0; }
|
||||
set { P = (byte)((P & ~0x40) | (value ? 0x40 : 0x00)); }
|
||||
}
|
||||
|
||||
/// <summary>Negative Flag</summary>
|
||||
private bool FlagN
|
||||
{
|
||||
get { return (P & 0x80) != 0; }
|
||||
set { P = (byte)((P & ~0x80) | (value ? 0x80 : 0x00)); }
|
||||
}
|
||||
|
||||
public int TotalExecutedCycles;
|
||||
public int PendingCycles;
|
||||
|
||||
public Func<ushort, byte> ReadMemory;
|
||||
public Action<ushort, byte> WriteMemory;
|
||||
|
||||
public void UnregisterMemoryMapper()
|
||||
{
|
||||
ReadMemory = null;
|
||||
WriteMemory = null;
|
||||
}
|
||||
|
||||
private ushort ReadWord(ushort address)
|
||||
{
|
||||
byte l = ReadMemory(address);
|
||||
byte h = ReadMemory(++address);
|
||||
return (ushort)((h << 8) | l);
|
||||
}
|
||||
|
||||
private void WriteWord(ushort address, ushort value)
|
||||
{
|
||||
byte l = (byte)(value & 0xFF);
|
||||
byte h = (byte)(value >> 8);
|
||||
WriteMemory(address, l);
|
||||
WriteMemory(++address, h);
|
||||
}
|
||||
|
||||
private ushort ReadWordPageWrap(ushort address)
|
||||
{
|
||||
ushort highAddress = (ushort)((address & 0xFF00) + ((address + 1) & 0xFF));
|
||||
return (ushort)(ReadMemory(address) | (ReadMemory(highAddress) << 8));
|
||||
}
|
||||
|
||||
private static readonly byte[] TableNZ =
|
||||
{
|
||||
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
/** GBDASM: GameBoy Disassembler ******************************/
|
||||
/** **/
|
||||
/** gbdasm.c **/
|
||||
/** **/
|
||||
/** This file contains the source of a portable disassembler **/
|
||||
/** for the customized Z80 CPU used in GameBoy handheld **/
|
||||
/** videogame console. **/
|
||||
/** **/
|
||||
/** Copyright (C) Marat Fayzullin 1995-2001 **/
|
||||
/** You are not allowed to distribute this software **/
|
||||
/** commercially. Please, notify me, if you make any **/
|
||||
/** changes to this file. **/
|
||||
/**************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.CPUs.Z80GB
|
||||
{
|
||||
public static class Disassembler
|
||||
{
|
||||
readonly static string[] mnemonics = new string[]
|
||||
{
|
||||
"NOP","LD BC,#h","LD (BC),A","INC BC","INC B","DEC B","LD B,*h","RLCA",
|
||||
"LD (#h),SP","ADD HL,BC","LD A,(BC)","DEC BC","INC C","DEC C","LD C,*h","RRCA",
|
||||
"STOP","LD DE,#h","LD (DE),A","INC DE","INC D","DEC D","LD D,*h","RLA",
|
||||
"JR @h","ADD HL,DE","LD A,(DE)","DEC DE","INC E","DEC E","LD E,*h","RRA",
|
||||
"JR NZ,@h","LD HL,#h","LD (HL+),A","INC HL","INC H","DEC H","LD H,*h","DAA",
|
||||
"JR Z,@h","ADD HL,HL","LD A,(HL+)","DEC HL","INC L","DEC L","LD L,*h","CPL",
|
||||
"JR NC,@h","LD SP,#h","LD (HL-),A","INC SP","INC (HL)","DEC (HL)","LD (HL),*h","SCF",
|
||||
"JR C,@h","ADD HL,SP","LD A,(HL-)","DEC SP","INC A","DEC A","LD A,*h","CCF",
|
||||
"LD B,B","LD B,C","LD B,D","LD B,E","LD B,H","LD B,L","LD B,(HL)","LD B,A",
|
||||
"LD C,B","LD C,C","LD C,D","LD C,E","LD C,H","LD C,L","LD C,(HL)","LD C,A",
|
||||
"LD D,B","LD D,C","LD D,D","LD D,E","LD D,H","LD D,L","LD D,(HL)","LD D,A",
|
||||
"LD E,B","LD E,C","LD E,D","LD E,E","LD E,H","LD E,L","LD E,(HL)","LD E,A",
|
||||
"LD H,B","LD H,C","LD H,D","LD H,E","LD H,H","LD H,L","LD H,(HL)","LD H,A",
|
||||
"LD L,B","LD L,C","LD L,D","LD L,E","LD L,H","LD L,L","LD L,(HL)","LD L,A",
|
||||
"LD (HL),B","LD (HL),C","LD (HL),D","LD (HL),E","LD (HL),H","LD (HL),L","HALT","LD (HL),A",
|
||||
"LD A,B","LD A,C","LD A,D","LD A,E","LD A,H","LD A,L","LD A,(HL)","LD A,A",
|
||||
"ADD B","ADD C","ADD D","ADD E","ADD H","ADD L","ADD (HL)","ADD A",
|
||||
"ADC B","ADC C","ADC D","ADC E","ADC H","ADC L","ADC (HL)","ADC A",
|
||||
"SUB B","SUB C","SUB D","SUB E","SUB H","SUB L","SUB (HL)","SUB A",
|
||||
"SBC B","SBC C","SBC D","SBC E","SBC H","SBC L","SBC (HL)","SBC A",
|
||||
"AND B","AND C","AND D","AND E","AND H","AND L","AND (HL)","AND A",
|
||||
"XOR B","XOR C","XOR D","XOR E","XOR H","XOR L","XOR (HL)","XOR A",
|
||||
"OR B","OR C","OR D","OR E","OR H","OR L","OR (HL)","OR A",
|
||||
"CP B","CP C","CP D","CP E","CP H","CP L","CP (HL)","CP A",
|
||||
"RET NZ","POP BC","JP NZ,#h","JP #h","CALL NZ,#h","PUSH BC","ADD *h","RST 00h",
|
||||
"RET Z","RET","JP Z,#h","PREFIX CBh","CALL Z,#h","CALL #h","ADC *h","RST 08h",
|
||||
"RET NC","POP DE","JP NC,#h","DB D3h","CALL NC,#h","PUSH DE","SUB *h","RST 10h",
|
||||
"RET C","RETI","JP C,#h","DB DBh","CALL C,#h","DB DDh","SBC *h","RST 18h",
|
||||
"LD (FF*h),A","POP HL","LD (FF00h+C),A","DB E3h","DB E4h","PUSH HL","AND *h","RST 20h",
|
||||
"ADD SP,@h","LD PC,HL","LD (#h),A","DB EBh","DB ECh","PREFIX EDh","XOR *h","RST 28h",
|
||||
"LD A,(FF*h)","POP AF","LD A,(FF00h+C)","DI","DB F4h","PUSH AF","OR *h","RST 30h",
|
||||
"LDHL SP,@h","LD SP,HL","LD A,(#h)","EI","DB FCh","DB FDh","CP *h","RST 38h"
|
||||
};
|
||||
|
||||
readonly static string[] mnemonicsCB = new string[]
|
||||
{
|
||||
"RLC B","RLC C","RLC D","RLC E","RLC H","RLC L","RLC (HL)","RLC A",
|
||||
"RRC B","RRC C","RRC D","RRC E","RRC H","RRC L","RRC (HL)","RRC A",
|
||||
"RL B","RL C","RL D","RL E","RL H","RL L","RL (HL)","RL A",
|
||||
"RR B","RR C","RR D","RR E","RR H","RR L","RR (HL)","RR A",
|
||||
"SLA B","SLA C","SLA D","SLA E","SLA H","SLA L","SLA (HL)","SLA A",
|
||||
"SRA B","SRA C","SRA D","SRA E","SRA H","SRA L","SRA (HL)","SRA A",
|
||||
"SWAP B","SWAP C","SWAP D","SWAP E","SWAP H","SWAP L","SWAP (HL)","SWAP A",
|
||||
"SRL B","SRL C","SRL D","SRL E","SRL H","SRL L","SRL (HL)","SRL A",
|
||||
"BIT 0,B","BIT 0,C","BIT 0,D","BIT 0,E","BIT 0,H","BIT 0,L","BIT 0,(HL)","BIT 0,A",
|
||||
"BIT 1,B","BIT 1,C","BIT 1,D","BIT 1,E","BIT 1,H","BIT 1,L","BIT 1,(HL)","BIT 1,A",
|
||||
"BIT 2,B","BIT 2,C","BIT 2,D","BIT 2,E","BIT 2,H","BIT 2,L","BIT 2,(HL)","BIT 2,A",
|
||||
"BIT 3,B","BIT 3,C","BIT 3,D","BIT 3,E","BIT 3,H","BIT 3,L","BIT 3,(HL)","BIT 3,A",
|
||||
"BIT 4,B","BIT 4,C","BIT 4,D","BIT 4,E","BIT 4,H","BIT 4,L","BIT 4,(HL)","BIT 4,A",
|
||||
"BIT 5,B","BIT 5,C","BIT 5,D","BIT 5,E","BIT 5,H","BIT 5,L","BIT 5,(HL)","BIT 5,A",
|
||||
"BIT 6,B","BIT 6,C","BIT 6,D","BIT 6,E","BIT 6,H","BIT 6,L","BIT 6,(HL)","BIT 6,A",
|
||||
"BIT 7,B","BIT 7,C","BIT 7,D","BIT 7,E","BIT 7,H","BIT 7,L","BIT 7,(HL)","BIT 7,A",
|
||||
"RES 0,B","RES 0,C","RES 0,D","RES 0,E","RES 0,H","RES 0,L","RES 0,(HL)","RES 0,A",
|
||||
"RES 1,B","RES 1,C","RES 1,D","RES 1,E","RES 1,H","RES 1,L","RES 1,(HL)","RES 1,A",
|
||||
"RES 2,B","RES 2,C","RES 2,D","RES 2,E","RES 2,H","RES 2,L","RES 2,(HL)","RES 2,A",
|
||||
"RES 3,B","RES 3,C","RES 3,D","RES 3,E","RES 3,H","RES 3,L","RES 3,(HL)","RES 3,A",
|
||||
"RES 4,B","RES 4,C","RES 4,D","RES 4,E","RES 4,H","RES 4,L","RES 4,(HL)","RES 4,A",
|
||||
"RES 5,B","RES 5,C","RES 5,D","RES 5,E","RES 5,H","RES 5,L","RES 5,(HL)","RES 5,A",
|
||||
"RES 6,B","RES 6,C","RES 6,D","RES 6,E","RES 6,H","RES 6,L","RES 6,(HL)","RES 6,A",
|
||||
"RES 7,B","RES 7,C","RES 7,D","RES 7,E","RES 7,H","RES 7,L","RES 7,(HL)","RES 7,A",
|
||||
"SET 0,B","SET 0,C","SET 0,D","SET 0,E","SET 0,H","SET 0,L","SET 0,(HL)","SET 0,A",
|
||||
"SET 1,B","SET 1,C","SET 1,D","SET 1,E","SET 1,H","SET 1,L","SET 1,(HL)","SET 1,A",
|
||||
"SET 2,B","SET 2,C","SET 2,D","SET 2,E","SET 2,H","SET 2,L","SET 2,(HL)","SET 2,A",
|
||||
"SET 3,B","SET 3,C","SET 3,D","SET 3,E","SET 3,H","SET 3,L","SET 3,(HL)","SET 3,A",
|
||||
"SET 4,B","SET 4,C","SET 4,D","SET 4,E","SET 4,H","SET 4,L","SET 4,(HL)","SET 4,A",
|
||||
"SET 5,B","SET 5,C","SET 5,D","SET 5,E","SET 5,H","SET 5,L","SET 5,(HL)","SET 5,A",
|
||||
"SET 6,B","SET 6,C","SET 6,D","SET 6,E","SET 6,H","SET 6,L","SET 6,(HL)","SET 6,A",
|
||||
"SET 7,B","SET 7,C","SET 7,D","SET 7,E","SET 7,H","SET 7,L","SET 7,(HL)","SET 7,A"
|
||||
};
|
||||
|
||||
class OpcodeReader
|
||||
{
|
||||
public OpcodeReader(ushort addr, Func<ushort, byte> reader)
|
||||
{
|
||||
this.startAddr = addr;
|
||||
this.addr = addr;
|
||||
this.reader = reader;
|
||||
}
|
||||
ushort addr, startAddr;
|
||||
|
||||
Func<ushort, byte> reader;
|
||||
int[] bytes = new int[] {-1,-1,-1};
|
||||
int bytectr = 0;
|
||||
public int Size { get { return bytectr; } }
|
||||
public byte Read()
|
||||
{
|
||||
byte val = reader(addr++); ;
|
||||
bytes[bytectr] = val;
|
||||
bytectr++;
|
||||
return val;
|
||||
}
|
||||
|
||||
public string Format(string mnemonic)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder(32);
|
||||
sb.AppendFormat("{0:X4}:", startAddr);
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
if (bytectr > i)
|
||||
sb.AppendFormat("{0:X2} ", bytes[i]);
|
||||
else sb.AppendFormat(" ");
|
||||
}
|
||||
sb.AppendFormat(" {0}",mnemonic);
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public static string DAsm(ushort addr, Func<ushort,byte> reader, out ushort size)
|
||||
{
|
||||
string ret;
|
||||
OpcodeReader or = new OpcodeReader(addr, reader);
|
||||
|
||||
byte A = or.Read();
|
||||
string str;
|
||||
switch (A)
|
||||
{
|
||||
case 0xCB:
|
||||
str = mnemonicsCB[or.Read()];
|
||||
break;
|
||||
default:
|
||||
str = mnemonics[A];
|
||||
break;
|
||||
}
|
||||
|
||||
if (str.IndexOf('*') != -1)
|
||||
{
|
||||
ret = str.Replace("*", string.Format("{0:X2}", or.Read()));
|
||||
}
|
||||
else if (str.IndexOf('@') != -1)
|
||||
{
|
||||
byte B = or.Read();
|
||||
bool neg = ((B & 0x80) != 0);
|
||||
char sign = neg ? '-' : '+';
|
||||
int val = neg ? 256 - B : B;
|
||||
ret = str.Replace("@", string.Format("{0}{1:X2}", sign, val));
|
||||
}
|
||||
else if (str.IndexOf('#') != -1)
|
||||
{
|
||||
byte B = or.Read();
|
||||
byte C = or.Read();
|
||||
ret = str.Replace("#", string.Format("{0:X4}", B + C * 256));
|
||||
}
|
||||
else ret = str;
|
||||
|
||||
size = (ushort)or.Size;
|
||||
return or.Format(ret);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,52 @@
|
|||
using System;
|
||||
|
||||
namespace BizHawk.Emulation.CPUs.Z80GB
|
||||
{
|
||||
public partial class Z80
|
||||
{
|
||||
private bool iff1;
|
||||
public bool IFF1 { get { return iff1; } set { iff1 = value; } }
|
||||
|
||||
private bool iff2;
|
||||
public bool IFF2 { get { return iff2; } set { iff2 = value; } }
|
||||
|
||||
private bool interrupt;
|
||||
public bool Interrupt { get { return interrupt; } set { interrupt = value; } }
|
||||
|
||||
private bool nonMaskableInterrupt;
|
||||
public bool NonMaskableInterrupt
|
||||
{
|
||||
get { return nonMaskableInterrupt; }
|
||||
set { if (value && !nonMaskableInterrupt) NonMaskableInterruptPending = true; nonMaskableInterrupt = value; }
|
||||
}
|
||||
|
||||
private bool nonMaskableInterruptPending;
|
||||
public bool NonMaskableInterruptPending { get { return nonMaskableInterruptPending; } set { nonMaskableInterruptPending = value; } }
|
||||
|
||||
private int interruptMode;
|
||||
public int InterruptMode
|
||||
{
|
||||
get { return interruptMode; }
|
||||
set { if (value < 0 || value > 2) throw new ArgumentOutOfRangeException(); interruptMode = value; }
|
||||
}
|
||||
|
||||
private bool halted;
|
||||
public bool Halted { get { return halted; } set { halted = value; } }
|
||||
|
||||
private void ResetInterrupts()
|
||||
{
|
||||
IFF1 = false;
|
||||
IFF2 = false;
|
||||
Interrupt = false;
|
||||
NonMaskableInterrupt = false;
|
||||
NonMaskableInterruptPending = false;
|
||||
InterruptMode = 1;
|
||||
Halted = false;
|
||||
}
|
||||
|
||||
private void Halt()
|
||||
{
|
||||
Halted = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
using System.Runtime.InteropServices;
|
||||
using System;
|
||||
|
||||
namespace BizHawk.Emulation.CPUs.Z80GB
|
||||
{
|
||||
public partial class Z80
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
[Serializable]
|
||||
public struct RegisterPair
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public ushort Word;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public byte Low;
|
||||
|
||||
[FieldOffset(1)]
|
||||
public byte High;
|
||||
|
||||
public RegisterPair(ushort value)
|
||||
{
|
||||
Word = value;
|
||||
Low = (byte)(Word);
|
||||
High = (byte)(Word >> 8);
|
||||
}
|
||||
|
||||
public static implicit operator ushort(RegisterPair rp)
|
||||
{
|
||||
return rp.Word;
|
||||
}
|
||||
|
||||
public static implicit operator RegisterPair(ushort value)
|
||||
{
|
||||
return new RegisterPair(value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool FlagC
|
||||
{
|
||||
get { return (RegAF.Low & 0x10) != 0; }
|
||||
set { RegAF.Low = (byte)((RegAF.Low & ~0x10) | (value ? 0x10 : 0x00)); }
|
||||
}
|
||||
|
||||
public bool FlagH
|
||||
{
|
||||
get { return (RegAF.Low & 0x20) != 0; }
|
||||
set { RegAF.Low = (byte)((RegAF.Low & ~0x20) | (value ? 0x20 : 0x00)); }
|
||||
}
|
||||
|
||||
public bool FlagN
|
||||
{
|
||||
get { return (RegAF.Low & 0x40) != 0; }
|
||||
set { RegAF.Low = (byte)((RegAF.Low & ~0x40) | (value ? 0x40 : 0x00)); }
|
||||
}
|
||||
|
||||
public bool FlagZ
|
||||
{
|
||||
get { return (RegAF.Low & 0x80) != 0; }
|
||||
set { RegAF.Low = (byte)((RegAF.Low & ~0x80) | (value ? 0x80 : 0x00)); }
|
||||
}
|
||||
|
||||
private RegisterPair RegAF;
|
||||
private RegisterPair RegBC;
|
||||
private RegisterPair RegDE;
|
||||
private RegisterPair RegHL;
|
||||
|
||||
private byte RegI; // I (interrupt vector)
|
||||
|
||||
private RegisterPair RegSP; // SP (stack pointer)
|
||||
private RegisterPair RegPC; // PC (program counter)
|
||||
|
||||
private void ResetRegisters()
|
||||
{
|
||||
RegAF = 0; RegBC = 0; RegDE = 0; RegHL = 0;
|
||||
RegI = 0;
|
||||
RegSP.Word = 0; RegPC.Word = 0;
|
||||
}
|
||||
|
||||
public byte RegisterA
|
||||
{
|
||||
get { return RegAF.High; }
|
||||
set { RegAF.High = value; }
|
||||
}
|
||||
|
||||
public byte RegisterF
|
||||
{
|
||||
get { return RegAF.Low; }
|
||||
set { RegAF.Low = (byte)(value&0xF0); }
|
||||
}
|
||||
|
||||
public ushort RegisterAF
|
||||
{
|
||||
get { return RegAF.Word; }
|
||||
set { RegAF.Word = (byte)(value&0xFFF0); }
|
||||
}
|
||||
|
||||
public byte RegisterB
|
||||
{
|
||||
get { return RegBC.High; }
|
||||
set { RegBC.High = value; }
|
||||
}
|
||||
|
||||
public byte RegisterC
|
||||
{
|
||||
get { return RegBC.Low; }
|
||||
set { RegBC.Low = value; }
|
||||
}
|
||||
|
||||
public ushort RegisterBC
|
||||
{
|
||||
get { return RegBC.Word; }
|
||||
set { RegBC.Word = value; }
|
||||
}
|
||||
|
||||
public byte RegisterD
|
||||
{
|
||||
get { return RegDE.High; }
|
||||
set { RegDE.High = value; }
|
||||
}
|
||||
|
||||
public byte RegisterE
|
||||
{
|
||||
get { return RegDE.Low; }
|
||||
set { RegDE.Low = value; }
|
||||
}
|
||||
public ushort RegisterDE
|
||||
{
|
||||
get { return RegDE.Word; }
|
||||
set { RegDE.Word = value; }
|
||||
}
|
||||
|
||||
public byte RegisterH
|
||||
{
|
||||
get { return RegHL.High; }
|
||||
set { RegHL.High = value; }
|
||||
}
|
||||
|
||||
public byte RegisterL
|
||||
{
|
||||
get { return RegHL.Low; }
|
||||
set { RegHL.Low = value; }
|
||||
}
|
||||
public ushort RegisterHL
|
||||
{
|
||||
get { return RegHL.Word; }
|
||||
set { RegHL.Word = value; }
|
||||
}
|
||||
|
||||
public ushort RegisterPC
|
||||
{
|
||||
get { return RegPC.Word; }
|
||||
set { RegPC.Word = value; }
|
||||
}
|
||||
public ushort RegisterSP
|
||||
{
|
||||
get { return RegSP.Word; }
|
||||
set { RegSP.Word = value; }
|
||||
}
|
||||
public byte RegisterI
|
||||
{
|
||||
get { return RegI; }
|
||||
set { RegI = value; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
namespace BizHawk.Emulation.CPUs.Z80GB
|
||||
{
|
||||
public partial class Z80
|
||||
{
|
||||
private void InitializeTables()
|
||||
{
|
||||
InitTableDaa();
|
||||
}
|
||||
|
||||
private static readonly byte[] IncTable =
|
||||
{
|
||||
160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
private static readonly byte[] DecTable =
|
||||
{
|
||||
192, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96,
|
||||
064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96,
|
||||
064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96,
|
||||
064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96,
|
||||
064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96,
|
||||
064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96,
|
||||
064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96,
|
||||
064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96,
|
||||
064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96,
|
||||
064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96,
|
||||
064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96,
|
||||
064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96,
|
||||
064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96,
|
||||
064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96,
|
||||
064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96,
|
||||
064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96
|
||||
};
|
||||
|
||||
private static readonly byte[] SwapTable =
|
||||
{
|
||||
0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0,
|
||||
0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, 0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1,
|
||||
0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72, 0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2,
|
||||
0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73, 0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3,
|
||||
0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74, 0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4,
|
||||
0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, 0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5,
|
||||
0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76, 0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6,
|
||||
0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7,
|
||||
0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78, 0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8,
|
||||
0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79, 0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9,
|
||||
0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A, 0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA,
|
||||
0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B, 0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB,
|
||||
0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C, 0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC,
|
||||
0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D, 0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD,
|
||||
0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E, 0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE,
|
||||
0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F, 0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF
|
||||
};
|
||||
|
||||
private static readonly byte[] mCycleTable = new byte[]
|
||||
{
|
||||
1, 3, 2, 2, 1, 1, 2, 1, 5, 2, 2, 2, 1, 1, 2, 1,
|
||||
1, 3, 2, 2, 1, 1, 2, 1, 3, 2, 2, 2, 1, 1, 2, 1,
|
||||
3, 3, 2, 2, 1, 1, 2, 1, 3, 2, 2, 2, 1, 1, 2, 1,
|
||||
3, 3, 2, 2, 1, 3, 3, 3, 3, 2, 2, 2, 1, 1, 2, 1,
|
||||
1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
|
||||
1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
|
||||
1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
|
||||
2, 2, 2, 2, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1,
|
||||
1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
|
||||
1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
|
||||
1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
|
||||
1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
|
||||
5, 3, 4, 4, 6, 4, 2, 4, 5, 4, 4, 1, 6, 6, 2, 4,
|
||||
5, 3, 4, 0, 6, 4, 2, 4, 5, 4, 4, 0, 6, 0, 2, 4,
|
||||
3, 3, 2, 0, 0, 4, 2, 4, 4, 1, 4, 0, 0, 0, 2, 4,
|
||||
3, 3, 2, 1, 0, 4, 2, 4, 3, 2, 4, 1, 0, 0, 2, 4,
|
||||
};
|
||||
|
||||
private static readonly byte[] cbMCycleTable = new byte[]
|
||||
{
|
||||
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, 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, 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, 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, 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, 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, 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,
|
||||
};
|
||||
|
||||
private ushort[] TableDaa;
|
||||
private void InitTableDaa()
|
||||
{
|
||||
TableDaa = new ushort[65536];
|
||||
for (int af = 0; af < 65536; ++af)
|
||||
{
|
||||
byte a = (byte)(af >> 8);
|
||||
byte tmp = a;
|
||||
|
||||
if (IsN(af))
|
||||
{
|
||||
if (IsH(af) || ((a & 0x0F) > 0x09)) tmp -= 0x06;
|
||||
if (IsC(af) || a > 0x99) tmp -= 0x60;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsH(af) || ((a & 0x0F) > 0x09)) tmp += 0x06;
|
||||
if (IsC(af) || a > 0x99) tmp += 0x60;
|
||||
}
|
||||
|
||||
TableDaa[af] = (ushort)((tmp * 256) + FlagByte(IsC(af) || a > 0x99, ((a ^ tmp) & 0x10) != 0, IsN(af), tmp == 0));
|
||||
}
|
||||
}
|
||||
|
||||
private static byte FlagByte(bool C, bool H, bool N, bool Z)
|
||||
{
|
||||
return (byte)(
|
||||
(C ? 0x10 : 0) +
|
||||
(H ? 0x20 : 0) +
|
||||
(N ? 0x40 : 0) +
|
||||
(Z ? 0x80 : 0)
|
||||
);
|
||||
}
|
||||
|
||||
private static bool IsC(int value) { return (value & 0x10) != 0; }
|
||||
private static bool IsH(int value) { return (value & 0x20) != 0; }
|
||||
private static bool IsN(int value) { return (value & 0x40) != 0; }
|
||||
private static bool IsZ(int value) { return (value & 0x80) != 0; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
|
||||
// This Z80-Gameboy emulator is a modified version of Ben Ryves 'Brazil' emulator.
|
||||
// It is MIT licensed (not public domain). (See Licenses)
|
||||
|
||||
namespace BizHawk.Emulation.CPUs.Z80GB
|
||||
{
|
||||
public sealed partial class Z80
|
||||
{
|
||||
public Z80()
|
||||
{
|
||||
InitializeTables();
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
ResetRegisters();
|
||||
ResetInterrupts();
|
||||
PendingCycles = 0;
|
||||
TotalExecutedCycles = 0;
|
||||
}
|
||||
|
||||
// Memory Access
|
||||
|
||||
public Func<ushort, byte> ReadMemory;
|
||||
public Action<ushort, byte> WriteMemory;
|
||||
|
||||
public void UnregisterMemoryMapper()
|
||||
{
|
||||
ReadMemory = null;
|
||||
WriteMemory = null;
|
||||
}
|
||||
|
||||
// State Save/Load
|
||||
|
||||
public void SaveStateText(TextWriter writer)
|
||||
{
|
||||
writer.WriteLine("[Z80]");
|
||||
writer.WriteLine("AF {0:X4}", RegAF.Word);
|
||||
writer.WriteLine("BC {0:X4}", RegBC.Word);
|
||||
writer.WriteLine("DE {0:X4}", RegDE.Word);
|
||||
writer.WriteLine("HL {0:X4}", RegHL.Word);
|
||||
writer.WriteLine("I {0:X2}", RegI);
|
||||
writer.WriteLine("SP {0:X4}", RegSP.Word);
|
||||
writer.WriteLine("PC {0:X4}", RegPC.Word);
|
||||
writer.WriteLine("IRQ {0}", interrupt);
|
||||
writer.WriteLine("NMI {0}", nonMaskableInterrupt);
|
||||
writer.WriteLine("NMIPending {0}", nonMaskableInterruptPending);
|
||||
writer.WriteLine("IM {0}", InterruptMode);
|
||||
writer.WriteLine("IFF1 {0}", IFF1);
|
||||
writer.WriteLine("IFF2 {0}", IFF2);
|
||||
writer.WriteLine("Halted {0}", Halted);
|
||||
writer.WriteLine("ExecutedCycles {0}", TotalExecutedCycles);
|
||||
writer.WriteLine("PendingCycles {0}", PendingCycles);
|
||||
writer.WriteLine("[/Z80]");
|
||||
writer.WriteLine();
|
||||
}
|
||||
|
||||
public void LoadStateText(TextReader reader)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
string[] args = reader.ReadLine().Split(' ');
|
||||
if (args[0].Trim() == "") continue;
|
||||
if (args[0] == "[/Z80]") break;
|
||||
if (args[0] == "AF")
|
||||
RegAF.Word = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "BC")
|
||||
RegBC.Word = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "DE")
|
||||
RegDE.Word = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "HL")
|
||||
RegHL.Word = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "I")
|
||||
RegI = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "SP")
|
||||
RegSP.Word = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "PC")
|
||||
RegPC.Word = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "IRQ")
|
||||
interrupt = bool.Parse(args[1]);
|
||||
else if (args[0] == "NMI")
|
||||
nonMaskableInterrupt = bool.Parse(args[1]);
|
||||
else if (args[0] == "NMIPending")
|
||||
nonMaskableInterruptPending = bool.Parse(args[1]);
|
||||
else if (args[0] == "IM")
|
||||
InterruptMode = int.Parse(args[1]);
|
||||
else if (args[0] == "IFF1")
|
||||
IFF1 = bool.Parse(args[1]);
|
||||
else if (args[0] == "IFF2")
|
||||
IFF2 = bool.Parse(args[1]);
|
||||
else if (args[0] == "Halted")
|
||||
Halted = bool.Parse(args[1]);
|
||||
else if (args[0] == "ExecutedCycles")
|
||||
TotalExecutedCycles = int.Parse(args[1]);
|
||||
else if (args[0] == "PendingCycles")
|
||||
PendingCycles = int.Parse(args[1]);
|
||||
|
||||
else
|
||||
Console.WriteLine("Skipping unrecognized identifier " + args[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveStateBinary(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(RegAF.Word);
|
||||
writer.Write(RegBC.Word);
|
||||
writer.Write(RegDE.Word);
|
||||
writer.Write(RegHL.Word);
|
||||
writer.Write(RegI);
|
||||
writer.Write(RegSP.Word);
|
||||
writer.Write(RegPC.Word);
|
||||
writer.Write(interrupt);
|
||||
writer.Write(nonMaskableInterrupt);
|
||||
writer.Write(nonMaskableInterruptPending);
|
||||
writer.Write(InterruptMode);
|
||||
writer.Write(IFF1);
|
||||
writer.Write(IFF2);
|
||||
writer.Write(Halted);
|
||||
writer.Write(TotalExecutedCycles);
|
||||
writer.Write(PendingCycles);
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader reader)
|
||||
{
|
||||
RegAF.Word = reader.ReadUInt16();
|
||||
RegBC.Word = reader.ReadUInt16();
|
||||
RegDE.Word = reader.ReadUInt16();
|
||||
RegHL.Word = reader.ReadUInt16();
|
||||
RegI = reader.ReadByte();
|
||||
RegSP.Word = reader.ReadUInt16();
|
||||
RegPC.Word = reader.ReadUInt16();
|
||||
interrupt = reader.ReadBoolean();
|
||||
nonMaskableInterrupt = reader.ReadBoolean();
|
||||
nonMaskableInterruptPending = reader.ReadBoolean();
|
||||
InterruptMode = reader.ReadInt32();
|
||||
IFF1 = reader.ReadBoolean();
|
||||
IFF2 = reader.ReadBoolean();
|
||||
Halted = reader.ReadBoolean();
|
||||
TotalExecutedCycles = reader.ReadInt32();
|
||||
PendingCycles = reader.ReadInt32();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,297 @@
|
|||
using System;
|
||||
|
||||
namespace BizHawk.Emulation.CPUs.Z80
|
||||
{
|
||||
public partial class Z80A
|
||||
{
|
||||
#region Mnemonics
|
||||
private static readonly string[] Opcodes = new[]
|
||||
{
|
||||
"NOP","LD BC,#h","LD (BC),A","INC BC","INC B","DEC B","LD B,*h","RLCA",
|
||||
"EX AF,AF'","ADD HL,BC","LD A,(BC)","DEC BC","INC C","DEC C","LD C,*h","RRCA",
|
||||
"DJNZ @h","LD DE,#h","LD (DE),A","INC DE","INC D","DEC D","LD D,*h","RLA",
|
||||
"JR @h","ADD HL,DE","LD A,(DE)","DEC DE","INC E","DEC E","LD E,*h","RRA",
|
||||
"JR NZ,@h","LD HL,#h","LD (#h),HL","INC HL","INC H","DEC H","LD H,*h","DAA",
|
||||
"JR Z,@h","ADD HL,HL","LD HL,(#h)","DEC HL","INC L","DEC L","LD L,*h","CPL",
|
||||
"JR NC,@h","LD SP,#h","LD (#h),A","INC SP","INC (HL)","DEC (HL)","LD (HL),*h","SCF",
|
||||
"JR C,@h","ADD HL,SP","LD A,(#h)","DEC SP","INC A","DEC A","LD A,*h","CCF",
|
||||
"LD B,B","LD B,C","LD B,D","LD B,E","LD B,H","LD B,L","LD B,(HL)","LD B,A",
|
||||
"LD C,B","LD C,C","LD C,D","LD C,E","LD C,H","LD C,L","LD C,(HL)","LD C,A",
|
||||
"LD D,B","LD D,C","LD D,D","LD D,E","LD D,H","LD D,L","LD D,(HL)","LD D,A",
|
||||
"LD E,B","LD E,C","LD E,D","LD E,E","LD E,H","LD E,L","LD E,(HL)","LD E,A",
|
||||
"LD H,B","LD H,C","LD H,D","LD H,E","LD H,H","LD H,L","LD H,(HL)","LD H,A",
|
||||
"LD L,B","LD L,C","LD L,D","LD L,E","LD L,H","LD L,L","LD L,(HL)","LD L,A",
|
||||
"LD (HL),B","LD (HL),C","LD (HL),D","LD (HL),E","LD (HL),H","LD (HL),L","HALT","LD (HL),A",
|
||||
"LD A,B","LD A,C","LD A,D","LD A,E","LD A,H","LD A,L","LD A,(HL)","LD A,A",
|
||||
"ADD B","ADD C","ADD D","ADD E","ADD H","ADD L","ADD (HL)","ADD A",
|
||||
"ADC B","ADC C","ADC D","ADC E","ADC H","ADC L","ADC (HL)","ADC A",
|
||||
"SUB B","SUB C","SUB D","SUB E","SUB H","SUB L","SUB (HL)","SUB A",
|
||||
"SBC B","SBC C","SBC D","SBC E","SBC H","SBC L","SBC (HL)","SBC A",
|
||||
"AND B","AND C","AND D","AND E","AND H","AND L","AND (HL)","AND A",
|
||||
"XOR B","XOR C","XOR D","XOR E","XOR H","XOR L","XOR (HL)","XOR A",
|
||||
"OR B","OR C","OR D","OR E","OR H","OR L","OR (HL)","OR A",
|
||||
"CP B","CP C","CP D","CP E","CP H","CP L","CP (HL)","CP A",
|
||||
"RET NZ","POP BC","JP NZ,#h","JP #h","CALL NZ,#h","PUSH BC","ADD *h","RST 00h",
|
||||
"RET Z","RET","JP Z,#h","PFX_CB","CALL Z,#h","CALL #h","ADC *h","RST 08h",
|
||||
"RET NC","POP DE","JP NC,#h","OUTA (*h)","CALL NC,#h","PUSH DE","SUB *h","RST 10h",
|
||||
"RET C","EXX","JP C,#h","INA (*h)","CALL C,#h","PFX_DD","SBC *h","RST 18h",
|
||||
"RET PO","POP HL","JP PO,#h","EX HL,(SP)","CALL PO,#h","PUSH HL","AND *h","RST 20h",
|
||||
"RET PE","LD PC,HL","JP PE,#h","EX DE,HL","CALL PE,#h","PFX_ED","XOR *h","RST 28h",
|
||||
"RET P","POP AF","JP P,#h","DI","CALL P,#h","PUSH AF","OR *h","RST 30h",
|
||||
"RET M","LD SP,HL","JP M,#h","EI","CALL M,#h","PFX_FD","CP *h","RST 38h"
|
||||
};
|
||||
|
||||
private static readonly string[] CBPrefixOpcodes = new[]
|
||||
{
|
||||
"RLC B","RLC C","RLC D","RLC E","RLC H","RLC L","RLC (HL)","RLC A",
|
||||
"RRC B","RRC C","RRC D","RRC E","RRC H","RRC L","RRC (HL)","RRC A",
|
||||
"RL B","RL C","RL D","RL E","RL H","RL L","RL (HL)","RL A",
|
||||
"RR B","RR C","RR D","RR E","RR H","RR L","RR (HL)","RR A",
|
||||
"SLA B","SLA C","SLA D","SLA E","SLA H","SLA L","SLA (HL)","SLA A",
|
||||
"SRA B","SRA C","SRA D","SRA E","SRA H","SRA L","SRA (HL)","SRA A",
|
||||
"SLL B","SLL C","SLL D","SLL E","SLL H","SLL L","SLL (HL)","SLL A",
|
||||
"SRL B","SRL C","SRL D","SRL E","SRL H","SRL L","SRL (HL)","SRL A",
|
||||
"BIT 0,B","BIT 0,C","BIT 0,D","BIT 0,E","BIT 0,H","BIT 0,L","BIT 0,(HL)","BIT 0,A",
|
||||
"BIT 1,B","BIT 1,C","BIT 1,D","BIT 1,E","BIT 1,H","BIT 1,L","BIT 1,(HL)","BIT 1,A",
|
||||
"BIT 2,B","BIT 2,C","BIT 2,D","BIT 2,E","BIT 2,H","BIT 2,L","BIT 2,(HL)","BIT 2,A",
|
||||
"BIT 3,B","BIT 3,C","BIT 3,D","BIT 3,E","BIT 3,H","BIT 3,L","BIT 3,(HL)","BIT 3,A",
|
||||
"BIT 4,B","BIT 4,C","BIT 4,D","BIT 4,E","BIT 4,H","BIT 4,L","BIT 4,(HL)","BIT 4,A",
|
||||
"BIT 5,B","BIT 5,C","BIT 5,D","BIT 5,E","BIT 5,H","BIT 5,L","BIT 5,(HL)","BIT 5,A",
|
||||
"BIT 6,B","BIT 6,C","BIT 6,D","BIT 6,E","BIT 6,H","BIT 6,L","BIT 6,(HL)","BIT 6,A",
|
||||
"BIT 7,B","BIT 7,C","BIT 7,D","BIT 7,E","BIT 7,H","BIT 7,L","BIT 7,(HL)","BIT 7,A",
|
||||
"RES 0,B","RES 0,C","RES 0,D","RES 0,E","RES 0,H","RES 0,L","RES 0,(HL)","RES 0,A",
|
||||
"RES 1,B","RES 1,C","RES 1,D","RES 1,E","RES 1,H","RES 1,L","RES 1,(HL)","RES 1,A",
|
||||
"RES 2,B","RES 2,C","RES 2,D","RES 2,E","RES 2,H","RES 2,L","RES 2,(HL)","RES 2,A",
|
||||
"RES 3,B","RES 3,C","RES 3,D","RES 3,E","RES 3,H","RES 3,L","RES 3,(HL)","RES 3,A",
|
||||
"RES 4,B","RES 4,C","RES 4,D","RES 4,E","RES 4,H","RES 4,L","RES 4,(HL)","RES 4,A",
|
||||
"RES 5,B","RES 5,C","RES 5,D","RES 5,E","RES 5,H","RES 5,L","RES 5,(HL)","RES 5,A",
|
||||
"RES 6,B","RES 6,C","RES 6,D","RES 6,E","RES 6,H","RES 6,L","RES 6,(HL)","RES 6,A",
|
||||
"RES 7,B","RES 7,C","RES 7,D","RES 7,E","RES 7,H","RES 7,L","RES 7,(HL)","RES 7,A",
|
||||
"SET 0,B","SET 0,C","SET 0,D","SET 0,E","SET 0,H","SET 0,L","SET 0,(HL)","SET 0,A",
|
||||
"SET 1,B","SET 1,C","SET 1,D","SET 1,E","SET 1,H","SET 1,L","SET 1,(HL)","SET 1,A",
|
||||
"SET 2,B","SET 2,C","SET 2,D","SET 2,E","SET 2,H","SET 2,L","SET 2,(HL)","SET 2,A",
|
||||
"SET 3,B","SET 3,C","SET 3,D","SET 3,E","SET 3,H","SET 3,L","SET 3,(HL)","SET 3,A",
|
||||
"SET 4,B","SET 4,C","SET 4,D","SET 4,E","SET 4,H","SET 4,L","SET 4,(HL)","SET 4,A",
|
||||
"SET 5,B","SET 5,C","SET 5,D","SET 5,E","SET 5,H","SET 5,L","SET 5,(HL)","SET 5,A",
|
||||
"SET 6,B","SET 6,C","SET 6,D","SET 6,E","SET 6,H","SET 6,L","SET 6,(HL)","SET 6,A",
|
||||
"SET 7,B","SET 7,C","SET 7,D","SET 7,E","SET 7,H","SET 7,L","SET 7,(HL)","SET 7,A"
|
||||
};
|
||||
|
||||
private static readonly string[] EDPrefixOpcodes = new[]
|
||||
{
|
||||
"DB EDh,00h","DB EDh,01h","DB EDh,02h","DB EDh,03h",
|
||||
"DB EDh,04h","DB EDh,05h","DB EDh,06h","DB EDh,07h",
|
||||
"DB EDh,08h","DB EDh,09h","DB EDh,0Ah","DB EDh,0Bh",
|
||||
"DB EDh,0Ch","DB EDh,0Dh","DB EDh,0Eh","DB EDh,0Fh",
|
||||
"DB EDh,10h","DB EDh,11h","DB EDh,12h","DB EDh,13h",
|
||||
"DB EDh,14h","DB EDh,15h","DB EDh,16h","DB EDh,17h",
|
||||
"DB EDh,18h","DB EDh,19h","DB EDh,1Ah","DB EDh,1Bh",
|
||||
"DB EDh,1Ch","DB EDh,1Dh","DB EDh,1Eh","DB EDh,1Fh",
|
||||
"DB EDh,20h","DB EDh,21h","DB EDh,22h","DB EDh,23h",
|
||||
"DB EDh,24h","DB EDh,25h","DB EDh,26h","DB EDh,27h",
|
||||
"DB EDh,28h","DB EDh,29h","DB EDh,2Ah","DB EDh,2Bh",
|
||||
"DB EDh,2Ch","DB EDh,2Dh","DB EDh,2Eh","DB EDh,2Fh",
|
||||
"DB EDh,30h","DB EDh,31h","DB EDh,32h","DB EDh,33h",
|
||||
"DB EDh,34h","DB EDh,35h","DB EDh,36h","DB EDh,37h",
|
||||
"DB EDh,38h","DB EDh,39h","DB EDh,3Ah","DB EDh,3Bh",
|
||||
"DB EDh,3Ch","DB EDh,3Dh","DB EDh,3Eh","DB EDh,3Fh",
|
||||
"IN B,(C)","OUT (C),B","SBC HL,BC","LD (#h),BC",
|
||||
"NEG","RETN","IM 0","LD I,A",
|
||||
"IN C,(C)","OUT (C),C","ADC HL,BC","LD BC,(#h)",
|
||||
"DB EDh,4Ch","RETI","DB EDh,4Eh","LD R,A",
|
||||
"IN D,(C)","OUT (C),D","SBC HL,DE","LD (#h),DE",
|
||||
"DB EDh,54h","DB EDh,55h","IM 1","LD A,I",
|
||||
"IN E,(C)","OUT (C),E","ADC HL,DE","LD DE,(#h)",
|
||||
"DB EDh,5Ch","DB EDh,5Dh","IM 2","LD A,R",
|
||||
"IN H,(C)","OUT (C),H","SBC HL,HL","LD (#h),HL",
|
||||
"DB EDh,64h","DB EDh,65h","DB EDh,66h","RRD",
|
||||
"IN L,(C)","OUT (C),L","ADC HL,HL","LD HL,(#h)",
|
||||
"DB EDh,6Ch","DB EDh,6Dh","DB EDh,6Eh","RLD",
|
||||
"IN F,(C)","DB EDh,71h","SBC HL,SP","LD (#h),SP",
|
||||
"DB EDh,74h","DB EDh,75h","DB EDh,76h","DB EDh,77h",
|
||||
"IN A,(C)","OUT (C),A","ADC HL,SP","LD SP,(#h)",
|
||||
"DB EDh,7Ch","DB EDh,7Dh","DB EDh,7Eh","DB EDh,7Fh",
|
||||
"DB EDh,80h","DB EDh,81h","DB EDh,82h","DB EDh,83h",
|
||||
"DB EDh,84h","DB EDh,85h","DB EDh,86h","DB EDh,87h",
|
||||
"DB EDh,88h","DB EDh,89h","DB EDh,8Ah","DB EDh,8Bh",
|
||||
"DB EDh,8Ch","DB EDh,8Dh","DB EDh,8Eh","DB EDh,8Fh",
|
||||
"DB EDh,90h","DB EDh,91h","DB EDh,92h","DB EDh,93h",
|
||||
"DB EDh,94h","DB EDh,95h","DB EDh,96h","DB EDh,97h",
|
||||
"DB EDh,98h","DB EDh,99h","DB EDh,9Ah","DB EDh,9Bh",
|
||||
"DB EDh,9Ch","DB EDh,9Dh","DB EDh,9Eh","DB EDh,9Fh",
|
||||
"LDI","CPI","INI","OUTI",
|
||||
"DB EDh,A4h","DB EDh,A5h","DB EDh,A6h","DB EDh,A7h",
|
||||
"LDD","CPD","IND","OUTD",
|
||||
"DB EDh,ACh","DB EDh,ADh","DB EDh,AEh","DB EDh,AFh",
|
||||
"LDIR","CPIR","INIR","OTIR",
|
||||
"DB EDh,B4h","DB EDh,B5h","DB EDh,B6h","DB EDh,B7h",
|
||||
"LDDR","CPDR","INDR","OTDR",
|
||||
"DB EDh,BCh","DB EDh,BDh","DB EDh,BEh","DB EDh,BFh",
|
||||
"DB EDh,C0h","DB EDh,C1h","DB EDh,C2h","DB EDh,C3h",
|
||||
"DB EDh,C4h","DB EDh,C5h","DB EDh,C6h","DB EDh,C7h",
|
||||
"DB EDh,C8h","DB EDh,C9h","DB EDh,CAh","DB EDh,CBh",
|
||||
"DB EDh,CCh","DB EDh,CDh","DB EDh,CEh","DB EDh,CFh",
|
||||
"DB EDh,D0h","DB EDh,D1h","DB EDh,D2h","DB EDh,D3h",
|
||||
"DB EDh,D4h","DB EDh,D5h","DB EDh,D6h","DB EDh,D7h",
|
||||
"DB EDh,D8h","DB EDh,D9h","DB EDh,DAh","DB EDh,DBh",
|
||||
"DB EDh,DCh","DB EDh,DDh","DB EDh,DEh","DB EDh,DFh",
|
||||
"DB EDh,E0h","DB EDh,E1h","DB EDh,E2h","DB EDh,E3h",
|
||||
"DB EDh,E4h","DB EDh,E5h","DB EDh,E6h","DB EDh,E7h",
|
||||
"DB EDh,E8h","DB EDh,E9h","DB EDh,EAh","DB EDh,EBh",
|
||||
"DB EDh,ECh","DB EDh,EDh","DB EDh,EEh","DB EDh,EFh",
|
||||
"DB EDh,F0h","DB EDh,F1h","DB EDh,F2h","DB EDh,F3h",
|
||||
"DB EDh,F4h","DB EDh,F5h","DB EDh,F6h","DB EDh,F7h",
|
||||
"DB EDh,F8h","DB EDh,F9h","DB EDh,FAh","DB EDh,FBh",
|
||||
"DB EDh,FCh","DB EDh,FDh","DB EDh,FEh","DB EDh,FFh"
|
||||
};
|
||||
|
||||
private static readonly string[] DDPrefixOpcodes = new[]
|
||||
{
|
||||
"NOP","LD BC,#h","LD (BC),A","INC BC","INC B","DEC B","LD B,*h","RLCA",
|
||||
"EX AF,AF'","ADD I%,BC","LD A,(BC)","DEC BC","INC C","DEC C","LD C,*h","RRCA",
|
||||
"DJNZ @h","LD DE,#h","LD (DE),A","INC DE","INC D","DEC D","LD D,*h","RLA",
|
||||
"JR @h","ADD I%,DE","LD A,(DE)","DEC DE","INC E","DEC E","LD E,*h","RRA",
|
||||
"JR NZ,@h","LD I%,#h","LD (#h),I%","INC I%","INC I%h","DEC I%h","LD I%h,*h","DAA",
|
||||
"JR Z,@h","ADD I%,I%","LD I%,(#h)","DEC I%","INC I%l","DEC I%l","LD I%l,*h","CPL",
|
||||
"JR NC,@h","LD SP,#h","LD (#h),A","INC SP","INC (I%+^h)","DEC (I%+^h)","LD (I%+^h),*h","SCF",
|
||||
"JR C,@h","ADD I%,SP","LD A,(#h)","DEC SP","INC A","DEC A","LD A,*h","CCF",
|
||||
"LD B,B","LD B,C","LD B,D","LD B,E","LD B,I%h","LD B,I%l","LD B,(I%+^h)","LD B,A",
|
||||
"LD C,B","LD C,C","LD C,D","LD C,E","LD C,I%h","LD C,I%l","LD C,(I%+^h)","LD C,A",
|
||||
"LD D,B","LD D,C","LD D,D","LD D,E","LD D,I%h","LD D,I%l","LD D,(I%+^h)","LD D,A",
|
||||
"LD E,B","LD E,C","LD E,D","LD E,E","LD E,I%h","LD E,I%l","LD E,(I%+^h)","LD E,A",
|
||||
"LD I%h,B","LD I%h,C","LD I%h,D","LD I%h,E","LD I%h,I%h","LD I%h,I%l","LD H,(I%+^h)","LD I%h,A",
|
||||
"LD I%l,B","LD I%l,C","LD I%l,D","LD I%l,E","LD I%l,I%h","LD I%l,I%l","LD L,(I%+^h)","LD I%l,A",
|
||||
"LD (I%+^h),B","LD (I%+^h),C","LD (I%+^h),D","LD (I%+^h),E","LD (I%+^h),H","LD (I%+^h),L","HALT","LD (I%+^h),A",
|
||||
"LD A,B","LD A,C","LD A,D","LD A,E","LD A,I%h","LD A,I%l","LD A,(I%+^h)","LD A,A",
|
||||
"ADD B","ADD C","ADD D","ADD E","ADD I%h","ADD I%l","ADD (I%+^h)","ADD A",
|
||||
"ADC B","ADC C","ADC D","ADC E","ADC I%h","ADC I%l","ADC (I%+^h)","ADC,A",
|
||||
"SUB B","SUB C","SUB D","SUB E","SUB I%h","SUB I%l","SUB (I%+^h)","SUB A",
|
||||
"SBC B","SBC C","SBC D","SBC E","SBC I%h","SBC I%l","SBC (I%+^h)","SBC A",
|
||||
"AND B","AND C","AND D","AND E","AND I%h","AND I%l","AND (I%+^h)","AND A",
|
||||
"XOR B","XOR C","XOR D","XOR E","XOR I%h","XOR I%l","XOR (I%+^h)","XOR A",
|
||||
"OR B","OR C","OR D","OR E","OR I%h","OR I%l","OR (I%+^h)","OR A",
|
||||
"CP B","CP C","CP D","CP E","CP I%h","CP I%l","CP (I%+^h)","CP A",
|
||||
"RET NZ","POP BC","JP NZ,#h","JP #h","CALL NZ,#h","PUSH BC","ADD *h","RST 00h",
|
||||
"RET Z","RET","JP Z,#h","PFX_CB","CALL Z,#h","CALL #h","ADC *h","RST 08h",
|
||||
"RET NC","POP DE","JP NC,#h","OUTA (*h)","CALL NC,#h","PUSH DE","SUB *h","RST 10h",
|
||||
"RET C","EXX","JP C,#h","INA (*h)","CALL C,#h","PFX_DD","SBC *h","RST 18h",
|
||||
"RET PO","POP I%","JP PO,#h","EX I%,(SP)","CALL PO,#h","PUSH I%","AND *h","RST 20h",
|
||||
"RET PE","LD PC,I%","JP PE,#h","EX DE,I%","CALL PE,#h","PFX_ED","XOR *h","RST 28h",
|
||||
"RET P","POP AF","JP P,#h","DI","CALL P,#h","PUSH AF","OR *h","RST 30h",
|
||||
"RET M","LD SP,I%","JP M,#h","EI","CALL M,#h","PFX_FD","CP *h","RST 38h"
|
||||
};
|
||||
|
||||
private static readonly string[] DDCBPrefixOpcodes = new[]
|
||||
{
|
||||
"RLC B","RLC C","RLC D","RLC E","RLC H","RLC L","RLC (I%@h)","RLC A",
|
||||
"RRC B","RRC C","RRC D","RRC E","RRC H","RRC L","RRC (I%@h)","RRC A",
|
||||
"RL B","RL C","RL D","RL E","RL H","RL L","RL (I%@h)","RL A",
|
||||
"RR B","RR C","RR D","RR E","RR H","RR L","RR (I%@h)","RR A",
|
||||
"SLA B","SLA C","SLA D","SLA E","SLA H","SLA L","SLA (I%@h)","SLA A",
|
||||
"SRA B","SRA C","SRA D","SRA E","SRA H","SRA L","SRA (I%@h)","SRA A",
|
||||
"SLL B","SLL C","SLL D","SLL E","SLL H","SLL L","SLL (I%@h)","SLL A",
|
||||
"SRL B","SRL C","SRL D","SRL E","SRL H","SRL L","SRL (I%@h)","SRL A",
|
||||
"BIT 0,B","BIT 0,C","BIT 0,D","BIT 0,E","BIT 0,H","BIT 0,L","BIT 0,(I%@h)","BIT 0,A",
|
||||
"BIT 1,B","BIT 1,C","BIT 1,D","BIT 1,E","BIT 1,H","BIT 1,L","BIT 1,(I%@h)","BIT 1,A",
|
||||
"BIT 2,B","BIT 2,C","BIT 2,D","BIT 2,E","BIT 2,H","BIT 2,L","BIT 2,(I%@h)","BIT 2,A",
|
||||
"BIT 3,B","BIT 3,C","BIT 3,D","BIT 3,E","BIT 3,H","BIT 3,L","BIT 3,(I%@h)","BIT 3,A",
|
||||
"BIT 4,B","BIT 4,C","BIT 4,D","BIT 4,E","BIT 4,H","BIT 4,L","BIT 4,(I%@h)","BIT 4,A",
|
||||
"BIT 5,B","BIT 5,C","BIT 5,D","BIT 5,E","BIT 5,H","BIT 5,L","BIT 5,(I%@h)","BIT 5,A",
|
||||
"BIT 6,B","BIT 6,C","BIT 6,D","BIT 6,E","BIT 6,H","BIT 6,L","BIT 6,(I%@h)","BIT 6,A",
|
||||
"BIT 7,B","BIT 7,C","BIT 7,D","BIT 7,E","BIT 7,H","BIT 7,L","BIT 7,(I%@h)","BIT 7,A",
|
||||
"RES 0,B","RES 0,C","RES 0,D","RES 0,E","RES 0,H","RES 0,L","RES 0,(I%@h)","RES 0,A",
|
||||
"RES 1,B","RES 1,C","RES 1,D","RES 1,E","RES 1,H","RES 1,L","RES 1,(I%@h)","RES 1,A",
|
||||
"RES 2,B","RES 2,C","RES 2,D","RES 2,E","RES 2,H","RES 2,L","RES 2,(I%@h)","RES 2,A",
|
||||
"RES 3,B","RES 3,C","RES 3,D","RES 3,E","RES 3,H","RES 3,L","RES 3,(I%@h)","RES 3,A",
|
||||
"RES 4,B","RES 4,C","RES 4,D","RES 4,E","RES 4,H","RES 4,L","RES 4,(I%@h)","RES 4,A",
|
||||
"RES 5,B","RES 5,C","RES 5,D","RES 5,E","RES 5,H","RES 5,L","RES 5,(I%@h)","RES 5,A",
|
||||
"RES 6,B","RES 6,C","RES 6,D","RES 6,E","RES 6,H","RES 6,L","RES 6,(I%@h)","RES 6,A",
|
||||
"RES 7,B","RES 7,C","RES 7,D","RES 7,E","RES 7,H","RES 7,L","RES 7,(I%@h)","RES 7,A",
|
||||
"SET 0,B","SET 0,C","SET 0,D","SET 0,E","SET 0,H","SET 0,L","SET 0,(I%@h)","SET 0,A",
|
||||
"SET 1,B","SET 1,C","SET 1,D","SET 1,E","SET 1,H","SET 1,L","SET 1,(I%@h)","SET 1,A",
|
||||
"SET 2,B","SET 2,C","SET 2,D","SET 2,E","SET 2,H","SET 2,L","SET 2,(I%@h)","SET 2,A",
|
||||
"SET 3,B","SET 3,C","SET 3,D","SET 3,E","SET 3,H","SET 3,L","SET 3,(I%@h)","SET 3,A",
|
||||
"SET 4,B","SET 4,C","SET 4,D","SET 4,E","SET 4,H","SET 4,L","SET 4,(I%@h)","SET 4,A",
|
||||
"SET 5,B","SET 5,C","SET 5,D","SET 5,E","SET 5,H","SET 5,L","SET 5,(I%@h)","SET 5,A",
|
||||
"SET 6,B","SET 6,C","SET 6,D","SET 6,E","SET 6,H","SET 6,L","SET 6,(I%@h)","SET 6,A",
|
||||
"SET 7,B","SET 7,C","SET 7,D","SET 7,E","SET 7,H","SET 7,L","SET 7,(I%@h)","SET 7,A"
|
||||
};
|
||||
#endregion
|
||||
|
||||
public string Disassemble(ushort pc, out int bytesToAdvance)
|
||||
{
|
||||
byte op = ReadMemory(pc++);
|
||||
string disassembly;
|
||||
switch (op)
|
||||
{
|
||||
case 0xCB:
|
||||
op = ReadMemory(pc++);
|
||||
disassembly = CBPrefixOpcodes[op];
|
||||
bytesToAdvance = 2;
|
||||
break;
|
||||
case 0xED:
|
||||
op = ReadMemory(pc++);
|
||||
disassembly = EDPrefixOpcodes[op];
|
||||
bytesToAdvance = 2;
|
||||
break;
|
||||
case 0xDD:
|
||||
op = ReadMemory(pc++);
|
||||
if (op == 0xCB)
|
||||
{
|
||||
op = ReadMemory(pc++);
|
||||
disassembly = DDCBPrefixOpcodes[op];
|
||||
bytesToAdvance = 3;
|
||||
} else {
|
||||
disassembly = DDPrefixOpcodes[op];
|
||||
bytesToAdvance = 2;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
disassembly = Opcodes[op];
|
||||
bytesToAdvance = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (disassembly.IndexOf('*') != -1)
|
||||
{
|
||||
disassembly = disassembly.Replace("*", string.Format("{0:X2}", ReadMemory(pc++)));
|
||||
bytesToAdvance++;
|
||||
}
|
||||
if (disassembly.IndexOf('#') != -1)
|
||||
{
|
||||
byte L = ReadMemory(pc++);
|
||||
byte H = ReadMemory(pc++);
|
||||
disassembly = disassembly.Replace("#", string.Format("{0:X4}", H << 8 | L));
|
||||
bytesToAdvance += 2;
|
||||
}
|
||||
if (disassembly.IndexOf('@') != -1)
|
||||
{
|
||||
sbyte b = (sbyte) ReadMemory(pc++);
|
||||
char sign = b < 0 ? '-' : '+';
|
||||
disassembly = disassembly.Replace("@", string.Format("{0}{1:X2}", sign, b));
|
||||
bytesToAdvance++;
|
||||
}
|
||||
|
||||
return disassembly;
|
||||
}
|
||||
|
||||
public string State()
|
||||
{
|
||||
int notused;
|
||||
string a = string.Format("{0:X4} {1}", RegPC.Word, Disassemble(RegPC.Word, out notused)).PadRight(41);
|
||||
string b = string.Format("AF: {0:X4} BC: {1:X4} DE: {2:X4} HL: {3:X4} IX: {4:X4} IY: {5:X4} SP: {6:X4} ",
|
||||
RegAF.Word, RegBC.Word, RegDE.Word, RegHL.Word, RegIX.Word, RegIY.Word, RegSP.Word);
|
||||
string val = a + b;
|
||||
if (RegFlagC) val += "C";
|
||||
if (RegFlagN) val += "N";
|
||||
if (RegFlagP) val += "P";
|
||||
if (RegFlag3) val += "3";
|
||||
if (RegFlagH) val += "H";
|
||||
if (RegFlag5) val += "5";
|
||||
if (RegFlagZ) val += "Z";
|
||||
if (RegFlagS) val += "S";
|
||||
|
||||
return val;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
|
||||
namespace BizHawk.Emulation.CPUs.Z80
|
||||
{
|
||||
public partial class Z80A
|
||||
{
|
||||
private bool iff1;
|
||||
public bool IFF1 { get { return iff1; } set { iff1 = value; } }
|
||||
|
||||
private bool iff2;
|
||||
public bool IFF2 { get { return iff2; } set { iff2 = value; } }
|
||||
|
||||
private bool interrupt;
|
||||
public bool Interrupt { get { return interrupt; } set { interrupt = value; } }
|
||||
|
||||
private bool nonMaskableInterrupt;
|
||||
public bool NonMaskableInterrupt
|
||||
{
|
||||
get { return nonMaskableInterrupt; }
|
||||
set { if (value && !nonMaskableInterrupt) NonMaskableInterruptPending = true; nonMaskableInterrupt = value; }
|
||||
}
|
||||
|
||||
private bool nonMaskableInterruptPending;
|
||||
public bool NonMaskableInterruptPending { get { return nonMaskableInterruptPending; } set { nonMaskableInterruptPending = value; } }
|
||||
|
||||
private int interruptMode;
|
||||
public int InterruptMode
|
||||
{
|
||||
get { return interruptMode; }
|
||||
set { if (value < 0 || value > 2) throw new ArgumentOutOfRangeException(); interruptMode = value; }
|
||||
}
|
||||
|
||||
private bool halted;
|
||||
public bool Halted { get { return halted; } set { halted = value; } }
|
||||
|
||||
public Action IRQCallback = delegate() { };
|
||||
public Action NMICallback = delegate() { };
|
||||
|
||||
private void ResetInterrupts()
|
||||
{
|
||||
IFF1 = false;
|
||||
IFF2 = false;
|
||||
Interrupt = false;
|
||||
NonMaskableInterrupt = false;
|
||||
NonMaskableInterruptPending = false;
|
||||
InterruptMode = 1;
|
||||
Halted = false;
|
||||
}
|
||||
|
||||
private void Halt()
|
||||
{
|
||||
Halted = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,239 @@
|
|||
using System.Runtime.InteropServices;
|
||||
using System;
|
||||
|
||||
namespace BizHawk.Emulation.CPUs.Z80
|
||||
{
|
||||
public partial class Z80A
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
[Serializable()]
|
||||
public struct RegisterPair
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public ushort Word;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public byte Low;
|
||||
|
||||
[FieldOffset(1)]
|
||||
public byte High;
|
||||
|
||||
public RegisterPair(ushort value)
|
||||
{
|
||||
Word = value;
|
||||
Low = (byte)(Word);
|
||||
High = (byte)(Word >> 8);
|
||||
}
|
||||
|
||||
public static implicit operator ushort(RegisterPair rp)
|
||||
{
|
||||
return rp.Word;
|
||||
}
|
||||
|
||||
public static implicit operator RegisterPair(ushort value)
|
||||
{
|
||||
return new RegisterPair(value);
|
||||
}
|
||||
}
|
||||
|
||||
private bool RegFlagC
|
||||
{
|
||||
get { return (RegAF.Low & 0x01) != 0; }
|
||||
set { RegAF.Low = (byte)((RegAF.Low & ~0x01) | (value ? 0x01 : 0x00)); }
|
||||
}
|
||||
|
||||
private bool RegFlagN
|
||||
{
|
||||
get { return (RegAF.Low & 0x02) != 0; }
|
||||
set { RegAF.Low = (byte)((RegAF.Low & ~0x02) | (value ? 0x02 : 0x00)); }
|
||||
}
|
||||
|
||||
private bool RegFlagP
|
||||
{
|
||||
get { return (RegAF.Low & 0x04) != 0; }
|
||||
set { RegAF.Low = (byte)((RegAF.Low & ~0x04) | (value ? 0x04 : 0x00)); }
|
||||
}
|
||||
|
||||
private bool RegFlag3
|
||||
{
|
||||
get { return (RegAF.Low & 0x08) != 0; }
|
||||
set { RegAF.Low = (byte)((RegAF.Low & ~0x08) | (value ? 0x08 : 0x00)); }
|
||||
}
|
||||
|
||||
private bool RegFlagH
|
||||
{
|
||||
get { return (RegAF.Low & 0x10) != 0; }
|
||||
set { RegAF.Low = (byte)((RegAF.Low & ~0x10) | (value ? 0x10 : 0x00)); }
|
||||
}
|
||||
|
||||
private bool RegFlag5
|
||||
{
|
||||
get { return (RegAF.Low & 0x20) != 0; }
|
||||
set { RegAF.Low = (byte)((RegAF.Low & ~0x20) | (value ? 0x20 : 0x00)); }
|
||||
}
|
||||
|
||||
private bool RegFlagZ
|
||||
{
|
||||
get { return (RegAF.Low & 0x40) != 0; }
|
||||
set { RegAF.Low = (byte)((RegAF.Low & ~0x40) | (value ? 0x40 : 0x00)); }
|
||||
}
|
||||
|
||||
private bool RegFlagS
|
||||
{
|
||||
get { return (RegAF.Low & 0x80) != 0; }
|
||||
set { RegAF.Low = (byte)((RegAF.Low & ~0x80) | (value ? 0x80 : 0x00)); }
|
||||
}
|
||||
|
||||
private RegisterPair RegAF;
|
||||
private RegisterPair RegBC;
|
||||
private RegisterPair RegDE;
|
||||
private RegisterPair RegHL;
|
||||
|
||||
private RegisterPair RegAltAF; // Shadow for A and F
|
||||
private RegisterPair RegAltBC; // Shadow for B and C
|
||||
private RegisterPair RegAltDE; // Shadow for D and E
|
||||
private RegisterPair RegAltHL; // Shadow for H and L
|
||||
|
||||
private byte RegI; // I (interrupt vector)
|
||||
private byte RegR; // R (memory refresh)
|
||||
|
||||
private RegisterPair RegIX; // IX (index register x)
|
||||
private RegisterPair RegIY; // IY (index register y)
|
||||
|
||||
private RegisterPair RegSP; // SP (stack pointer)
|
||||
private RegisterPair RegPC; // PC (program counter)
|
||||
|
||||
private void ResetRegisters()
|
||||
{
|
||||
// Clear main registers
|
||||
RegAF = 0; RegBC = 0; RegDE = 0; RegHL = 0;
|
||||
// Clear alternate registers
|
||||
RegAltAF = 0; RegAltBC = 0; RegAltDE = 0; RegAltHL = 0;
|
||||
// Clear special purpose registers
|
||||
RegI = 0; RegR = 0;
|
||||
RegIX.Word = 0; RegIY.Word = 0;
|
||||
RegSP.Word = 0; RegPC.Word = 0;
|
||||
}
|
||||
|
||||
public byte RegisterA
|
||||
{
|
||||
get { return RegAF.High; }
|
||||
set { RegAF.High = value; }
|
||||
}
|
||||
|
||||
public byte RegisterF
|
||||
{
|
||||
get { return RegAF.Low; }
|
||||
set { RegAF.Low = value; }
|
||||
}
|
||||
|
||||
public ushort RegisterAF
|
||||
{
|
||||
get { return RegAF.Word; }
|
||||
set { RegAF.Word = value; }
|
||||
}
|
||||
|
||||
public byte RegisterB
|
||||
{
|
||||
get { return RegBC.High; }
|
||||
set { RegBC.High = value; }
|
||||
}
|
||||
|
||||
public byte RegisterC
|
||||
{
|
||||
get { return RegBC.Low; }
|
||||
set { RegBC.Low = value; }
|
||||
}
|
||||
|
||||
public ushort RegisterBC
|
||||
{
|
||||
get { return RegBC.Word; }
|
||||
set { RegBC.Word = value; }
|
||||
}
|
||||
|
||||
public byte RegisterD
|
||||
{
|
||||
get { return RegDE.High; }
|
||||
set { RegDE.High = value; }
|
||||
}
|
||||
|
||||
public byte RegisterE
|
||||
{
|
||||
get { return RegDE.Low; }
|
||||
set { RegDE.Low = value; }
|
||||
}
|
||||
public ushort RegisterDE
|
||||
{
|
||||
get { return RegDE.Word; }
|
||||
set { RegDE.Word = value; }
|
||||
}
|
||||
|
||||
public byte RegisterH
|
||||
{
|
||||
get { return RegHL.High; }
|
||||
set { RegHL.High = value; }
|
||||
}
|
||||
|
||||
public byte RegisterL
|
||||
{
|
||||
get { return RegHL.Low; }
|
||||
set { RegHL.Low = value; }
|
||||
}
|
||||
public ushort RegisterHL
|
||||
{
|
||||
get { return RegHL.Word; }
|
||||
set { RegHL.Word = value; }
|
||||
}
|
||||
|
||||
public ushort RegisterPC
|
||||
{
|
||||
get { return RegPC.Word; }
|
||||
set { RegPC.Word = value; }
|
||||
}
|
||||
public ushort RegisterSP
|
||||
{
|
||||
get { return RegSP.Word; }
|
||||
set { RegSP.Word = value; }
|
||||
}
|
||||
public ushort RegisterIX
|
||||
{
|
||||
get { return RegIX.Word; }
|
||||
set { RegIX.Word = value; }
|
||||
}
|
||||
public ushort RegisterIY
|
||||
{
|
||||
get { return RegIY.Word; }
|
||||
set { RegIY.Word = value; }
|
||||
}
|
||||
public byte RegisterI
|
||||
{
|
||||
get { return RegI; }
|
||||
set { RegI = value; }
|
||||
}
|
||||
public byte RegisterR
|
||||
{
|
||||
get { return RegR; }
|
||||
set { RegR = value; }
|
||||
}
|
||||
public ushort RegisterShadowAF
|
||||
{
|
||||
get { return RegAltAF.Word; }
|
||||
set { RegAltAF.Word = value; }
|
||||
}
|
||||
public ushort RegisterShadowBC
|
||||
{
|
||||
get { return RegAltBC.Word; }
|
||||
set { RegAltBC.Word = value; }
|
||||
}
|
||||
public ushort RegisterShadowDE
|
||||
{
|
||||
get { return RegAltDE.Word; }
|
||||
set { RegAltDE.Word = value; }
|
||||
}
|
||||
public ushort RegisterShadowHL
|
||||
{
|
||||
get { return RegAltHL.Word; }
|
||||
set { RegAltHL.Word = value; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,321 @@
|
|||
namespace BizHawk.Emulation.CPUs.Z80
|
||||
{
|
||||
public partial class Z80A
|
||||
{
|
||||
private void InitialiseTables()
|
||||
{
|
||||
InitTableInc();
|
||||
InitTableDec();
|
||||
InitTableParity();
|
||||
InitTableALU();
|
||||
InitTableRotShift();
|
||||
InitTableHalfBorrow();
|
||||
InitTableHalfCarry();
|
||||
InitTableNeg();
|
||||
InitTableDaa();
|
||||
}
|
||||
|
||||
private byte[] TableInc;
|
||||
private void InitTableInc()
|
||||
{
|
||||
TableInc = new byte[256];
|
||||
for (int i = 0; i < 256; ++i)
|
||||
TableInc[i] = FlagByte(false, false, i == 0x80, UndocumentedX(i), (i & 0xF) == 0x0, UndocumentedY(i), i == 0, i > 127);
|
||||
}
|
||||
|
||||
private byte[] TableDec;
|
||||
private void InitTableDec()
|
||||
{
|
||||
TableDec = new byte[256];
|
||||
for (int i = 0; i < 256; ++i)
|
||||
TableDec[i] = FlagByte(false, true, i == 0x7F, UndocumentedX(i), (i & 0xF) == 0xF, UndocumentedY(i), i == 0, i > 127);
|
||||
}
|
||||
|
||||
private bool[] TableParity;
|
||||
private void InitTableParity()
|
||||
{
|
||||
TableParity = new bool[256];
|
||||
for (int i = 0; i < 256; ++i)
|
||||
{
|
||||
int Bits = 0;
|
||||
for (int j = 0; j < 8; ++j)
|
||||
{
|
||||
Bits += (i >> j) & 1;
|
||||
}
|
||||
TableParity[i] = (Bits & 1) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
private ushort[, , ,] TableALU;
|
||||
private void InitTableALU()
|
||||
{
|
||||
TableALU = new ushort[8, 256, 256, 2]; // Class, OP1, OP2, Carry
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
for (int op1 = 0; op1 < 256; ++op1)
|
||||
{
|
||||
for (int op2 = 0; op2 < 256; ++op2)
|
||||
{
|
||||
for (int c = 0; c < 2; ++c)
|
||||
{
|
||||
|
||||
int ac = (i == 1 || i == 3) ? c : 0;
|
||||
|
||||
bool S = false;
|
||||
bool Z = false;
|
||||
bool C = false;
|
||||
bool H = false;
|
||||
bool N = false;
|
||||
bool P = false;
|
||||
|
||||
byte result_b = 0;
|
||||
int result_si = 0;
|
||||
int result_ui = 0;
|
||||
|
||||
// Fetch result
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
result_si = (sbyte)op1 + (sbyte)op2 + ac;
|
||||
result_ui = op1 + op2 + ac;
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
case 7:
|
||||
result_si = (sbyte)op1 - (sbyte)op2 - ac;
|
||||
result_ui = op1 - op2 - ac;
|
||||
break;
|
||||
case 4:
|
||||
result_si = op1 & op2;
|
||||
break;
|
||||
case 5:
|
||||
result_si = op1 ^ op2;
|
||||
break;
|
||||
case 6:
|
||||
result_si = op1 | op2;
|
||||
break;
|
||||
}
|
||||
|
||||
result_b = (byte)result_si;
|
||||
|
||||
// Parity/Carry
|
||||
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 7:
|
||||
P = result_si < -128 || result_si > 127;
|
||||
C = result_ui < 0 || result_ui > 255;
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
P = TableParity[result_b];
|
||||
C = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Subtraction
|
||||
N = i == 2 || i == 3 || i == 7;
|
||||
|
||||
// Half carry
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
H = ((op1 & 0xF) + (op2 & 0xF) + (ac & 0xF)) > 0xF;
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
case 7:
|
||||
H = ((op1 & 0xF) - (op2 & 0xF) - (ac & 0xF)) < 0x0;
|
||||
break;
|
||||
case 4:
|
||||
H = true;
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
H = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Undocumented
|
||||
byte UndocumentedFlags = (byte)(result_b & 0x28);
|
||||
if (i == 7) UndocumentedFlags = (byte)(op2 & 0x28);
|
||||
|
||||
S = result_b > 127;
|
||||
Z = result_b == 0;
|
||||
|
||||
if (i == 7) result_b = (byte)op1;
|
||||
|
||||
TableALU[i, op1, op2, c] = (ushort)(
|
||||
result_b * 256 +
|
||||
((C ? 0x01 : 0) + (N ? 0x02 : 0) + (P ? 0x04 : 0) + (H ? 0x10 : 0) + (Z ? 0x40 : 0) + (S ? 0x80 : 0)) +
|
||||
(UndocumentedFlags));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool[,] TableHalfBorrow;
|
||||
private void InitTableHalfBorrow()
|
||||
{
|
||||
TableHalfBorrow = new bool[256, 256];
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
for (int j = 0; j < 256; j++)
|
||||
{
|
||||
TableHalfBorrow[i, j] = ((i & 0xF) - (j & 0xF)) < 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool[,] TableHalfCarry;
|
||||
private void InitTableHalfCarry()
|
||||
{
|
||||
TableHalfCarry = new bool[256, 256];
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
for (int j = 0; j < 256; j++)
|
||||
{
|
||||
TableHalfCarry[i, j] = ((i & 0xF) + (j & 0xF)) > 0xF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ushort[, ,] TableRotShift;
|
||||
private void InitTableRotShift()
|
||||
{
|
||||
TableRotShift = new ushort[2, 8, 65536]; // All, operation, AF
|
||||
for (int all = 0; all < 2; all++)
|
||||
{
|
||||
for (int y = 0; y < 8; ++y)
|
||||
{
|
||||
for (int af = 0; af < 65536; af++)
|
||||
{
|
||||
byte Old = (byte)(af >> 8);
|
||||
bool OldCarry = (af & 0x01) != 0;
|
||||
|
||||
ushort newAf = (ushort)(af & ~(0x13)); // Clear HALF-CARRY, SUBTRACT and CARRY flags
|
||||
|
||||
byte New = Old;
|
||||
if ((y & 1) == 0)
|
||||
{
|
||||
if ((Old & 0x80) != 0) ++newAf;
|
||||
|
||||
New <<= 1;
|
||||
|
||||
if ((y & 0x04) == 0) {
|
||||
if (((y & 0x02) == 0) ? ((newAf & 0x01) != 0) : OldCarry) New |= 0x01;
|
||||
} else {
|
||||
if ((y & 0x02) != 0) New |= 0x01;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if ((Old & 0x01) != 0) ++newAf;
|
||||
|
||||
New >>= 1;
|
||||
|
||||
if ((y & 0x04) == 0) {
|
||||
if (((y & 0x02) == 0) ? ((newAf & 0x01) != 0) : OldCarry) New |= 0x80;
|
||||
} else {
|
||||
if ((y & 0x02) == 0) New |= (byte)(Old & 0x80);
|
||||
}
|
||||
}
|
||||
|
||||
newAf &= 0xFF;
|
||||
newAf |= (ushort)(New * 256);
|
||||
|
||||
if (all == 1)
|
||||
{
|
||||
newAf &= unchecked((ushort)~0xC4); // Clear S, Z, P
|
||||
if (New > 127) newAf |= 0x80;
|
||||
if (New == 0) newAf |= 0x40;
|
||||
if (TableParity[New]) newAf |= 0x04;
|
||||
}
|
||||
|
||||
TableRotShift[all, y, af] = (ushort)((newAf & ~0x28) | ((newAf >> 8) & 0x28));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ushort[] TableNeg;
|
||||
private void InitTableNeg()
|
||||
{
|
||||
TableNeg = new ushort[65536];
|
||||
for (int af = 0; af < 65536; af++)
|
||||
{
|
||||
ushort raf = 0;
|
||||
byte b = (byte)(af >> 8);
|
||||
byte a = (byte)-b;
|
||||
raf |= (ushort)(a * 256);
|
||||
raf |= FlagByte(b != 0x00, true, b == 0x80, UndocumentedX(a), TableHalfCarry[a, b], UndocumentedY(a), a == 0, a > 127);
|
||||
TableNeg[af] = raf;
|
||||
}
|
||||
}
|
||||
|
||||
private ushort[] TableDaa;
|
||||
private void InitTableDaa()
|
||||
{
|
||||
TableDaa = new ushort[65536];
|
||||
for (int af = 0; af < 65536; ++af)
|
||||
{
|
||||
byte a = (byte)(af >> 8);
|
||||
byte tmp = a;
|
||||
|
||||
if (IsN(af))
|
||||
{
|
||||
if (IsH(af) || ((a & 0x0F) > 0x09)) tmp -= 0x06;
|
||||
if (IsC(af) || a > 0x99) tmp -= 0x60;
|
||||
} else {
|
||||
if (IsH(af) || ((a & 0x0F) > 0x09)) tmp += 0x06;
|
||||
if (IsC(af) || a > 0x99) tmp += 0x60;
|
||||
}
|
||||
|
||||
TableDaa[af] = (ushort)((tmp * 256) + FlagByte(IsC(af) || a > 0x99, IsN(af), TableParity[tmp], UndocumentedX(tmp), ((a ^ tmp) & 0x10) != 0, UndocumentedY(tmp), tmp == 0, tmp > 127));
|
||||
}
|
||||
}
|
||||
|
||||
private byte FlagByte(bool C, bool N, bool P, bool X, bool H, bool Y, bool Z, bool S)
|
||||
{
|
||||
return (byte)(
|
||||
(C ? 0x01 : 0) +
|
||||
(N ? 0x02 : 0) +
|
||||
(P ? 0x04 : 0) +
|
||||
(X ? 0x08 : 0) +
|
||||
(H ? 0x10 : 0) +
|
||||
(Y ? 0x20 : 0) +
|
||||
(Z ? 0x40 : 0) +
|
||||
(S ? 0x80 : 0)
|
||||
);
|
||||
}
|
||||
|
||||
private bool UndocumentedX(int value)
|
||||
{
|
||||
return (value & 0x08) != 0;
|
||||
}
|
||||
|
||||
private bool UndocumentedY(int value)
|
||||
{
|
||||
return (value & 0x20) != 0;
|
||||
}
|
||||
|
||||
private bool IsC(int value) { return (value & 0x01) != 0; }
|
||||
private bool IsN(int value) { return (value & 0x02) != 0; }
|
||||
private bool IsP(int value) { return (value & 0x04) != 0; }
|
||||
private bool IsX(int value) { return (value & 0x08) != 0; }
|
||||
private bool IsH(int value) { return (value & 0x10) != 0; }
|
||||
private bool IsY(int value) { return (value & 0x20) != 0; }
|
||||
private bool IsZ(int value) { return (value & 0x40) != 0; }
|
||||
private bool IsS(int value) { return (value & 0x80) != 0; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
|
||||
// This Z80 emulator is a modified version of Ben Ryves 'Brazil' emulator.
|
||||
// It is MIT licensed (not public domain). (See Licenses)
|
||||
|
||||
namespace BizHawk.Emulation.CPUs.Z80
|
||||
{
|
||||
/// <summary>
|
||||
/// ZiLOG Z80A CPU Emulator
|
||||
/// </summary>
|
||||
public sealed partial class Z80A
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an instance of the <see cref="Z80A"/> emulator class.
|
||||
/// </summary>
|
||||
public Z80A()
|
||||
{
|
||||
InitialiseTables();
|
||||
Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset the Z80 to its initial state
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
ResetRegisters();
|
||||
ResetInterrupts();
|
||||
PendingCycles = 0;
|
||||
ExpectedExecutedCycles = 0;
|
||||
TotalExecutedCycles = 0;
|
||||
}
|
||||
|
||||
// Memory Access
|
||||
|
||||
public Func<ushort, byte> ReadMemory;
|
||||
public Action<ushort, byte> WriteMemory;
|
||||
|
||||
public void UnregisterMemoryMapper()
|
||||
{
|
||||
ReadMemory = null;
|
||||
WriteMemory = null;
|
||||
}
|
||||
|
||||
// Hardware I/O Port Access
|
||||
|
||||
public Func<ushort, byte> ReadHardware;
|
||||
public Action<ushort, byte> WriteHardware;
|
||||
|
||||
// State Save/Load
|
||||
|
||||
public void SaveStateText(TextWriter writer)
|
||||
{
|
||||
writer.WriteLine("[Z80]");
|
||||
writer.WriteLine("AF {0:X4}", RegAF.Word);
|
||||
writer.WriteLine("BC {0:X4}", RegBC.Word);
|
||||
writer.WriteLine("DE {0:X4}", RegDE.Word);
|
||||
writer.WriteLine("HL {0:X4}", RegHL.Word);
|
||||
writer.WriteLine("ShadowAF {0:X4}", RegAltAF.Word);
|
||||
writer.WriteLine("ShadowBC {0:X4}", RegAltBC.Word);
|
||||
writer.WriteLine("ShadowDE {0:X4}", RegAltDE.Word);
|
||||
writer.WriteLine("ShadowHL {0:X4}", RegAltHL.Word);
|
||||
writer.WriteLine("I {0:X2}", RegI);
|
||||
writer.WriteLine("R {0:X2}", RegR);
|
||||
writer.WriteLine("IX {0:X4}", RegIX.Word);
|
||||
writer.WriteLine("IY {0:X4}", RegIY.Word);
|
||||
writer.WriteLine("SP {0:X4}", RegSP.Word);
|
||||
writer.WriteLine("PC {0:X4}", RegPC.Word);
|
||||
writer.WriteLine("IRQ {0}", interrupt);
|
||||
writer.WriteLine("NMI {0}", nonMaskableInterrupt);
|
||||
writer.WriteLine("NMIPending {0}", nonMaskableInterruptPending);
|
||||
writer.WriteLine("IM {0}", InterruptMode);
|
||||
writer.WriteLine("IFF1 {0}", IFF1);
|
||||
writer.WriteLine("IFF2 {0}", IFF2);
|
||||
writer.WriteLine("Halted {0}", Halted);
|
||||
writer.WriteLine("ExecutedCycles {0}", TotalExecutedCycles);
|
||||
writer.WriteLine("PendingCycles {0}", PendingCycles);
|
||||
writer.WriteLine("[/Z80]");
|
||||
writer.WriteLine();
|
||||
}
|
||||
|
||||
public void LoadStateText(TextReader reader)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
string[] args = reader.ReadLine().Split(' ');
|
||||
if (args[0].Trim() == "") continue;
|
||||
if (args[0] == "[/Z80]") break;
|
||||
if (args[0] == "AF")
|
||||
RegAF.Word = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "BC")
|
||||
RegBC.Word = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "DE")
|
||||
RegDE.Word = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "HL")
|
||||
RegHL.Word = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "ShadowAF")
|
||||
RegAltAF.Word = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "ShadowBC")
|
||||
RegAltBC.Word = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "ShadowDE")
|
||||
RegAltDE.Word = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "ShadowHL")
|
||||
RegAltHL.Word = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "I")
|
||||
RegI = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "R")
|
||||
RegR = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "IX")
|
||||
RegIX.Word = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "IY")
|
||||
RegIY.Word = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "SP")
|
||||
RegSP.Word = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "PC")
|
||||
RegPC.Word = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "IRQ")
|
||||
interrupt = bool.Parse(args[1]);
|
||||
else if (args[0] == "NMI")
|
||||
nonMaskableInterrupt = bool.Parse(args[1]);
|
||||
else if (args[0] == "NMIPending")
|
||||
nonMaskableInterruptPending = bool.Parse(args[1]);
|
||||
else if (args[0] == "IM")
|
||||
InterruptMode = int.Parse(args[1]);
|
||||
else if (args[0] == "IFF1")
|
||||
IFF1 = bool.Parse(args[1]);
|
||||
else if (args[0] == "IFF2")
|
||||
IFF2 = bool.Parse(args[1]);
|
||||
else if (args[0] == "Halted")
|
||||
Halted = bool.Parse(args[1]);
|
||||
else if (args[0] == "ExecutedCycles")
|
||||
TotalExecutedCycles = int.Parse(args[1]);
|
||||
else if (args[0] == "PendingCycles")
|
||||
PendingCycles = int.Parse(args[1]);
|
||||
|
||||
else
|
||||
Console.WriteLine("Skipping unrecognized identifier " + args[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveStateBinary(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(RegAF.Word);
|
||||
writer.Write(RegBC.Word);
|
||||
writer.Write(RegDE.Word);
|
||||
writer.Write(RegHL.Word);
|
||||
writer.Write(RegAltAF.Word);
|
||||
writer.Write(RegAltBC.Word);
|
||||
writer.Write(RegAltDE.Word);
|
||||
writer.Write(RegAltHL.Word);
|
||||
writer.Write(RegAltAF.Word);
|
||||
writer.Write(RegI);
|
||||
writer.Write(RegR);
|
||||
writer.Write(RegIX.Word);
|
||||
writer.Write(RegIY.Word);
|
||||
writer.Write(RegSP.Word);
|
||||
writer.Write(RegPC.Word);
|
||||
writer.Write(interrupt);
|
||||
writer.Write(nonMaskableInterrupt);
|
||||
writer.Write(nonMaskableInterruptPending);
|
||||
writer.Write(InterruptMode);
|
||||
writer.Write(IFF1);
|
||||
writer.Write(IFF2);
|
||||
writer.Write(Halted);
|
||||
writer.Write(TotalExecutedCycles);
|
||||
writer.Write(PendingCycles);
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader reader)
|
||||
{
|
||||
RegAF.Word = reader.ReadUInt16();
|
||||
RegBC.Word = reader.ReadUInt16();
|
||||
RegDE.Word = reader.ReadUInt16();
|
||||
RegHL.Word = reader.ReadUInt16();
|
||||
RegAltAF.Word = reader.ReadUInt16();
|
||||
RegAltBC.Word = reader.ReadUInt16();
|
||||
RegAltDE.Word = reader.ReadUInt16();
|
||||
RegAltHL.Word = reader.ReadUInt16();
|
||||
RegAltAF.Word = reader.ReadUInt16();
|
||||
RegI = reader.ReadByte();
|
||||
RegR = reader.ReadByte();
|
||||
RegIX.Word = reader.ReadUInt16();
|
||||
RegIY.Word = reader.ReadUInt16();
|
||||
RegSP.Word = reader.ReadUInt16();
|
||||
RegPC.Word = reader.ReadUInt16();
|
||||
interrupt = reader.ReadBoolean();
|
||||
nonMaskableInterrupt = reader.ReadBoolean();
|
||||
nonMaskableInterruptPending = reader.ReadBoolean();
|
||||
InterruptMode = reader.ReadInt32();
|
||||
IFF1 = reader.ReadBoolean();
|
||||
IFF2 = reader.ReadBoolean();
|
||||
Halted = reader.ReadBoolean();
|
||||
TotalExecutedCycles = reader.ReadInt32();
|
||||
PendingCycles = reader.ReadInt32();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.CPUs.x86
|
||||
{
|
||||
public class DisassemblyInfo
|
||||
{
|
||||
public int Addr;
|
||||
public string Mnemonic;
|
||||
public string Args;
|
||||
public string RawBytes;
|
||||
public int Length;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0:X6} {3,-12} {1,-8} {2}", Addr, Mnemonic, Args, RawBytes);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class x86<CpuType> where CpuType : struct, x86CpuType
|
||||
{
|
||||
private ushort ReadWord(int addr)
|
||||
{
|
||||
return (ushort)(ReadMemory(addr++) + (ReadMemory(addr) << 8));
|
||||
}
|
||||
|
||||
private string DisassembleRM8(ref int addr)
|
||||
{
|
||||
byte ModRM = ReadMemory(addr++);
|
||||
int mod = (ModRM >> 6) & 3;
|
||||
int r = (ModRM >> 3) & 7;
|
||||
int m = ModRM & 7;
|
||||
|
||||
string reg;
|
||||
switch (r)
|
||||
{
|
||||
case 0: reg = "AL"; break;
|
||||
case 1: reg = "CL"; break;
|
||||
case 2: reg = "DL"; break;
|
||||
case 3: reg = "BL"; break;
|
||||
case 4: reg = "AH"; break;
|
||||
case 5: reg = "CH"; break;
|
||||
case 6: reg = "DH"; break;
|
||||
case 7: reg = "BH"; break;
|
||||
default: reg = "UNKNOWN"; break;
|
||||
}
|
||||
return reg+", "+DisassembleMod(ref addr, mod, m, 1);
|
||||
}
|
||||
|
||||
private string DisassembleMod(ref int addr, int mod, int m, int size)
|
||||
{
|
||||
string ret;
|
||||
switch (mod)
|
||||
{
|
||||
case 0:
|
||||
switch (m)
|
||||
{
|
||||
case 0: return "[BX+SI]";
|
||||
case 1: return "[BX+DI]";
|
||||
case 2: return "[BP+SI]";
|
||||
case 3: return "[BP+DI]";
|
||||
case 4: return "[SI]";
|
||||
case 5: return "[DI]";
|
||||
case 6: ret = string.Format("{0:X4}h", ReadWord(addr)); addr += 2; return ret;
|
||||
case 7: return "[BX]";
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
switch (m)
|
||||
{
|
||||
case 0: return string.Format("[BX+SI] + {0:X2}h", ReadMemory(addr++));
|
||||
case 1: return string.Format("[BX+DI] + {0:X2}h", ReadMemory(addr++));
|
||||
case 2: return string.Format("[BP+SI] + {0:X2}h", ReadMemory(addr++));
|
||||
case 3: return string.Format("[BP+DI] + {0:X2}h", ReadMemory(addr++));
|
||||
case 4: return string.Format("[SI] + {0:X2}h", ReadMemory(addr++));
|
||||
case 5: return string.Format("[DI] + {0:X2}h", ReadMemory(addr++));
|
||||
case 6: return string.Format("[BP] + {0:X2}h", ReadMemory(addr++));
|
||||
case 7: return string.Format("[BX] + {0:X2}h", ReadMemory(addr++));
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
switch (m)
|
||||
{
|
||||
case 0: ret = string.Format("[BX+SI] + {0:X4}h", ReadWord(addr)); addr += 2; return ret;
|
||||
case 1: ret = string.Format("[BX+DI] + {0:X4}h", ReadWord(addr)); addr += 2; return ret;
|
||||
case 2: ret = string.Format("[BP+SI] + {0:X4}h", ReadWord(addr)); addr += 2; return ret;
|
||||
case 3: ret = string.Format("[BP+DI] + {0:X4}h", ReadWord(addr)); addr += 2; return ret;
|
||||
case 4: ret = string.Format("[SI] + {0:X4}h", ReadWord(addr)); addr += 2; return ret;
|
||||
case 5: ret = string.Format("[DI] + {0:X4}h", ReadWord(addr)); addr += 2; return ret;
|
||||
case 6: ret = string.Format("[BP] + {0:X4}h", ReadWord(addr)); addr += 2; return ret;
|
||||
case 7: ret = string.Format("[BX] + {0:X4}h", ReadWord(addr)); addr += 2; return ret;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
switch (m)
|
||||
{
|
||||
case 0: return size == 1 ? "AL" : "AX";
|
||||
case 1: return size == 1 ? "CL" : "CX";
|
||||
case 2: return size == 1 ? "DL" : "DX";
|
||||
case 3: return size == 1 ? "BL" : "BX";
|
||||
case 4: return size == 1 ? "AH" : "SP";
|
||||
case 5: return size == 1 ? "CH" : "BP";
|
||||
case 6: return size == 1 ? "DH" : "SI";
|
||||
case 7: return size == 1 ? "BH" : "DI";
|
||||
}
|
||||
break;
|
||||
}
|
||||
return "Disassembly Error";
|
||||
}
|
||||
|
||||
public DisassemblyInfo Disassemble(int addr)
|
||||
{
|
||||
var info = new DisassemblyInfo { Addr = addr };
|
||||
byte op1 = ReadMemory(addr++);
|
||||
switch (op1)
|
||||
{
|
||||
case 0x02: // ADD r8,r/m8
|
||||
info.Mnemonic = "ADD";
|
||||
info.Args = DisassembleRM8(ref addr);
|
||||
break;
|
||||
case 0xB0: // MOV AL, immed
|
||||
info.Mnemonic = "MOV";
|
||||
info.Args = string.Format("AL, {0:X2}h", ReadMemory(addr++));
|
||||
break;
|
||||
case 0xB1: // MOV CL, immed
|
||||
info.Mnemonic = "MOV";
|
||||
info.Args = string.Format("CL, {0:X2}h", ReadMemory(addr++));
|
||||
break;
|
||||
case 0xB2: // MOV DL, immed
|
||||
info.Mnemonic = "MOV";
|
||||
info.Args = string.Format("DL, {0:X2}h", ReadMemory(addr++));
|
||||
break;
|
||||
case 0xB3: // MOV BL, immed
|
||||
info.Mnemonic = "MOV";
|
||||
info.Args = string.Format("BL, {0:X2}h", ReadMemory(addr++));
|
||||
break;
|
||||
case 0xB4: // MOV AH, immed
|
||||
info.Mnemonic = "MOV";
|
||||
info.Args = string.Format("AH, {0:X2}h", ReadMemory(addr++));
|
||||
break;
|
||||
case 0xB5: // MOV CH, immed
|
||||
info.Mnemonic = "MOV";
|
||||
info.Args = string.Format("CH, {0:X2}h", ReadMemory(addr++));
|
||||
break;
|
||||
case 0xB6: // MOV DH, immed
|
||||
info.Mnemonic = "MOV";
|
||||
info.Args = string.Format("DH, {0:X2}h", ReadMemory(addr++));
|
||||
break;
|
||||
case 0xB7: // MOV BH, immed
|
||||
info.Mnemonic = "MOV";
|
||||
info.Args = string.Format("BH, {0:X2}h", ReadMemory(addr++));
|
||||
break;
|
||||
case 0xB8: // MOV AX, immed
|
||||
info.Mnemonic = "MOV";
|
||||
info.Args = string.Format("AX, {0:X4}h", ReadWord(addr)); addr += 2;
|
||||
break;
|
||||
case 0xB9: // MOV CX, immed
|
||||
info.Mnemonic = "MOV";
|
||||
info.Args = string.Format("CX, {0:X4}h", ReadWord(addr)); addr += 2;
|
||||
break;
|
||||
case 0xBA: // MOV DX, immed
|
||||
info.Mnemonic = "MOV";
|
||||
info.Args = string.Format("DX, {0:X4}h", ReadWord(addr)); addr += 2;
|
||||
break;
|
||||
case 0xBB: // MOV BX, immed
|
||||
info.Mnemonic = "MOV";
|
||||
info.Args = string.Format("BX, {0:X4}h", ReadWord(addr)); addr += 2;
|
||||
break;
|
||||
case 0xBC: // MOV SP, immed
|
||||
info.Mnemonic = "MOV";
|
||||
info.Args = string.Format("SP, {0:X4}h", ReadWord(addr)); addr += 2;
|
||||
break;
|
||||
case 0xBD: // MOV BP, immed
|
||||
info.Mnemonic = "MOV";
|
||||
info.Args = string.Format("BP, {0:X4}h", ReadWord(addr)); addr += 2;
|
||||
break;
|
||||
case 0xBE: // MOV SI, immed
|
||||
info.Mnemonic = "MOV";
|
||||
info.Args = string.Format("SI, {0:X4}h", ReadWord(addr)); addr += 2;
|
||||
break;
|
||||
case 0xBF: // MOV DI, immed
|
||||
info.Mnemonic = "MOV";
|
||||
info.Args = string.Format("DI, {0:X4}h", ReadWord(addr)); addr += 2;
|
||||
break;
|
||||
default:
|
||||
info.Mnemonic = "DB";
|
||||
info.Args = string.Format("{0:X2}h", op1);
|
||||
break;
|
||||
}
|
||||
|
||||
info.Length = addr - info.Addr;
|
||||
var sb = new StringBuilder();
|
||||
for (int p = info.Addr; p < info.Addr + info.Length; p++)
|
||||
sb.AppendFormat("{0:X2}", ReadMemory(p));
|
||||
info.RawBytes = sb.ToString();
|
||||
|
||||
return info;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
using System;
|
||||
|
||||
namespace BizHawk.Emulation.CPUs.x86
|
||||
{
|
||||
public partial class x86<CpuType> where CpuType: struct, x86CpuType
|
||||
{
|
||||
public void Execute(int cycles)
|
||||
{
|
||||
Console.WriteLine(Disassemble((CS << 4) + IP));
|
||||
byte opcode1 = ReadMemory((CS << 4) + IP);
|
||||
IP++;
|
||||
|
||||
switch (opcode1)
|
||||
{
|
||||
case 0xB0: // MOV AL, imm
|
||||
AL = ReadMemory((CS << 4) + IP++);
|
||||
PendingCycles -= timing_mov_ri8;
|
||||
break;
|
||||
case 0xB1: // MOV CL, immed
|
||||
CL = ReadMemory((CS << 4) + IP++);
|
||||
PendingCycles -= timing_mov_ri8;
|
||||
break;
|
||||
case 0xB2: // MOV DL, immed
|
||||
DL = ReadMemory((CS << 4) + IP++);
|
||||
PendingCycles -= timing_mov_ri8;
|
||||
break;
|
||||
case 0xB3: // MOV BL, immed
|
||||
BL = ReadMemory((CS << 4) + IP++);
|
||||
PendingCycles -= timing_mov_ri8;
|
||||
break;
|
||||
case 0xB4: // MOV AH, immed
|
||||
AH = ReadMemory((CS << 4) + IP++);
|
||||
PendingCycles -= timing_mov_ri8;
|
||||
break;
|
||||
case 0xB5: // MOV CH, immed
|
||||
CH = ReadMemory((CS << 4) + IP++);
|
||||
PendingCycles -= timing_mov_ri8;
|
||||
break;
|
||||
case 0xB6: // MOV DH, immed
|
||||
DH = ReadMemory((CS << 4) + IP++);
|
||||
PendingCycles -= timing_mov_ri8;
|
||||
break;
|
||||
case 0xB7: // MOV BH, immed
|
||||
BH = ReadMemory((CS << 4) + IP++);
|
||||
PendingCycles -= timing_mov_ri8;
|
||||
break;
|
||||
case 0xB8: // MOV AX, immed
|
||||
AX = (ushort)(ReadMemory((CS << 4) + IP++) + (ReadMemory((CS << 4) + IP++) << 8));
|
||||
PendingCycles -= timing_mov_ri16;
|
||||
break;
|
||||
case 0xB9: // MOV CX, imm
|
||||
CX = (ushort)(ReadMemory((CS << 4) + IP++) + (ReadMemory((CS << 4) + IP++) << 8));
|
||||
PendingCycles -= timing_mov_ri16;
|
||||
break;
|
||||
case 0xBA: // MOV DX, immed
|
||||
DX = (ushort)(ReadMemory((CS << 4) + IP++) + (ReadMemory((CS << 4) + IP++) << 8));
|
||||
PendingCycles -= timing_mov_ri16;
|
||||
break;
|
||||
case 0xBB: // MOV BX, immed
|
||||
BX = (ushort)(ReadMemory((CS << 4) + IP++) + (ReadMemory((CS << 4) + IP++) << 8));
|
||||
PendingCycles -= timing_mov_ri16;
|
||||
break;
|
||||
case 0xBC: // MOV SP, immed
|
||||
SP = (ushort)(ReadMemory((CS << 4) + IP++) + (ReadMemory((CS << 4) + IP++) << 8));
|
||||
PendingCycles -= timing_mov_ri16;
|
||||
break;
|
||||
case 0xBD: // MOV BP, immed
|
||||
BP = (ushort)(ReadMemory((CS << 4) + IP++) + (ReadMemory((CS << 4) + IP++) << 8));
|
||||
PendingCycles -= timing_mov_ri16;
|
||||
break;
|
||||
case 0xBE: // MOV SI, immed
|
||||
SI = (ushort)(ReadMemory((CS << 4) + IP++) + (ReadMemory((CS << 4) + IP++) << 8));
|
||||
PendingCycles -= timing_mov_ri16;
|
||||
break;
|
||||
case 0xBF: // MOV DI, immed
|
||||
DI = (ushort)(ReadMemory((CS << 4) + IP++) + (ReadMemory((CS << 4) + IP++) << 8));
|
||||
PendingCycles -= timing_mov_ri16;
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
namespace BizHawk.Emulation.CPUs.x86
|
||||
{
|
||||
public partial class x86<CpuType> where CpuType : struct, x86CpuType
|
||||
{
|
||||
// TODO test if static on these is a performance boost
|
||||
// it would be appropriate if so because closed types have their own static variables
|
||||
private int timing_mov_ri8;
|
||||
private int timing_mov_ri16;
|
||||
|
||||
private void InitTiming()
|
||||
{
|
||||
if (typeof(CpuType) == typeof(Intel8086))
|
||||
{
|
||||
timing_mov_ri8 = 4;
|
||||
timing_mov_ri16 = 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace BizHawk.Emulation.CPUs.x86
|
||||
{
|
||||
public interface x86CpuType { };
|
||||
public struct Intel8086 : x86CpuType { };
|
||||
|
||||
public sealed partial class x86<CpuType> where CpuType: struct, x86CpuType
|
||||
{
|
||||
// Machine State
|
||||
public Register16 RegAX;
|
||||
public Register16 RegBX;
|
||||
public Register16 RegCX;
|
||||
public Register16 RegDX;
|
||||
|
||||
public ushort CS;
|
||||
public ushort DS;
|
||||
public ushort ES;
|
||||
public ushort SS;
|
||||
|
||||
public ushort SI;
|
||||
public ushort DI;
|
||||
public ushort IP;
|
||||
public ushort SP;
|
||||
public ushort BP;
|
||||
|
||||
public bool CF;
|
||||
public bool PF;
|
||||
public bool AF;
|
||||
public bool ZF;
|
||||
public bool SF;
|
||||
public bool TP;
|
||||
public bool IF;
|
||||
public bool DF;
|
||||
public bool OF;
|
||||
|
||||
public ushort Flags
|
||||
{
|
||||
get
|
||||
{
|
||||
ushort value = 2;
|
||||
if (CF) value |= 1;
|
||||
if (PF) value |= 4;
|
||||
if (AF) value |= 16;
|
||||
if (ZF) value |= 64;
|
||||
if (SF) value |= 128;
|
||||
if (TP) value |= 256;
|
||||
if (IF) value |= 512;
|
||||
if (DF) value |= 1024;
|
||||
if (OF) value |= 2048;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public int PendingCycles;
|
||||
public int TotalExecutedCycles;
|
||||
|
||||
// Memory Access
|
||||
public Func<int, byte> ReadMemory;
|
||||
public Action<int, byte> WriteMemory;
|
||||
|
||||
public x86()
|
||||
{
|
||||
InitTiming();
|
||||
}
|
||||
|
||||
// We expect these properties to get inlined by the CLR -- at some point we should test this assumption
|
||||
public ushort AX { get { return RegAX.Word; } set { RegAX.Word = value; } }
|
||||
public ushort BX { get { return RegBX.Word; } set { RegBX.Word = value; } }
|
||||
public ushort CX { get { return RegCX.Word; } set { RegCX.Word = value; } }
|
||||
public ushort DX { get { return RegDX.Word; } set { RegDX.Word = value; } }
|
||||
public byte AL { get { return RegAX.Low; } set { RegAX.Low = value; } }
|
||||
public byte BL { get { return RegBX.Low; } set { RegBX.Low = value; } }
|
||||
public byte CL { get { return RegCX.Low; } set { RegCX.Low = value; } }
|
||||
public byte DL { get { return RegDX.Low; } set { RegDX.Low = value; } }
|
||||
public byte AH { get { return RegAX.High; } set { RegAX.High = value; } }
|
||||
public byte BH { get { return RegBX.High; } set { RegBX.High = value; } }
|
||||
public byte CH { get { return RegCX.High; } set { RegCX.High = value; } }
|
||||
public byte DH { get { return RegDX.High; } set { RegDX.High = value; } }
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct Register16
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public ushort Word;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public byte Low;
|
||||
|
||||
[FieldOffset(1)]
|
||||
public byte High;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("{0:X4}", Word);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
namespace BizHawk.Emulation.Consoles.Gameboy
|
||||
{
|
||||
public partial class Gameboy
|
||||
{
|
||||
public static readonly byte[] Bios =
|
||||
{
|
||||
0x31, 0xFE, 0xFF, 0xAF, 0x21, 0xFF, 0x9F, 0x32, 0xCB, 0x7C, 0x20, 0xFB, 0x21, 0x26, 0xFF, 0x0E,
|
||||
0x11, 0x3E, 0x80, 0x32, 0xE2, 0x0C, 0x3E, 0xF3, 0xE2, 0x32, 0x3E, 0x77, 0x77, 0x3E, 0xFC, 0xE0,
|
||||
0x47, 0x11, 0x04, 0x01, 0x21, 0x10, 0x80, 0x1A, 0xCD, 0x95, 0x00, 0xCD, 0x96, 0x00, 0x13, 0x7B,
|
||||
0xFE, 0x34, 0x20, 0xF3, 0x11, 0xD8, 0x00, 0x06, 0x08, 0x1A, 0x13, 0x22, 0x23, 0x05, 0x20, 0xF9,
|
||||
0x3E, 0x19, 0xEA, 0x10, 0x99, 0x21, 0x2F, 0x99, 0x0E, 0x0C, 0x3D, 0x28, 0x08, 0x32, 0x0D, 0x20,
|
||||
0xF9, 0x2E, 0x0F, 0x18, 0xF3, 0x67, 0x3E, 0x64, 0x57, 0xE0, 0x42, 0x3E, 0x91, 0xE0, 0x40, 0x04,
|
||||
0x1E, 0x02, 0x0E, 0x0C, 0xF0, 0x44, 0xFE, 0x90, 0x20, 0xFA, 0x0D, 0x20, 0xF7, 0x1D, 0x20, 0xF2,
|
||||
0x0E, 0x13, 0x24, 0x7C, 0x1E, 0x83, 0xFE, 0x62, 0x28, 0x06, 0x1E, 0xC1, 0xFE, 0x64, 0x20, 0x06,
|
||||
0x7B, 0xE2, 0x0C, 0x3E, 0x87, 0xF2, 0xF0, 0x42, 0x90, 0xE0, 0x42, 0x15, 0x20, 0xD2, 0x05, 0x20,
|
||||
0x4F, 0x16, 0x20, 0x18, 0xCB, 0x4F, 0x06, 0x04, 0xC5, 0xCB, 0x11, 0x17, 0xC1, 0xCB, 0x11, 0x17,
|
||||
0x05, 0x20, 0xF5, 0x22, 0x23, 0x22, 0x23, 0xC9, 0xCE, 0xED, 0x66, 0x66, 0xCC, 0x0D, 0x00, 0x0B,
|
||||
0x03, 0x73, 0x00, 0x83, 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x08, 0x11, 0x1F, 0x88, 0x89, 0x00, 0x0E,
|
||||
0xDC, 0xCC, 0x6E, 0xE6, 0xDD, 0xDD, 0xD9, 0x99, 0xBB, 0xBB, 0x67, 0x63, 0x6E, 0x0E, 0xEC, 0xCC,
|
||||
0xDD, 0xDC, 0x99, 0x9F, 0xBB, 0xB9, 0x33, 0x3E, 0x3c, 0x42, 0xB9, 0xA5, 0xB9, 0xA5, 0x42, 0x4C,
|
||||
0x21, 0x04, 0x01, 0x11, 0xA8, 0x00, 0x1A, 0x13, 0xBE, 0x20, 0xFE, 0x23, 0x7D, 0xFE, 0x34, 0x20,
|
||||
0xF5, 0x06, 0x19, 0x78, 0x86, 0x23, 0x05, 0x20, 0xFB, 0x86, 0x20, 0xFE, 0x3E, 0x01, 0xE0, 0x50
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,758 @@
|
|||
using BizHawk.Core;
|
||||
|
||||
namespace VgMuseum.Gameboy
|
||||
{
|
||||
partial class Debugger
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
this.btnRun = new System.Windows.Forms.Button();
|
||||
this.btnStepInto = new System.Windows.Forms.Button();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.label3 = new System.Windows.Forms.Label();
|
||||
this.label4 = new System.Windows.Forms.Label();
|
||||
this.label5 = new System.Windows.Forms.Label();
|
||||
this.label6 = new System.Windows.Forms.Label();
|
||||
this.txtRegAF = new System.Windows.Forms.TextBox();
|
||||
this.txtRegDE = new System.Windows.Forms.TextBox();
|
||||
this.txtRegPC = new System.Windows.Forms.TextBox();
|
||||
this.txtRegSP = new System.Windows.Forms.TextBox();
|
||||
this.txtRegHL = new System.Windows.Forms.TextBox();
|
||||
this.txtRegBC = new System.Windows.Forms.TextBox();
|
||||
this.groupBox1 = new System.Windows.Forms.GroupBox();
|
||||
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.checkFlag_Z = new System.Windows.Forms.CheckBox();
|
||||
this.label8 = new System.Windows.Forms.Label();
|
||||
this.label11 = new System.Windows.Forms.Label();
|
||||
this.label9 = new System.Windows.Forms.Label();
|
||||
this.checkFlag_H = new System.Windows.Forms.CheckBox();
|
||||
this.checkFlag_N = new System.Windows.Forms.CheckBox();
|
||||
this.checkFlag_C = new System.Windows.Forms.CheckBox();
|
||||
this.label12 = new System.Windows.Forms.Label();
|
||||
this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.vScrollBar1 = new System.Windows.Forms.VScrollBar();
|
||||
this.btnSeekPC = new System.Windows.Forms.Button();
|
||||
this.btnSeekUser = new System.Windows.Forms.Button();
|
||||
this.txtSeekUser = new System.Windows.Forms.TextBox();
|
||||
this.listBreakpoints = new System.Windows.Forms.ListBox();
|
||||
this.menuContextBreakpoints = new System.Windows.Forms.ContextMenuStrip(this.components);
|
||||
this.miBreakpointAdd = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.miBreakpointDelete = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.label10 = new System.Windows.Forms.Label();
|
||||
this.timerRunUpdate = new System.Windows.Forms.Timer(this.components);
|
||||
this.btnBreak = new System.Windows.Forms.Button();
|
||||
this.txtFrame = new System.Windows.Forms.TextBox();
|
||||
this.label13 = new System.Windows.Forms.Label();
|
||||
this.label14 = new System.Windows.Forms.Label();
|
||||
this.label15 = new System.Windows.Forms.Label();
|
||||
this.txtLine = new System.Windows.Forms.TextBox();
|
||||
this.txtDot = new System.Windows.Forms.TextBox();
|
||||
this.panel1 = new System.Windows.Forms.Panel();
|
||||
this.checkViewBg = new System.Windows.Forms.CheckBox();
|
||||
this.checkViewObj = new System.Windows.Forms.CheckBox();
|
||||
this.label7 = new System.Windows.Forms.Label();
|
||||
this.label16 = new System.Windows.Forms.Label();
|
||||
this.checkViewObjNoLimit = new System.Windows.Forms.CheckBox();
|
||||
this.lblInputActive = new System.Windows.Forms.Label();
|
||||
this.viewTiles0x9000 = new ViewportPanel();
|
||||
this.viewTiles0x8000 = new ViewportPanel();
|
||||
this.panelMemory = new ScrollableViewportPanel();
|
||||
this.viewDisassembly = new ViewportPanel();
|
||||
this.viewBG = new ViewportPanel();
|
||||
this.groupBox1.SuspendLayout();
|
||||
this.tableLayoutPanel1.SuspendLayout();
|
||||
this.tableLayoutPanel2.SuspendLayout();
|
||||
this.menuContextBreakpoints.SuspendLayout();
|
||||
this.panel1.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// btnRun
|
||||
//
|
||||
this.btnRun.Location = new System.Drawing.Point(377, 2);
|
||||
this.btnRun.Name = "btnRun";
|
||||
this.btnRun.Size = new System.Drawing.Size(75, 23);
|
||||
this.btnRun.TabIndex = 1;
|
||||
this.btnRun.Text = "Run";
|
||||
this.btnRun.UseVisualStyleBackColor = true;
|
||||
this.btnRun.Click += new System.EventHandler(this.btnRun_Click);
|
||||
//
|
||||
// btnStepInto
|
||||
//
|
||||
this.btnStepInto.Location = new System.Drawing.Point(458, 2);
|
||||
this.btnStepInto.Name = "btnStepInto";
|
||||
this.btnStepInto.Size = new System.Drawing.Size(75, 23);
|
||||
this.btnStepInto.TabIndex = 2;
|
||||
this.btnStepInto.Text = "Step Into";
|
||||
this.btnStepInto.UseVisualStyleBackColor = true;
|
||||
this.btnStepInto.Click += new System.EventHandler(this.btnStepInto_Click);
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.Location = new System.Drawing.Point(369, 63);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(23, 13);
|
||||
this.label1.TabIndex = 3;
|
||||
this.label1.Text = "AF:";
|
||||
//
|
||||
// label2
|
||||
//
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.Location = new System.Drawing.Point(428, 63);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(24, 13);
|
||||
this.label2.TabIndex = 4;
|
||||
this.label2.Text = "BC:";
|
||||
//
|
||||
// label3
|
||||
//
|
||||
this.label3.AutoSize = true;
|
||||
this.label3.Location = new System.Drawing.Point(367, 83);
|
||||
this.label3.Name = "label3";
|
||||
this.label3.Size = new System.Drawing.Size(25, 13);
|
||||
this.label3.TabIndex = 5;
|
||||
this.label3.Text = "DE:";
|
||||
//
|
||||
// label4
|
||||
//
|
||||
this.label4.AutoSize = true;
|
||||
this.label4.Location = new System.Drawing.Point(428, 83);
|
||||
this.label4.Name = "label4";
|
||||
this.label4.Size = new System.Drawing.Size(24, 13);
|
||||
this.label4.TabIndex = 6;
|
||||
this.label4.Text = "HL:";
|
||||
//
|
||||
// label5
|
||||
//
|
||||
this.label5.AutoSize = true;
|
||||
this.label5.Location = new System.Drawing.Point(428, 104);
|
||||
this.label5.Name = "label5";
|
||||
this.label5.Size = new System.Drawing.Size(24, 13);
|
||||
this.label5.TabIndex = 7;
|
||||
this.label5.Text = "PC:";
|
||||
//
|
||||
// label6
|
||||
//
|
||||
this.label6.AutoSize = true;
|
||||
this.label6.Location = new System.Drawing.Point(367, 104);
|
||||
this.label6.Name = "label6";
|
||||
this.label6.Size = new System.Drawing.Size(24, 13);
|
||||
this.label6.TabIndex = 8;
|
||||
this.label6.Text = "SP:";
|
||||
//
|
||||
// txtRegAF
|
||||
//
|
||||
this.txtRegAF.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.txtRegAF.Location = new System.Drawing.Point(392, 60);
|
||||
this.txtRegAF.MaxLength = 4;
|
||||
this.txtRegAF.Name = "txtRegAF";
|
||||
this.txtRegAF.Size = new System.Drawing.Size(34, 20);
|
||||
this.txtRegAF.TabIndex = 10;
|
||||
this.txtRegAF.Text = "FFF0";
|
||||
this.txtRegAF.Validating += new System.ComponentModel.CancelEventHandler(this.reg16_Validating);
|
||||
//
|
||||
// txtRegDE
|
||||
//
|
||||
this.txtRegDE.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.txtRegDE.Location = new System.Drawing.Point(392, 80);
|
||||
this.txtRegDE.MaxLength = 4;
|
||||
this.txtRegDE.Name = "txtRegDE";
|
||||
this.txtRegDE.Size = new System.Drawing.Size(34, 20);
|
||||
this.txtRegDE.TabIndex = 11;
|
||||
this.txtRegDE.Text = "FFF0";
|
||||
this.txtRegDE.Validating += new System.ComponentModel.CancelEventHandler(this.reg16_Validating);
|
||||
//
|
||||
// txtRegPC
|
||||
//
|
||||
this.txtRegPC.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.txtRegPC.Location = new System.Drawing.Point(452, 100);
|
||||
this.txtRegPC.MaxLength = 4;
|
||||
this.txtRegPC.Name = "txtRegPC";
|
||||
this.txtRegPC.Size = new System.Drawing.Size(34, 20);
|
||||
this.txtRegPC.TabIndex = 12;
|
||||
this.txtRegPC.Text = "FFF0";
|
||||
this.txtRegPC.Validating += new System.ComponentModel.CancelEventHandler(this.reg16_Validating);
|
||||
//
|
||||
// txtRegSP
|
||||
//
|
||||
this.txtRegSP.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.txtRegSP.Location = new System.Drawing.Point(392, 100);
|
||||
this.txtRegSP.MaxLength = 4;
|
||||
this.txtRegSP.Name = "txtRegSP";
|
||||
this.txtRegSP.Size = new System.Drawing.Size(34, 20);
|
||||
this.txtRegSP.TabIndex = 15;
|
||||
this.txtRegSP.Text = "FFF0";
|
||||
this.txtRegSP.Validating += new System.ComponentModel.CancelEventHandler(this.reg16_Validating);
|
||||
//
|
||||
// txtRegHL
|
||||
//
|
||||
this.txtRegHL.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.txtRegHL.Location = new System.Drawing.Point(452, 80);
|
||||
this.txtRegHL.MaxLength = 4;
|
||||
this.txtRegHL.Name = "txtRegHL";
|
||||
this.txtRegHL.Size = new System.Drawing.Size(34, 20);
|
||||
this.txtRegHL.TabIndex = 14;
|
||||
this.txtRegHL.Text = "FFF0";
|
||||
this.txtRegHL.Validating += new System.ComponentModel.CancelEventHandler(this.reg16_Validating);
|
||||
//
|
||||
// txtRegBC
|
||||
//
|
||||
this.txtRegBC.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.txtRegBC.Location = new System.Drawing.Point(452, 60);
|
||||
this.txtRegBC.MaxLength = 4;
|
||||
this.txtRegBC.Name = "txtRegBC";
|
||||
this.txtRegBC.Size = new System.Drawing.Size(34, 20);
|
||||
this.txtRegBC.TabIndex = 13;
|
||||
this.txtRegBC.Text = "FFF0";
|
||||
this.txtRegBC.Validating += new System.ComponentModel.CancelEventHandler(this.reg16_Validating);
|
||||
//
|
||||
// groupBox1
|
||||
//
|
||||
this.groupBox1.Controls.Add(this.tableLayoutPanel1);
|
||||
this.groupBox1.Location = new System.Drawing.Point(370, 149);
|
||||
this.groupBox1.Name = "groupBox1";
|
||||
this.groupBox1.Size = new System.Drawing.Size(96, 59);
|
||||
this.groupBox1.TabIndex = 16;
|
||||
this.groupBox1.TabStop = false;
|
||||
this.groupBox1.Text = "CPU Flags";
|
||||
//
|
||||
// tableLayoutPanel1
|
||||
//
|
||||
this.tableLayoutPanel1.AutoSize = true;
|
||||
this.tableLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
||||
this.tableLayoutPanel1.ColumnCount = 4;
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
|
||||
this.tableLayoutPanel1.Controls.Add(this.checkFlag_Z, 0, 1);
|
||||
this.tableLayoutPanel1.Controls.Add(this.label8, 0, 0);
|
||||
this.tableLayoutPanel1.Controls.Add(this.label11, 1, 0);
|
||||
this.tableLayoutPanel1.Controls.Add(this.label9, 2, 0);
|
||||
this.tableLayoutPanel1.Controls.Add(this.checkFlag_H, 2, 1);
|
||||
this.tableLayoutPanel1.Controls.Add(this.checkFlag_N, 1, 1);
|
||||
this.tableLayoutPanel1.Controls.Add(this.checkFlag_C, 3, 1);
|
||||
this.tableLayoutPanel1.Controls.Add(this.label12, 3, 0);
|
||||
this.tableLayoutPanel1.Location = new System.Drawing.Point(6, 19);
|
||||
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||
this.tableLayoutPanel1.RowCount = 2;
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(80, 33);
|
||||
this.tableLayoutPanel1.TabIndex = 17;
|
||||
//
|
||||
// checkFlag_Z
|
||||
//
|
||||
this.checkFlag_Z.AutoSize = true;
|
||||
this.checkFlag_Z.Location = new System.Drawing.Point(3, 16);
|
||||
this.checkFlag_Z.Name = "checkFlag_Z";
|
||||
this.checkFlag_Z.Size = new System.Drawing.Size(14, 14);
|
||||
this.checkFlag_Z.TabIndex = 8;
|
||||
this.checkFlag_Z.UseVisualStyleBackColor = true;
|
||||
this.checkFlag_Z.CheckedChanged += new System.EventHandler(this.cpuflag_checkChanged);
|
||||
//
|
||||
// label8
|
||||
//
|
||||
this.label8.AutoSize = true;
|
||||
this.label8.Location = new System.Drawing.Point(3, 0);
|
||||
this.label8.Name = "label8";
|
||||
this.label8.Size = new System.Drawing.Size(14, 13);
|
||||
this.label8.TabIndex = 2;
|
||||
this.label8.Text = "Z";
|
||||
//
|
||||
// label11
|
||||
//
|
||||
this.label11.AutoSize = true;
|
||||
this.label11.Location = new System.Drawing.Point(23, 0);
|
||||
this.label11.Name = "label11";
|
||||
this.label11.Size = new System.Drawing.Size(14, 13);
|
||||
this.label11.TabIndex = 5;
|
||||
this.label11.Text = "N";
|
||||
//
|
||||
// label9
|
||||
//
|
||||
this.label9.AutoSize = true;
|
||||
this.label9.Location = new System.Drawing.Point(43, 0);
|
||||
this.label9.Name = "label9";
|
||||
this.label9.Size = new System.Drawing.Size(14, 13);
|
||||
this.label9.TabIndex = 3;
|
||||
this.label9.Text = "H";
|
||||
//
|
||||
// checkFlag_H
|
||||
//
|
||||
this.checkFlag_H.AutoSize = true;
|
||||
this.checkFlag_H.Location = new System.Drawing.Point(43, 16);
|
||||
this.checkFlag_H.Name = "checkFlag_H";
|
||||
this.checkFlag_H.Size = new System.Drawing.Size(14, 14);
|
||||
this.checkFlag_H.TabIndex = 0;
|
||||
this.checkFlag_H.UseVisualStyleBackColor = true;
|
||||
this.checkFlag_H.CheckedChanged += new System.EventHandler(this.cpuflag_checkChanged);
|
||||
//
|
||||
// checkFlag_N
|
||||
//
|
||||
this.checkFlag_N.AutoSize = true;
|
||||
this.checkFlag_N.Location = new System.Drawing.Point(23, 16);
|
||||
this.checkFlag_N.Name = "checkFlag_N";
|
||||
this.checkFlag_N.Size = new System.Drawing.Size(14, 14);
|
||||
this.checkFlag_N.TabIndex = 10;
|
||||
this.checkFlag_N.UseVisualStyleBackColor = true;
|
||||
this.checkFlag_N.CheckedChanged += new System.EventHandler(this.cpuflag_checkChanged);
|
||||
//
|
||||
// checkFlag_C
|
||||
//
|
||||
this.checkFlag_C.AutoSize = true;
|
||||
this.checkFlag_C.Location = new System.Drawing.Point(63, 16);
|
||||
this.checkFlag_C.Name = "checkFlag_C";
|
||||
this.checkFlag_C.Size = new System.Drawing.Size(14, 14);
|
||||
this.checkFlag_C.TabIndex = 11;
|
||||
this.checkFlag_C.UseVisualStyleBackColor = true;
|
||||
this.checkFlag_C.CheckedChanged += new System.EventHandler(this.cpuflag_checkChanged);
|
||||
//
|
||||
// label12
|
||||
//
|
||||
this.label12.AutoSize = true;
|
||||
this.label12.Location = new System.Drawing.Point(63, 0);
|
||||
this.label12.Name = "label12";
|
||||
this.label12.Size = new System.Drawing.Size(14, 13);
|
||||
this.label12.TabIndex = 6;
|
||||
this.label12.Text = "C";
|
||||
//
|
||||
// tableLayoutPanel2
|
||||
//
|
||||
this.tableLayoutPanel2.ColumnCount = 2;
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
|
||||
this.tableLayoutPanel2.Controls.Add(this.vScrollBar1, 1, 0);
|
||||
this.tableLayoutPanel2.Controls.Add(this.viewDisassembly, 0, 0);
|
||||
this.tableLayoutPanel2.Location = new System.Drawing.Point(2, 2);
|
||||
this.tableLayoutPanel2.Name = "tableLayoutPanel2";
|
||||
this.tableLayoutPanel2.RowCount = 1;
|
||||
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
this.tableLayoutPanel2.Size = new System.Drawing.Size(350, 205);
|
||||
this.tableLayoutPanel2.TabIndex = 19;
|
||||
//
|
||||
// vScrollBar1
|
||||
//
|
||||
this.vScrollBar1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.vScrollBar1.Location = new System.Drawing.Point(334, 0);
|
||||
this.vScrollBar1.Maximum = 65535;
|
||||
this.vScrollBar1.Name = "vScrollBar1";
|
||||
this.vScrollBar1.Size = new System.Drawing.Size(16, 205);
|
||||
this.vScrollBar1.TabIndex = 20;
|
||||
this.vScrollBar1.Scroll += new System.Windows.Forms.ScrollEventHandler(this.vScrollBar1_Scroll);
|
||||
//
|
||||
// btnSeekPC
|
||||
//
|
||||
this.btnSeekPC.Location = new System.Drawing.Point(489, 100);
|
||||
this.btnSeekPC.Name = "btnSeekPC";
|
||||
this.btnSeekPC.Size = new System.Drawing.Size(52, 20);
|
||||
this.btnSeekPC.TabIndex = 20;
|
||||
this.btnSeekPC.Text = "Seek";
|
||||
this.btnSeekPC.UseVisualStyleBackColor = true;
|
||||
this.btnSeekPC.Click += new System.EventHandler(this.btnSeekPC_Click);
|
||||
//
|
||||
// btnSeekUser
|
||||
//
|
||||
this.btnSeekUser.Location = new System.Drawing.Point(489, 124);
|
||||
this.btnSeekUser.Name = "btnSeekUser";
|
||||
this.btnSeekUser.Size = new System.Drawing.Size(52, 20);
|
||||
this.btnSeekUser.TabIndex = 21;
|
||||
this.btnSeekUser.Text = "Seek";
|
||||
this.btnSeekUser.UseVisualStyleBackColor = true;
|
||||
this.btnSeekUser.Click += new System.EventHandler(this.btnSeekUser_Click);
|
||||
//
|
||||
// txtSeekUser
|
||||
//
|
||||
this.txtSeekUser.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.txtSeekUser.Location = new System.Drawing.Point(452, 123);
|
||||
this.txtSeekUser.MaxLength = 4;
|
||||
this.txtSeekUser.Name = "txtSeekUser";
|
||||
this.txtSeekUser.Size = new System.Drawing.Size(34, 20);
|
||||
this.txtSeekUser.TabIndex = 22;
|
||||
//
|
||||
// listBreakpoints
|
||||
//
|
||||
this.listBreakpoints.ContextMenuStrip = this.menuContextBreakpoints;
|
||||
this.listBreakpoints.FormattingEnabled = true;
|
||||
this.listBreakpoints.Location = new System.Drawing.Point(915, 245);
|
||||
this.listBreakpoints.Name = "listBreakpoints";
|
||||
this.listBreakpoints.Size = new System.Drawing.Size(120, 95);
|
||||
this.listBreakpoints.TabIndex = 25;
|
||||
//
|
||||
// menuContextBreakpoints
|
||||
//
|
||||
this.menuContextBreakpoints.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.miBreakpointAdd,
|
||||
this.miBreakpointDelete});
|
||||
this.menuContextBreakpoints.Name = "menuContextBreakpoints";
|
||||
this.menuContextBreakpoints.Size = new System.Drawing.Size(117, 48);
|
||||
this.menuContextBreakpoints.Opening += new System.ComponentModel.CancelEventHandler(this.menuContextBreakpoints_Opening);
|
||||
//
|
||||
// miBreakpointAdd
|
||||
//
|
||||
this.miBreakpointAdd.Name = "miBreakpointAdd";
|
||||
this.miBreakpointAdd.Size = new System.Drawing.Size(116, 22);
|
||||
this.miBreakpointAdd.Text = "Add";
|
||||
//
|
||||
// miBreakpointDelete
|
||||
//
|
||||
this.miBreakpointDelete.Name = "miBreakpointDelete";
|
||||
this.miBreakpointDelete.Size = new System.Drawing.Size(116, 22);
|
||||
this.miBreakpointDelete.Text = "Delete";
|
||||
//
|
||||
// label10
|
||||
//
|
||||
this.label10.AutoSize = true;
|
||||
this.label10.Location = new System.Drawing.Point(912, 227);
|
||||
this.label10.Name = "label10";
|
||||
this.label10.Size = new System.Drawing.Size(63, 13);
|
||||
this.label10.TabIndex = 26;
|
||||
this.label10.Text = "Breakpoints";
|
||||
//
|
||||
// timerRunUpdate
|
||||
//
|
||||
this.timerRunUpdate.Enabled = true;
|
||||
this.timerRunUpdate.Tick += new System.EventHandler(this.timerRunUpdate_Tick);
|
||||
//
|
||||
// btnBreak
|
||||
//
|
||||
this.btnBreak.Location = new System.Drawing.Point(377, 27);
|
||||
this.btnBreak.Name = "btnBreak";
|
||||
this.btnBreak.Size = new System.Drawing.Size(75, 23);
|
||||
this.btnBreak.TabIndex = 29;
|
||||
this.btnBreak.Text = "Break";
|
||||
this.btnBreak.UseVisualStyleBackColor = true;
|
||||
this.btnBreak.Click += new System.EventHandler(this.btnBreak_Click);
|
||||
//
|
||||
// txtFrame
|
||||
//
|
||||
this.txtFrame.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.txtFrame.Location = new System.Drawing.Point(775, 21);
|
||||
this.txtFrame.MaxLength = 4;
|
||||
this.txtFrame.Name = "txtFrame";
|
||||
this.txtFrame.ReadOnly = true;
|
||||
this.txtFrame.Size = new System.Drawing.Size(48, 20);
|
||||
this.txtFrame.TabIndex = 31;
|
||||
//
|
||||
// label13
|
||||
//
|
||||
this.label13.AutoSize = true;
|
||||
this.label13.Location = new System.Drawing.Point(730, 24);
|
||||
this.label13.Name = "label13";
|
||||
this.label13.Size = new System.Drawing.Size(39, 13);
|
||||
this.label13.TabIndex = 32;
|
||||
this.label13.Text = "Frame:";
|
||||
//
|
||||
// label14
|
||||
//
|
||||
this.label14.AutoSize = true;
|
||||
this.label14.Location = new System.Drawing.Point(739, 44);
|
||||
this.label14.Name = "label14";
|
||||
this.label14.Size = new System.Drawing.Size(30, 13);
|
||||
this.label14.TabIndex = 33;
|
||||
this.label14.Text = "Line:";
|
||||
//
|
||||
// label15
|
||||
//
|
||||
this.label15.AutoSize = true;
|
||||
this.label15.Location = new System.Drawing.Point(742, 66);
|
||||
this.label15.Name = "label15";
|
||||
this.label15.Size = new System.Drawing.Size(27, 13);
|
||||
this.label15.TabIndex = 34;
|
||||
this.label15.Text = "Dot:";
|
||||
//
|
||||
// txtLine
|
||||
//
|
||||
this.txtLine.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.txtLine.Location = new System.Drawing.Point(775, 43);
|
||||
this.txtLine.MaxLength = 4;
|
||||
this.txtLine.Name = "txtLine";
|
||||
this.txtLine.ReadOnly = true;
|
||||
this.txtLine.Size = new System.Drawing.Size(48, 20);
|
||||
this.txtLine.TabIndex = 35;
|
||||
//
|
||||
// txtDot
|
||||
//
|
||||
this.txtDot.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.txtDot.Location = new System.Drawing.Point(775, 65);
|
||||
this.txtDot.MaxLength = 4;
|
||||
this.txtDot.Name = "txtDot";
|
||||
this.txtDot.ReadOnly = true;
|
||||
this.txtDot.Size = new System.Drawing.Size(48, 20);
|
||||
this.txtDot.TabIndex = 36;
|
||||
//
|
||||
// panel1
|
||||
//
|
||||
this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
|
||||
this.panel1.Controls.Add(this.panelMemory);
|
||||
this.panel1.Location = new System.Drawing.Point(5, 225);
|
||||
this.panel1.Name = "panel1";
|
||||
this.panel1.Size = new System.Drawing.Size(545, 181);
|
||||
this.panel1.TabIndex = 38;
|
||||
//
|
||||
// checkViewBg
|
||||
//
|
||||
this.checkViewBg.AutoSize = true;
|
||||
this.checkViewBg.Checked = true;
|
||||
this.checkViewBg.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||
this.checkViewBg.Location = new System.Drawing.Point(564, 3);
|
||||
this.checkViewBg.Name = "checkViewBg";
|
||||
this.checkViewBg.Size = new System.Drawing.Size(41, 17);
|
||||
this.checkViewBg.TabIndex = 39;
|
||||
this.checkViewBg.Text = "BG";
|
||||
this.checkViewBg.UseVisualStyleBackColor = true;
|
||||
this.checkViewBg.CheckedChanged += new System.EventHandler(this.checkViewBg_CheckedChanged);
|
||||
//
|
||||
// checkViewObj
|
||||
//
|
||||
this.checkViewObj.AutoSize = true;
|
||||
this.checkViewObj.Checked = true;
|
||||
this.checkViewObj.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||
this.checkViewObj.Location = new System.Drawing.Point(611, 3);
|
||||
this.checkViewObj.Name = "checkViewObj";
|
||||
this.checkViewObj.Size = new System.Drawing.Size(46, 17);
|
||||
this.checkViewObj.TabIndex = 40;
|
||||
this.checkViewObj.Text = "OBJ";
|
||||
this.checkViewObj.UseVisualStyleBackColor = true;
|
||||
this.checkViewObj.CheckedChanged += new System.EventHandler(this.checkViewObj_CheckedChanged);
|
||||
//
|
||||
// label7
|
||||
//
|
||||
this.label7.AutoSize = true;
|
||||
this.label7.Location = new System.Drawing.Point(561, 195);
|
||||
this.label7.Name = "label7";
|
||||
this.label7.Size = new System.Drawing.Size(45, 13);
|
||||
this.label7.TabIndex = 42;
|
||||
this.label7.Text = "0x8000:";
|
||||
//
|
||||
// label16
|
||||
//
|
||||
this.label16.AutoSize = true;
|
||||
this.label16.Location = new System.Drawing.Point(701, 197);
|
||||
this.label16.Name = "label16";
|
||||
this.label16.Size = new System.Drawing.Size(45, 13);
|
||||
this.label16.TabIndex = 44;
|
||||
this.label16.Text = "0x8800:";
|
||||
//
|
||||
// checkViewObjNoLimit
|
||||
//
|
||||
this.checkViewObjNoLimit.AutoSize = true;
|
||||
this.checkViewObjNoLimit.Checked = true;
|
||||
this.checkViewObjNoLimit.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||
this.checkViewObjNoLimit.Location = new System.Drawing.Point(663, 3);
|
||||
this.checkViewObjNoLimit.Name = "checkViewObjNoLimit";
|
||||
this.checkViewObjNoLimit.Size = new System.Drawing.Size(44, 17);
|
||||
this.checkViewObjNoLimit.TabIndex = 45;
|
||||
this.checkViewObjNoLimit.Text = ">10";
|
||||
this.checkViewObjNoLimit.UseVisualStyleBackColor = true;
|
||||
this.checkViewObjNoLimit.CheckedChanged += new System.EventHandler(this.checkViewObjNoLimit_CheckedChanged);
|
||||
//
|
||||
// lblInputActive
|
||||
//
|
||||
this.lblInputActive.AutoSize = true;
|
||||
this.lblInputActive.Location = new System.Drawing.Point(711, 10);
|
||||
this.lblInputActive.Name = "lblInputActive";
|
||||
this.lblInputActive.Size = new System.Drawing.Size(13, 13);
|
||||
this.lblInputActive.TabIndex = 46;
|
||||
this.lblInputActive.Text = "o";
|
||||
//
|
||||
// viewTiles0x9000
|
||||
//
|
||||
this.viewTiles0x9000.Location = new System.Drawing.Point(704, 215);
|
||||
this.viewTiles0x9000.Name = "viewTiles0x9000";
|
||||
this.viewTiles0x9000.Size = new System.Drawing.Size(128, 128);
|
||||
this.viewTiles0x9000.TabIndex = 43;
|
||||
this.viewTiles0x9000.Paint += new System.Windows.Forms.PaintEventHandler(this.viewTiles0x9000_Paint);
|
||||
//
|
||||
// viewTiles0x8000
|
||||
//
|
||||
this.viewTiles0x8000.Location = new System.Drawing.Point(561, 215);
|
||||
this.viewTiles0x8000.Name = "viewTiles0x8000";
|
||||
this.viewTiles0x8000.Size = new System.Drawing.Size(128, 128);
|
||||
this.viewTiles0x8000.TabIndex = 41;
|
||||
this.viewTiles0x8000.Paint += new System.Windows.Forms.PaintEventHandler(this.viewTiles0x8000_Paint);
|
||||
//
|
||||
// panelMemory
|
||||
//
|
||||
this.panelMemory.AutoSize = true;
|
||||
this.panelMemory.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.panelMemory.Location = new System.Drawing.Point(0, 0);
|
||||
this.panelMemory.Name = "panelMemory";
|
||||
this.panelMemory.ScrollLargeChange = 10;
|
||||
this.panelMemory.ScrollMax = 4095;
|
||||
this.panelMemory.Size = new System.Drawing.Size(541, 177);
|
||||
this.panelMemory.TabIndex = 37;
|
||||
this.panelMemory.Paint += new System.Windows.Forms.PaintEventHandler(this.panelMemory_Paint);
|
||||
this.panelMemory.Scroll += new System.Windows.Forms.ScrollEventHandler(this.panelMemory_Scroll);
|
||||
//
|
||||
// viewDisassembly
|
||||
//
|
||||
this.viewDisassembly.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.viewDisassembly.Location = new System.Drawing.Point(3, 3);
|
||||
this.viewDisassembly.Name = "viewDisassembly";
|
||||
this.viewDisassembly.Size = new System.Drawing.Size(328, 199);
|
||||
this.viewDisassembly.TabIndex = 0;
|
||||
this.viewDisassembly.Paint += new System.Windows.Forms.PaintEventHandler(this.viewDisassembly_Paint);
|
||||
//
|
||||
// viewBG
|
||||
//
|
||||
this.viewBG.Location = new System.Drawing.Point(564, 27);
|
||||
this.viewBG.Name = "viewBG";
|
||||
this.viewBG.Size = new System.Drawing.Size(160, 144);
|
||||
this.viewBG.TabIndex = 23;
|
||||
this.viewBG.Paint += new System.Windows.Forms.PaintEventHandler(this.viewBG_Paint);
|
||||
this.viewBG.Leave += new System.EventHandler(this.viewBG_Leave);
|
||||
this.viewBG.KeyUp += new System.Windows.Forms.KeyEventHandler(this.viewBG_KeyUp);
|
||||
this.viewBG.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.viewBG_KeyPress);
|
||||
this.viewBG.Enter += new System.EventHandler(this.viewBG_Enter);
|
||||
this.viewBG.KeyDown += new System.Windows.Forms.KeyEventHandler(this.viewBG_KeyDown);
|
||||
//
|
||||
// Debugger
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(1065, 483);
|
||||
this.Controls.Add(this.lblInputActive);
|
||||
this.Controls.Add(this.checkViewObjNoLimit);
|
||||
this.Controls.Add(this.viewTiles0x9000);
|
||||
this.Controls.Add(this.label16);
|
||||
this.Controls.Add(this.label7);
|
||||
this.Controls.Add(this.viewTiles0x8000);
|
||||
this.Controls.Add(this.checkViewObj);
|
||||
this.Controls.Add(this.panel1);
|
||||
this.Controls.Add(this.checkViewBg);
|
||||
this.Controls.Add(this.label13);
|
||||
this.Controls.Add(this.txtDot);
|
||||
this.Controls.Add(this.txtLine);
|
||||
this.Controls.Add(this.label15);
|
||||
this.Controls.Add(this.label14);
|
||||
this.Controls.Add(this.btnBreak);
|
||||
this.Controls.Add(this.txtFrame);
|
||||
this.Controls.Add(this.label10);
|
||||
this.Controls.Add(this.tableLayoutPanel2);
|
||||
this.Controls.Add(this.listBreakpoints);
|
||||
this.Controls.Add(this.txtSeekUser);
|
||||
this.Controls.Add(this.viewBG);
|
||||
this.Controls.Add(this.groupBox1);
|
||||
this.Controls.Add(this.btnSeekUser);
|
||||
this.Controls.Add(this.btnSeekPC);
|
||||
this.Controls.Add(this.btnStepInto);
|
||||
this.Controls.Add(this.txtRegAF);
|
||||
this.Controls.Add(this.txtRegSP);
|
||||
this.Controls.Add(this.txtRegHL);
|
||||
this.Controls.Add(this.txtRegBC);
|
||||
this.Controls.Add(this.txtRegPC);
|
||||
this.Controls.Add(this.txtRegDE);
|
||||
this.Controls.Add(this.label2);
|
||||
this.Controls.Add(this.label6);
|
||||
this.Controls.Add(this.label5);
|
||||
this.Controls.Add(this.label4);
|
||||
this.Controls.Add(this.label3);
|
||||
this.Controls.Add(this.btnRun);
|
||||
this.Controls.Add(this.label1);
|
||||
this.KeyPreview = true;
|
||||
this.Name = "Debugger";
|
||||
this.Text = "Debugger";
|
||||
this.groupBox1.ResumeLayout(false);
|
||||
this.groupBox1.PerformLayout();
|
||||
this.tableLayoutPanel1.ResumeLayout(false);
|
||||
this.tableLayoutPanel1.PerformLayout();
|
||||
this.tableLayoutPanel2.ResumeLayout(false);
|
||||
this.menuContextBreakpoints.ResumeLayout(false);
|
||||
this.panel1.ResumeLayout(false);
|
||||
this.panel1.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private ViewportPanel viewDisassembly;
|
||||
private System.Windows.Forms.Button btnRun;
|
||||
private System.Windows.Forms.Button btnStepInto;
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.Label label2;
|
||||
private System.Windows.Forms.Label label3;
|
||||
private System.Windows.Forms.Label label4;
|
||||
private System.Windows.Forms.Label label5;
|
||||
private System.Windows.Forms.Label label6;
|
||||
private System.Windows.Forms.TextBox txtRegAF;
|
||||
private System.Windows.Forms.TextBox txtRegDE;
|
||||
private System.Windows.Forms.TextBox txtRegPC;
|
||||
private System.Windows.Forms.TextBox txtRegSP;
|
||||
private System.Windows.Forms.TextBox txtRegHL;
|
||||
private System.Windows.Forms.TextBox txtRegBC;
|
||||
private System.Windows.Forms.GroupBox groupBox1;
|
||||
private System.Windows.Forms.CheckBox checkFlag_H;
|
||||
private System.Windows.Forms.Label label8;
|
||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
|
||||
private System.Windows.Forms.Label label9;
|
||||
private System.Windows.Forms.Label label11;
|
||||
private System.Windows.Forms.Label label12;
|
||||
private System.Windows.Forms.CheckBox checkFlag_Z;
|
||||
private System.Windows.Forms.CheckBox checkFlag_N;
|
||||
private System.Windows.Forms.CheckBox checkFlag_C;
|
||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2;
|
||||
private System.Windows.Forms.VScrollBar vScrollBar1;
|
||||
private System.Windows.Forms.Button btnSeekPC;
|
||||
private System.Windows.Forms.Button btnSeekUser;
|
||||
private System.Windows.Forms.TextBox txtSeekUser;
|
||||
private ViewportPanel viewBG;
|
||||
private System.Windows.Forms.ListBox listBreakpoints;
|
||||
private System.Windows.Forms.Label label10;
|
||||
private System.Windows.Forms.ContextMenuStrip menuContextBreakpoints;
|
||||
private System.Windows.Forms.ToolStripMenuItem miBreakpointAdd;
|
||||
private System.Windows.Forms.ToolStripMenuItem miBreakpointDelete;
|
||||
private System.Windows.Forms.Timer timerRunUpdate;
|
||||
private System.Windows.Forms.Button btnBreak;
|
||||
private System.Windows.Forms.TextBox txtFrame;
|
||||
private System.Windows.Forms.Label label13;
|
||||
private System.Windows.Forms.Label label14;
|
||||
private System.Windows.Forms.Label label15;
|
||||
private System.Windows.Forms.TextBox txtLine;
|
||||
private System.Windows.Forms.TextBox txtDot;
|
||||
private ScrollableViewportPanel panelMemory;
|
||||
private System.Windows.Forms.Panel panel1;
|
||||
private System.Windows.Forms.CheckBox checkViewBg;
|
||||
private System.Windows.Forms.CheckBox checkViewObj;
|
||||
private ViewportPanel viewTiles0x8000;
|
||||
private System.Windows.Forms.Label label7;
|
||||
private ViewportPanel viewTiles0x9000;
|
||||
private System.Windows.Forms.Label label16;
|
||||
private System.Windows.Forms.CheckBox checkViewObjNoLimit;
|
||||
private System.Windows.Forms.Label lblInputActive;
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,382 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using BizHawk.Core;
|
||||
|
||||
namespace VgMuseum.Gameboy
|
||||
{
|
||||
public partial class Debugger : Form
|
||||
{
|
||||
readonly Gameboy gb;
|
||||
public Debugger(Gameboy gb)
|
||||
{
|
||||
this.gb = gb;
|
||||
InitializeComponent();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void viewDisassembly_Paint(object sender, PaintEventArgs e)
|
||||
{
|
||||
e.Graphics.Clear(SystemColors.Control);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
ushort addr = (ushort)vScrollBar1.Value;
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
ushort size;
|
||||
string str = VgMuseum.Z80GB.Disassembler.DAsm(addr, gb.Cpu.ReadMemory, out size);
|
||||
addr += size;
|
||||
sb.AppendLine(str);
|
||||
}
|
||||
using (Font font = new Font("Courier New", 8))
|
||||
{
|
||||
e.Graphics.DrawString(sb.ToString(), font, Brushes.Black, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Refresh()
|
||||
{
|
||||
txtRegAF.Text = string.Format("{0:X4}", gb.Cpu.RegisterAF);
|
||||
txtRegDE.Text = string.Format("{0:X4}", gb.Cpu.RegisterDE);
|
||||
txtRegPC.Text = string.Format("{0:X4}", gb.Cpu.RegisterPC);
|
||||
txtRegBC.Text = string.Format("{0:X4}", gb.Cpu.RegisterBC);
|
||||
txtRegHL.Text = string.Format("{0:X4}", gb.Cpu.RegisterHL);
|
||||
txtRegSP.Text = string.Format("{0:X4}", gb.Cpu.RegisterSP);
|
||||
checkFlag_Z.Checked = gb.Cpu.FlagZ;
|
||||
checkFlag_N.Checked = gb.Cpu.FlagN;
|
||||
checkFlag_H.Checked = gb.Cpu.FlagH;
|
||||
checkFlag_C.Checked = gb.Cpu.FlagC;
|
||||
txtFrame.Text = gb.Registers.Timing.frame.ToString();
|
||||
txtLine.Text = gb.Registers.Timing.line.ToString();
|
||||
txtDot.Text = gb.Registers.Timing.dot.ToString();
|
||||
base.Refresh();
|
||||
}
|
||||
|
||||
void DoStepInto()
|
||||
{
|
||||
DoBreak();
|
||||
gb.SingleStepInto();
|
||||
vScrollBar1.Value = gb.Cpu.RegisterPC;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
bool Running = false;
|
||||
void DoRun()
|
||||
{
|
||||
Running = true;
|
||||
gb.RunForever();
|
||||
Running = false;
|
||||
}
|
||||
|
||||
void DoBreak()
|
||||
{
|
||||
gb.DebugBreak = true;
|
||||
}
|
||||
|
||||
private void btnStepInto_Click(object sender, EventArgs e)
|
||||
{
|
||||
DoStepInto();
|
||||
}
|
||||
|
||||
bool TryParse16(string str, out ushort val)
|
||||
{
|
||||
return ushort.TryParse(str, NumberStyles.HexNumber, CultureInfo.CurrentCulture, out val);
|
||||
}
|
||||
|
||||
private void reg16_Validating(object sender, CancelEventArgs e)
|
||||
{
|
||||
var tb = (TextBox)sender;
|
||||
ushort val = 0;
|
||||
TryParse16(tb.Text, out val);
|
||||
if (sender == txtRegAF) gb.Cpu.RegisterAF = val;
|
||||
if (sender == txtRegDE) gb.Cpu.RegisterDE = val;
|
||||
if (sender == txtRegPC) gb.Cpu.RegisterPC = val;
|
||||
if (sender == txtRegBC) gb.Cpu.RegisterBC = val;
|
||||
if (sender == txtRegHL) gb.Cpu.RegisterHL = val;
|
||||
if (sender == txtRegSP) gb.Cpu.RegisterSP = val;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void vScrollBar1_Scroll(object sender, ScrollEventArgs e)
|
||||
{
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void btnSeekPC_Click(object sender, EventArgs e)
|
||||
{
|
||||
vScrollBar1.Value = gb.Cpu.RegisterPC;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void btnSeekUser_Click(object sender, EventArgs e)
|
||||
{
|
||||
ushort val;
|
||||
if (TryParse16(txtSeekUser.Text, out val))
|
||||
{
|
||||
vScrollBar1.Value = val;
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
|
||||
{
|
||||
switch (keyData)
|
||||
{
|
||||
case Keys.F11:
|
||||
DoStepInto();
|
||||
break;
|
||||
case Keys.F5:
|
||||
DoRun();
|
||||
break;
|
||||
case Keys.Escape:
|
||||
DoBreak();
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void menuContextBreakpoints_Opening(object sender, CancelEventArgs e)
|
||||
{
|
||||
miBreakpointDelete.Enabled = false;
|
||||
}
|
||||
|
||||
private void viewTiles0x8000_Paint(object sender, PaintEventArgs e)
|
||||
{
|
||||
using (Bitmap bmp = new Bitmap(160, 144, e.Graphics))
|
||||
{
|
||||
var linebuf = new byte[128];
|
||||
for (int y = 0; y < 144; y++)
|
||||
{
|
||||
gb.RenderTileLine(y, linebuf, 0x8000);
|
||||
for (int x = 0; x < 128; x++)
|
||||
{
|
||||
int gray = linebuf[x] << 6;
|
||||
bmp.SetPixel(x, y, Color.FromArgb(gray, gray, gray));
|
||||
}
|
||||
}
|
||||
e.Graphics.DrawImageUnscaled(bmp, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void viewTiles0x9000_Paint(object sender, PaintEventArgs e)
|
||||
{
|
||||
using (Bitmap bmp = new Bitmap(160, 144, e.Graphics))
|
||||
{
|
||||
var linebuf = new byte[128];
|
||||
for (int y = 0; y < 144; y++)
|
||||
{
|
||||
gb.RenderTileLine(y, linebuf, 0x8800);
|
||||
for (int x = 0; x < 128; x++)
|
||||
{
|
||||
int gray = linebuf[x] << 6;
|
||||
bmp.SetPixel(x, y, Color.FromArgb(gray, gray, gray));
|
||||
}
|
||||
}
|
||||
e.Graphics.DrawImageUnscaled(bmp, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void viewBG_Paint(object sender, PaintEventArgs e)
|
||||
{
|
||||
using (Bitmap bmp = new Bitmap(160, 144, e.Graphics))
|
||||
{
|
||||
var linebuf = new byte[160];
|
||||
for (int y = 0; y < 144; y++)
|
||||
{
|
||||
if(checkViewBg.Checked) gb.RenderBGLine(y, linebuf, false);
|
||||
if (checkViewObj.Checked) gb.RenderOBJLine(y, linebuf, !checkViewObjNoLimit.Checked);
|
||||
for (int x = 0; x < 160; x++)
|
||||
{
|
||||
int gray = linebuf[x]<<6;
|
||||
bmp.SetPixel(x, y, Color.FromArgb(gray, gray, gray));
|
||||
}
|
||||
}
|
||||
e.Graphics.DrawImageUnscaled(bmp, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void timerRunUpdate_Tick(object sender, EventArgs e)
|
||||
{
|
||||
if(Running)
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void btnRun_Click(object sender, EventArgs e)
|
||||
{
|
||||
DoRun();
|
||||
}
|
||||
|
||||
private void btnBreak_Click(object sender, EventArgs e)
|
||||
{
|
||||
DoBreak();
|
||||
}
|
||||
|
||||
private void checkFlag_Z_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void cpuflag_checkChanged(object sender, EventArgs e)
|
||||
{
|
||||
var cb = (CheckBox)sender;
|
||||
if (sender == checkFlag_Z) gb.Cpu.FlagZ = cb.Checked;
|
||||
if (sender == checkFlag_N) gb.Cpu.FlagN = cb.Checked;
|
||||
if (sender == checkFlag_H) gb.Cpu.FlagH = cb.Checked;
|
||||
if (sender == checkFlag_C) gb.Cpu.FlagC = cb.Checked;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
static char Remap(byte val)
|
||||
{
|
||||
if (val < ' ') return '.';
|
||||
else if (val >= 0x80) return '.';
|
||||
else return (char)val;
|
||||
}
|
||||
|
||||
private void panelMemory_Paint(object sender, PaintEventArgs e)
|
||||
{
|
||||
e.Graphics.Clear(SystemColors.Control);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
ushort addr = (ushort)(panelMemory.Scrollbar.Value * 16);
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
sb.AppendFormat("{0}:{1:X4} ", gb.DescribeParagraph(addr),addr);
|
||||
for (int x = 0; x < 16; x++)
|
||||
sb.AppendFormat("{0:X2} ", gb.ReadMemory((ushort)(addr+x)));
|
||||
sb.AppendFormat("| ");
|
||||
for (int x = 0; x < 16; x++)
|
||||
sb.Append(Remap(gb.ReadMemory((ushort)(addr + x))));
|
||||
sb.AppendLine();
|
||||
addr += 16;
|
||||
}
|
||||
using (Font font = new Font("Courier New", 8))
|
||||
{
|
||||
e.Graphics.DrawString(sb.ToString(), font, Brushes.Black, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void panelMemory_Scroll(object sender, ScrollEventArgs e)
|
||||
{
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void checkViewBg_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void checkViewObj_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void checkViewObjNoLimit_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void viewBG_KeyUp(object sender, KeyEventArgs e)
|
||||
{
|
||||
UpdateKeys();
|
||||
}
|
||||
|
||||
private void viewBG_KeyPress(object sender, KeyPressEventArgs e)
|
||||
{
|
||||
UpdateKeys();
|
||||
}
|
||||
|
||||
private void viewBG_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
UpdateKeys();
|
||||
}
|
||||
|
||||
protected override bool ProcessDialogKey(Keys keyData)
|
||||
{
|
||||
if (viewBG.Focused) return true;
|
||||
else return base.ProcessDialogKey(keyData);
|
||||
}
|
||||
|
||||
void UpdateKeys()
|
||||
{
|
||||
gb.Registers.Input.up = Keyboard.IsKeyDown(Keys.Up);
|
||||
gb.Registers.Input.down = Keyboard.IsKeyDown(Keys.Down);
|
||||
gb.Registers.Input.left = Keyboard.IsKeyDown(Keys.Left);
|
||||
gb.Registers.Input.right = Keyboard.IsKeyDown(Keys.Right);
|
||||
gb.Registers.Input.a = Keyboard.IsKeyDown(Keys.X);
|
||||
gb.Registers.Input.b = Keyboard.IsKeyDown(Keys.Z);
|
||||
gb.Registers.Input.select = Keyboard.IsKeyDown(Keys.E);
|
||||
gb.Registers.Input.right = Keyboard.IsKeyDown(Keys.R);
|
||||
}
|
||||
|
||||
private void viewBG_Enter(object sender, EventArgs e)
|
||||
{
|
||||
lblInputActive.ForeColor = Color.Red;
|
||||
UpdateKeys();
|
||||
}
|
||||
|
||||
private void viewBG_Leave(object sender, EventArgs e)
|
||||
{
|
||||
lblInputActive.ForeColor = SystemColors.ControlText;
|
||||
gb.Registers.Input.up = false;
|
||||
gb.Registers.Input.down = false;
|
||||
gb.Registers.Input.left = false;
|
||||
gb.Registers.Input.right = false;
|
||||
gb.Registers.Input.a = false;
|
||||
gb.Registers.Input.b = false;
|
||||
gb.Registers.Input.select = false;
|
||||
gb.Registers.Input.right = false;
|
||||
}
|
||||
|
||||
static class Keyboard
|
||||
{
|
||||
[Flags]
|
||||
private enum KeyStates
|
||||
{
|
||||
None = 0,
|
||||
Down = 1,
|
||||
Toggled = 2
|
||||
}
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
|
||||
private static extern short GetKeyState(int keyCode);
|
||||
|
||||
private static KeyStates GetKeyState(Keys key)
|
||||
{
|
||||
KeyStates state = KeyStates.None;
|
||||
|
||||
short retVal = GetKeyState((int)key);
|
||||
|
||||
//If the high-order bit is 1, the key is down
|
||||
//otherwise, it is up.
|
||||
if ((retVal & 0x8000) == 0x8000)
|
||||
state |= KeyStates.Down;
|
||||
|
||||
//If the low-order bit is 1, the key is toggled.
|
||||
if ((retVal & 1) == 1)
|
||||
state |= KeyStates.Toggled;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
public static bool IsKeyDown(Keys key)
|
||||
{
|
||||
return KeyStates.Down == (GetKeyState(key) & KeyStates.Down);
|
||||
}
|
||||
|
||||
public static bool IsKeyToggled(Keys key)
|
||||
{
|
||||
return KeyStates.Toggled == (GetKeyState(key) & KeyStates.Toggled);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="menuContextBreakpoints.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<metadata name="timerRunUpdate.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>193, 17</value>
|
||||
</metadata>
|
||||
</root>
|
|
@ -0,0 +1,838 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using BizHawk.Emulation.CPUs.Z80GB;
|
||||
|
||||
namespace BizHawk.Emulation.Consoles.Gameboy
|
||||
{
|
||||
public partial class Gameboy : IEmulator
|
||||
{
|
||||
public enum ECartType
|
||||
{
|
||||
ROM_ONLY = 0x00,
|
||||
ROM_MBC1 = 0x01,
|
||||
ROM_MBC1_RAM = 0x02,
|
||||
ROM_MBC1_RAM_BATT = 0x03,
|
||||
ROM_MBC2 = 0x05,
|
||||
ROM_MBC2_BATTERY = 0x06,
|
||||
ROM_RAM = 0x08,
|
||||
ROM_RAM_BATTERY = 0x09,
|
||||
ROM_MMM01 = 0x0B,
|
||||
ROM_MMM01_SRAM = 0x0C,
|
||||
ROM_MMM01_SRAM_BATT = 0x0D,
|
||||
ROM_MBC3_TIMER_BATT = 0x0F,
|
||||
ROM_MBC3_TIMER_RAM_BATT = 0x10,
|
||||
ROM_MBC3 = 0x11,
|
||||
ROM_MBC3_RAM = 0x12,
|
||||
ROM_MBC3_RAM_BATT = 0x13,
|
||||
ROM_MBC5 = 0x19,
|
||||
ROM_MBC5_RAM = 0x1A,
|
||||
ROM_MBC5_RAM_BATT = 0x1B,
|
||||
ROM_MBC5_RUMBLE = 0x1C,
|
||||
ROM_MBC5_RUMBLE_SRAM = 0x1D,
|
||||
ROM_MBC5_RUMBLE_SRAM_BATT = 0x1E,
|
||||
PocketCamera = 0x1F,
|
||||
Bandai_TAMA5 = 0xFD,
|
||||
Hudson_HuC_3 = 0xFE,
|
||||
Hudson_HuC_1 = 0xFF,
|
||||
}
|
||||
|
||||
public enum ESystemType
|
||||
{
|
||||
GB, //original gameboy
|
||||
GBP, //gameboy pocket
|
||||
GBC, //gameboy color
|
||||
SGB, //super gameboy
|
||||
GBA, //gameboy advance (emulating old hardware)
|
||||
}
|
||||
|
||||
public ECartType CartType;
|
||||
public ESystemType SystemType;
|
||||
|
||||
public struct TCartFlags
|
||||
{
|
||||
public bool GBC; //cart indicates itself as GBC aware
|
||||
public bool SGB; //cart indicates itself as SGB aware
|
||||
}
|
||||
public TCartFlags CartFlags = new TCartFlags();
|
||||
|
||||
static byte SetBit8(byte variable, int bit, bool val)
|
||||
{
|
||||
int mask = 1 << bit;
|
||||
int temp = variable;
|
||||
temp &= ~mask;
|
||||
if (val) temp |= mask;
|
||||
return (byte)temp;
|
||||
}
|
||||
|
||||
static byte SetBit8(byte variable, int bit, int val)
|
||||
{
|
||||
return SetBit8(variable, bit, val != 0);
|
||||
}
|
||||
|
||||
static bool GetBit8(byte variable, int bit)
|
||||
{
|
||||
return (variable & (1 << bit)) != 0;
|
||||
}
|
||||
|
||||
public class TRegisters
|
||||
{
|
||||
Gameboy gb;
|
||||
public TRegisters(Gameboy gb)
|
||||
{
|
||||
this.gb = gb;
|
||||
STAT = new TSTAT(gb);
|
||||
}
|
||||
|
||||
public bool BiosMapped = true;
|
||||
public class TLCDC
|
||||
{
|
||||
byte val;
|
||||
public bool Enabled { get { return GetBit8(val, 7); } set { val = SetBit8(val, 7, value); } }
|
||||
public ETileMap WindowTileMap { get { return GetBit8(val, 6) ? ETileMap.Region_9C00_9FFF : ETileMap.Region_9800_9BFF; } set { val = SetBit8(val, 6, (int)value); } }
|
||||
public ushort WindowTileMapAddr { get { return GetTileMapAddrFor(WindowTileMap); } }
|
||||
public bool WindowDisplay { get { return GetBit8(val, 5); } set { val = SetBit8(val, 5, value); } }
|
||||
public ETileData TileData { get { return GetBit8(val, 4) ? ETileData.Region_8000_8FFF : ETileData.Region_8800_97FF; } set { val = SetBit8(val, 4, (int)value); } }
|
||||
public ushort TileDataAddr { get { return GetTileDataAddrFor(TileData); } }
|
||||
public ETileMap BgTileMap { get { return GetBit8(val, 3) ? ETileMap.Region_9C00_9FFF : ETileMap.Region_9800_9BFF; } set { val = SetBit8(val, 3, (int)value); } }
|
||||
public ushort BgTileMapAddr { get { return GetTileMapAddrFor(BgTileMap); } }
|
||||
public EObjSize ObjSize { get { return GetBit8(val, 2) ? EObjSize.ObjSize_8x16 : EObjSize.ObjSize_8x8; } set { val = SetBit8(val, 2, (int)value); } }
|
||||
public bool ObjEnabled { get { return GetBit8(val, 1); } set { val = SetBit8(val, 1, value); } }
|
||||
public bool BgEnabled { get { return GetBit8(val, 0); } set { val = SetBit8(val, 0, value); } }
|
||||
|
||||
public byte Read() { return val; }
|
||||
public void Write(byte value) { val = value; }
|
||||
public void Poke(byte value) { val = value; }
|
||||
|
||||
public enum ETileMap
|
||||
{
|
||||
Region_9800_9BFF = 0,
|
||||
Region_9C00_9FFF = 1,
|
||||
}
|
||||
|
||||
public enum ETileData
|
||||
{
|
||||
Region_8800_97FF = 0,
|
||||
Region_8000_8FFF = 1,
|
||||
}
|
||||
|
||||
public ushort GetTileMapAddrFor(ETileMap tm) { return (ushort)(tm == ETileMap.Region_9800_9BFF ? 0x9800 : 0x9C00); }
|
||||
public ushort GetTileDataAddrFor(ETileData tm) { return (ushort)(tm == ETileData.Region_8800_97FF ? 0x8800 : 0x8000); }
|
||||
|
||||
|
||||
public enum EObjSize
|
||||
{
|
||||
ObjSize_8x8 = 0,
|
||||
ObjSize_8x16 = 1,
|
||||
}
|
||||
}
|
||||
public TLCDC LCDC = new TLCDC();
|
||||
public byte SCY, SCX;
|
||||
|
||||
public class TSTAT
|
||||
{
|
||||
readonly Gameboy gb;
|
||||
public TSTAT(Gameboy gb) { this.gb = gb; }
|
||||
|
||||
public byte Read()
|
||||
{
|
||||
//TODO (not done yet)
|
||||
int mode;
|
||||
if (gb.Registers.Timing.line >= 160) mode = 1;
|
||||
else if (gb.Registers.Timing.dot < 80) mode = 2;
|
||||
else if (gb.Registers.Timing.dot < 172 + 80) mode = 3;
|
||||
else mode = 0;
|
||||
|
||||
return (byte)mode;
|
||||
}
|
||||
}
|
||||
public TSTAT STAT;
|
||||
|
||||
public class TTiming
|
||||
{
|
||||
public int frame;
|
||||
public int line;
|
||||
public int dot;
|
||||
}
|
||||
public TTiming Timing = new TTiming();
|
||||
|
||||
public class TInput
|
||||
{
|
||||
public bool up, down, left, right, a, b, select, start;
|
||||
int val;
|
||||
public void Write(byte value)
|
||||
{
|
||||
val = value & 0x30;
|
||||
}
|
||||
public byte Read()
|
||||
{
|
||||
if ((val & 0x10) == 0)
|
||||
{
|
||||
int ret = SetBit8(0, 0, right) | SetBit8(0, 1, left) | SetBit8(0, 2, up) | SetBit8(0, 3, down);
|
||||
return (byte) (~ret);
|
||||
}
|
||||
else if((val & 0x10) == 0)
|
||||
{
|
||||
int ret = SetBit8(0, 0, a) | SetBit8(0, 1, b) | SetBit8(0, 2, select) | SetBit8(0, 3, start);
|
||||
return (byte)(~ret);
|
||||
}
|
||||
else return 0xFF;
|
||||
|
||||
//TODO return system type???
|
||||
}
|
||||
}
|
||||
public TInput Input = new TInput();
|
||||
|
||||
public byte Read_LY() { return (byte)Timing.line; }
|
||||
|
||||
};
|
||||
public TRegisters Registers;
|
||||
|
||||
public void SingleStepInto()
|
||||
{
|
||||
Cpu.TotalExecutedCycles = 0;
|
||||
Cpu.SingleStepInto();
|
||||
int elapsed = Cpu.TotalExecutedCycles;
|
||||
Timekeeping(elapsed);
|
||||
}
|
||||
|
||||
public bool DebugBreak;
|
||||
public void RunForever()
|
||||
{
|
||||
int sanity = 0;
|
||||
|
||||
for (; ; )
|
||||
{
|
||||
SingleStepInto();
|
||||
|
||||
sanity++;
|
||||
if (sanity == 100000)
|
||||
{
|
||||
//System.Windows.Forms.Application.DoEvents();
|
||||
if (DebugBreak) break;
|
||||
sanity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
DebugBreak = false;
|
||||
}
|
||||
|
||||
public void Timekeeping(int elapsed)
|
||||
{
|
||||
Registers.Timing.dot += elapsed;
|
||||
if (Registers.Timing.dot >= 456)
|
||||
{
|
||||
Registers.Timing.line++;
|
||||
Registers.Timing.dot -= 456;
|
||||
}
|
||||
if (Registers.Timing.line > 153)
|
||||
{
|
||||
Registers.Timing.frame++;
|
||||
Registers.Timing.line = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void DetachBios()
|
||||
{
|
||||
Registers.BiosMapped = false;
|
||||
Cpu.ReadMemory = ReadMemory;
|
||||
}
|
||||
|
||||
public class TSound
|
||||
{
|
||||
public byte[] WavePatternRam = new byte[16];
|
||||
}
|
||||
|
||||
public TSound Sound;
|
||||
|
||||
public byte[] Rom;
|
||||
public byte[] WRam;
|
||||
public byte[] SRam;
|
||||
public byte[] VRam;
|
||||
public byte[] HRam;
|
||||
public byte[] OAM;
|
||||
|
||||
public Z80 Cpu;
|
||||
public MemoryMapper Mapper;
|
||||
|
||||
public Gameboy()
|
||||
{
|
||||
}
|
||||
|
||||
public void LoadGame(IGame game)
|
||||
{
|
||||
Rom = game.GetRomData();
|
||||
|
||||
//inspect mapper and GBC flags
|
||||
CartType = (ECartType)Rom[0x0147];
|
||||
Mapper = new MemoryMapper(this);
|
||||
CartFlags.GBC = Rom[0x0143] == 0x80;
|
||||
CartFlags.SGB = Rom[0x0146] == 0x03;
|
||||
}
|
||||
|
||||
public bool BootFromBios = true;
|
||||
|
||||
public void HardReset()
|
||||
{
|
||||
Cpu = new CPUs.Z80GB.Z80();
|
||||
Cpu.ReadMemory = ReadMemoryBios;
|
||||
Cpu.WriteMemory = WriteMemory;
|
||||
Cpu.Reset();
|
||||
|
||||
//setup initial cpu registers. based on no evidence:
|
||||
//registers which may be used to identify system type are judged to be important; and
|
||||
//the initial contents of the stack are judged to be unimportant.
|
||||
switch (SystemType)
|
||||
{
|
||||
case ESystemType.GB:
|
||||
case ESystemType.SGB:
|
||||
Cpu.RegisterAF = 0x01;
|
||||
break;
|
||||
case ESystemType.GBP:
|
||||
Cpu.RegisterAF = 0xFF;
|
||||
break;
|
||||
case ESystemType.GBC:
|
||||
Cpu.RegisterAF = 0x11;
|
||||
break;
|
||||
case ESystemType.GBA:
|
||||
throw new NotImplementedException(); //decide what to do
|
||||
}
|
||||
Cpu.RegisterF = 0xB0;
|
||||
if (SystemType == ESystemType.GBA)
|
||||
Cpu.RegisterB = 0x01;
|
||||
else Cpu.RegisterB = 0x00;
|
||||
Cpu.RegisterC = 0x13;
|
||||
Cpu.RegisterDE = 0x00D8;
|
||||
Cpu.RegisterHL = 0x014D;
|
||||
Cpu.RegisterSP = 0xFFFE;
|
||||
if (BootFromBios) Cpu.RegisterPC = 0x0000;
|
||||
else Cpu.RegisterPC = 0x0100;
|
||||
|
||||
WRam = new byte[32 * 1024]; //GB has 4KB of WRam; GBC has 32KB of WRam
|
||||
SRam = new byte[8 * 1024]; //different carts may have different amounts of this
|
||||
VRam = new byte[0x2000];
|
||||
HRam = new byte[128];
|
||||
OAM = new byte[0xA0];
|
||||
|
||||
Sound = new TSound();
|
||||
Registers = new TRegisters(this);
|
||||
|
||||
Registers.LCDC.Poke(0x91);
|
||||
}
|
||||
|
||||
public byte ReadMemoryBios(ushort addr)
|
||||
{
|
||||
//we speculate that the bios unmaps itself after the first read of 0x100
|
||||
if (addr < 0x100)
|
||||
return Bios[addr];
|
||||
else if(addr == 0x100)
|
||||
DetachBios();
|
||||
return ReadMemory(addr);
|
||||
}
|
||||
|
||||
public string DescribeParagraph(ushort addr)
|
||||
{
|
||||
//todo - later on, this must say "RO1F" for bank 0x1F and etc.
|
||||
//and so it must call through to the mapper
|
||||
if (addr < 0x4000)
|
||||
return "ROM0";
|
||||
else if (addr < 0x8000)
|
||||
return "ROM1";
|
||||
else if (addr < 0xA000)
|
||||
return "VRA0";
|
||||
else if (addr < 0xC000)
|
||||
return "SRA0";
|
||||
else if (addr < 0xD000)
|
||||
return "WRA0";
|
||||
else if (addr < 0xE000)
|
||||
return "WRA1";
|
||||
else if (addr < 0xFE00)
|
||||
return "ECH0";
|
||||
else if (addr < 0xFEA0)
|
||||
return "OAM ";
|
||||
else if (addr < 0xFF00)
|
||||
return "----";
|
||||
else if (addr < 0xFF80)
|
||||
return "I/O ";
|
||||
else return "HRAM";
|
||||
}
|
||||
|
||||
public byte ReadMemory(ushort addr)
|
||||
{
|
||||
if (addr < 0x8000)
|
||||
return Rom[addr];
|
||||
else if (addr < 0xA000)
|
||||
return VRam[addr - 0x8000];
|
||||
else if (addr < 0xC000)
|
||||
return SRam[addr - 0xA000];
|
||||
else if (addr < 0xD000)
|
||||
return WRam[addr - 0xC000]; //bank 0 of WRam
|
||||
else if (addr < 0xE000)
|
||||
return WRam[addr - 0xC000]; //bank 1 of WRam (needs to be switchable)
|
||||
else if (addr < 0xFE00)
|
||||
return ReadMemory((ushort)(addr - 0xE000)); //echo of WRam; unusable; reserved ????
|
||||
else if (addr < 0xFEA0)
|
||||
return OAM[addr - 0xFE00];
|
||||
else if (addr < 0xFF00)
|
||||
return 0xFF; //"unusable memory"
|
||||
else if (addr < 0xFF80)
|
||||
return ReadRegister(addr);
|
||||
else if (addr < 0xFFFF)
|
||||
return HRam[addr - 0xFF80];
|
||||
else return ReadRegister(addr);
|
||||
}
|
||||
|
||||
public byte ReadRegister(ushort addr)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0xFF00: //REG_P1 - Register for reading joy pad info and determining system type. (R/W)
|
||||
return Registers.Input.Read();
|
||||
case 0xFF01: //REG_SB - Serial transfer data (R/W)
|
||||
return 0xFF;
|
||||
case 0xFF02: //REG_SC - SIO control (R/W)
|
||||
return 0xFF;
|
||||
case 0xFF04: //REG_DIV - Divider Register (R/W)
|
||||
return 0xFF;
|
||||
case 0xFF05: //REG_TIMA - Timer counter (R/W)
|
||||
return 0xFF;
|
||||
case 0xFF06: //REG_TMA - Timer Modulo (R/W)
|
||||
return 0xFF;
|
||||
case 0xFF07: //REG_TAC - Timer Control (R/W)
|
||||
return 0xFF;
|
||||
case 0xFF0F: //REG_IF - Interrupt Flag (R/W)
|
||||
return 0xFF;
|
||||
case 0xFF10: //REG_NR10 - Sound Mode 1 register, Sweep register (R/W)
|
||||
return 0xFF;
|
||||
case 0xFF11: //REG_NR11 - Sound Mode 1 register, Sound length/Wave pattern duty (R/W)
|
||||
return 0xFF;
|
||||
case 0xFF12: //REG_NR12 - Sound Mode 1 register, Envelope (R/W)
|
||||
return 0xFF;
|
||||
case 0xFF13: //REG_NR13 - Sound Mode 1 register, Frequency lo (W)
|
||||
return 0xFF;
|
||||
case 0xFF14: //REG_NR14 - Sound Mode 1 register, Frequency hi (R/W)
|
||||
return 0xFF;
|
||||
|
||||
//0xFF15 ???????????????
|
||||
|
||||
case 0xFF16: //REG_NR21 - Sound Mode 2 register, Sound Length; Wave Pattern Duty (R/W)
|
||||
return 0xFF;
|
||||
case 0xFF17: //REG_NR22 - Sound Mode 2 register, envelope (R/W)
|
||||
return 0xFF;
|
||||
case 0xFF18: //REG_NR23 - Sound Mode 2 register, frequency lo data (W)
|
||||
return 0xFF;
|
||||
case 0xFF19: //REG_NR24 - Sound Mode 2 register, frequency hi data (R/W)
|
||||
return 0xFF;
|
||||
case 0xFF1A: //REG_NR30 - Sound Mode 3 register, Sound on/off (R/W)
|
||||
return 0xFF;
|
||||
case 0xFF1B: //REG_NR31 - Sound Mode 3 register, sound length (R/W)
|
||||
return 0xFF;
|
||||
case 0xFF1C: //REG_NR32 - Sound Mode 3 register, Select output level (R/W)
|
||||
return 0xFF;
|
||||
case 0xFF1D: //REG_NR33 - Sound Mode 3 register, frequency's lower data (W)
|
||||
return 0xFF;
|
||||
case 0xFF1E: //REG_NR34 - Sound Mode 3 register, frequency's higher data (R/W)
|
||||
return 0xFF;
|
||||
|
||||
//0xFF1F ???????????????
|
||||
|
||||
case 0xFF20: //REG_NR41 - Sound Mode 4 register, sound length (R/W)
|
||||
return 0xFF;
|
||||
case 0xFF21: //REG_NR42 - Sound Mode 4 register, envelope (R/W)
|
||||
return 0xFF;
|
||||
case 0xFF22: //REG_NR43 - Sound Mode 4 register, polynomial counter (R/W)
|
||||
return 0xFF;
|
||||
case 0xFF23: //REG_NR44 - Sound Mode 4 register, counter/consecutive; inital (R/W)
|
||||
return 0xFF;
|
||||
case 0xFF24: //REG_NR50 - Channel control / ON-OFF / Volume (R/W)
|
||||
return 0xFF;
|
||||
case 0xFF25: //REG_NR51 - Selection of Sound output terminal (R/W)
|
||||
return 0xFF;
|
||||
case 0xFF26: //REG_NR52 - Sound on/off (R/W) (Value at reset: $F1-GB, $F0-SGB)
|
||||
return 0xFF;
|
||||
case 0xFF30: return Sound.WavePatternRam[0x00]; case 0xFF31: return Sound.WavePatternRam[0x01];
|
||||
case 0xFF32: return Sound.WavePatternRam[0x02]; case 0xFF33: return Sound.WavePatternRam[0x03];
|
||||
case 0xFF34: return Sound.WavePatternRam[0x04]; case 0xFF35: return Sound.WavePatternRam[0x05];
|
||||
case 0xFF36: return Sound.WavePatternRam[0x06]; case 0xFF37: return Sound.WavePatternRam[0x07];
|
||||
case 0xFF38: return Sound.WavePatternRam[0x08]; case 0xFF39: return Sound.WavePatternRam[0x09];
|
||||
case 0xFF3A: return Sound.WavePatternRam[0x0A]; case 0xFF3B: return Sound.WavePatternRam[0x0B];
|
||||
case 0xFF3C: return Sound.WavePatternRam[0x0C]; case 0xFF3D: return Sound.WavePatternRam[0x0D];
|
||||
case 0xFF3E: return Sound.WavePatternRam[0x0E]; case 0xFF3F: return Sound.WavePatternRam[0x0F];
|
||||
case 0xFF40: //REG_LCDC - LCD Control (R/W) (value $91 at reset)
|
||||
return Registers.LCDC.Read();
|
||||
case 0xFF41: //REG_STAT - LCDC Status (R/W)
|
||||
return Registers.STAT.Read();
|
||||
case 0xFF42: //REG_SCY - Scroll Y (R/W)
|
||||
return Registers.SCY;
|
||||
case 0xFF43: //REG_SCX - Scroll X (R/W)
|
||||
return Registers.SCX;
|
||||
case 0xFF44: //REG_LY - LCDC Y-Coordinate (R)
|
||||
return Registers.Read_LY();
|
||||
case 0xFF45: //REG_LYC - LY Compare (R/W)
|
||||
return 0xFF;
|
||||
case 0xFF46: //REG_DMA - DMA Transfer and Start Address (W)
|
||||
return 0xFF;
|
||||
case 0xFF47: //REG_BGP - BG & Window Palette Data (R/W)
|
||||
return 0xFF;
|
||||
case 0xFF48: //REG_OBP0 - Object Palette 0 Data (R/W)
|
||||
return 0xFF;
|
||||
case 0xFF49: //REG_OBP1 - Object Palette 1 Data (R/W)
|
||||
return 0xFF;
|
||||
case 0xFF4A: //REG_WY - Window Y Position (R/W)
|
||||
return 0xFF;
|
||||
case 0xFF4B: //REG_WX - Window X Position (R/W)
|
||||
return 0xFF;
|
||||
case 0xFFFF: //REG_IE
|
||||
return 0xFF;
|
||||
default:
|
||||
return 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteRegister(ushort addr, byte value)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0xFF00: //REG_P1 - Register for reading joy pad info and determining system type. (R/W)
|
||||
Registers.Input.Write(value);
|
||||
break;
|
||||
case 0xFF01: //REG_SB - Serial transfer data (R/W)
|
||||
return;
|
||||
case 0xFF02: //REG_SC - SIO control (R/W)
|
||||
return;
|
||||
case 0xFF04: //REG_DIV - Divider Register (R/W)
|
||||
return;
|
||||
case 0xFF05: //REG_TIMA - Timer counter (R/W)
|
||||
return;
|
||||
case 0xFF06: //REG_TMA - Timer Modulo (R/W)
|
||||
return;
|
||||
case 0xFF07: //REG_TAC - Timer Control (R/W)
|
||||
return;
|
||||
case 0xFF0F: //REG_IF - Interrupt Flag (R/W)
|
||||
return;
|
||||
case 0xFF10: //REG_NR10 - Sound Mode 1 register, Sweep register (R/W)
|
||||
return;
|
||||
case 0xFF11: //REG_NR11 - Sound Mode 1 register, Sound length/Wave pattern duty (R/W)
|
||||
return;
|
||||
case 0xFF12: //REG_NR12 - Sound Mode 1 register, Envelope (R/W)
|
||||
return;
|
||||
case 0xFF13: //REG_NR13 - Sound Mode 1 register, Frequency lo (W)
|
||||
return;
|
||||
case 0xFF14: //REG_NR14 - Sound Mode 1 register, Frequency hi (R/W)
|
||||
return;
|
||||
|
||||
//0xFF15 ???????????????
|
||||
|
||||
case 0xFF16: //REG_NR21 - Sound Mode 2 register, Sound Length; Wave Pattern Duty (R/W)
|
||||
return;
|
||||
case 0xFF17: //REG_NR22 - Sound Mode 2 register, envelope (R/W)
|
||||
return;
|
||||
case 0xFF18: //REG_NR23 - Sound Mode 2 register, frequency lo data (W)
|
||||
return;
|
||||
case 0xFF19: //REG_NR24 - Sound Mode 2 register, frequency hi data (R/W)
|
||||
return;
|
||||
case 0xFF1A: //REG_NR30 - Sound Mode 3 register, Sound on/off (R/W)
|
||||
return;
|
||||
case 0xFF1B: //REG_NR31 - Sound Mode 3 register, sound length (R/W)
|
||||
return;
|
||||
case 0xFF1C: //REG_NR32 - Sound Mode 3 register, Select output level (R/W)
|
||||
return;
|
||||
case 0xFF1D: //REG_NR33 - Sound Mode 3 register, frequency's lower data (W)
|
||||
return;
|
||||
case 0xFF1E: //REG_NR34 - Sound Mode 3 register, frequency's higher data (R/W)
|
||||
return;
|
||||
|
||||
//0xFF1F ???????????????
|
||||
|
||||
case 0xFF20: //REG_NR41 - Sound Mode 4 register, sound length (R/W)
|
||||
return;
|
||||
case 0xFF21: //REG_NR42 - Sound Mode 4 register, envelope (R/W)
|
||||
return;
|
||||
case 0xFF22: //REG_NR43 - Sound Mode 4 register, polynomial counter (R/W)
|
||||
return;
|
||||
case 0xFF23: //REG_NR44 - Sound Mode 4 register, counter/consecutive; inital (R/W)
|
||||
return;
|
||||
case 0xFF24: //REG_NR50 - Channel control / ON-OFF / Volume (R/W)
|
||||
return;
|
||||
case 0xFF25: //REG_NR51 - Selection of Sound output terminal (R/W)
|
||||
return;
|
||||
case 0xFF26: //REG_NR52 - Sound on/off (R/W) (Value at reset: $F1-GB, $F0-SGB)
|
||||
return;
|
||||
case 0xFF30: Sound.WavePatternRam[0x00] = value; break; case 0xFF31: Sound.WavePatternRam[0x01] = value; break;
|
||||
case 0xFF32: Sound.WavePatternRam[0x02] = value; break; case 0xFF33: Sound.WavePatternRam[0x03] = value; break;
|
||||
case 0xFF34: Sound.WavePatternRam[0x04] = value; break; case 0xFF35: Sound.WavePatternRam[0x05] = value; break;
|
||||
case 0xFF36: Sound.WavePatternRam[0x06] = value; break; case 0xFF37: Sound.WavePatternRam[0x07] = value; break;
|
||||
case 0xFF38: Sound.WavePatternRam[0x08] = value; break; case 0xFF39: Sound.WavePatternRam[0x09] = value; break;
|
||||
case 0xFF3A: Sound.WavePatternRam[0x0A] = value; break; case 0xFF3B: Sound.WavePatternRam[0x0B] = value; break;
|
||||
case 0xFF3C: Sound.WavePatternRam[0x0C] = value; break; case 0xFF3D: Sound.WavePatternRam[0x0D] = value; break;
|
||||
case 0xFF3E: Sound.WavePatternRam[0x0E] = value; break; case 0xFF3F: Sound.WavePatternRam[0x0F] = value; break;
|
||||
case 0xFF40: //REG_LCDC - LCD Control (R/W) (value $91 at reset)
|
||||
Registers.LCDC.Write(value);
|
||||
break;
|
||||
case 0xFF41: //REG_STAT - LCDC Status (R/W)
|
||||
return;
|
||||
case 0xFF42: //REG_SCY - Scroll Y (R/W)
|
||||
Registers.SCY = value;
|
||||
break;
|
||||
case 0xFF43: //REG_SCX - Scroll X (R/W)
|
||||
Registers.SCX = value;
|
||||
break;
|
||||
case 0xFF44: //REG_LY - LCDC Y-Coordinate (R)
|
||||
return;
|
||||
case 0xFF45: //REG_LYC - LY Compare (R/W)
|
||||
return;
|
||||
case 0xFF46: //REG_DMA - DMA Transfer and Start Address (W)
|
||||
return;
|
||||
case 0xFF47: //REG_BGP - BG & Window Palette Data (R/W)
|
||||
return;
|
||||
case 0xFF48: //REG_OBP0 - Object Palette 0 Data (R/W)
|
||||
return;
|
||||
case 0xFF49: //REG_OBP1 - Object Palette 1 Data (R/W)
|
||||
return;
|
||||
case 0xFF4A: //REG_WY - Window Y Position (R/W)
|
||||
return;
|
||||
case 0xFF4B: //REG_WX - Window X Position (R/W)
|
||||
return;
|
||||
case 0xFFFF: //REG_IE
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteMemory(ushort addr, byte value)
|
||||
{
|
||||
if (addr < 0x8000)
|
||||
return;
|
||||
else if (addr < 0xA000)
|
||||
VRam[addr - 0x8000] = value;
|
||||
else if (addr < 0xC000)
|
||||
SRam[addr - 0xA000] = value;
|
||||
else if (addr < 0xD000)
|
||||
WRam[addr - 0xC000] = value;
|
||||
else if (addr < 0xE000)
|
||||
WRam[addr - 0xC000] = value;
|
||||
else if (addr < 0xFE00)
|
||||
WriteMemory((ushort)(addr - 0xE000), value);
|
||||
else if (addr < 0xFEA0)
|
||||
OAM[addr - 0xFE00] = value;
|
||||
else if (addr < 0xFF00)
|
||||
return;
|
||||
else if (addr < 0xFF80)
|
||||
WriteRegister(addr,value);
|
||||
else if (addr < 0xFFFF)
|
||||
HRam[addr - 0xFF80] = value;
|
||||
else WriteRegister(addr, value);
|
||||
}
|
||||
|
||||
public void FrameAdvance(bool render)
|
||||
{
|
||||
Cpu.ExecuteCycles(4096);
|
||||
}
|
||||
|
||||
public IVideoProvider VideoProvider
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public ISoundProvider SoundProvider
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public int Frame
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public byte[] SaveRam
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public bool SaveRamModified
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
set
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveStateText(System.IO.TextWriter writer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void LoadStateText(System.IO.TextReader reader)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SaveStateBinary(System.IO.BinaryWriter writer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void LoadStateBinary(System.IO.BinaryReader reader)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public byte[] SaveStateBinary()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void RenderOBJLine(int line, byte[] output, bool limit)
|
||||
{
|
||||
int height = Registers.LCDC.ObjSize == TRegisters.TLCDC.EObjSize.ObjSize_8x16 ? 16 : 8;
|
||||
List<int> sprites = new List<int>();
|
||||
|
||||
//1st pass: select sprites to draw
|
||||
for (int s = 0; s < 40; s++)
|
||||
{
|
||||
int y = OAM[s * 4 + 0];
|
||||
y -= 16;
|
||||
if (line < y) continue;
|
||||
if (line < y + height) continue;
|
||||
|
||||
sprites.Add(s);
|
||||
if (sprites.Count == 10 && limit) break; //sprite limit
|
||||
}
|
||||
|
||||
//now render from low priority to high
|
||||
for (int i = sprites.Count - 1; i >= 0; i--)
|
||||
{
|
||||
int s = sprites[i];
|
||||
int y = OAM[s*4 + 0];
|
||||
int x = OAM[s*4 + 1];
|
||||
int pat = OAM[s*4 + 2];
|
||||
byte flags = OAM[s*4 + 3];
|
||||
bool priority = GetBit8(flags, 7);
|
||||
bool yflip = GetBit8(flags, 6);
|
||||
bool xflip = GetBit8(flags, 5);
|
||||
bool pal = GetBit8(flags, 4);
|
||||
|
||||
y -= 16;
|
||||
x -= 8;
|
||||
|
||||
int sprline = line - y;
|
||||
if(yflip)
|
||||
sprline = height - sprline - 1;
|
||||
|
||||
if (height == 16) pat = ~1;
|
||||
|
||||
ushort patternAddr = (ushort)(0x8000 + (pat << 4));
|
||||
patternAddr += (ushort)(sprline << 1);
|
||||
|
||||
int _lobits = ReadMemory(patternAddr);
|
||||
patternAddr += 1;
|
||||
int _hibits = ReadMemory(patternAddr);
|
||||
|
||||
for (int j = 0; j < 8; j++)
|
||||
{
|
||||
int px = x + j;
|
||||
if (px < 0) continue;
|
||||
if (px >= 160) continue;
|
||||
|
||||
int lobits;
|
||||
int hibits;
|
||||
|
||||
if (xflip)
|
||||
{
|
||||
lobits = _lobits >> (j);
|
||||
hibits = _hibits >> (j);
|
||||
}
|
||||
else
|
||||
{
|
||||
lobits = _lobits >> (7 - j);
|
||||
hibits = _hibits >> (7 - j);
|
||||
}
|
||||
lobits &= 1;
|
||||
hibits &= 1;
|
||||
int pixel = lobits | (hibits << 1);
|
||||
output[x] = (byte) pixel;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void RenderTileLine(int line, byte[] output, ushort tiledata)
|
||||
{
|
||||
int py = line;
|
||||
int ty = py >> 3;
|
||||
int tyr = py & 7;
|
||||
int tileRowOffset = ty << 5;
|
||||
|
||||
for (int x = 0; x < 128; x++)
|
||||
{
|
||||
int px = x;
|
||||
px &= 0xFF;
|
||||
int tx = px >> 3;
|
||||
int txr = px & 7;
|
||||
int tileOffset = tileRowOffset + tx;
|
||||
int tileAddr = tileOffset;
|
||||
int tileNum = ty * 16 + tx;
|
||||
tileNum = (tileNum) & 0xFF;
|
||||
ushort patternAddr = (ushort)(tiledata + (tileNum << 4));
|
||||
patternAddr += (ushort)(tyr << 1);
|
||||
|
||||
int lobits = ReadMemory(patternAddr);
|
||||
patternAddr += 1;
|
||||
int hibits = ReadMemory(patternAddr);
|
||||
lobits >>= (7 - txr);
|
||||
hibits >>= (7 - txr);
|
||||
lobits &= 1;
|
||||
hibits &= 1;
|
||||
int pixel = lobits | (hibits << 1);
|
||||
output[x] = (byte)pixel;
|
||||
}
|
||||
}
|
||||
|
||||
public void RenderBGLine(int line, byte[] output, bool scroll)
|
||||
{
|
||||
ushort tilemap = Registers.LCDC.BgTileMapAddr;
|
||||
ushort tiledata = Registers.LCDC.TileDataAddr;
|
||||
|
||||
int tileAdjust = (Registers.LCDC.TileData == TRegisters.TLCDC.ETileData.Region_8800_97FF ? 128 : 0);
|
||||
|
||||
int py = line;
|
||||
if(scroll) line += Registers.SCY;
|
||||
py &= 0xFF;
|
||||
int ty = py >> 3;
|
||||
int tyr = py & 7;
|
||||
int tileRowOffset = ty << 5;
|
||||
|
||||
for (int x = 0; x < 160; x++)
|
||||
{
|
||||
int px = x;
|
||||
if(scroll) px += Registers.SCX;
|
||||
px &= 0xFF;
|
||||
int tx = px >> 3;
|
||||
int txr = px & 7;
|
||||
int tileOffset = tileRowOffset + tx;
|
||||
int tileAddr = tilemap + tileOffset;
|
||||
int tileNum = ReadMemory((ushort)tileAddr);
|
||||
tileNum = (tileNum + tileAdjust) & 0xFF;
|
||||
ushort patternAddr = (ushort)(tiledata + (tileNum << 4));
|
||||
patternAddr += (ushort)(tyr<<1);
|
||||
|
||||
int lobits = ReadMemory(patternAddr);
|
||||
patternAddr += 1;
|
||||
int hibits = ReadMemory(patternAddr);
|
||||
lobits >>= (7 - txr);
|
||||
hibits >>= (7 - txr);
|
||||
lobits &= 1;
|
||||
hibits &= 1;
|
||||
int pixel = lobits | (hibits << 1);
|
||||
output[x] = (byte)pixel;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public bool DeterministicEmulation { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
namespace BizHawk.Emulation.Consoles.Gameboy
|
||||
{
|
||||
public partial class Gameboy
|
||||
{
|
||||
public static readonly ControllerDefinition GbController = new ControllerDefinition
|
||||
{
|
||||
Name = "Gameboy Controller",
|
||||
BoolButtons =
|
||||
{
|
||||
"Up", "Down", "Left", "Right", "A", "B", "Select", "Start"
|
||||
}
|
||||
};
|
||||
|
||||
public ControllerDefinition ControllerDefinition { get { return GbController; } }
|
||||
public IController Controller { get; set; }
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
|
||||
namespace BizHawk.Emulation.Consoles.Gameboy
|
||||
{
|
||||
partial class Gameboy
|
||||
{
|
||||
public class MemoryMapper
|
||||
{
|
||||
private readonly Gameboy gb;
|
||||
public MemoryMapper(Gameboy gb)
|
||||
{
|
||||
this.gb = gb;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
======= TurboGrafx compatibility issues =======
|
||||
|
||||
General:
|
||||
+ Noise Channel isn't right. Less obnoxious now, still not happy.
|
||||
+ T Flag implemented for ADC, EOR. Log warning if used by any other instructions. Should probably just do it for the others, but I hate not having a test case.
|
||||
+ Video timing is off.
|
||||
+ Audio volumes are probably wrong.
|
||||
+ LFO is not implemented, though I can't tell.
|
||||
|
||||
Air Zonk - Freezes shortly into a new game
|
||||
Battle Ace - Some gfx glitches
|
||||
Battle Royale - Doesnt boot
|
||||
Bravoman - Doesn't boot
|
||||
Cadash - Camera is all crazy. Very sensitive to interrupt timing and timing in general.
|
||||
Chouzetsu Rinjin - Doesnt boot
|
||||
Cyber Dodge - Minor gfx issues
|
||||
Davis Cup Tennis - uh lets just say it doesnt work in creative ways
|
||||
Dead Moon - Minor gfx issues
|
||||
Dragon Curse - Terribly annoying sprite flicker - presumably timing related
|
||||
Dragon Egg - Doesn't boot
|
||||
Fighting Run - Corruption issues
|
||||
Final Solider - Graphics corruption issues
|
||||
Griffon - Dies in the new game sequence
|
||||
Gunboat - Crash / CPU Break (Needs BRAM)
|
||||
Hisou Kihei Serd - Doesn't boot
|
||||
Idol Hanafuda Fan Club - Doesn't boot
|
||||
Impossamole - Gfx issues
|
||||
Legendary Axe - Doesn't boot
|
||||
Legendary Axe II - Corruption, freezes in opening sequence
|
||||
Madoo Granzort - Graphics issues because VPC renderer is not using new frame timing
|
||||
Magical Chase - Freezes when starting 1st level - waiting for DMA completion IRQ?
|
||||
MML Demo - Echo channels are too loud (equal volume!)
|
||||
Naxat Open - Crashes when you start new game
|
||||
New Adventure Island - Minor gfx issues
|
||||
Night Creatures - Some gfx glitches
|
||||
Outrun - 'Sea Wind' sample when selecting music is not playing ...... raster issues, now more annoying than before
|
||||
Populous - Game freezes on starting new game - *** NEEDS BATTERY SAVERAM ***
|
||||
Power Drift - Timing glitch... starting new game runs slower than it should
|
||||
R-Type - Funky corruption after killing final boss in Stage 8
|
||||
Side Arms - Video timing issue - bottom arms display shouldn't be visible
|
||||
Sinistron - Raster effect errors
|
||||
Soldier Blade - Freezes about 5 minutes in. Always in the same spot, fortunately. I have a savestate.
|
||||
Strip Fighter - Minor gfx glitches (status area)
|
||||
Turrican - Playable, issues with opening sequence
|
||||
Valkyrie No Densetsu - Freezes in title sequence
|
||||
|
||||
|
||||
Stuff I Fixed That's Not In Other Docs:
|
||||
|
||||
+ Street Fighter II special mapper
|
||||
+ Populous has special BRAM / saveRAM (I have not yet fixed this) - Gunboat appears to be the same
|
||||
+ After Burner - There is a 1-instruction delay on changes to IRQ Control Byte taking effect
|
||||
+ There is a one-instruction delay on changes to the I flag taking effect
|
||||
- Affects (Not a complete list): Blodia, Body Conquest 2, Champions Forever Boxing,
|
||||
Cross Wiber, Jackie Chan, Jigoku Meguri, New Adventure Island, World Beach volley
|
||||
- Actually, I'm not at all convinced there's always a 1-instruction delay on the I-flag.
|
||||
But this fixes several games; clearly there are delays in some scenarios. But it's not clear
|
||||
to me that this delay is ALWAYS present.
|
|
@ -0,0 +1,44 @@
|
|||
namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||
{
|
||||
public partial class PCEngine
|
||||
{
|
||||
public static readonly ControllerDefinition PCEngineController =
|
||||
new ControllerDefinition
|
||||
{
|
||||
Name = "PC Engine Controller",
|
||||
BoolButtons = { "Up", "Down", "Left", "Right", "II", "I", "Select", "Run" }
|
||||
};
|
||||
|
||||
public ControllerDefinition ControllerDefinition { get { return PCEngineController; } }
|
||||
public IController Controller { get; set; }
|
||||
|
||||
private byte inputSelector;
|
||||
public bool SEL { get { return ((inputSelector & 1) != 0) ;} }
|
||||
|
||||
private void WriteInput(byte value)
|
||||
{
|
||||
inputSelector = value;
|
||||
}
|
||||
|
||||
private byte ReadInput()
|
||||
{
|
||||
byte value = 0xBF;
|
||||
if (SEL == false) // return buttons
|
||||
{
|
||||
if (Controller["I"]) value &= 0xFE;
|
||||
if (Controller["II"]) value &= 0xFD;
|
||||
if (Controller["Select"]) value &= 0xFB;
|
||||
if (Controller["Run"]) value &= 0xF7;
|
||||
} else { //return directions
|
||||
if (Controller["Up"]) value &= 0xFE;
|
||||
if (Controller["Right"]) value &= 0xFD;
|
||||
if (Controller["Down"]) value &= 0xFB;
|
||||
if (Controller["Left"]) value &= 0xF7;
|
||||
}
|
||||
|
||||
if (Region == "Japan") value |= 0x40;
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||
{
|
||||
public partial class PCEngine
|
||||
{
|
||||
// Street Fighter 2 was a 20-megabit HuCard. The PCE has a maximum 8-megabit addressable ROM space.
|
||||
// Therefore SF2 had a special mapper to make this work.
|
||||
|
||||
// TODO: need to update SF2 mapper to reflect updates made to the primary mapper
|
||||
// (ie, the IOBuffer)
|
||||
// However, I believe more fixes will be made in the future, and SF2 works, so this is not
|
||||
// currently a priority.
|
||||
|
||||
private byte SF2MapperLatch;
|
||||
|
||||
private byte ReadMemorySF2(ushort addr)
|
||||
{
|
||||
int page = Cpu.MPR[addr >> 13];
|
||||
ushort addr13 = (ushort)(addr & 0x1FFF);
|
||||
|
||||
if (page < 0x40)
|
||||
{
|
||||
// read rom.
|
||||
return RomData[(page % RomPages << 13) | (addr13)];
|
||||
}
|
||||
if (page < 0x80)
|
||||
{
|
||||
// read rom with extended SF2 mapper.
|
||||
return RomData[(((page << 13) | addr13) & 0x7FFFF) + ((SF2MapperLatch + 1)*0x80000)];
|
||||
}
|
||||
if (page >= 0xF8 && page <= 0xFB)
|
||||
{
|
||||
// read RAM.
|
||||
return Ram[((page-0xF8) << 13) | addr13];
|
||||
}
|
||||
|
||||
if (page == 0xFF)
|
||||
{
|
||||
// hardware page.
|
||||
if (addr13 < 0x400) return VDC1.ReadVDC(addr13 & 0x03);
|
||||
if (addr13 < 0x800) return VCE.ReadVCE((addr13 & 0x07));
|
||||
if ((addr13 & ~1) == 0x0C00) return Cpu.TimerValue;
|
||||
if (addr13 >= 0x1000 && addr13 < 0x1400) return ReadInput();
|
||||
if (addr13 == 0x1402) return Cpu.IRQControlByte;
|
||||
if (addr13 == 0x1403) return Cpu.ReadIrqStatus();
|
||||
}
|
||||
Log.Error("MEM", "UNHANDLED READ: [{0:X2}] {1:X4}", page, addr13);
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
private void WriteMemorySF2(ushort addr, byte value)
|
||||
{
|
||||
int page = Cpu.MPR[addr >> 13];
|
||||
ushort addr13 = (ushort)(addr & 0x1FFF);
|
||||
|
||||
if ((addr & 0x1FFC) == 0x1FF0)
|
||||
{
|
||||
// Set SF2 pager.
|
||||
SF2MapperLatch = (byte) (addr & 0x03);
|
||||
return;
|
||||
}
|
||||
|
||||
if (page >= 0xF8 && page <= 0xFB)
|
||||
{
|
||||
// write RAM.
|
||||
Ram[addr13] = value;
|
||||
}
|
||||
|
||||
if (page == 0xFF)
|
||||
{
|
||||
// hardware page.
|
||||
if (addr13 < 0x400)
|
||||
VDC1.WriteVDC(addr13 & 3, value);
|
||||
else if (addr13 < 0x800)
|
||||
VCE.WriteVCE(addr13 & 7, value);
|
||||
else if (addr13 < 0x80A)
|
||||
PSG.WritePSG(addr13, value, Cpu.TotalExecutedCycles);
|
||||
else if (addr13 == 0x0C00)
|
||||
Cpu.WriteTimer(value);
|
||||
else if (addr13 == 0x0C01)
|
||||
Cpu.WriteTimerEnable(value);
|
||||
else if (addr13 >= 0x1000 && addr13 < 0x1400)
|
||||
WriteInput(value);
|
||||
else if (addr13 == 0x1402)
|
||||
Cpu.WriteIrqControl(value);
|
||||
else if (addr13 == 0x1403)
|
||||
Cpu.WriteIrqStatus();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||
{
|
||||
public partial class PCEngine
|
||||
{
|
||||
// The SuperGrafx has 32K of RAM and a different port configuration to allow
|
||||
// I/O access to VDC1, VDC2, and the VPC.
|
||||
|
||||
private byte ReadMemorySGX(ushort addr)
|
||||
{
|
||||
int page = Cpu.MPR[addr >> 13];
|
||||
ushort addr13 = (ushort)(addr & 0x1FFF);
|
||||
|
||||
if (page < 0x80) // read ROM
|
||||
return RomData[(page % RomPages << 13) | (addr13)];
|
||||
|
||||
if (page >= 0xF8 && page <= 0xFB) // read RAM
|
||||
return Ram[((page-0xF8) << 13) | addr13];
|
||||
|
||||
if (page == 0xFF) // hardware page.
|
||||
{
|
||||
if (addr13 < 0x400)
|
||||
{
|
||||
addr13 &= 0x1F;
|
||||
if (addr13 <= 0x07) return VDC1.ReadVDC(addr13 & 3);
|
||||
if (addr13 <= 0x0F) return VPC.ReadVPC(addr13);
|
||||
if (addr13 <= 0x17) return VDC2.ReadVDC(addr13 & 3);
|
||||
return 0xFF;
|
||||
}
|
||||
if (addr13 < 0x0800) { Cpu.PendingCycles--; return VCE.ReadVCE((addr13 & 0x07)); }
|
||||
if (addr13 < 0x080F) return IOBuffer;
|
||||
if ((addr13 & ~1) == 0x0C00) { IOBuffer = (byte) (Cpu.TimerValue | (IOBuffer & 0x80)); return IOBuffer; }
|
||||
if (addr13 >= 0x1000 &&
|
||||
addr13 < 0x1400) { IOBuffer = ReadInput(); return IOBuffer; }
|
||||
if ((addr13 & ~1) == 0x1400) return IOBuffer;
|
||||
if (addr13 == 0x1402) { IOBuffer = (byte) (Cpu.IRQControlByte | (IOBuffer & 0xF8)); return IOBuffer; }
|
||||
if (addr13 == 0x1403) { IOBuffer = (byte) (Cpu.ReadIrqStatus() | (IOBuffer & 0xF8)); return IOBuffer; }
|
||||
}
|
||||
Log.Error("MEM", "UNHANDLED READ: [{0:X2}] {1:X4}", page, addr13);
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
private void WriteMemorySGX(ushort addr, byte value)
|
||||
{
|
||||
int page = Cpu.MPR[addr >> 13];
|
||||
ushort addr13 = (ushort)(addr & 0x1FFF);
|
||||
|
||||
if (page >= 0xF8 && page <= 0xFB) // write RAM.
|
||||
Ram[((page-0xF8) << 13) | addr13] = value;
|
||||
|
||||
else if (page == 0xFF) // hardware page.
|
||||
{
|
||||
if (addr13 < 0x0400)
|
||||
{
|
||||
addr13 &= 0x1F;
|
||||
if (addr13 <= 0x07) VDC1.WriteVDC(addr13 & 3, value);
|
||||
else if (addr13 <= 0x0F) VPC.WriteVPC(addr13, value);
|
||||
else if (addr13 <= 0x17) VDC2.WriteVDC(addr13 & 3, value);
|
||||
}
|
||||
else if (addr13 < 0x0800) { Cpu.PendingCycles--; VCE.WriteVCE(addr13 & 7, value); }
|
||||
else if (addr13 < 0x080A) { IOBuffer = value; PSG.WritePSG(addr13, value, Cpu.TotalExecutedCycles); }
|
||||
else if (addr13 == 0x0C00) { IOBuffer = value; Cpu.WriteTimer(value); }
|
||||
else if (addr13 == 0x0C01) { IOBuffer = value; Cpu.WriteTimerEnable(value); }
|
||||
else if (addr13 >= 0x1000 &&
|
||||
addr13 < 0x1400) { IOBuffer = value; WriteInput(value); }
|
||||
else if (addr13 == 0x1402) { IOBuffer = value; Cpu.WriteIrqControl(value); }
|
||||
else if (addr13 == 0x1403) { IOBuffer = value; Cpu.WriteIrqStatus(); }
|
||||
else Log.Error("MEM", "unhandled hardware write [{0:X4}] : {1:X2}", addr13, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||
{
|
||||
public partial class PCEngine
|
||||
{
|
||||
// Much to my surprise, this silly I/O Buffer mechanic described in Charles MacDonald's doc is actually used by games.
|
||||
// As one example, Cyber Core requires emulation of the IOBuffer to boot.
|
||||
private byte IOBuffer;
|
||||
|
||||
private byte ReadMemory(ushort addr)
|
||||
{
|
||||
int page = Cpu.MPR[addr >> 13];
|
||||
ushort addr13 = (ushort)(addr & 0x1FFF);
|
||||
|
||||
if (page < 0x80) // read ROM
|
||||
return RomData[(page % RomPages << 13) | (addr13)];
|
||||
|
||||
if (page >= 0xF8 && page <= 0xFB) // read RAM
|
||||
return Ram[addr13];
|
||||
|
||||
if (page == 0xFF) // hardware page.
|
||||
{
|
||||
if (addr13 < 0x0400) return VDC1.ReadVDC(addr13 & 0x03);
|
||||
if (addr13 < 0x0800) { Cpu.PendingCycles--; return VCE.ReadVCE((addr13 & 0x07)); }
|
||||
if (addr13 < 0x080F) return IOBuffer;
|
||||
if ((addr13 & ~1) == 0x0C00) { IOBuffer = (byte) (Cpu.TimerValue | (IOBuffer & 0x80)); return IOBuffer; }
|
||||
if (addr13 >= 0x1000 &&
|
||||
addr13 < 0x1400) { IOBuffer = ReadInput(); return IOBuffer; }
|
||||
if ((addr13 & ~1) == 0x1400) return IOBuffer;
|
||||
if (addr13 == 0x1402) { IOBuffer = (byte) (Cpu.IRQControlByte | (IOBuffer & 0xF8)); return IOBuffer; }
|
||||
if (addr13 == 0x1403) { IOBuffer = (byte) (Cpu.ReadIrqStatus() | (IOBuffer & 0xF8)); return IOBuffer; }
|
||||
}
|
||||
|
||||
Log.Error("MEM", "UNHANDLED READ: [{0:X2}] {1:X4}", page, addr13);
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
private void WriteMemory(ushort addr, byte value)
|
||||
{
|
||||
int page = Cpu.MPR[addr >> 13];
|
||||
ushort addr13 = (ushort)(addr & 0x1FFF);
|
||||
|
||||
if (page >= 0xF8 && page <= 0xFB) // write RAM.
|
||||
{
|
||||
if (Cpu.debug)
|
||||
Log.Note("MEM", "*Mem* Changed {0:X4} from {1:X2} to {2:X2}", addr13, Ram[addr13], value);
|
||||
Ram[addr13] = value;
|
||||
}
|
||||
|
||||
if (page == 0xFF) // hardware page.
|
||||
{
|
||||
if (addr13 < 0x0400) VDC1.WriteVDC(addr13 & 3, value);
|
||||
else if (addr13 < 0x0800) { Cpu.PendingCycles--; VCE.WriteVCE(addr13 & 7, value); }
|
||||
else if (addr13 < 0x080A) { IOBuffer = value; PSG.WritePSG(addr13, value, Cpu.TotalExecutedCycles); }
|
||||
else if (addr13 == 0x0C00) { IOBuffer = value; Cpu.WriteTimer(value); }
|
||||
else if (addr13 == 0x0C01) { IOBuffer = value; Cpu.WriteTimerEnable(value); }
|
||||
else if (addr13 >= 0x1000 &&
|
||||
addr13 < 0x1400) { IOBuffer = value; WriteInput(value); }
|
||||
else if (addr13 == 0x1402) { IOBuffer = value; Cpu.WriteIrqControl(value); }
|
||||
else if (addr13 == 0x1403) { IOBuffer = value; Cpu.WriteIrqStatus(); }
|
||||
else Log.Error("MEM", "unhandled hardware write [{0:X4}] : {1:X2}", addr13, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,279 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using BizHawk.Emulation.CPUs.H6280;
|
||||
using BizHawk.Emulation.Sound;
|
||||
|
||||
namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||
{
|
||||
public enum NecSystemType
|
||||
{
|
||||
TurboGrafx,
|
||||
TurboCD,
|
||||
SuperGrafx
|
||||
}
|
||||
|
||||
public sealed partial class PCEngine : IEmulator
|
||||
{
|
||||
// ROM
|
||||
public byte[] RomData;
|
||||
public int RomPages;
|
||||
|
||||
// Machine
|
||||
public NecSystemType Type;
|
||||
public HuC6280 Cpu;
|
||||
public VDC VDC1, VDC2;
|
||||
public VCE VCE;
|
||||
public HuC6280PSG PSG;
|
||||
public VPC VPC;
|
||||
|
||||
private bool TurboGrafx { get { return Type == NecSystemType.TurboGrafx; } }
|
||||
private bool SuperGrafx { get { return Type == NecSystemType.SuperGrafx; } }
|
||||
private bool TurboCD { get { return Type == NecSystemType.TurboCD; } }
|
||||
|
||||
// Memory system
|
||||
public byte[] Ram;
|
||||
|
||||
// PC Engine timings:
|
||||
// 21,477,270 Machine clocks / sec
|
||||
// 7,159,090 Cpu cycles / sec
|
||||
|
||||
// At 60.00 FPS
|
||||
// 357,954 mclks / frame
|
||||
// 119,318 Cpu cycles / frame
|
||||
|
||||
// 263 lines / frame:
|
||||
// 1361 mclks / line
|
||||
// 454 Cpu cycles / line
|
||||
|
||||
public PCEngine(NecSystemType type)
|
||||
{
|
||||
Type = type;
|
||||
Controller = NullController.GetNullController();
|
||||
Init();
|
||||
}
|
||||
|
||||
private void Init()
|
||||
{
|
||||
Cpu = new HuC6280();
|
||||
VCE = new VCE();
|
||||
VDC1 = new VDC(Cpu, VCE);
|
||||
PSG = new HuC6280PSG();
|
||||
|
||||
if (TurboGrafx || TurboCD)
|
||||
{
|
||||
Ram = new byte[0x2000];
|
||||
Cpu.ReadMemory = ReadMemory;
|
||||
Cpu.WriteMemory = WriteMemory;
|
||||
Cpu.WriteVDC = VDC1.WriteVDC;
|
||||
}
|
||||
|
||||
if (SuperGrafx)
|
||||
{
|
||||
VDC2 = new VDC(Cpu, VCE);
|
||||
VPC = new VPC(VDC1, VDC2, VCE, Cpu);
|
||||
Ram = new byte[0x8000];
|
||||
Cpu.ReadMemory = ReadMemorySGX;
|
||||
Cpu.WriteMemory = WriteMemorySGX;
|
||||
Cpu.WriteVDC = VDC1.WriteVDC;
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadGame(IGame game)
|
||||
{
|
||||
if (game.GetRomData().Length == 0x60000)
|
||||
{
|
||||
// 384k roms require special loading code. Why ;_;
|
||||
// In memory, 384k roms look like [1st 256k][Then full 384k]
|
||||
RomData = new byte[0xA0000];
|
||||
var origRom = game.GetRomData();
|
||||
for (int i=0; i<0x40000; i++)
|
||||
RomData[i] = origRom[i];
|
||||
for (int i = 0; i < 0x60000; i++)
|
||||
RomData[i+0x40000] = origRom[i];
|
||||
RomPages = RomData.Length/8192;
|
||||
} else if (game.GetRomData().Length > 1024 * 1024) {
|
||||
// If the rom is bigger than 1 megabyte, switch to Street Fighter 2 mapper
|
||||
Cpu.ReadMemory = ReadMemorySF2;
|
||||
Cpu.WriteMemory = WriteMemorySF2;
|
||||
RomData = game.GetRomData();
|
||||
RomPages = RomData.Length / 8192;
|
||||
} else {
|
||||
// normal rom.
|
||||
RomData = game.GetRomData();
|
||||
RomPages = RomData.Length / 8192;
|
||||
}
|
||||
Cpu.ResetPC();
|
||||
}
|
||||
|
||||
public int Frame { get; set; }
|
||||
|
||||
public void FrameAdvance(bool render)
|
||||
{
|
||||
Controller.FrameNumber = Frame++;
|
||||
//Log.Note("CPU","======== FRAME {0} =========",Frame);
|
||||
PSG.BeginFrame(Cpu.TotalExecutedCycles);
|
||||
|
||||
if (SuperGrafx)
|
||||
VPC.ExecFrame(); // TODO supergrafx frameskipping (waiting on a larger update of VPC frame timing, once I get VDC timing correct)
|
||||
else
|
||||
VDC1.ExecFrame(render);
|
||||
|
||||
PSG.EndFrame(Cpu.TotalExecutedCycles);
|
||||
}
|
||||
|
||||
public void HardReset()
|
||||
{
|
||||
Init();
|
||||
Cpu.ResetPC();
|
||||
}
|
||||
|
||||
public IVideoProvider VideoProvider
|
||||
{
|
||||
get { return (IVideoProvider) VPC ?? VDC1; }
|
||||
}
|
||||
|
||||
public ISoundProvider SoundProvider
|
||||
{
|
||||
get { return PSG; }
|
||||
}
|
||||
|
||||
public string Region { get; set; }
|
||||
public bool DeterministicEmulation { get; set; }
|
||||
|
||||
public byte[] SaveRam
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public bool SaveRamModified
|
||||
{
|
||||
get { return false; }
|
||||
set { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public void SaveStateText(TextWriter writer)
|
||||
{
|
||||
writer.WriteLine("[PCEngine]");
|
||||
writer.Write("RAM ");
|
||||
Ram.SaveAsHex(writer);
|
||||
writer.WriteLine("Frame " + Frame);
|
||||
if (Cpu.ReadMemory == ReadMemorySF2)
|
||||
writer.WriteLine("SF2MapperLatch " + SF2MapperLatch);
|
||||
writer.WriteLine("IOBuffer {0:X2}", IOBuffer);
|
||||
writer.WriteLine();
|
||||
|
||||
if (SuperGrafx)
|
||||
{
|
||||
Cpu.SaveStateText(writer);
|
||||
VPC.SaveStateText(writer);
|
||||
VCE.SaveStateText(writer);
|
||||
VDC1.SaveStateText(writer, 1);
|
||||
VDC2.SaveStateText(writer, 2);
|
||||
PSG.SaveStateText(writer);
|
||||
}
|
||||
else
|
||||
{
|
||||
Cpu.SaveStateText(writer);
|
||||
VCE.SaveStateText(writer);
|
||||
VDC1.SaveStateText(writer, 1);
|
||||
PSG.SaveStateText(writer);
|
||||
}
|
||||
writer.WriteLine("[/PCEngine]");
|
||||
}
|
||||
|
||||
public void LoadStateText(TextReader reader)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
string[] args = reader.ReadLine().Split(' ');
|
||||
if (args[0].Trim() == "") continue;
|
||||
if (args[0] == "[PCEngine]") continue;
|
||||
if (args[0] == "[/PCEngine]") break;
|
||||
if (args[0] == "Frame")
|
||||
Frame = int.Parse(args[1]);
|
||||
else if (args[0] == "SF2MapperLatch")
|
||||
SF2MapperLatch = byte.Parse(args[1]);
|
||||
else if (args[0] == "IOBuffer")
|
||||
IOBuffer = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "RAM")
|
||||
Ram.ReadFromHex(args[1]);
|
||||
else if (args[0] == "[HuC6280]")
|
||||
Cpu.LoadStateText(reader);
|
||||
else if (args[0] == "[PSG]")
|
||||
PSG.LoadStateText(reader);
|
||||
else if (args[0] == "[VCE]")
|
||||
VCE.LoadStateText(reader);
|
||||
else if (args[0] == "[VPC]")
|
||||
VPC.LoadStateText(reader);
|
||||
else if (args[0] == "[VDC1]")
|
||||
VDC1.LoadStateText(reader,1);
|
||||
else if (args[0] == "[VDC2]")
|
||||
VDC2.LoadStateText(reader,2);
|
||||
else
|
||||
Console.WriteLine("Skipping unrecognized identifier " + args[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveStateBinary(BinaryWriter writer)
|
||||
{
|
||||
if (SuperGrafx == false)
|
||||
{
|
||||
writer.Write(Ram);
|
||||
writer.Write(Frame);
|
||||
writer.Write(SF2MapperLatch);
|
||||
writer.Write(IOBuffer);
|
||||
Cpu.SaveStateBinary(writer);
|
||||
VCE.SaveStateBinary(writer);
|
||||
VDC1.SaveStateBinary(writer);
|
||||
PSG.SaveStateBinary(writer);
|
||||
} else {
|
||||
writer.Write(Ram);
|
||||
writer.Write(Frame);
|
||||
writer.Write(IOBuffer);
|
||||
Cpu.SaveStateBinary(writer);
|
||||
VCE.SaveStateBinary(writer);
|
||||
VPC.SaveStateBinary(writer);
|
||||
VDC1.SaveStateBinary(writer);
|
||||
VDC2.SaveStateBinary(writer);
|
||||
PSG.SaveStateBinary(writer);
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader reader)
|
||||
{
|
||||
if (SuperGrafx == false)
|
||||
{
|
||||
Ram = reader.ReadBytes(0x2000);
|
||||
Frame = reader.ReadInt32();
|
||||
SF2MapperLatch = reader.ReadByte();
|
||||
IOBuffer = reader.ReadByte();
|
||||
Cpu.LoadStateBinary(reader);
|
||||
VCE.LoadStateBinary(reader);
|
||||
VDC1.LoadStateBinary(reader);
|
||||
PSG.LoadStateBinary(reader);
|
||||
} else {
|
||||
Ram = reader.ReadBytes(0x8000);
|
||||
Frame = reader.ReadInt32();
|
||||
IOBuffer = reader.ReadByte();
|
||||
Cpu.LoadStateBinary(reader);
|
||||
VCE.LoadStateBinary(reader);
|
||||
VPC.LoadStateBinary(reader);
|
||||
VDC1.LoadStateBinary(reader);
|
||||
VDC2.LoadStateBinary(reader);
|
||||
PSG.LoadStateBinary(reader);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] SaveStateBinary()
|
||||
{
|
||||
var buf = new byte[SuperGrafx ? 166550 : 75852];
|
||||
var stream = new MemoryStream(buf);
|
||||
var writer = new BinaryWriter(stream);
|
||||
SaveStateBinary(writer);
|
||||
//Console.WriteLine("LENGTH " + stream.Position);
|
||||
writer.Close();
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
|
||||
namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||
{
|
||||
// HuC6260 Video Color Encoder
|
||||
public sealed class VCE
|
||||
{
|
||||
public ushort VceAddress;
|
||||
public ushort[] VceData = new ushort[512];
|
||||
public int[] Palette = new int[512];
|
||||
|
||||
public void WriteVCE(int port, byte value)
|
||||
{
|
||||
//cpu.PendingCycles--; // VCE access puts CPU into a 1-cycle wait state.
|
||||
switch (port)
|
||||
{
|
||||
case 0: // Control Port. Doesn't control anything we care about...
|
||||
break;
|
||||
case 2: // Address LSB
|
||||
VceAddress &= 0xFF00;
|
||||
VceAddress |= value;
|
||||
break;
|
||||
case 3: // Address MSB
|
||||
VceAddress &= 0x00FF;
|
||||
VceAddress |= (ushort) (value << 8);
|
||||
VceAddress &= 0x01FF;
|
||||
break;
|
||||
case 4: // Data LSB
|
||||
VceData[VceAddress] &= 0xFF00;
|
||||
VceData[VceAddress] |= value;
|
||||
PrecomputePalette(VceAddress);
|
||||
break;
|
||||
case 5: // Data MSB
|
||||
VceData[VceAddress] &= 0x00FF;
|
||||
VceData[VceAddress] |= (ushort) (value << 8);
|
||||
PrecomputePalette(VceAddress);
|
||||
VceAddress++;
|
||||
VceAddress &= 0x1FF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public byte ReadVCE(int port)
|
||||
{
|
||||
//cpu.PendingCycles--;
|
||||
switch (port)
|
||||
{
|
||||
case 4: // Data LSB
|
||||
return (byte) (VceData[VceAddress] & 0xFF);
|
||||
case 5: // Data MSB
|
||||
byte value = (byte) ((VceData[VceAddress] >> 8) | 0xFE);
|
||||
VceAddress++;
|
||||
VceAddress &= 0x1FF;
|
||||
return value;
|
||||
default: return 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly byte[] PalConvert = {0, 36, 72, 109, 145, 182, 218, 255};
|
||||
|
||||
public void PrecomputePalette(int slot)
|
||||
{
|
||||
byte r = PalConvert[(VceData[slot] >> 3) & 7];
|
||||
byte g = PalConvert[(VceData[slot] >> 6) & 7];
|
||||
byte b = PalConvert[VceData[slot] & 7];
|
||||
Palette[slot] = Colors.ARGB(r, g, b);
|
||||
}
|
||||
|
||||
public void SaveStateText(TextWriter writer)
|
||||
{
|
||||
writer.WriteLine("[VCE]");
|
||||
writer.WriteLine("VceAddress {0:X4}", VceAddress);
|
||||
writer.Write("VceData ");
|
||||
VceData.SaveAsHex(writer);
|
||||
writer.WriteLine("[/VCE]\n");
|
||||
}
|
||||
|
||||
public void LoadStateText(TextReader reader)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
string[] args = reader.ReadLine().Split(' ');
|
||||
if (args[0].Trim() == "") continue;
|
||||
if (args[0] == "[/VCE]") break;
|
||||
if (args[0] == "VceAddress")
|
||||
VceAddress = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "VceData")
|
||||
VceData.ReadFromHex(args[1]);
|
||||
else
|
||||
Console.WriteLine("Skipping unrecognized identifier " + args[0]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < VceData.Length; i++)
|
||||
PrecomputePalette(i);
|
||||
}
|
||||
|
||||
public void SaveStateBinary(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(VceAddress);
|
||||
for (int i = 0; i < VceData.Length; i++)
|
||||
writer.Write(VceData[i]);
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader reader)
|
||||
{
|
||||
VceAddress = reader.ReadUInt16();
|
||||
for (int i = 0; i < VceData.Length; i++)
|
||||
{
|
||||
VceData[i] = reader.ReadUInt16();
|
||||
PrecomputePalette(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,337 @@
|
|||
namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||
{
|
||||
// This rendering code is only used for TurboGrafx/TurboCD Mode.
|
||||
// In SuperGrafx mode, separate rendering functions in the VPC class are used.
|
||||
|
||||
public partial class VDC
|
||||
{
|
||||
/* There are many line-counters here. Here is a breakdown of what they each are:
|
||||
|
||||
+ ScanLine is the current NTSC scanline. It has a range from 0 to 262.
|
||||
+ ActiveLine is the current offset into the framebuffer. 0 is the first
|
||||
line of active display, and the last value will be BufferHeight-1.
|
||||
+ BackgroundY is the current offset into the scroll plane. It is set with BYR
|
||||
register at certain sync points and incremented every scanline.
|
||||
Its values range from 0 - $1FF.
|
||||
+ RCRCount is the current offset into the RCR register. It is reset to $40
|
||||
on the first active display line of, and incremented every line. Its effective
|
||||
range is $40 - $146.
|
||||
|
||||
*/
|
||||
public int ScanLine;
|
||||
public int BackgroundY;
|
||||
|
||||
private byte[] PriorityBuffer = new byte[512];
|
||||
private byte[] InterSpritePriorityBuffer = new byte[512];
|
||||
private int latchedDisplayStartLine;
|
||||
private int ActiveLine;
|
||||
private int RCRCount;
|
||||
|
||||
// One possible cause of graphics corruption is that I am executing DMAs immediately.
|
||||
// DMAs are only executed when the system is OUTSIDE of the the active display period.
|
||||
// So... that could be a thing.
|
||||
// Although its actually not probably very much of a thing.
|
||||
|
||||
public void ExecFrame(bool render)
|
||||
{
|
||||
latchedDisplayStartLine = DisplayStartLine;
|
||||
ActiveLine = 0 - latchedDisplayStartLine;
|
||||
ScanLine = 0;
|
||||
|
||||
for (; ScanLine < 262;)
|
||||
{
|
||||
//Log.Note("VDC","ScanLine {0} (ActiveLine {1}, RCR {2}, BGY {3})",ScanLine, ActiveLine, RCRCount, BackgroundY);
|
||||
bool InActiveDisplay = false;
|
||||
if (ScanLine >= latchedDisplayStartLine && ScanLine < latchedDisplayStartLine + FrameHeight)
|
||||
InActiveDisplay = true;
|
||||
|
||||
if (ActiveLine == 0)
|
||||
{
|
||||
BackgroundY = Registers[BYR];
|
||||
RCRCount = 0x40;
|
||||
//Console.WriteLine("Latched BYR {0} at scanline {1}",BackgroundY, ScanLine);
|
||||
}
|
||||
|
||||
if ((RCRCount) == (Registers[RCR] & 0x3FF))
|
||||
{
|
||||
if (IntRasterCompare)
|
||||
{
|
||||
//Log.Note("VDC", "Enter RCR interrupt at scanline {0}",ScanLine);
|
||||
StatusByte |= StatusRasterCompare;
|
||||
cpu.IRQ1Assert = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ActiveLine == BufferHeight && IntVerticalBlank)
|
||||
{
|
||||
StatusByte |= StatusVerticalBlanking;
|
||||
cpu.IRQ1Assert = true;
|
||||
}
|
||||
cpu.Execute(82);
|
||||
|
||||
if (InActiveDisplay && render)
|
||||
RenderScanLine();
|
||||
|
||||
cpu.Execute(373);
|
||||
|
||||
ScanLine++;
|
||||
ActiveLine++;
|
||||
RCRCount++;
|
||||
BackgroundY++;
|
||||
BackgroundY &= 0x01FF;
|
||||
}
|
||||
}
|
||||
|
||||
public void RenderScanLine()
|
||||
{
|
||||
RenderBackgroundScanline();
|
||||
RenderSpritesScanline();
|
||||
|
||||
if (ScanLine == FrameHeight - 1)
|
||||
UpdateSpriteAttributeTable();
|
||||
}
|
||||
|
||||
public void UpdateSpriteAttributeTable()
|
||||
{
|
||||
if (/*(Registers[DCR] & 0x10) != 0 &&*/ Registers[SATB] <= 0x7F00)
|
||||
{
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
SpriteAttributeTable[i] = VRAM[Registers[SATB] + i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderBackgroundScanline()
|
||||
{
|
||||
// clear priority buffer
|
||||
for (int i = 0; i < FrameWidth; i++)
|
||||
PriorityBuffer[i] = 0;
|
||||
|
||||
if (BackgroundEnabled == false)
|
||||
{
|
||||
for (int i = 0; i < FrameWidth; i++)
|
||||
FrameBuffer[(ActiveLine * FrameWidth) + i] = vce.Palette[0];
|
||||
return;
|
||||
}
|
||||
|
||||
int batHeight = BatHeight * 8;
|
||||
int batWidth = BatWidth * 8;
|
||||
|
||||
int vertLine = BackgroundY;
|
||||
vertLine %= batHeight;
|
||||
int yTile = (vertLine / 8);
|
||||
int yOfs = vertLine % 8;
|
||||
|
||||
// TODO: x-scrolling is done super quick and shitty here and slow.
|
||||
// Ergo, make it better later.
|
||||
|
||||
int xScroll = Registers[BXR] & 0x3FF;
|
||||
for (int x = 0; x < FrameWidth; x++)
|
||||
{
|
||||
int xTile = ((x + xScroll) / 8) % BatWidth;
|
||||
int xOfs = (x + xScroll) & 7;
|
||||
int tileNo = VRAM[(ushort)(((yTile * BatWidth) + xTile))] & 2047;
|
||||
int paletteNo = VRAM[(ushort)(((yTile * BatWidth) + xTile))] >> 12;
|
||||
int paletteBase = paletteNo * 16;
|
||||
|
||||
byte c = PatternBuffer[(tileNo * 64) + (yOfs * 8) + xOfs];
|
||||
if (c == 0)
|
||||
FrameBuffer[(ActiveLine * FrameWidth) + x] = vce.Palette[0];
|
||||
else
|
||||
{
|
||||
FrameBuffer[(ActiveLine * FrameWidth) + x] = vce.Palette[paletteBase + c];
|
||||
PriorityBuffer[x] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] heightTable = { 16, 32, 64, 64 };
|
||||
|
||||
public void RenderSpritesScanline()
|
||||
{
|
||||
if (SpritesEnabled == false)
|
||||
return;
|
||||
|
||||
// clear inter-sprite priority buffer
|
||||
for (int i = 0; i < FrameWidth; i++)
|
||||
InterSpritePriorityBuffer[i] = 0;
|
||||
|
||||
for (int i = 0; i < 64; i++)
|
||||
{
|
||||
int y = (SpriteAttributeTable[(i * 4) + 0] & 1023) - 64;
|
||||
int x = (SpriteAttributeTable[(i * 4) + 1] & 1023) - 32;
|
||||
ushort flags = SpriteAttributeTable[(i * 4) + 3];
|
||||
int height = heightTable[(flags >> 12) & 3];
|
||||
|
||||
if (y + height <= ActiveLine || y > ActiveLine)
|
||||
continue;
|
||||
|
||||
int patternNo = (((SpriteAttributeTable[(i * 4) + 2]) >> 1) & 0x1FF);
|
||||
int paletteBase = 256 + ((flags & 15) * 16);
|
||||
int width = (flags & 0x100) == 0 ? 16 : 32;
|
||||
bool priority = (flags & 0x80) != 0;
|
||||
bool hflip = (flags & 0x0800) != 0;
|
||||
bool vflip = (flags & 0x8000) != 0;
|
||||
|
||||
if (width == 32)
|
||||
patternNo &= 0x1FE;
|
||||
|
||||
int yofs = 0;
|
||||
if (vflip == false)
|
||||
{
|
||||
yofs = (ActiveLine - y) & 15;
|
||||
if (height == 32)
|
||||
{
|
||||
patternNo &= 0x1FD;
|
||||
if (ActiveLine - y >= 16)
|
||||
{
|
||||
y += 16;
|
||||
patternNo += 2;
|
||||
}
|
||||
}
|
||||
else if (height == 64)
|
||||
{
|
||||
patternNo &= 0x1F9;
|
||||
if (ActiveLine - y >= 48)
|
||||
{
|
||||
y += 48;
|
||||
patternNo += 6;
|
||||
}
|
||||
else if (ActiveLine - y >= 32)
|
||||
{
|
||||
y += 32;
|
||||
patternNo += 4;
|
||||
}
|
||||
else if (ActiveLine - y >= 16)
|
||||
{
|
||||
y += 16;
|
||||
patternNo += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
else // vflip == true
|
||||
{
|
||||
yofs = 15 - ((ActiveLine - y) & 15);
|
||||
if (height == 32)
|
||||
{
|
||||
patternNo &= 0x1FD;
|
||||
if (ActiveLine - y < 16)
|
||||
{
|
||||
y += 16;
|
||||
patternNo += 2;
|
||||
}
|
||||
}
|
||||
else if (height == 64)
|
||||
{
|
||||
patternNo &= 0x1F9;
|
||||
if (ActiveLine - y < 16)
|
||||
{
|
||||
y += 48;
|
||||
patternNo += 6;
|
||||
}
|
||||
else if (ActiveLine - y < 32)
|
||||
{
|
||||
y += 32;
|
||||
patternNo += 4;
|
||||
}
|
||||
else if (ActiveLine - y < 48)
|
||||
{
|
||||
y += 16;
|
||||
patternNo += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hflip == false)
|
||||
{
|
||||
if (x + width > 0 && y + height > 0)
|
||||
{
|
||||
for (int xs = x >= 0 ? x : 0; xs < x + 16 && xs >= 0 && xs < FrameWidth; xs++)
|
||||
{
|
||||
byte pixel = SpriteBuffer[(patternNo * 256) + (yofs * 16) + (xs - x)];
|
||||
if (pixel != 0 && InterSpritePriorityBuffer[xs] == 0)
|
||||
{
|
||||
InterSpritePriorityBuffer[xs] = 1;
|
||||
if (priority || PriorityBuffer[xs] == 0)
|
||||
FrameBuffer[(ActiveLine * FrameWidth) + xs] = vce.Palette[paletteBase + pixel];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (width == 32)
|
||||
{
|
||||
patternNo++;
|
||||
x += 16;
|
||||
for (int xs = x >= 0 ? x : 0; xs < x + 16 && xs >= 0 && xs < FrameWidth; xs++)
|
||||
{
|
||||
byte pixel = SpriteBuffer[(patternNo * 256) + (yofs * 16) + (xs - x)];
|
||||
if (pixel != 0 && InterSpritePriorityBuffer[xs] == 0)
|
||||
{
|
||||
InterSpritePriorityBuffer[xs] = 1;
|
||||
if (priority || PriorityBuffer[xs] == 0)
|
||||
FrameBuffer[(ActiveLine * FrameWidth) + xs] = vce.Palette[paletteBase + pixel];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // hflip = true
|
||||
if (x + width > 0 && y + height > 0)
|
||||
{
|
||||
if (width == 32)
|
||||
patternNo++;
|
||||
for (int xs = x >= 0 ? x : 0; xs < x + 16 && xs >= 0 && xs < FrameWidth; xs++)
|
||||
{
|
||||
byte pixel = SpriteBuffer[(patternNo * 256) + (yofs * 16) + 15 - (xs - x)];
|
||||
if (pixel != 0 && InterSpritePriorityBuffer[xs] == 0)
|
||||
{
|
||||
InterSpritePriorityBuffer[xs] = 1;
|
||||
if (priority || PriorityBuffer[xs] == 0)
|
||||
FrameBuffer[(ActiveLine * FrameWidth) + xs] = vce.Palette[paletteBase + pixel];
|
||||
}
|
||||
}
|
||||
if (width == 32)
|
||||
{
|
||||
patternNo--;
|
||||
x += 16;
|
||||
for (int xs = x >= 0 ? x : 0; xs < x + 16 && xs >= 0 && xs < FrameWidth; xs++)
|
||||
{
|
||||
byte pixel = SpriteBuffer[(patternNo * 256) + (yofs * 16) + 15 - (xs - x)];
|
||||
if (pixel != 0 && InterSpritePriorityBuffer[xs] == 0)
|
||||
{
|
||||
InterSpritePriorityBuffer[xs] = 1;
|
||||
if (priority || PriorityBuffer[xs] == 0)
|
||||
FrameBuffer[(ActiveLine * FrameWidth) + xs] = vce.Palette[paletteBase + pixel];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int FrameWidth = 256;
|
||||
private int FrameHeight = 240;
|
||||
private int[] FrameBuffer = new int[256 * 240];
|
||||
|
||||
public int[] GetVideoBuffer()
|
||||
{
|
||||
return FrameBuffer;
|
||||
}
|
||||
|
||||
public int BufferWidth
|
||||
{
|
||||
get { return FrameWidth; }
|
||||
}
|
||||
|
||||
public int BufferHeight
|
||||
{
|
||||
get { return FrameHeight; }
|
||||
}
|
||||
|
||||
public int BackgroundColor
|
||||
{
|
||||
get { return vce.Palette[256]; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,331 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using BizHawk.Emulation.CPUs.H6280;
|
||||
|
||||
namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||
{
|
||||
// HuC6270 Video Display Controller
|
||||
|
||||
public sealed partial class VDC : IVideoProvider
|
||||
{
|
||||
public ushort[] VRAM = new ushort[0x8000];
|
||||
public ushort[] SpriteAttributeTable = new ushort[256];
|
||||
public byte[] PatternBuffer = new byte[0x20000];
|
||||
public byte[] SpriteBuffer = new byte[0x20000];
|
||||
public byte RegisterLatch;
|
||||
public ushort[] Registers = new ushort[0x20];
|
||||
public ushort ReadBuffer;
|
||||
public byte StatusByte;
|
||||
|
||||
public ushort IncrementWidth
|
||||
{
|
||||
get
|
||||
{
|
||||
switch ((Registers[5] >> 11) & 3)
|
||||
{
|
||||
case 0: return 1;
|
||||
case 1: return 32;
|
||||
case 2: return 64;
|
||||
case 3: return 128;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
public bool BackgroundEnabled { get { return (Registers[CR] & 0x80) != 0; } }
|
||||
public bool SpritesEnabled { get { return (Registers[CR] & 0x40) != 0; } }
|
||||
public bool IntVerticalBlank { get { return (Registers[CR] & 0x08) != 0; } }
|
||||
public bool IntRasterCompare { get { return (Registers[CR] & 0x04) != 0; } }
|
||||
public bool IntSpriteOverflow { get { return (Registers[CR] & 0x02) != 0; } }
|
||||
public bool IntSpriteCollision { get { return (Registers[CR] & 0x01) != 0; } }
|
||||
|
||||
public int BatWidth { get { switch((Registers[MWR] >> 4) & 3) { case 0: return 32; case 1: return 64; default: return 128; } } }
|
||||
public int BatHeight { get { return ((Registers[MWR] & 0x40) == 0) ? 32 : 64; } }
|
||||
|
||||
public int RequestedFrameWidth { get { return ((Registers[HDR] & 0x3F) + 1) * 8; } }
|
||||
public int RequestedFrameHeight { get { return ((Registers[VDW] & 0x1FF) + 1); } }
|
||||
|
||||
public int DisplayStartLine { get { return (Registers[VPR] >> 8) + (Registers[VPR] & 0x1F); } }
|
||||
|
||||
private const int MAWR = 0; // Memory Address Write Register
|
||||
private const int MARR = 1; // Memory Address Read Register
|
||||
private const int VRR = 2; // VRAM Read Register
|
||||
private const int VWR = 2; // VRAM Write Register
|
||||
private const int CR = 5; // Control Register
|
||||
private const int RCR = 6; // Raster Compare Register
|
||||
private const int BXR = 7; // Background X-scroll Register
|
||||
private const int BYR = 8; // Background Y-scroll Register
|
||||
private const int MWR = 9; // Memory-access Width Register
|
||||
private const int HSR = 10; // Horizontal Sync Register
|
||||
private const int HDR = 11; // Horizontal Display Register
|
||||
private const int VPR = 12; // Vertical synchronous register
|
||||
private const int VDW = 13; // Vertical display register
|
||||
private const int VCR = 14; // Vertical display END position register;
|
||||
private const int DCR = 15; // DMA Control Register
|
||||
private const int SOUR = 16; // Source address for DMA
|
||||
private const int DESR = 17; // Destination address for DMA
|
||||
private const int LENR = 18; // Length of DMA transfer. Writing this will initiate DMA.
|
||||
private const int SATB = 19; // Sprite Attribute Table base location in VRAM
|
||||
|
||||
private const int RegisterSelect = 0;
|
||||
private const int LSB = 2;
|
||||
private const int MSB = 3;
|
||||
|
||||
public const byte StatusVerticalBlanking = 0x20;
|
||||
public const byte StatusVramVramDmaComplete = 0x10;
|
||||
public const byte StatusVramSatDmaComplete = 0x08;
|
||||
public const byte StatusRasterCompare = 0x04;
|
||||
public const byte StatusSpriteOverflow = 0x02;
|
||||
public const byte StatusSprite0Collision = 0x01;
|
||||
|
||||
private HuC6280 cpu;
|
||||
private VCE vce;
|
||||
|
||||
public VDC(HuC6280 cpu, VCE vce)
|
||||
{
|
||||
this.cpu = cpu;
|
||||
this.vce = vce;
|
||||
}
|
||||
|
||||
public void WriteVDC(int port, byte value)
|
||||
{
|
||||
cpu.PendingCycles--;
|
||||
if (port == RegisterSelect)
|
||||
{
|
||||
RegisterLatch = (byte)(value & 0x1F);
|
||||
Log.Note("CPU","LATCH VDC REGISTER: {0:X}",RegisterLatch);
|
||||
}
|
||||
else if (port == LSB)
|
||||
{
|
||||
Registers[RegisterLatch] &= 0xFF00;
|
||||
Registers[RegisterLatch] |= value;
|
||||
}
|
||||
else if (port == MSB)
|
||||
{
|
||||
Registers[RegisterLatch] &= 0x00FF;
|
||||
Registers[RegisterLatch] |= (ushort) (value << 8);
|
||||
CompleteMSBWrite();
|
||||
}
|
||||
}
|
||||
|
||||
private void CompleteMSBWrite()
|
||||
{
|
||||
switch (RegisterLatch)
|
||||
{
|
||||
case MARR: // Memory Address Read Register
|
||||
ReadBuffer = VRAM[Registers[MARR] & 0x7FFF];
|
||||
break;
|
||||
case VWR: // VRAM Write Register
|
||||
VRAM[Registers[MAWR] & 0x7FFF] = Registers[VWR];
|
||||
UpdatePatternData((ushort) (Registers[MAWR] & 0x7FFF));
|
||||
UpdateSpriteData((ushort) (Registers[MAWR] & 0x7FFF));
|
||||
Registers[MAWR] += IncrementWidth;
|
||||
break;
|
||||
case CR:
|
||||
//if (Registers[CR] == 0)
|
||||
Log.Note("CPU", "****************** WRITE TO CR: {0:X}", Registers[CR]);
|
||||
break;
|
||||
case BXR:
|
||||
Registers[BXR] &= 0x3FF;
|
||||
break;
|
||||
case BYR:
|
||||
Registers[BYR] &= 0x1FF;
|
||||
BackgroundY = Registers[BYR];
|
||||
Console.WriteLine("Updating BYR to {0} at scanline {1}", BackgroundY, ScanLine);
|
||||
break;
|
||||
case HDR: // Horizontal Display Register - update framebuffer size
|
||||
FrameWidth = RequestedFrameWidth;
|
||||
if (FrameBuffer.Length != FrameWidth * FrameHeight)
|
||||
{
|
||||
FrameBuffer = new int[FrameWidth*FrameHeight];
|
||||
Console.WriteLine("RESIZED FRAME BUFFER: width="+FrameWidth);
|
||||
}
|
||||
break;
|
||||
case VPR:
|
||||
int vds = Registers[VPR] >> 8;
|
||||
int vsw = Registers[VPR] & 0x1F;
|
||||
Console.WriteLine("SET VPR: VDS {0} VSW {1} startpos={2} {3}",vds, vsw, vds+vsw, DisplayStartLine);
|
||||
break;
|
||||
case VDW: // Vertical Display Word? - update framebuffer size
|
||||
Console.WriteLine("REQUEST FRAME HEIGHT=" + RequestedFrameHeight);
|
||||
FrameHeight = RequestedFrameHeight;
|
||||
if (FrameBuffer.Length != FrameWidth * FrameHeight)
|
||||
{
|
||||
FrameBuffer = new int[FrameWidth * FrameHeight];
|
||||
Console.WriteLine("RESIZED FRAME BUFFER: height="+FrameHeight);
|
||||
}
|
||||
break;
|
||||
case VCR:
|
||||
Console.WriteLine("VCR / END POSITION: "+(Registers[VCR] & 0xFF));
|
||||
break;
|
||||
case LENR: // Initiate DMA transfer
|
||||
InitiateDMA();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public byte ReadVDC(int port)
|
||||
{
|
||||
cpu.PendingCycles--;
|
||||
byte retval = 0;
|
||||
switch (port)
|
||||
{
|
||||
case 0: // return status byte;
|
||||
retval = StatusByte;
|
||||
StatusByte = 0; // TODO maybe bit 6 should be preserved. but we dont currently emulate it.
|
||||
cpu.IRQ1Assert = false;
|
||||
return retval;
|
||||
case 1: // unused
|
||||
return 0;
|
||||
case 2: // LSB
|
||||
return (byte) (ReadBuffer & 0xFF);
|
||||
case 3: // MSB
|
||||
retval = (byte)(ReadBuffer >> 8);
|
||||
if (RegisterLatch == VRR)
|
||||
{
|
||||
Registers[MARR] += IncrementWidth;
|
||||
ReadBuffer = VRAM[Registers[MARR]&0x7FFF];
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void InitiateDMA()
|
||||
{
|
||||
Console.WriteLine("DOING DMA ********************************************* ");
|
||||
int advanceSource = (Registers[DCR] & 4) == 0 ? +1 : -1;
|
||||
int advanceDest = (Registers[DCR] & 8) == 0 ? +1 : -1;
|
||||
|
||||
for (;Registers[LENR]<0xFFFF;Registers[LENR]--)
|
||||
{
|
||||
VRAM[Registers[DESR] & 0x7FFF] = VRAM[Registers[SOUR] & 0x7FFF];
|
||||
UpdatePatternData(Registers[DESR]);
|
||||
UpdateSpriteData(Registers[DESR]);
|
||||
Registers[DESR] = (ushort)(Registers[DESR] + advanceDest);
|
||||
Registers[SOUR] = (ushort)(Registers[SOUR] + advanceSource);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdatePatternData(ushort addr)
|
||||
{
|
||||
int tileNo = (addr >> 4);
|
||||
int tileLineOffset = (addr & 0x7);
|
||||
|
||||
int bitplane01 = VRAM[(tileNo * 16) + tileLineOffset];
|
||||
int bitplane23 = VRAM[(tileNo * 16) + tileLineOffset + 8];
|
||||
|
||||
int patternBufferBase = (tileNo * 64) + (tileLineOffset * 8);
|
||||
|
||||
for (int x = 0; x < 8; x++)
|
||||
{
|
||||
byte pixel = (byte) ((bitplane01 >> x) & 1);
|
||||
pixel |= (byte) (((bitplane01 >> (x + 8)) & 1) << 1);
|
||||
pixel |= (byte) (((bitplane23 >> x) & 1) << 2);
|
||||
pixel |= (byte) (((bitplane23 >> (x + 8)) & 1) << 3);
|
||||
PatternBuffer[patternBufferBase + (7 - x)] = pixel;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateSpriteData(ushort addr)
|
||||
{
|
||||
int tileNo = addr >> 6;
|
||||
int tileOfs = addr & 0x3F;
|
||||
int bitplane = tileOfs/16;
|
||||
int line = addr & 0x0F;
|
||||
|
||||
int ofs = (tileNo*256) + (line*16) + 15;
|
||||
ushort value = VRAM[addr];
|
||||
byte bitAnd = (byte) (~(1 << bitplane));
|
||||
byte bitOr = (byte) (1 << bitplane);
|
||||
|
||||
for (int i=0; i<16; i++)
|
||||
{
|
||||
|
||||
if ((value & 1) == 1)
|
||||
SpriteBuffer[ofs] |= bitOr;
|
||||
else
|
||||
SpriteBuffer[ofs] &= bitAnd;
|
||||
ofs--;
|
||||
value >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveStateText(TextWriter writer, int vdcNo)
|
||||
{
|
||||
writer.WriteLine("[VDC"+vdcNo+"]");
|
||||
writer.Write("VRAM ");
|
||||
VRAM.SaveAsHex(writer);
|
||||
writer.Write("SAT ");
|
||||
SpriteAttributeTable.SaveAsHex(writer);
|
||||
writer.Write("Registers ");
|
||||
Registers.SaveAsHex(writer);
|
||||
|
||||
writer.WriteLine("RegisterLatch {0:X2}", RegisterLatch);
|
||||
writer.WriteLine("ReadBuffer {0:X4}", ReadBuffer);
|
||||
writer.WriteLine("StatusByte {0:X2}", StatusByte);
|
||||
writer.WriteLine("[/VDC"+vdcNo+"]\n");
|
||||
}
|
||||
|
||||
public void LoadStateText(TextReader reader, int vdcNo)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
string[] args = reader.ReadLine().Split(' ');
|
||||
if (args[0].Trim() == "") continue;
|
||||
if (args[0] == "[/VDC"+vdcNo+"]") break;
|
||||
if (args[0] == "VRAM")
|
||||
VRAM.ReadFromHex(args[1]);
|
||||
else if (args[0] == "SAT")
|
||||
SpriteAttributeTable.ReadFromHex(args[1]);
|
||||
else if (args[0] == "Registers")
|
||||
Registers.ReadFromHex(args[1]);
|
||||
else if (args[0] == "RegisterLatch")
|
||||
RegisterLatch = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "ReadBuffer")
|
||||
ReadBuffer = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "StatusByte")
|
||||
StatusByte = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else
|
||||
Console.WriteLine("Skipping unrecognized identifier " + args[0]);
|
||||
}
|
||||
|
||||
for (ushort i = 0; i < VRAM.Length; i++)
|
||||
{
|
||||
UpdatePatternData(i);
|
||||
UpdateSpriteData(i);
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveStateBinary(BinaryWriter writer)
|
||||
{
|
||||
for (int i=0; i < VRAM.Length; i++)
|
||||
writer.Write(VRAM[i]);
|
||||
for (int i=0; i < SpriteAttributeTable.Length; i++)
|
||||
writer.Write(SpriteAttributeTable[i]);
|
||||
for (int i = 0; i < Registers.Length; i++)
|
||||
writer.Write(Registers[i]);
|
||||
writer.Write(RegisterLatch);
|
||||
writer.Write(ReadBuffer);
|
||||
writer.Write(StatusByte);
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader reader)
|
||||
{
|
||||
for (ushort i=0; i < VRAM.Length; i++)
|
||||
{
|
||||
VRAM[i] = reader.ReadUInt16();
|
||||
UpdatePatternData(i);
|
||||
UpdateSpriteData(i);
|
||||
}
|
||||
for (int i=0; i < SpriteAttributeTable.Length; i++)
|
||||
SpriteAttributeTable[i] = reader.ReadUInt16();
|
||||
for (int i=0; i < Registers.Length; i++)
|
||||
Registers[i] = reader.ReadUInt16();
|
||||
RegisterLatch = reader.ReadByte();
|
||||
ReadBuffer = reader.ReadUInt16();
|
||||
StatusByte = reader.ReadByte();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,466 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using BizHawk.Emulation.CPUs.H6280;
|
||||
|
||||
namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||
{
|
||||
// ------------------------------------------------------
|
||||
// HuC6202 Video Priority Controller
|
||||
// ------------------------------------------------------
|
||||
// Responsible for merging VDC1 and VDC2 data on the SuperGrafx.
|
||||
// Pretty much all documentation on the SuperGrafx courtesy of Charles MacDonald.
|
||||
|
||||
// VPC Frame timing has not been updated with new stuff. I expect Madoo Granzort to be fixed once the timing is updated.
|
||||
// However, 1) The new frame timing is not working 100% yet, 2) I haven't decided if I want to continue having separate
|
||||
// rendering in the VPC and the VDC.
|
||||
|
||||
public sealed class VPC : IVideoProvider
|
||||
{
|
||||
public VDC VDC1;
|
||||
public VDC VDC2;
|
||||
public VCE VCE;
|
||||
private HuC6280 CPU;
|
||||
|
||||
public byte[] Registers = {0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
public int Window1Width { get { return ((Registers[3] & 3) << 8) | Registers[2]; } }
|
||||
public int Window2Width { get { return ((Registers[5] & 3) << 8) | Registers[4]; } }
|
||||
public int PriorityModeSlot0 { get { return Registers[0] & 0x0F; } }
|
||||
public int PriorityModeSlot1 { get { return (Registers[0] >> 4) & 0x0F; } }
|
||||
public int PriorityModeSlot2 { get { return Registers[1] & 0x0F; } }
|
||||
public int PriorityModeSlot3 { get { return (Registers[1] >> 4) & 0x0F; } }
|
||||
|
||||
public VPC(VDC vdc1, VDC vdc2, VCE vce, HuC6280 cpu)
|
||||
{
|
||||
VDC1 = vdc1;
|
||||
VDC2 = vdc2;
|
||||
VCE = vce;
|
||||
CPU = cpu;
|
||||
|
||||
// latch initial video buffer
|
||||
FrameBuffer = vdc1.GetVideoBuffer();
|
||||
FrameWidth = vdc1.BufferWidth;
|
||||
FrameHeight = vdc1.BufferHeight;
|
||||
}
|
||||
|
||||
public byte ReadVPC(int port)
|
||||
{
|
||||
switch (port)
|
||||
{
|
||||
case 0x08: return Registers[0];
|
||||
case 0x09: return Registers[1];
|
||||
case 0x0A: return Registers[2];
|
||||
case 0x0B: return Registers[3];
|
||||
case 0x0C: return Registers[4];
|
||||
case 0x0D: return Registers[5];
|
||||
case 0x0E: return Registers[6];
|
||||
case 0x0F: return 0;
|
||||
default: return 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteVPC(int port, byte value)
|
||||
{
|
||||
switch (port)
|
||||
{
|
||||
case 0x08: Registers[0] = value; break;
|
||||
case 0x09: Registers[1] = value; break;
|
||||
case 0x0A: Registers[2] = value; break;
|
||||
case 0x0B: Registers[3] = value; break;
|
||||
case 0x0C: Registers[4] = value; break;
|
||||
case 0x0D: Registers[5] = value; break;
|
||||
case 0x0E:
|
||||
// CPU Store Immediate VDC Select
|
||||
CPU.WriteVDC = (value & 1) == 0 ? (Action<int,byte>) VDC1.WriteVDC : VDC2.WriteVDC;
|
||||
Registers[6] = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveStateBinary(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(Registers);
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader reader)
|
||||
{
|
||||
Registers = reader.ReadBytes(7);
|
||||
}
|
||||
|
||||
public void SaveStateText(TextWriter writer)
|
||||
{
|
||||
writer.WriteLine("[VPC]");
|
||||
writer.Write("Registers ");
|
||||
Registers.SaveAsHex(writer);
|
||||
writer.WriteLine("[/VPC]\n");
|
||||
}
|
||||
|
||||
public void LoadStateText(TextReader reader)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
string[] args = reader.ReadLine().Split(' ');
|
||||
if (args[0].Trim() == "") continue;
|
||||
if (args[0] == "[/VPC]") break;
|
||||
if (args[0] == "Registers")
|
||||
Registers.ReadFromHex(args[1]);
|
||||
else
|
||||
Console.WriteLine("Skipping unrecognized identifier " + args[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// We use a single priority mode for the whole frame.
|
||||
// No commercial SGX games really use the 'window' features AFAIK.
|
||||
// And there are no homebrew SGX games I know of.
|
||||
|
||||
private const int BXR = 7;
|
||||
private const int BYR = 8;
|
||||
|
||||
private int EffectivePriorityMode = 0;
|
||||
|
||||
private int FrameHeight;
|
||||
private int FrameWidth;
|
||||
private int[] FrameBuffer;
|
||||
|
||||
private byte[] PriorityBuffer = new byte[512];
|
||||
private byte[] InterSpritePriorityBuffer = new byte[512];
|
||||
|
||||
public void ExecFrame()
|
||||
{
|
||||
// Determine the effective priority mode.
|
||||
if (Window1Width < 0x40 && Window2Width < 0x40)
|
||||
EffectivePriorityMode = PriorityModeSlot3 >> 2;
|
||||
else if (Window2Width > 512)
|
||||
EffectivePriorityMode = PriorityModeSlot1 >> 2;
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Unsupported VPC window settings");
|
||||
EffectivePriorityMode = 0;
|
||||
}
|
||||
|
||||
// Latch frame dimensions and framebuffer, for purely dumb reasons
|
||||
FrameWidth = VDC1.BufferWidth;
|
||||
FrameHeight = VDC1.BufferHeight;
|
||||
FrameBuffer = VDC1.GetVideoBuffer();
|
||||
|
||||
for (int ScanLine = 0; ScanLine < 262; ScanLine++)
|
||||
{
|
||||
VDC1.ScanLine = ScanLine;
|
||||
VDC2.ScanLine = ScanLine;
|
||||
|
||||
if ((ScanLine + 64) == (VDC1.Registers[6] & 0x3FF))
|
||||
{
|
||||
if (VDC1.IntRasterCompare)
|
||||
{
|
||||
VDC1.StatusByte |= VDC.StatusRasterCompare;
|
||||
CPU.IRQ1Assert = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((ScanLine + 64) == (VDC2.Registers[6] & 0x3FF))
|
||||
{
|
||||
if (VDC2.IntRasterCompare)
|
||||
{
|
||||
VDC2.StatusByte |= VDC.StatusRasterCompare;
|
||||
CPU.IRQ1Assert = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ScanLine == 240 && VDC1.IntVerticalBlank)
|
||||
{
|
||||
VDC1.StatusByte |= VDC.StatusVerticalBlanking;
|
||||
CPU.IRQ1Assert = true;
|
||||
}
|
||||
|
||||
if (ScanLine == 240 && VDC2.IntVerticalBlank)
|
||||
{
|
||||
VDC2.StatusByte |= VDC.StatusVerticalBlanking;
|
||||
CPU.IRQ1Assert = true;
|
||||
}
|
||||
|
||||
CPU.Execute(455);
|
||||
|
||||
if (ScanLine < FrameHeight)
|
||||
RenderScanLine();
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderScanLine()
|
||||
{
|
||||
if (VDC1.ScanLine == 0)
|
||||
{
|
||||
VDC1.BackgroundY = VDC1.Registers[BYR];
|
||||
VDC2.BackgroundY = VDC2.Registers[BYR];
|
||||
}
|
||||
|
||||
InitializeScanLine(VDC1.ScanLine);
|
||||
|
||||
switch (EffectivePriorityMode)
|
||||
{
|
||||
case 0:
|
||||
RenderBackgroundScanline(VDC1, 12);
|
||||
RenderBackgroundScanline(VDC2, 2);
|
||||
RenderSpritesScanline(VDC1, 11, 14);
|
||||
RenderSpritesScanline(VDC2, 1, 3);
|
||||
break;
|
||||
case 1:
|
||||
RenderBackgroundScanline(VDC1, 12);
|
||||
RenderBackgroundScanline(VDC2, 2);
|
||||
RenderSpritesScanline(VDC1, 11, 14);
|
||||
RenderSpritesScanline(VDC2, 1, 13);
|
||||
break;
|
||||
}
|
||||
|
||||
if (VDC1.ScanLine == FrameHeight - 1)
|
||||
{
|
||||
VDC1.UpdateSpriteAttributeTable();
|
||||
VDC2.UpdateSpriteAttributeTable();
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeScanLine(int scanline)
|
||||
{
|
||||
// Clear priority buffer
|
||||
for (int i = 0; i < FrameWidth; i++)
|
||||
PriorityBuffer[i] = 0;
|
||||
|
||||
// Initialize scanline to background color
|
||||
for (int i = 0; i < FrameWidth; i++)
|
||||
FrameBuffer[(scanline * FrameWidth) + i] = VCE.Palette[0];
|
||||
}
|
||||
|
||||
private void RenderBackgroundScanline(VDC vdc, byte priority)
|
||||
{
|
||||
if (vdc.BackgroundEnabled == false)
|
||||
{
|
||||
vdc.BackgroundY++;
|
||||
vdc.BackgroundY &= 0x01FF;
|
||||
return;
|
||||
}
|
||||
|
||||
int vertLine = vdc.BackgroundY;
|
||||
vertLine %= vdc.BatHeight * 8;
|
||||
int yTile = (vertLine / 8);
|
||||
int yOfs = vertLine % 8;
|
||||
vdc.BackgroundY++;
|
||||
vdc.BackgroundY &= 0x01FF;
|
||||
|
||||
int xScroll = vdc.Registers[BXR] & 0x3FF;
|
||||
for (int x = 0; x < FrameWidth; x++)
|
||||
{
|
||||
if (PriorityBuffer[x] >= priority) continue;
|
||||
int xTile = ((x + xScroll) / 8) % vdc.BatWidth;
|
||||
int xOfs = (x + xScroll) & 7;
|
||||
int tileNo = vdc.VRAM[(ushort)(((yTile * vdc.BatWidth) + xTile))] & 2047;
|
||||
int paletteNo = vdc.VRAM[(ushort)(((yTile * vdc.BatWidth) + xTile))] >> 12;
|
||||
int paletteBase = paletteNo * 16;
|
||||
|
||||
byte c = vdc.PatternBuffer[(tileNo * 64) + (yOfs * 8) + xOfs];
|
||||
if (c != 0)
|
||||
{
|
||||
FrameBuffer[(vdc.ScanLine * FrameWidth) + x] = VCE.Palette[paletteBase + c];
|
||||
PriorityBuffer[x] = priority;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] heightTable = { 16, 32, 64, 64 };
|
||||
|
||||
private void RenderSpritesScanline(VDC vdc, byte lowPriority, byte highPriority)
|
||||
{
|
||||
if (vdc.SpritesEnabled == false)
|
||||
return;
|
||||
|
||||
// clear inter-sprite priority buffer
|
||||
for (int i = 0; i < FrameWidth; i++)
|
||||
InterSpritePriorityBuffer[i] = 0;
|
||||
|
||||
for (int i = 0; i < 64; i++)
|
||||
{
|
||||
int y = (vdc.SpriteAttributeTable[(i * 4) + 0] & 1023) - 64;
|
||||
int x = (vdc.SpriteAttributeTable[(i * 4) + 1] & 1023) - 32;
|
||||
ushort flags = vdc.SpriteAttributeTable[(i * 4) + 3];
|
||||
int height = heightTable[(flags >> 12) & 3];
|
||||
|
||||
if (y + height <= vdc.ScanLine || y > vdc.ScanLine)
|
||||
continue;
|
||||
|
||||
int patternNo = (((vdc.SpriteAttributeTable[(i * 4) + 2]) >> 1) & 0x1FF);
|
||||
int paletteBase = 256 + ((flags & 15) * 16);
|
||||
int width = (flags & 0x100) == 0 ? 16 : 32;
|
||||
bool priority = (flags & 0x80) != 0;
|
||||
bool hflip = (flags & 0x0800) != 0;
|
||||
bool vflip = (flags & 0x8000) != 0;
|
||||
|
||||
if (width == 32)
|
||||
patternNo &= 0x1FE;
|
||||
|
||||
int yofs = 0;
|
||||
if (vflip == false)
|
||||
{
|
||||
yofs = (vdc.ScanLine - y) & 15;
|
||||
if (height == 32)
|
||||
{
|
||||
patternNo &= 0x1FD;
|
||||
if (vdc.ScanLine - y >= 16)
|
||||
{
|
||||
y += 16;
|
||||
patternNo += 2;
|
||||
}
|
||||
}
|
||||
else if (height == 64)
|
||||
{
|
||||
patternNo &= 0x1F9;
|
||||
if (vdc.ScanLine - y >= 48)
|
||||
{
|
||||
y += 48;
|
||||
patternNo += 6;
|
||||
}
|
||||
else if (vdc.ScanLine - y >= 32)
|
||||
{
|
||||
y += 32;
|
||||
patternNo += 4;
|
||||
}
|
||||
else if (vdc.ScanLine - y >= 16)
|
||||
{
|
||||
y += 16;
|
||||
patternNo += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
else // vflip == true
|
||||
{
|
||||
yofs = 15 - ((vdc.ScanLine - y) & 15);
|
||||
if (height == 32)
|
||||
{
|
||||
patternNo &= 0x1FD;
|
||||
if (vdc.ScanLine - y < 16)
|
||||
{
|
||||
y += 16;
|
||||
patternNo += 2;
|
||||
}
|
||||
}
|
||||
else if (height == 64)
|
||||
{
|
||||
patternNo &= 0x1F9;
|
||||
if (vdc.ScanLine - y < 16)
|
||||
{
|
||||
y += 48;
|
||||
patternNo += 6;
|
||||
}
|
||||
else if (vdc.ScanLine - y < 32)
|
||||
{
|
||||
y += 32;
|
||||
patternNo += 4;
|
||||
}
|
||||
else if (vdc.ScanLine - y < 48)
|
||||
{
|
||||
y += 16;
|
||||
patternNo += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hflip == false)
|
||||
{
|
||||
if (x + width > 0 && y + height > 0)
|
||||
{
|
||||
for (int xs = x >= 0 ? x : 0; xs < x + 16 && xs >= 0 && xs < FrameWidth; xs++)
|
||||
{
|
||||
byte pixel = vdc.SpriteBuffer[(patternNo * 256) + (yofs * 16) + (xs - x)];
|
||||
if (pixel != 0 && InterSpritePriorityBuffer[xs] == 0)
|
||||
{
|
||||
InterSpritePriorityBuffer[xs] = 1;
|
||||
byte myPriority = priority ? highPriority : lowPriority;
|
||||
if (PriorityBuffer[xs] < myPriority)
|
||||
{
|
||||
FrameBuffer[(vdc.ScanLine * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel];
|
||||
PriorityBuffer[xs] = myPriority;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (width == 32)
|
||||
{
|
||||
patternNo++;
|
||||
x += 16;
|
||||
for (int xs = x >= 0 ? x : 0; xs < x + 16 && xs >= 0 && xs < FrameWidth; xs++)
|
||||
{
|
||||
byte pixel = vdc.SpriteBuffer[(patternNo * 256) + (yofs * 16) + (xs - x)];
|
||||
if (pixel != 0 && InterSpritePriorityBuffer[xs] == 0)
|
||||
{
|
||||
InterSpritePriorityBuffer[xs] = 1;
|
||||
byte myPriority = priority ? highPriority : lowPriority;
|
||||
if (PriorityBuffer[xs] < myPriority)
|
||||
{
|
||||
FrameBuffer[(vdc.ScanLine * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel];
|
||||
PriorityBuffer[xs] = myPriority;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // hflip = true
|
||||
if (x + width > 0 && y + height > 0)
|
||||
{
|
||||
if (width == 32)
|
||||
patternNo++;
|
||||
for (int xs = x >= 0 ? x : 0; xs < x + 16 && xs >= 0 && xs < FrameWidth; xs++)
|
||||
{
|
||||
byte pixel = vdc.SpriteBuffer[(patternNo * 256) + (yofs * 16) + 15 - (xs - x)];
|
||||
if (pixel != 0 && InterSpritePriorityBuffer[xs] == 0)
|
||||
{
|
||||
InterSpritePriorityBuffer[xs] = 1;
|
||||
byte myPriority = priority ? highPriority : lowPriority;
|
||||
if (PriorityBuffer[xs] < myPriority)
|
||||
{
|
||||
FrameBuffer[(vdc.ScanLine * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel];
|
||||
PriorityBuffer[xs] = myPriority;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (width == 32)
|
||||
{
|
||||
patternNo--;
|
||||
x += 16;
|
||||
for (int xs = x >= 0 ? x : 0; xs < x + 16 && xs >= 0 && xs < FrameWidth; xs++)
|
||||
{
|
||||
byte pixel = vdc.SpriteBuffer[(patternNo * 256) + (yofs * 16) + 15 - (xs - x)];
|
||||
if (pixel != 0 && InterSpritePriorityBuffer[xs] == 0)
|
||||
{
|
||||
InterSpritePriorityBuffer[xs] = 1;
|
||||
byte myPriority = priority ? highPriority : lowPriority;
|
||||
if (PriorityBuffer[xs] < myPriority)
|
||||
{
|
||||
FrameBuffer[(vdc.ScanLine * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel];
|
||||
PriorityBuffer[xs] = myPriority;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int[] GetVideoBuffer()
|
||||
{
|
||||
return FrameBuffer;
|
||||
}
|
||||
|
||||
public int BufferWidth
|
||||
{
|
||||
get { return FrameWidth; }
|
||||
}
|
||||
|
||||
public int BufferHeight
|
||||
{
|
||||
get { return FrameHeight; }
|
||||
}
|
||||
|
||||
public int BackgroundColor
|
||||
{
|
||||
get { return VCE.Palette[0]; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
using System;
|
||||
|
||||
namespace BizHawk.Emulation.Consoles.Sega
|
||||
{
|
||||
public partial class GenVDP
|
||||
{
|
||||
// TODO: make this a requirement of constructor?
|
||||
public Func<int, short> DmaReadFrom68000; // TODO make ushort
|
||||
|
||||
public int DmaLength { get { return Registers[19] | (Registers[20] << 8); } }
|
||||
|
||||
public int DmaSource
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((Registers[23] & 0x80) == 0) // 68000 -> VRAM copy mode
|
||||
return ((Registers[21] << 1) | (Registers[22] << 9) | (Registers[23] << 17)) & 0xFFFFFE;
|
||||
|
||||
// Else VRAM/VRAM copy mode
|
||||
return (Registers[21] | (Registers[22] << 8)) & 0xFFFFFE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private bool DmaFillModePending;
|
||||
|
||||
private void ExecuteDmaFill(ushort data)
|
||||
{
|
||||
Console.WriteLine("DMA FILL REQD, WRITE {0:X4}, {1:X4} times, at {2:X4}", data, DmaLength, VdpDataAddr);
|
||||
|
||||
// TODO: Is the address really VdpDataAddr and not DMA source? I guess that makes sense.
|
||||
// TODO: It should spread this out, not do it all at once.
|
||||
// TODO: DMA can go to places besides just VRAM (eg CRAM, VSRAM)
|
||||
// TODO: Does DMA fill really use the actual increment register value?
|
||||
|
||||
int length = DmaLength;
|
||||
if (length == 0)
|
||||
length = 0x10000; // Really necessary?
|
||||
|
||||
byte fillByte = (byte)(data >> 8);
|
||||
|
||||
do
|
||||
{
|
||||
VRAM[VdpDataAddr & 0xFFFF] = fillByte;
|
||||
UpdatePatternBuffer(VdpDataAddr & 0xFFFF);
|
||||
VdpDataAddr += Registers[15];
|
||||
} while (--length > 0);
|
||||
|
||||
DmaFillModePending = false;
|
||||
}
|
||||
|
||||
private void Execute68000VramCopy()
|
||||
{
|
||||
Console.WriteLine("DMA 68000 -> VRAM COPY REQ'D. LENGTH {0:X4}, SOURCE {1:X4}", DmaLength, DmaSource);
|
||||
|
||||
int length = DmaLength;
|
||||
if (length == 0)
|
||||
length = 0x10000;
|
||||
|
||||
int source = DmaSource;
|
||||
|
||||
do
|
||||
{
|
||||
ushort value = (ushort) DmaReadFrom68000(source);
|
||||
source += 2;
|
||||
// TODO funky source behavior
|
||||
WriteVdpData(value);
|
||||
} while (--length > 0);
|
||||
|
||||
// TODO: find correct number of 68k cycles to burn
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
using System;
|
||||
|
||||
namespace BizHawk.Emulation.Consoles.Sega
|
||||
{
|
||||
public partial class GenVDP
|
||||
{
|
||||
public void RenderLine()
|
||||
{
|
||||
if (ScanLine == 223)
|
||||
{
|
||||
for (int i = 0; i < FrameBuffer.Length; i++)
|
||||
FrameBuffer[i] = 0;
|
||||
|
||||
//RenderPatterns();
|
||||
RenderPalette();
|
||||
RenderScrollA();
|
||||
RenderScrollB();
|
||||
RenderSprites();
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderPalette()
|
||||
{
|
||||
for (int p = 0; p < 4; p++)
|
||||
for (int i = 0; i < 16; i++)
|
||||
FrameBuffer[(p*FrameWidth) + i] = Palette[(p*16) + i];
|
||||
}
|
||||
|
||||
private void RenderPatterns()
|
||||
{
|
||||
for (int yi=0; yi<28; yi++)
|
||||
for (int xi=0; xi<(Display40Mode?40:32); xi++)
|
||||
RenderPattern(xi * 8, yi * 8, (yi * (Display40Mode ? 40 : 32)) + xi, 0);
|
||||
}
|
||||
|
||||
private void RenderPattern(int x, int y, int pattern, int palette)
|
||||
{
|
||||
for (int yi = 0; yi < 8; yi++)
|
||||
{
|
||||
for (int xi = 0; xi < 8; xi++)
|
||||
{
|
||||
byte c = PatternBuffer[(pattern*64) + (yi*8) + xi];
|
||||
if (c != 0)
|
||||
FrameBuffer[((y + yi)*FrameWidth) + xi + x] = Palette[(palette*16) + c];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderScrollA()
|
||||
{
|
||||
for (int yc=0; yc<24; yc++)
|
||||
{
|
||||
for (int xc=0; xc<32; xc++)
|
||||
{
|
||||
int cellOfs = NameTableAddrA + (yc*NameTableWidth*2) + (xc*2);
|
||||
int info = (VRAM[cellOfs+1] << 8) | VRAM[cellOfs];
|
||||
int pattern = info & 0x7FF;
|
||||
int palette = (info >> 13) & 3;
|
||||
RenderPattern(xc*8, yc*8, pattern,palette);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderScrollB()
|
||||
{
|
||||
for (int yc = 0; yc < 24; yc++)
|
||||
{
|
||||
for (int xc = 0; xc < 40; xc++)
|
||||
{
|
||||
int cellOfs = NameTableAddrB + (yc * NameTableWidth * 2) + (xc * 2);
|
||||
int info = (VRAM[cellOfs+1] << 8) | VRAM[cellOfs];
|
||||
int pattern = info & 0x7FF;
|
||||
int palette = (info >> 13) & 3;
|
||||
RenderPattern(xc * 8, yc * 8, pattern, palette);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void RenderSprites()
|
||||
{
|
||||
Sprite sprite = FetchSprite(0);
|
||||
/*if (sprite.X > 0)
|
||||
Console.WriteLine("doot");*/
|
||||
}
|
||||
|
||||
private Sprite FetchSprite(int spriteNo)
|
||||
{
|
||||
int satbase = SpriteAttributeTableAddr + (spriteNo*8);
|
||||
Sprite sprite = new Sprite();
|
||||
sprite.Y = (VRAM[satbase + 1] | (VRAM[satbase + 0] << 8) & 0x3FF) - 128;
|
||||
sprite.X = (VRAM[satbase + 7] | (VRAM[satbase + 6] << 8) & 0x3FF) - 128;
|
||||
sprite.Width = ((VRAM[satbase + 2] >> 2) & 3) + 1;
|
||||
sprite.Height = (VRAM[satbase + 2] & 3) + 1;
|
||||
sprite.Link = VRAM[satbase + 3] & 0x7F;
|
||||
sprite.PatternIndex = VRAM[satbase + 5] | (VRAM[satbase + 6] << 8) & 0x7FF;
|
||||
sprite.Palette = (VRAM[satbase + 5] >> 5) & 3;
|
||||
return sprite;
|
||||
}
|
||||
|
||||
|
||||
struct Sprite
|
||||
{
|
||||
public int X, Y;
|
||||
public int Width, Height;
|
||||
public int Link;
|
||||
public int Palette;
|
||||
public int PatternIndex;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,264 @@
|
|||
using System;
|
||||
|
||||
namespace BizHawk.Emulation.Consoles.Sega
|
||||
{
|
||||
public sealed partial class GenVDP : IVideoProvider
|
||||
{
|
||||
// Memory
|
||||
public byte[] VRAM = new byte[0x10000];
|
||||
public ushort[] CRAM = new ushort[64];
|
||||
public ushort[] VSRAM = new ushort[40];
|
||||
public byte[] Registers = new byte[0x20];
|
||||
|
||||
public byte[] PatternBuffer = new byte[0x20000];
|
||||
public int[] Palette = new int[64];
|
||||
public int[] FrameBuffer = new int[256*224];
|
||||
public int FrameWidth = 256;
|
||||
|
||||
public int ScanLine;
|
||||
|
||||
public bool HInterruptsEnabled { get { return (Registers[0] & 0x10) != 0; } }
|
||||
public bool DisplayEnabled { get { return (Registers[1] & 0x40) != 0; } }
|
||||
public bool VInterruptEnabled { get { return (Registers[1] & 0x20) != 0; } }
|
||||
public bool DmaEnabled { get { return (Registers[1] & 0x10) != 0; } }
|
||||
public bool CellBasedVertScroll { get { return (Registers[11] & 0x08) != 0; } }
|
||||
public bool Display40Mode { get { return (Registers[12] & 0x81) != 0; } }
|
||||
|
||||
private ushort NameTableAddrA;
|
||||
private ushort NameTableAddrB;
|
||||
private ushort NameTableAddrWindow;
|
||||
private ushort SpriteAttributeTableAddr;
|
||||
private ushort HScrollTableAddr;
|
||||
private byte NameTableWidth;
|
||||
private byte NameTableHeight;
|
||||
|
||||
private bool ControlWordPending;
|
||||
private ushort VdpDataAddr;
|
||||
private byte VdpDataCode;
|
||||
|
||||
private static readonly byte[] PalXlatTable = { 0, 0, 36, 36, 73, 73, 109, 109, 145, 145, 182, 182, 219, 219, 255, 255 };
|
||||
|
||||
public void WriteVdpControl(ushort data)
|
||||
{
|
||||
Console.WriteLine("[PC = {0:X6}] VDP: Control Write {1:X4}", /*Genesis._MainCPU.PC*/0, data);
|
||||
|
||||
if (ControlWordPending == false)
|
||||
{
|
||||
if ((data & 0xC000) == 0x8000)
|
||||
{
|
||||
int reg = (data >> 8) & 0x1F;
|
||||
byte value = (byte) (data & 0xFF);
|
||||
WriteVdpRegister(reg, value);
|
||||
VdpDataCode = 0; // should we just clone GenesisPlus behavior?
|
||||
} else {
|
||||
ControlWordPending = true;
|
||||
VdpDataAddr &= 0xC000;
|
||||
VdpDataAddr |= (ushort) (data & 0x3FFF);
|
||||
VdpDataCode &= 0x3C;
|
||||
VdpDataCode |= (byte) (data >> 14);
|
||||
Console.WriteLine("Address = {0:X4}", VdpDataAddr);
|
||||
Console.WriteLine("Code = {0:X2}", VdpDataCode);
|
||||
}
|
||||
} else {
|
||||
ControlWordPending = false;
|
||||
|
||||
// Update data address and code
|
||||
VdpDataAddr &= 0x3FFF;
|
||||
VdpDataAddr |= (ushort) ((data & 0x03) << 14);
|
||||
Console.WriteLine("Address = {0:X4}", VdpDataAddr);
|
||||
VdpDataCode &= 0x03;
|
||||
VdpDataCode |= (byte) ((data >> 2) & 0x3C);
|
||||
Console.WriteLine("Code = {0:X2}", VdpDataCode);
|
||||
|
||||
if ((VdpDataCode & 0x20) != 0 && DmaEnabled) // DMA triggered
|
||||
{
|
||||
Console.WriteLine("DMA TIME!");
|
||||
|
||||
// what type of DMA?
|
||||
switch (Registers[23] >> 6)
|
||||
{
|
||||
case 2:
|
||||
Console.WriteLine("VRAM FILL");
|
||||
DmaFillModePending = true;
|
||||
break;
|
||||
case 3:
|
||||
Console.WriteLine("VRAM COPY **** UNIMPLEMENTED ***");
|
||||
break;
|
||||
default:
|
||||
Console.WriteLine("68k->VRAM COPY **** UNIMPLEMENTED ***");
|
||||
Execute68000VramCopy();
|
||||
break;
|
||||
}
|
||||
Console.WriteLine("DMA LEN = "+DmaLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ushort ReadVdpControl()
|
||||
{
|
||||
Console.WriteLine("VDP: Control Read");
|
||||
ushort value = 0;
|
||||
value |= 0x8000; // Fifo empty
|
||||
return value;
|
||||
}
|
||||
|
||||
public void WriteVdpData(ushort data)
|
||||
{
|
||||
ControlWordPending = false;
|
||||
|
||||
// byte-swap incoming data when A0 is set
|
||||
if ((VdpDataAddr & 1) != 0)
|
||||
data = (ushort) ((data >> 8) | (data << 8));
|
||||
|
||||
if (DmaFillModePending)
|
||||
{
|
||||
ExecuteDmaFill(data);
|
||||
}
|
||||
|
||||
switch (VdpDataCode & 7)
|
||||
{
|
||||
case 1: // VRAM Write
|
||||
VRAM[VdpDataAddr & 0xFFFE] = (byte) data;
|
||||
VRAM[(VdpDataAddr & 0xFFFE) + 1] = (byte) (data >> 8);
|
||||
UpdatePatternBuffer(VdpDataAddr & 0xFFFE);
|
||||
UpdatePatternBuffer((VdpDataAddr & 0xFFFE) + 1);
|
||||
Console.WriteLine("Wrote VRAM[{0:X4}] = {1:X4}", VdpDataAddr, data);
|
||||
VdpDataAddr += Registers[0x0F];
|
||||
break;
|
||||
case 3: // CRAM write
|
||||
CRAM[(VdpDataAddr / 2) % 64] = data;
|
||||
Console.WriteLine("Wrote CRAM[{0:X2}] = {1:X4}", (VdpDataAddr / 2) % 64, data);
|
||||
ProcessPalette((VdpDataAddr/2)%64);
|
||||
VdpDataAddr += Registers[0x0F];
|
||||
break;
|
||||
case 5: // VSRAM write
|
||||
VSRAM[(VdpDataAddr / 2) % 40] = data;
|
||||
Console.WriteLine("Wrote VSRAM[{0:X2}] = {1:X4}", (VdpDataAddr / 2) % 40, data);
|
||||
VdpDataAddr += Registers[0x0F];
|
||||
break;
|
||||
default:
|
||||
Console.WriteLine("VDP DATA WRITE WITH UNHANDLED CODE!!!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public ushort ReadVdpData()
|
||||
{
|
||||
Console.WriteLine("VDP: Data Read");
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void WriteVdpRegister(int register, byte data)
|
||||
{
|
||||
Console.WriteLine("Register {0}: {1:X2}", register, data);
|
||||
switch (register)
|
||||
{
|
||||
case 0x00:
|
||||
Registers[register] = data;
|
||||
Console.WriteLine("HINT enabled: "+ HInterruptsEnabled);
|
||||
break;
|
||||
case 0x01:
|
||||
Registers[register] = data;
|
||||
Console.WriteLine("DmaEnabled: "+DmaEnabled);
|
||||
Console.WriteLine("VINT enabled: " + VInterruptEnabled);
|
||||
break;
|
||||
case 0x02: // Name Table Address for Layer A
|
||||
NameTableAddrA = (ushort) ((data & 0x38) << 10);
|
||||
Console.WriteLine("SET NTa A = {0:X4}",NameTableAddrA);
|
||||
break;
|
||||
case 0x03: // Name Table Address for Window
|
||||
NameTableAddrWindow = (ushort) ((data & 0x3E) << 10);
|
||||
Console.WriteLine("SET NTa W = {0:X4}", NameTableAddrWindow);
|
||||
break;
|
||||
case 0x04: // Name Table Address for Layer B
|
||||
NameTableAddrB = (ushort) (data << 13);
|
||||
Console.WriteLine("SET NTa B = {0:X4}", NameTableAddrB);
|
||||
break;
|
||||
case 0x05: // Sprite Attribute Table Address
|
||||
SpriteAttributeTableAddr = (ushort) (data << 9);
|
||||
Console.WriteLine("SET SAT attr = {0:X4}", SpriteAttributeTableAddr);
|
||||
break;
|
||||
case 0x0C: // Mode Set #4
|
||||
// TODO interlaced modes
|
||||
if ((data & 0x81) == 0)
|
||||
{
|
||||
// Display is 32 cells wide
|
||||
if (FrameWidth != 256)
|
||||
{
|
||||
FrameBuffer = new int[256*224];
|
||||
FrameWidth = 256;
|
||||
Console.WriteLine("SWITCH TO 32 CELL WIDE MODE");
|
||||
}
|
||||
} else {
|
||||
// Display is 40 cells wide
|
||||
if (FrameWidth != 320)
|
||||
{
|
||||
FrameBuffer = new int[320*224];
|
||||
FrameWidth = 320;
|
||||
Console.WriteLine("SWITCH TO 40 CELL WIDE MODE");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x0D: // H Scroll Table Address
|
||||
HScrollTableAddr = (ushort) (data << 10);
|
||||
Console.WriteLine("SET HScrollTab attr = {0:X4}", HScrollTableAddr);
|
||||
break;
|
||||
case 0x0F:
|
||||
Console.WriteLine("Set Data Increment to "+data);
|
||||
break;
|
||||
case 0x10:
|
||||
switch (data & 0x03)
|
||||
{
|
||||
case 0: NameTableWidth = 32; break;
|
||||
case 1: NameTableWidth = 64; break;
|
||||
case 2: NameTableWidth = 32; break; // invalid setting
|
||||
case 3: NameTableWidth = 128; break;
|
||||
}
|
||||
switch ((data>>4) & 0x03)
|
||||
{
|
||||
case 0: NameTableHeight = 32; break;
|
||||
case 1: NameTableHeight = 64; break;
|
||||
case 2: NameTableHeight = 32; break; // invalid setting
|
||||
case 3: NameTableHeight = 128; break;
|
||||
}
|
||||
Console.WriteLine("Name Table Dimensions set to {0}x{1}", NameTableWidth, NameTableHeight);
|
||||
break;
|
||||
}
|
||||
Registers[register] = data;
|
||||
}
|
||||
|
||||
private void ProcessPalette(int slot)
|
||||
{
|
||||
byte r = PalXlatTable[(CRAM[slot] & 0x000F) >> 0];
|
||||
byte g = PalXlatTable[(CRAM[slot] & 0x00F0) >> 4];
|
||||
byte b = PalXlatTable[(CRAM[slot] & 0x0F00) >> 8];
|
||||
Palette[slot] = Colors.ARGB(r, g, b);
|
||||
}
|
||||
|
||||
private void UpdatePatternBuffer(int addr)
|
||||
{
|
||||
PatternBuffer[(addr*2) + 1] = (byte) (VRAM[addr^1] & 0x0F);
|
||||
PatternBuffer[(addr*2) + 0] = (byte) (VRAM[addr^1] >> 4);
|
||||
}
|
||||
|
||||
public int[] GetVideoBuffer()
|
||||
{
|
||||
return FrameBuffer;
|
||||
}
|
||||
|
||||
public int BufferWidth
|
||||
{
|
||||
get { return FrameWidth; }
|
||||
}
|
||||
|
||||
public int BufferHeight
|
||||
{
|
||||
get { return 224; }
|
||||
}
|
||||
|
||||
public int BackgroundColor
|
||||
{
|
||||
get { return Palette[Registers[7] & 0x3F]; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
namespace BizHawk.Emulation.Consoles.Sega
|
||||
{
|
||||
public partial class Genesis
|
||||
{
|
||||
public static readonly ControllerDefinition GenesisController = new ControllerDefinition
|
||||
{
|
||||
Name = "Genesis 3-Button Controller",
|
||||
BoolButtons =
|
||||
{
|
||||
"Reset",
|
||||
"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 A", "P1 B", "P1 C", "P1 Start"
|
||||
}
|
||||
};
|
||||
|
||||
public ControllerDefinition ControllerDefinition { get { return GenesisController; } }
|
||||
public IController Controller { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using BizHawk.Emulation.CPUs.M68K;
|
||||
using BizHawk.Emulation.CPUs.Z80;
|
||||
using BizHawk.Emulation.Sound;
|
||||
using MC68000;
|
||||
|
||||
namespace BizHawk.Emulation.Consoles.Sega
|
||||
{
|
||||
public sealed partial class Genesis : IEmulator, IMemoryController
|
||||
{
|
||||
// ROM
|
||||
public byte[] RomData;
|
||||
|
||||
// Machine stuff
|
||||
public MC68K MainCPU; // TODO un-static
|
||||
public M68000 _MainCPU;
|
||||
public Z80A SoundCPU;
|
||||
public GenVDP VDP;
|
||||
public SN76489 PSG;
|
||||
public YM2612 YM2612;
|
||||
public byte[] Ram = new byte[0x10000];
|
||||
public byte[] Z80Ram = new byte[0x2000];
|
||||
|
||||
private bool M68000HasZ80Bus = false;
|
||||
private bool Z80Reset = false;
|
||||
private bool Z80Runnable { get { return (Z80Reset == false && M68000HasZ80Bus == false); } }
|
||||
|
||||
private SoundMixer SoundMixer;
|
||||
|
||||
// Genesis timings:
|
||||
// 53,693,175 Machine clocks / sec
|
||||
// 7,670,454 Main 68000 cycles / sec (7 mclk divisor)
|
||||
// 3,579,545 Z80 cycles / sec (15 mclk divisor)
|
||||
|
||||
// At 59.92 FPS:
|
||||
// 896,081 mclks / frame
|
||||
// 128,011 Main 68000 cycles / frame
|
||||
// 59,738 Z80 cycles / frame
|
||||
|
||||
// At 262 lines/frame:
|
||||
// 3420 mclks / line
|
||||
// ~ 488.5 Main 68000 cycles / line
|
||||
// 228 Z80 cycles / line
|
||||
|
||||
// Video characteristics:
|
||||
// 224 lines are active display. The remaining 38 lines are vertical blanking.
|
||||
// In H40 mode, the dot clock is 480 pixels per line.
|
||||
// 320 are active display, the remaining 160 are horizontal blanking.
|
||||
// A total of 3420 mclks per line, but 2560 mclks are active display and 860 mclks are blanking.
|
||||
|
||||
public Genesis(bool sega360)
|
||||
{
|
||||
if (sega360) MainCPU = new MC68K(this);
|
||||
_MainCPU = new M68000();
|
||||
SoundCPU = new Z80A();
|
||||
YM2612 = new YM2612();
|
||||
PSG = new SN76489();
|
||||
VDP = new GenVDP();
|
||||
VDP.DmaReadFrom68000 = ReadW;
|
||||
SoundMixer = new SoundMixer(YM2612, PSG);
|
||||
|
||||
_MainCPU.ReadByte = ReadB;
|
||||
_MainCPU.ReadWord = ReadW;
|
||||
_MainCPU.ReadLong = ReadL;
|
||||
_MainCPU.WriteByte = WriteB;
|
||||
_MainCPU.WriteWord = WriteW;
|
||||
_MainCPU.WriteLong = WriteL;
|
||||
|
||||
SoundCPU.ReadMemory = ReadMemoryZ80;
|
||||
SoundCPU.WriteMemory = WriteMemoryZ80;
|
||||
SoundCPU.WriteHardware = (a, v) => { Console.WriteLine("Z80: Attempt I/O Write {0:X2}:{1:X2}", a, v); };
|
||||
SoundCPU.ReadHardware = x => 0xFF;
|
||||
SoundCPU.IRQCallback = () => SoundCPU.Interrupt = false;
|
||||
Z80Reset = true;
|
||||
}
|
||||
|
||||
public void LoadGame(IGame game)
|
||||
{
|
||||
RomData = new byte[0x400000];
|
||||
byte[] rom = game.GetRomData();
|
||||
for (int i = 0; i < rom.Length; i++)
|
||||
RomData[i] = rom[i];
|
||||
|
||||
if (MainCPU != null) MainCPU.Reset();
|
||||
_MainCPU.Reset();
|
||||
}
|
||||
|
||||
public void StepMine()
|
||||
{
|
||||
_MainCPU.Exec();
|
||||
}
|
||||
|
||||
public void StepHis()
|
||||
{
|
||||
MainCPU.Step();
|
||||
}
|
||||
|
||||
public void FrameAdvance(bool render)
|
||||
{
|
||||
Frame++;
|
||||
PSG.BeginFrame(SoundCPU.TotalExecutedCycles);
|
||||
for (VDP.ScanLine = 0; VDP.ScanLine < 262; VDP.ScanLine++)
|
||||
{
|
||||
Console.WriteLine("Frame {0} ScanLine {1}", Frame, VDP.ScanLine);
|
||||
|
||||
if (VDP.ScanLine < 224)
|
||||
VDP.RenderLine();
|
||||
|
||||
MainCPU.Execute(488);
|
||||
if (Z80Runnable)
|
||||
{
|
||||
//Console.WriteLine("running z80");
|
||||
SoundCPU.ExecuteCycles(228);
|
||||
SoundCPU.Interrupt = false;
|
||||
}
|
||||
|
||||
if (VDP.ScanLine == 224)
|
||||
{
|
||||
// End-frame stuff
|
||||
if (VDP.VInterruptEnabled)
|
||||
MainCPU.Interrupt(6);
|
||||
|
||||
if (Z80Runnable)
|
||||
SoundCPU.Interrupt = true;
|
||||
}
|
||||
}
|
||||
PSG.EndFrame(SoundCPU.TotalExecutedCycles);
|
||||
}
|
||||
|
||||
public IVideoProvider VideoProvider
|
||||
{
|
||||
get { return VDP; }
|
||||
}
|
||||
|
||||
public ISoundProvider SoundProvider
|
||||
{
|
||||
get { return SoundMixer; }
|
||||
}
|
||||
|
||||
public int Frame { get; set; }
|
||||
public bool DeterministicEmulation { get; set; }
|
||||
|
||||
public void HardReset()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public byte[] SaveRam
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public bool SaveRamModified
|
||||
{
|
||||
get
|
||||
{
|
||||
return false; // TODO implement
|
||||
}
|
||||
set
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveStateText(TextWriter writer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void LoadStateText(TextReader reader)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SaveStateBinary(BinaryWriter writer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader reader)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public byte[] SaveStateBinary()
|
||||
{
|
||||
return new byte[0];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
namespace BizHawk.Emulation.Consoles.Sega
|
||||
{
|
||||
public partial class Genesis
|
||||
{
|
||||
// todo ???????
|
||||
public bool SegaCD = false;
|
||||
|
||||
public int ReadIO(int offset)
|
||||
{
|
||||
int value;
|
||||
switch (offset)
|
||||
{
|
||||
case 0: // version
|
||||
value = SegaCD ? 0x00 : 0x20;
|
||||
switch((char)RomData[0x01F0])
|
||||
{
|
||||
case 'J': value |= 0x00; break;
|
||||
case 'U': value |= 0x80; break;
|
||||
case 'E': value |= 0xC0; break;
|
||||
case 'A': value |= 0xC0; break;
|
||||
case '4': value |= 0x80; break;
|
||||
default: value |= 0x80; break;
|
||||
}
|
||||
value |= 1; // US
|
||||
return value;
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
public void WriteIO(int offset, int value)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,208 @@
|
|||
using System;
|
||||
|
||||
namespace BizHawk.Emulation.Consoles.Sega
|
||||
{
|
||||
public partial class Genesis
|
||||
{
|
||||
private byte ReadByte(uint address) { return (byte) ReadB((int)address); }
|
||||
private ushort ReadWord(uint address) { return (ushort) ReadW((int)address); }
|
||||
private uint ReadLong(uint address) { return (uint) ReadL((int)address); }
|
||||
|
||||
private void WriteByte(uint address, byte value) { WriteB((int)address, (sbyte)value); }
|
||||
private void WriteWord(uint address, ushort value) { WriteW((int)address, (short)value); }
|
||||
private void WriteLong(uint address, uint value) { WriteL((int)address, (int) value); }
|
||||
|
||||
public sbyte ReadB(int address)
|
||||
{
|
||||
address &= 0x00FFFFFF;
|
||||
|
||||
if (address < 0x400000)
|
||||
return (sbyte) RomData[address];
|
||||
if (address >= 0xA10000 && address < 0xA1001F)
|
||||
return (sbyte) ReadIO((address >> 1) & 0x0F);
|
||||
|
||||
if (address >= 0xE00000)
|
||||
return (sbyte) Ram[address & 0xFFFF];
|
||||
|
||||
if (address == 0xA11100) // Z80 BUS status
|
||||
{
|
||||
Console.WriteLine("QUERY z80 bus status. 68000 can access? " + (M68000HasZ80Bus && Z80Reset == false));
|
||||
return (sbyte) (M68000HasZ80Bus && Z80Reset == false ? 0 : 1);
|
||||
}
|
||||
|
||||
if ((address & 0xFF0000) == 0xA00000)
|
||||
{
|
||||
return (sbyte) ReadMemoryZ80((ushort) (address & 0x7FFF));
|
||||
}
|
||||
|
||||
if (address >= 0xC00004 && address < 0xC00008)
|
||||
{
|
||||
ushort value = VDP.ReadVdpControl();
|
||||
if ((address & 1) == 0) // read MSB
|
||||
return (sbyte) (value >> 8);
|
||||
return (sbyte) value; // read LSB
|
||||
}
|
||||
|
||||
Console.WriteLine("UNHANDLED READB {0:X6}", address);
|
||||
return 0x7D;
|
||||
}
|
||||
|
||||
public short ReadW(int address)
|
||||
{
|
||||
address &= 0x00FFFFFF;
|
||||
|
||||
int maskedAddr;
|
||||
if (address < 0x400000)
|
||||
return (short)((RomData[address] << 8) | RomData[address + 1]);
|
||||
|
||||
if (address >= 0xE00000) // Work RAM
|
||||
{
|
||||
maskedAddr = address & 0xFFFF;
|
||||
return (short)((Ram[maskedAddr] << 8) | Ram[maskedAddr + 1]);
|
||||
}
|
||||
|
||||
if (address >= 0xC00004 && address < 0xC00008)
|
||||
return (short) VDP.ReadVdpControl();
|
||||
|
||||
Console.WriteLine("UNHANDLED READW {0:X6}", address);
|
||||
return 0x7DCD;
|
||||
}
|
||||
|
||||
public int ReadL(int address)
|
||||
{
|
||||
address &= 0x00FFFFFF;
|
||||
|
||||
int maskedAddr;
|
||||
if (address < 0x400000) // Cartridge ROM
|
||||
return (RomData[address] << 24) | (RomData[address + 1] << 16) | (RomData[address + 2] << 8) | RomData[address + 3];
|
||||
|
||||
if (address >= 0xE00000) // Work RAM
|
||||
{
|
||||
maskedAddr = address & 0xFFFF;
|
||||
return (Ram[maskedAddr] << 24) | (Ram[maskedAddr + 1] << 16) | (Ram[maskedAddr + 2] << 8) | Ram[maskedAddr + 3];
|
||||
}
|
||||
Console.WriteLine("UNHANDLED READL {0:X6}", address);
|
||||
return 0x7DCDCDCD;
|
||||
}
|
||||
|
||||
public void WriteB(int address, sbyte value)
|
||||
{
|
||||
address &= 0x00FFFFFF;
|
||||
|
||||
if (address >= 0xE00000) // Work RAM
|
||||
{
|
||||
Console.WriteLine("MEM[{0:X4}] change from {1:X2} to {2:X2}", address & 0xFFFF, Ram[address & 0xFFFF], value);
|
||||
Ram[address & 0xFFFF] = (byte)value;
|
||||
return;
|
||||
}
|
||||
if ((address & 0xFF0000) == 0xA00000)
|
||||
{
|
||||
WriteMemoryZ80((ushort)(address & 0x7FFF), (byte)value);
|
||||
return;
|
||||
}
|
||||
if (address == 0xA11100)
|
||||
{
|
||||
M68000HasZ80Bus = (value & 1) != 0;
|
||||
Console.WriteLine("68000 has the z80 bus: " + M68000HasZ80Bus);
|
||||
return;
|
||||
}
|
||||
if (address == 0xA11200) // Z80 RESET
|
||||
{
|
||||
Z80Reset = (value & 1) == 0;
|
||||
if (Z80Reset)
|
||||
SoundCPU.Reset();
|
||||
Console.WriteLine("z80 reset: " + Z80Reset);
|
||||
return;
|
||||
}
|
||||
if (address >= 0xC00000)
|
||||
{
|
||||
// when writing to VDP in byte mode, the LSB is duplicated into the MSB
|
||||
switch (address & 0x1F)
|
||||
{
|
||||
case 0x00:
|
||||
case 0x02:
|
||||
VDP.WriteVdpData((ushort) (value | (value << 8)));
|
||||
return;
|
||||
case 0x04:
|
||||
case 0x06:
|
||||
VDP.WriteVdpControl((ushort) (value | (value << 8)));
|
||||
return;
|
||||
case 0x11:
|
||||
case 0x13:
|
||||
case 0x15:
|
||||
case 0x17:
|
||||
Console.WriteLine("!+!+!+ PSG WRITE => {0:X2}",value);
|
||||
PSG.WritePsgData((byte) value, SoundCPU.TotalExecutedCycles);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine("UNHANDLED WRITEB {0:X6}:{1:X2}", address, value);
|
||||
}
|
||||
|
||||
public void WriteW(int address, short value)
|
||||
{
|
||||
address &= 0x00FFFFFF;
|
||||
|
||||
if (address >= 0xE00000) // Work RAM
|
||||
{
|
||||
Console.WriteLine("MEM[{0:X4}] change to {1:X4}", address & 0xFFFF, value);
|
||||
Ram[(address & 0xFFFF) + 0] = (byte)(value >> 8);
|
||||
Ram[(address & 0xFFFF) + 1] = (byte)value;
|
||||
return;
|
||||
}
|
||||
if (address >= 0xC00000)
|
||||
{
|
||||
switch (address & 0x1F)
|
||||
{
|
||||
case 0x00:
|
||||
case 0x02:
|
||||
VDP.WriteVdpData((ushort)value);
|
||||
return;
|
||||
case 0x04:
|
||||
case 0x06:
|
||||
VDP.WriteVdpControl((ushort)value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (address == 0xA11100) // Z80 BUSREQ
|
||||
{
|
||||
M68000HasZ80Bus = (value & 0x100) != 0;
|
||||
Console.WriteLine("68000 has the z80 bus: " + M68000HasZ80Bus);
|
||||
return;
|
||||
}
|
||||
if (address == 0xA11200) // Z80 RESET
|
||||
{
|
||||
Z80Reset = (value & 0x100) == 0;
|
||||
if (Z80Reset)
|
||||
SoundCPU.Reset();
|
||||
Console.WriteLine("z80 reset: " + Z80Reset);
|
||||
return;
|
||||
}
|
||||
Console.WriteLine("UNHANDLED WRITEW {0:X6}:{1:X4}", address, value);
|
||||
}
|
||||
|
||||
public void WriteL(int address, int value)
|
||||
{
|
||||
address &= 0x00FFFFFF;
|
||||
|
||||
if (address >= 0xE00000) // Work RAM
|
||||
{
|
||||
Console.WriteLine("MEM[{0:X4}] change to {1:X8}", address & 0xFFFF, value);
|
||||
Ram[(address & 0xFFFF) + 0] = (byte)(value >> 24);
|
||||
Ram[(address & 0xFFFF) + 1] = (byte)(value >> 16);
|
||||
Ram[(address & 0xFFFF) + 2] = (byte)(value >> 8);
|
||||
Ram[(address & 0xFFFF) + 3] = (byte)value;
|
||||
return;
|
||||
}
|
||||
if (address >= 0xC00000)
|
||||
{
|
||||
WriteW(address, (short)(value >> 16));
|
||||
WriteW(address, (short)value);
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine("UNHANDLED WRITEL {0:X6}:{1:X8}", address, value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
using System;
|
||||
|
||||
namespace BizHawk.Emulation.Consoles.Sega
|
||||
{
|
||||
public partial class Genesis
|
||||
{
|
||||
private int BankRegion;
|
||||
|
||||
public byte ReadMemoryZ80(ushort address)
|
||||
{
|
||||
if (address < 0x4000)
|
||||
{
|
||||
//Console.WriteLine("read z80 memory {0:X4}: {1:X2}",address, Z80Ram[address & 0x1FFF]);
|
||||
return Z80Ram[address & 0x1FFF];
|
||||
}
|
||||
if (address >= 0x4000 && address < 0x6000)
|
||||
{
|
||||
//Console.WriteLine(" === Z80 READS FM STATUS ===");
|
||||
return YM2612.ReadStatus();
|
||||
}
|
||||
if (address >= 0x8000)
|
||||
{
|
||||
// 68000 Bank region
|
||||
return (byte) ReadB(BankRegion | (address & 0x7FFF));
|
||||
}
|
||||
Console.WriteLine("UNHANDLED Z80 READ {0:X4}",address);
|
||||
return 0xCD;
|
||||
}
|
||||
|
||||
public void WriteMemoryZ80(ushort address, byte value)
|
||||
{
|
||||
if (address < 0x4000)
|
||||
{
|
||||
//Console.WriteLine("write z80 memory {0:X4}: {1:X2}",address, value);
|
||||
Z80Ram[address & 0x1FFF] = value;
|
||||
return;
|
||||
}
|
||||
if (address >= 0x4000 && address < 0x6000)
|
||||
{
|
||||
//Console.WriteLine(" === Z80 WRITES Z80 {0:X4}:{1:X2} ===",address, value);
|
||||
YM2612.Write(address & 3, value);
|
||||
return;
|
||||
}
|
||||
if (address == 0x6000)
|
||||
{
|
||||
BankRegion >>= 1;
|
||||
BankRegion |= (value & 1) << 23;
|
||||
BankRegion &= 0x00FF8000;
|
||||
Console.WriteLine("Bank pointing at {0:X8}",BankRegion);
|
||||
return;
|
||||
}
|
||||
if (address >= 0x8000)
|
||||
{
|
||||
WriteB(BankRegion | (address & 0x7FFF), (sbyte) value);
|
||||
return;
|
||||
}
|
||||
Console.WriteLine("UNHANDLED Z80 WRITE {0:X4}:{1:X2}", address, value);
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,20 @@
|
|||
======= Sega MasterSystem Compatibility Issues =======
|
||||
|
||||
* CodeMasters games don't work:
|
||||
+ CodeMasters games use a custom mapper (implemented now).
|
||||
+ CodeMasters games use alternate video mode, not implemented.
|
||||
+ Games are: Cosmic Spacehead, Fantastic Dizzy, Micro Machines
|
||||
|
||||
* F16 Fighting Falcon uses old SG-1000 video mode, and doesn't work on a GG/Genesis either.
|
||||
|
||||
That said.. I will fix CodeMasters games and F16 Fighting Falcon eventually, and add full
|
||||
SG-1000 support, it's just not the high priority currently.
|
||||
|
||||
* Raster effects are sometimes on the wrong line... but it's correct 99% of the time... :|
|
||||
Update: I think this is a timing issue resulting in latching/rendering at the wrong part
|
||||
of a scanline. I should play with this timing.
|
||||
|
||||
======= Game Gear compatibility issues =======
|
||||
|
||||
* Axe Battler is broken.
|
||||
* "SEGA" logo audio sample plays at low rate in Sonic & Tails.
|
|
@ -0,0 +1,77 @@
|
|||
namespace BizHawk.Emulation.Consoles.Sega
|
||||
{
|
||||
public partial class SMS
|
||||
{
|
||||
public static readonly ControllerDefinition SmsController = new ControllerDefinition
|
||||
{
|
||||
Name = "SMS Controller",
|
||||
BoolButtons =
|
||||
{
|
||||
"Reset", "Pause",
|
||||
"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 B1", "P1 B2",
|
||||
"P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 B1", "P2 B2"
|
||||
}
|
||||
};
|
||||
|
||||
public ControllerDefinition ControllerDefinition { get { return SmsController; } }
|
||||
public IController Controller { get; set; }
|
||||
|
||||
private byte ReadControls1()
|
||||
{
|
||||
byte value = 0xFF;
|
||||
|
||||
if (Controller["P1 Up"]) value &= 0xFE;
|
||||
if (Controller["P1 Down"]) value &= 0xFD;
|
||||
if (Controller["P1 Left"]) value &= 0xFB;
|
||||
if (Controller["P1 Right"]) value &= 0xF7;
|
||||
if (Controller["P1 B1"]) value &= 0xEF;
|
||||
if (Controller["P1 B2"]) value &= 0xDF;
|
||||
|
||||
if (Controller["P2 Up"]) value &= 0xBF;
|
||||
if (Controller["P2 Down"]) value &= 0x7F;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private byte ReadControls2()
|
||||
{
|
||||
byte value = 0xFF;
|
||||
|
||||
if (Controller["P2 Left"]) value &= 0xFE;
|
||||
if (Controller["P2 Right"]) value &= 0xFD;
|
||||
if (Controller["P2 B1"]) value &= 0xFB;
|
||||
if (Controller["P2 B2"]) value &= 0xF7;
|
||||
|
||||
if (Controller["Reset"]) value &= 0xEF;
|
||||
|
||||
if ((Port3F & 0x0F) == 5)
|
||||
{
|
||||
if (region == "Japan")
|
||||
{
|
||||
value &= 0x3F;
|
||||
}
|
||||
else // US / Europe
|
||||
{
|
||||
if (Port3F >> 4 == 0x0F)
|
||||
value |= 0xC0;
|
||||
else
|
||||
value &= 0x3F;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private byte ReadPort0()
|
||||
{
|
||||
if (IsGameGear == false)
|
||||
return 0xFF;
|
||||
byte value = 0xFF;
|
||||
if (Controller["Pause"])
|
||||
value ^= 0x80;
|
||||
if (Region == "US")
|
||||
value ^= 0x40;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
namespace BizHawk.Emulation.Consoles.Sega
|
||||
{
|
||||
public partial class SMS
|
||||
{
|
||||
// The CodeMasters mapper has 3 banks of 16kb, like the Sega mapper.
|
||||
// The differences are that the paging control addresses are different, and the first 1K of ROM is not protected.
|
||||
// Bank 0: Control Address $0000 - Maps $0000 - $3FFF
|
||||
// Bank 1: Control Address $4000 - Maps $4000 - $7FFF
|
||||
// Bank 2: Control Address $8000 - Maps $8000 - $BFFF
|
||||
// System RAM is at $C000+ as in the Sega mapper.
|
||||
|
||||
private byte ReadMemoryCM(ushort address)
|
||||
{
|
||||
if (address < BankSize * 1) return RomData[(RomBank0 * BankSize) + address];
|
||||
if (address < BankSize * 2) return RomData[(RomBank1 * BankSize) + (address & BankSizeMask)];
|
||||
if (address < BankSize * 3) return RomData[(RomBank2 * BankSize) + (address & BankSizeMask)];
|
||||
|
||||
return SystemRam[address & RamSizeMask];
|
||||
}
|
||||
|
||||
private void WriteMemoryCM(ushort address, byte value)
|
||||
{
|
||||
if (address >= 0xC000)
|
||||
SystemRam[address & RamSizeMask] = value;
|
||||
|
||||
else if (address == 0x0000) RomBank0 = (byte)(value % RomBanks);
|
||||
else if (address == 0x4000) RomBank1 = (byte)(value % RomBanks);
|
||||
else if (address == 0x8000) RomBank2 = (byte)(value % RomBanks);
|
||||
}
|
||||
|
||||
private void InitCodeMastersMapper()
|
||||
{
|
||||
Cpu.ReadMemory = ReadMemoryCM;
|
||||
Cpu.WriteMemory = WriteMemoryCM;
|
||||
WriteMemoryCM(0x0000, 0);
|
||||
WriteMemoryCM(0x4000, 1);
|
||||
WriteMemoryCM(0x8000, 2);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
namespace BizHawk.Emulation.Consoles.Sega
|
||||
{
|
||||
public partial class SMS
|
||||
{
|
||||
// The Sega memory mapper layout looks like so:
|
||||
// $0000-$03FF - ROM (unpaged)
|
||||
// $0400-$3FFF - ROM mapper slot 0
|
||||
// $4000-$7FFF - ROM mapper slot 1
|
||||
// $8000-$BFFF - ROM mapper slot 2 - OR - SaveRAM
|
||||
// $C000-$DFFF - System RAM
|
||||
// $E000-$FFFF - System RAM (mirror)
|
||||
// $FFFC - SaveRAM mapper control
|
||||
// $FFFD - Mapper slot 0 control
|
||||
// $FFFE - Mapper slot 1 control
|
||||
// $FFFF - Mapper slot 2 control
|
||||
|
||||
private const ushort BankSizeMask = 0x3FFF;
|
||||
private const ushort RamSizeMask = 0x1FFF;
|
||||
|
||||
private byte ReadMemory(ushort address)
|
||||
{
|
||||
if (address < 1024)
|
||||
return RomData[address];
|
||||
if (address < BankSize)
|
||||
return RomData[(RomBank0*BankSize) + address];
|
||||
if (address < BankSize * 2)
|
||||
return RomData[(RomBank1*BankSize) + (address & BankSizeMask)];
|
||||
if (address < BankSize * 3)
|
||||
{
|
||||
switch (SaveRamBank)
|
||||
{
|
||||
case 0: return RomData[(RomBank2*BankSize) + (address & BankSizeMask)];
|
||||
case 1: return SaveRAM[address & BankSizeMask];
|
||||
case 2: return SaveRAM[BankSize + (address & BankSizeMask)];
|
||||
}
|
||||
}
|
||||
return SystemRam[address & RamSizeMask];
|
||||
}
|
||||
|
||||
private void WriteMemory(ushort address, byte value)
|
||||
{
|
||||
if (address >= 0xC000)
|
||||
SystemRam[address & RamSizeMask] = value;
|
||||
|
||||
else if (address >= 0x8000)
|
||||
{
|
||||
SaveRamModified = true;
|
||||
switch (SaveRamBank)
|
||||
{
|
||||
case 1: SaveRAM[address & BankSizeMask] = value; return;
|
||||
case 2: SaveRAM[BankSize + (address & BankSizeMask)] = value; return;
|
||||
}
|
||||
}
|
||||
|
||||
if (address >= 0xFFFC)
|
||||
{
|
||||
if (address == 0xFFFC)
|
||||
{
|
||||
if ((value & 8) != 0)
|
||||
SaveRamBank = (byte)((value & 4) == 0 ? 1 : 2); // SaveRAM selected
|
||||
else
|
||||
SaveRamBank = 0; // ROM bank selected
|
||||
}
|
||||
else if (address == 0xFFFD) RomBank0 = (byte)(value % RomBanks);
|
||||
else if (address == 0xFFFE) RomBank1 = (byte)(value % RomBanks);
|
||||
else if (address == 0xFFFF) RomBank2 = (byte)(value % RomBanks);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void InitSegaMapper()
|
||||
{
|
||||
Cpu.ReadMemory = ReadMemory;
|
||||
Cpu.WriteMemory = WriteMemory;
|
||||
WriteMemory(0xFFFC, 0);
|
||||
WriteMemory(0xFFFD, 0);
|
||||
WriteMemory(0xFFFE, 1);
|
||||
WriteMemory(0xFFFF, 2);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,398 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BizHawk.Emulation.CPUs.Z80;
|
||||
using BizHawk.Emulation.Sound;
|
||||
|
||||
/*****************************************************
|
||||
|
||||
VDP:
|
||||
+ Double Size Sprites
|
||||
+ HCounter
|
||||
+ Possibly work on VDP tests, but they don't really affect games.
|
||||
- Other video modes / CodeMasters 224 lines / old SG-1000 video modes.
|
||||
|
||||
GENERAL:
|
||||
+ Debug some GameGear game issues.
|
||||
+ Port 3F emulation (Japanese BIOS)
|
||||
+ Try to clean up the organization of the source code.
|
||||
|
||||
**********************************************************/
|
||||
|
||||
namespace BizHawk.Emulation.Consoles.Sega
|
||||
{
|
||||
public enum DisplayType { NTSC, PAL }
|
||||
|
||||
public sealed partial class SMS : IEmulator
|
||||
{
|
||||
// Constants
|
||||
public const int BankSize = 16384;
|
||||
|
||||
// ROM
|
||||
public byte[] RomData;
|
||||
public byte RomBank0, RomBank1, RomBank2;
|
||||
public byte RomBanks;
|
||||
public string[] Options;
|
||||
|
||||
// SaveRAM
|
||||
public byte[] SaveRAM = new byte[BankSize*2];
|
||||
public byte SaveRamBank;
|
||||
|
||||
public byte[] SaveRam { get { return SaveRAM; } }
|
||||
public bool SaveRamModified { get; set; }
|
||||
|
||||
// Machine resources
|
||||
public Z80A Cpu;
|
||||
public byte[] SystemRam;
|
||||
public VDP Vdp;
|
||||
public SN76489 PSG;
|
||||
public YM2413 YM2413;
|
||||
public SoundMixer SoundMixer;
|
||||
public bool IsGameGear = false;
|
||||
|
||||
public bool HasYM2413 = false;
|
||||
public int IPeriod = 228;
|
||||
|
||||
public int Frame { get; set; }
|
||||
private byte Port01 = 0xFF;
|
||||
private byte Port02 = 0xFF;
|
||||
private byte Port3F = 0xFF;
|
||||
|
||||
private int framesPerSecond;
|
||||
private int scanlinesPerFrame;
|
||||
|
||||
private DisplayType displayType;
|
||||
public DisplayType DisplayType
|
||||
{
|
||||
get { return displayType; }
|
||||
set
|
||||
{
|
||||
displayType = value;
|
||||
if (displayType == DisplayType.NTSC)
|
||||
{
|
||||
framesPerSecond = 60;
|
||||
scanlinesPerFrame = 262;
|
||||
} else // PAL
|
||||
{
|
||||
framesPerSecond = 50;
|
||||
scanlinesPerFrame = 313;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool DeterministicEmulation { get; set; }
|
||||
|
||||
public void Init()
|
||||
{
|
||||
DisplayType = DisplayType.NTSC;
|
||||
if (Controller == null)
|
||||
Controller = NullController.GetNullController();
|
||||
|
||||
Cpu = new Z80A();
|
||||
Cpu.ReadHardware = ReadPort;
|
||||
Cpu.WriteHardware = WritePort;
|
||||
|
||||
Vdp = new VDP(IsGameGear ? VdpMode.GameGear : VdpMode.SMS);
|
||||
PSG = new SN76489();
|
||||
YM2413 = new YM2413();
|
||||
SoundMixer = new SoundMixer(PSG, YM2413);
|
||||
|
||||
ActiveSoundProvider = PSG;
|
||||
HardReset();
|
||||
//PSG.StereoPanning = 0xDA;
|
||||
}
|
||||
|
||||
public void HardReset()
|
||||
{
|
||||
Cpu.Reset();
|
||||
Cpu.RegisterSP = 0xDFF0;
|
||||
Vdp = new VDP(Vdp.VdpMode);
|
||||
PSG.Reset();
|
||||
YM2413.Reset();
|
||||
SystemRam = new byte[0x2000];
|
||||
|
||||
if (Options.Contains("CMMapper") == false)
|
||||
InitSegaMapper();
|
||||
else
|
||||
InitCodeMastersMapper();
|
||||
}
|
||||
|
||||
public void LoadGame(IGame game)
|
||||
{
|
||||
RomData = game.GetRomData();
|
||||
RomBanks = (byte)(RomData.Length/BankSize);
|
||||
Options = game.GetOptions();
|
||||
foreach (string option in Options)
|
||||
{
|
||||
var args = option.Split('=');
|
||||
if (option == "FM") HasYM2413 = true;
|
||||
else if (args[0] == "IPeriod") IPeriod = int.Parse(args[1]);
|
||||
else if (args[0] == "Japan") Region = "Japan";
|
||||
}
|
||||
|
||||
Init();
|
||||
|
||||
// In Outrun FM mode, when you start a race, the PSG noise channel is left on until a "wheel squeeling"
|
||||
// sound effect occurs, and this happens again every "extended play" checkpoint. Every emulator I have tested
|
||||
// does this; if it's a bug, it is a bug that no emulator gets right.
|
||||
// In any case, it is annoying, so this turns it off.
|
||||
// My view is that because I believe it to be a game bug (not an emulation bug), this doesn't count as a game-specific hack :)
|
||||
|
||||
if (HasYM2413 && game.Name.StartsWith("OutRun", StringComparison.InvariantCultureIgnoreCase))
|
||||
SoundMixer.DisableSource(PSG);
|
||||
|
||||
// TODO: at some point we need a proper Game Library construct with various metadata about emulation hints and options for games and roms.
|
||||
}
|
||||
|
||||
public byte ReadPort(ushort port)
|
||||
{
|
||||
switch (port & 0xFF)
|
||||
{
|
||||
case 0x00: return ReadPort0();
|
||||
case 0x01: return Port01;
|
||||
case 0x02: return Port02;
|
||||
case 0x03: return 0x00;
|
||||
case 0x04: return 0xFF;
|
||||
case 0x05: return 0x00;
|
||||
case 0x06: return 0xFF;
|
||||
case 0x7E: return Vdp.ReadVLineCounter();
|
||||
case 0x7F: break; // hline counter TODO
|
||||
case 0xBE: return Vdp.ReadVram();
|
||||
case 0xBF: Cpu.Interrupt = false; return Vdp.ReadVdpStatus();
|
||||
case 0xC0:
|
||||
case 0xDC: return ReadControls1();
|
||||
case 0xC1:
|
||||
case 0xDD: return ReadControls2();
|
||||
case 0xF2:
|
||||
if (HasYM2413)
|
||||
{
|
||||
ActiveSoundProvider = SoundMixer;
|
||||
return YM2413.DetectionValue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
public void WritePort(ushort port, byte value)
|
||||
{
|
||||
switch (port & 0xFF)
|
||||
{
|
||||
case 0x01: Port01 = value; break;
|
||||
case 0x02: Port02 = value; break;
|
||||
case 0x06: PSG.StereoPanning = value; break;
|
||||
case 0x3F: Port3F = value; break;
|
||||
case 0x7E:
|
||||
case 0x7F: PSG.WritePsgData(value, Cpu.TotalExecutedCycles); break;
|
||||
case 0xBE: Vdp.WriteVdpData(value); break;
|
||||
case 0xBD:
|
||||
case 0xBF: Vdp.WriteVdpRegister(value); break;
|
||||
case 0xF0: if (HasYM2413) YM2413.RegisterLatch = value; break;
|
||||
case 0xF1: if (HasYM2413) YM2413.Write(value); break;
|
||||
case 0xF2: if (HasYM2413) YM2413.DetectionValue = value; break;
|
||||
}
|
||||
}
|
||||
|
||||
private int lineIntLinesRemaining;
|
||||
|
||||
private void ProcessFrameInterrupt()
|
||||
{
|
||||
if (Vdp.ScanLine == 193)
|
||||
Vdp.StatusByte |= 0x80;
|
||||
|
||||
if ((Vdp.StatusByte & 0x80) != 0 && Vdp.EnableFrameInterrupts)
|
||||
Cpu.Interrupt = true;
|
||||
}
|
||||
|
||||
private void ProcessLineInterrupt()
|
||||
{
|
||||
if (Vdp.ScanLine <= 192)
|
||||
{
|
||||
if (lineIntLinesRemaining-- <= 0)
|
||||
{
|
||||
if (Vdp.EnableLineInterrupts)
|
||||
{
|
||||
Cpu.Interrupt = true;
|
||||
}
|
||||
lineIntLinesRemaining = Vdp.Registers[0x0A];
|
||||
}
|
||||
return;
|
||||
}
|
||||
// else we're outside the active display period
|
||||
lineIntLinesRemaining = Vdp.Registers[0x0A];
|
||||
}
|
||||
|
||||
public void FrameAdvance(bool render)
|
||||
{
|
||||
Controller.FrameNumber = Frame++;
|
||||
PSG.BeginFrame(Cpu.TotalExecutedCycles);
|
||||
|
||||
if (IsGameGear == false)
|
||||
Cpu.NonMaskableInterrupt = Controller["Pause"];
|
||||
|
||||
for (Vdp.ScanLine = 0; Vdp.ScanLine < scanlinesPerFrame; Vdp.ScanLine++)
|
||||
{
|
||||
ProcessFrameInterrupt();
|
||||
ProcessLineInterrupt();
|
||||
|
||||
if (Vdp.ScanLine < 192)
|
||||
Vdp.RenderCurrentScanline(render);
|
||||
|
||||
Cpu.ExecuteCycles(IPeriod);
|
||||
|
||||
if (Vdp.ScanLine == 192)
|
||||
Vdp.RenderBlankingRegions();
|
||||
}
|
||||
PSG.EndFrame(Cpu.TotalExecutedCycles);
|
||||
}
|
||||
|
||||
public void SaveStateText(TextWriter writer)
|
||||
{
|
||||
writer.WriteLine("[SMS]\n");
|
||||
Cpu.SaveStateText(writer);
|
||||
PSG.SaveStateText(writer);
|
||||
Vdp.SaveStateText(writer);
|
||||
|
||||
writer.WriteLine("Frame {0}", Frame);
|
||||
writer.WriteLine("Bank0 {0}", RomBank0);
|
||||
writer.WriteLine("Bank1 {0}", RomBank1);
|
||||
writer.WriteLine("Bank2 {0}", RomBank2);
|
||||
writer.Write("RAM ");
|
||||
SystemRam.SaveAsHex(writer);
|
||||
writer.WriteLine("Port01 {0:X2}", Port01);
|
||||
writer.WriteLine("Port02 {0:X2}", Port02);
|
||||
writer.WriteLine("Port3F {0:X2}", Port3F);
|
||||
int SaveRamLen = Util.SaveRamBytesUsed(SaveRAM);
|
||||
if (SaveRamLen > 0)
|
||||
{
|
||||
writer.Write("SaveRAM ");
|
||||
SaveRAM.SaveAsHex(writer, SaveRamLen);
|
||||
}
|
||||
if (HasYM2413)
|
||||
{
|
||||
writer.Write("FMRegs " );
|
||||
YM2413.opll.reg.SaveAsHex(writer);
|
||||
}
|
||||
writer.WriteLine("[/SMS]");
|
||||
}
|
||||
|
||||
public void LoadStateText(TextReader reader)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
string[] args = reader.ReadLine().Split(' ');
|
||||
if (args[0].Trim() == "") continue;
|
||||
if (args[0] == "[SMS]") continue;
|
||||
if (args[0] == "[/SMS]") break;
|
||||
if (args[0] == "Bank0")
|
||||
RomBank0 = byte.Parse(args[1]);
|
||||
else if (args[0] == "Bank1")
|
||||
RomBank1 = byte.Parse(args[1]);
|
||||
else if (args[0] == "Bank2")
|
||||
RomBank2 = byte.Parse(args[1]);
|
||||
else if (args[0] == "Frame")
|
||||
Frame = int.Parse(args[1]);
|
||||
else if (args[0] == "RAM")
|
||||
SystemRam.ReadFromHex(args[1]);
|
||||
else if (args[0] == "SaveRAM")
|
||||
{
|
||||
for (int i = 0; i < SaveRAM.Length; i++) SaveRAM[i] = 0;
|
||||
SaveRAM.ReadFromHex(args[1]);
|
||||
}
|
||||
else if (args[0] == "FMRegs")
|
||||
{
|
||||
byte[] regs = new byte[YM2413.opll.reg.Length];
|
||||
regs.ReadFromHex(args[1]);
|
||||
for (byte i=0; i<regs.Length; i++)
|
||||
YM2413.Write(i, regs[i]);
|
||||
}
|
||||
else if (args[0] == "Port01")
|
||||
Port01 = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "Port02")
|
||||
Port02 = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "Port3F")
|
||||
Port3F = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "[Z80]")
|
||||
Cpu.LoadStateText(reader);
|
||||
else if (args[0] == "[PSG]")
|
||||
PSG.LoadStateText(reader);
|
||||
else if (args[0] == "[VDP]")
|
||||
Vdp.LoadStateText(reader);
|
||||
else
|
||||
Console.WriteLine("Skipping unrecognized identifier " + args[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] SaveStateBinary()
|
||||
{
|
||||
var buf = new byte[24802 + 16384 + 16384];
|
||||
var stream = new MemoryStream(buf);
|
||||
var writer = new BinaryWriter(stream);
|
||||
SaveStateBinary(writer);
|
||||
writer.Close();
|
||||
return buf;
|
||||
}
|
||||
|
||||
public void SaveStateBinary(BinaryWriter writer)
|
||||
{
|
||||
Cpu.SaveStateBinary(writer);
|
||||
PSG.SaveStateBinary(writer);
|
||||
Vdp.SaveStateBinary(writer);
|
||||
|
||||
writer.Write(Frame);
|
||||
writer.Write(RomBank0);
|
||||
writer.Write(RomBank1);
|
||||
writer.Write(RomBank2);
|
||||
writer.Write(SystemRam);
|
||||
writer.Write(SaveRAM);
|
||||
writer.Write(Port01);
|
||||
writer.Write(Port02);
|
||||
writer.Write(Port3F);
|
||||
writer.Write(YM2413.opll.reg);
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader reader)
|
||||
{
|
||||
Cpu.LoadStateBinary(reader);
|
||||
PSG.LoadStateBinary(reader);
|
||||
Vdp.LoadStateBinary(reader);
|
||||
|
||||
Frame = reader.ReadInt32();
|
||||
RomBank0 = reader.ReadByte();
|
||||
RomBank1 = reader.ReadByte();
|
||||
RomBank2 = reader.ReadByte();
|
||||
SystemRam = reader.ReadBytes(SystemRam.Length);
|
||||
reader.Read(SaveRAM, 0, SaveRAM.Length);
|
||||
Port01 = reader.ReadByte();
|
||||
Port02 = reader.ReadByte();
|
||||
Port3F = reader.ReadByte();
|
||||
if (HasYM2413)
|
||||
{
|
||||
byte[] regs = new byte[YM2413.opll.reg.Length];
|
||||
reader.Read(regs, 0, regs.Length);
|
||||
for (byte i = 0; i < regs.Length; i++)
|
||||
YM2413.Write(i, regs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public IVideoProvider VideoProvider { get { return Vdp; } }
|
||||
|
||||
private ISoundProvider ActiveSoundProvider;
|
||||
public ISoundProvider SoundProvider { get { return ActiveSoundProvider; } }
|
||||
|
||||
private string region = "Export";
|
||||
public string Region
|
||||
{
|
||||
get { return region; }
|
||||
set
|
||||
{
|
||||
if (value.NotIn(validRegions))
|
||||
throw new Exception("Passed value "+value+" is not a valid region!");
|
||||
region = value;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly string[] validRegions = {"Export", "Japan"};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,515 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
|
||||
namespace BizHawk.Emulation.Consoles.Sega
|
||||
{
|
||||
public enum VdpCommand
|
||||
{
|
||||
VramRead,
|
||||
VramWrite,
|
||||
RegisterWrite,
|
||||
CramWrite
|
||||
}
|
||||
|
||||
public enum VdpMode
|
||||
{
|
||||
SMS,
|
||||
GameGear
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emulates the Texas Instruments TMS9918 VDP.
|
||||
/// </summary>
|
||||
public sealed class VDP : IVideoProvider
|
||||
{
|
||||
// VDP State
|
||||
public byte[] VRAM = new byte[0x4000]; //16kb video RAM
|
||||
public byte[] CRAM; // SMS = 32 bytes, GG = 64 bytes CRAM
|
||||
public byte[] Registers = new byte[] { 0x06, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xF0, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
public byte StatusByte;
|
||||
|
||||
private bool vdpWaitingForLatchByte = true;
|
||||
private byte vdpLatch;
|
||||
private byte vdpBuffer;
|
||||
private ushort vdpAddress;
|
||||
private VdpCommand vdpCommand;
|
||||
private ushort vdpAddressClamp;
|
||||
|
||||
private VdpMode mode;
|
||||
public VdpMode VdpMode { get { return mode; } }
|
||||
|
||||
public int ScanLine;
|
||||
|
||||
public int[] FrameBuffer = new int[256*192];
|
||||
public int[] GameGearFrameBuffer = new int[160*144];
|
||||
|
||||
// preprocessed state assist stuff.
|
||||
public int[] Palette = new int[32];
|
||||
|
||||
private static readonly byte[] SMSPalXlatTable = { 0, 85, 170, 255 };
|
||||
private static readonly byte[] GGPalXlatTable = { 0, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255 };
|
||||
|
||||
public bool ShiftSpritesLeft8Pixels { get { return (Registers[0] & 8) > 0; } }
|
||||
public bool EnableLineInterrupts { get { return (Registers[0] & 16) > 0; } }
|
||||
public bool LeftBlanking { get { return (Registers[0] & 32) > 0; } }
|
||||
public bool HorizScrollLock { get { return (Registers[0] & 64) > 0; } }
|
||||
public bool VerticalScrollLock { get { return (Registers[0] & 128) > 0; } }
|
||||
public bool DisplayOn { get { return (Registers[1] & 64) > 0; } }
|
||||
public bool EnableFrameInterrupts { get { return (Registers[1] & 32) > 0; } }
|
||||
public bool Enable8x16Sprites { get { return (Registers[1] & 2) > 0; } }
|
||||
public byte BackdropColor { get { return (byte) (16 + (Registers[7] & 15)); } }
|
||||
public int NameTableBase { get { return 1024 * (Registers[2] & 0x0E); } }
|
||||
public int SpriteAttributeTableBase { get { return ((Registers[5] >> 1) << 8) & 0x3FFF; } }
|
||||
public int SpriteTileBase { get { return (Registers[6] & 4) > 0 ? 256: 0; } }
|
||||
|
||||
private readonly byte[] VLineCounterTable =
|
||||
{
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
|
||||
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
|
||||
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
|
||||
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
|
||||
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
|
||||
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
|
||||
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
|
||||
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
|
||||
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
|
||||
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
|
||||
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA,
|
||||
0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
|
||||
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
|
||||
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
|
||||
};
|
||||
|
||||
public byte[] PatternBuffer = new byte[0x8000];
|
||||
|
||||
private byte[] ScanlinePriorityBuffer = new byte[256];
|
||||
private byte[] SpriteCollisionBuffer = new byte[256];
|
||||
|
||||
public VDP(VdpMode mode)
|
||||
{
|
||||
this.mode = mode;
|
||||
if (mode == VdpMode.SMS) CRAM = new byte[32];
|
||||
if (mode == VdpMode.GameGear) CRAM = new byte[64];
|
||||
}
|
||||
|
||||
public byte ReadVram()
|
||||
{
|
||||
vdpWaitingForLatchByte = true;
|
||||
byte value = vdpBuffer;
|
||||
vdpBuffer = VRAM[vdpAddress & vdpAddressClamp];
|
||||
vdpAddress++;
|
||||
return value;
|
||||
}
|
||||
|
||||
public byte ReadVdpStatus()
|
||||
{
|
||||
vdpWaitingForLatchByte = true;
|
||||
byte returnValue = StatusByte;
|
||||
StatusByte &= 0x1F;
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
public byte ReadVLineCounter()
|
||||
{
|
||||
return VLineCounterTable[ScanLine];
|
||||
}
|
||||
|
||||
public void WriteVdpRegister(byte value)
|
||||
{
|
||||
if (vdpWaitingForLatchByte)
|
||||
{
|
||||
vdpLatch = value;
|
||||
vdpWaitingForLatchByte = false;
|
||||
vdpAddress = (ushort)((vdpAddress & 0xFF00) | value);
|
||||
return;
|
||||
}
|
||||
|
||||
vdpWaitingForLatchByte = true;
|
||||
switch (value & 0xC0)
|
||||
{
|
||||
case 0x00: // read VRAM
|
||||
vdpCommand = VdpCommand.VramRead;
|
||||
vdpAddressClamp = 0x3FFF;
|
||||
vdpAddress = (ushort)(((value & 63) << 8) | vdpLatch);
|
||||
vdpBuffer = VRAM[vdpAddress & vdpAddressClamp];
|
||||
vdpAddress++;
|
||||
break;
|
||||
case 0x40: // write VRAM
|
||||
vdpCommand = VdpCommand.VramWrite;
|
||||
vdpAddressClamp = 0x3FFF;
|
||||
vdpAddress = (ushort)(((value & 63) << 8) | vdpLatch);
|
||||
break;
|
||||
case 0x80: // VDP register write
|
||||
Registers[value & 0x0F] = vdpLatch;
|
||||
break;
|
||||
case 0xC0: // write CRAM / modify palette
|
||||
vdpCommand = VdpCommand.CramWrite;
|
||||
vdpAddressClamp = (byte) (mode == VdpMode.SMS ? 0x1F : 0x3F);
|
||||
vdpAddress = (ushort)(((value & 63) << 8) | vdpLatch);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteVdpData(byte value)
|
||||
{
|
||||
vdpWaitingForLatchByte = true;
|
||||
vdpBuffer = value;
|
||||
if (vdpCommand == VdpCommand.CramWrite)
|
||||
{
|
||||
// Write Palette / CRAM
|
||||
CRAM[vdpAddress & vdpAddressClamp] = value;
|
||||
vdpAddress++;
|
||||
UpdatePrecomputedPalette();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Write VRAM and update pre-computed pattern buffer.
|
||||
UpdatePatternBuffer((ushort)(vdpAddress & vdpAddressClamp), value);
|
||||
VRAM[vdpAddress & vdpAddressClamp] = value;
|
||||
vdpAddress++;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdatePrecomputedPalette()
|
||||
{
|
||||
if (mode == VdpMode.SMS)
|
||||
{
|
||||
for (int i=0; i<32; i++)
|
||||
{
|
||||
byte value = CRAM[i];
|
||||
byte r = SMSPalXlatTable[(value & 0x03)];
|
||||
byte g = SMSPalXlatTable[(value & 0x0C) >> 2];
|
||||
byte b = SMSPalXlatTable[(value & 0x30) >> 4];
|
||||
Palette[i] = Colors.ARGB(r, g, b);
|
||||
}
|
||||
} else // GameGear
|
||||
{
|
||||
for (int i=0; i<32; i++)
|
||||
{
|
||||
ushort value = (ushort) ((CRAM[(i*2) + 1] << 8) | CRAM[(i*2) + 0]);
|
||||
byte r = GGPalXlatTable[(value & 0x000F)];
|
||||
byte g = GGPalXlatTable[(value & 0x00F0) >> 4];
|
||||
byte b = GGPalXlatTable[(value & 0x0F00) >> 8];
|
||||
Palette[i] = Colors.ARGB(r, g, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly byte[] pow2 = {1, 2, 4, 8, 16, 32, 64, 128};
|
||||
|
||||
private void UpdatePatternBuffer(ushort address, byte value)
|
||||
{
|
||||
// writing one byte affects 8 pixels due to stupid planar storage.
|
||||
for (int i=0; i<8; i++)
|
||||
{
|
||||
byte colorBit = pow2[address%4];
|
||||
byte sourceBit = pow2[7 - i];
|
||||
ushort dest = (ushort) (((address & 0xFFFC)*2) + i);
|
||||
if ((value & sourceBit) > 0) // setting bit
|
||||
PatternBuffer[dest] |= colorBit;
|
||||
else // clearing bit
|
||||
PatternBuffer[dest] &= (byte)~colorBit;
|
||||
}
|
||||
}
|
||||
|
||||
internal void RenderCurrentScanline(bool render)
|
||||
{
|
||||
// TODO: make frameskip actually skip rendering
|
||||
RenderBackgroundCurrentLine();
|
||||
RenderSpritesCurrentLine();
|
||||
}
|
||||
|
||||
internal void RenderBackgroundCurrentLine()
|
||||
{
|
||||
if (DisplayOn == false)
|
||||
{
|
||||
for (int x = 0; x < 256; x++)
|
||||
FrameBuffer[(ScanLine*256) + x] = BackdropColor;
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear the priority buffer for this scanline
|
||||
for (int p = 0; p < 256; p++)
|
||||
ScanlinePriorityBuffer[p] = 0;
|
||||
|
||||
int mapBase = NameTableBase;
|
||||
|
||||
int vertOffset = ScanLine + Registers[9];
|
||||
if (vertOffset >= 224)
|
||||
vertOffset -= 224;
|
||||
byte horzOffset = (HorizScrollLock && ScanLine < 16) ? (byte) 0 : Registers[8];
|
||||
|
||||
int yTile = vertOffset/8;
|
||||
|
||||
for (int xTile = 0; xTile<32; xTile++)
|
||||
{
|
||||
if (xTile == 24 && VerticalScrollLock)
|
||||
{
|
||||
vertOffset = ScanLine;
|
||||
yTile = vertOffset/8;
|
||||
}
|
||||
|
||||
byte PaletteBase = 0;
|
||||
int tileInfo = VRAM[mapBase+((yTile*32) + xTile)*2] | (VRAM[mapBase+(((yTile*32) + xTile)*2) + 1]<<8);
|
||||
int tileNo = tileInfo & 0x01FF;
|
||||
if ((tileInfo & 0x800) != 0)
|
||||
PaletteBase = 16;
|
||||
bool Priority = (tileInfo & 0x1000) != 0;
|
||||
bool VFlip = (tileInfo & 0x400) != 0;
|
||||
bool HFlip = (tileInfo & 0x200) != 0;
|
||||
|
||||
int yOfs = vertOffset & 7;
|
||||
if (VFlip)
|
||||
yOfs = 7 - yOfs;
|
||||
|
||||
if (HFlip == false)
|
||||
{
|
||||
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 0] + PaletteBase];
|
||||
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 1] + PaletteBase];
|
||||
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 2] + PaletteBase];
|
||||
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 3] + PaletteBase];
|
||||
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 4] + PaletteBase];
|
||||
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 5] + PaletteBase];
|
||||
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 6] + PaletteBase];
|
||||
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 7] + PaletteBase];
|
||||
|
||||
if (Priority)
|
||||
{
|
||||
horzOffset -= 8;
|
||||
for (int k = 0; k < 8; k++)
|
||||
{
|
||||
if (PatternBuffer[(tileNo * 64) + (yOfs * 8) + k] != 0)
|
||||
ScanlinePriorityBuffer[horzOffset] = 1;
|
||||
horzOffset++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else // Flipped Horizontally
|
||||
{
|
||||
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 7] + PaletteBase];
|
||||
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 6] + PaletteBase];
|
||||
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 5] + PaletteBase];
|
||||
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 4] + PaletteBase];
|
||||
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 3] + PaletteBase];
|
||||
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 2] + PaletteBase];
|
||||
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 1] + PaletteBase];
|
||||
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 0] + PaletteBase];
|
||||
|
||||
if (Priority)
|
||||
{
|
||||
horzOffset -= 8;
|
||||
for (int k = 7; k >= 0; k--)
|
||||
{
|
||||
if (PatternBuffer[(tileNo * 64) + (yOfs * 8) + k] != 0)
|
||||
ScanlinePriorityBuffer[horzOffset] = 1;
|
||||
horzOffset++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void RenderSpritesCurrentLine()
|
||||
{
|
||||
if (DisplayOn == false) return;
|
||||
int SpriteBase = SpriteAttributeTableBase;
|
||||
int SpriteHeight = Enable8x16Sprites ? 16 : 8;
|
||||
|
||||
// Clear the sprite collision buffer for this scanline
|
||||
for (int c = 0; c < 256; c++)
|
||||
SpriteCollisionBuffer[c] = 0;
|
||||
|
||||
// 208 is a special terminator sprite. Lets find it...
|
||||
int TerminalSprite = 64;
|
||||
for (int i = 0; i < 64; i++)
|
||||
{
|
||||
if (VRAM[SpriteBase + i] == 208)
|
||||
{
|
||||
TerminalSprite = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Loop through these sprites and render the current scanline
|
||||
int SpritesDrawnThisScanline = 0;
|
||||
for (int i = TerminalSprite - 1; i >= 0; i--)
|
||||
{
|
||||
if (SpritesDrawnThisScanline >= 8)
|
||||
StatusByte |= 0x40; // Set Overflow bit
|
||||
|
||||
int x = VRAM[SpriteBase + 0x80 + (i*2)];
|
||||
if (ShiftSpritesLeft8Pixels)
|
||||
x -= 8;
|
||||
|
||||
int y = VRAM[SpriteBase + i] + 1;
|
||||
if (y >= (Enable8x16Sprites ? 240 : 248)) y -= 256;
|
||||
|
||||
if (y+SpriteHeight<=ScanLine || y > ScanLine)
|
||||
continue;
|
||||
|
||||
int tileNo = VRAM[SpriteBase + 0x80 + (i*2) + 1];
|
||||
if (Enable8x16Sprites)
|
||||
tileNo &= 0xFE;
|
||||
tileNo += SpriteTileBase;
|
||||
|
||||
int ys = ScanLine - y;
|
||||
|
||||
for (int xs = 0; xs<8 && x+xs < 256; xs++)
|
||||
{
|
||||
byte color = PatternBuffer[(tileNo*64) + (ys*8) + xs];
|
||||
if (color != 0 && x+xs >= 0 && ScanlinePriorityBuffer[x + xs] == 0)
|
||||
{
|
||||
FrameBuffer[(ys + y)*256 + x + xs] = Palette[(color + 16)];
|
||||
if (SpriteCollisionBuffer[x + xs] != 0)
|
||||
StatusByte |= 0x20; // Set Collision bit
|
||||
SpriteCollisionBuffer[x + xs] = 1;
|
||||
}
|
||||
}
|
||||
SpritesDrawnThisScanline++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs render buffer blanking. This includes the left-column blanking as well as Game Gear blanking if requested.
|
||||
/// Should be called at the end of the frame.
|
||||
/// </summary>
|
||||
public void RenderBlankingRegions()
|
||||
{
|
||||
int blankingColor = Palette[BackdropColor];
|
||||
|
||||
if (LeftBlanking)
|
||||
{
|
||||
for (int y=0; y<192; y++)
|
||||
{
|
||||
for (int x=0; x<8; x++)
|
||||
FrameBuffer[(y*256) + x] = blankingColor;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == VdpMode.GameGear)
|
||||
{
|
||||
for (int y = 0; y < 144; y++)
|
||||
for (int x = 0; x < 160; x++)
|
||||
GameGearFrameBuffer[(y*160) + x] = FrameBuffer[((y + 24)*256) + x + 48];
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveStateText(TextWriter writer)
|
||||
{
|
||||
writer.WriteLine("[VDP]");
|
||||
writer.WriteLine("Mode " + Enum.GetName(typeof(VdpMode), VdpMode));
|
||||
writer.WriteLine("StatusByte {0:X2}", StatusByte);
|
||||
writer.WriteLine("WaitingForLatchByte {0}", vdpWaitingForLatchByte);
|
||||
writer.WriteLine("Latch {0:X2}", vdpLatch);
|
||||
writer.WriteLine("ReadBuffer {0:X2}", vdpBuffer);
|
||||
writer.WriteLine("VdpAddress {0:X4}", vdpAddress);
|
||||
writer.WriteLine("VdpAddressMask {0:X2}", vdpAddressClamp);
|
||||
writer.WriteLine("Command " + Enum.GetName(typeof(VdpCommand), vdpCommand));
|
||||
|
||||
writer.Write("Registers ");
|
||||
Registers.SaveAsHex(writer);
|
||||
writer.Write("CRAM ");
|
||||
CRAM.SaveAsHex(writer);
|
||||
writer.Write("VRAM ");
|
||||
VRAM.SaveAsHex(writer);
|
||||
|
||||
writer.WriteLine("[/VDP]");
|
||||
writer.WriteLine();
|
||||
}
|
||||
|
||||
public void LoadStateText(TextReader reader)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
string[] args = reader.ReadLine().Split(' ');
|
||||
if (args[0].Trim() == "") continue;
|
||||
if (args[0] == "[/VDP]") break;
|
||||
if (args[0] == "StatusByte")
|
||||
StatusByte = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "WaitingForLatchByte")
|
||||
vdpWaitingForLatchByte = bool.Parse(args[1]);
|
||||
else if (args[0] == "Latch")
|
||||
vdpLatch = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "ReadBuffer")
|
||||
vdpBuffer = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "VdpAddress")
|
||||
vdpAddress = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "VdpAddressMask")
|
||||
vdpAddressClamp = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "Command")
|
||||
vdpCommand = (VdpCommand) Enum.Parse(typeof (VdpCommand), args[1]);
|
||||
else if (args[0] == "Registers")
|
||||
Registers.ReadFromHex(args[1]);
|
||||
else if (args[0] == "CRAM")
|
||||
{
|
||||
CRAM.ReadFromHex(args[1]);
|
||||
UpdatePrecomputedPalette();
|
||||
}
|
||||
else if (args[0] == "VRAM")
|
||||
{
|
||||
VRAM.ReadFromHex(args[1]);
|
||||
for (ushort i=0; i<VRAM.Length; i++)
|
||||
UpdatePatternBuffer(i, VRAM[i]);
|
||||
}
|
||||
|
||||
else
|
||||
Console.WriteLine("Skipping unrecognized identifier "+args[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveStateBinary(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(StatusByte);
|
||||
writer.Write(vdpWaitingForLatchByte);
|
||||
writer.Write(vdpLatch);
|
||||
writer.Write(vdpBuffer);
|
||||
writer.Write(vdpAddress);
|
||||
writer.Write(vdpAddressClamp);
|
||||
writer.Write((byte)vdpCommand);
|
||||
writer.Write(Registers);
|
||||
writer.Write(CRAM);
|
||||
writer.Write(VRAM);
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader reader)
|
||||
{
|
||||
StatusByte = reader.ReadByte();
|
||||
vdpWaitingForLatchByte = reader.ReadBoolean();
|
||||
vdpLatch = reader.ReadByte();
|
||||
vdpBuffer = reader.ReadByte();
|
||||
vdpAddress = reader.ReadUInt16();
|
||||
vdpAddressClamp = reader.ReadUInt16();
|
||||
vdpCommand = (VdpCommand) Enum.ToObject(typeof(VdpCommand), reader.ReadByte());
|
||||
Registers = reader.ReadBytes(Registers.Length);
|
||||
CRAM = reader.ReadBytes(CRAM.Length);
|
||||
VRAM = reader.ReadBytes(VRAM.Length);
|
||||
UpdatePrecomputedPalette();
|
||||
for (ushort i = 0; i < VRAM.Length; i++)
|
||||
UpdatePatternBuffer(i, VRAM[i]);
|
||||
}
|
||||
|
||||
public int[] GetVideoBuffer()
|
||||
{
|
||||
return mode == VdpMode.SMS ? FrameBuffer : GameGearFrameBuffer;
|
||||
}
|
||||
|
||||
public int BufferWidth
|
||||
{
|
||||
get { return mode == VdpMode.SMS ? 256 : 160; }
|
||||
}
|
||||
|
||||
public int BufferHeight
|
||||
{
|
||||
get { return mode == VdpMode.SMS ? 192 : 144; }
|
||||
}
|
||||
|
||||
public int BackgroundColor
|
||||
{
|
||||
get { return Palette[BackdropColor]; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
namespace BizHawk
|
||||
{
|
||||
public static class CRC32
|
||||
{
|
||||
// Lookup table for speed.
|
||||
private static uint[] CRC32Table;
|
||||
|
||||
static CRC32()
|
||||
{
|
||||
//unchecked {
|
||||
CRC32Table = new uint[256];
|
||||
for (uint i = 0; i < 256; ++i)
|
||||
{
|
||||
uint crc = i;
|
||||
for (int j = 8; j > 0; --j)
|
||||
{
|
||||
if ((crc & 1) == 1)
|
||||
crc = ((crc >> 1) ^ 0xEDB88320);
|
||||
else
|
||||
crc >>= 1;
|
||||
}
|
||||
CRC32Table[i] = crc;
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
public static int Calculate(byte[] data)
|
||||
{
|
||||
//unchecked {
|
||||
uint Result = 0xFFFFFFFF;
|
||||
foreach (var b in data)
|
||||
Result = (((Result) >> 8) ^ CRC32Table[b ^ ((Result) & 0xFF)]);
|
||||
return (int)~Result;
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace BizHawk
|
||||
{
|
||||
public class GameInfo
|
||||
{
|
||||
public string Name;
|
||||
public string System;
|
||||
public string MetaData;
|
||||
public int CRC32;
|
||||
|
||||
public string[] GetOptions()
|
||||
{
|
||||
if (string.IsNullOrEmpty(MetaData))
|
||||
return new string[0];
|
||||
return MetaData.Split(';').Where(opt => string.IsNullOrEmpty(opt) == false).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Database
|
||||
{
|
||||
private static Dictionary<int, GameInfo> db = new Dictionary<int, GameInfo>();
|
||||
|
||||
public static void LoadDatabase(string path)
|
||||
{
|
||||
using (var reader = new StreamReader(new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
|
||||
{
|
||||
reader.ReadLine(); // Skip header row
|
||||
|
||||
while (reader.EndOfStream == false)
|
||||
{
|
||||
string line = reader.ReadLine();
|
||||
if (line.Trim().Length == 0) continue;
|
||||
string[] items = line.Split('\t');
|
||||
|
||||
var Game = new GameInfo();
|
||||
Game.CRC32 = Int32.Parse(items[0], NumberStyles.HexNumber);
|
||||
Game.Name = items[2];
|
||||
Game.System = items[3];
|
||||
Game.MetaData = items.Length >= 6 ? items[5] : null;
|
||||
db[Game.CRC32] = Game;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static GameInfo GetGameInfo(byte[] RomData, string fileName)
|
||||
{
|
||||
int crc = CRC32.Calculate(RomData);
|
||||
if (db.ContainsKey(crc))
|
||||
return db[crc];
|
||||
|
||||
// rom is not in database. make some best-guesses
|
||||
var Game = new GameInfo();
|
||||
Game.CRC32 = crc;
|
||||
Game.MetaData = "NotInDatabase";
|
||||
|
||||
string ext = Path.GetExtension(fileName).ToUpperInvariant();
|
||||
|
||||
switch (ext)
|
||||
{
|
||||
case ".SMS": Game.System = "SMS"; break;
|
||||
case ".GG" : Game.System = "GG"; break;
|
||||
case ".SG" : Game.System = "SG"; break;
|
||||
case ".PCE": Game.System = "PCE"; break;
|
||||
case ".SGX": Game.System = "SGX"; break;
|
||||
case ".GB" : Game.System = "GB"; break;
|
||||
case ".BIN":
|
||||
case ".SMD": Game.System = "GEN"; break;
|
||||
case ".NES": Game.System = "NES"; break;
|
||||
}
|
||||
|
||||
Game.Name = Path.GetFileNameWithoutExtension(fileName).Replace('_', ' ');
|
||||
// If filename is all-caps, then attempt to proper-case the title.
|
||||
if (Game.Name == Game.Name.ToUpperInvariant())
|
||||
Game.Name = Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(Game.Name.ToLower());
|
||||
|
||||
return Game;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace BizHawk
|
||||
{
|
||||
public sealed class Game : IGame
|
||||
{
|
||||
public byte[] RomData;
|
||||
private string name;
|
||||
private string[] options;
|
||||
private const int BankSize = 4096;
|
||||
|
||||
public Game(string path, params string[] options)
|
||||
{
|
||||
name = Path.GetFileNameWithoutExtension(path).Replace('_',' ');
|
||||
this.options = options;
|
||||
if (this.options == null)
|
||||
this.options = new string[0];
|
||||
|
||||
this.options = options;
|
||||
|
||||
using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
int length = (int)stream.Length;
|
||||
|
||||
stream.Position = 0;
|
||||
if (length % BankSize == 512) // 512-byte ROM header is present
|
||||
{
|
||||
stream.Position += 512;
|
||||
length -= 512;
|
||||
}
|
||||
if (length % BankSize == 64) // 64-byte ROM header is present
|
||||
{
|
||||
stream.Position += 64;
|
||||
length -= 64;
|
||||
}
|
||||
|
||||
if (length % BankSize != 0)
|
||||
throw new Exception("Not a valid ROM.");
|
||||
RomData = new byte[length];
|
||||
stream.Read(RomData, 0, length);
|
||||
}
|
||||
}
|
||||
|
||||
public void Patch(string patch)
|
||||
{
|
||||
using (var stream = new FileStream(patch, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
IPS.Patch(RomData, stream);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] GetRomData()
|
||||
{
|
||||
return RomData;
|
||||
}
|
||||
|
||||
public string[] GetOptions()
|
||||
{
|
||||
return options;
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return name; }
|
||||
set { name = value; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace BizHawk
|
||||
{
|
||||
public static class IPS
|
||||
{
|
||||
public static void Patch(byte[] rom, Stream patch)
|
||||
{
|
||||
var ipsHeader = new byte[5];
|
||||
patch.Read(ipsHeader, 0, 5);
|
||||
|
||||
string header = "PATCH";
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
if (ipsHeader[i] != header[i])
|
||||
{
|
||||
Console.WriteLine("Patch file specified is invalid.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// header verified, loop over patch entries
|
||||
uint EOF = ('E' * 0x10000 + 'O' * 0x100 + 'F');
|
||||
|
||||
while (true)
|
||||
{
|
||||
uint offset = Read24(patch);
|
||||
if (offset == EOF) return;
|
||||
ushort size = Read16(patch);
|
||||
|
||||
if (size != 0) // non-RLE patch
|
||||
{
|
||||
var patchData = new byte[size];
|
||||
patch.Read(patchData, 0, size);
|
||||
for (int i = 0; i < size; i++)
|
||||
rom[offset++] = patchData[i];
|
||||
}
|
||||
else // RLE patch
|
||||
{
|
||||
size = Read16(patch);
|
||||
byte value = (byte)patch.ReadByte();
|
||||
for (int i = 0; i < size; i++)
|
||||
rom[offset++] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ushort Read16(Stream patch)
|
||||
{
|
||||
int Upper = patch.ReadByte();
|
||||
int Lower = patch.ReadByte();
|
||||
return (ushort)(Upper * 0x100 + Lower);
|
||||
}
|
||||
|
||||
private static uint Read24(Stream patch)
|
||||
{
|
||||
int Upper = patch.ReadByte();
|
||||
int Middle = patch.ReadByte();
|
||||
int Lower = patch.ReadByte();
|
||||
return (uint)(Upper * 0x10000 + Middle * 0x100 + Lower);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
using System.IO;
|
||||
|
||||
namespace BizHawk
|
||||
{
|
||||
public class InputRecorder : IController
|
||||
{
|
||||
private IController baseController;
|
||||
private BinaryWriter writer;
|
||||
|
||||
public InputRecorder(IController baseController, BinaryWriter writer)
|
||||
{
|
||||
this.baseController = baseController;
|
||||
this.writer = writer;
|
||||
}
|
||||
|
||||
public void CloseMovie()
|
||||
{
|
||||
writer.Close();
|
||||
}
|
||||
|
||||
public ControllerDefinition Type
|
||||
{
|
||||
get { return baseController.Type; }
|
||||
}
|
||||
|
||||
public bool this[string name]
|
||||
{
|
||||
get { return baseController[name]; }
|
||||
}
|
||||
|
||||
public bool IsPressed(string name)
|
||||
{
|
||||
return baseController[name];
|
||||
}
|
||||
|
||||
public float GetFloat(string name)
|
||||
{
|
||||
return baseController.GetFloat(name);
|
||||
}
|
||||
|
||||
public void UnpressButton(string name)
|
||||
{
|
||||
baseController.UnpressButton(name);
|
||||
}
|
||||
|
||||
private int frame;
|
||||
public int FrameNumber
|
||||
{
|
||||
get { return frame; }
|
||||
set
|
||||
{
|
||||
if (frame != value)
|
||||
{
|
||||
frame = value;
|
||||
RecordFrame();
|
||||
}
|
||||
baseController.FrameNumber = value;
|
||||
}
|
||||
}
|
||||
|
||||
private void RecordFrame()
|
||||
{
|
||||
int encodedValue = 0;
|
||||
for (int i=0; i<Type.BoolButtons.Count; i++)
|
||||
{
|
||||
if (baseController[Type.BoolButtons[i]])
|
||||
{
|
||||
encodedValue |= (1 << i);
|
||||
}
|
||||
}
|
||||
writer.Seek(frame*2, SeekOrigin.Begin);
|
||||
writer.Write((ushort)encodedValue);
|
||||
}
|
||||
}
|
||||
|
||||
public class InputPlayback : IController
|
||||
{
|
||||
private ControllerDefinition def;
|
||||
private int[] input;
|
||||
|
||||
public InputPlayback(ControllerDefinition controllerDefinition, BinaryReader reader)
|
||||
{
|
||||
def = controllerDefinition;
|
||||
int numFrames = (int) (reader.BaseStream.Length/2);
|
||||
input = new int[numFrames];
|
||||
for (int i=0; i<numFrames; i++)
|
||||
input[i] = reader.ReadUInt16();
|
||||
}
|
||||
|
||||
public ControllerDefinition Type
|
||||
{
|
||||
get { return def; }
|
||||
}
|
||||
|
||||
public bool this[string name]
|
||||
{
|
||||
get { return IsPressed(name); }
|
||||
}
|
||||
|
||||
public bool IsPressed(string name)
|
||||
{
|
||||
if (FrameNumber >= input.Length)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < def.BoolButtons.Count; i++)
|
||||
{
|
||||
if (def.BoolButtons[i] == name)
|
||||
{
|
||||
return (input[FrameNumber] & (1 << i)) != 0;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public float GetFloat(string name)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public void UnpressButton(string name) {}
|
||||
public int FrameNumber { get; set; }
|
||||
|
||||
public bool MovieEnded { get { return FrameNumber >= input.Length; } }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
namespace BizHawk
|
||||
{
|
||||
public class NullController : IController
|
||||
{
|
||||
public ControllerDefinition Type { get { return null; } }
|
||||
public bool this[string name] { get { return false; } }
|
||||
public bool IsPressed(string name) { return false; }
|
||||
public float GetFloat(string name) { return 0f; }
|
||||
public void UnpressButton(string name) { }
|
||||
public int FrameNumber { get; set; }
|
||||
|
||||
private static NullController nullController = new NullController();
|
||||
public static NullController GetNullController() { return nullController; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace BizHawk
|
||||
{
|
||||
public class NullEmulator : IEmulator, IVideoProvider, ISoundProvider
|
||||
{
|
||||
private int[] frameBuffer = new int[256 * 192];
|
||||
private Random rand = new Random();
|
||||
public IVideoProvider VideoProvider { get { return this; } }
|
||||
public ISoundProvider SoundProvider { get { return this; } }
|
||||
public void LoadGame(IGame game) { }
|
||||
public void FrameAdvance(bool render)
|
||||
{
|
||||
if (render == false) return;
|
||||
for (int i = 0; i < 256 * 192; i++)
|
||||
frameBuffer[i] = Colors.Luminosity((byte)rand.Next());
|
||||
}
|
||||
public void HardReset() { }
|
||||
public ControllerDefinition ControllerDefinition { get { return null; } }
|
||||
public IController Controller { get; set; }
|
||||
public int Frame { get; set; }
|
||||
public byte[] SaveRam { get { return new byte[0]; } }
|
||||
public bool DeterministicEmulation { get; set; }
|
||||
public bool SaveRamModified { get; set; }
|
||||
public void SaveStateText(TextWriter writer) { }
|
||||
public void LoadStateText(TextReader reader) { }
|
||||
public void SaveStateBinary(BinaryWriter writer) { }
|
||||
public void LoadStateBinary(BinaryReader reader) { }
|
||||
public byte[] SaveStateBinary() { return new byte[1]; }
|
||||
public int[] GetVideoBuffer() { return frameBuffer; }
|
||||
public int BufferWidth { get { return 256; } }
|
||||
public int BufferHeight { get { return 192; } }
|
||||
public int BackgroundColor { get { return 0; } }
|
||||
public void GetSamples(short[] samples) { }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace BizHawk
|
||||
{
|
||||
/// <summary>
|
||||
/// Loader for .SMD Genesis ROM format (Super Magic Drive)
|
||||
/// </summary>
|
||||
public sealed class SmdGame : IGame
|
||||
{
|
||||
public byte[] RomData;
|
||||
private string name;
|
||||
private string[] options;
|
||||
|
||||
private const int BankSize = 16384;
|
||||
|
||||
// TODO we need a consistent interface for these ROM loader implementations, that easily supports loading direct files, or from Content.
|
||||
// TODO also should support Name-set, and some other crap.
|
||||
// TODO we should inject a way to support IPS patches, because the patch needs to be applied before de-interlacing (I assume).
|
||||
public SmdGame(string path, params string[] options)
|
||||
{
|
||||
name = Path.GetFileNameWithoutExtension(path).Replace('_', ' ');
|
||||
this.options = options;
|
||||
if (this.options == null)
|
||||
this.options = new string[0];
|
||||
|
||||
using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
int length = (int)stream.Length;
|
||||
|
||||
stream.Position = 0;
|
||||
if (length % BankSize == 512) // 512-byte ROM header is present
|
||||
{
|
||||
stream.Position += 512;
|
||||
length -= 512;
|
||||
}
|
||||
|
||||
if (length % BankSize != 0)
|
||||
throw new Exception("Not a valid ROM.");
|
||||
var rawRomData = new byte[length];
|
||||
stream.Read(rawRomData, 0, length);
|
||||
RomData = DeInterleave(rawRomData);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] DeInterleave(byte[] source)
|
||||
{
|
||||
// SMD files are interleaved in pages of 16k, with the first 8k containing all
|
||||
// odd bytes and the second 8k containing all even bytes.
|
||||
|
||||
int size = source.Length;
|
||||
if (size > 0x400000) size = 0x400000;
|
||||
int pages = size / 0x4000;
|
||||
byte[] output = new byte[size];
|
||||
|
||||
for (int page = 0; page < pages; page++)
|
||||
{
|
||||
for (int i = 0; i < 0x2000; i++)
|
||||
{
|
||||
output[(page * 0x4000) + (i * 2) + 0] = source[(page * 0x4000) + 0x2000 + i];
|
||||
output[(page * 0x4000) + (i * 2) + 1] = source[(page * 0x4000) + 0x0000 + i];
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
public byte[] GetRomData()
|
||||
{
|
||||
return RomData;
|
||||
}
|
||||
|
||||
public string[] GetOptions()
|
||||
{
|
||||
return options;
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return name; }
|
||||
set { name = value; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace BizHawk
|
||||
{
|
||||
public class ControllerDefinition
|
||||
{
|
||||
public string Name;
|
||||
public List<string> BoolButtons = new List<string>();
|
||||
public List<string> FloatControls = new List<string>();
|
||||
}
|
||||
|
||||
public interface IController
|
||||
{
|
||||
ControllerDefinition Type { get; }
|
||||
|
||||
bool this[string name] { get; }
|
||||
bool IsPressed(string name);
|
||||
float GetFloat(string name);
|
||||
void UnpressButton(string name);
|
||||
|
||||
int FrameNumber { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
using System.IO;
|
||||
|
||||
namespace BizHawk
|
||||
{
|
||||
public interface IEmulator
|
||||
{
|
||||
IVideoProvider VideoProvider { get; }
|
||||
ISoundProvider SoundProvider { get; }
|
||||
|
||||
ControllerDefinition ControllerDefinition { get; }
|
||||
IController Controller { get; set; }
|
||||
|
||||
void LoadGame(IGame game);
|
||||
void FrameAdvance(bool render);
|
||||
void HardReset();
|
||||
|
||||
int Frame { get; }
|
||||
bool DeterministicEmulation { get; set; }
|
||||
|
||||
byte[] SaveRam { get; }
|
||||
bool SaveRamModified { get; set; }
|
||||
|
||||
// TODO: should IEmulator expose a way of enumerating the Options it understands?
|
||||
// (the answer is yes)
|
||||
|
||||
void SaveStateText(TextWriter writer);
|
||||
void LoadStateText(TextReader reader);
|
||||
void SaveStateBinary(BinaryWriter writer);
|
||||
void LoadStateBinary(BinaryReader reader);
|
||||
byte[] SaveStateBinary();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
namespace BizHawk
|
||||
{
|
||||
public interface IGame
|
||||
{
|
||||
byte[] GetRomData();
|
||||
string[] GetOptions();
|
||||
string Name { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
namespace BizHawk
|
||||
{
|
||||
public interface ISoundProvider
|
||||
{
|
||||
void GetSamples(short[] samples);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
namespace BizHawk
|
||||
{
|
||||
public interface IVideoProvider
|
||||
{
|
||||
int[] GetVideoBuffer();
|
||||
|
||||
int BufferWidth { get; }
|
||||
int BufferHeight { get; }
|
||||
int BackgroundColor { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace BizHawk
|
||||
{
|
||||
public static class Log
|
||||
{
|
||||
static Log()
|
||||
{
|
||||
// You can set current desired logging settings here.
|
||||
// Production builds should be done with all logging disabled.
|
||||
//LogToConsole = true;
|
||||
//LogToFile = true;
|
||||
//LogFilename = "d:/bizhawk.log";
|
||||
//EnableDomain("CPU");
|
||||
//EnableDomain("VDC");
|
||||
//EnableDomain("MEM");
|
||||
}
|
||||
|
||||
// ============== Logging Domain Configuration ==============
|
||||
|
||||
private static List<string> EnabledLogDomains = new List<string>();
|
||||
|
||||
public static void EnableDomain(string domain)
|
||||
{
|
||||
if (EnabledLogDomains.Contains(domain) == false)
|
||||
EnabledLogDomains.Add(domain);
|
||||
}
|
||||
|
||||
public static void DisableDomain(string domain)
|
||||
{
|
||||
if (EnabledLogDomains.Contains(domain))
|
||||
EnabledLogDomains.Remove(domain);
|
||||
}
|
||||
|
||||
// ============== Logging Action Configuration ==============
|
||||
|
||||
public static Action<string> LogAction = DefaultLogger;
|
||||
|
||||
// NOTEs are only logged if the domain is enabled.
|
||||
// ERRORs are logged regardless.
|
||||
|
||||
public static void Note(string domain, string msg, params object[] vals)
|
||||
{
|
||||
if (EnabledLogDomains.Contains(domain))
|
||||
LogAction(String.Format(msg, vals));
|
||||
}
|
||||
|
||||
public static void Error(string domain, string msg, params object[] vals)
|
||||
{
|
||||
LogAction(String.Format(msg, vals));
|
||||
}
|
||||
|
||||
// ============== Default Logger Action ==============
|
||||
|
||||
private static bool LogToConsole;
|
||||
private static bool LogToFile;
|
||||
|
||||
private static string LogFilename = "bizhawk.txt";
|
||||
private static StreamWriter writer;
|
||||
|
||||
private static void DefaultLogger(string message)
|
||||
{
|
||||
if (LogToConsole)
|
||||
Console.WriteLine(message);
|
||||
|
||||
if (LogToFile && writer == null)
|
||||
writer = new StreamWriter(LogFilename);
|
||||
|
||||
if (LogToFile)
|
||||
writer.WriteLine(message);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
-------------------------------
|
||||
BizHawk.Emulation Project Notes
|
||||
-------------------------------
|
||||
|
||||
1) Please keep this project free of non-portable external references.
|
||||
Windows.Forms should not be referenced, nor System.Windows.Drawing, nor XNA, nor SlimDX, etc.
|
||||
This project should be very close to vanilla C# consisting primarily of basic data manipulation.
|
||||
|
||||
2) If you want to include ie Winforms code for something such as a debugger, this should be placed in
|
||||
a separate project. That project does not yet exist at the time of this writing. If you are creating
|
||||
a debugger, please talk to me (vecna) and we'll figure out the right place to put it.
|
||||
|
||||
3) The current plan is that all actual emulation cores go in this assembly.
|
||||
At least one reason for this is that .NET will not inline calls across assemblies.
|
||||
At this time, please do not separate emulation into multiple assemblies - this is negotiable, but
|
||||
for now this is the plan.
|
||||
|
||||
4) IEmulator exposes a DeterministicEmulation property. When enabled, a well-behaving core must
|
||||
use strictly deterministic (TAS-safe, not having de-syncs) emulation, regardless of the performance cost.
|
||||
When disabled, the core is free to take whatever shortcuts it deems reasonable for performance.
|
||||
|
||||
Note that in many cases, frameskipping will not be possible in Deterministic mode. In that case the
|
||||
client should still be free to request a frameskip, but the core will ignore this request and do a full
|
||||
frame execution.
|
||||
|
||||
Deterministic means exactly that, no more, no less; it's not a synonym for 'max emulation quality'.
|
||||
|
||||
5) Classes should default sealed. Make unsealed only if the class is _designed for inheritance_.
|
||||
|
||||
6) For GENERATED CPUs, DO NOT UPDATE THE GENERATED FILES DIRECTLY!!!!!!!!!!!!!!
|
||||
Open the CpuCoreGenerator solution and make your changes there. Run the program to regenerate your cpus.
|
||||
CpuCoreGenerator is a separate solution and it may not be obvious that it is there.
|
||||
|
||||
---------------
|
||||
Emulation Notes
|
||||
---------------
|
||||
|
||||
1) I'm not super happy with the IController interfaces. If someone has suggestions on how they could be
|
||||
improved, I'm all for it. Although API changes would break all emulators, controls are typically pretty
|
||||
simple to update.
|
||||
|
||||
2) Various improvements to ISoundProvider, IVideoProvider, and IEmulator are under consideration.
|
||||
Feedback is appreciated.
|
||||
|
||||
ISoundProvider:
|
||||
* A means of controlling overall volume for the SoundProvider would be beneficial
|
||||
when it comes to mixing multiple ISoundProviders.
|
||||
* Current implementations generate garbage on each frame, although the API does not _require_
|
||||
garbage to be created, it encourages it because you cannot provide a large buffer and specify
|
||||
a smaller number of samples to fill. Also the BufferAsync generates garbage and probably the
|
||||
SoundMixer. It's not really clear to me how much of a problem generating garbage is on PC.
|
||||
|
||||
IVideoProvider:
|
||||
* Some hints about different aspect ratios and non-square pixels could be useful.
|
||||
* For some arcades, screen rotations could be important, but we could assign this to be
|
||||
the responsibility of the emulator/video provider rather than the blitter.
|
||||
* I suppose NTSC/PAL (ie: target fps) could be useful also.
|
||||
|
||||
IEmulator:
|
||||
* The LoadGame(IGame) is potentially problematic. Perhaps we simply remove this. Most likely
|
||||
the constructor would then take an IGame as a requirement (or ICDImage or IRomSet or IFileSystem...)
|
||||
* IEmulator should provide metadata about what Options it recognizes.
|
||||
* Possibly, a lot of the metadata type functions should be removed from IEmulator and added to new
|
||||
interface (such as IConsole, IArcade, IComputer, whatever)
|
|
@ -0,0 +1,36 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("BizHawk.Emulation")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Microsoft")]
|
||||
[assembly: AssemblyProduct("BizHawk.Emulation")]
|
||||
[assembly: AssemblyCopyright("Copyright © Microsoft 2011")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("38dc7bf9-d86c-4217-acd8-36dc09f8ba57")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,341 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
|
||||
namespace BizHawk.Emulation.Sound
|
||||
{
|
||||
// Emulates PSG audio unit of a PC Engine / Turbografx-16 / SuperGrafx.
|
||||
// It is embedded on the CPU and doesn't have its own part number. None the less, it is emulated separately from the 6280 CPU.
|
||||
|
||||
public sealed class HuC6280PSG : ISoundProvider
|
||||
{
|
||||
public class PSGChannel
|
||||
{
|
||||
public ushort Frequency;
|
||||
public byte Panning;
|
||||
public byte Volume;
|
||||
public bool Enabled;
|
||||
public bool NoiseChannel;
|
||||
public bool DDA;
|
||||
public ushort NoiseFreq;
|
||||
public short DDAValue;
|
||||
public short[] Wave = new short[32];
|
||||
public float SampleOffset;
|
||||
}
|
||||
|
||||
public PSGChannel[] Channels = new PSGChannel[8];
|
||||
|
||||
public byte VoiceLatch;
|
||||
private byte WaveTableWriteOffset;
|
||||
|
||||
private Queue<QueuedCommand> commands = new Queue<QueuedCommand>(256);
|
||||
private int frameStartTime, frameStopTime;
|
||||
|
||||
private const int SampleRate = 44100;
|
||||
private const int PsgBase = 3580000;
|
||||
private static byte[] LogScale = { 0, 0, 10, 10, 13, 13, 16, 16, 20, 20, 26, 26, 32, 32, 40, 40, 51, 51, 64, 64, 81, 81, 102, 102, 128, 128, 161, 161, 203, 203, 255, 255 };
|
||||
|
||||
public byte MainVolumeLeft;
|
||||
public byte MainVolumeRight;
|
||||
|
||||
public HuC6280PSG()
|
||||
{
|
||||
for (int i=0; i<8; i++)
|
||||
Channels[i] = new PSGChannel();
|
||||
}
|
||||
|
||||
public void BeginFrame(int cycles)
|
||||
{
|
||||
while (commands.Count > 0)
|
||||
{
|
||||
var cmd = commands.Dequeue();
|
||||
WritePSGImmediate(cmd.Register, cmd.Value);
|
||||
}
|
||||
frameStartTime = cycles;
|
||||
}
|
||||
|
||||
public void EndFrame(int cycles)
|
||||
{
|
||||
frameStopTime = cycles;
|
||||
}
|
||||
|
||||
public void WritePSG(ushort register, byte value, int cycles)
|
||||
{
|
||||
commands.Enqueue(new QueuedCommand { Register = register, Value = value, Time = cycles-frameStartTime });
|
||||
}
|
||||
|
||||
public void WritePSGImmediate(ushort register, byte value)
|
||||
{
|
||||
switch (register)
|
||||
{
|
||||
case 0x800: // Set Voice Latch
|
||||
VoiceLatch = (byte) (value & 7);
|
||||
break;
|
||||
case 0x801: // Global Volume select;
|
||||
MainVolumeLeft = (byte) ((value >> 4) & 0x0F);
|
||||
MainVolumeRight = (byte) (value & 0x0F);
|
||||
break;
|
||||
case 0x802: // Frequency LSB
|
||||
Channels[VoiceLatch].Frequency &= 0xFF00;
|
||||
Channels[VoiceLatch].Frequency |= value;
|
||||
break;
|
||||
case 0x803: // Frequency MSB
|
||||
Channels[VoiceLatch].Frequency &= 0x00FF;
|
||||
Channels[VoiceLatch].Frequency |= (ushort)(value << 8);
|
||||
Channels[VoiceLatch].Frequency &= 0x0FFF;
|
||||
break;
|
||||
case 0x804: // Voice Volume
|
||||
Channels[VoiceLatch].Volume = (byte) (value & 0x1F);
|
||||
Channels[VoiceLatch].Enabled = (value & 0x80) != 0;
|
||||
Channels[VoiceLatch].DDA = (value & 0x40) != 0;
|
||||
if (Channels[VoiceLatch].Enabled == false && Channels[VoiceLatch].DDA)
|
||||
WaveTableWriteOffset = 0;
|
||||
break;
|
||||
case 0x805: // Panning
|
||||
Channels[VoiceLatch].Panning = value;
|
||||
break;
|
||||
case 0x806: // Wave data
|
||||
if (Channels[VoiceLatch].DDA == false)
|
||||
{
|
||||
Channels[VoiceLatch].Wave[WaveTableWriteOffset++] = (short) ((value*2047) - 32767);
|
||||
WaveTableWriteOffset &= 31;
|
||||
} else {
|
||||
Channels[VoiceLatch].DDAValue = (short)((value * 2047) - 32767);
|
||||
}
|
||||
break;
|
||||
case 0x807: // Noise
|
||||
Channels[VoiceLatch].NoiseChannel = ((value & 0x80) != 0) && VoiceLatch >= 4;
|
||||
if ((value & 0x1F) == 0x1F)
|
||||
value &= 0xFE;
|
||||
Channels[VoiceLatch].NoiseFreq = (ushort) (PsgBase/(64*(0x1F - (value & 0x1F))));
|
||||
break;
|
||||
case 0x0808: // LFO
|
||||
// TODO: implement LFO
|
||||
break;
|
||||
case 0x809: // LFO Control
|
||||
if ((value & 0x80) == 0 && (value & 3) != 0)
|
||||
{
|
||||
Channels[1].Enabled = false;
|
||||
} else
|
||||
{
|
||||
Channels[1].Enabled = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void GetSamples(short[] samples)
|
||||
{
|
||||
int elapsedCycles = frameStopTime - frameStartTime;
|
||||
int start = 0;
|
||||
while (commands.Count > 0)
|
||||
{
|
||||
var cmd = commands.Dequeue();
|
||||
int pos = ((cmd.Time * samples.Length) / elapsedCycles) & ~1;
|
||||
MixSamples(samples, start, pos - start);
|
||||
start = pos;
|
||||
WritePSGImmediate(cmd.Register, cmd.Value);
|
||||
}
|
||||
MixSamples(samples, start, samples.Length - start);
|
||||
}
|
||||
|
||||
private void MixSamples(short[] samples, int start, int len)
|
||||
{
|
||||
for (int i = 0; i < 6; i++)
|
||||
MixChannel(samples, start, len, Channels[i]);
|
||||
}
|
||||
|
||||
private void MixChannel(short[] samples, int start, int len, PSGChannel channel)
|
||||
{
|
||||
if (channel.Enabled == false) return;
|
||||
if (channel.DDA == false && (channel.Frequency == 0 || channel.Volume == 0)) return;
|
||||
|
||||
int freq = PsgBase / (32 * (channel.DDA ? 1 : (int)channel.Frequency));
|
||||
int leftVol = channel.Panning >> 4;
|
||||
int rightVol = channel.Panning & 15;
|
||||
leftVol *= MainVolumeLeft;
|
||||
rightVol *= MainVolumeRight;
|
||||
leftVol /= 16;
|
||||
rightVol /= 16;
|
||||
|
||||
short[] wave = channel.Wave;
|
||||
if (channel.NoiseChannel)
|
||||
{
|
||||
wave = Waves.NoiseWave;
|
||||
freq = channel.NoiseFreq;
|
||||
leftVol /= 2;
|
||||
rightVol /= 2;
|
||||
}
|
||||
|
||||
float adjustedWaveLengthInSamples = SampleRate / (channel.NoiseChannel ? freq/512f : freq);
|
||||
float moveThroughWaveRate = wave.Length / adjustedWaveLengthInSamples;
|
||||
|
||||
int end = start + len;
|
||||
for (int i=start; i<end;)
|
||||
{
|
||||
channel.SampleOffset %= wave.Length;
|
||||
short value = channel.DDA ? channel.DDAValue : wave[(int) channel.SampleOffset];
|
||||
|
||||
float left = ((value * LogScale[channel.Volume] / 255f / 6f) * (leftVol / 15f));
|
||||
|
||||
if (left>32768f || left <-32768f) Console.WriteLine("HEY BAD THINGS");
|
||||
samples[i++] += (short) left;
|
||||
samples[i++] += (short)((value * LogScale[channel.Volume] / 255f / 6f) * (rightVol / 15f));
|
||||
|
||||
channel.SampleOffset += moveThroughWaveRate;
|
||||
channel.SampleOffset %= wave.Length;
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveStateText(TextWriter writer)
|
||||
{
|
||||
writer.WriteLine("[PSG]");
|
||||
|
||||
writer.WriteLine("MainVolumeLeft {0:X2}", MainVolumeLeft);
|
||||
writer.WriteLine("MainVolumeRight {0:X2}", MainVolumeRight);
|
||||
writer.WriteLine("VoiceLatch {0}", VoiceLatch);
|
||||
writer.WriteLine("WaveTableWriteOffset {0:X2}", WaveTableWriteOffset);
|
||||
writer.WriteLine();
|
||||
|
||||
for (int i = 0; i<6; i++)
|
||||
{
|
||||
writer.WriteLine("[Channel{0}]",i+1);
|
||||
writer.WriteLine("Frequency {0:X4}", Channels[i].Frequency);
|
||||
writer.WriteLine("Panning {0:X2}", Channels[i].Panning);
|
||||
writer.WriteLine("Volume {0:X2}", Channels[i].Volume);
|
||||
writer.WriteLine("Enabled {0}", Channels[i].Enabled);
|
||||
if (i.In(4,5))
|
||||
{
|
||||
writer.WriteLine("NoiseChannel {0}", Channels[i].NoiseChannel);
|
||||
writer.WriteLine("NoiseFreq {0:X4}", Channels[i].NoiseFreq);
|
||||
}
|
||||
writer.WriteLine("DDA {0}",Channels[i].DDA);
|
||||
writer.WriteLine("DDAValue {0:X4}", Channels[i].DDAValue);
|
||||
writer.WriteLine("SampleOffset {0}", Channels[i].SampleOffset);
|
||||
writer.Write("Wave ");
|
||||
Channels[i].Wave.SaveAsHex(writer);
|
||||
writer.WriteLine("[/Channel{0}]\n",i+1);
|
||||
}
|
||||
|
||||
writer.WriteLine("[/PSG]\n");
|
||||
}
|
||||
|
||||
public void LoadStateText(TextReader reader)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
string[] args = reader.ReadLine().Split(' ');
|
||||
if (args[0].Trim() == "") continue;
|
||||
if (args[0] == "[/PSG]") break;
|
||||
if (args[0] == "MainVolumeLeft")
|
||||
MainVolumeLeft = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "MainVolumeRight")
|
||||
MainVolumeRight = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "VoiceLatch")
|
||||
VoiceLatch = byte.Parse(args[1]);
|
||||
else if (args[0] == "WaveTableWriteOffset")
|
||||
WaveTableWriteOffset = byte.Parse(args[1]);
|
||||
else if (args[0] == "[Channel1]")
|
||||
LoadChannelStateText(reader, 0);
|
||||
else if (args[0] == "[Channel2]")
|
||||
LoadChannelStateText(reader, 1);
|
||||
else if (args[0] == "[Channel3]")
|
||||
LoadChannelStateText(reader, 2);
|
||||
else if (args[0] == "[Channel4]")
|
||||
LoadChannelStateText(reader, 3);
|
||||
else if (args[0] == "[Channel5]")
|
||||
LoadChannelStateText(reader, 4);
|
||||
else if (args[0] == "[Channel6]")
|
||||
LoadChannelStateText(reader, 5);
|
||||
else
|
||||
Console.WriteLine("Skipping unrecognized identifier " + args[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadChannelStateText(TextReader reader, int channel)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
string[] args = reader.ReadLine().Split(' ');
|
||||
if (args[0].Trim() == "") continue;
|
||||
if (args[0] == "[/Channel"+(channel+1)+"]") break;
|
||||
if (args[0] == "Frequency")
|
||||
Channels[channel].Frequency = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "Panning")
|
||||
Channels[channel].Panning = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "Volume")
|
||||
Channels[channel].Volume = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "Enabled")
|
||||
Channels[channel].Enabled = bool.Parse(args[1]);
|
||||
else if (args[0] == "NoiseChannel")
|
||||
Channels[channel].NoiseChannel = bool.Parse(args[1]);
|
||||
else if (args[0] == "NoiseFreq")
|
||||
Channels[channel].NoiseFreq = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "DDA")
|
||||
Channels[channel].DDA = bool.Parse(args[1]);
|
||||
else if (args[0] == "DDAValue")
|
||||
Channels[channel].DDAValue = short.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "SampleOffset")
|
||||
Channels[channel].SampleOffset = float.Parse(args[1]);
|
||||
else if (args[0] == "Wave")
|
||||
Channels[channel].Wave.ReadFromHex(args[1]);
|
||||
else
|
||||
Console.WriteLine("Skipping unrecognized identifier " + args[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveStateBinary(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(MainVolumeLeft);
|
||||
writer.Write(MainVolumeRight);
|
||||
writer.Write(VoiceLatch);
|
||||
writer.Write(WaveTableWriteOffset);
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
writer.Write(Channels[i].Frequency);
|
||||
writer.Write(Channels[i].Panning);
|
||||
writer.Write(Channels[i].Volume);
|
||||
writer.Write(Channels[i].Enabled);
|
||||
writer.Write(Channels[i].NoiseChannel);
|
||||
writer.Write(Channels[i].NoiseFreq);
|
||||
writer.Write(Channels[i].DDA);
|
||||
writer.Write(Channels[i].DDAValue);
|
||||
writer.Write(Channels[i].SampleOffset);
|
||||
for (int j = 0; j < 32; j++)
|
||||
writer.Write(Channels[i].Wave[j]);
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader reader)
|
||||
{
|
||||
MainVolumeLeft = reader.ReadByte();
|
||||
MainVolumeRight = reader.ReadByte();
|
||||
VoiceLatch = reader.ReadByte();
|
||||
WaveTableWriteOffset = reader.ReadByte();
|
||||
|
||||
for (int i=0; i<6; i++)
|
||||
{
|
||||
Channels[i].Frequency = reader.ReadUInt16();
|
||||
Channels[i].Panning = reader.ReadByte();
|
||||
Channels[i].Volume = reader.ReadByte();
|
||||
Channels[i].Enabled = reader.ReadBoolean();
|
||||
Channels[i].NoiseChannel = reader.ReadBoolean();
|
||||
Channels[i].NoiseFreq = reader.ReadUInt16();
|
||||
Channels[i].DDA = reader.ReadBoolean();
|
||||
Channels[i].DDAValue = reader.ReadInt16();
|
||||
Channels[i].SampleOffset = reader.ReadSingle();
|
||||
for (int j = 0; j < 32; j++)
|
||||
Channels[i].Wave[j] = reader.ReadInt16();
|
||||
}
|
||||
}
|
||||
|
||||
class QueuedCommand
|
||||
{
|
||||
public ushort Register;
|
||||
public byte Value;
|
||||
public int Time;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,440 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
|
||||
// Emulates a Texas Instruments SN76489. This is found in:
|
||||
// + Sega 8-bit consoles (SMS/MarkIII/Game Gear/SG-1000/SC-3000/etc)
|
||||
// + In the Genesis/MegaDrive as a 2ndary sound source
|
||||
// + Some arcade hardware
|
||||
|
||||
// The Game Gear version is enhanced to support stereo output.
|
||||
// TODO at this time, I dont know if some arcades need a different PsgBase value or if it is constant.
|
||||
// TODO the noise channel emulation is not perfect.
|
||||
// TODO the freq->note translation should be moved to a separate utility class.
|
||||
|
||||
namespace BizHawk.Emulation.Sound
|
||||
{
|
||||
public sealed class SN76489 : ISoundProvider
|
||||
{
|
||||
public sealed class Channel
|
||||
{
|
||||
public ushort Frequency;
|
||||
public byte Volume;
|
||||
public short[] Wave;
|
||||
public bool Noise;
|
||||
public byte NoiseType;
|
||||
public float WaveOffset;
|
||||
public bool Left = true;
|
||||
public bool Right = true;
|
||||
|
||||
private const int SampleRate = 44100;
|
||||
private static byte[] LogScale = { 0, 10, 13, 16, 20, 26, 32, 40, 51, 64, 81, 102, 128, 161, 203, 255 };
|
||||
|
||||
public void Mix(short[] samples, int start, int len)
|
||||
{
|
||||
if (Volume == 0) return;
|
||||
|
||||
float adjustedWaveLengthInSamples = SampleRate / (Noise ? (Frequency / 512f) : Frequency);
|
||||
float moveThroughWaveRate = Wave.Length / adjustedWaveLengthInSamples;
|
||||
|
||||
int end = start + len;
|
||||
for (int i = start; i < end; )
|
||||
{
|
||||
short value = Wave[(int)WaveOffset];
|
||||
|
||||
samples[i++] += (short)(Left ? (value / 4 * LogScale[Volume] / 0x1FF) : 0);
|
||||
samples[i++] += (short)(Right ? (value / 4 * LogScale[Volume] / 0x1FF) : 0);
|
||||
WaveOffset += moveThroughWaveRate;
|
||||
if (WaveOffset >= Wave.Length)
|
||||
WaveOffset %= Wave.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Channel[] Channels = new Channel[4];
|
||||
public byte PsgLatch;
|
||||
|
||||
private Queue<QueuedCommand> commands = new Queue<QueuedCommand>(256);
|
||||
private int frameStartTime, frameStopTime;
|
||||
|
||||
private const int PsgBase = 111861;
|
||||
|
||||
public SN76489()
|
||||
{
|
||||
for (int i=0; i<4; i++)
|
||||
{
|
||||
Channels[i] = new Channel();
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
Channels[i].Wave = Waves.ImperfectSquareWave;
|
||||
break;
|
||||
case 3:
|
||||
Channels[i].Wave = Waves.NoiseWave;
|
||||
Channels[i].Noise = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
PsgLatch = 0;
|
||||
foreach (var channel in Channels)
|
||||
{
|
||||
channel.Frequency = 0;
|
||||
channel.Volume = 0;
|
||||
channel.NoiseType = 0;
|
||||
channel.WaveOffset = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
public void BeginFrame(int cycles)
|
||||
{
|
||||
while (commands.Count > 0)
|
||||
{
|
||||
var cmd = commands.Dequeue();
|
||||
WritePsgDataImmediate(cmd.Value);
|
||||
}
|
||||
frameStartTime = cycles;
|
||||
}
|
||||
|
||||
public void EndFrame(int cycles)
|
||||
{
|
||||
frameStopTime = cycles;
|
||||
}
|
||||
|
||||
public void WritePsgData(byte value, int cycles)
|
||||
{
|
||||
commands.Enqueue(new QueuedCommand {Value = value, Time = cycles-frameStartTime});
|
||||
}
|
||||
|
||||
public void WritePsgDataImmediate(byte value)
|
||||
{
|
||||
switch (value & 0xF0)
|
||||
{
|
||||
case 0x80:
|
||||
case 0xA0:
|
||||
case 0xC0:
|
||||
PsgLatch = value;
|
||||
break;
|
||||
case 0xE0:
|
||||
PsgLatch = value;
|
||||
Channels[3].NoiseType = (byte) (value & 0x03);
|
||||
switch (Channels[3].NoiseType)
|
||||
{
|
||||
case 0: Channels[3].Frequency = PsgBase/16; break;
|
||||
case 1: Channels[3].Frequency = PsgBase/32; break;
|
||||
case 2: Channels[3].Frequency = PsgBase/64; break;
|
||||
case 3: Channels[3].Frequency = Channels[2].Frequency; break;
|
||||
}
|
||||
break;
|
||||
case 0x90:
|
||||
Channels[0].Volume = (byte)(~value & 15);
|
||||
PsgLatch = value;
|
||||
break;
|
||||
case 0xB0:
|
||||
Channels[1].Volume = (byte)(~value & 15);
|
||||
PsgLatch = value;
|
||||
break;
|
||||
case 0xD0:
|
||||
Channels[2].Volume = (byte)(~value & 15);
|
||||
PsgLatch = value;
|
||||
break;
|
||||
case 0xF0:
|
||||
Channels[3].Volume = (byte)(~value & 15);
|
||||
PsgLatch = value;
|
||||
break;
|
||||
default:
|
||||
byte channel = (byte) ((PsgLatch & 0x60) >> 5);
|
||||
if ((PsgLatch & 16) == 0) // Tone latched
|
||||
{
|
||||
int f = PsgBase/(((value & 0x03F)*16) + (PsgLatch & 0x0F) + 1);
|
||||
if (f > 15000)
|
||||
f = 0; // upper bound of playable frequency
|
||||
Channels[channel].Frequency = (ushort) f;
|
||||
if (Channels[3].NoiseType == 3 && channel == 2)
|
||||
Channels[3].Frequency = (ushort) f;
|
||||
} else { // volume latched
|
||||
Channels[channel].Volume = (byte)(~value & 15);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public byte StereoPanning
|
||||
{
|
||||
get
|
||||
{
|
||||
byte value = 0;
|
||||
if (Channels[0].Left) value |= 0x10;
|
||||
if (Channels[0].Right) value |= 0x01;
|
||||
if (Channels[1].Left) value |= 0x20;
|
||||
if (Channels[1].Right) value |= 0x02;
|
||||
if (Channels[2].Left) value |= 0x40;
|
||||
if (Channels[2].Right) value |= 0x04;
|
||||
if (Channels[3].Left) value |= 0x80;
|
||||
if (Channels[3].Right) value |= 0x08;
|
||||
return value;
|
||||
}
|
||||
set
|
||||
{
|
||||
Channels[0].Left = (value & 0x10) != 0;
|
||||
Channels[0].Right = (value & 0x01) != 0;
|
||||
Channels[1].Left = (value & 0x20) != 0;
|
||||
Channels[1].Right = (value & 0x02) != 0;
|
||||
Channels[2].Left = (value & 0x40) != 0;
|
||||
Channels[2].Right = (value & 0x04) != 0;
|
||||
Channels[3].Left = (value & 0x80) != 0;
|
||||
Channels[3].Right = (value & 0x08) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveStateText(TextWriter writer)
|
||||
{
|
||||
writer.WriteLine("[PSG]");
|
||||
writer.WriteLine("Volume0 {0:X2}", Channels[0].Volume);
|
||||
writer.WriteLine("Volume1 {0:X2}", Channels[1].Volume);
|
||||
writer.WriteLine("Volume2 {0:X2}", Channels[2].Volume);
|
||||
writer.WriteLine("Volume3 {0:X2}", Channels[3].Volume);
|
||||
writer.WriteLine("Freq0 {0:X4}", Channels[0].Frequency);
|
||||
writer.WriteLine("Freq1 {0:X4}", Channels[1].Frequency);
|
||||
writer.WriteLine("Freq2 {0:X4}", Channels[2].Frequency);
|
||||
writer.WriteLine("Freq3 {0:X4}", Channels[3].Frequency);
|
||||
writer.WriteLine("NoiseType {0:X}", Channels[3].NoiseType);
|
||||
writer.WriteLine("PsgLatch {0:X2}", PsgLatch);
|
||||
writer.WriteLine("Panning {0:X2}", StereoPanning);
|
||||
writer.WriteLine("[/PSG]");
|
||||
writer.WriteLine();
|
||||
}
|
||||
|
||||
public void LoadStateText(TextReader reader)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
string[] args = reader.ReadLine().Split(' ');
|
||||
if (args[0].Trim() == "") continue;
|
||||
if (args[0] == "[/PSG]") break;
|
||||
if (args[0] == "Volume0")
|
||||
Channels[0].Volume = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "Volume1")
|
||||
Channels[1].Volume = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "Volume2")
|
||||
Channels[2].Volume = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "Volume3")
|
||||
Channels[3].Volume = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "Freq0")
|
||||
Channels[0].Frequency = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "Freq1")
|
||||
Channels[1].Frequency = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "Freq2")
|
||||
Channels[2].Frequency = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "Freq3")
|
||||
Channels[3].Frequency = ushort.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "NoiseType")
|
||||
Channels[3].NoiseType = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "PsgLatch")
|
||||
PsgLatch = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
else if (args[0] == "Panning")
|
||||
StereoPanning = byte.Parse(args[1], NumberStyles.HexNumber);
|
||||
|
||||
else
|
||||
Console.WriteLine("Skipping unrecognized identifier " + args[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveStateBinary(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(Channels[0].Volume);
|
||||
writer.Write(Channels[1].Volume);
|
||||
writer.Write(Channels[2].Volume);
|
||||
writer.Write(Channels[3].Volume);
|
||||
writer.Write(Channels[0].Frequency);
|
||||
writer.Write(Channels[1].Frequency);
|
||||
writer.Write(Channels[2].Frequency);
|
||||
writer.Write(Channels[3].Frequency);
|
||||
writer.Write(Channels[3].NoiseType);
|
||||
writer.Write(PsgLatch);
|
||||
writer.Write(StereoPanning);
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader reader)
|
||||
{
|
||||
Channels[0].Volume = reader.ReadByte();
|
||||
Channels[1].Volume = reader.ReadByte();
|
||||
Channels[2].Volume = reader.ReadByte();
|
||||
Channels[3].Volume = reader.ReadByte();
|
||||
Channels[0].Frequency = reader.ReadUInt16();
|
||||
Channels[1].Frequency = reader.ReadUInt16();
|
||||
Channels[2].Frequency = reader.ReadUInt16();
|
||||
Channels[3].Frequency = reader.ReadUInt16();
|
||||
Channels[3].NoiseType = reader.ReadByte();
|
||||
PsgLatch = reader.ReadByte();
|
||||
StereoPanning = reader.ReadByte();
|
||||
}
|
||||
|
||||
#region Frequency -> Note Conversion (for interested humans)
|
||||
|
||||
public static string GetNote(int freq)
|
||||
{
|
||||
if (freq < 26) return "LOW";
|
||||
if (freq > 4435) return "HIGH";
|
||||
|
||||
for (int i = 0; i < frequencies.Length - 1; i++)
|
||||
{
|
||||
if (freq >= frequencies[i + 1]) continue;
|
||||
int nextNoteDistance = frequencies[i + 1] - frequencies[i];
|
||||
int distance = freq - frequencies[i];
|
||||
if (distance < nextNoteDistance / 2)
|
||||
{
|
||||
// note identified
|
||||
return notes[i];
|
||||
}
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
// For the curious, A4 = 440hz. Every octave is a doubling, so A5=880, A3=220
|
||||
// Each next step is a factor of the 12-root of 2. So to go up a step you multiply by 1.0594630943592952645618252949463
|
||||
// Next step from A4 is A#4. A#4 = (440.00 * 1.05946...) = 466.163...
|
||||
// Note that because frequencies must be integers, SMS games will be slightly out of pitch to a normally tuned instrument, especially at the low end.
|
||||
|
||||
private static readonly int[] frequencies =
|
||||
{
|
||||
27, // A0
|
||||
29, // A#0
|
||||
31, // B0
|
||||
33, // C1
|
||||
35, // C#1
|
||||
37, // D1
|
||||
39, // D#1
|
||||
41, // E1
|
||||
44, // F1
|
||||
46, // F#1
|
||||
49, // G1
|
||||
52, // G#1
|
||||
55, // A1
|
||||
58, // A#1
|
||||
62, // B1
|
||||
65, // C2
|
||||
69, // C#2
|
||||
73, // D2
|
||||
78, // D#2
|
||||
82, // E2
|
||||
87, // F2
|
||||
92, // F#2
|
||||
98, // G2
|
||||
104, // G#2
|
||||
110, // A2
|
||||
117, // A#2
|
||||
123, // B2
|
||||
131, // C3
|
||||
139, // C#3
|
||||
147, // D3
|
||||
156, // D#3
|
||||
165, // E3
|
||||
175, // F3
|
||||
185, // F#3
|
||||
196, // G3
|
||||
208, // G#3
|
||||
220, // A3
|
||||
233, // A#3
|
||||
247, // B3
|
||||
262, // C4
|
||||
277, // C#4
|
||||
294, // D4
|
||||
311, // D#4
|
||||
330, // E4
|
||||
349, // F4
|
||||
370, // F#4
|
||||
392, // G4
|
||||
415, // G#4
|
||||
440, // A4
|
||||
466, // A#4
|
||||
494, // B4
|
||||
523, // C5
|
||||
554, // C#5
|
||||
587, // D5
|
||||
622, // D#5
|
||||
659, // E5
|
||||
698, // F5
|
||||
740, // F#5
|
||||
784, // G5
|
||||
831, // G#5
|
||||
880, // A5
|
||||
932, // A#5
|
||||
988, // B5
|
||||
1046, // C6
|
||||
1109, // C#6
|
||||
1175, // D6
|
||||
1245, // D#6
|
||||
1319, // E6
|
||||
1397, // F6
|
||||
1480, // F#6
|
||||
1568, // G6
|
||||
1661, // G#6
|
||||
1760, // A6
|
||||
1865, // A#6
|
||||
1976, // B6
|
||||
2093, // C7
|
||||
2217, // C#7
|
||||
2349, // D7
|
||||
2489, // D#7
|
||||
2637, // E7
|
||||
2794, // F7
|
||||
2960, // F#7
|
||||
3136, // G7
|
||||
3322, // G#7
|
||||
3520, // A7
|
||||
3729, // A#7
|
||||
3951, // B7
|
||||
4186, // C8
|
||||
4435 // C#8
|
||||
};
|
||||
|
||||
private static readonly string[] notes =
|
||||
{
|
||||
"A0","A#0","B0",
|
||||
"C1","C#1","D1","D#1","E1","F1","F#1","G1","G#1","A1","A#1","B1",
|
||||
"C2","C#2","D2","D#2","E2","F2","F#2","G2","G#2","A2","A#2","B2",
|
||||
"C3","C#3","D3","D#3","E3","F3","F#3","G3","G#3","A3","A#3","B3",
|
||||
"C4","C#4","D4","D#4","E4","F4","F#4","G4","G#4","A4","A#4","B4",
|
||||
"C5","C#5","D5","D#5","E5","F5","F#5","G5","G#5","A5","A#5","B5",
|
||||
"C6","C#6","D6","D#6","E6","F6","F#6","G6","G#6","A6","A#6","B6",
|
||||
"C7","C#7","D7","D#7","E7","F7","F#7","G7","G#7","A7","A#7","B7",
|
||||
"C8","HIGH"
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
public void GetSamples(short[] samples)
|
||||
{
|
||||
int elapsedCycles = frameStopTime - frameStartTime;
|
||||
int start = 0;
|
||||
while (commands.Count > 0)
|
||||
{
|
||||
var cmd = commands.Dequeue();
|
||||
int pos = ((cmd.Time*samples.Length)/elapsedCycles) & ~1;
|
||||
GetSamplesImmediate(samples, start, pos-start);
|
||||
start = pos;
|
||||
WritePsgDataImmediate(cmd.Value);
|
||||
}
|
||||
GetSamplesImmediate(samples, start, samples.Length - start);
|
||||
}
|
||||
|
||||
public void GetSamplesImmediate(short[] samples, int start, int len)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
Channels[i].Mix(samples, start, len);
|
||||
}
|
||||
|
||||
class QueuedCommand
|
||||
{
|
||||
public byte Value;
|
||||
public int Time;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace BizHawk.Emulation.Sound
|
||||
{
|
||||
// Generates SEMI-synchronous sound, or "buffered asynchronous" sound.
|
||||
|
||||
// This class will try as hard as it can to request the correct number of samples on each frame and then
|
||||
// send them out to the sound card as it needs them.
|
||||
|
||||
// However, it has minimum/maximum buffer targets and will request smaller or larger frames if it has to.
|
||||
// The ultimate goal of this strategy is to make MOST frames 100% correct, and if errors must occur,
|
||||
// concentrate it on a single frame, rather than distribute small errors across most frames, as
|
||||
// distributing error to most frames tends to result in persistently distorted audio, especially when
|
||||
// sample playback is involved.
|
||||
|
||||
public sealed class BufferedAsync : ISoundProvider
|
||||
{
|
||||
public ISoundProvider BaseSoundProvider;
|
||||
|
||||
private Queue<short> buffer = new Queue<short>(4096);
|
||||
|
||||
private const int SamplesInOneFrame = 1470;
|
||||
private const int TargetExtraSamples = 882;
|
||||
private const int MaxExcessSamples = 4096;
|
||||
|
||||
public void GetSamples(short[] samples)
|
||||
{
|
||||
int samplesToGenerate = SamplesInOneFrame;
|
||||
if (buffer.Count > samples.Length + MaxExcessSamples)
|
||||
samplesToGenerate = 0;
|
||||
if (buffer.Count - samples.Length < TargetExtraSamples)
|
||||
samplesToGenerate += SamplesInOneFrame;
|
||||
if (samplesToGenerate + buffer.Count < samples.Length)
|
||||
samplesToGenerate = samples.Length - buffer.Count;
|
||||
|
||||
var mySamples = new short[samplesToGenerate];
|
||||
|
||||
BaseSoundProvider.GetSamples(mySamples);
|
||||
|
||||
for (int i = 0; i < mySamples.Length; i++)
|
||||
buffer.Enqueue(mySamples[i]);
|
||||
|
||||
for (int i = 0; i < samples.Length; i++)
|
||||
samples[i] = buffer.Dequeue();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,486 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BizHawk.Emulation.Sound
|
||||
{
|
||||
public class MetaspuSoundProvider : ISoundProvider
|
||||
{
|
||||
public ISynchronizingAudioBuffer buffer = Metaspu.metaspu_construct(ESynchMethod.ESynchMethod_Z);
|
||||
public MetaspuSoundProvider()
|
||||
{
|
||||
}
|
||||
|
||||
public void GetSamples(short[] samples)
|
||||
{
|
||||
buffer.output_samples(samples, samples.Length / 2);
|
||||
}
|
||||
}
|
||||
|
||||
public interface ISynchronizingAudioBuffer
|
||||
{
|
||||
void enqueue_samples(short[] buf, int samples_provided);
|
||||
|
||||
//returns the number of samples actually supplied, which may not match the number requested
|
||||
int output_samples(short[] buf, int samples_requested);
|
||||
};
|
||||
|
||||
public enum ESynchMethod
|
||||
{
|
||||
ESynchMethod_N, //nitsuja's
|
||||
ESynchMethod_Z, //zero's
|
||||
//ESynchMethod_P, //PCSX2 spu2-x //ohno! not available yet in c#
|
||||
};
|
||||
|
||||
public static class Metaspu
|
||||
{
|
||||
public static ISynchronizingAudioBuffer metaspu_construct(ESynchMethod method)
|
||||
{
|
||||
switch (method)
|
||||
{
|
||||
case ESynchMethod.ESynchMethod_Z:
|
||||
return new ZeromusSynchronizer();
|
||||
case ESynchMethod.ESynchMethod_N:
|
||||
return new NitsujaSynchronizer();
|
||||
default:
|
||||
return new NitsujaSynchronizer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ZeromusSynchronizer : ISynchronizingAudioBuffer
|
||||
{
|
||||
public ZeromusSynchronizer()
|
||||
{
|
||||
//#ifdef NDEBUG
|
||||
adjustobuf = new Adjustobuf(200, 1000);
|
||||
//#else
|
||||
//adjustobuf = new Adjustobuf(22000, 44000);
|
||||
//#endif
|
||||
|
||||
}
|
||||
|
||||
//adjustobuf(200,1000)
|
||||
bool mixqueue_go = false;
|
||||
|
||||
public void enqueue_samples(short[] buf, int samples_provided)
|
||||
{
|
||||
int ctr = 0;
|
||||
for (int i = 0; i < samples_provided; i++)
|
||||
{
|
||||
short left = buf[ctr++];
|
||||
short right = buf[ctr++];
|
||||
adjustobuf.enqueue(left, right);
|
||||
}
|
||||
}
|
||||
|
||||
//returns the number of samples actually supplied, which may not match the number requested
|
||||
public int output_samples(short[] buf, int samples_requested)
|
||||
{
|
||||
int ctr=0;
|
||||
int done = 0;
|
||||
if (!mixqueue_go)
|
||||
{
|
||||
if (adjustobuf.size > 200)
|
||||
mixqueue_go = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < samples_requested; i++)
|
||||
{
|
||||
if (adjustobuf.size == 0)
|
||||
{
|
||||
mixqueue_go = false;
|
||||
break;
|
||||
}
|
||||
done++;
|
||||
short left, right;
|
||||
adjustobuf.dequeue(out left, out right);
|
||||
buf[ctr++] = left;
|
||||
buf[ctr++] = right;
|
||||
}
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
Adjustobuf adjustobuf;
|
||||
class Adjustobuf
|
||||
{
|
||||
public Adjustobuf(int _minLatency, int _maxLatency)
|
||||
{
|
||||
minLatency = _minLatency;
|
||||
maxLatency = _maxLatency;
|
||||
rollingTotalSize = 0;
|
||||
targetLatency = (maxLatency + minLatency)/2;
|
||||
rate = 1.0f;
|
||||
cursor = 0.0f;
|
||||
curr[0] = curr[1] = 0;
|
||||
kAverageSize = 80000;
|
||||
}
|
||||
|
||||
float rate, cursor;
|
||||
int minLatency, targetLatency, maxLatency;
|
||||
Queue<short> buffer = new Queue<short>();
|
||||
Queue<int> statsHistory = new Queue<int>();
|
||||
public int size = 0;
|
||||
short[] curr = new short[2];
|
||||
|
||||
public void enqueue(short left, short right)
|
||||
{
|
||||
buffer.Enqueue(left);
|
||||
buffer.Enqueue(right);
|
||||
size++;
|
||||
}
|
||||
|
||||
long rollingTotalSize;
|
||||
|
||||
uint kAverageSize;
|
||||
|
||||
void addStatistic()
|
||||
{
|
||||
statsHistory.Enqueue(size);
|
||||
rollingTotalSize += size;
|
||||
if (statsHistory.Count > kAverageSize)
|
||||
{
|
||||
rollingTotalSize -= statsHistory.Peek();
|
||||
statsHistory.Dequeue();
|
||||
|
||||
float averageSize = (float)(rollingTotalSize / kAverageSize);
|
||||
//static int ctr=0; ctr++; if((ctr&127)==0) printf("avg size: %f curr size: %d rate: %f\n",averageSize,size,rate);
|
||||
{
|
||||
float targetRate;
|
||||
if(averageSize < targetLatency)
|
||||
{
|
||||
targetRate = 1.0f - (targetLatency-averageSize)/kAverageSize;
|
||||
}
|
||||
else if(averageSize > targetLatency) {
|
||||
targetRate = 1.0f + (averageSize-targetLatency)/kAverageSize;
|
||||
} else targetRate = 1.0f;
|
||||
|
||||
//rate = moveValueTowards(rate,targetRate,0.001f);
|
||||
rate = targetRate;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void dequeue(out short left, out short right)
|
||||
{
|
||||
left = right = 0;
|
||||
addStatistic();
|
||||
if(size==0) { return; }
|
||||
cursor += rate;
|
||||
while(cursor>1.0f) {
|
||||
cursor -= 1.0f;
|
||||
if(size>0) {
|
||||
curr[0] = buffer.Dequeue();
|
||||
curr[1] = buffer.Dequeue();
|
||||
size--;
|
||||
}
|
||||
}
|
||||
left = curr[0];
|
||||
right = curr[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NitsujaSynchronizer : ISynchronizingAudioBuffer
|
||||
{
|
||||
struct ssamp
|
||||
{
|
||||
public short l, r;
|
||||
public ssamp(short ll, short rr) { l = ll; r = rr; }
|
||||
};
|
||||
|
||||
List<ssamp> sampleQueue = new List<ssamp>();
|
||||
|
||||
// returns values going between 0 and y-1 in a saw wave pattern, based on x
|
||||
static int pingpong(int x, int y)
|
||||
{
|
||||
x %= 2*y;
|
||||
if(x >= y)
|
||||
x = 2*y - x - 1;
|
||||
return x;
|
||||
|
||||
// in case we want to switch to odd buffer sizes for more sharpness
|
||||
//x %= 2*(y-1);
|
||||
//if(x >= y)
|
||||
// x = 2*(y-1) - x;
|
||||
//return x;
|
||||
}
|
||||
|
||||
static ssamp crossfade (ssamp lhs, ssamp rhs, int cur, int start, int end)
|
||||
{
|
||||
if(cur <= start)
|
||||
return lhs;
|
||||
if(cur >= end)
|
||||
return rhs;
|
||||
|
||||
// in case we want sine wave interpolation instead of linear here
|
||||
//float ang = 3.14159f * (float)(cur - start) / (float)(end - start);
|
||||
//cur = start + (int)((1-cosf(ang))*0.5f * (end - start));
|
||||
|
||||
int inNum = cur - start;
|
||||
int outNum = end - cur;
|
||||
int denom = end - start;
|
||||
|
||||
int lrv = ((int)lhs.l * outNum + (int)rhs.l * inNum) / denom;
|
||||
int rrv = ((int)lhs.r * outNum + (int)rhs.r * inNum) / denom;
|
||||
|
||||
return new ssamp((short)lrv,(short)rrv);
|
||||
}
|
||||
|
||||
static void emit_sample(short[] outbuf, ref int cursor, ssamp sample)
|
||||
{
|
||||
outbuf[cursor++] = sample.l;
|
||||
outbuf[cursor++] = sample.r;
|
||||
}
|
||||
|
||||
static void emit_samples(short[] outbuf, ref int outcursor, ssamp[] samplebuf, int incursor, int samples)
|
||||
{
|
||||
for(int i=0;i<samples;i++)
|
||||
emit_sample(outbuf,ref outcursor, samplebuf[i+incursor]);
|
||||
}
|
||||
|
||||
static short abs(short value)
|
||||
{
|
||||
if (value < 0) return (short)-value;
|
||||
else return value;
|
||||
}
|
||||
|
||||
static int abs(int value)
|
||||
{
|
||||
if (value < 0) return -value;
|
||||
else return value;
|
||||
}
|
||||
|
||||
public void enqueue_samples(short[] buf, int samples_provided)
|
||||
{
|
||||
int cursor = 0;
|
||||
for(int i=0;i<samples_provided;i++)
|
||||
{
|
||||
sampleQueue.Add(new ssamp(buf[cursor+0],buf[cursor+1]));
|
||||
cursor += 2;
|
||||
}
|
||||
}
|
||||
|
||||
public int output_samples(short[] buf, int samples_requested)
|
||||
{
|
||||
Console.WriteLine("{0} {1}", samples_requested, sampleQueue.Count); //add this line
|
||||
|
||||
|
||||
int bufcursor = 0;
|
||||
int audiosize = samples_requested;
|
||||
int queued = sampleQueue.Count;
|
||||
|
||||
// I am too lazy to deal with odd numbers
|
||||
audiosize &= ~1;
|
||||
queued &= ~1;
|
||||
|
||||
if(queued > 0x200 && audiosize > 0) // is there any work to do?
|
||||
{
|
||||
// are we going at normal speed?
|
||||
// or more precisely, are the input and output queues/buffers of similar size?
|
||||
if(queued > 900 || audiosize > queued * 2)
|
||||
{
|
||||
// not normal speed. we have to resample it somehow in this case.
|
||||
if(audiosize <= queued)
|
||||
{
|
||||
// fast forward speed
|
||||
// this is the easy case, just crossfade it and it sounds ok
|
||||
for(int i = 0; i < audiosize; i++)
|
||||
{
|
||||
int j = i + queued - audiosize;
|
||||
ssamp outsamp = crossfade(sampleQueue[i],sampleQueue[j], i,0,audiosize);
|
||||
emit_sample(buf,ref bufcursor,outsamp);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// slow motion speed
|
||||
// here we take a very different approach,
|
||||
// instead of crossfading it, we select a single sample from the queue
|
||||
// and make sure that the index we use to select a sample is constantly moving
|
||||
// and that it starts at the first sample in the queue and ends on the last one.
|
||||
//
|
||||
// hopefully the index doesn't move discontinuously or we'll get slight crackling
|
||||
// (there might still be a minor bug here that causes this occasionally)
|
||||
//
|
||||
// here's a diagram of how the index we sample from moves:
|
||||
//
|
||||
// queued (this axis represents the index we sample from. the top means the end of the queue)
|
||||
// ^
|
||||
// | --> audiosize (this axis represents the output index we write to, right meaning forward in output time/position)
|
||||
// | A C C end
|
||||
// A A B C C C
|
||||
// A A A B C C C
|
||||
// A A A B C C
|
||||
// A A C
|
||||
// start
|
||||
//
|
||||
// yes, this means we are spending some stretches of time playing the sound backwards,
|
||||
// but the stretches are short enough that this doesn't sound weird.
|
||||
// this lets us avoid most crackling problems due to the endpoints matching up.
|
||||
|
||||
// first calculate a shorter-than-full window
|
||||
// that has minimal slope at the endpoints
|
||||
// (to further reduce crackling, especially in sine waves)
|
||||
int beststart = 0, extraAtEnd = 0;
|
||||
{
|
||||
int bestend = queued;
|
||||
const int worstdiff = 99999999;
|
||||
int beststartdiff = worstdiff;
|
||||
int bestenddiff = worstdiff;
|
||||
for(int i = 0; i < 128; i+=2)
|
||||
{
|
||||
int diff = abs(sampleQueue[i].l - sampleQueue[i+1].l) + abs(sampleQueue[i].r - sampleQueue[i+1].r);
|
||||
if(diff < beststartdiff)
|
||||
{
|
||||
beststartdiff = diff;
|
||||
beststart = i;
|
||||
}
|
||||
}
|
||||
for(int i = queued-3; i > queued-3-128; i-=2)
|
||||
{
|
||||
int diff = abs(sampleQueue[i].l - sampleQueue[i+1].l) + abs(sampleQueue[i].r - sampleQueue[i+1].r);
|
||||
if(diff < bestenddiff)
|
||||
{
|
||||
bestenddiff = diff;
|
||||
bestend = i+1;
|
||||
}
|
||||
}
|
||||
|
||||
extraAtEnd = queued - bestend;
|
||||
queued = bestend - beststart;
|
||||
|
||||
int oksize = queued;
|
||||
while(oksize + queued*2 + beststart + extraAtEnd <= samples_requested)
|
||||
oksize += queued*2;
|
||||
audiosize = oksize;
|
||||
|
||||
for(int x = 0; x < beststart; x++)
|
||||
{
|
||||
emit_sample(buf,ref bufcursor,sampleQueue[x]);
|
||||
}
|
||||
//sampleQueue.erase(sampleQueue.begin(), sampleQueue.begin() + beststart);
|
||||
sampleQueue.RemoveRange(0, beststart);
|
||||
//zero 08-nov-2010: did i do this right?
|
||||
}
|
||||
|
||||
|
||||
int midpointX = audiosize >> 1;
|
||||
int midpointY = queued >> 1;
|
||||
|
||||
// all we need to do here is calculate the X position of the leftmost "B" in the above diagram.
|
||||
// TODO: we should calculate it with a simple equation like
|
||||
// midpointXOffset = min(something,somethingElse);
|
||||
// but it's a little difficult to work it out exactly
|
||||
// so here's a stupid search for the value for now:
|
||||
|
||||
int prevA = 999999;
|
||||
int midpointXOffset = queued/2;
|
||||
while(true)
|
||||
{
|
||||
int a = abs(pingpong(midpointX - midpointXOffset, queued) - midpointY) - midpointXOffset;
|
||||
if(((a > 0) != (prevA > 0) || (a < 0) != (prevA < 0)) && prevA != 999999)
|
||||
{
|
||||
if(((a + prevA)&1)!=0) // there's some sort of off-by-one problem with this search since we're moving diagonally...
|
||||
midpointXOffset++; // but this fixes it most of the time...
|
||||
break; // found it
|
||||
}
|
||||
prevA = a;
|
||||
midpointXOffset--;
|
||||
if(midpointXOffset < 0)
|
||||
{
|
||||
midpointXOffset = 0;
|
||||
break; // failed to find it. the two sides probably meet exactly in the center.
|
||||
}
|
||||
}
|
||||
|
||||
int leftMidpointX = midpointX - midpointXOffset;
|
||||
int rightMidpointX = midpointX + midpointXOffset;
|
||||
int leftMidpointY = pingpong(leftMidpointX, queued);
|
||||
int rightMidpointY = (queued-1) - pingpong((int)audiosize-1 - rightMidpointX + queued*2, queued);
|
||||
|
||||
// output the left almost-half of the sound (section "A")
|
||||
for(int x = 0; x < leftMidpointX; x++)
|
||||
{
|
||||
int i = pingpong(x, queued);
|
||||
emit_sample(buf,ref bufcursor,sampleQueue[i]);
|
||||
}
|
||||
|
||||
// output the middle stretch (section "B")
|
||||
int y = leftMidpointY;
|
||||
int dyMidLeft = (leftMidpointY < midpointY) ? 1 : -1;
|
||||
int dyMidRight = (rightMidpointY > midpointY) ? 1 : -1;
|
||||
for(int x = leftMidpointX; x < midpointX; x++, y+=dyMidLeft)
|
||||
emit_sample(buf,ref bufcursor,sampleQueue[y]);
|
||||
for(int x = midpointX; x < rightMidpointX; x++, y+=dyMidRight)
|
||||
emit_sample(buf, ref bufcursor, sampleQueue[y]);
|
||||
|
||||
// output the end of the queued sound (section "C")
|
||||
for(int x = rightMidpointX; x < audiosize; x++)
|
||||
{
|
||||
int i = (queued-1) - pingpong((int)audiosize-1 - x + queued*2, queued);
|
||||
emit_sample(buf,ref bufcursor,sampleQueue[i]);
|
||||
}
|
||||
|
||||
for(int x = 0; x < extraAtEnd; x++)
|
||||
{
|
||||
int i = queued + x;
|
||||
emit_sample(buf,ref bufcursor,sampleQueue[i]);
|
||||
}
|
||||
queued += extraAtEnd;
|
||||
audiosize += beststart + extraAtEnd;
|
||||
} //end else
|
||||
|
||||
//sampleQueue.erase(sampleQueue.begin(), sampleQueue.begin() + queued);
|
||||
sampleQueue.RemoveRange(0, queued);
|
||||
//zero 08-nov-2010: did i do this right?
|
||||
return audiosize;
|
||||
}
|
||||
else
|
||||
{
|
||||
// normal speed
|
||||
// just output the samples straightforwardly.
|
||||
//
|
||||
// at almost-full speeds (like 50/60 FPS)
|
||||
// what will happen is that we rapidly fluctuate between entering this branch
|
||||
// and entering the "slow motion speed" branch above.
|
||||
// but that's ok! because all of these branches sound similar enough that we can get away with it.
|
||||
// so the two cases actually complement each other.
|
||||
|
||||
if(audiosize >= queued)
|
||||
{
|
||||
emit_samples(buf,ref bufcursor, sampleQueue.ToArray(),0,queued);
|
||||
//sampleQueue.erase(sampleQueue.begin(), sampleQueue.begin() + queued);
|
||||
sampleQueue.RemoveRange(0, queued);
|
||||
//zero 08-nov-2010: did i do this right?
|
||||
return queued;
|
||||
}
|
||||
else
|
||||
{
|
||||
emit_samples(buf,ref bufcursor, sampleQueue.ToArray(),0,audiosize);
|
||||
//sampleQueue.erase(sampleQueue.begin(), sampleQueue.begin()+audiosize);
|
||||
sampleQueue.RemoveRange(0, audiosize);
|
||||
//zero 08-nov-2010: did i do this right?
|
||||
return audiosize;
|
||||
}
|
||||
|
||||
} //end normal speed
|
||||
|
||||
} //end if there is any work to do
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
} //output_samples
|
||||
|
||||
|
||||
}; //NitsujaSynchronizer
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
using System.Collections.Generic;
|
||||
using BizHawk.Emulation.Sound;
|
||||
|
||||
namespace BizHawk.Emulation.Sound
|
||||
{
|
||||
// This is a straightforward class to mix/chain multiple ISoundProvider sources.
|
||||
// TODO: Fine-tuned volume control would be a good thing.
|
||||
|
||||
public sealed class SoundMixer : ISoundProvider
|
||||
{
|
||||
private List<ISoundProvider> SoundProviders;
|
||||
|
||||
public SoundMixer(params ISoundProvider[] soundProviders)
|
||||
{
|
||||
SoundProviders = new List<ISoundProvider>(soundProviders);
|
||||
}
|
||||
|
||||
public void AddSource(ISoundProvider source)
|
||||
{
|
||||
SoundProviders.Add(source);
|
||||
}
|
||||
|
||||
public void DisableSource(ISoundProvider source)
|
||||
{
|
||||
SoundProviders.Remove(source);
|
||||
}
|
||||
|
||||
public void GetSamples(short[] samples)
|
||||
{
|
||||
foreach (var soundSource in SoundProviders)
|
||||
soundSource.GetSamples(samples);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
namespace BizHawk.Emulation.Sound
|
||||
{
|
||||
public static class Waves
|
||||
{
|
||||
public static readonly short[] SquareWave =
|
||||
{
|
||||
-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
|
||||
32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767
|
||||
};
|
||||
|
||||
public static readonly short[] ImperfectSquareWave =
|
||||
{
|
||||
-32768,-30145,-27852,-26213,-24902,-23592,-22282,-20971,-19988,-19005,-18350,-17694,-17366,-17039,-16711,-16711,
|
||||
32767, 30145, 27852, 26213, 24902, 23592, 22282, 20971, 19988, 19005, 18350, 17694, 17366, 17039, 16711, 16711
|
||||
};
|
||||
|
||||
public static readonly short[] NoiseWave =
|
||||
{
|
||||
32767, 32767, 32767,-32768,-32768,-32768, 32767,-32768, 32767, 32767, 32767, 32767,-32768,-32768,-32768,-32768, 32767, 32767,-32768,-32768, 32767, 32767,-32768,-32768, 32767, 32767,-32768,-32768, 32767, 32767,-32768,-32768, 32767,-32768,-32768,
|
||||
32767, 32767,-32768,-32768, 32767, 32767,-32768,-32768, 32767, 32767,-32768, 32767, 32767,-32768, 32767, 32767, 32767,-32768,-32768, 32767, 32767,-32768,-32768, 32767,-32768,-32768,-32768, 32767,-32768,-32768,-32768,-32768, 32767,-32768, 32767,
|
||||
32767, 32767,-32768,-32768,-32768,-32768,-32768, 32767,-32768,-32768, 32767,-32768, 32767,-32768, 32767,-32768, 32767, 32767,-32768,-32768,-32768, 32767,-32768, 32767,-32768,-32768, 32767, 32767,-32768, 32767,-32768,-32768,-32768, 32767,-32768,
|
||||
32767, 32767, 32767, 32767, 32767, 32767, 32767,-32768, 32767, 32767,-32768, 32767, 32767, 32767,-32768,-32768,-32768, 32767,-32768,-32768, 32767,-32768, 32767,-32768, 32767,-32768,-32768,-32768, 32767,-32768, 32767,-32768,-32768, 32767,-32768,
|
||||
-32768, 32767, 32767, 32767,-32768, 32767,-32768,-32768,-32768,-32768,-32768,-32768,-32768, 32767,-32768, 32767,-32768, 32767,-32768,-32768, 32767, 32767, 32767,-32768, 32767, 32767,-32768,-32768, 32767, 32767, 32767,-32768, 32767, 32767, 32767,
|
||||
-32768,-32768, 32767, 32767, 32767,-32768,-32768,-32768,-32768,-32768, 32767, 32767,-32768,-32768,-32768, 32767,-32768,-32768, 32767, 32767, 32767,-32768,-32768,-32768,-32768,-32768, 32767, 32767, 32767,-32768,-32768,-32768, 32767,-32768, 32767,
|
||||
32767,-32768, 32767, 32767, 32767, 32767, 32767, 32767, 32767,-32768,-32768, 32767, 32767,-32768, 32767,-32768, 32767, 32767,-32768, 32767,-32768,-32768, 32767, 32767, 32767,-32768, 32767,-32768,-32768, 32767, 32767, 32767, 32767, 32767,-32768,
|
||||
32767, 32767,-32768, 32767,-32768,-32768,-32768, 32767,-32768, 32767, 32767, 32767, 32767,-32768, 32767,-32768, 32767, 32767, 32767,-32768, 32767, 32767, 32767,-32768,-32768, 32767,-32768,-32768, 32767, 32767, 32767,-32768, 32767, 32767, 32767,
|
||||
-32768,-32768,-32768, 32767, 32767, 32767,-32768, 32767,-32768,-32768, 32767, 32767,-32768,-32768, 32767,-32768,-32768,-32768, 32767, 32767,-32768,-32768,-32768, 32767, 32767, 32767, 32767,-32768, 32767, 32767, 32767, 32767, 32767,-32768, 32767,
|
||||
32767,-32768,-32768, 32767,-32768,-32768,-32768,-32768, 32767,-32768, 32767, 32767,-32768, 32767, 32767,-32768, 32767,-32768,-32768,-32768,-32768, 32767,-32768, 32767,-32768, 32767, 32767,-32768,-32768, 32767, 32767, 32767, 32767,-32768, 32767,
|
||||
-32768, 32767, 32767, 32767,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768, 32767, 32767, 32767, 32767,-32768, 32767, 32767,-32768,-32768, 32767, 32767, 32767,-32768, 32767, 32767, 32767,-32768,-32768,-32768, 32767, 32767,-32768,
|
||||
-32768,-32768,-32768,-32768, 32767, 32767,-32768,-32768, 32767,-32768,-32768, 32767, 32767,-32768,-32768,-32768,-32768,-32768,-32768,-32768, 32767,-32768, 32767, 32767, 32767,-32768,-32768,-32768,-32768,-32768,-32768, 32767, 32767,-32768,-32768,
|
||||
32767,-32768, 32767,-32768, 32767, 32767,-32768,-32768, 32767,-32768,-32768,-32768, 32767,-32768, 32767,-32768, 32767, 32767,-32768,-32768,-32768,-32768, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767,-32768,-32768,-32768, 32767, 32767,
|
||||
-32768, 32767,-32768, 32767, 32767,-32768,-32768, 32767,-32768,-32768,-32768,-32768, 32767,-32768,-32768, 32767,-32768,-32768,-32768,-32768, 32767,-32768,-32768,-32768,-32768, 32767, 32767,-32768, 32767, 32767,-32768,-32768,-32768, 32767, 32767,
|
||||
32767,-32768,-32768, 32767,-32768,-32768,-32768,-32768, 32767,-32768, 32767,-32768,-32768, 32767, 32767, 32767,-32768,-32768,-32768,-32768,-32768, 32767
|
||||
};
|
||||
|
||||
public static readonly short[] TriangleWave =
|
||||
{
|
||||
-32768,-32513,-32257,-32001,-31745,-31489,-31233,-30977,-30721,-30465,-30209,-29953,-29697,-29441,-29185,-28929,-28673,-28417,-28161,-27905,-27649,-27393,-27137,-26881,-26625,-26369,-26113,-25857,-25601,-25345,-25089,-24833,-24577,-24321,-24065,
|
||||
-23809,-23553,-23297,-23041,-22785,-22529,-22273,-22017,-21761,-21505,-21249,-20993,-20737,-20481,-20225,-19969,-19713,-19457,-19201,-18945,-18689,-18433,-18177,-17921,-17665,-17409,-17153,-16897,-16641,-16385,-16129,-15873,-15617,-15361,-15105,
|
||||
-14849,-14593,-14337,-14081,-13825,-13569,-13313,-13057,-12801,-12545,-12289,-12033,-11777,-11521,-11265,-11009,-10753,-10497,-10241,-9985,-9729,-9473,-9217,-8961,-8705,-8449,-8193,-7937,-7681,-7425,-7169,-6913,-6657,-6401,-6145,-5889,-5633,-5377,
|
||||
-5121,-4865,-4609,-4353,-4097,-3841,-3585,-3329,-3073,-2817,-2561,-2305,-2049,-1793,-1537,-1281,-1025,-769,-513,-257,-1,255,511,767,1023,1279,1535,1791,2047,2303,2559,2815,3071,3327,3583,3839,4095,4351,4607,4863,5119,5375,5631,5887,6143,6399,
|
||||
6655,6911,7167,7423,7679,7935,8191,8447,8703,8959,9215,9471,9727,9983,10239,10495,10751,11007,11263,11519,11775,12031,12287,12543,12799,13055,13311,13567,13823,14079,14335,14591,14847,15103,15359,15615,15871,16127,16383,16639,16895,
|
||||
17151,17407,17663,17919,18175,18431,18687,18943,19199,19455,19711,19967,20223,20479,20735,20991,21247,21503,21759,22015,22271,22527,22783,23039,23295,23551,23807,24063,24319,24575,24831,25087,25343,25599,25855,26111,26367,26623,
|
||||
26879,27135,27391,27647,27903,28159,28415,28671,28927,29183,29439,29695,29951,30207,30463,30719,30975,31231,31487,31743,31999,32255,32511,32767,32511,32255,31999,31743,31487,31231,30975,30719,30463,30207,29951,29695,29439,29183,
|
||||
28927,28671,28415,28159,27903,27647,27391,27135,26879,26623,26367,26111,25855,25599,25343,25087,24831,24575,24319,24063,23807,23551,23295,23039,22783,22527,22271,22015,21759,21503,21247,20991,20735,20479,20223,19967,19711,19455,
|
||||
19199,18943,18687,18431,18175,17919,17663,17407,17151,16895,16639,16383,16127,15871,15615,15359,15103,14847,14591,14335,14079,13823,13567,13311,13055,12799,12543,12287,12031,11775,11519,11263,11007,10751,10495,10239,9983,9727,9471,
|
||||
9215,8959,8703,8447,8191,7935,7679,7423,7167,6911,6655,6399,6143,5887,5631,5375,5119,4863,4607,4351,4095,3839,3583,3327,3071,2815,2559,2303,2047,1791,1535,1279,1023,767,511,255,-1,-257,-513,-769,-1025,-1281,-1537,-1793,-2049,-2305,-2561,
|
||||
-2817,-3073,-3329,-3585,-3841,-4097,-4353,-4609,-4865,-5121,-5377,-5633,-5889,-6145,-6401,-6657,-6913,-7169,-7425,-7681,-7937,-8193,-8449,-8705,-8961,-9217,-9473,-9729,-9985,-10241,-10497,-10753,-11009,-11265,-11521,-11777,-12033,-12289,-12545,
|
||||
-12801,-13057,-13313,-13569,-13825,-14081,-14337,-14593,-14849,-15105,-15361,-15617,-15873,-16129,-16385,-16641,-16897,-17153,-17409,-17665,-17921,-18177,-18433,-18689,-18945,-19201,-19457,-19713,-19969,-20225,-20481,-20737,-20993,-21249,-21505,
|
||||
-21761,-22017,-22273,-22529,-22785,-23041,-23297,-23553,-23809,-24065,-24321,-24577,-24833,-25089,-25345,-25601,-25857,-26113,-26369,-26625,-26881,-27137,-27393,-27649,-27905,-28161,-28417,-28673,-28929,-29185,-29441,-29697,-29953,-30209,-30465,
|
||||
-30721,-30977,-31233,-31489,-31745,-32001,-32257,-32513
|
||||
};
|
||||
|
||||
/*public static short[] SineWave;
|
||||
public static short[] SawWave;
|
||||
|
||||
public static void InitWaves()
|
||||
{
|
||||
TriangleWave = new short[512];
|
||||
for (int i = 0; i < 256; i++)
|
||||
TriangleWave[i] = (short)((ushort.MaxValue*i/256)-short.MinValue);
|
||||
for (int i = 0; i < 256; i++)
|
||||
TriangleWave[256+i] = TriangleWave[256-i];
|
||||
TriangleWave[256] = short.MaxValue;
|
||||
|
||||
SawWave = new short[512];
|
||||
for (int i = 0; i < 512; i++)
|
||||
SawWave[i] = (short)((ushort.MaxValue * i / 512) - short.MinValue);
|
||||
|
||||
SineWave = new short[1024];
|
||||
for (int i=0; i<1024; i++)
|
||||
{
|
||||
SineWave[i] = (short) (Math.Sin(i*Math.PI*2/1024d)*32767);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue