Permalink
Browse files

Some fixes. - TODO: Docs

  • Loading branch information...
1 parent 85b10c1 commit 004cd8a92bd583284da39a637bd819aab39d0a1d @Deradon committed Apr 11, 2012
View
@@ -19,12 +19,17 @@ if ARGV.length == 1
assembler.write(tmp_file.path)
dump = tmp_file.read.unpack("S>*")
end
- debugger = DCPU16::Debugger.new(dump)
+ debugger = DCPU16::Debugger.new(dump, :update_every => 10000)
debugger.run
elsif ARGV.length == 2
file = File.open(ARGV[0])
assembler = DCPU16::Assembler.new(file.read)
assembler.write(ARGV[1])
+
+ File.open(ARGV[1] + ".hex", "w") do |file|
+ file.write(assembler.dump.join("\n"))
+ end
+
else
puts "Usage: dcpu16 <input> <output>"
exit
@@ -0,0 +1,155 @@
+; set screen width - the field width is 16
+; corresponding to 16 bits per row word
+
+SET X, 40 ; set your output screen width
+SUB X, 16 ;
+SET [0x10ff], X ; store offset
+
+; setup the starting field, 1 bit = 1 cell, 8 words = 128 cells
+:setup
+ SET [0x1001], 0x0021 ; numbers I picked randomly which create
+ SET [0x1002], 0x1110 ; some chaos
+ SET [0x1003], 0x0070
+ SET [0x1004], 0x0d09
+ SET [0x1005], 0x1fb1
+ SET [0x1006], 0x2a7f
+ SET [0x1007], 0x0aa4
+ SET [0x1008], 0x0404
+ SET I, 0
+ SET PC, draw
+
+; cycle through each row and draw its cells
+; I = current row, J = current bit, Y = bit id (for printing)
+:draw
+ ADD I, 1
+ SET J, 1
+ SET Y, 0
+ IFN I, 9
+ SET PC, drawcells
+ SET I, 0
+ SET PC, checkfield
+
+; cycle through each bit in row and draw as cell
+; I = current row, J = bit to mask, Y = bit#, X = (16*I)-Y (+printing offset)
+:drawcells
+ IFE J, 0
+ SET PC, draw
+ SET A, [0x1000+I]
+ AND A, J
+ SHL J, 1 ; shift mask bit
+ ADD Y, 1 ; increment bit id number
+ SET X, 16
+ MUL X, I
+ SUB X, Y
+ SET C, [0x10ff]
+ MUL C, I
+ ADD X, C
+ SUB X, [0x10ff]
+ IFE A, 0
+ SET [0x8000+X], 46
+ IFG A, 0
+ SET [0x8000+X], 48
+ SET PC, drawcells
+
+; cycle through each row and map neighbours
+:checkfield
+ ADD I, 1
+ IFN I, 9
+ SET PC, checkrow
+ SET I, 0
+ SET PC, newfield
+
+; generate and store 3 words A, B, C
+; A indicates if cell has 2 horizontal neighbours
+; B indicates if call has 1 "
+; C is a copy of current row, for checking vertical neighbours later
+:checkrow
+ SET A, [0x1000+I]
+ SET B, A
+ SET C, A
+ SHL A, 1 ; shift current row left
+ SHR B, 1 ; shift current row right
+ SET Y, C ; wrap around
+ AND Y, 1
+ IFN Y, 0
+ ADD B, 0x8000
+ SET Y, C
+ AND Y, 0x8000
+ IFN Y, 0
+ ADD A, 1
+ SET X, A ; store A
+ AND A, B ; AND A, B reveals which cells have 2 horizontal neighbours
+ XOR B, X ; XOR A(X), B reveals which cells have 1 horizontal neighbour
+ SET [0x2000+I], A
+ SET [0x3000+I], B
+ SET [0x4000+I], C
+ SET PC, checkfield
+
+; cycle through each row, create new generation
+:newfield
+ ADD I, 1
+ SET J, 1
+ SET X, 0 ; X will contain our new row
+ IFN I, 9
+ SET PC, newrow
+ SET I, 0
+ SET PC, draw
+
+; check each bit in row against data created by :checkfield, I = row
+:newrow
+ IFE J, 0
+ SET PC, writerow
+ SET Y, 0 ; counter
+ SET A, [0x1fff+I] ; check if cell, row+ cell,
+ SET B, [0x2000+I] ; and row- cell have 2 neighbours
+ SET C, [0x2001+I]
+ AND A, J
+ AND B, J ; mask bit and
+ AND C, J ; check for hit
+ IFN A, 0
+ ADD Y, 2
+ IFN B, 0
+ ADD Y, 2
+ IFN C, 0
+ ADD Y, 2
+ SET A, [0x2fff+I] ; check if cell, row+ cell,
+ SET B, [0x3000+I] ; and row- cell have 1 neighbour
+ SET C, [0x3001+I] ;
+ AND A, J
+ AND B, J
+ AND C, J
+ IFN A, 0
+ ADD Y, 1
+ IFN B, 0
+ ADD Y, 1
+ IFN C, 0
+ ADD Y, 1
+ SET A, [0x3fff+I] ; check if cell above is neighbour
+ SET B, [0x4001+I] ; check if cell below is neighbour
+ AND A, J
+ AND B, J
+ IFN A, 0
+ ADD Y, 1
+ IFN B, 0
+ ADD Y, 1
+ IFE Y, 3 ; if 3 neighbours, cell is alive
+ ADD X, J ; add the mask bit to X
+ IFE Y, 2 ; if 2 neighbours, cell remains alive
+ SET PC, checkalive ; if it is already
+ SHL J, 1
+ SET PC, newrow
+
+; if neighbours = 2, the cell will only continue living (not come alive)
+:checkalive
+ SET A, [0x1000+I]
+ AND A, J
+ IFN A, 0
+ ADD X, J
+ SHL J, 1
+ SET PC, newrow
+
+; replace row in field with new generation
+:writerow
+ SET [0x1000+I], X
+ SET PC, newfield
+
@@ -196,9 +196,11 @@ def assemble#(text)
next if cleaned.empty?
tokens = cleaned.split(/\s/)
- op.error("Wrong number of tokens - #{tokens}") unless (2..4) === tokens.size
+ op.error("Wrong number of tokens - #{tokens}") unless (1..4) === tokens.size
labels[tokens.shift[1..-1]] = location if tokens[0].start_with?(":")
+ next if tokens.size == 0
+
parse_op(op, tokens)
@body << op
View
@@ -32,7 +32,9 @@ class CPU
def initialize(memory = [])
@cycle = 0
@memory = DCPU16::Memory.new(memory)
- @registers = Array.new(REGISTERS_COUNT) { Register.new }
+ @registers = []
+ [:A, :B, :C, :X, :Y, :Z, :I, :J].each { |r| @registers << Register.new(0x0, r) }
+
@PC = Register.new(0x0)
@SP = Register.new(0xFFFF)
@O = Register.new(0x0)
@@ -62,7 +64,9 @@ def run
# Current clock_cycle
def hz
- diff = Time.now - (@started_at || Time.now)
+ now = Time.now
+ @started_at ||= now
+ diff = now - @started_at
diff = 1 if diff == 0
@cycle / diff
end
@@ -84,28 +88,30 @@ def last_instruction
# Perform a single step
# TODO: Refacor if/else/if/else ... hacky mess here
- def step
- @instruction = Instruction.new(@memory.read(@PC))
- @PC += 1
-
- op = @instruction.op
- a = get_operand(@instruction.a)
- b = get_operand(@instruction.b) if @instruction.b
-
- if @skip
- @skip = false
- else
- if b # Basic Instruction
- result = self.send(op, a.value, b.value)
- else # Non-Basic Instruction
- result = self.send(op, a.value)
+ def step(steps = 1)
+ steps.times do
+ @instruction = Instruction.new(@memory.read(@PC))
+ @PC += 1
+
+ op = @instruction.op
+ a = get_operand(@instruction.a)
+ b = get_operand(@instruction.b) if @instruction.b
+
+ if @skip
+ @skip = false
+ else
+ if b # Basic Instruction
+ result = self.send(op, a.value, b.value)
+ else # Non-Basic Instruction
+ result = self.send(op, a.value)
+ end
+ a.write(result) if result
end
- a.write(result) if result
- end
- # Notify observers
- changed
- notify_observers(self)
+ # Notify observers
+ changed
+ notify_observers(self)
+ end
end
def to_s
@@ -117,7 +123,11 @@ def to_s
# Clock: #{clock_cycle}
* HZ: #{hz}
* Last: #{last_instruction.inspect}
-* Reg.: #{registers.inspect}
+* Reg.: #{registers.join("\n ")}
+* SP: #{@SP}
+* PC: #{@PC}
+* O: #{@O}
+ 0x8000: #{memory[0x8000].value.to_s(16)}
EOF
end
@@ -3,8 +3,9 @@ class CPU
class Register
attr_reader :value
- def initialize(value = 0x0)
+ def initialize(value = 0x0, name = nil)
@default_value = value
+ @name = name
reset
end
@@ -25,12 +26,23 @@ def -(value)
end
def write(value)
+ value = 0 if value > 0xFFFF
@value = value
end
def reset
@value = @default_value
end
+
+ def inspect
+ { :name => @name, :value => @value, :default_value => @default_value }
+ end
+
+ def to_s
+ vh = ""
+ dh =
+ "name: #{@name}\tvalue: 0x%04x(#{@value})\tdefault: 0x%04x#{@default_value}" % [@value, @default_value]
+ end
end
end
end
@@ -1,25 +1,50 @@
module DCPU16
class Debugger
attr_reader :cpu, :screen
- def initialize(dump = [])
+ def initialize(dump = [], options = {})
@cpu = DCPU16::CPU.new(dump)
@screen = DCPU16::Screen.new(@cpu.memory)
+ @update_every = options[:update_every] || 100000
+ @step_mode = true#false || options[:step_mode]
@cpu.add_observer(self)
end
def run
- clear_screen
+ at_exit do
+ puts @cpu.to_s
+ end
+
begin
- @cpu.run
+ if @step_mode
+ @update_every = nil
+ while a = $stdin.gets
+ a = a.to_i
+ a = (@last_step_count || 1) if a < 1
+ @last_step_count = a
+
+ @cpu.step(a)
+ print @screen
+ print @cpu.to_s
+ end
+ else
+ clear_screen
+ @cpu.run
+ end
rescue DCPU16::Instructions::Reserved => e
- puts @cpu.to_s
+ print @cpu.to_s
end
end
# Observer
def update(cpu)
- #cpu
+ @counter ||= 0
+ if @update_every && @counter > @update_every
+ @counter = 0
+ clear_screen
+ print @cpu.to_s
+ end
+ @counter += 1
end
# Clears screen for output
@@ -28,6 +28,7 @@ def write(offset, value)
# HACK: so we can just pass a Fixnum or a Register
offset = offset.value if offset.respond_to? :value
+ value &= 0xFFFF
@memory[offset] = value
# Notify observers
@@ -13,6 +13,7 @@ def value
alias_method :read, :value
def write(value)
+ value &= 0xFFFF
@value = value
@memory.write(@offset, value)
end
Oops, something went wrong.

0 comments on commit 004cd8a

Please sign in to comment.