major cleanup
This commit is contained in:
parent
49937af24e
commit
7552e539c5
276
package-lock.json
generated
276
package-lock.json
generated
|
@ -107,9 +107,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/js": {
|
"node_modules/@eslint/js": {
|
||||||
"version": "8.56.0",
|
"version": "8.57.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
|
||||||
"integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==",
|
"integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||||
|
@ -171,32 +171,32 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/gen-mapping": {
|
"node_modules/@jridgewell/gen-mapping": {
|
||||||
"version": "0.3.3",
|
"version": "0.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
|
||||||
"integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
|
"integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/set-array": "^1.0.1",
|
"@jridgewell/set-array": "^1.2.1",
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||||
"@jridgewell/trace-mapping": "^0.3.9"
|
"@jridgewell/trace-mapping": "^0.3.24"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/resolve-uri": {
|
"node_modules/@jridgewell/resolve-uri": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||||
"integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
|
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/set-array": {
|
"node_modules/@jridgewell/set-array": {
|
||||||
"version": "1.1.2",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
|
||||||
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
|
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
|
@ -219,9 +219,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/trace-mapping": {
|
"node_modules/@jridgewell/trace-mapping": {
|
||||||
"version": "0.3.22",
|
"version": "0.3.25",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
|
||||||
"integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==",
|
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/resolve-uri": "^3.1.0",
|
"@jridgewell/resolve-uri": "^3.1.0",
|
||||||
|
@ -276,9 +276,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/eslint": {
|
"node_modules/@types/eslint": {
|
||||||
"version": "8.56.2",
|
"version": "8.56.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.5.tgz",
|
||||||
"integrity": "sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw==",
|
"integrity": "sha512-u5/YPJHo1tvkSF2CE0USEkxon82Z5DBy2xR+qfyYNszpX9qcs4sT6uq2kBbj4BXY1+DBGDPnrhMZV3pKWGNukw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/estree": "*",
|
"@types/estree": "*",
|
||||||
|
@ -308,18 +308,18 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "20.11.17",
|
"version": "20.11.25",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.17.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.25.tgz",
|
||||||
"integrity": "sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==",
|
"integrity": "sha512-TBHyJxk2b7HceLVGFcpAUjsa5zIdsPWlR6XHfyGzd0SFu+/NFgQgMAl96MSDZgQDvJAvV6BKsFOrt6zIL09JDw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~5.26.4"
|
"undici-types": "~5.26.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/semver": {
|
"node_modules/@types/semver": {
|
||||||
"version": "7.5.7",
|
"version": "7.5.8",
|
||||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
|
||||||
"integrity": "sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==",
|
"integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
|
@ -903,9 +903,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/browserslist": {
|
"node_modules/browserslist": {
|
||||||
"version": "4.22.3",
|
"version": "4.23.0",
|
||||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.3.tgz",
|
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
|
||||||
"integrity": "sha512-UAp55yfwNv0klWNapjs/ktHoguxuQNGnOzxYmfnXIS+8AsRDZkSDxg7R1AX3GKzn078SBI5dzwzj/Yx0Or0e3A==",
|
"integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -922,8 +922,8 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"caniuse-lite": "^1.0.30001580",
|
"caniuse-lite": "^1.0.30001587",
|
||||||
"electron-to-chromium": "^1.4.648",
|
"electron-to-chromium": "^1.4.668",
|
||||||
"node-releases": "^2.0.14",
|
"node-releases": "^2.0.14",
|
||||||
"update-browserslist-db": "^1.0.13"
|
"update-browserslist-db": "^1.0.13"
|
||||||
},
|
},
|
||||||
|
@ -950,9 +950,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001587",
|
"version": "1.0.30001597",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001587.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001597.tgz",
|
||||||
"integrity": "sha512-HMFNotUmLXn71BQxg8cijvqxnIAofforZOwGsxyXJ0qugTdspUF4sPSJ2vhgprHCB996tIDzEq1ubumPDV8ULA==",
|
"integrity": "sha512-7LjJvmQU6Sj7bL0j5b5WY/3n7utXUJvAe1lxhsHDbLmwX9mdL86Yjtr+5SRCyf8qME4M7pU2hswj0FpyBVCv9w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -1104,34 +1104,6 @@
|
||||||
"webpack": "^5.1.0"
|
"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": {
|
"node_modules/copy-webpack-plugin/node_modules/globby": {
|
||||||
"version": "14.0.1",
|
"version": "14.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/globby/-/globby-14.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/globby/-/globby-14.0.1.tgz",
|
||||||
|
@ -1152,12 +1124,6 @@
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"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": {
|
"node_modules/copy-webpack-plugin/node_modules/path-type": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz",
|
||||||
|
@ -1170,25 +1136,6 @@
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"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": {
|
"node_modules/copy-webpack-plugin/node_modules/slash": {
|
||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz",
|
||||||
|
@ -1310,15 +1257,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.4.668",
|
"version": "1.4.700",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.668.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.700.tgz",
|
||||||
"integrity": "sha512-ZOBocMYCehr9W31+GpMclR+KBaDZOoAEabLdhpZ8oU1JFDwIaFY0UDbpXVEUFc0BIP2O2Qn3rkfCjQmMR4T/bQ==",
|
"integrity": "sha512-40dqKQ3F7C8fbBEmjSeJ+qEHCKzPyrP9SkeIBZ3wSCUH9nhWStrDz030XlDzlhNhlul1Z0fz7TpDFnsIzo4Jtg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/enhanced-resolve": {
|
"node_modules/enhanced-resolve": {
|
||||||
"version": "5.15.0",
|
"version": "5.16.0",
|
||||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz",
|
||||||
"integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==",
|
"integrity": "sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"graceful-fs": "^4.2.4",
|
"graceful-fs": "^4.2.4",
|
||||||
|
@ -1368,16 +1315,16 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint": {
|
"node_modules/eslint": {
|
||||||
"version": "8.56.0",
|
"version": "8.57.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
|
||||||
"integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==",
|
"integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.2.0",
|
"@eslint-community/eslint-utils": "^4.2.0",
|
||||||
"@eslint-community/regexpp": "^4.6.1",
|
"@eslint-community/regexpp": "^4.6.1",
|
||||||
"@eslint/eslintrc": "^2.1.4",
|
"@eslint/eslintrc": "^2.1.4",
|
||||||
"@eslint/js": "8.56.0",
|
"@eslint/js": "8.57.0",
|
||||||
"@humanwhocodes/config-array": "^0.11.13",
|
"@humanwhocodes/config-array": "^0.11.14",
|
||||||
"@humanwhocodes/module-importer": "^1.0.1",
|
"@humanwhocodes/module-importer": "^1.0.1",
|
||||||
"@nodelib/fs.walk": "^1.2.8",
|
"@nodelib/fs.walk": "^1.2.8",
|
||||||
"@ungap/structured-clone": "^1.2.0",
|
"@ungap/structured-clone": "^1.2.0",
|
||||||
|
@ -1668,9 +1615,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/flatted": {
|
"node_modules/flatted": {
|
||||||
"version": "3.2.9",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz",
|
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
|
||||||
"integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",
|
"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/fs.realpath": {
|
"node_modules/fs.realpath": {
|
||||||
|
@ -1819,9 +1766,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/hasown": {
|
"node_modules/hasown": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||||
"integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==",
|
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"function-bind": "^1.1.2"
|
"function-bind": "^1.1.2"
|
||||||
|
@ -2772,9 +2719,9 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/sass": {
|
"node_modules/sass": {
|
||||||
"version": "1.71.0",
|
"version": "1.71.1",
|
||||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.71.0.tgz",
|
"resolved": "https://registry.npmjs.org/sass/-/sass-1.71.1.tgz",
|
||||||
"integrity": "sha512-HKKIKf49Vkxlrav3F/w6qRuPcmImGVbIXJ2I3Kg0VMA+3Bav+8yE9G5XmP5lMj6nl4OlqbPftGAscNaNu28b8w==",
|
"integrity": "sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chokidar": ">=3.0.0 <4.0.0",
|
"chokidar": ">=3.0.0 <4.0.0",
|
||||||
|
@ -2789,9 +2736,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/sass-loader": {
|
"node_modules/sass-loader": {
|
||||||
"version": "14.1.0",
|
"version": "14.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-14.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-14.1.1.tgz",
|
||||||
"integrity": "sha512-LS2mLeFWA+orYxHNu+O18Xe4jR0kyamNOOUsE3NyBP4DvIL+8stHpNX0arYTItdPe80kluIiJ7Wfe/9iHSRO0Q==",
|
"integrity": "sha512-QX8AasDg75monlybel38BZ49JP5Z+uSKfKwF2rO7S74BywaRmGQMUBw9dtkS+ekyM/QnP+NOrRYq8ABMZ9G8jw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"neo-async": "^2.6.2"
|
"neo-async": "^2.6.2"
|
||||||
|
@ -2829,23 +2776,58 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/schema-utils": {
|
"node_modules/schema-utils": {
|
||||||
"version": "3.3.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz",
|
||||||
"integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
|
"integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/json-schema": "^7.0.8",
|
"@types/json-schema": "^7.0.9",
|
||||||
"ajv": "^6.12.5",
|
"ajv": "^8.9.0",
|
||||||
"ajv-keywords": "^3.5.2"
|
"ajv-formats": "^2.1.1",
|
||||||
|
"ajv-keywords": "^5.1.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10.13.0"
|
"node": ">= 12.13.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
"url": "https://opencollective.com/webpack"
|
"url": "https://opencollective.com/webpack"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/schema-utils/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/schema-utils/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/schema-utils/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/semver": {
|
"node_modules/semver": {
|
||||||
"version": "7.6.0",
|
"version": "7.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
|
||||||
|
@ -3023,9 +3005,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/terser": {
|
"node_modules/terser": {
|
||||||
"version": "5.27.0",
|
"version": "5.29.1",
|
||||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/terser/-/terser-5.29.1.tgz",
|
||||||
"integrity": "sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==",
|
"integrity": "sha512-lZQ/fyaIGxsbGxApKmoPTODIzELy3++mXhS5hOqaAWZjQtpq/hFHAc+rm29NND1rYRxRWKcjuARNwULNXa5RtQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/source-map": "^0.3.3",
|
"@jridgewell/source-map": "^0.3.3",
|
||||||
|
@ -3074,6 +3056,24 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/terser-webpack-plugin/node_modules/schema-utils": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/json-schema": "^7.0.8",
|
||||||
|
"ajv": "^6.12.5",
|
||||||
|
"ajv-keywords": "^3.5.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.13.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/webpack"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/text-table": {
|
"node_modules/text-table": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
|
||||||
|
@ -3093,9 +3093,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ts-api-utils": {
|
"node_modules/ts-api-utils": {
|
||||||
"version": "1.2.1",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
|
||||||
"integrity": "sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==",
|
"integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16"
|
"node": ">=16"
|
||||||
|
@ -3149,9 +3149,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.3.3",
|
"version": "5.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz",
|
||||||
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
|
"integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
|
@ -3238,9 +3238,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/webpack": {
|
"node_modules/webpack": {
|
||||||
"version": "5.90.2",
|
"version": "5.90.3",
|
||||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.2.tgz",
|
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.3.tgz",
|
||||||
"integrity": "sha512-ziXu8ABGr0InCMEYFnHrYweinHK2PWrMqnwdHk2oK3rRhv/1B+2FnfwYv5oD+RrknK/Pp/Hmyvu+eAsaMYhzCw==",
|
"integrity": "sha512-h6uDYlWCctQRuXBs1oYpVe6sFcWedl0dpcVaTf/YF67J9bKvwJajFulMVSYKHrksMB3I/pIagRzDxwxkebuzKA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/eslint-scope": "^3.7.3",
|
"@types/eslint-scope": "^3.7.3",
|
||||||
|
@ -3383,6 +3383,24 @@
|
||||||
"node": ">=4.0"
|
"node": ">=4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/webpack/node_modules/schema-utils": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/json-schema": "^7.0.8",
|
||||||
|
"ajv": "^6.12.5",
|
||||||
|
"ajv-keywords": "^3.5.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.13.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/webpack"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { CpuEvent, CpuEventHandler, UiCpuSignal, UiCpuSignalHandler, UiEvent, Ui
|
||||||
import { byte_array_to_js_source, format_hex } from "./etc";
|
import { byte_array_to_js_source, format_hex } from "./etc";
|
||||||
import { Instruction, ISA } from "./instructionSet";
|
import { Instruction, ISA } from "./instructionSet";
|
||||||
import { m256, u2, u3, u8 } from "./num";
|
import { m256, u2, u3, u8 } from "./num";
|
||||||
|
import { DEFAULT_VRAM_BANK } from "./constants";
|
||||||
|
|
||||||
export type TempInstrState = {
|
export type TempInstrState = {
|
||||||
pos: u8;
|
pos: u8;
|
||||||
|
@ -25,6 +26,7 @@ export class Computer {
|
||||||
private carry_flag: boolean = false;
|
private carry_flag: boolean = false;
|
||||||
private program_counter: u8 = 0;
|
private program_counter: u8 = 0;
|
||||||
private bank: u2 = 0;
|
private bank: u2 = 0;
|
||||||
|
private vram_bank: u2 = DEFAULT_VRAM_BANK;
|
||||||
private current_instr: TempInstrState | null = null;
|
private current_instr: TempInstrState | null = null;
|
||||||
events: CpuEventHandler = new CpuEventHandler();
|
events: CpuEventHandler = new CpuEventHandler();
|
||||||
|
|
||||||
|
@ -142,9 +144,9 @@ export class Computer {
|
||||||
return this.call_stack.pop() ?? null;
|
return this.call_stack.pop() ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
setBank(bank_no: u2): void {
|
setBank(bank: u2): void {
|
||||||
this.events.dispatch(CpuEvent.SwitchBank, { bank: bank_no });
|
this.events.dispatch(CpuEvent.SwitchBank, { bank: bank });
|
||||||
this.bank = bank_no;
|
this.bank = bank;
|
||||||
}
|
}
|
||||||
|
|
||||||
setCarry(state: boolean): void {
|
setCarry(state: boolean): void {
|
||||||
|
@ -156,6 +158,11 @@ export class Computer {
|
||||||
return this.carry_flag;
|
return this.carry_flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setVramBank(bank: u2): void {
|
||||||
|
this.vram_bank = bank;
|
||||||
|
this.events.dispatch(CpuEvent.SetVramBank, { bank });
|
||||||
|
}
|
||||||
|
|
||||||
reset(): void {
|
reset(): void {
|
||||||
this.events.dispatch(CpuEvent.Reset);
|
this.events.dispatch(CpuEvent.Reset);
|
||||||
this.banks = init_banks();
|
this.banks = init_banks();
|
||||||
|
@ -164,6 +171,7 @@ export class Computer {
|
||||||
this.current_instr = null;
|
this.current_instr = null;
|
||||||
this.program_counter = 0;
|
this.program_counter = 0;
|
||||||
this.carry_flag = false;
|
this.carry_flag = false;
|
||||||
|
this.vram_bank = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
init_events(ui: UiCpuSignalHandler): void {
|
init_events(ui: UiCpuSignalHandler): void {
|
||||||
|
@ -172,10 +180,11 @@ export class Computer {
|
||||||
});
|
});
|
||||||
ui.listen(UiCpuSignal.RequestMemoryChange, ({ address, bank, value }) => this.setMemory(address, value, bank));
|
ui.listen(UiCpuSignal.RequestMemoryChange, ({ address, bank, value }) => this.setMemory(address, value, bank));
|
||||||
ui.listen(UiCpuSignal.RequestRegisterChange, ({ register_no, value }) => this.setRegister(register_no, value));
|
ui.listen(UiCpuSignal.RequestRegisterChange, ({ register_no, value }) => this.setRegister(register_no, value));
|
||||||
ui.listen(UiCpuSignal.RequestMemoryDump, () =>
|
ui.listen(UiCpuSignal.RequestMemoryDump, (callback) => callback(this.dump_memory()));
|
||||||
this.events.dispatch(CpuEvent.MemoryDumped, { memory: this.dump_memory() })
|
|
||||||
);
|
|
||||||
ui.listen(UiCpuSignal.RequestCpuReset, () => this.reset());
|
ui.listen(UiCpuSignal.RequestCpuReset, () => this.reset());
|
||||||
|
ui.listen(UiCpuSignal.RequestProgramCounterChange, ({ address }) => {
|
||||||
|
this.setProgramCounter(address);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
load_memory(program: Array<u8>): void {
|
load_memory(program: Array<u8>): void {
|
||||||
|
|
3
src/constants.ts
Normal file
3
src/constants.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
const DEFAULT_VRAM_BANK = 3;
|
||||||
|
|
||||||
|
export { DEFAULT_VRAM_BANK };
|
29
src/etc.ts
29
src/etc.ts
|
@ -3,6 +3,9 @@
|
||||||
* @copyright Alexander Bass 2024
|
* @copyright Alexander Bass 2024
|
||||||
* @license GPL-3.0
|
* @license GPL-3.0
|
||||||
*/
|
*/
|
||||||
|
import el from "./util/elementMaker";
|
||||||
|
// Re-export el
|
||||||
|
export { el };
|
||||||
import { u8 } from "./num";
|
import { u8 } from "./num";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,28 +29,6 @@ export const byte_array_to_js_source = (bytes: Array<u8>): string => {
|
||||||
return str;
|
return str;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an html element
|
|
||||||
* @param type
|
|
||||||
* @param id id attribute to set
|
|
||||||
*/
|
|
||||||
export function el<E extends keyof HTMLElementTagNameMap>(
|
|
||||||
type: E,
|
|
||||||
id?: string,
|
|
||||||
class_list?: string
|
|
||||||
): HTMLElementTagNameMap[E];
|
|
||||||
export function el(type: string, id?: string, class_list?: string): HTMLElement | undefined {
|
|
||||||
const element = document.createElement(type);
|
|
||||||
if (id !== undefined) {
|
|
||||||
element.id = id;
|
|
||||||
}
|
|
||||||
if (class_list !== undefined) {
|
|
||||||
element.className = class_list;
|
|
||||||
}
|
|
||||||
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type NonEmptyArray<T> = T[] & { 0: T };
|
export type NonEmptyArray<T> = T[] & { 0: T };
|
||||||
|
|
||||||
export const SVG_NS = "http://www.w3.org/2000/svg";
|
export const SVG_NS = "http://www.w3.org/2000/svg";
|
||||||
|
@ -57,6 +38,10 @@ export function in_range(check: number, start: number, end: number): boolean {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the `i`th element in a list. Negative indices return null.
|
||||||
|
* Out of range indices return null
|
||||||
|
*/
|
||||||
export function at<T>(l: Array<T>, i: number): T | null {
|
export function at<T>(l: Array<T>, i: number): T | null {
|
||||||
if (i < 0) {
|
if (i < 0) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -22,10 +22,10 @@ export enum CpuEvent {
|
||||||
Print,
|
Print,
|
||||||
Reset,
|
Reset,
|
||||||
Halt,
|
Halt,
|
||||||
MemoryDumped,
|
|
||||||
MemoryAccessed,
|
MemoryAccessed,
|
||||||
SwitchBank,
|
SwitchBank,
|
||||||
SetFlagCarry,
|
SetFlagCarry,
|
||||||
|
SetVramBank,
|
||||||
}
|
}
|
||||||
|
|
||||||
type VoidDataCpuEventList = CpuEvent.Halt | CpuEvent.Reset | CpuEvent.Cycle;
|
type VoidDataCpuEventList = CpuEvent.Halt | CpuEvent.Reset | CpuEvent.Cycle;
|
||||||
|
@ -40,9 +40,9 @@ interface CpuEventMap {
|
||||||
[CpuEvent.InvalidParsed]: { pos: u8; code: u8 };
|
[CpuEvent.InvalidParsed]: { pos: u8; code: u8 };
|
||||||
[CpuEvent.InstructionExecuted]: { instr: Instruction };
|
[CpuEvent.InstructionExecuted]: { instr: Instruction };
|
||||||
[CpuEvent.SwitchBank]: { bank: u2 };
|
[CpuEvent.SwitchBank]: { bank: u2 };
|
||||||
|
[CpuEvent.SetVramBank]: { bank: u2 };
|
||||||
[CpuEvent.Print]: string;
|
[CpuEvent.Print]: string;
|
||||||
[CpuEvent.SetFlagCarry]: boolean;
|
[CpuEvent.SetFlagCarry]: boolean;
|
||||||
[CpuEvent.MemoryDumped]: { memory: [Uint8Array, Uint8Array, Uint8Array, Uint8Array] };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CpuEventHandler extends EventHandler<CpuEvent> {
|
export interface CpuEventHandler extends EventHandler<CpuEvent> {
|
||||||
|
@ -68,14 +68,17 @@ export enum UiCpuSignal {
|
||||||
RequestRegisterChange,
|
RequestRegisterChange,
|
||||||
RequestCpuReset,
|
RequestCpuReset,
|
||||||
RequestMemoryDump,
|
RequestMemoryDump,
|
||||||
|
RequestProgramCounterChange,
|
||||||
}
|
}
|
||||||
|
|
||||||
type VoidDataUiCpuSignalList = UiCpuSignal.RequestCpuReset | UiCpuSignal.RequestMemoryDump;
|
type VoidDataUiCpuSignalList = UiCpuSignal.RequestCpuReset;
|
||||||
|
|
||||||
interface UiCpuSignalMap {
|
interface UiCpuSignalMap {
|
||||||
[UiCpuSignal.RequestCpuCycle]: number;
|
[UiCpuSignal.RequestCpuCycle]: number;
|
||||||
[UiCpuSignal.RequestMemoryChange]: { address: u8; bank: u2; value: u8 };
|
[UiCpuSignal.RequestMemoryChange]: { address: u8; bank: u2; value: u8 };
|
||||||
[UiCpuSignal.RequestRegisterChange]: { register_no: u3; value: u8 };
|
[UiCpuSignal.RequestRegisterChange]: { register_no: u3; value: u8 };
|
||||||
|
[UiCpuSignal.RequestProgramCounterChange]: { address: u8 };
|
||||||
|
[UiCpuSignal.RequestMemoryDump]: (memory: [Uint8Array, Uint8Array, Uint8Array, Uint8Array]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UiCpuSignalHandler extends EventHandler<UiCpuSignal> {
|
export interface UiCpuSignalHandler extends EventHandler<UiCpuSignal> {
|
||||||
|
|
|
@ -26,31 +26,40 @@
|
||||||
<button type="button" id="edit_button"></button>
|
<button type="button" id="edit_button"></button>
|
||||||
</div>
|
</div>
|
||||||
<span id="cycles"></span>
|
<span id="cycles"></span>
|
||||||
<div id="memory_bank_view">
|
<div id="memory_bank_view"></div>
|
||||||
<div id="bank_boxes">
|
|
||||||
<button class="nostyle">1</button>
|
<div id="reset_buttons"></div>
|
||||||
<button class="nostyle selected">2</button>
|
|
||||||
<button class="nostyle">3</button>
|
|
||||||
<button class="nostyle">4</button>
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
const d = document.getElementById("bank_boxes");
|
|
||||||
const a = [...d.children];
|
|
||||||
for (const b of a) {
|
|
||||||
b.addEventListener("click", () => {
|
|
||||||
a.forEach((ab) => ab.classList.remove("selected"));
|
|
||||||
b.classList.add("selected");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
console.log(a);
|
|
||||||
</script>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="window_box">
|
<div id="window_box">
|
||||||
<div id="instruction_explainer"></div>
|
<div id="instruction_explainer"></div>
|
||||||
<div id="printout"></div>
|
<div id="printout"></div>
|
||||||
<div id="tv"></div>
|
<div id="tv"></div>
|
||||||
|
<div id="bank_viz">
|
||||||
|
<div id="visualization">
|
||||||
|
<!-- TODO make these generated by software -->
|
||||||
|
<div id="cpu">CPU</div>
|
||||||
|
<svg id="cpu_bank" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 100">
|
||||||
|
<polyline points="0,43 7,43 7,12.5 20,12.5" stroke="yellow" />
|
||||||
|
<polyline points="0,47.5 13,47.5 13,37.5 20,37.5" stroke="gray" />
|
||||||
|
<polyline points="0,52.5 13,52.5 13,62.5 20,62.5" stroke="gray" />
|
||||||
|
<polyline points="0,57 7,57 7,87.5 20,87.5" stroke="gray" />
|
||||||
|
</svg>
|
||||||
|
<div id="targets">
|
||||||
|
<div id="target">BANK 0</div>
|
||||||
|
<div id="target" class="gray">BANK 1</div>
|
||||||
|
<div id="target" class="gray">BANK 2</div>
|
||||||
|
<div id="target" class="gray">BANK 3</div>
|
||||||
|
</div>
|
||||||
|
<svg id="vram_bank" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 100">
|
||||||
|
<polyline points="0,43 7,43 7,12.5 20,12.5" stroke="gray" />
|
||||||
|
<polyline points="0,47.5 13,47.5 13,37.5 20,37.5" stroke="gray" />
|
||||||
|
<polyline points="0,52.5 13,52.5 13,62.5 20,62.5" stroke="gray" />
|
||||||
|
<polyline points="0,57 7,57 7,87.5 20,87.5" stroke="yellow" />
|
||||||
|
</svg>
|
||||||
|
<div id="cpu">VRAM</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,7 @@ interface GenericComputer {
|
||||||
setBank: (bank_no: u2) => void;
|
setBank: (bank_no: u2) => void;
|
||||||
getCarry(): boolean;
|
getCarry(): boolean;
|
||||||
setCarry(state: boolean): void;
|
setCarry(state: boolean): void;
|
||||||
|
setVramBank(bank: u2): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AfterExecutionComputerAction {
|
interface AfterExecutionComputerAction {
|
||||||
|
@ -738,3 +739,15 @@ ISA.insertInstruction(0xf1, {
|
||||||
a.dispatch(CpuEvent.Print, byte.toString(10));
|
a.dispatch(CpuEvent.Print, byte.toString(10));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ISA.insertInstruction(0xff, {
|
||||||
|
name: "Set VRAM Bank",
|
||||||
|
desc: "Set memory bank which screen gets pixels from memory bank (P1)",
|
||||||
|
params: [new ConstParam("memory bank to select")],
|
||||||
|
execute(c, p, a) {
|
||||||
|
const bank_no = p[0];
|
||||||
|
if (!isU2(bank_no)) throw new Error("TO2O");
|
||||||
|
c.setVramBank(bank_no);
|
||||||
|
a.dispatch(CpuEvent.SetVramBank, { bank: bank_no });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
*/
|
*/
|
||||||
import { format_hex, in_range } from "./etc";
|
import { format_hex, in_range } from "./etc";
|
||||||
import { InstrCategory, Instruction, InstructionSet, ParameterType, ParamType } from "./instructionSet";
|
import { InstrCategory, Instruction, InstructionSet, ParameterType, ParamType } from "./instructionSet";
|
||||||
import { u8 } from "./num.js";
|
import { u8 } from "./num";
|
||||||
|
|
||||||
export function generate_isa(iset: InstructionSet): string {
|
export function generate_isa(iset: InstructionSet): string {
|
||||||
const instructions: Array<[u8, Instruction]> = [];
|
const instructions: Array<[u8, Instruction]> = [];
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
#memory {
|
#memory {
|
||||||
grid-template-columns: repeat(16, min-content);
|
.celled_viewer.selected {
|
||||||
|
grid-template-columns: repeat(16, min-content);
|
||||||
|
// display: block;
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
.celled_viewer {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
.program_counter {
|
.program_counter {
|
||||||
outline: 3px solid orange;
|
outline: 3px solid orange;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ body {
|
||||||
main {
|
main {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
#grid {
|
#grid {
|
||||||
|
@ -33,13 +34,12 @@ main {
|
||||||
grid-template-columns: min-content min-content min-content;
|
grid-template-columns: min-content min-content min-content;
|
||||||
grid-template-rows: min-content min-content min-content;
|
grid-template-rows: min-content min-content min-content;
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
". regmemlabel . cycles "
|
". regmemlabel . cycles"
|
||||||
". registers . bank "
|
". registers . bank "
|
||||||
|
"reset memory memory memory"
|
||||||
"title memory memory memory"
|
"title memory memory memory"
|
||||||
". buttons buttons buttons ";
|
". buttons buttons buttons";
|
||||||
#memory {
|
|
||||||
grid-area: memory;
|
|
||||||
}
|
|
||||||
#window_box {
|
#window_box {
|
||||||
grid-area: windowbox;
|
grid-area: windowbox;
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,25 @@ main {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
transform: scale(-1, -1);
|
transform: scale(-1, -1);
|
||||||
}
|
}
|
||||||
|
#memory {
|
||||||
|
grid-area: memory;
|
||||||
|
}
|
||||||
|
#reset_buttons {
|
||||||
|
grid-area: reset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#reset_buttons {
|
||||||
|
flex-direction: column;
|
||||||
|
display: flex;
|
||||||
|
align-items: end;
|
||||||
|
button {
|
||||||
|
margin-bottom: 7px;
|
||||||
|
margin-right: 7px;
|
||||||
|
border: 5px solid yellow;
|
||||||
|
padding: 5px;
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#labelcontainer {
|
#labelcontainer {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
height: 18px;
|
height: 18px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
border-bottom: 5px solid yellow;
|
border-bottom: 5px solid var(--border);
|
||||||
|
|
||||||
cursor: s-resize;
|
cursor: s-resize;
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,12 @@
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
width: 500px;
|
width: 500px;
|
||||||
|
.window:not(:has(#resize)) {
|
||||||
|
border-bottom: 5px solid var(--border);
|
||||||
|
&.collapsed {
|
||||||
|
border-bottom: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
.window {
|
.window {
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -30,16 +36,18 @@
|
||||||
font-size: 0.6em;
|
font-size: 0.6em;
|
||||||
color: lightgray;
|
color: lightgray;
|
||||||
border-bottom: 5px solid var(--border);
|
border-bottom: 5px solid var(--border);
|
||||||
background: repeating-linear-gradient(
|
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAHUlEQVQI12P4/5/h////DP8ZoICJAQ0wIstiVQEA3CAI/c69TucAAAAASUVORK5CYII=");
|
||||||
to top,
|
image-rendering: pixelated;
|
||||||
transparent,
|
|
||||||
transparent 2px,
|
|
||||||
transparent 2px,
|
|
||||||
yellow 2px,
|
|
||||||
yellow 4px
|
|
||||||
),
|
|
||||||
repeating-linear-gradient(to right, transparent, transparent 2px, transparent 2px, yellow 2px, yellow 4px);
|
|
||||||
|
|
||||||
|
// 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 {
|
#text {
|
||||||
word-break: keep-all;
|
word-break: keep-all;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
@ -56,9 +64,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.window.collapsed > :not(:first-child) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.window#tv {
|
.window#tv {
|
||||||
|
@ -69,6 +74,41 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#bank_viz {
|
||||||
|
#visualization {
|
||||||
|
user-select: none;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 80%;
|
||||||
|
// max-width: 500px;
|
||||||
|
#cpu,
|
||||||
|
#target {
|
||||||
|
border: 3px solid yellow;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
svg polyline {
|
||||||
|
stroke-linecap: butt;
|
||||||
|
stroke-width: 3;
|
||||||
|
fill: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 50px;
|
||||||
|
}
|
||||||
|
#vram_bank {
|
||||||
|
transform: scale(-1, 1);
|
||||||
|
}
|
||||||
|
#targets {
|
||||||
|
white-space: nowrap;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#instruction_explainer {
|
#instruction_explainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -86,6 +126,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#expl_icon {
|
#expl_icon {
|
||||||
|
user-select: none;
|
||||||
margin-inline-end: 0.5em;
|
margin-inline-end: 0.5em;
|
||||||
font-size: 30px;
|
font-size: 30px;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
|
|
30
src/ui.ts
30
src/ui.ts
|
@ -1,15 +1,20 @@
|
||||||
import { CpuEvent, CpuEventHandler, UiCpuSignalHandler, UiEvent, UiEventHandler } from "./events";
|
import { CpuEvent, CpuEventHandler, UiCpuSignalHandler, UiEventHandler } from "./events";
|
||||||
import { $ } from "./etc";
|
import { $ } from "./etc";
|
||||||
import { InstructionExplainer } from "./ui/windows/instructionExplainer";
|
import UiComponent, { UiComponentConstructor } from "./ui/uiComponent";
|
||||||
import { MemoryView } from "./ui/memoryView";
|
// Components
|
||||||
import { frequencyIndicator } from "./ui/frequencyIndicator";
|
import MemoryView from "./ui/components/memoryView";
|
||||||
import { RegisterView } from "./ui/registerView";
|
import frequencyIndicator from "./ui/components/frequencyIndicator";
|
||||||
import { Screen } from "./ui/windows/screen";
|
import RegisterView from "./ui/components/registerView";
|
||||||
import { EditButton } from "./ui/editButton";
|
import BankSelector from "./ui/components/bank_view_selector";
|
||||||
import { UiComponent, UiComponentConstructor } from "./ui/uiComponent";
|
import EditButton from "./ui/components/editButton";
|
||||||
import { pausePlay } from "./ui/pausePlay";
|
import pausePlay from "./ui/components/pausePlay";
|
||||||
import { Printout } from "./ui/windows/printout";
|
import SaveLoad from "./ui/components/saveLoad";
|
||||||
import { SaveLoad } from "./ui/saveLoad";
|
import ResetButtons from "./ui/components/reset_buttons";
|
||||||
|
// Window Components
|
||||||
|
import InstructionExplainer from "./ui/windows/instructionExplainer";
|
||||||
|
import Screen from "./ui/windows/screen";
|
||||||
|
import Printout from "./ui/windows/printout";
|
||||||
|
import BankVisualizer from "./ui/windows/bank_visualizer";
|
||||||
|
|
||||||
export class UI {
|
export class UI {
|
||||||
ui_events: UiEventHandler = new UiEventHandler();
|
ui_events: UiEventHandler = new UiEventHandler();
|
||||||
|
@ -26,6 +31,9 @@ export class UI {
|
||||||
this.register_component(EditButton, $("edit_button"));
|
this.register_component(EditButton, $("edit_button"));
|
||||||
this.register_component(pausePlay, $("controls_buttons"));
|
this.register_component(pausePlay, $("controls_buttons"));
|
||||||
this.register_component(SaveLoad, $("save_load_buttons"));
|
this.register_component(SaveLoad, $("save_load_buttons"));
|
||||||
|
this.register_component(BankSelector, $("memory_bank_view"));
|
||||||
|
this.register_component(BankVisualizer, $("bank_viz"));
|
||||||
|
this.register_component(ResetButtons, $("reset_buttons"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private register_component(ctor: UiComponentConstructor, e: HTMLElement): void {
|
private register_component(ctor: UiComponentConstructor, e: HTMLElement): void {
|
||||||
|
|
|
@ -5,28 +5,36 @@
|
||||||
*/
|
*/
|
||||||
import { NonEmptyArray, el, format_hex } from "../etc";
|
import { NonEmptyArray, el, format_hex } from "../etc";
|
||||||
import { u8 } from "../num";
|
import { u8 } from "../num";
|
||||||
|
import EditorContext from "./editableHex";
|
||||||
|
|
||||||
interface GenericCell {
|
interface GenericCell {
|
||||||
el: HTMLElement;
|
el: HTMLElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class CelledViewer {
|
export default class CelledViewer {
|
||||||
cells: Array<GenericCell> = [];
|
cells: Array<GenericCell> = [];
|
||||||
width: number;
|
readonly width: number;
|
||||||
height: number;
|
readonly height: number;
|
||||||
element: HTMLElement;
|
container: HTMLElement;
|
||||||
constructor(width: number, height: number, element: HTMLElement) {
|
editor: EditorContext;
|
||||||
this.element = element;
|
constructor(width: number, height: number, element: HTMLElement, edit_callback: (address: u8, value: u8) => void) {
|
||||||
|
this.container = element;
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
this.element.classList.add("celled_viewer");
|
|
||||||
|
this.container.classList.add("celled_viewer");
|
||||||
for (let i = 0; i < this.width * this.height; i++) {
|
for (let i = 0; i < this.width * this.height; i++) {
|
||||||
const mem_cell_el = el("div");
|
const mem_cell_el = el("div").fin();
|
||||||
mem_cell_el.append("0", "0");
|
mem_cell_el.append("0", "0");
|
||||||
this.element.appendChild(mem_cell_el);
|
this.container.appendChild(mem_cell_el);
|
||||||
const mem_cell = { el: mem_cell_el };
|
const mem_cell = { el: mem_cell_el };
|
||||||
this.cells.push(mem_cell);
|
this.cells.push(mem_cell);
|
||||||
}
|
}
|
||||||
|
const list = this.cells.map((c) => c.el);
|
||||||
|
|
||||||
|
this.editor = new EditorContext(list, this.width, this.height, (address, value) => {
|
||||||
|
edit_callback(address, value);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
reset(): void {
|
reset(): void {
|
||||||
|
@ -53,6 +61,12 @@ export abstract class CelledViewer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clear_all_classes(): void {
|
||||||
|
for (const cell of this.cells) {
|
||||||
|
cell.el.className = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
add_cell_class_exclusive(address: u8, css_class: string): void {
|
add_cell_class_exclusive(address: u8, css_class: string): void {
|
||||||
this.remove_all_cell_class(css_class);
|
this.remove_all_cell_class(css_class);
|
||||||
this.add_cell_class(address, css_class);
|
this.add_cell_class(address, css_class);
|
||||||
|
|
35
src/ui/components/bank_view_selector.ts
Normal file
35
src/ui/components/bank_view_selector.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import { el } from "../../etc";
|
||||||
|
import { u2 } from "../../num";
|
||||||
|
import { UiEventHandler, UiEvent } from "../../events";
|
||||||
|
import UiComponent from "../uiComponent";
|
||||||
|
|
||||||
|
export default class BankSelector implements UiComponent {
|
||||||
|
container: HTMLElement;
|
||||||
|
events: UiEventHandler;
|
||||||
|
private bank_buttons: Array<HTMLButtonElement>;
|
||||||
|
constructor(element: HTMLElement, events: UiEventHandler) {
|
||||||
|
this.container = element;
|
||||||
|
this.events = events;
|
||||||
|
|
||||||
|
const bank_boxes = el("div").id("bank_boxes").fin();
|
||||||
|
this.bank_buttons = [];
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
const button = el("button").cl("nostyle").fin();
|
||||||
|
bank_boxes.appendChild(button);
|
||||||
|
button.addEventListener("click", () => {
|
||||||
|
for (const b of this.bank_buttons) b.classList.remove("selected");
|
||||||
|
button.classList.add("selected");
|
||||||
|
this.events.dispatch(UiEvent.ChangeViewBank, { bank: i as u2 });
|
||||||
|
});
|
||||||
|
button.textContent = i.toString();
|
||||||
|
this.bank_buttons.push(button);
|
||||||
|
}
|
||||||
|
this.bank_buttons[0].classList.add("selected");
|
||||||
|
|
||||||
|
this.container.appendChild(bank_boxes);
|
||||||
|
}
|
||||||
|
reset(): void {
|
||||||
|
for (const b of this.bank_buttons) b.classList.remove("selected");
|
||||||
|
this.bank_buttons[0].classList.add("selected");
|
||||||
|
}
|
||||||
|
}
|
41
src/ui/components/editButton.ts
Normal file
41
src/ui/components/editButton.ts
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import { el, $ } from "../../etc";
|
||||||
|
import { UiEventHandler, UiEvent, UiCpuSignalHandler, UiCpuSignal } from "../../events";
|
||||||
|
import UiComponent from "../uiComponent";
|
||||||
|
|
||||||
|
export default class EditButton implements UiComponent {
|
||||||
|
container: HTMLElement;
|
||||||
|
events: UiEventHandler;
|
||||||
|
cpu_signals: UiCpuSignalHandler;
|
||||||
|
constructor(element: HTMLElement, event: UiEventHandler, cpu_signals: UiCpuSignalHandler) {
|
||||||
|
this.container = element;
|
||||||
|
this.events = event;
|
||||||
|
this.cpu_signals = cpu_signals;
|
||||||
|
const image = el("img").at("src", "pencil.png").st("width", "20px").st("height", "20px").fin();
|
||||||
|
this.container.classList.add("editor_toggle");
|
||||||
|
this.container.addEventListener("click", () => this.edit_toggle());
|
||||||
|
this.container.appendChild(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(): void {
|
||||||
|
const is_on = this.container.classList.contains("on");
|
||||||
|
if (is_on) {
|
||||||
|
this.edit_toggle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
edit_toggle(): void {
|
||||||
|
const is_on = this.container.classList.contains("on");
|
||||||
|
if (is_on) {
|
||||||
|
this.container.classList.remove("on");
|
||||||
|
$("root").classList.remove("editor");
|
||||||
|
this.container.classList.add("off");
|
||||||
|
this.events.dispatch(UiEvent.EditOff);
|
||||||
|
} else {
|
||||||
|
this.events.dispatch(UiEvent.EditOn);
|
||||||
|
$("root").classList.add("editor");
|
||||||
|
this.container.classList.add("on");
|
||||||
|
this.container.classList.remove("off");
|
||||||
|
this.cpu_signals.dispatch(UiCpuSignal.RequestProgramCounterChange, { address: 0 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +1,15 @@
|
||||||
import { CpuEvent, CpuEventHandler, UiEventHandler } from "../events";
|
import { CpuEvent, CpuEventHandler, UiEventHandler } from "../../events";
|
||||||
import { UiComponent } from "./uiComponent";
|
import UiComponent from "../uiComponent";
|
||||||
|
|
||||||
export class frequencyIndicator implements UiComponent {
|
export default class frequencyIndicator implements UiComponent {
|
||||||
element: HTMLElement;
|
container: HTMLElement;
|
||||||
private running: number | null = null;
|
private running: number | null = null;
|
||||||
private count: number = 0;
|
private count: number = 0;
|
||||||
private last_value: number = 0;
|
private last_value: number = 0;
|
||||||
private last_time: number = 0;
|
private last_time: number = 0;
|
||||||
events: UiEventHandler;
|
events: UiEventHandler;
|
||||||
constructor(element: HTMLElement, events: UiEventHandler) {
|
constructor(element: HTMLElement, events: UiEventHandler) {
|
||||||
this.element = element;
|
this.container = element;
|
||||||
this.events = events;
|
this.events = events;
|
||||||
this.start();
|
this.start();
|
||||||
}
|
}
|
||||||
|
@ -34,11 +34,11 @@ export class frequencyIndicator implements UiComponent {
|
||||||
const value = Math.round(this.count / dt);
|
const value = Math.round(this.count / dt);
|
||||||
|
|
||||||
if (this.last_value !== value) {
|
if (this.last_value !== value) {
|
||||||
this.element.textContent = `${value}hz`;
|
this.container.textContent = `${value}hz`;
|
||||||
this.last_value = value;
|
this.last_value = value;
|
||||||
}
|
}
|
||||||
if (value === 0) {
|
if (value === 0) {
|
||||||
this.element.textContent = "";
|
this.container.textContent = "";
|
||||||
}
|
}
|
||||||
this.last_time = new_time;
|
this.last_time = new_time;
|
||||||
this.count = 0;
|
this.count = 0;
|
||||||
|
@ -51,6 +51,7 @@ export class frequencyIndicator implements UiComponent {
|
||||||
this.stop();
|
this.stop();
|
||||||
this.count = 0;
|
this.count = 0;
|
||||||
this.last_value = 0;
|
this.last_value = 0;
|
||||||
|
this.start();
|
||||||
}
|
}
|
||||||
init_cpu_events(c: CpuEventHandler): void {
|
init_cpu_events(c: CpuEventHandler): void {
|
||||||
c.listen(CpuEvent.Cycle, () => {
|
c.listen(CpuEvent.Cycle, () => {
|
126
src/ui/components/memoryView.ts
Normal file
126
src/ui/components/memoryView.ts
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
import { CpuEvent, CpuEventHandler, UiCpuSignal, UiCpuSignalHandler, UiEvent, UiEventHandler } from "../../events";
|
||||||
|
import { ParamType } from "../../instructionSet";
|
||||||
|
import { u2, u8 } from "../../num";
|
||||||
|
import UiComponent from "../uiComponent";
|
||||||
|
import { el } from "../../etc";
|
||||||
|
import CelledViewer from "../celledViewer";
|
||||||
|
|
||||||
|
/** Only to be run once */
|
||||||
|
function create_banks(
|
||||||
|
element: HTMLElement,
|
||||||
|
edit_callback: (address: u8, bank: u2, value: u8) => void
|
||||||
|
): [CelledViewer, CelledViewer, CelledViewer, CelledViewer] {
|
||||||
|
const list: Array<CelledViewer> = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
const child = el("div").fin();
|
||||||
|
list.push(
|
||||||
|
new CelledViewer(16, 16, child, (address: u8, value: u8) => {
|
||||||
|
edit_callback(address, i as u2, value);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
element.appendChild(child);
|
||||||
|
}
|
||||||
|
list[0].container.classList.add("selected");
|
||||||
|
|
||||||
|
return list as [CelledViewer, CelledViewer, CelledViewer, CelledViewer];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class MemoryView implements UiComponent {
|
||||||
|
container: HTMLElement;
|
||||||
|
program_counter: u8 = 0;
|
||||||
|
last_accessed_cell: { address: u8; bank: u2 } | null = null;
|
||||||
|
events: UiEventHandler;
|
||||||
|
cpu_signals: UiCpuSignalHandler;
|
||||||
|
banks: [CelledViewer, CelledViewer, CelledViewer, CelledViewer];
|
||||||
|
constructor(element: HTMLElement, events: UiEventHandler, cpu_signals: UiCpuSignalHandler) {
|
||||||
|
this.container = element;
|
||||||
|
this.events = events;
|
||||||
|
this.cpu_signals = cpu_signals;
|
||||||
|
|
||||||
|
this.banks = create_banks(element, (address, bank, value) => {
|
||||||
|
cpu_signals.dispatch(UiCpuSignal.RequestMemoryChange, { address, bank, value });
|
||||||
|
});
|
||||||
|
for (const bank of this.banks) {
|
||||||
|
this.events.listen(UiEvent.EditOn, () => {
|
||||||
|
bank.editor.enable();
|
||||||
|
bank.clear_all_classes();
|
||||||
|
});
|
||||||
|
this.events.listen(UiEvent.EditOff, () => {
|
||||||
|
bank.editor.disable();
|
||||||
|
bank.clear_all_classes();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.events.listen(UiEvent.ChangeViewBank, ({ bank }) => this.set_bank(bank));
|
||||||
|
}
|
||||||
|
|
||||||
|
get program(): CelledViewer {
|
||||||
|
return this.banks[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
set_bank(bank: u2): void {
|
||||||
|
for (const bank of this.banks) bank.container.classList.remove("selected");
|
||||||
|
this.banks[bank].container.classList.add("selected");
|
||||||
|
}
|
||||||
|
|
||||||
|
set_program_counter(position: u8): void {
|
||||||
|
this.program.remove_cell_class(this.program_counter, "program_counter");
|
||||||
|
this.program.add_cell_class(position, "program_counter");
|
||||||
|
this.program_counter = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(): void {
|
||||||
|
for (const viewer of this.banks) viewer.reset();
|
||||||
|
this.last_accessed_cell = null;
|
||||||
|
this.set_program_counter(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
init_cpu_events(c: CpuEventHandler): void {
|
||||||
|
c.listen(CpuEvent.MemoryAccessed, ({ address, bank, value }) => {
|
||||||
|
if (this.last_accessed_cell?.address !== address || this.last_accessed_cell?.bank !== bank) {
|
||||||
|
if (this.last_accessed_cell !== null) {
|
||||||
|
this.banks[this.last_accessed_cell.bank].remove_cell_class(this.last_accessed_cell.address, "last_access");
|
||||||
|
}
|
||||||
|
this.banks[bank].add_cell_class(address, "last_access");
|
||||||
|
this.last_accessed_cell = { address, bank };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
c.listen(CpuEvent.MemoryChanged, ({ address, bank, value }) => {
|
||||||
|
if (bank !== 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.banks[bank].set_cell_value(address, value);
|
||||||
|
});
|
||||||
|
c.listen(CpuEvent.ProgramCounterChanged, ({ counter }) => {
|
||||||
|
this.set_program_counter(counter);
|
||||||
|
});
|
||||||
|
c.listen(CpuEvent.ParameterParsed, ({ param, code, pos }) => {
|
||||||
|
this.program.add_cell_class(pos, "instruction_argument");
|
||||||
|
const t = param.type;
|
||||||
|
this.program.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");
|
||||||
|
}
|
||||||
|
this.program.add_cell_class(pos, name);
|
||||||
|
});
|
||||||
|
c.listen(CpuEvent.InstructionParsed, ({ instr, code, pos }) => {
|
||||||
|
this.program.remove_all_cell_class("instruction_argument");
|
||||||
|
this.program.remove_all_cell_class("current_instruction");
|
||||||
|
this.program.remove_cell_class(pos, "constant", "register", "memory", "invalid");
|
||||||
|
this.program.add_cell_class(pos, "current_instruction");
|
||||||
|
this.program.add_cell_class(pos, "instruction");
|
||||||
|
});
|
||||||
|
c.listen(CpuEvent.InvalidParsed, ({ code, pos }) => {
|
||||||
|
this.program.remove_cell_class(pos, "constant", "register", "memory", "instruction");
|
||||||
|
this.program.add_cell_class(pos, "invalid");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,11 @@
|
||||||
import { el } from "../etc";
|
import { el } from "../../etc";
|
||||||
import { UiEventHandler, UiEvent, CpuEventHandler, UiCpuSignalHandler, UiCpuSignal } from "../events";
|
import { UiEventHandler, UiEvent, CpuEventHandler, UiCpuSignalHandler, UiCpuSignal } from "../../events";
|
||||||
import { UiComponent } from "./uiComponent";
|
import UiComponent from "../uiComponent";
|
||||||
|
|
||||||
const MAX_SLIDER = 1000;
|
const MAX_SLIDER = 1000;
|
||||||
|
|
||||||
export class pausePlay implements UiComponent {
|
export default class pausePlay implements UiComponent {
|
||||||
element: HTMLElement;
|
container: HTMLElement;
|
||||||
start_button: HTMLButtonElement;
|
start_button: HTMLButtonElement;
|
||||||
step_button: HTMLButtonElement;
|
step_button: HTMLButtonElement;
|
||||||
range: HTMLInputElement;
|
range: HTMLInputElement;
|
||||||
|
@ -15,35 +15,32 @@ export class pausePlay implements UiComponent {
|
||||||
cpu_signals: UiCpuSignalHandler;
|
cpu_signals: UiCpuSignalHandler;
|
||||||
|
|
||||||
constructor(element: HTMLElement, events: UiEventHandler, cpu_signals: UiCpuSignalHandler) {
|
constructor(element: HTMLElement, events: UiEventHandler, cpu_signals: UiCpuSignalHandler) {
|
||||||
this.element = element;
|
this.container = element;
|
||||||
this.events = events;
|
this.events = events;
|
||||||
this.cpu_signals = cpu_signals;
|
this.cpu_signals = cpu_signals;
|
||||||
this.start_button = el("button", "pause_play_button");
|
this.start_button = el("button").id("pause_play_button").tx("Start").fin();
|
||||||
this.step_button = el("button", "step_button");
|
this.step_button = el("button").id("step_button").tx("Step").fin();
|
||||||
this.range = el("input", "speed_range");
|
this.range = el("input")
|
||||||
this.range.max = MAX_SLIDER.toString();
|
.id("speed_range")
|
||||||
this.range.min = "0";
|
.at("type", "range")
|
||||||
this.range.type = "range";
|
.at("min", "0")
|
||||||
|
.at("max", MAX_SLIDER.toString())
|
||||||
|
.at("value", "0")
|
||||||
|
.fin();
|
||||||
this.start_button.addEventListener("click", () => this.toggle());
|
this.start_button.addEventListener("click", () => this.toggle());
|
||||||
this.step_button.addEventListener("click", () => this.step());
|
this.step_button.addEventListener("click", () => this.step());
|
||||||
this.range.addEventListener("input", (e) => {
|
this.range.addEventListener("input", (e) => {
|
||||||
const delay = MAX_SLIDER - parseInt((e.target as HTMLInputElement).value, 10) + 10;
|
const delay = MAX_SLIDER - parseInt((e.target as HTMLInputElement).value, 10) + 10;
|
||||||
this.cycle_delay = delay;
|
this.cycle_delay = delay;
|
||||||
});
|
});
|
||||||
this.start_button.textContent = "Start";
|
|
||||||
this.step_button.textContent = "Step";
|
|
||||||
this.element.appendChild(this.start_button);
|
|
||||||
this.element.appendChild(this.step_button);
|
|
||||||
this.element.appendChild(this.range);
|
|
||||||
this.cycle_delay = 1000;
|
|
||||||
this.range.value = "0";
|
|
||||||
|
|
||||||
this.events.listen(UiEvent.EditOn, () => {
|
this.container.appendChild(this.start_button);
|
||||||
this.disable();
|
this.container.appendChild(this.step_button);
|
||||||
});
|
this.container.appendChild(this.range);
|
||||||
this.events.listen(UiEvent.EditOff, () => {
|
this.cycle_delay = 1000;
|
||||||
this.enable();
|
|
||||||
});
|
this.events.listen(UiEvent.EditOn, () => this.disable());
|
||||||
|
this.events.listen(UiEvent.EditOff, () => this.enable());
|
||||||
}
|
}
|
||||||
|
|
||||||
disable(): void {
|
disable(): void {
|
||||||
|
@ -72,9 +69,8 @@ export class pausePlay implements UiComponent {
|
||||||
|
|
||||||
private cycle(): void {
|
private cycle(): void {
|
||||||
const loop = (): void => {
|
const loop = (): void => {
|
||||||
if (this.on === false) {
|
if (this.on === false) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.cpu_signals.dispatch(UiCpuSignal.RequestCpuCycle, 1);
|
this.cpu_signals.dispatch(UiCpuSignal.RequestCpuCycle, 1);
|
||||||
setTimeout(loop, this.cycle_delay);
|
setTimeout(loop, this.cycle_delay);
|
||||||
};
|
};
|
||||||
|
@ -89,15 +85,13 @@ export class pausePlay implements UiComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
start(): void {
|
start(): void {
|
||||||
if (!this.on) {
|
if (this.on) return;
|
||||||
this.toggle();
|
this.toggle();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stop(): void {
|
stop(): void {
|
||||||
if (this.on) {
|
if (!this.on) return;
|
||||||
this.toggle();
|
this.toggle();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reset(): void {
|
reset(): void {
|
31
src/ui/components/registerView.ts
Normal file
31
src/ui/components/registerView.ts
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { CpuEvent, CpuEventHandler, UiCpuSignal, UiCpuSignalHandler, UiEvent, UiEventHandler } from "../../events";
|
||||||
|
import { isU3, u3, u8 } from "../../num";
|
||||||
|
import CelledViewer from "../celledViewer";
|
||||||
|
import UiComponent from "../uiComponent";
|
||||||
|
|
||||||
|
export default class RegisterView extends CelledViewer implements UiComponent {
|
||||||
|
events: UiEventHandler;
|
||||||
|
cpu_signals: UiCpuSignalHandler;
|
||||||
|
constructor(element: HTMLElement, events: UiEventHandler, cpu_signals: UiCpuSignalHandler) {
|
||||||
|
super(8, 1, element, (address: u8, value: u8) => {
|
||||||
|
if (!isU3(address)) throw new Error("unreachable");
|
||||||
|
this.cpu_signals.dispatch(UiCpuSignal.RequestRegisterChange, { register_no: address as u3, value });
|
||||||
|
});
|
||||||
|
this.events = events;
|
||||||
|
this.cpu_signals = cpu_signals;
|
||||||
|
|
||||||
|
this.events.listen(UiEvent.EditOn, () => {
|
||||||
|
this.editor.enable();
|
||||||
|
for (const cell of this.cells) cell.el.className = "";
|
||||||
|
});
|
||||||
|
this.events.listen(UiEvent.EditOff, () => {
|
||||||
|
this.editor.disable();
|
||||||
|
for (const cell of this.cells) cell.el.className = "";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
init_cpu_events(c: CpuEventHandler): void {
|
||||||
|
c.listen(CpuEvent.RegisterChanged, ({ register_no, value }) => this.set_cell_value(register_no, value));
|
||||||
|
c.listen(CpuEvent.Reset, () => this.reset());
|
||||||
|
}
|
||||||
|
}
|
26
src/ui/components/reset_buttons.ts
Normal file
26
src/ui/components/reset_buttons.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { el } from "../../etc";
|
||||||
|
import { UiEventHandler, UiCpuSignalHandler, UiEvent, UiCpuSignal } from "../../events";
|
||||||
|
import UiComponent from "../uiComponent";
|
||||||
|
|
||||||
|
export default class ResetButtons implements UiComponent {
|
||||||
|
container: HTMLElement;
|
||||||
|
events: UiEventHandler;
|
||||||
|
cpu_signals: UiCpuSignalHandler;
|
||||||
|
constructor(element: HTMLElement, events: UiEventHandler, cpu_signals: UiCpuSignalHandler) {
|
||||||
|
this.container = element;
|
||||||
|
this.events = events;
|
||||||
|
this.cpu_signals = cpu_signals;
|
||||||
|
const reset_button = el("button").cl("nostyle").tx("R").fin();
|
||||||
|
const trash_button = el("button").cl("nostyle").tx("T").fin();
|
||||||
|
|
||||||
|
reset_button.addEventListener("click", () => this.reset_clicked());
|
||||||
|
trash_button.addEventListener("click", () => this.trash_clicked());
|
||||||
|
this.container.append(reset_button, trash_button);
|
||||||
|
}
|
||||||
|
|
||||||
|
reset_clicked(): void {}
|
||||||
|
|
||||||
|
trash_clicked(): void {
|
||||||
|
this.cpu_signals.dispatch(UiCpuSignal.RequestCpuReset);
|
||||||
|
}
|
||||||
|
}
|
90
src/ui/components/saveLoad.ts
Normal file
90
src/ui/components/saveLoad.ts
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
import { el } from "../../etc";
|
||||||
|
import { UiEventHandler, UiCpuSignalHandler, UiCpuSignal } from "../../events";
|
||||||
|
import { u2, u8, m256, isU2 } from "../../num";
|
||||||
|
import UiComponent from "../uiComponent";
|
||||||
|
|
||||||
|
export default class SaveLoad implements UiComponent {
|
||||||
|
container: HTMLElement;
|
||||||
|
events: UiEventHandler;
|
||||||
|
save_button: HTMLButtonElement;
|
||||||
|
binary_upload: HTMLInputElement;
|
||||||
|
cpu_signals: UiCpuSignalHandler;
|
||||||
|
constructor(element: HTMLElement, events: UiEventHandler, cpu_signals: UiCpuSignalHandler) {
|
||||||
|
this.container = element;
|
||||||
|
this.events = events;
|
||||||
|
this.cpu_signals = cpu_signals;
|
||||||
|
|
||||||
|
this.save_button = el("button").id("save_button").tx("Save").fin();
|
||||||
|
this.binary_upload = el("input")
|
||||||
|
.id("binary_upload")
|
||||||
|
.at("type", "file")
|
||||||
|
.at("name", "binary_upload")
|
||||||
|
.st("display", "none")
|
||||||
|
.fin();
|
||||||
|
const label = el("label").cl("button").at("for", "binary_upload").tx("Load Binary").fin();
|
||||||
|
|
||||||
|
this.container.append(this.binary_upload, label, this.save_button);
|
||||||
|
|
||||||
|
this.save_button.addEventListener("click", () => {
|
||||||
|
this.download();
|
||||||
|
});
|
||||||
|
this.binary_upload.addEventListener("change", (e) => {
|
||||||
|
this.upload_changed(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private download(): void {
|
||||||
|
this.cpu_signals.dispatch(UiCpuSignal.RequestMemoryDump, (memory) => {
|
||||||
|
const flattened = new Uint8Array(256 * memory.length);
|
||||||
|
for (let x = 0; x < 4; x++) {
|
||||||
|
for (let y = 0; y < 256; x++) {
|
||||||
|
flattened[256 * x + y] = memory[x][y];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const blob = new Blob([flattened], { type: "application/octet-stream" });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = url;
|
||||||
|
link.download = "bin.bin";
|
||||||
|
link.click();
|
||||||
|
link.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private upload_changed(e: Event): void {
|
||||||
|
const t = e.target;
|
||||||
|
if (t === null) return;
|
||||||
|
|
||||||
|
const file = (t as HTMLInputElement).files?.[0];
|
||||||
|
if (file === undefined) {
|
||||||
|
console.error("No files attribute on file input");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.addEventListener("load", (e) => {
|
||||||
|
const target = e.target;
|
||||||
|
if (target === null) return;
|
||||||
|
const data = target.result;
|
||||||
|
if (!(data instanceof ArrayBuffer)) {
|
||||||
|
console.error("Data is not arraybuffer");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const view = new Uint8Array(data);
|
||||||
|
const array = [...view] as Array<u8>;
|
||||||
|
this.cpu_signals.dispatch(UiCpuSignal.RequestCpuReset);
|
||||||
|
for (const [i, v] of array.entries()) {
|
||||||
|
const address = m256(i);
|
||||||
|
const bank = Math.floor(i / 256);
|
||||||
|
if (!isU2(bank)) {
|
||||||
|
// throw new Error("Too many banks in data file");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.cpu_signals.dispatch(UiCpuSignal.RequestMemoryChange, { address, bank, value: v });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,41 +0,0 @@
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +1,23 @@
|
||||||
// This file was cobbled together and is the messiest part of this project
|
// This file was cobbled together and is the messiest part of this project
|
||||||
|
|
||||||
import { at } from "../etc";
|
import { at } from "../etc";
|
||||||
import { u8 } from "../num";
|
import { isU8, u8 } from "../num";
|
||||||
|
|
||||||
const HEX_CHARACTERS = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"];
|
const HEX_CHARACTERS = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"];
|
||||||
|
|
||||||
export class EditorContext {
|
export default class EditorContext {
|
||||||
private list: Array<HTMLElement>;
|
private list: Array<HTMLElement>;
|
||||||
private width: number;
|
private width: number;
|
||||||
|
private height: number;
|
||||||
private enabled: boolean = false;
|
private enabled: boolean = false;
|
||||||
private current_cell_info: { left?: string; right?: string; old?: string };
|
private current_cell_info: { left?: string; right?: string; old?: string };
|
||||||
private edit_callback: (n: number, value: u8) => void;
|
private edit_callback: (n: u8, value: u8) => void;
|
||||||
constructor(list: Array<HTMLElement>, width: number, callback: (n: number, value: u8) => void) {
|
constructor(list: Array<HTMLElement>, width: number, height: number, callback: (n: u8, value: u8) => void) {
|
||||||
|
if (!isU8(width * height - 1)) {
|
||||||
|
throw new RangeError("Grid is too big for editor. Maximum area is 256");
|
||||||
|
}
|
||||||
this.list = list;
|
this.list = list;
|
||||||
|
this.height = height;
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.edit_callback = callback;
|
this.edit_callback = callback;
|
||||||
this.current_cell_info = {};
|
this.current_cell_info = {};
|
||||||
|
@ -35,7 +40,7 @@ export class EditorContext {
|
||||||
this.current_cell_info.right = undefined;
|
this.current_cell_info.right = undefined;
|
||||||
cell.classList.add("caret_selected");
|
cell.classList.add("caret_selected");
|
||||||
|
|
||||||
// Reset cursor position (I know there's an API for this, but this is a simpler, more robust solution)
|
// Reset cursor position (there's an API for this, but this is a simpler, more robust solution)
|
||||||
cell.textContent = cell.textContent ?? "00";
|
cell.textContent = cell.textContent ?? "00";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -49,7 +54,7 @@ export class EditorContext {
|
||||||
const text = `${left}${right}`;
|
const text = `${left}${right}`;
|
||||||
cell.textContent = text;
|
cell.textContent = text;
|
||||||
const val = Number.parseInt(text, 16) as u8;
|
const val = Number.parseInt(text, 16) as u8;
|
||||||
this.edit_callback(i, val);
|
this.edit_callback(i as u8, val);
|
||||||
cell.classList.add("recent_edit");
|
cell.classList.add("recent_edit");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,97 +0,0 @@
|
||||||
import { CpuEvent, CpuEventHandler, UiCpuSignal, UiCpuSignalHandler, UiEvent, UiEventHandler } from "../events";
|
|
||||||
import { ParamType } from "../instructionSet";
|
|
||||||
import { u8 } from "../num.js";
|
|
||||||
import { UiComponent } from "./uiComponent";
|
|
||||||
import { CelledViewer } from "./celledViewer";
|
|
||||||
import { EditorContext } from "./editableHex";
|
|
||||||
|
|
||||||
export class MemoryView extends CelledViewer implements UiComponent {
|
|
||||||
program_counter: u8 = 0;
|
|
||||||
last_accessed_cell: u8 | null = null;
|
|
||||||
events: UiEventHandler;
|
|
||||||
cpu_signals: UiCpuSignalHandler;
|
|
||||||
constructor(element: HTMLElement, events: UiEventHandler, cpu_signals: UiCpuSignalHandler) {
|
|
||||||
super(16, 16, element);
|
|
||||||
this.program_counter = 0;
|
|
||||||
this.events = events;
|
|
||||||
this.cpu_signals = cpu_signals;
|
|
||||||
|
|
||||||
const list = this.cells.map((c) => c.el);
|
|
||||||
const editor = new EditorContext(list, this.width, (i, value) => {
|
|
||||||
this.cpu_signals.dispatch(UiCpuSignal.RequestMemoryChange, { address: i as u8, bank: 0, value });
|
|
||||||
});
|
|
||||||
this.events.listen(UiEvent.EditOn, () => {
|
|
||||||
editor.enable();
|
|
||||||
for (const cell of this.cells) {
|
|
||||||
cell.el.className = "";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.events.listen(UiEvent.EditOff, () => {
|
|
||||||
editor.disable();
|
|
||||||
for (const cell of this.cells) {
|
|
||||||
cell.el.className = "";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
set_program_counter(position: u8): void {
|
|
||||||
this.remove_cell_class(this.program_counter, "program_counter");
|
|
||||||
this.add_cell_class(position, "program_counter");
|
|
||||||
this.program_counter = position;
|
|
||||||
}
|
|
||||||
|
|
||||||
reset(): void {
|
|
||||||
super.reset();
|
|
||||||
this.last_accessed_cell = null;
|
|
||||||
this.set_program_counter(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
init_cpu_events(c: CpuEventHandler): void {
|
|
||||||
c.listen(CpuEvent.MemoryAccessed, ({ address, bank, value }) => {
|
|
||||||
if (bank !== 0) return;
|
|
||||||
if (this.last_accessed_cell !== address) {
|
|
||||||
if (this.last_accessed_cell !== null) {
|
|
||||||
this.remove_cell_class(this.last_accessed_cell, "last_access");
|
|
||||||
}
|
|
||||||
this.add_cell_class(address, "last_access");
|
|
||||||
this.last_accessed_cell = address;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
c.listen(CpuEvent.MemoryChanged, ({ address, bank, value }) => {
|
|
||||||
if (bank !== 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.set_cell_value(address, value);
|
|
||||||
});
|
|
||||||
c.listen(CpuEvent.ProgramCounterChanged, ({ counter }) => {
|
|
||||||
this.set_program_counter(counter);
|
|
||||||
});
|
|
||||||
c.listen(CpuEvent.ParameterParsed, ({ param, code, pos }) => {
|
|
||||||
this.add_cell_class(pos, "instruction_argument");
|
|
||||||
const t = param.type;
|
|
||||||
this.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");
|
|
||||||
}
|
|
||||||
this.add_cell_class(pos, name);
|
|
||||||
});
|
|
||||||
c.listen(CpuEvent.InstructionParsed, ({ instr, code, pos }) => {
|
|
||||||
this.remove_all_cell_class("instruction_argument");
|
|
||||||
this.remove_all_cell_class("current_instruction");
|
|
||||||
this.remove_cell_class(pos, "constant", "register", "memory", "invalid");
|
|
||||||
this.add_cell_class(pos, "current_instruction");
|
|
||||||
this.add_cell_class(pos, "instruction");
|
|
||||||
});
|
|
||||||
c.listen(CpuEvent.InvalidParsed, ({ code, pos }) => {
|
|
||||||
this.remove_cell_class(pos, "constant", "register", "memory", "instruction");
|
|
||||||
this.add_cell_class(pos, "invalid");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
import { CpuEvent, CpuEventHandler, UiCpuSignal, UiCpuSignalHandler, UiEvent, UiEventHandler } from "../events";
|
|
||||||
import { u3 } from "../num";
|
|
||||||
import { CelledViewer } from "./celledViewer";
|
|
||||||
import { EditorContext } from "./editableHex";
|
|
||||||
import { UiComponent } from "./uiComponent";
|
|
||||||
|
|
||||||
export class RegisterView extends CelledViewer implements UiComponent {
|
|
||||||
events: UiEventHandler;
|
|
||||||
cpu_signals: UiCpuSignalHandler;
|
|
||||||
constructor(element: HTMLElement, events: UiEventHandler, cpu_signals: UiCpuSignalHandler) {
|
|
||||||
super(8, 1, element);
|
|
||||||
this.events = events;
|
|
||||||
this.cpu_signals = cpu_signals;
|
|
||||||
|
|
||||||
const list = this.cells.map((c) => c.el);
|
|
||||||
const editor = new EditorContext(list, this.width, (i, value) => {
|
|
||||||
this.cpu_signals.dispatch(UiCpuSignal.RequestRegisterChange, { register_no: i as u3, value });
|
|
||||||
});
|
|
||||||
this.events.listen(UiEvent.EditOn, () => {
|
|
||||||
editor.enable();
|
|
||||||
for (const cell of this.cells) {
|
|
||||||
cell.el.className = "";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.events.listen(UiEvent.EditOff, () => {
|
|
||||||
editor.disable();
|
|
||||||
for (const cell of this.cells) {
|
|
||||||
cell.el.className = "";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
init_cpu_events(c: CpuEventHandler): void {
|
|
||||||
c.listen(CpuEvent.RegisterChanged, ({ register_no, value }) => {
|
|
||||||
this.set_cell_value(register_no, value);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,91 +0,0 @@
|
||||||
import { el } from "../etc";
|
|
||||||
import { UiEventHandler, CpuEventHandler, CpuEvent, UiCpuSignalHandler, UiCpuSignal } from "../events";
|
|
||||||
import { u2, u8, m256 } from "../num";
|
|
||||||
import { UiComponent } from "./uiComponent";
|
|
||||||
|
|
||||||
export class SaveLoad implements UiComponent {
|
|
||||||
element: HTMLElement;
|
|
||||||
events: UiEventHandler;
|
|
||||||
save_button: HTMLButtonElement;
|
|
||||||
binary_upload: HTMLInputElement;
|
|
||||||
cpu_signals: UiCpuSignalHandler;
|
|
||||||
|
|
||||||
constructor(element: HTMLElement, events: UiEventHandler, cpu_signals: UiCpuSignalHandler) {
|
|
||||||
this.element = element;
|
|
||||||
this.events = events;
|
|
||||||
this.cpu_signals = cpu_signals;
|
|
||||||
this.save_button = el("button", "save_button");
|
|
||||||
this.binary_upload = el("input", "binary_upload");
|
|
||||||
this.binary_upload.type = "file";
|
|
||||||
this.binary_upload.name = "binary_upload";
|
|
||||||
this.binary_upload.style.display = "none";
|
|
||||||
const label = el("label");
|
|
||||||
this.save_button.textContent = "Save";
|
|
||||||
label.textContent = "Load Binary";
|
|
||||||
label.classList.add("button");
|
|
||||||
label.setAttribute("for", "binary_upload");
|
|
||||||
|
|
||||||
this.element.appendChild(this.binary_upload);
|
|
||||||
this.element.appendChild(label);
|
|
||||||
this.element.appendChild(this.save_button);
|
|
||||||
this.save_button.addEventListener("click", () => {
|
|
||||||
this.cpu_signals.dispatch(UiCpuSignal.RequestMemoryDump);
|
|
||||||
});
|
|
||||||
this.binary_upload.addEventListener("change", (e) => {
|
|
||||||
this.upload_changed(e);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private upload_changed(e: Event): void {
|
|
||||||
const t = e.target;
|
|
||||||
if (t === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const file: File | undefined = (t as HTMLInputElement).files?.[0];
|
|
||||||
if (file === undefined) {
|
|
||||||
console.log("No files attribute on file input");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const reader = new FileReader();
|
|
||||||
console.log(file);
|
|
||||||
reader.addEventListener("load", (e) => {
|
|
||||||
if (e.target !== null) {
|
|
||||||
const data = e.target.result;
|
|
||||||
if (data instanceof ArrayBuffer) {
|
|
||||||
const view = new Uint8Array(data);
|
|
||||||
const array = [...view] as Array<u8>;
|
|
||||||
this.cpu_signals.dispatch(UiCpuSignal.RequestCpuReset);
|
|
||||||
for (const [i, v] of array.entries()) {
|
|
||||||
const address = m256(i);
|
|
||||||
const bank = Math.floor(i / 256) as u2;
|
|
||||||
this.cpu_signals.dispatch(UiCpuSignal.RequestMemoryChange, { address, bank, value: v });
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log("not array");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
reader.readAsArrayBuffer(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line class-methods-use-this
|
|
||||||
init_cpu_events(e: CpuEventHandler): void {
|
|
||||||
e.listen(CpuEvent.MemoryDumped, ({ memory }) => {
|
|
||||||
const flattened = new Uint8Array(256 * memory.length);
|
|
||||||
for (let x = 0; x < 4; x++) {
|
|
||||||
for (let y = 0; y < 256; x++) {
|
|
||||||
flattened[256 * x + y] = memory[x][y];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const blob = new Blob([flattened], { type: "application/octet-stream" });
|
|
||||||
const url = URL.createObjectURL(blob);
|
|
||||||
|
|
||||||
const link = document.createElement("a");
|
|
||||||
link.href = url;
|
|
||||||
link.download = "bin.bin";
|
|
||||||
link.click();
|
|
||||||
link.remove();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -12,8 +12,8 @@ import { CpuEventHandler, UiCpuSignalHandler, UiEventHandler } from "../events";
|
||||||
// - CpuEventHandler: listen to events created as a result of CPU actions
|
// - CpuEventHandler: listen to events created as a result of CPU actions
|
||||||
// - UiCpuEventSignaler: dispatch signals to request actions from the CPU
|
// - UiCpuEventSignaler: dispatch signals to request actions from the CPU
|
||||||
|
|
||||||
export interface UiComponent {
|
export default interface UiComponent {
|
||||||
element: HTMLElement;
|
container: HTMLElement;
|
||||||
/** Allows listening and emitting UiEvents*/
|
/** Allows listening and emitting UiEvents*/
|
||||||
events: UiEventHandler;
|
events: UiEventHandler;
|
||||||
/** Creating signals for the cpu to process */
|
/** Creating signals for the cpu to process */
|
||||||
|
|
|
@ -1,52 +1,78 @@
|
||||||
import { el } from "../etc";
|
import { el } from "../etc";
|
||||||
export abstract class WindowBox {
|
|
||||||
element: HTMLElement;
|
interface WindowBoxOptions {
|
||||||
|
collapsed?: boolean;
|
||||||
|
fit_content?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BORDER_STROKE = 5; // px
|
||||||
|
|
||||||
|
export default abstract class WindowBox {
|
||||||
|
container: HTMLElement;
|
||||||
title_bar: HTMLElement;
|
title_bar: HTMLElement;
|
||||||
readonly title: string;
|
readonly title: string;
|
||||||
private resize: HTMLElement;
|
|
||||||
private collapse_button: HTMLButtonElement;
|
private collapse_button: HTMLButtonElement;
|
||||||
private collapsed: boolean = false;
|
private collapsed = false;
|
||||||
private resize_func: (e: MouseEvent) => void;
|
private fit_content = false;
|
||||||
|
private resize?: HTMLElement;
|
||||||
|
private resize_func?: (e: MouseEvent) => void;
|
||||||
|
|
||||||
constructor(element: HTMLElement, title: string, options?: { collapsed?: boolean }) {
|
constructor(element: HTMLElement, title: string, options?: WindowBoxOptions) {
|
||||||
this.element = element;
|
this.container = element;
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.element.classList.add("window");
|
|
||||||
this.title_bar = el("div", undefined, "window_title");
|
this.container.classList.add("window");
|
||||||
this.element.appendChild(this.title_bar);
|
this.title_bar = el("div").cl("window_title").fin();
|
||||||
const title_bar_text_box = el("div", "text");
|
if (this.container.firstChild !== null) {
|
||||||
|
this.container.firstChild.before(this.title_bar);
|
||||||
|
} else {
|
||||||
|
this.container.appendChild(this.title_bar);
|
||||||
|
}
|
||||||
|
const title_bar_text_box = el("div").id("text").fin();
|
||||||
title_bar_text_box.textContent = title;
|
title_bar_text_box.textContent = title;
|
||||||
|
|
||||||
|
this.collapse_button = el("button").id("collapse_button").cl("nostyle").fin();
|
||||||
|
this.collapse_button.addEventListener("click", () => this.toggle_collapse());
|
||||||
|
|
||||||
this.title_bar.appendChild(title_bar_text_box);
|
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.title_bar.appendChild(this.collapse_button);
|
||||||
this.resize.addEventListener("mousedown", (e) => {
|
|
||||||
window.addEventListener("mousemove", this.resize_func);
|
if (options?.collapsed) this.collapse();
|
||||||
});
|
|
||||||
window.addEventListener("mouseup", () => {
|
if (options?.fit_content) {
|
||||||
this.remove_resize_listeners();
|
this.fit_content = true;
|
||||||
});
|
} else {
|
||||||
window.addEventListener("mouseleave", () => {
|
this.resize = el("div").id("resize").fin();
|
||||||
this.remove_resize_listeners();
|
this.container.appendChild(this.resize);
|
||||||
});
|
this.resize_func = this.resize_move.bind(this);
|
||||||
if (options?.collapsed) {
|
this.resize.addEventListener("mousedown", (e) => {
|
||||||
this.collapse();
|
if (this.resize_func) window.addEventListener("mousemove", this.resize_func);
|
||||||
|
});
|
||||||
|
window.addEventListener("mouseup", () => this.remove_resize_listeners());
|
||||||
|
window.addEventListener("mouseleave", () => this.remove_resize_listeners());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
collapse(): void {
|
collapse(): void {
|
||||||
this.element.classList.add("collapsed");
|
this.container.classList.add("collapsed");
|
||||||
this.remove_resize_listeners();
|
this.remove_resize_listeners();
|
||||||
this.resize.style.visibility = "hidden";
|
if (this.resize) this.resize.style.visibility = "hidden";
|
||||||
this.element.style.height = `${this.title_bar.offsetHeight + 4}px`;
|
this.set_height(this.title_bar.offsetHeight - BORDER_STROKE);
|
||||||
this.collapsed = true;
|
this.collapsed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
correct_height_value(height: number): number {
|
||||||
|
if (this.fit_content) {
|
||||||
|
let height_sum = 0;
|
||||||
|
for (const c of this.container.children) {
|
||||||
|
height_sum += (<HTMLElement>c).offsetHeight;
|
||||||
|
}
|
||||||
|
return height_sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
toggle_collapse(): void {
|
toggle_collapse(): void {
|
||||||
if (this.collapsed) {
|
if (this.collapsed) {
|
||||||
this.uncollapse();
|
this.uncollapse();
|
||||||
|
@ -55,15 +81,21 @@ export abstract class WindowBox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set_height(height: number): void {
|
||||||
|
this.container.style.height = `${height + 2 * BORDER_STROKE}px`;
|
||||||
|
}
|
||||||
|
|
||||||
uncollapse(): void {
|
uncollapse(): void {
|
||||||
this.element.classList.remove("collapsed");
|
this.container.classList.remove("collapsed");
|
||||||
this.resize.style.visibility = "unset";
|
if (this.resize) this.resize.style.visibility = "unset";
|
||||||
this.element.style.height = `${this.title_bar.offsetHeight + 10 + 200}px`;
|
const new_height = this.correct_height_value(this.title_bar.offsetHeight + 200);
|
||||||
|
this.set_height(new_height);
|
||||||
|
|
||||||
this.collapsed = false;
|
this.collapsed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
remove_resize_listeners(): void {
|
remove_resize_listeners(): void {
|
||||||
window.removeEventListener("mousemove", this.resize_func);
|
if (this.resize_func) window.removeEventListener("mousemove", this.resize_func);
|
||||||
}
|
}
|
||||||
|
|
||||||
resize_move(e: MouseEvent): void {
|
resize_move(e: MouseEvent): void {
|
||||||
|
@ -72,11 +104,11 @@ export abstract class WindowBox {
|
||||||
this.remove_resize_listeners();
|
this.remove_resize_listeners();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const distance_to_title = e.clientY - this.element.offsetTop - this.title_bar.offsetHeight + window.scrollY + 5;
|
const distance_to_title = e.clientY - this.container.offsetTop - this.title_bar.offsetHeight + window.scrollY + 5;
|
||||||
if (distance_to_title <= 5) {
|
if (distance_to_title <= 5) {
|
||||||
this.collapse();
|
this.collapse();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.element.style.height = `${e.clientY - this.element.offsetTop + window.scrollY + 8}px`;
|
this.set_height(e.clientY - this.container.offsetTop + window.scrollY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
35
src/ui/windows/bank_visualizer.ts
Normal file
35
src/ui/windows/bank_visualizer.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import { CpuEvent, CpuEventHandler, UiEventHandler } from "../../events";
|
||||||
|
import UiComponent from "../uiComponent";
|
||||||
|
import WindowBox from "../windowBox";
|
||||||
|
import { DEFAULT_VRAM_BANK } from "../../constants";
|
||||||
|
|
||||||
|
export default class BankVisualizer extends WindowBox implements UiComponent {
|
||||||
|
events: UiEventHandler;
|
||||||
|
cpu_banks: Array<SVGPolylineElement>;
|
||||||
|
vram_banks: Array<SVGPolylineElement>;
|
||||||
|
constructor(element: HTMLElement, events: UiEventHandler) {
|
||||||
|
super(element, "Bank Status", { fit_content: true });
|
||||||
|
this.events = events;
|
||||||
|
this.cpu_banks = [...element.querySelectorAll("#cpu_bank>polyline")] as Array<SVGPolylineElement>;
|
||||||
|
this.vram_banks = [...element.querySelectorAll("#vram_bank>polyline")] as Array<SVGPolylineElement>;
|
||||||
|
}
|
||||||
|
|
||||||
|
init_cpu_events(c: CpuEventHandler): void {
|
||||||
|
c.listen(CpuEvent.SetVramBank, ({ bank }) => {
|
||||||
|
for (const bank_path of this.vram_banks) bank_path.setAttribute("stroke", "gray");
|
||||||
|
this.vram_banks[bank].setAttribute("stroke", "yellow");
|
||||||
|
});
|
||||||
|
c.listen(CpuEvent.SwitchBank, ({ bank }) => {
|
||||||
|
for (const bank_path of this.cpu_banks) bank_path.setAttribute("stroke", "gray");
|
||||||
|
this.cpu_banks[bank].setAttribute("stroke", "yellow");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(): void {
|
||||||
|
for (const bank_path of this.vram_banks) bank_path.setAttribute("stroke", "gray");
|
||||||
|
for (const bank_path of this.cpu_banks) bank_path.setAttribute("stroke", "gray");
|
||||||
|
this.vram_banks[DEFAULT_VRAM_BANK].setAttribute("stroke", "yellow");
|
||||||
|
|
||||||
|
this.cpu_banks[0].setAttribute("stroke", "yellow");
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,10 +2,10 @@ import { el, format_hex } from "../../etc";
|
||||||
import { CpuEvent, CpuEventHandler, UiCpuSignalHandler, UiEventHandler } from "../../events";
|
import { CpuEvent, CpuEventHandler, UiCpuSignalHandler, UiEventHandler } from "../../events";
|
||||||
import { Instruction, ParamType, ParameterType } from "../../instructionSet";
|
import { Instruction, ParamType, ParameterType } from "../../instructionSet";
|
||||||
import { u8 } from "../../num";
|
import { u8 } from "../../num";
|
||||||
import { WindowBox } from "../windowBox";
|
import WindowBox from "../windowBox";
|
||||||
import { UiComponent } from "../uiComponent";
|
import UiComponent from "../uiComponent";
|
||||||
|
|
||||||
export class InstructionExplainer extends WindowBox implements UiComponent {
|
export default class InstructionExplainer extends WindowBox implements UiComponent {
|
||||||
events: UiEventHandler;
|
events: UiEventHandler;
|
||||||
cpu_signals: UiCpuSignalHandler;
|
cpu_signals: UiCpuSignalHandler;
|
||||||
constructor(element: HTMLElement, events: UiEventHandler, cpu_signals: UiCpuSignalHandler) {
|
constructor(element: HTMLElement, events: UiEventHandler, cpu_signals: UiCpuSignalHandler) {
|
||||||
|
@ -19,16 +19,17 @@ export class InstructionExplainer extends WindowBox implements UiComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
private add_box(box_icon_text: string, name: string, css_class: string): void {
|
private add_box(box_icon_text: string, name: string, css_class: string): void {
|
||||||
const instr_box = el("div", "expl_box");
|
const instr_box = el("div").id("expl_box").fin();
|
||||||
const instr_icon = el("span", "expl_icon");
|
const instr_icon = el("span")
|
||||||
instr_icon.classList.add(css_class);
|
.id("expl_icon")
|
||||||
instr_icon.setAttribute("title", css_class.toUpperCase());
|
.cl(css_class)
|
||||||
instr_icon.textContent = box_icon_text;
|
.at("title", css_class.toUpperCase())
|
||||||
const instr_box_text = el("span", "expl_text");
|
.tx(box_icon_text)
|
||||||
instr_box_text.textContent = name;
|
.fin();
|
||||||
|
const instr_box_text = el("span").id("expl_text").tx(name).fin();
|
||||||
instr_box.appendChild(instr_icon);
|
instr_box.appendChild(instr_icon);
|
||||||
instr_box.appendChild(instr_box_text);
|
instr_box.appendChild(instr_box_text);
|
||||||
this.element.appendChild(instr_box);
|
this.container.appendChild(instr_box);
|
||||||
}
|
}
|
||||||
|
|
||||||
add_parameter(param: ParameterType, pos: u8, byte: u8): void {
|
add_parameter(param: ParameterType, pos: u8, byte: u8): void {
|
||||||
|
@ -64,6 +65,6 @@ export class InstructionExplainer extends WindowBox implements UiComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
reset(): void {
|
reset(): void {
|
||||||
this.element.querySelectorAll("#expl_box").forEach((e) => e.remove());
|
this.container.querySelectorAll("#expl_box").forEach((e) => e.remove());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { el } from "../../etc";
|
import { el } from "../../etc";
|
||||||
import { CpuEvent, CpuEventHandler, UiCpuSignalHandler, UiEventHandler } from "../../events";
|
import { CpuEvent, CpuEventHandler, UiCpuSignalHandler, UiEventHandler } from "../../events";
|
||||||
import { WindowBox } from "../windowBox";
|
import WindowBox from "../windowBox";
|
||||||
import { UiComponent } from "../uiComponent";
|
import UiComponent from "../uiComponent";
|
||||||
|
|
||||||
export class Printout extends WindowBox implements UiComponent {
|
export default class Printout extends WindowBox implements UiComponent {
|
||||||
events: UiEventHandler;
|
events: UiEventHandler;
|
||||||
text_box: HTMLElement;
|
text_box: HTMLElement;
|
||||||
cpu_signals: UiCpuSignalHandler;
|
cpu_signals: UiCpuSignalHandler;
|
||||||
|
@ -11,8 +11,8 @@ export class Printout extends WindowBox implements UiComponent {
|
||||||
super(element, "Printout");
|
super(element, "Printout");
|
||||||
this.cpu_signals = cpu_signals;
|
this.cpu_signals = cpu_signals;
|
||||||
this.events = events;
|
this.events = events;
|
||||||
this.text_box = el("div", "printout_text");
|
this.text_box = el("div").id("printout_text").fin();
|
||||||
this.element.appendChild(this.text_box);
|
this.container.appendChild(this.text_box);
|
||||||
}
|
}
|
||||||
|
|
||||||
init_cpu_events(c: CpuEventHandler): void {
|
init_cpu_events(c: CpuEventHandler): void {
|
||||||
|
|
|
@ -1,35 +1,41 @@
|
||||||
|
import { DEFAULT_VRAM_BANK } from "../../constants";
|
||||||
import { el } from "../../etc";
|
import { el } from "../../etc";
|
||||||
import { UiEventHandler, CpuEventHandler, CpuEvent } from "../../events";
|
import { UiEventHandler, CpuEventHandler, CpuEvent, UiCpuSignalHandler, UiCpuSignal } from "../../events";
|
||||||
import { u4, u8 } from "../../num";
|
import { u2, u4, u8 } from "../../num";
|
||||||
import { UiComponent } from "../uiComponent";
|
import UiComponent from "../uiComponent";
|
||||||
import { WindowBox } from "../windowBox";
|
import WindowBox from "../windowBox";
|
||||||
export class Screen extends WindowBox implements UiComponent {
|
|
||||||
|
const CANVAS_SIZE = 512;
|
||||||
|
const WIDTH = 16;
|
||||||
|
|
||||||
|
export default class Screen extends WindowBox implements UiComponent {
|
||||||
events: UiEventHandler;
|
events: UiEventHandler;
|
||||||
screen: HTMLCanvasElement;
|
screen: HTMLCanvasElement;
|
||||||
|
cpu_signals: UiCpuSignalHandler;
|
||||||
ctx: CanvasRenderingContext2D;
|
ctx: CanvasRenderingContext2D;
|
||||||
scale: [number, number];
|
scale: number;
|
||||||
constructor(element: HTMLElement, event: UiEventHandler) {
|
current_vram_bank: u2 = DEFAULT_VRAM_BANK;
|
||||||
super(element, "TV", { collapsed: true });
|
constructor(element: HTMLElement, event: UiEventHandler, cpu_signals: UiCpuSignalHandler) {
|
||||||
this.screen = el("canvas", "screen");
|
super(element, "TV", { collapsed: true, fit_content: true });
|
||||||
|
this.cpu_signals = cpu_signals;
|
||||||
this.events = event;
|
this.events = event;
|
||||||
const canvas_size = [512, 512];
|
|
||||||
const data_size = [16, 16];
|
this.scale = CANVAS_SIZE / WIDTH;
|
||||||
this.scale = [canvas_size[0] / data_size[0], canvas_size[1] / data_size[1]];
|
this.screen = el("canvas").id("screen").fin();
|
||||||
[this.screen.width, this.screen.height] = canvas_size;
|
this.screen.width = CANVAS_SIZE;
|
||||||
|
this.screen.height = CANVAS_SIZE;
|
||||||
const ctx = this.screen.getContext("2d");
|
const ctx = this.screen.getContext("2d");
|
||||||
if (ctx === null) {
|
if (ctx === null) {
|
||||||
throw new Error("could not load screen");
|
throw new Error("could not load screen");
|
||||||
}
|
}
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.element.appendChild(this.screen);
|
this.container.appendChild(this.screen);
|
||||||
this.test_pattern();
|
this.test_pattern();
|
||||||
}
|
}
|
||||||
|
|
||||||
private test_pattern(): void {
|
private test_pattern(): void {
|
||||||
for (let x = 0; x < 16; x++) {
|
for (let x = 0; x < 256; x++) {
|
||||||
for (let y = 0; y < 16; y++) {
|
this.setPixel(x as u8, x as u8);
|
||||||
this.setPixel(x as u4, y as u4, (x + 16 * y) as u8);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,14 +49,24 @@ export class Screen extends WindowBox implements UiComponent {
|
||||||
init_cpu_events(c: CpuEventHandler): void {
|
init_cpu_events(c: CpuEventHandler): void {
|
||||||
c.listen(CpuEvent.MemoryChanged, ({ address, bank, value }) => {
|
c.listen(CpuEvent.MemoryChanged, ({ address, bank, value }) => {
|
||||||
if (bank !== 1) return;
|
if (bank !== 1) return;
|
||||||
const x = (address % 16) as u4;
|
|
||||||
const y = Math.floor(address / 16) as u4;
|
this.setPixel(address, value);
|
||||||
this.setPixel(x, y, value);
|
});
|
||||||
|
c.listen(CpuEvent.SetVramBank, ({ bank }) => {
|
||||||
|
this.current_vram_bank = bank;
|
||||||
|
this.cpu_signals.dispatch(UiCpuSignal.RequestMemoryDump, (memory) => {
|
||||||
|
const vram = memory[this.current_vram_bank];
|
||||||
|
for (const [i, pixel] of vram.entries()) {
|
||||||
|
this.setPixel(i as u8, pixel as u8);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setPixel(x: u4, y: u4, value: u8): void {
|
setPixel(address: u8, value: u8): void {
|
||||||
const point: [number, number] = [x * this.scale[0], y * this.scale[1]];
|
const x = (address % 16) as u4;
|
||||||
|
const y = Math.floor(address / 16) as u4;
|
||||||
|
const point: [number, number] = [x * this.scale, y * this.scale];
|
||||||
|
|
||||||
const RED_SCALE = 255 / 2 ** 3;
|
const RED_SCALE = 255 / 2 ** 3;
|
||||||
const GREEN_SCALE = 255 / 2 ** 3;
|
const GREEN_SCALE = 255 / 2 ** 3;
|
||||||
|
@ -61,6 +77,6 @@ export class Screen extends WindowBox implements UiComponent {
|
||||||
|
|
||||||
const color = `rgb(${red},${green},${blue})`;
|
const color = `rgb(${red},${green},${blue})`;
|
||||||
this.ctx.fillStyle = color;
|
this.ctx.fillStyle = color;
|
||||||
this.ctx.fillRect(...point, ...this.scale);
|
this.ctx.fillRect(...point, this.scale, this.scale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
47
src/util/elementMaker.ts
Normal file
47
src/util/elementMaker.ts
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
class ElementInProgress<E extends HTMLElement> {
|
||||||
|
private element: E;
|
||||||
|
constructor(el: E) {
|
||||||
|
this.element = el;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set attribute */
|
||||||
|
at(name: string, value: string): ElementInProgress<E> {
|
||||||
|
this.element.setAttribute(name, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set id */
|
||||||
|
id(id: string): ElementInProgress<E> {
|
||||||
|
this.element.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Add class */
|
||||||
|
cl(class_name: string): ElementInProgress<E> {
|
||||||
|
this.element.classList.add(class_name);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set textContent */
|
||||||
|
tx(text_contents: string): ElementInProgress<E> {
|
||||||
|
this.element.textContent = text_contents;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set style */
|
||||||
|
st(name: string, value: string): ElementInProgress<E> {
|
||||||
|
this.element.style.setProperty(name, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
fin(): E {
|
||||||
|
return this.element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function el<E extends keyof HTMLElementTagNameMap>(
|
||||||
|
name: E
|
||||||
|
): ElementInProgress<HTMLElementTagNameMap[E]> {
|
||||||
|
const element = document.createElement(name);
|
||||||
|
return new ElementInProgress(element);
|
||||||
|
}
|
74
svg.html
74
svg.html
|
@ -1,74 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>Document</title>
|
|
||||||
<style>
|
|
||||||
html {
|
|
||||||
color: white;
|
|
||||||
background-color: black;
|
|
||||||
font-family: monospace;
|
|
||||||
font-size: 2em;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
margin-top: 100px;
|
|
||||||
align-content: center;
|
|
||||||
margin-inline: auto;
|
|
||||||
margin-block: auto;
|
|
||||||
}
|
|
||||||
#cont {
|
|
||||||
margin-top: 100px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
#cpu,
|
|
||||||
#target {
|
|
||||||
margin-block: auto;
|
|
||||||
border: solid 3px yellow;
|
|
||||||
padding: 6px;
|
|
||||||
}
|
|
||||||
#targets {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 9px;
|
|
||||||
}
|
|
||||||
#connections {
|
|
||||||
}
|
|
||||||
div#target.gray {
|
|
||||||
border-color: gray;
|
|
||||||
}
|
|
||||||
svg polyline {
|
|
||||||
fill: transparent;
|
|
||||||
stroke-width: 3;
|
|
||||||
stroke-linecap: butt;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="cont">
|
|
||||||
<div id="cpu">CPU</div>
|
|
||||||
<svg
|
|
||||||
id="connections"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 100 100"
|
|
||||||
preserveAspectRatio="none"
|
|
||||||
width="100"
|
|
||||||
height="100%"
|
|
||||||
>
|
|
||||||
<polyline points="0,43 50,43 50,10, 100,10" stroke="yellow" />
|
|
||||||
<polyline points="0,48 64,48 64,37, 100,37" stroke="gray" />
|
|
||||||
<polyline points="0,53 64,53 64,63, 100,63" stroke="gray" />
|
|
||||||
<polyline points="0,58 50,58 50,90, 100,90" stroke="gray" />
|
|
||||||
</svg>
|
|
||||||
<div id="targets">
|
|
||||||
<div id="target">Main</div>
|
|
||||||
<div id="target" class="gray">Bank 1</div>
|
|
||||||
<div id="target" class="gray">Bank 2</div>
|
|
||||||
<div id="target" class="gray">VRAM</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
26
todo.md
26
todo.md
|
@ -1,3 +1,5 @@
|
||||||
|
Verify mod256 behavior on negatives
|
||||||
|
|
||||||
Edit Mode
|
Edit Mode
|
||||||
|
|
||||||
- Select where program counter is
|
- Select where program counter is
|
||||||
|
@ -8,18 +10,26 @@ Speed control slider behavior
|
||||||
Speed control slider styling
|
Speed control slider styling
|
||||||
Overclock Box
|
Overclock Box
|
||||||
|
|
||||||
|
Error log:
|
||||||
error in instruction when number out of range (fix new Error("todo"))
|
error in instruction when number out of range (fix new Error("todo"))
|
||||||
|
|
||||||
UI for showing which Memory bank is selected
|
- Allow ignoring errors
|
||||||
VRAM select instruction
|
- Highlight errored instruction parameters and give description
|
||||||
|
|
||||||
|
Hard reset button (resets everything)
|
||||||
|
|
||||||
|
Soft reset button. Resets program counter and registers.
|
||||||
|
|
||||||
|
Fixed size windows
|
||||||
|
|
||||||
|
- TV
|
||||||
|
- Bank Status
|
||||||
|
|
||||||
|
Limit size to printout text buffer
|
||||||
|
|
||||||
Improve instruction explainer. Clearly show what is an instruction and what is a parameter
|
Improve instruction explainer. Clearly show what is an instruction and what is a parameter
|
||||||
|
|
||||||
Verify mod256 behavior on negatives
|
Ui showing CPU flag(s) (Carry)
|
||||||
|
|
||||||
UI showing CPU flag(s) (Carry)
|
|
||||||
|
|
||||||
Error log
|
|
||||||
|
|
||||||
Responsive layout
|
Responsive layout
|
||||||
|
|
||||||
|
@ -28,5 +38,3 @@ standardize names of all things
|
||||||
Documentation with standard names
|
Documentation with standard names
|
||||||
|
|
||||||
Example Programs
|
Example Programs
|
||||||
|
|
||||||
Ui for togging your mother.
|
|
||||||
|
|
Loading…
Reference in a new issue