Rdcpu16 0.0.4.beta → 0.0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/bin/dcpu16 CHANGED
@@ -19,12 +19,19 @@ if ARGV.length == 1
19
19
  assembler.write(tmp_file.path)
20
20
  dump = tmp_file.read.unpack("S>*")
21
21
  end
22
- debugger = DCPU16::Debugger.new(dump)
22
+
23
+ # TODO: get args from command line
24
+ debugger = DCPU16::Debugger.new(dump, :update_every => 10000, :step_mode => false)
23
25
  debugger.run
24
26
  elsif ARGV.length == 2
25
27
  file = File.open(ARGV[0])
26
28
  assembler = DCPU16::Assembler.new(file.read)
27
29
  assembler.write(ARGV[1])
30
+
31
+ File.open(ARGV[1] + ".hex", "w") do |file|
32
+ file.write(assembler.to_s)
33
+ end
34
+
28
35
  else
29
36
  puts "Usage: dcpu16 <input> <output>"
30
37
  exit
@@ -1,275 +1,88 @@
1
- # WIP
2
- # Taken from: https://github.com/dcpu16/dcpu16-rb/blob/master/asm.rb
3
- module DCPU16
4
- class Assembler
5
- class Instruction
6
- attr_accessor :inst, :a, :b
7
- attr_reader :num, :line
8
-
9
- def initialize(num, line)
10
- @num, @line = num, line
11
- @inst, @a, @b = 0, 0, 0
12
- @extensions = []
13
- @references = []
14
- end
15
-
16
- def extend(value)
17
- # Check value against max int
18
- @extensions << value
19
- end
20
-
21
- def reference(label)
22
- @references[@extensions.size] = label
23
- @extensions << 0 # Placeholder
24
- end
25
-
26
- def resolve(labels)
27
- @references.each_with_index do |label, i|
28
- next unless label
29
- location = labels[label]
30
- error("Cannot find label #{label} in #{labels.keys.join(", ")}") unless location
31
- @extensions[i] = location
32
- end
33
- end
1
+ require 'dcpu16/assembler/constants'
2
+ require 'dcpu16/assembler/instruction'
3
+ require 'dcpu16/assembler/line'
34
4
 
35
- def word
36
- @inst + (@a << 4) + (@b << 10)
37
- end
38
-
39
- def words
40
- [word] + @extensions.dup
41
- end
42
-
43
- def bytes
44
- words.map{|w| [w].pack('n') }.join("")
45
- end
46
-
47
- def size
48
- @extensions.size + 1
49
- end
50
-
51
- def hex(w)
52
- "%04x" % w
53
- end
54
-
55
- def to_s
56
- "#{@num}: #{words.map{|w| hex(w)}.join(" ")}" # ; #{line}"
57
- end
58
-
59
- def error(message)
60
- raise Exception.new("Line #{num}: #{message}\n#{line}")
61
- end
62
- end
63
-
64
-
65
-
66
- def self.declare(map, start, string)
67
- count = start
68
- string.split(" ").each do |token|
69
- map[token] = count
70
- count += 1
71
- end
72
- end
73
-
74
- HEX_RE = /^0x[0-9a-fA-F]+$/
75
- INT_RE = /^\d+$/
76
- REG_RE = /^[A-Z]+$/
77
- LABEL_RE = /^[a-z_]+$/
78
- INDIRECT_RE = /^\[.+\]/
79
- INDIRECT_OFFSET_RE = /^[^+]+\+[^+]+$/
80
-
81
- EXT_PREFIX = 0
82
-
83
- INDIRECT = 0x08
84
- INDIRECT_OFFSET = 0x10
85
- INDIRECT_NEXT = 0x1e
86
- NEXT = 0x1f
87
- LITERAL = 0x20
88
-
89
- INSTRUCTIONS = {}
90
- EXTENDED_INSTRUCTIONS = {}
91
- VALUES = {}
92
-
93
- declare(INSTRUCTIONS, 1, "SET ADD SUB MUL DIV MOD SHL SHR AND BOR XOR IFE IFN IFG IFB")
94
- declare(EXTENDED_INSTRUCTIONS, 1, "JSR")
95
- declare(VALUES, 0, "A B C X Y Z I J")
96
- declare(VALUES, 0x18, "POP PEEK PUSH SP PC O")
97
5
 
6
+ module DCPU16
7
+ class Assembler
98
8
  attr_reader :input
