public void Can_Change_Segment_Memory_Bank_By_Setting_Special_Memory_Location() { // Arrange var mem = new Memory(enableBankSwitching: true); // Add a new bank to segment 1 with blank RAM. Segment 1 starts at 8192 (0x2000). // Adding a new bank does not change current bank number (which will still be 0) mem.AddMemorySegmentBank(1); // Fill some data in to segment 1 in current bank number (0). mem[0x2000] = 0x42; mem[0x2001] = 0x21; // Load machine code into memory that will switch segment 1 bank from 0 to 1 ushort codeAddress = 0xc000; ushort codeInsAddress = codeAddress; // Prepare memory address 0x02 with memory segment bank number to use. Writing to 0x02 will not actually do the change. mem[codeInsAddress++] = (byte)OpCodeId.LDA_I; // LDA (Load Accumulator) mem[codeInsAddress++] = 0x01; // |-Value: The memory segment bank number to put in actual memory mem[codeInsAddress++] = (byte)OpCodeId.STA_ZP; // STA (Store Accumulator) mem[codeInsAddress++] = 0x02; // |-ZeroPage address $0002 // Write the segment number to address 0x01, which will trigger the bank number specified in 0x01 to be loaded in to the segment number written to 0x01. mem[codeInsAddress++] = (byte)OpCodeId.LDA_I; // LDA (Load Accumulator) mem[codeInsAddress++] = 0x01; // |-Value: The memory segment number to change. mem[codeInsAddress++] = (byte)OpCodeId.STA_ZP; // STA (Store Accumulator) mem[codeInsAddress++] = 0x01; // |-ZeroPage address $0001 mem[codeInsAddress++] = 0x00; // BRK (Break/Force Interrupt) - emulator configured to stop execution when reaching this instruction // Initialize emulator with CPU, memory, and execution parameters var computerBuilder = new ComputerBuilder(); computerBuilder .WithCPU() .WithStartAddress(codeAddress) .WithMemory(mem) .WithInstructionExecutedEventHandler( (s, e) => Debug.WriteLine(OutputGen.GetLastInstructionDisassembly(e.CPU, e.Mem))) .WithExecOptions(options => { options.ExecuteUntilInstruction = OpCodeId.BRK; // Emulator will stop executing when a BRK instruction is reached. }); var computer = computerBuilder.Build(); // Act computer.Run(); // Assert // Check that segment 1 now has changed bank number to 1. Assert.Equal(1, mem.MemorySegments[1].CurrentBankNumber); // Check that content of the memory in segment 1 now is blank (bank 1 was blank when we added it) Assert.Equal(0x00, mem[0x2000]); Assert.Equal(0x00, mem[0x2001]); }
public void ShouldBuildEmptyComputer() { IComputerBuilder builder = new ComputerBuilder(); var computer = builder.Build(); Assert.Null(computer.HardDrive); Assert.Null(computer.Motherboard); Assert.Null(computer.Cpu); Assert.Null(computer.Memory); Assert.Null(computer.GraphicsCard); Assert.Null(computer.Case); }
public void SetComputerNameWithoutServices() { var builder = new ComputerBuilder(); builder.SetName("computer"); var comp = builder.Build(); var expected = "computer"; var actual = comp.Name; Assert.Equal(expected, actual); }
private Computer SetupEmulator(EmulatorConfig emulatorConfig) { Debug.WriteLine($"Loading 6502 machine code binary file."); Debug.WriteLine($"{emulatorConfig.ProgramBinaryFile}"); if (!File.Exists(emulatorConfig.ProgramBinaryFile)) { Debug.WriteLine($"File does not exist."); throw new Exception($"Cannot find 6502 binary file: {emulatorConfig.ProgramBinaryFile}"); } var enableBankSwitching = emulatorConfig.Memory.MemoryBanks.EnableMemoryBanks; var mem = new Memory(enableBankSwitching: enableBankSwitching); if (enableBankSwitching) { // Add additional memory banks for memory segment 1 (0x2000) and up (segment 0 cannot have multiple banks) for (byte memorySegmentNumber = 1; memorySegmentNumber < mem.MemorySegments.Count; memorySegmentNumber++) { // By default each segment has one bank when Memory is created above. // Thus we add the specified BanksPerSegment-1 new banks to each segment. for (int i = 0; i < emulatorConfig.Memory.MemoryBanks.BanksPerSegment - 1; i++) { // Add additional memory banks for segment. Memory in those will be blank (0x00). mem.AddMemorySegmentBank(memorySegmentNumber); } } } BinaryLoader.Load( mem, emulatorConfig.ProgramBinaryFile, out ushort loadedAtAddress, out int fileLength); // Initialize emulator with CPU, memory, and execution parameters var computerBuilder = new ComputerBuilder(); computerBuilder .WithCPU() .WithStartAddress(loadedAtAddress) .WithMemory(mem) .WithExecOptions(options => { // Emulator will stop executing when a BRK instruction is reached. options.ExecuteUntilInstruction = emulatorConfig.StopAtBRK?OpCodeId.BRK:null; }); var computer = computerBuilder.Build(); return(computer); }
public void AddCase_ValidCase_ReturnsComputerWithACase() { // Arrange ComputerBuilder computerBuilder = new ComputerBuilder(); // Act Case myCase = new Case(15, 15, 15, 2, 2); computerBuilder.AddCase(myCase); var builder = computerBuilder.Build(); // Assert Assert.AreEqual(builder.PCCase, myCase); }
public static TestContext NewTestContext(ushort startPos = 0x1000, uint memorySize = 1024 *64) { var builder = new ComputerBuilder() .WithCPU() .WithMemory(memorySize); var computer = builder.Build(); var computerCopy = computer.Clone(); return(new TestContext { Computer = computer, OriginalComputer = computerCopy }); }
public Mon() { var mem = new Memory(); var computerBuilder = new ComputerBuilder(); computerBuilder .WithCPU() //.WithStartAddress() .WithMemory(mem) .WithInstructionExecutedEventHandler( (s, e) => Debug.WriteLine(OutputGen.GetLastInstructionDisassembly(e.CPU, e.Mem))); // .WithExecOptions(options => // { // }); Computer = computerBuilder.Build(); }
public void Computer_Can_Be_Reset_And_Restart_At_ResetVector() { // Arrange var builder = new ComputerBuilder() .WithCPU() .WithMemory(1024 * 64); var computer = builder.Build(); // Act computer.Reset(); // Assert Assert.Equal(computer.Mem[CPU.ResetVector], computer.CPU.PC); // Not sure if the CPU hardware will have SP set to 0xff on power on, or if there is code in the reset vector in ROM that does this. // Assert.Equal(0xff, cpu.SP); }
public void ShouldBuildFullComputerWithBuilder() { IComputerBuilder builder = new ComputerBuilder(); builder.SetHardDrive(12.3, "SSD", 15, 22); builder.SetMotherBoard(4, 500, 3, "ATX", 4); builder.SetCpu(1200, "AMD", "AM4", 500, 6); builder.SetMemory(2000, 2000, "DDR4", 16); builder.SetGraphicsCard(2, 2000, 1400, 4); builder.SetCase(100, 30, 100, 4, 4); var computer = builder.Build(); Assert.NotNull(computer.HardDrive); Assert.NotNull(computer.Motherboard); Assert.NotNull(computer.Cpu); Assert.NotNull(computer.Memory); Assert.NotNull(computer.GraphicsCard); Assert.NotNull(computer.Case); }
public static void Run() { Console.WriteLine($"--------------------------------------------------------"); Console.WriteLine($"Run 6502 code that multiplies two 16 bit signed numbers."); Console.WriteLine($"--------------------------------------------------------"); string prgFileName = "../.cache/ConsoleTestPrograms/AssemblerSource/multiply_2_16bit_numbers.prg"; Console.WriteLine(""); Console.WriteLine($"Loading binary into emulator memory..."); var mem = BinaryLoader.Load( prgFileName, out ushort loadedAtAddress); Console.WriteLine($"Loading done."); Console.WriteLine(""); Console.WriteLine($"Data & code load address: {loadedAtAddress.ToHex(), 10} ({loadedAtAddress})"); Console.WriteLine($"Code start address: {loadedAtAddress.ToHex(), 10} ({loadedAtAddress})"); ushort valA = 1337; ushort valB = 42; Console.WriteLine(""); Console.WriteLine($"Multiply {valA.ToDecimalAndHex()} with {valB.ToDecimalAndHex()}"); ushort sourceAddressA = 0xd000; ushort sourceAddressB = 0xd002; ushort resultAddress = 0xd004; mem[(ushort)(sourceAddressA + 0)] = valA.Lowbyte(); mem[(ushort)(sourceAddressA + 1)] = valA.Highbyte(); mem[(ushort)(sourceAddressB + 0)] = valB.Lowbyte(); mem[(ushort)(sourceAddressB + 1)] = valB.Highbyte(); Console.WriteLine(""); Console.WriteLine($"Value A set memory location {sourceAddressA.ToHex()}"); Console.WriteLine($"{((ushort)(sourceAddressA + 0)).ToHex()} : {mem[(ushort)(sourceAddressA + 0)].ToHex()}"); Console.WriteLine($"{((ushort)(sourceAddressA + 1)).ToHex()} : {mem[(ushort)(sourceAddressA + 1)].ToHex()}"); Console.WriteLine($"Value B set memory location {sourceAddressB.ToHex()}"); Console.WriteLine($"{((ushort)(sourceAddressB + 0)).ToHex()} : {mem[(ushort)(sourceAddressB + 0)].ToHex()}"); Console.WriteLine($"{((ushort)(sourceAddressB + 1)).ToHex()} : {mem[(ushort)(sourceAddressB + 1)].ToHex()}"); // Initialize CPU, set PC to start position var computerBuilder = new ComputerBuilder(); computerBuilder .WithCPU() .WithStartAddress(loadedAtAddress) .WithMemory(mem) .WithInstructionExecutedEventHandler( (s, e) => Console.WriteLine($"{e.CPU.PC.ToHex()}: {e.CPU.ExecState.LastOpCode.Value.ToOpCodeId()}")) .WithExecOptions(options => { options.ExecuteUntilInstruction = OpCodeId.BRK; }); var computer = computerBuilder.Build(); Console.WriteLine(""); Console.WriteLine("Running 6502 multiplication routine..."); computer.Run(); Console.WriteLine(""); Console.WriteLine("Done."); Console.WriteLine(""); Console.WriteLine($"Result stored at {resultAddress.ToHex()}:"); Console.WriteLine($"{((ushort)(resultAddress + 0)).ToHex()} : {mem[(ushort)(resultAddress + 0)].ToHex()}"); Console.WriteLine($"{((ushort)(resultAddress + 1)).ToHex()} : {mem[(ushort)(resultAddress + 1)].ToHex()}"); Console.WriteLine(""); ushort result = mem.FetchWord(resultAddress); Console.WriteLine($"Result:"); Console.WriteLine($"{valA.ToDecimalAndHex()} * {valB.ToDecimalAndHex()} = {result.ToDecimalAndHex()}"); }
public override void Run() { var data = (DataModel)Data; var cb = new ComputerBuilder(); var amps = new List <Computer>(); for (int i = 0; i < 5; i++) { amps.Add(cb.Build()); } var phasePermutations = data.PhaseSettings.Permutations(); var maxThrusterSignal = 0; var withPS = new[] { 0 }; foreach (var phaseSetting in phasePermutations) { foreach (var amp in amps) { amp.State = new ComputerState { Memory = (int[])data.Program.Clone() }; } var prevI = new[] { 4, 0, 1, 2, 3 }; var ps = phaseSetting.ToArray(); var iteration = 0; while (iteration > -1) { for (int i = 0; i < ps.Length; i++) { if (iteration == 0) { amps[i].State.Inputs.Add(ps[i]); } amps[i].State.Inputs.Add(amps[prevI[i]].State.Output.LastOrDefault()); amps[i].State.Halt = false; amps[i].Run(); } if (data.FeedbackMode && amps.Last().State.AwaitingInput) { iteration++; } else { iteration = -1; } } var signal = amps[4].State.Output.Last(); if (signal > maxThrusterSignal) { maxThrusterSignal = signal; withPS = ps; } } Result.Add(ThrusterSignalKey, maxThrusterSignal); Result.Add(PhaseSettingKey, withPS); }
public static void Run() { // Test program // - adds values from two memory location // - divides it by 2 (rotate right one bit position) // - stores it in another memory location // Load input data into memory byte value1 = 12; byte value2 = 30; ushort value1Address = 0xd000; ushort value2Address = 0xd001; ushort resultAddress = 0xd002; var mem = new Memory(); mem[value1Address] = value1; mem[value2Address] = value2; // Load machine code into memory ushort codeAddress = 0xc000; ushort codeInsAddress = codeAddress; mem[codeInsAddress++] = 0xad; // LDA (Load Accumulator) mem[codeInsAddress++] = 0x00; // |-Lowbyte of $d000 mem[codeInsAddress++] = 0xd0; // |-Highbyte of $d000 mem[codeInsAddress++] = 0x18; // CLC (Clear Carry flag) mem[codeInsAddress++] = 0x6d; // ADC (Add with Carry, adds memory to accumulator) mem[codeInsAddress++] = 0x01; // |-Lowbyte of $d001 mem[codeInsAddress++] = 0xd0; // |-Highbyte of $d001 mem[codeInsAddress++] = 0x6a; // ROR (Rotate Right, rotates accumulator right one bit position) mem[codeInsAddress++] = 0x8d; // STA (Store Accumulator, store to accumulator to memory) mem[codeInsAddress++] = 0x02; // |-Lowbyte of $d002 mem[codeInsAddress++] = 0xd0; // |-Highbyte of $d002 mem[codeInsAddress++] = 0x00; // BRK (Break/Force Interrupt) - emulator configured to stop execution when reaching this instruction // Initialize emulator with CPU, memory, and execution parameters var computerBuilder = new ComputerBuilder(); computerBuilder .WithCPU() .WithStartAddress(codeAddress) .WithMemory(mem) .WithInstructionExecutedEventHandler( (s, e) => Console.WriteLine(OutputGen.GetLastInstructionDisassembly(e.CPU, e.Mem))) .WithExecOptions(options => { options.ExecuteUntilInstruction = OpCodeId.BRK; // Emulator will stop executing when a BRK instruction is reached. }); var computer = computerBuilder.Build(); // Run program computer.Run(); Console.WriteLine($"Execution stopped"); Console.WriteLine($"CPU state: {OutputGen.GetProcessorState(computer.CPU)}"); Console.WriteLine($"Stats: {computer.CPU.ExecState.InstructionsExecutionCount} instruction(s) processed, and used {computer.CPU.ExecState.CyclesConsumed} cycles."); // Print result byte result = mem[resultAddress]; Console.WriteLine($"Result: ({value1} + {value2}) / 2 = {result}"); }
public void Run() { Console.WriteLine($"------------------------------------------------------------------"); Console.WriteLine($"Run 6502 functional test program with decimal tests disabled"); Console.WriteLine("Functional test downloaded and compiled from:"); Console.WriteLine($"------------------------------------------------------------------"); Console.WriteLine($"Downloading functional test source code from"); Console.WriteLine("https://github.com/Klaus2m5/6502_65C02_functional_tests"); Console.WriteLine($"Modifying it to set source code line 'disable_decimal = 0' to 'disable_decimal = 1'"); Console.WriteLine($"And compiling it to a binary that can be loaded into the 6502 emulator..."); // Set the download directory to the same directory where current application runs in. var currentAssemblyLocation = System.Reflection.Assembly.GetEntryAssembly().Location; var downloadDir = System.IO.Path.GetDirectoryName(currentAssemblyLocation); var functionalTestBinary = _functionalTestCompiler.Get6502FunctionalTestBinary( disableDecimalTests: true, downloadDir: downloadDir ); Console.WriteLine($"Download and compilation complete."); Console.WriteLine($"Binary location (as well as .lst file):"); Console.WriteLine($"{functionalTestBinary}"); // There is no 2 byte header in the 6502_functional_test.bin file. // It's supposed to be loaded to memory at 0x0000, and started at 0x0400 Console.WriteLine(""); Console.WriteLine($"Loading binary into emulator memory..."); ushort loadAddress = 0x000A; ushort startAddress = 0x0400; var mem = BinaryLoader.Load( functionalTestBinary, out ushort loadedAtAddress, out int fileLength, forceLoadAddress: loadAddress); Console.WriteLine($"Loading done."); // The rest of the bytes are considered the code Console.WriteLine(""); Console.WriteLine($"Data & code load address: {loadAddress.ToHex(), 10} ({loadAddress})"); Console.WriteLine($"Code+data length (bytes): 0x{fileLength, -8:X8} ({fileLength})"); Console.WriteLine($"Code start address: {startAddress.ToHex(), 10} ({startAddress})"); //Console.WriteLine("Press Enter to start"); //Console.ReadLine(); // Initialize CPU, set PC to start position var computerBuilder = new ComputerBuilder(); computerBuilder .WithCPU() .WithStartAddress(0x400) .WithMemory(mem) //.WithInstructionAboutToBeExecutedEventHandler(OnInstructionToBeExecuted) .WithInstructionExecutedEventHandler(OnInstructionExecuted) .WithUnknownInstructionEventHandler(OnUnknownOpCodeDetected) .WithExecOptions(options => { options.ExecuteUntilExecutedInstructionAtPC = 0x336d; // A successful run has about 26765880 instructions (the version that was run 2021-02-06, that may change) // We increase to almost double, and will exit if not finished then. options.MaxNumberOfInstructions = 50000000; options.UnknownInstructionThrowsException = false; }); var computer = computerBuilder.Build(); Console.WriteLine(""); Console.WriteLine($"If test logic succeeds, the test program will reach a specific memory location: {computer.DefaultExecOptions.ExecuteUntilExecutedInstructionAtPC.Value.ToHex()}, and the emulator will then stop processing."); Console.WriteLine($"If test logic fails, the test program will loop forever at the location the error was found. The emulator will try executing a maximum #instructions {computer.DefaultExecOptions.MaxNumberOfInstructions.Value} before giving up."); Console.WriteLine($"If unknown opcode is found, it's logged and ignored, and processing continues on next instruction."); // Execute program Console.WriteLine(""); Console.WriteLine("Starting code execution..."); computer.Run(); Console.WriteLine(""); Console.WriteLine("Code execution done."); var cpu = computer.CPU; var execState = cpu.ExecState; Console.WriteLine(""); Console.WriteLine($"Last instruction: {OutputGen.BuildInstructionString(computer.CPU, computer.Mem, computer.CPU.ExecState.PCBeforeLastOpCodeExecuted.Value)}"); Console.WriteLine($"CPU state: {OutputGen.GetProcessorState(computer.CPU)}"); Console.WriteLine($"Total # CPU instructions executed: {execState.InstructionsExecutionCount}"); Console.WriteLine($"Total # CPU cycles consumed: {execState.CyclesConsumed}"); Console.WriteLine(""); // Evaluate success/failure if (cpu.PC == computer.DefaultExecOptions.ExecuteUntilExecutedInstructionAtPC.Value) { Console.WriteLine($"Success!"); Console.WriteLine($"PC reached expected success memory location: {computer.DefaultExecOptions.ExecuteUntilExecutedInstructionAtPC.Value.ToHex()}"); } else { Console.WriteLine($"Probably failure"); Console.WriteLine($"The emulator executer a maximum #instructions {computer.DefaultExecOptions.MaxNumberOfInstructions.Value}, and did not manage to get PC to the configured success location: {computer.DefaultExecOptions.ExecuteUntilExecutedInstructionAtPC.Value.ToHex()}"); Console.WriteLine($"The functional test program would end in a forever-loop on the same memory location if it fails."); Console.WriteLine($"Verify the last PC location against the functional test program's .lst file to find out which logic test failed."); } }
public static void Run() { Console.Clear(); string prgFileName = "../../.cache/Examples/ConsoleTestPrograms/AssemblerSource/hostinteraction_scroll_text.prg"; Console.WriteLine($"Loading 6502 machine code binary file."); Console.WriteLine($"{prgFileName}"); if (!File.Exists(prgFileName)) { Console.WriteLine($"File does not exist."); return; } var mem = BinaryLoader.Load( prgFileName, out ushort loadedAtAddress, out int fileLength); // Initialize emulator with CPU, memory, and execution parameters var computerBuilder = new ComputerBuilder(); computerBuilder .WithCPU() .WithStartAddress(loadedAtAddress) .WithMemory(mem) // .WithInstructionExecutedEventHandler( // (s, e) => Console.WriteLine(OutputGen.GetLastInstructionDisassembly(e.CPU, e.Mem))) .WithExecOptions(options => { options.ExecuteUntilInstruction = OpCodeId.BRK; // Emulator will stop executing when a BRK instruction is reached. }); var computer = computerBuilder.Build(); // The shared memory location in the emulator that the 6502 program writes to to update screen. // 80 columns and 25 rows, 1 byte per character = 2000 (0x07d0) bytes const ushort SCREEN_MEM = 0x1000; const int SCREEN_MEM_COLS = 80; // const int SCREEN_MEM_ROWS = 25; const ushort SCREEN_REFRESH_STATUS = 0xf000; const int SCREEN_REFRESH_STATUS_HOST_REFRESH_BIT = 0; const int SCREEN_REFRESH_STATUS_EMULATOR_DONE_BIT = 1; Console.WriteLine(""); Console.WriteLine("Screen being updated indirectly by 6502 machine code running emulator!"); Console.WriteLine(""); bool cont = true; while (cont) { // Set emulator Refresh bit (maybe controlled by host frame counter in future?) // Emulator will wait until this bit is set until "redrawing" new data into memory mem.SetBit(SCREEN_REFRESH_STATUS, SCREEN_REFRESH_STATUS_HOST_REFRESH_BIT); bool shouldExecuteEmulator = true; while (shouldExecuteEmulator) { // Execute a number of instructions computer.Run(new ExecOptions { MaxNumberOfInstructions = 10 }); shouldExecuteEmulator = !mem.IsBitSet(SCREEN_REFRESH_STATUS, SCREEN_REFRESH_STATUS_EMULATOR_DONE_BIT); } RenderRow(mem, SCREEN_MEM, SCREEN_MEM_COLS); //RenderScreen(mem, SCREEN_MEM, SCREEN_MEM_COLS, SCREEN_MEM_ROWS); // Clear the flag that the emulator set to indicate it's done. mem.ClearBit(SCREEN_REFRESH_STATUS, SCREEN_REFRESH_STATUS_EMULATOR_DONE_BIT); bool shouldExecuteHost = true; while (shouldExecuteHost) { Thread.Sleep(80); // Control speed of screen update shouldExecuteHost = false; } } Console.WriteLine($"Execution stopped"); Console.WriteLine($"CPU state: {OutputGen.GetProcessorState(computer.CPU)}"); Console.WriteLine($"Stats: {computer.CPU.ExecState.InstructionsExecutionCount} instruction(s) processed, and used {computer.CPU.ExecState.CyclesConsumed} cycles."); }
public void Can_Run_6502_Functional_Test_Program_With_Decimal_Mode_Disabled_Successfully() { // Arrange var functionalTestCompiler = new FunctionalTestCompiler(NullLogger <FunctionalTestCompiler> .Instance); var functionalTestBinary = functionalTestCompiler.Get6502FunctionalTestBinary(disableDecimalTests: true); // There is no 2 byte header in the 6502_functional_test.bin file. // It's supposed to be loaded to memory at 0x0000, and started at 0x0400 ushort loadAddress = 0x000A; ushort startAddress = 0x0400; var mem = BinaryLoader.Load( functionalTestBinary, out ushort loadedAtAddress, out int fileLength, forceLoadAddress: loadAddress); _output.WriteLine($"Data & code load address: {loadAddress.ToHex(), 10} ({loadAddress})"); _output.WriteLine($"Code+data length (bytes): 0x{fileLength, -8:X8} ({fileLength})"); _output.WriteLine($"Code start address: {startAddress.ToHex(), 10} ({startAddress})"); var computerBuilder = new ComputerBuilder(); computerBuilder .WithCPU() .WithStartAddress(startAddress) .WithMemory(mem) .WithExecOptions(options => { // A successful run has about 26765880 instructions (the version that was run 2021-02-06, that may change) // We increase to almost double, and will exit if not finished then. options.MaxNumberOfInstructions = 50000000; options.ExecuteUntilExecutedInstructionAtPC = 0x336d; options.UnknownInstructionThrowsException = false; }); var computer = computerBuilder.Build(); var execOptions = computer.DefaultExecOptions; _output.WriteLine($"If test logic succeeds, the test program will reach a specific memory location: {execOptions.ExecuteUntilExecutedInstructionAtPC.Value.ToHex()}, and the emulator will then stop processing."); _output.WriteLine($"If test logic fails, the test program will loop forever at the location the error was found. The emulator will try executing a maximum #instructions {execOptions.MaxNumberOfInstructions.Value} before giving up."); _output.WriteLine($"If unknown opcode is found, it's logged and ignored, and processing continues on next instruction."); // Act computer.Run(); // Assert var cpu = computer.CPU; var execState = cpu.ExecState; _output.WriteLine($"CPU last PC: {cpu.PC.ToHex()}"); _output.WriteLine($"CPU last opcode: {execState.LastOpCode.Value.ToOpCodeId()} ({execState.LastOpCode.Value.ToHex()})"); _output.WriteLine($"Total # CPU instructions executed: {execState.InstructionsExecutionCount}"); _output.WriteLine($"Total # CPU cycles consumed: {execState.CyclesConsumed}"); if (cpu.PC == computer.DefaultExecOptions.ExecuteUntilExecutedInstructionAtPC.Value) { _output.WriteLine($"Success. PC reached expected success memory location: {computer.DefaultExecOptions.ExecuteUntilExecutedInstructionAtPC.Value.ToHex()}"); Assert.True(true); } else { _output.WriteLine($"Probably failure. The emulator executer a maximum #instructions {computer.DefaultExecOptions.MaxNumberOfInstructions.Value}, and did not manage to get PC to the configured success location: {computer.DefaultExecOptions.ExecuteUntilExecutedInstructionAtPC.Value.ToHex()}"); _output.WriteLine($"The functional test program would end in a forever-loop on the same memory location if it fails."); _output.WriteLine($"Verify the last PC location against the functional test program's .lst file to find out which logic test failed."); Assert.True(false); } }