C64 core: tape loading added, lots of bugfixes and improvements

This commit is contained in:
Kabuto 2015-09-28 01:30:58 +02:00
parent 69d0d813eb
commit 1e9564a337
15 changed files with 1174 additions and 930 deletions

View File

@ -1965,7 +1965,7 @@ namespace BizHawk.Client.EmuHawk
if (VersionInfo.DeveloperBuild)
{
return FormatFilter(
"Rom Files", "*.nes;*.fds;*unf;*.sms;*.gg;*.sg;*.pce;*.sgx;*.bin;*.smd;*.rom;*.a26;*.a78;*.lnx;*.m3u;*.cue;*.ccd;*.exe;*.gb;*.gbc;*.gba;*.gen;*.md;*.col;.int;*.smc;*.sfc;*.prg;*.d64;*.g64;*.crt;*.sgb;*.xml;*.z64;*.v64;*.n64;*.ws;*.wsc;*.dsk;*.do;*.po;*.psf;*.minipsf;*.nsf;%ARCH%",
"Rom Files", "*.nes;*.fds;*unf;*.sms;*.gg;*.sg;*.pce;*.sgx;*.bin;*.smd;*.rom;*.a26;*.a78;*.lnx;*.m3u;*.cue;*.ccd;*.exe;*.gb;*.gbc;*.gba;*.gen;*.md;*.col;.int;*.smc;*.sfc;*.prg;*.d64;*.g64;*.crt;*.tap;*.sgb;*.xml;*.z64;*.v64;*.n64;*.ws;*.wsc;*.dsk;*.do;*.po;*.psf;*.minipsf;*.nsf;%ARCH%",
"Music Files", "*.psf;*.minipsf;*.sid;*.nsf",
"Disc Images", "*.cue;*.ccd;*.m3u",
"NES", "*.nes;*.fds;*.unf;*.nsf;%ARCH%",
@ -1986,7 +1986,7 @@ namespace BizHawk.Client.EmuHawk
"PlayStation", "*.cue;*.ccd;*.m3u",
"PSX Executables (experimental)", "*.exe",
"PSF Playstation Sound File", "*.psf;*.minipsf",
"Commodore 64 (experimental)", "*.prg; *.d64, *.g64; *.crt;%ARCH%",
"Commodore 64 (experimental)", "*.prg; *.d64, *.g64; *.crt; *.tap;%ARCH%",
"SID Commodore 64 Music File", "*.sid;%ARCH%",
"Nintendo 64", "*.z64;*.v64;*.n64",
"WonderSwan", "*.ws;*.wsc;%ARCH%",

View File

@ -315,6 +315,7 @@ namespace BizHawk.Emulation.Common
case ".T64":
case ".G64":
case ".CRT":
case ".TAP":
game.System = "C64";
break;

View File

@ -151,6 +151,7 @@
<Compile Include="Computers\Commodore64\C64.IDebuggable.cs">
<DependentUpon>C64.cs</DependentUpon>
</Compile>
<Compile Include="Computers\Commodore64\C64.IDisassemblable.cs" />
<Compile Include="Computers\Commodore64\C64.IDriveLight.cs">
<DependentUpon>C64.cs</DependentUpon>
</Compile>
@ -176,11 +177,11 @@
<Compile Include="Computers\Commodore64\Cartridge\Mapper0013.cs" />
<Compile Include="Computers\Commodore64\Cartridge\Mapper0020.cs" />
<Compile Include="Computers\Commodore64\CassettePort\CassettePortDevice.cs" />
<Compile Include="Computers\Commodore64\CassettePort\Tape.cs" />
<Compile Include="Computers\Commodore64\InputFileInfo.cs" />
<Compile Include="Computers\Commodore64\Media\PRG.cs" />
<Compile Include="Computers\Commodore64\Cartridge\Cart.cs" />
<Compile Include="Computers\Commodore64\MOS\CartridgePort.cs" />
<Compile Include="Computers\Commodore64\MOS\CassettePort.cs" />
<Compile Include="Computers\Commodore64\MOS\Chip2114.cs" />
<Compile Include="Computers\Commodore64\MOS\Chip23XX.cs" />
<Compile Include="Computers\Commodore64\MOS\Chip4864.cs" />

View File

@ -2931,5 +2931,10 @@ namespace BizHawk.Emulation.Cores.Components.M6502
if (!rdy_freeze)
mi++;
} //ExecuteOne
public bool AtInstructionStart()
{
return Microcode[opcode][mi] >= Uop.End;
}
}
}

View File

@ -51,15 +51,110 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
}
}
public IMemoryCallbackSystem MemoryCallbacks
{
[FeatureNotImplemented]
get { throw new NotImplementedException(); }
}
public bool CanStep(StepType type)
{
switch (type)
{
case StepType.Into:
case StepType.Over:
case StepType.Out:
return true;
default:
return false;
}
}
[FeatureNotImplemented]
public void Step(StepType type) { throw new NotImplementedException(); }
public bool CanStep(StepType type) { return false; }
}
public void Step(StepType type)
{
switch (type)
{
case StepType.Into:
StepInto();
break;
case StepType.Out:
StepOut();
break;
case StepType.Over:
StepOver();
break;
}
}
private void StepInto()
{
while (board.cpu.AtInstructionStart())
{
DoCycle();
}
while (!board.cpu.AtInstructionStart())
{
DoCycle();
}
}
private void StepOver()
{
var instruction = board.cpu.Peek(board.cpu.PC);
if (instruction == JSR)
{
var destination = board.cpu.PC + JSRSize;
while (board.cpu.PC != destination)
{
StepInto();
}
}
else
{
StepInto();
}
}
private void StepOut()
{
var instr = board.cpu.Peek(board.cpu.PC);
JSRCount = instr == JSR ? 1 : 0;
var bailOutFrame = Frame + 1;
while (true)
{
StepInto();
instr = board.cpu.Peek(board.cpu.PC);
if (instr == JSR)
{
JSRCount++;
}
else if ((instr == RTS || instr == RTI) && JSRCount <= 0)
{
StepInto();
JSRCount = 0;
break;
}
else if (instr == RTS || instr == RTI)
{
JSRCount--;
}
else //Emergency Bailout Logic
{
if (Frame == bailOutFrame)
{
break;
}
}
}
}
private int JSRCount = 0;
private const byte JSR = 0x20;
private const byte RTI = 0x40;
private const byte RTS = 0x60;
private const byte JSRSize = 3;
public IMemoryCallbackSystem MemoryCallbacks { get; private set; }
}
}

View File

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
{
public partial class C64 : IDisassemblable
{
public string Cpu
{
get
{
return "6510";
}
set
{
}
}
public string PCRegisterName
{
get { return "PC"; }
}
public IEnumerable<string> AvailableCpus
{
get { yield return "6510"; }
}
public string Disassemble(MemoryDomain m, uint addr, out int length)
{
return Components.M6502.MOS6502X.Disassemble((ushort)addr, out length, (a) => m.PeekByte(a));
}
}
}

View File

@ -102,6 +102,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
sid.HardReset();
vic.HardReset();
userPort.HardReset();
cassPort.HardReset();
// because of how mapping works, the cpu needs to be hard reset twice
cpu.HardReset();

View File

@ -4,6 +4,7 @@ using System.IO;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Computers.Commodore64.MOS;
using System.Windows.Forms;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
{
@ -21,8 +22,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
isReleased: false
)]
[ServiceNotApplicable(typeof(IRegionable), typeof(ISettable<,>))]
sealed public partial class C64 : IEmulator, IStatable, IInputPollable, IDriveLight, IDebuggable
{
sealed public partial class C64 : IEmulator, IStatable, IInputPollable, IDriveLight, IDebuggable, IDisassemblable
{
// framework
public C64(CoreComm comm, GameInfo game, byte[] rom, string romextension)
{
@ -33,13 +34,37 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
inputFileInfo.Data = rom;
inputFileInfo.Extension = romextension;
CoreComm = comm;
Init(Region.PAL);
Nullable<Region> region = queryUserForRegion();
if (region == null)
{
throw new Exception("Can't construct new C64 because you didn't choose anything");
}
Init(region.Value);
cyclesPerFrame = board.vic.CyclesPerFrame;
SetupMemoryDomains();
HardReset();
MemoryCallbacks = new MemoryCallbackSystem();
HardReset();
(ServiceProvider as BasicServiceProvider).Register<IVideoProvider>(board.vic);
}
}
private Nullable<Region> queryUserForRegion()
{
Form prompt = new Form() { Width = 160, Height = 120, FormBorderStyle = FormBorderStyle.FixedDialog, Text = "Region selector", StartPosition = FormStartPosition.CenterScreen };
Label textLabel = new Label() { Left = 10, Top = 10, Width = 260, Text = "Please choose a region:" };
RadioButton palButton = new RadioButton() { Left = 10, Top = 30, Width = 70, Text = "PAL", Checked = true };
RadioButton ntscButton = new RadioButton() { Left = 80, Top = 30, Width = 70, Text = "NTSC" };
Button confirmation = new Button() { Text = "Ok", Left = 40, Width = 80, Top = 60, DialogResult = DialogResult.OK };
confirmation.Click += (sender, e) => { prompt.Close(); };
prompt.Controls.Add(textLabel);
prompt.Controls.Add(palButton);
prompt.Controls.Add(ntscButton);
prompt.Controls.Add(confirmation);
prompt.AcceptButton = confirmation;
return prompt.ShowDialog() == DialogResult.OK ? palButton.Checked ? new Nullable<Region>(Region.PAL) : ntscButton.Checked ? new Nullable<Region>(Region.NTSC) : null : null;
}
// internal variables
private int _frame = 0;
@ -63,7 +88,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
_frame = 0;
_lagcount = 0;
_islag = false;
}
frameCycles = 0;
}
// audio/video
public void EndAsyncSound() { } //TODO
@ -101,79 +127,69 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
}
}
// process frame
public void FrameAdvance(bool render, bool rendersound)
int frameCycles;
private void DoCycle()
{
if (frameCycles == 0) {
board.inputRead = false;
board.PollInput();
board.cpu.LagCycles = 0;
}
//disk.Execute();
board.Execute();
frameCycles++;
// load PRG file if needed
if (loadPrg)
{
// check to see if cpu PC is at the BASIC warm start vector
if (board.cpu.PC == ((board.ram.Peek(0x0303) << 8) | board.ram.Peek(0x0302)))
{
//board.ram.Poke(0x0302, 0xAE);
//board.ram.Poke(0x0303, 0xA7);
////board.ram.Poke(0x0302, board.ram.Peek(0x0308));
////board.ram.Poke(0x0303, board.ram.Peek(0x0309));
//if (inputFileInfo.Data.Length >= 6)
//{
// board.ram.Poke(0x0039, inputFileInfo.Data[4]);
// board.ram.Poke(0x003A, inputFileInfo.Data[5]);
//}
PRG.Load(board.pla, inputFileInfo.Data);
loadPrg = false;
}
}
if (frameCycles == cyclesPerFrame)
{
board.Flush();
_islag = !board.inputRead;
if (_islag)
_lagcount++;
frameCycles -= cyclesPerFrame;
_frame++;
//Console.WriteLine("CPUPC: " + C64Util.ToHex(board.cpu.PC, 4) + " 1541PC: " + C64Util.ToHex(disk.PC, 4));
int test = board.cpu.LagCycles;
DriveLightOn = DriveLED;
}
}
// process frame
public void FrameAdvance(bool render, bool rendersound)
{
board.inputRead = false;
board.PollInput();
board.cpu.LagCycles = 0;
do
{
DoCycle();
}
while (frameCycles != 0);
}
for (int count = 0; count < cyclesPerFrame; count++)
{
//disk.Execute();
board.Execute();
#if false
if (board.cpu.PC == 0xE16F && (board.cpu.ReadPort() & 0x7) == 7)
{
// HUGE kernal hack to load files
// the only purpose for this is to be able to run the Lorenz
// test suite!
int fileNameLength = board.ram.Peek(0xB7);
int fileNameOffset = board.ram.Peek(0xBB) | ((int)board.ram.Peek(0xBC) << 8);
byte[] fileNameRaw = new byte[fileNameLength];
for (int i = 0; i < fileNameLength; i++)
{
fileNameRaw[i] = board.ram.Peek(fileNameOffset + i);
}
var enc = System.Text.Encoding.ASCII;
string fileName = enc.GetString(fileNameRaw);
string filePath = Path.Combine(@"E:\Programming\Visual Studio 2013\Vice\testprogs\general\Lorenz-2.15\src\", fileName + ".prg");
if (File.Exists(filePath))
{
PRG.Load(board.pla, File.ReadAllBytes(filePath));
}
board.cpu.PC = 0xE1B5;
}
#endif
// load PRG file if needed
if (loadPrg)
{
// check to see if cpu PC is at the BASIC warm start vector
if (board.cpu.PC == ((board.ram.Peek(0x0303) << 8) | board.ram.Peek(0x0302)))
{
//board.ram.Poke(0x0302, 0xAE);
//board.ram.Poke(0x0303, 0xA7);
////board.ram.Poke(0x0302, board.ram.Peek(0x0308));
////board.ram.Poke(0x0303, board.ram.Peek(0x0309));
//if (inputFileInfo.Data.Length >= 6)
//{
// board.ram.Poke(0x0039, inputFileInfo.Data[4]);
// board.ram.Poke(0x003A, inputFileInfo.Data[5]);
//}
PRG.Load(board.pla, inputFileInfo.Data);
loadPrg = false;
}
}
}
board.Flush();
_islag = !board.inputRead;
if (_islag)
_lagcount++;
_frame++;
//Console.WriteLine("CPUPC: " + C64Util.ToHex(board.cpu.PC, 4) + " 1541PC: " + C64Util.ToHex(disk.PC, 4));
int test = board.cpu.LagCycles;
DriveLightOn = DriveLED;
}
private void HandleFirmwareError(string file)
private void HandleFirmwareError(string file)
{
System.Windows.Forms.MessageBox.Show("the C64 core is referencing a firmware file which could not be found. Please make sure it's in your configured C64 firmwares folder. The referenced filename is: " + file);
throw new FileNotFoundException();
@ -212,6 +228,13 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
{
board.cartPort.Connect(cart);
}
break;
case @".TAP":
CassettePort.Tape tape = CassettePort.Tape.Load(inputFileInfo.Data);
if (tape != null)
{
board.cassPort.Connect(tape);
}
break;
case @".PRG":
if (inputFileInfo.Data.Length > 2)
@ -238,5 +261,5 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
board.HardReset();
//disk.HardReset();
}
}
}
}

