C64: Improved disk support.

This commit is contained in:
Anthony Konzel 2016-03-09 19:53:02 -06:00
parent aaa0da85fd
commit 9557a25301
10 changed files with 702 additions and 371 deletions

View File

@ -1,121 +1,121 @@
using BizHawk.Emulation.Common; using BizHawk.Emulation.Common;
using Newtonsoft.Json; using Newtonsoft.Json;
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Drawing; using System.Drawing;
namespace BizHawk.Emulation.Cores.Computers.Commodore64 namespace BizHawk.Emulation.Cores.Computers.Commodore64
{ {
// adelikat: changing settings to default object until there are actually settings, as the ui depends on it to know if there are any settings avaialable // adelikat: changing settings to default object until there are actually settings, as the ui depends on it to know if there are any settings avaialable
public partial class C64 : ISettable<C64.C64Settings, C64.C64SyncSettings> public partial class C64 : ISettable<C64.C64Settings, C64.C64SyncSettings>
{ {
public C64Settings GetSettings() public C64Settings GetSettings()
{ {
return Settings.Clone(); return Settings.Clone();
} }
public C64SyncSettings GetSyncSettings() public C64SyncSettings GetSyncSettings()
{ {
return SyncSettings.Clone(); return SyncSettings.Clone();
} }
public bool PutSettings(C64Settings o) public bool PutSettings(C64Settings o)
{ {
Settings = o; Settings = o;
return false; return false;
} }
public bool PutSyncSettings(C64SyncSettings o) public bool PutSyncSettings(C64SyncSettings o)
{ {
SyncSettings = o; SyncSettings = o;
return false; return false;
} }
internal C64Settings Settings { get; private set; } internal C64Settings Settings { get; private set; }
internal C64SyncSettings SyncSettings { get; private set; } internal C64SyncSettings SyncSettings { get; private set; }
public class C64Settings public class C64Settings
{ {
[DisplayName("Border type")] [DisplayName("Border type")]
[Description("Select how to show the border area")] [Description("Select how to show the border area")]
[DefaultValue(BorderType.SmallProportional)] [DefaultValue(BorderType.SmallProportional)]
public BorderType BorderType { get; set; } public BorderType BorderType { get; set; }
public C64Settings Clone() public C64Settings Clone()
{ {
return (C64Settings)MemberwiseClone(); return (C64Settings)MemberwiseClone();
} }
public C64Settings() public C64Settings()
{ {
BizHawk.Common.SettingsUtil.SetDefaultValues(this); BizHawk.Common.SettingsUtil.SetDefaultValues(this);
} }
} }
public class C64SyncSettings public class C64SyncSettings
{ {
[DisplayName("VIC type")] [DisplayName("VIC type")]
[Description("Set the type of video chip to use")] [Description("Set the type of video chip to use")]
[DefaultValue(VicType.Pal)] [DefaultValue(VicType.Pal)]
public VicType VicType { get; set; } public VicType VicType { get; set; }
[DisplayName("SID type")] [DisplayName("SID type")]
[Description("Set the type of sound chip to use")] [Description("Set the type of sound chip to use")]
[DefaultValue(SidType.OldR2)] [DefaultValue(SidType.OldR2)]
public SidType SidType { get; set; } public SidType SidType { get; set; }
[DisplayName("Tape drive type")] [DisplayName("Tape drive type")]
[Description("Set the type of tape drive attached")] [Description("Set the type of tape drive attached")]
[DefaultValue(TapeDriveType.None)] [DefaultValue(TapeDriveType.None)]
public TapeDriveType TapeDriveType { get; set; } public TapeDriveType TapeDriveType { get; set; }
[DisplayName("Disk drive type")] [DisplayName("Disk drive type")]
[Description("Set the type of disk drive attached")] [Description("Set the type of disk drive attached")]
[DefaultValue(DiskDriveType.None)] [DefaultValue(DiskDriveType.None)]
public DiskDriveType DiskDriveType { get; set; } public DiskDriveType DiskDriveType { get; set; }
public C64SyncSettings Clone() public C64SyncSettings Clone()
{ {
return (C64SyncSettings)MemberwiseClone(); return (C64SyncSettings)MemberwiseClone();
} }
public C64SyncSettings() public C64SyncSettings()
{ {
BizHawk.Common.SettingsUtil.SetDefaultValues(this); BizHawk.Common.SettingsUtil.SetDefaultValues(this);
} }
} }
public enum VicType public enum VicType
{ {
Pal, Ntsc, NtscOld, Drean Pal, Ntsc, NtscOld, Drean
} }
public enum CiaType public enum CiaType
{ {
Pal, Ntsc, PalRevA, NtscRevA Pal, Ntsc, PalRevA, NtscRevA
} }
public enum BorderType public enum BorderType
{ {
SmallProportional, SmallFixed, Normal, Full SmallProportional, SmallFixed, Normal, Full
} }
public enum SidType public enum SidType
{ {
OldR2, OldR3, OldR4AR, NewR5 OldR2, OldR3, OldR4AR, NewR5
} }
public enum TapeDriveType public enum TapeDriveType
{ {
None, Commodore1530 None, Commodore1530
} }
public enum DiskDriveType public enum DiskDriveType
{ {
None, Commodore1541, Commodore1541II, Commodore1571 None, Commodore1541, Commodore1541II
} }
} }
} }

