Rdcpu16 0.0.1
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/MIT-LICENSE +21 -0
- data/README.rdoc +10 -0
- data/Rakefile +42 -0
- data/lib/dcpu16/cpu/instructions.rb +137 -0
- data/lib/dcpu16/cpu.rb +111 -0
- data/lib/dcpu16/instruction.rb +57 -0
- data/lib/dcpu16/literal.rb +19 -0
- data/lib/dcpu16/memory.rb +40 -0
- data/lib/dcpu16/operand.rb +61 -0
- data/lib/dcpu16/register.rb +35 -0
- data/lib/dcpu16/support/debug.rb +47 -0
- data/lib/dcpu16/version.rb +4 -0
- data/lib/dcpu16/word.rb +20 -0
- data/lib/dcpu16.rb +6 -0
- data/spec/integration/example1_spec.rb +28 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/unit/dcpu16_cpu_spec.rb +45 -0
- data/spec/unit/dcpu16_instruction_spec.rb +28 -0
- data/spec/unit/dcpu16_instructions_spec.rb +255 -0
- data/spec/unit/dcpu16_memory_spec.rb +16 -0
- data/spec/unit/dcpu16_operand_spec.rb +78 -0
- data/spec/unit/dcpu16_register_spec.rb +6 -0
- data/spec/unit/dcpu16_word_spec.rb +17 -0
- data/spec/unit/support/sample_observer.rb +14 -0
- metadata +90 -0
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Copyright 2012 Patrick Helm
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
21
|
+
|
data/README.rdoc
ADDED
data/Rakefile
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
##!/usr/bin/env rake
|
|
2
|
+
##include Rake::DSL
|
|
3
|
+
|
|
4
|
+
require "bundler/gem_tasks"
|
|
5
|
+
|
|
6
|
+
#begin
|
|
7
|
+
# require 'bundler/setup'
|
|
8
|
+
#rescue LoadError
|
|
9
|
+
# puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
|
10
|
+
#end
|
|
11
|
+
#begin
|
|
12
|
+
# require 'rdoc/task'
|
|
13
|
+
#rescue LoadError
|
|
14
|
+
# require 'rdoc/rdoc'
|
|
15
|
+
# require 'rake/rdoctask'
|
|
16
|
+
# RDoc::Task = Rake::RDocTask
|
|
17
|
+
#end
|
|
18
|
+
|
|
19
|
+
#RDoc::Task.new(:rdoc) do |rdoc|
|
|
20
|
+
# rdoc.rdoc_dir = 'rdoc'
|
|
21
|
+
# rdoc.title = 'RubyRescuetime'
|
|
22
|
+
# rdoc.options << '--line-numbers'
|
|
23
|
+
# rdoc.rdoc_files.include('README.rdoc')
|
|
24
|
+
# rdoc.rdoc_files.include('lib/**/*.rb')
|
|
25
|
+
#end
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
#Bundler::GemHelper.install_tasks
|
|
30
|
+
|
|
31
|
+
## require 'rake/testtask'
|
|
32
|
+
|
|
33
|
+
##Rake::TestTask.new(:test) do |t|
|
|
34
|
+
## t.libs << 'lib'
|
|
35
|
+
## t.libs << 'test'
|
|
36
|
+
## t.pattern = 'test/**/*_test.rb'
|
|
37
|
+
## t.verbose = true
|
|
38
|
+
##end
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
##task :default => :test
|
|
42
|
+
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
module DCPU16
|
|
2
|
+
module Instructions
|
|
3
|
+
# sets a to b
|
|
4
|
+
def set(a, b)
|
|
5
|
+
@cycle += 1
|
|
6
|
+
b
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# sets a to a+b, sets O to 0x0001 if there's an overflow, 0x0 otherwise
|
|
10
|
+
def add(a, b)
|
|
11
|
+
@cycle += 2
|
|
12
|
+
(a + b > 0xFFFF) ? @O.write(0x0001) : @O.write(0x0)
|
|
13
|
+
a + b
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# sets a to a-b, sets O to 0xffff if there's an underflow, 0x0 otherwise
|
|
17
|
+
def sub(a, b)
|
|
18
|
+
@cycle += 2
|
|
19
|
+
(b > a) ? @O.write(0xffff) : @O.write(0x0)
|
|
20
|
+
a - b
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# sets a to a*b, sets O to
|
|
24
|
+
def mul(a, b)
|
|
25
|
+
@cycle += 2
|
|
26
|
+
@O.write( ( (a * b) >> 16) & 0xffff )
|
|
27
|
+
a * b
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# sets a to a/b, sets O to ((a<<16)/b)&0xffff. if b==0, sets a and O to 0 instead.
|
|
31
|
+
def div(a, b)
|
|
32
|
+
@cycle += 3
|
|
33
|
+
if b == 0
|
|
34
|
+
@O.write(0)
|
|
35
|
+
return 0
|
|
36
|
+
else
|
|
37
|
+
@O.write( ( (a << 16) / b) & 0xffff )
|
|
38
|
+
return (a / b)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# sets a to a%b. if b==0, sets a to 0 instead.
|
|
43
|
+
def mod(a, b)
|
|
44
|
+
@cycle += 3
|
|
45
|
+
(b == 0) ? 0 : a % b
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# sets a to a<<b, sets O to ((a<<b)>>16)&0xffff
|
|
49
|
+
def shl(a, b)
|
|
50
|
+
@cycle += 2
|
|
51
|
+
@O.write( ( (a << b) >> 16 ) & 0xffff )
|
|
52
|
+
a << b
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# sets a to a>>b, sets O to ((a<<16)>>b)&0xffff
|
|
56
|
+
def shr(a, b)
|
|
57
|
+
@cycle += 2
|
|
58
|
+
@O.write( ( (a << 16) >> b) & 0xffff )
|
|
59
|
+
a >> b
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# sets a to a & b
|
|
63
|
+
def and(a, b)
|
|
64
|
+
@cycle += 1
|
|
65
|
+
a & b
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# sets a to a | b
|
|
69
|
+
def bor(a, b)
|
|
70
|
+
@cycle += 1
|
|
71
|
+
a | b
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# sets a to a ^ b
|
|
75
|
+
def xor(a, b)
|
|
76
|
+
@cycle += 1
|
|
77
|
+
a ^ b
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# performs next instruction only if a == b
|
|
81
|
+
def ife(a, b)
|
|
82
|
+
@cycle += 2
|
|
83
|
+
@skip = !(a == b)
|
|
84
|
+
@cycle += 1 unless a == b
|
|
85
|
+
|
|
86
|
+
return nil
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# performs next instruction only if a != b
|
|
90
|
+
def ifn(a, b)
|
|
91
|
+
@cycle += 2
|
|
92
|
+
@skip = !(a != b)
|
|
93
|
+
@cycle += 1 unless a != b
|
|
94
|
+
|
|
95
|
+
return nil
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# performs next instruction only if a > b
|
|
99
|
+
def ifg(a, b)
|
|
100
|
+
@cycle += 2
|
|
101
|
+
@skip = !(a > b)
|
|
102
|
+
@cycle += 1 unless a > b
|
|
103
|
+
|
|
104
|
+
return nil
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# performs next instruction only if (a & b) != 0
|
|
108
|
+
def ifb(a, b)
|
|
109
|
+
@cycle += 2
|
|
110
|
+
@skip = !((a & b) != 0)
|
|
111
|
+
@cycle += 1 unless (a & b) != 0
|
|
112
|
+
|
|
113
|
+
return nil
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class Reserved < StandardError; end
|
|
118
|
+
|
|
119
|
+
### NON - Basic ###
|
|
120
|
+
def reserved(a)
|
|
121
|
+
raise Reserved
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# JSR a - pushes the address of the next instruction to the stack, then sets PC to a
|
|
125
|
+
def jsr(a)
|
|
126
|
+
@cycle += 2
|
|
127
|
+
|
|
128
|
+
# pushes the address of the next instruction to the stack
|
|
129
|
+
@SP -= 1
|
|
130
|
+
@memory.write(@SP, @PC.read)
|
|
131
|
+
|
|
132
|
+
@PC.write(a)
|
|
133
|
+
return nil
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
data/lib/dcpu16/cpu.rb
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
require 'dcpu16/support/debug'
|
|
2
|
+
|
|
3
|
+
require 'dcpu16/cpu/instructions'
|
|
4
|
+
|
|
5
|
+
require 'dcpu16/instruction'
|
|
6
|
+
require 'dcpu16/literal'
|
|
7
|
+
require 'dcpu16/memory'
|
|
8
|
+
require 'dcpu16/operand'
|
|
9
|
+
require 'dcpu16/register'
|
|
10
|
+
|
|
11
|
+
require "observer"
|
|
12
|
+
|
|
13
|
+
module DCPU16
|
|
14
|
+
class CPU
|
|
15
|
+
include DCPU16::Debug
|
|
16
|
+
include DCPU16::Instructions
|
|
17
|
+
include Observable
|
|
18
|
+
|
|
19
|
+
RAM_SIZE = 0x10000
|
|
20
|
+
REGISTERS = [:A, :B, :C, :X, :Y, :Z, :I, :J]
|
|
21
|
+
REGISTERS_COUNT = REGISTERS.length
|
|
22
|
+
CLOCK_CYCLE = 100000 # cycles per second
|
|
23
|
+
|
|
24
|
+
attr_accessor :cycle, :memory, :registers
|
|
25
|
+
|
|
26
|
+
# program counter (PC), stack pointer (SP), overflow (O)
|
|
27
|
+
attr_accessor :PC, :SP, :O
|
|
28
|
+
|
|
29
|
+
# HACK: actually used to determine if we need to skip next instruction
|
|
30
|
+
attr_accessor :skip
|
|
31
|
+
|
|
32
|
+
attr_accessor :clock_cycle
|
|
33
|
+
|
|
34
|
+
def initialize(memory = [])
|
|
35
|
+
@cycle = 0
|
|
36
|
+
@memory = DCPU16::Memory.new(memory)
|
|
37
|
+
@registers = Array.new(REGISTERS_COUNT) { DCPU16::Register.new }
|
|
38
|
+
@PC = DCPU16::Register.new(0x0)
|
|
39
|
+
@SP = DCPU16::Register.new(0xFFFF)
|
|
40
|
+
@O = DCPU16::Register.new(0x0)
|
|
41
|
+
|
|
42
|
+
@clock_cycle = CLOCK_CYCLE
|
|
43
|
+
@debug = true
|
|
44
|
+
@skip = false
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Define alias_methods for Registers: A(), B(), ...
|
|
48
|
+
REGISTERS.each_with_index { |k, v| define_method(k) { registers[v] } }
|
|
49
|
+
|
|
50
|
+
# Run in endless loop
|
|
51
|
+
def run
|
|
52
|
+
@started_at = Time.now
|
|
53
|
+
max_cycles = 1
|
|
54
|
+
|
|
55
|
+
while true do
|
|
56
|
+
if @cycle < max_cycles
|
|
57
|
+
step
|
|
58
|
+
else
|
|
59
|
+
diff = Time.now - @started_at
|
|
60
|
+
max_cycles = (diff * @clock_cycle)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Resets the CPU and its sub-systems (registers, memory, ...)
|
|
66
|
+
def reset
|
|
67
|
+
@cycle = 0
|
|
68
|
+
@memory.reset
|
|
69
|
+
@registers.each { |r| r.reset }
|
|
70
|
+
@PC.reset
|
|
71
|
+
@SP.reset
|
|
72
|
+
@O.reset
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# DOC
|
|
76
|
+
def last_instruction
|
|
77
|
+
@instruction
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Perform a single step
|
|
81
|
+
# TODO: Refacor if/else/if/else ...
|
|
82
|
+
def step
|
|
83
|
+
@instruction = Instruction.new(@memory.read(@PC))
|
|
84
|
+
@PC += 1
|
|
85
|
+
|
|
86
|
+
op = @instruction.op
|
|
87
|
+
a = get_operand(@instruction.a)
|
|
88
|
+
b = get_operand(@instruction.b) if @instruction.b
|
|
89
|
+
|
|
90
|
+
if @skip
|
|
91
|
+
@skip = false
|
|
92
|
+
else
|
|
93
|
+
if b
|
|
94
|
+
result = self.send(op, a.value, b.value)
|
|
95
|
+
else
|
|
96
|
+
result = self.send(op, a.value)
|
|
97
|
+
end
|
|
98
|
+
a.write(result) if result
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
changed
|
|
102
|
+
notify_observers(self)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# TODO: May be removed
|
|
106
|
+
def get_operand(value)
|
|
107
|
+
DCPU16::Operand.new(self, value)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Instructions are 1-3 words long and are fully defined by the first word.
|
|
2
|
+
# In a basic instruction, the lower four bits of the first word of the instruction are the opcode,
|
|
3
|
+
# and the remaining twelve bits are split into two six bit values, called a and b.
|
|
4
|
+
# a is always handled by the processor before b, and is the lower six bits.
|
|
5
|
+
|
|
6
|
+
# In bits (with the least significant being last),
|
|
7
|
+
# a basic instruction has the format: bbbbbbaaaaaaoooo
|
|
8
|
+
|
|
9
|
+
# bbbb bbaa aaaa oooo
|
|
10
|
+
# 0000 0000 0000 1111 -> 0x000f
|
|
11
|
+
# 0000 0011 1111 0000 -> 0x03f0
|
|
12
|
+
# 1111 1100 0000 0000 -> 0xfc00
|
|
13
|
+
|
|
14
|
+
# Non-basic opcodes always have their lower four bits unset, have one value and a six bit opcode.
|
|
15
|
+
# In binary, they have the format: aaaaaaoooooo0000
|
|
16
|
+
|
|
17
|
+
# aaaa aaoo oooo 0000
|
|
18
|
+
# 0000 0011 1111 0000 -> 0x03f0
|
|
19
|
+
# 1111 1100 0000 0000 -> 0xfc00
|
|
20
|
+
|
|
21
|
+
module DCPU16
|
|
22
|
+
class Instruction
|
|
23
|
+
INSTRUCTIONS = [
|
|
24
|
+
:reserved,
|
|
25
|
+
:set, :add, :sub, :mul, :div, :mod,
|
|
26
|
+
:shl, :shr,
|
|
27
|
+
:and, :bor, :xor,
|
|
28
|
+
:ife, :ifn, :ifg, :ifb
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
NON_BASIC_INSTRUCTIONS = [
|
|
32
|
+
:reserved,
|
|
33
|
+
:jsr
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
attr_reader :opcode, :a, :b, :word
|
|
37
|
+
|
|
38
|
+
def initialize(word = nil)
|
|
39
|
+
@word = word.value
|
|
40
|
+
@opcode = @word & 0x000F
|
|
41
|
+
|
|
42
|
+
if @opcode == 0x0
|
|
43
|
+
@non_basic = true
|
|
44
|
+
@opcode = (@word >> 4) & 0x3f
|
|
45
|
+
@a = @word >> 10
|
|
46
|
+
else
|
|
47
|
+
@a = (@word >> 4) & 0x3f
|
|
48
|
+
@b = @word >> 10
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def op
|
|
53
|
+
@non_basic ? NON_BASIC_INSTRUCTIONS[@opcode] : INSTRUCTIONS[@opcode]
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Ruby, Y U NO INHERIT FROM INTEGER?
|
|
2
|
+
module DCPU16
|
|
3
|
+
class Literal
|
|
4
|
+
def initialize(value)
|
|
5
|
+
@value = value
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def value
|
|
9
|
+
@value
|
|
10
|
+
end
|
|
11
|
+
alias_method :read, :value
|
|
12
|
+
|
|
13
|
+
# If any instruction tries to assign a literal value, the assignment fails silently
|
|
14
|
+
def write(value)
|
|
15
|
+
# pass
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require 'dcpu16/word'
|
|
2
|
+
|
|
3
|
+
module DCPU16
|
|
4
|
+
class Memory < Array
|
|
5
|
+
SIZE = 0x10000
|
|
6
|
+
DEFAULT_VALUE = 0x0
|
|
7
|
+
|
|
8
|
+
def initialize(default = [])
|
|
9
|
+
super(SIZE, DEFAULT_VALUE)
|
|
10
|
+
default.each_with_index { |word, offset| write(offset, word) }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def read(offset)
|
|
14
|
+
# HACK: so we can just pass a Fixnum or a Register
|
|
15
|
+
offset = offset.value if offset.respond_to? :value
|
|
16
|
+
|
|
17
|
+
DCPU16::Word.new(self[offset], self, offset)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def write(offset, value)
|
|
21
|
+
# HACK: so we can just pass a Fixnum or a Register
|
|
22
|
+
offset = offset.value if offset.respond_to? :value
|
|
23
|
+
|
|
24
|
+
self[offset] = value
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def reset
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
def [](key)
|
|
32
|
+
super(key)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def []=(key, value)
|
|
36
|
+
super(key, value)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# TODO: Test && Refacoring
|
|
2
|
+
module DCPU16
|
|
3
|
+
class Operand
|
|
4
|
+
class NotFound < StandardError; end
|
|
5
|
+
|
|
6
|
+
def self.new(cpu, value)
|
|
7
|
+
if (0x00..0x07).include?(value)
|
|
8
|
+
# register (A, B, C, X, Y, Z, I or J, in that order)
|
|
9
|
+
return cpu.registers[value]
|
|
10
|
+
elsif (0x08..0x0f).include?(value)
|
|
11
|
+
# [register]
|
|
12
|
+
register = cpu.registers[value - DCPU16::CPU::REGISTERS_COUNT]
|
|
13
|
+
return cpu.memory.read(register)
|
|
14
|
+
elsif (0x10..0x17).include?(value)
|
|
15
|
+
cpu.cycle += 1
|
|
16
|
+
offset = cpu.memory.read(cpu.PC).value
|
|
17
|
+
cpu.PC += 1
|
|
18
|
+
return cpu.memory.read(offset + cpu.registers[value - 0x10].read)
|
|
19
|
+
elsif value == 0x18
|
|
20
|
+
# POP / [SP++]
|
|
21
|
+
r = cpu.memory.read(cpu.SP)
|
|
22
|
+
cpu.SP += 1
|
|
23
|
+
return r
|
|
24
|
+
elsif value == 0x19
|
|
25
|
+
# PEEK / [SP]
|
|
26
|
+
return cpu.memory.read(cpu.SP)
|
|
27
|
+
elsif value == 0x1a
|
|
28
|
+
# PUSH / [--SP]
|
|
29
|
+
cpu.SP -= 1
|
|
30
|
+
cpu.memory.read(cpu.SP)
|
|
31
|
+
elsif value == 0x1b
|
|
32
|
+
# SP
|
|
33
|
+
return cpu.SP
|
|
34
|
+
elsif value == 0x1c
|
|
35
|
+
# PC
|
|
36
|
+
return cpu.PC
|
|
37
|
+
elsif value == 0x1d
|
|
38
|
+
# O
|
|
39
|
+
return cpu.O
|
|
40
|
+
elsif value == 0x1e
|
|
41
|
+
# [next word]
|
|
42
|
+
cpu.cycle += 1
|
|
43
|
+
offset = cpu.memory.read(cpu.PC)
|
|
44
|
+
cpu.PC += 1
|
|
45
|
+
return cpu.memory.read(offset)
|
|
46
|
+
elsif value == 0x1f
|
|
47
|
+
# next word (literal)
|
|
48
|
+
cpu.cycle += 1
|
|
49
|
+
r = cpu.memory.read(cpu.PC)
|
|
50
|
+
cpu.PC += 1
|
|
51
|
+
return r
|
|
52
|
+
elsif (0x20..0x3f).include?(value)
|
|
53
|
+
# literal value 0x00-0x1f (literal)
|
|
54
|
+
DCPU16::Literal.new(value - 0x20)
|
|
55
|
+
else
|
|
56
|
+
raise NotFound
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module DCPU16
|
|
2
|
+
class Register
|
|
3
|
+
attr_reader :value
|
|
4
|
+
|
|
5
|
+
def initialize(value = 0x0)
|
|
6
|
+
@default_value = value
|
|
7
|
+
reset
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def value
|
|
11
|
+
warn "[Register #{self}] No Value defined" unless @value
|
|
12
|
+
@value
|
|
13
|
+
end
|
|
14
|
+
alias_method :read, :value
|
|
15
|
+
|
|
16
|
+
def +(value)
|
|
17
|
+
@value += value
|
|
18
|
+
self
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def -(value)
|
|
22
|
+
@value -= value
|
|
23
|
+
self
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def write(value)
|
|
27
|
+
@value = value
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def reset
|
|
31
|
+
@value = @default_value
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Just a simple Debugger class
|
|
2
|
+
module DCPU16
|
|
3
|
+
module Debug
|
|
4
|
+
# Debug-mode turned on?
|
|
5
|
+
def debug?
|
|
6
|
+
@debug ||= false
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
# Debug-Wrapper
|
|
11
|
+
def debug(msg = nil, &block)
|
|
12
|
+
return unless debug?
|
|
13
|
+
|
|
14
|
+
puts "\n[DEBUG] - #{caller.first}"
|
|
15
|
+
msg.each { |m| puts(m) } if msg.is_a?(Array)
|
|
16
|
+
|
|
17
|
+
if msg.is_a?(Hash)
|
|
18
|
+
msg.each do |k, v|
|
|
19
|
+
puts "[#{k.to_s}]"
|
|
20
|
+
|
|
21
|
+
if v.is_a?(Array)
|
|
22
|
+
v.each {|m| puts(m) }
|
|
23
|
+
else
|
|
24
|
+
puts v
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
elsif (msg.is_a?(String) || msg.is_a?(Symbol))
|
|
28
|
+
puts msg.to_s
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
yield if block_given?
|
|
32
|
+
puts "\n"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# DCPU16::CPU
|
|
36
|
+
def debug_state
|
|
37
|
+
debug do
|
|
38
|
+
puts "Cycle:\t#{@cycle}"
|
|
39
|
+
puts "PC:\t#{@PC}"
|
|
40
|
+
puts "SP:\t#{@SP}"
|
|
41
|
+
puts "O:\t#{@O}"
|
|
42
|
+
puts "R:\t#{@registers}"
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
data/lib/dcpu16/word.rb
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module DCPU16
|
|
2
|
+
class Word
|
|
3
|
+
def initialize(value, memory = nil, offset = nil)
|
|
4
|
+
@value = value
|
|
5
|
+
@memory = memory
|
|
6
|
+
@offset = offset
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def value
|
|
10
|
+
@value
|
|
11
|
+
end
|
|
12
|
+
alias_method :read, :value
|
|
13
|
+
|
|
14
|
+
def write(value)
|
|
15
|
+
@value = value
|
|
16
|
+
@memory.write(@offset, value)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
data/lib/dcpu16.rb
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
describe DCPU16::CPU do
|
|
4
|
+
describe "quick example by notch" do
|
|
5
|
+
let(:dump) do
|
|
6
|
+
[ 0x7c01, 0x0030, 0x7de1, 0x1000, 0x0020, 0x7803, 0x1000, 0xc00d,
|
|
7
|
+
0x7dc1, 0x001a, 0xa861, 0x7c01, 0x2000, 0x2161, 0x2000, 0x8463,
|
|
8
|
+
0x806d, 0x7dc1, 0x000d, 0x9031, 0x7c10, 0x0018, 0x7dc1, 0x001a,
|
|
9
|
+
#0x9037, 0x61c1, 0x7dc1, 0x001a, 0x0000, 0x0000, 0x0000, 0x0000 ]
|
|
10
|
+
0x9037, 0x61c1, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 ]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
subject { DCPU16::CPU.new(dump) }
|
|
14
|
+
|
|
15
|
+
# Just double-check that memory is loaded
|
|
16
|
+
specify { subject.memory.read(0x0).value.should == 0x7c01 }
|
|
17
|
+
# specify { subject.memory.read(0x1b).value.should == 0x001a }
|
|
18
|
+
|
|
19
|
+
specify do
|
|
20
|
+
expect { subject.run }.to raise_error
|
|
21
|
+
|
|
22
|
+
subject.cycle.should == 104
|
|
23
|
+
subject.A.value.should == 0x2000
|
|
24
|
+
subject.X.value.should == 0x40
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require 'spec_helper.rb'
|
|
2
|
+
require 'unit/support/sample_observer'
|
|
3
|
+
|
|
4
|
+
describe DCPU16::CPU do
|
|
5
|
+
# Initialize with non failing memory dump
|
|
6
|
+
subject { DCPU16::CPU.new( Array.new(10000, 0x01) ) }
|
|
7
|
+
|
|
8
|
+
describe "Register" do
|
|
9
|
+
its(:registers) { subject.length == 8 }
|
|
10
|
+
its(:registers) { subject[0] == be_kind_of(DCPU16::Register) }
|
|
11
|
+
|
|
12
|
+
its(:PC) { should be_kind_of(DCPU16::Register) }
|
|
13
|
+
its(:SP) { should be_kind_of(DCPU16::Register) }
|
|
14
|
+
its(:O) { should be_kind_of(DCPU16::Register) }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
its(:cycle) { should == 0 }
|
|
18
|
+
its(:memory) { should be_kind_of(DCPU16::Memory) }
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
describe "#reset" do
|
|
22
|
+
it "resets its #cycle" do
|
|
23
|
+
subject.cycle = 2
|
|
24
|
+
subject.reset
|
|
25
|
+
subject.cycle.should == 0
|
|
26
|
+
end
|
|
27
|
+
it "resets its #memory" do
|
|
28
|
+
subject.memory.should_receive(:reset)
|
|
29
|
+
subject.reset
|
|
30
|
+
end
|
|
31
|
+
it "resets its #registers" do
|
|
32
|
+
subject.registers.each { |register| register.should_receive(:reset) }
|
|
33
|
+
subject.reset
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
describe "Observable" do
|
|
38
|
+
let(:observer) { DCPU16::SampleObserver.new }
|
|
39
|
+
specify do
|
|
40
|
+
subject.add_observer(observer)
|
|
41
|
+
expect { subject.step }.to change{observer.cycle}.by(1)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
describe DCPU16::Instruction do
|
|
4
|
+
context "when initialized with 0x00" do
|
|
5
|
+
let(:word) { DCPU16::Word.new(0x00) }
|
|
6
|
+
subject { DCPU16::Instruction.new(word) }
|
|
7
|
+
its(:opcode) { should == 0 }
|
|
8
|
+
its(:a) { should == 0 }
|
|
9
|
+
its(:b) { should be_nil }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
context "when initialized with 0x0001" do
|
|
13
|
+
let(:word) { DCPU16::Word.new(0x0001) }
|
|
14
|
+
subject { DCPU16::Instruction.new(word) }
|
|
15
|
+
its(:opcode) { should == 1 }
|
|
16
|
+
its(:a) { should == 0 }
|
|
17
|
+
its(:b) { should == 0 }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
context "when initialized with 0xa861" do
|
|
21
|
+
let(:word) { DCPU16::Word.new(0xa861) }
|
|
22
|
+
subject { DCPU16::Instruction.new(word) }
|
|
23
|
+
its(:opcode) { should == 1 }
|
|
24
|
+
its(:a) { should == 0x6 }
|
|
25
|
+
its(:b) { should == 0xa + 0x20 } # Literal value (offset += 0x20)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
require 'spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
describe DCPU16::Instructions do
|
|
4
|
+
subject { DCPU16::CPU.new }
|
|
5
|
+
let(:a) { 0x01 }
|
|
6
|
+
let(:b) { 0x02 }
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
describe "#set" do
|
|
10
|
+
it "sets a to b" do
|
|
11
|
+
subject.set(a, b).should == b
|
|
12
|
+
end
|
|
13
|
+
it "take 1 cycle, plus the cost of a and b" do
|
|
14
|
+
expect { subject.set(a, b) }.to change{subject.cycle}.by(1)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
describe "#add" do
|
|
20
|
+
it "sets a to a+b" do
|
|
21
|
+
subject.add(2, 3).should == 5
|
|
22
|
+
end
|
|
23
|
+
context "with overflow" do
|
|
24
|
+
it "sets O to 0x0001" do
|
|
25
|
+
subject.add(0xFFFE, 2)
|
|
26
|
+
subject.O.value.should == 0x0001
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
context "without overflow" do
|
|
30
|
+
it "sets O to 0x0" do
|
|
31
|
+
subject.add(0xFFFE, 1)
|
|
32
|
+
subject.O.value.should == 0x0
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
it "take 2 cycles, plus the cost of a and b" do
|
|
36
|
+
expect { subject.add(a, b) }.to change{subject.cycle}.by(2)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
describe "#sub" do
|
|
42
|
+
it "sets a to a-b" do
|
|
43
|
+
subject.sub(3, 2).should == 1
|
|
44
|
+
end
|
|
45
|
+
context "with underflow" do
|
|
46
|
+
it "set O to 0xffff" do
|
|
47
|
+
subject.sub(2, 3)
|
|
48
|
+
subject.O.value.should == 0xffff
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
context "without underflow" do
|
|
52
|
+
it "set O to 0x0" do
|
|
53
|
+
subject.sub(2, 2)
|
|
54
|
+
subject.O.value.should == 0x0
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
it "take 2 cycles, plus the cost of a and b" do
|
|
58
|
+
expect { subject.sub(a, b) }.to change{subject.cycle}.by(2)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
describe "#mul" do
|
|
64
|
+
it "sets a to a*b" do
|
|
65
|
+
subject.mul(2, 3).should == 6
|
|
66
|
+
end
|
|
67
|
+
it "sets O to ((a*b)>>16)&0xffff" do
|
|
68
|
+
subject.mul(256, 256)
|
|
69
|
+
subject.O.value.should == ((256*256)>>16) & 0xFFFF
|
|
70
|
+
end
|
|
71
|
+
it "take 2 cycles, plus the cost of a and b" do
|
|
72
|
+
expect { subject.mul(a, b) }.to change{subject.cycle}.by(2)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
describe "#div" do
|
|
78
|
+
it "sets a to a/b" do
|
|
79
|
+
subject.div(9, 3).should == 3
|
|
80
|
+
subject.div(8, 3).should == 2
|
|
81
|
+
end
|
|
82
|
+
context "when b != 0" do
|
|
83
|
+
it "sets O to ((a<<16)/b)&0xffff" do
|
|
84
|
+
subject.div(1, 2)
|
|
85
|
+
subject.O.value.should == ((1 << 16)/2) & 0xFFFF
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
context "when b == 0" do
|
|
89
|
+
it "sets a and O to 0" do
|
|
90
|
+
subject.div(3, 0).should == 0
|
|
91
|
+
subject.O.value.should == 0
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
it "take 3 cycles, plus the cost of a and b" do
|
|
95
|
+
expect { subject.div(a, b) }.to change{subject.cycle}.by(3)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
describe "#mod" do
|
|
101
|
+
context "when b != 0" do
|
|
102
|
+
it "sets a to a % b" do
|
|
103
|
+
subject.mod(3, 2).should == 1
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
context "when b == 0" do
|
|
107
|
+
it "sets a to 0" do
|
|
108
|
+
subject.mod(3, 0).should == 0
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
it "take 3 cycles, plus the cost of a and b" do
|
|
112
|
+
expect { subject.mod(a, b) }.to change{subject.cycle}.by(3)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
describe "#shl" do
|
|
118
|
+
it "sets a to a<<b" do
|
|
119
|
+
subject.shl(4, 2).should == 16
|
|
120
|
+
end
|
|
121
|
+
it "sets O to ((a<<b)>>16)&0xffff" do
|
|
122
|
+
subject.shl(1, 17)
|
|
123
|
+
subject.O.value.should == ((1 << 17) >> 16)&0xffff
|
|
124
|
+
end
|
|
125
|
+
it "take 2 cycles, plus the cost of a and b" do
|
|
126
|
+
expect { subject.shl(a, b) }.to change{subject.cycle}.by(2)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
describe "#shr" do
|
|
132
|
+
it "sets a to a>>b" do
|
|
133
|
+
subject.shr(4, 2).should == 1
|
|
134
|
+
end
|
|
135
|
+
it "sets O to ((a<<16)>>b)&0xffff" do
|
|
136
|
+
subject.shr(2, 2)
|
|
137
|
+
subject.O.value.should == ((2 << 16) >> 2) & 0xffff
|
|
138
|
+
end
|
|
139
|
+
it "take 2 cycles, plus the cost of a and b" do
|
|
140
|
+
expect { subject.shr(a, b) }.to change{subject.cycle}.by(2)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
describe "#and" do
|
|
146
|
+
it "sets a to a&b" do
|
|
147
|
+
subject.and(1, 2).should == 0
|
|
148
|
+
subject.and(1, 3).should == 1
|
|
149
|
+
end
|
|
150
|
+
it "take 1 cycles, plus the cost of a and b" do
|
|
151
|
+
expect { subject.and(a, b) }.to change{subject.cycle}.by(1)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
describe "#bor" do
|
|
157
|
+
it "sets a to a|b" do
|
|
158
|
+
subject.bor(1, 2).should == 3
|
|
159
|
+
end
|
|
160
|
+
it "take 1 cycles, plus the cost of a and b" do
|
|
161
|
+
expect { subject.bor(a, b) }.to change{subject.cycle}.by(1)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
describe "#xor" do
|
|
167
|
+
it "sets a to a^b" do
|
|
168
|
+
subject.xor(1, 3).should == 2
|
|
169
|
+
end
|
|
170
|
+
it "take 1 cycles, plus the cost of a and b" do
|
|
171
|
+
expect { subject.xor(a, b) }.to change{subject.cycle}.by(1)
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
describe "#ife" do
|
|
177
|
+
it "performs next instruction only if a==b" do
|
|
178
|
+
subject.ife(1, 1)
|
|
179
|
+
subject.skip.should be_false
|
|
180
|
+
subject.ife(1, 2)
|
|
181
|
+
subject.skip.should be_true
|
|
182
|
+
end
|
|
183
|
+
context "if test passes" do
|
|
184
|
+
it "take 2 cycles, plus the cost of a and b" do
|
|
185
|
+
expect { subject.ife(2, 2) }.to change{subject.cycle}.by(2)
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
context "if test fails" do
|
|
189
|
+
it "take 3 cycles, plus the cost of a and b" do
|
|
190
|
+
expect { subject.ife(2, 3) }.to change{subject.cycle}.by(3)
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
describe "#ifn" do
|
|
197
|
+
it "performs next instruction only if a!=b" do
|
|
198
|
+
subject.ifn(1, 1)
|
|
199
|
+
subject.skip.should be_true
|
|
200
|
+
subject.ifn(1, 2)
|
|
201
|
+
subject.skip.should be_false
|
|
202
|
+
end
|
|
203
|
+
context "if test passes" do
|
|
204
|
+
it "take 2 cycles, plus the cost of a and b" do
|
|
205
|
+
expect { subject.ifn(1, 2) }.to change{subject.cycle}.by(2)
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
context "if test fails" do
|
|
209
|
+
it "take 3 cycles, plus the cost of a and b" do
|
|
210
|
+
expect { subject.ifn(2, 2) }.to change{subject.cycle}.by(3)
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
describe "#ifg" do
|
|
217
|
+
it "performs next instruction only if a > b" do
|
|
218
|
+
subject.ifg(1, 2)
|
|
219
|
+
subject.skip.should be_true
|
|
220
|
+
subject.ifg(2, 1)
|
|
221
|
+
subject.skip.should be_false
|
|
222
|
+
end
|
|
223
|
+
context "if test passes" do
|
|
224
|
+
it "take 2 cycles, plus the cost of a and b" do
|
|
225
|
+
expect { subject.ifg(3, 2) }.to change{subject.cycle}.by(2)
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
context "if test fails" do
|
|
229
|
+
it "take 3 cycles, plus the cost of a and b" do
|
|
230
|
+
expect { subject.ifg(2, 3) }.to change{subject.cycle}.by(3)
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
describe "#ifb" do
|
|
237
|
+
it "performs next instruction only if (a&b)!=0" do
|
|
238
|
+
subject.ifb(1, 3)
|
|
239
|
+
subject.skip.should be_false
|
|
240
|
+
subject.ifb(1, 2)
|
|
241
|
+
subject.skip.should be_true
|
|
242
|
+
end
|
|
243
|
+
context "if test passes" do
|
|
244
|
+
it "take 2 cycles, plus the cost of a and b" do
|
|
245
|
+
expect { subject.ifb(1, 3) }.to change{subject.cycle}.by(2)
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
context "if test fails" do
|
|
249
|
+
it "take 3 cycles, plus the cost of a and b" do
|
|
250
|
+
expect { subject.ifb(1, 2) }.to change{subject.cycle}.by(3)
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require 'spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
describe DCPU16::Memory do
|
|
4
|
+
its(:length) { should == 0x10000 }
|
|
5
|
+
|
|
6
|
+
describe "#read" do
|
|
7
|
+
specify { subject.read(0).should be_a_kind_of(DCPU16::Word) }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
specify "#write" do
|
|
12
|
+
subject.write(0x1000, 42)
|
|
13
|
+
subject.read(0x1000).value.should == 42
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
require 'spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
describe DCPU16::Instructions do
|
|
4
|
+
let(:cpu) { DCPU16::CPU.new }
|
|
5
|
+
|
|
6
|
+
context "when value is between 0x00 and 0x07" do
|
|
7
|
+
specify { DCPU16::Operand.new(cpu, 0x0).should == cpu.A }
|
|
8
|
+
specify { DCPU16::Operand.new(cpu, 0x1).should == cpu.B }
|
|
9
|
+
specify { DCPU16::Operand.new(cpu, 0x2).should == cpu.C }
|
|
10
|
+
specify { DCPU16::Operand.new(cpu, 0x3).should == cpu.X }
|
|
11
|
+
specify { DCPU16::Operand.new(cpu, 0x4).should == cpu.Y }
|
|
12
|
+
specify { DCPU16::Operand.new(cpu, 0x5).should == cpu.Z }
|
|
13
|
+
specify { DCPU16::Operand.new(cpu, 0x6).should == cpu.I }
|
|
14
|
+
specify { DCPU16::Operand.new(cpu, 0x7).should == cpu.J }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
context "when value is between 0x08 and 0x17" do
|
|
18
|
+
before(:all) do
|
|
19
|
+
cpu.memory.write(0x1000, 0x2a)
|
|
20
|
+
cpu.registers.each { |r| r.write(0x1000) }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
specify { DCPU16::Operand.new(cpu, 0x8).value.should == 0x2a }
|
|
24
|
+
specify { DCPU16::Operand.new(cpu, 0xF).value.should == 0x2a }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
context "when value is between 0x10 and 0x17" do
|
|
29
|
+
pending
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
context "when value is 0x18" do
|
|
33
|
+
pending
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
context "when value is 0x19" do
|
|
37
|
+
pending
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
context "when value is 0x1A" do
|
|
41
|
+
pending
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
context "when value is 0x1B" do
|
|
45
|
+
pending
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
context "when value is 0x1C" do
|
|
49
|
+
pending
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
context "when value is 0x1D" do
|
|
53
|
+
pending
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
context "when value is 0x1E" do
|
|
57
|
+
pending
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
context "when value is 0x1F" do
|
|
61
|
+
pending
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
context "when value is between 0x20 and 0x3f" do
|
|
65
|
+
specify { DCPU16::Operand.new(cpu, 0x20).should be_a_kind_of(DCPU16::Literal) }
|
|
66
|
+
specify { DCPU16::Operand.new(cpu, 0x20).value.should == 0x0 }
|
|
67
|
+
specify { DCPU16::Operand.new(cpu, 0x3f).value.should == 0x1f }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
context "when value is > 0x3f" do
|
|
71
|
+
specify do
|
|
72
|
+
expect {
|
|
73
|
+
DCPU16::Operand.new(cpu, 0x40)
|
|
74
|
+
}.to raise_error(DCPU16::Operand::NotFound)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
describe DCPU16::Word do
|
|
4
|
+
let(:memory) { DCPU16::Memory.new }
|
|
5
|
+
subject { DCPU16::Word.new(2, memory, 0) }
|
|
6
|
+
|
|
7
|
+
specify { subject.should be_a_kind_of(DCPU16::Word) }
|
|
8
|
+
its(:value) { should == 2 }
|
|
9
|
+
its(:read) { should == 2 }
|
|
10
|
+
|
|
11
|
+
describe "#write" do
|
|
12
|
+
before { subject.write(42) }
|
|
13
|
+
its(:value) { should == 42 }
|
|
14
|
+
specify { memory.read(0).value.should == 42 }
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
metadata
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: Rdcpu16
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Patrick Helm
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2012-04-09 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: rspec
|
|
16
|
+
requirement: &74148950 !ruby/object:Gem::Requirement
|
|
17
|
+
none: false
|
|
18
|
+
requirements:
|
|
19
|
+
- - ~>
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '2.6'
|
|
22
|
+
type: :development
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: *74148950
|
|
25
|
+
description: This is a simple Ruby port of the fictive 16-bit-cpu DCPU16.
|
|
26
|
+
email:
|
|
27
|
+
- deradon87@gmail.com
|
|
28
|
+
executables: []
|
|
29
|
+
extensions: []
|
|
30
|
+
extra_rdoc_files: []
|
|
31
|
+
files:
|
|
32
|
+
- lib/dcpu16/cpu.rb
|
|
33
|
+
- lib/dcpu16/operand.rb
|
|
34
|
+
- lib/dcpu16/memory.rb
|
|
35
|
+
- lib/dcpu16/word.rb
|
|
36
|
+
- lib/dcpu16/version.rb
|
|
37
|
+
- lib/dcpu16/cpu/instructions.rb
|
|
38
|
+
- lib/dcpu16/support/debug.rb
|
|
39
|
+
- lib/dcpu16/literal.rb
|
|
40
|
+
- lib/dcpu16/instruction.rb
|
|
41
|
+
- lib/dcpu16/register.rb
|
|
42
|
+
- lib/dcpu16.rb
|
|
43
|
+
- MIT-LICENSE
|
|
44
|
+
- Rakefile
|
|
45
|
+
- README.rdoc
|
|
46
|
+
- spec/integration/example1_spec.rb
|
|
47
|
+
- spec/unit/dcpu16_word_spec.rb
|
|
48
|
+
- spec/unit/support/sample_observer.rb
|
|
49
|
+
- spec/unit/dcpu16_register_spec.rb
|
|
50
|
+
- spec/unit/dcpu16_cpu_spec.rb
|
|
51
|
+
- spec/unit/dcpu16_memory_spec.rb
|
|
52
|
+
- spec/unit/dcpu16_instructions_spec.rb
|
|
53
|
+
- spec/unit/dcpu16_operand_spec.rb
|
|
54
|
+
- spec/unit/dcpu16_instruction_spec.rb
|
|
55
|
+
- spec/spec_helper.rb
|
|
56
|
+
homepage: https://github.com/Deradon/Rdcpu16
|
|
57
|
+
licenses: []
|
|
58
|
+
post_install_message:
|
|
59
|
+
rdoc_options: []
|
|
60
|
+
require_paths:
|
|
61
|
+
- lib
|
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
63
|
+
none: false
|
|
64
|
+
requirements:
|
|
65
|
+
- - ! '>='
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0'
|
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
69
|
+
none: false
|
|
70
|
+
requirements:
|
|
71
|
+
- - ! '>='
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: '0'
|
|
74
|
+
requirements: []
|
|
75
|
+
rubyforge_project:
|
|
76
|
+
rubygems_version: 1.8.10
|
|
77
|
+
signing_key:
|
|
78
|
+
specification_version: 3
|
|
79
|
+
summary: Ruby port of DCPU16
|
|
80
|
+
test_files:
|
|
81
|
+
- spec/integration/example1_spec.rb
|
|
82
|
+
- spec/unit/dcpu16_word_spec.rb
|
|
83
|
+
- spec/unit/support/sample_observer.rb
|
|
84
|
+
- spec/unit/dcpu16_register_spec.rb
|
|
85
|
+
- spec/unit/dcpu16_cpu_spec.rb
|
|
86
|
+
- spec/unit/dcpu16_memory_spec.rb
|
|
87
|
+
- spec/unit/dcpu16_instructions_spec.rb
|
|
88
|
+
- spec/unit/dcpu16_operand_spec.rb
|
|
89
|
+
- spec/unit/dcpu16_instruction_spec.rb
|
|
90
|
+
- spec/spec_helper.rb
|