diff --git a/CPU_emu/Class_CPU.cs b/CPU_emu/CPU/CPU.cs similarity index 82% rename from CPU_emu/Class_CPU.cs rename to CPU_emu/CPU/CPU.cs index 068594f..8a8b9dc 100644 --- a/CPU_emu/Class_CPU.cs +++ b/CPU_emu/CPU/CPU.cs @@ -13,11 +13,11 @@ namespace CPU_emulator; public partial class CPU { - + public uint PC { get; set; } = 0xFFFC; // ProgramCounter public bool SlowDown { get; set; } = false; public int SlowDownTime { get; set; } = 750; // - + public ushort SP; // StackPointer public byte A, X, Y; // registers private ushort ResetVector = 0xFFFC; @@ -29,16 +29,16 @@ public partial class CPU private bool _ExitRequested; private bool _SteppingMode; internal ulong _CpuCycle; - private const uint MAX_MEM = 1024 * 64; - private byte[] Data = new byte[MAX_MEM]; + //private const uint MAX_MEM = 1024 * 64; + //private byte[] Data = new byte[MAX_MEM]; - MemoryBus _memoryBus; + MappedBus _memoryBus; private byte NegativeFlagBit = 0b10000000, OverflowFlagBit = 0b01000000, - BreakFlagBit = 0b000010000, - UnusedFlagBit = 0b000100000, + BreakFlagBit = 0b000010000, + UnusedFlagBit = 0b000100000, InterruptDisableFlagBit = 0b000000100, ZeroBit = 0b00000001; @@ -51,12 +51,13 @@ public byte[] Memory { get { - return Data; + return _memoryBus.Dump(0, _memoryBus.RamSize); } set { - Data = value; + //Data = value; + _memoryBus.Load(0, value); OnMemoryUpdate?.Invoke(this, new CPUEventArgs(this)); } } @@ -64,18 +65,19 @@ public byte[] Memory //public uint PC { get => pC; set => pC = value; } //public uint InitialPC { get => _initialPC; set => _initialPC = value; } - public event EventHandler OnFlagsUpdate; - public event EventHandler OnMemoryUpdate; - public event EventHandler OnRegisterUpdate; - public event EventHandler OnCpuCycleIncrement; - public event EventHandler OnProgramCounterUpdate; - public event EventHandler OnStackPointerUpdate; - public event EventHandler OnPCoverflow; - public event EventHandler OnBreak; - - public CPU() + public event EventHandler? OnFlagsUpdate; + public event EventHandler? OnMemoryUpdate; + public event EventHandler? OnRegisterUpdate; + public event EventHandler? OnCpuCycleIncrement; + public event EventHandler? OnProgramCounterUpdate; + public event EventHandler? OnStackPointerUpdate; + public event EventHandler? OnPCoverflow; + public event EventHandler? OnBreak; + + public CPU(MappedBus memoryBus) { - _memoryBus = new MemoryBus(ref Data); + //_memoryBus = new MemoryBus(ref Data); + _memoryBus = memoryBus; SetVectors(); } @@ -97,7 +99,7 @@ public void Reset() SetRegister("A", 0); SetRegister("X", 0); SetRegister("Y", 0); - + ExitRequested = false; // set all status flags to false @@ -111,24 +113,24 @@ public void Reset() } - + public void Start() { ExitRequested = false; - + var CpuRunner = new BackgroundWorker(); CpuRunner.DoWork += CpuRunner_DoWork; CpuRunner.RunWorkerAsync(); } - + private void CpuRunner_DoWork(object sender, DoWorkEventArgs e) { - + Type thisType = this.GetType(); - + while (CpuIsRunning) { byte instruction = FetchByte(); @@ -143,7 +145,7 @@ private void CpuRunner_DoWork(object sender, DoWorkEventArgs e) { break; } - if (PC >= MAX_MEM) + if (PC >= _memoryBus.RamSize) { OnPCoverflow?.Invoke(this, new CPUEventArgs(this)); break; @@ -184,11 +186,6 @@ private void CallInstruction(Type thisType, byte instruction) } } - private uint AddrAbsolute() - { - return FetchWord(ref _CpuCycle); - } - private byte PullByteFromStack(ref ulong CpuCycle) { IncrementSP(); @@ -198,13 +195,13 @@ private byte PullByteFromStack(ref ulong CpuCycle) return b; } - private void PushByteToStack(byte b,ref ulong CpuCycle) + private void PushByteToStack(byte b, ref ulong CpuCycle) { WriteByteToMemory(b, SP); IncrementCpuCycle(1); DecrementSP(); IncrementCpuCycle(1); - + } public void Stop() @@ -222,48 +219,41 @@ private void SetZeroAndNegativeFlags(byte register) private byte FetchByte() { - //byte data = Data[PC]; byte data = _memoryBus.Read((ushort)PC); IncrementPC(); IncrementCpuCycle(1); return data; } - private ushort FetchWord(ref ulong CpuCycle) + private ushort FetchWord() { ushort LoByte = ReadByteFromMemory((ushort)PC); - PC++; + IncrementPC(); ushort HiByte = (ushort)(ReadByteFromMemory((ushort)PC) << 8); + IncrementPC(); IncrementCpuCycle(2); return LoByte |= HiByte; } - public void WriteByteToMemory(byte b,ushort address) + public void WriteByteToMemory(byte b, ushort address) { - //Data[address] = b; _memoryBus.Write(address, b); OnMemoryUpdate?.Invoke(this, new CPUEventArgs(this)); } private byte ReadByteFromMemory(ushort address) { - //return Data[address]; return _memoryBus.Read(address); } private ushort ReadWordFromMemory(ushort address) { - //ushort tmpPC = ReadByteFromMemory(ResetVector); - //ResetVector++; - //tmpPC |= (ushort)(ReadByteFromMemory(ResetVector) << 8); - - ushort LoByte = ReadByteFromMemory(address); address++; - ushort HiByte = (ushort)(ReadByteFromMemory(address) <<8); - + ushort HiByte = (ushort)(ReadByteFromMemory(address) << 8); + return LoByte |= HiByte; } @@ -293,7 +283,7 @@ private void IncrementCpuCycle(ulong count) private void ResetCpuCycle() { - _CpuCycle =0; + _CpuCycle = 0; OnCpuCycleIncrement?.Invoke(this, new CPUEventArgs(this)); } @@ -314,8 +304,8 @@ public void DecrementPC() PC--; OnProgramCounterUpdate?.Invoke(this, new CPUEventArgs(this)); } - - //UT + + public void SetRegister(string regname, byte value) { switch (regname) @@ -334,13 +324,10 @@ public void SetRegister(string regname, byte value) } OnRegisterUpdate?.Invoke(this, new CPUEventArgs(this)); } - //UT + public void ResetMemory() { - for (int i = 0; i < MAX_MEM; i++) - { - Data[i] = 0x00; - } + _memoryBus.EraseRam(); SetVectors(); @@ -348,14 +335,14 @@ public void ResetMemory() } public byte[] ReadMemory() { - return Data; + return _memoryBus.DumpRam(); } internal void UpdateMemoryRange(byte[] data, int startAddress) { for (int i = 0; i < data.Length; i++) { - Memory[startAddress + i] = data[i]; + _memoryBus.Write((ushort)(startAddress + i), data[i]); } OnMemoryUpdate?.Invoke(this, new CPUEventArgs(this)); @@ -364,7 +351,7 @@ internal void UpdateMemoryRange(byte[] data, int startAddress) public class CPUEventArgs : EventArgs { - CPU cpu = new CPU(); + CPU cpu; public string Message { get; set; } public byte[] Memory { get; set; } @@ -395,5 +382,5 @@ private void SetProperties() Cycles = cpu._CpuCycle; } - + } diff --git a/CPU_emu/CPU_emu.CMD_Methods.cs b/CPU_emu/CPU/CPU_CMD_Methods.cs similarity index 51% rename from CPU_emu/CPU_emu.CMD_Methods.cs rename to CPU_emu/CPU/CPU_CMD_Methods.cs index d70669a..706adea 100644 --- a/CPU_emu/CPU_emu.CMD_Methods.cs +++ b/CPU_emu/CPU/CPU_CMD_Methods.cs @@ -15,29 +15,85 @@ public void Cmd_00() #endregion #region LDA - // Load Accumulator immidiate A9 + [Opcode(2)] - public void Cmd_A9() + public void Cmd_A9() // Load Accumulator immidiate A9 { - SetRegister("A", FetchByte()); + byte value = AddrImmediate(); + SetRegister("A", value); SetZeroAndNegativeFlags(A); } - // Load Accumulator zeropage A5 - public void Cmd_A5() + // LDA $zz + [Opcode(2)] + public void Cmd_A5() // Load Accumulator zeropage A5 { - SetRegister("A", ReadByteFromMemory(FetchByte())); + ushort addr = AddrZeroPage(); + byte value = ReadByteFromMemory(addr); + SetRegister("A", value); SetZeroAndNegativeFlags(A); } - // Load Accumulator zeropage X B5 - public void Cmd_B5() + // LDA $zz,X + [Opcode(4)] + public void Cmd_B5() // Load Accumulator zeropage X B5 { - byte b_tmp = FetchByte(); - b_tmp += X; // add regX to address - SetRegister("A", ReadByteFromMemory(b_tmp)); + ushort addr = AddrZeroPageX(); + byte value = ReadByteFromMemory(addr); + SetRegister("A", value); + SetZeroAndNegativeFlags(A); + } + + // LDA $nnnn (absolute) + [Opcode(4)] + public void Cmd_AD() // Load Accumulator absolute AD + { + ushort addr = AddrAbsolute(); + byte value = ReadByteFromMemory(addr); + SetRegister("A", value); + SetZeroAndNegativeFlags(A); + } + + // LDA $nnnn,X + [Opcode(4)] + public void Cmd_BD() // Load Accumulator absolute X BD + { + ushort addr = AddrAbsoluteX(); + byte value = ReadByteFromMemory(addr); + SetRegister("A", value); + SetZeroAndNegativeFlags(A); + } + + // LDA $nnnn,Y + [Opcode(4)] + public void Cmd_B9() + { + ushort addr = AddrAbsoluteY(); + byte value = ReadByteFromMemory(addr); + SetRegister("A", value); SetZeroAndNegativeFlags(A); } + + // LDA ($zz,X) – Indirect,X + [Opcode(6)] + public void Cmd_A1() + { + ushort addr = AddrIndirectX(); + byte value = ReadByteFromMemory(addr); + SetRegister("A", value); + SetZeroAndNegativeFlags(A); + } + + // LDA ($zz),Y – Indirect,Y + [Opcode(5)] + public void Cmd_B1() + { + ushort addr = AddrIndirectY(); + byte value = ReadByteFromMemory(addr); + SetRegister("A", value); + SetZeroAndNegativeFlags(A); + } + #endregion #region LDX @@ -105,4 +161,7 @@ public void Cmd_48() } #endregion + + + } \ No newline at end of file diff --git a/CPU_emu/CPU/CPU_addressing_helper.cs b/CPU_emu/CPU/CPU_addressing_helper.cs new file mode 100644 index 0000000..ddc9c61 --- /dev/null +++ b/CPU_emu/CPU/CPU_addressing_helper.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CPU_emulator +{ + public partial class CPU + { + #region addressig helper + + // #Immediate + private byte AddrImmediate() + { + return FetchByte(); + } + + // $zz – Zero Page + private ushort AddrZeroPage() + { + // return (ushort)(addr & 0x00FF); cast ushort + return FetchByte(); // implicit ushort + } + + // Zero Page,X – 8-Bit-Baseaddress + X, Wrap at 0xFF → 0x00 + private ushort AddrZeroPageX() + { + byte baseAddr = FetchByte(); + byte effectiveAddr = (byte)(baseAddr + X); // automatic 8-Bit-Wrap + return effectiveAddr; + } + + // Absolute + private ushort AddrAbsolute() + { + return FetchWord(); + } + + // Absolute,X (no page wrap taken into account) + private ushort AddrAbsoluteX() + { + return (ushort)(AddrAbsolute() + X); + } + + // Absolute,Y + private ushort AddrAbsoluteY() + { + return (ushort)(AddrAbsolute() + Y); + } + + // Indirect,X = ($zz,X) + private ushort AddrIndirectX() + { + byte zpAddr = (byte)(FetchByte() + X); + byte lo = ReadByteFromMemory(zpAddr); + byte hi = ReadByteFromMemory((byte)(zpAddr + 1)); + return (ushort)(lo | (hi << 8)); + } + + // Indirect,Y = ($zz),Y + private ushort AddrIndirectY() + { + byte zpAddr = FetchByte(); + byte lo = ReadByteFromMemory(zpAddr); + byte hi = ReadByteFromMemory((byte)(zpAddr + 1)); + return (ushort)((lo | (hi << 8)) + Y); + } + + + + #endregion + } +} diff --git a/CPU_emu/CPU_emu.opcodes.cs b/CPU_emu/CPU_emu.opcodes.cs deleted file mode 100644 index c1f7b6d..0000000 --- a/CPU_emu/CPU_emu.opcodes.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace CPU_emulator -{ - public partial class CPU - { - //private const byte - // OC_BRK = 0x00, - // // LDA - // OC_LDA_IM = 0xA9, - // OC_LDA_ZP = 0xA5, - // OC_LDA_ZPX = 0xB5, - // // LDX - // OC_LDX_IM = 0xA2, - // OC_LDX_ZP = 0xA6, - // OC_LDX_ZPY = 0xB6, - // // LDY - // OC_LDY_IM = 0xA0, - // OC_LDY_ZP = 0xA4, - // OC_LDY_ZPX = 0xB4, - - // // JMP - // OC_JMP_ABS = 0x4C, - // OC_JMP_IND = 0x6C, - - // // Stack - // OC_PHA = 0x48, - // OC_PLA = 0x68 - // ; - - - } -} diff --git a/CPU_emu/Forms/CPU_emu.cs b/CPU_emu/Forms/CPU_emu.cs index c5885d8..c9dbbbd 100644 --- a/CPU_emu/Forms/CPU_emu.cs +++ b/CPU_emu/Forms/CPU_emu.cs @@ -1,4 +1,5 @@ using Bulb; +using CPU_emu; using Newtonsoft.Json; using System; using System.Collections.Generic; @@ -9,6 +10,7 @@ using System.Text; using System.Text.RegularExpressions; using System.Windows.Forms; +using static System.Runtime.InteropServices.JavaScript.JSType; namespace CPU_emulator @@ -27,12 +29,30 @@ public partial class CPU_emu : Form private ConfigSettings config = new ConfigSettings(); private string _configFilePath = Application.StartupPath + Path.DirectorySeparatorChar + "Data" + Path.DirectorySeparatorChar + "config.json"; + private byte[] _data = new byte[65536]; + private RamBus ram; + private RomBus basicRom; + private RomBus kernalRom; + private IoDevice ioDevice; + public CPU_emu() { //CheckForIllegalCrossThreadCalls = false; InitializeComponent(); - Cpu = new CPU(); + ram = new RamBus(_data); + //var basicRom = new RomBus(File.ReadAllBytes("basic.rom"), 0xA000); + //var kernalRom = new RomBus(File.ReadAllBytes("kernal.rom"), 0xE000); + + basicRom = new RomBus(new byte[1024 * 8], 0xA000); + kernalRom = new RomBus(new byte[1024 * 8], 0xE000); + + ioDevice = new IoDevice(); + + MappedBus memoryBus = new MappedBus(ram, basicRom, kernalRom, ioDevice); + + + Cpu = new CPU(memoryBus); Cpu.OnFlagsUpdate += Cpu_onFlagsUpdate; Cpu.OnMemoryUpdate += Cpu_OnMemoryUpdate; Cpu.OnRegisterUpdate += Cpu_OnRegisterUpdate; @@ -42,6 +62,8 @@ public CPU_emu() Cpu.OnBreak += Cpu_OnBreak; Cpu.OnCpuCycleIncrement += Cpu_OnCpuCycleIncrement; + + Cpu.Reset(); // config.OnPropertyChanged gets initialized @ CPU_emu_Load() due to race conditions @@ -392,7 +414,8 @@ private void SaveMemoryToFile(string fileName) try { fs = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write); - fs.Write(Cpu.Memory, 0, Cpu.Memory.Length); + //fs.Write(Cpu.Memory, 0, Cpu.Memory.Length); + fs.Write(ram.Dump(0, ram.RamSize)); fs.Close(); } @@ -477,10 +500,10 @@ private void LoadMemoryFromFile(string fileName,int insertIndex) fs = File.OpenRead(fileName); //Debug.Print(fs.Length.ToString()); //Debug.Print(Cpu.Memory.Length.ToString()); - if (fs.Length <= Cpu.Memory.Length) + if (fs.Length <= ram.RamSize) { mem = new byte[fs.Length]; - tmpCpuMem = new byte[Cpu.Memory.Length]; + tmpCpuMem = new byte[ram.RamSize]; tmpCpuMem = Cpu.Memory; fs.Read(mem, 0, (int)fs.Length); @@ -495,15 +518,15 @@ private void LoadMemoryFromFile(string fileName,int insertIndex) { StringBuilder ErrorMessage = new StringBuilder(); ErrorMessage.AppendLine("File is too large"); - ErrorMessage.AppendLine("File has " + fs.Length.ToString() + "bytes."); - ErrorMessage.AppendLine("Max allowed size is " + Cpu.Memory.Length.ToString() + " bytes."); + ErrorMessage.AppendLine($"File has {fs.Length.ToString()} bytes."); + ErrorMessage.AppendLine($"Max allowed size is {ram.RamSize} bytes."); MessageBox.Show(ErrorMessage.ToString(), "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error); } } catch (Exception e) { - MessageBox.Show(e.Message + "\n---\n" + e.InnerException.Message, "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error); + MessageBox.Show(e.Message + "\n---\n" , "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error); UseWaitCursor = false; throw; } diff --git a/CPU_emu/LedBulb.cs b/CPU_emu/GUI-Helper/LedBulb.cs similarity index 100% rename from CPU_emu/LedBulb.cs rename to CPU_emu/GUI-Helper/LedBulb.cs diff --git a/CPU_emu/SevenSegment.cs b/CPU_emu/GUI-Helper/SevenSegment.cs similarity index 100% rename from CPU_emu/SevenSegment.cs rename to CPU_emu/GUI-Helper/SevenSegment.cs diff --git a/CPU_emu/SevenSegmentArray.cs b/CPU_emu/GUI-Helper/SevenSegmentArray.cs similarity index 100% rename from CPU_emu/SevenSegmentArray.cs rename to CPU_emu/GUI-Helper/SevenSegmentArray.cs diff --git a/CPU_emu/Interfaces/IMemoryBus.cs b/CPU_emu/Interfaces/IMemoryBus.cs index 7149b5f..29ef61a 100644 --- a/CPU_emu/Interfaces/IMemoryBus.cs +++ b/CPU_emu/Interfaces/IMemoryBus.cs @@ -6,7 +6,7 @@ namespace CPU_emu; -internal interface IMemoryBus +public interface IMemoryBus { byte Read(ushort address); void Write(ushort address, byte value); diff --git a/CPU_emu/MemoryBus.cs b/CPU_emu/MemoryBus.cs deleted file mode 100644 index 83c6312..0000000 --- a/CPU_emu/MemoryBus.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace CPU_emu -{ - internal class MemoryBus : IMemoryBus - { - private readonly byte[] _memory; - - public MemoryBus(int memSize = 1024 * 64) - { - _memory = new byte[memSize]; - } - public MemoryBus(ref byte[] memory) - { - _memory = memory; - } - public byte Read(ushort address) - { - return _memory[address]; - } - - public void Write(ushort address, byte value) - { - _memory[address] = value; - } - - public void Load(ushort address, params byte[] data) - { - Array.Copy(data, 0, _memory, address, data.Length); - } - - public byte[] Dump(ushort address, int length) - { - return _memory.Skip(address).Take(length).ToArray(); - } - - - } -} diff --git a/CPU_emu/MemoryBus/IoDevice.cs b/CPU_emu/MemoryBus/IoDevice.cs new file mode 100644 index 0000000..e3aef73 --- /dev/null +++ b/CPU_emu/MemoryBus/IoDevice.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CPU_emu; + +public class IoDevice : IMemoryBus +{ + public byte[] Dump(ushort address, int length) + { + throw new NotImplementedException(); + } + + public void Load(ushort address, params byte[] data) + { + throw new NotImplementedException(); + } + + public byte Read(ushort address) + { + Console.WriteLine($"[IO READ] from ${address:X4}"); + return 0xFF; // Dummy-Value + } + + public void Write(ushort address, byte value) + { + Console.WriteLine($"[IO WRITE] to ${address:X4} = ${value:X2}"); + } +} diff --git a/CPU_emu/MemoryBus/MappedBus.cs b/CPU_emu/MemoryBus/MappedBus.cs new file mode 100644 index 0000000..b8ad25f --- /dev/null +++ b/CPU_emu/MemoryBus/MappedBus.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CPU_emu; + +public class MappedBus : IMemoryBus +{ + + private readonly RamBus _ram; + private readonly RomBus _basicRom; + private readonly RomBus _kernalRom; + private readonly IoDevice _io; + + public int RamSize + { + get { return _ram.RamSize; } + } + + public MappedBus(RamBus ram, RomBus basicRom, RomBus kernalRom, IoDevice io) + { + _ram = ram; + _basicRom = basicRom; + _kernalRom = kernalRom; + _io = io; + } + + public void EraseRam() + { + _ram.Load(0,new byte[_ram.RamSize]); + } + + public byte[] Dump(ushort address, int length) + { + return _ram.Dump(address, length); + } + + public byte[] DumpRam() + { + return _ram.Dump(0, _ram.RamSize); + } + + public void Load(ushort address, params byte[] data) + { + _ram.Load(0, data); + } + + public byte Read(ushort address) + { + //if (address >= 0xA000 && address <= 0xBFFF) + // return _basicRom.Read(address); + //else if (address >= 0xD000 && address <= 0xDFFF) + // return _io.Read(address); + //else if (address >= 0xE000) + // return _kernalRom.Read(address); + //else + return _ram.Read(address); + } + + public void Write(ushort address, byte value) + { + //if (address >= 0xD000 && address <= 0xDFFF) + // _io.Write(address, value); + //else if (address < 0xA000 || (address >= 0xC000 && address < 0xD000)) + _ram.Write(address, value); + // Writes to ROM are ignored + } +} diff --git a/CPU_emu/MemoryBus/RamBus.cs b/CPU_emu/MemoryBus/RamBus.cs new file mode 100644 index 0000000..dc4b3ae --- /dev/null +++ b/CPU_emu/MemoryBus/RamBus.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CPU_emu; + +public class RamBus : IMemoryBus +{ + private readonly byte[] _memory; + + public int RamSize + { + get { return _memory.Length; } + } + + public RamBus(byte[] memory) + { + _memory = memory; + } + + /// + /// Read data from random address + /// + /// address to read + /// byte + public byte Read(ushort address) + { + return _memory[address]; + } + + /// + /// Write data to random address + /// + /// Address to store data + /// data + public void Write(ushort address, byte value) + { + _memory[address] = value; + } + + /// + /// Load a chunk of data into RAM + /// + /// Startaddress + /// data byte[] + public void Load(ushort address, params byte[] data) + { + Array.Copy(data, 0, _memory, address, data.Length); + } + + /// + /// Reada range of RAM + /// + /// Startaddress + /// Lenght of data + /// byte[] + public byte[] Dump(ushort address, int length) + { + return _memory.Skip(address).Take(length).ToArray(); + } + + +} diff --git a/CPU_emu/MemoryBus/RomBus.cs b/CPU_emu/MemoryBus/RomBus.cs new file mode 100644 index 0000000..8951f16 --- /dev/null +++ b/CPU_emu/MemoryBus/RomBus.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CPU_emu; + +public class RomBus : IMemoryBus +{ + + private readonly byte[] _rom; + private readonly ushort _base; + + public RomBus(byte[] rom, ushort baseAddress = 0) + { + _rom = rom; + _base = baseAddress; + } + + /// + /// + /// + /// Start dump at this address + /// Dump 'length' bytes + /// byte[] + public byte[] Dump(ushort address, int length) + { + return _rom.Skip(address).Take(length).ToArray(); + } + + public void Load(ushort address, params byte[] data) + { + // No load. It's a ROM + } + + /// + /// + /// + /// Address t read + /// byte + public byte Read(ushort address) + { + return _rom[address - _base]; + } + + public void Write(ushort address, byte value) + { + // No write. It's am ROM ;-) + } +} diff --git a/CPU_emuTests/CPU_Tests.cs b/CPU_emuTests/CPU_Tests.cs index 8745d51..d15da71 100644 --- a/CPU_emuTests/CPU_Tests.cs +++ b/CPU_emuTests/CPU_Tests.cs @@ -1,6 +1,10 @@ +using CPU_emu; using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.DataCollection; +using NuGet.Frameworks; using System.Reflection; +using System.Reflection.Emit; + namespace CPU_emulator; @@ -9,13 +13,30 @@ namespace CPU_emulator; public class CPU_Tests { private CPU cpu; - + private RamBus ram; + private RomBus basicRom; + private RomBus kernalRom; + private IoDevice ioDevice; + private byte[] _data = new byte[65536]; + [SetUp] public void Setup() { - cpu = new CPU(); + InitializeMemory(); + MappedBus memoryBus = new MappedBus(ram, basicRom, kernalRom, ioDevice); + cpu = new CPU(memoryBus); } - + + private void InitializeMemory() + { + ram = new RamBus(_data); + //var basicRom = new RomBus(File.ReadAllBytes("basic.rom"), 0xA000); + //var kernalRom = new RomBus(File.ReadAllBytes("kernal.rom"), 0xE000); + basicRom = new RomBus(new byte[1024 * 8], 0xA000); + kernalRom = new RomBus(new byte[1024 * 8], 0xE000); + ioDevice = new IoDevice(); + } + #region set registers [Test] @@ -321,76 +342,127 @@ public void DecrementSP_Test() public class CPU_Command_Tests { private CPU cpu; + private RamBus ram; + private RomBus basicRom; + private RomBus kernalRom; + private IoDevice ioDevice; + private byte[] _data = new byte[65536]; [SetUp] - public void SetUp() + public void Setup() { - cpu = new CPU(); + InitializeMemory(); + MappedBus memoryBus = new MappedBus(ram, basicRom, kernalRom, ioDevice); + cpu = new CPU(memoryBus); } + private void InitializeMemory() + { + ram = new RamBus(_data); + //var basicRom = new RomBus(File.ReadAllBytes("basic.rom"), 0xA000); + //var kernalRom = new RomBus(File.ReadAllBytes("kernal.rom"), 0xE000); + basicRom = new RomBus(new byte[1024 * 8], 0xA000); + kernalRom = new RomBus(new byte[1024 * 8], 0xE000); + ioDevice = new IoDevice(); + } + + //public void RunSingleOpcodeTest(byte opcode, byte operand, ushort pcStart = 0x200) + //{ + // cpu.Reset(); + // cpu.WriteByteToMemory(operand, pcStart); + // cpu.SetPC(pcStart); + + // TestHelper.GetPrivateMethod("CallInstruction", cpu).Invoke(cpu, new object[] { cpu.GetType(), opcode }); + //} + #region LDA + // Test Load Accumulator immidiate A9 - [TestCase(0xff, ExpectedResult = new object[] { false, true, 0xff, 3 })] - [TestCase(0x00, ExpectedResult = new object[] { true, false, 0x00, 3 })] - public object[] Cmd_A9_Test(byte b) + [TestCase(0xff, false, true, 0xff, 3)] + [TestCase(0x00, true, false, 0x00, 3)] + public void Cmd_A9_Test(byte value, bool expectedZ, bool expectedN, byte expectedA, int expectedCycles) { - object[] result = new object[4]; - - cpu.WriteByteToMemory(b, 0x200); + cpu.WriteByteToMemory(value, 0x200); cpu.SetPC(0x200); - - TestHelper.GetPrivateMethod("CallInstruction", cpu).Invoke(cpu, new object[] { cpu.GetType() , (byte)0xA9 }); - - result[0] = cpu.flags["Z"]; - result[1] = cpu.flags["N"]; - result[2] = cpu.A; - result[3] = cpu.CpuCycle; - return result; + TestHelper.GetPrivateMethod("CallInstruction", cpu).Invoke(cpu, new object[] { cpu.GetType(), (byte)0xA9 }); + Assert.Multiple(() => + { + Assert.That(cpu.flags["Z"], Is.EqualTo(expectedZ), "Zero Flag"); + Assert.That(cpu.flags["N"], Is.EqualTo(expectedN), "Negative Flag"); + Assert.That(cpu.A, Is.EqualTo(expectedA), "Register A"); + Assert.That(cpu.CpuCycle, Is.EqualTo(expectedCycles), "CPU Cycles"); + }); } - + + // Test Load Accumulator zeropage A5 - [TestCase(0xff, ExpectedResult = new object[] { false, true, 0xff })] - [TestCase(0x00, ExpectedResult = new object[] { true, false, 0x00 })] - public object[] Cmd_A5_Test(byte b) + [TestCase(0xff, false, true, 0xff, 3)] + [TestCase(0x00, true, false, 0x00, 3)] + public void Cmd_A5_Test(byte value, bool expectedZ, bool expectedN, byte expectedA, int expectedCycles) { - object[] result = new object[3]; - - cpu.WriteByteToMemory(b, 0x01); // set byte on zeropage adr. 0x01 + cpu.WriteByteToMemory(value, 0x01); // set byte on zeropage adr. 0x01 cpu.WriteByteToMemory(0x01, 0x200); cpu.SetPC(0x200); - cpu.Cmd_A5(); + TestHelper.GetPrivateMethod("CallInstruction", cpu).Invoke(cpu, new object[] { cpu.GetType(), (byte)0xA5 }); + + Assert.Multiple(() => + { + Assert.That(cpu.flags["Z"], Is.EqualTo(expectedZ), "Zero Flag"); + Assert.That(cpu.flags["N"], Is.EqualTo(expectedN), "Negative Flag"); + Assert.That(cpu.A, Is.EqualTo(expectedA), "Register A"); + Assert.That(cpu.CpuCycle, Is.EqualTo(expectedCycles), "CPU Cycles"); + }); - result[0] = cpu.flags["Z"]; - result[1] = cpu.flags["N"]; - result[2] = cpu.A; + } - return result; + // Load Accumulator zeropage X B5 + [TestCase(0xff, false, true, 0xff, 5)] + [TestCase(0x00, true, false, 0x00, 5)] + public void Cmd_B5_Test(byte value, bool expectedZ, bool expectedN, byte expectedA, int expectedCycles) + { + cpu.Reset(); + cpu.SetRegister("X",0x06); // load X with 0x06 + cpu.WriteByteToMemory(value, 0x07); // set byte on zeropage adr. 0x07 + cpu.WriteByteToMemory(0x01, 0x200); // set 0x01 on the next position of PC so value ox X (0x06) + 0x01 from instruction points to 0x07 + cpu.SetPC(0x200); + + TestHelper.GetPrivateMethod("CallInstruction", cpu).Invoke(cpu, new object[] { cpu.GetType(), (byte)0xB5 }); + Assert.Multiple(() => + { + Assert.That(cpu.flags["Z"], Is.EqualTo(expectedZ), "Zero Flag"); + Assert.That(cpu.flags["N"], Is.EqualTo(expectedN), "Negative Flag"); + Assert.That(cpu.A, Is.EqualTo(expectedA), "Register A"); + Assert.That(cpu.CpuCycle, Is.EqualTo(expectedCycles), "CPU Cycles"); + }); } - // Load Accumulator zeropage X B5 - [TestCase(0xff, ExpectedResult = new object[] { false, true, 0xff })] - [TestCase(0x00, ExpectedResult = new object[] { true, false, 0x00 })] - public object[] Cmd_B5_Test(byte b) + // Load Accumulator absolute AD + [TestCase(0xff, false, true, 0xff, 6)] + [TestCase(0x00, true, false, 0x00, 6)] + public void Cmd_AD_Test(byte value, bool expectedZ, bool expectedN, byte expectedA, int expectedCycles) { - object[] result = new object[3]; - cpu.SetRegister("X",0x06); - cpu.WriteByteToMemory(b, 0x07); // set byte on zeropage adr. 0x01 - cpu.WriteByteToMemory(0x01, 0x200); + cpu.Reset(); + cpu.WriteByteToMemory(value, 0xFFEE); + cpu.WriteByteToMemory(0xEE, 0x200); + cpu.WriteByteToMemory(0xFF, 0x201); cpu.SetPC(0x200); - cpu.Cmd_B5(); - result[0] = cpu.flags["Z"]; - result[1] = cpu.flags["N"]; - result[2] = cpu.A; + TestHelper.GetPrivateMethod("CallInstruction", cpu).Invoke(cpu, new object[] { cpu.GetType(), (byte)0xAD }); - return result; + Assert.Multiple(() => + { + Assert.That(cpu.flags["Z"], Is.EqualTo(expectedZ), "Zero Flag"); + Assert.That(cpu.flags["N"], Is.EqualTo(expectedN), "Negative Flag"); + Assert.That(cpu.A, Is.EqualTo(expectedA), "Register A"); + Assert.That(cpu.CpuCycle, Is.EqualTo(expectedCycles), "CPU Cycles"); + }); } - #endregion + #endregion #region LDX @@ -569,4 +641,5 @@ public static MethodInfo GetPrivateMethod(string methodName, object objectUnderT return method; } + } \ No newline at end of file