2024-12-10 22:30:15 +00:00
|
|
|
import { Ops, opToString } from "./arch.ts";
|
2024-12-10 14:45:19 +00:00
|
|
|
|
|
|
|
export type Line = {
|
|
|
|
labels?: number[];
|
|
|
|
} & LineKind;
|
|
|
|
|
|
|
|
export type Label = { label: number };
|
|
|
|
|
|
|
|
export type LineKind =
|
|
|
|
| { type: "op"; op: number }
|
|
|
|
| { type: "lit"; val: number }
|
|
|
|
| { type: "ref"; label: number };
|
|
|
|
|
|
|
|
export class Assembler {
|
|
|
|
private lines: Line[] = [];
|
|
|
|
private labelCounter = 0;
|
|
|
|
|
|
|
|
public push(...values: number[]) {
|
|
|
|
for (const value of values) {
|
|
|
|
this.addLit(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public addOp(op: number): Assembler {
|
|
|
|
this.lines.push({ type: "op", op });
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public addLit(val: number): Assembler {
|
|
|
|
this.lines.push({ type: "lit", val });
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public addRef({ label }: Label): Assembler {
|
|
|
|
this.lines.push({ type: "ref", label });
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public setLabel({ label }: Label): Assembler {
|
|
|
|
const line = this.lines.at(-1);
|
|
|
|
if (line === undefined) {
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
if (line.labels === undefined) {
|
|
|
|
line.labels = [];
|
|
|
|
}
|
|
|
|
line.labels.push(label);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public makeLabel(): Label {
|
|
|
|
const label = this.labelCounter;
|
|
|
|
this.labelCounter += 1;
|
|
|
|
return { label };
|
|
|
|
}
|
|
|
|
|
|
|
|
public concat(assembler: Assembler) {
|
|
|
|
this.lines.push(...assembler.lines);
|
|
|
|
}
|
|
|
|
|
|
|
|
public assemble(): number[] {
|
|
|
|
let ip = 0;
|
|
|
|
const output: number[] = [];
|
|
|
|
const locs: { [key: number]: number } = {};
|
|
|
|
const refs: { [key: number]: number } = {};
|
|
|
|
for (const line of this.lines) {
|
|
|
|
switch (line.type) {
|
|
|
|
case "op":
|
|
|
|
output.push(line.op);
|
|
|
|
ip += 1;
|
|
|
|
break;
|
|
|
|
case "lit":
|
|
|
|
output.push(line.val);
|
|
|
|
ip += 1;
|
|
|
|
break;
|
|
|
|
case "ref":
|
|
|
|
output.push(0);
|
|
|
|
refs[ip] = line.label;
|
|
|
|
ip += 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (let i = 0; i < output.length; ++i) {
|
|
|
|
if (!(i in refs)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!(refs[i] in locs)) {
|
|
|
|
console.error(
|
|
|
|
`Assembler: label '${refs[i]}' used at ${i} not defined`,
|
|
|
|
);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
output[i] = locs[refs[i]];
|
|
|
|
}
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
}
|
2024-12-10 22:30:15 +00:00
|
|
|
|
|
|
|
export type Line2 = { labels?: number[]; ins: Ins2 };
|
|
|
|
|
|
|
|
export type Ins2 = [Ops, ...Lit[]];
|
|
|
|
|
|
|
|
export type Lit = number | string | boolean | Label;
|
|
|
|
|
|
|
|
export class Assembler2 {
|
|
|
|
private lines: Line2[] = [];
|
|
|
|
private addedLabels: number[] = [];
|
|
|
|
private labelCounter = 0;
|
|
|
|
|
|
|
|
public add(ins: Ins2): Assembler2 {
|
|
|
|
if (this.addedLabels.length > 0) {
|
|
|
|
this.lines.push({ ins, labels: this.addedLabels });
|
|
|
|
this.addedLabels = [];
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
this.lines.push({ ins });
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public makeLabel(): Label {
|
|
|
|
return { label: this.labelCounter++ };
|
|
|
|
}
|
|
|
|
|
|
|
|
public setLabel({ label }: Label) {
|
|
|
|
this.addedLabels.push(label);
|
|
|
|
}
|
|
|
|
|
|
|
|
public assemble(): number[] {
|
|
|
|
let ip = 0;
|
|
|
|
const output: number[] = [];
|
|
|
|
const locs: { [key: number]: number } = {};
|
|
|
|
const refs: { [key: number]: number } = {};
|
|
|
|
for (const line of this.lines) {
|
|
|
|
for (const lit of line.ins as Lit[]) {
|
|
|
|
if (typeof lit === "number") {
|
|
|
|
output.push(lit);
|
|
|
|
ip += 1;
|
|
|
|
} else if (typeof lit === "boolean") {
|
|
|
|
output.push(lit ? 1 : 0);
|
|
|
|
ip += 1;
|
|
|
|
} else if (typeof lit === "string") {
|
|
|
|
for (let i = 0; i < lit.length; ++i) {
|
|
|
|
output.push(lit.charCodeAt(i));
|
|
|
|
ip += 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
output.push(0);
|
|
|
|
refs[ip] = lit.label;
|
|
|
|
ip += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (let i = 0; i < output.length; ++i) {
|
|
|
|
if (!(i in refs)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!(refs[i] in locs)) {
|
|
|
|
console.error(
|
|
|
|
`Assembler: label '${refs[i]}' used at ${i} not defined`,
|
|
|
|
);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
output[i] = locs[refs[i]];
|
|
|
|
}
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
|
|
|
public printProgram() {
|
|
|
|
for (const line of this.lines) {
|
|
|
|
for (const label of line.labels ?? []) {
|
|
|
|
console.log(`.L${label}:`);
|
|
|
|
}
|
|
|
|
const op = opToString(line.ins[0] as unknown as number).padEnd(
|
|
|
|
13,
|
|
|
|
" ",
|
|
|
|
);
|
|
|
|
const args = (line.ins.slice(1) as Lit[]).map((lit) => {
|
|
|
|
if (typeof lit === "number") {
|
|
|
|
return lit;
|
|
|
|
} else if (typeof lit === "boolean") {
|
|
|
|
return lit.toString();
|
|
|
|
} else if (typeof lit === "string") {
|
|
|
|
return '"' +
|
|
|
|
lit.replaceAll("\\", "\\\\").replaceAll("\0", "\\0")
|
|
|
|
.replaceAll("\n", "\\n").replaceAll("\t", "\\t")
|
|
|
|
.replaceAll("\r", "\\r") +
|
|
|
|
'"';
|
|
|
|
} else {
|
|
|
|
return `.L${lit.label}`;
|
|
|
|
}
|
|
|
|
}).join(", ");
|
|
|
|
console.log(` ${op} ${args}`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|