diff --git a/LR35902/Bus.cs b/LR35902/Bus.cs index 6914b13..5070682 100644 --- a/LR35902/Bus.cs +++ b/LR35902/Bus.cs @@ -3,6 +3,7 @@ // namespace EightBit.GameBoy { + using LR35902; using System; using System.Collections.Generic; @@ -15,13 +16,13 @@ namespace EightBit.GameBoy public const int CyclesPerLine = CyclesPerFrame / TotalLineCount; public const int RomPageSize = 0x4000; - private readonly Rom bootRom = new Rom(0x100); // 0x0000 - 0x00ff - private readonly List gameRomBanks = new List(); // 0x0000 - 0x3fff, 0x4000 - 0x7fff (switchable) - private readonly List ramBanks = new List(); // 0xa000 - 0xbfff (switchable) - private readonly UnusedMemory unmapped2000 = new UnusedMemory(0x2000, 0xff); // 0xa000 - 0xbfff - private readonly Ram lowInternalRam = new Ram(0x2000); // 0xc000 - 0xdfff (mirrored at 0xe000) - private readonly UnusedMemory unmapped60 = new UnusedMemory(0x60, 0xff); // 0xfea0 - 0xfeff - private readonly Ram highInternalRam = new Ram(0x80); // 0xff80 - 0xffff + private readonly Rom bootRom = new(0x100); // 0x0000 - 0x00ff + private readonly List gameRomBanks = new(); // 0x0000 - 0x3fff, 0x4000 - 0x7fff (switchable) + private readonly List ramBanks = new(); // 0xa000 - 0xbfff (switchable) + private readonly UnusedMemory unmapped2000 = new(0x2000, 0xff); // 0xa000 - 0xbfff + private readonly Ram lowInternalRam = new(0x2000); // 0xc000 - 0xdfff (mirrored at 0xe000) + private readonly UnusedMemory unmapped60 = new(0x60, 0xff); // 0xfea0 - 0xfeff + private readonly Ram highInternalRam = new(0x80); // 0xff80 - 0xffff private bool enabledLCD = false; @@ -240,20 +241,20 @@ namespace EightBit.GameBoy // ROM type switch (this.gameRomBanks[0].Peek(0x147)) { - case (byte)CartridgeType.ROM: - this.rom = true; - break; - case (byte)CartridgeType.ROM_MBC1: - this.rom = this.banked = true; - break; - case (byte)CartridgeType.ROM_MBC1_RAM: - this.rom = this.banked = this.ram = true; - break; - case (byte)CartridgeType.ROM_MBC1_RAM_BATTERY: - this.rom = this.banked = this.ram = this.battery = true; - break; - default: - throw new InvalidOperationException("Unhandled cartridge ROM type"); + case (byte)CartridgeType.ROM: + this.rom = true; + break; + case (byte)CartridgeType.ROM_MBC1: + this.rom = this.banked = true; + break; + case (byte)CartridgeType.ROM_MBC1_RAM: + this.rom = this.banked = this.ram = true; + break; + case (byte)CartridgeType.ROM_MBC1_RAM_BATTERY: + this.rom = this.banked = this.ram = this.battery = true; + break; + default: + throw new InvalidOperationException("Unhandled cartridge ROM type"); } // ROM size @@ -262,28 +263,28 @@ namespace EightBit.GameBoy var romSizeSpecification = this.Peek(0x148); switch (romSizeSpecification) { - case 0x52: - gameRomBanks = 72; - break; - case 0x53: - gameRomBanks = 80; - break; - case 0x54: - gameRomBanks = 96; - break; - default: - if (romSizeSpecification > 6) - { - throw new InvalidOperationException("Invalid ROM size specification"); - } + case 0x52: + gameRomBanks = 72; + break; + case 0x53: + gameRomBanks = 80; + break; + case 0x54: + gameRomBanks = 96; + break; + default: + if (romSizeSpecification > 6) + { + throw new InvalidOperationException("Invalid ROM size specification"); + } - gameRomBanks = 1 << (romSizeSpecification + 1); - if (gameRomBanks != this.gameRomBanks.Count) - { - throw new InvalidOperationException("ROM size specification mismatch"); - } + gameRomBanks = 1 << (romSizeSpecification + 1); + if (gameRomBanks != this.gameRomBanks.Count) + { + throw new InvalidOperationException("ROM size specification mismatch"); + } - break; + break; } // RAM size diff --git a/LR35902/Disassembler.cs b/LR35902/Disassembler.cs index 2dcbdcc..c63a606 100644 --- a/LR35902/Disassembler.cs +++ b/LR35902/Disassembler.cs @@ -4,6 +4,8 @@ namespace EightBit.GameBoy { + using LR35902; + public enum IoRegister { Abbreviated, // FF00 + dd diff --git a/LR35902/DisplayCharacteristics.cs b/LR35902/DisplayCharacteristics.cs index 1513669..5496529 100644 --- a/LR35902/DisplayCharacteristics.cs +++ b/LR35902/DisplayCharacteristics.cs @@ -15,5 +15,5 @@ namespace EightBit.GameBoy public const int RasterHeight = 144; public const int PixelCount = RasterWidth * RasterHeight; - } + } } diff --git a/LR35902/IoRegisters.cs b/LR35902/IoRegisters.cs index 7403268..05da485 100644 --- a/LR35902/IoRegisters.cs +++ b/LR35902/IoRegisters.cs @@ -68,8 +68,8 @@ namespace EightBit.GameBoy public const int BOOT_DISABLE = 0x50; private readonly Bus bus; - private readonly Register16 divCounter = new Register16(0xab, 0xcc); - private readonly Register16 dmaAddress = new Register16(); + private readonly Register16 divCounter = new(0xab, 0xcc); + private readonly Register16 dmaAddress = new(); private int timerCounter = 0; @@ -288,7 +288,7 @@ namespace EightBit.GameBoy private void TriggerKeypadInterrupt() => this.TriggerInterrupt(Interrupts.KeypadPressed); - private void Bus_WrittenByte(object sender, System.EventArgs e) + private void Bus_WrittenByte(object? sender, System.EventArgs e) { var address = this.bus.Address.Word; var value = this.bus.Data; @@ -296,55 +296,55 @@ namespace EightBit.GameBoy switch (port) { - case P1: - this.scanP14 = (value & (byte)Bits.Bit4) == 0; - this.scanP15 = (value & (byte)Bits.Bit5) == 0; - break; + case P1: + this.scanP14 = (value & (byte)Bits.Bit4) == 0; + this.scanP15 = (value & (byte)Bits.Bit5) == 0; + break; - case SB: // R/W - case SC: // R/W - break; + case SB: // R/W + case SC: // R/W + break; - case DIV: // R/W - this.Poke(port, 0); - this.timerCounter = this.divCounter.Word = 0; - break; - case TIMA: // R/W - break; - case TMA: // R/W - break; - case TAC: // R/W - break; + case DIV: // R/W + this.Poke(port, 0); + this.timerCounter = this.divCounter.Word = 0; + break; + case TIMA: // R/W + break; + case TMA: // R/W + break; + case TAC: // R/W + break; - case IF: // R/W - break; + case IF: // R/W + break; - case LCDC: - case STAT: - case SCY: - case SCX: - break; - case DMA: - this.dmaAddress.Word = Chip.PromoteByte(value); - this.dmaTransferActive = true; - break; - case LY: // R/O - this.Poke(port, 0); - break; - case BGP: - case OBP0: - case OBP1: - case WY: - case WX: - break; + case LCDC: + case STAT: + case SCY: + case SCX: + break; + case DMA: + this.dmaAddress.Word = Chip.PromoteByte(value); + this.dmaTransferActive = true; + break; + case LY: // R/O + this.Poke(port, 0); + break; + case BGP: + case OBP0: + case OBP1: + case WY: + case WX: + break; - case BOOT_DISABLE: - this.BootRomDisabled = value != 0; - break; + case BOOT_DISABLE: + this.BootRomDisabled = value != 0; + break; } } - private void Bus_ReadingByte(object sender, System.EventArgs e) + private void Bus_ReadingByte(object? sender, System.EventArgs e) { var address = this.bus.Address.Word; var io = (address >= BASE) && (address < 0xff80); diff --git a/LR35902/LR35902.BlarggTest/Board.cs b/LR35902/LR35902.BlarggTest/Board.cs index 64cc921..11df5d6 100644 --- a/LR35902/LR35902.BlarggTest/Board.cs +++ b/LR35902/LR35902.BlarggTest/Board.cs @@ -6,7 +6,7 @@ namespace LR35902.BlarggTest { using System; - public class Board : EightBit.GameBoy.Bus + internal class Board : EightBit.GameBoy.Bus { private readonly Configuration configuration; private readonly EightBit.GameBoy.Disassembler disassembler; @@ -30,17 +30,19 @@ namespace LR35902.BlarggTest public void Plug(string path) => this.LoadGameRom(this.configuration.RomDirectory + "/" + path); - private void Board_WrittenByte(object sender, System.EventArgs e) + private void Board_WrittenByte(object? sender, System.EventArgs e) { switch (this.Address.Word) { case EightBit.GameBoy.IoRegisters.BASE + EightBit.GameBoy.IoRegisters.SB: System.Console.Out.Write(Convert.ToChar(this.Data)); break; + default: + break; } } - private void CPU_ExecutingInstruction_Debug(object sender, System.EventArgs e) + private void CPU_ExecutingInstruction_Debug(object? sender, System.EventArgs e) { if (this.IO.BootRomDisabled) { diff --git a/LR35902/LR35902.BlarggTest/Computer.cs b/LR35902/LR35902.BlarggTest/Computer.cs index 3b3a64f..db1af2a 100644 --- a/LR35902/LR35902.BlarggTest/Computer.cs +++ b/LR35902/LR35902.BlarggTest/Computer.cs @@ -4,11 +4,9 @@ namespace LR35902.BlarggTest { - public class Computer + internal class Computer(Configuration configuration) { - private readonly Board board; - - public Computer(Configuration configuration) => this.board = new Board(configuration); + private readonly Board board = new(configuration); public void Run() { diff --git a/LR35902/LR35902.BlarggTest/Configuration.cs b/LR35902/LR35902.BlarggTest/Configuration.cs index 1de063f..bc69075 100644 --- a/LR35902/LR35902.BlarggTest/Configuration.cs +++ b/LR35902/LR35902.BlarggTest/Configuration.cs @@ -4,9 +4,9 @@ namespace LR35902.BlarggTest { - public class Configuration + internal class Configuration { - public bool DebugMode { get; set; } = false; + public bool DebugMode { get; set; } public string RomDirectory { get; set; } = "roms"; } diff --git a/LR35902/LR35902.BlarggTest/LR35902.BlarggTest.csproj b/LR35902/LR35902.BlarggTest/LR35902.BlarggTest.csproj index 1b0e1c5..d36dbaf 100644 --- a/LR35902/LR35902.BlarggTest/LR35902.BlarggTest.csproj +++ b/LR35902/LR35902.BlarggTest/LR35902.BlarggTest.csproj @@ -7,7 +7,7 @@ False False True - latest + latest-all Exe diff --git a/LR35902/LR35902.BlarggTest/Program.cs b/LR35902/LR35902.BlarggTest/Program.cs index 2e3d845..47de35b 100644 --- a/LR35902/LR35902.BlarggTest/Program.cs +++ b/LR35902/LR35902.BlarggTest/Program.cs @@ -4,7 +4,7 @@ namespace LR35902.BlarggTest { - public static class Program + internal static class Program { public static void Main(string[] args) { @@ -17,7 +17,7 @@ namespace LR35902.BlarggTest var computer = new Computer(configuration); - computer.Plug("blargg/cpu_instrs.gb"); // Passed + //computer.Plug("blargg/cpu_instrs.gb"); // Passed ////computer.Plug("blargg/01-special.gb"); // Passed ////computer.Plug("blargg/02-interrupts.gb"); // Passed ////computer.Plug("blargg/03-op sp,hl.gb"); // Passed @@ -30,8 +30,8 @@ namespace LR35902.BlarggTest ////computer.Plug("blargg/10-bit ops.gb"); // Passed ////computer.Plug("blargg/11-op a,(hl).gb"); // Passed - ////computer.Plug("blargg/instr_timing.gb"); // Failed #255 - ////computer.Plug("blargg/interrupt_time.gb"); // Failed + computer.Plug("blargg/instr_timing.gb"); // Failed #255 + //computer.Plug("blargg/interrupt_time.gb"); // Failed computer.RaisePOWER(); computer.Run(); diff --git a/LR35902/LR35902.FuseTest/TestRunner.cs b/LR35902/LR35902.FuseTest/TestRunner.cs index cd84668..abff66e 100644 --- a/LR35902/LR35902.FuseTest/TestRunner.cs +++ b/LR35902/LR35902.FuseTest/TestRunner.cs @@ -19,7 +19,7 @@ namespace Fuse { private readonly Test test; private readonly Result result; - private readonly EightBit.Ram ram = new EightBit.Ram(0x10000); + private readonly EightBit.Ram ram = new(0x10000); public TestRunner(Test test, Result result) { @@ -31,7 +31,7 @@ namespace Fuse public bool Unimplemented { get; private set; } = false; - public override EightBit.MemoryMapping Mapping(ushort address) => new EightBit.MemoryMapping(this.ram, 0, EightBit.Mask.Sixteen, EightBit.AccessLevel.ReadWrite); + public override EightBit.MemoryMapping Mapping(ushort address) => new(this.ram, 0, EightBit.Mask.Sixteen, EightBit.AccessLevel.ReadWrite); public void Run() { diff --git a/LR35902/LR35902.cs b/LR35902/LR35902.cs index bdf882e..8f4ea97 100644 --- a/LR35902/LR35902.cs +++ b/LR35902/LR35902.cs @@ -2,19 +2,18 @@ // Copyright (c) Adrian Conlon. All rights reserved. // -namespace EightBit.GameBoy +namespace LR35902 { - using System; + using EightBit; + using EightBit.GameBoy; + using Bus = EightBit.GameBoy.Bus; - public class LR35902 : IntelProcessor + public sealed class LR35902(Bus bus) : IntelProcessor(bus) { - private readonly Bus bus; - private readonly Register16 af = new Register16((int)Mask.Sixteen); + private readonly Bus bus = bus; + private readonly Register16 af = new((int)Mask.Sixteen); private bool prefixCB = false; - public LR35902(Bus bus) - : base(bus) => this.bus = bus; - public int ClockCycles => this.Cycles * 4; public override Register16 AF @@ -73,7 +72,7 @@ namespace EightBit.GameBoy { this.bus.IO.Poke(IoRegisters.IF, 0); this.LowerINT(); - var index = Chip.FindFirstSet(masked); + var index = FindFirstSet(masked); this.Bus.Data = (byte)(0x38 + (index << 3)); } else @@ -148,30 +147,18 @@ namespace EightBit.GameBoy private void Start() => this.Stopped = false; - private byte R(int r) + private byte R(int r) => r switch { - switch (r) - { - case 0: - return this.B; - case 1: - return this.C; - case 2: - return this.D; - case 3: - return this.E; - case 4: - return this.H; - case 5: - return this.L; - case 6: - return this.MemoryRead(this.HL.Word); - case 7: - return this.A; - default: - throw new ArgumentOutOfRangeException(nameof(r)); - } - } + 0 => this.B, + 1 => this.C, + 2 => this.D, + 3 => this.E, + 4 => this.H, + 5 => this.L, + 6 => this.MemoryRead(this.HL.Word), + 7 => this.A, + _ => throw new ArgumentOutOfRangeException(nameof(r)), + }; private void R(int r, byte value) { @@ -206,87 +193,53 @@ namespace EightBit.GameBoy } } - private Register16 RP(int rp) + private Register16 RP(int rp) => rp switch { - switch (rp) - { - case 0: - return this.BC; - case 1: - return this.DE; - case 2: - return this.HL; - case 3: - return this.SP; - default: - throw new ArgumentOutOfRangeException(nameof(rp)); - } - } + 0 => this.BC, + 1 => this.DE, + 2 => this.HL, + 3 => this.SP, + _ => throw new ArgumentOutOfRangeException(nameof(rp)), + }; - private Register16 RP2(int rp) + private Register16 RP2(int rp) => rp switch { - switch (rp) - { - case 0: - return this.BC; - case 1: - return this.DE; - case 2: - return this.HL; - case 3: - return this.AF; - default: - throw new ArgumentOutOfRangeException(nameof(rp)); - } - } + 0 => this.BC, + 1 => this.DE, + 2 => this.HL, + 3 => this.AF, + _ => throw new ArgumentOutOfRangeException(nameof(rp)), + }; private void ExecuteCB(int x, int y, int z) { switch (x) { case 0: // rot[y] r[z] - { - var operand = this.R(z); - switch (y) - { - case 0: - operand = this.RLC(operand); - break; - case 1: - operand = this.RRC(operand); - break; - case 2: - operand = this.RL(operand); - break; - case 3: - operand = this.RR(operand); - break; - case 4: - operand = this.SLA(operand); - break; - case 5: - operand = this.SRA(operand); - break; - case 6: // GB: SWAP r - operand = this.Swap(operand); - break; - case 7: - operand = this.SRL(operand); - break; - default: - throw new InvalidOperationException("Unreachable code block reached"); - } - - this.Tick(2); - this.R(z, operand); - this.F = AdjustZero(this.F, operand); - if (z == 6) { + var operand = this.R(z); + operand = y switch + { + 0 => this.RLC(operand), + 1 => this.RRC(operand), + 2 => this.RL(operand), + 3 => this.RR(operand), + 4 => this.SLA(operand), + 5 => this.SRA(operand), + 6 => this.Swap(operand), // GB: SWAP r + 7 => this.SRL(operand), + _ => throw new InvalidOperationException("Unreachable code block reached"), + }; this.Tick(2); - } + this.R(z, operand); + this.F = AdjustZero(this.F, operand); + if (z == 6) + { + this.Tick(2); + } - break; - } + break; + } case 1: // BIT y, r[z] this.Bit(y, this.R(z)); @@ -543,7 +496,7 @@ namespace EightBit.GameBoy else { this.R(y, this.R(z)); - if ((y == 6) || (z == 6)) + if (y == 6 || z == 6) { this.Tick(); // M operations } @@ -847,73 +800,41 @@ namespace EightBit.GameBoy return operand; } - private bool JumpConditionalFlag(int flag) + private bool JumpConditionalFlag(int flag) => flag switch { - switch (flag) - { - case 0: // NZ - return this.JumpConditional((this.F & (byte)StatusBits.ZF) == 0); - case 1: // Z - return this.JumpConditional((this.F & (byte)StatusBits.ZF) != 0); - case 2: // NC - return this.JumpConditional((this.F & (byte)StatusBits.CF) == 0); - case 3: // C - return this.JumpConditional((this.F & (byte)StatusBits.CF) != 0); - default: - throw new ArgumentOutOfRangeException(nameof(flag)); - } - } + 0 => this.JumpConditional((this.F & (byte)StatusBits.ZF) == 0), // NZ + 1 => this.JumpConditional((this.F & (byte)StatusBits.ZF) != 0), // Z + 2 => this.JumpConditional((this.F & (byte)StatusBits.CF) == 0), // NC + 3 => this.JumpConditional((this.F & (byte)StatusBits.CF) != 0), // C + _ => throw new ArgumentOutOfRangeException(nameof(flag)), + }; - private bool JumpRelativeConditionalFlag(int flag) + private bool JumpRelativeConditionalFlag(int flag) => flag switch { - switch (flag) - { - case 0: // NZ - return this.JumpRelativeConditional((this.F & (byte)StatusBits.ZF) == 0); - case 1: // Z - return this.JumpRelativeConditional((this.F & (byte)StatusBits.ZF) != 0); - case 2: // NC - return this.JumpRelativeConditional((this.F & (byte)StatusBits.CF) == 0); - case 3: // C - return this.JumpRelativeConditional((this.F & (byte)StatusBits.CF) != 0); - default: - throw new ArgumentOutOfRangeException(nameof(flag)); - } - } + 0 => this.JumpRelativeConditional((this.F & (byte)StatusBits.ZF) == 0), // NZ + 1 => this.JumpRelativeConditional((this.F & (byte)StatusBits.ZF) != 0), // Z + 2 => this.JumpRelativeConditional((this.F & (byte)StatusBits.CF) == 0), // NC + 3 => this.JumpRelativeConditional((this.F & (byte)StatusBits.CF) != 0), // C + _ => throw new ArgumentOutOfRangeException(nameof(flag)), + }; - private bool ReturnConditionalFlag(int flag) + private bool ReturnConditionalFlag(int flag) => flag switch { - switch (flag) - { - case 0: // NZ - return this.ReturnConditional((this.F & (byte)StatusBits.ZF) == 0); - case 1: // Z - return this.ReturnConditional((this.F & (byte)StatusBits.ZF) != 0); - case 2: // NC - return this.ReturnConditional((this.F & (byte)StatusBits.CF) == 0); - case 3: // C - return this.ReturnConditional((this.F & (byte)StatusBits.CF) != 0); - default: - throw new ArgumentOutOfRangeException(nameof(flag)); - } - } + 0 => this.ReturnConditional((this.F & (byte)StatusBits.ZF) == 0), // NZ + 1 => this.ReturnConditional((this.F & (byte)StatusBits.ZF) != 0), // Z + 2 => this.ReturnConditional((this.F & (byte)StatusBits.CF) == 0), // NC + 3 => this.ReturnConditional((this.F & (byte)StatusBits.CF) != 0), // C + _ => throw new ArgumentOutOfRangeException(nameof(flag)), + }; - private bool CallConditionalFlag(int flag) + private bool CallConditionalFlag(int flag) => flag switch { - switch (flag) - { - case 0: // NZ - return this.CallConditional((this.F & (byte)StatusBits.ZF) == 0); - case 1: // Z - return this.CallConditional((this.F & (byte)StatusBits.ZF) != 0); - case 2: // NC - return this.CallConditional((this.F & (byte)StatusBits.CF) == 0); - case 3: // C - return this.CallConditional((this.F & (byte)StatusBits.CF) != 0); - default: - throw new ArgumentOutOfRangeException(nameof(flag)); - } - } + 0 => this.CallConditional((this.F & (byte)StatusBits.ZF) == 0), // NZ + 1 => this.CallConditional((this.F & (byte)StatusBits.ZF) != 0), // Z + 2 => this.CallConditional((this.F & (byte)StatusBits.CF) == 0), // NC + 3 => this.CallConditional((this.F & (byte)StatusBits.CF) != 0), // C + _ => throw new ArgumentOutOfRangeException(nameof(flag)), + }; private void Add(Register16 operand, Register16 value) { @@ -1054,7 +975,7 @@ namespace EightBit.GameBoy private void Bit(int n, byte operand) { var carry = this.F & (byte)StatusBits.CF; - this.AndR(operand, Bit(n)); + _ = this.AndR(operand, Bit(n)); this.F = SetBit(this.F, StatusBits.CF, carry); } @@ -1076,19 +997,19 @@ namespace EightBit.GameBoy } else { - if (((this.F & (byte)StatusBits.HC) != 0) || LowNibble((byte)updated) > 9) + if ((this.F & (byte)StatusBits.HC) != 0 || LowNibble((byte)updated) > 9) { updated += 6; } - if (((this.F & (byte)StatusBits.CF) != 0) || updated > 0x9F) + if ((this.F & (byte)StatusBits.CF) != 0 || updated > 0x9F) { updated += 0x60; } } this.F = ClearBit(this.F, (byte)StatusBits.HC | (byte)StatusBits.ZF); - this.F = SetBit(this.F, StatusBits.CF, ((this.F & (byte)StatusBits.CF) != 0) || ((updated & (int)Bits.Bit8) != 0)); + this.F = SetBit(this.F, StatusBits.CF, (this.F & (byte)StatusBits.CF) != 0 || (updated & (int)Bits.Bit8) != 0); this.A = LowByte(updated); this.F = AdjustZero(this.F, this.A);