Rdcpu16 0.0.4.beta → 0.0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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