Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose vm page + implement OS #233

Merged
merged 48 commits into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
02253c6
Make vm page accessible
netalondon Dec 24, 2023
5035a44
Make runbar visible
netalondon Dec 25, 2023
15ddf69
Add option to load file
netalondon Dec 25, 2023
1c46903
Update current tool on page enter
netalondon Dec 25, 2023
1d9f1d0
Add option to load and use test files
netalondon Dec 25, 2023
9b0e3b7
Rename Xor files to match chip name
netalondon Dec 25, 2023
ba2d01a
Add versioning system to update files automatically
netalondon Dec 25, 2023
c89379c
Merge branch 'fix-xor-menu' into vm
netalondon Dec 25, 2023
d6afce8
Merge remote-tracking branch 'upstream/main' into vm
netalondon Dec 28, 2023
3272057
Merge branch 'vm' into os
netalondon Jan 22, 2024
4d9884b
Add math builtins
netalondon Jan 22, 2024
cbbf53f
Implement screen os library
netalondon Jan 22, 2024
54b1775
Fix imports
netalondon Jan 23, 2024
b93935e
Disable animate for high speeds
netalondon Jan 23, 2024
5f30b8b
Implement most of the synchronous os functions
netalondon Jan 23, 2024
e237cd9
Merge remote-tracking branch 'upstream/main' into os
netalondon Jan 23, 2024
3e40915
Add font
netalondon Jan 24, 2024
3fcc1ec
Implement blocking operations (keybaord, sys.wait)
netalondon Jan 24, 2024
0fb4a36
Complete sys library
netalondon Jan 24, 2024
2951635
Allow loading multiple vm files
netalondon Jan 24, 2024
733c08b
Add os error handling
netalondon Jan 25, 2024
20166cb
Correctly load compare files for vm tests
netalondon Jan 25, 2024
b912210
Show correct stack frame values for entry function
netalondon Jan 25, 2024
7b1b9e0
Don't add implicit if there is a single function
netalondon Jan 25, 2024
806b3f2
Remove unused bootstrap function
netalondon Jan 25, 2024
35d4f5f
Don't push frame for entry function
netalondon Jan 25, 2024
9db1bfe
Perform final return (for tests to pass)
netalondon Jan 25, 2024
3bce242
Add vm parse errors
netalondon Jan 28, 2024
adaaca5
Make illegal offsets a build error
netalondon Jan 28, 2024
8227630
Handle negative numbers correctly
netalondon Jan 28, 2024
08d2338
Raise errors for illegal nArgs and nVars
netalondon Jan 28, 2024
499761e
Fix error message
netalondon Jan 28, 2024
3db0c32
Fix test setVar bug
netalondon Jan 28, 2024
4b5f24d
Remove comments
netalondon Jan 28, 2024
8b16783
Add out of segment space errors
netalondon Jan 28, 2024
9b79ddc
Merge remote-tracking branch 'upstream/main' into os
netalondon Feb 19, 2024
c798566
Fix formatting
netalondon Feb 19, 2024
5d0fce5
Don't throw out of bounds error when settings segments using the test…
netalondon Feb 19, 2024
a3776bb
Remove comment
netalondon Feb 23, 2024
5b81618
Add second RAM view (vm page)
netalondon Feb 25, 2024
4380b46
Setup error messages for translation
netalondon Feb 26, 2024
d159984
Fixed undefined function error message
netalondon Feb 26, 2024
93e78de
Replace setInterval and setTimeout with requestAnimationFrame
netalondon Feb 26, 2024
605869a
Make requested changes
netalondon Feb 26, 2024
350f763
Fix formatting
netalondon Feb 26, 2024
d9b3e6b
Merge remote-tracking branch 'upstream/main' into os
netalondon Feb 26, 2024
b8db2fe
Display font explicitly
netalondon Feb 26, 2024
e9d1cc7
Hide vm page
netalondon Feb 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 132 additions & 19 deletions components/src/stores/vm.store.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import { FileSystem } from "@davidsouther/jiffies/lib/esm/fs.js";
// import { VM as multVM } from "@nand2tetris/simulator/testing/mult.js";
import { isErr, unwrap } from "@davidsouther/jiffies/lib/esm/result.js";
import { FIBONACCI } from "@nand2tetris/projects/samples/vm.js";
import { Dispatch, MutableRefObject, useContext, useMemo, useRef } from "react";
import { BaseContext } from "./base.context.js";
import { useImmerReducer } from "../react.js";
import {
KeyboardAdapter,
MemoryAdapter,
MemoryKeyboard,
} from "@nand2tetris/simulator/cpu/memory.js";
import { VMTest } from "@nand2tetris/simulator/test/vmtst.js";
import { Span } from "@nand2tetris/simulator/languages/base.js";
import { TST } from "@nand2tetris/simulator/languages/tst.js";
import { VM, VmInstruction } from "@nand2tetris/simulator/languages/vm.js";
import { VMTest } from "@nand2tetris/simulator/test/vmtst.js";
import { Vm, VmFrame } from "@nand2tetris/simulator/vm/vm.js";
import { Dispatch, MutableRefObject, useContext, useMemo, useRef } from "react";
import { compare } from "../compare.js";
import { useImmerReducer } from "../react.js";
import { BaseContext } from "./base.context.js";
import { ImmMemory } from "./imm_memory.js";
import { unwrap } from "@davidsouther/jiffies/lib/esm/result.js";
import { VmFrame, Vm } from "@nand2tetris/simulator/vm/vm.js";
import { Span } from "@nand2tetris/simulator/languages/base.js";

