diff --git a/src/app/page.tsx b/src/app/page.tsx
index 0fa2562..8b37d93 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -1,9 +1,9 @@
-import Emulator from "@/components/emulator/emulator";
-
-export default function Home() {
- return (
-
-
-
- );
-}
+import EmulatorComponent from "@/components/emulator/emulatorComponent";
+
+export default function Home() {
+ return (
+
+
+
+ );
+}
diff --git a/src/app/ui/emulator.module.css b/src/app/ui/emulator.module.css
index 89d94f0..3d6060a 100644
--- a/src/app/ui/emulator.module.css
+++ b/src/app/ui/emulator.module.css
@@ -42,7 +42,7 @@
height: auto;
align-content: center;
padding: 10px 0 10px 5px;
- margin: 2px;
+ margin: 2px !important;
}
.register {
diff --git a/src/components/canvas/canvas.tsx b/src/components/canvas/canvas.tsx
index 296be6e..9ab5e60 100644
--- a/src/components/canvas/canvas.tsx
+++ b/src/components/canvas/canvas.tsx
@@ -1,53 +1,55 @@
-import React, {useEffect, useRef, useState} from 'react'
-
-interface ICanvasProps {
- className?: string
- vram: Array
-}
-
-export default function Canvas(props: ICanvasProps) {
- const [vram, setVram] = useState(Array(32768));
- const canvasRef = useRef(null);
-
- useEffect(() => {
- setVram(props.vram);
- }, [props.vram])
-
- useEffect(() => {
- if(vram[0] != undefined) {
- const canvas = canvasRef.current;
-
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-expect-error
- const context = canvas.getContext('2d');
-
- context.fillStyle = '#ffffff';
- context.fillRect(0, 0, context.canvas.width, context.canvas.height);
-
- //each addr contains 2 pixels, with 8bpp
- //xxx xxx xx | xxx xxx xx
-
- let pixel = 0;
-
- for(let x = 0; x < 120; x++){
- for(let y = 0; y < 160; y++) {
- const data = vram[pixel];
-
- const red = Math.floor(((data & 224) / 7) * 255);
- const green = Math.floor(((data & 28) / 7) * 255);
- const blue = Math.floor(((data & 3) / 3) * 255);
-
- const hexRed = ('00' + red.toString(16)).slice(-2);
- const hexGreen = ('00' + green.toString(16)).slice(-2);
- const hexBlue = ('00' + blue.toString(16)).slice(-2);
-
- context.fillStyle = '#'+ hexRed + hexGreen + hexBlue;
- context.fillRect(y*2, x*2, 2, 2)
- pixel++;
- }
- }
- }
- }, [vram])
-
- return
+import React, {useEffect, useRef, useState} from 'react'
+
+interface ICanvasProps {
+ className?: string
+ memory: Array
+}
+
+export default function Canvas(props: ICanvasProps) {
+ const [memory, setMemory] = useState(Array(65535));
+ const canvasRef = useRef(null);
+
+ useEffect(() => {
+ setMemory(props.memory);
+ }, [props.memory])
+
+ useEffect(() => {
+ if(memory[0] != undefined) {
+ const canvas = canvasRef.current;
+
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-expect-error
+ const context = canvas.getContext('2d');
+
+ context.fillStyle = '#ffffff';
+ context.fillRect(0, 0, context.canvas.width, context.canvas.height);
+
+ //each addr contains 2 pixels, with 8bpp
+ //xxx xxx xx | xxx xxx xx
+
+ let pixel = 0;
+
+ const vramLocationOffset = 32768;
+
+ for(let x = 0; x < 120; x++){
+ for(let y = 0; y < 160; y++) {
+ const data = memory[pixel + vramLocationOffset];
+
+ const red = Math.floor(((data & 224) / 7) * 255);
+ const green = Math.floor(((data & 28) / 7) * 255);
+ const blue = Math.floor(((data & 3) / 3) * 255);
+
+ const hexRed = ('00' + red.toString(16)).slice(-2);
+ const hexGreen = ('00' + green.toString(16)).slice(-2);
+ const hexBlue = ('00' + blue.toString(16)).slice(-2);
+
+ context.fillStyle = '#'+ hexRed + hexGreen + hexBlue;
+ context.fillRect(y*2, x*2, 2, 2)
+ pixel++;
+ }
+ }
+ }
+ }, [memory])
+
+ return
}
\ No newline at end of file
diff --git a/src/components/emulator/emulator.tsx b/src/components/emulator/emulatorComponent.tsx
similarity index 53%
rename from src/components/emulator/emulator.tsx
rename to src/components/emulator/emulatorComponent.tsx
index 07bef10..7d17797 100644
--- a/src/components/emulator/emulator.tsx
+++ b/src/components/emulator/emulatorComponent.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable react-hooks/exhaustive-deps */
"use client";
import {
@@ -17,50 +18,14 @@ import styles from '@/app/ui/emulator.module.css';
import {useEffect, useState} from "react";
import Canvas from "@/components/canvas/canvas";
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
-import FastForwardIcon from '@mui/icons-material/FastForward';
import PauseIcon from '@mui/icons-material/Pause';
import StopIcon from '@mui/icons-material/Stop';
import MoveDownIcon from '@mui/icons-material/MoveDown';
-import ViewInstruction from "@/components/viewInstruction/viewInstruction";
-
-export enum instruction {
- NOP,
- MV,
- LI,
- LD,
- LDIND,
- LDIO,
- STIO,
- ADD,
- SUB,
- NEG,
- XOR,
- NAND,
- AND,
- OR,
- NOT,
- J,
- JNZ,
- JIMM,
- ADDI,
- ST,
- JZ,
- JN
-}
-
-export default function Emulator() {
- const [reg_PC, setReg_PC] = useState(0);
- const [reg_r0, setReg_r0] = useState(0);
- const [reg_r1, setReg_r1] = useState(0);
- const [reg_r2, setReg_r2] = useState(0);
- const [reg_r3, setReg_r3] = useState(0);
- const [reg_r4, setReg_r4] = useState(0);
- const [reg_r5, setReg_r5] = useState(0);
- const [reg_r6, setReg_r6] = useState(0);
- const [reg_r7, setReg_r7] = useState(0);
-
- const [ram, setRam] = useState(Array(32768));
- const [vram, setvram] = useState(Array(32768));
+import ViewInstructionComponent from "@/components/viewInstruction/viewInstructionComponent";
+import {CPUInit, CPUStep, ICPUState, Instruction, Register, verify16BitNum} from "@/emulation/emulator";
+
+export default function EmulatorComponent() {
+ const [CPUState, setCPUState] = useState({PC: 0, memory: Array(65535), registers: Array(8)});
const [clockSpeed, setClockSpeed] = useState("100");
const [clockSpeedModifier, setClockSpeedModifier] = useState(0);
@@ -80,76 +45,40 @@ export default function Emulator() {
const [showHexValues, setShowHexValues] = useState(false);
const [cpuTimeout, setCpuTimeout] = useState();
const [canvasUpdateKey, setCanvasUpdateKey] = useState();
- const [instructionsToRun, setInstructionsToRun] = useState(1000);
- const [advancedView, setAdvancedView] = useState(false);
-
- const setRandomRam = () => {
- setLoaded(false);
- setRunning(false);
- setAdvancedView(false);
-
- const newVram = Array(32768);
-
- for(let i = 0; i < 32768; i++) {
- newVram[i] = Math.floor(Math.random() * 256);
- }
-
- const emptyRam = Array(32768);
-
- for(let i = 0; i < 32768; i++) {
- emptyRam[i] = Math.floor(Math.random() * 65536);
- }
-
- setvram(newVram);
- setRam(emptyRam);
-
- setReg_PC(0);
- setReg_r0(Math.floor(Math.random() * 65536));
- setReg_r1(Math.floor(Math.random() * 65536));
- setReg_r2(Math.floor(Math.random() * 65536));
- setReg_r3(Math.floor(Math.random() * 65536));
- setReg_r4(Math.floor(Math.random() * 65536));
- setReg_r5(Math.floor(Math.random() * 65536));
- setReg_r6(Math.floor(Math.random() * 65536));
- setReg_r7(Math.floor(Math.random() * 65536));
- }
useEffect(() => {
- if(loaded || running) {
- setRandomRam();
- }
-
if(dynamicAssemble)
assemble()
}, [dynamicAssemble, code])
useEffect(() => {
- console.log("Assembler finished.")
- }, [assemblerComplete])
+ setCPUState(CPUInit());
- useEffect(() => {
- setRandomRam()
setInterval(() => {
setCanvasUpdateKey(Date.now().toString());
}, 1000)
console.log("Running 100 tests to determine clock modifier...");
+
let sum = 0;
for(let i = 0; i < 100; i++) {
- ram[0] = 8; //LI
- const clockStart = window.performance.now()
- cpuStep(1);
- const clockStop = window.performance.now()
+ const state: ICPUState = {
+ PC: 0,
+ registers: [0, 0, 0, 0, 0, 0, 0, 0],
+ memory: [8, 1]
+ };
+
+ const clockStart = window.performance.now();
+ CPUStep(state, 1);
+ const clockStop = window.performance.now();
- sum += (clockStop - clockStart) * 1000;
+ sum += (clockStop - clockStart);
}
- console.log("Tests complete. Result:", sum / 100);
+ console.log("Tests complete. Result:", sum / 100, "ms");
setClockSpeedModifier(sum / 100);
-
- setRandomRam()
}, []);
const recognisedArgType = [
@@ -177,269 +106,6 @@ export default function Emulator() {
['r', 'a']
]
- const getReg = (index: number): number => {
- switch(index) {
- default:
- return 12345; //error
- case 0:
- return reg_r0;
- case 1:
- return reg_r1;
- case 2:
- return reg_r2;
- case 3:
- return reg_r3;
- case 4:
- return reg_r4;
- case 5:
- return reg_r5;
- case 6:
- return reg_r6;
- case 7:
- return reg_r7;
- }
- }
-
- const setReg = (index: number, value: number) => {
- switch(index) {
- case 0:
- setReg_r0(value);
- break;
- case 1:
- setReg_r1(value);
- break;
- case 2:
- setReg_r2(value);
- break;
- case 3:
- setReg_r3(value);
- break;
- case 4:
- setReg_r4(value);
- break;
- case 5:
- setReg_r5(value);
- break;
- case 6:
- setReg_r6(value);
- break;
- case 7:
- setReg_r7(value);
- break;
- }
- }
-
- const cpuStep = (steps: number) => {
- const memory = ram;
- const videoMemory = vram;
-
- let PC = reg_PC;
- const regs = [
- reg_r0,
- reg_r1,
- reg_r2,
- reg_r3,
- reg_r4,
- reg_r5,
- reg_r6,
- reg_r7
- ]
-
- for(let step = 0; step < steps; step++) {
- if(PC < 0)
- PC = 65535 + PC + 1
-
- // fetch
- const currentInstruction = ('000000000000000' + (PC > 32767? videoMemory[PC - 32768] : memory[PC])?.toString(2)).slice(-16);
-
- //decode
- const opcode = parseInt(currentInstruction.substring(9, 14), 2);
- const srcA = parseInt(currentInstruction.substring(6, 9).split("").reverse().join(""), 2)
- const srcB = parseInt(currentInstruction.substring(3, 6).split("").reverse().join(""), 2)
- const dest = parseInt(currentInstruction.substring(0, 3).split("").reverse().join(""), 2)
-
- let didJump = false;
-
- // execute
- switch(opcode as instruction) {
- case undefined:
- case instruction.NOP: {
- //%PC = %PC + 1
- break;
- }
- case instruction.MV: {
- //%dest = %srcA
- regs[dest] = regs[srcA];
- //setReg(dest, getReg(srcA));
- break;
- }
- case instruction.LI: {
- //%dest = $imm
- const addr = ++PC;
- regs[dest] = addr > 32767? videoMemory[addr - 32768] : memory[addr];
- //setReg(dest, memory[PC + 1]);
- break;
- }
- case instruction.LD: {
- //%dest = memory[$imm]
- const addr = ++PC;
- const imm = addr > 32767? videoMemory[addr - 32768] : memory[addr];
-
- regs[dest] = imm > 32767? videoMemory[imm - 32768] : memory[imm];
- //setReg(dest, memory[memory[PC + 1]]);
- break;
- }
- case instruction.LDIND: {
- //%dest = memory[%srcA]
- const addr = regs[srcA];
-
- regs[dest] = addr > 32767? videoMemory[addr - 32768] : memory[addr];
- //setReg(dest, memory[getReg(srcA)]);
- break;
- }
- case instruction.LDIO: {
- //%dest = memory[%srcA + $imm]
- let addr = regs[srcA] + (++PC > 32767? videoMemory[PC - 32768] : memory[PC]);
- addr = addr > 65535? addr - 65536 : addr;
-
- regs[dest] = addr > 32767? videoMemory[addr - 32768] : memory[addr];
- //setReg(dest, memory[getReg(srcA) + memory[PC + 1]]);
- break;
- }
- case instruction.STIO: {
- //memory[%srcA + $imm] = %srcB
- const val = regs[srcB];
- const addr = regs[srcA] + (++PC > 32767? videoMemory[PC - 32768] : memory[PC]);
-
- if(addr > 32767) {
- videoMemory[addr - 32768] = val;
- } else {
- memory[addr] = val;
- }
- break;
- }
- case instruction.ADD: {
- //%dest = %srcA + %srcB
- const result = regs[srcA] + regs[srcB];
- regs[dest] = result > 65535 ? result - 65536 : result
- //setReg(dest, result > 65536 ? result % 65536 : result);
- break;
- }
- case instruction.SUB: {
- //%dest = %srcA – %srcB
- const result = regs[srcA] - regs[srcB];
- regs[dest] = result < 0 ? 65536 + result : result;
- //setReg(dest, result < 0 ? 65536 - result : result);
- break;
- }
- case instruction.NEG: {
- //%dest = ~(%srcA) + 1
- regs[dest] = parseInt(('0000000000000000' + ((regs[srcA]? regs[srcA] : 0).toString(2))).slice(-16).split("").map((el) => el === '0' ? '1' : '0').join(""), 2) + 1;
- //setReg(dest, parseInt(('0000000000000000' + (getReg(srcA).toString(2))).slice(-16).split("").map((el) => el === '0' ? '1' : '0').join(""), 2) + 1);
- break;
- }
- case instruction.XOR: {
- //%dest = %srcA XOR %srcB
- regs[dest] = regs[srcA] ^ regs[srcB];
- //setReg(dest, getReg(srcA) ^ getReg(srcB));
- break;
- }
- case instruction.NAND: {
- //%dest = %srcA NAND %srcB
- regs[dest] = parseInt((regs[srcA] & regs[srcB]).toString(2).split("").map((el) => el === '0' ? '1' : '0').join(""), 2);
- //setReg(dest, parseInt(('0000000000000000' + (getReg(srcA) & getReg(srcB)).toString(2)).slice(-16).split("").map((el) => el === '0' ? '1' : '0').join(""), 2));
- break;
- }
- case instruction.AND: {
- //%dest = %srcA AND %srcB
- regs[dest] = regs[srcA] & regs[srcB];
- //setReg(dest, getReg(srcA) & getReg(srcB));
- break;
- }
- case instruction.OR: {
- //%dest = %srcA OR %srcB
- regs[dest] = regs[srcA] | regs[srcB];
- setReg(dest, getReg(srcA) | getReg(srcB));
- break;
- }
- case instruction.NOT: {
- //%dest = ~(%srcA)
- regs[dest] = parseInt(('0000000000000000' + ((regs[srcA]? regs[srcA] : 0).toString(2))).slice(-16).split("").map((el) => el === '0' ? '1' : '0').join(""), 2);
- //setReg(dest, parseInt(('0000000000000000' + (getReg(srcA).toString(2))).slice(-16).split("").map((el) => el === '0' ? '1' : '0').join(""), 2));
- break;
- }
- case instruction.J: {
- //%PC = %srcA
- PC = regs[srcA];
- didJump = true;
- //jumpaddr = getReg(srcA);
- break;
- }
- case instruction.JNZ: {
- //%PC = %srcA == 0 ? %PC + 1 : $imm
- PC = regs[srcA] == 0 ? PC + 2 : (++PC > 32767? videoMemory[PC - 32768] : memory[PC]);
- didJump = true;
- //jumpaddr = getReg(srcA) == 0 ? -1 : memory[PC + 1];
- break;
- }
- case instruction.JIMM: {
- //%PC = $imm
- PC = ++PC > 32767? videoMemory[PC - 32768] : memory[PC];
- didJump = true;
- break;
- }
- case instruction.ADDI: {
- //%dest = %srcA + $imm
- const result = regs[srcA] + (++PC > 32767? videoMemory[PC - 32768] : memory[PC]);
- regs[dest] = result > 65535 ? result - 65536 : result;
- //setReg(dest, getReg(srcA) + memory[PC + 1]);
- //PC 5
- break;
- }
- case instruction.ST: {
- //memory[%imm] = %srcA
- const val = regs[srcA];
- const addr = ++PC > 32767? videoMemory[PC - 32768] : memory[PC];
-
- if(addr > 32767) {
- videoMemory[addr - 32768] = val;
- } else {
- memory[addr] = val;
- }
- break;
- }
- case instruction.JZ: {
- //%PC = %srcA != 0 ? %PC + 1 : $imm
- PC = regs[srcA] != 0 ? PC + 2 : (++PC > 32767? videoMemory[PC - 32768] : memory[PC]);
- didJump = true;
- break;
- }
- case instruction.JN: {
- //%PC = %srcA != 0 ? %PC + 1 : $imm
- PC = regs[srcA] < 32768 ? PC + 2 : (++PC > 32767? videoMemory[PC - 32768] : memory[PC]);
- didJump = true;
- break;
- }
- }
-
- PC = didJump ? PC : PC + 1;
-
- if(PC > 65535) {
- PC = PC - 65536;
- }
- }
-
- setReg_PC(PC);
- setRam(memory);
- setvram(videoMemory);
-
- for(let reg = 0; reg < 8; reg++) {
- setReg(reg, regs[reg])
- }
-
- setCompletedExecution(!completedExecution);
- }
-
const runProgram = () => {
if(running) {
setRunning(false);
@@ -448,12 +114,10 @@ export default function Emulator() {
const targetClock = parseFloat(clockSpeed) * 1000;
console.log("Target clock speed:", targetClock + "Hz");
- console.log("Clock speed modifier:", clockSpeedModifier.toFixed(3)+"microseconds");
+ console.log("Clock speed modifier:", clockSpeedModifier.toFixed(3)+"ms");
- const clocksPerMs = 100000/clockSpeedModifier;
- const stepsPerMs = targetClock/clocksPerMs;
- console.log("--> Clocks per millisecond:", 1000/clockSpeedModifier);
- console.log("--> Steps per millisecond:", stepsPerMs);
+ const stepsPerMs = targetClock/1000;
+ console.log("steps", stepsPerMs)
if(stepsPerMs < 1) {
setMillis(1000 / targetClock);
@@ -468,7 +132,8 @@ export default function Emulator() {
}
const stopProgram = () => {
- setRandomRam();
+ setCPUState(CPUInit());
+
setRunning(false);
setLoaded(false);
setRunning(false);
@@ -478,26 +143,24 @@ export default function Emulator() {
if(loaded && running) {
setRunning(false);
} else if(!loaded) {
- setRandomRam();
+ setCPUState(CPUInit());
setLoaded(true);
assemble()
}
- cpuStep(1);
+ const state = CPUStep(CPUState, 1);
+ setCPUState(state);
}
- const fastProgram = () => {
- setTimeout(() => {
- cpuStep(instructionsToRun);
-
- setCanvasUpdateKey(Date.now().toString())
- }, 0);
+ const CPUStepRunning = (steps: number) => {
+ setCPUState(CPUStep(CPUState, steps));
+ setCompletedExecution(!completedExecution);
}
useEffect(() => {
if(running) {
setCpuTimeout(setTimeout(() => {
- cpuStep(stepsPerMillis)
+ CPUStepRunning(stepsPerMillis);
}, millis));
} else {
clearInterval(cpuTimeout);
@@ -524,6 +187,7 @@ export default function Emulator() {
const usedLablesLine: number[] = [];
const usedLabelsAddr: number[] = [];
+
let byteCount = 0;
for(let line = 0; line < lines.length; line++) {
@@ -605,14 +269,14 @@ export default function Emulator() {
if(val < 0)
val = 65536 + val
- ram[addr++] = val;
+ CPUState.memory[addr++] = val;
byteCount += 2;
continue;
}
- const inst = instruction[tokens[0].toUpperCase() as keyof typeof instruction]
+ const inst = Instruction[tokens[0].toUpperCase() as keyof typeof Instruction]
// Check instruction is valid
if(inst === undefined) {
@@ -621,93 +285,95 @@ export default function Emulator() {
return;
}
+ byteCount += 2;
+
switch(inst) {
- case instruction.NOP: {
- ram[addr++] = 0 << 2;
+ case Instruction.NOP: {
+ CPUState.memory[addr++] = 0 << 2;
break;
}
- case instruction.MV: {
- ram[addr++] = 1 << 2;
+ case Instruction.MV: {
+ CPUState.memory[addr++] = 1 << 2;
break;
}
- case instruction.LI: {
- ram[addr++] = 2 << 2;
+ case Instruction.LI: {
+ CPUState.memory[addr++] = 2 << 2;
break;
}
- case instruction.LD: {
- ram[addr++] = 3 << 2;
+ case Instruction.LD: {
+ CPUState.memory[addr++] = 3 << 2;
break;
}
- case instruction.LDIND: {
- ram[addr++] = 4 << 2;
+ case Instruction.LDIND: {
+ CPUState.memory[addr++] = 4 << 2;
break;
}
- case instruction.LDIO: {
- ram[addr++] = 5 << 2;
+ case Instruction.LDIO: {
+ CPUState.memory[addr++] = 5 << 2;
break;
}
- case instruction.STIO: {
- ram[addr++] = 6 << 2;
+ case Instruction.STIO: {
+ CPUState.memory[addr++] = 6 << 2;
break;
}
- case instruction.ADD: {
- ram[addr++] = 7 << 2;
+ case Instruction.ADD: {
+ CPUState.memory[addr++] = 7 << 2;
break;
}
- case instruction.SUB: {
- ram[addr++] = 8 << 2;
+ case Instruction.SUB: {
+ CPUState.memory[addr++] = 8 << 2;
break;
}
- case instruction.NEG: {
- ram[addr++] = 9 << 2;
+ case Instruction.NEG: {
+ CPUState.memory[addr++] = 9 << 2;
break;
}
- case instruction.XOR: {
- ram[addr++] = 10 << 2;
+ case Instruction.XOR: {
+ CPUState.memory[addr++] = 10 << 2;
break;
}
- case instruction.NAND: {
- ram[addr++] = 11 << 2;
+ case Instruction.NAND: {
+ CPUState.memory[addr++] = 11 << 2;
break;
}
- case instruction.AND: {
- ram[addr++] = 12 << 2;
+ case Instruction.AND: {
+ CPUState.memory[addr++] = 12 << 2;
break;
}
- case instruction.OR: {
- ram[addr++] = 13 << 2;
+ case Instruction.OR: {
+ CPUState.memory[addr++] = 13 << 2;
break;
}
- case instruction.NOT: {
- ram[addr++] = 14 << 2;
+ case Instruction.NOT: {
+ CPUState.memory[addr++] = 14 << 2;
break;
}
- case instruction.J: {
- ram[addr++] = 15 << 2;
+ case Instruction.J: {
+ CPUState.memory[addr++] = 15 << 2;
break;
}
- case instruction.JNZ: {
- ram[addr++] = 16 << 2;
+ case Instruction.JNZ: {
+ CPUState.memory[addr++] = 16 << 2;
break;
}
- case instruction.JIMM: {
- ram[addr++] = 17 << 2;
+ case Instruction.JIMM: {
+ CPUState.memory[addr++] = 17 << 2;
break;
}
- case instruction.ADDI: {
- ram[addr++] = 18 << 2;
+ case Instruction.ADDI: {
+ CPUState.memory[addr++] = 18 << 2;
break;
}
- case instruction.ST: {
- ram[addr++] = 19 << 2;
+ case Instruction.ST: {
+ CPUState.memory[addr++] = 19 << 2;
break;
}
- case instruction.JZ: {
- ram[addr++] = 20 << 2;
+ case Instruction.JZ: {
+ CPUState.memory[addr++] = 20 << 2;
break;
}
- case instruction.JN: {
- ram[addr++] = 21 << 2;
+ case Instruction.JN: {
+ CPUState.memory[addr++] = 21 << 2;
break;
}
}
@@ -722,21 +388,21 @@ export default function Emulator() {
//addr += recognisedInstructionLength[opIndex];
const setRegisterSrcDest = (memoryAddr: number, reg: number, type: number) => {
- ram[memoryAddr] |= parseInt(('000' + reg.toString(2)).slice(-3).split("").reverse().join(""), 2) << (7 + type * 3);
+ CPUState.memory[memoryAddr] |= parseInt(('000' + reg.toString(2)).slice(-3).split("").reverse().join(""), 2) << (7 + type * 3);
}
const findRegisterTokenOffset = (i: number, token: number) => {
switch(inst) {
- case instruction.ST: {
+ case Instruction.ST: {
setRegisterSrcDest(addr - 2, i,token - 2);
break;
}
- case instruction.LDIO:
- case instruction.STIO:
- case instruction.J:
- case instruction.JZ:
- case instruction.JN:
- case instruction.JNZ: {
+ case Instruction.LDIO:
+ case Instruction.STIO:
+ case Instruction.J:
+ case Instruction.JZ:
+ case Instruction.JN:
+ case Instruction.JNZ: {
setRegisterSrcDest(addr - 1, i,token - 1);
break;
}
@@ -776,31 +442,6 @@ export default function Emulator() {
break;
}
- case 'i': {
- currentlyChecking = "IMM16";
-
- if(checkToken === "") {
- break;
- }
-
- let immVal = Number(checkToken.replace(",", ""))
-
- if(isNaN(immVal))
- break;
-
- if(immVal > 65536)
- break;
-
- error = false;
-
- if(immVal < 0)
- immVal = 65536 + immVal;
-
- ram[addr++] = immVal;
-
- break;
- }
-
case 'a': {
currentlyChecking = "IMM16LABEL"
@@ -823,9 +464,11 @@ export default function Emulator() {
if(immVal < 0)
immVal = 65536 + immVal;
- ram[addr++] = immVal;
+ CPUState.memory[addr++] = immVal;
}
+ byteCount += 2;
+
error = false;
break;
}
@@ -850,7 +493,7 @@ export default function Emulator() {
return;
} else {
- ram[usedLabelsAddr[i]] = labelsAddr[foundIndex];
+ CPUState.memory[usedLabelsAddr[i]] = labelsAddr[foundIndex];
}
}
@@ -907,28 +550,11 @@ export default function Emulator() {
size="small"
/>
- {advancedView? <>
- {" "}
-
-
-
- {" "}
-
- setInstructionsToRun(parseInt(e.target.value))}
- size="small"
- />
-
- > : "" }
{" "}
BBC state
Instruction
- 32767? vram[reg_PC - 32768] : ram[reg_PC]} immContent={reg_PC + 1 > 32767? vram[reg_PC - 32767] : ram[reg_PC + 1]} />
+
Registers
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
@@ -1006,7 +632,7 @@ export default function Emulator() {
PPU
-
diff --git a/src/components/viewInstruction/viewInstruction.tsx b/src/components/viewInstruction/viewInstructionComponent.tsx
similarity index 68%
rename from src/components/viewInstruction/viewInstruction.tsx
rename to src/components/viewInstruction/viewInstructionComponent.tsx
index fb82567..604d946 100644
--- a/src/components/viewInstruction/viewInstruction.tsx
+++ b/src/components/viewInstruction/viewInstructionComponent.tsx
@@ -2,14 +2,14 @@ import {TextField} from "@mui/material";
import {useEffect, useState} from "react";
import styles from '@/app/ui/viewInstruction.module.css';
-import {instruction} from "@/components/emulator/emulator";
+import {Instruction} from "@/emulation/emulator";
interface IViewInstructionProps {
memoryContent: number,
immContent: number
}
-export default function ViewInstruction(props: IViewInstructionProps) {
+export default function ViewInstructionComponent(props: IViewInstructionProps) {
const [decodedInstruction, setDecodedInstruction] = useState("");
useEffect(() => {
@@ -26,58 +26,58 @@ export default function ViewInstruction(props: IViewInstructionProps) {
const dest = "r" + parseInt(val.slice(0, 3).split("").reverse().join(("")), 2);
const imm = parseInt(('0000000000000000' + props.immContent.toString(2)).slice(-16), 2);
- let instn = instruction[parseInt(inst, 2)];
+ let instn = Instruction[parseInt(inst, 2)];
let theRest = " "
- switch(instruction[instn as keyof typeof instruction]) {
- case instruction.NOP: {
+ switch(Instruction[instn as keyof typeof Instruction]) {
+ case Instruction.NOP: {
break;
}
- case instruction.LDIND:
- case instruction.MV:
- case instruction.NEG:
- case instruction.NOT: {
+ case Instruction.LDIND:
+ case Instruction.MV:
+ case Instruction.NEG:
+ case Instruction.NOT: {
theRest += dest + ", " + srcA;
break;
}
- case instruction.LI:
- case instruction.LD: {
+ case Instruction.LI:
+ case Instruction.LD: {
theRest += dest + ", " + imm;
break;
}
- case instruction.STIO:
- case instruction.LDIO: {
+ case Instruction.STIO:
+ case Instruction.LDIO: {
theRest += srcA + ", " + srcB + ", " + imm;
break;
}
- case instruction.ADD:
- case instruction.SUB:
- case instruction.XOR:
- case instruction.NAND:
- case instruction.AND:
- case instruction.OR: {
+ case Instruction.ADD:
+ case Instruction.SUB:
+ case Instruction.XOR:
+ case Instruction.NAND:
+ case Instruction.AND:
+ case Instruction.OR: {
theRest += dest + ", " + srcA + ", " + srcB;
break;
}
- case instruction.J: {
+ case Instruction.J: {
theRest += srcA;
break;
}
- case instruction.JNZ:
- case instruction.JZ:
- case instruction.JN: {
+ case Instruction.JNZ:
+ case Instruction.JZ:
+ case Instruction.JN: {
theRest += srcA + ", " + imm;
break;
}
- case instruction.JIMM: {
+ case Instruction.JIMM: {
theRest += imm;
break;
}
- case instruction.ADDI: {
+ case Instruction.ADDI: {
theRest += dest + ", " + srcA + ", " + imm;
break;
}
- case instruction.ST: {
+ case Instruction.ST: {
theRest += imm + ", " + srcA;
break;
}
diff --git a/src/emulation/emulator.ts b/src/emulation/emulator.ts
new file mode 100644
index 0000000..b72a044
--- /dev/null
+++ b/src/emulation/emulator.ts
@@ -0,0 +1,229 @@
+export type Register = number;
+export type RegisterFile = Array;
+export type Memory = Array;
+
+export interface ICPUState {
+ PC: Register;
+ registers: RegisterFile;
+ memory: Memory;
+}
+
+export enum Instruction {
+ NOP,
+ MV,
+ LI,
+ LD,
+ LDIND,
+ LDIO,
+ STIO,
+ ADD,
+ SUB,
+ NEG,
+ XOR,
+ NAND,
+ AND,
+ OR,
+ NOT,
+ J,
+ JNZ,
+ JIMM,
+ ADDI,
+ ST,
+ JZ,
+ JN
+}
+
+export function CPUInit(): ICPUState {
+ const cpuState: ICPUState = {
+ PC: 0,
+ registers: [
+ Math.floor(Math.random() * 65535),
+ Math.floor(Math.random() * 65535),
+ Math.floor(Math.random() * 65535),
+ Math.floor(Math.random() * 65535),
+ Math.floor(Math.random() * 65535),
+ Math.floor(Math.random() * 65535),
+ Math.floor(Math.random() * 65535),
+ Math.floor(Math.random() * 65535)
+ ],
+ memory: Array(65535)
+ };
+
+ for (let i = 0; i < 65535; i++) {
+ cpuState.memory[i] = Math.floor(Math.random() * 65535);
+ }
+
+ return cpuState;
+}
+
+export function verify16BitNum(value: number): number { return value < 0 ? 65535 + value : value > 65535 ? value - 65536 : value; }
+
+export function CPUStep(state_0: ICPUState, steps: number): ICPUState {
+ const state: ICPUState = {
+ PC: state_0.PC,
+ registers: state_0.registers,
+ memory: state_0.memory
+ };
+
+ for(let step = 0; step < steps; step++)
+ {
+ // Fetch
+ const fetchedInstruction = ('0000000000000000' + state.memory[state.PC].toString(2)).slice(-16);
+
+ // Decode
+ const opcode = parseInt(fetchedInstruction.substring(9, 14), 2);
+ const srcA = parseInt(fetchedInstruction.substring(6, 9).split("").reverse().join(""), 2)
+ const srcB = parseInt(fetchedInstruction.substring(3, 6).split("").reverse().join(""), 2)
+ const dest = parseInt(fetchedInstruction.substring(0, 3).split("").reverse().join(""), 2)
+
+ // Execute
+ const instruction = opcode as Instruction;
+
+ switch(instruction) {
+ default:
+ case undefined: // If instruction doesn't exist
+ case Instruction.NOP:
+ // No operation
+
+ state.PC = verify16BitNum(++state.PC);
+ break;
+ case Instruction.MV:
+ // Move register to another register
+ state.registers[dest] = state.registers[srcA];
+
+ state.PC = verify16BitNum(++state.PC);
+ break;
+ case Instruction.LI:
+ // Load immediate into register
+ state.registers[dest] = state.memory[verify16BitNum(++state.PC)];
+
+ state.PC = verify16BitNum(++state.PC);
+ break;
+ case Instruction.LD:
+ // Load indirect through immediate into register
+ state.registers[dest] = state.memory[state.memory[verify16BitNum(++state.PC)]];
+
+ state.PC = verify16BitNum(++state.PC);
+ break;
+ case Instruction.LDIND:
+ // Load indirect through register into register
+ state.registers[dest] = state.memory[state.registers[srcA]];
+
+ state.PC = verify16BitNum(++state.PC);
+ break;
+ case Instruction.LDIO:
+ // Load indirect through register + immediate offset into register
+ state.registers[dest] = state.memory[verify16BitNum(state.registers[srcA] + state.memory[verify16BitNum(++state.PC)])];
+
+ state.PC = verify16BitNum(++state.PC);
+ break;
+ case Instruction.STIO:
+ // Store at register + immediate offset a register
+ state.memory[verify16BitNum(state.registers[srcA] + state.memory[verify16BitNum(++state.PC)])] = state.registers[srcB];
+
+ state.PC = verify16BitNum(++state.PC);
+ break;
+ case Instruction.ADD:
+ // Add two registers
+ state.registers[dest] = verify16BitNum(state.registers[srcA] + state.registers[srcB]);
+
+ state.PC = verify16BitNum(++state.PC);
+ break;
+ case Instruction.SUB:
+ // Subtract two registers
+ state.registers[dest] = verify16BitNum(state.registers[srcA] - state.registers[srcB]);
+
+ state.PC = verify16BitNum(++state.PC);
+ break;
+ case Instruction.NEG:
+ // Take 2's complement of number and store it
+ state.registers[dest] = parseInt(
+ ('0000000000000000' + state.registers[srcA].toString(2))
+ .slice(-16)
+ .split("")
+ .map((el) => el === '0' ? '1' : '0')
+ .join(""), 2) + 1;
+
+ state.PC = verify16BitNum(++state.PC);
+ break;
+ case Instruction.XOR:
+ // XOR two registers
+ state.registers[dest] = state.registers[srcA] ^ state.registers[srcB];
+
+ state.PC = verify16BitNum(++state.PC);
+ break;
+ case Instruction.NAND:
+ state.registers[dest] = parseInt(
+ (state.registers[srcA] & state.registers[srcB])
+ .toString(2)
+ .split("")
+ .map((el) => el === '0' ? '1' : '0')
+ .join(""), 2);
+
+ state.PC = verify16BitNum(++state.PC);
+ break;
+ case Instruction.AND:
+ // AND two registers
+ state.registers[dest] = state.registers[srcA] & state.registers[srcB];
+
+ state.PC = verify16BitNum(++state.PC);
+ break;
+ case Instruction.OR:
+ // OR two registers
+ state.registers[dest] = state.registers[srcA] | state.registers[srcB];
+
+ state.PC = verify16BitNum(++state.PC);
+ break;
+ case Instruction.NOT:
+ // Take the NOT of a register
+ state.registers[dest] = parseInt(
+ ('0000000000000000' + state.registers[srcA].toString(2))
+ .slice(-16)
+ .split("")
+ .map((el) => el === '0' ? '0' : '1')
+ .join(""), 2);
+
+ state.PC = verify16BitNum(++state.PC);
+ break;
+ case Instruction.J:
+ // Set PC to a register value
+ state.PC = state.registers[srcA];
+
+ break;
+ case Instruction.JNZ:
+ // Jump if register is not zero
+ state.PC = state.registers[srcA] !== 0 ? state.memory[verify16BitNum(++state.PC)] : verify16BitNum(state.PC + 2);
+
+ break;
+ case Instruction.JIMM:
+ // Jump to immediate value
+ state.PC = state.memory[verify16BitNum(++state.PC)];
+
+ break;
+ case Instruction.ADDI:
+ // Add a register with an immediate value
+ state.registers[dest] = verify16BitNum(state.registers[srcA] + state.memory[verify16BitNum(++state.PC)]);
+
+ state.PC = verify16BitNum(++state.PC);
+ break;
+ case Instruction.ST:
+ // Store register at immediate address
+ state.memory[verify16BitNum(++state.PC)] = state.registers[srcA];
+
+ state.PC = verify16BitNum(++state.PC);
+ break;
+ case Instruction.JZ:
+ // Jump if register is zero
+ state.PC = state.registers[srcA] === 0 ? state.memory[verify16BitNum(++state.PC)] : verify16BitNum(state.PC + 2);
+
+ break;
+ case Instruction.JN:
+ // Jump if register is negative
+ state.PC = state.registers[srcA] > 32767 ? state.memory[verify16BitNum(++state.PC)] : verify16BitNum(state.PC + 2);
+
+ break;
+ }
+ }
+
+ return state;
+}
\ No newline at end of file