Compare commits
2 commits
9e7da12bf5
...
b550a62af8
Author | SHA1 | Date | |
---|---|---|---|
b550a62af8 | |||
96f1104734 |
|
@ -11,7 +11,7 @@ export type TempInstrState = {
|
|||
params: Array<u8>;
|
||||
};
|
||||
|
||||
function init_banks(): [Uint8Array, Uint8Array, Uint8Array, Uint8Array] {
|
||||
function initBanks(): [Uint8Array, Uint8Array, Uint8Array, Uint8Array] {
|
||||
const banks = [];
|
||||
for (let i = 0; i < 4; i++) {
|
||||
banks.push(new Uint8Array(256));
|
||||
|
@ -19,8 +19,8 @@ function init_banks(): [Uint8Array, Uint8Array, Uint8Array, Uint8Array] {
|
|||
return banks as [Uint8Array, Uint8Array, Uint8Array, Uint8Array];
|
||||
}
|
||||
|
||||
export class Computer {
|
||||
private banks: [Uint8Array, Uint8Array, Uint8Array, Uint8Array] = init_banks();
|
||||
export default class Computer {
|
||||
private banks: [Uint8Array, Uint8Array, Uint8Array, Uint8Array] = initBanks();
|
||||
private registers: Uint8Array = new Uint8Array(8);
|
||||
private call_stack: Array<u8> = [];
|
||||
private carry_flag: boolean = false;
|
||||
|
@ -41,7 +41,7 @@ export class Computer {
|
|||
code: current_byte,
|
||||
});
|
||||
console.log(`Invalid instruction: ${formatHex(current_byte)}`);
|
||||
this.step_forward();
|
||||
this.stepForward();
|
||||
this.events.dispatch(CpuEvent.Cycle);
|
||||
return;
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ export class Computer {
|
|||
}
|
||||
|
||||
if (this.current_instr.pos === this.program_counter && this.current_instr.params.length > 0) {
|
||||
this.step_forward();
|
||||
this.stepForward();
|
||||
this.events.dispatch(CpuEvent.Cycle);
|
||||
return;
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ export class Computer {
|
|||
this.current_instr.params[this.current_instr.params_found] = current_byte;
|
||||
this.current_instr.params_found += 1;
|
||||
if (this.current_instr.params.length !== this.current_instr.params_found) {
|
||||
this.step_forward();
|
||||
this.stepForward();
|
||||
this.events.dispatch(CpuEvent.Cycle);
|
||||
return;
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ export class Computer {
|
|||
this.current_instr = null;
|
||||
|
||||
if (execution_post_action_state.should_step) {
|
||||
this.step_forward();
|
||||
this.stepForward();
|
||||
}
|
||||
this.events.dispatch(CpuEvent.Cycle);
|
||||
}
|
||||
|
@ -163,17 +163,6 @@ export class Computer {
|
|||
this.events.dispatch(CpuEvent.SetVramBank, { bank });
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this.events.dispatch(CpuEvent.Reset);
|
||||
this.banks = init_banks();
|
||||
this.registers = new Uint8Array(8);
|
||||
this.call_stack = [];
|
||||
this.current_instr = null;
|
||||
this.program_counter = 0;
|
||||
this.carry_flag = false;
|
||||
this.vram_bank = 3;
|
||||
}
|
||||
|
||||
initEvents(ui: UiCpuSignalHandler): void {
|
||||
ui.listen(UiCpuSignal.RequestCpuCycle, (cycle_count) => {
|
||||
for (let i = 0; i < cycle_count; i++) this.cycle();
|
||||
|
@ -185,6 +174,31 @@ export class Computer {
|
|||
ui.listen(UiCpuSignal.RequestProgramCounterChange, ({ address }) => {
|
||||
this.setProgramCounter(address);
|
||||
});
|
||||
ui.listen(UiCpuSignal.RequestCpuSoftReset, () => this.softReset());
|
||||
}
|
||||
|
||||
softReset(): void {
|
||||
this.events.dispatch(CpuEvent.SoftReset);
|
||||
for (let i = 0; i < 8; i++) this.setRegister(i as u3, 0);
|
||||
while (this.popCallStack() !== null) 0;
|
||||
this.setVramBank(DEFAULT_VRAM_BANK);
|
||||
this.setCarry(false);
|
||||
this.current_instr = null;
|
||||
this.setProgramCounter(0);
|
||||
this.setBank(0);
|
||||
}
|
||||
reset(): void {
|
||||
this.events.dispatch(CpuEvent.Reset);
|
||||
// Hard reset
|
||||
this.banks = initBanks();
|
||||
// Soft reset
|
||||
for (let i = 0; i < 8; i++) this.setRegister(i as u3, 0);
|
||||
while (this.popCallStack() !== null) 0;
|
||||
this.setVramBank(DEFAULT_VRAM_BANK);
|
||||
this.setCarry(false);
|
||||
this.current_instr = null;
|
||||
this.setProgramCounter(0);
|
||||
this.setBank(0);
|
||||
}
|
||||
|
||||
loadMemory(program: Array<u8>): void {
|
||||
|
@ -205,7 +219,7 @@ export class Computer {
|
|||
return this.banks;
|
||||
}
|
||||
|
||||
private step_forward(): void {
|
||||
private stepForward(): void {
|
||||
this.program_counter = m256(this.program_counter + 1);
|
||||
this.events.dispatch(CpuEvent.ProgramCounterChanged, { counter: this.program_counter });
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ export enum CpuEvent {
|
|||
Cycle,
|
||||
Print,
|
||||
Reset,
|
||||
SoftReset,
|
||||
Halt,
|
||||
MemoryAccessed,
|
||||
SwitchBank,
|
||||
|
@ -28,7 +29,7 @@ export enum CpuEvent {
|
|||
SetVramBank,
|
||||
}
|
||||
|
||||
type VoidDataCpuEventList = CpuEvent.Halt | CpuEvent.Reset | CpuEvent.Cycle;
|
||||
type VoidDataCpuEventList = CpuEvent.Halt | CpuEvent.Reset | CpuEvent.SoftReset | CpuEvent.Cycle;
|
||||
|
||||
interface CpuEventMap {
|
||||
[CpuEvent.MemoryChanged]: { address: u8; bank: u2; value: u8 };
|
||||
|
@ -67,11 +68,12 @@ export enum UiCpuSignal {
|
|||
RequestMemoryChange,
|
||||
RequestRegisterChange,
|
||||
RequestCpuReset,
|
||||
RequestCpuSoftReset,
|
||||
RequestMemoryDump,
|
||||
RequestProgramCounterChange,
|
||||
}
|
||||
|
||||
type VoidDataUiCpuSignalList = UiCpuSignal.RequestCpuReset;
|
||||
type VoidDataUiCpuSignalList = UiCpuSignal.RequestCpuReset | UiCpuSignal.RequestCpuSoftReset;
|
||||
|
||||
interface UiCpuSignalMap {
|
||||
[UiCpuSignal.RequestCpuCycle]: number;
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
* @copyright Alexander Bass 2024
|
||||
* @license GPL-3.0
|
||||
*/
|
||||
import { Computer } from "./computer";
|
||||
import Computer from "./computer";
|
||||
import UI from "./ui";
|
||||
import { $ } from "./etc";
|
||||
import { ISA } from "./instructionSet";
|
||||
import { generateIsa } from "./isaGenerator";
|
||||
import { UI } from "./ui";
|
||||
import { u8 } from "./num";
|
||||
|
||||
import "./style/style.scss";
|
||||
|
@ -47,6 +47,8 @@ function main(): void {
|
|||
window.comp = computer;
|
||||
window.ui = ui;
|
||||
|
||||
// Todo, move to ui component
|
||||
// or move to documentation
|
||||
$("ISA").textContent = generateIsa(ISA);
|
||||
|
||||
let fire = false;
|
||||
|
|
5
src/style/hover_text_box.scss
Normal file
5
src/style/hover_text_box.scss
Normal file
|
@ -0,0 +1,5 @@
|
|||
.hover_text_box {
|
||||
border: 5px solid yellow;
|
||||
background-color: black;
|
||||
user-select: none;
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
@use "memory_registers";
|
||||
@use "hover_text_box";
|
||||
@use "windows";
|
||||
@use "buttons";
|
||||
@use "vars";
|
||||
|
|
17
src/ui.ts
17
src/ui.ts
|
@ -5,18 +5,18 @@ import UiComponent, { UiComponentConstructor } from "./ui/uiComponent";
|
|||
import MemoryView from "./ui/components/memoryView";
|
||||
import frequencyIndicator from "./ui/components/frequencyIndicator";
|
||||
import RegisterView from "./ui/components/registerView";
|
||||
import BankSelector from "./ui/components/bank_view_selector";
|
||||
import BankSelector from "./ui/components/bankViewSelector";
|
||||
import EditButton from "./ui/components/editButton";
|
||||
import pausePlay from "./ui/components/pausePlay";
|
||||
import SaveLoad from "./ui/components/saveLoad";
|
||||
import ResetButtons from "./ui/components/reset_buttons";
|
||||
import ResetButtons from "./ui/components/resetButtons";
|
||||
// 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";
|
||||
import BankVisualizer from "./ui/windows/bankVisualizer";
|
||||
|
||||
export class UI {
|
||||
export default class UI {
|
||||
ui_events: UiEventHandler = new UiEventHandler();
|
||||
cpu_signaler: UiCpuSignalHandler = new UiCpuSignalHandler();
|
||||
private components: Array<UiComponent> = [];
|
||||
|
@ -47,9 +47,8 @@ export class UI {
|
|||
}
|
||||
|
||||
initEvents(cpu_events: CpuEventHandler): void {
|
||||
cpu_events.listen(CpuEvent.Reset, () => {
|
||||
this.reset();
|
||||
});
|
||||
cpu_events.listen(CpuEvent.Reset, () => this.reset());
|
||||
cpu_events.listen(CpuEvent.SoftReset, () => this.softReset());
|
||||
|
||||
for (const c of this.components) if (c.initCpuEvents) c.initCpuEvents(cpu_events);
|
||||
}
|
||||
|
@ -57,4 +56,8 @@ export class UI {
|
|||
reset(): void {
|
||||
for (const c of this.components) if (c.reset) c.reset();
|
||||
}
|
||||
|
||||
softReset(): void {
|
||||
for (const c of this.components) if (c.softReset) c.softReset();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import UiComponent from "../uiComponent";
|
|||
export default class BankSelector implements UiComponent {
|
||||
container: HTMLElement;
|
||||
events: UiEventHandler;
|
||||
private bank_buttons: Array<HTMLButtonElement>;
|
||||
bank_buttons: Array<HTMLButtonElement>;
|
||||
constructor(element: HTMLElement, events: UiEventHandler) {
|
||||
this.container = element;
|
||||
this.events = events;
|
||||
|
@ -28,8 +28,13 @@ export default class BankSelector implements UiComponent {
|
|||
|
||||
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");
|
||||
}
|
||||
|
||||
softReset(): void {
|
||||
this.reset();
|
||||
}
|
||||
}
|
|
@ -12,18 +12,18 @@ export default class EditButton implements UiComponent {
|
|||
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.addEventListener("click", () => this.editToggle());
|
||||
this.container.appendChild(image);
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
disable(): void {
|
||||
const is_on = this.container.classList.contains("on");
|
||||
if (is_on) {
|
||||
this.edit_toggle();
|
||||
this.editToggle();
|
||||
}
|
||||
}
|
||||
|
||||
edit_toggle(): void {
|
||||
editToggle(): void {
|
||||
const is_on = this.container.classList.contains("on");
|
||||
if (is_on) {
|
||||
this.container.classList.remove("on");
|
||||
|
@ -35,7 +35,14 @@ export default class EditButton implements UiComponent {
|
|||
$("root").classList.add("editor");
|
||||
this.container.classList.add("on");
|
||||
this.container.classList.remove("off");
|
||||
this.cpu_signals.dispatch(UiCpuSignal.RequestProgramCounterChange, { address: 0 });
|
||||
}
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this.disable();
|
||||
}
|
||||
|
||||
softReset(): void {
|
||||
this.disable();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,10 @@ import UiComponent from "../uiComponent";
|
|||
|
||||
export default class frequencyIndicator implements UiComponent {
|
||||
container: HTMLElement;
|
||||
private running: number | null = null;
|
||||
private count: number = 0;
|
||||
private last_value: number = 0;
|
||||
private last_time: number = 0;
|
||||
running: number | null = null;
|
||||
count: number = 0;
|
||||
last_value: number = 0;
|
||||
last_time: number = 0;
|
||||
events: UiEventHandler;
|
||||
constructor(element: HTMLElement, events: UiEventHandler) {
|
||||
this.container = element;
|
||||
|
@ -19,16 +19,16 @@ export default class frequencyIndicator implements UiComponent {
|
|||
if (this.running !== null) {
|
||||
throw new Error("Tried starting frequencyIndicator twice!");
|
||||
}
|
||||
setInterval(this.update_indicator.bind(this), 1000);
|
||||
window.setInterval(this.updateIndicator.bind(this), 1000);
|
||||
}
|
||||
|
||||
stop(): void {
|
||||
if (this.running === null) return;
|
||||
clearInterval(this.running);
|
||||
window.clearInterval(this.running);
|
||||
this.running = null;
|
||||
}
|
||||
|
||||
update_indicator(): void {
|
||||
updateIndicator(): void {
|
||||
const new_time = performance.now();
|
||||
const dt = (new_time - this.last_time) / 1000 || 1;
|
||||
const value = Math.round(this.count / dt);
|
||||
|
@ -44,15 +44,21 @@ export default class frequencyIndicator implements UiComponent {
|
|||
this.count = 0;
|
||||
}
|
||||
|
||||
clock_cycle(): void {
|
||||
clockCycle(): void {
|
||||
this.count += 1;
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this.stop();
|
||||
this.count = 0;
|
||||
this.last_value = 0;
|
||||
this.start();
|
||||
}
|
||||
|
||||
softReset(): void {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
initCpuEvents(c: CpuEventHandler): void {
|
||||
c.listen(CpuEvent.Cycle, () => {
|
||||
this.count += 1;
|
||||
|
|
|
@ -53,6 +53,7 @@ export default class MemoryView implements UiComponent {
|
|||
});
|
||||
}
|
||||
this.events.listen(UiEvent.ChangeViewBank, ({ bank }) => this.setBank(bank));
|
||||
this.setProgramCounter(0);
|
||||
}
|
||||
|
||||
get program(): CelledViewer {
|
||||
|
@ -76,6 +77,12 @@ export default class MemoryView implements UiComponent {
|
|||
this.setProgramCounter(0);
|
||||
}
|
||||
|
||||
softReset(): void {
|
||||
for (const viewer of this.banks) viewer.clearAllClasses();
|
||||
this.last_accessed_cell = null;
|
||||
this.setProgramCounter(0);
|
||||
}
|
||||
|
||||
initCpuEvents(c: CpuEventHandler): void {
|
||||
c.listen(CpuEvent.MemoryAccessed, ({ address, bank, value }) => {
|
||||
if (this.last_accessed_cell?.address !== address || this.last_accessed_cell?.bank !== bank) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { el } from "../../etc";
|
||||
import { UiEventHandler, UiEvent, CpuEventHandler, UiCpuSignalHandler, UiCpuSignal } from "../../events";
|
||||
import UiComponent from "../uiComponent";
|
||||
import HoverTextBox from "../hoverTextBox";
|
||||
|
||||
const MAX_SLIDER = 1000;
|
||||
|
||||
|
@ -41,18 +42,22 @@ export default class pausePlay implements UiComponent {
|
|||
|
||||
this.events.listen(UiEvent.EditOn, () => this.disable());
|
||||
this.events.listen(UiEvent.EditOff, () => this.enable());
|
||||
const tb = new HoverTextBox(this.start_button, el("span").tx("hover test").st("color", "yellow").fin(), "left", 10);
|
||||
tb.show();
|
||||
}
|
||||
|
||||
disable(): void {
|
||||
this.stop();
|
||||
this.start_button.setAttribute("disabled", "true");
|
||||
this.step_button.setAttribute("disabled", "true");
|
||||
|
||||
this.range.setAttribute("disabled", "true");
|
||||
}
|
||||
|
||||
enable(): void {
|
||||
this.start_button.removeAttribute("disabled");
|
||||
this.step_button.removeAttribute("disabled");
|
||||
|
||||
this.range.removeAttribute("disabled");
|
||||
}
|
||||
|
||||
|
@ -76,6 +81,7 @@ export default class pausePlay implements UiComponent {
|
|||
};
|
||||
loop();
|
||||
}
|
||||
|
||||
private step(): void {
|
||||
if (this.on) {
|
||||
this.stop();
|
||||
|
@ -98,4 +104,8 @@ export default class pausePlay implements UiComponent {
|
|||
this.stop();
|
||||
this.enable();
|
||||
}
|
||||
|
||||
softReset(): void {
|
||||
this.reset();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,10 +7,7 @@ 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 });
|
||||
});
|
||||
super(8, 1, element, (a, v) => this.onEdit(a, v));
|
||||
this.events = events;
|
||||
this.cpu_signals = cpu_signals;
|
||||
|
||||
|
@ -24,8 +21,16 @@ export default class RegisterView extends CelledViewer implements UiComponent {
|
|||
});
|
||||
}
|
||||
|
||||
onEdit(address: u8, value: u8): void {
|
||||
if (!isU3(address)) throw new Error("unreachable");
|
||||
this.cpu_signals.dispatch(UiCpuSignal.RequestRegisterChange, { register_no: address as u3, value });
|
||||
}
|
||||
|
||||
initCpuEvents(c: CpuEventHandler): void {
|
||||
c.listen(CpuEvent.RegisterChanged, ({ register_no, value }) => this.setCellValue(register_no, value));
|
||||
c.listen(CpuEvent.Reset, () => this.reset());
|
||||
}
|
||||
|
||||
softReset(): void {
|
||||
this.clearAllClasses();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { el } from "../../etc";
|
||||
import { UiEventHandler, UiCpuSignalHandler, UiEvent, UiCpuSignal } from "../../events";
|
||||
import { UiEventHandler, UiCpuSignalHandler, UiCpuSignal } from "../../events";
|
||||
import UiComponent from "../uiComponent";
|
||||
|
||||
export default class ResetButtons implements UiComponent {
|
||||
|
@ -10,17 +10,22 @@ export default class ResetButtons implements UiComponent {
|
|||
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();
|
||||
const reset_button = el("button").cl("nostyle").ti("Reset State").tx("⟳").fin();
|
||||
const trash_button = el("button").cl("nostyle").ti("Delete Code").tx("🗑").fin();
|
||||
|
||||
reset_button.addEventListener("click", () => this.resetClicked());
|
||||
trash_button.addEventListener("click", () => this.trashClicked());
|
||||
this.container.append(reset_button, trash_button);
|
||||
}
|
||||
|
||||
resetClicked(): void {}
|
||||
resetClicked(): void {
|
||||
this.cpu_signals.dispatch(UiCpuSignal.RequestCpuSoftReset);
|
||||
}
|
||||
|
||||
trashClicked(): void {
|
||||
this.cpu_signals.dispatch(UiCpuSignal.RequestCpuReset);
|
||||
const a = confirm("Clear all code? Irreversible");
|
||||
if (a) {
|
||||
this.cpu_signals.dispatch(UiCpuSignal.RequestCpuReset);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import { el } from "../../etc";
|
||||
import { UiEventHandler, UiCpuSignalHandler, UiCpuSignal } from "../../events";
|
||||
import { u2, u8, m256, isU2 } from "../../num";
|
||||
import { u8, m256, isU2 } from "../../num";
|
||||
import UiComponent from "../uiComponent";
|
||||
|
||||
export default class SaveLoad implements UiComponent {
|
||||
|
@ -14,16 +14,16 @@ export default class SaveLoad implements UiComponent {
|
|||
this.events = events;
|
||||
this.cpu_signals = cpu_signals;
|
||||
|
||||
this.save_button = el("button").id("save_button").tx("Save").fin();
|
||||
this.save_button = el("button").id("save_button").tx("⬇").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();
|
||||
const label = el("label").cl("button").at("for", "binary_upload").tx("⬆").fin();
|
||||
|
||||
this.container.append(this.binary_upload, label, this.save_button);
|
||||
this.container.append(this.save_button, this.binary_upload, label);
|
||||
|
||||
this.save_button.addEventListener("click", () => {
|
||||
this.download();
|
||||
|
|
48
src/ui/hoverTextBox.ts
Normal file
48
src/ui/hoverTextBox.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
import { el } from "../etc";
|
||||
|
||||
type RelativePosition = "top" | "bottom" | "left" | "right";
|
||||
|
||||
export default class HoverTextBox {
|
||||
parent: HTMLElement;
|
||||
position: RelativePosition;
|
||||
gap: number;
|
||||
contents: HTMLElement;
|
||||
shown: false | { container: HTMLElement; resize_event_fn: () => void };
|
||||
constructor(parent: HTMLElement, contents: HTMLElement, position: RelativePosition, gap: number) {
|
||||
this.gap = gap;
|
||||
this.position = position;
|
||||
this.parent = parent;
|
||||
this.contents = contents;
|
||||
this.shown = false;
|
||||
}
|
||||
|
||||
show(): void {
|
||||
if (this.shown) return;
|
||||
const container = el("div").st("position", "absolute").cl("hover_text_box").fin();
|
||||
container.appendChild(this.contents);
|
||||
const adjustBoxPosition = (cont: HTMLElement, parent: HTMLElement): void => {
|
||||
const parent_x = parent.offsetLeft;
|
||||
const parent_y = parent.offsetTop;
|
||||
const style = window.getComputedStyle(cont);
|
||||
|
||||
const new_cont_x = parent_x;
|
||||
const new_cont_y = parent_y + this.gap + cont.offsetHeight + 5;
|
||||
|
||||
cont.style.setProperty("top", `${new_cont_y}px`);
|
||||
cont.style.setProperty("left", `${new_cont_x}px`);
|
||||
};
|
||||
document.body.appendChild(container);
|
||||
adjustBoxPosition(container, this.parent);
|
||||
const adjust_fn = adjustBoxPosition.bind(undefined, container, this.parent);
|
||||
window.addEventListener("resize", adjust_fn);
|
||||
this.shown = { container: container, resize_event_fn: adjust_fn };
|
||||
}
|
||||
|
||||
hide(): void {
|
||||
if (!this.shown) return;
|
||||
this.shown.container.innerHTML = "";
|
||||
this.shown.container.remove();
|
||||
window.removeEventListener("resize", this.shown.resize_event_fn);
|
||||
this.shown = false;
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ export default abstract class WindowBox {
|
|||
title_bar: HTMLElement;
|
||||
readonly title: string;
|
||||
private collapse_button: HTMLButtonElement;
|
||||
private collapsed = false;
|
||||
private is_collapsed = false;
|
||||
private fit_content = false;
|
||||
private resize?: HTMLElement;
|
||||
private resize_func?: (e: MouseEvent) => void;
|
||||
|
@ -44,7 +44,7 @@ export default abstract class WindowBox {
|
|||
} else {
|
||||
this.resize = el("div").id("resize").fin();
|
||||
this.container.appendChild(this.resize);
|
||||
this.resize_func = this.resize_move.bind(this);
|
||||
this.resize_func = this.resizeMove.bind(this);
|
||||
this.resize.addEventListener("mousedown", (e) => {
|
||||
if (this.resize_func) window.addEventListener("mousemove", this.resize_func);
|
||||
});
|
||||
|
@ -58,7 +58,7 @@ export default abstract class WindowBox {
|
|||
this.removeResizeListeners();
|
||||
if (this.resize) this.resize.style.visibility = "hidden";
|
||||
this.setHeight(this.title_bar.offsetHeight - BORDER_STROKE);
|
||||
this.collapsed = true;
|
||||
this.is_collapsed = true;
|
||||
}
|
||||
|
||||
correctHeightValue(height: number): number {
|
||||
|
@ -74,7 +74,7 @@ export default abstract class WindowBox {
|
|||
}
|
||||
|
||||
toggleCollapse(): void {
|
||||
if (this.collapsed) {
|
||||
if (this.is_collapsed) {
|
||||
this.uncollapse();
|
||||
} else {
|
||||
this.collapse();
|
||||
|
@ -91,15 +91,15 @@ export default abstract class WindowBox {
|
|||
const new_height = this.correctHeightValue(this.title_bar.offsetHeight + 200);
|
||||
this.setHeight(new_height);
|
||||
|
||||
this.collapsed = false;
|
||||
this.is_collapsed = false;
|
||||
}
|
||||
|
||||
removeResizeListeners(): void {
|
||||
if (this.resize_func) window.removeEventListener("mousemove", this.resize_func);
|
||||
}
|
||||
|
||||
resize_move(e: MouseEvent): void {
|
||||
if (this.collapsed) {
|
||||
resizeMove(e: MouseEvent): void {
|
||||
if (this.is_collapsed) {
|
||||
this.uncollapse();
|
||||
this.removeResizeListeners();
|
||||
return;
|
||||
|
@ -111,4 +111,8 @@ export default abstract class WindowBox {
|
|||
}
|
||||
this.setHeight(e.clientY - this.container.offsetTop + window.scrollY);
|
||||
}
|
||||
|
||||
isCollapsed(): boolean {
|
||||
return this.is_collapsed;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,4 +32,8 @@ export default class BankVisualizer extends WindowBox implements UiComponent {
|
|||
|
||||
this.cpu_banks[0].setAttribute("stroke", "yellow");
|
||||
}
|
||||
|
||||
softReset(): void {
|
||||
this.reset();
|
||||
}
|
||||
}
|
|
@ -67,4 +67,8 @@ export default class InstructionExplainer extends WindowBox implements UiCompone
|
|||
reset(): void {
|
||||
this.container.querySelectorAll("#expl_box").forEach((e) => e.remove());
|
||||
}
|
||||
|
||||
softReset(): void {
|
||||
this.reset();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,4 +24,8 @@ export default class Printout extends WindowBox implements UiComponent {
|
|||
reset(): void {
|
||||
this.text_box.textContent = "";
|
||||
}
|
||||
|
||||
softReset(): void {
|
||||
this.reset();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ export default class Screen extends WindowBox implements UiComponent {
|
|||
ctx: CanvasRenderingContext2D;
|
||||
scale: number;
|
||||
current_vram_bank: u2 = DEFAULT_VRAM_BANK;
|
||||
|
||||
constructor(element: HTMLElement, event: UiEventHandler, cpu_signals: UiCpuSignalHandler) {
|
||||
super(element, "TV", { collapsed: true, fit_content: true });
|
||||
this.cpu_signals = cpu_signals;
|
||||
|
@ -25,27 +26,31 @@ export default class Screen extends WindowBox implements UiComponent {
|
|||
this.screen.width = CANVAS_SIZE;
|
||||
this.screen.height = CANVAS_SIZE;
|
||||
const ctx = this.screen.getContext("2d");
|
||||
if (ctx === null) {
|
||||
throw new Error("could not load screen");
|
||||
}
|
||||
|
||||
if (ctx === null) throw new Error("could not load screen");
|
||||
|
||||
this.ctx = ctx;
|
||||
this.container.appendChild(this.screen);
|
||||
this.test_pattern();
|
||||
|
||||
this.renderTestPattern();
|
||||
}
|
||||
|
||||
private test_pattern(): void {
|
||||
private renderTestPattern(): void {
|
||||
for (let x = 0; x < 256; x++) {
|
||||
this.setPixel(x as u8, x as u8);
|
||||
}
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
const ctx = this.screen.getContext("2d");
|
||||
if (ctx === null) {
|
||||
throw new Error("todo");
|
||||
for (let i = 0; i < 256; i++) {
|
||||
this.setPixel(i as u8, 0);
|
||||
}
|
||||
}
|
||||
|
||||
softReset(): void {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
initCpuEvents(c: CpuEventHandler): void {
|
||||
c.listen(CpuEvent.MemoryChanged, ({ address, bank, value }) => {
|
||||
if (bank !== 1) return;
|
||||
|
@ -68,6 +73,8 @@ export default class Screen extends WindowBox implements UiComponent {
|
|||
const y = Math.floor(address / 16) as u4;
|
||||
const point: [number, number] = [x * this.scale, y * this.scale];
|
||||
|
||||
// TODO, come up with better color scheme.
|
||||
// Probable a lookup table
|
||||
const RED_SCALE = 255 / 2 ** 3;
|
||||
const GREEN_SCALE = 255 / 2 ** 3;
|
||||
const BLUE_SCALE = 255 / 2 ** 2;
|
||||
|
|
|
@ -34,6 +34,13 @@ class ElementInProgress<E extends HTMLElement> {
|
|||
return this;
|
||||
}
|
||||
|
||||
/** Set title */
|
||||
ti(title: string): ElementInProgress<E> {
|
||||
this.element.title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Return created element */
|
||||
fin(): E {
|
||||
return this.element;
|
||||
}
|
||||
|
|
6
todo.md
6
todo.md
|
@ -29,7 +29,9 @@ Limit size to printout text buffer
|
|||
|
||||
Improve instruction explainer. Clearly show what is an instruction and what is a parameter
|
||||
|
||||
Ui showing CPU flag(s) (Carry)
|
||||
Ui showing CPU flag(s) (Carry) and call stack
|
||||
|
||||
Share programs from encoded url
|
||||
|
||||
Responsive layout
|
||||
|
||||
|
@ -37,4 +39,4 @@ standardize names of all things
|
|||
|
||||
Documentation with standard names
|
||||
|
||||
Example Programs
|
||||
Example Programs (loaded as one of those encoded string url things)
|
||||
|
|
Loading…
Reference in a new issue