Move interfaces and base implemenations from emulation to emulation.common

This commit is contained in:
adelikat 2013-11-04 01:39:19 +00:00
parent 026072ee68
commit 9751fd5a1a
86 changed files with 2688 additions and 2569 deletions

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{

View File

@ -1,6 +1,8 @@
using System;
using System.IO;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{
public class CoreFileProvider : ICoreFileProvider

View File

@ -1,5 +1,7 @@
using System.IO;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{
public class SaveSlotManager

View File

@ -1,6 +1,7 @@
using System;
using LuaInterface;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{

View File

@ -1,6 +1,8 @@
using System;
using System.Linq;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{
public class MemoryLuaLibrary : LuaLibraryBase

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{

View File

@ -2,6 +2,8 @@
using System.IO;
using System.Globalization;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{
public class Movie

View File

@ -5,6 +5,7 @@ using System.Text;
using System.IO;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Text;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{

View File

@ -1,6 +1,8 @@
using System;
using System.IO;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{
public class MovieSession

View File

@ -1,4 +1,5 @@
using System;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{

View File

@ -5,6 +5,8 @@ using System.IO;
using System.Linq;
using System.Text;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{
public class CheatList : IEnumerable<Cheat>

View File

@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{

View File

@ -2,6 +2,8 @@
using System.Globalization;
using System.Collections.Generic;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{
public abstract class Watch

View File

@ -5,6 +5,8 @@ using System.IO;
using System.Linq;
using System.Text;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{
public class WatchList : IEnumerable<Watch>

View File

@ -4,6 +4,7 @@ using System.IO;
using System.Runtime.InteropServices;
using BizHawk.Client.Common;
using BizHawk.Emulation.Common;
//some helpful p/invoke from http://www.codeproject.com/KB/audio-video/Motion_Detection.aspx?msg=1142967

View File

@ -4,6 +4,8 @@ using System.Linq;
using System.Text;
using System.Drawing;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.EmuHawk
{
/// <summary>

View File

@ -4,6 +4,7 @@ using System.Text;
using System.Diagnostics;
using BizHawk.Client.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.EmuHawk
{

View File

@ -6,6 +6,7 @@ using System.IO;
using System.Drawing;
using BizHawk.Client.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.EmuHawk
{

View File

@ -2,6 +2,8 @@
using System.Collections;
using System.Collections.Generic;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.EmuHawk
{
public interface IVideoWriter : IDisposable

View File

@ -7,6 +7,7 @@ using System.Text;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using ICSharpCode.SharpZipLib.Zip.Compression;
using BizHawk.Emulation.Common;
using BizHawk.Client.Common;
namespace BizHawk.Client.EmuHawk

View File

@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.EmuHawk
{
/// <summary>

View File

@ -5,6 +5,7 @@ using System.Text;
using System.IO;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.EmuHawk
{

View File

@ -13,6 +13,7 @@ using System.Drawing.Imaging;
//using d3d=SlimDX.Direct3D9;
using BizHawk.Common;
using BizHawk.Emulation.Common;
using BizHawk.Client.Common;
namespace BizHawk.Client.EmuHawk

View File

@ -3,12 +3,13 @@ using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using BizHawk.Client.Common;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Consoles.Calculator;
using BizHawk.Emulation.Consoles.GB;
using BizHawk.Emulation.Consoles.Nintendo.SNES;
using BizHawk.Client.Common;
namespace BizHawk.Client.EmuHawk
{
partial class MainForm

View File

@ -7,6 +7,7 @@ using SlimDX.Multimedia;
#endif
using BizHawk.Client.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.EmuHawk
{

View File

@ -4,6 +4,7 @@ using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using BizHawk.Emulation.Common;
using BizHawk.Client.Common;
namespace BizHawk.Client.EmuHawk

View File

@ -2,13 +2,14 @@
using System.Windows.Forms;
using System.IO;
using BizHawk.Client.Common;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Consoles.GB;
using BizHawk.Emulation.Consoles.Nintendo.SNES;
using BizHawk.Emulation.Consoles.Sega;
using BizHawk.Emulation.Consoles.Nintendo;
using BizHawk.Emulation.Consoles.Coleco;
using BizHawk.Emulation.Consoles.Nintendo.N64;
using BizHawk.Client.Common;
namespace BizHawk.Client.EmuHawk
{

View File

@ -9,6 +9,7 @@ using System.Threading.Tasks;
using System.Windows.Forms;
using BizHawk.Client.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.EmuHawk
{

View File

@ -10,6 +10,7 @@ using System.IO;
using BizHawk.Common;
using BizHawk.Client.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.EmuHawk
{

View File

@ -5,6 +5,7 @@ using System.Text;
using System.Globalization;
using BizHawk.Client.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.EmuHawk
{

View File

@ -7,6 +7,7 @@ using System.Windows.Forms;
using System.IO;
using BizHawk.Client.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.EmuHawk
{

View File

@ -7,6 +7,7 @@ using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.IO;
using BizHawk.Emulation.Common;
using BizHawk.Client.Common;
namespace BizHawk.Client.EmuHawk

View File

@ -6,6 +6,7 @@ using System.Text;
using System.Windows.Forms;
using BizHawk.Client.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.EmuHawk
{

View File

@ -5,6 +5,7 @@ using System.Linq;
using System.Windows.Forms;
using BizHawk.Client.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.EmuHawk
{

View File

@ -44,6 +44,15 @@
<Compile Include="Database\Database.cs" />
<Compile Include="Database\FirmwareDatabase.cs" />
<Compile Include="Database\GameInfo.cs" />
<Compile Include="Interfaces\Base Implementations\NullController.cs" />
<Compile Include="Interfaces\Base Implementations\NullEmulator.cs" />
<Compile Include="Interfaces\CoreComms.cs" />
<Compile Include="Interfaces\IController.cs" />
<Compile Include="Interfaces\ICoreFileProvider.cs" />
<Compile Include="Interfaces\IEmulator.cs" />
<Compile Include="Interfaces\ISoundProvider.cs" />
<Compile Include="Interfaces\ISyncSoundProvider.cs" />
<Compile Include="Interfaces\IVideoProvider.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>

View File

@ -1,4 +1,4 @@
namespace BizHawk
namespace BizHawk.Emulation.Common
{
public class NullController : IController
{

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.IO;
using BizHawk.Common;
namespace BizHawk
namespace BizHawk.Emulation.Common
{
public class NullEmulator : IEmulator, IVideoProvider, ISoundProvider
{

View File

@ -1,7 +1,8 @@
using System;
using System.IO;
using System.Text;
namespace BizHawk
namespace BizHawk.Emulation.Common
{
public class CoreComm
{

View File

@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
namespace BizHawk
namespace BizHawk.Emulation.Common
{
// doesn't do what is desired
// http://connect.microsoft.com/VisualStudio/feedback/details/459307/extension-add-methods-are-not-considered-in-c-collection-initializers

View File

@ -1,6 +1,6 @@
using System.IO;
namespace BizHawk
namespace BizHawk.Emulation.Common
{
public interface ICoreFileProvider
{

View File

@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.IO;
namespace BizHawk
namespace BizHawk.Emulation.Common
{
public interface IEmulator : IDisposable
{

View File

@ -0,0 +1,9 @@
namespace BizHawk.Emulation.Common
{
public interface ISoundProvider
{
void GetSamples(short[] samples);
void DiscardSamples();
int MaxVolume { get; set; }
}
}

View File

@ -1,4 +1,4 @@
namespace BizHawk
namespace BizHawk.Emulation.Common
{
public interface ISyncSoundProvider
{

View File

@ -0,0 +1,13 @@
namespace BizHawk.Emulation.Common
{
public interface IVideoProvider
{
int[] GetVideoBuffer();
int VirtualWidth { get; } // Used for controlling aspect ratio. Just return BufferWidth if you dont know what to do with this.
int BufferWidth { get; }
int BufferHeight { get; }
int BackgroundColor { get; }
}
}

View File

@ -452,11 +452,6 @@
<Compile Include="CPUs\Z80\Registers.cs" />
<Compile Include="CPUs\Z80\Tables.cs" />
<Compile Include="CPUs\Z80\Z80A.cs" />
<Compile Include="Interfaces\Base Implementations\NullController.cs" />
<Compile Include="Interfaces\Base Implementations\NullEmulator.cs" />
<Compile Include="Interfaces\CoreComms.cs" />
<Compile Include="Interfaces\ICoreFileProvider.cs" />
<Compile Include="Interfaces\ISyncSoundProvider.cs" />
<Compile Include="Properties\svnrev.cs" />
<Compile Include="Sound\CDAudio.cs" />
<Compile Include="Sound\MMC5Audio.cs" />
@ -467,10 +462,6 @@
<Compile Include="Sound\Utilities\SpeexResampler.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\ISoundProvider.cs" />
<Compile Include="Interfaces\IVideoProvider.cs" />
<Compile Include="Consoles\PC Engine\Input.cs" />
<Compile Include="Consoles\PC Engine\MemoryMap.cs" />
<Compile Include="Consoles\PC Engine\MemoryMap.SF2.cs" />

View File

@ -1,7 +1,10 @@
using BizHawk.Emulation.Computers.Commodore64.Cartridge;
using System.IO;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Computers.Commodore64.Cartridge;
using BizHawk.Emulation.Computers.Commodore64.Disk;
using BizHawk.Emulation.Computers.Commodore64.MOS;
using System.IO;
namespace BizHawk.Emulation.Computers.Commodore64
{
@ -16,15 +19,15 @@ namespace BizHawk.Emulation.Computers.Commodore64
private Motherboard board;
private bool loadPrg;
private byte[] GetFirmware(string name, int length)
{
byte[] result = new byte[length];
using (Stream source = CoreComm.CoreFileProvider.OpenFirmware("C64", name))
{
source.Read(result, 0, length);
}
return result;
}
private byte[] GetFirmware(string name, int length)
{
byte[] result = new byte[length];
using (Stream source = CoreComm.CoreFileProvider.OpenFirmware("C64", name))
{
source.Read(result, 0, length);
}
return result;
}
private void Init(Region initRegion)
{
@ -43,14 +46,14 @@ namespace BizHawk.Emulation.Computers.Commodore64
switch (inputFileInfo.Extension.ToUpper())
{
case @".CRT":
Cart cart = Cart.Load(inputFileInfo.Data);
Cart cart = Cart.Load(inputFileInfo.Data);
if (cart != null)
{
board.cartPort.Connect(cart);
}
break;
case @".PRG":
if (inputFileInfo.Data.Length > 2)
if (inputFileInfo.Data.Length > 2)
loadPrg = true;
break;
}
@ -58,10 +61,10 @@ namespace BizHawk.Emulation.Computers.Commodore64
private void InitRoms()
{
byte[] basicRom = GetFirmware("Basic", 0x2000);
byte[] charRom = GetFirmware("Chargen", 0x1000);
byte[] kernalRom = GetFirmware("Kernal", 0x2000);
byte[] basicRom = GetFirmware("Basic", 0x2000);
byte[] charRom = GetFirmware("Chargen", 0x1000);
byte[] kernalRom = GetFirmware("Kernal", 0x2000);
board.basicRom = new Chip23XX(Chip23XXmodel.Chip2364, basicRom);
board.kernalRom = new Chip23XX(Chip23XXmodel.Chip2364, kernalRom);
board.charRom = new Chip23XX(Chip23XXmodel.Chip2332, charRom);
@ -74,7 +77,7 @@ namespace BizHawk.Emulation.Computers.Commodore64
get
{
//return (disk.PeekVia1(0x00) & 0x08) != 0;
return false;
return false;
}
}

View File

@ -2,6 +2,7 @@
using System.Reflection;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Computers.Commodore64
{

View File

@ -1,6 +1,7 @@
using System.IO;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Computers.Commodore64
{

View File

@ -3,13 +3,15 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Computers.Commodore64.Experimental.Chips.Internals
{
sealed public partial class Sid
{
public ISoundProvider GetSoundProvider()
{
return new NullSound();
}
}
sealed public partial class Sid
{
public ISoundProvider GetSoundProvider()
{
return new NullSound();
}
}
}

View File

@ -4,6 +4,7 @@ using System.Linq;
using System.Text;
using BizHawk.Common;
using BizHawk.Emulation.Common;
#pragma warning disable 649 //adelikat: Disable dumb warnings until this file is complete
@ -17,24 +18,24 @@ namespace BizHawk.Emulation.Computers.Commodore64.Experimental.Chips.Internals
// palette
static private int[] palette =
{
Colors.ARGB(0x00, 0x00, 0x00),
Colors.ARGB(0xFF, 0xFF, 0xFF),
Colors.ARGB(0x68, 0x37, 0x2B),
Colors.ARGB(0x70, 0xA4, 0xB2),
Colors.ARGB(0x6F, 0x3D, 0x86),
Colors.ARGB(0x58, 0x8D, 0x43),
Colors.ARGB(0x35, 0x28, 0x79),
Colors.ARGB(0xB8, 0xC7, 0x6F),
Colors.ARGB(0x6F, 0x4F, 0x25),
Colors.ARGB(0x43, 0x39, 0x00),
Colors.ARGB(0x9A, 0x67, 0x59),
Colors.ARGB(0x44, 0x44, 0x44),
Colors.ARGB(0x6C, 0x6C, 0x6C),
Colors.ARGB(0x9A, 0xD2, 0x84),
Colors.ARGB(0x6C, 0x5E, 0xB5),
Colors.ARGB(0x95, 0x95, 0x95)
};
{
Colors.ARGB(0x00, 0x00, 0x00),
Colors.ARGB(0xFF, 0xFF, 0xFF),
Colors.ARGB(0x68, 0x37, 0x2B),
Colors.ARGB(0x70, 0xA4, 0xB2),
Colors.ARGB(0x6F, 0x3D, 0x86),
Colors.ARGB(0x58, 0x8D, 0x43),
Colors.ARGB(0x35, 0x28, 0x79),
Colors.ARGB(0xB8, 0xC7, 0x6F),
Colors.ARGB(0x6F, 0x4F, 0x25),
Colors.ARGB(0x43, 0x39, 0x00),
Colors.ARGB(0x9A, 0x67, 0x59),
Colors.ARGB(0x44, 0x44, 0x44),
Colors.ARGB(0x6C, 0x6C, 0x6C),
Colors.ARGB(0x9A, 0xD2, 0x84),
Colors.ARGB(0x6C, 0x5E, 0xB5),
Colors.ARGB(0x95, 0x95, 0x95)
};
public int[] GetVideoBuffer()
{

View File

@ -1,5 +1,7 @@
using System.Drawing;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Computers.Commodore64.MOS
{

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Consoles.Atari
{

View File

@ -1,5 +1,7 @@
using System;
using EMU7800.Core;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation
{

View File

@ -1,11 +1,13 @@
namespace BizHawk.Emulation.Consoles.Coleco
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Consoles.Coleco
{
public partial class ColecoVision
{
public static readonly ControllerDefinition ColecoVisionControllerDefinition = new ControllerDefinition
{
Name = "ColecoVision Basic Controller",
BoolButtons =
public partial class ColecoVision
{
public static readonly ControllerDefinition ColecoVisionControllerDefinition = new ControllerDefinition
{
Name = "ColecoVision Basic Controller",
BoolButtons =
{
"P1 Up", "P1 Down", "P1 Left", "P1 Right",
"P1 L", "P1 R",
@ -17,103 +19,103 @@
"P2 Key0", "P2 Key1", "P2 Key2", "P2 Key3", "P2 Key4", "P2 Key5",
"P2 Key6", "P2 Key7", "P2 Key8", "P2 Key9", "P2 Star", "P2 Pound"
}
};
};
public ControllerDefinition ControllerDefinition { get { return ColecoVisionControllerDefinition; } }
public IController Controller { get; set; }
public ControllerDefinition ControllerDefinition { get { return ColecoVisionControllerDefinition; } }
public IController Controller { get; set; }
enum InputPortMode { Left, Right }
InputPortMode InputPortSelection;
enum InputPortMode { Left, Right }
InputPortMode InputPortSelection;
byte ReadController1()
{
islag = false;
byte ReadController1()
{
islag = false;
if (InputPortSelection == InputPortMode.Left)
{
byte retval = 0x7F;
if (Controller["P1 Up"]) retval &= 0xFE;
if (Controller["P1 Right"]) retval &= 0xFD;
if (Controller["P1 Down"]) retval &= 0xFB;
if (Controller["P1 Left"]) retval &= 0xF7;
if (Controller["P1 L"]) retval &= 0x3F;
return retval;
}
if (InputPortSelection == InputPortMode.Left)
{
byte retval = 0x7F;
if (Controller["P1 Up"]) retval &= 0xFE;
if (Controller["P1 Right"]) retval &= 0xFD;
if (Controller["P1 Down"]) retval &= 0xFB;
if (Controller["P1 Left"]) retval &= 0xF7;
if (Controller["P1 L"]) retval &= 0x3F;
return retval;
}
if (InputPortSelection == InputPortMode.Right)
{
byte retval = 0;
if (InputPortSelection == InputPortMode.Right)
{
byte retval = 0;
// 0x00;
if (Controller["P1 Key8"]) retval = 0x01;
if (Controller["P1 Key4"]) retval = 0x02;
if (Controller["P1 Key5"]) retval = 0x03;
// 0x04;
if (Controller["P1 Key7"]) retval = 0x05;
if (Controller["P1 Pound"]) retval = 0x06;
if (Controller["P1 Key2"]) retval = 0x07;
// 0x08;
if (Controller["P1 Star"]) retval = 0x09;
if (Controller["P1 Key0"]) retval = 0x0A;
if (Controller["P1 Key9"]) retval = 0x0B;
if (Controller["P1 Key3"]) retval = 0x0C;
if (Controller["P1 Key1"]) retval = 0x0D;
if (Controller["P1 Key6"]) retval = 0x0E;
// 0x00;
if (Controller["P1 Key8"]) retval = 0x01;
if (Controller["P1 Key4"]) retval = 0x02;
if (Controller["P1 Key5"]) retval = 0x03;
// 0x04;
if (Controller["P1 Key7"]) retval = 0x05;
if (Controller["P1 Pound"]) retval = 0x06;
if (Controller["P1 Key2"]) retval = 0x07;
// 0x08;
if (Controller["P1 Star"]) retval = 0x09;
if (Controller["P1 Key0"]) retval = 0x0A;
if (Controller["P1 Key9"]) retval = 0x0B;
if (Controller["P1 Key3"]) retval = 0x0C;
if (Controller["P1 Key1"]) retval = 0x0D;
if (Controller["P1 Key6"]) retval = 0x0E;
if (Controller["P1 R"] == false) retval |= 0x40;
retval |= 0x30; // always set these bits
return retval;
}
if (Controller["P1 R"] == false) retval |= 0x40;
retval |= 0x30; // always set these bits
return retval;
}
return 0x7F;
}
return 0x7F;
}
byte ReadController2()
{
islag = false;
byte ReadController2()
{
islag = false;
if (InputPortSelection == InputPortMode.Left)
{
byte retval = 0x7F;
if (Controller["P2 Up"]) retval &= 0xFE;
if (Controller["P2 Right"]) retval &= 0xFD;
if (Controller["P2 Down"]) retval &= 0xFB;
if (Controller["P2 Left"]) retval &= 0xF7;
if (Controller["P2 L"]) retval &= 0x3F;
return retval;
}
if (InputPortSelection == InputPortMode.Left)
{
byte retval = 0x7F;
if (Controller["P2 Up"]) retval &= 0xFE;
if (Controller["P2 Right"]) retval &= 0xFD;
if (Controller["P2 Down"]) retval &= 0xFB;
if (Controller["P2 Left"]) retval &= 0xF7;
if (Controller["P2 L"]) retval &= 0x3F;
return retval;
}
if (InputPortSelection == InputPortMode.Right)
{
byte retval = 0;
if (InputPortSelection == InputPortMode.Right)
{
byte retval = 0;
// 0x00;
if (Controller["P2 Key8"]) retval = 0x01;
if (Controller["P2 Key4"]) retval = 0x02;
if (Controller["P2 Key5"]) retval = 0x03;
// 0x04;
if (Controller["P2 Key7"]) retval = 0x05;
if (Controller["P2 Pound"]) retval = 0x06;
if (Controller["P2 Key2"]) retval = 0x07;
// 0x08;
if (Controller["P2 Star"]) retval = 0x09;
if (Controller["P2 Key0"]) retval = 0x0A;
if (Controller["P2 Key9"]) retval = 0x0B;
if (Controller["P2 Key3"]) retval = 0x0C;
if (Controller["P2 Key1"]) retval = 0x0D;
if (Controller["P2 Key6"]) retval = 0x0E;
// 0x00;
if (Controller["P2 Key8"]) retval = 0x01;
if (Controller["P2 Key4"]) retval = 0x02;
if (Controller["P2 Key5"]) retval = 0x03;
// 0x04;
if (Controller["P2 Key7"]) retval = 0x05;
if (Controller["P2 Pound"]) retval = 0x06;
if (Controller["P2 Key2"]) retval = 0x07;
// 0x08;
if (Controller["P2 Star"]) retval = 0x09;
if (Controller["P2 Key0"]) retval = 0x0A;
if (Controller["P2 Key9"]) retval = 0x0B;
if (Controller["P2 Key3"]) retval = 0x0C;
if (Controller["P2 Key1"]) retval = 0x0D;
if (Controller["P2 Key6"]) retval = 0x0E;
if (Controller["P2 R"] == false) retval |= 0x40;
retval |= 0x30; // always set these bits
return retval;
}
if (Controller["P2 R"] == false) retval |= 0x40;
retval |= 0x30; // always set these bits
return retval;
}
return 0x7F;
}
return 0x7F;
}
public int Frame { get; set; }
public int LagCount { get { return _lagcount; } set { _lagcount = value; } }
public int Frame { get; set; }
public int LagCount { get { return _lagcount; } set { _lagcount = value; } }
public bool IsLagFrame
{
get
@ -121,8 +123,8 @@
return islag;
}
}
private int _lagcount = 0;
private bool islag = true;
}
}
}

View File

@ -3,6 +3,7 @@ using System.Globalization;
using System.IO;
using BizHawk.Common;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.CPUs.Z80;
namespace BizHawk.Emulation.Consoles.Coleco

View File

@ -1,4 +1,5 @@
using System;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Consoles.Intellivision
{

View File

@ -4,6 +4,7 @@ using System.Runtime.InteropServices;
using System.IO;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Consoles.Nintendo.GBA
{

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using BizHawk.Common;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Sound;
//http://wiki.nesdev.com/w/index.php/APU_Mixer_Emulation

View File

@ -1,5 +1,7 @@
using System;
using BizHawk.Common;
using BizHawk.Emulation.Common;
//simplifications/approximations:
//* "Note that no commercial games rely on this mirroring -- therefore you can take the easy way out and simply give all MMC5 games 64k PRG-RAM."

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Runtime.CompilerServices;
using BizHawk.Common;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.CPUs.M6502;
#pragma warning disable 162

View File

@ -2,7 +2,9 @@
using System.Linq;
using System.Text;
using System.IO;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Consoles.Nintendo
{

View File

@ -490,9 +490,9 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
get
{
if (api.snes_get_region() == LibsnesApi.SNES_REGION.NTSC)
return BizHawk.DisplayType.NTSC;
return DisplayType.NTSC;
else
return BizHawk.DisplayType.PAL;
return DisplayType.PAL;
}
}

View File

@ -4,192 +4,193 @@ using System.IO;
using System.Globalization;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Consoles.TurboGrafx
{
public sealed class ADPCM : ISoundProvider
{
ScsiCDBus SCSI;
PCEngine pce;
MetaspuSoundProvider SoundProvider = new MetaspuSoundProvider(ESynchMethod.ESynchMethod_V);
public sealed class ADPCM : ISoundProvider
{
ScsiCDBus SCSI;
PCEngine pce;
MetaspuSoundProvider SoundProvider = new MetaspuSoundProvider(ESynchMethod.ESynchMethod_V);
// ***************************************************************************
// ***************************************************************************
public ushort IOAddress;
public ushort ReadAddress;
public ushort WriteAddress;
public ushort AdpcmLength;
public ushort IOAddress;
public ushort ReadAddress;
public ushort WriteAddress;
public ushort AdpcmLength;
public int ReadTimer, WriteTimer;
public byte ReadBuffer, WriteBuffer;
public bool ReadPending, WritePending;
public int ReadTimer, WriteTimer;
public byte ReadBuffer, WriteBuffer;
public bool ReadPending, WritePending;
public byte[] RAM = new byte[0x10000];
// ***************************************************************************
public bool AdpcmIsPlaying { get; private set; }
public bool HalfReached { get; private set; }
public bool EndReached { get; private set; }
public bool AdpcmBusyWriting { get { return AdpcmCdDmaRequested; } }
public bool AdpcmBusyReading { get { return ReadPending; } }
public bool AdpcmCdDmaRequested { get { return (Port180B & 3) != 0; } }
public byte[] RAM = new byte[0x10000];
// ***************************************************************************
// ***************************************************************************
public byte Port180A
{
set { WritePending = true; WriteTimer = 24; WriteBuffer = value; }
get { ReadPending = true; ReadTimer = 24; return ReadBuffer; }
}
public bool AdpcmIsPlaying { get; private set; }
public bool HalfReached { get; private set; }
public bool EndReached { get; private set; }
public bool AdpcmBusyWriting { get { return AdpcmCdDmaRequested; } }
public bool AdpcmBusyReading { get { return ReadPending; } }
public bool AdpcmCdDmaRequested { get { return (Port180B & 3) != 0; } }
public byte Port180B;
public byte Port180D;
// ***************************************************************************
byte port180E;
public byte Port180E
{
get { return port180E; }
set
{
port180E = value;
float khz = 32 / (16 - (Port180E & 0x0F));
destSamplesPerSourceSample = 44.1f / khz;
}
}
public byte Port180A
{
set { WritePending = true; WriteTimer = 24; WriteBuffer = value; }
get { ReadPending = true; ReadTimer = 24; return ReadBuffer; }
}
// ***************************************************************************
public byte Port180B;
public byte Port180D;
public ADPCM(PCEngine pcEngine, ScsiCDBus scsi)
{
pce = pcEngine;
SCSI = scsi;
MaxVolume = 24576;
}
byte port180E;
public byte Port180E
{
get { return port180E; }
set
{
port180E = value;
float khz = 32 / (16 - (Port180E & 0x0F));
destSamplesPerSourceSample = 44.1f / khz;
}
}
public void AdpcmControlWrite(byte value)
{
//Log.Error("CD","ADPCM CONTROL WRITE {0:X2}",value);
if ((Port180D & 0x80) != 0 && (value & 0x80) == 0)
{
ReadAddress = 0;
WriteAddress = 0;
IOAddress = 0;
nibble = false;
AdpcmIsPlaying = false;
HalfReached = false;
EndReached = false;
playingSample = 0;
Playback44khzTimer = 0;
magnitude = 0;
}
// ***************************************************************************
if ((value & 8) != 0)
{
ReadAddress = IOAddress;
if ((value & 4) == 0)
ReadAddress--;
}
public ADPCM(PCEngine pcEngine, ScsiCDBus scsi)
{
pce = pcEngine;
SCSI = scsi;
MaxVolume = 24576;
}
if ((Port180D & 2) == 0 && (value & 2) != 0)
{
WriteAddress = IOAddress;
if ((value & 1) == 0)
WriteAddress--;
}
public void AdpcmControlWrite(byte value)
{
//Log.Error("CD","ADPCM CONTROL WRITE {0:X2}",value);
if ((Port180D & 0x80) != 0 && (value & 0x80) == 0)
{
ReadAddress = 0;
WriteAddress = 0;
IOAddress = 0;
nibble = false;
AdpcmIsPlaying = false;
HalfReached = false;
EndReached = false;
playingSample = 0;
Playback44khzTimer = 0;
magnitude = 0;
}
if ((value & 0x10) != 0)
{
AdpcmLength = IOAddress;
EndReached = false;
}
if ((value & 8) != 0)
{
ReadAddress = IOAddress;
if ((value & 4) == 0)
ReadAddress--;
}
if (AdpcmIsPlaying && (value & 0x20) == 0)
AdpcmIsPlaying = false; // clearing this bit stops playback
if ((Port180D & 2) == 0 && (value & 2) != 0)
{
WriteAddress = IOAddress;
if ((value & 1) == 0)
WriteAddress--;
}
if (AdpcmIsPlaying == false && (value & 0x20) != 0)
{
if ((value & 0x40) == 0)
Console.WriteLine("a thing thats normally set is not set");
if ((value & 0x10) != 0)
{
AdpcmLength = IOAddress;
EndReached = false;
}
AdpcmIsPlaying = true;
playingSample = 2048;
magnitude = 0;
Playback44khzTimer = 0;
}
if (AdpcmIsPlaying && (value & 0x20) == 0)
AdpcmIsPlaying = false; // clearing this bit stops playback
Port180D = value;
}
if (AdpcmIsPlaying == false && (value & 0x20) != 0)
{
if ((value & 0x40) == 0)
Console.WriteLine("a thing thats normally set is not set");
public void Think(int cycles)
{
Playback44khzTimer -= cycles;
if (Playback44khzTimer < 0)
{
Playback44khzTimer += 162.81f; // # of CPU cycles that translate to one 44100hz sample.
AdpcmEmitSample();
}
AdpcmIsPlaying = true;
playingSample = 2048;
magnitude = 0;
Playback44khzTimer = 0;
}
if (ReadTimer > 0) ReadTimer -= cycles;
if (WriteTimer > 0) WriteTimer -= cycles;
Port180D = value;
}
if (ReadPending && ReadTimer <= 0)
{
ReadBuffer = RAM[ReadAddress++];
ReadPending = false;
if (AdpcmLength > ushort.MinValue)
AdpcmLength--;
else
{
EndReached = true;
HalfReached = false;
//Port180D &= 0x9F;
}
}
public void Think(int cycles)
{
Playback44khzTimer -= cycles;
if (Playback44khzTimer < 0)
{
Playback44khzTimer += 162.81f; // # of CPU cycles that translate to one 44100hz sample.
AdpcmEmitSample();
}
if (WritePending && WriteTimer <= 0)
{
RAM[WriteAddress++] = WriteBuffer;
WritePending = false;
if (AdpcmLength < ushort.MaxValue)
AdpcmLength++;
HalfReached = AdpcmLength < 0x8000;
}
if (ReadTimer > 0) ReadTimer -= cycles;
if (WriteTimer > 0) WriteTimer -= cycles;
if (AdpcmCdDmaRequested)
{
if (SCSI.REQ && SCSI.IO && !SCSI.CD && !SCSI.ACK)
{
byte dmaByte = SCSI.DataBits;
RAM[WriteAddress++] = dmaByte;
AdpcmLength++;
if (ReadPending && ReadTimer <= 0)
{
ReadBuffer = RAM[ReadAddress++];
ReadPending = false;
if (AdpcmLength > ushort.MinValue)
AdpcmLength--;
else
{
EndReached = true;
HalfReached = false;
//Port180D &= 0x9F;
}
}
SCSI.ACK = false;
SCSI.REQ = false;
SCSI.Think();
}
if (WritePending && WriteTimer <= 0)
{
RAM[WriteAddress++] = WriteBuffer;
WritePending = false;
if (AdpcmLength < ushort.MaxValue)
AdpcmLength++;
HalfReached = AdpcmLength < 0x8000;
}
if (SCSI.DataTransferInProgress == false)
Port180B = 0;
}
if (AdpcmCdDmaRequested)
{
if (SCSI.REQ && SCSI.IO && !SCSI.CD && !SCSI.ACK)
{
byte dmaByte = SCSI.DataBits;
RAM[WriteAddress++] = dmaByte;
AdpcmLength++;
pce.IntADPCM = HalfReached;
pce.IntStop = EndReached;
pce.RefreshIRQ2();
}
// ***************************************************************************
// Playback Functions
// ***************************************************************************
SCSI.ACK = false;
SCSI.REQ = false;
SCSI.Think();
}
float Playback44khzTimer;
int playingSample;
float nextSampleTimer;
float destSamplesPerSourceSample;
bool nibble;
int magnitude;
if (SCSI.DataTransferInProgress == false)
Port180B = 0;
}
static readonly int[] StepSize =
pce.IntADPCM = HalfReached;
pce.IntStop = EndReached;
pce.RefreshIRQ2();
}
// ***************************************************************************
// Playback Functions
// ***************************************************************************
float Playback44khzTimer;
int playingSample;
float nextSampleTimer;
float destSamplesPerSourceSample;
bool nibble;
int magnitude;
static readonly int[] StepSize =
{
0x0002, 0x0006, 0x000A, 0x000E, 0x0012, 0x0016, 0x001A, 0x001E,
0x0002, 0x0006, 0x000A, 0x000E, 0x0013, 0x0017, 0x001B, 0x001F,
@ -242,241 +243,241 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
0x00C2, 0x0246, 0x03CA, 0x054E, 0x06D2, 0x0856, 0x09DA, 0x0B5E
};
static readonly int[] StepFactor = { -1, -1, -1, -1, 2, 4, 6, 8 };
static readonly int[] StepFactor = { -1, -1, -1, -1, 2, 4, 6, 8 };
int AddClamped(int num1, int num2, int min, int max)
{
int result = num1 + num2;
if (result < min) return min;
if (result > max) return max;
return result;
}
int AddClamped(int num1, int num2, int min, int max)
{
int result = num1 + num2;
if (result < min) return min;
if (result > max) return max;
return result;
}
byte ReadNibble()
{
byte value;
if (nibble == false)
value = (byte)(RAM[ReadAddress] >> 4);
else
{
value = (byte)(RAM[ReadAddress] & 0xF);
AdpcmLength--;
ReadAddress++;
}
byte ReadNibble()
{
byte value;
if (nibble == false)
value = (byte)(RAM[ReadAddress] >> 4);
else
{
value = (byte)(RAM[ReadAddress] & 0xF);
AdpcmLength--;
ReadAddress++;
}
nibble ^= true;
return value;
}
nibble ^= true;
return value;
}
void DecodeAdpcmSample()
{
byte sample = ReadNibble();
bool positive = (sample & 8) == 0;
int mag = sample & 7;
int m = StepFactor[mag];
int adjustment = StepSize[(magnitude * 8) + mag];
magnitude = AddClamped(magnitude, m, 0, 48);
if (positive == false) adjustment *= -1;
playingSample = AddClamped(playingSample, adjustment, 0, 4095);
}
void DecodeAdpcmSample()
{
byte sample = ReadNibble();
bool positive = (sample & 8) == 0;
int mag = sample & 7;
int m = StepFactor[mag];
int adjustment = StepSize[(magnitude * 8) + mag];
magnitude = AddClamped(magnitude, m, 0, 48);
if (positive == false) adjustment *= -1;
playingSample = AddClamped(playingSample, adjustment, 0, 4095);
}
void AdpcmEmitSample()
{
if (AdpcmIsPlaying == false)
SoundProvider.buffer.enqueue_sample(0, 0);
else
{
if (nextSampleTimer <= 0)
{
DecodeAdpcmSample();
nextSampleTimer += destSamplesPerSourceSample;
}
nextSampleTimer--;
void AdpcmEmitSample()
{
if (AdpcmIsPlaying == false)
SoundProvider.buffer.enqueue_sample(0, 0);
else
{
if (nextSampleTimer <= 0)
{
DecodeAdpcmSample();
nextSampleTimer += destSamplesPerSourceSample;
}
nextSampleTimer--;
HalfReached = AdpcmLength < 0x8000;
HalfReached = AdpcmLength < 0x8000;
if (AdpcmLength == 0)
{
AdpcmIsPlaying = false;
EndReached = true;
HalfReached = false;
}
if (AdpcmLength == 0)
{
AdpcmIsPlaying = false;
EndReached = true;
HalfReached = false;
}
short adjustedSample = (short)((playingSample - 2048) * MaxVolume / 2048);
SoundProvider.buffer.enqueue_sample(adjustedSample, adjustedSample);
}
}
short adjustedSample = (short)((playingSample - 2048) * MaxVolume / 2048);
SoundProvider.buffer.enqueue_sample(adjustedSample, adjustedSample);
}
}
public void GetSamples(short[] samples)
{
SoundProvider.GetSamples(samples);
}
public void GetSamples(short[] samples)
{
SoundProvider.GetSamples(samples);
}
public void DiscardSamples()
{
SoundProvider.DiscardSamples();
}
public void DiscardSamples()
{
SoundProvider.DiscardSamples();
}
public int MaxVolume { get; set; }
public int MaxVolume { get; set; }
// ***************************************************************************
// ***************************************************************************
public void SaveStateBinary(BinaryWriter writer)
{
writer.Write(RAM);
writer.Write(IOAddress);
writer.Write(AdpcmLength);
writer.Write(ReadAddress);
writer.Write((byte)ReadTimer);
writer.Write(ReadBuffer);
writer.Write(ReadPending);
writer.Write(WriteAddress);
writer.Write((byte)WriteTimer);
writer.Write(WriteBuffer);
writer.Write(WritePending);
writer.Write(Port180B);
writer.Write(Port180D);
writer.Write(Port180E);
writer.Write(AdpcmIsPlaying);
writer.Write(HalfReached);
writer.Write(EndReached);
public void SaveStateBinary(BinaryWriter writer)
{
writer.Write(RAM);
writer.Write(IOAddress);
writer.Write(AdpcmLength);
writer.Write(ReadAddress);
writer.Write((byte)ReadTimer);
writer.Write(ReadBuffer);
writer.Write(ReadPending);
writer.Write(WriteAddress);
writer.Write((byte)WriteTimer);
writer.Write(WriteBuffer);
writer.Write(WritePending);
writer.Write(Playback44khzTimer);
writer.Write((ushort)playingSample);
writer.Write(nextSampleTimer);
writer.Write(nibble);
writer.Write((byte)magnitude);
}
writer.Write(Port180B);
writer.Write(Port180D);
writer.Write(Port180E);
public void LoadStateBinary(BinaryReader reader)
{
RAM = reader.ReadBytes(0x10000);
IOAddress = reader.ReadUInt16();
AdpcmLength = reader.ReadUInt16();
ReadAddress = reader.ReadUInt16();
ReadTimer = reader.ReadByte();
ReadBuffer = reader.ReadByte();
ReadPending = reader.ReadBoolean();
WriteAddress = reader.ReadUInt16();
WriteTimer = reader.ReadByte();
WriteBuffer = reader.ReadByte();
WritePending = reader.ReadBoolean();
writer.Write(AdpcmIsPlaying);
writer.Write(HalfReached);
writer.Write(EndReached);
Port180B = reader.ReadByte();
Port180D = reader.ReadByte();
Port180E = reader.ReadByte();
AdpcmIsPlaying = reader.ReadBoolean();
HalfReached = reader.ReadBoolean();
EndReached = reader.ReadBoolean();
writer.Write(Playback44khzTimer);
writer.Write((ushort)playingSample);
writer.Write(nextSampleTimer);
writer.Write(nibble);
writer.Write((byte)magnitude);
}
Playback44khzTimer = reader.ReadSingle();
playingSample = reader.ReadUInt16();
nextSampleTimer = reader.ReadSingle();
nibble = reader.ReadBoolean();
magnitude = reader.ReadByte();
public void LoadStateBinary(BinaryReader reader)
{
RAM = reader.ReadBytes(0x10000);
IOAddress = reader.ReadUInt16();
AdpcmLength = reader.ReadUInt16();
ReadAddress = reader.ReadUInt16();
ReadTimer = reader.ReadByte();
ReadBuffer = reader.ReadByte();
ReadPending = reader.ReadBoolean();
WriteAddress = reader.ReadUInt16();
WriteTimer = reader.ReadByte();
WriteBuffer = reader.ReadByte();
WritePending = reader.ReadBoolean();
pce.IntADPCM = HalfReached;
pce.IntStop = EndReached;
pce.RefreshIRQ2();
}
Port180B = reader.ReadByte();
Port180D = reader.ReadByte();
Port180E = reader.ReadByte();
public void SaveStateText(TextWriter writer)
{
writer.WriteLine("[ADPCM]");
writer.Write("RAM ");
RAM.SaveAsHex(writer);
writer.WriteLine("IOAddress {0:X4}", IOAddress);
writer.WriteLine("AdpcmLength {0:X4}", AdpcmLength);
writer.WriteLine("ReadAddress {0:X4}", ReadAddress);
writer.WriteLine("ReadTimer {0}", ReadTimer);
writer.WriteLine("ReadBuffer {0:X2}", ReadBuffer);
writer.WriteLine("ReadPending {0}", ReadPending);
writer.WriteLine("WriteAddress {0:X4}", WriteAddress);
writer.WriteLine("WriteTimer {0}", WriteTimer);
writer.WriteLine("WriteBuffer {0:X2}", WriteBuffer);
writer.WriteLine("WritePending {0}", WritePending);
AdpcmIsPlaying = reader.ReadBoolean();
HalfReached = reader.ReadBoolean();
EndReached = reader.ReadBoolean();
writer.WriteLine("Port180B {0:X2}", Port180B);
writer.WriteLine("Port180D {0:X2}", Port180D);
writer.WriteLine("Port180E {0:X2}", Port180E);
Playback44khzTimer = reader.ReadSingle();
playingSample = reader.ReadUInt16();
nextSampleTimer = reader.ReadSingle();
nibble = reader.ReadBoolean();
magnitude = reader.ReadByte();
writer.WriteLine("AdpcmIsPlaying {0}", AdpcmIsPlaying);
writer.WriteLine("HalfReached {0}", HalfReached);
writer.WriteLine("EndReached {0}", EndReached);
pce.IntADPCM = HalfReached;
pce.IntStop = EndReached;
pce.RefreshIRQ2();
}
writer.WriteLine("Playback44khzTimer {0}", Playback44khzTimer);
writer.WriteLine("PlayingSample {0:X4}", playingSample);
writer.WriteLine("NextSampleTimer {0}", nextSampleTimer);
writer.WriteLine("FirstNibble {0}", nibble);
writer.WriteLine("Magnitude {0}", magnitude);
writer.WriteLine("[/ADPCM]\n");
}
public void SaveStateText(TextWriter writer)
{
writer.WriteLine("[ADPCM]");
writer.Write("RAM ");
RAM.SaveAsHex(writer);
writer.WriteLine("IOAddress {0:X4}", IOAddress);
writer.WriteLine("AdpcmLength {0:X4}", AdpcmLength);
writer.WriteLine("ReadAddress {0:X4}", ReadAddress);
writer.WriteLine("ReadTimer {0}", ReadTimer);
writer.WriteLine("ReadBuffer {0:X2}", ReadBuffer);
writer.WriteLine("ReadPending {0}", ReadPending);
writer.WriteLine("WriteAddress {0:X4}", WriteAddress);
writer.WriteLine("WriteTimer {0}", WriteTimer);
writer.WriteLine("WriteBuffer {0:X2}", WriteBuffer);
writer.WriteLine("WritePending {0}", WritePending);
public void LoadStateText(TextReader reader)
{
while (true)
{
string[] args = reader.ReadLine().Split(' ');
if (args[0].Trim() == "") continue;
if (args[0] == "[/ADPCM]") break;
if (args[0] == "RAM")
RAM.ReadFromHex(args[1]);
else if (args[0] == "IOAddress")
IOAddress = ushort.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "AdpcmLength")
AdpcmLength = ushort.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "ReadAddress")
ReadAddress = ushort.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "ReadTimer")
ReadTimer = int.Parse(args[1]);
else if (args[0] == "ReadBuffer")
ReadBuffer = byte.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "ReadPending")
ReadPending = bool.Parse(args[1]);
else if (args[0] == "WriteAddress")
WriteAddress = ushort.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "WriteTimer")
WriteTimer = int.Parse(args[1]);
else if (args[0] == "WriteBuffer")
WriteBuffer = byte.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "WritePending")
WritePending = bool.Parse(args[1]);
writer.WriteLine("Port180B {0:X2}", Port180B);
writer.WriteLine("Port180D {0:X2}", Port180D);
writer.WriteLine("Port180E {0:X2}", Port180E);
else if (args[0] == "Port180B")
Port180B = byte.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "Port180D")
Port180D = byte.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "Port180E")
Port180E = byte.Parse(args[1], NumberStyles.HexNumber);
writer.WriteLine("AdpcmIsPlaying {0}", AdpcmIsPlaying);
writer.WriteLine("HalfReached {0}", HalfReached);
writer.WriteLine("EndReached {0}", EndReached);
else if (args[0] == "AdpcmIsPlaying")
AdpcmIsPlaying = bool.Parse(args[1]);
else if (args[0] == "HalfReached")
HalfReached = bool.Parse(args[1]);
else if (args[0] == "EndReached")
EndReached = bool.Parse(args[1]);
writer.WriteLine("Playback44khzTimer {0}", Playback44khzTimer);
writer.WriteLine("PlayingSample {0:X4}", playingSample);
writer.WriteLine("NextSampleTimer {0}", nextSampleTimer);
writer.WriteLine("FirstNibble {0}", nibble);
writer.WriteLine("Magnitude {0}", magnitude);
writer.WriteLine("[/ADPCM]\n");
}
else if (args[0] == "Playback44khzTimer")
Playback44khzTimer = float.Parse(args[1]);
else if (args[0] == "PlayingSample")
playingSample = ushort.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "NextSampleTimer")
nextSampleTimer = float.Parse(args[1]);
else if (args[0] == "FirstNibble")
nibble = bool.Parse(args[1]);
else if (args[0] == "Magnitude")
magnitude = int.Parse(args[1]);
public void LoadStateText(TextReader reader)
{
while (true)
{
string[] args = reader.ReadLine().Split(' ');
if (args[0].Trim() == "") continue;
if (args[0] == "[/ADPCM]") break;
if (args[0] == "RAM")
RAM.ReadFromHex(args[1]);
else if (args[0] == "IOAddress")
IOAddress = ushort.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "AdpcmLength")
AdpcmLength = ushort.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "ReadAddress")
ReadAddress = ushort.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "ReadTimer")
ReadTimer = int.Parse(args[1]);
else if (args[0] == "ReadBuffer")
ReadBuffer = byte.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "ReadPending")
ReadPending = bool.Parse(args[1]);
else if (args[0] == "WriteAddress")
WriteAddress = ushort.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "WriteTimer")
WriteTimer = int.Parse(args[1]);
else if (args[0] == "WriteBuffer")
WriteBuffer = byte.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "WritePending")
WritePending = bool.Parse(args[1]);
else
Console.WriteLine("Skipping unrecognized identifier " + args[0]);
}
else if (args[0] == "Port180B")
Port180B = byte.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "Port180D")
Port180D = byte.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "Port180E")
Port180E = byte.Parse(args[1], NumberStyles.HexNumber);
pce.IntADPCM = HalfReached;
pce.IntStop = EndReached;
pce.RefreshIRQ2();
}
}
else if (args[0] == "AdpcmIsPlaying")
AdpcmIsPlaying = bool.Parse(args[1]);
else if (args[0] == "HalfReached")
HalfReached = bool.Parse(args[1]);
else if (args[0] == "EndReached")
EndReached = bool.Parse(args[1]);
else if (args[0] == "Playback44khzTimer")
Playback44khzTimer = float.Parse(args[1]);
else if (args[0] == "PlayingSample")
playingSample = ushort.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "NextSampleTimer")
nextSampleTimer = float.Parse(args[1]);
else if (args[0] == "FirstNibble")
nibble = bool.Parse(args[1]);
else if (args[0] == "Magnitude")
magnitude = int.Parse(args[1]);
else
Console.WriteLine("Skipping unrecognized identifier " + args[0]);
}
pce.IntADPCM = HalfReached;
pce.IntStop = EndReached;
pce.RefreshIRQ2();
}
}
}

View File

@ -1,4 +1,6 @@
namespace BizHawk.Emulation.Consoles.TurboGrafx
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Consoles.TurboGrafx
{
public partial class PCEngine
{
@ -7,13 +9,13 @@
{
Name = "PC Engine Controller",
BoolButtons =
{
"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Select", "P1 Run", "P1 B2", "P1 B1",
"P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 Select", "P2 Run", "P2 B2", "P2 B1",
"P3 Up", "P3 Down", "P3 Left", "P3 Right", "P3 Select", "P3 Run", "P3 B2", "P3 B1",
"P4 Up", "P4 Down", "P4 Left", "P4 Right", "P4 Select", "P4 Run", "P4 B2", "P4 B1",
"P5 Up", "P5 Down", "P5 Left", "P5 Right", "P5 Select", "P5 Run", "P5 B2", "P5 B1"
}
{
"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Select", "P1 Run", "P1 B2", "P1 B1",
"P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 Select", "P2 Run", "P2 B2", "P2 B1",
"P3 Up", "P3 Down", "P3 Left", "P3 Right", "P3 Select", "P3 Run", "P3 B2", "P3 B1",
"P4 Up", "P4 Down", "P4 Left", "P4 Right", "P4 Select", "P4 Run", "P4 B2", "P4 B1",
"P5 Up", "P5 Down", "P5 Left", "P5 Right", "P5 Select", "P5 Run", "P5 B2", "P5 B1"
}
};
public ControllerDefinition ControllerDefinition { get { return PCEngineController; } }

View File

@ -3,426 +3,427 @@ using System.Globalization;
using System.IO;
using BizHawk.Common;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.CPUs.H6280;
namespace BizHawk.Emulation.Consoles.TurboGrafx
{
// HuC6270 Video Display Controller
// 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;
internal bool DmaRequested;
internal bool SatDmaRequested;
internal bool SatDmaPerformed;
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;
internal bool DmaRequested;
internal bool SatDmaRequested;
internal bool SatDmaPerformed;
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 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 VBlankInterruptEnabled { get { return (Registers[CR] & 0x08) != 0; } }
public bool RasterCompareInterruptEnabled { get { return (Registers[CR] & 0x04) != 0; } }
public bool SpriteOverflowInterruptEnabled { get { return (Registers[CR] & 0x02) != 0; } }
public bool SpriteCollisionInterruptEnabled { get { return (Registers[CR] & 0x01) != 0; } }
public bool Sprite4ColorModeEnabled { get { return (Registers[MWR] & 0x0C) == 4; } }
public bool BackgroundEnabled { get { return (Registers[CR] & 0x80) != 0; } }
public bool SpritesEnabled { get { return (Registers[CR] & 0x40) != 0; } }
public bool VBlankInterruptEnabled { get { return (Registers[CR] & 0x08) != 0; } }
public bool RasterCompareInterruptEnabled { get { return (Registers[CR] & 0x04) != 0; } }
public bool SpriteOverflowInterruptEnabled { get { return (Registers[CR] & 0x02) != 0; } }
public bool SpriteCollisionInterruptEnabled { get { return (Registers[CR] & 0x01) != 0; } }
public bool Sprite4ColorModeEnabled { get { return (Registers[MWR] & 0x0C) == 4; } }
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 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 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); } }
public int DisplayStartLine { get { return (Registers[VPR] >> 8) + (Registers[VPR] & 0x1F); } }
const int MAWR = 0; // Memory Address Write Register
const int MARR = 1; // Memory Address Read Register
const int VRR = 2; // VRAM Read Register
const int VWR = 2; // VRAM Write Register
const int CR = 5; // Control Register
const int RCR = 6; // Raster Compare Register
const int BXR = 7; // Background X-scroll Register
const int BYR = 8; // Background Y-scroll Register
const int MWR = 9; // Memory-access Width Register
const int HSR = 10; // Horizontal Sync Register
const int HDR = 11; // Horizontal Display Register
const int VPR = 12; // Vertical synchronous register
const int VDW = 13; // Vertical display register
const int VCR = 14; // Vertical display END position register;
const int DCR = 15; // DMA Control Register
const int SOUR = 16; // Source address for DMA
const int DESR = 17; // Destination address for DMA
const int LENR = 18; // Length of DMA transfer. Writing this will initiate DMA.
const int SATB = 19; // Sprite Attribute Table base location in VRAM
const int MAWR = 0; // Memory Address Write Register
const int MARR = 1; // Memory Address Read Register
const int VRR = 2; // VRAM Read Register
const int VWR = 2; // VRAM Write Register
const int CR = 5; // Control Register
const int RCR = 6; // Raster Compare Register
const int BXR = 7; // Background X-scroll Register
const int BYR = 8; // Background Y-scroll Register
const int MWR = 9; // Memory-access Width Register
const int HSR = 10; // Horizontal Sync Register
const int HDR = 11; // Horizontal Display Register
const int VPR = 12; // Vertical synchronous register
const int VDW = 13; // Vertical display register
const int VCR = 14; // Vertical display END position register;
const int DCR = 15; // DMA Control Register
const int SOUR = 16; // Source address for DMA
const int DESR = 17; // Destination address for DMA
const int LENR = 18; // Length of DMA transfer. Writing this will initiate DMA.
const int SATB = 19; // Sprite Attribute Table base location in VRAM
const int RegisterSelect = 0;
const int LSB = 2;
const int MSB = 3;
const int RegisterSelect = 0;
const int LSB = 2;
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;
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;
const int VramSize = 0x8000;
const int VramSize = 0x8000;
PCEngine pce;
HuC6280 cpu;
VCE vce;
PCEngine pce;
HuC6280 cpu;
VCE vce;
public int MultiResHack = 0;
public int MultiResHack = 0;
public VDC(PCEngine pce, HuC6280 cpu, VCE vce)
{
this.pce = pce;
this.cpu = cpu;
this.vce = vce;
public VDC(PCEngine pce, HuC6280 cpu, VCE vce)
{
this.pce = pce;
this.cpu = cpu;
this.vce = vce;
Registers[HSR] = 0x00FF;
Registers[HDR] = 0x00FF;
Registers[VPR] = 0xFFFF;
Registers[VCR] = 0xFFFF;
ReadBuffer = 0xFFFF;
}
Registers[HSR] = 0x00FF;
Registers[HDR] = 0x00FF;
Registers[VPR] = 0xFFFF;
Registers[VCR] = 0xFFFF;
ReadBuffer = 0xFFFF;
}
public void WriteVDC(int port, byte value)
{
cpu.PendingCycles--;
port &= 3;
if (port == RegisterSelect)
{
RegisterLatch = (byte)(value & 0x1F);
}
else if (port == LSB)
{
Registers[RegisterLatch] &= 0xFF00;
Registers[RegisterLatch] |= value;
public void WriteVDC(int port, byte value)
{
cpu.PendingCycles--;
port &= 3;
if (port == RegisterSelect)
{
RegisterLatch = (byte)(value & 0x1F);
}
else if (port == LSB)
{
Registers[RegisterLatch] &= 0xFF00;
Registers[RegisterLatch] |= value;
if (RegisterLatch == BYR)
BackgroundY = Registers[BYR] & 0x1FF;
}
else if (port == MSB)
{
Registers[RegisterLatch] &= 0x00FF;
Registers[RegisterLatch] |= (ushort) (value << 8);
CompleteMSBWrite(RegisterLatch);
}
}
if (RegisterLatch == BYR)
BackgroundY = Registers[BYR] & 0x1FF;
}
else if (port == MSB)
{
Registers[RegisterLatch] &= 0x00FF;
Registers[RegisterLatch] |= (ushort)(value << 8);
CompleteMSBWrite(RegisterLatch);
}
}
void CompleteMSBWrite(int register)
{
switch (register)
{
case MARR: // Memory Address Read Register
ReadBuffer = VRAM[Registers[MARR] & 0x7FFF];
break;
case VWR: // VRAM Write Register
if (Registers[MAWR] < VramSize) // Several games attempt to write past the end of VRAM
{
VRAM[Registers[MAWR]] = Registers[VWR];
UpdatePatternData((ushort) (Registers[MAWR] & 0x7FFF));
UpdateSpriteData((ushort) (Registers[MAWR] & 0x7FFF));
}
Registers[MAWR] += IncrementWidth;
break;
case BXR:
Registers[BXR] &= 0x3FF;
break;
case BYR:
Registers[BYR] &= 0x1FF;
BackgroundY = Registers[BYR];
break;
case HDR: // Horizontal Display Register - update framebuffer size
FrameWidth = RequestedFrameWidth;
FramePitch = MultiResHack == 0 ? FrameWidth : MultiResHack;
if (FrameBuffer.Length != FramePitch * FrameHeight)
FrameBuffer = new int[FramePitch * FrameHeight];
break;
case VDW: // Vertical Display Word? - update framebuffer size
FrameHeight = RequestedFrameHeight;
FrameWidth = RequestedFrameWidth;
if (FrameHeight > 242)
FrameHeight = 242;
if (MultiResHack != 0)
FramePitch = MultiResHack;
if (FrameBuffer.Length != FramePitch * FrameHeight)
FrameBuffer = new int[FramePitch * FrameHeight];
break;
case LENR: // Initiate DMA transfer
DmaRequested = true;
break;
case SATB:
SatDmaRequested = true;
break;
}
}
void CompleteMSBWrite(int register)
{
switch (register)
{
case MARR: // Memory Address Read Register
ReadBuffer = VRAM[Registers[MARR] & 0x7FFF];
break;
case VWR: // VRAM Write Register
if (Registers[MAWR] < VramSize) // Several games attempt to write past the end of VRAM
{
VRAM[Registers[MAWR]] = Registers[VWR];
UpdatePatternData((ushort)(Registers[MAWR] & 0x7FFF));
UpdateSpriteData((ushort)(Registers[MAWR] & 0x7FFF));
}
Registers[MAWR] += IncrementWidth;
break;
case BXR:
Registers[BXR] &= 0x3FF;
break;
case BYR:
Registers[BYR] &= 0x1FF;
BackgroundY = Registers[BYR];
break;
case HDR: // Horizontal Display Register - update framebuffer size
FrameWidth = RequestedFrameWidth;
FramePitch = MultiResHack == 0 ? FrameWidth : MultiResHack;
if (FrameBuffer.Length != FramePitch * FrameHeight)
FrameBuffer = new int[FramePitch * FrameHeight];
break;
case VDW: // Vertical Display Word? - update framebuffer size
FrameHeight = RequestedFrameHeight;
FrameWidth = RequestedFrameWidth;
if (FrameHeight > 242)
FrameHeight = 242;
if (MultiResHack != 0)
FramePitch = MultiResHack;
if (FrameBuffer.Length != FramePitch * FrameHeight)
FrameBuffer = new int[FramePitch * FrameHeight];
break;
case LENR: // Initiate DMA transfer
DmaRequested = true;
break;
case SATB:
SatDmaRequested = true;
break;
}
}
public byte ReadVDC(int port)
{
cpu.PendingCycles--;
byte retval = 0;
public byte ReadVDC(int port)
{
cpu.PendingCycles--;
byte retval = 0;
port &= 3;
switch (port)
{
case 0: // return status byte;
retval = StatusByte;
StatusByte = 0; // 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;
case 3: // MSB
retval = (byte)(ReadBuffer >> 8);
if (RegisterLatch == VRR)
{
Registers[MARR] += IncrementWidth;
ReadBuffer = VRAM[Registers[MARR]&0x7FFF];
}
return retval;
}
return 0;
}
port &= 3;
switch (port)
{
case 0: // return status byte;
retval = StatusByte;
StatusByte = 0; // 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;
case 3: // MSB
retval = (byte)(ReadBuffer >> 8);
if (RegisterLatch == VRR)
{
Registers[MARR] += IncrementWidth;
ReadBuffer = VRAM[Registers[MARR] & 0x7FFF];
}
return retval;
}
return 0;
}
internal void RunDmaForScanline()
{
// TODO: dont do this all in one scanline. I guess it can do about 227 words per scanline.
// TODO: to be honest, dont do it in a block per scanline. put it in the CPU think function.
Console.WriteLine("******************************* Doing some dma ******************************");
int advanceSource = (Registers[DCR] & 4) == 0 ? +1 : -1;
int advanceDest = (Registers[DCR] & 8) == 0 ? +1 : -1;
int wordsDone = 0;
internal void RunDmaForScanline()
{
// TODO: dont do this all in one scanline. I guess it can do about 227 words per scanline.
// TODO: to be honest, dont do it in a block per scanline. put it in the CPU think function.
Console.WriteLine("******************************* Doing some dma ******************************");
int advanceSource = (Registers[DCR] & 4) == 0 ? +1 : -1;
int advanceDest = (Registers[DCR] & 8) == 0 ? +1 : -1;
int wordsDone = 0;
for (;Registers[LENR]<0xFFFF;Registers[LENR]--,wordsDone++)
{
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);
for (; Registers[LENR] < 0xFFFF; Registers[LENR]--, wordsDone++)
{
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);
/*if (wordsDone == 227) {
Console.WriteLine("ended dma for this scanline");
return;
}*/
}
/*if (wordsDone == 227) {
Console.WriteLine("ended dma for this scanline");
return;
}*/
}
DmaRequested = false;
//Console.WriteLine("DMA finished");
DmaRequested = false;
//Console.WriteLine("DMA finished");
if ((Registers[DCR] & 2) > 0)
{
//Log.Note("Vdc","FIRE VRAM-VRAM DMA COMPLETE IRQ");
StatusByte |= StatusVramVramDmaComplete;
cpu.IRQ1Assert = true;
}
}
if ((Registers[DCR] & 2) > 0)
{
//Log.Note("Vdc","FIRE VRAM-VRAM DMA COMPLETE IRQ");
StatusByte |= StatusVramVramDmaComplete;
cpu.IRQ1Assert = true;
}
}
public void UpdateSpriteAttributeTable()
{
if ((SatDmaRequested || (Registers[DCR] & 0x10) != 0) && Registers[SATB] <= 0x7F00)
{
SatDmaRequested = false;
SatDmaPerformed = true;
for (int i = 0; i < 256; i++)
{
SpriteAttributeTable[i] = VRAM[Registers[SATB] + i];
}
}
}
public void UpdateSpriteAttributeTable()
{
if ((SatDmaRequested || (Registers[DCR] & 0x10) != 0) && Registers[SATB] <= 0x7F00)
{
SatDmaRequested = false;
SatDmaPerformed = true;
for (int i = 0; i < 256; i++)
{
SpriteAttributeTable[i] = VRAM[Registers[SATB] + i];
}
}
}
public void UpdatePatternData(ushort addr)
{
int tileNo = (addr >> 4);
int tileLineOffset = (addr & 0x7);
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 bitplane01 = VRAM[(tileNo * 16) + tileLineOffset];
int bitplane23 = VRAM[(tileNo * 16) + tileLineOffset + 8];
int patternBufferBase = (tileNo * 64) + (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;
}
}
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;
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++)
{
int ofs = (tileNo * 256) + (line * 16) + 15;
ushort value = VRAM[addr];
byte bitAnd = (byte)(~(1 << bitplane));
byte bitOr = (byte)(1 << bitplane);
if ((value & 1) == 1)
SpriteBuffer[ofs] |= bitOr;
else
SpriteBuffer[ofs] &= bitAnd;
ofs--;
value >>= 1;
}
}
for (int i = 0; i < 16; i++)
{
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);
if ((value & 1) == 1)
SpriteBuffer[ofs] |= bitOr;
else
SpriteBuffer[ofs] &= bitAnd;
ofs--;
value >>= 1;
}
}
writer.WriteLine("RegisterLatch {0:X2}", RegisterLatch);
writer.WriteLine("ReadBuffer {0:X4}", ReadBuffer);
writer.WriteLine("StatusByte {0:X2}", StatusByte);
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("DmaRequested {0}", DmaRequested);
writer.WriteLine("SatDmaRequested {0}", SatDmaRequested);
writer.WriteLine("SatDmaPerformed {0}", SatDmaPerformed);
writer.WriteLine("RegisterLatch {0:X2}", RegisterLatch);
writer.WriteLine("ReadBuffer {0:X4}", ReadBuffer);
writer.WriteLine("StatusByte {0:X2}", StatusByte);
writer.WriteLine("ScanLine {0}", ScanLine);
writer.WriteLine("BackgroundY {0}", BackgroundY);
writer.WriteLine("RCRCounter {0}", RCRCounter);
writer.WriteLine("ActiveLine {0}", ActiveLine);
writer.WriteLine("DmaRequested {0}", DmaRequested);
writer.WriteLine("SatDmaRequested {0}", SatDmaRequested);
writer.WriteLine("SatDmaPerformed {0}", SatDmaPerformed);
writer.WriteLine("[/VDC"+vdcNo+"]\n");
}
writer.WriteLine("ScanLine {0}", ScanLine);
writer.WriteLine("BackgroundY {0}", BackgroundY);
writer.WriteLine("RCRCounter {0}", RCRCounter);
writer.WriteLine("ActiveLine {0}", ActiveLine);
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);
writer.WriteLine("[/VDC" + vdcNo + "]\n");
}
else if (args[0] == "DmaRequested")
DmaRequested = bool.Parse(args[1]);
else if (args[0] == "SatDmaRequested")
SatDmaRequested = bool.Parse(args[1]);
else if (args[0] == "SatDmaPerformed")
SatDmaPerformed = bool.Parse(args[1]);
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 if (args[0] == "ScanLine")
ScanLine = int.Parse(args[1]);
else if (args[0] == "BackgroundY")
BackgroundY = int.Parse(args[1]);
else if (args[0] == "RCRCounter")
RCRCounter = int.Parse(args[1]);
else if (args[0] == "ActiveLine")
ActiveLine = int.Parse(args[1]);
else if (args[0] == "DmaRequested")
DmaRequested = bool.Parse(args[1]);
else if (args[0] == "SatDmaRequested")
SatDmaRequested = bool.Parse(args[1]);
else if (args[0] == "SatDmaPerformed")
SatDmaPerformed = bool.Parse(args[1]);
else
Console.WriteLine("Skipping unrecognized identifier " + args[0]);
}
else if (args[0] == "ScanLine")
ScanLine = int.Parse(args[1]);
else if (args[0] == "BackgroundY")
BackgroundY = int.Parse(args[1]);
else if (args[0] == "RCRCounter")
RCRCounter = int.Parse(args[1]);
else if (args[0] == "ActiveLine")
ActiveLine = int.Parse(args[1]);
for (ushort i = 0; i < VRAM.Length; i++)
{
UpdatePatternData(i);
UpdateSpriteData(i);
}
else
Console.WriteLine("Skipping unrecognized identifier " + args[0]);
}
CompleteMSBWrite(HDR);
CompleteMSBWrite(VDW);
}
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);
CompleteMSBWrite(HDR);
CompleteMSBWrite(VDW);
}
writer.Write(DmaRequested);
writer.Write(SatDmaRequested);
writer.Write(SatDmaPerformed);
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);
writer.Write(ScanLine);
writer.Write(BackgroundY);
writer.Write(RCRCounter);
writer.Write(ActiveLine);
}
writer.Write(DmaRequested);
writer.Write(SatDmaRequested);
writer.Write(SatDmaPerformed);
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();
writer.Write(ScanLine);
writer.Write(BackgroundY);
writer.Write(RCRCounter);
writer.Write(ActiveLine);
}
DmaRequested = reader.ReadBoolean();
SatDmaRequested = reader.ReadBoolean();
SatDmaPerformed = reader.ReadBoolean();
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();
ScanLine = reader.ReadInt32();
BackgroundY = reader.ReadInt32();
RCRCounter = reader.ReadInt32();
ActiveLine = reader.ReadInt32();
DmaRequested = reader.ReadBoolean();
SatDmaRequested = reader.ReadBoolean();
SatDmaPerformed = reader.ReadBoolean();
CompleteMSBWrite(HDR);
CompleteMSBWrite(VDW);
}
}
ScanLine = reader.ReadInt32();
BackgroundY = reader.ReadInt32();
RCRCounter = reader.ReadInt32();
ActiveLine = reader.ReadInt32();
CompleteMSBWrite(HDR);
CompleteMSBWrite(VDW);
}
}
}

View File

@ -2,509 +2,510 @@
using System.IO;
using BizHawk.Common;
using BizHawk.Emulation.Common;
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.
// ------------------------------------------------------
// 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.
public sealed class VPC : IVideoProvider
{
PCEngine PCE;
public VDC VDC1;
public VDC VDC2;
public VCE VCE;
public HuC6280 CPU;
public sealed class VPC : IVideoProvider
{
PCEngine PCE;
public VDC VDC1;
public VDC VDC2;
public VCE VCE;
public HuC6280 CPU;
public byte[] Registers = {0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00};
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 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(PCEngine pce, VDC vdc1, VDC vdc2, VCE vce, HuC6280 cpu)
{
PCE = pce;
VDC1 = vdc1;
VDC2 = vdc2;
VCE = vce;
CPU = cpu;
public VPC(PCEngine pce, VDC vdc1, VDC vdc2, VCE vce, HuC6280 cpu)
{
PCE = pce;
VDC1 = vdc1;
VDC2 = vdc2;
VCE = vce;
CPU = cpu;
// latch initial video buffer
FrameBuffer = vdc1.GetVideoBuffer();
FrameWidth = vdc1.BufferWidth;
FrameHeight = vdc1.BufferHeight;
}
// latch initial video buffer
FrameBuffer = vdc1.GetVideoBuffer();
FrameWidth = vdc1.BufferWidth;
FrameHeight = vdc1.BufferHeight;
}
public byte ReadVPC(int port)
{
port &= 0x0F;
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 byte ReadVPC(int port)
{
port &= 0x0F;
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)
{
port &= 0x0F;
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 WriteVPC(int port, byte value)
{
port &= 0x0F;
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 SaveStateBinary(BinaryWriter writer)
{
writer.Write(Registers);
}
public void LoadStateBinary(BinaryReader reader)
{
Registers = reader.ReadBytes(7);
WriteVPC(0x0E, Registers[6]);
}
public void LoadStateBinary(BinaryReader reader)
{
Registers = reader.ReadBytes(7);
WriteVPC(0x0E, Registers[6]);
}
public void SaveStateText(TextWriter writer)
{
writer.WriteLine("[VPC]");
writer.Write("Registers ");
Registers.SaveAsHex(writer);
writer.WriteLine("[/VPC]\n");
}
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]);
}
WriteVPC(0x0E, Registers[6]);
}
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]);
}
WriteVPC(0x0E, Registers[6]);
}
// 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.
// Maybe we'll emulate it in the native-code version.
// 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.
// Maybe we'll emulate it in the native-code version.
const int RCR = 6;
const int BXR = 7;
const int BYR = 8;
const int VDW = 13;
const int DCR = 15;
const int RCR = 6;
const int BXR = 7;
const int BYR = 8;
const int VDW = 13;
const int DCR = 15;
int EffectivePriorityMode = 0;
int EffectivePriorityMode = 0;
int FrameHeight;
int FrameWidth;
int[] FrameBuffer;
int FrameHeight;
int FrameWidth;
int[] FrameBuffer;
byte[] PriorityBuffer = new byte[512];
byte[] InterSpritePriorityBuffer = new byte[512];
byte[] PriorityBuffer = new byte[512];
byte[] InterSpritePriorityBuffer = new byte[512];
public void ExecFrame(bool render)
{
// 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;
}
public void ExecFrame(bool render)
{
// 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();
// Latch frame dimensions and framebuffer, for purely dumb reasons
FrameWidth = VDC1.BufferWidth;
FrameHeight = VDC1.BufferHeight;
FrameBuffer = VDC1.GetVideoBuffer();
int ScanLine = 0;
while (true)
{
VDC1.ScanLine = ScanLine;
VDC2.ScanLine = ScanLine;
int ScanLine = 0;
while (true)
{
VDC1.ScanLine = ScanLine;
VDC2.ScanLine = ScanLine;
int ActiveDisplayStartLine = VDC1.DisplayStartLine;
int VBlankLine = ActiveDisplayStartLine + VDC1.Registers[VDW] + 1;
if (VBlankLine > 261)
VBlankLine = 261;
VDC1.ActiveLine = ScanLine - ActiveDisplayStartLine;
VDC2.ActiveLine = VDC1.ActiveLine;
bool InActiveDisplay = (ScanLine >= ActiveDisplayStartLine) && (ScanLine < VBlankLine);
int ActiveDisplayStartLine = VDC1.DisplayStartLine;
int VBlankLine = ActiveDisplayStartLine + VDC1.Registers[VDW] + 1;
if (VBlankLine > 261)
VBlankLine = 261;
VDC1.ActiveLine = ScanLine - ActiveDisplayStartLine;
VDC2.ActiveLine = VDC1.ActiveLine;
bool InActiveDisplay = (ScanLine >= ActiveDisplayStartLine) && (ScanLine < VBlankLine);
if (ScanLine == ActiveDisplayStartLine)
{
VDC1.RCRCounter = 0x40;
VDC2.RCRCounter = 0x40;
}
if (ScanLine == ActiveDisplayStartLine)
{
VDC1.RCRCounter = 0x40;
VDC2.RCRCounter = 0x40;
}
if (ScanLine == VBlankLine)
{
VDC1.UpdateSpriteAttributeTable();
VDC2.UpdateSpriteAttributeTable();
}
if (ScanLine == VBlankLine)
{
VDC1.UpdateSpriteAttributeTable();
VDC2.UpdateSpriteAttributeTable();
}
if (VDC1.RCRCounter == (VDC1.Registers[RCR] & 0x3FF))
{
if (VDC1.RasterCompareInterruptEnabled)
{
VDC1.StatusByte |= VDC.StatusRasterCompare;
CPU.IRQ1Assert = true;
}
}
if (VDC1.RCRCounter == (VDC1.Registers[RCR] & 0x3FF))
{
if (VDC1.RasterCompareInterruptEnabled)
{
VDC1.StatusByte |= VDC.StatusRasterCompare;
CPU.IRQ1Assert = true;
}
}
if (VDC2.RCRCounter == (VDC2.Registers[RCR] & 0x3FF))
{
if (VDC2.RasterCompareInterruptEnabled)
{
VDC2.StatusByte |= VDC.StatusRasterCompare;
CPU.IRQ1Assert = true;
}
}
if (VDC2.RCRCounter == (VDC2.Registers[RCR] & 0x3FF))
{
if (VDC2.RasterCompareInterruptEnabled)
{
VDC2.StatusByte |= VDC.StatusRasterCompare;
CPU.IRQ1Assert = true;
}
}
CPU.Execute(VDC1.HBlankCycles);
CPU.Execute(VDC1.HBlankCycles);
if (InActiveDisplay)
{
if (ScanLine == ActiveDisplayStartLine)
{
VDC1.BackgroundY = VDC1.Registers[BYR];
VDC2.BackgroundY = VDC2.Registers[BYR];
}
else
{
VDC1.BackgroundY++;
VDC1.BackgroundY &= 0x01FF;
VDC2.BackgroundY++;
VDC2.BackgroundY &= 0x01FF;
}
if (render) RenderScanLine();
}
if (InActiveDisplay)
{
if (ScanLine == ActiveDisplayStartLine)
{
VDC1.BackgroundY = VDC1.Registers[BYR];
VDC2.BackgroundY = VDC2.Registers[BYR];
}
else
{
VDC1.BackgroundY++;
VDC1.BackgroundY &= 0x01FF;
VDC2.BackgroundY++;
VDC2.BackgroundY &= 0x01FF;
}
if (render) RenderScanLine();
}
if (ScanLine == VBlankLine && VDC1.VBlankInterruptEnabled)
VDC1.StatusByte |= VDC.StatusVerticalBlanking;
if (ScanLine == VBlankLine && VDC1.VBlankInterruptEnabled)
VDC1.StatusByte |= VDC.StatusVerticalBlanking;
if (ScanLine == VBlankLine && VDC2.VBlankInterruptEnabled)
VDC2.StatusByte |= VDC.StatusVerticalBlanking;
if (ScanLine == VBlankLine && VDC2.VBlankInterruptEnabled)
VDC2.StatusByte |= VDC.StatusVerticalBlanking;
if (ScanLine == VBlankLine + 4 && VDC1.SatDmaPerformed)
{
VDC1.SatDmaPerformed = false;
if ((VDC1.Registers[DCR] & 1) > 0)
VDC1.StatusByte |= VDC.StatusVramSatDmaComplete;
}
if (ScanLine == VBlankLine + 4 && VDC1.SatDmaPerformed)
{
VDC1.SatDmaPerformed = false;
if ((VDC1.Registers[DCR] & 1) > 0)
VDC1.StatusByte |= VDC.StatusVramSatDmaComplete;
}
if (ScanLine == VBlankLine + 4 && VDC2.SatDmaPerformed)
{
VDC2.SatDmaPerformed = false;
if ((VDC2.Registers[DCR] & 1) > 0)
VDC2.StatusByte |= VDC.StatusVramSatDmaComplete;
}
if (ScanLine == VBlankLine + 4 && VDC2.SatDmaPerformed)
{
VDC2.SatDmaPerformed = false;
if ((VDC2.Registers[DCR] & 1) > 0)
VDC2.StatusByte |= VDC.StatusVramSatDmaComplete;
}
CPU.Execute(2);
CPU.Execute(2);
if ((VDC1.StatusByte & (VDC.StatusVerticalBlanking | VDC.StatusVramSatDmaComplete)) != 0)
CPU.IRQ1Assert = true;
if ((VDC1.StatusByte & (VDC.StatusVerticalBlanking | VDC.StatusVramSatDmaComplete)) != 0)
CPU.IRQ1Assert = true;
if ((VDC2.StatusByte & (VDC.StatusVerticalBlanking | VDC.StatusVramSatDmaComplete)) != 0)
CPU.IRQ1Assert = true;
if ((VDC2.StatusByte & (VDC.StatusVerticalBlanking | VDC.StatusVramSatDmaComplete)) != 0)
CPU.IRQ1Assert = true;
CPU.Execute(455 - VDC1.HBlankCycles - 2);
CPU.Execute(455 - VDC1.HBlankCycles - 2);
if (InActiveDisplay == false && VDC1.DmaRequested)
VDC1.RunDmaForScanline();
if (InActiveDisplay == false && VDC1.DmaRequested)
VDC1.RunDmaForScanline();
if (InActiveDisplay == false && VDC2.DmaRequested)
VDC2.RunDmaForScanline();
if (InActiveDisplay == false && VDC2.DmaRequested)
VDC2.RunDmaForScanline();
VDC1.RCRCounter++;
VDC2.RCRCounter++;
ScanLine++;
VDC1.RCRCounter++;
VDC2.RCRCounter++;
ScanLine++;
if (ScanLine == VCE.NumberOfScanlines)
break;
}
}
if (ScanLine == VCE.NumberOfScanlines)
break;
}
}
void RenderScanLine()
{
if (VDC1.ActiveLine >= FrameHeight)
return;
void RenderScanLine()
{
if (VDC1.ActiveLine >= FrameHeight)
return;
InitializeScanLine(VDC1.ActiveLine);
InitializeScanLine(VDC1.ActiveLine);
switch (EffectivePriorityMode)
{
case 0:
RenderBackgroundScanline(VDC1, 12, PCE.CoreComm.PCE_ShowBG1);
RenderBackgroundScanline(VDC2, 2, PCE.CoreComm.PCE_ShowBG2);
RenderSpritesScanline(VDC1, 11, 14, PCE.CoreComm.PCE_ShowOBJ1);
RenderSpritesScanline(VDC2, 1, 3, PCE.CoreComm.PCE_ShowOBJ2);
break;
case 1:
RenderBackgroundScanline(VDC1, 12, PCE.CoreComm.PCE_ShowBG1);
RenderBackgroundScanline(VDC2, 2, PCE.CoreComm.PCE_ShowBG2);
RenderSpritesScanline(VDC1, 11, 14, PCE.CoreComm.PCE_ShowOBJ1);
RenderSpritesScanline(VDC2, 1, 13, PCE.CoreComm.PCE_ShowOBJ2);
break;
}
}
switch (EffectivePriorityMode)
{
case 0:
RenderBackgroundScanline(VDC1, 12, PCE.CoreComm.PCE_ShowBG1);
RenderBackgroundScanline(VDC2, 2, PCE.CoreComm.PCE_ShowBG2);
RenderSpritesScanline(VDC1, 11, 14, PCE.CoreComm.PCE_ShowOBJ1);
RenderSpritesScanline(VDC2, 1, 3, PCE.CoreComm.PCE_ShowOBJ2);
break;
case 1:
RenderBackgroundScanline(VDC1, 12, PCE.CoreComm.PCE_ShowBG1);
RenderBackgroundScanline(VDC2, 2, PCE.CoreComm.PCE_ShowBG2);
RenderSpritesScanline(VDC1, 11, 14, PCE.CoreComm.PCE_ShowOBJ1);
RenderSpritesScanline(VDC2, 1, 13, PCE.CoreComm.PCE_ShowOBJ2);
break;
}
}
void InitializeScanLine(int scanline)
{
// Clear priority buffer
Array.Clear(PriorityBuffer, 0, FrameWidth);
void InitializeScanLine(int scanline)
{
// Clear priority buffer
Array.Clear(PriorityBuffer, 0, FrameWidth);
// Initialize scanline to background color
for (int i = 0; i < FrameWidth; i++)
FrameBuffer[(scanline * FrameWidth) + i] = VCE.Palette[256];
}
// Initialize scanline to background color
for (int i = 0; i < FrameWidth; i++)
FrameBuffer[(scanline * FrameWidth) + i] = VCE.Palette[256];
}
void RenderBackgroundScanline(VDC vdc, byte priority, bool show)
{
if (vdc.BackgroundEnabled == false)
return;
void RenderBackgroundScanline(VDC vdc, byte priority, bool show)
{
if (vdc.BackgroundEnabled == false)
return;
int vertLine = vdc.BackgroundY;
vertLine %= vdc.BatHeight * 8;
int yTile = (vertLine / 8);
int yOfs = vertLine % 8;
int vertLine = vdc.BackgroundY;
vertLine %= vdc.BatHeight * 8;
int yTile = (vertLine / 8);
int yOfs = vertLine % 8;
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;
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.ActiveLine * FrameWidth) + x] = show ? VCE.Palette[paletteBase + c] : VCE.Palette[0];
PriorityBuffer[x] = priority;
}
}
}
byte c = vdc.PatternBuffer[(tileNo * 64) + (yOfs * 8) + xOfs];
if (c != 0)
{
FrameBuffer[(vdc.ActiveLine * FrameWidth) + x] = show ? VCE.Palette[paletteBase + c] : VCE.Palette[0];
PriorityBuffer[x] = priority;
}
}
}
static byte[] heightTable = { 16, 32, 64, 64 };
static byte[] heightTable = { 16, 32, 64, 64 };
void RenderSpritesScanline(VDC vdc, byte lowPriority, byte highPriority, bool show)
{
if (vdc.SpritesEnabled == false)
return;
void RenderSpritesScanline(VDC vdc, byte lowPriority, byte highPriority, bool show)
{
if (vdc.SpritesEnabled == false)
return;
// clear inter-sprite priority buffer
Array.Clear(InterSpritePriorityBuffer, 0, FrameWidth);
// clear inter-sprite priority buffer
Array.Clear(InterSpritePriorityBuffer, 0, FrameWidth);
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];
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.ActiveLine || y > vdc.ActiveLine)
continue;
if (y + height <= vdc.ActiveLine || y > vdc.ActiveLine)
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;
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;
if (width == 32)
patternNo &= 0x1FE;
int yofs;
if (vflip == false)
{
yofs = (vdc.ActiveLine - y) & 15;
if (height == 32)
{
patternNo &= 0x1FD;
if (vdc.ActiveLine - y >= 16)
{
y += 16;
patternNo += 2;
}
}
else if (height == 64)
{
patternNo &= 0x1F9;
if (vdc.ActiveLine - y >= 48)
{
y += 48;
patternNo += 6;
}
else if (vdc.ActiveLine - y >= 32)
{
y += 32;
patternNo += 4;
}
else if (vdc.ActiveLine - y >= 16)
{
y += 16;
patternNo += 2;
}
}
}
else // vflip == true
{
yofs = 15 - ((vdc.ActiveLine - y) & 15);
if (height == 32)
{
patternNo &= 0x1FD;
if (vdc.ActiveLine - y < 16)
{
y += 16;
patternNo += 2;
}
}
else if (height == 64)
{
patternNo &= 0x1F9;
if (vdc.ActiveLine - y < 16)
{
y += 48;
patternNo += 6;
}
else if (vdc.ActiveLine - y < 32)
{
y += 32;
patternNo += 4;
}
else if (vdc.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 = 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)
{
if (show) FrameBuffer[(vdc.ActiveLine * 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)
{
if (show) FrameBuffer[(vdc.ActiveLine * 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)
{
if (show) FrameBuffer[(vdc.ActiveLine * 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)
{
if (show) FrameBuffer[(vdc.ActiveLine * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel];
PriorityBuffer[xs] = myPriority;
}
}
}
}
}
}
}
}
int yofs;
if (vflip == false)
{
yofs = (vdc.ActiveLine - y) & 15;
if (height == 32)
{
patternNo &= 0x1FD;
if (vdc.ActiveLine - y >= 16)
{
y += 16;
patternNo += 2;
}
}
else if (height == 64)
{
patternNo &= 0x1F9;
if (vdc.ActiveLine - y >= 48)
{
y += 48;
patternNo += 6;
}
else if (vdc.ActiveLine - y >= 32)
{
y += 32;
patternNo += 4;
}
else if (vdc.ActiveLine - y >= 16)
{
y += 16;
patternNo += 2;
}
}
}
else // vflip == true
{
yofs = 15 - ((vdc.ActiveLine - y) & 15);
if (height == 32)
{
patternNo &= 0x1FD;
if (vdc.ActiveLine - y < 16)
{
y += 16;
patternNo += 2;
}
}
else if (height == 64)
{
patternNo &= 0x1F9;
if (vdc.ActiveLine - y < 16)
{
y += 48;
patternNo += 6;
}
else if (vdc.ActiveLine - y < 32)
{
y += 32;
patternNo += 4;
}
else if (vdc.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 = 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)
{
if (show) FrameBuffer[(vdc.ActiveLine * 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)
{
if (show) FrameBuffer[(vdc.ActiveLine * 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)
{
if (show) FrameBuffer[(vdc.ActiveLine * 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)
{
if (show) FrameBuffer[(vdc.ActiveLine * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel];
PriorityBuffer[xs] = myPriority;
}
}
}
}
}
}
}
}
public int[] GetVideoBuffer() { return FrameBuffer; }
public int[] GetVideoBuffer() { return FrameBuffer; }
public int VirtualWidth { get { return FrameWidth; } }
public int BufferWidth { get { return FrameWidth; } }
public int BufferHeight { get { return FrameHeight; } }
public int BackgroundColor { get { return VCE.Palette[0]; } }
}
public int VirtualWidth { get { return FrameWidth; } }
public int BufferWidth { get { return FrameWidth; } }
public int BufferHeight { get { return FrameHeight; } }
public int BackgroundColor { get { return VCE.Palette[0]; } }
}
}

View File

@ -3,6 +3,8 @@ using System.Runtime.InteropServices;
using System.IO;
using System.Collections.Generic;
using BizHawk.Emulation.Common;
#pragma warning disable 649 //adelikat: Disable dumb warnings until this file is complete
namespace BizHawk.Emulation.Consoles.PSX

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,6 @@
namespace BizHawk.Emulation.Consoles.Sega
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Consoles.Sega
{
partial class Genesis
{

View File

@ -1,4 +1,6 @@
namespace BizHawk.Emulation.Consoles.Sega
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Consoles.Sega
{
public partial class SMS
{

View File

@ -3,6 +3,7 @@ using System.Globalization;
using System.IO;
using BizHawk.Common;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.CPUs.Z80;

View File

@ -6,6 +6,7 @@ using System.Runtime.InteropServices;
using System.IO;
using BizHawk.Common;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.DiscSystem;
namespace BizHawk.Emulation.Consoles.Sega.Saturn

View File

@ -4,6 +4,8 @@ using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Consoles.Sony.PSP
{
public class PSP : IEmulator, IVideoProvider, ISyncSoundProvider

View File

@ -1,9 +0,0 @@
namespace BizHawk
{
public interface ISoundProvider
{
void GetSamples(short[] samples);
void DiscardSamples();
int MaxVolume { get; set; }
}
}

View File

@ -1,13 +0,0 @@
namespace BizHawk
{
public interface IVideoProvider
{
int[] GetVideoBuffer();
int VirtualWidth { get; } // Used for controlling aspect ratio. Just return BufferWidth if you dont know what to do with this.
int BufferWidth { get; }
int BufferHeight { get; }
int BackgroundColor { get; }
}
}

View File

@ -1,6 +1,7 @@
using System;
using System.IO;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.DiscSystem;
// The state of the cd player is quantized to the frame level.

View File

@ -4,355 +4,363 @@ using System.Globalization;
using System.IO;
using BizHawk.Common;
using BizHawk.Emulation.Common;
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.
// 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 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;
byte WaveTableWriteOffset;
public PSGChannel[] Channels = new PSGChannel[8];
Queue<QueuedCommand> commands = new Queue<QueuedCommand>(256);
long frameStartTime, frameStopTime;
public byte VoiceLatch;
byte WaveTableWriteOffset;
const int SampleRate = 44100;
const int PsgBase = 3580000;
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 };
static byte[] VolumeReductionTable = { 0x1F, 0x1D, 0x1B, 0x19, 0x17, 0x15, 0x13, 0x10, 0x0F, 0x0D, 0x0B, 0x09, 0x07, 0x05, 0x03, 0x00 };
Queue<QueuedCommand> commands = new Queue<QueuedCommand>(256);
long frameStartTime, frameStopTime;
public byte MainVolumeLeft;
public byte MainVolumeRight;
public int MaxVolume { get; set; }
const int SampleRate = 44100;
const int PsgBase = 3580000;
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 };
static byte[] VolumeReductionTable = { 0x1F, 0x1D, 0x1B, 0x19, 0x17, 0x15, 0x13, 0x10, 0x0F, 0x0D, 0x0B, 0x09, 0x07, 0x05, 0x03, 0x00 };
public HuC6280PSG()
{
MaxVolume = short.MaxValue;
Waves.InitWaves();
for (int i=0; i<8; i++)
Channels[i] = new PSGChannel();
}
public byte MainVolumeLeft;
public byte MainVolumeRight;
public int MaxVolume { get; set; }
public void BeginFrame(long cycles)
{
while (commands.Count > 0)
{
var cmd = commands.Dequeue();
WritePSGImmediate(cmd.Register, cmd.Value);
}
frameStartTime = cycles;
}
public HuC6280PSG()
{
MaxVolume = short.MaxValue;
Waves.InitWaves();
for (int i = 0; i < 8; i++)
Channels[i] = new PSGChannel();
}
public void EndFrame(long cycles)
{
frameStopTime = cycles;
}
public void BeginFrame(long cycles)
{
while (commands.Count > 0)
{
var cmd = commands.Dequeue();
WritePSGImmediate(cmd.Register, cmd.Value);
}
frameStartTime = cycles;
}
public void WritePSG(byte register, byte value, long cycles)
{
commands.Enqueue(new QueuedCommand { Register = register, Value = value, Time = cycles-frameStartTime });
}
public void EndFrame(long cycles)
{
frameStopTime = cycles;
}
public void WritePSGImmediate(int register, byte value)
{
register &= 0x0F;
switch (register)
{
case 0: // Set Voice Latch
VoiceLatch = (byte) (value & 7);
break;
case 1: // Global Volume select;
MainVolumeLeft = (byte) ((value >> 4) & 0x0F);
MainVolumeRight = (byte) (value & 0x0F);
break;
case 2: // Frequency LSB
Channels[VoiceLatch].Frequency &= 0xFF00;
Channels[VoiceLatch].Frequency |= value;
break;
case 3: // Frequency MSB
Channels[VoiceLatch].Frequency &= 0x00FF;
Channels[VoiceLatch].Frequency |= (ushort)(value << 8);
Channels[VoiceLatch].Frequency &= 0x0FFF;
break;
case 4: // 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 5: // Panning
Channels[VoiceLatch].Panning = value;
break;
case 6: // 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 7: // 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 8: // LFO
// TODO: implement LFO
break;
case 9: // LFO Control
if ((value & 0x80) == 0 && (value & 3) != 0)
{
Console.WriteLine("**************** LFO ON !!!!!!!!!! *****************");
Channels[1].Enabled = false;
} else
{
Channels[1].Enabled = true;
}
break;
}
}
public void WritePSG(byte register, byte value, long cycles)
{
commands.Enqueue(new QueuedCommand { Register = register, Value = value, Time = cycles - frameStartTime });
}
public void WritePSGImmediate(int register, byte value)
{
register &= 0x0F;
switch (register)
{
case 0: // Set Voice Latch
VoiceLatch = (byte)(value & 7);
break;
case 1: // Global Volume select;
MainVolumeLeft = (byte)((value >> 4) & 0x0F);
MainVolumeRight = (byte)(value & 0x0F);
break;
case 2: // Frequency LSB
Channels[VoiceLatch].Frequency &= 0xFF00;
Channels[VoiceLatch].Frequency |= value;
break;
case 3: // Frequency MSB
Channels[VoiceLatch].Frequency &= 0x00FF;
Channels[VoiceLatch].Frequency |= (ushort)(value << 8);
Channels[VoiceLatch].Frequency &= 0x0FFF;
break;
case 4: // 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 5: // Panning
Channels[VoiceLatch].Panning = value;
break;
case 6: // 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 7: // 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 8: // LFO
// TODO: implement LFO
break;
case 9: // LFO Control
if ((value & 0x80) == 0 && (value & 3) != 0)
{
Console.WriteLine("**************** LFO ON !!!!!!!!!! *****************");
Channels[1].Enabled = false;
}
else
{
Channels[1].Enabled = true;
}
break;
}
}
public void DiscardSamples() { }
public void GetSamples(short[] samples)
{
int elapsedCycles = (int) (frameStopTime - frameStartTime);
int start = 0;
while (commands.Count > 0)
{
var cmd = commands.Dequeue();
int pos = (int) ((cmd.Time * samples.Length) / elapsedCycles) & ~1;
MixSamples(samples, start, pos - start);
start = pos;
WritePSGImmediate(cmd.Register, cmd.Value);
}
MixSamples(samples, start, samples.Length - start);
}
public void GetSamples(short[] samples)
{
int elapsedCycles = (int)(frameStopTime - frameStartTime);
int start = 0;
while (commands.Count > 0)
{
var cmd = commands.Dequeue();
int pos = (int)((cmd.Time * samples.Length) / elapsedCycles) & ~1;
MixSamples(samples, start, pos - start);
start = pos;
WritePSGImmediate(cmd.Register, cmd.Value);
}
MixSamples(samples, start, samples.Length - start);
}
void MixSamples(short[] samples, int start, int len)
{
for (int i = 0; i < 6; i++)
MixChannel(samples, start, len, Channels[i]);
}
void MixSamples(short[] samples, int start, int len)
{
for (int i = 0; i < 6; i++)
MixChannel(samples, start, len, Channels[i]);
}
void MixChannel(short[] samples, int start, int len, PSGChannel channel)
{
if (channel.Enabled == false) return;
if (channel.DDA == false && channel.Volume == 0) return;
void MixChannel(short[] samples, int start, int len, PSGChannel channel)
{
if (channel.Enabled == false) return;
if (channel.DDA == false && channel.Volume == 0) return;
short[] wave = channel.Wave;
int freq;
short[] wave = channel.Wave;
int freq;
if (channel.NoiseChannel)
{
wave = Waves.NoiseWave;
freq = channel.NoiseFreq;
} else if (channel.DDA) {
freq = 0;
} else {
if (channel.Frequency <= 1) return;
freq = PsgBase / (32 * ((int)channel.Frequency));
}
if (channel.NoiseChannel)
{
wave = Waves.NoiseWave;
freq = channel.NoiseFreq;
}
else if (channel.DDA)
{
freq = 0;
}
else
{
if (channel.Frequency <= 1) return;
freq = PsgBase / (32 * ((int)channel.Frequency));
}
int globalPanFactorLeft = VolumeReductionTable[MainVolumeLeft];
int globalPanFactorRight = VolumeReductionTable[MainVolumeRight];
int channelPanFactorLeft = VolumeReductionTable[channel.Panning >> 4];
int channelPanFactorRight = VolumeReductionTable[channel.Panning & 0xF];
int channelVolumeFactor = 0x1f - channel.Volume;
int globalPanFactorLeft = VolumeReductionTable[MainVolumeLeft];
int globalPanFactorRight = VolumeReductionTable[MainVolumeRight];
int channelPanFactorLeft = VolumeReductionTable[channel.Panning >> 4];
int channelPanFactorRight = VolumeReductionTable[channel.Panning & 0xF];
int channelVolumeFactor = 0x1f - channel.Volume;
int volumeLeft = 0x1F - globalPanFactorLeft - channelPanFactorLeft - channelVolumeFactor;
if (volumeLeft < 0)
volumeLeft = 0;
int volumeLeft = 0x1F - globalPanFactorLeft - channelPanFactorLeft - channelVolumeFactor;
if (volumeLeft < 0)
volumeLeft = 0;
int volumeRight = 0x1F - globalPanFactorRight - channelPanFactorRight - channelVolumeFactor;
if (volumeRight < 0)
volumeRight = 0;
int volumeRight = 0x1F - globalPanFactorRight - channelPanFactorRight - channelVolumeFactor;
if (volumeRight < 0)
volumeRight = 0;
float adjustedWaveLengthInSamples = SampleRate / (channel.NoiseChannel ? freq/(float)(channel.Wave.Length*128) : freq);
float moveThroughWaveRate = wave.Length / adjustedWaveLengthInSamples;
float adjustedWaveLengthInSamples = SampleRate / (channel.NoiseChannel ? freq / (float)(channel.Wave.Length * 128) : 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];
int end = start + len;
for (int i = start; i < end; )
{
channel.SampleOffset %= wave.Length;
short value = channel.DDA ? channel.DDAValue : wave[(int)channel.SampleOffset];
samples[i++] += (short)(value * LogScale[volumeLeft] / 255f / 6f * MaxVolume / short.MaxValue);
samples[i++] += (short)(value * LogScale[volumeRight] / 255f / 6f * MaxVolume / short.MaxValue);
samples[i++] += (short)(value * LogScale[volumeLeft] / 255f / 6f * MaxVolume / short.MaxValue);
samples[i++] += (short)(value * LogScale[volumeRight] / 255f / 6f * MaxVolume / short.MaxValue);
channel.SampleOffset += moveThroughWaveRate;
channel.SampleOffset %= wave.Length;
}
}
channel.SampleOffset += moveThroughWaveRate;
channel.SampleOffset %= wave.Length;
}
}
public void SaveStateText(TextWriter writer)
{
writer.WriteLine("[PSG]");
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();
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);
}
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");
}
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], NumberStyles.HexNumber);
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]);
}
}
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], NumberStyles.HexNumber);
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]);
}
}
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]);
}
}
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);
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]);
}
}
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();
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();
}
}
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 byte Register;
public byte Value;
public long Time;
}
}
class QueuedCommand
{
public byte Register;
public byte Value;
public long Time;
}
}
}

