aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNat Lasseter <user@4574.co.uk>2019-12-11 21:53:12 +0000
committerNat Lasseter <user@4574.co.uk>2019-12-11 21:53:12 +0000
commit693130cae9bdb145af79ee2eb256b661003b6e45 (patch)
treefc4a450b012545c0f5599c667426d44451fbf71d
parent3bea5ec1373b842e6ad9f7cff050615e1f5ebde8 (diff)
Wrote Intcode (dis-)assembler
-rwxr-xr-xday09/das73
-rw-r--r--intcode/Readme.textile52
-rwxr-xr-xintcode/as155
-rwxr-xr-xintcode/das77
4 files changed, 284 insertions, 73 deletions
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 <code>@label</code>, 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
+
+- <code>@</code> := 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