99
- def initialize(input)
100
- @body = []
101
- @label_these = []
102
- @input = input
103
- self.assemble
104
- end
105
-
106
- def clean(line)
107
- line.gsub(/;.*/, "").gsub(/,/, " ").gsub(/\s+/, " ").strip
108
- end
109
9
 
110
- def dehex(token)
111
- return token.hex.to_s if HEX_RE === token
112
- token
10
+ def initialize(text)
11
+ @input = text
12
+ assemble
113
13
  end
114
14
 
115
- def parse_value(token, op)
116
- token = dehex(token)
117
-
118
- case token
119
- when INT_RE
120
- value = token.to_i
121
- return LITERAL + value if value <= 31
122
- op.extend value
123
- return NEXT
124
-
125
- when REG_RE
126
- return VALUES[token]
127
-
128
- when LABEL_RE
129
- op.reference(token)
130
- return NEXT
131
-
132
- when INDIRECT_RE
133
- inner = dehex(token[1..-2])
134
- case inner
135
- when INT_RE
136
- value = inner.to_i
137
- op.extend value
138
- return INDIRECT_NEXT
139
-
140
- when REG_RE
141
- reg = VALUES[inner]
142
- op.error("Can't use indirect addressing on non-basic reg #{reg}") unless reg <= VALUES["J"]
143
- return INDIRECT + reg
144
-
145
- when LABEL_RE
146
- op.reference(inner)
147
- return INDIRECT_NEXT
148
-
149
- when INDIRECT_OFFSET_RE
150
- offset, reg = inner.split("+").map{|x| x.strip }
151
- offset = dehex(offset)
152
-
153
- op.error("Malformed indirect offset value #{inner}") unless INT_RE === offset && REG_RE === reg
154
- value = offset.to_i# + VALUES[reg]
155
- op.extend value
156
-
157
- return INDIRECT_OFFSET + VALUES[reg]
158
- end
159
- else
160
- op.error("Unrecognized value #{token}")
161
- end
162
- end
163
-
164
- def parse_op(op, tokens)
165
- inst_name = tokens.shift
166
-
167
- inst_code = INSTRUCTIONS[inst_name]
168
- if inst_code
169
- op.inst = inst_code
170
- op.a = parse_value(tokens.shift, op)
171
- op.b = parse_value(tokens.shift, op)
172
- return
173
- end
174
-
175
- inst_code = EXTENDED_INSTRUCTIONS[inst_name]
176
- if inst_code
177
- op.inst = EXT_PREFIX
178
- op.a = inst_code
179
- op.b = parse_value(tokens.shift, op)
180
- end
181
-
182
- raise Exception.new("No such instruction: #{inst_name}") unless inst_code
183
- end
184
-
185
- def assemble#(text)
186
- text = @input
187
- labels = {}
188
-
189
- num = 0
15
+ # Assemble the given input.
16
+ def assemble
190
17
  location = 0
191
- text.each_line do |line|
192
- num += 1
193
- op = Instruction.new(num, line)
194
-
195
- cleaned = clean(line)
196
- next if cleaned.empty?
18
+ @labels = {}
19
+ @body = []
197
20
 
198
- tokens = cleaned.split(/\s/)
199
- op.error("Wrong number of tokens - #{tokens}") unless (2..4) === tokens.size
21
+ lines.each do |line|
22
+ # Set label location
23
+ @labels[line.label] = location if line.label
200
24
 
201
- labels[tokens.shift[1..-1]] = location if tokens[0].start_with?(":")
202
- parse_op(op, tokens)
25
+ # Skip when no op
26
+ next if line.op.empty?
203
27
 
28
+ op = Instruction.create(line.op, line.args, location)
204
29
  @body << op
205
30
  location += op.size
206
31
  end
207
32
 
208
- @body.each {|op| op.resolve(labels) }
209
-
210
- #display
33
+ # Apply labels
34
+ begin
35
+ @body.each { |op| op.apply_labels(@labels) }
36
+ rescue Exception => e
37
+ puts @labels.inspect
38
+ raise e
39
+ end
211
40
  end
212
41
 
213
- def display
214
- @body.each { |op| puts op }
42
+ # Returns assembled bytes
43
+ def bytes
44
+ @body.map { |op| op.bytes }.join
215
45
  end
216
46
 
47
+ def to_s
48
+ @body.join("\n")
49
+ end
217
50
 
218
- def dump#(filename)
219
- @body
220
- # File.open(filename, "w") do |file|
221
- # @body.each {|inst| file.write(inst.bytes) }
222
- # end
51
+ def lines
52
+ @lines ||= read_lines
223
53
  end
224
54
 
55
+ # Write bytes to file
225
56
  def write(filename)
226
- File.open(filename, "w") do |file|
227
- @body.each {|inst| file.write(inst.bytes) }
228
- end
57
+ File.open(filename, "w") { |file| file.write(bytes) }
229
58
  end
230
- end
231
- end
232
-
233
-
234
- #if __FILE__ == $PROGRAM_NAME
235
- # asm = Assembler.new
236
- # filename = "#{ARGV.first || "out.s"}.o"
237
- # asm.assemble ARGF
238
- # asm.dump filename
239
- #end
240
59
 
241
60
 
242
61
 
62
+ private
243
63
 
64
+ def read_lines
65
+ @lines = []
66
+ @input.each_line do |line|
67
+ empty = (line =~ RE_LINE_EMPTY)
68
+ comment = (line =~ RE_LINE_COMMENT)
244
69
 
70
+ next if empty || comment
245
71
 
72
+ line.gsub!(RE_LINE_CLEAN) { $1 }
246
73
 
74
+ match = line.match(RE_LINE)
75
+ label = match[1]
76
+ op = match[2]
77
+ args = match[3]
78
+ args = args.scan( RE_ARGS ).flatten.compact
247
79
 
80
+ line = Line.new(label, op, args)
81
+ @lines << line
82
+ end
248
83
 
249
- #attr_reader :input
250
- #def initialize(text)
251
- # @input = text
252
- #end
253
-
254
- #def dump
255
- # lines = []
256
- # @input.each_line do |line|
257
- # empty = (line =~ /^\s*$/)
258
- # comment = (line =~ /^\s*;+.*$/)
259
-
260
- # next if empty || comment
261
-
262
- # line.gsub!(/^\s*([^;]*).*$/) { $1 } # Strip some spaces
263
-
264
- # regex = /^(:\w*)?\s*(\w*)\s*([^,\s]*),?\s?([^\s]*).*$/
265
- # match = line.match(regex)
266
-
267
- # line = { :label => match[1],
268
- # :op => match[2],
269
- # :a => match[3],
270
- # :b => match[4] }
271
- # puts line
272
- # lines << line
273
- # end
274
- #end
84
+ return @lines
85
+ end
86
+ end
87
+ end
275
88
 
