UNPKG

m68kdecode

Version:

Port of deplinenoise/m68kdecode to Typescript

486 lines (451 loc) 13.4 kB
import { MemoryIndirection, FPConditionCode, ConditionCode, FPFormat, Operand, InstructionExtra, BitfieldData, Indexer, Displacement, FloatFormat, FPF_EXTENDED_REAL, REGLIST, FPCondition, FPF_BYTE_INT, FR, FPF_WORD_INT, FPF_DOUBLE, FPF_PACKED_DECIMAL_REAL_STATIC, FPF_PACKED_DECIMAL_REAL_DYNAMIC, FPF_SINGLE, FPF_LONG_INT, Condition, Bitfield, STATIC, DYNAMIC, IMM8, IMM16, IMM32, DR, AR, ARIndexer, DRIndexer, PCDISP, ARDISP, DISP, ABS16, ABS32, ARIND, ARINC, ARDEC, } from "./types"; import { getBits, i16, i8, simpleDisp, u8 } from "./lib"; class CodeStream { public pos = 0; constructor(public bytes: Uint8Array) {} hasWords(count: number): boolean { return this.pos + count * 2 <= this.bytes.length; } peekWord(offset: number): number { const p = this.pos + offset; if (p + 2 > this.bytes.length) { throw new Error("OutOfSpace"); } return (this.bytes[p] << 8) | this.bytes[p + 1]; } skipWords(count: number) { this.pos += 2 * count; } pull16(): number { const result = this.peekWord(0); this.pos += 2; return result; } pull32(): number { const a = this.pull16(); const b = this.pull16(); return (a << 16) | b; } ea(srcReg: number, srcMod: number, size: number): Operand | null { switch (srcMod) { case 0b000: return DR(srcReg); case 0b001: return AR(srcReg); case 0b010: return ARIND(srcReg); case 0b011: return ARINC(srcReg); case 0b100: return ARDEC(srcReg); case 0b101: return ARDISP(srcReg, simpleDisp(i16(this.pull16()))); case 0b110: return this.decodeExtendedEa(srcReg); case 0b111: { switch (srcReg) { case 0b000: return ABS16(i16(this.pull16())); case 0b001: return ABS32(this.pull32()); case 0b010: return PCDISP(i8(this.pos), simpleDisp(i16(this.pull16()))); case 0b011: return this.decodeExtendedEa(null); case 0b100: { switch (size) { case 1: return IMM8(u8(this.pull16())); case 2: return IMM16(this.pull16()); case 4: return IMM32(this.pull32()); default: { throw new Error("BadSize"); } } } } } } throw new Error("NotImplemented"); } decodeExtendedEa(srcReg: number | null): Operand { const pcOff = u8(this.pos); const ext = this.pull16(); const scale = u8(getBits(ext, 9, 2)); const idx = getBits(ext, 12, 3); const idxIsA = getBits(ext, 15, 1) === 1; if (0 !== (ext & (1 << 8))) { // Handle full extension word. const bd = getBits(ext, 4, 2); const od = getBits(ext, 0, 2); let baseDisplacement: number; switch (bd) { case 0: throw new Error("Reserved"); case 1: baseDisplacement = 0; break; case 2: baseDisplacement = i16(this.pull16()); break; case 3: baseDisplacement = this.pull32(); break; default: throw new Error("NotImplemented"); } let outerDisplacement: number; switch (od) { case 0: outerDisplacement = 0; break; case 1: outerDisplacement = 0; break; case 2: outerDisplacement = i16(this.pull16()); break; case 3: outerDisplacement = this.pull32(); break; default: throw new Error("NotImplemented"); } const suppressBase = getBits(ext, 7, 1) === 1; const suppressIndexer = getBits(ext, 6, 1) === 1; let indirection: MemoryIndirection | null; if (!suppressIndexer) { switch (getBits(ext, 0, 3)) { case 0b000: indirection = null; break; case 0b001: indirection = "IndirectPreIndexed"; break; case 0b010: indirection = "IndirectPreIndexed"; break; case 0b011: indirection = "IndirectPreIndexed"; break; case 0b100: throw new Error("Reserved"); case 0b101: indirection = "IndirectPostIndexed"; break; case 0b110: indirection = "IndirectPostIndexed"; break; case 0b111: indirection = "IndirectPostIndexed"; break; default: throw new Error("NotImplemented"); } } else { switch (getBits(ext, 0, 3)) { case 0b000: indirection = null; break; case 0b001: indirection = "Indirect"; break; case 0b010: indirection = "Indirect"; break; case 0b011: indirection = "Indirect"; break; default: throw new Error("Reserved"); } } let indexer: Indexer | null = null; if (!suppressIndexer) { indexer = idxIsA ? ARIndexer(idx, scale) : DRIndexer(idx, scale); } if (suppressBase) { return DISP({ baseDisplacement, outerDisplacement, indexer, indirection, }); } else { return srcReg !== null ? ARDISP(srcReg, { baseDisplacement, outerDisplacement, indexer, indirection, }) : PCDISP(pcOff, { baseDisplacement, outerDisplacement, indexer, indirection, }); } } else { // Handle brief extension word const disp = i8(ext & 0xff); const indexer: Indexer = idxIsA ? ARIndexer(idx, scale) : DRIndexer(idx, scale); const displacement: Displacement = { baseDisplacement: disp, outerDisplacement: 0, indexer, indirection: null, }; return srcReg !== null ? ARDISP(srcReg, displacement) : PCDISP(pcOff, displacement); } } imm8(): Operand { return IMM8(u8(this.pull16())); } imm16(): Operand { return IMM16(this.pull16()); } imm32(): Operand { return IMM32(this.pull32()); } dar(dOrA: number, regno: number): Operand { return dOrA === 0 ? DR(regno) : AR(regno); } bitfield( dynOff: number, off: number, dynWidth: number, width: number ): InstructionExtra { const bfOffset: BitfieldData = dynOff === 0 ? STATIC((off & 31) === 0 ? 32 : u8(off & 31)) : DYNAMIC(off); const bfWidth: BitfieldData = dynWidth === 0 ? STATIC((width & 31) === 0 ? 32 : u8(width & 31)) : DYNAMIC(width); return Bitfield(bfOffset, bfWidth); } cc(c: number): InstructionExtra { switch (c) { case 0b0000: return Condition(ConditionCode.CC_T); // Always True case 0b0001: return Condition(ConditionCode.CC_F); // Always False case 0b0010: return Condition(ConditionCode.CC_HI); // High case 0b0011: return Condition(ConditionCode.CC_LS); // Low or Same case 0b0100: return Condition(ConditionCode.CC_CC); // Carry Clear case 0b0101: return Condition(ConditionCode.CC_CS); // Carry Set case 0b0110: return Condition(ConditionCode.CC_NE); // Not Equal case 0b0111: return Condition(ConditionCode.CC_EQ); // Equal case 0b1000: return Condition(ConditionCode.CC_VC); // Overflow Clear case 0b1001: return Condition(ConditionCode.CC_VS); // Overflow Set case 0b1010: return Condition(ConditionCode.CC_PL); // Plus case 0b1011: return Condition(ConditionCode.CC_MI); // Negative case 0b1100: return Condition(ConditionCode.CC_GE); // Greater or Equal case 0b1101: return Condition(ConditionCode.CC_LT); // Less case 0b1110: return Condition(ConditionCode.CC_GT); // Greater default: return Condition(ConditionCode.CC_LE); // Less or Equal } } quickConst(i: number): Operand { return IMM8(i === 0 ? 8 : u8(i)); } decodeFp( rg: number, md: number, m_r: number, s: number, d: number, k: number ): [number, Operand | null, Operand | null, InstructionExtra] { if (m_r === 1) { let sz: number; let fpform: FPFormat; switch (s) { case 0b000: sz = 4; fpform = FPF_LONG_INT(); break; case 0b001: sz = 4; fpform = FPF_SINGLE(); break; case 0b010: sz = 10; fpform = FPF_EXTENDED_REAL(); break; case 0b011: sz = 12; fpform = FPF_PACKED_DECIMAL_REAL_STATIC(i8(k << 1) >> 1); break; case 0b100: sz = 2; fpform = FPF_WORD_INT(); break; case 0b101: sz = 8; fpform = FPF_DOUBLE(); break; case 0b110: sz = 1; fpform = FPF_BYTE_INT(); break; case 0b111: sz = 12; fpform = FPF_PACKED_DECIMAL_REAL_DYNAMIC(k >> 4); break; default: throw new Error("Reserved"); } return [sz, this.ea(rg, md, sz), FR(d), FloatFormat(fpform)]; } else { return [10, FR(s), FR(d), FloatFormat(FPF_EXTENDED_REAL())]; } } fpcc(c: number): InstructionExtra { switch (c) { case 0b000000: return FPCondition(FPConditionCode.FPCC_F); // False case 0b000001: return FPCondition(FPConditionCode.FPCC_EQ); // Equal case 0b000010: return FPCondition(FPConditionCode.FPCC_OGT); // Ordered Greater Than case 0b000011: return FPCondition(FPConditionCode.FPCC_OGE); // Ordered Greater Than or Equal case 0b000100: return FPCondition(FPConditionCode.FPCC_OLT); // Ordered Less Than case 0b000101: return FPCondition(FPConditionCode.FPCC_OLE); // Ordered Less Than or Equal case 0b000110: return FPCondition(FPConditionCode.FPCC_OGL); // Ordered Greater Than or Less Than case 0b000111: return FPCondition(FPConditionCode.FPCC_OR); // Ordered case 0b001000: return FPCondition(FPConditionCode.FPCC_UN); // Unordered case 0b001001: return FPCondition(FPConditionCode.FPCC_UEQ); // Unordered or Equal case 0b001010: return FPCondition(FPConditionCode.FPCC_UGT); // Unordered or Greater Than case 0b001011: return FPCondition(FPConditionCode.FPCC_UGE); // Unordered or Greater Than or Equal case 0b001100: return FPCondition(FPConditionCode.FPCC_ULT); // Unordered or Less Than case 0b001101: return FPCondition(FPConditionCode.FPCC_ULE); // Unordered or Less Than or Equal case 0b001110: return FPCondition(FPConditionCode.FPCC_NE); // Not Equal case 0b001111: return FPCondition(FPConditionCode.FPCC_T); // True case 0b010000: return FPCondition(FPConditionCode.FPCC_SF); // Signaling False case 0b010001: return FPCondition(FPConditionCode.FPCC_SEQ); // Signaling Equal case 0b010010: return FPCondition(FPConditionCode.FPCC_GT); // Greater Than case 0b010011: return FPCondition(FPConditionCode.FPCC_GE); // Greater Than or Equal case 0b010100: return FPCondition(FPConditionCode.FPCC_LT); // Less Than case 0b010101: return FPCondition(FPConditionCode.FPCC_LE); // Less Than or Equal case 0b010110: return FPCondition(FPConditionCode.FPCC_GL); // Greater Than or Less Than case 0b010111: return FPCondition(FPConditionCode.FPCC_GLE); // Greater Than or Less Than or Equal case 0b011000: return FPCondition(FPConditionCode.FPCC_NGLE); // Not (Greater Than or Less Than or Equal) case 0b011001: return FPCondition(FPConditionCode.FPCC_NGL); // Not (Greater Than or Less Than) case 0b011010: return FPCondition(FPConditionCode.FPCC_NLE); // Not (Less Than or Equal) case 0b011011: return FPCondition(FPConditionCode.FPCC_NLT); // Not (Less Than) case 0b011100: return FPCondition(FPConditionCode.FPCC_NGE); // Not (Greater Than or Equal) case 0b011101: return FPCondition(FPConditionCode.FPCC_NGT); // Not (Greater Than) case 0b011110: return FPCondition(FPConditionCode.FPCC_SNE); // Signaling Not Equal default: return FPCondition(FPConditionCode.FPCC_ST); // Signaling True } } decodeFpMovem( r: number, m: number, direction: number, mask: number, mode: number ): [number, Operand | null, Operand | null, InstructionExtra] { const ea = this.ea(r, m, 10); const regs: Operand = (mode & 1) === 0 ? REGLIST(mask) : DR(mask >> 4); const extra = FloatFormat(FPF_EXTENDED_REAL()); return direction === 0 ? [10, ea, regs, extra] // move from memory to float registers : [10, regs, ea, extra]; // move from registers to memory } } export default CodeStream;