Created
April 4, 2012 15:32
-
-
Save judofyr/2302836 to your computer and use it in GitHub Desktop.
Assembler for DCPU-16
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
that was fast.