View File

@ -156,17 +156,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
_board.Execute(); _board.Execute();
_frameCycles++; _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 != 0 && _board.Cpu.Pc == ((_board.Ram.Peek(0x0303) << 8) | _board.Ram.Peek(0x0302)))
{
Prg.Load(_board.Pla, _prgFile);
_loadPrg = false;
}
}
if (_frameCycles != _cyclesPerFrame) if (_frameCycles != _cyclesPerFrame)
{ {
return; return;
@ -188,8 +177,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
} }
private Motherboard _board; private Motherboard _board;
private bool _loadPrg;
[SaveState.DoNotSave] private byte[] _prgFile;
private byte[] GetFirmware(int length, params string[] names) private byte[] GetFirmware(int length, params string[] names)
{ {
@ -212,10 +199,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
if (diskDriveType == DiskDriveType.None) if (diskDriveType == DiskDriveType.None)
diskDriveType = DiskDriveType.Commodore1541; diskDriveType = DiskDriveType.Commodore1541;
break; break;
case C64Format.D71:
if (diskDriveType == DiskDriveType.None)
diskDriveType = DiskDriveType.Commodore1571;
break;
case C64Format.T64: case C64Format.T64:
case C64Format.TAP: case C64Format.TAP:
if (tapeDriveType == TapeDriveType.None) if (tapeDriveType == TapeDriveType.None)
@ -223,6 +206,19 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
tapeDriveType = TapeDriveType.Commodore1530; tapeDriveType = TapeDriveType.Commodore1530;
} }
break; break;
case C64Format.CRT:
// Nothing required.
break;
case C64Format.Unknown:
if (rom.Length >= 0xFE00)
{
throw new Exception("The image format is not known, and too large to be used as a PRG.");
}
if (diskDriveType == DiskDriveType.None)
diskDriveType = DiskDriveType.Commodore1541;
break;
default:
throw new Exception("The image format is not yet supported by the Commodore 64 core.");
} }
} }
@ -269,11 +265,25 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
_board.TapeDrive.Insert(tape); _board.TapeDrive.Insert(tape);
} }
break; break;
default: case C64Format.Unknown:
if (rom.Length > 2) var prgDisk = new DiskBuilder
{ {
_loadPrg = true; Entries = new List<DiskBuilder.Entry>
_prgFile = rom; {
new DiskBuilder.Entry
{
Closed = true,
Data = rom,
Locked = false,
Name = "PRG",
RecordLength = 0,
Type = DiskBuilder.FileType.Program
}
}
}.Build();
if (prgDisk != null)
{
_board.DiskDrive.InsertMedia(prgDisk);
} }
break; break;
} }
@ -294,9 +304,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
case DiskDriveType.Commodore1541II: case DiskDriveType.Commodore1541II:
_board.DiskDrive.DriveRom.Flash(GetFirmware(0x4000, "Drive1541II")); _board.DiskDrive.DriveRom.Flash(GetFirmware(0x4000, "Drive1541II"));
break; break;
case DiskDriveType.Commodore1571:
_board.DiskDrive.DriveRom.Flash(GetFirmware(0x8000, "Drive1571"));
break;
} }
} }