@@ -0,0 +1,48 @@
1
+ module DCPU16
2
+ class Assembler
3
+ RE_LINE_EMPTY = /^\s*$/
4
+ RE_LINE_COMMENT = /^\s*;+.*$/
5
+
6
+ #RE_LINE_CLEAN = /^\s*([^;]*).*$/
7
+ # "asd" | 'asd' | [^"';"]
8
+ RE_LINE_CLEAN = /^\s*((?:(?:"[^"]*")|(?:'[^']*')|(?:[^;'"]*))*).*$/
9
+
10
+ # Parsing the line
11
+ RE_LINE = /\A
12
+ (:\w*)? # label
13
+ \s*
14
+ (\w*) # op
15
+ \s*
16
+ (.*?) # args
17
+ \s*
18
+ \Z/x
19
+
20
+ # Parsing the arguments
21
+ # TODO: no comma needed atm, fix this
22
+ RE_ARGS = /([^'",\s]+)|("[^"]+")|('[^']+')/
23
+ # args.scan( RE_ARGS ).flatten.compact
24
+
25
+
26
+ # Regex against a op: 1: label | 2: OP | 3: A | 4: b
27
+ #RE_OP = /^(:\w*)?\s*(\w*)\s*([^,\s]*),?\s?([^\s]*).*$/
28
+ RE_OP = /^(:\w*)?\s*(\w*)\s*([^,\n]*),?\s?([^\s]*).*$/
29
+
30
+ REGISTER = {
31
+ :A => 0x0,
32
+ :B => 0x1,
33
+ :C => 0x2,
34
+ :X => 0x3,
35
+ :Y => 0x4,
36
+ :Z => 0x5,
37
+ :I => 0x6,
38
+ :J => 0x7,
39
+ :POP => 0x18,
40
+ :PEEK => 0x19,
41
+ :PUSH => 0x1a,
42
+ :SP => 0x1b,
43
+ :PC => 0x1c,
44
+ :O => 0x1d
45
+ }
46
+ end
47
+ end
48
+
@@ -0,0 +1,235 @@
1
+ module DCPU16
2
+ class Assembler
3
+
4
+ BASIC_INSTRUCTIONS = {
5
+ :SET => 0x1,
6
+ :ADD => 0x2,
7
+ :SUB => 0x3,
8
+ :MUL => 0x4,
9
+ :DIV => 0x5,
10
+ :MOD => 0x6,
11
+ :SHL => 0x7,
12
+ :SHR => 0x8,
13
+ :AND => 0x9,
14
+ :BOR => 0xa,
15
+ :XOR => 0xb,
16
+ :IFE => 0xc,
17
+ :IFN => 0xd,
18
+ :IFG => 0xe,
19
+ :IFB => 0xf
20
+ }
21
+
22
+ NON_BASIC_INSTRUCTIONS = {
23
+ :JSR => 0x01
24
+ }
25
+
26
+ INSTRUCTIONS = BASIC_INSTRUCTIONS.merge(NON_BASIC_INSTRUCTIONS)
27
+ # INDIRECT: [x+y] =>
28
+
29
+ # + 0x00-0x07: register (A, B, C, X, Y, Z, I or J, in that order) SET A 0x0001
30
+ # I 0x08-0x0f: [register] SET [A] 0x????
31
+ # I 0x10-0x17: [next word + register] SET [A+0x1000] 0x???? 0x????
32
+ # + 0x18: POP / [SP++]
33
+ # + 0x19: PEEK / [SP]
34
+ # + 0x1a: PUSH / [--SP]
35
+ # + 0x1b: SP
36
+ # + 0x1c: PC
37
+ # + 0x1d: O
38
+ # I 0x1e: [next word] SET [0x1000] 0x???? 0x????
39
+ # V 0x1f: next word (literal) SET 0x1000 0x???? 0x????
40
+ # V 0x20-0x3f: literal value 0x00-0x1f (literal) SET 0x???? 0x????
41
+ class Instruction; end;
42
+
43
+ # BasicInstruction like: SET, ADD, ...
44
+ class BasicInstruction < Instruction
45
+ def initialize(op, a, b, location)
46
+ super(op, location)
47
+ @a = value(a)
48
+ @b = value(b)
49
+ end
50
+
51
+ def word
52
+ @op_value + (@a << 4) + (@b << 10)
53
+ end
54
+ end
55
+
56
+ # NonBasicInstruction like: JSR
57
+ class NonBasicInstruction < Instruction
58
+ def initialize(op, a, location)
59
+ super(op, location)
60
+ @a = value(a)
61
+ end
62
+
63
+
64
+
65
+ def word
66
+ (@op_value << 4) + (@a << 10)
67
+ end
68
+ end
69
+
70
+ # DatInstruction: dat "foobar"
71
+ class DatInstruction < Instruction
72
+ def initialize(op, args, location)
73
+ super(op, location)
74
+
75
+ args.each do |arg|
76
+ # TODO: shitty string detection here, refactor please
77
+ re_string = /^"([^"]*)"|'([^']*)'$/
78
+ match = arg.match(re_string)
79
+ if match
80
+ s = match[1] || match[2]
81
+ s.each_byte { |b| @words << b } # Add chars
82
+ else
83
+ @words << dehex(arg) # No string given, assume value/label here
84
+ end
85
+ end
86
+ end
87
+
88
+ def words
89
+ @words
90
+ end
91
+
92
+ def size
93
+ @words.size
94
+ end
95
+ end
96
+
97
+
98
+
99
+
100
+ class Instruction #< Struct.new(:op, :args, :location)
101
+ RE_INDIRECT = /^\[([^+]+)\+?([^+]+)?\]$/
102
+ RE_HEX = /^0[xX][0-9a-fA-F]{1,4}$/
103
+ RE_INT = /^\d{1,5}$/
104
+ LITERALS = (0x00..0x1f)
105
+
106
+ attr_accessor :a, :b, :location, :op
107
+
108
+ class << self
109
+ def instruction_type(op)
110
+ return :basic if BASIC_INSTRUCTIONS[op]
111
+ return :non_basic if NON_BASIC_INSTRUCTIONS[op]
112
+ return op
113
+ end
114
+
115
+ # InstructionFactory
116
+ def create(op, args, location)
117
+ @op = op.upcase.intern
118
+
119
+ case instruction_type(@op)
120
+ when :basic
121
+ BasicInstruction.new(@op, args[0], args[1], location)
122
+ when :non_basic
123
+ NonBasicInstruction.new(@op, args[0], location)
124
+ when :DAT
125
+ DatInstruction.new(@op, args, location)
126
+ else
127
+ raise "Instruction not found: #{@op}"
128
+ end
129
+ end
130
+ end
131
+
132
+
133
+ def initialize(op, location)
134
+ @words = []
135
+ @op = op
136
+ @location = location
137
+ @op_value = INSTRUCTIONS[@op]
138
+ end
139
+
140
+
141
+ def add_word(value)
142
+ raise "NilValue" unless value
143
+ @words << value
144
+ end
145
+
146
+ def value(operand)
147
+ operand = operand.to_s
148
+
149
+ register = REGISTER[operand.upcase.intern]
150
+ return register if register
151
+
152
+ # Is a indirect: [A], [0x8000], [0x8000 + A], [0x30]
153
+ indirect = operand.match(RE_INDIRECT)
154
+ if indirect
155
+ if indirect[2]
156
+ # hacky implementation
157
+ r = REGISTER[indirect[2].upcase.intern]
158
+ i = 1
159
+ if !r
160
+ r = REGISTER[indirect[1].upcase.intern]
161
+ i = 2
162
+ end
163
+ raise "Something went wrong (yeah, its a stupid message right here) [#{operand}]" unless r
164
+ add_word(dehex(indirect[i]))
165
+
166
+ # 0x10-0x17: [next word + register]
167
+ return (r + 0x10)
168
+ else
169
+ r = REGISTER[indirect[1].upcase.intern]
170
+ if r
171
+ # 0x08-0x0f: [register]
172
+ return (r + 0x08)
173
+ else
174
+ # 0x1e: [next word]
175
+ add_word(dehex(indirect[1]))
176
+ return 0x1e
177
+ end
178
+ end
179
+ end
180
+
181
+ value = dehex(operand)
182
+ if (0x00..0x1f).include?(value)
183
+ # 0x20-0x3f: literal value 0x00-0x1f (literal)
184
+ return (value + 0x20)
185
+ else
186
+ # 0x1f: next word (literal) || next word (label)
187
+ add_word(value)
188
+ return 0x1f
189
+ end
190
+ end
191
+
192
+ def word
193
+ raise "Must be implemented by #{self.class.name}!"
194
+ end
195
+
196
+ # Returns size of Instruction in bytes
197
+ def size
198
+ @words.size + 1
199
+ end
200
+
201
+ # Add Labels to values
202
+ def apply_labels(labels = {})
203
+ @words.map! { |word| labels[word] || dehex(word, :raise => true) }
204
+ end
205
+
206
+ def words
207
+ [word] + @words
208
+ end
209
+
210
+ def bytes
211
+ words.map { |w| [w].pack('n') }.join("")
212
+ end
213
+
214
+ def to_s(options = {:line_number => false})
215
+ raw = words.map{|w| hex(w)}.join(" ")
216
+ options[:line_number] ? "#{location.to_s(16)}:\t#{raw}" : raw
217
+ end
218
+
219
+ # Convert string to hex or dec value; if none of them return input
220
+ # Raise error if option enabled and no hex/dec value
221
+ def dehex(v, options = {:raise => false})
222
+ v = v.to_s
223
+ return v.hex if v.match(RE_HEX)
224
+ return v.to_i if v.match(RE_INT)
225
+ raise "'#{v}' is no Hex or Int (label not found?) [#{@words.inspect}]\n" if options[:raise]
226
+ return v.downcase
227
+ end
228
+
229
+ def hex(w)
230
+ "%04x" % w
231
+ end
232
+ end
233
+ end
234
+ end
235
+
@@ -0,0 +1,16 @@
1
+ module DCPU16
2
+ class Assembler
3
+ class Line < Struct.new(:raw_label, :op, :args)
4
+ # TODO: refactor
5
+ def label
6
+ return @label if @label || raw_label.nil?
7
+
8
+ @label = raw_label.downcase
9
+ @label = @label[1, @label.length] if @label.start_with?(":")
10
+
11
+ return @label
12
+ end
13
+ end
14
+ end
15
+ end
16
+
@@ -32,7 +32,9 @@ module DCPU16
32
32
  def initialize(memory = [])
