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 Newtonsoft.Json;
using System;
using System.ComponentModel;
using System.Drawing;
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
public partial class C64 : ISettable<C64.C64Settings, C64.C64SyncSettings>
{
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
{
[DisplayName("Border type")]
[Description("Select how to show the border area")]
[DefaultValue(BorderType.SmallProportional)]
public BorderType BorderType { get; set; }
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; }
[DisplayName("SID type")]
[Description("Set the type of sound chip to use")]
[DefaultValue(SidType.OldR2)]
public SidType SidType { get; set; }
[DisplayName("Tape drive type")]
[Description("Set the type of tape drive attached")]
[DefaultValue(TapeDriveType.None)]
public TapeDriveType TapeDriveType { get; set; }
[DisplayName("Disk drive type")]
[Description("Set the type of disk drive attached")]
[DefaultValue(DiskDriveType.None)]
public DiskDriveType DiskDriveType { get; set; }
public C64SyncSettings Clone()
{
return (C64SyncSettings)MemberwiseClone();
}
public C64SyncSettings()
{
BizHawk.Common.SettingsUtil.SetDefaultValues(this);
}
}
public enum VicType
{
Pal, Ntsc, NtscOld, Drean
}
public enum CiaType
{
Pal, Ntsc, PalRevA, NtscRevA
}
public enum BorderType
{
SmallProportional, SmallFixed, Normal, Full
}
public enum SidType
{
OldR2, OldR3, OldR4AR, NewR5
}
public enum TapeDriveType
{
None, Commodore1530
}
public enum DiskDriveType
{
None, Commodore1541, Commodore1541II, Commodore1571
}
}
using BizHawk.Emulation.Common;
using Newtonsoft.Json;
using System;
using System.ComponentModel;
using System.Drawing;
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
public partial class C64 : ISettable<C64.C64Settings, C64.C64SyncSettings>
{
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
{
[DisplayName("Border type")]
[Description("Select how to show the border area")]
[DefaultValue(BorderType.SmallProportional)]
public BorderType BorderType { get; set; }
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; }
[DisplayName("SID type")]
[Description("Set the type of sound chip to use")]
[DefaultValue(SidType.OldR2)]
public SidType SidType { get; set; }
[DisplayName("Tape drive type")]
[Description("Set the type of tape drive attached")]
[DefaultValue(TapeDriveType.None)]
public TapeDriveType TapeDriveType { get; set; }
[DisplayName("Disk drive type")]
[Description("Set the type of disk drive attached")]
[DefaultValue(DiskDriveType.None)]
public DiskDriveType DiskDriveType { get; set; }
public C64SyncSettings Clone()
{
return (C64SyncSettings)MemberwiseClone();
}
public C64SyncSettings()
{
BizHawk.Common.SettingsUtil.SetDefaultValues(this);
}
}
public enum VicType
{
Pal, Ntsc, NtscOld, Drean
}
public enum CiaType
{
Pal, Ntsc, PalRevA, NtscRevA
}
public enum BorderType
{
SmallProportional, SmallFixed, Normal, Full
}
public enum SidType
{
OldR2, OldR3, OldR4AR, NewR5
}
public enum TapeDriveType
{
None, Commodore1530
}
public enum DiskDriveType
{
None, Commodore1541, Commodore1541II
}
}
}

View File

@ -156,17 +156,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
_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 != 0 && _board.Cpu.Pc == ((_board.Ram.Peek(0x0303) << 8) | _board.Ram.Peek(0x0302)))
{
Prg.Load(_board.Pla, _prgFile);
_loadPrg = false;
}
}
if (_frameCycles != _cyclesPerFrame)
{
return;
@ -188,8 +177,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
}
private Motherboard _board;
private bool _loadPrg;
[SaveState.DoNotSave] private byte[] _prgFile;
private byte[] GetFirmware(int length, params string[] names)
{
@ -212,10 +199,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
if (diskDriveType == DiskDriveType.None)
diskDriveType = DiskDriveType.Commodore1541;
break;
case C64Format.D71:
if (diskDriveType == DiskDriveType.None)
diskDriveType = DiskDriveType.Commodore1571;
break;
case C64Format.T64:
case C64Format.TAP:
if (tapeDriveType == TapeDriveType.None)
@ -223,6 +206,19 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
tapeDriveType = TapeDriveType.Commodore1530;
}
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);
}
break;
default:
if (rom.Length > 2)
case C64Format.Unknown:
var prgDisk = new DiskBuilder
{
_loadPrg = true;
_prgFile = rom;
Entries = new List<DiskBuilder.Entry>
{
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;
}
@ -294,9 +304,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
case DiskDriveType.Commodore1541II:
_board.DiskDrive.DriveRom.Flash(GetFirmware(0x4000, "Drive1541II"));
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;
}
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:
WriteRegister(addr, val);
break;

View File

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

View File

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

View File

@ -1,85 +1,85 @@
using System;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
public sealed class LatchedPort
{
public int Direction;
public int Latch;
public LatchedPort()
{
Direction = 0x00;
Latch = 0x00;
}
// data works like this in these types of systems:
//
// directionA directionB result
// 0 0 1
// 1 0 latchA
// 0 1 latchB
// 1 1 latchA && latchB
//
// 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
// 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.
public int ReadInput(int bus)
{
return (Latch & Direction) | ((Direction ^ 0xFF) & bus);
}
public int ReadOutput()
{
return (Latch & Direction) | (Direction ^ 0xFF);
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
public sealed class LatchedBooleanPort
{
public bool Direction;
public bool Latch;
public LatchedBooleanPort()
{
Direction = false;
Latch = false;
}
// data dir bus out
// 0 0 0 0
// 0 0 1 1
// 0 1 0 0
// 0 1 1 0
// 1 0 0 0
// 1 0 1 1
// 1 1 0 1
// 1 1 1 1
public bool ReadInput(bool bus)
{
return (Direction && Latch) || (!Direction && bus);
}
public bool ReadOutput()
{
return (Latch || !Direction);
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
}
using System;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
public sealed class LatchedPort
{
public int Direction;
public int Latch;
public LatchedPort()
{
Direction = 0x00;
Latch = 0x00;
}
// data works like this in these types of systems:
//
// directionA directionB result
// 0 0 1
// 1 0 latchA
// 0 1 latchB
// 1 1 latchA && latchB
//
// 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
// 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.
public int ReadInput(int bus)
{
return (Latch & Direction) | ((Direction ^ 0xFF) & bus);
}
public int ReadOutput()
{
return (Latch & Direction) | (Direction ^ 0xFF);
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
public sealed class LatchedBooleanPort
{
public bool Direction;
public bool Latch;
public LatchedBooleanPort()
{
Direction = false;
Latch = false;
}
// data dir bus out
// 0 0 0 0
// 0 0 1 1
// 0 1 0 0
// 0 1 1 0
// 1 0 0 0
// 1 0 1 1
// 1 1 0 1
// 1 1 1 1
public bool ReadInput(bool bus)
{
return (Direction && Latch) || (!Direction && bus);
}
public bool ReadOutput()
{
return (Latch || !Direction);
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
}

View File

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

View File

@ -6,7 +6,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
{
public static class D64
{
private static readonly int[] densityTable =
private static readonly int[] DensityTable =
{
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
};
private static readonly int[] gcrDecodeTable =
private static readonly int[] GcrDecodeTable =
{
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //00xxx
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
};
private static readonly int[] gcrEncodeTable =
private static readonly int[] GcrEncodeTable =
{
Convert.ToByte("01010", 2), // 0
Convert.ToByte("01011", 2), // 1
@ -46,7 +46,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
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,
@ -58,7 +58,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
17, 17, 17, 17, 17
};
private static readonly int[] standardTrackLengthBytes =
private static readonly int[] StandardTrackLengthBytes =
{
6250, 6666, 7142, 7692
};
@ -116,14 +116,14 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
for (var i = 0; i < count; i += 4)
{
Array.Copy(source, i, data, 0, 4);
gcr[0] = gcrEncodeTable[data[0] >> 4];
gcr[1] = gcrEncodeTable[data[0] & 0xF];
gcr[2] = gcrEncodeTable[data[1] >> 4];
gcr[3] = gcrEncodeTable[data[1] & 0xF];
gcr[4] = gcrEncodeTable[data[2] >> 4];
gcr[5] = gcrEncodeTable[data[2] & 0xF];
gcr[6] = gcrEncodeTable[data[3] >> 4];
gcr[7] = gcrEncodeTable[data[3] & 0xF];
gcr[0] = GcrEncodeTable[data[0] >> 4];
gcr[1] = GcrEncodeTable[data[0] & 0xF];
gcr[2] = GcrEncodeTable[data[1] >> 4];
gcr[3] = GcrEncodeTable[data[1] & 0xF];
gcr[4] = GcrEncodeTable[data[2] >> 4];
gcr[5] = GcrEncodeTable[data[2] & 0xF];
gcr[6] = GcrEncodeTable[data[3] >> 4];
gcr[7] = GcrEncodeTable[data[3] & 0xF];
// -------- -------- -------- -------- --------
// 00000111 11222223 33334444 45555566 66677777
@ -175,7 +175,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
for (var i = 0; i < trackCount; i++)
{
var sectors = sectorsPerTrack[i];
var sectors = SectorsPerTrack[i];
var trackLengthBits = 0;
using (var trackMem = new MemoryStream())
{
@ -187,10 +187,10 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
trackMem.Write(diskData, 0, diskData.Length);
trackLengthBits += bitsWritten;
}
var density = densityTable[i];
var density = DensityTable[i];
// 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);
}
@ -198,7 +198,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
trackDatas.Add(trackMem.ToArray());
trackLengths.Add(trackLengthBits);
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 BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.User
{
public abstract class UserPortDevice
{
public Func<bool> ReadCounter1;
public Func<bool> ReadCounter2;
public Func<bool> ReadHandshake;
public Func<bool> ReadSerial1;
public Func<bool> ReadSerial2;
public virtual void HardReset()
{
// note: this will not disconnect any attached media
}
public virtual bool ReadAtn()
{
return true;
}
public virtual int ReadData()
{
return 0xFF;
}
public virtual bool ReadFlag2()
{
return true;
}
public virtual bool ReadPa2()
{
return true;
}
public virtual bool ReadReset()
{
return true;
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
}
using System;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.User
{
public abstract class UserPortDevice
{
public Func<bool> ReadCounter1;
public Func<bool> ReadCounter2;
public Func<bool> ReadHandshake;
public Func<bool> ReadSerial1;
public Func<bool> ReadSerial2;
public virtual void HardReset()
{
// note: this will not disconnect any attached media
}
public virtual bool ReadAtn()
{
return true;
}
public virtual int ReadData()
{
return 0xFF;
}
public virtual bool ReadFlag2()
{
return true;
}
public virtual bool ReadPa2()
{
return true;
}
public virtual bool ReadReset()
{
return true;
}
public void SyncState(Serializer ser)
{
SaveState.SyncObject(ser, this);
}
}
}