View File

@ -173,6 +173,22 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
_icr |= 0x80; _icr |= 0x80;
} }
break; break;
case 0xE:
var oldCra = _cra;
WriteRegister(addr, val);
// Toggle output begins high when timer starts.
if ((_cra & 0x05) == 0x05 && (oldCra & 0x01) == 0)
_prb |= 0x40;
break;
case 0xF:
var oldCrb = _crb;
WriteRegister(addr, val);
// Toggle output begins high when timer starts.
if ((_crb & 0x05) == 0x05 && (oldCrb & 0x01) == 0)
_prb |= 0x80;
break;
default: default:
WriteRegister(addr, val); WriteRegister(addr, val);
break; break;

View File

@ -1,69 +1,69 @@
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{ {
public sealed partial class Cia public sealed partial class Cia
{ {
private void CountTod() private void CountTod()
{ {
if (_todCounter > 0) if (_todCounter > 0)
{ {
_todCounter -= _todDen; _todCounter -= _todDen;
return; return;
} }
_todCounter += _todNum * ((_cra & 0x80) != 0 ? 6 : 5); _todCounter += _todNum * ((_cra & 0x80) != 0 ? 6 : 5);
_tod10Ths++; _tod10Ths++;
if (_tod10Ths > 9) if (_tod10Ths > 9)
{ {
_tod10Ths = 0; _tod10Ths = 0;
_todlo = (_todSec & 0x0F) + 1; _todlo = (_todSec & 0x0F) + 1;
_todhi = (_todSec >> 4); _todhi = (_todSec >> 4);
if (_todlo > 9) if (_todlo > 9)
{ {
_todlo = 0; _todlo = 0;
_todhi++; _todhi++;
} }
if (_todhi > 5) if (_todhi > 5)
{ {
_todSec = 0; _todSec = 0;
_todlo = (_todMin & 0x0F) + 1; _todlo = (_todMin & 0x0F) + 1;
_todhi = (_todMin >> 4); _todhi = (_todMin >> 4);
if (_todlo > 9) if (_todlo > 9)
{ {
_todlo = 0; _todlo = 0;
_todhi++; _todhi++;
} }
if (_todhi > 5) if (_todhi > 5)
{ {
_todMin = 0; _todMin = 0;
_todlo = (_todHr & 0x0F) + 1; _todlo = (_todHr & 0x0F) + 1;
_todhi = (_todHr >> 4); _todhi = (_todHr >> 4);
_todHr &= 0x80; _todHr &= 0x80;
if (_todlo > 9) if (_todlo > 9)
{ {
_todlo = 0; _todlo = 0;
_todhi++; _todhi++;
} }
_todHr |= (_todhi << 4) | _todlo; _todHr |= (_todhi << 4) | _todlo;
if ((_todHr & 0x1F) > 0x11) if ((_todHr & 0x1F) > 0x11)
{ {
_todHr &= 0x80 ^ 0x80; _todHr &= 0x80 ^ 0x80;
} }
} }
else else
{ {
_todMin = (_todhi << 4) | _todlo; _todMin = (_todhi << 4) | _todlo;
} }
} }
else else
{ {
_todSec = (_todhi << 4) | _todlo; _todSec = (_todhi << 4) | _todlo;
} }
} }
if (_tod10Ths == _alm10Ths && _todSec == _almSec && _todMin == _almMin && _todHr == _almHr) if (_tod10Ths == _alm10Ths && _todSec == _almSec && _todMin == _almMin && _todHr == _almHr)
{ {
TriggerInterrupt(4); TriggerInterrupt(4);
} }
} }
} }
} }

