final commit before major rework
This commit is contained in:
parent
b550a62af8
commit
f850b21869
|
@ -37,7 +37,7 @@
|
||||||
"quote-props" : ["warn","as-needed"],
|
"quote-props" : ["warn","as-needed"],
|
||||||
"dot-notation":"warn",
|
"dot-notation":"warn",
|
||||||
"one-var":["warn","never"],
|
"one-var":["warn","never"],
|
||||||
"no-use-before-define":"warn",
|
"no-use-before-define":"off",
|
||||||
"no-multi-assign":"warn",
|
"no-multi-assign":"warn",
|
||||||
"no-else-return":"warn",
|
"no-else-return":"warn",
|
||||||
"spaced-comment":"warn",
|
"spaced-comment":"warn",
|
||||||
|
|
|
@ -4,11 +4,18 @@ 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";
|
import { DEFAULT_VRAM_BANK } from "./constants";
|
||||||
|
|
||||||
|
interface ParsedParameter {
|
||||||
|
pos: u8;
|
||||||
|
code: u8;
|
||||||
|
valid: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export type TempInstrState = {
|
export type TempInstrState = {
|
||||||
pos: u8;
|
pos: u8;
|
||||||
params_found: number;
|
params_found: number;
|
||||||
instr: Instruction;
|
instr: Instruction;
|
||||||
params: Array<u8>;
|
valid: boolean;
|
||||||
|
params: Array<ParsedParameter>;
|
||||||
};
|
};
|
||||||
|
|
||||||
function initBanks(): [Uint8Array, Uint8Array, Uint8Array, Uint8Array] {
|
function initBanks(): [Uint8Array, Uint8Array, Uint8Array, Uint8Array] {
|
||||||
|
@ -22,7 +29,7 @@ function initBanks(): [Uint8Array, Uint8Array, Uint8Array, Uint8Array] {
|
||||||
export default class Computer {
|
export default class Computer {
|
||||||
private banks: [Uint8Array, Uint8Array, Uint8Array, Uint8Array] = initBanks();
|
private banks: [Uint8Array, Uint8Array, Uint8Array, Uint8Array] = initBanks();
|
||||||
private registers: Uint8Array = new Uint8Array(8);
|
private registers: Uint8Array = new Uint8Array(8);
|
||||||
private call_stack: Array<u8> = [];
|
// private call_stack: Array<u8> = [];
|
||||||
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;
|
||||||
|
@ -36,7 +43,7 @@ export default class Computer {
|
||||||
if (this.current_instr === null) {
|
if (this.current_instr === null) {
|
||||||
const parsed_instruction = ISA.getInstruction(current_byte);
|
const parsed_instruction = ISA.getInstruction(current_byte);
|
||||||
if (parsed_instruction === null) {
|
if (parsed_instruction === null) {
|
||||||
this.events.dispatch(CpuEvent.InvalidParsed, {
|
this.events.dispatch(CpuEvent.InvalidInstructionParsed, {
|
||||||
pos: this.program_counter,
|
pos: this.program_counter,
|
||||||
code: current_byte,
|
code: current_byte,
|
||||||
});
|
});
|
||||||
|
@ -45,35 +52,55 @@ export default class Computer {
|
||||||
this.events.dispatch(CpuEvent.Cycle);
|
this.events.dispatch(CpuEvent.Cycle);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.current_instr = {
|
this.current_instr = {
|
||||||
pos: this.program_counter,
|
pos: this.program_counter,
|
||||||
instr: parsed_instruction,
|
instr: parsed_instruction,
|
||||||
params_found: 0,
|
params_found: 0,
|
||||||
params: new Array<u8>(parsed_instruction.params.length),
|
valid: true,
|
||||||
|
params: new Array<ParsedParameter>(parsed_instruction.params.length),
|
||||||
};
|
};
|
||||||
this.events.dispatch(CpuEvent.InstructionParsed, {
|
|
||||||
|
this.events.dispatch(CpuEvent.InstructionParseBegin, {
|
||||||
pos: this.program_counter,
|
pos: this.program_counter,
|
||||||
instr: parsed_instruction,
|
instr: parsed_instruction,
|
||||||
code: current_byte,
|
code: current_byte,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
if (this.current_instr.pos === this.program_counter && this.current_instr.params.length > 0) {
|
|
||||||
this.stepForward();
|
this.stepForward();
|
||||||
this.events.dispatch(CpuEvent.Cycle);
|
this.events.dispatch(CpuEvent.Cycle);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if (this.current_instr.pos === this.program_counter && this.current_instr.params.length > 0) {
|
||||||
|
// this.stepForward();
|
||||||
|
// this.events.dispatch(CpuEvent.Cycle);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
if (this.current_instr.params.length !== this.current_instr.params_found) {
|
if (this.current_instr.params.length !== this.current_instr.params_found) {
|
||||||
const b = this.current_instr.instr.params[this.current_instr.params_found];
|
const b = this.current_instr.instr.params[this.current_instr.params_found];
|
||||||
|
|
||||||
|
const valid = b.validate(current_byte);
|
||||||
|
if (valid) {
|
||||||
this.events.dispatch(CpuEvent.ParameterParsed, {
|
this.events.dispatch(CpuEvent.ParameterParsed, {
|
||||||
param: b,
|
param: b,
|
||||||
pos: this.program_counter,
|
pos: this.program_counter,
|
||||||
code: current_byte,
|
code: current_byte,
|
||||||
});
|
});
|
||||||
this.current_instr.params[this.current_instr.params_found] = current_byte;
|
} else {
|
||||||
|
this.events.dispatch(CpuEvent.InvalidParameterParsed, {
|
||||||
|
param: b,
|
||||||
|
pos: this.program_counter,
|
||||||
|
code: current_byte,
|
||||||
|
});
|
||||||
|
this.current_instr.valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const param = { pos: this.program_counter, code: current_byte, valid };
|
||||||
|
|
||||||
|
this.current_instr.params[this.current_instr.params_found] = param;
|
||||||
this.current_instr.params_found += 1;
|
this.current_instr.params_found += 1;
|
||||||
|
|
||||||
if (this.current_instr.params.length !== this.current_instr.params_found) {
|
if (this.current_instr.params.length !== this.current_instr.params_found) {
|
||||||
this.stepForward();
|
this.stepForward();
|
||||||
this.events.dispatch(CpuEvent.Cycle);
|
this.events.dispatch(CpuEvent.Cycle);
|
||||||
|
@ -87,8 +114,12 @@ export default class Computer {
|
||||||
},
|
},
|
||||||
dispatch: this.events.dispatch.bind(this.events),
|
dispatch: this.events.dispatch.bind(this.events),
|
||||||
};
|
};
|
||||||
this.current_instr.instr.execute(this, this.current_instr.params, execution_post_action_state);
|
if (this.current_instr.valid) {
|
||||||
|
const params: Array<u8> = this.current_instr.params.map((p) => p.code);
|
||||||
|
this.current_instr.instr.execute(this, params, execution_post_action_state);
|
||||||
this.events.dispatch(CpuEvent.InstructionExecuted, { instr: this.current_instr.instr });
|
this.events.dispatch(CpuEvent.InstructionExecuted, { instr: this.current_instr.instr });
|
||||||
|
}
|
||||||
|
this.events.dispatch(CpuEvent.InstructionParseEnd);
|
||||||
this.current_instr = null;
|
this.current_instr = null;
|
||||||
|
|
||||||
if (execution_post_action_state.should_step) {
|
if (execution_post_action_state.should_step) {
|
||||||
|
@ -134,15 +165,15 @@ export default class Computer {
|
||||||
this.program_counter = new_value;
|
this.program_counter = new_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
pushCallStack(address: u8): boolean {
|
// pushCallStack(address: u8): boolean {
|
||||||
if (this.call_stack.length >= 8) return false;
|
// if (this.call_stack.length >= 8) return false;
|
||||||
this.call_stack.push(address);
|
// this.call_stack.push(address);
|
||||||
return true;
|
// return true;
|
||||||
}
|
// }
|
||||||
|
|
||||||
popCallStack(): u8 | null {
|
// popCallStack(): u8 | null {
|
||||||
return this.call_stack.pop() ?? null;
|
// return this.call_stack.pop() ?? null;
|
||||||
}
|
// }
|
||||||
|
|
||||||
setBank(bank: u2): void {
|
setBank(bank: u2): void {
|
||||||
this.events.dispatch(CpuEvent.SwitchBank, { bank: bank });
|
this.events.dispatch(CpuEvent.SwitchBank, { bank: bank });
|
||||||
|
@ -170,17 +201,17 @@ export default 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, (callback) => callback(this.dumpMemory()));
|
ui.listen(UiCpuSignal.RequestMemoryDump, (callback) => callback(this.dumpMemory()));
|
||||||
ui.listen(UiCpuSignal.RequestCpuReset, () => this.reset());
|
|
||||||
ui.listen(UiCpuSignal.RequestProgramCounterChange, ({ address }) => {
|
ui.listen(UiCpuSignal.RequestProgramCounterChange, ({ address }) => {
|
||||||
this.setProgramCounter(address);
|
this.setProgramCounter(address);
|
||||||
});
|
});
|
||||||
|
ui.listen(UiCpuSignal.RequestCpuReset, () => this.reset());
|
||||||
ui.listen(UiCpuSignal.RequestCpuSoftReset, () => this.softReset());
|
ui.listen(UiCpuSignal.RequestCpuSoftReset, () => this.softReset());
|
||||||
}
|
}
|
||||||
|
|
||||||
softReset(): void {
|
softReset(): void {
|
||||||
this.events.dispatch(CpuEvent.SoftReset);
|
this.events.dispatch(CpuEvent.SoftReset);
|
||||||
for (let i = 0; i < 8; i++) this.setRegister(i as u3, 0);
|
for (let i = 0; i < 8; i++) this.setRegister(i as u3, 0);
|
||||||
while (this.popCallStack() !== null) 0;
|
// while (this.popCallStack() !== null) 0;
|
||||||
this.setVramBank(DEFAULT_VRAM_BANK);
|
this.setVramBank(DEFAULT_VRAM_BANK);
|
||||||
this.setCarry(false);
|
this.setCarry(false);
|
||||||
this.current_instr = null;
|
this.current_instr = null;
|
||||||
|
@ -193,7 +224,7 @@ export default class Computer {
|
||||||
this.banks = initBanks();
|
this.banks = initBanks();
|
||||||
// Soft reset
|
// Soft reset
|
||||||
for (let i = 0; i < 8; i++) this.setRegister(i as u3, 0);
|
for (let i = 0; i < 8; i++) this.setRegister(i as u3, 0);
|
||||||
while (this.popCallStack() !== null) 0;
|
// while (this.popCallStack() !== null) 0;
|
||||||
this.setVramBank(DEFAULT_VRAM_BANK);
|
this.setVramBank(DEFAULT_VRAM_BANK);
|
||||||
this.setCarry(false);
|
this.setCarry(false);
|
||||||
this.current_instr = null;
|
this.current_instr = null;
|
||||||
|
|
|
@ -14,9 +14,11 @@ export enum CpuEvent {
|
||||||
MemoryChanged,
|
MemoryChanged,
|
||||||
RegisterChanged,
|
RegisterChanged,
|
||||||
ProgramCounterChanged,
|
ProgramCounterChanged,
|
||||||
InstructionParsed,
|
InstructionParseBegin,
|
||||||
|
InstructionParseEnd,
|
||||||
ParameterParsed,
|
ParameterParsed,
|
||||||
InvalidParsed,
|
InvalidParameterParsed,
|
||||||
|
InvalidInstructionParsed,
|
||||||
InstructionExecuted,
|
InstructionExecuted,
|
||||||
Cycle,
|
Cycle,
|
||||||
Print,
|
Print,
|
||||||
|
@ -29,16 +31,22 @@ export enum CpuEvent {
|
||||||
SetVramBank,
|
SetVramBank,
|
||||||
}
|
}
|
||||||
|
|
||||||
type VoidDataCpuEventList = CpuEvent.Halt | CpuEvent.Reset | CpuEvent.SoftReset | CpuEvent.Cycle;
|
type VoidDataCpuEventList =
|
||||||
|
| CpuEvent.Halt
|
||||||
|
| CpuEvent.Reset
|
||||||
|
| CpuEvent.SoftReset
|
||||||
|
| CpuEvent.Cycle
|
||||||
|
| CpuEvent.InstructionParseEnd;
|
||||||
|
|
||||||
interface CpuEventMap {
|
interface CpuEventMap {
|
||||||
[CpuEvent.MemoryChanged]: { address: u8; bank: u2; value: u8 };
|
[CpuEvent.MemoryChanged]: { address: u8; bank: u2; value: u8 };
|
||||||
[CpuEvent.MemoryAccessed]: { address: u8; bank: u2; value: u8 };
|
[CpuEvent.MemoryAccessed]: { address: u8; bank: u2; value: u8 };
|
||||||
[CpuEvent.RegisterChanged]: { register_no: u3; value: u8 };
|
[CpuEvent.RegisterChanged]: { register_no: u3; value: u8 };
|
||||||
[CpuEvent.ProgramCounterChanged]: { counter: u8 };
|
[CpuEvent.ProgramCounterChanged]: { counter: u8 };
|
||||||
[CpuEvent.InstructionParsed]: { pos: u8; code: u8; instr: Instruction };
|
[CpuEvent.InstructionParseBegin]: { pos: u8; code: u8; instr: Instruction };
|
||||||
[CpuEvent.ParameterParsed]: { pos: u8; code: u8; param: ParameterType };
|
[CpuEvent.ParameterParsed]: { pos: u8; code: u8; param: ParameterType };
|
||||||
[CpuEvent.InvalidParsed]: { pos: u8; code: u8 };
|
[CpuEvent.InvalidParameterParsed]: { pos: u8; code: u8; param: ParameterType };
|
||||||
|
[CpuEvent.InvalidInstructionParsed]: { 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.SetVramBank]: { bank: u2 };
|
||||||
|
|
|
@ -11,24 +11,33 @@
|
||||||
This computer requires JavaScript. Your browser either doesn't support it, or you have it disabled.
|
This computer requires JavaScript. Your browser either doesn't support it, or you have it disabled.
|
||||||
</noscript>
|
</noscript>
|
||||||
<main>
|
<main>
|
||||||
|
<div id="left_column">
|
||||||
|
<div id="program_meta">
|
||||||
|
<div id="title_div">
|
||||||
|
<input type="text" name="" id="" value="New Program" />
|
||||||
|
</div>
|
||||||
|
<div id="btn_div">
|
||||||
|
<span id="save_load_buttons"></span>
|
||||||
|
<button>🔗</button>
|
||||||
|
<button id="trash_button">🗑</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div id="grid">
|
<div id="grid">
|
||||||
<div id="title">VIRTUAL 8-BIT COMPUTER</div>
|
<div id="title">VIRTUAL 8-BIT COMPUTER</div>
|
||||||
<div id="registers"></div>
|
<div id="registers"></div>
|
||||||
<div id="labelcontainer">
|
<!-- <div id="reg_label">↯REGISTERS</div> -->
|
||||||
<div id="registers_label">↯REGISTERS</div>
|
<!-- <div id="mem_label">MEMORY↯</div> -->
|
||||||
<div id="memory_label">MEMORY↯</div>
|
|
||||||
</div>
|
|
||||||
<div id="memory"></div>
|
<div id="memory"></div>
|
||||||
|
|
||||||
<div id="controls_bar">
|
<div id="controls_bar">
|
||||||
<span id="controls_buttons"></span>
|
<span id="controls_buttons"></span>
|
||||||
<span id="save_load_buttons"></span>
|
|
||||||
<button type="button" id="edit_button"></button>
|
<button type="button" id="edit_button"></button>
|
||||||
|
<div id="reset_button"></div>
|
||||||
</div>
|
</div>
|
||||||
<span id="cycles"></span>
|
<span id="cycles"></span>
|
||||||
<div id="memory_bank_view"></div>
|
<div id="memory_bank_view"></div>
|
||||||
|
</div>
|
||||||
<div id="reset_buttons"></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="window_box">
|
<div id="window_box">
|
||||||
|
@ -63,6 +72,20 @@
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<article>
|
||||||
|
<details id="isa_container">
|
||||||
|
<summary>Instruction Set</summary>
|
||||||
<pre id="ISA"></pre>
|
<pre id="ISA"></pre>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<h1>Virtual 8-Bit Computer</h1>
|
||||||
|
<p>
|
||||||
|
Above is a virtual virtual virtual Lorem ipsum dolor sit amet consectetur adipisicing elit. Provident architecto
|
||||||
|
voluptas repudiandae eligendi mollitia necessitatibus tempore autem ea pariatur ex quae eum itaque ducimus,
|
||||||
|
velit nam voluptatem deserunt ad nesciunt.
|
||||||
|
</p>
|
||||||
|
<h2>How it works</h2>
|
||||||
|
<p></p>
|
||||||
|
</article>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -7,7 +7,7 @@ import Computer from "./computer";
|
||||||
import UI from "./ui";
|
import UI from "./ui";
|
||||||
import { $ } from "./etc";
|
import { $ } from "./etc";
|
||||||
import { ISA } from "./instructionSet";
|
import { ISA } from "./instructionSet";
|
||||||
import { generateIsa } from "./isaGenerator";
|
import { generateIsaTable } from "./isaGenerator";
|
||||||
import { u8 } from "./num";
|
import { u8 } from "./num";
|
||||||
|
|
||||||
import "./style/style.scss";
|
import "./style/style.scss";
|
||||||
|
@ -49,7 +49,7 @@ function main(): void {
|
||||||
|
|
||||||
// Todo, move to ui component
|
// Todo, move to ui component
|
||||||
// or move to documentation
|
// or move to documentation
|
||||||
$("ISA").textContent = generateIsa(ISA);
|
$("ISA").replaceWith(generateIsaTable(ISA));
|
||||||
|
|
||||||
let fire = false;
|
let fire = false;
|
||||||
window.firehose = (): void => {
|
window.firehose = (): void => {
|
||||||
|
|
|
@ -10,7 +10,10 @@ import { isU2, isU3, m256, u2, u3, u8 } from "./num";
|
||||||
export enum ParamType {
|
export enum ParamType {
|
||||||
Const,
|
Const,
|
||||||
Register,
|
Register,
|
||||||
Memory,
|
ConstMemory,
|
||||||
|
Bank,
|
||||||
|
// A register which holds a memory address
|
||||||
|
RegisterAddress,
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class ParameterType {
|
export abstract class ParameterType {
|
||||||
|
@ -20,6 +23,9 @@ export abstract class ParameterType {
|
||||||
this.desc = description;
|
this.desc = description;
|
||||||
this.type = p_type;
|
this.type = p_type;
|
||||||
}
|
}
|
||||||
|
validate(n: number): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConstParam extends ParameterType {
|
class ConstParam extends ParameterType {
|
||||||
|
@ -31,10 +37,28 @@ class RegisParam extends ParameterType {
|
||||||
constructor(d: string) {
|
constructor(d: string) {
|
||||||
super(d, ParamType.Register);
|
super(d, ParamType.Register);
|
||||||
}
|
}
|
||||||
|
validate(n: number): boolean {
|
||||||
|
return isU3(n);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
class MemorParam extends ParameterType {
|
|
||||||
|
class BankParam extends ParameterType {
|
||||||
constructor(d: string) {
|
constructor(d: string) {
|
||||||
super(d, ParamType.Memory);
|
super(d, ParamType.Bank);
|
||||||
|
}
|
||||||
|
validate(n: number): boolean {
|
||||||
|
return isU2(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConstMemorParam extends ParameterType {
|
||||||
|
constructor(d: string) {
|
||||||
|
super(d, ParamType.ConstMemory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class RegisAddrParam extends ParameterType {
|
||||||
|
constructor(d: string) {
|
||||||
|
super(d, ParamType.RegisterAddress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,12 +69,13 @@ interface GenericComputer {
|
||||||
getProgramCounter: () => u8;
|
getProgramCounter: () => u8;
|
||||||
getRegister: (number: u3) => u8;
|
getRegister: (number: u3) => u8;
|
||||||
setRegister: (number: u3, value: u8) => void;
|
setRegister: (number: u3, value: u8) => void;
|
||||||
pushCallStack: (address: u8) => boolean;
|
// pushCallStack: (address: u8) => boolean;
|
||||||
popCallStack: () => u8 | null;
|
// popCallStack: () => u8 | null;
|
||||||
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;
|
setVramBank(bank: u2): void;
|
||||||
|
softReset(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AfterExecutionComputerAction {
|
interface AfterExecutionComputerAction {
|
||||||
|
@ -129,9 +154,9 @@ ISA.addCategory(category(0xf0, 0xff, "IO"));
|
||||||
|
|
||||||
// COPY
|
// COPY
|
||||||
ISA.insertInstruction(0x10, {
|
ISA.insertInstruction(0x10, {
|
||||||
name: "Copy R -> M",
|
name: "Copy CR -> CA",
|
||||||
desc: "Copy the byte in register (P1) to the memory address (P2)",
|
desc: "Copy the byte in register (P1) to the memory address (P2)",
|
||||||
params: [new RegisParam("Write the byte in this register"), new MemorParam("To this memory address")],
|
params: [new RegisParam("Write the byte in this register"), new ConstMemorParam("To this memory address")],
|
||||||
execute(c, p) {
|
execute(c, p) {
|
||||||
const [register_no, mem_address] = p;
|
const [register_no, mem_address] = p;
|
||||||
if (!isU3(register_no)) throw new Error("TODO");
|
if (!isU3(register_no)) throw new Error("TODO");
|
||||||
|
@ -140,9 +165,9 @@ ISA.insertInstruction(0x10, {
|
||||||
});
|
});
|
||||||
|
|
||||||
ISA.insertInstruction(0x11, {
|
ISA.insertInstruction(0x11, {
|
||||||
name: "Copy M -> R",
|
name: "Copy CA -> R",
|
||||||
desc: "Copy the byte in memory address (P1) to the register (P2)",
|
desc: "Copy the byte in memory address (P1) to the register (P2)",
|
||||||
params: [new MemorParam(""), new RegisParam("")],
|
params: [new ConstMemorParam(""), new RegisParam("")],
|
||||||
execute(c, p) {
|
execute(c, p) {
|
||||||
const [register_no, mem_address] = p;
|
const [register_no, mem_address] = p;
|
||||||
if (!isU3(register_no)) throw new Error("TODO");
|
if (!isU3(register_no)) throw new Error("TODO");
|
||||||
|
@ -151,9 +176,9 @@ ISA.insertInstruction(0x11, {
|
||||||
});
|
});
|
||||||
|
|
||||||
ISA.insertInstruction(0x12, {
|
ISA.insertInstruction(0x12, {
|
||||||
name: "Copy M -> M",
|
name: "Copy CM -> CA",
|
||||||
desc: "Copy the byte in memory address (P1) to memory address (P2)",
|
desc: "Copy the byte in memory address (P1) to memory address (P2)",
|
||||||
params: [new MemorParam("Copy the byte in this memory address"), new MemorParam("To this memory address")],
|
params: [new ConstMemorParam("Copy the byte in this memory address"), new ConstMemorParam("To this memory address")],
|
||||||
execute(c, p) {
|
execute(c, p) {
|
||||||
const [mem_address_1, mem_address_2] = p;
|
const [mem_address_1, mem_address_2] = p;
|
||||||
c.setMemory(mem_address_2, c.getMemory(mem_address_1));
|
c.setMemory(mem_address_2, c.getMemory(mem_address_1));
|
||||||
|
@ -172,10 +197,10 @@ ISA.insertInstruction(0x13, {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
ISA.insertInstruction(0x14, {
|
ISA.insertInstruction(0x14, {
|
||||||
name: "Load RM -> R",
|
name: "Load RA -> R",
|
||||||
desc: "Copy the byte in memory addressed by register (P1) to register (P2)",
|
desc: "Copy the byte in memory addressed by register (P1) to register (P2)",
|
||||||
params: [
|
params: [
|
||||||
new RegisParam("Copy the byte in the memory cell addressed in this register"),
|
new RegisAddrParam("Copy the byte in the memory cell addressed in this register"),
|
||||||
new RegisParam("To this register"),
|
new RegisParam("To this register"),
|
||||||
],
|
],
|
||||||
execute(c, p) {
|
execute(c, p) {
|
||||||
|
@ -186,11 +211,11 @@ ISA.insertInstruction(0x14, {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
ISA.insertInstruction(0x15, {
|
ISA.insertInstruction(0x15, {
|
||||||
name: "Save R -> RM",
|
name: "Save R -> RA",
|
||||||
desc: "Copy the byte in register (P1) to the memory cell addressed in register (P2)",
|
desc: "Copy the byte in register (P1) to the memory cell addressed in register (P2)",
|
||||||
params: [
|
params: [
|
||||||
new RegisParam("Copy the value in this register"),
|
new RegisParam("Copy the value in this register"),
|
||||||
new RegisParam("To the memory cell addressed in this register"),
|
new RegisAddrParam("To the memory cell addressed in this register"),
|
||||||
],
|
],
|
||||||
execute(c, p) {
|
execute(c, p) {
|
||||||
const [register_no_1, register_no_2] = p;
|
const [register_no_1, register_no_2] = p;
|
||||||
|
@ -213,7 +238,7 @@ ISA.insertInstruction(0x17, {
|
||||||
ISA.insertInstruction(0x18, {
|
ISA.insertInstruction(0x18, {
|
||||||
name: "Zero Memory",
|
name: "Zero Memory",
|
||||||
desc: "Set the byte in memory address (P1) to 0",
|
desc: "Set the byte in memory address (P1) to 0",
|
||||||
params: [new RegisParam("Set the value in this memory address to 0")],
|
params: [new RegisAddrParam("Set the value in this memory address to 0")],
|
||||||
execute(c, p) {
|
execute(c, p) {
|
||||||
const mem_address = p[0];
|
const mem_address = p[0];
|
||||||
c.setMemory(mem_address, 0);
|
c.setMemory(mem_address, 0);
|
||||||
|
@ -234,7 +259,7 @@ ISA.insertInstruction(0x19, {
|
||||||
ISA.insertInstruction(0x1f, {
|
ISA.insertInstruction(0x1f, {
|
||||||
name: "Set bank",
|
name: "Set bank",
|
||||||
desc: "Selects which bank of memory to write and read to",
|
desc: "Selects which bank of memory to write and read to",
|
||||||
params: [new ConstParam("Bank number")],
|
params: [new BankParam("Bank number")],
|
||||||
execute(c, p) {
|
execute(c, p) {
|
||||||
const bank_no = p[0];
|
const bank_no = p[0];
|
||||||
if (!isU2(bank_no)) {
|
if (!isU2(bank_no)) {
|
||||||
|
@ -255,9 +280,8 @@ ISA.insertInstruction(0x20, {
|
||||||
params: [new RegisParam("new instruction counter location")],
|
params: [new RegisParam("new instruction counter location")],
|
||||||
execute: (c, p, a) => {
|
execute: (c, p, a) => {
|
||||||
const register_no = p[0];
|
const register_no = p[0];
|
||||||
if (!isU3(register_no)) {
|
if (!isU3(register_no)) throw new Error("todo");
|
||||||
throw new Error("todo");
|
|
||||||
}
|
|
||||||
const new_address = c.getRegister(register_no);
|
const new_address = c.getRegister(register_no);
|
||||||
c.setProgramCounter(new_address);
|
c.setProgramCounter(new_address);
|
||||||
a.noStep();
|
a.noStep();
|
||||||
|
@ -333,33 +357,33 @@ ISA.insertInstruction(0x29, {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
ISA.insertInstruction(0x2d, {
|
// ISA.insertInstruction(0x2d, {
|
||||||
name: "Call",
|
// name: "Call",
|
||||||
desc: "",
|
// desc: "",
|
||||||
params: [new ConstParam("the subroutine at this memory address")],
|
// params: [new ConstParam("the subroutine at this memory address")],
|
||||||
execute(c, p, a) {
|
// execute(c, p, a) {
|
||||||
const current_address = c.getProgramCounter();
|
// const current_address = c.getProgramCounter();
|
||||||
const success = c.pushCallStack(current_address);
|
// const success = c.pushCallStack(current_address);
|
||||||
// TODO Handle success/failure
|
// // TODO Handle success/failure
|
||||||
|
|
||||||
const new_address = p[0];
|
// const new_address = p[0];
|
||||||
c.setProgramCounter(new_address);
|
// c.setProgramCounter(new_address);
|
||||||
|
|
||||||
a.noStep();
|
// a.noStep();
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
|
|
||||||
ISA.insertInstruction(0x2e, {
|
// ISA.insertInstruction(0x2e, {
|
||||||
name: "Return",
|
// name: "Return",
|
||||||
desc: "",
|
// desc: "",
|
||||||
params: [],
|
// params: [],
|
||||||
execute(c, p, a) {
|
// execute(c, p, a) {
|
||||||
const new_address = c.popCallStack();
|
// const new_address = c.popCallStack();
|
||||||
if (new_address === null) throw new Error("TODO handle this");
|
// if (new_address === null) throw new Error("TODO handle this");
|
||||||
c.setProgramCounter(m256(new_address + 1));
|
// c.setProgramCounter(m256(new_address + 1));
|
||||||
a.noStep();
|
// a.noStep();
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
|
|
||||||
ISA.insertInstruction(0x2c, {
|
ISA.insertInstruction(0x2c, {
|
||||||
name: "NoOp",
|
name: "NoOp",
|
||||||
|
@ -369,11 +393,12 @@ ISA.insertInstruction(0x2c, {
|
||||||
});
|
});
|
||||||
|
|
||||||
ISA.insertInstruction(0x2f, {
|
ISA.insertInstruction(0x2f, {
|
||||||
name: "Halt and Catch Fire",
|
name: "Halt",
|
||||||
desc: "Stops program execu..... Fire! FIRE EVERYWHERE!",
|
desc: "Stops CPU execution and soft resets",
|
||||||
params: [],
|
params: [],
|
||||||
execute(c, p, a) {
|
execute(c, p, a) {
|
||||||
a.dispatch(CpuEvent.Halt);
|
a.dispatch(CpuEvent.Halt);
|
||||||
|
c.softReset();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -743,7 +768,7 @@ ISA.insertInstruction(0xf1, {
|
||||||
ISA.insertInstruction(0xff, {
|
ISA.insertInstruction(0xff, {
|
||||||
name: "Set VRAM Bank",
|
name: "Set VRAM Bank",
|
||||||
desc: "Set memory bank which screen gets pixels from memory bank (P1)",
|
desc: "Set memory bank which screen gets pixels from memory bank (P1)",
|
||||||
params: [new ConstParam("memory bank to select")],
|
params: [new BankParam("memory bank to select")],
|
||||||
execute(c, p, a) {
|
execute(c, p, a) {
|
||||||
const bank_no = p[0];
|
const bank_no = p[0];
|
||||||
if (!isU2(bank_no)) throw new Error("TO2O");
|
if (!isU2(bank_no)) throw new Error("TO2O");
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
* @copyright Alexander Bass 2024
|
* @copyright Alexander Bass 2024
|
||||||
* @license GPL-3.0
|
* @license GPL-3.0
|
||||||
*/
|
*/
|
||||||
import { formatHex, inRange } from "./etc";
|
import { el, formatHex, inRange } from "./etc";
|
||||||
import { InstrCategory, Instruction, InstructionSet, ParameterType, ParamType } from "./instructionSet";
|
import { InstrCategory, Instruction, InstructionSet, ParameterType, ParamType } from "./instructionSet";
|
||||||
import { u8 } from "./num";
|
import { u8 } from "./num";
|
||||||
|
|
||||||
|
@ -13,7 +13,13 @@ function parameterDescription(params: Array<ParameterType>): string {
|
||||||
str += " ";
|
str += " ";
|
||||||
}
|
}
|
||||||
for (const p of params) {
|
for (const p of params) {
|
||||||
const p_map = { [ParamType.Const]: "C", [ParamType.Memory]: "M", [ParamType.Register]: "R" };
|
const p_map = {
|
||||||
|
[ParamType.Const]: "C",
|
||||||
|
[ParamType.ConstMemory]: "CM",
|
||||||
|
[ParamType.Register]: "R",
|
||||||
|
[ParamType.Bank]: "B",
|
||||||
|
[ParamType.RegisterAddress]: "RA",
|
||||||
|
};
|
||||||
const char = p_map[p.type];
|
const char = p_map[p.type];
|
||||||
str += char;
|
str += char;
|
||||||
str += " ";
|
str += " ";
|
||||||
|
@ -21,39 +27,46 @@ function parameterDescription(params: Array<ParameterType>): string {
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateIsa(iset: InstructionSet): string {
|
export function generateIsaTable(iset: InstructionSet): HTMLTableElement {
|
||||||
|
const table = el("table").fin();
|
||||||
|
|
||||||
|
const headings = el("tr").fin();
|
||||||
|
|
||||||
|
headings.appendChild(el("td").tx("Code").fin());
|
||||||
|
headings.appendChild(el("td").tx("Parameters").fin());
|
||||||
|
headings.appendChild(el("td").tx("Name").fin());
|
||||||
|
headings.appendChild(el("td").tx("Description").fin());
|
||||||
|
table.appendChild(headings);
|
||||||
|
|
||||||
const instructions: Array<[u8, Instruction]> = [];
|
const instructions: Array<[u8, Instruction]> = [];
|
||||||
|
|
||||||
for (const kv of iset.instructions.entries()) instructions.push(kv);
|
for (const kv of iset.instructions.entries()) instructions.push(kv);
|
||||||
|
|
||||||
let output_string = "INSTRUCTIONS\n";
|
|
||||||
|
|
||||||
const max_instr_name_len = instructions.map((i) => i[1].name.length).reduce((acc, p) => Math.max(p, acc), 0);
|
|
||||||
instructions.sort((a, b) => a[0] - b[0]);
|
|
||||||
|
|
||||||
let current_category: InstrCategory | null = null;
|
let current_category: InstrCategory | null = null;
|
||||||
|
for (const [code, instr] of instructions) {
|
||||||
for (const instruction of instructions) {
|
const cat = iset.category_ranges.find((i) => inRange(code, i.start, i.end));
|
||||||
const cat = iset.category_ranges.find((i) => inRange(instruction[0], i.start, i.end));
|
|
||||||
if (cat === undefined) {
|
if (cat === undefined) {
|
||||||
throw new Error("Instruction found which is not part of category");
|
throw new Error("Instruction found which is not part of category");
|
||||||
}
|
}
|
||||||
if (current_category !== cat) {
|
if (current_category !== cat) {
|
||||||
output_string += `-- ${cat.name.toUpperCase()} --\n`;
|
const category_row = el("tr").fin();
|
||||||
|
category_row.appendChild(el("td").tx(cat.name.toUpperCase()).at("colspan", "4").cl("category").fin());
|
||||||
|
table.appendChild(category_row);
|
||||||
current_category = cat;
|
current_category = cat;
|
||||||
}
|
}
|
||||||
const hex_code = formatHex(instruction[0]);
|
const row = el("tr").fin();
|
||||||
|
const instr_text = instr.name;
|
||||||
|
|
||||||
const short_description = instruction[1].name.padEnd(max_instr_name_len, " ");
|
row.appendChild(
|
||||||
const parameters = parameterDescription(instruction[1].params);
|
el("td")
|
||||||
const description = instruction[1].desc;
|
.tx(`0x${formatHex(code)}`)
|
||||||
output_string += `0x${hex_code}: ${short_description}`;
|
.fin()
|
||||||
if (parameters.length !== 0) {
|
);
|
||||||
output_string += ` -${parameters}- `;
|
row.appendChild(el("td").tx(parameterDescription(instr.params)).fin());
|
||||||
} else {
|
row.appendChild(el("td").tx(instr_text).fin());
|
||||||
output_string += " - ";
|
row.appendChild(el("td").tx(instr.desc).fin());
|
||||||
|
table.appendChild(row);
|
||||||
}
|
}
|
||||||
output_string += `${description}\n`;
|
|
||||||
}
|
return table;
|
||||||
return output_string;
|
|
||||||
}
|
}
|
||||||
|
|
25
src/style/article.scss
Normal file
25
src/style/article.scss
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3 {
|
||||||
|
color: white;
|
||||||
|
font-family: charter;
|
||||||
|
margin-block: unset;
|
||||||
|
// margin-bottom: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-top: unset;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
color: lightgray;
|
||||||
|
}
|
||||||
|
|
||||||
|
article {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
width: 1000px;
|
||||||
|
font-family: Helvetica, sans-serif;
|
||||||
|
margin-inline: auto;
|
||||||
|
}
|
|
@ -1,9 +1,16 @@
|
||||||
@use "memory_registers";
|
@use "memory_registers";
|
||||||
@use "hover_text_box";
|
@use "hover_text_box";
|
||||||
|
@use "article";
|
||||||
@use "windows";
|
@use "windows";
|
||||||
|
@use "top_meta";
|
||||||
@use "buttons";
|
@use "buttons";
|
||||||
|
@use "table";
|
||||||
@use "vars";
|
@use "vars";
|
||||||
|
|
||||||
|
#isa_container summary {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
@ -35,9 +42,9 @@ 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"
|
". reglabel . ."
|
||||||
". registers . bank "
|
". registers . bank "
|
||||||
"reset memory memory memory"
|
"memlabel memory memory memory"
|
||||||
"title memory memory memory"
|
"title memory memory memory"
|
||||||
". buttons buttons buttons";
|
". buttons buttons buttons";
|
||||||
|
|
||||||
|
@ -50,8 +57,11 @@ main {
|
||||||
#memory_bank_view {
|
#memory_bank_view {
|
||||||
grid-area: bank;
|
grid-area: bank;
|
||||||
}
|
}
|
||||||
#labelcontainer {
|
#reg_label {
|
||||||
grid-area: regmemlabel;
|
grid-area: reglabel;
|
||||||
|
}
|
||||||
|
#mem_label {
|
||||||
|
grid-area: memlabel;
|
||||||
}
|
}
|
||||||
#cycles {
|
#cycles {
|
||||||
grid-area: cycles;
|
grid-area: cycles;
|
||||||
|
@ -70,9 +80,6 @@ main {
|
||||||
#memory {
|
#memory {
|
||||||
grid-area: memory;
|
grid-area: memory;
|
||||||
}
|
}
|
||||||
#reset_buttons {
|
|
||||||
grid-area: reset;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#reset_buttons {
|
#reset_buttons {
|
||||||
|
@ -88,15 +95,20 @@ main {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#labelcontainer {
|
#reg_label,
|
||||||
column-gap: 18px;
|
#mem_label {
|
||||||
font-size: 0.75em;
|
font-size: 0.75em;
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#mem_label {
|
||||||
|
writing-mode: vertical-lr;
|
||||||
|
text-align: right;
|
||||||
|
margin-top: 5px;
|
||||||
|
user-select: none;
|
||||||
|
transform: scale(-1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
#memory_bank_view {
|
#memory_bank_view {
|
||||||
display: flex;
|
display: flex;
|
||||||
#bank_boxes {
|
#bank_boxes {
|
||||||
|
|
34
src/style/table.scss
Normal file
34
src/style/table.scss
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
// border: 2px solid lightgray;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: lightgray;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
border: 1px solid rgb(190, 190, 190);
|
||||||
|
padding: 10px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background-color: rgb(235, 235, 235);
|
||||||
|
}
|
||||||
|
|
||||||
|
.category {
|
||||||
|
border-left: 2px solid yellow;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:nth-child(even) td {
|
||||||
|
background-color: rgb(22, 22, 22);
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:nth-child(odd) td {
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
caption {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
55
src/style/top_meta.scss
Normal file
55
src/style/top_meta.scss
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
input[type="text"] {
|
||||||
|
background-color: transparent;
|
||||||
|
outline: none;
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#program_meta {
|
||||||
|
align-items: center;
|
||||||
|
#title_div {
|
||||||
|
flex: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#btn_div {
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
#save_load_buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
border-bottom: 2px solid yellow;
|
||||||
|
margin-bottom: 7px;
|
||||||
|
button,
|
||||||
|
label.button {
|
||||||
|
font-size: 28px;
|
||||||
|
|
||||||
|
margin: unset;
|
||||||
|
padding: unset;
|
||||||
|
border: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
#trash_button:hover {
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"] {
|
||||||
|
text-decoration: underline;
|
||||||
|
margin-block: auto;
|
||||||
|
padding: 10px;
|
||||||
|
&:hover {
|
||||||
|
background-color: darkslategray;
|
||||||
|
}
|
||||||
|
&:focus {
|
||||||
|
background-color: darkslategray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
display: flex;
|
||||||
|
}
|
|
@ -9,7 +9,8 @@ import BankSelector from "./ui/components/bankViewSelector";
|
||||||
import EditButton from "./ui/components/editButton";
|
import EditButton from "./ui/components/editButton";
|
||||||
import pausePlay from "./ui/components/pausePlay";
|
import pausePlay from "./ui/components/pausePlay";
|
||||||
import SaveLoad from "./ui/components/saveLoad";
|
import SaveLoad from "./ui/components/saveLoad";
|
||||||
import ResetButtons from "./ui/components/resetButtons";
|
import ResetButton from "./ui/components/resetButton";
|
||||||
|
import TrashButton from "./ui/components/trashButton";
|
||||||
// Window Components
|
// Window Components
|
||||||
import InstructionExplainer from "./ui/windows/instructionExplainer";
|
import InstructionExplainer from "./ui/windows/instructionExplainer";
|
||||||
import Screen from "./ui/windows/screen";
|
import Screen from "./ui/windows/screen";
|
||||||
|
@ -23,7 +24,7 @@ export default class UI {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.register_component(MemoryView, $("memory"));
|
this.register_component(MemoryView, $("memory"));
|
||||||
this.register_component(frequencyIndicator, $("cycles"));
|
// this.register_component(frequencyIndicator, $("cycles"));
|
||||||
this.register_component(InstructionExplainer, $("instruction_explainer"));
|
this.register_component(InstructionExplainer, $("instruction_explainer"));
|
||||||
this.register_component(RegisterView, $("registers"));
|
this.register_component(RegisterView, $("registers"));
|
||||||
this.register_component(Screen, $("tv"));
|
this.register_component(Screen, $("tv"));
|
||||||
|
@ -33,7 +34,8 @@ export default class UI {
|
||||||
this.register_component(SaveLoad, $("save_load_buttons"));
|
this.register_component(SaveLoad, $("save_load_buttons"));
|
||||||
this.register_component(BankSelector, $("memory_bank_view"));
|
this.register_component(BankSelector, $("memory_bank_view"));
|
||||||
this.register_component(BankVisualizer, $("bank_viz"));
|
this.register_component(BankVisualizer, $("bank_viz"));
|
||||||
this.register_component(ResetButtons, $("reset_buttons"));
|
this.register_component(ResetButton, $("reset_button"));
|
||||||
|
this.register_component(TrashButton, $("trash_button"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private register_component(ctor: UiComponentConstructor, e: HTMLElement): void {
|
private register_component(ctor: UiComponentConstructor, e: HTMLElement): void {
|
||||||
|
|
|
@ -5,6 +5,14 @@ import UiComponent from "../uiComponent";
|
||||||
import { el } from "../../etc";
|
import { el } from "../../etc";
|
||||||
import CelledViewer from "../celledViewer";
|
import CelledViewer from "../celledViewer";
|
||||||
|
|
||||||
|
const p_map = {
|
||||||
|
[ParamType.Const]: "constant",
|
||||||
|
[ParamType.ConstMemory]: "memory",
|
||||||
|
[ParamType.Register]: "register",
|
||||||
|
[ParamType.Bank]: "bank",
|
||||||
|
[ParamType.RegisterAddress]: "regaddr",
|
||||||
|
};
|
||||||
|
|
||||||
/** Only to be run once */
|
/** Only to be run once */
|
||||||
function createBanks(
|
function createBanks(
|
||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
|
@ -106,26 +114,17 @@ export default class MemoryView implements UiComponent {
|
||||||
this.program.addCellClass(pos, "instruction_argument");
|
this.program.addCellClass(pos, "instruction_argument");
|
||||||
const t = param.type;
|
const t = param.type;
|
||||||
this.program.removeCellClass(pos, "constant", "register", "memory", "instruction", "invalid");
|
this.program.removeCellClass(pos, "constant", "register", "memory", "instruction", "invalid");
|
||||||
let name: string = "";
|
const name = p_map[t];
|
||||||
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.addCellClass(pos, name);
|
this.program.addCellClass(pos, name);
|
||||||
});
|
});
|
||||||
c.listen(CpuEvent.InstructionParsed, ({ instr, code, pos }) => {
|
c.listen(CpuEvent.InstructionParseBegin, ({ instr, code, pos }) => {
|
||||||
this.program.removeAllCellClass("instruction_argument");
|
this.program.removeAllCellClass("instruction_argument");
|
||||||
this.program.removeAllCellClass("current_instruction");
|
this.program.removeAllCellClass("current_instruction");
|
||||||
this.program.removeCellClass(pos, "constant", "register", "memory", "invalid");
|
this.program.removeCellClass(pos, "constant", "register", "memory", "invalid");
|
||||||
this.program.addCellClass(pos, "current_instruction");
|
this.program.addCellClass(pos, "current_instruction");
|
||||||
this.program.addCellClass(pos, "instruction");
|
this.program.addCellClass(pos, "instruction");
|
||||||
});
|
});
|
||||||
c.listen(CpuEvent.InvalidParsed, ({ code, pos }) => {
|
c.listen(CpuEvent.InvalidInstructionParsed, ({ code, pos }) => {
|
||||||
this.program.removeCellClass(pos, "constant", "register", "memory", "instruction");
|
this.program.removeCellClass(pos, "constant", "register", "memory", "instruction");
|
||||||
this.program.addCellClass(pos, "invalid");
|
this.program.addCellClass(pos, "invalid");
|
||||||
});
|
});
|
||||||
|
|
|
@ -42,8 +42,12 @@ 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();
|
const s_width = this.start_button.offsetWidth;
|
||||||
|
this.start_button.style.width = `${s_width.toString()}px`;
|
||||||
|
|
||||||
|
// const tb = new HoverTextBox(this.start_button, el("span").tx("hover test").st("color", "yellow").fin(), "left", 10);
|
||||||
|
// tb.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
disable(): void {
|
disable(): void {
|
||||||
|
@ -68,7 +72,7 @@ export default class pausePlay implements UiComponent {
|
||||||
} else {
|
} else {
|
||||||
this.on = true;
|
this.on = true;
|
||||||
this.cycle();
|
this.cycle();
|
||||||
this.start_button.textContent = "Storp";
|
this.start_button.textContent = "Stop";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
18
src/ui/components/resetButton.ts
Normal file
18
src/ui/components/resetButton.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { el } from "../../etc";
|
||||||
|
import { UiEventHandler, UiCpuSignalHandler, UiCpuSignal } from "../../events";
|
||||||
|
import UiComponent from "../uiComponent";
|
||||||
|
|
||||||
|
export default class ResetButton 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").ti("Reset State").tx("⟳").fin();
|
||||||
|
|
||||||
|
reset_button.addEventListener("click", () => this.cpu_signals.dispatch(UiCpuSignal.RequestCpuSoftReset));
|
||||||
|
this.container.appendChild(reset_button);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,31 +0,0 @@
|
||||||
import { el } from "../../etc";
|
|
||||||
import { UiEventHandler, UiCpuSignalHandler, 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").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 {
|
|
||||||
this.cpu_signals.dispatch(UiCpuSignal.RequestCpuSoftReset);
|
|
||||||
}
|
|
||||||
|
|
||||||
trashClicked(): void {
|
|
||||||
const a = confirm("Clear all code? Irreversible");
|
|
||||||
if (a) {
|
|
||||||
this.cpu_signals.dispatch(UiCpuSignal.RequestCpuReset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -37,7 +37,7 @@ export default class SaveLoad implements UiComponent {
|
||||||
this.cpu_signals.dispatch(UiCpuSignal.RequestMemoryDump, (memory) => {
|
this.cpu_signals.dispatch(UiCpuSignal.RequestMemoryDump, (memory) => {
|
||||||
const flattened = new Uint8Array(256 * memory.length);
|
const flattened = new Uint8Array(256 * memory.length);
|
||||||
for (let x = 0; x < 4; x++) {
|
for (let x = 0; x < 4; x++) {
|
||||||
for (let y = 0; y < 256; x++) {
|
for (let y = 0; y < 256; y++) {
|
||||||
flattened[256 * x + y] = memory[x][y];
|
flattened[256 * x + y] = memory[x][y];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
23
src/ui/components/trashButton.ts
Normal file
23
src/ui/components/trashButton.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { el } from "../../etc";
|
||||||
|
import { UiEventHandler, UiCpuSignalHandler, UiCpuSignal } from "../../events";
|
||||||
|
import UiComponent from "../uiComponent";
|
||||||
|
|
||||||
|
export default class TrashButton 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 trash_button = el("button").cl("nostyle").ti("Delete Code").tx("🗑").fin();
|
||||||
|
|
||||||
|
this.container.addEventListener("click", () => this.trashClicked());
|
||||||
|
}
|
||||||
|
|
||||||
|
trashClicked(): void {
|
||||||
|
if (confirm("Clear all code? Irreversible")) {
|
||||||
|
this.cpu_signals.dispatch(UiCpuSignal.RequestCpuReset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,14 @@ import { u8 } from "../../num";
|
||||||
import WindowBox from "../windowBox";
|
import WindowBox from "../windowBox";
|
||||||
import UiComponent from "../uiComponent";
|
import UiComponent from "../uiComponent";
|
||||||
|
|
||||||
|
const p_map = {
|
||||||
|
[ParamType.Const]: "constant",
|
||||||
|
[ParamType.ConstMemory]: "memory",
|
||||||
|
[ParamType.Register]: "register",
|
||||||
|
[ParamType.Bank]: "bank",
|
||||||
|
[ParamType.RegisterAddress]: "regaddr",
|
||||||
|
};
|
||||||
|
|
||||||
export default class InstructionExplainer extends WindowBox implements UiComponent {
|
export default class InstructionExplainer extends WindowBox implements UiComponent {
|
||||||
events: UiEventHandler;
|
events: UiEventHandler;
|
||||||
cpu_signals: UiCpuSignalHandler;
|
cpu_signals: UiCpuSignalHandler;
|
||||||
|
@ -34,33 +42,33 @@ export default class InstructionExplainer extends WindowBox implements UiCompone
|
||||||
|
|
||||||
addParameter(param: ParameterType, pos: u8, byte: u8): void {
|
addParameter(param: ParameterType, pos: u8, byte: u8): void {
|
||||||
const t = param.type;
|
const t = param.type;
|
||||||
let name;
|
|
||||||
if (t === ParamType.Const) {
|
this.addBox(formatHex(byte), param.desc, p_map[t]);
|
||||||
name = "constant";
|
|
||||||
} else if (t === ParamType.Memory) {
|
|
||||||
name = "memory";
|
|
||||||
} else if (t === ParamType.Register) {
|
|
||||||
name = "register";
|
|
||||||
} else {
|
|
||||||
throw new Error("unreachable");
|
|
||||||
}
|
|
||||||
this.addBox(formatHex(byte), param.desc, name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addInvalid(pos: u8, byte: u8): void {
|
addInvalidParam(param: ParameterType, pos: u8, byte: u8): void {
|
||||||
|
const t = param.type;
|
||||||
|
|
||||||
|
this.addBox(formatHex(byte), param.desc, `${p_map[t]} invalid`);
|
||||||
|
}
|
||||||
|
|
||||||
|
addInvalidInstr(pos: u8, byte: u8): void {
|
||||||
this.reset();
|
this.reset();
|
||||||
this.addBox(formatHex(byte), "Invalid Instruction", "invalid");
|
this.addBox(formatHex(byte), "Invalid Instruction", "invalid");
|
||||||
}
|
}
|
||||||
|
|
||||||
initCpuEvents(c: CpuEventHandler): void {
|
initCpuEvents(c: CpuEventHandler): void {
|
||||||
|
c.listen(CpuEvent.InstructionParseBegin, ({ instr, code, pos }) => {
|
||||||
|
this.addInstruction(instr, pos, code);
|
||||||
|
});
|
||||||
c.listen(CpuEvent.ParameterParsed, ({ param, code, pos }) => {
|
c.listen(CpuEvent.ParameterParsed, ({ param, code, pos }) => {
|
||||||
this.addParameter(param, pos, code);
|
this.addParameter(param, pos, code);
|
||||||
});
|
});
|
||||||
c.listen(CpuEvent.InstructionParsed, ({ instr, code, pos }) => {
|
c.listen(CpuEvent.InvalidParameterParsed, ({ param, code, pos }) => {
|
||||||
this.addInstruction(instr, pos, code);
|
this.addInvalidParam(param, code, pos);
|
||||||
});
|
});
|
||||||
c.listen(CpuEvent.InvalidParsed, ({ code, pos }) => {
|
c.listen(CpuEvent.InvalidInstructionParsed, ({ code, pos }) => {
|
||||||
this.addInvalid(pos, code);
|
this.addInvalidInstr(pos, code);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue