private void LogInstructionToDebugger(S8Instruction instr) { if (VerboseMode) { var regs = GetChangedRegs(); if (!string.IsNullOrEmpty(regs)) { LogMessage(regs); } instr.DecodeInstruction(); LogMessage(instr.Instruction2Text(state.pc, true)); prevRegs = (byte[])state.regs.Clone(); } }
public UInt16 Dissasemble(UInt16 start, UInt16 length, bool showAddress = false, bool allowOutsideLoadedMemory = false) { S8Instruction s8i; UInt16 currentAddress = start; UInt16 endAddress = (UInt16)(currentAddress + length); // Special - if no length givien, assume 20 instructions if (length == 0) { endAddress = (UInt16)(currentAddress + 20); //endAddress = cpu.state.memoryUsed; } if (!allowOutsideLoadedMemory) { if (endAddress > cpu.state.memoryUsed) { endAddress = cpu.state.memoryUsed; } } // Dont read outside physical memory if (endAddress > bytes.Length) { endAddress = (UInt16)(bytes.Length - 1); } while (currentAddress < endAddress) { int lineAddress = currentAddress; byte opcode = bytes[currentAddress++]; byte param = bytes[currentAddress++]; s8i = new S8Instruction(opcode, param); s8i.DecodeInstruction(); LogMessage(s8i.Instruction2Text(lineAddress, showAddress)); } return(currentAddress); }
public bool ExecuteInstruction(S8Instruction instr) { LogInstructionToDebugger(instr); // STOPP if (instr.operationClass == 0x0) { return(false); } // increase counter if we do anything else than STOPP state.pc += 2; // SETT if (instr.operationClass == 0x1) { state.regs[instr.operation] = (byte)instr.value; } else if (instr.operationClass == 0x2) { state.regs[instr.operation] = state.regs[instr.argument1]; } // FINN else if (instr.operationClass == 0x3) { state.regs[1] = (byte)((instr.address & 0x0f00) >> 8); state.regs[0] = (byte)(instr.address & 0xff); } // LOAD / STORE else if (instr.operationClass == 0x4) { int addr = ((state.regs[1] << 8) | state.regs[0]) & 0xfff; switch (instr.operation) { case 0: // LAST state.regs[instr.argument1] = state.memory[addr]; break; case 1: //LAGR state.memory[addr] = (byte)state.regs[instr.argument1]; break; #if _EXPERIMENTAL_ case 2: //VLAST state.regs[instr.argument1] = this.HWDisplay.Memory[GetFrameBufferAddress()]; break; case 3: //VLAGR UInt16 hwaddr = GetFrameBufferAddress(); this.HWDisplay.Write(hwaddr, (byte)state.regs[instr.argument1]); break; #endif default: LogMessage(ERROR_MESSAGE[(int)ERROR_MESSAGE_ID.segmentationFault]); return(false); } } // ALU else if (instr.operationClass == 0x5) { byte reg1 = state.regs[instr.argument1]; byte reg2 = state.regs[instr.argument2]; if (instr.operation == 0x0) { state.regs[instr.argument1] &= reg2; } else if (instr.operation == 0x1) { state.regs[instr.argument1] |= reg2; } else if (instr.operation == 0x2) { state.regs[instr.argument1] ^= reg2; } else if (instr.operation == 0x3) { state.regs[instr.argument1] = (byte)((reg1 << reg2) & 0xff); } else if (instr.operation == 0x4) { state.regs[instr.argument1] >>= reg2; } else if (instr.operation == 0x5) { state.regs[instr.argument1] = (byte)((reg1 + reg2) & 0xff); } else if (instr.operation == 0x6) { state.regs[instr.argument1] = (byte)((reg1 - reg2) & 0xff); } else { LogMessage(ERROR_MESSAGE[(int)ERROR_MESSAGE_ID.segmentationFault]); return(false); } } // I/O else if (instr.operationClass == 0x6) { switch (instr.operation) { case 0x0: // LES if (state.stdin.Length > state.inputPtr) { state.regs[instr.argument1] = state.stdin[state.inputPtr++]; } else { LogMessage(ERROR_MESSAGE[(int)ERROR_MESSAGE_ID.readAfterEndOfInput]); return(false); } break; case 0x01: // SKRIV { // This limit broke the runner for the NPST 2021 day 7. // It was put in as a security measure, but it created just pain :-( // Removed!!! //if (state.outputStream.Length > 1000) //{ // LogMessage(ERROR_MESSAGE[(int)ERROR_MESSAGE_ID.segmentationFault]); // return false; //} byte val = state.regs[instr.argument1]; //state.stdout += val.ToString("X2"); state.outputStream.Seek(0, SeekOrigin.End); state.outputStream.WriteByte(val); } break; #if _EXPERIMENTAL_ case 0x02: //INN state.regs[instr.argument1] = this.HWIO.ReadIO(GetIOAddress()); break; case 0x03: //UT this.HWIO.WriteIO(GetIOAddress(), state.regs[instr.argument1]); break; case 0x04: //VSYNK this.HWDisplay.VSync(); break; #endif default: { LogMessage(ERROR_MESSAGE[(int)ERROR_MESSAGE_ID.segmentationFault]); return(false); } } } // CMP else if (instr.operationClass == 0x7) { byte reg1 = state.regs[instr.argument1]; byte reg2 = state.regs[instr.argument2]; if (instr.operation == 0x0) { state.flag = reg1 == reg2; } else if (instr.operation == 0x1) { state.flag = reg1 != reg2; } else if (instr.operation == 0x2) { state.flag = reg1 < reg2; } else if (instr.operation == 0x3) { state.flag = reg1 <= reg2; } else if (instr.operation == 0x4) { state.flag = reg1 > reg2; } else if (instr.operation == 0x5) { state.flag = reg1 >= reg2; } else { LogMessage(ERROR_MESSAGE[(int)ERROR_MESSAGE_ID.segmentationFault]); return(false); } } // JMP else if (instr.operationClass == 0x8) { state.pc = instr.address; } // COND JMP else if (instr.operationClass == 0x9) { if (state.flag) { state.pc = instr.address; } } // CALL else if (instr.operationClass == 0xa) { if (!stack.Push(state.pc)) { LogMessage(ERROR_MESSAGE[(int)ERROR_MESSAGE_ID.recursionLimitExceeded]); return(false); } state.pc = instr.address; } // RET else if (instr.operationClass == 0xb) { state.pc = stack.Pop(); if (state.pc < 0) { state.pc = 0; LogMessage(ERROR_MESSAGE[(int)ERROR_MESSAGE_ID.segmentationFault]); return(false); } } else if (instr.operationClass == 0xc) { // No Op.. Do nothing } else { LogMessage(ERROR_MESSAGE[(int)ERROR_MESSAGE_ID.segmentationFault]); return(false); } return(true); }
/// <summary> /// Run the code "runSteps" number of cpu steps. /// </summary> /// <param name="runSteps">Number of CPU steps to run. 0 means no limit, runs until STOPP or crash</param> /// <param name="verbose"></param> /// <param name="showaddress"></param> /// <returns></returns> public bool RunSteps(int runSteps) { int stepsLeft = 1;// Default 1 step left if (runSteps > 0) { stepsLeft = runSteps; } if (state.memoryUsed == 0) { LogMessage("No s8 program loaded"); return(false); } if (state.memoryUsed == 0) { return(false); } while ((stepsLeft > 0) && (!state.crashed)) { // if there is a limit of how many steps we can run, reduce the steps left if (runSteps > 0) { stepsLeft--; } if (++state.tick > state.maxTicks) { var strErr = ERROR_MESSAGE[(int)ERROR_MESSAGE_ID.resourcesExhausted].Replace("${maxTicks}", state.tick.ToString()); LogMessage(strErr); return(false); } byte opcode = state.memory[state.pc]; byte param = state.memory[state.pc + 1]; //yield { pc, flag, regs, memory, stdout, inputPtr }; S8Instruction s8 = new S8Instruction(opcode, param); if (!ExecuteInstruction(s8)) { state.crashed = true; } // After each step in the CPU tell anyone listening to events about the step. Used to update UI CpuStepInfo(state.pc); if (state.breakpoints[state.pc]) { return(true); } } if (state.crashed) { return(false); } return(true); }