View File

@ -9,26 +9,33 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.CassettePort
{
public class CassettePortDevice
{
public Func<bool> ReadDataOutput;
public Func<bool> ReadMotor;
public Func<bool> ReadDataOutput;
public Func<bool> ReadMotor;
Commodore64.CassettePort.Tape tape;
public void HardReset()
{
}
public void HardReset()
{
if (tape != null) tape.rewind();
}
virtual public bool ReadDataInputBuffer()
{
return true;
}
virtual public bool ReadDataInputBuffer()
{
return tape != null && !ReadMotor() ? tape.read() : true;
}
virtual public bool ReadSenseBuffer()
{
return true;
}
virtual public bool ReadSenseBuffer()
{
return tape == null; // Just assume that "play" is constantly pressed as long as a tape is inserted
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
internal void Connect(Tape tape)
{
this.tape = tape;
}
}
}

View File

@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.CassettePort
{
/**
* This class represents a tape. Only TAP-style tapes are supported for now.
*/
class Tape
{
private byte[] tapeData;
private byte version;
private uint pos, cycle, start, end;
public Tape(byte version, byte[] tapeData, uint start, uint end)
{
this.version = version;
this.tapeData = tapeData;
this.start = start;
this.end = end;
rewind();
}
// Rewinds the tape back to start
public void rewind()
{
pos = start;
cycle = 0;
}
// Reads from tape, this will tell the caller if the flag pin should be raised
public bool read()
{
if (cycle == 0)
{
Console.WriteLine("Tape @ " + pos.ToString());
if (pos >= end)
{
return true;
}
else
{
cycle = ((uint)tapeData[pos++])*8;
if (cycle == 0)
{
if (version == 0)
{
cycle = 256 * 8; // unspecified overflow condition
}
else
{
cycle = BitConverter.ToUInt32(tapeData, (int)pos-1)>>8;
pos += 3;
if (cycle == 0)
{
throw new Exception("Bad tape data");
}
}
}
}
}
// Send a single negative pulse at the end of a cycle
return --cycle != 0;
}
// Try to construct a tape file from file data. Returns null if not a tape file, throws exceptions for bad tape files.
// (Note that some error conditions aren't caught right here.)
static public Tape Load(byte[] tapeFile)
{
Tape result = null;
if (System.Text.Encoding.ASCII.GetString(tapeFile, 0, 12) == "C64-TAPE-RAW")
{
byte version = tapeFile[12];
if (version > 1) throw new Exception("This tape has an unsupported version");
uint size = BitConverter.ToUInt32(tapeFile, 16);
if (size + 20 != tapeFile.Length)
{
throw new Exception("Tape file header specifies a length that doesn't match the file size");
}
result = new Tape(version, tapeFile, 20, (uint)tapeFile.Length);
}
return result;
}
}
}

View File

@ -1,30 +0,0 @@
using System;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
public class CassettePort
{
public Func<bool> ReadDataOutput;
public Func<bool> ReadMotor;
public void HardReset()
{
}
virtual public bool ReadDataInputBuffer()
{
return true;
}
virtual public bool ReadSenseBuffer()
{
return true;
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
}

View File

@ -103,9 +103,14 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
}
}
// ------------------------------------
internal bool AtInstructionStart()
{
return cpu.AtInstructionStart();
}
public ushort PC
// ------------------------------------
public ushort PC
{
get
{

File diff suppressed because it is too large Load Diff

View File

@ -11,8 +11,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
static int vblankstart = 0x00D % lines;
static int vblankend = 0x018 % lines;
static int hblankoffset = 20;
static int hblankstart = (0x18C + hblankoffset) % scanwidth;
static int hblankend = (0x1F0 + hblankoffset) % scanwidth;
static int hblankstart = (0x18C + hblankoffset) % scanwidth - 8; // -8 because the VIC repeats internal pixel cycles around 0x18C
static int hblankend = (0x1F0 + hblankoffset) % scanwidth - 8;
static int[] timing = Vic.TimingBuilder_XRaster(0x19C, 0x200, scanwidth, 0x18C, 8);
static int[] fetch = Vic.TimingBuilder_Fetch(timing, 0x174);

View File

@ -74,7 +74,10 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
lastRasterLine = rasterLine;
}
// display enable compare
// display enable compare
if (rasterLine == 0)
badlineEnable = false;
if (rasterLine == 0x030)
badlineEnable |= displayEnable;