Skip to content

Instantly share code, notes, and snippets.

@judofyr
Created April 4, 2012 15:32
Show Gist options
  • Save judofyr/2302836 to your computer and use it in GitHub Desktop.
Save judofyr/2302836 to your computer and use it in GitHub Desktop.
Assembler for DCPU-16
class Register < Struct.new(:name)
BASIC = %w[A B C X Y Z I J].map(&:to_sym)
SPECIAL = %w[POP PEEK PUSH SP PC O].map(&:to_sym)
ALL = BASIC + SPECIAL
attr_accessor :plus
def value
case name
when *BASIC
BASIC.index(name)
when *SPECIAL
SPECIAL.index(name) + 0x18
end
end
def +(n)
@plus = n
self
end
end
class Instruction < Struct.new(:name, :a, :b)
BASIC = %w[SET ADD SUB MUL DIV MOD SHL SHR AND BOR XOR IFE IFN IFG IFB].map(&:to_sym)
NON = %w[JSR].map(&:to_sym)
ALL = BASIC + NON
def basic?
BASIC.include?(name)
end
def opcode
if basic?
BASIC.index(name) + 1
else
NON.index(name) + 1
end
end
def to_memory(mem = [])
if basic?
acode, an = value(a)
bcode, bn = value(b)
mem << (opcode | (acode << 4) | (bcode << 10))
mem << an if an
mem << bn if bn
else
acode, an = value(a)
mem << ((opcode << 4) | (acode << 10))
mem << an if an
end
mem
end
def value(thing)
case thing
when Register
if thing.plus
[thing.value + 0x10, thing.plus]
else
thing.value
end
when Indirect
thing.value
when Fixnum
if thing <= 0x1F
thing + 0x20
else
[0x1F, thing]
end
else
[0x1F, thing]
end
end
end
class Indirect < Struct.new(:cell)
def value
case cell
when Fixnum
[0x1E, cell]
when Register
0x08 + cell.value
else
raise "Missing: #{cell}"
end
end
end
class Unit
Register::ALL.each do |name|
define_method(name.to_s.downcase) { Register.new(name) }
end
Instruction::BASIC.each do |name|
define_method(name) do |a, b|
@ins << Instruction.new(name.to_sym, a, b)
end
end
def JSR(a)
@ins << Instruction.new(:JSR, a)
end
def at(t)
Indirect.new(t)
end
def initialize
@ins = []
end
def label(name)
@ins << name
end
def memory
labels = {}
res = []
@ins.each do |i|
if i.is_a?(Symbol)
labels[i] = res.compact.size
else
res.concat(i.to_memory)
end
end
res.each_with_index do |x, idx|
if x.is_a?(Symbol)
res[idx] = labels[x]
end
end
res
end
def dump
memory.each_slice(8).each_with_index do |r, i|
print "#{(i*8).to_s(16).rjust(4, '0')}: "
puts r.map { |x| x.to_s(16).rjust(4, '0') }.join(" ")
end
end
end
def unit(&blk)
Unit.new.tap { |x| x.instance_eval(&blk) }
end
if $0 == __FILE__
unit do
SET a, 0x30
SET at(0x1000), 0x20
SUB a, at(0x1000)
IFN a, 0x10
SET pc, :crash
SET i, 10
SET a, 0x2000
label :loop
SET (i+0x2000), at(a)
SUB i, 1
IFN i, 0
SET pc, :loop
SET x, 0x4
JSR :testsub
SET pc, :crash
label :testsub
SHL x, 0x4
SET pc, pop
label :crash
SET pc, :crash
end.dump
end
@judofyr
Copy link
Author

judofyr commented Apr 13, 2012

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment