Rdcpu16 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,10 @@
1
+ = Rdcpu16
2
+
3
+ == Ruby port of: http://0x10c.com/doc/dcpu-16.txt
4
+ This is a simple Ruby port of the fictive 16-bit-cpu DCPU16.
5
+
6
+ == Make sure to checkout these resources:
7
+
8
+ * http://0x10c.com/
9
+ * http://0x10cforum.com/
10
+
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
+
@@ -0,0 +1,4 @@
1
+ module DCPU16
2
+ VERSION = "0.0.1"
3
+ end
4
+
@@ -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,6 @@
1
+ require 'dcpu16/cpu'
2
+ require 'dcpu16/version'
3
+
4
+ module DCPU16
5
+ end
6
+
@@ -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
+
@@ -0,0 +1,2 @@
1
+ require 'dcpu16'
2
+
@@ -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,6 @@
1
+ require 'spec_helper.rb'
2
+
3
+ describe DCPU16::Register do
4
+ subject { DCPU16::Register.new(0x00) }
5
+ end
6
+
@@ -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
+
@@ -0,0 +1,14 @@
1
+ module DCPU16
2
+ class SampleObserver
3
+ attr_reader :cycle
4
+
5
+ def initialize
6
+ @cycle = 0
7
+ end
8
+
9
+ def update(cpu)
10
+ @cycle = cpu.cycle
11
+ end
12
+ end
13
+ end
14
+
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