From 60e1775b874015a3451e4bde10a8eb30701b1165 Mon Sep 17 00:00:00 2001 From: Julian Blake Kongslie Date: Tue, 6 Jul 2021 09:44:36 -0700 Subject: Initial commit. --- asm.rb | 196 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100755 asm.rb (limited to 'asm.rb') diff --git a/asm.rb b/asm.rb new file mode 100755 index 0000000..2ca9155 --- /dev/null +++ b/asm.rb @@ -0,0 +1,196 @@ +#!/usr/bin/ruby -w + +$insts = {} + +$next_opcode = 0 + +def inst(pattern, vector=false, &code) + return if vector + throw "instruction with this pattern already defined" if $insts.member?(pattern) + $insts[pattern] = $next_opcode + $next_opcode += 1 +end + +load "insts.rb" + +$labels = {} +$pc = 0 + +ARGF.each_line do | line | + line.chomp! + line.gsub!(/^\s+/, "") + line.gsub!(/#.*/, "") + line.gsub!(/\s+$/, "") + + next if line == "" + + if line =~ /^([a-zA-Z_][a-zA-Z0-9_]*):$/ + $labels[$1] = $pc + $stdout.write("\t// #{line}\n") + next + elsif line =~ /^\.origin\s+((?:0x[0-9a-fA-F]+)?(?:\d+))$/ + $pc = $1.to_i(0) + $stdout.write("@#{$pc.to_s(16)}\t// #{line}\n") + next + elsif line =~ /^\.bytes?\s+(.*)$/ + data = $1.split(/\s+/) + data.map { | x | throw "invalid byte #{x}" unless x =~ /^-?(?:(?:0x[0-9a-fA-F]+)|(?:\d+))$/ } + data = data.map { | x | x.to_i(0) } + data = data.map { | x | x < 0 ? x + 0x100 : x } + data.map { | x | throw "invalid byte 0x#{x.to_s(16)}" unless x >= 0 && x <= 0xff } + data = data.map { | x | x.to_s(16).rjust(2, "0") } + $pc += data.size + $stdout.write("#{data.join(" ")}\t// #{line}\n") + next + elsif line =~ /^\.shorts?\s+(.*)$/ + data = $1.split(/\s+/) + data.map { | x | throw "invalid short #{x}" unless x =~ /^-?(?:(?:0x[0-9a-fA-F]+)|(?:\d+))$/ } + data = data.map { | x | x.to_i(0) } + data = data.map { | x | x < 0 ? x + 0x10000 : x } + data.map { | x | throw "invalid short 0x#{x.to_s(16)}" unless x >= 0 && x <= 0xffff } + data = data.flat_map { | x | [x & 0xff, x >> 8] } + data = data.map { | x | x.to_s(16).rjust(2, "0") } + $pc += data.size + $stdout.write("#{data.join(" ")}\t// #{line}\n") + next + elsif line =~ /^\.longs?\s+(.*)$/ + data = $1.split(/\s+/) + data.map { | x | throw "invalid long #{x}" unless x =~ /^-?(?:(?:0x[0-9a-fA-F]+)|(?:\d+))$/ } + data = data.map { | x | x.to_i(0) } + data = data.map { | x | x < 0 ? x + 0x100000000 : x } + data.map { | x | throw "invalid long 0x#{x.to_s(16)}" unless x >= 0 && x <= 0xffffffff } + data = data.flat_map { | x | [x & 0xff, (x >> 8) & 0xff, (x >> 16) & 0xff, x >> 24] } + data = data.map { | x | x.to_s(16).rjust(2, "0") } + $pc += data.size + $stdout.write("#{data.join(" ")}\t// #{line}\n") + next + elsif line =~ /^\.quads?\s+(.*)$/ + data = $1.split(/\s+/) + data.map { | x | throw "invalid quad #{x}" unless x =~ /^-?(?:(?:0x[0-9a-fA-F]+)|(?:\d+))$/ } + data = data.map { | x | x.to_i(0) } + data = data.map { | x | x < 0 ? x + 0x10000000000000000 : x } + data.map { | x | throw "invalid quad 0x#{x.to_s(16)}" unless x >= 0 && x <= 0xffffffffffffffff } + data = data.flat_map { | x | [x & 0xff, (x >> 8) & 0xff, (x >> 16) & 0xff, (x >> 24) & 0xff, (x >> 32) & 0xff, (x >> 40) & 0xff, (x >> 48) & 0xff, x >> 56] } + data = data.map { | x | x.to_s(16).rjust(2, "0") } + $pc += data.size + $stdout.write("#{data.join(" ")}\t// #{line}\n") + next + end + + parts = line.split(/\s+/) + + patterns = [parts.shift] + values = [] + parts.each do | part | + case part + when /^(-?(?:(?:0x[0-9a-fA-F]+)|(?:\d+)))$/ + patterns << "#" + values << $1.to_i(0) + when /^([a-zA-Z_][a-zA-Z0-9_]+)$/ + throw "unknown label #{$1}" unless $labels.member?($1) + patterns << "#" + values << $labels[$1] + when /^\$(-?(?:(?:0x[0-9a-fA-F]+)|(?:\d+)))$/ + patterns << "$" + values << $1.to_i(0) + when /^\[(-?(?:(?:0x[0-9a-fA-F]+)|(?:\d+)))\]$/ + patterns << "[#]" + values << $1.to_i(0) + when /^\[([a-zA-Z_][a-zA-Z0-9_]+)\]$/ + throw "unknown label #{$1}" unless $labels.member?($1) + patterns << "[#]" + values << $labels[$1] + when /^\[\$(-?(?:(?:0x[0-9a-fA-F]+)|(?:\d+)))\]$/ + patterns << "[$]" + values << $1.to_i(0) + else + throw "invalid part #{part}" + end + end + + pattern = patterns.join(" ") + throw "unknown instruction pattern #{pattern}" unless $insts.member?(pattern) + opcode = $insts[pattern] + + # Encoding format: + # +-+-+-+-+-+-+-+-+ + # |M|I|SEL| SHIFT | + # +-+-+-+-+-+-+-+-+ + # M = More data after this + # I = Invert this field before shifting + # SEL = Field selector (0=opcode, 2=A, 3=B) + # SHIFT = Data to shift in + + # This gives us these common first nibbles: + # 0 last opcode byte + # 8 extended opcode byte + # A operand A byte + # B operand B byte + # E operand A byte (with inversion) + # F operand B byte (with inversion) + + space = false + values.each_with_index do | value, idx | + inverted = value < 0 + if inverted + value *= -1 + value -= 1 + end + nibbles = [] + while value != 0 + nibbles << (value & 0xf).to_s(16) + value = value >> 4 + end + if inverted and nibbles.empty? + nibbles << "f" + end + nibbles.reverse! + if idx == 0 + if inverted + $stdout.write(" ") if space + $stdout.write("e#{nibbles.shift}") + $pc += 1 + space = true + end + nibbles.each do | nibble | + $stdout.write(" ") if space + $stdout.write("a#{nibble}") + $pc += 1 + space = true + end + elsif idx == 1 + if inverted + $stdout.write(" ") if space + $stdout.write("f#{nibbles.shift}") + $pc += 1 + space = true + end + nibbles.each do | nibble | + $stdout.write(" ") if space + $stdout.write("b#{nibble}") + $pc += 1 + space = true + end + else + throw "don't know how to encode this many fields" + end + end + + nibbles = [] + while opcode != 0 + nibbles << (opcode & 0xf).to_s(16) + opcode = opcode >> 4 + end + nibbles << "0" if nibbles.empty? + last = nibbles.shift + nibbles.reverse! + nibbles.each do | nibble | + $stdout.write(" ") if space + $stdout.write("8#{nibble}") + $pc += 1 + space = true + end + $stdout.write(" ") if space + $stdout.write("0#{last}\t// #{line}\n") + $pc += 1 +end -- cgit v1.2.3