From daddaafabf8bfd764a1b9feb16204c17aaf21260 Mon Sep 17 00:00:00 2001 From: Ola Horg Jacobsen Date: Sun, 8 Dec 2024 22:18:51 +0100 Subject: [PATCH 1/5] Try to fix broken register margin --- src/app/ui/emulator.module.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 { From 81dda248a590dfd259b95cd5a4a8b8037c0a88da Mon Sep 17 00:00:00 2001 From: Ola Horg Jacobsen Date: Sun, 8 Dec 2024 22:20:27 +0100 Subject: [PATCH 2/5] Rename components --- src/app/page.tsx | 18 +++++++++--------- .../{emulator.tsx => emulatorComponent.tsx} | 6 +++--- ...uction.tsx => viewInstructionComponent.tsx} | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) rename src/components/emulator/{emulator.tsx => emulatorComponent.tsx} (99%) rename src/components/viewInstruction/{viewInstruction.tsx => viewInstructionComponent.tsx} (95%) 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/components/emulator/emulator.tsx b/src/components/emulator/emulatorComponent.tsx similarity index 99% rename from src/components/emulator/emulator.tsx rename to src/components/emulator/emulatorComponent.tsx index 07bef10..4662519 100644 --- a/src/components/emulator/emulator.tsx +++ b/src/components/emulator/emulatorComponent.tsx @@ -21,7 +21,7 @@ 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"; +import ViewInstructionComponent from "@/components/viewInstruction/viewInstructionComponent"; export enum instruction { NOP, @@ -48,7 +48,7 @@ export enum instruction { JN } -export default function Emulator() { +export default function EmulatorComponent() { const [reg_PC, setReg_PC] = useState(0); const [reg_r0, setReg_r0] = useState(0); const [reg_r1, setReg_r1] = useState(0); @@ -967,7 +967,7 @@ export default function Emulator() { BBC state Instruction - 32767? vram[reg_PC - 32768] : ram[reg_PC]} immContent={reg_PC + 1 > 32767? vram[reg_PC - 32767] : ram[reg_PC + 1]} /> + 32767? vram[reg_PC - 32768] : ram[reg_PC]} immContent={reg_PC + 1 > 32767? vram[reg_PC - 32767] : ram[reg_PC + 1]} /> Registers diff --git a/src/components/viewInstruction/viewInstruction.tsx b/src/components/viewInstruction/viewInstructionComponent.tsx similarity index 95% rename from src/components/viewInstruction/viewInstruction.tsx rename to src/components/viewInstruction/viewInstructionComponent.tsx index fb82567..a8e0803 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 "@/components/emulator/emulatorComponent"; interface IViewInstructionProps { memoryContent: number, immContent: number } -export default function ViewInstruction(props: IViewInstructionProps) { +export default function ViewInstructionComponent(props: IViewInstructionProps) { const [decodedInstruction, setDecodedInstruction] = useState(""); useEffect(() => { From db36808b5fb040ca5db66f026f92dda196557cc9 Mon Sep 17 00:00:00 2001 From: Ola Horg Jacobsen Date: Sun, 8 Dec 2024 23:05:08 +0100 Subject: [PATCH 3/5] Created CPU emulator file --- src/components/emulator/emulatorComponent.tsx | 3 + src/emulation/emulator.ts | 217 ++++++++++++++++++ 2 files changed, 220 insertions(+) create mode 100644 src/emulation/emulator.ts diff --git a/src/components/emulator/emulatorComponent.tsx b/src/components/emulator/emulatorComponent.tsx index 4662519..6b6dfe1 100644 --- a/src/components/emulator/emulatorComponent.tsx +++ b/src/components/emulator/emulatorComponent.tsx @@ -22,6 +22,7 @@ import PauseIcon from '@mui/icons-material/Pause'; import StopIcon from '@mui/icons-material/Stop'; import MoveDownIcon from '@mui/icons-material/MoveDown'; import ViewInstructionComponent from "@/components/viewInstruction/viewInstructionComponent"; +import {CPUInit} from "@/emulation/emulator"; export enum instruction { NOP, @@ -112,6 +113,8 @@ export default function EmulatorComponent() { setReg_r5(Math.floor(Math.random() * 65536)); setReg_r6(Math.floor(Math.random() * 65536)); setReg_r7(Math.floor(Math.random() * 65536)); + + console.log(CPUInit()) } useEffect(() => { diff --git a/src/emulation/emulator.ts b/src/emulation/emulator.ts new file mode 100644 index 0000000..21b8d8d --- /dev/null +++ b/src/emulation/emulator.ts @@ -0,0 +1,217 @@ +type Register = number; +type RegisterFile = Array; +type Memory = Array; + +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; +} + +function verify16BitNum(value: number): number { return value < 0 ? 65535 + value : value > 65535 ? value - 65536 : value; } + +export function CPUStep(state: ICPUState) { + // Fetch + const fetchedInstruction = ('0000000000000000' + state.memory[state.PC]).toString().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) { + 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; + } +} \ No newline at end of file From 4081c9b957c45c65763d63dadb641bd58680c148 Mon Sep 17 00:00:00 2001 From: Ola Horg Jacobsen Date: Mon, 9 Dec 2024 00:07:15 +0100 Subject: [PATCH 4/5] Feature parity but laggy --- src/components/canvas/canvas.tsx | 106 ++-- src/components/emulator/emulatorComponent.tsx | 571 ++++-------------- .../viewInstructionComponent.tsx | 50 +- src/emulation/emulator.ts | 334 +++++----- 4 files changed, 361 insertions(+), 700 deletions(-) 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/emulatorComponent.tsx b/src/components/emulator/emulatorComponent.tsx index 6b6dfe1..f147f7c 100644 --- a/src/components/emulator/emulatorComponent.tsx +++ b/src/components/emulator/emulatorComponent.tsx @@ -1,3 +1,4 @@ +/* eslint-disable react-hooks/exhaustive-deps */ "use client"; import { @@ -17,51 +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 ViewInstructionComponent from "@/components/viewInstruction/viewInstructionComponent"; -import {CPUInit} from "@/emulation/emulator"; - -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 -} +import {CPUInit, CPUStep, ICPUState, Instruction, Register, verify16BitNum} from "@/emulation/emulator"; export default function EmulatorComponent() { - 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)); + const [CPUState, setCPUState] = useState({PC: 0, memory: Array(65535), registers: Array(8)}); const [clockSpeed, setClockSpeed] = useState("100"); const [clockSpeedModifier, setClockSpeedModifier] = useState(0); @@ -81,78 +45,40 @@ export default function EmulatorComponent() { 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)); - - console.log(CPUInit()) - } 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] + }; - sum += (clockStop - clockStart) * 1000; + const clockStart = window.performance.now(); + CPUStep(state, 1); + const clockStop = window.performance.now(); + + sum += (clockStop - clockStart); } - console.log("Tests complete. Result:", sum / 100); + console.log("Tests complete. Result:", sum / 100, "ms"); setClockSpeedModifier(sum / 100); - - setRandomRam() }, []); const recognisedArgType = [ @@ -180,269 +106,6 @@ export default function EmulatorComponent() { ['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); @@ -451,12 +114,10 @@ export default function EmulatorComponent() { 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); @@ -471,7 +132,8 @@ export default function EmulatorComponent() { } const stopProgram = () => { - setRandomRam(); + setCPUState(CPUInit()); + setRunning(false); setLoaded(false); setRunning(false); @@ -481,26 +143,24 @@ export default function EmulatorComponent() { 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); @@ -508,6 +168,8 @@ export default function EmulatorComponent() { }, [running, completedExecution]) const assemble = () => { + const state = CPUInit(); + setAssemblerComplete(false); if(code === "") { @@ -527,6 +189,7 @@ export default function EmulatorComponent() { const usedLablesLine: number[] = []; const usedLabelsAddr: number[] = []; + let byteCount = 0; for(let line = 0; line < lines.length; line++) { @@ -608,14 +271,14 @@ export default function EmulatorComponent() { if(val < 0) val = 65536 + val - ram[addr++] = val; + state.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) { @@ -625,92 +288,92 @@ export default function EmulatorComponent() { } switch(inst) { - case instruction.NOP: { - ram[addr++] = 0 << 2; + case Instruction.NOP: { + state.memory[addr++] = 0 << 2; break; } - case instruction.MV: { - ram[addr++] = 1 << 2; + case Instruction.MV: { + state.memory[addr++] = 1 << 2; break; } - case instruction.LI: { - ram[addr++] = 2 << 2; + case Instruction.LI: { + state.memory[addr++] = 2 << 2; break; } - case instruction.LD: { - ram[addr++] = 3 << 2; + case Instruction.LD: { + state.memory[addr++] = 3 << 2; break; } - case instruction.LDIND: { - ram[addr++] = 4 << 2; + case Instruction.LDIND: { + state.memory[addr++] = 4 << 2; break; } - case instruction.LDIO: { - ram[addr++] = 5 << 2; + case Instruction.LDIO: { + state.memory[addr++] = 5 << 2; break; } - case instruction.STIO: { - ram[addr++] = 6 << 2; + case Instruction.STIO: { + state.memory[addr++] = 6 << 2; break; } - case instruction.ADD: { - ram[addr++] = 7 << 2; + case Instruction.ADD: { + state.memory[addr++] = 7 << 2; break; } - case instruction.SUB: { - ram[addr++] = 8 << 2; + case Instruction.SUB: { + state.memory[addr++] = 8 << 2; break; } - case instruction.NEG: { - ram[addr++] = 9 << 2; + case Instruction.NEG: { + state.memory[addr++] = 9 << 2; break; } - case instruction.XOR: { - ram[addr++] = 10 << 2; + case Instruction.XOR: { + state.memory[addr++] = 10 << 2; break; } - case instruction.NAND: { - ram[addr++] = 11 << 2; + case Instruction.NAND: { + state.memory[addr++] = 11 << 2; break; } - case instruction.AND: { - ram[addr++] = 12 << 2; + case Instruction.AND: { + state.memory[addr++] = 12 << 2; break; } - case instruction.OR: { - ram[addr++] = 13 << 2; + case Instruction.OR: { + state.memory[addr++] = 13 << 2; break; } - case instruction.NOT: { - ram[addr++] = 14 << 2; + case Instruction.NOT: { + state.memory[addr++] = 14 << 2; break; } - case instruction.J: { - ram[addr++] = 15 << 2; + case Instruction.J: { + state.memory[addr++] = 15 << 2; break; } - case instruction.JNZ: { - ram[addr++] = 16 << 2; + case Instruction.JNZ: { + state.memory[addr++] = 16 << 2; break; } - case instruction.JIMM: { - ram[addr++] = 17 << 2; + case Instruction.JIMM: { + state.memory[addr++] = 17 << 2; break; } - case instruction.ADDI: { - ram[addr++] = 18 << 2; + case Instruction.ADDI: { + state.memory[addr++] = 18 << 2; break; } - case instruction.ST: { - ram[addr++] = 19 << 2; + case Instruction.ST: { + state.memory[addr++] = 19 << 2; break; } - case instruction.JZ: { - ram[addr++] = 20 << 2; + case Instruction.JZ: { + state.memory[addr++] = 20 << 2; break; } - case instruction.JN: { - ram[addr++] = 21 << 2; + case Instruction.JN: { + state.memory[addr++] = 21 << 2; break; } } @@ -725,21 +388,21 @@ export default function EmulatorComponent() { //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); + state.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; } @@ -799,7 +462,7 @@ export default function EmulatorComponent() { if(immVal < 0) immVal = 65536 + immVal; - ram[addr++] = immVal; + state.memory[addr++] = immVal; break; } @@ -826,7 +489,7 @@ export default function EmulatorComponent() { if(immVal < 0) immVal = 65536 + immVal; - ram[addr++] = immVal; + state.memory[addr++] = immVal; } error = false; @@ -853,12 +516,13 @@ export default function EmulatorComponent() { return; } else { - ram[usedLabelsAddr[i]] = labelsAddr[foundIndex]; + state.memory[usedLabelsAddr[i]] = labelsAddr[foundIndex]; } } setAssemblerComplete(true); setAssemblerError(false); + setCPUState(state); setAssemblerMessage("Success - assembled " + byteCount + " bytes."); } @@ -910,28 +574,11 @@ export default function EmulatorComponent() { 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 - +
- - + + - - + + - - + + - - + +
- - + + - - + + - - + + - - + + @@ -1009,7 +656,7 @@ export default function EmulatorComponent() { PPU - +
diff --git a/src/components/viewInstruction/viewInstructionComponent.tsx b/src/components/viewInstruction/viewInstructionComponent.tsx index a8e0803..604d946 100644 --- a/src/components/viewInstruction/viewInstructionComponent.tsx +++ b/src/components/viewInstruction/viewInstructionComponent.tsx @@ -2,7 +2,7 @@ import {TextField} from "@mui/material"; import {useEffect, useState} from "react"; import styles from '@/app/ui/viewInstruction.module.css'; -import {instruction} from "@/components/emulator/emulatorComponent"; +import {Instruction} from "@/emulation/emulator"; interface IViewInstructionProps { memoryContent: number, @@ -26,58 +26,58 @@ export default function ViewInstructionComponent(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 index 21b8d8d..b72a044 100644 --- a/src/emulation/emulator.ts +++ b/src/emulation/emulator.ts @@ -1,8 +1,8 @@ -type Register = number; -type RegisterFile = Array; -type Memory = Array; +export type Register = number; +export type RegisterFile = Array; +export type Memory = Array; -interface ICPUState { +export interface ICPUState { PC: Register; registers: RegisterFile; memory: Memory; @@ -56,162 +56,174 @@ export function CPUInit(): ICPUState { return cpuState; } -function verify16BitNum(value: number): number { return value < 0 ? 65535 + value : value > 65535 ? value - 65536 : value; } - -export function CPUStep(state: ICPUState) { - // Fetch - const fetchedInstruction = ('0000000000000000' + state.memory[state.PC]).toString().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) { - 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; +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 From 0d797a582f5c7176ee04e5bd7148bad6ce9a9d31 Mon Sep 17 00:00:00 2001 From: Ola Horg Jacobsen Date: Mon, 9 Dec 2024 00:16:16 +0100 Subject: [PATCH 5/5] Added back byte counting --- src/components/emulator/emulatorComponent.tsx | 84 +++++++------------ 1 file changed, 30 insertions(+), 54 deletions(-) diff --git a/src/components/emulator/emulatorComponent.tsx b/src/components/emulator/emulatorComponent.tsx index f147f7c..7d17797 100644 --- a/src/components/emulator/emulatorComponent.tsx +++ b/src/components/emulator/emulatorComponent.tsx @@ -168,8 +168,6 @@ export default function EmulatorComponent() { }, [running, completedExecution]) const assemble = () => { - const state = CPUInit(); - setAssemblerComplete(false); if(code === "") { @@ -271,7 +269,7 @@ export default function EmulatorComponent() { if(val < 0) val = 65536 + val - state.memory[addr++] = val; + CPUState.memory[addr++] = val; byteCount += 2; @@ -287,93 +285,95 @@ export default function EmulatorComponent() { return; } + byteCount += 2; + switch(inst) { case Instruction.NOP: { - state.memory[addr++] = 0 << 2; + CPUState.memory[addr++] = 0 << 2; break; } case Instruction.MV: { - state.memory[addr++] = 1 << 2; + CPUState.memory[addr++] = 1 << 2; break; } case Instruction.LI: { - state.memory[addr++] = 2 << 2; + CPUState.memory[addr++] = 2 << 2; break; } case Instruction.LD: { - state.memory[addr++] = 3 << 2; + CPUState.memory[addr++] = 3 << 2; break; } case Instruction.LDIND: { - state.memory[addr++] = 4 << 2; + CPUState.memory[addr++] = 4 << 2; break; } case Instruction.LDIO: { - state.memory[addr++] = 5 << 2; + CPUState.memory[addr++] = 5 << 2; break; } case Instruction.STIO: { - state.memory[addr++] = 6 << 2; + CPUState.memory[addr++] = 6 << 2; break; } case Instruction.ADD: { - state.memory[addr++] = 7 << 2; + CPUState.memory[addr++] = 7 << 2; break; } case Instruction.SUB: { - state.memory[addr++] = 8 << 2; + CPUState.memory[addr++] = 8 << 2; break; } case Instruction.NEG: { - state.memory[addr++] = 9 << 2; + CPUState.memory[addr++] = 9 << 2; break; } case Instruction.XOR: { - state.memory[addr++] = 10 << 2; + CPUState.memory[addr++] = 10 << 2; break; } case Instruction.NAND: { - state.memory[addr++] = 11 << 2; + CPUState.memory[addr++] = 11 << 2; break; } case Instruction.AND: { - state.memory[addr++] = 12 << 2; + CPUState.memory[addr++] = 12 << 2; break; } case Instruction.OR: { - state.memory[addr++] = 13 << 2; + CPUState.memory[addr++] = 13 << 2; break; } case Instruction.NOT: { - state.memory[addr++] = 14 << 2; + CPUState.memory[addr++] = 14 << 2; break; } case Instruction.J: { - state.memory[addr++] = 15 << 2; + CPUState.memory[addr++] = 15 << 2; break; } case Instruction.JNZ: { - state.memory[addr++] = 16 << 2; + CPUState.memory[addr++] = 16 << 2; break; } case Instruction.JIMM: { - state.memory[addr++] = 17 << 2; + CPUState.memory[addr++] = 17 << 2; break; } case Instruction.ADDI: { - state.memory[addr++] = 18 << 2; + CPUState.memory[addr++] = 18 << 2; break; } case Instruction.ST: { - state.memory[addr++] = 19 << 2; + CPUState.memory[addr++] = 19 << 2; break; } case Instruction.JZ: { - state.memory[addr++] = 20 << 2; + CPUState.memory[addr++] = 20 << 2; break; } case Instruction.JN: { - state.memory[addr++] = 21 << 2; + CPUState.memory[addr++] = 21 << 2; break; } } @@ -388,7 +388,7 @@ export default function EmulatorComponent() { //addr += recognisedInstructionLength[opIndex]; const setRegisterSrcDest = (memoryAddr: number, reg: number, type: number) => { - state.memory[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) => { @@ -442,31 +442,6 @@ export default function EmulatorComponent() { 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; - - state.memory[addr++] = immVal; - - break; - } - case 'a': { currentlyChecking = "IMM16LABEL" @@ -489,9 +464,11 @@ export default function EmulatorComponent() { if(immVal < 0) immVal = 65536 + immVal; - state.memory[addr++] = immVal; + CPUState.memory[addr++] = immVal; } + byteCount += 2; + error = false; break; } @@ -516,13 +493,12 @@ export default function EmulatorComponent() { return; } else { - state.memory[usedLabelsAddr[i]] = labelsAddr[foundIndex]; + CPUState.memory[usedLabelsAddr[i]] = labelsAddr[foundIndex]; } } setAssemblerComplete(true); setAssemblerError(false); - setCPUState(state); setAssemblerMessage("Success - assembled " + byteCount + " bytes."); }