ucisc 0.1.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.
@@ -0,0 +1,48 @@
1
+ module MicroCisc
2
+ module Compile
3
+ class LabelGenerator
4
+ def initialize
5
+ @labels = []
6
+ @count = 0
7
+ end
8
+
9
+ def push_context
10
+ @count += 1
11
+ @labels << "{#{@count}"
12
+ end
13
+
14
+ def pop_context
15
+ @labels << last_open.sub('{', '}')
16
+ end
17
+
18
+ def end_label
19
+ last_open.sub('{', '}')
20
+ end
21
+
22
+ def start_label
23
+ last_open
24
+ end
25
+
26
+ def last_open
27
+ if @labels.empty?
28
+ raise ArgumentException, "No open label context"
29
+ end
30
+ # Go backwards until we find an open that we didn't see the close for first
31
+ i = @labels.size - 1
32
+ closed = nil
33
+ while(i >= 0)
34
+ if @labels[i].start_with?('}')
35
+ closed = @labels[i]
36
+ elsif !closed
37
+ return @labels[i]
38
+ elsif closed && @labels[i].end_with?(closed[1..-1])
39
+ closed = nil
40
+ else
41
+ raise 'Invalid state, contexts are out of order'
42
+ end
43
+ i -= 1
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,113 @@
1
+ module MicroCisc
2
+ module Compile
3
+ class Statement
4
+ SUGAR_REGEX = /(?<name>(\$|&)[^\s\[\]]+)\s+(?<op>as|=)\s+(?<param>.+)/
5
+ FUNCTION_REGEX = /(?<stack>[^\s\[\]]+)\s*(\[(?<words>[0-9]+)\]){0,1}\s+<=\s+(?<label>[a-zA-Z_][a-zA-Z0-9_\-@$!%]*)\s*\(\s*(?<args>[^)]*)/
6
+ IMM_REGEX = / (0x){0,1}(?<imm_val>[0-9A-Fa-f])\.imm/
7
+ attr_reader :original, :minimal
8
+
9
+ def initialize(label_generator, statement, sugar)
10
+ @label_generator = label_generator
11
+ @original = statement
12
+ @minimal = filter_comments(statement)
13
+ @sugar = sugar
14
+ end
15
+
16
+ def filter_comments(instruction)
17
+ # Remove all inline comments
18
+ instruction = instruction.to_s.strip.gsub(/\/[^\/]*\//, '')
19
+ # Remove all word comments
20
+ instruction = instruction.gsub(/'[^\s]*/, '')
21
+ # Remove all line comments
22
+ instruction = instruction.gsub(/#.*/, '')
23
+ # Single space
24
+ instruction.gsub(/\s+/, ' ')
25
+ end
26
+
27
+ def create_variable(name, arg)
28
+ if arg.end_with?("mem") || arg.end_with?("reg")
29
+ name = name[1..-1]
30
+ arg = arg[0..-4]
31
+
32
+ @sugar["$#{name}"] = "#{arg}mem"
33
+ @sugar["&#{name}"] = "#{arg}reg"
34
+ else
35
+ @sugar[name] = arg
36
+ end
37
+ end
38
+
39
+ def parse
40
+ if FUNCTION_REGEX =~ @minimal
41
+ parse_function_call
42
+ elsif SUGAR_REGEX =~ @minimal
43
+ match = SUGAR_REGEX.match(@minimal)
44
+ name = match['name']
45
+ if match['op'] == 'as'
46
+ create_variable(name, match['param'])
47
+ []
48
+ elsif match['op'] == '='
49
+ @minimal = match['param']
50
+ instruction = Instruction.new(@label_generator, minimal, original, @sugar)
51
+ dest = instruction.dest
52
+ if [1, 2, 3].include?(dest)
53
+ create_variable(name, "#{dest}.mem")
54
+ else
55
+ dest -= 4 if dest > 4
56
+ create_variable(name, "#{dest}.reg")
57
+ end
58
+ [instruction]
59
+ else
60
+ raise ArgumentError, "Invalid syntax declaration: #{@minimal}"
61
+ end
62
+ else
63
+ [Instruction.new(@label_generator, @minimal, original, @sugar)]
64
+ end
65
+ end
66
+
67
+ def parse_function_call
68
+ match = FUNCTION_REGEX.match(@minimal)
69
+ label = match['label']
70
+
71
+ stack = match['stack']
72
+ stack = @sugar[stack] if @sugar[stack]
73
+ raise ArgumentError, "Invalid stack param, mem register expected: #{stack}" unless stack =~ /^[1-3]\.mem$/
74
+ stackp = stack.sub('mem', 'reg')
75
+
76
+ return_words = match['words'].to_i
77
+ args = match['args'].split(',').map(&:strip)
78
+
79
+ instructions = []
80
+ if return_words > 0
81
+ instruction = "copy #{stackp} -#{return_words}.imm #{stackp}"
82
+ instructions << Instruction.new(@label_generator, instruction, " #{instruction} # return vars - #{original}", @sugar)
83
+ end
84
+
85
+ instruction = "copy 0.reg #{args.size + 2}.imm #{stack} push"
86
+ instructions << Instruction.new(@label_generator, instruction, " #{instruction} # return addr - #{original}", @sugar)
87
+
88
+ stack_delta = 1 + return_words
89
+ args = args.each do |arg|
90
+ arg = arg.split(' ').map { |a| @sugar[a] || a }.join(' ')
91
+ is_stack = arg.start_with?(stack)
92
+ if is_stack
93
+ offset = stack_delta
94
+ if arg_imm = IMM_REGEX.match(arg)
95
+ arg_imm = arg_imm['imm_val'].to_i(16)
96
+ arg = arg.sub(IMM_REGEX, '')
97
+ else
98
+ arg_imm = 0
99
+ end
100
+ offset_immediate = (offset + arg_imm) > 0 ? " #{(offset + arg_imm)}.imm" : ''
101
+ arg = "#{arg}#{offset_immediate}"
102
+ end
103
+ instruction = "copy #{arg} #{stack} push"
104
+ stack_delta += 1
105
+ instructions << Instruction.new(@label_generator, instruction, " #{instruction} # push arg - #{original}", @sugar)
106
+ end
107
+ instruction = "copy 0.reg #{label}.disp 0.reg"
108
+ instructions << Instruction.new(@label_generator, instruction, " #{instruction} # call - #{original}", @sugar)
109
+ instructions
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,3 @@
1
+ module MicroCisc
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,140 @@
1
+ module MicroCisc
2
+ module Vm
3
+ # This is a generic device base class providing memory and control access
4
+ #
5
+ # From the docs, the control word layout is as follows:
6
+ #
7
+ # * 0x0 - Device ID, read only. Unique system wide.
8
+ # * 0x1 - Bank address (MSB) | Device type (LSB)
9
+ # * 0x2 - Bus access device ID
10
+ # * 0x3 - Interrupt code (MSB) | Device status (LSB)
11
+ # * 0x4 - Local register with interrupt handler address (read/write)
12
+ # * 0x5 to 0xF - Device type specific
13
+ class Device
14
+ attr_reader :id
15
+
16
+ def initialize(id, type, local_blocks, rom_blocks = [])
17
+ @id = id
18
+ @external_read = 0x001F
19
+ @privileged_read = 0x001F
20
+ @privileged_write = 0x0010
21
+ @internal_write = 0x000C
22
+
23
+ @local_blocks = local_blocks
24
+ rom_blocks.each { |block| block.freeze }
25
+ ram_blocks = Array.new(local_blocks - rom_blocks.size).map do
26
+ Array.new(256).map { 0 }
27
+ end
28
+ @local_mem = rom_blocks + ram_blocks
29
+
30
+ @control = 0
31
+ @control_mem = Array.new(16).map { 0 }
32
+ @control_mem[0] = id
33
+ @control_mem[1] = type & 0xFF
34
+
35
+ @devices = [self]
36
+ end
37
+
38
+ def bank_index=(index)
39
+ @control_mem[1] = ((index & 0xFF) << 8) | (@control_mem[1] & 0xFF)
40
+ end
41
+
42
+ def devices=(devices)
43
+ @devices = [self] + devices
44
+ @devices.each_with_index { |device, index| device.bank_index = index }
45
+ end
46
+
47
+ def source_halted(source_device_id)
48
+ @control_mem[2] = 0 if source_device_id == @control_mem[2]
49
+ end
50
+
51
+ def write_control(source_device_id, address, value)
52
+ address = address & 0xF
53
+ return if address == 0
54
+ if source_device_id == @id
55
+ return if (1 << (address - 1)) & @internal_write == 0
56
+ @control_mem[address] = value
57
+ elsif source_device_id == @control_mem[2]
58
+ return if (1 << (address - 1)) & @privileged_write == 0
59
+ @control_mem[address] = value
60
+ handle_control_update(address, value)
61
+ end
62
+ end
63
+
64
+ def read_control(source_device_id, address)
65
+ return @control_mem[0] if address == 0
66
+ if source_device_id == @id || source_device_id == @control_mem[2]
67
+ return 0 if (1 << (address - 1)) & @privileged_read == 0
68
+ handle_control_read(address)
69
+ @control_mem[address]
70
+ else
71
+ return 0 if (1 << (address - 1)) & @external_read == 0
72
+ handle_control_read(address)
73
+ @control_mem[address]
74
+ end
75
+ end
76
+
77
+ def handle_control_read(address)
78
+ # Does nothing by default, override in subclass
79
+ end
80
+
81
+ def handle_control_update(address, value)
82
+ # Does nothing by default, override in subclass
83
+ end
84
+
85
+ def banked?(address)
86
+ banked = ((address & 0xF000) >> 12)
87
+ if banked == 0
88
+ banked = 1
89
+ else
90
+ banked = 1 << banked
91
+ end
92
+ (banked & @control) != 0
93
+ end
94
+
95
+ def write_mem(source_device_id, address, value)
96
+ banked = banked?(address)
97
+ device = (address >> 4)
98
+ if banked && source_device_id == @id && device < @devices.size
99
+ @devices[device].write_control(source_device_id, address & 0xF, value)
100
+ elsif banked && source_device_id == @id && device >= 256
101
+ device = (address >> 8)
102
+ if device < @devices.size
103
+ @devices[device].write_mem(source_device_id, address & 0xFF, value)
104
+ else
105
+ page = (address & 0xFF00) >> 8
106
+ @local_mem[page][address & 0xFF] = value
107
+ end
108
+ elsif !banked && source_device_id == @id
109
+ page = (address & 0xFF00) >> 8
110
+ @local_mem[page][address & 0xFF] = value
111
+ elsif source_device_id == @control_mem[2]
112
+ page = (@control_mem[3] & 0xFF00) >> 8
113
+ @local_mem[page][address & 0xFF] = value
114
+ end
115
+ end
116
+
117
+ def read_mem(source_device_id, address, force_local = false)
118
+ banked = banked?(address) && !force_local
119
+ device = (address >> 4)
120
+ if banked && source_device_id == @id && device < @devices.size
121
+ @devices[device].read_control(source_device_id, address & 0xF)
122
+ elsif banked && source_device_id == @id && device >= 256
123
+ device = (address >> 8)
124
+ if device < @devices.size
125
+ @devices[device].read_mem(source_device_id, address & 0xFF)
126
+ else
127
+ page = (address & 0xFF00) >> 8
128
+ @local_mem[page][address & 0xFF]
129
+ end
130
+ elsif !banked && source_device_id == @id
131
+ page = (address & 0xFF00) >> 8
132
+ @local_mem[page][address & 0xFF]
133
+ elsif source_device_id == @control_mem[2]
134
+ page = @control_mem[3] & 0xFF00
135
+ @local_mem[page][address * 0xFF]
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,9 @@
1
+ module MicroCisc
2
+ module Vm
3
+ class EmptyDevice < Device
4
+ def initialize
5
+ super(0, 0, 0)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,447 @@
1
+ module MicroCisc
2
+ module Vm
3
+ # Many of the coding decisions made in here are for performance reasons:
4
+ #
5
+ # 1. Mostly one big class, avoiding object creation and referencing
6
+ # 2. More verbose programming style in some cases
7
+ # 3. Optimized/strange bitwise math for performance/avoiding ruby magic
8
+ # 4. Some strange branching structures to shortcut common paths
9
+ # 5. Few accessor methods, prefer instance vars
10
+ # 6. Little runtime error checking
11
+ #
12
+ # Performance isn't a deal breaker, but 1+ MIPS, roughly speaking, gives me a few
13
+ # times the MIPS of an original 6502 which puts me in the ball park of where I
14
+ # need to be to do some real programming. We just want something reasonable for
15
+ # debuging and doing some real coding, actual hardware will leave this in the
16
+ # dust most likely.
17
+ class Processor < Device
18
+ OP_MASK = 0x8000
19
+ EFFECT_MASK = 0x6000
20
+ DESTINATION_MASK = 0x1C00
21
+ SOURCE_MASK = 0x0380
22
+ ALU_OP_MASK = 0x000F
23
+ IMMEDIATE_MASK = 0x0030
24
+ INCREMENT_MASK = 0x0040
25
+ IMMEDIATE_SIGN_MASK = 0x0020
26
+
27
+ OP_SHIFT = 15
28
+ EFFECT_SHIFT = 13
29
+ DESTINATION_SHIFT = 10
30
+ SOURCE_SHIFT = 7
31
+
32
+ NEGATIVE_MASK = 0x0004
33
+ ZERO_MASK = 0x0002
34
+ OVERFLOW_MASK = 0x0001
35
+
36
+ SIGNED_MODE_FLAG = 0x0100
37
+ HALT_MODE_FLAG = 0x0200
38
+
39
+ SIGN_BIT = 0x8000
40
+ PAGE_MASK = 0xFFC0
41
+
42
+ MEM_ARGS = [1, 2, 3]
43
+
44
+ attr_accessor :flags, :overflow, :debug
45
+ attr_reader :pc, :control, :count
46
+
47
+ def initialize(id, local_blocks, rom_blocks = [])
48
+ super(id, 1, local_blocks, rom_blocks)
49
+
50
+ @registers = [0, 0, 0, 0]
51
+ @pc = 0
52
+ @flags = 0
53
+ @overflow = 0
54
+ @debug = false
55
+ @run = false
56
+ @pc_modified = false
57
+ @count = 0
58
+ end
59
+
60
+ def handle_control_update(address, value)
61
+ if address == 0x7
62
+ self.pc = value
63
+ elsif address == 0x8
64
+ set_register(1, value)
65
+ elsif address == 0x9
66
+ set_register(2, value)
67
+ elsif address == 0xA
68
+ set_register(3, value)
69
+ elsif address == 0xB
70
+ @flags = value & 0xFFFF
71
+ elsif address == 0xC
72
+ self.control = value
73
+ end
74
+ end
75
+
76
+ def control=(value)
77
+ @control = value & 0xFFFF
78
+ end
79
+
80
+ def pc=(value)
81
+ @pc_modified = true
82
+ @pc = value & 0xFFFF
83
+ end
84
+
85
+ def register(id)
86
+ @registers[id]
87
+ end
88
+
89
+ def set_register(id, value)
90
+ @registers[id] = value
91
+ end
92
+
93
+ def extract_immediates(word, is_copy, inc_is_immediate, signed, half_width)
94
+ if is_copy
95
+ immediate_mask = IMMEDIATE_MASK | ALU_OP_MASK
96
+ immediate_shift = 0
97
+ else
98
+ immediate_mask = IMMEDIATE_MASK
99
+ immediate_shift = 4
100
+ end
101
+
102
+ sign_mask = IMMEDIATE_SIGN_MASK
103
+ if inc_is_immediate
104
+ sign_mask = INCREMENT_MASK
105
+ immediate_mask = immediate_mask | INCREMENT_MASK
106
+ end
107
+
108
+ if signed && ((word & sign_mask) != 0)
109
+ # Fancy bit inverse for high performance sign extend
110
+ [~(~(word & immediate_mask) & immediate_mask) >> immediate_shift]
111
+ elsif half_width
112
+ [
113
+ (word & immediate_mask) >> (immediate_shift + 3),
114
+ (word & (immediate_mask >> 3)) >> immediate_shift
115
+ ]
116
+ else
117
+ [(word & immediate_mask) >> immediate_shift]
118
+ end
119
+ end
120
+
121
+ # * 0 = zero?
122
+ # * 1 = not zero?
123
+ # * 2 = negative?
124
+ # * 3 = store
125
+ def store?(flags, effect)
126
+ return true if effect == 3 # handle the common case quickly
127
+
128
+ zero = flags & ZERO_MASK != 0
129
+ (effect == 0 && zero) ||
130
+ (effect == 1 && !zero) ||
131
+ (effect == 2 && (flags & NEGATIVE_MASK != 0))
132
+ end
133
+
134
+ def source_value(source, immediate)
135
+ case source
136
+ when 0
137
+ @pc + immediate
138
+ when 1,2,3
139
+ read_mem(@id, @registers[source] + immediate)
140
+ when 4
141
+ immediate
142
+ else
143
+ @registers[source - 4] + immediate
144
+ end
145
+ end
146
+
147
+ def destination_value(destination, immediate)
148
+ case destination
149
+ when 0
150
+ @pc
151
+ when 1,2,3
152
+ read_mem(@id, @registers[destination] + immediate)
153
+ when 4
154
+ @control
155
+ else
156
+ @registers[destination - 4]
157
+ end
158
+ end
159
+
160
+ def exec_instruction(word)
161
+ source = (word & SOURCE_MASK) >> SOURCE_SHIFT
162
+ destination = (word & DESTINATION_MASK) >> DESTINATION_SHIFT
163
+ effect = (word & EFFECT_MASK) >> EFFECT_SHIFT
164
+
165
+ signed = !MEM_ARGS.include?(source)
166
+ inc_is_immediate = signed && !MEM_ARGS.include?(destination)
167
+ is_copy = (word & OP_MASK) == 0
168
+ half_width = is_copy && MEM_ARGS.include?(source) && MEM_ARGS.include?(destination)
169
+ immediates = extract_immediates(word, is_copy, inc_is_immediate, signed, half_width)
170
+
171
+ alu = word & ALU_OP_MASK
172
+ result = compute_result(is_copy, source, destination, immediates, alu, true)
173
+
174
+ return false unless store?(@flags, effect)
175
+
176
+ push = !inc_is_immediate && (word & INCREMENT_MASK) > 0
177
+ store_result(result, source, destination, immediates, push, 0)
178
+
179
+ # Detect halt instruction
180
+ return 1 if immediates.first == 0 && source == 0 && destination == 0
181
+ 0
182
+ end
183
+
184
+ def compute_result(is_copy, source, destination, immediates, alu, update_flags)
185
+ source_value = source_value(source, immediates.first)
186
+ if is_copy
187
+ source_value
188
+ else
189
+ dest_immediate = immediates.size > 1 ? immediates.last : 0
190
+ destination_value = destination_value(destination, dest_immediate)
191
+ compute(alu, source_value, destination_value, update_flags)
192
+ end
193
+ end
194
+
195
+ def store_result(value, source, destination, immediates, push, sign)
196
+ case destination
197
+ when 0
198
+ @pc = value
199
+ @pc_modified = true
200
+ when 1,2,3
201
+ if push
202
+ @registers[destination] = (@registers[destination] - 1) & 0xFFFF
203
+ end
204
+ imm = immediates.size > 1 ? immediates.last : 0
205
+ address = @registers[destination] + imm
206
+ write_mem(@id, address, value)
207
+ when 4
208
+ self.control = value
209
+ else
210
+ @registers[destination - 4] = value
211
+ end
212
+ if push && !MEM_ARGS.include?(destination) && MEM_ARGS.include?(source)
213
+ @registers[source] = (@registers[source] + 1) & 0xFFFF
214
+ end
215
+ end
216
+
217
+ def compute(alu_code, arg1, arg2, update_flags)
218
+ overflow = 0
219
+ overflow_reg = 0
220
+
221
+ case alu_code
222
+ when 0x00
223
+ # INV
224
+ value = (arg1 & 0xFFFF) ^ 0xFFFF
225
+ when 0x01
226
+ # AND
227
+ value = arg2 & arg1
228
+ when 0x02
229
+ # OR
230
+ value = arg2 | arg1
231
+ when 0x03
232
+ # XOR
233
+ value = arg2 ^ arg1
234
+ when 0x04
235
+ # Negate (2's compliment)
236
+ value = -1 * arg1
237
+ when 0x05
238
+ # Shift left, zero extend
239
+ value = arg2 << arg1
240
+ overflow_reg = (value & 0xFFFF0000) >> 16
241
+ value = value & 0xFFFF
242
+ when 0x06
243
+ # Shift right, repsect signed mode
244
+ overflow_mask = ~(-1 << arg1) & 0xFFFF
245
+ overflow_reg = arg2 & overflow_mask
246
+ overflow_reg >> (arg1 - 16) if arg1 > 16
247
+
248
+ if @control & SIGNED_MODE_FLAG == 0
249
+ value = (arg2 & 0xFFFF) >> arg1
250
+ else
251
+ value = arg2 >> arg1
252
+ end
253
+ when 0x07
254
+ # Swap MSB and LSB bytes
255
+ value = ((arg1 & 0xFF00) >> 8) | ((arg1 & 0x00FF) << 8)
256
+ when 0x08
257
+ # Zero LSB
258
+ value = arg1 & 0xFF00
259
+ when 0x09
260
+ # Zero MSB
261
+ value = arg1 & 0x00FF
262
+ when 0x0A,0x0B
263
+ arg1 = (arg1 ^ 0xFFFF) + 1 if alu_code == 0x0B
264
+ value = (arg1 & 0xFFFF) + (arg2 & 0xFFFF)
265
+ # Add, respect signed mode
266
+ if @control & SIGNED_MODE_FLAG == 0
267
+ if value > 0xFFFF
268
+ overflow = 1
269
+ overflow_reg = 1
270
+ end
271
+ else
272
+ if ((arg1 & SIGN_BIT) == (arg2 & SIGN_BIT)) &&
273
+ ((arg1 & SIGN_BIT) != (value & SIGN_BIT))
274
+ overflow = 1
275
+ overflow_reg = (value & 0xFFFF0000 >> 16) & 0xFFFF
276
+ end
277
+ end
278
+ value = value & 0xFFFF
279
+ when 0x0C
280
+ if @control & SIGNED_MODE_FLAG == 0
281
+ arg1 = ~(~arg1 & 0xFFFF) if arg1 & SIGN_BIT != 0
282
+ arg2 = ~(~arg2 & 0xFFFF) if arg2 & SIGN_BIT != 0
283
+ end
284
+ value = arg1 * arg2
285
+ overflow_reg = (value & 0xFFFF0000 >> 16) & 0xFFFF
286
+ if ((overflow_reg & SIGN_BIT) == (value & SIGN_BIT)) &&
287
+ overflow_reg == 0xFFFF
288
+ # There was no actual overflow, it's just the sign extension
289
+ overflow = 0
290
+ else
291
+ overflow = 1
292
+ end
293
+ value = value & 0xFFFF
294
+ when 0x0D
295
+ if @control & SIGNED_MODE_FLAG == 0
296
+ arg1 = ~(~arg1 & 0xFFFF) if arg1 & SIGN_BIT != 0
297
+ arg2 = ~(~arg2 & 0xFFFF) if arg2 & SIGN_BIT != 0
298
+ end
299
+ value = arg2 / arg1
300
+ overflow_reg = arg2 % arg1
301
+ value = value & 0xFFFF
302
+ when 0x0E
303
+ value = arg1 & PAGE_MASK
304
+ when 0x0F
305
+ value = arg1 + @overflow
306
+ else
307
+ raise ArgumentError, "Unsupported ALU code #{alu_code.to_s(16).upcase}"
308
+ end
309
+
310
+ zero = value == 0 ? 1 : 0
311
+ negative = (value & 0x8000) == 0 ? 0 : 1
312
+
313
+ if update_flags
314
+ flags =
315
+ (overflow * OVERFLOW_MASK) |
316
+ (zero * ZERO_MASK) |
317
+ (negative * NEGATIVE_MASK)
318
+ @flags = flags
319
+ @overflow = overflow_reg
320
+ end
321
+ value & 0xFFFF
322
+ end
323
+
324
+ def start(debug = false)
325
+ @debug = debug
326
+ @run = true
327
+ run
328
+ end
329
+
330
+ def run
331
+ @t0 = Time.now
332
+ @count = 0
333
+ while(@run) do
334
+ word = read_mem(@id, @pc, true)
335
+ if @debug
336
+ # Pause before executing next command
337
+ do_command("#{'%04x' % [@pc]} #{ucisc(word)} ")
338
+ end
339
+ special = exec_instruction(word)
340
+ if special != 0
341
+ halt if special == 1
342
+ @debug = true if special == 2
343
+ end
344
+ if @pc_modified
345
+ @pc_modified = false
346
+ else
347
+ @pc += 1
348
+ end
349
+ @count += 1
350
+ # if count & 0xFF == 0
351
+ # read_from_processor
352
+ # end
353
+ end
354
+ end
355
+
356
+ def do_command(prefix = '')
357
+ return unless @debug
358
+ $stdout.print "#{prefix}> "
359
+ command = $stdin.readline
360
+ exit(1) if /exit/.match(command)
361
+ @debug = true if /debug|n|next/.match(command)
362
+ @debug = false if /c|continue/.match(command)
363
+ MicroCisc.logger.info(stack_string) if /stack/.match(command)
364
+ byebug if /break/.match(command)
365
+ end
366
+
367
+ def format_data(data)
368
+ data.map { |w| '%04X' % w }.join(' ').gsub(/(([0-9A-Za-z]{4} ){16})/, "\\1\n")
369
+ end
370
+
371
+ def halt
372
+ delta = (Time.now - @t0)
373
+ MicroCisc.logger.info("HALT: #{@count} instructions in #{delta}s")
374
+ MicroCisc.logger.info("Stack: " + stack_string)
375
+
376
+ @run = false
377
+ end
378
+
379
+ def stack_string
380
+ address = @registers[1] & 0xFFFF
381
+ str = "#{'%04X' % address} => "
382
+ while(address > 0xFF00 && address < 0x10000 && address - @registers[1] < 10)
383
+ str += "0x#{'%04X' % read_mem(@id, address)} "
384
+ address += 1
385
+ end
386
+ str
387
+ end
388
+
389
+ def ucisc(word)
390
+ source = (word & SOURCE_MASK) >> SOURCE_SHIFT
391
+ destination = (word & DESTINATION_MASK) >> DESTINATION_SHIFT
392
+ effect = (word & EFFECT_MASK) >> EFFECT_SHIFT
393
+
394
+ src =
395
+ if source == 0
396
+ '0.reg'
397
+ elsif source < 4
398
+ "#{source}.mem"
399
+ elsif source == 4
400
+ '4.val'
401
+ else
402
+ "#{source - 4}.reg"
403
+ end
404
+
405
+ dest =
406
+ if destination == 0
407
+ '0.reg'
408
+ elsif destination < 4
409
+ "#{destination}.mem"
410
+ elsif destination == 4
411
+ '4.reg'
412
+ else
413
+ "#{destination - 4}.reg"
414
+ end
415
+
416
+ # Eh?
417
+ signed = !MEM_ARGS.include?(source)
418
+ inc_is_immediate = signed && !MEM_ARGS.include?(destination)
419
+ is_copy = (word & OP_MASK) == 0
420
+ half_width = is_copy && MEM_ARGS.include?(source) && MEM_ARGS.include?(destination)
421
+ immediates = extract_immediates(word, is_copy, inc_is_immediate, signed, half_width)
422
+
423
+ value = source_value(source, immediates.first)
424
+ store = store?(@flags, effect)
425
+
426
+ alu = word & ALU_OP_MASK
427
+ result = compute_result(is_copy, source, destination, immediates, alu, true)
428
+ alu = is_copy ? '' : "0x#{alu.to_s(16).upcase}.op "
429
+
430
+ imm0 = immediates.first < 0 ? "-#{(immediates.first * -1)}.imm" : "#{immediates.first}.imm"
431
+ imm1 =
432
+ if half_width
433
+ immediates.last < 0 ? "-#{(immediates.last * -1)}.imm " : "#{immediates.last}.imm "
434
+ else
435
+ ""
436
+ end
437
+ eff = "#{effect}.eff"
438
+ push = !inc_is_immediate && (word & INCREMENT_MASK) > 0
439
+ push = push ? 'push ' : ''
440
+ ins = is_copy ? 'copy' : 'compute'
441
+
442
+
443
+ "Stack: #{stack_string}\n#{ins} #{alu}#{src} #{imm0} #{dest} #{imm1}#{eff} #{push}# value: #{value} (0x#{'%04x' % value}), result: #{result} (0x#{'%04x' % result}), #{'not ' if !store}stored"
444
+ end
445
+ end
446
+ end
447
+ end