diff --git a/TODO b/TODO index 7ee5542..72c4d95 100644 --- a/TODO +++ b/TODO @@ -1,6 +1,5 @@ Add screen (VRAM?) Live memory and register editing (Probably should pause autostep when it reaches the cell you're modifying) Draw lines between registers and memory when used -Explain mode to explain what instructions are doing -Window showing ISA + Save binary button \ No newline at end of file diff --git a/index.html b/index.html index 8850f76..9fb742b 100644 --- a/index.html +++ b/index.html @@ -4,30 +4,27 @@ - Document - + Virtual 8-Bit Computer
VIRTUAL 8-BIT COMPUTER
-
-
-
-
-
←REGISTERS
-
MEMORY↯
-
-
-
+
+
+
←REGISTERS
+
MEMORY↯
+
-
-
- - - - +
+ + + + + +
+

 	
diff --git a/package-lock.json b/package-lock.json
index c9abae1..2b3b42f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7,10 +7,14 @@
             "devDependencies": {
                 "@typescript-eslint/eslint-plugin": "^6.0.0",
                 "@typescript-eslint/parser": "^6.0.0",
+                "css-loader": "^6.10.0",
                 "eslint": "^8.44.0",
+                "sass": "^1.71.0",
+                "sass-loader": "^14.1.0",
+                "style-loader": "^3.3.4",
                 "ts-loader": "^9.4.4",
                 "typescript": "^5.1.6",
-                "webpack": "^5.88.2",
+                "webpack": "^5.90.2",
                 "webpack-cli": "^5.1.4"
             }
         },
@@ -782,6 +786,19 @@
                 "url": "https://github.com/chalk/ansi-styles?sponsor=1"
             }
         },
+        "node_modules/anymatch": {
+            "version": "3.1.3",
+            "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+            "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+            "dev": true,
+            "dependencies": {
+                "normalize-path": "^3.0.0",
+                "picomatch": "^2.0.4"
+            },
+            "engines": {
+                "node": ">= 8"
+            }
+        },
         "node_modules/argparse": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@@ -803,6 +820,15 @@
             "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
             "dev": true
         },
+        "node_modules/binary-extensions": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+            "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+            "dev": true,
+            "engines": {
+                "node": ">=8"
+            }
+        },
         "node_modules/brace-expansion": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
@@ -907,6 +933,42 @@
                 "url": "https://github.com/chalk/chalk?sponsor=1"
             }
         },
+        "node_modules/chokidar": {
+            "version": "3.6.0",
+            "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+            "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+            "dev": true,
+            "dependencies": {
+                "anymatch": "~3.1.2",
+                "braces": "~3.0.2",
+                "glob-parent": "~5.1.2",
+                "is-binary-path": "~2.1.0",
+                "is-glob": "~4.0.1",
+                "normalize-path": "~3.0.0",
+                "readdirp": "~3.6.0"
+            },
+            "engines": {
+                "node": ">= 8.10.0"
+            },
+            "funding": {
+                "url": "https://paulmillr.com/funding/"
+            },
+            "optionalDependencies": {
+                "fsevents": "~2.3.2"
+            }
+        },
+        "node_modules/chokidar/node_modules/glob-parent": {
+            "version": "5.1.2",
+            "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+            "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+            "dev": true,
+            "dependencies": {
+                "is-glob": "^4.0.1"
+            },
+            "engines": {
+                "node": ">= 6"
+            }
+        },
         "node_modules/chrome-trace-event": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
@@ -980,6 +1042,53 @@
                 "node": ">= 8"
             }
         },
+        "node_modules/css-loader": {
+            "version": "6.10.0",
+            "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.10.0.tgz",
+            "integrity": "sha512-LTSA/jWbwdMlk+rhmElbDR2vbtQoTBPr7fkJE+mxrHj+7ru0hUmHafDRzWIjIHTwpitWVaqY2/UWGRca3yUgRw==",
+            "dev": true,
+            "dependencies": {
+                "icss-utils": "^5.1.0",
+                "postcss": "^8.4.33",
+                "postcss-modules-extract-imports": "^3.0.0",
+                "postcss-modules-local-by-default": "^4.0.4",
+                "postcss-modules-scope": "^3.1.1",
+                "postcss-modules-values": "^4.0.0",
+                "postcss-value-parser": "^4.2.0",
+                "semver": "^7.5.4"
+            },
+            "engines": {
+                "node": ">= 12.13.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/webpack"
+            },
+            "peerDependencies": {
+                "@rspack/core": "0.x || 1.x",
+                "webpack": "^5.0.0"
+            },
+            "peerDependenciesMeta": {
+                "@rspack/core": {
+                    "optional": true
+                },
+                "webpack": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/cssesc": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+            "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+            "dev": true,
+            "bin": {
+                "cssesc": "bin/cssesc"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
         "node_modules/debug": {
             "version": "4.3.4",
             "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -1397,6 +1506,20 @@
             "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
             "dev": true
         },
+        "node_modules/fsevents": {
+            "version": "2.3.3",
+            "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+            "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+            "dev": true,
+            "hasInstallScript": true,
+            "optional": true,
+            "os": [
+                "darwin"
+            ],
+            "engines": {
+                "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+            }
+        },
         "node_modules/function-bind": {
             "version": "1.1.2",
             "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@@ -1534,6 +1657,18 @@
                 "node": ">= 0.4"
             }
         },
+        "node_modules/icss-utils": {
+            "version": "5.1.0",
+            "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
+            "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
+            "dev": true,
+            "engines": {
+                "node": "^10 || ^12 || >= 14"
+            },
+            "peerDependencies": {
+                "postcss": "^8.1.0"
+            }
+        },
         "node_modules/ignore": {
             "version": "5.3.1",
             "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
@@ -1543,6 +1678,12 @@
                 "node": ">= 4"
             }
         },
+        "node_modules/immutable": {
+            "version": "4.3.5",
+            "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz",
+            "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==",
+            "dev": true
+        },
         "node_modules/import-fresh": {
             "version": "3.3.0",
             "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@@ -1612,6 +1753,18 @@
                 "node": ">=10.13.0"
             }
         },
+        "node_modules/is-binary-path": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+            "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+            "dev": true,
+            "dependencies": {
+                "binary-extensions": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
         "node_modules/is-core-module": {
             "version": "2.13.1",
             "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
@@ -1898,6 +2051,24 @@
             "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
             "dev": true
         },
+        "node_modules/nanoid": {
+            "version": "3.3.7",
+            "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
+            "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
+            "dev": true,
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/ai"
+                }
+            ],
+            "bin": {
+                "nanoid": "bin/nanoid.cjs"
+            },
+            "engines": {
+                "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+            }
+        },
         "node_modules/natural-compare": {
             "version": "1.4.0",
             "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -1916,6 +2087,15 @@
             "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
             "dev": true
         },
+        "node_modules/normalize-path": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+            "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+            "dev": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
         "node_modules/once": {
             "version": "1.4.0",
             "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -2117,6 +2297,112 @@
                 "node": ">=8"
             }
         },
+        "node_modules/postcss": {
+            "version": "8.4.35",
+            "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz",
+            "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==",
+            "dev": true,
+            "funding": [
+                {
+                    "type": "opencollective",
+                    "url": "https://opencollective.com/postcss/"
+                },
+                {
+                    "type": "tidelift",
+                    "url": "https://tidelift.com/funding/github/npm/postcss"
+                },
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/ai"
+                }
+            ],
+            "dependencies": {
+                "nanoid": "^3.3.7",
+                "picocolors": "^1.0.0",
+                "source-map-js": "^1.0.2"
+            },
+            "engines": {
+                "node": "^10 || ^12 || >=14"
+            }
+        },
+        "node_modules/postcss-modules-extract-imports": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz",
+            "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==",
+            "dev": true,
+            "engines": {
+                "node": "^10 || ^12 || >= 14"
+            },
+            "peerDependencies": {
+                "postcss": "^8.1.0"
+            }
+        },
+        "node_modules/postcss-modules-local-by-default": {
+            "version": "4.0.4",
+            "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.4.tgz",
+            "integrity": "sha512-L4QzMnOdVwRm1Qb8m4x8jsZzKAaPAgrUF1r/hjDR2Xj7R+8Zsf97jAlSQzWtKx5YNiNGN8QxmPFIc/sh+RQl+Q==",
+            "dev": true,
+            "dependencies": {
+                "icss-utils": "^5.0.0",
+                "postcss-selector-parser": "^6.0.2",
+                "postcss-value-parser": "^4.1.0"
+            },
+            "engines": {
+                "node": "^10 || ^12 || >= 14"
+            },
+            "peerDependencies": {
+                "postcss": "^8.1.0"
+            }
+        },
+        "node_modules/postcss-modules-scope": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.1.1.tgz",
+            "integrity": "sha512-uZgqzdTleelWjzJY+Fhti6F3C9iF1JR/dODLs/JDefozYcKTBCdD8BIl6nNPbTbcLnGrk56hzwZC2DaGNvYjzA==",
+            "dev": true,
+            "dependencies": {
+                "postcss-selector-parser": "^6.0.4"
+            },
+            "engines": {
+                "node": "^10 || ^12 || >= 14"
+            },
+            "peerDependencies": {
+                "postcss": "^8.1.0"
+            }
+        },
+        "node_modules/postcss-modules-values": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz",
+            "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==",
+            "dev": true,
+            "dependencies": {
+                "icss-utils": "^5.0.0"
+            },
+            "engines": {
+                "node": "^10 || ^12 || >= 14"
+            },
+            "peerDependencies": {
+                "postcss": "^8.1.0"
+            }
+        },
+        "node_modules/postcss-selector-parser": {
+            "version": "6.0.15",
+            "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz",
+            "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==",
+            "dev": true,
+            "dependencies": {
+                "cssesc": "^3.0.0",
+                "util-deprecate": "^1.0.2"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/postcss-value-parser": {
+            "version": "4.2.0",
+            "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+            "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+            "dev": true
+        },
         "node_modules/prelude-ls": {
             "version": "1.2.1",
             "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -2164,6 +2450,18 @@
                 "safe-buffer": "^5.1.0"
             }
         },
+        "node_modules/readdirp": {
+            "version": "3.6.0",
+            "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+            "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+            "dev": true,
+            "dependencies": {
+                "picomatch": "^2.2.1"
+            },
+            "engines": {
+                "node": ">=8.10.0"
+            }
+        },
         "node_modules/rechoir": {
             "version": "0.8.0",
             "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz",
@@ -2291,6 +2589,63 @@
                 }
             ]
         },
+        "node_modules/sass": {
+            "version": "1.71.0",
+            "resolved": "https://registry.npmjs.org/sass/-/sass-1.71.0.tgz",
+            "integrity": "sha512-HKKIKf49Vkxlrav3F/w6qRuPcmImGVbIXJ2I3Kg0VMA+3Bav+8yE9G5XmP5lMj6nl4OlqbPftGAscNaNu28b8w==",
+            "dev": true,
+            "dependencies": {
+                "chokidar": ">=3.0.0 <4.0.0",
+                "immutable": "^4.0.0",
+                "source-map-js": ">=0.6.2 <2.0.0"
+            },
+            "bin": {
+                "sass": "sass.js"
+            },
+            "engines": {
+                "node": ">=14.0.0"
+            }
+        },
+        "node_modules/sass-loader": {
+            "version": "14.1.0",
+            "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-14.1.0.tgz",
+            "integrity": "sha512-LS2mLeFWA+orYxHNu+O18Xe4jR0kyamNOOUsE3NyBP4DvIL+8stHpNX0arYTItdPe80kluIiJ7Wfe/9iHSRO0Q==",
+            "dev": true,
+            "dependencies": {
+                "neo-async": "^2.6.2"
+            },
+            "engines": {
+                "node": ">= 18.12.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/webpack"
+            },
+            "peerDependencies": {
+                "@rspack/core": "0.x || 1.x",
+                "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0",
+                "sass": "^1.3.0",
+                "sass-embedded": "*",
+                "webpack": "^5.0.0"
+            },
+            "peerDependenciesMeta": {
+                "@rspack/core": {
+                    "optional": true
+                },
+                "node-sass": {
+                    "optional": true
+                },
+                "sass": {
+                    "optional": true
+                },
+                "sass-embedded": {
+                    "optional": true
+                },
+                "webpack": {
+                    "optional": true
+                }
+            }
+        },
         "node_modules/schema-utils": {
             "version": "3.3.0",
             "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
@@ -2384,6 +2739,15 @@
                 "node": ">= 8"
             }
         },
+        "node_modules/source-map-js": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+            "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+            "dev": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
         "node_modules/source-map-support": {
             "version": "0.5.21",
             "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
@@ -2427,6 +2791,22 @@
                 "url": "https://github.com/sponsors/sindresorhus"
             }
         },
+        "node_modules/style-loader": {
+            "version": "3.3.4",
+            "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.4.tgz",
+            "integrity": "sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==",
+            "dev": true,
+            "engines": {
+                "node": ">= 12.13.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/webpack"
+            },
+            "peerDependencies": {
+                "webpack": "^5.0.0"
+            }
+        },
         "node_modules/supports-color": {
             "version": "7.2.0",
             "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -2644,6 +3024,12 @@
                 "punycode": "^2.1.0"
             }
         },
+        "node_modules/util-deprecate": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+            "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+            "dev": true
+        },
         "node_modules/watchpack": {
             "version": "2.4.0",
             "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
@@ -2658,9 +3044,9 @@
             }
         },
         "node_modules/webpack": {
-            "version": "5.90.1",
-            "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.1.tgz",
-            "integrity": "sha512-SstPdlAC5IvgFnhiRok8hqJo/+ArAbNv7rhU4fnWGHNVfN59HSQFaxZDSAL3IFG2YmqxuRs+IU33milSxbPlog==",
+            "version": "5.90.2",
+            "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.2.tgz",
+            "integrity": "sha512-ziXu8ABGr0InCMEYFnHrYweinHK2PWrMqnwdHk2oK3rRhv/1B+2FnfwYv5oD+RrknK/Pp/Hmyvu+eAsaMYhzCw==",
             "dev": true,
             "dependencies": {
                 "@types/eslint-scope": "^3.7.3",
diff --git a/package.json b/package.json
index 7a2c542..515d454 100644
--- a/package.json
+++ b/package.json
@@ -3,14 +3,18 @@
     "devDependencies": {
         "@typescript-eslint/eslint-plugin": "^6.0.0",
         "@typescript-eslint/parser": "^6.0.0",
+        "css-loader": "^6.10.0",
         "eslint": "^8.44.0",
+        "sass": "^1.71.0",
+        "sass-loader": "^14.1.0",
+        "style-loader": "^3.3.4",
         "ts-loader": "^9.4.4",
         "typescript": "^5.1.6",
-        "webpack": "^5.88.2",
+        "webpack": "^5.90.2",
         "webpack-cli": "^5.1.4"
     },
     "scripts": {
         "build": "webpack --mode=production",
         "watch": "webpack --mode=development --watch"
     }
-}
\ No newline at end of file
+}
diff --git a/src/computer.ts b/src/computer.ts
index a202dec..4c07eaf 100644
--- a/src/computer.ts
+++ b/src/computer.ts
@@ -1,7 +1,7 @@
-import { CpuEvent, CpuEventHandler, MemoryCellType, UiEvent, UiEventHandler } from "./events";
+import { CpuEvent, CpuEventHandler, UiEvent, UiEventHandler } from "./events";
 import { byte_array_to_js_source, format_hex, u8 } from "./etc";
 import { EventHandler } from "./eventHandler";