export interface VmSim {
RAM: MemoryAdapter;
Expand All @@ -34,27 +36,38 @@ export interface VmPageState {
vm: VmSim;
controls: ControlsState;
test: VMTestSim;
files: VMFiles;
}

export interface ControlsState {
runningTest: boolean;
error: string;
exitCode: number | undefined;
animate: boolean;
}

export interface VMFiles {
tst: string;
cmp: string;
out: string;
}

export type VmStoreDispatch = Dispatch<{
action: keyof ReturnType<typeof makeVmStore>["reducers"];
payload?: unknown;
}>;

export interface VmFile {
name: string;
content: string;
}

function reduceVMTest(
vmTest: VMTest,
dispatch: MutableRefObject<VmStoreDispatch>
): VmSim {
const RAM = new ImmMemory(vmTest.vm.RAM, dispatch);
const Screen = new ImmMemory(vmTest.vm.Screen, dispatch);
const Keyboard = new MemoryKeyboard(
new ImmMemory(vmTest.vm.Keyboard, dispatch)
);
const Keyboard = new MemoryKeyboard(new ImmMemory(vmTest.vm.RAM, dispatch));
const highlight = vmTest.vm.derivedLine();

return {
Expand All @@ -74,45 +87,145 @@ export function makeVmStore(
dispatch: MutableRefObject<VmStoreDispatch>
) {
const parsed = unwrap(VM.parse(FIBONACCI));
const vm = unwrap(Vm.build(parsed.instructions));
const test = new VMTest().with(vm);
let vm = unwrap(Vm.build(parsed.instructions));
let test = new VMTest().with(vm);
let useTest = false;
let animate = true;
const reducers = {
setTst(state: VmPageState, { tst, cmp }: { tst: string; cmp?: string }) {
state.files.tst = tst;
state.files.cmp = cmp ?? "";
},
setExitCode(state: VmPageState, code: number | undefined) {
state.controls.exitCode = code;
console.log("exit code", code);
},
update(state: VmPageState) {
state.vm = reduceVMTest(test, dispatch);
state.test.useTest = useTest;
state.test.highlight = test.currentStep?.span;
},
setAnimate(state: VmPageState, value: boolean) {
state.controls.animate = value;
},
testStep(state: VmPageState) {
state.files.out = test.log();
},
testFinished(state: VmPageState) {
const passed = compare(state.files.cmp.trim(), state.files.out);
setStatus(
passed
? `Simulation successful: The output file is identical to the compare file`
: `Simulation error: The output file differs from the compare file`
);
},
};
const initialState: VmPageState = {
vm: reduceVMTest(test, dispatch),
controls: {
error: "",
exitCode: undefined,
runningTest: false,
animate: true,
},
test: {
useTest,
highlight: undefined,
},
files: {
tst: "",
cmp: "",
out: "",
},
};
const actions = {
step() {
if (useTest) {
test.step();
} else {
vm.step();
loadVm(files: VmFile[]) {
const parsed = [];

for (const file of files) {
const parseResult = VM.parse(file.content);

if (isErr(parseResult)) {
setStatus(`Parse error: ${parseResult.err.message}`);
return false;
}
parsed.push({
name: file.name,
instructions: unwrap(parseResult).instructions,
});
}
const buildResult = Vm.buildFromFiles(parsed);

if (isErr(buildResult)) {
setStatus(`Build Error: ${buildResult.err.message}`);
return false;
}

vm = unwrap(buildResult);
test.vm = vm;
test.reset();
dispatch.current({ action: "update" });
return true;
},
loadTest(source: string, cmp?: string) {
dispatch.current({ action: "setTst", payload: { tst: source, cmp } });
const tst = TST.parse(source);

if (isErr(tst)) {
setStatus(`Failed to parse test`);
return false;
}
setStatus(`Parsed tst`);

vm.reset();
test = VMTest.from(unwrap(tst));
test.vm = vm;
dispatch.current({ action: "update" });
return true;
},
setAnimate(value: boolean) {
animate = value;
dispatch.current({ action: "setAnimate", payload: value });
},
step() {
try {
let done = false;
if (useTest) {
done = test.step();
dispatch.current({ action: "testStep" });
if (done) {
dispatch.current({ action: "testFinished" });
}
} else {
const exitCode = vm.step();
if (exitCode !== undefined) {
done = true;
dispatch.current({ action: "setExitCode", payload: exitCode });
}
}
if (animate) {
dispatch.current({ action: "update" });
}
return done;
} catch (e) {
setStatus(`Runtime error: ${(e as Error).message}`);
return true;
}
},
reset() {
test.reset();
vm.reset();
dispatch.current({ action: "update" });
dispatch.current({ action: "setExitCode", payload: undefined });
setStatus("Reset");
},
toggleUseTest() {
useTest = !useTest;
dispatch.current({ action: "update" });
},
initialize() {
this.loadTest("repeat {\n\tvmstep;\n}", "");
dispatch.current({ action: "update" });
},
};

return { initialState, reducers, actions };
Expand Down
22 changes: 11 additions & 11 deletions simulator/src/cpu/memory.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { assert } from "@davidsouther/jiffies/lib/esm/assert.js";
import { FileSystem } from "@davidsouther/jiffies/lib/esm/fs.js";
import { load } from "../fs.js";
import { op } from "../util/asm.js";
import { int10, int16, int2 } from "../util/twos.js";
import { load } from "../fs.js";

export const FORMATS = ["bin", "dec", "hex", "asm"];
export type Format = typeof FORMATS[number];

export const SCREEN_OFFSET = 0x4000;
export const SCREEN_ROWS = 512;
export const SCREEN_COLS = 256;
export const SCREEN_ROWS = 256;
export const SCREEN_COLS = 32; // These are 16-bit columns
export const SCREEN_SIZE = SCREEN_ROWS * SCREEN_COLS;
export const KEYBOARD_OFFSET = 0x6000;

Expand All @@ -19,8 +19,8 @@ export interface MemoryAdapter {
set(index: number, value: number): void;
reset(): void;
update(cell: number, value: string, format: Format): void;
load(fs: FileSystem, path: string): Promise<void>;
loadBytes(bytes: number[]): void;
load(fs: FileSystem, path: string, offset?: number): Promise<void>;
loadBytes(bytes: number[], offset?: number): void;
range(start?: number, end?: number): number[];
map<T>(
fn: (index: number, value: number) => T,
Expand Down Expand Up @@ -95,17 +95,17 @@ export class Memory implements MemoryAdapter {
}
}

async load(fs: FileSystem, path: string) {
async load(fs: FileSystem, path: string, offset?: number) {
try {
this.loadBytes(await load(fs, path));
this.loadBytes(await load(fs, path), offset);
} catch (cause) {
// throw new Error(`ROM32K Failed to load file ${path}`, { cause });
throw new Error(`Memory Failed to load file ${path}`);
}
}

loadBytes(bytes: number[]): void {
this.memory.set(new Int16Array(bytes));
loadBytes(bytes: number[], offset?: number): void {
this.memory.set(new Int16Array(bytes), offset);
this.memory.fill(0, bytes.length, this.size);
}

Expand Down Expand Up @@ -162,11 +162,11 @@ export class SubMemory implements MemoryAdapter {
}

load(fs: FileSystem, path: string): Promise<void> {
return this.parent.load(fs, path);
return this.parent.load(fs, path, this.offset);
}

loadBytes(bytes: number[]): void {
return this.parent.loadBytes(bytes);
return this.parent.loadBytes(bytes, this.offset);
}

range(start?: number, end?: number): number[] {
Expand Down
12 changes: 6 additions & 6 deletions simulator/src/languages/vm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,11 @@ vmSemantics.addAttribute<
});

vmSemantics.addAttribute<VmInstruction>("instruction", {
StackInstruction({ op }, { segment }, { value }) {
StackInstruction({ op }, { segment }, value) {
return {
op: op as "push" | "pop",
segment,
offset: value,
offset: Number(value.sourceString),
};
},
OpInstruction({ op }) {
Expand All @@ -188,11 +188,11 @@ vmSemantics.addAttribute<VmInstruction>("instruction", {
| "not",
};
},
FunctionInstruction(_, { name }, { value: nArgs }) {
return { op: "function", name, nVars: nArgs };
FunctionInstruction(_, { name }, nVars) {
return { op: "function", name, nVars: Number(nVars.sourceString) };
},
CallInstruction(_, { name }, { value: nArgs }) {
return { op: "call", name, nArgs };
CallInstruction(_, { name }, nArgs) {
return { op: "call", name, nArgs: Number(nArgs.sourceString) };
},
ReturnInstruction(_) {
return { op: "return" };
Expand Down
3 changes: 3 additions & 0 deletions simulator/src/test/vmtst.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export class VMTest extends Test<VMTestInstruction> {
}
if (variable === "RAM" && index !== undefined) {
this.vm.RAM.set(index, value);
return;
}
if (index !== undefined) {
this.vm.memory.setSegment(variable as Segment, index, value);
Expand All @@ -84,10 +85,12 @@ export class VMTest extends Test<VMTestInstruction> {
case "arg":
case "argument":
this.vm.memory.ARG = value;
this.vm.entryArgInitialized = true;
break;
case "lcl":
case "local":
this.vm.memory.LCL = value;
this.vm.entryLocalInitialized = true;
break;
case "this":
this.vm.memory.THIS = value;
Expand Down
Loading
Loading