68kcounter
Version:
68000 ASM source code cycle counter
227 lines (196 loc) • 5.24 kB
text/typescript
import json5 from "json5";
import {
AddressingMode,
aliases,
Directive,
isDirective,
isMnemonic,
isQualifier,
Mnemonic,
Qualifier,
Qualifiers,
} from "../syntax";
import lookupAddressingMode from "./operandMode";
import tokenize from "./tokenize";
export abstract class Node {
type: string;
loc: Location;
text: string;
constructor(start: number, text: string, type = "") {
this.type = type;
this.text = text;
this.loc = {
start,
end: start + text.length,
};
}
}
export interface Location {
start: number;
end: number;
}
export class LabelNode extends Node {
name: string;
local: boolean;
macro: boolean;
constructor(start: number, text: string) {
super(start, text, "Label");
this.name = text;
this.local = text.indexOf(".") === 0;
this.macro = text.includes("@");
}
}
export class OpcodeNode extends Node {
op: MnemonicNode | DirectiveNode | MacroNode;
qualifier?: QualifierNode;
constructor(start: number, text: string) {
super(start, text, "Opcode");
let [op, qualifier] = text.toUpperCase().split(".");
if (aliases[op]) op = aliases[op];
if (isMnemonic(op)) {
this.op = new MnemonicNode(start, op);
if (qualifier === Qualifiers.S) {
qualifier = Qualifiers.B;
}
} else if (isDirective(op)) {
this.op = new DirectiveNode(start, op);
} else {
this.op = new MacroNode(start, op);
}
if (isQualifier(qualifier)) {
const sepPos = text.indexOf(".");
this.qualifier = new QualifierNode(start + sepPos + 1, qualifier);
}
}
}
export class MnemonicNode extends Node {
name: Mnemonic;
constructor(start: number, name: Mnemonic) {
super(start, name, "Mnemonic");
this.name = name;
}
}
export class DirectiveNode extends Node {
name: Directive;
constructor(start: number, name: Directive) {
super(start, name, "Directive");
this.name = name;
}
}
export class MacroNode extends Node {
name: string;
constructor(start: number, name: string) {
super(start, name, "Macro");
this.name = name;
}
}
export class QualifierNode extends Node {
name: Qualifier;
constructor(start: number, name: Qualifier) {
super(start, name, "Qualifier");
this.name = name;
}
}
export class EffectiveAddressNode extends Node {
mode: AddressingMode;
constructor(start: number, text: string) {
super(start, text, "EffectiveAddress");
this.mode = lookupAddressingMode(text);
}
}
export class StringNode extends Node {
value: string;
constructor(start: number, text: string) {
super(start, text, "String");
try {
this.value = json5.parse(text);
} catch (_) {
this.value = "";
console.error("Unable to parse text", { text });
}
}
}
export class MacroArgNode extends Node {
index: number;
constructor(start: number, text: string) {
super(start, text, "MacroArg");
this.index = parseInt(text.substring(1), 10);
}
}
export class MacroInvocationsNode extends Node {
constructor(start: number, text: string) {
super(start, text, "MacroInvocations");
}
}
export class CommentNode extends Node {
constructor(start: number, text: string) {
super(start, text, "Comment");
}
}
export class StatementNode extends Node {
label?: LabelNode;
opcode?: OpcodeNode;
operands: Node[];
comment?: CommentNode;
constructor(text: string) {
super(0, text);
this.operands = [];
const tokens = tokenize(text);
for (const i in tokens) {
const [start, text] = tokens[i];
if (text[0] === ";" || (text[0] === "*" && text[1] !== "+")) {
this.comment = new CommentNode(start, text);
} else if (start === 0) {
this.label = new LabelNode(start, text);
} else if (!this.opcode) {
this.opcode = new OpcodeNode(start, text);
} else {
// Operands
if (["'", '"'].includes(text[0])) {
this.operands.push(new StringNode(start, text));
} else if (text === "\\@") {
this.operands.push(new MacroInvocationsNode(start, text));
} else if (text[0] === "\\") {
this.operands.push(new MacroArgNode(start, text));
} else {
this.operands.push(new EffectiveAddressNode(start, text));
}
}
}
}
isLabel(): this is LabelStatement {
return !this.opcode && this.label !== undefined;
}
isInstruction(): this is InstructionStatement {
return this.opcode !== undefined && this.opcode.op instanceof MnemonicNode;
}
isDirective(): this is DirectiveStatement {
return this.opcode !== undefined && this.opcode.op instanceof DirectiveNode;
}
isMacro(): this is MacroStatement {
return this.opcode !== undefined && this.opcode.op instanceof MacroNode;
}
}
export interface InstructionStatement {
opcode: {
op: MnemonicNode;
qualifier?: QualifierNode;
};
operands: EffectiveAddressNode[];
}
export interface DirectiveStatement {
opcode: {
op: DirectiveNode;
qualifier?: QualifierNode;
};
operands: Node[];
}
export interface MacroStatement {
opcode: {
op: MacroNode;
};
operands: Node[];
}
export interface LabelStatement {
label: LabelNode;
}