Webpack copy. add windows
This commit is contained in:
parent
8a38dded92
commit
1f6a95c253
194
package-lock.json
generated
194
package-lock.json
generated
|
@ -7,6 +7,7 @@
|
|||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||
"@typescript-eslint/parser": "^6.0.0",
|
||||
"copy-webpack-plugin": "^12.0.2",
|
||||
"css-loader": "^6.10.0",
|
||||
"eslint": "^8.44.0",
|
||||
"sass": "^1.71.0",
|
||||
|
@ -262,6 +263,18 @@
|
|||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@sindresorhus/merge-streams": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz",
|
||||
"integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/eslint": {
|
||||
"version": "8.56.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.2.tgz",
|
||||
|
@ -753,6 +766,45 @@
|
|||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv-formats": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
|
||||
"integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ajv": "^8.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ajv": "^8.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"ajv": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/ajv-formats/node_modules/ajv": {
|
||||
"version": "8.12.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
|
||||
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv-formats/node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ajv-keywords": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
|
||||
|
@ -1028,6 +1080,127 @@
|
|||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/copy-webpack-plugin": {
|
||||
"version": "12.0.2",
|
||||
"resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-12.0.2.tgz",
|
||||
"integrity": "sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fast-glob": "^3.3.2",
|
||||
"glob-parent": "^6.0.1",
|
||||
"globby": "^14.0.0",
|
||||
"normalize-path": "^3.0.0",
|
||||
"schema-utils": "^4.2.0",
|
||||
"serialize-javascript": "^6.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18.12.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"webpack": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/copy-webpack-plugin/node_modules/ajv": {
|
||||
"version": "8.12.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
|
||||
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/copy-webpack-plugin/node_modules/ajv-keywords": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
|
||||
"integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ajv": "^8.8.2"
|
||||
}
|
||||
},
|
||||
"node_modules/copy-webpack-plugin/node_modules/globby": {
|
||||
"version": "14.0.1",
|
||||
"resolved": "https://registry.npmjs.org/globby/-/globby-14.0.1.tgz",
|
||||
"integrity": "sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@sindresorhus/merge-streams": "^2.1.0",
|
||||
"fast-glob": "^3.3.2",
|
||||
"ignore": "^5.2.4",
|
||||
"path-type": "^5.0.0",
|
||||
"slash": "^5.1.0",
|
||||
"unicorn-magic": "^0.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/copy-webpack-plugin/node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/copy-webpack-plugin/node_modules/path-type": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz",
|
||||
"integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/copy-webpack-plugin/node_modules/schema-utils": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz",
|
||||
"integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.9",
|
||||
"ajv": "^8.9.0",
|
||||
"ajv-formats": "^2.1.1",
|
||||
"ajv-keywords": "^5.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12.13.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/copy-webpack-plugin/node_modules/slash": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz",
|
||||
"integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=14.16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
|
@ -2474,6 +2647,15 @@
|
|||
"node": ">= 10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/require-from-string": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.8",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
|
||||
|
@ -2985,6 +3167,18 @@
|
|||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/unicorn-magic": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz",
|
||||
"integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/update-browserslist-db": {
|
||||
"version": "1.0.13",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||
"@typescript-eslint/parser": "^6.0.0",
|
||||
"copy-webpack-plugin": "^12.0.2",
|
||||
"css-loader": "^6.10.0",
|
||||
"eslint": "^8.44.0",
|
||||
"sass": "^1.71.0",
|
||||
|
|
|
@ -72,6 +72,12 @@ export enum UiEvent {
|
|||
// Ui Events
|
||||
EditOn,
|
||||
EditOff,
|
||||
ConsoleOn,
|
||||
ConsoleOff,
|
||||
ExplainerOn,
|
||||
ExplainerOff,
|
||||
VideoOn,
|
||||
VideoOff,
|
||||
}
|
||||
|
||||
interface UiEventMap {
|
||||
|
|
BIN
src/include/explainer.png
Normal file
BIN
src/include/explainer.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.3 KiB |
38
src/include/index.html
Normal file
38
src/include/index.html
Normal file
|
@ -0,0 +1,38 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<script src="main.js"></script>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Virtual 8-Bit Computer</title>
|
||||
</head>
|
||||
<body id="root">
|
||||
<main>
|
||||
<div id="grid">
|
||||
<div id="title">VIRTUAL 8-BIT COMPUTER</div>
|
||||
<div id="registers"></div>
|
||||
<div id="labelcontainer">
|
||||
<div id="registers_label">←REGISTERS</div>
|
||||
<div id="memory_label">MEMORY↯</div>
|
||||
</div>
|
||||
<div id="memory"></div>
|
||||
|
||||
<div id="controls_bar">
|
||||
<span id="controls_buttons"></span>
|
||||
<label for="binary_upload" class="button">Load Binary</label>
|
||||
<input id="binary_upload" name="binary_upload" id="binary_upload" style="display: none" type="file" />
|
||||
<button type="button" id="save_button">Save</button>
|
||||
<button type="button" id="edit_button"></button>
|
||||
</div>
|
||||
<span id="cycles"></span>
|
||||
</div>
|
||||
<div id="window_box">
|
||||
<div id="instruction_explainer"></div>
|
||||
<div id="printout"></div>
|
||||
<div id="tv"></div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<pre id="ISA"></pre>
|
||||
</body>
|
||||
</html>
|
BIN
src/include/pencil.png
Normal file
BIN
src/include/pencil.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.3 KiB |
BIN
src/include/texout.png
Normal file
BIN
src/include/texout.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
BIN
src/include/tv.png
Normal file
BIN
src/include/tv.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
|
@ -10,7 +10,7 @@ import { generate_isa } from "./isaGenerator";
|
|||
import { UI } from "./ui";
|
||||
import { u8 } from "./num";
|
||||
|
||||
import "./style.scss";
|
||||
import "./style/style.scss";
|
||||
import { CpuEvent } from "./events";
|
||||
|
||||
declare global {
|
||||
|
|
310
src/style.scss
310
src/style.scss
|
@ -1,310 +0,0 @@
|
|||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-size: 0.5em;
|
||||
}
|
||||
|
||||
:root {
|
||||
--Border: #ffff00;
|
||||
--mem-instruction: #3af78f;
|
||||
--mem-memory: #ff26a8;
|
||||
--mem-register: #9e0ef7;
|
||||
--mem-constant: #19f7f0;
|
||||
--mem-invalid: #bf2e2e;
|
||||
}
|
||||
|
||||
img {
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
|
||||
body {
|
||||
color: #808080;
|
||||
background-color: black;
|
||||
font-size: 2.5em;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
#labelcontainer {
|
||||
column-gap: 18px;
|
||||
font-size: 0.85em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
grid-area: regmemlabel;
|
||||
}
|
||||
|
||||
#main {
|
||||
justify-content: center;
|
||||
display: grid;
|
||||
grid-template-columns: min-content max-content max-content min-content 500px;
|
||||
grid-template-rows: min-content 1.5fr 10px 2fr 2fr min-content;
|
||||
grid-template-areas:
|
||||
"cycles registers regmemlabel . explainer "
|
||||
"title . . ribbon explainer "
|
||||
"title . . ribbon ."
|
||||
"title . . ribbon printout "
|
||||
"title . . . printout "
|
||||
". buttons buttons . .";
|
||||
#memory {
|
||||
grid-column: 2/4;
|
||||
grid-row: 2/6;
|
||||
}
|
||||
#cycles {
|
||||
grid-area: cycles;
|
||||
text-align: left;
|
||||
align-self: center;
|
||||
font-size: 0.48em;
|
||||
}
|
||||
#title {
|
||||
grid-area: title;
|
||||
writing-mode: vertical-lr;
|
||||
text-align: left;
|
||||
user-select: none;
|
||||
transform: scale(-1, -1);
|
||||
}
|
||||
#ribbon_menu {
|
||||
grid-area: ribbon;
|
||||
}
|
||||
}
|
||||
|
||||
#printout {
|
||||
border: 4px dashed var(--Border);
|
||||
grid-area: printout;
|
||||
padding: 10px;
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
max-height: 200px;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
|
||||
#instruction_explainer {
|
||||
grid-area: explainer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
border: 5px dashed var(--Border);
|
||||
#expl_box {
|
||||
padding-inline: 20px;
|
||||
padding-block-start: 10px;
|
||||
}
|
||||
|
||||
#expl_text {
|
||||
font-size: 0.6em;
|
||||
text-align: center;
|
||||
vertical-align: center;
|
||||
}
|
||||
|
||||
#expl_icon {
|
||||
margin-inline-end: 0.5em;
|
||||
font-size: 30px;
|
||||
padding: 5px;
|
||||
color: var(--color);
|
||||
outline: 3px dashed var(--color);
|
||||
}
|
||||
}
|
||||
|
||||
.invalid {
|
||||
--color: var(--mem-invalid);
|
||||
}
|
||||
.constant {
|
||||
--color: var(--mem-constant);
|
||||
}
|
||||
.register {
|
||||
--color: var(--mem-register);
|
||||
}
|
||||
.memory {
|
||||
--color: var(--mem-memory);
|
||||
}
|
||||
.instruction {
|
||||
--color: var(--mem-instruction);
|
||||
}
|
||||
|
||||
div[contenteditable] {
|
||||
&.caret_selected {
|
||||
box-sizing: border-box;
|
||||
outline: 2px solid red;
|
||||
}
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.pending_edit {
|
||||
color: green;
|
||||
}
|
||||
#memory {
|
||||
grid-area: memory;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(16, min-content);
|
||||
gap: 5px;
|
||||
padding: 10px;
|
||||
border: 5px solid yellow;
|
||||
|
||||
div {
|
||||
user-select: none;
|
||||
caret-color: transparent;
|
||||
text-align: center;
|
||||
color: var(--color);
|
||||
}
|
||||
|
||||
.program_counter {
|
||||
outline: 3px solid orange;
|
||||
}
|
||||
|
||||
.instruction_argument,
|
||||
.current_instruction {
|
||||
outline: 3px dashed var(--color);
|
||||
}
|
||||
.recent_edit {
|
||||
color: lime;
|
||||
}
|
||||
div.last_access {
|
||||
color: orange;
|
||||
}
|
||||
|
||||
.invalid {
|
||||
&::after {
|
||||
user-select: none;
|
||||
float: right;
|
||||
position: relative;
|
||||
right: 0.5em;
|
||||
width: 0px;
|
||||
font-size: 0.5em;
|
||||
content: "!";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div#main.editor {
|
||||
#memory,
|
||||
#registers {
|
||||
border-style: dashed;
|
||||
div {
|
||||
cursor: text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ribbon_menu {
|
||||
margin-inline: 8px;
|
||||
.editor_toggle {
|
||||
//TODO CHANGE COLORS WHen
|
||||
&.off {
|
||||
}
|
||||
&.on {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#registers {
|
||||
grid-area: registers;
|
||||
border: 5px solid yellow;
|
||||
border-bottom: none !important;
|
||||
|
||||
grid-template-columns: repeat(8, min-content);
|
||||
max-width: fit-content;
|
||||
display: grid;
|
||||
column-gap: 5px;
|
||||
color: lightgray;
|
||||
padding: 10px;
|
||||
|
||||
div {
|
||||
max-height: min-content;
|
||||
// margin-block: auto;
|
||||
}
|
||||
}
|
||||
|
||||
button,
|
||||
label.button {
|
||||
border: 4px solid yellow;
|
||||
color: gray;
|
||||
margin: 10px;
|
||||
margin-inline: 0px;
|
||||
padding: 10px;
|
||||
font-size: 0.8em;
|
||||
font-family: monospace;
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
border: 4px solid rgb(255, 255, 128);
|
||||
color: orange;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
input[type="range"]:disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
button.no_style {
|
||||
border: none;
|
||||
color: inherit;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: inherit;
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
button:hover,
|
||||
label.button:hover {
|
||||
color: white;
|
||||
}
|
||||
|
||||
#controls_bar {
|
||||
grid-area: buttons;
|
||||
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
#controls_buttons {
|
||||
display: flex;
|
||||
gap: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
input[type="range"] {
|
||||
background-color: transparent;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
margin: 18px 0;
|
||||
}
|
||||
|
||||
input[type="range"]:focus {
|
||||
outline: none;
|
||||
}
|
||||
// 2024 and we still have to do this
|
||||
input[type="range"]::-webkit-slider-runnable-track {
|
||||
height: 0.5em;
|
||||
cursor: pointer;
|
||||
background: yellow;
|
||||
border-radius: 0px;
|
||||
}
|
||||
input[type="range"]::-webkit-slider-thumb {
|
||||
border: 4px solid yellow;
|
||||
background-color: black;
|
||||
height: 42px;
|
||||
width: 20px;
|
||||
border-radius: 0px;
|
||||
cursor: pointer;
|
||||
margin-top: -18px;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
input[type="range"]:focus::-webkit-slider-runnable-track {
|
||||
background: yellow;
|
||||
}
|
||||
input[type="range"]::-moz-range-track {
|
||||
height: 0.5em;
|
||||
cursor: pointer;
|
||||
background: yellow;
|
||||
border-radius: 0px;
|
||||
}
|
||||
input[type="range"]::-moz-range-thumb {
|
||||
border: 4px solid yellow;
|
||||
background-color: black;
|
||||
height: 36px;
|
||||
width: 16px;
|
||||
border-radius: 0px;
|
||||
cursor: pointer;
|
||||
}
|
101
src/style/buttons.scss
Normal file
101
src/style/buttons.scss
Normal file
|
@ -0,0 +1,101 @@
|
|||
@use "sass:math";
|
||||
|
||||
button,
|
||||
label.button {
|
||||
border: 4px solid yellow;
|
||||
color: gray;
|
||||
margin: 10px;
|
||||
margin-inline: 0px;
|
||||
padding: 10px;
|
||||
font-size: 0.8em;
|
||||
font-family: monospace;
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
border: 4px solid rgb(255, 255, 128);
|
||||
color: orange;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
input[type="range"]:disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
button.nostyle {
|
||||
border: none;
|
||||
color: inherit;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: inherit;
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
button:hover,
|
||||
label.button:hover {
|
||||
color: white;
|
||||
}
|
||||
|
||||
#controls_bar {
|
||||
grid-area: buttons;
|
||||
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
#controls_buttons {
|
||||
display: flex;
|
||||
gap: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
input[type="range"] {
|
||||
background-color: transparent;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
margin: 18px 0;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
input[type="range"]:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
// 2024 and we still have to do this
|
||||
|
||||
@mixin sliderTrack {
|
||||
height: 0.5em;
|
||||
cursor: pointer;
|
||||
background: yellow;
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
@mixin sliderThumb($height, $width) {
|
||||
border: 4px solid yellow;
|
||||
background-color: black;
|
||||
height: $height;
|
||||
width: $width;
|
||||
border-radius: 0px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input[type="range"]::-webkit-slider-runnable-track {
|
||||
@include sliderTrack;
|
||||
}
|
||||
input[type="range"]::-moz-range-track {
|
||||
@include sliderTrack;
|
||||
}
|
||||
|
||||
input[type="range"]::-webkit-slider-thumb {
|
||||
@include sliderThumb(42px, 20px);
|
||||
-webkit-appearance: none;
|
||||
margin-top: -18px;
|
||||
}
|
||||
|
||||
input[type="range"]::-moz-range-thumb {
|
||||
@include sliderThumb(36px, 16px);
|
||||
}
|
||||
|
||||
input[type="range"]:focus::-webkit-slider-runnable-track {
|
||||
background: yellow;
|
||||
}
|
69
src/style/memory_registers.scss
Normal file
69
src/style/memory_registers.scss
Normal file
|
@ -0,0 +1,69 @@
|
|||
#memory {
|
||||
grid-template-columns: repeat(16, min-content);
|
||||
|
||||
.program_counter {
|
||||
outline: 3px solid orange;
|
||||
}
|
||||
|
||||
.instruction_argument,
|
||||
.current_instruction {
|
||||
outline: 3px dashed var(--color);
|
||||
}
|
||||
|
||||
.invalid {
|
||||
&::after {
|
||||
user-select: none;
|
||||
float: right;
|
||||
position: relative;
|
||||
right: 0.5em;
|
||||
width: 0px;
|
||||
font-size: 0.5em;
|
||||
content: "!";
|
||||
}
|
||||
}
|
||||
}
|
||||
#registers {
|
||||
border-bottom: none !important;
|
||||
|
||||
grid-template-columns: repeat(8, min-content);
|
||||
color: lightgray;
|
||||
}
|
||||
|
||||
#labelcontainer {
|
||||
column-gap: 18px;
|
||||
font-size: 0.85em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
}
|
||||
.celled_viewer {
|
||||
display: grid;
|
||||
max-width: fit-content;
|
||||
gap: 5px;
|
||||
border: 5px solid yellow;
|
||||
padding: 10px;
|
||||
user-select: none;
|
||||
|
||||
div {
|
||||
.pending_edit {
|
||||
color: green;
|
||||
}
|
||||
&.recent_edit {
|
||||
color: lime;
|
||||
}
|
||||
&.last_access {
|
||||
color: orange;
|
||||
}
|
||||
user-select: none;
|
||||
caret-color: transparent;
|
||||
text-align: center;
|
||||
color: var(--color);
|
||||
&[contenteditable] {
|
||||
&.caret_selected {
|
||||
box-sizing: border-box;
|
||||
outline: 2px solid red;
|
||||
}
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
}
|
93
src/style/style.scss
Normal file
93
src/style/style.scss
Normal file
|
@ -0,0 +1,93 @@
|
|||
@use "memory_registers";
|
||||
@use "windows";
|
||||
@use "buttons";
|
||||
@use "vars";
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-size: 0.5em;
|
||||
}
|
||||
|
||||
img {
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
|
||||
body {
|
||||
color: #808080;
|
||||
background-color: black;
|
||||
font-size: 2.5em;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
main {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#grid {
|
||||
justify-content: center;
|
||||
display: grid;
|
||||
grid-template-columns: min-content min-content min-content;
|
||||
grid-template-rows: min-content min-content min-content;
|
||||
grid-template-areas:
|
||||
"cycles registers regmemlabel"
|
||||
"title memory memory"
|
||||
". buttons buttons ";
|
||||
#memory {
|
||||
grid-area: memory;
|
||||
// grid-column: 2/4;
|
||||
// grid-row: 2/6;
|
||||
}
|
||||
#window_box {
|
||||
grid-area: windowbox;
|
||||
}
|
||||
#registers {
|
||||
grid-area: registers;
|
||||
}
|
||||
#labelcontainer {
|
||||
grid-area: regmemlabel;
|
||||
}
|
||||
#cycles {
|
||||
grid-area: cycles;
|
||||
text-align: left;
|
||||
align-self: center;
|
||||
font-size: 0.48em;
|
||||
user-select: none;
|
||||
}
|
||||
#title {
|
||||
grid-area: title;
|
||||
writing-mode: vertical-lr;
|
||||
text-align: left;
|
||||
user-select: none;
|
||||
transform: scale(-1, -1);
|
||||
}
|
||||
}
|
||||
|
||||
.invalid {
|
||||
--color: var(--mem-invalid);
|
||||
}
|
||||
.constant {
|
||||
--color: var(--mem-constant);
|
||||
}
|
||||
.register {
|
||||
--color: var(--mem-register);
|
||||
}
|
||||
.memory {
|
||||
--color: var(--mem-memory);
|
||||
}
|
||||
.instruction {
|
||||
--color: var(--mem-instruction);
|
||||
}
|
||||
|
||||
div#main.editor {
|
||||
#memory,
|
||||
#registers {
|
||||
border-style: dashed;
|
||||
div {
|
||||
cursor: text;
|
||||
}
|
||||
}
|
||||
}
|
8
src/style/vars.scss
Normal file
8
src/style/vars.scss
Normal file
|
@ -0,0 +1,8 @@
|
|||
:root {
|
||||
--border: #ffff00;
|
||||
--mem-instruction: #3af78f;
|
||||
--mem-memory: #ff26a8;
|
||||
--mem-register: #9e0ef7;
|
||||
--mem-constant: #19f7f0;
|
||||
--mem-invalid: #bf2e2e;
|
||||
}
|
108
src/style/windows.scss
Normal file
108
src/style/windows.scss
Normal file
|
@ -0,0 +1,108 @@
|
|||
#resize {
|
||||
// background-color: red;
|
||||
width: 100%;
|
||||
height: 18px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
border-bottom: 5px solid yellow;
|
||||
|
||||
cursor: s-resize;
|
||||
}
|
||||
|
||||
#window_box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
margin-left: 10px;
|
||||
max-width: 500px;
|
||||
.window {
|
||||
overflow-y: hidden;
|
||||
position: relative;
|
||||
border: 5px Solid var(--border);
|
||||
border-bottom: unset;
|
||||
&.collapsed {
|
||||
.window_title {
|
||||
// border-bottom: unset;
|
||||
}
|
||||
}
|
||||
.window_title {
|
||||
position: sticky;
|
||||
user-select: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 0.6em;
|
||||
color: lightgray;
|
||||
border-bottom: 5px solid var(--border);
|
||||
background: repeating-linear-gradient(
|
||||
to top,
|
||||
transparent,
|
||||
transparent 2px,
|
||||
transparent 2px,
|
||||
yellow 2px,
|
||||
yellow 4px
|
||||
),
|
||||
repeating-linear-gradient(to right, transparent, transparent 2px, transparent 2px, yellow 2px, yellow 4px);
|
||||
|
||||
#text {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
height: 100%;
|
||||
padding-inline: 10px;
|
||||
background-color: black;
|
||||
}
|
||||
#collapse_button {
|
||||
height: 23px !important;
|
||||
aspect-ratio: 1;
|
||||
border: 2px solid white;
|
||||
background-color: black;
|
||||
margin-right: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.window#tv {
|
||||
height: unset;
|
||||
#screen {
|
||||
max-width: 100%;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
}
|
||||
|
||||
#instruction_explainer {
|
||||
grid-area: explainer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
height: 400px;
|
||||
#expl_box {
|
||||
padding-inline: 20px;
|
||||
padding-block-start: 10px;
|
||||
}
|
||||
|
||||
#expl_text {
|
||||
font-size: 0.6em;
|
||||
text-align: center;
|
||||
vertical-align: center;
|
||||
}
|
||||
|
||||
#expl_icon {
|
||||
margin-inline-end: 0.5em;
|
||||
font-size: 30px;
|
||||
padding: 5px;
|
||||
color: var(--color);
|
||||
outline: 3px dashed var(--color);
|
||||
}
|
||||
}
|
||||
|
||||
#printout {
|
||||
grid-area: printout;
|
||||
height: 500px;
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
overflow-x: hidden;
|
||||
#printout_text {
|
||||
padding-inline: 10px;
|
||||
}
|
||||
}
|
28
src/ui.ts
28
src/ui.ts
|
@ -1,19 +1,16 @@
|
|||
import { CpuEvent, CpuEventHandler, UiEvent, UiEventHandler } from "./events";
|
||||
import { $ } from "./etc";
|
||||
import { InstructionExplainer } from "./ui/instructionExplainer";
|
||||
import { InstructionExplainer } from "./ui/windows/instructionExplainer";
|
||||
import { MemoryView } from "./ui/memoryView";
|
||||
import { frequencyIndicator } from "./ui/frequencyIndicator";
|
||||
import { RegisterView } from "./ui/registerView";
|
||||
import { Screen } from "./ui/screen";
|
||||
import { Ribbon } from "./ui/ribbon";
|
||||
import { Screen } from "./ui/windows/screen";
|
||||
import { EditButton } from "./ui/edit_button";
|
||||
import { UiComponent, UiComponentConstructor } from "./ui/uiComponent";
|
||||
import { pausePlay } from "./ui/pausePlay";
|
||||
import { Printout } from "./ui/windows/printout";
|
||||
|
||||
export class UI {
|
||||
printout: HTMLElement;
|
||||
|
||||
auto_running: boolean;
|
||||
|
||||
events: UiEventHandler = new UiEventHandler();
|
||||
|
||||
private components: Array<UiComponent>;
|
||||
|
@ -30,27 +27,23 @@ export class UI {
|
|||
this.register_component(frequencyIndicator, $("cycles"));
|
||||
this.register_component(InstructionExplainer, $("instruction_explainer"));
|
||||
this.register_component(RegisterView, $("registers"));
|
||||
this.register_component(Screen, $("screen") as HTMLCanvasElement);
|
||||
this.register_component(Ribbon, $("ribbon_menu"));
|
||||
this.register_component(Screen, $("tv"));
|
||||
this.register_component(Printout, $("printout"));
|
||||
this.register_component(EditButton, $("edit_button"));
|
||||
this.register_component(pausePlay, $("controls_buttons"));
|
||||
this.printout = $("printout");
|
||||
|
||||
this.auto_running = false;
|
||||
const pp_button = $("pause_play_button");
|
||||
}
|
||||
private register_component(c: UiComponentConstructor, e: HTMLElement): void {
|
||||
private register_component(ctor: UiComponentConstructor, e: HTMLElement): void {
|
||||
if (e === undefined) {
|
||||
console.log(c);
|
||||
console.log(ctor);
|
||||
throw new Error("Could not find HTML element while registering UI component");
|
||||
}
|
||||
const component = new c(e, this.events);
|
||||
const component = new ctor(e, this.events);
|
||||
this.components.push(component);
|
||||
}
|
||||
|
||||
init_events(cpu_events: CpuEventHandler): void {
|
||||
cpu_events.listen(CpuEvent.Print, (char) => {
|
||||
this.printout.textContent = (this.printout.textContent ?? "") + char;
|
||||
});
|
||||
cpu_events.listen(CpuEvent.Reset, () => {
|
||||
this.reset();
|
||||
});
|
||||
|
@ -64,6 +57,5 @@ export class UI {
|
|||
for (const c of this.components) {
|
||||
c.reset();
|
||||
}
|
||||
this.printout.textContent = "";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ export abstract class CelledViewer {
|
|||
this.element = element;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.element.classList.add("celled_viewer");
|
||||
for (let i = 0; i < this.width * this.height; i++) {
|
||||
const mem_cell_el = el("div");
|
||||
mem_cell_el.append("0", "0");
|
||||
|
|
41
src/ui/edit_button.ts
Normal file
41
src/ui/edit_button.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
import { el, $ } from "../etc";
|
||||
import { UiEventHandler, UiEvent } from "../events";
|
||||
import { UiComponent } from "./uiComponent";
|
||||
|
||||
export class EditButton implements UiComponent {
|
||||
element: HTMLElement;
|
||||
events: UiEventHandler;
|
||||
constructor(element: HTMLElement, event: UiEventHandler) {
|
||||
this.element = element;
|
||||
this.events = event;
|
||||
|
||||
const image = el("img");
|
||||
image.src = "pencil.png";
|
||||
image.style.width = "20px";
|
||||
image.style.height = "20px";
|
||||
this.element.classList.add("editor_toggle");
|
||||
this.element.addEventListener("click", () => this.edit_toggle());
|
||||
this.element.appendChild(image);
|
||||
}
|
||||
reset(): void {
|
||||
const is_on = this.element.classList.contains("on");
|
||||
if (is_on) {
|
||||
this.edit_toggle();
|
||||
}
|
||||
}
|
||||
|
||||
edit_toggle(): void {
|
||||
const is_on = this.element.classList.contains("on");
|
||||
if (is_on) {
|
||||
this.element.classList.remove("on");
|
||||
$("root").classList.remove("editor");
|
||||
this.element.classList.add("off");
|
||||
this.events.dispatch(UiEvent.EditOff);
|
||||
} else {
|
||||
this.events.dispatch(UiEvent.EditOn);
|
||||
$("root").classList.add("editor");
|
||||
this.element.classList.add("on");
|
||||
this.element.classList.remove("off");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,6 +24,14 @@ export class EditorContext {
|
|||
cell.addEventListener("keydown", (e) => {
|
||||
this.keydown(e, i);
|
||||
});
|
||||
cell.addEventListener("input", (e) => {
|
||||
const target = e.target as HTMLElement;
|
||||
if (target === null) return;
|
||||
const text = target.textContent ?? "";
|
||||
if (text.length !== 2) {
|
||||
target.textContent = text.substring(0, 2);
|
||||
}
|
||||
});
|
||||
cell.addEventListener("focus", () => {
|
||||
if (!this.enabled) return;
|
||||
this.current_cell_info.old = cell.textContent ?? "00";
|
||||
|
|
|
@ -37,6 +37,9 @@ export class frequencyIndicator implements UiComponent {
|
|||
this.element.textContent = `${value}hz`;
|
||||
this.last_value = value;
|
||||
}
|
||||
if (value === 0) {
|
||||
this.element.textContent = "";
|
||||
}
|
||||
this.last_time = new_time;
|
||||
this.count = 0;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { el } from "../etc";
|
||||
import { UiEventHandler, CpuEventHandler, UiEvent } from "../events";
|
||||
import { UiEventHandler, UiEvent } from "../events";
|
||||
import { UiComponent } from "./uiComponent";
|
||||
|
||||
const MAX_SLIDER = 1000;
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
import { el, $ } from "../etc";
|
||||
import { UiEventHandler, UiEvent } from "../events";
|
||||
import { UiComponent } from "./uiComponent";
|
||||
|
||||
function new_button(name: string, img_path: string, additional_class?: string): HTMLButtonElement {
|
||||
const button = el("button", "", "no_style ribbon_button");
|
||||
const image = el("img");
|
||||
image.src = img_path;
|
||||
image.width = 64;
|
||||
image.height = 64;
|
||||
if (additional_class !== undefined) {
|
||||
button.classList.add(additional_class);
|
||||
}
|
||||
button.appendChild(image);
|
||||
return button;
|
||||
}
|
||||
|
||||
export class Ribbon implements UiComponent {
|
||||
element: HTMLElement;
|
||||
events: UiEventHandler;
|
||||
edit_button: HTMLButtonElement;
|
||||
console_button: HTMLButtonElement;
|
||||
display_button: HTMLButtonElement;
|
||||
explainer_button: HTMLButtonElement;
|
||||
constructor(element: HTMLElement, event: UiEventHandler) {
|
||||
this.element = element;
|
||||
this.events = event;
|
||||
|
||||
this.edit_button = new_button("Edit", "pencil.png", "editor_toggle");
|
||||
this.console_button = new_button("Console", "texout.png");
|
||||
this.display_button = new_button("Video", "tv.png");
|
||||
this.explainer_button = new_button("Explainer", "explainer.png");
|
||||
this.edit_button.addEventListener("click", () => this.edit_toggle());
|
||||
this.element.appendChild(this.edit_button);
|
||||
this.element.appendChild(this.console_button);
|
||||
this.element.appendChild(this.display_button);
|
||||
this.element.appendChild(this.explainer_button);
|
||||
}
|
||||
reset(): void {
|
||||
const is_on = this.edit_button.classList.contains("on");
|
||||
if (is_on) {
|
||||
this.edit_toggle();
|
||||
}
|
||||
}
|
||||
|
||||
edit_toggle(): void {
|
||||
const is_on = this.edit_button.classList.contains("on");
|
||||
if (is_on) {
|
||||
this.edit_button.classList.remove("on");
|
||||
$("main").classList.remove("editor");
|
||||
this.edit_button.classList.add("off");
|
||||
this.events.dispatch(UiEvent.EditOff);
|
||||
} else {
|
||||
this.events.dispatch(UiEvent.EditOn);
|
||||
$("main").classList.add("editor");
|
||||
this.edit_button.classList.add("on");
|
||||
this.edit_button.classList.remove("off");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,7 +9,9 @@ export interface UiComponent {
|
|||
element: HTMLElement;
|
||||
/** Allows listening and emitting UiEvents*/
|
||||
events: UiEventHandler;
|
||||
/** Completely reset the state of the component */
|
||||
reset: () => void;
|
||||
soft_reset?: () => void;
|
||||
/** Allows listening CPUEvents*/
|
||||
init_cpu_events?: (c: CpuEventHandler) => void;
|
||||
}
|
||||
|
|
82
src/ui/windowBox.ts
Normal file
82
src/ui/windowBox.ts
Normal file
|
@ -0,0 +1,82 @@
|
|||
import { el } from "../etc";
|
||||
export abstract class WindowBox {
|
||||
element: HTMLElement;
|
||||
readonly title: string;
|
||||
title_bar: HTMLElement;
|
||||
private resize: HTMLElement;
|
||||
private collapse_button: HTMLButtonElement;
|
||||
private collapsed: boolean = false;
|
||||
private resize_func: (e: MouseEvent) => void;
|
||||
|
||||
constructor(element: HTMLElement, title: string, options?: { collapsed?: boolean }) {
|
||||
this.element = element;
|
||||
this.title = title;
|
||||
this.element.classList.add("window");
|
||||
this.title_bar = el("div", undefined, "window_title");
|
||||
this.element.appendChild(this.title_bar);
|
||||
const title_bar_text_box = el("div", "text");
|
||||
title_bar_text_box.textContent = title;
|
||||
this.title_bar.appendChild(title_bar_text_box);
|
||||
this.resize = el("div", "resize");
|
||||
this.element.appendChild(this.resize);
|
||||
this.resize_func = this.resize_move.bind(this);
|
||||
this.collapse_button = el("button", "collapse_button", "nostyle");
|
||||
this.collapse_button.addEventListener("click", () => {
|
||||
this.toggle_collapse();
|
||||
});
|
||||
this.title_bar.appendChild(this.collapse_button);
|
||||
this.resize.addEventListener("mousedown", (e) => {
|
||||
window.addEventListener("mousemove", this.resize_func);
|
||||
});
|
||||
window.addEventListener("mouseup", () => {
|
||||
this.remove_resize_listeners();
|
||||
});
|
||||
window.addEventListener("mouseleave", () => {
|
||||
this.remove_resize_listeners();
|
||||
});
|
||||
if (options?.collapsed) {
|
||||
this.collapse();
|
||||
}
|
||||
}
|
||||
|
||||
collapse(): void {
|
||||
this.element.classList.add("collapsed");
|
||||
this.remove_resize_listeners();
|
||||
this.resize.style.visibility = "hidden";
|
||||
this.element.style.height = `${this.title_bar.offsetHeight + 4}px`;
|
||||
this.collapsed = true;
|
||||
}
|
||||
|
||||
toggle_collapse(): void {
|
||||
if (this.collapsed) {
|
||||
this.uncollapse();
|
||||
} else {
|
||||
this.collapse();
|
||||
}
|
||||
}
|
||||
|
||||
uncollapse(): void {
|
||||
this.element.classList.remove("collapsed");
|
||||
this.resize.style.visibility = "unset";
|
||||
this.element.style.height = `${this.title_bar.offsetHeight + 10 + 200}px`;
|
||||
this.collapsed = false;
|
||||
}
|
||||
|
||||
remove_resize_listeners(): void {
|
||||
window.removeEventListener("mousemove", this.resize_func);
|
||||
}
|
||||
|
||||
resize_move(e: MouseEvent): void {
|
||||
if (this.collapsed) {
|
||||
this.uncollapse();
|
||||
this.remove_resize_listeners();
|
||||
return;
|
||||
}
|
||||
const distance_to_title = e.clientY - this.element.offsetTop - this.title_bar.offsetHeight + window.scrollY + 5;
|
||||
if (distance_to_title <= 5) {
|
||||
this.collapse();
|
||||
return;
|
||||
}
|
||||
this.element.style.height = `${e.clientY - this.element.offsetTop + window.scrollY + 8}px`;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import { UiEventHandler, CpuEventHandler, CpuEvent } from "../events";
|
||||
import { u1, u2 } from "../num";
|
||||
import { UiComponent } from "./uiComponent";
|
||||
import { UiEventHandler, CpuEventHandler, CpuEvent } from "../../events";
|
||||
import { u1, u2 } from "../../num";
|
||||
import { UiComponent } from "../uiComponent";
|
||||
|
||||
class BankIndicator implements UiComponent {
|
||||
element: HTMLElement;
|
|
@ -1,14 +1,14 @@
|
|||
import { el, format_hex } from "../etc";
|
||||
import { CpuEvent, CpuEventHandler, UiEventHandler } from "../events";
|
||||
import { Instruction, ParamType, ParameterType } from "../instructionSet";
|
||||
import { u8 } from "../num";
|
||||
import { UiComponent } from "./uiComponent";
|
||||
import { el, format_hex } from "../../etc";
|
||||
import { CpuEvent, CpuEventHandler, UiEventHandler } from "../../events";
|
||||
import { Instruction, ParamType, ParameterType } from "../../instructionSet";
|
||||
import { u8 } from "../../num";
|
||||
import { WindowBox } from "../windowBox";
|
||||
import { UiComponent } from "../uiComponent";
|
||||
|
||||
export class InstructionExplainer implements UiComponent {
|
||||
element: HTMLElement;
|
||||
export class InstructionExplainer extends WindowBox implements UiComponent {
|
||||
events: UiEventHandler;
|
||||
constructor(element: HTMLElement, e: UiEventHandler) {
|
||||
this.element = element;
|
||||
super(element, "Instruction Explainer");
|
||||
this.events = e;
|
||||
}
|
||||
add_instruction(instr: Instruction, pos: u8, byte: u8): void {
|
||||
|
@ -29,7 +29,7 @@ export class InstructionExplainer implements UiComponent {
|
|||
this.element.appendChild(instr_box);
|
||||
}
|
||||
|
||||
add_param(param: ParameterType, pos: u8, byte: u8): void {
|
||||
add_parameter(param: ParameterType, pos: u8, byte: u8): void {
|
||||
const t = param.type;
|
||||
let name;
|
||||
if (t === ParamType.Const) {
|
||||
|
@ -51,7 +51,7 @@ export class InstructionExplainer implements UiComponent {
|
|||
|
||||
init_cpu_events(c: CpuEventHandler): void {
|
||||
c.listen(CpuEvent.ParameterParsed, ({ param, code, pos }) => {
|
||||
this.add_param(param, pos, code);
|
||||
this.add_parameter(param, pos, code);
|
||||
});
|
||||
c.listen(CpuEvent.InstructionParsed, ({ instr, code, pos }) => {
|
||||
this.add_instruction(instr, pos, code);
|
||||
|
@ -62,6 +62,6 @@ export class InstructionExplainer implements UiComponent {
|
|||
}
|
||||
|
||||
reset(): void {
|
||||
this.element.innerHTML = "";
|
||||
this.element.querySelectorAll("#expl_box").forEach((e) => e.remove());
|
||||
}
|
||||
}
|
25
src/ui/windows/printout.ts
Normal file
25
src/ui/windows/printout.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import { el } from "../../etc";
|
||||
import { CpuEvent, CpuEventHandler, UiEventHandler } from "../../events";
|
||||
import { WindowBox } from "../windowBox";
|
||||
import { UiComponent } from "../uiComponent";
|
||||
|
||||
export class Printout extends WindowBox implements UiComponent {
|
||||
events: UiEventHandler;
|
||||
text_box: HTMLElement;
|
||||
constructor(element: HTMLElement, events: UiEventHandler) {
|
||||
super(element, "Printout");
|
||||
this.events = events;
|
||||
this.text_box = el("div", "printout_text");
|
||||
this.element.appendChild(this.text_box);
|
||||
}
|
||||
|
||||
init_cpu_events(c: CpuEventHandler): void {
|
||||
c.listen(CpuEvent.Print, (c) => {
|
||||
this.text_box.textContent += c;
|
||||
});
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this.text_box.textContent = "";
|
||||
}
|
||||
}
|
|
@ -1,32 +1,39 @@
|
|||
import { UiEventHandler, CpuEventHandler, CpuEvent } from "../events";
|
||||
import { u4, u8 } from "../num";
|
||||
import { UiComponent } from "./uiComponent";
|
||||
export class Screen implements UiComponent {
|
||||
element: HTMLCanvasElement;
|
||||
import { el } from "../../etc";
|
||||
import { UiEventHandler, CpuEventHandler, CpuEvent } from "../../events";
|
||||
import { u4, u8 } from "../../num";
|
||||
import { UiComponent } from "../uiComponent";
|
||||
import { WindowBox } from "../windowBox";
|
||||
export class Screen extends WindowBox implements UiComponent {
|
||||
events: UiEventHandler;
|
||||
screen: HTMLCanvasElement;
|
||||
ctx: CanvasRenderingContext2D;
|
||||
scale: [number, number];
|
||||
constructor(element: HTMLElement, event: UiEventHandler) {
|
||||
this.element = element as HTMLCanvasElement;
|
||||
super(element, "TV", { collapsed: true });
|
||||
this.screen = el("canvas", "screen");
|
||||
this.events = event;
|
||||
const canvas_size = [512, 512];
|
||||
const data_size = [16, 16];
|
||||
this.scale = [canvas_size[0] / data_size[0], canvas_size[1] / data_size[1]];
|
||||
[this.element.width, this.element.height] = canvas_size;
|
||||
const ctx = this.element.getContext("2d");
|
||||
[this.screen.width, this.screen.height] = canvas_size;
|
||||
const ctx = this.screen.getContext("2d");
|
||||
if (ctx === null) {
|
||||
throw new Error("todo");
|
||||
throw new Error("could not load screen");
|
||||
}
|
||||
this.ctx = ctx;
|
||||
// for (let x = 0; x < 16; x++) {
|
||||
// for (let y = 0; y < 16; y++) {
|
||||
// this.setPixel(x as u4, y as u4, (x + 16 * y) as u8);
|
||||
// }
|
||||
// }
|
||||
this.element.appendChild(this.screen);
|
||||
}
|
||||
|
||||
private test_pattern(): void {
|
||||
for (let x = 0; x < 16; x++) {
|
||||
for (let y = 0; y < 16; y++) {
|
||||
this.setPixel(x as u4, y as u4, (x + 16 * y) as u8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
const ctx = this.element.getContext("2d");
|
||||
const ctx = this.screen.getContext("2d");
|
||||
if (ctx === null) {
|
||||
throw new Error("todo");
|
||||
}
|
74
svg.html
Normal file
74
svg.html
Normal file
|
@ -0,0 +1,74 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Document</title>
|
||||
<style>
|
||||
html {
|
||||
color: white;
|
||||
background-color: black;
|
||||
font-family: monospace;
|
||||
font-size: 2em;
|
||||
}
|
||||
body {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 100px;
|
||||
align-content: center;
|
||||
margin-inline: auto;
|
||||
margin-block: auto;
|
||||
}
|
||||
#cont {
|
||||
margin-top: 100px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
#cpu,
|
||||
#target {
|
||||
margin-block: auto;
|
||||
border: solid 3px yellow;
|
||||
padding: 6px;
|
||||
}
|
||||
#targets {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 9px;
|
||||
}
|
||||
#connections {
|
||||
}
|
||||
div#target.gray {
|
||||
border-color: gray;
|
||||
}
|
||||
svg polyline {
|
||||
fill: transparent;
|
||||
stroke-width: 3;
|
||||
stroke-linecap: butt;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="cont">
|
||||
<div id="cpu">CPU</div>
|
||||
<svg
|
||||
id="connections"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 100 100"
|
||||
preserveAspectRatio="none"
|
||||
width="100"
|
||||
height="100%"
|
||||
>
|
||||
<polyline points="0,43 50,43 50,10, 100,10" stroke="yellow" />
|
||||
<polyline points="0,48 64,48 64,37, 100,37" stroke="gray" />
|
||||
<polyline points="0,53 64,53 64,63, 100,63" stroke="gray" />
|
||||
<polyline points="0,58 50,58 50,90, 100,90" stroke="gray" />
|
||||
</svg>
|
||||
<div id="targets">
|
||||
<div id="target">Main</div>
|
||||
<div id="target" class="gray">Bank 1</div>
|
||||
<div id="target" class="gray">Bank 2</div>
|
||||
<div id="target" class="gray">VRAM</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
4
todo.md
4
todo.md
|
@ -3,7 +3,6 @@ Edit Mode
|
|||
- Select where program counter is
|
||||
|
||||
Implement HCF
|
||||
Move start/stop/auto logic into computer
|
||||
|
||||
Speed control slider behavior
|
||||
Speed control slider styling
|
||||
|
@ -11,9 +10,6 @@ Overclock Box
|
|||
|
||||
error in instruction when number out of range (fix new Error("todo"))
|
||||
|
||||
UI for screen (toggling (click an icon?))
|
||||
UI for togging other UI elements
|
||||
|
||||
UI for showing which Memory bank is selected
|
||||
VRAM select instruction
|
||||
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
/* eslint-disable no-undef */
|
||||
const path = require("path");
|
||||
const CopyPlugin = require("copy-webpack-plugin");
|
||||
|
||||
const config = {
|
||||
entry: "./src/index.ts",
|
||||
|
@ -33,6 +36,11 @@ const config = {
|
|||
filename: "main.js",
|
||||
path: path.resolve(__dirname, "dist"),
|
||||
},
|
||||
plugins: [
|
||||
new CopyPlugin({
|
||||
patterns: [{ from: "./src/include", to: "./" }],
|
||||
}),
|
||||
],
|
||||
};
|
||||
module.exports = (env, argv) => {
|
||||
if (argv.mode === "development") {
|
||||
|
|
Loading…
Reference in a new issue