#!/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