FDS: SaveRam support. The "saveram" actually stored is a simple collection of (originalside ^ side), more or less. Like any wip beta, don't get too attached to your saves yet. Also misc cleanup.
This commit is contained in:
parent
84a86a72a5
commit
afaa256e37
|
@ -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
|
|||
/// <summary>
|
||||
/// .fds disk image
|
||||
/// </summary>
|
||||
public byte[] diskimage;
|
||||
byte[] diskimage;
|
||||
|
||||
/// <summary>
|
||||
/// should only be called once, before emulation begins
|
||||
/// </summary>
|
||||
/// <param name="diskimage"></param>
|
||||
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);
|
||||
|
|
|
@ -87,6 +87,12 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
return tmp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// advance a 16 bit CRC register with 1 new input bit. x.25 standard
|
||||
/// </summary>
|
||||
/// <param name="crc"></param>
|
||||
/// <param name="bit"></param>
|
||||
/// <returns></returns>
|
||||
static ushort CCITT(ushort crc, int bit)
|
||||
{
|
||||
int bitc = crc & 1;
|
||||
|
@ -96,6 +102,12 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
return crc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// advance a 16 bit CRC register with 8 new input bits. x.25 standard
|
||||
/// </summary>
|
||||
/// <param name="crc"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>the original contents of this disk when it was loaded. for virtual saveram diff</summary>
|
||||
byte[] originaldisk = null;
|
||||
/// <summary>currently loaded disk side (ca 65k bytes)</summary>
|
||||
byte[] disk = null;
|
||||
/// <summary>current disk location in BITS, not bytes</summary>
|
||||
|
@ -116,6 +129,9 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
/// <summary>true if current disk is writeprotected</summary>
|
||||
bool writeprotect = true;
|
||||
|
||||
/// <summary>
|
||||
/// eject the loaded disk
|
||||
/// </summary>
|
||||
public void Eject()
|
||||
{
|
||||
disk = null;
|
||||
|
@ -124,8 +140,16 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
Console.WriteLine("FDS: Disk ejected");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// insert a new disk. might have to eject first???
|
||||
/// </summary>
|
||||
/// <param name="side">least significant bits appear first on physical disk</param>
|
||||
/// <param name="bitlength">length of disk in bits</param>
|
||||
/// <param name="writeprotect">disk is write protected</param>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// insert a side image from an fds disk
|
||||
/// </summary>
|
||||
/// <param name="side"></param>
|
||||
/// <param name="writeprotect"></param>
|
||||
/// <param name="side">65500 bytes from a broken-ass .fds file to be corrected</param>
|
||||
/// <param name="writeprotect">disk is write protected</param>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// memorydomain debugging
|
||||
/// </summary>
|
||||
|
@ -165,6 +221,9 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
}
|
||||
|
||||
// all timings are in terms of PPU cycles (@5.37mhz)
|
||||
/// <summary>
|
||||
/// ppu cycles until next action
|
||||
/// </summary>
|
||||
int cycleswaiting = 0;
|
||||
|
||||
enum RamAdapterState
|
||||
|
@ -177,11 +236,17 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
SPINUP,
|
||||
/// <summary>head moving back to beginning</summary>
|
||||
RESET,
|
||||
/// <summary>nothing</summary>
|
||||
/// <summary>nothing happening</summary>
|
||||
IDLE,
|
||||
};
|
||||
/// <summary>
|
||||
/// physical state of the drive
|
||||
/// </summary>
|
||||
RamAdapterState state = RamAdapterState.IDLE;
|
||||
|
||||
/// <summary>
|
||||
/// set cycleswaiting param after a state change
|
||||
/// </summary>
|
||||
void SetCycles()
|
||||
{
|
||||
// these are mostly guesses
|
||||
|
@ -208,8 +273,11 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// data write reg
|
||||
|
||||
/// <summary>
|
||||
/// data write reg
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
public void Write4024(byte value)
|
||||
{
|
||||
bytetransferflag = false;
|
||||
|
@ -217,16 +285,28 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
//Console.WriteLine("!!4024:{0:x2}", value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// cached 4025 write; can be modified internally by some things
|
||||
/// </summary>
|
||||
byte cached4025;
|
||||
|
||||
/// <summary>
|
||||
/// can be raised on byte transfer complete
|
||||
/// </summary>
|
||||
public bool irq;
|
||||
|
||||
/// <summary>true if 4025.1 is set to true</summary>
|
||||
bool transferreset = false;
|
||||
|
||||
/// <summary>
|
||||
/// 16 bit CRC register. in normal operation, will become all 0 on finishing a read (see x.25 spec for more details)
|
||||
/// </summary>
|
||||
ushort crc = 0;
|
||||
|
||||
// control reg
|
||||
/// <summary>
|
||||
/// control reg
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
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
|
||||
/// <summary>
|
||||
/// general status reg, some bits are from outside the RamAdapter class
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public byte Read4030()
|
||||
{
|
||||
byte ret = 0;
|
||||
|
@ -290,12 +373,16 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
/// </summary>
|
||||
int lastreaddiskpos;
|
||||
|
||||
/// <summary>
|
||||
/// more status stuff
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// more status stuff
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public byte Read4032()
|
||||
{
|
||||
byte ret = 0xff;
|
||||
|
@ -317,7 +408,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// 5.37mhz
|
||||
/// clock at ~5.37mhz
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue