Compare commits
2 commits
f290b836cf
...
7e4d3c8da8
Author | SHA1 | Date | |
---|---|---|---|
7e4d3c8da8 | |||
e827ac11b5 |
7
TODO
7
TODO
|
@ -5,9 +5,10 @@ Move start/stop/auto logic into computer
|
|||
Speed control slider behavior
|
||||
Speed control slider styling
|
||||
|
||||
Determine how to implement 16 bit integers in code (new instructions?)
|
||||
|
||||
UI for screen (toggling (click an icon?))
|
||||
UI for togging other UI elements
|
||||
|
||||
Instruction assign memory based on register
|
||||
UI for showing which Memory bank is selected
|
||||
VRAM select instruction
|
||||
|
||||
Error log
|
||||
|
|
143
newISA.md
Normal file
143
newISA.md
Normal file
|
@ -0,0 +1,143 @@
|
|||
# ISA V0.7
|
||||
|
||||
# Terms
|
||||
|
||||
The form of this document is a list of instructions set into categories.
|
||||
Each instruction starts with the recommended mnemonic and then the parameters for the instruction.
|
||||
|
||||
```
|
||||
<Code> <mnemonic> <parameter 1> <parameter 2> ...
|
||||
```
|
||||
|
||||
Each parameter is is abbreviated into a category
|
||||
|
||||
- R: Register
|
||||
- M: Memory address
|
||||
- C: Constant
|
||||
|
||||
Parts of this document marked with **!!** are uncertain and will likely change
|
||||
|
||||
# Instructions
|
||||
|
||||
## Memory & Register Management
|
||||
|
||||
**COPY** _from_, _to_ - Copies the byte in _from_ to the destination _to_\
|
||||
0x10 COPY R M\
|
||||
0x11 COPY M R\
|
||||
0x12 COPY M M\
|
||||
0x13 COPY R R
|
||||
|
||||
**ZERO** _to_ - Sets the value in _to_ to 0\
|
||||
0x17 ZERO R\
|
||||
0x18 ZERO M
|
||||
|
||||
0x19 **SET** R C - Sets the value in R to C
|
||||
|
||||
0x1F **SETB** R - Sets the current memory bank to the value in R
|
||||
|
||||
## Control Flow
|
||||
|
||||
0x00 **NOP** - Does nothing
|
||||
|
||||
**GOTO** _address_ - Moves instruction counter to _address_\
|
||||
0x20 GOTO R\
|
||||
0x21 GOTO C
|
||||
|
||||
**GOTRUE** R, _address_ - Moves instruction counter to _address_ if R is true\
|
||||
0x22 GOTOTRUE R R\
|
||||
0x23 GOTOTRUE R C
|
||||
|
||||
0x2A **GOCRY** _address_ - Moves the instruction counter to _address_ if the cary flag is set
|
||||
|
||||
0x2D **CALL** R - Moves the instruction counter to R and pushes the address of the call instruction
|
||||
|
||||
0x2E **RET** - Pops the address of the last call instruction off of the call stack and moves the instruction counter past it.
|
||||
|
||||
0x2F **HCF** - Halt and catch fire.
|
||||
|
||||
## Comparison
|
||||
|
||||
**EQU** _result_, _p1_, _p2_ - Sets _result_ to true if the values in _p1_ and _p2_ are the same\
|
||||
0x30 EQU R R R\
|
||||
0x31 EQU R R C
|
||||
|
||||
**LT** _result_, _p1_, _p2_ - Sets _result_ to true if the value in _p1_ is less than the value in _p2_\
|
||||
0x32 LT R R R \
|
||||
0x33 LT R R C
|
||||
|
||||
**GT** _result_, _p1_, _p2_ - Sets _result_ to true if the value in _p1_ is greater than the value in _p2_\
|
||||
0x34 GT R R R \
|
||||
0x35 GT R R C
|
||||
|
||||
Reserved for LEQ GEQ
|
||||
|
||||
0x36\
|
||||
0x37\
|
||||
0x38\
|
||||
0x39
|
||||
|
||||
## Logic / Bitwise
|
||||
|
||||
**OR** R, _value_ - Sets each bit in R to its OR with the respective bit in _value_\
|
||||
0x40 OR R R\
|
||||
0x41 OR R C
|
||||
|
||||
**AND** R, _value_ - Sets each bit in R to its AND with the respective bit in _value_\
|
||||
0x42 AND R R\
|
||||
0x43 AND R C
|
||||
|
||||
**XOR** R, _value_ - Sets each bit in R to its XOR with the respective bit in _value_\
|
||||
0x44 XOR R R\
|
||||
0x45 XOR R C
|
||||
|
||||
**LBS** R, _quantity_ - Shifts each bit in R to the left by _quantity_. Fills new bits with 0\
|
||||
0x46 LBS R R\
|
||||
0x47 LBS R C
|
||||
|
||||
**RBS** R, _quantity_ - Shifts each bit in R to the right by _quantity_. Fills new bits with 0\
|
||||
0x48 RBS R R\
|
||||
0x49 RBS R C
|
||||
|
||||
0x4A **NOT** R - Flips each bit in value R
|
||||
|
||||
## Arithmetic
|
||||
|
||||
**ADD** _to_, _from_ - Adds to the the byte in _to_ with the value in _from_\
|
||||
0x50 ADD R R\
|
||||
0x51 ADD R C
|
||||
|
||||
**SUB** _to_, _from_ - Subtracts from the value in _to_ by the value in _from_\
|
||||
0x52 SUB R R\
|
||||
0x53 SUB R C
|
||||
|
||||
0x5E **INC** R - Increments the value in R by 1
|
||||
|
||||
0x5F **DEC** R - Decrements the value in R by 1
|
||||
|
||||
## IO
|
||||
|
||||
!! **INTXT** R - Read 1 byte of input to R
|
||||
|
||||
0xF0 **OUTXT** R - Prints the value in R to output as ASCII
|
||||
|
||||
0xF1 **OUT** R - Prints the value in R to output as base 10
|
||||
|
||||
0xFF **VRB** R - Selects the bank number in R to be used as VRAM
|
||||
|
||||
# What is True?
|
||||
|
||||
True is defined as having the least significant bit in a byte set to 1. False is defined as having the least significant bit in a byte set to 0.
|
||||
|
||||
# What are flags
|
||||
|
||||
The CPU has the following flags
|
||||
|
||||
- Carry
|
||||
|
||||
Flags are set as the result of instructions.
|
||||
|
||||
## Carry
|
||||
|
||||
When the add instruction runs, the result can be greater than 8 bits in size. In this case, the carry flag is set true.
|
||||
|
||||
!! The cary flag is set to false in two cases: At the start of an **ADD** instruction, and after running a **GOCRY** instruction
|
|
@ -1,7 +1,7 @@
|
|||
import { CpuEvent, CpuEventHandler, UiEvent, UiEventHandler } from "./events";
|
||||
import { byte_array_to_js_source, format_hex } from "./etc";
|
||||
import { Instruction, ISA } from "./instructionSet";
|
||||
import { m256, u1, u3, u8 } from "./num";
|
||||
import { m256, u1, u2, u3, u8 } from "./num";
|
||||
|
||||
export type TempInstrState = {
|
||||
pos: u8;
|
||||
|
@ -10,13 +10,21 @@ export type TempInstrState = {
|
|||
params: Array<u8>;
|
||||
};
|
||||
|
||||
function init_banks(): [Uint8Array, Uint8Array, Uint8Array, Uint8Array] {
|
||||
const banks = [];
|
||||
for (let i = 0; i < 4; i++) {
|
||||
banks.push(new Uint8Array(256));
|
||||
}
|
||||
return banks as [Uint8Array, Uint8Array, Uint8Array, Uint8Array];
|
||||
}
|
||||
|
||||
export class Computer {
|
||||
private memory: Uint8Array = new Uint8Array(256);
|
||||
private vram: Uint8Array = new Uint8Array(256);
|
||||
private banks: [Uint8Array, Uint8Array, Uint8Array, Uint8Array] = init_banks();
|
||||
private registers: Uint8Array = new Uint8Array(8);
|
||||
private call_stack: Array<u8> = [];
|
||||
private carry_flag: boolean = false;
|
||||
private program_counter: u8 = 0;
|
||||
private bank: u1 = 0;
|
||||
private bank: u2 = 0;
|
||||
private current_instr: TempInstrState | null = null;
|
||||
events: CpuEventHandler = new CpuEventHandler();
|
||||
|
||||
|
@ -94,34 +102,22 @@ export class Computer {
|
|||
}
|
||||
this.events.dispatch(CpuEvent.Cycle);
|
||||
}
|
||||
private getMemorySilent(address: u8, bank_override?: u1): u8 {
|
||||
const banks = [this.memory, this.vram];
|
||||
|
||||
const bank = banks[bank_override ?? this.bank];
|
||||
private getMemorySilent(address: u8, bank_override?: u2): u8 {
|
||||
const bank = this.banks[bank_override ?? this.bank];
|
||||
const value = bank[address] as u8;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
getMemory(address: u8, bank_override?: u1): u8 {
|
||||
getMemory(address: u8, bank_override?: u2): u8 {
|
||||
const value = this.getMemorySilent(address, bank_override);
|
||||
this.events.dispatch(CpuEvent.MemoryAccessed, { address, bank: this.bank, value });
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
setMemory(address: u8, value: u8): void {
|
||||
let bank: Uint8Array | undefined;
|
||||
if (this.bank === 0) {
|
||||
bank = this.memory;
|
||||
} else if (this.bank === 1) {
|
||||
bank = this.vram;
|
||||
} else {
|
||||
const _: never = this.bank;
|
||||
}
|
||||
if (bank === undefined) {
|
||||
throw new Error("unreachable");
|
||||
}
|
||||
bank[address] = value;
|
||||
this.banks[this.bank][address] = value;
|
||||
this.events.dispatch(CpuEvent.MemoryChanged, { address, bank: this.bank, value });
|
||||
}
|
||||
|
||||
|
@ -153,18 +149,28 @@ export class Computer {
|
|||
return this.call_stack.pop() ?? null;
|
||||
}
|
||||
|
||||
setBank(bank_no: u1): void {
|
||||
setBank(bank_no: u2): void {
|
||||
this.events.dispatch(CpuEvent.SwitchBank, { bank: bank_no });
|
||||
this.bank = bank_no;
|
||||
}
|
||||
|
||||
setCarry(state: boolean): void {
|
||||
this.carry_flag = state;
|
||||
this.events.dispatch(CpuEvent.SetFlagCarry, true);
|
||||
}
|
||||
|
||||
getCarry(): boolean {
|
||||
return this.carry_flag;
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this.events.dispatch(CpuEvent.Reset);
|
||||
this.memory = new Uint8Array(256);
|
||||
this.banks = init_banks();
|
||||
this.registers = new Uint8Array(8);
|
||||
this.call_stack = [];
|
||||
this.current_instr = null;
|
||||
this.program_counter = 0;
|
||||
this.carry_flag = false;
|
||||
}
|
||||
|
||||
init_events(ui: UiEventHandler): void {
|
||||
|
@ -175,20 +181,21 @@ export class Computer {
|
|||
}
|
||||
|
||||
load_memory(program: Array<u8>): void {
|
||||
// TODO allow loading into other banks
|
||||
console.log(byte_array_to_js_source(program));
|
||||
const max_loop: u8 = Math.min(255, program.length) as u8;
|
||||
for (let i: u8 = 0; i < 255; i++) {
|
||||
for (let i: u8 = 0; i < 256; i++) {
|
||||
// Don't fire event if no change is made
|
||||
if (this.memory[i] === program[i]) continue;
|
||||
if (this.banks[0][i] === program[i]) continue;
|
||||
|
||||
this.memory[i] = program[i];
|
||||
this.banks[0][i] = program[i];
|
||||
this.events.dispatch(CpuEvent.MemoryChanged, { address: i as u8, bank: 0, value: program[i] });
|
||||
}
|
||||
this.program_counter = 0;
|
||||
}
|
||||
|
||||
dump_memory(): Uint8Array {
|
||||
return this.memory;
|
||||
return this.banks[0];
|
||||
}
|
||||
|
||||
private step_forward(): void {
|
||||
|
|
|
@ -42,3 +42,10 @@ export function el(type: string, id?: string): HTMLElement | undefined {
|
|||
}
|
||||
|
||||
export type NonEmptyArray<T> = T[] & { 0: T };
|
||||
|
||||
export const SVG_NS = "http://www.w3.org/2000/svg";
|
||||
|
||||
export function in_range(check: number, start: number, end: number): boolean {
|
||||
if (check >= start && check <= end) return true;
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
import { EventHandler } from "./eventHandler";
|
||||
import { Instruction, ParameterType } from "./instructionSet";
|
||||
import { u1, u3, u8 } from "./num";
|
||||
import { u1, u2, u3, u8 } from "./num";
|
||||
|
||||
//
|
||||
// CPU Event Handler Definition
|
||||
|
@ -26,6 +26,7 @@ export enum CpuEvent {
|
|||
// ClockStopped,
|
||||
MemoryAccessed,
|
||||
SwitchBank,
|
||||
SetFlagCarry,
|
||||
}
|
||||
|
||||
type VoidDataCpuEventList = CpuEvent.Halt | CpuEvent.Reset | CpuEvent.Cycle;
|
||||
|
@ -33,16 +34,17 @@ type VoidDataCpuEventList = CpuEvent.Halt | CpuEvent.Reset | CpuEvent.Cycle;
|
|||
// | CpuEvent.ClockStopped;
|
||||
|
||||
interface CpuEventMap {
|
||||
[CpuEvent.MemoryChanged]: { address: u8; bank: u1; value: u8 };
|
||||
[CpuEvent.MemoryAccessed]: { address: u8; bank: u1; value: u8 };
|
||||
[CpuEvent.MemoryChanged]: { address: u8; bank: u2; value: u8 };
|
||||
[CpuEvent.MemoryAccessed]: { address: u8; bank: u2; value: u8 };
|
||||
[CpuEvent.RegisterChanged]: { register_no: u3; value: u8 };
|
||||
[CpuEvent.ProgramCounterChanged]: { counter: u8 };
|
||||
[CpuEvent.InstructionParsed]: { pos: u8; code: u8; instr: Instruction };
|
||||
[CpuEvent.ParameterParsed]: { pos: u8; code: u8; param: ParameterType };
|
||||
[CpuEvent.InvalidParsed]: { pos: u8; code: u8 };
|
||||
[CpuEvent.InstructionExecuted]: { instr: Instruction };
|
||||
[CpuEvent.SwitchBank]: { bank: u1 };
|
||||
[CpuEvent.SwitchBank]: { bank: u2 };
|
||||
[CpuEvent.Print]: string;
|
||||
[CpuEvent.SetFlagCarry]: boolean;
|
||||
}
|
||||
|
||||
export interface CpuEventHandler extends EventHandler<CpuEvent> {
|
||||
|
|
25
src/index.ts
25
src/index.ts
|
@ -17,6 +17,7 @@ declare global {
|
|||
interface Window {
|
||||
comp: Computer;
|
||||
ui: UI;
|
||||
firehose: () => void;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,7 +79,6 @@ function main(): void {
|
|||
ui.init_events(computer.events);
|
||||
computer.load_memory(program);
|
||||
computer.init_events(ui.events);
|
||||
|
||||
window.comp = computer;
|
||||
window.ui = ui;
|
||||
|
||||
|
@ -112,10 +112,17 @@ function main(): void {
|
|||
});
|
||||
reader.readAsArrayBuffer(file);
|
||||
});
|
||||
|
||||
// computer.events.firehose((ident, data) => {
|
||||
// console.log(`New Event: ${CpuEvent[ident]}. data: `, data);
|
||||
// });
|
||||
let fire = false;
|
||||
window.firehose = (): void => {
|
||||
if (fire === false) {
|
||||
computer.events.firehose((ident, data) => {
|
||||
console.log(`New Event: ${CpuEvent[ident]}. data: `, data);
|
||||
});
|
||||
fire = true;
|
||||
} else {
|
||||
console.error("Firehose already started");
|
||||
}
|
||||
};
|
||||
|
||||
$("save_button").addEventListener("click", () => {
|
||||
const memory = computer.dump_memory();
|
||||
|
@ -125,11 +132,15 @@ function main(): void {
|
|||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
link.download = "bin.bin";
|
||||
link.style.display = "none";
|
||||
link.click();
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
main();
|
||||
// at least you know it's bad
|
||||
try {
|
||||
main();
|
||||
} catch (e) {
|
||||
alert(e);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* @license GPL-3.0
|
||||
*/
|
||||
import { CpuEvent, CpuEventHandler } from "./events";
|
||||
import { format_hex } from "./etc";
|
||||
import { isU3, m256, u1, u3, u8 } from "./num";
|
||||
import { format_hex, in_range } from "./etc";
|
||||
import { isU2, isU3, m256, u2, u3, u8 } from "./num";
|
||||
|
||||
export enum ParamType {
|
||||
Const,
|
||||
|
@ -47,7 +47,9 @@ interface GenericComputer {
|
|||
setRegister: (number: u3, value: u8) => void;
|
||||
pushCallStack: (address: u8) => boolean;
|
||||
popCallStack: () => u8 | null;
|
||||
setBank: (bank_no: u1) => void;
|
||||
setBank: (bank_no: u2) => void;
|
||||
getCarry(): boolean;
|
||||
setCarry(state: boolean): void;
|
||||
}
|
||||
|
||||
interface AfterExecutionComputerAction {
|
||||
|
@ -67,11 +69,18 @@ export interface Instruction {
|
|||
) => void;
|
||||
}
|
||||
|
||||
export type InstrCategory = {
|
||||
start: u8;
|
||||
end: u8;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export class InstructionSet {
|
||||
instructions: Map<u8, Instruction>;
|
||||
|
||||
category_ranges: Array<InstrCategory>;
|
||||
constructor() {
|
||||
this.instructions = new Map();
|
||||
this.category_ranges = [];
|
||||
}
|
||||
|
||||
insertInstruction(hexCode: u8, instruction: Instruction): void {
|
||||
|
@ -81,24 +90,152 @@ export class InstructionSet {
|
|||
this.instructions.set(hexCode, instruction);
|
||||
}
|
||||
|
||||
addCategory(c: InstrCategory): void {
|
||||
// Check for overlap with existing ranges
|
||||
for (const r of this.category_ranges) {
|
||||
if (in_range(c.start, r.start, r.end) || in_range(c.end, r.start, r.end)) {
|
||||
throw new Error(`Range of ${c.start}...${c.end} is already registered`);
|
||||
}
|
||||
}
|
||||
|
||||
this.category_ranges.push(c);
|
||||
}
|
||||
|
||||
getInstruction(hexCode: u8): Instruction | null {
|
||||
// console.log(format_hex(hexCode));
|
||||
return this.instructions.get(hexCode) ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
function category(start: u8, end: u8, name: string): InstrCategory {
|
||||
return { start, end, name };
|
||||
}
|
||||
|
||||
export const ISA = new InstructionSet();
|
||||
|
||||
ISA.addCategory(category(0x10, 0x1f, "Memory & Register Management"));
|
||||
ISA.addCategory(category(0x20, 0x2f, "Control Flow"));
|
||||
ISA.addCategory(category(0x30, 0x3f, "Comparison"));
|
||||
ISA.addCategory(category(0x40, 0x4f, "Logic / Bitwise"));
|
||||
ISA.addCategory(category(0x50, 0x5f, "Arithmetic"));
|
||||
ISA.addCategory(category(0xf0, 0xff, "IO"));
|
||||
|
||||
// The definitions for actual instructions.
|
||||
|
||||
ISA.insertInstruction(0x00, {
|
||||
name: "NoOp",
|
||||
desc: "No operation; do nothing",
|
||||
params: [],
|
||||
execute: () => {},
|
||||
//
|
||||
// MEMORY & REGISTER MANAGEMENT
|
||||
// 0x10 -> 0x1F
|
||||
//
|
||||
|
||||
// COPY
|
||||
ISA.insertInstruction(0x10, {
|
||||
name: "Copy R -> M",
|
||||
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")],
|
||||
execute(c, p) {
|
||||
const [register_no, mem_address] = p;
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
c.setMemory(mem_address, c.getRegister(register_no));
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x10, {
|
||||
ISA.insertInstruction(0x11, {
|
||||
name: "Copy M -> R",
|
||||
desc: "Copy the byte in memory address (P1) to the register (P2)",
|
||||
params: [new MemorParam(""), new RegisParam("")],
|
||||
execute(c, p) {
|
||||
const [register_no, mem_address] = p;
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
c.setRegister(register_no, c.getMemory(mem_address));
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x12, {
|
||||
name: "Copy M -> M",
|
||||
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")],
|
||||
execute(c, p) {
|
||||
const [mem_address_1, mem_address_2] = p;
|
||||
c.setMemory(mem_address_2, c.getMemory(mem_address_1));
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x13, {
|
||||
name: "Copy R -> R",
|
||||
desc: "Copy the byte in register (P1) to register (P2)",
|
||||
params: [new RegisParam("Copy the byte in this register"), new RegisParam("To this register")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("todo");
|
||||
if (!isU3(register_no_2)) throw new Error("todo");
|
||||
c.setRegister(register_no_2, c.getRegister(register_no_1));
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x17, {
|
||||
name: "Zero Register",
|
||||
desc: "Set the byte in register (P1) to 0",
|
||||
params: [new RegisParam("Set the value in this register to 0")],
|
||||
execute(c, p) {
|
||||
const register_no = p[0];
|
||||
if (!isU3(register_no)) throw new Error("todo");
|
||||
c.setRegister(register_no, 0);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x18, {
|
||||
name: "Zero Memory",
|
||||
desc: "Set the byte in memory address (P1) to 0",
|
||||
params: [new RegisParam("Set the value in this memory address to 0")],
|
||||
execute(c, p) {
|
||||
const mem_address = p[0];
|
||||
c.setMemory(mem_address, 0);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x19, {
|
||||
name: "Set Register",
|
||||
desc: "Assigns constant value (P2) to register (P1)",
|
||||
params: [new RegisParam("Set this register"), new ConstParam("to this constant")],
|
||||
execute(c, p) {
|
||||
const [register_no, value] = p;
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
c.setRegister(register_no, value);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x1f, {
|
||||
name: "Set bank",
|
||||
desc: "Selects which bank of memory to write and read to",
|
||||
params: [new ConstParam("Bank number")],
|
||||
execute(c, p) {
|
||||
const bank_no = p[0];
|
||||
if (!isU2(bank_no)) {
|
||||
throw new Error("TODO");
|
||||
}
|
||||
c.setBank(bank_no);
|
||||
},
|
||||
});
|
||||
|
||||
//
|
||||
// CONTROL FLOW
|
||||
// 0x20 -> 0x2F
|
||||
//
|
||||
|
||||
ISA.insertInstruction(0x20, {
|
||||
name: "Goto",
|
||||
desc: "Moves the CPU instruction counter to the value in register (P1)",
|
||||
params: [new RegisParam("new instruction counter location")],
|
||||
execute: (c, p, a) => {
|
||||
const register_no = p[0];
|
||||
if (!isU3(register_no)) {
|
||||
throw new Error("todo");
|
||||
}
|
||||
const new_address = c.getRegister(register_no);
|
||||
c.setProgramCounter(new_address);
|
||||
a.noStep();
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x21, {
|
||||
name: "Goto",
|
||||
desc: "Moves the CPU instruction counter to the value in (P1)",
|
||||
params: [new ConstParam("new instruction counter location")],
|
||||
|
@ -109,184 +246,67 @@ ISA.insertInstruction(0x10, {
|
|||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x20, {
|
||||
name: "LoadToRegister",
|
||||
desc: "Sets the byte in register (P1) to be the contents of memory cell at address in register (P2)",
|
||||
params: [new RegisParam("Set this register to"), new RegisParam("the byte held in this memory address")],
|
||||
execute(c, p) {
|
||||
const [register_no, register_2] = p;
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
if (!isU3(register_2)) throw new Error("TODO");
|
||||
const mem_value = c.getMemory(c.getRegister(register_2));
|
||||
|
||||
c.setRegister(register_no, mem_value);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x21, {
|
||||
name: "SaveToMemory",
|
||||
desc: "Writes the byte in register (P1) to the memory cell (P2)",
|
||||
params: [new RegisParam("Write the byte in this register"), new MemorParam("To this memory address")],
|
||||
execute(c, p) {
|
||||
const [register_no, mem_address] = p;
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
c.setMemory(mem_address, c.getRegister(register_no));
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x2f, {
|
||||
name: "AssignRegister",
|
||||
desc: "Assigns constant value (P2) to register (P1)",
|
||||
params: [new RegisParam("Set this register"), new ConstParam("to this constant")],
|
||||
execute(c, p) {
|
||||
const [register_no, value] = p;
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
c.setRegister(register_no, value);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x11, {
|
||||
name: "GotoIfLowBitHigh",
|
||||
desc: "Moves the CPU instruction counter to the value in (P1) if the value in register (P2) has the lowest bit true",
|
||||
params: [new ConstParam("Set program counter to this constant"), new RegisParam("if this register's 1 bit is set")],
|
||||
execute(c, p, a) {
|
||||
const [new_address, check_register_no] = p;
|
||||
if (!isU3(check_register_no)) throw new Error("TODO");
|
||||
if (c.getRegister(check_register_no) % 2 === 1) {
|
||||
c.setProgramCounter(new_address);
|
||||
a.noStep();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x30, {
|
||||
name: "Increment",
|
||||
desc: "Increments the value within register (P1) by 1",
|
||||
params: [new RegisParam("register to be incremented")],
|
||||
execute(c, p) {
|
||||
const register_no = p[0];
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
const current_value = c.getRegister(register_no);
|
||||
const new_value = m256(current_value + 1);
|
||||
c.setRegister(register_no, new_value);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x31, {
|
||||
name: "Decrement",
|
||||
desc: "Decrements the value within register (P1) by 1",
|
||||
params: [new RegisParam("register to be decremented")],
|
||||
execute(c, p) {
|
||||
const register_no = p[0];
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
const current_value = c.getRegister(register_no);
|
||||
let new_value = current_value - 1;
|
||||
if (new_value === -1) {
|
||||
new_value = 255;
|
||||
}
|
||||
c.setRegister(register_no, new_value as u8);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x40, {
|
||||
name: "Add",
|
||||
desc: "Adds the contents of (P1) and (P2) and stores result to register (P1). (Overflow will be taken mod 256)",
|
||||
params: [new RegisParam("set this register to"), new RegisParam("it's sum with the value in this register")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
const new_value = m256(c.getRegister(register_no_1) + c.getRegister(register_no_2));
|
||||
c.setRegister(register_no_1, new_value);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x50, {
|
||||
name: "Equals",
|
||||
desc: "If byte in register (P2) equals byte in register (P3), set byte in register (P1) to 0x01",
|
||||
params: [
|
||||
new RegisParam("Set this register to be 0x01"),
|
||||
new RegisParam("if this register and"),
|
||||
new RegisParam("this register are equal (else 0x00)"),
|
||||
],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2, register_no_3] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
if (!isU3(register_no_3)) throw new Error("TODO");
|
||||
const truth = c.getRegister(register_no_2) === c.getRegister(register_no_3) ? 0x01 : 0x00;
|
||||
c.setRegister(register_no_1, truth);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0xfe, {
|
||||
name: "PrintASCII",
|
||||
desc: "Prints the ASCII byte in register (P1) to console",
|
||||
params: [new RegisParam("Register to print from")],
|
||||
execute(c, p, a) {
|
||||
const register_no = p[0];
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
const asciiByte = c.getRegister(register_no);
|
||||
|
||||
const char = String.fromCharCode(asciiByte);
|
||||
a.dispatch(CpuEvent.Print, char);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x48, {
|
||||
name: "Bitwise And",
|
||||
desc: "Takes each bit in register (P1) and compares to the respective bit in register (P2). Each bit in register (P1) is set to 1 if the respective bit in both registers are 1",
|
||||
ISA.insertInstruction(0x22, {
|
||||
name: "Goto if True",
|
||||
desc: "Moves the instruction counter to the value in register (P2) if the value in register (P1) is true",
|
||||
params: [new RegisParam(""), new RegisParam("")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
const new_value = c.getRegister(register_no_1) & c.getMemory(register_no_2);
|
||||
c.setRegister(register_no_1, new_value as u8);
|
||||
execute: (c, p, a) => {
|
||||
const register_no_1 = p[0];
|
||||
if (!isU3(register_no_1)) throw new Error("todo");
|
||||
const bool = c.getRegister(register_no_1);
|
||||
if (!bool) return;
|
||||
const register_no_2 = p[1];
|
||||
if (!isU3(register_no_2)) throw new Error("todo");
|
||||
const new_address = c.getRegister(register_no_2);
|
||||
c.setProgramCounter(new_address);
|
||||
a.noStep();
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0xff, {
|
||||
name: "Print",
|
||||
desc: "Prints the byte in register (P1) to console as base 10",
|
||||
params: [new RegisParam("Register to print from")],
|
||||
execute(c, p, a) {
|
||||
ISA.insertInstruction(0x23, {
|
||||
name: "Goto if True",
|
||||
desc: "Moves the instruction counter to the value in (P2) if the value in register (P1) is true",
|
||||
params: [new RegisParam(""), new ConstParam("")],
|
||||
execute: (c, p, a) => {
|
||||
const [register_no, constant_value] = p;
|
||||
if (!isU3(register_no)) throw new Error("todo");
|
||||
const bool = c.getRegister(register_no);
|
||||
if (!bool) return;
|
||||
c.setProgramCounter(constant_value);
|
||||
a.noStep();
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x28, {
|
||||
name: "Goto if Carry Flag set",
|
||||
desc: "Moves the instruction counter to the value in register (P1) if CPU Carry flag is true",
|
||||
params: [new RegisParam("")],
|
||||
execute: (c, p, a) => {
|
||||
if (!c.getCarry()) return;
|
||||
const register_no = p[0];
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
const byte = c.getRegister(register_no);
|
||||
|
||||
a.dispatch(CpuEvent.Print, byte.toString(10));
|
||||
if (!isU3(register_no)) throw new Error("todo");
|
||||
const register_value = c.getRegister(register_no);
|
||||
c.setProgramCounter(register_value);
|
||||
a.noStep();
|
||||
c.setCarry(false);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x29, {
|
||||
name: "Goto if Carry Flag set",
|
||||
desc: "Moves the instruction counter to the value in (P1) if CPU Carry flag is true",
|
||||
params: [new ConstParam("")],
|
||||
execute: (c, p, a) => {
|
||||
if (!c.getCarry()) return;
|
||||
const goto_address = p[0];
|
||||
c.setProgramCounter(goto_address);
|
||||
a.noStep();
|
||||
c.setCarry(false);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0xfd, {
|
||||
name: "Print 16 bit",
|
||||
desc: "Prints the byte in register (P1) as the upper half and the byte in register (P2) as the lower half of a 16 bit number. Formats to decimal",
|
||||
params: [new RegisParam("Upper 8 bits of number"), new RegisParam("Lower 8 bits of number")],
|
||||
execute(c, p, a) {
|
||||
const [upper_register_no, lower_register_no] = p;
|
||||
if (!isU3(upper_register_no)) throw new Error("TODO");
|
||||
if (!isU3(lower_register_no)) throw new Error("TODO");
|
||||
const upper = c.getRegister(upper_register_no);
|
||||
const lower = c.getRegister(lower_register_no);
|
||||
const sum = upper * 16 * 16 + lower;
|
||||
|
||||
a.dispatch(CpuEvent.Print, sum.toString(10));
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x66, {
|
||||
name: "Halt and Catch Fire",
|
||||
desc: "Stops program execu..... Fire! FIRE EVERYWHERE!",
|
||||
params: [],
|
||||
execute(c, p, a) {
|
||||
a.dispatch(CpuEvent.Halt);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0xa0, {
|
||||
ISA.insertInstruction(0x2d, {
|
||||
name: "Call",
|
||||
desc: "Calls a subroute",
|
||||
desc: "",
|
||||
params: [new ConstParam("the subroutine at this memory address")],
|
||||
execute(c, p, a) {
|
||||
const current_address = c.getProgramCounter();
|
||||
|
@ -300,30 +320,390 @@ ISA.insertInstruction(0xa0, {
|
|||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0xa1, {
|
||||
ISA.insertInstruction(0x2e, {
|
||||
name: "Return",
|
||||
desc: "returns from a subroutine",
|
||||
desc: "",
|
||||
params: [],
|
||||
execute(c, p, a) {
|
||||
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));
|
||||
|
||||
a.noStep();
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0xb1, {
|
||||
name: "Set bank",
|
||||
desc: "Selects which bank of memory to write and read to",
|
||||
params: [new ConstParam("Bank number")],
|
||||
execute(c, p) {
|
||||
const bank_no = p[0];
|
||||
if (!(bank_no === 1 || bank_no === 0)) {
|
||||
throw new Error("TODO");
|
||||
}
|
||||
c.setBank(bank_no);
|
||||
|
||||
ISA.insertInstruction(0x2c, {
|
||||
name: "NoOp",
|
||||
desc: "No operation; do nothing",
|
||||
params: [],
|
||||
execute: () => {},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x2f, {
|
||||
name: "Halt and Catch Fire",
|
||||
desc: "Stops program execu..... Fire! FIRE EVERYWHERE!",
|
||||
params: [],
|
||||
execute(c, p, a) {
|
||||
a.dispatch(CpuEvent.Halt);
|
||||
},
|
||||
});
|
||||
|
||||
//
|
||||
// Comparison
|
||||
// 0x30 -> 0x3F
|
||||
//
|
||||
|
||||
ISA.insertInstruction(0x30, {
|
||||
name: "Equals",
|
||||
desc: "If byte in register (P2) equals byte in register (P3), set byte in register (P1) to true",
|
||||
params: [
|
||||
new RegisParam("Set this register to true"),
|
||||
new RegisParam("if this register and"),
|
||||
new RegisParam("this register are equal (else false)"),
|
||||
],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2, register_no_3] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
if (!isU3(register_no_3)) throw new Error("TODO");
|
||||
const truth = c.getRegister(register_no_2) === c.getRegister(register_no_3) ? 0x01 : 0x00;
|
||||
c.setRegister(register_no_1, truth);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x31, {
|
||||
name: "Equals",
|
||||
desc: "If byte in register (P2) equals constant byte (P3), set byte in register (P1) to true",
|
||||
params: [
|
||||
new RegisParam("Set this register to true"),
|
||||
new RegisParam("if this register and"),
|
||||
new ConstParam("this constant are equal (else false)"),
|
||||
],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2, constant_value] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
const truth = c.getRegister(register_no_2) === constant_value ? 0x01 : 0x00;
|
||||
c.setRegister(register_no_1, truth);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x32, {
|
||||
name: "Less Than",
|
||||
desc: "Sets register (P1) to true if value in register (P2) is less than the value in register (P3)",
|
||||
params: [
|
||||
new RegisParam("Set this register to true"),
|
||||
new RegisParam("if this register is less than"),
|
||||
new RegisParam("this register"),
|
||||
],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2, register_no_3] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
if (!isU3(register_no_3)) throw new Error("TODO");
|
||||
const truth = c.getRegister(register_no_2) < c.getRegister(register_no_3) ? 0x01 : 0x00;
|
||||
c.setRegister(register_no_1, truth);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x33, {
|
||||
name: "Less Than",
|
||||
desc: "Sets register (P1) to true if value in register (P2) is less than the constant value (P3)",
|
||||
params: [
|
||||
new RegisParam("Set this register to true"),
|
||||
new RegisParam("if this register is less than"),
|
||||
new ConstParam("this constant"),
|
||||
],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2, constant_value] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
const truth = c.getRegister(register_no_2) < constant_value ? 0x01 : 0x00;
|
||||
c.setRegister(register_no_1, truth);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x34, {
|
||||
name: "Greater Than",
|
||||
desc: "Sets register (P1) to true if value in register (P2) is greater than the value in register (P3)",
|
||||
params: [
|
||||
new RegisParam("Set this register to true"),
|
||||
new RegisParam("if this register is greater than"),
|
||||
new RegisParam("this register"),
|
||||
],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2, register_no_3] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
if (!isU3(register_no_3)) throw new Error("TODO");
|
||||
const truth = c.getRegister(register_no_2) > c.getRegister(register_no_3) ? 0x01 : 0x00;
|
||||
c.setRegister(register_no_1, truth);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x35, {
|
||||
name: "Greater than",
|
||||
desc: "Sets register (P1) to true if value in register (P2) is greater than the constant value (P3)",
|
||||
params: [
|
||||
new RegisParam("Set this register to true"),
|
||||
new RegisParam("if this register is greater than"),
|
||||
new ConstParam("this constant"),
|
||||
],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2, constant_value] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
const truth = c.getRegister(register_no_2) > constant_value ? 0x01 : 0x00;
|
||||
c.setRegister(register_no_1, truth);
|
||||
},
|
||||
});
|
||||
|
||||
//
|
||||
// Logic / Bitwise
|
||||
// 0x40 -> 0x4F
|
||||
//
|
||||
|
||||
ISA.insertInstruction(0x40, {
|
||||
name: "Bitwise OR",
|
||||
desc: "Sets each bit in register (P1) to its OR with the respective bit in register (P2)",
|
||||
params: [new RegisParam(""), new RegisParam("")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
const new_value = c.getRegister(register_no_1) | c.getRegister(register_no_2);
|
||||
c.setRegister(register_no_1, new_value as u8);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x41, {
|
||||
name: "Bitwise OR",
|
||||
desc: "Sets each bit in register (P1) to its OR with the respective bit in constant value (P2)",
|
||||
params: [new RegisParam(""), new ConstParam("")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, constant_value] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
const new_value = c.getRegister(register_no_1) | constant_value;
|
||||
c.setRegister(register_no_1, new_value as u8);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x42, {
|
||||
name: "Bitwise AND",
|
||||
desc: "Sets each bit in register (P1) to its AND with the respective bit in register (P2)",
|
||||
params: [new RegisParam(""), new RegisParam("")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
const new_value = c.getRegister(register_no_1) & c.getRegister(register_no_2);
|
||||
c.setRegister(register_no_1, new_value as u8);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x43, {
|
||||
name: "Bitwise AND",
|
||||
desc: "Sets each bit in register (P1) to its AND with the respective bit in constant value (P2)",
|
||||
params: [new RegisParam(""), new ConstParam("")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, constant_value] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
const new_value = c.getRegister(register_no_1) & constant_value;
|
||||
c.setRegister(register_no_1, new_value as u8);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x44, {
|
||||
name: "Bitwise XOR",
|
||||
desc: "Sets each bit in register (P1) to its XOR with the respective bit in register (P2)",
|
||||
params: [new RegisParam(""), new RegisParam("")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
const new_value = c.getRegister(register_no_1) ^ c.getRegister(register_no_2);
|
||||
c.setRegister(register_no_1, new_value as u8);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x45, {
|
||||
name: "Bitwise XOR",
|
||||
desc: "Sets each bit in register (P1) to its XOR with the respective bit in constant value (P2)",
|
||||
params: [new RegisParam(""), new ConstParam("")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, constant_value] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
const new_value = c.getRegister(register_no_1) ^ constant_value;
|
||||
c.setRegister(register_no_1, new_value as u8);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x46, {
|
||||
name: "Left Bit Shift",
|
||||
desc: "Shifts each bit in register (P1) to the left by the amount in register (P2). Fills new bits with 0",
|
||||
params: [new RegisParam(""), new RegisParam("")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
const new_value = c.getRegister(register_no_1) << c.getRegister(register_no_2);
|
||||
c.setRegister(register_no_1, new_value as u8);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x47, {
|
||||
name: "Left Bit Shift",
|
||||
desc: "Shifts each bit in register (P1) to the left by the constant value (P2). Fills new bits with 0",
|
||||
params: [new RegisParam(""), new ConstParam("")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, constant_value] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
const new_value = c.getRegister(register_no_1) << constant_value;
|
||||
c.setRegister(register_no_1, new_value as u8);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x48, {
|
||||
name: "Right Bit Shift",
|
||||
desc: "Shifts each bit in register (P1) to the right by the amount in register (P2). Fills new bits with 0",
|
||||
params: [new RegisParam(""), new RegisParam("")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
const new_value = c.getRegister(register_no_1) >> c.getRegister(register_no_2);
|
||||
c.setRegister(register_no_1, new_value as u8);
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x49, {
|
||||
name: "Right Bit Shift",
|
||||
desc: "Shifts each bit in register (P1) to the right by the constant value (P2). Fills new bits with 0",
|
||||
params: [new RegisParam(""), new ConstParam("")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, constant_value] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
const new_value = c.getRegister(register_no_1) >> constant_value;
|
||||
c.setRegister(register_no_1, new_value as u8);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x4a, {
|
||||
name: "Bitwise NOT",
|
||||
desc: "Flips each bit in register (P1)",
|
||||
params: [new RegisParam("")],
|
||||
execute(c, p) {
|
||||
const register_no = p[0];
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
const new_value = ~c.getRegister(register_no);
|
||||
c.setRegister(register_no, new_value as u8);
|
||||
},
|
||||
});
|
||||
|
||||
//
|
||||
// Arithmetic
|
||||
// 0x50 -> 0x5F
|
||||
//
|
||||
|
||||
ISA.insertInstruction(0x50, {
|
||||
name: "Add",
|
||||
desc: "Adds to the byte in register (P1) with the value in register (P2)",
|
||||
params: [new RegisParam("set this register to"), new RegisParam("it's sum with the value in this register")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
const sum = c.getRegister(register_no_1) + c.getRegister(register_no_2);
|
||||
if (sum > 255) {
|
||||
c.setCarry(true);
|
||||
}
|
||||
c.setRegister(register_no_1, m256(sum));
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x51, {
|
||||
name: "Add",
|
||||
desc: "Adds to the byte in register (P1) with the value in register (P2)",
|
||||
params: [new RegisParam("set this register to"), new ConstParam("it's sum with this constant")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, constant_value] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
const sum = c.getRegister(register_no_1) + constant_value;
|
||||
if (sum > 255) c.setCarry(true);
|
||||
c.setRegister(register_no_1, m256(sum));
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x52, {
|
||||
name: "Add",
|
||||
desc: "Subtracts from the value in register (P1) by the value in register (P2)",
|
||||
params: [new RegisParam("set this register to"), new RegisParam("it's difference with the value in this register")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, register_no_2] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
if (!isU3(register_no_2)) throw new Error("TODO");
|
||||
const difference = c.getRegister(register_no_1) - c.getRegister(register_no_2);
|
||||
if (difference < 0) {
|
||||
c.setCarry(true);
|
||||
}
|
||||
c.setRegister(register_no_1, m256(difference));
|
||||
},
|
||||
});
|
||||
ISA.insertInstruction(0x53, {
|
||||
name: "Add",
|
||||
desc: "Subtracts from the value in register (P1) by the constant value (P2)",
|
||||
params: [new RegisParam("set this register to"), new ConstParam("it's difference with this constant")],
|
||||
execute(c, p) {
|
||||
const [register_no_1, constant_value] = p;
|
||||
if (!isU3(register_no_1)) throw new Error("TODO");
|
||||
const difference = c.getRegister(register_no_1) + constant_value;
|
||||
if (difference < 0) c.setCarry(true);
|
||||
c.setRegister(register_no_1, m256(difference));
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x5e, {
|
||||
name: "Increment",
|
||||
desc: "Increments the value within register (P1) by 1",
|
||||
params: [new RegisParam("register to be incremented")],
|
||||
execute(c, p) {
|
||||
const register_no = p[0];
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
const current_value = c.getRegister(register_no);
|
||||
const incremented = current_value + 1;
|
||||
if (incremented > 255) c.setCarry(true);
|
||||
c.setRegister(register_no, m256(incremented));
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0x5f, {
|
||||
name: "Decrement",
|
||||
desc: "Decrements the value within register (P1) by 1",
|
||||
params: [new RegisParam("register to be decremented")],
|
||||
execute(c, p) {
|
||||
const register_no = p[0];
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
const current_value = c.getRegister(register_no);
|
||||
const decremented = current_value - 1;
|
||||
if (decremented < 0) c.setCarry(true);
|
||||
c.setRegister(register_no, m256(decremented) as u8);
|
||||
},
|
||||
});
|
||||
|
||||
//
|
||||
// IO
|
||||
// 0xF0 -> 0xFF
|
||||
//
|
||||
|
||||
ISA.insertInstruction(0xf0, {
|
||||
name: "PrintASCII",
|
||||
desc: "Prints the ASCII byte in register (P1) to console",
|
||||
params: [new RegisParam("Register to print from")],
|
||||
execute(c, p, a) {
|
||||
const register_no = p[0];
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
const asciiByte = c.getRegister(register_no);
|
||||
|
||||
const char = String.fromCharCode(asciiByte);
|
||||
a.dispatch(CpuEvent.Print, char);
|
||||
},
|
||||
});
|
||||
|
||||
ISA.insertInstruction(0xf1, {
|
||||
name: "Print",
|
||||
desc: "Prints the byte in register (P1) to console as base 10",
|
||||
params: [new RegisParam("Register to print from")],
|
||||
execute(c, p, a) {
|
||||
const register_no = p[0];
|
||||
if (!isU3(register_no)) throw new Error("TODO");
|
||||
const byte = c.getRegister(register_no);
|
||||
|
||||
a.dispatch(CpuEvent.Print, byte.toString(10));
|
||||
},
|
||||
});
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
* @copyright Alexander Bass 2024
|
||||
* @license GPL-3.0
|
||||
*/
|
||||
import { format_hex } from "./etc";
|
||||
import { Instruction, InstructionSet } from "./instructionSet";
|
||||
import { format_hex, in_range } from "./etc";
|
||||
import { InstrCategory, Instruction, InstructionSet, ParameterType, ParamType } from "./instructionSet";
|
||||
import { u8 } from "./num.js";
|
||||
|
||||
export function generate_isa(iset: InstructionSet): string {
|
||||
|
@ -17,12 +17,43 @@ export function generate_isa(iset: InstructionSet): string {
|
|||
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;
|
||||
|
||||
for (const instruction of instructions) {
|
||||
const cat = iset.category_ranges.find((i) => in_range(instruction[0], i.start, i.end));
|
||||
if (cat === undefined) {
|
||||
throw new Error("Instruction found which is not part of category");
|
||||
}
|
||||
if (current_category !== cat) {
|
||||
output_string += `-- ${cat.name.toUpperCase()} --\n`;
|
||||
current_category = cat;
|
||||
}
|
||||
const hex_code = format_hex(instruction[0]);
|
||||
|
||||
const short_description = instruction[1].name.padEnd(max_instr_name_len, " ");
|
||||
const parameter_count = instruction[1].params.length;
|
||||
const parameters = parameter_description(instruction[1].params);
|
||||
const description = instruction[1].desc;
|
||||
output_string += `0x${hex_code}: ${short_description} - ${parameter_count} Parameter - ${description}\n`;
|
||||
output_string += `0x${hex_code}: ${short_description}`;
|
||||
if (parameters.length !== 0) {
|
||||
output_string += ` -${parameters}- `;
|
||||
} else {
|
||||
output_string += " - ";
|
||||
}
|
||||
output_string += `${description}\n`;
|
||||
}
|
||||
return output_string;
|
||||
}
|
||||
|
||||
function parameter_description(params: Array<ParameterType>): string {
|
||||
let str = "";
|
||||
if (params.length !== 0) {
|
||||
str += " ";
|
||||
}
|
||||
for (const p of params) {
|
||||
const p_map = { [ParamType.Const]: "C", [ParamType.Memory]: "M", [ParamType.Register]: "R" };
|
||||
const char = p_map[p.type];
|
||||
str += char;
|
||||
str += " ";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,13 @@ export type u4 = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14
|
|||
|
||||
export const m256 = (number: number): u8 => (number % 256) as u8;
|
||||
|
||||
export function isU2(n: number): n is u2 {
|
||||
if (n < 4 && n >= 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a number is a u3 type (unsigned 3-bit integer).
|
||||
* Does not check for non integers
|
||||
|
|
|
@ -52,7 +52,7 @@ body {
|
|||
"title . . . ."
|
||||
"title . . . printout "
|
||||
"title . . . printout "
|
||||
". buttons buttons buttons buttons";
|
||||
". buttons buttons . .";
|
||||
#memory {
|
||||
grid-column: 2/4;
|
||||
grid-row: 2/6;
|
||||
|
@ -198,6 +198,53 @@ label.button:hover {
|
|||
|
||||
#controls_bar {
|
||||
grid-area: buttons;
|
||||
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
input[type="range"] {
|
||||
background-color: transparent;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
margin: 18px 0;
|
||||
// width: 100%;
|
||||
}
|
||||
input[type="range"]:focus {
|
||||
outline: none;
|
||||
}
|
||||
// 2024 and we still have to do this
|
||||
input[type="range"]::-webkit-slider-runnable-track {
|
||||
height: 0.5em;
|
||||
cursor: pointer;
|
||||
background: yellow;
|
||||
border-radius: 0px;
|
||||
}
|
||||
input[type="range"]::-webkit-slider-thumb {
|
||||
border: 4px solid yellow;
|
||||
background-color: black;
|
||||
height: 42px;
|
||||
width: 20px;
|
||||
border-radius: 0px;
|
||||
cursor: pointer;
|
||||
margin-top: -18px;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
input[type="range"]:focus::-webkit-slider-runnable-track {
|
||||
background: yellow;
|
||||
}
|
||||
input[type="range"]::-moz-range-track {
|
||||
height: 0.5em;
|
||||
cursor: pointer;
|
||||
background: yellow;
|
||||
border-radius: 0px;
|
||||
}
|
||||
input[type="range"]::-moz-range-thumb {
|
||||
border: 4px solid yellow;
|
||||
background-color: black;
|
||||
height: 36px;
|
||||
width: 16px;
|
||||
border-radius: 0px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
22
src/ui/bankIndicator.ts
Normal file
22
src/ui/bankIndicator.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { UiEventHandler, CpuEventHandler, CpuEvent } from "../events";
|
||||
import { u1, u2 } from "../num";
|
||||
import { UiComponent } from "./uiComponent";
|
||||
|
||||
class BankIndicator implements UiComponent {
|
||||
element: HTMLElement;
|
||||
events: UiEventHandler;
|
||||
constructor(element: HTMLElement, events: UiEventHandler) {
|
||||
this.element = element;
|
||||
this.events = events;
|
||||
}
|
||||
|
||||
reset(): void {}
|
||||
|
||||
select_bank(bank_no: u2): void {}
|
||||
|
||||
init_cpu_events(c: CpuEventHandler): void {
|
||||
c.listen(CpuEvent.SwitchBank, ({ bank }) => {
|
||||
this.select_bank(bank);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -31,7 +31,8 @@ export class MemoryView extends CelledViewer implements UiComponent {
|
|||
}
|
||||
|
||||
init_cpu_events(c: CpuEventHandler): void {
|
||||
c.listen(CpuEvent.MemoryAccessed, ({ address, value }) => {
|
||||
c.listen(CpuEvent.MemoryAccessed, ({ address, bank, value }) => {
|
||||
if (bank !== 0) return;
|
||||
if (this.last_accessed_cell !== address) {
|
||||
if (this.last_accessed_cell !== null) {
|
||||
this.remove_cell_class(this.last_accessed_cell, "last_access");
|
||||
|
|
|
@ -43,12 +43,6 @@ export class Screen implements UiComponent {
|
|||
|
||||
setPixel(x: u4, y: u4, value: u8): void {
|
||||
const point: [number, number] = [x * this.scale[0], y * this.scale[1]];
|
||||
// const RED_SCALE = 255 / 2 ** 2;
|
||||
// const GREEN_SCALE = 255 / 2 ** 2;
|
||||
// const BLUE_SCALE = 255 / 2 ** 2;
|
||||
// const red = ((value >> 4) & 0b11) * RED_SCALE;
|
||||
// const green = ((value >> 2) & 0b11) * GREEN_SCALE;
|
||||
// const blue = (value & 0b11) * BLUE_SCALE;
|
||||
|
||||
const RED_SCALE = 255 / 2 ** 3;
|
||||
const GREEN_SCALE = 255 / 2 ** 3;
|
||||
|
@ -58,7 +52,6 @@ export class Screen implements UiComponent {
|
|||
const blue = (value & 0b11) * BLUE_SCALE;
|
||||
|
||||
const color = `rgb(${red},${green},${blue})`;
|
||||
console.log(x, y, value, color);
|
||||
this.ctx.fillStyle = color;
|
||||
this.ctx.fillRect(...point, ...this.scale);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue