diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/FDS/FDS.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/FDS/FDS.cs
index cb889a23bb..a37c230a4b 100644
--- a/BizHawk.Emulation/Consoles/Nintendo/NES/FDS/FDS.cs
+++ b/BizHawk.Emulation/Consoles/Nintendo/NES/FDS/FDS.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using System.IO;
namespace BizHawk.Emulation.Consoles.Nintendo
{
@@ -22,7 +23,18 @@ namespace BizHawk.Emulation.Consoles.Nintendo
///
/// .fds disk image
///
- public byte[] diskimage;
+ byte[] diskimage;
+
+ ///
+ /// should only be called once, before emulation begins
+ ///
+ ///
+ public void SetDiskImage(byte[] diskimage)
+ {
+ this.diskimage = diskimage;
+ diskdiffs = new byte[NumSides][];
+ }
+
RamAdapter diskdrive;
FDSAudio audio;
@@ -50,6 +62,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo
return true;
}
+ // with a bit of change, these methods could work with a better disk format
+
public int NumSides
{
get
@@ -60,7 +74,12 @@ namespace BizHawk.Emulation.Consoles.Nintendo
public void Eject()
{
- diskdrive.Eject();
+ if (currentside != null)
+ {
+ diskdiffs[(int)currentside] = diskdrive.MakeDiff();
+ diskdrive.Eject();
+ currentside = null;
+ }
}
public void InsertSide(int side)
@@ -68,8 +87,79 @@ namespace BizHawk.Emulation.Consoles.Nintendo
byte[] buf = new byte[65500];
Buffer.BlockCopy(diskimage, 16 + side * 65500, buf, 0, 65500);
diskdrive.InsertBrokenImage(buf, false /*true*/);
+ if (diskdiffs[side] != null)
+ diskdrive.ApplyDiff(diskdiffs[side]);
+ currentside = side;
}
+ int? currentside = null;
+
+ byte[][] diskdiffs;
+
+ public byte[] ReadSaveRam()
+ {
+ // update diff for currently loaded disk first!
+ if (currentside != null)
+ diskdiffs[(int)currentside] = diskdrive.MakeDiff();
+ MemoryStream ms = new MemoryStream();
+ BinaryWriter bw = new BinaryWriter(ms);
+ bw.Write(Encoding.ASCII.GetBytes("FDSS"));
+ bw.Write(NumSides);
+ for (int i = 0; i < NumSides; i++)
+ {
+ if (diskdiffs[i] != null)
+ {
+ bw.Write(diskdiffs[i].Length);
+ bw.Write(diskdiffs[i]);
+ }
+ else
+ {
+ bw.Write((int)0);
+ }
+ }
+ bw.Close();
+ return ms.ToArray();
+ }
+
+ public void StoreSaveRam(byte[] data)
+ {
+ // it's strange to modify a disk that's in the process of being read.
+ // but in fact, StoreSaveRam() is only called once right at startup, so this is no big deal
+ //if (currentside != null)
+ // throw new Exception("FDS Saveram: Can't load when a disk is active!");
+ MemoryStream ms = new MemoryStream(data, false);
+ BinaryReader br = new BinaryReader(ms);
+ byte[] cmp = Encoding.ASCII.GetBytes("FDSS");
+ byte[] tmp = br.ReadBytes(cmp.Length);
+ if (!cmp.SequenceEqual(tmp))
+ throw new Exception("FDS Saveram: bad header");
+ int n = br.ReadInt32();
+ if (n != NumSides)
+ throw new Exception("FDS Saveram: wrong number of sides");
+ for (int i = 0; i < NumSides; i++)
+ {
+ int l = br.ReadInt32();
+ if (l > 0)
+ diskdiffs[i] = br.ReadBytes(l);
+ else
+ diskdiffs[i] = null;
+ }
+ if (currentside != null && diskdiffs[(int)currentside] != null)
+ diskdrive.ApplyDiff(diskdiffs[(int)currentside]);
+ }
+
+ public void ClearSaveRam()
+ {
+ if (currentside != null)
+ throw new Exception("FDS Saveram: Can't clear when a disk is active!");
+ for (int i = 0; i < diskdiffs.Length; i++)
+ diskdiffs[i] = null;
+ }
+
+ public override byte[] SaveRam
+ { get { throw new Exception("FDS Saveram: Must access with method api!"); } }
+
+
public MemoryDomain GetDiskPeeker()
{
return new MemoryDomain("FDS SIDE", diskdrive.NumBytes, Endian.Little, diskdrive.PeekData, null);
diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/FDS/RamAdapter.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/FDS/RamAdapter.cs
index bdff080ca3..33048e6d15 100644
--- a/BizHawk.Emulation/Consoles/Nintendo/NES/FDS/RamAdapter.cs
+++ b/BizHawk.Emulation/Consoles/Nintendo/NES/FDS/RamAdapter.cs
@@ -87,6 +87,12 @@ namespace BizHawk.Emulation.Consoles.Nintendo
return tmp;
}
+ ///
+ /// advance a 16 bit CRC register with 1 new input bit. x.25 standard
+ ///
+ ///
+ ///
+ ///
static ushort CCITT(ushort crc, int bit)
{
int bitc = crc & 1;
@@ -96,6 +102,12 @@ namespace BizHawk.Emulation.Consoles.Nintendo
return crc;
}
+ ///
+ /// advance a 16 bit CRC register with 8 new input bits. x.25 standard
+ ///
+ ///
+ ///
+ ///
static ushort CCITT_8(ushort crc, byte b)
{
for (int i = 0; i < 8; i++)
@@ -106,7 +118,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo
return crc;
}
-
+ /// the original contents of this disk when it was loaded. for virtual saveram diff
+ byte[] originaldisk = null;
/// currently loaded disk side (ca 65k bytes)
byte[] disk = null;
/// current disk location in BITS, not bytes
@@ -116,6 +129,9 @@ namespace BizHawk.Emulation.Consoles.Nintendo
/// true if current disk is writeprotected
bool writeprotect = true;
+ ///
+ /// eject the loaded disk
+ ///
public void Eject()
{
disk = null;
@@ -124,8 +140,16 @@ namespace BizHawk.Emulation.Consoles.Nintendo
Console.WriteLine("FDS: Disk ejected");
}
+ ///
+ /// insert a new disk. might have to eject first???
+ ///
+ /// least significant bits appear first on physical disk
+ /// length of disk in bits
+ /// disk is write protected
public void Insert(byte[] side, int bitlength, bool writeprotect)
{
+ if (side.Length * 8 < bitlength)
+ throw new ArgumentException("Disk too small for parameter!");
disk = side;
disksize = bitlength;
diskpos = 0;
@@ -133,13 +157,14 @@ namespace BizHawk.Emulation.Consoles.Nintendo
state = RamAdapterState.INSERTING;
SetCycles();
Console.WriteLine("FDS: Disk Inserted");
+ originaldisk = (byte[])disk.Clone();
}
///
/// insert a side image from an fds disk
///
- ///
- ///
+ /// 65500 bytes from a broken-ass .fds file to be corrected
+ /// disk is write protected
public void InsertBrokenImage(byte[] side, bool writeprotect)
{
byte[] realside = FixFDSSide(side);
@@ -147,6 +172,37 @@ namespace BizHawk.Emulation.Consoles.Nintendo
//File.WriteAllBytes("fdsdebug.bin", realside);
}
+ public void ApplyDiff(byte[] data)
+ {
+ int bitsize = data[0] * 0x10000 + data[1] * 0x100 + data[2];
+ if (bitsize != disksize)
+ throw new ArgumentException("Disk size mismatch!");
+ int pos = 0;
+ while (bitsize > 0)
+ {
+ disk[pos] ^= data[pos + 3];
+ pos++;
+ bitsize -= 8;
+ }
+ }
+
+ public byte[] MakeDiff()
+ {
+ byte[] ret = new byte[(disksize + 7) / 8 + 3];
+ int bitsize = disksize;
+ ret[0] = (byte)(bitsize / 0x10000);
+ ret[1] = (byte)(bitsize / 0x100);
+ ret[2] = (byte)(bitsize);
+ int pos = 0;
+ while (bitsize > 0)
+ {
+ ret[pos + 3] = (byte)(disk[pos] ^ originaldisk[pos]);
+ pos++;
+ bitsize -= 8;
+ }
+ return ret;
+ }
+
///
/// memorydomain debugging
///
@@ -165,6 +221,9 @@ namespace BizHawk.Emulation.Consoles.Nintendo
}
// all timings are in terms of PPU cycles (@5.37mhz)
+ ///
+ /// ppu cycles until next action
+ ///
int cycleswaiting = 0;
enum RamAdapterState
@@ -177,11 +236,17 @@ namespace BizHawk.Emulation.Consoles.Nintendo
SPINUP,
/// head moving back to beginning
RESET,
- /// nothing
+ /// nothing happening
IDLE,
};
+ ///
+ /// physical state of the drive
+ ///
RamAdapterState state = RamAdapterState.IDLE;
+ ///
+ /// set cycleswaiting param after a state change
+ ///
void SetCycles()
{
// these are mostly guesses
@@ -208,8 +273,11 @@ namespace BizHawk.Emulation.Consoles.Nintendo
break;
}
}
-
- // data write reg
+
+ ///
+ /// data write reg
+ ///
+ ///
public void Write4024(byte value)
{
bytetransferflag = false;
@@ -217,16 +285,28 @@ namespace BizHawk.Emulation.Consoles.Nintendo
//Console.WriteLine("!!4024:{0:x2}", value);
}
+ ///
+ /// cached 4025 write; can be modified internally by some things
+ ///
byte cached4025;
+ ///
+ /// can be raised on byte transfer complete
+ ///
public bool irq;
/// true if 4025.1 is set to true
bool transferreset = false;
+ ///
+ /// 16 bit CRC register. in normal operation, will become all 0 on finishing a read (see x.25 spec for more details)
+ ///
ushort crc = 0;
- // control reg
+ ///
+ /// control reg
+ ///
+ ///
public void Write4025(byte value)
{
if ((value & 1) != 0) // start motor
@@ -260,12 +340,15 @@ namespace BizHawk.Emulation.Consoles.Nintendo
irq = false; // ??
cached4025 = value;
- if ((cached4025 & 4) == 0)
- if ((cached4025 & 0x10) != 0)
- Console.WriteLine("FDS: Starting CRC");
+ //if ((cached4025 & 4) == 0)
+ // if ((cached4025 & 0x10) != 0)
+ // Console.WriteLine("FDS: Starting CRC");
}
- // some bits come from outside RamAdapter
+ ///
+ /// general status reg, some bits are from outside the RamAdapter class
+ ///
+ ///
public byte Read4030()
{
byte ret = 0;
@@ -290,12 +373,16 @@ namespace BizHawk.Emulation.Consoles.Nintendo
///
int lastreaddiskpos;
+ ///
+ /// more status stuff
+ ///
+ ///
public byte Read4031()
{
bytetransferflag = false;
irq = false; //??
//Console.WriteLine("{0:x2} @{1}", readreglatch, lastreaddiskpos);
- // it seems very hard to avoid this situation, hence the switch to latched shift regs
+ // note that the shift regs are latched, hence this doesn't happen
//if (readregpos != 0)
//{
// Console.WriteLine("FDS == BIT MISSED ==");
@@ -303,6 +390,10 @@ namespace BizHawk.Emulation.Consoles.Nintendo
return readreglatch;
}
+ ///
+ /// more status stuff
+ ///
+ ///
public byte Read4032()
{
byte ret = 0xff;
@@ -317,7 +408,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
}
///
- /// 5.37mhz
+ /// clock at ~5.37mhz
///
public void Clock()
{
@@ -356,27 +447,25 @@ namespace BizHawk.Emulation.Consoles.Nintendo
state = RamAdapterState.RUNNING;
SetCycles();
//transferreset = false; // this definitely does not happen.
- //numcrc = 0;
Console.WriteLine("FDS: Spin up complete! Disk is running");
break;
case RamAdapterState.IDLE:
SetCycles();
break;
-
}
}
}
+ // read and write shift regs, with bit positions and latched values for reload
byte readreg;
byte writereg;
int readregpos;
int writeregpos;
byte readreglatch;
byte writereglatch;
-
- bool _bytetransferflag;
- bool bytetransferflag { get { return _bytetransferflag; } set { _bytetransferflag = value; } }
+
+ bool bytetransferflag;
bool lookingforendofgap = false;
@@ -395,7 +484,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
{
if (bit == 1) // found!
{
- Console.WriteLine("FDS: End of Gap @{0}", diskpos);
+ //Console.WriteLine("FDS: End of Gap @{0}", diskpos);
lookingforendofgap = false;//cached4025 &= unchecked((byte)~0x40); // stop looking for end of gap
readregpos = 0;
@@ -427,9 +516,9 @@ namespace BizHawk.Emulation.Consoles.Nintendo
if ((cached4025 & 0x10) != 0)
{
- Console.WriteLine("FDS: crc byte {0:x2} @{1}", readreg, diskpos);
+ //Console.WriteLine("FDS: crc byte {0:x2} @{1}", readreg, diskpos);
cached4025 &= unchecked((byte)~0x10); // clear CRC reading. no real effect other than to silence debug??
- Console.WriteLine("FDS: Final CRC {0:x4}", crc);
+ //Console.WriteLine("FDS: Final CRC {0:x4}", crc);
}
}
}
@@ -465,25 +554,25 @@ namespace BizHawk.Emulation.Consoles.Nintendo
bytetransferflag = true;
if ((cached4025 & 0x80) != 0)
irq = true;
- Console.WriteLine("FDS: Write @{0} Reload {1:x2}", diskpos + 1, writereglatch);
+ //Console.WriteLine("FDS: Write @{0} Reload {1:x2}", diskpos + 1, writereglatch);
if ((cached4025 & 0x10) != 0)
{
- Console.WriteLine("FDS: write clear CRC", readreg, diskpos);
+ //Console.WriteLine("FDS: write clear CRC", readreg, diskpos);
if (crc == 0)
{
cached4025 &= unchecked((byte)~0x10); // clear CRC reading
- Console.WriteLine("FDS: write CRC commit finished");
+ //Console.WriteLine("FDS: write CRC commit finished");
// it seems that after a successful CRC, the writereglatch is reset to 0 value. this is needed?
writereglatch = 0;
}
- Console.WriteLine("{0:x4}", crc);
+ //Console.WriteLine("{0:x4}", crc);
writereg = (byte)crc;
- Console.WriteLine("{0:x2}", writereg);
+ //Console.WriteLine("{0:x2}", writereg);
crc >>= 8;
- Console.WriteLine("{0:x4}", crc);
+ //Console.WriteLine("{0:x4}", crc);
// loaded the first CRC byte to write, so stop computing CRC on data
writecomputecrc = false;
}
diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/NES.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/NES.cs
index f73c696832..465acdbc29 100644
--- a/BizHawk.Emulation/Consoles/Nintendo/NES/NES.cs
+++ b/BizHawk.Emulation/Consoles/Nintendo/NES/NES.cs
@@ -327,12 +327,21 @@ namespace BizHawk.Emulation.Consoles.Nintendo
public byte[] ReadSaveRam()
{
+ if (board is FDS)
+ return (board as FDS).ReadSaveRam();
+
if (board == null || board.SaveRam == null)
return null;
return (byte[])board.SaveRam.Clone();
}
public void StoreSaveRam(byte[] data)
{
+ if (board is FDS)
+ {
+ (board as FDS).StoreSaveRam(data);
+ return;
+ }
+
if (board == null || board.SaveRam == null)
return;
Array.Copy(data, board.SaveRam, data.Length);
@@ -340,6 +349,12 @@ namespace BizHawk.Emulation.Consoles.Nintendo
public void ClearSaveRam()
{
+ if (board is FDS)
+ {
+ (board as FDS).ClearSaveRam();
+ return;
+ }
+
if (board == null || board.SaveRam == null)
return;
for (int i = 0; i < board.SaveRam.Length; i++)
@@ -348,7 +363,13 @@ namespace BizHawk.Emulation.Consoles.Nintendo
public bool SaveRamModified
{
- get { if (board == null) return false; if (board.SaveRam == null) return false; return true; }
+ get
+ {
+ if (board == null) return false;
+ if (board is FDS) return true;
+ if (board.SaveRam == null) return false;
+ return true;
+ }
set { }
}
@@ -374,7 +395,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
domains.Add(CIRAMdomain);
domains.Add(OAMdoman);
- if (board.SaveRam != null)
+ if (!(board is FDS) && board.SaveRam != null)
{
var BatteryRam = new MemoryDomain("Battery RAM", board.SaveRam.Length, Endian.Little,
addr => board.SaveRam[addr], (addr, value) => board.SaveRam[addr] = value);
@@ -483,7 +504,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
cart = new CartInfo();
var fdsboard = new FDS();
fdsboard.biosrom = fdsbios;
- fdsboard.diskimage = rom;
+ fdsboard.SetDiskImage(rom);
fdsboard.Create(this);
fdsboard.Configure(origin);