33
33
  @cycle = 0
34
34
  @memory = DCPU16::Memory.new(memory)
35
- @registers = Array.new(REGISTERS_COUNT) { Register.new }
35
+ @registers = []
36
+ [:A, :B, :C, :X, :Y, :Z, :I, :J].each { |r| @registers << Register.new(0x0, r) }
37
+
36
38
  @PC = Register.new(0x0)
37
39
  @SP = Register.new(0xFFFF)
38
40
  @O = Register.new(0x0)
@@ -62,7 +64,9 @@ module DCPU16
62
64
 
63
65
  # Current clock_cycle
64
66
  def hz
65
- diff = Time.now - (@started_at || Time.now)
67
+ now = Time.now
68
+ @started_at ||= now
69
+ diff = now - @started_at
66
70
  diff = 1 if diff == 0
67
71
  @cycle / diff
68
72
  end
@@ -84,28 +88,30 @@ module DCPU16
84
88
 
85
89
  # Perform a single step
86
90
  # TODO: Refacor if/else/if/else ... hacky mess here
87
- def step
88
- @instruction = Instruction.new(@memory.read(@PC))
89
- @PC += 1
90
-
91
- op = @instruction.op
92
- a = get_operand(@instruction.a)
93
- b = get_operand(@instruction.b) if @instruction.b
94
-
95
- if @skip
96
- @skip = false
97
- else
98
- if b # Basic Instruction
99
- result = self.send(op, a.value, b.value)
100
- else # Non-Basic Instruction
101
- result = self.send(op, a.value)
91
+ def step(steps = 1)
92
+ steps.times do
93
+ @instruction = Instruction.new(@memory.read(@PC))
94
+ @PC += 1
95
+
96
+ op = @instruction.op
97
+ a = get_operand(@instruction.a)
98
+ b = get_operand(@instruction.b) if @instruction.b
99
+
100
+ if @skip
101
+ @skip = false
102
+ else
103
+ if b # Basic Instruction
104
+ result = self.send(op, a.value, b.value)
105
+ else # Non-Basic Instruction
106
+ result = self.send(op, a.value)
107
+ end
108
+ a.write(result) if result
102
109
  end
