Merge branch 'master' into waterbox2

# Conflicts:
#	BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.ISaveRam.cs
This commit is contained in:
adelikat 2017-06-19 10:04:46 -05:00
commit 4982af7521
17 changed files with 1050 additions and 448 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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,22 +50,50 @@ 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()
{
maria.Execute(cycle, scanline);
cycle++;
cpu_cycle++;
tia._hsyncCnt++;
if (cpu_cycle <= (2 + (slow_access ? 1 : 0)))
{
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)))
{
if (!cpu_is_halted)
{
cpu.ExecuteOne();
}
else
{
// 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 (cpu_halt_pending)
{
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
@ -88,17 +119,6 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
slow_access = false;
}
}
if (cycle == 454)
{
scanline++;
cycle = 0;
tia._hsyncCnt = 0;
}
}
}
private void GetControllerState(IController controller)

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using BizHawk.Emulation.Common;

View File

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

View File

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

View File

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

View File

@ -198,7 +198,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
}
// state
//why was all of this stuff not in the savestate???????
int swp_divider_counter;
bool swp_silence;
int duty_step;
@ -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);
@ -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;
@ -704,18 +695,6 @@ 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
@ -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();
}
@ -806,7 +783,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
{
delay = 2;
}
} else
}
else
{
if (timer % 2 == 1)
{
@ -837,7 +815,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
apu.call_from_write = false;
}
}
}
}
@ -875,7 +852,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
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.
@ -893,9 +869,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
if (!en)
{
// If the DMC bit is clear, the DMC bytes remaining will be set to 0
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)
sample_length = 0;
}
else
{
@ -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;
}
}
}
@ -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)
@ -1343,7 +1309,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
// 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)

View File

@ -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,19 +401,8 @@ 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])
{
cpu_step++;
if (cpu_step == 5) cpu_step = 0;
cpu_stepcounter = 0;
///////////////////////////
// OAM DMA start
///////////////////////////
@ -525,16 +514,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
IRQ_delay = true;
}
ppu.ppu_open_bus_decay(0);
Board.ClockCPU();
ppu.PostCpuInstructionOne();
}
}
#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

View File

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

View File

@ -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,12 +294,35 @@ 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
// 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_pending = false;
}
@ -337,10 +336,5 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
nes.Board.ClockPPU();
}
}
//hack
//public bool PAL = false;
//bool SPRITELIMIT = true;
}
}

View File

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

View File

@ -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:
{
@ -112,60 +130,25 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
at &= 0x03;
at <<= 2;
bgdata.at = at;
runppu(1);
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,7 +209,7 @@ 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
// "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)
{
@ -256,6 +241,7 @@ 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 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,11 +382,35 @@ 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);
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.
//so adjust for that here
@ -483,7 +494,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
pipeline(pixelcolor, target, xt*32+xp);
target++;
} //loop across 8 pixels
} //loop across 32 tiles
}
@ -750,7 +760,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
}
// this sequence is tuned to pass 10-even_odd_timing.nes
runppu(1);
runppu(1);
@ -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--;
}
}

View File

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