This commit is contained in:
beirich 2011-01-11 02:55:51 +00:00
commit 89e4c5a674
201 changed files with 63298 additions and 0 deletions

View File

@ -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>

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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;
}
}
}

View File

@ -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";
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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
}
}

View File

@ -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 }
};
}
}

View File

@ -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

View File

@ -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
};
}
}

View File

@ -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();
}
}
}

View File

@ -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);
}
}

View File

@ -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");
}
}
}
}

View File

@ -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
}
}

View File

@ -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;
}
}
}
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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

View File

@ -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
};
}
}

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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; }
}
}
}

View File

@ -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; }
}
}

View File

@ -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();
}
}
}

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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; }
}
}
}

View File

@ -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; }
}
}

View File

@ -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();
}
}
}

View File

@ -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;
}
}
}

View File

@ -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();
}
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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);
}
}
}

View File

@ -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
};
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}
}

View File

@ -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>

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -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;
}
}
}
}

View File

@ -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.

View File

@ -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;
}
}
}

View File

@ -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();
}
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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]; }
}
}
}

View File

@ -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();
}
}
}

View File

@ -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]; }
}
}
}

View File

@ -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
}
}
}

View File

@ -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;
}
}
}

View File

@ -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]; }
}
}
}

View File

@ -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; }
}
}

View File

@ -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];
}
}
}

View File

@ -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)
{
}
}
}

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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.

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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"};
}
}

View File

@ -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]; }
}
}
}

View File

@ -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;
//}
}
}
}

View File

@ -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;
}
}
}

View File

@ -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; }
}
}
}

View File

@ -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);
}
}
}

View File

@ -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; } }
}
}

View File

@ -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; }
}
}

View File

@ -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) { }
}
}

View File

@ -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; }
}
}
}

View File

@ -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; }
}
}

View File

@ -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();
}
}

View File

@ -0,0 +1,9 @@
namespace BizHawk
{
public interface IGame
{
byte[] GetRomData();
string[] GetOptions();
string Name { get; }
}
}

View File

@ -0,0 +1,7 @@
namespace BizHawk
{
public interface ISoundProvider
{
void GetSamples(short[] samples);
}
}

View File

@ -0,0 +1,11 @@
namespace BizHawk
{
public interface IVideoProvider
{
int[] GetVideoBuffer();
int BufferWidth { get; }
int BufferHeight { get; }
int BackgroundColor { get; }
}
}

75
BizHawk.Emulation/Log.cs Normal file
View File

@ -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);
}
}
}

View File

@ -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)

View File

@ -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")]

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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();
}
}
}

View File

@ -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
}

View File

@ -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);
}
}
}

View File

@ -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