VicturalMachine(1)

Simple execute with bench instructions


Memory

const createMemory = (sizeInBytes) => {
  const ab = new ArrayBuffer(sizeInBytes)
  const dv = new DataView(ab)
  return dv
}
 
export default createMemory

instructions set

const MOVE_LIT_REG = 0x10
const MOVE_REG_REG = 0x11
const MOVE_REG_MEM = 0x12
const MOVE_MEM_REG = 0x13
const ADD_REG_REG = 0x14
const JMP_NOT_EQ = 0x15
export {
    MOVE_LIT_REG,
    MOVE_REG_REG,
    MOVE_REG_MEM,
    MOVE_MEM_REG,
    ADD_REG_REG,
    JMP_NOT_EQ,
}

CPU

use cpu to similar execution to the instructions

  • to define the register from machine
constructor(memory){
        this.memory = memory
 
        this.registerNames = [
            'ip','acc',
            'r1','r2','r3','r4',
            'r5','r6','r7','r8',
            'sp','fp'
        ]
        // 16bits means every register need 2 bits
        this.registers = createMemory(this.registerNames.length * 2)
 
        this.registerMap = this.registerNames.reduce((map,name,i) => {
            map[name] = i*2
            return map
        },{})
}
  • we cannot immediation to get the address from memory, so we need create the fucntion to see the address which near the target address
viewMemoryAt(address) {
        // 0x0f01: 0x04 0x05 0xA3 0xFE 0x13 0x0D 0x44 0x0F
        const nextEightBytes = Array.from({length: 8}, (_, i) =>
            this.memory.getUint8(address + i)
        ).map(v => `0x${v.toString(16).padStart(2, '0')}`);
 
        console.log(`0x${address.toString(16).padStart(4, '0')}: ${nextEightBytes.join(' ')}`);
    }
the full code
import createMemory from "./create-memory.js"
import {
    MOVE_LIT_REG,
    MOVE_REG_REG,
    MOVE_REG_MEM,
    MOVE_MEM_REG, ADD_REG_REG, JMP_NOT_EQ
} from "./instruction.js"
class CPU {
    constructor(memory){
        this.memory = memory
 
        this.registerNames = [
            'ip','acc',
            'r1','r2','r3','r4',
            'r5','r6','r7','r8',
            'sp','fp'
        ]
        // 16bits means every register need 2 bits
        this.registers = createMemory(this.registerNames.length * 2)
 
        this.registerMap = this.registerNames.reduce((map,name,i) => {
            map[name] = i*2
            return map
        },{})
    }
    
    debug(){
        this.registerNames.forEach(name => {
            console.log(`${name}: 0x${this.getRegister(name).toString(16).padStart(4,'0')}`)
        })
        console.log()
    }
 
    viewMemoryAt(address) {
        // 0x0f01: 0x04 0x05 0xA3 0xFE 0x13 0x0D 0x44 0x0F
        const nextEightBytes = Array.from({length: 8}, (_, i) =>
            this.memory.getUint8(address + i)
        ).map(v => `0x${v.toString(16).padStart(2, '0')}`);
 
        console.log(`0x${address.toString(16).padStart(4, '0')}: ${nextEightBytes.join(' ')}`);
    }
    getRegister(name){
        if(!(name in this.registerMap)){
            throw new Error(`getRegister: No such register '${name}' `)
        }
        return this.registers.getUint16(this.registerMap[name])
    }
 
    setRegister(name,value){
        if(!(name in this.registerMap)){
            throw new Error(`setRegister: No such register '${name}' `)
        }
        return this.registers.setUint16(this.registerMap[name],value)
    }
 
    fetch(){
        const nextInstructionAddress = this.getRegister('ip')
        const instruction = this.memory.getUint8(nextInstructionAddress)
        this.setRegister('ip',nextInstructionAddress + 1)
        return instruction
    }
    
    fetch16(){
        const nextInstructionAddress = this.getRegister('ip')
        const instruction = this.memory.getUint16(nextInstructionAddress)
        this.setRegister('ip',nextInstructionAddress + 2)
        return instruction
    }
 
    excute(instruction){
        switch (instruction){
            case MOVE_LIT_REG: {
                const literal = this.fetch16()
                const register = (this.fetch() % this.registerNames.length) * 2
                this.registers.setUint16(register,literal)
                return
            }
            case MOVE_REG_REG : {
                const registerFrom = (this.fetch() % this.registerNames.length) * 2
                const registerTo = (this.fetch() % this.registerNames.length) * 2
                const value = this.registers.getUint16(registerFrom)
                this.registers.setUint16(registerTo,value)
                return
            }
            case MOVE_REG_MEM: {
                const registerFrom = (this.fetch() % this.registerNames.length) * 2
                const address = this.fetch16()
                const value = this.registers.getUint16(registerFrom)
                this.memory.setUint16(address,value)
                return
            }
            case MOVE_MEM_REG: {
                const address = this.fetch16()
                const registerTo = (this.fetch() % this.registerNames.length) * 2
                const value = this.memory.getUint16(address)
                this.registers.setUint16(registerTo,value)
                return
            }
            case ADD_REG_REG: {
                const r1 = this.fetch()
                const r2 = this.fetch()
                const registerValue1 = this.registers.getUint16(r1 * 2)
                const registerValue2 = this.registers.getUint16(r2 * 2)
                this.setRegister('acc',registerValue1 + registerValue2)
                return
            }
            case JMP_NOT_EQ: {
                const value = this.fetch16()
                const address = this.fetch16()
                if (value !== this.getRegister('acc')){
                    this.setRegister('ip',address)
                }
                return
            }
        }
    }
 
    step(){
        const instruction = this.fetch()
        return this.excute(instruction)
    }
    
}
 
export default CPU