-import { ConstParam, Instruction, ISA, MemorParam, RegisParam } from "./instructionSet";
+import { Instruction, ISA } from "./instructionSet";
 
 export type TempInstrState = {
 	pos: u8;
@@ -9,16 +9,11 @@ export type TempInstrState = {
 	instr: Instruction;
 	params: Uint8Array;
 };
-export type ComputerState = {
-	memory: Uint8Array;
-	program_counter: u8;
-	registers: Uint8Array;
-	current_instruction: TempInstrState | null;
-};
 
 export class Computer {
 	private memory = new Uint8Array(256);
 	private registers = new Uint8Array(8);
+	private call_stack: Array = [];
 	private program_counter: u8 = 0;
 	private current_instr: TempInstrState | null = null;
 	events: CpuEventHandler = new EventHandler() as CpuEventHandler;
@@ -36,13 +31,13 @@ export class Computer {
 		if (this.current_instr === null) {
 			const parsed_instruction = ISA.getInstruction(current_byte);
 			if (parsed_instruction === null) {
-				this.events.dispatch(CpuEvent.MemoryByteParsed, {
-					type: MemoryCellType.InvalidInstruction,
+				this.events.dispatch(CpuEvent.InvalidParsed, {
 					pos: this.program_counter,
 					code: current_byte,
 				});
 				console.log(`Invalid instruction: ${format_hex(current_byte)}`);
 				this.step_forward();
+				this.events.dispatch(CpuEvent.ClockCycle, null);
 				return;
 			}
 			this.current_instr = {
@@ -51,8 +46,7 @@ export class Computer {
 				params_found: 0,
 				params: new Uint8Array(parsed_instruction.params.length),
 			};
-			this.events.dispatch(CpuEvent.MemoryByteParsed, {
-				type: MemoryCellType.Instruction,
+			this.events.dispatch(CpuEvent.InstructionParsed, {
 				pos: this.program_counter,
 				instr: parsed_instruction,
 				code: current_byte,
@@ -61,32 +55,23 @@ export class Computer {
 
 		if (this.current_instr.pos === this.program_counter && this.current_instr.params.length > 0) {
 			this.step_forward();
+			this.events.dispatch(CpuEvent.ClockCycle, null);
 			return;
 		}
 
 		if (this.current_instr.params.length !== this.current_instr.params_found) {
-			let a;
 			const b = this.current_instr.instr.params[this.current_instr.params_found];
-			if (b instanceof ConstParam) {
-				a = MemoryCellType.Constant;
-			} else if (b instanceof RegisParam) {
-				a = MemoryCellType.Register;
-			} else if (b instanceof MemorParam) {
-				a = MemoryCellType.Memory;
-			}
-			if (a === undefined) {
-				throw new Error("Shouldn't");
-			}
-			this.events.dispatch(CpuEvent.MemoryByteParsed, {
-				type: a,
+
+			this.events.dispatch(CpuEvent.ParameterParsed, {
+				param: b,
 				pos: this.program_counter,
 				code: current_byte,
-				param: b,
 			});
 			this.current_instr.params[this.current_instr.params_found] = current_byte;
 			this.current_instr.params_found += 1;
 			if (this.current_instr.params.length !== this.current_instr.params_found) {
 				this.step_forward();
+				this.events.dispatch(CpuEvent.ClockCycle, null);
 				return;
 			}
 		}
@@ -104,6 +89,7 @@ export class Computer {
 		if (execution_post_action_state.should_step) {
 			this.step_forward();
 		}
+		this.events.dispatch(CpuEvent.ClockCycle, null);
 	}
 
 	getMemory(address: u8): u8 {
@@ -130,18 +116,30 @@ export class Computer {
 		this.program_counter = new_value;
 	}
 
+	pushCallStack(address: u8): boolean {
+		if (this.call_stack.length >= 8) return false;
+		this.call_stack.push(address);
+		return true;
+	}
+
+	popCallStack(): u8 | null {
+		return this.call_stack.pop() ?? null;
+	}
+
 	reset(): void {
+		this.events.dispatch(CpuEvent.Reset, null);
 		this.memory = new Uint8Array(256);
 		this.registers = new Uint8Array(8);
+		this.call_stack = [];
 		this.current_instr = null;
 		this.program_counter = 0;
-		this.events.dispatch(CpuEvent.Reset, null);
 	}
 
 	init_events(ui: UiEventHandler): void {
-		ui.listen(UiEvent.RequestCpuCycle, () => {
-			this.cycle();
+		ui.listen(UiEvent.RequestCpuCycle, (n) => {
+			for (let i = 0; i < n; i++) this.cycle();
 		});
+		ui.listen(UiEvent.RequestMemoryChange, ({ address, value }) => this.setMemory(address, value));
 	}
 
 	load_memory(program: Array): void {
@@ -149,9 +147,8 @@ export class Computer {
 		const max_loop = Math.min(this.memory.length, program.length);
 		for (let i = 0; i < max_loop; i++) {
 			// Don't fire event if no change is made
-			if (this.memory[i] === program[i]) {
-				continue;
-			}
+			if (this.memory[i] === program[i]) continue;
+
 			this.memory[i] = program[i];
 			this.events.dispatch(CpuEvent.MemoryChanged, { address: i, value: program[i] });
 		}
diff --git a/src/etc.ts b/src/etc.ts
index 924d35e..47ae339 100644
--- a/src/etc.ts
+++ b/src/etc.ts
@@ -1,4 +1,7 @@
+import { UiEventHandler } from "./events";
+
 // The u8 type represents an unsigned 8bit integer: byte. It does not add any safety, other than as a hint to the programmer.
+// export type u8 = 0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|48|49|50|51|52|53|54|55|56|57|58|59|60|61|62|63|64|65|66|67|68|69|70|71|72|73|74|75|76|77|78|79|80|81|82|83|84|85|86|87|88|89|90|91|92|93|94|95|96|97|98|99|100|101|102|103|104|105|106|107|108|109|110|111|112|113|114|115|116|117|118|119|120|121|122|123|124|125|126|127|128|129|130|131|132|133|134|135|136|137|138|139|140|141|142|143|144|145|146|147|148|149|150|151|152|153|154|155|156|157|158|159|160|161|162|163|164|165|166|167|168|169|170|171|172|173|174|175|176|177|178|179|180|181|182|183|184|185|186|187|188|189|190|191|192|193|194|195|196|197|198|199|200|201|202|203|204|205|206|207|208|209|210|211|212|213|214|215|216|217|218|219|220|221|222|223|224|225|226|227|228|229|230|231|232|233|234|235|236|237|238|239|240|241|242|243|244|245|246|247|248|249|250|251|252|253|254|255;
 export type u8 = number;
 
 // Jquery lite
diff --git a/src/eventHandler.ts b/src/eventHandler.ts
index 68972b4..42eb749 100644
--- a/src/eventHandler.ts
+++ b/src/eventHandler.ts
@@ -27,7 +27,7 @@ export class EventHandler {
 		const event = new Event(identifier);
 		this.events.push(event);
 	}
-	dispatch(identifier: T, event_data: unknown): void {
+	dispatch(identifier: T, event_data?: unknown): void {
 		const event = this.events.find((e) => e.identifier === identifier);
 		if (event === undefined) {
 			throw new Error("Event not found");
diff --git a/src/events.ts b/src/events.ts
index 0fa883a..f2d1251 100644
--- a/src/events.ts
+++ b/src/events.ts
@@ -6,18 +6,14 @@ export enum CpuEvent {
 	MemoryChanged,
 	RegisterChanged,
 	ProgramCounterChanged,
-	MemoryByteParsed,
+	InstructionParsed,
+	ParameterParsed,
+	InvalidParsed,
 	InstructionExecuted,
+	ClockCycle,
 	Print,
 	Reset,
-}
-
-export enum MemoryCellType {
-	Instruction,
-	InvalidInstruction,
-	Register,
-	Memory,
-	Constant,
+	Halt,
 }
 
 // Handily explained in https://www.cgjennings.ca/articles/typescript-events/
@@ -25,8 +21,12 @@ interface CpuEventMap {
 	[CpuEvent.MemoryChanged]: { address: u8; value: u8 };
 	[CpuEvent.RegisterChanged]: { register_no: u8; value: u8 };
 	[CpuEvent.ProgramCounterChanged]: { counter: u8 };
+	[CpuEvent.Halt]: null;
 	[CpuEvent.Reset]: null;
-	[CpuEvent.MemoryByteParsed]: { type: MemoryCellType; pos: u8; code: u8; param?: ParameterType; instr?: Instruction };
+	[CpuEvent.ClockCycle]: null;
+	[CpuEvent.InstructionParsed]: { pos: u8; code: u8; instr: Instruction };
+	[CpuEvent.ParameterParsed]: { pos: u8; code: u8; param: ParameterType };
+	[CpuEvent.InvalidParsed]: { pos: u8; code: u8 };
 	[CpuEvent.InstructionExecuted]: { instr: Instruction };
 	[CpuEvent.Print]: string;
 }
@@ -38,10 +38,12 @@ export interface CpuEventHandler extends EventHandler {
 
 export enum UiEvent {
 	RequestCpuCycle,
+	RequestMemoryChange,
 }
 
 interface UiEventMap {
-	[UiEvent.RequestCpuCycle]: null;
+	[UiEvent.RequestCpuCycle]: number;
+	[UiEvent.RequestMemoryChange]: { address: u8; value: u8 };
 }
 
 export interface UiEventHandler extends EventHandler {
diff --git a/src/index.ts b/src/index.ts
index e97a127..4859439 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -4,9 +4,28 @@ import { ISA } from "./instructionSet";
 import { generate_isa } from "./isaGenerator";
 import { UI } from "./ui";
 
+import "./style.scss";
+
 function main(): void {
+	// const program = [
+	// 	0x2f, 0x00, 0xf0, 0x20, 0x07, 0x00, 0x50, 0x05, 0x06, 0x07, 0x11, 0x00, 0x05, 0xfe, 0x07, 0x30, 0x00, 0x10, 0x03,
+	// 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	// 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	// 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	// 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	// 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	// 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	// 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	// 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	// 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	// 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	// 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	// 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57,
+	// 	0x6f, 0x72, 0x6c, 0x64, 0x21, 0x0a, 0x00, 0x00, 0x00,
+	// ];
+
 	const program = [
-		0x2f, 0x00, 0xf0, 0x20, 0x07, 0x00, 0x50, 0x05, 0x06, 0x07, 0x11, 0x00, 0x05, 0xfe, 0x07, 0x30, 0x00, 0x10, 0x03,
+		0x01, 0x00, 0x01, 0x00, 0xa0, 0x10, 0xff, 0x00, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0xa1,
 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -22,28 +41,25 @@ function main(): void {
 		0x6f, 0x72, 0x6c, 0x64, 0x21, 0x0a, 0x00, 0x00, 0x00,
 	];
 
-	const container = $("container");
-	if (container === null) {
-		throw new Error("no");
-	}
 	const computer = new Computer();
 
-	const ui = new UI(container);
+	const ui = new UI();
 	ui.init_events(computer.events);
 	computer.load_memory(program);
 	computer.init_events(ui.events);
 	// eslint-disable-next-line @typescript-eslint/no-explicit-any
 	(window).comp = computer;
+	// eslint-disable-next-line @typescript-eslint/no-explicit-any
+	(window).ui = ui;
 
 	$("ISA").textContent = generate_isa(ISA);
 
-	// eslint-disable-next-line prefer-arrow-callback
-	$("binary_upload").addEventListener("change", function (e) {
+	$("binary_upload").addEventListener("change", (e) => {
 		if (e.target === null) {
 			return;
 		}
 
-		// eslint-disable-next-line @typescript-eslint/no-explicit-any, prefer-destructuring
+		// eslint-disable-next-line @typescript-eslint/no-explicit-any
 		const file: File = (e.target).files[0];
 		const reader = new FileReader();
 		console.log(file);
diff --git a/src/instructionSet.ts b/src/instructionSet.ts
index dafec60..52726da 100644
--- a/src/instructionSet.ts
+++ b/src/instructionSet.ts
@@ -1,16 +1,36 @@
 import { CpuEvent, CpuEventHandler } from "./events";
 import { format_hex, u8 } from "./etc";
 
-export class ParameterType {
+export enum ParamType {
+	Const,
+	Register,
+	Memory,
+}
+
+export abstract class ParameterType {
 	readonly desc: string;
-	constructor(description: string) {
+	readonly type: ParamType;
+	constructor(description: string, p_type: ParamType) {
 		this.desc = description;
+		this.type = p_type;
 	}
 }
 
-export class ConstParam extends ParameterType {}
-export class RegisParam extends ParameterType {}
-export class MemorParam extends ParameterType {}
+class ConstParam extends ParameterType {
+	constructor(d: string) {
+		super(d, ParamType.Const);
+	}
+}
+class RegisParam extends ParameterType {
+	constructor(d: string) {
+		super(d, ParamType.Register);
+	}
+}
+class MemorParam extends ParameterType {
+	constructor(d: string) {
+		super(d, ParamType.Memory);
+	}
+}
 
 interface GenericComputer {
 	getMemory: (address: u8) => u8;
@@ -19,6 +39,8 @@ interface GenericComputer {
 	getProgramCounter: () => u8;
 	getRegister: (number: u8) => u8;
 	setRegister: (number: u8, value: u8) => void;
+	pushCallStack: (address: u8) => boolean;
+	popCallStack: () => u8 | null;
 }
 
 interface AfterExecutionComputerAction {
@@ -31,7 +53,11 @@ export interface Instruction {
 	readonly name: string;
 	readonly desc: string;
 	readonly params: Array;
-	execute: (computer_reference: GenericComputer, parameters: Uint8Array, a: AfterExecutionComputerAction) => void;
+	readonly execute: (
+		computer_reference: GenericComputer,
+		parameters: Uint8Array,
+		a: AfterExecutionComputerAction
+	) => void;
 }
 
 export class InstructionSet {
@@ -55,6 +81,8 @@ export class InstructionSet {
 
 export const ISA = new InstructionSet();
 
+// The definitions for actual instructions.
+
 ISA.insertInstruction(0x00, {
 	name: "NoOp",
 	desc: "No operation; do nothing",
@@ -86,7 +114,7 @@ ISA.insertInstruction(0x20, {
 ISA.insertInstruction(0x21, {
 	name: "SaveToMemory",
 	desc: "Writes the byte in register (P1) to the memory cell (P2)",
-	params: [new RegisParam(""), new MemorParam("")],
+	params: [new RegisParam("Write the byte in this register"), new MemorParam("To this memory address")],
 	execute(c, p) {
 		const [register_no, mem_address] = p;
 		c.setMemory(mem_address, c.getRegister(register_no));
@@ -115,6 +143,7 @@ ISA.insertInstruction(0x11, {
 		}
 	},
 });
+
 ISA.insertInstruction(0x30, {
 	name: "Increment",
 	desc: "Increments the value within register (P1) by 1",
@@ -122,7 +151,8 @@ ISA.insertInstruction(0x30, {
 	execute(c, p) {
 		const register_no = p[0];
 		const current_value = c.getRegister(register_no);
-		c.setRegister(register_no, current_value + 1);
+		const new_value = (current_value + 1) % 256;
+		c.setRegister(register_no, new_value);
 	},
 });
 
@@ -133,14 +163,18 @@ ISA.insertInstruction(0x31, {
 	execute(c, p) {
 		const register_no = p[0];
 		const current_value = c.getRegister(register_no);
-		c.setRegister(register_no, current_value - 1);
+		let new_value = current_value - 1;
+		if (new_value === -1) {
+			new_value = 255;
+		}
+		c.setRegister(register_no, new_value);
 	},
 });
 
 ISA.insertInstruction(0x40, {
 	name: "Add",
 	desc: "Adds the contents of (P1) and (P2) and stores result to register (P1). (Overflow will be taken mod 256)",
-	params: [new RegisParam(""), new RegisParam("")],
+	params: [new RegisParam("set this register to"), new RegisParam("it's sum with the value in this register")],
 	execute(c, p) {
 		const [register_no_1, register_no_2] = p;
 		const new_value = (c.getRegister(register_no_1) + c.getRegister(register_no_2)) % 256;
@@ -175,3 +209,81 @@ ISA.insertInstruction(0xfe, {
 		a.dispatch(CpuEvent.Print, char);
 	},
 });
+
+ISA.insertInstruction(0x48, {
+	name: "Bitwise And",
+	desc: "Takes each bit in register (P1) and compares to the respective bit in register (P2). Each bit in register (P1) is set to 1 if the respective bit in both registers are 1",
+	params: [new RegisParam(""), new RegisParam("")],
+	execute(c, p) {
+		const [register_no_1, register_no_2] = p;
+		const new_value = c.getRegister(register_no_1) & c.getMemory(register_no_2);
+		c.setRegister(register_no_1, new_value);
+	},
+});
+
+ISA.insertInstruction(0xff, {
+	name: "Print",
+	desc: "Prints the byte in register (P1) to console as base 10",
+	params: [new RegisParam("Register to print from")],
+	execute(c, p, a) {
+		const register_no = p[0];
+		const byte = c.getRegister(register_no);
+
+		a.dispatch(CpuEvent.Print, byte.toString(10));
+	},
+});
+
+ISA.insertInstruction(0xfd, {
+	name: "Print 16 bit",
+	desc: "Prints the byte in register (P1) as the upper half and the byte in register (P2) as the lower half of a 16 bit number. Formats to decimal",
+	params: [new RegisParam("Upper 8 bits of number"), new RegisParam("Lower 8 bits of number")],
+	execute(c, p, a) {
+		const [upper_register_no, lower_register_no] = p;
+		const upper = c.getRegister(upper_register_no);
+		const lower = c.getRegister(lower_register_no);
+		const sum = upper * 16 * 16 + lower;
+
+		a.dispatch(CpuEvent.Print, sum.toString(10));
+	},
+});
+
+ISA.insertInstruction(0x66, {
+	name: "Halt and Catch Fire",
+	desc: "Stops program execu..... Fire! FIRE EVERYWHERE!",
+	params: [],
+	execute(c, p, a) {
+		a.dispatch(CpuEvent.Halt, null);
+	},
+});
+
+ISA.insertInstruction(0xa0, {
+	name: "Call",
+	desc: "",
+	params: [new ConstParam("the subroutine at this memory address")],
+	execute(c, p, a) {
+		const current_address = c.getProgramCounter();
+		const success = c.pushCallStack(current_address);
+		// TODO Handle success/failure
+
+		const new_address = p[0];
+		c.setProgramCounter(new_address);
+
+		a.noStep();
+	},
+});
+
+ISA.insertInstruction(0xa1, {
+	name: "Return",
+	desc: "",
+	params: [],
+	execute(c, p, a) {
+		const new_address = c.popCallStack();
+		if (new_address === null) {
+			throw new Error("TODO handle this");
+		}
+
+		c.setProgramCounter(new_address + 1);
+
+		a.noStep();
+	},
+});
diff --git a/src/isaGenerator.ts b/src/isaGenerator.ts
index 2b4a088..5a9d630 100644
--- a/src/isaGenerator.ts
+++ b/src/isaGenerator.ts
@@ -3,15 +3,14 @@ import { Instruction, InstructionSet } from "./instructionSet";
 
 export function generate_isa(iset: InstructionSet): string {
 	const instructions: Array<[u8, Instruction]> = [];
-	for (const kv of iset.instructions.entries()) {
-		instructions.push(kv);
-	}
+
+	for (const kv of iset.instructions.entries()) instructions.push(kv);
+
 	let output_string = "INSTRUCTIONS\n";
-	let max_instr_name_len = 0;
-	for (const instruction of instructions) {
-		const short_description = instruction[1].name;
-		max_instr_name_len = Math.max(max_instr_name_len, short_description.length);
-	}
+
+	const max_instr_name_len = instructions.map((i) => i[1].name.length).reduce((acc, p) => Math.max(p, acc), 0);
+	instructions.sort((a, b) => a[0] - b[0]);
+
 	for (const instruction of instructions) {
 		const hex_code = format_hex(instruction[0]);
 		const short_description = instruction[1].name.padEnd(max_instr_name_len, " ");
diff --git a/src/style.scss b/src/style.scss
new file mode 100644
index 0000000..ea3f147
--- /dev/null
+++ b/src/style.scss
@@ -0,0 +1,192 @@
+* {
+	box-sizing: border-box;
+}
+
+pre {
+	font-size: 0.5em;
+}
+
+:root {
+	--Border: Yellow;
+	--mem-instruction: greenyellow;
+	--mem-register: Purple;
+	--mem-constant: lightgray;
+	--mem-memory: lightgray;
+	--mem-invalid: red;
+}
+
+body {
+	color: gray;
+	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 10px 500px;
+	grid-template-rows: min-content 1.5fr 10px 2fr 2fr min-content;
+	grid-template-areas:
+		"cycles registers regmemlabel . explainer "
+		"title .         .           . explainer "
+		"title .         .           . ."
+		"title .         .           . printout "
+		"title .         .           . printout "
+		".     buttons   buttons     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);
+	}
+}
+
+#printout {
+	border: 4px dashed yellow;
+	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 yellow;
+	#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);
+}
+
+#memory {
+	grid-area: memory;
+	display: grid;
+	grid-template-columns: repeat(16, min-content);
+	gap: 5px;
+	padding: 10px;
+	border: 5px solid yellow;
+	div {
+		text-align: center;
+		color: var(--color);
+	}
+	.program_counter {
+		outline: 3px solid orange;
+	}
+	.instruction_argument {
+		outline: 3px dashed purple;
+	}
+	.current_instruction {
+		outline: 3px dashed greenyellow;
+	}
+
+	.invalid {
+		&::after {
+			user-select: none;
+			float: right;
+			position: relative;
+			right: 0.5em;
+			width: 0px;
+			font-size: 0.5em;
+			content: "!";
+		}
+	}
+}
+
+#registers {
+	grid-area: registers;
+	border: 5px solid yellow;
+	border-bottom: none;
+
+	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:hover,
+label.button:hover {
+	color: white;
+}
+
+#controls_bar {
+	grid-area: buttons;
+	display: flex;
+	gap: 10px;
+}
diff --git a/src/ui.ts b/src/ui.ts
index ef7ad6e..4709e19 100644
--- a/src/ui.ts
+++ b/src/ui.ts
@@ -1,41 +1,40 @@
-import { ComputerState } from "./computer";
-import { CpuEvent, CpuEventHandler, MemoryCellType, UiEvent, UiEventHandler } from "./events";
+import { CpuEvent, CpuEventHandler, UiEvent, UiEventHandler } from "./events";
 import { $, el, format_hex, u8 } from "./etc";
-import { Instruction, ParameterType } from "./instructionSet";
 import { EventHandler } from "./eventHandler";
+import { InstructionExplainer } from "./ui/instructionExplainer";
+import { MemoryView } from "./ui/memoryView";
+import { ParamType } from "./instructionSet";
+import { frequencyIndicator } from "./ui/frequencyIndicator";
+// Certainly the messiest portion of this program
+// Needs to be broken into components
+
+let delay = 100;
 
 export class UI {
-	container: HTMLElement;
-	program_memory: HTMLElement;
-	program_memory_cells: Array = [];
 	registers: HTMLElement;
 	printout: HTMLElement;
-	instruction_explainer: HTMLElement;
 	register_cells: Array = [];
-	instruction_parsing_addresses: Array = [];
-	program_counter: u8 = 0;
 
 	auto_running: boolean;
 
 	events: UiEventHandler = new EventHandler() as UiEventHandler;
 
-	constructor(parent: HTMLElement) {
+	frequencyIndicator: frequencyIndicator;
+	memory: MemoryView;
+	instruction_explainer: InstructionExplainer;
+
+	constructor() {
 		for (const [, e_type] of Object.entries(UiEvent)) {
 			this.events.register_event(e_type as UiEvent);
 		}
 		this.events.seal();
-		this.container = parent;
+
+		this.memory = new MemoryView($("memory"));
+		this.frequencyIndicator = new frequencyIndicator($("cycles"));
+
+		this.instruction_explainer = new InstructionExplainer($("instruction_explainer"));
+
 		this.printout = $("printout");
-		this.instruction_explainer = $("instruction_explainer");
-		const program_mem = $("memory");
-		for (let i = 0; i < 256; i++) {
-			const mem_cell = el("div", `p_${i}`);
-			mem_cell.textContent = "00";
-			program_mem.appendChild(mem_cell);
-			this.program_memory_cells.push(mem_cell);
-		}
-		this.program_memory_cells[0].classList.add("div", "program_counter");
-		this.program_memory = program_mem;
 
 		const registers = $("registers");
 		for (let i = 0; i < 8; i++) {
@@ -66,21 +65,24 @@ export class UI {
 				this.stop_auto();
 			}
 
-			this.events.dispatch(UiEvent.RequestCpuCycle, null);
+			this.events.dispatch(UiEvent.RequestCpuCycle, 1);
+		});
+
+		$("delay_range").addEventListener("input", (e) => {
+			delay = parseInt((e.target as HTMLInputElement).value, 10);
+			// console.log(delay);
 		});
 	}
 
 	init_events(cpu_events: CpuEventHandler): void {
 		cpu_events.listen(CpuEvent.MemoryChanged, ({ address, value }) => {
-			this.program_memory_cells[address].textContent = format_hex(value);
+			this.memory.set_cell_value(address, value);
 		});
 		cpu_events.listen(CpuEvent.RegisterChanged, ({ register_no, value }) => {
 			this.register_cells[register_no].textContent = format_hex(value);
 		});
 		cpu_events.listen(CpuEvent.ProgramCounterChanged, ({ counter }) => {
-			this.program_memory_cells[this.program_counter].classList.remove("program_counter");
-			this.program_memory_cells[counter].classList.add("program_counter");
-			this.program_counter = counter;
+			this.memory.set_program_counter(counter);
 		});
 		cpu_events.listen(CpuEvent.Print, (char) => {
 			this.printout.textContent = (this.printout.textContent ?? "") + char;
@@ -89,78 +91,51 @@ export class UI {
 			this.reset();
 		});
 
-		const map: Map = new Map();
+		this.frequencyIndicator.init_cpu_events(cpu_events);
+		this.memory.init_cpu_events(cpu_events);
+		this.instruction_explainer.init_cpu_events(cpu_events);
 
-		map.set(MemoryCellType.Constant, "constant");
-		map.set(MemoryCellType.Instruction, "instruction");
-		map.set(MemoryCellType.InvalidInstruction, "invalid_instruction");
-		map.set(MemoryCellType.Memory, "memory");
-		map.set(MemoryCellType.Register, "register");
-		cpu_events.listen(CpuEvent.MemoryByteParsed, (e) => {
-			const { type, pos, code } = e;
-			const css_class = map.get(type);
-			if (css_class === undefined) {
-				throw new Error("Something went wrong");
+		cpu_events.listen(CpuEvent.ParameterParsed, ({ param, code, pos }) => {
+			this.memory.add_cell_class(pos, "instruction_argument");
+			this.instruction_explainer.add_param(param, pos, code);
+			const t = param.type;
+			this.memory.remove_cell_class(pos, "constant", "register", "memory", "instruction", "invalid");
+			let name: string = "";
+			if (t === ParamType.Const) {
+				name = "constant";
+			} else if (t === ParamType.Memory) {
+				name = "memory";
+			} else if (t === ParamType.Register) {
+				name = "register";
+			} else {
+				throw new Error("unreachable");
 			}
-			for (const other_class of map.values()) {
-				if (other_class === css_class) continue;
-				this.program_memory_cells[pos].classList.remove(other_class);
-			}
-			if (type === MemoryCellType.Instruction) {
-				while (this.instruction_parsing_addresses.length > 0) {
-					const num = this.instruction_parsing_addresses.pop();
-					if (num === undefined) {
-						throw new Error("Shouldn't happen");
-					}
-					this.program_memory_cells[num].classList.remove("instruction_argument");
-					this.program_memory_cells[num].classList.remove("current_instruction");
-				}
-				this.instruction_explainer.innerHTML = "";
-				const { instr } = e as { instr: Instruction };
-				this.program_memory_cells[pos].classList.add("current_instruction");
-				this.instruction_parsing_addresses.push(pos);
-				const instr_box = el("div", "expl_box");
-				const instr_icon = el("span", "expl_icon");
-				instr_icon.classList.add(css_class);
-				instr_icon.setAttribute("title", css_class.toUpperCase());
-				instr_icon.textContent = format_hex(code);
-				const instr_box_text = el("span", "expl_text");
-				instr_box_text.textContent = `${instr.name}`;
-				instr_box.appendChild(instr_icon);
-				instr_box.appendChild(instr_box_text);
-				this.instruction_explainer.appendChild(instr_box);
-			} else if (type !== MemoryCellType.InvalidInstruction) {
-				const { param } = e as { param: ParameterType };
-				this.program_memory_cells[pos].classList.add("instruction_argument");
-				this.instruction_parsing_addresses.push(pos);
-				const instr_box = el("div", "expl_box");
-				const instr_icon = el("span", "expl_icon");
-				instr_icon.classList.add(css_class);
-				instr_icon.setAttribute("title", css_class.toUpperCase());
-				instr_icon.textContent = format_hex(code);
-				const instr_box_text = el("span", "expl_text");
-				instr_box_text.textContent = `${param.desc}`;
-				instr_box.appendChild(instr_icon);
-				instr_box.appendChild(instr_box_text);
-				this.instruction_explainer.appendChild(instr_box);
-			}
-			this.program_memory_cells[pos].classList.add(css_class);
+			this.memory.add_cell_class(pos, name);
 		});
+		cpu_events.listen(CpuEvent.InstructionParsed, ({ instr, code, pos }) => {
+			this.instruction_explainer.add_instruction(instr, pos, code);
 
-		cpu_events.listen(CpuEvent.InstructionExecuted, ({ instr }) => {});
+			this.memory.remove_all_cell_class("instruction_argument");
+			this.memory.remove_all_cell_class("current_instruction");
+			this.memory.add_cell_class(pos, "current_instruction");
+			this.memory.remove_cell_class(pos, "constant", "register", "memory", "invalid");
+			this.memory.add_cell_class(pos, "instruction");
+		});
+		cpu_events.listen(CpuEvent.InvalidParsed, ({ code, pos }) => {
+			this.memory.remove_cell_class(pos, "constant", "register", "memory", "instruction");
+			this.memory.add_cell_class(pos, "invalid");
+			this.instruction_explainer.add_invalid(pos, code);
+		});
 	}
 
 	reset(): void {
 		this.stop_auto();
-		this.program_memory_cells.forEach((c) => {
-			c.className = "";
-			c.textContent = "00";
-		});
 		this.register_cells.forEach((r) => {
 			r.textContent = "00";
 		});
-		this.program_counter = 0;
-		this.program_memory_cells[0].classList.add("program_counter");
+		this.frequencyIndicator.reset();
+		this.instruction_explainer.reset();
+		this.memory.reset();
 		this.printout.textContent = "";
 	}
 
@@ -173,9 +148,8 @@ export class UI {
 			if (this.auto_running === false) {
 				return;
 			}
-			this.events.dispatch(UiEvent.RequestCpuCycle, null);
-			setTimeout(loop, speed);
-			// requestAnimationFrame(loop);
+			this.events.dispatch(UiEvent.RequestCpuCycle, 1);
+			setTimeout(loop, delay);
 		};
 		loop();
 	}
@@ -183,15 +157,4 @@ export class UI {
 	stop_auto(): void {
 		this.auto_running = false;
 	}
-
-	state_update_event(state: ComputerState): void {
-		const current_instr = state.current_instruction;
-		if (current_instr !== null) {
-			this.program_memory_cells[current_instr.pos].classList.add("current_instruction");
-			for (let i = 0; i < current_instr.params_found; i++) {
-				const offset = i + 1 + current_instr.pos;
-				this.program_memory_cells[offset].classList.add("instruction_argument");
-			}
-		}
-	}
 }
diff --git a/src/ui/editableHex.ts b/src/ui/editableHex.ts
new file mode 100644
index 0000000..f3eafbe
--- /dev/null
+++ b/src/ui/editableHex.ts
@@ -0,0 +1,122 @@
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+function set_caret(el: any, pos: number): boolean {
+	const selection = window.getSelection();
+	const range = document.createRange();
+	if (selection === null) {
+		return false;
+	}
+
+	selection.removeAllRanges();
+	range.selectNode(el);
+
+	range.setStart(el, pos);
+	range.setEnd(el, pos);
+	range.collapse(true);
+	selection.removeAllRanges();
+	selection.addRange(range);
+	el.focus();
+	return true;
+}
+
+function get_caret(el: HTMLElement): null | number {
+	const sel = window.getSelection();
+	if (sel === null) {
+		return null;
+	}
+	const pos = sel.getRangeAt(0).startOffset;
+	const endPos = pos + Array.from(el.innerHTML.slice(0, pos)).length - el.innerHTML.slice(0, pos).split("").length;
+	return endPos;
+}
+
+const hex_characters = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"];
+function replace_non_hex(c: string): string {
+	if (hex_characters.includes(c)) {
+		return c;
+	}
+	return "0";
+}
+
+function editable_constraints(e: Event): boolean {
+	const target = e.target as HTMLDivElement;
+	const text = target.innerHTML ?? "";
+	if (text.length !== 2) {
+		const pos = get_caret(target);
+		const new_str = [...(target.textContent ?? "").substring(0, 2).padStart(2, "0").toUpperCase()]
+			.map(replace_non_hex)
+			.join("");
+		target.innerHTML = "";
+		// For the caret selection to work right, each character must be its own node, complicating this greatly
+		target.append(new_str.substring(0, 1), new_str.substring(1));
+
+		if (pos !== null) {
+			if (pos >= 2) {
+				return true;
+			}
+			set_caret(target, pos);
+		}
+	}
+	return false;
+}
+
+function at(l: Array, i: number): T | null {
+	if (i < 0) {
+		return null;
+	}
+	if (i >= l.length) {
+		return null;
+	}
+	return l[i];
+}
+
+export function make_editable(
+	list: Array,
+	width: number,
+	height: number,
+	on_edit: (n: number, value: string) => void
+): void {
+	for (const [i, cell] of list.entries()) {
+		cell.setAttribute("contenteditable", "true");
+		cell.setAttribute("spellcheck", "false");
+		const next: null | HTMLElement = at(list, i + 1);
+		const prev: null | HTMLElement = at(list, i - 1);
+		const up: null | HTMLElement = at(list, i - width);
+		const down: null | HTMLElement = at(list, i + width);
+		cell.addEventListener("keydown", (e) => {
+			const caret_position = get_caret(cell);
+			const k = e.key;
+			if (k === "ArrowUp") {
+				(up ?? prev)?.focus();
+				cell.blur();
+			} else if (k === "ArrowDown") {
+				(down ?? next)?.focus();
+				cell.blur();
+			} else if ((k === "ArrowLeft" || k === "Backspace") && caret_position === 0) {
+				prev?.focus();
+				cell.blur();
+			} else if (k === "ArrowRight" && caret_position === 1) {
+				next?.focus();
+				cell.blur();
+			} else if (k === "Enter") {
+				cell.blur();
+			} else if (k === "Escape") {
+				cell.blur();
+				return;
+			} else {
+				return;
+			}
+			e.preventDefault();
+		});
+		let previous_text = cell.textContent ?? "";
+		cell.addEventListener("input", (e) => {
+			const current_text = cell.textContent ?? "";
+			if (current_text !== previous_text) {
+				previous_text = cell.textContent ?? "";
+				on_edit(i, current_text);
+			}
+			if (editable_constraints(e) === true) {
+				next?.focus();
+				cell.blur();
+			}
+		});
+	}
+}
diff --git a/src/ui/frequencyIndicator.ts b/src/ui/frequencyIndicator.ts
new file mode 100644
index 0000000..3a689d0
--- /dev/null
+++ b/src/ui/frequencyIndicator.ts
@@ -0,0 +1,52 @@
+import { CpuEvent, CpuEventHandler, UiEventHandler } from "../events";
+import { UiComponent } from "./uiComponent";
+
+export class frequencyIndicator implements UiComponent {
+	element: HTMLElement;
+	private running: number | null = null;
+	private count: number = 0;
+	private last_value: number = 0;
+	private last_time: number = 0;
+	constructor(element: HTMLElement) {
+		this.element = element;
+		this.start();
+	}
+
+	start(): void {
+		if (this.running !== null) {
+			throw new Error("Tried starting frequencyIndicator twice!");
+		}
+		setInterval(this.update_indicator.bind(this), 1000);
+	}
+
+	stop(): void {
+		if (this.running === null) return;
+		clearInterval(this.running);
+		this.running = null;
+	}
+
+	update_indicator(): void {
+		if (this.last_value !== this.count) {
+			this.element.textContent = `${this.count}hz`;
+			this.last_value = this.count;
+		}
+		this.count = 0;
+	}
+
+	init_events(eh: UiEventHandler): void {
+		this;
+	}
+	clock_cycle(): void {
+		this.count += 1;
+	}
+	reset(): void {
+		this.stop();
+		this.count = 0;
+		this.last_value = 0;
+	}
+	init_cpu_events(c: CpuEventHandler) {
+		c.listen(CpuEvent.ClockCycle, () => {
+			this.count += 1;
+		});
+	}
+}
diff --git a/src/ui/instructionExplainer.ts b/src/ui/instructionExplainer.ts
new file mode 100644
index 0000000..ede412e
--- /dev/null
+++ b/src/ui/instructionExplainer.ts
@@ -0,0 +1,55 @@
+import { el, format_hex, u8 } from "../etc";
+import { CpuEvent, CpuEventHandler, UiEventHandler } from "../events";
+import { Instruction, ParamType, ParameterType } from "../instructionSet";
+import { UiComponent } from "./uiComponent";
+
+export class InstructionExplainer implements UiComponent {
+	element: HTMLElement;
+	constructor(element: HTMLElement) {
+		this.element = element;
+	}
+	add_instruction(instr: Instruction, pos: u8, byte: u8): void {
+		this.reset();
+		this.add_box(format_hex(byte), instr.name, "instruction");
+	}
+
+	private add_box(box_icon_text: string, name: string, css_class: string): void {
+		const instr_box = el("div", "expl_box");
+		const instr_icon = el("span", "expl_icon");
+		instr_icon.classList.add(css_class);
+		instr_icon.setAttribute("title", css_class.toUpperCase());
+		instr_icon.textContent = box_icon_text;
+		const instr_box_text = el("span", "expl_text");
+		instr_box_text.textContent = name;
+		instr_box.appendChild(instr_icon);
+		instr_box.appendChild(instr_box_text);
+		this.element.appendChild(instr_box);
+	}
+
+	add_param(param: ParameterType, pos: u8, byte: u8): void {
+		const t = param.type;
+		let name;
+		if (t === ParamType.Const) {
+			name = "constant";
+		} else if (t === ParamType.Memory) {
+			name = "memory";
+		} else if (t === ParamType.Register) {
+			name = "register";
+		} else {
+			throw new Error("unreachable");
+		}
+		this.add_box(format_hex(byte), param.desc, name);
+	}
+
+	add_invalid(pos: u8, byte: u8) {
+		this.reset();
+		this.add_box(format_hex(byte), "Invalid Instruction", "invalid");
+	}
+
+	init_events(eh: UiEventHandler): void {}
+	init_cpu_events(c: CpuEventHandler) {}
+
+	reset(): void {
+		this.element.innerHTML = "";
+	}
+}
diff --git a/src/ui/memoryView.ts b/src/ui/memoryView.ts
new file mode 100644
index 0000000..a1de6e0
--- /dev/null
+++ b/src/ui/memoryView.ts
@@ -0,0 +1,78 @@
+import { el, format_hex, u8 } from "../etc";
+import { CpuEventHandler, UiEventHandler } from "../events";
+import { UiComponent } from "./uiComponent";
+
+export enum CellTag {
+	ProgramCounter = "program_counter",
+}
+
+type MemoryCell = {
+	el: HTMLElement;
+};
+
+export class MemoryView implements UiComponent {
+	element: HTMLElement;
+	cells: Array = [];
+	program_counter: number = 0;
+	constructor(element: HTMLElement) {
+		this.element = element;
+		for (let i = 0; i < 256; i++) {
+			const mem_cell_el = el("div");
+			mem_cell_el.textContent = "00";
+			element.appendChild(mem_cell_el);
+			const mem_cell = { el: mem_cell_el, tags: [] };
+			this.cells.push(mem_cell);
+		}
+		this.set_program_counter(0);
+	}
+
+	add_cell_class(address: u8, ...css_class: string[]): void {
+		for (const str of css_class) {
+			this.cells[address].el.classList.add(str);
+		}
+	}
+
+	remove_cell_class(address: u8, ...css_class: string[]): void {
+		for (const str of css_class) {
+			this.cells[address].el.classList.remove(str);
+		}
+	}
+
+	remove_all_cell_class(css_class: string): void {
+		for (const cell of this.cells) {
+			cell.el.classList.remove(css_class);
+		}
+	}
+
+	add_cell_class_exclusive(address: u8, css_class: string): void {
+		this.remove_all_cell_class(css_class);
+		this.add_cell_class(address, css_class);
+	}
+
+	set_cell_value(address: u8, value: u8): void {
+		this.cells[address].el.textContent = format_hex(value);
+	}
+
+	set_program_counter(position: number): void {
+		this.cells[this.program_counter].el.classList.remove("program_counter");
+		this.cells[position].el.classList.add("program_counter");
+		this.program_counter = position;
+	}
+
+	reset(): void {
+		this.element.innerHTML = "";
+		for (let i = 0; i < 256; i++) {
+			const mem_cell_el = el("div");
+			mem_cell_el.textContent = "00";
+			this.element.appendChild(mem_cell_el);
+			const mem_cell = { el: mem_cell_el };
+			this.cells.push(mem_cell);
+		}
+		this.set_program_counter(0);
+	}
+
+	init_events(eh: UiEventHandler): void {
+		this;
+	}
+	init_cpu_events(c: CpuEventHandler) {}
+}
diff --git a/src/ui/uiComponent.ts b/src/ui/uiComponent.ts
new file mode 100644
index 0000000..c7d281e
--- /dev/null
+++ b/src/ui/uiComponent.ts
@@ -0,0 +1,9 @@
+import { u8 } from "../etc";
+import { CpuEventHandler, UiEventHandler } from "../events";
+
+export interface UiComponent {
+	element: HTMLElement;
+	reset: () => void;
+	init_events: (ui: UiEventHandler) => void;
+	init_cpu_events: (c: CpuEventHandler) => void;
+}
diff --git a/style.css b/style.css
deleted file mode 100644
index 7d12fd3..0000000
--- a/style.css
+++ /dev/null
@@ -1,136 +0,0 @@
-* {
-	box-sizing: border-box;
-}
-
-#memory {
-	display: grid;
-	grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
-	grid-gap: 5px;
-	padding: 10px;
-	border: 5px solid yellow;
-	/* color: lightgray; */
-}
-
-pre {
-	font-size: 0.5em;
-}
-
-#memory .instruction {
-	color: greenyellow;
-}
-#memory .constant {
-	color: purple;
-}
-#memory .register {
-	color: orange;
-}
-#memory .memory {
-	color: pink;
-}
-#memory .invalid_instruction {
-	color: maroon;
-}
-
-#memory div {
-	aspect-ratio: 1;
-	text-align: center;
-	margin: auto;
-}
-
-body {
-	color: gray;
-	background-color: black;
-	font-size: 2.5em;
-	font-family: monospace;
-}
-
-#registers div {
-	max-height: min-content;
-	margin-block: auto;
-}
-
-#subcontainer {
-	display: flex;
-	flex-direction: row;
-	column-gap: 15px;
-}
-
-#labelcontainer {
-	column-gap: 18px;
-	font-size: 0.85em;
-	display: flex;
-	align-items: center;
-	justify-content: space-between;
-	user-select: none;
-}
-
-#main {
-	display: flex;
-	flex-direction: row;
-}
-
-#title {
-	writing-mode: vertical-lr;
-	text-align: left;
-	user-select: none;
-	transform: scale(-1, -1);
-}
-
-#printout {
-	border: 4px dashed yellow;
-	width: 1000px;
-	padding: 10px;
-	margin-left: 20px;
-	word-wrap: break-word;
-	word-break: break-all;
-}
-
-#memory div.program_counter {
-	outline: 3px solid orange;
-}
-
-#memory div.instruction_argument {
-	outline: 3px dashed purple;
-}
-#memory div.current_instruction {
-	outline: 3px dashed greenyellow;
-}
-
-#registers {
-	border: 5px solid yellow;
-	border-bottom: none;
-	grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
-	max-width: fit-content;
-	display: grid;
-	column-gap: 5px;
-	color: lightgray;
-	padding: 10px;
-}
-
-#container {
-	font-family: monospace;
-	max-width: min-content;
-	max-height: min-content;
-}
-
-button,
-label.button {
-	border: 4px solid yellow;
-	color: gray;
-	margin: 10px;
-	margin-inline: 8px;
-	padding: 10px;
-	font-size: 0.8em;
-	font-family: monospace;
-	background-color: transparent;
-	cursor: pointer;
-	user-select: none;
-}
-button:hover,
-label.button:hover {
-	color: white;
-}
-#controls_bar {
-	margin-left: 1.12em;
-	display: flex;
-}
diff --git a/webpack.config.js b/webpack.config.js
index 944d810..60de58d 100755
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -11,12 +11,23 @@ const config = {
 			},
 			{
 				test: /\.html/,
-				type: "asset/resource"         
-			}
+				type: "asset/resource",
+			},
+			{
+				test: /\.s[ac]ss$/i,
+				use: [
+					// Creates `style` nodes from JS strings
+					"style-loader",
+					// Translates CSS into CommonJS
+					"css-loader",
+					// Compiles Sass to CSS
+					"sass-loader",
+				],
+			},
 		],
 	},
 	resolve: {
-		extensions: [".ts"],
+		extensions: [".ts", ".scss", ".css"],
 	},
 	output: {
 		filename: "main.js",
@@ -32,4 +43,4 @@ module.exports = (env, argv) => {
 	}
 
 	return config;
-};
\ No newline at end of file
+};