commit caa26dd32afc72e42962975c0b9d05d12b27e5b7 Author: ChronosX88 Date: Mon Feb 17 20:55:41 2020 +0400 init commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..a96b4db --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# YAVM + +YAVM *(yet another virtual machine)* - is a toy implementation of registry-based virtual (abstract) machine for simple assembler. +Features: +- Load number into register +- Add two numbers +- Substract two numbers +- Multiple two numbers +- Divide two numbers diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..365414d --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/ChronosX88/YAVM + +go 1.13 diff --git a/main.go b/main.go new file mode 100644 index 0000000..c1236d0 --- /dev/null +++ b/main.go @@ -0,0 +1,18 @@ +package main + +import "fmt" + +func main() { + vm := NewVM() + vm.CurrentProgramBytecode = []uint8{ + 0, 0, 1, 244, // load $0 #500 + 0, 1, 1, 244, // load $1 #500 + 1, 0, 1, 2, // add $0 $1 $2 -> $2 = 1000 + 2, 0, 1, 2, // sub $0 $1 $2 -> $2 = 0 + 3, 0, 1, 2, // multiple $0 $1 $2 -> $2 = 250000 + 4, 0, 1, 2, // divide $0 $1 $2 -> $2 = 1 + } + vm.Run() + fmt.Println("Current register state:", vm.Registers) +} + diff --git a/opcodes.go b/opcodes.go new file mode 100644 index 0000000..f8bf6da --- /dev/null +++ b/opcodes.go @@ -0,0 +1,49 @@ +package main + +type Opcode int + +const ( + // LOAD is opcode for loading number into register + // Args: + // 1. register for loading number to + // 2. two bytes for number itself + LOAD Opcode = 0 + + // ADD is opcode for adding two number from register and storing to register which specified in bytecode + // Args: + // 1. first number from register + // 2. second number from register + // 3. to which register need to store result of adding + ADD Opcode = 1 + SUB Opcode = 2 + MULTIPLE Opcode = 3 + DIVIDE Opcode = 4 + HALT Opcode = 45 + ILLEGAL Opcode = 50 +) + +func OpcodeFrom(b uint8) Opcode { + switch b { + case 0: { + return LOAD + } + case 1: { + return ADD + } + case 2: { + return SUB + } + case 3: { + return MULTIPLE + } + case 4: { + return DIVIDE + } + case 45: { + return HALT + } + default: { + return ILLEGAL + } + } +} diff --git a/vm.go b/vm.go new file mode 100644 index 0000000..f13c96d --- /dev/null +++ b/vm.go @@ -0,0 +1,87 @@ +package main + +import "fmt" + +type VM struct { + Registers []int32 + CurrentProgramBytecode []uint8 + CurrentProgramPos int + Remainder uint32 +} + +func NewVM() *VM { + return &VM { + Registers: make([]int32, 32), + CurrentProgramBytecode: make([]uint8, 0), + CurrentProgramPos: 0, + Remainder: 0, + } +} + +func (vm *VM) Run() { + for { + if vm.CurrentProgramPos >= len(vm.CurrentProgramBytecode) { + break + } + + switch vm.decodeOpcode() { + case HALT: { + fmt.Println("HALT encountered") + return + } + case LOAD: { + register := uint(vm.next8Bits()) + number := uint32(vm.next16Bits()) + fmt.Println(register, number) + vm.Registers[register] = int32(number) + } + case ADD: { + number1 := vm.Registers[uint(vm.next8Bits())] + number2 := vm.Registers[uint(vm.next8Bits())] + targetRegister := vm.next8Bits() + vm.Registers[targetRegister] = number1 + number2 + } + case SUB: { + number1 := vm.Registers[uint(vm.next8Bits())] + number2 := vm.Registers[uint(vm.next8Bits())] + targetRegister := vm.next8Bits() + vm.Registers[targetRegister] = number1 - number2 + } + case MULTIPLE: { + number1 := vm.Registers[uint(vm.next8Bits())] + number2 := vm.Registers[uint(vm.next8Bits())] + targetRegister := vm.next8Bits() + vm.Registers[targetRegister] = number1 * number2 + } + case DIVIDE: { + number1 := vm.Registers[uint(vm.next8Bits())] + number2 := vm.Registers[uint(vm.next8Bits())] + targetRegister := vm.next8Bits() + vm.Registers[targetRegister] = number1 / number2 + vm.Remainder = uint32(number1 % number2) + } + default: { + fmt.Println("Unrecognized opcode, terminating VM...") + return + } + } + } +} + +func (vm *VM) decodeOpcode() Opcode { + opcode := OpcodeFrom(vm.CurrentProgramBytecode[vm.CurrentProgramPos]) + vm.CurrentProgramPos++ + return opcode +} + +func (vm *VM) next8Bits() uint8 { + result := vm.CurrentProgramBytecode[vm.CurrentProgramPos] + vm.CurrentProgramPos++ + return result +} + +func (vm *VM) next16Bits() uint16 { + result := ((uint16(vm.CurrentProgramBytecode[vm.CurrentProgramPos])) << 8) | uint16(vm.CurrentProgramBytecode[vm.CurrentProgramPos + 1]) + vm.CurrentProgramPos += 2 + return result +}