[ChannelF] Cleanup + optimize some code

This commit is contained in:
CasualPokePlayer 2024-09-06 13:42:58 -07:00
parent 314f32b95f
commit 84f4d6ae28
26 changed files with 418 additions and 578 deletions

View File

@ -8,7 +8,7 @@ namespace BizHawk.Emulation.Cores.Components.FairchildF8
/// <summary>
/// Disassembler
/// </summary>
public sealed partial class F3850 : IDisassemblable
public sealed partial class F3850<TLink> : IDisassemblable
{
private static string Result(string format, Func<ushort, byte> read, ref ushort addr)
{

View File

@ -2,7 +2,7 @@
namespace BizHawk.Emulation.Cores.Components.FairchildF8
{
public sealed partial class F3850
public sealed partial class F3850<TLink>
{
public const int MaxInstructionLength = 48;

View File

@ -5,21 +5,21 @@ namespace BizHawk.Emulation.Cores.Components.FairchildF8
/// <summary>
/// ALU Operations
/// </summary>
public sealed partial class F3850
public sealed partial class F3850<TLink>
{
public void Read_Func(byte dest, byte src_l, byte src_h)
{
Regs[dest] = ReadMemory((ushort)(Regs[src_l] | (Regs[src_h]) << 8));
Regs[dest] = _link.ReadMemory((ushort)(Regs[src_l] | (Regs[src_h]) << 8));
}
public void Write_Func(byte dest_l, byte dest_h, byte src)
{
WriteMemory((ushort)(Regs[dest_l] | (Regs[dest_h] << 8)), Regs[src]);
_link.WriteMemory((ushort)(Regs[dest_l] | (Regs[dest_h] << 8)), Regs[src]);
}
public void IN_Func(byte dest, byte src)
{
Regs[dest] = ReadHardware(Regs[src]);
Regs[dest] = _link.ReadHardware(Regs[src]);
}
/// <summary>
@ -48,7 +48,7 @@ namespace BizHawk.Emulation.Cores.Components.FairchildF8
{
// data is complemented between accumulator and I/O pins (because PINs are active-low)
// however for ease here we will make them active-high
WriteHardware(Regs[dest], Regs[src]);
_link.WriteHardware(Regs[dest], Regs[src]);
}
public void ClearFlags_Func()

View File

@ -7,7 +7,7 @@ namespace BizHawk.Emulation.Cores.Components.FairchildF8
/// <summary>
/// Internal Registers
/// </summary>
public sealed partial class F3850
public sealed partial class F3850<TLink>
{
/// <summary>
/// Registers (counters and scratchpad)

View File

@ -3,7 +3,7 @@
/// <summary>
/// Vectors of Instruction Operations
/// </summary>
public sealed partial class F3850
public sealed partial class F3850<TLink>
{
/// <summary>
/// LR - LOAD REGISTER

View File

@ -29,7 +29,7 @@ namespace BizHawk.Emulation.Cores.Components.FairchildF8
///
/// Note: Programmable timer and interrupt logic from the F3851 is not currently emulated
/// </summary>
public sealed partial class F3850
public sealed partial class F3850<TLink> where TLink : IF3850Link
{
// operations that can take place in an instruction
public const byte ROMC_01 = 1;
@ -100,8 +100,11 @@ namespace BizHawk.Emulation.Cores.Components.FairchildF8
public const byte OP_DS = 157;
public const byte OP_LIS = 158;
public F3850()
private readonly TLink _link;
public F3850(TLink link)
{
_link = link;
Reset();
}
@ -133,36 +136,6 @@ namespace BizHawk.Emulation.Cores.Components.FairchildF8
public IMemoryCallbackSystem MemoryCallbacks { get; set; }
// Memory Access
public Func<ushort, byte> ReadMemory;
public Action<ushort, byte> WriteMemory;
public Func<ushort, byte> PeekMemory;
public Func<ushort, byte> DummyReadMemory;
// Hardware I/O Port Access
public Func<ushort, byte> ReadHardware;
public Action<ushort, byte> WriteHardware;
public Action<ushort> OnExecFetch;
public void SetCallbacks
(
Func<ushort, byte> ReadMemory,
Func<ushort, byte> DummyReadMemory,
Func<ushort, byte> PeekMemory,
Action<ushort, byte> WriteMemory,
Func<ushort, byte> ReadHardware,
Action<ushort, byte> WriteHardware
)
{
this.ReadMemory = ReadMemory;
this.DummyReadMemory = DummyReadMemory;
this.PeekMemory = PeekMemory;
this.WriteMemory = WriteMemory;
this.ReadHardware = ReadHardware;
this.WriteHardware = WriteHardware;
}
/// <summary>
/// Runs a single CPU clock cycle
/// </summary>
@ -180,7 +153,7 @@ namespace BizHawk.Emulation.Cores.Components.FairchildF8
{
// always the last tick within an opcode instruction cycle
case END:
OnExecFetch?.Invoke(RegPC0);
_link.OnExecFetch(RegPC0);
TraceCallback?.Invoke(State());
opcode = Regs[DB];
instr_pntr = 0;
@ -785,12 +758,12 @@ namespace BizHawk.Emulation.Cores.Components.FairchildF8
{
int bytes_read = 0;
ushort pc = (ushort)(RegPC0 - 1);
string disasm = disassemble ? Disassemble(pc, ReadMemory, out bytes_read) : "---";
string disasm = disassemble ? Disassemble(pc, _link.ReadMemory, out bytes_read) : "---";
string byte_code = null;
for (ushort i = 0; i < bytes_read; i++)
{
byte_code += ReadMemory((ushort)(pc + i)).ToString("X2");
byte_code += _link.ReadMemory((ushort)(pc + i)).ToString("X2");
if (i < (bytes_read - 1))
{
byte_code += " ";
@ -868,7 +841,7 @@ namespace BizHawk.Emulation.Cores.Components.FairchildF8
public void SyncState(Serializer ser)
{
ser.BeginSection(nameof(F3850));
ser.BeginSection("F3850");
ser.Sync(nameof(Regs), ref Regs, false);
ser.Sync(nameof(cur_instr), ref cur_instr, false);
ser.Sync(nameof(instr_pntr), ref instr_pntr);

View File

@ -0,0 +1,19 @@
namespace BizHawk.Emulation.Cores.Components.FairchildF8
{
// Interface that has all the methods required by the F3850 to talk to
// the emulator core.
// Should only be used as a generic type argument for the F3850, and
// implementations should be structs where possible. This combination allows
// the JITer to generate much faster code than calling a Func<> or Action<>.
public interface IF3850Link
{
byte ReadMemory(ushort address);
void WriteMemory(ushort address, byte value);
byte ReadHardware(ushort address);
void WriteHardware(ushort address, byte value);
// This only calls when the first byte of an instruction is fetched.
void OnExecFetch(ushort address);
}
}

View File

@ -2,36 +2,29 @@
{
/// <summary>
/// Hangman ChannelF Cartridge
/// 2KB ROM / NO RAM
/// Utilises 2102 SRAM over IO
/// </summary>
public class mapper_HANG : VesCartBase
public class MapperHANG : VesCartBase
{
public override string BoardType => "HANG";
public mapper_HANG(byte[] rom)
public MapperHANG(byte[] rom)
{
ROM = new byte[0x10000 - 0x800];
for (int i = 0; i < rom.Length; i++)
{
ROM[i] = rom[i];
if (i > 3000)
{
var test = rom[i];
}
}
RAM = new byte[0x400];
_rom = new byte[0x10000 - 0x800];
Array.Copy(rom, _rom, rom.Length);
_rom.AsSpan(rom.Length).Fill(0xFF);
_ram = new byte[0x400];
}
public override byte ReadBus(ushort addr)
{
var off = addr - 0x800;
return ROM[off];
return _rom[off];
}
public override void WriteBus(ushort addr, byte value)
{
// no writeable memory
// no directly writeable memory
}
public override byte ReadPort(ushort addr)
@ -45,6 +38,5 @@
var index = addr - 0x20;
SRAM2102_Write(index, data);
}
}
}

View File

@ -1,27 +1,25 @@
namespace BizHawk.Emulation.Cores.Consoles.ChannelF
{
/// <summary>
/// ChannelF Cartridge that utilises 2102 SRAM over IO
/// Maze ChannelF Cartridge
/// Utilises 2102 SRAM over IO
/// </summary>
public class mapper_MAZE : VesCartBase
public class MapperMAZE : VesCartBase
{
public override string BoardType => "MAZE";
public mapper_MAZE(byte[] rom)
public MapperMAZE(byte[] rom)
{
ROM = new byte[0x10000 - 0x800];
for (int i = 0; i < rom.Length; i++)
{
ROM[i] = rom[i];
}
RAM = new byte[0x400];
_rom = new byte[0x10000 - 0x800];
Array.Copy(rom, _rom, rom.Length);
_rom.AsSpan(rom.Length).Fill(0xFF);
_ram = new byte[0x400];
}
public override byte ReadBus(ushort addr)
{
var off = addr - 0x800;
return ROM[off];
return _rom[off];
}
public override void WriteBus(ushort addr, byte value)

View File

@ -3,19 +3,15 @@
/// <summary>
/// Sean Riddle's modified SCHACH cart mapper (multi-cart) (WIP)
/// </summary>
public class mapper_RIDDLE : VesCartBase
public class MapperRIDDLE : VesCartBase
{
public override string BoardType => "RIDDLE";
public mapper_RIDDLE(byte[] rom)
public MapperRIDDLE(byte[] rom)
{
ROM = new byte[rom.Length];
for (int i = 0; i < rom.Length; i++)
{
ROM[i] = rom[i];
}
RAM = new byte[0x800];
_rom = new byte[rom.Length];
Array.Copy(rom, _rom, rom.Length);
_ram = new byte[0x800];
}
public override byte ReadBus(ushort addr)
@ -23,15 +19,15 @@
var result = 0xFF;
var off = addr - 0x800;
if (addr >= 0x2800 && addr < 0x3000)
if (addr is >= 0x2800 and < 0x3000)
{
// 2KB RAM
result = RAM[addr - 0x2800];
result = _ram[addr - 0x2800];
}
else
{
if (off < ROM.Length)
result = ROM[off + (MultiBank * 0x2000) + (MultiHalfBank * 0x1000)];
if (off < _rom.Length)
result = _rom[off + (MultiBank * 0x2000) + (MultiHalfBank * 0x1000)];
}
return (byte)result;
@ -40,9 +36,9 @@
public override void WriteBus(ushort addr, byte value)
{
// 2KB writeable memory at 0x2800;
if (addr >= 0x2800 && addr < 0x3000)
if (addr is >= 0x2800 and < 0x3000)
{
RAM[addr - 0x2800] = value;
_ram[addr - 0x2800] = value;
}
else if (addr == 0x3000)
{
@ -50,16 +46,10 @@
MultiBank = value & 0x1F;
MultiHalfBank = (value & 0x20) >> 5;
}
else
{
}
}
public override byte ReadPort(ushort addr)
{
return 0xFF;
}
=> 0xFF;
public override void WritePort(ushort addr, byte data)
{

View File

@ -5,48 +5,42 @@
/// Any size ROM / 2KB RAM mapped at 0x2800 - 0x2FFF
/// Info here: http://www.seanriddle.com/chanfmulti.html
/// </summary>
public class mapper_SCHACH : VesCartBase
public class MapperSCHACH : VesCartBase
{
public override string BoardType => "SCHACH";
public override bool HasActivityLED => true;
public override string ActivityLEDDescription => "Chess Brain Thinking Activity";
public mapper_SCHACH(byte[] rom)
public MapperSCHACH(byte[] rom)
{
ROM = new byte[0x10000 - 0x800];
for (int i = 0; i < rom.Length; i++)
{
ROM[i] = rom[i];
}
RAM = new byte[0x800];
_rom = new byte[0x10000 - 0x800];
Array.Copy(rom, _rom, rom.Length);
_rom.AsSpan(rom.Length).Fill(0xFF);
_ram = new byte[0x800];
}
public override byte ReadBus(ushort addr)
{
var result = 0xFF;
var off = addr - 0x800;
if (addr >= 0x2800 && addr < 0x3000)
byte result;
if (addr is >= 0x2800 and < 0x3000)
{
// 2KB RAM
result = RAM[addr - 0x2800];
result = _ram[addr - 0x2800];
}
else
{
if (off < ROM.Length)
result = ROM[off];
result = _rom[addr - 0x800];
}
return (byte)result;
return result;
}
public override void WriteBus(ushort addr, byte value)
{
// 2KB writeable memory at 0x2800;
if (addr >= 0x2800 && addr < 0x3000)
if (addr is >= 0x2800 and < 0x3000)
{
RAM[addr - 0x2800] = value;
_ram[addr - 0x2800] = value;
}
else if (addr == 0x3800)
{
@ -59,9 +53,7 @@
}
public override byte ReadPort(ushort addr)
{
return 0xFF;
}
=> 0xFF;
public override void WritePort(ushort addr, byte data)
{

View File

@ -4,28 +4,22 @@
/// Standard ChannelF Cartridge
/// 2KB ROM / NO RAM
/// </summary>
public class mapper_STD : VesCartBase
public class MapperSTD : VesCartBase
{
public override string BoardType => "STD";
public mapper_STD(byte[] rom)
public MapperSTD(byte[] rom)
{
ROM = new byte[0x10000 - 0x800];
for (int i = 0; i < rom.Length; i++)
{
ROM[i] = rom[i];
}
RAM = new byte[0];
_rom = new byte[0x10000 - 0x800];
Array.Copy(rom, _rom, rom.Length);
_rom.AsSpan(rom.Length).Fill(0xFF);
_ram = [ ];
}
public override byte ReadBus(ushort addr)
{
var off = addr - 0x800;
if (off < ROM.Length)
return ROM[off];
else
return 0xFF;
return _rom[off];
}
public override void WriteBus(ushort addr, byte value)
@ -34,9 +28,7 @@
}
public override byte ReadPort(ushort addr)
{
return 0xFF;
}
=> 0xFF;
public override void WritePort(ushort addr, byte data)
{

View File

@ -19,24 +19,12 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
}
}
public virtual byte[] ROM
{
get { return _rom; }
protected set { _rom = value; }
}
protected byte[] _rom;
public virtual byte[] RAM
{
get { return _ram; }
protected set { _ram = value; }
}
protected byte[] _ram;
public virtual bool HasActivityLED { get; set; }
public virtual string ActivityLEDDescription { get; set; }
public bool ActivityLED;
public int MultiBank;
public int MultiHalfBank;
@ -58,38 +46,36 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
public static VesCartBase Configure(GameInfo gi, byte[] rom)
{
// get board type
string boardStr = gi.OptionPresent("board") ? gi.GetStringValue("board") : "STD";
var boardStr = gi.OptionPresent("board") ? gi.GetStringValue("board") : "STD";
switch (boardStr)
{
// The supplied ROM is actually a BIOS
case "BIOS":
// we can just pass the rom into channel f and because it does not detect a 0x55 at rom[0] it will just jump straight to onboard games
// (hockey and tennis)
return new mapper_STD(rom);
return new MapperSTD(rom);
// standard cart layout
case "STD":
// any number of F3851 Program Storage Units (1KB ROM each) or F3856 Program Storage Unit (2KB ROM)
// no on-pcb RAM and no extra IO
return new mapper_STD(rom);
return new MapperSTD(rom);
case "MAZE":
return new mapper_MAZE(rom);
return new MapperMAZE(rom);
case "RIDDLE":
// Sean Riddle's modified SCHACH multi-cart
return new mapper_RIDDLE(rom);
return new MapperRIDDLE(rom);
case "SCHACH":
default:
// F3853 Memory Interface Chip, 6KB of ROM and 2KB of RAM
// - default to this
return new mapper_SCHACH(rom);
return new MapperSCHACH(rom);
case "HANG":
return new mapper_HANG(rom);
return new MapperHANG(rom);
}
}
@ -106,7 +92,7 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
if (m_read_write == 0)
{
m_addr = m_addr_latch;
m_data0 = RAM[m_addr] & 1;
m_data0 = _ram[m_addr] & 1;
return (byte)((m_latch[0] & 0x7f) | (m_data0 << 7));
}
@ -141,7 +127,7 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
if (m_read_write == 1)
{
RAM[m_addr] = (byte)m_data0;
_ram[m_addr] = (byte)m_data0;
}
}
else
@ -187,7 +173,7 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
public virtual void SyncState(Serializer ser)
{
ser.BeginSection("Cart");
ser.Sync(nameof(RAM), ref _ram, false);
ser.Sync(nameof(_ram), ref _ram, false);
ser.Sync(nameof(m_latch), ref m_latch, false);
ser.Sync(nameof(m_addr_latch), ref m_addr_latch);
ser.Sync(nameof(m_addr), ref m_addr);

View File

@ -1,25 +1,21 @@
using System.Collections.Generic;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Consoles.ChannelF
{
public partial class ChannelF
{
public ControllerDefinition ChannelFControllerDefinition
{
get
private static readonly Lazy<ControllerDefinition> _channelFControllerDefinition = new(() =>
{
ControllerDefinition definition = new("ChannelF Controller");
string pre = "P1 ";
// sticks
var stickR = new List<string>
{
const string P1_PREFIX = "P1 ";
string[] stickR =
[
// P1 (right) stick
pre + "Forward", pre + "Back", pre + "Left", pre + "Right", pre + "CCW", pre + "CW", pre + "Pull", pre + "Push"
};
P1_PREFIX + "Forward", P1_PREFIX + "Back", P1_PREFIX + "Left", P1_PREFIX + "Right", P1_PREFIX + "CCW", P1_PREFIX + "CW", P1_PREFIX + "Pull", P1_PREFIX + "Push"
];
foreach (var s in stickR)
{
@ -27,13 +23,12 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
definition.CategoryLabels[s] = "Right Controller";
}
pre = "P2 ";
var stickL = new List<string>
{
const string P2_PREFIX = "P2 ";
string[] stickL =
[
// P2 (left) stick
pre + "Forward", pre + "Back", pre + "Left", pre + "Right", pre + "CCW", pre + "CW", pre + "Pull", pre + "Push"
};
P2_PREFIX + "Forward", P2_PREFIX + "Back", P2_PREFIX + "Left", P2_PREFIX + "Right", P2_PREFIX + "CCW", P2_PREFIX + "CW", P2_PREFIX + "Pull", P2_PREFIX + "Push"
];
foreach (var s in stickL)
{
@ -42,10 +37,10 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
}
// console
var consoleButtons = new List<string>
{
string[] consoleButtons =
[
"TIME", "MODE", "HOLD", "START", "RESET"
};
];
foreach (var s in consoleButtons)
{
@ -54,64 +49,68 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
}
return definition.MakeImmutable();
}
}
});
public bool[] StateConsole = new bool[5];
public string[] ButtonsConsole =
{
private readonly string[] _buttonsConsole =
[
"TIME", "MODE", "HOLD", "START", "RESET"
};
];
public byte DataConsole
private bool[] _stateConsole = new bool[5];
private byte DataConsole
{
get
{
int w = 0;
for (int i = 0; i < 5; i++)
var w = 0;
for (var i = 0; i < 5; i++)
{
byte mask = (byte) (1 << i);
w = StateConsole[i] ? w | mask : w & ~mask;
var mask = (byte)(1 << i);
w = _stateConsole[i] ? w | mask : w & ~mask;
}
return (byte)(w & 0xFF);
}
}
public bool[] StateRight = new bool[8];
public string[] ButtonsRight =
{
"Right", "Left", "Back", "Forward", "CCW", "CW", "Pull", "Push"
};
public byte DataRight
private bool[] _stateRight = new bool[8];
private readonly string[] _buttonsRight =
[
"P1 Right", "P1 Left", "P1 Back", "P1 Forward", "P1 CCW", "P1 CW", "P1 Pull", "P1 Push"
];
private byte DataRight
{
get
{
int w = 0;
for (int i = 0; i < 8; i++)
var w = 0;
for (var i = 0; i < 8; i++)
{
byte mask = (byte)(1 << i);
w = StateRight[i] ? w | mask : w & ~mask;
var mask = (byte)(1 << i);
w = _stateRight[i] ? w | mask : w & ~mask;
}
return (byte)(w & 0xFF);
}
}
public bool[] StateLeft = new bool[8];
public string[] ButtonsLeft =
{
"Right", "Left", "Back", "Forward", "CCW", "CW", "Pull", "Push"
};
public byte DataLeft
private bool[] _stateLeft = new bool[8];
private readonly string[] _buttonsLeft =
[
"P2 Right", "P2 Left", "P2 Back", "P2 Forward", "P2 CCW", "P2 CW", "P2 Pull", "P2 Push"
];
private byte DataLeft
{
get
{
int w = 0;
for (int i = 0; i < 8; i++)
var w = 0;
for (var i = 0; i < 8; i++)
{
byte mask = (byte)(1 << i);
w = StateLeft[i] ? w | mask : w & ~mask;
var mask = (byte)(1 << i);
w = _stateLeft[i] ? w | mask : w & ~mask;
}
return (byte)(w & 0xFF);

View File

@ -0,0 +1,27 @@
using BizHawk.Emulation.Cores.Components.FairchildF8;
namespace BizHawk.Emulation.Cores.Consoles.ChannelF
{
public partial class ChannelF
{
public readonly struct CpuLink(ChannelF channelF) : IF3850Link
{
public byte ReadMemory(ushort address)
=> channelF.ReadBus(address);
public void WriteMemory(ushort address, byte value)
=> channelF.WriteBus(address, value);
public byte ReadHardware(ushort address)
=> channelF.ReadPort(address);
public void WriteHardware(ushort address, byte value)
=> channelF.WritePort(address, value);
public void OnExecFetch(ushort address)
{
// TODO: implement
}
}
}
}

View File

@ -7,10 +7,10 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
public partial class ChannelF : IDebuggable
{
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
=> CPU.GetCpuFlagsAndRegisters();
=> _cpu.GetCpuFlagsAndRegisters();
public void SetCpuRegister(string register, int value)
=> CPU.SetCpuRegister(register, value);
=> _cpu.SetCpuRegister(register, value);
public IMemoryCallbackSystem MemoryCallbacks { get; }
@ -19,6 +19,6 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
[FeatureNotImplemented]
public void Step(StepType type) => throw new NotImplementedException();
public long TotalExecutedCycles => CPU.TotalExecutedCycles;
public long TotalExecutedCycles => _cpu.TotalExecutedCycles;
}
}

View File

@ -6,21 +6,20 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
{
public IEmulatorServiceProvider ServiceProvider { get; }
public ControllerDefinition ControllerDefinition { get; set; }
public ControllerDefinition ControllerDefinition { get; }
public string SystemId => VSystemID.Raw.ChannelF;
public bool DeterministicEmulation { get; set; }
public bool DeterministicEmulation => true;
public int CpuClocksPerFrame;
public int FrameClock;
private int _cpuClocksPerFrame;
private int _frameClock;
private void CalcClock()
{
// CPU speeds from https://en.wikipedia.org/wiki/Fairchild_Channel_F
// also https://github.com/mamedev/mame/blob/c8192c898ce7f68c0c0b87e44199f0d3e710439b/src/mame/drivers/channelf.cpp
double cpuFreq, pixelClock;
int pixelClocksPerFrame;
if (Region == DisplayType.NTSC)
{
HTotal = 256;
@ -37,8 +36,7 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
cpuFreq = NTSC_COLORBURST / 2;
// NTSC pixel clock is NTSC Colorburst * 8 / 7
pixelClock = NTSC_COLORBURST * 8 / 7;
// NTSC refresh rate is (pixelclock * 8 / 7) / (256 * 264)
pixelClocksPerFrame = 256 * 264;
// NTSC refresh rate is (pixelclock * 8 / 7) / (HTotal * VTotal)
// (aka (1023750000 * 8) / (256 * 264 * 286 * 7)
// reduced to 234375 / 3872
VsyncNumerator = 234375;
@ -55,19 +53,18 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
ScanlineRepeats = 5;
PixelWidth = 2;
if (version == ConsoleVersion.ChannelF)
if (_version == ConsoleVersion.ChannelF)
{
// PAL CPU speed is 2MHz
cpuFreq = 2000000;
// PAL pixel clock is 4MHz
pixelClock = 4000000;
// PAL refresh rate is pixelclock / (256 * 312)
pixelClocksPerFrame = 256 * 312;
// PAL refresh rate is pixelclock / (HTotal * VTotal)
// reduced to 15625 / 312
VsyncNumerator = 15625;
VsyncDenominator = 312;
}
else if (version == ConsoleVersion.ChannelF_II)
else if (_version == ConsoleVersion.ChannelF_II)
{
// PAL CPU speed for gen 2 seems to be contested
// various sources seem to say 1.77MHz (i.e. PAL Colorburst * 2 / 5)
@ -81,8 +78,7 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
// not entirely sure what the pixel clock for PAL is here
// presumingly, it's just cpuFreq * 2?
pixelClock = PAL_COLORBURST * 8 / 9;
// PAL refresh rate is pixelclock / (256 * 312)
pixelClocksPerFrame = 256 * 312;
// PAL refresh rate is pixelclock / (HTotal * VTotal)
// (aka (4433618.75 * 8) / (256 * 312 * 9)
// reduced to 17734475 / 359424
VsyncNumerator = 17734475;
@ -94,10 +90,12 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
}
}
var c = cpuFreq * pixelClocksPerFrame / pixelClock;
CpuClocksPerFrame = (int) c;
PixelClocksPerCpuClock = pixelClock / cpuFreq;
PixelClocksPerFrame = pixelClocksPerFrame;
PixelClocksPerFrame = HTotal * VTotal;
var c = cpuFreq * PixelClocksPerFrame / pixelClock;
// note: this always results in a nice integer, no precision is lost!
_cpuClocksPerFrame = (int)c;
SetupAudio();
}
@ -109,22 +107,22 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
if (_tracer.IsEnabled())
{
CPU.TraceCallback = s => _tracer.Put(s);
_cpu.TraceCallback = s => _tracer.Put(s);
}
else
{
CPU.TraceCallback = null;
_cpu.TraceCallback = null;
}
PollInput();
while (FrameClock++ < CpuClocksPerFrame)
while (_frameClock++ < _cpuClocksPerFrame)
{
CPU.ExecuteOne();
_cpu.ExecuteOne();
ClockVideo();
}
FrameClock = 0;
_frameClock = 0;
_frame++;
if (_isLag)
@ -134,10 +132,6 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
}
private int _frame;
#pragma warning disable CS0414
//private int _lagcount;
//private bool _islag;
#pragma warning restore CS0414
public void ResetCounters()
{
@ -154,8 +148,8 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
private void ConsoleReset()
{
CPU.Reset();
Cartridge.Reset();
_cpu.Reset();
_cartridge.Reset();
}
}
}

View File

@ -25,38 +25,6 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
return ret ? PutSettingsDirtyBits.RebootCore : PutSettingsDirtyBits.None;
}
[CoreSettings]
public class ChannelFSyncSettings
{
[DisplayName("Deterministic Emulation")]
[Description("If true, the core agrees to behave in a completely deterministic manner")]
[DefaultValue(true)]
public bool DeterministicEmulation { get; set; }
[DisplayName("Region")]
[Description("NTSC or PAL - Affects the CPU clock speed and refresh rate")]
[DefaultValue(RegionType.NTSC)]
public RegionType Region { get; set; }
[DisplayName("Version")]
[Description("Channel F II has a very slightly different BIOS to Channel F and a slightly slower CPU in the PAL version compared to v1")]
[DefaultValue(ConsoleVersion.ChannelF)]
public ConsoleVersion Version { get; set; }
public ChannelFSyncSettings Clone()
{
return (ChannelFSyncSettings) MemberwiseClone();
}
public ChannelFSyncSettings()
{
SettingsUtil.SetDefaultValues(this);
}
public static bool NeedsReboot(ChannelFSyncSettings x, ChannelFSyncSettings y)
{
return !DeepEquality.DeepEquals(x, y);
}
}
public enum RegionType
{
NTSC,
@ -68,5 +36,28 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
ChannelF,
ChannelF_II
}
[CoreSettings]
public class ChannelFSyncSettings
{
[DisplayName("Region")]
[Description("NTSC or PAL - Affects the CPU clock speed and refresh rate")]
[DefaultValue(RegionType.NTSC)]
public RegionType Region { get; set; }
[DisplayName("Version")]
[Description("Channel F II has a very slightly different BIOS to Channel F and a slightly slower CPU in the PAL version compared to v1")]
[DefaultValue(ConsoleVersion.ChannelF)]
public ConsoleVersion Version { get; set; }
public ChannelFSyncSettings Clone()
=> (ChannelFSyncSettings)MemberwiseClone();
public ChannelFSyncSettings()
=> SettingsUtil.SetDefaultValues(this);
public static bool NeedsReboot(ChannelFSyncSettings x, ChannelFSyncSettings y)
=> !DeepEquality.DeepEquals(x, y);
}
}
}

View File

@ -29,7 +29,7 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
{
_samplesPerFrame = (int)(SAMPLE_RATE * VsyncDenominator / VsyncNumerator);
// TODO: more precise audio clocking
_cyclesPerSample = CpuClocksPerFrame / (double)_samplesPerFrame;
_cyclesPerSample = _cpuClocksPerFrame / (double)_samplesPerFrame;
_sampleBuffer = new short[_samplesPerFrame];
_filteredSampleBuffer = new double[_samplesPerFrame];
_toneBuffer = new int[_samplesPerFrame];
@ -37,8 +37,7 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
private void AudioChange()
{
var currSample = (int)(FrameClock / _cyclesPerSample);
var currSample = (int)(_frameClock / _cyclesPerSample);
while (currSample < _samplesPerFrame)
{
_toneBuffer[currSample++] = _tone;
@ -150,9 +149,7 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
}
public void GetSamplesAsync(short[] samples)
{
throw new NotSupportedException("Async is not available");
}
=> throw new NotSupportedException("Async is not available");
public void DiscardSamples()
{

View File

@ -7,14 +7,14 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
private void SyncState(Serializer ser)
{
ser.BeginSection("ChannelF");
ser.Sync(nameof(VRAM), ref VRAM, false);
ser.Sync(nameof(_latch_colour), ref _latch_colour);
ser.Sync(nameof(_latch_x), ref _latch_x);
ser.Sync(nameof(_latch_y), ref _latch_y);
ser.Sync(nameof(_vram), ref _vram, false);
ser.Sync(nameof(_latchColour), ref _latchColour);
ser.Sync(nameof(_latchX), ref _latchX);
ser.Sync(nameof(_latchY), ref _latchY);
ser.Sync(nameof(_pixelClockCounter), ref _pixelClockCounter);
ser.Sync(nameof(_pixelClocksRemaining), ref _pixelClocksRemaining);
ser.Sync(nameof(FrameClock), ref FrameClock);
ser.Sync(nameof(_frameClock), ref _frameClock);
ser.Sync(nameof(_frame), ref _frame);
ser.Sync(nameof(_isLag), ref _isLag);
ser.Sync(nameof(_lagCount), ref _lagCount);
@ -30,75 +30,21 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
ser.Sync(nameof(_currTone), ref _currTone);
ser.Sync(nameof(_samplePosition), ref _samplePosition);
ser.Sync(nameof(StateConsole), ref StateConsole, false);
ser.Sync(nameof(StateRight), ref StateRight, false);
ser.Sync(nameof(StateLeft), ref StateLeft, false);
ser.Sync(nameof(_stateConsole), ref _stateConsole, false);
ser.Sync(nameof(_stateRight), ref _stateRight, false);
ser.Sync(nameof(_stateLeft), ref _stateLeft, false);
ser.Sync(nameof(OutputLatch), ref OutputLatch, false);
ser.Sync(nameof(_outputLatch), ref _outputLatch, false);
ser.Sync(nameof(LS368Enable), ref LS368Enable);
//ser.Sync(nameof(ControllersEnabled), ref ControllersEnabled);
CPU.SyncState(ser);
Cartridge.SyncState(ser);
_cpu.SyncState(ser);
_cartridge.SyncState(ser);
ser.EndSection();
if (ser.IsReader)
{
SyncAllByteArrayDomains();
}
/*
byte[] core = null;
if (ser.IsWriter)
{
var ms = new MemoryStream();
ms.Close();
core = ms.ToArray();
}
if (ser.IsWriter)
{
ser.SyncEnum(nameof(_machineType), ref _machineType);
_cpu.SyncState(ser);
ser.BeginSection(nameof(ChannelF));
_machine.SyncState(ser);
ser.Sync("Frame", ref _machine.FrameCount);
ser.Sync("LagCount", ref _lagCount);
ser.Sync("IsLag", ref _isLag);
ser.EndSection();
}
if (ser.IsReader)
{
var tmpM = _machineType;
ser.SyncEnum(nameof(_machineType), ref _machineType);
if (tmpM != _machineType && _machineType.ToString() != "72")
{
string msg = "SAVESTATE FAILED TO LOAD!!\n\n";
msg += "Current Configuration: " + tmpM.ToString();
msg += "\n";
msg += "Saved Configuration: " + _machineType.ToString();
msg += "\n\n";
msg += "If you wish to load this SaveState ensure that you have the correct machine configuration selected, reboot the core, then try again.";
CoreComm.ShowMessage(msg);
_machineType = tmpM;
}
else
{
_cpu.SyncState(ser);
ser.BeginSection(nameof(ChannelF));
_machine.SyncState(ser);
ser.Sync("Frame", ref _machine.FrameCount);
ser.Sync("LagCount", ref _lagCount);
ser.Sync("IsLag", ref _isLag);
ser.EndSection();
SyncAllByteArrayDomains();
}
}
*/
}
}
}

View File

@ -8,14 +8,12 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
/// <summary>
/// 128x64 pixels - 8192x2bits (2 KB)
/// For the purposes of this core we will use 8192 bytes and just mask 0x03
/// (Also adding an additional 10 rows to the RAM buffer so that it's more aligned with the actual display)
/// </summary>
public byte[] VRAM = new byte[128 * 64];
private byte[] _vram = new byte[128 * 64];
public static readonly int[] FPalette =
{
//0x101010, 0xFDFDFD, 0x5331FF, 0x5DCC02, 0xF33F4B, 0xE0E0E0, 0xA6FF91, 0xD0CEFF
[
// 0x101010, 0xFDFDFD, 0x5331FF, 0x5DCC02, 0xF33F4B, 0xE0E0E0, 0xA6FF91, 0xD0CEFF
Colors.ARGB(0x10, 0x10, 0x10), // Black
Colors.ARGB(0xFD, 0xFD, 0xFD), // White
@ -25,24 +23,23 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
Colors.ARGB(0xE0, 0xE0, 0xE0), // Gray
Colors.ARGB(0x91, 0xFF, 0xA6), // BGreen
Colors.ARGB(0xCE, 0xD0, 0xFF), // BBlue
};
];
public static readonly int[] CMap =
{
[
0, 1, 1, 1,
7, 4, 2, 3,
5, 4, 2, 3,
6, 4, 2, 3,
};
];
private int _latch_colour = 2;
private int _latch_x;
private int _latch_y;
private int[] videoBuffer;
private int _latchColour = 2;
private int _latchX;
private int _latchY;
private int[] _videoBuffer;
private double _pixelClockCounter;
private double _pixelClocksRemaining;
private int ScanlineRepeats;
private int PixelWidth;
private int HTotal;
@ -54,10 +51,8 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
private double PixelClocksPerCpuClock;
private double PixelClocksPerFrame;
public void SetupVideo()
{
videoBuffer = new int[HTotal * VTotal];
}
private void SetupVideo()
=> _videoBuffer = new int[HTotal * VTotal];
/// <summary>
/// Called after every CPU clock
@ -67,7 +62,7 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
while (_pixelClocksRemaining > 1)
{
var currScanline = (int)(_pixelClockCounter / HTotal);
var currPixelInLine = (int)(_pixelClockCounter % HTotal);
var currPixelInLine = (int)(_pixelClockCounter - currScanline * HTotal);
var currRowInVram = currScanline / ScanlineRepeats;
var currColInVram = currPixelInLine / PixelWidth;
@ -84,12 +79,12 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
// active display
if (currRowInVram < 64)
{
var p1 = (VRAM[(currRowInVram * 0x80) + 125]) & 0x03;
var p2 = (VRAM[(currRowInVram * 0x80) + 126]) & 0x03;
var p1 = _vram[(currRowInVram * 0x80) + 125] & 0x03;
var p2 = _vram[(currRowInVram * 0x80) + 126] & 0x03;
var pOffset = ((p2 & 0x02) | (p1 >> 1)) << 2;
var colourIndex = pOffset + (VRAM[currColInVram | (currRowInVram << 7)] & 0x03);
videoBuffer[(currScanline * HTotal) + currPixelInLine] = FPalette[CMap[colourIndex]];
var colourIndex = pOffset + (_vram[currColInVram | (currRowInVram << 7)] & 0x03);
_videoBuffer[(currScanline * HTotal) + currPixelInLine] = FPalette[CMap[colourIndex]];
}
}
@ -98,13 +93,16 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
}
_pixelClocksRemaining += PixelClocksPerCpuClock;
_pixelClockCounter %= PixelClocksPerFrame;
while (_pixelClockCounter >= PixelClocksPerFrame)
{
_pixelClockCounter -= PixelClocksPerFrame;
}
}
private int HDisplayable => HBlankOn - HBlankOff;
private int VDisplayable => VBlankOn - VBlankOff;
private int[] ClampBuffer(int[] buffer, int originalWidth, int originalHeight, int trimLeft, int trimTop, int trimRight, int trimBottom)
private static int[] ClampBuffer(int[] buffer, int originalWidth, int originalHeight, int trimLeft, int trimTop, int trimRight, int trimBottom)
{
var newWidth = originalWidth - trimLeft - trimRight;
var newHeight = originalHeight - trimTop - trimBottom;
@ -112,7 +110,7 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
for (var y = 0; y < newHeight; y++)
{
for (int x = 0; x < newWidth; x++)
for (var x = 0; x < newWidth; x++)
{
var originalIndex = (y + trimTop) * originalWidth + (x + trimLeft);
var newIndex = y * newWidth + x;
@ -139,8 +137,6 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
public int VsyncDenominator { get; private set; }
public int[] GetVideoBuffer()
{
// https://channelf.se/veswiki/index.php?title=VRAM
// 'The emulator MESS uses a fixed 102x58 resolution starting at (4,4) but the safe area for a real system is about 95x58 pixels'
// 'Note that the pixel aspect is a lot closer to widescreen (16:9) than standard definition (4:3). On a TV from the 70's or 80's pixels are rectangular, standing up. In widescreen mode they are close to perfect squares'
@ -150,29 +146,9 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
// Right now we are just trimming based on the HBLANK and VBLANK values (we might need to go further like the other emulators)
// VirtualWidth is being used to force the aspect ratio into 4:3
// On real hardware it looks like this (so we are close): https://www.youtube.com/watch?v=ZvQA9tiEIuQ
return ClampBuffer(videoBuffer, HTotal, VTotal, HBlankOff, VBlankOff, HTotal - HBlankOn, VTotal - VBlankOn);
}
public int[] GetVideoBuffer()
=> ClampBuffer(_videoBuffer, HTotal, VTotal, HBlankOff, VBlankOff, HTotal - HBlankOn, VTotal - VBlankOn);
public DisplayType Region => region == RegionType.NTSC ? DisplayType.NTSC : DisplayType.PAL;
/*
private void BuildFrameFromRAM()
{
for (int r = 0; r < 64; r++)
{
// lines
var p1 = (VRAM[(r * 0x80) + 125]) & 0x03;
var p2 = (VRAM[(r * 0x80) + 126]) & 0x03;
var pOffset = ((p2 & 0x02) | (p1 >> 1)) << 2;
for (int c = 0; c < 128; c++)
{
// columns
var colourIndex = pOffset + (VRAM[c | (r << 7)] & 0x03);
frameBuffer[(r << 7) + c] = CMap[colourIndex];
}
}
}
*/
public DisplayType Region => _region == RegionType.NTSC ? DisplayType.NTSC : DisplayType.PAL;
}
}

View File

@ -18,63 +18,48 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
public IInputCallbackSystem InputCallbacks { get; } = new InputCallbackSystem();
private int _lagCount = 0;
private bool _isLag = false;
private int _lagCount;
private bool _isLag;
/// <summary>
/// Cycles through all the input callbacks
/// This should be done once per frame
/// </summary>
public bool PollInput()
private void PollInput()
{
bool noInput = true;
for (int i = 0; i < ButtonsConsole.Length; i++)
for (var i = 0; i < _buttonsConsole.Length; i++)
{
var key = ButtonsConsole[i];
bool prevState = StateConsole[i]; // CTRLConsole.Bit(i);
bool currState = _controller.IsPressed(key);
var key = _buttonsConsole[i];
var prevState = _stateConsole[i];
var currState = _controller.IsPressed(key);
if (currState != prevState)
{
StateConsole[i] = currState;
noInput = false;
_stateConsole[i] = currState;
if (key == "RESET" && StateConsole[i])
if (key == "RESET" && _stateConsole[i])
{
ConsoleReset();
for (int l = 0; l < OutputLatch.Length; l++)
for (var l = 0; l < _outputLatch.Length; l++)
{
OutputLatch[l] = 0;
_outputLatch[l] = 0;
}
return true;
return;
}
}
}
for (int i = 0; i < ButtonsRight.Length; i++)
for (var i = 0; i < _buttonsRight.Length; i++)
{
var key = "P1 " + ButtonsRight[i];
bool prevState = StateRight[i];
bool currState = _controller.IsPressed(key);
if (currState != prevState)
{
StateRight[i] = currState;
noInput = false;
}
var key = _buttonsRight[i];
_stateRight[i] = _controller.IsPressed(key);
}
for (int i = 0; i < ButtonsLeft.Length; i++)
for (var i = 0; i < _buttonsLeft.Length; i++)
{
var key = "P2 " + ButtonsLeft[i];
bool prevState = StateLeft[i];
bool currState = _controller.IsPressed(key);
if (currState != prevState)
{
StateLeft[i] = currState;
noInput = false;
var key = _buttonsLeft[i];
_stateLeft[i] = _controller.IsPressed(key);
}
}
return noInput;
}
}
}

View File

@ -19,12 +19,12 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
addr =>
{
if (addr is < 0 or > 63) throw new ArgumentOutOfRangeException(paramName: nameof(addr), addr, message: "address out of range");
return CPU.Regs[addr];
return _cpu.Regs[addr];
},
(addr, value) =>
{
if (addr is < 0 or > 63) throw new ArgumentOutOfRangeException(paramName: nameof(addr), addr, message: "address out of range");
CPU.Regs[addr] = value;
_cpu.Regs[addr] = value;
}, 1),
new MemoryDomainDelegate("System Bus", 0x10000, MemoryDomain.Endian.Big,
addr =>
@ -49,11 +49,10 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
private void SyncAllByteArrayDomains()
{
SyncByteArrayDomain("BIOS1", BIOS01);
SyncByteArrayDomain("BIOS2", BIOS02);
Cartridge.SyncByteArrayDomain(this);
//SyncByteArrayDomain("ROM", Rom);
SyncByteArrayDomain("VRAM", VRAM);
SyncByteArrayDomain("BIOS1", _bios01);
SyncByteArrayDomain("BIOS2", _bios02);
_cartridge.SyncByteArrayDomain(this);
SyncByteArrayDomain("VRAM", _vram);
}
public void SyncByteArrayDomain(string name, byte[] data)

View File

@ -1,7 +1,4 @@
using System.Collections.Generic;
using System.Linq;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Components.FairchildF8;
namespace BizHawk.Emulation.Cores.Consoles.ChannelF
@ -15,71 +12,60 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
var ser = new BasicServiceProvider(this);
ServiceProvider = ser;
CoreComm = lp.Comm;
_gameInfo = lp.Roms.Select(r => r.Game).ToList();
_files = lp.Roms.Select(r => r.RomData).ToList();
var gameInfo = lp.Roms[0].Game;
var rom = lp.Roms[0].RomData;
_syncSettings = lp.SyncSettings ?? new ChannelFSyncSettings();
region = _syncSettings.Region;
version = _syncSettings.Version;
_region = _syncSettings.Region;
_version = _syncSettings.Version;
MemoryCallbacks = new MemoryCallbackSystem([ "System Bus" ]);
ControllerDefinition = ChannelFControllerDefinition;
ControllerDefinition = _channelFControllerDefinition.Value;
if (version == ConsoleVersion.ChannelF)
if (_version == ConsoleVersion.ChannelF)
{
BIOS01 = CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("ChannelF", "ChannelF_sl131253"));
BIOS02 = CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("ChannelF", "ChannelF_sl131254"));
_bios01 = CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("ChannelF", "ChannelF_sl131253"));
_bios02 = CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("ChannelF", "ChannelF_sl131254"));
}
else
{
BIOS01 = CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("ChannelF", "ChannelF_sl90025"));
BIOS02 = CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("ChannelF", "ChannelF_sl131254"));
_bios01 = CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("ChannelF", "ChannelF_sl90025"));
_bios02 = CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("ChannelF", "ChannelF_sl131254"));
}
Cartridge = VesCartBase.Configure(_gameInfo[0], _files[0]);
CPU = new F3850
if (_bios01.Length != 1024 || _bios02.Length != 1024)
{
ReadMemory = ReadBus,
WriteMemory = WriteBus,
ReadHardware = ReadPort,
WriteHardware = WritePort,
DummyReadMemory = ReadBus
};
throw new InvalidOperationException("BIOS must be exactly 1024 bytes!");
}
_tracer = new TraceBuffer(CPU.TraceHeader);
_cartridge = VesCartBase.Configure(gameInfo, rom);
//var rom = _files.First();
//Array.Copy(rom, 0, Rom, 0, rom.Length);
_cpu = new F3850<CpuLink>(new CpuLink(this));
_tracer = new TraceBuffer(_cpu.TraceHeader);
CalcClock();
SetupVideo();
ser.Register<IVideoProvider>(this);
ser.Register<ITraceable>(_tracer);
ser.Register<IDisassemblable>(CPU);
ser.Register<ISoundProvider>(this);
ser.Register<IDisassemblable>(_cpu);
ser.Register<IStatable>(new StateSerializer(SyncState));
SetupMemoryDomains();
}
internal CoreComm CoreComm { get; }
private CoreComm CoreComm { get; }
public List<GameInfo> _gameInfo;
private readonly List<byte[]> _files;
public F3850 CPU;
private readonly F3850<CpuLink> _cpu;
private readonly TraceBuffer _tracer;
public IController _controller;
private IController _controller;
public VesCartBase Cartridge;
public RegionType region;
public ConsoleVersion version;
private readonly VesCartBase _cartridge;
private readonly RegionType _region;
private readonly ConsoleVersion _version;
public bool DriveLightEnabled => Cartridge.HasActivityLED;
public bool DriveLightEnabled => _cartridge.HasActivityLED;
public bool DriveLightOn => Cartridge.ActivityLED;
public bool DriveLightOn => _cartridge.ActivityLED;
public string DriveLightIconDescription => "Computer thinking activity";
}

View File

@ -5,8 +5,8 @@
/// </summary>
public partial class ChannelF
{
public byte[] BIOS01 = new byte[1024];
public byte[] BIOS02 = new byte[1024];
private readonly byte[] _bios01;
private readonly byte[] _bios02;
/// <summary>
/// Simulates reading a byte of data from the address space
@ -16,17 +16,17 @@
if (addr < 0x400)
{
// BIOS ROM 1
return BIOS01[addr];
return _bios01[addr];
}
else if (addr < 0x800)
{
// BIOS ROM 2
return BIOS02[addr - 0x400];
return _bios02[addr - 0x400];
}
else
{
// Cartridge Memory Space
return Cartridge.ReadBus(addr);
return _cartridge.ReadBus(addr);
}
}
@ -35,8 +35,6 @@
/// Channel F addressable through the address space)
/// </summary>
public void WriteBus(ushort addr, byte value)
{
Cartridge.WriteBus(addr, value);
}
=> _cartridge.WriteBus(addr, value);
}
}

View File

@ -18,17 +18,16 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
/// Depending on the attached cartridge, there may be additional hardware on the IO bus
/// All CPU and PSU I/O ports are active-low with output-latches
/// </summary>
public byte[] OutputLatch = new byte[0xFF];
private byte[] _outputLatch = new byte[0xFF];
public bool LS368Enable;
private bool LS368Enable;
/// <summary>
/// CPU is attempting to read from a port
/// </summary>
public byte ReadPort(ushort addr)
private byte ReadPort(ushort addr)
{
var result = 0xFF;
int result;
switch (addr)
{
case 0:
@ -38,7 +37,7 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
// b2: HOLD
// b3: START
// RESET button is connected directly to the RST pin on the CPU (this is handled here in the PollInput() method)
result = (~DataConsole & 0x0F) | OutputLatch[addr];
result = (~DataConsole & 0x0F) | _outputLatch[addr];
InputCallbacks.Call();
_isLag = false;
break;
@ -53,7 +52,7 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
// b4: CCW
// b5: CW
var v1 = LS368Enable ? DataRight : DataRight | 0xC0;
result = (~v1) | OutputLatch[addr];
result = ~v1 | _outputLatch[addr];
InputCallbacks.Call();
_isLag = false;
break;
@ -71,21 +70,22 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
// b6: PULL
// b7: PUSH
var v2 = LS368Enable ? DataLeft : 0xFF;
result = (~v2) | OutputLatch[addr];
result = ~v2 | _outputLatch[addr];
if (LS368Enable)
{
InputCallbacks.Call();
_isLag = false;
}
break;
case 5:
result = OutputLatch[addr];
result = _outputLatch[addr];
break;
default:
// possible cartridge hardware IO space
result = (Cartridge.ReadPort(addr));
result = _cartridge.ReadPort(addr);
break;
}
@ -95,37 +95,37 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
/// <summary>
/// CPU is attempting to write to the specified IO port
/// </summary>
public void WritePort(ushort addr, byte value)
private void WritePort(ushort addr, byte value)
{
switch (addr)
{
case 0:
OutputLatch[addr] = value;
_outputLatch[addr] = value;
LS368Enable = !value.Bit(6);
if (value.Bit(5))
{
// WRT pulse
// pulse clocks the 74195 parallel access shift register which feeds inputs of 2 NAND gates
// writing data to both sets of even and odd VRAM chips (based on the row and column addresses latched into the 7493 ICs)
VRAM[((_latch_y) * 0x80) + _latch_x] = (byte)_latch_colour;
_vram[_latchY * 0x80 + _latchX] = (byte)_latchColour;
}
break;
case 1:
OutputLatch[addr] = value;
_latch_colour = ((value ^ 0xFF) >> 6) & 0x03;
_outputLatch[addr] = value;
_latchColour = ((value ^ 0xFF) >> 6) & 0x03;
break;
case 4:
OutputLatch[addr] = value;
_latch_x = (value | 0x80) ^ 0xFF;
_outputLatch[addr] = value;
_latchX = (value | 0x80) ^ 0xFF;
break;
case 5:
OutputLatch[addr] = value;
_latch_y = (value | 0xC0) ^ 0xFF;
var audio = ((value) >> 6) & 0x03;
_outputLatch[addr] = value;
_latchY = (value | 0xC0) ^ 0xFF;
var audio = (value >> 6) & 0x03;
if (audio != _tone)
{
_tone = audio;
@ -136,7 +136,7 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF
default:
// possible write to cartridge hardware
Cartridge.WritePort(addr, value);
_cartridge.WritePort(addr, value);
break;
}
}