View File

@ -164,7 +164,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
_prb &= 0x7F; _prb &= 0x7F;
_tbPrb7NegativeNextCycle = false; _tbPrb7NegativeNextCycle = false;
} }
switch (_taState) switch (_taState)
{ {
@ -268,6 +268,11 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{ {
CheckIrqs(); CheckIrqs();
} }
if ((_cra & 0x02) != 0)
_ddra |= 0x40;
if ((_crb & 0x02) != 0)
_ddrb |= 0x80;
} }
private void Ta_Count() private void Ta_Count()
@ -412,9 +417,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
_tbState = TimerState.LoadThenCount; _tbState = TimerState.LoadThenCount;
} }
if ((_cra & 0x02) != 0) if ((_crb & 0x02) != 0)
{ {
if ((_cra & 0x04) != 0) if ((_crb & 0x04) != 0)
{ {
_tbPrb7NegativeNextCycle = true; _tbPrb7NegativeNextCycle = true;
_prb |= 0x80; _prb |= 0x80;
@ -423,7 +428,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{ {
_prb ^= 0x80; _prb ^= 0x80;
} }
_ddrb |= 0x80;
} }
} }

View File

@ -1,85 +1,85 @@
using System; using System;
using BizHawk.Common; using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{ {
public sealed class LatchedPort public sealed class LatchedPort
{ {
public int Direction; public int Direction;
public int Latch; public int Latch;
public LatchedPort() public LatchedPort()
{ {
Direction = 0x00; Direction = 0x00;
Latch = 0x00; Latch = 0x00;
} }
// data works like this in these types of systems: // data works like this in these types of systems:
// //
// directionA directionB result // directionA directionB result
// 0 0 1 // 0 0 1
// 1 0 latchA // 1 0 latchA
// 0 1 latchB // 0 1 latchB
// 1 1 latchA && latchB // 1 1 latchA && latchB
// //
// however because this uses transistor logic, there are cases where wired-ands // however because this uses transistor logic, there are cases where wired-ands
// cause the pull-up resistors not to be enough to keep the bus bit set to 1 when // cause the pull-up resistors not to be enough to keep the bus bit set to 1 when
// both the direction and latch are 1 (the keyboard and joystick port 2 can do this.) // both the direction and latch are 1 (the keyboard and joystick port 2 can do this.)
// the class does not handle this case as it must be handled differently in every occurrence. // the class does not handle this case as it must be handled differently in every occurrence.
public int ReadInput(int bus) public int ReadInput(int bus)
{ {
return (Latch & Direction) | ((Direction ^ 0xFF) & bus); return (Latch & Direction) | ((Direction ^ 0xFF) & bus);
} }
public int ReadOutput() public int ReadOutput()
{ {
return (Latch & Direction) | (Direction ^ 0xFF); return (Latch & Direction) | (Direction ^ 0xFF);
} }
public void SyncState(Serializer ser) public void SyncState(Serializer ser)
{ {
SaveState.SyncObject(ser, this); SaveState.SyncObject(ser, this);
} }
} }
public sealed class LatchedBooleanPort public sealed class LatchedBooleanPort
{ {
public bool Direction; public bool Direction;
public bool Latch; public bool Latch;
public LatchedBooleanPort() public LatchedBooleanPort()
{ {
Direction = false; Direction = false;
Latch = false; Latch = false;
} }
// data dir bus out // data dir bus out
// 0 0 0 0 // 0 0 0 0
// 0 0 1 1 // 0 0 1 1
// 0 1 0 0 // 0 1 0 0
// 0 1 1 0 // 0 1 1 0
// 1 0 0 0 // 1 0 0 0
// 1 0 1 1 // 1 0 1 1
// 1 1 0 1 // 1 1 0 1
// 1 1 1 1 // 1 1 1 1
public bool ReadInput(bool bus) public bool ReadInput(bool bus)
{ {
return (Direction && Latch) || (!Direction && bus); return (Direction && Latch) || (!Direction && bus);
} }
public bool ReadOutput() public bool ReadOutput()
{ {
return (Latch || !Direction); return (Latch || !Direction);
} }
public void SyncState(Serializer ser) public void SyncState(Serializer ser)
{ {
SaveState.SyncObject(ser, this); SaveState.SyncObject(ser, this);
} }
} }
} }

View File

@ -108,9 +108,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
[SaveState.SaveWithName("HandshakeCb2NextClock")] [SaveState.SaveWithName("HandshakeCb2NextClock")]
private bool _handshakeCb2NextClock; private bool _handshakeCb2NextClock;
[SaveState.SaveWithName("ShiftRegisterCounter")]
private int _shiftCount;
[SaveState.SaveWithName("CA1")] [SaveState.SaveWithName("CA1")]
public bool Ca1; public bool Ca1;
[SaveState.SaveWithName("CA2")] [SaveState.SaveWithName("CA2")]

View File

@ -6,7 +6,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
{ {
public static class D64 public static class D64
{ {
private static readonly int[] densityTable = private static readonly int[] DensityTable =
{ {
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
@ -18,7 +18,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
0, 0, 0, 0, 0 0, 0, 0, 0, 0
}; };
private static readonly int[] gcrDecodeTable = private static readonly int[] GcrDecodeTable =
{ {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //00xxx 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //00xxx
0xFF, 0x08, 0x00, 0x01, 0xFF, 0x0C, 0x04, 0x05, //01xxx 0xFF, 0x08, 0x00, 0x01, 0xFF, 0x0C, 0x04, 0x05, //01xxx
@ -26,7 +26,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
0xFF, 0x09, 0x0A, 0x0B, 0xFF, 0x0D, 0x0E, 0xFF //11xxx 0xFF, 0x09, 0x0A, 0x0B, 0xFF, 0x0D, 0x0E, 0xFF //11xxx
}; };
private static readonly int[] gcrEncodeTable = private static readonly int[] GcrEncodeTable =
{ {
Convert.ToByte("01010", 2), // 0 Convert.ToByte("01010", 2), // 0
Convert.ToByte("01011", 2), // 1 Convert.ToByte("01011", 2), // 1
@ -46,7 +46,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
Convert.ToByte("10101", 2) // F Convert.ToByte("10101", 2) // F
}; };
private static readonly int[] sectorsPerTrack = private static readonly int[] SectorsPerTrack =
{ {
21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
@ -58,7 +58,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
17, 17, 17, 17, 17 17, 17, 17, 17, 17
}; };
private static readonly int[] standardTrackLengthBytes = private static readonly int[] StandardTrackLengthBytes =
{ {
6250, 6666, 7142, 7692 6250, 6666, 7142, 7692
}; };
@ -116,14 +116,14 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
for (var i = 0; i < count; i += 4) for (var i = 0; i < count; i += 4)
{ {
Array.Copy(source, i, data, 0, 4); Array.Copy(source, i, data, 0, 4);
gcr[0] = gcrEncodeTable[data[0] >> 4]; gcr[0] = GcrEncodeTable[data[0] >> 4];
gcr[1] = gcrEncodeTable[data[0] & 0xF]; gcr[1] = GcrEncodeTable[data[0] & 0xF];
gcr[2] = gcrEncodeTable[data[1] >> 4]; gcr[2] = GcrEncodeTable[data[1] >> 4];
gcr[3] = gcrEncodeTable[data[1] & 0xF]; gcr[3] = GcrEncodeTable[data[1] & 0xF];
gcr[4] = gcrEncodeTable[data[2] >> 4]; gcr[4] = GcrEncodeTable[data[2] >> 4];
gcr[5] = gcrEncodeTable[data[2] & 0xF]; gcr[5] = GcrEncodeTable[data[2] & 0xF];
gcr[6] = gcrEncodeTable[data[3] >> 4]; gcr[6] = GcrEncodeTable[data[3] >> 4];
gcr[7] = gcrEncodeTable[data[3] & 0xF]; gcr[7] = GcrEncodeTable[data[3] & 0xF];
// -------- -------- -------- -------- -------- // -------- -------- -------- -------- --------
// 00000111 11222223 33334444 45555566 66677777 // 00000111 11222223 33334444 45555566 66677777
@ -175,7 +175,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
for (var i = 0; i < trackCount; i++) for (var i = 0; i < trackCount; i++)
{ {
var sectors = sectorsPerTrack[i]; var sectors = SectorsPerTrack[i];
var trackLengthBits = 0; var trackLengthBits = 0;
using (var trackMem = new MemoryStream()) using (var trackMem = new MemoryStream())
{ {
@ -187,10 +187,10 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
trackMem.Write(diskData, 0, diskData.Length); trackMem.Write(diskData, 0, diskData.Length);
trackLengthBits += bitsWritten; trackLengthBits += bitsWritten;
} }
var density = densityTable[i]; var density = DensityTable[i];
// we pad the tracks with extra gap bytes to meet MNIB standards // we pad the tracks with extra gap bytes to meet MNIB standards
while (trackMem.Length < standardTrackLengthBytes[density]) while (trackMem.Length < StandardTrackLengthBytes[density])
{ {
trackMem.WriteByte(0x55); trackMem.WriteByte(0x55);
} }
@ -198,7 +198,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
trackDatas.Add(trackMem.ToArray()); trackDatas.Add(trackMem.ToArray());
trackLengths.Add(trackLengthBits); trackLengths.Add(trackLengthBits);
trackNumbers.Add(i * 2); trackNumbers.Add(i * 2);
trackDensities.Add(densityTable[i]); trackDensities.Add(DensityTable[i]);
} }
} }

View File

@ -0,0 +1,307 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
{
public class DiskBuilder
{
public enum FileType
{
Deleted = 0,
Sequential = 1,
Program = 2,
User = 3,
Relative = 4
}
protected class BamEntry
{
public int Data { get; private set; }
public int Sectors { get; private set; }
public BamEntry(int sectors)
{
Data = 0;
for (var i = 0; i < sectors; i++)
{
Data >>= 1;
Data |= 0x800000;
}
Data |= (sectors << 24);
Sectors = sectors;
}
private int GetBit(int sector)
{
if (sector < 0 || sector >= Sectors)
{
return 0;
}
return 0x800000 >> sector;
}
public void Allocate(int sector)
{
var bit = GetBit(sector);
if (bit != 0 && (Data & bit) != 0)
{
Data &= ~bit;
Data -= 0x1000000;
}
}
public void Free(int sector)
{
var bit = GetBit(sector);
if (bit != 0 && (Data & bit) == 0)
{
Data |= bit;
Data += 0x1000000;
}
}
public int SectorsRemaining
{
get { return (Data >> 24) & 0xFF; }
}
public bool this[int sector]
{
get { return (Data & (1 << sector)) != 0; }
set
{
if (value)
Free(sector);
else
Allocate(sector);
}
}
public byte[] GetBytes()
{
return GetBytesEnumerable().ToArray();
}
private IEnumerable<byte> GetBytesEnumerable()
{
yield return unchecked((byte)(Data >> 24));
yield return unchecked((byte)(Data >> 16));
yield return unchecked((byte)(Data >> 8));
yield return unchecked((byte)Data);
}
public IEnumerable<bool> Entries
{
get
{
var d = Data;
for (var i = 0; i < Sectors; i++)
{
d <<= 1;
yield return (d & 0x1000000) != 0;
}
}
}
}
protected class LocatedEntry
{
public Entry Entry { get; set; }
public int DirectoryTrack { get; set; }
public int DirectorySector { get; set; }
public int Track { get; set; }
public int Sector { get; set; }
public int SideTrack { get; set; }
public int SideSector { get; set; }
public int LengthInSectors { get; set; }
}
public class Entry
{
public FileType Type { get; set; }
public bool Locked { get; set; }
public bool Closed { get; set; }
public string Name { get; set; }
public int RecordLength { get; set; }
public byte[] Data { get; set; }
}
private static readonly int[] SectorsPerTrack =
{
21, 21, 21, 21, 21,
21, 21, 21, 21, 21,
21, 21, 21, 21, 21,
21, 21, 19, 19, 19,
19, 19, 19, 19, 18,
18, 18, 18, 18, 18,
17, 17, 17, 17, 17,
17, 17, 17, 17, 17
};
public List<Entry> Entries { get; set; }
public int VersionType { get; set; }
public string Title { get; set; }
public DiskBuilder()
{
Entries = new List<Entry>();
VersionType = 0x41;
}
public Disk Build()
{
const int tracks = 35;
var trackByteOffsets = new int[tracks];
var bam = new BamEntry[tracks];
var diskFull = false;
for (var i = 0; i < tracks; i++)
{
bam[i] = new BamEntry(SectorsPerTrack[i]);
if (i > 0)
{
trackByteOffsets[i] = trackByteOffsets[i - 1] + (SectorsPerTrack[i - 1] * 256);
}
}
var bytes = new byte[trackByteOffsets[tracks - 1] + (SectorsPerTrack[tracks - 1] *256)];
var currentTrack = 16;
var currentSector = 0;
var interleaveStart = 0;
var sectorInterleave = 3;
var directory = new List<LocatedEntry>();
Func<int, int, int> GetOutputOffset = (t, s) => trackByteOffsets[t] + (s*256);
foreach (var entry in Entries)
{
var sourceOffset = 0;
var dataLength = entry.Data == null ? 0 : entry.Data.Length;
var lengthInSectors = dataLength / 254;
var dataRemaining = dataLength;
var directoryEntry = new LocatedEntry
{
Entry = entry,
LengthInSectors = lengthInSectors + 1,
Track = currentTrack,
Sector = currentSector
};
directory.Add(directoryEntry);
while (!diskFull)
{
var outputOffset = GetOutputOffset(currentTrack, currentSector);
if (dataRemaining > 254)
{
Array.Copy(entry.Data, sourceOffset, bytes, outputOffset + 2, 254);
dataRemaining -= 254;
}
else
{
if (dataRemaining > 0)
{
Array.Copy(entry.Data, sourceOffset, bytes, outputOffset + 2, dataRemaining);
bytes[outputOffset + 0] = 0;
bytes[outputOffset + 1] = (byte) dataRemaining;
dataRemaining = 0;
}
}
bam[currentTrack].Allocate(currentSector);
currentSector += sectorInterleave;
if (currentSector >= SectorsPerTrack[currentTrack])
{
interleaveStart++;
if (interleaveStart >= sectorInterleave)
{
interleaveStart = 0;
if (currentTrack >= 17)
{
currentTrack++;
if (currentTrack >= 35)
{
diskFull = true;
break;
}
}
else
{
currentTrack--;
if (currentTrack < 0)
currentTrack = 18;
}
}
currentSector = interleaveStart;
}
if (dataRemaining <= 0)
break;
bytes[outputOffset + 0] = (byte)(currentTrack + 1);
bytes[outputOffset + 1] = (byte) currentSector;
}
if (diskFull)
break;
}
// write Directory
var directoryOffset = -(0x20);
currentTrack = 17;
currentSector = 1;
var directoryOutputOffset = GetOutputOffset(currentTrack, currentSector);
var fileIndex = 0;
bam[currentTrack].Allocate(currentSector);
foreach (var entry in directory)
{
directoryOffset += 0x20;
if (directoryOffset == 0x100)
{
directoryOffset = 0;
currentSector += 3;
bytes[directoryOutputOffset] = (byte) currentTrack;
bytes[directoryOutputOffset + 1] = (byte) currentSector;
directoryOutputOffset = GetOutputOffset(currentTrack, currentSector);
bam[currentTrack].Allocate(currentSector);
}
bytes[directoryOutputOffset + directoryOffset + 0x00] = 0x00;
bytes[directoryOutputOffset + directoryOffset + 0x01] = 0x00;
bytes[directoryOutputOffset + directoryOffset + 0x02] = (byte)((int)entry.Entry.Type | (entry.Entry.Locked ? 0x40 : 0x00) | (entry.Entry.Closed ? 0x80 : 0x00));
bytes[directoryOutputOffset + directoryOffset + 0x03] = (byte)(entry.Track + 1);
bytes[directoryOutputOffset + directoryOffset + 0x04] = (byte)entry.Sector;
for (var i = 0x05; i <= 0x14; i++)
bytes[directoryOutputOffset + directoryOffset + i] = 0xA0;
var fileNameBytes = Encoding.ASCII.GetBytes(entry.Entry.Name ?? string.Format("FILE{0:D3}", fileIndex));
Array.Copy(fileNameBytes, 0, bytes, directoryOutputOffset + directoryOffset + 0x05, Math.Min(fileNameBytes.Length, 0x10));
bytes[directoryOutputOffset + directoryOffset + 0x1E] = (byte)(entry.LengthInSectors & 0xFF);
bytes[directoryOutputOffset + directoryOffset + 0x1F] = (byte)((entry.LengthInSectors >> 8) & 0xFF);
fileIndex++;
}
bytes[directoryOutputOffset + 0x00] = 0x00;
bytes[directoryOutputOffset + 0x01] = 0xFF;
// write BAM
var bamOutputOffset = GetOutputOffset(17, 0);
bytes[bamOutputOffset + 0x00] = 18;
bytes[bamOutputOffset + 0x01] = 1;
bytes[bamOutputOffset + 0x02] = (byte)VersionType;
for (var i = 0; i < 35; i++)
{
Array.Copy(bam[i].GetBytes(), 0, bytes, bamOutputOffset + 4 + (i * 4), 4);
}
for (var i = 0x90; i <= 0xAA; i++)
{
bytes[bamOutputOffset + i] = 0xA0;
}
var titleBytes = Encoding.ASCII.GetBytes(Title ?? "UNTITLED");
Array.Copy(titleBytes, 0, bytes, bamOutputOffset + 0x90, Math.Min(titleBytes.Length, 0x10));
return D64.Read(bytes);
}
}
}

View File

@ -1,49 +1,49 @@
using System; using System;
using BizHawk.Common; using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.User namespace BizHawk.Emulation.Cores.Computers.Commodore64.User
{ {
public abstract class UserPortDevice public abstract class UserPortDevice
{ {
public Func<bool> ReadCounter1; public Func<bool> ReadCounter1;
public Func<bool> ReadCounter2; public Func<bool> ReadCounter2;
public Func<bool> ReadHandshake; public Func<bool> ReadHandshake;
public Func<bool> ReadSerial1; public Func<bool> ReadSerial1;
public Func<bool> ReadSerial2; public Func<bool> ReadSerial2;
public virtual void HardReset() public virtual void HardReset()
{ {
// note: this will not disconnect any attached media // note: this will not disconnect any attached media
} }
public virtual bool ReadAtn() public virtual bool ReadAtn()
{ {
return true; return true;
} }
public virtual int ReadData() public virtual int ReadData()
{ {
return 0xFF; return 0xFF;
} }
public virtual bool ReadFlag2() public virtual bool ReadFlag2()
{ {
return true; return true;
} }
public virtual bool ReadPa2() public virtual bool ReadPa2()
{ {
return true; return true;
} }
public virtual bool ReadReset() public virtual bool ReadReset()
{ {
return true; return true;
} }
public void SyncState(Serializer ser) public void SyncState(Serializer ser)
{ {
SaveState.SyncObject(ser, this); SaveState.SyncObject(ser, this);
} }
} }
} }