Merge branch 'master' into waterbox2
# Conflicts: # BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.ISaveRam.cs
This commit is contained in:
commit
4982af7521
|
@ -318,6 +318,15 @@ namespace BizHawk.Client.Common
|
|||
}
|
||||
else
|
||||
{
|
||||
// at this point, file is either assigned to the ROM path, if it exists,
|
||||
// or is empty and corecomm is not a libretro core
|
||||
// so, we still need to check path here before continuing
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
Console.WriteLine("No ROM to Load");
|
||||
return false;
|
||||
}
|
||||
|
||||
// if not libretro:
|
||||
// do extension checknig
|
||||
ext = file.Extension.ToLowerInvariant();
|
||||
|
|
|
@ -3597,10 +3597,10 @@ namespace BizHawk.Client.EmuHawk
|
|||
((OpenAdvanced_OpenRom)ioa).Path = loader.CanonicalFullPath;
|
||||
}
|
||||
|
||||
string loaderName = "*" + OpenAdvancedSerializer.Serialize(ioa);
|
||||
|
||||
if (result)
|
||||
{
|
||||
|
||||
string loaderName = "*" + OpenAdvancedSerializer.Serialize(ioa);
|
||||
Emulator = loader.LoadedEmulator;
|
||||
Global.Game = loader.Game;
|
||||
CoreFileProvider.SyncCoreCommInputSignals(nextComm);
|
||||
|
|
|
@ -1,14 +1,53 @@
|
|||
using System.ComponentModel;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
using System.IO;
|
||||
|
||||
using BizHawk.Client.Common;
|
||||
using LuaInterface;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
[Description("Represents a canvas object returned by the gui.createcanvas() method")]
|
||||
public partial class LuaCanvas : Form
|
||||
{
|
||||
private Color _defaultForeground = Color.White;
|
||||
private Color? _defaultBackground;
|
||||
|
||||
#region Helpers
|
||||
private readonly Dictionary<string, Image> _imageCache = new Dictionary<string, Image>();
|
||||
|
||||
private readonly Dictionary<Color, SolidBrush> _solidBrushes = new Dictionary<Color, SolidBrush>();
|
||||
private readonly Dictionary<Color, Pen> _pens = new Dictionary<Color, Pen>();
|
||||
|
||||
private SolidBrush GetBrush(Color color)
|
||||
{
|
||||
SolidBrush b;
|
||||
if (!_solidBrushes.TryGetValue(color, out b))
|
||||
{
|
||||
b = new SolidBrush(color);
|
||||
_solidBrushes[color] = b;
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
private Pen GetPen(Color color)
|
||||
{
|
||||
Pen p;
|
||||
if (!_pens.TryGetValue(color, out p))
|
||||
{
|
||||
p = new Pen(color);
|
||||
_pens[color] = p;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private readonly Graphics _graphics;
|
||||
|
||||
public LuaCanvas(int width, int height)
|
||||
|
@ -38,6 +77,287 @@ namespace BizHawk.Client.EmuHawk
|
|||
pictureBox.Refresh();
|
||||
}
|
||||
|
||||
[LuaMethodAttributes("defaultForeground", "Sets the default foreground color to use in drawing methods, white by default")]
|
||||
public void SetDefaultForegroundColor(Color color)
|
||||
{
|
||||
_defaultForeground = color;
|
||||
}
|
||||
|
||||
[LuaMethodAttributes("defaultBackground", "Sets the default background color to use in drawing methods, transparent by default")]
|
||||
public void SetDefaultBackgroundColor(Color color)
|
||||
{
|
||||
_defaultBackground = color;
|
||||
}
|
||||
|
||||
[LuaMethodAttributes("drawBezier", "Draws a Bezier curve using the table of coordinates provided in the given color")]
|
||||
public void DrawBezier(LuaTable points, Color color)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pointsArr = new Point[4];
|
||||
|
||||
var i = 0;
|
||||
foreach (LuaTable point in points.Values)
|
||||
{
|
||||
pointsArr[i] = new Point((int)(double)(point[1]), (int)(double)(point[2]));
|
||||
i++;
|
||||
if (i >= 4)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_graphics.DrawBezier(GetPen(color), pointsArr[0], pointsArr[1], pointsArr[2], pointsArr[3]);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// need to stop the script from here
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[LuaMethodAttributes(
|
||||
"drawBox", "Draws a rectangle on screen from x1/y1 to x2/y2. Same as drawRectangle except it receives two points intead of a point and width/height")]
|
||||
public void DrawBox(int x, int y, int x2, int y2, Color? line = null, Color? background = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (x < x2)
|
||||
{
|
||||
x2 = Math.Abs(x - x2);
|
||||
}
|
||||
else
|
||||
{
|
||||
x2 = x - x2;
|
||||
x -= x2;
|
||||
}
|
||||
|
||||
if (y < y2)
|
||||
{
|
||||
y2 = Math.Abs(y - y2);
|
||||
}
|
||||
else
|
||||
{
|
||||
y2 = y - y2;
|
||||
y -= y2;
|
||||
}
|
||||
|
||||
_graphics.DrawRectangle(GetPen(line ?? _defaultForeground), x, y, x2, y2);
|
||||
|
||||
var bg = background ?? _defaultBackground;
|
||||
if (bg.HasValue)
|
||||
{
|
||||
_graphics.FillRectangle(GetBrush(bg.Value), x + 1, y + 1, x2 - 1, y2 - 1);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// need to stop the script from here
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[LuaMethodAttributes(
|
||||
"drawEllipse", "Draws an ellipse at the given coordinates and the given width and height. Line is the color of the ellipse. Background is the optional fill color")]
|
||||
public void DrawEllipse(int x, int y, int width, int height, Color? line = null, Color? background = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var bg = background ?? _defaultBackground;
|
||||
if (bg.HasValue)
|
||||
{
|
||||
var brush = GetBrush(bg.Value);
|
||||
_graphics.FillEllipse(brush, x, y, width, height);
|
||||
}
|
||||
|
||||
_graphics.DrawEllipse(GetPen(line ?? _defaultForeground), x, y, width, height);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// need to stop the script from here
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[LuaMethodAttributes(
|
||||
"drawIcon", "draws an Icon (.ico) file from the given path at the given coordinate. width and height are optional. If specified, it will resize the image accordingly")]
|
||||
public void DrawIcon(string path, int x, int y, int? width = null, int? height = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Icon icon;
|
||||
if (width.HasValue && height.HasValue)
|
||||
{
|
||||
icon = new Icon(path, width.Value, height.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
icon = new Icon(path);
|
||||
}
|
||||
|
||||
_graphics.DrawIcon(icon, x, y);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// need to stop the script from here
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[LuaMethodAttributes(
|
||||
"drawImage", "draws an image file from the given path at the given coordinate. width and height are optional. If specified, it will resize the image accordingly")]
|
||||
public void DrawImage(string path, int x, int y, int? width = null, int? height = null, bool cache = true)
|
||||
{
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
//Log("File not found: " + path);
|
||||
return;
|
||||
}
|
||||
|
||||
Image img;
|
||||
if (_imageCache.ContainsKey(path))
|
||||
{
|
||||
img = _imageCache[path];
|
||||
}
|
||||
else
|
||||
{
|
||||
img = Image.FromFile(path);
|
||||
if (cache)
|
||||
{
|
||||
_imageCache.Add(path, img);
|
||||
}
|
||||
}
|
||||
|
||||
_graphics.DrawImage(img, x, y, width ?? img.Width, height ?? img.Height);
|
||||
}
|
||||
|
||||
[LuaMethodAttributes(
|
||||
"clearImageCache", "clears the image cache that is built up by using gui.drawImage, also releases the file handle for cached images")]
|
||||
public void ClearImageCache()
|
||||
{
|
||||
foreach (var image in _imageCache)
|
||||
{
|
||||
image.Value.Dispose();
|
||||
}
|
||||
|
||||
_imageCache.Clear();
|
||||
}
|
||||
|
||||
[LuaMethodAttributes(
|
||||
"drawImageRegion", "draws a given region of an image file from the given path at the given coordinate, and optionally with the given size")]
|
||||
public void DrawImageRegion(string path, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y, int? dest_width = null, int? dest_height = null)
|
||||
{
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
//Log("File not found: " + path);
|
||||
return;
|
||||
}
|
||||
|
||||
Image img;
|
||||
if (_imageCache.ContainsKey(path))
|
||||
{
|
||||
img = _imageCache[path];
|
||||
}
|
||||
else
|
||||
{
|
||||
img = Image.FromFile(path);
|
||||
_imageCache.Add(path, img);
|
||||
}
|
||||
|
||||
var destRect = new Rectangle(dest_x, dest_y, dest_width ?? source_width, dest_height ?? source_height);
|
||||
|
||||
_graphics.DrawImage(img, destRect, source_x, source_y, source_width, source_height, GraphicsUnit.Pixel);
|
||||
}
|
||||
|
||||
[LuaMethodAttributes(
|
||||
"drawLine", "Draws a line from the first coordinate pair to the 2nd. Color is optional (if not specified it will be drawn black)")]
|
||||
public void DrawLine(int x1, int y1, int x2, int y2, Color? color = null)
|
||||
{
|
||||
_graphics.DrawLine(GetPen(color ?? _defaultForeground), x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
[LuaMethodAttributes("drawAxis", "Draws an axis of the specified size at the coordinate pair.)")]
|
||||
public void DrawAxis(int x, int y, int size, Color? color = null)
|
||||
{
|
||||
DrawLine(x + size, y, x - size, y, color);
|
||||
DrawLine(x, y + size, x, y - size, color);
|
||||
}
|
||||
|
||||
[LuaMethodAttributes(
|
||||
"drawArc",
|
||||
"draws a Arc shape at the given coordinates and the given width and height"
|
||||
)]
|
||||
public void DrawArc(int x, int y, int width, int height, int startangle, int sweepangle, Color? line = null)
|
||||
{
|
||||
var pen = new Pen(line.HasValue ? line.Value : Color.Black);
|
||||
_graphics.DrawArc(pen, x, y, width, height, startangle, sweepangle);
|
||||
}
|
||||
|
||||
[LuaMethodAttributes("drawPie", "draws a Pie shape at the given coordinates and the given width and height")]
|
||||
public void DrawPie(
|
||||
int x,
|
||||
int y,
|
||||
int width,
|
||||
int height,
|
||||
int startangle,
|
||||
int sweepangle,
|
||||
Color? line = null,
|
||||
Color? background = null)
|
||||
{
|
||||
var bg = background ?? _defaultBackground;
|
||||
if (bg.HasValue)
|
||||
{
|
||||
var brush = GetBrush(bg.Value);
|
||||
_graphics.FillPie(brush, x, y, width, height, startangle, sweepangle);
|
||||
}
|
||||
|
||||
_graphics.DrawPie(GetPen(line ?? _defaultForeground), x + 1, y + 1, width - 1, height - 1, startangle, sweepangle);
|
||||
}
|
||||
|
||||
[LuaMethodAttributes(
|
||||
"drawPixel", "Draws a single pixel at the given coordinates in the given color. Color is optional (if not specified it will be drawn black)")]
|
||||
public void DrawPixel(int x, int y, Color? color = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_graphics.DrawLine(GetPen(color ?? _defaultForeground), x, y, x + 0.1F, y);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// need to stop the script from here
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[LuaMethodAttributes(
|
||||
"drawPolygon", "Draws a polygon using the table of coordinates specified in points. This should be a table of tables(each of size 2). Line is the color of the polygon. Background is the optional fill color")]
|
||||
public void DrawPolygon(LuaTable points, Color? line = null, Color? background = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pointsArr = new Point[points.Values.Count];
|
||||
var i = 0;
|
||||
foreach (LuaTable point in points.Values)
|
||||
{
|
||||
pointsArr[i] = new Point((int)(double)(point[1]), (int)(double)(point[2]));
|
||||
i++;
|
||||
}
|
||||
|
||||
_graphics.DrawPolygon(GetPen(line ?? _defaultForeground), pointsArr);
|
||||
var bg = background ?? _defaultBackground;
|
||||
if (bg.HasValue)
|
||||
{
|
||||
_graphics.FillPolygon(GetBrush(bg.Value), pointsArr);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// need to stop the script from here
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[LuaMethodAttributes(
|
||||
"DrawRectangle",
|
||||
"Draws a rectangle at the given coordinate and the given width and height. Line is the color of the box. Background is the optional fill color")]
|
||||
|
|
|
@ -314,7 +314,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502
|
|||
//the interrupt would then take place if necessary, using a cached PC. but im not so sure about that.
|
||||
/*VOP_NMI*/ new Uop[] { Uop.FetchDummy, Uop.FetchDummy, Uop.PushPCH, Uop.PushPCL, Uop.PushP_NMI, Uop.FetchPCLVector, Uop.FetchPCHVector, Uop.End_SuppressInterrupt },
|
||||
/*VOP_IRQ*/ new Uop[] { Uop.FetchDummy, Uop.FetchDummy, Uop.PushPCH, Uop.PushPCL, Uop.PushP_IRQ, Uop.FetchPCLVector, Uop.FetchPCHVector, Uop.End_SuppressInterrupt },
|
||||
/*VOP_RESET*/ new Uop[] { Uop.FetchDummy, Uop.FetchDummy, Uop.FetchDummy, Uop.PushDummy, Uop.PushDummy, Uop.PushP_Reset, Uop.FetchPCLVector, Uop.FetchPCHVector, Uop.End_SuppressInterrupt },
|
||||
/*VOP_RESET*/ new Uop[] { Uop.FetchDummy, /*Uop.FetchDummy,*/ Uop.FetchDummy, Uop.PushDummy, Uop.PushDummy, Uop.PushP_Reset, Uop.FetchPCLVector, Uop.FetchPCHVector, Uop.End_SuppressInterrupt },
|
||||
/*VOP_Fetch1_NoInterrupt*/ new Uop[] { Uop.Fetch1_Real },
|
||||
};
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
|
|||
}
|
||||
}
|
||||
|
||||
public IMemoryCallbackSystem MemoryCallbacks { get; private set; }
|
||||
public IMemoryCallbackSystem MemoryCallbacks { get; } = new MemoryCallbackSystem();
|
||||
|
||||
public bool CanStep(StepType type)
|
||||
{
|
||||
|
|
|
@ -12,7 +12,10 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
|
|||
//Maria related variables
|
||||
public int cycle;
|
||||
public int cpu_cycle;
|
||||
public int scanline;
|
||||
public bool cpu_is_haltable;
|
||||
public bool cpu_is_halted;
|
||||
public bool cpu_halt_pending;
|
||||
public bool cpu_resume_pending;
|
||||
|
||||
// there are 4 maria cycles in a CPU cycle (fast access, both NTSC and PAL)
|
||||
// if the 6532 or TIA are accessed (PC goes to one of those addresses) the next access will be slower by 1/2 a CPU cycle
|
||||
|
@ -47,58 +50,75 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
|
|||
// read the controller state here for now
|
||||
GetControllerState(controller);
|
||||
|
||||
scanline = 0;
|
||||
maria.RunFrame();
|
||||
}
|
||||
|
||||
// actually execute the frame
|
||||
while (scanline < 263)
|
||||
public void RunCPUCycle()
|
||||
{
|
||||
cpu_cycle++;
|
||||
tia._hsyncCnt++;
|
||||
|
||||
if (cpu_cycle <= (2 + (slow_access ? 1 : 0)))
|
||||
{
|
||||
maria.Execute(cycle, scanline);
|
||||
cycle++;
|
||||
cpu_cycle++;
|
||||
tia._hsyncCnt++;
|
||||
cpu_is_haltable = true;
|
||||
} else
|
||||
{
|
||||
cpu_is_haltable = false;
|
||||
}
|
||||
|
||||
// the time a cpu cycle takes depends on the status of the address bus
|
||||
// any address in range of the TIA or m6532 takes an extra cycle to complete
|
||||
if (cpu_cycle==(4 + (slow_access ? 2 : 0)))
|
||||
// the time a cpu cycle takes depends on the status of the address bus
|
||||
// any address in range of the TIA or m6532 takes an extra cycle to complete
|
||||
if (cpu_cycle == (4 + (slow_access ? 2 : 0)))
|
||||
{
|
||||
if (!cpu_is_halted)
|
||||
{
|
||||
cpu.ExecuteOne();
|
||||
cpu_cycle = 0;
|
||||
}
|
||||
|
||||
// determine if the next access will be fast or slow
|
||||
if (cpu.PC < 0x0400)
|
||||
else
|
||||
{
|
||||
if ((cpu.PC & 0xFF) < 0x20)
|
||||
{
|
||||
if ((A7800_control_register & 0x1) == 0 && (cpu.PC < 0x20))
|
||||
{
|
||||
slow_access = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
slow_access = true;
|
||||
}
|
||||
}
|
||||
else if (cpu.PC < 0x300)
|
||||
{
|
||||
slow_access = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
slow_access = false;
|
||||
}
|
||||
// we still want to keep track of CPU time even if it is halted, so increment the counter here
|
||||
// The basic 6502 has no halt state, this feature is specific to SALLY
|
||||
cpu.TotalExecutedCycles++;
|
||||
}
|
||||
|
||||
cpu_cycle = 0;
|
||||
|
||||
if (cycle == 454)
|
||||
if (cpu_halt_pending)
|
||||
{
|
||||
scanline++;
|
||||
cycle = 0;
|
||||
tia._hsyncCnt = 0;
|
||||
cpu_halt_pending = false;
|
||||
cpu_is_halted = true;
|
||||
}
|
||||
|
||||
if (cpu_resume_pending)
|
||||
{
|
||||
cpu_resume_pending = false;
|
||||
cpu_is_halted = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// determine if the next access will be fast or slow
|
||||
if (cpu.PC < 0x0400)
|
||||
{
|
||||
if ((cpu.PC & 0xFF) < 0x20)
|
||||
{
|
||||
if ((A7800_control_register & 0x1) == 0 && (cpu.PC < 0x20))
|
||||
{
|
||||
slow_access = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
slow_access = true;
|
||||
}
|
||||
}
|
||||
else if (cpu.PC < 0x300)
|
||||
{
|
||||
slow_access = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
slow_access = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void GetControllerState(IController controller)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
|
|
|
@ -127,18 +127,20 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
|
|||
if (_isPAL)
|
||||
{
|
||||
maria._frameHz = 50;
|
||||
maria._screen_width = 454;
|
||||
maria._screen_width = 320;
|
||||
maria._screen_height = 313;
|
||||
maria._palette = PALPalette;
|
||||
}
|
||||
else
|
||||
{
|
||||
maria._frameHz = 60;
|
||||
maria._screen_width = 454;
|
||||
maria._screen_width = 320;
|
||||
maria._screen_height = 263;
|
||||
maria._palette = NTSCPalette;
|
||||
}
|
||||
|
||||
maria.Core = this;
|
||||
|
||||
ser.Register<IVideoProvider>(maria);
|
||||
ser.Register<ISoundProvider>(tia);
|
||||
ServiceProvider = ser;
|
||||
|
@ -146,6 +148,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
|
|||
_tracer = new TraceBuffer { Header = cpu.TraceHeader };
|
||||
ser.Register<ITraceable>(_tracer);
|
||||
|
||||
SetupMemoryDomains();
|
||||
HardReset();
|
||||
}
|
||||
|
||||
|
@ -174,7 +177,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
|
|||
|
||||
private void ExecFetch(ushort addr)
|
||||
{
|
||||
//MemoryCallbacks.CallExecutes(addr);
|
||||
MemoryCallbacks.CallExecutes(addr);
|
||||
}
|
||||
|
||||
private void Reset_Mapper(string m)
|
||||
|
|
|
@ -1,14 +1,34 @@
|
|||
using System;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Common.NumberExtensions;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
|
||||
{
|
||||
// Emulates the Atari 7800 Maria graphics chip
|
||||
public class Maria : IVideoProvider
|
||||
{
|
||||
public A7800Hawk Core { get; set; }
|
||||
|
||||
struct GFX_Object
|
||||
{
|
||||
public byte palette;
|
||||
public byte width;
|
||||
public ushort addr;
|
||||
public byte h_pos;
|
||||
|
||||
// additional entries used only in 5-byte header mode
|
||||
public bool write_mode;
|
||||
public bool ind_mode;
|
||||
public bool exp_mode;
|
||||
}
|
||||
|
||||
// technically there is no limit on he number of graphics objects, but since dma is automatically killed
|
||||
// at the end of a scanline, we have an effective limit
|
||||
GFX_Object[] GFX_Objects = new GFX_Object[64];
|
||||
|
||||
public int _frameHz = 60;
|
||||
public int _screen_width = 454;
|
||||
public int _screen_width = 320;
|
||||
public int _screen_height = 263;
|
||||
|
||||
public int[] _vidbuffer;
|
||||
|
@ -19,9 +39,9 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
|
|||
return _vidbuffer;
|
||||
}
|
||||
|
||||
public int VirtualWidth => 454;
|
||||
public int VirtualWidth => 320;
|
||||
public int VirtualHeight => _screen_height;
|
||||
public int BufferWidth => 454;
|
||||
public int BufferWidth => 320;
|
||||
public int BufferHeight => _screen_height;
|
||||
public int BackgroundColor => unchecked((int)0xff000000);
|
||||
public int VsyncNumerator => _frameHz;
|
||||
|
@ -30,11 +50,287 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
|
|||
// the Maria chip can directly access memory
|
||||
public Func<ushort, byte> ReadMemory;
|
||||
|
||||
public int cycle;
|
||||
public int scanline;
|
||||
public bool sl_DMA_complete;
|
||||
public bool do_dma;
|
||||
|
||||
public int DMA_phase = 0;
|
||||
public int DMA_phase_counter;
|
||||
|
||||
public static int DMA_START_UP = 0;
|
||||
public static int DMA_HEADER = 1;
|
||||
public static int DMA_GRAPHICS = 2;
|
||||
public static int DMA_CHAR_MAP = 3;
|
||||
public static int DMA_SHUTDOWN_OTHER = 4;
|
||||
public static int DMA_SHUTDOWN_LAST = 5;
|
||||
|
||||
public int header_read_time = 8; // default for 4 byte headers (10 for 5 bytes ones)
|
||||
public int DMA_phase_next;
|
||||
public int base_scanline;
|
||||
|
||||
public ushort display_zone_pointer;
|
||||
public int display_zone_counter;
|
||||
|
||||
public byte current_DLL_offset;
|
||||
public ushort current_DLL_addr;
|
||||
public bool current_DLL_DLI;
|
||||
public bool current_DLL_H16;
|
||||
public bool current_DLL_H8;
|
||||
|
||||
public int header_counter;
|
||||
public int header_pointer; // since headers could be 4 or 5 bytes, we need a seperate pointer
|
||||
|
||||
// each frame contains 263 scanlines
|
||||
// each scanline consists of 113.5 CPU cycles (fast access) which equates to 454 Maria cycles
|
||||
// In total there are 29850.5 CPU cycles (fast access) in a frame
|
||||
public void Execute(int cycle, int scanline)
|
||||
public void RunFrame()
|
||||
{
|
||||
scanline = 0;
|
||||
|
||||
Core.Maria_regs[8] = 0x80; // indicates VBlank state
|
||||
|
||||
// we start off in VBlank for 20 scanlines
|
||||
// at the end of vblank is a DMA to set up the display for the start of drawing
|
||||
// this is free time for the CPU to set up display lists
|
||||
while (scanline < 19)
|
||||
{
|
||||
Core.RunCPUCycle();
|
||||
cycle++;
|
||||
|
||||
if (cycle == 454)
|
||||
{
|
||||
scanline++;
|
||||
cycle = 0;
|
||||
Core.tia._hsyncCnt = 0;
|
||||
Core.cpu.RDY = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// "The end of vblank is made up of a DMA startup plus a long shut down"
|
||||
sl_DMA_complete = false;
|
||||
do_dma = false;
|
||||
|
||||
for (int i=0; i<454;i++)
|
||||
{
|
||||
if (i<28)
|
||||
{
|
||||
// DMA doesn't start until 7 CPU cycles into a scanline
|
||||
}
|
||||
else if (i==28 && Core.Maria_regs[0x1C].Bit(6) && !Core.Maria_regs[0x1C].Bit(6))
|
||||
{
|
||||
Core.cpu_halt_pending = true;
|
||||
DMA_phase = DMA_START_UP;
|
||||
DMA_phase_counter = 0;
|
||||
do_dma = true;
|
||||
}
|
||||
else if (!sl_DMA_complete && do_dma)
|
||||
{
|
||||
RunDMA(true);
|
||||
}
|
||||
|
||||
Core.RunCPUCycle();
|
||||
}
|
||||
|
||||
scanline++;
|
||||
cycle = 0;
|
||||
do_dma = false;
|
||||
Core.Maria_regs[8] = 0; // we have now left VBLank'
|
||||
base_scanline = 0;
|
||||
sl_DMA_complete = false;
|
||||
|
||||
// Now proceed with the remaining scanlines
|
||||
// the first one is a pre-render line, since we didn't actually put any data into the buffer yet
|
||||
while (scanline < 263)
|
||||
{
|
||||
|
||||
if (cycle < 28)
|
||||
{
|
||||
// DMA doesn't start until 7 CPU cycles into a scanline
|
||||
}
|
||||
else if (cycle == 28 && Core.Maria_regs[0x1C].Bit(6) && !Core.Maria_regs[0x1C].Bit(6))
|
||||
{
|
||||
Core.cpu_halt_pending = true;
|
||||
DMA_phase = DMA_START_UP;
|
||||
DMA_phase_counter = 0;
|
||||
do_dma = true;
|
||||
}
|
||||
else if (!sl_DMA_complete && do_dma)
|
||||
{
|
||||
RunDMA(false);
|
||||
}
|
||||
|
||||
Core.RunCPUCycle();
|
||||
|
||||
cycle++;
|
||||
|
||||
if (cycle == 454)
|
||||
{
|
||||
scanline++;
|
||||
cycle = 0;
|
||||
Core.tia._hsyncCnt = 0;
|
||||
Core.cpu.RDY = true;
|
||||
do_dma = false;
|
||||
sl_DMA_complete = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RunDMA(bool short_dma)
|
||||
{
|
||||
// During DMA the CPU is HALTED, This appears to happen on the falling edge of Phi2
|
||||
// Current implementation is that a HALT request must be acknowledged in phi1
|
||||
// if the CPU is now in halted state, start DMA
|
||||
if (Core.cpu_is_halted)
|
||||
{
|
||||
DMA_phase_counter++;
|
||||
|
||||
if (DMA_phase_counter==2 && DMA_phase==DMA_START_UP)
|
||||
{
|
||||
DMA_phase_counter = 0;
|
||||
if (short_dma)
|
||||
{
|
||||
DMA_phase = DMA_SHUTDOWN_LAST;
|
||||
|
||||
// also here we load up the display list list
|
||||
// is the timing correct?
|
||||
display_zone_pointer = (ushort)((Core.Maria_regs[0xC] << 8) | Core.Maria_regs[0x10]);
|
||||
display_zone_counter = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
DMA_phase = DMA_HEADER;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (DMA_phase == DMA_HEADER)
|
||||
{
|
||||
// get all the data from the display list header
|
||||
if (DMA_phase_counter==1)
|
||||
{
|
||||
header_counter++;
|
||||
GFX_Objects[header_counter].addr = ReadMemory((ushort)(current_DLL_addr + header_pointer));
|
||||
header_pointer++;
|
||||
byte temp = ReadMemory((ushort)(current_DLL_addr + header_pointer));
|
||||
// if there is no width, then we must have an extended header
|
||||
// or at the end of this list
|
||||
if ((temp & 0x1F) == 0)
|
||||
{
|
||||
if ((temp & 0xE0) == 0)
|
||||
{
|
||||
// at the end of the list, time to end the DMA
|
||||
// check if we are at the end of the zone
|
||||
if (scanline == base_scanline + current_DLL_offset)
|
||||
{
|
||||
DMA_phase_next = DMA_SHUTDOWN_LAST;
|
||||
}
|
||||
else
|
||||
{
|
||||
DMA_phase_next = DMA_SHUTDOWN_OTHER;
|
||||
}
|
||||
header_read_time = 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we are in 5 Byte header mode
|
||||
GFX_Objects[header_counter].write_mode = temp.Bit(7);
|
||||
GFX_Objects[header_counter].ind_mode = temp.Bit(5);
|
||||
header_pointer++;
|
||||
temp = ReadMemory((ushort)(current_DLL_addr + header_pointer));
|
||||
GFX_Objects[header_counter].addr |= (ushort)(temp << 8);
|
||||
header_pointer++;
|
||||
temp = ReadMemory((ushort)(current_DLL_addr + header_pointer));
|
||||
GFX_Objects[header_counter].width = (byte)(temp & 0x1F);
|
||||
GFX_Objects[header_counter].palette = (byte)((temp & 0xE0) >> 5);
|
||||
header_pointer++;
|
||||
GFX_Objects[header_pointer].h_pos = ReadMemory((ushort)(current_DLL_addr + header_pointer));
|
||||
header_pointer++;
|
||||
|
||||
GFX_Objects[header_pointer].exp_mode = true;
|
||||
DMA_phase_next = DMA_GRAPHICS;
|
||||
|
||||
header_read_time = 10;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GFX_Objects[header_counter].width = (byte)(temp & 0x1F);
|
||||
GFX_Objects[header_counter].palette = (byte)((temp & 0xE0) >> 5);
|
||||
header_pointer++;
|
||||
temp = ReadMemory((ushort)(current_DLL_addr + header_pointer));
|
||||
GFX_Objects[header_counter].addr |= (ushort)(temp << 8);
|
||||
header_pointer++;
|
||||
GFX_Objects[header_pointer].h_pos = ReadMemory((ushort)(current_DLL_addr + header_pointer));
|
||||
header_pointer++;
|
||||
|
||||
GFX_Objects[header_pointer].exp_mode = false;
|
||||
DMA_phase_next = DMA_GRAPHICS;
|
||||
|
||||
header_read_time = 8;
|
||||
}
|
||||
|
||||
}
|
||||
else if (DMA_phase_counter == header_read_time)
|
||||
{
|
||||
DMA_phase_counter = 0;
|
||||
DMA_phase = DMA_phase_next;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (DMA_phase == DMA_GRAPHICS)
|
||||
{
|
||||
if (DMA_phase_counter == 1)
|
||||
{
|
||||
// get all the graphics data
|
||||
}
|
||||
|
||||
if (DMA_phase_counter == 3)
|
||||
{
|
||||
DMA_phase = DMA_SHUTDOWN_OTHER;
|
||||
DMA_phase_counter = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (DMA_phase == DMA_SHUTDOWN_OTHER)
|
||||
{
|
||||
Core.cpu_resume_pending = true;
|
||||
sl_DMA_complete = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (DMA_phase == DMA_SHUTDOWN_LAST)
|
||||
{
|
||||
if (DMA_phase_counter==6)
|
||||
{
|
||||
Core.cpu_resume_pending = true;
|
||||
sl_DMA_complete = true;
|
||||
|
||||
// on the last line of a zone, we load up the disply list list for the next zone.
|
||||
display_zone_counter++;
|
||||
ushort temp_addr = (ushort)(display_zone_pointer + 3 * display_zone_counter);
|
||||
byte temp = ReadMemory(temp_addr);
|
||||
current_DLL_addr = (ushort)(ReadMemory((ushort)(temp_addr + 1)) << 8);
|
||||
current_DLL_addr |= ReadMemory((ushort)(temp_addr + 2));
|
||||
|
||||
current_DLL_offset = (byte)(temp & 0xF + 1);
|
||||
current_DLL_DLI = temp.Bit(7);
|
||||
current_DLL_H16 = temp.Bit(6);
|
||||
current_DLL_H8 = temp.Bit(5);
|
||||
|
||||
header_counter = -1;
|
||||
header_pointer = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
using BizHawk.Common.BufferExtensions;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores.Components.M6502;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
|
||||
{
|
||||
|
@ -10,6 +9,8 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
|
|||
{
|
||||
public byte ReadMemory(ushort addr)
|
||||
{
|
||||
MemoryCallbacks.CallReads(addr);
|
||||
|
||||
if (addr < 0x0400) {
|
||||
if ((addr & 0xFF) < 0x20)
|
||||
{
|
||||
|
@ -85,6 +86,8 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
|
|||
|
||||
public void WriteMemory(ushort addr, byte value)
|
||||
{
|
||||
MemoryCallbacks.CallWrites(addr);
|
||||
|
||||
if (addr < 0x0400)
|
||||
{
|
||||
if ((addr & 0xFF) < 0x20)
|
||||
|
@ -92,18 +95,25 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
|
|||
// return TIA registers or control register if it is still unlocked
|
||||
if ((A7800_control_register & 0x1) == 0 && (addr < 0x20))
|
||||
{
|
||||
A7800_control_register = value; // TODO: what to return here?
|
||||
A7800_control_register = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
TIA_regs[addr] = value; // TODO: what to return here?
|
||||
TIA_regs[addr] = value;
|
||||
}
|
||||
}
|
||||
else if ((addr & 0xFF) < 0x40)
|
||||
{
|
||||
if ((A7800_control_register & 0x2) > 0)
|
||||
{
|
||||
Maria_regs[(addr & 0x3F) - 0x20] = value;
|
||||
var temp = (addr & 0x3F) - 0x20;
|
||||
|
||||
// register 8 is read only and controlled by Maria
|
||||
if (temp != 8)
|
||||
Maria_regs[temp] = value;
|
||||
|
||||
if (temp==4) // WSYNC
|
||||
cpu.RDY = false;
|
||||
}
|
||||
}
|
||||
else if (addr < 0x100)
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
//TODO - so many integers in the square wave output keep us from exactly unbiasing the waveform. also other waves probably. consider improving the unbiasing.
|
||||
//ALSO - consider whether we should even be doing it: the nonlinear-mixing behaviour probably depends on those biases being there.
|
||||
//if we have a better high-pass filter somewhere then we might could cope with the weird biases
|
||||
//(mix higher integer precision with the non-linear mixer and then highpass filter befoure outputting s16s)
|
||||
// TODO - so many integers in the square wave output keep us from exactly unbiasing the waveform. also other waves probably. consider improving the unbiasing.
|
||||
// ALSO - consider whether we should even be doing it: the nonlinear-mixing behaviour probably depends on those biases being there.
|
||||
// if we have a better high-pass filter somewhere then we might could cope with the weird biases
|
||||
// (mix higher integer precision with the non-linear mixer and then highpass filter befoure outputting s16s)
|
||||
|
||||
//http://wiki.nesdev.com/w/index.php/APU_Mixer_Emulation
|
||||
//http://wiki.nesdev.com/w/index.php/APU
|
||||
//http://wiki.nesdev.com/w/index.php/APU_Pulse
|
||||
//sequencer ref: http://wiki.nesdev.com/w/index.php/APU_Frame_Counter
|
||||
// http://wiki.nesdev.com/w/index.php/APU_Mixer_Emulation
|
||||
// http://wiki.nesdev.com/w/index.php/APU
|
||||
// http://wiki.nesdev.com/w/index.php/APU_Pulse
|
||||
// sequencer ref: http://wiki.nesdev.com/w/index.php/APU_Frame_Counter
|
||||
|
||||
//TODO - refactor length counter to be separate component
|
||||
// TODO - refactor length counter to be separate component
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
@ -65,10 +65,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
static int[] DMC_RATE_PAL = { 398, 354, 316, 298, 276, 236, 210, 198, 176, 148, 132, 118, 98, 78, 66, 50 };
|
||||
static int[] LENGTH_TABLE = { 10, 254, 20, 2, 40, 4, 80, 6, 160, 8, 60, 10, 14, 12, 26, 14, 12, 16, 24, 18, 48, 20, 96, 22, 192, 24, 72, 26, 16, 28, 32, 30 };
|
||||
static byte[,] PULSE_DUTY = {
|
||||
{0,1,0,0,0,0,0,0}, //(12.5%)
|
||||
{0,1,1,0,0,0,0,0}, //(25%)
|
||||
{0,1,1,1,1,0,0,0}, //(50%)
|
||||
{1,0,0,1,1,1,1,1}, //(25% negated (75%))
|
||||
{0,1,0,0,0,0,0,0}, // (12.5%)
|
||||
{0,1,1,0,0,0,0,0}, // (25%)
|
||||
{0,1,1,1,1,0,0,0}, // (50%)
|
||||
{1,0,0,1,1,1,1,1}, // (25% negated (75%))
|
||||
};
|
||||
static byte[] TRIANGLE_TABLE =
|
||||
{
|
||||
|
@ -91,17 +91,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
public int unit;
|
||||
APU apu;
|
||||
|
||||
//reg0
|
||||
// reg0
|
||||
int duty_cnt, env_loop, env_constant, env_cnt_value;
|
||||
public bool len_halt;
|
||||
//reg1
|
||||
// reg1
|
||||
int sweep_en, sweep_divider_cnt, sweep_negate, sweep_shiftcount;
|
||||
bool sweep_reload;
|
||||
//reg2/3
|
||||
// reg2/3
|
||||
int len_cnt;
|
||||
public int timer_raw_reload_value, timer_reload_value;
|
||||
|
||||
//misc..
|
||||
// misc..
|
||||
int lenctr_en;
|
||||
|
||||
public void SyncState(Serializer ser)
|
||||
|
@ -143,7 +143,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
|
||||
public void WriteReg(int addr, byte val)
|
||||
{
|
||||
//Console.WriteLine("write pulse {0:X} {1:X}", addr, val);
|
||||
// Console.WriteLine("write pulse {0:X} {1:X}", addr, val);
|
||||
switch (addr)
|
||||
{
|
||||
case 0:
|
||||
|
@ -162,7 +162,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
case 2:
|
||||
timer_reload_value = (timer_reload_value & 0x700) | val;
|
||||
timer_raw_reload_value = timer_reload_value * 2 + 2;
|
||||
//if (unit == 1) Console.WriteLine("{0} timer_reload_value: {1}", unit, timer_reload_value);
|
||||
// if (unit == 1) Console.WriteLine("{0} timer_reload_value: {1}", unit, timer_reload_value);
|
||||
break;
|
||||
case 3:
|
||||
if (apu.len_clock_active)
|
||||
|
@ -181,11 +181,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
duty_step = 0;
|
||||
env_start_flag = 1;
|
||||
|
||||
//allow the lenctr_en to kill the len_cnt
|
||||
// allow the lenctr_en to kill the len_cnt
|
||||
set_lenctr_en(lenctr_en);
|
||||
|
||||
//serves as a useful note-on diagnostic
|
||||
//if(unit==1) Console.WriteLine("{0} timer_reload_value: {1}", unit, timer_reload_value);
|
||||
// serves as a useful note-on diagnostic
|
||||
// if(unit==1) Console.WriteLine("{0} timer_reload_value: {1}", unit, timer_reload_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -193,12 +193,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
public void set_lenctr_en(int value)
|
||||
{
|
||||
lenctr_en = value;
|
||||
//if the length counter is not enabled, then we must disable the length system in this way
|
||||
// if the length counter is not enabled, then we must disable the length system in this way
|
||||
if (lenctr_en == 0) len_cnt = 0;
|
||||
}
|
||||
|
||||
//state
|
||||
//why was all of this stuff not in the savestate???????
|
||||
// state
|
||||
int swp_divider_counter;
|
||||
bool swp_silence;
|
||||
int duty_step;
|
||||
|
@ -211,34 +210,34 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
|
||||
public void clock_length_and_sweep()
|
||||
{
|
||||
//this should be optimized to update only when `timer_reload_value` changes
|
||||
// this should be optimized to update only when `timer_reload_value` changes
|
||||
int sweep_shifter = timer_reload_value >> sweep_shiftcount;
|
||||
if (sweep_negate == 1)
|
||||
sweep_shifter = -sweep_shifter + unit;
|
||||
sweep_shifter += timer_reload_value;
|
||||
|
||||
//this sweep logic is always enabled:
|
||||
// this sweep logic is always enabled:
|
||||
swp_silence = (timer_reload_value < 8 || (sweep_shifter > 0x7FF)); // && sweep_negate == 0));
|
||||
|
||||
//does enable only block the pitch bend? does the clocking proceed?
|
||||
// does enable only block the pitch bend? does the clocking proceed?
|
||||
if (sweep_en == 1)
|
||||
{
|
||||
//clock divider
|
||||
// clock divider
|
||||
if (swp_divider_counter != 0) swp_divider_counter--;
|
||||
if (swp_divider_counter == 0)
|
||||
{
|
||||
swp_divider_counter = sweep_divider_cnt + 1;
|
||||
|
||||
//divider was clocked: process sweep pitch bend
|
||||
// divider was clocked: process sweep pitch bend
|
||||
if (sweep_shiftcount != 0 && !swp_silence)
|
||||
{
|
||||
timer_reload_value = sweep_shifter;
|
||||
timer_raw_reload_value = (timer_reload_value << 1) + 2;
|
||||
}
|
||||
//TODO - does this change the user's reload value or the latched reload value?
|
||||
// TODO - does this change the user's reload value or the latched reload value?
|
||||
}
|
||||
|
||||
//handle divider reload, after clocking happens
|
||||
// handle divider reload, after clocking happens
|
||||
if (sweep_reload)
|
||||
{
|
||||
swp_divider_counter = sweep_divider_cnt + 1;
|
||||
|
@ -246,7 +245,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
}
|
||||
}
|
||||
|
||||
//env_loopdoubles as "halt length counter"
|
||||
// env_loopdoubles as "halt length counter"
|
||||
if ((env_loop == 0 || len_halt) && len_cnt > 0)
|
||||
len_cnt--;
|
||||
}
|
||||
|
@ -296,22 +295,22 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
duty_step++;
|
||||
}
|
||||
duty_value = PULSE_DUTY[duty_cnt, duty_step] == 1;
|
||||
//reload timer
|
||||
// reload timer
|
||||
timer_counter = timer_raw_reload_value;
|
||||
}
|
||||
|
||||
int newsample;
|
||||
|
||||
if (duty_value) //high state of duty cycle
|
||||
if (duty_value) // high state of duty cycle
|
||||
{
|
||||
newsample = env_output;
|
||||
if (swp_silence || len_cnt == 0)
|
||||
newsample = 0; // silenced
|
||||
}
|
||||
else
|
||||
newsample = 0; //duty cycle is 0, silenced.
|
||||
newsample = 0; // duty cycle is 0, silenced.
|
||||
|
||||
//newsample -= env_output >> 1; //unbias
|
||||
// newsample -= env_output >> 1; //unbias
|
||||
if (newsample != sample)
|
||||
{
|
||||
apu.recalculate = true;
|
||||
|
@ -350,20 +349,20 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
{
|
||||
APU apu;
|
||||
|
||||
//reg0 (sweep)
|
||||
// reg0 (sweep)
|
||||
int env_cnt_value, env_loop, env_constant;
|
||||
public bool len_halt;
|
||||
|
||||
//reg2 (mode and period)
|
||||
// reg2 (mode and period)
|
||||
int mode_cnt, period_cnt;
|
||||
|
||||
//reg3 (length counter and envelop trigger)
|
||||
// reg3 (length counter and envelop trigger)
|
||||
int len_cnt;
|
||||
|
||||
//set from apu:
|
||||
// set from apu:
|
||||
int lenctr_en;
|
||||
|
||||
//state
|
||||
// state
|
||||
int shift_register = 1;
|
||||
int timer_counter;
|
||||
public int sample;
|
||||
|
@ -413,12 +412,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
ser.Sync("period_cnt", ref period_cnt);
|
||||
|
||||
ser.Sync("len_halt", ref len_halt);
|
||||
|
||||
//ser.Sync("mode_cnt", ref mode_cnt);
|
||||
//ser.Sync("period_cnt", ref period_cnt);
|
||||
|
||||
ser.Sync("len_cnt", ref len_cnt);
|
||||
|
||||
ser.Sync("lenctr_en", ref lenctr_en);
|
||||
|
||||
ser.Sync("shift_register", ref shift_register);
|
||||
|
@ -442,7 +436,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
case 0:
|
||||
env_cnt_value = val & 0xF;
|
||||
env_constant = (val >> 4) & 1;
|
||||
//we want to delay a halt until after a length clock if they happen on the same cycle
|
||||
// we want to delay a halt until after a length clock if they happen on the same cycle
|
||||
if (env_loop==0 && ((val >> 5) & 1)==1)
|
||||
{
|
||||
len_halt = true;
|
||||
|
@ -454,7 +448,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
case 2:
|
||||
period_cnt = NOISE_TABLE[val & 0xF];
|
||||
mode_cnt = (val >> 7) & 1;
|
||||
//Console.WriteLine("noise period: {0}, vol: {1}", (val & 0xF), env_cnt_value);
|
||||
// Console.WriteLine("noise period: {0}, vol: {1}", (val & 0xF), env_cnt_value);
|
||||
break;
|
||||
case 3:
|
||||
if (apu.len_clock_active)
|
||||
|
@ -478,8 +472,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
public void set_lenctr_en(int value)
|
||||
{
|
||||
lenctr_en = value;
|
||||
//Console.WriteLine("noise lenctr_en: " + lenctr_en);
|
||||
//if the length counter is not enabled, then we must disable the length system in this way
|
||||
// Console.WriteLine("noise lenctr_en: " + lenctr_en);
|
||||
// if the length counter is not enabled, then we must disable the length system in this way
|
||||
if (lenctr_en == 0) len_cnt = 0;
|
||||
}
|
||||
|
||||
|
@ -525,7 +519,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
if (timer_counter > 0) timer_counter--;
|
||||
if (timer_counter == 0 && period_cnt != 0)
|
||||
{
|
||||
//reload timer
|
||||
// reload timer
|
||||
timer_counter = period_cnt;
|
||||
int feedback_bit;
|
||||
if (mode_cnt == 1) feedback_bit = (shift_register >> 6) & 1;
|
||||
|
@ -551,13 +545,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
|
||||
public sealed class TriangleUnit
|
||||
{
|
||||
//reg0
|
||||
// reg0
|
||||
int linear_counter_reload, control_flag;
|
||||
//reg1 (n/a)
|
||||
//reg2/3
|
||||
// reg1 (n/a)
|
||||
// reg2/3
|
||||
int timer_cnt, halt_flag, len_cnt;
|
||||
public bool halt_2;
|
||||
//misc..
|
||||
// misc..
|
||||
int lenctr_en;
|
||||
int linear_counter, timer, timer_cnt_reload;
|
||||
int seq = 0;
|
||||
|
@ -589,13 +583,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
public void set_lenctr_en(int value)
|
||||
{
|
||||
lenctr_en = value;
|
||||
//if the length counter is not enabled, then we must disable the length system in this way
|
||||
// if the length counter is not enabled, then we must disable the length system in this way
|
||||
if (lenctr_en == 0) len_cnt = 0;
|
||||
}
|
||||
|
||||
public void WriteReg(int addr, byte val)
|
||||
{
|
||||
//Console.WriteLine("tri writes addr={0}, val={1:x2}", addr, val);
|
||||
// Console.WriteLine("tri writes addr={0}, val={1:x2}", addr, val);
|
||||
switch (addr)
|
||||
{
|
||||
case 0:
|
||||
|
@ -623,11 +617,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
}
|
||||
halt_flag = 1;
|
||||
|
||||
//allow the lenctr_en to kill the len_cnt
|
||||
// allow the lenctr_en to kill the len_cnt
|
||||
set_lenctr_en(lenctr_en);
|
||||
break;
|
||||
}
|
||||
//Console.WriteLine("tri timer_reload_value: {0}", timer_cnt_reload);
|
||||
// Console.WriteLine("tri timer_reload_value: {0}", timer_cnt_reload);
|
||||
}
|
||||
|
||||
public bool Debug_IsSilenced
|
||||
|
@ -649,11 +643,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
|
||||
public void Run()
|
||||
{
|
||||
//when clocked by timer
|
||||
//seq steps forward
|
||||
//except when linear counter or
|
||||
//length counter is 0
|
||||
|
||||
// when clocked by timer, seq steps forward
|
||||
// except when linear counter or length counter is 0
|
||||
bool en = len_cnt != 0 && linear_counter != 0;
|
||||
|
||||
bool do_clock = false;
|
||||
|
@ -672,9 +663,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
|
||||
newsample = TRIANGLE_TABLE[seq];
|
||||
|
||||
//special hack: frequently, games will use the maximum frequency triangle in order to mute it
|
||||
//apparently this results in the DAC for the triangle wave outputting a steady level at about 7.5
|
||||
//so we'll emulate it at the digital level
|
||||
// special hack: frequently, games will use the maximum frequency triangle in order to mute it
|
||||
// apparently this results in the DAC for the triangle wave outputting a steady level at about 7.5
|
||||
// so we'll emulate it at the digital level
|
||||
if (timer_cnt_reload == 1) newsample = 8;
|
||||
|
||||
if (newsample != sample)
|
||||
|
@ -687,7 +678,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
|
||||
public void clock_length_and_sweep()
|
||||
{
|
||||
//env_loopdoubles as "halt length counter"
|
||||
// env_loopdoubles as "halt length counter"
|
||||
if (len_cnt > 0 && halt_flag == 0)
|
||||
len_cnt--;
|
||||
}
|
||||
|
@ -704,21 +695,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
linear_counter--;
|
||||
}
|
||||
|
||||
//declick when the sound begins
|
||||
//if (halt_flag == 1 && control_flag == 0)
|
||||
//{
|
||||
// seq = 16;
|
||||
// Console.WriteLine("declicked triangle");
|
||||
//}
|
||||
|
||||
//declick on end of sound
|
||||
//bool en = len_cnt != 0 && linear_counter != 0;
|
||||
//if (!en)
|
||||
// if (sample < 0) sample++; else if (sample > 0) sample--;
|
||||
|
||||
halt_flag = control_flag;
|
||||
}
|
||||
} //class TriangleUnit
|
||||
} // class TriangleUnit
|
||||
|
||||
sealed class DMCUnit
|
||||
{
|
||||
|
@ -740,7 +719,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
bool loop_flag;
|
||||
int timer_reload;
|
||||
|
||||
//dmc delay per visual 2a03
|
||||
// dmc delay per visual 2a03
|
||||
int delay;
|
||||
|
||||
// this timer never stops, ever, so it is convenient to use for even/odd timing used elsewhere
|
||||
|
@ -778,8 +757,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
|
||||
ser.Sync("dmc_call_delay", ref delay);
|
||||
|
||||
//int sample = 0; //junk
|
||||
//ser.Sync("sample", ref sample);
|
||||
ser.EndSection();
|
||||
}
|
||||
|
||||
|
@ -792,7 +769,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
Clock();
|
||||
}
|
||||
|
||||
//Any time the sample buffer is in an empty state and bytes remaining is not zero, the following occur:
|
||||
// Any time the sample buffer is in an empty state and bytes remaining is not zero, the following occur:
|
||||
// also note that the halt for DMC DMA occurs on APU cycles only (hence the timer check)
|
||||
if (!sample_buffer_filled && sample_length > 0 && apu.dmc_dma_countdown == -1 && delay==0)
|
||||
{
|
||||
|
@ -806,7 +783,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
{
|
||||
delay = 2;
|
||||
}
|
||||
} else
|
||||
}
|
||||
else
|
||||
{
|
||||
if (timer % 2 == 1)
|
||||
{
|
||||
|
@ -837,18 +815,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
apu.call_from_write = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void Clock()
|
||||
{
|
||||
//If the silence flag is clear, bit 0 of the shift register is applied to the counter as follows:
|
||||
//if bit 0 is clear and the delta-counter is greater than 1, the counter is decremented by 2;
|
||||
//otherwise, if bit 0 is set and the delta-counter is less than 126, the counter is incremented by 2
|
||||
// If the silence flag is clear, bit 0 of the shift register is applied to the counter as follows:
|
||||
// if bit 0 is clear and the delta-counter is greater than 1, the counter is decremented by 2;
|
||||
// otherwise, if bit 0 is set and the delta-counter is less than 126, the counter is incremented by 2
|
||||
if (!out_silence)
|
||||
{
|
||||
//apply current sample bit to delta counter
|
||||
// apply current sample bit to delta counter
|
||||
if (out_shift.Bit(0))
|
||||
{
|
||||
if (out_deltacounter < 126)
|
||||
|
@ -859,26 +836,25 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
if (out_deltacounter > 1)
|
||||
out_deltacounter -= 2;
|
||||
}
|
||||
//Console.WriteLine("dmc out sample: {0}", out_deltacounter);
|
||||
// Console.WriteLine("dmc out sample: {0}", out_deltacounter);
|
||||
apu.recalculate = true;
|
||||
}
|
||||
|
||||
//The right shift register is clocked.
|
||||
// The right shift register is clocked.
|
||||
out_shift >>= 1;
|
||||
|
||||
//The bits-remaining counter is decremented. If it becomes zero, a new cycle is started.
|
||||
// The bits-remaining counter is decremented. If it becomes zero, a new cycle is started.
|
||||
if (out_bits_remaining == 0)
|
||||
{
|
||||
//The bits-remaining counter is loaded with 8.
|
||||
// The bits-remaining counter is loaded with 8.
|
||||
out_bits_remaining = 7;
|
||||
//If the sample buffer is empty then the silence flag is set
|
||||
// If the sample buffer is empty then the silence flag is set
|
||||
if (!sample_buffer_filled)
|
||||
{
|
||||
out_silence = true;
|
||||
//out_deltacounter = 64; //gonna go out on a limb here and guess this gets reset. could make some things pop, though, if they dont end at 0.
|
||||
}
|
||||
else
|
||||
//otherwise, the silence flag is cleared and the sample buffer is emptied into the shift register.
|
||||
// otherwise, the silence flag is cleared and the sample buffer is emptied into the shift register.
|
||||
{
|
||||
out_silence = false;
|
||||
out_shift = sample_buffer;
|
||||
|
@ -892,15 +868,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
{
|
||||
if (!en)
|
||||
{
|
||||
//If the DMC bit is clear, the DMC bytes remaining will be set to 0
|
||||
// If the DMC bit is clear, the DMC bytes remaining will be set to 0
|
||||
// and the DMC will silence when it empties.
|
||||
sample_length = 0;
|
||||
//and the DMC will silence when it empties.
|
||||
// (what does this mean? does out_deltacounter get reset to 0? maybe just that the out_silence flag gets set, but this is natural)
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//only start playback if playback is stopped
|
||||
//Console.Write(sample_length); Console.Write(" "); Console.Write(sample_buffer_filled); Console.Write(" "); Console.Write(apu.dmc_irq); Console.Write("\n");
|
||||
// only start playback if playback is stopped
|
||||
// Console.Write(sample_length); Console.Write(" "); Console.Write(sample_buffer_filled); Console.Write(" "); Console.Write(apu.dmc_irq); Console.Write("\n");
|
||||
if (sample_length == 0)
|
||||
{
|
||||
sample_address = user_address;
|
||||
|
@ -914,7 +889,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
}
|
||||
}
|
||||
|
||||
//irq is acknowledged or sure to be clear, in either case
|
||||
// irq is acknowledged or sure to be clear, in either case
|
||||
apu.dmc_irq = false;
|
||||
apu.SyncIRQ();
|
||||
}
|
||||
|
@ -926,7 +901,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
|
||||
public void WriteReg(int addr, byte val)
|
||||
{
|
||||
//Console.WriteLine("DMC writes addr={0}, val={1:x2}", addr, val);
|
||||
// Console.WriteLine("DMC writes addr={0}, val={1:x2}", addr, val);
|
||||
switch (addr)
|
||||
{
|
||||
case 0:
|
||||
|
@ -934,12 +909,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
loop_flag = val.Bit(6);
|
||||
timer_reload = DMC_RATE[val & 0xF];
|
||||
if (!irq_enabled) apu.dmc_irq = false;
|
||||
//apu.dmc_irq = false;
|
||||
// apu.dmc_irq = false;
|
||||
apu.SyncIRQ();
|
||||
break;
|
||||
case 1:
|
||||
out_deltacounter = val & 0x7F;
|
||||
//apu.nes.LogLine("~~ out_deltacounter set to {0}", out_deltacounter);
|
||||
// apu.nes.LogLine("~~ out_deltacounter set to {0}", out_deltacounter);
|
||||
apu.recalculate = true;
|
||||
break;
|
||||
case 2:
|
||||
|
@ -958,10 +933,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
sample_buffer = apu.nes.ReadMemory((ushort)sample_address);
|
||||
sample_buffer_filled = true;
|
||||
sample_address = (ushort)(sample_address + 1);
|
||||
//Console.WriteLine(sample_length);
|
||||
//Console.WriteLine(user_length);
|
||||
// Console.WriteLine(sample_length);
|
||||
// Console.WriteLine(user_length);
|
||||
sample_length--;
|
||||
//apu.pending_length_change = 1;
|
||||
// apu.pending_length_change = 1;
|
||||
}
|
||||
if (sample_length == 0)
|
||||
{
|
||||
|
@ -972,7 +947,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
}
|
||||
else if (irq_enabled) apu.dmc_irq = true;
|
||||
}
|
||||
//Console.WriteLine("fetching dmc byte: {0:X2}", sample_buffer);
|
||||
// Console.WriteLine("fetching dmc byte: {0:X2}", sample_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1030,14 +1005,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
dmc.Fetch();
|
||||
}
|
||||
|
||||
void sequencer_reset()
|
||||
{
|
||||
sequencer_counter = 0;
|
||||
sequencer_step = 0;
|
||||
}
|
||||
|
||||
//these figures are not valid for PAL. they must be recalculated with nintendulator's values above
|
||||
|
||||
int[][] sequencer_lut = new int[2][];
|
||||
|
||||
static int[][] sequencer_lut_ntsc = new int[][]{
|
||||
|
@ -1056,10 +1023,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
{
|
||||
seq_tick--;
|
||||
|
||||
// check if we will be doing the extra frame ticks or not
|
||||
if (seq_tick==0)
|
||||
{
|
||||
if (((val >> 7) & 1) > 0)
|
||||
sequencer_mode = (val >> 7) & 1;
|
||||
|
||||
// Console.WriteLine("apu 4017 = {0:X2}", val);
|
||||
// check if we will be doing the extra frame ticks or not
|
||||
if (sequencer_mode==1)
|
||||
{
|
||||
if (!doing_tick_quarter)
|
||||
{
|
||||
|
@ -1067,18 +1037,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
HalfFrame();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (seq_tick==0)
|
||||
{
|
||||
sequencer_mode = (val >> 7) & 1;
|
||||
//Console.WriteLine("apu 4017 = {0:X2}", val);
|
||||
sequencer_irq_inhibit = (val >> 6) & 1;
|
||||
if (sequencer_irq_inhibit == 1)
|
||||
{
|
||||
sequencer_irq_flag = false;
|
||||
}
|
||||
sequencer_reset();
|
||||
|
||||
sequencer_counter = 0;
|
||||
sequencer_step = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1117,23 +1084,23 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
|
||||
void sequencer_check()
|
||||
{
|
||||
//Console.WriteLine("sequencer mode {0} step {1}", sequencer_mode, sequencer_step);
|
||||
// Console.WriteLine("sequencer mode {0} step {1}", sequencer_mode, sequencer_step);
|
||||
bool quarter, half, reset;
|
||||
switch (sequencer_mode)
|
||||
{
|
||||
case 0: //4-step
|
||||
case 0: // 4-step
|
||||
quarter = true;
|
||||
half = sequencer_step == 1;
|
||||
reset = sequencer_step == 3;
|
||||
if (reset && sequencer_irq_inhibit == 0)
|
||||
{
|
||||
//Console.WriteLine("{0} {1,5} set irq_assert", nes.Frame, sequencer_counter);
|
||||
//sequencer_irq_assert = 2;
|
||||
// Console.WriteLine("{0} {1,5} set irq_assert", nes.Frame, sequencer_counter);
|
||||
// sequencer_irq_assert = 2;
|
||||
sequencer_irq_flag = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1: //5-step
|
||||
case 1: // 5-step
|
||||
quarter = sequencer_step != 3;
|
||||
half = sequencer_step == 1;
|
||||
reset = sequencer_step == 4;
|
||||
|
@ -1174,12 +1141,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
|
||||
public void NESSoftReset()
|
||||
{
|
||||
//need to study what happens to apu and stuff..
|
||||
// need to study what happens to apu and stuff..
|
||||
sequencer_irq = false;
|
||||
sequencer_irq_flag = false;
|
||||
_WriteReg(0x4015, 0);
|
||||
|
||||
//for 4017, its as if the last value written gets rewritten
|
||||
// for 4017, its as if the last value written gets rewritten
|
||||
sequencer_mode = (seq_val >> 7) & 1;
|
||||
sequencer_irq_inhibit = (seq_val >> 6) & 1;
|
||||
if (sequencer_irq_inhibit == 1)
|
||||
|
@ -1193,9 +1160,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
public void NESHardReset()
|
||||
{
|
||||
// "at power on it is as if $00 was written to $4017 9-12 cycles before the reset vector"
|
||||
// that translates to a starting value for the counter of -2
|
||||
|
||||
sequencer_counter = -2;
|
||||
// that translates to a starting value for the counter of -3
|
||||
sequencer_counter = -1;
|
||||
}
|
||||
|
||||
public void WriteReg(int addr, byte val)
|
||||
|
@ -1271,7 +1237,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
return (byte)ret;
|
||||
}
|
||||
default:
|
||||
//don't return 0xFF here or SMB will break
|
||||
// don't return 0xFF here or SMB will break
|
||||
return 0x00;
|
||||
}
|
||||
}
|
||||
|
@ -1283,13 +1249,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
case 0x4015:
|
||||
{
|
||||
byte ret = PeekReg(0x4015);
|
||||
//Console.WriteLine("{0} {1,5} $4015 clear irq, was at {2}", nes.Frame, sequencer_counter, sequencer_irq);
|
||||
// Console.WriteLine("{0} {1,5} $4015 clear irq, was at {2}", nes.Frame, sequencer_counter, sequencer_irq);
|
||||
sequencer_irq_flag = false;
|
||||
SyncIRQ();
|
||||
return ret;
|
||||
}
|
||||
default:
|
||||
//don't return 0xFF here or SMB will break
|
||||
// don't return 0xFF here or SMB will break
|
||||
return 0x00;
|
||||
}
|
||||
}
|
||||
|
@ -1328,7 +1294,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
|
||||
EmitSample();
|
||||
|
||||
//we need to predict if there will be a length clock here, because the sequencer ticks last, but the
|
||||
// we need to predict if there will be a length clock here, because the sequencer ticks last, but the
|
||||
// timer reload shouldn't happen if length clock and write happen simultaneously
|
||||
// I'm not sure if we can avoid this by simply processing the sequencer first
|
||||
// but at the moment that would break everything, so this is good enough for now
|
||||
|
@ -1339,11 +1305,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
len_clock_active = true;
|
||||
}
|
||||
|
||||
//handle writes
|
||||
//notes: this set up is a bit convoluded at the moment, mainly because APU behaviour is not entirely understood
|
||||
//in partiuclar, there are several clock pulses affecting the APU, and when new written are latched is not known in detail
|
||||
//the current code simply matches known behaviour
|
||||
|
||||
// handle writes
|
||||
// notes: this set up is a bit convoluded at the moment, mainly because APU behaviour is not entirely understood
|
||||
// in partiuclar, there are several clock pulses affecting the APU, and when new written are latched is not known in detail
|
||||
// the current code simply matches known behaviour
|
||||
if (pending_reg != -1)
|
||||
{
|
||||
if (pending_reg == 0x4015 || pending_reg == 0x4015 || pending_reg == 0x4003 || pending_reg==0x4007)
|
||||
|
@ -1375,9 +1340,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
SyncIRQ();
|
||||
nes.irq_apu = irq_pending;
|
||||
|
||||
//since the units run concurrently, the APU frame sequencer is ran last because
|
||||
//it can change the ouput values of the pulse/triangle channels
|
||||
//we want the changes to affect it on the *next* cycle.
|
||||
// since the units run concurrently, the APU frame sequencer is ran last because
|
||||
// it can change the ouput values of the pulse/triangle channels
|
||||
// we want the changes to affect it on the *next* cycle.
|
||||
|
||||
if (sequencer_irq_flag == false)
|
||||
sequencer_irq = false;
|
||||
|
@ -1433,7 +1398,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
int s_noise = noise.sample;
|
||||
int s_dmc = dmc.sample;
|
||||
|
||||
//int s_ext = 0; //gamepak
|
||||
// int s_ext = 0; //gamepak
|
||||
|
||||
/*
|
||||
if (!EnableSquare1) s_pulse0 = 0;
|
||||
|
@ -1443,7 +1408,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
if (!EnableDMC) s_dmc = 0;
|
||||
*/
|
||||
|
||||
//more properly correct
|
||||
// more properly correct
|
||||
float pulse_out, tnd_out;
|
||||
if (s_pulse0 == 0 && s_pulse1 == 0)
|
||||
pulse_out = 0;
|
||||
|
@ -1452,8 +1417,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
tnd_out = 0;
|
||||
else tnd_out = 159.79f / (1 / ((s_tri / 8227.0f) + (s_noise / 12241.0f /* * NOISEADJUST*/) + (s_dmc / 22638.0f)) + 100);
|
||||
float output = pulse_out + tnd_out;
|
||||
//output = output * 2 - 1;
|
||||
//this needs to leave enough headroom for straying DC bias due to the DMC unit getting stuck outputs. smb3 is bad about that.
|
||||
// output = output * 2 - 1;
|
||||
// this needs to leave enough headroom for straying DC bias due to the DMC unit getting stuck outputs. smb3 is bad about that.
|
||||
int mix = (int)(20000 * output);
|
||||
|
||||
dlist.Add(new Delta(sampleclock, mix - oldmix));
|
||||
|
|
|
@ -385,10 +385,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
//at least it should be, but something is off with that (start up time?) so it is 3,3,3,4,3 for now
|
||||
//NTSC:
|
||||
//sequence of ppu clocks per cpu clock: 3
|
||||
ByteBuffer cpu_sequence;
|
||||
public ByteBuffer cpu_sequence;
|
||||
static ByteBuffer cpu_sequence_NTSC = new ByteBuffer(new byte[] { 3, 3, 3, 3, 3 });
|
||||
static ByteBuffer cpu_sequence_PAL = new ByteBuffer(new byte[] { 3, 3, 3, 4, 3 });
|
||||
public int cpu_step, cpu_stepcounter, cpu_deadcounter;
|
||||
public int cpu_deadcounter;
|
||||
|
||||
public int oam_dma_index;
|
||||
public bool oam_dma_exec = false;
|
||||
|
@ -401,140 +401,122 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
public bool do_the_reread;
|
||||
public byte DB; //old data bus values from previous reads
|
||||
|
||||
|
||||
#if VS2012
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
internal void RunCpuOne()
|
||||
{
|
||||
cpu_stepcounter++;
|
||||
if (cpu_stepcounter == cpu_sequence[cpu_step])
|
||||
///////////////////////////
|
||||
// OAM DMA start
|
||||
///////////////////////////
|
||||
|
||||
if (sprdma_countdown > 0)
|
||||
{
|
||||
cpu_step++;
|
||||
if (cpu_step == 5) cpu_step = 0;
|
||||
cpu_stepcounter = 0;
|
||||
|
||||
///////////////////////////
|
||||
// OAM DMA start
|
||||
///////////////////////////
|
||||
|
||||
if (sprdma_countdown > 0)
|
||||
sprdma_countdown--;
|
||||
if (sprdma_countdown == 0)
|
||||
{
|
||||
sprdma_countdown--;
|
||||
if (sprdma_countdown == 0)
|
||||
if (cpu.TotalExecutedCycles % 2 == 0)
|
||||
{
|
||||
if (cpu.TotalExecutedCycles % 2 == 0)
|
||||
{
|
||||
cpu_deadcounter = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
cpu_deadcounter = 1;
|
||||
}
|
||||
oam_dma_exec = true;
|
||||
cpu.RDY = false;
|
||||
oam_dma_index = 0;
|
||||
special_case_delay = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (oam_dma_exec && apu.dmc_dma_countdown != 1 && !dmc_realign)
|
||||
{
|
||||
if (cpu_deadcounter == 0)
|
||||
{
|
||||
|
||||
if (oam_dma_index % 2 == 0)
|
||||
{
|
||||
oam_dma_byte = ReadMemory(oam_dma_addr);
|
||||
oam_dma_addr++;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteMemory(0x2004, oam_dma_byte);
|
||||
}
|
||||
oam_dma_index++;
|
||||
if (oam_dma_index == 512) oam_dma_exec = false;
|
||||
|
||||
cpu_deadcounter = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
cpu_deadcounter--;
|
||||
cpu_deadcounter = 1;
|
||||
}
|
||||
}
|
||||
else if (apu.dmc_dma_countdown == 1)
|
||||
{
|
||||
dmc_realign = true;
|
||||
}
|
||||
else if (dmc_realign)
|
||||
{
|
||||
dmc_realign = false;
|
||||
}
|
||||
/////////////////////////////
|
||||
// OAM DMA end
|
||||
/////////////////////////////
|
||||
|
||||
|
||||
/////////////////////////////
|
||||
// dmc dma start
|
||||
/////////////////////////////
|
||||
|
||||
if (apu.dmc_dma_countdown > 0)
|
||||
{
|
||||
oam_dma_exec = true;
|
||||
cpu.RDY = false;
|
||||
dmc_dma_exec = true;
|
||||
apu.dmc_dma_countdown--;
|
||||
if (apu.dmc_dma_countdown == 0)
|
||||
{
|
||||
apu.RunDMCFetch();
|
||||
dmc_dma_exec = false;
|
||||
apu.dmc_dma_countdown = -1;
|
||||
do_the_reread = true;
|
||||
}
|
||||
oam_dma_index = 0;
|
||||
special_case_delay = true;
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
// dmc dma end
|
||||
/////////////////////////////
|
||||
apu.RunOne(true);
|
||||
|
||||
if (cpu.RDY && !IRQ_delay)
|
||||
{
|
||||
cpu.IRQ = _irq_apu || Board.IRQSignal;
|
||||
}
|
||||
else if (special_case_delay || apu.dmc_dma_countdown == 3)
|
||||
{
|
||||
cpu.IRQ = _irq_apu || Board.IRQSignal;
|
||||
special_case_delay = false;
|
||||
}
|
||||
|
||||
cpu.ExecuteOne();
|
||||
apu.RunOne(false);
|
||||
|
||||
if (ppu.double_2007_read > 0)
|
||||
ppu.double_2007_read--;
|
||||
|
||||
if (do_the_reread && cpu.RDY)
|
||||
do_the_reread = false;
|
||||
|
||||
if (IRQ_delay)
|
||||
IRQ_delay = false;
|
||||
|
||||
if (!dmc_dma_exec && !oam_dma_exec && !cpu.RDY)
|
||||
{
|
||||
cpu.RDY = true;
|
||||
IRQ_delay = true;
|
||||
}
|
||||
|
||||
ppu.ppu_open_bus_decay(0);
|
||||
|
||||
Board.ClockCPU();
|
||||
ppu.PostCpuInstructionOne();
|
||||
}
|
||||
|
||||
if (oam_dma_exec && apu.dmc_dma_countdown != 1 && !dmc_realign)
|
||||
{
|
||||
if (cpu_deadcounter == 0)
|
||||
{
|
||||
|
||||
if (oam_dma_index % 2 == 0)
|
||||
{
|
||||
oam_dma_byte = ReadMemory(oam_dma_addr);
|
||||
oam_dma_addr++;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteMemory(0x2004, oam_dma_byte);
|
||||
}
|
||||
oam_dma_index++;
|
||||
if (oam_dma_index == 512) oam_dma_exec = false;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
cpu_deadcounter--;
|
||||
}
|
||||
}
|
||||
else if (apu.dmc_dma_countdown == 1)
|
||||
{
|
||||
dmc_realign = true;
|
||||
}
|
||||
else if (dmc_realign)
|
||||
{
|
||||
dmc_realign = false;
|
||||
}
|
||||
/////////////////////////////
|
||||
// OAM DMA end
|
||||
/////////////////////////////
|
||||
|
||||
|
||||
/////////////////////////////
|
||||
// dmc dma start
|
||||
/////////////////////////////
|
||||
|
||||
if (apu.dmc_dma_countdown > 0)
|
||||
{
|
||||
cpu.RDY = false;
|
||||
dmc_dma_exec = true;
|
||||
apu.dmc_dma_countdown--;
|
||||
if (apu.dmc_dma_countdown == 0)
|
||||
{
|
||||
apu.RunDMCFetch();
|
||||
dmc_dma_exec = false;
|
||||
apu.dmc_dma_countdown = -1;
|
||||
do_the_reread = true;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
// dmc dma end
|
||||
/////////////////////////////
|
||||
apu.RunOne(true);
|
||||
|
||||
if (cpu.RDY && !IRQ_delay)
|
||||
{
|
||||
cpu.IRQ = _irq_apu || Board.IRQSignal;
|
||||
}
|
||||
else if (special_case_delay || apu.dmc_dma_countdown == 3)
|
||||
{
|
||||
cpu.IRQ = _irq_apu || Board.IRQSignal;
|
||||
special_case_delay = false;
|
||||
}
|
||||
|
||||
cpu.ExecuteOne();
|
||||
apu.RunOne(false);
|
||||
|
||||
if (ppu.double_2007_read > 0)
|
||||
ppu.double_2007_read--;
|
||||
|
||||
if (do_the_reread && cpu.RDY)
|
||||
do_the_reread = false;
|
||||
|
||||
if (IRQ_delay)
|
||||
IRQ_delay = false;
|
||||
|
||||
if (!dmc_dma_exec && !oam_dma_exec && !cpu.RDY)
|
||||
{
|
||||
cpu.RDY = true;
|
||||
IRQ_delay = true;
|
||||
}
|
||||
|
||||
Board.ClockCPU();
|
||||
}
|
||||
|
||||
#if VS2012
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
public byte ReadReg(int addr)
|
||||
{
|
||||
byte ret_spec;
|
||||
|
@ -681,7 +663,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
case 0x4013:
|
||||
apu.WriteReg(addr, val);
|
||||
break;
|
||||
case 0x4014: Exec_OAMDma(val); break;
|
||||
case 0x4014:
|
||||
//schedule a sprite dma event for beginning 1 cycle in the future.
|
||||
//this receives 2 because thats just the way it works out.
|
||||
oam_dma_addr = (ushort)(val << 8);
|
||||
sprdma_countdown = 1;
|
||||
break;
|
||||
case 0x4015: apu.WriteReg(addr, val); break;
|
||||
case 0x4016:
|
||||
if (_isVS)
|
||||
|
@ -740,15 +727,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
return 0;
|
||||
}
|
||||
|
||||
void Exec_OAMDma(byte val)
|
||||
{
|
||||
//schedule a sprite dma event for beginning 1 cycle in the future.
|
||||
//this receives 2 because thats just the way it works out.
|
||||
oam_dma_addr = (ushort)(val << 8);
|
||||
|
||||
sprdma_countdown = 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the provided palette as current.
|
||||
/// Applies the current deemph settings if needed to expand a 64-entry palette to 512
|
||||
|
|
|
@ -60,8 +60,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
ser.Sync("cpu_accumulate", ref cpu_accumulate);
|
||||
ser.Sync("_irq_apu", ref _irq_apu);
|
||||
ser.Sync("sprdma_countdown", ref sprdma_countdown);
|
||||
ser.Sync("cpu_step", ref cpu_step);
|
||||
ser.Sync("cpu_stepcounter", ref cpu_stepcounter);
|
||||
ser.Sync("cpu_deadcounter", ref cpu_deadcounter);
|
||||
|
||||
//oam related
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
//http://nesdev.parodius.com/bbs/viewtopic.php?p=4571&sid=db4c7e35316cc5d734606dd02f11dccb
|
||||
|
||||
using System;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using BizHawk.Common;
|
||||
|
||||
|
@ -8,6 +6,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
{
|
||||
public sealed partial class PPU
|
||||
{
|
||||
public int cpu_step, cpu_stepcounter;
|
||||
|
||||
// this only handles region differences within the PPU
|
||||
int preNMIlines;
|
||||
int postNMIlines;
|
||||
|
@ -203,6 +203,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
|
||||
public void SyncState(Serializer ser)
|
||||
{
|
||||
ser.Sync("cpu_step", ref cpu_step);
|
||||
ser.Sync("cpu_stepcounter", ref cpu_stepcounter);
|
||||
ser.Sync("ppudead", ref ppudead);
|
||||
ser.Sync("idleSynch", ref idleSynch);
|
||||
ser.Sync("NMI_PendingInstructions", ref NMI_PendingInstructions);
|
||||
|
@ -255,32 +257,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
ppu_open_bus_decay_timer = new int[8];
|
||||
}
|
||||
|
||||
#if VS2012
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
void TriggerNMI()
|
||||
{
|
||||
nes.cpu.NMI = true;
|
||||
}
|
||||
|
||||
//this gets called once after each cpu instruction executes.
|
||||
//anything that needs to happen at instruction granularity can get checked here
|
||||
//to save having to check it at ppu cycle granularity
|
||||
public void PostCpuInstructionOne()
|
||||
{
|
||||
if (NMI_PendingInstructions > 0)
|
||||
{
|
||||
NMI_PendingInstructions--;
|
||||
if (NMI_PendingInstructions <= 0)
|
||||
{
|
||||
TriggerNMI();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if VS2012
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
void runppu(int x)
|
||||
{
|
||||
//run one ppu cycle at a time so we can interact with the ppu and clockPPU at high granularity
|
||||
|
@ -318,13 +294,36 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
|
||||
ppur.status.cycle++;
|
||||
is_even_cycle = !is_even_cycle;
|
||||
//might not actually run a cpu cycle if there are none to be run right now
|
||||
nes.RunCpuOne();
|
||||
|
||||
// Here we execute a CPU instruction if enough PPU cycles have passed
|
||||
// also do other things that happen at instruction level granularity
|
||||
cpu_stepcounter++;
|
||||
if (cpu_stepcounter == nes.cpu_sequence[cpu_step])
|
||||
{
|
||||
cpu_step++;
|
||||
if (cpu_step == 5) cpu_step = 0;
|
||||
cpu_stepcounter = 0;
|
||||
|
||||
// this is where the CPU instruction is called
|
||||
nes.RunCpuOne();
|
||||
|
||||
// decay the ppu bus, approximating real behaviour
|
||||
ppu_open_bus_decay(0);
|
||||
|
||||
// Check for NMIs
|
||||
if (NMI_PendingInstructions > 0)
|
||||
{
|
||||
NMI_PendingInstructions--;
|
||||
if (NMI_PendingInstructions <= 0)
|
||||
{
|
||||
nes.cpu.NMI = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Reg2002_vblank_active_pending)
|
||||
{
|
||||
//if (Reg2002_vblank_active_pending)
|
||||
Reg2002_vblank_active = 1;
|
||||
Reg2002_vblank_active = 1;
|
||||
Reg2002_vblank_active_pending = false;
|
||||
}
|
||||
|
||||
|
@ -337,10 +336,5 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
nes.Board.ClockPPU();
|
||||
}
|
||||
}
|
||||
|
||||
//hack
|
||||
//public bool PAL = false;
|
||||
//bool SPRITELIMIT = true;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -309,6 +309,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
public Reg_2000 reg_2000;
|
||||
public Reg_2001 reg_2001;
|
||||
byte reg_2003;
|
||||
|
||||
void regs_reset()
|
||||
{
|
||||
//TODO - would like to reconstitute the entire PPU instead of all this..
|
||||
|
@ -396,12 +397,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
return (byte)((Reg2002_vblank_active << 7) | (Reg2002_objhit << 6) | (Reg2002_objoverflow << 5) | (ppu_open_bus & 0x1F));
|
||||
}
|
||||
|
||||
void clear_2002()
|
||||
{
|
||||
Reg2002_objhit = Reg2002_objoverflow = 0;
|
||||
Reg2002_vblank_clear_pending = true;
|
||||
}
|
||||
|
||||
//OAM ADDRESS (write)
|
||||
void write_2003(int addr, byte value)
|
||||
{
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
//http://nesdev.parodius.com/bbs/viewtopic.php?p=4571&sid=db4c7e35316cc5d734606dd02f11dccb
|
||||
|
||||
//TODO - correctly emulate PPU OFF state
|
||||
//TODO - correctly emulate PPU OFF state
|
||||
|
||||
using BizHawk.Common;
|
||||
using System;
|
||||
|
@ -10,6 +8,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
sealed partial class PPU
|
||||
{
|
||||
const int kFetchTime = 2;
|
||||
const int kLineTime = 341;
|
||||
|
||||
struct BGDataRecord
|
||||
{
|
||||
|
@ -64,7 +63,26 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
void Read_bgdata(ref BGDataRecord bgdata)
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
Read_bgdata(i, ref bgdata);
|
||||
runppu(1);
|
||||
|
||||
if (PPUON && i==6)
|
||||
{
|
||||
ppu_was_on = true;
|
||||
}
|
||||
|
||||
if (PPUON && i==7)
|
||||
{
|
||||
if (!race_2006)
|
||||
ppur.increment_hsc();
|
||||
|
||||
if (ppur.status.cycle == 256 && !race_2006)
|
||||
ppur.increment_vs();
|
||||
|
||||
ppu_was_on = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// attempt to emulate graphics pipeline behaviour
|
||||
|
@ -78,7 +96,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
if (reg_2001.color_disable)
|
||||
pixelcolor_latch_2 &= 0x30;
|
||||
|
||||
xbuf[(target - 2)] = PaletteAdjustPixel(pixelcolor_latch_2);
|
||||
//TODO - check flashing sirens in werewolf
|
||||
//tack on the deemph bits. THESE MAY BE ORDERED WRONG. PLEASE CHECK IN THE PALETTE CODE
|
||||
xbuf[(target - 2)] = (short)(pixelcolor_latch_2 | reg_2001.intensity_lsl_6);
|
||||
}
|
||||
|
||||
if (row_check >= 1)
|
||||
|
@ -96,10 +116,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
case 0:
|
||||
ppu_addr_temp = ppur.get_ntread();
|
||||
bgdata.nt = ppubus_read(ppu_addr_temp, true, true);
|
||||
runppu(1);
|
||||
break;
|
||||
case 1:
|
||||
runppu(1);
|
||||
break;
|
||||
case 2:
|
||||
{
|
||||
|
@ -111,61 +129,26 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
if ((ppur.ht & 2) != 0) at >>= 2;
|
||||
at &= 0x03;
|
||||
at <<= 2;
|
||||
bgdata.at = at;
|
||||
runppu(1);
|
||||
|
||||
bgdata.at = at;
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
runppu(1);
|
||||
break;
|
||||
case 4:
|
||||
ppu_addr_temp = ppur.get_ptread(bgdata.nt);
|
||||
bgdata.pt_0 = ppubus_read(ppu_addr_temp, true, true);
|
||||
runppu(1);
|
||||
break;
|
||||
case 5:
|
||||
runppu(1);
|
||||
break;
|
||||
case 6:
|
||||
ppu_addr_temp |= 8;
|
||||
bgdata.pt_1 = ppubus_read(ppu_addr_temp, true, true);
|
||||
|
||||
|
||||
runppu(1);
|
||||
if (PPUON)
|
||||
{
|
||||
ppu_was_on = true;
|
||||
}
|
||||
|
||||
break;
|
||||
case 7:
|
||||
|
||||
runppu(1);
|
||||
//horizontal scroll clocked at cycle 3 and then
|
||||
//vertical scroll at 256
|
||||
if (PPUON)
|
||||
{
|
||||
if (!race_2006)
|
||||
ppur.increment_hsc();
|
||||
|
||||
if (ppur.status.cycle == 256 && !race_2006)
|
||||
ppur.increment_vs();
|
||||
}
|
||||
|
||||
ppu_was_on = false;
|
||||
break;
|
||||
} //switch(cycle)
|
||||
}
|
||||
|
||||
//TODO - check flashing sirens in werewolf
|
||||
short PaletteAdjustPixel(int pixel)
|
||||
{
|
||||
//tack on the deemph bits. THESE MAY BE ORDERED WRONG. PLEASE CHECK IN THE PALETTE CODE
|
||||
return (short)(pixel | reg_2001.intensity_lsl_6);
|
||||
}
|
||||
|
||||
const int kLineTime = 341;
|
||||
public unsafe void FrameAdvance()
|
||||
{
|
||||
BGDataRecord* bgdata = stackalloc BGDataRecord[34]; //one at the end is junk, it can never be rendered
|
||||
|
@ -191,12 +174,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
runppu(3);
|
||||
bool nmi_destiny = reg_2000.vblank_nmi_gen && Reg2002_vblank_active;
|
||||
runppu(3);
|
||||
if (nmi_destiny) TriggerNMI();
|
||||
if (nmi_destiny) nes.cpu.NMI = true;
|
||||
|
||||
nes.Board.AtVsyncNMI();
|
||||
runppu(postNMIlines * kLineTime - delay);
|
||||
|
||||
//this seems to run just before the dummy scanline begins
|
||||
clear_2002();
|
||||
//this seems to happen just before the dummy scanline begins
|
||||
Reg2002_objhit = Reg2002_objoverflow = 0;
|
||||
Reg2002_vblank_clear_pending = true;
|
||||
|
||||
idleSynch ^= true;
|
||||
|
||||
|
@ -224,8 +209,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
yp = sl - 1;
|
||||
ppuphase = PPUPHASE.BG;
|
||||
|
||||
// "If PPUADDR is not less then 8 when rendering starts, the first 8 fights in OAM and written to from
|
||||
// the current location off PPUADDR"
|
||||
// "If PPUADDR is not less then 8 when rendering starts, the first 8 bytes in OAM and written to from
|
||||
// the current location off PPUADDR"
|
||||
if (sl == 0 && PPUON && reg_2003 >= 8 && region==Region.NTSC)
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
|
@ -233,7 +218,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
OAM[i] = OAM[reg_2003 & 0xF8 + i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (NTViewCallback != null && yp == NTViewCallback.Scanline) NTViewCallback.Callback();
|
||||
if (PPUViewCallback != null && yp == PPUViewCallback.Scanline) PPUViewCallback.Callback();
|
||||
|
||||
|
@ -255,7 +240,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
|
||||
//check all the conditions that can cause things to render in these 8px
|
||||
bool renderspritenow = show_obj_new && (xt > 0 || reg_2001.show_obj_leftmost);
|
||||
bool renderbgnow;
|
||||
bool renderbgnow;
|
||||
bool hit_pending = false;
|
||||
|
||||
for (int xp = 0; xp < 8; xp++, rasterpos++)
|
||||
{
|
||||
|
@ -350,7 +336,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
{
|
||||
if (yp >= read_value && yp < read_value + spriteHeight && PPUON)
|
||||
{
|
||||
Reg2002_objoverflow = true;
|
||||
hit_pending = true;
|
||||
//Reg2002_objoverflow = true;
|
||||
}
|
||||
|
||||
if (yp >= read_value && yp < read_value + spriteHeight && spr_true_count == 0)
|
||||
|
@ -395,10 +382,34 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
|
||||
//process the current clock's worth of bg data fetching
|
||||
//this needs to be split into 8 pieces or else exact sprite 0 hitting wont work due to the cpu not running while the sprite renders below
|
||||
|
||||
|
||||
if (PPUON)
|
||||
Read_bgdata(xp, ref bgdata[xt + 2]);
|
||||
else
|
||||
runppu(1);
|
||||
|
||||
runppu(1);
|
||||
|
||||
if (PPUON && xp == 6)
|
||||
{
|
||||
ppu_was_on = true;
|
||||
}
|
||||
|
||||
if (PPUON && xp == 7)
|
||||
{
|
||||
if (!race_2006)
|
||||
ppur.increment_hsc();
|
||||
|
||||
if (ppur.status.cycle == 256 && !race_2006)
|
||||
ppur.increment_vs();
|
||||
|
||||
ppu_was_on = false;
|
||||
}
|
||||
|
||||
if (hit_pending)
|
||||
{
|
||||
hit_pending = false;
|
||||
Reg2002_objoverflow = true;
|
||||
}
|
||||
|
||||
renderbgnow = show_bg_new && (xt > 0 || reg_2001.show_bg_leftmost);
|
||||
//bg pos is different from raster pos due to its offsetability.
|
||||
|
@ -482,8 +493,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
} //oamcount loop
|
||||
|
||||
pipeline(pixelcolor, target, xt*32+xp);
|
||||
target++;
|
||||
|
||||
target++;
|
||||
} //loop across 8 pixels
|
||||
} //loop across 32 tiles
|
||||
}
|
||||
|
@ -749,14 +759,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
Read_bgdata(ref bgdata[xt]);
|
||||
}
|
||||
|
||||
// this sequence is tuned to pass 10-even_odd_timing.nes
|
||||
|
||||
// this sequence is tuned to pass 10-even_odd_timing.nes
|
||||
runppu(1);
|
||||
|
||||
runppu(1);
|
||||
|
||||
runppu(1);
|
||||
|
||||
|
||||
runppu(1);
|
||||
bool evenOddDestiny = PPUON;
|
||||
|
||||
|
@ -775,7 +784,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
|
||||
//idle for pre NMI lines
|
||||
runppu(preNMIlines * kLineTime);
|
||||
|
||||
} //FrameAdvance
|
||||
|
||||
void FrameAdvance_ppudead()
|
||||
|
@ -786,7 +794,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
//should write to those regs during that time, it needs
|
||||
//to wait for vblank
|
||||
|
||||
runppu(241 * kLineTime+3);// -8*3);
|
||||
runppu(241 * kLineTime-3);// -8*3);
|
||||
ppudead--;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,12 +18,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
{
|
||||
byte* buf = Api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM);
|
||||
var size = Api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM);
|
||||
if (buf == null)
|
||||
if (buf == null && Api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.SGB_CARTRAM) > 0)
|
||||
{
|
||||
buf = Api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.SGB_CARTRAM);
|
||||
size = Api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.SGB_CARTRAM);
|
||||
}
|
||||
|
||||
if (buff == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var ret = new byte[size];
|
||||
Marshal.Copy((IntPtr)buf, ret, 0, size);
|
||||
return ret;
|
||||
|
|
Loading…
Reference in New Issue