Skip to content

Commit

Permalink
Fix c instruction parsing and test
Browse files Browse the repository at this point in the history
  • Loading branch information
netalondon committed Dec 13, 2023
1 parent 344c903 commit 40c8594
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 53 deletions.
14 changes: 5 additions & 9 deletions simulator/src/cpu/alu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ export type COMMANDS_ASM =
| "-1"
| "D"
| "A"
| "M"
| "!D"
| "!A"
| "-D"
Expand All @@ -15,12 +14,9 @@ export type COMMANDS_ASM =
| "A-1"
| "D+A"
| "D-A"
| "D-M"
| "A-D"
| "D&A"
| "D&M"
| "D|A"
| "D|M";
| "D|A";

export type COMMANDS_OP =
| 0b101010
Expand Down Expand Up @@ -95,14 +91,14 @@ export const COMMANDS_ALU: {
export const COMMANDS: {
asm: Record<COMMANDS_ASM, COMMANDS_OP>;
op: Record<COMMANDS_OP, COMMANDS_ASM>;
getOp: (asm: string) => COMMANDS_OP;
} = {
asm: {
"0": 0b101010, // 42 0x2A
"1": 0b111111, // 63 0x3F
"-1": 0b111010, // 58 0x3A
D: 0b001100, // 12 0x0C
A: 0b110000, // 48 0x30
M: 0b110000, // 48 0x30
"!D": 0b001101, // 13 0x0D
"!A": 0b110001, // 49 0x31
"-D": 0b001111, // 15 0x0F
Expand All @@ -113,12 +109,9 @@ export const COMMANDS: {
"A-1": 0b110010, // 50 0x32
"D+A": 0b000010, // 2 0x02
"D-A": 0b010011, // 19 0x13
"D-M": 0b010011, // 19 0x13
"A-D": 0b000111, // 7 0x07
"D&A": 0b000000, // 0 0x00
"D&M": 0b000000, // 0 0x00
"D|A": 0b010101, // 21 0x15
"D|M": 0b010101, // 21 0x15
},
op: {
0x2a: "0",
Expand All @@ -140,6 +133,9 @@ export const COMMANDS: {
0x00: "D&A",
0x15: "D|A",
},
getOp(asm: string) {
return COMMANDS.asm[asm.replace("M", "A") as COMMANDS_ASM];
},
};

export type ASSIGN_ASM = "" | "M" | "D" | "MD" | "A" | "AM" | "AD" | "AMD";
Expand Down
10 changes: 7 additions & 3 deletions simulator/src/cpu/memory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,14 @@ export class Memory implements MemoryAdapter {
}

update(cell: number, value: string, format: Format) {
let current: number;
let current: number | undefined;
switch (format) {
case "asm":
current = op(value);
try {
current = op(value);
} catch {
current = undefined;
}
break;
case "bin":
current = int2(value);
Expand All @@ -85,7 +89,7 @@ export class Memory implements MemoryAdapter {
break;
}

if (isFinite(current) && current <= 0xffff) {
if (current !== undefined && isFinite(current) && current <= 0xffff) {
this.set(cell, current);
}
}
Expand Down
42 changes: 21 additions & 21 deletions simulator/src/languages/asm.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ describe("asm language", () => {
expect(match).toHaveSucceeded();
expect(asmSemantics(match).instruction).toEqual({
type: "C",
op: COMMANDS.asm["-1"],
op: COMMANDS.getOp("-1"),
isM: false,
});
});
Expand All @@ -44,7 +44,7 @@ describe("asm language", () => {
expect(match).toHaveSucceeded();
expect(asmSemantics(match).instruction).toEqual({
type: "C",
op: COMMANDS.asm["M"],
op: COMMANDS.getOp("M"),
store: ASSIGN.asm["D"],
isM: true,
});
Expand All @@ -55,7 +55,7 @@ describe("asm language", () => {
expect(match).toHaveSucceeded();
expect(asmSemantics(match).instruction).toEqual({
type: "C",
op: COMMANDS.asm["A+1"],
op: COMMANDS.getOp("A+1"),
store: ASSIGN.asm["M"],
isM: true,
});
Expand All @@ -66,7 +66,7 @@ describe("asm language", () => {
expect(match).toHaveSucceeded();
expect(asmSemantics(match).instruction).toEqual({
type: "C",
op: COMMANDS.asm["D"],
op: COMMANDS.getOp("D"),
jump: JUMP.asm["JEQ"],
isM: false,
});
Expand All @@ -77,7 +77,7 @@ describe("asm language", () => {
expect(match).toHaveSucceeded();
expect(asmSemantics(match).instruction).toEqual({
type: "C",
op: COMMANDS.asm["D"],
op: COMMANDS.getOp("D"),
jump: JUMP.asm["JEQ"],
store: ASSIGN.asm["A"],
isM: false,
Expand All @@ -92,59 +92,59 @@ describe("asm language", () => {
expect(instructions[0]).toEqual({ type: "A", label: "R0" });
expect(instructions[1]).toEqual({
type: "C",
op: COMMANDS.asm["M"],
op: COMMANDS.getOp("M"),
store: ASSIGN.asm["D"],
isM: true,
});
expect(instructions[2]).toEqual({ type: "A", label: "R1" });
expect(instructions[3]).toEqual({
type: "C",
op: COMMANDS.asm["D-M"],
op: COMMANDS.getOp("D-M"),
store: ASSIGN.asm["D"],
isM: true,
});
expect(instructions[4]).toEqual({ type: "A", label: "OUTPUT_FIRST" });
expect(instructions[5]).toEqual({
type: "C",
op: COMMANDS.asm["D"],
op: COMMANDS.getOp("D"),
jump: JUMP.asm["JGT"],
isM: false,
});
expect(instructions[6]).toEqual({ type: "A", label: "R1" });
expect(instructions[7]).toEqual({
type: "C",
op: COMMANDS.asm["M"],
op: COMMANDS.getOp("M"),
store: ASSIGN.asm["D"],
isM: true,
});
expect(instructions[8]).toEqual({ type: "A", label: "OUTPUT_D" });
expect(instructions[9]).toEqual({
type: "C",
op: COMMANDS.asm["0"],
op: COMMANDS.getOp("0"),
jump: JUMP.asm["JMP"],
isM: false,
});
expect(instructions[10]).toEqual({ type: "L", label: "OUTPUT_FIRST" });
expect(instructions[11]).toEqual({ type: "A", label: "R0" });
expect(instructions[12]).toEqual({
type: "C",
op: COMMANDS.asm["M"],
op: COMMANDS.getOp("M"),
store: ASSIGN.asm["D"],
isM: true,
});
expect(instructions[13]).toEqual({ type: "L", label: "OUTPUT_D" });
expect(instructions[14]).toEqual({ type: "A", label: "R2" });
expect(instructions[15]).toEqual({
type: "C",
op: COMMANDS.asm["D"],
op: COMMANDS.getOp("D"),
store: ASSIGN.asm["M"],
isM: true,
});
expect(instructions[16]).toEqual({ type: "L", label: "INFINITE_LOOP" });
expect(instructions[17]).toEqual({ type: "A", label: "INFINITE_LOOP" });
expect(instructions[18]).toEqual({
type: "C",
op: COMMANDS.asm["0"],
op: COMMANDS.getOp("0"),
jump: JUMP.asm["JMP"],
isM: false,
});
Expand All @@ -160,24 +160,24 @@ describe("asm language", () => {

expect(asm.instructions).toEqual([
{ type: "A", value: 0 },
{ type: "C", op: COMMANDS.asm["M"], store: ASSIGN.asm["D"], isM: true },
{ type: "C", op: COMMANDS.getOp("M"), store: ASSIGN.asm["D"], isM: true },
{ type: "A", value: 1 },
{ type: "C", op: COMMANDS.asm["D-M"], store: ASSIGN.asm["D"], isM: true },
{ type: "C", op: COMMANDS.getOp("D-M"), store: ASSIGN.asm["D"], isM: true },
{ type: "A", value: 10 },
{ type: "C", op: COMMANDS.asm["D"], jump: JUMP.asm["JGT"], isM: false },
{ type: "C", op: COMMANDS.getOp("D"), jump: JUMP.asm["JGT"], isM: false },
{ type: "A", value: 1 },
{ type: "C", op: COMMANDS.asm["M"], store: ASSIGN.asm["D"], isM: true },
{ type: "C", op: COMMANDS.getOp("M"), store: ASSIGN.asm["D"], isM: true },
{ type: "A", value: 12 },
{ type: "C", op: COMMANDS.asm["0"], jump: JUMP.asm["JMP"], isM: false },
{ type: "C", op: COMMANDS.getOp("0"), jump: JUMP.asm["JMP"], isM: false },
{ type: "L", label: "OUTPUT_FIRST" },
{ type: "A", value: 0 },
{ type: "C", op: COMMANDS.asm["M"], store: ASSIGN.asm["D"], isM: true },
{ type: "C", op: COMMANDS.getOp("M"), store: ASSIGN.asm["D"], isM: true },
{ type: "L", label: "OUTPUT_D" },
{ type: "A", value: 2 },
{ type: "C", op: COMMANDS.asm["D"], store: ASSIGN.asm["M"], isM: true },
{ type: "C", op: COMMANDS.getOp("D"), store: ASSIGN.asm["M"], isM: true },
{ type: "L", label: "INFINITE_LOOP" },
{ type: "A", value: 14 },
{ type: "C", op: COMMANDS.asm["0"], jump: JUMP.asm["JMP"], isM: false },
{ type: "C", op: COMMANDS.getOp("0"), jump: JUMP.asm["JMP"], isM: false },
]);
});

Expand Down
12 changes: 8 additions & 4 deletions simulator/src/languages/asm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
ASSIGN_ASM,
ASSIGN_OP,
COMMANDS,
COMMANDS_ASM,
COMMANDS_OP,
JUMP,
JUMP_ASM,
Expand Down Expand Up @@ -122,12 +121,12 @@ asmSemantics.addAttribute<AsmInstruction>("instruction", {
},
CInstruction(assignN, opN, jmpN): AsmCInstruction {
const assign = assignN.child(0)?.child(0)?.sourceString as ASSIGN_ASM;
const op = opN.sourceString.replace("M", "A") as COMMANDS_ASM;
const op = opN.sourceString;
const jmp = jmpN.child(0)?.child(1)?.sourceString as JUMP_ASM;
const isM = opN.sourceString.includes("M");
const inst: AsmCInstruction = {
type: "C",
op: COMMANDS.asm[op],
op: COMMANDS.getOp(op),
isM,
span: {
start: assignN.source.startIdx,
Expand Down Expand Up @@ -227,7 +226,12 @@ export function translateInstruction(inst: AsmInstruction) {
return inst.value;
}
if (inst.type === "C") {
return makeC(inst.isM, inst.op, inst.store, inst.jump);
return makeC(
inst.isM,
inst.op,
(inst.store ?? "") as ASSIGN_OP,
(inst.jump ?? "") as ASSIGN_OP
);
}
return undefined;
}
Expand Down
12 changes: 6 additions & 6 deletions simulator/src/util/asm.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@ describe("asm", () => {
});

it("makes C instruction", () => {
expect(makeC(true, COMMANDS.asm["D"], ASSIGN.asm["M"], JUMP.asm[""])).toBe(
0b111_1_001100_001_000
);
expect(
makeC(true, COMMANDS.asm["D-M"], ASSIGN.asm["D"], JUMP.asm[""])
makeC(true, COMMANDS.getOp("D"), ASSIGN.asm["M"], JUMP.asm[""])
).toBe(0b111_1_001100_001_000);
expect(
makeC(true, COMMANDS.getOp("D-M"), ASSIGN.asm["D"], JUMP.asm[""])
).toBe(0b111_1_010011_010_000);
expect(
makeC(false, COMMANDS.asm["D"], ASSIGN.asm[""], JUMP.asm["JGT"])
makeC(false, COMMANDS.getOp("D"), ASSIGN.asm[""], JUMP.asm["JGT"])
).toBe(0b111_0_001100_000_001);
expect(
makeC(false, COMMANDS.asm["0"], ASSIGN.asm[""], JUMP.asm["JMP"])
makeC(false, COMMANDS.getOp("0"), ASSIGN.asm[""], JUMP.asm["JMP"])
).toBe(0b111_0_101010_000_111);
});
});
29 changes: 19 additions & 10 deletions simulator/src/util/asm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,28 +69,37 @@ function aop(asm: string): number {
}

function cop(asm: string): number {
const firstPass = asm.match(
/(?:(?<assignExists>.+)=)?(.+)(?:;(?<jumpExists>.+))?/
);
const { assignExists, jumpExists } = firstPass?.groups ?? {};

const parts = asm.match(
/(?:(?<assign>[AMD]{1,3})=)?(?<operation>[-!01ADM&|]{1,3})(?:;(?<jump>JGT|JLT|JGE|JLE|JEQ|JMP))?/
/(?:(?<assign>[AMD]{1,3})=)?(?<operation>[-+!01ADM&|]{1,3})(?:;(?<jump>JGT|JLT|JGE|JLE|JEQ|JMP))?/
);
const { assign, operation, jump } = parts?.groups ?? {};
const mode = operation.includes("M") || assign.includes("M");
const aop = ASSIGN.asm[(assign as ASSIGN_ASM) ?? ""];
const jop = JUMP.asm[(jump as JUMP_ASM) ?? ""];
const cop = COMMANDS.asm[(operation as COMMANDS_ASM) ?? ""];
const mode = operation.includes("M") || (assign?.includes("M") ?? false);
const aop =
ASSIGN.asm[(assign ?? (assignExists ? undefined : "")) as ASSIGN_ASM];
const jop = JUMP.asm[(jump ?? (jumpExists ? undefined : "")) as JUMP_ASM];
const cop = COMMANDS.getOp(operation);

return makeC(mode, cop, aop, jop);
}

export function makeC(
isM: boolean,
op: COMMANDS_OP,
assign: ASSIGN_OP = 0,
jmp: JUMP_OP = 0
op: COMMANDS_OP | undefined,
assign: ASSIGN_OP | undefined,
jmp: JUMP_OP | undefined
): number {
if (op === undefined || assign === undefined || jmp === undefined) {
throw new Error("Invalid c instruction");
}
const C = 0xe000;
const A = isM ? 0x1000 : 0;
const O = op << 6;
const D = (assign ?? 0) << 3;
const J = jmp ?? 0;
const D = assign << 3;
const J = jmp;
return C + A + O + D + J;
}

0 comments on commit 40c8594

Please sign in to comment.