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