From 693130cae9bdb145af79ee2eb256b661003b6e45 Mon Sep 17 00:00:00 2001 From: Nat Lasseter Date: Wed, 11 Dec 2019 21:53:12 +0000 Subject: Wrote Intcode (dis-)assembler --- day09/das | 73 ----------------------- intcode/Readme.textile | 52 +++++++++++++++++ intcode/as | 155 +++++++++++++++++++++++++++++++++++++++++++++++++ intcode/das | 77 ++++++++++++++++++++++++ 4 files changed, 284 insertions(+), 73 deletions(-) delete mode 100755 day09/das create mode 100644 intcode/Readme.textile create mode 100755 intcode/as create mode 100755 intcode/das diff --git a/day09/das b/day09/das deleted file mode 100755 index 9b1eecf..0000000 --- a/day09/das +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env ruby - -class Program - def initialize(program) - @mem = program - @pc = 0 - end - - def dis_next - opcode = @mem[@pc] % 100 - - p3, p2, p1 = ("%03d" % (@mem[@pc] / 100)).chars - - print "#{@pc}: " - case opcode - when 1 - puts "add #{Program.sigil(p1)}#{@mem[@pc+1]} #{Program.sigil(p2)}#{@mem[@pc+2]} #{Program.sigil(p3)}#{@mem[@pc+3]}" - @pc += 4 - when 2 - puts "mul #{Program.sigil(p1)}#{@mem[@pc+1]} #{Program.sigil(p2)}#{@mem[@pc+2]} #{Program.sigil(p3)}#{@mem[@pc+3]}" - @pc += 4 - when 3 - puts "in #{Program.sigil(p1)}#{@mem[@pc+1]}" - @pc += 2 - when 4 - puts "out #{Program.sigil(p1)}#{@mem[@pc+1]}" - @pc += 2 - when 5 - puts "jnz #{Program.sigil(p1)}#{@mem[@pc+1]} #{Program.sigil(p2)}#{@mem[@pc+2]}" - @pc += 3 - when 6 - puts "jez #{Program.sigil(p1)}#{@mem[@pc+1]} #{Program.sigil(p2)}#{@mem[@pc+2]}" - @pc += 3 - when 7 - puts "tlt #{Program.sigil(p1)}#{@mem[@pc+1]} #{Program.sigil(p2)}#{@mem[@pc+2]} #{Program.sigil(p3)}#{@mem[@pc+3]}" - @pc += 4 - when 8 - puts "teq #{Program.sigil(p1)}#{@mem[@pc+1]} #{Program.sigil(p2)}#{@mem[@pc+2]} #{Program.sigil(p3)}#{@mem[@pc+3]}" - @pc += 4 - when 9 - puts "arb #{Program.sigil(p1)}#{@mem[@pc+1]}" - @pc += 2 - when 99 - puts "hlt" - @pc += 1 - else - puts "dat #{@mem[@pc]}" - @pc += 1 - end - end - - def dis_all - until @pc >= @mem.length do - dis_next - end - end - - private - - def self.sigil(p) - case p - when "0" - "@" - when "2" - "&" - end - end -end - -input = $stdin.readlines[0].strip.split(",").map(&:to_i) - -p = Program.new(input) -p.dis_all diff --git a/intcode/Readme.textile b/intcode/Readme.textile new file mode 100644 index 0000000..844faf5 --- /dev/null +++ b/intcode/Readme.textile @@ -0,0 +1,52 @@ +h1. Intcode (dis-)assembler + +p. This is an assembler and disassembler pair for the intcode vm. + +h2. Assembler + +p. East instruction, directive, or label should appear on a line by itself. + +h3. Usage + +bc. $ ./as source.code + +h3. Instruction codes + +- add 1 2 3 := Add +- mul 1 2 3 := Multiply +- inp 1 := Input +- out 1 := Output +- jnz 1 2 := Jump if not zero +- jez 1 2 := Jump if equal to zero +- tlt 1 2 3 := Test if less than +- teq 1 2 3 := Test if equal to +- arb 1 := Adjust relative base +- hlt := Halt + +p. Optionally, commas may be used to separate arguments. + +h3. Labels + +p. Specify a label with: + +bc. label: + +p. then use it in the assembly. Sigilising it is valid, immediate @label@, positional @label, or relative @&label@. The merits of doing so in some cases are... Hmm. + +h3. Directives + +- @org location := Organise the code to this location +- @dat 1 2 3 4 ... := Insert this data literally into the bytecode + +h3. Sigils + +- @ := Positional argument +- @&@ := Relative argument + +h2. Disassembler + +p. Prints the location of the start of each instruction and the instruction itself using the syntax detailed above. + +h3. Usage + +bc. $ ./das byte.code diff --git a/intcode/as b/intcode/as new file mode 100755 index 0000000..6a4aaa0 --- /dev/null +++ b/intcode/as @@ -0,0 +1,155 @@ +#!/usr/bin/env ruby + +exit 1 if ARGV.length != 1 + +source = ARGV.shift&.strip + +lines = File.readlines(source) + +class ProgramError < Exception ; end +class ParameterError < ProgramError ; end +class LabelError < ProgramError ; end + +class Program + def initialize + @memory = [] + @org = 0 + @labeldb = {} + end + + def parse(line) + case line[0] + when "@org" + @org = line[1].to_i + when "@dat" + line.shift + line.each do |d| + @memory[@org] = d.to_i + @org += 1 + end + when "add" + raise ParameterError, "Expected positional argument" unless line[3] =~ /\A@/ + @memory[@org] = Program.flag_digits(line[1..3]) + 1 + @memory[@org + 1] = Program.desigil(line[1]) + @memory[@org + 2] = Program.desigil(line[2]) + @memory[@org + 3] = Program.desigil(line[3]) + @org += 4 + when "mul" + raise ParameterError, "Expected positional argument" unless line[3] =~ /\A@/ + @memory[@org] = Program.flag_digits(line[1..3]) + 2 + @memory[@org + 1] = Program.desigil(line[1]) + @memory[@org + 2] = Program.desigil(line[2]) + @memory[@org + 3] = Program.desigil(line[3]) + @org += 4 + when "inp" + raise ParameterError, "Expected positional argument" unless line[1] =~ /\A@/ + @memory[@org] = 3 + @memory[@org + 1] = Program.desigil(line[1]) + @org += 2 + when "out" + @memory[@org] = Program.flag_digits(line[1..1]) + 4 + @memory[@org + 1] = Program.desigil(line[1]) + @org += 2 + when "jnz" + @memory[@org] = Program.flag_digits(line[1..2]) + 5 + @memory[@org + 1] = Program.desigil(line[1]) + @memory[@org + 2] = Program.desigil(line[2]) + @org += 3 + when "jez" + @memory[@org] = Program.flag_digits(line[1..2]) + 6 + @memory[@org + 1] = Program.desigil(line[1]) + @memory[@org + 2] = Program.desigil(line[2]) + @org += 3 + when "tlt" + raise ParameterError, "Expected positional argument" unless line[3] =~ /\A@/ + @memory[@org] = Program.flag_digits(line[1..3]) + 7 + @memory[@org + 1] = Program.desigil(line[1]) + @memory[@org + 2] = Program.desigil(line[2]) + @memory[@org + 3] = Program.desigil(line[3]) + @org += 4 + when "teq" + raise ParameterError, "Expected positional argument" unless line[3] =~ /\A@/ + @memory[@org] = Program.flag_digits(line[1..3]) + 8 + @memory[@org + 1] = Program.desigil(line[1]) + @memory[@org + 2] = Program.desigil(line[2]) + @memory[@org + 3] = Program.desigil(line[3]) + @org += 4 + when "arb" + @memory[@org] = Program.flag_digits(line[1..1]) + 9 + @memory[@org + 1] = Program.desigil(line[1]) + @org += 2 + when "hlt" + @memory[@org] = 99 + @org += 1 + when /(.*):/ + @labeldb[$1] = @org + end + end + + def dump + reify_nils + reify_labels + @memory.join(",") + end + + private + def reify_nils + @memory.map! do |x| + x.nil? ? 0 : x + end + end + + def reify_labels + @memory.map! do |x| + if x.is_a?(String) then + if @labeldb.has_key?(x) then + @labeldb[x] + else + raise LabelError, "Label not found" + end + else + x + end + end + end + + def self.flag_digits(args) + sig = 0 + until args.empty? do + a = args.pop + case a + when /\A@/ + when /\A&/ + sig += 2 + else + sig += 1 + end + sig *= 10 + end + sig * 10 + end + + def self.desigil(str) + case str + when /\A@/ + str[1..-1].to_i + when /\A&/ + str[1..-1].to_i + when /\A[+-0-9]/ + str.to_i + else + str + end + end +end + +p = Program.new + +lines.each do |line| + line = line.split(";")[0].strip + next if line.nil? || line.empty? + line = line.split(/\s*[,\s]\s*/) + p.parse(line) +end + +puts p.dump diff --git a/intcode/das b/intcode/das new file mode 100755 index 0000000..c84d9a0 --- /dev/null +++ b/intcode/das @@ -0,0 +1,77 @@ +#!/usr/bin/env ruby + +class Program + def initialize(program) + @mem = program + @pc = 0 + end + + def dis_next + opcode = @mem[@pc] % 100 + + p3, p2, p1 = ("%03d" % (@mem[@pc] / 100)).chars + + print "#{@pc}: " + case opcode + when 1 + puts "add #{Program.sigil(p1)}#{@mem[@pc+1]}, #{Program.sigil(p2)}#{@mem[@pc+2]}, #{Program.sigil(p3)}#{@mem[@pc+3]}" + @pc += 4 + when 2 + puts "mul #{Program.sigil(p1)}#{@mem[@pc+1]}, #{Program.sigil(p2)}#{@mem[@pc+2]}, #{Program.sigil(p3)}#{@mem[@pc+3]}" + @pc += 4 + when 3 + puts "inp #{Program.sigil(p1)}#{@mem[@pc+1]}" + @pc += 2 + when 4 + puts "out #{Program.sigil(p1)}#{@mem[@pc+1]}" + @pc += 2 + when 5 + puts "jnz #{Program.sigil(p1)}#{@mem[@pc+1]}, #{Program.sigil(p2)}#{@mem[@pc+2]}" + @pc += 3 + when 6 + puts "jez #{Program.sigil(p1)}#{@mem[@pc+1]}, #{Program.sigil(p2)}#{@mem[@pc+2]}" + @pc += 3 + when 7 + puts "tlt #{Program.sigil(p1)}#{@mem[@pc+1]}, #{Program.sigil(p2)}#{@mem[@pc+2]}, #{Program.sigil(p3)}#{@mem[@pc+3]}" + @pc += 4 + when 8 + puts "teq #{Program.sigil(p1)}#{@mem[@pc+1]}, #{Program.sigil(p2)}#{@mem[@pc+2]}, #{Program.sigil(p3)}#{@mem[@pc+3]}" + @pc += 4 + when 9 + puts "arb #{Program.sigil(p1)}#{@mem[@pc+1]}" + @pc += 2 + when 99 + puts "hlt" + @pc += 1 + else + puts "@dat #{@mem[@pc]}" + @pc += 1 + end + end + + def dis_all + until @pc >= @mem.length do + dis_next + end + end + + private + + def self.sigil(p) + case p + when "0" + "@" + when "2" + "&" + end + end +end + +exit 1 if ARGV.length != 1 + +source = ARGV.shift.strip + +input = File.readlines(source)[0].strip.split(",").map(&:to_i) + +p = Program.new(input) +p.dis_all -- cgit v1.2.1