UNPKG

68kcounter

Version:
240 lines (239 loc) 8.5 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.timingLabels = exports.rangeN = exports.multiplyTiming = exports.addTimings = exports.formatTiming = exports.instructionTimings = exports.popCount = void 0; const tables_1 = require("./tables"); const syntax_1 = require("../syntax"); const instructionQualifier_1 = __importDefault(require("../parse/instructionQualifier")); const evaluate_1 = __importDefault(require("../parse/evaluate")); function popCount(x) { x -= (x >> 1) & 0x55555555; x = (x & 0x33333333) + ((x >> 2) & 0x33333333); x = (x + (x >> 4)) & 0x0f0f0f0f; x += x >> 8; x += x >> 16; return x & 0x7f; } exports.popCount = popCount; /** * Look up timing information for a parsed instruction statement */ function instructionTimings(statement, vars) { const key = buildKey(statement); if (!key || !timingMap.has(key)) { return null; } const calculation = { ...timingMap.get(key) }; const timings = [...calculation.base]; const { opcode: { op }, operands, } = statement; const source = operands[0]; // Calculate n multiplier: if (calculation.multiplier) { // Shift if (syntax_1.mnemonicGroups.SHIFT.includes(op.name)) { if (source.mode === syntax_1.AddressingModes.Imm) { calculation.n = evaluate_1.default(source.text, vars) || [1, 8]; } else { // Range for register calculation.n = [0, 63]; } } // MULU else if (op.name === syntax_1.Mnemonics.MULU) { // n = the number of ones in the <ea> const value = evaluate_1.default(source.text, vars); if (source.mode === syntax_1.AddressingModes.Imm && value !== undefined) { calculation.n = popCount(value); } else { calculation.n = [0, 16]; } } // MULS else if (op.name === syntax_1.Mnemonics.MULS) { // n = concatenate the <ea> with a zero as the LSB; // n is the resultant number of 10 or 01 patterns in the 17-bit source; // i.e. worst case happens when the source is $5555 const value = evaluate_1.default(source.text, vars); if (source.mode === syntax_1.AddressingModes.Imm && value !== undefined) { calculation.n = popCount((value ^ (value << 1)) & 0xffff); } else { calculation.n = [0, 16]; } } // MOVEM else if (op.name === syntax_1.Mnemonics.MOVEM) { const listOperand = operands.find((o) => o.mode === syntax_1.AddressingModes.RegList); if (listOperand) { calculation.n = listOperand && rangeN(listOperand.text); } else { calculation.n = 1; } } // Apply multiplier: if (calculation.n) { // Range if (Array.isArray(calculation.n)) { for (const i in calculation.n) { const m = multiplyTiming(calculation.multiplier, calculation.n[i]); timings[i] = addTimings(calculation.base[0], m); } } // Single value else { const m = multiplyTiming(calculation.multiplier, calculation.n); for (const i in timings) { timings[i] = addTimings(timings[i], m); } } } } // Add effective address lookup if (calculation.ea) { for (const i in timings) { timings[i] = addTimings(timings[i], calculation.ea); } } // Add labels for multiple values const labels = timings.length > 1 ? timingLabels(op.name) : []; return { values: timings, labels, calculation }; } exports.instructionTimings = instructionTimings; /** * Convert timing to string per the 68000 documentation * * clock(read/write) */ const formatTiming = (timing) => `${timing[0]}(${timing[1]}/${timing[2]})`; exports.formatTiming = formatTiming; /** * Add two timing vectors */ function addTimings(a, b) { return [a[0] + b[0], a[1] + b[1], a[2] + b[2]]; } exports.addTimings = addTimings; /** * Multiply a timing vector by a scalar value */ function multiplyTiming(t, scalar) { return [t[0] * scalar, t[1] * scalar, t[2] * scalar]; } exports.multiplyTiming = multiplyTiming; /** * Calculate timing n value from register range used in MOVEM */ function rangeN(range) { return range.split("/").reduce((acc, v) => { const [from, to] = v.split("-").map((n) => { const t = n[0].toUpperCase(); return parseInt(n.substr(1), 10) + (t === "A" ? 8 : 0); }); return acc + (to ? to - from + 1 : 1); }, 0); } exports.rangeN = rangeN; /** * Get text labels for multiple timings */ function timingLabels(op) { if ([...syntax_1.mnemonicGroups.SCC, ...syntax_1.mnemonicGroups.BCC].includes(op)) { return ["Taken", "Not taken"]; } if (syntax_1.mnemonicGroups.DBCC.includes(op)) { return ["Taken", "Not taken", "Expired"]; } if (op === syntax_1.Mnemonics.CHK) { return ["No trap", "Trap >", "Trap <"]; } if (op === syntax_1.Mnemonics.TRAPV) { return ["No trap", "Trap"]; } // Default return ["Min", "Max"]; } exports.timingLabels = timingLabels; /** * Build string key for map lookup */ function buildKey(statement) { const { opcode, operands } = statement; if (!opcode) { return null; } let key = opcode.op.name; const qualifier = instructionQualifier_1.default(statement); if (qualifier) { key += "." + qualifier; } if (operands.length) { key += " " + operands.map((o) => o.mode).join(","); } return key; } // Flatten table into key/value for simple lookup by instruction string // e.g. // "MOVE.L Dn,Dn": [4, 1, 0] const timingMap = new Map(); for (const row of tables_1.baseTimes) { const [mnemonics, qualifiers, operands, base, multiplier] = row; for (const mnemonic of mnemonics) { for (const qualifier of qualifiers) { let key = String(mnemonic); if (qualifier) { key += "." + qualifier; } const eaSize = qualifier === syntax_1.Qualifiers.L ? 1 : 0; let o; if (Array.isArray(operands[0])) { // EA lookup in source for (o of operands[0]) { let k = key + " " + o; if (operands[1]) { k += "," + operands[1]; } let ea; if (tables_1.lookupTimes[o]) { ea = tables_1.lookupTimes[o][eaSize]; // Special cases: if (mnemonic === syntax_1.Mnemonics.TAS && o === syntax_1.AddressingModes.AbsW) { ea = [8, 1, 0]; } if ((mnemonic === syntax_1.Mnemonics.TAS || mnemonic === syntax_1.Mnemonics.CHK || mnemonic === syntax_1.Mnemonics.MULS || mnemonic === syntax_1.Mnemonics.MULU) && o === syntax_1.AddressingModes.AbsL) { ea = [12, 2, 0]; } } timingMap.set(k, { base, ea, multiplier }); } } else if (Array.isArray(operands[1])) { // EA lookup in dest for (o of operands[1]) { const k = key + " " + operands[0] + "," + o; let ea; if (tables_1.lookupTimes[o]) { ea = tables_1.lookupTimes[o][eaSize]; } timingMap.set(k, { base, ea, multiplier }); } } else { // Regular operands if (operands.length) { key += " " + operands.join(","); } timingMap.set(key, { base: base, multiplier }); } } } }