commit
042fd6ddaa
File diff suppressed because it is too large
Load Diff
|
@ -937,7 +937,7 @@ namespace BizHawk.Client.Common
|
|||
}
|
||||
break;
|
||||
case "A78":
|
||||
var gamedbpath = Path.Combine(PathManager.GetExeDirectoryAbsolute(), "gamedb", "EMU7800.csv");
|
||||
var gamedbpath = Path.Combine(PathManager.GetExeDirectoryAbsolute(), "gamedb", "gamedb_a7800.csv");
|
||||
nextEmulator = new A7800Hawk(nextComm, game, rom.RomData, gamedbpath, GetCoreSettings<A7800Hawk>(), GetCoreSyncSettings<A7800Hawk>());
|
||||
break;
|
||||
case "C64":
|
||||
|
|
|
@ -822,6 +822,12 @@
|
|||
<Compile Include="tools\GB\GBGPUView.Designer.cs">
|
||||
<DependentUpon>GBGPUView.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="tools\GB\GBPrinterView.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="tools\GB\GBPrinterView.Designer.cs">
|
||||
<DependentUpon>GBPrinterView.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="tools\Genesis\GenDbgWind.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
|
@ -1489,6 +1495,9 @@
|
|||
<EmbeddedResource Include="tools\GB\GBGPUView.resx">
|
||||
<DependentUpon>GBGPUView.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="tools\GB\GBPrinterView.resx">
|
||||
<DependentUpon>GBPrinterView.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="tools\Genesis\GenDbgWind.resx">
|
||||
<DependentUpon>GenDbgWind.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
|
@ -2084,7 +2093,6 @@
|
|||
<None Include="images\checkbox.png" />
|
||||
<None Include="images\Erase.png" />
|
||||
<None Include="images\bsnes.png" />
|
||||
<None Include="images\emu7800.png" />
|
||||
<None Include="images\genplus.png" />
|
||||
<None Include="images\gambatte.png" />
|
||||
<None Include="images\dual.png" />
|
||||
|
@ -2165,4 +2173,4 @@
|
|||
<PreBuildEvent />
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)Build\Common.targets" />
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -306,7 +306,8 @@
|
|||
this.LoadGBInSGBMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.toolStripSeparator28 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.GBGPUViewerMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.GBGameGenieMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.GBPrinterViewerMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.GBGameGenieMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.GBASubMenu = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.GBACoreSelectionSubMenu = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.GBAmGBAMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
|
@ -2744,7 +2745,8 @@
|
|||
this.LoadGBInSGBMenuItem,
|
||||
this.toolStripSeparator28,
|
||||
this.GBGPUViewerMenuItem,
|
||||
this.GBGameGenieMenuItem});
|
||||
this.GBGameGenieMenuItem,
|
||||
this.GBPrinterViewerMenuItem});
|
||||
this.GBSubMenu.Name = "GBSubMenu";
|
||||
this.GBSubMenu.Size = new System.Drawing.Size(34, 19);
|
||||
this.GBSubMenu.Text = "&GB";
|
||||
|
@ -2782,10 +2784,17 @@
|
|||
this.GBGameGenieMenuItem.Size = new System.Drawing.Size(233, 22);
|
||||
this.GBGameGenieMenuItem.Text = "&Game Genie Encoder/Decoder";
|
||||
this.GBGameGenieMenuItem.Click += new System.EventHandler(this.GBGameGenieMenuItem_Click);
|
||||
//
|
||||
// GBASubMenu
|
||||
//
|
||||
this.GBASubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
//
|
||||
// GBPrinterViewerMenuItem
|
||||
//
|
||||
this.GBPrinterViewerMenuItem.Name = "GBPrinterViewerMenuItem";
|
||||
this.GBPrinterViewerMenuItem.Size = new System.Drawing.Size(233, 22);
|
||||
this.GBPrinterViewerMenuItem.Text = "&Printer Viewer";
|
||||
this.GBPrinterViewerMenuItem.Click += new System.EventHandler(this.GBPrinterViewerMenuItem_Click);
|
||||
//
|
||||
// GBASubMenu
|
||||
//
|
||||
this.GBASubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.GBACoreSelectionSubMenu,
|
||||
this.GBAcoresettingsToolStripMenuItem1,
|
||||
this.toolStripSeparator33,
|
||||
|
@ -4155,6 +4164,7 @@
|
|||
private System.Windows.Forms.ToolStripMenuItem SaveMovieContextMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem VirtualPadMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem GBGPUViewerMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem GBPrinterViewerMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem AudioThrottleMenuItem;
|
||||
private System.Windows.Forms.ToolStripSeparator toolStripSeparator27;
|
||||
private System.Windows.Forms.ToolStripMenuItem VsyncEnabledMenuItem;
|
||||
|
|
|
@ -2018,6 +2018,11 @@ namespace BizHawk.Client.EmuHawk
|
|||
GlobalWin.Tools.LoadGameGenieEc();
|
||||
}
|
||||
|
||||
private void GBPrinterViewerMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
GlobalWin.Tools.Load<GBPrinterView>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GBA
|
||||
|
|
|
@ -205,9 +205,6 @@
|
|||
<data name="calculator" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\images\calculator.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="emu7800" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\images\emu7800.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="ts_v_piano_16_green_blue" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\images\tastudio\ts_v_piano_16_green_blue.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
|
@ -1560,4 +1557,4 @@
|
|||
<data name="NGPController" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\images\ControllerImages\NGPController.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
</root>
|
||||
</root>
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 856 B |
|
@ -0,0 +1,148 @@
|
|||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
partial class GBPrinterView
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(GBPrinterView));
|
||||
this.paperView = new BmpView();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.paperScroll = new System.Windows.Forms.VScrollBar();
|
||||
this.menuStrip1 = new System.Windows.Forms.MenuStrip();
|
||||
this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.saveImageToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.editToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.copyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.menuStrip1.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// menuStrip1
|
||||
//
|
||||
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.fileToolStripMenuItem,
|
||||
this.editToolStripMenuItem});
|
||||
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
|
||||
this.menuStrip1.Name = "menuStrip1";
|
||||
this.menuStrip1.Size = new System.Drawing.Size(336, 24);
|
||||
this.menuStrip1.TabIndex = 2;
|
||||
this.menuStrip1.Text = "menuStrip1";
|
||||
//
|
||||
// fileToolStripMenuItem
|
||||
//
|
||||
this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.saveImageToolStripMenuItem});
|
||||
this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
|
||||
this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20);
|
||||
this.fileToolStripMenuItem.Text = "&File";
|
||||
//
|
||||
// saveImageToolStripMenuItem
|
||||
//
|
||||
this.saveImageToolStripMenuItem.Name = "saveImageToolStripMenuItem";
|
||||
this.saveImageToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.S)));
|
||||
this.saveImageToolStripMenuItem.Size = new System.Drawing.Size(183, 22);
|
||||
this.saveImageToolStripMenuItem.Text = "&Save Image...";
|
||||
this.saveImageToolStripMenuItem.Click += new System.EventHandler(this.saveImageToolStripMenuItem_Click);
|
||||
//
|
||||
// editToolStripMenuItem
|
||||
//
|
||||
this.editToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.copyToolStripMenuItem});
|
||||
this.editToolStripMenuItem.Name = "editToolStripMenuItem";
|
||||
this.editToolStripMenuItem.Size = new System.Drawing.Size(39, 20);
|
||||
this.editToolStripMenuItem.Text = "&Edit";
|
||||
//
|
||||
// copyToolStripMenuItem
|
||||
//
|
||||
this.copyToolStripMenuItem.Name = "copyToolStripMenuItem";
|
||||
this.copyToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.C)));
|
||||
this.copyToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
|
||||
this.copyToolStripMenuItem.Text = "&Copy";
|
||||
this.copyToolStripMenuItem.Click += new System.EventHandler(this.copyToolStripMenuItem_Click);
|
||||
//
|
||||
// paperView
|
||||
//
|
||||
this.paperView.Name = "paperView";
|
||||
this.paperView.Location = new System.Drawing.Point(0, 48);
|
||||
this.paperView.Size = new System.Drawing.Size(320, 320);
|
||||
this.paperView.BackColor = System.Drawing.Color.Black;
|
||||
this.paperView.TabIndex = 0;
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Location = new System.Drawing.Point(0, 24);
|
||||
this.label1.Size = new System.Drawing.Size(336, 24);
|
||||
this.label1.Text = "Note: the printer is only connected while this window is open.";
|
||||
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||
//
|
||||
// paperScroll
|
||||
//
|
||||
this.paperScroll.Name = "paperScroll";
|
||||
this.paperScroll.Location = new System.Drawing.Point(320, 48);
|
||||
this.paperScroll.Size = new System.Drawing.Size(16, 320);
|
||||
this.paperScroll.Minimum = 0;
|
||||
this.paperScroll.SmallChange = 8;
|
||||
this.paperScroll.LargeChange = 160;
|
||||
this.paperScroll.ValueChanged += PaperScroll_ValueChanged;
|
||||
//
|
||||
// GBPrinterView
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.AutoSize = true;
|
||||
this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
||||
this.ClientSize = new System.Drawing.Size(336, 358);
|
||||
this.Controls.Add(this.menuStrip1);
|
||||
this.Controls.Add(this.paperView);
|
||||
this.Controls.Add(this.label1);
|
||||
this.Controls.Add(this.paperScroll);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.MainMenuStrip = this.menuStrip1;
|
||||
this.MaximizeBox = false;
|
||||
this.Name = "GBPrinterView";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Text = "Printer Viewer";
|
||||
this.menuStrip1.ResumeLayout(false);
|
||||
this.menuStrip1.PerformLayout();
|
||||
this.FormClosed += GBPrinterView_FormClosed;
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
private System.Windows.Forms.MenuStrip menuStrip1;
|
||||
private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem saveImageToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem editToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem copyToolStripMenuItem;
|
||||
private BmpView paperView;
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.VScrollBar paperScroll;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,215 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using BizHawk.Common.NumberExtensions;
|
||||
using BizHawk.Client.Common;
|
||||
using BizHawk.Emulation.Cores.Nintendo.Gameboy;
|
||||
using BizHawk.Client.EmuHawk.WinFormExtensions;
|
||||
using System.Collections.Generic;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy;
|
||||
using BizHawk.Common;
|
||||
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
public partial class GBPrinterView : Form, IToolFormAutoConfig
|
||||
{
|
||||
const int PaperWidth = 160;
|
||||
|
||||
// the bg color
|
||||
private static readonly uint PaperColor = (uint)Color.AntiqueWhite.ToArgb();
|
||||
|
||||
private ColorMatrix PaperAdjustment;
|
||||
|
||||
[RequiredService]
|
||||
public IGameboyCommon Gb { get; private set; }
|
||||
|
||||
// If we've connected the printer yet
|
||||
bool connected = false;
|
||||
|
||||
// the entire bitmap
|
||||
Bitmap printerHistory;
|
||||
|
||||
public GBPrinterView()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// adjust the color of the printed output to be more papery
|
||||
PaperAdjustment = new ColorMatrix();
|
||||
PaperAdjustment.Matrix00 = (0xFA - 0x10) / 255F;
|
||||
PaperAdjustment.Matrix40 = 0x10 / 255F;
|
||||
PaperAdjustment.Matrix11 = (0xEB - 0x10) / 255F;
|
||||
PaperAdjustment.Matrix41 = 0x10 / 255F;
|
||||
PaperAdjustment.Matrix22 = (0xD7 - 0x18) / 255F;
|
||||
PaperAdjustment.Matrix42 = 0x18 / 255F;
|
||||
|
||||
paperView.ChangeBitmapSize(PaperWidth, PaperWidth);
|
||||
|
||||
ClearPaper();
|
||||
}
|
||||
|
||||
private void GBPrinterView_FormClosed(object sender, FormClosedEventArgs e)
|
||||
{
|
||||
if (Gb != null)
|
||||
{
|
||||
Gb.SetPrinterCallback(null);
|
||||
}
|
||||
}
|
||||
|
||||
public bool UpdateBefore => false;
|
||||
|
||||
public bool AskSaveChanges() => true;
|
||||
|
||||
public void FastUpdate()
|
||||
{
|
||||
}
|
||||
|
||||
public void NewUpdate(ToolFormUpdateType type)
|
||||
{
|
||||
}
|
||||
|
||||
public void Restart()
|
||||
{
|
||||
// Really, there's not necessarilly a reason to clear it at all,
|
||||
// since the paper would still be there,
|
||||
// but it just seems right to get a blank slate on reset.
|
||||
ClearPaper();
|
||||
|
||||
connected = false;
|
||||
}
|
||||
|
||||
public void UpdateValues()
|
||||
{
|
||||
// Automatically connect once the game is running
|
||||
if (!connected)
|
||||
{
|
||||
Gb.SetPrinterCallback(OnPrint);
|
||||
connected = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The printer callback that . See PrinterCallback for details.
|
||||
/// </summary>
|
||||
void OnPrint(IntPtr image, byte height, byte topMargin, byte bottomMargin, byte exposure)
|
||||
{
|
||||
// In this implementation:
|
||||
// the bottom margin and top margin are just white lines at the top and bottom
|
||||
// exposure is ignored
|
||||
|
||||
// The page received image
|
||||
Bitmap page = new Bitmap(PaperWidth, height);
|
||||
|
||||
var bmp = page.LockBits(new Rectangle(0, 0, PaperWidth, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < PaperWidth; x++)
|
||||
{
|
||||
uint pixel;
|
||||
unsafe
|
||||
{
|
||||
// Pixel of the image; it's just sent from the core as a big bitmap that's 160xheight
|
||||
pixel = *(uint*)(image + (x + y * PaperWidth) * sizeof(uint));
|
||||
}
|
||||
|
||||
SetPixel(bmp, x, y, pixel);
|
||||
}
|
||||
}
|
||||
|
||||
page.UnlockBits(bmp);
|
||||
|
||||
// add it to the bottom of the history
|
||||
int oldHeight = printerHistory.Height;
|
||||
ResizeHistory(printerHistory.Height + page.Height + topMargin + bottomMargin);
|
||||
using (var g = Graphics.FromImage(printerHistory))
|
||||
{
|
||||
// Make it brown
|
||||
ImageAttributes a = new ImageAttributes();
|
||||
a.SetColorMatrix(PaperAdjustment);
|
||||
|
||||
g.DrawImage(page, new Rectangle(0, oldHeight + topMargin, page.Width, page.Height), 0F, 0F, page.Width, page.Height, GraphicsUnit.Pixel, a);
|
||||
g.Flush();
|
||||
}
|
||||
RefreshView();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a 2x pixel
|
||||
/// </summary>
|
||||
/// <param name="bmp">The bitmap data to draw to</param>
|
||||
/// <param name="x">X position</param>
|
||||
/// <param name="y">Y position</param>
|
||||
/// <param name="c">The ARGB color to set that pixel to</param>
|
||||
unsafe void SetPixel(BitmapData bmp, int x, int y, uint c)
|
||||
{
|
||||
uint* pixel = (uint*)(bmp.Scan0 + x * 4 + y * bmp.Stride);
|
||||
*pixel = c;
|
||||
}
|
||||
|
||||
void ClearPaper()
|
||||
{
|
||||
ResizeHistory(8);
|
||||
RefreshView();
|
||||
}
|
||||
|
||||
void ResizeHistory(int height)
|
||||
{
|
||||
// copy to a new image of height
|
||||
var newHistory = new Bitmap(PaperWidth, height);
|
||||
using (var g = Graphics.FromImage(newHistory))
|
||||
{
|
||||
g.Clear(Color.FromArgb((int)PaperColor));
|
||||
if (printerHistory != null)
|
||||
g.DrawImage(printerHistory, Point.Empty);
|
||||
g.Flush();
|
||||
}
|
||||
|
||||
if (printerHistory != null)
|
||||
printerHistory.Dispose();
|
||||
printerHistory = newHistory;
|
||||
|
||||
// Update scrollbar, viewport is a square
|
||||
paperScroll.Maximum = Math.Max(0, height);
|
||||
}
|
||||
|
||||
void RefreshView()
|
||||
{
|
||||
using (Graphics g = Graphics.FromImage(paperView.BMP))
|
||||
{
|
||||
g.Clear(Color.FromArgb((int)PaperColor));
|
||||
g.DrawImage(printerHistory, new Point(0, -paperScroll.Value));
|
||||
g.Flush();
|
||||
}
|
||||
|
||||
paperView.Refresh();
|
||||
}
|
||||
|
||||
private void saveImageToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
// slight hack to use the nice SaveFile() feature of a BmpView
|
||||
|
||||
BmpView toSave = new BmpView();
|
||||
toSave.ChangeBitmapSize(printerHistory.Size);
|
||||
using (var g = Graphics.FromImage(toSave.BMP))
|
||||
{
|
||||
g.DrawImage(printerHistory, Point.Empty);
|
||||
g.Flush();
|
||||
}
|
||||
toSave.SaveFile();
|
||||
}
|
||||
|
||||
private void copyToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
Clipboard.SetImage(printerHistory);
|
||||
}
|
||||
|
||||
private void PaperScroll_ValueChanged(object sender, System.EventArgs e)
|
||||
{
|
||||
RefreshView();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,280 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="menuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
AAABAAQAICAAAAEAIACoEAAARgAAACAgAAABAAgAqAgAAO4QAAAQEAAAAQAgAGgEAACWGQAAEBAAAAEA
|
||||
CABoBQAA/h0AACgAAAAgAAAAQAAAAAEAIAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAB7e3tAfHx8v3d3d/9tbW3/aWlp/2lpaf9paWn/aWlp/2lpaf9paWn/aWlp/2lpaf9paWn/aWlp/2lp
|
||||
af9paWn/aWlp/2lpaf9oaGj/ZmZm/3BwcP+EhIT/jo6Ov4+Pj0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAHt7e0B6enq/fX19/4GBgf+Dg4P/g4OD/4ODg/+Dg4P/g4OD/4KCgv+BgYH/fn5+/3x8
|
||||
fP98fHz/fn5+/4GBgf+CgoL/goKC/3x8fP9ycnL/dXV1/4WFhf+MjIzPiIiIcISEhDB/f38QAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAd3d3QHl5eb+JiYn/qKio/7i4uP+3t7f/t7e3/7e3t/+3t7f/tra2/7Gx
|
||||
sf+oqKj/o6Oj/6SkpP+pqan/s7Oz/7a2tv+0tLT/paWl/4iIiP9+fn7/h4eH/4mJie+FhYXPgoKCj4SE
|
||||
hDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB3d3dAeHh4v4+Pj/+7u7v/0dHR/8/Pz//Pz8//0NDQ/9HR
|
||||
0f/Q0ND/ycnJ/729vf+3t7f/uLi4/729vv/IyMr/zs7P/83Nzv+9vb3/np6e/46Ojv+NjY3/ioqK/4KC
|
||||
gv9+fn6/f39/QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHd3d0B4eHi/jo6O/7q6uv/Ozs7/ysrK/8rK
|
||||
yv/Ozs7/0NDQ/9DQ0P/Kysr/vb29/7e3t/+3t7f/urq8/8LCx//IyM3/zMzO/8TExf+ysrL/o6Oj/5iY
|
||||
mP+MjIz/fn5+/3Z2dr93d3dAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAd3d3QHZ2dr+JiYn/ra2t/7e3
|
||||
t/+lpaX/p6en/7y8vP/Jycn/zs7O/8zMzP/Dw8L/urq9/7Kyu/+oqLz/nZ3C/6Cgxf+xscf/ubnD/7i4
|
||||
vP+vr7D/oKCh/5OTlP+JiYr/hYWFz4aGhnCKioowj4+PEAAAAAAAAAAAAAAAAAAAAAB3d3dAdXV1v4CA
|
||||
gP+VlZX/ioqK/2BgYP9mZmb/mpqa/7u7u//Jycn/z8/O/8zMzP/AwMj/qqrE/4iIv/9ZWbn/Vla3/39/
|
||||
uP+cnLn/rq66/7GxtP+kpKf/n5+i/6Skpf+hoaHvkpKSz4uLi4+KioowAAAAAAAAAAAAAAAAAAAAAHNz
|
||||
c0B0dHS/dHR0/3R0dP9gYGD/NjY2/zs7O/9wcHD/m5ub/7y8vP/Ozs7/0NDQ/8PDzv+mpsn/eHjD/zg4
|
||||
u/8xMbf/ZGS1/4SEtf+Rkbf/lJS1/4yMrf+UlK7/ra22/62trv+Wlpb/ioqKv4uLi0AAAAAAAAAAAAAA
|
||||
AAAAAAAAb29vQHBwcL9jY2P/S0tL/zY2Nv8mJib/KCgo/zw8PP9nZ2f/qKio/8rKyv/Pz87/wsLO/6am
|
||||
yv94eMj/ODjJ/zAwxf9hYb7/cXG4/19fsv9YWLH/WVmz/3Jyt/+jo73/sLCy/5eXmP+Kioq/i4uLQAAA
|
||||
AAAAAAAAAAAAAAAAAABvb29AcHBwv2VlZf9QUFD/PDw8/ygoKP8pKSn/Pz8//2pqav+pqan/ysrK/87O
|
||||
zv/Gxs//sLDM/42NzP9cXND/VFTN/3d3w/90dLv/S0u0/zo6tf9AQL3/YWHC/5+fwv+xsbX/l5eZ/4qK
|
||||
ir+Li4tAAAAAAAAAAAAAAAAAAAAAAHNzc0B1dXW/enp6/4WFhf9wcHD/PDw8/z8/P/94eHj/o6Oj/7+/
|
||||
v//Ozs7/0NDQ/83N0P/Gxs//uLjP/6Sk0f+ens3/paXD/42Nvv9VVb3/OzvB/0BAzP9hYc7/n5/H/7Gx
|
||||
tf+Xl5n/ioqKv4uLi0AAAAAAAAAAAAAAAAAAAAAAd3d3QHh4eL+Hh4f/pqam/5ubm/9nZ2f/ampq/6Oj
|
||||
o//ExMT/zMzM/9DQ0P/Q0ND/0dHR/9HR0f/OztH/ysrS/8bGzv/Cwsf/p6fE/3Z2xf9fX8r/YmLT/3t7
|
||||
0/+oqMj/srK1/5iYmf+Kioq/i4uLQAAAAAAAAAAAAAAAAAAAAAB3d3dAeHh4v4yMjP+0tLT/vb29/6io
|
||||
qP+pqan/v7+//8zMzP/Pz8//0NDQ/9DQ0P/R0dH/0dHR/9DQ0f/OztH/zc3Q/8zMzf/Bwcz/r6/N/6am
|
||||
z/+oqNP/r6/Q/7u7x/+0tLX/mJiZ/4qKir+Li4tAAAAAAAAAAAAAAAAAAAAAAHd3d0B4eHi/jo6O/7q6
|
||||
uv/Nzc3/yMjI/8fHx//Ly8v/zc3N/87Ozv/Ozs7/zs7O/87Ozv/Ozs7/zs7O/87Ozv/Ozs7/zs7O/8zM
|
||||
zv/Kys7/ycnP/8nJz//IyMz/xMTG/7W1tf+ZmZn/ioqKv4uLi0AAAAAAAAAAAAAAAAAAAAAAd3d3QHh4
|
||||
eL+Ojo7/uLi4/8zMzP/IyMj/xsbG/8fHx//IyMj/yMjI/8jIyP/IyMj/yMjI/8jIyP/IyMj/yMjI/8jI
|
||||
yP/IyMj/yMjI/8fHyP/Hx8n/x8fJ/8bGyP/ExMT/tbW1/5mZmf+Kioq/i4uLQAAAAAAAAAAAAAAAAAAA
|
||||
AAB3d3dAdnZ2v4aGhv+jo6P/r6+v/6qqqv+nqKj/pqio/6WpqP+lqaj/pamo/6WpqP+lqaj/pamo/6Wp
|
||||
qP+lqaj/pamo/6WpqP+mqaj/pqio/6eoqP+oqKj/rKys/7Kysv+rq6v/lZWV/4qKir+Li4tAAAAAAAAA
|
||||
AAAAAAAAAAAAAHNzc0B0dHS/dnZ2/3t7e/94eXn/b3Bw/2lsbP9nbm3/ZW9t/2Vvbf9lb23/ZW9t/2Vv
|
||||
bf9lb23/ZW9t/2Vvbf9lb23/ZW9t/2Zvbf9obmz/am1s/2xtbP94eHj/j4+P/5eXl/+Pj4//ioqKv4uL
|
||||
i0AAAAAAAAAAAAAAAAAAAAAAb29vQHFxcb9sbGz/Y2Nj/11eXv9bXV3/UWNf/0FtY/84c2b/NnNm/zZz
|
||||
Zv82c2b/NnNm/zZzZv82c2b/NnNm/zZzZv82c2b/O3Bl/0RrYv9OZV//Vl5c/2VkZf95eHn/hYWF/4mJ
|
||||
if+Kioq/i4uLQAAAAAAAAAAAAAAAAAAAAABra2tAbW1tv2dnZ/9bW1v/XV9e/2xzcf9eioD/NKWM/x2z
|
||||
kv8atJP/GLWT/xi1k/8YtZP/GLWT/xi1k/8YtZP/GLWT/xi1k/8krZD/PZ6J/1OOgf9oe3f/cXBx/25u
|
||||
bv90dHT/hISE/4qKir+Li4tAAAAAAAAAAAAAAAAAAAAAAGtra0BsbGy/ZWVl/1dXV/9cX1//dH17/2Se
|
||||
kf8swaD/DdSp/wnWqv8H16r/B9eq/wfXqv8H16r/B9eq/wfXqv8H16r/B9eq/xfNpv83uZ3/VaOS/3GK
|
||||
hf93d3f/aWlp/2xsbP+BgYH/jIyMv4uLi0AAAAAAAAAAAAAAAAAAAAAAa2trQGxsbL9lZWX/V1dX/1xf
|
||||
Xv90fXv/Y5+R/yjDof8J1qr/BNir/wLZq/8C2av/Atmr/wLZq/8C2av/Atmr/wLZq/8C2av/E8+m/zS6
|
||||
nf9To5L/cIqE/3d3d/9paWn/bGxs/4GBgf+MjIy/i4uLQAAAAAAAAAAAAAAAAAAAAABra2tAbGxsv2Vl
|
||||
Zf9XV1f/XF9e/3R9e/9in5L/JsOi/wbXqv8C2av/ANus/wDbrP8A26z/ANus/wDbrP8A26z/ANus/wDb
|
||||
rP8R0Kf/M7ud/1Kkkv9wioT/d3d3/2lpaf9sbGz/gYGB/4yMjL+Li4tAAAAAAAAAAAAAAAAAAAAAAGtr
|
||||
a0BsbGy/ZWVl/1dXV/9cX17/dH17/2Kfkv8mw6L/Bteq/wLZq/8A26z/ANus/wDbrP8A26z/ANus/wDb
|
||||
rP8A26z/ANus/xHQp/8zu53/UqSS/3CKhP93d3f/aWlp/2xsbP+BgYH/jIyMv4uLi0AAAAAAAAAAAAAA
|
||||
AAAAAAAAa2trQGxsbL9lZWX/V1dX/1xfXv90fXv/Yp+S/ybDov8G16r/Atmr/wDbrP8A26z/ANus/wDb
|
||||
rP8A26z/ANus/wDbrP8A26z/EdCn/zO7nf9SpJL/cIqE/3d3d/9paWn/bGxs/4GBgf+MjIy/i4uLQAAA
|
||||
AAAAAAAAAAAAAAAAAABra2tAbGxsv2VlZf9XV1f/XF9e/3R9e/9in5L/JsOi/wbXqv8C2av/ANus/wDb
|
||||
rP8A26z/ANus/wDbrP8A26z/ANus/wDbrP8R0Kf/M7ud/1Kkkv9wioT/d3d3/2lpaf9sbGz/gYGB/4yM
|
||||
jL+Li4tAAAAAAAAAAAAAAAAAAAAAAGtra0BsbGy/ZWVl/1dXV/9cX17/dH17/2Ofkf8ow6H/Cdaq/wTY
|
||||
q/8C2qv/Atqr/wLaq/8C2qv/Atqr/wLaq/8C2qv/Atqr/xPPpv80up3/U6OS/3CKhP93d3f/aWlp/2xs
|
||||
bP+BgYH/jIyMv4uLi0AAAAAAAAAAAAAAAAAAAAAAa2trQGxsbL9lZWX/V1dX/1xfX/90fXv/ZJ6R/yzB
|
||||
oP8N1Kn/Cdaq/wfYqv8H2Kr/B9iq/wfYqv8H2Kr/B9iq/wfYqv8H2Kr/F82m/ze5nf9Vo5L/cYqE/3d3
|
||||
d/9paWn/bGxs/4GBgf+MjIy/i4uLQAAAAAAAAAAAAAAAAAAAAABra2tAbW1tv2dnZ/9bW1v/XWBf/210
|
||||
cv9fi4H/NaaN/x60k/8btpT/GbeU/xm3lP8Zt5T/GbeU/xm3lP8Zt5T/GbeU/xm3lP8lr5H/Pp+K/1SP
|
||||
gv9pfHj/cnFx/25ubv90dHT/hISE/4qKir+Li4tAAAAAAAAAAAAAAAAAAAAAAG9vb0BxcXG/bGxs/2Rk
|
||||
ZP9fYGD/XWBg/1RmYv9EcGb/O3Zp/zl2af85d2n/OXdp/zl3af85d2n/OXdp/zl3af85d2n/OXdp/z5z
|
||||
aP9HbWX/UWdi/1lhX/9nZ2f/enp6/4WFhf+JiYn/ioqKv4uLi0AAAAAAAAAAAAAAAAAAAAAAc3NzQHV1
|
||||
db9xcXH/aGho/2BgYP9XWFj/UVRU/09WVf9NV1X/TVdV/01XVf9NV1X/TVdV/01XVf9NV1X/TVdV/01X
|
||||
Vf9NV1X/TldV/1BWVP9SVVT/VFVU/2FhYf95eXn/hoaG74iIiM+Li4uPioqKMAAAAAAAAAAAAAAAAAAA
|
||||
AAB3d3dAeXl5v3R0dP9oaGj/YGBg/1paWv9XWFj/VlhY/1VZWP9VWVj/VVlY/1VZWP9VWVj/VVlY/1VZ
|
||||
WP9VWVj/VVlY/1VZWP9WWVj/VlhY/1dYWP9YWFj/X19f/2xsbP91dXXPfX19cIqKijCPj48QAAAAAAAA
|
||||
AAAAAAAAAAAAAHt7e0B8fHy/dXV1/2hoaP9gYGD/XFxc/1paWv9aWlr/Wlpa/1paWv9aWlr/Wlpa/1pa
|
||||
Wv9aWlr/Wlpa/1paWv9aWlr/Wlpa/1paWv9aWlr/Wlpa/1paWv9eXl7/ZmZm/2lpab9ra2tAAAAAAAAA
|
||||
AAAAAAAA8AAAP/AAAD/wAAAP8AAAD/AAAA/wAAAP8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AA
|
||||
AAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AA
|
||||
AAPwAAAD8AAAD/AAAA8oAAAAIAAAAEAAAAABAAgAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAA/zMz
|
||||
M/82Njb/ODg4/z57bv87fW//OH9v/zd/cP9DQ0P/SEhI/01NTf9MX1v/T15b/1FRUf9XV1f/VFlY/1dY
|
||||
WP9TXFr/UF5b/1RcWv9YWln/Wlpa/1ldXP9aXFz/XFxc/11eXv9KYVz/TGBc/11lY/9UbWf/XWhm/1Jt
|
||||
aP9HdWv/RnZs/1dzbf9Zcm3/THpw/0t8cf9gYGD/YGJi/2JiYv9hZGP/YWVk/2FmZP9kZGT/ZmZm/2Nq
|
||||
af9ibmv/aGho/2pqav9pbGv/bGxs/25ubv9jcm//ZHNv/29xcf9tcnH/bnNy/2R6dv9ofHf/aHx4/2h/
|
||||
ev9sf3v/b357/3BwcP9ycnL/cXd2/3R0dP92dnb/cnl3/3N8ev90fHr/eHh4/3p6ev95fHv/en18/3x8
|
||||
fP9+fn7/NoBw/zuFdf88hHT/Q4Bz/0CCdP9Whnz/Uol9/2qAfP9sgHz/Skq5/0tLvP9PT77/UVG//1pa
|
||||
uP9jY7T/Z2e1/2pqtv9mZrn/YWG+/2Rkv/9vb7n/bW28/3FxvP99fbj/SkrD/05Owv9RUcf/UlLI/25u
|
||||
wf9pacr/dXXD/3l5xP9wcMr/cXHO/3p6yf87m4b/PpiE/zych/8crY7/HqyN/x2vj/8ero//K6SK/yym
|
||||
i/8mqIv/IKuN/yepjP8hrY7/OLKX/zS2mf84spj/M7ud/zW6nf8wvp//Lr+g/1Sdjf9Ymov/WJqM/1if
|
||||
kP9piIL/aoiB/2qMhf9To5H/V6CQ/1Kkkv8ewp//D8yj/wvOpP8OzaT/DM6k/w7OpP8Yx6H/EM2j/xbN
|
||||
pv8Wzqb/Gcyl/wbXqv8P0qj/CNaq/wPZq/8E2Kv/Btiq/wDarP8R0aj/gICA/4KCgv+DhIT/hISE/4aG
|
||||
hv+IiIj/ioqK/4yMjP+Ojo7/j4+Q/5CQkP+SkpL/lJSU/5aWl/+YmJj/mpqa/5ubnf+cnJ3/np6i/5yc
|
||||
pP+Tk6//n5+p/4qKsf+Kirf/gYG5/5WVv/+amrr/nqKh/6Ghof+jpKT/pKSk/6ampv+lpan/pqau/6mp
|
||||
qf+rq67/ra2t/6KisP+lpbL/qamz/66usv+mprn/pKS+/7CwsP+1tbX/srK4/7e3uv+xsb7/t7e+/7i4
|
||||
uP+9vb7/hobA/4ODxv+KisD/h4fP/5aWw/+amsH/nJzD/5iYxv+QkM3/mprM/6Kixf+oqMT/o6PJ/6Wl
|
||||
y/+goM7/qqrN/7KywP+zs8X/u7vA/76+wf+6usX/vb3H/7e3yf+wsM3/u7vL/7+/yv++vs3/oKDQ/7Gx
|
||||
0P+/wMD/wMHB/8TExf/Bwcr/wMDN/8bGzf/Jycn/y8vN/83Nzf/BwdD/ycnQ/83N0P/Q0ND/AAAA/wBM
|
||||
TExJSURDQ0NDQ0FBQUFBOUFCQ0A0RKOoqaelo6QAAExMTE2ipqmpqaqqqaimpKWnqKmno0lJo6enpqOl
|
||||
pQAASUlJoqq+xs3Nzc3NxMC+vb/FxsazqqWnqKalo6KiAABISEmirs30+Pj4+PTz087O0PL0886+r6qo
|
||||
paJNTaOoAEhISKOvzfP09Pj6/vjy09DS6e716M2+r6qmoqKlp6kASEhITaq/xL7BzvT6+PTn0cza2+DR
|
||||
z8W/r6qpqKipqABERERJpKmlQ0SvzfT5+Org12Nj1bzLyLW1tLOvq6ipAENDQ0NBNBkJCjOvzvj97+Ft
|
||||
Wlhfurm5uLbHwbCsqagAQEBBNDAQCAEDDUnB9Pn24nBpZmBkXlxdZbzIsqyoqQBBQUE0MBgJAgMNS8T0
|
||||
+fvs3W9rbGNbV1pq2ce0rKmoAEREQ0RJSDANDUOx0/j+/fvx8N7c1WBnaW7ZybOsqakARERITaiwq0lM
|
||||
s9P4+v7+/fz77+vf1m5v2N/Js6yoqABISEmircTNwcHT+Pr+/v7+/vr59+7k4/Dk5sqzrKmpAEhISKKu
|
||||
zfPz8/T5+P36+vr6/vr59/f1+/bozb6tqKgASEhJoq7G1PPy8vP08/Pz8/Pz8/Py8+ry6NTNs6ypqQBI
|
||||
SERMp7G/v769vb29vb29vb29vb29vr7BxMCwqqioAENDRERMTaJKRz9VVVVVVVVVPVU+Pz9GSqSsrqyq
|
||||
qakAQEBAQDMxLS4jJVJQUE9PT09PT1BRJCMvN0mlqKioqAA0NDQzLSYqNVNzfHV2dHZ0dnR2fXhyVDo4
|
||||
QUmjqKmpADMzMzEsGSo7h3+VlJSbk5uTm5OUj4CFiUJAQU2nqakAMzMzMSwZKz6Ig6GfnZ2dnZ2dnZyZ
|
||||
go6LRTRBTaepqQAzMzMwKBcrPY2Em5+goKCgoKCgn5iBjotFNEBNp6mpADMzMzEsGSs9jYSbnaCgoKCg
|
||||
oKCemIGOi0U0QU2nqakAMzMzMCgYKz2NhJudnaCgoKCgoJ+YgY6LRTRATaepqQAzMzMxLBkrPYiDm52g
|
||||
oKCgoKCgnpiBjotFNEFNp6mpADMzMzAoGCtViIShn52dnZ2dnZ2cmYKMi0U0QE2nqakAMzMzMSwZKzyH
|
||||
f5WUk5GRkZGRkZaPgIWJRTRDoqepqQA0NDQzLScqNlNzfHV3dnZ2dnZ2fXlyVDs5QUijp6ipAEFBQUAz
|
||||
LCccHSEFB05OTk5OTk4GBCAfHjJEoqaoqakAQ0NDQTQwJhcTDBsaGhoaGhoaGhoLDBMWLEOipqioqABJ
|
||||
SUlIQTEoFRQQDw8PDw8PDw8PDw8QEBUoNEiipqmpAElJSUhBMCYYFBQUFBQUFBQUFBQUFBQUFSYtM0FM
|
||||
p6jwAAA/8AAAP/AAAA/wAAAP8AAAD/AAAA/wAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AA
|
||||
AAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AA
|
||||
AAPwAAAP8AAADygAAAAQAAAAIAAAAAEAIAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHx8
|
||||
fP9paWn/aWlp/2lpaf9paWn/aWlp/2lpaf9paWn/aWlp/2ZmZv+Pj4//AAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAB5eXn/09PT/9LS0v/S0tL/z8/P/7e3t/+5ubn/0tLS/83Nzf+BgYH/i4uL/4ODg/8AAAAAAAAAAAAA
|
||||
AAAAAAAAeHh4/9DQ0P/Gxsb/0NDQ/9HR0f+4uLj/tra2/8PDzP/Ozs//t7e3/5aWlv90dHT/AAAAAAAA
|
||||
AAAAAAAAAAAAAHZ2dv+QkJD/IyMj/6ysrP/Q0ND/0tLR/5iYx/8YGK//gICy/7i4vf+goKX/uLi4/4uL
|
||||
i/8AAAAAAAAAAAAAAABubm7/JCQk/xwcHP8lJSX/xsbG/9HR0f+YmMn/GBjS/3h4vv83N6z/Q0O6/76+
|
||||
w/+Li4v/AAAAAAAAAAAAAAAAeHh4/62trf8lJSX/u7u7/9DQ0P/R0dH/0dHR/8bG0/+6ur//OjrC/0JC
|
||||
2v++vsT/i4uL/wAAAAAAAAAAAAAAAHh4eP/R0dH/xsbG/9DQ0P/R0dH/0dHR/9HR0f/R0dH/0dHR/8rK
|
||||
0v/Ly9P/w8PD/4uLi/8AAAAAAAAAAAAAAAB5eXn/zc3N/8bGxv/Gxsb/xsbG/8bGxv/Gxsb/xsbG/8bG
|
||||
xv/Gxsb/x8fH/8PDw/+Li4v/AAAAAAAAAAAAAAAAc3Nz/2NjY/9NTk7/RlNQ/0VTUP9FU1D/RVNQ/0VT
|
||||
UP9FU1D/SlFP/09PT/+Ojo7/i4uL/wAAAAAAAAAAAAAAAGxsbP9RUVH/gY2K/xLSqP8K1qr/Ctaq/wrW
|
||||
qv8K1qr/Ctaq/0mvmf9/fn//YmJi/4yMjP8AAAAAAAAAAAAAAABsbGz/UFBQ/4CNiv8J1qr/ANus/wDb
|
||||
rP8A26z/ANus/wDbrP9EsZn/f35+/2JiYv+MjIz/AAAAAAAAAAAAAAAAbGxs/1BQUP+AjYr/Cdaq/wDb
|
||||
rP8A26z/ANus/wDbrP8A26z/RLGZ/39+fv9iYmL/jIyM/wAAAAAAAAAAAAAAAGxsbP9QUFD/gI2K/wnW
|
||||
qv8A26z/ANus/wDbrP8A26z/ANus/0Sxmf9/fn7/YmJi/4yMjP8AAAAAAAAAAAAAAABsbGz/UVFR/4GN
|
||||
iv8S0qj/Cteq/wrXqv8K16r/Cteq/wrXqv9Jr5n/f35+/2JiYv+MjIz/AAAAAAAAAAAAAAAAc3Nz/2Vl
|
||||
Zf9RUlL/SldU/0lXVP9JV1T/SVdU/0lXVP9JV1T/TlRT/1NTU/+Pj4//i4uL/wAAAAAAAAAAAAAAAHx8
|
||||
fP9iYmL/Wlpa/1paWv9aWlr/Wlpa/1paWv9aWlr/Wlpa/1paWv9bW1v/ampq/wAAAAAAAAAAwAcAAMAD
|
||||
AADAAwAAwAEAAMABAADAAQAAwAEAAMABAADAAQAAwAEAAMABAADAAQAAwAEAAMABAADAAQAAwAMAACgA
|
||||
AAAQAAAAIAAAAAEACAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAcHBz/IyMj/yQkJP8lJSX/TU5O/09P
|
||||
T/9KUU//RVNQ/0ZTUP9OVFP/SVdU/0pXVP9QUFD/UVFR/1FSUv9TU1P/Wlpa/1tbW/9iYmL/Y2Nj/2Vl
|
||||
Zf9mZmb/aWlp/2pqav9sbGz/bm5u/3Nzc/90dHT/dnZ2/3h4eP95eXn/fHx8/39+fv8YGK//Nzes/0ND
|
||||
uv94eL7/GBjS/zo6wv9CQtr/Sa+Z/0Sxmf8J1qr/Ctaq/wDbrP8S0qj/gYGB/4ODg/+AjYr/gY2K/4uL
|
||||
i/+MjIz/jo6O/4+Pj/+QkJD/lpaW/4CAsv+goKX/rKys/62trf+2trb/t7e3/7i4uP+5ubn/u7u7/7i4
|
||||
vf+6ur//mJjH/5iYyf++vsP/vr7E/8PDw//Gxsb/x8fH/8PDzP/Nzc3/zs7P/8/Pz//GxtP/ysrS/8vL
|
||||
0//Q0ND/0dHR/9LS0f/T09P//////wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
|
||||
AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
|
||||
AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
|
||||
AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
|
||||
AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
|
||||
AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
|
||||
AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
|
||||
AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
|
||||
AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
|
||||
AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
|
||||
AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
|
||||
AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/VVUfFhYWFhYWFxYVNVVVVVVV
|
||||
HlRTU009P1NLLjIvVVVVVR1RSFFSPjxKTD03G1VVVVUcNgE6UVNDIThBOT4yVVVVGQIAA0hSRCUkIiNF
|
||||
MlVVVR07A0BRUlJOQiYnRjJVVVUdUkhRUlJSUlJPUEcyVVVVHktISEhISEhISElHMlVVVRoTBAgHBwcH
|
||||
BwYFNDJVVVUYDTEtKysrKysoIBIzVVVVGAwwKiwsLCwsKSASM1VVVRgMMCosLCwsLCkgEjNVVVUYDDAq
|
||||
LCwsLCwpIBIzVVVVGA0xLSsrKysrKCASM1VVVRoUDgsKCgoKCgkPNTJVVVUfEhAQEBAQEBAQERdVVcAH
|
||||
AADAAwAAwAMAAMABAADAAQAAwAEAAMABAADAAQAAwAEAAMABAADAAQAAwAEAAMABAADAAQAAwAEAAMAD
|
||||
AAA=
|
||||
</value>
|
||||
</data>
|
||||
</root>
|
|
@ -63,9 +63,6 @@
|
|||
<Reference Include="ELFSharp">
|
||||
<HintPath>..\References\ELFSharp.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="EMU7800">
|
||||
<HintPath>..\References\EMU7800.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>..\References\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
|
@ -372,26 +369,6 @@
|
|||
<Compile Include="Consoles\Atari\A7800Hawk\TIA_Sound\Tia.Audio.cs" />
|
||||
<Compile Include="Consoles\Atari\A7800Hawk\TIA_Sound\TIA.cs" />
|
||||
<Compile Include="Consoles\Atari\A7800Hawk\TIA_Sound\Tia.SyncState.cs" />
|
||||
<Compile Include="Consoles\Atari\7800\Atari7800.cs" />
|
||||
<Compile Include="Consoles\Atari\7800\Atari7800.IDebuggable.cs">
|
||||
<DependentUpon>Atari7800.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Atari\7800\Atari7800.IEmulator.cs">
|
||||
<DependentUpon>Atari7800.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Atari\7800\Atari7800.IInputPollable.cs">
|
||||
<DependentUpon>Atari7800.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Atari\7800\Atari7800.IMemoryDomains.cs">
|
||||
<DependentUpon>Atari7800.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Atari\7800\Atari7800.IStatable.cs">
|
||||
<DependentUpon>Atari7800.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Atari\7800\Atari7800Control.cs" />
|
||||
<Compile Include="Consoles\Atari\7800\Atari7800.ISaveRam.cs">
|
||||
<DependentUpon>Atari7800.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Atari\lynx\LibLynx.cs" />
|
||||
<Compile Include="Consoles\Atari\lynx\Lynx.cs" />
|
||||
<Compile Include="Consoles\Atari\lynx\Lynx.IInputPollable.cs">
|
||||
|
@ -535,6 +512,7 @@
|
|||
<Compile Include="Consoles\Nintendo\Gameboy\GambatteLink.IVideoProvider.cs">
|
||||
<DependentUpon>GambatteLink.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Nintendo\Gameboy\GambattePrinter.cs" />
|
||||
<Compile Include="Consoles\Nintendo\Gameboy\GamebatteLink.ISoundProvider.cs">
|
||||
<DependentUpon>GambatteLink.cs</DependentUpon>
|
||||
</Compile>
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Atari.Atari7800
|
||||
{
|
||||
public partial class Atari7800 : IDebuggable
|
||||
{
|
||||
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
|
||||
{
|
||||
return new Dictionary<string, RegisterValue>
|
||||
{
|
||||
["A"] = _theMachine.CPU.A,
|
||||
["P"] = _theMachine.CPU.P,
|
||||
["PC"] = _theMachine.CPU.PC,
|
||||
["S"] = _theMachine.CPU.S,
|
||||
["X"] = _theMachine.CPU.X,
|
||||
["Y"] = _theMachine.CPU.Y,
|
||||
["Flag B"] = _theMachine.CPU.fB,
|
||||
["Flag C"] = _theMachine.CPU.fC,
|
||||
["Flag D"] = _theMachine.CPU.fD,
|
||||
["Flag I"] = _theMachine.CPU.fI,
|
||||
["Flag N"] = _theMachine.CPU.fN,
|
||||
["Flag V"] = _theMachine.CPU.fV,
|
||||
["Flag Z"] = _theMachine.CPU.fZ
|
||||
};
|
||||
}
|
||||
|
||||
public void SetCpuRegister(string register, int value)
|
||||
{
|
||||
switch (register)
|
||||
{
|
||||
default:
|
||||
throw new InvalidOperationException();
|
||||
case "A":
|
||||
_theMachine.CPU.A = (byte)value;
|
||||
break;
|
||||
case "P":
|
||||
_theMachine.CPU.P = (byte)value;
|
||||
break;
|
||||
case "PC":
|
||||
_theMachine.CPU.PC = (ushort)value;
|
||||
break;
|
||||
case "S":
|
||||
_theMachine.CPU.S = (byte)value;
|
||||
break;
|
||||
case "X":
|
||||
_theMachine.CPU.X = (byte)value;
|
||||
break;
|
||||
case "Y":
|
||||
_theMachine.CPU.Y = (byte)value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public IMemoryCallbackSystem MemoryCallbacks
|
||||
{
|
||||
[FeatureNotImplemented]
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public bool CanStep(StepType type)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
[FeatureNotImplemented]
|
||||
public void Step(StepType type)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public int TotalExecutedCycles => (int)_theMachine.CPU.Clock;
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Atari.Atari7800
|
||||
{
|
||||
public partial class Atari7800 : IEmulator
|
||||
{
|
||||
public IEmulatorServiceProvider ServiceProvider { get; }
|
||||
|
||||
public ControllerDefinition ControllerDefinition { get; private set; }
|
||||
|
||||
public void FrameAdvance(IController controller, bool render, bool rendersound)
|
||||
{
|
||||
_frame++;
|
||||
|
||||
if (controller.IsPressed("Power"))
|
||||
{
|
||||
// it seems that theMachine.Reset() doesn't clear ram, etc
|
||||
// this should leave hsram intact but clear most other things
|
||||
HardReset();
|
||||
}
|
||||
|
||||
ControlAdapter.Convert(controller, _theMachine.InputState);
|
||||
_theMachine.ComputeNextFrame(_avProvider.Framebuffer);
|
||||
|
||||
_islag = _theMachine.InputState.Lagged;
|
||||
|
||||
if (_islag)
|
||||
{
|
||||
_lagcount++;
|
||||
}
|
||||
|
||||
_avProvider.FillFrameBuffer();
|
||||
}
|
||||
|
||||
public int Frame => _frame;
|
||||
|
||||
public string SystemId => "A78"; // TODO 2600?
|
||||
|
||||
public bool DeterministicEmulation { get; set; }
|
||||
|
||||
public void ResetCounters()
|
||||
{
|
||||
_frame = 0;
|
||||
_lagcount = 0;
|
||||
_islag = false;
|
||||
}
|
||||
|
||||
public CoreComm CoreComm { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_avProvider != null)
|
||||
{
|
||||
_avProvider.Dispose();
|
||||
_avProvider = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Atari.Atari7800
|
||||
{
|
||||
public partial class Atari7800 : IInputPollable
|
||||
{
|
||||
public int LagCount
|
||||
{
|
||||
get { return _lagcount; }
|
||||
set { _lagcount = value; }
|
||||
}
|
||||
|
||||
public bool IsLagFrame
|
||||
{
|
||||
get { return _islag; }
|
||||
set { _islag = value; }
|
||||
}
|
||||
|
||||
public IInputCallbackSystem InputCallbacks { get; } = new InputCallbackSystem();
|
||||
|
||||
private bool _islag = true;
|
||||
private int _lagcount;
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
using EMU7800.Core;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Atari.Atari7800
|
||||
{
|
||||
public partial class Atari7800
|
||||
{
|
||||
private List<MemoryDomain> _MemoryDomains;
|
||||
|
||||
private IMemoryDomains MemoryDomains;
|
||||
|
||||
public void SetupMemoryDomains(HSC7800 hsc7800)
|
||||
{
|
||||
// reset memory domains
|
||||
if (_MemoryDomains == null)
|
||||
{
|
||||
_MemoryDomains = new List<MemoryDomain>();
|
||||
if (_theMachine is Machine7800)
|
||||
{
|
||||
_MemoryDomains.Add(new MemoryDomainDelegate(
|
||||
"RAM", 0x1000, MemoryDomain.Endian.Unknown,
|
||||
delegate(long addr)
|
||||
{
|
||||
if (addr < 0 || addr >= 0x1000)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
if (addr < 0x800)
|
||||
{
|
||||
return ((Machine7800)_theMachine).RAM1[(ushort)addr];
|
||||
}
|
||||
|
||||
return ((Machine7800)_theMachine).RAM2[(ushort)addr];
|
||||
},
|
||||
|
||||
delegate(long addr, byte val)
|
||||
{
|
||||
if (addr < 0 || addr >= 0x1000)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
else if (addr < 0x800)
|
||||
{
|
||||
((Machine7800)_theMachine).RAM1[(ushort)(addr & 0x800)] = val;
|
||||
}
|
||||
else
|
||||
{
|
||||
((Machine7800)_theMachine).RAM2[(ushort)addr] = val;
|
||||
}
|
||||
}, 1));
|
||||
|
||||
_MemoryDomains.Add(new MemoryDomainByteArray(
|
||||
"BIOS ROM", MemoryDomain.Endian.Unknown,
|
||||
_bios, false, 1));
|
||||
|
||||
if (hsc7800 != null)
|
||||
{
|
||||
_MemoryDomains.Add(new MemoryDomainByteArray(
|
||||
"HSC ROM", MemoryDomain.Endian.Unknown, _hsbios, false, 1));
|
||||
|
||||
_MemoryDomains.Add(new MemoryDomainByteArray(
|
||||
"HSC RAM", MemoryDomain.Endian.Unknown, _hsram, true, 1));
|
||||
}
|
||||
|
||||
_MemoryDomains.Add(new MemoryDomainDelegate(
|
||||
"System Bus", 65536, MemoryDomain.Endian.Unknown,
|
||||
delegate(long addr)
|
||||
{
|
||||
if (addr < 0 || addr >= 0x10000)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
return _theMachine.Mem[(ushort)addr];
|
||||
},
|
||||
delegate(long addr, byte val)
|
||||
{
|
||||
if (addr < 0 || addr >= 0x10000)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
_theMachine.Mem[(ushort)addr] = val;
|
||||
}, 1));
|
||||
}
|
||||
else // todo 2600?
|
||||
{
|
||||
}
|
||||
|
||||
MemoryDomains = new MemoryDomainList(_MemoryDomains);
|
||||
(ServiceProvider as BasicServiceProvider).Register<IMemoryDomains>(MemoryDomains);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
using System;
|
||||
using BizHawk.Emulation.Common;
|
||||
using EMU7800.Core;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Atari.Atari7800
|
||||
{
|
||||
public partial class Atari7800 : ISaveRam
|
||||
{
|
||||
public byte[] CloneSaveRam()
|
||||
{
|
||||
return (byte[])_hsram.Clone();
|
||||
}
|
||||
|
||||
public void StoreSaveRam(byte[] data)
|
||||
{
|
||||
Buffer.BlockCopy(data, 0, _hsram, 0, data.Length);
|
||||
}
|
||||
|
||||
public bool SaveRamModified => _gameInfo.MachineType == MachineType.A7800PAL
|
||||
|| _gameInfo.MachineType == MachineType.A7800NTSC;
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
using System.IO;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
using EMU7800.Core;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Atari.Atari7800
|
||||
{
|
||||
public partial class Atari7800 : IStatable
|
||||
{
|
||||
public bool BinarySaveStatesPreferred => true;
|
||||
|
||||
public void SaveStateText(TextWriter writer)
|
||||
{
|
||||
SyncState(new Serializer(writer));
|
||||
}
|
||||
|
||||
public void LoadStateText(TextReader reader)
|
||||
{
|
||||
SyncState(new Serializer(reader));
|
||||
}
|
||||
|
||||
public void SaveStateBinary(BinaryWriter bw)
|
||||
{
|
||||
SyncState(new Serializer(bw));
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader br)
|
||||
{
|
||||
SyncState(new Serializer(br));
|
||||
}
|
||||
|
||||
public byte[] SaveStateBinary()
|
||||
{
|
||||
MemoryStream ms = new MemoryStream();
|
||||
BinaryWriter bw = new BinaryWriter(ms);
|
||||
SaveStateBinary(bw);
|
||||
bw.Flush();
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
private void SyncState(Serializer ser)
|
||||
{
|
||||
byte[] core = null;
|
||||
if (ser.IsWriter)
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
_theMachine.Serialize(new BinaryWriter(ms));
|
||||
ms.Close();
|
||||
core = ms.ToArray();
|
||||
}
|
||||
|
||||
ser.BeginSection("Atari7800");
|
||||
ser.Sync("core", ref core, false);
|
||||
ser.Sync("Lag", ref _lagcount);
|
||||
ser.Sync("Frame", ref _frame);
|
||||
ser.Sync("IsLag", ref _islag);
|
||||
ser.EndSection();
|
||||
if (ser.IsReader)
|
||||
{
|
||||
_theMachine = MachineBase.Deserialize(new BinaryReader(new MemoryStream(core, false)));
|
||||
_avProvider.ConnectToMachine(_theMachine, _gameInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,301 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
using EMU7800.Core;
|
||||
using EMU7800.Win;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Atari.Atari7800
|
||||
{
|
||||
[Core(
|
||||
"EMU7800",
|
||||
"",
|
||||
isPorted: true,
|
||||
isReleased: true,
|
||||
portedVersion: "v1.5",
|
||||
portedUrl: "http://emu7800.sourceforge.net/")]
|
||||
[ServiceNotApplicable(typeof(ISettable<,>), typeof(IDriveLight))]
|
||||
public partial class Atari7800 : IEmulator, ISaveRam, IDebuggable, IStatable, IInputPollable, IRegionable
|
||||
{
|
||||
// TODO:
|
||||
// some things don't work when you try to plug in a 2600 game
|
||||
static Atari7800()
|
||||
{
|
||||
// add alpha bits to palette tables
|
||||
for (int i = 0; i < TIATables.NTSCPalette.Length; i++)
|
||||
{
|
||||
TIATables.NTSCPalette[i] |= unchecked((int)0xff000000);
|
||||
}
|
||||
|
||||
for (int i = 0; i < TIATables.PALPalette.Length; i++)
|
||||
{
|
||||
TIATables.PALPalette[i] |= unchecked((int)0xff000000);
|
||||
}
|
||||
|
||||
for (int i = 0; i < MariaTables.NTSCPalette.Length; i++)
|
||||
{
|
||||
MariaTables.NTSCPalette[i] |= unchecked((int)0xff000000);
|
||||
}
|
||||
|
||||
for (int i = 0; i < MariaTables.PALPalette.Length; i++)
|
||||
{
|
||||
MariaTables.PALPalette[i] |= unchecked((int)0xff000000);
|
||||
}
|
||||
}
|
||||
|
||||
public Atari7800(CoreComm comm, GameInfo game, byte[] rom, string gameDbFn)
|
||||
{
|
||||
var ser = new BasicServiceProvider(this);
|
||||
ser.Register<IVideoProvider>(_avProvider);
|
||||
ser.Register<ISoundProvider>(_avProvider);
|
||||
ServiceProvider = ser;
|
||||
|
||||
CoreComm = comm;
|
||||
byte[] highscoreBios = comm.CoreFileProvider.GetFirmware("A78", "Bios_HSC", false, "Some functions may not work without the high score BIOS.");
|
||||
byte[] palBios = comm.CoreFileProvider.GetFirmware("A78", "Bios_PAL", false, "The game will not run if the correct region BIOS is not available.");
|
||||
byte[] ntscBios = comm.CoreFileProvider.GetFirmware("A78", "Bios_NTSC", false, "The game will not run if the correct region BIOS is not available.");
|
||||
|
||||
if (GameProgramLibrary.EMU7800DB == null)
|
||||
{
|
||||
GameProgramLibrary.EMU7800DB = new GameProgramLibrary(new StreamReader(gameDbFn));
|
||||
}
|
||||
|
||||
if (rom.Length % 1024 == 128)
|
||||
{
|
||||
Console.WriteLine("Trimming 128 byte .a78 header...");
|
||||
byte[] newrom = new byte[rom.Length - 128];
|
||||
Buffer.BlockCopy(rom, 128, newrom, 0, newrom.Length);
|
||||
rom = newrom;
|
||||
}
|
||||
|
||||
_gameInfo = GameProgramLibrary.EMU7800DB.TryRecognizeRom(rom);
|
||||
CoreComm.RomStatusDetails = _gameInfo.ToString();
|
||||
Console.WriteLine("Rom Determiniation from 7800DB:");
|
||||
Console.WriteLine(_gameInfo.ToString());
|
||||
|
||||
_rom = rom;
|
||||
_hsbios = highscoreBios;
|
||||
_bios = _gameInfo.MachineType == MachineType.A7800PAL ? palBios : ntscBios;
|
||||
_pal = _gameInfo.MachineType == MachineType.A7800PAL || _gameInfo.MachineType == MachineType.A2600PAL;
|
||||
|
||||
if (_bios == null)
|
||||
{
|
||||
throw new MissingFirmwareException("The BIOS corresponding to the region of the game you loaded is required to run Atari 7800 games.");
|
||||
}
|
||||
|
||||
HardReset();
|
||||
}
|
||||
|
||||
public DisplayType Region => _pal ? DisplayType.PAL : DisplayType.NTSC;
|
||||
|
||||
public Atari7800Control ControlAdapter { get; private set; }
|
||||
|
||||
private readonly byte[] _rom;
|
||||
private readonly byte[] _hsbios;
|
||||
private readonly byte[] _bios;
|
||||
private readonly GameProgram _gameInfo;
|
||||
private readonly byte[] _hsram = new byte[2048];
|
||||
private readonly bool _pal;
|
||||
|
||||
private Cart _cart;
|
||||
private MachineBase _theMachine;
|
||||
private int _frame = 0;
|
||||
|
||||
private class ConsoleLogger : ILogger
|
||||
{
|
||||
public void WriteLine(string format, params object[] args)
|
||||
{
|
||||
Console.WriteLine(format, args);
|
||||
}
|
||||
|
||||
public void WriteLine(object value)
|
||||
{
|
||||
Console.WriteLine(value);
|
||||
}
|
||||
|
||||
public void Write(string format, params object[] args)
|
||||
{
|
||||
Console.Write(format, args);
|
||||
}
|
||||
|
||||
public void Write(object value)
|
||||
{
|
||||
Console.Write(value);
|
||||
}
|
||||
}
|
||||
|
||||
private void HardReset()
|
||||
{
|
||||
_cart = Cart.Create(_rom, _gameInfo.CartType);
|
||||
ILogger logger = new ConsoleLogger();
|
||||
|
||||
HSC7800 hsc7800 = null;
|
||||
if (_hsbios != null)
|
||||
{
|
||||
hsc7800 = new HSC7800(_hsbios, _hsram);
|
||||
}
|
||||
|
||||
Bios7800 bios7800 = new Bios7800(_bios);
|
||||
_theMachine = MachineBase.Create(
|
||||
_gameInfo.MachineType,
|
||||
_cart,
|
||||
bios7800,
|
||||
hsc7800,
|
||||
_gameInfo.LController,
|
||||
_gameInfo.RController,
|
||||
logger);
|
||||
|
||||
_theMachine.Reset();
|
||||
_theMachine.InputState.InputPollCallback = InputCallbacks.Call;
|
||||
|
||||
ControlAdapter = new Atari7800Control(_theMachine);
|
||||
ControllerDefinition = ControlAdapter.ControlType;
|
||||
|
||||
_avProvider.ConnectToMachine(_theMachine, _gameInfo);
|
||||
|
||||
SetupMemoryDomains(hsc7800);
|
||||
}
|
||||
|
||||
#region audio\video
|
||||
|
||||
private MyAVProvider _avProvider = new MyAVProvider();
|
||||
|
||||
private class MyAVProvider : IVideoProvider, ISoundProvider, IDisposable
|
||||
{
|
||||
// to sync exactly with audio as this emulator creates and times it, the frame rate should be exactly 60:1 or 50:1
|
||||
private int _frameHz;
|
||||
|
||||
public FrameBuffer Framebuffer { get; private set; }
|
||||
public void ConnectToMachine(MachineBase m, GameProgram g)
|
||||
{
|
||||
_frameHz = m.FrameHZ;
|
||||
Framebuffer = m.CreateFrameBuffer();
|
||||
BufferWidth = Framebuffer.VisiblePitch;
|
||||
BufferHeight = Framebuffer.Scanlines;
|
||||
_vidbuffer = new int[BufferWidth * BufferHeight];
|
||||
|
||||
uint newsamplerate = (uint)m.SoundSampleFrequency;
|
||||
if (newsamplerate != _samplerate)
|
||||
{
|
||||
// really shouldn't happen (after init), but if it does, we're ready
|
||||
_resampler?.Dispose();
|
||||
_resampler = new SpeexResampler((SpeexResampler.Quality)3, newsamplerate, 44100, newsamplerate, 44100, null, null);
|
||||
_samplerate = newsamplerate;
|
||||
_dcfilter = new DCFilter(256);
|
||||
}
|
||||
|
||||
if (g.MachineType == MachineType.A2600PAL)
|
||||
{
|
||||
_palette = TIATables.PALPalette;
|
||||
}
|
||||
else if (g.MachineType == MachineType.A7800PAL)
|
||||
{
|
||||
_palette = MariaTables.PALPalette;
|
||||
}
|
||||
else if (g.MachineType == MachineType.A2600NTSC)
|
||||
{
|
||||
_palette = TIATables.NTSCPalette;
|
||||
}
|
||||
else
|
||||
{
|
||||
_palette = MariaTables.NTSCPalette;
|
||||
}
|
||||
}
|
||||
|
||||
private uint _samplerate;
|
||||
private int[] _vidbuffer;
|
||||
private SpeexResampler _resampler;
|
||||
private DCFilter _dcfilter;
|
||||
private int[] _palette;
|
||||
|
||||
public void FillFrameBuffer()
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* src_ = Framebuffer.VideoBuffer)
|
||||
fixed (int* dst_ = _vidbuffer)
|
||||
fixed (int* pal = _palette)
|
||||
{
|
||||
byte* src = src_;
|
||||
int* dst = dst_;
|
||||
for (int i = 0; i < _vidbuffer.Length; i++)
|
||||
{
|
||||
*dst++ = pal[*src++];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int[] GetVideoBuffer()
|
||||
{
|
||||
return _vidbuffer;
|
||||
}
|
||||
|
||||
public int VirtualWidth => 275;
|
||||
public int VirtualHeight => BufferHeight;
|
||||
public int BufferWidth { get; private set; }
|
||||
public int BufferHeight { get; private set; }
|
||||
public int BackgroundColor => unchecked((int)0xff000000);
|
||||
public int VsyncNumerator => _frameHz;
|
||||
public int VsyncDenominator => 1;
|
||||
|
||||
#region ISoundProvider
|
||||
|
||||
public bool CanProvideAsync => false;
|
||||
|
||||
public void GetSamplesSync(out short[] samples, out int nsamp)
|
||||
{
|
||||
int nsampin = Framebuffer.SoundBufferByteLength;
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* src = Framebuffer.SoundBuffer)
|
||||
{
|
||||
for (int i = 0; i < nsampin; i++)
|
||||
{
|
||||
// the buffer values don't really get very large at all,
|
||||
// so this doesn't overflow
|
||||
short s = (short)(src[i] * 200);
|
||||
_resampler.EnqueueSample(s, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_resampler.GetSamplesSync(out samples, out nsamp);
|
||||
_dcfilter.PushThroughSamples(samples, nsamp * 2);
|
||||
}
|
||||
|
||||
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
|
||||
|
||||
public void SetSyncMode(SyncSoundMode mode)
|
||||
{
|
||||
if (mode == SyncSoundMode.Async)
|
||||
{
|
||||
throw new NotSupportedException("Async mode is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
public void GetSamplesAsync(short[] samples)
|
||||
{
|
||||
throw new InvalidOperationException("Async mode is not supported.");
|
||||
}
|
||||
|
||||
public void DiscardSamples()
|
||||
{
|
||||
_resampler?.DiscardSamples();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_resampler != null)
|
||||
{
|
||||
_resampler.Dispose();
|
||||
_resampler = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,411 +0,0 @@
|
|||
using System;
|
||||
|
||||
using EMU7800.Core;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Atari.Atari7800
|
||||
{
|
||||
public class Atari7800Control
|
||||
{
|
||||
private static readonly ControllerDefinition Joystick = new ControllerDefinition
|
||||
{
|
||||
Name = "Atari 7800 Joystick Controller",
|
||||
BoolButtons =
|
||||
{
|
||||
// hard reset, not passed to EMU7800
|
||||
"Power",
|
||||
|
||||
// on the console
|
||||
"Reset",
|
||||
"Select",
|
||||
"BW", // should be "Color"??
|
||||
"Left Difficulty", // better not put P# on these as they might not correspond to player numbers
|
||||
"Right Difficulty",
|
||||
"Pause",
|
||||
|
||||
// ports
|
||||
"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Trigger",
|
||||
"P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 Trigger"
|
||||
}
|
||||
};
|
||||
|
||||
private static readonly ControllerDefinition Paddles = new ControllerDefinition
|
||||
{
|
||||
Name = "Atari 7800 Paddle Controller",
|
||||
BoolButtons =
|
||||
{
|
||||
// hard reset, not passed to EMU7800
|
||||
"Power",
|
||||
|
||||
// on the console
|
||||
"Reset",
|
||||
"Select",
|
||||
"BW", // should be "Color"??
|
||||
"Left Difficulty", // better not put P# on these as they might not correspond to player numbers
|
||||
"Right Difficulty",
|
||||
|
||||
// ports
|
||||
"P1 Trigger",
|
||||
"P2 Trigger",
|
||||
"P3 Trigger",
|
||||
"P4 Trigger"
|
||||
},
|
||||
FloatControls = // should be in [0..700000]
|
||||
{
|
||||
"P1 Paddle",
|
||||
"P2 Paddle",
|
||||
"P3 Paddle",
|
||||
"P4 Paddle"
|
||||
},
|
||||
FloatRanges =
|
||||
{
|
||||
// what is the center point supposed to be here?
|
||||
new[] { 0.0f, 0.0f, 700000.0f },
|
||||
new[] { 0.0f, 0.0f, 700000.0f },
|
||||
new[] { 0.0f, 0.0f, 700000.0f },
|
||||
new[] { 0.0f, 0.0f, 700000.0f }
|
||||
}
|
||||
};
|
||||
|
||||
private static readonly ControllerDefinition Keypad = new ControllerDefinition
|
||||
{
|
||||
Name = "Atari 7800 Keypad Controller",
|
||||
BoolButtons =
|
||||
{
|
||||
// hard reset, not passed to EMU7800
|
||||
"Power",
|
||||
|
||||
// on the console
|
||||
"Reset",
|
||||
"Select",
|
||||
"BW", // should be "Color"??
|
||||
"Toggle Left Difficulty", // better not put P# on these as they might not correspond to player numbers
|
||||
"Toggle Right Difficulty",
|
||||
|
||||
// ports
|
||||
"P1 Keypad1", "P1 Keypad2", "P1 Keypad3",
|
||||
"P1 Keypad4", "P1 Keypad5", "P1 Keypad6",
|
||||
"P1 Keypad7", "P1 Keypad8", "P1 Keypad9",
|
||||
"P1 KeypadA", "P1 Keypad0", "P1 KeypadP",
|
||||
"P2 Keypad1", "P2 Keypad2", "P2 Keypad3",
|
||||
"P2 Keypad4", "P2 Keypad5", "P2 Keypad6",
|
||||
"P2 Keypad7", "P2 Keypad8", "P2 Keypad9",
|
||||
"P2 KeypadA", "P2 Keypad0", "P2 KeypadP",
|
||||
"P3 Keypad1", "P3 Keypad2", "P3 Keypad3",
|
||||
"P3 Keypad4", "P3 Keypad5", "P3 Keypad6",
|
||||
"P3 Keypad7", "P3 Keypad8", "P3 Keypad9",
|
||||
"P3 KeypadA", "P3 Keypad0", "P3 KeypadP",
|
||||
"P4 Keypad1", "P4 Keypad2", "P4 Keypad3",
|
||||
"P4 Keypad4", "P4 Keypad5", "P4 Keypad6",
|
||||
"P4 Keypad7", "P4 Keypad8", "P4 Keypad9",
|
||||
"P4 KeypadA", "P4 Keypad0", "P4 KeypadP"
|
||||
}
|
||||
};
|
||||
|
||||
private static readonly ControllerDefinition Driving = new ControllerDefinition
|
||||
{
|
||||
Name = "Atari 7800 Driving Controller",
|
||||
BoolButtons =
|
||||
{
|
||||
// hard reset, not passed to EMU7800
|
||||
"Power",
|
||||
|
||||
// on the console
|
||||
"Reset",
|
||||
"Select",
|
||||
"BW", // should be "Color"??
|
||||
"Toggle Left Difficulty", // better not put P# on these as they might not correspond to player numbers
|
||||
"Toggle Right Difficulty",
|
||||
|
||||
// ports
|
||||
"P1 Trigger",
|
||||
"P2 Trigger"
|
||||
},
|
||||
FloatControls = // should be in [0..3]
|
||||
{
|
||||
"P1 Driving",
|
||||
"P2 Driving"
|
||||
},
|
||||
FloatRanges =
|
||||
{
|
||||
new[] { 0.0f, 0.0f, 3.0f },
|
||||
new[] { 0.0f, 0.0f, 3.0f },
|
||||
new[] { 0.0f, 0.0f, 3.0f }
|
||||
}
|
||||
};
|
||||
|
||||
private static readonly ControllerDefinition BoosterGrip = new ControllerDefinition
|
||||
{
|
||||
Name = "Atari 7800 Booster Grip Controller",
|
||||
BoolButtons =
|
||||
{
|
||||
// hard reset, not passed to EMU7800
|
||||
"Power",
|
||||
|
||||
// on the console
|
||||
"Reset",
|
||||
"Select",
|
||||
"BW", // should be "Color"??
|
||||
"Toggle Left Difficulty", // better not put P# on these as they might not correspond to player numbers
|
||||
"Toggle Right Difficulty",
|
||||
|
||||
// ports
|
||||
// NB: as referenced by the emu, p1t2 = p1t2, p1t3 = p2t2, p2t2 = p3t2, p2t3 = p4t2
|
||||
"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Trigger", "P1 Trigger 2", "P1 Trigger 3",
|
||||
"P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 Trigger", "P2 Trigger 2", "P2 Trigger 3"
|
||||
}
|
||||
};
|
||||
|
||||
private static readonly ControllerDefinition ProLineJoystick = new ControllerDefinition
|
||||
{
|
||||
Name = "Atari 7800 ProLine Joystick Controller",
|
||||
BoolButtons =
|
||||
{
|
||||
// hard reset, not passed to EMU7800
|
||||
"Power",
|
||||
|
||||
// on the console
|
||||
"Reset",
|
||||
"Select",
|
||||
"BW", // should be "Color"??
|
||||
"Toggle Left Difficulty", // better not put P# on these as they might not correspond to player numbers
|
||||
"Toggle Right Difficulty",
|
||||
"Pause",
|
||||
|
||||
// ports
|
||||
"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Trigger", "P1 Trigger 2",
|
||||
"P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 Trigger", "P2 Trigger 2"
|
||||
}
|
||||
};
|
||||
|
||||
private static readonly ControllerDefinition Lightgun = new ControllerDefinition
|
||||
{
|
||||
Name = "Atari 7800 Light Gun Controller",
|
||||
BoolButtons =
|
||||
{
|
||||
// hard reset, not passed to EMU7800
|
||||
"Power",
|
||||
|
||||
// on the console
|
||||
"Reset",
|
||||
"Select",
|
||||
"Pause",
|
||||
"Left Difficulty", // better not put P# on these as they might not correspond to player numbers
|
||||
"Right Difficulty",
|
||||
|
||||
// ports
|
||||
"P1 Trigger",
|
||||
"P2 Trigger"
|
||||
},
|
||||
FloatControls = // vpos should be actual scanline number. hpos should be in [0..319]??
|
||||
{
|
||||
"P1 VPos", "P1 HPos",
|
||||
"P2 VPos", "P2 HPos"
|
||||
},
|
||||
FloatRanges =
|
||||
{
|
||||
// how many scanlines are there again??
|
||||
new[] { 0.0f, 0.0f, 240.0f },
|
||||
new[] { 0.0f, 0.0f, 319.0f },
|
||||
new[] { 0.0f, 0.0f, 240.0f },
|
||||
new[] { 0.0f, 0.0f, 319.0f }
|
||||
}
|
||||
};
|
||||
|
||||
private struct ControlAdapter
|
||||
{
|
||||
public readonly ControllerDefinition Type;
|
||||
public readonly Controller Left;
|
||||
public readonly Controller Right;
|
||||
public readonly Action<IController, InputState> Convert;
|
||||
|
||||
public ControlAdapter(ControllerDefinition type, Controller left, Controller right, Action<IController, InputState> convert)
|
||||
{
|
||||
Type = type;
|
||||
Left = left;
|
||||
Right = right;
|
||||
Convert = convert;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly ControlAdapter[] Adapters =
|
||||
{
|
||||
new ControlAdapter(Joystick, Controller.Joystick, Controller.Joystick, ConvertJoystick),
|
||||
new ControlAdapter(Paddles, Controller.Paddles, Controller.Paddles, ConvertPaddles),
|
||||
new ControlAdapter(Keypad, Controller.Keypad, Controller.Keypad, ConvertKeypad),
|
||||
new ControlAdapter(Driving, Controller.Driving, Controller.Driving, ConvertDriving),
|
||||
new ControlAdapter(BoosterGrip, Controller.BoosterGrip, Controller.BoosterGrip, ConvertBoosterGrip),
|
||||
new ControlAdapter(ProLineJoystick, Controller.ProLineJoystick, Controller.ProLineJoystick, ConvertProLineJoystick),
|
||||
new ControlAdapter(Lightgun, Controller.Lightgun, Controller.Lightgun, ConvertLightgun),
|
||||
};
|
||||
|
||||
private static void ConvertConsoleButtons(IController c, InputState s)
|
||||
{
|
||||
s.RaiseInput(0, MachineInput.Reset, c.IsPressed("Reset"));
|
||||
s.RaiseInput(0, MachineInput.Select, c.IsPressed("Select"));
|
||||
s.RaiseInput(0, MachineInput.Color, c.IsPressed("BW"));
|
||||
if (c.IsPressed("Toggle Left Difficulty"))
|
||||
{
|
||||
s.RaiseInput(0, MachineInput.LeftDifficulty, c.IsPressed("Toggle Left Difficulty"));
|
||||
}
|
||||
|
||||
if (c.IsPressed("Toggle Right Difficulty"))
|
||||
{
|
||||
s.RaiseInput(0, MachineInput.RightDifficulty, c.IsPressed("Toggle Right Difficulty"));
|
||||
}
|
||||
}
|
||||
|
||||
private static void ConvertConsoleButtons7800(IController c, InputState s)
|
||||
{
|
||||
s.RaiseInput(0, MachineInput.Reset, c.IsPressed("Reset"));
|
||||
s.RaiseInput(0, MachineInput.Select, c.IsPressed("Select"));
|
||||
s.RaiseInput(0, MachineInput.Color, c.IsPressed("Pause"));
|
||||
if (c.IsPressed("Toggle Left Difficulty"))
|
||||
{
|
||||
s.RaiseInput(0, MachineInput.LeftDifficulty, c.IsPressed("Toggle Left Difficulty"));
|
||||
}
|
||||
|
||||
if (c.IsPressed("Toggle Right Difficulty"))
|
||||
{
|
||||
s.RaiseInput(0, MachineInput.RightDifficulty, c.IsPressed("Toggle Right Difficulty"));
|
||||
}
|
||||
}
|
||||
|
||||
private static void ConvertDirections(IController c, InputState s, int p)
|
||||
{
|
||||
string ps = $"P{p + 1} ";
|
||||
s.RaiseInput(p, MachineInput.Up, c.IsPressed(ps + "Up"));
|
||||
s.RaiseInput(p, MachineInput.Down, c.IsPressed(ps + "Down"));
|
||||
s.RaiseInput(p, MachineInput.Left, c.IsPressed(ps + "Left"));
|
||||
s.RaiseInput(p, MachineInput.Right, c.IsPressed(ps + "Right"));
|
||||
}
|
||||
|
||||
private static void ConvertTrigger(IController c, InputState s, int p)
|
||||
{
|
||||
string ps = $"P{p + 1} ";
|
||||
s.RaiseInput(p, MachineInput.Fire, c.IsPressed(ps + "Trigger"));
|
||||
}
|
||||
|
||||
private static void ConvertJoystick(IController c, InputState s)
|
||||
{
|
||||
s.ClearControllerInput();
|
||||
ConvertConsoleButtons(c, s);
|
||||
ConvertDirections(c, s, 0);
|
||||
ConvertDirections(c, s, 1);
|
||||
ConvertTrigger(c, s, 0);
|
||||
ConvertTrigger(c, s, 1);
|
||||
}
|
||||
|
||||
private static void ConvertPaddles(IController c, InputState s)
|
||||
{
|
||||
s.ClearControllerInput();
|
||||
ConvertConsoleButtons(c, s);
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
string ps = $"P{i + 1} ";
|
||||
ConvertTrigger(c, s, i);
|
||||
s.RaisePaddleInput(i, 700000, (int)c.GetFloat(ps + "Trigger"));
|
||||
}
|
||||
}
|
||||
|
||||
private static void ConvertKeypad(IController c, InputState s)
|
||||
{
|
||||
s.ClearControllerInput();
|
||||
ConvertConsoleButtons(c, s);
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
string ps = $"P{i + 1} ";
|
||||
s.RaiseInput(i, MachineInput.NumPad1, c.IsPressed(ps + "Keypad1"));
|
||||
s.RaiseInput(i, MachineInput.NumPad2, c.IsPressed(ps + "Keypad2"));
|
||||
s.RaiseInput(i, MachineInput.NumPad3, c.IsPressed(ps + "Keypad3"));
|
||||
s.RaiseInput(i, MachineInput.NumPad4, c.IsPressed(ps + "Keypad4"));
|
||||
s.RaiseInput(i, MachineInput.NumPad5, c.IsPressed(ps + "Keypad5"));
|
||||
s.RaiseInput(i, MachineInput.NumPad6, c.IsPressed(ps + "Keypad6"));
|
||||
s.RaiseInput(i, MachineInput.NumPad7, c.IsPressed(ps + "Keypad7"));
|
||||
s.RaiseInput(i, MachineInput.NumPad8, c.IsPressed(ps + "Keypad8"));
|
||||
s.RaiseInput(i, MachineInput.NumPad9, c.IsPressed(ps + "Keypad9"));
|
||||
s.RaiseInput(i, MachineInput.NumPadMult, c.IsPressed(ps + "KeypadA"));
|
||||
s.RaiseInput(i, MachineInput.NumPad0, c.IsPressed(ps + "Keypad0"));
|
||||
s.RaiseInput(i, MachineInput.NumPadHash, c.IsPressed(ps + "KeypadP"));
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly MachineInput[] Drvlut =
|
||||
{
|
||||
MachineInput.Driving0,
|
||||
MachineInput.Driving1,
|
||||
MachineInput.Driving2,
|
||||
MachineInput.Driving3
|
||||
};
|
||||
|
||||
private static void ConvertDriving(IController c, InputState s)
|
||||
{
|
||||
s.ClearControllerInput();
|
||||
ConvertConsoleButtons(c, s);
|
||||
ConvertTrigger(c, s, 0);
|
||||
ConvertTrigger(c, s, 1);
|
||||
s.RaiseInput(0, Drvlut[(int)c.GetFloat("P1 Driving")], true);
|
||||
s.RaiseInput(1, Drvlut[(int)c.GetFloat("P2 Driving")], true);
|
||||
}
|
||||
|
||||
private static void ConvertBoosterGrip(IController c, InputState s)
|
||||
{
|
||||
s.ClearControllerInput();
|
||||
ConvertConsoleButtons(c, s);
|
||||
ConvertDirections(c, s, 0);
|
||||
ConvertDirections(c, s, 1);
|
||||
|
||||
// weird mapping is intentional
|
||||
s.RaiseInput(0, MachineInput.Fire, c.IsPressed("P1 Trigger"));
|
||||
s.RaiseInput(0, MachineInput.Fire2, c.IsPressed("P1 Trigger 2"));
|
||||
s.RaiseInput(1, MachineInput.Fire2, c.IsPressed("P1 Trigger 3"));
|
||||
s.RaiseInput(1, MachineInput.Fire, c.IsPressed("P2 Trigger"));
|
||||
s.RaiseInput(2, MachineInput.Fire2, c.IsPressed("P2 Trigger 2"));
|
||||
s.RaiseInput(3, MachineInput.Fire2, c.IsPressed("P2 Trigger 3"));
|
||||
}
|
||||
|
||||
private static void ConvertProLineJoystick(IController c, InputState s)
|
||||
{
|
||||
s.ClearControllerInput();
|
||||
ConvertConsoleButtons7800(c, s);
|
||||
ConvertDirections(c, s, 0);
|
||||
ConvertDirections(c, s, 1);
|
||||
s.RaiseInput(0, MachineInput.Fire, c.IsPressed("P1 Trigger"));
|
||||
s.RaiseInput(0, MachineInput.Fire2, c.IsPressed("P1 Trigger 2"));
|
||||
s.RaiseInput(1, MachineInput.Fire, c.IsPressed("P2 Trigger"));
|
||||
s.RaiseInput(1, MachineInput.Fire2, c.IsPressed("P2 Trigger 2"));
|
||||
}
|
||||
|
||||
private static void ConvertLightgun(IController c, InputState s)
|
||||
{
|
||||
s.ClearControllerInput();
|
||||
ConvertConsoleButtons7800(c, s);
|
||||
ConvertTrigger(c, s, 0);
|
||||
ConvertTrigger(c, s, 1);
|
||||
s.RaiseLightgunPos(0, (int)c.GetFloat("P1 VPos"), (int)c.GetFloat("P1 HPos"));
|
||||
s.RaiseLightgunPos(1, (int)c.GetFloat("P2 VPos"), (int)c.GetFloat("P2 HPos"));
|
||||
}
|
||||
|
||||
public Action<IController, InputState> Convert { get; private set; }
|
||||
|
||||
public ControllerDefinition ControlType { get; private set; }
|
||||
|
||||
public Atari7800Control(MachineBase mac)
|
||||
{
|
||||
var l = mac.InputState.LeftControllerJack;
|
||||
var r = mac.InputState.RightControllerJack;
|
||||
|
||||
foreach (var a in Adapters)
|
||||
{
|
||||
if (a.Left == l && a.Right == r)
|
||||
{
|
||||
Convert = a.Convert;
|
||||
ControlType = a.Type;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception($"Couldn't connect Atari 7800 controls \"{l}\" and \"{r}\"");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -497,6 +497,30 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
|
|||
}
|
||||
}
|
||||
|
||||
GambattePrinter printer;
|
||||
|
||||
/// <summary>
|
||||
/// set up Printer callback
|
||||
/// </summary>
|
||||
public void SetPrinterCallback(PrinterCallback callback)
|
||||
{
|
||||
// Copying SetScanlineCallback for this check, I assume this is still a bug somewhere
|
||||
if (GambatteState == IntPtr.Zero)
|
||||
{
|
||||
return; // not sure how this is being reached. tried the debugger...
|
||||
}
|
||||
|
||||
if (callback != null)
|
||||
{
|
||||
printer = new GambattePrinter(this, callback);
|
||||
}
|
||||
else
|
||||
{
|
||||
printer.Disconnect();
|
||||
printer = null;
|
||||
}
|
||||
}
|
||||
|
||||
LibGambatte.ScanlineCallback scanlinecb;
|
||||
ScanlineCallback endofframecallback;
|
||||
|
||||
|
|
|
@ -0,0 +1,314 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
|
||||
{
|
||||
/// <summary>
|
||||
/// Emulate the gameboy printer in managed code
|
||||
/// </summary>
|
||||
public class GambattePrinter
|
||||
{
|
||||
// A loose c->c# port of SameBoy's printer code
|
||||
|
||||
enum CommandState : byte
|
||||
{
|
||||
GB_PRINTER_COMMAND_MAGIC1,
|
||||
GB_PRINTER_COMMAND_MAGIC2,
|
||||
GB_PRINTER_COMMAND_ID,
|
||||
GB_PRINTER_COMMAND_COMPRESSION,
|
||||
GB_PRINTER_COMMAND_LENGTH_LOW,
|
||||
GB_PRINTER_COMMAND_LENGTH_HIGH,
|
||||
GB_PRINTER_COMMAND_DATA,
|
||||
GB_PRINTER_COMMAND_CHECKSUM_LOW,
|
||||
GB_PRINTER_COMMAND_CHECKSUM_HIGH,
|
||||
GB_PRINTER_COMMAND_ACTIVE,
|
||||
GB_PRINTER_COMMAND_STATUS,
|
||||
}
|
||||
enum CommandID : byte
|
||||
{
|
||||
GB_PRINTER_INIT_COMMAND = 1,
|
||||
GB_PRINTER_START_COMMAND = 2,
|
||||
GB_PRINTER_DATA_COMMAND = 4,
|
||||
GB_PRINTER_NOP_COMMAND = 0xF,
|
||||
}
|
||||
|
||||
const int GB_PRINTER_MAX_COMMAND_LENGTH = 0x280;
|
||||
const int GB_PRINTER_DATA_SIZE = 0x280;
|
||||
|
||||
const ushort SerialIRQAddress = 0x58;
|
||||
|
||||
Gameboy gb;
|
||||
PrinterCallback callback;
|
||||
LibGambatte.LinkCallback linkCallback;
|
||||
|
||||
CommandState command_state;
|
||||
CommandID command_id;
|
||||
|
||||
bool compression;
|
||||
ushort length_left;
|
||||
byte[] command_data = new byte[GB_PRINTER_MAX_COMMAND_LENGTH];
|
||||
ushort command_length;
|
||||
ushort checksum;
|
||||
byte status;
|
||||
|
||||
byte[] image = new byte[160 * 200];
|
||||
ushort image_offset;
|
||||
|
||||
byte compression_run_lenth;
|
||||
bool compression_run_is_compressed;
|
||||
|
||||
public GambattePrinter(Gameboy gb, PrinterCallback callback)
|
||||
{
|
||||
this.gb = gb;
|
||||
this.callback = callback;
|
||||
|
||||
linkCallback = OnSerial;
|
||||
LibGambatte.gambatte_setlinkcallback(gb.GambatteState, linkCallback);
|
||||
|
||||
// connect the cable
|
||||
LibGambatte.gambatte_linkstatus(gb.GambatteState, 259);
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
if (gb.GambatteState != IntPtr.Zero)
|
||||
LibGambatte.gambatte_setlinkcallback(gb.GambatteState, null);
|
||||
}
|
||||
|
||||
void OnSerial()
|
||||
{
|
||||
if (LibGambatte.gambatte_linkstatus(gb.GambatteState, 256) != 0) // ClockTrigger
|
||||
{
|
||||
LibGambatte.gambatte_linkstatus(gb.GambatteState, 257); // ack
|
||||
|
||||
byte output = HandleSerial((byte)LibGambatte.gambatte_linkstatus(gb.GambatteState, 258)); // GetOut
|
||||
LibGambatte.gambatte_linkstatus(gb.GambatteState, output); // ShiftIn
|
||||
}
|
||||
}
|
||||
|
||||
byte HandleSerial(byte byte_received)
|
||||
{
|
||||
byte byte_to_send = 0;
|
||||
|
||||
switch (command_state)
|
||||
{
|
||||
case CommandState.GB_PRINTER_COMMAND_MAGIC1:
|
||||
if (byte_received != 0x88)
|
||||
{
|
||||
return byte_to_send;
|
||||
}
|
||||
status &= 254;
|
||||
command_length = 0;
|
||||
checksum = 0;
|
||||
break;
|
||||
|
||||
case CommandState.GB_PRINTER_COMMAND_MAGIC2:
|
||||
if (byte_received != 0x33)
|
||||
{
|
||||
if (byte_received != 0x88)
|
||||
{
|
||||
command_state = CommandState.GB_PRINTER_COMMAND_MAGIC1;
|
||||
}
|
||||
return byte_to_send;
|
||||
}
|
||||
break;
|
||||
|
||||
case CommandState.GB_PRINTER_COMMAND_ID:
|
||||
command_id = (CommandID)(byte_received & 0xF);
|
||||
break;
|
||||
|
||||
case CommandState.GB_PRINTER_COMMAND_COMPRESSION:
|
||||
compression = (byte_received & 1) != 0;
|
||||
break;
|
||||
|
||||
case CommandState.GB_PRINTER_COMMAND_LENGTH_LOW:
|
||||
length_left = byte_received;
|
||||
break;
|
||||
|
||||
case CommandState.GB_PRINTER_COMMAND_LENGTH_HIGH:
|
||||
length_left |= (ushort)((byte_received & 3) << 8);
|
||||
break;
|
||||
|
||||
case CommandState.GB_PRINTER_COMMAND_DATA:
|
||||
if (command_length != GB_PRINTER_MAX_COMMAND_LENGTH)
|
||||
{
|
||||
if (compression)
|
||||
{
|
||||
if (compression_run_lenth == 0)
|
||||
{
|
||||
compression_run_is_compressed = (byte_received & 0x80) != 0;
|
||||
compression_run_lenth = (byte)((byte_received & 0x7F) + 1 + (compression_run_is_compressed ? 1 : 0));
|
||||
}
|
||||
else if (compression_run_is_compressed)
|
||||
{
|
||||
while (compression_run_lenth > 0)
|
||||
{
|
||||
command_data[command_length++] = byte_received;
|
||||
compression_run_lenth--;
|
||||
if (command_length == GB_PRINTER_MAX_COMMAND_LENGTH)
|
||||
{
|
||||
compression_run_lenth = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
command_data[command_length++] = byte_received;
|
||||
compression_run_lenth--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
command_data[command_length++] = byte_received;
|
||||
}
|
||||
}
|
||||
length_left--;
|
||||
break;
|
||||
|
||||
case CommandState.GB_PRINTER_COMMAND_CHECKSUM_LOW:
|
||||
checksum ^= byte_received;
|
||||
break;
|
||||
|
||||
case CommandState.GB_PRINTER_COMMAND_CHECKSUM_HIGH:
|
||||
checksum ^= (ushort)(byte_received << 8);
|
||||
if (checksum != 0)
|
||||
{
|
||||
status |= 1; /* Checksum error*/
|
||||
command_state = CommandState.GB_PRINTER_COMMAND_MAGIC1;
|
||||
return byte_to_send;
|
||||
}
|
||||
break;
|
||||
|
||||
case CommandState.GB_PRINTER_COMMAND_ACTIVE:
|
||||
byte_to_send = 0x81;
|
||||
break;
|
||||
|
||||
case CommandState.GB_PRINTER_COMMAND_STATUS:
|
||||
|
||||
if (((int)command_id & 0xF) == (byte)CommandID.GB_PRINTER_INIT_COMMAND)
|
||||
{
|
||||
/* Games expect INIT commands to return 0? */
|
||||
byte_to_send = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte_to_send = status;
|
||||
}
|
||||
|
||||
/* Printing is done instantly, but let the game recieve a 6 (Printing) status at least once, for compatibility */
|
||||
if (status == 6)
|
||||
{
|
||||
status = 4; /* Done */
|
||||
}
|
||||
|
||||
command_state = CommandState.GB_PRINTER_COMMAND_MAGIC1;
|
||||
HandleCommand();
|
||||
return byte_to_send;
|
||||
}
|
||||
|
||||
if (command_state >= CommandState.GB_PRINTER_COMMAND_ID && command_state < CommandState.GB_PRINTER_COMMAND_CHECKSUM_LOW)
|
||||
{
|
||||
checksum += byte_received;
|
||||
}
|
||||
|
||||
if (command_state != CommandState.GB_PRINTER_COMMAND_DATA)
|
||||
{
|
||||
command_state++;
|
||||
}
|
||||
|
||||
if (command_state == CommandState.GB_PRINTER_COMMAND_DATA)
|
||||
{
|
||||
if (length_left == 0)
|
||||
{
|
||||
command_state++;
|
||||
}
|
||||
}
|
||||
|
||||
return byte_to_send;
|
||||
}
|
||||
|
||||
void HandleCommand()
|
||||
{
|
||||
switch (command_id)
|
||||
{
|
||||
case CommandID.GB_PRINTER_INIT_COMMAND:
|
||||
status = 0;
|
||||
image_offset = 0;
|
||||
break;
|
||||
|
||||
case CommandID.GB_PRINTER_START_COMMAND:
|
||||
if (command_length == 4)
|
||||
{
|
||||
status = 6; /* Printing */
|
||||
uint[] outputImage = new uint[image_offset];
|
||||
|
||||
int palette = command_data[2];
|
||||
uint[] colors = new uint[] {
|
||||
0xFFFFFFFFU,
|
||||
0xFFAAAAAAU,
|
||||
0xFF555555U,
|
||||
0xFF000000U
|
||||
};
|
||||
for (int i = 0; i < image_offset; i++)
|
||||
{
|
||||
outputImage[i] = colors[(palette >> (image[i] * 2)) & 3];
|
||||
}
|
||||
|
||||
if (callback != null)
|
||||
{
|
||||
// The native-friendly callback almost seems silly now :P
|
||||
unsafe
|
||||
{
|
||||
fixed (uint* imagePtr = outputImage)
|
||||
{
|
||||
callback((IntPtr)imagePtr, (byte)(image_offset / 160),
|
||||
(byte)(command_data[1] >> 4), (byte)(command_data[1] & 7),
|
||||
(byte)(command_data[3] & 0x7F));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
image_offset = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case CommandID.GB_PRINTER_DATA_COMMAND:
|
||||
if (command_length == GB_PRINTER_DATA_SIZE)
|
||||
{
|
||||
image_offset %= (ushort)image.Length;
|
||||
status = 8; /* Received 0x280 bytes */
|
||||
|
||||
int data_index = 0;
|
||||
|
||||
for (int row = 2; row > 0; row--)
|
||||
{
|
||||
for (int tile_x = 0; tile_x < 160 / 8; tile_x++)
|
||||
{
|
||||
for (int y = 0; y < 8; y++, data_index += 2)
|
||||
{
|
||||
for (int x_pixel = 0; x_pixel < 8; x_pixel++)
|
||||
{
|
||||
image[image_offset + tile_x * 8 + x_pixel + y * 160] =
|
||||
(byte)((command_data[data_index] >> 7) | ((command_data[data_index + 1] >> 7) << 1));
|
||||
command_data[data_index] <<= 1;
|
||||
command_data[data_index + 1] <<= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
image_offset += 8 * 160;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,6 +13,15 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy
|
|||
/// <param name="lcdc">current value of register $ff40 (LCDC)</param>
|
||||
public delegate void ScanlineCallback(byte lcdc);
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="image">The image data</param>
|
||||
/// <param name="height">How tall an image is, in pixels. Image is only valid up to that height and must be assumed to be garbage below that.</param>
|
||||
/// <param name="top_margin">The top margin of blank pixels. Just form feeds the printer a certain amount at the top.</param>
|
||||
/// <param name="bottom_margin">The bottom margin of blank pixels. Just form feeds the printer a certain amount at the bottom.</param>
|
||||
/// <param name="exposure">The darkness/intensity of the print job. What the exact values mean is somewhat subjective but 127 is the most exposed/darkest value.</param>
|
||||
public delegate void PrinterCallback(IntPtr image, byte height, byte top_margin, byte bottom_margin, byte exposure);
|
||||
|
||||
public interface IGameboyCommon : ISpecializedEmulatorService
|
||||
{
|
||||
bool IsCGBMode();
|
||||
|
@ -23,6 +32,12 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy
|
|||
/// </summary>
|
||||
/// <param name="line">scanline. -1 = end of frame, -2 = RIGHT NOW</param>
|
||||
void SetScanlineCallback(ScanlineCallback callback, int line);
|
||||
|
||||
/// <summary>
|
||||
/// Set up printer callback
|
||||
/// </summary>
|
||||
/// <param name="callback">The callback to get the image. Setting this to non-null also "connects" the printer as the serial device.</param>
|
||||
void SetPrinterCallback(PrinterCallback callback);
|
||||
}
|
||||
|
||||
public class GPUMemoryAreas : IMonitor
|
||||
|
|
|
@ -264,6 +264,20 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
|
|||
/// <param name="callback">the callback</param>
|
||||
[DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gambatte_setrtccallback(IntPtr core, RTCCallback callback);
|
||||
|
||||
/// <summary>
|
||||
/// type of the link data sent callback
|
||||
/// </summary>
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void LinkCallback();
|
||||
|
||||
/// <summary>
|
||||
/// sets the Link data sent callback.
|
||||
/// </summary>
|
||||
/// <param name="core">opaque state pointer</param>
|
||||
/// <param name="callback">the callback</param>
|
||||
[DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gambatte_setlinkcallback(IntPtr core, LinkCallback callback);
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the currently loaded ROM image is treated as having CGB support.
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy
|
|||
public long Time;
|
||||
public Buttons Keys;
|
||||
}
|
||||
|
||||
|
||||
[BizImport(CC)]
|
||||
public abstract bool Init(bool cgb, byte[] spc, int spclen);
|
||||
|
||||
|
@ -51,5 +51,8 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy
|
|||
|
||||
[BizImport(CC)]
|
||||
public abstract bool HasSaveRam();
|
||||
|
||||
[BizImport(CC)]
|
||||
public abstract void SetPrinterCallback(PrinterCallback callback);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -339,5 +339,13 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PrinterCallback _printerCallback;
|
||||
|
||||
public void SetPrinterCallback(PrinterCallback callback)
|
||||
{
|
||||
_printerCallback = callback;
|
||||
_core.SetPrinterCallback(callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,9 +66,6 @@
|
|||
<Component Id="D3D8.DLL" DiskId="1" Guid="7A00676A-C29E-4314-8F5B-61F5CA523248">
|
||||
<File Id="D3D8.DLL" Name="d3d8.dll" Source="..\output\dll\d3d8.dll" />
|
||||
</Component>
|
||||
<Component Id="EMU7800.DLL" DiskId="1" Guid="812823F6-5CB7-4B2E-A6F2-2583F6332AA3">
|
||||
<File Id="EMU7800.DLL" Name="EMU7800.dll" Source="..\output\dll\EMU7800.dll" />
|
||||
</Component>
|
||||
<Component Id="FFMPEG.EXE" DiskId="1" Guid="75864EED-3332-4976-90FE-BE92C5FE1F4A">
|
||||
<File Id="FFMPEG.EXE" Name="ffmpeg.exe" Source="..\output\dll\ffmpeg.exe" />
|
||||
</Component>
|
||||
|
@ -560,9 +557,6 @@
|
|||
</Directory>
|
||||
</Directory>
|
||||
<Directory Id="GAMEDB" Name="gamedb">
|
||||
<Component Id="EMU7800.CSV" DiskId="1" Guid="12F97BC8-6696-4F39-9E35-9A4DD8F58E99">
|
||||
<File Id="EMU7800.CSV" Name="EMU7800.csv" Source="..\output\gamedb\EMU7800.csv" />
|
||||
</Component>
|
||||
<Component Id="GAME.DB" DiskId="1" Guid="CB1C1E8C-9ED9-45C0-8EC5-0D39DF7191F9">
|
||||
<File Id="GAME.DB" Name="game.db" Source="..\output\gamedb\game.db" />
|
||||
</Component>
|
||||
|
@ -847,4 +841,4 @@
|
|||
</Directory>
|
||||
</Directory>
|
||||
</Fragment>
|
||||
</Wix>
|
||||
</Wix>
|
||||
|
|
|
@ -50,7 +50,6 @@
|
|||
<ComponentRef Id="OPENTK.GLCONTROL.DLL" />
|
||||
<ComponentRef Id="BIZHAWK.COMMON.DLL" />
|
||||
<ComponentRef Id="BLIP_BUF.DLL" />
|
||||
<ComponentRef Id="EMU7800.DLL" />
|
||||
<ComponentRef Id="FFMPEG.EXE" />
|
||||
<ComponentRef Id="FFMPEG_README.TXT" />
|
||||
<ComponentRef Id="FREETYPE6.DLL" />
|
||||
|
@ -79,7 +78,6 @@
|
|||
<ComponentRef Id="SLIMDX.DLL" />
|
||||
<ComponentRef Id="ZLIB1.DLL" />
|
||||
<ComponentRef Id="GAME.DB" />
|
||||
<ComponentRef Id="EMU7800.CSV" />
|
||||
<ComponentRef Id="GAMEDB.TXT" />
|
||||
<ComponentRef Id="GAMEDB_A2600.TXT" />
|
||||
<ComponentRef Id="GAMEDB_COLECO.TXT" />
|
||||
|
@ -365,4 +363,4 @@
|
|||
<Icon Id="BizHawkIcon.exe" SourceFile="..\output\EmuHawk.exe" />
|
||||
<Icon Id="DiscoHawkIcon.exe" SourceFile="..\output\DiscoHawk.exe" />
|
||||
</Product>
|
||||
</Wix>
|
||||
</Wix>
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
Besides the main Bizhawk.sln, there are a number of other projects, each with its own build method. To develop Bizhawk, you do not need to build these other projects, as they are included prebuild in SVN. You need only build them if you want to modify the cores themselves.
|
||||
|
||||
|
||||
EMU7800: Load EMU7800/EMU7800.sln; builds as .NET 4.0. Does file automatically copy to appropriate folder??
|
||||
|
||||
genplus-gx: Load genplus-gx/libretro/msvc/msvc-2010.sln; builds as VC++ (VC10). Output dll automatically copies to appropriate folder.
|
||||
waterbox: check the waterbox/ folder for instructions on the alpha waterbox builds.
|
||||
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace EMU7800
|
||||
{
|
||||
public class Class1
|
||||
{
|
||||
}
|
||||
}
|
|
@ -1,154 +0,0 @@
|
|||
/*
|
||||
* AddressSpace.cs
|
||||
*
|
||||
* The class representing the memory map or address space of a machine.
|
||||
*
|
||||
* Copyright © 2003, 2011 Mike Murphy
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
|
||||
namespace EMU7800.Core
|
||||
{
|
||||
public sealed class AddressSpace
|
||||
{
|
||||
public MachineBase M { get; private set; }
|
||||
|
||||
readonly int AddrSpaceShift;
|
||||
readonly int AddrSpaceSize;
|
||||
readonly int AddrSpaceMask;
|
||||
|
||||
readonly int PageShift;
|
||||
readonly int PageSize;
|
||||
|
||||
readonly IDevice[] MemoryMap;
|
||||
IDevice Snooper;
|
||||
|
||||
public byte DataBusState { get; private set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "AddressSpace";
|
||||
}
|
||||
|
||||
public byte this[ushort addr]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Snooper != null)
|
||||
{
|
||||
// here DataBusState is just facilitating a dummy read to the snooper device
|
||||
// the read operation may have important side effects within the device
|
||||
DataBusState = Snooper[addr];
|
||||
}
|
||||
var pageno = (addr & AddrSpaceMask) >> PageShift;
|
||||
var dev = MemoryMap[pageno];
|
||||
DataBusState = dev[addr];
|
||||
return DataBusState;
|
||||
}
|
||||
set
|
||||
{
|
||||
DataBusState = value;
|
||||
if (Snooper != null)
|
||||
{
|
||||
Snooper[addr] = DataBusState;
|
||||
}
|
||||
var pageno = (addr & AddrSpaceMask) >> PageShift;
|
||||
var dev = MemoryMap[pageno];
|
||||
dev[addr] = DataBusState;
|
||||
}
|
||||
}
|
||||
|
||||
public void Map(ushort basea, ushort size, IDevice device)
|
||||
{
|
||||
if (device == null)
|
||||
throw new ArgumentNullException("device");
|
||||
|
||||
for (int addr = basea; addr < basea + size; addr += PageSize)
|
||||
{
|
||||
var pageno = (addr & AddrSpaceMask) >> PageShift;
|
||||
MemoryMap[pageno] = device;
|
||||
}
|
||||
|
||||
LogDebug("{0}: Mapped {1} to ${2:x4}:${3:x4}", this, device, basea, basea + size - 1);
|
||||
}
|
||||
|
||||
public void Map(ushort basea, ushort size, Cart cart)
|
||||
{
|
||||
if (cart == null)
|
||||
throw new ArgumentNullException("cart");
|
||||
|
||||
cart.Attach(M);
|
||||
var device = (IDevice)cart;
|
||||
if (cart.RequestSnooping)
|
||||
{
|
||||
Snooper = device;
|
||||
}
|
||||
Map(basea, size, device);
|
||||
}
|
||||
|
||||
#region Constructors
|
||||
|
||||
private AddressSpace()
|
||||
{
|
||||
}
|
||||
|
||||
public AddressSpace(MachineBase m, int addrSpaceShift, int pageShift)
|
||||
{
|
||||
if (m == null)
|
||||
throw new ArgumentNullException("m");
|
||||
|
||||
M = m;
|
||||
|
||||
AddrSpaceShift = addrSpaceShift;
|
||||
AddrSpaceSize = 1 << AddrSpaceShift;
|
||||
AddrSpaceMask = AddrSpaceSize - 1;
|
||||
|
||||
PageShift = pageShift;
|
||||
PageSize = 1 << PageShift;
|
||||
|
||||
MemoryMap = new IDevice[1 << addrSpaceShift >> PageShift];
|
||||
IDevice nullDev = new NullDevice(M);
|
||||
for (var pageno=0; pageno < MemoryMap.Length; pageno++)
|
||||
{
|
||||
MemoryMap[pageno] = nullDev;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public AddressSpace(DeserializationContext input, MachineBase m, int addrSpaceShift, int pageShift) : this(m, addrSpaceShift, pageShift)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException("input");
|
||||
|
||||
input.CheckVersion(1);
|
||||
DataBusState = input.ReadByte();
|
||||
}
|
||||
|
||||
public void GetObjectData(SerializationContext output)
|
||||
{
|
||||
if (output == null)
|
||||
throw new ArgumentNullException("output");
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(DataBusState);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
[System.Diagnostics.Conditional("DEBUG")]
|
||||
void LogDebug(string format, params object[] args)
|
||||
{
|
||||
if (M == null || M.Logger == null)
|
||||
return;
|
||||
M.Logger.WriteLine(format, args);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
* BIOS7800.cs
|
||||
*
|
||||
* The BIOS of the Atari 7800.
|
||||
*
|
||||
* Copyright © 2004 Mike Murphy
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
|
||||
namespace EMU7800.Core
|
||||
{
|
||||
public sealed class Bios7800 : IDevice
|
||||
{
|
||||
readonly byte[] ROM;
|
||||
readonly ushort Mask;
|
||||
|
||||
public ushort Size { get { return (ushort)ROM.Length; } }
|
||||
|
||||
public void Reset() { }
|
||||
|
||||
public byte this[ushort addr]
|
||||
{
|
||||
get { return ROM[addr & Mask]; }
|
||||
set { }
|
||||
}
|
||||
|
||||
public Bios7800(byte[] rom)
|
||||
{
|
||||
if (rom == null)
|
||||
throw new ArgumentNullException("rom");
|
||||
if (rom.Length != 4096 && rom.Length != 16384)
|
||||
throw new ArgumentException("ROM size not 4096 or 16384", "rom");
|
||||
|
||||
ROM = rom;
|
||||
Mask = (ushort)ROM.Length;
|
||||
Mask--;
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public Bios7800(DeserializationContext input)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException("input");
|
||||
|
||||
input.CheckVersion(1);
|
||||
ROM = input.ReadExpectedBytes(4096, 16384);
|
||||
|
||||
Mask = (ushort)ROM.Length;
|
||||
Mask--;
|
||||
}
|
||||
|
||||
public void GetObjectData(SerializationContext output)
|
||||
{
|
||||
if (output == null)
|
||||
throw new ArgumentNullException("output");
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(ROM);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
/*
|
||||
* why this sucks:
|
||||
* A read costs 3 shifts and an or. A write costs 2 shifts. Additional shifts are
|
||||
* needed elsewhere to figure out which item in a BufferElement[] to access. Because
|
||||
* the encapsulation is for a BufferElement and not a whole array of them, code elsewhere
|
||||
* is gunked up with 'BufferElement.SIZE' shifts. If the 32 bit "alias" was actually used,
|
||||
* there might be some purpose to this code: but it's only used for a ZeroMemory()
|
||||
* replacement. Every use of BufferElement in the code is a BufferElement[] used as a gunked
|
||||
* up replacement for a byte[].
|
||||
*
|
||||
* A small speed increase was observed hacking this out; but my motivation was more about cleaness
|
||||
* and stomping out bad ideas.
|
||||
*/
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// Frames are composed of <see cref="BufferElement"/>s,
|
||||
/// that group bytes into machine words for efficient array processing.
|
||||
/// Bytes are packed in logical little endian order.
|
||||
/// </summary>
|
||||
public struct BufferElement
|
||||
{
|
||||
/// <summary>
|
||||
/// The number of bytes contained within a <see cref="BufferElement"/>.
|
||||
/// </summary>
|
||||
public const int SIZE = 4; // 2^SHIFT
|
||||
|
||||
/// <summary>
|
||||
/// The mask value applied against a byte array index to access the individual bytes within a <see cref="BufferElement"/>.
|
||||
/// </summary>
|
||||
public const int MASK = 3; // SIZE - 1
|
||||
|
||||
/// <summary>
|
||||
/// The left shift value to convert a byte array index to a <see cref="BufferElement"/> array index.
|
||||
/// </summary>
|
||||
public const int SHIFT = 2;
|
||||
|
||||
uint _data;
|
||||
|
||||
/// <summary>
|
||||
/// A convenience accessor for reading/writing individual bytes within this <see cref="BufferElement"/>.
|
||||
/// </summary>
|
||||
/// <param name="offset"></param>
|
||||
public byte this[int offset]
|
||||
{
|
||||
get
|
||||
{
|
||||
var i = (offset & MASK) << 3;
|
||||
return (byte)(_data >> i);
|
||||
}
|
||||
set
|
||||
{
|
||||
var i = (offset & MASK) << 3;
|
||||
var d = (uint)value << i;
|
||||
var di = (uint)0xff << i;
|
||||
_data = _data & ~di | d;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Zeros out all bytes of this <see cref="BufferElement"/>.
|
||||
/// </summary>
|
||||
public void ClearAll()
|
||||
{
|
||||
_data = 0;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
|
@ -1,170 +0,0 @@
|
|||
/*
|
||||
* Cart.cs
|
||||
*
|
||||
* An abstraction of a game cart. Attributable to Kevin Horton's Bankswitching
|
||||
* document, the Stella source code, and Eckhard Stolberg's 7800 Bankswitching Guide.
|
||||
*
|
||||
* Copyright Š 2003, 2004, 2010, 2011 Mike Murphy
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
|
||||
namespace EMU7800.Core
|
||||
{
|
||||
public abstract class Cart : IDevice
|
||||
{
|
||||
static int _multicartBankSelector;
|
||||
|
||||
protected MachineBase M { get; set; }
|
||||
protected internal byte[] ROM { get; set; }
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public virtual void Reset() { }
|
||||
|
||||
public abstract byte this[ushort addr] { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
public virtual void Attach(MachineBase m)
|
||||
{
|
||||
if (m == null)
|
||||
throw new ArgumentNullException("m");
|
||||
if (M != null && M != m)
|
||||
throw new InvalidOperationException("Cart already attached to a different machine.");
|
||||
M = m;
|
||||
}
|
||||
|
||||
public virtual void StartFrame()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void EndFrame()
|
||||
{
|
||||
}
|
||||
|
||||
protected internal virtual bool RequestSnooping
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of the specified cart.
|
||||
/// </summary>
|
||||
/// <param name="romBytes"></param>
|
||||
/// <param name="cartType"></param>
|
||||
/// <exception cref="Emu7800Exception">Specified CartType is unexpected.</exception>
|
||||
public static Cart Create(byte[] romBytes, CartType cartType)
|
||||
{
|
||||
if (cartType == CartType.None)
|
||||
{
|
||||
switch (romBytes.Length)
|
||||
{
|
||||
case 2048:
|
||||
cartType = CartType.A2K;
|
||||
break;
|
||||
case 4096:
|
||||
cartType = CartType.A4K;
|
||||
break;
|
||||
case 8192:
|
||||
cartType = CartType.A8K;
|
||||
break;
|
||||
case 16384:
|
||||
cartType = CartType.A16K;
|
||||
break;
|
||||
case 32768:
|
||||
cartType = CartType.A32K;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (cartType)
|
||||
{
|
||||
case CartType.A2K: return new CartA2K(romBytes);
|
||||
case CartType.A4K: return new CartA4K(romBytes);
|
||||
case CartType.A8K: return new CartA8K(romBytes);
|
||||
case CartType.A8KR: return new CartA8KR(romBytes);
|
||||
case CartType.A16K: return new CartA16K(romBytes);
|
||||
case CartType.A16KR: return new CartA16KR(romBytes);
|
||||
case CartType.DC8K: return new CartDC8K(romBytes);
|
||||
case CartType.PB8K: return new CartPB8K(romBytes);
|
||||
case CartType.TV8K: return new CartTV8K(romBytes);
|
||||
case CartType.CBS12K: return new CartCBS12K(romBytes);
|
||||
case CartType.A32K: return new CartA32K(romBytes);
|
||||
case CartType.A32KR: return new CartA32KR(romBytes);
|
||||
case CartType.MN16K: return new CartMN16K(romBytes);
|
||||
case CartType.DPC: return new CartDPC(romBytes);
|
||||
case CartType.M32N12K: return new CartA2K(romBytes, _multicartBankSelector++);
|
||||
case CartType.A7808: return new Cart7808(romBytes);
|
||||
case CartType.A7816: return new Cart7816(romBytes);
|
||||
case CartType.A7832P: return new Cart7832P(romBytes);
|
||||
case CartType.A7832: return new Cart7832(romBytes);
|
||||
case CartType.A7848: return new Cart7848(romBytes);
|
||||
case CartType.A78SGP: return new Cart78SGP(romBytes);
|
||||
case CartType.A78SG: return new Cart78SG(romBytes, false);
|
||||
case CartType.A78SGR: return new Cart78SG(romBytes, true);
|
||||
case CartType.A78S9: return new Cart78S9(romBytes);
|
||||
case CartType.A78S4: return new Cart78S4(romBytes, false);
|
||||
case CartType.A78S4R: return new Cart78S4(romBytes, true);
|
||||
case CartType.A78AB: return new Cart78AB(romBytes);
|
||||
case CartType.A78AC: return new Cart78AC(romBytes);
|
||||
default:
|
||||
throw new Emu7800Exception("Unexpected CartType: " + cartType);
|
||||
}
|
||||
}
|
||||
|
||||
protected void LoadRom(byte[] romBytes, int multicartBankSize, int multicartBankNo)
|
||||
{
|
||||
if (romBytes == null)
|
||||
throw new ArgumentNullException("romBytes");
|
||||
|
||||
ROM = new byte[multicartBankSize];
|
||||
Buffer.BlockCopy(romBytes, multicartBankSize*multicartBankNo, ROM, 0, multicartBankSize);
|
||||
}
|
||||
|
||||
protected void LoadRom(byte[] romBytes, int minSize)
|
||||
{
|
||||
if (romBytes == null)
|
||||
throw new ArgumentNullException("romBytes");
|
||||
|
||||
if (romBytes.Length >= minSize)
|
||||
{
|
||||
ROM = romBytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
ROM = new byte[minSize];
|
||||
Buffer.BlockCopy(romBytes, 0, ROM, 0, romBytes.Length);
|
||||
}
|
||||
}
|
||||
|
||||
protected void LoadRom(byte[] romBytes)
|
||||
{
|
||||
LoadRom(romBytes, romBytes.Length);
|
||||
}
|
||||
|
||||
protected Cart()
|
||||
{
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
protected Cart(DeserializationContext input)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException("input");
|
||||
|
||||
input.CheckVersion(1);
|
||||
}
|
||||
|
||||
public virtual void GetObjectData(SerializationContext output)
|
||||
{
|
||||
if (output == null)
|
||||
throw new ArgumentNullException("output");
|
||||
|
||||
output.WriteVersion(1);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Atari 7800 non-bankswitched 8KB cartridge
|
||||
/// </summary>
|
||||
public sealed class Cart7808 : Cart
|
||||
{
|
||||
//
|
||||
// Cart Format Mapping to ROM Address Space
|
||||
// 0x0000:0x2000 0xE000:0x2000 (repeated downward to 0x4000)
|
||||
//
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public override byte this[ushort addr]
|
||||
{
|
||||
get { return ROM[addr & 0x1fff]; }
|
||||
set { }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private Cart7808()
|
||||
{
|
||||
}
|
||||
|
||||
public Cart7808(byte[] romBytes)
|
||||
{
|
||||
LoadRom(romBytes, 0x2000);
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public Cart7808(DeserializationContext input, MachineBase m) : base(input)
|
||||
{
|
||||
input.CheckVersion(1);
|
||||
LoadRom(input.ReadExpectedBytes(0x2000), 0x2000);
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(ROM);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Atari 7800 non-bankswitched 16KB cartridge
|
||||
/// </summary>
|
||||
public sealed class Cart7816 : Cart
|
||||
{
|
||||
//
|
||||
// Cart Format Mapping to ROM Address Space
|
||||
// 0x0000:0x4000 0xC000:0x4000 (repeated downward to 0x4000)
|
||||
//
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public override byte this[ushort addr]
|
||||
{
|
||||
get { return ROM[addr & 0x3fff]; }
|
||||
set { }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private Cart7816()
|
||||
{
|
||||
}
|
||||
|
||||
public Cart7816(byte[] romBytes)
|
||||
{
|
||||
LoadRom(romBytes, 0x4000);
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public Cart7816(DeserializationContext input, MachineBase m) : base(input)
|
||||
{
|
||||
input.CheckVersion(1);
|
||||
LoadRom(input.ReadExpectedBytes(0x4000), 0x4000);
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(ROM);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Atari 7800 non-bankswitched 32KB cartridge
|
||||
/// </summary>
|
||||
public sealed class Cart7832 : Cart
|
||||
{
|
||||
//
|
||||
// Cart Format Mapping to ROM Address Space
|
||||
// 0x0000:0x8000 0x8000:0x8000 (repeated downward until 0x4000)
|
||||
//
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public override byte this[ushort addr]
|
||||
{
|
||||
get { return ROM[addr & 0x7fff]; }
|
||||
set { }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private Cart7832()
|
||||
{
|
||||
}
|
||||
|
||||
public Cart7832(byte[] romBytes)
|
||||
{
|
||||
LoadRom(romBytes, 0x8000);
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public Cart7832(DeserializationContext input, MachineBase m) : base(input)
|
||||
{
|
||||
input.CheckVersion(1);
|
||||
LoadRom(input.ReadExpectedBytes(0x8000), 0x8000);
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(ROM);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Atari 7800 non-bankswitched 32KB cartridge w/Pokey
|
||||
/// </summary>
|
||||
public sealed class Cart7832P : Cart
|
||||
{
|
||||
//
|
||||
// Cart Format Mapping to ROM Address Space
|
||||
// 0x4000:0x4000 Pokey
|
||||
// 0x0000:0x8000 0x8000:0x8000
|
||||
//
|
||||
PokeySound _pokeySound;
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
if (_pokeySound != null)
|
||||
_pokeySound.Reset();
|
||||
}
|
||||
|
||||
public override byte this[ushort addr]
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((addr & 0xF000) == 0x4000 && _pokeySound != null) ? _pokeySound.Read(addr) : ROM[addr & 0x7fff];
|
||||
}
|
||||
set
|
||||
{
|
||||
if ((addr & 0xF000) == 0x4000 && _pokeySound != null)
|
||||
_pokeySound.Update(addr, value);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public override void Attach(MachineBase m)
|
||||
{
|
||||
base.Attach(m);
|
||||
if (_pokeySound == null)
|
||||
_pokeySound = new PokeySound(M);
|
||||
}
|
||||
|
||||
public override void StartFrame()
|
||||
{
|
||||
if (_pokeySound != null)
|
||||
_pokeySound.StartFrame();
|
||||
}
|
||||
|
||||
public override void EndFrame()
|
||||
{
|
||||
if (_pokeySound != null)
|
||||
_pokeySound.EndFrame();
|
||||
}
|
||||
|
||||
private Cart7832P()
|
||||
{
|
||||
}
|
||||
|
||||
public Cart7832P(byte[] romBytes)
|
||||
{
|
||||
LoadRom(romBytes, 0x8000);
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public Cart7832P(DeserializationContext input, MachineBase m) : base(input)
|
||||
{
|
||||
input.CheckVersion(1);
|
||||
LoadRom(input.ReadExpectedBytes(0x8000), 0x8000);
|
||||
_pokeySound = input.ReadOptionalPokeySound(m);
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
if (_pokeySound == null)
|
||||
throw new Emu7800SerializationException("Cart7832P must be attached before serialization.");
|
||||
|
||||
base.GetObjectData(output);
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(ROM);
|
||||
output.WriteOptional(_pokeySound);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Atari 7800 non-bankswitched 48KB cartridge
|
||||
/// </summary>
|
||||
public sealed class Cart7848 : Cart
|
||||
{
|
||||
//
|
||||
// Cart Format Mapping to ROM Address Space
|
||||
// 0x0000:0xc000 0x4000:0xc000
|
||||
//
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public override byte this[ushort addr]
|
||||
{
|
||||
get { return ROM[addr - 0x4000]; }
|
||||
set { }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private Cart7848()
|
||||
{
|
||||
}
|
||||
|
||||
public Cart7848(byte[] romBytes)
|
||||
{
|
||||
LoadRom(romBytes, 0xc000);
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public Cart7848(DeserializationContext input, MachineBase m) : base(input)
|
||||
{
|
||||
input.CheckVersion(1);
|
||||
LoadRom(input.ReadExpectedBytes(0xc000), 0xc000);
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(ROM);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Atari 7800 Absolute bankswitched cartridge
|
||||
/// </summary>
|
||||
public sealed class Cart78AB : Cart
|
||||
{
|
||||
//
|
||||
// Cart Format Mapping to ROM Address Space
|
||||
// Bank0: 0x00000:0x4000
|
||||
// Bank1: 0x04000:0x4000 0x4000:0x4000 Bank0-1 (0 on startup)
|
||||
// Bank2: 0x08000:0x4000 0x8000:0x4000 Bank2
|
||||
// Bank3: 0x0c000:0x4000 0xc000:0x4000 Bank3
|
||||
//
|
||||
readonly int[] Bank = new int[4];
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public override byte this[ushort addr]
|
||||
{
|
||||
get { return ROM[ (Bank[addr >> 14] << 14) | (addr & 0x3fff) ]; }
|
||||
set
|
||||
{
|
||||
if ((addr >> 14) == 2)
|
||||
{
|
||||
Bank[1] = (value - 1) & 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private Cart78AB()
|
||||
{
|
||||
}
|
||||
|
||||
public Cart78AB(byte[] romBytes)
|
||||
{
|
||||
Bank[1] = 0;
|
||||
Bank[2] = 2;
|
||||
Bank[3] = 3;
|
||||
LoadRom(romBytes, 0x10000);
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public Cart78AB(DeserializationContext input, MachineBase m) : base(input)
|
||||
{
|
||||
var version = input.CheckVersion(1, 2);
|
||||
LoadRom(input.ReadBytes());
|
||||
Bank = input.ReadIntegers(4);
|
||||
if (version == 1)
|
||||
input.ReadInt32();
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
|
||||
output.WriteVersion(2);
|
||||
output.Write(ROM);
|
||||
output.Write(Bank);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Atari 7800 Activision bankswitched cartridge
|
||||
/// </summary>
|
||||
public sealed class Cart78AC : Cart
|
||||
{
|
||||
//
|
||||
// Cart Format Mapping to ROM Address Space
|
||||
// Bank0 : 0x00000:0x2000
|
||||
// Bank1 : 0x02000:0x2000
|
||||
// Bank2 : 0x04000:0x2000 0x4000:0x2000 Bank13
|
||||
// Bank3 : 0x06000:0x2000 0x6000:0x2000 Bank12
|
||||
// Bank4 : 0x08000:0x2000 0x8000:0x2000 Bank15
|
||||
// Bank5 : 0x0a000:0x2000 0xa000:0x2000 Bank(2*n) n in [0-7], n=0 on startup
|
||||
// Bank6 : 0x0c000:0x2000 0xc000:0x2000 Bank(2*n+1)
|
||||
// Bank7 : 0x0e000:0x2000 0xe000:0x2000 Bank14
|
||||
// Bank8 : 0x10000:0x2000
|
||||
// Bank9 : 0x12000:0x2000
|
||||
// Bank10: 0x14000:0x2000
|
||||
// Bank11: 0x16000:0x2000
|
||||
// Bank12: 0x18000:0x2000
|
||||
// Bank13: 0x1a000:0x2000
|
||||
// Bank14: 0x1c000:0x2000
|
||||
// Bank15: 0x1e000:0x2000
|
||||
//
|
||||
// Banks are actually 16KB, but handled as 8KB for implementation ease.
|
||||
//
|
||||
readonly int[] Bank = new int[8];
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public override byte this[ushort addr]
|
||||
{
|
||||
get
|
||||
{
|
||||
return ROM[ (Bank[addr >> 13] << 13) | (addr & 0x1fff) ];
|
||||
}
|
||||
set
|
||||
{
|
||||
if ((addr & 0xfff0) == 0xff80)
|
||||
{
|
||||
Bank[5] = (addr & 7) << 1;
|
||||
Bank[6] = Bank[5] + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private Cart78AC()
|
||||
{
|
||||
}
|
||||
|
||||
public Cart78AC(byte[] romBytes)
|
||||
{
|
||||
Bank[2] = 13;
|
||||
Bank[3] = 12;
|
||||
Bank[4] = 15;
|
||||
Bank[5] = 0;
|
||||
Bank[6] = 1;
|
||||
Bank[7] = 14;
|
||||
LoadRom(romBytes, 0x20000);
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public Cart78AC(DeserializationContext input, MachineBase m) : base(input)
|
||||
{
|
||||
input.CheckVersion(1);
|
||||
LoadRom(input.ReadBytes());
|
||||
Bank = input.ReadIntegers(8);
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(ROM);
|
||||
output.Write(Bank);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Atari 7800 SuperGame S4 bankswitched cartridge
|
||||
/// </summary>
|
||||
public sealed class Cart78S4 : Cart
|
||||
{
|
||||
//
|
||||
// Cart Format Mapping to ROM Address Space
|
||||
// Bank0: 0x00000:0x4000
|
||||
// Bank1: 0x04000:0x4000 0x4000:0x4000 Bank2
|
||||
// Bank2: 0x08000:0x4000 0x8000:0x4000 Bank0 (0 on startup)
|
||||
// Bank3: 0x0c000:0x4000 0xc000:0x4000 Bank3
|
||||
//
|
||||
// Banks 0-3 are the same as banks 4-7
|
||||
//
|
||||
readonly byte[] RAM;
|
||||
readonly int[] Bank = new int[4];
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public override byte this[ushort addr]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (RAM != null && addr >= 0x6000 && addr <= 0x7fff)
|
||||
{
|
||||
return RAM[addr & 0x1fff];
|
||||
}
|
||||
return ROM[(Bank[addr >> 14] << 14) | (addr & 0x3fff)];
|
||||
}
|
||||
set
|
||||
{
|
||||
if (RAM != null && addr >= 0x6000 && addr <= 0x7fff)
|
||||
{
|
||||
RAM[addr & 0x1fff] = value;
|
||||
}
|
||||
else if ((addr >> 14) == 2)
|
||||
{
|
||||
Bank[2] = value & 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private Cart78S4()
|
||||
{
|
||||
}
|
||||
|
||||
public Cart78S4(byte[] romBytes, bool needRAM)
|
||||
{
|
||||
if (needRAM)
|
||||
{
|
||||
RAM = new byte[0x2000];
|
||||
}
|
||||
|
||||
LoadRom(romBytes, 0xffff);
|
||||
|
||||
Bank[1] = 2;
|
||||
Bank[2] = 0;
|
||||
Bank[3] = 3;
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public Cart78S4(DeserializationContext input, MachineBase m) : base(input)
|
||||
{
|
||||
var version = input.CheckVersion(1, 2);
|
||||
LoadRom(input.ReadBytes());
|
||||
Bank = input.ReadIntegers(4);
|
||||
if (version == 1)
|
||||
input.ReadInt32();
|
||||
RAM = input.ReadOptionalBytes();
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
|
||||
output.WriteVersion(2);
|
||||
output.Write(ROM);
|
||||
output.Write(Bank);
|
||||
output.WriteOptional(RAM);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Atari 7800 SuperGame S9 bankswitched cartridge
|
||||
/// </summary>
|
||||
public sealed class Cart78S9 : Cart
|
||||
{
|
||||
//
|
||||
// Cart Format Mapping to ROM Address Space
|
||||
// Bank0: 0x00000:0x4000
|
||||
// Bank1: 0x04000:0x4000 0x4000:0x4000 Bank0
|
||||
// Bank2: 0x08000:0x4000 0x8000:0x4000 Bank0-8 (1 on startup)
|
||||
// Bank3: 0x0c000:0x4000 0xc000:0x4000 Bank8
|
||||
// Bank4: 0x10000:0x4000
|
||||
// Bank5: 0x14000:0x4000
|
||||
// Bank6: 0x18000:0x4000
|
||||
// Bank7: 0x1c000:0x4000
|
||||
// Bank8: 0x20000:0x4000
|
||||
//
|
||||
readonly int[] Bank = new int[4];
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public override byte this[ushort addr]
|
||||
{
|
||||
get { return ROM[ (Bank[addr >> 14] << 14) | (addr & 0x3fff) ]; }
|
||||
set
|
||||
{
|
||||
if ((addr >> 14) == 2 && value < 8)
|
||||
{
|
||||
Bank[2] = (value + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private Cart78S9()
|
||||
{
|
||||
}
|
||||
|
||||
public Cart78S9(byte[] romBytes)
|
||||
{
|
||||
Bank[1] = 0;
|
||||
Bank[2] = 1;
|
||||
Bank[3] = 8;
|
||||
LoadRom(romBytes, 0x24000);
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public Cart78S9(DeserializationContext input, MachineBase m) : base(input)
|
||||
{
|
||||
input.CheckVersion(1);
|
||||
LoadRom(input.ReadBytes());
|
||||
Bank = input.ReadIntegers(4);
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(ROM);
|
||||
output.Write(Bank);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Atari 7800 SuperGame bankswitched cartridge
|
||||
/// </summary>
|
||||
public sealed class Cart78SG : Cart
|
||||
{
|
||||
//
|
||||
// Cart Format Mapping to ROM Address Space
|
||||
// Bank0: 0x00000:0x4000
|
||||
// Bank1: 0x04000:0x4000 0x4000:0x4000 Bank6
|
||||
// Bank2: 0x08000:0x4000 0x8000:0x4000 Bank0-7 (0 on startup)
|
||||
// Bank3: 0x0c000:0x4000 0xc000:0x4000 Bank7
|
||||
// Bank4: 0x10000:0x4000
|
||||
// Bank5: 0x14000:0x4000
|
||||
// Bank6: 0x18000:0x4000
|
||||
// Bank7: 0x1c000:0x4000
|
||||
//
|
||||
readonly int[] Bank = new int[4];
|
||||
readonly byte[] RAM;
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public override byte this[ushort addr]
|
||||
{
|
||||
get
|
||||
{
|
||||
var bankNo = addr >> 14;
|
||||
if (RAM != null && bankNo == 1)
|
||||
{
|
||||
return RAM[addr & 0x3fff];
|
||||
}
|
||||
return ROM[ (Bank[bankNo] << 14) | (addr & 0x3fff) ];
|
||||
}
|
||||
set
|
||||
{
|
||||
var bankNo = addr >> 14;
|
||||
if (bankNo == 2)
|
||||
{
|
||||
Bank[2] = value & 7;
|
||||
}
|
||||
else if (RAM != null && bankNo == 1)
|
||||
{
|
||||
RAM[addr & 0x3fff] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private Cart78SG()
|
||||
{
|
||||
}
|
||||
|
||||
public Cart78SG(byte[] romBytes, bool needRAM)
|
||||
{
|
||||
if (needRAM)
|
||||
{
|
||||
// This works for titles that use 8KB instead of 16KB
|
||||
RAM = new byte[0x4000];
|
||||
}
|
||||
|
||||
Bank[1] = 6;
|
||||
Bank[2] = 0;
|
||||
Bank[3] = 7;
|
||||
|
||||
LoadRom(romBytes, 0x20000);
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public Cart78SG(DeserializationContext input, MachineBase m) : base(input)
|
||||
{
|
||||
var version = input.CheckVersion(1, 2);
|
||||
LoadRom(input.ReadBytes());
|
||||
Bank = input.ReadIntegers(4);
|
||||
if (version == 1)
|
||||
input.ReadInt32();
|
||||
RAM = input.ReadOptionalBytes(0x4000);
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
|
||||
output.WriteVersion(2);
|
||||
output.Write(ROM);
|
||||
output.Write(Bank);
|
||||
output.WriteOptional(RAM);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Atari 7800 SuperGame bankswitched cartridge w/Pokey
|
||||
/// </summary>
|
||||
public sealed class Cart78SGP : Cart
|
||||
{
|
||||
//
|
||||
// Cart Format Mapping to ROM Address Space
|
||||
// Bank0: 0x00000:0x4000
|
||||
// Bank1: 0x04000:0x4000 0x4000:0x4000 Pokey
|
||||
// Bank2: 0x08000:0x4000 0x8000:0x4000 Bank0-7 (0 on startup)
|
||||
// Bank3: 0x0c000:0x4000 0xc000:0x4000 Bank7
|
||||
// Bank4: 0x10000:0x4000
|
||||
// Bank5: 0x14000:0x4000
|
||||
// Bank6: 0x18000:0x4000
|
||||
// Bank7: 0x1c000:0x4000
|
||||
//
|
||||
readonly int[] _bank = new int[4];
|
||||
PokeySound _pokeySound;
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
if (_pokeySound != null)
|
||||
_pokeySound.Reset();
|
||||
}
|
||||
|
||||
public override byte this[ushort addr]
|
||||
{
|
||||
get
|
||||
{
|
||||
var bankNo = addr >> 14;
|
||||
switch (bankNo)
|
||||
{
|
||||
case 1:
|
||||
return (_pokeySound != null) ? _pokeySound.Read(addr) : (byte)0;
|
||||
default:
|
||||
return ROM[(_bank[bankNo] << 14) | (addr & 0x3fff)];
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
var bankNo = addr >> 14;
|
||||
switch (bankNo)
|
||||
{
|
||||
case 1:
|
||||
if (_pokeySound != null)
|
||||
_pokeySound.Update(addr, value);
|
||||
break;
|
||||
case 2:
|
||||
_bank[2] = value & 7;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public override void Attach(MachineBase m)
|
||||
{
|
||||
base.Attach(m);
|
||||
if (_pokeySound == null)
|
||||
_pokeySound = new PokeySound(M);
|
||||
}
|
||||
|
||||
public override void StartFrame()
|
||||
{
|
||||
if (_pokeySound != null)
|
||||
_pokeySound.StartFrame();
|
||||
}
|
||||
|
||||
public override void EndFrame()
|
||||
{
|
||||
if (_pokeySound != null)
|
||||
_pokeySound.EndFrame();
|
||||
}
|
||||
|
||||
private Cart78SGP()
|
||||
{
|
||||
}
|
||||
|
||||
public Cart78SGP(byte[] romBytes)
|
||||
{
|
||||
_bank[2] = 0;
|
||||
_bank[3] = 7;
|
||||
|
||||
LoadRom(romBytes, 0x20000);
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public Cart78SGP(DeserializationContext input, MachineBase m) : base(input)
|
||||
{
|
||||
input.CheckVersion(1);
|
||||
LoadRom(input.ReadBytes());
|
||||
_bank = input.ReadIntegers(4);
|
||||
_pokeySound = input.ReadOptionalPokeySound(m);
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
if (_pokeySound == null)
|
||||
throw new Emu7800SerializationException("Cart78SGP must be attached before serialization.");
|
||||
|
||||
base.GetObjectData(output);
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(ROM);
|
||||
output.Write(_bank);
|
||||
output.WriteOptional(_pokeySound);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Atari standard 16KB bankswitched carts
|
||||
/// </summary>
|
||||
public sealed class CartA16K : Cart
|
||||
{
|
||||
//
|
||||
// Cart Format Mapping to ROM Address Space
|
||||
// Bank1: 0x0000:0x1000 0x1000:0x1000 Bank selected by accessing 0x1ff9-0x1ff9
|
||||
// Bank2: 0x1000:0x1000
|
||||
// Bank3: 0x2000:0x1000
|
||||
// Bank4: 0x3000:0x1000
|
||||
//
|
||||
ushort BankBaseAddr;
|
||||
|
||||
int Bank
|
||||
{
|
||||
set { BankBaseAddr = (ushort)(value * 0x1000); }
|
||||
}
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
Bank = 0;
|
||||
}
|
||||
|
||||
public override byte this[ushort addr]
|
||||
{
|
||||
get
|
||||
{
|
||||
addr &= 0x0fff;
|
||||
UpdateBank(addr);
|
||||
return ROM[BankBaseAddr + addr];
|
||||
}
|
||||
set
|
||||
{
|
||||
addr &= 0x0fff;
|
||||
UpdateBank(addr);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private CartA16K()
|
||||
{
|
||||
}
|
||||
|
||||
public CartA16K(byte[] romBytes)
|
||||
{
|
||||
LoadRom(romBytes, 0x4000);
|
||||
Bank = 0;
|
||||
}
|
||||
|
||||
void UpdateBank(ushort addr)
|
||||
{
|
||||
if (addr < 0x0ff6 || addr > 0x0ff9)
|
||||
{}
|
||||
else
|
||||
{
|
||||
Bank = addr - 0x0ff6;
|
||||
}
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public CartA16K(DeserializationContext input, MachineBase m) : base(input)
|
||||
{
|
||||
input.CheckVersion(1);
|
||||
LoadRom(input.ReadExpectedBytes(0x4000), 0x4000);
|
||||
BankBaseAddr = input.ReadUInt16();
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(ROM);
|
||||
output.Write(BankBaseAddr);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Atari standard 16KB bankswitched carts with 128 bytes of RAM
|
||||
/// </summary>
|
||||
public sealed class CartA16KR : Cart
|
||||
{
|
||||
//
|
||||
// Cart Format Mapping to ROM Address Space
|
||||
// Bank1: 0x0000:0x1000 0x1000:0x1000 Bank selected by accessing 0x1ff9-0x1ff9
|
||||
// Bank2: 0x1000:0x1000
|
||||
// Bank3: 0x2000:0x1000
|
||||
// Bank4: 0x3000:0x1000
|
||||
// Shadows ROM
|
||||
// 0x1000:0x0080 RAM write port
|
||||
// 0x1080:0x0080 RAM read port
|
||||
//
|
||||
ushort BankBaseAddr;
|
||||
readonly byte[] RAM;
|
||||
|
||||
int Bank
|
||||
{
|
||||
set { BankBaseAddr = (ushort)(value * 0x1000); }
|
||||
}
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
Bank = 0;
|
||||
}
|
||||
|
||||
public override byte this[ushort addr]
|
||||
{
|
||||
get
|
||||
{
|
||||
addr &= 0x0fff;
|
||||
if (addr < 0x0100 && addr >= 0x0080)
|
||||
{
|
||||
return RAM[addr & 0x7f];
|
||||
}
|
||||
UpdateBank(addr);
|
||||
return ROM[BankBaseAddr + addr];
|
||||
}
|
||||
set
|
||||
{
|
||||
addr &= 0x0fff;
|
||||
if (addr < 0x0080)
|
||||
{
|
||||
RAM[addr & 0x7f] = value;
|
||||
return;
|
||||
}
|
||||
UpdateBank(addr);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private CartA16KR()
|
||||
{
|
||||
}
|
||||
|
||||
public CartA16KR(byte[] romBytes)
|
||||
{
|
||||
LoadRom(romBytes, 0x4000);
|
||||
Bank = 0;
|
||||
RAM = new byte[0x80];
|
||||
}
|
||||
|
||||
void UpdateBank(ushort addr)
|
||||
{
|
||||
if (addr < 0x0ff6 || addr > 0x0ff9)
|
||||
{}
|
||||
else
|
||||
{
|
||||
Bank = addr - 0x0ff6;
|
||||
}
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public CartA16KR(DeserializationContext input, MachineBase m) : base(input)
|
||||
{
|
||||
input.CheckVersion(1);
|
||||
LoadRom(input.ReadExpectedBytes(0x4000), 0x4000);
|
||||
BankBaseAddr = input.ReadUInt16();
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(ROM);
|
||||
output.Write(BankBaseAddr);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Atari standard 2KB carts (no bankswitching)
|
||||
/// </summary>
|
||||
public sealed class CartA2K : Cart
|
||||
{
|
||||
//
|
||||
// Cart Format Mapping to ROM Address Space
|
||||
// 0x0000:0x0800 0x1000:0x0800
|
||||
// 0x1800:0x0800 (1st 2k bank repeated)
|
||||
//
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public override byte this[ushort addr]
|
||||
{
|
||||
get { return ROM[addr & 0x07ff]; }
|
||||
set { }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private CartA2K()
|
||||
{
|
||||
}
|
||||
|
||||
public CartA2K(byte[] romBytes)
|
||||
{
|
||||
LoadRom(romBytes, 0x0800);
|
||||
}
|
||||
|
||||
public CartA2K(byte[] romBytes, int multicartBankSelector)
|
||||
{
|
||||
LoadRom(romBytes, 0x800, multicartBankSelector & 0x1f);
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public CartA2K(DeserializationContext input, MachineBase m) : base(input)
|
||||
{
|
||||
input.CheckVersion(1);
|
||||
LoadRom(input.ReadExpectedBytes(0x0800), 0x0800);
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(ROM);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Atari standard 32KB bankswitched carts
|
||||
/// </summary>
|
||||
public sealed class CartA32K : Cart
|
||||
{
|
||||
//
|
||||
// Cart Format Mapping to ROM Address Space
|
||||
// Bank1: 0x0000:0x1000 0x1000:0x1000 Bank selected by accessing 0x0ff4-0x0ffc
|
||||
// Bank2: 0x1000:0x1000
|
||||
// Bank3: 0x2000:0x1000
|
||||
// Bank4: 0x3000:0x1000
|
||||
// Bank5: 0x4000:0x1000
|
||||
// Bank6: 0x5000:0x1000
|
||||
// Bank7: 0x6000:0x1000
|
||||
// Bank8: 0x7000:0x1000
|
||||
//
|
||||
ushort BankBaseAddr;
|
||||
|
||||
int Bank
|
||||
{
|
||||
set { BankBaseAddr = (ushort)(value * 0x1000); }
|
||||
}
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
Bank = 7;
|
||||
}
|
||||
|
||||
public override byte this[ushort addr]
|
||||
{
|
||||
get
|
||||
{
|
||||
addr &= 0x0fff;
|
||||
UpdateBank(addr);
|
||||
return ROM[BankBaseAddr + addr];
|
||||
}
|
||||
set
|
||||
{
|
||||
addr &= 0x0fff;
|
||||
UpdateBank(addr);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private CartA32K()
|
||||
{
|
||||
}
|
||||
|
||||
public CartA32K(byte[] romBytes)
|
||||
{
|
||||
LoadRom(romBytes, 0x8000);
|
||||
Bank = 7;
|
||||
}
|
||||
|
||||
void UpdateBank(ushort addr)
|
||||
{
|
||||
if (addr < 0x0ffc && addr >= 0x0ff4)
|
||||
{
|
||||
Bank = addr - 0x0ff4;
|
||||
}
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public CartA32K(DeserializationContext input, MachineBase m) : base(input)
|
||||
{
|
||||
input.CheckVersion(1);
|
||||
LoadRom(input.ReadExpectedBytes(0x8000), 0x8000);
|
||||
BankBaseAddr = input.ReadUInt16();
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(ROM);
|
||||
output.Write(BankBaseAddr);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Atari standard 32KB bankswitched carts with 128 bytes of RAM
|
||||
/// </summary>
|
||||
public sealed class CartA32KR : Cart
|
||||
{
|
||||
//
|
||||
// Cart Format Mapping to ROM Address Space
|
||||
// Bank1: 0x0000:0x1000 0x1000:0x1000 Bank selected by accessing 0x0ff4-0x0ffc
|
||||
// Bank2: 0x1000:0x1000
|
||||
// Bank3: 0x2000:0x1000
|
||||
// Bank4: 0x3000:0x1000
|
||||
// Bank5: 0x4000:0x1000
|
||||
// Bank6: 0x5000:0x1000
|
||||
// Bank7: 0x6000:0x1000
|
||||
// Bank8: 0x7000:0x1000
|
||||
// Shadows ROM
|
||||
// 0x1000:0x80 RAM write port
|
||||
// 0x1080:0x80 RAM read port
|
||||
//
|
||||
ushort BankBaseAddr;
|
||||
readonly byte[] RAM;
|
||||
|
||||
int Bank
|
||||
{
|
||||
set { BankBaseAddr = (ushort)(value * 0x1000); }
|
||||
}
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
Bank = 7;
|
||||
}
|
||||
|
||||
public override byte this[ushort addr]
|
||||
{
|
||||
get
|
||||
{
|
||||
addr &= 0x0fff;
|
||||
if (addr >= 0x0080 && addr < 0x0100)
|
||||
{
|
||||
return RAM[addr & 0x007f];
|
||||
}
|
||||
UpdateBank(addr);
|
||||
return ROM[BankBaseAddr + addr];
|
||||
}
|
||||
set
|
||||
{
|
||||
addr &= 0x0fff;
|
||||
if (addr < 0x0080)
|
||||
{
|
||||
RAM[addr & 0x007f] = value;
|
||||
return;
|
||||
}
|
||||
UpdateBank(addr);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private CartA32KR()
|
||||
{
|
||||
}
|
||||
|
||||
public CartA32KR(byte[] romBytes)
|
||||
{
|
||||
LoadRom(romBytes, 0x8000);
|
||||
RAM = new byte[0x80];
|
||||
Bank = 7;
|
||||
}
|
||||
|
||||
void UpdateBank(ushort addr)
|
||||
{
|
||||
if (addr < 0x0ffc && addr >= 0x0ff4 )
|
||||
{
|
||||
Bank = addr - 0x0ff4;
|
||||
}
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public CartA32KR(DeserializationContext input, MachineBase m) : base(input)
|
||||
{
|
||||
input.CheckVersion(1);
|
||||
LoadRom(input.ReadExpectedBytes(0x8000), 0x8000);
|
||||
RAM = input.ReadExpectedBytes(0x80);
|
||||
BankBaseAddr = input.ReadUInt16();
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(ROM);
|
||||
output.Write(RAM);
|
||||
output.Write(BankBaseAddr);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Atari standard 4KB carts (no bankswitching)
|
||||
/// </summary>
|
||||
public sealed class CartA4K : Cart
|
||||
{
|
||||
#region IDevice Members
|
||||
|
||||
public override void Reset() { }
|
||||
|
||||
public override byte this[ushort addr]
|
||||
{
|
||||
get { return ROM[addr & 0x0fff]; }
|
||||
set { }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private CartA4K()
|
||||
{
|
||||
}
|
||||
|
||||
public CartA4K(byte[] romBytes)
|
||||
{
|
||||
LoadRom(romBytes, 0x1000);
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public CartA4K(DeserializationContext input, MachineBase m) : base(input)
|
||||
{
|
||||
input.CheckVersion(1);
|
||||
LoadRom(input.ReadExpectedBytes(0x1000), 0x1000);
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(ROM);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Atari standard 8KB bankswitched carts
|
||||
/// </summary>
|
||||
public sealed class CartA8K : Cart
|
||||
{
|
||||
//
|
||||
// Cart Format Mapping to ROM Address Space
|
||||
// Bank1: 0x0000:0x1000 0x1000:0x1000 Bank selected by accessing 0x1ff8,0x1ff9
|
||||
// Bank2: 0x1000:0x1000
|
||||
//
|
||||
ushort BankBaseAddr;
|
||||
|
||||
int Bank
|
||||
{
|
||||
set { BankBaseAddr = (ushort)(value * 0x1000); }
|
||||
}
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
Bank = 1;
|
||||
}
|
||||
|
||||
public override byte this[ushort addr]
|
||||
{
|
||||
get
|
||||
{
|
||||
addr &= 0x0fff;
|
||||
UpdateBank(addr);
|
||||
return ROM[BankBaseAddr + addr];
|
||||
}
|
||||
set
|
||||
{
|
||||
addr &= 0x0fff;
|
||||
UpdateBank(addr);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private CartA8K()
|
||||
{
|
||||
}
|
||||
|
||||
public CartA8K(byte[] romBytes)
|
||||
{
|
||||
LoadRom(romBytes, 0x2000);
|
||||
Bank = 1;
|
||||
}
|
||||
|
||||
void UpdateBank(ushort addr)
|
||||
{
|
||||
switch(addr)
|
||||
{
|
||||
case 0x0ff8:
|
||||
Bank = 0;
|
||||
break;
|
||||
case 0x0ff9:
|
||||
Bank = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public CartA8K(DeserializationContext input, MachineBase m) : base(input)
|
||||
{
|
||||
input.CheckVersion(1);
|
||||
LoadRom(input.ReadExpectedBytes(0x2000), 0x2000);
|
||||
BankBaseAddr = input.ReadUInt16();
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(ROM);
|
||||
output.Write(BankBaseAddr);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Atari standard 8KB bankswitched carts with 128 bytes of RAM
|
||||
/// </summary>
|
||||
public sealed class CartA8KR : Cart
|
||||
{
|
||||
//
|
||||
// Cart Format Mapping to ROM Address Space
|
||||
// Bank1: 0x0000:0x1000 0x1000:0x1000 Bank selected by accessing 0x1ff8,0x1ff9
|
||||
// Bank2: 0x1000:0x1000
|
||||
// Shadows ROM
|
||||
// 0x1000:0x0080 RAM write port
|
||||
// 0x1080:0x0080 RAM read port
|
||||
//
|
||||
ushort BankBaseAddr;
|
||||
readonly byte[] RAM;
|
||||
|
||||
int Bank
|
||||
{
|
||||
set { BankBaseAddr = (ushort)(value * 0x1000); }
|
||||
}
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
Bank = 1;
|
||||
}
|
||||
|
||||
public override byte this[ushort addr]
|
||||
{
|
||||
get
|
||||
{
|
||||
addr &= 0x0fff;
|
||||
if (addr < 0x0100 && addr >= 0x0080)
|
||||
{
|
||||
return RAM[addr & 0x7f];
|
||||
}
|
||||
UpdateBank(addr);
|
||||
return ROM[BankBaseAddr + addr];
|
||||
}
|
||||
set
|
||||
{
|
||||
addr &= 0x0fff;
|
||||
if (addr < 0x0080)
|
||||
{
|
||||
RAM[addr & 0x7f] = value;
|
||||
return;
|
||||
}
|
||||
UpdateBank(addr);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private CartA8KR()
|
||||
{
|
||||
}
|
||||
|
||||
public CartA8KR(byte[] romBytes)
|
||||
{
|
||||
LoadRom(romBytes, 0x2000);
|
||||
Bank = 1;
|
||||
RAM = new byte[0x80];
|
||||
}
|
||||
|
||||
void UpdateBank(ushort addr)
|
||||
{
|
||||
switch(addr)
|
||||
{
|
||||
case 0x0ff8:
|
||||
Bank = 0;
|
||||
break;
|
||||
case 0x0ff9:
|
||||
Bank = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public CartA8KR(DeserializationContext input, MachineBase m) : base(input)
|
||||
{
|
||||
input.CheckVersion(1);
|
||||
LoadRom(input.ReadExpectedBytes(0x2000), 0x2000);
|
||||
RAM = input.ReadExpectedBytes(0x80);
|
||||
BankBaseAddr = input.ReadUInt16();
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(ROM);
|
||||
output.Write(RAM);
|
||||
output.Write(BankBaseAddr);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// CBS RAM Plus 12KB bankswitched carts with 128 bytes of RAM.
|
||||
/// </summary>
|
||||
public sealed class CartCBS12K : Cart
|
||||
{
|
||||
//
|
||||
// Cart Format Mapping to ROM Address Space
|
||||
// Bank1: 0x0000:0x1000 Bank1:0x1000:0x1000 Select Segment: 0ff8-0ffa
|
||||
// Bank2: 0x1000:0x1000
|
||||
// Bank3: 0x2000:0x1000
|
||||
// Shadows ROM
|
||||
// 0x1000:0x80 RAM write port
|
||||
// 0x1080:0x80 RAM read port
|
||||
//
|
||||
ushort BankBaseAddr;
|
||||
readonly byte[] RAM;
|
||||
|
||||
int Bank
|
||||
{
|
||||
set { BankBaseAddr = (ushort)(value * 0x1000); }
|
||||
}
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
Bank = 2;
|
||||
}
|
||||
|
||||
public override byte this[ushort addr]
|
||||
{
|
||||
get
|
||||
{
|
||||
addr &= 0x0fff;
|
||||
if (addr < 0x0200 && addr >= 0x0100)
|
||||
{
|
||||
return RAM[addr & 0xff];
|
||||
}
|
||||
UpdateBank(addr);
|
||||
return ROM[BankBaseAddr + addr];
|
||||
}
|
||||
set
|
||||
{
|
||||
addr &= 0x0fff;
|
||||
if (addr < 0x0100)
|
||||
{
|
||||
RAM[addr & 0xff] = value;
|
||||
return;
|
||||
}
|
||||
UpdateBank(addr);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private CartCBS12K()
|
||||
{
|
||||
}
|
||||
|
||||
public CartCBS12K(byte[] romBytes)
|
||||
{
|
||||
LoadRom(romBytes, 0x3000);
|
||||
Bank = 2;
|
||||
RAM = new byte[0x100];
|
||||
}
|
||||
|
||||
void UpdateBank(ushort addr)
|
||||
{
|
||||
if (addr < 0x0ff8 || addr > 0x0ffa) { }
|
||||
else
|
||||
{
|
||||
Bank = addr - 0x0ff8;
|
||||
}
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public CartCBS12K(DeserializationContext input, MachineBase m) : base(input)
|
||||
{
|
||||
input.CheckVersion(1);
|
||||
LoadRom(input.ReadExpectedBytes(0x3000), 0x3000);
|
||||
RAM = input.ReadExpectedBytes(0x100);
|
||||
BankBaseAddr = input.ReadUInt16();
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(ROM);
|
||||
output.Write(RAM);
|
||||
output.Write(BankBaseAddr);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Activison's Robot Tank and Decathlon 8KB bankswitching cart.
|
||||
/// </summary>
|
||||
public sealed class CartDC8K : Cart
|
||||
{
|
||||
//
|
||||
// Cart Format Mapping to ROM Address Space
|
||||
// Bank1: 0x0000:0x1000 0x1000:0x1000 Bank selected by A13=0/1?
|
||||
// Bank2: 0x1000:0x1000
|
||||
//
|
||||
// This does what the Stella code does, which is to follow A13 to determine
|
||||
// the bank. Since A0-A12 are the only significant bits on the program
|
||||
// counter, I am unsure how the cart/hardware could utilize this.
|
||||
//
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public override byte this[ushort addr]
|
||||
{
|
||||
get { return (addr & 0x2000) == 0 ? ROM[addr & 0x0fff + 0x1000] : ROM[addr & 0x0fff]; }
|
||||
set { }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private CartDC8K()
|
||||
{
|
||||
}
|
||||
|
||||
public CartDC8K(byte[] romBytes)
|
||||
{
|
||||
LoadRom(romBytes, 0x2000);
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public CartDC8K(DeserializationContext input, MachineBase m) : base(input)
|
||||
{
|
||||
input.CheckVersion(1);
|
||||
LoadRom(input.ReadExpectedBytes(0x2000), 0x2000);
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(ROM);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,341 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Pitfall II cartridge.
|
||||
/// There are two 4k banks, 2k display bank, and the DPC chip.
|
||||
/// For complete details on the DPC chip see David P. Crane's United States Patent Number 4,644,495.
|
||||
/// </summary>
|
||||
public sealed class CartDPC : Cart
|
||||
{
|
||||
//
|
||||
// Cart Format Mapping to ROM Address Space
|
||||
// Bank1: 0x0000:0x1000 0x1000:0x1000 Bank selected by accessing 0x1ff8,0x1ff9
|
||||
// Bank2: 0x1000:0x1000
|
||||
//
|
||||
const ushort DisplayBaseAddr = 0x2000;
|
||||
ushort BankBaseAddr;
|
||||
|
||||
readonly byte[] MusicAmplitudes = new byte[] { 0x00, 0x04, 0x05, 0x09, 0x06, 0x0a, 0x0b, 0x0f };
|
||||
|
||||
readonly byte[] Tops = new byte[8];
|
||||
readonly byte[] Bots = new byte[8];
|
||||
readonly ushort[] Counters = new ushort[8];
|
||||
readonly byte[] Flags = new byte[8];
|
||||
readonly bool[] MusicMode = new bool[3];
|
||||
|
||||
ulong LastSystemClock;
|
||||
double FractionalClocks;
|
||||
|
||||
byte _ShiftRegister;
|
||||
|
||||
int Bank
|
||||
{
|
||||
set { BankBaseAddr = (ushort)(value * 0x1000); }
|
||||
}
|
||||
|
||||
//
|
||||
// Generate a sequence of pseudo-random numbers 255 numbers long
|
||||
// by emulating an 8-bit shift register with feedback taps at
|
||||
// bits 4, 3, 2, and 0.
|
||||
byte ShiftRegister
|
||||
{
|
||||
get
|
||||
{
|
||||
var a = _ShiftRegister;
|
||||
a &= (1 << 0);
|
||||
|
||||
var x = _ShiftRegister;
|
||||
x &= (1 << 2);
|
||||
x >>= 2;
|
||||
a ^= x;
|
||||
|
||||
x = _ShiftRegister;
|
||||
x &= (1 << 3);
|
||||
x >>= 3;
|
||||
a ^= x;
|
||||
|
||||
x = _ShiftRegister;
|
||||
x &= (1 << 4);
|
||||
x >>= 4;
|
||||
a ^= x;
|
||||
|
||||
a <<= 7;
|
||||
_ShiftRegister >>= 1;
|
||||
_ShiftRegister |= a;
|
||||
|
||||
return _ShiftRegister;
|
||||
}
|
||||
set { _ShiftRegister = value; }
|
||||
}
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
Bank = 1;
|
||||
LastSystemClock = 3*M.CPU.Clock;
|
||||
FractionalClocks = 0.0;
|
||||
ShiftRegister = 1;
|
||||
}
|
||||
|
||||
public override byte this[ushort addr]
|
||||
{
|
||||
get
|
||||
{
|
||||
addr &= 0x0fff;
|
||||
if (addr < 0x0040)
|
||||
{
|
||||
return ReadPitfall2Reg(addr);
|
||||
}
|
||||
UpdateBank(addr);
|
||||
return ROM[BankBaseAddr + addr];
|
||||
}
|
||||
set
|
||||
{
|
||||
addr &= 0x0fff;
|
||||
if (addr >= 0x0040 && addr < 0x0080)
|
||||
{
|
||||
WritePitfall2Reg(addr, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateBank(addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private CartDPC()
|
||||
{
|
||||
}
|
||||
|
||||
public CartDPC(byte[] romBytes)
|
||||
{
|
||||
LoadRom(romBytes, 0x2800);
|
||||
Bank = 1;
|
||||
}
|
||||
|
||||
void UpdateBank(ushort addr)
|
||||
{
|
||||
switch(addr)
|
||||
{
|
||||
case 0x0ff8:
|
||||
Bank = 0;
|
||||
break;
|
||||
case 0x0ff9:
|
||||
Bank = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
byte ReadPitfall2Reg(ushort addr)
|
||||
{
|
||||
byte result;
|
||||
|
||||
var i = addr & 0x07;
|
||||
var fn = (addr >> 3) & 0x07;
|
||||
|
||||
// Update flag register for selected data fetcher
|
||||
if ((Counters[i] & 0x00ff) == Tops[i])
|
||||
{
|
||||
Flags[i] = 0xff;
|
||||
}
|
||||
else if ((Counters[i] & 0x00ff) == Bots[i])
|
||||
{
|
||||
Flags[i] = 0x00;
|
||||
}
|
||||
|
||||
switch (fn)
|
||||
{
|
||||
case 0x00:
|
||||
if (i < 4)
|
||||
{
|
||||
// This is a random number read
|
||||
result = ShiftRegister;
|
||||
break;
|
||||
}
|
||||
// Its a music read
|
||||
UpdateMusicModeDataFetchers();
|
||||
|
||||
byte j = 0;
|
||||
if (MusicMode[0] && Flags[5] != 0)
|
||||
{
|
||||
j |= 0x01;
|
||||
}
|
||||
if (MusicMode[1] && Flags[6] != 0)
|
||||
{
|
||||
j |= 0x02;
|
||||
}
|
||||
if (MusicMode[2] && Flags[7] != 0)
|
||||
{
|
||||
j |= 0x04;
|
||||
}
|
||||
result = MusicAmplitudes[j];
|
||||
break;
|
||||
// DFx display data read
|
||||
case 0x01:
|
||||
result = ROM[DisplayBaseAddr + 0x7ff - Counters[i]];
|
||||
break;
|
||||
// DFx display data read AND'd w/flag
|
||||
case 0x02:
|
||||
result = ROM[DisplayBaseAddr + 0x7ff - Counters[i]];
|
||||
result &= Flags[i];
|
||||
break;
|
||||
// DFx flag
|
||||
case 0x07:
|
||||
result = Flags[i];
|
||||
break;
|
||||
default:
|
||||
result = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Clock the selected data fetcher's counter if needed
|
||||
if (i < 5 || i >= 5 && MusicMode[i - 5] == false)
|
||||
{
|
||||
Counters[i]--;
|
||||
Counters[i] &= 0x07ff;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void UpdateMusicModeDataFetchers()
|
||||
{
|
||||
var sysClockDelta = 3*M.CPU.Clock - LastSystemClock;
|
||||
LastSystemClock = 3*M.CPU.Clock;
|
||||
|
||||
var OSCclocks = ((15750.0 * sysClockDelta) / 1193191.66666667) + FractionalClocks;
|
||||
|
||||
var wholeClocks = (int)OSCclocks;
|
||||
FractionalClocks = OSCclocks - wholeClocks;
|
||||
if (wholeClocks <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i=0; i < 3; i++)
|
||||
{
|
||||
var r = i + 5;
|
||||
if (!MusicMode[i]) continue;
|
||||
|
||||
var top = Tops[r] + 1;
|
||||
var newLow = Counters[r] & 0x00ff;
|
||||
|
||||
if (Tops[r] != 0)
|
||||
{
|
||||
newLow -= (wholeClocks % top);
|
||||
if (newLow < 0)
|
||||
{
|
||||
newLow += top;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
newLow = 0;
|
||||
}
|
||||
|
||||
if (newLow <= Bots[r])
|
||||
{
|
||||
Flags[r] = 0x00;
|
||||
}
|
||||
else if (newLow <= Tops[r])
|
||||
{
|
||||
Flags[r] = 0xff;
|
||||
}
|
||||
|
||||
Counters[r] = (ushort)((Counters[r] & 0x0700) | (ushort)newLow);
|
||||
}
|
||||
}
|
||||
|
||||
void WritePitfall2Reg(ushort addr, byte val)
|
||||
{
|
||||
var i = addr & 0x07;
|
||||
var fn = (addr >> 3) & 0x07;
|
||||
|
||||
switch (fn)
|
||||
{
|
||||
// DFx top count
|
||||
case 0x00:
|
||||
Tops[i] = val;
|
||||
Flags[i] = 0x00;
|
||||
break;
|
||||
// DFx bottom count
|
||||
case 0x01:
|
||||
Bots[i] = val;
|
||||
break;
|
||||
// DFx counter low
|
||||
case 0x02:
|
||||
Counters[i] &= 0x0700;
|
||||
if (i >= 5 && MusicMode[i - 5])
|
||||
{
|
||||
// Data fetcher is in music mode so its low counter value
|
||||
// should be loaded from the top register not the poked value
|
||||
Counters[i] |= Tops[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Data fetcher is either not a music mode data fetcher or it
|
||||
// isn't in music mode so it's low counter value should be loaded
|
||||
// with the poked value
|
||||
Counters[i] |= val;
|
||||
}
|
||||
break;
|
||||
// DFx counter high
|
||||
case 0x03:
|
||||
Counters[i] &= 0x00ff;
|
||||
Counters[i] |= (ushort)((val & 0x07) << 8);
|
||||
// Execute special code for music mode data fetchers
|
||||
if (i >= 5)
|
||||
{
|
||||
MusicMode[i - 5] = (val & 0x10) != 0;
|
||||
// NOTE: We are not handling the clock source input for
|
||||
// the music mode data fetchers. We're going to assume
|
||||
// they always use the OSC input.
|
||||
}
|
||||
break;
|
||||
// Random Number Generator Reset
|
||||
case 0x06:
|
||||
ShiftRegister = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public CartDPC(DeserializationContext input, MachineBase m) : base(input)
|
||||
{
|
||||
input.CheckVersion(1);
|
||||
LoadRom(input.ReadExpectedBytes(0x2800), 0x2800);
|
||||
BankBaseAddr = input.ReadUInt16();
|
||||
Tops = input.ReadExpectedBytes(8);
|
||||
Bots = input.ReadExpectedBytes(8);
|
||||
Counters = input.ReadUnsignedShorts(8);
|
||||
Flags = input.ReadExpectedBytes(8);
|
||||
MusicMode = input.ReadBooleans(3);
|
||||
LastSystemClock = input.ReadUInt64();
|
||||
FractionalClocks = input.ReadDouble();
|
||||
_ShiftRegister = input.ReadByte();
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(ROM);
|
||||
output.Write(BankBaseAddr);
|
||||
output.Write(Tops);
|
||||
output.Write(Bots);
|
||||
output.Write(Counters);
|
||||
output.Write(Flags);
|
||||
output.Write(MusicMode);
|
||||
output.Write(LastSystemClock);
|
||||
output.Write(FractionalClocks);
|
||||
output.Write(_ShiftRegister);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,136 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// M-Network 16KB bankswitched carts with 2KB RAM.
|
||||
/// </summary>
|
||||
public sealed class CartMN16K : Cart
|
||||
{
|
||||
//
|
||||
// Cart Format Mapping to ROM Address Space
|
||||
// Segment1: 0x0000:0x0800 Bank1:0x1000:0x0800 Select Seg: 1fe0-1fe6, 1fe7=RAM Seg1
|
||||
// Segment2: 0x0800:0x0800 Bank2:0x1800:0x0800 Always Seg8
|
||||
// Segment3: 0x1000:0x0800
|
||||
// Segment4: 0x1800:0x0800
|
||||
// Segment5: 0x2000:0x0800
|
||||
// Segment6: 0x2800:0x0800
|
||||
// Segment7: 0x3000:0x0800
|
||||
// Segment8: 0x3800:0x0800
|
||||
//
|
||||
// RAM RAM Segment1 when 1fe7 select is accessed
|
||||
// Segment1: 0x0000:0x0400 0x1000-0x13FF write port
|
||||
// Segment2: 0x0400:0x0400 0x1400-0x17FF read port
|
||||
//
|
||||
// RAM Segment2: 1ff8-1ffb selects 256-byte block
|
||||
// 0x1800-0x18ff write port
|
||||
// 0x1900-0x19ff read port
|
||||
//
|
||||
ushort BankBaseAddr, BankBaseRAMAddr;
|
||||
bool RAMBankOn;
|
||||
readonly byte[] RAM;
|
||||
|
||||
int Bank
|
||||
{
|
||||
set
|
||||
{
|
||||
BankBaseAddr = (ushort)(value << 11); // multiply by 2048
|
||||
RAMBankOn = (value == 0x07);
|
||||
}
|
||||
}
|
||||
|
||||
int BankRAM
|
||||
{
|
||||
set { BankBaseRAMAddr = (ushort) (value << 8); } // multiply by 256
|
||||
}
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
Bank = 0;
|
||||
BankRAM = 0;
|
||||
}
|
||||
|
||||
public override byte this[ushort addr]
|
||||
{
|
||||
get
|
||||
{
|
||||
addr &= 0x0fff;
|
||||
UpdateBanks(addr);
|
||||
if (RAMBankOn && addr >= 0x0400 && addr < 0x0800)
|
||||
{
|
||||
return RAM[addr & 0x03ff];
|
||||
}
|
||||
if (addr >= 0x0900 && addr < 0x0a00)
|
||||
{
|
||||
return RAM[0x400 + BankBaseRAMAddr + (addr & 0xff)];
|
||||
}
|
||||
return addr < 0x0800 ? ROM[BankBaseAddr + (addr & 0x07ff)] : ROM[0x3800 + (addr & 0x07ff)];
|
||||
}
|
||||
set
|
||||
{
|
||||
addr &= 0x0fff;
|
||||
UpdateBanks(addr);
|
||||
if (RAMBankOn && addr < 0x0400)
|
||||
{
|
||||
RAM[addr & 0x03ff] = value;
|
||||
}
|
||||
else if (addr >= 0x0800 && addr < 0x0900)
|
||||
{
|
||||
RAM[0x400 + BankBaseRAMAddr + (addr & 0xff)] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private CartMN16K()
|
||||
{
|
||||
}
|
||||
|
||||
public CartMN16K(byte[] romBytes)
|
||||
{
|
||||
LoadRom(romBytes, 0x4000);
|
||||
RAM = new byte[0x800];
|
||||
Bank = 0;
|
||||
BankRAM = 0;
|
||||
}
|
||||
|
||||
void UpdateBanks(ushort addr)
|
||||
{
|
||||
if (addr >= 0x0fe0 && addr < 0x0fe8)
|
||||
{
|
||||
Bank = addr & 0x07;
|
||||
}
|
||||
else if (addr >= 0x0fe8 && addr < 0x0fec)
|
||||
{
|
||||
BankRAM = addr & 0x03;
|
||||
}
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public CartMN16K(DeserializationContext input, MachineBase m) : base(input)
|
||||
{
|
||||
input.CheckVersion(1);
|
||||
LoadRom(input.ReadExpectedBytes(0x4000), 0x4000);
|
||||
RAM = input.ReadExpectedBytes(0x800);
|
||||
BankBaseAddr = input.ReadUInt16();
|
||||
BankBaseRAMAddr = input.ReadUInt16();
|
||||
RAMBankOn = input.ReadBoolean();
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(ROM);
|
||||
output.Write(RAM);
|
||||
output.Write(BankBaseAddr);
|
||||
output.Write(BankBaseRAMAddr);
|
||||
output.Write(RAMBankOn);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Parker Brothers 8KB bankswitched carts.
|
||||
/// </summary>
|
||||
public sealed class CartPB8K : Cart
|
||||
{
|
||||
//
|
||||
// Cart Format Mapping to ROM Address Space
|
||||
// Segment1: 0x0000:0x0400 Bank1:0x1000:0x0400 Select Segment: 1fe0-1fe7
|
||||
// Segment2: 0x0400:0x0400 Bank2:0x1400:0x0400 Select Segment: 1fe8-1ff0
|
||||
// Segment3: 0x0800:0x0400 Bank3:0x1800:0x0400 Select Segment: 1ff0-1ff8
|
||||
// Segment4: 0x0c00:0x0400 Bank4:0x1c00:0x0400 Always Segment8
|
||||
// Segment5: 0x1000:0x0400
|
||||
// Segment6: 0x1400:0x0400
|
||||
// Segment7: 0x1800:0x0400
|
||||
// Segment8: 0x1c00:0x0400
|
||||
//
|
||||
readonly ushort[] SegmentBase;
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SegmentBase[0] = ComputeSegmentBase(4);
|
||||
SegmentBase[1] = ComputeSegmentBase(5);
|
||||
SegmentBase[2] = ComputeSegmentBase(6);
|
||||
}
|
||||
|
||||
public override byte this[ushort addr]
|
||||
{
|
||||
get
|
||||
{
|
||||
addr &= 0x0fff;
|
||||
UpdateSegmentBases(addr);
|
||||
return ROM[SegmentBase[addr >> 10] + (addr & 0x03ff)];
|
||||
}
|
||||
set
|
||||
{
|
||||
addr &= 0x0fff;
|
||||
UpdateSegmentBases(addr);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private CartPB8K()
|
||||
{
|
||||
}
|
||||
|
||||
public CartPB8K(byte[] romBytes)
|
||||
{
|
||||
LoadRom(romBytes, 0x2000);
|
||||
SegmentBase = new ushort[4];
|
||||
SegmentBase[0] = ComputeSegmentBase(4);
|
||||
SegmentBase[1] = ComputeSegmentBase(5);
|
||||
SegmentBase[2] = ComputeSegmentBase(6);
|
||||
SegmentBase[3] = ComputeSegmentBase(7);
|
||||
}
|
||||
|
||||
static ushort ComputeSegmentBase(int slice)
|
||||
{
|
||||
return (ushort)(slice << 10); // multiply by 1024
|
||||
}
|
||||
|
||||
void UpdateSegmentBases(ushort addr)
|
||||
{
|
||||
if (addr < 0xfe0 || addr >= 0x0ff8) { }
|
||||
else if (addr >= 0x0fe0 && addr < 0x0fe8)
|
||||
{
|
||||
SegmentBase[0] = ComputeSegmentBase(addr & 0x07);
|
||||
}
|
||||
else if (addr >= 0x0fe8 && addr < 0x0ff0)
|
||||
{
|
||||
SegmentBase[1] = ComputeSegmentBase(addr & 0x07);
|
||||
}
|
||||
else if (addr >= 0x0ff0 && addr < 0x0ff8)
|
||||
{
|
||||
SegmentBase[2] = ComputeSegmentBase(addr & 0x07);
|
||||
}
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public CartPB8K(DeserializationContext input, MachineBase m) : base(input)
|
||||
{
|
||||
input.CheckVersion(1);
|
||||
LoadRom(input.ReadExpectedBytes(0x2000), 0x2000);
|
||||
SegmentBase = input.ReadUnsignedShorts();
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(ROM);
|
||||
output.Write(SegmentBase);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Tigervision 8KB bankswitched carts
|
||||
/// </summary>
|
||||
public sealed class CartTV8K : Cart
|
||||
{
|
||||
//
|
||||
// Cart Format Mapping to ROM Address Space
|
||||
// Segment1: 0x0000:0x0800 0x1000:0x0800 Selected segment via $003F
|
||||
// Segment2: 0x0800:0x0800 0x1800:0x0800 Always last segment
|
||||
// Segment3: 0x1000:0x0800
|
||||
// Segment4: 0x1800:0x0800
|
||||
//
|
||||
ushort BankBaseAddr;
|
||||
readonly ushort LastBankBaseAddr;
|
||||
|
||||
byte Bank
|
||||
{
|
||||
set
|
||||
{
|
||||
BankBaseAddr = (ushort)(0x0800 * value);
|
||||
BankBaseAddr %= (ushort)ROM.Length;
|
||||
}
|
||||
}
|
||||
|
||||
protected internal override bool RequestSnooping
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
Bank = 0;
|
||||
}
|
||||
|
||||
public override byte this[ushort addr]
|
||||
{
|
||||
get
|
||||
{
|
||||
addr &= 0x0fff;
|
||||
return addr < 0x0800 ? ROM[BankBaseAddr + (addr & 0x07ff)] : ROM[LastBankBaseAddr + (addr & 0x07ff)];
|
||||
}
|
||||
set
|
||||
{
|
||||
if (addr <= 0x003f)
|
||||
{
|
||||
Bank = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private CartTV8K()
|
||||
{
|
||||
}
|
||||
|
||||
public CartTV8K(byte[] romBytes)
|
||||
{
|
||||
LoadRom(romBytes, 0x1000);
|
||||
Bank = 0;
|
||||
LastBankBaseAddr = (ushort)(ROM.Length - 0x0800);
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public CartTV8K(DeserializationContext input, MachineBase m) : base(input)
|
||||
{
|
||||
input.CheckVersion(1);
|
||||
LoadRom(input.ReadExpectedBytes(0x1000), 0x1000);
|
||||
BankBaseAddr = input.ReadUInt16();
|
||||
LastBankBaseAddr = input.ReadUInt16();
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(ROM);
|
||||
output.Write(BankBaseAddr);
|
||||
output.Write(LastBankBaseAddr);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* CartType.cs
|
||||
*
|
||||
* Defines the set of all known Game Cartridges.
|
||||
*
|
||||
* 2010 Mike Murphy
|
||||
*
|
||||
*/
|
||||
namespace EMU7800.Core
|
||||
{
|
||||
public enum CartType
|
||||
{
|
||||
None,
|
||||
A2K, // Atari 2kb cart
|
||||
TV8K, // Tigervision 8kb bankswitched cart
|
||||
A4K, // Atari 4kb cart
|
||||
PB8K, // Parker Brothers 8kb bankswitched cart
|
||||
MN16K, // M-Network 16kb bankswitched cart
|
||||
A16K, // Atari 16kb bankswitched cart
|
||||
A16KR, // Atari 16kb bankswitched cart w/128 bytes RAM
|
||||
A8K, // Atari 8KB bankswitched cart
|
||||
A8KR, // Atari 8KB bankswitched cart w/128 bytes RAM
|
||||
A32K, // Atari 32KB bankswitched cart
|
||||
A32KR, // Atari 32KB bankswitched cart w/128 bytes RAM
|
||||
CBS12K, // CBS' RAM Plus bankswitched cart w/256 bytes RAM
|
||||
DC8K, // Special Activision cart (Robot Tank and Decathlon)
|
||||
DPC, // Pitfall II DPC cart
|
||||
M32N12K, // 32N1 Multicart: 32x2KB
|
||||
A7808, // Atari7800 non-bankswitched 8KB cart
|
||||
A7816, // Atari7800 non-bankswitched 16KB cart
|
||||
A7832, // Atari7800 non-bankswitched 32KB cart
|
||||
A7832P, // Atari7800 non-bankswitched 32KB cart w/Pokey
|
||||
A7848, // Atari7800 non-bankswitched 48KB cart
|
||||
A78SG, // Atari7800 SuperGame cart
|
||||
A78SGP, // Atari7800 SuperGame cart w/Pokey
|
||||
A78SGR, // Atari7800 SuperGame cart w/RAM
|
||||
A78S9, // Atari7800 SuperGame cart, nine banks
|
||||
A78S4, // Atari7800 SuperGame cart, four banks
|
||||
A78S4R, // Atari7800 SuperGame cart, four banks, w/RAM
|
||||
A78AB, // F18 Hornet cart (Absolute)
|
||||
A78AC, // Double dragon cart (Activision)
|
||||
};
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
internal enum ConsoleSwitch
|
||||
{
|
||||
GameReset,
|
||||
GameSelect,
|
||||
GameBW,
|
||||
LeftDifficultyA,
|
||||
RightDifficultyA,
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
public enum Controller
|
||||
{
|
||||
None,
|
||||
Joystick,
|
||||
Paddles,
|
||||
Keypad,
|
||||
Driving,
|
||||
BoosterGrip,
|
||||
ProLineJoystick,
|
||||
Lightgun,
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
public enum ControllerAction
|
||||
{
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
Trigger, // Interpretation: 7800 RFire; 2600 Fire, BoosterGrip top
|
||||
Trigger2, // Interpretation: 7800 LFire, BoosterGrip trigger
|
||||
Keypad1, Keypad2, Keypad3,
|
||||
Keypad4, Keypad5, Keypad6,
|
||||
Keypad7, Keypad8, Keypad9,
|
||||
KeypadA, Keypad0, KeypadP,
|
||||
Driving0, Driving1, Driving2, Driving3,
|
||||
}
|
||||
}
|
|
@ -1,247 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace EMU7800.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A context for deserializing <see cref="MachineBase"/> objects.
|
||||
/// </summary>
|
||||
public class DeserializationContext
|
||||
{
|
||||
#region Fields
|
||||
|
||||
readonly BinaryReader _binaryReader;
|
||||
|
||||
#endregion
|
||||
|
||||
public bool ReadBoolean()
|
||||
{
|
||||
return _binaryReader.ReadBoolean();
|
||||
}
|
||||
|
||||
public byte ReadByte()
|
||||
{
|
||||
return _binaryReader.ReadByte();
|
||||
}
|
||||
|
||||
public ushort ReadUInt16()
|
||||
{
|
||||
return _binaryReader.ReadUInt16();
|
||||
}
|
||||
|
||||
public int ReadInt32()
|
||||
{
|
||||
return _binaryReader.ReadInt32();
|
||||
}
|
||||
|
||||
public uint ReadUInt32()
|
||||
{
|
||||
return _binaryReader.ReadUInt32();
|
||||
}
|
||||
|
||||
public long ReadInt64()
|
||||
{
|
||||
return _binaryReader.ReadInt64();
|
||||
}
|
||||
|
||||
public ulong ReadUInt64()
|
||||
{
|
||||
return _binaryReader.ReadUInt64();
|
||||
}
|
||||
|
||||
public double ReadDouble()
|
||||
{
|
||||
return _binaryReader.ReadDouble();
|
||||
}
|
||||
|
||||
/*
|
||||
public BufferElement ReadBufferElement()
|
||||
{
|
||||
var be = new BufferElement();
|
||||
for (var i = 0; i < BufferElement.SIZE; i++)
|
||||
be[i] = ReadByte();
|
||||
return be;
|
||||
}
|
||||
*/
|
||||
|
||||
public byte[] ReadBytes()
|
||||
{
|
||||
var count = _binaryReader.ReadInt32();
|
||||
if (count <= 0)
|
||||
return new byte[0];
|
||||
if (count > 0x40000)
|
||||
throw new Emu7800SerializationException("Byte array length too large.");
|
||||
return _binaryReader.ReadBytes(count);
|
||||
}
|
||||
|
||||
public byte[] ReadExpectedBytes(params int[] expectedSizes)
|
||||
{
|
||||
var count = _binaryReader.ReadInt32();
|
||||
if (!expectedSizes.Any(t => t == count))
|
||||
throw new Emu7800SerializationException("Byte array length incorrect.");
|
||||
return _binaryReader.ReadBytes(count);
|
||||
}
|
||||
|
||||
public byte[] ReadOptionalBytes(params int[] expectedSizes)
|
||||
{
|
||||
var hasBytes = _binaryReader.ReadBoolean();
|
||||
return (hasBytes) ? ReadExpectedBytes(expectedSizes) : null;
|
||||
}
|
||||
|
||||
public ushort[] ReadUnsignedShorts(params int[] expectedSizes)
|
||||
{
|
||||
var bytes = ReadExpectedBytes(expectedSizes.Select(t => t << 1).ToArray());
|
||||
var ushorts = new ushort[bytes.Length >> 1];
|
||||
Buffer.BlockCopy(bytes, 0, ushorts, 0, bytes.Length);
|
||||
return ushorts;
|
||||
}
|
||||
|
||||
public int[] ReadIntegers(params int[] expectedSizes)
|
||||
{
|
||||
var bytes = ReadExpectedBytes(expectedSizes.Select(t => t << 2).ToArray());
|
||||
var integers = new int[bytes.Length >> 2];
|
||||
Buffer.BlockCopy(bytes, 0, integers, 0, bytes.Length);
|
||||
return integers;
|
||||
}
|
||||
|
||||
public uint[] ReadUnsignedIntegers(params int[] expectedSizes)
|
||||
{
|
||||
var bytes = ReadExpectedBytes(expectedSizes.Select(t => t << 2).ToArray());
|
||||
var uints = new uint[bytes.Length >> 2];
|
||||
Buffer.BlockCopy(bytes, 0, uints, 0, bytes.Length);
|
||||
return uints;
|
||||
}
|
||||
|
||||
public bool[] ReadBooleans(params int[] expectedSizes)
|
||||
{
|
||||
var bytes = ReadExpectedBytes(expectedSizes);
|
||||
var booleans = new bool[bytes.Length];
|
||||
for (var i = 0; i < bytes.Length; i++)
|
||||
booleans[i] = (bytes[i] != 0);
|
||||
return booleans;
|
||||
}
|
||||
|
||||
public int CheckVersion(params int[] validVersions)
|
||||
{
|
||||
var magicNumber = _binaryReader.ReadInt32();
|
||||
if (magicNumber != 0x78000087)
|
||||
throw new Emu7800SerializationException("Magic number not found.");
|
||||
var version = _binaryReader.ReadInt32();
|
||||
if (!validVersions.Any(t => t == version))
|
||||
throw new Emu7800SerializationException("Invalid version number found.");
|
||||
return version;
|
||||
}
|
||||
|
||||
public MachineBase ReadMachine()
|
||||
{
|
||||
var typeName = _binaryReader.ReadString();
|
||||
if (string.IsNullOrWhiteSpace(typeName))
|
||||
throw new Emu7800SerializationException("Invalid type name.");
|
||||
|
||||
var type = Type.GetType(typeName);
|
||||
if (type == null)
|
||||
throw new Emu7800SerializationException("Unable to resolve type name: " + typeName);
|
||||
|
||||
return (MachineBase)Activator.CreateInstance(type, new object[] { this });
|
||||
}
|
||||
|
||||
public AddressSpace ReadAddressSpace(MachineBase m, int addrSpaceShift, int pageShift)
|
||||
{
|
||||
var addressSpace = new AddressSpace(this, m, addrSpaceShift, pageShift);
|
||||
return addressSpace;
|
||||
}
|
||||
|
||||
public M6502 ReadM6502(MachineBase m, int runClocksMultiple)
|
||||
{
|
||||
var cpu = new M6502(this, m, runClocksMultiple);
|
||||
return cpu;
|
||||
}
|
||||
|
||||
public Maria ReadMaria(Machine7800 m, int scanlines)
|
||||
{
|
||||
var maria = new Maria(this, m, scanlines);
|
||||
return maria;
|
||||
}
|
||||
|
||||
public PIA ReadPIA(MachineBase m)
|
||||
{
|
||||
var pia = new PIA(this, m);
|
||||
return pia;
|
||||
}
|
||||
|
||||
public TIA ReadTIA(MachineBase m)
|
||||
{
|
||||
var tia = new TIA(this, m);
|
||||
return tia;
|
||||
}
|
||||
|
||||
public TIASound ReadTIASound(MachineBase m, int cpuClocksPerSample)
|
||||
{
|
||||
var tiaSound = new TIASound(this, m, cpuClocksPerSample);
|
||||
return tiaSound;
|
||||
}
|
||||
|
||||
public RAM6116 ReadRAM6116()
|
||||
{
|
||||
var ram6116 = new RAM6116(this);
|
||||
return ram6116;
|
||||
}
|
||||
|
||||
public InputState ReadInputState()
|
||||
{
|
||||
var inputState = new InputState(this);
|
||||
return inputState;
|
||||
}
|
||||
|
||||
public HSC7800 ReadOptionalHSC7800()
|
||||
{
|
||||
var exist = ReadBoolean();
|
||||
return exist ? new HSC7800(this) : null;
|
||||
}
|
||||
|
||||
public Bios7800 ReadOptionalBios7800()
|
||||
{
|
||||
var exist = ReadBoolean();
|
||||
return exist ? new Bios7800(this) : null;
|
||||
}
|
||||
|
||||
public PokeySound ReadOptionalPokeySound(MachineBase m)
|
||||
{
|
||||
var exist = ReadBoolean();
|
||||
return exist ? new PokeySound(this, m) : null;
|
||||
}
|
||||
|
||||
public Cart ReadCart(MachineBase m)
|
||||
{
|
||||
var typeName = _binaryReader.ReadString();
|
||||
if (string.IsNullOrWhiteSpace(typeName))
|
||||
throw new Emu7800SerializationException("Invalid type name.");
|
||||
|
||||
var type = Type.GetType(typeName);
|
||||
if (type == null)
|
||||
throw new Emu7800SerializationException("Unable to resolve type name: " + typeName);
|
||||
|
||||
return (Cart)Activator.CreateInstance(type, new object[] { this, m });
|
||||
}
|
||||
|
||||
#region Constructors
|
||||
|
||||
private DeserializationContext()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new instance of <see cref="DeserializationContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="binaryReader"/>
|
||||
internal DeserializationContext(BinaryReader binaryReader)
|
||||
{
|
||||
if (binaryReader == null)
|
||||
throw new ArgumentNullException("binaryReader");
|
||||
_binaryReader = binaryReader;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace EMU7800.Core
|
||||
{
|
||||
public class Emu7800Exception : Exception
|
||||
{
|
||||
internal Emu7800Exception()
|
||||
{
|
||||
}
|
||||
|
||||
internal Emu7800Exception(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
internal Emu7800Exception(string message, Exception ex) : base(message, ex)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace EMU7800.Core
|
||||
{
|
||||
public class Emu7800SerializationException : Emu7800Exception
|
||||
{
|
||||
private Emu7800SerializationException()
|
||||
{
|
||||
}
|
||||
|
||||
internal Emu7800SerializationException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
internal Emu7800SerializationException(string message, Exception ex) : base(message, ex)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,170 +0,0 @@
|
|||
/*
|
||||
* FontRenderer
|
||||
*
|
||||
* A simple font renderer for displaying text during emulation. Font data and
|
||||
* rendering algorithm courtesy of Bradford W. Mott's Stella source.
|
||||
*
|
||||
* Copyright © 2004 Mike Murphy
|
||||
*
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace EMU7800.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A simple font renderer for displaying text during emulation.
|
||||
/// </summary>
|
||||
public class FontRenderer
|
||||
{
|
||||
static readonly uint[] AlphaFontData =
|
||||
{
|
||||
0x699f999, // A
|
||||
0xe99e99e, // B
|
||||
0x6988896, // C
|
||||
0xe99999e, // D
|
||||
0xf88e88f, // E
|
||||
0xf88e888, // F
|
||||
0x698b996, // G
|
||||
0x999f999, // H
|
||||
0x7222227, // I
|
||||
0x72222a4, // J
|
||||
0x9accaa9, // K
|
||||
0x888888f, // L
|
||||
0x9ff9999, // M
|
||||
0x9ddbb99, // N
|
||||
0x6999996, // O
|
||||
0xe99e888, // P
|
||||
0x69999b7, // Q
|
||||
0xe99ea99, // R
|
||||
0x6986196, // S
|
||||
0x7222222, // T
|
||||
0x9999996, // U
|
||||
0x9999966, // V
|
||||
0x9999ff9, // W
|
||||
0x99fff99, // X
|
||||
0x9996244, // Y
|
||||
0xf12488f // Z
|
||||
};
|
||||
|
||||
static readonly uint[] DigitFontData =
|
||||
{
|
||||
0x69bd996, // 0
|
||||
0x2622227, // 1
|
||||
0x691248f, // 2
|
||||
0x6916196, // 3
|
||||
0xaaaf222, // 4
|
||||
0xf88e11e, // 5
|
||||
0x698e996, // 6
|
||||
0xf112244, // 7
|
||||
0x6996996, // 8
|
||||
0x6997196 // 9
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Draw specified text at specified position using the specified foreground and background colors.
|
||||
/// </summary>
|
||||
/// <param name="frameBuffer"></param>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="xoffset"></param>
|
||||
/// <param name="yoffset"></param>
|
||||
/// <param name="fore"></param>
|
||||
/// <param name="back"></param>
|
||||
/// <exception cref="ArgumentNullException">text must be non-null.</exception>
|
||||
public void DrawText(FrameBuffer frameBuffer, string text, int xoffset, int yoffset, byte fore, byte back)
|
||||
{
|
||||
if (text == null)
|
||||
throw new ArgumentNullException("text");
|
||||
|
||||
var textchars = text.ToUpper().ToCharArray();
|
||||
|
||||
for (var i = 0; i < text.Length + 1; i++)
|
||||
{
|
||||
for (var j = 0; j < 9; j++)
|
||||
{
|
||||
var pos = (j + yoffset) * frameBuffer.VisiblePitch + i * 5;
|
||||
for (var k = 0; k < 5; k++)
|
||||
{
|
||||
while (pos >= frameBuffer.VideoBufferByteLength)
|
||||
{
|
||||
pos -= frameBuffer.VideoBufferByteLength;
|
||||
}
|
||||
while (pos < 0)
|
||||
{
|
||||
pos += frameBuffer.VideoBufferByteLength;
|
||||
}
|
||||
frameBuffer.VideoBuffer[pos++] = back;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < text.Length; i++)
|
||||
{
|
||||
var c = textchars[i];
|
||||
uint fdata;
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '/':
|
||||
case '\\':
|
||||
fdata = 0x0122448;
|
||||
break;
|
||||
case '(':
|
||||
fdata = 0x2488842;
|
||||
break;
|
||||
case ')':
|
||||
fdata = 0x4211124;
|
||||
break;
|
||||
case '.':
|
||||
fdata = 0x0000066;
|
||||
break;
|
||||
case ':':
|
||||
fdata = 0x0660660;
|
||||
break;
|
||||
case '-':
|
||||
fdata = 0x0007000;
|
||||
break;
|
||||
default:
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
{
|
||||
fdata = AlphaFontData[c - 'A'];
|
||||
}
|
||||
else if (c >= '0' && c <= '9')
|
||||
{
|
||||
fdata = DigitFontData[c - '0'];
|
||||
}
|
||||
else
|
||||
{
|
||||
fdata = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
var ypos = 8;
|
||||
for (var j = 0; j < 32; j++)
|
||||
{
|
||||
var xpos = j & 3;
|
||||
if (xpos == 0)
|
||||
{
|
||||
ypos--;
|
||||
}
|
||||
|
||||
var pos = (ypos + yoffset) * frameBuffer.VisiblePitch + (4 - xpos) + xoffset;
|
||||
while (pos >= frameBuffer.VideoBufferByteLength)
|
||||
{
|
||||
pos -= frameBuffer.VideoBufferByteLength;
|
||||
}
|
||||
while (pos < 0)
|
||||
{
|
||||
pos += frameBuffer.VideoBufferByteLength;
|
||||
}
|
||||
if (((fdata >> j) & 1) != 0)
|
||||
{
|
||||
frameBuffer.VideoBuffer[pos] = fore;
|
||||
}
|
||||
}
|
||||
xoffset += 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace EMU7800.Core
|
||||
{
|
||||
public class FrameBuffer
|
||||
{
|
||||
/// <summary>
|
||||
/// Number of visible pixels on a single horizontal line.
|
||||
/// </summary>
|
||||
public int VisiblePitch { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of <see cref="BufferElement"/>s that represent <c>VisiblePitch</c>.
|
||||
/// </summary>
|
||||
//public int VideoBufferElementVisiblePitch { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of visible scan lines.
|
||||
/// </summary>
|
||||
public int Scanlines { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of bytes contained by <c>VideoBuffer</c>.
|
||||
/// </summary>
|
||||
public int VideoBufferByteLength { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of <see cref="BufferElement"/>s contained by <c>VideoBuffer</c>
|
||||
/// </summary>
|
||||
//public int VideoBufferElementLength { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of bytes contained by <c>SoundBuffer</c>.
|
||||
/// </summary>
|
||||
public int SoundBufferByteLength { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of <see cref="BufferElement"/>s contained by <c>SoundBuffer</c>
|
||||
/// </summary>
|
||||
//public int SoundBufferElementLength { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The buffer containing computed pixel data.
|
||||
/// </summary>
|
||||
public byte[] VideoBuffer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The buffer containing computed PCM audio data.
|
||||
/// </summary>
|
||||
public byte[] SoundBuffer { get; private set; }
|
||||
|
||||
#region Constructors
|
||||
|
||||
private FrameBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
internal FrameBuffer(int visiblePitch, int scanLines)
|
||||
{
|
||||
if (visiblePitch < 0)
|
||||
throw new ArgumentException("visiblePitch must be non-negative.");
|
||||
if (scanLines < 0)
|
||||
throw new ArgumentException("scanLines must be non-negative.");
|
||||
|
||||
VisiblePitch = visiblePitch;
|
||||
//VideoBufferElementVisiblePitch = VisiblePitch >> BufferElement.SHIFT;
|
||||
Scanlines = scanLines;
|
||||
VideoBufferByteLength = VisiblePitch * Scanlines;
|
||||
//VideoBufferElementLength = VideoBufferElementVisiblePitch * Scanlines;
|
||||
SoundBufferByteLength = Scanlines << 1;
|
||||
//SoundBufferElementLength = SoundBufferByteLength >> BufferElement.SHIFT;
|
||||
|
||||
VideoBuffer = new byte[VideoBufferByteLength + 64];
|
||||
SoundBuffer = new byte[SoundBufferByteLength + 64];
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* HSC7800.cs
|
||||
*
|
||||
* The 7800 High Score cartridge--courtesy of Matthias <matthias@atari8bit.de>.
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
|
||||
namespace EMU7800.Core
|
||||
{
|
||||
public sealed class HSC7800 : IDevice
|
||||
{
|
||||
readonly byte[] ROM;
|
||||
readonly ushort Mask;
|
||||
|
||||
public static ushort Size { get; private set; }
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
}
|
||||
|
||||
public byte this[ushort addr]
|
||||
{
|
||||
get { return ROM[addr & Mask]; }
|
||||
set { }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public RAM6116 SRAM { get; private set; }
|
||||
|
||||
#region Constructors
|
||||
|
||||
private HSC7800()
|
||||
{
|
||||
}
|
||||
|
||||
public HSC7800(byte[] hscRom, byte[] ram)
|
||||
{
|
||||
if (hscRom == null)
|
||||
throw new ArgumentNullException("hscRom");
|
||||
if (ram == null)
|
||||
throw new ArgumentNullException("ram");
|
||||
if (hscRom.Length != 4096)
|
||||
throw new ArgumentException("ROM size not 4096", "hscRom");
|
||||
|
||||
ROM = hscRom;
|
||||
SRAM = new RAM6116(ram);
|
||||
|
||||
Size = Mask = (ushort)ROM.Length;
|
||||
Mask--;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public HSC7800(DeserializationContext input)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException("input");
|
||||
|
||||
input.CheckVersion(1);
|
||||
ROM = input.ReadExpectedBytes(4096);
|
||||
SRAM = input.ReadRAM6116();
|
||||
|
||||
Size = Mask = (ushort)ROM.Length;
|
||||
Mask--;
|
||||
}
|
||||
|
||||
public void GetObjectData(SerializationContext output)
|
||||
{
|
||||
if (output == null)
|
||||
throw new ArgumentNullException("output");
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(ROM);
|
||||
output.Write(SRAM);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
* IDevice.cs
|
||||
*
|
||||
* Defines interface for devices accessable via the AddressSpace class.
|
||||
*
|
||||
* Copyright © 2003, 2011 Mike Murphy
|
||||
*
|
||||
*/
|
||||
namespace EMU7800.Core
|
||||
{
|
||||
public interface IDevice
|
||||
{
|
||||
void Reset();
|
||||
byte this[ushort addr] { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
public interface ILogger
|
||||
{
|
||||
void WriteLine(string format, params object[] args);
|
||||
void WriteLine(object value);
|
||||
void Write(string format, params object[] args);
|
||||
void Write(object value);
|
||||
}
|
||||
}
|
|
@ -1,398 +0,0 @@
|
|||
/*
|
||||
* InputState.cs
|
||||
*
|
||||
* Class containing the input state of the console and its controllers,
|
||||
* mapping emulator input devices to external input.
|
||||
*
|
||||
* Copyright © 2003-2010 Mike Murphy
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
|
||||
namespace EMU7800.Core
|
||||
{
|
||||
public class InputState
|
||||
{
|
||||
#region Fields
|
||||
|
||||
const int
|
||||
PaddleOhmMin = 100000,
|
||||
PaddleOhmMax = 800000;
|
||||
|
||||
const int
|
||||
LeftControllerJackIndex = 0,
|
||||
RightControllerJackIndex = 1,
|
||||
ConsoleSwitchIndex = 2,
|
||||
ControllerActionStateIndex = 3,
|
||||
OhmsIndex = ControllerActionStateIndex + 4,
|
||||
LightgunPositionIndex = ControllerActionStateIndex + 4,
|
||||
InputStateSize = ControllerActionStateIndex + 8 + 1;
|
||||
|
||||
// For driving controllers
|
||||
readonly byte[] _rotGrayCodes = new byte[] { 0x0f, 0x0d, 0x0c, 0x0e };
|
||||
readonly int[] _rotState = new int[2];
|
||||
|
||||
readonly int[] _nextInputState = new int[InputStateSize];
|
||||
readonly int[] _inputState = new int[InputStateSize];
|
||||
|
||||
bool _lagged = true;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Members
|
||||
|
||||
/// <summary>
|
||||
/// Enables the incoming input state buffer to be populated prior to the start of the frame.
|
||||
/// Useful for input playback senarios.
|
||||
/// </summary>
|
||||
/// <return>Return value is ignored.</return>
|
||||
public Func<int[], object> InputAdvancing { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables access to the input state buffer.
|
||||
/// Useful for input recording senarios.
|
||||
/// </summary>
|
||||
/// <return>Return value is ignored.</return>
|
||||
public Func<int[], object> InputAdvanced { get; set; }
|
||||
|
||||
public void CaptureInputState()
|
||||
{
|
||||
if (InputAdvancing != null)
|
||||
InputAdvancing(_nextInputState);
|
||||
Buffer.BlockCopy(_nextInputState, 0, _inputState, 0, InputStateSize * sizeof(int));
|
||||
if (InputAdvanced != null)
|
||||
InputAdvanced(_inputState);
|
||||
_lagged = true;
|
||||
}
|
||||
|
||||
public Controller LeftControllerJack
|
||||
{
|
||||
get { return (Controller)_nextInputState[LeftControllerJackIndex]; }
|
||||
set { _nextInputState[LeftControllerJackIndex] = (int)value; }
|
||||
}
|
||||
|
||||
public Controller RightControllerJack
|
||||
{
|
||||
get { return (Controller)_nextInputState[RightControllerJackIndex]; }
|
||||
set { _nextInputState[RightControllerJackIndex] = (int)value; }
|
||||
}
|
||||
|
||||
public bool IsGameBWConsoleSwitchSet
|
||||
{
|
||||
get { return (_nextInputState[ConsoleSwitchIndex] & (1 << (int) ConsoleSwitch.GameBW)) != 0; }
|
||||
}
|
||||
|
||||
public bool IsLeftDifficultyAConsoleSwitchSet
|
||||
{
|
||||
get { return (_nextInputState[ConsoleSwitchIndex] & (1 << (int)ConsoleSwitch.LeftDifficultyA)) != 0; }
|
||||
}
|
||||
|
||||
public bool IsRightDifficultyAConsoleSwitchSet
|
||||
{
|
||||
get { return (_nextInputState[ConsoleSwitchIndex] & (1 << (int)ConsoleSwitch.RightDifficultyA)) != 0; }
|
||||
}
|
||||
|
||||
public bool Lagged
|
||||
{
|
||||
get { return _lagged; }
|
||||
}
|
||||
|
||||
public Action InputPollCallback { get; set; }
|
||||
|
||||
public void RaiseInput(int playerNo, MachineInput input, bool down)
|
||||
{
|
||||
if (InputPollCallback != null)
|
||||
{
|
||||
InputPollCallback();
|
||||
}
|
||||
switch (input)
|
||||
{
|
||||
case MachineInput.Fire:
|
||||
SetControllerActionState(playerNo, ControllerAction.Trigger, down);
|
||||
break;
|
||||
case MachineInput.Fire2:
|
||||
SetControllerActionState(playerNo, ControllerAction.Trigger2, down);
|
||||
break;
|
||||
case MachineInput.Left:
|
||||
SetControllerActionState(playerNo, ControllerAction.Left, down);
|
||||
if (down) SetControllerActionState(playerNo, ControllerAction.Right, false);
|
||||
break;
|
||||
case MachineInput.Up:
|
||||
SetControllerActionState(playerNo, ControllerAction.Up, down);
|
||||
if (down) SetControllerActionState(playerNo, ControllerAction.Down, false);
|
||||
break;
|
||||
case MachineInput.Right:
|
||||
SetControllerActionState(playerNo, ControllerAction.Right, down);
|
||||
if (down) SetControllerActionState(playerNo, ControllerAction.Left, false);
|
||||
break;
|
||||
case MachineInput.Down:
|
||||
SetControllerActionState(playerNo, ControllerAction.Down, down);
|
||||
if (down) SetControllerActionState(playerNo, ControllerAction.Up, false);
|
||||
break;
|
||||
case MachineInput.NumPad7:
|
||||
SetControllerActionState(playerNo, ControllerAction.Keypad7, down);
|
||||
break;
|
||||
case MachineInput.NumPad8:
|
||||
SetControllerActionState(playerNo, ControllerAction.Keypad8, down);
|
||||
break;
|
||||
case MachineInput.NumPad9:
|
||||
SetControllerActionState(playerNo, ControllerAction.Keypad9, down);
|
||||
break;
|
||||
case MachineInput.NumPad4:
|
||||
SetControllerActionState(playerNo, ControllerAction.Keypad4, down);
|
||||
break;
|
||||
case MachineInput.NumPad5:
|
||||
SetControllerActionState(playerNo, ControllerAction.Keypad5, down);
|
||||
break;
|
||||
case MachineInput.NumPad6:
|
||||
SetControllerActionState(playerNo, ControllerAction.Keypad6, down);
|
||||
break;
|
||||
case MachineInput.NumPad1:
|
||||
SetControllerActionState(playerNo, ControllerAction.Keypad1, down);
|
||||
break;
|
||||
case MachineInput.NumPad2:
|
||||
SetControllerActionState(playerNo, ControllerAction.Keypad2, down);
|
||||
break;
|
||||
case MachineInput.NumPad3:
|
||||
SetControllerActionState(playerNo, ControllerAction.Keypad3, down);
|
||||
break;
|
||||
case MachineInput.NumPadMult:
|
||||
SetControllerActionState(playerNo, ControllerAction.KeypadA, down);
|
||||
break;
|
||||
case MachineInput.NumPad0:
|
||||
SetControllerActionState(playerNo, ControllerAction.Keypad0, down);
|
||||
break;
|
||||
case MachineInput.NumPadHash:
|
||||
SetControllerActionState(playerNo, ControllerAction.KeypadP, down);
|
||||
break;
|
||||
case MachineInput.Driving0:
|
||||
SetControllerActionState(playerNo, ControllerAction.Driving0, true);
|
||||
SetControllerActionState(playerNo, ControllerAction.Driving1, false);
|
||||
SetControllerActionState(playerNo, ControllerAction.Driving2, false);
|
||||
SetControllerActionState(playerNo, ControllerAction.Driving3, false);
|
||||
break;
|
||||
case MachineInput.Driving1:
|
||||
SetControllerActionState(playerNo, ControllerAction.Driving0, false);
|
||||
SetControllerActionState(playerNo, ControllerAction.Driving1, true);
|
||||
SetControllerActionState(playerNo, ControllerAction.Driving2, false);
|
||||
SetControllerActionState(playerNo, ControllerAction.Driving3, false);
|
||||
break;
|
||||
case MachineInput.Driving2:
|
||||
SetControllerActionState(playerNo, ControllerAction.Driving0, false);
|
||||
SetControllerActionState(playerNo, ControllerAction.Driving1, false);
|
||||
SetControllerActionState(playerNo, ControllerAction.Driving2, true);
|
||||
SetControllerActionState(playerNo, ControllerAction.Driving3, false);
|
||||
break;
|
||||
case MachineInput.Driving3:
|
||||
SetControllerActionState(playerNo, ControllerAction.Driving0, false);
|
||||
SetControllerActionState(playerNo, ControllerAction.Driving1, false);
|
||||
SetControllerActionState(playerNo, ControllerAction.Driving2, false);
|
||||
SetControllerActionState(playerNo, ControllerAction.Driving3, true);
|
||||
break;
|
||||
case MachineInput.Reset:
|
||||
SetConsoleSwitchState(ConsoleSwitch.GameReset, down);
|
||||
break;
|
||||
case MachineInput.Select:
|
||||
SetConsoleSwitchState(ConsoleSwitch.GameSelect, down);
|
||||
break;
|
||||
case MachineInput.Color:
|
||||
if (down) ToggleConsoleSwitchState(ConsoleSwitch.GameBW);
|
||||
break;
|
||||
case MachineInput.LeftDifficulty:
|
||||
if (down) ToggleConsoleSwitchState(ConsoleSwitch.LeftDifficultyA);
|
||||
break;
|
||||
case MachineInput.RightDifficulty:
|
||||
if (down) ToggleConsoleSwitchState(ConsoleSwitch.RightDifficultyA);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void RaisePaddleInput(int playerNo, int valMax, int val)
|
||||
{
|
||||
var ohms = PaddleOhmMax - (PaddleOhmMax - PaddleOhmMin) / valMax * val;
|
||||
_nextInputState[OhmsIndex + (playerNo & 3)] = ohms;
|
||||
}
|
||||
|
||||
public void RaiseLightgunPos(int playerNo, int scanline, int hpos)
|
||||
{
|
||||
var i = LightgunPositionIndex + ((playerNo & 1) << 1);
|
||||
_nextInputState[i++] = scanline;
|
||||
_nextInputState[i] = hpos;
|
||||
}
|
||||
|
||||
public void ClearAllInput()
|
||||
{
|
||||
_nextInputState[ConsoleSwitchIndex] = 0;
|
||||
ClearLeftJackInput();
|
||||
ClearRightJackInput();
|
||||
}
|
||||
|
||||
// For Bizhawk
|
||||
// Emu7800's client does not call Clear input every frame so console switches behave like switches
|
||||
// Bizhawk needs to call a clear input function every frame, if we put switches in there, they would behave like buttons
|
||||
public void ClearControllerInput()
|
||||
{
|
||||
ClearLeftJackInput();
|
||||
ClearRightJackInput();
|
||||
}
|
||||
|
||||
public void ClearInputByPlayer(int playerNo)
|
||||
{
|
||||
_nextInputState[OhmsIndex + (playerNo & 3)] = 0;
|
||||
_nextInputState[ControllerActionStateIndex + (playerNo & 3)] = 0;
|
||||
_nextInputState[LightgunPositionIndex + ((playerNo & 1) << 1)] = _nextInputState[LightgunPositionIndex + ((playerNo & 1) << 1) + 1] = 0;
|
||||
}
|
||||
|
||||
public void ClearLeftJackInput()
|
||||
{
|
||||
_nextInputState[OhmsIndex] = _nextInputState[OhmsIndex + 1] = 0;
|
||||
_nextInputState[ControllerActionStateIndex] = 0;
|
||||
switch (LeftControllerJack)
|
||||
{
|
||||
case Controller.Paddles:
|
||||
_nextInputState[ControllerActionStateIndex] = _nextInputState[ControllerActionStateIndex + 1] = 0;
|
||||
break;
|
||||
default:
|
||||
_nextInputState[ControllerActionStateIndex] = 0;
|
||||
break;
|
||||
}
|
||||
_nextInputState[LightgunPositionIndex] = _nextInputState[LightgunPositionIndex + 1] = 0;
|
||||
}
|
||||
|
||||
public void ClearRightJackInput()
|
||||
{
|
||||
_nextInputState[OhmsIndex + 2] = _nextInputState[OhmsIndex + 3] = 0;
|
||||
switch (RightControllerJack)
|
||||
{
|
||||
case Controller.Paddles:
|
||||
_nextInputState[ControllerActionStateIndex + 2] = _nextInputState[ControllerActionStateIndex + 3] = 0;
|
||||
break;
|
||||
default:
|
||||
_nextInputState[ControllerActionStateIndex + 1] = 0;
|
||||
break;
|
||||
}
|
||||
_nextInputState[LightgunPositionIndex + 2] = _nextInputState[LightgunPositionIndex + 3] = 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public InputState()
|
||||
{
|
||||
}
|
||||
|
||||
public InputState(DeserializationContext input)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException("input");
|
||||
|
||||
input.CheckVersion(2);
|
||||
_rotState = input.ReadIntegers(2);
|
||||
_nextInputState = input.ReadIntegers(InputStateSize);
|
||||
_inputState = input.ReadIntegers(InputStateSize);
|
||||
_lagged = input.ReadBoolean();
|
||||
}
|
||||
|
||||
public void GetObjectData(SerializationContext output)
|
||||
{
|
||||
if (output == null)
|
||||
throw new ArgumentNullException("output");
|
||||
|
||||
output.WriteVersion(2);
|
||||
output.Write(_rotState);
|
||||
output.Write(_nextInputState);
|
||||
output.Write(_inputState);
|
||||
output.Write(_lagged);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Members
|
||||
|
||||
internal bool SampleCapturedConsoleSwitchState(ConsoleSwitch consoleSwitch)
|
||||
{
|
||||
_lagged = false;
|
||||
return (_inputState[ConsoleSwitchIndex] & (1 << (int)consoleSwitch)) != 0;
|
||||
}
|
||||
|
||||
internal bool SampleCapturedControllerActionState(int playerno, ControllerAction action)
|
||||
{
|
||||
_lagged = false;
|
||||
return (_inputState[ControllerActionStateIndex + (playerno & 3)] & (1 << (int)action)) != 0;
|
||||
}
|
||||
|
||||
internal int SampleCapturedOhmState(int playerNo)
|
||||
{
|
||||
_lagged = false;
|
||||
return _inputState[OhmsIndex + (playerNo & 3)];
|
||||
}
|
||||
|
||||
internal void SampleCapturedLightGunPosition(int playerNo, out int scanline, out int hpos)
|
||||
{
|
||||
_lagged = false;
|
||||
var i = LightgunPositionIndex + ((playerNo & 1) << 1);
|
||||
scanline = _inputState[i++];
|
||||
hpos = _inputState[i];
|
||||
}
|
||||
|
||||
internal byte SampleCapturedDrivingState(int playerNo)
|
||||
{
|
||||
_lagged = false;
|
||||
if (SampleCapturedControllerActionState(playerNo, ControllerAction.Driving0))
|
||||
_rotState[playerNo] = 0;
|
||||
else if (SampleCapturedControllerActionState(playerNo, ControllerAction.Driving1))
|
||||
_rotState[playerNo] = 1;
|
||||
else if (SampleCapturedControllerActionState(playerNo, ControllerAction.Driving2))
|
||||
_rotState[playerNo] = 2;
|
||||
else if (SampleCapturedControllerActionState(playerNo, ControllerAction.Driving3))
|
||||
_rotState[playerNo] = 3;
|
||||
return _rotGrayCodes[_rotState[playerNo]];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Object Overrides
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return GetType().Name;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
void SetControllerActionState(int playerNo, ControllerAction action, bool value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
_nextInputState[ControllerActionStateIndex + (playerNo & 3)] |= (1 << (int)action);
|
||||
}
|
||||
else
|
||||
{
|
||||
_nextInputState[ControllerActionStateIndex + (playerNo & 3)] &= ~(1 << (int)action);
|
||||
}
|
||||
}
|
||||
|
||||
void SetConsoleSwitchState(ConsoleSwitch consoleSwitch, bool value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
_nextInputState[ConsoleSwitchIndex] |= (byte)(1 << (byte)consoleSwitch);
|
||||
}
|
||||
else
|
||||
{
|
||||
_nextInputState[ConsoleSwitchIndex] &= (byte)~(1 << (byte)consoleSwitch);
|
||||
}
|
||||
}
|
||||
|
||||
void ToggleConsoleSwitchState(ConsoleSwitch consoleSwitch)
|
||||
{
|
||||
var consoleSwitchState = (_nextInputState[ConsoleSwitchIndex] & (1 << (int) consoleSwitch)) != 0;
|
||||
SetConsoleSwitchState(consoleSwitch, !consoleSwitchState);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,339 +0,0 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Appendix: How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) 19yy <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) 19yy name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
File diff suppressed because it is too large
Load Diff
|
@ -1,242 +0,0 @@
|
|||
/*
|
||||
* M6502DASM.cs
|
||||
*
|
||||
* Provides disassembly services.
|
||||
*
|
||||
* Copyright © 2003, 2004 Mike Murphy
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace EMU7800.Core
|
||||
{
|
||||
public static class M6502DASM
|
||||
{
|
||||
// Instruction Mnemonics
|
||||
enum m : uint
|
||||
{
|
||||
ADC = 1, AND, ASL,
|
||||
BIT, BCC, BCS, BEQ, BMI, BNE, BPL, BRK, BVC, BVS,
|
||||
CLC, CLD, CLI, CLV, CMP, CPX, CPY,
|
||||
DEC, DEX, DEY,
|
||||
EOR,
|
||||
INC, INX, INY,
|
||||
JMP, JSR,
|
||||
LDA, LDX, LDY, LSR,
|
||||
NOP,
|
||||
ORA,
|
||||
PLA, PLP, PHA, PHP,
|
||||
ROL, ROR, RTI, RTS,
|
||||
SEC, SEI, STA, SBC, SED, STX, STY,
|
||||
TAX, TAY, TSX, TXA, TXS, TYA,
|
||||
|
||||
// Illegal/undefined opcodes
|
||||
isb,
|
||||
kil,
|
||||
lax,
|
||||
rla,
|
||||
sax,
|
||||
top
|
||||
}
|
||||
|
||||
// Addressing Modes
|
||||
enum a : uint
|
||||
{
|
||||
REL, // Relative: $aa (branch instructions only)
|
||||
ZPG, // Zero Page: $aa
|
||||
ZPX, // Zero Page Indexed X: $aa,X
|
||||
ZPY, // Zero Page Indexed Y: $aa,Y
|
||||
ABS, // Absolute: $aaaa
|
||||
ABX, // Absolute Indexed X: $aaaa,X
|
||||
ABY, // Absolute Indexed Y: $aaaa,Y
|
||||
IDX, // Indexed Indirect: ($aa,X)
|
||||
IDY, // Indirect Indexed: ($aa),Y
|
||||
IND, // Indirect Absolute: ($aaaa) (JMP only)
|
||||
IMM, // Immediate: #aa
|
||||
IMP, // Implied
|
||||
ACC // Accumulator
|
||||
}
|
||||
|
||||
static readonly m[] MnemonicMatrix = {
|
||||
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
/*0*/ m.BRK, m.ORA, m.kil, 0, 0, m.ORA, m.ASL, 0, m.PHP, m.ORA, m.ASL, 0, m.top, m.ORA, m.ASL, 0,/*0*/
|
||||
/*1*/ m.BPL, m.ORA, m.kil, 0, 0, m.ORA, m.ASL, 0, m.CLC, m.ORA, 0, 0, m.top, m.ORA, m.ASL, 0,/*1*/
|
||||
/*2*/ m.JSR, m.AND, m.kil, 0, m.BIT, m.AND, m.ROL, 0, m.PLP, m.AND, m.ROL, 0, m.BIT, m.AND, m.ROL, 0,/*2*/
|
||||
/*3*/ m.BMI, m.AND, m.kil, 0, 0, m.AND, m.ROL, 0, m.SEC, m.AND, 0, 0, m.top, m.AND, m.ROL, m.rla,/*3*/
|
||||
/*4*/ m.RTI, m.EOR, m.kil, 0, 0, m.EOR, m.LSR, 0, m.PHA, m.EOR, m.LSR, 0, m.JMP, m.EOR, m.LSR, 0,/*4*/
|
||||
/*5*/ m.BVC, m.EOR, m.kil, 0, 0, m.EOR, m.LSR, 0, m.CLI, m.EOR, 0, 0, m.top, m.EOR, m.LSR, 0,/*5*/
|
||||
/*6*/ m.RTS, m.ADC, m.kil, 0, 0, m.ADC, m.ROR, 0, m.PLA, m.ADC, m.ROR, 0, m.JMP, m.ADC, m.ROR, 0,/*6*/
|
||||
/*7*/ m.BVS, m.ADC, m.kil, 0, 0, m.ADC, m.ROR, 0, m.SEI, m.ADC, 0, 0, m.top, m.ADC, m.ROR, 0,/*7*/
|
||||
/*8*/ 0, m.STA, 0, m.sax, m.STY, m.STA, m.STX, m.sax, m.DEY, 0, m.TXA, 0, m.STY, m.STA, m.STX, m.sax,/*8*/
|
||||
/*9*/ m.BCC, m.STA, m.kil, 0, m.STY, m.STA, m.STX, m.sax, m.TYA, m.STA, m.TXS, 0, m.top, m.STA, 0, 0,/*9*/
|
||||
/*A*/ m.LDY, m.LDA, m.LDX, m.lax, m.LDY, m.LDA, m.LDX, m.lax, m.TAY, m.LDA, m.TAX, 0, m.LDY, m.LDA, m.LDX, m.lax,/*A*/
|
||||
/*B*/ m.BCS, m.LDA, m.kil, m.lax, m.LDY, m.LDA, m.LDX, m.lax, m.CLV, m.LDA, m.TSX, 0, m.LDY, m.LDA, m.LDX, m.lax,/*B*/
|
||||
/*C*/ m.CPY, m.CMP, 0, 0, m.CPY, m.CMP, m.DEC, 0, m.INY, m.CMP, m.DEX, 0, m.CPY, m.CMP, m.DEC, 0,/*C*/
|
||||
/*D*/ m.BNE, m.CMP, m.kil, 0, 0, m.CMP, m.DEC, 0, m.CLD, m.CMP, 0, 0, m.top, m.CMP, m.DEC, 0,/*D*/
|
||||
/*E*/ m.CPX, m.SBC, 0, 0, m.CPX, m.SBC, m.INC, 0, m.INX, m.SBC, m.NOP, 0, m.CPX, m.SBC, m.INC, m.isb,/*E*/
|
||||
/*F*/ m.BEQ, m.SBC, m.kil, 0, 0, m.SBC, m.INC, 0, m.SED, m.SBC, 0, 0, m.top, m.SBC, m.INC, m.isb /*F*/
|
||||
};
|
||||
|
||||
static readonly a[] AddressingModeMatrix = {
|
||||
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
/*0*/ a.IMP, a.IDX, a.IMP, 0, 0, a.ZPG, a.ZPG, 0, a.IMP, a.IMM, a.ACC, 0, a.ABS, a.ABS, a.ABS, 0,/*0*/
|
||||
/*1*/ a.REL, a.IDY, a.IMP, 0, 0, a.ZPG, a.ZPG, 0, a.IMP, a.ABY, 0, 0, a.ABS, a.ABX, a.ABX, 0,/*1*/
|
||||
/*2*/ a.ABS, a.IDX, a.IMP, 0, a.ZPG, a.ZPG, a.ZPG, 0, a.IMP, a.IMM, a.ACC, 0, a.ABS, a.ABS, a.ABS, 0,/*2*/
|
||||
/*3*/ a.REL, a.IDY, a.IMP, 0, 0, a.ZPG, a.ZPG, 0, a.IMP, a.ABY, 0, 0, a.ABS, a.ABX, a.ABX, a.ABX,/*3*/
|
||||
/*4*/ a.IMP, a.IDY, a.IMP, 0, 0, a.ZPG, a.ZPG, 0, a.IMP, a.IMM, a.ACC, 0, a.ABS, a.ABS, a.ABS, 0,/*4*/
|
||||
/*5*/ a.REL, a.IDY, a.IMP, 0, 0, a.ZPG, a.ZPG, 0, a.IMP, a.ABY, 0, 0, a.ABS, a.ABX, a.ABX, 0,/*5*/
|
||||
/*6*/ a.IMP, a.IDX, a.IMP, 0, 0, a.ZPG, a.ZPG, 0, a.IMP, a.IMM, a.ACC, 0, a.IND, a.ABS, a.ABS, 0,/*6*/
|
||||
/*7*/ a.REL, a.IDY, a.IMP, 0, 0, a.ZPX, a.ZPX, 0, a.IMP, a.ABY, 0, 0, a.ABS, a.ABX, a.ABX, 0,/*7*/
|
||||
/*8*/ 0, a.IDY, 0, a.IDX, a.ZPG, a.ZPG, a.ZPG, a.ZPG, a.IMP, 0, a.IMP, 0, a.ABS, a.ABS, a.ABS, a.ABS,/*8*/
|
||||
/*9*/ a.REL, a.IDY, a.IMP, 0, a.ZPX, a.ZPX, a.ZPY, a.ZPY, a.IMP, a.ABY, a.IMP, 0, a.ABS, a.ABX, 0, 0,/*9*/
|
||||
/*A*/ a.IMM, a.IND, a.IMM, a.IDX, a.ZPG, a.ZPG, a.ZPG, a.ZPX, a.IMP, a.IMM, a.IMP, 0, a.ABS, a.ABS, a.ABS, a.ABS,/*A*/
|
||||
/*B*/ a.REL, a.IDY, a.IMP, a.IDY, a.ZPX, a.ZPX, a.ZPY, a.ZPY, a.IMP, a.ABY, a.IMP, 0, a.ABX, a.ABX, a.ABY, a.ABY,/*B*/
|
||||
/*C*/ a.IMM, a.IDX, 0, 0, a.ZPG, a.ZPG, a.ZPG, 0, a.IMP, a.IMM, a.IMP, 0, a.ABS, a.ABS, a.ABS, 0,/*C*/
|
||||
/*D*/ a.REL, a.IDY, a.IMP, 0, 0, a.ZPX, a.ZPX, 0, a.IMP, a.ABY, 0, 0, a.ABS, a.ABX, a.ABX, 0,/*D*/
|
||||
/*E*/ a.IMM, a.IDX, 0, 0, a.ZPG, a.ZPG, a.ZPG, 0, a.IMP, a.IMM, a.IMP, 0, a.ABS, a.ABS, a.ABS, a.ABS,/*E*/
|
||||
/*F*/ a.REL, a.IDY, a.IMP, 0, 0, a.ZPX, a.ZPX, 0, a.IMP, a.ABY, 0, 0, a.ABS, a.ABX, a.ABX, a.ABX /*F*/
|
||||
};
|
||||
|
||||
public static string GetRegisters(M6502 cpu)
|
||||
{
|
||||
var dSB = new StringBuilder();
|
||||
dSB.Append(String.Format(
|
||||
"PC:{0:x4} A:{1:x2} X:{2:x2} Y:{3:x2} S:{4:x2} P:",
|
||||
cpu.PC, cpu.A, cpu.X, cpu.Y, cpu.S));
|
||||
|
||||
const string flags = "nv0bdizcNV1BDIZC";
|
||||
|
||||
for (var i = 0; i < 8; i++)
|
||||
{
|
||||
dSB.Append(((cpu.P & (1 << (7 - i))) == 0) ? flags[i] : flags[i + 8]);
|
||||
}
|
||||
return dSB.ToString();
|
||||
}
|
||||
|
||||
public static string Disassemble(AddressSpace addrSpace, ushort atAddr, ushort untilAddr)
|
||||
{
|
||||
var dSB = new StringBuilder();
|
||||
var dPC = atAddr;
|
||||
while (atAddr < untilAddr)
|
||||
{
|
||||
dSB.AppendFormat("{0:x4}: ", dPC);
|
||||
var len = GetInstructionLength(addrSpace, dPC);
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
if (i < len)
|
||||
{
|
||||
dSB.AppendFormat("{0:x2} ", addrSpace[atAddr++]);
|
||||
}
|
||||
else
|
||||
{
|
||||
dSB.Append(" ");
|
||||
}
|
||||
}
|
||||
dSB.AppendFormat("{0,-15}{1}", RenderOpCode(addrSpace, dPC), Environment.NewLine);
|
||||
dPC += (ushort)len;
|
||||
}
|
||||
if (dSB.Length > 0)
|
||||
{
|
||||
dSB.Length--; // Trim trailing newline
|
||||
}
|
||||
return dSB.ToString();
|
||||
}
|
||||
|
||||
public static string MemDump(AddressSpace addrSpace, ushort atAddr, ushort untilAddr)
|
||||
{
|
||||
var dSB = new StringBuilder();
|
||||
var len = untilAddr - atAddr;
|
||||
while (len-- >= 0)
|
||||
{
|
||||
dSB.AppendFormat("{0:x4}: ", atAddr);
|
||||
for (var i = 0; i < 8; i++)
|
||||
{
|
||||
dSB.AppendFormat("{0:x2} ", addrSpace[atAddr++]);
|
||||
if (i == 3)
|
||||
{
|
||||
dSB.Append(" ");
|
||||
}
|
||||
}
|
||||
dSB.Append("\n");
|
||||
}
|
||||
if (dSB.Length > 0)
|
||||
{
|
||||
dSB.Length--; // Trim trailing newline
|
||||
}
|
||||
return dSB.ToString();
|
||||
}
|
||||
|
||||
public static string RenderOpCode(AddressSpace addrSpace, ushort PC)
|
||||
{
|
||||
var num_operands = GetInstructionLength(addrSpace, PC) - 1;
|
||||
var PC1 = (ushort)(PC + 1);
|
||||
string addrmodeStr;
|
||||
|
||||
switch (AddressingModeMatrix[addrSpace[PC]])
|
||||
{
|
||||
case a.REL:
|
||||
addrmodeStr = String.Format("${0:x4}", (ushort)(PC + (sbyte)(addrSpace[PC1]) + 2));
|
||||
break;
|
||||
case a.ZPG:
|
||||
case a.ABS:
|
||||
addrmodeStr = RenderEA(addrSpace, PC1, num_operands);
|
||||
break;
|
||||
case a.ZPX:
|
||||
case a.ABX:
|
||||
addrmodeStr = RenderEA(addrSpace, PC1, num_operands) + ",X";
|
||||
break;
|
||||
case a.ZPY:
|
||||
case a.ABY:
|
||||
addrmodeStr = RenderEA(addrSpace, PC1, num_operands) + ",Y";
|
||||
break;
|
||||
case a.IDX:
|
||||
addrmodeStr = "(" + RenderEA(addrSpace, PC1, num_operands) + ",X)";
|
||||
break;
|
||||
case a.IDY:
|
||||
addrmodeStr = "(" + RenderEA(addrSpace, PC1, num_operands) + "),Y";
|
||||
break;
|
||||
case a.IND:
|
||||
addrmodeStr = "(" + RenderEA(addrSpace, PC1, num_operands) + ")";
|
||||
break;
|
||||
case a.IMM:
|
||||
addrmodeStr = "#" + RenderEA(addrSpace, PC1, num_operands);
|
||||
break;
|
||||
default:
|
||||
// a.IMP, a.ACC
|
||||
addrmodeStr = string.Empty;
|
||||
break;
|
||||
}
|
||||
|
||||
return string.Format("{0} {1}", MnemonicMatrix[addrSpace[PC]], addrmodeStr);
|
||||
}
|
||||
|
||||
static int GetInstructionLength(AddressSpace addrSpace, ushort PC)
|
||||
{
|
||||
switch (AddressingModeMatrix[addrSpace[PC]])
|
||||
{
|
||||
case a.ACC:
|
||||
case a.IMP:
|
||||
return 1;
|
||||
case a.REL:
|
||||
case a.ZPG:
|
||||
case a.ZPX:
|
||||
case a.ZPY:
|
||||
case a.IDX:
|
||||
case a.IDY:
|
||||
case a.IMM:
|
||||
return 2;
|
||||
default:
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
static string RenderEA(AddressSpace addrSpace, ushort PC, int bytes)
|
||||
{
|
||||
var lsb = addrSpace[PC];
|
||||
var msb = (bytes == 2) ? addrSpace[(ushort)(PC + 1)] : (byte)0;
|
||||
var ea = (ushort)(lsb | (msb << 8));
|
||||
return string.Format((bytes == 1) ? "${0:x2}" : "${0:x4}", ea);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
/*
|
||||
* Machine2600.cs
|
||||
*
|
||||
* The realization of a 2600 machine.
|
||||
*
|
||||
* Copyright © 2003, 2004 Mike Murphy
|
||||
*
|
||||
*/
|
||||
namespace EMU7800.Core
|
||||
{
|
||||
public class Machine2600 : MachineBase
|
||||
{
|
||||
#region Fields
|
||||
|
||||
protected TIA TIA { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
TIA.Reset();
|
||||
PIA.Reset();
|
||||
CPU.Reset();
|
||||
}
|
||||
|
||||
public override void ComputeNextFrame(FrameBuffer frameBuffer)
|
||||
{
|
||||
base.ComputeNextFrame(frameBuffer);
|
||||
TIA.StartFrame();
|
||||
CPU.RunClocks = (FrameBuffer.Scanlines + 3) * 76;
|
||||
while (CPU.RunClocks > 0 && !CPU.Jammed)
|
||||
{
|
||||
if (TIA.WSYNCDelayClocks > 0)
|
||||
{
|
||||
CPU.Clock += (ulong)TIA.WSYNCDelayClocks / 3;
|
||||
CPU.RunClocks -= TIA.WSYNCDelayClocks / 3;
|
||||
TIA.WSYNCDelayClocks = 0;
|
||||
}
|
||||
if (TIA.EndOfFrame)
|
||||
{
|
||||
break;
|
||||
}
|
||||
CPU.Execute();
|
||||
}
|
||||
TIA.EndFrame();
|
||||
}
|
||||
|
||||
public Machine2600(Cart cart, ILogger logger, int slines, int startl, int fHZ, int sRate, int[] p)
|
||||
: base(logger, slines, startl, fHZ, sRate, p, 160)
|
||||
{
|
||||
Mem = new AddressSpace(this, 13, 6); // 2600: 13bit, 64byte pages
|
||||
|
||||
CPU = new M6502(this, 1);
|
||||
|
||||
TIA = new TIA(this);
|
||||
for (ushort i = 0; i < 0x1000; i += 0x100)
|
||||
{
|
||||
Mem.Map(i, 0x0080, TIA);
|
||||
}
|
||||
|
||||
PIA = new PIA(this);
|
||||
for (ushort i = 0x0080; i < 0x1000; i += 0x100)
|
||||
{
|
||||
Mem.Map(i, 0x0080, PIA);
|
||||
}
|
||||
|
||||
Cart = cart;
|
||||
Mem.Map(0x1000, 0x1000, Cart);
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public Machine2600(DeserializationContext input, int[] palette) : base(input, palette)
|
||||
{
|
||||
input.CheckVersion(1);
|
||||
|
||||
Mem = input.ReadAddressSpace(this, 13, 6); // 2600: 13bit, 64byte pages
|
||||
|
||||
CPU = input.ReadM6502(this, 1);
|
||||
|
||||
TIA = input.ReadTIA(this);
|
||||
for (ushort i = 0; i < 0x1000; i += 0x100)
|
||||
{
|
||||
Mem.Map(i, 0x0080, TIA);
|
||||
}
|
||||
|
||||
PIA = input.ReadPIA(this);
|
||||
for (ushort i = 0x0080; i < 0x1000; i += 0x100)
|
||||
{
|
||||
Mem.Map(i, 0x0080, PIA);
|
||||
}
|
||||
|
||||
Cart = input.ReadCart(this);
|
||||
Mem.Map(0x1000, 0x1000, Cart);
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(Mem);
|
||||
output.Write(CPU);
|
||||
output.Write(TIA);
|
||||
output.Write(PIA);
|
||||
output.Write(Cart);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
public sealed class Machine2600NTSC : Machine2600
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
return GetType().Name;
|
||||
}
|
||||
|
||||
public Machine2600NTSC(Cart cart, ILogger logger)
|
||||
: base(cart, logger, 262, 16, 60, 31440 /* NTSC_SAMPLES_PER_SEC */, TIATables.NTSCPalette)
|
||||
{
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public Machine2600NTSC(DeserializationContext input) : base(input, TIATables.NTSCPalette)
|
||||
{
|
||||
input.CheckVersion(1);
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
output.WriteVersion(1);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
public sealed class Machine2600PAL : Machine2600
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
return GetType().Name;
|
||||
}
|
||||
|
||||
public Machine2600PAL(Cart cart, ILogger logger)
|
||||
: base(cart, logger, 312, 32, 50, 31200 /* PAL_SAMPLES_PER_SEC */, TIATables.PALPalette)
|
||||
{
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public Machine2600PAL(DeserializationContext input) : base(input, TIATables.PALPalette)
|
||||
{
|
||||
input.CheckVersion(1);
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
output.WriteVersion(1);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,255 +0,0 @@
|
|||
/*
|
||||
* Machine7800.cs
|
||||
*
|
||||
* The realization of a 7800 machine.
|
||||
*
|
||||
* Copyright © 2003-2005 Mike Murphy
|
||||
*
|
||||
*/
|
||||
namespace EMU7800.Core
|
||||
{
|
||||
public class Machine7800 : MachineBase
|
||||
{
|
||||
#region Fields
|
||||
|
||||
protected Maria Maria { get; set; }
|
||||
public RAM6116 RAM1 { get; protected set; }
|
||||
public RAM6116 RAM2 { get; protected set; }
|
||||
public Bios7800 BIOS { get; private set; }
|
||||
public HSC7800 HSC { get; private set; }
|
||||
public bool using_bios=true;
|
||||
|
||||
#endregion
|
||||
|
||||
public void SwapInBIOS()
|
||||
{
|
||||
if (BIOS == null)
|
||||
return;
|
||||
Mem.Map((ushort)(0x10000 - BIOS.Size), BIOS.Size, BIOS);
|
||||
using_bios = true;
|
||||
}
|
||||
|
||||
public void SwapOutBIOS()
|
||||
{
|
||||
if (BIOS == null)
|
||||
return;
|
||||
Mem.Map((ushort)(0x10000 - BIOS.Size), BIOS.Size, Cart);
|
||||
using_bios = false;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
SwapInBIOS();
|
||||
if (HSC != null)
|
||||
HSC.Reset();
|
||||
Cart.Reset();
|
||||
Maria.Reset();
|
||||
PIA.Reset();
|
||||
CPU.Reset();
|
||||
}
|
||||
|
||||
public override void ComputeNextFrame(FrameBuffer frameBuffer)
|
||||
{
|
||||
base.ComputeNextFrame(frameBuffer);
|
||||
|
||||
AssertDebug(CPU.Jammed || CPU.RunClocks <= 0 && (CPU.RunClocks % CPU.RunClocksMultiple) == 0);
|
||||
AssertDebug(CPU.Jammed || ((CPU.Clock + (ulong)(CPU.RunClocks / CPU.RunClocksMultiple)) % (114 * (ulong)FrameBuffer.Scanlines)) == 0);
|
||||
|
||||
ulong startOfScanlineCpuClock = 0;
|
||||
|
||||
Maria.StartFrame();
|
||||
Cart.StartFrame();
|
||||
for (var i = 0; i < FrameBuffer.Scanlines && !CPU.Jammed; i++)
|
||||
{
|
||||
AssertDebug(CPU.RunClocks <= 0 && (CPU.RunClocks % CPU.RunClocksMultiple) == 0);
|
||||
var newStartOfScanlineCpuClock = CPU.Clock + (ulong)(CPU.RunClocks / CPU.RunClocksMultiple);
|
||||
|
||||
AssertDebug(startOfScanlineCpuClock == 0 || newStartOfScanlineCpuClock == startOfScanlineCpuClock + 114);
|
||||
startOfScanlineCpuClock = newStartOfScanlineCpuClock;
|
||||
|
||||
CPU.RunClocks += (7 * CPU.RunClocksMultiple);
|
||||
var remainingRunClocks = (114 - 7) * CPU.RunClocksMultiple;
|
||||
|
||||
CPU.Execute();
|
||||
if (CPU.Jammed)
|
||||
break;
|
||||
if (CPU.EmulatorPreemptRequest)
|
||||
{
|
||||
Maria.DoDMAProcessing();
|
||||
var remainingCpuClocks = 114 - (CPU.Clock - startOfScanlineCpuClock);
|
||||
CPU.Clock += remainingCpuClocks;
|
||||
CPU.RunClocks = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
var dmaClocks = Maria.DoDMAProcessing();
|
||||
|
||||
// CHEAT: Ace of Aces: Title screen has a single scanline flicker without this. Maria DMA clock counting probably not 100% accurate.
|
||||
if (i == 203 && FrameBuffer.Scanlines == 262 /*NTSC*/ || i == 228 && FrameBuffer.Scanlines == 312 /*PAL*/)
|
||||
if (dmaClocks == 152 && remainingRunClocks == 428 && (CPU.RunClocks == -4 || CPU.RunClocks == -8))
|
||||
dmaClocks -= 4;
|
||||
|
||||
// Unsure exactly what to do if Maria DMA processing extends past the current scanline.
|
||||
// For now, throw away half remaining until we are within the current scanline.
|
||||
// KLAX initialization starts DMA without initializing the DLL data structure.
|
||||
// Maria processing then runs away causing an invalid CPU opcode to be executed that jams the machine.
|
||||
// So Maria must give up at some point, but not clear exactly how.
|
||||
// Anyway, this makes KLAX work without causing breakage elsewhere.
|
||||
while ((CPU.RunClocks + remainingRunClocks) < dmaClocks)
|
||||
{
|
||||
dmaClocks >>= 1;
|
||||
}
|
||||
|
||||
// Assume the CPU waits until the next div4 boundary to proceed after DMA processing.
|
||||
if ((dmaClocks & 3) != 0)
|
||||
{
|
||||
dmaClocks += 4;
|
||||
dmaClocks -= (dmaClocks & 3);
|
||||
}
|
||||
|
||||
CPU.Clock += (ulong)(dmaClocks / CPU.RunClocksMultiple);
|
||||
CPU.RunClocks -= dmaClocks;
|
||||
|
||||
CPU.RunClocks += remainingRunClocks;
|
||||
|
||||
CPU.Execute();
|
||||
if (CPU.Jammed)
|
||||
break;
|
||||
if (CPU.EmulatorPreemptRequest)
|
||||
{
|
||||
var remainingCpuClocks = 114 - (CPU.Clock - startOfScanlineCpuClock);
|
||||
CPU.Clock += remainingCpuClocks;
|
||||
CPU.RunClocks = 0;
|
||||
}
|
||||
}
|
||||
Cart.EndFrame();
|
||||
Maria.EndFrame();
|
||||
}
|
||||
|
||||
public Machine7800(Cart cart, Bios7800 bios, HSC7800 hsc, ILogger logger, int scanlines, int startl, int fHZ, int sRate, int[] p)
|
||||
: base(logger, scanlines, startl, fHZ, sRate, p, 320)
|
||||
{
|
||||
Mem = new AddressSpace(this, 16, 6); // 7800: 16bit, 64byte pages
|
||||
|
||||
CPU = new M6502(this, 4);
|
||||
|
||||
Maria = new Maria(this, scanlines);
|
||||
Mem.Map(0x0000, 0x0040, Maria);
|
||||
Mem.Map(0x0100, 0x0040, Maria);
|
||||
Mem.Map(0x0200, 0x0040, Maria);
|
||||
Mem.Map(0x0300, 0x0040, Maria);
|
||||
|
||||
PIA = new PIA(this);
|
||||
Mem.Map(0x0280, 0x0080, PIA);
|
||||
Mem.Map(0x0480, 0x0080, PIA);
|
||||
Mem.Map(0x0580, 0x0080, PIA);
|
||||
|
||||
RAM1 = new RAM6116();
|
||||
RAM2 = new RAM6116();
|
||||
Mem.Map(0x1800, 0x0800, RAM1);
|
||||
Mem.Map(0x2000, 0x0800, RAM2);
|
||||
|
||||
Mem.Map(0x0040, 0x00c0, RAM2); // page 0 shadow
|
||||
Mem.Map(0x0140, 0x00c0, RAM2); // page 1 shadow
|
||||
Mem.Map(0x2800, 0x0800, RAM2); // shadow1
|
||||
Mem.Map(0x3000, 0x0800, RAM2); // shadow2
|
||||
Mem.Map(0x3800, 0x0800, RAM2); // shadow3
|
||||
|
||||
BIOS = bios;
|
||||
HSC = hsc;
|
||||
|
||||
if (HSC != null)
|
||||
{
|
||||
Mem.Map(0x1000, 0x800, HSC.SRAM);
|
||||
Mem.Map(0x3000, 0x1000, HSC);
|
||||
Logger.WriteLine("7800 Highscore Cartridge Installed");
|
||||
}
|
||||
|
||||
Cart = cart;
|
||||
Mem.Map(0x4000, 0xc000, Cart);
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public Machine7800(DeserializationContext input, int[] palette, int scanlines) : base(input, palette)
|
||||
{
|
||||
input.CheckVersion(1);
|
||||
|
||||
Mem = input.ReadAddressSpace(this, 16, 6); // 7800: 16bit, 64byte pages
|
||||
|
||||
CPU = input.ReadM6502(this, 4);
|
||||
|
||||
Maria = input.ReadMaria(this, scanlines);
|
||||
Mem.Map(0x0000, 0x0040, Maria);
|
||||
Mem.Map(0x0100, 0x0040, Maria);
|
||||
Mem.Map(0x0200, 0x0040, Maria);
|
||||
Mem.Map(0x0300, 0x0040, Maria);
|
||||
|
||||
PIA = input.ReadPIA(this);
|
||||
Mem.Map(0x0280, 0x0080, PIA);
|
||||
Mem.Map(0x0480, 0x0080, PIA);
|
||||
Mem.Map(0x0580, 0x0080, PIA);
|
||||
|
||||
RAM1 = input.ReadRAM6116();
|
||||
RAM2 = input.ReadRAM6116();
|
||||
Mem.Map(0x1800, 0x0800, RAM1);
|
||||
Mem.Map(0x2000, 0x0800, RAM2);
|
||||
|
||||
Mem.Map(0x0040, 0x00c0, RAM2); // page 0 shadow
|
||||
Mem.Map(0x0140, 0x00c0, RAM2); // page 1 shadow
|
||||
Mem.Map(0x2800, 0x0800, RAM2); // shadow1
|
||||
Mem.Map(0x3000, 0x0800, RAM2); // shadow2
|
||||
Mem.Map(0x3800, 0x0800, RAM2); // shadow3
|
||||
|
||||
BIOS = input.ReadOptionalBios7800();
|
||||
HSC = input.ReadOptionalHSC7800();
|
||||
|
||||
if (HSC != null)
|
||||
{
|
||||
Mem.Map(0x1000, 0x800, HSC.SRAM);
|
||||
Mem.Map(0x3000, 0x1000, HSC);
|
||||
}
|
||||
|
||||
Cart = input.ReadCart(this);
|
||||
Mem.Map(0x4000, 0xc000, Cart);
|
||||
|
||||
using_bios = input.ReadBoolean();
|
||||
|
||||
if (using_bios && BIOS != null)
|
||||
{
|
||||
Mem.Map((ushort)(0x10000 - BIOS.Size), BIOS.Size, BIOS);
|
||||
}
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(Mem);
|
||||
output.Write(CPU);
|
||||
output.Write(Maria);
|
||||
output.Write(PIA);
|
||||
output.Write(RAM1);
|
||||
output.Write(RAM2);
|
||||
output.WriteOptional(BIOS);
|
||||
output.WriteOptional(HSC);
|
||||
output.Write(Cart);
|
||||
output.Write(using_bios);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
[System.Diagnostics.Conditional("DEBUG")]
|
||||
void AssertDebug(bool cond)
|
||||
{
|
||||
if (!cond)
|
||||
System.Diagnostics.Debugger.Break();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
public sealed class Machine7800NTSC : Machine7800
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
return GetType().Name;
|
||||
}
|
||||
|
||||
public Machine7800NTSC(Cart cart, Bios7800 bios, HSC7800 hsc, ILogger logger)
|
||||
: base(cart, bios, hsc, logger, 262, 16, 60, 31440 /* NTSC_SAMPLES_PER_SEC */, MariaTables.NTSCPalette)
|
||||
{
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public Machine7800NTSC(DeserializationContext input) : base(input, MariaTables.NTSCPalette, 262)
|
||||
{
|
||||
input.CheckVersion(1);
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
output.WriteVersion(1);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
public sealed class Machine7800PAL : Machine7800
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
return GetType().Name;
|
||||
}
|
||||
|
||||
public Machine7800PAL(Cart cart, Bios7800 bios, HSC7800 hsc, ILogger logger)
|
||||
: base(cart, bios, hsc, logger, 312, 34, 50, 31200 /* PAL_SAMPLES_PER_SEC */, MariaTables.PALPalette)
|
||||
{
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public Machine7800PAL(DeserializationContext input) : base(input, MariaTables.PALPalette, 312)
|
||||
{
|
||||
input.CheckVersion(1);
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationContext output)
|
||||
{
|
||||
base.GetObjectData(output);
|
||||
output.WriteVersion(1);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,331 +0,0 @@
|
|||
/*
|
||||
/*
|
||||
* MachineBase.cs
|
||||
*
|
||||
* Abstraction of an emulated machine.
|
||||
*
|
||||
* Copyright © 2003, 2004 Mike Murphy
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace EMU7800.Core
|
||||
{
|
||||
public abstract class MachineBase
|
||||
{
|
||||
#region Fields
|
||||
|
||||
ILogger _Logger;
|
||||
FrameBuffer _FrameBuffer;
|
||||
|
||||
bool _MachineHalt;
|
||||
int _FrameHZ;
|
||||
readonly int _VisiblePitch, _Scanlines;
|
||||
|
||||
protected Cart Cart { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Properties
|
||||
|
||||
internal FrameBuffer FrameBuffer
|
||||
{
|
||||
get
|
||||
{
|
||||
AssertDebug(_FrameBuffer != null);
|
||||
return _FrameBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// The machine's Central Processing Unit.
|
||||
/// </summary>
|
||||
public M6502 CPU { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The machine's Address Space.
|
||||
/// </summary>
|
||||
public AddressSpace Mem { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The machine's Peripheral Interface Adaptor device.
|
||||
/// </summary>
|
||||
public PIA PIA { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reports whether the machine has been halted due to an internal condition or error.
|
||||
/// </summary>
|
||||
public bool MachineHalt
|
||||
{
|
||||
get { return _MachineHalt; }
|
||||
internal set { if (value) _MachineHalt = true; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The machine input state.
|
||||
/// </summary>
|
||||
public InputState InputState { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The current frame number.
|
||||
/// </summary>
|
||||
public long FrameNumber { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The first scanline that is visible.
|
||||
/// </summary>
|
||||
public int FirstScanline { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Frame rate.
|
||||
/// </summary>
|
||||
public int FrameHZ
|
||||
{
|
||||
get { return _FrameHZ < 1 ? 1 : _FrameHZ; }
|
||||
set { _FrameHZ = value < 1 ? 1 : value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Number of sound samples per second.
|
||||
/// </summary>
|
||||
public int SoundSampleFrequency { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The color palette for the configured machine.
|
||||
/// </summary>
|
||||
public int[] Palette { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Dumps CPU registers to the log when NOP instructions are encountered.
|
||||
/// </summary>
|
||||
public bool NOPRegisterDumping { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The configured logger sink.
|
||||
/// </summary>
|
||||
public ILogger Logger
|
||||
{
|
||||
get { return _Logger ?? (_Logger = new NullLogger()); }
|
||||
set { _Logger = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of the specified machine.
|
||||
/// </summary>
|
||||
/// <param name="machineType"></param>
|
||||
/// <param name="cart"></param>
|
||||
/// <param name="bios">7800 BIOS, optional.</param>
|
||||
/// <param name="hsc">7800 High Score cart, optional.</param>
|
||||
/// <param name="p1">Left controller, optional.</param>
|
||||
/// <param name="p2">Right controller, optional.</param>
|
||||
/// <param name="logger"></param>
|
||||
/// <exception cref="ArgumentNullException">Cart must not be null.</exception>
|
||||
/// <exception cref="Emu7800Exception">Specified MachineType is unexpected.</exception>
|
||||
public static MachineBase Create(MachineType machineType, Cart cart, Bios7800 bios, HSC7800 hsc, Controller p1, Controller p2, ILogger logger)
|
||||
{
|
||||
if (cart == null)
|
||||
throw new ArgumentNullException("cart");
|
||||
|
||||
MachineBase m;
|
||||
switch (machineType)
|
||||
{
|
||||
case MachineType.A2600NTSC:
|
||||
m = new Machine2600NTSC(cart, logger);
|
||||
break;
|
||||
case MachineType.A2600PAL:
|
||||
m = new Machine2600PAL(cart, logger);
|
||||
break;
|
||||
case MachineType.A7800NTSC:
|
||||
m = new Machine7800NTSC(cart, bios, hsc, logger);
|
||||
break;
|
||||
case MachineType.A7800PAL:
|
||||
m = new Machine7800PAL(cart, bios, hsc, logger);
|
||||
break;
|
||||
default:
|
||||
throw new Emu7800Exception("Unexpected MachineType: " + machineType);
|
||||
}
|
||||
|
||||
m.InputState.LeftControllerJack = p1;
|
||||
m.InputState.RightControllerJack = p2;
|
||||
|
||||
m.Reset();
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserialize a <see cref="MachineBase"/> from the specified stream.
|
||||
/// </summary>
|
||||
/// <param name="binaryReader"/>
|
||||
/// <exception cref="ArgumentNullException"/>
|
||||
/// <exception cref="Emu7800SerializationException"/>
|
||||
public static MachineBase Deserialize(BinaryReader binaryReader)
|
||||
{
|
||||
var context = new DeserializationContext(binaryReader);
|
||||
MachineBase m;
|
||||
try
|
||||
{
|
||||
m = context.ReadMachine();
|
||||
}
|
||||
catch (Emu7800SerializationException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (TargetInvocationException ex)
|
||||
{
|
||||
// TargetInvocationException wraps exceptions that unwind an Activator.CreateInstance() frame.
|
||||
throw new Emu7800SerializationException("Serialization stream does not describe a valid machine.", ex.InnerException);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Emu7800SerializationException("Serialization stream does not describe a valid machine.", ex);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the state of the machine.
|
||||
/// </summary>
|
||||
public virtual void Reset()
|
||||
{
|
||||
Logger.WriteLine("Machine {0} reset ({1} HZ {2} scanlines)", this, FrameHZ, _Scanlines);
|
||||
FrameNumber = 0;
|
||||
_MachineHalt = false;
|
||||
InputState.ClearAllInput();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the next machine frame, updating contents of the provided <see cref="FrameBuffer"/>.
|
||||
/// </summary>
|
||||
/// <param name="frameBuffer">The framebuffer to contain the computed output.</param>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
/// <exception cref="ArgumentException">frameBuffer is incompatible with machine.</exception>
|
||||
public virtual void ComputeNextFrame(FrameBuffer frameBuffer)
|
||||
{
|
||||
if (MachineHalt)
|
||||
return;
|
||||
|
||||
InputState.CaptureInputState();
|
||||
|
||||
_FrameBuffer = frameBuffer;
|
||||
FrameNumber++;
|
||||
|
||||
for (var i = 0; i < _FrameBuffer.SoundBufferByteLength; i++)
|
||||
_FrameBuffer.SoundBuffer[i] = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a <see cref="FrameBuffer"/> with compatible dimensions for this machine.
|
||||
/// </summary>
|
||||
public FrameBuffer CreateFrameBuffer()
|
||||
{
|
||||
var fb = new FrameBuffer(_VisiblePitch, _Scanlines);
|
||||
return fb;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialize the state of the machine to the specified stream.
|
||||
/// </summary>
|
||||
/// <param name="binaryWriter"/>
|
||||
/// <exception cref="ArgumentNullException"/>
|
||||
/// <exception cref="Emu7800SerializationException"/>
|
||||
public void Serialize(BinaryWriter binaryWriter)
|
||||
{
|
||||
var context = new SerializationContext(binaryWriter);
|
||||
try
|
||||
{
|
||||
context.Write(this);
|
||||
}
|
||||
catch (Emu7800SerializationException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Emu7800SerializationException("Problem serializing specified machine.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
private MachineBase()
|
||||
{
|
||||
}
|
||||
|
||||
protected MachineBase(ILogger logger, int scanLines, int firstScanline, int fHZ, int soundSampleFreq, int[] palette, int vPitch) : this()
|
||||
{
|
||||
InputState = new InputState();
|
||||
Logger = logger;
|
||||
_Scanlines = scanLines;
|
||||
FirstScanline = firstScanline;
|
||||
FrameHZ = fHZ;
|
||||
SoundSampleFrequency = soundSampleFreq;
|
||||
Palette = palette;
|
||||
_VisiblePitch = vPitch;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
protected MachineBase(DeserializationContext input, int[] palette)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException("input");
|
||||
if (palette == null)
|
||||
throw new ArgumentNullException("palette");
|
||||
if (palette.Length != 0x100)
|
||||
throw new ArgumentException("palette incorrect size, must be 256.");
|
||||
|
||||
input.CheckVersion(1);
|
||||
_MachineHalt = input.ReadBoolean();
|
||||
_FrameHZ = input.ReadInt32();
|
||||
_VisiblePitch = input.ReadInt32();
|
||||
_Scanlines = input.ReadInt32();
|
||||
FirstScanline = input.ReadInt32();
|
||||
SoundSampleFrequency = input.ReadInt32();
|
||||
NOPRegisterDumping = input.ReadBoolean();
|
||||
InputState = input.ReadInputState();
|
||||
|
||||
Palette = palette;
|
||||
Logger = null;
|
||||
}
|
||||
|
||||
public virtual void GetObjectData(SerializationContext output)
|
||||
{
|
||||
if (output == null)
|
||||
throw new ArgumentNullException("output");
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(_MachineHalt);
|
||||
output.Write(_FrameHZ);
|
||||
output.Write(_VisiblePitch);
|
||||
output.Write(_Scanlines);
|
||||
output.Write(FirstScanline);
|
||||
output.Write(SoundSampleFrequency);
|
||||
output.Write(NOPRegisterDumping);
|
||||
output.Write(InputState);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
[System.Diagnostics.Conditional("DEBUG")]
|
||||
void AssertDebug(bool cond)
|
||||
{
|
||||
if (!cond)
|
||||
System.Diagnostics.Debugger.Break();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* HostInput.cs
|
||||
*
|
||||
* Copyright © 2009 Mike Murphy
|
||||
*
|
||||
*/
|
||||
namespace EMU7800.Core
|
||||
{
|
||||
public enum MachineInput
|
||||
{
|
||||
End,
|
||||
Pause,
|
||||
Mute,
|
||||
Fire,
|
||||
Fire2,
|
||||
Left,
|
||||
Right,
|
||||
Up,
|
||||
Down,
|
||||
NumPad1, NumPad2, NumPad3,
|
||||
NumPad4, NumPad5, NumPad6,
|
||||
NumPad7, NumPad8, NumPad9,
|
||||
NumPadMult, NumPad0, NumPadHash,
|
||||
Driving0, Driving1, Driving2, Driving3,
|
||||
Reset,
|
||||
Select,
|
||||
Color,
|
||||
LeftDifficulty,
|
||||
RightDifficulty,
|
||||
SetKeyboardToPlayer1,
|
||||
SetKeyboardToPlayer2,
|
||||
SetKeyboardToPlayer3,
|
||||
SetKeyboardToPlayer4,
|
||||
PanLeft, PanRight, PanUp, PanDown,
|
||||
SaveMachine,
|
||||
TakeScreenshot,
|
||||
LeftPaddleSwap,
|
||||
GameControllerSwap,
|
||||
RightPaddleSwap,
|
||||
ShowFrameStats,
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
/*
|
||||
* MachineType.cs
|
||||
*
|
||||
* The set of known machines.
|
||||
*
|
||||
* Copyright © 2010 Mike Murphy
|
||||
*
|
||||
*/
|
||||
namespace EMU7800.Core
|
||||
{
|
||||
public enum MachineType
|
||||
{
|
||||
None,
|
||||
A2600NTSC,
|
||||
A2600PAL,
|
||||
A7800NTSC,
|
||||
A7800PAL
|
||||
};
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,180 +0,0 @@
|
|||
/*
|
||||
* MariaTables.cs
|
||||
*
|
||||
* Palette tables for the Maria class.
|
||||
* All derived from Dan Boris' 7800/MAME code.
|
||||
*
|
||||
* Copyright © 2004 Mike Murphy
|
||||
*
|
||||
*/
|
||||
namespace EMU7800.Core
|
||||
{
|
||||
public static class MariaTables
|
||||
{
|
||||
public static readonly int[] NTSCPalette =
|
||||
{
|
||||
0x000000, 0x1c1c1c, 0x393939, 0x595959, // Grey
|
||||
0x797979, 0x929292, 0xababab, 0xbcbcbc,
|
||||
0xcdcdcd, 0xd9d9d9, 0xe6e6e6, 0xececec,
|
||||
0xf2f2f2, 0xf8f8f8, 0xffffff, 0xffffff,
|
||||
|
||||
0x391701, 0x5e2304, 0x833008, 0xa54716, // Gold
|
||||
0xc85f24, 0xe37820, 0xff911d, 0xffab1d,
|
||||
0xffc51d, 0xffce34, 0xffd84c, 0xffe651,
|
||||
0xfff456, 0xfff977, 0xffff98, 0xffff98,
|
||||
|
||||
0x451904, 0x721e11, 0x9f241e, 0xb33a20, // Orange
|
||||
0xc85122, 0xe36920, 0xff811e, 0xff8c25,
|
||||
0xff982c, 0xffae38, 0xffc545, 0xffc559,
|
||||
0xffc66d, 0xffd587, 0xffe4a1, 0xffe4a1,
|
||||
|
||||
0x4a1704, 0x7e1a0d, 0xb21d17, 0xc82119, // Red Orange
|
||||
0xdf251c, 0xec3b38, 0xfa5255, 0xfc6161,
|
||||
0xff706e, 0xff7f7e, 0xff8f8f, 0xff9d9e,
|
||||
0xffabad, 0xffb9bd, 0xffc7ce, 0xffc7ce,
|
||||
|
||||
0x050568, 0x3b136d, 0x712272, 0x8b2a8c, // Pink
|
||||
0xa532a6, 0xb938ba, 0xcd3ecf, 0xdb47dd,
|
||||
0xea51eb, 0xf45ff5, 0xfe6dff, 0xfe7afd,
|
||||
0xff87fb, 0xff95fd, 0xffa4ff, 0xffa4ff,
|
||||
|
||||
0x280479, 0x400984, 0x590f90, 0x70249d, // Purple
|
||||
0x8839aa, 0xa441c3, 0xc04adc, 0xd054ed,
|
||||
0xe05eff, 0xe96dff, 0xf27cff, 0xf88aff,
|
||||
0xff98ff, 0xfea1ff, 0xfeabff, 0xfeabff,
|
||||
|
||||
0x35088a, 0x420aad, 0x500cd0, 0x6428d0, // Purple Blue
|
||||
0x7945d0, 0x8d4bd4, 0xa251d9, 0xb058ec,
|
||||
0xbe60ff, 0xc56bff, 0xcc77ff, 0xd183ff,
|
||||
0xd790ff, 0xdb9dff, 0xdfaaff, 0xdfaaff,
|
||||
|
||||
0x051e81, 0x0626a5, 0x082fca, 0x263dd4, // Blue1
|
||||
0x444cde, 0x4f5aee, 0x5a68ff, 0x6575ff,
|
||||
0x7183ff, 0x8091ff, 0x90a0ff, 0x97a9ff,
|
||||
0x9fb2ff, 0xafbeff, 0xc0cbff, 0xc0cbff,
|
||||
|
||||
0x0c048b, 0x2218a0, 0x382db5, 0x483ec7, // Blue2
|
||||
0x584fda, 0x6159ec, 0x6b64ff, 0x7a74ff,
|
||||
0x8a84ff, 0x918eff, 0x9998ff, 0xa5a3ff,
|
||||
0xb1aeff, 0xb8b8ff, 0xc0c2ff, 0xc0c2ff,
|
||||
|
||||
0x1d295a, 0x1d3876, 0x1d4892, 0x1c5cac, // Light Blue
|
||||
0x1c71c6, 0x3286cf, 0x489bd9, 0x4ea8ec,
|
||||
0x55b6ff, 0x70c7ff, 0x8cd8ff, 0x93dbff,
|
||||
0x9bdfff, 0xafe4ff, 0xc3e9ff, 0xc3e9ff,
|
||||
|
||||
0x2f4302, 0x395202, 0x446103, 0x417a12, // Turquoise
|
||||
0x3e9421, 0x4a9f2e, 0x57ab3b, 0x5cbd55,
|
||||
0x61d070, 0x69e27a, 0x72f584, 0x7cfa8d,
|
||||
0x87ff97, 0x9affa6, 0xadffb6, 0xadffb6,
|
||||
|
||||
0x0a4108, 0x0d540a, 0x10680d, 0x137d0f, // Green Blue
|
||||
0x169212, 0x19a514, 0x1cb917, 0x1ec919,
|
||||
0x21d91b, 0x47e42d, 0x6ef040, 0x78f74d,
|
||||
0x83ff5b, 0x9aff7a, 0xb2ff9a, 0xb2ff9a,
|
||||
|
||||
0x04410b, 0x05530e, 0x066611, 0x077714, // Green
|
||||
0x088817, 0x099b1a, 0x0baf1d, 0x48c41f,
|
||||
0x86d922, 0x8fe924, 0x99f927, 0xa8fc41,
|
||||
0xb7ff5b, 0xc9ff6e, 0xdcff81, 0xdcff81,
|
||||
|
||||
0x02350f, 0x073f15, 0x0c4a1c, 0x2d5f1e, // Yellow Green
|
||||
0x4f7420, 0x598324, 0x649228, 0x82a12e,
|
||||
0xa1b034, 0xa9c13a, 0xb2d241, 0xc4d945,
|
||||
0xd6e149, 0xe4f04e, 0xf2ff53, 0xf2ff53,
|
||||
|
||||
0x263001, 0x243803, 0x234005, 0x51541b, // Orange Green
|
||||
0x806931, 0x978135, 0xaf993a, 0xc2a73e,
|
||||
0xd5b543, 0xdbc03d, 0xe1cb38, 0xe2d836,
|
||||
0xe3e534, 0xeff258, 0xfbff7d, 0xfbff7d,
|
||||
|
||||
0x401a02, 0x581f05, 0x702408, 0x8d3a13, // Light Orange
|
||||
0xab511f, 0xb56427, 0xbf7730, 0xd0853a,
|
||||
0xe19344, 0xeda04e, 0xf9ad58, 0xfcb75c,
|
||||
0xffc160, 0xffc671, 0xffcb83, 0xffcb83
|
||||
};
|
||||
|
||||
public static readonly int[] PALPalette =
|
||||
{
|
||||
0x000000, 0x1c1c1c, 0x393939, 0x595959, // Grey
|
||||
0x797979, 0x929292, 0xababab, 0xbcbcbc,
|
||||
0xcdcdcd, 0xd9d9d9, 0xe6e6e6, 0xececec,
|
||||
0xf2f2f2, 0xf8f8f8, 0xffffff, 0xffffff,
|
||||
|
||||
0x263001, 0x243803, 0x234005, 0x51541b, // Orange Green
|
||||
0x806931, 0x978135, 0xaf993a, 0xc2a73e,
|
||||
0xd5b543, 0xdbc03d, 0xe1cb38, 0xe2d836,
|
||||
0xe3e534, 0xeff258, 0xfbff7d, 0xfbff7d,
|
||||
|
||||
0x263001, 0x243803, 0x234005, 0x51541b, // Orange Green
|
||||
0x806931, 0x978135, 0xaf993a, 0xc2a73e,
|
||||
0xd5b543, 0xdbc03d, 0xe1cb38, 0xe2d836,
|
||||
0xe3e534, 0xeff258, 0xfbff7d, 0xfbff7d,
|
||||
|
||||
0x401a02, 0x581f05, 0x702408, 0x8d3a13, // Light Orange
|
||||
0xab511f, 0xb56427, 0xbf7730, 0xd0853a,
|
||||
0xe19344, 0xeda04e, 0xf9ad58, 0xfcb75c,
|
||||
0xffc160, 0xffc671, 0xffcb83, 0xffcb83,
|
||||
|
||||
0x391701, 0x5e2304, 0x833008, 0xa54716, // Gold
|
||||
0xc85f24, 0xe37820, 0xff911d, 0xffab1d,
|
||||
0xffc51d, 0xffce34, 0xffd84c, 0xffe651,
|
||||
0xfff456, 0xfff977, 0xffff98, 0xffff98,
|
||||
|
||||
0x451904, 0x721e11, 0x9f241e, 0xb33a20, // Orange
|
||||
0xc85122, 0xe36920, 0xff811e, 0xff8c25,
|
||||
0xff982c, 0xffae38, 0xffc545, 0xffc559,
|
||||
0xffc66d, 0xffd587, 0xffe4a1, 0xffe4a1,
|
||||
|
||||
0x4a1704, 0x7e1a0d, 0xb21d17, 0xc82119, // Red Orange
|
||||
0xdf251c, 0xec3b38, 0xfa5255, 0xfc6161,
|
||||
0xff706e, 0xff7f7e, 0xff8f8f, 0xff9d9e,
|
||||
0xffabad, 0xffb9bd, 0xffc7ce, 0xffc7ce,
|
||||
|
||||
0x050568, 0x3b136d, 0x712272, 0x8b2a8c, // Pink
|
||||
0xa532a6, 0xb938ba, 0xcd3ecf, 0xdb47dd,
|
||||
0xea51eb, 0xf45ff5, 0xfe6dff, 0xfe7afd,
|
||||
0xff87fb, 0xff95fd, 0xffa4ff, 0xffa4ff,
|
||||
|
||||
0x280479, 0x400984, 0x590f90, 0x70249d, // Purple
|
||||
0x8839aa, 0xa441c3, 0xc04adc, 0xd054ed,
|
||||
0xe05eff, 0xe96dff, 0xf27cff, 0xf88aff,
|
||||
0xff98ff, 0xfea1ff, 0xfeabff, 0xfeabff,
|
||||
|
||||
0x051e81, 0x0626a5, 0x082fca, 0x263dd4, // Blue1
|
||||
0x444cde, 0x4f5aee, 0x5a68ff, 0x6575ff,
|
||||
0x7183ff, 0x8091ff, 0x90a0ff, 0x97a9ff,
|
||||
0x9fb2ff, 0xafbeff, 0xc0cbff, 0xc0cbff,
|
||||
|
||||
0x0c048b, 0x2218a0, 0x382db5, 0x483ec7, // Blue2
|
||||
0x584fda, 0x6159ec, 0x6b64ff, 0x7a74ff,
|
||||
0x8a84ff, 0x918eff, 0x9998ff, 0xa5a3ff,
|
||||
0xb1aeff, 0xb8b8ff, 0xc0c2ff, 0xc0c2ff,
|
||||
|
||||
0x1d295a, 0x1d3876, 0x1d4892, 0x1c5cac, // Light Blue
|
||||
0x1c71c6, 0x3286cf, 0x489bd9, 0x4ea8ec,
|
||||
0x55b6ff, 0x70c7ff, 0x8cd8ff, 0x93dbff,
|
||||
0x9bdfff, 0xafe4ff, 0xc3e9ff, 0xc3e9ff,
|
||||
|
||||
0x2f4302, 0x395202, 0x446103, 0x417a12, // Turquoise
|
||||
0x3e9421, 0x4a9f2e, 0x57ab3b, 0x5cbd55,
|
||||
0x61d070, 0x69e27a, 0x72f584, 0x7cfa8d,
|
||||
0x87ff97, 0x9affa6, 0xadffb6, 0xadffb6,
|
||||
|
||||
0x0a4108, 0x0d540a, 0x10680d, 0x137d0f, // Green Blue
|
||||
0x169212, 0x19a514, 0x1cb917, 0x1ec919,
|
||||
0x21d91b, 0x47e42d, 0x6ef040, 0x78f74d,
|
||||
0x83ff5b, 0x9aff7a, 0xb2ff9a, 0xb2ff9a,
|
||||
|
||||
0x04410b, 0x05530e, 0x066611, 0x077714, // Green
|
||||
0x088817, 0x099b1a, 0x0baf1d, 0x48c41f,
|
||||
0x86d922, 0x8fe924, 0x99f927, 0xa8fc41,
|
||||
0xb7ff5b, 0xc9ff6e, 0xdcff81, 0xdcff81,
|
||||
|
||||
0x02350f, 0x073f15, 0x0c4a1c, 0x2d5f1e, // Yellow Green
|
||||
0x4f7420, 0x598324, 0x649228, 0x82a12e,
|
||||
0xa1b034, 0xa9c13a, 0xb2d241, 0xc4d945,
|
||||
0xd6e149, 0xe4f04e, 0xf2ff53, 0xf2ff53
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
* NullDevice.cs
|
||||
*
|
||||
* Default memory mappable device.
|
||||
*
|
||||
* Copyright © 2003, 2004 Mike Murphy
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
|
||||
namespace EMU7800.Core
|
||||
{
|
||||
public sealed class NullDevice : IDevice
|
||||
{
|
||||
MachineBase M { get; set; }
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
Log("{0} reset", this);
|
||||
}
|
||||
|
||||
public byte this[ushort addr]
|
||||
{
|
||||
get
|
||||
{
|
||||
LogDebug("NullDevice: Peek at ${0:x4}, PC=${1:x4}", addr, M.CPU.PC);
|
||||
return 0;
|
||||
}
|
||||
set
|
||||
{
|
||||
LogDebug("NullDevice: Poke at ${0:x4},${1:x2}, PC=${2:x4}", addr, value, M.CPU.PC);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public override String ToString()
|
||||
{
|
||||
return "NullDevice";
|
||||
}
|
||||
|
||||
#region Constructors
|
||||
|
||||
private NullDevice()
|
||||
{
|
||||
}
|
||||
|
||||
public NullDevice(MachineBase m)
|
||||
{
|
||||
if (m == null)
|
||||
throw new ArgumentNullException("m");
|
||||
M = m;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
void Log(string format, params object[] args)
|
||||
{
|
||||
if (M == null || M.Logger == null)
|
||||
return;
|
||||
M.Logger.WriteLine(format, args);
|
||||
}
|
||||
|
||||
[System.Diagnostics.Conditional("DEBUG")]
|
||||
void LogDebug(string format, params object[] args)
|
||||
{
|
||||
if (M == null || M.Logger == null)
|
||||
return;
|
||||
M.Logger.WriteLine(format, args);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
namespace EMU7800.Core
|
||||
{
|
||||
public class NullLogger : ILogger
|
||||
{
|
||||
public void WriteLine(string format, params object[] args)
|
||||
{
|
||||
}
|
||||
|
||||
public void WriteLine(object value)
|
||||
{
|
||||
}
|
||||
|
||||
public void Write(string format, params object[] args)
|
||||
{
|
||||
}
|
||||
|
||||
public void Write(object value)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,348 +0,0 @@
|
|||
/*
|
||||
* PIA.cs
|
||||
*
|
||||
* The Peripheral Interface Adapter (6532) device.
|
||||
* a.k.a. RIOT (RAM I/O Timer?)
|
||||
*
|
||||
* Copyright © 2003, 2004, 2012 Mike Murphy
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
|
||||
namespace EMU7800.Core
|
||||
{
|
||||
public sealed class PIA : IDevice
|
||||
{
|
||||
readonly MachineBase M;
|
||||
|
||||
readonly byte[] RAM = new byte[0x80];
|
||||
|
||||
ulong TimerTarget;
|
||||
int TimerShift;
|
||||
bool IRQEnabled, IRQTriggered;
|
||||
|
||||
public byte DDRA { get; private set; }
|
||||
public byte DDRB { get; private set; }
|
||||
|
||||
public byte WrittenPortA { get; private set; }
|
||||
public byte WrittenPortB { get; private set; }
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
// Some games will loop/hang on $0284 if these are initialized to zero
|
||||
TimerShift = 10;
|
||||
TimerTarget = M.CPU.Clock + (ulong)(0xff << TimerShift);
|
||||
|
||||
IRQEnabled = false;
|
||||
IRQTriggered = false;
|
||||
|
||||
DDRA = 0;
|
||||
|
||||
Log("{0} reset", this);
|
||||
}
|
||||
|
||||
public byte this[ushort addr]
|
||||
{
|
||||
get { return peek(addr); }
|
||||
set { poke(addr, value); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "PIA/RIOT M6532";
|
||||
}
|
||||
|
||||
#region Constructors
|
||||
|
||||
private PIA()
|
||||
{
|
||||
}
|
||||
|
||||
public PIA(MachineBase m)
|
||||
{
|
||||
if (m == null)
|
||||
throw new ArgumentNullException("m");
|
||||
M = m;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
byte peek(ushort addr)
|
||||
{
|
||||
if ((addr & 0x200) == 0)
|
||||
{
|
||||
return RAM[addr & 0x7f];
|
||||
}
|
||||
|
||||
switch ((byte)(addr & 7))
|
||||
{
|
||||
case 0: // SWCHA: Controllers
|
||||
return ReadPortA();
|
||||
case 1: // SWCHA DDR: 0=input, 1=output
|
||||
return DDRA;
|
||||
case 2: // SWCHB: Console switches (on 7800, PB2 & PB4 are used)
|
||||
return ReadPortB();
|
||||
case 3: // SWCHB DDR: 0=input, 1=output
|
||||
return 0;
|
||||
case 4: // INTIM
|
||||
case 6:
|
||||
return ReadTimerRegister();
|
||||
case 5: // INTFLG
|
||||
case 7:
|
||||
return ReadInterruptFlag();
|
||||
default:
|
||||
LogDebug("PIA: Unhandled peek ${0:x4}, PC=${1:x4}", addr, M.CPU.PC);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void poke(ushort addr, byte data)
|
||||
{
|
||||
if ((addr & 0x200) == 0)
|
||||
{
|
||||
RAM[addr & 0x7f] = data;
|
||||
return;
|
||||
}
|
||||
|
||||
// A2 Distinguishes I/O registers from the Timer
|
||||
if ((addr & 0x04) != 0)
|
||||
{
|
||||
if ((addr & 0x10) != 0)
|
||||
{
|
||||
IRQEnabled = (addr & 0x08) != 0;
|
||||
SetTimerRegister(data, addr & 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogDebug("PIA: Timer: Unhandled poke ${0:x4} w/${1:x2}, PC=${2:x4}", addr, data, M.CPU.PC);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch ((byte)(addr & 3))
|
||||
{
|
||||
case 0: // SWCHA: Port A
|
||||
WritePortA(data);
|
||||
break;
|
||||
case 1: // SWACNT: Port A DDR
|
||||
DDRA = data;
|
||||
break;
|
||||
case 2: // SWCHB: Port B
|
||||
WritePortB(data);
|
||||
break;
|
||||
case 3: // SWBCNT: Port B DDR
|
||||
DDRB = data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 0: TIM1T: set 1 clock interval ( 838 nsec/interval)
|
||||
// 1: TIM8T: set 8 clock interval ( 6.7 usec/interval)
|
||||
// 2: TIM64T: set 64 clock interval ( 53.6 usec/interval)
|
||||
// 3: T1024T: set 1024 clock interval (858.2 usec/interval)
|
||||
void SetTimerRegister(byte data, int interval)
|
||||
{
|
||||
IRQTriggered = false;
|
||||
TimerShift = new[] { 0, 3, 6, 10 }[interval];
|
||||
TimerTarget = M.CPU.Clock + (ulong)(data << TimerShift);
|
||||
}
|
||||
|
||||
byte ReadTimerRegister()
|
||||
{
|
||||
IRQTriggered = false;
|
||||
var delta = (int)(TimerTarget - M.CPU.Clock);
|
||||
if (delta >= 0)
|
||||
{
|
||||
return (byte)(delta >> TimerShift);
|
||||
}
|
||||
if (delta != -1)
|
||||
{
|
||||
IRQTriggered = true;
|
||||
}
|
||||
return (byte)(delta >= -256 ? delta : 0);
|
||||
}
|
||||
|
||||
byte ReadInterruptFlag()
|
||||
{
|
||||
var delta = (int)(TimerTarget - M.CPU.Clock);
|
||||
return (byte)((delta >= 0 || IRQEnabled && IRQTriggered) ? 0x00 : 0x80);
|
||||
}
|
||||
|
||||
// PortA: Controller Jacks
|
||||
//
|
||||
// Left Jack Right Jack
|
||||
// ------------- -------------
|
||||
// \ 1 2 3 4 5 / \ 1 2 3 4 5 /
|
||||
// \ 6 7 8 9 / \ 6 7 8 9 /
|
||||
// --------- ---------
|
||||
//
|
||||
// pin 1 D4 PIA SWCHA D0 PIA SWCHA
|
||||
// pin 2 D5 PIA SWCHA D1 PIA SWCHA
|
||||
// pin 3 D6 PIA SWCHA D2 PIA SWCHA
|
||||
// pin 4 D7 PIA SWCHA D3 PIA SWCHA
|
||||
// pin 5 D7 TIA INPT1 (Dumped) D7 TIA INPT3 (Dumped) 7800: Right Fire
|
||||
// pin 6 D7 TIA INPT4 (Latched) D7 TIA INPT5 (Latched) 2600: Fire
|
||||
// pin 7 +5 +5
|
||||
// pin 8 GND GND
|
||||
// pin 9 D7 TIA INPT0 (Dumped) D7 TIA INPT2 (Dumped) 7800: Left Fire
|
||||
//
|
||||
byte ReadPortA()
|
||||
{
|
||||
var porta = 0;
|
||||
var mi = M.InputState;
|
||||
|
||||
switch (mi.LeftControllerJack)
|
||||
{
|
||||
case Controller.Joystick:
|
||||
case Controller.ProLineJoystick:
|
||||
case Controller.BoosterGrip:
|
||||
porta |= mi.SampleCapturedControllerActionState(0, ControllerAction.Up) ? 0 : (1 << 4);
|
||||
porta |= mi.SampleCapturedControllerActionState(0, ControllerAction.Down) ? 0 : (1 << 5);
|
||||
porta |= mi.SampleCapturedControllerActionState(0, ControllerAction.Left) ? 0 : (1 << 6);
|
||||
porta |= mi.SampleCapturedControllerActionState(0, ControllerAction.Right) ? 0 : (1 << 7);
|
||||
break;
|
||||
case Controller.Driving:
|
||||
porta |= mi.SampleCapturedDrivingState(0) << 4;
|
||||
break;
|
||||
case Controller.Paddles:
|
||||
porta |= mi.SampleCapturedControllerActionState(0, ControllerAction.Trigger) ? 0 : (1 << 7);
|
||||
porta |= mi.SampleCapturedControllerActionState(1, ControllerAction.Trigger) ? 0 : (1 << 6);
|
||||
break;
|
||||
case Controller.Lightgun:
|
||||
porta |= mi.SampleCapturedControllerActionState(0, ControllerAction.Trigger) ? (1 << 4) : 0;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (mi.RightControllerJack)
|
||||
{
|
||||
case Controller.Joystick:
|
||||
case Controller.ProLineJoystick:
|
||||
case Controller.BoosterGrip:
|
||||
porta |= mi.SampleCapturedControllerActionState(1, ControllerAction.Up) ? 0 : (1 << 0);
|
||||
porta |= mi.SampleCapturedControllerActionState(1, ControllerAction.Down) ? 0 : (1 << 1);
|
||||
porta |= mi.SampleCapturedControllerActionState(1, ControllerAction.Left) ? 0 : (1 << 2);
|
||||
porta |= mi.SampleCapturedControllerActionState(1, ControllerAction.Right) ? 0 : (1 << 3);
|
||||
break;
|
||||
case Controller.Driving:
|
||||
porta |= mi.SampleCapturedDrivingState(1);
|
||||
break;
|
||||
case Controller.Paddles:
|
||||
porta |= mi.SampleCapturedControllerActionState(2, ControllerAction.Trigger) ? 0 : (1 << 3);
|
||||
porta |= mi.SampleCapturedControllerActionState(3, ControllerAction.Trigger) ? 0 : (1 << 2);
|
||||
break;
|
||||
case Controller.Lightgun:
|
||||
porta |= mi.SampleCapturedControllerActionState(1, ControllerAction.Trigger) ? (1 << 0) : 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return (byte)porta;
|
||||
}
|
||||
|
||||
void WritePortA(byte porta)
|
||||
{
|
||||
WrittenPortA = (byte)((porta & DDRA) | (WrittenPortA & (~DDRA)));
|
||||
}
|
||||
|
||||
void WritePortB(byte portb)
|
||||
{
|
||||
WrittenPortB = (byte)((portb & DDRB) | (WrittenPortB & (~DDRB)));
|
||||
}
|
||||
|
||||
// PortB: Console Switches
|
||||
//
|
||||
// D0 Game Reset 0=on
|
||||
// D1 Game Select 0=on
|
||||
// D2 (used on 7800)
|
||||
// D3 Console Color 1=Color, 0=B/W
|
||||
// D4 (used on 7800)
|
||||
// D5 (unused)
|
||||
// D6 Left Difficulty A 1=A (pro), 0=B (novice)
|
||||
// D7 Right Difficulty A 1=A (pro), 0=B (novice)
|
||||
//
|
||||
byte ReadPortB()
|
||||
{
|
||||
var portb = 0;
|
||||
var mi = M.InputState;
|
||||
|
||||
portb |= mi.SampleCapturedConsoleSwitchState(ConsoleSwitch.GameReset) ? 0 : (1 << 0);
|
||||
portb |= mi.SampleCapturedConsoleSwitchState(ConsoleSwitch.GameSelect) ? 0 : (1 << 1);
|
||||
portb |= mi.SampleCapturedConsoleSwitchState(ConsoleSwitch.GameBW) ? 0 : (1 << 3);
|
||||
portb |= mi.SampleCapturedConsoleSwitchState(ConsoleSwitch.LeftDifficultyA) ? (1 << 6) : 0;
|
||||
portb |= mi.SampleCapturedConsoleSwitchState(ConsoleSwitch.RightDifficultyA) ? (1 << 7) : 0;
|
||||
|
||||
return (byte)portb;
|
||||
}
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public PIA(DeserializationContext input, MachineBase m) : this(m)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException("input");
|
||||
|
||||
var version = input.CheckVersion(1, 2);
|
||||
RAM = input.ReadExpectedBytes(0x80);
|
||||
TimerTarget = input.ReadUInt64();
|
||||
TimerShift = input.ReadInt32();
|
||||
IRQEnabled = input.ReadBoolean();
|
||||
IRQTriggered = input.ReadBoolean();
|
||||
DDRA = input.ReadByte();
|
||||
WrittenPortA = input.ReadByte();
|
||||
if (version > 1)
|
||||
{
|
||||
DDRB = input.ReadByte();
|
||||
WrittenPortB = input.ReadByte();
|
||||
}
|
||||
}
|
||||
|
||||
public void GetObjectData(SerializationContext output)
|
||||
{
|
||||
if (output == null)
|
||||
throw new ArgumentNullException("output");
|
||||
|
||||
output.WriteVersion(2);
|
||||
output.Write(RAM);
|
||||
output.Write(TimerTarget);
|
||||
output.Write(TimerShift);
|
||||
output.Write(IRQEnabled);
|
||||
output.Write(IRQTriggered);
|
||||
output.Write(DDRA);
|
||||
output.Write(WrittenPortA);
|
||||
output.Write(DDRB);
|
||||
output.Write(WrittenPortB);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
void Log(string format, params object[] args)
|
||||
{
|
||||
if (M == null || M.Logger == null)
|
||||
return;
|
||||
M.Logger.WriteLine(format, args);
|
||||
}
|
||||
|
||||
[System.Diagnostics.Conditional("DEBUG")]
|
||||
void LogDebug(string format, params object[] args)
|
||||
{
|
||||
if (M == null || M.Logger == null)
|
||||
return;
|
||||
M.Logger.WriteLine(format, args);
|
||||
}
|
||||
|
||||
[System.Diagnostics.Conditional("DEBUG")]
|
||||
void AssertDebug(bool cond)
|
||||
{
|
||||
if (!cond)
|
||||
System.Diagnostics.Debugger.Break();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,437 +0,0 @@
|
|||
/*
|
||||
* PokeySound.cs
|
||||
*
|
||||
* Emulation of the audio features of the Atari Pot Keyboard Integrated Circuit (POKEY, C012294).
|
||||
*
|
||||
* Implementation inspired by prior works of Greg Stanton (ProSystem Emulator) and Ron Fries.
|
||||
*
|
||||
* Copyright © 2012 Mike Murphy
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
|
||||
namespace EMU7800.Core
|
||||
{
|
||||
public sealed class PokeySound
|
||||
{
|
||||
#region Constants and Tables
|
||||
|
||||
const int
|
||||
AUDF1 = 0x00, // write reg: channel 1 frequency
|
||||
AUDC1 = 0x01, // write reg: channel 1 generator
|
||||
AUDF2 = 0x02, // write reg: channel 2 frequency
|
||||
AUDC2 = 0x03, // write reg: channel 2 generator
|
||||
AUDF3 = 0x04, // write reg: channel 3 frequency
|
||||
AUDC3 = 0x05, // write reg: channel 3 generator
|
||||
AUDF4 = 0x06, // write reg: channel 4 frequency
|
||||
AUDC4 = 0x07, // write reg: channel 4 generator
|
||||
AUDCTL = 0x08, // write reg: control over audio channels
|
||||
SKCTL = 0x0f, // write reg: control over serial port
|
||||
RANDOM = 0x0a; // read reg: random number generator value
|
||||
|
||||
const int
|
||||
AUDCTL_POLY9 = 0x80, // make 17-bit poly counter into a 9-bit poly counter
|
||||
AUDCTL_CH1_179 = 0x40, // clocks channel 1 with 1.79 MHz, instead of 64 kHz
|
||||
AUDCTL_CH3_179 = 0x20, // clocks channel 3 with 1.79 MHz, instead of 64 kHz
|
||||
AUDCTL_CH1_CH2 = 0x10, // clock channel 2 with channel 1, instead of 64 kHz (16-bit)
|
||||
AUDCTL_CH3_CH4 = 0x08, // clock channel 4 with channel 3, instead of 64 kHz (16-bit)
|
||||
AUDCTL_CH1_FILTER = 0x04, // inserts high-pass filter into channel 1, clocked by channel 3
|
||||
AUDCTL_CH2_FILTER = 0x02, // inserts high-pass filter into channel 2, clocked by channel 4
|
||||
AUDCTL_CLOCK_15 = 0x01; // change normal clock base from 64 kHz to 15 kHz
|
||||
|
||||
const int
|
||||
AUDC_NOTPOLY5 = 0x80,
|
||||
AUDC_POLY4 = 0x40,
|
||||
AUDC_PURE = 0x20,
|
||||
AUDC_VOLUME_ONLY = 0x10,
|
||||
AUDC_VOLUME_MASK = 0x0f;
|
||||
|
||||
const int
|
||||
DIV_64 = 28,
|
||||
DIV_15 = 114,
|
||||
POLY9_SIZE = 0x01ff,
|
||||
POLY17_SIZE = 0x0001ffff,
|
||||
POKEY_FREQ = 1787520,
|
||||
SKCTL_RESET = 3;
|
||||
|
||||
const int CPU_TICKS_PER_AUDIO_SAMPLE = 57;
|
||||
|
||||
readonly byte[] _poly04 = { 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0 };
|
||||
readonly byte[] _poly05 = { 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1 };
|
||||
readonly byte[] _poly17 = new byte[POLY9_SIZE]; // should be POLY17_SIZE, but instead wrapping around to conserve storage
|
||||
|
||||
readonly Random _random = new Random();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Object State
|
||||
|
||||
readonly MachineBase M;
|
||||
|
||||
readonly int _pokeyTicksPerSample;
|
||||
int _pokeyTicks;
|
||||
|
||||
ulong _lastUpdateCpuClock;
|
||||
int _bufferIndex;
|
||||
|
||||
readonly byte[] _audf = new byte[4];
|
||||
readonly byte[] _audc = new byte[4];
|
||||
byte _audctl, _skctl;
|
||||
|
||||
int _baseMultiplier;
|
||||
int _poly04Counter;
|
||||
int _poly05Counter;
|
||||
int _poly17Counter, _poly17Size;
|
||||
|
||||
readonly int[] _divideMax = new int[4];
|
||||
readonly int[] _divideCount = new int[4];
|
||||
readonly byte[] _output = new byte[4];
|
||||
readonly byte[] _outvol = new byte[4];
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Members
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_poly04Counter = _poly05Counter = _poly17Counter = _audctl = _skctl = 0;
|
||||
|
||||
_baseMultiplier = DIV_64;
|
||||
_poly17Size = POLY17_SIZE;
|
||||
|
||||
_pokeyTicks = 0;
|
||||
|
||||
for (var ch = 0; ch < 4; ch++)
|
||||
{
|
||||
_outvol[ch] = _output[ch] = _audc[ch] = _audf[ch] = 0;
|
||||
_divideCount[ch] = Int32.MaxValue;
|
||||
_divideMax[ch] = Int32.MaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
public void StartFrame()
|
||||
{
|
||||
_lastUpdateCpuClock = M.CPU.Clock;
|
||||
_bufferIndex = 0;
|
||||
}
|
||||
|
||||
public void EndFrame()
|
||||
{
|
||||
RenderSamples(M.FrameBuffer.SoundBufferByteLength - _bufferIndex);
|
||||
}
|
||||
|
||||
public byte Read(ushort addr)
|
||||
{
|
||||
addr &= 0xf;
|
||||
|
||||
switch (addr)
|
||||
{
|
||||
// If the 2 least significant bits of SKCTL are 0, the random number generator is disabled (return all 1s.)
|
||||
// Ballblazer music relies on this.
|
||||
case RANDOM:
|
||||
return (_skctl & SKCTL_RESET) == 0 ? (byte)0xff : (byte)_random.Next(0xff);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void Update(ushort addr, byte data)
|
||||
{
|
||||
if (M.CPU.Clock > _lastUpdateCpuClock)
|
||||
{
|
||||
var updCpuClocks = (int)(M.CPU.Clock - _lastUpdateCpuClock);
|
||||
var samples = updCpuClocks / CPU_TICKS_PER_AUDIO_SAMPLE;
|
||||
RenderSamples(samples);
|
||||
_lastUpdateCpuClock += (ulong)(samples * CPU_TICKS_PER_AUDIO_SAMPLE);
|
||||
}
|
||||
|
||||
addr &= 0xf;
|
||||
|
||||
switch (addr)
|
||||
{
|
||||
case AUDF1:
|
||||
_audf[0] = data;
|
||||
ResetChannel1();
|
||||
if ((_audctl & AUDCTL_CH1_CH2) != 0)
|
||||
ResetChannel2();
|
||||
break;
|
||||
case AUDC1:
|
||||
_audc[0] = data;
|
||||
ResetChannel1();
|
||||
break;
|
||||
case AUDF2:
|
||||
_audf[1] = data;
|
||||
ResetChannel2();
|
||||
break;
|
||||
case AUDC2:
|
||||
_audc[1] = data;
|
||||
ResetChannel2();
|
||||
break;
|
||||
case AUDF3:
|
||||
_audf[2] = data;
|
||||
ResetChannel3();
|
||||
if ((_audctl & AUDCTL_CH3_CH4) != 0)
|
||||
ResetChannel4();
|
||||
break;
|
||||
case AUDC3:
|
||||
_audc[2] = data;
|
||||
ResetChannel3();
|
||||
break;
|
||||
case AUDF4:
|
||||
_audf[3] = data;
|
||||
ResetChannel4();
|
||||
break;
|
||||
case AUDC4:
|
||||
_audc[3] = data;
|
||||
ResetChannel4();
|
||||
break;
|
||||
case AUDCTL:
|
||||
_audctl = data;
|
||||
_poly17Size = ((_audctl & AUDCTL_POLY9) != 0) ? POLY9_SIZE : POLY17_SIZE;
|
||||
_baseMultiplier = ((_audctl & AUDCTL_CLOCK_15) != 0) ? DIV_15 : DIV_64;
|
||||
ResetChannel1();
|
||||
ResetChannel2();
|
||||
ResetChannel3();
|
||||
ResetChannel4();
|
||||
break;
|
||||
case SKCTL:
|
||||
_skctl = data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
private PokeySound()
|
||||
{
|
||||
_random.NextBytes(_poly17);
|
||||
for (var i = 0; i < _poly17.Length; i++)
|
||||
_poly17[i] &= 0x01;
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
public PokeySound(MachineBase m) : this()
|
||||
{
|
||||
if (m == null)
|
||||
throw new ArgumentNullException("m");
|
||||
|
||||
M = m;
|
||||
|
||||
// Add 8-bits of fractional representation to reduce distortion on output
|
||||
_pokeyTicksPerSample = (POKEY_FREQ << 8) / M.SoundSampleFrequency;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public PokeySound(DeserializationContext input, MachineBase m) : this(m)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException("input");
|
||||
|
||||
input.CheckVersion(1);
|
||||
_lastUpdateCpuClock = input.ReadUInt64();
|
||||
_bufferIndex = input.ReadInt32();
|
||||
_audf = input.ReadBytes();
|
||||
_audc = input.ReadBytes();
|
||||
_audctl = input.ReadByte();
|
||||
_skctl = input.ReadByte();
|
||||
_output = input.ReadBytes();
|
||||
_outvol = input.ReadBytes();
|
||||
_divideMax = input.ReadIntegers(4);
|
||||
_divideCount = input.ReadIntegers(4);
|
||||
_pokeyTicks = input.ReadInt32();
|
||||
_pokeyTicksPerSample = input.ReadInt32();
|
||||
_baseMultiplier = input.ReadInt32();
|
||||
_poly04Counter = input.ReadInt32();
|
||||
_poly05Counter = input.ReadInt32();
|
||||
_poly17Counter = input.ReadInt32();
|
||||
_poly17Size = input.ReadInt32();
|
||||
}
|
||||
|
||||
public void GetObjectData(SerializationContext output)
|
||||
{
|
||||
if (output == null)
|
||||
throw new ArgumentNullException("output");
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(_lastUpdateCpuClock);
|
||||
output.Write(_bufferIndex);
|
||||
output.Write(_audf);
|
||||
output.Write(_audc);
|
||||
output.Write(_audctl);
|
||||
output.Write(_skctl);
|
||||
output.Write(_output);
|
||||
output.Write(_outvol);
|
||||
output.Write(_divideMax);
|
||||
output.Write(_divideCount);
|
||||
output.Write(_pokeyTicks);
|
||||
output.Write(_pokeyTicksPerSample);
|
||||
output.Write(_baseMultiplier);
|
||||
output.Write(_poly04Counter);
|
||||
output.Write(_poly05Counter);
|
||||
output.Write(_poly17Counter);
|
||||
output.Write(_poly17Size);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
void RenderSamples(int count)
|
||||
{
|
||||
const int POKEY_SAMPLE = 4;
|
||||
var poly17Length = (_poly17Size > _poly17.Length ? _poly17.Length : _poly17Size);
|
||||
|
||||
while (count > 0 && _bufferIndex < M.FrameBuffer.SoundBufferByteLength)
|
||||
{
|
||||
var nextEvent = POKEY_SAMPLE;
|
||||
var wholeTicksToConsume = (_pokeyTicks >> 8);
|
||||
|
||||
for (var ch = 0; ch < 4; ch++)
|
||||
{
|
||||
if (_divideCount[ch] <= wholeTicksToConsume)
|
||||
{
|
||||
wholeTicksToConsume = _divideCount[ch];
|
||||
nextEvent = ch;
|
||||
}
|
||||
}
|
||||
|
||||
for (var ch = 0; ch < 4; ch++)
|
||||
_divideCount[ch] -= wholeTicksToConsume;
|
||||
|
||||
_pokeyTicks -= (wholeTicksToConsume << 8);
|
||||
|
||||
if (nextEvent == POKEY_SAMPLE)
|
||||
{
|
||||
_pokeyTicks += _pokeyTicksPerSample;
|
||||
|
||||
byte sample = 0;
|
||||
for (var ch = 0; ch < 4; ch++)
|
||||
sample += _outvol[ch];
|
||||
|
||||
M.FrameBuffer.SoundBuffer[_bufferIndex++] += sample;
|
||||
count--;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
_divideCount[nextEvent] += _divideMax[nextEvent];
|
||||
|
||||
_poly04Counter += wholeTicksToConsume;
|
||||
_poly04Counter %= _poly04.Length;
|
||||
|
||||
_poly05Counter += wholeTicksToConsume;
|
||||
_poly05Counter %= _poly05.Length;
|
||||
|
||||
_poly17Counter += wholeTicksToConsume;
|
||||
_poly17Counter %= poly17Length;
|
||||
|
||||
if ((_audc[nextEvent] & AUDC_NOTPOLY5) != 0 || _poly05[_poly05Counter] != 0)
|
||||
{
|
||||
if ((_audc[nextEvent] & AUDC_PURE) != 0)
|
||||
_output[nextEvent] ^= 1;
|
||||
else if ((_audc[nextEvent] & AUDC_POLY4) != 0)
|
||||
_output[nextEvent] = _poly04[_poly04Counter];
|
||||
else
|
||||
_output[nextEvent] = _poly17[_poly17Counter];
|
||||
}
|
||||
|
||||
_outvol[nextEvent] = (_output[nextEvent] != 0) ? (byte)(_audc[nextEvent] & AUDC_VOLUME_MASK) : (byte)0;
|
||||
}
|
||||
}
|
||||
|
||||
// As defined in the manual, the exact divider values are different depending on the frequency and resolution:
|
||||
// 64 kHz or 15 kHz AUDF + 1
|
||||
// 1 MHz, 8-bit AUDF + 4
|
||||
// 1 MHz, 16-bit AUDF[CHAN1] + 256 * AUDF[CHAN2] + 7
|
||||
|
||||
void ResetChannel1()
|
||||
{
|
||||
var val = ((_audctl & AUDCTL_CH1_179) != 0) ? (_audf[0] + 4) : ((_audf[0] + 1) * _baseMultiplier);
|
||||
if (val != _divideMax[0])
|
||||
{
|
||||
_divideMax[0] = val;
|
||||
if (val < _divideCount[0])
|
||||
_divideCount[0] = val;
|
||||
}
|
||||
UpdateVolumeSettingsForChannel(0);
|
||||
}
|
||||
|
||||
void ResetChannel2()
|
||||
{
|
||||
int val;
|
||||
if ((_audctl & AUDCTL_CH1_CH2) != 0)
|
||||
{
|
||||
val = ((_audctl & AUDCTL_CH1_179) != 0) ? (_audf[1] * 256 + _audf[0] + 7) : ((_audf[1] * 256 + _audf[0] + 1) * _baseMultiplier);
|
||||
}
|
||||
else
|
||||
{
|
||||
val = ((_audf[1] + 1) * _baseMultiplier);
|
||||
}
|
||||
if (val != _divideMax[1])
|
||||
{
|
||||
_divideMax[1] = val;
|
||||
if (val < _divideCount[1])
|
||||
_divideCount[1] = val;
|
||||
}
|
||||
UpdateVolumeSettingsForChannel(1);
|
||||
}
|
||||
|
||||
void ResetChannel3()
|
||||
{
|
||||
var val = ((_audctl & AUDCTL_CH3_179) != 0) ? (_audf[2] + 4) : ((_audf[2] + 1) * _baseMultiplier);
|
||||
if (val != _divideMax[2])
|
||||
{
|
||||
_divideMax[2] = val;
|
||||
if (val < _divideCount[2])
|
||||
_divideCount[2] = val;
|
||||
}
|
||||
UpdateVolumeSettingsForChannel(2);
|
||||
}
|
||||
|
||||
void ResetChannel4()
|
||||
{
|
||||
int val;
|
||||
if ((_audctl & AUDCTL_CH3_CH4) != 0)
|
||||
{
|
||||
val = ((_audctl & AUDCTL_CH3_179) != 0) ? (_audf[3] * 256 + _audf[2] + 7) : ((_audf[3] * 256 + _audf[2] + 1) * _baseMultiplier);
|
||||
}
|
||||
else
|
||||
{
|
||||
val = ((_audf[3] + 1) * _baseMultiplier);
|
||||
}
|
||||
if (val != _divideMax[3])
|
||||
{
|
||||
_divideMax[3] = val;
|
||||
if (val < _divideCount[3])
|
||||
_divideCount[3] = val;
|
||||
}
|
||||
UpdateVolumeSettingsForChannel(3);
|
||||
}
|
||||
|
||||
void UpdateVolumeSettingsForChannel(int ch)
|
||||
{
|
||||
if (((_audc[ch] & AUDC_VOLUME_ONLY) != 0) || ((_audc[ch] & AUDC_VOLUME_MASK) == 0) || (_divideMax[ch] < (_pokeyTicksPerSample >> 8)))
|
||||
{
|
||||
_outvol[ch] = (byte)(_audc[ch] & AUDC_VOLUME_MASK);
|
||||
_divideCount[ch] = Int32.MaxValue;
|
||||
_divideMax[ch] = Int32.MaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
[System.Diagnostics.Conditional("DEBUG")]
|
||||
void LogDebug(string format, params object[] args)
|
||||
{
|
||||
if (M == null || M.Logger == null)
|
||||
return;
|
||||
M.Logger.WriteLine(format, args);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
* RAM6116.cs
|
||||
*
|
||||
* Implements a 6116 RAM device found in the 7800.
|
||||
*
|
||||
* Copyright © 2004 Mike Murphy
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
|
||||
namespace EMU7800.Core
|
||||
{
|
||||
public sealed class RAM6116 : IDevice
|
||||
{
|
||||
readonly byte[] RAM;
|
||||
|
||||
#region IDevice Members
|
||||
|
||||
public void Reset() {}
|
||||
|
||||
public byte this[ushort addr]
|
||||
{
|
||||
get { return RAM[addr & 0x07ff]; }
|
||||
set { RAM[addr & 0x07ff] = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public RAM6116()
|
||||
{
|
||||
RAM = new byte[0x800];
|
||||
}
|
||||
|
||||
public RAM6116(byte[] ram)
|
||||
{
|
||||
RAM = ram;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public RAM6116(DeserializationContext input)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException("input");
|
||||
|
||||
input.CheckVersion(1);
|
||||
RAM = input.ReadExpectedBytes(0x800);
|
||||
}
|
||||
|
||||
public void GetObjectData(SerializationContext output)
|
||||
{
|
||||
if (output == null)
|
||||
throw new ArgumentNullException("output");
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(RAM);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,232 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace EMU7800.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A context for serializing <see cref="MachineBase"/> objects.
|
||||
/// </summary>
|
||||
public class SerializationContext
|
||||
{
|
||||
#region Fields
|
||||
|
||||
readonly BinaryWriter _binaryWriter;
|
||||
|
||||
#endregion
|
||||
|
||||
public void Write(byte value)
|
||||
{
|
||||
_binaryWriter.Write(value);
|
||||
}
|
||||
|
||||
public void Write(ushort value)
|
||||
{
|
||||
_binaryWriter.Write(value);
|
||||
}
|
||||
|
||||
public void Write(int value)
|
||||
{
|
||||
_binaryWriter.Write(value);
|
||||
}
|
||||
|
||||
public void Write(uint value)
|
||||
{
|
||||
_binaryWriter.Write(value);
|
||||
}
|
||||
|
||||
public void Write(long value)
|
||||
{
|
||||
_binaryWriter.Write(value);
|
||||
}
|
||||
|
||||
public void Write(ulong value)
|
||||
{
|
||||
_binaryWriter.Write(value);
|
||||
}
|
||||
|
||||
public void Write(bool value)
|
||||
{
|
||||
_binaryWriter.Write(value);
|
||||
}
|
||||
|
||||
public void Write(double value)
|
||||
{
|
||||
_binaryWriter.Write(value);
|
||||
}
|
||||
|
||||
/*
|
||||
public void Write(BufferElement bufferElement)
|
||||
{
|
||||
for (var i = 0; i < BufferElement.SIZE; i++)
|
||||
Write(bufferElement[i]);
|
||||
}
|
||||
*/
|
||||
|
||||
public void Write(byte[] bytes)
|
||||
{
|
||||
_binaryWriter.Write(bytes.Length);
|
||||
if (bytes.Length > 0)
|
||||
_binaryWriter.Write(bytes);
|
||||
}
|
||||
|
||||
public void Write(ushort[] ushorts)
|
||||
{
|
||||
var bytes = new byte[ushorts.Length << 1];
|
||||
Buffer.BlockCopy(ushorts, 0, bytes, 0, bytes.Length);
|
||||
Write(bytes);
|
||||
}
|
||||
|
||||
public void Write(int[] ints)
|
||||
{
|
||||
var bytes = new byte[ints.Length << 2];
|
||||
Buffer.BlockCopy(ints, 0, bytes, 0, bytes.Length);
|
||||
Write(bytes);
|
||||
}
|
||||
|
||||
public void Write(uint[] uints)
|
||||
{
|
||||
var bytes = new byte[uints.Length << 2];
|
||||
Buffer.BlockCopy(uints, 0, bytes, 0, bytes.Length);
|
||||
Write(bytes);
|
||||
}
|
||||
|
||||
public void Write(bool[] booleans)
|
||||
{
|
||||
var bytes = new byte[booleans.Length];
|
||||
for (var i = 0; i < bytes.Length; i++)
|
||||
{
|
||||
bytes[i] = (byte)(booleans[i] ? 0xff : 0x00);
|
||||
}
|
||||
Write(bytes);
|
||||
}
|
||||
|
||||
public void Write(MachineBase m)
|
||||
{
|
||||
WriteTypeName(m);
|
||||
m.GetObjectData(this);
|
||||
}
|
||||
|
||||
public void Write(AddressSpace mem)
|
||||
{
|
||||
mem.GetObjectData(this);
|
||||
}
|
||||
|
||||
public void Write(M6502 cpu)
|
||||
{
|
||||
cpu.GetObjectData(this);
|
||||
}
|
||||
|
||||
public void Write(PIA pia)
|
||||
{
|
||||
pia.GetObjectData(this);
|
||||
}
|
||||
|
||||
public void Write(TIA tia)
|
||||
{
|
||||
tia.GetObjectData(this);
|
||||
}
|
||||
|
||||
public void Write(TIASound tiaSound)
|
||||
{
|
||||
tiaSound.GetObjectData(this);
|
||||
}
|
||||
|
||||
public void Write(Maria maria)
|
||||
{
|
||||
maria.GetObjectData(this);
|
||||
}
|
||||
|
||||
public void Write(Cart cart)
|
||||
{
|
||||
WriteTypeName(cart);
|
||||
cart.GetObjectData(this);
|
||||
}
|
||||
|
||||
public void Write(RAM6116 ram6116)
|
||||
{
|
||||
ram6116.GetObjectData(this);
|
||||
}
|
||||
|
||||
public void Write(InputState inputState)
|
||||
{
|
||||
inputState.GetObjectData(this);
|
||||
}
|
||||
|
||||
public void WriteVersion(int version)
|
||||
{
|
||||
Write(0x78000087);
|
||||
Write(version);
|
||||
}
|
||||
|
||||
public void WriteOptional(byte[] bytes)
|
||||
{
|
||||
var hasBytes = (bytes != null);
|
||||
_binaryWriter.Write(hasBytes);
|
||||
if (!hasBytes)
|
||||
return;
|
||||
_binaryWriter.Write(bytes.Length);
|
||||
if (bytes.Length > 0)
|
||||
_binaryWriter.Write(bytes);
|
||||
}
|
||||
|
||||
public void WriteOptional(HSC7800 hsc7800)
|
||||
{
|
||||
var exist = (hsc7800 != null);
|
||||
Write(exist);
|
||||
if (!exist)
|
||||
return;
|
||||
hsc7800.GetObjectData(this);
|
||||
}
|
||||
|
||||
public void WriteOptional(Bios7800 bios7800)
|
||||
{
|
||||
var exist = (bios7800 != null);
|
||||
Write(exist);
|
||||
if (!exist)
|
||||
return;
|
||||
bios7800.GetObjectData(this);
|
||||
}
|
||||
|
||||
public void WriteOptional(PokeySound pokeySound)
|
||||
{
|
||||
var exist = (pokeySound != null);
|
||||
Write(exist);
|
||||
if (!exist)
|
||||
return;
|
||||
pokeySound.GetObjectData(this);
|
||||
}
|
||||
|
||||
#region Constructors
|
||||
|
||||
private SerializationContext()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new instance of <see cref="SerializationContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="binaryWriter"/>
|
||||
internal SerializationContext(BinaryWriter binaryWriter)
|
||||
{
|
||||
if (binaryWriter == null)
|
||||
throw new ArgumentNullException("binaryWriter");
|
||||
_binaryWriter = binaryWriter;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
void WriteTypeName(object o)
|
||||
{
|
||||
if (o == null)
|
||||
throw new Emu7800SerializationException("Type unexpectedly null.");
|
||||
var typeName = o.GetType().FullName;
|
||||
if (string.IsNullOrWhiteSpace(typeName))
|
||||
throw new Emu7800SerializationException("Unable to discover type name.");
|
||||
_binaryWriter.Write(typeName);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
1350
EMU7800/Core/TIA.cs
1350
EMU7800/Core/TIA.cs
File diff suppressed because it is too large
Load Diff
|
@ -1,361 +0,0 @@
|
|||
/*
|
||||
* TIASound.cs
|
||||
*
|
||||
* Sound emulation for the 2600. Based upon TIASound © 1997 by Ron Fries.
|
||||
*
|
||||
* Copyright © 2003, 2004 Mike Murphy
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************/
|
||||
/* */
|
||||
/* License Information and Copyright Notice */
|
||||
/* ======================================== */
|
||||
/* */
|
||||
/* TiaSound is Copyright(c) 1997 by Ron Fries */
|
||||
/* */
|
||||
/* This library is free software; you can redistribute it and/or modify it */
|
||||
/* under the terms of version 2 of the GNU Library General Public License */
|
||||
/* as published by the Free Software Foundation. */
|
||||
/* */
|
||||
/* This library is distributed in the hope that it will be useful, but */
|
||||
/* WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library */
|
||||
/* General Public License for more details. */
|
||||
/* To obtain a copy of the GNU Library General Public License, write to the */
|
||||
/* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||
/* */
|
||||
/* Any permitted reproduction of these routines, in whole or in part, must */
|
||||
/* bear this legend. */
|
||||
/* */
|
||||
/*****************************************************************************/
|
||||
|
||||
using System;
|
||||
|
||||
namespace EMU7800.Core
|
||||
{
|
||||
public sealed class TIASound
|
||||
{
|
||||
#region Constants and Tables
|
||||
|
||||
// Clock Source Clock Modifier Source Pattern
|
||||
const int
|
||||
SET_TO_1 = 0x00, // 0 0 0 0 3.58 Mhz/114 none (pure) none
|
||||
//POLY4 = 0x01, // 0 0 0 1 3.58 Mhz/114 none (pure) 4-bit poly
|
||||
//DIV31_POLY4 = 0x02, // 0 0 1 0 3.58 Mhz/114 divide by 31 4-bit poly
|
||||
//POLY5_POLY4 = 0x03, // 0 0 1 1 3.58 Mhz/114 5-bit poly 4-bit poly
|
||||
//PURE = 0x04, // 0 1 0 0 3.58 Mhz/114 none (pure) pure (~Q)
|
||||
//PURE2 = 0x05, // 0 1 0 1 3.58 Mhz/114 none (pure) pure (~Q)
|
||||
//DIV31_PURE = 0x06, // 0 1 1 0 3.58 Mhz/114 divide by 31 pure (~Q)
|
||||
//POLY5_2 = 0x07, // 0 1 1 1 3.58 Mhz/114 5-bit poly pure (~Q)
|
||||
POLY9 = 0x08; // 1 0 0 0 3.58 Mhz/114 none (pure) 9-bit poly
|
||||
//POLY5 = 0x09, // 1 0 0 1 3.58 Mhz/114 none (pure) 5-bit poly
|
||||
//DIV31_POLY5 = 0x0a, // 1 0 1 0 3.58 Mhz/114 divide by 31 5-bit poly
|
||||
//POLY5_POLY5 = 0x0b, // 1 0 1 1 3.58 Mhz/114 5-bit poly 5-bit poly
|
||||
//DIV3_PURE = 0x0c, // 1 1 0 0 1.19 Mhz/114 none (pure) pure (~Q)
|
||||
//DIV3_PURE2 = 0x0d, // 1 1 0 1 1.19 Mhz/114 none (pure) pure (~Q)
|
||||
//DIV93_PURE = 0x0e, // 1 1 1 0 1.19 Mhz/114 divide by 31 pure (~Q)
|
||||
//DIV3_POLY5 = 0x0f; // 1 1 1 1 1.19 Mhz/114 5-bit poly pure (~Q)
|
||||
|
||||
const int
|
||||
AUDC0 = 0x15, // audio control 0 (D3-0)
|
||||
AUDC1 = 0x16, // audio control 1 (D4-0)
|
||||
AUDF0 = 0x17, // audio frequency 0 (D4-0)
|
||||
AUDF1 = 0x18, // audio frequency 1 (D3-0)
|
||||
AUDV0 = 0x19, // audio volume 0 (D3-0)
|
||||
AUDV1 = 0x1a; // audio volume 1 (D3-0)
|
||||
|
||||
// The 4bit and 5bit patterns are the identical ones used in the tia chip.
|
||||
readonly byte[] Bit4 = new byte[] { 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0 }; // 2^4 - 1 = 15
|
||||
readonly byte[] Bit5 = new byte[] { 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1 }; // 2^5 - 1 = 31
|
||||
|
||||
// [Ron] treated the 'Div by 31' counter as another polynomial because of
|
||||
// the way it operates. It does not have a 50% duty cycle, but instead
|
||||
// has a 13:18 ratio (of course, 13+18 = 31). This could also be
|
||||
// implemented by using counters.
|
||||
readonly byte[] Div31 = new byte[] { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
// Rather than have a table with 511 entries, I use a random number
|
||||
readonly byte[] Bit9 = new byte[511]; // 2^9 - 1 = 511
|
||||
|
||||
readonly int[] P4 = new int[2]; // Position counter for the 4-bit POLY array
|
||||
readonly int[] P5 = new int[2]; // Position counter for the 5-bit POLY array
|
||||
readonly int[] P9 = new int[2]; // Position counter for the 9-bit POLY array
|
||||
|
||||
readonly int[] DivByNCounter = new int[2]; // Divide by n counter, one for each channel
|
||||
readonly int[] DivByNMaximum = new int[2]; // Divide by n maximum, one for each channel
|
||||
|
||||
readonly int _cpuClocksPerSample;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Object State
|
||||
|
||||
readonly MachineBase M;
|
||||
|
||||
// The TIA Sound registers
|
||||
readonly byte[] AUDC = new byte[2];
|
||||
readonly byte[] AUDF = new byte[2];
|
||||
readonly byte[] AUDV = new byte[2];
|
||||
|
||||
// The last output volume for each channel
|
||||
readonly byte[] OutputVol = new byte[2];
|
||||
|
||||
// Used to determine how much sound to render
|
||||
ulong LastUpdateCPUClock;
|
||||
|
||||
int BufferIndex;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Members
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
for (var chan = 0; chan < 2; chan++)
|
||||
{
|
||||
OutputVol[chan] = 0;
|
||||
DivByNCounter[chan] = 0;
|
||||
DivByNMaximum[chan] = 0;
|
||||
AUDC[chan] = 0;
|
||||
AUDF[chan] = 0;
|
||||
AUDV[chan] = 0;
|
||||
P4[chan] = 0;
|
||||
P5[chan] = 0;
|
||||
P9[chan] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void StartFrame()
|
||||
{
|
||||
LastUpdateCPUClock = M.CPU.Clock;
|
||||
BufferIndex = 0;
|
||||
}
|
||||
|
||||
public void EndFrame()
|
||||
{
|
||||
RenderSamples(M.FrameBuffer.SoundBufferByteLength - BufferIndex);
|
||||
}
|
||||
|
||||
public void Update(ushort addr, byte data)
|
||||
{
|
||||
if (M.CPU.Clock > LastUpdateCPUClock)
|
||||
{
|
||||
var updCPUClocks = (int)(M.CPU.Clock - LastUpdateCPUClock);
|
||||
var samples = updCPUClocks / _cpuClocksPerSample;
|
||||
RenderSamples(samples);
|
||||
LastUpdateCPUClock += (ulong)(samples * _cpuClocksPerSample);
|
||||
}
|
||||
|
||||
byte chan;
|
||||
|
||||
switch (addr)
|
||||
{
|
||||
case AUDC0:
|
||||
AUDC[0] = (byte)(data & 0x0f);
|
||||
chan = 0;
|
||||
break;
|
||||
case AUDC1:
|
||||
AUDC[1] = (byte)(data & 0x0f);
|
||||
chan = 1;
|
||||
break;
|
||||
case AUDF0:
|
||||
AUDF[0] = (byte)(data & 0x1f);
|
||||
chan = 0;
|
||||
break;
|
||||
case AUDF1:
|
||||
AUDF[1] = (byte)(data & 0x1f);
|
||||
chan = 1;
|
||||
break;
|
||||
case AUDV0:
|
||||
AUDV[0] = (byte)(data & 0x0f);
|
||||
chan = 0;
|
||||
break;
|
||||
case AUDV1:
|
||||
AUDV[1] = (byte)(data & 0x0f);
|
||||
chan = 1;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
byte new_divn_max;
|
||||
|
||||
if (AUDC[chan] == SET_TO_1)
|
||||
{
|
||||
// indicate the clock is zero so no process will occur
|
||||
new_divn_max = 0;
|
||||
// and set the output to the selected volume
|
||||
OutputVol[chan] = AUDV[chan];
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise calculate the 'divide by N' value
|
||||
new_divn_max = (byte)(AUDF[chan] + 1);
|
||||
// if bits D2 & D3 are set, then multiply the 'div by n' count by 3
|
||||
if ((AUDC[chan] & 0x0c) == 0x0c)
|
||||
{
|
||||
new_divn_max *= 3;
|
||||
}
|
||||
}
|
||||
|
||||
// only reset those channels that have changed
|
||||
if (new_divn_max != DivByNMaximum[chan])
|
||||
{
|
||||
DivByNMaximum[chan] = new_divn_max;
|
||||
|
||||
// if the channel is now volume only or was volume only...
|
||||
if (DivByNCounter[chan] == 0 || new_divn_max == 0)
|
||||
{
|
||||
// reset the counter (otherwise let it complete the previous)
|
||||
DivByNCounter[chan] = new_divn_max;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
private TIASound()
|
||||
{
|
||||
var r = new Random();
|
||||
r.NextBytes(Bit9);
|
||||
for (var i = 0; i < Bit9.Length; i++)
|
||||
{
|
||||
Bit9[i] &= 0x01;
|
||||
}
|
||||
}
|
||||
|
||||
public TIASound(MachineBase m, int cpuClocksPerSample) : this()
|
||||
{
|
||||
if (m == null)
|
||||
throw new ArgumentNullException("m");
|
||||
if (cpuClocksPerSample <= 0)
|
||||
throw new ArgumentException("cpuClocksPerSample must be positive.");
|
||||
|
||||
M = m;
|
||||
_cpuClocksPerSample = cpuClocksPerSample;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Serialization Members
|
||||
|
||||
public TIASound(DeserializationContext input, MachineBase m, int cpuClocksPerSample) : this(m, cpuClocksPerSample)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException("input");
|
||||
|
||||
input.CheckVersion(1);
|
||||
Bit9 = input.ReadExpectedBytes(511);
|
||||
P4 = input.ReadIntegers(2);
|
||||
P5 = input.ReadIntegers(2);
|
||||
P9 = input.ReadIntegers(2);
|
||||
DivByNCounter = input.ReadIntegers(2);
|
||||
DivByNMaximum = input.ReadIntegers(2);
|
||||
AUDC = input.ReadExpectedBytes(2);
|
||||
AUDF = input.ReadExpectedBytes(2);
|
||||
AUDV = input.ReadExpectedBytes(2);
|
||||
OutputVol = input.ReadExpectedBytes(2);
|
||||
LastUpdateCPUClock = input.ReadUInt64();
|
||||
BufferIndex = input.ReadInt32();
|
||||
}
|
||||
|
||||
public void GetObjectData(SerializationContext output)
|
||||
{
|
||||
if (output == null)
|
||||
throw new ArgumentNullException("output");
|
||||
|
||||
output.WriteVersion(1);
|
||||
output.Write(Bit9);
|
||||
output.Write(P4);
|
||||
output.Write(P5);
|
||||
output.Write(P9);
|
||||
output.Write(DivByNCounter);
|
||||
output.Write(DivByNMaximum);
|
||||
output.Write(AUDC);
|
||||
output.Write(AUDF);
|
||||
output.Write(AUDV);
|
||||
output.Write(OutputVol);
|
||||
output.Write(LastUpdateCPUClock);
|
||||
output.Write(BufferIndex);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
void RenderSamples(int count)
|
||||
{
|
||||
for (; BufferIndex < M.FrameBuffer.SoundBufferByteLength && count-- > 0; BufferIndex++)
|
||||
{
|
||||
if (DivByNCounter[0] > 1)
|
||||
{
|
||||
DivByNCounter[0]--;
|
||||
}
|
||||
else if (DivByNCounter[0] == 1)
|
||||
{
|
||||
DivByNCounter[0] = DivByNMaximum[0];
|
||||
ProcessChannel(0);
|
||||
}
|
||||
if (DivByNCounter[1] > 1)
|
||||
{
|
||||
DivByNCounter[1]--;
|
||||
}
|
||||
else if (DivByNCounter[1] == 1)
|
||||
{
|
||||
DivByNCounter[1] = DivByNMaximum[1];
|
||||
ProcessChannel(1);
|
||||
}
|
||||
|
||||
M.FrameBuffer.SoundBuffer[BufferIndex] += (byte)(OutputVol[0] + OutputVol[1]);
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessChannel(int chan)
|
||||
{
|
||||
// the P5 counter has multiple uses, so we inc it here
|
||||
if (++P5[chan] >= 31)
|
||||
{ // POLY5 size: 2^5 - 1 = 31
|
||||
P5[chan] = 0;
|
||||
}
|
||||
|
||||
// check clock modifier for clock tick
|
||||
if ((AUDC[chan] & 0x02) == 0 ||
|
||||
((AUDC[chan] & 0x01) == 0 && Div31[P5[chan]] == 1) ||
|
||||
((AUDC[chan] & 0x01) == 1 && Bit5[P5[chan]] == 1))
|
||||
{
|
||||
if ((AUDC[chan] & 0x04) != 0)
|
||||
{ // pure modified clock selected
|
||||
OutputVol[chan] = (OutputVol[chan] != 0) ? (byte)0 : AUDV[chan];
|
||||
}
|
||||
else if ((AUDC[chan] & 0x08) != 0)
|
||||
{ // check for poly5/poly9
|
||||
if (AUDC[chan] == POLY9)
|
||||
{ // check for poly9
|
||||
if (++P9[chan] >= 511)
|
||||
{ // poly9 size: 2^9 - 1 = 511
|
||||
P9[chan] = 0;
|
||||
}
|
||||
OutputVol[chan] = (Bit9[P9[chan]] == 1) ? AUDV[chan] : (byte)0;
|
||||
}
|
||||
else
|
||||
{ // must be poly5
|
||||
OutputVol[chan] = (Bit5[P5[chan]] == 1) ? AUDV[chan] : (byte)0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // poly4 is the only remaining possibility
|
||||
if (++P4[chan] >= 15)
|
||||
{ // POLY4 size: 2^4 - 1 = 15
|
||||
P4[chan] = 0;
|
||||
}
|
||||
OutputVol[chan] = (Bit4[P4[chan]] == 1) ? AUDV[chan] : (byte)0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,424 +0,0 @@
|
|||
/*
|
||||
* TIATables.cs
|
||||
*
|
||||
* Mask tables for the Television Interface Adaptor class. All derived from
|
||||
* Bradford Mott's Stella code.
|
||||
*
|
||||
* Copyright © 2003, 2004 Mike Murphy
|
||||
*
|
||||
*/
|
||||
namespace EMU7800.Core
|
||||
{
|
||||
public static class TIATables
|
||||
{
|
||||
public static readonly TIACxPairFlags[] CollisionMask = BuildCollisionMaskTable();
|
||||
public static readonly uint[][] PFMask = BuildPFMaskTable();
|
||||
public static readonly bool[][] BLMask = BuildBLMaskTable();
|
||||
public static readonly bool[][][] MxMask = BuildMxMaskTable();
|
||||
public static readonly byte[][][] PxMask = BuildPxMaskTable();
|
||||
public static readonly byte[] GRPReflect = BuildGRPReflectTable();
|
||||
|
||||
public static readonly int[] NTSCPalette =
|
||||
{
|
||||
0x000000, 0x000000, 0x4a4a4a, 0x4a4a4a,
|
||||
0x6f6f6f, 0x6f6f6f, 0x8e8e8e, 0x8e8e8e,
|
||||
0xaaaaaa, 0xaaaaaa, 0xc0c0c0, 0xc0c0c0,
|
||||
0xd6d6d6, 0xd6d6d6, 0xececec, 0xececec,
|
||||
|
||||
0x484800, 0x484800, 0x69690f, 0x69690f,
|
||||
0x86861d, 0x86861d, 0xa2a22a, 0xa2a22a,
|
||||
0xbbbb35, 0xbbbb35, 0xd2d240, 0xd2d240,
|
||||
0xe8e84a, 0xe8e84a, 0xfcfc54, 0xfcfc54,
|
||||
|
||||
0x7c2c00, 0x7c2c00, 0x904811, 0x904811,
|
||||
0xa26221, 0xa26221, 0xb47a30, 0xb47a30,
|
||||
0xc3903d, 0xc3903d, 0xd2a44a, 0xd2a44a,
|
||||
0xdfb755, 0xdfb755, 0xecc860, 0xecc860,
|
||||
|
||||
0x901c00, 0x901c00, 0xa33915, 0xa33915,
|
||||
0xb55328, 0xb55328, 0xc66c3a, 0xc66c3a,
|
||||
0xd5824a, 0xd5824a, 0xe39759, 0xe39759,
|
||||
0xf0aa67, 0xf0aa67, 0xfcbc74, 0xfcbc74,
|
||||
|
||||
0x940000, 0x940000, 0xa71a1a, 0xa71a1a,
|
||||
0xb83232, 0xb83232, 0xc84848, 0xc84848,
|
||||
0xd65c5c, 0xd65c5c, 0xe46f6f, 0xe46f6f,
|
||||
0xf08080, 0xf08080, 0xfc9090, 0xfc9090,
|
||||
|
||||
0x840064, 0x840064, 0x97197a, 0x97197a,
|
||||
0xa8308f, 0xa8308f, 0xb846a2, 0xb846a2,
|
||||
0xc659b3, 0xc659b3, 0xd46cc3, 0xd46cc3,
|
||||
0xe07cd2, 0xe07cd2, 0xec8ce0, 0xec8ce0,
|
||||
|
||||
0x500084, 0x500084, 0x68199a, 0x68199a,
|
||||
0x7d30ad, 0x7d30ad, 0x9246c0, 0x9246c0,
|
||||
0xa459d0, 0xa459d0, 0xb56ce0, 0xb56ce0,
|
||||
0xc57cee, 0xc57cee, 0xd48cfc, 0xd48cfc,
|
||||
|
||||
0x140090, 0x140090, 0x331aa3, 0x331aa3,
|
||||
0x4e32b5, 0x4e32b5, 0x6848c6, 0x6848c6,
|
||||
0x7f5cd5, 0x7f5cd5, 0x956fe3, 0x956fe3,
|
||||
0xa980f0, 0xa980f0, 0xbc90fc, 0xbc90fc,
|
||||
|
||||
0x000094, 0x000094, 0x181aa7, 0x181aa7,
|
||||
0x2d32b8, 0x2d32b8, 0x4248c8, 0x4248c8,
|
||||
0x545cd6, 0x545cd6, 0x656fe4, 0x656fe4,
|
||||
0x7580f0, 0x7580f0, 0x8490fc, 0x8490fc,
|
||||
|
||||
0x001c88, 0x001c88, 0x183b9d, 0x183b9d,
|
||||
0x2d57b0, 0x2d57b0, 0x4272c2, 0x4272c2,
|
||||
0x548ad2, 0x548ad2, 0x65a0e1, 0x65a0e1,
|
||||
0x75b5ef, 0x75b5ef, 0x84c8fc, 0x84c8fc,
|
||||
|
||||
0x003064, 0x003064, 0x185080, 0x185080,
|
||||
0x2d6d98, 0x2d6d98, 0x4288b0, 0x4288b0,
|
||||
0x54a0c5, 0x54a0c5, 0x65b7d9, 0x65b7d9,
|
||||
0x75cceb, 0x75cceb, 0x84e0fc, 0x84e0fc,
|
||||
|
||||
0x004030, 0x004030, 0x18624e, 0x18624e,
|
||||
0x2d8169, 0x2d8169, 0x429e82, 0x429e82,
|
||||
0x54b899, 0x54b899, 0x65d1ae, 0x65d1ae,
|
||||
0x75e7c2, 0x75e7c2, 0x84fcd4, 0x84fcd4,
|
||||
|
||||
0x004400, 0x004400, 0x1a661a, 0x1a661a,
|
||||
0x328432, 0x328432, 0x48a048, 0x48a048,
|
||||
0x5cba5c, 0x5cba5c, 0x6fd26f, 0x6fd26f,
|
||||
0x80e880, 0x80e880, 0x90fc90, 0x90fc90,
|
||||
|
||||
0x143c00, 0x143c00, 0x355f18, 0x355f18,
|
||||
0x527e2d, 0x527e2d, 0x6e9c42, 0x6e9c42,
|
||||
0x87b754, 0x87b754, 0x9ed065, 0x9ed065,
|
||||
0xb4e775, 0xb4e775, 0xc8fc84, 0xc8fc84,
|
||||
|
||||
0x303800, 0x303800, 0x505916, 0x505916,
|
||||
0x6d762b, 0x6d762b, 0x88923e, 0x88923e,
|
||||
0xa0ab4f, 0xa0ab4f, 0xb7c25f, 0xb7c25f,
|
||||
0xccd86e, 0xccd86e, 0xe0ec7c, 0xe0ec7c,
|
||||
|
||||
0x482c00, 0x482c00, 0x694d14, 0x694d14,
|
||||
0x866a26, 0x866a26, 0xa28638, 0xa28638,
|
||||
0xbb9f47, 0xbb9f47, 0xd2b656, 0xd2b656,
|
||||
0xe8cc63, 0xe8cc63, 0xfce070, 0xfce070
|
||||
};
|
||||
|
||||
public static readonly int[] PALPalette =
|
||||
{
|
||||
0x000000, 0x000000, 0x2b2b2b, 0x2b2b2b,
|
||||
0x525252, 0x525252, 0x767676, 0x767676,
|
||||
0x979797, 0x979797, 0xb6b6b6, 0xb6b6b6,
|
||||
0xd2d2d2, 0xd2d2d2, 0xececec, 0xececec,
|
||||
|
||||
0x000000, 0x000000, 0x2b2b2b, 0x2b2b2b,
|
||||
0x525252, 0x525252, 0x767676, 0x767676,
|
||||
0x979797, 0x979797, 0xb6b6b6, 0xb6b6b6,
|
||||
0xd2d2d2, 0xd2d2d2, 0xececec, 0xececec,
|
||||
|
||||
0x805800, 0x000000, 0x96711a, 0x2b2b2b,
|
||||
0xab8732, 0x525252, 0xbe9c48, 0x767676,
|
||||
0xcfaf5c, 0x979797, 0xdfc06f, 0xb6b6b6,
|
||||
0xeed180, 0xd2d2d2, 0xfce090, 0xececec,
|
||||
|
||||
0x445c00, 0x000000, 0x5e791a, 0x2b2b2b,
|
||||
0x769332, 0x525252, 0x8cac48, 0x767676,
|
||||
0xa0c25c, 0x979797, 0xb3d76f, 0xb6b6b6,
|
||||
0xc4ea80, 0xd2d2d2, 0xd4fc90, 0xececec,
|
||||
|
||||
0x703400, 0x000000, 0x89511a, 0x2b2b2b,
|
||||
0xa06b32, 0x525252, 0xb68448, 0x767676,
|
||||
0xc99a5c, 0x979797, 0xdcaf6f, 0xb6b6b6,
|
||||
0xecc280, 0xd2d2d2, 0xfcd490, 0xececec,
|
||||
|
||||
0x006414, 0x000000, 0x1a8035, 0x2b2b2b,
|
||||
0x329852, 0x525252, 0x48b06e, 0x767676,
|
||||
0x5cc587, 0x979797, 0x6fd99e, 0xb6b6b6,
|
||||
0x80ebb4, 0xd2d2d2, 0x90fcc8, 0xececec,
|
||||
|
||||
0x700014, 0x000000, 0x891a35, 0x2b2b2b,
|
||||
0xa03252, 0x525252, 0xb6486e, 0x767676,
|
||||
0xc95c87, 0x979797, 0xdc6f9e, 0xb6b6b6,
|
||||
0xec80b4, 0xd2d2d2, 0xfc90c8, 0xececec,
|
||||
|
||||
0x005c5c, 0x000000, 0x1a7676, 0x2b2b2b,
|
||||
0x328e8e, 0x525252, 0x48a4a4, 0x767676,
|
||||
0x5cb8b8, 0x979797, 0x6fcbcb, 0xb6b6b6,
|
||||
0x80dcdc, 0xd2d2d2, 0x90ecec, 0xececec,
|
||||
|
||||
0x70005c, 0x000000, 0x841a74, 0x2b2b2b,
|
||||
0x963289, 0x525252, 0xa8489e, 0x767676,
|
||||
0xb75cb0, 0x979797, 0xc66fc1, 0xb6b6b6,
|
||||
0xd380d1, 0xd2d2d2, 0xe090e0, 0xececec,
|
||||
|
||||
0x003c70, 0x000000, 0x195a89, 0x2b2b2b,
|
||||
0x2f75a0, 0x525252, 0x448eb6, 0x767676,
|
||||
0x57a5c9, 0x979797, 0x68badc, 0xb6b6b6,
|
||||
0x79ceec, 0xd2d2d2, 0x88e0fc, 0xececec,
|
||||
|
||||
0x580070, 0x000000, 0x6e1a89, 0x2b2b2b,
|
||||
0x8332a0, 0x525252, 0x9648b6, 0x767676,
|
||||
0xa75cc9, 0x979797, 0xb76fdc, 0xb6b6b6,
|
||||
0xc680ec, 0xd2d2d2, 0xd490fc, 0xececec,
|
||||
|
||||
0x002070, 0x000000, 0x193f89, 0x2b2b2b,
|
||||
0x2f5aa0, 0x525252, 0x4474b6, 0x767676,
|
||||
0x578bc9, 0x979797, 0x68a1dc, 0xb6b6b6,
|
||||
0x79b5ec, 0xd2d2d2, 0x88c8fc, 0xececec,
|
||||
|
||||
0x340080, 0x000000, 0x4a1a96, 0x2b2b2b,
|
||||
0x5f32ab, 0x525252, 0x7248be, 0x767676,
|
||||
0x835ccf, 0x979797, 0x936fdf, 0xb6b6b6,
|
||||
0xa280ee, 0xd2d2d2, 0xb090fc, 0xececec,
|
||||
|
||||
0x000088, 0x000000, 0x1a1a9d, 0x2b2b2b,
|
||||
0x3232b0, 0x525252, 0x4848c2, 0x767676,
|
||||
0x5c5cd2, 0x979797, 0x6f6fe1, 0xb6b6b6,
|
||||
0x8080ef, 0xd2d2d2, 0x9090fc, 0xececec,
|
||||
|
||||
0x000000, 0x000000, 0x2b2b2b, 0x2b2b2b,
|
||||
0x525252, 0x525252, 0x767676, 0x767676,
|
||||
0x979797, 0x979797, 0xb6b6b6, 0xb6b6b6,
|
||||
0xd2d2d2, 0xd2d2d2, 0xececec, 0xececec,
|
||||
|
||||
0x000000, 0x000000, 0x2b2b2b, 0x2b2b2b,
|
||||
0x525252, 0x525252, 0x767676, 0x767676,
|
||||
0x979797, 0x979797, 0xb6b6b6, 0xb6b6b6,
|
||||
0xd2d2d2, 0xd2d2d2, 0xececec, 0xececec
|
||||
};
|
||||
|
||||
static uint[][] BuildPFMaskTable()
|
||||
{
|
||||
var tabl = new uint[2][];
|
||||
tabl[0] = new uint[160];
|
||||
tabl[1] = new uint[160];
|
||||
|
||||
for (var i = 0; i < 20; i++)
|
||||
{
|
||||
uint mask = 0;
|
||||
if (i < 4)
|
||||
{
|
||||
mask = (uint)(1 << i);
|
||||
}
|
||||
else if (i < 12)
|
||||
{
|
||||
mask = (uint)(1 << (11 + 4 - i));
|
||||
}
|
||||
else if (i < 20)
|
||||
{
|
||||
mask = (uint)(1 << i);
|
||||
}
|
||||
for (var j = 0; j < 4; j++)
|
||||
{
|
||||
// for non-reflected mode
|
||||
tabl[0][4 * i + j] = mask;
|
||||
tabl[0][80 + 4 * i + j] = mask;
|
||||
|
||||
// for reflected mode
|
||||
tabl[1][4 * i + j] = mask;
|
||||
tabl[1][159 - 4 * i - j] = mask;
|
||||
}
|
||||
}
|
||||
return tabl;
|
||||
}
|
||||
|
||||
static bool[][] BuildBLMaskTable()
|
||||
{
|
||||
var tabl = new bool[4][];
|
||||
for (var size = 0; size < 4; size++)
|
||||
{
|
||||
tabl[size] = new bool[160];
|
||||
for (var i = 0; i < 160; i++)
|
||||
{
|
||||
tabl[size][i] = false;
|
||||
}
|
||||
for (var i = 0; i < (1 << size); i++)
|
||||
{
|
||||
tabl[size][i] = true;
|
||||
}
|
||||
}
|
||||
return tabl;
|
||||
}
|
||||
|
||||
static bool[][][] BuildMxMaskTable()
|
||||
{
|
||||
var tabl = new bool[4][][];
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
tabl[i] = new bool[8][];
|
||||
for (var j = 0; j < 8; j++)
|
||||
{
|
||||
tabl[i][j] = new bool[160];
|
||||
for (var k = 0; k < 160; k++)
|
||||
{
|
||||
tabl[i][j][k] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var size = 0; size < 4; size++)
|
||||
{
|
||||
for (var i = 0; i < (1 << size); i++)
|
||||
{
|
||||
tabl[size][0][i] = true;
|
||||
|
||||
tabl[size][1][i] = true;
|
||||
tabl[size][1][i + 16] = true;
|
||||
|
||||
tabl[size][2][i] = true;
|
||||
tabl[size][2][i + 32] = true;
|
||||
|
||||
tabl[size][3][i] = true;
|
||||
tabl[size][3][i + 16] = true;
|
||||
tabl[size][3][i + 32] = true;
|
||||
|
||||
tabl[size][4][i] = true;
|
||||
tabl[size][4][i + 64] = true;
|
||||
|
||||
tabl[size][5][i] = true;
|
||||
|
||||
tabl[size][6][i] = true;
|
||||
tabl[size][6][i + 32] = true;
|
||||
tabl[size][6][i + 64] = true;
|
||||
|
||||
tabl[size][7][i] = true;
|
||||
}
|
||||
}
|
||||
return tabl;
|
||||
}
|
||||
|
||||
static byte[][][] BuildPxMaskTable()
|
||||
{
|
||||
// [suppress mode, nusiz, pixel]
|
||||
// suppress=1: suppress on
|
||||
// suppress=0: suppress off
|
||||
var tabl = new byte[2][][]; //2 8 160
|
||||
tabl[0] = new byte[8][];
|
||||
tabl[1] = new byte[8][];
|
||||
for (var nusiz = 0; nusiz < 8; nusiz++)
|
||||
{
|
||||
tabl[0][nusiz] = new byte[160];
|
||||
tabl[1][nusiz] = new byte[160];
|
||||
for (var hpos = 0; hpos < 160; hpos++)
|
||||
{
|
||||
// nusiz:
|
||||
// 0: one copy
|
||||
// 1: two copies-close
|
||||
// 2: two copies-med
|
||||
// 3: three copies-close
|
||||
// 4: two copies-wide
|
||||
// 5: double size player
|
||||
// 6: 3 copies medium
|
||||
// 7: quad sized player
|
||||
tabl[0][nusiz][hpos] = tabl[1][nusiz][hpos] = 0;
|
||||
if (nusiz >= 0 && nusiz <= 4 || nusiz == 6)
|
||||
{
|
||||
if (hpos >= 0 && hpos < 8)
|
||||
{
|
||||
tabl[0][nusiz][hpos] = (byte)(1 << (7 - hpos));
|
||||
}
|
||||
}
|
||||
if (nusiz == 1 || nusiz == 3)
|
||||
{
|
||||
if (hpos >= 16 && hpos < 24)
|
||||
{
|
||||
tabl[0][nusiz][hpos] = (byte)(1 << (23 - hpos));
|
||||
tabl[1][nusiz][hpos] = (byte)(1 << (23 - hpos));
|
||||
}
|
||||
}
|
||||
if (nusiz == 2 || nusiz == 3 || nusiz == 6)
|
||||
{
|
||||
if (hpos >= 32 && hpos < 40)
|
||||
{
|
||||
tabl[0][nusiz][hpos] = (byte)(1 << (39 - hpos));
|
||||
tabl[1][nusiz][hpos] = (byte)(1 << (39 - hpos));
|
||||
}
|
||||
}
|
||||
if (nusiz == 4 || nusiz == 6)
|
||||
{
|
||||
if (hpos >= 64 && hpos < 72)
|
||||
{
|
||||
tabl[0][nusiz][hpos] = (byte)(1 << (71 - hpos));
|
||||
tabl[1][nusiz][hpos] = (byte)(1 << (71 - hpos));
|
||||
}
|
||||
}
|
||||
if (nusiz == 5)
|
||||
{
|
||||
if (hpos >= 0 && hpos < 16)
|
||||
{
|
||||
tabl[0][nusiz][hpos] = (byte)(1 << ((15 - hpos) >> 1));
|
||||
}
|
||||
}
|
||||
if (nusiz == 7)
|
||||
{
|
||||
if (hpos >= 0 && hpos < 32)
|
||||
{
|
||||
tabl[0][nusiz][hpos] = (byte)(1 << ((31 - hpos) >> 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var shift = nusiz == 5 || nusiz == 7 ? 2 : 1;
|
||||
while (shift-- > 0)
|
||||
{
|
||||
for (var i = 159; i > 0; i--)
|
||||
{
|
||||
tabl[0][nusiz][i] = tabl[0][nusiz][i - 1];
|
||||
tabl[1][nusiz][i] = tabl[1][nusiz][i - 1];
|
||||
}
|
||||
tabl[0][nusiz][0] = tabl[1][nusiz][0] = 0;
|
||||
}
|
||||
}
|
||||
return tabl;
|
||||
}
|
||||
|
||||
static byte[] BuildGRPReflectTable()
|
||||
{
|
||||
var tabl = new byte[256];
|
||||
|
||||
for (var i = 0; i < 256; i++)
|
||||
{
|
||||
var s = (byte)i;
|
||||
var r = (byte)0;
|
||||
for (var j = 0; j < 8; j++)
|
||||
{
|
||||
r <<= 1;
|
||||
r |= (byte)(s & 1);
|
||||
s >>= 1;
|
||||
}
|
||||
tabl[i] = r;
|
||||
}
|
||||
return tabl;
|
||||
}
|
||||
|
||||
static bool tstCx(int i, TIACxFlags cxf1, TIACxFlags cxf2)
|
||||
{
|
||||
var f1 = (int)cxf1;
|
||||
var f2 = (int)cxf2;
|
||||
return ((i & f1) != 0) && ((i & f2) != 0);
|
||||
}
|
||||
|
||||
static TIACxPairFlags[] BuildCollisionMaskTable()
|
||||
{
|
||||
var tabl = new TIACxPairFlags[64];
|
||||
|
||||
for (var i = 0; i < 64; i++)
|
||||
{
|
||||
tabl[i] = 0;
|
||||
if (tstCx(i, TIACxFlags.M0, TIACxFlags.P1)) { tabl[i] |= TIACxPairFlags.M0P1; }
|
||||
if (tstCx(i, TIACxFlags.M0, TIACxFlags.P0)) { tabl[i] |= TIACxPairFlags.M0P0; }
|
||||
if (tstCx(i, TIACxFlags.M1, TIACxFlags.P0)) { tabl[i] |= TIACxPairFlags.M1P0; }
|
||||
if (tstCx(i, TIACxFlags.M1, TIACxFlags.P1)) { tabl[i] |= TIACxPairFlags.M1P1; }
|
||||
if (tstCx(i, TIACxFlags.P0, TIACxFlags.PF)) { tabl[i] |= TIACxPairFlags.P0PF; }
|
||||
if (tstCx(i, TIACxFlags.P0, TIACxFlags.BL)) { tabl[i] |= TIACxPairFlags.P0BL; }
|
||||
if (tstCx(i, TIACxFlags.P1, TIACxFlags.PF)) { tabl[i] |= TIACxPairFlags.P1PF; }
|
||||
if (tstCx(i, TIACxFlags.P1, TIACxFlags.BL)) { tabl[i] |= TIACxPairFlags.P1BL; }
|
||||
if (tstCx(i, TIACxFlags.M0, TIACxFlags.PF)) { tabl[i] |= TIACxPairFlags.M0PF; }
|
||||
if (tstCx(i, TIACxFlags.M0, TIACxFlags.BL)) { tabl[i] |= TIACxPairFlags.M0BL; }
|
||||
if (tstCx(i, TIACxFlags.M1, TIACxFlags.PF)) { tabl[i] |= TIACxPairFlags.M1PF; }
|
||||
if (tstCx(i, TIACxFlags.M1, TIACxFlags.BL)) { tabl[i] |= TIACxPairFlags.M1BL; }
|
||||
if (tstCx(i, TIACxFlags.BL, TIACxFlags.PF)) { tabl[i] |= TIACxPairFlags.BLPF; }
|
||||
if (tstCx(i, TIACxFlags.P0, TIACxFlags.P1)) { tabl[i] |= TIACxPairFlags.P0P1; }
|
||||
if (tstCx(i, TIACxFlags.M0, TIACxFlags.M1)) { tabl[i] |= TIACxPairFlags.M0M1; }
|
||||
}
|
||||
return tabl;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,127 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{E01193DF-F104-4B95-9D1B-FAD830F6F620}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>EMU7800</RootNamespace>
|
||||
<AssemblyName>EMU7800</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Class1.cs" />
|
||||
<Compile Include="Core\AddressSpace.cs" />
|
||||
<Compile Include="Core\Bios7800.cs" />
|
||||
<Compile Include="Core\BufferElement.cs" />
|
||||
<Compile Include="Core\Cart.cs" />
|
||||
<Compile Include="Core\Cart7808.cs" />
|
||||
<Compile Include="Core\Cart7816.cs" />
|
||||
<Compile Include="Core\Cart7832.cs" />
|
||||
<Compile Include="Core\Cart7832P.cs" />
|
||||
<Compile Include="Core\Cart7848.cs" />
|
||||
<Compile Include="Core\Cart78AB.cs" />
|
||||
<Compile Include="Core\Cart78AC.cs" />
|
||||
<Compile Include="Core\Cart78S4.cs" />
|
||||
<Compile Include="Core\Cart78S9.cs" />
|
||||
<Compile Include="Core\Cart78SG.cs" />
|
||||
<Compile Include="Core\Cart78SGP.cs" />
|
||||
<Compile Include="Core\CartA16K.cs" />
|
||||
<Compile Include="Core\CartA16KR.cs" />
|
||||
<Compile Include="Core\CartA2K.cs" />
|
||||
<Compile Include="Core\CartA32K.cs" />
|
||||
<Compile Include="Core\CartA32KR.cs" />
|
||||
<Compile Include="Core\CartA4K.cs" />
|
||||
<Compile Include="Core\CartA8K.cs" />
|
||||
<Compile Include="Core\CartA8KR.cs" />
|
||||
<Compile Include="Core\CartCBS12K.cs" />
|
||||
<Compile Include="Core\CartDC8K.cs" />
|
||||
<Compile Include="Core\CartDPC.cs" />
|
||||
<Compile Include="Core\CartMN16K.cs" />
|
||||
<Compile Include="Core\CartPB8K.cs" />
|
||||
<Compile Include="Core\CartTV8K.cs" />
|
||||
<Compile Include="Core\CartType.cs" />
|
||||
<Compile Include="Core\ConsoleSwitch.cs" />
|
||||
<Compile Include="Core\Controller.cs" />
|
||||
<Compile Include="Core\ControllerAction.cs" />
|
||||
<Compile Include="Core\DeserializationContext.cs" />
|
||||
<Compile Include="Core\Emu7800Exception.cs" />
|
||||
<Compile Include="Core\Emu7800SerializationException.cs" />
|
||||
<Compile Include="Core\FontRenderer.cs" />
|
||||
<Compile Include="Core\FrameBuffer.cs" />
|
||||
<Compile Include="Core\HSC7800.cs" />
|
||||
<Compile Include="Core\IDevice.cs" />
|
||||
<Compile Include="Core\ILogger.cs" />
|
||||
<Compile Include="Core\InputState.cs" />
|
||||
<Compile Include="Core\M6502.cs" />
|
||||
<Compile Include="Core\M6502DASM.cs" />
|
||||
<Compile Include="Core\Machine2600.cs" />
|
||||
<Compile Include="Core\Machine2600NTSC.cs" />
|
||||
<Compile Include="Core\Machine2600PAL.cs" />
|
||||
<Compile Include="Core\Machine7800.cs" />
|
||||
<Compile Include="Core\Machine7800NTSC.cs" />
|
||||
<Compile Include="Core\Machine7800PAL.cs" />
|
||||
<Compile Include="Core\MachineBase.cs" />
|
||||
<Compile Include="Core\MachineInput.cs" />
|
||||
<Compile Include="Core\MachineType.cs" />
|
||||
<Compile Include="Core\Maria.cs" />
|
||||
<Compile Include="Core\MariaTables.cs" />
|
||||
<Compile Include="Core\NullDevice.cs" />
|
||||
<Compile Include="Core\NullLogger.cs" />
|
||||
<Compile Include="Core\PIA.cs" />
|
||||
<Compile Include="Core\PokeySound.cs" />
|
||||
<Compile Include="Core\RAM6116.cs" />
|
||||
<Compile Include="Core\SerializationContext.cs" />
|
||||
<Compile Include="Core\TIA.cs" />
|
||||
<Compile Include="Core\TIASound.cs" />
|
||||
<Compile Include="Core\TIATables.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Win\GameProgram.cs" />
|
||||
<Compile Include="Win\GameProgramLibrary.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Core\LICENSE.TXT" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>copy /y $(TargetDir)$(TargetFileName) $(ProjectDir)..\references\$(TargetFileName)
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -1,20 +0,0 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 11.00
|
||||
# Visual Studio 2010
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EMU7800", "EMU7800.csproj", "{E01193DF-F104-4B95-9D1B-FAD830F6F620}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{E01193DF-F104-4B95-9D1B-FAD830F6F620}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E01193DF-F104-4B95-9D1B-FAD830F6F620}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E01193DF-F104-4B95-9D1B-FAD830F6F620}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E01193DF-F104-4B95-9D1B-FAD830F6F620}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -1,36 +0,0 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("EMU7800")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("EMU7800")]
|
||||
[assembly: AssemblyCopyright("")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("b1f45280-2ae9-468c-8db8-4663fe6f2300")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
* GameProgram.cs
|
||||
*
|
||||
* Represents attribute data associated with ROMs
|
||||
*
|
||||
* Copyright 2003, 2004, 2010 © Mike Murphy
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* unlike EMU7800 Core stuff, this has been hacked around a bit
|
||||
*/
|
||||
|
||||
using System.Text;
|
||||
using EMU7800.Core;
|
||||
|
||||
namespace EMU7800.Win
|
||||
{
|
||||
public class GameProgram
|
||||
{
|
||||
public string MD5 { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Manufacturer { get; set; }
|
||||
public string Author { get; set; }
|
||||
public string Year { get; set; }
|
||||
public string ModelNo { get; set; }
|
||||
public string Rarity { get; set; }
|
||||
public CartType CartType { get; set; }
|
||||
public MachineType MachineType { get; set; }
|
||||
public Controller LController { get; set; }
|
||||
public Controller RController { get; set; }
|
||||
public string HelpUri { get; set; }
|
||||
|
||||
public string DiscoveredRomFullName { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var s = new StringBuilder("GameSettings:\n");
|
||||
s.AppendFormat(" MD5: {0}\n", MD5);
|
||||
s.AppendFormat(" Title: {0}\n", Title);
|
||||
s.AppendFormat(" Manufacturer: {0}\n", Manufacturer);
|
||||
s.AppendFormat(" Author: {0}\n", Author);
|
||||
s.AppendFormat(" Year: {0}\n", Year);
|
||||
s.AppendFormat(" ModelNo: {0}\n", ModelNo);
|
||||
s.AppendFormat(" Rarity: {0}\n", Rarity);
|
||||
s.AppendFormat(" CartType: {0}\n", CartType);
|
||||
s.AppendFormat(" MachineType: {0}\n", MachineType);
|
||||
s.AppendFormat(" LController: {0}\n", LController);
|
||||
s.AppendFormat(" RController: {0}\n", RController);
|
||||
s.AppendFormat(" HelpUri: {0}", HelpUri);
|
||||
if (DiscoveredRomFullName != null) s.AppendFormat("\n Discovered Rom Filename: {0}", DiscoveredRomFullName);
|
||||
return s.ToString();
|
||||
}
|
||||
|
||||
public GameProgram(string md5)
|
||||
{
|
||||
MD5 = md5;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// not in db, so guess
|
||||
/// </summary>
|
||||
/// <param name="md5"></param>
|
||||
/// <returns></returns>
|
||||
public static GameProgram GetCompleteGuess(string md5)
|
||||
{
|
||||
GameProgram ret = new GameProgram(md5);
|
||||
ret.Title = "UNKNOWN";
|
||||
//ret.CartType = CartType.A7848; // will be guessed for us
|
||||
ret.MachineType = MachineType.A7800NTSC;
|
||||
ret.LController = Controller.Joystick;
|
||||
ret.RController = Controller.Joystick;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,384 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using EMU7800.Core;
|
||||
|
||||
/*
|
||||
* unlike EMU7800 Core stuff, this has been hacked around significantly
|
||||
*/
|
||||
|
||||
namespace EMU7800.Win
|
||||
{
|
||||
public class GameProgramLibrary : Dictionary<string, GameProgram>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
const string
|
||||
BIOS78_NTSC_MD5 = "0763f1ffb006ddbe32e52d497ee848ae",
|
||||
BIOS78_NTSC_ALTERNATE_MD5 = "b32526ea179dc9ab9b2e5f8a2662b298",
|
||||
BIOS78_PAL_MD5 = "397bb566584be7b9764e7a68974c4263",
|
||||
HSC78_MD5 = "c8a73288ab97226c52602204ab894286";
|
||||
/*
|
||||
readonly IDictionary<string, string> _misc7800DiscoveredRoms = new Dictionary<string, string>();
|
||||
*/
|
||||
// these enums are used for matching column names in the .csv file
|
||||
enum Column
|
||||
{
|
||||
None,
|
||||
MD5,
|
||||
Title,
|
||||
Manufacturer,
|
||||
Author,
|
||||
Year,
|
||||
ModelNo,
|
||||
Rarity,
|
||||
CartType,
|
||||
MachineType,
|
||||
LController,
|
||||
RController,
|
||||
HelpUri
|
||||
};
|
||||
|
||||
//const string RomPropertiesFileName = "ROMProperties.csv";
|
||||
|
||||
static readonly Dictionary<Column, int> _columnPositions = new Dictionary<Column, int>
|
||||
{
|
||||
{ Column.MD5, -1},
|
||||
{ Column.Title, -1},
|
||||
{ Column.Manufacturer, -1},
|
||||
{ Column.Author, -1 },
|
||||
{ Column.Year, - 1},
|
||||
{ Column.ModelNo, -1},
|
||||
{ Column.Rarity, -1},
|
||||
{ Column.CartType, -1},
|
||||
{ Column.MachineType, -1},
|
||||
{ Column.LController, -1},
|
||||
{ Column.RController, -1},
|
||||
{ Column.HelpUri, -1},
|
||||
};
|
||||
|
||||
//readonly RomFileAccessor _romFileAccessor = new RomFileAccessor();
|
||||
readonly MD5CryptoServiceProvider _cryptoProvider = new MD5CryptoServiceProvider();
|
||||
readonly StringBuilder _sb = new StringBuilder();
|
||||
//readonly ILogger _logger;
|
||||
|
||||
#endregion
|
||||
|
||||
public static GameProgramLibrary EMU7800DB = null;
|
||||
|
||||
#region Constructors
|
||||
|
||||
private GameProgramLibrary()
|
||||
{
|
||||
}
|
||||
|
||||
public GameProgramLibrary(TextReader r)//, ILogger logger)
|
||||
{
|
||||
//if (logger == null)
|
||||
// throw new ArgumentNullException("logger");
|
||||
|
||||
//_logger = logger;
|
||||
|
||||
//var settings = new GlobalSettings(logger);
|
||||
//var fn = Path.Combine(settings.BaseDirectory, RomPropertiesFileName);
|
||||
|
||||
Clear();
|
||||
//StreamReader r = null;
|
||||
try
|
||||
{
|
||||
//r = new StreamReader(fn);
|
||||
InitializeColumnPos(r.ReadLine());
|
||||
|
||||
while (true)
|
||||
{
|
||||
var line = r.ReadLine();
|
||||
if (line == null)
|
||||
break;
|
||||
var gp = CreateGameSettingsFromLine(line);
|
||||
if (gp == null)
|
||||
continue;
|
||||
if (ContainsKey(gp.MD5))
|
||||
Console.WriteLine("7800DB: Duplicate MD5 key found: {0}", gp.MD5); else Add(gp.MD5, gp);
|
||||
}
|
||||
r.Close();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//if (Util.IsCriticalException(ex))
|
||||
throw;
|
||||
//_logger.WriteLine(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (r != null)
|
||||
r.Dispose();
|
||||
}
|
||||
|
||||
Console.WriteLine("7800DB: {0} entries loaded.", Count);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Game Program Accessors
|
||||
|
||||
public GameProgram TryRecognizeRom(byte[] bytes)
|
||||
{
|
||||
//if (string.IsNullOrWhiteSpace(fullName))
|
||||
// throw new ArgumentException("fullName");
|
||||
|
||||
//var bytes = _romFileAccessor.GetRomBytes(fullName);
|
||||
if (bytes == null)
|
||||
return null;
|
||||
|
||||
var md5 = ComputeMD5Digest(bytes);
|
||||
if (string.IsNullOrWhiteSpace(md5))
|
||||
return null;
|
||||
|
||||
var gp = GetGameProgramFromMd5(md5);
|
||||
if (gp == null)
|
||||
gp = GameProgram.GetCompleteGuess(md5);
|
||||
//gp.DiscoveredRomFullName = fullName;
|
||||
if (gp.CartType == CartType.None)
|
||||
{
|
||||
switch (gp.MachineType)
|
||||
{
|
||||
case MachineType.A2600NTSC:
|
||||
case MachineType.A2600PAL:
|
||||
switch (bytes.Length)
|
||||
{
|
||||
case 2048: gp.CartType = CartType.A2K; break;
|
||||
case 4096: gp.CartType = CartType.A4K; break;
|
||||
case 8192: gp.CartType = CartType.A8K; break;
|
||||
case 16384: gp.CartType = CartType.A16K; break;
|
||||
}
|
||||
break;
|
||||
case MachineType.A7800NTSC:
|
||||
case MachineType.A7800PAL:
|
||||
switch (bytes.Length)
|
||||
{
|
||||
case 8192: gp.CartType = CartType.A7808; break;
|
||||
case 16384: gp.CartType = CartType.A7816; break;
|
||||
case 32768: gp.CartType = CartType.A7832; break;
|
||||
case 49152: gp.CartType = CartType.A7848; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return gp;
|
||||
/*
|
||||
if (md5.Equals(HSC78_MD5, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (!_misc7800DiscoveredRoms.ContainsKey(md5))
|
||||
_misc7800DiscoveredRoms.Add(md5, fullName);
|
||||
_logger.WriteLine("Found 7800 Highscore Cart: {0}", fullName);
|
||||
return null;
|
||||
}
|
||||
if (md5.Equals(BIOS78_NTSC_MD5, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (!_misc7800DiscoveredRoms.ContainsKey(md5))
|
||||
_misc7800DiscoveredRoms.Add(md5, fullName);
|
||||
_logger.WriteLine("Found 7800 NTSC BIOS: {0}", fullName);
|
||||
return null;
|
||||
}
|
||||
if (md5.Equals(BIOS78_NTSC_ALTERNATE_MD5, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (!_misc7800DiscoveredRoms.ContainsKey(md5))
|
||||
_misc7800DiscoveredRoms.Add(md5, fullName);
|
||||
_logger.WriteLine("Found incorrect but widely used 7800 NTSC BIOS: {0}", fullName);
|
||||
return null;
|
||||
}
|
||||
if (md5.Equals(BIOS78_PAL_MD5, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (!_misc7800DiscoveredRoms.ContainsKey(md5))
|
||||
_misc7800DiscoveredRoms.Add(md5, fullName);
|
||||
_logger.WriteLine("Found 7800 PAL BIOS: {0}", fullName);
|
||||
return null;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
public GameProgram GetGameProgramFromFullName(string fullName)
|
||||
{
|
||||
var bytes = _romFileAccessor.GetRomBytes(fullName);
|
||||
if (bytes == null)
|
||||
throw new ArgumentException("File not readable: {0}", fullName);
|
||||
var md5 = ComputeMD5Digest(bytes);
|
||||
return !string.IsNullOrWhiteSpace(md5) ? GetGameProgramFromMd5(md5) : null;
|
||||
}
|
||||
*/
|
||||
public GameProgram GetGameProgramFromMd5(string md5)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(md5))
|
||||
throw new ArgumentNullException("md5");
|
||||
GameProgram gp;
|
||||
return TryGetValue(md5, out gp) ? gp : null;
|
||||
}
|
||||
/*
|
||||
public byte[] GetRomBytes(string fullName)
|
||||
{
|
||||
return _romFileAccessor.GetRomBytes(fullName);
|
||||
}
|
||||
|
||||
public byte[] Get78HighScoreCartBytes()
|
||||
{
|
||||
string fullName;
|
||||
if (!_misc7800DiscoveredRoms.TryGetValue(HSC78_MD5, out fullName))
|
||||
return null;
|
||||
return _romFileAccessor.GetRomBytes(fullName);
|
||||
}
|
||||
|
||||
public byte[] Get78BiosBytes(MachineType machineType)
|
||||
{
|
||||
string fullName = null;
|
||||
switch (machineType)
|
||||
{
|
||||
case MachineType.A7800NTSC:
|
||||
if (!_misc7800DiscoveredRoms.TryGetValue(BIOS78_NTSC_MD5, out fullName))
|
||||
_misc7800DiscoveredRoms.TryGetValue(BIOS78_NTSC_ALTERNATE_MD5, out fullName);
|
||||
break;
|
||||
case MachineType.A7800PAL:
|
||||
_misc7800DiscoveredRoms.TryGetValue(BIOS78_PAL_MD5, out fullName);
|
||||
break;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(fullName))
|
||||
return null;
|
||||
return _romFileAccessor.GetRomBytes(fullName);
|
||||
}*/
|
||||
|
||||
#endregion
|
||||
|
||||
#region Game Progam Related Utilities
|
||||
/*
|
||||
public string ComputeMD5Digest(string fullName)
|
||||
{
|
||||
var bytes = _romFileAccessor.GetRomBytes(fullName);
|
||||
if (bytes == null)
|
||||
throw new ArgumentException("File not readable: {0}", fullName);
|
||||
return ComputeMD5Digest(bytes);
|
||||
}
|
||||
*/
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
static void InitializeColumnPos(string line)
|
||||
{
|
||||
var colno = 0;
|
||||
var columnNames = line.Split(',');
|
||||
|
||||
foreach (var columnName in columnNames)
|
||||
{
|
||||
var col = ToColumn(columnName);
|
||||
if (col != Column.None)
|
||||
_columnPositions[col] = colno;
|
||||
colno++;
|
||||
}
|
||||
|
||||
if (_columnPositions[Column.MD5] < 0)
|
||||
throw new ApplicationException("ROMProperties.csv: Required column missing: MD5");
|
||||
if (_columnPositions[Column.CartType] < 0)
|
||||
throw new ApplicationException("ROMProperties.csv: Required column missing: CartType");
|
||||
if (_columnPositions[Column.MachineType] < 0)
|
||||
throw new ApplicationException("ROMProperties.csv: Required column missing: MachineType");
|
||||
if (_columnPositions[Column.LController] < 0)
|
||||
throw new ApplicationException("ROMProperties.csv: Required column missing: LController");
|
||||
if (_columnPositions[Column.RController] < 0)
|
||||
throw new ApplicationException("ROMProperties.csv: Required column missing: RController");
|
||||
}
|
||||
|
||||
static GameProgram CreateGameSettingsFromLine(string line)
|
||||
{
|
||||
var row = new string[13];
|
||||
var linesplit = line.Split(',');
|
||||
for (var i = 0; i < row.Length && i < linesplit.Length; i++)
|
||||
row[i] = linesplit[i];
|
||||
|
||||
var md5 = row[_columnPositions[Column.MD5]];
|
||||
var gp = new GameProgram(md5)
|
||||
{
|
||||
Title = _columnPositions[Column.Title] >= 0 ? row[_columnPositions[Column.Title]] : string.Empty,
|
||||
Manufacturer = _columnPositions[Column.Manufacturer] >= 0 ? row[_columnPositions[Column.Manufacturer]] : string.Empty,
|
||||
Author = _columnPositions[Column.Author] >= 0 ? row[_columnPositions[Column.Author]] : string.Empty,
|
||||
Year = _columnPositions[Column.Year] >= 0 ? row[_columnPositions[Column.Year]] : string.Empty,
|
||||
ModelNo = _columnPositions[Column.ModelNo] >= 0 ? row[_columnPositions[Column.ModelNo]] : string.Empty,
|
||||
Rarity = _columnPositions[Column.Rarity] >= 0 ? row[_columnPositions[Column.Rarity]] : string.Empty,
|
||||
CartType = ToCartType(row[_columnPositions[Column.CartType]]),
|
||||
MachineType = ToMachineType(row[_columnPositions[Column.MachineType]])
|
||||
};
|
||||
|
||||
gp.LController = ToController(row[_columnPositions[Column.LController]]);
|
||||
gp.RController = ToController(row[_columnPositions[Column.RController]]);
|
||||
|
||||
if (gp.LController == Controller.None)
|
||||
gp.LController = GetDefaultController(gp.MachineType);
|
||||
if (gp.RController == Controller.None)
|
||||
gp.RController = GetDefaultController(gp.MachineType);
|
||||
|
||||
if (_columnPositions[Column.HelpUri] < row.Length)
|
||||
{
|
||||
string helpUri = row[_columnPositions[Column.HelpUri]];
|
||||
if (helpUri != null) helpUri = helpUri.Trim();
|
||||
if (helpUri != null && !helpUri.Length.Equals(0))
|
||||
gp.HelpUri = helpUri;
|
||||
}
|
||||
|
||||
return gp;
|
||||
}
|
||||
|
||||
static Controller GetDefaultController(MachineType machineType)
|
||||
{
|
||||
switch (machineType)
|
||||
{
|
||||
case MachineType.A7800NTSC:
|
||||
case MachineType.A7800PAL:
|
||||
return Controller.ProLineJoystick;
|
||||
default:
|
||||
return Controller.Joystick;
|
||||
}
|
||||
}
|
||||
|
||||
static Column ToColumn(string columnName)
|
||||
{
|
||||
Column result;
|
||||
return Enum.TryParse(columnName, true, out result) ? result : Column.None;
|
||||
}
|
||||
|
||||
static CartType ToCartType(string cartType)
|
||||
{
|
||||
CartType result;
|
||||
return Enum.TryParse(cartType, true, out result) ? result : CartType.None;
|
||||
}
|
||||
|
||||
static MachineType ToMachineType(string machineType)
|
||||
{
|
||||
MachineType result;
|
||||
return Enum.TryParse(machineType, true, out result) ? result : MachineType.None;
|
||||
}
|
||||
|
||||
static Controller ToController(string controller)
|
||||
{
|
||||
Controller result;
|
||||
return Enum.TryParse(controller, true, out result) ? result : Controller.None;
|
||||
}
|
||||
|
||||
string ComputeMD5Digest(byte[] bytes)
|
||||
{
|
||||
return (bytes != null) ? StringifyMD5(_cryptoProvider.ComputeHash(bytes)) : null;
|
||||
}
|
||||
|
||||
string StringifyMD5(byte[] bytes)
|
||||
{
|
||||
if (bytes == null || bytes.Length < 16)
|
||||
return string.Empty;
|
||||
_sb.Length = 0;
|
||||
for (var i = 0; i < 16; i++)
|
||||
_sb.AppendFormat("{0:x2}", bytes[i]);
|
||||
return _sb.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue