Webpack copy. add windows

This commit is contained in:
Alexander Bass 2024-03-06 23:41:02 -05:00
parent 8a38dded92
commit 1f6a95c253
31 changed files with 910 additions and 423 deletions

194
package-lock.json generated
View file

@ -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",

View file

@ -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",

View file

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

38
src/include/index.html Normal file
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View file

@ -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 {

View file

@ -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
View 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;
}

View 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
View 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
View 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
View 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;
}
}

View file

@ -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 = "";
}
}

View file

@ -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
View 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");
}
}
}

View file

@ -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";

View file

@ -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;
}

View file

@ -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;

View file

@ -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");
}
}
}

View file

@ -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
View 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`;
}
}

View file

@ -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;

View file

@ -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