bulk commit before breaking everything
This commit is contained in:
parent
96f1104734
commit
b550a62af8
|
@ -163,17 +163,6 @@ export default class Computer {
|
||||||
this.events.dispatch(CpuEvent.SetVramBank, { bank });
|
this.events.dispatch(CpuEvent.SetVramBank, { bank });
|
||||||
}
|
}
|
||||||
|
|
||||||
reset(): void {
|
|
||||||
this.events.dispatch(CpuEvent.Reset);
|
|
||||||
this.banks = initBanks();
|
|
||||||
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 {
|
initEvents(ui: UiCpuSignalHandler): void {
|
||||||
ui.listen(UiCpuSignal.RequestCpuCycle, (cycle_count) => {
|
ui.listen(UiCpuSignal.RequestCpuCycle, (cycle_count) => {
|
||||||
for (let i = 0; i < cycle_count; i++) this.cycle();
|
for (let i = 0; i < cycle_count; i++) this.cycle();
|
||||||
|
@ -185,6 +174,31 @@ export default class Computer {
|
||||||
ui.listen(UiCpuSignal.RequestProgramCounterChange, ({ address }) => {
|
ui.listen(UiCpuSignal.RequestProgramCounterChange, ({ address }) => {
|
||||||
this.setProgramCounter(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 {
|
loadMemory(program: Array<u8>): void {
|
||||||
|
|
|
@ -21,6 +21,7 @@ export enum CpuEvent {
|
||||||
Cycle,
|
Cycle,
|
||||||
Print,
|
Print,
|
||||||
Reset,
|
Reset,
|
||||||
|
SoftReset,
|
||||||
Halt,
|
Halt,
|
||||||
MemoryAccessed,
|
MemoryAccessed,
|
||||||
SwitchBank,
|
SwitchBank,
|
||||||
|
@ -28,7 +29,7 @@ export enum CpuEvent {
|
||||||
SetVramBank,
|
SetVramBank,
|
||||||
}
|
}
|
||||||
|
|
||||||
type VoidDataCpuEventList = CpuEvent.Halt | CpuEvent.Reset | CpuEvent.Cycle;
|
type VoidDataCpuEventList = CpuEvent.Halt | CpuEvent.Reset | CpuEvent.SoftReset | CpuEvent.Cycle;
|
||||||
|
|
||||||
interface CpuEventMap {
|
interface CpuEventMap {
|
||||||
[CpuEvent.MemoryChanged]: { address: u8; bank: u2; value: u8 };
|
[CpuEvent.MemoryChanged]: { address: u8; bank: u2; value: u8 };
|
||||||
|
@ -67,11 +68,12 @@ export enum UiCpuSignal {
|
||||||
RequestMemoryChange,
|
RequestMemoryChange,
|
||||||
RequestRegisterChange,
|
RequestRegisterChange,
|
||||||
RequestCpuReset,
|
RequestCpuReset,
|
||||||
|
RequestCpuSoftReset,
|
||||||
RequestMemoryDump,
|
RequestMemoryDump,
|
||||||
RequestProgramCounterChange,
|
RequestProgramCounterChange,
|
||||||
}
|
}
|
||||||
|
|
||||||
type VoidDataUiCpuSignalList = UiCpuSignal.RequestCpuReset;
|
type VoidDataUiCpuSignalList = UiCpuSignal.RequestCpuReset | UiCpuSignal.RequestCpuSoftReset;
|
||||||
|
|
||||||
interface UiCpuSignalMap {
|
interface UiCpuSignalMap {
|
||||||
[UiCpuSignal.RequestCpuCycle]: number;
|
[UiCpuSignal.RequestCpuCycle]: number;
|
||||||
|
|
|
@ -48,6 +48,7 @@ function main(): void {
|
||||||
window.ui = ui;
|
window.ui = ui;
|
||||||
|
|
||||||
// Todo, move to ui component
|
// Todo, move to ui component
|
||||||
|
// or move to documentation
|
||||||
$("ISA").textContent = generateIsa(ISA);
|
$("ISA").textContent = generateIsa(ISA);
|
||||||
|
|
||||||
let fire = false;
|
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 "memory_registers";
|
||||||
|
@use "hover_text_box";
|
||||||
@use "windows";
|
@use "windows";
|
||||||
@use "buttons";
|
@use "buttons";
|
||||||
@use "vars";
|
@use "vars";
|
||||||
|
|
|
@ -47,9 +47,8 @@ export default class UI {
|
||||||
}
|
}
|
||||||
|
|
||||||
initEvents(cpu_events: CpuEventHandler): void {
|
initEvents(cpu_events: CpuEventHandler): void {
|
||||||
cpu_events.listen(CpuEvent.Reset, () => {
|
cpu_events.listen(CpuEvent.Reset, () => this.reset());
|
||||||
this.reset();
|
cpu_events.listen(CpuEvent.SoftReset, () => this.softReset());
|
||||||
});
|
|
||||||
|
|
||||||
for (const c of this.components) if (c.initCpuEvents) c.initCpuEvents(cpu_events);
|
for (const c of this.components) if (c.initCpuEvents) c.initCpuEvents(cpu_events);
|
||||||
}
|
}
|
||||||
|
@ -57,4 +56,8 @@ export default class UI {
|
||||||
reset(): void {
|
reset(): void {
|
||||||
for (const c of this.components) if (c.reset) c.reset();
|
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 {
|
export default class BankSelector implements UiComponent {
|
||||||
container: HTMLElement;
|
container: HTMLElement;
|
||||||
events: UiEventHandler;
|
events: UiEventHandler;
|
||||||
private bank_buttons: Array<HTMLButtonElement>;
|
bank_buttons: Array<HTMLButtonElement>;
|
||||||
constructor(element: HTMLElement, events: UiEventHandler) {
|
constructor(element: HTMLElement, events: UiEventHandler) {
|
||||||
this.container = element;
|
this.container = element;
|
||||||
this.events = events;
|
this.events = events;
|
||||||
|
@ -28,8 +28,13 @@ export default class BankSelector implements UiComponent {
|
||||||
|
|
||||||
this.container.appendChild(bank_boxes);
|
this.container.appendChild(bank_boxes);
|
||||||
}
|
}
|
||||||
|
|
||||||
reset(): void {
|
reset(): void {
|
||||||
for (const b of this.bank_buttons) b.classList.remove("selected");
|
for (const b of this.bank_buttons) b.classList.remove("selected");
|
||||||
this.bank_buttons[0].classList.add("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;
|
this.cpu_signals = cpu_signals;
|
||||||
const image = el("img").at("src", "pencil.png").st("width", "20px").st("height", "20px").fin();
|
const image = el("img").at("src", "pencil.png").st("width", "20px").st("height", "20px").fin();
|
||||||
this.container.classList.add("editor_toggle");
|
this.container.classList.add("editor_toggle");
|
||||||
this.container.addEventListener("click", () => this.edit_toggle());
|
this.container.addEventListener("click", () => this.editToggle());
|
||||||
this.container.appendChild(image);
|
this.container.appendChild(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
reset(): void {
|
disable(): void {
|
||||||
const is_on = this.container.classList.contains("on");
|
const is_on = this.container.classList.contains("on");
|
||||||
if (is_on) {
|
if (is_on) {
|
||||||
this.edit_toggle();
|
this.editToggle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
edit_toggle(): void {
|
editToggle(): void {
|
||||||
const is_on = this.container.classList.contains("on");
|
const is_on = this.container.classList.contains("on");
|
||||||
if (is_on) {
|
if (is_on) {
|
||||||
this.container.classList.remove("on");
|
this.container.classList.remove("on");
|
||||||
|
@ -35,7 +35,14 @@ export default class EditButton implements UiComponent {
|
||||||
$("root").classList.add("editor");
|
$("root").classList.add("editor");
|
||||||
this.container.classList.add("on");
|
this.container.classList.add("on");
|
||||||
this.container.classList.remove("off");
|
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 {
|
export default class frequencyIndicator implements UiComponent {
|
||||||
container: HTMLElement;
|
container: HTMLElement;
|
||||||
private running: number | null = null;
|
running: number | null = null;
|
||||||
private count: number = 0;
|
count: number = 0;
|
||||||
private last_value: number = 0;
|
last_value: number = 0;
|
||||||
private last_time: number = 0;
|
last_time: number = 0;
|
||||||
events: UiEventHandler;
|
events: UiEventHandler;
|
||||||
constructor(element: HTMLElement, events: UiEventHandler) {
|
constructor(element: HTMLElement, events: UiEventHandler) {
|
||||||
this.container = element;
|
this.container = element;
|
||||||
|
@ -19,16 +19,16 @@ export default class frequencyIndicator implements UiComponent {
|
||||||
if (this.running !== null) {
|
if (this.running !== null) {
|
||||||
throw new Error("Tried starting frequencyIndicator twice!");
|
throw new Error("Tried starting frequencyIndicator twice!");
|
||||||
}
|
}
|
||||||
setInterval(this.update_indicator.bind(this), 1000);
|
window.setInterval(this.updateIndicator.bind(this), 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
stop(): void {
|
stop(): void {
|
||||||
if (this.running === null) return;
|
if (this.running === null) return;
|
||||||
clearInterval(this.running);
|
window.clearInterval(this.running);
|
||||||
this.running = null;
|
this.running = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
update_indicator(): void {
|
updateIndicator(): void {
|
||||||
const new_time = performance.now();
|
const new_time = performance.now();
|
||||||
const dt = (new_time - this.last_time) / 1000 || 1;
|
const dt = (new_time - this.last_time) / 1000 || 1;
|
||||||
const value = Math.round(this.count / dt);
|
const value = Math.round(this.count / dt);
|
||||||
|
@ -44,15 +44,21 @@ export default class frequencyIndicator implements UiComponent {
|
||||||
this.count = 0;
|
this.count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
clock_cycle(): void {
|
clockCycle(): void {
|
||||||
this.count += 1;
|
this.count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
reset(): void {
|
reset(): void {
|
||||||
this.stop();
|
this.stop();
|
||||||
this.count = 0;
|
this.count = 0;
|
||||||
this.last_value = 0;
|
this.last_value = 0;
|
||||||
this.start();
|
this.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
softReset(): void {
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
|
||||||
initCpuEvents(c: CpuEventHandler): void {
|
initCpuEvents(c: CpuEventHandler): void {
|
||||||
c.listen(CpuEvent.Cycle, () => {
|
c.listen(CpuEvent.Cycle, () => {
|
||||||
this.count += 1;
|
this.count += 1;
|
||||||
|
|
|
@ -53,6 +53,7 @@ export default class MemoryView implements UiComponent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.events.listen(UiEvent.ChangeViewBank, ({ bank }) => this.setBank(bank));
|
this.events.listen(UiEvent.ChangeViewBank, ({ bank }) => this.setBank(bank));
|
||||||
|
this.setProgramCounter(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
get program(): CelledViewer {
|
get program(): CelledViewer {
|
||||||
|
@ -76,6 +77,12 @@ export default class MemoryView implements UiComponent {
|
||||||
this.setProgramCounter(0);
|
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 {
|
initCpuEvents(c: CpuEventHandler): void {
|
||||||
c.listen(CpuEvent.MemoryAccessed, ({ address, bank, value }) => {
|
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?.address !== address || this.last_accessed_cell?.bank !== bank) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
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";
|
||||||
|
import HoverTextBox from "../hoverTextBox";
|
||||||
|
|
||||||
const MAX_SLIDER = 1000;
|
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.EditOn, () => this.disable());
|
||||||
this.events.listen(UiEvent.EditOff, () => this.enable());
|
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 {
|
disable(): void {
|
||||||
this.stop();
|
this.stop();
|
||||||
this.start_button.setAttribute("disabled", "true");
|
this.start_button.setAttribute("disabled", "true");
|
||||||
this.step_button.setAttribute("disabled", "true");
|
this.step_button.setAttribute("disabled", "true");
|
||||||
|
|
||||||
this.range.setAttribute("disabled", "true");
|
this.range.setAttribute("disabled", "true");
|
||||||
}
|
}
|
||||||
|
|
||||||
enable(): void {
|
enable(): void {
|
||||||
this.start_button.removeAttribute("disabled");
|
this.start_button.removeAttribute("disabled");
|
||||||
this.step_button.removeAttribute("disabled");
|
this.step_button.removeAttribute("disabled");
|
||||||
|
|
||||||
this.range.removeAttribute("disabled");
|
this.range.removeAttribute("disabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,6 +81,7 @@ export default class pausePlay implements UiComponent {
|
||||||
};
|
};
|
||||||
loop();
|
loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
private step(): void {
|
private step(): void {
|
||||||
if (this.on) {
|
if (this.on) {
|
||||||
this.stop();
|
this.stop();
|
||||||
|
@ -98,4 +104,8 @@ export default class pausePlay implements UiComponent {
|
||||||
this.stop();
|
this.stop();
|
||||||
this.enable();
|
this.enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
softReset(): void {
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,7 @@ export default class RegisterView extends CelledViewer 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) {
|
||||||
super(8, 1, element, (address: u8, value: u8) => {
|
super(8, 1, element, (a, v) => this.onEdit(a, v));
|
||||||
if (!isU3(address)) throw new Error("unreachable");
|
|
||||||
this.cpu_signals.dispatch(UiCpuSignal.RequestRegisterChange, { register_no: address as u3, value });
|
|
||||||
});
|
|
||||||
this.events = events;
|
this.events = events;
|
||||||
this.cpu_signals = cpu_signals;
|
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 {
|
initCpuEvents(c: CpuEventHandler): void {
|
||||||
c.listen(CpuEvent.RegisterChanged, ({ register_no, value }) => this.setCellValue(register_no, value));
|
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 { el } from "../../etc";
|
||||||
import { UiEventHandler, UiCpuSignalHandler, UiEvent, UiCpuSignal } from "../../events";
|
import { UiEventHandler, UiCpuSignalHandler, UiCpuSignal } from "../../events";
|
||||||
import UiComponent from "../uiComponent";
|
import UiComponent from "../uiComponent";
|
||||||
|
|
||||||
export default class ResetButtons implements UiComponent {
|
export default class ResetButtons implements UiComponent {
|
||||||
|
@ -10,17 +10,22 @@ export default class ResetButtons implements UiComponent {
|
||||||
this.container = element;
|
this.container = element;
|
||||||
this.events = events;
|
this.events = events;
|
||||||
this.cpu_signals = cpu_signals;
|
this.cpu_signals = cpu_signals;
|
||||||
const reset_button = el("button").cl("nostyle").tx("R").fin();
|
const reset_button = el("button").cl("nostyle").ti("Reset State").tx("⟳").fin();
|
||||||
const trash_button = el("button").cl("nostyle").tx("T").fin();
|
const trash_button = el("button").cl("nostyle").ti("Delete Code").tx("🗑").fin();
|
||||||
|
|
||||||
reset_button.addEventListener("click", () => this.resetClicked());
|
reset_button.addEventListener("click", () => this.resetClicked());
|
||||||
trash_button.addEventListener("click", () => this.trashClicked());
|
trash_button.addEventListener("click", () => this.trashClicked());
|
||||||
this.container.append(reset_button, trash_button);
|
this.container.append(reset_button, trash_button);
|
||||||
}
|
}
|
||||||
|
|
||||||
resetClicked(): void {}
|
resetClicked(): void {
|
||||||
|
this.cpu_signals.dispatch(UiCpuSignal.RequestCpuSoftReset);
|
||||||
|
}
|
||||||
|
|
||||||
trashClicked(): void {
|
trashClicked(): void {
|
||||||
|
const a = confirm("Clear all code? Irreversible");
|
||||||
|
if (a) {
|
||||||
this.cpu_signals.dispatch(UiCpuSignal.RequestCpuReset);
|
this.cpu_signals.dispatch(UiCpuSignal.RequestCpuReset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { el } from "../../etc";
|
import { el } from "../../etc";
|
||||||
import { UiEventHandler, UiCpuSignalHandler, UiCpuSignal } from "../../events";
|
import { UiEventHandler, UiCpuSignalHandler, UiCpuSignal } from "../../events";
|
||||||
import { u2, u8, m256, isU2 } from "../../num";
|
import { u8, m256, isU2 } from "../../num";
|
||||||
import UiComponent from "../uiComponent";
|
import UiComponent from "../uiComponent";
|
||||||
|
|
||||||
export default class SaveLoad implements UiComponent {
|
export default class SaveLoad implements UiComponent {
|
||||||
|
@ -14,16 +14,16 @@ export default class SaveLoad implements UiComponent {
|
||||||
this.events = events;
|
this.events = events;
|
||||||
this.cpu_signals = cpu_signals;
|
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")
|
this.binary_upload = el("input")
|
||||||
.id("binary_upload")
|
.id("binary_upload")
|
||||||
.at("type", "file")
|
.at("type", "file")
|
||||||
.at("name", "binary_upload")
|
.at("name", "binary_upload")
|
||||||
.st("display", "none")
|
.st("display", "none")
|
||||||
.fin();
|
.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.save_button.addEventListener("click", () => {
|
||||||
this.download();
|
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;
|
title_bar: HTMLElement;
|
||||||
readonly title: string;
|
readonly title: string;
|
||||||
private collapse_button: HTMLButtonElement;
|
private collapse_button: HTMLButtonElement;
|
||||||
private collapsed = false;
|
private is_collapsed = false;
|
||||||
private fit_content = false;
|
private fit_content = false;
|
||||||
private resize?: HTMLElement;
|
private resize?: HTMLElement;
|
||||||
private resize_func?: (e: MouseEvent) => void;
|
private resize_func?: (e: MouseEvent) => void;
|
||||||
|
@ -44,7 +44,7 @@ export default abstract class WindowBox {
|
||||||
} else {
|
} else {
|
||||||
this.resize = el("div").id("resize").fin();
|
this.resize = el("div").id("resize").fin();
|
||||||
this.container.appendChild(this.resize);
|
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) => {
|
this.resize.addEventListener("mousedown", (e) => {
|
||||||
if (this.resize_func) window.addEventListener("mousemove", this.resize_func);
|
if (this.resize_func) window.addEventListener("mousemove", this.resize_func);
|
||||||
});
|
});
|
||||||
|
@ -58,7 +58,7 @@ export default abstract class WindowBox {
|
||||||
this.removeResizeListeners();
|
this.removeResizeListeners();
|
||||||
if (this.resize) this.resize.style.visibility = "hidden";
|
if (this.resize) this.resize.style.visibility = "hidden";
|
||||||
this.setHeight(this.title_bar.offsetHeight - BORDER_STROKE);
|
this.setHeight(this.title_bar.offsetHeight - BORDER_STROKE);
|
||||||
this.collapsed = true;
|
this.is_collapsed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
correctHeightValue(height: number): number {
|
correctHeightValue(height: number): number {
|
||||||
|
@ -74,7 +74,7 @@ export default abstract class WindowBox {
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleCollapse(): void {
|
toggleCollapse(): void {
|
||||||
if (this.collapsed) {
|
if (this.is_collapsed) {
|
||||||
this.uncollapse();
|
this.uncollapse();
|
||||||
} else {
|
} else {
|
||||||
this.collapse();
|
this.collapse();
|
||||||
|
@ -91,15 +91,15 @@ export default abstract class WindowBox {
|
||||||
const new_height = this.correctHeightValue(this.title_bar.offsetHeight + 200);
|
const new_height = this.correctHeightValue(this.title_bar.offsetHeight + 200);
|
||||||
this.setHeight(new_height);
|
this.setHeight(new_height);
|
||||||
|
|
||||||
this.collapsed = false;
|
this.is_collapsed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
removeResizeListeners(): void {
|
removeResizeListeners(): void {
|
||||||
if (this.resize_func) window.removeEventListener("mousemove", this.resize_func);
|
if (this.resize_func) window.removeEventListener("mousemove", this.resize_func);
|
||||||
}
|
}
|
||||||
|
|
||||||
resize_move(e: MouseEvent): void {
|
resizeMove(e: MouseEvent): void {
|
||||||
if (this.collapsed) {
|
if (this.is_collapsed) {
|
||||||
this.uncollapse();
|
this.uncollapse();
|
||||||
this.removeResizeListeners();
|
this.removeResizeListeners();
|
||||||
return;
|
return;
|
||||||
|
@ -111,4 +111,8 @@ export default abstract class WindowBox {
|
||||||
}
|
}
|
||||||
this.setHeight(e.clientY - this.container.offsetTop + window.scrollY);
|
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");
|
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 {
|
reset(): void {
|
||||||
this.container.querySelectorAll("#expl_box").forEach((e) => e.remove());
|
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 {
|
reset(): void {
|
||||||
this.text_box.textContent = "";
|
this.text_box.textContent = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
softReset(): void {
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ export default class Screen extends WindowBox implements UiComponent {
|
||||||
ctx: CanvasRenderingContext2D;
|
ctx: CanvasRenderingContext2D;
|
||||||
scale: number;
|
scale: number;
|
||||||
current_vram_bank: u2 = DEFAULT_VRAM_BANK;
|
current_vram_bank: u2 = DEFAULT_VRAM_BANK;
|
||||||
|
|
||||||
constructor(element: HTMLElement, event: UiEventHandler, cpu_signals: UiCpuSignalHandler) {
|
constructor(element: HTMLElement, event: UiEventHandler, cpu_signals: UiCpuSignalHandler) {
|
||||||
super(element, "TV", { collapsed: true, fit_content: true });
|
super(element, "TV", { collapsed: true, fit_content: true });
|
||||||
this.cpu_signals = cpu_signals;
|
this.cpu_signals = cpu_signals;
|
||||||
|
@ -25,27 +26,31 @@ export default class Screen extends WindowBox implements UiComponent {
|
||||||
this.screen.width = CANVAS_SIZE;
|
this.screen.width = CANVAS_SIZE;
|
||||||
this.screen.height = CANVAS_SIZE;
|
this.screen.height = CANVAS_SIZE;
|
||||||
const ctx = this.screen.getContext("2d");
|
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.ctx = ctx;
|
||||||
this.container.appendChild(this.screen);
|
this.container.appendChild(this.screen);
|
||||||
this.test_pattern();
|
|
||||||
|
this.renderTestPattern();
|
||||||
}
|
}
|
||||||
|
|
||||||
private test_pattern(): void {
|
private renderTestPattern(): void {
|
||||||
for (let x = 0; x < 256; x++) {
|
for (let x = 0; x < 256; x++) {
|
||||||
this.setPixel(x as u8, x as u8);
|
this.setPixel(x as u8, x as u8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reset(): void {
|
reset(): void {
|
||||||
const ctx = this.screen.getContext("2d");
|
for (let i = 0; i < 256; i++) {
|
||||||
if (ctx === null) {
|
this.setPixel(i as u8, 0);
|
||||||
throw new Error("todo");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
softReset(): void {
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
|
||||||
initCpuEvents(c: CpuEventHandler): void {
|
initCpuEvents(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;
|
||||||
|
@ -68,6 +73,8 @@ export default class Screen extends WindowBox implements UiComponent {
|
||||||
const y = Math.floor(address / 16) as u4;
|
const y = Math.floor(address / 16) as u4;
|
||||||
const point: [number, number] = [x * this.scale, y * this.scale];
|
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 RED_SCALE = 255 / 2 ** 3;
|
||||||
const GREEN_SCALE = 255 / 2 ** 3;
|
const GREEN_SCALE = 255 / 2 ** 3;
|
||||||
const BLUE_SCALE = 255 / 2 ** 2;
|
const BLUE_SCALE = 255 / 2 ** 2;
|
||||||
|
|
|
@ -34,6 +34,12 @@ class ElementInProgress<E extends HTMLElement> {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Set title */
|
||||||
|
ti(title: string): ElementInProgress<E> {
|
||||||
|
this.element.title = title;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/** Return created element */
|
/** Return created element */
|
||||||
fin(): E {
|
fin(): E {
|
||||||
return this.element;
|
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
|
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
|
Responsive layout
|
||||||
|
|
||||||
|
@ -37,4 +39,4 @@ standardize names of all things
|
||||||
|
|
||||||
Documentation with standard names
|
Documentation with standard names
|
||||||
|
|
||||||
Example Programs
|
Example Programs (loaded as one of those encoded string url things)
|
||||||
|
|
Loading…
Reference in a new issue