diff --git a/BizHawk.Client.Common/RomLoader.cs b/BizHawk.Client.Common/RomLoader.cs index 096632fec1..9827265f2d 100644 --- a/BizHawk.Client.Common/RomLoader.cs +++ b/BizHawk.Client.Common/RomLoader.cs @@ -672,7 +672,7 @@ namespace BizHawk.Client.Common nextEmulator = new Atari7800(nextComm, game, rom.RomData, gamedbpath); break; case "C64": - var c64 = new C64(nextComm, game, rom.RomData, rom.Extension); + var c64 = new C64(nextComm, game, rom.RomData, rom.Extension, GetCoreSettings(), GetCoreSyncSettings()); nextEmulator = c64; break; case "GBA": diff --git a/BizHawk.Client.Common/movie/PlatformFrameRates.cs b/BizHawk.Client.Common/movie/PlatformFrameRates.cs index b093fc2e27..726c5ad73b 100644 --- a/BizHawk.Client.Common/movie/PlatformFrameRates.cs +++ b/BizHawk.Client.Common/movie/PlatformFrameRates.cs @@ -8,6 +8,12 @@ namespace BizHawk.Client.Common // these are political numbers, designed to be in accord with tasvideos.org tradition. theyre not necessarily mathematical factualities (although they may be in some cases) // it would be nice if we could turn this into a rational expression natively, and also, to write some comments about the derivation and ideal valees (since this seems to be where theyre all collected) // are we collecting them anywhere else? for avi-writing code perhaps? + + // just some constants, according to specs + private static readonly double PAL_CARRIER = 15625 * 283.75 + 25; // 4.43361875 MHz + private static readonly double NTSC_CARRIER = 4500000 * 227.5 / 286; // 3.579545454... MHz + private static readonly double PAL_N_CARRIER = 15625 * 229.25 + 25; // 3.58205625 MHz + private static readonly Dictionary _rates = new Dictionary { { "NES", 60.098813897440515532 }, // discussion here: http://forums.nesdev.com/viewtopic.php?t=492 ; a rational expression would be (19687500 / 11) / ((341*262-0.529780.5)/3) -> (118125000 / 1965513) -> 60.098813897440515529533511098629 (so our chosen number is very close) @@ -48,6 +54,11 @@ namespace BizHawk.Client.Common {"PSX", 44100.0*768*11/7/263/3413}, //59.292862562 {"PSX_PAL", 44100.0*768*11/7/314/3406}, //49.7645593576 + {"C64_PAL", PAL_CARRIER*2/9/312/63}, + {"C64_NTSC", NTSC_CARRIER*2/7/263/65}, + {"C64_NTSC_OLD", NTSC_CARRIER*2/7/262/64}, + {"C64_DREAN", PAL_N_CARRIER*2/7/312/65}, + //according to ryphecha, using //clocks[2] = { 53.693182e06, 53.203425e06 }; //ntsc console, pal console //lpf[2][2] = { { 263, 262.5 }, { 314, 312.5 } }; //ntsc,pal; noninterlaced, interlaced diff --git a/BizHawk.Client.EmuHawk/MainForm.cs b/BizHawk.Client.EmuHawk/MainForm.cs index a25d511693..6d63a1b68b 100644 --- a/BizHawk.Client.EmuHawk/MainForm.cs +++ b/BizHawk.Client.EmuHawk/MainForm.cs @@ -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%", diff --git a/BizHawk.Emulation.Common/Database/Database.cs b/BizHawk.Emulation.Common/Database/Database.cs index 16d6b65e85..ee30a95d48 100644 --- a/BizHawk.Emulation.Common/Database/Database.cs +++ b/BizHawk.Emulation.Common/Database/Database.cs @@ -315,6 +315,7 @@ namespace BizHawk.Emulation.Common case ".T64": case ".G64": case ".CRT": + case ".TAP": game.System = "C64"; break; diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 32e208be7d..4bc13fcbf3 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -151,6 +151,7 @@ C64.cs + C64.cs @@ -160,6 +161,7 @@ C64.cs + C64.cs @@ -176,14 +178,15 @@ + - + @@ -193,7 +196,8 @@ - + + diff --git a/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Execute.cs b/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Execute.cs index 30e0bec3fd..a79d0f8881 100644 --- a/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Execute.cs +++ b/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Execute.cs @@ -2931,5 +2931,10 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (!rdy_freeze) mi++; } //ExecuteOne + + public bool AtInstructionStart() + { + return Microcode[opcode][mi] >= Uop.End; + } } } diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/C64.IDebuggable.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/C64.IDebuggable.cs index f01f915c14..8e29874b02 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/C64.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/C64.IDebuggable.cs @@ -51,15 +51,110 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64 } } - public IMemoryCallbackSystem MemoryCallbacks + public bool CanStep(StepType type) { - [FeatureNotImplemented] - get { throw new NotImplementedException(); } + 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; } } } diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/C64.IDisassemblable.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/C64.IDisassemblable.cs new file mode 100644 index 0000000000..9afa0b3220 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/C64.IDisassemblable.cs @@ -0,0 +1,30 @@ +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 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)); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/C64.ISettable.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/C64.ISettable.cs new file mode 100644 index 0000000000..a19d5ebc69 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/C64.ISettable.cs @@ -0,0 +1,75 @@ +using BizHawk.Emulation.Common; + +using Newtonsoft.Json; + +using System; +using System.ComponentModel; +using System.Drawing; + + +namespace BizHawk.Emulation.Cores.Computers.Commodore64 +{ + public partial class C64 : ISettable + { + public C64Settings GetSettings() + { + return Settings.Clone(); + } + + public C64SyncSettings GetSyncSettings() + { + return SyncSettings.Clone(); + } + + public bool PutSettings(C64Settings o) + { + Settings = o; + return false; + } + + public bool PutSyncSettings(C64SyncSettings o) + { + SyncSettings = o; + return false; + } + + internal C64Settings Settings { get; private set; } + internal C64SyncSettings SyncSettings { get; private set; } + + public class C64Settings + { + public C64Settings Clone() + { + return (C64Settings)MemberwiseClone(); + } + + public C64Settings() + { + BizHawk.Common.SettingsUtil.SetDefaultValues(this); + } + } + + public class C64SyncSettings + { + [DisplayName("VIC type")] + [Description("Set the type of video chip to use")] + [DefaultValue(VicType.PAL)] + public VicType vicType { get; set; } + + public C64SyncSettings Clone() + { + return (C64SyncSettings)MemberwiseClone(); + } + + public C64SyncSettings() + { + BizHawk.Common.SettingsUtil.SetDefaultValues(this); + } + } + + public enum VicType + { + PAL, NTSC, NTSC_OLD, DREAN + } + } +} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/C64.Motherboard.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/C64.Motherboard.cs index 99633d578a..7fc07731ba 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/C64.Motherboard.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/C64.Motherboard.cs @@ -42,25 +42,48 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64 private C64 _c64; - public Motherboard(C64 c64, Region initRegion) + public Motherboard(C64 c64, C64.VicType initRegion) { // note: roms need to be added on their own externally _c64 = c64; - + int clockNum, clockDen, mainsFrq; + switch (initRegion) + { + case C64.VicType.PAL: + clockNum = 17734475; + clockDen = 18; + mainsFrq = 50; + break; + case C64.VicType.NTSC: + case C64.VicType.NTSC_OLD: + clockNum = 11250000; + clockDen = 11; + mainsFrq = 60; + break; + case C64.VicType.DREAN: + clockNum = 14328225; + clockDen = 14; + mainsFrq = 50; + break; + default: + throw new System.Exception(); + } cartPort = new CartridgePort(); cassPort = new CassettePortDevice(); - cia0 = new MOS6526(initRegion); - cia1 = new MOS6526(initRegion); + cia0 = new MOS6526(clockNum, clockDen*mainsFrq); + cia1 = new MOS6526(clockNum, clockDen*mainsFrq); colorRam = new Chip2114(); cpu = new MOS6510(); pla = new MOSPLA(); ram = new Chip4864(); serPort = new SerialPort(); - sid = MOS6581.Create(44100, initRegion); + sid = MOS6581.Create(44100, clockNum, clockDen); switch (initRegion) { - case Region.NTSC: vic = MOS6567.Create(); break; - case Region.PAL: vic = MOS6569.Create(); break; + case C64.VicType.NTSC: vic = MOS6567R8.Create(); break; + case C64.VicType.PAL: vic = MOS6569.Create(); break; + case C64.VicType.NTSC_OLD: vic = MOS6567R56A.Create(); break; + case C64.VicType.DREAN: vic = MOS6572.Create(); break; } userPort = new UserPortDevice(); } @@ -102,6 +125,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(); diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/C64.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/C64.cs index 3d0028a017..cfbfac420e 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/C64.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/C64.cs @@ -4,28 +4,25 @@ using System.IO; using BizHawk.Emulation.Common; using BizHawk.Emulation.Cores.Computers.Commodore64.MOS; +using System.Windows.Forms; namespace BizHawk.Emulation.Cores.Computers.Commodore64 { - // TODO: use the EMulation.Common Region enum - public enum Region - { - NTSC, - PAL - } - [CoreAttributes( "C64Hawk", "SaxxonPIke", isPorted: false, isReleased: false )] - [ServiceNotApplicable(typeof(IRegionable), typeof(ISettable<,>))] - sealed public partial class C64 : IEmulator, IStatable, IInputPollable, IDriveLight, IDebuggable + [ServiceNotApplicable(typeof(ISettable<,>))] + sealed public partial class C64 : IEmulator, IStatable, IInputPollable, IDriveLight, IDebuggable, IDisassemblable, IRegionable, ISettable { // framework - public C64(CoreComm comm, GameInfo game, byte[] rom, string romextension) + public C64(CoreComm comm, GameInfo game, byte[] rom, string romextension, object Settings, object SyncSettings) { + PutSyncSettings((C64SyncSettings)SyncSettings ?? new C64SyncSettings()); + PutSettings((C64Settings)Settings ?? new C64Settings()); + ServiceProvider = new BasicServiceProvider(this); InputCallbacks = new InputCallbackSystem(); @@ -33,14 +30,37 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64 inputFileInfo.Data = rom; inputFileInfo.Extension = romextension; CoreComm = comm; - Init(Region.PAL); + Init(this.SyncSettings.vicType); cyclesPerFrame = board.vic.CyclesPerFrame; SetupMemoryDomains(); + MemoryCallbacks = new MemoryCallbackSystem(); HardReset(); (ServiceProvider as BasicServiceProvider).Register(board.vic); } + + /*private DisplayType 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; + + if (prompt.ShowDialog() != DialogResult.OK || !palButton.Checked && !ntscButton.Checked) + { + throw new Exception("Can't construct new C64 because you didn't choose anything"); + } + return palButton.Checked ? DisplayType.PAL : DisplayType.NTSC; + }*/ + // internal variables private int _frame = 0; private int cyclesPerFrame; @@ -63,6 +83,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64 _frame = 0; _lagcount = 0; _islag = false; + frameCycles = 0; } // audio/video @@ -92,6 +113,12 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64 public IEmulatorServiceProvider ServiceProvider { get; private set; } + public DisplayType Region + { + get; + private set; + } + public void Dispose() { if (board.sid != null) @@ -101,76 +128,66 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64 } } + int frameCycles; + // process frame public void FrameAdvance(bool render, bool rendersound) { - board.inputRead = false; - board.PollInput(); - board.cpu.LagCycles = 0; - - for (int count = 0; count < cyclesPerFrame; count++) + do { - //disk.Execute(); - board.Execute(); + DoCycle(); + } + while (frameCycles != 0); + } -#if false - if (board.cpu.PC == 0xE16F && (board.cpu.ReadPort() & 0x7) == 7) + 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))) { - // HUGE kernal hack to load files - // the only purpose for this is to be able to run the Lorenz - // test suite! + //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)); - 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; - } + //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 (frameCycles == cyclesPerFrame) + { + board.Flush(); + _islag = !board.inputRead; - if (_islag) - _lagcount++; - _frame++; + if (_islag) + _lagcount++; + frameCycles -= cyclesPerFrame; + _frame++; - //Console.WriteLine("CPUPC: " + C64Util.ToHex(board.cpu.PC, 4) + " 1541PC: " + C64Util.ToHex(disk.PC, 4)); + //Console.WriteLine("CPUPC: " + C64Util.ToHex(board.cpu.PC, 4) + " 1541PC: " + C64Util.ToHex(disk.PC, 4)); - int test = board.cpu.LagCycles; - DriveLightOn = DriveLED; + int test = board.cpu.LagCycles; + DriveLightOn = DriveLED; + } } private void HandleFirmwareError(string file) @@ -190,7 +207,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64 return result; } - private void Init(Region initRegion) + private void Init(VicType initRegion) { board = new Motherboard(this, initRegion); InitRoms(); @@ -213,6 +230,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) loadPrg = true; diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/CassettePort/CassettePortDevice.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/CassettePort/CassettePortDevice.cs index d8f567713c..44aa71e893 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/CassettePort/CassettePortDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/CassettePort/CassettePortDevice.cs @@ -11,24 +11,31 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.CassettePort { public Func ReadDataOutput; public Func ReadMotor; + Commodore64.CassettePort.Tape tape; public void HardReset() { + if (tape != null) tape.rewind(); } virtual public bool ReadDataInputBuffer() { - return true; + return tape != null && !ReadMotor() ? tape.read() : true; } virtual public bool ReadSenseBuffer() { - return true; + 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); } + + internal void Connect(Tape tape) + { + this.tape = tape; + } } } diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/CassettePort/Tape.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/CassettePort/Tape.cs new file mode 100644 index 0000000000..2c04d1bf09 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/CassettePort/Tape.cs @@ -0,0 +1,101 @@ +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 readonly byte[] tapeData; + private readonly byte version; + private uint pos, cycle; + private readonly uint 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) + { + 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; + } + + public void SyncState(Serializer ser) + { + ser.BeginSection("tape"); + ser.Sync("pos", ref pos); + ser.Sync("cycle", ref cycle); + ser.EndSection(); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/CassettePort.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/CassettePort.cs deleted file mode 100644 index 26ddeb1b49..0000000000 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/CassettePort.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using BizHawk.Common; - -namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS -{ - public class CassettePort - { - public Func ReadDataOutput; - public Func 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); - } - } -} diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6510.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6510.cs index 557312b0a9..51cb927531 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6510.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6510.cs @@ -99,9 +99,14 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS } set { - lagCycles = value; - } - } + lagCycles = value; + } + } + + internal bool AtInstructionStart() + { + return cpu.AtInstructionStart(); + } // ------------------------------------ diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6526-2.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6526-2.cs index bc0d060fb1..30a3bdfd9d 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6526-2.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6526-2.cs @@ -233,7 +233,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS LatchedPort portA; LatchedPort portB; - public MOS6526_2(Region region) + public MOS6526_2(Common.DisplayType region) { a = new CiaTimer(serialPortA, underFlowA); b = new CiaTimer(serialPortB, underFlowB); @@ -241,10 +241,10 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS portB = new LatchedPort(); switch (region) { - case Region.NTSC: + case Common.DisplayType.NTSC: tod_period = 14318181 / 140; break; - case Region.PAL: + case Common.DisplayType.PAL: tod_period = 17734472 / 180; break; } diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6526.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6526.cs index 97a8ab2185..cec480ae07 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6526.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6526.cs @@ -51,7 +51,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS // ------------------------------------ bool alarmSelect; - Region chipRegion; bool cntPos; bool enableIntAlarm; bool enableIntFlag; @@ -77,16 +76,22 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS byte[] todAlarm; bool todAlarmPM; int todCounter; - int todCounterLatch; bool todIn; bool todPM; - + bool oldFlag; // ------------------------------------ - public MOS6526(Region region) + int todStepsNum; + int todStepsDen; + + // todStepsNum/todStepsDen is the number of clock cycles it takes the external clock source to advance one cycle + // (50 or 60 Hz depending on AC frequency in use). + // By default the CIA assumes 60 Hz and will thus count incorrectly when fed with 50 Hz. + public MOS6526(int todStepsNum, int todStepsDen) { - chipRegion = region; - enableIntTimer = new bool[2]; + this.todStepsNum = todStepsNum; + this.todStepsDen = todStepsDen; + enableIntTimer = new bool[2]; intTimer = new bool[2]; timerDelay = new int[2]; timerInMode = new InMode[2]; @@ -96,7 +101,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS timerRunMode = new RunMode[2]; tod = new byte[4]; todAlarm = new byte[4]; - SetTodIn(chipRegion); portA = new LatchedPort(); portB = new LatchedPort(); @@ -161,6 +165,10 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS cntPos = false; underflow[0] = false; underflow[1] = false; + + bool newFlag = ReadFlag(); + intFlag |= oldFlag && !newFlag; + oldFlag = newFlag; } } @@ -203,29 +211,14 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS todAlarm[1] = 0; todAlarm[2] = 0; todAlarm[3] = 0; - todCounter = todCounterLatch; - todIn = (chipRegion == Region.PAL); + todCounter = 0; + todIn = false; todPM = false; pinCnt = false; pinPC = true; } - private void SetTodIn(Region region) - { - switch (region) - { - case Region.NTSC: - todCounterLatch = 14318181 / 140; - todIn = false; - break; - case Region.PAL: - todCounterLatch = 17734472 / 180; - todIn = true; - break; - } - } - // ------------------------------------ private byte BCDAdd(byte i, byte j, out bool overflow) @@ -338,9 +331,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS { bool todV; - if (todCounter == 0) + if (todCounter <= 0) { - todCounter = todCounterLatch; + todCounter += todStepsNum*(todIn ? 6 : 5); tod[0] = BCDAdd(tod[0], 1, out todV); if (tod[0] >= 10) { @@ -366,7 +359,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS } } } - todCounter--; + todCounter -= todStepsDen; } } diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6567R56A.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6567R56A.cs new file mode 100644 index 0000000000..49570065e4 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6567R56A.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS +{ + // vic ntsc old + // TODO is everything right? it's mostly a copy from the other NTSC chip with tweaks wherever it was neccessary to fix something + static public class MOS6567R56A + { + static int cycles = 64; + static int scanwidth = cycles * 8; + static int lines = 262; + 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[] timing = Vic.TimingBuilder_XRaster(0x19C, 0x200, scanwidth, -1, -1); + static int[] fetch = Vic.TimingBuilder_Fetch(timing, 0x174); + static int[] ba = Vic.TimingBuilder_BA(fetch); + static int[] act = Vic.TimingBuilder_Act(timing, 0x004, 0x14C, hblankstart, hblankend); + + static int[][] pipeline = new int[][] + { + timing, + fetch, + ba, + act + }; + + static public Vic Create() + { + return new Vic( + cycles, lines, + pipeline, + 14318181 / 14, + hblankstart, hblankend, + vblankstart, vblankend + ); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6567.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6567R8.cs similarity index 79% rename from BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6567.cs rename to BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6567R8.cs index 939e8211f8..f6470b0d6a 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6567.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6567R8.cs @@ -1,41 +1,41 @@ -using System.Drawing; - -namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS -{ - // vic ntsc - static public class MOS6567 - { - static int cycles = 65; - static int scanwidth = cycles * 8; - static int lines = 263; - 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[] timing = Vic.TimingBuilder_XRaster(0x19C, 0x200, scanwidth, 0x18C, 8); - static int[] fetch = Vic.TimingBuilder_Fetch(timing, 0x174); - static int[] ba = Vic.TimingBuilder_BA(fetch); - static int[] act = Vic.TimingBuilder_Act(timing, 0x004, 0x14C, hblankstart, hblankend); - - static int[][] pipeline = new int[][] - { - timing, - fetch, - ba, - act - }; - - static public Vic Create() - { - return new Vic( - cycles, lines, - pipeline, - 14318181 / 14, - hblankstart, hblankend, - vblankstart, vblankend - ); - } - } -} +using System.Drawing; + +namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS +{ + // vic ntsc + static public class MOS6567R8 + { + static int cycles = 65; + static int scanwidth = cycles * 8; + static int lines = 263; + static int vblankstart = 0x00D % lines; + static int vblankend = 0x018 % lines; + static int hblankoffset = 20; + 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); + static int[] ba = Vic.TimingBuilder_BA(fetch); + static int[] act = Vic.TimingBuilder_Act(timing, 0x004, 0x14C, hblankstart, hblankend); + + static int[][] pipeline = new int[][] + { + timing, + fetch, + ba, + act + }; + + static public Vic Create() + { + return new Vic( + cycles, lines, + pipeline, + 14318181 / 14, + hblankstart, hblankend, + vblankstart, vblankend + ); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6572.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6572.cs new file mode 100644 index 0000000000..ad11e5737c --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6572.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS +{ + // pal n / drean - TODO correct? + class MOS6572 + { + static int cycles = 65; + static int scanwidth = cycles * 8; + static int lines = 312; + static int vblankstart = 0x12C % lines; + static int vblankend = 0x00F % lines; + static int hblankoffset = 20; + 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); + static int[] ba = Vic.TimingBuilder_BA(fetch); + static int[] act = Vic.TimingBuilder_Act(timing, 0x004, 0x14C, hblankstart, hblankend); + + static int[][] pipeline = new int[][] + { + timing, + fetch, + ba, + act + }; + + static public Vic Create() + { + return new Vic( + cycles, lines, + pipeline, + 14328225 / 14, + hblankstart, hblankend, + vblankstart, vblankend + ); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6581.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6581.cs index 9485937697..d19800e88c 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6581.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6581.cs @@ -4119,9 +4119,9 @@ } }; - static public Sid Create(int newSampleRate, Region newRegion) + static public Sid Create(int newSampleRate, int clockFrqNum, int clockFrqDen) { - return new Sid(waveTable, newSampleRate, newRegion); + return new Sid(waveTable, (uint)newSampleRate, (uint)clockFrqNum, (uint)clockFrqDen); } } } diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Sid.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Sid.cs index d74e8fc828..873dcfacb5 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Sid.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Sid.cs @@ -42,20 +42,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS public Func ReadPotX; public Func ReadPotY; - public Sid(int[][] newWaveformTable, int newSampleRate, Region newRegion) + public Sid(int[][] newWaveformTable, uint sampleRate, uint cyclesNum, uint cyclesDen) { - uint cyclesPerSec = 0; - uint cyclesNum; - uint cyclesDen; - uint sampleRate = 44100; - - switch (newRegion) - { - case Region.NTSC: cyclesNum = 14318181; cyclesDen = 14; break; - case Region.PAL: cyclesNum = 17734472; cyclesDen = 18; break; - default: return; - } - waveformTable = newWaveformTable; envelopes = new Envelope[3]; diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Vic.Parse.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Vic.Parse.cs index f510f09159..ac5d441678 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Vic.Parse.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Vic.Parse.cs @@ -145,8 +145,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS } else if (parseba == 0x1000) { - pinBA = !badline; - } + pinBA = !badline; + } else { parsecycleBAsprite0 = (parseba & 0x000F); diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Vic.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Vic.cs index ba587c6360..c1bbf6a699 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Vic.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Vic.cs @@ -75,6 +75,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS } // display enable compare + if (rasterLine == 0) + badlineEnable = false; + if (rasterLine == 0x030) badlineEnable |= displayEnable;