using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Consoles.Nintendo
{
/*
* http://sourceforge.net/p/fceultra/code/2696/tree/fceu/src/fds.cpp - only used for timer info
* http://nesdev.com/FDS%20technical%20reference.txt - implementation is mostly a combination of
* http://wiki.nesdev.com/w/index.php/Family_Computer_Disk_System - these two documents
* http://nesdev.com/diskspec.txt - not useless
*/
[NES.INESBoardImplCancel]
public class FDS : NES.NESBoardBase
{
///
/// fds bios image; should be 8192 bytes
///
public byte[] biosrom;
///
/// .fds disk image
///
public byte[] diskimage;
RamAdapter diskdrive;
FDSAudio audio;
int audioclock;
// as we have [INESBoardImplCancel], this will only be called with an fds disk image
public override bool Configure(NES.EDetectionOrigin origin)
{
if (biosrom == null || biosrom.Length != 8192)
throw new Exception("FDS bios image needed!");
Cart.vram_size = 8;
Cart.wram_size = 32;
Cart.wram_battery = false;
Cart.system = "FDS";
Cart.board_type = "FAMICOM_DISK_SYSTEM";
diskdrive = new RamAdapter();
audio = new FDSAudio();
InsertSide(0);
// set mirroring
return true;
}
public int NumSides
{
get
{
return diskimage[4];
}
}
public void Eject()
{
diskdrive.Eject();
}
public void InsertSide(int side)
{
byte[] buf = new byte[65500];
Buffer.BlockCopy(diskimage, 16 + side * 65500, buf, 0, 65500);
diskdrive.InsertBrokenImage(buf, true);
}
void SetIRQ()
{
IRQSignal = _diskirq || _timerirq;
}
bool _diskirq;
bool _timerirq;
bool diskirq { get { return _diskirq; } set { _diskirq = value; SetIRQ(); } }
bool timerirq { get { return _timerirq; } set { _timerirq = value; SetIRQ(); } }
bool diskenable = false;
bool soundenable = false;
int timerlatch;
int timervalue;
byte timerreg;
byte reg4026;
public override void WriteEXP(int addr, byte value)
{
Console.WriteLine("W{0:x4}:{1:x2} {2:x4}", addr + 0x4000, value, NES.cpu.PC);
if (addr >= 0x0040)
{
audio.WriteReg(addr + 0x4000, value);
return;
}
switch (addr)
{
case 0x0020:
timerlatch &= 0xff00;
timerlatch |= value;
timerirq = false;
break;
case 0x0021:
timerlatch &= 0x00ff;
timerlatch |= value << 8;
timerirq = false;
break;
case 0x0022:
timerreg = (byte)(value & 3);
timervalue = timerlatch * 3;
break;
case 0x0023:
diskenable = (value & 1) != 0;
soundenable = (value & 2) != 0;
break;
case 0x0024:
if (diskenable)
diskdrive.Write4024(value);
break;
case 0x0025:
if (diskenable)
diskdrive.Write4025(value);
SetMirrorType((value & 8) == 0 ? EMirrorType.Vertical : EMirrorType.Horizontal);
break;
case 0x0026:
if (diskenable)
reg4026 = value;
break;
}
diskirq = diskdrive.irq;
}
public override byte ReadEXP(int addr)
{
byte ret = NES.DB;
if (addr >= 0x0040)
return audio.ReadReg(addr + 0x4000, ret);
switch (addr)
{
case 0x0030:
if (diskenable)
{
int tmp = diskdrive.Read4030() & 0xd2;
ret &= 0x2c;
if (timerirq)
ret |= 1;
ret |= (byte)tmp;
timerirq = false;
}
break;
case 0x0031:
if (diskenable)
ret = diskdrive.Read4031();
break;
case 0x0032:
if (diskenable)
{
int tmp = diskdrive.Read4032() & 0x47;
ret &= 0xb8;
ret |= (byte)tmp;
}
break;
case 0x0033:
if (diskenable)
{
ret = reg4026;
ret &= 0x80; // set battery flag
}
break;
}
diskirq = diskdrive.irq;
if (addr != 0x0032)
Console.WriteLine("R{0:x4}:{1:x2} {2:x4}", addr + 0x4000, ret, NES.cpu.PC);
return ret;
}
public override void ClockPPU()
{
if ((timerreg & 2) != 0 && timervalue > 0)
{
timervalue--;
if (timervalue == 0)
{
if ((timerreg & 1) != 0)
{
timervalue = timerlatch * 3;
}
else
{
timerreg &= unchecked((byte)~2);
timervalue = 0;
timerlatch = 0;
}
timerirq = true;
}
}
diskdrive.Clock();
diskirq = diskdrive.irq;
audioclock++;
if (audioclock == 3)
{
audioclock = 0;
audio.Clock();
}
}
public override byte ReadWRAM(int addr)
{
return WRAM[addr & 0x1fff];
}
public override void WriteWRAM(int addr, byte value)
{
WRAM[addr & 0x1fff] = value;
}
public override byte ReadPRG(int addr)
{
if (addr >= 0x6000)
return biosrom[addr & 0x1fff];
else
return WRAM[addr + 0x2000];
}
public override void WritePRG(int addr, byte value)
{
if (addr < 0x6000)
WRAM[addr + 0x2000] = value;
}
public override void ApplyCustomAudio(short[] samples)
{
audio.ApplyCustomAudio(samples);
}
}
}