View File

@ -3,313 +3,317 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using BizHawk.Emulation.Common;
// Emulates a Texas Instruments SN76489
// 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;
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;
const int SampleRate = 44100;
static byte[] LogScale = { 0, 10, 13, 16, 20, 26, 32, 40, 51, 64, 81, 102, 128, 161, 203, 255 };
const int SampleRate = 44100;
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, int maxVolume)
{
if (Volume == 0) return;
public void Mix(short[] samples, int start, int len, int maxVolume)
{
if (Volume == 0) return;
float adjustedWaveLengthInSamples = SampleRate / (Noise ? (Frequency / (float)Wave.Length) : Frequency);
float moveThroughWaveRate = Wave.Length / adjustedWaveLengthInSamples;
float adjustedWaveLengthInSamples = SampleRate / (Noise ? (Frequency / (float)Wave.Length) : Frequency);
float moveThroughWaveRate = Wave.Length / adjustedWaveLengthInSamples;
int end = start + len;
for (int i = start; i < end; )
{
short value = Wave[(int)WaveOffset];
int end = start + len;
for (int i = start; i < end; )
{
short value = Wave[(int)WaveOffset];
samples[i++] += (short)(Left ? (value / 4 * LogScale[Volume] / 0xFF * maxVolume / short.MaxValue) : 0);
samples[i++] += (short)(Right ? (value / 4 * LogScale[Volume] / 0xFF * maxVolume / short.MaxValue) : 0);
WaveOffset += moveThroughWaveRate;
if (WaveOffset >= Wave.Length)
WaveOffset %= Wave.Length;
}
}
}
public Channel[] Channels = new Channel[4];
public byte PsgLatch;
samples[i++] += (short)(Left ? (value / 4 * LogScale[Volume] / 0xFF * maxVolume / short.MaxValue) : 0);
samples[i++] += (short)(Right ? (value / 4 * LogScale[Volume] / 0xFF * maxVolume / short.MaxValue) : 0);
WaveOffset += moveThroughWaveRate;
if (WaveOffset >= Wave.Length)
WaveOffset %= Wave.Length;
}
}
}
Queue<QueuedCommand> commands = new Queue<QueuedCommand>(256);
int frameStartTime, frameStopTime;
public Channel[] Channels = new Channel[4];
public byte PsgLatch;
const int PsgBase = 111861;
Queue<QueuedCommand> commands = new Queue<QueuedCommand>(256);
int frameStartTime, frameStopTime;
public SN76489()
{
MaxVolume = short.MaxValue * 2 / 3;
Waves.InitWaves();
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;
}
}
}
const int PsgBase = 111861;
public void Reset()
{
PsgLatch = 0;
foreach (var channel in Channels)
{
channel.Frequency = 0;
channel.Volume = 0;
channel.NoiseType = 0;
channel.WaveOffset = 0f;
}
}
public SN76489()
{
MaxVolume = short.MaxValue * 2 / 3;
Waves.InitWaves();
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 BeginFrame(int cycles)
{
while (commands.Count > 0)
{
var cmd = commands.Dequeue();
WritePsgDataImmediate(cmd.Value);
}
frameStartTime = cycles;
}
public void Reset()
{
PsgLatch = 0;
foreach (var channel in Channels)
{
channel.Frequency = 0;
channel.Volume = 0;
channel.NoiseType = 0;
channel.WaveOffset = 0f;
}
}
public void EndFrame(int cycles)
{
frameStopTime = cycles;
}
public void BeginFrame(int cycles)
{
while (commands.Count > 0)
{
var cmd = commands.Dequeue();
WritePsgDataImmediate(cmd.Value);
}
frameStartTime = cycles;
}
public void WritePsgData(byte value, int cycles)
{
commands.Enqueue(new QueuedCommand {Value = value, Time = cycles-frameStartTime});
}
public void EndFrame(int cycles)
{
frameStopTime = cycles;
}
void UpdateNoiseType(int value)
{
Channels[3].NoiseType = (byte)(value & 0x07);
switch (Channels[3].NoiseType & 3)
{
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;
}
var newWave = (value & 4) == 0 ? Waves.PeriodicWave16 : Waves.NoiseWave;
if (newWave != Channels[3].Wave)
{
Channels[3].Wave = newWave;
Channels[3].WaveOffset = 0f;
}
}
public void WritePsgData(byte value, int cycles)
{
commands.Enqueue(new QueuedCommand { Value = value, Time = cycles - frameStartTime });
}
void WritePsgDataImmediate(byte value)
{
switch (value & 0xF0)
{
case 0x80:
case 0xA0:
case 0xC0:
PsgLatch = value;
break;
case 0xE0:
PsgLatch = value;
UpdateNoiseType(value);
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) == 3 && channel == 2)
Channels[3].Frequency = (ushort) f;
} else { // volume latched
Channels[channel].Volume = (byte)(~value & 15);
}
break;
}
}
void UpdateNoiseType(int value)
{
Channels[3].NoiseType = (byte)(value & 0x07);
switch (Channels[3].NoiseType & 3)
{
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;
}
var newWave = (value & 4) == 0 ? Waves.PeriodicWave16 : Waves.NoiseWave;
if (newWave != Channels[3].Wave)
{
Channels[3].Wave = newWave;
Channels[3].WaveOffset = 0f;
}
}
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;
}
}
void WritePsgDataImmediate(byte value)
{
switch (value & 0xF0)
{
case 0x80:
case 0xA0:
case 0xC0:
PsgLatch = value;
break;
case 0xE0:
PsgLatch = value;
UpdateNoiseType(value);
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) == 3 && channel == 2)
Channels[3].Frequency = (ushort)f;
}
else
{ // volume latched
Channels[channel].Volume = (byte)(~value & 15);
}
break;
}
}
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 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 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);
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();
}
else
Console.WriteLine("Skipping unrecognized identifier " + args[0]);
}
UpdateNoiseType(Channels[3].NoiseType);
}
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);
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);
}
else
Console.WriteLine("Skipping unrecognized identifier " + args[0]);
}
UpdateNoiseType(Channels[3].NoiseType);
}
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();
UpdateNoiseType(reader.ReadByte());
PsgLatch = reader.ReadByte();
StereoPanning = reader.ReadByte();
}
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);
}
#region Frequency -> Note Conversion (for interested humans)
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();
UpdateNoiseType(reader.ReadByte());
PsgLatch = reader.ReadByte();
StereoPanning = reader.ReadByte();
}
public static string GetNote(int freq)
{
if (freq < 26) return "LOW";
if (freq > 4435) return "HIGH";
#region Frequency -> Note Conversion (for interested humans)
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 "?";
}
public static string GetNote(int freq)
{
if (freq < 26) return "LOW";
if (freq > 4435) return "HIGH";
// 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.
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 "?";
}
static readonly int[] frequencies =
// 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.
static readonly int[] frequencies =
{
27, // A0
29, // A#0
@ -402,7 +406,7 @@ namespace BizHawk.Emulation.Sound
4435 // C#8
};
static readonly string[] notes =
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",
@ -415,38 +419,38 @@ namespace BizHawk.Emulation.Sound
"C8","HIGH"
};
#endregion
#endregion
public int MaxVolume { get; set; }
public void DiscardSamples() { commands.Clear(); }
public void GetSamples(short[] samples)
{
int elapsedCycles = frameStopTime - frameStartTime;
if (elapsedCycles == 0)
elapsedCycles = 1; // hey it's better than diving by zero
public int MaxVolume { get; set; }
public void DiscardSamples() { commands.Clear(); }
public void GetSamples(short[] samples)
{
int elapsedCycles = frameStopTime - frameStartTime;
if (elapsedCycles == 0)
elapsedCycles = 1; // hey it's better than diving by zero
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);
}
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, MaxVolume);
}
public void GetSamplesImmediate(short[] samples, int start, int len)
{
for (int i = 0; i < 4; i++)
Channels[i].Mix(samples, start, len, MaxVolume);
}
class QueuedCommand
{
public byte Value;
public int Time;
}
}
class QueuedCommand
{
public byte Value;
public int Time;
}
}
}

View File

@ -1,5 +1,7 @@
using System.Collections.Generic;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Sound
{
// Generates SEMI-synchronous sound, or "buffered asynchronous" sound.

View File

@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Sound.Utilities
{
/// <summary>

View File

@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Sound
{
/// <summary>

View File

@ -1,27 +1,28 @@
using System.Collections.Generic;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Sound
{
// This is a straightforward class to mix/chain multiple ISoundProvider sources.
// This is a straightforward class to mix/chain multiple ISoundProvider sources.
public sealed class SoundMixer : ISoundProvider
{
List<ISoundProvider> SoundProviders;
public sealed class SoundMixer : ISoundProvider
{
List<ISoundProvider> SoundProviders;
public SoundMixer(params ISoundProvider[] soundProviders)
{
SoundProviders = new List<ISoundProvider>(soundProviders);
}
public void AddSource(ISoundProvider source)
{
SoundProviders.Add(source);
}
public SoundMixer(params ISoundProvider[] soundProviders)
{
SoundProviders = new List<ISoundProvider>(soundProviders);
}
public void DisableSource(ISoundProvider source)
{
SoundProviders.Remove(source);
}
public void AddSource(ISoundProvider source)
{
SoundProviders.Add(source);
}
public void DisableSource(ISoundProvider source)
{
SoundProviders.Remove(source);
}
public void DiscardSamples()
{
@ -29,21 +30,21 @@ namespace BizHawk.Emulation.Sound
soundSource.DiscardSamples();
}
public void GetSamples(short[] samples)
{
foreach (var soundSource in SoundProviders)
soundSource.GetSamples(samples);
}
public void GetSamples(short[] samples)
{
foreach (var soundSource in SoundProviders)
soundSource.GetSamples(samples);
}
// Splits the volume space equally between available sources.
public void EqualizeVolumes()
{
int eachVolume = short.MaxValue / SoundProviders.Count;
foreach (var source in SoundProviders)
source.MaxVolume = eachVolume;
}
// Splits the volume space equally between available sources.
public void EqualizeVolumes()
{
int eachVolume = short.MaxValue / SoundProviders.Count;
foreach (var source in SoundProviders)
source.MaxVolume = eachVolume;
}
// Not actually supported on mixer.
public int MaxVolume { get; set; }
}
// Not actually supported on mixer.
public int MaxVolume { get; set; }
}
}

View File

@ -4,6 +4,8 @@ using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Sound.Utilities
{
/// <summary>

View File

@ -8,6 +8,7 @@
// TODO This should eventually be replaced, due to 1) uncertain licensing terms 2) This is not a native C# implementation, but a naive port.
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Sound
{

View File

@ -3,6 +3,8 @@ using System.Diagnostics;
using System.Collections.Generic;
using System.IO;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Sound
{
// ======================================================================