103
- a.write(result) if result
104
- end
105
110
 
106
- # Notify observers
107
- changed
108
- notify_observers(self)
111
+ # Notify observers
112
+ changed
113
+ notify_observers(self)
114
+ end
109
115
  end
110
116
 
111
117
  def to_s
@@ -117,7 +123,11 @@ module DCPU16
117
123
  # Clock: #{clock_cycle}
118
124
  * HZ: #{hz}
119
125
  * Last: #{last_instruction.inspect}
120
- * Reg.: #{registers.inspect}
126
+ * Reg.: #{registers.join("\n ")}
127
+ * SP: #{@SP}
128
+ * PC: #{@PC}
129
+ * O: #{@O}
130
+ 0x8000: #{memory[0x8000].value.to_s(16)}
121
131
  EOF
122
132
  end
123
133
 
@@ -50,6 +50,7 @@ module DCPU16
50
50
  end
51
51
  end
52
52
 
53
+ # TODO: what if noop found?
53
54
  def op
54
55
  @non_basic ? NON_BASIC_INSTRUCTIONS[@opcode] : INSTRUCTIONS[@opcode]
55
56
  end
@@ -3,8 +3,9 @@ module DCPU16
3
3
  class Register
4
4
  attr_reader :value
5
5
 
6
- def initialize(value = 0x0)
6
+ def initialize(value = 0x0, name = nil)
7
7
  @default_value = value
8
+ @name = name
8
9
  reset
9
10
  end
10
11
 
@@ -16,21 +17,33 @@ module DCPU16
16
17
 
17
18
  def +(value)
18
19
  @value += value
20
+ @value &= 0xFFFF
19
21
  self
20
22
  end
21
23
 
22
24
  def -(value)
23
25
  @value -= value
26
+ @value &= 0xFFFF
24
27
  self
25
28
  end
26
29
 
27
30
  def write(value)
28
- @value = value
31
+ @value = value & 0xFFFF
29
32
  end
30
33
 
31
34
  def reset
32
35
  @value = @default_value
33
36
  end
