Class: MicroCisc::Vm::Processor
- Inherits:
-
Device
- Object
- Device
- MicroCisc::Vm::Processor
show all
- Defined in:
- lib/micro_cisc/vm/processor.rb
Overview
Many of the coding decisions made in here are for performance reasons:
- Mostly one big class, avoiding object creation and referencing
- More verbose programming style in some cases
- Optimized/strange bitwise math for performance/avoiding ruby magic
- Some strange branching structures to shortcut common paths
- Few accessor methods, prefer instance vars
- Little runtime error checking
Performance isn't a deal breaker, but 1+ MIPS, roughly speaking, gives me a few
times the MIPS of an original 6502 which puts me in the ball park of where I
need to be to do some real programming. We just want something reasonable for
debuging and doing some real coding, actual hardware will leave this in the
dust most likely.
Constant Summary
collapse
- OP_MASK =
0x8000
- EFFECT_MASK =
0x6000
- DESTINATION_MASK =
0x1C00
- SOURCE_MASK =
0x0380
- ALU_OP_MASK =
0x000F
- IMMEDIATE_MASK =
0x0030
- INCREMENT_MASK =
0x0040
- IMMEDIATE_SIGN_MASK =
0x0020
- OP_SHIFT =
15
- EFFECT_SHIFT =
13
- DESTINATION_SHIFT =
10
- SOURCE_SHIFT =
7
- NEGATIVE_MASK =
0x0004
- ZERO_MASK =
0x0002
- OVERFLOW_MASK =
0x0001
- SIGNED_MODE_FLAG =
0x0100
- HALT_MODE_FLAG =
0x0200
- SIGN_BIT =
0x8000
- PAGE_MASK =
0xFFC0
- MEM_ARGS =
[1, 2, 3]
Constants inherited
from Device
Device::TYPE_BLOCK_IO, Device::TYPE_BLOCK_MEMORY, Device::TYPE_HID, Device::TYPE_INVALID, Device::TYPE_PROCESSOR, Device::TYPE_SERIAL, Device::TYPE_TERMINAL
Instance Attribute Summary collapse
Attributes inherited from Device
#id
Instance Method Summary
collapse
-
#compute(alu_code, arg1, arg2, update_flags) ⇒ Object
-
#compute_result(is_copy, source, destination, immediates, alu, update_flags) ⇒ Object
-
#destination_value(destination, immediate) ⇒ Object
-
#do_command(prefix = '') ⇒ Object
-
#exec_instruction(word) ⇒ Object
-
#extract_immediates(word, is_copy, inc_is_immediate, signed, half_width) ⇒ Object
-
#format_data(data) ⇒ Object
-
#halt ⇒ Object
-
#handle_control_update(address, value) ⇒ Object
-
#initialize(id, local_blocks, rom_blocks = []) ⇒ Processor
constructor
A new instance of Processor.
-
#register(id) ⇒ Object
-
#run ⇒ Object
-
#set_register(id, value) ⇒ Object
-
#source_value(source, immediate) ⇒ Object
-
#stack_string ⇒ Object
-
#start(debug = false) ⇒ Object
-
#store?(flags, effect) ⇒ Boolean
- 0 = zero? * 1 = not zero? * 2 = negative? * 3 = store.
-
#store_result(value, source, destination, immediates, push, sign) ⇒ Object
-
#ucisc(word) ⇒ Object
Methods inherited from Device
#bank_index=, #banked?, #devices=, #handle_control_read, #read_control, #read_mem, #write_control, #write_mem
Constructor Details
#initialize(id, local_blocks, rom_blocks = []) ⇒ Processor
Returns a new instance of Processor.
47
48
49
50
51
52
53
54
55
56
57
58
59
|
# File 'lib/micro_cisc/vm/processor.rb', line 47
def initialize(id, local_blocks, rom_blocks = [])
super(id, 1, local_blocks, rom_blocks)
@registers = [0, 0, 0, 0]
@pc = 0
@flags = 0
@overflow = 0
@debug = false
@run = false
@pc_modified = false
@count = 0
@clocks_per_second = 200_000
end
|
Instance Attribute Details
#control ⇒ Object
Returns the value of attribute control.
45
46
47
|
# File 'lib/micro_cisc/vm/processor.rb', line 45
def control
@control
end
|
#count ⇒ Object
Returns the value of attribute count.
45
46
47
|
# File 'lib/micro_cisc/vm/processor.rb', line 45
def count
@count
end
|
#debug ⇒ Object
Returns the value of attribute debug.
44
45
46
|
# File 'lib/micro_cisc/vm/processor.rb', line 44
def debug
@debug
end
|
#flags ⇒ Object
Returns the value of attribute flags.
44
45
46
|
# File 'lib/micro_cisc/vm/processor.rb', line 44
def flags
@flags
end
|
#overflow ⇒ Object
Returns the value of attribute overflow.
44
45
46
|
# File 'lib/micro_cisc/vm/processor.rb', line 44
def overflow
@overflow
end
|
#pc ⇒ Object
Returns the value of attribute pc.
45
46
47
|
# File 'lib/micro_cisc/vm/processor.rb', line 45
def pc
@pc
end
|
Instance Method Details
#compute(alu_code, arg1, arg2, update_flags) ⇒ Object
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
|
# File 'lib/micro_cisc/vm/processor.rb', line 225
def compute(alu_code, arg1, arg2, update_flags)
overflow = 0
overflow_reg = 0
case alu_code
when 0x00
value = (arg1 & 0xFFFF) ^ 0xFFFF
when 0x01
value = arg2 & arg1
when 0x02
value = arg2 | arg1
when 0x03
value = arg2 ^ arg1
when 0x04
value = -1 * arg1
when 0x05
value = arg2 << arg1
overflow_reg = (value & 0xFFFF0000) >> 16
value = value & 0xFFFF
when 0x06
overflow_mask = ~(-1 << arg1) & 0xFFFF
overflow_reg = arg2 & overflow_mask
overflow_reg >> (arg1 - 16) if arg1 > 16
if @control & SIGNED_MODE_FLAG == 0
value = (arg2 & 0xFFFF) >> arg1
else
value = arg2 >> arg1
end
when 0x07
value = ((arg1 & 0xFF00) >> 8) | ((arg1 & 0x00FF) << 8)
when 0x08
value = arg1 & 0xFF00
when 0x09
value = arg1 & 0x00FF
when 0x0A,0x0B
arg1 = (arg1 ^ 0xFFFF) + 1 if alu_code == 0x0B
value = (arg1 & 0xFFFF) + (arg2 & 0xFFFF)
if @control & SIGNED_MODE_FLAG == 0
if value > 0xFFFF
overflow = 1
overflow_reg = 1
end
else
if ((arg1 & SIGN_BIT) == (arg2 & SIGN_BIT)) &&
((arg1 & SIGN_BIT) != (value & SIGN_BIT))
overflow = 1
overflow_reg = (value & 0xFFFF0000 >> 16) & 0xFFFF
end
end
value = value & 0xFFFF
when 0x0C
if @control & SIGNED_MODE_FLAG == 0
arg1 = ~(~arg1 & 0xFFFF) if arg1 & SIGN_BIT != 0
arg2 = ~(~arg2 & 0xFFFF) if arg2 & SIGN_BIT != 0
end
value = arg1 * arg2
overflow_reg = (value & 0xFFFF0000 >> 16) & 0xFFFF
if ((overflow_reg & SIGN_BIT) == (value & SIGN_BIT)) &&
overflow_reg == 0xFFFF
overflow = 0
else
overflow = 1
end
value = value & 0xFFFF
when 0x0D
if @control & SIGNED_MODE_FLAG == 0
arg1 = ~(~arg1 & 0xFFFF) if arg1 & SIGN_BIT != 0
arg2 = ~(~arg2 & 0xFFFF) if arg2 & SIGN_BIT != 0
end
value = arg2 / arg1
overflow_reg = arg2 % arg1
value = value & 0xFFFF
when 0x0E
value = arg1 & PAGE_MASK
when 0x0F
value = arg1 + @overflow
else
raise ArgumentError, "Unsupported ALU code #{alu_code.to_s(16).upcase}"
end
zero = value == 0 ? 1 : 0
negative = (value & 0x8000) == 0 ? 0 : 1
if update_flags
flags =
(overflow * OVERFLOW_MASK) |
(zero * ZERO_MASK) |
(negative * NEGATIVE_MASK)
@flags = flags
@overflow = overflow_reg
end
value & 0xFFFF
end
|
#compute_result(is_copy, source, destination, immediates, alu, update_flags) ⇒ Object
192
193
194
195
196
197
198
199
200
201
|
# File 'lib/micro_cisc/vm/processor.rb', line 192
def compute_result(is_copy, source, destination, immediates, alu, update_flags)
source_value = source_value(source, immediates.first)
if is_copy
source_value
else
dest_immediate = immediates.size > 1 ? immediates.last : 0
destination_value = destination_value(destination, dest_immediate)
compute(alu, source_value, destination_value, update_flags)
end
end
|
#destination_value(destination, immediate) ⇒ Object
148
149
150
151
152
153
154
155
156
157
158
159
|
# File 'lib/micro_cisc/vm/processor.rb', line 148
def destination_value(destination, immediate)
case destination
when 0
@pc
when 1,2,3
read_mem(@id, (@registers[destination] + immediate) & 0xFFFF)
when 4
@control
else
@registers[destination - 4]
end
end
|
#do_command(prefix = '') ⇒ Object
367
368
369
370
371
372
373
374
375
376
|
# File 'lib/micro_cisc/vm/processor.rb', line 367
def do_command(prefix = '')
return unless @debug
$stdout.print "#{prefix}> "
command = $stdin.readline
exit(1) if /exit/.match(command)
@debug = true if /debug|n|next/.match(command)
@debug = false if /c|continue/.match(command)
MicroCisc.logger.info(stack_string) if /stack/.match(command)
byebug if /break/.match(command)
end
|
#exec_instruction(word) ⇒ Object
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
|
# File 'lib/micro_cisc/vm/processor.rb', line 161
def exec_instruction(word)
source = (word & SOURCE_MASK) >> SOURCE_SHIFT
destination = (word & DESTINATION_MASK) >> DESTINATION_SHIFT
effect = (word & EFFECT_MASK) >> EFFECT_SHIFT
signed = !MEM_ARGS.include?(source)
inc_is_immediate = signed && !MEM_ARGS.include?(destination)
is_copy = (word & OP_MASK) == 0
half_width = is_copy && MEM_ARGS.include?(source) && MEM_ARGS.include?(destination)
immediates = (word, is_copy, inc_is_immediate, signed, half_width)
alu = word & ALU_OP_MASK
result = compute_result(is_copy, source, destination, immediates, alu, true)
return false unless store?(@flags, effect)
push = !inc_is_immediate && (word & INCREMENT_MASK) > 0
store_result(result, source, destination, immediates, push, 0)
if immediates.first == 0 && source == 0 && destination == 0
if alu == 0
return 1
else
@pc += 1
return 2
end
end
0
end
|
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
# File 'lib/micro_cisc/vm/processor.rb', line 94
def (word, is_copy, inc_is_immediate, signed, half_width)
if is_copy
immediate_mask = IMMEDIATE_MASK | ALU_OP_MASK
immediate_shift = 0
else
immediate_mask = IMMEDIATE_MASK
immediate_shift = 4
end
sign_mask = IMMEDIATE_SIGN_MASK
if inc_is_immediate
sign_mask = INCREMENT_MASK
immediate_mask = immediate_mask | INCREMENT_MASK
end
if signed && ((word & sign_mask) != 0)
[~(~(word & immediate_mask) & immediate_mask) >> immediate_shift]
elsif half_width
[
(word & immediate_mask) >> (immediate_shift + 3),
(word & (immediate_mask >> 3)) >> immediate_shift
]
else
[(word & immediate_mask) >> immediate_shift]
end
end
|
378
379
380
|
# File 'lib/micro_cisc/vm/processor.rb', line 378
def format_data(data)
data.map { |w| '%04X' % w }.join(' ').gsub(/(([0-9A-Za-z]{4} ){16})/, "\\1\n")
end
|
#halt ⇒ Object
382
383
384
385
386
387
388
|
# File 'lib/micro_cisc/vm/processor.rb', line 382
def halt
delta = (Time.now - @t0)
MicroCisc.logger.info("HALT: #{@count} instructions in #{delta}s")
MicroCisc.logger.info("Stack: " + stack_string)
@run = false
end
|
#handle_control_update(address, value) ⇒ Object
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
# File 'lib/micro_cisc/vm/processor.rb', line 61
def handle_control_update(address, value)
if address == 0x7
self.pc = value
elsif address == 0x8
set_register(1, value)
elsif address == 0x9
set_register(2, value)
elsif address == 0xA
set_register(3, value)
elsif address == 0xB
@flags = value & 0xFFFF
elsif address == 0xC
self.control = value
end
end
|
#register(id) ⇒ Object
86
87
88
|
# File 'lib/micro_cisc/vm/processor.rb', line 86
def register(id)
@registers[id]
end
|
#run ⇒ Object
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
|
# File 'lib/micro_cisc/vm/processor.rb', line 338
def run
@t0 = Time.now
@count = 0
while(@run) do
word = read_mem(@id, @pc, true)
if @debug
do_command("#{'%04x' % [@pc]} #{ucisc(word)} ")
end
special = exec_instruction(word)
if special != 0
halt if special == 1
@debug = true if special == 2
end
if @pc_modified
@pc_modified = false
else
@pc += 1
end
@count += 1
allowed_count = (Time.now - @t0) * @clocks_per_second
while @count >= allowed_count
sleep(1.0 / @clocks_per_second)
allowed_count = (Time.now - @t0) * @clocks_per_second
end
end
end
|
#set_register(id, value) ⇒ Object
90
91
92
|
# File 'lib/micro_cisc/vm/processor.rb', line 90
def set_register(id, value)
@registers[id] = value
end
|
#source_value(source, immediate) ⇒ Object
135
136
137
138
139
140
141
142
143
144
145
146
|
# File 'lib/micro_cisc/vm/processor.rb', line 135
def source_value(source, immediate)
case source
when 0
(@pc + immediate) & 0xFFFF
when 1,2,3
read_mem(@id, (@registers[source] + immediate) & 0xFFFF)
when 4
immediate
else
(@registers[source - 4] + immediate) & 0xFFFF
end
end
|
#stack_string ⇒ Object
390
391
392
393
394
395
396
397
398
|
# File 'lib/micro_cisc/vm/processor.rb', line 390
def stack_string
address = @registers[1] & 0xFFFF
str = "#{'%04X' % address} => "
while(address > 0xFF00 && address < 0x10000 && address - (0xFFFF & @registers[1]) < 10)
str += "0x#{'%04X' % read_mem(@id, address)} "
address += 1
end
str
end
|
#start(debug = false) ⇒ Object
332
333
334
335
336
|
# File 'lib/micro_cisc/vm/processor.rb', line 332
def start(debug = false)
@debug = debug
@run = true
run
end
|
#store?(flags, effect) ⇒ Boolean
- 0 = zero?
- 1 = not zero?
- 2 = negative?
- 3 = store
126
127
128
129
130
131
132
133
|
# File 'lib/micro_cisc/vm/processor.rb', line 126
def store?(flags, effect)
return true if effect == 3
zero = flags & ZERO_MASK != 0
(effect == 0 && zero) ||
(effect == 1 && !zero) ||
(effect == 2 && (flags & NEGATIVE_MASK != 0))
end
|
#store_result(value, source, destination, immediates, push, sign) ⇒ Object
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
|
# File 'lib/micro_cisc/vm/processor.rb', line 203
def store_result(value, source, destination, immediates, push, sign)
case destination
when 0
@pc = value
@pc_modified = true
when 1,2,3
if push
@registers[destination] = (@registers[destination] - 1) & 0xFFFF
end
imm = immediates.size > 1 ? immediates.last : 0
address = @registers[destination] + imm
write_mem(@id, address, value)
when 4
self.control = value
else
@registers[destination - 4] = value
end
if push && !MEM_ARGS.include?(destination) && MEM_ARGS.include?(source)
@registers[source] = (@registers[source] + 1) & 0xFFFF
end
end
|
#ucisc(word) ⇒ Object
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
|
# File 'lib/micro_cisc/vm/processor.rb', line 400
def ucisc(word)
source = (word & SOURCE_MASK) >> SOURCE_SHIFT
destination = (word & DESTINATION_MASK) >> DESTINATION_SHIFT
effect = (word & EFFECT_MASK) >> EFFECT_SHIFT
src =
if source == 0
'0.reg'
elsif source < 4
"#{source}.mem"
elsif source == 4
'4.val'
else
"#{source - 4}.reg"
end
dest =
if destination == 0
'0.reg'
elsif destination < 4
"#{destination}.mem"
elsif destination == 4
'4.reg'
else
"#{destination - 4}.reg"
end
signed = !MEM_ARGS.include?(source)
inc_is_immediate = signed && !MEM_ARGS.include?(destination)
is_copy = (word & OP_MASK) == 0
half_width = is_copy && MEM_ARGS.include?(source) && MEM_ARGS.include?(destination)
immediates = (word, is_copy, inc_is_immediate, signed, half_width)
value = source_value(source, immediates.first)
store = store?(@flags, effect)
alu = word & ALU_OP_MASK
result = compute_result(is_copy, source, destination, immediates, alu, true)
alu = is_copy ? '' : "0x#{alu.to_s(16).upcase}.op "
imm0 = immediates.first < 0 ? "-#{(immediates.first * -1)}.imm" : "#{immediates.first}.imm"
imm1 =
if half_width
immediates.last < 0 ? "-#{(immediates.last * -1)}.imm " : "#{immediates.last}.imm "
else
""
end
eff = "#{effect}.eff"
push = !inc_is_immediate && (word & INCREMENT_MASK) > 0
push = push ? 'push ' : ''
ins = is_copy ? 'copy' : 'compute'
"Stack: #{stack_string}\n#{ins} #{alu}#{src} #{imm0} #{dest} #{imm1}#{eff} #{push}# value: #{value} (0x#{'%04x' % (value & 0xFFFF)}), result: #{result} (0x#{'%04x' % (result & 0xFFFF)}), #{'not ' if !store}stored"
end
|