private string NameOfRegGP(RegGeneral register) { switch (register) { case RegGeneral.R0: return("R0"); case RegGeneral.R1: return("R1"); case RegGeneral.R2: return("R2"); case RegGeneral.R3: return("R3"); case RegGeneral.R4: return("R4"); case RegGeneral.R5: return("R5"); case RegGeneral.R6: return("R6"); case RegGeneral.R7: return("R7"); default: return("??"); } }
private void BitPatternBTI(ushort operand, out ushort value, out RegGeneral destination) { destination = (RegGeneral)((operand & 0xE000) >> 13); RegGeneral source = (RegGeneral)((operand & 0x1C00) >> 10); bool asRegister = (operand & 0x0100) != 0; value = asRegister ? (ushort)(R[(int)source] & 0x000F) : (ushort)((operand & 0x1E00) >> 9); }
/// <summary> /// Executes an ALU operation. /// </summary> /// <param name="operand">Input: machine code word</param> /// <param name="value">Output: result value of operation</param> /// <param name="destination">Output: index of general register result should be written to.</param> private void BitPatternALU(ushort operand, out ushort value, out RegGeneral destination) { ushort address; // Decode the operand word's constituent bits. FEDC BA98 7654 3210 // SAAA rrrE OOOO ORRR int addressingMode = (operand & 0x7000) >> 12; RegGeneral source = (RegGeneral)((operand & 0x0E00) >> 9); bool eightBitMode = (operand & 0x0100) != 0; destination = (RegGeneral)(operand & 0x0007); // R = destination register SegmentIndex dataSeg = (operand & 0x8000) != 0 ? SegmentIndex.ES : SegmentIndex.DS; switch (addressingMode) // will always be between 0x0 and 0x7 { case 0: // Addressing mode: Immediate (r == 0), Absolute (r == 1), else Control Register. if (source == 0) // Immediate { value = eightBitMode ? ReadMemInt8(PC, SegmentIndex.CS) : ReadMemInt16(PC, SegmentIndex.CS); PC += 2; // advance PC two bytes because we're reading an immediate value. } else if ((int)source == 1) // Absolute { address = ReadMemInt16(PC, SegmentIndex.CS); PC += 2; // advance PC two bytes because we're reading an immediate value. value = eightBitMode ? ReadMemInt8(address, dataSeg) : ReadMemInt16(address, dataSeg); } else // Control Register { RegControl cr = (RegControl)((operand & 0x0700) >> 8); value = ReadControlRegister(operand, cr); } break; case 1: // Addressing mode: Register value = R[(int)source]; break; case 2: // Addressing mode: Indirect value = eightBitMode ? ReadMemInt8(R[(int)source], dataSeg) : ReadMemInt16(R[(int)source], dataSeg); break; case 3: // Addressing mode: Absolute Offset AKA Indirect Offset address = (ushort)(R[(int)source] + ReadMemInt16(PC, SegmentIndex.CS)); PC += 2; // advance PC two bytes because we're reading an immediate value. value = eightBitMode ? ReadMemInt8(address, dataSeg) : ReadMemInt16(address, dataSeg); break; default: // addressing of 0x4 ~ 0x7 is an Indirect Indexed operation. int indexRegister = addressingMode; // bit pattern is 1ii, where ii = r4 - r7. address = (ushort)(R[(int)source] + R[indexRegister]); value = eightBitMode ? ReadMemInt8(address, dataSeg) : ReadMemInt16(address, dataSeg); break; } }
/// <summary> /// Executes a STOre operation (same bit pattern as ALU, but writes a value from r0 to destination). /// </summary> /// <param name="operand">Input: machine code word</param> /// <param name="destAddress">Output: memory address that is the destination of the operation</param> /// <param name="source">Output: general register that is the source of the operation</param> private void BitPatternSTO(ushort operand, out ushort destAddress, out RegGeneral source) { // Decode the operand word's constituent bits. FEDC BA98 7654 3210 // SAAA rrrE OOOO ORRR int addressingMode = (operand & 0x7000) >> 12; RegGeneral addrRegister = (RegGeneral)((operand & 0x0E00) >> 9); source = (RegGeneral)(operand & 0x0007); // R = source register switch (addressingMode) // will always be between 0x0 and 0x7 { case 0: // Immediate (r == 0), Absolute (r == 1), else Control Register if (addrRegister == 0) { // Immediate - no such addressing mode for STO. source = RegGeneral.None; destAddress = 0; Interrupt_UndefFault(operand); } else if ((int)addrRegister == 1) { destAddress = ReadMemInt16(PC, SegmentIndex.CS); PC += 2; // advance PC two bytes because we're reading an immediate value. } else { RegControl cr = (RegControl)((operand & 0x0700) >> 8); WriteControlRegister(operand, cr, R[(int)source]); // set source = none so calling function doesn't attempt to interpret this as well. source = RegGeneral.None; destAddress = 0; } break; case 1: // Register - no such addressing mode for STO. source = RegGeneral.None; destAddress = 0; Interrupt_UndefFault(operand); break; case 2: // Indirect destAddress = R[(int)addrRegister]; break; case 3: // Absolute Offset AKA Indirect Offset destAddress = (ushort)(R[(int)addrRegister] + ReadMemInt16(PC, SegmentIndex.CS)); PC += 2; // advance PC two bytes because we're reading an immediate value. break; default: // $8-$F are Indirect Indexed operations. int indexRegister = addressingMode; // bit pattern is 01ii, indicating r4 - r7. destAddress = (ushort)(R[(int)source] + R[indexRegister]); break; } }
private void BitPatternSHF(ushort operand, out ushort value, out RegGeneral destination) { destination = (RegGeneral)((operand & 0xE000) >> 13); if ((operand & 0x1000) == 0) { value = (ushort)(((operand & 0x0F00) >> 8) + 1); } else { value = R[(operand & 0x0700) >> 8]; } }
private string DisassembleBTT(string name, ushort operand, ushort nextword, ushort address, bool showMemoryContents, out ushort instructionSize) { instructionSize = 2; RegGeneral destination = (RegGeneral)((operand & 0xE000) >> 13); bool as_register = (operand & 0x1000) != 0; ushort value = as_register ? (ushort)((operand & 0x0700) >> 8) : (ushort)((operand & 0x0F00) >> 8); return ($"{name,-8}{NameOfRegGP(destination)}, {(as_register ? $"{NameOfRegGP((RegGeneral)value),-8}(${R[value]:X1})" : $"${value:X1}")}"); }
private void BitPatternSET(ushort operand, out ushort value, out RegGeneral destination) { destination = (RegGeneral)((operand & 0xE000) >> 13); value = (ushort)((operand & 0x1F00) >> 8); if ((operand & 0x0001) == 1) { if (value <= 0x0A) { value = (ushort)(0x0001 << (value + 0x05)); } else { value = (ushort)(0xFFE0 + value); } } }
private string DisassembleSHF(string name, ushort operand, ushort nextword, ushort address, bool showMemoryContents, out ushort instructionSize) { instructionSize = 2; RegGeneral destination = (RegGeneral)((operand & 0xE000) >> 13); string value; if ((operand & 0x1000) == 0) { int shiftby = ((operand & 0x0F00) >> 8) + 1; value = $"${(ushort)shiftby:X2}"; } else { value = NameOfRegGP((RegGeneral)((operand & 0x0700) >> 8)); } return($"{name,-8}{NameOfRegGP(destination)}, {value}"); }
private string DisassembleJMP(string name, ushort operand, ushort nextword, ushort address, bool showMemoryContents, out ushort instructionSize) { int addressingmode = (operand & 0x7000) >> 12; RegGeneral r_src = (RegGeneral)((operand & 0x1C00) >> 10); int index_bits = (operand & 0x0300) >> 8; SegmentIndex segData = (operand & 0x8000) != 0 ? SegmentIndex.ES : SegmentIndex.DS; bool isFar = (operand & 0x0100) != 0; if (isFar) { name += ".F"; } switch (addressingmode) { case 0: // Immediate or Absolute bool absolute = (operand & 0x0200) != 0; instructionSize = (ushort)(isFar && !absolute ? 8 : 4); if (absolute) { return ($"{name,-8}[${nextword:X4}]{string.Format($" (${DebugReadMemory(nextword, SegmentIndex.CS):X4})", DebugReadMemory(nextword, SegmentIndex.CS))}"); } return($"{name,-8}${nextword:X4}{(isFar ? ", $<SEGREG>" : string.Empty)}"); case 1: // Register instructionSize = 2; return($"{name,-8}{NameOfRegGP(r_src)} (${R[(int)r_src]:X4})"); case 2: // Indirect instructionSize = 2; return ($"{name,-8}[{NameOfRegGP(r_src)}] (${DebugReadMemory(R[(int)r_src], segData):X4})"); case 3: // Indirect Offset (also Absolute Offset) instructionSize = 4; return ($"{name,-8}[{NameOfRegGP(r_src)},${nextword:X4}] (${DebugReadMemory((ushort)(R[(int)r_src] + nextword), segData):X4})"); default: // $8 - $f = Indirect Indexed instructionSize = 2; index_bits += (operand & 0x4000) >> 12; return ($"{name,-8}[{NameOfRegGP(r_src)},{NameOfRegGP((RegGeneral)index_bits)}] (${DebugReadMemory((ushort)(R[(int)r_src] + R[index_bits]), segData):X4})"); } }
private string DisassembleSET(string name, ushort operand, ushort nextword, ushort address, bool showMemoryContents, out ushort instructionSize) { instructionSize = 2; RegGeneral destination = (RegGeneral)((operand & 0xE000) >> 13); int value = (operand & 0x1F00) >> 8; if ((operand & 0x0001) == 1) { if (value <= 0x0A) { value = (ushort)(0x0001 << (value + 0x05)); } else { value = (ushort)(0xFFE0 + value); } } return($"{name,-8}{NameOfRegGP(destination)}, {string.Format($"${value:X2}", value)}"); }
private void BitPatternSTK(ushort operand, out ushort value, out RegGeneral destination) { destination = RegGeneral.None; // unused value = (ushort)(operand & 0xFF01); }
private void BitPatternBRA(ushort operand, out ushort value, out RegGeneral destination) { destination = RegGeneral.None; // not used value = (ushort)((operand & 0xFF00) >> 7); // (shift 8 - 1) to multiply result by two, per spec. }
private void BitPatternIMM(ushort operand, out ushort value, out RegGeneral destination) { destination = (RegGeneral)((operand & 0xE000) >> 13); value = (ushort)(((operand & 0x1F00) >> 8) + 1); }
private string NameOfRegGP(RegGeneral register) { switch (register) { case RegGeneral.R0: return "R0"; case RegGeneral.R1: return "R1"; case RegGeneral.R2: return "R2"; case RegGeneral.R3: return "R3"; case RegGeneral.R4: return "R4"; case RegGeneral.R5: return "R5"; case RegGeneral.R6: return "R6"; case RegGeneral.R7: return "R7"; default: return "??"; } }
private string DisassembleALU(string name, ushort operand, ushort nextword, ushort address, bool showMemoryContents, out ushort instructionSize) { int addressingmode = (operand & 0x7000) >> 12; RegGeneral regDest = (RegGeneral)(operand & 0x0007); RegGeneral regSrc = (RegGeneral)((operand & 0x0E00) >> 9); bool isEightBit = (operand & 0x0100) != 0; SegmentIndex segData = (operand & 0x8000) != 0 ? SegmentIndex.ES : SegmentIndex.DS; switch (addressingmode) { case 0: if (regSrc == 0) // immediate { if (name == "STO") { instructionSize = 2; return("???"); } instructionSize = 4; string disasm = $"{name + (isEightBit ? ".8" : string.Empty),-8}{NameOfRegGP(regDest)}, ${nextword:X4}"; if (showMemoryContents) { disasm = AppendMemoryContents(disasm, nextword); } return(disasm); } if ((int)regSrc == 1) // absolute { instructionSize = 4; string disasm = $"{name + (isEightBit ? ".8" : string.Empty),-8}{NameOfRegGP(regDest)}, [${nextword:X4}]"; if (showMemoryContents) { disasm = AppendMemoryContents(disasm, DebugReadMemory(nextword, segData)); } return(disasm); } else // control register { instructionSize = 2; RegControl cr = (RegControl)((operand & 0x0700) >> 8); string disasm = $"{name,-8}{NameOfRegGP(regDest)}, {NameOfRegSP(cr)}"; if (showMemoryContents) { disasm = AppendMemoryContents(disasm, nextword); } return(disasm); } case 1: // Register instructionSize = 2; return ($"{name + (isEightBit ? ".8" : string.Empty),-8}{NameOfRegGP(regDest)}, {NameOfRegGP(regSrc),-12}(${R[(int)regSrc]:X4})"); case 2: // Indirect instructionSize = 2; return ($"{name + (isEightBit ? ".8" : string.Empty),-8}{NameOfRegGP(regDest)}, [{NameOfRegGP(regSrc)}] (${DebugReadMemory(R[(int)regSrc], segData):X4})"); case 3: // Indirect Offset (also Absolute Offset) instructionSize = 4; return ($"{name + (isEightBit ? ".8" : string.Empty),-8}{NameOfRegGP(regDest)}, [{NameOfRegGP(regSrc)},${nextword:X4}] (${DebugReadMemory((ushort)(R[(int)regSrc] + nextword), segData):X4})"); default: // $4 - $7 are Indirect Indexed instructionSize = 2; RegGeneral regIndex = (RegGeneral)((operand & 0x7000) >> 12); return ($"{name + (isEightBit ? ".8" : string.Empty),-8}{NameOfRegGP(regDest)}, [{NameOfRegGP(regSrc)},{NameOfRegGP(regIndex)}] (${DebugReadMemory((ushort)(R[(int)regSrc] + R[(int)regIndex]), segData):X4})"); } }
private void BitPatternSET(ushort operand, out ushort value, out RegGeneral destination) { destination = (RegGeneral)((operand & 0xE000) >> 13); value = (ushort)((operand & 0x1F00) >> 8); if ((operand & 0x0001) == 1) { if (value <= 0x0A) value = (ushort)(0x0001 << (value + 0x05)); else value = (ushort)(0xFFE0 + value); } }
private void BitPatternFLG(ushort operand, out ushort value, out RegGeneral destination) { destination = RegGeneral.None; // unused value = (ushort)(operand & 0xF000); // flags to set }
private void BitPatternHWQ(ushort operand, out ushort value, out RegGeneral destination) { destination = RegGeneral.None; // Unused. value = (ushort)((operand & 0xFF00) >> 8); }
private void BitPatternJMI(ushort operand, out ushort address, out uint addressFar, out bool isFarJump) { ushort nextword; // Decode the operand word's constituent bits. FEDC BA98 7654 3210 // SAAA rrrF OOOO ORRR int addressingMode = (operand & 0x7000) >> 12; RegGeneral source = (RegGeneral)((operand & 0x0E00) >> 9); SegmentIndex dataSeg = (operand & 0x8000) != 0 ? SegmentIndex.ES : SegmentIndex.DS; addressFar = 0; isFarJump = (operand & 0x0100) != 0; // F = far jump mode switch (addressingMode) // will always be between 0x0 and 0xf { case 0: // Immediate (r == 0) or Absolute (r == 1) if (source == 0) { address = ReadMemInt16(PC, SegmentIndex.CS); PC += 2; // advance PC two bytes because we're reading an immediate value. if (isFarJump) { addressFar = ReadMemInt16(PC, SegmentIndex.CS); PC += 2; // advance PC two bytes because we're reading an immediate value. addressFar |= (uint)ReadMemInt16(PC, SegmentIndex.CS) << 16; PC += 2; // advance PC two bytes because we're reading an immediate value. } } else { nextword = ReadMemInt16(PC, SegmentIndex.CS); PC += 2; // advance PC two bytes because we're reading an immediate value. address = ReadMemInt16(nextword, dataSeg); if (isFarJump) { addressFar = ReadMemInt16((ushort)(nextword + 2), dataSeg); addressFar |= (uint)ReadMemInt16((ushort)(nextword + 4), dataSeg) << 16; } } break; case 1: // Register address = R[(int)source]; break; case 2: // Indirect address = ReadMemInt16(R[(int)source], dataSeg); if (isFarJump) { addressFar = ReadMemInt16((ushort)(R[(int)source] + 2), dataSeg); addressFar |= (uint)ReadMemInt16((ushort)(R[(int)source] + 4), dataSeg) << 16; } break; case 3: // Indirect Offset AKA Absolute Offset nextword = ReadMemInt16(PC, SegmentIndex.CS); PC += 2; // advance PC two bytes because we're reading an immediate value. address = ReadMemInt16((ushort)(R[(int)source] + nextword), dataSeg); if (isFarJump) { addressFar = ReadMemInt16((ushort)(R[(int)source] + nextword + 2), dataSeg); addressFar |= (uint)ReadMemInt16((ushort)(R[(int)source] + nextword + 4), dataSeg) << 16; } break; default: // 0x04 - 0x07 are Indirect Indexed int indexRegister = (operand & 0x7000) >> 12; // bit pattern is 1ii, indicating R4 - R7 address = ReadMemInt16((ushort)(R[(int)source] + R[indexRegister]), dataSeg); if (isFarJump) { addressFar = ReadMemInt16((ushort)(R[(int)source] + R[indexRegister] + 2), dataSeg); addressFar |= (uint)ReadMemInt16((ushort)(R[(int)source] + R[indexRegister] + 4), dataSeg) << 16; } break; } }