37
+
38
+ def inspect
39
+ { :name => @name, :value => @value, :default_value => @default_value }
40
+ end
41
+
42
+ def to_s
43
+ vh = ""
44
+ dh =
45
+ "name: #{@name}\tvalue: 0x%04x(#{@value})\tdefault: 0x%04x#{@default_value}" % [@value, @default_value]
46
+ end
34
47
  end
35
48
  end
36
49
  end
@@ -1,25 +1,51 @@
1
1
  module DCPU16
2
2
  class Debugger
3
3
  attr_reader :cpu, :screen
4
- def initialize(dump = [])
4
+ def initialize(dump = [], options = {})
5
5
  @cpu = DCPU16::CPU.new(dump)
6
6
  @screen = DCPU16::Screen.new(@cpu.memory)
7
+ @update_every = options[:update_every] || 100000
8
+ @step_mode = false || options[:step_mode]
7
9
 
8
10
  @cpu.add_observer(self)
9
11
  end
10
12
 
11
13
  def run
12
- clear_screen
14
+ at_exit do
15
+ print @screen
16
+ puts @cpu.to_s
17
+ end
18
+
13
19
  begin
14
- @cpu.run
20
+ if @step_mode
21
+ @update_every = nil
22
+ while a = $stdin.gets
23
+ a = a.to_i
24
+ a = (@last_step_count || 1) if a < 1
25
+ @last_step_count = a
26
+
27
+ @cpu.step(a)
28
+ print @screen
29
+ print @cpu.to_s
30
+ end
31
+ else
32
+ clear_screen
33
+ @cpu.run
34
+ end
15
35
  rescue DCPU16::Instructions::Reserved => e
16
- puts @cpu.to_s
36
+ print @cpu.to_s
17
37
  end
18
38
  end
19
39
 
20
40
  # Observer
21
41
  def update(cpu)
22
- #cpu
42
+ @counter ||= 0
43
+ if @update_every && @counter > @update_every
44
+ @counter = 0
45
+ clear_screen
46
+ print @cpu.to_s
47
+ end
48
+ @counter += 1
23
49
  end
24
50
 
25
51
  # Clears screen for output
@@ -28,6 +28,7 @@ module DCPU16
28
28
  # HACK: so we can just pass a Fixnum or a Register
29
29
  offset = offset.value if offset.respond_to? :value
30
30
 
31
+ value &= 0xFFFF
31
32
  @memory[offset] = value
32
33
 
33
34
  # Notify observers
@@ -13,6 +13,7 @@ module DCPU16
13
13
  alias_method :read, :value
14
14
 
15
15
  def write(value)
16
+ value &= 0xFFFF
16
17
  @value = value
17
18
  @memory.write(@offset, value)
18
19
  end
@@ -39,6 +39,7 @@ module DCPU16
39
39
 
40
40
 
41
41
  # Initial Screen dump
42
+ # CHECK: might be buggy
42
43
  @chars = []
43
44
  @height.times do |h|
44
45
  @width.times do |w|
@@ -57,14 +58,14 @@ module DCPU16
57
58
  end
58
59
 
59
60
  def memory_offset_end
60
- @memory_offset_end ||= 0x82FE#@memory_offset + size*2 - 2
61
+ @memory_offset_end ||= @memory_offset + size - 1
61
62
  end
62
63
 
63
64
  def to_s
64
65
  return @to_s if @to_s
65
66
 
66
67
  @to_s = "\e[?1049h\e[17;1H"
67
- @to_s << chars.join
68
+ @to_s << @chars.join
68
69
  @to_s << frame
69
70
  end
70
71
 
@@ -93,16 +94,16 @@ module DCPU16
93
94
  # Callback from observed memory
94
95
  def update(offset, value)
95
96
  return unless (memory_offset..memory_offset_end).include?(offset)
96
- # @to_s = nil
97
+ @to_s = nil
97
98
 
98
- diff = (offset - @memory_offset) / 2
99
+ diff = offset - @memory_offset
99
100
  h = diff / @width
100
101
  w = diff % @width
101
102
  @chars[diff] = Char.new(value, w + @x_offset, h + @y_offset)
102
103
  print @chars[diff]
103
- # changed
104
- # notify_observers(self)
105
- # print @chars[diff]
104
+ changed
105
+ notify_observers(self)
106
+ print @chars[diff]
106
107
  end
107
108
  end
108
109
 
@@ -137,19 +138,3 @@ module DCPU16
137
138
  end
138
139
  end
139
140
 
140
-
141
-
142
- #The high 8 bits determine the color; the highest 4 are the foreground and the lowest 4 are the background
143
-
144
- #args = []
145
- #args << (value >> 15)
146
- #if value > 0x7F
147
- # args << color_to_ansi(value >> 12) + 30
148
- # args << color_to_ansi(value >> 8) + 40
149
- #end
150
-
151
- #char = " " if char.ord.zero?
152
-
153
- #color = "\e[#{args*';'}m"
154
- #print "\e7\e[#{rows+1};#{cols+1}H#{color}#{char}\e8"
155
-
@@ -1,4 +1,4 @@
1
1
  module DCPU16
2
- VERSION = "0.0.4.beta"
2
+ VERSION = "0.0.4.0"
3
3
  end
4
4
 
@@ -1,4 +1,4 @@
1
1
  module DCPU16
2
- VERSION = "0.0.4.alpha2"
2
+ VERSION = "0.0.4.0"
3
3
  end
4
4
 
@@ -7,6 +7,8 @@
7
7
  ; * Clock: 100000 (Clock-Speed of CPU [defined])
8
8
  ; * HZ: 99999.02 (Clock-Speed of CPU [real])
9
9
 
10
+ SET C, [0x3000] ; Example
11
+ SET C, 0x2000
10
12
  SET C, 0x30 ; Init OuterLoop
11
13
  :outer_loop SET B, 0 ; Init InnerLoop
12
14
  :inner_loop SET [0x8000+B], C ;draw
@@ -5,10 +5,11 @@ describe DCPU16::Assembler do
5
5
  subject { DCPU16::Assembler.new(asm) }
6
6
 
7
7
  its(:input) { should == asm }
8
- its(:dump) { should be_a_kind_of(Array) }
8
+ its(:lines) { should be_a_kind_of(Array) }
9
9
  specify do
10
- # puts subject.dump
11
- puts subject.dump.first.bytes.length
10
+ # subject.lines
11
+ subject.assemble
12
+ #assert false
12
13
  end
13
14
  end
14
15
 
@@ -39,7 +39,7 @@ describe DCPU16::Screen do
39
39
  specify { subject.should_receive(:update).tap { memory.write(0x8000, 40) } }
40
40
  context "memory outside of screen changed" do
41
41
  specify { observer.should_not_receive(:update).tap { memory.write(0x7FFF, 40) } }
42
- specify { observer.should_not_receive(:update).tap { memory.write(0x8000 + 0x180, 40) } }
42
+ specify { observer.should_not_receive(:update).tap { memory.write(0x8000 + 0x300, 40) } }
43
43
  end
44
44
  context "memory of screen changed" do
45
45
  pending "Disabled Observer right now!"
metadata CHANGED
@@ -1,19 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: Rdcpu16
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4.beta
5
- prerelease: 6
4
+ version: 0.0.4.0
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Patrick Helm
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-11 00:00:00.000000000 Z
12
+ date: 2012-04-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &71628860 !ruby/object:Gem::Requirement
16
+ requirement: &84851380 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,7 +21,7 @@ dependencies:
21
21
  version: '2.6'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *71628860
24
+ version_requirements: *84851380
25
25
  description: This is a simple Ruby port of the fictive 16-bit-cpu DCPU16.
26
26
  email:
27
27
  - deradon87@gmail.com
@@ -35,6 +35,9 @@ files:
35
35
  - lib/dcpu16/assembler.rb
36
36
  - lib/dcpu16/memory.rb
37
37
  - lib/dcpu16/version.rb
38
+ - lib/dcpu16/assembler/line.rb
39
+ - lib/dcpu16/assembler/instruction.rb
40
+ - lib/dcpu16/assembler/constants.rb
38
41
  - lib/dcpu16/memory/word.rb
39
42
  - lib/dcpu16/cpu/operand.rb
40
43
  - lib/dcpu16/cpu/instruction.rb
@@ -79,9 +82,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
79
82
  required_rubygems_version: !ruby/object:Gem::Requirement
80
83
  none: false
81
84
  requirements:
82
- - - ! '>'
85
+ - - ! '>='
83
86
  - !ruby/object:Gem::Version
84
- version: 1.3.1
87
+ version: '0'
85
88
  requirements: []
86
89
  rubyforge_project:
87
90
  rubygems_version: 1.8.10