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 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