Last active
November 14, 2015 11:58
-
-
Save icebreaker/3c260812b94ff434c303 to your computer and use it in GitHub Desktop.
A small "experimental" VM written in Ruby.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; | |
; Prints first N from the Fibonacci sequence. | |
; | |
read_n: | |
mov dx, 78 | |
prc dx | |
mov dx, 58 | |
prc dx | |
rdr dx | |
cmp dx, 0 | |
je read_n | |
mov cx, 0 | |
mov ax, 0 | |
mov bx, 1 | |
loop: | |
prr bx | |
push bx | |
add bx, ax | |
pop ax | |
add cx, 1 | |
cmp cx, dx | |
jne loop |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; | |
; Prints a NxM matrix of 'X's | |
; | |
read_x: | |
mov ax, 88 | |
prc ax | |
mov ax, 58 | |
prc ax | |
rdr ax | |
cmp ax, 0 | |
je read_x | |
read_y: | |
mov bx, 89 | |
prc bx | |
mov bx, 58 | |
prc bx | |
rdr bx | |
cmp bx, 0 | |
je read_y | |
mov cx, 0 | |
mov dx, 0 | |
draw_y: | |
push dx | |
draw_x: | |
mov dx, 88 | |
prc dx | |
mov dx, 32 | |
prc dx | |
add cx, 1 | |
cmp cx, ax | |
jne draw_x | |
mov dx, 10 | |
prc dx | |
xor cx, cx | |
pop dx | |
add dx, 1 | |
cmp dx, bx | |
jne draw_y | |
push ax | |
mov ax, 10 | |
prc ax | |
pop ax |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
class VM | |
REG_0 = 0x00 | |
REG_1 = 0x01 | |
REG_2 = 0x02 | |
REG_3 = 0x03 | |
REG_4 = 0x04 | |
REG_N = 0x05 | |
OP_NOP = 0x00 | |
OP_PUSH = 0x01 | |
OP_POP = 0x02 | |
OP_LDR = 0x03 | |
OP_MOV = 0x04 | |
OP_ADD = 0x05 | |
OP_SUB = 0x06 | |
OP_CMP = 0x07 | |
OP_JNE = 0x08 | |
OP_JE = 0x09 | |
OP_JMP = 0x10 | |
OP_XOR = 0x11 | |
OP_RDR = 0x12 | |
OP_PRR = 0x13 | |
OP_PRC = 0x14 | |
OP_RDC = 0x15 | |
attr_reader :registers | |
def initialize | |
@registers = [].fill(0x00, 0x00, REG_N) | |
@stack = [] | |
end | |
def eval(bytecode) | |
i = 0 | |
c = 0 | |
l = bytecode.length - 3 | |
while i <= l | |
case bytecode[i+0] # op | |
when OP_NOP # nop | |
# nop | |
when OP_PUSH # push r1 | |
@stack.push(@registers[bytecode[i+1]]) | |
when OP_POP # pop r1 | |
@registers[bytecode[i+1]] = @stack.pop | |
when OP_LDR # ldr r1, 10 | |
@registers[bytecode[i+1]] = bytecode[i+2] | |
when OP_MOV # mov r1, r2 | |
@registers[bytecode[i+1]] = @registers[bytecode[i+2]] | |
when OP_ADD # add r1, r2 | |
@registers[bytecode[i+1]] += @registers[bytecode[i+2]] | |
when OP_SUB # sub r1, r2 | |
@registers[bytecode[i+1]] -= @registers[bytecode[i+2]] | |
when OP_CMP # cmp r1, r2 | |
c = ((@registers[bytecode[i+1]] == @registers[bytecode[i+2]]) ? 1 : 0) | |
when OP_JNE # jne addr | |
i = bytecode[i+1] if c == 0 | |
when OP_JE # je addr | |
i = bytecode[i+1] if c == 1 | |
when OP_JMP # jmp addr | |
i = bytecode[i+1] | |
when OP_XOR # xor r1, r2 | |
@registers[bytecode[i+1]] ^= @registers[bytecode[i+2]] | |
when OP_RDR # rdr r1 | |
@registers[bytecode[i+1]] = STDIN.gets.chomp.to_i | |
when OP_PRR # prr r1 | |
STDOUT.puts(@registers[bytecode[i+1]]) | |
when OP_PRC # prc r1 | |
STDOUT.putc(@registers[bytecode[i+1]]) | |
when OP_RDC # rdc r1 | |
@registers[bytecode[i+1]] = STDIN.getc.ord | |
else | |
raise ArgumentError, "Unsupported opcode: #{bytecode[i]}" | |
end | |
i += 3 | |
end | |
end | |
end | |
class ASM | |
attr_reader :source, :bytecode | |
def initialize(source) | |
@source = source | |
@bytecode = [] | |
compile! | |
end | |
private | |
REGS = { | |
'ax' => VM::REG_1, | |
'bx' => VM::REG_2, | |
'cx' => VM::REG_3, | |
'dx' => VM::REG_4 | |
} | |
OPS = { | |
'nop' => VM::OP_NOP, | |
'push' => VM::OP_PUSH, | |
'pop' => VM::OP_POP, | |
'ldr' => VM::OP_LDR, | |
'mov' => VM::OP_MOV, | |
'add' => VM::OP_ADD, | |
'sub' => VM::OP_SUB, | |
'cmp' => VM::OP_CMP, | |
'jne' => VM::OP_JNE, | |
'je' => VM::OP_JE, | |
'jmp' => VM::OP_JMP, | |
'xor' => VM::OP_XOR, | |
'rdr' => VM::OP_RDR, | |
'prr' => VM::OP_PRR, | |
'prc' => VM::OP_PRC, | |
'rdc' => VM::OP_RDC | |
} | |
def compile! | |
labels = {} | |
@source.split("\n").each_with_index do |line, index| | |
line.strip! | |
next if line.length == 0 or line[0] == ';' | |
if line =~ /^(.*?)\s+(.*?)(\s*,\s*(.*?))?$/ | |
op = OPS[$1.downcase] | |
raise ArgumentError, "Invalid operator: #{$1} on line #{index+1}" unless op | |
if $4 | |
r1 = REGS[$2.downcase] | |
raise ArgumentError, "Invalid register: #{$2} on line #{index+1}" unless r1 | |
if $4[1] == 'x' | |
r2 = REGS[$4.downcase] | |
raise ArgumentError, "Invalid register: #{$4} on line #{index+1}" unless r2 | |
else | |
r2 = VM::REG_0 | |
@bytecode += [VM::OP_LDR, r2, $4.to_i] | |
end | |
else | |
case op | |
when VM::OP_JNE, VM::OP_JE, VM::OP_JMP | |
r1 = labels[$2.downcase] | |
raise ArgumentError, "Invalid label: #{$2} on line #{index+1}" unless r1 | |
else | |
r1 = REGS[$2.downcase] | |
raise ArgumentError, "Invalid register: #{$2} on line #{index+1}" unless r1 | |
end | |
r2 = VM::OP_NOP | |
end | |
@bytecode += [op, r1, r2] | |
elsif line =~ /^(.*?):$/ | |
labels[$1.strip] = @bytecode.length - 3 | |
else | |
raise ArgumentError, "Invalid syntax: #{line} on line #{index + 1}" | |
end | |
end | |
end | |
end | |
if ARGV.size > 0 | |
VM.new.eval(ASM.new(File.read(ARGV[0])).bytecode) | |
else | |
STDOUT.puts "usage: %s file.asm" % File.basename(__FILE__) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is interpreter.Is possible to create assembler?
convertassembler code to normal code for linux 32? http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html
creating working ELF binary from scratch