diff --git a/src/BizHawk.Emulation.Cores/CPUs/FairchildF8/F3850.Disassembler.cs b/src/BizHawk.Emulation.Cores/CPUs/FairchildF8/F3850.Disassembler.cs index f03b8a709f..602b1c3fb4 100644 --- a/src/BizHawk.Emulation.Cores/CPUs/FairchildF8/F3850.Disassembler.cs +++ b/src/BizHawk.Emulation.Cores/CPUs/FairchildF8/F3850.Disassembler.cs @@ -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) { diff --git a/src/BizHawk.Emulation.Cores/CPUs/FairchildF8/F3850.Execute.cs b/src/BizHawk.Emulation.Cores/CPUs/FairchildF8/F3850.Execute.cs index eec20ccda7..bc33c352d5 100644 --- a/src/BizHawk.Emulation.Cores/CPUs/FairchildF8/F3850.Execute.cs +++ b/src/BizHawk.Emulation.Cores/CPUs/FairchildF8/F3850.Execute.cs @@ -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; diff --git a/src/BizHawk.Emulation.Cores/CPUs/FairchildF8/F3850.Operations.cs b/src/BizHawk.Emulation.Cores/CPUs/FairchildF8/F3850.Operations.cs index 37fee2bce7..090d5ee2ff 100644 --- a/src/BizHawk.Emulation.Cores/CPUs/FairchildF8/F3850.Operations.cs +++ b/src/BizHawk.Emulation.Cores/CPUs/FairchildF8/F3850.Operations.cs @@ -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() diff --git a/src/BizHawk.Emulation.Cores/CPUs/FairchildF8/F3850.Registers.cs b/src/BizHawk.Emulation.Cores/CPUs/FairchildF8/F3850.Registers.cs index dfae8a6e33..f3b1af4d1c 100644 --- a/src/BizHawk.Emulation.Cores/CPUs/FairchildF8/F3850.Registers.cs +++ b/src/BizHawk.Emulation.Cores/CPUs/FairchildF8/F3850.Registers.cs @@ -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) diff --git a/src/BizHawk.Emulation.Cores/CPUs/FairchildF8/F3850.Tables.cs b/src/BizHawk.Emulation.Cores/CPUs/FairchildF8/F3850.Tables.cs index 45fcba1e01..370bd5839a 100644 --- a/src/BizHawk.Emulation.Cores/CPUs/FairchildF8/F3850.Tables.cs +++ b/src/BizHawk.Emulation.Cores/CPUs/FairchildF8/F3850.Tables.cs @@ -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 diff --git a/src/BizHawk.Emulation.Cores/CPUs/FairchildF8/F3850.cs b/src/BizHawk.Emulation.Cores/CPUs/FairchildF8/F3850.cs index 23d0ce1f01..c799cd0667 100644 --- a/src/BizHawk.Emulation.Cores/CPUs/FairchildF8/F3850.cs +++ b/src/BizHawk.Emulation.Cores/CPUs/FairchildF8/F3850.cs @@ -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); diff --git a/src/BizHawk.Emulation.Cores/CPUs/FairchildF8/IF3850Link.cs b/src/BizHawk.Emulation.Cores/CPUs/FairchildF8/IF3850Link.cs new file mode 100644 index 0000000000..d6ed7d0683 --- /dev/null +++ b/src/BizHawk.Emulation.Cores/CPUs/FairchildF8/IF3850Link.cs @@ -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); + } +} diff --git a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/mapper_HANG.cs b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/MapperHANG.cs similarity index 63% rename from src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/mapper_HANG.cs rename to src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/MapperHANG.cs index 98805ba4f7..1051e0f587 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/mapper_HANG.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/MapperHANG.cs @@ -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); } - } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/mapper_MAZE.cs b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/MapperMAZE.cs similarity index 62% rename from src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/mapper_MAZE.cs rename to src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/MapperMAZE.cs index 79eab02e6f..a21e08bcc2 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/mapper_MAZE.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/MapperMAZE.cs @@ -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) @@ -38,7 +36,7 @@ public override void WritePort(ushort addr, byte data) { var index = addr - 0x24; - SRAM2102_Write(index, data); + SRAM2102_Write(index, data); } } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/mapper_RIDDLE.cs b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/MapperRIDDLE.cs similarity index 63% rename from src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/mapper_RIDDLE.cs rename to src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/MapperRIDDLE.cs index 8330f555b9..a1f620193f 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/mapper_RIDDLE.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/MapperRIDDLE.cs @@ -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) { diff --git a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/mapper_SCHACH.cs b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/MapperSCHACH.cs similarity index 64% rename from src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/mapper_SCHACH.cs rename to src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/MapperSCHACH.cs index c1ac910789..49342f4f9a 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/mapper_SCHACH.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/MapperSCHACH.cs @@ -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) { diff --git a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/mapper_STD.cs b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/MapperSTD.cs similarity index 63% rename from src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/mapper_STD.cs rename to src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/MapperSTD.cs index 03aae45a39..80d777a445 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/mapper_STD.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/MapperSTD.cs @@ -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) { diff --git a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/VesCartBase.cs b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/VesCartBase.cs index 5f55044081..4ccc57f2bf 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/VesCartBase.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Cart/VesCartBase.cs @@ -8,7 +8,7 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF { public abstract class VesCartBase { - public abstract string BoardType { get; } + public abstract string BoardType { get; } public virtual void SyncByteArrayDomain(ChannelF sys) { @@ -19,23 +19,11 @@ 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[] _rom; protected byte[] _ram; public virtual bool HasActivityLED { get; set; } public virtual string ActivityLEDDescription { get; set; } - public bool ActivityLED; public int MultiBank; @@ -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": + 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,8 +127,8 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF if (m_read_write == 1) { - RAM[m_addr] = (byte)m_data0; - } + _ram[m_addr] = (byte)m_data0; + } } else { @@ -171,7 +157,7 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF b.CopyTo(resBytes, 0); m_addr_latch = (ushort)(resBytes[0] | resBytes[1] << 8); } - } + } public virtual void Reset() { @@ -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); diff --git a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.Controllers.cs b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.Controllers.cs index d4e91def2c..dd335bad0f 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.Controllers.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.Controllers.cs @@ -1,117 +1,116 @@ -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 + private static readonly Lazy<ControllerDefinition> _channelFControllerDefinition = new(() => { - get + ControllerDefinition definition = new("ChannelF Controller"); + + // sticks + + const string P1_PREFIX = "P1 "; + string[] stickR = + [ + // P1 (right) stick + 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) { - ControllerDefinition definition = new("ChannelF Controller"); - - string pre = "P1 "; - - // sticks - var stickR = new List<string> - { - // P1 (right) stick - pre + "Forward", pre + "Back", pre + "Left", pre + "Right", pre + "CCW", pre + "CW", pre + "Pull", pre + "Push" - }; - - foreach (var s in stickR) - { - definition.BoolButtons.Add(s); - definition.CategoryLabels[s] = "Right Controller"; - } - - pre = "P2 "; - - var stickL = new List<string> - { - // P2 (left) stick - pre + "Forward", pre + "Back", pre + "Left", pre + "Right", pre + "CCW", pre + "CW", pre + "Pull", pre + "Push" - }; - - foreach (var s in stickL) - { - definition.BoolButtons.Add(s); - definition.CategoryLabels[s] = "Left Controller"; - } - - // console - var consoleButtons = new List<string> - { - "TIME", "MODE", "HOLD", "START", "RESET" - }; - - foreach (var s in consoleButtons) - { - definition.BoolButtons.Add(s); - definition.CategoryLabels[s] = "Console"; - } - - return definition.MakeImmutable(); + definition.BoolButtons.Add(s); + definition.CategoryLabels[s] = "Right Controller"; } - } - public bool[] StateConsole = new bool[5]; - public string[] ButtonsConsole = - { + const string P2_PREFIX = "P2 "; + string[] stickL = + [ + // P2 (left) stick + 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) + { + definition.BoolButtons.Add(s); + definition.CategoryLabels[s] = "Left Controller"; + } + + // console + string[] consoleButtons = + [ + "TIME", "MODE", "HOLD", "START", "RESET" + ]; + + foreach (var s in consoleButtons) + { + definition.BoolButtons.Add(s); + definition.CategoryLabels[s] = "Console"; + } + + return definition.MakeImmutable(); + }); + + 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); diff --git a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.CpuLink.cs b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.CpuLink.cs new file mode 100644 index 0000000000..f470f1d46c --- /dev/null +++ b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.CpuLink.cs @@ -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 + } + } + } +} diff --git a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.IDebuggable.cs b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.IDebuggable.cs index 254f197a4c..29cad4e5d8 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.IDebuggable.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.IDebuggable.cs @@ -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; } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.IEmulator.cs b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.IEmulator.cs index 8084f7e818..061883d3a7 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.IEmulator.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.IEmulator.cs @@ -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(); } } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.ISettable.cs b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.ISettable.cs index da4b8a3740..98af9cd565 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.ISettable.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.ISettable.cs @@ -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); + } } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.ISoundProvider.cs b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.ISoundProvider.cs index 4384ac22ca..30b464c492 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.ISoundProvider.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.ISoundProvider.cs @@ -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; @@ -130,7 +129,7 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF for (var i = 2; i < samples.Length; i++) { filteredSamples[i] = (b0 / a0) * samples[i] + (b1 / a0) * samples[i - 1] + (b2 / a0) * samples[i - 2] - - (a1 / a0) * filteredSamples[i - 1] - (a2 / a0) * filteredSamples[i - 2]; + - (a1 / a0) * filteredSamples[i - 1] - (a2 / a0) * filteredSamples[i - 2]; } for (var i = 0; i < samples.Length; i++) @@ -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() { @@ -166,7 +163,7 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF public void GetSamplesSync(out short[] samples, out int nsamp) { - // process tone buffer + // process tone buffer for (var t = 0; t < _toneBuffer.Length; t++) { var tValue = _toneBuffer[t]; @@ -189,7 +186,7 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF _currTone = tValue; if (_rampCounter <= 0) - _sampleBuffer[t] = (short)((GetWaveSample(_samplePosition++, _currTone) * _amplitude) / 30); + _sampleBuffer[t] = (short)((GetWaveSample(_samplePosition++, _currTone) * _amplitude) / 30); } } else if (_currTone > 0) diff --git a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.IStatable.cs b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.IStatable.cs index b20772fecd..6ab0cdf802 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.IStatable.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.IStatable.cs @@ -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(_pixelClockCounter), ref _pixelClockCounter); + 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(); - } - } - */ } } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.IVideoProvider.cs b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.IVideoProvider.cs index c253d986b5..d0f9e677ba 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.IVideoProvider.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.IVideoProvider.cs @@ -8,41 +8,38 @@ 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 - - Colors.ARGB(0x10, 0x10, 0x10), // Black - Colors.ARGB(0xFD, 0xFD, 0xFD), // White - Colors.ARGB(0xFF, 0x31, 0x53), // Red - Colors.ARGB(0x02, 0xCC, 0x5D), // Green - Colors.ARGB(0x4B, 0x3F, 0xF3), // Blue - Colors.ARGB(0xE0, 0xE0, 0xE0), // Gray - Colors.ARGB(0x91, 0xFF, 0xA6), // BGreen - Colors.ARGB(0xCE, 0xD0, 0xFF), // BBlue - }; + [ + // 0x101010, 0xFDFDFD, 0x5331FF, 0x5DCC02, 0xF33F4B, 0xE0E0E0, 0xA6FF91, 0xD0CEFF + + Colors.ARGB(0x10, 0x10, 0x10), // Black + Colors.ARGB(0xFD, 0xFD, 0xFD), // White + Colors.ARGB(0xFF, 0x31, 0x53), // Red + Colors.ARGB(0x02, 0xCC, 0x5D), // Green + Colors.ARGB(0x4B, 0x3F, 0xF3), // Blue + 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,20 +51,18 @@ 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 /// </summary> private void ClockVideo() - { + { 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,27 +79,30 @@ 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]]; } } _pixelClockCounter++; - _pixelClocksRemaining -= 1; + _pixelClocksRemaining -= 1; } _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,40 +137,18 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF public int VsyncDenominator { get; private set; } + // 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' + // https://channelf.se/veswiki/index.php?title=Resolution + // 'Even though PAL televisions system has more lines vertically, the Channel F displays about the same as on the original NTSC video system' + // + // 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 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' - // https://channelf.se/veswiki/index.php?title=Resolution - // 'Even though PAL televisions system has more lines vertically, the Channel F displays about the same as on the original NTSC video system' - // - // 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); - } + => 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; } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.InputPollable.cs b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.InputPollable.cs index 7131276af4..1319ea4793 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.InputPollable.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.InputPollable.cs @@ -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; } } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.MemoryDomains.cs b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.MemoryDomains.cs index 58e071710c..f28aceabc2 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.MemoryDomains.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.MemoryDomains.cs @@ -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) diff --git a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.cs b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.cs index 789d71f0f4..991ba5b1a6 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.cs @@ -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"; } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Memory.cs b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Memory.cs index 06976b966f..920df11212 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Memory.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Memory.cs @@ -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); } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Ports.cs b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Ports.cs index 06dd4c0f9b..524597dbd3 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Ports.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/Ports.cs @@ -18,27 +18,26 @@ 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: - // Console Buttons - these are connected to pins 0-3 (bits 0-3) through a 7404 Hex Inverter - // b0: TIME - // b1: MODE - // 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]; + // Console Buttons - these are connected to pins 0-3 (bits 0-3) through a 7404 Hex Inverter + // b0: TIME + // b1: MODE + // 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]; InputCallbacks.Call(); _isLag = false; break; @@ -46,14 +45,14 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF case 1: // right controller (player 1) // connected through 7404 Hex Inverter - // b0: RIGHT - // b1: LEFT - // b2: BACK - // b3: FORWARD - // b4: CCW - // b5: CW + // b0: RIGHT + // b1: LEFT + // b2: BACK + // b3: FORWARD + // b4: CCW + // b5: CW var v1 = LS368Enable ? DataRight : DataRight | 0xC0; - result = (~v1) | OutputLatch[addr]; + result = ~v1 | _outputLatch[addr]; InputCallbacks.Call(); _isLag = false; break; @@ -62,30 +61,31 @@ namespace BizHawk.Emulation.Cores.Consoles.ChannelF // left controller (player 2) // connected through LS368 Hex Interting 3-State Buffer // the enable pin of this IC is driven by a CPU write to pin 6 on port 0 - // b0: RIGHT - // b1: LEFT - // b2: BACK - // b3: FORWARD - // b4: CCW - // b5: CW - // b6: PULL - // b7: PUSH + // b0: RIGHT + // b1: LEFT + // b2: BACK + // b3: FORWARD + // b4: CCW + // b5: CW + // 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; } }