Move interfaces and base implemenations from emulation to emulation.common
This commit is contained in:
parent
026072ee68
commit
9751fd5a1a
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
public class CoreFileProvider : ICoreFileProvider
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using System.IO;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
public class SaveSlotManager
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
|
||||
using LuaInterface;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
public class MemoryLuaLibrary : LuaLibraryBase
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
using System.IO;
|
||||
using System.Globalization;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
public class Movie
|
||||
|
|
|
@ -5,6 +5,7 @@ using System.Text;
|
|||
using System.IO;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
|
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Text;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
public class MovieSession
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
using System.Globalization;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
public abstract class Watch
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Drawing;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
@ -4,6 +4,7 @@ using System.Text;
|
|||
using System.Diagnostics;
|
||||
|
||||
using BizHawk.Client.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
|
|
|
@ -6,6 +6,7 @@ using System.IO;
|
|||
using System.Drawing;
|
||||
|
||||
using BizHawk.Client.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
public interface IVideoWriter : IDisposable
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -3,6 +3,8 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
@ -5,6 +5,7 @@ using System.Text;
|
|||
using System.IO;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -7,6 +7,7 @@ using SlimDX.Multimedia;
|
|||
#endif
|
||||
|
||||
using BizHawk.Client.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -9,6 +9,7 @@ using System.Threading.Tasks;
|
|||
using System.Windows.Forms;
|
||||
|
||||
using BizHawk.Client.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
|
|
|
@ -10,6 +10,7 @@ using System.IO;
|
|||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Client.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
|
|
|
@ -5,6 +5,7 @@ using System.Text;
|
|||
using System.Globalization;
|
||||
|
||||
using BizHawk.Client.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
|
|
|
@ -7,6 +7,7 @@ using System.Windows.Forms;
|
|||
using System.IO;
|
||||
|
||||
using BizHawk.Client.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -6,6 +6,7 @@ using System.Text;
|
|||
using System.Windows.Forms;
|
||||
|
||||
using BizHawk.Client.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
|
|
|
@ -5,6 +5,7 @@ using System.Linq;
|
|||
using System.Windows.Forms;
|
||||
|
||||
using BizHawk.Client.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
namespace BizHawk
|
||||
namespace BizHawk.Emulation.Common
|
||||
{
|
||||
public class NullController : IController
|
||||
{
|
|
@ -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
|
||||
{
|
|
@ -1,7 +1,8 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
namespace BizHawk
|
||||
|
||||
namespace BizHawk.Emulation.Common
|
||||
{
|
||||
public class CoreComm
|
||||
{
|
|
@ -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
|
|
@ -1,6 +1,6 @@
|
|||
using System.IO;
|
||||
|
||||
namespace BizHawk
|
||||
namespace BizHawk.Emulation.Common
|
||||
{
|
||||
public interface ICoreFileProvider
|
||||
{
|
|
@ -2,7 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace BizHawk
|
||||
namespace BizHawk.Emulation.Common
|
||||
{
|
||||
public interface IEmulator : IDisposable
|
||||
{
|
|
@ -0,0 +1,9 @@
|
|||
namespace BizHawk.Emulation.Common
|
||||
{
|
||||
public interface ISoundProvider
|
||||
{
|
||||
void GetSamples(short[] samples);
|
||||
void DiscardSamples();
|
||||
int MaxVolume { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
namespace BizHawk
|
||||
namespace BizHawk.Emulation.Common
|
||||
{
|
||||
public interface ISyncSoundProvider
|
||||
{
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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" />
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using System.Reflection;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System.IO;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using System.Drawing;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64.MOS
|
||||
{
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Consoles.Atari
|
||||
{
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using System;
|
||||
|
||||
using EMU7800.Core;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Consoles.Intellivision
|
||||
{
|
||||
|
|
|
@ -4,6 +4,7 @@ using System.Runtime.InteropServices;
|
|||
using System.IO;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Consoles.Nintendo.GBA
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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."
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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; } }
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]; } }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
@ -1,4 +1,6 @@
|
|||
namespace BizHawk.Emulation.Consoles.Sega
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Consoles.Sega
|
||||
{
|
||||
partial class Genesis
|
||||
{
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
namespace BizHawk.Emulation.Consoles.Sega
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Consoles.Sega
|
||||
{
|
||||
public partial class SMS
|
||||
{
|
||||
|
|
|
@ -3,6 +3,7 @@ using System.Globalization;
|
|||
using System.IO;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.CPUs.Z80;
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
namespace BizHawk
|
||||
{
|
||||
public interface ISoundProvider
|
||||
{
|
||||
void GetSamples(short[] samples);
|
||||
void DiscardSamples();
|
||||
int MaxVolume { get; set; }
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Sound
|
||||
{
|
||||
// Generates SEMI-synchronous sound, or "buffered asynchronous" sound.
|
||||
|
|
|
@ -3,6 +3,8 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Sound.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Sound
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Sound.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -3,6 +3,8 @@ using System.Diagnostics;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Sound
|
||||
{
|
||||
// ======================================================================
|
||||
|
|
Loading…
Reference in New Issue