Skip to content
Open
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ The instructions would be intuitive and simple compared to MIPS (e.g., no system
## Getting Started
1. Ensure you have installed [Rust](https://www.rust-lang.org/learn/get-started) and [Node.js](https://nodejs.org/en/download)
2. Complete the [Tauri Prerequisites Installation](https://tauri.app/v1/guides/getting-started/prerequisites/#installing) process
3. Install tauri using `cargo install tauri-cli wasm-pack` in a terminal emulator
3. Install tauri using `cargo install tauri-cli@^1.4.0 wasm-pack` in a terminal emulator
4. Clone this repository to your system
5. Open this repository in your terminal
6. Update npm using `npm install -g npm@latest`
7. Run `npm install` to install the node dependencies
8. Run the test suite to ensure everything works using `cargo test`
9. Run the CLI application by using `cargo run`
9. Run the CLI application by using `cargo run` (if this command causes an error skip it for now)
10. Run the tauri application in development mode using `cargo tauri dev`
11. Test the WebAssembly code by going to http://localhost:1420 while the tauri application is open
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.20.0",
"react-split-pane": "^0.1.92",
"vite-plugin-top-level-await": "^1.3.1",
"vite-plugin-wasm": "^3.2.2",
"vite-plugin-wasm-pack": "^0.1.12",
Expand All @@ -30,18 +31,18 @@
"devDependencies": {
"@babel/core": "^7.22.17",
"@tauri-apps/cli": "^1.4.0",
"eslint-plugin-react": "^7.33.2",
"lodash": "^4.17.21",
"npm-run-all": "^4.1.5",
"tailwindcss": "^3.3.3",
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
"@typescript-eslint/eslint-plugin": "^7.2.0",
"@typescript-eslint/parser": "^7.2.0",
"@vitejs/plugin-react": "^4.2.1",
"eslint": "^8.57.0",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.6",
"lodash": "^4.17.21",
"npm-run-all": "^4.1.5",
"tailwindcss": "^3.3.3",
"typescript": "^5.2.2",
"vite": "^5.2.0"
}
Expand Down
10 changes: 10 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ const HOME_PATH = "/";
const CODE_PATH = "/code/";
const DOWNLOAD_PATH = "/downloads/";


/*
The app itself is routed through the Home page, and through to the code which controls the various
ide like functions to navigate the code you write, the download path has not been fully worked out, but
the idea behind it is to work on adding the release versions of rezasm so that the user can choose what to use.

The code path leads to the component that possesses the code that initializes the Controls, the Console, and the Memory
Viewer. It also handles the style css so that the gui can be loaded in.
*/

function App() {
return (
<HashRouter future={{ v7_startTransition: true }}>
Expand Down
137 changes: 126 additions & 11 deletions src/components/Code.jsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import React, {useEffect, useState} from "react";
import React, { useEffect, useState, useRef } from "react";
import RegistryView from "./RegistryView.jsx";
import {loadWasm} from "../rust_functions.ts";
import {Tabs, Tab} from "./Tabs.jsx";
import { loadWasm } from "../rust_functions.ts";
import { Tabs, Tab } from "./Tabs.jsx";

import MemoryView from "./MemoryView.jsx";
import Console from "./Console.jsx";
import Controls from "./Controls.jsx";
import Editor from "./Editor.jsx";
import {useSimulator} from "./simulator.ts";
import { useSimulator } from "./simulator.ts";
// import { saveFile, loadFile } from "./SaveLoad.jsx";

function Code() {

/*
This is where the pieces of gui are initialized

The Code component handles the actual usage of the gui buttons created and applies them to the Tauri app
*/

function Code() {
const {
state,
error,
Expand All @@ -28,34 +35,142 @@ function Code() {
} = useSimulator();
const [wasmLoaded, setWasmLoaded] = useState(false);

// Create a ref to access the textarea in the Editor component
const editorRef = useRef(null);

useEffect(() => {
loadWasm()
.then((loaded) => setWasmLoaded(loaded))
.catch(() => setWasmLoaded(false));
}, []);

// Save file functionality
const saveFile = () => {
const code = editorRef.current?.value; // Access the textarea value using the ref
if (!code) {
alert("No code to save!");
return;
}

// Get the file name from the input field
const fileNameInput = document.getElementById("file-name");
const fileName = fileNameInput?.value || "code.txt"; // Default to "code.txt" if no name is provided

const blob = new Blob([code], { type: "text/plain" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = fileName; // Use the user-provided file name
a.click();
URL.revokeObjectURL(url);
};

// Load file functionality
const loadFile = (event) => {
const file = event.target.files[0];
if (!file) {
alert("No file selected!");
return;
}
const reader = new FileReader();
reader.onload = (e) => {
const content = e.target.result;
if (editorRef.current) {
editorRef.current.value = content; // Set the textarea value using the ref
setCode(content); // Update the code in the simulator state
}
};
reader.readAsText(file);
};

return (
<div>
<div className="fill px-4">
<Controls state={state} setState={setState} start={start} stop={stop} step={step} reset={reset} load={load} error={error} stepBack={stepBack}/>
{/* File Dropdown and Name Input Container */}
<div className="file-controls">
<input
type="text"
id="file-name"
placeholder="Enter file name"
className="file-name-input"
/>
<select
className="file-dropdown"
onChange={(e) => {
const selectedOption = e.target.value;

if (selectedOption === "save") {
saveFile(); // Trigger save functionality
} else if (selectedOption === "load") {
document.getElementById("file-input").click(); // Trigger file input for load
}

// Reset the dropdown to the default option
e.target.value = ""; // Set the value back to the default
}}
>
<option value="" disabled selected>
File options
</option>
<option value="save">Save Code</option>
<option value="load">Load Code</option>
</select>
{/* Hidden file input for loading files */}
<input
id="file-input"
type="file"
accept=".txt"
onChange={loadFile}
style={{ display: "none" }}
/>
</div>

{/* Controls Section */}
<div className="top-bar">
<Controls
state={state}
setState={setState}
start={start}
stop={stop}
step={step}
reset={reset}
load={load}
error={error}
stepBack={stepBack}
/>
</div>


<div className="fill px-4"> {/* initialize the editor */}
<div className="mt-2 mb-2 row codearea">
<div className="w-5/6 h-full pe-4">
<Editor state={state} setCode={setCode} />
{/* Pass the ref to the Editor component */}
<Editor state={state} setCode={setCode} editorRef={editorRef} />
</div>
<div className="w-1/6">
<RegistryView loaded={wasmLoaded} registerCallback={registerCallback} />
<RegistryView
loaded={wasmLoaded}
registerCallback={registerCallback}
/>
</div>
</div>
</div>
<Tabs>
<Tab label="Console">
<div className="fill" id="tabs_console" data-tab-active>
<Console loaded={wasmLoaded} registerCallback={registerCallback} exitCode={exitCode} error={error} />
<Console
loaded={wasmLoaded}
registerCallback={registerCallback}
exitCode={exitCode}
error={error}
/>
</div>
</Tab>
<Tab label="Memory Viewer">
<div className="fill" id="tabs_memory">
<MemoryView loaded={wasmLoaded} registerCallback={registerCallback} />
<MemoryView
loaded={wasmLoaded}
registerCallback={registerCallback}
/>
</div>
</Tab>
</Tabs>
Expand Down
33 changes: 23 additions & 10 deletions src/components/Controls.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,26 @@ import React from "react";
import {STATE} from "./simulator.ts";
import _ from "lodash";

// Debounce function to limit the rate at which a function is executed
const debounce =
_.debounce((func) => func(), 250, {leading: true, trailing: false, maxWait: 250});

/**
* This component creates the GUI buttons for each control function and also manages the state transitions
* such as idle, stop, running, etc.
*/
function Controls({state, setState, start, stop, step, stepBack, reset, load, error}) {
// Check if the current state is an error state
const isErrorState = error.current !== "";

return (
<div className="mt-2 mb-2 row">
{/* Start/Stop Button */}
{state.current === STATE.RUNNING ?
<button className="btn-operation bg-red-500 hover:bg-red-700"
disabled={state.current !== STATE.RUNNING || isErrorState}
onClick={() => {
debounce(stop);
debounce(stop); // Stop the simulation
}}>
Stop
</button>
Expand All @@ -23,50 +30,56 @@ function Controls({state, setState, start, stop, step, stepBack, reset, load, er
disabled={(state.current !== STATE.IDLE && state.current !== STATE.STOPPED) || isErrorState}
onClick={() => {
debounce(() => {
reset()
.then(() => load())
.then(() => start());
reset() // Reset the simulation
.then(() => load()) // Load the initial state
.then(() => start()); // Start the simulation
});
}}>
Start
</button>
}

{/* Pause/Resume Button */}
{state.current === STATE.PAUSED ?
<button className="btn-operation bg-emerald-600 hover:bg-emerald-700"
onClick={() => {
setState(STATE.RUNNING);
debounce(start);
setState(STATE.RUNNING); // Change state to running
debounce(start); // Resume the simulation
}}>
Resume
</button>
:
<button className="btn-operation bg-cyan-600 hover:bg-cyan-700"
disabled={state.current !== STATE.RUNNING}
onClick={() => {
setState(STATE.PAUSED);
setState(STATE.PAUSED); // Pause the simulation
}}>
Pause
</button>
}

{/* Step Button */}
<button className="btn-operation bg-blue-500 hover:bg-blue-700"
disabled={(state.current !== STATE.PAUSED && state.current !== STATE.IDLE) || isErrorState}
onClick={() => {
debounce(step);
debounce(step); // Execute a single step in the simulation
}}>
Step
</button>

{/* Step Back Button */}
<button className="btn-operation bg-teal-600 hover:bg-teal-700"
disabled={state.current !== STATE.PAUSED}
onClick={() => {
debounce(stepBack)
debounce(stepBack); // Go back one step in the simulation
}}>
Step Back
</button>

{/* Reset Button */}
<button className="btn-operation bg-orange-500 hover:bg-orange-700"
onClick={() => {
debounce(reset);
debounce(reset); // Reset the simulation to its initial state
}}>
Reset
</button>
Expand Down
Loading